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萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章