H264编码和解码的问题——b intra refresh

解决了一个问题,留在这也算做个笔记。

是在做流媒体过程中,有时出现无法解码的问题。

具体的,编码器使用x264,推流到服务器上,再使用播放器播放流媒体服务器的视频流。当推一个视频文件时,可以从流媒体服务器播放,但是推一个摄像头的视频流时,怎么样也无法播放。使用opencv的VideoCapture获取待编码发送的视频帧。

把收到的流存成.264文件,直接使用播放器播放这个收到的文件,最奇怪的事就在这里,vlc不能播放,但是potplayer可以正常播放。而测试的RTSP播放器使用openh264的解码器,也不能播放。

然后,使用ffprobe统计一下,非常有用!

大概这样:

ffprobe.exe -show_frames received.264 > receivied.txt

就会得到一个每个视频帧的描述的文本文件,找pict_type项,或者,干脆把pict_type项全部取出来,可以直接使用ffprobe,可以选择需要输出的项,是更好的,但是这里直接在msys2(cygwin应该也可以)里的grep:

./ffprobe.exe -show_frames received.264 | grep pict_type > reveived_picttype.txt

发现全部都是P帧,一个I帧都没有,看来是编码那边根本没有吐出I帧!再搜索一下,看看x264的编码选项,有一个非常可疑:

b_intra_refresh,在x264.h里有注释:Whether or not to use periodic intra refresh instead of IDR frames.

设置成了1。然后再搜,找到了相关说明:

https://en.wikipedia.org/wiki/X264

http://www.chaneru.com/Roku/HLS/X264_Settings.htm#intra-refresh

大概说下结果,就是这个选项开启的话,会使用intra refresh技术,就是把I帧分配到每个P帧(结合sei?)里,一条一条发P帧,最后组合成一个I帧。这样的好处是,可以更轻易地保证每个独立的帧的大小都不超过TCP或UDP包的最大大小。如果使用I帧+P帧,I帧一般是不能放在一个UDP里发送的,需要分片。使用这个intra refresh,生成的每个帧的大小都是相差不大的,比如可能会有这种连续的包的大小:

b_intra_refresh=1:

11k 11k 11k 11k  11k 11k 11k 11k  ......

b_intra_refresh=0:

32k  4k    4k   4k   32k  4k   4k    4k   ......

并且看这里:This benefits low-latency streaming by making it possible to achieve more constant frame sizes than is possible with standard IDR-frames. It also increases the resilience of the video stream to packet loss,很有道理,就是说,这样,一是实现了更“常数”的帧大小,二是对丢包更鲁棒。后者也很有道理,如果丢了一个I帧的任何一个分片,后面2秒都是花的;而如果只丢了一个intra帧,相当于只有一小条I帧被丢了。


回到最初遇到的问题,可以放文件,因为那个文件是个动画片,场景切换较多,应该确实收到了I帧,也许是场景切换(就是背景发生大的变化)会导致一个I帧的发送,即使设置了Intra refresh。而不能放监控的视频,因为监控视频的背景几乎好似永远不变的,就一直没有I帧过来,于是无法播放。

所以我想可以得到的收获像这样:

1 如果可以保证只是自己的产品使用,并且不想对帧分片,且需要对丢包不那么敏感,设置这个选项为True。当然要使用可以解这个的解码器。

2 如果要和其他产品交互,比如要求使用系统解码器,或者不同的播放器,老老实实设为False,不然没几个人播放得了。

3 vlc、potplayer、ffprobe都要学会灵活使用。

4 编码参数什么的,直接抄代码是可以,但是也要留个心眼,出了问题要想到这里。

你可能感兴趣的:(H264编码和解码的问题——b intra refresh)