NVIDIA 视频编解码器 SDK v13.0

NVENC 视频编码器 API 编程指南

基于 NVIDIA Kepler™ 及更高版本 GPU 架构的 NVIDIA® GPU 包含一个基于硬件的 H.264/HEVC/AV1 视频编码器(以下简称 NVENC)。NVENC 硬件采用 YUV/RGB 作为输入,并生成符合 H.264/HEVC/AV1 标准的视频比特流。NVENC 硬件的编码功能可以使用 NVIDIA Video Codec SDK 中提供的 NVENCODE API 进行访问。

本文档提供有关如何使用 SDK 中公开的 NVENCODE API 编程 NVENC 的信息。NVENCODE API 在 Windows(Windows 10 及更高版本)和 Linux 上公开了编码功能。

预计开发人员应了解 H.264/HEVC/AV1 视频编解码器,并熟悉 Windows 和/或 Linux 开发环境。

NVENCODE API 保证 二进制向后兼容性(并在向后兼容性被破坏时进行明确说明)。这意味着使用旧版本发布 API 编译的应用程序将继续在 NVIDIA 发布的未来驱动程序版本上工作。

开发人员可以创建一个客户端应用程序,该应用程序调用 Windows 的 nvEncodeAPI.dll 或 Linux 的 libnvidia-encode.so 公开的 NVENCODE API 函数。这些库作为 NVIDIA 显示驱动程序的一部分安装。客户端应用程序可以在运行时链接到这些库,在 Windows 上使用 LoadLibrary() 或在 Linux 上使用 dlopen()

NVENCODE API 函数、结构和其他参数在 SDK 中包含的 nvEncodeAPI.h 中公开。

NVENCODE API 是一个 C-API,并使用类似于 C++ 接口的设计模式,其中应用程序创建 API 的实例并检索函数指针表以进一步与编码器交互。对于喜欢使用带有即用型代码的更高级别 API 的程序员,SDK 包含示例 C++ 类,这些类公开了重要的 API 函数。

本文档的其余部分重点介绍 nvEncodeAPI.h 中公开的 C-API。NVENCODE API 旨在接受原始视频帧(YUV 或 RGB 格式)并输出 H.264、HEVC 或 AV1 比特流。广义上,编码流程包括以下步骤

  1. 初始化编码器
  2. 设置所需的编码参数
  3. 分配输入/输出缓冲区
  4. 将帧复制到输入缓冲区并从输出缓冲区读取比特流。这可以同步完成(Windows 和 Linux),也可以异步完成(仅限 Windows 10 及更高版本)。
  5. 清理 - 释放所有已分配的输入/输出缓冲区
  6. 关闭编码会话

这些步骤在本文档的其余部分进行了解释,并在 Video Codec SDK 包中包含的示例应用程序中进行了演示。

打开编码会话

加载 DLL 或共享对象库后,客户端与 API 的首次交互是调用 NvEncodeAPICreateInstance。这会将传递给 NvEncodeAPICreateInstance 的输入/输出缓冲区填充为指向实现接口中提供的功能的函数的指针。

加载 NVENC 接口后,客户端应首先调用 NvEncOpenEncodeSessionEx 以打开编码会话。此函数返回一个编码会话句柄,该句柄必须用于当前会话中对 API 函数的所有后续调用。

初始化编码设备

NVIDIA 编码器支持使用以下类型的设备

DirectX 9

  • 客户端应创建一个 DirectX 9 设备,其行为标志包括:D3DCREATE_FPU_PRESERVED3DCREATE_MULTITHREADEDD3DCREATE_HARDWARE_VERTEXPROCESSING
  • 客户端应将指向创建的设备的 IUnknown 接口的指针(类型转换为 void *)作为 NV_ENC_OPEN_ENCODE_SESSION_EX_PARAMS::device, 传递,并将 NV_ENC_OPEN_ENCODE_SESSION_EX_PARAMS::deviceType 设置为 NV_ENC_DEVICE_TYPE_DIRECTX。 仅 Windows 10 及更高版本的 Windows 操作系统支持使用 DirectX 设备。

DirectX 10

  • 客户端应将指向创建的设备的 IUnknown 接口的指针(类型转换为 void *)作为 NV_ENC_OPEN_ENCODE_SESSION_EX_PARAMS::device, 传递,并将 NV_ENC_OPEN_ENCODE_SESSION_EX_PARAMS::deviceType 设置为 NV_ENC_DEVICE_TYPE_DIRECTX。 仅 Windows 10 及更高版本的 Windows 操作系统支持使用 DirectX 设备。

DirectX 11

  • 客户端应将指向创建的设备的 IUnknown 接口的指针(类型转换为 void *)作为 NV_ENC_OPEN_ENCODE_SESSION_EX_PARAMS::device, 传递,并将 NV_ENC_OPEN_ENCODE_SESSION_EX_PARAMS::deviceType 设置为 NV_ENC_DEVICE_TYPE_DIRECTX。 仅 Windows 10 及更高版本的 Windows 操作系统支持使用 DirectX 设备。

DirectX 12

  • 客户端应将指向创建的设备的 IUnknown 接口的指针(类型转换为 void *)作为 NV_ENC_OPEN_ENCODE_SESSION_EX_PARAMS::device, 传递,并将 NV_ENC_OPEN_ENCODE_SESSION_EX_PARAMS::deviceType 设置为 NV_ENC_DEVICE_TYPE_DIRECTX。 仅 Windows 10 20H1 及更高版本的 Windows 操作系统支持使用 DirectX 12 设备。

CUDA

  • 客户端应创建一个浮动 CUDA 上下文,并将 CUDA 上下文句柄作为 NV_ENC_OPEN_ENCODE_SESSION_EX_PARAMS::device, 传递,并将 NV_ENC_OPEN_ENCODE_SESSION_EX_PARAMS::deviceType 设置为 NV_ENC_DEVICE_TYPE_CUDA。 Linux 和 Windows 10 及更高版本的 Windows 操作系统支持使用 CUDA 设备进行编码。

OpenGL

  • 客户端应创建一个 OpenGL 上下文并使其成为当前上下文(为了将上下文与调用 NVENCODE API 的线程/进程相关联)到调用 NVENCODE API 的线程。NV_ENC_OPEN_ENCODE_SESSION_EX_PARAMS::device 必须为 NULL,并且 NV_ENC_OPEN_ENCODE_SESSION_EX_PARAMS::deviceType 必须设置为 NV_ENC_DEVICE_TYPE_OPENGL。 仅 Linux 支持使用 OpenGL 设备类型进行编码。

选择编码器编解码器 GUID

客户端应选择一个编码 GUID,该 GUID 代表用于编码视频序列的所需编解码器,具体方法如下

  1. 客户端应调用 NvEncGetEncodeGUIDCount 以从 NVIDIA 视频编码器接口获取支持的编码器 GUID 的数量。
  2. 客户端应使用此计数来分配足够大的缓冲区以容纳支持的编码器 GUID。
  3. 然后,客户端应调用 NvEncGetEncodeGUIDs 以填充此列表。

客户端应从此列表中选择一个符合其要求的 GUID,并将其用作编码会话其余部分的 encodeGUID

编码器 TUNING INFO 和预设配置

NVIDIA 编码器接口公开了四种不同的 tuning info 枚举(高质量、低延迟、超低延迟和无损),以满足不同的视频编码用例。表 1 显示了适用于某些常用用例的建议 tuning info。
用例tuning info 参数的建议值
  1. 高质量、延迟容忍的转码
  2. 视频存档
  3. OTT 流媒体编码
超高质量 / 高质量
  1. 云游戏
  2. 流媒体
  3. 视频会议

在高带宽通道中,可以容忍更大的偶尔帧大小

低延迟,带 CBR
  1. 云游戏
  2. 流媒体
  3. 视频会议

在严格带宽受限的通道中

超低延迟,带 CBR
  1. 保留原始视频素材以供后续编辑
  2. 通用无损数据存档(视频或非视频)
无损

对于每个 tuning info,都提供了从 P1(最高性能)到 P7(最低性能)的七个预设,以控制性能/质量的权衡。使用这些预设将自动为选定的 tuning info 设置所有相关的编码参数。这是 API 公开的粗略控制级别。如果需要,可以调整预设中的特定属性/参数。这将在接下来的两个小节中进行解释。

枚举预设 GUID

客户端可以按如下方式枚举所选 encodeGUID 的支持的预设 GUID

  1. 客户端应调用 NvEncGetEncodePresetCount 以获取支持的编码器 GUID 的数量。
  2. 客户端应使用此计数来分配足够大的缓冲区以容纳支持的预设 GUID。
  3. 然后,客户端应调用 NvEncGetEncodePresetGUIDs 以填充此列表。

选择编码器预设配置

如上所述,客户端可以直接使用 presetGUID 来配置编码会话。这将自动为 tuning info/预设组合暗示的用例设置具有适当参数的硬件编码器。如果需要,客户端可以选择微调预设中的编码器配置参数并覆盖预设默认值。从编程的角度来看,这种方法通常更方便,因为程序员只需要更改他/她感兴趣的配置参数,而将其他所有内容都按照预设定义进行预配置。

以下是获取预设编码配置以及可选地更改选择配置参数的步骤

  1. 如上文 枚举预设 GUID 部分所述,枚举支持的预设。
  2. 选择要从中获取编码配置的预设 GUID。
  3. 客户端应使用选定的 encodeGUID、tuningInfopresetGUID 作为输入调用 NvEncGetEncodePresetConfigEx
  4. 所需的预设编码器配置可以通过 NV_ENC_PRESET_CONFIG::presetCfg 检索。
  5. 如果需要,使用相应的配置 API 覆盖默认编码器参数。

选择编码器配置文件

客户端可以指定一个配置文件以针对特定的编码场景进行编码。例如,在 iPhone/iPod 上播放视频、为蓝光光盘创作编码视频等需要某些配置文件。

客户端应执行以下操作以检索支持的编码器配置文件的列表

  1. 客户端应调用 NvEncGetEncodeProfileGUIDCount 以从 NVIDIA 视频编码器接口获取支持的编码器 GUID 的数量。
  2. 客户端应使用此计数来分配足够大小的缓冲区以容纳支持的编码配置文件 GUID。
  3. 然后,客户端应调用 NvEncGetEncodeProfileGUIDs 以填充此列表。

客户端应选择最符合要求的配置文件 GUID。

获取支持的输入格式列表

NVENCODE API 接受多种不同格式的输入帧,例如特定格式的 YUV 和 RGB,如 NV_ENC_BUFFER_FORMAT 中枚举的那样。

支持的输入格式列表可以按如下方式检索

  1. 客户端应调用 NvEncGetInputFormatCount 以获取支持的输入格式的数量。
  2. 客户端应使用此计数来分配缓冲区以容纳支持的输入缓冲区格式列表(这些格式是 NV_ENC_BUFFER_FORMAT 类型的列表元素)。
  3. 通过调用 NvEncGetInputFormats 检索支持的输入缓冲区格式。

客户端应选择此列表中枚举的格式以创建输入缓冲区。

查询编码器功能

NVIDIA 视频编码器硬件已经经历了多个世代的发展,在每个新一代 GPU 中都添加了许多功能。为了方便应用程序动态地了解系统上底层硬件编码器的功能,NVENCODE API 提供了专用的 API 来查询这些功能。在利用所需编码器功能之前,查询对该功能的支持是一种良好的编程实践。

查询编码器功能可以按如下方式完成

  1. NV_ENC_CAPS_PARAM::capsToQuery 参数中指定要查询的功能属性。这应该是 NV_ENC_CAPS 枚举的成员。
  2. 调用 NvEncGetEncodeCaps 以确定对所需属性的支持。

有关各个功能属性的解释,请参阅 API 参考 NV_ENC_CAPS 枚举定义。

初始化硬件编码器会话

客户端需要使用通过 NV_ENC_INITIALIZE_PARAMS 指定的有效编码器配置和编码器句柄(在成功打开编码会话后返回)调用 NvEncInitializeEncoder

编码会话属性

配置编码会话属性

编码会话配置分为三个部分

会话参数

输入格式、输出尺寸、显示纵横比、帧速率、平均比特率等常用参数在 NV_ENC_INITIALIZE_PARAMS 结构中可用。客户端应使用此结构的实例作为 NvEncInitalizeEncoder 的输入。

客户端必须填充 NV_ENC_INITIALIZE_PARAMS 结构的以下成员,以便成功初始化编码会话

  • NV_ENC_INITALIZE_PARAMS::encodeGUID:客户端必须选择合适的编解码器 GUID,如 选择编码器编解码器 GUID 部分所述。
  • NV_ENC_INITIALIZE_PARAMS::encodeWidth:客户端必须指定编码视频的所需宽度。
  • NV_ENC_INITIALIZE_PARAMS::encodeHeight:客户端必须指定编码视频的所需高度。

NV_ENC_INITIALIZE_PARAMS::reportSliceOffsets 可用于启用切片偏移量的报告。此功能要求 NV_ENC_INITALIZE_PARAMS::enableEncodeAsync 设置为 0,并且不适用于 Kepler GPU 上基于 MB 和基于字节的切片。

高级编解码器级别参数

处理编码比特流的参数,例如 GOP 长度、编码器配置文件、速率控制模式等,通过 NV_ENC_CONFIG 结构公开。客户端可以通过 NV_ENC_INITIALIZE_PARAMS::encodeConfig 传递编解码器级别参数,如下所述。

高级编解码器特定参数

高级 H.264、HEVC 和 AV1 特定参数分别在 NV_ENC_CONFIG_H264NV_ENC_CONFIG_HEVCNV_ENC_CONFIG_AV1 结构中可用。

客户端可以通过 NV_ENC_CONFIG::encodeCodecConfig 结构传递编解码器特定参数。

完成用于编码的编解码器配置

使用预设进行高级控制

这是配置 NVIDIA 视频编码器接口的最简单方法,并且涉及客户端执行的最少设置步骤。这适用于客户端不需要微调任何编解码器级别参数的用例。

在这种情况下,客户端应遵循以下步骤

  • 客户端应指定会话参数,如 会话参数 部分所述。
  • 可选地,客户端可以枚举并选择最适合当前用例的预设 GUID,如 选择编码器编解码器 GUID 部分所述。然后,客户端应使用 NV_ENC_INITIALIZE_PARAMS::presetGUID 传递选定的预设 GUID。这有助于 NVIDIA 视频编码器接口根据提供的 encodeGUID、tuning infopresetGUID 正确配置编码器会话。
  • 客户端应将高级编解码器级别参数指针 NV_ENC_INITIALIZE_PARAMS::encodeConfig::encodeCodecConfig 设置为 NULL。

通过覆盖预设参数进行更精细的控制

客户端可以选择在各个预设设置的参数之上编辑一些编码参数,如下所示

  1. 客户端应指定会话参数,如 会话参数 部分所述。
  2. 客户端应枚举并选择最适合当前用例的预设 GUID,如 选择编码器编解码器 GUID 部分所述。客户端应检索预设编码配置,如 选择编码器预设配置 部分所述。
  3. 客户端可能需要显式查询编码器的功能,以支持某些功能或某些编码配置参数。为此,客户端应执行以下操作
  4. 通过 NV_ENC_CAPS_PARAM::capsToQuery 参数指定所需的功能属性。这应该是 NV_ENC_CAPS 枚举的成员。
  5. 调用 NvEncGetEncodeCaps 以确定对所需属性的支持。有关各个功能属性的解释,请参阅 API 参考中的 NV_ENC_CAPS 枚举定义。
  6. 选择所需的 tuning info 和预设 GUID,并获取相应的预设编码配置,如 编码器 TUNING INFO 和预设配置 部分所述。
  7. 然后,客户端可以根据其要求覆盖预设 NV_ENC_CONFIG 中的任何参数。客户端应使用 NV_ENC_INITIALIZE_PARAMS::encodeConfig::encodeCodecConfig 指针传递微调后的 NV_ENC_CONFIG 结构。
  8. 此外,客户端还应通过 NV_ENC_INITIALIZE_PARAMS::presetGUID 传递选定的预设 GUID。这是为了允许 NVIDIA 视频编码器接口对与编码会话关联的内部参数进行编程,以确保编码输出符合客户端的请求。请注意,在此处传递预设 GUID 不会覆盖微调后的参数。

速率控制

