1. 程式人生 > >H264編碼和解碼的問題——b intra refresh

H264編碼和解碼的問題——b intra refresh

解決了一個問題,留在這也算做個筆記。

是在做流媒體過程中,有時出現無法解碼的問題。

具體的,編碼器使用x264,推流到伺服器上,再使用播放器播放流媒體伺服器的視訊流。當推一個視訊檔案時,可以從流媒體伺服器播放,但是推一個攝像頭的視訊流時,怎麼樣也無法播放。使用opencv的VideoCapture獲取待編碼傳送的視訊幀。

把收到的流存成.264檔案,直接使用播放器播放這個收到的檔案,最奇怪的事就在這裡,vlc不能播放,但是potplayer可以正常播放。而測試的RTSP播放器使用openh264的解碼器,也不能播放。

然後,使用ffprobe統計一下,非常有用!

大概這樣:

ffprobe.exe -show_frames received.264 > receivied.txt

就會得到一個每個視訊幀的描述的文字檔案,找pict_type項,或者,乾脆把pict_type項全部取出來,可以直接使用ffprobe,可以選擇需要輸出的項,是更好的,但是這裡直接在msys2(cygwin應該也可以)裡的grep:

./ffprobe.exe -show_frames received.264 | grep pict_type > reveived_picttype.txt

發現全部都是P幀,一個I幀都沒有,看來是編碼那邊根本沒有吐出I幀!再搜尋一下,看看x264的編碼選項,有一個非常可疑:

b_intra_refresh,在x264.h裡有註釋:Whether or not to use periodic intra refresh instead of IDR frames.

設定成了1。然後再搜,找到了相關說明:

https://en.wikipedia.org/wiki/X264

http://www.chaneru.com/Roku/HLS/X264_Settings.htm#intra-refresh

大概說下結果,就是這個選項開啟的話,會使用intra refresh技術,就是把I幀分配到每個P幀(結合sei?)裡,一條一條發P幀,最後組合成一個I幀。這樣的好處是,可以更輕易地保證每個獨立的幀的大小都不超過TCP或UDP包的最大大小。如果使用I幀+P幀,I幀一般是不能放在一個UDP裡傳送的,需要分片。使用這個intra refresh,生成的每個幀的大小都是相差不大的,比如可能會有這種連續的包的大小:

b_intra_refresh=1:

11k 11k 11k 11k  11k 11k 11k 11k  ......

b_intra_refresh=0:

32k  4k    4k   4k   32k  4k   4k    4k   ......

並且看這裡:This benefits low-latency streaming by making it possible to achieve more constant frame sizes than is possible with standard IDR-frames. It also increases the resilience of the video stream to packet loss,很有道理,就是說,這樣,一是實現了更“常數”的幀大小,二是對丟包更魯棒。後者也很有道理,如果丟了一個I幀的任何一個分片,後面2秒都是花的;而如果只丟了一個intra幀,相當於只有一小條I幀被丟了。

回到最初遇到的問題,可以放檔案,因為那個檔案是個動畫片,場景切換較多,應該確實收到了I幀,也許是場景切換(就是背景發生大的變化)會導致一個I幀的傳送,即使設定了Intra refresh。而不能放監控的視訊,因為監控視訊的背景幾乎好似永遠不變的,就一直沒有I幀過來,於是無法播放。

所以我想可以得到的收穫像這樣:

1 如果可以保證只是自己的產品使用,並且不想對幀分片,且需要對丟包不那麼敏感,設定這個選項為True。當然要使用可以解這個的解碼器。

2 如果要和其他產品互動,比如要求使用系統解碼器,或者不同的播放器,老老實實設為False,不然沒幾個人播放得了。

3 vlc、potplayer、ffprobe都要學會靈活使用。

4 編碼引數什麼的,直接抄程式碼是可以,但是也要留個心眼,出了問題要想到這裡。