内容简介:不久前,团队发现其Android平台App在播放MV视频《凤凰花开的路口》时,会带有如电流声一般的杂音,这影响了用户体验。 研发同学在初步定位时,发现有如下特征:然而,各平台都是统一用HLS格式播放,即源头都是一样的。对于该问题,我们的定位思路如下:分析播放流程如上图(图中内容从左往右),概括其关键步骤如下:
一、问题背景与分析
不久前,团队发现其Android平台App在播放MV视频《凤凰花开的路口》时,会带有如电流声一般的杂音,这影响了用户体验。 研发同学在初步定位时,发现有如下特征:
- Android平台杂音问题必现;
- iOS、PC平台能正常播放,没有噪音。
然而,各平台都是统一用HLS格式播放,即源头都是一样的。对于该问题,我们的定位思路如下:
- 梳理视频播放流程;
- 找到切入点排查。
二、播放流程概览
分析播放流程如上图(图中内容从左往右),概括其关键步骤如下:
-
播放器初始化:
read_thread audioq sampq
-
数据读取:
avformat_open_input avformat_find_stream_info av_find_best_stream stream_component_open av_read_frame(ic, pkt) audioq
-
音频解码:
-
在
audio_thread
中对audioq
中的数据进行decoder_decode_frame
解码; -
解码后的帧
AVFrame
存放到sampq
中;
-
在
-
音频播放:
-
aout_thread_n
中,通过调用回调接口sdl_audio_callback
,对sampq
中的音频帧数据进行解码成PCM数据; -
写入PCM数据到buffer数组,并由
AudioTrack
播放。
-
三、问题分解与切入
在梳理出播放流程后,标记出找到有可能出错的环节,方便进行“分层定位”(图中黄色标记)
AudioTrack
接下来,根据难易程度,对上述环节逐个验证。
1、播放下载文件是否正常
把Android平台播放的ts文件与各平台的进行比对,发现两者一样,该环节正常。
2、 AudioTrack
设置是否正常
通过日志检查 AudioTrack
以下配置参数:
- 采样率
- 位深
- 频道
以上参数设置的值与音频流的相符合,该环节正常。
3、音频解码逻辑是否有问题
验证解码逻辑是否有问题,可以通过对PCM数据进行分析来确认。
对 aout_thread_n
进行修改,将PCM数据额外输出到本地,并与正常的PCM数据进行对比。
正常PCM数据频谱图:
异常PCM数据频谱图:
正常PCM数据波形图:
异常PCM数据波形图:
对比分析可得出:
- 从频谱图中看出,异常的PCM在人耳十分敏感的频响(1000~8000Hz )区域内的音频数据严重缺失,导致“杂音问题”
- 从波形图中看出,异常的与正常的无声区和有声区都吻合,若解封装、解码逻辑出现异常,极大几率是呈现无波动(一条直线的形式)情况。因此可以先大胆 假设解码、解封装逻辑是符合预期的
若解码逻辑正常,再结合之前已经验证文件下载正常。可以 推测是数据读取环节出现异常 。
4、数据读取是否有问题
通过对数据读取的各步骤增加日志后,发现在 av_find_best_stream
音频流选择时出现异常: ffmpeg -i
发现,该视频ts分片有2个音频流
通过强制分别读取两条音频流数据播放,发现:
- 第一条正常播放(PCM数据正常)
- 第二条播放杂音(PCM数据异常)
- Android平台选择了第二条进行播放
基于此,也就验证了在第3步中的假设是正确的。
由上分析,可以得出结论: Android平台选择了第二条数据有问题的流进行播放。
四、问题根源:音频流选择
1、选择方式
分析代码,大致如下所列, av_find_best_stream
函数选择音频流,该函数会根据2个主要参数进行选择:
-
各音频流的在探测媒体类型(
avformat_find_stream_info
)时, 额外解码出来的帧数 (选择多的) - 各音频流的 比特率 (选择高的)
int av_find_best_stream(AVFormatContext *ic, enum AVMediaType type, int wanted_stream_nb, int related_stream, AVCodec **decoder_ret, int flags) { for (i = 0; i < nb_streams; i++) { count = st->codec_info_nb_frames; //音频流探测中解码的帧数 bitrate = avctx->bit_rate;//音频流的比特率 multiframe = FFMIN(5, count); //先比较解码帧数,再比较音频流比特率,谁大谁选 if ((best_multiframe > multiframe) || (best_multiframe == multiframe && best_bitrate > bitrate) || (best_multiframe == multiframe && best_bitrate == bitrate && best_count >= count)) continue; best_count = count; best_bitrate = bitrate; best_multiframe = multiframe; ret = real_stream_index;//最后选择的流index best_decoder = decoder; } return ret; }
在该视频中,我们可以看到:
codec_info_nb_frames |
bit_rate |
|
---|---|---|
audio_stream 1 |
38 |
122625 |
audio_stream 2 |
39 |
126375 |
第二条流的解码帧数和比特率要比第一条高,因此选择了第二条流播放
2、对比同类方案
分析了以上选择规则后,我们对各平台、框架进行了选择规则的对比:
备注:
- ExoPlayer对多音频流的ts分片支持不完善(issue),因此测试时需要调整相关接口。但选择规则依然以上述所示(DefaultTrackSelector)
-
iOS和PC平台采用闭源组件,因此测试时使用了 “互换两条音频流顺序”
的方法进行测试。互换后,两平台都播放了杂音音频流
ffmpeg -i INPUT_FILE -map 0:0 -map 0:2 -map 0:1 -c copy -y OUTPUT_FILE
-
QuickTime同样是闭源,互换音频流后无法明显差别,通过 合成第三条音频流
,来验证是它是对所有音频流全播放
ffmpeg -i INPUT_FILE_1 -i INPUT_FILE_2 -map 0:0 -map 0:1 -map 0:2 -map 1:0 -c copy OUTPUT_FILE
3、总结
从以上数据看到,iOS和PC平台会默认选择第一条流,而在Android平台的FFmpeg和ExoPlayer会根据音频流属性来选择数值更好的一条。
- “默认选择第一条”方案能 更容易地把音源问题暴露 。
- “比较音频流属性”方案能 更大几率地选择质量更好的流 来提升用户体验。
但以上2个选择方案都 无法识别 “内容异常”的音频流。
五、问题解决方案
因此,处理该问题,需要从音源上进行修复和规避,我们的建议是从源头杜绝,从终端规避:
- 编辑重新上架正常音源;
- 短期内增加双音频流的检测上报,帮助后台、编辑进行复查;
- 长远看由后台开发工具,分别对存量视频进行双音频流检测和对增量视频保证只转码单音频流;
参考资料
- https://ffmpeg.org/doxygen/2.8/
- https://github.com/google/ExoPlayer
- https://www.jianshu.com/p/daf0a61cc1e0
- https://www.jianshu.com/p/a6a4bf59cdae
- http://km.oa.com/articles/show/319627
- https://codeday.me/bug/20170711/39603.html
QQ音乐团队诚聘测试、研发。有意者请发送简历至 tmezp@tencent.com ,请注明来自公众号,我们将优先拜读。
以上所述就是小编给大家介绍的《追根溯源解杂音之谜,臻于至善得完美音质》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Clean Architecture
Robert C. Martin / Prentice Hall / 2017-9-20 / USD 34.99
Practical Software Architecture Solutions from the Legendary Robert C. Martin (“Uncle Bob”) By applying universal rules of software architecture, you can dramatically improve developer producti......一起来看看 《Clean Architecture》 这本书的介绍吧!