解决GB28181对接RTSP倍速播放导致FFmpeg缓冲区满导致花屏问题
在GB28181协议对接RTSP流时,倍速播放会导致FFmpeg缓冲区溢出,进而造成花屏问题。这是由于倍速播放增加了数据流的处理速度,而FFmpeg的缓冲区未能及时处理这些增量数据,导致视频帧丢失或渲染错误。为解决此问题,可能需要优化数据缓存策略或调整流的处理速率,确保FFmpeg能够稳定地处理高吞吐量的视频流。
下面是我在网上找到的一些思路及方法:
大华、海康和华为等NVR录像机gb28181平台倍速播放原理 - 阿风小子 - 博客园
基本原理如下:
因为这些平台都不会牵扯到编解码,所以只能在编码后的数据进行处理,原始的编码数据来源于相机(大部分),所以对于安防,编码的源头在相机端,这是一个大致前提。
平台收到相机编码后的数据后,如果要进行倍速播放,那怎么发送数据呢?策略如下:
1、4倍以下,按照全帧率4倍码流发送数据,即1s发送100帧(假设25fps),注意这里对dts和pts不做任何修改。
2、4倍以上,只发送I帧数据。这里也不修改dits和pts.
我们收到数据后直接播放会出现什么问题呢?
1、如果我们收到的100fps,但是此时dts和pts却没有改变,播放器播放的时候会出现跳秒和花屏现象。为什么呢?
这是因为dts和pts没有修改,播放器还是按照正常dts和pts速率来解码播放,导致缓存长满,这时候播放器一般会丢包处理,丢包后刚好这一段数据中包含I帧,那么就会出现花屏现象。
2、如果按照只接受I帧来收数据,如果不修改dts和pts就会出现非常严重的卡顿,因为还是按照原来的dts和pts来解码播放。
所以这时候就必须修改dts和pts,同时将编码端的gop调整到25左右,不然,如果gop是50那么2s才会发一帧数据,假设8倍速率,也就是4帧每秒,这可不行,最好设置为gop25,那么8倍就是8帧,16倍就是16帧,凑合着可以看呢。
如果你要对接NVR28181平台的倍速播放功能,就是拉流后对dts和pts进行修改,因为只有P帧(假设相机编码没有B帧),dts=pts。
对于rtsp,如果让服务器发送倍速码流,增加scale头域就可以了。
为了解决在倍速播放RTSP流时,FFmpeg缓冲区满导致花屏的问题,我采用了重新计算时间戳的方法。倍速播放会加速视频帧的处理和播放速度,导致FFmpeg缓冲区在短时间内接收大量数据,超出了其处理能力,最终引发花屏现象。为了避免这一问题,我通过调整每一帧的时间戳,使其与倍速播放的速率相匹配。具体来说,在接收到每一帧数据时,重新计算该帧的显示时间,确保帧与帧之间的时间间隔按照倍速播放的设定进行压缩或扩展,从而实现流畅的播放效果。
此外,我还优化了缓冲区管理策略,在播放过程中适时清空已处理的帧,并控制缓冲区的最大填充量,避免过多的帧数据堆积造成溢出。这种方式有效避免了由于帧积压而导致的渲染问题,使得倍速播放时FFmpeg能够顺利地处理高吞吐量的数据流,减少了花屏和卡顿现象,确保了视频播放的稳定性和流畅性。
经测试,随着倍速提升,缓冲区打满的速度会更快,同样的通过ffplay来播放RTSP的倍速流,ffplay未进行丢包或计算时间戳,而是全部缓存,导致倍速播放并不生效,VLC是通过重新计算时间戳来达到倍速播放。
下面是代码:
//speed_multiplier:当前播放倍速
void adjust_timestamps(AVPacket *packet, double speed_multiplier) {
// 检查速度倍数是否合法
if (speed_multiplier <= 0) return;
// 修改 PTS 和 DTS
if (packet->pts != AV_NOPTS_VALUE) {
packet->pts = (int64_t)(packet->pts / speed_multiplier);
}
if (packet->dts != AV_NOPTS_VALUE) {
packet->dts = (int64_t)(packet->dts / speed_multiplier);
}
// 确保 PTS >= DTS
if (packet->pts < packet->dts) {
packet->pts = packet->dts;
}
}