Merge pull request #21022 from sivanov-work:async_mfp_demux
G-API: oneVPL Implement asynchronous MFP demux data provider * Add dummy dmux * Initial commit for draft versionn * Demux for low res file works * Add media source resolver to work over incorrect MIME * Add MFP Demux logger * stash changes * Extend IDataProvider with CodecId, Add troubleshooting info * Add IDapaProvider dispatcher * Add ComPtrGuard wrappers * Add new unit test scope for MFP demux & Add minor changes * Enhance UTs * Remove ATL header * Remove ATL another one * Fix build * Add static for some methods * Initial commit * Add async demuxing * Apply tdd idea * Intro IDataProvider changes: +fetch_bitstream, -fetch_data * Fix UTs * Remove IDataProvider::CodecId & Fix EOF hang * Remove sync demux * Remove mfp async dependencies * Remove VPL dependencies from IDataProvider declaration * Apply comments * Fix compilation * Suppress unused warning * Apply some comments * Apply some comments * Apply commentspull/21113/head
parent
ac4b592b4e
commit
02f08879a4
24 changed files with 1725 additions and 148 deletions
@ -0,0 +1,32 @@ |
|||||||
|
#ifndef GAPI_STREAMING_ONEVPL_DATA_PROVIDER_DEFINES_HPP |
||||||
|
#define GAPI_STREAMING_ONEVPL_DATA_PROVIDER_DEFINES_HPP |
||||||
|
|
||||||
|
#ifdef HAVE_ONEVPL |
||||||
|
#include <vpl/mfxcommon.h> |
||||||
|
#include <vpl/mfxvideo.h> |
||||||
|
#endif // HAVE_ONEVPL
|
||||||
|
|
||||||
|
#include <opencv2/gapi/own/assert.hpp> |
||||||
|
#include <opencv2/gapi/streaming/onevpl/data_provider_interface.hpp> |
||||||
|
|
||||||
|
namespace cv { |
||||||
|
namespace gapi { |
||||||
|
namespace wip { |
||||||
|
namespace onevpl { |
||||||
|
|
||||||
|
#ifdef HAVE_ONEVPL |
||||||
|
struct IDataProvider::mfx_bitstream : public mfxBitstream {}; |
||||||
|
#else // HAVE_ONEVPL
|
||||||
|
struct IDataProvider::mfx_bitstream { |
||||||
|
mfx_bitstream() { |
||||||
|
GAPI_Assert(false && "Reject to create `mfxBitstream` because library compiled without VPL/MFX support"); |
||||||
|
} |
||||||
|
}; |
||||||
|
#endif // HAVE_ONEVPL
|
||||||
|
|
||||||
|
} // namespace onevpl
|
||||||
|
} // namespace wip
|
||||||
|
} // namespace gapi
|
||||||
|
} // namespace cv
|
||||||
|
|
||||||
|
#endif // GAPI_STREAMING_ONEVPL_DATA_PROVIDER_DEFINES_HPP
|
@ -0,0 +1,68 @@ |
|||||||
|
// 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/data_provider_dispatcher.hpp" |
||||||
|
#include "streaming/onevpl/file_data_provider.hpp" |
||||||
|
#include "streaming/onevpl/demux/async_mfp_demux_data_provider.hpp" |
||||||
|
#include "logger.hpp" |
||||||
|
|
||||||
|
namespace cv { |
||||||
|
namespace gapi { |
||||||
|
namespace wip { |
||||||
|
namespace onevpl { |
||||||
|
|
||||||
|
IDataProvider::Ptr DataProviderDispatcher::create(const std::string& file_path, |
||||||
|
const std::vector<CfgParam> &cfg_params) { |
||||||
|
GAPI_LOG_INFO(nullptr, "try select suitable IDataProvider for source: " << |
||||||
|
file_path); |
||||||
|
|
||||||
|
IDataProvider::Ptr provider; |
||||||
|
|
||||||
|
// 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\""); |
||||||
|
auto codec_it = |
||||||
|
std::find_if(cfg_params.begin(), cfg_params.end(), [] (const CfgParam& value) { |
||||||
|
return value.get_name() == "mfxImplDescription.mfxDecoderDescription.decoder.CodecID"; |
||||||
|
}); |
||||||
|
if (codec_it != cfg_params.end()) { |
||||||
|
GAPI_LOG_DEBUG(nullptr, "Dispatcher found \"mfxImplDescription.mfxDecoderDescription.decoder.CodecID\"" |
||||||
|
" so try on raw data provider at first"); |
||||||
|
|
||||||
|
try { |
||||||
|
provider = std::make_shared<FileDataProvider>(file_path, cfg_params); |
||||||
|
GAPI_LOG_INFO(nullptr, "raw data provider created"); |
||||||
|
} catch (const DataProviderUnsupportedException& ex) { |
||||||
|
GAPI_LOG_INFO(nullptr, "raw data provider creation is failed, reason: " << |
||||||
|
ex.what()); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if (!provider) { |
||||||
|
GAPI_LOG_DEBUG(nullptr, "Try on MFP data provider"); |
||||||
|
try { |
||||||
|
provider = std::make_shared<MFPAsyncDemuxDataProvider>(file_path); |
||||||
|
GAPI_LOG_INFO(nullptr, "MFP data provider created"); |
||||||
|
} catch (const DataProviderUnsupportedException& ex) { |
||||||
|
GAPI_LOG_INFO(nullptr, "MFP data provider creation is failed, reason: " << |
||||||
|
ex.what()); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// final check
|
||||||
|
if (!provider) { |
||||||
|
GAPI_LOG_WARNING(nullptr, "Cannot find suitable data provider"); |
||||||
|
throw DataProviderUnsupportedException("Unsupported source or configuration parameters");; |
||||||
|
} |
||||||
|
return provider; |
||||||
|
} |
||||||
|
} // namespace onevpl
|
||||||
|
} // namespace wip
|
||||||
|
} // namespace gapi
|
||||||
|
} // namespace cv
|
||||||
|
#endif // HAVE_ONEVPL
|
@ -0,0 +1,29 @@ |
|||||||
|
// 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_DATA_PROVIDER_DISPATCHER_HPP |
||||||
|
#define GAPI_STREAMING_ONEVPL_DATA_PROVIDER_DISPATCHER_HPP |
||||||
|
|
||||||
|
#ifdef HAVE_ONEVPL |
||||||
|
#include <opencv2/gapi/streaming/onevpl/data_provider_interface.hpp> |
||||||
|
#include <opencv2/gapi/streaming/onevpl/cfg_params.hpp> |
||||||
|
|
||||||
|
namespace cv { |
||||||
|
namespace gapi { |
||||||
|
namespace wip { |
||||||
|
namespace onevpl { |
||||||
|
|
||||||
|
struct GAPI_EXPORTS DataProviderDispatcher { |
||||||
|
|
||||||
|
static IDataProvider::Ptr create(const std::string& file_path, |
||||||
|
const std::vector<CfgParam> &codec_params = {}); |
||||||
|
}; |
||||||
|
} // namespace onevpl
|
||||||
|
} // namespace wip
|
||||||
|
} // namespace gapi
|
||||||
|
} // namespace cv
|
||||||
|
#endif // HAVE_ONEVPL
|
||||||
|
#endif // GAPI_STREAMING_ONEVPL_DATA_PROVIDER_DISPATCHER_HPP
|
@ -0,0 +1,820 @@ |
|||||||
|
// 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 <errno.h> |
||||||
|
#ifdef _WIN32 |
||||||
|
|
||||||
|
#pragma comment(lib, "Mf.lib") |
||||||
|
#pragma comment(lib, "Mfuuid.lib") |
||||||
|
#pragma comment(lib, "Mfplat.lib") |
||||||
|
#pragma comment(lib, "shlwapi.lib") |
||||||
|
#pragma comment(lib, "mfreadwrite.lib") |
||||||
|
#endif // _WIN32
|
||||||
|
|
||||||
|
#include "streaming/onevpl/demux/async_mfp_demux_data_provider.hpp" |
||||||
|
#include "logger.hpp" |
||||||
|
|
||||||
|
namespace cv { |
||||||
|
namespace gapi { |
||||||
|
namespace wip { |
||||||
|
namespace onevpl { |
||||||
|
#ifdef _WIN32 |
||||||
|
static HRESULT create_media_source(const std::string& url, IMFMediaSource **ppSource) { |
||||||
|
wchar_t sURL[MAX_PATH]; |
||||||
|
GAPI_Assert(url.size() < MAX_PATH && "Windows MAX_PATH limit was reached"); |
||||||
|
size_t ret_url_length = 0; |
||||||
|
mbstowcs_s(&ret_url_length, sURL, url.data(), url.size()); |
||||||
|
|
||||||
|
HRESULT hr = S_OK; |
||||||
|
ComPtrGuard<IMFSourceResolver> source_resolver = createCOMPtrGuard<IMFSourceResolver>(); |
||||||
|
{ |
||||||
|
IMFSourceResolver *source_resolver_tmp = nullptr; |
||||||
|
hr = MFCreateSourceResolver(&source_resolver_tmp); |
||||||
|
if (FAILED(hr)) { |
||||||
|
throw DataProviderSystemErrorException(HRESULT_CODE(hr), |
||||||
|
"cannot create MFCreateSourceResolver from URI: " + |
||||||
|
url); |
||||||
|
} |
||||||
|
source_resolver.reset(source_resolver_tmp); |
||||||
|
} |
||||||
|
|
||||||
|
MF_OBJECT_TYPE ObjectType = MF_OBJECT_INVALID; |
||||||
|
/**
|
||||||
|
* NB: |
||||||
|
* CreateObjectFromURL throws exception if actual container type is mismatched with |
||||||
|
* file extension. To overcome this situation by MFP it is possible to apply 2 step |
||||||
|
* approach: at first step we pass special flag |
||||||
|
* `MF_RESOLUTION_KEEP_BYTE_STREAM_ALIVE_ON_FAIL` which claims to fail with error |
||||||
|
* in any case of input instead exception throwing; |
||||||
|
* at the second step we must cease `MF_RESOLUTION_KEEP_BYTE_STREAM_ALIVE_ON_FAIL` |
||||||
|
* flag AND set another special flag |
||||||
|
* `MF_RESOLUTION_CONTENT_DOES_NOT_HAVE_TO_MATCH_EXTENSION_OR_MIME_TYPE` |
||||||
|
* to filter out container type & file extension mismatch errors. |
||||||
|
* |
||||||
|
* If it failed at second phase then some other errors were not related |
||||||
|
* to types-extension disturbance would happen and data provider must fail ultimately. |
||||||
|
* |
||||||
|
* If second step passed then data provider would continue execution |
||||||
|
*/ |
||||||
|
IUnknown *source_unknown_tmp = nullptr; |
||||||
|
DWORD resolver_flags = MF_RESOLUTION_MEDIASOURCE | MF_RESOLUTION_READ | |
||||||
|
MF_RESOLUTION_KEEP_BYTE_STREAM_ALIVE_ON_FAIL; |
||||||
|
hr = source_resolver->CreateObjectFromURL(sURL, |
||||||
|
resolver_flags, |
||||||
|
nullptr, &ObjectType, |
||||||
|
&source_unknown_tmp); |
||||||
|
if (FAILED(hr)) { |
||||||
|
GAPI_LOG_DEBUG(nullptr, "Cannot create MF_RESOLUTION_MEDIASOURCE using file extension, " |
||||||
|
" looks like actual media container type doesn't match to file extension. " |
||||||
|
"Try special mode"); |
||||||
|
resolver_flags ^= MF_RESOLUTION_KEEP_BYTE_STREAM_ALIVE_ON_FAIL; |
||||||
|
resolver_flags ^= MF_RESOLUTION_CONTENT_DOES_NOT_HAVE_TO_MATCH_EXTENSION_OR_MIME_TYPE; |
||||||
|
hr = source_resolver->CreateObjectFromURL(sURL, resolver_flags, |
||||||
|
nullptr, &ObjectType, |
||||||
|
&source_unknown_tmp); |
||||||
|
if (FAILED(hr)) { |
||||||
|
GAPI_LOG_WARNING(nullptr, "Cannot create MF_RESOLUTION_MEDIASOURCE from URI: " << |
||||||
|
url << ". Abort"); |
||||||
|
throw DataProviderSystemErrorException(HRESULT_CODE(hr), |
||||||
|
"CreateObjectFromURL failed"); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
ComPtrGuard<IUnknown> source_unknown = createCOMPtrGuard(source_unknown_tmp); |
||||||
|
hr = source_unknown->QueryInterface(__uuidof(IMFMediaSource), (void**)ppSource); |
||||||
|
if (FAILED(hr)) { |
||||||
|
throw DataProviderSystemErrorException(HRESULT_CODE(hr), |
||||||
|
"QueryInterface for IMFMediaSource failed"); |
||||||
|
} |
||||||
|
|
||||||
|
return hr; |
||||||
|
} |
||||||
|
|
||||||
|
/*
|
||||||
|
* The next part of converting GUID into string function GetGUIDNameConst |
||||||
|
* was copied and modified from |
||||||
|
* https://docs.microsoft.com/en-us/windows/win32/medfound/media-type-debugging-code
|
||||||
|
*/ |
||||||
|
#ifndef IF_EQUAL_RETURN |
||||||
|
#define IF_EQUAL_RETURN(param, val) if(val == param) return #val |
||||||
|
#endif |
||||||
|
|
||||||
|
static const char* GetGUIDNameConst(const GUID& guid) |
||||||
|
{ |
||||||
|
IF_EQUAL_RETURN(guid, MF_MT_MAJOR_TYPE); |
||||||
|
IF_EQUAL_RETURN(guid, MF_MT_MAJOR_TYPE); |
||||||
|
IF_EQUAL_RETURN(guid, MF_MT_SUBTYPE); |
||||||
|
IF_EQUAL_RETURN(guid, MF_MT_ALL_SAMPLES_INDEPENDENT); |
||||||
|
IF_EQUAL_RETURN(guid, MF_MT_FIXED_SIZE_SAMPLES); |
||||||
|
IF_EQUAL_RETURN(guid, MF_MT_COMPRESSED); |
||||||
|
IF_EQUAL_RETURN(guid, MF_MT_SAMPLE_SIZE); |
||||||
|
IF_EQUAL_RETURN(guid, MF_MT_WRAPPED_TYPE); |
||||||
|
IF_EQUAL_RETURN(guid, MF_MT_AUDIO_NUM_CHANNELS); |
||||||
|
IF_EQUAL_RETURN(guid, MF_MT_AUDIO_SAMPLES_PER_SECOND); |
||||||
|
IF_EQUAL_RETURN(guid, MF_MT_AUDIO_FLOAT_SAMPLES_PER_SECOND); |
||||||
|
IF_EQUAL_RETURN(guid, MF_MT_AUDIO_AVG_BYTES_PER_SECOND); |
||||||
|
IF_EQUAL_RETURN(guid, MF_MT_AUDIO_BLOCK_ALIGNMENT); |
||||||
|
IF_EQUAL_RETURN(guid, MF_MT_AUDIO_BITS_PER_SAMPLE); |
||||||
|
IF_EQUAL_RETURN(guid, MF_MT_AUDIO_VALID_BITS_PER_SAMPLE); |
||||||
|
IF_EQUAL_RETURN(guid, MF_MT_AUDIO_SAMPLES_PER_BLOCK); |
||||||
|
IF_EQUAL_RETURN(guid, MF_MT_AUDIO_CHANNEL_MASK); |
||||||
|
IF_EQUAL_RETURN(guid, MF_MT_AUDIO_FOLDDOWN_MATRIX); |
||||||
|
IF_EQUAL_RETURN(guid, MF_MT_AUDIO_WMADRC_PEAKREF); |
||||||
|
IF_EQUAL_RETURN(guid, MF_MT_AUDIO_WMADRC_PEAKTARGET); |
||||||
|
IF_EQUAL_RETURN(guid, MF_MT_AUDIO_WMADRC_AVGREF); |
||||||
|
IF_EQUAL_RETURN(guid, MF_MT_AUDIO_WMADRC_AVGTARGET); |
||||||
|
IF_EQUAL_RETURN(guid, MF_MT_AUDIO_PREFER_WAVEFORMATEX); |
||||||
|
IF_EQUAL_RETURN(guid, MF_MT_AAC_PAYLOAD_TYPE); |
||||||
|
IF_EQUAL_RETURN(guid, MF_MT_AAC_AUDIO_PROFILE_LEVEL_INDICATION); |
||||||
|
IF_EQUAL_RETURN(guid, MF_MT_FRAME_SIZE); |
||||||
|
IF_EQUAL_RETURN(guid, MF_MT_FRAME_RATE); |
||||||
|
IF_EQUAL_RETURN(guid, MF_MT_FRAME_RATE_RANGE_MAX); |
||||||
|
IF_EQUAL_RETURN(guid, MF_MT_FRAME_RATE_RANGE_MIN); |
||||||
|
IF_EQUAL_RETURN(guid, MF_MT_PIXEL_ASPECT_RATIO); |
||||||
|
IF_EQUAL_RETURN(guid, MF_MT_DRM_FLAGS); |
||||||
|
IF_EQUAL_RETURN(guid, MF_MT_PAD_CONTROL_FLAGS); |
||||||
|
IF_EQUAL_RETURN(guid, MF_MT_SOURCE_CONTENT_HINT); |
||||||
|
IF_EQUAL_RETURN(guid, MF_MT_VIDEO_CHROMA_SITING); |
||||||
|
IF_EQUAL_RETURN(guid, MF_MT_INTERLACE_MODE); |
||||||
|
IF_EQUAL_RETURN(guid, MF_MT_TRANSFER_FUNCTION); |
||||||
|
IF_EQUAL_RETURN(guid, MF_MT_VIDEO_PRIMARIES); |
||||||
|
IF_EQUAL_RETURN(guid, MF_MT_CUSTOM_VIDEO_PRIMARIES); |
||||||
|
IF_EQUAL_RETURN(guid, MF_MT_YUV_MATRIX); |
||||||
|
IF_EQUAL_RETURN(guid, MF_MT_VIDEO_LIGHTING); |
||||||
|
IF_EQUAL_RETURN(guid, MF_MT_VIDEO_NOMINAL_RANGE); |
||||||
|
IF_EQUAL_RETURN(guid, MF_MT_GEOMETRIC_APERTURE); |
||||||
|
IF_EQUAL_RETURN(guid, MF_MT_MINIMUM_DISPLAY_APERTURE); |
||||||
|
IF_EQUAL_RETURN(guid, MF_MT_PAN_SCAN_APERTURE); |
||||||
|
IF_EQUAL_RETURN(guid, MF_MT_PAN_SCAN_ENABLED); |
||||||
|
IF_EQUAL_RETURN(guid, MF_MT_AVG_BITRATE); |
||||||
|
IF_EQUAL_RETURN(guid, MF_MT_AVG_BIT_ERROR_RATE); |
||||||
|
IF_EQUAL_RETURN(guid, MF_MT_MAX_KEYFRAME_SPACING); |
||||||
|
IF_EQUAL_RETURN(guid, MF_MT_DEFAULT_STRIDE); |
||||||
|
IF_EQUAL_RETURN(guid, MF_MT_PALETTE); |
||||||
|
IF_EQUAL_RETURN(guid, MF_MT_USER_DATA); |
||||||
|
IF_EQUAL_RETURN(guid, MF_MT_AM_FORMAT_TYPE); |
||||||
|
IF_EQUAL_RETURN(guid, MF_MT_MPEG_START_TIME_CODE); |
||||||
|
IF_EQUAL_RETURN(guid, MF_MT_MPEG2_PROFILE); |
||||||
|
IF_EQUAL_RETURN(guid, MF_MT_MPEG2_LEVEL); |
||||||
|
IF_EQUAL_RETURN(guid, MF_MT_MPEG2_FLAGS); |
||||||
|
IF_EQUAL_RETURN(guid, MF_MT_MPEG_SEQUENCE_HEADER); |
||||||
|
IF_EQUAL_RETURN(guid, MF_MT_DV_AAUX_SRC_PACK_0); |
||||||
|
IF_EQUAL_RETURN(guid, MF_MT_DV_AAUX_CTRL_PACK_0); |
||||||
|
IF_EQUAL_RETURN(guid, MF_MT_DV_AAUX_SRC_PACK_1); |
||||||
|
IF_EQUAL_RETURN(guid, MF_MT_DV_AAUX_CTRL_PACK_1); |
||||||
|
IF_EQUAL_RETURN(guid, MF_MT_DV_VAUX_SRC_PACK); |
||||||
|
IF_EQUAL_RETURN(guid, MF_MT_DV_VAUX_CTRL_PACK); |
||||||
|
IF_EQUAL_RETURN(guid, MF_MT_ARBITRARY_HEADER); |
||||||
|
IF_EQUAL_RETURN(guid, MF_MT_ARBITRARY_FORMAT); |
||||||
|
IF_EQUAL_RETURN(guid, MF_MT_IMAGE_LOSS_TOLERANT); |
||||||
|
IF_EQUAL_RETURN(guid, MF_MT_MPEG4_SAMPLE_DESCRIPTION); |
||||||
|
IF_EQUAL_RETURN(guid, MF_MT_MPEG4_CURRENT_SAMPLE_ENTRY); |
||||||
|
IF_EQUAL_RETURN(guid, MF_MT_ORIGINAL_4CC); |
||||||
|
IF_EQUAL_RETURN(guid, MF_MT_ORIGINAL_WAVE_FORMAT_TAG); |
||||||
|
|
||||||
|
// Media types
|
||||||
|
|
||||||
|
IF_EQUAL_RETURN(guid, MFMediaType_Audio); |
||||||
|
IF_EQUAL_RETURN(guid, MFMediaType_Video); |
||||||
|
IF_EQUAL_RETURN(guid, MFMediaType_Protected); |
||||||
|
IF_EQUAL_RETURN(guid, MFMediaType_SAMI); |
||||||
|
IF_EQUAL_RETURN(guid, MFMediaType_Script); |
||||||
|
IF_EQUAL_RETURN(guid, MFMediaType_Image); |
||||||
|
IF_EQUAL_RETURN(guid, MFMediaType_HTML); |
||||||
|
IF_EQUAL_RETURN(guid, MFMediaType_Binary); |
||||||
|
IF_EQUAL_RETURN(guid, MFMediaType_FileTransfer); |
||||||
|
|
||||||
|
IF_EQUAL_RETURN(guid, MFVideoFormat_AI44); // FCC('AI44')
|
||||||
|
IF_EQUAL_RETURN(guid, MFVideoFormat_ARGB32); // D3DFMT_A8R8G8B8
|
||||||
|
IF_EQUAL_RETURN(guid, MFVideoFormat_AV1); |
||||||
|
IF_EQUAL_RETURN(guid, MFVideoFormat_AYUV); // FCC('AYUV')
|
||||||
|
IF_EQUAL_RETURN(guid, MFVideoFormat_DV25); // FCC('dv25')
|
||||||
|
IF_EQUAL_RETURN(guid, MFVideoFormat_DV50); // FCC('dv50')
|
||||||
|
IF_EQUAL_RETURN(guid, MFVideoFormat_DVH1); // FCC('dvh1')
|
||||||
|
IF_EQUAL_RETURN(guid, MFVideoFormat_DVSD); // FCC('dvsd')
|
||||||
|
IF_EQUAL_RETURN(guid, MFVideoFormat_DVSL); // FCC('dvsl')
|
||||||
|
IF_EQUAL_RETURN(guid, MFVideoFormat_H264); // FCC('H264')
|
||||||
|
IF_EQUAL_RETURN(guid, MFVideoFormat_H265); |
||||||
|
IF_EQUAL_RETURN(guid, MFVideoFormat_HEVC); |
||||||
|
IF_EQUAL_RETURN(guid, MFVideoFormat_HEVC_ES); |
||||||
|
IF_EQUAL_RETURN(guid, MFVideoFormat_I420); // FCC('I420')
|
||||||
|
IF_EQUAL_RETURN(guid, MFVideoFormat_IYUV); // FCC('IYUV')
|
||||||
|
IF_EQUAL_RETURN(guid, MFVideoFormat_M4S2); // FCC('M4S2')
|
||||||
|
IF_EQUAL_RETURN(guid, MFVideoFormat_MJPG); |
||||||
|
IF_EQUAL_RETURN(guid, MFVideoFormat_MP43); // FCC('MP43')
|
||||||
|
IF_EQUAL_RETURN(guid, MFVideoFormat_MP4S); // FCC('MP4S')
|
||||||
|
IF_EQUAL_RETURN(guid, MFVideoFormat_MP4V); // FCC('MP4V')
|
||||||
|
IF_EQUAL_RETURN(guid, MFVideoFormat_MPG1); // FCC('MPG1')
|
||||||
|
IF_EQUAL_RETURN(guid, MFVideoFormat_MSS1); // FCC('MSS1')
|
||||||
|
IF_EQUAL_RETURN(guid, MFVideoFormat_MSS2); // FCC('MSS2')
|
||||||
|
IF_EQUAL_RETURN(guid, MFVideoFormat_NV11); // FCC('NV11')
|
||||||
|
IF_EQUAL_RETURN(guid, MFVideoFormat_NV12); // FCC('NV12')
|
||||||
|
IF_EQUAL_RETURN(guid, MFVideoFormat_P010); // FCC('P010')
|
||||||
|
IF_EQUAL_RETURN(guid, MFVideoFormat_P016); // FCC('P016')
|
||||||
|
IF_EQUAL_RETURN(guid, MFVideoFormat_P210); // FCC('P210')
|
||||||
|
IF_EQUAL_RETURN(guid, MFVideoFormat_P216); // FCC('P216')
|
||||||
|
IF_EQUAL_RETURN(guid, MFVideoFormat_RGB24); // D3DFMT_R8G8B8
|
||||||
|
IF_EQUAL_RETURN(guid, MFVideoFormat_RGB32); // D3DFMT_X8R8G8B8
|
||||||
|
IF_EQUAL_RETURN(guid, MFVideoFormat_RGB555); // D3DFMT_X1R5G5B5
|
||||||
|
IF_EQUAL_RETURN(guid, MFVideoFormat_RGB565); // D3DFMT_R5G6B5
|
||||||
|
IF_EQUAL_RETURN(guid, MFVideoFormat_RGB8); |
||||||
|
IF_EQUAL_RETURN(guid, MFVideoFormat_UYVY); // FCC('UYVY')
|
||||||
|
IF_EQUAL_RETURN(guid, MFVideoFormat_v210); // FCC('v210')
|
||||||
|
IF_EQUAL_RETURN(guid, MFVideoFormat_v410); // FCC('v410')
|
||||||
|
IF_EQUAL_RETURN(guid, MFVideoFormat_WMV1); // FCC('WMV1')
|
||||||
|
IF_EQUAL_RETURN(guid, MFVideoFormat_WMV2); // FCC('WMV2')
|
||||||
|
IF_EQUAL_RETURN(guid, MFVideoFormat_WMV3); // FCC('WMV3')
|
||||||
|
IF_EQUAL_RETURN(guid, MFVideoFormat_WVC1); // FCC('WVC1')
|
||||||
|
IF_EQUAL_RETURN(guid, MFVideoFormat_VP90); |
||||||
|
IF_EQUAL_RETURN(guid, MFVideoFormat_Y210); // FCC('Y210')
|
||||||
|
IF_EQUAL_RETURN(guid, MFVideoFormat_Y216); // FCC('Y216')
|
||||||
|
IF_EQUAL_RETURN(guid, MFVideoFormat_Y410); // FCC('Y410')
|
||||||
|
IF_EQUAL_RETURN(guid, MFVideoFormat_Y416); // FCC('Y416')
|
||||||
|
IF_EQUAL_RETURN(guid, MFVideoFormat_Y41P); |
||||||
|
IF_EQUAL_RETURN(guid, MFVideoFormat_Y41T); |
||||||
|
IF_EQUAL_RETURN(guid, MFVideoFormat_YUY2); // FCC('YUY2')
|
||||||
|
IF_EQUAL_RETURN(guid, MFVideoFormat_YV12); // FCC('YV12')
|
||||||
|
IF_EQUAL_RETURN(guid, MFVideoFormat_YVYU); |
||||||
|
|
||||||
|
IF_EQUAL_RETURN(guid, MFAudioFormat_PCM); // WAVE_FORMAT_PCM
|
||||||
|
IF_EQUAL_RETURN(guid, MFAudioFormat_Float); // WAVE_FORMAT_IEEE_FLOAT
|
||||||
|
IF_EQUAL_RETURN(guid, MFAudioFormat_DTS); // WAVE_FORMAT_DTS
|
||||||
|
IF_EQUAL_RETURN(guid, MFAudioFormat_Dolby_AC3_SPDIF); // WAVE_FORMAT_DOLBY_AC3_SPDIF
|
||||||
|
IF_EQUAL_RETURN(guid, MFAudioFormat_DRM); // WAVE_FORMAT_DRM
|
||||||
|
IF_EQUAL_RETURN(guid, MFAudioFormat_WMAudioV8); // WAVE_FORMAT_WMAUDIO2
|
||||||
|
IF_EQUAL_RETURN(guid, MFAudioFormat_WMAudioV9); // WAVE_FORMAT_WMAUDIO3
|
||||||
|
IF_EQUAL_RETURN(guid, MFAudioFormat_WMAudio_Lossless); // WAVE_FORMAT_WMAUDIO_LOSSLESS
|
||||||
|
IF_EQUAL_RETURN(guid, MFAudioFormat_WMASPDIF); // WAVE_FORMAT_WMASPDIF
|
||||||
|
IF_EQUAL_RETURN(guid, MFAudioFormat_MSP1); // WAVE_FORMAT_WMAVOICE9
|
||||||
|
IF_EQUAL_RETURN(guid, MFAudioFormat_MP3); // WAVE_FORMAT_MPEGLAYER3
|
||||||
|
IF_EQUAL_RETURN(guid, MFAudioFormat_MPEG); // WAVE_FORMAT_MPEG
|
||||||
|
IF_EQUAL_RETURN(guid, MFAudioFormat_AAC); // WAVE_FORMAT_MPEG_HEAAC
|
||||||
|
IF_EQUAL_RETURN(guid, MFAudioFormat_ADTS); // WAVE_FORMAT_MPEG_ADTS_AAC
|
||||||
|
|
||||||
|
return "<unknown>"; |
||||||
|
} |
||||||
|
|
||||||
|
static IDataProvider::mfx_codec_id_type convert_to_mfx_codec_id(const GUID& guid) { |
||||||
|
if (guid == MFVideoFormat_H264) { |
||||||
|
return MFX_CODEC_AVC; |
||||||
|
} else if (guid == MFVideoFormat_H265 || |
||||||
|
guid == MFVideoFormat_HEVC || |
||||||
|
guid == MFVideoFormat_HEVC_ES) { |
||||||
|
return MFX_CODEC_HEVC; |
||||||
|
} else if (guid == MFAudioFormat_MPEG) { |
||||||
|
return MFX_CODEC_MPEG2; |
||||||
|
} else if (guid == MFVideoFormat_WVC1) { |
||||||
|
return MFX_CODEC_VC1; |
||||||
|
} else if (guid == MFVideoFormat_VP90) { |
||||||
|
return MFX_CODEC_VP9; |
||||||
|
} else if (guid == MFVideoFormat_AV1) { |
||||||
|
return MFX_CODEC_AV1; |
||||||
|
} else if (guid == MFVideoFormat_MJPG) { |
||||||
|
return MFX_CODEC_JPEG; |
||||||
|
} |
||||||
|
|
||||||
|
throw DataProviderUnsupportedException(std::string("unsupported codec type: ") + |
||||||
|
GetGUIDNameConst(guid)); |
||||||
|
} |
||||||
|
|
||||||
|
bool MFPAsyncDemuxDataProvider::select_supported_video_stream( |
||||||
|
ComPtrGuard<IMFPresentationDescriptor> &descriptor, |
||||||
|
mfx_codec_id_type &out_codec_id, |
||||||
|
void *source_id) { |
||||||
|
DWORD stream_count = 0; |
||||||
|
BOOL is_stream_selected = false; |
||||||
|
descriptor->GetStreamDescriptorCount(&stream_count); |
||||||
|
GAPI_LOG_DEBUG(nullptr, "[" << source_id << "] " << |
||||||
|
"received stream count: " << stream_count); |
||||||
|
for (DWORD stream_index = 0; |
||||||
|
stream_index < stream_count && !is_stream_selected; stream_index++) { |
||||||
|
|
||||||
|
GAPI_LOG_DEBUG(nullptr, "[" << source_id << "] " << |
||||||
|
"check stream info by index: " << stream_index); |
||||||
|
IMFStreamDescriptor *stream_descriptor_tmp = nullptr; |
||||||
|
descriptor->GetStreamDescriptorByIndex(stream_index, &is_stream_selected, |
||||||
|
&stream_descriptor_tmp); |
||||||
|
if (!stream_descriptor_tmp) { |
||||||
|
GAPI_LOG_WARNING(nullptr, "[" << source_id << "] " << |
||||||
|
"Cannot get stream descriptor by index: " << |
||||||
|
stream_index); |
||||||
|
continue; |
||||||
|
} |
||||||
|
|
||||||
|
ComPtrGuard<IMFStreamDescriptor> stream_descriptor = |
||||||
|
createCOMPtrGuard(stream_descriptor_tmp); |
||||||
|
is_stream_selected = false; // deselect until supported stream found
|
||||||
|
IMFMediaTypeHandler *handler_tmp = nullptr; |
||||||
|
stream_descriptor->GetMediaTypeHandler(&handler_tmp); |
||||||
|
if (!handler_tmp) { |
||||||
|
GAPI_LOG_WARNING(nullptr, "[" << source_id << "] " << |
||||||
|
"Cannot get media type handler for stream by index: " << |
||||||
|
stream_index); |
||||||
|
continue; |
||||||
|
} |
||||||
|
|
||||||
|
ComPtrGuard<IMFMediaTypeHandler> handler = createCOMPtrGuard(handler_tmp); |
||||||
|
GUID guidMajorType; |
||||||
|
if (FAILED(handler->GetMajorType(&guidMajorType))) { |
||||||
|
GAPI_LOG_WARNING(nullptr, "[" << source_id << "] " << |
||||||
|
"Cannot get major GUID type for stream by index: " << |
||||||
|
stream_index); |
||||||
|
continue; |
||||||
|
} |
||||||
|
|
||||||
|
if (guidMajorType != MFMediaType_Video) { |
||||||
|
GAPI_LOG_DEBUG(nullptr, "[" << source_id << "] " << |
||||||
|
"Skipping non-video stream"); |
||||||
|
continue; |
||||||
|
} |
||||||
|
GAPI_LOG_DEBUG(nullptr, "[" << source_id << "] " << |
||||||
|
"video stream detected"); |
||||||
|
IMFMediaType *media_type_tmp = nullptr; |
||||||
|
handler->GetCurrentMediaType(&media_type_tmp); |
||||||
|
if (!media_type_tmp) { |
||||||
|
GAPI_LOG_WARNING(nullptr, "[" << source_id << "] " << |
||||||
|
"Cannot determine media type for stream by index: " << |
||||||
|
stream_index); |
||||||
|
continue; |
||||||
|
} |
||||||
|
|
||||||
|
ComPtrGuard<IMFMediaType> media_type = createCOMPtrGuard(media_type_tmp); |
||||||
|
GUID subtype; |
||||||
|
if (SUCCEEDED(media_type->GetGUID(MF_MT_SUBTYPE, &subtype))) { |
||||||
|
GAPI_LOG_DEBUG(nullptr, "[" << source_id << "] " << |
||||||
|
"video type: " << GetGUIDNameConst(subtype)); |
||||||
|
|
||||||
|
std::string is_codec_supported("unsupported, skip..."); |
||||||
|
try { |
||||||
|
out_codec_id = convert_to_mfx_codec_id(subtype); |
||||||
|
is_stream_selected = true; |
||||||
|
is_codec_supported = "selected!"; |
||||||
|
} catch (...) {} |
||||||
|
|
||||||
|
GAPI_LOG_INFO(nullptr, "[" << source_id << "] " << |
||||||
|
"video stream index: " << stream_index << |
||||||
|
", codec: " << GetGUIDNameConst(subtype) << |
||||||
|
" - " << is_codec_supported) |
||||||
|
} else { |
||||||
|
GAPI_LOG_WARNING(nullptr, "[" << source_id << "] " << |
||||||
|
"Cannot get media GUID subtype for stream by index: " << |
||||||
|
stream_index); |
||||||
|
continue; |
||||||
|
} |
||||||
|
} |
||||||
|
return is_stream_selected; |
||||||
|
} |
||||||
|
|
||||||
|
MFPAsyncDemuxDataProvider::MFPAsyncDemuxDataProvider(const std::string& file_path, |
||||||
|
size_t keep_preprocessed_buf_count_value) : |
||||||
|
keep_preprocessed_buf_count(keep_preprocessed_buf_count_value), |
||||||
|
source(createCOMPtrGuard<IMFMediaSource>()), |
||||||
|
source_reader(createCOMPtrGuard<IMFSourceReader>()), |
||||||
|
codec(std::numeric_limits<uint32_t>::max()), |
||||||
|
provider_state(State::InProgress) { |
||||||
|
|
||||||
|
submit_read_request.clear(); |
||||||
|
com_interface_reference_count = 1; // object itself
|
||||||
|
|
||||||
|
HRESULT hr = S_OK; |
||||||
|
hr = MFStartup(MF_VERSION); |
||||||
|
if (FAILED(hr)) { |
||||||
|
throw DataProviderSystemErrorException(HRESULT_CODE(hr), "Cannot initialize MFStartup"); |
||||||
|
} |
||||||
|
|
||||||
|
GAPI_LOG_INFO(nullptr, "[" << this << "] " << |
||||||
|
" initializing, URI " << file_path); |
||||||
|
IMFMediaSource *source_tmp = nullptr; |
||||||
|
hr = create_media_source(file_path, &source_tmp); |
||||||
|
if (FAILED(hr)) { |
||||||
|
throw DataProviderSystemErrorException(HRESULT_CODE(hr), "Cannot create IMFMediaSource"); |
||||||
|
} |
||||||
|
source.reset(source_tmp); |
||||||
|
|
||||||
|
GAPI_LOG_DEBUG(nullptr, "[" << this << "] " << |
||||||
|
" start creating source attributes"); |
||||||
|
IMFAttributes *attrs_tmp = nullptr; |
||||||
|
|
||||||
|
// NB: create 2 attributes for disable converters & async callback capability
|
||||||
|
const UINT32 relevant_attributes_count = 2; |
||||||
|
hr = MFCreateAttributes(&attrs_tmp, relevant_attributes_count); |
||||||
|
if (FAILED(hr)) { |
||||||
|
throw DataProviderSystemErrorException(HRESULT_CODE(hr), "MFCreateAttributes failed"); |
||||||
|
} |
||||||
|
|
||||||
|
ComPtrGuard<IMFAttributes> attributes = createCOMPtrGuard(attrs_tmp); |
||||||
|
hr = attributes->SetUINT32(MF_READWRITE_DISABLE_CONVERTERS, TRUE); |
||||||
|
|
||||||
|
// set the callback pointer.
|
||||||
|
if (SUCCEEDED(hr)) |
||||||
|
{ |
||||||
|
hr = attributes->SetUnknown( |
||||||
|
MF_SOURCE_READER_ASYNC_CALLBACK, |
||||||
|
this |
||||||
|
); |
||||||
|
} |
||||||
|
if (FAILED(hr)) { |
||||||
|
throw DataProviderSystemErrorException(HRESULT_CODE(hr), "Cannot set MFP async callback "); |
||||||
|
} |
||||||
|
|
||||||
|
GAPI_LOG_DEBUG(nullptr, "[" << this << "] " << |
||||||
|
"is getting presentation descriptor"); |
||||||
|
IMFPresentationDescriptor* descriptor_tmp = nullptr; |
||||||
|
hr = source->CreatePresentationDescriptor(&descriptor_tmp); |
||||||
|
if (FAILED(hr)) { |
||||||
|
throw DataProviderSystemErrorException(HRESULT_CODE(hr), |
||||||
|
"CreatePresentationDescriptor failed"); |
||||||
|
} |
||||||
|
ComPtrGuard<IMFPresentationDescriptor> descriptor = createCOMPtrGuard(descriptor_tmp); |
||||||
|
if (!MFPAsyncDemuxDataProvider::select_supported_video_stream(descriptor, codec, this)) { |
||||||
|
// NB: let's pretty notify clients about list of supported codecs to keep
|
||||||
|
// contract in explicit way to avoid continuous troubleshooting
|
||||||
|
const auto &supported_codecs = get_supported_mfx_codec_ids(); |
||||||
|
std::string ss; |
||||||
|
for (mfxU32 id : supported_codecs) { |
||||||
|
ss += mfx_codec_id_to_cstr(id); |
||||||
|
ss += ", "; |
||||||
|
} |
||||||
|
if (!ss.empty()) { |
||||||
|
ss.erase(ss.size() - 2, 2); |
||||||
|
} |
||||||
|
|
||||||
|
GAPI_LOG_WARNING(nullptr, "[" << this << "] " |
||||||
|
"couldn't find video stream with supported params, " |
||||||
|
"expected codecs: " << ss); |
||||||
|
throw DataProviderUnsupportedException("couldn't find supported video stream"); |
||||||
|
} |
||||||
|
|
||||||
|
GAPI_LOG_DEBUG(nullptr, "[" << this << "] " << |
||||||
|
"is creating media source"); |
||||||
|
IMFSourceReader *source_reader_tmp = nullptr; |
||||||
|
hr = MFCreateSourceReaderFromMediaSource(source.get(), attributes.get(), |
||||||
|
&source_reader_tmp); |
||||||
|
if (FAILED(hr)) { |
||||||
|
throw DataProviderSystemErrorException(HRESULT_CODE(hr), |
||||||
|
"MFCreateSourceReaderFromMediaSource failed"); |
||||||
|
} |
||||||
|
source_reader = createCOMPtrGuard(source_reader_tmp); |
||||||
|
|
||||||
|
GAPI_LOG_DEBUG(nullptr, "[" << this << "] " << |
||||||
|
"created IMFSourceReader: " << source_reader); |
||||||
|
|
||||||
|
// Ask for the first sample.
|
||||||
|
hr = request_next(hr, 0, 0); |
||||||
|
if (FAILED(hr)) { |
||||||
|
throw DataProviderSystemErrorException(HRESULT_CODE(hr), |
||||||
|
"ReadSample failed while requesting initial sample"); |
||||||
|
} |
||||||
|
GAPI_LOG_INFO(nullptr, "[" << this << "] " << |
||||||
|
"initialized"); |
||||||
|
} |
||||||
|
|
||||||
|
MFPAsyncDemuxDataProvider::~MFPAsyncDemuxDataProvider() { |
||||||
|
GAPI_LOG_INFO(nullptr, "[" << this << "] " << |
||||||
|
"begin deinitializing"); |
||||||
|
|
||||||
|
flush(); |
||||||
|
|
||||||
|
{ |
||||||
|
std::unique_lock<std::mutex> l(buffer_storage_mutex); |
||||||
|
GAPI_LOG_INFO(nullptr, "Clean up async storage, count: " << |
||||||
|
worker_key_to_buffer_mapping_storage.size()); |
||||||
|
for (auto& buffer : worker_key_to_buffer_mapping_storage) { |
||||||
|
if (buffer.second) { |
||||||
|
buffer.second->Unlock(); |
||||||
|
} |
||||||
|
} |
||||||
|
worker_key_to_buffer_mapping_storage.clear(); |
||||||
|
} |
||||||
|
|
||||||
|
GAPI_LOG_INFO(nullptr, "Clean data storage, elapsed buffer count: " << |
||||||
|
processing_key_to_buffer_mapping_storage.size()); |
||||||
|
for (auto& buffer : processing_key_to_buffer_mapping_storage) { |
||||||
|
if (buffer.second) { |
||||||
|
buffer.second->Unlock(); |
||||||
|
} |
||||||
|
} |
||||||
|
processing_key_to_buffer_mapping_storage.clear(); |
||||||
|
|
||||||
|
// release COM object before overall MFP shutdown
|
||||||
|
source_reader.reset(); |
||||||
|
source.reset(); |
||||||
|
|
||||||
|
MFShutdown(); |
||||||
|
GAPI_LOG_INFO(nullptr, "[" << this << "] " << |
||||||
|
"deinitialized"); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
ULONG MFPAsyncDemuxDataProvider::AddRef() { |
||||||
|
// align behavior with InterlockedIncrement
|
||||||
|
return com_interface_reference_count.fetch_add(1) + 1; |
||||||
|
} |
||||||
|
|
||||||
|
ULONG MFPAsyncDemuxDataProvider::Release() { |
||||||
|
auto count = com_interface_reference_count.fetch_sub(1); |
||||||
|
GAPI_Assert(count != 0 && "Incorrect reference counting for MFPAsyncDemuxDataProvider"); |
||||||
|
count -= 1; // align behavior with InterlockedDecrement
|
||||||
|
return count; |
||||||
|
} |
||||||
|
|
||||||
|
HRESULT MFPAsyncDemuxDataProvider::QueryInterface(REFIID riid, void** ppv) |
||||||
|
{ |
||||||
|
static const QITAB qit[] = |
||||||
|
{ |
||||||
|
QITABENT(MFPAsyncDemuxDataProvider, IMFSourceReaderCallback), |
||||||
|
{ 0 }, |
||||||
|
}; |
||||||
|
return QISearch(this, qit, riid, ppv); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
STDMETHODIMP |
||||||
|
MFPAsyncDemuxDataProvider::OnReadSample(HRESULT status, DWORD, |
||||||
|
DWORD stream_flag, LONGLONG, |
||||||
|
IMFSample *sample_ptr) { |
||||||
|
GAPI_LOG_DEBUG(nullptr, "[" << this << "] status: " << std::to_string(HRESULT_CODE(status)) << |
||||||
|
", stream flags: " << stream_flag << |
||||||
|
", sample: " << sample_ptr); |
||||||
|
HRESULT hr = S_OK; |
||||||
|
if (FAILED(status)) { |
||||||
|
hr = status; |
||||||
|
} |
||||||
|
|
||||||
|
// check EOF
|
||||||
|
if (stream_flag & MF_SOURCE_READERF_ENDOFSTREAM) { |
||||||
|
GAPI_LOG_DEBUG(nullptr, "[" << this << "] EOF"); |
||||||
|
|
||||||
|
// close reader
|
||||||
|
provider_state.store(State::Exhausted); |
||||||
|
buffer_storage_non_empty_cond.notify_all(); |
||||||
|
return hr; |
||||||
|
} |
||||||
|
|
||||||
|
submit_read_request.clear(); |
||||||
|
|
||||||
|
// extract stream data
|
||||||
|
size_t worker_buffer_count = 0; |
||||||
|
if (SUCCEEDED(hr)) { |
||||||
|
if (sample_ptr) { |
||||||
|
// Get the video frame buffer from the sample.
|
||||||
|
IMFMediaBuffer *buffer_ptr = nullptr; |
||||||
|
hr = sample_ptr->ConvertToContiguousBuffer(&buffer_ptr); |
||||||
|
GAPI_Assert(SUCCEEDED(hr) && |
||||||
|
"MFPAsyncDemuxDataProvider::OnReadSample - ConvertToContiguousBuffer failed"); |
||||||
|
|
||||||
|
DWORD max_buffer_size = 0; |
||||||
|
DWORD curr_size = 0; |
||||||
|
|
||||||
|
// lock buffer directly into mfx bitstream
|
||||||
|
std::shared_ptr<mfx_bitstream> staging_stream = std::make_shared<mfx_bitstream>(); |
||||||
|
staging_stream->Data = nullptr; |
||||||
|
|
||||||
|
hr = buffer_ptr->Lock(&staging_stream->Data, &max_buffer_size, &curr_size); |
||||||
|
GAPI_Assert(SUCCEEDED(hr) && |
||||||
|
"MFPAsyncDemuxDataProvider::OnReadSample - Lock failed"); |
||||||
|
|
||||||
|
staging_stream->MaxLength = max_buffer_size; |
||||||
|
staging_stream->DataLength = curr_size; |
||||||
|
staging_stream->CodecId = get_mfx_codec_id(); |
||||||
|
|
||||||
|
GAPI_LOG_DEBUG(nullptr, "[" << this << "] bitstream created, data: " << |
||||||
|
static_cast<void*>(staging_stream->Data) << |
||||||
|
", MaxLength: " << staging_stream->MaxLength << |
||||||
|
", DataLength: " << staging_stream->DataLength); |
||||||
|
|
||||||
|
worker_buffer_count = produce_worker_data(staging_stream->Data, |
||||||
|
createCOMPtrGuard(buffer_ptr), |
||||||
|
std::move(staging_stream)); |
||||||
|
} |
||||||
|
} else { |
||||||
|
GAPI_LOG_WARNING(nullptr, "[" << this << "] callback failed" |
||||||
|
", status: " << std::to_string(HRESULT_CODE(status)) << |
||||||
|
", stream flags: " << stream_flag << |
||||||
|
", sample: " << sample_ptr); |
||||||
|
} |
||||||
|
|
||||||
|
hr = request_next(hr, stream_flag, worker_buffer_count); |
||||||
|
return hr; |
||||||
|
} |
||||||
|
|
||||||
|
size_t MFPAsyncDemuxDataProvider::get_locked_buffer_size() const { |
||||||
|
std::unique_lock<std::mutex> l(buffer_storage_mutex); |
||||||
|
return worker_locked_buffer_storage.size(); |
||||||
|
} |
||||||
|
|
||||||
|
STDMETHODIMP MFPAsyncDemuxDataProvider::OnEvent(DWORD, IMFMediaEvent *) { |
||||||
|
return S_OK; |
||||||
|
} |
||||||
|
|
||||||
|
STDMETHODIMP MFPAsyncDemuxDataProvider::OnFlush(DWORD) { |
||||||
|
provider_state.store(State::Exhausted); |
||||||
|
buffer_storage_non_empty_cond.notify_all(); |
||||||
|
return S_OK; |
||||||
|
} |
||||||
|
|
||||||
|
void MFPAsyncDemuxDataProvider::flush() { |
||||||
|
if(source_reader) { |
||||||
|
GAPI_LOG_INFO(nullptr, "[" << this << "] set flush"); |
||||||
|
source_reader->Flush(static_cast<DWORD>(MF_SOURCE_READER_ALL_STREAMS)); |
||||||
|
} |
||||||
|
|
||||||
|
size_t iterations = 0; |
||||||
|
const int waiting_ms = 100; |
||||||
|
const size_t warning_iteration_wait_count = 300; // approx 30 sec
|
||||||
|
while (provider_state.load() != State::Exhausted) { |
||||||
|
iterations++; |
||||||
|
if (iterations > warning_iteration_wait_count) { |
||||||
|
GAPI_LOG_WARNING(nullptr, "[" << this << "] is still waiting for flush finishing, " |
||||||
|
"iteration: " << iterations); |
||||||
|
} else { |
||||||
|
GAPI_LOG_DEBUG(nullptr, "[" << this << "] is waiting for flush finishing, " |
||||||
|
"iteration: " << iterations); |
||||||
|
} |
||||||
|
std::unique_lock<std::mutex> l(buffer_storage_mutex); |
||||||
|
buffer_storage_non_empty_cond.wait_for(l, std::chrono::milliseconds(waiting_ms)); |
||||||
|
} |
||||||
|
|
||||||
|
GAPI_LOG_INFO(nullptr, "[" << this << "] has flushed in: " << |
||||||
|
iterations * waiting_ms << "ms interval"); |
||||||
|
} |
||||||
|
|
||||||
|
HRESULT MFPAsyncDemuxDataProvider::request_next(HRESULT hr, |
||||||
|
DWORD stream_flag, |
||||||
|
size_t worker_buffer_count) { |
||||||
|
GAPI_LOG_DEBUG(nullptr, "[" << this << "] status: " << |
||||||
|
std::to_string(HRESULT_CODE(hr)) << |
||||||
|
", stream flags: " << stream_flag << |
||||||
|
", worker buffer count: (" << worker_buffer_count << |
||||||
|
"/" << keep_preprocessed_buf_count << ")"); |
||||||
|
// check gap in stream
|
||||||
|
if (stream_flag & MF_SOURCE_READERF_STREAMTICK ) { |
||||||
|
GAPI_LOG_INFO(nullptr, "[" << this << "] stream gap detected"); |
||||||
|
return hr; |
||||||
|
} |
||||||
|
|
||||||
|
if (FAILED(hr)) { |
||||||
|
GAPI_LOG_WARNING(nullptr, "[" << this << "] callback error " |
||||||
|
", status: " << std::to_string(HRESULT_CODE(hr)) << |
||||||
|
", stream flags: " << stream_flag); |
||||||
|
} |
||||||
|
|
||||||
|
// put on worker buffers available ready
|
||||||
|
if (worker_buffer_count < keep_preprocessed_buf_count) { |
||||||
|
// only one consumer might make submit
|
||||||
|
if (!submit_read_request.test_and_set()) { |
||||||
|
hr = source_reader->ReadSample((DWORD)MF_SOURCE_READER_FIRST_VIDEO_STREAM, |
||||||
|
0, NULL, NULL, NULL, NULL); |
||||||
|
GAPI_LOG_DEBUG(nullptr, "[" << this << "] submit read sample, status: " << |
||||||
|
std::to_string(HRESULT_CODE(hr))); |
||||||
|
} |
||||||
|
} |
||||||
|
return hr; |
||||||
|
} |
||||||
|
|
||||||
|
void MFPAsyncDemuxDataProvider::consume_worker_data() { |
||||||
|
// wait callback exchange
|
||||||
|
std::unique_lock<std::mutex> l(buffer_storage_mutex); |
||||||
|
buffer_storage_non_empty_cond.wait(l, [this] { |
||||||
|
bool empty = worker_locked_buffer_storage.empty(); |
||||||
|
if (empty) { |
||||||
|
if (!submit_read_request.test_and_set()) { |
||||||
|
(void)source_reader->ReadSample((DWORD)MF_SOURCE_READER_FIRST_VIDEO_STREAM, |
||||||
|
0, NULL, NULL, NULL, NULL); |
||||||
|
} |
||||||
|
} else { |
||||||
|
worker_key_to_buffer_mapping_storage.swap(processing_key_to_buffer_mapping_storage); |
||||||
|
worker_locked_buffer_storage.swap(processing_locked_buffer_storage); |
||||||
|
} |
||||||
|
|
||||||
|
return !empty || provider_state == State::Exhausted; |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
size_t MFPAsyncDemuxDataProvider::produce_worker_data(void *key, |
||||||
|
ComPtrGuard<IMFMediaBuffer> &&buffer, |
||||||
|
std::shared_ptr<mfx_bitstream> &&staging_stream) { |
||||||
|
size_t bitstream_count = 0; |
||||||
|
size_t worker_buffer_count = 0; |
||||||
|
{ |
||||||
|
std::unique_lock<std::mutex> l(buffer_storage_mutex); |
||||||
|
|
||||||
|
// remember sample buffer to keep data safe
|
||||||
|
worker_key_to_buffer_mapping_storage.emplace(key, std::move(buffer)); |
||||||
|
worker_buffer_count = worker_key_to_buffer_mapping_storage.size(); |
||||||
|
|
||||||
|
// remember bitstream for consuming
|
||||||
|
worker_locked_buffer_storage.push(std::move(staging_stream)); |
||||||
|
bitstream_count = worker_locked_buffer_storage.size(); |
||||||
|
buffer_storage_non_empty_cond.notify_all(); |
||||||
|
} |
||||||
|
GAPI_DbgAssert(worker_buffer_count == bitstream_count && |
||||||
|
"worker_key_to_buffer_mapping_storage & worker_locked_buffer_storage" |
||||||
|
" must be the same size" ); |
||||||
|
GAPI_LOG_DEBUG(nullptr, "[" << this << "] created dmux buffer by key: " << |
||||||
|
key << ", ready bitstream count: " << |
||||||
|
bitstream_count); |
||||||
|
|
||||||
|
return worker_buffer_count; |
||||||
|
} |
||||||
|
|
||||||
|
/////////////// IDataProvider methods ///////////////
|
||||||
|
IDataProvider::mfx_codec_id_type MFPAsyncDemuxDataProvider::get_mfx_codec_id() const { |
||||||
|
return codec; |
||||||
|
} |
||||||
|
|
||||||
|
bool MFPAsyncDemuxDataProvider::fetch_bitstream_data(std::shared_ptr<mfx_bitstream> &out_bitsream) { |
||||||
|
if (empty()) { |
||||||
|
GAPI_LOG_DEBUG(nullptr, "[" << this << "] empty"); |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
// utilize consumed bitstream portion allocated at prev step
|
||||||
|
if (out_bitsream) { |
||||||
|
// make dmux buffer unlock for not empty bitstream
|
||||||
|
GAPI_LOG_DEBUG(nullptr, "[" << this << "] " << |
||||||
|
"bitstream before fetch: " << |
||||||
|
out_bitsream.get() << |
||||||
|
", DataOffset: " << |
||||||
|
out_bitsream->DataOffset << |
||||||
|
", DataLength: " << |
||||||
|
out_bitsream->DataLength); |
||||||
|
if (out_bitsream->DataOffset < out_bitsream->DataLength) { |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
// cleanup
|
||||||
|
auto it = processing_key_to_buffer_mapping_storage.find(out_bitsream->Data); |
||||||
|
if (it == processing_key_to_buffer_mapping_storage.end()) { |
||||||
|
GAPI_LOG_WARNING(nullptr, "[" << this << "] " << |
||||||
|
"cannot find appropriate dmux buffer by key: " << |
||||||
|
static_cast<void*>(out_bitsream->Data)); |
||||||
|
GAPI_Assert(false && "invalid bitstream key"); |
||||||
|
} |
||||||
|
if (it->second) { |
||||||
|
it->second->Unlock(); |
||||||
|
} |
||||||
|
processing_key_to_buffer_mapping_storage.erase(it); |
||||||
|
} |
||||||
|
|
||||||
|
// consume new bitstream portion
|
||||||
|
if (processing_locked_buffer_storage.empty() && |
||||||
|
provider_state.load() == State::InProgress) { |
||||||
|
// get worker data collected from another thread
|
||||||
|
consume_worker_data(); |
||||||
|
} |
||||||
|
|
||||||
|
// EOF check: nothing to process at this point
|
||||||
|
if (processing_locked_buffer_storage.empty()) { |
||||||
|
GAPI_DbgAssert(provider_state == State::Exhausted && "Source reader must be drained"); |
||||||
|
out_bitsream.reset(); |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
out_bitsream = processing_locked_buffer_storage.front(); |
||||||
|
processing_locked_buffer_storage.pop(); |
||||||
|
|
||||||
|
GAPI_LOG_DEBUG(nullptr, "[" << this << "] " |
||||||
|
"bitstream after fetch: " << |
||||||
|
out_bitsream.get() << |
||||||
|
", DataOffset: " << |
||||||
|
out_bitsream->DataOffset << |
||||||
|
", DataLength: " << |
||||||
|
out_bitsream->DataLength); |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
bool MFPAsyncDemuxDataProvider::empty() const { |
||||||
|
return (provider_state.load() == State::Exhausted) && |
||||||
|
(processing_locked_buffer_storage.size() == 0) && |
||||||
|
(get_locked_buffer_size() == 0); |
||||||
|
} |
||||||
|
#else // _WIN32
|
||||||
|
|
||||||
|
MFPAsyncDemuxDataProvider::MFPAsyncDemuxDataProvider(const std::string&) { |
||||||
|
GAPI_Assert(false && "Unsupported: Microsoft Media Foundation is not available"); |
||||||
|
} |
||||||
|
IDataProvider::mfx_codec_id_type MFPAsyncDemuxDataProvider::get_mfx_codec_id() const { |
||||||
|
GAPI_Assert(false && "Unsupported: Microsoft Media Foundation is not available"); |
||||||
|
return std::numeric_limits<mfx_codec_id_type>::max(); |
||||||
|
} |
||||||
|
|
||||||
|
bool MFPAsyncDemuxDataProvider::fetch_bitstream_data(std::shared_ptr<mfx_bitstream> &) { |
||||||
|
GAPI_Assert(false && "Unsupported: Microsoft Media Foundation is not available"); |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
bool MFPAsyncDemuxDataProvider::empty() const override { |
||||||
|
GAPI_Assert(false && "Unsupported: Microsoft Media Foundation is not available"); |
||||||
|
return true; |
||||||
|
} |
||||||
|
#endif // _WIN32
|
||||||
|
} // namespace onevpl
|
||||||
|
} // namespace wip
|
||||||
|
} // namespace gapi
|
||||||
|
} // namespace cv
|
||||||
|
|
||||||
|
#endif // HAVE_ONEVPL
|
@ -0,0 +1,126 @@ |
|||||||
|
// 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_DEMUX_ASYNC_MFP_DEMUX_DATA_PROVIDER_HPP |
||||||
|
#define GAPI_STREAMING_ONEVPL_DEMUX_ASYNC_MFP_DEMUX_DATA_PROVIDER_HPP |
||||||
|
|
||||||
|
#include <atomic> |
||||||
|
#include <condition_variable> |
||||||
|
#include <mutex> |
||||||
|
#include <queue> |
||||||
|
|
||||||
|
#ifdef HAVE_ONEVPL |
||||||
|
#include <vpl/mfxvideo.h> |
||||||
|
|
||||||
|
#ifdef _WIN32 |
||||||
|
#define NOMINMAX |
||||||
|
#include <mfapi.h> |
||||||
|
#include <mfidl.h> |
||||||
|
#include <mfreadwrite.h> |
||||||
|
#include <mfobjects.h> |
||||||
|
#include <mftransform.h> |
||||||
|
#include <mferror.h> |
||||||
|
#include <shlwapi.h> |
||||||
|
#include <wmcontainer.h> |
||||||
|
#include <wmcodecdsp.h> |
||||||
|
#undef NOMINMAX |
||||||
|
|
||||||
|
#include <opencv2/gapi/streaming/onevpl/data_provider_interface.hpp> |
||||||
|
#include "streaming/onevpl/data_provider_defines.hpp" |
||||||
|
#include "streaming/onevpl/utils.hpp" |
||||||
|
|
||||||
|
namespace cv { |
||||||
|
namespace gapi { |
||||||
|
namespace wip { |
||||||
|
namespace onevpl { |
||||||
|
struct GAPI_EXPORTS MFPAsyncDemuxDataProvider : public IDataProvider, |
||||||
|
public IMFSourceReaderCallback { |
||||||
|
MFPAsyncDemuxDataProvider(const std::string& file_path, |
||||||
|
size_t keep_preprocessed_buf_count_value = 3); |
||||||
|
~MFPAsyncDemuxDataProvider(); |
||||||
|
|
||||||
|
mfx_codec_id_type get_mfx_codec_id() const override; |
||||||
|
bool fetch_bitstream_data(std::shared_ptr<mfx_bitstream> &out_bitsream) override; |
||||||
|
bool empty() const override; |
||||||
|
|
||||||
|
protected: /* For Unit tests only */ |
||||||
|
enum class State { |
||||||
|
InProgress, |
||||||
|
Exhausted |
||||||
|
}; |
||||||
|
|
||||||
|
// IUnknown methods forbidden for current implementations
|
||||||
|
STDMETHODIMP QueryInterface(REFIID iid, void** ppv) override; |
||||||
|
STDMETHODIMP_(ULONG) AddRef() override; |
||||||
|
STDMETHODIMP_(ULONG) Release() override; |
||||||
|
|
||||||
|
// IMFSourceReaderCallback methods
|
||||||
|
virtual STDMETHODIMP OnReadSample(HRESULT status, DWORD stream_index, |
||||||
|
DWORD stream_flag, LONGLONG timestamp, |
||||||
|
IMFSample *sample_ptr) override; |
||||||
|
STDMETHODIMP OnEvent(DWORD, IMFMediaEvent *) override; |
||||||
|
STDMETHODIMP OnFlush(DWORD) override; |
||||||
|
|
||||||
|
// implementation methods
|
||||||
|
void flush(); |
||||||
|
HRESULT request_next(HRESULT hr, DWORD stream_flag, |
||||||
|
size_t worker_buffer_count); |
||||||
|
void consume_worker_data(); |
||||||
|
virtual size_t produce_worker_data(void *key, |
||||||
|
ComPtrGuard<IMFMediaBuffer> &&buffer, |
||||||
|
std::shared_ptr<mfx_bitstream> &&staging_stream); |
||||||
|
size_t get_locked_buffer_size() const; |
||||||
|
|
||||||
|
private: |
||||||
|
static bool select_supported_video_stream(ComPtrGuard<IMFPresentationDescriptor> &descriptor, |
||||||
|
mfx_codec_id_type &out_codec_id, |
||||||
|
void *source_id); |
||||||
|
// members
|
||||||
|
size_t keep_preprocessed_buf_count; |
||||||
|
|
||||||
|
// COM members
|
||||||
|
ComPtrGuard<IMFMediaSource> source; |
||||||
|
ComPtrGuard<IMFSourceReader> source_reader; |
||||||
|
std::atomic<ULONG> com_interface_reference_count; |
||||||
|
|
||||||
|
mfx_codec_id_type codec; |
||||||
|
|
||||||
|
// worker & processing buffers
|
||||||
|
std::map<void*, ComPtrGuard<IMFMediaBuffer>> worker_key_to_buffer_mapping_storage; |
||||||
|
std::map<void*, ComPtrGuard<IMFMediaBuffer>> processing_key_to_buffer_mapping_storage; |
||||||
|
std::queue<std::shared_ptr<mfx_bitstream>> worker_locked_buffer_storage; |
||||||
|
std::queue<std::shared_ptr<mfx_bitstream>> processing_locked_buffer_storage; |
||||||
|
std::condition_variable buffer_storage_non_empty_cond; |
||||||
|
mutable std::mutex buffer_storage_mutex; |
||||||
|
|
||||||
|
std::atomic_flag submit_read_request; |
||||||
|
std::atomic<State> provider_state; |
||||||
|
}; |
||||||
|
} // namespace onevpl
|
||||||
|
} // namespace wip
|
||||||
|
} // namespace gapi
|
||||||
|
} // namespace cv
|
||||||
|
|
||||||
|
#else // _WIN32
|
||||||
|
namespace cv { |
||||||
|
namespace gapi { |
||||||
|
namespace wip { |
||||||
|
namespace onevpl { |
||||||
|
struct GAPI_EXPORTS MFPAsyncDemuxDataProvider : public IDataProvider { |
||||||
|
explicit MFPAsyncDemuxDataProvider(const std::string&); |
||||||
|
|
||||||
|
mfx_codec_id_type get_mfx_codec_id() const override; |
||||||
|
bool fetch_bitstream_data(std::shared_ptr<mfx_bitstream> &out_bitsream) override; |
||||||
|
bool empty() const override; |
||||||
|
}; |
||||||
|
} // namespace onevpl
|
||||||
|
} // namespace wip
|
||||||
|
} // namespace gapi
|
||||||
|
} // namespace cv
|
||||||
|
|
||||||
|
#endif // _WIN32
|
||||||
|
#endif // HAVE_ONEVPL
|
||||||
|
#endif // GAPI_STREAMING_ONEVPL_DEMUX_ASYNC_MFP_DEMUX_DATA_PROVIDER_HPP
|
@ -0,0 +1,304 @@ |
|||||||
|
// 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 <future> |
||||||
|
|
||||||
|
#include "../test_precomp.hpp" |
||||||
|
|
||||||
|
#include "../common/gapi_tests_common.hpp" |
||||||
|
#include "streaming/onevpl/data_provider_dispatcher.hpp" |
||||||
|
#include "streaming/onevpl/file_data_provider.hpp" |
||||||
|
#include "streaming/onevpl/demux/async_mfp_demux_data_provider.hpp" |
||||||
|
#include "streaming/onevpl/source_priv.hpp" |
||||||
|
|
||||||
|
namespace opencv_test |
||||||
|
{ |
||||||
|
namespace |
||||||
|
{ |
||||||
|
using source_t = std::string; |
||||||
|
using dd_valid_t = bool; |
||||||
|
using demux_valid_t = bool; |
||||||
|
using dec_valid_t = bool; |
||||||
|
using array_element_t = |
||||||
|
std::tuple<source_t, dd_valid_t, demux_valid_t, dec_valid_t>; |
||||||
|
array_element_t files[] = { |
||||||
|
array_element_t {"highgui/video/VID00003-20100701-2204.3GP", |
||||||
|
false, true, false}, |
||||||
|
array_element_t {"highgui/video/VID00003-20100701-2204.avi", |
||||||
|
false, true, false}, |
||||||
|
array_element_t {"highgui/video/VID00003-20100701-2204.mpg", |
||||||
|
false, true, false}, |
||||||
|
array_element_t {"highgui/video/VID00003-20100701-2204.wmv", |
||||||
|
false, true, false}, |
||||||
|
array_element_t {"highgui/video/sample_322x242_15frames.yuv420p.libaom-av1.mp4", |
||||||
|
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", |
||||||
|
true, true, true}, |
||||||
|
array_element_t {"highgui/video/sample_322x242_15frames.yuv420p.mjpeg.mp4", |
||||||
|
/* MFP cannot extract video MJPEG subtype from that */ |
||||||
|
false, false, true}, |
||||||
|
array_element_t {"highgui/video/big_buck_bunny.h264", |
||||||
|
false, false, false}, |
||||||
|
array_element_t {"highgui/video/big_buck_bunny.h265", |
||||||
|
false, false, false} |
||||||
|
}; |
||||||
|
|
||||||
|
class OneVPL_Source_MFPAsyncDispatcherTest : public ::testing::TestWithParam<array_element_t> {}; |
||||||
|
TEST_P(OneVPL_Source_MFPAsyncDispatcherTest, open_and_decode_file) |
||||||
|
{ |
||||||
|
using namespace cv::gapi::wip::onevpl; |
||||||
|
|
||||||
|
source_t path = findDataFile(std::get<0>(GetParam())); |
||||||
|
dd_valid_t dd_result = std::get<1>(GetParam()); |
||||||
|
dec_valid_t dec_result = std::get<3>(GetParam()); |
||||||
|
|
||||||
|
// open demux source & check format support
|
||||||
|
std::unique_ptr<MFPAsyncDemuxDataProvider> provider_ptr; |
||||||
|
try { |
||||||
|
provider_ptr.reset(new MFPAsyncDemuxDataProvider(path)); |
||||||
|
} catch (...) { |
||||||
|
EXPECT_FALSE(dd_result); |
||||||
|
GTEST_SUCCEED(); |
||||||
|
return; |
||||||
|
} |
||||||
|
EXPECT_TRUE(dd_result); |
||||||
|
|
||||||
|
// initialize MFX
|
||||||
|
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 = provider_ptr->get_mfx_codec_id(); |
||||||
|
EXPECT_EQ(MFXSetConfigFilterProperty(cfg_inst_0,(mfxU8 *)"mfxImplDescription.mfxDecoderDescription.decoder.CodecID", |
||||||
|
mfx_param_0), MFX_ERR_NONE); |
||||||
|
|
||||||
|
// create MFX session
|
||||||
|
mfxSession mfx_session{}; |
||||||
|
mfxStatus sts = MFXCreateSession(mfx_handle, 0, &mfx_session); |
||||||
|
EXPECT_EQ(MFX_ERR_NONE, sts); |
||||||
|
|
||||||
|
// create proper bitstream
|
||||||
|
std::shared_ptr<IDataProvider::mfx_bitstream> bitstream{}; |
||||||
|
|
||||||
|
// prepare dec params
|
||||||
|
mfxVideoParam mfxDecParams {}; |
||||||
|
mfxDecParams.mfx.CodecId = mfx_param_0.Data.U32; |
||||||
|
mfxDecParams.IOPattern = MFX_IOPATTERN_OUT_SYSTEM_MEMORY; |
||||||
|
do { |
||||||
|
bool fetched = provider_ptr->fetch_bitstream_data(bitstream); |
||||||
|
if (dec_result) { |
||||||
|
EXPECT_TRUE(fetched); |
||||||
|
} |
||||||
|
sts = MFXVideoDECODE_DecodeHeader(mfx_session, bitstream.get(), &mfxDecParams); |
||||||
|
EXPECT_TRUE(MFX_ERR_NONE == sts || MFX_ERR_MORE_DATA == sts); |
||||||
|
} while (sts == MFX_ERR_MORE_DATA && !provider_ptr->empty()); |
||||||
|
|
||||||
|
if (dec_result) { |
||||||
|
EXPECT_EQ(MFX_ERR_NONE, sts); |
||||||
|
} else { |
||||||
|
EXPECT_FALSE(MFX_ERR_NONE == sts); |
||||||
|
} |
||||||
|
|
||||||
|
MFXVideoDECODE_Close(mfx_session); |
||||||
|
MFXClose(mfx_session); |
||||||
|
MFXUnload(mfx_handle); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
TEST_P(OneVPL_Source_MFPAsyncDispatcherTest, choose_dmux_provider) |
||||||
|
{ |
||||||
|
using namespace cv::gapi::wip::onevpl; |
||||||
|
|
||||||
|
|
||||||
|
source_t path = findDataFile(std::get<0>(GetParam())); |
||||||
|
dd_valid_t dd_result = std::get<1>(GetParam()); |
||||||
|
|
||||||
|
std::shared_ptr<IDataProvider> provider_ptr; |
||||||
|
|
||||||
|
// choose demux provider for empty CfgParams
|
||||||
|
try { |
||||||
|
provider_ptr = DataProviderDispatcher::create(path); |
||||||
|
} catch (...) { |
||||||
|
EXPECT_FALSE(dd_result); |
||||||
|
provider_ptr = DataProviderDispatcher::create(path, |
||||||
|
{ CfgParam::create<std::string>( |
||||||
|
"mfxImplDescription.mfxDecoderDescription.decoder.CodecID", |
||||||
|
"MFX_CODEC_HEVC") /* Doesn't matter what codec for RAW here*/}); |
||||||
|
EXPECT_TRUE(std::dynamic_pointer_cast<FileDataProvider>(provider_ptr)); |
||||||
|
GTEST_SUCCEED(); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
EXPECT_TRUE(dd_result); |
||||||
|
EXPECT_TRUE(std::dynamic_pointer_cast<MFPAsyncDemuxDataProvider>(provider_ptr)); |
||||||
|
} |
||||||
|
|
||||||
|
INSTANTIATE_TEST_CASE_P(MFP_VPL_DecodeHeaderTests, OneVPL_Source_MFPAsyncDispatcherTest, |
||||||
|
testing::ValuesIn(files)); |
||||||
|
|
||||||
|
namespace test { |
||||||
|
struct IntrusiveAsyncDemuxDataProvider : |
||||||
|
public cv::gapi::wip::onevpl::MFPAsyncDemuxDataProvider { |
||||||
|
|
||||||
|
using base_t = cv::gapi::wip::onevpl::MFPAsyncDemuxDataProvider; |
||||||
|
using base_t::base_t; |
||||||
|
|
||||||
|
~IntrusiveAsyncDemuxDataProvider() { |
||||||
|
destroyed = true; |
||||||
|
} |
||||||
|
|
||||||
|
STDMETHODIMP OnReadSample(HRESULT status, DWORD stream_index, |
||||||
|
DWORD stream_flag, LONGLONG timestamp, |
||||||
|
IMFSample *sample_ptr) override { |
||||||
|
if (IntrusiveAsyncDemuxDataProvider::need_request_next) { |
||||||
|
return base_t::OnReadSample(status, stream_index, stream_flag, |
||||||
|
timestamp, sample_ptr); |
||||||
|
} |
||||||
|
return status; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
// implementation methods
|
||||||
|
size_t produce_worker_data(void *key, |
||||||
|
cv::gapi::wip::onevpl::ComPtrGuard<IMFMediaBuffer> &&buffer, |
||||||
|
std::shared_ptr<mfx_bitstream> &&staging_stream) override { |
||||||
|
return base_t::produce_worker_data(key, std::move(buffer), |
||||||
|
std::move(staging_stream)); |
||||||
|
} |
||||||
|
|
||||||
|
static bool need_request_next; |
||||||
|
static bool destroyed; |
||||||
|
}; |
||||||
|
|
||||||
|
bool IntrusiveAsyncDemuxDataProvider::need_request_next{}; |
||||||
|
bool IntrusiveAsyncDemuxDataProvider::destroyed{}; |
||||||
|
} // namespace test
|
||||||
|
|
||||||
|
TEST(OneVPL_Source_MFPAsyncDemux, sync_flush) { |
||||||
|
using namespace cv::gapi::wip::onevpl; |
||||||
|
|
||||||
|
source_t path = findDataFile("highgui/video/sample_322x242_15frames.yuv420p.libx265.mp4"); |
||||||
|
test::IntrusiveAsyncDemuxDataProvider::need_request_next = false; |
||||||
|
const size_t preprocessed_samples_count = 3; |
||||||
|
{ |
||||||
|
test::IntrusiveAsyncDemuxDataProvider provider(path, preprocessed_samples_count); |
||||||
|
size_t produce_buffer_count = 199 * preprocessed_samples_count; |
||||||
|
std::thread producer([&provider, produce_buffer_count]() { |
||||||
|
size_t total_produced_count = 0; |
||||||
|
for (size_t i = 0; i < produce_buffer_count; i ++) { |
||||||
|
total_produced_count += provider.produce_worker_data( |
||||||
|
reinterpret_cast<void*>(i), |
||||||
|
createCOMPtrGuard<IMFMediaBuffer>(nullptr), |
||||||
|
{}); |
||||||
|
} |
||||||
|
}); |
||||||
|
producer.join(); |
||||||
|
} |
||||||
|
|
||||||
|
EXPECT_EQ(test::IntrusiveAsyncDemuxDataProvider::destroyed, true); |
||||||
|
} |
||||||
|
|
||||||
|
TEST(OneVPL_Source_MFPAsyncDemux, async_flush) { |
||||||
|
using namespace cv::gapi::wip::onevpl; |
||||||
|
|
||||||
|
source_t path = findDataFile("highgui/video/sample_322x242_15frames.yuv420p.libx265.mp4"); |
||||||
|
test::IntrusiveAsyncDemuxDataProvider::need_request_next = true; |
||||||
|
const size_t preprocessed_samples_count = 999; |
||||||
|
{ |
||||||
|
std::shared_ptr<IDataProvider::mfx_bitstream> stream; |
||||||
|
test::IntrusiveAsyncDemuxDataProvider provider(path, preprocessed_samples_count); |
||||||
|
EXPECT_TRUE(provider.fetch_bitstream_data(stream)); |
||||||
|
EXPECT_TRUE(stream); |
||||||
|
} |
||||||
|
|
||||||
|
EXPECT_EQ(test::IntrusiveAsyncDemuxDataProvider::destroyed, true); |
||||||
|
} |
||||||
|
|
||||||
|
TEST(OneVPL_Source_MFPAsyncDemux, eof_async_detection) { |
||||||
|
using namespace cv::gapi::wip::onevpl; |
||||||
|
|
||||||
|
source_t path = findDataFile("highgui/video/sample_322x242_15frames.yuv420p.libx265.mp4"); |
||||||
|
test::IntrusiveAsyncDemuxDataProvider::need_request_next = false; |
||||||
|
const size_t preprocessed_samples_count = 0; // do not ask sample at start
|
||||||
|
test::IntrusiveAsyncDemuxDataProvider provider(path, preprocessed_samples_count); |
||||||
|
std::promise<void> start_consume_data; |
||||||
|
std::future<void> wait_consume_data = start_consume_data.get_future(); |
||||||
|
|
||||||
|
std::thread fetcher([&provider, &start_consume_data]() { |
||||||
|
std::shared_ptr<IDataProvider::mfx_bitstream> stream; |
||||||
|
start_consume_data.set_value(); |
||||||
|
EXPECT_FALSE(provider.fetch_bitstream_data(stream)); |
||||||
|
EXPECT_FALSE(stream); |
||||||
|
}); |
||||||
|
|
||||||
|
wait_consume_data.wait(); |
||||||
|
std::this_thread::sleep_for(std::chrono::seconds(2)); // hope fetched has slept on condition
|
||||||
|
|
||||||
|
test::IntrusiveAsyncDemuxDataProvider::need_request_next = true; |
||||||
|
provider.OnReadSample(S_OK, 0, MF_SOURCE_READERF_ENDOFSTREAM, 0, nullptr); |
||||||
|
fetcher.join(); |
||||||
|
} |
||||||
|
|
||||||
|
TEST(OneVPL_Source_MFPAsyncDemux, produce_consume) { |
||||||
|
using namespace cv::gapi::wip::onevpl; |
||||||
|
|
||||||
|
source_t path = findDataFile("highgui/video/sample_322x242_15frames.yuv420p.libx265.mp4"); |
||||||
|
test::IntrusiveAsyncDemuxDataProvider::need_request_next = false; |
||||||
|
const size_t preprocessed_samples_count = 3; |
||||||
|
test::IntrusiveAsyncDemuxDataProvider provider(path, preprocessed_samples_count); |
||||||
|
|
||||||
|
std::promise<void> start_consume_data; |
||||||
|
std::future<void> wait_consume_data = start_consume_data.get_future(); |
||||||
|
size_t produce_buffer_count = 199 * preprocessed_samples_count; |
||||||
|
std::thread producer([&provider, &wait_consume_data, produce_buffer_count]() { |
||||||
|
wait_consume_data.wait(); |
||||||
|
size_t total_produced_count = 0; |
||||||
|
for (size_t i = 0; i < produce_buffer_count; i ++) { |
||||||
|
std::shared_ptr<IDataProvider::mfx_bitstream> dummy_stream = |
||||||
|
std::make_shared<IDataProvider::mfx_bitstream>(); |
||||||
|
dummy_stream->DataLength = static_cast<mfxU32>(i); // control block
|
||||||
|
dummy_stream->DataOffset = static_cast<mfxU32>(i); // control block
|
||||||
|
dummy_stream->Data = reinterpret_cast<mfxU8*>(i); |
||||||
|
total_produced_count = provider.produce_worker_data( |
||||||
|
dummy_stream->Data, |
||||||
|
createCOMPtrGuard<IMFMediaBuffer>(nullptr), |
||||||
|
std::move(dummy_stream)); |
||||||
|
EXPECT_TRUE(total_produced_count <= produce_buffer_count); |
||||||
|
} |
||||||
|
}); |
||||||
|
|
||||||
|
std::thread consumer([&provider, &start_consume_data, produce_buffer_count]() { |
||||||
|
|
||||||
|
start_consume_data.set_value(); |
||||||
|
size_t total_consumed_count = 0; |
||||||
|
std::shared_ptr<IDataProvider::mfx_bitstream> dummy_stream; |
||||||
|
size_t stream_idx = 0; |
||||||
|
do { |
||||||
|
EXPECT_TRUE(provider.fetch_bitstream_data(dummy_stream)); |
||||||
|
EXPECT_TRUE(dummy_stream); |
||||||
|
EXPECT_EQ(dummy_stream->DataLength, stream_idx); |
||||||
|
stream_idx ++; |
||||||
|
total_consumed_count++; |
||||||
|
} while (total_consumed_count != produce_buffer_count); |
||||||
|
}); |
||||||
|
|
||||||
|
producer.join(); |
||||||
|
consumer.join(); |
||||||
|
} |
||||||
|
} |
||||||
|
} // namespace opencv_test
|
||||||
|
|
||||||
|
#endif // HAVE_ONEVPL
|
Loading…
Reference in new issue