Skip to content

encode: codec profile and level fixes - #171

Open
srinathkr-nv wants to merge 7 commits into
KhronosGroup:mainfrom
srinathkr-nv:codec-profile-level-fixes
Open

encode: codec profile and level fixes#171
srinathkr-nv wants to merge 7 commits into
KhronosGroup:mainfrom
srinathkr-nv:codec-profile-level-fixes

Conversation

@srinathkr-nv

@srinathkr-nv srinathkr-nv commented Feb 17, 2026

Copy link
Copy Markdown
Contributor

Description

This series of commits refactors the code related to the determination of the codec profile and level (and tier, as applicable) for all codecs, to avoid redundant calculations / storage and to provide a single point early in the encoder initialization stage where these values are decided.

Another effect of this refactoring is to use the proper codec profile when creating the video profile used for queries of capabilities and recommended settings. Previously, the video profile constructed for these purposes used a default codec profile, which may not be representative of (or may not support) the coding tools requested.

Type of change

Bug fixes and refactoring

Tests

NVIDIA L2 / NVIDIA 580.94.17 / Ubuntu 24.04.3 LTS

Total Tests: 70
Passed: 50
Crashed: 0
Failed: 0
Not Supported: 2
Skipped: 18 (in skip list)
Success Rate: 100.0%

Additional Details (optional)

Prior to this series of commits, running the test framework would show 1 unsupported test case, and 51 passing test cases. The unsupported test case was encode_h265_main10_profile, which is actually supported by the NVIDIA driver. The list of passing tests included encode_av1_high_profile and encode_av1_professional_profile although these profiles are not supported.

With the current changes, encode_av1_high_profile and encode_av1_professional_profile are correctly marked as unsupported and encode_h265_main10_profile executes successfully.

@srinathkr-nv srinathkr-nv changed the title Codec profile and level fixes encode: codec profile and level fixes Feb 17, 2026
@srinathkr-nv
srinathkr-nv force-pushed the codec-profile-level-fixes branch from 49f442f to 4fba129 Compare April 20, 2026 09:11
@srinathkr-nv srinathkr-nv changed the title encode: codec profile and level fixes Encoder codec profile and level fixes Apr 20, 2026
@srinathkr-nv srinathkr-nv changed the title Encoder codec profile and level fixes encode: codec profile and level fixes Apr 20, 2026
Comment thread vk_video_encoder/libs/VkVideoEncoder/VkEncoderConfigAV1.cpp Outdated
Comment thread vk_video_encoder/libs/VkVideoEncoder/VkEncoderConfigH264.cpp
Comment thread vk_video_encoder/libs/VkVideoEncoder/VkEncoderConfigH264.cpp
Comment thread vk_video_encoder/libs/VkVideoEncoder/VkEncoderConfigH265.cpp
Comment thread vk_video_encoder/libs/VkVideoEncoder/VkEncoderConfigH265.h
Comment thread vk_video_encoder/libs/VkVideoEncoder/VkEncoderConfigH264.cpp Outdated
Comment thread vk_video_encoder/libs/VkVideoEncoder/VkEncoderConfigH264.cpp
@dabrain34

Copy link
Copy Markdown
Contributor

@srinathkr-nv ping :)

@srinathkr-nv

Copy link
Copy Markdown
Contributor Author

Sorry for the delay; I have previously noticed your comments but I haven't been able to get around to addressing them. I'll try doing that next week.

Comment thread vk_video_encoder/libs/VkVideoEncoder/VkEncoderConfigH264.cpp Outdated
@srinathkr-nv

Copy link
Copy Markdown
Contributor Author

I have added new commits to this PR to address some of the main issues with the profile and level determination logic. This essentially reverses the direction I had taken in the earlier commits in this series, so I will be reworking this set of changes again to have a clean history.

