VK_KHR_video_encode_h264

本文档概述了一项在 Vulkan 中启用执行 H.264/AVC 视频编码操作的提案。

1. 问题陈述

VK_KHR_video_queue 扩展引入了对视频编码操作的支持,而 VK_KHR_video_encode_queue 扩展进一步扩展了此功能,添加了特定于视频编码的 API。

本提案的目标是在此基础设施的基础上引入对编码符合 H.264/AVC 视频压缩标准的原始视频流序列的支持。

2. 解决方案空间

由于 VK_KHR_video_queueVK_KHR_video_encode_queue 扩展已经为编解码器特定的视频编码扩展的设计奠定了基础,因此此扩展只需要定义 API,以便在使用编解码器无关的 API 期间在不同点提供必要的编解码器特定参数。特别是

  • 允许指定 H.264 序列和图片参数集(SPS、PPS)以存储在视频会话参数对象中的 API

  • 允许指定特定于编码图片的 H.264 信息的 API,包括对先前存储的 SPS 和 PPS 条目的引用

  • 允许指定特定于活动参考图片和视频编码操作中使用的可选重建图片的 H.264 参考图片信息的 API

编解码器特定的编码参数由应用程序通过专用于 H.264 视频编码的视频标准头文件提供的自定义定义来指定。

本提案首先使用 VK_KHR_video_decode_h264 扩展中常用的 H.264 定义,并添加另一个特定于 H.264 编码的视频标准头。因此,此扩展使用以下视频标准头:

  • vulkan_video_codec_h264std - 包含所有 H.264 视频编码操作的通用定义。

  • vulkan_video_codec_h264std_encode - 包含特定于 H.264 视频编码操作的定义。

这些头文件可以按如下方式包含:

#include <vk_video/vulkan_video_codec_h264std.h>
#include <vk_video/vulkan_video_codec_h264std_encode.h>

3. 提案

3.1. 视频标准头

此扩展使用新的 vulkan_video_codec_h264std_encode 视频标准头。实现必须始终支持此视频标准头的至少 1.0.0 版本。

3.2. H.264 编码配置文件

此扩展引入了新的视频编解码操作 VK_VIDEO_CODEC_OPERATION_ENCODE_H264_BIT_KHR。此标志可用于检查特定的队列族是否支持 H.264/AVC 内容的编码,如 VkQueueFamilyVideoPropertiesKHR 中返回的那样。

H.264 编码配置文件可以通过使用此新的视频编解码操作的 VkVideoProfileInfoKHR 结构来定义,并通过在 pNext 链中包含以下新的特定于编解码器的配置文件信息结构来定义:

typedef struct VkVideoEncodeH264ProfileInfoKHR {
    VkStructureType                              sType;
    const void*                                  pNext;
    StdVideoH264ProfileIdc                       stdProfileIdc;
} VkVideoEncodeH264ProfileInfoKHR;

stdProfileIdc 指定 H.264 配置文件指示符。

3.3. H.264 编码能力

当调用 vkGetPhysicalDeviceVideoCapabilitiesKHR 命令检索特定于 H.264 视频编码的能力时,应用程序需要在 VkVideoCapabilitiesKHRpNext 链中包含以下新结构:

typedef struct VkVideoEncodeH264CapabilitiesKHR {
    VkStructureType                        sType;
    void*                                  pNext;
    VkVideoEncodeH264CapabilityFlagsKHR    flags;
    StdVideoH264LevelIdc                   maxLevelIdc;
    uint32_t                               maxSliceCount;
    uint32_t                               maxPPictureL0ReferenceCount;
    uint32_t                               maxBPictureL0ReferenceCount;
    uint32_t                               maxL1ReferenceCount;
    uint32_t                               maxTemporalLayerCount;
    VkBool32                               expectDyadicTemporalLayerPattern;
    int32_t                                minQp;
    int32_t                                maxQp;
    VkBool32                               prefersGopRemainingFrames;
    VkBool32                               requiresGopRemainingFrames;
    VkVideoEncodeH264StdFlagsKHR           stdSyntaxFlags;
} VkVideoEncodeH264CapabilitiesKHR;

flags 表示对各种 H.264 编码能力的支持。

  • VK_VIDEO_ENCODE_H264_CAPABILITY_HRD_COMPLIANCE_BIT_KHR - 当存在相关的 HRD 参数时,支持生成符合 HRD 标准的码流。

  • VK_VIDEO_ENCODE_H264_CAPABILITY_PREDICTION_WEIGHT_TABLE_GENERATED_BIT_KHR - 支持在必要时生成编码过程使用的权重表,而不是由应用程序提供。

  • VK_VIDEO_ENCODE_H264_CAPABILITY_ROW_UNALIGNED_SLICE_BIT_KHR - 支持不以宏块行边界开始/结束的切片。

  • VK_VIDEO_ENCODE_H264_CAPABILITY_DIFFERENT_SLICE_TYPE_BIT_KHR - 支持帧内不同的切片类型。

  • VK_VIDEO_ENCODE_H264_CAPABILITY_B_FRAME_IN_L0_LIST_BIT_KHR - 支持将 B 帧包含在 L0 参考列表中。

  • VK_VIDEO_ENCODE_H264_CAPABILITY_B_FRAME_IN_L1_LIST_BIT_KHR - 支持将 B 帧包含在 L1 参考列表中。

  • VK_VIDEO_ENCODE_H264_CAPABILITY_PER_PICTURE_TYPE_MIN_MAX_QP_BIT_KHR - 支持在启用码率控制时,对每种图像类型使用不同的最小/最大 QP 值。

  • VK_VIDEO_ENCODE_H264_CAPABILITY_PER_SLICE_CONSTANT_QP_BIT_KHR - 支持在禁用码率控制时,对帧的每个切片使用不同的恒定 QP 值。

  • VK_VIDEO_ENCODE_H264_CAPABILITY_GENERATE_PREFIX_NALU_BIT_KHR - 支持生成前缀 NAL 单元。