NVENC 支持多种速率控制模式,并通过 NV_ENC_INITIALIZE_PARAMS::encodeConfig::rcParams 结构提供对与速率控制算法相关的各种参数的控制。速率控制算法在 NVENC 固件中实现。

NVENC 支持以下速率控制模式

恒定比特率 (CBR):恒定比特率通过将 rateControlMode 设置为 NV_ENC_PARAMS_RC_CBR 来指定。在此模式下,速率控制算法仅需要 averageBitRate 并将其用作目标输出比特率。客户端可以使用 NV_ENC_RC_PARAMS::lowDelayKeyFrameScale 控制 I 帧与 P 帧的比率,这在 I 帧最终生成大量比特的情况下对于避免通道拥塞非常有用。如果需要严格遵守比特率,请设置 NV_ENC_CONFIG_H264/ NV_ENC_CONFIG_HEVC::enableFillerDataInsertion = 1NV_ENC_CONFIG_AV1::enableBitstreamPadding = 1

可变比特率 (VBR):可变比特率通过将 rateControlMode 设置为 NV_ENC_PARAMS_RC_VBR 来指定。编码器尝试在长期内符合 averageBitRate 的平均比特率,同时在编码期间任何时候都不超过 maxBitRate。在此模式下,必须指定 averageBitRate。如果未指定 maxBitRate,NVENC 将其设置为内部确定的默认值。建议客户端同时指定 maxBitRateaverageBitRate 参数以获得更好的控制。

恒定 QP:此模式通过将 rateControlMode 设置为 NV_ENC_PARAMS_RC_CONSTQP 来指定。在此模式下,整个帧都使用 NV_ENC_RC_PARAMS::constQP 中指定的 QP 进行编码。

目标质量:此模式通过将 rateControlMode 设置为 VBR 以及 targetQuality 中所需的目标质量来指定。H264/HEVC 的目标质量范围为 0 到 51,AV1 的目标质量范围为 0 到 63(Video Codec SDK 8.0 及更高版本也支持小数值)。在此模式下,编码器尝试为每帧保持恒定的质量,方法是允许比特率在 maxBitRate 中指定的比特率参数范围内变化。因此,最终的平均比特率可能会因编码的视频内容而异。在此模式下,如果设置了 maxBitRate,它将构成实际比特率的上限。因此,如果 maxBitRate 设置得太低,则比特率可能会受到限制,从而可能无法实现所需的目标质量。

多通道帧编码

在确定用于编码帧的 QP 时,如果 NVENC 知道帧的整体复杂性以最优化方式分配可用比特预算,则会很有益处。在某些情况下,多通道编码也可能有助于捕获帧之间较大的运动。为此,NVENC 支持以下类型的多通道帧编码模式

  • 每帧 1 通道编码 (NV_ENC_MULTI_PASS_DISABLED)
  • 每帧 2 通道,第一通道为四分之一分辨率,第二通道为全分辨率 (NV_ENC_TWO_PASS_QUARTER_RESOLUTION)
  • 每帧 2 通道,两个通道均为全分辨率 (NV_ENC_TWO_PASS_FULL_RESOLUION)。

在 1 通道速率控制模式下,NVENC 估计宏块所需的 QP 并立即编码宏块。在 2 通道速率控制模式下,NVENC 估计要编码的帧的复杂性,并在第一通道中确定帧上的比特分布。在第二通道中,NVENC 使用第一通道中确定的分布对帧中的宏块进行编码。因此,使用 2 通道速率控制模式,NVENC 可以更优化地在帧内分配比特,并且可以更接近目标比特率,特别是对于 CBR 编码。但是请注意,在其他所有条件相同的情况下,2 通道速率控制模式的性能低于 1 通道速率控制模式。客户端应用程序应在评估各种模式后选择合适的多通道速率控制模式,因为每种模式都有其自身的优点和缺点。NV_ENC_TWO_PASS_FULL_RESOLUION 为第二通道生成更好的统计信息,而 NV_ENC_TWO_PASS_QUARTER_RESOLUTION 会捕获更大的运动矢量并将其作为提示馈送到第二通道。

设置编码会话属性

一旦所有编码器设置都已最终确定,客户端应填充 NV_ENC_CONFIG 结构,并将其用作 NvEncInitializeEncoder 的输入,以冻结当前编码会话的编码设置。某些设置(例如速率控制模式、平均比特率、分辨率等)可以即时更改。

客户端需要在初始化编码会话时显式指定以下内容

操作模式

如果客户端想要在异步模式下运行,则应将 NV_ENC_INITIALIZE_PARAMS::enableEncodeAsync 设置为 1,如果要在同步模式下运行,则设置为 0。

仅 Windows 10 及更高版本支持异步模式编码。有关更详细的说明,请参阅第 6 章。

图片类型决策

如果客户端想要按显示顺序发送输入缓冲区,则必须设置 enablePTD = 1。 如果 enablePTD 设置为 1,则确定图片类型的决策将由 NVENCODE API 做出。

如果客户端想要按编码顺序发送输入缓冲区,则必须设置 enablePTD = 0, 并且必须指定

NV_ENC_PIC_PARAMS::pictureType

NV_ENC_PIC_PARAMS_H264/NV_ENC_PIC_PARAMS_HEVC/NV_ENC_PIC_PARAMS_AV1::displayPOCSyntax

NV_ENC_PIC_PARAMS_H264/NV_ENC_PIC_PARAMS_HEVC/NV_ENC_PIC_PARAMS_AV1::refPicFlag

NV_ENC_PIC_PARAMS_AV1::goldenFrameFlag/arfFrameFlag/arf2FrameFlag/bwdFrameFlag/overlayFrameFlag

创建保存输入/输出数据所需的资源

初始化编码会话后,客户端应分配缓冲区以保存输入/输出数据。

客户端可以选择通过调用 NvEncCreateInputBuffer API 通过 NVIDIA 视频编码器接口分配输入缓冲区。在这种情况下,客户端负责在关闭编码会话之前销毁已分配的输入缓冲区。客户端还有责任根据选择的输入缓冲区格式使用有效的输入数据填充输入缓冲区。

客户端应使用 NvEncCreateBitstreamBuffer API 分配缓冲区以保存输出编码比特流。客户端负责在关闭编码会话之前销毁这些缓冲区。

或者,在客户端不能或不想通过 NVIDIA 视频编码器接口分配输入缓冲区的情况下,它可以将任何外部分配的 DirectX 资源用作输入缓冲区。但是,客户端必须执行一些简单的处理,才能在使用前将这些资源映射到 NVIDIA 视频编码器接口识别的资源句柄。翻译过程在 外部分配的输入缓冲区 部分中进行了解释。

如果客户端已使用 CUDA 设备初始化编码器会话,并且希望使用 NOT 通过 NVIDIA 视频编码器接口分配的输入缓冲区,则客户端需要使用使用 cuMemAlloc 系列 API 分配的缓冲区。NVIDIA 视频编码器接口支持 CUdeviceptrCUarray 输入格式。

如果客户端已使用 OpenGL 设备类型初始化编码器会话,并且希望使用 NOT 通过 NVIDIA 视频编码器接口分配的输入缓冲区,则客户端需要提供先前分配的纹理。

客户端可以使用 glGenTextures() 生成纹理,将其绑定到 NV_ENC_INPUT_RESOURCE_OPENGL_TEX::GL_TEXTURE_RECTANGLENV_ENC_INPUT_RESOURCE_OPENGL_TEX::GL_TEXTURE_2D 目标,使用 glTexImage2D() 为其分配存储空间,并将数据复制到其中。

请注意,NVENCODE API 的 OpenGL 接口仅在 Linux 上受支持。

如果客户端已使用 DirectX 12 设备初始化编码器会话,则客户端必须使用 ID3D12Device::CreateCommittedResource() API 分配输入和输出缓冲区。客户端必须执行一些简单的处理,以将这些输入和输出资源映射到 NVIDIA 视频编码器接口可识别的资源句柄,然后才能使用。翻译过程在 DirectX 12 的输入输出缓冲区分配 部分中进行了解释。

注意:客户端应至少分配 (1 + NB) 个输入和输出缓冲区,其中 NB 是连续 P 帧之间的 B 帧数量。

检索序列参数

配置编码会话后,客户端可以随时通过调用 NvEncGetSequenceParams 来检索序列参数信息(H.264/HEVC 的 SPS 和序列头 OBU,以及 AV1 的序列头 OBU)。客户端有责任分配和最终释放大小为 MAX_SEQ_HDR_LEN 的缓冲区,以保存序列参数信息。

默认情况下,SPS/PPS 和序列头 OBU 数据将分别附加到 H.264/HEVC 和 AV1 的每个 IDR 帧和关键帧。但是,客户端也可以请求编码器根据需要生成 SPS/PPS 和序列头 OBU 数据。要实现此目的,请设置 NV_ENC_PIC_PARAMS::encodePicFlags = NV_ENC_PIC_FLAG_OUTPUT_SPSPPS。 然后,为当前输入生成的输出码流将包含 H.264/HEVC 的 SPS/PPS 或 AV1 的序列头 OBU。

客户端可以在编码器初始化 (NvEncInitializeEncoder) 并且会话处于活动状态后,随时调用 NvEncGetSequenceParams

配置编码会话并分配输入/输出缓冲区后,客户端可以开始流式传输输入数据以进行编码。客户端需要将有效输入缓冲区的句柄和有效位流(输出)缓冲区传递给 NVIDIA 视频编码器接口,以编码输入图片。

准备用于编码的输入缓冲区

有两种方法可以分配输入缓冲区并将其传递给视频编码器。

通过 NVIDIA 视频编码器接口分配的输入缓冲区

如果客户端已通过 NvEncCreateInputBuffer 分配了输入缓冲区,则客户端需要在将缓冲区用作编码输入之前填充有效输入数据。为此,客户端应调用 NvEncLockInputBuffer 以获取输入缓冲区的 CPU 指针。客户端填充输入数据后,应调用 NvEncUnlockInputBuffer。输入缓冲区应仅在解锁后才传递给编码器。任何输入缓冲区都应在销毁/重新分配之前通过调用 NvEncUnlockInputBuffer 来解锁。

外部分配的输入缓冲区

要将外部分配的缓冲区传递给编码器,请按照以下步骤操作

  1. 使用外部分配缓冲区的属性填充 NV_ENC_REGISTER_RESOURCE
  2. 使用上一步中填充的 NV_ENC_REGISTER_RESOURCE 调用 NvEncRegisterResource
  3. NvEncRegisterResourceNV_ENC_REGISTER_RESOURCE::registeredResource 中返回一个不透明句柄,应保存该句柄。
  4. 使用上面返回的句柄调用 NvEncMapInputResource
  5. 映射的句柄将在 NV_ENC_MAP_INPUT_RESOURCE::mappedResource. 中可用。
  6. 客户端应将此映射的句柄 (NV_ENC_MAP_INPUT_RESOURCE::mappedResource) 用作 NV_ENC_PIC_PARAMS 中的输入缓冲区句柄。
  7. 客户端完成资源使用后,必须调用 NvEncUnmapInputResource
  8. 客户端还必须在销毁注册资源之前,使用 NvEncRegisterResource 返回的句柄调用 NvEncUnregisterResource

映射的资源句柄 (NV_ENC_MAP_INPUT_RESOURCE::mappedResource) 在处于映射状态时,不应用于 NVIDIA 视频编码器接口之外的任何其他目的。此类用法不受支持,可能会导致未定义的行为。

DirectX 12 的输入输出缓冲区分配

输入和输出缓冲区的分配应按以下方式完成

  1. 输入缓冲区应使用 DirectX 12 ID3D12Device::CreateCommittedResource() API 创建,方法是指定 D3D12_HEAP_PROPERTIES::Type = D3D12_HEAP_TYPE_DEFAULTD3D12_RESOURCE_DESC::Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D
  2. 输出缓冲区应使用 DirectX 12 ID3D12Device::CreateCommittedResource() API 创建,方法是指定 D3D12_HEAP_PROPERTIES::Type = D3D12_HEAP_TYPE_READBACKD3D12_RESOURCE_DESC::Dimension = D3D12_RESOURCE_DIMENSION_BUFFER
  3. 对于 HEVC、H.264 或 AV1 编码,建议的输出缓冲区大小为

    输出缓冲区大小 = 2 * 输入 YUV 缓冲区大小

    以字节为单位。

要将这些外部分配的输入和输出缓冲区传递给编码器,请按照以下步骤操作

  1. 使用外部分配缓冲区的属性填充 NV_ENC_REGISTER_RESOURCE
  2. 要在 DirectX 12 中启用显式同步,API NvEncRegisterResource 接受两个 NV_ENC_FENCE_POINT_D3D12 指针类型对象(Fence point 是 ID3D12Fence 指针和 value 的一对),NV_ENC_REGISTER_RESOURCE_PARAMS_D3D12::pInputFencePointNV_ENC_REGISTER_RESOURCE_PARAMS_D3D12::pOutputFencePoint,用于注册输入缓冲区。NVENC 引擎等待 pInputFencePoint 到达后再处理 NV_ENC_REGISTER_RESOURCE::resourceToRegister。当资源处理完成时,NVENC 引擎会发出 pOutputFencePoint 信号,以便其他需要使用此资源的引擎可以开始处理。
  3. 使用上一步中填充的 NV_ENC_REGISTER_RESOURCE 调用 NvEncRegisterResource
  4. NvEncRegisterResourceNV_ENC_REGISTER_RESOURCE::registeredResource 中返回一个不透明句柄,应保存该句柄。
  5. 客户端应将此注册的句柄 (NV_ENC_REGISTER_RESOURCE::registeredResource) 用作 NV_ENC_INPUT_RESOURCE_D3D12::pInputBufferNV_ENC_INPUT_RESOURCE_D3D12::pOutputBuffer 中的输入和输出缓冲区句柄。
  6. 客户端还必须在销毁注册资源之前,使用 NvEncRegisterResource 返回的句柄调用 NvEncUnregisterResource

注册的资源句柄 (NV_ENC_REGISTER_RESOURCE::registeredResource) 在处于注册状态时,不应用于 NVIDIA 视频编码器接口之外的任何其他目的。此类用法不受支持,可能会导致未定义的行为。

配置逐帧编码参数

客户端应使用要应用于当前输入图片的参数填充 NV_ENC_PIC_PARAMS。客户端可以逐帧执行以下操作。

强制将当前帧编码为帧内帧

要强制将当前帧作为帧内 (I) 帧,请设置

NV_ENC_PIC_PARAMS::encodePicFlags = NV_ENC_PIC_FLAG_FORCEINTRA

强制将当前帧用作参考帧

要强制将当前帧用作参考帧,请设置

NV_ENC_PIC_PARAMS_H264/NV_ENC_PIC_PARAMS_HEVC/NV_ENC_PIC_PARAMS_AV1::refPicFlag = 1

强制将当前帧用作 IDR 帧

要强制将当前帧编码为 IDR 帧,请设置

NV_ENC_PIC_PARAMS::encodePicFlags = NV_ENC_PIC_FLAG_FORCEIDR

请求生成序列参数

要将 SPS/PPS (H.264 和 HEVC) 或序列头 OBU (AV1) 与当前编码的帧一起包含,请设置 NV_ENC_PIC_PARAMS::encodePicFlags = NV_ENC_PIC_FLAG_OUTPUT_SPSPPS

提交输入帧进行编码

客户端应调用 NvEncEncodePicture 来执行编码。

输入图片数据将从指定的输入缓冲区获取,编码后的位流将在编码过程完成后在指定的位流(输出)缓冲区中可用。

编解码器无关的参数(例如时间戳、持续时间、输入缓冲区指针等)通过结构 NV_ENC_PIC_PARAMS 传递,而编解码器特定的参数通过结构 NV_ENC_PIC_PARAMS_H264/NV_ENC_PIC_PARAMS_HEVC/NV_ENC_PIC_PARAMS_AV1 传递,具体取决于所使用的编解码器。

客户端应使用 NV_ENC_PIC_PARAMS::codecPicParams 成员在 NV_ENC_PIC_PARAMS 中指定编解码器特定的结构。

