记录一次组播花屏问题的查找

新开发的组播播放器,发现在视频源运动剧烈的时候会出现比较严重的花屏。开始以为是组播丢包导致的,但打印了丢包检测的日志,发现并没有发生丢包的时候也会出现花屏。开始怀疑是视频源发送的问题,于是在接收端把接收到的H.264数据写成文件,再用Elecard StreamEye显示码流图像。发现StreamEye里面图像也是花的,于是结合wireshark抓包继续分析接收到的组播数据。结果在wireshark中看到,局域网内有其他设备也会偶尔性的向组播组发送数据,导致RTP聚包解析出错,于是根据抓包的结果,把其他发送组播的设备给关闭掉。

但是关闭后,依然有很严重的花屏现象。这时候开始检查搭建的环境是否有问题,浪费了一些时间,结果发现其他环境上面也有花屏的问题。正当完全没有头绪的时候,无意中录了一段只有30几帧的短码流。这段短码流,用ffplay播放也是花屏的,但是用eseye_u.exe播放是正常的!但奇怪的是,ffplay播放的时候并没有出现什么报错的信息,但是解出来的码流就是花的。用StreamEye的SAnalyzer.exe分析录下来的码流,发现每一帧都是由多个NAL组成的。IDR帧由2个NAL组成,P帧由4个NAL组成。难道说ffmpeg的h.264解码器有bug?于是尝试更新ffmpeg版本,结果发现升级到4.2.1,解码依然是花的。又尝试给ffplay更换解码器,用-vcodec libopenh264选项更换成openh264的解码器。由于视频源是high profile的,之前看到网上说openh264只支持baseline profile,本来估计会解码失败。结果解码成功了,而且并没有花屏!


PS:这时候,去翻了一下openh264的官方更新说明。早在2015发布的1.5.0版本,就有说明

  • Decoder support of 'Constrained High Profile' of H.264

Note:
'Constrained High Profile' = 'Constrained Baseline Profile' plus:

CABAC
Intra 8x8 mode support
8x8 transform
QP scaling matrices
QP per chroma component
Mono 4:0:0 (experimental)
Weighted prediction

由于我们用的视频流是实时直播的,也没有B帧,所以是符合'Constrained High Profile'的。而B帧的解码,是2019年发布的2.0.0版本才支持的,不过我还没有测试过

  • B-frame decoding support for Main and High Profile with two test cases

回到正题,这时候,只需要把解码器换成libopenh264,花屏的问题就可以解决了。但是总是有不甘心,没道理ffmpeg自带h264解码器,会有这么严重的bug。回去看了下ffmpeg代码,h264解码器中AVOption也没什么会导致花屏的参数。后面又录了几次组播接收到的码流,终于有一次ffmpeg解码报错了

[h264 @ 00929f40] reference picture missing during reorder
[h264 @ 00929f40] Missing reference picture, default is 2
[h264 @ 009902c0] reference picture missing during reorder
[h264 @ 009902c0] Missing reference picture, default is 65546

本身这一类报错信息在解码丢包的视频流中会经常出现。但根据这些报错信息,在stackoverflow中搜到一篇问答
https://stackoverflow.com/questions/27870545/decode-h264-video-using-libavcodec-c

Every input packet (avpkt) for avcodec_decode_video2 should contain full (and only) data for one frame i.e. it shouldn't be truncated in the middle of the frame NALs.

这句话一言惊醒了梦中人。于是回头看,eseye_u.exe中每一帧的长度是属于该帧的所有NAL长度总和,而我送给avcodec的avpkt.size总是小于这个总长度。想起来之前在SAnalyzer.exe中看到一帧是分成多个nal的,bug产生的原因已经很清楚了,就是没有把完整的一帧送给解码器,而是一个一个nal发送的。

你可能感兴趣的:(记录一次组播花屏问题的查找)