maxLevelIdc 表示支持的最大 H.264 级别指示符。

maxSliceCount 表示实现对编码帧可包含的 H.264 切片数量的上限,尽管对于给定的帧,其实际最大值可能会更小,这取决于其尺寸和前面描述的一些功能标志。

maxPPictureL0ReferenceCountmaxBPictureL0ReferenceCountmaxL1ReferenceCount 表示编码帧可以通过 L0 和 L1 参考列表引用的最大参考帧数,具体取决于图像类型(P 或 B)。这些能力不会限制应用程序可以包含在 L0 和 L1 参考列表中的引用数量,因为实际上,实现可能会根据编码内容和/或编码器实现的能力来限制使用的有效引用数量。但是,它们确实间接表明是否支持编码 P 或 B 帧。特别是:

  • 如果 maxPPictureL0ReferenceCount 为零,则实现不支持编码 P 帧。

  • 如果 maxBPictureL0ReferenceCountmaxL1ReferenceCount 均为零,则实现不支持编码 B 帧。

maxTemporalLayerCount 表示支持的 H.264 时间层数,而 expectDyadicTemporalLayerPattern 表示实现的 (如果通过 VkVideoEncodeCapabilitiesKHR::maxRateControlLayers 对于给定的 H.264 编码配置文件大于 1 来指示支持) 多层码率控制算法是否期望应用程序使用二元时间层模式以实现准确操作。

minQpmaxQp 表示可以在码率控制配置中使用的 QP 值的支持范围,或者在禁用码率控制时用作恒定 QP。

prefersGopRemainingFramesrequiresGopRemainingFrames 分别表示实现是否首选或要求应用程序跟踪当前 GOP(图像组)中剩余的帧数(对于每种类型),因为某些实现可能需要此信息才能准确地运行其码率控制算法。

stdSyntaxFlags 包含一组标志,这些标志向应用程序提供有关哪些视频标准参数或参数值受支持的信息,以便直接按应用程序指定的方式使用。这些标志不限制应用程序可以指定的视频标准参数值,而是提供有关遵守这些参数值的保证。

3.4. H.264 编码参数集

在编码 H.264 视频流时,必须使用视频会话参数对象。应用程序需要在创建用于 H.264 编码使用的视频会话参数对象时,在 VkVideoSessionParametersCreateInfoKHRpNext 链中包含以下新结构,以指定所创建对象的参数集容量:

typedef struct VkVideoEncodeH264SessionParametersCreateInfoKHR {
    VkStructureType                                        sType;
    const void*                                            pNext;
    uint32_t                                               maxStdSPSCount;
    uint32_t                                               maxStdPPSCount;
    const VkVideoEncodeH264SessionParametersAddInfoKHR*    pParametersAddInfo;
} VkVideoEncodeH264SessionParametersCreateInfoKHR;

可选的 pParametersAddInfo 成员还允许指定要添加到所创建对象的初始参数集。

typedef struct VkVideoEncodeH264SessionParametersAddInfoKHR {
    VkStructureType                            sType;
    const void*                                pNext;
    uint32_t                                   stdSPSCount;
    const StdVideoH264SequenceParameterSet*    pStdSPSs;
    uint32_t                                   stdPPSCount;
    const StdVideoH264PictureParameterSet*     pStdPPSs;
} VkVideoEncodeH264SessionParametersAddInfoKHR;

此结构还可以包含在 VkVideoSessionParametersUpdateInfoKHRpNext 链中,该结构用于视频会话参数更新操作,以便在创建对象后向对象添加更多参数集。

单个参数集使用参数集 ID 作为其键进行存储,具体来说:

  • H.264 SPS 条目使用 seq_parameter_set_id 值进行标识。

  • H.264 PPS 条目使用一对 seq_parameter_set_idpic_parameter_set_id 值进行标识。

H.264/AVC 视频压缩标准始终需要 SPS 和 PPS,因此应用程序必须在能够录制视频编码操作之前,向使用的参数对象添加每个参数集的实例。

此外,H.264/AVC 视频压缩标准还允许修改现有参数集,但由于存储在视频会话参数对象中的参数无法在 Vulkan 中更改,因此在这种情况下,应用程序必须创建新的参数对象,如 VK_KHR_video_queue 的提案中所述。

由于实现可以覆盖存储在视频会话参数对象中的 SPS 和 PPS 条目中的参数,如 VK_KHR_video_encode_queue 的提案中所述,因此本提案引入了特定于 H.264 编码的其他结构,以便与 vkGetEncodedVideoSessionParametersKHR 命令一起使用。

首先,必须在 VkVideoEncodeSessionParametersGetInfoKHRpNext 链中包含以下新结构,以标识该命令预期返回反馈信息或编码参数集数据的 H.264 参数集:

typedef struct VkVideoEncodeH264SessionParametersGetInfoKHR {
    VkStructureType    sType;
    const void*        pNext;
    VkBool32           writeStdSPS;
    VkBool32           writeStdPPS;
    uint32_t           stdSPSId;
    uint32_t           stdPPSId;
} VkVideoEncodeH264SessionParametersGetInfoKHR;

writeStdSPSwriteStdPPS 指定是否请求 SPS 或 PPS 反馈/码流数据。如果需要,两者都可以请求。

stdSPSIdstdPPSId 用于标识要请求数据的 SPS 和/或 PPS,后者仅与 PPS 查询相关。