如果客户端已使用 DirectX 12 设备初始化编码器会话,则客户端必须在 NV_ENC_PIC_PARAMS:: inputBuffer 中传递指向 NV_ENC_INPUT_RESOURCE_D3D12 的指针,其中包含注册的资源句柄和相应的输入 NV_ENC_FENCE_POINT_D3D12,供 NVENC 在开始编码之前等待。客户端必须在 NV_ENC_PIC_PARAMS::outputBuffer 中传递指向 NV_ENC_OUTPUT_RESOURCE_D3D12 的指针,其中包含注册的资源句柄和相应的输出 NV_ENC_FENCE_POINT_D3D12。NVENC 引擎等待 NV_ENC_INPUT_RESOURCE_D3D12::inputFencePoint 到达后再开始处理输入缓冲区。当资源处理完成时,NVENC 引擎会发出 NV_ENC_OUTPUT_RESOURCE_D3D12::outputFencePoint 信号,以便其他需要使用这些输入和输出资源的引擎可以开始处理。

检索编码后的输出

在完成输入图片的编码过程后,客户端需要调用 NvEncLockBitstream 以获取编码位流的 CPU 指针。客户端可以制作编码数据的本地副本,或传递 CPU 指针以进行进一步处理(例如,传递给媒体文件写入器)。

CPU 指针将保持有效,直到客户端调用 NvEncUnlockBitstream。客户端应在完成输出数据处理后调用 NvEncUnlockBitstream

如果客户端已使用 DirectX 12 设备初始化编码器会话,则客户端必须在 NV_ENC_LOCK_BITSTREAM::outputBitstream 中传递相同的 NV_ENC_OUTPUT_RESOURCE_D3D12 指针,以检索输出,该指针在编码期间已在 NV_ENC_PIC_PARAMS::outputBuffer 中发送。

客户端必须确保在销毁/释放所有位流缓冲区(例如,在关闭编码会话时),甚至在将它们重新用作后续帧的输出缓冲区之前,都已解锁所有位流缓冲区。

通知输入流结束

要通知输入流结束,客户端必须调用 NvEncEncodePicture,并将标志 NV_ENC_PIC_PARAMS:: encodePicFlags 设置为 NV_ENC_FLAGS_EOS,并将 NV_ENC_PIC_PARAMS 的所有其他成员设置为 0。为 EOS 通知调用 NvEncEncodePicture 时,不需要输入缓冲区。

EOS 通知有效地刷新了编码器。这可以在单个编码会话中多次调用。但是,此操作必须在关闭编码会话之前完成。

释放资源

编码完成后,客户端应销毁所有已分配的资源。

如果客户端已通过 NVIDIA 视频编码器接口分配了输入缓冲区,则应调用 NvEncDestroyInputBuffer。客户端必须确保首先通过调用 NvEncUnlockInputBuffer解锁输入缓冲区,然后再销毁它。

客户端应调用 NvEncDestroyBitstreamBuffer 以销毁其已分配的每个位流缓冲区。客户端必须确保首先通过调用 NvEncUnlockBitstream解锁位流缓冲区,然后再销毁它。

关闭编码会话

客户端应调用 NvEncDestroyEncoder 以关闭编码会话。客户端应确保在调用 NvEncDestroyEncoder 之前,已销毁所有与要关闭的编码会话绑定的资源。这些资源包括输入缓冲区、位流缓冲区、SPS/PPS 缓冲区等。

它还必须确保所有注册的事件都已注销,并且所有映射的输入缓冲区句柄都已取消映射。

NVIDIA 视频编码器接口支持以下两种操作模式。

异步模式

此操作模式用于异步输出缓冲区处理。对于此模式,客户端分配一个事件对象,并将该事件与分配的输出缓冲区关联。此事件对象作为 NvEncEncodePicture API 的一部分传递给 NVIDIA 编码器接口。客户端可以在单独的线程中等待事件。当事件发出信号时,客户端调用 NVIDIA 视频编码器接口以复制编码器生成的输出位流。请注意,编码器仅在 Windows 10 及更高版本上,且驱动程序在 WDDM 模式下运行时才支持异步操作模式。在 Linux 和具有 TCC 模式的 Windows 中(Tesla 板卡上提供 TCC 模式1),仅支持同步模式(请参阅 同步模式 部分)

客户端应将标志 NV_ENC_INITIALIZE_PARAMS::enableEncodeAsync 设置为 1,以指示其希望在异步模式下运行。在创建事件对象(为每个分配的输出位流缓冲区创建一个对象)后,客户端需要使用 NvEncRegisterAsyncEvent 向 NVIDIA 视频编码器接口注册它们。客户端需要将位流缓冲区句柄和相应的事件句柄作为输入传递给 NvEncEncodePicture。当硬件编码器完成当前输入数据的编码时,NVIDIA 视频编码器接口将发出此事件信号。然后,客户端可以调用 NvEncLockBitstream,在非阻塞模式下将 NV_ENC_LOCK_BITSTREAM::doNotWait 标志设置为 1,以获取输出数据。

客户端应在销毁事件对象之前调用 NvEncUnregisterAsyncEvent 以注销事件句柄。在可能的情况下,NVIDIA 建议使用异步操作模式而不是同步模式。

异步模式的逐步控制流程如下

  1. 在异步模式下工作时,输出样本必须包含一个事件 + 输出缓冲区,并且客户端必须以多线程方式工作(D3D9 设备应使用 MULTITHREADED 标志创建)。
  2. 输出缓冲区使用 NvEncCreateBitstreamBuffer API 分配。NVIDIA 视频编码器接口将在 NV_ENC_CREATE_BITSTREAM_BUFFER::bitstreambuffer 中返回指向输出内存的不透明指针。此不透明输出指针应在 NvEncEncodePictureNvEncLockBitsteam/ NvEncUnlockBitsteam 调用中使用。为了使用 CPU 访问输出内存,客户端必须调用 NvEncLockBitsteam API。IO 缓冲区的数量应至少为 4 + B 帧的数量。
  3. 事件是使用 Windows 的 CreateEvent API 分配的 Windows 事件句柄,并在编码前使用函数 NvEncRegisterAsyncEvent 注册。事件的注册在每个编码会话中只需要一次。客户端必须在使用 NvEncUnregisterAsyncEvent 销毁事件句柄之前注销事件。事件句柄的数量必须与输出缓冲区的数量相同,因为每个输出缓冲区都与一个事件关联。
  4. 客户端必须创建一个辅助线程,在该线程中,它可以等待完成事件并从输出样本复制位流数据。客户端将有两个线程:一个是主应用程序线程,它向 NVIDIA 编码器提交编码工作,而辅助线程等待完成事件并从输出缓冲区复制压缩的位流数据。
  5. 客户端必须在 NV_ENC_PIC_PARAMS::outputBitstreamNV_ENC_PIC_PARAMS:: completionEvent 字段中分别发送输出缓冲区和事件,作为 NvEncEncodePicture API 调用的一部分。
  6. 然后,客户端应在辅助线程中等待事件,其顺序与其调用 NvEncEncodePicture 调用的顺序相同,而与输入缓冲区重新排序无关(编码顺序 != 显示顺序)。当 enablePTD = 1 时,NVIDIA 编码器会处理 B 帧情况下的重新排序,这种方式对于编码器客户端是透明的。对于 AV1,NVIDIA 编码器还透明地执行帧位流打包,这意味着它始终将与前导的非显示帧对应的位流与随后出现的第一个显示帧的位流连接到单个输出缓冲区中。因此,每个输出缓冲区始终包含要显示的单个帧以及自上一个要显示的帧以来按编码顺序排列的所有先前非显示帧。
  7. 当事件发出信号时,客户端必须在 NV_ENC_LOCK_BITSTREAM::outputBitstream 字段中发送它正在等待的样本事件的输出缓冲区,作为 NvEncLockBitstream 调用的一部分。
  8. NVIDIA 编码器接口在 NV_ENC_LOCK_BITSTREAM 中返回 CPU 指针和位流大小(以字节为单位)。
  9. 复制位流数据后,客户端必须为锁定的输出位流缓冲区调用 NvEncUnlockBitstream

注意

  • 客户端将按照事件和输出缓冲区排队的顺序接收它们的信号。
  • NV_ENC_LOCK_BITSTREAM::pictureType 向客户端通知输出图片类型。
  • 一旦 NVIDIA 视频编码器接口发出事件信号,并且客户端已从输出缓冲区复制数据,输入和输出样本(输出缓冲区和输出完成事件)都可以自由重用。

同步模式

此操作模式用于同步输出缓冲区处理。在此模式下,客户端对 NVIDIA 视频编码器接口进行阻塞调用,以从编码器检索输出位流数据。客户端将标志 NV_ENC_INITIALIZE_PARAMS::enableEncodeAsync 设置为 0,以在同步模式下操作。然后,客户端必须调用 NvEncEncodePicture,而不设置完成事件句柄。客户端必须调用 NvEncLockBitstream,并将标志 NV_ENC_LOCK_BITSTREAM::doNotWait 设置为 0,以便锁定调用阻塞,直到硬件编码器完成写入输出位流。然后,客户端可以操作生成的位流数据并调用 NvEncUnlockBitstream。这是 Linux 上唯一支持的模式。

线程模型

为了获得最大的编码性能,编码器客户端应创建一个单独的线程来等待事件或在对编码器接口进行任何阻塞调用时使用。

客户端应避免从主编码器处理线程进行任何阻塞调用。主编码器线程应仅用于编码器初始化,并使用 NvEncEncodePicture API 向硬件编码器提交工作,该 API 是非阻塞的。

输出缓冲区处理,例如在异步模式下等待完成事件,或在同步模式下调用阻塞 API(例如 NvEncLockBitstream/NvEncUnlockBitstream),应在辅助线程中完成。这确保了主编码器线程永远不会被阻塞,除非编码器客户端耗尽资源。

还建议分配许多输入和输出缓冲区,以避免资源冲突并提高整体编码器吞吐量。

在 Windows 上,当编码设备类型为 DirectX 时,从主线程调用 DXGI API(例如 IDXGIOutputDuplication::AcquireNextFrame)以及从辅助线程调用 NvEncLockBitstream/NvEncUnlockBitstream 可能会导致次优或未定义的行为。这是因为 NvEncLockBitstream 可能会在内部使用应用程序的 DirectX 设备。

为了在这种应用程序中获得最佳性能,应使用以下编码器设置

  • NV_ENC_INITIALIZE_PARAMS::enableEncodeAsync = 1
  • NV_ENC_LOCK_BITSTREAM::doNotWait = 0
  • NV_ENC_INITIALIZE_PARAMS::enableOutputInVidmem = 0

使用 CUDA 的编码器功能

尽管 GPU 上的核心视频编码器硬件完全独立于 GPU 上的 CUDA 核心或图形引擎,但以下编码器功能在内部使用 CUDA 进行硬件加速。

注意

启用这些功能对整体 CUDA 或图形性能的影响很小,提供此列表纯粹是为了提供信息。

  • 用于高质量预设的双程速率控制模式
  • 前瞻
  • 所有自适应量化模式
  • 加权预测
  • RGB 内容的编码
  • 时间滤波

NVENC 可以用作硬件加速器,以执行运动搜索并生成运动矢量和模式信息。生成的运动矢量或模式决策可以用于例如运动补偿滤波,或用于支持 NVENC 未完全支持的其他编解码器,或者仅用作自定义编码器的运动矢量提示。下面解释了使用此功能的步骤。

对于涉及计算机视觉、AI 和帧插用例,Turing 及更高版本的 GPU 包含另一个硬件加速器,用于计算帧之间的光流矢量,与运动矢量相比,光流矢量提供更好的视觉匹配。

查询仅运动估计模式功能

在使用仅运动估计 (ME) 模式之前,客户端应显式查询编码器是否支持仅 ME 模式。为此,客户端应执行以下操作

  1. 将功能属性指定为 NV_ENC_CAPS_SUPPORT_MEONLY_MODE,以通过 NV_ENC_CAPS_PARAM::capsToQuery 参数进行查询。
  2. 客户端应调用 NvEncGetEncoderCaps 以确定是否支持所需属性。

NV_ENC_CAPS_SUPPORT_MEONLY_MODE 指示硬件中支持仅 ME 模式。

0:不支持仅 ME 模式。

1:支持仅 ME 模式。

如果使用 DirectX 12 设备,则不支持仅运动估计 (ME) 模式。

创建输入/输出数据资源

客户端应通过调用 NvEncCreateInputBuffer API 分配至少一个用于输入图片的缓冲区,还应通过使用 NvEncCreateInputBuffer API 分配一个用于参考帧的缓冲区。客户端负责填充有效输入数据。

创建输入资源后,客户端需要通过使用 NvEncCreateMVBuffer API 分配输出数据资源。

填充仅 ME 模式设置

结构 NV_ENC_CODEC_CONFIG::NV_ENC_CONFIG_H264_MEONLY 提供了控制 NVENC 硬件返回的运动矢量和模式的分区类型的能力。具体而言,客户端可以通过设置以下标志来禁用帧内模式和/或特定的 MV 分区大小

NV_ENC_CONFIG_H264_MEONLY::disableIntraSearchNV_ENC_CONFIG_H264_MEONLY::disablePartition16x16 NV_ENC_CONFIG_H264_MEONLY::disablePartition8x16 NV_ENC_CONFIG_H264_MEONLY::disablePartition16x8 NV_ENC_CONFIG_H264_MEONLY::disablePartition8x8

API 还公开了一个参数 NV_ENC_CONFIG::NV_ENC_MV_PRECISION,用于控制硬件返回的运动矢量的精度。对于全像素精度,客户端必须忽略运动矢量的两个 LSB。对于亚像素精度,运动矢量的两个 LSB 表示运动矢量的小数部分。要获取每个宏块的运动矢量,建议通过设置 NV_ENC_CONFIG_H264_MEONLY::disableIntraSearch = 1 来禁用帧内模式,并让 NVENC 决定运动矢量的最佳分区大小。

运行运动估计

客户端应创建 NV_ENC_MEONLY_PARAMS 的实例。

输入图片缓冲区和参考帧缓冲区的指针需要分别馈送到 NV_ENC_MEONLY_PARAMS::inputBufferNV_ENC_MEONLY_PARAMS::referenceFrame

NvEncCreateMVBuffer API 在 NV_ENC_CREATE_MV_BUFFER::mvBuffer 字段中返回的指针需要馈送到 NV_ENC_MEONLY_PARAMS::mvBuffer

为了在异步模式下运行,客户端应创建一个事件并在 NV_ENC_MEONLY_PARAMS::completionEvent 中传递此事件。运动估计完成后,将发出此事件信号。每个输出缓冲区应与不同的事件指针关联。

客户端应调用 NvEncRunMotionEstimationOnly 以在硬件编码器上运行运动估计。

对于异步模式,客户端应等待运动估计完成信号,然后再重用输出缓冲区和应用程序终止。

客户端必须使用 NvEncLockBitstream 锁定 NV_ENC_CREATE_MV_BUFFER::mvBuffer 以获取运动矢量数据。

最后,包含输出运动矢量的 NV_ENC_LOCK_BITSTREAM::bitstreamBufferPtr 应分别类型转换为 H.264/HEVC 的 NV_ENC_H264_MV_DATA*/NV_ENC_HEVC_MV_DATA*。然后,客户端应通过调用 NvEncUnlockBitstream 来解锁 NV_ENC_CREATE_MV_BUFFER::mvBuffer

为立体用例启用运动估计

对于需要处理两个视图的立体用例,我们建议以下方法以获得更好的性能和运动矢量质量

  • 客户端应创建单个编码会话。
  • 客户端应在单独的线程上启动左视图和右视图的处理。
  • 客户端应将 NV_ENC_MEONLY_PARAMS::viewID 设置为 0 和 1 以分别表示左视图和右视图。
  • 主线程应等待已启动 NVENC 以处理左视图和右视图的线程完成。

释放创建的资源

完成运动估计的使用后,客户端应调用 NvEncDestroyInputBuffer 以销毁输入图片缓冲区和参考帧缓冲区,并应调用 NvEncDestroyMVBuffer 以销毁运动矢量数据缓冲区。

查询最大支持的视频编解码器 SDK 版本

客户端应用程序可以使用 NvEncodeAPIGetMaxSupportedVersion 来检索底层 NVIDIA 显示驱动程序支持的最大视频编解码器 SDK 版本。

NvEncodeAPIGetMaxSupportedVersion 使客户端能够构建可在支持不同视频编解码器 SDK 版本的不同 NVIDIA 显示驱动程序版本之间工作的应用程序。

前瞻

前瞻通过使编码器能够缓冲指定数量的帧,估计其复杂性,并在这些帧之间按与其复杂性成正比的方式适当分配比特,从而提高了视频编码器的速率控制精度。这也动态地分配 B 帧和 P 帧。

要使用此功能,客户端必须遵循以下步骤

  1. 可以使用 NvEncGetEncodeCaps 查询当前硬件中该功能的可用性,并检查 NV_ENC_CAPS_SUPPORT_LOOKAHEAD。
  2. 需要在初始化期间通过设置 NV_ENC_INITIALIZE_PARAMS::encodeconfig->rcParams.enableLookahead = 1 来启用前瞻。
  3. 要前瞻的帧数应在 NV_ENC_INITIALIZE_PARAMS::encodeconfig->rcParams.lookaheadDepth 中设置,最多可以为 32。
  4. 默认情况下,前瞻启用帧内帧和 B 帧的自适应插入。但是,可以通过将 NV_ENC_INITIALIZE_PARAMS::encodeconfig->rcParams.disableIadapt 和/或 NV_ENC_INITIALIZE_PARAMS::encodeconfig->rcParams.disableBadapt 设置为 1 来禁用它们。
  5. 启用该功能后,帧将在编码器中排队,因此 NvEncEncodePicture 将返回 NV_ENC_ERR_NEED_MORE_INPUT,直到编码器有足够的输入帧来满足前瞻要求。应持续馈入帧,直到 NvEncEncodePicture 返回 NV_ENC_SUCCESS

B 帧作为参考

使用 B 帧作为参考可提高主观和客观编码质量,且对性能没有影响。因此,强烈建议启用多个 B 帧的用户启用此功能。

要使用此功能,请按照以下步骤操作

  • 使用 NvEncGetEncodeCaps API 查询该功能的可用性,并在返回值中检查 NV_ENC_CAPS_SUPPORT_BFRAME_REF_MODE
  • 在编码器初始化期间,设置 NV_ENC_CONFIG_H264/NV_ENC_CONFIG_HEVC/NV_ENC_CONFIG_AV1::useBFramesAsRef = NV_ENC_BFRAME_REF_MODE_MIDDLE
    • 对于 H.264 和 HEVC,这将把第 (N/2) 个 B 帧设置为参考,其中 N = B 帧的数量。如果 N 为奇数,则将选择第 (N-1)/2 个帧作为参考。
    • 对于 AV1,这将把每隔一个 B 帧设置为 Altref2 参考,但 Altref 间隔中的最后一个 B 帧除外。

重新配置 API

NvEncReconfigureEncoder 允许客户端更改 NV_ENC_INITIALIZE_PARAMS 中的编码器初始化参数,而无需关闭现有编码器会话并重新创建新的编码会话。这有助于客户端避免由于销毁和重新创建编码会话而引入的延迟。此 API 在视频会议、游戏流媒体等传输介质容易出现不稳定的场景中非常有用。

使用此 API,客户端可以使用相同的编码会话动态更改比特率、帧率、分辨率等参数。重新配置的参数通过 NV_ENC_RECONFIGURE_PARAMS::reInitEncodeParams. 传递。

但是,该 API 当前不支持重新配置所有参数,其中一些参数列在下面

  • 更改 GOP 结构 (NV_ENC_CONFIG_H264::idrPeriod,NV_ENC_CONFIG::gopLength, NV_ENC_CONFIG::frameIntervalP)
  • 从同步编码模式更改为异步模式,反之亦然。
  • 更改 NV_ENC_INITIALIZE_PARAMS::maxEncodeWidthNV_ENC_INITIALIZE_PARAMS::maxEncodeHeight。
  • 更改 NV_ENC_INITIALIZE_PARAMS::enablePTD 中的图片类型决策。
  • 更改位深度。
  • 更改色度格式。
  • 更改 NV_ENC_CONFIG_HEVC::maxCUSize
  • 更改 NV_ENC_CONFIG::frameFieldMode

如果尝试重新配置任何不支持的参数,API 将失败。

仅当在创建编码器会话时设置了 NV_ENC_INITIALIZE_PARAMS::maxEncodeWidthNV_ENC_INITIALIZE_PARAMS::maxEncodeHeight 时,才可能进行分辨率更改。

如果客户端希望使用此 API 更改分辨率,建议通过将 NV_ENC_RECONFIGURE_PARAMS::forceIDR 设置为 1 来强制将重新配置后的下一帧作为 IDR 帧。

如果客户端希望重置内部速率控制状态,请将 NV_ENC_RECONFIGURE_PARAMS::resetEncoder to 设置为 1。

自适应量化 (AQ)

此功能通过基于序列的空间和时间特性调整编码 QP(在速率控制算法评估的 QP 之上),从而提高视觉质量。 当前 SDK 支持两种 AQ 模式,如下所述

空间 AQ

空间 AQ 模式基于帧的空间特性调整 QP 值。 由于低复杂度的平坦区域比高复杂度的细节区域在视觉上更容易察觉到质量差异,因此会将额外的比特分配给帧的平坦区域,从而牺牲具有高空间细节的区域的比特。 虽然空间 AQ 提高了编码视频的可感知视觉质量,但所需的比特重新分配会导致大多数情况下 PSNR 下降。 因此,在基于 PSNR 的评估期间,应关闭此功能。

要在您的应用程序中使用空间 AQ,请按照以下步骤操作。

  • 空间 AQ 可以在初始化期间启用,方法是设置 NV_ENC_INITIALIZE_PARAMS::encodeconfig->rcParams. enableAQ = 1
  • QP 调整的强度可以通过设置 NV_ENC_INITIALIZE_PARAMS::encodeconfig->rcParams.aqStrength 来控制,范围从 1(最不激进)到 15(最激进)。 如果未设置,强度将由驱动程序自动选择。

时间 AQ

时间 AQ 尝试基于序列的时间特性调整编码 QP(在速率控制算法评估的 QP 之上)。 时间 AQ 通过调整帧中恒定或跨帧运动较小但具有高空间细节的区域的 QP 来提高编码帧的质量,从而使它们成为未来帧的更好参考。 与将额外的比特分配给被参考帧中的残差相比,将额外的比特分配给参考帧中的此类区域更好,因为它有助于提高整体编码视频质量。 如果帧内的大部分区域具有很少或没有运动,但具有高空间细节(例如,高细节的非移动背景),则启用时间 AQ 将会获得最大的好处。

时间 AQ 的潜在缺点之一是,启用时间 AQ 可能会导致 GOP 内每帧消耗的比特发生较大波动。 I/P 帧将比平均 P 帧大小消耗更多比特,而 B 帧将消耗更少比特。 虽然目标比特率将在 GOP 级别保持不变,但帧大小在一个 GOP 内从一帧到下一帧的波动将比不使用时间 AQ 时更大。 如果需要 GOP 内每个帧大小都采用严格的 CBR 配置文件,则不建议启用时间 AQ。 此外,由于某些复杂度估计是在 CUDA 中执行的,因此启用时间 AQ 可能会对性能产生一定的影响。

要在您的应用程序中使用时间 AQ,请按照以下步骤操作。

  1. 通过调用 API NvEncGetEncodeCaps 并检查 NV_ENC_CAPS_SUPPORT_TEMPORAL_AQ,查询当前硬件是否支持时间 AQ。
  2. 如果支持,可以在初始化期间启用时间 AQ,方法是设置 NV_ENC_INITIALIZE_PARAMS::encodeconfig->rcParams.enableTemporalAQ = 1。

时间 AQ 使用 CUDA 预处理,因此需要 CUDA 处理能力,具体取决于分辨率和内容。

启用时间 AQ 可能会导致编码器性能出现非常小的下降。

高比特深度编码

所有 NVIDIA GPU 都支持 8 位编码(具有 8 位精度的 RGB/YUV 输入)。 从 Pascal 架构开始,NVIDIA GPU 支持高比特深度 HEVC 编码(具有 10 位输入精度的 HEVC main-10 配置文件)。 从 Ada 架构开始,NVIDIA GPU 支持高比特深度 AV1 编码(具有 8 位或 10 位输入精度的 AV1 main 配置文件)。 从 Blackwell 架构开始,NVIDIA GPU 支持高比特深度 H264 编码(具有 10 位输入精度的 H264 high-10 配置文件)。 要编码 10 位内容,需要遵循以下步骤。

  1. 可以使用 NvEncGetEncodeCaps 查询此功能的可用性,并检查 NV_ENC_CAPS_SUPPORT_10BIT_ENCODE
  2. 为 H264 创建编码器会话时使用 NV_ENC_H264_PROFILE_HIGH_10_GUID,为 HEVC 创建编码器会话时使用 NV_ENC_HEVC_PROFILE_MAIN10_GUID,为 AV1 创建编码器会话时使用 NV_ENC_AV1_PROFILE_MAIN_GUID
  3. 在编码器初始化期间,
    • 对于 H264,对于 8 位输入内容,设置 encodeConfig->encodeCodecConfig.h264Config.outputBitDepth = NV_ENC_BIT_DEPTH_10encodeConfig->encodeCodecConfig.h264Config.inputBitDepth = NV_ENC_BIT_DEPTH_8,对于 10 位输入内容,设置 encodeConfig->encodeCodecConfig.h264Config.inputBitDepth = NV_ENC_BIT_DEPTH_10。 对于 8 位输入内容,NVENC 在编码之前执行内部 HW 8 位到 10 位输入转换。
    • 对于 HEVC,对于 8 位输入内容,设置 encodeConfig->encodeCodecConfig.hevcConfig.outputBitDepth = NV_ENC_BIT_DEPTH_10encodeConfig->encodeCodecConfig.hevcConfig.inputBitDepth = NV_ENC_BIT_DEPTH_8,对于 10 位输入内容,设置 encodeConfig->encodeCodecConfig.hevcConfig.inputBitDepth = NV_ENC_BIT_DEPTH_10。 对于 8 位输入内容,在 Blackwell 之前,NVENC 在编码之前执行内部 CUDA 8 位到 10 位输入转换。 在 Blackwell 架构 GPU 中,NVENC 在编码之前执行内部 HW 8 位到 10 位输入转换。
    • 对于 AV1,对于 8 位输入内容,设置 encodeConfig->encodeCodecConfig.av1Config.outputBitDepth = NV_ENC_BIT_DEPTH_10encodeConfig->encodeCodecConfig.av1Config.inputBitDepth = NV_ENC_BIT_DEPTH_8,对于 10 位输入内容,设置 encodeConfig->encodeCodecConfig.av1Config.inputBitDepth = NV_ENC_BIT_DEPTH_10。 对于 8 位输入内容,NVENC 在编码之前执行内部 HW 8 位到 10 位输入转换。
  4. 其他编码参数(如预设、速率控制模式等)可以根据需要设置。

加权预测

加权预测涉及运动补偿预测的乘法加权因子和加法偏移量的计算。 对于具有光照变化的內容,加权预测可提供显著的质量增益。 从 Pascal 架构 GPU 开始,NVENCODE API 支持 HEVC 和 H.264 的加权预测。

启用加权预测需要遵循以下步骤。

  1. 可以使用 NvEncGetEncodeCaps 查询此功能的可用性,并检查 NV_ENC_CAPS_SUPPORT_WEIGHTED_PREDICTION
  2. 在编码器初始化期间,设置 NV_ENC_INITIALIZE_PARAMS:: enableWeightedPrediction = 1

如果编码会话配置了 B 帧,则不支持加权预测。

如果使用 DirectX 12 设备,则不支持加权预测。

加权预测使用 CUDA 预处理,因此需要 CUDA 处理能力,具体取决于分辨率和内容。

启用加权预测也可能会导致编码器性能出现非常小的下降。

H.264、HEVC 和 AV1 中的长期参考帧

NVENCODE API 提供了将特定帧标记为长期参考 (LTR) 帧并使用的功能,这些帧稍后可以用作编码当前图片的参考。 这有助于错误隐藏,在这种情况下,如果中间帧丢失数据,客户端解码器可以从长期参考帧进行预测。 此功能在视频流应用程序中很有用,可用于从接收端的帧丢失中恢复。

要启用此功能,请遵循以下步骤。

  1. 在编码器初始化期间,
    • 对于 H.264,设置 NV_ENC_CONFIG_H264:enableLTR = 1
    • 对于 HEVC,设置 NV_ENC_CONFIG_HEVC:enableLTR = 1
    • 对于 AV1,设置 NV_ENC_CONFIG_AV1:enableLTR = 1
  2. 当前硬件中支持的最大长期参考图片数量可以使用 NvEncGetEncoderCaps 查询,并检查 NV_ENC_CAPS_NUM_MAX_LTR_FRAMES

在正常编码操作期间,需要遵循以下步骤来将特定帧标记为 LTR 帧。

  1. 配置 LTR 帧的数量
    • 对于 H.264,设置 NV_ENC_CONFIG_H264:ltrNumFrames
    • 对于 HEVC,设置 NV_ENC_CONFIG_HEVC:ltrNumFrames
    • 对于 AV1,设置 NV_ENC_CONFIG_AV1:ltrNumFrames
  2. 客户端可以通过为 H.264、HEVC 和 AV1 分别设置 NV_ENC_PIC_PARAMS_H264::ltrMarkFrame = 1 OR NV_ENC_PIC_PARAMS_HEVC::ltrMarkFrame = 1 OR NV_ENC_PIC_PARAMS_AV1::ltrMarkFrame = 1,将任何帧标记为 LTR。 每个 LTR 帧都需要分配一个 LTR 帧索引。 此值应介于 0 和 ltrNumFrames- 1 之间。
  3. LTR 帧索引可以通过为 H264、HEVC 和 AV1 分别设置 NV_ENC_PIC_PARAMS_H264::ltrMarkFrameIdx OR NV_ENC_PIC_PARAMS_HEVC::ltrMarkFrameIdx OR NV_ENC_PIC_PARAMS_AV1::ltrMarkFrameIdx 来分配。

先前标记为长期参考帧的帧可以用于以下方式预测当前帧

  1. 要用作参考的 LTR 帧必须使用 H.264、HEVC 和 AV1 的 NV_ENC_PIC_PARAMS_H264::ltrUseFrameBitmap OR NV_ENC_PIC_PARAMS_HEVC::ltrUseFrameBitmap OR NV_ENC_PIC_PARAMS_AV1::ltrUseFrameBitmap 来指定。 位位置指定将用作参考的帧的 LTR 帧索引。

当编码会话配置了 B 帧时,当前 SDK 不支持 LTR。

强调 MAP

NVENCODE API 中的强调映射功能提供了一种指定帧中要以不同质量级别编码的区域的方法,精度达到宏块级别。 根据每个宏块的实际强调级别,编码器会对用于编码该宏块的量化参数应用调整。 此调整的值取决于以下因素:

  • 由速率控制算法决定的 QP 的绝对值,具体取决于速率控制约束。 通常,对于给定的强调级别,由速率控制确定的 QP 越高,(负)调整越高。
  • 宏块的强调级别值。
注意

QP 调整在速率控制算法运行后执行。 因此,使用此功能时可能会发生 VBV/速率违规。

当客户端预先了解图像复杂度(例如,NVFBC 的分类映射功能)并且以更高质量(更低的 QP)编码这些高复杂度区域很重要时,即使可能以违反比特率/VBV 缓冲区大小约束为代价,强调级别映射也很有用。 当启用 AQ(空间/时间)时,不支持此功能。

请按照以下步骤启用此功能。

  1. 使用 NvEncGetEncodeCaps API 查询此功能的可用性,并检查 NV_ENC_CAPS_SUPPORT_EMPHASIS_LEVEL_MAP.
  2. 设置 NV_ENC_RC_PARAMS::qpMapMode = NV_ENC_QP_MAP_EMPHASIS.
  3. 使用 enum NV_ENC_EMPHASIS_MAP_LEVEL 中的值填充 NV_ENC_PIC_PARAMS::qpDeltaMap (,这是一个有符号字节数组,包含当前图片中每个宏块的光栅扫描顺序值)。

如上所述,NV_ENC_EMPHASIS_MAP_LEVEL 的值越高,对 QP 进行的(负)调整越高,以强调该宏块的质量。 用户可以为想要以更高质量编码的区域选择更高的强调级别。

视频内存中的 NVENC 输出

从 SDK 9.0 开始,NVENCODE API 支持视频内存中的比特流和 H.264 仅 ME 模式输出。 这对于使用 CUDA 或 DirectX 着色器执行 NVENC 输出操作的用例很有帮助。 将 NVENC 的输出留在视频内存中可以避免不必要的缓冲区 PCIe 传输。 视频内存应由客户端应用程序分配,作为一维缓冲区。 此功能目前支持 H.264、HEVC 和 AV1 编码以及 H.264 仅 ME 模式。 此功能支持 DirectX 11 和 CUDA 接口。

要使输出在视频内存中可用,请按照以下步骤操作。

  1. 调用 nvEncInitializeEncoder() 时,设置 NV_ENC_INITIALIZE_PARAMS::enableOutputInVidmem = 1
  2. 在视频内存中分配一维缓冲区,供 NVENC 写入输出。
    • 对于 AV1、HEVC 或 H.264 编码,此缓冲区的建议大小为

      输出缓冲区大小 = 2 * 输入 YUV 缓冲区大小 + sizeof(NV_ENC_ENCODE_OUT_PARAMS)

      输出缓冲区的前 sizeof(NV_ENC_ENCODE_OUT_PARAMS) 字节包含 NV_ENC_ENCODE_OUT_PARAMS 结构,后跟编码比特流数据。

    • 对于 H.264 仅 ME 输出,输出缓冲区的建议大小为

      输出缓冲区大小 = HeightInMbs * WidthInMbs * sizeof(NV_ENC_H264_MV_DATA)

      其中 HeightInMbsWidthInMbs 分别是 16x16 宏块数量的图片高度和宽度。

    • 对于 DirectX 11 接口,可以使用 DirectX 11 CreateBuffer() API 创建此缓冲区,方法是指定 usage = D3D11_USAGE_DEFAULTBindFlags = (D3D11_BIND_VIDEO_ENCODER | D3D11_BIND_SHADER_RESOURCE);和CPUAccessFlags = 0
    • 对于 CUDA 接口,可以使用 cuMemAlloc() 创建此缓冲区。
  3. 使用 nvEncRegisterResource() 注册此缓冲区,方法是指定
    • NV_ENC_REGISTER_RESOURCE::bufferUsage =NV_ENC_OUTPUT_BITSTREAM,如果输出是编码比特流,
    • 以及 NV_ENC_REGISTER_RESOURCE::bufferUsage= NV_ENC_OUTPUT_MOTION_VECTOR,如果输出是 H.264 仅 ME 模式下的运动矢量。
    • 设置 NV_ENC_REGISTER_RESOURCE::bufferFormat= NV_ENC_BUFFER_FORMAT_U8NvEncRegisterResource() 将在 NV_ENC_REGISTER_RESOURCE::registeredResource 中返回已注册的句柄。
  4. 设置NV_ENC_MAP_INPUT_RESOURCE::registeredResource = NV_ENC_REGISTER_RESOURCE::registeredResource,这是在上一步中获得的。
  5. 调用 nvEncMapInputResource(),它将在 NV_ENC_MAP_INPUT_RESOURCE::mappedResource 中返回映射的资源句柄。
  6. 对于 AV1/HEVC/H.264 编码模式,通过将 NV_ENC_PIC_PARAMS::outputBitstream 设置为 NV_ENC_MAP_INPUT_RESOURCE:: mappedResource,调用 nvEncEncodePicture()
  7. 对于 H.264 仅 ME 模式,通过将 NV_ENC_MEONLY_PARAMS::mvBuffer 设置为 NV_ENC_MAP_INPUT_RESOURCE::mappedResource,调用 nvEncRunMotionEstimationOnly()。

读取输出缓冲区时,请注意以下事项

在调用 nvEncEncodePicture()nvEncRunMotionEstimationOnly() 后,客户端只有在取消映射此输出缓冲区后才能使用输出缓冲区进行进一步处理。 不应调用 NvEncLockBitstream()

在异步模式下运行时,客户端应用程序应等待事件后再读取输出。 在同步模式下,不会触发任何事件,同步由 NVIDIA 驱动程序在内部处理。

要访问输出,请按照以下步骤操作

  1. 客户端必须通过使用 nvEncMapInputResource() 返回的映射资源句柄 NV_ENC_MAP_INPUT_RESOURCE::mappedResource 调用 nvEncUnmapInputResource() 来取消映射输入缓冲区。 在此之后,输出缓冲区可以用于进一步处理/读取等。
  2. 在编码的情况下,此缓冲区的前 sizeof(NV_ENC_ENCODE_OUT_PARAMS) 字节应解释为 NV_ENC_ENCODE_OUT_PARAMS 结构,后跟编码比特流数据。编码比特流的大小由 NV_ENC_ENCODE_OUT_PARAMS::bitstreamSizeInBytes 给出。
  3. 如果指定了 CUDA 模式,则对此缓冲区的所有 CUDA 操作都必须使用默认流。 要在系统内存中获取输出,可以通过调用任何 CUDA API(例如 cuMemcpyDtoH())和默认流来读取输出缓冲区。 驱动程序确保仅在 NVENC 完成将输出写入其中后才读取输出缓冲区。
  4. 对于 DX11 模式,可以使用任何 DirectX 11 API 来读取输出。 驱动程序确保仅在 NVENC 完成将输出写入其中后才读取输出缓冲区。 要在系统内存中获取输出,可以使用 CopyResource()(这是一个 DirectX 11 API)将数据复制到 CPU 可读的暂存缓冲区中。 然后可以在调用 Map()(这是一个 DirectX 11 API)后读取此暂存缓冲区。

HEVC 中的 Alpha 图层编码支持

NVENCODE API 实现了对 HEVC 中 Alpha 图层编码的支持。 此功能允许应用程序编码包含 YUV 数据的基本层和包含 Alpha 通道数据的辅助层。

要启用此功能,请遵循以下步骤

  1. 可以使用 nvEncGetEncodeCaps() 查询此功能的可用性,并检查 NV_ENC_CAPS_SUPPORT_ALPHA_LAYER_ENCODING。 请注意,Alpha 图层编码仅支持 NV_ENC_BUFFER_FORMAT_NV12, NV_ENC_BUFFER_FORMAT_ARGBNV_ENC_BUFFER_FORMAT_ABGR 输入格式。
  2. 在编码器初始化期间,设置 NV_ENC_CONFIG_HEVC:: enableAlphaLayerEncoding = 1。 客户端还可以通过设置 NV_ENC_RC_PARAMS::alphaLayerBitrateRatio 来指定要在 YUV 和辅助 Alpha 图层之间分配比特率的比率。 例如,如果 NV_ENC_RC_PARAMS::alphaLayerBitrateRatio = 3,则 75% 的比特将用于基本层编码,而另外 25% 将用于 Alpha 图层。

在正常编码操作期间,需要遵循以下步骤进行 Alpha 图层编码

  1. nvEncEncodePicture() : 中传递 Alpha 输入:
    • 对于输入格式 NV_ENC_BUFFER_FORMAT_NV12,YUV 数据应在 NV_ENC_PIC_PARAMS::inputBuffer 中传递,而 Alpha 输入数据需要使用 NV_ENC_PIC_PARAMS::alphaBuffer 单独传递。NV_ENC_PIC_PARAMS::alphaBuffer 的格式应为 NV_ENC_BUFFER_FORMAT_NV12。 亮度平面应包含 Alpha 数据,而色度分量应 memset 为 0x80。
    • 对于输入格式 NV_ENC_BUFFER_FORMAT_ABGRNV_ENC_BUFFER_FORMAT_ARGB,输入数据应在 NV_ENC_PIC_PARAMS::inputBuffer 中传递。 在这种情况下,字段 NV_ENC_PIC_PARAMS::alphaBuffer 应设置为 NULL。
  2. YUV 和 Alpha 图层的编码输出都使用对 NvEncLockBitstream 的调用来获取。 字段 NV_ENC_LOCK_BITSTREAM::bitstreamSizeInBytes 将包含总编码大小,即 YUV 图层比特流数据、Alpha 比特流数据和任何其他标头数据的大小。 客户端可以使用字段 NV_ENC_LOCK_BITSTREAM::alphaLayerSizeInBytes 单独获取 Alpha 图层的大小。

在以下情况下不支持 Alpha 编码

  1. 当启用子帧模式时。
  2. 输入图像为 YUV 422 或 444。
  3. 输入图像的比特深度为 10 位。
  4. 比特流输出指定为在视频内存中。
  5. 启用加权预测。

H.264、HEVC 和 AV1 中的时间可分级视频编码 (SVC)

NVENCODE API 支持时间可分级视频编码 (SVC),如 H.264/AVC 视频压缩标准的附录 G 中所指定。 时间 SVC 会产生具有基本层和多个辅助层的分层结构。

要使用时间 SVC,请按照以下步骤操作

  1. 通过调用 API NvEncGetEncodeCaps 并检查 NV_ENC_CAPS_SUPPORT_TEMPORAL_SVC,查询当前硬件是否支持时间 SVC。
  2. 如果支持,请使用 nvEncGetEncodeCaps() 查询 SVC 中支持的最大时间层数,并检查 NV_ENC_CAPS_NUM_MAX_TEMPORAL_LAYERS 的值
  3. 在编码器初始化期间,设置 NV_ENC_CONFIG_H264::enableTemporalSVC = 1NV_ENC_CONFIG_HEVC::enableTemporalSVC = 1NV_ENC_CONFIG_AV1::enableTemporalSVC = 1。 分别使用 NV_ENC_CONFIG_H264::numTemporalLayersNV_ENC_CONFIG_HEVC::numTemporalLayersNV_ENC_CONFIG_AV1::numTemporalLayersNV_ENC_CONFIG_H264::maxTemporalLayersNV_ENC_CONFIG_HEVC::maxTemporalLayersMinus1NV_ENC_CONFIG_AV1::maxTemporalLayersMinus1 指定时间层数和最大时间层数。
  4. 如果最大时间层数大于 2,则帧重排序的最小 DPB 大小需要为 (maxTemporalLayers - 2) * 2。 因此,将 NV_ENC_CONFIG_H264::maxNumRefFramesNV_ENC_CONFIG_HEVC::maxNumRefFramesInDPBNV_ENC_CONFIG_AV1::maxNumRefFramesInDPB 设置为大于或等于此值。 请注意,NV_ENC_CONFIG_H264::maxNumRefFramesNV_ENC_CONFIG_HEVC::maxNumRefFramesInDPBNV_ENC_CONFIG_AV1::maxNumRefFramesInDPB 的默认值为 NV_ENC_CAPS::NV_ENC_CAPS_NUM_MAX_TEMPORAL_LAYERS
  5. 默认情况下,启用时间 SVC 时会添加 SVC 前缀 NALU。 要禁用此功能,请设置 NV_ENC_CONFIG_H264::disableSVCPrefixNalu = 0
  6. NVENCODE API 支持在比特流中添加可分级信息 SEI 消息。 要启用此 SEI,请设置 NV_ENC_CONFIG_H264::enableScalabilityInfoSEI = 1。 此 SEI 将添加到编码比特流中的每个 IDR 帧。 请注意,此 SEI 中当前仅支持与时间可分级性相关的字段子集。

启用时间 SVC 后,只有基本层帧可以标记为长期参考帧。

当前 B 帧不支持时间 SVC。 启用时间 SVC 后,将忽略字段 NV_ENC_CONFIG::frameIntervalP

错误恢复功能

在涉及视频流的典型场景中,客户端解码器通常会出现比特错误。 为了最大限度地减少这些错误的影响并从这些错误中恢复,NVENCODE API 提供了一些错误恢复功能,本节将对此进行说明。

参考图片失效

NVENCODE API 提供了一种机制,用于在客户端解码器发现客户端解码的图片已损坏时,使某些图片失效。 这种失效是通过使用 API NvEncInvalidateRefFrames 来实现的。 客户端可以通过请求流媒体服务器上的编码器使此帧失效来防止进一步损坏,这将阻止所有后续帧使用当前帧作为运动估计的参考帧。 然后,服务器根据可用于参考的旧的短期和长期帧进行参考。 如果没有帧可用作参考,则当前帧将编码为帧内帧。 参数 NV_ENC_CONFIG_H264::maxNumRefFramesNV_ENC_CONFIG_HEVC::maxNumRefFramesInDPBNV_ENC_CONFIG_AV1::maxNumRefFramesInDPB 确定 DPB 中的帧数,将其设置为较大的值将允许较旧的帧在 DPB 中可用,即使某些帧已失效,并且与在没有参考帧的情况下将编码的帧内帧相比,可以获得更好的图片质量。

要通过 API NvEncInvalidateRefFrames 使其失效的特定帧使用每个帧的唯一编号进行标识,该编号称为时间戳。 这是通过字段 NV_ENC_PIC_PARAMS::inputTimeStamp 编码图片时发送到编码器的时间戳。 这可以是任何单调递增的唯一编号。 在其最常见的形式中,唯一编号可以是图片的呈现时间戳。 编码器使用 inputTimeStamp 作为唯一标识符将其 DPB 中的帧存储起来,并在通过 API NvEncInvalidateRefFrames 请求时使用该标识符使相应的帧失效。

帧内刷新

章节 参考图片失效 中描述的参考图片失效技术取决于带外上行通道的可用性,以报告解码器(客户端)端的比特流错误。 当此类上行通道不可用时,或者在比特流更可能遭受更频繁错误的情况下,帧内刷新机制可以用作错误恢复机制。 此外,当使用无限 GOP 长度时,不会传输帧内帧,帧内刷新可能是从传输错误中恢复的有用机制。

NVENCODE API 提供了一种实现帧内刷新的机制。 应将 enableIntraRefresh 标志设置为 1 以启用帧内刷新。intraRefreshPeriod 确定帧内刷新再次发生的时间周期,intraRefreshCnt 设置帧内刷新将发生的帧数。

帧内刷新会导致在 intraRefreshCnt 个连续帧中,使用帧内宏块编码帧的连续部分。 然后,在从第一个帧内刷新帧开始的 intraRefreshPeriod 帧之后,整个周期重复。 根据传输期间可能发生的错误概率,适当设置 intraRefreshPeriodintraRefreshCnt 至关重要。 例如,对于容易出错的网络,intraRefreshPeriod 可能很小,例如 30,从而对于 30 FPS 视频流每秒启用恢复。 对于错误发生几率较低的网络,该值可以设置得更高。intraRefreshPeriod 的值越低,质量会略有下降,因为在帧内刷新周期中,强制为帧内编码的宏块总数比例更大,但可以更快地从网络错误中恢复。

intraRefreshCnt 确定在一个帧内刷新周期内,帧内刷新将发生的帧数。intraRefreshCnt 的值越小,将更快地刷新整个帧(而不是以条带方式缓慢刷新),因此可以更快地进行错误恢复。 但是,较低的 intraRefreshCnt 也意味着每帧发送的帧内宏块数量更多,因此质量会略有下降。

对于 H.264/HEVC,默认的 NVENCODE API 帧内刷新行为是基于切片的,对于 AV1,默认的 NVENCODE API 帧内刷新行为是基于瓦片的,即帧内刷新波中的帧将具有多个切片/瓦片,其中一个切片/瓦片仅包含帧内编码的 MB/CTU/SB。

  • 对于 AV1,在帧内刷新波期间使用的瓦片数量由驱动程序根据 intraRefreshCntintraRefreshPeriod 的值自动确定。 应用程序指定的任何自定义瓦片配置将在帧内刷新波的持续时间内被忽略。
  • 如果应用程序未明确指定切片数量,或者如果指定的切片数量少于 3 个,则在帧内刷新波期间,驱动程序将为每帧设置 3 个切片。
  • 对于 NV_ENC_CONFIG_H264::sliceMode = 0(基于 MB 的切片)、2(基于 MB 行的切片)和 3(切片数量),驱动程序将在帧内刷新周期期间保持切片计数,该计数等于根据切片模式设置计算的切片计数和 intraRefreshCnt 个切片中的最小值。
  • 对于 NV_ENC_CONFIG_H264::sliceMode = 1(基于字节的切片),帧内刷新波期间的切片数量始终为 3。

对于某些用例,客户端可能希望避免帧中存在多个切片。 在这种情况下,客户端可以启用单一切片帧内刷新。

  • 通过调用 API NvEncGetEncodeCaps 并检查 NV_ENC_CAPS_SINGLE_SLICE_INTRA_REFRESH,查询当前驱动程序对单一切片帧内刷新的支持。
  • 如果支持,可以通过设置 NV_ENC_CONFIG_H264::singleSliceIntraRefresh / NV_ENC_CONFIG_HEVC::singleSliceIntraRefresh 来启用单一切片帧内刷新。

如果在帧内刷新波期间发生分辨率重新配置,则正在进行的波将立即终止。 下一个波将在 NV_ENC_CONFIG_H264::intraRefreshPeriod 个帧数后开始。

帧内刷新以编码顺序应用,并且仅应用于可以用作参考的帧。

HEVC 和 AV1 中的多 NVENC 拆分帧编码

启用分帧编码后,每个输入帧被分割成水平条带,这些条带由独立的 NVENC 并行且同时编码,与单 NVENC 编码相比,通常可以提高编码速度。

请注意以下事项

  1. 虽然此功能提高了编码速度,但会降低质量。
  2. 总体编码吞吐量(当所有 NVENC 都被充分利用时,在一定时间间隔内编码的总帧数)将保持不变。
  3. 此功能仅适用于 HEVC 和 AV1。

因此,此功能应用于在单个编码会话中实现更高的编码速度,这在单个 NVENC 上是不可能实现的,因为特定输入流的水平条带是在多个 NVENC 上同时编码的。如上所述,当创建多个编码会话以充分利用所有 NVENC 时,此功能不会影响总体吞吐量。如果 NVENC 的数量少于请求的分帧模式,则水平条带的数量将被强制设置为 NVENC 的数量。

以下列出了启用该功能的两种模式。

1. 隐式模式:自动触发此功能的条件是

  • GPU 上的 NVENC 数量:2 个或更多。
  • 帧高度:HEVC 必须为 2112 像素或更多,AV1 必须为 2048 像素或更多。
  • 预设和调优信息配置:表 2 总结了启用分帧编码的预设和调优信息组合
调优信息预设
P1P2P3P4P5P6P7
高质量
低延迟
超低延迟

2. 显式模式

以下模式支持 HEVC 和 AV1 的分帧编码

  • NV_ENC_SPLIT_ENCODE_MODE::NV_ENC_SPLIT_AUTO_MODE(默认):隐式模式,如上所述。
  • NV_ENC_SPLIT_ENCODE_MODE::NV_ENC_SPLIT_AUTO_FORCED_MODE:分帧编码将针对所有配置启用,水平条带的数量由驱动程序自动选择,以实现平衡的性能和视觉质量。
  • NV_ENC_SPLIT_ENCODE_MODE::NV_ENC_SPLIT_TWO_FORCED_MODE:分帧编码将针对所有配置启用,当 NVENC 的数量 > 1 时,水平条带的数量强制为 2。
  • NV_ENC_SPLIT_ENCODE_MODE::NV_ENC_SPLIT_THREE_FORCED_MODE:分帧编码将针对所有配置启用,当 NVENC 的数量 > 2 时,水平条带的数量强制为 3,否则为 NVENC 条带数。
  • NV_ENC_SPLIT_ENCODE_MODE::NV_ENC_SPLIT_FOUR_FORCED_MODE:分帧编码将针对所有配置启用,当 NVENC 的数量 > 3 时,水平条带的数量强制为 4,否则为 NVENC 条带数。
  • NV_ENC_SPLIT_ENCODE_MODE::NV_ENC_SPLIT_DISABLE:分帧编码将针对所有配置禁用。

请注意,一些编码功能与分帧编码的使用不兼容。当使用以下任何功能时,分帧编码始终被禁用

  1. 加权预测 (HEVC)。
  2. Alpha 图层编码 (HEVC)。
  3. 码流子帧回读模式 (HEVC)
  4. 视频内存中的码流输出 (HEVC/AV1)

NVENC 重建帧输出

从 SDK 12.1 开始,NVENCODE API 支持 Turing 及更高版本的 GPU 的 H.264、HEVC 和 AV1 编码的重建帧输出。这对于需要重建帧输出以访问编码质量的用例非常有用,因此无需解码码流,从而全面提高性能。重建帧缓冲区应由客户端应用程序分配为二维缓冲区。当前硬件中此功能的可用性也可以使用 NvEncGetEncodeCaps() 查询,并检查 NV_ENC_CAPS_OUTPUT_RECON_SURFACE。支持的缓冲区格式为:NV_ENC_BUFFER_FORMAT_NV12NV_ENC_BUFFER_FORMAT_YUV420_10BIT。对于 CUDA 接口,还支持 NV_ENC_BUFFER_FORMAT_NV16NV_ENC_BUFFER_FORMAT_P210V_ENC_BUFFER_FORMAT_YUV444NV_ENC_BUFFER_FORMAT_YUV444_10BIT

请按照以下步骤进行重建帧输出

  1. 调用 nvEncInitializeEncoder() 时,设置 NV_ENC_INITIALIZE_PARAMS::enableReconFrameOutput = 1
  2. 分配二维缓冲区,供 NVENC 写入重建帧输出。
    • 对于 CUDA 接口,可以使用 cuMemAllocPitch(). 创建此缓冲区。
    • 对于 DirectX 9 接口,可以使用 CreateOffscreenPlainSurface() 或 CreateSurface() API 创建此缓冲区。
    • 对于 DirectX 11 接口,可以使用 DirectX 11 CreateTexture2D() API 创建此缓冲区,通过指定 usage = D3D11_USAGE_DEFAULTBindFlags = (D3D11_BIND_SHADER_RESOURCE);以及 CPUAccessFlags = 0
  3. 使用 nvEncRegisterResource() 注册此缓冲区,方法是指定
    • NV_ENC_REGISTER_RESOURCE::bufferUsage =NV_ENC_OUTPUT_RECON
    • NV_ENC_REGISTER_RESOURCE::bufferFormat 设置为所需的值。NvEncRegisterResource() 将在 NV_ENC_REGISTER_RESOURCE::registeredResource 中返回注册句柄。
  4. NV_ENC_MAP_INPUT_RESOURCE::registeredResource 设置为 NV_ENC_REGISTER_RESOURCE::registeredResource,这是在上一步中获得的。
  5. 调用 nvEncMapInputResource()。它将在 NV_ENC_MAP_INPUT_RESOURCE::mappedResource 中返回映射的资源句柄。
  6. 通过将 NV_ENC_PIC_PARAMS::outputReconBuffer 设置为 NV_ENC_MAP_INPUT_RESOURCE:: mappedResource,并将 NV_ENC_PIC_PARAMS::encodePicFlags 设置为 NV_ENC_PIC_FLAG_OUTPUT_RECON_FRAME,来调用 nvEncEncodePicture()

读取重建输出时,请注意以下事项

在调用 nvEncEncodePicture() 后,客户端只有在取消映射此输出缓冲区后才能使用输出缓冲区进行进一步处理。

在异步模式下运行时,客户端应用程序应等待事件后再读取输出。 在同步模式下,不会触发任何事件,同步由 NVIDIA 驱动程序在内部处理。

要访问重建帧输出,请按照以下步骤操作

  1. 客户端必须通过使用映射的资源句柄 NV_ENC_MAP_INPUT_RESOURCE::mappedResource 调用 nvEncUnmapInputResource() 来取消映射重建帧缓冲区,该句柄由 nvEncMapInputResource() 返回。在此之后,输出重建缓冲区可以用于进一步处理/读取等。
  2. 如果指定了 CUDA 模式,要获取系统内存中的输出,可以通过调用任何 CUDA API(例如 cuMemcpyDtoH())来读取重建输出缓冲区。驱动程序确保仅在 NVENC 完成将输出写入缓冲区后才读取输出缓冲区。
  3. 对于 DX11 模式,可以使用任何 DirectX 11 API 来读取输出。 驱动程序确保仅在 NVENC 完成将输出写入其中后才读取输出缓冲区。 要在系统内存中获取输出,可以使用 CopyResource()(这是一个 DirectX 11 API)将数据复制到 CPU 可读的暂存缓冲区中。 然后可以在调用 Map()(这是一个 DirectX 11 API)后读取此暂存缓冲区。

编码帧统计信息

从 SDK 12.1 开始,NVENCODE API 支持 Turing 及更高版本的 GPU 的 H.264、HEVC 和 AV1 编码的编码帧统计信息输出。当前硬件中此功能的可用性可以使用 NvEncGetEncodeCaps() 查询,并检查 NV_ENC_CAPS_OUTPUT_ROW_STATSNV_ENC_CAPS_OUTPUT_BLOCK_STATS。这对于需要编码帧统计信息(QP 和比特计数,在行或块级别)的用例非常有用。

请按照以下步骤获取编码帧输出统计信息

  1. 调用 nvEncInitializeEncoder() 时,设置 NV_ENC_INITIALIZE_PARAMS::enableOutputStats = 1NV_ENC_INITIALIZE_PARAMS::outputStatsLevelNV_ENC_OUTPUT_STATS_ROW_LEVELNV_ENC_OUTPUT_STATS_BLOCK_LEVELNV_ENC_OUTPUT_STATS_ROW_LEVEL 支持 Turing 和 Ampere GPU。NV_ENC_OUTPUT_STATS_BLOCK_LEVEL 支持 ADA 及更高架构。
  2. 设置以下参数以获取每行的编码帧统计信息
    1. NV_ENC_LOCK_BITSTREAM:: outputStatsPtrSize=sizeof(NV_ENC_OUTPUT_STATS_ROW) x 行数.
    2. H.264 的行数 = (PicHeight + 15 ) >> 4
    3. HEVC 的行数 = (PicHeight + 31 ) >> 5
  3. 设置以下参数以获取每个块的编码帧统计信息
    1. NV_ENC_LOCK_BITSTREAM:: outputStatsPtrSize=sizeof(NV_ENC_OUTPUT_STATS_BLOCK) x 块数.
    2. H.264 的块数 = (PicWidth + 15 ) >> 4 * (PicHeight + 15 ) >> 4
    3. HEVC 的块数 = (PicWidth + 31 ) >> 5 * (PicHeight + 31 ) >> 5
    4. AV1 的块数 = (PicWidth + 63 ) >> 6 * (PicHeight + 63 ) >> 6
  4. 分配大小为 NV_ENC_LOCK_BITSTREAM::outputStatsPtrSize 的系统内存缓冲区,并将其分配给 NV_ENC_LOCK_BITSTREAM::outputStatsPtr,然后调用 NvEncLockBitstream() API。
  5. NV_ENC_OUTPUT_STATS_BLOCKNV_ENC_OUTPUT_STATS_ROW 格式从 NV_ENC_LOCK_BITSTREAM::outputStatsPtr 读取编码帧统计信息。
  6. 调用 NvEncUnlockBitstream() API。

迭代编码

从 SDK 12.1 开始,NVENCODE API 支持 Turing 及更高版本的 GPU 的 H.264、HEVC 和 AV1 编码器的迭代编码。当前硬件中此功能的可用性可以使用 NvEncGetEncodeCaps() 查询,并检查 NV_ENC_CAPS_DISABLE_ENC_STATE_ADVANCE。使用此功能,可以多次编码同一帧,例如,每次使用不同的 QP 或 delta-QP。NVENC 将所有新状态(对应于这些迭代中的每一次)存储在其内部状态缓冲区中。然后可以使用 NvEncRestoreEncoderState() API 将 NVENC 状态推进到任何一次迭代。

启用迭代编码的步骤

  1. 调用 NvEncInitializeEncoder() API 时,将 NV_ENC_INITIALIZE_PARAMS::numStateBuffers 设置为所需的值。对于 H.264 和 HEVC,可以分配的最大状态缓冲区数为 16,对于 AV1,最大状态缓冲区数为 32。

应用程序可以在帧的每次迭代之前调用 NvEncReconfigureEncoder() API 来设置所需的编码参数,例如不同的 QP 值。或者,它也可以将 NV_ENC_PIC_PARAMS::qpDeltaMap 数组设置为所需的值,以编码帧的当前迭代。

当图片类型决策 (PTD) 由应用程序做出时进行迭代编码

请按照以下步骤多次编码同一帧

  1. NV_ENC_PIC_PARAMS::encodePicFlags 设置为 NV_ENC_PIC_FLAG_DISABLE_ENC_STATE_ADVANCE
  2. NV_ENC_PIC_PARAMS::frameIdx 设置为有效值。对于帧的所有迭代,它必须相同。
  3. NV_ENC_PIC_PARAMS::stateBufferIdx 设置为所需的索引,以将编码器状态保存在内部状态缓冲区中。对于每次迭代,应指定不同的状态缓冲区索引,以便以后可以使用它来推进编码器状态。
  4. 调用 NvEncEncodePicture() API。它必须返回 NV_ENC_SUCCESS
  5. 根据需要重复上述步骤 1-4,以使用不同的编码参数编码同一帧的多次迭代。由于编码器状态保存在内部状态缓冲区中,因此帧的最大迭代次数将取决于可用于保存状态的状态缓冲区数。
  6. 为所有迭代调用 NvEncLockBitstream() API 以获取编码输出。应用程序也可以在每次迭代后立即调用 NvEncLockBitstream() API。
  7. 选择内部状态缓冲区索引以推进编码器状态,并将其分配给所选迭代的 NV_ENC_RESTORE_ENCODER_STATE_PARAMS::bufferIdx。此外,选择要更新的状态类型,即 NV_ENC_STATE_RESTORE_TYPE,并将其分配给 NV_ENC_RESTORE_ENCODER_STATE_PARAMS::state。调用 NvEncRestoreEncoderState() API。
  8. 如果应用程序选择的状态类型不是 NV_ENC_STATE_RESTORE_FULL,则应用程序必须调用 NvEncRestoreEncoderState() API 两次,一次将 NV_ENC_RESTORE_ENCODER_STATE_PARAMS::state 设置为 NV_ENC_STATE_RESTORE_ENCODE,另一次将 NV_ENC_RESTORE_ENCODER_STATE_PARAMS::state 设置为 NV_ENC_STATE_RESTORE_RATE_CONTROL,每次都使用所需的状态缓冲区索引,并且不必按相同的顺序。
  9. 要编码下一帧,请递增 NV_ENC_PIC_PARAMS::frameIdx 并重复步骤 1-8。

当图片类型决策 (PTD) 由 NVENCODE API 做出时,H.264 和 HEVC 的迭代编码

请按照以下步骤多次编码同一帧

  1. NV_ENC_PIC_PARAMS::encodePicFlagsNV_ENC_PIC_PARAMS::frameIdxNV_ENC_PIC_PARAMS::stateBufferIdx 设置为有效值(如上一节所述),并调用 NvEncEncodePicture() API。此 API 将返回 NV_ENC_SUCCESSNV_ENC_ERR_NEED_MORE_INPUT 状态。
  2. 如果它返回 NV_ENC_SUCCESS,则应用程序现在可以对此帧进行迭代编码。
  3. 如果它返回 NV_ENC_ERR_NEED_MORE_INPUT,则应用程序现在无法对此帧进行迭代编码。应用程序必须发送下一帧进行编码,直到它返回 NV_ENC_SUCCESS。应用程序现在可以对返回 NV_ENC_SUCCESS 的此帧进行迭代编码。
  4. 调用 NvEncLockBitstream() API 以获取第一次迭代的编码输出。
    1. 如果 NV_ENC_LOCK_BITSTREAM::frameIdxDisplay 与步骤 1 或 3 中的 NV_ENC_PIC_PARAMS::frameIdx 相同,则必须为所有剩余迭代调用 NvEncLockBitstream() API。
    2. 在某些情况下,NV_ENC_LOCK_BITSTREAM::frameIdxDisplay 可能与 NV_ENC_PIC_PARAMS::frameIdx 不同,这表明接收到的帧与在步骤 1 或 3 中进行迭代编码的帧不同。在这种情况下,如果需要,应用程序必须对与 NV_ENC_LOCK_BITSTREAM::frameIdxDisplay 对应的帧进行迭代编码。必须为该帧的所有迭代调用 NvEncLockBitstream() API,以获取编码输出,然后调用 NvEncRestoreEncoderState() API 以恢复状态。NVENCODE API 将保存与步骤 1 或 3 中的 NV_ENC_PIC_PARAMS::frameIdx 对应的帧的所有迭代的编码参数,并在所有先前帧(返回 NV_ENC_ERR_NEED_MORE_INPUT 状态的帧)被编码并且所有这些帧的编码器状态都被恢复后,将它们发送进行编码。
  5. 为选定的帧迭代调用 NvEncRestoreEncoderState() API。
    1. 如果任何帧返回 NV_ENC_ERR_NEED_MORE_INPUT,NVENCODE API 现在将发送其中一个帧进行编码。
    2. 如果对于在步骤 a) 中发送进行编码的帧,NV_ENC_PIC_PARAMS::encodePicFlags 未设置为 NV_ENC_PIC_FLAG_DISABLE_ENC_STATE_ADVANCE,则后续帧也将被发送进行编码。
  6. 如果存在返回 NV_ENC_ERR_NEED_MORE_INPUT 的帧,则应用程序必须调用 NvEncLockBitstream() API。NV_ENC_LOCK_BITSTREAM::frameIdxDisplay 将指示现在可以进行迭代编码的帧。
  7. 对所有返回 NV_ENC_ERR_NEED_MORE_INPUT 状态的帧重复上述步骤。

缓冲区重排序

  1. 当存在 B 帧时,驱动程序会进行缓冲区重排序。缓冲区重排序是针对输出码流缓冲区、完成事件和重建帧缓冲区完成的。
  2. 进行此重排序是为了使应用程序可以按解码顺序获取输出,而无需考虑图片类型。
  3. 对于状态缓冲区索引,没有重排序。

下表描述了 API 调用以及将具有编码输出、重建帧输出和内部状态的缓冲区

序号API 调用返回参数注释
1 NvEncEncodePicture (I1, N1=1, O1, E1, R1, F1=0)NV_ENC_SUCCESS
2 NvEncLockBitstream(O1)frameIdxDisplay=1, picType: NV_ENC_PIC_TYPE_I
3 NvEncEncodePicture (I2, N2=2, O2, E2, R2, S1, F2=1)NV_ENC_ERR _NEED_MORE_INPUT
4 NvEncEncodePicture (I3, N3=3, O3, E3, R3, S2, F3=1)NV_ENC_SUCCESS
5 NvEncEncodePicture (I3, N3=3, O4, E4, R4, S3, F4=1)NV_ENC_SUCCESS
6 NvEncLockBitstream(O2)frameIdxDisplay=3, picType: NV_ENC_PIC_TYPE_P帧 3 的迭代 1 的输出,R2 中的 Recon 输出,S2 中保存的内部状态
7 NvEncLockBitstream(O3)frameIdxDisplay=3, picType: NV_ENC_PIC_TYPE_P帧 3 的迭代 2 的输出,R3 中的 Recon 输出,S3 中保存的内部状态
8 NvEncRestoreEncoderState(S2 或 S3)NV_ENC_SUCCESS帧 2 将被发送进行编码
9 NvEncLockBitstream (O4)frameIdxDisplay=2, picType: NV_ENC_PIC_TYPE_B帧 2 的迭代 1 的输出,R4 中的 Recon 输出,S1 中保存的内部状态
10 NvEncEncodePicture (I2, N2=2, O5, E5, R5, S4, F5=1)NV_ENC_SUCCESS
11 NvEncLockBitstream(O5)frameIdxDisplay=2, picType: NV_ENC_PIC_TYPE_B帧 2 的迭代 2 的输出,R5 中的 Recon 输出,S4 中保存的内部状态
12 NvEncRestoreEncoderState(S1 或 S4)NV_ENC_SUCCESS

注意:I1、N1、O1、E1、R1、S1 代表第一个帧的第一次迭代的输入缓冲区、帧索引、输出缓冲区、完成事件、重建缓冲区和状态缓冲区索引。F1=0 代表 NV_ENC_PIC_PARAMS::encodePicFlags 未设置为 NV_ENC_PIC_FLAG_DISABLE_ENC_STATE_ADVANCE。F1=1 代表 NV_ENC_PIC_PARAMS::encodePicFlags 设置为 NV_ENC_PIC_FLAG_DISABLE_ENC_STATE_ADVANCE

当图片类型决策 (PTD) 由 NVENCODE API 做出时,AV1 的迭代编码

由于存在不可显示帧,因此与 H.264 和 HEVC 编码相比,AV1 的迭代编码存在一些差异。当 NV_ENC_CONFIG::frameIntervalP 设置为大于 1 时,将启用不可显示帧。本节详细描述了这些差异。

请按照以下步骤多次编码同一帧

  1. NV_ENC_PIC_PARAMS::encodePicFlagsNV_ENC_PIC_PARAMS::frameIdxNV_ENC_PIC_PARAMS::stateBufferIdx 设置为有效值(如上一节所述),并调用 NvEncEncodePicture() API。此 API 将返回 NV_ENC_SUCCESSNV_ENC_ERR_NEED_MORE_INPUT 状态。
  2. 如果它返回 NV_ENC_SUCCESS,则应用程序现在可以对此帧进行迭代编码。
  3. 如果它返回 NV_ENC_ERR_NEED_MORE_INPUT,则应用程序现在无法对此帧进行迭代编码。应用程序必须发送下一帧进行编码,直到它返回 NV_ENC_SUCCESS。应用程序现在可以对返回 NV_ENC_SUCCESS 的此帧进行迭代编码。
  4. 调用 NvEncLockBitstream() API 以获取第一次迭代的编码输出。
    1. 如果 NV_ENC_LOCK_BITSTREAM::frameIdxDisplay 与步骤 1 或 3 中的 NV_ENC_PIC_PARAMS::frameIdx 相同,则必须为所有剩余迭代调用 NvEncLockBitstream() API。
    2. 在某些情况下,NV_ENC_LOCK_BITSTREAM::frameIdxDisplay 可能与 NV_ENC_PIC_PARAMS::frameIdx 不同,这表明接收到的帧与在步骤 1 或 3 中进行迭代编码的帧不同。在这种情况下,如果需要,应用程序必须对与 NV_ENC_LOCK_BITSTREAM::frameIdxDisplay 对应的帧进行迭代编码。必须为该帧的所有迭代调用 NvEncLockBitstream() API,以获取编码输出,然后调用 NvEncRestoreEncoderState() API 以恢复状态。NVENCODE API 将保存与步骤 1 或 3 中的 NV_ENC_PIC_PARAMS::frameIdx 对应的帧的所有迭代的编码参数,并在所有先前帧(返回 NV_ENC_ERR_NEED_MORE_INPUT 状态的帧)被编码并且所有这些帧的编码器状态都被恢复后,将它们发送进行编码。
    3. 如果为 NV_ENC_LOCK_BITSTREAM::frameIdxDisplay 帧将 NV_ENC_PIC_PARAMS::encodePicFlags 设置为 NV_ENC_PIC_FLAG_DISABLE_ENC_STATE_ADVANCE,并且在此帧之前存在返回 NV_ENC_ERR_NEED_MORE_INPUT 状态的帧,则接收到的编码帧将是不可显示帧。
    4. 对于任何不可显示帧,在编码完所有在此帧之前返回 NV_ENC_ERR_NEED_MORE_INPUT 的帧之后,无论该帧的迭代次数如何,都将只编码一次相应的 OVERLAY 帧。
  5. 应用程序必须调用 NvEncRestoreEncoderState() API 以恢复此帧。它可能会返回 NV_ENC_ERR_NEED_MORE_OUTPUTNV_ENC_SUCCESS 状态。
    1. 如果它返回 NV_ENC_ERR_NEED_MORE_OUTPUT,应用程序必须再次调用 NvEncRestoreEncoderState() API,并将输出缓冲区作为输入放入 NV_ENC_RESTORE_ENCODER_STATE_PARAMS::outputBitstream 中。如果启用了异步编码模式,则应用程序必须将完成事件作为输入放入 NV_ENC_RESTORE_ENCODER_STATE_PARAMS::completionEvent 中。
    2. 如果 NvEncRestoreEncoderState() API 返回 NV_ENC_SUCCESS,则 NVENCODE API 现在将发送其中一个返回 NV_ENC_ERR_NEED_MORE_INPUT 的帧进行编码。
    3. 如果对于在步骤 b) 中发送进行编码的帧,NV_ENC_PIC_PARAMS::encodePicFlags 未设置为 NV_ENC_PIC_FLAG_DISABLE_ENC_STATE_ADVANCE,则后续帧也将被发送进行编码。
  6. 如果存在返回 NV_ENC_ERR_NEED_MORE_OUTPUT 的帧,请调用 NvEncLockBitstream() API。
    1. 应用程序应按照发送进行编码的相同顺序接收这些帧的编码输出。
    2. 如果 NV_ENC_LOCK_BITSTREAM::frameIdxDisplay 的顺序不同,则表明接收到不可显示帧。
    3. 应用程序现在可以对与 NV_ENC_LOCK_BITSTREAM::frameIdxDisplay 对应的帧进行迭代编码(如果需要)。
    4. 对于任何不可显示帧,在编码完所有在此帧之前返回 NV_ENC_ERR_NEED_MORE_INPUT 的帧之后,都将只编码一次相应的 OVERLAY 帧。
  7. 对所有返回 NV_ENC_ERR_NEED_MORE_OUTPUT 的帧重复步骤 5) 和 6) 。

下表描述了 API 调用以及将具有编码输出、重建帧输出和内部状态的缓冲区

序号API 调用返回参数注释
1 NvEncEncodePicture (I1, N1=1, O1, E1, R1, F1=0)NV_ENC_SUCCESS
2 NvEncLockBitstream(O1)frameIdxDisplay=1, picType: NV_ENC_PIC_TYPE_IR1 中的 Recon 输出
3 NvEncEncodePicture (I2, N2=2, O2, E2, R2, S1, F2=1)NV_ENC_ERR _NEED_MORE_INPUT
4 NvEncEncodePicture (I3, N3=3, O3, E3, R3, S2, F3=1)NV_ENC_SUCCESS
5 NvEncEncodePicture (I3, N3=3, O4, E4, R4, S3, F4=1)NV_ENC_SUCCESS
6 NvEncLockBitstream(O2)frameIdxDisplay=3, picType: NV_ENC_PIC_TYPE_P帧 3 的迭代 1 的输出,R3 中的 Recon 输出,S2 中保存的内部状态,不可显示帧
7 NvEncLockBitstream(O3)frameIdxDisplay=3, picType: NV_ENC_PIC_TYPE_P帧 3 的迭代 2 的输出,R4 中的 Recon 输出,S3 中保存的内部状态,不可显示帧
8 NvEncRestoreEncoderState(S2 或 S3)NV_ENC_ERR_NEED _MORE_OUTPUT应用程序必须再次调用此 API
9 NvEncRestoreEncoderState(O5, E5, S2 或 S3)NV_ENC_SUCCESS帧 2 将被发送进行编码
10 NvEncLockBitstream (O4)frameIdxDisplay=2, picType: NV_ENC_PIC_TYPE_B帧 2 的迭代 1 的输出,R2 中的 Recon 输出,S1 中保存的内部状态
11 NvEncEncodePicture (I2, N2=2, O6, E6, R5, S4, F5=1)NV_ENC_SUCCESS
12 NvEncLockBitstream(O5)frameIdxDisplay=2, picType: NV_ENC_PIC_TYPE_B帧 2 的迭代 2 的输出,R5 中的 Recon 输出,S4 中保存的内部状态
13 NvEncRestoreEncoderState(S1 或 S4)NV_ENC_SUCCESS将发送与帧 3 对应的 OVERLAY 帧进行编码
14 NvEncLockBitstream(O6)frameIdxDisplay=3, picType: NV_ENC_PIC_TYPE_P与帧 3 对应的 OVERLAY 帧

注意:I1、N1、O1、E1、R1、S1 代表第一个帧的第一次迭代的输入缓冲区、帧索引、输出缓冲区、完成事件、重建缓冲区、状态缓冲区索引。F1=0 代表 NV_ENC_PIC_PARAMS::encodePicFlags 未设置为 NV_ENC_PIC_FLAG_DISABLE_ENC_STATE_ADVANCE。F1=1 代表 NV_ENC_PIC_PARAMS::encodePicFlags 设置为 NV_ENC_PIC_FLAG_DISABLE_ENC_STATE_ADVANCE。当图片类型决策由应用程序做出时,重建缓冲区没有重排序。应用程序必须使用 NV_ENC_LOCK_BITSTREAM::frameIdxDisplay 来跟踪此缓冲区。

外部前瞻

从 SDK 12.1 开始,NVENCODE API 支持 Turing 及更高版本的 GPU 的 H.264、HEVC 和 AV1 编码器的外部前瞻。外部前瞻产生与内部前瞻相同的结果,内部前瞻只需设置 NV_ENC_RC_PARAMS:: lookaheadDepth 即可启用。迭代编码不支持内部前瞻。因此,外部前瞻功能优于内部前瞻的优势在于它可以与迭代编码一起使用。

请按照以下步骤使用外部前瞻

  1. 调用 nvEncInitializeEncoder() API 时,将 NV_ENC_RC_PARAMS::enableExtLookahead 设置为 1,并将 Set NV_ENC_RC_PARAMS::lookaheadDepth 设置为所需的值。

应用程序需要为每个帧执行以下操作

  1. 调用 NvEncLookaheadPicture(),并将 NV_ENC_LOOKAHEAD_PIC_PARAMS::inputBuffer 设置为从 ::NvEncCreateInputBuffer()::NvEncMapInputResource() API 获取的指针。
  2. 对于前瞻深度 N,在为第一帧调用 NvEncEncodePicture() API 之前,应用程序必须调用 NvEncLookaheadPicture () API N+1 次。

例如,对于等于 4 的前瞻深度

  1. 为帧 0 调用 NvEncLookaheadPicture() API
  2. 为帧 1 调用 NvEncLookaheadPicture() API
  3. 为帧 2 调用 NvEncLookaheadPicture() API
  4. 为帧 3 调用 NvEncLookaheadPicture() API
  5. 为帧 4 调用 NvEncLookaheadPicture() API
  6. 为帧 0 调用 NvEncEncodePicture() API
  7. 为帧 5 调用 NvEncLookaheadPicture() API
  8. 为帧 1 调用 NvEncEncodePicture() API
  9. 为帧 6 调用 NvEncLookaheadPicture() API
  10. 等等…

单向 B 帧

单向 B 帧仅使用过去的帧作为 L0 和 L1 参考列表,并避免了传统 B 帧中观察到的延迟问题。因此,它们可以代替 P 帧使用,尤其是在低延迟编码中。单向 B 帧提高了视频编码质量,并且对性能影响很小。

要使用此功能,请按照以下步骤操作

  • 使用 NvEncGetEncodeCaps API 查询该功能的可用性,并在返回值中检查 NV_ENC_CAPS_SUPPORT_UNIDIRECTIONAL_B
  • 在编码器初始化期间,设置 enableUniDirectionalB = 1
    • 此功能目前支持 HEVC。

前瞻级别

前瞻级别通过使编码器能够缓冲指定数量的帧、估计其复杂性并在这些帧之间按与其复杂性成比例的方式适当分配比特,从而提高视频编码质量。它确定 CTB 的传播,并将较低的 QP 值分配给传播最大的 CTB,并提高视频编码器的速率控制精度。有 4 个不同的前瞻级别,它们为不同的质量和性能权衡提供了支持。NV_ENC_LOOKAHEAD_LEVEL_0 具有最高的性能,而 NV_ENC_LOOKAHEAD_LEVEL_3 具有最高的质量。用户可以根据其质量/性能需求选择适当的前瞻级别

要使用此功能,客户端必须遵循以下步骤

  1. 可以使用 NvEncGetEncodeCaps 查询当前硬件中该功能的可用性,并检查 NV_ENC_CAPS_SUPPORT_LOOKAHEAD_LEVEL.
  2. 需要在初始化期间启用前瞻,方法是将 NV_ENC_INITIALIZE_PARAMS::encodeconfig->rcParams.enableLookahead = 1 设置为 1。
  3. 需要在初始化期间设置前瞻级别,方法是将 NV_ENC_INITIALIZE_PARAMS::encodeconfig->rcParams.lookaheadLevel = NV_ENC_LOOKAHEAD_LEVEL_0...3 设置为 NV_ENC_LOOKAHEAD_LEVEL_0...3。
  4. 要前瞻的帧数应在 NV_ENC_INITIALIZE_PARAMS::encodeconfig->rcParams.lookaheadDepth 中设置,该值最大可为 32。
  5. 启用该功能后,帧将在编码器中排队,因此 NvEncEncodePicture 将返回 NV_ENC_ERR_NEED_MORE_INPUT,直到编码器具有足够数量的输入帧以满足前瞻要求。应持续输入帧,直到 NvEncEncodePicture 返回 NV_ENC_SUCCESS

时间滤波器

时间滤波器尝试基于相邻的过去帧和未来帧来过滤帧,并且非常适用于使用相机捕获的自然视频内容,这些内容可能具有传感器/其他噪声。时间滤波器提高了视频的客观质量,并且在容忍延迟的编码情况下非常有用。

要使用时间滤波器,请在您的应用程序中执行以下步骤。

  1. 通过调用 API NvEncGetEncodeCaps 并检查 NV_ENC_CAPS_SUPPORT_TEMPORAL_FILTER,查询当前硬件的时间滤波器的可用性。
  2. 如果支持,则可以在初始化期间通过设置 NV_ENC_CONFIG_HEVC::tfLevel = NV_ENC_TEMPORAL_FILTER_LEVEL_4 来启用时间滤波器。

时间滤波器使用 CUDA 预处理,因此需要 CUDA 处理能力,具体取决于分辨率和内容。

启用时间滤波器可能会导致编码器性能略有下降。

HEVC 中的多视点视频编码 (MV-HEVC)

HEVC 视频压缩标准包括用于在单个码流中编码多个相关视点或图层的扩展。HEVC 规范(ITU-T H.265 / ISO/IEC 23008-2)的附件 G 基于附件 F,定义了使用 HEVC 表示和解码多个图层的语法。这些扩展的一个关键应用是多视点 HEVC (MV-HEVC),它通过在同一码流中将左眼视图和右眼视图编码为单独的图层,从而实现立体 3D 视频的高效编码。这允许通过利用两个视图之间的冗余,以高效的方式传输 3D 立体视频所需的两个视图。

要使用 MV-HEVC,请在应用程序中执行以下步骤

  1. 通过调用 API NvEncGetEncodeCaps 并检查 NV_ENC_CAPS_SUPPORT_MVHEVC_ENCODE,查询当前硬件的 MV-HEVC 的可用性。
  2. 如果支持,则在编码器初始化期间,设置 NV_ENC_CONFIG_HEVC::enableMVHEVC = 1
  3. 目前,仅支持 2 个视图。
  4. NVENCODE API 期望视图的顺序为 Frame0(view0)、Frame0(view1)、Frame1(view0)、Frame1(view1) 等。因此,请适当地设置 NV_ENC_PIC_PARAMS_HEVC::viewId
  5. NVENCODE API 支持在码流中添加 3D 参考显示信息 SEI 消息。要启用此 SEI 消息,请设置 NV_ENC_CONFIG_HEVC::outputHevc3DReferenceDisplayInfo = 1。此 SEI 消息将添加到编码码流中的每个 IDR 帧。请注意,用户指定的 HEVC_3D_REFERENCE_DISPLAY_INFO 也可以通过使用适当的值填充结构 NV_ENC_PIC_PARAMS_HEVC::p3DReferenceDisplayInfo 来插入。

MV-HEVC 目前不支持与以下编码功能一起使用

  1. LTR
  2. Alpha 图层编码
  3. UniDirectionalB
  4. 前瞻
  5. 时间滤波器
  6. 分帧编码
  7. 2 次编码
  8. NV_ENC_TUNING_INFO_HIGH_QUALITY 之外的 NV_ENC_TUNING_INFO

HDR10/HDR10+: MaxCLL、Mastering Display 和 ITU-T T.35 SEI/元数据

HDR10 和 HDR10+ SEI/元数据被嵌入到 HEVC/AV1 码流中,以确保 HDR 内容在各种设备上准确显示。这些元数据包括 MaxCLL 和 Mastering Display 元数据,用于描述峰值亮度和色域,从而实现适当的色调映射。HDR10+ 还使用 ITU-T T.35 SEI/元数据进行动态的逐帧调整,从而增强视觉质量。嵌入这些元数据可确保 HDR 显示器上的兼容性和最佳性能。

API 按照如下方式支持 HDR10 和 HDR10+ SEI/元数据

  1. 对于 HEVC,NVENCODE API 支持在码流中添加内容亮度级别信息 SEI 消息。要启用此功能,请设置 NV_ENC_CONFIG_HEVC::outputMaxCll = 1。此 SEI 消息将添加到编码码流中的每个 IDR 帧。请注意,用户指定的内容亮度级别也可以通过填充结构体 NV_ENC_PIC_PARAMS_HEVC::pMaxCll 并赋予适当的值来插入。
  2. 对于 AV1,NVENCODE API 支持在码流中添加内容亮度级别元数据。要启用此功能,请设置 NV_ENC_CONFIG_AV1::outputMaxCll = 1。此元数据将添加到编码码流中的每个关键帧。请注意,用户指定的内容亮度级别也可以通过填充结构体 NV_ENC_PIC_PARAMS_AV1::pMaxCll 并赋予适当的值来插入。
  3. 对于 HEVC,NVENCODE API 支持在码流中添加母带显示色彩容量 SEI 消息。要启用此 SEI 消息,请设置 NV_ENC_CONFIG_HEVC::outputMasteringDisplay = 1。此 SEI 消息将添加到编码码流中的每个 IDR 帧。请注意,用户指定的 MASTERING_DISPLAY_INFO 也可以通过填充结构体 NV_ENC_PIC_PARAMS_HEVC::pMasteringDisplay 并赋予适当的值来插入。
  4. 对于 AV1,NVENCODE API 支持在码流中添加母带显示色彩容量元数据。要启用此元数据,请设置 NV_ENC_CONFIG_AV1::outputMasteringDisplay = 1。此 SEI 消息将添加到编码码流中的每个关键帧。请注意,用户指定的 MASTERING_DISPLAY_INFO 也可以通过填充结构体 NV_ENC_PIC_PARAMS_AV1::pMasteringDisplay 并赋予适当的值来插入。
  5. 对于 HEVC,NVENCODE API 支持在码流中添加 ITUT-T T.35 SEI 消息作为用户 SEI 消息。NV_ENC_PIC_PARAMS_HEVC::seiPayloadArray 可用于通过更新正确的负载类型来写入 ITUT-T T.35。
  6. 对于 AV1,NVENCODE API 支持在码流中添加 ITUT-T T.35 元数据作为用户 obu。NV_ENC_PIC_PARAMS_AV1::obuPayloadArray 可用于通过更新正确的负载类型来写入 ITUT-T T.35。

外部 ME 提示

运动矢量提示可以传递给 NVENC,以指导运动搜索。可以为每个 MB/CTU/SB 传递 L0 和 L1(对于 B 帧)方向的提示矢量。

要在 H264 和 HEVC 中使用此功能,请按照以下步骤操作

  1. 在编码器初始化时设置 NV_ENC_INITIALIZE_PARAMS::enableExternalMEHints,通过填充 NV_ENC_INITIALIZE_PARAMS::maxMEHintCountsPerBlock 中的相应字段,为每个分区类型指定每个 MB/CTU 每个方向的最大提示候选数量。NV_ENC_INITIALIZE_PARAMS::maxMEHintCountsPerBlock[0] 用于 L0 预测器,NV_ENC_INITIALIZE_PARAMS::maxMEHintCountsPerBlock[1] 用于 L1 预测器。
  2. 对于每一帧,通过填充 NV_ENC_PIC_PARAMS::meHintCountsPerBlock 中的相应字段,为不同的分区类型指定每个块每个方向的提示候选数量。通过 NV_ENC_PIC_PARAMS::meExternalHints 传递指向存储提示的缓冲区的指针。对于每个 MB/CTU,应按照以下分区类型顺序提供提示:16x16、16x8、8x16、8x8。对于每种分区类型,应提供 L0 提示,然后提供 L1 提示(如果可用)。对于 HEVC,仅支持 16x16 和 8x8 分区大小。对于 H264 和 HEVC,外部提示范围(以整像素为单位)在水平方向为 [-2048, 2047],在垂直方向为 [-512, 511]。H264 和 HEVC 的提示结构为 NVENC_EXTERNAL_ME_HINT

要在 AV1 中使用此功能,请按照以下步骤操作

  1. 在编码器初始化时设置 NV_ENC_INITIALIZE_PARAMS::enableExternalMEHints,通过填充 NV_ENC_INITIALIZE_PARAMS::maxMEHintCountsPerBlock 中的相应字段,为每个 SB 每个方向指定最大提示候选数量。NV_ENC_INITIALIZE_PARAMS::maxMEHintCountsPerBlock[0] 用于 L0 预测器,NV_ENC_INITIALIZE_PARAMS::maxMEHintCountsPerBlock[1] 用于 L1 预测器。
  2. 对于每一帧,通过填充 NV_ENC_PIC_PARAMS::meHintCountsPerBlock 中的相应字段,为每个块每个方向指定提示候选数量。通过 NV_ENC_PIC_PARAMS::meExternalSbHints 传递指向存储提示的缓冲区的指针,并通过填充 NV_ENC_PIC_PARAMS::meSbHintsCount 字段来指定帧的外部 ME SB 提示候选总数。对于每个 SB,应提供 L0 提示,然后提供 L1 提示(如果可用)。对于 AV1,外部提示范围(以整像素为单位)在水平方向为 [-1023, 1023],在垂直方向为 [-511, 511]。AV1 的提示结构为 NVENC_EXTERNAL_ME_SB_HINT

NVIDIA 硬件视频编码器在各种应用中用于多种目的。一些常见的应用包括:视频录制(存档)、游戏直播(在线广播/多播视频游戏)、转码(直播和视频点播)和流媒体(游戏或直播内容)。这些用例中的每一个都对其质量、比特率、延迟容忍度、性能约束等有独特的要求。虽然 NVIDIA 编码器接口提供了使用许多 API 灵活控制设置的功能,但下表可用作某些流行用例的推荐设置的通用指南,以提供最佳的编码码流质量。这些建议尤其适用于基于第二代 Maxwell 架构及更高版本的 GPU。对于早期的 GPU(Kepler 和第一代 Maxwell),建议客户使用表 5中的信息作为起点,并调整设置以实现适当的性能-质量权衡。

*: 推荐用于低运动游戏和自然视频。

**: 推荐在第二代 Maxwell GPU 及更高版本上使用。

***: 这些功能对于在嘈杂介质中传输期间进行错误恢复非常有用。对于需要减少视频内存占用的用例,应遵循以下指南。

  • 避免使用 B 帧。 B 帧需要额外的缓冲区进行重排序,因此避免使用 B 帧可以节省视频内存使用量。
  • 减少最大参考帧数。 减少最大参考帧数会导致 NVIDIA 显示驱动程序在内部分配较少的缓冲区,从而减少视频内存占用。
  • 使用单遍速率控制模式。 与单遍相比,双遍速率控制会消耗额外的视频内存,这是因为为第一遍编码分配了额外的内存。完整分辨率的第一遍双遍速率控制模式比四分之一分辨率的第一遍消耗更多的内存。
  • 避免自适应量化 / 加权预测。 诸如自适应量化 / 加权预测之类的功能会在视频内存中分配额外的缓冲区。如果不使用这些功能,则可以避免这些分配。
  • 避免前瞻。 前瞻会为在前瞻队列中缓冲的帧分配额外的缓冲区在视频内存中。
  • 避免时间滤波器。 时间滤波器需要相邻帧,并在视频内存中分配额外的缓冲区。
  • 避免 UHQ 调优信息。 UHQ 调优信息启用前瞻和时间滤波器,这些功能对内存的要求更高。

优化 NVENC 以获得性能 NVENC 针对高性能/高吞吐量编码进行了优化。为了最大限度地利用它,应用程序必须能够以足够快的节奏提供帧。NVENC 最常见的瓶颈取决于正在实施的视频编码管道:

  • 从磁盘编码帧:
    • 文件读取速度:从磁盘加载 RAW 帧
    • PCIe 速度:将 RAW 帧从主机传输到设备,或将压缩帧从设备传输到主机
  • 从磁盘转码帧:
    • 解码器速度:解码器可能是瓶颈,尤其是在主机中运行时。如果可能,建议使用 NVDEC,或者,如果 NVDEC 不支持所需的格式,我们建议解码器在设备上运行,例如 NVJPEG 2000
    • 计算速度:解码器和编码器之间的计算步骤,例如,像素格式转换、缩放或 AI 滤波器。

一般建议:

  • 应用程序应在 GPU 上实施完整(或大部分)视频管道,即解码、计算和编码,以减少通过 PCIe 的传输量。
  • 管道的不同步骤应使用多个线程,以保证并发性,例如,在编码帧 0 时,应将帧 1 加载到设备中,同时从磁盘加载帧 2。
  • 当使用多 NVENC 技术(如分帧编码 (SFE))进行转码时,NVDEC 可能会成为瓶颈。单个 NVDEC 应该能够以足够快的速度解码帧,以供 2 个 NVENC 解码。例如,当使用 3 路(或更多)SFE 时,应用程序应利用多个解码器会话(如果可能)来增加正在使用的 NVDEC 数量。

但是请注意,上述指南可能会导致编码质量有所损失。因此,建议客户进行适当的评估,以在编码质量、速度和内存消耗之间实现适当的平衡。

声明

本文档仅供参考,不应被视为对产品特定功能、状况或质量的保证。NVIDIA Corporation(“NVIDIA”)对本文档中包含的信息的准确性或完整性不作任何明示或暗示的陈述或保证,并且对本文档中包含的任何错误不承担任何责任。NVIDIA 对因使用此类信息而造成的后果或使用,或因其使用可能导致的任何专利或第三方其他权利的侵犯不承担任何责任。本文档不构成开发、发布或交付任何材料(下文定义)、代码或功能的承诺。

NVIDIA 保留随时对此文档进行更正、修改、增强、改进和任何其他更改的权利,恕不另行通知。

客户在下订单前应获取最新的相关信息,并应核实此类信息是最新且完整的。

NVIDIA 产品在销售时受 NVIDIA 标准销售条款和条件的约束,除非 NVIDIA 和客户的授权代表签署的个别销售协议(“销售条款”)另有约定。NVIDIA 在此明确反对将任何客户通用条款和条件应用于购买本文档中引用的 NVIDIA 产品。本文档未直接或间接地形成任何合同义务。

NVIDIA 产品并非设计、授权或保证适用于医疗、军事、航空、航天或生命维持设备,也不适用于 NVIDIA 产品的故障或失灵可能合理预期会导致人身伤害、死亡或财产或环境损害的应用。对于在上述设备或应用中包含和/或使用 NVIDIA 产品,NVIDIA 不承担任何责任,因此,此类包含和/或使用由客户自行承担风险。

NVIDIA 不对基于本文档的产品适用于任何特定用途作出任何陈述或保证。NVIDIA 不一定对每种产品的全部参数进行测试。客户全权负责评估和确定本文档中包含的任何信息的适用性,确保产品适合并满足客户计划的应用,并为应用执行必要的测试,以避免应用或产品的默认设置。客户产品设计的缺陷可能会影响 NVIDIA 产品的质量和可靠性,并可能导致超出本文档中包含的额外或不同的条件和/或要求。NVIDIA 对可能基于以下原因或归因于以下原因的任何默认设置、损坏、成本或问题不承担任何责任:(i) 以任何违反本文档的方式使用 NVIDIA 产品;或 (ii) 客户产品设计。

商标

NVIDIA、NVIDIA 徽标以及 cuBLAS、CUDA、CUDA Toolkit、cuDNN、DALI、DIGITS、DGX、DGX-1、DGX-2、DGX Station、DLProf、GPU、Jetson、Kepler、Maxwell、NCCL、Nsight Compute、Nsight Systems、NVCaffe、NVIDIA Deep Learning SDK、NVIDIA Developer Program、NVIDIA GPU Cloud、NVLink、NVSHMEM、PerfWorks、Pascal、SDK Manager、Tegra、TensorRT、TensorRT Inference Server、Tesla、TF-TRT、Triton Inference Server、Turing 和 Volta 是 NVIDIA Corporation 在美国和其他国家/地区的商标和/或注册商标。其他公司和产品名称可能是与其关联的各自公司的商标。

1

要检查您的板卡运行的模式,请运行驱动程序随附的命令行实用程序 nvidia-smi(Windows 上为 nvidia-smi.exe)。
© 2010-2025 NVIDIA Corporation。保留所有权利。 上次更新时间:2025 年 1 月 27 日。