@srinathkr-nv
srinathkr-nv requested a review from dabrain34 July 8, 2026 10:37
Comment thread vk_video_encoder/libs/VkVideoEncoder/VkEncoderConfigH264.cpp Outdated
Comment thread vk_video_encoder/libs/VkVideoEncoder/VkEncoderConfigH265.cpp Outdated
Comment thread vk_video_encoder/libs/VkVideoEncoder/VkEncoderConfigH265.cpp Outdated
@dabrain34

dabrain34 commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

Thanks for this set of patches, I confirm that capabilities is retrieved before determining the right profile and level.

Could you please squash the commit history in order to avoid the intermediate non working state ?

@srinathkr-nv

Copy link
Copy Markdown
Contributor Author

Thanks for this set of patches, I confirm that capabilities is retrieved before determining the right profile and level.

Could you please squash the commit history in order to avoid the intermediate non working state ?

Great, thank you for trying it out and confirming that it works for you. I was waiting for this confirmation before I reorganized the commits; I will work on this part next.

@srinathkr-nv
srinathkr-nv force-pushed the codec-profile-level-fixes branch from e9599e9 to 78befa7 Compare July 21, 2026 08:06
@srinathkr-nv
srinathkr-nv requested a review from dabrain34 July 21, 2026 08:08
@dabrain34

Copy link
Copy Markdown
Contributor

That's great news I will have a deeper look a bit later.
I feel that we could still squash some commits such as the h264 level ones or the h264config ones as 10 commits looks quite a lot in that PR.

Comment thread vk_video_encoder/libs/VkVideoEncoder/VkEncoderConfigH265.cpp
@dabrain34

Copy link
Copy Markdown
Contributor

@srinathkr-nv could you please review the history of commits and shorten a bit ? See my comment #171 (comment)

@dabrain34 dabrain34 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please squash some commits

@srinathkr-nv
srinathkr-nv force-pushed the codec-profile-level-fixes branch 2 times, most recently from 3a82d42 to 7212773 Compare August 13, 2026 09:03
@srinathkr-nv

Copy link
Copy Markdown
Contributor Author

I have now folded four of the original commits which touched the the H.264 level determination code into a single commit.

The commits a409e1f, 5886e91, c8863a2 and 1827c29 fundamentally re-architect the code involving profile and level determination and should be kept as they are.

That leaves the remaining two commits in this PR, which are intentionally kept separate because it is awkward to squash them into any of the other commits (and they have a very specific focus area).

@srinathkr-nv
srinathkr-nv requested a review from dabrain34 August 13, 2026 09:10
@dabrain34

Copy link
Copy Markdown
Contributor

We dont use Signed-off tag in this repository in the commit message, any reason you'd like to have it now ?
Regarding the commit message, it can be very verbose and pretty difficult to read.

Regarding the history, the 4 patches does not exist anymore, so I dont know what are the patches you'd like to keep.

I would squash 02cc3936 EncoderConfigH264: add a profile-probing loop in InitVideoProfileCapabilities and 31652193 EncoderConfigH264: simplify transform_8x8_mode_flag handling into
4b68dcf8 encode: add/move codec profile auto-selection into InitVideoProfileCapabilities as this is part of improving the auto selection of profile.

EncoderConfigH265: add MaxLumaSr check to IsSuitableLevel and 4824c3e7 VkEncoderConfigH264: improve level limits table and related code could also be squashed in 088ba79a encode: separate level/tier determination into a distinct initialization step

And then clarify a bit the commit message that we are improving the level selection and then the profile selection.

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))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

by default GetConsecutiveBFrameCount will be initialized to CONSECUTIVE_B_FRAME_COUNT_MAX_VALUE which is 255. So I would first validate that you want BFrameCount with the command line in order to select the right profile according to this. Otherwise it will never be STD_VIDEO_H264_PROFILE_IDC_BASELINE

you could for example add the line

const uint8_t bFrameCount = gopStructure.GetConsecutiveBFrameCount();
const bool wantsBFrames = (bFrameCount != CONSECUTIVE_B_FRAME_COUNT_MAX_VALUE) && (bFrameCount > 0);

And use wantsBFrames to validate that b-frame has been selected

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will see if there's a better way to do this; if not, I'll apply your suggestion to the code.