当使用 vkGetEncodedVideoSessionParametersKHR 命令请求反馈时,可以将以下新结构包含在 VkVideoEncodeSessionParametersFeedbackInfoKHRpNext 链中:

typedef struct VkVideoEncodeH264SessionParametersFeedbackInfoKHR {
    VkStructureType    sType;
    void*              pNext;
    VkBool32           hasStdSPSOverrides;
    VkBool32           hasStdPPSOverrides;
} VkVideoEncodeH264SessionParametersFeedbackInfoKHR;

如果输入参数中设置了相应的 writeStd 字段,则 hasStdSPSOverrideshasStdPPSOverrides 的结果值将指示是否分别对 SPS 和/或 PPS 应用了覆盖。

当使用 vkGetEncodedVideoSessionParametersKHR 命令请求编码码流数据时,输出主机数据缓冲区将填充所请求的 H.264 参数集的编码码流。

正如 VK_KHR_video_encode_queue 扩展的提案中详细描述的那样,应用程序可以选择自行编码通常存储在视频会话参数对象中的参数。然而,如果实现对 SPS 或 PPS 参数应用了覆盖,则可能导致不符合规范的码流,因此通常建议应用程序使用通过 vkGetEncodedVideoSessionParametersKHR 命令检索的编码参数集数据。

3.5. H.264 编码参数

应用程序需要通过 VkVideoEncodeInfoKHRpNext 链,使用以下新结构来提供 H.264 特定的编码参数。

typedef struct VkVideoEncodeH264PictureInfoKHR {
    VkStructureType                             sType;
    const void*                                 pNext;
    uint32_t                                    naluSliceEntryCount;
    const VkVideoEncodeH264NaluSliceInfoKHR*    pNaluSliceEntries;
    const StdVideoEncodeH264PictureInfo*        pStdPictureInfo;
    VkBool32                                    generatePrefixNalu;
} VkVideoEncodeH264PictureInfoKHR;

naluSliceEntryCount 指定要为帧编码的切片数量,并且 pNaluSliceEntries 数组的元素为每个切片提供额外的附加信息,如下所述。

pStdPictureInfo 指向在 vulkan_video_codec_h264std_encode 视频标准头文件中定义的编解码器特定编码参数。

活动的 SPS 和 PPS(源自绑定的视频会话参数对象)由 seq_parameter_set_idpic_parameter_set_id 参数标识。

pStdPictureInfo→pRefLists 指向的结构指定了与参考列表相关的编解码器特定参数。特别是,它指定了与 L0 和 L1 参考列表元素对应的 DPB 插槽,以及参考图片标记和参考列表修改操作。

如果支持 VK_VIDEO_ENCODE_H264_CAPABILITY_GENERATE_PREFIX_NALU_BIT_KHR 功能标志,则可以将 generatePrefixNalu 设置为 VK_TRUE,以请求在每个编码切片之前生成前缀 NAL 单元。

单个切片的参数通过以下新结构的实例提供。

typedef struct VkVideoEncodeH264NaluSliceInfoKHR {
    VkStructureType                         sType;
    const void*                             pNext;
    int32_t                                 constantQp;
    const StdVideoEncodeH264SliceHeader*    pStdSliceHeader;
} VkVideoEncodeH264NaluSliceInfoKHR;

constantQp 指定在禁用速率控制时用于切片的恒定 QP 值。

pStdSliceHeader 指向要在切片头中使用的编解码器特定编码参数。

应用程序需要通过 VkVideoEncodeInfoKHR::pReferenceSlots 的相应元素的 pNext 链和 VkVideoEncodeInfoKHR::pSetupReferenceSlotpNext 链,使用以下新结构来提供 H.264 特定的活动参考图片和可选的重建图片信息。

typedef struct VkVideoEncodeH264DpbSlotInfoKHR {
    VkStructureType                           sType;
    const void*                               pNext;
    const StdVideoEncodeH264ReferenceInfo*    pStdReferenceInfo;
} VkVideoEncodeH264DpbSlotInfoKHR;

pStdReferenceInfo 指向在 vulkan_video_codec_h264std_encode 视频标准头文件中定义的编解码器特定参考图片参数。

应用程序有责任指定符合 H.264/AVC 视频压缩标准规则的编解码器特定参数。从 API 使用的角度来看,指定不合规的输入并非违法,但它们可能导致视频编码操作无法成功完成,并会导致输出码流和重建图片(如果指定了重建图片)在操作执行后具有未定义的内容。

实现可能会覆盖其中的一些参数,以便符合编码器实现的任何限制,但这不会影响编码的整体操作。应用程序可以选择通过使用新的 VK_VIDEO_SESSION_CREATE_ALLOW_ENCODE_PARAMETER_OPTIMIZATIONS_BIT_KHR 标志创建视频会话来选择额外的优化覆盖,从而针对使用场景获得更好的性能或效率。

有关各个 H.264 码流语法元素、导出值以及一般情况下如何解释这些参数的更多信息,请参考 ITU-T H.264 规范 的相应章节。

3.6. H.264 参考列表

为了填充用于编码预测图片的 L0 和 L1 参考列表,应用程序必须将 VkVideoEncodeH264PictureInfoKHR::pStdPictureInfo→pRefLists 指向的结构的 RefPicList0RefPicList1 数组成员的相应元素设置为参考图片的 DPB 插槽索引,同时必须将 RefPicList0RefPicList1 的所有未使用的元素设置为 STD_VIDEO_H264_NO_REFERENCE_PICTURE。通常,参考图片资源通过根据 VK_KHR_video_encode_queue 扩展定义的编解码器无关语义将其包含在活动参考图片列表中来指定。

在所有情况下,L0 和 L1 参考列表引用的 DPB 插槽索引集以及 VkVideoEncodeInfoKHR::pReferenceSlots 中指定的活动参考图片列表必须匹配,但活动参考图片包含在 pReferenceSlots 数组中的顺序无关紧要。

3.7. H.264 速率控制

此提案添加了一组特定于 H.264 编码的可选速率控制参数,这些参数为实现的速率控制算法提供了额外的指导。

当速率控制未禁用且未设置为实现默认行为时,应用程序可以在 VkVideoEncodeRateControlInfoKHRpNext 链中包含以下新结构。

typedef struct VkVideoEncodeH264RateControlInfoKHR {
    VkStructureType                         sType;
    const void*                             pNext;
    VkVideoEncodeH264RateControlFlagsKHR    flags;
    uint32_t                                gopFrameCount;
    uint32_t                                idrPeriod;
    uint32_t                                consecutiveBFrameCount;
    uint32_t                                temporalLayerCount;
} VkVideoEncodeH264RateControlInfoKHR;

flags 可以包含以下一个或多个标志。

  • VK_VIDEO_ENCODE_H264_RATE_CONTROL_ATTEMPT_HRD_COMPLIANCE_BIT_KHR 可用于指示应用程序希望实现的速率控制算法尽可能尝试生成符合 HRD 的码流。

  • VK_VIDEO_ENCODE_H264_RATE_CONTROL_REGULAR_GOP_BIT_KHR 可用于指示应用程序打算根据 gopFrameCountidrPeriodconsecutiveBFrameCount 中指定的参数使用规则 GOP 结构。

  • VK_VIDEO_ENCODE_H264_RATE_CONTROL_REFERENCE_PATTERN_FLAT_BIT_KHR 可用于指示应用程序打算在 GOP 中遵循平面参考模式,其中每个 P 帧使用最后一个非 B 帧作为参考,每个 B 帧分别使用最后一个和下一个非 B 帧作为前向和后向参考。

  • VK_VIDEO_ENCODE_H264_RATE_CONTROL_REFERENCE_PATTERN_DYADIC_BIT_KHR 可用于指示应用程序打算遵循二元参考模式。

  • VK_VIDEO_ENCODE_H264_RATE_CONTROL_TEMPORAL_LAYER_PATTERN_DYADIC_BIT_KHR 可用于指示应用程序在使用多个时间层时打算遵循二元时间层模式。

gopFrameCountidrPeriodconsecutiveBFrameCount 分别指定 GOP 大小、IDR 周期和非 B 帧之间的连续 B 帧数量,这些参数定义了实现速率控制算法应期望的 GOP 的典型结构。如果 flags 中也指定了 VK_VIDEO_ENCODE_H264_RATE_CONTROL_REGULAR_GOP_BIT_KHR,则实现将期望所有 GOP 都遵循此结构,否则它可能会假设应用程序会不时偏离这些值。如果这些值中的任何一个为零,则实现的速率控制算法将不会对 GOP 结构的相应参数做出任何假设。

temporalLayerCount 指示应用程序打算使用的 H.264 时间层的数量,并且在使用多层速率控制时,预计它与速率控制层的数量匹配。

以下新结构可以包含在 VkVideoEncodeRateControlLayerInfoKHRpNext 链中,以指定特定于 H.264 编码的每个速率控制层的其他指导参数。

typedef struct VkVideoEncodeH264RateControlLayerInfoKHR {
    VkStructureType                  sType;
    const void*                      pNext;
    VkBool32                         useMinQp;
    VkVideoEncodeH264QpKHR           minQp;
    VkBool32                         useMaxQp;
    VkVideoEncodeH264QpKHR           maxQp;
    VkBool32                         useMaxFrameSize;
    VkVideoEncodeH264FrameSizeKHR    maxFrameSize;
} VkVideoEncodeH264RateControlLayerInfoKHR;

useMinQp 设置为 VK_TRUE 时,minQp 指定实现速率控制算法应使用的每个图片类型的 QP 值的下限。类似地,当 useMaxQp 设置为 VK_TRUE 时,maxQp 指定 QP 值的上限。

useMaxFrameSize 设置为 VK_TRUE 时,maxFrameSize 指定实现速率控制算法应针对的每个图片类型的最大帧大小(以字节为单位)。

某些实现可能受益于或需要关于当前编码的 GOP 中剩余帧数的额外指导,分别由 prefersGopRemainingFramesrequiresGopRemainingFrames 功能指示。这可能是因为实现无法跟踪 GOP 内编码流的当前位置,或者因为实现可以使用此信息来更好地响应 GOP 结构的动态变化。此提案通过引入以下新结构来解决此问题,该结构可以包含在 VkVideoBeginCodingInfoKHRpNext 链中。

typedef struct VkVideoEncodeH264GopRemainingFrameInfoKHR {
    VkStructureType    sType;
    const void*        pNext;
    VkBool32           useGopRemainingFrames;
    uint32_t           gopRemainingI;
    uint32_t           gopRemainingP;
    uint32_t           gopRemainingB;
} VkVideoEncodeH264GopRemainingFrameInfoKHR;

useGopRemainingFrames 设置为 VK_TRUE 时,实现的速率控制算法可以使用 gopRemainingIgopRemainingPgopRemainingB 中指定的值作为当前编码 GOP 中相应类型的剩余帧数的指导。

4. 示例

4.1. 选择支持 H.264 编码的队列族

uint32_t queueFamilyIndex;
uint32_t queueFamilyCount;

vkGetPhysicalDeviceQueueFamilyProperties2(physicalDevice, &queueFamilyCount, NULL);

VkQueueFamilyProperties2* props = calloc(queueFamilyCount,
    sizeof(VkQueueFamilyProperties2));
VkQueueFamilyVideoPropertiesKHR* videoProps = calloc(queueFamilyCount,
    sizeof(VkQueueFamilyVideoPropertiesKHR));

for (queueFamilyIndex = 0; queueFamilyIndex < queueFamilyCount; ++queueFamilyIndex) {
    props[queueFamilyIndex].sType = VK_STRUCTURE_TYPE_QUEUE_FAMILY_PROPERTIES_2;
    props[queueFamilyIndex].pNext = &videoProps[queueFamilyIndex];

    videoProps[queueFamilyIndex].sType = VK_STRUCTURE_TYPE_QUEUE_FAMILY_VIDEO_PROPERTIES_KHR;
}

vkGetPhysicalDeviceQueueFamilyProperties2(physicalDevice, &queueFamilyCount, props);

for (queueFamilyIndex = 0; queueFamilyIndex < queueFamilyCount; ++queueFamilyIndex) {
    if ((props[queueFamilyIndex].queueFamilyProperties.queueFlags & VK_QUEUE_VIDEO_ENCODE_BIT_KHR) != 0 &&
        (videoProps[queueFamilyIndex].videoCodecOperations & VK_VIDEO_CODEC_OPERATION_ENCODE_H264_BIT_KHR) != 0) {
        break;
    }
}

if (queueFamilyIndex < queueFamilyCount) {
    // Found appropriate queue family
    ...
} else {
    // Did not find a queue family with the needed capabilities
    ...
}

4.2. 检查对 H.264 编码配置文件的支持并查询其功能

VkResult result;

VkVideoEncodeH264ProfileInfoKHR encodeH264ProfileInfo = {
    .sType = VK_STRUCTURE_TYPE_VIDEO_ENCODE_H264_PROFILE_INFO_KHR,
    .pNext = NULL,
    .stdProfileIdc = STD_VIDEO_H264_PROFILE_IDC_BASELINE
};

VkVideoProfileInfoKHR profileInfo = {
    .sType = VK_STRUCTURE_TYPE_VIDEO_PROFILE_INFO_KHR,
    .pNext = &encodeH264ProfileInfo,
    .videoCodecOperation = VK_VIDEO_CODEC_OPERATION_ENCODE_H264_BIT_KHR,
    .chromaSubsampling = VK_VIDEO_CHROMA_SUBSAMPLING_420_BIT_KHR,
    .lumaBitDepth = VK_VIDEO_COMPONENT_BIT_DEPTH_8_BIT_KHR,
    .chromaBitDepth = VK_VIDEO_COMPONENT_BIT_DEPTH_8_BIT_KHR
};

VkVideoEncodeH264CapabilitiesKHR encodeH264Capabilities = {
    .sType = VK_STRUCTURE_TYPE_VIDEO_ENCODE_H264_CAPABILITIES_KHR,
    .pNext = NULL,
};

VkVideoEncodeCapabilitiesKHR encodeCapabilities = {
    .sType = VK_STRUCTURE_TYPE_VIDEO_ENCODE_CAPABILITIES_KHR,
    .pNext = &encodeH264Capabilities
}

VkVideoCapabilitiesKHR capabilities = {
    .sType = VK_STRUCTURE_TYPE_VIDEO_CAPABILITIES_KHR,
    .pNext = &encodeCapabilities
};

result = vkGetPhysicalDeviceVideoCapabilitiesKHR(physicalDevice, &profileInfo, &capabilities);

if (result == VK_SUCCESS) {
    // Profile is supported, check additional capabilities
    ...
} else {
    // Profile is not supported, result provides additional information about why
    ...
}

4.3. 创建和更新 H.264 视频会话参数对象

VkVideoSessionParametersKHR videoSessionParams = VK_NULL_HANDLE;

VkVideoEncodeH264SessionParametersCreateInfoKHR encodeH264CreateInfo = {
    .sType = VK_STRUCTURE_TYPE_VIDEO_ENCODE_H264_SESSION_PARAMETERS_CREATE_INFO_KHR,
    .pNext = NULL,
    .maxStdSPSCount = ... // SPS capacity
    .maxStdPPSCount = ... // PPS capacity
    .pParametersAddInfo = ... // parameters to add at creation time or NULL
};

VkVideoSessionParametersCreateInfoKHR createInfo = {
    .sType = VK_STRUCTURE_TYPE_VIDEO_SESSION_PARAMETERS_CREATE_INFO_KHR,
    .pNext = &encodeH264CreateInfo,
    .flags = 0,
    .videoSessionParametersTemplate = ... // template to use or VK_NULL_HANDLE
    .videoSession = videoSession
};

vkCreateVideoSessionParametersKHR(device, &createInfo, NULL, &videoSessionParams);

...

StdVideoH264SequenceParameterSet sps = {};
// parse and populate SPS parameters
...

StdVideoH264PictureParameterSet pps = {};
// parse and populate PPS parameters
...

VkVideoEncodeH264SessionParametersAddInfoKHR encodeH264AddInfo = {
    .sType = VK_STRUCTURE_TYPE_VIDEO_ENCODE_H264_SESSION_PARAMETERS_ADD_INFO_KHR,
    .pNext = NULL,
    .stdSPSCount = 1,
    .pStdSPSs = &sps,
    .stdPPSCount = 1,
    .pStdPPSs = &pps
};

VkVideoSessionParametersUpdateInfoKHR updateInfo = {
    .sType = VK_STRUCTURE_TYPE_VIDEO_SESSION_PARAMETERS_UPDATE_INFO_KHR,
    .pNext = &encodeH264AddInfo,
    .updateSequenceCount = 1 // incremented for each subsequent update
};

vkUpdateVideoSessionParametersKHR(device, &videoSessionParams, &updateInfo);

4.4. 记录产生 I 帧且也被设置为参考帧的 H.264 编码操作

// Bound reference resource list provided has to include reconstructed picture resource
vkCmdBeginVideoCodingKHR(commandBuffer, ...);

StdVideoEncodeH264ReferenceInfo stdReferenceInfo = {};
// Populate H.264 reference picture info for the reconstructed picture
stdReferenceInfo.primary_pic_type = STD_VIDEO_H264_PICTURE_TYPE_I;
...

VkVideoEncodeH264DpbSlotInfoKHR encodeH264DpbSlotInfo = {
    .sType = VK_STRUCTURE_TYPE_VIDEO_ENCODE_H264_DPB_SLOT_INFO_KHR,
    .pNext = NULL,
    .pStdReferenceInfo = &stdReferenceInfo
};

VkVideoReferenceSlotInfoKHR setupSlotInfo = {
    .sType = VK_STRUCTURE_TYPE_VIDEO_REFERENCE_SLOT_INFO_KHR,
    .pNext = &encodeH264DpbSlotInfo
    ...
};

StdVideoEncodeH264ReferenceListsInfo stdRefListInfo = {};
// No references are used so just initialize the RefPicLists
for (uint32_t i = 0; i < STD_VIDEO_H264_MAX_NUM_LIST_REF; ++i) {
    stdRefListInfo.RefPicList0[i] = STD_VIDEO_H264_NO_REFERENCE_PICTURE;
    stdRefListInfo.RefPicList1[i] = STD_VIDEO_H264_NO_REFERENCE_PICTURE;
}
// Populate H.264 reference list modification/marking ops and other parameters
...

StdVideoEncodeH264PictureInfo stdPictureInfo = {};
// Populate H.264 picture info for the encode input picture
...
// Make sure that the reconstructed picture is requested to be set up as reference
stdPictureInfo.flags.is_reference = 1;
...
stdPictureInfo.primary_pic_type = STD_VIDEO_H264_PICTURE_TYPE_I;
...
stdPictureInfo.pRefLists = &stdRefListInfo;

VkVideoEncodeH264PictureInfoKHR encodeH264PictureInfo = {
    .sType = VK_STRUCTURE_TYPE_VIDEO_ENCODE_H264_PICTURE_INFO_KHR,
    .pNext = NULL,
    .naluSliceEntryCount = ... // number of slices to encode
    .pNaluSliceEntries = ... // pointer to the array of slice parameters
    .pStdPictureInfo = &stdPictureInfo
};

VkVideoEncodeInfoKHR encodeInfo = {
    .sType = VK_STRUCTURE_TYPE_VIDEO_ENCODE_INFO_KHR,
    .pNext = &encodeH264PictureInfo,
    ...
    .pSetupReferenceSlot = &setupSlotInfo,
    ...
};

vkCmdEncodeVideoKHR(commandBuffer, &encodeInfo);

vkCmdEndVideoCodingKHR(commandBuffer, ...);

4.5. 记录产生具有单个前向参考的 P 帧的 H.264 编码操作

// Bound reference resource list provided has to include the used reference picture resource
vkCmdBeginVideoCodingKHR(commandBuffer, ...);

StdVideoEncodeH264ReferenceInfo stdForwardReferenceInfo = {};
// Populate H.264 reference picture info for the forward referenced picture
...

VkVideoEncodeH264DpbSlotInfoKHR encodeH264DpbSlotInfo = {
    .sType = VK_STRUCTURE_TYPE_VIDEO_ENCODE_H264_DPB_SLOT_INFO_KHR,
    .pNext = NULL,
    .pStdReferenceInfo = &stdForwardReferenceInfo
};

VkVideoReferenceSlotInfoKHR referenceSlotInfo = {
    .sType = VK_STRUCTURE_TYPE_VIDEO_REFERENCE_SLOT_INFO_KHR,
    .pNext = &encodeH264DpbSlotInfo,
    .slotIndex = ... // DPB slot index of the forward reference picture
    ...
};

StdVideoEncodeH264ReferenceListsInfo stdRefListInfo = {};
// Initialize the RefPicLists and add the forward reference to the L0 list
for (uint32_t i = 0; i < STD_VIDEO_H264_MAX_NUM_LIST_REF; ++i) {
    stdRefListInfo.RefPicList0[i] = STD_VIDEO_H264_NO_REFERENCE_PICTURE;
    stdRefListInfo.RefPicList1[i] = STD_VIDEO_H264_NO_REFERENCE_PICTURE;
}
stdRefListInfo.RefPicList0[0] = ... // DPB slot index of the forward reference picture
// Populate H.264 reference list modification/marking ops and other parameters
...

StdVideoEncodeH264PictureInfo stdPictureInfo = {};
// Populate H.264 picture info for the encode input picture
...
stdPictureInfo.primary_pic_type = STD_VIDEO_H264_PICTURE_TYPE_P;
...
stdPictureInfo.pRefLists = &stdRefListInfo;

VkVideoEncodeH264PictureInfoKHR encodeH264PictureInfo = {
    .sType = VK_STRUCTURE_TYPE_VIDEO_ENCODE_H264_PICTURE_INFO_KHR,
    .pNext = NULL,
    .naluSliceEntryCount = ... // number of slices to encode
    .pNaluSliceEntries = ... // pointer to the array of slice parameters
    .pStdPictureInfo = &stdPictureInfo
};

VkVideoEncodeInfoKHR encodeInfo = {
    .sType = VK_STRUCTURE_TYPE_VIDEO_ENCODE_INFO_KHR,
    .pNext = &encodeH264PictureInfo,
    ...
    .referenceSlotCount = 1,
    .pReferenceSlots = &referenceSlotInfo
};

vkCmdEncodeVideoKHR(commandBuffer, &encodeInfo);

vkCmdEndVideoCodingKHR(commandBuffer, ...);

4.6. 记录产生具有前向和后向参考的 B 帧的 H.264 编码操作

// Bound reference resource list provided has to include the used reference picture resources
vkCmdBeginVideoCodingKHR(commandBuffer, ...);

StdVideoEncodeH264ReferenceInfo stdForwardReferenceInfo = {};
// Populate H.264 reference picture info for the forward referenced picture
...

StdVideoEncodeH264ReferenceInfo stdBackwardReferenceInfo = {};
// Populate H.264 reference picture info for the backward referenced picture
...

VkVideoEncodeH264DpbSlotInfoKHR encodeH264DpbSlotInfo[] = {
    {
        .sType = VK_STRUCTURE_TYPE_VIDEO_ENCODE_H264_DPB_SLOT_INFO_KHR,
        .pNext = NULL,
        .pStdReferenceInfo = &stdForwardReferenceInfo
    },
    {
        .sType = VK_STRUCTURE_TYPE_VIDEO_ENCODE_H264_DPB_SLOT_INFO_KHR,
        .pNext = NULL,
        .pStdReferenceInfo = &stdBackwardReferenceInfo
    }
};

VkVideoReferenceSlotInfoKHR referenceSlotInfo[] = {
    {
        .sType = VK_STRUCTURE_TYPE_VIDEO_REFERENCE_SLOT_INFO_KHR,
        .pNext = &encodeH264DpbSlotInfo[0],
        .slotIndex = ... // DPB slot index of the forward reference picture
        ...
    },
    {
        .sType = VK_STRUCTURE_TYPE_VIDEO_REFERENCE_SLOT_INFO_KHR,
        .pNext = &encodeH264DpbSlotInfo[1],
        .slotIndex = ... // DPB slot index of the backward reference picture
        ...
    }
};

StdVideoEncodeH264ReferenceListsInfo stdRefListInfo = {};
// Initialize the RefPicLists, add the forward reference to the L0 list,
// and add the backward reference to the L1 list
for (uint32_t i = 0; i < STD_VIDEO_H264_MAX_NUM_LIST_REF; ++i) {
    stdRefListInfo.RefPicList0[i] = STD_VIDEO_H264_NO_REFERENCE_PICTURE;
    stdRefListInfo.RefPicList1[i] = STD_VIDEO_H264_NO_REFERENCE_PICTURE;
}
stdRefListInfo.RefPicList0[0] = ... // DPB slot index of the forward reference picture
stdRefListInfo.RefPicList1[0] = ... // DPB slot index of the backward reference picture
// Populate H.264 reference list modification/marking ops and other parameters
...

StdVideoEncodeH264PictureInfo stdPictureInfo = {};
// Populate H.264 picture info for the encode input picture
...
stdPictureInfo.primary_pic_type = STD_VIDEO_H264_PICTURE_TYPE_B;
...
stdPictureInfo.pRefLists = &stdRefListInfo;

VkVideoEncodeH264PictureInfoKHR encodeH264PictureInfo = {
    .sType = VK_STRUCTURE_TYPE_VIDEO_ENCODE_H264_PICTURE_INFO_KHR,
    .pNext = NULL,
    .naluSliceEntryCount = ... // number of slices to encode
    .pNaluSliceEntries = ... // pointer to the array of slice parameters
    .pStdPictureInfo = &stdPictureInfo
};

VkVideoEncodeInfoKHR encodeInfo = {
    .sType = VK_STRUCTURE_TYPE_VIDEO_ENCODE_INFO_KHR,
    .pNext = &encodeH264PictureInfo,
    ...
    .referenceSlotCount = sizeof(referenceSlotInfo) / sizeof(referenceSlotInfo[0]),
    .pReferenceSlots = &referenceSlotInfo[0]
};

vkCmdEncodeVideoKHR(commandBuffer, &encodeInfo);

vkCmdEndVideoCodingKHR(commandBuffer, ...);

4.7. 使用可选的 H.264 控制更改 H.264 编码会话的速率控制配置

vkCmdBeginVideoCodingKHR(commandBuffer, ...);

// Include the optional H.264 rate control layer information
// In this example we restrict the QP range to be used by the implementation
VkVideoEncodeH264RateControlLayerInfoKHR rateControlLayersH264[] = {
    {
        .sType = VK_STRUCTURE_TYPE_VIDEO_ENCODE_H264_RATE_CONTROL_LAYER_INFO_KHR,
        .pNext = NULL,
        .useMinQp = VK_TRUE,
        .minQp = { /* min I frame QP */, /* min P frame QP */, /* min B frame QP */ },
        .useMaxQp = VK_TRUE,
        .minQp = { /* max I frame QP */, /* max P frame QP */, /* max B frame QP */ },
        .useMaxFrameSize = VK_FALSE,
        .maxFrameSize = { 0, 0, 0 }
    },
    ...
};

VkVideoEncodeRateControlLayerInfoKHR rateControlLayers[] = {
    {
        .sType = VK_STRUCTURE_TYPE_VIDEO_ENCODE_RATE_CONTROL_LAYER_INFO_KHR,
        .pNext = &rateControlLayersH264[0],
        ...
    },
    ...
};

