正如我在问题中所说的那样,MPlayer无法播放我的UDP流有两个(实际上是三个)原因。
第一个原因与数据包处理有关。NVEnc使用称为NALU的数据块填充其输出缓冲区,并使用“起始码”分隔这些块,主要用于比特流同步。(如果您想了解有关Annex B及其竞争对手AVCC的更多信息,请查看szatmary的优秀SO答案)。
现在的问题是,NVEnc有时会在单个输出缓冲区中传递多个NALU。虽然大多数NALU包含编码的视频帧,但有时需要(并且在流的开始处是强制性的)发送一些元数据,例如分辨率、帧速率等。NVEnc通过生成这些特殊的NALU来帮助解决此问题(稍后将详细介绍)。
结果发现,播放器软件不支持在单个UDP数据包中获取多个NALU。这意味着您必须编写一个简单的循环,查找起始码(由两个或三个“0”字节跟随一个“1”字节组成),以切割输出缓冲区并将每个NALU发送到自己的UDP数据包中。(请注意,UDP数据包仍必须包括这些起始码。)
数据包处理的另一个问题是,IP数据包通常不能超过一定的大小。同样,SO答案提供了有关不同情境下这些限制的宝贵见解。重要的是,在您不必自己处理此问题的同时,您必须在创建编码器对象时设置以下参数,告诉NVEnc对其输出进行“切片”:
m_stEncodeConfig.encodeCodecConfig.h264Config.sliceMode = 1;
m_stEncodeConfig.encodeCodecConfig.h264Config.sliceModeData = 1500 - 28;
(使用m_stEncodeConfig
作为参数结构传递给NvEncInitializeEncoder()
,以太网数据包的MTU为1500,IP4头和UDP头大小之和为28)。
MPlayer无法播放我的流的第二个原因与视频流的性质有关,这与将其存储在文件中不同。当播放器软件开始播放H.264文件时,它会查找包含分辨率、帧速率等所需元数据NALU,并存储该信息,因此永远不需要再次使用。但是,当要求播放流时,播放器将错过该流的开头,直到发送者重新发送元数据之前无法开始播放。
问题在于:除非另有说明,否则NVEnc只会在编码会话的最开始生成元数据NALU。以下是需要设置的编码器配置参数:
m_stEncodeConfig.encodeCodecConfig.h264Config.repeatSPSPPS = 1;
这会告诉NVEnc定期重新生成SPS/PPS NAL单元(我认为默认情况下,这意味着每个IDR帧都会重新生成)。
大功告成!克服了这些障碍后,您将能够欣赏到在几乎不影响CPU的情况下生成压缩视频流的强大功能。
编辑:
我意识到这种超级简单的UDP流式传输是不被鼓励的,因为它实际上并没有符合任何标准。Mplayer可以播放这样的流,但VLC却不能,尽管它几乎可以播放任何东西。主要原因是数据流中甚至没有表明发送的介质类型(在本例中是视频)。我目前正在研究最简单的方法以满足公认的标准。
ffmpeg
内容时偶然发现了答案,并且在输出中得到了一堆“non-existing PPS 1 referenced”,而内容正在播放时这些输出会一直持续。当然,SO有答案,那是因为缺少SPSPPS。我确认包含repeatSPSPPS=1
的存档内容会减少这些问题,而且即使出现这些问题,也只有很少的情况,直到最后它变成一个,此时就会平息。 - LB2