if ((tuningMode == VK_VIDEO_ENCODE_TUNING_MODE_LOSSLESS_KHR) ||
(input.chromaSubsampling == VK_VIDEO_CHROMA_SUBSAMPLING_444_BIT_KHR)) {
minProfile = STD_VIDEO_H264_PROFILE_IDC_HIGH_444_PREDICTIVE;
} else if (gopStructure.GetConsecutiveBFrameCount() > 0) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you should validate that b-frame has been validated by command line argument and not CONSECUTIVE_B_FRAME_COUNT_MAX_VALUE

StdVideoH264ProfileIdc minProfile = STD_VIDEO_H264_PROFILE_IDC_BASELINE;
if ((tuningMode == VK_VIDEO_ENCODE_TUNING_MODE_LOSSLESS_KHR) ||
(input.chromaSubsampling == VK_VIDEO_CHROMA_SUBSAMPLING_444_BIT_KHR)) {
minProfile = STD_VIDEO_H264_PROFILE_IDC_HIGH_444_PREDICTIVE;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here STD_VIDEO_H264_PROFILE_IDC_HIGH should be selected if
f ((encodeBitDepthLuma > 8) ||
(encodeChromaSubsampling != VK_VIDEO_CHROMA_SUBSAMPLING_420_BIT_KHR))

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The HIGH profile for H.264 does not support bit-depth > 8 and a chroma subsampling other than 4:2:0 and 4:0:0. The correct profile for those cases would be the HIGH_10 and HIGH_422 profiles, but correctly using those profiles will require additional changes beyond just the profile- and level-related fixes which I'm making here.

Comment thread vk_video_encoder/libs/VkVideoEncoder/VkEncoderConfigH264.cpp Outdated
Comment thread vk_video_encoder/libs/VkVideoEncoder/VkEncoderConfigH265.cpp Outdated
Comment thread vk_video_encoder/libs/VkVideoEncoder/VkEncoderConfigAV1.cpp Outdated
The current formatting of the level limits table makes it hard to read
and compare against the source for the values within (i.e. table A-1 of
the H.264 specification). Further, we have a "prog" column in this table
whose entries have been taken from table A-4 of the H.264 specification
and specify the value to be used for the SPS frame_mbs_only_flag syntax
element for a given level, but these values are not used anywhere in the
code as interlaced encoding is not supported (and therefore
frame_mbs_only_flag = 1 always).

This change adds some whitespace to align the columns of this table and
make it more readable, while removing the unused "prog" column.

When editing the table, it was noticed that no entries were present for
levels 6.0, 6.1, 6.2 defined in table A-1. These entries have been added
now, and they allow removing some workarounds where the level was
previously artificially limited to level 5.2 as the maximum supported
level.

Finally, this change also fixes a bug in the level determination code in
EncoderConfigH264::DetermineLevel(). This code uses a fixed multiplier
equal to 1200 for the MaxBR and MaxCPB entries in the level limits
table, taken from table A-1 of the H.264 specification, but this
multiplier is to be used only for the Baseline, Constrained Baseline,
Main and Extended profiles. Profiles greater than these use a larger
multiplier, as specified in table A-2 of the H.264 specification. Due to
the use of a smaller multiplier, this code selects a greater level than
is strictly necessary in some situations, based on the input settings.

This commit updates this code to use a multiplier dependent on the codec
profile.
…ion step

Currently, the codec level determination logic does not follow a
consistent pattern between all the codec implementations. This change
introduces DetermineLevelTier() as a pure virtual method on
EncoderConfig, called from VkVideoEncoder::InitEncoder() after
InitDeviceCapabilities() and before InitDpbCount(). This ensures that
device capabilities and preferred settings for the selected quality
level are available when determining the codec level.

An improvement over the existing codec-specific level determination
logic found in some of the codecs is that the new method returns a bool
instead of void, to allow signaling failures in determining the
appropriate level (as can happen for certain combinations of input
parameters).

Each codec then provides an overriding implementation for this method:

- H.264: the the level computation has been extracted from
  InitDpbCount() into the new method.

- H.265: GetLevelTier() has been renamed to DetermineLevelTier(). Also,
  instead of returning a struct containing the level and tier, the
  existing (unused) members for the same purpose in the config structure
  are now assigned.

- AV1: DetermineLevelTier() now returns false instead of clamping to
  LEVEL_7_3 when the resolution/bitrate exceeds all defined levels.

For H.265, in addition, some of the code involved in setting the codec
profile has been simplified to remove various conditionals at the time
of populating the SPS. Future changes will ensure correct codec profile
initialization early on when initializing the encoder configuration.
This change is a refactoring of video profile initialization rather than
a plain renaming.

In the prior code, InitVideoProfile() would call
GetDefaultVideoProfileIdc() if videoProfileIdc was not initialized.
However, no code would initialize videoProfileIdc to a proper value, so
GetDefaultVideoProfileIdc() would get called always and return a fixed
value for each codec. This was problematic in some cases as the fixed
codec profile did not necessarily support all of the coding tools
requested by the user when encoding, or would be incompatible with the
codec profile actually needed for encoding.

InitVideoProfile() has been changed to MakeVideoProfile(), a pure
factory that takes the codec profile as an explicit parameter. This
removes the implicit dependency on GetDefaultVideoProfileIdc() and the
videoProfileIdc member, and allows deleting both of these.

Another motivation for this refactoring is that we will require more
flexibility in constructing and updating video profile instances in the
future, so this change is a step towards that goal.

One more improvement with this change is to move the
encodeBitDepth{Luma,Chroma} defaulting into InitializeParameters() so
that it happens before any profile-dependent logic, and there was no
specific reason previously to tie it to the video profile
initialization.

MakeVideoProfile() now gets called from the capability query code, so
the InitDeviceCapabilities() method has also been renamed to
InitVideoProfileCapabilities() to better reflect its new responsibility
of selecting the video profile first and then querying capabilities.

Currently, the codec profile used for the MakeVideoProfile() call is a
default value per codec (HIGH for H.264, MAIN for H.265/AV1). Proper
logic to determine an appropriate profile will be added in a subsequent
commit.
…pabilities

Each codec's InitVideoProfileCapabilities() implementation now
determines the video profile from the encoding parameters when the user
hasn't specified one explicitly.

For H.265 and AV1, the logic is straightforward and uses the bit-depth
and/or chroma subsampling to auto-select the profile.

For H.264, the code also takes into account the number of B-frames,
entropy coding mode and tuning mode (all of which can be configured by
the user, but some have default values). The auto-selection logic also
has been cleaned up a bit because we previously had hard-coded
assumptions (like the adaptive transform would always be supported),
which would lead to the code choosing a codec profile which may not be
supported by the underlying device.

The profile auto-selection logic has not been placed in a separate
helper because although it would be easy to do this for H.265 and AV1,
it would be non-trivial for H.264, where the correct profile
determination depends on device capabilities (as will be introduced in a
future change), so we need the profile determination logic adjacent to
the capabilities query.
…bilities

This helps the automatic profile selection logic select an appropriate
profile based both on the requested features and the codec profiles
supported by the device. The prior logic would prefer only the requested
features, potentially configuring a codec profile which would not be
supported by the device.
With this change, the adaptiveTransformMode parameter is either ENABLE
or DISABLE at the end of InitVideoProfileCapabilities(), based on
hardware support for transform_8x8_mode and the final determined
profile. This removes the need for a three-way branch in
InitSpsPpsParameters() and simplifies code for setting this flag.
Add the missing luma sample rate (MaxLumaSr) check from Annex A of the
H.265 specification. The check rejects a level if
picSizeInSamples * frameRate exceeds the level's MaxLumaSr limit.
@srinathkr-nv
srinathkr-nv force-pushed the codec-profile-level-fixes branch from 7212773 to 20ee9f7 Compare August 21, 2026 05:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants