Android音频子系统(十四)------耳机杂音问题解析

你好!这里是风筝的博客,

欢迎和我一起交流。


背景介绍:
【前提条件】OPPO的模拟有线耳机
【操作步骤】打开全民K歌进行任意一首音乐K歌的时候
【实际结果】耳机里面有滋滋的杂音
【期望结果】耳机里面没有滋滋的杂音
【出现次数/复现次数】用户反馈偶现

【前提条件(Preconditions)】:无
【操作步骤(Steps)】:进入QQ/微信–插入数字耳机–进入某个单人聊天界面–拨打语音通话–听回铃音
【实际结果(Actual results)】:QQ/微信数字耳机语音通话回铃音有严重pop音
【期望结果(Desired result)】:无此现象
【出现次数/复现次数(Occurrences/Recurrences)】:5/5

近期出现大量数字耳机/模拟耳机杂音问题,项目即将上市,问题较大,压力山大…

模拟耳机下行出现杂音问题,一般杂音问题需要先对dump进行分析,确认杂音是在哪个环境引入。
打开af_mixer_write.wav查看,没有发现杂音。
打开streamout.pcm.xx.AudioALSAPlaybackHandlerNormal.wav查看,模拟耳机走的是normal,主要查看这个dump就行,同样没有发现杂音。
因为模拟耳机是从PMIC接出来,使用的是MTK内部的codec,而streamout是HAL的数据,如果HAL没问题,那么问题大概出现在了kernel里面,也就是codec里面。
根据过往经验,怀疑应该和性能有关。但是目前是概率的,而且是用户反馈,本次没有很好的debug手段。

只能把问题聚焦于数字耳机杂音问题。
因为是必现问题,本地进行试验验证,发现可以复现,而且是特定耳机必现,看起来是数字耳机问题,但是这款数字耳机在其他手机不会出现。

不过问题不大,只要我能复现,就有很多手段可以debug。

这题数字耳机问题就很像之前碰到的一个问题,这种情况基本都是由于往USB数字耳机写数据超时导致的,查看log:

AudioALSAPlaybackHandlerUsb: latency_in_s,0.000142,0.000000,0.010944,0.000001,0.000000, totalTime 0.011088 > logTimeout 0.007000 TIMEOUT!! queue 512

可以看到有多恨这种timeout的打印。

一般这种情况都是可以通过增大buffer size解决,在prepareUsb中,将
period size 256, period_count 2
修改为
period size 768, period_count 4
修改之后自测可以解决QQ/微信数字耳机语音通话回铃音有严重pop音问题,但是我是不太愿意去修改buffer的大小的,因为这么修改会有个影响:数字耳机的时延会增大
在手机中,耳机时延是一个很重要的体验,我是不太愿意随便修改这个。

但是最近耳机问题实在太大了,项目压力又大,只能先将版本发送给测试进行测试,结果QQ/微信问题可以解决了、但是其他场景:抖音数字耳机通话回铃声(抖音互关之后可以发起通话)、今日头条视频播放、王者荣耀组队语音下行,都还出现了杂音问题。

首先,这些问题不会是我修改buffer size引入的,修改有副作用(时延影响),但绝不会引起这些问题,说明杂音问题普遍存在,增大period size等修改无法完全解决问题。

正好,一起分析了。

看了下这几份的log,发现这几个场景都是fast通路,我也搞不懂为啥一个破抖音还要走fast,不过像fast这种低延时的通路,period size确实要比deepbuffer这种场景要小很多,很容易出现xrun的情况。

所以本质上这题还是写数据超时导致的xrun问题。我倒要看看到底是哪个线程导致了我们audio写数据不及时。
因为问题好复现,直接针对场景抓取了systrace:
Android音频子系统(十四)------耳机杂音问题解析_第1张图片
可以看到,write线程和mix线程正常运行,没有发现被阻塞的情况,CPU load情况也未见异常。

这就很奇怪了,从systrace无法看到问题原因,但是我又大概率觉得这题就是性能问题。

