ffmpeg在avformat_open_input里面已經實現了UDP的協議,所以只需要設置好參數,將url傳遞進去就可以了。
和打開文件的方式基本一樣:
01 |
AVCodecContext *pVideoCodecCtx = NULL; |
02 |
AVCodec *pVideoCodec = NULL; |
03 |
avcodec_register_all(); |
05 |
avformat_network_init(); |
06 |
if (m_pConfigManager == NULL) |
13 |
CString url = m_pConfigManager->GetURL(); |
14 |
int portNumber = m_pConfigManager->GetPortNumber(channel); |
15 |
url.Format(_T( "%s:%d" ), url, portNumber); |
17 |
m_pFormatContext[channel] = avformat_alloc_context(); |
18 |
int err = avformat_open_input(&(m_pFormatContext[channel]), url, NULL, NULL); |
21 |
fprintf (stderr, "Can't open %s!" , url); |
25 |
m_pFormatContext[channel]->flags |= AVFMT_FLAG_GENPTS; |
26 |
m_pFormatContext[channel]->flags |= AVFMT_GENERIC_INDEX; |
27 |
m_pFormatContext[channel]->max_index_size = 99; |
29 |
if (av_find_stream_info(m_pFormatContext[channel]) < 0) |
31 |
fprintf (stderr, "Can't find stream info \n" ); |
36 |
av_dump_format(m_pFormatContext[channel], 0, url, false ); |
之后就可以通過av_read_frame來獲取通過UPD組播協議發送來的packet,並發送至自己的一個隊列A進行緩存,再由DirectShow等視頻開發框架從該隊列A獲取數據,並進行解碼顯示。
與讀取文件不同,讀取UDP組播協議數據包必須不斷調用av_read_frame。這是因為ffmpeg在讀取UDP時會新建一個數據緩沖區B,數據通過UDP組播協議發送出來后,就被存儲到這個緩沖區B里;如果長時間不調用av_read_frame(例如在讀取文件時,如果自己建立的隊列A已滿,則不再調用av_read_frame,而是使本線程休眠若干毫秒,等待DirectShow等前端框架從隊列A取出數據包進行解碼),那么不斷到來的UDP數據包就會將緩沖區B寫爆,之后再調用av_read_frame就會返回-5,代表IO錯誤。
因為UDP組播一般是與實時源(Live Source)相關聯,因此過時的數據包是沒有用的,因此當隊列A寫滿時,采取從隊列A彈出若干個數據包,之后再進行av_read_frame操作。這樣的代價是在播放過程中會產生丟包(視頻中出現馬賽克、花屏),但是延時會控制在一個合理的范圍內;也可以直接丟棄av_read_frame得到的數據包,這樣視頻中的馬賽克較少,但是這樣會產生無法估計的延時。
另外,在同時讀取多路組播數據包時,也會遇到與緩沖區B相關的問題:av_find_stream_info會在視頻流中尋找並解析PPS、SPS,是非常耗時的。在做好第一路的初始化之后,就應該立即新建一個線程用於調用av_read_frame函數獲取數據(否則,第一路的緩沖區B會被寫爆);與此同時,第二路的初始化工作也將開始;由於此時有一個讀取第一路UDP數據包的線程來爭奪CPU時間,因此第二路的av_find_stream_info將會花費更多的時間,這將導致第二路的緩沖區B被寫爆。
上述問題的解決思路有兩個,第一個是同時初始化兩路UDP連接,但是在我的程序里由於有一些共享資源的限制,線程同步比較麻煩,所以后來放棄了;第二個是盡量減少av_find_stream_info的時間或者加大緩沖區B的大小。看了AVFormatContext的屬性,經過試驗發現,減小probesize和max_analyze_duration可以減少av_find_stream_info的時間,但是沒有找到可以增大緩沖區B的辦法。。。上面兩個屬性也不可以減少的太多,而要依據視頻源的SPS和PPS的特點來確定,否則會找不到stream_info,也就沒有分辨率等信息。
解決了緩沖區的問題后,我發現播放高清視頻(1080i,720p,1080p)時播放時丟包非常嚴重。查了半天,發現播放時隊列A幾乎是時時處於滿狀態,導致程序主動丟棄了很多數據包。再看CPU的狀態,幾乎是滿負荷運轉。輸出解碼和緩存的調試信息后,發現基本上解碼一幀,會緩存五幀……看來是CPU太慢了(四年前的機器,AMD Turion64×2)。換到旁邊同學的i3-2300(盒)上運行就非常流暢了……
看來我該換筆記本了。等28號HKUST出學生機計划,看看價錢靠不靠譜吧~謝謝陽總工提供的消息!
歡迎轉載,轉載請注明出處:http://guoyb.com/Tech/26.html