ffmpeg调用avformat_open_input打开rtmp转发流阻塞

 

G28181协议接入时,为了减少视频处理服务的改动,故采用了rtmp转发的方案。使用nginx搭建rtmp转发服务器,设备代理将流推送给nginx,nginx再将流转发给视频处理服务。但设备代理并不是每次都能够成功推流,如果失败,视频处理服务使用ffmpeg的avformat_open_input会阻塞住,导致整个服务卡死。各种超时设置均无效。经过调试发现avformat_open_input卡在了进行流侦测的时候,具体的代码如下:

本段代码位于rtmpproto.c的rtmp_open函数。代码大体意思就是收不到音频数据,视频数据或元数据,就一直接收。调试时发现,第一调用get_packet时返回0,之后再调用get_packet再无返回。期初使用简单粗暴的处理方式,即如果ret==0,也执行goto fail。但一运行程序变当掉了,原因很简单,ret=0被认为调用成功,其他函数便会进行数据拷贝,地址无效,程序直接就挂了。

不知为什么,ffmpeg在此处没有做超时处理,深层次的代码也为做任何超时处理。通过strace进行调用跟踪,发现底层采用异步IO,读取超时,但该错误没有抛上来。

突发奇想,启动另一个线程调用avformat_open_input,待取流超时后,杀掉线程。但事实证明,这是一个天真的想法,因为pthread_cancel不是一个靠的住的函数。虽然知道了问题所在,但却束手无策。但最终还是在网上找到了解决方案。

该方案具体如下:创建AVFormatContext成功后,设置中断回调(数据成员interrupt_callback.opaque(自定义指针),和interrupt_callback.callback(回调函数)),设置一个全局变量(类成员变量)last_packet_timestamp,调用avformat_open_input前初始化为time(nullptr),在回调函数里计算当前时间和last_packet_timestame的差值,当差值超过某一阈值时,比如说10s时,则认为取流超时,返回一个小于0的值,告诉调用者取流失败,其他情况下返回0。

但是用该方案一定要注意,成功取到流,每次调用av_read_frame成功后,一定要更新last_packet_timestamp。否则,av_read_frame还会超值,因为设置的中断回调会被循环调用,如果last_packet_timestamp不更新,很快就会超时。

发布了27 篇原创文章 · 获赞 11 · 访问量 5万+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章