Open Source Computer Vision Library
https://opencv.org/
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
387 lines
15 KiB
387 lines
15 KiB
// 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 <algorithm> |
|
#include <sstream> |
|
|
|
#include "streaming/onevpl/engine/decode/decode_engine_legacy.hpp" |
|
#include "streaming/onevpl/accelerators/accel_policy_dx11.hpp" |
|
#include "streaming/onevpl/accelerators/accel_policy_cpu.hpp" |
|
#include "streaming/onevpl/utils.hpp" |
|
#include "streaming/onevpl/cfg_params_parser.hpp" |
|
#include "streaming/onevpl/data_provider_defines.hpp" |
|
|
|
#include "streaming/onevpl/source_priv.hpp" |
|
#include "logger.hpp" |
|
|
|
#ifndef HAVE_ONEVPL |
|
namespace cv { |
|
namespace gapi { |
|
namespace wip { |
|
namespace onevpl { |
|
bool GSource::Priv::pull(cv::gapi::wip::Data&) { |
|
return true; |
|
} |
|
GMetaArg GSource::Priv::descr_of() const { |
|
return {}; |
|
} |
|
} // namespace onevpl |
|
} // namespace wip |
|
} // namespace gapi |
|
} // namespace cv |
|
|
|
#else // HAVE_ONEVPL |
|
|
|
namespace cv { |
|
namespace gapi { |
|
namespace wip { |
|
namespace onevpl { |
|
|
|
enum { |
|
VPL_NEW_API_MAJOR_VERSION = 2, |
|
VPL_NEW_API_MINOR_VERSION = 2 |
|
}; |
|
|
|
GSource::Priv::Priv() : |
|
mfx_handle(MFXLoad()), |
|
mfx_impl_description(), |
|
mfx_handle_configs(), |
|
cfg_params(), |
|
mfx_session(), |
|
description(), |
|
description_is_valid(false), |
|
engine() |
|
{ |
|
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>) : |
|
GSource::Priv() |
|
{ |
|
// Enable Config |
|
if (params.empty()) |
|
{ |
|
GAPI_LOG_INFO(nullptr, "No special cfg params requested - use default"); |
|
this->cfg_params = getDefaultCfgParams(); |
|
} |
|
else |
|
{ |
|
this->cfg_params = params; |
|
} |
|
|
|
GAPI_LOG_DEBUG(nullptr, "Requested cfg params count: " << cfg_params.size()); |
|
this->mfx_handle_configs.resize(cfg_params.size()); |
|
|
|
// Build VPL handle config from major input params |
|
// VPL dispatcher then uses this config handle to look up for all existing VPL impl |
|
// satisfying major input params and available in the system |
|
GAPI_LOG_INFO(nullptr, "Creating VPL config from input params"); |
|
auto cfg_param_it = cfg_params.begin(); |
|
for (mfxConfig& cfg_inst : mfx_handle_configs) { |
|
cfg_inst = MFXCreateConfig(mfx_handle); |
|
GAPI_Assert(cfg_inst && "MFXCreateConfig failed"); |
|
|
|
if (!cfg_param_it->is_major()) { |
|
GAPI_LOG_DEBUG(nullptr, "Skip not major param: " << cfg_param_it->get_name()); |
|
++cfg_param_it; |
|
continue; |
|
} |
|
|
|
GAPI_LOG_DEBUG(nullptr, "Apply major param: " << cfg_param_it->get_name()); |
|
mfxVariant mfx_param = cfg_param_to_mfx_variant(*cfg_param_it); |
|
mfxStatus sts = MFXSetConfigFilterProperty(cfg_inst, |
|
(mfxU8 *)cfg_param_it->get_name().c_str(), |
|
mfx_param); |
|
if (sts != MFX_ERR_NONE ) |
|
{ |
|
GAPI_LOG_WARNING(nullptr, "MFXSetConfigFilterProperty failed, error: " << |
|
mfxstatus_to_string(sts) << |
|
" - for \"" << cfg_param_it->get_name() << "\""); |
|
GAPI_Assert(false && "MFXSetConfigFilterProperty failed"); |
|
} |
|
|
|
++cfg_param_it; |
|
} |
|
|
|
// collect optional-preferred input parameters from input params |
|
// which may (optionally) or may not be used to choose the most preferrable |
|
// VPL implementation (for example, specific API version or Debug/Release VPL build) |
|
std::vector<CfgParam> preferred_params; |
|
std::copy_if(cfg_params.begin(), cfg_params.end(), std::back_inserter(preferred_params), |
|
[] (const CfgParam& param) { return !param.is_major(); }); |
|
std::sort(preferred_params.begin(), preferred_params.end()); |
|
|
|
GAPI_LOG_DEBUG(nullptr, "Find MFX better implementation from handle: " << mfx_handle << |
|
" is satisfying preferrable params count: " << preferred_params.size()); |
|
int i = 0; |
|
mfxImplDescription *idesc = nullptr; |
|
std::vector<mfxImplDescription*> available_impl_descriptions; |
|
std::map<size_t/*matches count*/, int /*impl index*/> matches_count; |
|
while (MFX_ERR_NONE == MFXEnumImplementations(mfx_handle, |
|
i, |
|
MFX_IMPLCAPS_IMPLDESCSTRUCTURE, |
|
reinterpret_cast<mfxHDL *>(&idesc))) { |
|
|
|
available_impl_descriptions.push_back(idesc); |
|
|
|
std::stringstream ss; |
|
mfxHDL hImplPath = nullptr; |
|
if (MFX_ERR_NONE == MFXEnumImplementations(mfx_handle, i, MFX_IMPLCAPS_IMPLPATH, &hImplPath)) { |
|
if (hImplPath) { |
|
ss << "Implementation path: " << reinterpret_cast<mfxChar *>(hImplPath) << std::endl; |
|
MFXDispReleaseImplDescription(mfx_handle, hImplPath); |
|
} |
|
} |
|
ss << *idesc << std::endl; |
|
|
|
GAPI_LOG_INFO(nullptr, "Implementation index: " << i << "\n" << ss.str()); |
|
|
|
// Only one VPL implementation is required for GSource here. |
|
// Let's find intersection params from available impl with preferrable input params |
|
// to find best match. |
|
// An available VPL implementation with max matching count |
|
std::vector<CfgParam> impl_params = get_params_from_string<CfgParam>(ss.str()); |
|
std::sort(impl_params.begin(), impl_params.end()); |
|
GAPI_LOG_DEBUG(nullptr, "Find implementation cfg params count: " << impl_params.size()); |
|
|
|
std::vector<CfgParam> matched_params; |
|
std::set_intersection(impl_params.begin(), impl_params.end(), |
|
preferred_params.begin(), preferred_params.end(), |
|
std::back_inserter(matched_params)); |
|
|
|
if (preferred_params.empty()) { |
|
// in case of no input preferrance we consider all params are matched |
|
// for the first available VPL implementation. It will be a chosen one |
|
matches_count.emplace(impl_params.size(), i++); |
|
GAPI_LOG_DEBUG(nullptr, "No preferrable params, use the first one implementation"); |
|
break; |
|
} else { |
|
GAPI_LOG_DEBUG(nullptr, "Equal param intersection count: " << matched_params.size()); |
|
matches_count.emplace(matches_count.size(), i++); |
|
} |
|
} |
|
|
|
// Extract the most suitable VPL implementation by max score |
|
auto max_match_it = matches_count.rbegin(); |
|
GAPI_Assert(max_match_it != matches_count.rend() && |
|
"Cannot find matched MFX implementation for requested configuration"); |
|
|
|
int impl_number = max_match_it->second; |
|
GAPI_LOG_INFO(nullptr, "Chosen implementation index: " << impl_number); |
|
|
|
// release unusable impl available_impl_descriptions |
|
std::swap(mfx_impl_description, available_impl_descriptions[impl_number]); |
|
for (mfxImplDescription* unusable_impl_descr : available_impl_descriptions) { |
|
if (unusable_impl_descr) { |
|
MFXDispReleaseImplDescription(mfx_handle, unusable_impl_descr); |
|
} |
|
} |
|
available_impl_descriptions.clear(); |
|
|
|
// create session for implementation |
|
mfxStatus sts = MFXCreateSession(mfx_handle, impl_number, &mfx_session); |
|
if (MFX_ERR_NONE != sts) { |
|
GAPI_LOG_WARNING(nullptr, "Cannot create MFX Session for implementation index:" << |
|
std::to_string(impl_number) << |
|
", error: " << mfxstatus_to_string(sts)); |
|
} |
|
|
|
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(); |
|
|
|
// TODO Add factory static method in ProcessingEngineBase |
|
if (mfx_impl_description->ApiVersion.Major >= VPL_NEW_API_MAJOR_VERSION) { |
|
GAPI_Assert(false && |
|
"GSource mfx_impl_description->ApiVersion.Major >= VPL_NEW_API_MAJOR_VERSION" |
|
" - is not implemented"); |
|
} else { |
|
engine.reset(new VPLLegacyDecodeEngine(std::move(acceleration))); |
|
} |
|
} |
|
|
|
//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()); |
|
|
|
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)); |
|
} |
|
|
|
// set valid description |
|
description.size = cv::Size { |
|
mfxDecParams.mfx.FrameInfo.Width, |
|
mfxDecParams.mfx.FrameInfo.Height}; |
|
switch(mfxDecParams.mfx.FrameInfo.FourCC) { |
|
case MFX_FOURCC_I420: |
|
GAPI_Assert(false && "Cannot create 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"); |
|
} |
|
} |
|
description_is_valid = true; |
|
|
|
return {bitstream, mfxDecParams}; |
|
} |
|
|
|
std::unique_ptr<VPLAccelerationPolicy> GSource::Priv::initializeHWAccel() |
|
{ |
|
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"; |
|
}); |
|
if (accel_mode_it == cfg_params.end()) |
|
{ |
|
GAPI_LOG_DEBUG(nullptr, "No HW Accel requested. Use CPU"); |
|
|
|
ret.reset(new VPLCPUAccelerationPolicy); |
|
return ret; |
|
} |
|
|
|
GAPI_LOG_DEBUG(nullptr, "Add HW acceleration support"); |
|
mfxVariant accel_mode = cfg_param_to_mfx_variant(*accel_mode_it); |
|
|
|
switch(accel_mode.Data.U32) { |
|
case MFX_ACCEL_MODE_VIA_D3D11: |
|
{ |
|
std::unique_ptr<VPLDX11AccelerationPolicy> cand(new VPLDX11AccelerationPolicy); |
|
ret = std::move(cand); |
|
break; |
|
} |
|
case MFX_ACCEL_MODE_NA: |
|
{ |
|
std::unique_ptr<VPLCPUAccelerationPolicy> cand(new VPLCPUAccelerationPolicy); |
|
ret = std::move(cand); |
|
break; |
|
} |
|
default: |
|
{ |
|
GAPI_LOG_WARNING(nullptr, "Cannot initialize HW Accel: " |
|
"invalid accelerator type: " << |
|
std::to_string(accel_mode.Data.U32)); |
|
GAPI_Assert(false && "Cannot initialize HW Accel"); |
|
} |
|
} |
|
|
|
return ret; |
|
} |
|
|
|
const std::vector<CfgParam>& GSource::Priv::getDefaultCfgParams() |
|
{ |
|
static const std::vector<CfgParam> def_params = |
|
get_params_from_string<CfgParam>( |
|
"mfxImplDescription.Impl: MFX_IMPL_TYPE_HARDWARE\n" |
|
"mfxImplDescription.AccelerationMode: MFX_ACCEL_MODE_VIA_D3D11\n"); |
|
|
|
return def_params; |
|
} |
|
|
|
const std::vector<CfgParam>& GSource::Priv::getCfgParams() const |
|
{ |
|
return cfg_params; |
|
} |
|
|
|
bool GSource::Priv::pull(cv::gapi::wip::Data& data) |
|
{ |
|
ProcessingEngineBase::ExecutionStatus status = ProcessingEngineBase::ExecutionStatus::Continue; |
|
while (0 == engine->get_ready_frames_count() && |
|
status == ProcessingEngineBase::ExecutionStatus::Continue) { |
|
status = engine->process(mfx_session); |
|
} |
|
|
|
if (engine->get_ready_frames_count()) { |
|
engine->get_frame(data); |
|
return true; |
|
} else { |
|
return false; |
|
} |
|
} |
|
|
|
GMetaArg GSource::Priv::descr_of() const |
|
{ |
|
GAPI_Assert(description_is_valid); |
|
GMetaArg arg(description); |
|
return arg; |
|
} |
|
} // namespace onevpl |
|
} // namespace wip |
|
} // namespace gapi |
|
} // namespace cv |
|
|
|
#endif // HAVE_ONEVPL
|
|
|