就像我在问题中所说,MPlayer 无法播放我的 UDP 流有两个(实际上是三个)原因。
第一个原因与打包有关。 NVEnc 用称为 NALU 的数据块填充其输出缓冲区,并用主要用于比特流同步的“起始码”分隔这些数据块。 (如果您想了解有关附件 B 及其竞争对手 AVCC 的更多信息,请转到 szatmary's 优秀 SO answer)。
现在的问题是 NVEnc 有时会在一个输出缓冲区中提供多个这样的 NALU。尽管大多数 NALU 包含编码的视频帧,但有时也有必要(并且在流的开头强制)发送一些元数据,例如分辨率、帧率等。NVEnc 也通过生成那些特殊的 NALU 来帮助解决这个问题(更多再往下看)。
事实证明,播放器软件不支持在单个 UDP 数据包中获取多个 NALU。这意味着您必须编写一个简单的循环来查找起始代码(两个或三个“0”字节后跟一个“1”字节)来分割输出缓冲区并在其自己的 UDP 数据包中发送每个 NALU。 (但请注意,UDP 数据包仍必须包含这些起始代码。)
打包的另一个问题是IP数据包通常不能超过一定的大小。同样,SO answer 提供了宝贵的洞察力,让您了解这些限制在各种情况下是什么。这里重要的是,虽然您不必自己处理这个问题,但您必须通过在创建编码器对象时设置以下参数来告诉 NVEnc “切片”其输出:
m_stEncodeConfig.encodeCodecConfig.h264Config.sliceMode = 1;
m_stEncodeConfig.encodeCodecConfig.h264Config.sliceModeData = 1500 - 28;
(m_stEncodeConfig 是传递给NvEncInitializeEncoder() 的参数结构,1500 是以太网数据包的 MTU,28 是添加的 IP4 标头和 UDP 标头的大小)。
MPlayer 无法播放我的流的第二个原因与流视频的性质有关,而不是存储在文件中。当播放器软件开始播放 H.264 文件时,它会找到包含分辨率、帧率等所需的元数据 NALU,存储该信息,因此不再需要它。而当被要求播放流时,它将错过该流的开头,并且在发送方重新发送元数据之前无法开始播放。
这就是问题所在:除非另有说明,否则 NVEnc 只会在编码会话开始时生成元数据 NALU。这里是需要设置的编码器配置参数:
m_stEncodeConfig.encodeCodecConfig.h264Config.repeatSPSPPS = 1;
这告诉 NVEnc 不时地重新生成 SPS/PPS NALU(我认为默认情况下,这意味着每个 IDR 帧)。
然后瞧!清除这些障碍后,您将能够欣赏生成压缩视频流的强大功能,同时几乎不会占用 CPU。
编辑:
我意识到这种超简单的 UDP 流是不鼓励的,因为它并不真正符合任何标准。 Mplayer 会播放这样的流,但 VLC,否则几乎可以播放任何东西,不会。最重要的原因是数据流中没有任何内容甚至指示正在发送的媒体类型(在本例中为视频)。我目前正在研究以找到满足公认标准的最简单方法。