NVENC编码指导

介绍

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

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

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

NVENCODE API保证二进制向后兼容性(并且会在向后兼容性被破坏时进行显式引用)。这意味着使用已发布 API 的旧版本编译的应用程序将继续在 NVIDIA 发布的未来驱动程序版本上运行。
SDK下载地址

基本编码流程

开发人员可以创建一个客户端应用程序,调用由nvEncodeAPI.dll/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 函数。

NVENCODE API 旨在接受原始视频帧(YUV 或 RGB 格式)并输出 H.264 或 HEVC 比特流。大体上,编码流程包括以下步骤:

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

这些步骤在文档的其余部分进行了解释,并在视频编解码器 SDK 包中包含的示例应用程序中进行了演示。

为编码器设置硬件

打开编码会话

加载 DLL 或共享对象库后,客户端与 API 的第一次交互是调用NvEncodeAPICreateInstance,该接口会将动态库中的函数加载到对应的函数指针中,供后续调用NVENCODE API提供方法。

    m_nvenc = { NV_ENCODE_API_FUNCTION_LIST_VER };
    NVENC_API_CALL(NvEncodeAPICreateInstance(&m_nvenc));

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

    NV_ENC_OPEN_ENCODE_SESSION_EX_PARAMS encodeSessionExParams = { NV_ENC_OPEN_ENCODE_SESSION_EX_PARAMS_VER };
    encodeSessionExParams.device = m_pDevice;  // dx11 device等
    encodeSessionExParams.deviceType = m_eDeviceType;  // NV_ENC_DEVICE_TYPE_DIRECTX
    encodeSessionExParams.apiVersion = NVENCAPI_VERSION;
    void *hEncoder = NULL;
    NVENC_API_CALL(m_nvenc.nvEncOpenEncodeSessionEx(&encodeSessionExParams, &hEncoder));

对于DirectX 11来说,deviceType 设置为NV_ENC_DEVICE_TYPE_DIRECTX

设置编解码器GUID

NV_ENC_CODEC_H264_GUID,
NV_ENC_CODEC_HEVC_GUID

设置编码器预设值

总共7个预设值

NV_ENC_PRESET_P1_GUID,
NV_ENC_PRESET_P2_GUID,
NV_ENC_PRESET_P3_GUID,
NV_ENC_PRESET_P4_GUID,
NV_ENC_PRESET_P5_GUID,
NV_ENC_PRESET_P6_GUID,
NV_ENC_PRESET_P7_GUID,

tuning info

typedef enum NV_ENC_TUNING_INFO
{
    NV_ENC_TUNING_INFO_UNDEFINED         = 0,   /**< Undefined tuningInfo. Invalid value for encoding. */
    NV_ENC_TUNING_INFO_HIGH_QUALITY      = 1,   /**< Tune presets for latency tolerant encoding.*/
    NV_ENC_TUNING_INFO_LOW_LATENCY       = 2,   /**< Tune presets for low latency streaming.*/
    NV_ENC_TUNING_INFO_ULTRA_LOW_LATENCY = 3,   /**< Tune presets for ultra low latency streaming.*/
    NV_ENC_TUNING_INFO_LOSSLESS          = 4,   /**< Tune presets for lossless encoding.*/
    NV_ENC_TUNING_INFO_COUNT                    /**< Count number of tuningInfos. Invalid value. */
}NV_ENC_TUNING_INFO;

流行视频编码用例的调整信息如下:

用例 调整信息参数的推荐值
高品质耐延迟转码
视频存档
OTT 流媒体编码
High quality
云游戏
流媒体
视频会议
Low latency, with CBR
云游戏
流媒体
视频会议
在严格带宽受限的信道中
Ultra-low latency, with CBR
保留原始视频片段以供日后编辑
一般无损数据归档(视频或非视频)
Lossless

选择编码器预设配置

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

  1. 枚举支持的预设。
  2. 选择要为其获取编码配置的预设GUID。
  3. 调用NvEncGetEncodePresetConfigEx,传入选定的encodeGUIDtuningInfopresetGUID作为输入。
  4. 可以通过以下方式检索所需的预设编码器配置 NV_ENC_PRESET_CONFIG::presetCfg.
  5. 如果需要,使用相应的配置 API 覆盖默认编码器参数。
