iOS h264直播遇到的問題

花屏問題

  • 丟失參考幀導(dǎo)致的

一般 H.264 碼流有 I、B、P 三種幀類型,I 幀是關(guān)鍵幀,B 幀是雙向預(yù)測內(nèi)插編碼幀,P 幀是前向預(yù)測編碼幀。

I 幀由于是幀內(nèi)壓縮,因此可以獨立解碼播放,而 B 幀,一旦丟失了 I 幀或者后面的 P 幀,則會解碼失敗,而 P 幀一旦丟失了前面的 I/B/P 幀,也會導(dǎo)致解碼失敗。

對于丟失了參考幀而導(dǎo)致的解碼失敗,一般就會出現(xiàn)花屏的現(xiàn)象,花屏的嚴重程度依賴于丟失的參考幀對即將解碼的幀的重要程度。

那么,什么情況下會丟失參考幀呢 ?

首先,推流/播放的代碼層面,需要注意,不要丟棄編碼后、解碼前的視頻幀數(shù)據(jù),不過實際場景中,遇到下面的情況,難免還是會產(chǎn)生丟幀:
網(wǎng)絡(luò)不好,編碼后的數(shù)據(jù)發(fā)不出去

系統(tǒng)低內(nèi)存,隊列里面無法承受更多的幀數(shù)據(jù)

因此,在這些極端的情況下,不得不丟幀的話,最合理的策略就應(yīng)該是一次丟一整個 GOP,即:一旦開始丟了一個 I 幀,那么在遇到下一個 I 幀之前的所有視頻幀,均丟棄掉,這樣即可有效避免播放器端產(chǎn)生解碼花屏。

  • 播放器沒有從關(guān)鍵幀開始解碼

原理依然如上面所述,如果不從關(guān)鍵幀開始解碼,則必然會由于丟失了參考信息而導(dǎo)致解碼花屏。

因此,播放器,無論是首播,還是斷網(wǎng)重連后,都應(yīng)該判斷第一幀視頻是否是關(guān)鍵幀,如果不是,則應(yīng)該等到第一個關(guān)鍵幀到達之后再送入解碼器。

基于 ffmpeg 的播放器,如何判斷關(guān)鍵幀,可以參考文章:《FFMPEG Tips (3) 如何讀取每一幀的信息》

  • 推流端圖像尺寸和格式處理不當

圖像的格式和尺寸,都是非常重要的參數(shù),一定要嚴格配置正確。

比如:如果采集到的視頻是 NV21 ,編碼器只支持 I420,那么編碼出來的圖像自然會出現(xiàn)顏色問題。

比如:在一些場景切換的過程中,前后攝像頭切換,視頻的尺寸可能發(fā)生了變化,但是剪裁、處理、編碼模塊沒有相應(yīng)的修改尺寸,那么,也會出現(xiàn)各種視頻錯亂的現(xiàn)象。

  • 如何判斷是音頻幀還是視頻幀

int video_stream_idx = av_find_best_stream(ic, AVMEDIA_TYPE_VIDEO, -1, -1, NULL, 0);

int audio_stream_idx = av_find_best_stream(ic, AVMEDIA_TYPE_AUDIO, -1, -1, NULL, 0);

每一個 AVPacket 都有一個成員變量:stream_index,由該成員變量即可判斷這個 Packet 到底是音頻還是視頻了:

if (avpkt.stream_index == video_stream_idx) {
    LOGD("read a video frame");
} else if (avpkt.stream_index == audio_stream_idx) {
    LOGD("read audio frame);
}
  • 如何判斷是否為關(guān)鍵幀

if (avpkt.flags & AV_PKT_FLAG_KEY) {
    LOGD("read a key frame");
}
  • 如何獲取幀的數(shù)據(jù)和大小

幀的數(shù)據(jù)和大小直接定義在 AVPacket 結(jié)構(gòu)體中,對應(yīng)的成員變量如下:

// 壓縮編碼的數(shù)據(jù),一幀音頻/視頻
uint8_t *data;

// 數(shù)據(jù)的大小
int size;
  • 如何獲取幀的時間戳信息

每一個幀都可能攜帶有 2 個時間戳信息,一個是解碼時間戳 dts,一個是顯示時間戳 pts,解碼時間戳告訴我們什么時候需要解碼,顯示時間戳告訴我們什么時候需要顯示,只有在碼流中存在 B 幀的情況下,這兩個時間戳才會不一致。

這些時間戳信息不一定存在于碼流中(取決于生產(chǎn)端),如果不存在,則其值為:AV_NOPTS_VALUE

一定要選擇正確地方式打印時間戳,時間戳是使用 long long 來表示的,即 int64_t,因此打印的時候,需要使用 “%lld” 來打印,例如:

while (!interrupt) {
    int ret = av_read_frame(player->ic, &avpkt);
    if (ret < 0) {
        break;
    }
    if (avpkt.stream_index == video_stream_idx) {
        LOGD("read video frame, timestamp = %lld \n”, avpkt.pts);
    } else if (avpkt.stream_index == audio_stream_idx) {
        LOGD("read audio frame, timestamp = %lld \n”, avpkt.pts);
    }
}

由此,我們就可以通過這些 log 信息調(diào)試一下某一段音視頻流的時間戳是否正確,比如是否出現(xiàn)了時間戳的回滾和錯亂,則必然會導(dǎo)致播放端出現(xiàn)音視頻不同步或者顯示異常等情況。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容