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信息,这样可以保证客户端一定能正确获取。