webrtc拉流花屏
srs4.0
支持rtmp
与webrtc
之间的互相转化. 使用obs推流到srs上,然后通过其官方带的demo页面播放,效果还不错,但是播放一会视频就花屏了.
一开始我猜想应该是srs
实现上的问题. 后面就放下了一段时间, 直到最近,测试某云服务的webrtc
直播功能.
现在的各个云厂家直播服务基本都支持了webrtc sfu
功能,且支持rtmp
推流,webrtc
观看,
rtmp
作为传统的直播推流方式,各种硬件编码器,导播工具都广泛支持,依然是主流的推流方式,rtmp
协议简单,基于tcp, 在这种推流方式下,
使用webrtc
观看,一般是对视频编码有一定要求,比如无B帧的H264编码,这样可以不再额外做视频转码,因为视频转码消耗太大.音频由于两种协议编码格式不兼容,
一般都是要进行转码,好在音频转码消耗远小于视频,一般云厂商都会内置. 这样的直播方式,使用webrtc
观看虽然无法发挥出动态码率的作用,但是webrtc
观看的超低延时,
仍然让很多直播产品都争相加入.
接着说我在某些云厂商的webrtc
观看功能看到了什么,那就是和之前srs
上看到的一样的问题. 场景还是obs推流,webrtc
观看功能,然后看到了花屏.
云厂商们开发的SFU是如何实现的,我不得而知,但是不太可能都是基于srs
实现的,毕竟srs
这个功能也没有出来很久. 这就不得不让人好奇了,因为webrtc
解码前是有完整帧判断的,
对h264来说,只要中间出现丢帧,之后属于同一GoP
的帧都将会被丢弃, 这样是不会出现花屏的.
花屏分析
首先排除上行的问题,因为使用rtmp
拉流方式同一时间的码流画面是正常的,只有webrtc
拉流会偶尔出现花屏.
再从下行链路分析,先抓个包看看, 这里payload 125
为h264
的RTP流:
从抓包上看,丢包乱序现象比较严重,我一查线路,本地是联通网,部署srs的机器是电信网, 这个应该是丢包乱序的原因, 花屏也应该和丢包乱序有关.
我将本地网络也换成了电信网,再次测试抓包,基本没有任何丢包乱序,花屏也消失了,这也证实了上面的猜测.
乱序和丢包固然可能造成webrtc
观看卡顿, 但是不应该会花屏,进一步分析需要更详细的信息. webrtc
在chrome中是强制加密的,走DTLS
协议, srs
有个功能是关闭webrtc
加密,
关闭加密可以在sdp中协商,但是只有chromium
或者chrome canary
版支持. 关闭加密后wireshark能分析出更多的信息,常用在调试中.
srs
关闭加密功能在播放url参数中增加
|
|
启动chrome打开详细日志:
|
|
再次复现花屏现象,查看日志,发现一些warning
日志:
|
|
日志里有dropping frame
, 确实存在丢帧现象, 还有个警告Treating as key frame since WebRTC-SpsPpsIdrIsH264Keyframe is disabled
.
WebRTC-SpsPpsIdrIsH264Keyframe
是chrome的一个field trial
, 默认为false. 这个选项的意思是只有sps pps idr
在一起,才会被当作关键帧. webrtc
中关键帧判断会影响参考关系,
进而影响完整帧和丢帧逻辑. 一般来说chrome端webrtc的rtp封装h264都会在关键帧前插入sps pps
.
再来看去除加密的抓包信息,可以分析出h264格式,需要在wireshark中Telephony
的RTP
选项中指定一下payload
.
看到wireshark
解密后的数据流,对应花屏日志,应该能看出端倪了. 抓包里红色部分应该是对应日志警告Treating...
的内容. 可以看到sequence 58784
这个包前面丢包很严重,
它的解析信息如下:
这个RTP
包是个single nal
的封装, nal type
为5,并且有marker
标记,h264封装中表示是完整一帧的结束包. 那么这是一个h264关键帧了? 这个结论不完全正确,仔细看它的前面,
这个包前面有很多IDR-Slice
,但是都没有marker
标记,原因是因为该h264
码流是多slice编码的,一帧是由多个slice组成,所以sequence 58784
虽然是single nal
封装,
并且有marker
标记,但它只是关键帧中的最后一个slice,它无法完整代表关键帧, 它的first_mb_in_slice
字段也能表明这不是一帧的开始的slice.
分析到这里,大概的原因也就清晰了,这里chrome webrtc
把这一个slice当成了keyframe
,而实际它并不完整,因为前面有大量丢包,一旦chrome
将它确定为关键帧,那么后续的完整P帧也将
会因为参考关系完整而被送入解码器,这一帧本身也会被送入解码,这样就会造成花屏了.
这恐怕也是chrome webrtc
中WebRTC-SpsPpsIdrIsH264Keyframe
选项出现的原因. 如果这个选项enable
, 那么只有挨着sps pps
的那个idr slice
, 并且直到marker
标记的RTP
包,
才算做一个完整的keyframe
,这样就不会影响解码前的完整帧和参考关系判断,也就不会出现花屏了.
规避
知道花屏的原因了,可以通过chrome增加上述参数验证:
|
|
加上这个参数启动后再次测试,果然不再出现花屏现象了, 但是乱序丢包依然还是会造成卡顿的.
当然chrome的参数并不由直播发起方控制,要想在直播源处避免这个问题,那么最好禁用多slice编码.
obs使用x264编码,可以通过设置threads=1
参数禁止多slice. 这样推出去的h264码流每帧都是单slice的.