-
Notifications
You must be signed in to change notification settings - Fork 13
encode: codec profile and level fixes #171
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
37ab751
6e2dab3
fe29120
a8bb6ac
f341c6b
b02b077
b8e01ef
deaad4d
4fba129
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -63,6 +63,13 @@ StdVideoH264LevelIdc EncoderConfigH264::DetermineLevel(uint8_t dpbSize, | |
| uint32_t _vbvBufferSize, | ||
| double frameRate) | ||
| { | ||
| uint32_t cpbBrNalFactor = 1200; | ||
|
|
||
| // Adjust cpbBrNalFactor depending on whether the High (or greater) profile | ||
| // is being used | ||
| if (profileIdc >= STD_VIDEO_H264_PROFILE_IDC_HIGH) { | ||
| cpbBrNalFactor = (profileIdc >= STD_VIDEO_H264_PROFILE_IDC_HIGH_444_PREDICTIVE) ? 4800 : 1500; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. would be nice to explain these values with the spec reference |
||
| } | ||
|
|
||
| uint32_t frameSizeInMbs = pic_width_in_mbs * pic_height_in_map_units; | ||
| for (uint32_t idx = 0; idx < levelLimitsSize; idx++) { | ||
|
|
@@ -71,8 +78,8 @@ StdVideoH264LevelIdc EncoderConfigH264::DetermineLevel(uint8_t dpbSize, | |
| if ((frameSizeInMbs) > ((uint32_t)levelLimits[idx].maxFS)) continue; | ||
| if ((frameSizeInMbs * numRefFrames * 384) > levelLimits[idx].maxDPB * 1024) continue; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This method I would recommend to calculate all of that after having called This can be performed by calling |
||
|
|
||
| if ((bitrate != 0) && (bitrate > ((uint32_t)levelLimits[idx].maxBR * 1200))) continue; | ||
| if ((_vbvBufferSize != 0) && (_vbvBufferSize > ((uint32_t)levelLimits[idx].maxCPB * 1200))) continue; | ||
| if ((bitrate != 0) && (bitrate > ((uint32_t)levelLimits[idx].maxBR * cpbBrNalFactor))) continue; | ||
| if ((_vbvBufferSize != 0) && (_vbvBufferSize > ((uint32_t)levelLimits[idx].maxCPB * cpbBrNalFactor))) continue; | ||
|
|
||
| return levelLimits[idx].level; | ||
| } | ||
|
|
@@ -310,11 +317,6 @@ bool EncoderConfigH264::InitSpsPpsParameters(StdVideoH264SequenceParameterSet *s | |
| sps->pic_order_cnt_type = STD_VIDEO_H264_POC_TYPE_2; | ||
| } | ||
|
|
||
| // FIXME: Check if the HW supports transform_8x8_mode_is_supported | ||
| // based on capabilities or profiles supported | ||
| const bool transform_8x8_mode_is_supported = true; | ||
| const bool bIsFastestPreset = false; | ||
|
|
||
| if (adaptiveTransformMode == ADAPTIVE_TRANSFORM_ENABLE) { | ||
| pps->flags.transform_8x8_mode_flag = true; | ||
| if ((profileIdc == STD_VIDEO_H264_PROFILE_IDC_BASELINE) || | ||
|
|
@@ -326,12 +328,9 @@ bool EncoderConfigH264::InitSpsPpsParameters(StdVideoH264SequenceParameterSet *s | |
| pps->flags.transform_8x8_mode_flag = false; | ||
| } else { | ||
| // Autoselect | ||
| if (!bIsFastestPreset || transform_8x8_mode_is_supported) { | ||
| if ((profileIdc == STD_VIDEO_H264_PROFILE_IDC_INVALID) || | ||
| (profileIdc >= STD_VIDEO_H264_PROFILE_IDC_HIGH)) { | ||
| // Unconditionally enable 8x8 transform | ||
| pps->flags.transform_8x8_mode_flag = true; | ||
| } | ||
| if (profileIdc >= STD_VIDEO_H264_PROFILE_IDC_HIGH) { | ||
| // Unconditionally enable 8x8 transform | ||
| pps->flags.transform_8x8_mode_flag = true; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This value has been already computed in use8x8Transform maybe we could keep this value and reuse it or have a method to calculate properly this value. I would be in favor to have a member value |
||
| } | ||
| } | ||
|
|
||
|
|
@@ -348,28 +347,6 @@ bool EncoderConfigH264::InitSpsPpsParameters(StdVideoH264SequenceParameterSet *s | |
| pps->flags.constrained_intra_pred_flag = true; | ||
| } | ||
|
|
||
| // If the profileIdc hasn't been specified, force set it now. | ||
| if (profileIdc == STD_VIDEO_H264_PROFILE_IDC_INVALID) { | ||
| profileIdc = STD_VIDEO_H264_PROFILE_IDC_BASELINE; | ||
|
|
||
| if (entropyCodingMode == ENTROPY_CODING_MODE_CABAC) { | ||
| profileIdc = STD_VIDEO_H264_PROFILE_IDC_MAIN; | ||
| } | ||
|
|
||
| if ((gopStructure.GetConsecutiveBFrameCount() > 0) || pps->flags.entropy_coding_mode_flag || !sps->flags.frame_mbs_only_flag) | ||
| profileIdc = STD_VIDEO_H264_PROFILE_IDC_MAIN; | ||
|
|
||
| if (pps->flags.transform_8x8_mode_flag) { | ||
| profileIdc = STD_VIDEO_H264_PROFILE_IDC_HIGH; | ||
| } | ||
|
|
||
| if ((sps->flags.qpprime_y_zero_transform_bypass_flag && | ||
| (rateControlMode == VK_VIDEO_ENCODE_RATE_CONTROL_MODE_DISABLED_BIT_KHR)) || | ||
| (sps->chroma_format_idc == STD_VIDEO_H264_CHROMA_FORMAT_IDC_444)) { | ||
| profileIdc = STD_VIDEO_H264_PROFILE_IDC_HIGH_444_PREDICTIVE; | ||
| } | ||
| } | ||
|
|
||
| sps->profile_idc = profileIdc; | ||
| sps->level_idc = levelIdc; | ||
|
|
||
|
|
@@ -491,42 +468,67 @@ VkResult EncoderConfigH264::InitDeviceCapabilities(const VulkanDeviceContext* vk | |
| return VK_SUCCESS; | ||
| } | ||
|
|
||
| int8_t EncoderConfigH264::InitDpbCount() | ||
| void EncoderConfigH264::InitProfileLevel() | ||
| { | ||
| dpbCount = 0; // TODO: What is the need for this? | ||
| // FIXME: Check if the HW supports transform_8x8_mode_is_supported | ||
| // based on capabilities or profiles supported | ||
| bool use8x8Transform = true; | ||
|
|
||
| if (adaptiveTransformMode == ADAPTIVE_TRANSFORM_ENABLE) { | ||
| use8x8Transform = true; | ||
| } else if (adaptiveTransformMode == ADAPTIVE_TRANSFORM_DISABLE) { | ||
| use8x8Transform = false; | ||
| } else { | ||
| // Autoselect | ||
| if ((profileIdc == STD_VIDEO_H264_PROFILE_IDC_INVALID) || | ||
| (profileIdc >= STD_VIDEO_H264_PROFILE_IDC_HIGH)) { | ||
| // Unconditionally enable 8x8 transform | ||
| use8x8Transform = true; | ||
| } | ||
| } | ||
|
|
||
| // If the profileIdc hasn't been specified, force set it now. | ||
| if (profileIdc == STD_VIDEO_H264_PROFILE_IDC_INVALID) { | ||
| profileIdc = STD_VIDEO_H264_PROFILE_IDC_BASELINE; | ||
|
|
||
| // Upgrade to MAIN profile if using B-frames or CABAC entropy coding | ||
| if ((gopStructure.GetConsecutiveBFrameCount() > 0) || (entropyCodingMode == ENTROPY_CODING_MODE_CABAC)) | ||
| profileIdc = STD_VIDEO_H264_PROFILE_IDC_MAIN; | ||
|
|
||
| if (use8x8Transform) { | ||
| profileIdc = STD_VIDEO_H264_PROFILE_IDC_HIGH; | ||
| } | ||
|
|
||
| // Upgrade to HIGH_444_PREDICTIVE for lossless encoding or 4:4:4 chroma | ||
| if ((tuningMode == VK_VIDEO_ENCODE_TUNING_MODE_LOSSLESS_KHR) || | ||
| (input.chromaSubsampling == VK_VIDEO_CHROMA_SUBSAMPLING_444_BIT_KHR)) { | ||
| profileIdc = STD_VIDEO_H264_PROFILE_IDC_HIGH_444_PREDICTIVE; | ||
| } | ||
| } | ||
|
|
||
| // spsInfo->level represents the smallest level that we require for the | ||
| // given stream. This level constrains the maximum size (in terms of | ||
| // number of frames) that the DPB can have. levelDpbSize is this maximum | ||
| // value. | ||
| uint32_t levelBitRate = ((rateControlMode != VK_VIDEO_ENCODE_RATE_CONTROL_MODE_DISABLED_BIT_KHR) && hrdBitrate == 0) | ||
| ? averageBitrate // constrained by avg bitrate | ||
| : hrdBitrate; // constrained by max bitrate | ||
|
|
||
| assert(pic_width_in_mbs > 0); | ||
| assert(pic_height_in_map_units > 0); | ||
| uint32_t frameSizeInMbs = pic_width_in_mbs * pic_height_in_map_units; | ||
|
|
||
| double frameRate = ((frameRateNumerator > 0) && (frameRateDenominator > 0)) | ||
| ? (double)frameRateNumerator / frameRateDenominator | ||
| : (double)FRAME_RATE_NUM_DEFAULT / (double)FRAME_RATE_DEN_DEFAULT; | ||
|
|
||
| // WAR for super HD resolution (bypass H264 level check for frame mode and use level 5.2) | ||
| if ((frameSizeInMbs > ((uint32_t)levelLimits[levelLimitsSize - 1].maxFS) || | ||
| ((frameSizeInMbs * frameRate) > ((uint32_t)levelLimits[levelLimitsSize - 1].maxMBPS)))) { | ||
| levelIdc = STD_VIDEO_H264_LEVEL_IDC_5_2; | ||
| } else { | ||
| // find lowest possible level | ||
| levelIdc = DetermineLevel(dpbCount, levelBitRate, vbvBufferSize, frameRate); | ||
| } | ||
| // find lowest possible level | ||
| levelIdc = DetermineLevel(dpbCount, levelBitRate, vbvBufferSize, frameRate); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. DetermineLevel might return STD_VIDEO_H264_LEVEL_IDC_INVALID which can lead to incorrect access to |
||
| } | ||
|
|
||
| int8_t EncoderConfigH264::InitDpbCount() | ||
| { | ||
| dpbCount = 0; // TODO: What is the need for this? | ||
|
|
||
| uint8_t levelDpbSize = (uint8_t)(((1024 * levelLimits[levelIdc].maxDPB)) / | ||
| ((pic_width_in_mbs * pic_height_in_map_units) * 384)); | ||
|
|
||
| // XXX: If the level is 5.2, it is highly likely that we forced it to that | ||
| // value as a WAR for super HD. In that case, force the DPB size to | ||
| // DEFAULT_MAX_NUM_REF_FRAMES. Otherwise, clamp the computed DPB size to DEFAULT_MAX_NUM_REF_FRAMES. | ||
| levelDpbSize = (levelIdc == STD_VIDEO_H264_LEVEL_IDC_5_2) ? (uint8_t)DEFAULT_MAX_NUM_REF_FRAMES : std::min(uint8_t(DEFAULT_MAX_NUM_REF_FRAMES), levelDpbSize); | ||
| levelDpbSize = std::min(uint8_t(DEFAULT_MAX_NUM_REF_FRAMES), levelDpbSize); | ||
|
|
||
| uint8_t dpbSize = (uint8_t)((dpbCount < 1) ? levelDpbSize : (uint8_t)(std::min((uint8_t)dpbCount, levelDpbSize))) + 1; | ||
|
|
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This method
InitProfileLevelis called too early preventingDetermineLevelTierto work properly becausenumRefFramesmember variable line 79 will be always 0 at this time.I would recommend to calculate all of that after having called
InitDeviceCapabilitiesto get a validnumRefFrames.