/* * 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 "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( std::max( 0, static_cast( 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( 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( 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( 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( 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_EncoderCapabilities.m_bHWSupportsAppControlledSlicePartitioning && m_EncoderCapabilities.m_bHWSupportSliceModeAuto ) { pPicInfo->slice_mode = PIPE_VIDEO_SLICE_MODE_AUTO; } 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( 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( std::ceil( ( static_cast( 100 - m_uiQualityVsSpeed ) / 100.0f ) * static_cast( 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( std::ceil( ( height_in_blocks * width_in_blocks ) / static_cast( 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( std::ceil( ( height_in_blocks * width_in_blocks ) / static_cast( 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( 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( std::ceil( m_uiOutputWidth / 16.0 ) ) * 16; UINT alignedHeight = static_cast( 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( ( ( ( 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( std::ceil( width / 16.0 ) ); const int FrameHeightInMbs = static_cast( 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( std::ceil( width / 16.0 ) ); const int numMbY = static_cast( 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 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( this, m_pPipeVideoCodec, static_cast( std::ceil( textureWidth / ( 1 << m_pPipeVideoCodec->two_pass.pow2_downscale_factor ) ) ), static_cast( 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