/* * Copyright © Microsoft Corporation * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice (including the next * paragraph) shall be included in all copies or substantial portions of the * Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include "hmft_entrypoints.h" #include "wpptrace.h" #include "codecapi.tmh" /*************************************************************************** * Quality VBR utility functions. ***************************************************************************/ #define MIN_QP 16 #define MAX_QP 44 #define QUALITY2QP_RATIO ( (double) ( MIN_QP - MAX_QP ) / 99.0 ) #define QUALITY2QP_OFFSET ( (double) ( 100 * MAX_QP - MIN_QP ) / 99.0 ) // // MessageId: VFW_E_CODECAPI_LINEAR_RANGE // // MessageText: // // Parameter has linear range.%0 // #define VFW_E_CODECAPI_LINEAR_RANGE ( (HRESULT) 0x80040310L ) // // MessageId: VFW_E_CODECAPI_ENUMERATED // // MessageText: // // Parameter is enumerated. It has no range.%0 // #define VFW_E_CODECAPI_ENUMERATED ( (HRESULT) 0x80040311L ) // // MessageId: VFW_E_CODECAPI_NO_DEFAULT // // MessageText: // // No default value.%0 // #define VFW_E_CODECAPI_NO_DEFAULT ( (HRESULT) 0x80040313L ) // // MessageId: VFW_E_CODECAPI_NO_CURRENT_VALUE // // MessageText: // // No current value.%0 // #define VFW_E_CODECAPI_NO_CURRENT_VALUE ( (HRESULT) 0x80040314L ) // Compute AVC QP from a given CodecAPI quality setting. DWORD CalculateQPFromQuality( DWORD Quality ) { const double k = QUALITY2QP_RATIO; const double b = QUALITY2QP_OFFSET; DWORD QP = (DWORD) ( k * Quality + b + 0.5 ); if( QP < MIN_QP ) QP = MIN_QP; else if( QP > MAX_QP ) QP = MAX_QP; return QP; } // Compute the CodecAPI quality setting from a given QP. DWORD CalculateQualityFromQP( DWORD QP ) { const double k = QUALITY2QP_RATIO; const double b = QUALITY2QP_OFFSET; DWORD Quality = (DWORD) ( (double) ( QP - b ) / k + 0.5 ); if( Quality < 1 ) Quality = 1; else if( Quality > 100 ) Quality = 100; return Quality; } // Supported static const char * StringFromCodecAPI( const GUID *Api ) { if( !Api ) { return "NULL"; } else if( *Api == CODECAPI_AVEncCommonRateControlMode ) { return "CODECAPI_AVEncCommonRateControlMode"; } else if( *Api == CODECAPI_AVEncCommonQuality ) { return "CODECAPI_AVEncCommonQuality"; } else if( *Api == CODECAPI_AVEncCommonQualityVsSpeed ) { return "CODECAPI_AVEncCommonQualityVsSpeed"; } else if( *Api == CODECAPI_AVEncCommonMeanBitRate ) { return "CODECAPI_AVEncCommonMeanBitRate"; } else if( *Api == CODECAPI_AVEncCommonMaxBitRate ) { return "CODECAPI_AVEncCommonMaxBitRate"; } else if( *Api == CODECAPI_AVEncCommonBufferSize ) { return "CODECAPI_AVEncCommonBufferSize"; } else if( *Api == CODECAPI_AVEncCommonBufferInLevel ) { return "CODECAPI_AVEncCommonBufferInLevel"; } else if( *Api == CODECAPI_AVLowLatencyMode ) { return "CODECAPI_AVLowLatencyMode"; } else if( *Api == CODECAPI_AVEncH264CABACEnable ) { return "CODECAPI_AVEncH264CABACEnable"; } else if( *Api == CODECAPI_AVEncMPVGOPSize ) { return "CODECAPI_AVEncMPVGOPSize"; } else if( *Api == CODECAPI_AVEnableInLoopDeblockFilter ) { return "CODECAPI_AVEnableInLoopDeblockFilter"; } else if( *Api == CODECAPI_AVEncMPVDefaultBPictureCount ) { return "CODECAPI_AVEncMPVDefaultBPictureCount"; } else if( *Api == CODECAPI_AVEncVideoContentType ) { return "CODECAPI_AVEncVideoContentType"; } else if( *Api == CODECAPI_AVEncVideoEncodeQP ) { return "CODECAPI_AVEncVideoEncodeQP"; } else if( *Api == CODECAPI_AVEncVideoMinQP ) { return "CODECAPI_AVEncVideoMinQP"; } else if( *Api == CODECAPI_AVEncVideoForceKeyFrame ) { return "CODECAPI_AVEncVideoForceKeyFrame"; } else if( *Api == CODECAPI_AVEncH264SPSID ) { return "CODECAPI_AVEncH264SPSID"; } else if( *Api == CODECAPI_AVEncH264PPSID ) { return "CODECAPI_AVEncH264PPSID"; } else if( *Api == CODECAPI_AVEncVideoTemporalLayerCount ) { return "CODECAPI_AVEncVideoTemporalLayerCount"; } else if( *Api == CODECAPI_AVEncVideoSelectLayer ) { return "CODECAPI_AVEncVideoSelectLayer"; } else if( *Api == CODECAPI_AVEncVideoEncodeFrameTypeQP ) { return "CODECAPI_AVEncVideoEncodeFrameTypeQP"; } else if( *Api == CODECAPI_AVEncSliceControlMode ) { return "CODECAPI_AVEncSliceControlMode"; } else if( *Api == CODECAPI_AVEncSliceControlSize ) { return "CODECAPI_AVEncSliceControlSize"; } else if( *Api == CODECAPI_AVEncVideoMaxNumRefFrame ) { return "CODECAPI_AVEncVideoMaxNumRefFrame"; } else if( *Api == CODECAPI_AVEncVideoMeanAbsoluteDifference ) { return "CODECAPI_AVEncVideoMeanAbsoluteDifference"; } else if( *Api == CODECAPI_AVEncVideoMaxQP ) { return "CODECAPI_AVEncVideoMaxQP"; } else if( *Api == CODECAPI_AVEncVideoGradualIntraRefresh ) { return "CODECAPI_AVEncVideoGradualIntraRefresh"; } else if( *Api == CODECAPI_AVScenarioInfo ) { return "CODECAPI_AVScenarioInfo"; } else if( *Api == CODECAPI_AVEncVideoROIEnabled ) { return "CODECAPI_AVEncVideoROIEnabled"; } else if( *Api == CODECAPI_AVEncVideoLTRBufferControl ) { return "CODECAPI_AVEncVideoLTRBufferControl"; } else if( *Api == CODECAPI_AVEncVideoMarkLTRFrame ) { return "CODECAPI_AVEncVideoMarkLTRFrame"; } else if( *Api == CODECAPI_AVEncVideoUseLTRFrame ) { return "CODECAPI_AVEncVideoUseLTRFrame"; } else if( *Api == CODECAPI_AVEncVideoDirtyRectEnabled ) { return "CODECAPI_AVEncVideoDirtyRectEnabled"; } else if( *Api == CODECAPI_AVEncSliceGenerationMode ) { return "CODECAPI_AVEncSliceGenerationMode"; } return "Unknown CodecAPI"; } static std::string StringFromVariant( VARIANT *Value ) { if( !Value ) { return "NULL"; } else if( Value->vt == VT_UI4 ) { return std::to_string( Value->ulVal ); } else if( Value->vt = VT_UI8 ) { return std::to_string( Value->ullVal ); } else if( Value->vt == VT_BOOL ) { return std::to_string( Value->boolVal ); } return "Unsupported Variant"; } // ------------------------------------------------------------------------ // ICodecAPI public methods (listed in same order as hmft_entrypoints.h) // ------------------------------------------------------------------------ // ICodecAPI::IsSupported // https://learn.microsoft.com/en-us/windows/win32/api/strmif/nf-strmif-icodecapi-issupported HRESULT CDX12EncHMFT::IsSupported( const GUID *Api ) { HRESULT hr = E_NOTIMPL; CHECKNULL_GOTO( Api, E_POINTER, done ); if( *Api == CODECAPI_AVEncCommonRateControlMode || *Api == CODECAPI_AVEncCommonQuality || *Api == CODECAPI_AVEncCommonQualityVsSpeed || *Api == CODECAPI_AVEncCommonMeanBitRate || *Api == CODECAPI_AVEncCommonMaxBitRate || *Api == CODECAPI_AVEncCommonBufferSize || *Api == CODECAPI_AVEncCommonBufferInLevel || *Api == CODECAPI_AVLowLatencyMode || *Api == CODECAPI_AVEncH264CABACEnable || *Api == CODECAPI_AVEncMPVGOPSize || *Api == CODECAPI_AVEnableInLoopDeblockFilter || *Api == CODECAPI_AVEncMPVDefaultBPictureCount || *Api == CODECAPI_AVEncVideoContentType || *Api == CODECAPI_AVEncVideoEncodeQP || *Api == CODECAPI_AVEncVideoMinQP || *Api == CODECAPI_AVEncVideoForceKeyFrame || *Api == CODECAPI_AVEncH264SPSID || *Api == CODECAPI_AVEncH264PPSID || *Api == CODECAPI_AVEncVideoTemporalLayerCount || *Api == CODECAPI_AVEncVideoSelectLayer || *Api == CODECAPI_AVEncVideoEncodeFrameTypeQP || *Api == CODECAPI_AVEncSliceControlMode || *Api == CODECAPI_AVEncSliceControlSize || *Api == CODECAPI_AVEncVideoMaxNumRefFrame || *Api == CODECAPI_AVEncVideoMeanAbsoluteDifference || *Api == CODECAPI_AVEncVideoMaxQP || *Api == CODECAPI_AVEncVideoGradualIntraRefresh || *Api == CODECAPI_AVScenarioInfo || *Api == CODECAPI_AVEncVideoROIEnabled || *Api == CODECAPI_AVEncVideoLTRBufferControl || *Api == CODECAPI_AVEncVideoMarkLTRFrame || *Api == CODECAPI_AVEncVideoUseLTRFrame ) { hr = S_OK; return hr; } if( m_EncoderCapabilities.m_HWSupportDirtyRects.bits.supports_info_type_dirty ) { if( *Api == CODECAPI_AVEncVideoDirtyRectEnabled ) { hr = S_OK; return hr; } } if( m_EncoderCapabilities.m_HWSupportSlicedFences.bits.supported ) { if( *Api == CODECAPI_AVEncSliceGenerationMode ) { hr = S_OK; return hr; } } done: return hr; } // ICodecAPI::IsModifiable // https://learn.microsoft.com/en-us/windows/win32/api/strmif/nf-strmif-icodecapi-ismodifiable HRESULT CDX12EncHMFT::IsModifiable( const GUID *Api ) { return E_NOTIMPL; } // ICodecAPI::GetParameterRange // https://learn.microsoft.com/en-us/windows/win32/api/strmif/nf-strmif-icodecapi-getparameterrange HRESULT CDX12EncHMFT::GetParameterRange( const GUID *Api, VARIANT *ValueMin, VARIANT *ValueMax, VARIANT *SteppingDelta ) { if( !Api || !ValueMin || !ValueMax || !SteppingDelta ) return E_POINTER; if( *Api == CODECAPI_AVEncVideoTemporalLayerCount ) { ValueMin->vt = VT_UI4; ValueMin->ulVal = 1; ValueMax->vt = VT_UI4; ValueMax->ulVal = HMFT_MAX_TEMPORAL_LAYERS; SteppingDelta->vt = VT_UI4; SteppingDelta->ulVal = 1; return S_OK; } else if( *Api == CODECAPI_AVEncMPVDefaultBPictureCount ) { ValueMin->vt = VT_UI4; ValueMin->ulVal = 0; ValueMax->vt = VT_UI4; ValueMax->ulVal = HMFT_MAX_BFRAMES; SteppingDelta->vt = VT_UI4; SteppingDelta->ulVal = 1; return S_OK; } else if( *Api == CODECAPI_AVEncVideoEncodeFrameTypeQP ) { return E_NOTIMPL; } else if( *Api == CODECAPI_AVEncSliceControlMode ) { static_assert( PIPE_VIDEO_SLICE_MODE_BLOCKS == 0 ); static_assert( PIPE_VIDEO_SLICE_MODE_MAX_SLICE_SIZE == 1 ); static_assert( PIPE_VIDEO_SLICE_MODE_AUTO == 2 ); // If PIPE_VIDEO_SLICE_MODE_BLOCKS is not supported, return error. if( m_EncoderCapabilities.m_HWSupportedSliceModes.HasAll( PIPE_VIDEO_SLICE_MODE_BLOCKS, PIPE_VIDEO_SLICE_MODE_MAX_SLICE_SIZE, PIPE_VIDEO_SLICE_MODE_AUTO ) ) { ValueMin->vt = VT_UI4; ValueMin->ulVal = 0; ValueMax->vt = VT_UI4; ValueMax->ulVal = 2; SteppingDelta->vt = VT_UI4; SteppingDelta->ulVal = 1; } else if( m_EncoderCapabilities.m_HWSupportedSliceModes.HasAll( PIPE_VIDEO_SLICE_MODE_BLOCKS, PIPE_VIDEO_SLICE_MODE_MAX_SLICE_SIZE ) ) { ValueMin->vt = VT_UI4; ValueMin->ulVal = 0; ValueMax->vt = VT_UI4; ValueMax->ulVal = 1; SteppingDelta->vt = VT_UI4; SteppingDelta->ulVal = 1; } else if( m_EncoderCapabilities.m_HWSupportedSliceModes.HasAll( PIPE_VIDEO_SLICE_MODE_BLOCKS ) ) { ValueMin->vt = VT_UI4; ValueMin->ulVal = 0; ValueMax->vt = VT_UI4; ValueMax->ulVal = 0; SteppingDelta->vt = VT_UI4; SteppingDelta->ulVal = 0; } else { // Nothing is supported. return E_NOTIMPL; } return S_OK; } else if( *Api == CODECAPI_AVEncSliceControlSize ) { HRESULT hr = S_OK; // default is 0 to MAX_UINT which means that the range can not be determined. ValueMin->vt = VT_UI4; ValueMin->ulVal = 0; ValueMax->vt = VT_UI4; ValueMax->ulVal = 0xffffffff; SteppingDelta->vt = VT_UI4; SteppingDelta->ulVal = 1; switch( m_uiSliceControlMode ) { case SLICE_CONTROL_MODE_MB: if( m_spOutputType ) { // TODO%%% - this is assuming 16x16 macroblocks UINT32 uiMBPerRow = ( ( m_uiOutputWidth + 15 ) >> 4 ); UINT32 uiMBRows = ( ( m_uiOutputHeight + 15 ) >> 4 ); ValueMin->ulVal = ( (ULONG) ( uiMBPerRow * uiMBRows + m_EncoderCapabilities.m_uiMaxHWSupportedMaxSlices - 1 ) / m_EncoderCapabilities.m_uiMaxHWSupportedMaxSlices ); ValueMax->ulVal = (ULONG) ( uiMBPerRow * uiMBRows ); } break; case SLICE_CONTROL_MODE_BITS: // NOTE: DX12 Encode API doesn't support mode 1 - TODO%%% // For Bits per Slice mode we can only determine minimum number of bits ValueMin->ulVal = HMFT_MIN_BITS_PER_SLICE; ValueMax->ulVal = 0xffffffff; break; #if VIDEO_CODEC_H264ENC case SLICE_CONTROL_MODE_MB_ROW: if( m_spOutputType ) { UINT32 uiMBRows = ( ( m_uiOutputHeight + 15 ) >> 4 ); ValueMin->ulVal = 1; //((ULONG) (iMBRows + m_EncoderCapabilities.m_uiMaxHWSupportedMaxSlices - 1) / // m_EncoderCapabilities.m_uiMaxHWSupportedMaxSlices); ValueMax->ulVal = (ULONG) std::min( uiMBRows, ( m_uiOutputHeight / m_EncoderCapabilities.m_uiMaxHWSupportedMaxSlices ) >> 4 ); } break; #endif default: assert( FALSE ); hr = E_INVALIDARG; // this should be unreachable code break; } return hr; } else if( *Api == CODECAPI_AVEncVideoMaxNumRefFrame ) { ValueMin->vt = VT_UI4; ValueMin->ulVal = 1; ValueMax->vt = VT_UI4; ValueMax->ulVal = m_uiMaxNumRefFrame; SteppingDelta->vt = VT_UI4; SteppingDelta->ulVal = 1; return S_OK; } else if( *Api == CODECAPI_AVEncVideoMeanAbsoluteDifference ) { return E_NOTIMPL; } else if( *Api == CODECAPI_AVEncVideoMaxQP ) { // range [0, 51] ValueMin->vt = VT_UI4; ValueMin->ulVal = 0; ValueMax->vt = VT_UI4; ValueMax->ulVal = AVC_MAX_QP; SteppingDelta->vt = VT_UI4; SteppingDelta->ulVal = 1; return S_OK; } else if( *Api == CODECAPI_AVEncVideoMinQP ) { // range [0, 51] ValueMin->vt = VT_UI4; ValueMin->ulVal = 0; ValueMax->vt = VT_UI4; ValueMax->ulVal = AVC_MAX_QP; SteppingDelta->vt = VT_UI4; SteppingDelta->ulVal = 1; return S_OK; } else if( *Api == CODECAPI_AVEncVideoDirtyRectEnabled ) { ValueMin->vt = VT_UI4; ValueMin->ulVal = 0; ValueMax->vt = VT_UI4; ValueMax->ulVal = DIRTY_RECT_MODE_MAX - 1; SteppingDelta->vt = VT_UI4; SteppingDelta->ulVal = 1; return S_OK; } return E_NOTIMPL; } // ICodecAPI::GetParameterValues // https://learn.microsoft.com/en-us/windows/win32/api/strmif/nf-strmif-icodecapi-getparametervalues HRESULT CDX12EncHMFT::GetParameterValues( const GUID *Api, VARIANT **Values, ULONG *ValuesCount ) { HRESULT hr = ( Api && Values && ValuesCount ) ? S_OK : E_POINTER; if( SUCCEEDED( hr ) ) { if( *Api == CODECAPI_AVEncVideoTemporalLayerCount ) { hr = VFW_E_CODECAPI_LINEAR_RANGE; } else if( *Api == CODECAPI_AVEncVideoGradualIntraRefresh ) { // Our HMFT doesn't support HMFT_INTRA_REFRESH_MODE_PERIODIC *ValuesCount = 2; CHECKNULL_GOTO( *Values = (VARIANT *) CoTaskMemAlloc( ( *ValuesCount ) * sizeof( VARIANT ) ), E_OUTOFMEMORY, done ); ( *Values )[0].vt = VT_UI4; ( *Values )[0].ulVal = HMFT_INTRA_REFRESH_MODE_NONE; ( *Values )[1].vt = VT_UI4; ( *Values )[1].ulVal = HMFT_INTRA_REFRESH_MODE_CONTINUAL; } else if( *Api == CODECAPI_AVEncVideoLTRBufferControl ) { *ValuesCount = m_EncoderCapabilities.m_uiMaxHWSupportedLongTermReferences + 1; CHECKNULL_GOTO( *Values = (VARIANT *) CoTaskMemAlloc( ( *ValuesCount ) * sizeof( VARIANT ) ), E_OUTOFMEMORY, done ); for( ULONG i = 0; i < *ValuesCount; i++ ) { ( *Values )[i].vt = VT_UI4; ( *Values )[i].ulVal = ( i | ( 1 << 16 ) ); } } else { hr = E_NOTIMPL; } } done: return hr; } // ICodecAPI::GetValue // https://learn.microsoft.com/en-us/windows/win32/api/strmif/nf-strmif-icodecapi-getvalue HRESULT CDX12EncHMFT::GetValue( const GUID *Api, VARIANT *Value ) { HRESULT hr = S_OK; CHECKNULL_GOTO( Api, E_POINTER, done ); CHECKNULL_GOTO( Value, E_POINTER, done ); if( *Api == CODECAPI_AVEncCommonRateControlMode ) { Value->vt = VT_UI4; Value->ulVal = m_uiRateControlMode; } else if( *Api == CODECAPI_AVEncCommonQuality ) { Value->vt = VT_UI4; Value->ulVal = m_uiQuality[m_uiSelectedLayer]; } else if( *Api == CODECAPI_AVEncCommonQualityVsSpeed ) { Value->vt = VT_UI4; Value->ulVal = m_uiQualityVsSpeed; } else if( *Api == CODECAPI_AVEncVideoLTRBufferControl ) { Value->vt = VT_UI4; // The first field, Bits[0..15], is the number of LTR frames controlled by application. // The second field, Bits[16..31], is the trust mode of LTR control. A value of 1 (Trust Until) means the encoder // may use an LTR frame unless the application explicitly invalidates it via CODECAPI_AVEncVideoUseLTRFrame // control. Other values are invalid and reserved for future use. Value->ulVal = (UINT32) ( m_uiMaxLongTermReferences | ( m_uiTrustModeLongTermReferences << 16 ) ); } else if( *Api == CODECAPI_AVEncVideoMarkLTRFrame ) { CHECKBOOL_GOTO( m_bMarkLTRFrameSet, VFW_E_CODECAPI_NO_CURRENT_VALUE, done ); Value->vt = VT_UI4; Value->ulVal = m_uiMarkLTRFrame; } else if( *Api == CODECAPI_AVEncVideoUseLTRFrame ) { CHECKBOOL_GOTO( m_bUseLTRFrameSet, VFW_E_CODECAPI_NO_CURRENT_VALUE, done ); Value->vt = VT_UI4; Value->ulVal = m_uiUseLTRFrame; } else if( *Api == CODECAPI_AVEncCommonMeanBitRate ) { CHECKBOOL_GOTO( m_bMeanBitRateSet, VFW_E_CODECAPI_NO_CURRENT_VALUE, done ); Value->vt = VT_UI4; Value->ulVal = m_uiMeanBitRate; } else if( *Api == CODECAPI_AVEncCommonMaxBitRate ) { CHECKBOOL_GOTO( m_bPeakBitRateSet, VFW_E_CODECAPI_NO_CURRENT_VALUE, done ); Value->vt = VT_UI4; Value->ulVal = m_uiPeakBitRate; } else if( *Api == CODECAPI_AVEncCommonBufferSize ) { CHECKBOOL_GOTO( m_bBufferSizeSet, VFW_E_CODECAPI_NO_CURRENT_VALUE, done ); Value->vt = VT_UI4; Value->ulVal = m_uiBufferSize; } else if( *Api == CODECAPI_AVEncCommonBufferInLevel ) { CHECKBOOL_GOTO( m_bBufferInLevelSet, VFW_E_CODECAPI_NO_CURRENT_VALUE, done ); Value->vt = VT_UI4; Value->ulVal = m_uiBufferInLevel; } else if( *Api == CODECAPI_AVLowLatencyMode ) { Value->vt = VT_BOOL; Value->boolVal = m_bLowLatency ? VARIANT_TRUE : VARIANT_FALSE; } else if( *Api == CODECAPI_AVEncH264CABACEnable ) { Value->vt = VT_BOOL; Value->boolVal = m_bCabacEnable ? VARIANT_TRUE : VARIANT_FALSE; } else if( *Api == CODECAPI_AVEnableInLoopDeblockFilter ) { Value->vt = VT_UI4; Value->ulVal = m_uiEnableInLoopBlockFilter; } else if( *Api == CODECAPI_AVEncMPVGOPSize ) { Value->vt = VT_UI4; Value->ulVal = m_uiGopSize; } else if( *Api == CODECAPI_AVEncMPVDefaultBPictureCount ) { Value->vt = VT_UI4; Value->ulVal = m_uiBFrameCount; } else if( *Api == CODECAPI_AVEncVideoContentType ) { Value->vt = VT_UI4; Value->ulVal = m_uiContentType; } else if( *Api == CODECAPI_AVEncVideoEncodeQP ) { UINT64 ullFrameQP = 0; ullFrameQP = ( 2 * ( m_uiEncodeFrameTypeIQP[m_uiSelectedLayer] + m_uiEncodeFrameTypePQP[m_uiSelectedLayer] + m_uiEncodeFrameTypeBQP[m_uiSelectedLayer] ) + 3 ) / 6; Value->vt = VT_UI8; Value->ullVal = ullFrameQP; } else if( *Api == CODECAPI_AVEncVideoMinQP ) { Value->vt = VT_UI4; Value->ulVal = m_uiMinQP; } else if( *Api == CODECAPI_AVEncVideoForceKeyFrame ) { Value->vt = VT_UI4; Value->ulVal = 0; } else if( *Api == CODECAPI_AVEncH264SPSID ) { Value->vt = VT_UI4; Value->ulVal = m_uiSPSID; } else if( *Api == CODECAPI_AVEncH264PPSID ) { Value->vt = VT_UI4; Value->ulVal = m_uiPPSID; } else if( *Api == CODECAPI_AVEncVideoTemporalLayerCount ) { Value->vt = VT_UI4; Value->ulVal = m_uiLayerCount; } else if( *Api == CODECAPI_AVEncVideoSelectLayer ) { Value->vt = VT_UI4; Value->ulVal = m_uiSelectedLayer; } else if( *Api == CODECAPI_AVEncVideoEncodeFrameTypeQP ) { Value->vt = VT_UI8; Value->ullVal = (UINT64) m_uiEncodeFrameTypeIQP[m_uiSelectedLayer] | ( (UINT64) m_uiEncodeFrameTypePQP[m_uiSelectedLayer] << 16 ) | ( (UINT64) m_uiEncodeFrameTypeBQP[m_uiSelectedLayer] << 32 ); } else if( *Api == CODECAPI_AVEncSliceControlMode ) { CHECKBOOL_GOTO( m_bSliceControlModeSet, VFW_E_CODECAPI_NO_CURRENT_VALUE, done ); Value->vt = VT_UI4; Value->ulVal = m_uiSliceControlMode; } else if( *Api == CODECAPI_AVEncSliceControlSize ) { CHECKBOOL_GOTO( m_bSliceControlSizeSet, VFW_E_CODECAPI_NO_CURRENT_VALUE, done ); Value->vt = VT_UI4; Value->ulVal = m_uiSliceControlSize; } else if( *Api == CODECAPI_AVEncVideoMaxNumRefFrame ) { Value->vt = VT_UI4; Value->ulVal = m_uiMaxNumRefFrame; } else if( *Api == CODECAPI_AVEncVideoMeanAbsoluteDifference ) { Value->vt = VT_UI4; Value->ulVal = m_uiMeanAbsoluteDifference; } else if( *Api == CODECAPI_AVEncVideoMaxQP ) { Value->vt = VT_UI4; Value->ulVal = m_uiMaxQP; } else if( *Api == CODECAPI_AVEncVideoGradualIntraRefresh ) { Value->vt = VT_UI4; Value->ulVal = ( m_uiIntraRefreshSize << 16 ) | m_uiIntraRefreshMode; } else if( *Api == CODECAPI_AVScenarioInfo ) { Value->vt = VT_UI4; Value->ulVal = (ULONG) m_eScenarioInfo; } else if( *Api == CODECAPI_AVEncVideoROIEnabled ) { Value->vt = VT_UI4; Value->ulVal = m_bVideoROIEnabled; } else { hr = E_NOTIMPL; CHECKHR_GOTO( hr, done ); } done: MFE_INFO( "[dx12 hmft 0x%p] CodecApi GetValue %s, %s - hr=0x%x", this, StringFromCodecAPI( Api ), StringFromVariant( Value ).c_str(), hr ); return hr; } // ICodecAPI::SetValue // https://learn.microsoft.com/en-us/windows/win32/api/strmif/nf-strmif-icodecapi-setvalue HRESULT CDX12EncHMFT::SetValue( const GUID *Api, VARIANT *Value ) { HRESULT hr = S_OK; CHECKNULL_GOTO( Api, E_POINTER, done ); CHECKNULL_GOTO( Value, E_POINTER, done ); if( *Api == CODECAPI_AVEncCommonRateControlMode ) { if( Value->vt == VT_UI4 ) { if( Value->ulVal == eAVEncCommonRateControlMode_UnconstrainedVBR || Value->ulVal == eAVEncCommonRateControlMode_Quality || Value->ulVal == eAVEncCommonRateControlMode_CBR || Value->ulVal == eAVEncCommonRateControlMode_PeakConstrainedVBR ) { debug_printf( "[dx12 hmft 0x%p] SET CODECAPI_AVEncCommonRateControlMode - %u\n", this, Value->ulVal ); m_uiRateControlMode = Value->ulVal; m_bRateControlModeSet = TRUE; } } CHECKBOOL_GOTO( m_bRateControlModeSet, E_INVALIDARG, done ); } else if( *Api == CODECAPI_AVEncCommonQuality ) { if( Value->vt != VT_UI4 ) { CHECKHR_GOTO( E_INVALIDARG, done ); } { DWORD QP; UINT32 val = Value->ulVal; if( val < 1 ) val = 1; else if( val > 100 ) val = 100; m_uiQuality[m_uiSelectedLayer] = val; QP = CalculateQPFromQuality( val ); debug_printf( "[dx12 hmft 0x%p] SET CODECAPI_AVEncCommonQuality - %u\n", this, val ); debug_printf( "[dx12 hmft 0x%p] SET CODECAPI_AVEncCommonQuality (QP) - %u\n", this, QP ); m_uiEncodeFrameTypeIQP[m_uiSelectedLayer] = QP; m_uiEncodeFrameTypePQP[m_uiSelectedLayer] = QP; m_uiEncodeFrameTypeBQP[m_uiSelectedLayer] = QP; } } else if( *Api == CODECAPI_AVEncCommonQualityVsSpeed ) { debug_printf( "[dx12 hmft 0x%p] SET CODECAPI_AVEncCommonQualityVsSpeed - %u\n", this, Value->ulVal ); if( Value->vt != VT_UI4 || Value->ulVal > 100 ) { CHECKHR_GOTO( E_INVALIDARG, done ); } m_uiQualityVsSpeed = Value->ulVal; } else if( *Api == CODECAPI_AVEncVideoLTRBufferControl ) { debug_printf( "[dx12 hmft 0x%p] SET CODECAPI_AVEncVideoLTRBufferControl - %u\n", this, Value->ulVal ); if( Value->vt != VT_UI4 || Value->ulVal == 0 ) { CHECKHR_GOTO( E_INVALIDARG, done ); } // The first field, Bits[0..15], is the number of LTR frames controlled by application. // The second field, Bits[16..31], is the trust mode of LTR control. A value of 1 (Trust Until) means the encoder // may use an LTR frame unless the application explicitly invalidates it via CODECAPI_AVEncVideoUseLTRFrame // control. Other values are invalid and reserved for future use. // Validate TrustMode higher 16 bits part is valid according to spec above. if( ( (UINT32) ( ( Value->ullVal >> 16 ) & 0xFF ) ) != 1 ) { CHECKHR_GOTO( E_INVALIDARG, done ); } m_uiMaxLongTermReferences = (UINT32) ( Value->ullVal & 0xFF ); m_uiTrustModeLongTermReferences = 1; debug_printf( "[dx12 hmft 0x%p] Details for CODECAPI_AVEncVideoLTRBufferControl - MaxLTR: %u - LTR Trust Mode: %u\n", this, m_uiMaxLongTermReferences, m_uiTrustModeLongTermReferences ); } else if( *Api == CODECAPI_AVEncVideoMarkLTRFrame ) { debug_printf( "[dx12 hmft 0x%p] SET CODECAPI_AVEncVideoMarkLTRFrame - %u\n", this, Value->ulVal ); if( Value->vt != VT_UI4 || ( Value->ulVal >= m_uiMaxLongTermReferences ) ) { CHECKHR_GOTO( E_INVALIDARG, done ); } m_uiMarkLTRFrame = Value->ulVal; m_bMarkLTRFrameSet = TRUE; } else if( *Api == CODECAPI_AVEncVideoUseLTRFrame ) { debug_printf( "[dx12 hmft 0x%p] SET CODECAPI_AVEncVideoUseLTRFrame - %u\n", this, Value->ulVal ); if( Value->vt != VT_UI4 || 0 == ( Value->ulVal & 0xffff ) ) { CHECKHR_GOTO( E_INVALIDARG, done ); } m_uiUseLTRFrame = Value->ulVal; m_bUseLTRFrameSet = TRUE; } else if( *Api == CODECAPI_AVEncCommonMeanBitRate ) { debug_printf( "[dx12 hmft 0x%p] SET CODECAPI_AVEncCommonMeanBitRate - %u\n", this, Value->ulVal ); if( Value->vt != VT_UI4 || Value->ulVal == 0 ) { CHECKHR_GOTO( E_INVALIDARG, done ); } m_uiMeanBitRate = Value->ulVal; m_bMeanBitRateSet = TRUE; if( m_bPeakBitRateSet && ( m_uiPeakBitRate < m_uiMeanBitRate ) ) { m_uiPeakBitRate = m_uiMeanBitRate; CHECKHR_GOTO( E_INVALIDARG, done ); } } else if( *Api == CODECAPI_AVEncCommonMaxBitRate ) { debug_printf( "[dx12 hmft 0x%p] SET CODECAPI_AVEncCommonMaxBitRate - %u\n", this, Value->ulVal ); if( Value->vt != VT_UI4 || Value->ulVal == 0 ) { CHECKHR_GOTO( E_INVALIDARG, done ); } m_uiPeakBitRate = Value->ulVal; m_bPeakBitRateSet = TRUE; if( m_bMeanBitRateSet && ( m_uiMeanBitRate > m_uiPeakBitRate ) ) { m_uiPeakBitRate = m_uiMeanBitRate; CHECKHR_GOTO( E_INVALIDARG, done ); } } else if( *Api == CODECAPI_AVEncCommonBufferSize ) { debug_printf( "[dx12 hmft 0x%p] SET CODECAPI_AVEncCommonBufferSize - %u\n", this, Value->ulVal ); if( Value->vt != VT_UI4 || Value->ulVal == 0 ) { CHECKHR_GOTO( E_INVALIDARG, done ); } m_uiBufferSize = Value->ulVal; m_bBufferSizeSet = TRUE; } else if( *Api == CODECAPI_AVEncCommonBufferInLevel ) { debug_printf( "[dx12 hmft 0x%p] SET CODECAPI_AVEncCommonBufferInLevel - %u\n", this, Value->ulVal ); if( Value->vt != VT_UI4 || Value->ulVal == 0 ) { CHECKHR_GOTO( E_INVALIDARG, done ); } m_uiBufferInLevel = Value->ulVal; m_bBufferInLevelSet = TRUE; } else if( *Api == CODECAPI_AVLowLatencyMode ) { if( m_gpuFeatureFlags.m_bDisableAsync ) { debug_printf( "[dx12 hmft 0x%p] Async is disabled due to lack of GPU support \n", this ); m_bLowLatency = TRUE; } else { debug_printf( "[dx12 hmft 0x%p] SET CODECAPI_AVLowLatencyMode - %s\n", this, (bool) Value->boolVal ? "true" : "false" ); if( Value->vt != VT_UI4 && Value->vt != VT_BOOL ) { CHECKHR_GOTO( E_INVALIDARG, done ); } m_bLowLatency = Value->boolVal; if( ( m_eScenarioInfo == eAVScenarioInfo_DisplayRemoting ) || ( m_eScenarioInfo == eAVScenarioInfo_DisplayRemotingWithFeatureMap ) || ( m_eScenarioInfo == eAVScenarioInfo_CameraRecord ) || ( m_eScenarioInfo == eAVScenarioInfo_VideoConference ) || ( m_eScenarioInfo == eAVScenarioInfo_LiveStreaming ) ) { m_bLowLatency = TRUE; } } } else if( *Api == CODECAPI_AVEncH264CABACEnable ) { debug_printf( "[dx12 hmft 0x%p] SET CODECAPI_AVEncH264CABACEnable - %s\n", this, (bool) Value->boolVal ? "true" : "false" ); if( Value->vt != VT_UI4 && Value->vt != VT_BOOL ) { CHECKHR_GOTO( E_INVALIDARG, done ); } m_bCabacEnable = Value->boolVal; } else if( *Api == CODECAPI_AVEnableInLoopDeblockFilter ) { debug_printf( "[dx12 hmft 0x%p] SET CODECAPI_AVEnableInLoopDeblockFilter - %s\n", this, Value->ulVal ? "true" : "false" ); if( Value->vt != VT_UI4 ) { CHECKHR_GOTO( E_INVALIDARG, done ); } m_uiEnableInLoopBlockFilter = Value->ulVal; } else if( *Api == CODECAPI_AVEncMPVGOPSize ) { if( Value->vt != VT_UI4 ) { CHECKHR_GOTO( E_INVALIDARG, done ); } // While this is a UINT32, it can be passed a value of -1 to indicate infinite GOP debug_printf( "[dx12 hmft 0x%p] SET CODECAPI_AVEncMPVGOPSize - %d\n", this, (INT32) ( Value->ulVal ) ); m_uiGopSize = Value->ulVal; m_bGopSizeSet = TRUE; if( m_uiGopSize == 0 ) { UINT32 uiFrameSize = m_uiOutputWidth * m_uiOutputHeight; if( uiFrameSize < 496 * 384 ) { m_uiGopSize = m_FrameRate.Numerator * 3; // 3 seconds for CIF } else if( uiFrameSize < 960 * 600 ) { m_uiGopSize = m_FrameRate.Numerator * 2; // 2 seconds for SD } else { m_uiGopSize = m_FrameRate.Numerator * 1; // 1 second for HD } } if( m_uiGopSize == -1 ) { // For DX12 back-end and gop-tracker, gop-size of 0 is infinite m_uiGopSize = 0; } debug_printf( "[dx12 hmft 0x%p] Resulting CODECAPI_AVEncMPVGOPSize - %u\n", this, m_uiGopSize ); } else if( *Api == CODECAPI_AVEncMPVDefaultBPictureCount ) { if( Value->vt != VT_UI4 /* || Value->ulVal > HMFT_MAX_BFRAMES */ ) { CHECKHR_GOTO( E_INVALIDARG, done ); } debug_printf( "[dx12 hmft 0x%p] SET CODECAPI_AVEncMPVDefaultBPictureCount - %u\n", this, Value->ulVal ); m_uiBFrameCount = Value->ulVal; // Handle the case where B frame range is not checked by the caller, clamp to HMFT_MAX_BFRAMES (= 0 right now) if( m_uiBFrameCount > HMFT_MAX_BFRAMES ) { debug_printf( "[dx12 hmft 0x%p] Clamp CODECAPI_AVEncMPVDefaultBPictureCount to %u\n", this, HMFT_MAX_BFRAMES ); m_uiBFrameCount = HMFT_MAX_BFRAMES; } } else if( *Api == CODECAPI_AVEncVideoContentType ) { debug_printf( "[dx12 hmft 0x%p] SET CODECAPI_AVEncVideoContentType - %u\n", this, Value->ulVal ); if( Value->vt != VT_UI4 || ( Value->ulVal != eAVEncVideoContentType_Unknown && Value->ulVal != eAVEncVideoContentType_FixedCameraAngle ) ) { CHECKHR_GOTO( E_INVALIDARG, done ); } m_uiContentType = Value->ulVal; m_bContentTypeSet = TRUE; } else if( *Api == CODECAPI_AVEncVideoEncodeQP ) { UINT32 uiFrameQP = ( (UINT32) Value->ullVal ) & 0xFFFF; debug_printf( "[dx12 hmft 0x%p] SET CODECAPI_AVEncVideoEncodeQP - %u\n", this, uiFrameQP ); if( Value->vt != VT_UI8 || ( ( ( (UINT32) Value->ullVal ) & 0xFFFF ) > AVC_MAX_QP ) || m_uiRateControlMode != eAVEncCommonRateControlMode_Quality ) { CHECKHR_GOTO( E_INVALIDARG, done ); } m_uiEncodeFrameTypeIQP[m_uiSelectedLayer] = uiFrameQP; m_uiEncodeFrameTypePQP[m_uiSelectedLayer] = uiFrameQP; m_uiEncodeFrameTypeBQP[m_uiSelectedLayer] = uiFrameQP; // validate frame QPs are within H.264-allowed limits if( AVC_MAX_QP < uiFrameQP ) { MFE_ERROR( "[dx12 hmft 0x%p] The QP set in CODECAPI_AVEncVideoEncodeQP is greater than 51", this ); CHECKHR_GOTO( E_INVALIDARG, done ); } // validate frame QP settings against the right range of [MinQP, MaxQP] if exists if( ( TRUE == m_bMaxQPSet && m_uiMaxQP < uiFrameQP ) || ( TRUE == m_bMinQPSet && m_uiMinQP > uiFrameQP ) ) { MFE_ERROR( "[dx12 hmft 0x%p] The QP set in CODECAPI_AVEncVideoEncodeQP is outside min and max values", this ); CHECKHR_GOTO( E_INVALIDARG, done ); } // only when it succeeds, set the flag to TRUE m_bEncodeQPSet = TRUE; } else if( *Api == CODECAPI_AVEncVideoEncodeFrameTypeQP ) { if( Value->vt != VT_UI8 || ( m_uiRateControlMode != eAVEncCommonRateControlMode_Quality ) ) { CHECKHR_GOTO( E_INVALIDARG, done ); } m_uiEncodeFrameTypeIQP[m_uiSelectedLayer] = (UINT32) ( Value->ullVal & 0xFFFF ); m_uiEncodeFrameTypePQP[m_uiSelectedLayer] = (UINT32) ( ( Value->ullVal >> 16 ) & 0xFFFF ); m_uiEncodeFrameTypeBQP[m_uiSelectedLayer] = (UINT32) ( ( Value->ullVal >> 32 ) & 0xFFFF ); // Validate that the frame QPs are within H.264-allowed limits // We need to perform this check here because there are places // later in the MFT layer that assume that if frame QPs have been set that they // are within the valid range if( AVC_MAX_QP < m_uiEncodeFrameTypeIQP[m_uiSelectedLayer] || AVC_MAX_QP < m_uiEncodeFrameTypePQP[m_uiSelectedLayer] || AVC_MAX_QP < m_uiEncodeFrameTypeBQP[m_uiSelectedLayer] ) { MFE_ERROR( "[dx12 hmft 0x%p] At least one of the QPs set in CODECAPI_AVEncVideoEncodeFrameTypeQP is greater than 51", this ); CHECKHR_GOTO( E_INVALIDARG, done ); } // validate frame QP settings against the right range of [MinQP, MaxQP] if exists if( ( TRUE == m_bMaxQPSet && ( m_uiMaxQP < m_uiEncodeFrameTypeIQP[m_uiSelectedLayer] || m_uiMaxQP < m_uiEncodeFrameTypePQP[m_uiSelectedLayer] || m_uiMaxQP < m_uiEncodeFrameTypeBQP[m_uiSelectedLayer] ) ) || ( TRUE == m_bMinQPSet && ( m_uiMinQP > m_uiEncodeFrameTypeIQP[m_uiSelectedLayer] || m_uiMinQP > m_uiEncodeFrameTypePQP[m_uiSelectedLayer] || m_uiMinQP > m_uiEncodeFrameTypeBQP[m_uiSelectedLayer] ) ) ) { MFE_ERROR( "[dx12 hmft 0x%p] At least one of the QPs set in CODECAPI_AVEncVideoEncodeFrameTypeQP is outside min and max values", this ); CHECKHR_GOTO( E_INVALIDARG, done ); } // only when it succeeds, set the flag to TRUE debug_printf( "[dx12 hmft 0x%p] SET CODECAPI_AVEncVideoEncodeFrameTypeQP - %u, %u, %u (I, P, B)\n", this, m_uiEncodeFrameTypeIQP[m_uiSelectedLayer], m_uiEncodeFrameTypePQP[m_uiSelectedLayer], m_uiEncodeFrameTypeBQP[m_uiSelectedLayer] ); m_bEncodeQPSet = TRUE; } else if( *Api == CODECAPI_AVEncVideoMinQP ) { debug_printf( "[dx12 hmft 0x%p] SET CODECAPI_AVEncVideoMinQP - %u\n", this, Value->ulVal ); if( Value->vt != VT_UI4 || ( Value->ulVal > AVC_MAX_QP ) ) { CHECKHR_GOTO( E_INVALIDARG, done ); } // validation against max QP, if max QP is set, and max QP is less than min QP // then this is an invalid setting if( TRUE == m_bMaxQPSet && Value->ulVal > m_uiMaxQP ) { MFE_ERROR( "[dx12 hmft 0x%p] Min QP is greater than max QP", this ); CHECKHR_GOTO( E_INVALIDARG, done ); } m_uiMinQP = Value->ulVal; m_bMinQPSet = TRUE; // HLK, and perhaps other apps, expect that min-QP applies even in Quality mode // For example, a Quality value of 100 translates to a QP value of 16. If min QP // is then set to 21, we need to adjust accordingly. if( m_uiMinQP > m_uiEncodeFrameTypeIQP[m_uiSelectedLayer] ) { m_uiEncodeFrameTypeIQP[m_uiSelectedLayer] = m_uiMinQP; m_uiEncodeFrameTypePQP[m_uiSelectedLayer] = m_uiMinQP; m_uiEncodeFrameTypeBQP[m_uiSelectedLayer] = m_uiMinQP; } } else if( *Api == CODECAPI_AVEncVideoMaxQP ) { debug_printf( "[dx12 hmft 0x%p] SET CODECAPI_AVEncVideoMaxQP - %u\n", this, Value->ulVal ); if( Value->vt != VT_UI4 ) { CHECKHR_GOTO( E_INVALIDARG, done ); } // validation against min QP, // if min QP is set, and min QP is larger than max QP // this is an invalid setting if( TRUE == m_bMinQPSet && Value->ulVal < m_uiMinQP ) { MFE_ERROR( "[dx12 hmft 0x%p] Min QP is greater than max QP", this ); CHECKHR_GOTO( E_INVALIDARG, done ); } m_uiMaxQP = Value->ulVal; m_bMaxQPSet = TRUE; // HLK, and perhaps other apps, expect that max-QP applies even in Quality mode // For example, a Quality value of 100 translates to a QP value of 16. If max QP // is then set to 15, we need to adjust accordingly. if( m_uiMaxQP < m_uiEncodeFrameTypeIQP[m_uiSelectedLayer] ) { m_uiEncodeFrameTypeIQP[m_uiSelectedLayer] = m_uiMaxQP; m_uiEncodeFrameTypePQP[m_uiSelectedLayer] = m_uiMaxQP; m_uiEncodeFrameTypeBQP[m_uiSelectedLayer] = m_uiMaxQP; } } else if( *Api == CODECAPI_AVEncVideoForceKeyFrame ) { if( Value->vt != VT_UI4 ) { CHECKHR_GOTO( E_INVALIDARG, done ); } debug_printf( "[dx12 hmft 0x%p] SET CODECAPI_AVEncVideoForceKeyFrame - %u\n", this, Value->ulVal ); if( Value->ulVal > 0 ) { m_bForceKeyFrame = TRUE; } } else if( *Api == CODECAPI_AVEncVideoTemporalLayerCount ) { debug_printf( "[dx12 hmft 0x%p] SET CODECAPI_AVEncVideoTemporalLayerCount - %u\n", this, Value->ulVal ); CHECKBOOL_GOTO( Value->vt == VT_UI4, E_INVALIDARG, done ); CHECKBOOL_GOTO( Value->ulVal <= HMFT_MAX_TEMPORAL_LAYERS, MF_E_OUT_OF_RANGE, done ); if( !m_spOutputType ) { m_uiLayerCount = Value->ulVal; m_bLayerCountSet = TRUE; } // dynamic change only allowed if the initial setting of layer count happens before SetOutputType is called if( m_bLayerCountSet ) { m_uiLayerCount = Value->ulVal; } } else if( *Api == CODECAPI_AVEncVideoSelectLayer ) { debug_printf( "[dx12 hmft 0x%p] SET CODECAPI_AVEncVideoSelectLayer - %u\n", this, Value->ulVal ); if( Value->vt != VT_UI4 || Value->ulVal >= HMFT_MAX_TEMPORAL_LAYERS ) { CHECKHR_GOTO( E_INVALIDARG, done ); } if( Value->ulVal > m_uiLayerCount ) { MFE_ERROR( "[dx12 hmft 0x%p] User tried to select a layer that was greater than the current layer count", this ); CHECKHR_GOTO( E_INVALIDARG, done ); } m_uiSelectedLayer = Value->ulVal; } else if( *Api == CODECAPI_AVEncH264SPSID ) { debug_printf( "[dx12 hmft 0x%p] SET CODECAPI_AVEncH264SPSID - %u\n", this, Value->ulVal ); if( Value->vt != VT_UI4 || ( Value->ulVal > 31 ) ) { CHECKHR_GOTO( E_INVALIDARG, done ); } m_uiSPSID = Value->ulVal; m_bSPSIDSet = TRUE; } else if( *Api == CODECAPI_AVEncH264PPSID ) { debug_printf( "[dx12 hmft 0x%p] SET CODECAPI_AVEncH264PPSID - %u\n", this, Value->ulVal ); if( Value->vt != VT_UI4 || ( Value->ulVal > 255 ) ) { CHECKHR_GOTO( E_INVALIDARG, done ); } m_uiPPSID = Value->ulVal; m_bPPSIDSet = TRUE; } else if( *Api == CODECAPI_AVEncSliceControlMode ) { if( Value->vt != VT_UI4 || Value->ulVal >= SLICE_CONTROL_MODE_MAX ) { CHECKHR_GOTO( E_INVALIDARG, done ); } debug_printf( "[dx12 hmft 0x%p] SET CODECAPI_AVEncSliceControlMode - %u\n", this, Value->ulVal ); m_uiSliceControlMode = Value->ulVal; m_bSliceControlModeSet = TRUE; } else if( *Api == CODECAPI_AVEncSliceControlSize ) { // 0 is invalid for any modes // value 0 for slice control size won't be set in core encoder if( Value->vt != VT_UI4 || 0 == Value->ulVal ) { CHECKHR_GOTO( E_INVALIDARG, done ); } // If slice control mode hasn't been set, don't allow slice control size CHECKBOOL_GOTO( m_bSliceControlModeSet, E_INVALIDARG, done ); debug_printf( "[dx12 hmft 0x%p] SET CODECAPI_AVEncSliceControlSize - %u\n", this, Value->ulVal ); // This is a dynamic property and may be set before SetOutputType() has been called. // Don't use member vars that may not be initialized yet if( m_spOutputType ) { UINT32 uiMBRows = ( ( m_uiOutputHeight + 15 ) >> 4 ); UINT32 uiMBPerRow = ( ( m_uiOutputWidth + 15 ) >> 4 ); if( SLICE_CONTROL_MODE_MB == m_uiSliceControlMode ) { // the slice size in the number of MBs if( Value->ulVal > (ULONG) ( uiMBPerRow * uiMBRows ) ) { MFE_ERROR( "[dx12 hmft 0x%p] User tried to set slice size to a value greater than the total number of macroblocks " "in macroblock/slice mode", this ); CHECKHR_GOTO( MF_E_OUT_OF_RANGE, done ); } if( uiMBRows * uiMBPerRow / Value->ulVal + ( 0 != ( uiMBRows * uiMBPerRow ) % Value->ulVal ) > m_EncoderCapabilities.m_uiMaxHWSupportedMaxSlices ) { MFE_ERROR( "[dx12 hmft 0x%p] The number of slices in macroblock/slice mode is greater than maximum supported by hardware", this ); CHECKHR_GOTO( MF_E_OUT_OF_RANGE, done ); } } if( SLICE_CONTROL_MODE_MB_ROW == m_uiSliceControlMode ) { // the slice size in MB row if( Value->ulVal > uiMBRows ) { MFE_ERROR( "[dx12 hmft 0x%p] User tried to set slice size to a value greater than the total number of macroblock " "rows in macroblock-row/slice mode", this ); CHECKHR_GOTO( MF_E_OUT_OF_RANGE, done ); } if( uiMBRows / Value->ulVal + ( 0 != uiMBRows % Value->ulVal ) > m_EncoderCapabilities.m_uiMaxHWSupportedMaxSlices ) { MFE_ERROR( "[dx12 hmft 0x%p] The number of slices in macroblock/slice mode is greater than maximum supported by hardware", this ); CHECKHR_GOTO( MF_E_OUT_OF_RANGE, done ); } // CHECKBOOL_GOTO(Value->ulVal <= m_EncoderCapabilities.m_uiMaxHWSupportedMaxSlices, MF_E_OUT_OF_RANGE, done); } } if( SLICE_CONTROL_MODE_BITS == m_uiSliceControlMode ) { // the slice size in bits if( Value->ulVal < HMFT_MIN_BITS_PER_SLICE ) { MFE_ERROR( "[dx12 hmft 0x%p] User tried to set slice size to a value less than the minimum bits/slice in bits/slice mode", this ); CHECKHR_GOTO( E_INVALIDARG, done ); } } m_uiSliceControlSize = Value->ulVal; m_bSliceControlSizeSet = TRUE; } else if( *Api == CODECAPI_AVEncVideoMaxNumRefFrame ) { debug_printf( "[dx12 hmft 0x%p] SET CODECAPI_AVEncVideoMaxNumRefFrame - %u\n", this, Value->ulVal ); if( Value->vt != VT_UI4 ) { CHECKHR_GOTO( E_INVALIDARG, done ); } m_uiMaxNumRefFrame = Value->ulVal; m_bMaxNumRefFrameSet = TRUE; } else if( *Api == CODECAPI_AVEncVideoMeanAbsoluteDifference ) { debug_printf( "[dx12 hmft 0x%p] SET CODECAPI_AVEncVideoMeanAbsoluteDifference - %u\n", this, Value->ulVal ); if( Value->vt != VT_UI4 ) { CHECKHR_GOTO( E_INVALIDARG, done ); } m_uiMeanAbsoluteDifference = Value->ulVal; } else if( *Api == CODECAPI_AVEncVideoGradualIntraRefresh ) { debug_printf( "[dx12 hmft 0x%p] SET CODECAPI_AVEncVideoGradualIntraRefresh - %u\n", this, Value->ulVal ); if( Value->vt != VT_UI4 || ( ( Value->ulVal & 0xFFFF ) >= HMFT_INTRA_REFRESH_MODE_MAX ) ) { CHECKHR_GOTO( E_INVALIDARG, done ); } m_uiIntraRefreshMode = Value->ulVal & 0xFFFF; m_uiIntraRefreshSize = Value->ulVal >> 16 & 0xFFFF; } else if( *Api == CODECAPI_AVScenarioInfo ) { // Accept any value since this is only a scenario hint and we should not fail any setting debug_printf( "[dx12 hmft 0x%p] SET CODECAPI_AVScenarioInfo - %u\n", this, Value->ulVal ); if( Value->vt != VT_UI4 ) { CHECKHR_GOTO( E_INVALIDARG, done ); } m_eScenarioInfo = (eAVScenarioInfo) Value->ulVal; if( ( m_eScenarioInfo == eAVScenarioInfo_DisplayRemoting ) || ( m_eScenarioInfo == eAVScenarioInfo_DisplayRemotingWithFeatureMap ) || ( m_eScenarioInfo == eAVScenarioInfo_CameraRecord ) || ( m_eScenarioInfo == eAVScenarioInfo_VideoConference ) || ( m_eScenarioInfo == eAVScenarioInfo_LiveStreaming ) ) { m_bLowLatency = TRUE; } } else if( *Api == CODECAPI_AVEncVideoROIEnabled ) { debug_printf( "[dx12 hmft 0x%p] SET CODECAPI_AVEncVideoROIEnabled - %u\n", this, Value->ulVal ); if( Value->vt != VT_UI4 ) { CHECKHR_GOTO( E_INVALIDARG, done ); } m_bVideoROIEnabled = Value->ulVal ? TRUE : FALSE; } else if( *Api == CODECAPI_AVEncVideoDirtyRectEnabled ) { debug_printf( "[dx12 hmft 0x%p] SET CODECAPI_AVEncVideoDirtyRectEnabled - %u\n", this, Value->ulVal ); if( Value->vt != VT_UI4 || Value->ulVal >= DIRTY_RECT_MODE_MAX ) { CHECKHR_GOTO( E_INVALIDARG, done ); } if( !m_EncoderCapabilities.m_HWSupportDirtyRects.bits.supports_info_type_dirty ) { CHECKHR_GOTO( E_INVALIDARG, done ); } m_uiDirtyRectEnabled = Value->ulVal; } else { CHECKHR_GOTO( E_NOTIMPL, done ); } done: MFE_INFO( "[dx12 hmft 0x%p] CodecApi SetValue %s, %s - hr=0x%x", this, StringFromCodecAPI( Api ), StringFromVariant( Value ).c_str(), hr ); return hr; } // ICodecAPI::GetDefaultValue // https://learn.microsoft.com/en-us/windows/win32/api/strmif/nf-strmif-icodecapi-getdefaultvalue HRESULT CDX12EncHMFT::GetDefaultValue( const GUID *Api, VARIANT *Value ) { return E_NOTIMPL; } // ICodecAPI::RegisterForEvent // https://learn.microsoft.com/en-us/windows/win32/api/strmif/nf-strmif-icodecapi-registerforevent HRESULT CDX12EncHMFT::RegisterForEvent( const GUID *Api, LONG_PTR userData ) { return E_NOTIMPL; } // ICodecAPI::UnregisterForEvent // https://learn.microsoft.com/en-us/windows/win32/api/strmif/nf-strmif-icodecapi-unregisterforevent HRESULT CDX12EncHMFT::UnregisterForEvent( const GUID *Api ) { return E_NOTIMPL; } // ICodecAPI::SetAllDefaults // https://learn.microsoft.com/en-us/windows/win32/api/strmif/nf-strmif-icodecapi-setalldefaults HRESULT CDX12EncHMFT::SetAllDefaults( void ) { return E_NOTIMPL; } // ICodecAPI::SetValueWithNotify // https://learn.microsoft.com/en-us/windows/win32/api/strmif/nf-strmif-icodecapi-setvaluewithnotify HRESULT CDX12EncHMFT::SetValueWithNotify( const GUID *Api, VARIANT *Value, GUID **ChangedParam, ULONG *ChangedParamCount ) { return E_NOTIMPL; } // ICodecAPI::SetAllDefaultsWithNotify // https://learn.microsoft.com/en-us/windows/win32/api/strmif/nf-strmif-icodecapi-setalldefaultswithnotify HRESULT CDX12EncHMFT::SetAllDefaultsWithNotify( GUID **ChangedParam, ULONG *ChangedParamCount ) { return E_NOTIMPL; } // ICodecAPI::GetAllSettings // https://learn.microsoft.com/en-us/windows/win32/api/strmif/nf-strmif-icodecapi-getallsettings HRESULT CDX12EncHMFT::GetAllSettings( IStream *pStream ) { return E_NOTIMPL; } // ICodecAPI::SetAllSettings // https://learn.microsoft.com/en-us/windows/win32/api/strmif/nf-strmif-icodecapi-setallsettings HRESULT CDX12EncHMFT::SetAllSettings( IStream *pStream ) { return E_NOTIMPL; } // ICodecAPI::SetAllSettingsWithNotify // https://learn.microsoft.com/en-us/windows/win32/api/strmif/nf-strmif-icodecapi-setvaluewithnotify HRESULT CDX12EncHMFT::SetAllSettingsWithNotify( IStream *pStream, GUID **ChangedParam, ULONG *ChangedParamCount ) { return E_NOTIMPL; } // --------------------------------- // End of IMFTransform public method // ---------------------------------