如何使用NVidia NVEnc硬件编码器通过UDP传输H.264视频?

时间:2015-10-17 11:13:43

标签: video h.264 nvidia video-encoding nvenc

这将是一个自我回答的问题,因为它在整整一个星期的过程中让我疯狂,并且我希望让程序员们免于我经历的挫折。

情况如下:您希望使用NVidia的NVEnc硬件编码器(分别在Kepler和Maxwell卡上提供,分别为GT(x)7xx和GT(x)9xx)来流式传输图形输出通过UDP申请。这不是一条琐碎的道路,但它可以非常有效,因为它可以避免下载"下载"从视频内存到系统内存的帧直到编码阶段之后,因为NVEnc能够直接访问视频内存。

我已经设法通过简单地逐帧编写NVEnc的输出缓冲区来生成.h264 文件。 VLC在播放这样的文件时没有遇到任何问题,除了时间关闭(我没有尝试解决这个问题,因为我只需要该文件进行调试)。

当我尝试通过UDP流式传输编码帧时出现问题:VLC和MPlayer都无法呈现视频。事实证明,有两个原因,我将在答案中解释。

1 个答案:

答案 0 :(得分:10)

就像我在问题中所说的那样,有两个(好的,实际上是三个)MPlayer无法播放我的UDP流的原因。

第一个原因与打包有关。 NVEnc用称为NALU的数据块填充其输出缓冲区,它用“起始码”分隔,主要用于比特流同步。 (如果您想了解有关附件B及其竞争对手AVCC的更多信息,请转至szatmary's优秀SO answer

现在的问题是NVEnc有时会在单个输出缓冲区中提供多个这样的NALU。虽然大多数NALU包含编码视频帧,但有时也需要(并且在流的开头必须)发送一些元数据,如分辨率,帧速率等.NVEnc也通过生成那些特殊的NALU来帮助实现这一点(更多)在那更进一步下来。)

事实证明,播放器软件不支持在单个UDP数据包中获得多个NALU。这意味着您必须编写一个简单的循环来查找起始代码(两个或三个“0”字节后跟一个“1”字节)来切断输出缓冲区并将每个NALU发送到自己的UDP数据包中。 (但请注意,UDP数据包仍必须包含那些起始代码。)

打包的另一个问题是IP数据包通常不能超过一定的大小。同样,SO answer提供了有关各种情况下这些限制的宝贵见解。这里重要的是,虽然您不必自己处理,但您必须通过在创建编码器对象时设置以下参数来告诉NVEnc“切片”其输出:

m_stEncodeConfig.encodeCodecConfig.h264Config.sliceMode = 1;
m_stEncodeConfig.encodeCodecConfig.h264Config.sliceModeData = 1500 - 28;

m_stEncodeConfig是将传递给NvEncInitializeEncoder()的参数struct,1500是以太网数据包的MTU,28是IP4头和UDP头的增加大小。)< / p>

MPlayer无法播放我的视频流的第二个原因与流式视频的性质有关,而不是将其存储在文件中。当播放器软件开始播放H.264文件时,它将找到包含分辨率,帧速率等所需的元数据NALU,存储该信息,因此再也不需要它。当被要求播放流时,它将错过该流的开头,并且在发件人重新发送元数据之前无法开始播放。

这就是问题所在:除非另有说明,否则NVEnc只会在编码会话开始时生成元数据NALU。以下是需要设置的编码器配置参数:

m_stEncodeConfig.encodeCodecConfig.h264Config.repeatSPSPPS = 1;

这告诉NVEnc不时重新生成SPS / PPS NALU(我认为默认情况下,这意味着每个IDR帧)。

瞧!通过清除这些障碍,您将能够体会到生成压缩视频流的能力,同时几乎不会对CPU造成负担。

编辑: 我意识到这种超简单的UDP流是不鼓励的,因为它并不真正符合任何标准。 Mplayer将播放这样的流,但VLC,否则几乎可以播放任何东西,不会。最重要的原因是数据流中没有任何内容甚至表示正在发送的媒体类型(在本例中为视频)。我目前正在进行研究,以找到满足公认标准的最简单方法。