NV_ENC_PRESET_CONFIG presetConfig = { NV_ENC_PRESET_CONFIG_VER, { NV_ENC_CONFIG_VER } };
m_nvenc.nvEncGetEncodePresetConfigEx(m_hEncoder, codecGuid, presetGuid, tuningInfo, &presetConfig);

选择编码器配置文件

客户端可以为特定的编码场景指定要编码的配置文件。例如,编码视频以在 iPhone/iPod 上播放、编码用于蓝光光盘创作的视频等需要某些配置文件。
客户端应执行以下操作以检索支持的编码器配置文件列表:

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

获取支持的输入格式列表

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 以确定对所需属性的支持。

初始化硬件编码器会话

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

NVENC_API_CALL(m_nvenc.nvEncInitializeEncoder(m_hEncoder, &m_initializeParams));

配置编码会话属性NV_ENC_INITIALIZE_PARAMS

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

会话参数

NV_ENC_INITIALIZE_PARAMS结构体中包含可用的输入格式、输出尺寸、显示纵横比、帧率、平均码率等常用参数 。客户端应该使用NvEncInitalizeEncoder传入这个结构的一个实例。
客户必须填充NV_ENC_INITIALIZE_PARAMS的以下成员:

  • NV_ENC_INITALIZE_PARAMS::encodeGUID:客户端必须选择合适的编解码器 GUID。
  • NV_ENC_INITIALIZE_PARAMS::encodeWidth:客户端必须指定编码视频的所需宽度。
  • NV_ENC_INITIALIZE_PARAMS::encodeHeight:客户端必须指定编码视频的所需高度。
    NV_ENC_INITALIZE_PARAMS::reportSliceOffsets可用于启用切片偏移的报告。此功能需要 NV_ENC_INITALIZE_PARAMS::enableEncodeAsync 设置为 0,并且不适用于 Kepler GPU 上基于 MB 和基于字节的切片。

高级编解码器级参数NV_ENC_CONFIG

参数NV_ENC_INITIALIZE_PARAMS::NV_ENC_CONFIG encodeConfig:处理编码比特流的参数,如 GOP 长度、编码器配置文件、速率控制模式等。

typedef struct _NV_ENC_CONFIG
{
    uint32_t                        version;           
    GUID                            profileGUID;       
    uint32_t                        gopLength;         
    int32_t                         frameIntervalP;    
    uint32_t                        monoChromeEncoding;
    NV_ENC_PARAMS_FRAME_FIELD_MODE  frameFieldMode;    
                                                                                                              
    NV_ENC_MV_PRECISION             mvPrecision;       
    NV_ENC_RC_PARAMS                rcParams;          
    NV_ENC_CODEC_CONFIG             encodeCodecConfig; 
    uint32_t                        reserved [278];    
    void*                           reserved2[64];     
} NV_ENC_CONFIG;

NV_ENC_CODEC_CONFIG encodeCodecConfig;为高级H.264HEVC编解码器特定参数,

完成编码的编解码器配置

使用预设进行高级控制

这是配置 NVIDIA 视频编码器接口的最简单方法,客户端执行的设置步骤最少。这适用于客户端不需要微调任何编解码器级别参数的用例。
在这种情况下,客户端应执行以下步骤:

  • 客户端应指定会话参数
  • 或者,客户端可以枚举和选择最适合当前用例的预设 GUID。然后客户端应该使用传递选定的预设 GUID NV_ENC_INITIALIZE_PARAMS::presetGUID. 这有助于NVIDIA视频编码器根据encodeGUID, tuning info and presetGUID做出正确的编码会话配置。
  • 客户端应设置高级编解码器级参数指针NV_ENC_INITIALIZE_PARAMS::encodeConfig::encodeCodecConfig 为 NULL。

速率控制

