音频缺失录制分析

  • 2020 年 1 月 20 日
  • 笔记

实验框架:

实验框架

RTMP Reader和Muxing各自包含音视频的AVCodecContext,共四个AVCodecContext

背景:

用户实际推流过程中,存在推流无音频数据的异常场景,导致录制RTMP Reader无法正确初始化音频的AVCodecContext,进而影响录制Muxing音频AVCodecContext初始化,最终导致录制文件出现静音等问题。

正常的音频推流过程是:AAC Sequence header | AAC data | AAC data | …… | AAC data

场景1:视频包正常推送,音频包则只推送AAC Sequence header,对应推流端代码是

1.正常初始化推流端RTMP Reader的音视频AVCodecContext

2.正常初始化推流端Muxing的音视频AVCodecContext

3.调用avformat_write_header

4. RTMP Reader读取音频视频包,Muxing写视频包,丢弃音频包

抓包如下:

抓包

红框中包含on mata data和视频的sps/pps以及音频的AAC Sequence header

结果:

录制RTMP Reader堵塞于avformat_find_stream_info直至超时返回,此时RTMP Reader的音视频AVCodecContext均已存在,但是音频AVCodecContext并未正确初始化,音频AVCodecContext如下:

AVCodecContext

红框的重要信息中只有bit_rate存在了,其他全未被初始化,用该音频AVCodecContext初始化Muxing的音频AVCodecContext时,ffmpeg会报错:

ffmpeg报错

此时若忽略音频的AVCodecContext,可以正常录制静音文件,这样做存在的问题是若后续推送了正常的音频数据,也会被录制端忽略。

场景2:视频包正常推送,音频包完全不推送,对应推流端代码是

1.正常初始化推流端RTMP Reader的音视频AVCodecContext

2.正常初始化推流端Muxing的视频AVCodecContext,初始化音频AVCodecContext为0,不打开音频stream

3.调用avformat_write_header

4. RTMP Reader读取音频视频包,Muxing写视频包,丢弃音频包

抓包如下:

抓包

红框看到只有on mata data和video的sps/pps,没有audio的AAC Sequence header

结果:

录制RTMP Reader堵塞于avformat_find_stream_info直至超时返回,此时RTMP Reader的视频AVCodecContext已生成并初始化,而音频AVCodecContext指针则为0,忽略音频录制则静音录制。缺点同场景1。

场景3:视频包正常推送,音频包以及aac sequence header均延迟推送,该场景需要修改rtmp server的代码实现,对应的代码实现是

1.推流端初始化时推送aac sequence header,音频数据则延迟推送

2.rtmp server接收到aac sequence header先进行保存,等到第一个音频数据包达到再一起推送给录制模块,实现均延迟的效果

录制中途,日志有(ffmpeg发现了上行音频stream):

结论同场景2.

场景4:视频包正常推送,音频包只发送数据,不发送aac sequence header,代码实现:

1.rtmp server接收到aac sequence header直接丢弃,只发送后续音频数据包

该场景实际是会影响音频AVCodecContext的extradata的初始化,该场景中,录制代码对录制hls和非hls有不同的做法,录制hls时,放弃录制音频,其他格式则依旧使用无extradata的AVCodecContext录制。

本实验也对该场景做了详细实验:

1.录制flv/mp4时,无extradata也可以正常录制音频数据,播放正常;

2.录制hls时,若强制使用无extradata的AVCodecContext进行录制,则会core掉(这也是录制代码当时要区分hls与非hls录制逻辑的原因);

录制优化:

当前版本,录制初始化设置获取音视频AVCodecContext超时时间为90秒,并有重试逻辑,获取3次不成功就会减少超时时间,最终还不成功则忽略音频AVCodecContext,直接录制静音视频。实验过程中发现,以上3种场景,只要推流端之后能正确推音频数据上来,录制中使用avformat_open_input得到的AVFormatContext中的音频AVCodecContext都会被正确初始化。意味着,如果录制途中再去获取音频的AVCodecContext是可以获取到的,这刚好适用于录制hls的场景,因为录制每次切ts分片的时候都会重新调用setup muxing。

优化效果:

假定,m3u8里有两个ts分片,1.ts和2.ts,1.ts不含音频数据,2.ts含有音频数据(优化的结果)。

ffplay/potplay/hls.js 播放全程静音

ios 1分钟前静音,1分钟后正常同步音频