Merge pull request #21049 from sivanov-work:vpl_dx11_merge

G-API: oneVPL merge DX11 acceleration

* Merge DX11 initial

* Fold conditions row in MACRO in utils

* Inject DeviceSelector

* Turn on DeviceSelector in DX11

* Change sharedLock logic & Move FMT checking in FrameAdapter c-tor

* Move out NumSuggestFrame to configure params

* Drain file source fix

* Fix compilation

* Force zero initializetion of SharedLock

* Fix some compiler warnings

* Fix integer comparison warnings

* Fix integers in sample

* Integrate Demux

* Fix compilation

* Add predefined names for some CfgParam

* Trigger CI

* Fix MultithreadCtx bug, Add Dx11 GetBlobParam(), Get rif of ATL CComPtr

* Fix UT: remove unit test with deprecated video from opencv_extra

* Add creators for most usable CfgParam

* Eliminate some warnings

* Fix warning in GAPI_Assert

* Apply comments

* Add VPL wrapped header with MSVC pragma to get rid of global warning masking
pull/21217/head
Sergey Ivanov 3 years ago committed by GitHub
parent 41d108ead6
commit 5c91f5b71d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 7
      modules/gapi/CMakeLists.txt
  2. 65
      modules/gapi/include/opencv2/gapi/streaming/onevpl/cfg_params.hpp
  3. 30
      modules/gapi/perf/streaming/gapi_streaming_source_perf_tests.cpp
  4. 26
      modules/gapi/samples/onevpl_infer_single_roi.cpp
  5. 38
      modules/gapi/src/backends/render/grenderocv.cpp
  6. 125
      modules/gapi/src/streaming/onevpl/accelerators/accel_policy_cpu.cpp
  7. 6
      modules/gapi/src/streaming/onevpl/accelerators/accel_policy_cpu.hpp
  8. 364
      modules/gapi/src/streaming/onevpl/accelerators/accel_policy_dx11.cpp
  9. 46
      modules/gapi/src/streaming/onevpl/accelerators/accel_policy_dx11.hpp
  10. 19
      modules/gapi/src/streaming/onevpl/accelerators/accel_policy_interface.hpp
  11. 404
      modules/gapi/src/streaming/onevpl/accelerators/dx11_alloc_resource.cpp
  12. 151
      modules/gapi/src/streaming/onevpl/accelerators/dx11_alloc_resource.hpp
  13. 39
      modules/gapi/src/streaming/onevpl/accelerators/surface/cpu_frame_adapter.cpp
  14. 1
      modules/gapi/src/streaming/onevpl/accelerators/surface/cpu_frame_adapter.hpp
  15. 232
      modules/gapi/src/streaming/onevpl/accelerators/surface/dx11_frame_adapter.cpp
  16. 63
      modules/gapi/src/streaming/onevpl/accelerators/surface/dx11_frame_adapter.hpp
  17. 16
      modules/gapi/src/streaming/onevpl/accelerators/surface/surface.cpp
  18. 40
      modules/gapi/src/streaming/onevpl/accelerators/surface/surface.hpp
  19. 6
      modules/gapi/src/streaming/onevpl/accelerators/surface/surface_pool.hpp
  20. 296
      modules/gapi/src/streaming/onevpl/accelerators/utils/elastic_barrier.hpp
  21. 95
      modules/gapi/src/streaming/onevpl/accelerators/utils/shared_lock.cpp
  22. 47
      modules/gapi/src/streaming/onevpl/accelerators/utils/shared_lock.hpp
  23. 44
      modules/gapi/src/streaming/onevpl/cfg_param_device_selector.cpp
  24. 28
      modules/gapi/src/streaming/onevpl/cfg_params.cpp
  25. 55
      modules/gapi/src/streaming/onevpl/cfg_params_parser.cpp
  26. 13
      modules/gapi/src/streaming/onevpl/cfg_params_parser.hpp
  27. 3
      modules/gapi/src/streaming/onevpl/data_provider_defines.hpp
  28. 7
      modules/gapi/src/streaming/onevpl/data_provider_dispatcher.cpp
  29. 3
      modules/gapi/src/streaming/onevpl/data_provider_interface_exception.cpp
  30. 2
      modules/gapi/src/streaming/onevpl/demux/async_mfp_demux_data_provider.hpp
  31. 314
      modules/gapi/src/streaming/onevpl/engine/decode/decode_engine_legacy.cpp
  32. 10
      modules/gapi/src/streaming/onevpl/engine/decode/decode_engine_legacy.hpp
  33. 10
      modules/gapi/src/streaming/onevpl/engine/decode/decode_session.cpp
  34. 11
      modules/gapi/src/streaming/onevpl/engine/decode/decode_session.hpp
  35. 4
      modules/gapi/src/streaming/onevpl/engine/engine_session.hpp
  36. 6
      modules/gapi/src/streaming/onevpl/engine/processing_engine_base.cpp
  37. 7
      modules/gapi/src/streaming/onevpl/engine/processing_engine_base.hpp
  38. 6
      modules/gapi/src/streaming/onevpl/file_data_provider.cpp
  39. 25
      modules/gapi/src/streaming/onevpl/onevpl_export.hpp
  40. 116
      modules/gapi/src/streaming/onevpl/source_priv.cpp
  41. 15
      modules/gapi/src/streaming/onevpl/source_priv.hpp
  42. 466
      modules/gapi/src/streaming/onevpl/utils.cpp
  43. 16
      modules/gapi/src/streaming/onevpl/utils.hpp
  44. 86
      modules/gapi/test/common/gapi_streaming_tests_common.hpp
  45. 97
      modules/gapi/test/streaming/gapi_streaming_tests.cpp
  46. 348
      modules/gapi/test/streaming/gapi_streaming_utils_test.cpp
  47. 262
      modules/gapi/test/streaming/gapi_streaming_vpl_core_test.cpp
  48. 6
      modules/gapi/test/streaming/gapi_streaming_vpl_data_provider.cpp
  49. 17
      modules/gapi/test/streaming/gapi_streaming_vpl_device_selector.cpp