NVENC 支持多种速率控制模式,并通过结构NV_ENC_INITIALIZE_PARAMS::encodeConfig::rcParams提供对与速率控制算法相关的各种参数的控制 . 速率控制算法在 NVENC 固件中实现。
NVENC 支持以下速率控制模式:
恒定比特率 (CBR):恒定比特率设置rateControlModeNV_ENC_PARAMS_RC_CBR. 在这种模式下,只有averageBitRate需要并用作速率控制算法的目标输出比特率。客户端可以使用NV_ENC_RC_PARAMS::lowDelayKeyFrameScale来控制 I 帧与 P 帧的比率,如果 I 帧最终生成大量比特,这对于避免信道拥塞很有用。设置NV_ENC_CONFIG_H264/ NV_ENC_CONFIG_HEVC::enableFillerDataInsertion = 1以防需要严格遵守比特率。
可变比特率 (VBR):可变比特率设置rateControlModeNV_ENC_PARAMS_RC_VBR。 编码器尝试符合平均比特率averageBitRate,并在编码的任何时间不内超过maxBitRate。在这种模式下,averageBitRate必须指定。如果maxBitRate未指定,NVENC 会将其设置为内部确定的默认值。为了更好的控制,建议客户端同时指定这两个参数maxBitRateaverageBitRate
恒定 QP:此模式设置rateControlModeNV_ENC_PARAMS_RC_CONSTQP. 在这种模式下,整个帧使用指定的 QP (NV_ENC_RC_PARAMS::constQP)编码。
目标质量:此模式设置rateControlMode为VBR 和所需的目标质量targetQuality. 此目标质量的范围是 0 到 51(视频编解码器 SDK 8.0 及更高版本也支持小数值)。在这种模式下,编码器通过maxBitRate中指定的比特率参数的变化来保持每帧的质量. 因此,最终的平均比特率可能会因正在编码的视频内容而有很大差异。如果maxBitRate未指定,编码器将根据需要使用尽可能多的位来实现目标质量。然而,如果maxBitRate设置后,它将形成实际比特率的上限。如果maxBitRate在此模式下设置过低,比特率可能会受到限制,导致可能无法实现所需的目标质量。

多通道帧编码

设置编码会话属性

完成所有编码器设置后,客户端应调用NvEncInitializeEncoder并传入填充好的编码设置结构体: NV_ENC_CONFIG。某些设置,例如速率控制模式、平均比特率、分辨率等,可以即时更改。

NVENC_API_CALL(m_nvenc.nvEncInitializeEncoder(m_hEncoder, &m_initializeParams));

客户端需要在初始化 Encode Session 时明确指定以下内容:

操作模式

客户端应该设置NV_ENC_INITIALIZE_PARAMS::enableEncodeAsync, 如果要在异步模式下运行,则为 1;如果要在同步模式下运行,则为 0。
异步模式编码仅在 Windows 7 及更高版本上受支持

图片类型决定

如果客户端想按显示顺序发送输入缓冲区,它必须设置 enablePTD = 1。 如果enablePTD设置为 1,确定图片类型的决定将由 NVENCODE API 进行。
如果客户端想以编码顺序发送输入缓冲区,它必须设置enablePTD = 0, 并且必须指定

NV_ENC_PIC_PARAMS::pictureType
NV_ENC_PIC_PARAMS_H264/NV_ENC_PIC_PARAMS_HEVC::displayPOCSyntax
NV_ENC_PIC_PARAMS_H264/NV_ENC_PIC_PARAMS_HEVC::refPicFlag

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

一旦编码会话被初始化,客户端应该分配缓冲区来保存输入/输出数据。
客户端调用 NvEncCreateInputBuffer 来分配输入缓冲区。在这种情况下,客户端负责在关闭编码会话之前销毁分配的输入缓冲区。根据所选的输入缓冲区格式,用有效的输入数据填充输入缓冲区也是客户端的责任。
客户端调用NvEncCreateBitstreamBuffer来分配输出的比特流缓冲区。在关闭编码会话之前销毁这些缓冲区是客户端的责任。

NV_ENC_CREATE_BITSTREAM_BUFFER createBitstreamBuffer = { NV_ENC_CREATE_BITSTREAM_BUFFER_VER };
NVENC_API_CALL(m_nvenc.nvEncCreateBitstreamBuffer(m_hEncoder, &createBitstreamBuffer));

或者,在客户端不能或不想通过 NVIDIA 视频编码器接口分配输入缓冲区的情况下,它可以使用任何外部分配的 DirectX 资源作为输入缓冲区。但是,客户端必须执行一些简单的处理,才能在使用前将这些资源映射到 NVIDIA 视频编码器接口识别的资源句柄。这一过程详见外部分配的输入缓冲区。
如果客户端已使用 CUDA 设备初始化编码器会话,并希望使用未通过 NVIDIA 视频编码器接口分配的输入缓冲区,则客户端需要使用使用cuMemAllocAPI 家族。NVIDIA 视频编码器接口支持CUdeviceptrCUarray输入格式。
如果客户端已使用 OpenGL 设备类型初始化编码器会话并希望使用未通过 NVIDIA 视频编码器接口分配的输入缓冲区,则客户端需要提供之前分配的纹理。
客户端可以使用glGenTextures生成纹理, 将其绑定到 NV_ENC_INPUT_RESOURCE_OPENGL_TEX::GL_TEXTURE_RECTANGLE 或者 NV_ENC_INPUT_RESOURCE_OPENGL_TEX::GL_TEXTURE_2D 目标,使用glTexImage2D()为它分配存储,并将数据复制到其中。
请注意,NVENCODE API 的 OpenGL 接口仅在 Linux 上受支持。
注意:客户端应至少分配(1 + NB) 个输入和输出缓冲区,其中NB是连续P帧之间的B帧数。

检索序列参数

配置编码会话后,客户端可以随时通过调用NvEncGetSequenceParams获取序列参数信息(SPS) . 分配和最终取消分配用于保存序列参数信息(SPS)大小的缓冲区是客户端的责任。
默认情况下,SPS/PPS 数据将附加到每个 IDR 帧。但是,客户端也可以请求编码器按需生成 SPS/PPS 数据。要完成此操作,请设置 NV_ENC_PIC_PARAMS::encodePicFlags = NV_ENC_PIC_FLAG_OUTPUT_SPSPPS。 为当前输入生成的输出比特流将包括 SPS/PPS。
客户端可以在编码器初始化后(NvEncInitializeEncoder) 并且会话处于活动状态的任何时候调用 NvEncGetSequenceParams

编码视频流

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

为编码准备输入缓冲区

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

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

如果客户端通过NvEncCreateInputBuffer方式分配了输入缓冲区 ,客户端需要在使用缓冲区作为输入进行编码之前填充有效的输入数据。为此,客户端应调用NvEncLockInputBuffer获取指向输入缓冲区的 CPU 指针。一旦客户端填充了输入数据,它应该调用 NvEncUnlockInputBuffer. 输入缓冲区只有在解锁后才应传递给编码器。任何输入缓冲区都应在销毁/重新分配它们之前调用解锁 NvEncUnlockInputBuffer

外部分配的输入缓冲区

要将外部分配的缓冲区传递给编码器,请执行以下步骤:

  1. 填充具有外部分配缓冲区的属性的NV_ENC_REGISTER_RESOURCE
  2. 调用NvEncRegisterResource,将上述填充好的NV_ENC_REGISTER_RESOURCE属性注册。
  3. NvEncRegisterResource返回一个已注册的资源句柄NV_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. 在销毁注册的资源之前,客户端还必须调用NvEncUnregisterResource,传入NvEncRegisterResource返回的句柄。
    映射的资源句柄 (NV_ENC_MAP_INPUT_RESOURCE::mappedResource) 不应在 NVIDIA 视频编码器接口处于映射状态时用于任何其他目的。这种用法不受支持,可能会导致未定义的行为。

配置每帧编码参数

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

强制将当前帧编码为帧内 (I) 帧

要将当前帧强制为帧内 (I) 帧,请设置NV_ENC_PIC_PARAMS::encodePicFlags = NV_ENC_PIC_FLAG_FORCEINTRA

强制将当前帧用作参考帧

要强制将当前帧用作参考帧,请设置NV_ENC_PIC_PARAMS_H264/NV_ENC_PIC_PARAMS_HEVC::refPicFlag = 1

强制将当前帧用作 IDR 帧

要强制将当前帧编码为 IDR 帧,请设置NV_ENC_PIC_PARAMS::encodePicFlags = NV_ENC_PIC_FLAG_FORCEIDR

请求生成序列参数

要将 SPS/PPS 与当前编码的帧一起包含,请设置 NV_ENC_PIC_PARAMS::encodePicFlags = NV_ENC_PIC_FLAG_OUTPUT_SPSPPS

提交输入帧进行编码

客户应该调用 NvEncEncodePicture 执行编码。

NVENCSTATUS NVENCAPI NvEncEncodePicture(void* encoder, NV_ENC_PIC_PARAMS* encodePicParams);

输入图片数据将从指定的输入缓冲区中获取,一旦编码过程完成,编码的码流将在指定的码流(输出)缓冲区。
编解码器不可知的参数,如时间戳、持续时间、输入缓冲区指针等,通过结构NV_ENC_PIC_PARAMS 传递,而特定于编解码器的参数通过NV_ENC_PIC_PARAMS_H264/NV_ENC_PIC_PARAMS_HEVC结构传递。
客户端应使用 NV_ENC_PIC_PARAMS::codecPicParams 成员,指定特定于编解码器的结构 NV_ENC_PIC_PARAMS

检索编码输出

在完成输入图片的编码过程后,客户端需要调用 NvEncLockBitstream获取指向编码码流的 CPU 指针。客户端可以制作编码数据的本地副本或传递 CPU 指针以供进一步处理(例如,发送到媒体文件编写器)。
CPU 指针直到客户端调用 NvEncUnlockBitstream之前将保持有效。客户应该在它完成处理输出数据之后调用 NvEncUnlockBitstream
客户端必须确保所有码流缓冲区在销毁/取消分配之前(例如,在关闭编码会话时)或在将它们重新用作于后续帧的输出缓冲区之前都需要将其解锁。

编码结束

通知输入流结束

要通知输入流结束,客户端必须调用 NvEncEncodePicture,并将NV_ENC_PIC_PARAMS:: encodePicFlags设置为NV_ENC_FLAGS_EOSNV_ENC_PIC_PARAMS 其他成员设置为 0。当调用NvEncEncodePicture用于 EOS 通知后,不再需要输入缓冲区 。
EOS 通知有效地刷新了编码器。这可以在单个编码会话中多次调用。但是,此操作必须在关闭编码会话之前完成。

释放资源

编码完成后,客户端应销毁所有分配的资源。
客户应该调用NvEncDestroyInputBuffer,如果它通过 NVIDIA 视频编码器接口分配了输入缓冲区。在摧毁它之前,客户端必须确保调用NvEncUnlockInputBuffer解锁了输入缓冲区。
客户应该调用NvEncDestroyBitstreamBuffer销毁它分配的每个比特流缓冲区。在摧毁它之前,客户端必须确保调用NvEncUnlockBitstream解锁了比特流缓冲区。

关闭编码会话

客户应该调用NvEncDestroyEncoder关闭编码会话。客户端应在调用NvEncDestroyEncoder之前,确保与正在关闭的编码会话相关的所有资源都已销毁。 这些包括输入缓冲区、码流缓冲区、SPS/PPS 缓冲区等。
它还必须确保所有已注册的事件都已取消注册,并且所有映射的输入缓冲区句柄都已取消映射。

操作模式

NVIDIA Video Encoder Interface 支持以下两种操作模式。

异步模式

这种操作模式用于异步输出缓冲区处理。对于这种模式,客户端分配一个事件对象并将事件与分配的输出缓冲区相关联。此事件对象作为 NVIDIA 编码器接口的一部分传递给NvEncEncodePicture应用程序接口。客户端可以在单独的线程中等待事件。当事件发出信号时,客户端调用 NVIDIA 视频编码器接口来复制编码器产生的输出比特流。请注意,编码器仅支持 Windows 7 及更高版本的异步操作模式,在 WDDM 模式下运行的驱动程序。
客户端应该设置标志NV_ENC_INITIALIZE_PARAMS::enableEncodeAsync为1,表示它要在异步模式下运行。在创建事件对象(为每个输出比特流缓冲区分配一个对象)后,客户端需要调用 NvEncRegisterAsyncEvent。客户端需要将比特流缓冲区句柄和相应的事件句柄作为输入传递给NvEncEncodePicture。当硬件编码器完成对当前输入数据的编码时,NVIDIA 视频编码器接口将发出此事件信号。然后客户端可以在非阻塞模式(NV_ENC_LOCK_BITSTREAM::doNotWait 标志设置为 1 )下调用NvEncLockBitstream 以获取输出数据。
在销毁事件对象之前,客户应该调用NvEncUnregisterAsyncEvent取消注册的事件句柄。只要有可能,NVIDIA 建议使用异步操作模式而不是同步模式。
异步模式的分步控制流程如下:

  1. 在异步模式下工作时,输出样本必须由事件 + 输出缓冲区组成,并且客户端必须以多线程方式工作(D3D9 设备应使用 MULTITHREADED 标志创建)。
  2. 输出缓冲区使用 NvEncCreateBitstreamBuffer来分配。它将返回一个指向输出内存的不透明指针 NV_ENC_CREATE_BITSTREAM_BUFFER::bitstreambuffer。这个不透明的输出指针应该用于NvEncEncodePictureNvEncLockBitsteam/ NvEncUnlockBitsteam的调用中。要使用 CPU 访问输出存储器,客户端必须调用NvEncLockBitsteam。IO 缓冲区的数量应至少为 4 + B 帧的数量。
  3. 如果这些事件是使用CreateEvent分配的 Windows 事件句柄,则在编码前需要使用NvEncRegisterAsyncEvent进行注册。每个编码会话只需要注册一次事件。客户端必须在销毁事件句柄之前,使用NvEncUnregisterAsyncEvent取消注册事件。事件句柄的数量必须与输出缓冲区的数量相同,因为每个输出缓冲区都与一个事件相关联。
  4. 客户端必须创建一个辅助线程,它可以在其中等待完成事件并从输出样本中复制比特流数据。客户端将有两个线程:一个是主应用程序线程,它向 NVIDIA 编码器提交编码工作,而辅助线程等待完成事件并从输出缓冲区复制压缩的比特流数据。
  5. 客户端必须将输出缓冲区和事件发送到 NV_ENC_PIC_PARAMS::outputBitstreamNV_ENC_PIC_PARAMS::completionEvent字段,依次调用NvEncEncodePicture API
  6. 然后客户端应该按照它调用的相同顺序在辅助线程上等待事件,NvEncEncodePicture调用与输入缓冲区重新排序无关(编码顺序 != 显示顺序)。NVIDIA Encoder 负责处理 B 帧的重新排序,并且应该对编码器客户端透明。
  7. 当事件得到信号时,客户端必须向下发送它正在等待的样本事件的输出缓冲区,即调用NvEncLockBitstream的参数NV_ENC_LOCK_BITSTREAM::outputBitstream
  8. NVIDIA 编码器接口返回作为NV_ENC_LOCK_BITSTREAM一部分的,以字节为单位的CPU指针和比特流大小。
  9. 复制比特流数据后,对于锁定的输出比特流缓冲区,客户端必须调用 NvEncUnlockBitstream

提示:

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

同步模式

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

线程模型

为了获得最大的编码性能,编码器客户端应该创建一个单独的线程来等待事件或在对编码器接口进行任何阻塞调用时。
客户端应避免从主编码器处理线程进行任何阻塞调用。主编码器线程应仅用于编码器初始化并以非阻塞方式使用NvEncEncodePicture将工作提交给硬件编码器。
输出缓冲区处理,例如在异步模式下等待完成事件,或在同步模式下调用阻塞 APINvEncLockBitstream/NvEncUnlockBitstream,这些应该在辅助线程中完成。这确保了主编码器线程永远不会被阻塞,除非编码器客户端耗尽资源。
还建议分配许多输入和输出缓冲区,以避免资源危害并提高整体编码器吞吐量。

使用 CUDA 的编码器功能

先进的功能和设置

向前预测

向前预测是通过编码器能够缓冲指定数量的帧、估计它们的复杂性并在与它们的复杂性成比例的这些帧之间适当地分配比特,来提高视频编码器的速率控制精度。这也会动态分配 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. 默认情况下,向前预测启用I帧和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查询功能的可用性,并在返回值中检查 NV_ENC_CAPS_SUPPORT_BFRAME_REF_MODE
  • 在编码器初始化期间,设置NV_ENC_CONFIG_H264::useBFramesAsRef = NV_ENC_BFRAME_REF_MODE_MIDDLE,这会将第 (N/2) 个B帧设置为参考,其中N = B帧的数量。如果 N 为奇数,则将选取 (N-1)/2帧作为参考。

重新配置 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
  • 改变位深。

如果尝试重新配置不受支持的参数,API 将失败。
只有在创建编码器会话时设置了NV_ENC_INITIALIZE_PARAMS::maxEncodeWidthNV_ENC_INITIALIZE_PARAMS::maxEncodeHeight的情况下才能更改分辨率 。
如果客户端希望使用此 API更改分辨率,建议通过设置NV_ENC_RECONFIGURE_PARAMS::forceIDR为1,来将重新配置后的下一帧强制为 IDR 帧 。
如果客户端希望重置内部速率控制状态,请设置NV_ENC_RECONFIGURE_PARAMS::resetEncoder到 1。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,793评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,567评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,342评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,825评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,814评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,680评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,033评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,687评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,175评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,668评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,775评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,419评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,020评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,978评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,206评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,092评论 2 351
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,510评论 2 343

推荐阅读更多精彩内容