不知大家有沒有發現FFmpeg長時間解碼會出現延時增大(特別是在丟包的情況下)?如果在播放本地文件,這個問題是沒有影響的。但是如果播放的是實時流,則圖像的延時就越來越大。本人是做安防監控的,很多招標項目對解碼器的圖像延時都有要求:不能高於250毫秒。所以,對實時性要求高的場合,要盡量降低圖像的延時。
網絡攝像頭從采集圖像到通過網絡傳輸到客戶端解碼的過程中都會產生延時,一般延時有幾部分組成:編碼延時、網絡傳輸延時、解碼延時。編碼延時是由編碼器產生的,只能由設備方去改善,如果是FFmpeg軟編碼的方式,也是需要優化的,優化方法見我的另外一篇博文怎么降低FFmpeg的編碼延時。網絡傳輸延時一般很小。解碼延時是產生延時的一個很重要的部分,所以我們要想辦法降低解碼時的延時。優化解碼延時的方法我歸納如下:
第一,減少視頻的緩沖幀數,盡量收到視頻就送去解碼。
第二,從網絡接收數據,解碼,還有顯示操作用多線程處理,從而提高並發性。一般地,我們做播放器會把數據接收+解碼放在一個線程處理,而顯示放在另外一個線程;或者數據接收用一個線程,而解碼+顯示放在另外一個線程。中間實現一個緩沖隊列,把第一個線程輸出的數據扔到緩沖區,而第二個線程從隊列里取數據,讀到一幀就馬上解碼或顯示。這樣可以保證數據包可以得到最快的速度的處理,不會因為網絡阻塞而影響后面的處理流程。
第三,清空FFmpeg編碼器的緩沖區。當前面兩種方法都無效時,這種方法可能就最管用了。在網絡環境中,傳輸數據用UDP經常有丟包,而丟包很容易造成FFmpeg解碼器緩沖的幀數增加。解決辦法是:隔一段時間清空解碼器緩存。偽代碼如下:
int got_picture = 0;
//重置解碼器,在解碼I幀之前清空解碼器緩存
//m_bReset為True,表示即將清空解碼器緩存
if(m_bReset && nFrameType ==1)
{
avcodec_flush_buffers(m_pVideoCodecCtx);
m_bReset = FALSE;
}
AVPacket avpkt;
av_init_packet(&avpkt);
avpkt.size = inLen;
avpkt.data = inbuf;
int len = avcodec_decode_video2(m_pVideoCodecCtx, picture, &got_picture, &avpkt);
if (len < 0)
{
TRACE("Error while decoding frame Len %d\n", inLen);
return FALSE;
}
不過要注意的是:不能任何一幀前都可以進行清空操作,理想是在I幀之前做清空,否則圖像會有不連續或馬賽克的現象。