@ -174,10 +174,13 @@ set(gapi_srcs
src/streaming/onevpl/utils.cpp
src/streaming/onevpl/data_provider_interface_exception.cpp
src/streaming/onevpl/accelerators/surface/cpu_frame_adapter.cpp
src/streaming/onevpl/accelerators/surface/dx11_frame_adapter.cpp
src/streaming/onevpl/accelerators/surface/surface.cpp
src/streaming/onevpl/accelerators/surface/surface_pool.cpp
src/streaming/onevpl/accelerators/utils/shared_lock.cpp
src/streaming/onevpl/accelerators/accel_policy_cpu.cpp
src/streaming/onevpl/accelerators/accel_policy_dx11.cpp
src/streaming/onevpl/accelerators/dx11_alloc_resource.cpp
src/streaming/onevpl/engine/engine_session.cpp
src/streaming/onevpl/engine/processing_engine_base.cpp
src/streaming/onevpl/engine/decode/decode_engine_legacy.cpp
@ -282,12 +285,16 @@ if(HAVE_GAPI_ONEVPL)
if(TARGET opencv_test_gapi)
ocv_target_compile_definitions(opencv_test_gapi PRIVATE -DHAVE_ONEVPL)
ocv_target_link_libraries(opencv_test_gapi PRIVATE ${VPL_IMPORTED_TARGETS})
if(MSVC)
target_compile_options(opencv_test_gapi PUBLIC "/wd4201")
endif()
if(HAVE_D3D11 AND HAVE_OPENCL)
ocv_target_include_directories(opencv_test_gapi SYSTEM PRIVATE ${OPENCL_INCLUDE_DIRS})
endif()
endif()
ocv_target_compile_definitions(${the_module} PRIVATE -DHAVE_ONEVPL)
ocv_target_link_libraries(${the_module} PRIVATE ${VPL_IMPORTED_TARGETS})
if(HAVE_D3D11 AND HAVE_OPENCL)
ocv_target_include_directories(${the_module} SYSTEM PRIVATE ${OPENCL_INCLUDE_DIRS})
endif()

@ -46,9 +46,72 @@ struct GAPI_EXPORTS CfgParam {
double_t,
void*,
std::string>;
/**
* @brief frames_pool_size_name
*
* Special configuration parameter name for onevp::GSource:
*
* @note frames_pool_size_name allows to allocate surfaces pool appropriate size to keep
* decoded frames in accelerator memory ready before
* they would be consumed by onevp::GSource::pull operation. If you see
* a lot of WARNING about lack of free surface then it's time to increase
* frames_pool_size_name but be aware of accelerator free memory volume.
* If not set then MFX implementation use
* mfxFrameAllocRequest::NumFrameSuggested behavior
*
*/
static constexpr const char *frames_pool_size_name() { return "frames_pool_size"; }
static CfgParam create_frames_pool_size(uint64_t value);
/**
* @brief acceleration_mode_name
*
* Special configuration parameter names for onevp::GSource:
*
* @note acceleration_mode_name allows to activate hardware acceleration &
* device memory management.
* Supported values:
* - MFX_ACCEL_MODE_VIA_D3D11 Will activate DX11 acceleration and will produces
* MediaFrames with data allocated in DX11 device memory
*
* If not set then MFX implementation will use default acceleration behavior:
* all decoding operation uses default GPU resources but MediaFrame produces
* data allocated by using host RAM
*
*/
static constexpr const char *acceleration_mode_name() { return "mfxImplDescription.AccelerationMode"; }
static CfgParam create_acceleration_mode(uint32_t value);
static CfgParam create_acceleration_mode(const char* value);
/**
* @brief decoder_id_name
*
* Special configuration parameter names for onevp::GSource:
*
* @note decoder_id_name allows to specify VPL decoder type which MUST present
* in case of RAW video input data and MUST NOT present as CfgParam if video
* stream incapsulated into container(*.mp4, *.mkv and so on). In latter case
* onevp::GSource will determine it automatically
* Supported values:
* - MFX_CODEC_AVC
* - MFX_CODEC_HEVC
* - MFX_CODEC_MPEG2
* - MFX_CODEC_VC1
* - MFX_CODEC_CAPTURE
* - MFX_CODEC_VP9
* - MFX_CODEC_AV1
*
*/
static constexpr const char *decoder_id_name() { return "mfxImplDescription.mfxDecoderDescription.decoder.CodecID"; }
static CfgParam create_decoder_id(uint32_t value);
static CfgParam create_decoder_id(const char* value);
static constexpr const char *implementation_name() { return "mfxImplDescription.Impl"; }
static CfgParam create_implementation(uint32_t value);
static CfgParam create_implementation(const char* value);
/**
* Create onevp::GSource configuration parameter.
* Create generic onevp::GSource configuration parameter.
*
*@param name name of parameter.
*@param value value of parameter.

@ -18,16 +18,19 @@ using namespace perf;
const std::string files[] = {
"highgui/video/big_buck_bunny.h265",
"highgui/video/big_buck_bunny.h264",
"highgui/video/sample_322x242_15frames.yuv420p.libx265.mp4",
};
const std::string codec[] = {
"MFX_CODEC_HEVC",
"MFX_CODEC_AVC"
"MFX_CODEC_AVC",
"",
};
using source_t = std::string;
using codec_t = std::string;
using source_description_t = std::tuple<source_t, codec_t>;
using accel_mode_t = std::string;
using source_description_t = std::tuple<source_t, codec_t, accel_mode_t>;
class OneVPLSourcePerfTest : public TestPerfParams<source_description_t> {};
class VideoCapSourcePerfTest : public TestPerfParams<source_t> {};
@ -39,12 +42,20 @@ PERF_TEST_P_(OneVPLSourcePerfTest, TestPerformance)
const auto params = GetParam();
source_t src = findDataFile(get<0>(params));
codec_t type = get<1>(params);
accel_mode_t mode = get<2>(params);
std::vector<CfgParam> cfg_params {
CfgParam::create<std::string>("mfxImplDescription.Impl", "MFX_IMPL_TYPE_HARDWARE"),
CfgParam::create("mfxImplDescription.mfxDecoderDescription.decoder.CodecID", type),
CfgParam::create_implementation("MFX_IMPL_TYPE_HARDWARE"),
};
if (!type.empty()) {
cfg_params.push_back(CfgParam::create_decoder_id(type.c_str()));
}
if (!mode.empty()) {
cfg_params.push_back(CfgParam::create_acceleration_mode(mode.c_str()));
}
auto source_ptr = cv::gapi::wip::make_onevpl_src(src, cfg_params);
cv::gapi::wip::Data out;
@ -72,12 +83,17 @@ PERF_TEST_P_(VideoCapSourcePerfTest, TestPerformance)
}
INSTANTIATE_TEST_CASE_P(Streaming, OneVPLSourcePerfTest,
Values(source_description_t(files[0], codec[0]),
source_description_t(files[1], codec[1])));
Values(source_description_t(files[0], codec[0], ""),
source_description_t(files[0], codec[0], "MFX_ACCEL_MODE_VIA_D3D11"),
source_description_t(files[1], codec[1], ""),
source_description_t(files[1], codec[1], "MFX_ACCEL_MODE_VIA_D3D11"),
source_description_t(files[2], codec[2], ""),
source_description_t(files[2], codec[2], "MFX_ACCEL_MODE_VIA_D3D11")));
INSTANTIATE_TEST_CASE_P(Streaming, VideoCapSourcePerfTest,
Values(files[0],
files[1]));
files[1],
files[2]));
} // namespace opencv_test
#endif // HAVE_ONEVPL

@ -38,12 +38,14 @@
const std::string about =
"This is an OpenCV-based version of oneVPLSource decoder example";
const std::string keys =
"{ h help | | Print this help message }"
"{ input | | Path to the input demultiplexed video file }"
"{ output | | Path to the output RAW video file. Use .avi extension }"
"{ facem | face-detection-adas-0001.xml | Path to OpenVINO IE face detection model (.xml) }"
"{ faced | CPU | Target device for face detection model (e.g. CPU, GPU, VPU, ...) }"
"{ cfg_params | <prop name>:<value>;<prop name>:<value> | Semicolon separated list of oneVPL mfxVariants which is used for configuring source (see `MFXSetConfigFilterProperty` by https://spec.oneapi.io/versions/latest/elements/oneVPL/source/index.html) }";
"{ h help | | Print this help message }"
"{ input | | Path to the input demultiplexed video file }"
"{ output | | Path to the output RAW video file. Use .avi extension }"
"{ facem | face-detection-adas-0001.xml | Path to OpenVINO IE face detection model (.xml) }"
"{ faced | AUTO | Target device for face detection model (e.g. AUTO, GPU, VPU, ...) }"
"{ cfg_params | <prop name>:<value>;<prop name>:<value> | Semicolon separated list of oneVPL mfxVariants which is used for configuring source (see `MFXSetConfigFilterProperty` by https://spec.oneapi.io/versions/latest/elements/oneVPL/source/index.html) }"
"{ streaming_queue_capacity | 1 | Streaming executor queue capacity. Calculated automaticaly if 0 }"
"{ frames_pool_size | 0 | OneVPL source applies this parameter as preallocated frames pool size}";
namespace {
@ -194,6 +196,8 @@ int main(int argc, char *argv[]) {
std::string file_path = cmd.get<std::string>("input");
const std::string output = cmd.get<std::string>("output");
const auto face_model_path = cmd.get<std::string>("facem");
const auto streaming_queue_capacity = cmd.get<uint64_t>("streaming_queue_capacity");
const auto source_queue_capacity = cmd.get<uint64_t>("frames_pool_size");
// check ouput file extension
if (!output.empty()) {
@ -217,6 +221,10 @@ int main(int argc, char *argv[]) {
return -1;
}
if (source_queue_capacity != 0) {
source_cfgs.push_back(cv::gapi::wip::onevpl::CfgParam::create_frames_pool_size(source_queue_capacity));
}
const std::string& device_id = cmd.get<std::string>("faced");
auto face_net = cv::gapi::ie::Params<custom::FaceDetector> {
face_model_path, // path to topology IR
@ -298,6 +306,10 @@ int main(int argc, char *argv[]) {
< custom::OCVLocateROI
, custom::OCVBBoxes>();
auto networks = cv::gapi::networks(face_net);
auto face_detection_args = cv::compile_args(networks, kernels);
if (streaming_queue_capacity != 0) {
face_detection_args += cv::compile_args(cv::gapi::streaming::queue_capacity{ streaming_queue_capacity });
}
// Create source
cv::Ptr<cv::gapi::wip::IStreamSource> cap;
@ -331,7 +343,7 @@ int main(int argc, char *argv[]) {
cv::GStreamingCompiled pipeline;
try {
pipeline = cv::GComputation(cv::GIn(in), cv::GOut(out))
.compileStreaming(cv::compile_args(kernels, networks));
.compileStreaming(std::move(face_detection_args));
} catch (const std::exception& ex) {
std::cerr << "Exception occured during pipeline construction: " << ex.what() << std::endl;
return -1;

@ -128,15 +128,12 @@ GAPI_OCV_KERNEL_ST(RenderFrameOCVImpl, cv::gapi::wip::draw::GRenderFrame, Render
out = in;
auto desc = out.desc();
auto w_out = out.access(cv::MediaFrame::Access::W);
auto out_y = cv::Mat(desc.size, CV_8UC1, w_out.ptr[0], w_out.stride[0]);
auto out_uv = cv::Mat(desc.size / 2, CV_8UC2, w_out.ptr[1], w_out.stride[1]);
auto r_in = in.access(cv::MediaFrame::Access::R);
cv::Mat upsample_uv, yuv;
{
auto r_in = in.access(cv::MediaFrame::Access::R);
auto in_y = cv::Mat(desc.size, CV_8UC1, r_in.ptr[0], r_in.stride[0]);
auto in_uv = cv::Mat(desc.size / 2, CV_8UC2, r_in.ptr[1], r_in.stride[1]);
auto in_y = cv::Mat(desc.size, CV_8UC1, r_in.ptr[0], r_in.stride[0]);
auto in_uv = cv::Mat(desc.size / 2, CV_8UC2, r_in.ptr[1], r_in.stride[1]);
/* FIXME How to render correctly on NV12 format ?
*
@ -157,19 +154,26 @@ GAPI_OCV_KERNEL_ST(RenderFrameOCVImpl, cv::gapi::wip::draw::GRenderFrame, Render
*
*/
// NV12 -> YUV
cv::Mat upsample_uv, yuv;
cv::resize(in_uv, upsample_uv, in_uv.size() * 2, cv::INTER_LINEAR);
cv::merge(std::vector<cv::Mat>{in_y, upsample_uv}, yuv);
// NV12 -> YUV
cv::resize(in_uv, upsample_uv, in_uv.size() * 2, cv::INTER_LINEAR);
cv::merge(std::vector<cv::Mat>{in_y, upsample_uv}, yuv);
}
cv::gapi::wip::draw::drawPrimitivesOCVYUV(yuv, prims, state.ftpr);
// YUV -> NV12
cv::Mat out_u, out_v, uv_plane;
std::vector<cv::Mat> chs = { out_y, out_u, out_v };
cv::split(yuv, chs);
cv::merge(std::vector<cv::Mat>{chs[1], chs[2]}, uv_plane);
cv::resize(uv_plane, out_uv, uv_plane.size() / 2, cv::INTER_LINEAR);
{
auto w_out = out.access(cv::MediaFrame::Access::W);
auto out_y = cv::Mat(desc.size, CV_8UC1, w_out.ptr[0], w_out.stride[0]);
auto out_uv = cv::Mat(desc.size / 2, CV_8UC2, w_out.ptr[1], w_out.stride[1]);
cv::Mat out_u, out_v, uv_plane;
std::vector<cv::Mat> chs = { out_y, out_u, out_v };
cv::split(yuv, chs);
cv::merge(std::vector<cv::Mat>{chs[1], chs[2]}, uv_plane);
cv::resize(uv_plane, out_uv, uv_plane.size() / 2, cv::INTER_LINEAR);
}
}
static void setup(const cv::GFrameDesc& /* in_nv12 */,

@ -21,8 +21,100 @@ namespace cv {
namespace gapi {
namespace wip {
namespace onevpl {
namespace utils {
mfxU32 GetSurfaceSize_(mfxU32 FourCC, mfxU32 width, mfxU32 height) {
mfxU32 nbytes = 0;
VPLCPUAccelerationPolicy::VPLCPUAccelerationPolicy() {
mfxU32 half_width = width / 2;
mfxU32 half_height = height / 2;
switch (FourCC) {
case MFX_FOURCC_I420:
case MFX_FOURCC_NV12:
nbytes = width * height + 2 * half_width * half_height;
break;
case MFX_FOURCC_I010:
case MFX_FOURCC_P010:
nbytes = width * height + 2 * half_width * half_height;
nbytes *= 2;
break;
case MFX_FOURCC_RGB4:
nbytes = width * height * 4;
break;
default:
break;
}
return nbytes;
}
surface_ptr_t create_surface_RGB4_(mfxFrameInfo frameInfo,
std::shared_ptr<void> out_buf_ptr,
size_t out_buf_ptr_offset,
size_t out_buf_size)
{
mfxU8* buf = reinterpret_cast<mfxU8*>(out_buf_ptr.get());
mfxU16 surfW = frameInfo.Width * 4;
mfxU16 surfH = frameInfo.Height;
(void)surfH;
// TODO more intelligent check
if (out_buf_size <= out_buf_ptr_offset) {
GAPI_LOG_WARNING(nullptr, "Not enough buffer, ptr: " << out_buf_ptr <<
", size: " << out_buf_size <<
", offset: " << out_buf_ptr_offset <<
", W: " << surfW <<
", H: " << surfH);
GAPI_Assert(false && "Invalid offset");
}
std::unique_ptr<mfxFrameSurface1> handle(new mfxFrameSurface1);
memset(handle.get(), 0, sizeof(mfxFrameSurface1));
handle->Info = frameInfo;
handle->Data.B = buf + out_buf_ptr_offset;
handle->Data.G = handle->Data.B + 1;
handle->Data.R = handle->Data.B + 2;
handle->Data.A = handle->Data.B + 3;
handle->Data.Pitch = surfW;
return Surface::create_surface(std::move(handle), out_buf_ptr);
}
surface_ptr_t create_surface_other_(mfxFrameInfo frameInfo,
std::shared_ptr<void> out_buf_ptr,
size_t out_buf_ptr_offset,
size_t out_buf_size)
{
mfxU8* buf = reinterpret_cast<mfxU8*>(out_buf_ptr.get());
mfxU16 surfH = frameInfo.Height;
mfxU16 surfW = (frameInfo.FourCC == MFX_FOURCC_P010) ? frameInfo.Width * 2 : frameInfo.Width;
// TODO more intelligent check
if (out_buf_size <=
out_buf_ptr_offset + (surfW * surfH) + ((surfW / 2) * (surfH / 2))) {
GAPI_LOG_WARNING(nullptr, "Not enough buffer, ptr: " << out_buf_ptr <<
", size: " << out_buf_size <<
", offset: " << out_buf_ptr_offset <<
", W: " << surfW <<
", H: " << surfH);
GAPI_Assert(false && "Invalid offset");
}
std::unique_ptr<mfxFrameSurface1> handle(new mfxFrameSurface1);
memset(handle.get(), 0, sizeof(mfxFrameSurface1));
handle->Info = frameInfo;
handle->Data.Y = buf + out_buf_ptr_offset;
handle->Data.U = buf + out_buf_ptr_offset + (surfW * surfH);
handle->Data.V = handle->Data.U + ((surfW / 2) * (surfH / 2));
handle->Data.Pitch = surfW;
return Surface::create_surface(std::move(handle), out_buf_ptr);
}
} // namespace utils
VPLCPUAccelerationPolicy::VPLCPUAccelerationPolicy(device_selector_ptr_t selector) :
VPLAccelerationPolicy(selector) {
GAPI_LOG_INFO(nullptr, "created");
}
@ -117,6 +209,37 @@ VPLCPUAccelerationPolicy::create_surface_pool(size_t pool_size, size_t surface_s
return preallocated_pool_memory_ptr;
}
VPLCPUAccelerationPolicy::pool_key_t
VPLCPUAccelerationPolicy::create_surface_pool(const mfxFrameAllocRequest& alloc_request, mfxVideoParam& param) {
// External (application) allocation of decode surfaces
GAPI_LOG_DEBUG(nullptr, "Query mfxFrameAllocRequest.NumFrameSuggested: " << alloc_request.NumFrameSuggested <<
", mfxFrameAllocRequest.Type: " << alloc_request.Type);
mfxU32 singleSurfaceSize = utils::GetSurfaceSize_(param.mfx.FrameInfo.FourCC,
param.mfx.FrameInfo.Width,
param.mfx.FrameInfo.Height);
if (!singleSurfaceSize) {
throw std::runtime_error("Cannot determine surface size for: fourCC: " +
std::to_string(param.mfx.FrameInfo.FourCC) +
", width: " + std::to_string(param.mfx.FrameInfo.Width) +
", height: " + std::to_string(param.mfx.FrameInfo.Height));
}
const auto &frameInfo = param.mfx.FrameInfo;
auto surface_creator =
[&frameInfo] (std::shared_ptr<void> out_buf_ptr, size_t out_buf_ptr_offset,
size_t out_buf_size) -> surface_ptr_t {
return (frameInfo.FourCC == MFX_FOURCC_RGB4) ?
utils::create_surface_RGB4_(frameInfo, out_buf_ptr, out_buf_ptr_offset,
out_buf_size) :
utils::create_surface_other_(frameInfo, out_buf_ptr, out_buf_ptr_offset,
out_buf_size);};
return create_surface_pool(alloc_request.NumFrameSuggested,
singleSurfaceSize, surface_creator);
}
VPLCPUAccelerationPolicy::surface_weak_ptr_t VPLCPUAccelerationPolicy::get_free_surface(pool_key_t key) {
auto pool_it = pool_table.find(key);
if (pool_it == pool_table.end()) {

@ -13,7 +13,6 @@
#include "opencv2/gapi/own/exports.hpp" // GAPI_EXPORTS
#ifdef HAVE_ONEVPL
#include <vpl/mfxvideo.h>
#include "streaming/onevpl/accelerators/accel_policy_interface.hpp"
#include "streaming/onevpl/accelerators/surface/surface_pool.hpp"
@ -25,14 +24,15 @@ namespace onevpl {
// GAPI_EXPORTS for tests
struct GAPI_EXPORTS VPLCPUAccelerationPolicy final : public VPLAccelerationPolicy
{
VPLCPUAccelerationPolicy();
VPLCPUAccelerationPolicy(device_selector_ptr_t selector);
~VPLCPUAccelerationPolicy();
using pool_t = CachedPool;
void init(session_t session) override;
void deinit(session_t session) override;
pool_key_t create_surface_pool(size_t pool_size, size_t surface_size_bytes, surface_ptr_ctr_t creator) override;
pool_key_t create_surface_pool(size_t pool_size, size_t surface_size_bytes, surface_ptr_ctr_t creator);
pool_key_t create_surface_pool(const mfxFrameAllocRequest& alloc_request, mfxVideoParam& param) override;
surface_weak_ptr_t get_free_surface(pool_key_t key) override;
size_t get_free_surface_count(pool_key_t key) const override;
size_t get_surface_count(pool_key_t key) const override;

@ -5,8 +5,10 @@
// Copyright (C) 2021 Intel Corporation
#ifdef HAVE_ONEVPL
#include <opencv2/gapi/util/compiler_hints.hpp>
#include "streaming/onevpl/accelerators/accel_policy_dx11.hpp"
#include "streaming/onevpl/accelerators/surface/cpu_frame_adapter.hpp"
#include "streaming/onevpl/accelerators/surface/dx11_frame_adapter.hpp"
#include "streaming/onevpl/accelerators/surface/surface.hpp"
#include "streaming/onevpl/utils.hpp"
#include "logger.hpp"
@ -30,28 +32,60 @@ namespace gapi {
namespace wip {
namespace onevpl {
VPLDX11AccelerationPolicy::VPLDX11AccelerationPolicy() :
hw_handle(nullptr)
VPLDX11AccelerationPolicy::VPLDX11AccelerationPolicy(device_selector_ptr_t selector) :
VPLAccelerationPolicy(selector),
hw_handle(),
device_context(),
allocator()
{
#ifdef CPU_ACCEL_ADAPTER
adapter.reset(new VPLCPUAccelerationPolicy);
#endif
// setup dx11 device
IDeviceSelector::DeviceScoreTable devices = get_device_selector()->select_devices();
GAPI_Assert(devices.size() == 1 && "Multiple(or zero) acceleration devices case is unsupported");
AccelType accel_type = devices.begin()->second.get_type();
GAPI_Assert(accel_type == AccelType::DX11 &&
"Unexpected device AccelType while is waiting AccelType::DX11");
hw_handle = reinterpret_cast<ID3D11Device*>(devices.begin()->second.get_ptr());
// setup dx11 context
IDeviceSelector::DeviceContexts contexts = get_device_selector()->select_context();
GAPI_Assert(contexts.size() == 1 && "Multiple(or zero) acceleration context case is unsupported");
accel_type = contexts.begin()->get_type();
GAPI_Assert(accel_type == AccelType::DX11 &&
"Unexpected context AccelType while is waiting AccelType::DX11");
device_context = reinterpret_cast<ID3D11DeviceContext*>(contexts.begin()->get_ptr());
// setup dx11 allocator
memset(&allocator, 0, sizeof(mfxFrameAllocator));
allocator.Alloc = alloc_cb;
allocator.Lock = lock_cb;
allocator.Unlock = unlock_cb;
allocator.GetHDL = get_hdl_cb;
allocator.Free = free_cb;
allocator.pthis = this;
}
VPLDX11AccelerationPolicy::~VPLDX11AccelerationPolicy()
{
if (hw_handle)
{
GAPI_LOG_INFO(nullptr, "VPLDX11AccelerationPolicy release ID3D11Device");
hw_handle->Release();
for (auto& allocation_pair : allocation_table) {
allocation_pair.second.reset();
}
GAPI_LOG_INFO(nullptr, "destroyed");
}
void VPLDX11AccelerationPolicy::init(session_t session) {
mfxStatus sts = MFXVideoCORE_GetHandle(session, MFX_HANDLE_D3D11_DEVICE, reinterpret_cast<mfxHDL*>(&hw_handle));
mfxStatus sts = MFXVideoCORE_SetHandle(session, MFX_HANDLE_D3D11_DEVICE,
static_cast<mfxHDL>(hw_handle));
if (sts != MFX_ERR_NONE)
{
throw std::logic_error("Cannot create VPLDX11AccelerationPolicy, MFXVideoCORE_GetHandle error: " +
throw std::logic_error("Cannot create VPLDX11AccelerationPolicy, MFXVideoCORE_SetHandle error: " +
mfxstatus_to_string(sts));
}
sts = MFXVideoCORE_SetFrameAllocator(session, &allocator);
if (sts != MFX_ERR_NONE)
{
throw std::logic_error("Cannot create VPLDX11AccelerationPolicy, MFXVideoCORE_SetFrameAllocator error: " +
mfxstatus_to_string(sts));
}
@ -63,53 +97,289 @@ void VPLDX11AccelerationPolicy::deinit(session_t session) {
}
VPLDX11AccelerationPolicy::pool_key_t
VPLDX11AccelerationPolicy::create_surface_pool(size_t pool_size, size_t surface_size_bytes,
surface_ptr_ctr_t creator) {
GAPI_LOG_DEBUG(nullptr, "pool size: " << pool_size << ", surface size bytes: " << surface_size_bytes);
VPLDX11AccelerationPolicy::create_surface_pool(const mfxFrameAllocRequest& alloc_req,
mfxVideoParam& param) {
param.IOPattern = MFX_IOPATTERN_OUT_VIDEO_MEMORY;
#ifdef CPU_ACCEL_ADAPTER
return adapter->create_surface_pool(pool_size, surface_size_bytes, creator);
#endif
(void)pool_size;
(void)surface_size_bytes;
(void)creator;
throw std::runtime_error("VPLDX11AccelerationPolicy::create_surface_pool() is not implemented");
// allocate textures by explicit request
mfxFrameAllocResponse mfxResponse;
mfxStatus sts = on_alloc(&alloc_req, &mfxResponse);
if (sts != MFX_ERR_NONE)
{
throw std::logic_error("Cannot create allocated memory for surfaces, error: " +
mfxstatus_to_string(sts));
}
// get reference pointer
auto table_it = allocation_table.find(alloc_req.AllocId);
GAPI_DbgAssert (allocation_table.end() != table_it);
mfxU16 numSurfaces = alloc_req.NumFrameSuggested;
// NB: create pool with numSurfaces reservation
pool_t pool(numSurfaces);
for (int i = 0; i < numSurfaces; i++) {
std::unique_ptr<mfxFrameSurface1> handle(new mfxFrameSurface1 {});
handle->Info = param.mfx.FrameInfo;
handle->Data.MemId = mfxResponse.mids[i];
pool.push_back(Surface::create_surface(std::move(handle), table_it->second));
}
// remember pool by key
pool_key_t key = reinterpret_cast<pool_key_t>(table_it->second.get());
GAPI_LOG_INFO(nullptr, "New pool allocated, key: " << key <<
", surface count: " << pool.total_size());
try {
if (!pool_table.emplace(key, std::move(pool)).second) {
throw std::runtime_error(std::string("VPLDX11AccelerationPolicy::create_surface_pool - ") +
"cannot insert pool, table size: " + std::to_string(pool_table.size()));
}
} catch (const std::exception&) {
throw;
}
return key;
}
VPLDX11AccelerationPolicy::surface_weak_ptr_t VPLDX11AccelerationPolicy::get_free_surface(pool_key_t key)
{
#ifdef CPU_ACCEL_ADAPTER
return adapter->get_free_surface(key);
#endif
(void)key;
throw std::runtime_error("VPLDX11AccelerationPolicy::get_free_surface() is not implemented");
VPLDX11AccelerationPolicy::surface_weak_ptr_t VPLDX11AccelerationPolicy::get_free_surface(pool_key_t key) {
auto pool_it = pool_table.find(key);
if (pool_it == pool_table.end()) {
std::stringstream ss;
ss << "key is not found: " << key << ", table size: " << pool_table.size();
const std::string& str = ss.str();
GAPI_LOG_WARNING(nullptr, str);
throw std::runtime_error(std::string(__FUNCTION__) + " - " + str);
}
pool_t& requested_pool = pool_it->second;
return requested_pool.find_free();
}
size_t VPLDX11AccelerationPolicy::get_free_surface_count(pool_key_t key) const {
#ifdef CPU_ACCEL_ADAPTER
return adapter->get_free_surface_count(key);
#endif
(void)key;
throw std::runtime_error("get_free_surface_count() is not implemented");
size_t VPLDX11AccelerationPolicy::get_free_surface_count(pool_key_t) const {
GAPI_Assert(false && "get_free_surface_count() is not implemented");
}
size_t VPLDX11AccelerationPolicy::get_surface_count(pool_key_t key) const {
#ifdef CPU_ACCEL_ADAPTER
return adapter->get_surface_count(key);
#endif
(void)key;
throw std::runtime_error("VPLDX11AccelerationPolicy::get_surface_count() is not implemented");
size_t VPLDX11AccelerationPolicy::get_surface_count(pool_key_t) const {
GAPI_Assert(false && "VPLDX11AccelerationPolicy::get_surface_count() is not implemented");
}
cv::MediaFrame::AdapterPtr VPLDX11AccelerationPolicy::create_frame_adapter(pool_key_t key,
mfxFrameSurface1* surface) {
auto pool_it = pool_table.find(key);
if (pool_it == pool_table.end()) {
std::stringstream ss;
ss << "key is not found: " << key << ", table size: " << pool_table.size();
const std::string& str = ss.str();
GAPI_LOG_WARNING(nullptr, str);
throw std::runtime_error(std::string(__FUNCTION__) + " - " + str);
}
#ifdef CPU_ACCEL_ADAPTER
return adapter->create_frame_adapter(key, surface);
#endif
(void)key;
(void)surface;
throw std::runtime_error("VPLDX11AccelerationPolicy::create_frame_adapter() is not implemented");
pool_t& requested_pool = pool_it->second;
return cv::MediaFrame::AdapterPtr{new VPLMediaFrameDX11Adapter(requested_pool.find_by_handle(surface))};
}
mfxStatus VPLDX11AccelerationPolicy::alloc_cb(mfxHDL pthis, mfxFrameAllocRequest *request,
mfxFrameAllocResponse *response) {
if (!pthis) {
return MFX_ERR_MEMORY_ALLOC;
}
VPLDX11AccelerationPolicy *self = static_cast<VPLDX11AccelerationPolicy *>(pthis);
return self->on_alloc(request, response);
}
mfxStatus VPLDX11AccelerationPolicy::lock_cb(mfxHDL pthis, mfxMemId mid, mfxFrameData *ptr) {
VPLDX11AccelerationPolicy *self = static_cast<VPLDX11AccelerationPolicy *>(pthis);
GAPI_LOG_DEBUG(nullptr, "called from: " << self ? "Policy" : "Resource");
cv::util::suppress_unused_warning(self);
return on_lock(mid, ptr);
}
mfxStatus VPLDX11AccelerationPolicy::unlock_cb(mfxHDL pthis, mfxMemId mid, mfxFrameData *ptr) {
VPLDX11AccelerationPolicy *self = static_cast<VPLDX11AccelerationPolicy *>(pthis);
GAPI_LOG_DEBUG(nullptr, "called from: " << self ? "Policy" : "Resource");
cv::util::suppress_unused_warning(self);
return on_unlock(mid, ptr);
}
mfxStatus VPLDX11AccelerationPolicy::get_hdl_cb(mfxHDL pthis, mfxMemId mid, mfxHDL *handle) {
VPLDX11AccelerationPolicy *self = static_cast<VPLDX11AccelerationPolicy *>(pthis);
GAPI_LOG_DEBUG(nullptr, "called from: " << self ? "Policy" : "Resource");
cv::util::suppress_unused_warning(self);
return on_get_hdl(mid, handle);
}
mfxStatus VPLDX11AccelerationPolicy::free_cb(mfxHDL pthis, mfxFrameAllocResponse *response) {
if (!pthis) {
return MFX_ERR_MEMORY_ALLOC;
}
VPLDX11AccelerationPolicy *self = static_cast<VPLDX11AccelerationPolicy *>(pthis);
return self->on_free(response);
}
mfxStatus VPLDX11AccelerationPolicy::on_alloc(const mfxFrameAllocRequest *request,
mfxFrameAllocResponse *response) {
GAPI_LOG_DEBUG(nullptr, "Requested allocation id: " << std::to_string(request->AllocId) <<
", type: " << ext_mem_frame_type_to_cstr(request->Type) <<
", size: " << request->Info.Width << "x" << request->Info.Height <<
", frames minimum count: " << request->NumFrameMin <<
", frames suggested count: " << request->NumFrameSuggested);
auto table_it = allocation_table.find(request->AllocId);
if (allocation_table.end() != table_it) {
GAPI_LOG_WARNING(nullptr, "Allocation already exists, id: " + std::to_string(request->AllocId) +
". Total allocation size: " + std::to_string(allocation_table.size()));
// TODO cache
allocation_t &resources_array = table_it->second;
response->AllocId = request->AllocId;
GAPI_DbgAssert(static_cast<size_t>(std::numeric_limits<mfxU16>::max()) > resources_array->size() &&
"Invalid num frames: overflow");
response->NumFrameActual = static_cast<mfxU16>(resources_array->size());
response->mids = reinterpret_cast<mfxMemId *>(resources_array->data());
return MFX_ERR_NONE;
}
DXGI_FORMAT colorFormat = VPLMediaFrameDX11Adapter::get_dx11_color_format(request->Info.FourCC);
if (DXGI_FORMAT_UNKNOWN == colorFormat || colorFormat != DXGI_FORMAT_NV12) {
GAPI_LOG_WARNING(nullptr, "Unsupported fourcc :" << request->Info.FourCC);
return MFX_ERR_UNSUPPORTED;
}
D3D11_TEXTURE2D_DESC desc = { 0 };
desc.Width = request->Info.Width;
desc.Height = request->Info.Height;
desc.MipLevels = 1;
// single texture with subresources
desc.ArraySize = request->NumFrameSuggested;
desc.Format = colorFormat;
desc.SampleDesc.Count = 1;
desc.Usage = D3D11_USAGE_DEFAULT;
desc.MiscFlags = D3D11_RESOURCE_MISC_SHARED;
desc.BindFlags = D3D11_BIND_DECODER;
if (request->Type & MFX_MEMTYPE_SHARED_RESOURCE) {
desc.BindFlags |= D3D11_BIND_SHADER_RESOURCE;
desc.MiscFlags = D3D11_RESOURCE_MISC_SHARED;
}
ComPtrGuard<ID3D11Texture2D> main_texture = createCOMPtrGuard<ID3D11Texture2D>();
HRESULT err = S_OK;
{
ID3D11Texture2D *pTexture2D = nullptr;
err = hw_handle->CreateTexture2D(&desc, nullptr, &pTexture2D);
if (FAILED(err)) {
GAPI_LOG_WARNING(nullptr, "Cannot create texture, error: " + std::to_string(HRESULT_CODE(err)));
return MFX_ERR_MEMORY_ALLOC;
}
main_texture.reset(pTexture2D);
}
// create staging texture to read it from
desc.ArraySize = 1;
desc.Usage = D3D11_USAGE_STAGING;
desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ | D3D11_CPU_ACCESS_WRITE;
desc.BindFlags = 0;
desc.MiscFlags = 0;
std::vector<ComPtrGuard<ID3D11Texture2D>> staging_textures;
staging_textures.reserve(request->NumFrameSuggested);
for (int i = 0; i < request->NumFrameSuggested; i++ ) {
ID3D11Texture2D *staging_texture_2d = nullptr;
err = hw_handle->CreateTexture2D(&desc, NULL, &staging_texture_2d);
if (FAILED(err)) {
GAPI_LOG_WARNING(nullptr, "Cannot create staging texture, error: " + std::to_string(HRESULT_CODE(err)));
return MFX_ERR_MEMORY_ALLOC;
}
staging_textures.push_back(createCOMPtrGuard(staging_texture_2d));
}
// for multiple subresources initialize allocation array
auto cand_resource_it = allocation_table.end();
{
// insert into global table
auto inserted_it =
allocation_table.emplace(request->AllocId,
DX11AllocationRecord::create(request->NumFrameSuggested,
device_context,
allocator,
std::move(main_texture),
std::move(staging_textures)));
if (!inserted_it.second) {
GAPI_LOG_WARNING(nullptr, "Cannot assign allocation by id: " + std::to_string(request->AllocId) +
" - aldeady exist. Total allocation size: " + std::to_string(allocation_table.size()));
return MFX_ERR_MEMORY_ALLOC;
}
GAPI_LOG_DEBUG(nullptr, "allocation by id: " << request->AllocId <<
" was created, total allocations count: " << allocation_table.size());
cand_resource_it = inserted_it.first;
}
// fill out response
GAPI_DbgAssert(cand_resource_it != allocation_table.end() && "Invalid cand_resource_it");
allocation_t &resources_array = cand_resource_it->second;
response->AllocId = request->AllocId;
response->NumFrameActual = request->NumFrameSuggested;
response->mids = reinterpret_cast<mfxMemId *>(resources_array->data());
return MFX_ERR_NONE;
}
mfxStatus VPLDX11AccelerationPolicy::on_lock(mfxMemId mid, mfxFrameData *ptr) {
DX11AllocationRecord::AllocationId data = reinterpret_cast<DX11AllocationRecord::AllocationId>(mid);
if (!data) {
GAPI_LOG_WARNING(nullptr, "Allocation record is empty");
return MFX_ERR_LOCK_MEMORY;
}
return data->acquire_access(ptr);
}
mfxStatus VPLDX11AccelerationPolicy::on_unlock(mfxMemId mid, mfxFrameData *ptr) {
DX11AllocationRecord::AllocationId data = reinterpret_cast<DX11AllocationRecord::AllocationId>(mid);
if (!data) {
return MFX_ERR_LOCK_MEMORY;
}
return data->release_access(ptr);
}
mfxStatus VPLDX11AccelerationPolicy::on_get_hdl(mfxMemId mid, mfxHDL *handle) {
DX11AllocationRecord::AllocationId data = reinterpret_cast<DX11AllocationRecord::AllocationId>(mid);
if (!data) {
return MFX_ERR_INVALID_HANDLE;
}
mfxHDLPair *pPair = reinterpret_cast<mfxHDLPair *>(handle);
pPair->first = data->get_texture_ptr();
pPair->second = static_cast<mfxHDL>(reinterpret_cast<DX11AllocationItem::subresource_id_t *>(
static_cast<uint64_t>(data->get_subresource())));
GAPI_LOG_DEBUG(nullptr, "texture : " << pPair->first << ", sub id: " << pPair->second);
return MFX_ERR_NONE;
}
mfxStatus VPLDX11AccelerationPolicy::on_free(mfxFrameAllocResponse *response) {
GAPI_LOG_DEBUG(nullptr, "Allocations count before: " << allocation_table.size() <<
", requested id: " << response->AllocId);
auto table_it = allocation_table.find(response->AllocId);
if (allocation_table.end() == table_it) {
GAPI_LOG_WARNING(nullptr, "Cannot find allocation id: " + std::to_string(response->AllocId) +
". Total allocation size: " + std::to_string(allocation_table.size()));
return MFX_ERR_MEMORY_ALLOC;
}
allocation_table.erase(table_it);
return MFX_ERR_NONE;
}
} // namespace onevpl
} // namespace wip

@ -6,18 +6,14 @@
#ifndef GAPI_STREAMING_ONEVPL_ACCELERATORS_ACCEL_POLICY_DX11_HPP
#define GAPI_STREAMING_ONEVPL_ACCELERATORS_ACCEL_POLICY_DX11_HPP
#include <map>
#include "opencv2/gapi/own/exports.hpp" // GAPI_EXPORTS
//TODO Remove the next MACRO right after DX11 implementation
#define CPU_ACCEL_ADAPTER
#ifdef HAVE_ONEVPL
#include <vpl/mfxvideo.h>
#include "streaming/onevpl/accelerators/accel_policy_interface.hpp"
#ifdef CPU_ACCEL_ADAPTER
#include "streaming/onevpl/accelerators/accel_policy_cpu.hpp"
#endif
#include "streaming/onevpl/accelerators/surface/surface_pool.hpp"
#include "streaming/onevpl/accelerators/dx11_alloc_resource.hpp"
#ifdef HAVE_DIRECTX
#ifdef HAVE_D3D11
@ -39,30 +35,52 @@ namespace onevpl {
struct GAPI_EXPORTS VPLDX11AccelerationPolicy final: public VPLAccelerationPolicy
{
// GAPI_EXPORTS for tests
VPLDX11AccelerationPolicy();
VPLDX11AccelerationPolicy(device_selector_ptr_t selector);
~VPLDX11AccelerationPolicy();
using pool_t = CachedPool;
void init(session_t session) override;
void deinit(session_t session) override;
pool_key_t create_surface_pool(size_t pool_size, size_t surface_size_bytes, surface_ptr_ctr_t creator) override;
pool_key_t create_surface_pool(const mfxFrameAllocRequest& alloc_request,
mfxVideoParam& param) override;
surface_weak_ptr_t get_free_surface(pool_key_t key) override;
size_t get_free_surface_count(pool_key_t key) const override;
size_t get_surface_count(pool_key_t key) const override;
cv::MediaFrame::AdapterPtr create_frame_adapter(pool_key_t key,
mfxFrameSurface1* surface) override;
mfxFrameSurface1* surface) override;
private:
ID3D11Device *hw_handle;
ID3D11DeviceContext* device_context;
#ifdef CPU_ACCEL_ADAPTER
std::unique_ptr<VPLCPUAccelerationPolicy> adapter;
#endif
mfxFrameAllocator allocator;
static mfxStatus MFX_CDECL alloc_cb(mfxHDL pthis,
mfxFrameAllocRequest *request,
mfxFrameAllocResponse *response);
static mfxStatus MFX_CDECL lock_cb(mfxHDL pthis, mfxMemId mid, mfxFrameData *ptr);
static mfxStatus MFX_CDECL unlock_cb(mfxHDL pthis, mfxMemId mid, mfxFrameData *ptr);
static mfxStatus MFX_CDECL get_hdl_cb(mfxHDL pthis, mfxMemId mid, mfxHDL *handle);
static mfxStatus MFX_CDECL free_cb(mfxHDL pthis, mfxFrameAllocResponse *response);
virtual mfxStatus on_alloc(const mfxFrameAllocRequest *request,
mfxFrameAllocResponse *response);
static mfxStatus on_lock(mfxMemId mid, mfxFrameData *ptr);
static mfxStatus on_unlock(mfxMemId mid, mfxFrameData *ptr);
static mfxStatus on_get_hdl(mfxMemId mid, mfxHDL *handle);
virtual mfxStatus on_free(mfxFrameAllocResponse *response);
using alloc_id_t = mfxU32;
using allocation_t = std::shared_ptr<DX11AllocationRecord>;
std::map<alloc_id_t, allocation_t> allocation_table;
std::map<pool_key_t, pool_t> pool_table;
};
} // namespace onevpl
} // namespace wip
} // namespace gapi
} // namespace cv
#undef NOMINMAX
#endif // HAVE_D3D11
#endif // HAVE_DIRECTX

@ -12,9 +12,10 @@
#include <type_traits>
#include <opencv2/gapi/media.hpp>
#include <opencv2/gapi/streaming/onevpl/device_selector_interface.hpp>
#ifdef HAVE_ONEVPL
#include <vpl/mfxvideo.h>
#include "streaming/onevpl/onevpl_export.hpp"
namespace cv {
namespace gapi {
@ -24,7 +25,10 @@ namespace onevpl {
class Surface;
struct VPLAccelerationPolicy
{
virtual ~VPLAccelerationPolicy() {}
using device_selector_ptr_t = std::shared_ptr<IDeviceSelector>;
VPLAccelerationPolicy(device_selector_ptr_t selector) : device_selector(selector) {}
virtual ~VPLAccelerationPolicy() = default;
using pool_key_t = void*;
@ -36,6 +40,13 @@ struct VPLAccelerationPolicy
size_t out_buf_ptr_offset,
size_t out_buf_ptr_size)>;
device_selector_ptr_t get_device_selector() {
return device_selector;
}
const device_selector_ptr_t get_device_selector() const {
return device_selector;
}
virtual void init(session_t session) = 0;
virtual void deinit(session_t session) = 0;
@ -43,7 +54,7 @@ struct VPLAccelerationPolicy
// for existing workspace in existing pool (see realloc)
// thus it is not implemented,
// PLEASE provide initial memory area large enough
virtual pool_key_t create_surface_pool(size_t pool_size, size_t surface_size_bytes, surface_ptr_ctr_t creator) = 0;
virtual pool_key_t create_surface_pool(const mfxFrameAllocRequest& alloc_request, mfxVideoParam& param) = 0;
virtual surface_weak_ptr_t get_free_surface(pool_key_t key) = 0;
virtual size_t get_free_surface_count(pool_key_t key) const = 0;
@ -51,6 +62,8 @@ struct VPLAccelerationPolicy
virtual cv::MediaFrame::AdapterPtr create_frame_adapter(pool_key_t key,
mfxFrameSurface1* surface) = 0;
private:
device_selector_ptr_t device_selector;
};
} // namespace onevpl
} // namespace wip

@ -0,0 +1,404 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
//
// Copyright (C) 2021 Intel Corporation
#ifdef HAVE_ONEVPL
#include "streaming/onevpl/accelerators/dx11_alloc_resource.hpp"
#include "streaming/onevpl/accelerators/utils/shared_lock.hpp"
#include "logger.hpp"
#ifdef HAVE_DIRECTX
#ifdef HAVE_D3D11
namespace cv {
namespace gapi {
namespace wip {
namespace onevpl {
LockAdapter::LockAdapter(mfxFrameAllocator origin_allocator) :
lockable_allocator(origin_allocator),
impl() {
GAPI_DbgAssert((lockable_allocator.Lock && lockable_allocator.Unlock) &&
"Cannot create LockAdapter for empty origin allocator");
// abandon unusable c-allocator interfaces
// because LockAdapter requires Lock & Unlock only
lockable_allocator.Alloc = nullptr;
lockable_allocator.Free = nullptr;
lockable_allocator.pthis = nullptr;
}
size_t LockAdapter::read_lock(mfxMemId mid, mfxFrameData &data) {
size_t prev_lock_count = 0;
if (impl) {
prev_lock_count = impl->shared_lock();
}
// dispatch to VPL allocator using READ access mode
mfxStatus sts = MFX_ERR_LOCK_MEMORY;
try {
sts = lockable_allocator.Lock(nullptr, mid, &data);
} catch(...) {
}
// adapter will throw error if VPL frame allocator fails
if (sts != MFX_ERR_NONE) {
impl->unlock_shared();
GAPI_Assert(false && "Cannot lock frame on READ using VPL allocator");
}
return prev_lock_count;
}
size_t LockAdapter::unlock_read(mfxMemId mid, mfxFrameData &data) {
GAPI_DbgAssert(!impl || !is_write_acquired() &&
"Reject `unlock_read` in `write_lock` state");
lockable_allocator.Unlock(nullptr, mid, &data);
return impl ? impl->unlock_shared() : 0;
}
void LockAdapter::write_lock(mfxMemId mid, mfxFrameData &data) {
if (impl) {
// TODO consider using `try_lock` in loop with limited iteration count
// to prevent dead-lock with WARN at least notification
impl->lock();
}
// dispatch to VPL allocator using READ access mode
mfxStatus sts = MFX_ERR_LOCK_MEMORY;
try {
sts = lockable_allocator.Lock(nullptr, mid, &data);
} catch(...) {
}
// adapter will throw error if VPL frame allocator fails
if (sts != MFX_ERR_NONE) {
impl->unlock();
GAPI_Assert(false && "Cannot lock frame on WRITE using VPL allocator");
}
}
bool LockAdapter::is_write_acquired() {
if(!impl) return true;
return impl->owns();
}
void LockAdapter::unlock_write(mfxMemId mid, mfxFrameData &data) {
GAPI_DbgAssert(is_write_acquired() &&
"Reject `unlock_write` for unlocked state");
lockable_allocator.Unlock(nullptr, mid, &data);
if (impl) {
impl->unlock();
}
}
SharedLock* LockAdapter::set_adaptee(SharedLock* new_impl) {
SharedLock* old_impl = impl;
impl = new_impl;
return old_impl;
}
SharedLock* LockAdapter::get_adaptee() {
return impl;
}
NativeHandleAdapter::NativeHandleAdapter(mfxFrameAllocator origin_allocator) :
native_handle_getter(origin_allocator) {
GAPI_DbgAssert(native_handle_getter.GetHDL &&
"Cannot create NativeHandleAdapter for empty origin allocator");
// abandon unusable c-allocator interfaces
// because NativeHandleAdapter requires `GetHDL` only
native_handle_getter.Alloc = nullptr;
native_handle_getter.Free = nullptr;
native_handle_getter.Lock = nullptr;
native_handle_getter.Unlock = nullptr;
native_handle_getter.pthis = nullptr;
}
void NativeHandleAdapter::get_handle(mfxMemId mid, mfxHDL& out) {
if (native_handle_getter.GetHDL(nullptr, mid, &out) != MFX_ERR_NONE) {
GAPI_Assert(nullptr && "Cannot get native handle for resourse by mid");
}
}
DX11AllocationItem::DX11AllocationItem(std::weak_ptr<DX11AllocationRecord> parent,
ID3D11DeviceContext* origin_ctx,
mfxFrameAllocator origin_allocator,
ComSharedPtrGuard<ID3D11Texture2D> tex_ptr,
subresource_id_t subtex_id,
ComPtrGuard<ID3D11Texture2D>&& staging_tex_ptr) :
LockAdapter(origin_allocator),
NativeHandleAdapter(origin_allocator),
shared_device_context(origin_ctx),
texture_ptr(tex_ptr),
subresource_id(subtex_id),
staging_texture_ptr(std::move(staging_tex_ptr)),
observer(parent) {
GAPI_DbgAssert(texture_ptr &&
"Cannot create DX11AllocationItem for empty texture");
GAPI_DbgAssert(staging_texture_ptr &&
"Cannot create DX11AllocationItem for empty staging texture");
GAPI_DbgAssert(observer.lock() &&
"Cannot create DX11AllocationItem for empty parent");
shared_device_context->AddRef();
}
DX11AllocationItem::~DX11AllocationItem() {
release();
observer.reset();
if (shared_device_context) {
shared_device_context->Release();
}
}
void DX11AllocationItem::release() {
auto parent = observer.lock();
GAPI_LOG_DEBUG(nullptr, "texture: " << texture_ptr <<
", subresource id: " << subresource_id <<
", parent: " << parent.get());
cv::util::suppress_unused_warning(parent);
}
ID3D11Texture2D* DX11AllocationItem::get_texture_ptr() {
return texture_ptr.get();
}
ID3D11Texture2D* DX11AllocationItem::get_staging_texture_ptr() {
return staging_texture_ptr.get();
}
DX11AllocationItem::subresource_id_t DX11AllocationItem::get_subresource() const {
return subresource_id;
}
ID3D11DeviceContext* DX11AllocationItem::get_device_ctx_ptr() {
return shared_device_context;//shared_device_context.get();
}
void DX11AllocationItem::on_first_in_impl(mfxFrameData *ptr) {
D3D11_MAP mapType = D3D11_MAP_READ;
UINT mapFlags = D3D11_MAP_FLAG_DO_NOT_WAIT;
shared_device_context->CopySubresourceRegion(get_staging_texture_ptr(), 0,
0, 0, 0,
get_texture_ptr(),
get_subresource(),
nullptr);
HRESULT err = S_OK;
D3D11_MAPPED_SUBRESOURCE lockedRect {};
do {
err = shared_device_context->Map(get_staging_texture_ptr(), 0, mapType, mapFlags, &lockedRect);
if (S_OK != err && DXGI_ERROR_WAS_STILL_DRAWING != err) {
GAPI_LOG_WARNING(nullptr, "Cannot Map staging texture in device context, error: " << std::to_string(HRESULT_CODE(err)));
GAPI_Assert(false && "Cannot Map staging texture in device context");
}
} while (DXGI_ERROR_WAS_STILL_DRAWING == err);
if (FAILED(err)) {
GAPI_LOG_WARNING(nullptr, "Cannot lock frame");
GAPI_Assert(false && "Cannot lock frame");
return ;
}
D3D11_TEXTURE2D_DESC desc {};
get_texture_ptr()->GetDesc(&desc);
switch (desc.Format) {
case DXGI_FORMAT_NV12:
ptr->Pitch = (mfxU16)lockedRect.RowPitch;
ptr->Y = (mfxU8 *)lockedRect.pData;
ptr->UV = (mfxU8 *)lockedRect.pData + desc.Height * lockedRect.RowPitch;
GAPI_Assert(ptr->Y && ptr->UV && "DXGI_FORMAT_NV12 locked frame data is nullptr");
break;
default:
GAPI_LOG_WARNING(nullptr, "Unknown DXGI format: " << desc.Format);
return;
}
}
void DX11AllocationItem::on_last_out_impl(mfxFrameData *ptr) {
shared_device_context->Unmap(get_staging_texture_ptr(), 0);
if (ptr) {
ptr->Pitch = 0;
ptr->U = ptr->V = ptr->Y = 0;
ptr->A = ptr->R = ptr->G = ptr->B = 0;
}
}
mfxStatus DX11AllocationItem::acquire_access(mfxFrameData *ptr) {
if (is_write_acquired()) {
return exclusive_access_acquire_unsafe(ptr);
}
return shared_access_acquire_unsafe(ptr);
}
mfxStatus DX11AllocationItem::release_access(mfxFrameData *ptr) {
if (is_write_acquired()) {
return exclusive_access_release_unsafe(ptr);
}
return shared_access_release_unsafe(ptr);
}
mfxStatus DX11AllocationItem::shared_access_acquire_unsafe(mfxFrameData *ptr) {
GAPI_LOG_DEBUG(nullptr, "acquire READ lock: " << this);
GAPI_LOG_DEBUG(nullptr, "texture: " << get_texture_ptr() <<
", sub id: " << get_subresource());
// shared access requires elastic barrier
// first-in visited thread uses resource mapping on host memory
// subsequent threads reuses mapped memory
//
// exclusive access is prohibited while any one shared access has been obtained
visit_in(ptr);
if (!(ptr->Y && (ptr->UV || (ptr->U && ptr->V)))) {
GAPI_LOG_WARNING(nullptr, "No any data obtained: " << this);
return MFX_ERR_LOCK_MEMORY;
}
GAPI_LOG_DEBUG(nullptr, "READ access granted: " << this);
return MFX_ERR_NONE;
}
mfxStatus DX11AllocationItem::shared_access_release_unsafe(mfxFrameData *ptr) {
GAPI_LOG_DEBUG(nullptr, "releasing READ lock: " << this);
GAPI_LOG_DEBUG(nullptr, "texture: " << get_texture_ptr() <<
", sub id: " << get_subresource());
// releasing shared access requires elastic barrier
// last-out thread must make memory unmapping then and only then no more
// read access is coming. If another read-access goes into critical section
// (or waiting for acees) we must drop off unmapping procedure
visit_out(ptr);
GAPI_LOG_DEBUG(nullptr, "access on READ released: " << this);
return MFX_ERR_NONE;
}
mfxStatus DX11AllocationItem::exclusive_access_acquire_unsafe(mfxFrameData *ptr) {
GAPI_LOG_DEBUG(nullptr, "acquire WRITE lock: " << this);
GAPI_LOG_DEBUG(nullptr, "texture: " << get_texture_ptr() <<
", sub id: " << get_subresource());
D3D11_MAP mapType = D3D11_MAP_WRITE;
UINT mapFlags = D3D11_MAP_FLAG_DO_NOT_WAIT;
HRESULT err = S_OK;
D3D11_MAPPED_SUBRESOURCE lockedRect {};
do {
err = get_device_ctx_ptr()->Map(get_staging_texture_ptr(), 0, mapType, mapFlags, &lockedRect);
if (S_OK != err && DXGI_ERROR_WAS_STILL_DRAWING != err) {
GAPI_LOG_WARNING(nullptr, "Cannot Map staging texture in device context, error: " << std::to_string(HRESULT_CODE(err)));
return MFX_ERR_LOCK_MEMORY;
}
} while (DXGI_ERROR_WAS_STILL_DRAWING == err);
if (FAILED(err)) {
GAPI_LOG_WARNING(nullptr, "Cannot lock frame");
return MFX_ERR_LOCK_MEMORY;
}
D3D11_TEXTURE2D_DESC desc {};
get_texture_ptr()->GetDesc(&desc);
switch (desc.Format) {
case DXGI_FORMAT_NV12:
ptr->Pitch = (mfxU16)lockedRect.RowPitch;
ptr->Y = (mfxU8 *)lockedRect.pData;
ptr->UV = (mfxU8 *)lockedRect.pData + desc.Height * lockedRect.RowPitch;
if (!ptr->Y || !ptr->UV) {
GAPI_LOG_WARNING(nullptr, "DXGI_FORMAT_NV12 locked frame data is nullptr");
return MFX_ERR_LOCK_MEMORY;
}
break;
default:
GAPI_LOG_WARNING(nullptr, "Unknown DXGI format: " << desc.Format);
return MFX_ERR_LOCK_MEMORY;
}
GAPI_LOG_DEBUG(nullptr, "WRITE access granted: " << this);
return MFX_ERR_NONE;
}
mfxStatus DX11AllocationItem::exclusive_access_release_unsafe(mfxFrameData *ptr) {
GAPI_LOG_DEBUG(nullptr, "releasing WRITE lock: " << this);
GAPI_LOG_DEBUG(nullptr, "texture: " << get_texture_ptr() <<
", sub id: " << get_subresource());
get_device_ctx_ptr()->Unmap(get_staging_texture_ptr(), 0);
get_device_ctx_ptr()->CopySubresourceRegion(get_texture_ptr(),
get_subresource(),
0, 0, 0,
get_staging_texture_ptr(), 0,
nullptr);
if (ptr) {
ptr->Pitch = 0;
ptr->U = ptr->V = ptr->Y = 0;
ptr->A = ptr->R = ptr->G = ptr->B = 0;
}
GAPI_LOG_DEBUG(nullptr, "access on WRITE released: " << this);
return MFX_ERR_NONE;
}
DX11AllocationRecord::DX11AllocationRecord() = default;
DX11AllocationRecord::~DX11AllocationRecord() {
GAPI_LOG_DEBUG(nullptr, "record: " << this <<
", subresources count: " << resources.size());
for (AllocationId id : resources) {
delete id;
}
resources.clear();
GAPI_LOG_DEBUG(nullptr, "release final referenced texture: " << texture_ptr.get());
}
void DX11AllocationRecord::init(unsigned int items,
ID3D11DeviceContext* origin_ctx,
mfxFrameAllocator origin_allocator,
ComPtrGuard<ID3D11Texture2D>&& texture,
std::vector<ComPtrGuard<ID3D11Texture2D>> &&staging_textures) {
GAPI_DbgAssert(items != 0 && "Cannot create DX11AllocationRecord with empty items");
GAPI_DbgAssert(items == staging_textures.size() && "Allocation items count and staging size are not equal");
GAPI_DbgAssert(origin_ctx &&
"Cannot create DX11AllocationItem for empty origin_ctx");
auto shared_allocator_copy = origin_allocator;
GAPI_DbgAssert((shared_allocator_copy.Lock && shared_allocator_copy.Unlock) &&
"Cannot create DX11AllocationItem for empty origin allocator");
// abandon unusable c-allocator interfaces
shared_allocator_copy.Alloc = nullptr;
shared_allocator_copy.Free = nullptr;
shared_allocator_copy.pthis = nullptr;
GAPI_LOG_DEBUG(nullptr, "subresources count: " << items << ", text: " << texture.get());
resources.reserve(items);
// no AddRef here, because DX11AllocationRecord receive ownership it here
texture_ptr = createCOMSharedPtrGuard(std::move(texture));
for(unsigned int i = 0; i < items; i++) {
resources.emplace_back(new DX11AllocationItem(get_ptr(), origin_ctx, shared_allocator_copy,
texture_ptr, i, std::move(staging_textures[i])));
}
}
DX11AllocationRecord::Ptr DX11AllocationRecord::get_ptr() {
return shared_from_this();
}
DX11AllocationRecord::AllocationId* DX11AllocationRecord::data() {
return resources.data();
}
size_t DX11AllocationRecord::size() const {
return resources.size();
}
} // namespace onevpl
} // namespace wip
} // namespace gapi
} // namespace cv
#endif // HAVE_D3D11
#endif // HAVE_DIRECTX
#endif // HAVE_ONEVPL

@ -0,0 +1,151 @@
#ifndef GAPI_STREAMING_ONEVPL_ACCEL_DX11_ALLOC_RESOURCE_HPP
#define GAPI_STREAMING_ONEVPL_ACCEL_DX11_ALLOC_RESOURCE_HPP
#include <map>
#include "opencv2/gapi/own/exports.hpp" // GAPI_EXPORTS
#include <opencv2/gapi/util/compiler_hints.hpp>
#ifdef HAVE_ONEVPL
#include "streaming/onevpl/onevpl_export.hpp"
#include "streaming/onevpl/accelerators/utils/elastic_barrier.hpp"
#include "streaming/onevpl/utils.hpp"
#ifdef HAVE_DIRECTX
#ifdef HAVE_D3D11
#pragma comment(lib,"d3d11.lib")
#define D3D11_NO_HELPERS
#define NOMINMAX
#include <d3d11.h>
#include <d3d11_4.h>
#include <codecvt>
#include "opencv2/core/directx.hpp"
#ifdef HAVE_OPENCL
#include <CL/cl_d3d11.h>
#endif // HAVE_OPENCL
#undef D3D11_NO_HELPERS
#undef NOMINMAX
namespace cv {
namespace gapi {
namespace wip {
namespace onevpl {
class SharedLock;
// GAPI_EXPORTS for tests
struct GAPI_EXPORTS LockAdapter {
LockAdapter(mfxFrameAllocator origin_allocator);
size_t read_lock(mfxMemId mid, mfxFrameData &data);
size_t unlock_read(mfxMemId mid, mfxFrameData &data);
void write_lock(mfxMemId mid, mfxFrameData &data);
bool is_write_acquired();
void unlock_write(mfxMemId mid, mfxFrameData &data);
SharedLock* set_adaptee(SharedLock* new_impl);
SharedLock* get_adaptee();
private:
LockAdapter(const LockAdapter&) = delete;
LockAdapter(LockAdapter&&) = delete;
LockAdapter& operator= (const LockAdapter&) = delete;
LockAdapter& operator= (LockAdapter&&) = delete;
mfxFrameAllocator lockable_allocator;
SharedLock* impl;
};
struct GAPI_EXPORTS NativeHandleAdapter {
NativeHandleAdapter(mfxFrameAllocator origin_allocator);
void get_handle(mfxMemId mid, mfxHDL& out);
private:
mfxFrameAllocator native_handle_getter;
};
struct DX11AllocationRecord;
struct DX11AllocationItem : public LockAdapter,
public NativeHandleAdapter,
public elastic_barrier<DX11AllocationItem> {
using subresource_id_t = unsigned int;
friend struct DX11AllocationRecord;
friend class elastic_barrier<DX11AllocationItem>;
~DX11AllocationItem();
void release();
ID3D11Texture2D* get_texture_ptr();
ID3D11Texture2D* get_staging_texture_ptr();
DX11AllocationItem::subresource_id_t get_subresource() const;
ID3D11DeviceContext* get_device_ctx_ptr();
// public transactional access to resources.
// implements dispatching through different access acquisition modes.
// current acquisition mode determined by `LockAdapter` with `is_write_acquired()`
mfxStatus acquire_access(mfxFrameData *ptr);
mfxStatus release_access(mfxFrameData *ptr);
private:
DX11AllocationItem(std::weak_ptr<DX11AllocationRecord> parent,
ID3D11DeviceContext* origin_ctx,
mfxFrameAllocator origin_allocator,
ComSharedPtrGuard<ID3D11Texture2D> texture_ptr,
subresource_id_t subresource_id,
ComPtrGuard<ID3D11Texture2D>&& staging_tex_ptr);
// elastic barrier interface impl
void on_first_in_impl(mfxFrameData *ptr);
void on_last_out_impl(mfxFrameData *ptr);
mfxStatus shared_access_acquire_unsafe(mfxFrameData *ptr);
mfxStatus shared_access_release_unsafe(mfxFrameData *ptr);
mfxStatus exclusive_access_acquire_unsafe(mfxFrameData *ptr);
mfxStatus exclusive_access_release_unsafe(mfxFrameData *ptr);
ID3D11DeviceContext* shared_device_context;
ComSharedPtrGuard<ID3D11Texture2D> texture_ptr;
subresource_id_t subresource_id = 0;
ComPtrGuard<ID3D11Texture2D> staging_texture_ptr;
std::weak_ptr<DX11AllocationRecord> observer;
};
struct DX11AllocationRecord : public std::enable_shared_from_this<DX11AllocationRecord> {
using Ptr = std::shared_ptr<DX11AllocationRecord>;
~DX11AllocationRecord();
template<typename... Args>
static Ptr create(Args&& ...args) {
std::shared_ptr<DX11AllocationRecord> record(new DX11AllocationRecord);
record->init(std::forward<Args>(args)...);
return record;
}
Ptr get_ptr();
// Raw ptr is required as a part of VPL `Mid` c-interface
// which requires contiguous memory
using AllocationId = DX11AllocationItem*;
AllocationId* data();
size_t size() const;
private:
DX11AllocationRecord();
void init(unsigned int items, ID3D11DeviceContext* origin_ctx,
mfxFrameAllocator origin_allocator,
ComPtrGuard<ID3D11Texture2D>&& texture, std::vector<ComPtrGuard<ID3D11Texture2D>> &&staging_textures);
std::vector<AllocationId> resources;
ComSharedPtrGuard<ID3D11Texture2D> texture_ptr;
};
} // namespace onevpl
} // namespace wip
} // namespace gapi
} // namespace cv
#endif // HAVE_D3D11
#endif // HAVE_DIRECTX
#endif // HAVE_ONEVPL
#endif // GAPI_STREAMING_ONEVPL_ACCEL_DX11_ALLOC_RESOURCE_HPP

@ -9,12 +9,7 @@
#include "logger.hpp"
#ifdef HAVE_ONEVPL
#if (MFX_VERSION >= 2000)
#include <vpl/mfxdispatcher.h>
#endif
#include <vpl/mfx.h>
#include "streaming/onevpl/onevpl_export.hpp"
namespace cv {
namespace gapi {
@ -25,24 +20,10 @@ VPLMediaFrameCPUAdapter::VPLMediaFrameCPUAdapter(std::shared_ptr<Surface> surfac
parent_surface_ptr(surface) {
GAPI_Assert(parent_surface_ptr && "Surface is nullptr");
parent_surface_ptr->obtain_lock();
GAPI_LOG_DEBUG(nullptr, "surface: " << parent_surface_ptr->get_handle() <<
", w: " << parent_surface_ptr->get_info().Width <<
", h: " << parent_surface_ptr->get_info().Height <<
", p: " << parent_surface_ptr->get_data().Pitch);
}
VPLMediaFrameCPUAdapter::~VPLMediaFrameCPUAdapter() {
// Each VPLMediaFrameCPUAdapter releases mfx surface counter
// The last VPLMediaFrameCPUAdapter releases shared Surface pointer
// The last surface pointer releases workspace memory
parent_surface_ptr->release_lock();
}
cv::GFrameDesc VPLMediaFrameCPUAdapter::meta() const {
GFrameDesc desc;
const Surface::info_t& info = parent_surface_ptr->get_info();
switch(info.FourCC)
{
@ -50,14 +31,26 @@ cv::GFrameDesc VPLMediaFrameCPUAdapter::meta() const {
throw std::runtime_error("MediaFrame doesn't support I420 type");
break;
case MFX_FOURCC_NV12:
desc.fmt = MediaFormat::NV12;
frame_desc.fmt = MediaFormat::NV12;
break;
default:
throw std::runtime_error("MediaFrame unknown 'fmt' type: " + std::to_string(info.FourCC));
}
desc.size = cv::Size{info.Width, info.Height};
return desc;
frame_desc.size = cv::Size{info.Width, info.Height};
parent_surface_ptr->obtain_lock();
}
VPLMediaFrameCPUAdapter::~VPLMediaFrameCPUAdapter() {
// Each VPLMediaFrameCPUAdapter releases mfx surface counter
// The last VPLMediaFrameCPUAdapter releases shared Surface pointer
// The last surface pointer releases workspace memory
parent_surface_ptr->release_lock();
}
cv::GFrameDesc VPLMediaFrameCPUAdapter::meta() const {
return frame_desc;
}
MediaFrame::View VPLMediaFrameCPUAdapter::access(MediaFrame::Access) {

@ -33,6 +33,7 @@ public:
void deserialize(cv::gapi::s11n::IIStream&) override;
private:
std::shared_ptr<Surface> parent_surface_ptr;
GFrameDesc frame_desc;
};
} // namespace onevpl
} // namespace wip

@ -0,0 +1,232 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
//
// Copyright (C) 2021 Intel Corporation
#include "streaming/onevpl/accelerators/surface/dx11_frame_adapter.hpp"
#include "streaming/onevpl/accelerators/dx11_alloc_resource.hpp"
#include "streaming/onevpl/accelerators/surface/surface.hpp"
#include "logger.hpp"
#ifdef HAVE_ONEVPL
#include "streaming/onevpl/onevpl_export.hpp"
#ifdef HAVE_INF_ENGINE
// For IE classes (ParamMap, etc)
#include <inference_engine.hpp>
#endif // HAVE_INF_ENGINE
namespace cv {
namespace gapi {
namespace wip {
namespace onevpl {
void lock_mid(mfxMemId mid, mfxFrameData &data, MediaFrame::Access mode) {
LockAdapter* alloc_data = reinterpret_cast<LockAdapter *>(mid);
if (mode == MediaFrame::Access::R) {
alloc_data->read_lock(mid, data);
} else {
alloc_data->write_lock(mid, data);
}
}
void unlock_mid(mfxMemId mid, mfxFrameData &data, MediaFrame::Access mode) {
LockAdapter* alloc_data = reinterpret_cast<LockAdapter*>(data.MemId);
if (mode == MediaFrame::Access::R) {
alloc_data->unlock_read(mid, data);
} else {
alloc_data->unlock_write(mid, data);
}
}
VPLMediaFrameDX11Adapter::VPLMediaFrameDX11Adapter(std::shared_ptr<Surface> surface):
parent_surface_ptr(surface) {
GAPI_Assert(parent_surface_ptr && "Surface is nullptr");
const Surface::info_t& info = parent_surface_ptr->get_info();
Surface::data_t& data = parent_surface_ptr->get_data();
GAPI_LOG_DEBUG(nullptr, "surface: " << parent_surface_ptr->get_handle() <<
", w: " << info.Width << ", h: " << info.Height <<
", p: " << data.Pitch);
switch(info.FourCC)
{
case MFX_FOURCC_I420:
throw std::runtime_error("MediaFrame doesn't support I420 type");
break;
case MFX_FOURCC_NV12:
frame_desc.fmt = MediaFormat::NV12;
break;
default:
throw std::runtime_error("MediaFrame unknown 'fmt' type: " + std::to_string(info.FourCC));
}
frame_desc.size = cv::Size{info.Width, info.Height};
LockAdapter* alloc_data = reinterpret_cast<LockAdapter*>(data.MemId);
alloc_data->set_adaptee(this);
parent_surface_ptr->obtain_lock();
}
VPLMediaFrameDX11Adapter::~VPLMediaFrameDX11Adapter() {
// Each VPLMediaFrameDX11Adapter releases mfx surface counter
// The last VPLMediaFrameDX11Adapter releases shared Surface pointer
// The last surface pointer releases workspace memory
Surface::data_t& data = parent_surface_ptr->get_data();
LockAdapter* alloc_data = reinterpret_cast<LockAdapter*>(data.MemId);
alloc_data->set_adaptee(nullptr);
parent_surface_ptr->release_lock();
}
cv::GFrameDesc VPLMediaFrameDX11Adapter::meta() const {
return frame_desc;
}
MediaFrame::View VPLMediaFrameDX11Adapter::access(MediaFrame::Access mode) {
Surface::data_t& data = parent_surface_ptr->get_data();
const Surface::info_t& info = parent_surface_ptr->get_info();
void* frame_id = reinterpret_cast<void*>(this);
GAPI_LOG_DEBUG(nullptr, "START lock frame in surface: " << parent_surface_ptr->get_handle() <<
", frame id: " << frame_id);
// lock MT
lock_mid(data.MemId, data, mode);
GAPI_LOG_DEBUG(nullptr, "FINISH lock frame in surface: " << parent_surface_ptr->get_handle() <<
", frame id: " << frame_id);
using stride_t = typename cv::MediaFrame::View::Strides::value_type;
stride_t pitch = static_cast<stride_t>(data.Pitch);
// NB: make copy for some copyable object, because access release may be happened
// after source/pool destruction, so we need a copy
auto parent_surface_ptr_copy = parent_surface_ptr;
switch(info.FourCC) {
case MFX_FOURCC_I420:
{
GAPI_Assert(data.Y && data.U && data.V && "MFX_FOURCC_I420 frame data is nullptr");
cv::MediaFrame::View::Ptrs pp = { data.Y, data.U, data.V, nullptr };
cv::MediaFrame::View::Strides ss = { pitch, pitch / 2, pitch / 2, 0u };
return cv::MediaFrame::View(std::move(pp), std::move(ss),
[parent_surface_ptr_copy,
frame_id, mode] () {
parent_surface_ptr_copy->obtain_lock();
auto& data = parent_surface_ptr_copy->get_data();
GAPI_LOG_DEBUG(nullptr, "START unlock frame in surface: " << parent_surface_ptr_copy->get_handle() <<
", frame id: " << frame_id);
unlock_mid(data.MemId, data, mode);
GAPI_LOG_DEBUG(nullptr, "FINISH unlock frame in surface: " << parent_surface_ptr_copy->get_handle() <<
", frame id: " << frame_id);
parent_surface_ptr_copy->release_lock();
});
}
case MFX_FOURCC_NV12:
{
if (!data.Y || !data.UV) {
GAPI_LOG_WARNING(nullptr, "Empty data detected!!! for surface: " << parent_surface_ptr->get_handle() <<
", frame id: " << frame_id);
}
GAPI_Assert(data.Y && data.UV && "MFX_FOURCC_NV12 frame data is nullptr");
cv::MediaFrame::View::Ptrs pp = { data.Y, data.UV, nullptr, nullptr };
cv::MediaFrame::View::Strides ss = { pitch, pitch, 0u, 0u };
return cv::MediaFrame::View(std::move(pp), std::move(ss),
[parent_surface_ptr_copy,
frame_id, mode] () {
parent_surface_ptr_copy->obtain_lock();
auto& data = parent_surface_ptr_copy->get_data();
GAPI_LOG_DEBUG(nullptr, "START unlock frame in surface: " << parent_surface_ptr_copy->get_handle() <<
", frame id: " << frame_id);
unlock_mid(data.MemId, data, mode);
GAPI_LOG_DEBUG(nullptr, "FINISH unlock frame in surface: " << parent_surface_ptr_copy->get_handle() <<
", frame id: " << frame_id);
parent_surface_ptr_copy->release_lock();
});
}
break;
default:
throw std::runtime_error("MediaFrame unknown 'fmt' type: " + std::to_string(info.FourCC));
}
}
cv::util::any VPLMediaFrameDX11Adapter::blobParams() const {
#ifdef HAVE_INF_ENGINE
GAPI_Assert(false && "VPLMediaFrameDX11Adapter::blobParams() is not fully operable "
"in G-API streaming. Please waiting for future PRs");
Surface::data_t& data = parent_surface_ptr->get_data();
NativeHandleAdapter* native_handle_getter = reinterpret_cast<NativeHandleAdapter*>(data.MemId);
mfxHDLPair handle{};
native_handle_getter->get_handle(data.MemId, reinterpret_cast<mfxHDL&>(handle));
InferenceEngine::ParamMap params{{"SHARED_MEM_TYPE", "VA_SURFACE"},
{"DEV_OBJECT_HANDLE", handle.first},
{"COLOR_FORMAT", InferenceEngine::ColorFormat::NV12},
{"VA_PLANE",
static_cast<DX11AllocationItem::subresource_id_t>(
reinterpret_cast<uint64_t>(
reinterpret_cast<DX11AllocationItem::subresource_id_t *>(
handle.second)))}};//,
const Surface::info_t& info = parent_surface_ptr->get_info();
InferenceEngine::TensorDesc tdesc({InferenceEngine::Precision::U8,
{1, 3, static_cast<size_t>(info.Height),
static_cast<size_t>(info.Width)},
InferenceEngine::Layout::NCHW});
return std::make_pair(tdesc, params);
#else
GAPI_Assert(false && "VPLMediaFrameDX11Adapter::blobParams() is not implemented");
#endif // HAVE_INF_ENGINE
}
void VPLMediaFrameDX11Adapter::serialize(cv::gapi::s11n::IOStream&) {
GAPI_Assert(false && "VPLMediaFrameDX11Adapter::serialize() is not implemented");
}
void VPLMediaFrameDX11Adapter::deserialize(cv::gapi::s11n::IIStream&) {
GAPI_Assert(false && "VPLMediaFrameDX11Adapter::deserialize() is not implemented");
}
DXGI_FORMAT VPLMediaFrameDX11Adapter::get_dx11_color_format(uint32_t mfx_fourcc) {
switch (mfx_fourcc) {
case MFX_FOURCC_NV12:
return DXGI_FORMAT_NV12;
case MFX_FOURCC_YUY2:
return DXGI_FORMAT_YUY2;
case MFX_FOURCC_RGB4:
return DXGI_FORMAT_B8G8R8A8_UNORM;
case MFX_FOURCC_P8:
case MFX_FOURCC_P8_TEXTURE:
return DXGI_FORMAT_P8;
case MFX_FOURCC_ARGB16:
case MFX_FOURCC_ABGR16:
return DXGI_FORMAT_R16G16B16A16_UNORM;
case MFX_FOURCC_P010:
return DXGI_FORMAT_P010;
case MFX_FOURCC_A2RGB10:
return DXGI_FORMAT_R10G10B10A2_UNORM;
case DXGI_FORMAT_AYUV:
case MFX_FOURCC_AYUV:
return DXGI_FORMAT_AYUV;
default:
return DXGI_FORMAT_UNKNOWN;
}
}
} // namespace onevpl
} // namespace wip
} // namespace gapi
} // namespace cv
#endif // HAVE_ONEVPL

@ -0,0 +1,63 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
//
// Copyright (C) 2021 Intel Corporation
#ifndef GAPI_STREAMING_ONEVPL_ACCELERATORS_SURFACE_DX11_FRAME_ADAPTER_HPP
#define GAPI_STREAMING_ONEVPL_ACCELERATORS_SURFACE_DX11_FRAME_ADAPTER_HPP
#include <memory>
#include <opencv2/gapi/media.hpp>
#include "opencv2/gapi/own/exports.hpp" // GAPI_EXPORTS
#include "streaming/onevpl/accelerators/utils/shared_lock.hpp"
#ifdef HAVE_ONEVPL
#include "streaming/onevpl/onevpl_export.hpp"
#ifdef HAVE_DIRECTX
#ifdef HAVE_D3D11
#define D3D11_NO_HELPERS
#define NOMINMAX
#include <d3d11.h>
#include <codecvt>
#include "opencv2/core/directx.hpp"
#ifdef HAVE_OPENCL
#include <CL/cl_d3d11.h>
#endif
namespace cv {
namespace gapi {
namespace wip {
namespace onevpl {
class Surface;
class VPLMediaFrameDX11Adapter final: public cv::MediaFrame::IAdapter,
public SharedLock {
public:
// GAPI_EXPORTS for tests
GAPI_EXPORTS VPLMediaFrameDX11Adapter(std::shared_ptr<Surface> assoc_surface);
GAPI_EXPORTS ~VPLMediaFrameDX11Adapter();
cv::GFrameDesc meta() const override;
MediaFrame::View access(MediaFrame::Access) override;
// The default implementation does nothing
cv::util::any blobParams() const override;
void serialize(cv::gapi::s11n::IOStream&) override;
void deserialize(cv::gapi::s11n::IIStream&) override;
static DXGI_FORMAT get_dx11_color_format(uint32_t mfx_fourcc);
private:
std::shared_ptr<Surface> parent_surface_ptr;
mfxFrameAllocator allocator;
GFrameDesc frame_desc;
};
} // namespace onevpl
} // namespace wip
} // namespace gapi
} // namespace cv
#undef NOMINMAX
#endif // HAVE_D3D11
#endif // HAVE_DIRECTX
#endif // HAVE_ONEVPL
#endif // GAPI_STREAMING_ONEVPL_ACCELERATORS_SURFACE_DX11_FRAME_ADAPTER_HPP

@ -20,18 +20,20 @@ Surface::Surface(std::unique_ptr<handle_t>&& surf, std::shared_ptr<void> associa
mirrored_locked_count() {
GAPI_Assert(mfx_surface && "Surface is nullptr");
mirrored_locked_count.store(mfx_surface->Data.Locked);
GAPI_LOG_DEBUG(nullptr, "create surface: " << mfx_surface <<
", locked count: " << mfx_surface->Data.Locked);
}
Surface::~Surface() {
GAPI_LOG_DEBUG(nullptr, "destroy surface: " << mfx_surface <<
", worspace memory counter: " << workspace_memory_ptr.use_count());
", worspace memory counter: " <<
workspace_memory_ptr.use_count());
}
std::shared_ptr<Surface> Surface::create_surface(std::unique_ptr<handle_t>&& surf,
std::shared_ptr<void> accociated_memory) {
Surface::info_t& info = surf->Info;
info.FourCC = MFX_FOURCC_NV12;
surface_ptr_t ret {new Surface(std::move(surf), accociated_memory)};
return ret;
}
@ -48,14 +50,16 @@ const Surface::data_t& Surface::get_data() const {
return mfx_surface->Data;
}
Surface::data_t& Surface::get_data() {
return const_cast<Surface::data_t&>(static_cast<const Surface*>(this)->get_data());
}
size_t Surface::get_locks_count() const {
return mirrored_locked_count.load();
return mirrored_locked_count.load() + mfx_surface->Data.Locked;
}
size_t Surface::obtain_lock() {
size_t locked_count = mirrored_locked_count.fetch_add(1);
GAPI_Assert(locked_count < std::numeric_limits<mfxU16>::max() && "Too many references ");
mfx_surface->Data.Locked = static_cast<mfxU16>(locked_count + 1);
GAPI_LOG_DEBUG(nullptr, "surface: " << mfx_surface.get() <<
", locked times: " << locked_count + 1);
return locked_count; // return preceding value
@ -63,9 +67,7 @@ size_t Surface::obtain_lock() {
size_t Surface::release_lock() {
size_t locked_count = mirrored_locked_count.fetch_sub(1);
GAPI_Assert(locked_count < std::numeric_limits<mfxU16>::max() && "Too many references ");
GAPI_Assert(locked_count && "Surface lock counter is invalid");
mfx_surface->Data.Locked = static_cast<mfxU16>(locked_count - 1);
GAPI_LOG_DEBUG(nullptr, "surface: " << mfx_surface.get() <<
", locked times: " << locked_count - 1);
return locked_count; // return preceding value

@ -13,12 +13,7 @@
#include "opencv2/gapi/own/exports.hpp" // GAPI_EXPORTS
#ifdef HAVE_ONEVPL
#if (MFX_VERSION >= 2000)
#include <vpl/mfxdispatcher.h>
#endif
#include <vpl/mfx.h>
#include "streaming/onevpl/onevpl_export.hpp"
namespace cv {
namespace gapi {
@ -45,24 +40,21 @@ namespace onevpl {
* - @ref Surface::obtain_lock() against @ref Surface::release_lock()
* - @ref Surface::release_lock() against @ref Surface::release_lock()
*/
class Surface {
using handle_t = mfxFrameSurface1;
std::shared_ptr<void> workspace_memory_ptr;
std::unique_ptr<handle_t> mfx_surface;
std::atomic<size_t> mirrored_locked_count;
class GAPI_EXPORTS Surface final { // GAPI_EXPORTS for tests
public:
using handle_t = mfxFrameSurface1;
using info_t = mfxFrameInfo;
using data_t = mfxFrameData;
// GAPI_EXPORTS for tests
GAPI_EXPORTS static std::shared_ptr<Surface> create_surface(std::unique_ptr<handle_t>&& surf,
std::shared_ptr<void> accociated_memory);
GAPI_EXPORTS ~Surface();
GAPI_EXPORTS handle_t* get_handle() const;
GAPI_EXPORTS const info_t& get_info() const;
GAPI_EXPORTS const data_t& get_data() const;
static std::shared_ptr<Surface> create_surface(std::unique_ptr<handle_t>&& surf,
std::shared_ptr<void> accociated_memory);
~Surface();
handle_t* get_handle() const;
const info_t& get_info() const;
const data_t& get_data() const;
data_t& get_data();
/**
* Extract value thread-safe lock counter (see @ref Surface description).
@ -71,7 +63,7 @@ public:
*
* @return fetched locks count.
*/
GAPI_EXPORTS size_t get_locks_count() const;
size_t get_locks_count() const;
/**
* Atomically increase value of thread-safe lock counter (see @ref Surface description).
@ -80,7 +72,7 @@ public:
*
* @return locks count just before its increasing.
*/
GAPI_EXPORTS size_t obtain_lock();
size_t obtain_lock();
/**
* Atomically decrease value of thread-safe lock counter (see @ref Surface description).
@ -89,9 +81,13 @@ public:
*
* @return locks count just before its decreasing.
*/
GAPI_EXPORTS size_t release_lock();
size_t release_lock();
private:
Surface(std::unique_ptr<handle_t>&& surf, std::shared_ptr<void> accociated_memory);
std::shared_ptr<void> workspace_memory_ptr;
std::unique_ptr<handle_t> mfx_surface;
std::atomic<size_t> mirrored_locked_count;
};
using surface_ptr_t = std::shared_ptr<Surface>;

@ -8,11 +8,7 @@
#include "opencv2/gapi/own/exports.hpp" // GAPI_EXPORTS
#ifdef HAVE_ONEVPL
#if (MFX_VERSION >= 2000)
#include <vpl/mfxdispatcher.h>
#endif
#include <vpl/mfx.h>
#include "streaming/onevpl/onevpl_export.hpp"
namespace cv {
namespace gapi {

@ -0,0 +1,296 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
//
// Copyright (C) 2021 Intel Corporation
#ifndef GAPI_STREAMING_ONEVPL_ACCELERATORS_UTILS_ELASTIC_BARRIER_HPP
#define GAPI_STREAMING_ONEVPL_ACCELERATORS_UTILS_ELASTIC_BARRIER_HPP
#include <atomic>
namespace cv {
namespace gapi {
namespace wip {
namespace onevpl {
template<typename Impl>
class elastic_barrier {
public:
using self_t = Impl;
elastic_barrier() :
incoming_requests(),
outgoing_requests(),
pending_requests(),
reinit(false) {
}
self_t* get_self() {
return static_cast<self_t*>(this);
}
template<typename ...Args>
void visit_in (Args&& ...args) {
on_lock(std::forward<Args>(args)...);
}
template<typename ...Args>
void visit_out (Args&& ...args) {
on_unlock(std::forward<Args>(args)...);
}
protected:
~elastic_barrier() = default;
private:
std::atomic<size_t> incoming_requests;
std::atomic<size_t> outgoing_requests;
std::atomic<size_t> pending_requests;
std::atomic<bool> reinit;
template<typename ...Args>
void on_first_in(Args&& ...args) {
get_self()->on_first_in_impl(std::forward<Args>(args)...);
}
template<typename ...Args>
void on_last_out(Args&& ...args) {
get_self()->on_last_out_impl(std::forward<Args>(args)...);
}
template<typename ...Args>
void on_lock(Args&& ...args) {
// Read access is more complex
// each `incoming` request must check in before acquire resource
size_t thread_id = incoming_requests.fetch_add(1);
if (thread_id == 0) {
/*
* only one `incoming` request is allowable to init resource
* at first time
* let's filter out the first one by `thread_id`
*
* The first one `incoming` request becomes main `incoming` request
* */
if (outgoing_requests.load() == 0) {
get_self()->on_first_in(std::forward<Args>(args)...);
/*
* The main `incoming` request finished resource initialization
* and became `outgoing`
*
* Non empty `outgoing` count means that
* other further `incoming` (or busy-wait) requests
* are getting on with its job without resource initialization,
* because main `incoming` request has already initialized it at here
* */
outgoing_requests.fetch_add(1);
return;
}
return;
} else {
/*
* CASE 1)
*
* busy wait for others `incoming` requests for resource initialization
* besides main `incoming` request which are getting on
* resource initialization at this point
*
* */
// OR
/*
* CASE 2)
*
* busy wait for ALL `incoming` request for resource initialization
* including main `incoming` request. It will happen if
* new `incoming` requests had came here while resource was getting on deinit
* in `on_unlock` in another processing thread.
* In this case no actual main `incoming` request is available and
* all `incoming` requests must be in busy-wait stare
*
* */
// Each `incoming` request became `busy-wait` request
size_t busy_thread_id = pending_requests.fetch_add(1);
/*
* CASE 1)
*
* Non empty `outgoing` requests count means that other further `incoming` or
* `busy-wait` request are getting on with its job
* without resource initialization because
* main thread has already initialized it at here
* */
while (outgoing_requests.load() == 0) {
// OR
/*
* CASE 2)
*
* In case of NO master `incoming `request is available and doesn't
* provide resource initialization. All `incoming` requests must be in
* busy-wait state.
* If it is not true then CASE 1) is going on
*
* OR
*
* `on_unlock` is in deinitialization phase in another thread.
* Both cases mean busy-wait state here
* */
if (pending_requests.load() == incoming_requests.load()) {
/*
* CASE 2) ONLY
*
* It will happen if 'on_unlock` in another thread
* finishes its execution only
*
* `on_unlock` in another thread might finished with either
* deinitialization action or without deinitialization action
* (the call off deinitialization case)
*
* We must not continue at here (without reinit)
* if deinitialization happens in `on_unlock` in another thread.
* So try it on
* */
// only single `busy-wait` request must make sure about possible
// deinitialization. So first `busy-wait` request becomes
// main `busy-wait` request
if (busy_thread_id == 0) {
bool expected_reinit = true;
if (!reinit.compare_exchange_strong(expected_reinit, false)) {
/*
* deinitialization called off in `on_unlock`
* because new `incoming` request had appeared at here before
* `on_unlock` started deinit procedure in another thread.
* So no reinit required because no deinit had happended
*
* main `busy-wait` request must break busy-wait state
* and become `outgoing` request.
* Non empty `outgoing` count means that other
* further `incoming` requests or
* `busy-wait` requests are getting on with its job
* without resource initialization/reinitialization
* because no deinit happened in `on_unlock`
* in another thread
* */
break; //just quit busy loop
} else {
/* Deinitialization had happened in `on_unlock`
* in another thread right before
* new `incoming` requests appeared.
* So main `busy-wait` request must start reinit procedure
*/
get_self()->on_first_in(std::forward<Args>(args)...);
/*
* Main `busy-wait` request has finished reinit procedure
* and becomes `outgong` request.
* Non empty `outgoing` count means that other
* further `incoming` requests or
* `busy-wait` requests are getting on with its job
* without resource initialization because
* main `busy-wait` request
* has already re-initialized it at here
*/
outgoing_requests.fetch_add(1);
pending_requests.fetch_sub(1);
return;
}
}
}
}
// All non main requests became `outgoing` and look at on initialized resource
outgoing_requests++;
// Each `busy-wait` request are not busy-wait now
pending_requests.fetch_sub(1);
}
return;
}
template<typename ...Args>
void on_unlock(Args&& ...args) {
// Read unlock
/*
* Each released `outgoing` request checks out to doesn't use resource anymore.
* The last `outgoing` request becomes main `outgoing` request and
* must deinitialize resource if no `incoming` or `busy-wait` requests
* are waiting for it
*/
size_t thread_id = outgoing_requests.fetch_sub(1);
if (thread_id == 1) {
/*
* Make sure that no another `incoming` (including `busy-wait)
* exists.
* But beforehand its must make sure that no `incoming` or `pending`
* requests are exist.
*
* The main `outgoing` request is an one of `incoming` request
* (it is the oldest one in the current `incoming` bunch) and still
* holds resource in initialized state (thus we compare with 1).
* We must not deinitialize resource before decrease
* `incoming` requests counter because
* after it has got 0 value in `on_lock` another thread
* will start initialize resource procedure which will get conflict
* with current deinitialize procedure
*
* From this point, all `on_lock` request in another thread would
* become `busy-wait` without reaching main `incoming` state (CASE 2)
* */
if (incoming_requests.load() == 1) {
/*
* The main `outgoing` request is ready to deinit shared resource
* in unconflicting manner.
*
* This is a critical section for single thread for main `outgoing`
* request
*
* CASE 2 only available in `on_lock` thread
* */
get_self()->on_last_out(std::forward<Args>(args)...);
/*
* Before main `outgoinq` request become released it must notify
* subsequent `busy-wait` requests in `on_lock` in another thread
* that main `busy-wait` must start reinit resource procedure
* */
reinit.store(true);
/*
* Deinitialize procedure is finished and main `outgoing` request
* (it is the oldest one in `incoming` request) must become released
*
* Right after when we decrease `incoming` counter
* the condition for equality
* `busy-wait` and `incoming` counter will become true (CASE 2 only)
* in `on_lock` in another threads. After that
* a main `busy-wait` request would check `reinit` condition
* */
incoming_requests.fetch_sub(1);
return;
}
/*
* At this point we have guarantee that new `incoming` requests
* had became increased in `on_lock` in another thread right before
* current thread deinitialize resource.
*
* So call off deinitialization procedure here
* */
}
incoming_requests.fetch_sub(1);
}
elastic_barrier(const elastic_barrier&) = delete;
elastic_barrier(elastic_barrier&&) = delete;
elastic_barrier& operator() (const elastic_barrier&) = delete;
elastic_barrier& operator() (elastic_barrier&&) = delete;
};
} // namespace onevpl
} // namespace wip
} // namespace gapi
} // namespace cv
#endif // GAPI_STREAMING_ONEVPL_ACCELERATORS_UTILS_ELASTIC_BARRIER_HPP

@ -0,0 +1,95 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
//
// Copyright (C) 2021 Intel Corporation
#include <thread>
#include "streaming/onevpl/accelerators/utils/shared_lock.hpp"
namespace cv {
namespace gapi {
namespace wip {
namespace onevpl {
SharedLock::SharedLock() {
exclusive_lock.store(false);
shared_counter.store(0);
}
size_t SharedLock::shared_lock() {
size_t prev = 0;
bool in_progress = false;
bool pred_excl = exclusive_lock.load();
do {
if (!pred_excl) {
// if no exclusive lock then start shared lock transaction
prev = shared_counter.fetch_add(1);
in_progress = true; // transaction is in progress
} else {
if (in_progress) {
in_progress = false;
shared_counter.fetch_sub(1);
}
std::this_thread::yield();
}
// test if exclusive lock happened before
pred_excl = exclusive_lock.load();
} while (pred_excl || !in_progress);
return prev;
}
size_t SharedLock::unlock_shared() {
return shared_counter.fetch_sub(1);
}
void SharedLock::lock() {
bool in_progress = false;
size_t prev_shared = shared_counter.load();
do {
if (prev_shared == 0) {
bool expected = false;
while (!exclusive_lock.compare_exchange_strong(expected, true)) {
expected = false;
std::this_thread::yield();
}
in_progress = true;
} else {
if (in_progress) {
in_progress = false;
exclusive_lock.store(false);
}
std::this_thread::yield();
}
prev_shared = shared_counter.load();
} while (prev_shared != 0 || !in_progress);
}
bool SharedLock::try_lock() {
if (shared_counter.load() != 0) {
return false;
}
bool expected = false;
if (exclusive_lock.compare_exchange_strong(expected, true)) {
if (shared_counter.load() == 0) {
return true;
} else {
exclusive_lock.store(false);
}
}
return false;
}
void SharedLock::unlock() {
exclusive_lock.store(false);
}
bool SharedLock::owns() const {
return exclusive_lock.load();
}
} // namespace onevpl
} // namespace wip
} // namespace gapi
} // namespace cv

@ -0,0 +1,47 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
//
// Copyright (C) 2021 Intel Corporation
#ifndef GAPI_STREAMING_ONEVPL_ACCELERATORS_SURFACE_SHARED_LOCK_HPP
#define GAPI_STREAMING_ONEVPL_ACCELERATORS_SURFACE_SHARED_LOCK_HPP
#include <atomic>
#include <memory>
#include "opencv2/gapi/own/exports.hpp" // GAPI_EXPORTS
namespace cv {
namespace gapi {
namespace wip {
namespace onevpl {
class GAPI_EXPORTS SharedLock {
public:
SharedLock();
~SharedLock() = default;
size_t shared_lock();
size_t unlock_shared();
void lock();
bool try_lock();
void unlock();
bool owns() const;
private:
SharedLock(const SharedLock&) = delete;
SharedLock& operator= (const SharedLock&) = delete;
SharedLock(SharedLock&&) = delete;
SharedLock& operator== (SharedLock&&) = delete;
std::atomic<bool> exclusive_lock;
std::atomic<size_t> shared_counter;
};
} // namespace onevpl
} // namespace wip
} // namespace gapi
} // namespace cv
#endif // GAPI_STREAMING_ONEVPL_ACCELERATORS_SURFACE_SHARED_LOCK_HPP

@ -5,7 +5,7 @@
// Copyright (C) 2021 Intel Corporation
#ifdef HAVE_ONEVPL
#include <vpl/mfxvideo.h>
#include "streaming/onevpl/onevpl_export.hpp"
#include <opencv2/gapi/own/assert.hpp>
#include <opencv2/gapi/util/variant.hpp>
@ -44,7 +44,7 @@ CfgParamDeviceSelector::CfgParamDeviceSelector(const CfgParams& cfg_params) :
auto accel_mode_it =
std::find_if(cfg_params.begin(), cfg_params.end(), [] (const CfgParam& value) {
return value.get_name() == "mfxImplDescription.AccelerationMode";
return value.get_name() == CfgParam::acceleration_mode_name();
});
if (accel_mode_it == cfg_params.end())
{
@ -128,9 +128,11 @@ CfgParamDeviceSelector::CfgParamDeviceSelector(const CfgParams& cfg_params) :
suggested_device = IDeviceSelector::create<Device>(hw_handle, "GPU", AccelType::DX11);
suggested_context = IDeviceSelector::create<Context>(device_context, AccelType::DX11);
#else
GAPI_LOG_WARNING(nullptr, "Unavailable \"mfxImplDescription.AccelerationMode: MFX_ACCEL_MODE_VIA_D3D11\""
GAPI_LOG_WARNING(nullptr, "Unavailable \"" << CfgParam::acceleration_mode_name() << ": MFX_ACCEL_MODE_VIA_D3D11\""
"was chosen for current project configuration");
throw std::logic_error("Unsupported \"mfxImplDescription.AccelerationMode: MFX_ACCEL_MODE_VIA_D3D11\"");
throw std::logic_error(std::string("Unsupported \"") +
CfgParam::acceleration_mode_name() +
": MFX_ACCEL_MODE_VIA_D3D11\"");
#endif // HAVE_DIRECTX
#endif // HAVE_D3D11
break;
@ -140,7 +142,8 @@ CfgParamDeviceSelector::CfgParamDeviceSelector(const CfgParams& cfg_params) :
break;
}
default:
throw std::logic_error("Unsupported \"mfxImplDescription.AccelerationMode\" requested: " +
throw std::logic_error(std::string("Unsupported \"") +
CfgParam::acceleration_mode_name() +"\" requested: " +
std::to_string(accel_mode.Data.U32));
break;
}
@ -154,14 +157,16 @@ CfgParamDeviceSelector::CfgParamDeviceSelector(Device::Ptr device_ptr,
suggested_context(IDeviceSelector::create<Context>(nullptr, AccelType::HOST)) {
auto accel_mode_it =
std::find_if(cfg_params.begin(), cfg_params.end(), [] (const CfgParam& value) {
return value.get_name() == "mfxImplDescription.AccelerationMode";
return value.get_name() == CfgParam::acceleration_mode_name();
});
if (accel_mode_it == cfg_params.end()) {
GAPI_LOG_WARNING(nullptr, "Cannot deternime \"device_ptr\" type. "
"Make sure a param \"mfxImplDescription.AccelerationMode\" "
"Make sure a param \"" << CfgParam::acceleration_mode_name() << "\" "
"presents in configurations and has correct value according to "
"\"device_ptr\" type");
throw std::logic_error("Missing \"mfxImplDescription.AccelerationMode\" param");
throw std::logic_error(std::string("Missing \"") +
CfgParam::acceleration_mode_name() +
"\" param");
}
GAPI_LOG_DEBUG(nullptr, "Turn on HW acceleration support for device: " <<
@ -169,7 +174,7 @@ CfgParamDeviceSelector::CfgParamDeviceSelector(Device::Ptr device_ptr,
", context: " << ctx_ptr);
if (!device_ptr) {
GAPI_LOG_WARNING(nullptr, "Empty \"device_ptr\" is not allowed when "
"param \"mfxImplDescription.AccelerationMode\" existed");
"param \"" << CfgParam::acceleration_mode_name() << "\" existed");
throw std::logic_error("Invalid param: \"device_ptr\"");
}
@ -191,23 +196,36 @@ CfgParamDeviceSelector::CfgParamDeviceSelector(Device::Ptr device_ptr,
suggested_context = IDeviceSelector::create<Context>(ctx_ptr, AccelType::DX11);
ID3D11DeviceContext* dx_ctx_ptr =
reinterpret_cast<ID3D11DeviceContext*>(suggested_context.get_ptr());
// oneVPL recommendation
{
ID3D11Multithread *pD11Multithread = nullptr;
dx_ctx_ptr->QueryInterface(IID_PPV_ARGS(&pD11Multithread));
pD11Multithread->SetMultithreadProtected(true);
pD11Multithread->Release();
}
dx_ctx_ptr->AddRef();
#else
GAPI_LOG_WARNING(nullptr, "Unavailable \"mfxImplDescription.AccelerationMode: MFX_ACCEL_MODE_VIA_D3D11\""
GAPI_LOG_WARNING(nullptr, "Unavailable \"" << CfgParam::acceleration_mode_name() <<
": MFX_ACCEL_MODE_VIA_D3D11\""
"was chosen for current project configuration");
throw std::logic_error("Unsupported \"mfxImplDescription.AccelerationMode: MFX_ACCEL_MODE_VIA_D3D11\"");
throw std::logic_error(std::string("Unsupported \"") +
CfgParam::acceleration_mode_name() + ": MFX_ACCEL_MODE_VIA_D3D11\"");
#endif // HAVE_DIRECTX
#endif // HAVE_D3D11
break;
}
case MFX_ACCEL_MODE_NA: {
GAPI_LOG_WARNING(nullptr, "Incompatible \"mfxImplDescription.AccelerationMode: MFX_ACCEL_MODE_NA\" with "
GAPI_LOG_WARNING(nullptr, "Incompatible \"" << CfgParam::acceleration_mode_name() <<
": MFX_ACCEL_MODE_NA\" with "
"\"device_ptr\" and \"ctx_ptr\" arguments. "
"You should not clarify these arguments with \"MFX_ACCEL_MODE_NA\" mode");
throw std::logic_error("Incompatible param: MFX_ACCEL_MODE_NA");
}
default:
throw std::logic_error("Unsupported \"mfxImplDescription.AccelerationMode\" requested: " +
throw std::logic_error(std::string("Unsupported \"") + CfgParam::acceleration_mode_name() +
"\" requested: " +
std::to_string(accel_mode.Data.U32));
break;
}

@ -86,6 +86,34 @@ CfgParam::CfgParam (const std::string& param_name, value_t&& param_value, bool i
CfgParam::~CfgParam() = default;
CfgParam CfgParam::create_frames_pool_size(uint64_t value) {
return CfgParam::create(CfgParam::frames_pool_size_name(), value, false);
}
CfgParam CfgParam::create_acceleration_mode(uint32_t value) {
return CfgParam::create(CfgParam::acceleration_mode_name(), value);
}
CfgParam CfgParam::create_acceleration_mode(const char* value) {
return CfgParam::create(CfgParam::acceleration_mode_name(), std::string(value));
}
CfgParam CfgParam::create_decoder_id(uint32_t value) {
return CfgParam::create(CfgParam::decoder_id_name(), value);
}
CfgParam CfgParam::create_decoder_id(const char* value) {
return CfgParam::create(CfgParam::decoder_id_name(), std::string(value));
}
CfgParam CfgParam::create_implementation(uint32_t value) {
return CfgParam::create(CfgParam::implementation_name(), value);
}
CfgParam CfgParam::create_implementation(const char* value) {
return CfgParam::create(CfgParam::implementation_name(), std::string(value));
}
CfgParam& CfgParam::operator=(const CfgParam& src) {
if (this != &src) {
m_priv = src.m_priv;

@ -22,19 +22,16 @@ namespace onevpl {
template <>
struct ParamCreator<CfgParam> {
template<typename ValueType>
CfgParam create (const std::string& name, ValueType&& value) {
CfgParam create (const std::string& name, ValueType&& value, bool is_major_flag = false) {
return CfgParam::create(name, std::forward<ValueType>(value), is_major_flag);
}
bool is_major_flag = false;
};
template <>
struct ParamCreator<mfxVariant> {
template<typename ValueType>
mfxVariant create (const std::string& name, ValueType&& value) {
static_assert(std::is_same<typename std::decay<ValueType>::type, mfxU32>::value,
"ParamCreator<mfxVariant> supports mfxU32 at the moment. "
"Feel free to extend for more types");
mfxVariant create (const std::string& name, ValueType&& value, bool is_major_flag = false) {
cv::util::suppress_unused_warning(is_major_flag);
return create_impl(name, value);
}
private:
@ -44,6 +41,18 @@ private:
ret.Data.U32 = value;
return ret;
}
mfxVariant create_impl(const std::string&, mfxI64 value) {
mfxVariant ret;
ret.Type = MFX_VARIANT_TYPE_I64;
ret.Data.I64 = value;
return ret;
}
mfxVariant create_impl(const std::string&, mfxU64 value) {
mfxVariant ret;
ret.Type = MFX_VARIANT_TYPE_U64;
ret.Data.U64 = value;
return ret;
}
};
template<typename ValueType>
@ -67,14 +76,16 @@ std::vector<ValueType> get_params_from_string(const std::string& str) {
std::string value = line.substr(name_endline_pos + 2);
ParamCreator<ValueType> creator;
if (name == "mfxImplDescription.Impl") {
if (name == CfgParam::implementation_name()) {
ret.push_back(creator.create<mfxU32>(name, cstr_to_mfx_impl(value.c_str())));
} else if (name == "mfxImplDescription.mfxDecoderDescription.decoder.CodecID") {
} else if (name == CfgParam::decoder_id_name()) {
ret.push_back(creator.create<mfxU32>(name, cstr_to_mfx_codec_id(value.c_str())));
} else if (name == "mfxImplDescription.AccelerationMode") {
} else if (name == CfgParam::acceleration_mode_name()) {
ret.push_back(creator.create<mfxU32>(name, cstr_to_mfx_accel_mode(value.c_str())));
} else if (name == "mfxImplDescription.ApiVersion.Version") {
ret.push_back(creator.create<mfxU32>(name, cstr_to_mfx_version(value.c_str())));
} else if (name == CfgParam::frames_pool_size_name()) {
ret.push_back(creator.create(name, strtoull_or_throw(value.c_str()), false));
} else {
GAPI_LOG_DEBUG(nullptr, "Cannot parse configuration param, name: " << name <<
", value: " << value);
@ -116,6 +127,32 @@ mfxVariant cfg_param_to_mfx_variant(const CfgParam& cfg_val) {
}), cfg_val.get_value());
return ret;
}
size_t strtoull_or_throw(const char* str) {
char *end_ptr = nullptr;
errno = 0;
size_t ret = strtoull(str, &end_ptr, 10);
if ((end_ptr == str) ||
((ret == LONG_MAX || ret == LONG_MIN) && errno == ERANGE)) {
// nothing parsed from the string, handle errors or exit
GAPI_LOG_WARNING(nullptr, "strtoull failed for: " << str);
GAPI_Assert(false && "strtoull_or_throw");
}
return ret;
}
int64_t strtoll_or_throw(const char* str) {
char *end_ptr = nullptr;
errno = 0;
int64_t ret = strtoll(str, &end_ptr, 10);
if ((end_ptr == str) ||
((ret == LONG_MAX || ret == LONG_MIN) && errno == ERANGE)) {
// nothing parsed from the string, handle errors or exit
GAPI_LOG_WARNING(nullptr, "strtoll failed for: " << str);
GAPI_Assert(false && "strtoll_or_throw");
}
return ret;
}
} // namespace onevpl
} // namespace wip
} // namespace gapi

@ -8,12 +8,7 @@
#define GAPI_STREAMING_ONEVPL_CFG_PARAM_PARSER_HPP
#ifdef HAVE_ONEVPL
#if (MFX_VERSION >= 2000)
#include <vpl/mfxdispatcher.h>
#endif // MFX_VERSION
#include <vpl/mfx.h>
#include <vpl/mfxvideo.h>
#include "streaming/onevpl/onevpl_export.hpp"
#include <map>
#include <string>
@ -31,10 +26,14 @@ std::vector<ValueType> get_params_from_string(const std::string& str);
template <typename ReturnType>
struct ParamCreator {
template<typename ValueType>
ReturnType create(const std::string& name, ValueType&& value);
ReturnType create(const std::string& name, ValueType&& value, bool is_major = false);
};
mfxVariant cfg_param_to_mfx_variant(const CfgParam& value);
size_t strtoull_or_throw(const char* str);
int64_t strtoll_or_throw(const char* str);
} // namespace onevpl
} // namespace wip
} // namespace gapi

@ -2,8 +2,7 @@
#define GAPI_STREAMING_ONEVPL_DATA_PROVIDER_DEFINES_HPP
#ifdef HAVE_ONEVPL
#include <vpl/mfxcommon.h>
#include <vpl/mfxvideo.h>
#include "streaming/onevpl/onevpl_export.hpp"
#endif // HAVE_ONEVPL
#include <opencv2/gapi/own/assert.hpp>

@ -25,13 +25,14 @@ IDataProvider::Ptr DataProviderDispatcher::create(const std::string& file_path,
// Look-up CodecId from input params
// If set then raw data provider is preferred
GAPI_LOG_DEBUG(nullptr, "try find explicit cfg param\"mfxImplDescription.mfxDecoderDescription.decoder.CodecID\"");
GAPI_LOG_DEBUG(nullptr, "try find explicit cfg param \"" <<
CfgParam::decoder_id_name() <<"\"");
auto codec_it =
std::find_if(cfg_params.begin(), cfg_params.end(), [] (const CfgParam& value) {
return value.get_name() == "mfxImplDescription.mfxDecoderDescription.decoder.CodecID";
return value.get_name() == CfgParam::decoder_id_name();
});
if (codec_it != cfg_params.end()) {
GAPI_LOG_DEBUG(nullptr, "Dispatcher found \"mfxImplDescription.mfxDecoderDescription.decoder.CodecID\""
GAPI_LOG_DEBUG(nullptr, "Dispatcher found \"" << CfgParam::decoder_id_name() << "\""
" so try on raw data provider at first");
try {

@ -5,8 +5,7 @@
// Copyright (C) 2021 Intel Corporation
#ifdef HAVE_ONEVPL
#include <vpl/mfxvideo.h>
#include <vpl/mfxjpeg.h>
#include "streaming/onevpl/onevpl_export.hpp"
#endif // HAVE_ONEVPL
#include <errno.h>

@ -13,7 +13,7 @@
#include <queue>
#ifdef HAVE_ONEVPL
#include <vpl/mfxvideo.h>
#include "streaming/onevpl/onevpl_export.hpp"
#ifdef _WIN32
#define NOMINMAX

@ -16,6 +16,7 @@
#include "streaming/onevpl/engine/decode/decode_session.hpp"
#include "streaming/onevpl/accelerators/accel_policy_interface.hpp"
#include "streaming/onevpl/accelerators/surface/surface.hpp"
#include "streaming/onevpl/cfg_params_parser.hpp"
#include "streaming/onevpl/utils.hpp"
#include "logger.hpp"
@ -24,95 +25,6 @@ namespace cv {
namespace gapi {
namespace wip {
namespace onevpl {
/* UTILS */
mfxU32 GetSurfaceSize(mfxU32 FourCC, mfxU32 width, mfxU32 height) {
mfxU32 nbytes = 0;
mfxU32 half_width = width / 2;
mfxU32 half_height = height / 2;
switch (FourCC) {
case MFX_FOURCC_I420:
case MFX_FOURCC_NV12:
nbytes = width * height + 2 * half_width * half_height;
break;
case MFX_FOURCC_I010:
case MFX_FOURCC_P010:
nbytes = width * height + 2 * half_width * half_height;
nbytes *= 2;
break;
case MFX_FOURCC_RGB4:
nbytes = width * height * 4;
break;
default:
GAPI_LOG_WARNING(nullptr, "Unsupported FourCC requested: " << FourCC);
GAPI_Assert(false && "Unsupported FourCC requested");
break;
}
return nbytes;
}
surface_ptr_t create_surface_RGB4(mfxFrameInfo frameInfo,
std::shared_ptr<void> out_buf_ptr,
size_t out_buf_ptr_offset,
size_t out_buf_size)
{
mfxU8* buf = reinterpret_cast<mfxU8*>(out_buf_ptr.get());
mfxU16 surfW = frameInfo.Width * 4;
mfxU16 surfH = frameInfo.Height;
(void)surfH;
// TODO more intelligent check
if (out_buf_size <= out_buf_ptr_offset) {
throw std::runtime_error(std::string("Insufficient buffer size: ") +
std::to_string(out_buf_size) + ", buffer offset: " +
std::to_string(out_buf_ptr_offset) +
", expected surface width: " + std::to_string(surfW) +
", height: " + std::to_string(surfH));
}
std::unique_ptr<mfxFrameSurface1> handle(new mfxFrameSurface1);
memset(handle.get(), 0, sizeof(mfxFrameSurface1));
handle->Info = frameInfo;
handle->Data.B = buf + out_buf_ptr_offset;
handle->Data.G = handle->Data.B + 1;
handle->Data.R = handle->Data.B + 2;
handle->Data.A = handle->Data.B + 3;
handle->Data.Pitch = surfW;
return Surface::create_surface(std::move(handle), out_buf_ptr);
}
surface_ptr_t create_surface_other(mfxFrameInfo frameInfo,
std::shared_ptr<void> out_buf_ptr,
size_t out_buf_ptr_offset,
size_t out_buf_size)
{
mfxU8* buf = reinterpret_cast<mfxU8*>(out_buf_ptr.get());
mfxU16 surfH = frameInfo.Height;
mfxU16 surfW = (frameInfo.FourCC == MFX_FOURCC_P010) ? frameInfo.Width * 2 : frameInfo.Width;
// TODO more intelligent check
if (out_buf_size <=
out_buf_ptr_offset + (surfW * surfH) + ((surfW / 2) * (surfH / 2))) {
throw std::runtime_error(std::string("Insufficient buffer size: ") +
std::to_string(out_buf_size) + ", buffer offset: " +
std::to_string(out_buf_ptr_offset) +
", expected surface width: " + std::to_string(surfW) +
", height: " + std::to_string(surfH));
}
std::unique_ptr<mfxFrameSurface1> handle(new mfxFrameSurface1);
memset(handle.get(), 0, sizeof(mfxFrameSurface1));
handle->Info = frameInfo;
handle->Data.Y = buf + out_buf_ptr_offset;
handle->Data.U = buf + out_buf_ptr_offset + (surfW * surfH);
handle->Data.V = handle->Data.U + ((surfW / 2) * (surfH / 2));
handle->Data.Pitch = surfW;
return Surface::create_surface(std::move(handle), out_buf_ptr);
}
VPLLegacyDecodeEngine::VPLLegacyDecodeEngine(std::unique_ptr<VPLAccelerationPolicy>&& accel)
: ProcessingEngineBase(std::move(accel)) {
@ -146,8 +58,9 @@ VPLLegacyDecodeEngine::VPLLegacyDecodeEngine(std::unique_ptr<VPLAccelerationPoli
// enqueue decode operation with current session surface
my_sess.last_status =
MFXVideoDECODE_DecodeFrameAsync(my_sess.session,
my_sess.last_status == MFX_ERR_NONE
(my_sess.data_provider || (my_sess.stream && my_sess.stream->DataLength))
? my_sess.stream.get()
: nullptr, /* No more data to read, start decode draining mode*/
my_sess.procesing_surface_ptr.lock()->get_handle(),
&sync_pair.second,
@ -164,33 +77,30 @@ VPLLegacyDecodeEngine::VPLLegacyDecodeEngine(std::unique_ptr<VPLAccelerationPoli
}
my_sess.last_status =
MFXVideoDECODE_DecodeFrameAsync(my_sess.session,
&my_sess.stream,
my_sess.stream.get(),
my_sess.procesing_surface_ptr.lock()->get_handle(),
&sync_pair.second,
&sync_pair.first);
} catch (const std::runtime_error& ex) {
// NB: not an error, yield CPU ticks to check
// surface availability at a next phase.
// But print WARNING to notify user about pipeline stuck
GAPI_LOG_WARNING(nullptr, "[" << my_sess.session <<
"] has no surface, reason: " <<
ex.what());
// TODO it is supposed to place `break;` here
// to simulate `yield`-like behavior.
// Further DX11 intergation logic claims more strict rules
// for enqueue surfaces. If no free surface
// is available it had better to wait free one by checking
// for async result than waste time in spinning.
//
// Put it as-is at now to not break
// current compatibility and avoid further merge-conflicts
break;
}
}
if (my_sess.last_status == MFX_ERR_NONE) {
my_sess.sync_queue.emplace(sync_pair);
} else if (my_sess.last_status != MFX_ERR_MORE_DATA) /* suppress MFX_ERR_MORE_DATA warning */ {
GAPI_LOG_WARNING(nullptr, "pending ops count: " << my_sess.sync_queue.size() <<
GAPI_LOG_WARNING(nullptr, "decode pending ops count: " <<
my_sess.sync_queue.size() <<
", sync id: " << sync_pair.first <<
", status: " << mfxstatus_to_string(my_sess.last_status));
", status: " <<
mfxstatus_to_string(my_sess.last_status));
}
return ExecutionStatus::Continue;
},
@ -198,20 +108,26 @@ VPLLegacyDecodeEngine::VPLLegacyDecodeEngine(std::unique_ptr<VPLAccelerationPoli
[this] (EngineSession& sess) -> ExecutionStatus
{
LegacyDecodeSession& my_sess = static_cast<LegacyDecodeSession&>(sess);
if (!my_sess.sync_queue.empty()) // FIFO: check the oldest async operation complete
{
LegacyDecodeSession::op_handle_t& pending_op = my_sess.sync_queue.front();
sess.last_status = MFXVideoCORE_SyncOperation(sess.session, pending_op.first, 0);
GAPI_LOG_DEBUG(nullptr, "pending ops count: " << my_sess.sync_queue.size() <<
", sync id: " << pending_op.first <<
", status: " << mfxstatus_to_string(my_sess.last_status));
// put frames in ready queue on success
if (MFX_ERR_NONE == sess.last_status) {
on_frame_ready(my_sess, pending_op.second);
do {
if (!my_sess.sync_queue.empty()) { // FIFO: check the oldest async operation complete
LegacyDecodeSession::op_handle_t& pending_op = my_sess.sync_queue.front();
sess.last_status = MFXVideoCORE_SyncOperation(sess.session, pending_op.first, 0);
GAPI_LOG_DEBUG(nullptr, "pending ops count: " <<
my_sess.sync_queue.size() <<
", sync id: " <<
pending_op.first <<
", surface: " <<
pending_op.second <<
", status: " <<
mfxstatus_to_string(my_sess.last_status));
// put frames in ready queue on success
if (MFX_ERR_NONE == sess.last_status) {
on_frame_ready(my_sess, pending_op.second);
}
}
}
} while (MFX_ERR_NONE == sess.last_status && !my_sess.sync_queue.empty());
return ExecutionStatus::Continue;
},
// 4) Falls back on generic status procesing
@ -222,45 +138,138 @@ VPLLegacyDecodeEngine::VPLLegacyDecodeEngine(std::unique_ptr<VPLAccelerationPoli
);
}
void VPLLegacyDecodeEngine::initialize_session(mfxSession mfx_session,
DecoderParams&& decoder_param,
std::shared_ptr<onevpl::IDataProvider> provider)
{
mfxFrameAllocRequest decRequest = {};
ProcessingEngineBase::session_ptr
VPLLegacyDecodeEngine::initialize_session(mfxSession mfx_session,
const std::vector<CfgParam>& cfg_params,
std::shared_ptr<IDataProvider> provider) {
GAPI_DbgAssert(provider && "Cannot create decoder, data provider is nullptr");
// init session
acceleration_policy->init(mfx_session);
// Get codec ID from data provider
IDataProvider::mfx_codec_id_type decoder_id_name = provider->get_mfx_codec_id();
// Prepare video param
mfxVideoParam mfxDecParams {};
mfxDecParams.mfx.CodecId = decoder_id_name;
// set memory stream direction accroding to accelearion policy device type
IDeviceSelector::DeviceScoreTable devices = acceleration_policy->get_device_selector()->select_devices();
GAPI_Assert(devices.size() == 1 && "Multiple(or zero) acceleration devices case is unsupported");
AccelType accel_type = devices.begin()->second.get_type();
if (accel_type == AccelType::DX11) {
mfxDecParams.IOPattern = MFX_IOPATTERN_OUT_VIDEO_MEMORY;
} else if (accel_type == AccelType::HOST) {
mfxDecParams.IOPattern = MFX_IOPATTERN_OUT_SYSTEM_MEMORY;
} else {
GAPI_Assert(false && "unsupported AccelType from device selector");
}
// try fetch & decode input data
mfxStatus sts = MFX_ERR_NONE;
std::shared_ptr<IDataProvider::mfx_bitstream> bitstream{};
bool can_fetch_data = false;
do {
can_fetch_data = provider->fetch_bitstream_data(bitstream);
if (!can_fetch_data) {
// must fetch data always because EOF critical at this point
GAPI_LOG_WARNING(nullptr, "cannot decode header from provider: " << provider.get() <<
". Unexpected EOF");
throw std::runtime_error("Error reading bitstream: EOF");
}
sts = MFXVideoDECODE_DecodeHeader(mfx_session, bitstream.get(), &mfxDecParams);
if(MFX_ERR_NONE != sts && MFX_ERR_MORE_DATA != sts) {
throw std::runtime_error("Error decoding header, error: " +
mfxstatus_to_string(sts));
}
} while (sts == MFX_ERR_MORE_DATA && !provider->empty());
if (MFX_ERR_NONE != sts) {
GAPI_LOG_WARNING(nullptr, "cannot decode header from provider: " << provider.get()
<< ". Make sure data source is valid and/or "
"\"" << CfgParam::decoder_id_name() << "\""
" has correct value in case of demultiplexed raw input");
throw std::runtime_error("Error decode header, error: " +
mfxstatus_to_string(sts));
}
mfxFrameAllocRequest decRequest {};
// Query number required surfaces for decoder
MFXVideoDECODE_QueryIOSurf(mfx_session, &decoder_param.param, &decRequest);
MFXVideoDECODE_QueryIOSurf(mfx_session, &mfxDecParams, &decRequest);
// External (application) allocation of decode surfaces
GAPI_LOG_DEBUG(nullptr, "Query IOSurf for session: " << mfx_session <<
", mfxFrameAllocRequest.NumFrameMin: " << decRequest.NumFrameMin <<
", mfxFrameAllocRequest.NumFrameSuggested: " << decRequest.NumFrameSuggested <<
", mfxFrameAllocRequest.Type: " << decRequest.Type);
mfxU32 singleSurfaceSize = GetSurfaceSize(decoder_param.param.mfx.FrameInfo.FourCC,
decoder_param.param.mfx.FrameInfo.Width,
decoder_param.param.mfx.FrameInfo.Height);
if (!singleSurfaceSize) {
throw std::runtime_error("Cannot determine surface size for: fourCC" +
std::to_string(decoder_param.param.mfx.FrameInfo.FourCC) +
", width: " + std::to_string(decoder_param.param.mfx.FrameInfo.Width) +
", height: " + std::to_string(decoder_param.param.mfx.FrameInfo.Height));
// NB: override NumFrameSuggested preallocation size (how many frames we can hold)
size_t preallocated_frames_count = decRequest.NumFrameSuggested;
// NB: if you see bunch of WARNING about "cannot get free surface from pool"
// and have abundant RAM size then increase `preallocated_frames_count`
// to keep more free surfaces in a round. Otherwise VPL decode pipeline will be waiting
// till application is freeing unusable surface on its side.
//
auto queue_capacity_it = std::find_if(cfg_params.begin(), cfg_params.end(), [] (const CfgParam& value) {
return value.get_name() == CfgParam::frames_pool_size_name();
});
if (queue_capacity_it != cfg_params.end()) {
cv::util::visit(cv::util::overload_lambdas(
[&preallocated_frames_count](uint8_t value) { preallocated_frames_count = static_cast<size_t>(value); },
[&preallocated_frames_count](int8_t value) { preallocated_frames_count = static_cast<size_t>(value); },
[&preallocated_frames_count](uint16_t value) { preallocated_frames_count = static_cast<size_t>(value); },
[&preallocated_frames_count](int16_t value) { preallocated_frames_count = static_cast<size_t>(value); },
[&preallocated_frames_count](uint32_t value) { preallocated_frames_count = static_cast<size_t>(value); },
[&preallocated_frames_count](int32_t value) { preallocated_frames_count = static_cast<size_t>(value); },
[&preallocated_frames_count](uint64_t value) { preallocated_frames_count = static_cast<size_t>(value); },
[&preallocated_frames_count](int64_t value) { preallocated_frames_count = static_cast<size_t>(value); },
[&preallocated_frames_count](float_t value) { preallocated_frames_count = static_cast<size_t>(value); },
[&preallocated_frames_count](double_t value) { preallocated_frames_count = static_cast<size_t>(value); },
[&preallocated_frames_count](void*) { GAPI_Assert(false && "`void*` is unsupported type"); },
[&preallocated_frames_count](const std::string& value) {
preallocated_frames_count = strtoull_or_throw(value.c_str());
}),
queue_capacity_it->get_value());
GAPI_LOG_INFO(nullptr, "Try to use CfgParam \"" << CfgParam::frames_pool_size_name() << "\": " <<
preallocated_frames_count << ", for session: " << mfx_session);
}
if (preallocated_frames_count < decRequest.NumFrameMin) {
GAPI_LOG_WARNING(nullptr, "Cannot proceed with CfgParam \"" << CfgParam::frames_pool_size_name() << "\": " <<
preallocated_frames_count << ". It must be equal or greater than "
"mfxFrameAllocRequest.NumFrameMin: " << decRequest.NumFrameMin);
throw std::runtime_error(std::string("Invalid value of param: ") +
CfgParam::frames_pool_size_name() + ", underflow");
} else {
if (static_cast<size_t>(std::numeric_limits<mfxU16>::max()) < preallocated_frames_count) {
GAPI_LOG_WARNING(nullptr, "Cannot proceed with CfgParam \"" << CfgParam::frames_pool_size_name() << "\": " <<
preallocated_frames_count << ". It must not be equal than " <<
std::numeric_limits<mfxU16>::max());
throw std::runtime_error(std::string("Invalid value of param: ") +
CfgParam::frames_pool_size_name() + ", overflow");
}
decRequest.NumFrameSuggested = static_cast<mfxU16>(preallocated_frames_count);
GAPI_LOG_DEBUG(nullptr, "mfxFrameAllocRequest overriden by user input for session: " << mfx_session <<
", mfxFrameAllocRequest.NumFrameMin: " << decRequest.NumFrameMin <<
", mfxFrameAllocRequest.NumFrameSuggested: " << decRequest.NumFrameSuggested <<
", mfxFrameAllocRequest.Type: " << decRequest.Type);
}
const auto &frameInfo = decoder_param.param.mfx.FrameInfo;
auto surface_creator =
[&frameInfo] (std::shared_ptr<void> out_buf_ptr, size_t out_buf_ptr_offset,
size_t out_buf_size) -> surface_ptr_t {
return (frameInfo.FourCC == MFX_FOURCC_RGB4) ?
create_surface_RGB4(frameInfo, out_buf_ptr, out_buf_ptr_offset,
out_buf_size) :
create_surface_other(frameInfo, out_buf_ptr, out_buf_ptr_offset,
out_buf_size);};
//TODO Configure preallocation size (how many frames we can hold)
const size_t preallocated_frames_count = 30;
VPLAccelerationPolicy::pool_key_t decode_pool_key =
acceleration_policy->create_surface_pool(decRequest.NumFrameSuggested * preallocated_frames_count,
singleSurfaceSize,
surface_creator);
acceleration_policy->create_surface_pool(decRequest, mfxDecParams);
// Input parameters finished, now initialize decode
// create decoder for session accoring to header recovered from source file
sts = MFXVideoDECODE_Init(mfx_session, &mfxDecParams);
if (MFX_ERR_NONE != sts) {
throw std::runtime_error("Error initializing Decode, error: " +
mfxstatus_to_string(sts));
}
DecoderParams decoder_param {bitstream, mfxDecParams};
// create session
std::shared_ptr<LegacyDecodeSession> sess_ptr =
@ -271,6 +280,7 @@ void VPLLegacyDecodeEngine::initialize_session(mfxSession mfx_session,
sess_ptr->init_surface_pool(decode_pool_key);
// prepare working decode surface
sess_ptr->swap_surface(*this);
return sess_ptr;
}
ProcessingEngineBase::ExecutionStatus VPLLegacyDecodeEngine::execute_op(operation_t& op, EngineSession& sess) {
@ -303,14 +313,13 @@ ProcessingEngineBase::ExecutionStatus VPLLegacyDecodeEngine::process_error(mfxSt
sess.swap_surface(*this);
return ExecutionStatus::Continue;
} catch (const std::runtime_error& ex) {
GAPI_LOG_WARNING(nullptr, "[" << sess.session << "] error: " << ex.what() <<
"Abort");
// TODO it is supposed to be `break;` here in future PR
GAPI_LOG_WARNING(nullptr, "[" << sess.session << "] error: " << ex.what());
return ExecutionStatus::Continue; // read more data
}
}
case MFX_ERR_MORE_DATA: // The function requires more bitstream at input before decoding can proceed
if (!sess.data_provider || sess.data_provider->empty()) {
// No more data to drain from decoder, start encode draining mode
if (!(sess.data_provider || (sess.stream && sess.stream->DataLength))) {
// No more data to drain from decoder
return ExecutionStatus::Processed;
}
else
@ -326,7 +335,7 @@ ProcessingEngineBase::ExecutionStatus VPLLegacyDecodeEngine::process_error(mfxSt
return ExecutionStatus::Continue;
} catch (const std::runtime_error& ex) {
GAPI_LOG_WARNING(nullptr, "[" << sess.session << "] error: " << ex.what());
// TODO it is supposed to be `break;` here in future PR
return ExecutionStatus::Continue; // read more data
}
break;
}
@ -371,9 +380,8 @@ ProcessingEngineBase::ExecutionStatus VPLLegacyDecodeEngine::process_error(mfxSt
sess.swap_surface(*this);
return ExecutionStatus::Continue;
} catch (const std::runtime_error& ex) {
GAPI_LOG_WARNING(nullptr, "[" << sess.session << "] error: " << ex.what() <<
"Abort");
// TODO it is supposed to be `break;` here in future PR
GAPI_LOG_WARNING(nullptr, "[" << sess.session << "] error: " << ex.what());
return ExecutionStatus::Continue;
}
default:
GAPI_LOG_WARNING(nullptr, "Unknown status code: " << mfxstatus_to_string(status) <<

@ -12,10 +12,7 @@
#include "streaming/onevpl/engine/processing_engine_base.hpp"
#ifdef HAVE_ONEVPL
#if (MFX_VERSION >= 2000)
#include <vpl/mfxdispatcher.h>
#endif
#include <vpl/mfx.h>
#include "streaming/onevpl/onevpl_export.hpp"
namespace cv {
namespace gapi {
@ -31,8 +28,9 @@ class VPLLegacyDecodeEngine : public ProcessingEngineBase {
public:
VPLLegacyDecodeEngine(std::unique_ptr<VPLAccelerationPolicy>&& accel);
void initialize_session(mfxSession mfx_session, DecoderParams&& decoder_param,
std::shared_ptr<IDataProvider> provider) override;
session_ptr initialize_session(mfxSession mfx_session,
const std::vector<CfgParam>& cfg_params,
std::shared_ptr<IDataProvider> provider) override;
private:
ExecutionStatus execute_op(operation_t& op, EngineSession& sess) override;

@ -52,8 +52,10 @@ void LegacyDecodeSession::swap_surface(VPLLegacyDecodeEngine& engine) {
procesing_surface_ptr = cand;
} catch (const std::runtime_error& ex) {
GAPI_LOG_WARNING(nullptr, "[" << session << "] error: " << ex.what() <<
"Abort");
GAPI_LOG_WARNING(nullptr, "[" << session << "] error: " << ex.what());
// Delegate exception processing on caller
throw;
}
}
@ -72,6 +74,10 @@ Data::Meta LegacyDecodeSession::generate_frame_meta() {
};
return meta;
}
const mfxVideoParam& LegacyDecodeSession::get_video_param() const {
return mfx_decoder_param;
}
} // namespace onevpl
} // namespace wip
} // namespace gapi

@ -15,10 +15,7 @@
#include "streaming/onevpl/engine/engine_session.hpp"
#include "streaming/onevpl/accelerators/accel_policy_interface.hpp"
#ifdef HAVE_ONEVPL
#if (MFX_VERSION >= 2000)
#include <vpl/mfxdispatcher.h>
#endif
#include <vpl/mfx.h>
#include "streaming/onevpl/onevpl_export.hpp"
namespace cv {
namespace gapi {
@ -40,11 +37,11 @@ public:
void swap_surface(VPLLegacyDecodeEngine& engine);
void init_surface_pool(VPLAccelerationPolicy::pool_key_t key);
mfxVideoParam mfx_decoder_param;
std::shared_ptr<IDataProvider> data_provider;
Data::Meta generate_frame_meta();
const mfxVideoParam& get_video_param() const override;
private:
mfxVideoParam mfx_decoder_param;
std::shared_ptr<IDataProvider> data_provider;
VPLAccelerationPolicy::pool_key_t decoder_pool_id;
mfxFrameAllocRequest request;

@ -18,7 +18,7 @@
#include <opencv2/gapi/streaming/onevpl/data_provider_interface.hpp>
#ifdef HAVE_ONEVPL
#include <vpl/mfxvideo.h>
#include "streaming/onevpl/onevpl_export.hpp"
namespace cv {
namespace gapi {
@ -40,6 +40,8 @@ struct GAPI_EXPORTS EngineSession {
EngineSession(mfxSession sess, std::shared_ptr<IDataProvider::mfx_bitstream>&& str);
std::string error_code_to_str() const;
virtual ~EngineSession();
virtual const mfxVideoParam& get_video_param() const = 0;
};
} // namespace onevpl
} // namespace wip

@ -23,7 +23,8 @@ ProcessingEngineBase::ProcessingEngineBase(std::unique_ptr<VPLAccelerationPolicy
}
ProcessingEngineBase::~ProcessingEngineBase() {
GAPI_LOG_INFO(nullptr, "destroyed");
GAPI_LOG_INFO(nullptr, "destroyed, elapsed sessions count: " << sessions.size());
sessions.clear();
}
ProcessingEngineBase::ExecutionStatus ProcessingEngineBase::process(mfxSession session) {
@ -42,6 +43,7 @@ ProcessingEngineBase::ExecutionStatus ProcessingEngineBase::process(mfxSession s
{
exec_data.op_id = 0;
}
cv::util::suppress_unused_warning(old_op_id);
GAPI_LOG_DEBUG(nullptr, "[" << session <<"] finish op id: " << old_op_id <<
", " << processing_session->error_code_to_str() <<
", " << ProcessingEngineBase::status_to_string(status) <<
@ -57,6 +59,7 @@ ProcessingEngineBase::ExecutionStatus ProcessingEngineBase::process(mfxSession s
}
if (status == ExecutionStatus::Processed) {
GAPI_LOG_INFO(nullptr, "Processed [" << session << "]");
sessions.erase(sess_it);
execution_table.erase(session);
}
@ -90,6 +93,7 @@ void ProcessingEngineBase::get_frame(Data &data)
{
data = ready_frames.front();
ready_frames.pop();
GAPI_LOG_DEBUG(nullptr, " elapsed ready frames count: " << ready_frames.size());
}
const VPLAccelerationPolicy* ProcessingEngineBase::get_accel() const {

@ -8,6 +8,7 @@
#define GAPI_STREAMING_ONEVPL_ENGINE_PROCESSING_ENGINE_BASE_HPP
#include <queue>
#include <opencv2/gapi/streaming/onevpl/cfg_params.hpp>
#include "streaming/onevpl/engine/engine_session.hpp"
#include "opencv2/gapi/own/exports.hpp" // GAPI_EXPORTS
@ -49,9 +50,9 @@ public:
ProcessingEngineBase(std::unique_ptr<VPLAccelerationPolicy>&& accel);
virtual ~ProcessingEngineBase();
virtual void initialize_session(mfxSession mfx_session,
DecoderParams&& decoder_param,
std::shared_ptr<IDataProvider> provider) = 0;
virtual session_ptr initialize_session(mfxSession mfx_session,
const std::vector<CfgParam>& cfg_params,
std::shared_ptr<IDataProvider> provider) = 0;
ExecutionStatus process(mfxSession session);
size_t get_ready_frames_count() const;

@ -28,14 +28,14 @@ FileDataProvider::FileDataProvider(const std::string& file_path,
codec_params.size());
auto codec_it =
std::find_if(codec_params.begin(), codec_params.end(), [] (const CfgParam& value) {
return value.get_name() == "mfxImplDescription.mfxDecoderDescription.decoder.CodecID";
return value.get_name() == CfgParam::decoder_id_name();
});
if (codec_it == codec_params.end())
{
GAPI_LOG_WARNING(nullptr, "[" << this << "] " <<
"\"mfxImplDescription.mfxDecoderDescription.decoder.CodecID\" "
"\"" << CfgParam::decoder_id_name() << "\" "
"is absent, total param count" << codec_params.size());
throw DataProviderUnsupportedException("\"mfxImplDescription.mfxDecoderDescription.decoder.CodecID\" "
throw DataProviderUnsupportedException(std::string("\"") + CfgParam::decoder_id_name() + "\" "
"is required for FileDataProvider");
}

@ -0,0 +1,25 @@
#ifndef GAPI_STREAMING_ONEVPL_EXPORT_HPP
#define GAPI_STREAMING_ONEVPL_EXPORT_HPP
#if defined(_MSC_VER)
#pragma warning(push)
#pragma warning(disable : 4201)
#pragma warning(disable : 4302)
#pragma warning(disable : 4311)
#pragma warning(disable : 4312)
#endif // defined(_MSC_VER)
#ifdef HAVE_ONEVPL
#if (MFX_VERSION >= 2000)
#include <vpl/mfxdispatcher.h>
#endif // MFX_VERSION
#include <vpl/mfx.h>
#include <vpl/mfxvideo.h>
#endif // HAVE_ONEVPL
#if defined(_MSC_VER)
#pragma warning(pop)
#endif // defined(_MSC_VER)
#endif // GAPI_STREAMING_ONEVPL_EXPORT_HPP

@ -53,14 +53,15 @@ GSource::Priv::Priv() :
mfx_session(),
description(),
description_is_valid(false),
engine()
engine(),
consumed_frames_count()
{
GAPI_LOG_INFO(nullptr, "Initialized MFX handle: " << mfx_handle);
}
GSource::Priv::Priv(std::shared_ptr<IDataProvider> provider,
const std::vector<CfgParam>& params,
std::shared_ptr<IDeviceSelector>) :
std::shared_ptr<IDeviceSelector> device_selector) :
GSource::Priv()
{
// Enable Config
@ -193,13 +194,9 @@ GSource::Priv::Priv(std::shared_ptr<IDataProvider> provider,
GAPI_LOG_INFO(nullptr, "Initialized MFX session: " << mfx_session);
// initialize decoder
// Find codec ID from config
IDataProvider::mfx_codec_id_type decoder_id = provider->get_mfx_codec_id();
// create session driving engine if required
if (!engine) {
std::unique_ptr<VPLAccelerationPolicy> acceleration = initializeHWAccel();
std::unique_ptr<VPLAccelerationPolicy> acceleration = initializeHWAccel(device_selector);
// TODO Add factory static method in ProcessingEngineBase
if (mfx_impl_description->ApiVersion.Major >= VPL_NEW_API_MAJOR_VERSION) {
@ -211,106 +208,54 @@ GSource::Priv::Priv(std::shared_ptr<IDataProvider> provider,
}
}
//create decoder for session accoring to header recovered from source file
DecoderParams decoder_param = create_decoder_from_file(decoder_id, provider);
// create engine session for processing mfx session pipeline
engine->initialize_session(mfx_session, std::move(decoder_param),
provider);
//prepare session for processing
engine->process(mfx_session);
}
GSource::Priv::~Priv()
{
GAPI_LOG_INFO(nullptr, "Unload MFX implementation description: " << mfx_impl_description);
MFXDispReleaseImplDescription(mfx_handle, mfx_impl_description);
GAPI_LOG_INFO(nullptr, "Unload MFX handle: " << mfx_handle);
MFXUnload(mfx_handle);
}
DecoderParams GSource::Priv::create_decoder_from_file(uint32_t decoder_id,
std::shared_ptr<IDataProvider> provider)
{
GAPI_DbgAssert(provider && "Cannot create decoder, data provider is nullptr");
std::shared_ptr<IDataProvider::mfx_bitstream> bitstream{};
// Retrieve the frame information from input stream
mfxVideoParam mfxDecParams {};
mfxDecParams.mfx.CodecId = decoder_id;
mfxDecParams.IOPattern = MFX_IOPATTERN_OUT_SYSTEM_MEMORY;//MFX_IOPATTERN_OUT_VIDEO_MEMORY;
mfxStatus sts = MFX_ERR_NONE;
bool can_fetch_data = false;
do {
can_fetch_data = provider->fetch_bitstream_data(bitstream);
if (!can_fetch_data) {
// must fetch data always because EOF critical at this point
GAPI_LOG_WARNING(nullptr, "cannot decode header from provider: " << provider.get() <<
". Unexpected EOF");
throw std::runtime_error("Error reading bitstream: EOF");
}
sts = MFXVideoDECODE_DecodeHeader(mfx_session, bitstream.get(), &mfxDecParams);
if(MFX_ERR_NONE != sts && MFX_ERR_MORE_DATA != sts) {
throw std::runtime_error("Error decoding header, error: " +
mfxstatus_to_string(sts));
}
} while (sts == MFX_ERR_MORE_DATA && !provider->empty());
auto engine_session_ptr = engine->initialize_session(mfx_session, cfg_params,
provider);
if (MFX_ERR_NONE != sts) {
GAPI_LOG_WARNING(nullptr, "cannot decode header from provider: " << provider.get()
<< ". Make sure data source is valid and/or "
"\"mfxImplDescription.mfxDecoderDescription.decoder.CodecID\""
" has correct value in case of demultiplexed raw input");
throw std::runtime_error("Error decode header, error: " +
mfxstatus_to_string(sts));
}
// Input parameters finished, now initialize decode
sts = MFXVideoDECODE_Init(mfx_session, &mfxDecParams);
if (MFX_ERR_NONE != sts) {
throw std::runtime_error("Error initializing Decode, error: " +
mfxstatus_to_string(sts));
}
const mfxVideoParam& video_param = engine_session_ptr->get_video_param();
// set valid description
description.size = cv::Size {
mfxDecParams.mfx.FrameInfo.Width,
mfxDecParams.mfx.FrameInfo.Height};
switch(mfxDecParams.mfx.FrameInfo.FourCC) {
video_param.mfx.FrameInfo.Width,
video_param.mfx.FrameInfo.Height};
switch(video_param.mfx.FrameInfo.FourCC) {
case MFX_FOURCC_I420:
GAPI_Assert(false && "Cannot create GMetaArg description: "
"MediaFrame doesn't support I420 type");
throw std::runtime_error("Cannot parse GMetaArg description: MediaFrame doesn't support I420 type");
case MFX_FOURCC_NV12:
description.fmt = cv::MediaFormat::NV12;
break;
default:
{
GAPI_LOG_WARNING(nullptr, "Cannot create GMetaArg description: "
"MediaFrame unknown 'fmt' type: " <<
std::to_string(mfxDecParams.mfx.FrameInfo.FourCC));
GAPI_Assert(false && "Cannot create GMetaArg description: invalid value");
}
throw std::runtime_error("Cannot parse GMetaArg description: MediaFrame unknown 'fmt' type: " +
std::to_string(video_param.mfx.FrameInfo.FourCC));
}
description_is_valid = true;
return {bitstream, mfxDecParams};
//prepare session for processing
engine->process(mfx_session);
}
GSource::Priv::~Priv() {
engine.reset();
GAPI_LOG_INFO(nullptr, "consumed frames count: " << consumed_frames_count);
GAPI_LOG_INFO(nullptr, "Unload MFX implementation description: " << mfx_impl_description);
MFXDispReleaseImplDescription(mfx_handle, mfx_impl_description);
GAPI_LOG_INFO(nullptr, "Unload MFX handle: " << mfx_handle);
MFXUnload(mfx_handle);
}
std::unique_ptr<VPLAccelerationPolicy> GSource::Priv::initializeHWAccel()
std::unique_ptr<VPLAccelerationPolicy> GSource::Priv::initializeHWAccel(std::shared_ptr<IDeviceSelector> selector)
{
std::unique_ptr<VPLAccelerationPolicy> ret;
auto accel_mode_it = std::find_if(cfg_params.begin(), cfg_params.end(), [] (const CfgParam& value) {
return value.get_name() == "mfxImplDescription.AccelerationMode";
return value.get_name() == CfgParam::acceleration_mode_name();
});
if (accel_mode_it == cfg_params.end())
{
GAPI_LOG_DEBUG(nullptr, "No HW Accel requested. Use CPU");
ret.reset(new VPLCPUAccelerationPolicy);
ret.reset(new VPLCPUAccelerationPolicy(selector));
return ret;
}
@ -320,13 +265,13 @@ std::unique_ptr<VPLAccelerationPolicy> GSource::Priv::initializeHWAccel()
switch(accel_mode.Data.U32) {
case MFX_ACCEL_MODE_VIA_D3D11:
{
std::unique_ptr<VPLDX11AccelerationPolicy> cand(new VPLDX11AccelerationPolicy);
std::unique_ptr<VPLDX11AccelerationPolicy> cand(new VPLDX11AccelerationPolicy(selector));
ret = std::move(cand);
break;
}
case MFX_ACCEL_MODE_NA:
{
std::unique_ptr<VPLCPUAccelerationPolicy> cand(new VPLCPUAccelerationPolicy);
std::unique_ptr<VPLCPUAccelerationPolicy> cand(new VPLCPUAccelerationPolicy(selector));
ret = std::move(cand);
break;
}
@ -367,6 +312,7 @@ bool GSource::Priv::pull(cv::gapi::wip::Data& data)
if (engine->get_ready_frames_count()) {
engine->get_frame(data);
consumed_frames_count++;
return true;
} else {
return false;

@ -17,14 +17,7 @@
#include <opencv2/gapi/streaming/onevpl/source.hpp>
#ifdef HAVE_ONEVPL
#if (MFX_VERSION >= 2000)
#include <vpl/mfxdispatcher.h>
#endif // MFX_VERSION
#include <vpl/mfx.h>
#include <vpl/mfxvideo.h>
#include "streaming/onevpl/onevpl_export.hpp"
#include "streaming/onevpl/engine/processing_engine_base.hpp"
namespace cv {
@ -49,9 +42,7 @@ struct GSource::Priv
GMetaArg descr_of() const;
private:
Priv();
DecoderParams create_decoder_from_file(uint32_t decoder_id,
std::shared_ptr<IDataProvider> provider);
std::unique_ptr<VPLAccelerationPolicy> initializeHWAccel();
std::unique_ptr<VPLAccelerationPolicy> initializeHWAccel(std::shared_ptr<IDeviceSelector> selector);
mfxLoader mfx_handle;
mfxImplDescription *mfx_impl_description;
@ -64,6 +55,8 @@ private:
bool description_is_valid;
std::unique_ptr<ProcessingEngineBase> engine;
size_t consumed_frames_count;
};
} // namespace onevpl
} // namespace wip

@ -14,6 +14,18 @@
#include "streaming/onevpl/utils.hpp"
#include "logger.hpp"
#define ONEVPL_STRINGIFY_CASE(value) \
case value: return #value;
#define ONEVPL_STRINGIFY_IF(value) \
if (!strcmp(cstr, #value)) { \
return value; \
}
#define APPEND_STRINGIFY_MASK_N_ERASE(value, pref, mask) \
if (value & mask) { ss << pref << #mask; value ^= mask; }
namespace cv {
namespace gapi {
namespace wip {
@ -21,153 +33,96 @@ namespace onevpl {
const char* mfx_impl_to_cstr(const mfxIMPL impl) {
switch (impl) {
case MFX_IMPL_TYPE_SOFTWARE:
return "MFX_IMPL_TYPE_SOFTWARE";
case MFX_IMPL_TYPE_HARDWARE:
return "MFX_IMPL_TYPE_HARDWARE";
default:
return "unknown mfxIMPL";
ONEVPL_STRINGIFY_CASE(MFX_IMPL_TYPE_SOFTWARE);
ONEVPL_STRINGIFY_CASE(MFX_IMPL_TYPE_HARDWARE);
default: return "unknown mfxIMPL";
}
}
mfxIMPL cstr_to_mfx_impl(const char* cstr) {
if (!strcmp(cstr, "MFX_IMPL_TYPE_SOFTWARE")) {
return MFX_IMPL_TYPE_SOFTWARE;
} else if (!strcmp(cstr, "MFX_IMPL_TYPE_HARDWARE")) {
return MFX_IMPL_TYPE_HARDWARE;
}
throw std::logic_error(std::string("Invalid \"mfxImplDescription.Impl\":") + cstr);
ONEVPL_STRINGIFY_IF(MFX_IMPL_TYPE_SOFTWARE)
ONEVPL_STRINGIFY_IF(MFX_IMPL_TYPE_HARDWARE)
throw std::logic_error(std::string("Invalid \"") + CfgParam::implementation_name() +
"\":" + cstr);
}
const char* mfx_accel_mode_to_cstr (const mfxAccelerationMode mode) {
switch (mode) {
case MFX_ACCEL_MODE_NA:
return "MFX_ACCEL_MODE_NA";
case MFX_ACCEL_MODE_VIA_D3D9:
return "MFX_ACCEL_MODE_VIA_D3D9";
case MFX_ACCEL_MODE_VIA_D3D11:
return "MFX_ACCEL_MODE_VIA_D3D11";
case MFX_ACCEL_MODE_VIA_VAAPI:
return "MFX_ACCEL_MODE_VIA_VAAPI";
case MFX_ACCEL_MODE_VIA_VAAPI_DRM_MODESET:
return "MFX_ACCEL_MODE_VIA_VAAPI_DRM_MODESET";
case MFX_ACCEL_MODE_VIA_VAAPI_GLX:
return "MFX_ACCEL_MODE_VIA_VAAPI_GLX";
case MFX_ACCEL_MODE_VIA_VAAPI_X11:
return "MFX_ACCEL_MODE_VIA_VAAPI_X11";
case MFX_ACCEL_MODE_VIA_VAAPI_WAYLAND:
return "MFX_ACCEL_MODE_VIA_VAAPI_WAYLAND";
case MFX_ACCEL_MODE_VIA_HDDLUNITE:
return "MFX_ACCEL_MODE_VIA_HDDLUNITE";
default:
return "unknown mfxAccelerationMode";
ONEVPL_STRINGIFY_CASE(MFX_ACCEL_MODE_NA)
ONEVPL_STRINGIFY_CASE(MFX_ACCEL_MODE_VIA_D3D9)
ONEVPL_STRINGIFY_CASE(MFX_ACCEL_MODE_VIA_D3D11)
ONEVPL_STRINGIFY_CASE(MFX_ACCEL_MODE_VIA_VAAPI)
ONEVPL_STRINGIFY_CASE(MFX_ACCEL_MODE_VIA_VAAPI_DRM_MODESET)
ONEVPL_STRINGIFY_CASE(MFX_ACCEL_MODE_VIA_VAAPI_GLX)
ONEVPL_STRINGIFY_CASE(MFX_ACCEL_MODE_VIA_VAAPI_X11)
ONEVPL_STRINGIFY_CASE(MFX_ACCEL_MODE_VIA_VAAPI_WAYLAND)
ONEVPL_STRINGIFY_CASE(MFX_ACCEL_MODE_VIA_HDDLUNITE)
default: return "unknown mfxAccelerationMode";
}
return "unknown mfxAccelerationMode";
}
mfxAccelerationMode cstr_to_mfx_accel_mode(const char* cstr) {
if (!strcmp(cstr, "MFX_ACCEL_MODE_NA")) {
return MFX_ACCEL_MODE_NA;
} else if (!strcmp(cstr, "MFX_ACCEL_MODE_VIA_D3D9")) {
return MFX_ACCEL_MODE_VIA_D3D9;
} else if (!strcmp(cstr, "MFX_ACCEL_MODE_VIA_D3D11")) {
return MFX_ACCEL_MODE_VIA_D3D11;
} else if (!strcmp(cstr, "MFX_ACCEL_MODE_VIA_VAAPI")) {
return MFX_ACCEL_MODE_VIA_VAAPI;
} else if (!strcmp(cstr, "MFX_ACCEL_MODE_VIA_VAAPI_DRM_MODESET")) {
return MFX_ACCEL_MODE_VIA_VAAPI_DRM_MODESET;
} else if (!strcmp(cstr, "MFX_ACCEL_MODE_VIA_VAAPI_GLX")) {
return MFX_ACCEL_MODE_VIA_VAAPI_GLX;
} else if (!strcmp(cstr, "MFX_ACCEL_MODE_VIA_VAAPI_X11")) {
return MFX_ACCEL_MODE_VIA_VAAPI_X11;
} else if (!strcmp(cstr, "MFX_ACCEL_MODE_VIA_VAAPI_WAYLAND")) {
return MFX_ACCEL_MODE_VIA_VAAPI_WAYLAND;
} else if (!strcmp(cstr, "MFX_ACCEL_MODE_VIA_HDDLUNITE")) {
return MFX_ACCEL_MODE_VIA_HDDLUNITE;
}
throw std::logic_error(std::string("Invalid \"mfxImplDescription.AccelerationMode\":") + cstr);
ONEVPL_STRINGIFY_IF(MFX_ACCEL_MODE_NA)
ONEVPL_STRINGIFY_IF(MFX_ACCEL_MODE_VIA_D3D9)
ONEVPL_STRINGIFY_IF(MFX_ACCEL_MODE_VIA_D3D11)
ONEVPL_STRINGIFY_IF(MFX_ACCEL_MODE_VIA_VAAPI)
ONEVPL_STRINGIFY_IF(MFX_ACCEL_MODE_VIA_VAAPI_DRM_MODESET)
ONEVPL_STRINGIFY_IF(MFX_ACCEL_MODE_VIA_VAAPI_GLX)
ONEVPL_STRINGIFY_IF(MFX_ACCEL_MODE_VIA_VAAPI_X11)
ONEVPL_STRINGIFY_IF(MFX_ACCEL_MODE_VIA_VAAPI_WAYLAND)
ONEVPL_STRINGIFY_IF(MFX_ACCEL_MODE_VIA_HDDLUNITE)
throw std::logic_error(std::string("Invalid \"") +
CfgParam::acceleration_mode_name() +
"\":" + cstr);
}
const char* mfx_resource_type_to_cstr (const mfxResourceType type) {
switch (type) {
case MFX_RESOURCE_SYSTEM_SURFACE:
return "MFX_RESOURCE_SYSTEM_SURFACE";
case MFX_RESOURCE_VA_SURFACE:
return "MFX_RESOURCE_VA_SURFACE";
case MFX_RESOURCE_VA_BUFFER:
return "MFX_RESOURCE_VA_BUFFER";
case MFX_RESOURCE_DX9_SURFACE:
return "MFX_RESOURCE_DX9_SURFACE";
case MFX_RESOURCE_DX11_TEXTURE:
return "MFX_RESOURCE_DX11_TEXTURE";
case MFX_RESOURCE_DX12_RESOURCE:
return "MFX_RESOURCE_DX12_RESOURCE";
case MFX_RESOURCE_DMA_RESOURCE:
return "MFX_RESOURCE_DMA_RESOURCE";
case MFX_RESOURCE_HDDLUNITE_REMOTE_MEMORY:
return "MFX_RESOURCE_HDDLUNITE_REMOTE_MEMORY";
default:
return "unknown mfxResourceType";
ONEVPL_STRINGIFY_CASE(MFX_RESOURCE_SYSTEM_SURFACE)
ONEVPL_STRINGIFY_CASE(MFX_RESOURCE_VA_SURFACE)
ONEVPL_STRINGIFY_CASE(MFX_RESOURCE_VA_BUFFER)
ONEVPL_STRINGIFY_CASE(MFX_RESOURCE_DX9_SURFACE)
ONEVPL_STRINGIFY_CASE(MFX_RESOURCE_DX11_TEXTURE)
ONEVPL_STRINGIFY_CASE(MFX_RESOURCE_DX12_RESOURCE)
ONEVPL_STRINGIFY_CASE(MFX_RESOURCE_DMA_RESOURCE)
ONEVPL_STRINGIFY_CASE(MFX_RESOURCE_HDDLUNITE_REMOTE_MEMORY)
default: return "unknown mfxResourceType";
}
}
mfxResourceType cstr_to_mfx_resource_type(const char* cstr) {
if (!strcmp(cstr, "MFX_RESOURCE_SYSTEM_SURFACE")) {
return MFX_RESOURCE_SYSTEM_SURFACE;
} else if (!strcmp(cstr, "MFX_RESOURCE_VA_SURFACE")) {
return MFX_RESOURCE_VA_SURFACE;
} else if (!strcmp(cstr, "MFX_RESOURCE_VA_BUFFER")) {
return MFX_RESOURCE_VA_BUFFER;
} else if (!strcmp(cstr, "MFX_RESOURCE_DX9_SURFACE")) {
return MFX_RESOURCE_DX9_SURFACE;
} else if (!strcmp(cstr, "MFX_RESOURCE_DX11_TEXTURE")) {
return MFX_RESOURCE_DX11_TEXTURE;
} else if (!strcmp(cstr, "MFX_RESOURCE_DX12_RESOURCE")) {
return MFX_RESOURCE_DX12_RESOURCE;
} else if (!strcmp(cstr, "MFX_RESOURCE_DMA_RESOURCE")) {
return MFX_RESOURCE_DMA_RESOURCE;
} else if (!strcmp(cstr, "MFX_RESOURCE_HDDLUNITE_REMOTE_MEMORY")) {
return MFX_RESOURCE_HDDLUNITE_REMOTE_MEMORY;
}
ONEVPL_STRINGIFY_IF(MFX_RESOURCE_SYSTEM_SURFACE)
ONEVPL_STRINGIFY_IF(MFX_RESOURCE_VA_SURFACE)
ONEVPL_STRINGIFY_IF(MFX_RESOURCE_VA_BUFFER)
ONEVPL_STRINGIFY_IF(MFX_RESOURCE_DX9_SURFACE)
ONEVPL_STRINGIFY_IF(MFX_RESOURCE_DX11_TEXTURE)
ONEVPL_STRINGIFY_IF(MFX_RESOURCE_DX12_RESOURCE)
ONEVPL_STRINGIFY_IF(MFX_RESOURCE_DMA_RESOURCE)
ONEVPL_STRINGIFY_IF(MFX_RESOURCE_HDDLUNITE_REMOTE_MEMORY)
throw std::logic_error(std::string("Invalid \"decoder.Profiles.MemDesc.MemHandleType\":") + cstr);
}
mfxU32 cstr_to_mfx_codec_id(const char* cstr) {
if (!strcmp(cstr, "MFX_CODEC_AVC")) {
return MFX_CODEC_AVC;
} else if (!strcmp(cstr, "MFX_CODEC_HEVC")) {
return MFX_CODEC_HEVC;
} else if (!strcmp(cstr, "MFX_CODEC_MPEG2")) {
return MFX_CODEC_MPEG2;
} else if (!strcmp(cstr, "MFX_CODEC_VC1")) {
return MFX_CODEC_VC1;
} else if (!strcmp(cstr, "MFX_CODEC_CAPTURE")) {
return MFX_CODEC_CAPTURE;
} else if (!strcmp(cstr, "MFX_CODEC_VP9")) {
return MFX_CODEC_VP9;
} else if (!strcmp(cstr, "MFX_CODEC_AV1")) {
return MFX_CODEC_AV1;
}
throw std::logic_error(std::string("Cannot parse \"mfxImplDescription.mfxDecoderDescription.decoder.CodecID\" value: ") + cstr);
ONEVPL_STRINGIFY_IF(MFX_CODEC_AVC)
ONEVPL_STRINGIFY_IF(MFX_CODEC_HEVC)
ONEVPL_STRINGIFY_IF(MFX_CODEC_MPEG2)
ONEVPL_STRINGIFY_IF(MFX_CODEC_VC1)
ONEVPL_STRINGIFY_IF(MFX_CODEC_CAPTURE)
ONEVPL_STRINGIFY_IF(MFX_CODEC_VP9)
ONEVPL_STRINGIFY_IF(MFX_CODEC_AV1)
throw std::logic_error(std::string("Cannot parse \"") + CfgParam::decoder_id_name() +
"\" value: " + cstr);
}
const char* mfx_codec_id_to_cstr(mfxU32 mfx_id) {
switch(mfx_id) {
case MFX_CODEC_AVC:
return "MFX_CODEC_AVC";
case MFX_CODEC_HEVC:
return "MFX_CODEC_HEVC";
case MFX_CODEC_MPEG2:
return "MFX_CODEC_MPEG2";
case MFX_CODEC_VC1:
return "MFX_CODEC_VC1";
case MFX_CODEC_VP9:
return "MFX_CODEC_VP9";
case MFX_CODEC_AV1:
return "MFX_CODEC_AV1";
case MFX_CODEC_JPEG:
return "MFX_CODEC_JPEG";
ONEVPL_STRINGIFY_CASE(MFX_CODEC_AVC)
ONEVPL_STRINGIFY_CASE(MFX_CODEC_HEVC)
ONEVPL_STRINGIFY_CASE(MFX_CODEC_MPEG2)
ONEVPL_STRINGIFY_CASE(MFX_CODEC_VC1)
ONEVPL_STRINGIFY_CASE(MFX_CODEC_VP9)
ONEVPL_STRINGIFY_CASE(MFX_CODEC_AV1)
ONEVPL_STRINGIFY_CASE(MFX_CODEC_JPEG)
default:
return "<unsupported>";
}
@ -189,145 +144,92 @@ const char* mfx_codec_type_to_cstr(const mfxU32 fourcc, const mfxU32 type) {
switch (fourcc) {
case MFX_CODEC_JPEG: {
switch (type) {
case MFX_PROFILE_UNKNOWN:
return "MFX_PROFILE_UNKNOWN";
case MFX_PROFILE_JPEG_BASELINE:
return "MFX_PROFILE_JPEG_BASELINE";
default:
return "<unknown MFX_CODEC_JPEG profile";
ONEVPL_STRINGIFY_CASE(MFX_PROFILE_UNKNOWN)
ONEVPL_STRINGIFY_CASE(MFX_PROFILE_JPEG_BASELINE)
default: return "<unknown MFX_CODEC_JPEG profile";
}
}
case MFX_CODEC_AVC: {
switch (type) {
case MFX_PROFILE_UNKNOWN:
return "MFX_PROFILE_UNKNOWN";
case MFX_PROFILE_AVC_BASELINE:
return "MFX_PROFILE_AVC_BASELINE";
case MFX_PROFILE_AVC_MAIN:
return "MFX_PROFILE_AVC_MAIN";
case MFX_PROFILE_AVC_EXTENDED:
return "MFX_PROFILE_AVC_EXTENDED";
case MFX_PROFILE_AVC_HIGH:
return "MFX_PROFILE_AVC_HIGH";
case MFX_PROFILE_AVC_HIGH10:
return "MFX_PROFILE_AVC_HIGH10";
case MFX_PROFILE_AVC_HIGH_422:
return "MFX_PROFILE_AVC_HIGH_422";
case MFX_PROFILE_AVC_CONSTRAINED_BASELINE:
return "MFX_PROFILE_AVC_CONSTRAINED_BASELINE";
case MFX_PROFILE_AVC_CONSTRAINED_HIGH:
return "MFX_PROFILE_AVC_CONSTRAINED_HIGH";
case MFX_PROFILE_AVC_PROGRESSIVE_HIGH:
return "MFX_PROFILE_AVC_PROGRESSIVE_HIGH";
default:
return "<unknown MFX_CODEC_AVC profile";
ONEVPL_STRINGIFY_CASE(MFX_PROFILE_UNKNOWN)
ONEVPL_STRINGIFY_CASE(MFX_PROFILE_AVC_BASELINE)
ONEVPL_STRINGIFY_CASE(MFX_PROFILE_AVC_MAIN)
ONEVPL_STRINGIFY_CASE(MFX_PROFILE_AVC_EXTENDED)
ONEVPL_STRINGIFY_CASE(MFX_PROFILE_AVC_HIGH)
ONEVPL_STRINGIFY_CASE(MFX_PROFILE_AVC_HIGH10)
ONEVPL_STRINGIFY_CASE(MFX_PROFILE_AVC_HIGH_422)
ONEVPL_STRINGIFY_CASE(MFX_PROFILE_AVC_CONSTRAINED_BASELINE)
ONEVPL_STRINGIFY_CASE(MFX_PROFILE_AVC_CONSTRAINED_HIGH)
ONEVPL_STRINGIFY_CASE(MFX_PROFILE_AVC_PROGRESSIVE_HIGH)
default: return "<unknown MFX_CODEC_AVC profile";
}
}
case MFX_CODEC_HEVC: {
switch (type) {
case MFX_PROFILE_UNKNOWN:
return "MFX_PROFILE_UNKNOWN";
case MFX_PROFILE_HEVC_MAIN:
return "MFX_PROFILE_HEVC_MAIN";
case MFX_PROFILE_HEVC_MAIN10:
return "MFX_PROFILE_HEVC_MAIN10";
case MFX_PROFILE_HEVC_MAINSP:
return "MFX_PROFILE_HEVC_MAINSP";
case MFX_PROFILE_HEVC_REXT:
return "MFX_PROFILE_HEVC_REXT";
case MFX_PROFILE_HEVC_SCC:
return "MFX_PROFILE_HEVC_SCC";
default:
return "<unknown MFX_CODEC_HEVC profile";
ONEVPL_STRINGIFY_CASE(MFX_PROFILE_UNKNOWN)
ONEVPL_STRINGIFY_CASE(MFX_PROFILE_HEVC_MAIN)
ONEVPL_STRINGIFY_CASE(MFX_PROFILE_HEVC_MAIN10)
ONEVPL_STRINGIFY_CASE(MFX_PROFILE_HEVC_MAINSP)
ONEVPL_STRINGIFY_CASE(MFX_PROFILE_HEVC_REXT)
ONEVPL_STRINGIFY_CASE(MFX_PROFILE_HEVC_SCC)
default: return "<unknown MFX_CODEC_HEVC profile";
}
}
case MFX_CODEC_MPEG2: {
switch (type) {
case MFX_PROFILE_UNKNOWN:
return "MFX_PROFILE_UNKNOWN";
case MFX_PROFILE_MPEG2_SIMPLE:
return "MFX_PROFILE_MPEG2_SIMPLE";
case MFX_PROFILE_MPEG2_MAIN:
return "MFX_PROFILE_MPEG2_MAIN";
case MFX_LEVEL_MPEG2_HIGH:
return "MFX_LEVEL_MPEG2_HIGH";
case MFX_LEVEL_MPEG2_HIGH1440:
return "MFX_LEVEL_MPEG2_HIGH1440";
default:
return "<unknown MFX_CODEC_MPEG2 profile";
ONEVPL_STRINGIFY_CASE(MFX_PROFILE_UNKNOWN)
ONEVPL_STRINGIFY_CASE(MFX_PROFILE_MPEG2_SIMPLE)
ONEVPL_STRINGIFY_CASE(MFX_PROFILE_MPEG2_MAIN)
ONEVPL_STRINGIFY_CASE(MFX_LEVEL_MPEG2_HIGH)
ONEVPL_STRINGIFY_CASE(MFX_LEVEL_MPEG2_HIGH1440)
default: return "<unknown MFX_CODEC_MPEG2 profile";
}
}
case MFX_CODEC_VP8: {
switch (type) {
case MFX_PROFILE_UNKNOWN:
return "MFX_PROFILE_UNKNOWN";
case MFX_PROFILE_VP8_0:
return "MFX_PROFILE_VP8_0";
case MFX_PROFILE_VP8_1:
return "MFX_PROFILE_VP8_1";
case MFX_PROFILE_VP8_2:
return "MFX_PROFILE_VP8_2";
case MFX_PROFILE_VP8_3:
return "MFX_PROFILE_VP8_3";
default:
return "<unknown MFX_CODEC_VP8 profile";
ONEVPL_STRINGIFY_CASE(MFX_PROFILE_UNKNOWN)
ONEVPL_STRINGIFY_CASE(MFX_PROFILE_VP8_0)
ONEVPL_STRINGIFY_CASE(MFX_PROFILE_VP8_1)
ONEVPL_STRINGIFY_CASE(MFX_PROFILE_VP8_2)
ONEVPL_STRINGIFY_CASE(MFX_PROFILE_VP8_3)
default: return "<unknown MFX_CODEC_VP8 profile";
}
}
case MFX_CODEC_VC1: {
switch (type) {
case MFX_PROFILE_UNKNOWN:
return "MFX_PROFILE_UNKNOWN";
case MFX_PROFILE_VC1_SIMPLE:
return "MFX_PROFILE_VC1_SIMPLE";
case MFX_PROFILE_VC1_MAIN:
return "MFX_PROFILE_VC1_MAIN";
case MFX_PROFILE_VC1_ADVANCED:
return "MFX_PROFILE_VC1_ADVANCED";
default:
return "<unknown MFX_CODEC_VC1 profile";
ONEVPL_STRINGIFY_CASE(MFX_PROFILE_UNKNOWN)
ONEVPL_STRINGIFY_CASE(MFX_PROFILE_VC1_SIMPLE)
ONEVPL_STRINGIFY_CASE(MFX_PROFILE_VC1_MAIN)
ONEVPL_STRINGIFY_CASE(MFX_PROFILE_VC1_ADVANCED)
default: return "<unknown MFX_CODEC_VC1 profile";
}
}
case MFX_CODEC_VP9: {
switch (type) {
case MFX_PROFILE_UNKNOWN:
return "MFX_PROFILE_UNKNOWN";
case MFX_PROFILE_VP9_0:
return "MFX_PROFILE_VP9_0";
case MFX_PROFILE_VP9_1:
return "MFX_PROFILE_VP9_1";
case MFX_PROFILE_VP9_2:
return "MFX_PROFILE_VP9_2";
case MFX_PROFILE_VP9_3:
return "MFX_PROFILE_VP9_3";
default:
return "<unknown MFX_CODEC_VP9 profile";
ONEVPL_STRINGIFY_CASE(MFX_PROFILE_UNKNOWN)
ONEVPL_STRINGIFY_CASE(MFX_PROFILE_VP9_0)
ONEVPL_STRINGIFY_CASE(MFX_PROFILE_VP9_1)
ONEVPL_STRINGIFY_CASE(MFX_PROFILE_VP9_2)
ONEVPL_STRINGIFY_CASE(MFX_PROFILE_VP9_3)
default: return "<unknown MFX_CODEC_VP9 profile";
}
}
case MFX_CODEC_AV1: {
switch (type) {
case MFX_PROFILE_UNKNOWN:
return "MFX_PROFILE_UNKNOWN";
case MFX_PROFILE_AV1_MAIN:
return "MFX_PROFILE_AV1_MAIN";
case MFX_PROFILE_AV1_HIGH:
return "MFX_PROFILE_AV1_HIGH";
case MFX_PROFILE_AV1_PRO:
return "MFX_PROFILE_AV1_PRO";
default:
return "<unknown MFX_CODEC_AV1 profile";
ONEVPL_STRINGIFY_CASE(MFX_PROFILE_UNKNOWN)
ONEVPL_STRINGIFY_CASE(MFX_PROFILE_AV1_MAIN)
ONEVPL_STRINGIFY_CASE(MFX_PROFILE_AV1_HIGH)
ONEVPL_STRINGIFY_CASE(MFX_PROFILE_AV1_PRO)
default: return "<unknown MFX_CODEC_AV1 profile";
}
}
default:
return "unknown codec type :";
default: return "unknown codec type :";
}
}
@ -354,8 +256,8 @@ std::ostream& operator<< (std::ostream& out, const mfxImplDescription& idesc)
{
out << "mfxImplDescription.Version: " << static_cast<int>(idesc.Version.Major)
<< "." << static_cast<int>(idesc.Version.Minor) << std::endl;
out << "mfxImplDescription.Impl: " << mfx_impl_to_cstr(idesc.Impl) << std::endl;
out << "(*)mfxImplDescription.AccelerationMode: " << mfx_accel_mode_to_cstr(idesc.AccelerationMode) << std::endl;
out << "(*)" << CfgParam::implementation_name() << ": " << mfx_impl_to_cstr(idesc.Impl) << std::endl;
out << "(*)" << CfgParam::acceleration_mode_name() << ": " << mfx_accel_mode_to_cstr(idesc.AccelerationMode) << std::endl;
out << "mfxImplDescription.ApiVersion: " << idesc.ApiVersion.Major << "." << idesc.ApiVersion.Minor << std::endl;
out << "(*)mfxImplDescription.ApiVersion.Version: " << idesc.ApiVersion.Version << std::endl;
out << "mfxImplDescription.ImplName: " << idesc.ImplName << std::endl;
@ -386,7 +288,7 @@ std::ostream& operator<< (std::ostream& out, const mfxImplDescription& idesc)
<< "." << static_cast<int>(dec.Version.Minor) << std::endl;
for (int codec = 0; codec < dec.NumCodecs; codec++) {
auto cid = dec.Codecs[codec].CodecID;
out << "(*)mfxImplDescription.mfxDecoderDescription.decoder.CodecID: " << cid;//(cid & 0xff) << "." << (cid >> 8 & 0xff) << "." << (cid >> 16 & 0xff) << "." << (cid >> 24 & 0xff) << std::endl;
out << "(*)" << CfgParam::decoder_id_name() << ": " << cid;//(cid & 0xff) << "." << (cid >> 8 & 0xff) << "." << (cid >> 16 & 0xff) << "." << (cid >> 24 & 0xff) << std::endl;
out << "mfxImplDescription.mfxDecoderDescription.decoder.MaxcodecLevel: " << dec.Codecs[codec].MaxcodecLevel << std::endl;
for (int profile = 0; profile < dec.Codecs[codec].NumProfiles; profile++) {
out << "mfxImplDescription.mfxDecoderDescription.decoder.Profiles: "
@ -422,69 +324,69 @@ std::string mfxstatus_to_string(int64_t err) {
}
std::string mfxstatus_to_string(mfxStatus err) {
switch(err)
{
case MFX_ERR_NONE:
return "MFX_ERR_NONE";
case MFX_ERR_UNKNOWN:
return "MFX_ERR_UNKNOWN";
case MFX_ERR_NULL_PTR:
return "MFX_ERR_NULL_PTR";
case MFX_ERR_UNSUPPORTED:
return "MFX_ERR_UNSUPPORTED";
case MFX_ERR_MEMORY_ALLOC:
return "MFX_ERR_MEMORY_ALLOC";
case MFX_ERR_NOT_ENOUGH_BUFFER:
return "MFX_ERR_NOT_ENOUGH_BUFFER";
case MFX_ERR_INVALID_HANDLE:
return "MFX_ERR_INVALID_HANDLE";
case MFX_ERR_LOCK_MEMORY:
return "MFX_ERR_LOCK_MEMORY";
case MFX_ERR_NOT_INITIALIZED:
return "MFX_ERR_NOT_INITIALIZED";
case MFX_ERR_NOT_FOUND:
return "MFX_ERR_NOT_FOUND";
case MFX_ERR_MORE_DATA:
return "MFX_ERR_MORE_DATA";
case MFX_ERR_MORE_SURFACE:
return "MFX_ERR_MORE_SURFACE";
case MFX_ERR_ABORTED:
return "MFX_ERR_ABORTED";
case MFX_ERR_DEVICE_LOST:
return "MFX_ERR_DEVICE_LOST";
case MFX_ERR_INCOMPATIBLE_VIDEO_PARAM:
return "MFX_ERR_INCOMPATIBLE_VIDEO_PARAM";
case MFX_ERR_INVALID_VIDEO_PARAM:
return "MFX_ERR_INVALID_VIDEO_PARAM";
case MFX_ERR_UNDEFINED_BEHAVIOR:
return "MFX_ERR_UNDEFINED_BEHAVIOR";
case MFX_ERR_DEVICE_FAILED:
return "MFX_ERR_DEVICE_FAILED";
case MFX_ERR_MORE_BITSTREAM:
return "MFX_ERR_MORE_BITSTREAM";
case MFX_ERR_GPU_HANG:
return "MFX_ERR_GPU_HANG";
case MFX_ERR_REALLOC_SURFACE:
return "MFX_ERR_REALLOC_SURFACE";
case MFX_ERR_RESOURCE_MAPPED:
return "MFX_ERR_RESOURCE_MAPPED";
case MFX_ERR_NOT_IMPLEMENTED:
return "MFX_ERR_NOT_IMPLEMENTED";
case MFX_WRN_DEVICE_BUSY:
return "MFX_WRN_DEVICE_BUSY";
case MFX_WRN_VIDEO_PARAM_CHANGED:
return "MFX_WRN_VIDEO_PARAM_CHANGED";
case MFX_WRN_IN_EXECUTION:
return "MFX_WRN_IN_EXECUTION";
default:
break;
switch(err) {
ONEVPL_STRINGIFY_CASE(MFX_ERR_NONE)
ONEVPL_STRINGIFY_CASE(MFX_ERR_UNKNOWN)
ONEVPL_STRINGIFY_CASE(MFX_ERR_NULL_PTR)
ONEVPL_STRINGIFY_CASE(MFX_ERR_UNSUPPORTED)
ONEVPL_STRINGIFY_CASE(MFX_ERR_MEMORY_ALLOC)
ONEVPL_STRINGIFY_CASE(MFX_ERR_NOT_ENOUGH_BUFFER)
ONEVPL_STRINGIFY_CASE(MFX_ERR_INVALID_HANDLE)
ONEVPL_STRINGIFY_CASE(MFX_ERR_LOCK_MEMORY)
ONEVPL_STRINGIFY_CASE(MFX_ERR_NOT_INITIALIZED)
ONEVPL_STRINGIFY_CASE(MFX_ERR_NOT_FOUND)
ONEVPL_STRINGIFY_CASE(MFX_ERR_MORE_DATA)
ONEVPL_STRINGIFY_CASE(MFX_ERR_MORE_SURFACE)
ONEVPL_STRINGIFY_CASE(MFX_ERR_ABORTED)
ONEVPL_STRINGIFY_CASE(MFX_ERR_DEVICE_LOST)
ONEVPL_STRINGIFY_CASE(MFX_ERR_INCOMPATIBLE_VIDEO_PARAM)
ONEVPL_STRINGIFY_CASE(MFX_ERR_INVALID_VIDEO_PARAM)
ONEVPL_STRINGIFY_CASE(MFX_ERR_UNDEFINED_BEHAVIOR)
ONEVPL_STRINGIFY_CASE(MFX_ERR_DEVICE_FAILED)
ONEVPL_STRINGIFY_CASE(MFX_ERR_MORE_BITSTREAM)
ONEVPL_STRINGIFY_CASE(MFX_ERR_GPU_HANG)
ONEVPL_STRINGIFY_CASE(MFX_ERR_REALLOC_SURFACE)
ONEVPL_STRINGIFY_CASE(MFX_ERR_RESOURCE_MAPPED)
ONEVPL_STRINGIFY_CASE(MFX_ERR_NOT_IMPLEMENTED)
ONEVPL_STRINGIFY_CASE(MFX_WRN_DEVICE_BUSY)
ONEVPL_STRINGIFY_CASE(MFX_WRN_VIDEO_PARAM_CHANGED)
ONEVPL_STRINGIFY_CASE(MFX_WRN_IN_EXECUTION)
default: break;
}
std::string ret("<unknown ");
ret += std::to_string(err) + ">";
return ret;
}
std::string ext_mem_frame_type_to_cstr(int type) {
std::stringstream ss;
APPEND_STRINGIFY_MASK_N_ERASE(type, "|", MFX_MEMTYPE_DXVA2_DECODER_TARGET);
APPEND_STRINGIFY_MASK_N_ERASE(type, "|", MFX_MEMTYPE_DXVA2_PROCESSOR_TARGET);
// NB: accoring to VPL source the commented MFX_* constane below are belong to the
// same actual integral value as condition abobe. So it is impossible
// to distinct them in condition branch. Just put this comment and possible
// constans here...
//APPEND_STRINGIFY_MASK_N_ERASE(type, "|", MFX_MEMTYPE_VIDEO_MEMORY_DECODER_TARGET);
//APPEND_STRINGIFY_MASK_N_ERASE(type, "|", MFX_MEMTYPE_VIDEO_MEMORY_PROCESSOR_TARGET);
APPEND_STRINGIFY_MASK_N_ERASE(type, "|", MFX_MEMTYPE_SYSTEM_MEMORY);
APPEND_STRINGIFY_MASK_N_ERASE(type, "|", MFX_MEMTYPE_RESERVED1);
APPEND_STRINGIFY_MASK_N_ERASE(type, "|", MFX_MEMTYPE_FROM_ENCODE);
APPEND_STRINGIFY_MASK_N_ERASE(type, "|", MFX_MEMTYPE_FROM_DECODE);
APPEND_STRINGIFY_MASK_N_ERASE(type, "|", MFX_MEMTYPE_FROM_VPPIN);
APPEND_STRINGIFY_MASK_N_ERASE(type, "|", MFX_MEMTYPE_FROM_VPPOUT);
APPEND_STRINGIFY_MASK_N_ERASE(type, "|", MFX_MEMTYPE_FROM_ENC);
APPEND_STRINGIFY_MASK_N_ERASE(type, "|", MFX_MEMTYPE_INTERNAL_FRAME);
APPEND_STRINGIFY_MASK_N_ERASE(type, "|", MFX_MEMTYPE_EXTERNAL_FRAME);
APPEND_STRINGIFY_MASK_N_ERASE(type, "|", MFX_MEMTYPE_EXPORT_FRAME);
//APPEND_STRINGIFY_MASK_N_ERASE(type, "|", MFX_MEMTYPE_SHARED_RESOURCE);
APPEND_STRINGIFY_MASK_N_ERASE(type, "|", MFX_MEMTYPE_VIDEO_MEMORY_ENCODER_TARGET);
if (type != 0) {
ss << "(rest: " << std::to_string(type) << ")";
}
return ss.str();
}
} // namespace onevpl
} // namespace wip
} // namespace gapi

@ -8,12 +8,7 @@
#define GAPI_STREAMING_ONEVPL_ONEVPL_UTILS_HPP
#ifdef HAVE_ONEVPL
#if (MFX_VERSION >= 2000)
#include <vpl/mfxdispatcher.h>
#endif // MFX_VERSION
#include <vpl/mfx.h>
#include <vpl/mfxvideo.h>
#include "streaming/onevpl/onevpl_export.hpp"
#include <map>
#include <memory>
@ -42,11 +37,19 @@ void release(COMNonManageableType *ptr) {
template <typename COMNonManageableType>
using ComPtrGuard = std::unique_ptr<COMNonManageableType, decltype(&release<COMNonManageableType>)>;
template <typename COMNonManageableType>
using ComSharedPtrGuard = std::shared_ptr<COMNonManageableType>;
template <typename COMNonManageableType>
ComPtrGuard<COMNonManageableType> createCOMPtrGuard(COMNonManageableType *ptr = nullptr) {
return ComPtrGuard<COMNonManageableType> {ptr, &release<COMNonManageableType>};
}
template <typename COMNonManageableType>
ComSharedPtrGuard<COMNonManageableType> createCOMSharedPtrGuard(ComPtrGuard<COMNonManageableType>&& unique_guard) {
return ComSharedPtrGuard<COMNonManageableType>(std::move(unique_guard));
}
const char* mfx_impl_to_cstr(const mfxIMPL impl);
@ -75,6 +78,7 @@ std::string mfxstatus_to_string(mfxStatus err);
std::ostream& operator<< (std::ostream& out, const mfxImplDescription& idesc);
std::string ext_mem_frame_type_to_cstr(int type);
} // namespace onevpl
} // namespace wip
} // namespace gapi

@ -0,0 +1,86 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
//
// Copyright (C) 2021 Intel Corporation
#ifndef OPENCV_GAPI_STREAMING_TESTS_COMMON_HPP
#define OPENCV_GAPI_STREAMING_TESTS_COMMON_HPP
#include "gapi_tests_common.hpp"
#include <opencv2/gapi/streaming/onevpl/source.hpp>
#include <opencv2/gapi/streaming/onevpl/data_provider_interface.hpp>
#include "streaming/onevpl/data_provider_defines.hpp"
#ifdef HAVE_ONEVPL
#include "streaming/onevpl/onevpl_export.hpp"
namespace opencv_test {
namespace streaming {
namespace onevpl {
struct StreamDataProvider : public cv::gapi::wip::onevpl::IDataProvider {
StreamDataProvider(std::istream& in) : data_stream (in) {
EXPECT_TRUE(in);
}
mfx_codec_id_type get_mfx_codec_id() const override {
return MFX_CODEC_HEVC;
}
bool fetch_bitstream_data(std::shared_ptr<mfx_bitstream> &out_bitstream) override {
if (empty()) {
return false;
}
if (!out_bitstream) {
out_bitstream = std::make_shared<mfx_bitstream>();
out_bitstream->MaxLength = 2000000;
out_bitstream->Data = (mfxU8 *)calloc(out_bitstream->MaxLength, sizeof(mfxU8));
if(!out_bitstream->Data) {
throw std::runtime_error("Cannot allocate bitstream.Data bytes: " +
std::to_string(out_bitstream->MaxLength * sizeof(mfxU8)));
}
out_bitstream->CodecId = get_mfx_codec_id();
}
mfxU8 *p0 = out_bitstream->Data;
mfxU8 *p1 = out_bitstream->Data + out_bitstream->DataOffset;
EXPECT_FALSE(out_bitstream->DataOffset > out_bitstream->MaxLength - 1);
EXPECT_FALSE(out_bitstream->DataLength + out_bitstream->DataOffset > out_bitstream->MaxLength);
std::copy_n(p1, out_bitstream->DataLength, p0);
out_bitstream->DataOffset = 0;
out_bitstream->DataLength += static_cast<mfxU32>(fetch_data(out_bitstream->MaxLength - out_bitstream->DataLength,
out_bitstream->Data + out_bitstream->DataLength));
return out_bitstream->DataLength != 0;
}
size_t fetch_data(size_t out_data_size, void* out_data_buf) {
data_stream.read(reinterpret_cast<char*>(out_data_buf), out_data_size);
return data_stream.gcount();
}
bool empty() const override {
return data_stream.eof() || data_stream.bad();
}
private:
std::istream& data_stream;
};
static const unsigned char hevc_header[] = {
0x00, 0x00, 0x00, 0x01, 0x40, 0x01, 0x0C, 0x06, 0xFF, 0xFF, 0x01, 0x40, 0x00,
0x00, 0x03, 0x00, 0x80, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x78, 0x00,
0x00, 0x04, 0x02, 0x10, 0x30, 0x00, 0x00, 0x03, 0x00, 0x10, 0x00, 0x00, 0x03,
0x01, 0xE5, 0x00, 0x00, 0x00, 0x01, 0x42, 0x01, 0x06, 0x01, 0x40, 0x00, 0x00,
0x03, 0x00, 0x80, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x78, 0x00, 0x00,
0xA0, 0x10, 0x20, 0x61, 0x63, 0x41, 0x00, 0x86, 0x49, 0x1B, 0x2B, 0x20, 0x00,
0x00, 0x00, 0x01, 0x44, 0x01, 0xC0, 0x71, 0xC0, 0xD9, 0x20, 0x00, 0x00, 0x00,
0x01, 0x26, 0x01, 0xAF, 0x0C
};
} // namespace onevpl
} // namespace streaming
} // namespace opencv_test
#endif // HAVE_ONEVPL
#endif // OPENCV_GAPI_STREAMING_TESTS_HPP

@ -7,7 +7,7 @@
#include "../test_precomp.hpp"
#include "../common/gapi_tests_common.hpp"
#include "../common/gapi_streaming_tests_common.hpp"
#include <thread> // sleep_for (Delay)
@ -24,18 +24,8 @@
#include <opencv2/gapi/streaming/cap.hpp>
#include <opencv2/gapi/streaming/desync.hpp>
#include <opencv2/gapi/streaming/format.hpp>
#include <opencv2/gapi/gstreaming.hpp>
#include <opencv2/gapi/streaming/onevpl/source.hpp>
#include "streaming/onevpl/data_provider_defines.hpp"
#ifdef HAVE_ONEVPL
#if (MFX_VERSION >= 2000)
#include <vpl/mfxdispatcher.h>
#endif
#include <vpl/mfx.h>
#endif // HAVE_ONEVPL
namespace opencv_test
{
@ -116,7 +106,7 @@ struct GAPI_Streaming: public ::testing::TestWithParam<std::tuple<KernelPackage,
using namespace cv::gapi;
auto args = cv::compile_args(use_only{pkg});
if (cap) {
args += cv::compile_args(streaming::queue_capacity{cap.value()});
args += cv::compile_args(cv::gapi::streaming::queue_capacity{cap.value()});
}
return args;
}
@ -269,57 +259,6 @@ void checkPullOverload(const cv::Mat& ref,
EXPECT_EQ(0., cv::norm(ref, out_mat, cv::NORM_INF));
}
#ifdef HAVE_ONEVPL
struct StreamDataProvider : public cv::gapi::wip::onevpl::IDataProvider {
StreamDataProvider(std::istream& in) : data_stream (in) {
EXPECT_TRUE(in);
}
mfx_codec_id_type get_mfx_codec_id() const override {
return MFX_CODEC_HEVC;
}
bool fetch_bitstream_data(std::shared_ptr<mfx_bitstream> &out_bitstream) override {
if (empty()) {
return false;
}
if (!out_bitstream) {
out_bitstream = std::make_shared<mfx_bitstream>();
out_bitstream->MaxLength = 2000000;
out_bitstream->Data = (mfxU8 *)calloc(out_bitstream->MaxLength, sizeof(mfxU8));
if(!out_bitstream->Data) {
throw std::runtime_error("Cannot allocate bitstream.Data bytes: " +
std::to_string(out_bitstream->MaxLength * sizeof(mfxU8)));
}
out_bitstream->CodecId = get_mfx_codec_id();
}
mfxU8 *p0 = out_bitstream->Data;
mfxU8 *p1 = out_bitstream->Data + out_bitstream->DataOffset;
EXPECT_FALSE(out_bitstream->DataOffset > out_bitstream->MaxLength - 1);
EXPECT_FALSE(out_bitstream->DataLength + out_bitstream->DataOffset > out_bitstream->MaxLength);
std::copy_n(p1, out_bitstream->DataLength, p0);
out_bitstream->DataOffset = 0;
out_bitstream->DataLength += static_cast<mfxU32>(fetch_data(out_bitstream->MaxLength - out_bitstream->DataLength,
out_bitstream->Data + out_bitstream->DataLength));
return out_bitstream->DataLength != 0;
}
size_t fetch_data(size_t out_data_size, void* out_data_buf) {
data_stream.read(reinterpret_cast<char*>(out_data_buf), out_data_size);
return (size_t)data_stream.gcount();
}
bool empty() const override {
return data_stream.eof() || data_stream.bad();
}
private:
std::istream& data_stream;
};
#endif // HAVE_ONEVPL
} // anonymous namespace
TEST_P(GAPI_Streaming, SmokeTest_ConstInput_GMat)
@ -2244,31 +2183,21 @@ TEST(GAPI_Streaming, TestPythonAPI)
}
#ifdef HAVE_ONEVPL
const unsigned char hevc_header[] = {
0x00, 0x00, 0x00, 0x01, 0x40, 0x01, 0x0C, 0x06, 0xFF, 0xFF, 0x01, 0x40, 0x00,
0x00, 0x03, 0x00, 0x80, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x78, 0x00,
0x00, 0x04, 0x02, 0x10, 0x30, 0x00, 0x00, 0x03, 0x00, 0x10, 0x00, 0x00, 0x03,
0x01, 0xE5, 0x00, 0x00, 0x00, 0x01, 0x42, 0x01, 0x06, 0x01, 0x40, 0x00, 0x00,
0x03, 0x00, 0x80, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x78, 0x00, 0x00,
0xA0, 0x10, 0x20, 0x61, 0x63, 0x41, 0x00, 0x86, 0x49, 0x1B, 0x2B, 0x20, 0x00,
0x00, 0x00, 0x01, 0x44, 0x01, 0xC0, 0x71, 0xC0, 0xD9, 0x20, 0x00, 0x00, 0x00,
0x01, 0x26, 0x01, 0xAF, 0x0C
};
TEST(OneVPL_Source, Init)
{
using CfgParam = cv::gapi::wip::onevpl::CfgParam;
std::vector<CfgParam> src_params;
src_params.push_back(CfgParam::create<uint32_t>("mfxImplDescription.Impl",
MFX_IMPL_TYPE_HARDWARE));
src_params.push_back(CfgParam::create<uint32_t>("mfxImplDescription.AccelerationMode",
MFX_ACCEL_MODE_VIA_D3D11, false));
src_params.push_back(CfgParam::create<uint32_t>("mfxImplDescription.mfxDecoderDescription.decoder.CodecID",
MFX_CODEC_HEVC));
src_params.push_back(CfgParam::create_implementation(MFX_IMPL_TYPE_HARDWARE));
src_params.push_back(CfgParam::create_acceleration_mode(MFX_ACCEL_MODE_VIA_D3D11));
src_params.push_back(CfgParam::create_decoder_id(MFX_CODEC_HEVC));
std::stringstream stream(std::ios_base::in | std::ios_base::out | std::ios_base::binary);
EXPECT_TRUE(stream.write(reinterpret_cast<char*>(const_cast<unsigned char *>(hevc_header)),
sizeof(hevc_header)));
auto stream_data_provider = std::make_shared<StreamDataProvider>(stream);
EXPECT_TRUE(stream.write(reinterpret_cast<char*>(const_cast<unsigned char *>(streaming::onevpl::hevc_header)),
sizeof(streaming::onevpl::hevc_header)));
std::shared_ptr<cv::gapi::wip::onevpl::IDataProvider> stream_data_provider =
std::make_shared<streaming::onevpl::StreamDataProvider>(stream);
cv::Ptr<cv::gapi::wip::IStreamSource> cap;
bool cap_created = false;
@ -2285,7 +2214,7 @@ TEST(OneVPL_Source, Init)
}
EXPECT_TRUE(stream_data_provider->empty());
}
#endif
#endif // HAVE_ONEVPL
TEST(GAPI_Streaming, TestDesyncRMat) {
cv::GMat in;

@ -0,0 +1,348 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
//
// Copyright (C) 2021 Intel Corporation
#include "../test_precomp.hpp"
#include "../common/gapi_streaming_tests_common.hpp"
#include <chrono>
#include <future>
#define private public
#include "streaming/onevpl/accelerators/utils/shared_lock.hpp"
#undef private
#include "streaming/onevpl/accelerators/utils/elastic_barrier.hpp"
namespace opencv_test
{
namespace
{
using cv::gapi::wip::onevpl::SharedLock;
struct TestBarrier : public cv::gapi::wip::onevpl::elastic_barrier<TestBarrier> {
void on_first_in_impl(size_t visitor_id) {
static std::atomic<int> thread_counter{};
thread_counter++;
EXPECT_EQ(thread_counter.load(), 1);
visitors_in.insert(visitor_id);
last_visitor_id = visitor_id;
thread_counter--;
EXPECT_EQ(thread_counter.load(), 0);
}
void on_last_out_impl(size_t visitor_id) {
static std::atomic<int> thread_counter{};
thread_counter++;
EXPECT_EQ(thread_counter.load(), 1);
visitors_out.insert(visitor_id);
last_visitor_id = visitor_id;
thread_counter--;
EXPECT_EQ(thread_counter.load(), 0);
}
size_t last_visitor_id = 0;
std::set<size_t> visitors_in;
std::set<size_t> visitors_out;
};
TEST(OneVPL_SharedLock, Create) {
SharedLock lock;
EXPECT_EQ(lock.shared_counter.load(), size_t{0});
}
TEST(OneVPL_SharedLock, Read_SingleThread)
{
SharedLock lock;
const size_t single_thread_read_count = 100;
for(size_t i = 0; i < single_thread_read_count; i++) {
lock.shared_lock();
EXPECT_FALSE(lock.owns());
}
EXPECT_EQ(lock.shared_counter.load(), single_thread_read_count);
for(size_t i = 0; i < single_thread_read_count; i++) {
lock.unlock_shared();
EXPECT_FALSE(lock.owns());
}
EXPECT_EQ(lock.shared_counter.load(), size_t{0});
}
TEST(OneVPL_SharedLock, TryLock_SingleThread)
{
SharedLock lock;
EXPECT_TRUE(lock.try_lock());
EXPECT_TRUE(lock.owns());
lock.unlock();
EXPECT_FALSE(lock.owns());
EXPECT_EQ(lock.shared_counter.load(), size_t{0});
}
TEST(OneVPL_SharedLock, Write_SingleThread)
{
SharedLock lock;
lock.lock();
EXPECT_TRUE(lock.owns());
lock.unlock();
EXPECT_FALSE(lock.owns());
EXPECT_EQ(lock.shared_counter.load(), size_t{0});
}
TEST(OneVPL_SharedLock, TryLockTryLock_SingleThread)
{
SharedLock lock;
lock.try_lock();
EXPECT_FALSE(lock.try_lock());
lock.unlock();
EXPECT_FALSE(lock.owns());
}
TEST(OneVPL_SharedLock, ReadTryLock_SingleThread)
{
SharedLock lock;
lock.shared_lock();
EXPECT_FALSE(lock.owns());
EXPECT_FALSE(lock.try_lock());
lock.unlock_shared();
EXPECT_TRUE(lock.try_lock());
EXPECT_TRUE(lock.owns());
lock.unlock();
}
TEST(OneVPL_SharedLock, WriteTryLock_SingleThread)
{
SharedLock lock;
lock.lock();
EXPECT_TRUE(lock.owns());
EXPECT_FALSE(lock.try_lock());
lock.unlock();
EXPECT_TRUE(lock.try_lock());
EXPECT_TRUE(lock.owns());
lock.unlock();
}
TEST(OneVPL_SharedLock, Write_MultiThread)
{
SharedLock lock;
std::promise<void> barrier;
std::shared_future<void> sync = barrier.get_future();
static const size_t inc_count = 10000000;
size_t shared_value = 0;
auto work = [&lock, &shared_value](size_t count) {
for (size_t i = 0; i < count; i ++) {
lock.lock();
shared_value ++;
lock.unlock();
}
};
std::thread worker_thread([&barrier, sync, work] () {
std::thread sub_worker([&barrier, work] () {
barrier.set_value();
work(inc_count);
});
sync.wait();
work(inc_count);
sub_worker.join();
});
sync.wait();
work(inc_count);
worker_thread.join();
EXPECT_EQ(shared_value, inc_count * 3);
}
TEST(OneVPL_SharedLock, ReadWrite_MultiThread)
{
SharedLock lock;
std::promise<void> barrier;
std::future<void> sync = barrier.get_future();
static const size_t inc_count = 10000000;
size_t shared_value = 0;
auto write_work = [&lock, &shared_value](size_t count) {
for (size_t i = 0; i < count; i ++) {
lock.lock();
shared_value ++;
lock.unlock();
}
};
auto read_work = [&lock, &shared_value](size_t count) {
auto old_shared_value = shared_value;
for (size_t i = 0; i < count; i ++) {
lock.shared_lock();
EXPECT_TRUE(shared_value >= old_shared_value);
old_shared_value = shared_value;
lock.unlock_shared();
}
};
std::thread writer_thread([&barrier, write_work] () {
barrier.set_value();
write_work(inc_count);
});
sync.wait();
read_work(inc_count);
writer_thread.join();
EXPECT_EQ(shared_value, inc_count);
}
TEST(OneVPL_ElasticBarrier, single_thread_visit)
{
TestBarrier barrier;
const size_t max_visit_count = 10000;
size_t visit_id = 0;
for (visit_id = 0; visit_id < max_visit_count; visit_id++) {
barrier.visit_in(visit_id);
EXPECT_EQ(barrier.visitors_in.size(), size_t{1});
}
EXPECT_EQ(barrier.last_visitor_id, size_t{0});
EXPECT_EQ(barrier.visitors_out.size(), size_t{0});
for (visit_id = 0; visit_id < max_visit_count; visit_id++) {
barrier.visit_out(visit_id);
EXPECT_EQ(barrier.visitors_in.size(), size_t{1});
}
EXPECT_EQ(barrier.last_visitor_id, visit_id - 1);
EXPECT_EQ(barrier.visitors_out.size(), size_t{1});
}
TEST(OneVPL_ElasticBarrier, multi_thread_visit)
{
TestBarrier tested_barrier;
static const size_t max_visit_count = 10000000;
std::atomic<size_t> visit_in_wait_counter{};
std::promise<void> start_sync_barrier;
std::shared_future<void> start_sync = start_sync_barrier.get_future();
std::promise<void> phase_sync_barrier;
std::shared_future<void> phase_sync = phase_sync_barrier.get_future();
auto visit_worker_job = [&tested_barrier,
&visit_in_wait_counter,
start_sync,
phase_sync] (size_t worker_id) {
start_sync.wait();
// first phase
const size_t begin_range = worker_id * max_visit_count;
const size_t end_range = (worker_id + 1) * max_visit_count;
for (size_t visit_id = begin_range; visit_id < end_range; visit_id++) {
tested_barrier.visit_in(visit_id);
}
// notify all worker first phase ready
visit_in_wait_counter.fetch_add(1);
// wait main second phase
phase_sync.wait();
// second phase
for (size_t visit_id = begin_range; visit_id < end_range; visit_id++) {
tested_barrier.visit_out(visit_id);
}
};
auto visit_main_job = [&tested_barrier,
&visit_in_wait_counter,
&phase_sync_barrier] (size_t total_workers_count,
size_t worker_id) {
const size_t begin_range = worker_id * max_visit_count;
const size_t end_range = (worker_id + 1) * max_visit_count;
for (size_t visit_id = begin_range; visit_id < end_range; visit_id++) {
tested_barrier.visit_in(visit_id);
}
// wait all workers first phase done
visit_in_wait_counter.fetch_add(1);
while (visit_in_wait_counter.load() != total_workers_count) {
std::this_thread::yield();
};
// TEST invariant: last_visitor_id MUST be one from any FIRST worker visitor_id
bool one_of_available_ids_matched = false;
for (size_t id = 0; id < total_workers_count; id ++) {
size_t expected_last_visitor_for_id = id * max_visit_count;
one_of_available_ids_matched |=
(tested_barrier.last_visitor_id == expected_last_visitor_for_id) ;
}
EXPECT_TRUE(one_of_available_ids_matched);
// unblock all workers to work out second phase
phase_sync_barrier.set_value();
// continue second phase
for (size_t visit_id = begin_range; visit_id < end_range; visit_id++) {
tested_barrier.visit_out(visit_id);
}
};
size_t max_worker_count = std::thread::hardware_concurrency();
if (max_worker_count < 2) {
max_worker_count = 2; // logical 2 threads required at least
}
std::vector<std::thread> workers;
workers.reserve(max_worker_count);
for (size_t worker_id = 1; worker_id < max_worker_count; worker_id++) {
workers.emplace_back(visit_worker_job, worker_id);
}
// let's go for first phase
start_sync_barrier.set_value();
// utilize main thread as well
visit_main_job(max_worker_count, 0);
// join all threads second phase
for (auto& w : workers) {
w.join();
}
// TEST invariant: last_visitor_id MUST be one from any LATTER worker visitor_id
bool one_of_available_ids_matched = false;
for (size_t id = 0; id < max_worker_count; id ++) {
one_of_available_ids_matched |=
(tested_barrier.last_visitor_id == ((id + 1) * max_visit_count - 1)) ;
}
EXPECT_TRUE(one_of_available_ids_matched);
}
}
} // opencv_test

@ -7,7 +7,7 @@
#include "../test_precomp.hpp"
#include "../common/gapi_tests_common.hpp"
#include "../common/gapi_streaming_tests_common.hpp"
#include <chrono>
#include <future>
@ -27,14 +27,16 @@
#include <opencv2/gapi/streaming/desync.hpp>
#include <opencv2/gapi/streaming/format.hpp>
#include <opencv2/gapi/streaming/onevpl/source.hpp>
#ifdef HAVE_ONEVPL
#include <opencv2/gapi/streaming/onevpl/data_provider_interface.hpp>
#include "streaming/onevpl/cfg_param_device_selector.hpp"
#include "streaming/onevpl/accelerators/surface/surface.hpp"
#include "streaming/onevpl/accelerators/surface/cpu_frame_adapter.hpp"
#include "streaming/onevpl/accelerators/accel_policy_cpu.hpp"
#include "streaming/onevpl/accelerators/accel_policy_dx11.hpp"
#include "streaming/onevpl/accelerators/dx11_alloc_resource.hpp"
#include "streaming/onevpl/accelerators/utils/shared_lock.hpp"
#include "streaming/onevpl/engine/processing_engine_base.hpp"
#include "streaming/onevpl/engine/engine_session.hpp"
@ -60,6 +62,11 @@ struct TestProcessingSession : public cv::gapi::wip::onevpl::EngineSession {
TestProcessingSession(mfxSession mfx_session) :
EngineSession(mfx_session, {}) {
}
const mfxVideoParam& get_video_param() const override {
static mfxVideoParam empty;
return empty;
}
};
struct TestProcessingEngine: public cv::gapi::wip::onevpl::ProcessingEngineBase {
@ -98,13 +105,65 @@ struct TestProcessingEngine: public cv::gapi::wip::onevpl::ProcessingEngineBase
);
}
void initialize_session(mfxSession mfx_session,
cv::gapi::wip::onevpl::DecoderParams&&,
std::shared_ptr<cv::gapi::wip::onevpl::IDataProvider>) override {
std::shared_ptr<cv::gapi::wip::onevpl::EngineSession>
initialize_session(mfxSession mfx_session,
const std::vector<cv::gapi::wip::onevpl::CfgParam>&,
std::shared_ptr<cv::gapi::wip::onevpl::IDataProvider>) override {
return register_session<TestProcessingSession>(mfx_session);
}
};
register_session<TestProcessingSession>(mfx_session);
template <class LockProcessor, class UnlockProcessor>
class TestLockableAllocator {
public :
using self_t = TestLockableAllocator<LockProcessor, UnlockProcessor>;
mfxFrameAllocator get() {
return m_allocator;
}
private:
TestLockableAllocator(mfxFrameAllocator allocator) :
m_allocator(allocator) {
}
static mfxStatus MFX_CDECL lock_cb(mfxHDL, mfxMemId mid, mfxFrameData *ptr) {
auto it = lock_processor_table.find(mid);
EXPECT_TRUE(it != lock_processor_table.end());
return it->second(mid, ptr);
}
static mfxStatus MFX_CDECL unlock_cb(mfxHDL, mfxMemId mid, mfxFrameData *ptr) {
auto it = unlock_processor_table.find(mid);
EXPECT_TRUE(it != unlock_processor_table.end());
return it->second(mid, ptr);
}
template <class L, class U>
friend TestLockableAllocator<L,U> create_test_allocator(mfxMemId, L, U);
static std::map<mfxMemId, LockProcessor> lock_processor_table;
static std::map<mfxMemId, UnlockProcessor> unlock_processor_table;
mfxFrameAllocator m_allocator;
};
template <class LockProcessor, class UnlockProcessor>
std::map<mfxMemId, UnlockProcessor> TestLockableAllocator<LockProcessor, UnlockProcessor>::lock_processor_table {};
template <class LockProcessor, class UnlockProcessor>
std::map<mfxMemId, UnlockProcessor> TestLockableAllocator<LockProcessor, UnlockProcessor>::unlock_processor_table {};
template <class LockProcessor, class UnlockProcessor>
TestLockableAllocator<LockProcessor, UnlockProcessor>
create_test_allocator(mfxMemId mid, LockProcessor lock_p, UnlockProcessor unlock_p) {
mfxFrameAllocator allocator {};
TestLockableAllocator<LockProcessor, UnlockProcessor>::lock_processor_table[mid] = lock_p;
allocator.Lock = &TestLockableAllocator<LockProcessor, UnlockProcessor>::lock_cb;
TestLockableAllocator<LockProcessor, UnlockProcessor>::unlock_processor_table[mid] = unlock_p;
allocator.Unlock = &TestLockableAllocator<LockProcessor, UnlockProcessor>::unlock_cb;
return TestLockableAllocator<LockProcessor, UnlockProcessor> {allocator};
}
cv::gapi::wip::onevpl::surface_ptr_t create_test_surface(std::shared_ptr<void> out_buf_ptr,
size_t, size_t) {
@ -262,8 +321,10 @@ TEST(OneVPL_Source_CPU_Accelerator, InitDestroy)
{
using cv::gapi::wip::onevpl::VPLCPUAccelerationPolicy;
using cv::gapi::wip::onevpl::VPLAccelerationPolicy;
using cv::gapi::wip::onevpl::CfgParamDeviceSelector;
auto acceleration_policy = std::make_shared<VPLCPUAccelerationPolicy>();
auto acceleration_policy =
std::make_shared<VPLCPUAccelerationPolicy>(std::make_shared<CfgParamDeviceSelector>());
size_t surface_count = 10;
size_t surface_size_bytes = 1024;
@ -292,9 +353,11 @@ TEST(OneVPL_Source_CPU_Accelerator, PoolProduceConsume)
{
using cv::gapi::wip::onevpl::VPLCPUAccelerationPolicy;
using cv::gapi::wip::onevpl::VPLAccelerationPolicy;
using cv::gapi::wip::onevpl::CfgParamDeviceSelector;
using cv::gapi::wip::onevpl::Surface;
auto acceleration_policy = std::make_shared<VPLCPUAccelerationPolicy>();
auto acceleration_policy =
std::make_shared<VPLCPUAccelerationPolicy>(std::make_shared<CfgParamDeviceSelector>());
size_t surface_count = 10;
size_t surface_size_bytes = 1024;
@ -348,9 +411,11 @@ TEST(OneVPL_Source_CPU_Accelerator, PoolProduceConcurrentConsume)
{
using cv::gapi::wip::onevpl::VPLCPUAccelerationPolicy;
using cv::gapi::wip::onevpl::VPLAccelerationPolicy;
using cv::gapi::wip::onevpl::CfgParamDeviceSelector;
using cv::gapi::wip::onevpl::Surface;
auto acceleration_policy = std::make_shared<VPLCPUAccelerationPolicy>();
auto acceleration_policy =
std::make_shared<VPLCPUAccelerationPolicy>(std::make_shared<CfgParamDeviceSelector>());
size_t surface_count = 10;
size_t surface_size_bytes = 1024;
@ -416,7 +481,7 @@ TEST(OneVPL_Source_ProcessingEngine, Init)
TestProcessingEngine engine(std::move(accel));
mfxSession mfx_session{};
engine.initialize_session(mfx_session, DecoderParams{}, std::shared_ptr<IDataProvider>{});
engine.initialize_session(mfx_session, {}, std::shared_ptr<IDataProvider>{});
EXPECT_EQ(0, engine.get_ready_frames_count());
ProcessingEngineBase::ExecutionStatus ret = engine.process(mfx_session);
@ -444,6 +509,181 @@ TEST(OneVPL_Source_ProcessingEngine, Init)
cv::gapi::wip::Data frame;
engine.get_frame(frame);
}
#ifdef HAVE_DIRECTX
#ifdef HAVE_D3D11
TEST(OneVPL_Source_DX11_Accel, Init)
{
using namespace cv::gapi::wip::onevpl;
std::vector<CfgParam> cfg_params_w_dx11;
cfg_params_w_dx11.push_back(CfgParam::create_acceleration_mode(MFX_ACCEL_MODE_VIA_D3D11));
VPLDX11AccelerationPolicy accel(std::make_shared<CfgParamDeviceSelector>(cfg_params_w_dx11));
mfxLoader mfx_handle = MFXLoad();
mfxConfig cfg_inst_0 = MFXCreateConfig(mfx_handle);
EXPECT_TRUE(cfg_inst_0);
mfxVariant mfx_param_0;
mfx_param_0.Type = MFX_VARIANT_TYPE_U32;
mfx_param_0.Data.U32 = MFX_IMPL_TYPE_HARDWARE;
EXPECT_EQ(MFXSetConfigFilterProperty(cfg_inst_0,(mfxU8 *)CfgParam::implementation_name(),
mfx_param_0), MFX_ERR_NONE);
mfxConfig cfg_inst_1 = MFXCreateConfig(mfx_handle);
EXPECT_TRUE(cfg_inst_1);
mfxVariant mfx_param_1;
mfx_param_1.Type = MFX_VARIANT_TYPE_U32;
mfx_param_1.Data.U32 = MFX_ACCEL_MODE_VIA_D3D11;
EXPECT_EQ(MFXSetConfigFilterProperty(cfg_inst_1,(mfxU8 *)CfgParam::acceleration_mode_name(),
mfx_param_1), MFX_ERR_NONE);
mfxConfig cfg_inst_2 = MFXCreateConfig(mfx_handle);
EXPECT_TRUE(cfg_inst_2);
mfxVariant mfx_param_2;
mfx_param_2.Type = MFX_VARIANT_TYPE_U32;
mfx_param_2.Data.U32 = MFX_CODEC_HEVC;
EXPECT_EQ(MFXSetConfigFilterProperty(cfg_inst_2,(mfxU8 *)CfgParam::decoder_id_name(),
mfx_param_2), MFX_ERR_NONE);
// create session
mfxSession mfx_session{};
mfxStatus sts = MFXCreateSession(mfx_handle, 0, &mfx_session);
EXPECT_EQ(MFX_ERR_NONE, sts);
// assign acceleration
EXPECT_NO_THROW(accel.init(mfx_session));
// create proper bitstream
mfxBitstream bitstream{};
const int BITSTREAM_BUFFER_SIZE = 2000000;
bitstream.MaxLength = BITSTREAM_BUFFER_SIZE;
bitstream.Data = (mfxU8 *)calloc(bitstream.MaxLength, sizeof(mfxU8));
EXPECT_TRUE(bitstream.Data);
// simulate read stream
bitstream.DataOffset = 0;
bitstream.DataLength = sizeof(streaming::onevpl::hevc_header) * sizeof(streaming::onevpl::hevc_header[0]);
memcpy(bitstream.Data, streaming::onevpl::hevc_header, bitstream.DataLength);
bitstream.CodecId = MFX_CODEC_HEVC;
// prepare dec params
mfxVideoParam mfxDecParams {};
mfxDecParams.mfx.CodecId = bitstream.CodecId;
mfxDecParams.IOPattern = MFX_IOPATTERN_OUT_VIDEO_MEMORY;
sts = MFXVideoDECODE_DecodeHeader(mfx_session, &bitstream, &mfxDecParams);
EXPECT_EQ(MFX_ERR_NONE, sts);
mfxFrameAllocRequest request{};
memset(&request, 0, sizeof(request));
sts = MFXVideoDECODE_QueryIOSurf(mfx_session, &mfxDecParams, &request);
EXPECT_EQ(MFX_ERR_NONE, sts);
// Allocate surfaces for decoder
VPLAccelerationPolicy::pool_key_t key = accel.create_surface_pool(request,
mfxDecParams);
auto cand_surface = accel.get_free_surface(key).lock();
sts = MFXVideoDECODE_Init(mfx_session, &mfxDecParams);
EXPECT_EQ(MFX_ERR_NONE, sts);
MFXVideoDECODE_Close(mfx_session);
EXPECT_EQ(MFX_ERR_NONE, sts);
EXPECT_NO_THROW(accel.deinit(mfx_session));
MFXClose(mfx_session);
MFXUnload(mfx_handle);
}
#endif // HAVE_DIRECTX
#endif // HAVE_D3D11
TEST(OneVPL_Source_DX11_FrameLockable, LockUnlock_without_Adaptee)
{
using namespace cv::gapi::wip::onevpl;
mfxMemId mid = 0;
int lock_counter = 0;
int unlock_counter = 0;
std::function<mfxStatus(mfxMemId, mfxFrameData *)> lock =
[&lock_counter] (mfxMemId, mfxFrameData *) {
lock_counter ++;
return MFX_ERR_NONE;
};
std::function<mfxStatus(mfxMemId, mfxFrameData *)> unlock =
[&unlock_counter] (mfxMemId, mfxFrameData *) {
unlock_counter++;
return MFX_ERR_NONE;
};
auto test_allocator = create_test_allocator(mid, lock, unlock);
LockAdapter adapter(test_allocator.get());
mfxFrameData data;
const int exec_count = 123;
for (int i = 0; i < exec_count; i ++) {
EXPECT_EQ(adapter.read_lock(mid, data), 0);
adapter.write_lock(mid, data);
EXPECT_EQ(adapter.unlock_read(mid, data), 0);
adapter.unlock_write(mid, data);
}
EXPECT_EQ(lock_counter, exec_count * 2);
EXPECT_EQ(unlock_counter, exec_count * 2);
}
TEST(OneVPL_Source_DX11_FrameLockable, LockUnlock_with_Adaptee)
{
using namespace cv::gapi::wip::onevpl;
mfxMemId mid = 0;
int r_lock_counter = 0;
int r_unlock_counter = 0;
int w_lock_counter = 0;
int w_unlock_counter = 0;
SharedLock adaptee;
std::function<mfxStatus(mfxMemId, mfxFrameData *)> lock =
[&r_lock_counter, &w_lock_counter, &adaptee] (mfxMemId, mfxFrameData *) {
if (adaptee.owns()) {
w_lock_counter ++;
} else {
r_lock_counter ++;
}
return MFX_ERR_NONE;
};
std::function<mfxStatus(mfxMemId, mfxFrameData *)> unlock =
[&r_unlock_counter, &w_unlock_counter, &adaptee] (mfxMemId, mfxFrameData *) {
if (adaptee.owns()) {
w_unlock_counter ++;
} else {
r_unlock_counter ++;
}
return MFX_ERR_NONE;
};
auto test_allocator = create_test_allocator(mid, lock, unlock);
LockAdapter adapter(test_allocator.get());
adapter.set_adaptee(&adaptee);
mfxFrameData data;
const int exec_count = 123;
for (int i = 0; i < exec_count; i ++) {
EXPECT_EQ(adapter.read_lock(mid, data), 0);
EXPECT_FALSE(adaptee.try_lock());
EXPECT_EQ(adapter.unlock_read(mid, data), 1);
EXPECT_TRUE(adaptee.try_lock());
adaptee.unlock();
adapter.write_lock(mid, data);
adapter.unlock_write(mid, data);
}
EXPECT_EQ(r_lock_counter, exec_count);
EXPECT_EQ(r_unlock_counter, exec_count);
EXPECT_EQ(w_lock_counter, exec_count);
EXPECT_EQ(w_unlock_counter, exec_count);
}
}
} // namespace opencv_test
#endif // HAVE_ONEVPL

@ -39,8 +39,6 @@ array_element_t files[] = {
true, true, true},
array_element_t {"highgui/video/sample_322x242_15frames.yuv420p.libvpx-vp9.mp4",
true, true, true},
array_element_t {"highgui/video/sample_322x242_15frames.yuv420p.libx264.avi",
true, true, true},
array_element_t {"highgui/video/sample_322x242_15frames.yuv420p.libx264.mp4",
true, true, true},
array_element_t {"highgui/video/sample_322x242_15frames.yuv420p.libx265.mp4",
@ -82,7 +80,7 @@ TEST_P(OneVPL_Source_MFPAsyncDispatcherTest, open_and_decode_file)
mfxVariant mfx_param_0;
mfx_param_0.Type = MFX_VARIANT_TYPE_U32;
mfx_param_0.Data.U32 = provider_ptr->get_mfx_codec_id();
EXPECT_EQ(MFXSetConfigFilterProperty(cfg_inst_0,(mfxU8 *)"mfxImplDescription.mfxDecoderDescription.decoder.CodecID",
EXPECT_EQ(MFXSetConfigFilterProperty(cfg_inst_0,(mfxU8 *)CfgParam::decoder_id_name(),
mfx_param_0), MFX_ERR_NONE);
// create MFX session
@ -135,7 +133,7 @@ TEST_P(OneVPL_Source_MFPAsyncDispatcherTest, choose_dmux_provider)
EXPECT_FALSE(dd_result);
provider_ptr = DataProviderDispatcher::create(path,
{ CfgParam::create<std::string>(
"mfxImplDescription.mfxDecoderDescription.decoder.CodecID",
CfgParam::decoder_id_name(),
"MFX_CODEC_HEVC") /* Doesn't matter what codec for RAW here*/});
EXPECT_TRUE(std::dynamic_pointer_cast<FileDataProvider>(provider_ptr));
GTEST_SUCCEED();

@ -30,7 +30,7 @@
#endif // HAVE_DIRECTX
#ifdef HAVE_ONEVPL
#include <vpl/mfxvideo.h>
#include "streaming/onevpl/onevpl_export.hpp"
#include "streaming/onevpl/cfg_param_device_selector.hpp"
namespace opencv_test
@ -94,8 +94,7 @@ TEST(OneVPL_Source_Device_Selector_CfgParam, DefaultDeviceWithAccelNACfgParam)
{
using namespace cv::gapi::wip::onevpl;
std::vector<CfgParam> cfg_params_w_no_accel;
cfg_params_w_no_accel.push_back(CfgParam::create<uint32_t>("mfxImplDescription.AccelerationMode",
MFX_ACCEL_MODE_NA));
cfg_params_w_no_accel.push_back(CfgParam::create_acceleration_mode(MFX_ACCEL_MODE_NA));
CfgParamDeviceSelector selector(cfg_params_w_no_accel);
IDeviceSelector::DeviceScoreTable devs = selector.select_devices();
EXPECT_EQ(devs.size(), 1);
@ -126,8 +125,7 @@ TEST(OneVPL_Source_Device_Selector_CfgParam, DefaultDeviceWithDX11AccelCfgParam_
{
using namespace cv::gapi::wip::onevpl;
std::vector<CfgParam> cfg_params_w_dx11;
cfg_params_w_dx11.push_back(CfgParam::create<uint32_t>("mfxImplDescription.AccelerationMode",
MFX_ACCEL_MODE_VIA_D3D11));
cfg_params_w_dx11.push_back(CfgParam::create_acceleration_mode(MFX_ACCEL_MODE_VIA_D3D11));
std::unique_ptr<CfgParamDeviceSelector> selector_ptr;
EXPECT_NO_THROW(selector_ptr.reset(new CfgParamDeviceSelector(cfg_params_w_dx11)));
IDeviceSelector::DeviceScoreTable devs = selector_ptr->select_devices();
@ -146,8 +144,7 @@ TEST(OneVPL_Source_Device_Selector_CfgParam, NULLDeviceWithDX11AccelCfgParam_DX1
{
using namespace cv::gapi::wip::onevpl;
std::vector<CfgParam> cfg_params_w_dx11;
cfg_params_w_dx11.push_back(CfgParam::create<uint32_t>("mfxImplDescription.AccelerationMode",
MFX_ACCEL_MODE_VIA_D3D11));
cfg_params_w_dx11.push_back(CfgParam::create_acceleration_mode(MFX_ACCEL_MODE_VIA_D3D11));
Device::Ptr empty_device_ptr = nullptr;
Context::Ptr empty_ctx_ptr = nullptr;
EXPECT_THROW(CfgParamDeviceSelector sel(empty_device_ptr, "GPU",
@ -179,8 +176,7 @@ TEST(OneVPL_Source_Device_Selector_CfgParam, ExternalDeviceWithDX11AccelCfgParam
std::unique_ptr<CfgParamDeviceSelector> selector_ptr;
std::vector<CfgParam> cfg_params_w_dx11;
cfg_params_w_dx11.push_back(CfgParam::create<uint32_t>("mfxImplDescription.AccelerationMode",
MFX_ACCEL_MODE_VIA_D3D11));
cfg_params_w_dx11.push_back(CfgParam::create_acceleration_mode(MFX_ACCEL_MODE_VIA_D3D11));
EXPECT_NO_THROW(selector_ptr.reset(new CfgParamDeviceSelector(device, "GPU",
device_context,
cfg_params_w_dx11)));
@ -205,8 +201,7 @@ TEST(OneVPL_Source_Device_Selector_CfgParam, DX11DeviceFromCfgParamWithDX11Disab
{
using namespace cv::gapi::wip::onevpl;
std::vector<CfgParam> cfg_params_w_non_existed_dx11;
cfg_params_w_not_existed_dx11.push_back(CfgParam::create<uint32_t>("mfxImplDescription.AccelerationMode",
MFX_ACCEL_MODE_VIA_D3D11));
cfg_params_w_not_existed_dx11.push_back(CfgParam::create_acceleration_mode(MFX_ACCEL_MODE_VIA_D3D11));
EXPECT_THROW(CfgParamDeviceSelector{cfg_params_w_non_existed_dx11},
std::logic_error);
}

Loading…
Cancel
Save