而且模拟耳机也出现了王者荣耀语音通话杂音问题,查看log,因为通话是会跑汇顶的通话算法的,会从kernel里面拿取下行数据做参考信号(走的AudioALSACaptureHandlerDsp),解析出汇顶通话dump:
Android音频子系统(十四)------耳机杂音问题解析_第2张图片
可以看到算法从kernel拿到的数据就出现了问题,这里出现补零。

没辙,还是得从音频方面入手,为了优化这些杂音/pop问题,对stop_threshold进行修改,一般情况下stop_threshold即为整个buffer size的大小,这样音频数据空时会停止DMA传输,我尝试将stop_threshold修改大,这样DMA不会停止传输,而是会传输旧数据,来获得更好的音频效果。

修改完毕之后测试,发现问题确实有改善,但是还是有轻微pop无法避免。

此时问题就陷入瓶颈了,分析不下去了。。。

这个时候测试有一台机器,是没有这个问题的,我看了下,不是机器问题,是版本问题,正常的机器里面的版本是一个半月前的版本,这说明之前的版本是没有这个问题的,这个问题是后来才有的!

不过版本间隔太久了,无法确定是哪个天引入的了,我把两个版本的提交拉出来对比分析,想找出来问题提交,但是Android上面一共几百个仓库,两千多条提交,这无异于大海捞针啊~

所以我们只需要关注几个和音频相关的仓库:kernel里面sound目录和drivers/usb的提交,Audio Hal的提交,AudioFlinger的提交,音频音效的提交,音频参数的提交,把这些里面的提交全部排查了一遍,眼睛都看花了…

但是!!!
还是没有发现问题的存在,差点破防了。

不行,这玩意还是得理一理,既然不是audio的提交引入的,看起来又是性能问题,我在想是不是CPU频率被修改了,进入某些场景被降频了,查看了下log,之前在文章说过:Android音频子系统(十二)------抖音直播功耗问题解析

音频就这几个场景会修改频率,查看log,频率没有被修改,都跑在预设的频率上,说明CPU没有被降频。

思来想去,只有最后一个方向了,内核性能方向!

这是最棘手的了,咱一个音频选手,让咱搞内核性能相关的那也搞不定啊,主要也看不懂,要是把锅甩出去,也没啥理由能站得住脚啊,又只能自己搞了。

把这一个半月的CPU仓库的修改都拉出来看了下,感谢兄弟们良好的编程习惯,虽然内容看不懂,但是commit message还算写得清楚,主要排查有没有性能相关的修改,主要方向就是调度优化和稳定性啥的,还真给我找出几个稳定性的提交,可疑,非常可疑!

最终,把这几个补丁筛选出来,一个个revert掉,起构建测试,终于找到了原因。

内核组之前加入了一笔补丁,CPU调度相关的,这笔补丁会导致热线程采集中断处理耗时长,把这笔补丁回退之后,数字耳机终于没有问题了,同步测试模拟耳机,也没有异常了!!!

麻了,又给人背锅了,这问题给我弄得欲生欲死的,每天都搞到很晚,好累啊。。。
Android音频子系统(十四)------耳机杂音问题解析_第3张图片

问题果然和我一开始想的那样,就是性能问题,但是细细回想一下这题,如果不是知道问题是由其他补丁引入的,然后进一步排查问题补丁的话,要想正面解决的话,确实还是比较头疼的。因为这题原因是底层中断耗时长问题,systrace主要是AP侧的线程情况,对于kernel的情况是不太好分析的,所以如果想要正面解决的话,可能还得需要在kernel开trace来分析:使用trace查看函数调用关系|分析Linux性能

在此之前我本来想的是如果实在不能解决,我是打算提频处理的,提频可以获取更好的性能,但是会引入功耗变高的问题,这就另说了,不过幸好这回有补丁可以解决,终于可以好好休息了~

你可能感兴趣的:(音频子系统,Android,音视频,audio,数字耳机杂音,模拟耳机杂音)