FFmpeg 直播黑屏問題分析解決

背景

FFmpeg是非常優秀的開源框架,在使用其進行二次開發及適配的過程中,難免會遇到各種各樣的問題。
這次記錄的是基於FFmpeg開發的播放器中,直播黑屏問題分析思路及解決方法。本文不涉及詳細的代碼流程,主要是提供分析思路。
問題如下:
在直播節目的使用過程中,某些播放源起播後畫面黑屏,但卻有聲音,直播節目基於HLS。

分析思路

對於這類問題的分析,需要獲取網絡包和對應的日誌,才能更好地定位問題原因。
分析步驟如下:
1、確認流的格式,確保流本身正常,視頻編碼方式可以支持。
可以在代碼中dump流,或者直接從網絡包中過濾去響應的媒體流,我更傾向於第二種。
流可以用PC端的VLC播放,可以明確流本身並無問題。同時查看編解碼信息,確認這種視頻格式我們是支持的(工具->編解碼器信息,截圖只是示例,不是出問題的流)
在這裏插入圖片描述
2、查看FFmpeg是否解析出正確的音視頻格式。
我們可以在av_find_stream_info函數之後,使用av_dump_format函數將音視頻信息dump出來,來確認FFmpeg是否解析正確。
可以看到,音頻(PID[0xb3]),其相應信息已經解析出來Audio: mp2, 48000 Hz, stereo, s16, 192 kb/s
而另一個數據(PID[0xb2]),則具體信息並不清楚。

03-28 09:17:55.073 I/ffmpeg( 3431):   Duration: 
03-28 09:17:55.073 I/ffmpeg( 3431): N/A
03-28 09:17:55.073 I/ffmpeg( 3431): , start: 
03-28 09:17:55.073 I/ffmpeg( 3431): 75309.284411
03-28 09:17:55.073 I/ffmpeg( 3431): , bitrate: 
03-28 09:17:55.073 I/ffmpeg( 3431): N/A
03-28 09:17:55.073 I/ffmpeg( 3431): 
03-28 09:17:55.073 I/ffmpeg( 3431):   Program 5 
03-28 09:17:55.073 I/ffmpeg( 3431):     Metadata:
03-28 09:17:55.073 I/ffmpeg( 3431):       service_name    : CCTV2 HD
03-28 09:17:55.073 I/ffmpeg( 3431):   No Program
03-28 09:17:55.083 I/ffmpeg( 3431):     Stream #0.0
03-28 09:17:55.083 I/ffmpeg( 3431): [0xb2]
03-28 09:17:55.083 I/ffmpeg( 3431): : Data: [0][0][0][0] / 0x0000
03-28 09:17:55.083 I/ffmpeg( 3431): 
03-28 09:17:55.083 I/ffmpeg( 3431):     Stream #0.1
03-28 09:17:55.083 I/ffmpeg( 3431): [0xb3]
03-28 09:17:55.083 I/ffmpeg( 3431): : Audio: mp2, 48000 Hz, stereo, s16, 192 kb/s
03-28 09:17:55.083 I/ffmpeg( 3431): 

3、使用TS解析工具解析並對比正常播放的流,可以看到正常播放時dump信息中,video信息已經解析出來,TS解析工具中,PID[0xb2]也即是視頻PID。所以基本可以確定是FFmpeg沒有正確解析PMT表。

03-28 09:18:51.983 I/ffmpeg( 3431): N/A
03-28 09:18:51.983 I/ffmpeg( 3431): , start: 
03-28 09:18:51.983 I/ffmpeg( 3431): 75369.524411
03-28 09:18:51.983 I/ffmpeg( 3431): , bitrate: 
03-28 09:18:51.983 I/ffmpeg( 3431): N/A
03-28 09:18:51.983 I/ffmpeg( 3431): 
03-28 09:18:51.983 I/ffmpeg( 3431):   Program 5 
03-28 09:18:51.983 I/ffmpeg( 3431):     Stream #0.0
03-28 09:18:51.983 I/ffmpeg( 3431): [0xb2]
03-28 09:18:51.983 I/ffmpeg( 3431): : Video: h264 (High), 1920x1088
03-28 09:18:51.983 I/ffmpeg( 3431): , 90k tbr
03-28 09:18:51.983 I/ffmpeg( 3431): , 90k tbn
03-28 09:18:51.983 I/ffmpeg( 3431): 
03-28 09:18:51.983 I/ffmpeg( 3431):     Stream #0.1
03-28 09:18:51.983 I/ffmpeg( 3431): [0xb3]
03-28 09:18:51.983 I/ffmpeg( 3431): (eng)
03-28 09:18:51.983 I/ffmpeg( 3431): : Audio: mp2, 48000 Hz, stereo, s16, 192 kb/s

4、結合代碼分析。
對於TS流PAT/PMT表的解析,也即是demux的流程了,該流程在avformat_open_input中。
查看mpegts對應的demux模塊。對於avformat_open_input流程,一般是調用read_head函數,即mpegts_read_header。

AVInputFormat ff_mpegts_demuxer = {
    "mpegts",
    NULL_IF_CONFIG_SMALL("MPEG-2 transport stream format"),
    sizeof(MpegTSContext),
    mpegts_probe,
    mpegts_read_header,
    mpegts_read_packet,
    mpegts_read_close,
    .read_seek = mpegts_read_seek,
    .read_timestamp = mpegts_get_dts,
    .flags = AVFMT_SHOW_IDS|AVFMT_TS_DISCONT| AVFMT_GENERIC_INDEX,
};

可以看到,爲了加快起播速度,probe_size 設置的較小(256kB),handle_packets函數會根據設置的probe_size,讀取一定數量的ts包進行解析,這就造成一種情況,可能讀取不到PAT表。

static int mpegts_read_header(AVFormatContext *s,
                              AVFormatParameters *ap)
{
   ....省略
            //find PMT, would stop parse
			 probe_size =256 * 1024;//fast play
			 handle_packets(ts, probe_size / ts->raw_packet_size);
   ....省略
        }

5、使用EasyICE分析TS切片。
256kB對應的偏移量爲0x40000,拿m3u8中第一個下載的分片(注意是第一個下載的分片,不是列表中的第一個,因爲不一定從頭開始下載)。
使用EasyICE分析,可以看到,PAT表偏移爲0x6C3A8,大於0x40000,所以demux階段,並沒有獲取到PAT/PMT表,所以無法解析出正確的視頻格式。
而音頻爲什麼能解析出來呢,這是因爲av_find_stream_info還有彈窗流程,可以解析出音頻格式,但視頻在這種情況下無法完整解析出來。
在這裏插入圖片描述
總結:該問題在於FFmpeg沒有讀取到PAT/PMT,導致解析錯誤。原因可能是直播流比較大,這種情況下,媒體數據較大,PSI/SI信息在TS流中佔比可能較小,讀取太小的數據就可能出錯。

解決思路

解析該問題的方法有兩種:
1、從客戶端解決,即加大demux階段解析的數據大小,但其實無法保證在流更大或者PSI/SI分佈太稀疏的情況下也正常,且網絡較差的情況下,起播速度會有一定影響。
2、從CDN解決,媒體服務器在對於HLS協議的直播流,可以在每個分片的最開始插入PSI/SI信息,這樣可以保證客戶端一定能正確獲取。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章