/*############################################################################ # Copyright (C) 2005 Intel Corporation # # SPDX-License-Identifier: MIT ############################################################################*/ #include #include "mfx_samples_config.h" #include "sample_defs.h" #if defined(_WIN32) || defined(_WIN64) #include #include #endif #include #include #include #include #include "pipeline_decode.h" #include "sysmem_allocator.h" #if defined(_WIN32) || defined(_WIN64) #include "d3d11_allocator.h" #include "d3d11_device.h" #include "d3d_allocator.h" #include "d3d_device.h" #endif #if defined LIBVA_SUPPORT #include "vaapi_allocator.h" #include "vaapi_device.h" #include "vaapi_utils.h" #endif #if defined(LIBVA_WAYLAND_SUPPORT) #include "class_wayland.h" #endif #include "version.h" #define __SYNC_WA // avoid sync issue on Media SDK side #ifndef MFX_VERSION #error MFX_VERSION not defined #endif CDecodingPipeline::CDecodingPipeline() : m_FileWriter(), m_FileReader(), m_mfxBS(8 * 1024 * 1024), totalBytesProcessed(0), m_pLoader(), m_mfxSession(), m_impl(0), m_pmfxDEC(NULL), m_pmfxVPP(NULL), m_mfxVideoParams(), m_mfxVppVideoParams(), m_pGeneralAllocator(NULL), m_pmfxAllocatorParams(NULL), m_memType(SYSTEM_MEMORY), m_bExternalAlloc(false), m_bDecOutSysmem(false), m_mfxResponse({}), m_mfxVppResponse({}), m_pCurrentFreeSurface(NULL), m_pCurrentFreeVppSurface(NULL), m_pCurrentFreeOutputSurface(NULL), m_pCurrentOutputSurface(NULL), m_pDeliverOutputSemaphore(NULL), m_pDeliveredEvent(NULL), m_error(MFX_ERR_NONE), m_bStopDeliverLoop(false), m_eWorkMode(MODE_PERFORMANCE), m_bIsMVC(false), m_bIsVideoWall(false), m_bIsCompleteFrame(false), m_fourcc(0), m_bPrintLatency(false), m_bOutI420(false), m_bDxgiFs(false), m_vppOutWidth(0), m_vppOutHeight(0), m_nTimeout(0), m_nMaxFps(0), m_nFrames(0), m_diMode(0), m_bVppIsUsed(false), m_bVppFullColorRange(false), m_bSoftRobustFlag(false), m_vLatency(), m_fpsLimiter(), m_VppVideoSignalInfo({}), m_VppSurfaceExtParams(), m_ContentLight({}), m_DisplayColor({}), m_OutSurfaceExtParams(), #if defined(LINUX32) || defined(LINUX64) m_strDevicePath(), #endif m_hwdev(NULL), #if D3D_SURFACES_SUPPORT m_d3dRender(), #endif m_bRenderWin(false), m_nRenderWinX(0), m_nRenderWinY(0), m_nRenderWinW(0), m_nRenderWinH(0), #ifdef LIBVA_SUPPORT m_export_mode(vaapiAllocatorParams::DONOT_EXPORT), #else m_export_mode(0), #endif m_monitorType(0), #ifdef LIBVA_SUPPORT m_libvaBackend(0), m_bPerfMode(false), #endif m_bResetFileWriter(false), m_bResetFileReader(false), m_verSessionInit(API_2X) { // reserve some space to reduce dynamic reallocation impact on pipeline execution m_vLatency.reserve(1000); m_VppVideoSignalInfo.Header.BufferId = MFX_EXTBUFF_VPP_VIDEO_SIGNAL_INFO; m_VppVideoSignalInfo.Header.BufferSz = sizeof(m_VppVideoSignalInfo); } CDecodingPipeline::~CDecodingPipeline() { Close(); } #if (defined(_WIN64) || defined(_WIN32)) && (MFX_VERSION >= 1031) mfxU32 CDecodingPipeline::GetPreferredAdapterNum(const mfxAdaptersInfo& adapters, const sInputParams& params) { if (adapters.NumActual == 0 || !adapters.Adapters) return 0; if (params.bPreferdGfx) { // Find dGfx adapter in list and return it's index auto idx = std::find_if(adapters.Adapters, adapters.Adapters + adapters.NumActual, [](const mfxAdapterInfo info) { return info.Platform.MediaAdapterType == mfxMediaAdapterType::MFX_MEDIA_DISCRETE; }); // No dGfx in list if (idx == adapters.Adapters + adapters.NumActual) { msdk_printf( MSDK_STRING("Warning: No dGfx detected on machine. Will pick another adapter\n")); return 0; } return static_cast(std::distance(adapters.Adapters, idx)); } if (params.bPreferiGfx) { // Find iGfx adapter in list and return it's index auto idx = std::find_if(adapters.Adapters, adapters.Adapters + adapters.NumActual, [](const mfxAdapterInfo info) { return info.Platform.MediaAdapterType == mfxMediaAdapterType::MFX_MEDIA_INTEGRATED; }); // No iGfx in list if (idx == adapters.Adapters + adapters.NumActual) { msdk_printf( MSDK_STRING("Warning: No iGfx detected on machine. Will pick another adapter\n")); return 0; } return static_cast(std::distance(adapters.Adapters, idx)); } // Other ways return 0, i.e. best suitable detected by dispatcher return 0; } #endif mfxStatus CDecodingPipeline::GetImpl(const sInputParams& params, mfxIMPL& impl) { if (!params.bUseHWLib) { impl = MFX_IMPL_SOFTWARE; return MFX_ERR_NONE; } #if (defined(_WIN64) || defined(_WIN32)) && (MFX_VERSION >= 1031) mfxU32 num_adapters_available; mfxStatus sts = MFXQueryAdaptersNumber(&num_adapters_available); MSDK_CHECK_STATUS(sts, "MFXQueryAdaptersNumber failed"); mfxBitstreamWrapper dummy_stream(8 * 1024 * 1024); std::vector displays_data(num_adapters_available); mfxAdaptersInfo adapters = { displays_data.data(), mfxU32(displays_data.size()), 0u }; constexpr mfxU32 realloc_limit = 4; // Limit for bitstream reallocation (it would be 2^realloc_limit times more than in beginning) before reporting an error in case if SPS couldn't be found for (mfxU32 i = 0;; ++i) { sts = m_FileReader->ReadNextFrame(&dummy_stream); if (MFX_ERR_MORE_DATA == sts && i < realloc_limit && dummy_stream.MaxLength == dummy_stream.DataLength) { // Ignore the error now and alloc more data for bitstream dummy_stream.Extend(dummy_stream.MaxLength * 2); } MSDK_CHECK_STATUS(sts, "m_FileReader->ReadNextFrame failed"); sts = MFXQueryAdaptersDecode(&dummy_stream, params.videoType, &adapters); if (MFX_ERR_MORE_DATA == sts) { continue; } if (sts == MFX_ERR_NOT_FOUND) { msdk_printf(MSDK_STRING("ERROR: No suitable adapters found for this workload\n")); } MSDK_CHECK_STATUS(sts, "MFXQueryAdapters failed"); break; } m_FileReader->Reset(); mfxU32 idx = GetPreferredAdapterNum(adapters, params); switch (adapters.Adapters[idx].Number) { case 0: impl = MFX_IMPL_HARDWARE; break; case 1: impl = MFX_IMPL_HARDWARE2; break; case 2: impl = MFX_IMPL_HARDWARE3; break; case 3: impl = MFX_IMPL_HARDWARE4; break; default: // Try searching on all display adapters impl = MFX_IMPL_HARDWARE_ANY; break; } #else // Library should pick first available compatible adapter during InitEx call with MFX_IMPL_HARDWARE_ANY impl = MFX_IMPL_HARDWARE_ANY; #endif // (defined(_WIN64) || defined(_WIN32)) && (MFX_VERSION >= 1031) // If d3d11 surfaces are used ask the library to run acceleration through D3D11 // feature may be unsupported due to OS or MSDK API version // prefer d3d11 by default #if D3D_SURFACES_SUPPORT if (D3D9_MEMORY != params.memType || D3D9_MEMORY != m_memType) impl |= MFX_IMPL_VIA_D3D11; #endif return MFX_ERR_NONE; } mfxStatus CDecodingPipeline::Init(sInputParams* pParams) { MSDK_CHECK_POINTER(pParams, MFX_ERR_NULL_PTR); mfxStatus sts = MFX_ERR_NONE; // prepare input stream file reader // for VP8 complete and single frame reader is a requirement // create reader that supports completeframe mode for latency oriented scenarios if (pParams->bLowLat || pParams->bCalLat) { switch (pParams->videoType) { case MFX_CODEC_AVC: m_FileReader.reset(new CH264FrameReader()); m_bIsCompleteFrame = true; m_bPrintLatency = pParams->bCalLat; break; case MFX_CODEC_JPEG: m_FileReader.reset(new CJPEGFrameReader()); m_bIsCompleteFrame = true; m_bPrintLatency = pParams->bCalLat; break; case MFX_CODEC_VP8: case MFX_CODEC_VP9: case MFX_CODEC_AV1: m_FileReader.reset(new CIVFFrameReader()); m_bIsCompleteFrame = true; m_bPrintLatency = pParams->bCalLat; break; default: return MFX_ERR_UNSUPPORTED; // latency mode is supported only for H.264 and JPEG codecs } } else { switch (pParams->videoType) { case MFX_CODEC_VP8: case MFX_CODEC_VP9: case MFX_CODEC_AV1: m_FileReader.reset(new CIVFFrameReader()); break; default: m_FileReader.reset(new CSmplBitstreamReader()); break; } } if (pParams->fourcc) m_fourcc = pParams->fourcc; #ifdef LIBVA_SUPPORT if (pParams->bPerfMode) m_bPerfMode = true; #endif if (pParams->Width) m_vppOutWidth = pParams->Width; if (pParams->Height) m_vppOutHeight = pParams->Height; #if defined(LINUX32) || defined(LINUX64) m_strDevicePath = pParams->strDevicePath; #endif if (pParams->memType) m_memType = pParams->memType; else { switch (pParams->mode) { case MODE_PERFORMANCE: #if defined(_WIN32) || defined(_WIN64) m_memType = pParams->bUseHWLib ? D3D11_MEMORY : SYSTEM_MEMORY; #elif defined(LIBVA_SUPPORT) m_memType = pParams->bUseHWLib ? D3D9_MEMORY : SYSTEM_MEMORY; #endif break; case MODE_RENDERING: m_memType = D3D9_MEMORY; break; case MODE_FILE_DUMP: m_memType = SYSTEM_MEMORY; break; default: assert(0); MSDK_CHECK_STATUS(MFX_ERR_UNSUPPORTED, "Unexpected eWorkMode"); } } m_nMaxFps = pParams->nMaxFPS; m_nFrames = pParams->nFrames ? pParams->nFrames : MFX_INFINITE; m_bOutI420 = pParams->outI420; m_nTimeout = pParams->nTimeout; m_bSoftRobustFlag = pParams->bSoftRobustFlag; // Initializing file reader totalBytesProcessed = 0; sts = m_FileReader->Init(pParams->strSrcFile); if (sts == MFX_ERR_UNSUPPORTED && pParams->videoType == MFX_CODEC_AV1) { m_FileReader.reset(new CSmplBitstreamReader()); msdk_printf(MSDK_STRING("WARNING: Stream is not IVF, default reader\n")); } MSDK_CHECK_STATUS(sts, "m_FileReader->Init failed"); mfxInitParamlWrap initPar; auto threadsPar = initPar.AddExtBuffer(); MSDK_CHECK_POINTER(threadsPar, MFX_ERR_MEMORY_ALLOC); initPar.GPUCopy = pParams->gpuCopy; if (pParams->nThreadsNum) { threadsPar->NumThread = pParams->nThreadsNum; } if (pParams->SchedulingType) { threadsPar->SchedulingType = pParams->SchedulingType; } if (pParams->Priority) { threadsPar->Priority = pParams->Priority; } if (pParams->eDeinterlace) { m_diMode = pParams->eDeinterlace; } if (pParams->bUseFullColorRange) { m_bVppFullColorRange = pParams->bUseFullColorRange; } if (pParams->bDxgiFs) { m_bDxgiFs = pParams->bDxgiFs; } bool bResolutionSpecified = pParams->Width || pParams->Height; // potentially VPP can be inserted if (bResolutionSpecified) m_bDecOutSysmem = pParams->bUseHWLib ? false : true; else m_bDecOutSysmem = m_memType == SYSTEM_MEMORY; m_eWorkMode = pParams->mode; m_monitorType = pParams->monitorType; // create device and allocator #if defined(LIBVA_SUPPORT) m_libvaBackend = pParams->libvaBackend; #endif // defined(MFX_LIBVA_SUPPORT) m_verSessionInit = pParams->verSessionInit; if (m_verSessionInit == API_1X) { initPar.Version.Major = 1; initPar.Version.Minor = 0; sts = GetImpl(*pParams, initPar.Implementation); MSDK_CHECK_STATUS(sts, "GetImpl failed"); sts = m_mfxSession.InitEx(initPar); MSDK_CHECK_STATUS(sts, "m_mfxSession.InitEx failed"); } else { initPar.Implementation = pParams->bUseHWLib ? MFX_IMPL_HARDWARE : MFX_IMPL_SOFTWARE; m_pLoader.reset(new VPLImplementationLoader); if (pParams->dGfxIdx >= 0) m_pLoader->SetDiscreteAdapterIndex(pParams->dGfxIdx); else m_pLoader->SetAdapterType(pParams->adapterType); if (pParams->adapterNum >= 0) m_pLoader->SetAdapterNum(pParams->adapterNum); #ifdef ONEVPL_EXPERIMENTAL if (pParams->PCIDeviceSetup) m_pLoader->SetPCIDevice(pParams->PCIDomain, pParams->PCIBus, pParams->PCIDevice, pParams->PCIFunction); #if (defined(_WIN64) || defined(_WIN32)) if (pParams->luid.HighPart > 0 || pParams->luid.LowPart) m_pLoader->SetupLUID(pParams->luid); #else m_pLoader->SetupDRMRenderNodeNum(pParams->DRMRenderNodeNum); #endif #endif if (!pParams->accelerationMode && pParams->bUseHWLib) { #if D3D_SURFACES_SUPPORT pParams->accelerationMode = MFX_ACCEL_MODE_VIA_D3D11; #elif defined(LIBVA_SUPPORT) pParams->accelerationMode = MFX_ACCEL_MODE_VIA_VAAPI; #endif } bool bLowLatencyMode = !pParams->dispFullSearch; sts = m_pLoader->ConfigureAndEnumImplementations(initPar.Implementation, pParams->accelerationMode, bLowLatencyMode); MSDK_CHECK_STATUS(sts, "m_mfxSession.EnumImplementations failed"); sts = m_mfxSession.CreateSession(m_pLoader.get()); MSDK_CHECK_STATUS(sts, "m_mfxSession.CreateSession failed"); } mfxVersion version; sts = m_mfxSession.QueryVersion(&version); // get real API version of the loaded library MSDK_CHECK_STATUS(sts, "m_mfxSession.QueryVersion failed"); sts = m_mfxSession.QueryIMPL(&m_impl); // get actual library implementation MSDK_CHECK_STATUS(sts, "m_mfxSession.QueryIMPL failed"); #if defined(_WIN32) || defined(_WIN64) if (pParams->videoType == MFX_CODEC_AV1 && MFX_IMPL_VIA_MASK(m_impl) == MFX_IMPL_VIA_D3D9) { sts = MFX_ERR_UNSUPPORTED; MSDK_CHECK_STATUS(sts, "AV1d have no DX9 support \n"); } #endif bool isDeviceRequired = false; mfxHandleType hdl_t; #if D3D_SURFACES_SUPPORT isDeviceRequired = m_memType != SYSTEM_MEMORY || !m_bDecOutSysmem; hdl_t = #if MFX_D3D11_SUPPORT D3D11_MEMORY == m_memType ? MFX_HANDLE_D3D11_DEVICE : #endif // #if MFX_D3D11_SUPPORT MFX_HANDLE_D3D9_DEVICE_MANAGER; #elif LIBVA_SUPPORT if (MFX_IMPL_SOFTWARE != initPar.Implementation) { isDeviceRequired = true; // on Linux MediaSDK doesn't create device internally hdl_t = MFX_HANDLE_VA_DISPLAY; } #endif if (isDeviceRequired) { sts = CreateHWDevice(); MSDK_CHECK_STATUS(sts, "CreateHWDevice failed"); if (pParams->bUseHWLib) { mfxHDL hdl = NULL; sts = m_hwdev->GetHandle(hdl_t, &hdl); MSDK_CHECK_STATUS(sts, "m_hwdev->GetHandle failed"); sts = m_mfxSession.SetHandle(hdl_t, hdl); MSDK_CHECK_STATUS(sts, "m_mfxSession.SetHandle failed"); } } if (pParams->bIsMVC && !CheckVersion(&version, MSDK_FEATURE_MVC)) { msdk_printf(MSDK_STRING("error: MVC is not supported in the %d.%d API version\n"), (int)version.Major, (int)version.Minor); return MFX_ERR_UNSUPPORTED; } if ((pParams->videoType == MFX_CODEC_JPEG) && !CheckVersion(&version, MSDK_FEATURE_JPEG_DECODE)) { msdk_printf(MSDK_STRING("error: Jpeg is not supported in the %d.%d API version\n"), (int)version.Major, (int)version.Minor); return MFX_ERR_UNSUPPORTED; } if (pParams->bLowLat && !CheckVersion(&version, MSDK_FEATURE_LOW_LATENCY)) { msdk_printf( MSDK_STRING("error: Low Latency mode is not supported in the %d.%d API version\n"), (int)version.Major, (int)version.Minor); return MFX_ERR_UNSUPPORTED; } if (pParams->eDeinterlace && (pParams->eDeinterlace != MFX_DEINTERLACING_ADVANCED) && (pParams->eDeinterlace != MFX_DEINTERLACING_BOB)) { msdk_printf(MSDK_STRING("error: Unsupported deinterlace value: %d\n"), (int)pParams->eDeinterlace); return MFX_ERR_UNSUPPORTED; } if (pParams->bRenderWin) { m_bRenderWin = pParams->bRenderWin; // note: currently position is unsupported for Wayland #if !defined(LIBVA_WAYLAND_SUPPORT) m_nRenderWinX = pParams->nRenderWinX; m_nRenderWinY = pParams->nRenderWinY; #endif } m_fpsLimiter.Reset(pParams->nMaxFPS); // create decoder m_pmfxDEC = new MFXVideoDECODE(m_mfxSession); MSDK_CHECK_POINTER(m_pmfxDEC, MFX_ERR_MEMORY_ALLOC); // set video type in parameters m_mfxVideoParams.mfx.CodecId = pParams->videoType; m_mfxVideoParams.mfx.IgnoreLevelConstrain = pParams->bIgnoreLevelConstrain; // Populate parameters. Involves DecodeHeader call sts = InitMfxParams(pParams); MSDK_CHECK_STATUS(sts, "InitMfxParams failed"); if (m_bVppIsUsed) { m_pmfxVPP = new MFXVideoVPP(m_mfxSession); if (!m_pmfxVPP) return MFX_ERR_MEMORY_ALLOC; } if (m_eWorkMode == MODE_FILE_DUMP) { // prepare YUV file writer sts = m_FileWriter.Init(pParams->strDstFile, pParams->numViews); MSDK_CHECK_STATUS(sts, "m_FileWriter.Init failed"); } else if ((m_eWorkMode != MODE_PERFORMANCE) && (m_eWorkMode != MODE_RENDERING)) { msdk_printf(MSDK_STRING("error: unsupported work mode\n")); return MFX_ERR_UNSUPPORTED; } sts = CreateAllocator(); MSDK_CHECK_STATUS(sts, "CreateAllocator failed"); // in case of HW accelerated decode frames must be allocated prior to decoder initialization sts = AllocFrames(); MSDK_CHECK_STATUS(sts, "AllocFrames failed"); sts = m_pmfxDEC->Init(&m_mfxVideoParams); if (MFX_WRN_PARTIAL_ACCELERATION == sts) { msdk_printf(MSDK_STRING("WARNING: partial acceleration\n")); MSDK_IGNORE_MFX_STS(sts, MFX_WRN_PARTIAL_ACCELERATION); } MSDK_CHECK_STATUS(sts, "m_pmfxDEC->Init failed"); if (m_bVppIsUsed) { if (m_diMode) m_mfxVppVideoParams.vpp.Out.PicStruct = MFX_PICSTRUCT_PROGRESSIVE; if (pParams->ScalingMode) { auto par = m_mfxVppVideoParams.AddExtBuffer(); par->ScalingMode = pParams->ScalingMode; } sts = m_pmfxVPP->Init(&m_mfxVppVideoParams); if (MFX_WRN_PARTIAL_ACCELERATION == sts) { msdk_printf(MSDK_STRING("WARNING: partial acceleration\n")); MSDK_IGNORE_MFX_STS(sts, MFX_WRN_PARTIAL_ACCELERATION); } MSDK_CHECK_STATUS(sts, "m_pmfxVPP->Init failed"); } sts = m_pmfxDEC->GetVideoParam(&m_mfxVideoParams); MSDK_CHECK_STATUS(sts, "m_pmfxDEC->GetVideoParam failed"); if (m_eWorkMode == MODE_RENDERING) { sts = CreateRenderingWindow(pParams); MSDK_CHECK_STATUS(sts, "CreateRenderingWindow failed"); } return sts; } bool CDecodingPipeline::IsVppRequired(sInputParams* pParams) { bool bVppIsUsed = false; /* Re-size */ if ((m_mfxVideoParams.mfx.FrameInfo.CropW != pParams->Width) || (m_mfxVideoParams.mfx.FrameInfo.CropH != pParams->Height)) { bVppIsUsed = pParams->Width && pParams->Height; if ((MODE_DECODER_POSTPROC_AUTO == pParams->nDecoderPostProcessing) || (MODE_DECODER_POSTPROC_FORCE == pParams->nDecoderPostProcessing)) { /* Decoder will make decision about internal post-processing usage slightly later */ bVppIsUsed = false; } } // JPEG and Capture decoders can provide output in nv12 and rgb4 formats if (pParams->videoType == MFX_CODEC_JPEG) { bVppIsUsed |= m_fourcc && (m_fourcc != MFX_FOURCC_NV12) && (m_fourcc != MFX_FOURCC_RGB4); } else { bVppIsUsed |= m_fourcc && (m_fourcc != m_mfxVideoParams.mfx.FrameInfo.FourCC); } if (pParams->eDeinterlace) { bVppIsUsed = true; } if ((MODE_DECODER_POSTPROC_AUTO == pParams->nDecoderPostProcessing) || (MODE_DECODER_POSTPROC_FORCE == pParams->nDecoderPostProcessing)) { /* Decoder will make decision about internal post-processing usage slightly later */ if ((pParams->videoType == MFX_CODEC_AVC) || (pParams->videoType == MFX_CODEC_HEVC)) bVppIsUsed = false; } return bVppIsUsed; } void CDecodingPipeline::Close() { #if D3D_SURFACES_SUPPORT m_d3dRender.Close(); #endif MSDK_SAFE_DELETE(m_pmfxDEC); MSDK_SAFE_DELETE(m_pmfxVPP); DeleteFrames(); DeallocateExtMVCBuffers(); m_mfxSession.Close(); m_FileWriter.Close(); if (m_FileReader.get()) m_FileReader->Close(); auto vppExtParams = m_mfxVppVideoParams.GetExtBuffer(); if (vppExtParams) MSDK_SAFE_DELETE_ARRAY(vppExtParams->AlgList); // allocator if used as external for MediaSDK must be deleted after decoder DeleteAllocator(); return; } mfxStatus CDecodingPipeline::CreateRenderingWindow(sInputParams* pParams) { mfxStatus sts = MFX_ERR_NONE; #if D3D_SURFACES_SUPPORT sWindowParams windowParams; windowParams.lpWindowName = pParams->bWallNoTitle ? NULL : MSDK_STRING("sample_decode"); windowParams.nx = pParams->nWallW; windowParams.ny = pParams->nWallH; if (m_bVppIsUsed) { windowParams.nWidth = m_mfxVppVideoParams.vpp.Out.Width; windowParams.nHeight = m_mfxVppVideoParams.vpp.Out.Height; } else { windowParams.nWidth = m_mfxVideoParams.mfx.FrameInfo.Width; windowParams.nHeight = m_mfxVideoParams.mfx.FrameInfo.Height; } windowParams.ncell = pParams->nWallCell; windowParams.nAdapter = pParams->nWallMonitor; windowParams.lpClassName = MSDK_STRING("Render Window Class"); windowParams.dwStyle = WS_OVERLAPPEDWINDOW; windowParams.hWndParent = NULL; windowParams.hMenu = NULL; windowParams.hInstance = GetModuleHandle(NULL); windowParams.lpParam = NULL; windowParams.bFullScreen = FALSE; sts = m_d3dRender.Init(windowParams); MSDK_CHECK_STATUS(sts, "m_d3dRender.Init failed"); //setting videowall flag m_bIsVideoWall = 0 != windowParams.nx; #endif return sts; } mfxStatus CDecodingPipeline::InitMfxParams(sInputParams* pParams) { MSDK_CHECK_POINTER(m_pmfxDEC, MFX_ERR_NULL_PTR); mfxStatus sts = MFX_ERR_NONE; mfxU32& numViews = pParams->numViews; if (pParams->bErrorReport) { auto decErrorReport = m_mfxBS.AddExtBuffer(); MSDK_CHECK_POINTER(decErrorReport, MFX_ERR_MEMORY_ALLOC); } if (m_mfxVideoParams.mfx.CodecId == MFX_CODEC_VP9) m_mfxVideoParams.mfx.EnableReallocRequest = MFX_CODINGOPTION_ON; // try to find a sequence header in the stream // if header is not found this function exits with error (e.g. if device was lost and there's no header in the remaining stream) for (;;) { // trying to find PicStruct information in AVI headers if (m_mfxVideoParams.mfx.CodecId == MFX_CODEC_JPEG) MJPEG_AVI_ParsePicStruct(&m_mfxBS); if (pParams->bErrorReport) { auto errorReport = m_mfxBS.GetExtBuffer(); MSDK_CHECK_POINTER(errorReport, MFX_ERR_NOT_INITIALIZED); errorReport->ErrorTypes = 0; // parse bit stream and fill mfx params sts = m_pmfxDEC->DecodeHeader(&m_mfxBS, &m_mfxVideoParams); PrintDecodeErrorReport(errorReport); } else { // parse bit stream and fill mfx params sts = m_pmfxDEC->DecodeHeader(&m_mfxBS, &m_mfxVideoParams); } if (!sts) { m_bVppIsUsed = IsVppRequired(pParams); } if (MFX_ERR_MORE_DATA == sts) { if (m_mfxBS.MaxLength == m_mfxBS.DataLength) { m_mfxBS.Extend(m_mfxBS.MaxLength * 2); } // read a portion of data totalBytesProcessed += m_mfxBS.DataOffset; sts = m_FileReader->ReadNextFrame(&m_mfxBS); MSDK_CHECK_STATUS(sts, "m_FileReader->ReadNextFrame failed"); continue; } else { // Enter MVC mode if (m_bIsMVC) { // Check for attached external parameters - if we have them already, // we don't need to attach them again if (NULL != m_mfxVideoParams.ExtParam) break; auto mvcSeqDesc = m_mfxVideoParams.AddExtBuffer(); MSDK_CHECK_POINTER(mvcSeqDesc, MFX_ERR_MEMORY_ALLOC); sts = m_pmfxDEC->DecodeHeader(&m_mfxBS, &m_mfxVideoParams); if (MFX_ERR_NOT_ENOUGH_BUFFER == sts) { sts = AllocateExtMVCBuffers(); MSDK_CHECK_STATUS(sts, "AllocateExtMVCBuffers failed"); MSDK_CHECK_POINTER(m_mfxVideoParams.ExtParam, MFX_ERR_MEMORY_ALLOC); continue; } } // if input is interlaced JPEG stream if (m_mfxBS.PicStruct == MFX_PICSTRUCT_FIELD_TFF || m_mfxBS.PicStruct == MFX_PICSTRUCT_FIELD_BFF) { m_mfxVideoParams.mfx.FrameInfo.CropH *= 2; m_mfxVideoParams.mfx.FrameInfo.Height = MSDK_ALIGN16(m_mfxVideoParams.mfx.FrameInfo.CropH); m_mfxVideoParams.mfx.FrameInfo.PicStruct = m_mfxBS.PicStruct; } switch (pParams->nRotation) { case 0: m_mfxVideoParams.mfx.Rotation = MFX_ROTATION_0; break; case 90: m_mfxVideoParams.mfx.Rotation = MFX_ROTATION_90; break; case 180: m_mfxVideoParams.mfx.Rotation = MFX_ROTATION_180; break; case 270: m_mfxVideoParams.mfx.Rotation = MFX_ROTATION_270; break; default: return MFX_ERR_UNSUPPORTED; } break; } } // check DecodeHeader status if (MFX_WRN_PARTIAL_ACCELERATION == sts) { msdk_printf(MSDK_STRING("WARNING: partial acceleration\n")); MSDK_IGNORE_MFX_STS(sts, MFX_WRN_PARTIAL_ACCELERATION); } MSDK_CHECK_STATUS(sts, "m_pmfxDEC->DecodeHeader failed"); if (!m_mfxVideoParams.mfx.FrameInfo.FrameRateExtN || !m_mfxVideoParams.mfx.FrameInfo.FrameRateExtD) { msdk_printf(MSDK_STRING("pretending that stream is 30fps one\n")); m_mfxVideoParams.mfx.FrameInfo.FrameRateExtN = 30; m_mfxVideoParams.mfx.FrameInfo.FrameRateExtD = 1; } if (!m_mfxVideoParams.mfx.FrameInfo.AspectRatioW || !m_mfxVideoParams.mfx.FrameInfo.AspectRatioH) { msdk_printf(MSDK_STRING("pretending that aspect ratio is 1:1\n")); m_mfxVideoParams.mfx.FrameInfo.AspectRatioW = 1; m_mfxVideoParams.mfx.FrameInfo.AspectRatioH = 1; } // Videoparams for RGB4 JPEG decoder output if ((pParams->fourcc == MFX_FOURCC_RGB4) && (pParams->videoType == MFX_CODEC_JPEG)) { m_mfxVideoParams.mfx.FrameInfo.FourCC = MFX_FOURCC_RGB4; m_mfxVideoParams.mfx.FrameInfo.ChromaFormat = MFX_CHROMAFORMAT_YUV444; if (pParams->chromaType == MFX_JPEG_COLORFORMAT_RGB) { m_mfxVideoParams.mfx.JPEGColorFormat = pParams->chromaType; } } /* Lets make final decision how to use VPP...*/ if (!m_bVppIsUsed) { if ((m_mfxVideoParams.mfx.FrameInfo.CropW != pParams->Width && pParams->Width) || (m_mfxVideoParams.mfx.FrameInfo.CropH != pParams->Height && pParams->Height) || (pParams->nDecoderPostProcessing && pParams->videoType == MFX_CODEC_AVC) || (pParams->nDecoderPostProcessing && pParams->videoType == MFX_CODEC_HEVC) || (pParams->nDecoderPostProcessing && pParams->videoType == MFX_CODEC_JPEG && pParams->fourcc == MFX_FOURCC_RGB4 && // No need to use decoder's post processing for decoding of JPEG with RGB 4:4:4 // to MFX_FOURCC_RGB4, because this decoding is done in one step // In every other case, color conversion is requred, so try decoder's post processing. !(m_mfxVideoParams.mfx.JPEGColorFormat == MFX_JPEG_COLORFORMAT_RGB && m_mfxVideoParams.mfx.FrameInfo.ChromaFormat == MFX_CHROMAFORMAT_YUV444))) { /* By default VPP used for resize */ m_bVppIsUsed = true; /* But... lets try to use decoder's post processing */ if (((MODE_DECODER_POSTPROC_AUTO == pParams->nDecoderPostProcessing) || (MODE_DECODER_POSTPROC_FORCE == pParams->nDecoderPostProcessing)) && (MFX_CODEC_AVC == m_mfxVideoParams.mfx.CodecId || MFX_CODEC_JPEG == m_mfxVideoParams.mfx.CodecId || MFX_CODEC_HEVC == m_mfxVideoParams.mfx.CodecId || MFX_CODEC_VP9 == m_mfxVideoParams.mfx.CodecId || MFX_CODEC_AV1 == m_mfxVideoParams.mfx.CodecId) && (MFX_PICSTRUCT_PROGRESSIVE == m_mfxVideoParams.mfx.FrameInfo.PicStruct)) /* ...And only for progressive!*/ { /* it is possible to use decoder's post-processing */ // JPEG only suppoted w/o resize, so use W/H from DecodeHeader(), if they are not set if (MFX_CODEC_JPEG == m_mfxVideoParams.mfx.CodecId && (!pParams->Width || !pParams->Height)) { pParams->Width = m_mfxVideoParams.mfx.FrameInfo.CropW; pParams->Height = m_mfxVideoParams.mfx.FrameInfo.CropH; } m_bVppIsUsed = false; auto decPostProcessing = m_mfxVideoParams.AddExtBuffer(); MSDK_CHECK_POINTER(decPostProcessing, MFX_ERR_MEMORY_ALLOC); decPostProcessing->In.CropX = 0; decPostProcessing->In.CropY = 0; decPostProcessing->In.CropW = m_mfxVideoParams.mfx.FrameInfo.CropW; decPostProcessing->In.CropH = m_mfxVideoParams.mfx.FrameInfo.CropH; decPostProcessing->Out.FourCC = m_mfxVideoParams.mfx.FrameInfo.FourCC; decPostProcessing->Out.ChromaFormat = m_mfxVideoParams.mfx.FrameInfo.ChromaFormat; if (pParams->videoType == MFX_CODEC_AVC || pParams->videoType == MFX_CODEC_HEVC) { switch (pParams->fourcc) { case MFX_FOURCC_RGB4: decPostProcessing->Out.FourCC = MFX_FOURCC_RGB4; decPostProcessing->Out.ChromaFormat = MFX_CHROMAFORMAT_YUV444; break; case MFX_FOURCC_NV12: decPostProcessing->Out.FourCC = MFX_FOURCC_NV12; decPostProcessing->Out.ChromaFormat = MFX_CHROMAFORMAT_YUV420; break; case MFX_FOURCC_P010: decPostProcessing->Out.FourCC = MFX_FOURCC_P010; decPostProcessing->Out.ChromaFormat = MFX_CHROMAFORMAT_YUV420; break; case MFX_FOURCC_P016: decPostProcessing->Out.FourCC = MFX_FOURCC_P016; decPostProcessing->Out.ChromaFormat = MFX_CHROMAFORMAT_YUV420; break; case MFX_FOURCC_YUY2: decPostProcessing->Out.FourCC = MFX_FOURCC_YUY2; decPostProcessing->Out.ChromaFormat = MFX_CHROMAFORMAT_YUV422; break; case MFX_FOURCC_Y210: decPostProcessing->Out.FourCC = MFX_FOURCC_Y210; decPostProcessing->Out.ChromaFormat = MFX_CHROMAFORMAT_YUV422; break; case MFX_FOURCC_Y216: decPostProcessing->Out.FourCC = MFX_FOURCC_Y216; decPostProcessing->Out.ChromaFormat = MFX_CHROMAFORMAT_YUV422; break; case MFX_FOURCC_AYUV: decPostProcessing->Out.FourCC = MFX_FOURCC_AYUV; decPostProcessing->Out.ChromaFormat = MFX_CHROMAFORMAT_YUV444; break; case MFX_FOURCC_Y410: decPostProcessing->Out.FourCC = MFX_FOURCC_Y410; decPostProcessing->Out.ChromaFormat = MFX_CHROMAFORMAT_YUV444; break; case MFX_FOURCC_Y416: decPostProcessing->Out.FourCC = MFX_FOURCC_Y416; decPostProcessing->Out.ChromaFormat = MFX_CHROMAFORMAT_YUV444; break; default: break; } } decPostProcessing->Out.Width = MSDK_ALIGN16(pParams->Width); decPostProcessing->Out.Height = MSDK_ALIGN16(pParams->Height); decPostProcessing->Out.CropX = 0; decPostProcessing->Out.CropY = 0; decPostProcessing->Out.CropW = pParams->Width; decPostProcessing->Out.CropH = pParams->Height; msdk_printf(MSDK_STRING("Decoder's post-processing is used for resizing\n")); } /* POSTPROC_FORCE */ if (MODE_DECODER_POSTPROC_FORCE == pParams->nDecoderPostProcessing && m_bVppIsUsed) { /* it is impossible to use decoder's post-processing */ msdk_printf(MSDK_STRING( "ERROR: decoder postprocessing (-dec_postproc forced) cannot resize this stream!\n")); return MFX_ERR_UNSUPPORTED; } if ((m_bVppIsUsed) && (MODE_DECODER_POSTPROC_AUTO == pParams->nDecoderPostProcessing)) msdk_printf(MSDK_STRING( "Decoder post-processing is unsupported for this stream, VPP is used\n")); } } // If MVC mode we need to detect number of views in stream if (m_bIsMVC) { auto sequenceBuffer = m_mfxVideoParams.GetExtBuffer(); MSDK_CHECK_POINTER(sequenceBuffer, MFX_ERR_INVALID_VIDEO_PARAM); mfxU32 i = 0; numViews = 0; for (i = 0; i < sequenceBuffer->NumView; ++i) { /* Some MVC streams can contain different information about number of views and view IDs, e.x. numVews = 2 and ViewId[0, 1] = 0, 2 instead of ViewId[0, 1] = 0, 1. numViews should be equal (max(ViewId[i]) + 1) to prevent crashes during output files writing */ if (sequenceBuffer->View[i].ViewId >= numViews) numViews = sequenceBuffer->View[i].ViewId + 1; } } else { numViews = 1; } // specify memory type if (!m_bVppIsUsed) m_mfxVideoParams.IOPattern = (mfxU16)(m_memType != SYSTEM_MEMORY ? MFX_IOPATTERN_OUT_VIDEO_MEMORY : MFX_IOPATTERN_OUT_SYSTEM_MEMORY); else m_mfxVideoParams.IOPattern = (mfxU16)(pParams->bUseHWLib ? MFX_IOPATTERN_OUT_VIDEO_MEMORY : MFX_IOPATTERN_OUT_SYSTEM_MEMORY); m_mfxVideoParams.AsyncDepth = pParams->nAsyncDepth; if (m_mfxVideoParams.mfx.CodecId == MFX_CODEC_AV1) m_mfxVideoParams.mfx.FilmGrain = pParams->bDisableFilmGrain ? 0 : m_mfxVideoParams.mfx.FilmGrain; return MFX_ERR_NONE; } mfxStatus CDecodingPipeline::InitVppFilters() { auto vppExtParams = m_mfxVppVideoParams.AddExtBuffer(); MSDK_CHECK_POINTER(vppExtParams, MFX_ERR_MEMORY_ALLOC); vppExtParams->NumAlg = 4; /* In case of Reset() this code called twice! * But required to have only one allocation to prevent memleaks * Deallocation done in Close() */ if (NULL == vppExtParams->AlgList) vppExtParams->AlgList = new mfxU32[vppExtParams->NumAlg]; if (!vppExtParams->AlgList) return MFX_ERR_NULL_PTR; vppExtParams->AlgList[0] = MFX_EXTBUFF_VPP_DENOISE; // turn off denoising (on by default) vppExtParams->AlgList[1] = MFX_EXTBUFF_VPP_SCENE_ANALYSIS; // turn off scene analysis (on by default) vppExtParams->AlgList[2] = MFX_EXTBUFF_VPP_DETAIL; // turn off detail enhancement (on by default) vppExtParams->AlgList[3] = MFX_EXTBUFF_VPP_PROCAMP; // turn off processing amplified (on by default) if (m_diMode) { auto vppDi = m_mfxVppVideoParams.AddExtBuffer(); MSDK_CHECK_POINTER(vppDi, MFX_ERR_MEMORY_ALLOC); vppDi->Mode = m_diMode; } return MFX_ERR_NONE; } mfxStatus CDecodingPipeline::InitVppParams() { m_mfxVppVideoParams.IOPattern = (mfxU16)(m_bDecOutSysmem ? MFX_IOPATTERN_IN_SYSTEM_MEMORY : MFX_IOPATTERN_IN_VIDEO_MEMORY); m_mfxVppVideoParams.IOPattern |= (m_memType != SYSTEM_MEMORY) ? MFX_IOPATTERN_OUT_VIDEO_MEMORY : MFX_IOPATTERN_OUT_SYSTEM_MEMORY; MSDK_MEMCPY_VAR(m_mfxVppVideoParams.vpp.In, &m_mfxVideoParams.mfx.FrameInfo, sizeof(mfxFrameInfo)); MSDK_MEMCPY_VAR(m_mfxVppVideoParams.vpp.Out, &m_mfxVppVideoParams.vpp.In, sizeof(mfxFrameInfo)); if (m_fourcc) { m_mfxVppVideoParams.vpp.Out.FourCC = m_fourcc; } if (m_vppOutWidth && m_vppOutHeight) { m_mfxVppVideoParams.vpp.Out.CropW = m_vppOutWidth; m_mfxVppVideoParams.vpp.Out.Width = MSDK_ALIGN16(m_vppOutWidth); m_mfxVppVideoParams.vpp.Out.CropH = m_vppOutHeight; m_mfxVppVideoParams.vpp.Out.Height = (MFX_PICSTRUCT_PROGRESSIVE == m_mfxVppVideoParams.vpp.Out.PicStruct) ? MSDK_ALIGN16(m_vppOutHeight) : MSDK_ALIGN32(m_vppOutHeight); } m_mfxVppVideoParams.AsyncDepth = m_mfxVideoParams.AsyncDepth; m_VppSurfaceExtParams.clear(); if (m_bVppFullColorRange) { //Let MSDK figure out the transfer matrix to use m_VppVideoSignalInfo.TransferMatrix = MFX_TRANSFERMATRIX_UNKNOWN; m_VppVideoSignalInfo.NominalRange = MFX_NOMINALRANGE_0_255; m_VppSurfaceExtParams.push_back((mfxExtBuffer*)&m_VppVideoSignalInfo); } // P010 video surfaces should be shifted if (m_memType != SYSTEM_MEMORY && (m_mfxVppVideoParams.vpp.Out.FourCC == MFX_FOURCC_P010 || m_mfxVppVideoParams.vpp.Out.FourCC == MFX_FOURCC_Y210 || m_mfxVppVideoParams.vpp.Out.FourCC == MFX_FOURCC_P016 || m_mfxVppVideoParams.vpp.Out.FourCC == MFX_FOURCC_Y216 || m_mfxVppVideoParams.vpp.Out.FourCC == MFX_FOURCC_Y416)) { m_mfxVppVideoParams.vpp.Out.Shift = 1; } return MFX_ERR_NONE; } mfxStatus CDecodingPipeline::CreateHWDevice() { #if D3D_SURFACES_SUPPORT mfxStatus sts = MFX_ERR_NONE; HWND window = NULL; bool render = (m_eWorkMode == MODE_RENDERING); if (render) { window = (D3D11_MEMORY == m_memType) ? NULL : m_d3dRender.GetWindowHandle(); } #if MFX_D3D11_SUPPORT if (D3D11_MEMORY == m_memType) m_hwdev = new CD3D11Device(); else #endif // #if MFX_D3D11_SUPPORT m_hwdev = new CD3D9Device(); if (NULL == m_hwdev) return MFX_ERR_MEMORY_ALLOC; mfxU32 adapterNum = (m_verSessionInit == API_1X) ? MSDKAdapter::GetNumber(m_mfxSession) : MSDKAdapter::GetNumber(m_pLoader.get()); sts = m_hwdev->Init(window, render ? (m_bIsMVC ? 2 : 1) : 0, adapterNum); MSDK_CHECK_STATUS(sts, "m_hwdev->Init failed"); if (m_bDxgiFs) { m_hwdev->SetDxgiFullScreen(); m_d3dRender.SetDxgiFullScreen(); } if (render) m_d3dRender.SetHWDevice(m_hwdev); #elif defined(LIBVA_DRM_SUPPORT) || defined(LIBVA_X11_SUPPORT) || \ defined(LIBVA_ANDROID_SUPPORT) || defined(LIBVA_WAYLAND_SUPPORT) mfxStatus sts = MFX_ERR_NONE; m_hwdev = CreateVAAPIDevice(m_strDevicePath, m_libvaBackend); if (NULL == m_hwdev) { return MFX_ERR_MEMORY_ALLOC; } mfxU32 adapterNum = (m_verSessionInit == API_1X) ? MSDKAdapter::GetNumber(m_mfxSession) : MSDKAdapter::GetNumber(m_pLoader.get()); sts = m_hwdev->Init(&m_monitorType, (m_eWorkMode == MODE_RENDERING) ? 1 : 0, adapterNum); MSDK_CHECK_STATUS(sts, "m_hwdev->Init failed"); #if defined(LIBVA_WAYLAND_SUPPORT) if (m_eWorkMode == MODE_RENDERING && m_libvaBackend == MFX_LIBVA_WAYLAND) { CVAAPIDeviceWayland* w_dev = dynamic_cast(m_hwdev); if (!w_dev) { MSDK_CHECK_STATUS(MFX_ERR_DEVICE_FAILED, "Failed to reach Wayland VAAPI device"); } Wayland* wld = w_dev->GetWaylandHandle(); if (!wld) { MSDK_CHECK_STATUS(MFX_ERR_DEVICE_FAILED, "Failed to reach Wayland VAAPI device"); } wld->SetRenderWinPos(m_nRenderWinX, m_nRenderWinY); wld->SetPerfMode(m_bPerfMode); } #endif //LIBVA_WAYLAND_SUPPORT #endif return MFX_ERR_NONE; } mfxStatus CDecodingPipeline::ResetDevice() { if (m_hwdev) return m_hwdev->Reset(); return CreateHWDevice(); } mfxStatus CDecodingPipeline::AllocFrames() { MSDK_CHECK_POINTER(m_pmfxDEC, MFX_ERR_NULL_PTR); mfxStatus sts = MFX_ERR_NONE; mfxFrameAllocRequest Request; mfxFrameAllocRequest VppRequest[2]; mfxU16 nSurfNum = 0; // number of surfaces for decoder mfxU16 nVppSurfNum = 0; // number of surfaces for vpp nSurfNum = std::max(0, 4); MSDK_ZERO_MEMORY(Request); MSDK_ZERO_MEMORY(VppRequest[0]); MSDK_ZERO_MEMORY(VppRequest[1]); sts = m_pmfxDEC->Query(&m_mfxVideoParams, &m_mfxVideoParams); MSDK_IGNORE_MFX_STS(sts, MFX_WRN_INCOMPATIBLE_VIDEO_PARAM); MSDK_CHECK_STATUS(sts, "m_pmfxDEC->Query failed"); // calculate number of surfaces required for decoder sts = m_pmfxDEC->QueryIOSurf(&m_mfxVideoParams, &Request); if (MFX_WRN_PARTIAL_ACCELERATION == sts) { msdk_printf(MSDK_STRING("WARNING: partial acceleration\n")); MSDK_IGNORE_MFX_STS(sts, MFX_WRN_PARTIAL_ACCELERATION); m_bDecOutSysmem = true; } MSDK_CHECK_STATUS(sts, "m_pmfxDEC->QueryIOSurf failed"); if (m_eWorkMode == MODE_RENDERING) { // Add surfaces for rendering smoothness Request.NumFrameSuggested += m_nMaxFps / 3; } if (m_bVppIsUsed) { // respecify memory type between Decoder and VPP m_mfxVideoParams.IOPattern = (mfxU16)(m_bDecOutSysmem ? MFX_IOPATTERN_OUT_SYSTEM_MEMORY : MFX_IOPATTERN_OUT_VIDEO_MEMORY); // recalculate number of surfaces required for decoder sts = m_pmfxDEC->QueryIOSurf(&m_mfxVideoParams, &Request); MSDK_IGNORE_MFX_STS(sts, MFX_WRN_PARTIAL_ACCELERATION); MSDK_CHECK_STATUS(sts, "m_pmfxDEC->QueryIOSurf failed"); sts = InitVppParams(); MSDK_CHECK_STATUS(sts, "InitVppParams failed"); sts = m_pmfxVPP->Query(&m_mfxVppVideoParams, &m_mfxVppVideoParams); MSDK_IGNORE_MFX_STS(sts, MFX_WRN_INCOMPATIBLE_VIDEO_PARAM); MSDK_CHECK_STATUS(sts, "m_pmfxVPP->Query failed"); // VppRequest[0] for input frames request, VppRequest[1] for output frames request sts = m_pmfxVPP->QueryIOSurf(&m_mfxVppVideoParams, VppRequest); if (MFX_WRN_PARTIAL_ACCELERATION == sts) { msdk_printf(MSDK_STRING("WARNING: partial acceleration\n")); MSDK_IGNORE_MFX_STS(sts, MFX_WRN_PARTIAL_ACCELERATION); } MSDK_CHECK_STATUS(sts, "m_pmfxVPP->QueryIOSurf failed"); if ((VppRequest[0].NumFrameSuggested < m_mfxVppVideoParams.AsyncDepth) || (VppRequest[1].NumFrameSuggested < m_mfxVppVideoParams.AsyncDepth)) return MFX_ERR_MEMORY_ALLOC; // If surfaces are shared by 2 components, c1 and c2. NumSurf = c1_out + c2_in - AsyncDepth + 1 // The number of surfaces shared by vpp input and decode output nSurfNum = Request.NumFrameSuggested + VppRequest[0].NumFrameSuggested - m_mfxVideoParams.AsyncDepth + 1; // The number of surfaces for vpp output // Need to add one more surface in render mode if AsyncDepth == 1 nVppSurfNum = VppRequest[1].NumFrameSuggested + (m_eWorkMode == MODE_RENDERING ? m_mfxVideoParams.AsyncDepth == 1 : 0); // prepare allocation request Request.NumFrameSuggested = Request.NumFrameMin = nSurfNum; // surfaces are shared between vpp input and decode output Request.Type |= MFX_MEMTYPE_EXTERNAL_FRAME | MFX_MEMTYPE_FROM_DECODE | MFX_MEMTYPE_FROM_VPPIN; } if ((Request.NumFrameSuggested < m_mfxVideoParams.AsyncDepth) && (m_impl & MFX_IMPL_HARDWARE_ANY)) return MFX_ERR_MEMORY_ALLOC; Request.Type |= (m_bDecOutSysmem) ? MFX_MEMTYPE_SYSTEM_MEMORY : MFX_MEMTYPE_VIDEO_MEMORY_DECODER_TARGET; #ifdef LIBVA_SUPPORT if (!m_bVppIsUsed && (m_export_mode != vaapiAllocatorParams::DONOT_EXPORT)) { Request.Type |= MFX_MEMTYPE_EXPORT_FRAME; } #endif // alloc frames for decoder sts = m_pGeneralAllocator->Alloc(m_pGeneralAllocator->pthis, &Request, &m_mfxResponse); MSDK_CHECK_STATUS(sts, "m_pGeneralAllocator->Alloc failed"); if (m_bVppIsUsed) { // alloc frames for VPP #ifdef LIBVA_SUPPORT if (m_export_mode != vaapiAllocatorParams::DONOT_EXPORT) { VppRequest[1].Type |= MFX_MEMTYPE_EXPORT_FRAME; } #endif VppRequest[1].NumFrameSuggested = VppRequest[1].NumFrameMin = nVppSurfNum; MSDK_MEMCPY_VAR(VppRequest[1].Info, &(m_mfxVppVideoParams.vpp.Out), sizeof(mfxFrameInfo)); sts = m_pGeneralAllocator->Alloc(m_pGeneralAllocator->pthis, &VppRequest[1], &m_mfxVppResponse); MSDK_CHECK_STATUS(sts, "m_pGeneralAllocator->Alloc failed"); // prepare mfxFrameSurface1 array for decoder nVppSurfNum = m_mfxVppResponse.NumFrameActual; // AllocVppBuffers should call before AllocBuffers to set the value of m_OutputSurfacesNumber sts = AllocVppBuffers(nVppSurfNum); MSDK_CHECK_STATUS(sts, "AllocVppBuffers failed"); } // prepare mfxFrameSurface1 array for decoder nSurfNum = m_mfxResponse.NumFrameActual; sts = AllocBuffers(nSurfNum); MSDK_CHECK_STATUS(sts, "AllocBuffers failed"); for (int i = 0; i < nSurfNum; i++) { // initating each frame: MSDK_MEMCPY_VAR(m_pSurfaces[i].frame.Info, &(Request.Info), sizeof(mfxFrameInfo)); m_pSurfaces[i].frame.Data.MemType = Request.Type; if (m_bExternalAlloc) { m_pSurfaces[i].frame.Data.MemId = m_mfxResponse.mids[i]; if (m_bVppFullColorRange) { m_pSurfaces[i].frame.Data.ExtParam = &m_VppSurfaceExtParams[0]; m_pSurfaces[i].frame.Data.NumExtParam = (mfxU16)m_VppSurfaceExtParams.size(); } } } // prepare mfxFrameSurface1 array for VPP for (int i = 0; i < nVppSurfNum; i++) { MSDK_MEMCPY_VAR(m_pVppSurfaces[i].frame.Info, &(VppRequest[1].Info), sizeof(mfxFrameInfo)); if (m_bExternalAlloc) { m_pVppSurfaces[i].frame.Data.MemId = m_mfxVppResponse.mids[i]; if (m_bVppFullColorRange) { m_pVppSurfaces[i].frame.Data.ExtParam = &m_VppSurfaceExtParams[0]; m_pVppSurfaces[i].frame.Data.NumExtParam = (mfxU16)m_VppSurfaceExtParams.size(); } } } return MFX_ERR_NONE; } mfxStatus CDecodingPipeline::ReallocCurrentSurface(const mfxFrameInfo& info) { mfxStatus sts = MFX_ERR_NONE; mfxMemId inMid = nullptr; mfxMemId outMid = nullptr; if (!m_pGeneralAllocator) return MFX_ERR_MEMORY_ALLOC; m_pCurrentFreeSurface->frame.Info.CropW = info.CropW; m_pCurrentFreeSurface->frame.Info.CropH = info.CropH; m_mfxVideoParams.mfx.FrameInfo.Width = MSDK_ALIGN16(std::max(info.Width, m_mfxVideoParams.mfx.FrameInfo.Width)); m_mfxVideoParams.mfx.FrameInfo.Height = MSDK_ALIGN16(std::max(info.Height, m_mfxVideoParams.mfx.FrameInfo.Height)); m_pCurrentFreeSurface->frame.Info.Width = m_mfxVideoParams.mfx.FrameInfo.Width; m_pCurrentFreeSurface->frame.Info.Height = m_mfxVideoParams.mfx.FrameInfo.Height; inMid = m_pCurrentFreeSurface->frame.Data.MemId; sts = m_pGeneralAllocator->ReallocFrame(inMid, &m_pCurrentFreeSurface->frame.Info, m_pCurrentFreeSurface->frame.Data.MemType, &outMid); if (MFX_ERR_NONE == sts) m_pCurrentFreeSurface->frame.Data.MemId = outMid; return sts; } mfxStatus CDecodingPipeline::CreateAllocator() { mfxStatus sts = MFX_ERR_NONE; m_pGeneralAllocator = new GeneralAllocator(); if (m_memType != SYSTEM_MEMORY || !m_bDecOutSysmem) { #if D3D_SURFACES_SUPPORT mfxHDL hdl = NULL; mfxHandleType hdl_t = #if MFX_D3D11_SUPPORT D3D11_MEMORY == m_memType ? MFX_HANDLE_D3D11_DEVICE : #endif // #if MFX_D3D11_SUPPORT MFX_HANDLE_D3D9_DEVICE_MANAGER; sts = m_hwdev->GetHandle(hdl_t, &hdl); MSDK_CHECK_STATUS(sts, "m_hwdev->GetHandle failed"); // create D3D allocator #if MFX_D3D11_SUPPORT if (D3D11_MEMORY == m_memType) { D3D11AllocatorParams* pd3dAllocParams = new D3D11AllocatorParams; MSDK_CHECK_POINTER(pd3dAllocParams, MFX_ERR_MEMORY_ALLOC); pd3dAllocParams->pDevice = reinterpret_cast(hdl); m_pmfxAllocatorParams = pd3dAllocParams; } else #endif // #if MFX_D3D11_SUPPORT { D3DAllocatorParams* pd3dAllocParams = new D3DAllocatorParams; MSDK_CHECK_POINTER(pd3dAllocParams, MFX_ERR_MEMORY_ALLOC); pd3dAllocParams->pManager = reinterpret_cast(hdl); m_pmfxAllocatorParams = pd3dAllocParams; } /* In case of video memory we must provide MediaSDK with external allocator thus we demonstrate "external allocator" usage model. Call SetAllocator to pass allocator to mediasdk */ sts = m_mfxSession.SetFrameAllocator(m_pGeneralAllocator); MSDK_CHECK_STATUS(sts, "m_mfxSession.SetFrameAllocator failed"); m_bExternalAlloc = true; #elif LIBVA_SUPPORT VADisplay va_dpy = NULL; sts = m_hwdev->GetHandle(MFX_HANDLE_VA_DISPLAY, (mfxHDL*)&va_dpy); MSDK_CHECK_STATUS(sts, "m_hwdev->GetHandle failed"); vaapiAllocatorParams* p_vaapiAllocParams = new vaapiAllocatorParams; MSDK_CHECK_POINTER(p_vaapiAllocParams, MFX_ERR_MEMORY_ALLOC); p_vaapiAllocParams->m_dpy = va_dpy; if (m_eWorkMode == MODE_RENDERING) { if (m_libvaBackend == MFX_LIBVA_DRM_MODESET) { #if defined(LIBVA_DRM_SUPPORT) CVAAPIDeviceDRM* drmdev = dynamic_cast(m_hwdev); p_vaapiAllocParams->m_export_mode = vaapiAllocatorParams::CUSTOM_FLINK; p_vaapiAllocParams->m_exporter = dynamic_cast(drmdev->getRenderer()); #endif } else if (m_libvaBackend == MFX_LIBVA_WAYLAND || m_libvaBackend == MFX_LIBVA_X11) { p_vaapiAllocParams->m_export_mode = vaapiAllocatorParams::PRIME; } } m_export_mode = p_vaapiAllocParams->m_export_mode; m_pmfxAllocatorParams = p_vaapiAllocParams; /* In case of video memory we must provide MediaSDK with external allocator thus we demonstrate "external allocator" usage model. Call SetAllocator to pass allocator to mediasdk */ sts = m_mfxSession.SetFrameAllocator(m_pGeneralAllocator); MSDK_CHECK_STATUS(sts, "m_mfxSession.SetFrameAllocator failed"); m_bExternalAlloc = true; #endif } else { sts = m_mfxSession.SetFrameAllocator(m_pGeneralAllocator); MSDK_CHECK_STATUS(sts, "m_mfxSession.SetFrameAllocator failed"); m_bExternalAlloc = true; } // initialize memory allocator sts = m_pGeneralAllocator->Init(m_pmfxAllocatorParams); MSDK_CHECK_STATUS(sts, "m_pGeneralAllocator->Init failed"); return MFX_ERR_NONE; } void CDecodingPipeline::DeleteFrames() { FreeBuffers(); m_pCurrentFreeSurface = NULL; MSDK_SAFE_FREE(m_pCurrentFreeOutputSurface); m_pCurrentFreeVppSurface = NULL; // delete frames if (m_pGeneralAllocator) { m_pGeneralAllocator->Free(m_pGeneralAllocator->pthis, &m_mfxResponse); } return; } void CDecodingPipeline::DeleteAllocator() { // delete allocator MSDK_SAFE_DELETE(m_pGeneralAllocator); MSDK_SAFE_DELETE(m_pmfxAllocatorParams); MSDK_SAFE_DELETE(m_hwdev); } void CDecodingPipeline::SetMultiView() { m_FileWriter.SetMultiView(); m_bIsMVC = true; } mfxStatus CDecodingPipeline::AllocateExtMVCBuffers() { mfxU32 i; auto mvcBuffer = m_mfxVideoParams.GetExtBuffer(); MSDK_CHECK_POINTER(mvcBuffer, MFX_ERR_MEMORY_ALLOC); mvcBuffer->View = new mfxMVCViewDependency[mvcBuffer->NumView]; MSDK_CHECK_POINTER(mvcBuffer->View, MFX_ERR_MEMORY_ALLOC); for (i = 0; i < mvcBuffer->NumView; ++i) { MSDK_ZERO_MEMORY(mvcBuffer->View[i]); } mvcBuffer->NumViewAlloc = mvcBuffer->NumView; mvcBuffer->ViewId = new mfxU16[mvcBuffer->NumViewId]; MSDK_CHECK_POINTER(mvcBuffer->ViewId, MFX_ERR_MEMORY_ALLOC); for (i = 0; i < mvcBuffer->NumViewId; ++i) { MSDK_ZERO_MEMORY(mvcBuffer->ViewId[i]); } mvcBuffer->NumViewIdAlloc = mvcBuffer->NumViewId; mvcBuffer->OP = new mfxMVCOperationPoint[mvcBuffer->NumOP]; MSDK_CHECK_POINTER(mvcBuffer->OP, MFX_ERR_MEMORY_ALLOC); for (i = 0; i < mvcBuffer->NumOP; ++i) { MSDK_ZERO_MEMORY(mvcBuffer->OP[i]); } mvcBuffer->NumOPAlloc = mvcBuffer->NumOP; return MFX_ERR_NONE; } void CDecodingPipeline::DeallocateExtMVCBuffers() { auto mvcBuffer = m_mfxVideoParams.GetExtBuffer(); if (mvcBuffer) { MSDK_SAFE_DELETE_ARRAY(mvcBuffer->View); MSDK_SAFE_DELETE_ARRAY(mvcBuffer->ViewId); MSDK_SAFE_DELETE_ARRAY(mvcBuffer->OP); } } mfxStatus CDecodingPipeline::ResetDecoder(sInputParams* pParams) { mfxStatus sts = MFX_ERR_NONE; // close decoder sts = m_pmfxDEC->Close(); MSDK_IGNORE_MFX_STS(sts, MFX_ERR_NOT_INITIALIZED); MSDK_CHECK_STATUS(sts, "m_pmfxDEC->Close failed"); // close VPP if (m_pmfxVPP) { sts = m_pmfxVPP->Close(); MSDK_IGNORE_MFX_STS(sts, MFX_ERR_NOT_INITIALIZED); MSDK_CHECK_STATUS(sts, "m_pmfxVPP->Close failed"); } // free allocated frames DeleteFrames(); // initialize parameters with values from parsed header sts = InitMfxParams(pParams); MSDK_CHECK_STATUS(sts, "InitMfxParams failed"); // in case of HW accelerated decode frames must be allocated prior to decoder initialization sts = AllocFrames(); MSDK_CHECK_STATUS(sts, "AllocFrames failed"); // init decoder sts = m_pmfxDEC->Init(&m_mfxVideoParams); if (MFX_WRN_PARTIAL_ACCELERATION == sts) { msdk_printf(MSDK_STRING("WARNING: partial acceleration\n")); MSDK_IGNORE_MFX_STS(sts, MFX_WRN_PARTIAL_ACCELERATION); } MSDK_CHECK_STATUS(sts, "m_pmfxDEC->Init failed"); if (m_pmfxVPP) { if (m_diMode) m_mfxVppVideoParams.vpp.Out.PicStruct = MFX_PICSTRUCT_PROGRESSIVE; if (pParams->ScalingMode) { auto par = m_mfxVppVideoParams.AddExtBuffer(); par->ScalingMode = pParams->ScalingMode; } sts = m_pmfxVPP->Init(&m_mfxVppVideoParams); if (MFX_WRN_PARTIAL_ACCELERATION == sts) { msdk_printf(MSDK_STRING("WARNING: partial acceleration\n")); MSDK_IGNORE_MFX_STS(sts, MFX_WRN_PARTIAL_ACCELERATION); } MSDK_CHECK_STATUS(sts, "m_pmfxVPP->Init failed"); } return MFX_ERR_NONE; } mfxStatus CDecodingPipeline::DeliverOutput(mfxFrameSurface1* frame) { CAutoTimer timer_fwrite(m_tick_fwrite); mfxStatus res = MFX_ERR_NONE, sts = MFX_ERR_NONE; if (!frame) { return MFX_ERR_NULL_PTR; } if (m_bResetFileWriter) { sts = m_FileWriter.Reset(); MSDK_CHECK_STATUS(sts, ""); m_bResetFileWriter = false; } if (m_bExternalAlloc) { if (m_eWorkMode == MODE_FILE_DUMP) { res = m_pGeneralAllocator->Lock(m_pGeneralAllocator->pthis, frame->Data.MemId, &(frame->Data)); if (MFX_ERR_NONE == res) { res = m_bOutI420 ? m_FileWriter.WriteNextFrameI420(frame) : m_FileWriter.WriteNextFrame(frame); sts = m_pGeneralAllocator->Unlock(m_pGeneralAllocator->pthis, frame->Data.MemId, &(frame->Data)); } if ((MFX_ERR_NONE == res) && (MFX_ERR_NONE != sts)) { res = sts; } } else if (m_eWorkMode == MODE_RENDERING) { #if D3D_SURFACES_SUPPORT res = m_d3dRender.RenderFrame(frame, m_pGeneralAllocator); #elif LIBVA_SUPPORT res = m_hwdev->RenderFrame(frame, m_pGeneralAllocator); #endif } } else { res = m_bOutI420 ? m_FileWriter.WriteNextFrameI420(frame) : m_FileWriter.WriteNextFrame(frame); } m_fpsLimiter.Work(); return res; } void CDecodingPipeline::DeliverLoop(void) { while (!m_bStopDeliverLoop) { m_pDeliverOutputSemaphore->Wait(); if (m_bStopDeliverLoop) { continue; } if (MFX_ERR_NONE != m_error) { continue; } msdkOutputSurface* pCurrentDeliveredSurface = m_DeliveredSurfacesPool.GetSurface(); if (!pCurrentDeliveredSurface) { m_error = MFX_ERR_NULL_PTR; continue; } mfxFrameSurface1* frame = &(pCurrentDeliveredSurface->surface->frame); m_error = DeliverOutput(frame); ReturnSurfaceToBuffers(pCurrentDeliveredSurface); pCurrentDeliveredSurface = NULL; msdk_atomic_inc32(&m_output_count); m_pDeliveredEvent->Signal(); } return; } void CDecodingPipeline::PrintPerFrameStat(bool force) { #define MY_COUNT 1 // TODO: this will be cmd option #define MY_THRESHOLD 10000.0 if ((!(m_output_count % MY_COUNT) && (m_eWorkMode != MODE_PERFORMANCE)) || force) { double fps, fps_fread, fps_fwrite; m_timer_overall.Sync(); fps = (m_tick_overall) ? m_output_count / CTimer::ConvertToSeconds(m_tick_overall) : 0.0; fps_fread = (m_tick_fread) ? m_output_count / CTimer::ConvertToSeconds(m_tick_fread) : 0.0; fps_fwrite = (m_tick_fwrite) ? m_output_count / CTimer::ConvertToSeconds(m_tick_fwrite) : 0.0; // decoding progress msdk_printf( MSDK_STRING("Frame number: %4d, fps: %0.3f, fread_fps: %0.3f, fwrite_fps: %.3f\r"), (int)m_output_count, fps, (fps_fread < MY_THRESHOLD) ? fps_fread : 0.0, (fps_fwrite < MY_THRESHOLD) ? fps_fwrite : 0.0); fflush(NULL); #if D3D_SURFACES_SUPPORT m_d3dRender.UpdateTitle(fps); #elif LIBVA_SUPPORT if (m_hwdev) m_hwdev->UpdateTitle(fps); #endif } } mfxStatus CDecodingPipeline::SyncOutputSurface(mfxU32 wait) { if (!m_pCurrentOutputSurface) { m_pCurrentOutputSurface = m_OutputSurfacesPool.GetSurface(); } if (!m_pCurrentOutputSurface) { return MFX_ERR_MORE_DATA; } mfxStatus sts = m_mfxSession.SyncOperation(m_pCurrentOutputSurface->syncp, wait); if (MFX_ERR_GPU_HANG == sts && m_bSoftRobustFlag) { msdk_printf(MSDK_STRING("GPU hang happened\n")); // Output surface can be corrupted // But should be delivered to output anyway sts = MFX_ERR_NONE; } if (MFX_WRN_IN_EXECUTION == sts) { return sts; } if (MFX_ERR_NONE == sts) { // we got completely decoded frame - pushing it to the delivering thread... ++m_synced_count; if (m_bPrintLatency) { m_vLatency.push_back(m_timer_overall.Sync() - m_pCurrentOutputSurface->surface->submit); } else { PrintPerFrameStat(); } if (m_eWorkMode == MODE_PERFORMANCE) { m_output_count = m_synced_count; m_fpsLimiter.Work(); ReturnSurfaceToBuffers(m_pCurrentOutputSurface); } else if (m_eWorkMode == MODE_FILE_DUMP) { sts = DeliverOutput(&(m_pCurrentOutputSurface->surface->frame)); if (MFX_ERR_NONE != sts) { sts = MFX_ERR_UNKNOWN; } else { m_output_count = m_synced_count; } ReturnSurfaceToBuffers(m_pCurrentOutputSurface); } else if (m_eWorkMode == MODE_RENDERING) { m_DeliveredSurfacesPool.AddSurface(m_pCurrentOutputSurface); m_pDeliveredEvent->Reset(); m_pDeliverOutputSemaphore->Post(); } m_pCurrentOutputSurface = NULL; } return sts; } mfxStatus CDecodingPipeline::RunDecoding() { mfxFrameSurface1* pOutSurface = NULL; mfxBitstream* pBitstream = &m_mfxBS; mfxStatus sts = MFX_ERR_NONE; bool bErrIncompatibleVideoParams = false; CTimeInterval<> decodeTimer(m_bIsCompleteFrame); time_t start_time = time(0); std::thread deliverThread; if (m_eWorkMode == MODE_RENDERING) { m_pDeliverOutputSemaphore = new MSDKSemaphore(sts); m_pDeliveredEvent = new MSDKEvent(sts, false, false); deliverThread = std::thread(&CDecodingPipeline::DeliverLoop, this); } while (((sts == MFX_ERR_NONE) || (MFX_ERR_MORE_DATA == sts) || (MFX_ERR_MORE_SURFACE == sts)) && (m_nFrames > m_output_count)) { if (MFX_ERR_NONE != m_error) { msdk_printf(MSDK_STRING("DeliverOutput return error = %d\n"), (int)m_error); break; } if (pBitstream && ((MFX_ERR_MORE_DATA == sts) || (m_bIsCompleteFrame && !pBitstream->DataLength))) { CAutoTimer timer_fread(m_tick_fread); sts = m_FileReader->ReadNextFrame(pBitstream); // read more data to input bit stream if (MFX_ERR_MORE_DATA == sts) { sts = MFX_ERR_NONE; // Timeout has expired or videowall mode m_timer_overall.Sync(); if (((CTimer::ConvertToSeconds(m_tick_overall) < m_nTimeout) && m_nTimeout) || m_bIsVideoWall) { m_FileReader->Reset(); m_bResetFileWriter = true; // Reset bitstream state pBitstream->DataFlag = 0; continue; } // we almost reached end of stream, need to pull buffered data now pBitstream = NULL; } } if ((MFX_ERR_NONE == sts) || (MFX_ERR_MORE_DATA == sts) || (MFX_ERR_MORE_SURFACE == sts)) { // here we check whether output is ready, though we do not wait... #ifndef __SYNC_WA mfxStatus _sts = SyncOutputSurface(0); if (MFX_ERR_UNKNOWN == _sts) { sts = _sts; break; } else if (MFX_ERR_NONE == _sts) { continue; } #endif } else { MSDK_CHECK_STATUS_NO_RET(sts, "ReadNextFrame failed"); } if ((MFX_ERR_NONE == sts) || (MFX_ERR_MORE_DATA == sts) || (MFX_ERR_MORE_SURFACE == sts)) { SyncFrameSurfaces(); SyncVppFrameSurfaces(); if (!m_pCurrentFreeSurface) { m_pCurrentFreeSurface = m_FreeSurfacesPool.GetSurface(); } if (!m_pCurrentFreeVppSurface) { m_pCurrentFreeVppSurface = m_FreeVppSurfacesPool.GetSurface(); } #ifndef __SYNC_WA if (!m_pCurrentFreeSurface || !m_pCurrentFreeVppSurface) { #else if (!m_pCurrentFreeSurface || (!m_pCurrentFreeVppSurface && m_bVppIsUsed) || (m_OutputSurfacesPool.GetSurfaceCount() == m_mfxVideoParams.AsyncDepth)) { #endif // we stuck with no free surface available, now we will sync... sts = SyncOutputSurface(MSDK_DEC_WAIT_INTERVAL); if (MFX_ERR_MORE_DATA == sts) { if ((m_eWorkMode == MODE_PERFORMANCE) || (m_eWorkMode == MODE_FILE_DUMP)) { sts = MFX_ERR_NOT_FOUND; } else if (m_eWorkMode == MODE_RENDERING) { if (m_synced_count != m_output_count) { sts = m_pDeliveredEvent->TimedWait(MSDK_DEC_WAIT_INTERVAL); } else { sts = MFX_ERR_NOT_FOUND; } } if (MFX_ERR_NOT_FOUND == sts) { msdk_printf( MSDK_STRING("fatal: failed to find output surface, that's a bug!\n")); break; } } MSDK_CHECK_ERR_NONE_STATUS_NO_RET(sts, "SyncOperation fail or timeout"); // note: MFX_WRN_IN_EXECUTION will also be treated as an error at this point continue; } if (!m_pCurrentFreeOutputSurface) { m_pCurrentFreeOutputSurface = GetFreeOutputSurface(); } if (!m_pCurrentFreeOutputSurface) { sts = MFX_ERR_NOT_FOUND; break; } } // exit by timeout if ((MFX_ERR_NONE == sts) && m_bIsVideoWall && (time(0) - start_time) >= m_nTimeout) { sts = MFX_ERR_NONE; break; } if ((MFX_ERR_NONE == sts) || (MFX_ERR_MORE_DATA == sts) || (MFX_ERR_MORE_SURFACE == sts)) { if (m_bIsCompleteFrame) { m_pCurrentFreeSurface->submit = m_timer_overall.Sync(); } pOutSurface = NULL; do { mfxExtDecodeErrorReport* errorReport = nullptr; if (pBitstream) { errorReport = (mfxExtDecodeErrorReport*)GetExtBuffer(pBitstream->ExtParam, pBitstream->NumExtParam, MFX_EXTBUFF_DECODE_ERROR_REPORT); } //Obtain HDR metadata only P010(10-bit) and w/o VPP. if (m_mfxVideoParams.mfx.FrameInfo.FourCC == MFX_FOURCC_P010 && !m_bVppIsUsed) { m_OutSurfaceExtParams.clear(); m_DisplayColor.Header.BufferId = MFX_EXTBUFF_MASTERING_DISPLAY_COLOUR_VOLUME; m_ContentLight.Header.BufferId = MFX_EXTBUFF_CONTENT_LIGHT_LEVEL_INFO; m_OutSurfaceExtParams.push_back((mfxExtBuffer*)&m_DisplayColor); m_OutSurfaceExtParams.push_back((mfxExtBuffer*)&m_ContentLight); m_pCurrentFreeSurface->frame.Data.ExtParam = reinterpret_cast(&m_OutSurfaceExtParams[0]); m_pCurrentFreeSurface->frame.Data.NumExtParam = static_cast(m_OutSurfaceExtParams.size()); } sts = m_pmfxDEC->DecodeFrameAsync(pBitstream, &(m_pCurrentFreeSurface->frame), &pOutSurface, &(m_pCurrentFreeOutputSurface->syncp)); PrintDecodeErrorReport(errorReport); if (pBitstream && MFX_ERR_MORE_DATA == sts && pBitstream->MaxLength == pBitstream->DataLength) { m_mfxBS.Extend(pBitstream->MaxLength * 2); } if (MFX_WRN_DEVICE_BUSY == sts) { if (m_bIsCompleteFrame) { //in low latency mode device busy leads to increasing of latency //msdk_printf(MSDK_STRING("Warning : latency increased due to MFX_WRN_DEVICE_BUSY\n")); } mfxStatus _sts = SyncOutputSurface(MSDK_DEC_WAIT_INTERVAL); // note: everything except MFX_ERR_NONE are errors at this point if (MFX_ERR_NONE == _sts) { sts = MFX_WRN_DEVICE_BUSY; } else { sts = _sts; if (MFX_ERR_MORE_DATA == sts) { // we can't receive MFX_ERR_MORE_DATA and have no output - that's a bug sts = MFX_WRN_DEVICE_BUSY; //MFX_ERR_NOT_FOUND; } } } } while (MFX_WRN_DEVICE_BUSY == sts); if (sts > MFX_ERR_NONE) { // ignoring warnings... if (m_pCurrentFreeOutputSurface->syncp) { MSDK_SELF_CHECK(pOutSurface); // output is available sts = MFX_ERR_NONE; } else { // output is not available sts = MFX_ERR_MORE_SURFACE; } } else if ((MFX_ERR_MORE_DATA == sts) && pBitstream) { if (m_bIsCompleteFrame && pBitstream->DataLength) { // In low_latency mode decoder have to process bitstream completely msdk_printf(MSDK_STRING( "error: Incorrect decoder behavior in low latency mode (bitstream length is not equal to 0 after decoding)\n")); sts = MFX_ERR_UNDEFINED_BEHAVIOR; continue; } } else if ((MFX_ERR_MORE_DATA == sts) && !pBitstream) { // that's it - we reached end of stream; now we need to render bufferred data... do { sts = SyncOutputSurface(MSDK_DEC_WAIT_INTERVAL); } while (MFX_ERR_NONE == sts); MSDK_IGNORE_MFX_STS(sts, MFX_ERR_MORE_DATA); if (sts) MSDK_PRINT_WRN_MSG(sts, "SyncOutputSurface failed") while (m_synced_count != m_output_count) { m_pDeliveredEvent->Wait(); } break; } else if (MFX_ERR_INCOMPATIBLE_VIDEO_PARAM == sts) { bErrIncompatibleVideoParams = true; // need to go to the buffering loop prior to reset procedure pBitstream = NULL; sts = MFX_ERR_NONE; continue; } else if (MFX_ERR_REALLOC_SURFACE == sts) { mfxVideoParam param{}; sts = m_pmfxDEC->GetVideoParam(¶m); if (MFX_ERR_NONE != sts) { // need to go to the buffering loop prior to reset procedure pBitstream = NULL; sts = MFX_ERR_NONE; continue; } sts = ReallocCurrentSurface(param.mfx.FrameInfo); if (MFX_ERR_NONE != sts) { // need to go to the buffering loop prior to reset procedure pBitstream = NULL; sts = MFX_ERR_NONE; } continue; } } if ((MFX_ERR_NONE == sts) || (MFX_ERR_MORE_DATA == sts) || (MFX_ERR_MORE_SURFACE == sts)) { // if current free surface is locked we are moving it to the used surfaces array /*if (m_pCurrentFreeSurface->frame.Data.Locked)*/ { m_UsedSurfacesPool.AddSurface(m_pCurrentFreeSurface); m_pCurrentFreeSurface = NULL; } } else { MSDK_CHECK_STATUS_NO_RET(sts, "DecodeFrameAsync returned error status"); } if (MFX_ERR_NONE == sts) { if (m_bVppIsUsed) { if (m_pCurrentFreeVppSurface) { do { if ((m_pCurrentFreeVppSurface->frame.Info.CropW == 0) || (m_pCurrentFreeVppSurface->frame.Info.CropH == 0)) { m_pCurrentFreeVppSurface->frame.Info.CropW = pOutSurface->Info.CropW; m_pCurrentFreeVppSurface->frame.Info.CropH = pOutSurface->Info.CropH; m_pCurrentFreeVppSurface->frame.Info.CropX = pOutSurface->Info.CropX; m_pCurrentFreeVppSurface->frame.Info.CropY = pOutSurface->Info.CropY; } if (pOutSurface->Info.PicStruct != m_pCurrentFreeVppSurface->frame.Info.PicStruct) { m_pCurrentFreeVppSurface->frame.Info.PicStruct = pOutSurface->Info.PicStruct; } if ((pOutSurface->Info.PicStruct == 0) && (m_pCurrentFreeVppSurface->frame.Info.PicStruct == 0)) { m_pCurrentFreeVppSurface->frame.Info.PicStruct = pOutSurface->Info.PicStruct = MFX_PICSTRUCT_PROGRESSIVE; } if (m_diMode) m_pCurrentFreeVppSurface->frame.Info.PicStruct = MFX_PICSTRUCT_PROGRESSIVE; // WA: RunFrameVPPAsync doesn't copy ViewId from input to output m_pCurrentFreeVppSurface->frame.Info.FrameId.ViewId = pOutSurface->Info.FrameId.ViewId; sts = m_pmfxVPP->RunFrameVPPAsync(pOutSurface, &(m_pCurrentFreeVppSurface->frame), NULL, &(m_pCurrentFreeOutputSurface->syncp)); if (MFX_WRN_DEVICE_BUSY == sts) { MSDK_SLEEP( 1); // just wait and then repeat the same call to RunFrameVPPAsync } } while (MFX_WRN_DEVICE_BUSY == sts); // process errors if (MFX_ERR_MORE_DATA == sts) { // will never happen actually continue; } else if (MFX_ERR_NONE != sts) { MSDK_PRINT_RET_MSG(sts, "RunFrameVPPAsync failed"); break; } m_UsedVppSurfacesPool.AddSurface(m_pCurrentFreeVppSurface); msdk_atomic_inc16(&(m_pCurrentFreeVppSurface->render_lock)); m_pCurrentFreeOutputSurface->surface = m_pCurrentFreeVppSurface; m_OutputSurfacesPool.AddSurface(m_pCurrentFreeOutputSurface); m_pCurrentFreeOutputSurface = NULL; m_pCurrentFreeVppSurface = NULL; } } else { msdkFrameSurface* surface = FindUsedSurface(pOutSurface); msdk_atomic_inc16(&(surface->render_lock)); m_pCurrentFreeOutputSurface->surface = surface; m_OutputSurfacesPool.AddSurface(m_pCurrentFreeOutputSurface); m_pCurrentFreeOutputSurface = NULL; } } } //while processing if (m_nFrames == m_output_count) { if (sts != MFX_ERR_NONE) msdk_printf( MSDK_STRING( "[WARNING] Decoder returned error %s that could be compensated during next iterations of decoding process.\ But requested amount of frames is already successfully decoded, so whole process is finished successfully."), StatusToString(sts).c_str()); sts = MFX_ERR_NONE; } PrintPerFrameStat(true); if (m_bPrintLatency && m_vLatency.size() > 0) { unsigned int frame_idx = 0; msdk_tick sum = 0; for (std::vector::iterator it = m_vLatency.begin(); it != m_vLatency.end(); ++it) { sum += *it; msdk_printf(MSDK_STRING("Frame %4d, latency=%5.5f ms\n"), ++frame_idx, (double)(CTimer::ConvertToSeconds(*it) * 1000)); } msdk_printf(MSDK_STRING("\nLatency summary:\n")); msdk_printf( MSDK_STRING("\nAVG=%5.5f ms, MAX=%5.5f ms, MIN=%5.5f ms"), (double)CTimer::ConvertToSeconds((msdk_tick)((mfxF64)sum / m_vLatency.size())) * 1000, (double)CTimer::ConvertToSeconds( *std::max_element(m_vLatency.begin(), m_vLatency.end())) * 1000, (double)CTimer::ConvertToSeconds( *std::min_element(m_vLatency.begin(), m_vLatency.end())) * 1000); } if (m_eWorkMode == MODE_RENDERING) { m_bStopDeliverLoop = true; m_pDeliverOutputSemaphore->Post(); if (deliverThread.joinable()) deliverThread.join(); } MSDK_SAFE_DELETE(m_pDeliverOutputSemaphore); MSDK_SAFE_DELETE(m_pDeliveredEvent); // exit in case of other errors MSDK_CHECK_STATUS(sts, "Unexpected error!!"); // if we exited main decoding loop with ERR_INCOMPATIBLE_PARAM we need to send this status to caller if (bErrIncompatibleVideoParams) { sts = MFX_ERR_INCOMPATIBLE_VIDEO_PARAM; } //skudo msdk_printf(MSDK_STRING("\n[sk] call m_FileReader->Reset()\n")); m_FileReader->Reset(); return sts; // ERR_NONE or ERR_INCOMPATIBLE_VIDEO_PARAM } void CDecodingPipeline::PrintInfo() { msdk_printf(MSDK_STRING("Decoding Sample Version %s\n\n"), GetMSDKSampleVersion().c_str()); msdk_printf(MSDK_STRING("\nInput video\t%s\n"), CodecIdToStr(m_mfxVideoParams.mfx.CodecId).c_str()); if (m_bVppIsUsed) { msdk_printf(MSDK_STRING("Output format\t%s (using vpp)\n"), m_bOutI420 ? MSDK_STRING("I420(YUV)") : CodecIdToStr(m_mfxVppVideoParams.vpp.Out.FourCC).c_str()); } else { msdk_printf(MSDK_STRING("Output format\t%s\n"), m_bOutI420 ? MSDK_STRING("I420(YUV)") : CodecIdToStr(m_mfxVideoParams.mfx.FrameInfo.FourCC).c_str()); } mfxFrameInfo Info = m_mfxVideoParams.mfx.FrameInfo; msdk_printf(MSDK_STRING("Input:\n")); msdk_printf(MSDK_STRING(" Resolution\t%dx%d\n"), (int)Info.Width, (int)Info.Height); msdk_printf(MSDK_STRING(" Crop X,Y,W,H\t%d,%d,%d,%d\n"), (int)Info.CropX, (int)Info.CropY, (int)Info.CropW, (int)Info.CropH); msdk_printf(MSDK_STRING("Output:\n")); if (m_vppOutHeight && m_vppOutWidth) { msdk_printf(MSDK_STRING(" Resolution\t%hux%hu\n"), (short int)m_vppOutWidth, (short int)m_vppOutHeight); } else { msdk_printf(MSDK_STRING(" Resolution\t%dx%d\n"), Info.CropW ? (int)Info.CropW : (int)Info.Width, Info.CropH ? (int)Info.CropH : (int)Info.Height); } mfxF64 dFrameRate = CalculateFrameRate(Info.FrameRateExtN, Info.FrameRateExtD); msdk_printf(MSDK_STRING("Frame rate\t%.2f\n"), (double)dFrameRate); const msdk_char* sMemType = #if defined(_WIN32) || defined(_WIN64) m_memType == D3D9_MEMORY ? MSDK_STRING("d3d") #else m_memType == D3D9_MEMORY ? MSDK_STRING("vaapi") #endif : (m_memType == D3D11_MEMORY ? MSDK_STRING("d3d11") : MSDK_STRING("system")); msdk_printf(MSDK_STRING("Memory type\t\t%s\n"), sMemType); const msdk_char* sImpl = (MFX_IMPL_VIA_D3D11 == MFX_IMPL_VIA_MASK(m_impl)) ? MSDK_STRING("hw_d3d11") : (MFX_IMPL_SOFTWARE == MFX_IMPL_BASETYPE(m_impl)) ? MSDK_STRING("sw") : MSDK_STRING("hw"); msdk_printf(MSDK_STRING("MediaSDK impl\t\t%s\n"), sImpl); mfxVersion ver; m_mfxSession.QueryVersion(&ver); msdk_printf(MSDK_STRING("MediaSDK version\t%d.%d\n"), (int)ver.Major, (int)ver.Minor); msdk_printf(MSDK_STRING("\n")); return; }