mesa/src/gallium/frontends/mediafoundation/encode_h264.cpp
Wenfeng Gao aa5398689b mediafoundation: Fix the frame number validation logic for motion hint
The external move region frame number was continuously generated. However, the current POC was reset based on IDR.
Modified the logic of validation and logged a warning in case of mismatch.

Reviewed-by: Pohsiang (John) Hsu <pohhsu@microsoft.com>
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/40756>
2026-04-02 13:54:02 +00:00

1232 lines
51 KiB
C++
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* 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.
*/
#if MFT_CODEC_H264ENC
#include "hmft_entrypoints.h"
#include "mfbufferhelp.h"
#include "mfpipeinterop.h"
#include "reference_frames_tracker_h264.h"
#include "wpptrace.h"
#include "encode_h264.tmh"
extern DWORD
CalculateQualityFromQP( DWORD QP );
// utility function to compute the cropping rectangle given texture and output dimensions
static void
ComputeCroppingRect( const UINT32 textureWidth,
const UINT32 textureHeight,
const UINT uiOutputWidth,
const UINT uiOutputHeight,
const enum pipe_video_profile outputPipeProfile,
BOOL &bFrameCroppingFlag,
UINT32 &uiFrameCropRightOffset,
UINT32 &uiFrameCropBottomOffset )
{
UINT32 iCropRight = textureWidth - uiOutputWidth;
UINT32 iCropBottom = textureHeight - uiOutputHeight;
if( iCropRight || iCropBottom )
{
UINT32 chromaFormatIdc = GetChromaFormatIdc( ConvertProfileToFormat( outputPipeProfile ) );
UINT32 cropUnitX = 1;
UINT32 cropUnitY = 1;
switch( chromaFormatIdc )
{
case 1:
cropUnitX = 2;
cropUnitY = 2;
break;
case 3:
cropUnitX = 1;
cropUnitY = 1;
break;
default:
{
UNREACHABLE( "Unsupported chroma format idc" );
}
break;
}
bFrameCroppingFlag = TRUE;
uiFrameCropRightOffset = iCropRight / cropUnitX;
uiFrameCropBottomOffset = iCropBottom / cropUnitY;
}
}
// utility function to compute the constraint set flags from profile
static unsigned
ConstraintSetFlagsFromProfile( eAVEncH264VProfile profile )
{
unsigned flags = 0;
// Constraint set flags
// 6 bits constrained_set_flag5 (lowest bits) to constrained_set_flag0 (highest bits)
uint32_t constraint_set_flag0 = 0;
uint32_t constraint_set_flag1 = 0;
uint32_t constraint_set_flag2 = 0;
uint32_t constraint_set_flag3 = 0;
uint32_t constraint_set_flag4 = 0;
uint32_t constraint_set_flag5 = 0;
switch( profile )
{
case eAVEncH264VProfile_ConstrainedBase:
constraint_set_flag1 = 1;
break;
case eAVEncH264VProfile_Base:
constraint_set_flag0 = 1;
constraint_set_flag1 = 1;
break;
case eAVEncH264VProfile_Main:
constraint_set_flag1 = 1;
break;
case eAVEncH264VProfile_ConstrainedHigh:
constraint_set_flag4 = 1;
constraint_set_flag5 = 1;
break;
}
flags = ( ( constraint_set_flag5 & 1 ) << 0 ) | ( ( constraint_set_flag4 & 1 ) << 1 ) | ( ( constraint_set_flag3 & 1 ) << 2 ) |
( ( constraint_set_flag2 & 1 ) << 3 ) | ( ( constraint_set_flag1 & 1 ) << 4 ) | ( ( constraint_set_flag0 & 1 ) << 5 );
return flags;
}
// utility function to fill in encoder picture descriptor (pPicInfo) which is used to pass information to DX12 encoder
HRESULT
CDX12EncHMFT::UpdateH264EncPictureDesc( pipe_h264_enc_picture_desc *pPicInfo, const uint32_t intra_period, const uint32_t ip_period )
{
HRESULT hr = S_OK;
// fields are arranged in the same order as pipe_h264_enc_picture_desc
// base
pPicInfo->base.profile = m_outputPipeProfile;
// seq
pPicInfo->seq.enc_frame_cropping_flag = m_bFrameCroppingFlag;
pPicInfo->seq.vui_parameters_present_flag = 1; // VUI Data - always true because we have timing_info_present_flag = 1
pPicInfo->seq.video_full_range_flag = m_VUIInfo.stVidSigType.bVideoFullRangeFlag;
pPicInfo->seq.enc_constraint_set_flags = ConstraintSetFlagsFromProfile( m_uiProfile );
pPicInfo->seq.level_idc = m_pPipeVideoCodec->level;
pPicInfo->seq.enc_frame_crop_right_offset = m_uiFrameCropRightOffset;
pPicInfo->seq.enc_frame_crop_bottom_offset = m_uiFrameCropBottomOffset;
/* TODO: check if we need this in gop_info.
pPicInfo->seq.pic_order_cnt_type = cur_frame_desc->gop_info->pic_order_cnt_type;
pPicInfo->seq.log2_max_frame_num_minus4 = cur_frame_desc->gop_info->log2_max_frame_num_minus4;
pPicInfo->seq.log2_max_pic_order_cnt_lsb_minus4 = cur_frame_desc->gop_info->log2_max_pic_order_cnt_lsb_minus4;
*/
pPicInfo->seq.pic_order_cnt_type = ( ip_period > 2 ) ? 0u : 2u; // 2 consecutive non reference frames -> 0u
pPicInfo->seq.log2_max_frame_num_minus4 = 4;
pPicInfo->seq.log2_max_pic_order_cnt_lsb_minus4 = pPicInfo->seq.log2_max_frame_num_minus4 + 1;
pPicInfo->seq.num_temporal_layers = m_bLayerCountSet ? HMFT_MAX_TEMPORAL_LAYERS : 1;
if( m_EncoderCapabilities.m_bHWSupportsH264CABACEncode )
{
pPicInfo->pic_ctrl.enc_cabac_enable = ( ( pPicInfo->base.profile == PIPE_VIDEO_PROFILE_MPEG4_AVC_MAIN ) ||
( pPicInfo->base.profile == PIPE_VIDEO_PROFILE_MPEG4_AVC_HIGH ) ) &&
m_bCabacEnable ?
true :
false;
}
pPicInfo->seq.max_num_ref_frames = m_pPipeVideoCodec->max_references;
pPicInfo->seq.vui_flags.aspect_ratio_info_present_flag = m_VUIInfo.bEnableSAR;
pPicInfo->seq.vui_flags.timing_info_present_flag = 1;
pPicInfo->seq.vui_flags.video_signal_type_present_flag = m_VUIInfo.bEnableVST;
pPicInfo->seq.vui_flags.colour_description_present_flag = m_VUIInfo.stVidSigType.bColorInfoPresent;
pPicInfo->seq.vui_flags.chroma_loc_info_present_flag = 0;
pPicInfo->seq.vui_flags.overscan_info_present_flag = 0;
pPicInfo->seq.vui_flags.overscan_appropriate_flag = 0;
pPicInfo->seq.vui_flags.fixed_frame_rate_flag = 1;
pPicInfo->seq.vui_flags.nal_hrd_parameters_present_flag = 0;
pPicInfo->seq.vui_flags.vcl_hrd_parameters_present_flag = 0;
pPicInfo->seq.vui_flags.low_delay_hrd_flag = 0;
pPicInfo->seq.vui_flags.pic_struct_present_flag = 0;
pPicInfo->seq.vui_flags.bitstream_restriction_flag = 1;
if( pPicInfo->seq.vui_flags.bitstream_restriction_flag )
{
pPicInfo->seq.vui_flags.motion_vectors_over_pic_boundaries_flag = 0;
}
pPicInfo->seq.aspect_ratio_idc = 255; // EXTENDED_SAR
pPicInfo->seq.sar_width = m_VUIInfo.stSARInfo.usWidth;
pPicInfo->seq.sar_height = m_VUIInfo.stSARInfo.usHeight;
pPicInfo->seq.num_units_in_tick = m_FrameRate.Denominator;
pPicInfo->seq.time_scale = m_FrameRate.Numerator * 2;
pPicInfo->seq.video_format = m_VUIInfo.stVidSigType.eVideoFormat; // VST - video signal type
pPicInfo->seq.colour_primaries = m_VUIInfo.stVidSigType.eColorPrimary;
pPicInfo->seq.transfer_characteristics = m_VUIInfo.stVidSigType.eColorTransfer;
pPicInfo->seq.matrix_coefficients = m_VUIInfo.stVidSigType.eColorMatrix;
pPicInfo->seq.chroma_sample_loc_type_top_field = 0;
pPicInfo->seq.chroma_sample_loc_type_bottom_field = 0;
if( pPicInfo->seq.vui_flags.bitstream_restriction_flag )
{
pPicInfo->seq.max_num_reorder_frames = 0;
}
memset( &pPicInfo->seq.nal_hrd_parameters, 0, sizeof( pipe_h264_enc_hrd_params ) );
memset( &pPicInfo->seq.vcl_hrd_parameters, 0, sizeof( pipe_h264_enc_hrd_params ) );
if( pPicInfo->seq.vui_flags.bitstream_restriction_flag )
{
pPicInfo->seq.max_bytes_per_pic_denom = 0;
pPicInfo->seq.max_bits_per_mb_denom = 0;
pPicInfo->seq.log2_max_mv_length_vertical = 0;
pPicInfo->seq.log2_max_mv_length_horizontal = 0;
pPicInfo->seq.max_dec_frame_buffering = pPicInfo->seq.max_num_ref_frames; // TODO: compute a more accurate value.
}
pPicInfo->seq.enc_constraint_set_flags = ConstraintSetFlagsFromProfile( m_uiProfile );
pPicInfo->intra_idr_period = intra_period;
pPicInfo->ip_period = ip_period;
pPicInfo->gop_size = intra_period;
return hr;
}
// internal function which contains the codec specific portion of PrepareForEncode
HRESULT
CDX12EncHMFT::PrepareForEncodeHelper( LPDX12EncodeContext pDX12EncodeContext,
bool dirtyRectFrameNumSet,
uint32_t dirtyRectFrameNum,
bool moveRegionFrameNumSet,
uint32_t moveRegionFrameNum )
{
HRESULT hr = S_OK;
pipe_h264_enc_picture_desc *pPicInfo = &pDX12EncodeContext->encoderPicInfo.h264enc;
const reference_frames_tracker_frame_descriptor_h264 *cur_frame_desc =
(const reference_frames_tracker_frame_descriptor_h264 *) m_pGOPTracker->get_frame_descriptor();
// Initialize raw headers array
pPicInfo->raw_headers = UTIL_DYNARRAY_INIT;
uint32_t height_in_blocks = 0;
uint32_t width_in_blocks = 0;
uint32_t rate_ctrl_active_layer_index = 0;
pPicInfo->requested_metadata = m_EncoderCapabilities.m_HWSupportedMetadataFlags;
pPicInfo->base.input_format = pDX12EncodeContext->pPipeVideoBuffer->buffer_format;
UpdateH264EncPictureDesc( pPicInfo, cur_frame_desc->gop_info->intra_period, cur_frame_desc->gop_info->ip_period );
if( pDX12EncodeContext->bROI )
{
// Convert to pipe roi params semantics
pPicInfo->roi.num = 1;
pPicInfo->roi.region[0].valid = true;
pPicInfo->roi.region[0].qp_value = pDX12EncodeContext->video_roi_area.QPDelta;
pPicInfo->roi.region[0].x = pDX12EncodeContext->video_roi_area.rect.left;
pPicInfo->roi.region[0].y = pDX12EncodeContext->video_roi_area.rect.top;
pPicInfo->roi.region[0].width =
( pDX12EncodeContext->video_roi_area.rect.right - pDX12EncodeContext->video_roi_area.rect.left );
pPicInfo->roi.region[0].height =
( pDX12EncodeContext->video_roi_area.rect.bottom - pDX12EncodeContext->video_roi_area.rect.top );
}
if( !m_uiEnableInLoopBlockFilter && ( ( PIPE_VIDEO_H264_ENC_DBK_MODE_DISABLE_ALL_SLICE_BLOCK_EDGES &
m_EncoderCapabilities.m_HWSupportedDisableDBKH264ModeFlags ) != 0 ) )
pPicInfo->dbk.disable_deblocking_filter_idc =
D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_H264_SLICES_DEBLOCKING_MODE_1_DISABLE_ALL_SLICE_BLOCK_EDGES;
else
pPicInfo->dbk.disable_deblocking_filter_idc =
D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_H264_SLICES_DEBLOCKING_MODE_0_ALL_LUMA_CHROMA_SLICE_BLOCK_EDGES_ALWAYS_FILTERED;
// Set the IDR exclusive long_term_reference_flag flag in the slice header or reset it to zero
pPicInfo->slice.long_term_reference_flag =
( cur_frame_desc->gop_info->reference_type == frame_descriptor_reference_type_long_term &&
( cur_frame_desc->gop_info->frame_type == PIPE_H2645_ENC_PICTURE_TYPE_IDR ) ) ?
1u :
0u;
pPicInfo->pic_ctrl.temporal_id = cur_frame_desc->gop_info->temporal_id;
pPicInfo->picture_type = cur_frame_desc->gop_info->frame_type;
pPicInfo->pic_order_cnt = cur_frame_desc->gop_info->picture_order_count;
pPicInfo->frame_num = cur_frame_desc->gop_info->frame_num;
pPicInfo->slice.frame_num = cur_frame_desc->gop_info->frame_num;
pPicInfo->idr_pic_id = cur_frame_desc->gop_info->idr_pic_id;
// Insert new headers on IDR
if( pPicInfo->picture_type == PIPE_H2645_ENC_PICTURE_TYPE_IDR )
{
if( pPicInfo->seq.num_temporal_layers > 1 )
{
struct pipe_enc_raw_header header_sei = { /* type */ 6 /*NAL_TYPE_SEI*/ };
util_dynarray_append( &pPicInfo->raw_headers, header_sei );
}
struct pipe_enc_raw_header header_sps = { /* type */ PIPE_H264_NAL_SPS };
util_dynarray_append( &pPicInfo->raw_headers, header_sps );
struct pipe_enc_raw_header header_pps = { /* type */ PIPE_H264_NAL_PPS };
util_dynarray_append( &pPicInfo->raw_headers, header_pps );
}
// Always insert AUD
struct pipe_enc_raw_header header_aud = { /* type */ PIPE_H264_NAL_AUD };
util_dynarray_append( &pPicInfo->raw_headers, header_aud );
// Always insert svc prefix slice header nal if num_temporal_layers > 1
if( pPicInfo->seq.num_temporal_layers > 1 )
{
struct pipe_enc_raw_header header_svc_prefix = { /* type */ 14 /*NAL_TYPE_PREFIX*/ };
util_dynarray_append( &pPicInfo->raw_headers, header_svc_prefix );
}
pPicInfo->not_referenced = ( cur_frame_desc->gop_info->reference_type == frame_descriptor_reference_type_none );
pPicInfo->is_ltr = ( cur_frame_desc->gop_info->reference_type == frame_descriptor_reference_type_long_term );
pPicInfo->ltr_index = cur_frame_desc->gop_info->ltr_index;
pDX12EncodeContext->longTermReferenceFrameInfo = cur_frame_desc->gop_info->long_term_reference_frame_info;
pPicInfo->num_ref_idx_l0_active_minus1 =
static_cast<uint32_t>( std::max( 0, static_cast<int32_t>( cur_frame_desc->l0_reference_list.size() - 1 ) ) );
pPicInfo->num_ref_idx_l1_active_minus1 = 0u;
// Pass valid DPB entries on all frames (even for I/IDR contains curr recon pic buffer)
pPicInfo->dpb_size = static_cast<uint8_t>( cur_frame_desc->dpb_snapshot.size() );
assert( pPicInfo->dpb_size <= PIPE_H264_MAX_DPB_SIZE );
memcpy( &pPicInfo->dpb[0], cur_frame_desc->dpb_snapshot.data(), sizeof( cur_frame_desc->dpb_snapshot[0] ) * pPicInfo->dpb_size );
for( unsigned i = 0; i < pPicInfo->dpb_size; i++ )
{
if( pPicInfo->dpb[i].pic_order_cnt == cur_frame_desc->gop_info->picture_order_count )
{
pPicInfo->dpb_curr_pic = static_cast<uint8_t>( i );
}
}
if( ( pPicInfo->picture_type == PIPE_H2645_ENC_PICTURE_TYPE_P ) || ( pPicInfo->picture_type == PIPE_H2645_ENC_PICTURE_TYPE_B ) )
{
for( uint32_t i = 0; i <= pPicInfo->num_ref_idx_l0_active_minus1; i++ )
pPicInfo->ref_list0[i] = cur_frame_desc->l0_reference_list[i];
if( cur_frame_desc->ref_list0_mod_operations.size() > PIPE_H264_MAX_NUM_LIST_REF )
{
assert( false );
hr = E_UNEXPECTED;
goto done;
}
pPicInfo->slice.num_ref_list0_mod_operations = static_cast<uint8_t>( cur_frame_desc->ref_list0_mod_operations.size() );
for( uint32_t i = 0; i < pPicInfo->slice.num_ref_list0_mod_operations; i++ )
pPicInfo->slice.ref_list0_mod_operations[i] = cur_frame_desc->ref_list0_mod_operations[i];
}
if( cur_frame_desc->mmco_operations.size() > PIPE_H264_MAX_NUM_LIST_REF )
{
assert( false );
hr = E_UNEXPECTED;
goto done;
}
pPicInfo->slice.num_ref_pic_marking_operations = static_cast<uint8_t>( cur_frame_desc->mmco_operations.size() );
if( pPicInfo->slice.num_ref_pic_marking_operations > 0 )
{
pPicInfo->slice.adaptive_ref_pic_marking_mode_flag = 1;
for( uint32_t i = 0; i < pPicInfo->slice.num_ref_pic_marking_operations; i++ )
pPicInfo->slice.ref_pic_marking_operations[i] = cur_frame_desc->mmco_operations[i];
}
if( m_uiDirtyRectEnabled )
{
if( dirtyRectFrameNumSet )
{
DIRTYRECT_INFO *pDirtyRectInfo = (DIRTYRECT_INFO *) m_pDirtyRectBlob.data();
UINT uiNumDirtyRects = min( pDirtyRectInfo->NumDirtyRects, (UINT) PIPE_ENC_DIRTY_RECTS_NUM_MAX );
if( uiNumDirtyRects > 0 )
{
bool foundSurfaceIndex = false;
uint8_t surfaceIndex = UINT8_MAX;
uint32_t search = dirtyRectFrameNum - 1;
if( m_EncoderCapabilities.m_HWSupportDirtyRects.bits.supports_require_auto_slice_mode )
{
pPicInfo->slice_mode = PIPE_VIDEO_SLICE_MODE_AUTO;
}
CHECKHR_GOTO( ValidateDirtyRects( pDX12EncodeContext, pDirtyRectInfo ), done );
assert( cur_frame_desc->dirty_rect_frame_num.size() == cur_frame_desc->dpb_snapshot.size() );
uint8_t dpbIndex = pPicInfo->ref_list0[0];
if( search == cur_frame_desc->dirty_rect_frame_num[dpbIndex] )
{
foundSurfaceIndex = true;
surfaceIndex = dpbIndex;
}
else
{
if( m_uiDirtyRectEnabled == DIRTY_RECT_MODE_IGNORE_FRAME_NUM )
{
debug_printf( "[dx12 hmft 0x%p] dirty rect frame num doesn't match, continue use\n", this );
foundSurfaceIndex = true;
surfaceIndex = dpbIndex;
}
else
{
debug_printf( "[dx12 hmft 0x%p] dirty rect frame num doesn't match, ignore dirty rect\n", this );
}
}
if( foundSurfaceIndex )
{
pPicInfo->dirty_info.input_mode = PIPE_ENC_DIRTY_INFO_INPUT_MODE_RECTS;
pPicInfo->dirty_info.dpb_reference_index = surfaceIndex;
pPicInfo->dirty_info.full_frame_skip = false;
pPicInfo->dirty_info.num_rects = uiNumDirtyRects;
for( UINT i = 0; i < uiNumDirtyRects; i++ )
{
pPicInfo->dirty_info.rects[i].top = pDirtyRectInfo->DirtyRects[i].top;
pPicInfo->dirty_info.rects[i].bottom = pDirtyRectInfo->DirtyRects[i].bottom;
pPicInfo->dirty_info.rects[i].left = pDirtyRectInfo->DirtyRects[i].left;
pPicInfo->dirty_info.rects[i].right = pDirtyRectInfo->DirtyRects[i].right;
}
}
}
}
}
// ----------------------------
// Move Regions (motion hints)
// ----------------------------
// Always reset per-frame optional hint structures
memset( &pPicInfo->move_info, 0, sizeof( pPicInfo->move_info ) );
if( moveRegionFrameNumSet && m_EncoderCapabilities.m_HWSupportMoveRects.bits.max_motion_hints > 0 &&
m_EncoderCapabilities.m_HWSupportMoveRects.bits.supports_precision_full_pixel &&
pPicInfo->picture_type == PIPE_H2645_ENC_PICTURE_TYPE_P ) // P-frames only
{
uint32_t current_poc = pPicInfo->pic_order_cnt;
// Validate frame number first
// Due to the way the move region frame number is generated, there can be cases where the frame number doesn't match the
// current POC. In those cases, we will log a warning but still try to use the move regions.
if( m_uiGopSize > 0 && ( moveRegionFrameNum % m_uiGopSize ) != current_poc )
{
debug_printf( "[dx12 hmft 0x%p] WARNING: MoveRegions frame mismatch (MRFN=%u, cur POC=%u, GOPSize=%u)\n",
this,
moveRegionFrameNum,
current_poc,
m_uiGopSize );
}
{
MOVEREGION_INFO *pMoveInfo = reinterpret_cast<MOVEREGION_INFO *>( m_pMoveRegionBlob.data() );
uint32_t maxRects = m_EncoderCapabilities.m_HWSupportMoveRects.bits.max_motion_hints;
uint32_t numRects = std::min( pMoveInfo->NumMoveRegions, maxRects );
if( numRects > 0 )
{
uint8_t surfaceIndex = pPicInfo->ref_list0[0];
if( surfaceIndex == PIPE_H2645_LIST_REF_INVALID_ENTRY )
{
debug_printf( "[dx12 hmft 0x%p] MoveRegions: invalid L0 reference, ignoring\n", this );
}
else
{
pPicInfo->move_info.input_mode = PIPE_ENC_MOVE_INFO_INPUT_MODE_RECTS;
pPicInfo->move_info.num_rects = numRects;
pPicInfo->move_info.dpb_reference_index = surfaceIndex;
pPicInfo->move_info.precision = PIPE_ENC_MOVE_INFO_PRECISION_UNIT_FULL_PIXEL;
for( uint32_t i = 0; i < numRects; i++ )
{
const MOVE_RECT &mr = pMoveInfo->MoveRegions[i];
pPicInfo->move_info.rects[i].source_point.x = mr.SourcePoint.x;
pPicInfo->move_info.rects[i].source_point.y = mr.SourcePoint.y;
pPicInfo->move_info.rects[i].dest_rect.left = mr.DestRect.left;
pPicInfo->move_info.rects[i].dest_rect.top = mr.DestRect.top;
pPicInfo->move_info.rects[i].dest_rect.right = mr.DestRect.right;
pPicInfo->move_info.rects[i].dest_rect.bottom = mr.DestRect.bottom;
}
}
}
}
}
pPicInfo->gpu_stats_qp_map = pDX12EncodeContext->pPipeResourceQPMapStats;
pPicInfo->gpu_stats_satd_map = pDX12EncodeContext->pPipeResourceSATDMapStats;
pPicInfo->gpu_stats_rc_bitallocation_map = pDX12EncodeContext->pPipeResourceRCBitAllocMapStats;
pPicInfo->gpu_stats_psnr = pDX12EncodeContext->pPipeResourcePSNRStats;
// Quality vs speed
// PIPE: The quality level range is [1..m_uiMaxHWSupportedQualityVsSpeedLevel]
// A lower value means higher quality (slower encoding speed), and a value of 1 represents the highest quality
// (slowest encoding speed). MF Range: 0 Lower quality, faster encoding. - 100 Higher quality, slower encoding.
pPicInfo->quality_modes.level = std::max(
1u,
static_cast<uint32_t>( std::ceil( ( static_cast<float>( 100 - m_uiQualityVsSpeed ) / 100.0f ) *
static_cast<double>( m_EncoderCapabilities.m_uiMaxHWSupportedQualityVsSpeedLevel ) ) ) );
if( m_pPipeVideoCodec->two_pass.enable && ( m_pPipeVideoCodec->two_pass.pow2_downscale_factor > 0 ) )
{
pPicInfo->twopass_frame_config.downscaled_source = pDX12EncodeContext->pDownscaledTwoPassPipeVideoBuffer;
pPicInfo->twopass_frame_config.skip_1st_pass = false;
}
// Slices data
height_in_blocks = ( ( pDX12EncodeContext->pPipeVideoBuffer->height + 15 ) >> 4 );
width_in_blocks = ( ( pDX12EncodeContext->pPipeVideoBuffer->width + 15 ) >> 4 );
if( m_bSliceControlModeSet && m_bSliceControlSizeSet )
{
// dirty rect is incompatible with Slice Mode, when auto mode is on
if( !( m_uiDirtyRectEnabled && m_EncoderCapabilities.m_HWSupportDirtyRects.bits.supports_require_auto_slice_mode ) )
{
if( SLICE_CONTROL_MODE_MB == m_uiSliceControlMode )
{
pPicInfo->slice_mode = PIPE_VIDEO_SLICE_MODE_BLOCKS;
uint32_t blocks_per_slice = m_uiSliceControlSize;
pPicInfo->num_slice_descriptors = static_cast<uint32_t>(
std::ceil( ( height_in_blocks * width_in_blocks ) / static_cast<double>( blocks_per_slice ) ) );
uint32_t slice_starting_mb = 0;
CHECKBOOL_GOTO( pPicInfo->num_slice_descriptors <= m_EncoderCapabilities.m_uiMaxHWSupportedMaxSlices,
MF_E_UNEXPECTED,
done );
CHECKBOOL_GOTO( pPicInfo->num_slice_descriptors >= 1, MF_E_UNEXPECTED, done );
uint32_t total_blocks = height_in_blocks * width_in_blocks;
uint32_t i = 0;
for( i = 0; i < pPicInfo->num_slice_descriptors - 1; i++ )
{
pPicInfo->slices_descriptors[i].macroblock_address = slice_starting_mb;
pPicInfo->slices_descriptors[i].num_macroblocks = blocks_per_slice;
slice_starting_mb += blocks_per_slice;
}
pPicInfo->slices_descriptors[i].macroblock_address = slice_starting_mb;
pPicInfo->slices_descriptors[i].num_macroblocks = total_blocks - slice_starting_mb;
}
else if( SLICE_CONTROL_MODE_BITS == m_uiSliceControlMode )
{
pPicInfo->slice_mode = PIPE_VIDEO_SLICE_MODE_MAX_SLICE_SIZE;
pPicInfo->max_slice_bytes = m_uiSliceControlSize / 8; /* bits to bytes */
}
else if( SLICE_CONTROL_MODE_MB_ROW == m_uiSliceControlMode )
{
pPicInfo->slice_mode = PIPE_VIDEO_SLICE_MODE_BLOCKS;
uint32_t blocks_per_slice = m_uiSliceControlSize * width_in_blocks;
pPicInfo->num_slice_descriptors = static_cast<uint32_t>(
std::ceil( ( height_in_blocks * width_in_blocks ) / static_cast<double>( blocks_per_slice ) ) );
uint32_t slice_starting_mb = 0;
CHECKBOOL_GOTO( pPicInfo->num_slice_descriptors <= m_EncoderCapabilities.m_uiMaxHWSupportedMaxSlices,
MF_E_UNEXPECTED,
done );
CHECKBOOL_GOTO( pPicInfo->num_slice_descriptors >= 1, MF_E_UNEXPECTED, done );
uint32_t total_blocks = height_in_blocks * width_in_blocks;
uint32_t i = 0;
for( i = 0; i < pPicInfo->num_slice_descriptors - 1; i++ )
{
pPicInfo->slices_descriptors[i].macroblock_address = slice_starting_mb;
pPicInfo->slices_descriptors[i].num_macroblocks = blocks_per_slice;
slice_starting_mb += blocks_per_slice;
}
pPicInfo->slices_descriptors[i].macroblock_address = slice_starting_mb;
pPicInfo->slices_descriptors[i].num_macroblocks = total_blocks - slice_starting_mb;
}
}
}
// Intra refresh (needs to be set after slices are set above)
if( m_uiIntraRefreshMode > 0 )
{
// dirty rect is incompatible with Intra Refresh when auto mode is on
if( !( m_uiDirtyRectEnabled && !m_EncoderCapabilities.m_HWSupportDirtyRects.bits.supports_require_auto_slice_mode ) )
{
// Use current encoder slice config for when NOT doing an intra-refresh wave
intra_refresh_slices_config non_ir_wave_slices_config = {};
CHECKBOOL_GOTO( ( m_EncoderCapabilities.m_HWSupportsIntraRefreshModes != PIPE_VIDEO_ENC_INTRA_REFRESH_NONE ),
MF_E_UNEXPECTED,
done );
non_ir_wave_slices_config.slice_mode = pPicInfo->slice_mode;
non_ir_wave_slices_config.num_slice_descriptors = pPicInfo->num_slice_descriptors;
memcpy( non_ir_wave_slices_config.slices_descriptors,
pPicInfo->slices_descriptors,
sizeof( non_ir_wave_slices_config.slices_descriptors ) );
non_ir_wave_slices_config.max_slice_bytes = pPicInfo->max_slice_bytes;
// Initialize IR tracker
if( !dynamic_cast<intra_refresh_tracker_row_h264 *>( m_pGOPTracker ) )
{
if( m_uiIntraRefreshSize > m_uiGopSize && m_uiGopSize != 0 )
{ // Infinite
m_uiIntraRefreshSize = m_uiGopSize;
}
CHECKBOOL_GOTO( m_uiIntraRefreshSize <= m_EncoderCapabilities.m_uiMaxHWSupportedIntraRefreshSize,
MF_E_UNEXPECTED,
done );
m_pGOPTracker = new intra_refresh_tracker_row_h264( m_pGOPTracker /* inject current pic tracker */,
m_uiIntraRefreshSize,
non_ir_wave_slices_config,
height_in_blocks * width_in_blocks );
CHECKNULL_GOTO( m_pGOPTracker, E_OUTOFMEMORY, done );
}
// Set pipe IR params
const intra_refresh_tracker_frame_descriptor_h264 *intra_refresh_frame_desc =
(const intra_refresh_tracker_frame_descriptor_h264 *) m_pGOPTracker->get_frame_descriptor();
pPicInfo->intra_refresh = intra_refresh_frame_desc->intra_refresh_params;
// Override slice params (as per DX12 spec for IR)
pPicInfo->slice_mode = intra_refresh_frame_desc->slices_config.slice_mode;
pPicInfo->num_slice_descriptors = intra_refresh_frame_desc->slices_config.num_slice_descriptors;
memcpy( pPicInfo->slices_descriptors,
intra_refresh_frame_desc->slices_config.slices_descriptors,
sizeof( intra_refresh_frame_desc->slices_config.slices_descriptors ) );
pPicInfo->max_slice_bytes = intra_refresh_frame_desc->slices_config.max_slice_bytes;
}
}
// Rate control
rate_ctrl_active_layer_index = cur_frame_desc->gop_info->temporal_id;
pPicInfo->rate_ctrl[rate_ctrl_active_layer_index].fill_data_enable = true;
pPicInfo->rate_ctrl[rate_ctrl_active_layer_index].skip_frame_enable = false;
if( m_uiRateControlMode == eAVEncCommonRateControlMode_CBR )
{
pPicInfo->rate_ctrl[rate_ctrl_active_layer_index].rate_ctrl_method = PIPE_H2645_ENC_RATE_CONTROL_METHOD_CONSTANT;
pPicInfo->rate_ctrl[rate_ctrl_active_layer_index].target_bitrate = m_bMeanBitRateSet ? m_uiMeanBitRate : m_uiOutputBitrate;
pPicInfo->rate_ctrl[rate_ctrl_active_layer_index].peak_bitrate = m_bMeanBitRateSet ? m_uiMeanBitRate : m_uiOutputBitrate;
}
else if( m_uiRateControlMode == eAVEncCommonRateControlMode_Quality )
{
#ifdef MF_MAP_QUALITY_CONTROL_MODE_TO_QVBR
// NOTE: MF CodecAPI doesn't currently have a rate-control mode that maps well to DX12 QVBR
/* Attempt using DX12 QVBR */
if( encoder_caps.m_bHWSupportsQualityVBRRateControlMode )
{
pPicInfo->rate_ctrl[rate_ctrl_active_layer_index].rate_ctrl_method = PIPE_H2645_ENC_RATE_CONTROL_METHOD_QUALITY_VARIABLE;
pPicInfo->rate_ctrl[rate_ctrl_active_layer_index].target_bitrate = m_bMeanBitRateSet ? m_uiMeanBitRate : m_uiOutputBitrate;
pPicInfo->rate_ctrl[rate_ctrl_active_layer_index].peak_bitrate = m_bPeakBitRateSet ? m_uiPeakBitRate : m_uiOutputBitrate;
pPicInfo->rate_ctrl[rate_ctrl_active_layer_index].vbr_quality_factor = ( ( ( 100 - m_uiQuality[0] ) / 100.0 ) * 50 ) + 1;
pPicInfo->rate_ctrl[rate_ctrl_active_layer_index].app_requested_hrd_buffer = 1;
pPicInfo->rate_ctrl[rate_ctrl_active_layer_index].vbv_buffer_size =
pPicInfo->rate_ctrl[rate_ctrl_active_layer_index].target_bitrate /
( ( m_FrameRate.Numerator / m_FrameRate.Denominator ) * 5.5 );
pPicInfo->rate_ctrl[rate_ctrl_active_layer_index].vbv_buf_initial_size =
pPicInfo->rate_ctrl[rate_ctrl_active_layer_index].vbv_buffer_size;
}
else
#endif // MF_MAP_QUALITY_CONTROL_MODE_TO_QVBR
{
/* Emulate with CQP mode if QVBR not available in HW */
pPicInfo->rate_ctrl[rate_ctrl_active_layer_index].rate_ctrl_method = PIPE_H2645_ENC_RATE_CONTROL_METHOD_DISABLE;
if( m_bEncodeQPSet )
{
pPicInfo->quant_i_frames = m_uiEncodeFrameTypeIQP[rate_ctrl_active_layer_index];
pPicInfo->quant_p_frames = m_uiEncodeFrameTypePQP[rate_ctrl_active_layer_index];
pPicInfo->quant_b_frames = m_uiEncodeFrameTypeBQP[rate_ctrl_active_layer_index];
}
else
{
pPicInfo->quant_i_frames = m_uiEncodeFrameTypeIQP[0];
pPicInfo->quant_p_frames = m_uiEncodeFrameTypePQP[0];
pPicInfo->quant_b_frames = m_uiEncodeFrameTypeBQP[0];
}
}
}
else if( m_uiRateControlMode == eAVEncCommonRateControlMode_UnconstrainedVBR )
{
pPicInfo->rate_ctrl[rate_ctrl_active_layer_index].rate_ctrl_method = PIPE_H2645_ENC_RATE_CONTROL_METHOD_VARIABLE;
pPicInfo->rate_ctrl[rate_ctrl_active_layer_index].target_bitrate = m_bMeanBitRateSet ? m_uiMeanBitRate : m_uiOutputBitrate;
pPicInfo->rate_ctrl[rate_ctrl_active_layer_index].peak_bitrate =
/* emulate "unconstrained" with 5x the target bitrate*/
m_bPeakBitRateSet ? m_uiPeakBitRate : ( 5 * pPicInfo->rate_ctrl[rate_ctrl_active_layer_index].target_bitrate );
}
else if( m_uiRateControlMode == eAVEncCommonRateControlMode_PeakConstrainedVBR && m_bPeakBitRateSet )
{
pPicInfo->rate_ctrl[rate_ctrl_active_layer_index].rate_ctrl_method = PIPE_H2645_ENC_RATE_CONTROL_METHOD_VARIABLE;
pPicInfo->rate_ctrl[rate_ctrl_active_layer_index].target_bitrate = m_bMeanBitRateSet ? m_uiMeanBitRate : m_uiOutputBitrate;
pPicInfo->rate_ctrl[rate_ctrl_active_layer_index].peak_bitrate =
m_bPeakBitRateSet ? m_uiPeakBitRate : pPicInfo->rate_ctrl[rate_ctrl_active_layer_index].target_bitrate;
}
pPicInfo->rate_ctrl[rate_ctrl_active_layer_index].vbv_buffer_size =
pPicInfo->rate_ctrl[rate_ctrl_active_layer_index].target_bitrate;
if( ( pPicInfo->rate_ctrl[rate_ctrl_active_layer_index].rate_ctrl_method != PIPE_H2645_ENC_RATE_CONTROL_METHOD_CONSTANT ) &&
( pPicInfo->rate_ctrl[rate_ctrl_active_layer_index].target_bitrate < 2000000u ) )
pPicInfo->rate_ctrl[rate_ctrl_active_layer_index].vbv_buffer_size =
(unsigned) std::min( 2000000.0, pPicInfo->rate_ctrl[rate_ctrl_active_layer_index].target_bitrate * 2.75 );
// Optional Rate control params for all RC modes
pPicInfo->rate_ctrl[rate_ctrl_active_layer_index].app_requested_qp_range = m_bMinQPSet || m_bMaxQPSet;
pPicInfo->rate_ctrl[rate_ctrl_active_layer_index].min_qp = m_uiMinQP;
pPicInfo->rate_ctrl[rate_ctrl_active_layer_index].max_qp = m_uiMaxQP;
if( m_bBufferSizeSet )
{
pPicInfo->rate_ctrl[rate_ctrl_active_layer_index].app_requested_hrd_buffer = true;
pPicInfo->rate_ctrl[rate_ctrl_active_layer_index].vbv_buffer_size = m_uiBufferSize;
pPicInfo->rate_ctrl[rate_ctrl_active_layer_index].vbv_buf_initial_size = m_uiBufferSize;
}
if( m_bBufferInLevelSet )
{
pPicInfo->rate_ctrl[rate_ctrl_active_layer_index].app_requested_hrd_buffer = true;
pPicInfo->rate_ctrl[rate_ctrl_active_layer_index].vbv_buf_initial_size = m_uiBufferInLevel;
}
// Frame Rate
pPicInfo->rate_ctrl[rate_ctrl_active_layer_index].frame_rate_num = m_FrameRate.Numerator;
pPicInfo->rate_ctrl[rate_ctrl_active_layer_index].frame_rate_den = m_FrameRate.Denominator;
// Spatial Quantization
pPicInfo->rate_ctrl[rate_ctrl_active_layer_index].spatial_adaptive_quantization_strength =
m_bVideoEnableSpatialAdaptiveQuantization ? 1u : 0u;
debug_printf( "[dx12 hmft 0x%p] MFT frontend submission - POC %d picture_type %s num_slice_descriptors %d\n",
this,
pPicInfo->pic_order_cnt,
ConvertPipeH2645FrameTypeToString( pPicInfo->picture_type ),
pPicInfo->num_slice_descriptors );
done:
if( FAILED( hr ) )
{
MFE_ERROR( "[dx12 hmft 0x%p] PrepareForEncodeHelper - hr=0x%x", this, hr );
}
return hr;
}
// generate SPS and PPS headers for codec private data (MF_MT_MPEG_SEQUENCE_HEADER)
HRESULT
CDX12EncHMFT::GetCodecPrivateData( LPBYTE pSPSPPSData, DWORD dwSPSPPSDataLen, LPDWORD lpdwSPSPPSDataLen )
{
HRESULT hr = S_OK;
UINT alignedWidth = static_cast<UINT>( std::ceil( m_uiOutputWidth / 16.0 ) ) * 16;
UINT alignedHeight = static_cast<UINT>( std::ceil( m_uiOutputHeight / 16.0 ) ) * 16;
int ret = EINVAL;
unsigned buf_size = dwSPSPPSDataLen;
const uint32_t intra_period = m_uiGopSize;
const uint32_t ip_period = m_uiBFrameCount + 1;
pipe_h264_enc_picture_desc h264_pic_desc = {};
ComputeCroppingRect( alignedWidth,
alignedHeight,
m_uiOutputWidth,
m_uiOutputHeight,
m_outputPipeProfile,
m_bFrameCroppingFlag,
m_uiFrameCropRightOffset,
m_uiFrameCropBottomOffset );
UpdateH264EncPictureDesc( &h264_pic_desc, intra_period, ip_period );
h264_pic_desc.pic_order_cnt = 0; // cur_frame_desc->gop_info->picture_order_count;
h264_pic_desc.picture_type = PIPE_H2645_ENC_PICTURE_TYPE_IDR; // cur_frame_desc->gop_info->frame_type;
h264_pic_desc.frame_num = 0; // cur_frame_desc->gop_info->frame_num;
h264_pic_desc.idr_pic_id = 0; // cur_frame_desc->gop_info->idr_pic_id;
// Rate Control
if( m_uiRateControlMode == eAVEncCommonRateControlMode_CBR )
{
h264_pic_desc.rate_ctrl[0].rate_ctrl_method = PIPE_H2645_ENC_RATE_CONTROL_METHOD_CONSTANT;
h264_pic_desc.rate_ctrl[0].target_bitrate = m_bMeanBitRateSet ? m_uiMeanBitRate : m_uiOutputBitrate;
h264_pic_desc.rate_ctrl[0].peak_bitrate = m_bMeanBitRateSet ? m_uiMeanBitRate : m_uiOutputBitrate;
}
else
{
h264_pic_desc.rate_ctrl[0].rate_ctrl_method = PIPE_H2645_ENC_RATE_CONTROL_METHOD_DISABLE;
h264_pic_desc.rate_ctrl[0].vbr_quality_factor =
static_cast<unsigned int>( ( ( ( 100 - m_uiQuality[0] ) / 100.0 ) * 50 ) + 1 );
h264_pic_desc.rate_ctrl[0].frame_rate_num = m_FrameRate.Numerator;
h264_pic_desc.rate_ctrl[0].frame_rate_den = m_FrameRate.Denominator;
}
// Set default valid CQP 26 with 30 fps, doesn't affect header building
// but needs to be valid, otherwise some drivers segfault
h264_pic_desc.quant_i_frames = m_uiEncodeFrameTypeIQP[0];
h264_pic_desc.quant_p_frames = m_uiEncodeFrameTypePQP[0];
h264_pic_desc.quant_b_frames = m_uiEncodeFrameTypeBQP[0];
ret = m_pPipeVideoCodec->get_encode_headers( m_pPipeVideoCodec, &h264_pic_desc.base, pSPSPPSData, &buf_size );
CHECKHR_GOTO( ConvertErrnoRetToHR( ret ), done );
*lpdwSPSPPSDataLen = (DWORD) buf_size;
done:
if( FAILED( hr ) )
{
MFE_ERROR( "[dx12 hmft 0x%p] GetCodecPrivateData - hr=0x%x", this, hr );
}
return hr;
}
// utility function to convert level to eAVEncH264VLevel
static HRESULT
ConvertLevelToAVEncH264VLevel( UINT32 uiLevel, eAVEncH264VLevel &level )
{
HRESULT hr = S_OK;
level = eAVEncH264VLevel5;
switch( uiLevel )
{
case 0: // possibly HLK is using 0 as auto.
case(UINT32) -1: // auto
level = eAVEncH264VLevel5;
break;
case 10:
level = eAVEncH264VLevel1;
break;
case 11:
level = eAVEncH264VLevel1_1;
break;
case 12:
level = eAVEncH264VLevel1_2;
break;
case 13:
level = eAVEncH264VLevel1_3;
break;
case 20:
level = eAVEncH264VLevel2;
break;
case 21:
level = eAVEncH264VLevel2_1;
break;
case 22:
level = eAVEncH264VLevel2_2;
break;
case 30:
level = eAVEncH264VLevel3;
break;
case 31:
level = eAVEncH264VLevel3_1;
break;
case 32:
level = eAVEncH264VLevel3_2;
break;
case 40:
level = eAVEncH264VLevel4;
break;
case 41:
level = eAVEncH264VLevel4_1;
break;
case 42:
level = eAVEncH264VLevel4_2;
break;
case 50:
level = eAVEncH264VLevel5;
break;
case 51:
level = eAVEncH264VLevel5_1;
break;
case 52:
level = eAVEncH264VLevel5_2;
break;
case 60:
level = eAVEncH264VLevel6;
break;
case 61:
level = eAVEncH264VLevel6_1;
break;
case 62:
level = eAVEncH264VLevel6_2;
break;
default:
hr = MF_E_INVALIDMEDIATYPE;
break;
}
return hr;
}
/* get max mb processing rate (MaxMBPS) with current level from table A-1 */
static int
LevelToMaxMBPS( eAVEncH264VLevel level_idc )
{
int maxMBPS = 0;
switch( level_idc )
{
case eAVEncH264VLevel1:
maxMBPS = 1485;
break;
case eAVEncH264VLevel1_b:
maxMBPS = 1485;
break;
// need workaround for 1_1 and 1_b as both enum value is 11
/*
case eAVEncH264VLevel1_1:
maxMBPS = 3000;
break;
*/
case eAVEncH264VLevel1_2:
maxMBPS = 6000;
break;
case eAVEncH264VLevel1_3:
maxMBPS = 11880;
break;
case eAVEncH264VLevel2:
maxMBPS = 11880;
break;
case eAVEncH264VLevel2_1:
maxMBPS = 19800;
break;
case eAVEncH264VLevel2_2:
maxMBPS = 20250;
break;
case eAVEncH264VLevel3:
maxMBPS = 40500;
break;
case eAVEncH264VLevel3_1:
maxMBPS = 108000;
break;
case eAVEncH264VLevel3_2:
maxMBPS = 216000;
break;
case eAVEncH264VLevel4:
maxMBPS = 245760;
break;
case eAVEncH264VLevel4_1:
maxMBPS = 245760;
break;
case eAVEncH264VLevel4_2:
maxMBPS = 522240;
break;
case eAVEncH264VLevel5:
maxMBPS = 589824;
break;
case eAVEncH264VLevel5_1:
maxMBPS = 983040;
break;
case eAVEncH264VLevel5_2:
maxMBPS = 2073600;
break;
case eAVEncH264VLevel6:
maxMBPS = 4177920;
break;
case eAVEncH264VLevel6_1:
maxMBPS = 8355840;
break;
case eAVEncH264VLevel6_2:
maxMBPS = 16711680;
break;
}
return maxMBPS;
}
/* get max frame size (MaxFS) with current level from table A-1 */
static int
LevelToMaxFS( eAVEncH264VLevel level_idc )
{
int maxFS = 0;
switch( level_idc )
{
case eAVEncH264VLevel1:
maxFS = 99;
break;
case eAVEncH264VLevel1_b:
maxFS = 99;
break;
// need workaround for 1_1 and 1_b as both enum value is 11
/*
case eAVEncH264VLevel1_1:
maxFS = 396;
break;
*/
case eAVEncH264VLevel1_2:
maxFS = 396;
break;
case eAVEncH264VLevel1_3:
maxFS = 396;
break;
case eAVEncH264VLevel2:
maxFS = 396;
break;
case eAVEncH264VLevel2_1:
maxFS = 792;
break;
case eAVEncH264VLevel2_2:
maxFS = 1620;
break;
case eAVEncH264VLevel3:
maxFS = 1620;
break;
case eAVEncH264VLevel3_1:
maxFS = 3600;
break;
case eAVEncH264VLevel3_2:
maxFS = 5120;
break;
case eAVEncH264VLevel4:
maxFS = 8192;
break;
case eAVEncH264VLevel4_1:
maxFS = 8192;
break;
case eAVEncH264VLevel4_2:
maxFS = 8704;
break;
case eAVEncH264VLevel5:
maxFS = 22080;
break;
case eAVEncH264VLevel5_1:
maxFS = 36864;
break;
case eAVEncH264VLevel5_2:
maxFS = 36864;
break;
case eAVEncH264VLevel6:
maxFS = 139264;
break;
case eAVEncH264VLevel6_1:
maxFS = 139264;
break;
case eAVEncH264VLevel6_2:
maxFS = 139264;
break;
}
return maxFS;
}
// utility function to check the level retrieved from the media type
HRESULT
CDX12EncHMFT::CheckMediaTypeLevel(
IMFMediaType *pmt, int width, int height, const encoder_capabilities &encoderCapabilities, eAVEncH264VLevel *pLevel ) const
{
HRESULT hr = S_OK;
UINT32 uiLevel = (UINT32) -1;
uiLevel = MFGetAttributeUINT32( pmt, MF_MT_VIDEO_LEVEL, uiLevel );
enum eAVEncH264VLevel AVEncLevel;
int maxMBPS = 0;
int maxFS = 0;
const int PicWidthInMbs = static_cast<UINT>( std::ceil( width / 16.0 ) );
const int FrameHeightInMbs = static_cast<UINT>( std::ceil( height / 16.0 ) );
const double frameRate = (double) m_FrameRate.Numerator / m_FrameRate.Denominator;
CHECKHR_GOTO( ConvertLevelToAVEncH264VLevel( uiLevel, AVEncLevel ), done );
maxFS = LevelToMaxFS( AVEncLevel );
// TODO: add more checks according to A.3.1
if( PicWidthInMbs * FrameHeightInMbs > maxFS || (double) PicWidthInMbs > sqrt( (double) maxFS * 8 ) ||
(double) FrameHeightInMbs > sqrt( (double) maxFS * 8 ) )
{
debug_printf( "[dx12 hmft 0x%p] CheckMediaTypeLevel failed: PicWidthInMbs, FrameHeightInMbs combination exceeded max frame "
"size constraints "
"(maxFS). (PicWidthInMbs = %d, FrameHeightInMbs = %d, maxFS = %d)\n",
this,
PicWidthInMbs,
FrameHeightInMbs,
maxFS );
CHECKHR_GOTO( E_INVALIDARG, done );
}
maxMBPS = LevelToMaxMBPS( AVEncLevel );
if( frameRate > ( (double) maxMBPS / (double) ( PicWidthInMbs * FrameHeightInMbs ) ) )
{
debug_printf(
"[dx12 hmft 0x%p] CheckMediaTypeLevel failed: frame rate exceeded maximum mb per sec (maxMBPS) constraints. (frameRate = "
"%d/%d, maxMBPS = %d, PicWidthInMbs = %d, FrameHeightInMbs = %d)\n",
this,
m_FrameRate.Numerator,
m_FrameRate.Denominator,
maxMBPS,
PicWidthInMbs,
FrameHeightInMbs );
CHECKHR_GOTO( E_INVALIDARG, done );
}
if( pLevel )
{
*pLevel = AVEncLevel;
}
done:
return hr;
}
// utility function to get max dpb size from the level and image dimensions
static int
GetMaxDPBSize( int width, int height, eAVEncH264VLevel level_idc )
{
const int numMbX = static_cast<UINT>( std::ceil( width / 16.0 ) );
const int numMbY = static_cast<UINT>( std::ceil( height / 16.0 ) );
const int numMbs = ( numMbX ) * ( numMbY );
int maxDpbMbs = 0;
switch( level_idc )
{
case eAVEncH264VLevel1:
maxDpbMbs = 396;
break;
case eAVEncH264VLevel1_b:
maxDpbMbs = 396;
break;
// eAVEncH264VLevel1_b, eAVEncH264VLevel1_1 has the same value in codecapi.h
/*
case eAVEncH264VLevel1_1:
maxDpbMbs = 900
break;
*/
case eAVEncH264VLevel1_2:
maxDpbMbs = 2376;
break;
case eAVEncH264VLevel1_3:
maxDpbMbs = 2376;
break;
case eAVEncH264VLevel2:
maxDpbMbs = 2376;
break;
case eAVEncH264VLevel2_1:
maxDpbMbs = 4752;
break;
case eAVEncH264VLevel2_2:
maxDpbMbs = 8100;
break;
case eAVEncH264VLevel3:
maxDpbMbs = 8100;
break;
case eAVEncH264VLevel3_1:
maxDpbMbs = 18000;
break;
case eAVEncH264VLevel3_2:
maxDpbMbs = 20480;
break;
case eAVEncH264VLevel4:
maxDpbMbs = 32768;
break;
case eAVEncH264VLevel4_1:
maxDpbMbs = 32768;
break;
case eAVEncH264VLevel4_2:
maxDpbMbs = 34816;
break;
case eAVEncH264VLevel5:
maxDpbMbs = 110400;
break;
case eAVEncH264VLevel5_1:
maxDpbMbs = 184320;
break;
case eAVEncH264VLevel5_2:
maxDpbMbs = 184320;
break;
case eAVEncH264VLevel6:
maxDpbMbs = 696320;
break;
case eAVEncH264VLevel6_1:
maxDpbMbs = 696320;
break;
case eAVEncH264VLevel6_2:
maxDpbMbs = 696320;
break;
default:
UNREACHABLE( "unexpected level_idc" );
break;
}
int maxDpbSize = ( maxDpbMbs / numMbs );
return maxDpbSize;
}
// utility function to get max reference frames from hardware capabilities given image dimensions
UINT32
CDX12EncHMFT::GetMaxReferences( unsigned int width, unsigned int height )
{
UINT32 uiMaxReferences = m_EncoderCapabilities.m_uiMaxHWSupportedDPBCapacity;
if( width != 0 && height != 0 )
{
int maxDPBSize = GetMaxDPBSize( width, height, m_uiLevel );
uiMaxReferences = std::min( (int) m_EncoderCapabilities.m_uiMaxHWSupportedDPBCapacity, maxDPBSize );
}
return uiMaxReferences;
}
// utility function to create reference frame tracker which manages the DPB and operations involving it, e.g. frame type, LTR,
// temporal layers, etc.
HRESULT
CDX12EncHMFT::CreateGOPTracker( uint32_t textureWidth, uint32_t textureHeight )
{
HRESULT hr = S_OK;
uint32_t MaxHWL0Ref = m_EncoderCapabilities.m_uiMaxHWSupportedL0References;
MaxHWL0Ref = std::min( 1u, MaxHWL0Ref ); // we only support 1
std::unique_ptr<dpb_buffer_manager> upTwoPassDPBManager;
SAFE_DELETE( m_pGOPTracker );
// B Frame not supported
CHECKBOOL_GOTO( ( m_uiBFrameCount == 0 ), E_INVALIDARG, done );
// Requested number of temporal layers higher than max supported by HW
CHECKBOOL_GOTO( m_uiLayerCount <= m_EncoderCapabilities.m_uiMaxTemporalLayers, MF_E_OUT_OF_RANGE, done );
// Validate logic expression (m_uiLayerCount > 1) => (m_uiBFrameCount == 0)
CHECKBOOL_GOTO( ( m_uiLayerCount <= 1 ) || ( m_uiBFrameCount == 0 ),
E_INVALIDARG,
done ); // B frame with temporal layers not implemented
// Validate logic expression (m_uiMaxLongTermReferences != 0) => (m_uiBFrameCount == 0)
CHECKBOOL_GOTO( ( m_uiMaxLongTermReferences == 0 ) || ( m_uiBFrameCount == 0 ), MF_E_OUT_OF_RANGE, done );
// Ensure that the number of long term references is <= than the max supported by HW
// TODO: This check should be added at CodecAPI_AVEncVideoLTRBufferControl level and fail there too, but would need to setup
// global encoder cap first.
CHECKBOOL_GOTO( ( m_uiMaxLongTermReferences <= m_EncoderCapabilities.m_uiMaxHWSupportedLongTermReferences ),
MF_E_OUT_OF_RANGE,
done );
assert( m_uiBFrameCount == 0 );
assert( m_uiMaxNumRefFrame == m_pPipeVideoCodec->max_references );
assert( 1 + m_uiMaxLongTermReferences <= m_uiMaxNumRefFrame );
assert( MaxHWL0Ref <= m_uiMaxNumRefFrame );
if( m_pPipeVideoCodec->two_pass.enable && ( m_pPipeVideoCodec->two_pass.pow2_downscale_factor > 0 ) )
{
upTwoPassDPBManager = std::make_unique<dpb_buffer_manager>(
this,
m_pPipeVideoCodec,
static_cast<unsigned>( std::ceil( textureWidth / ( 1 << m_pPipeVideoCodec->two_pass.pow2_downscale_factor ) ) ),
static_cast<unsigned>( std::ceil( textureHeight / ( 1 << m_pPipeVideoCodec->two_pass.pow2_downscale_factor ) ) ),
ConvertProfileToFormat( m_pPipeVideoCodec->profile ),
m_pPipeVideoCodec->max_references + 1 /*curr pic*/ +
( m_bLowLatency ? 0 : MFT_INPUT_QUEUE_DEPTH ), /*MFT process input queue depth for delayed in flight recon pic release*/
hr );
CHECKHR_GOTO( hr, done );
}
m_pGOPTracker = new reference_frames_tracker_h264( this,
m_pPipeVideoCodec,
textureWidth,
textureHeight,
m_uiGopSize,
m_uiBFrameCount,
m_bLayerCountSet,
m_uiLayerCount,
m_bLowLatency,
MaxHWL0Ref,
m_pPipeVideoCodec->max_references,
m_uiMaxLongTermReferences,
m_gpuFeatureFlags.m_bH264SendUnwrappedPOC,
std::move( upTwoPassDPBManager ),
hr );
CHECKHR_GOTO( hr, done );
done:
if( FAILED( hr ) )
{
MFE_ERROR( "[dx12 hmft 0x%p] CreateGOPTracker - hr=0x%x", this, hr );
}
return hr;
}
#endif