// Include the optional H.264 global rate control information
VkVideoEncodeH264RateControlInfoKHR rateControlInfoH264 = {
    .sType = VK_STRUCTURE_TYPE_VIDEO_ENCODE_H264_RATE_CONTROL_INFO_KHR,
    .pNext = NULL,
    .flags = VK_VIDEO_ENCODE_H264_RATE_CONTROL_REGULAR_GOP_BIT_KHR // Indicate the use of a regular GOP structure...
           | VK_VIDEO_ENCODE_H264_RATE_CONTROL_TEMPORAL_LAYER_PATTERN_DYADIC_BIT_KHR, // ... and a dyadic temporal layer pattern
    // Indicate a GOP structure of the form IBBBPBBBPBBBI with an IDR frame at the beginning of every 10th GOP
    .gopFrameCount = 12,
    .idrPeriod = 120,
    .consecutiveBFrameCount = 3,
    // This example uses multiple temporal layers with per layer rate control
    .temporalLayerCount = sizeof(rateControlLayers) / sizeof(rateControlLayers[0])
};

VkVideoEncodeRateControlInfoKHR rateControlInfo = {
    .sType = VK_STRUCTURE_TYPE_VIDEO_ENCODE_RATE_CONTROL_INFO_KHR,
    .pNext = &rateControlInfoH264,
    ...
    .layerCount = sizeof(rateControlLayers) / sizeof(rateControlLayers[0]),
    .pLayers = rateControlLayers,
    ...
};

// Change the rate control configuration for the video session
VkVideoCodingControlInfoKHR controlInfo = {
    .sType = VK_STRUCTURE_TYPE_VIDEO_CODING_CONTROL_INFO_KHR,
    .pNext = &rateControlInfo,
    .flags = VK_VIDEO_CODING_CONTROL_ENCODE_RATE_CONTROL_BIT_KHR
};

vkCmdControlVideoCodingKHR(commandBuffer, &controlInfo);

...

vkCmdEndVideoCodingKHR(commandBuffer, ...);

5. 问题

5.1. 已解决:应以何种形式提供编解码器特定参数?

vulkan_video_codec_h264std_encodevulkan_video_codec_h264std 视频标准头文件定义的结构形式提供。应用程序负责填充视频标准头文件定义的结构。应用程序还负责维护和管理这些数据结构,以便在需要时将其作为视频编码操作的输入提供。

5.2. 已解决:为什么 vulkan_video_codec_h264std 视频标准头文件没有版本号?

引入 vulkan_video_codec_h264std 视频标准头文件是为了共享 H.264/AVC 视频解码和视频编码中使用的通用定义,因为这两个功能是并行设计的。然而,由于没有视频编码扩展直接使用此视频标准头文件,仅作为特定视频编码操作的视频标准头文件的依赖项,因此认为没有必要采用单独的版本控制方案。

5.3. 已解决:编解码器特定输入参数的要求是什么?

从 API 使用的角度来看,应用程序为编解码器特定输入参数(参数集、图像信息等)提供任何值都是合法的。但是,如果输入数据不符合 H.264/AVC 视频压缩标准的要求,则视频编码操作可能会不成功地完成,并且通常,视频编码操作产生的输出将具有未定义的内容。

此外,如果任何指定的编解码器特定参数不符合 H.264/AVC 视频压缩标准的语法或语义要求,或者如果根据 H.264/AVC 视频压缩标准定义的规则从参数导出的值不符合 H.264/AVC 视频压缩标准或实现的功能,则某些命令可能会返回 VK_ERROR_INVALID_VIDEO_STD_PARAMETERS_KHR 错误。特别是,在此扩展中,以下命令可能会返回此错误代码

  • vkCreateVideoSessionParametersKHRvkUpdateVideoSessionParametersKHR - 如果指定的参数集根据这些规则无效

  • vkEndCommandBuffer - 如果提供给视频编码操作的编解码器特定图像信息根据这些规则无效

但是,不要求在上述情况下生成错误,因此应用程序不应依赖接收错误代码来验证所用编解码器特定参数的正确性。

5.4. 已解决:是否支持隔行扫描帧?

否。编码隔行 H.264 内容似乎不是需要支持的重要用例。

5.5. 已解决:我们是否希望允许应用程序为每个切片指定单独的参考列表?

在此扩展中不这样做。虽然 H.264/AVC 视频压缩标准似乎支持此功能,但为了简单起见,这里不公开这种灵活性。如果需要支持每个切片的参考列表操作,则分层扩展可以引入必要的 API 来启用它。

5.6. 已解决:当使用多个时间层时,实现是否生成前缀 NAL 单元?

仅当实现支持 VK_VIDEO_ENCODE_H264_CAPABILITY_GENERATE_PREFIX_NALU_BIT_KHR 功能标志,并且应用程序使用 generatePrefixNalu 参数显式请求生成前缀 NAL 单元时才生成。

如果应用程序打算在不支持生成前缀 NALU 单元的实现上使用多个时间层,则应用程序负责将其插入到最终比特流中。

5.7. 已解决:哪些编解码器特定参数保证不会被实现覆盖?

此提案仅要求实现不要覆盖 primary_pic_typeslice_type 参数,因为所使用的图像和切片类型是 H.264 编码基本操作的关键。此外,在 stdSyntaxFlags 功能中设置的位提供了关于实现将使用而不会覆盖的其他视频标准参数的额外保证。此扩展不包含关于编解码器特定参数覆盖的进一步限制,但是,未来的扩展可能会包含根据 API 用户需求提供额外保证的功能标志。

5.8. 已解决:如何请求 H.264 编码操作的参考图像设置?

由于根据视频扩展的最新修订,始终需要指定重建图像 DPB 插槽和资源,因此额外的编解码器语法控制是否请求参考图像设置,并且作为响应,重建图像激活 DPB 插槽。

对于 H.264 编码,如果且仅当设置了 StdVideoEncodeH264PictureInfo::flags.is_reference 标志时,才请求参考图像设置,并且为重建图像指定的 DPB 插槽会激活该图像。

6. 进一步功能

未来的扩展可以进一步扩展此处提供的功能,例如,公开支持允许每个切片输入和/或输出的编码模式。