最近几个平台都会遇到一些声音的问题,包括投影的蓝牙传输播放声音小、TV煲机中逐步无声等等。刚好试着使用一下TeeSink工具进行调试,顺便记录一下。
TeeSink的官方使用文档如下:
音频调试 | Android 开源项目 | Android Open Source Project
准备步骤
按照操作步骤,需要执行的工作如下:
1.编译出了userdebug/eng的版本软件烧录,以确保 ro.debuggable 为 [1]。
2.将Configuration.h中 TEE_SINK 配置的注释放开(有些厂家是默认放开的)
3.make libaudioflinger,然后替换板端的system/lib下同名so,确认权限后重启生效。
因为使用的平台版本是Android 9 、Android 11、Android 12,因此默认teesink抓取的录音会放至/data/misc/audioserver目录下。如果低于Android 7,目录为/data/misc/media。
4.配置af.tee=#。
TeeSink 保存录音名称解析
从 Android 11 上扒出对应的规则说明:
Tee filenames generated as follows:
"aftee_Date_ThreadId_C_reason.wav" RecordThread
"aftee_Date_ThreadId_M_reason.wav" MixerThread (Normal)
"aftee_Date_ThreadId_F_reason.wav" MixerThread (Fast)
"aftee_Date_ThreadId_TrackId_R_reason.wav" RecordTrack
"aftee_Date_ThreadId_TrackId_TrackName_T_reason.wav" PlaybackTrack
where Date = YYYYmmdd_HHMMSS_MSEC
where Reason = [ DTOR | DUMP | REMOVE ]
Examples:
aftee_20180424_153811_038_13_57_2_T_REMOVE.wav
aftee_20180424_153811_218_13_57_2_T_REMOVE.wav
aftee_20180424_153811_378_13_57_2_T_REMOVE.wav
aftee_20180424_153825_147_62_C_DUMP.wav
aftee_20180424_153825_148_62_59_R_DUMP.wav
aftee_20180424_153825_149_13_F_DUMP.wav
aftee_20180424_153842_125_62_59_R_REMOVE.wav
aftee_20180424_153842_168_62_C_DTOR.wav
Teesink使用问题记录
1.替换so后系统启动失败
确保了板端的软件和当前编译代码是一致的,在代码中放开了TEE_SINK的配置后,make出so替换进系统中,重启发现系统启动失败。
报错定位在AudioFlinger或者是SurfaceFlinger(?),出现了crash。
解决方法:
未知谜团,采用最简单粗暴的整机软件烧录编译解决。
2.配置af.tee=# 后,dump af没有teesink记录和录音生成
这里遇到三种不同的情况,需要一一确认:
2.1 读取不到 af.tee 的prop
按照官方文档给出的做法:
echo af.tee=# > /data/local.prop
chmod 644 /data/local.prop
reboot
重启后串口读取af.tee的取值,发现没有af.tee这个prop。这个情况在部分芯片上出现,估计是一些配置限定,令系统启动时没有读取local.prop这个文件。
解决方法:
新建的路走不通,直接在已有的prop文件上新增一条。直接mount了vendor分区,在build.prop / default.prop上加入af.tee的配置。
为啥需要在prop文件内配置呢?因为af.tee是不带persist前缀的,默认不带持久化处理,重启后会失效。想要启动之后自动生成这个prop配置,就需要在prop文件里配置,系统启动时会读取所有prop文件,一一配置里面的内容。
2.2 af.tee 的读取时机问题
在 2.1 出现重启后读取不到 af.tee 的情况下,正常进入系统后,手动配置了 af.tee=#,再进行dump发现没有效果。查阅源码,发现 AF 是在构造方法中解析 af.tee,并保存到mTeeSinkInputEnabled、mTeeSinkOutputEnabled、mTeeSinkTrackEnabled三个变量中。也就是说,af.tee 需要在audioserver 启动前配置完成才有效果。
解决方法:
手动配置完成后 kill audioserver重新触发一次流程。或者简单点,采用2.1的解决方式,在已有prop文件上加入af.tee的配置。
2.3 播放音视频时,dump没有teesink记录和录音生成
按照官方说明的参数配置,基本上可以顺利dump出录音记录了。
这里需要提一下af.tee的配置说明:
- 1 = 输入线程。即 RecordThread。dump后在data/misc/audioserver中记录的后缀为‘C’(capture)
- 2 = MixerThread 输出线程。官方指的是FastMixer 输出,但我查看了NBAIO_Tee.cpp的说明发现这里只要是混音线程都可以正常 dump 出来。这里的混音线程包括 Fast MixerThread 或者是Normal MixerThread。dump后在data/misc/audioserver中记录的后缀为‘M’(normal mixer)或者是 ‘F’ (fast mixer)。
- 4 = Track。Track包括输入的 RecordTrack 和输出的 AudioTrack 。dump后在data/misc/audioserver中记录的后缀为‘T’(track)或者‘R’(record track)。
#ifdef TEE_SINK
char value[PROPERTY_VALUE_MAX];
(void) property_get("ro.debuggable", value, "0");
int debuggable = atoi(value);
int teeEnabled = 0;
if (debuggable) {
(void) property_get("af.tee", value, "0");
teeEnabled = atoi(value);
}
// FIXME symbolic constants here
if (teeEnabled & 1) {
mTeeSinkInputEnabled = true;
}
if (teeEnabled & 2) {
mTeeSinkOutputEnabled = true;
}
if (teeEnabled & 4) {
mTeeSinkTrackEnabled = true;
}
#endif
注:以上代码是基于 Android 9 扒出来的,在 Android 11 上已经换了一种方式配置了。
根据上面的配置,能够确认TeeSink能够抓取的数据包括:MixerThread、RecordThread以及两个Thread内部的Tracks。反推一下,就能得出TeeSink无法使能的场景:
1.使用非MixerThread播放的数据。
本地媒体播放视频,dump不出数据;播放音频,顺利dump出来。对比了一下dump af 的内容,在播放音频时开启的PlaybackThread类型为MixerThread,播放视频时开启的PlaybackTrhead类型为DirectThread。DirectThread默认不会处理这些数据,一般会直接送到hal层由芯片去解析数据,因此在 fw 层其实还是非pcm数据,因此Google不会对DirectThread存流。内置的TeeSink只能获取MixerThread和下面AudioTrack的内容,有这个区别。
2.不通过AF传输的数据。
这里给两个场景:
(1)信源播放
TV的本质业务是信源播放。尽管Google后续版本是想要规范TV产品业务进AOSP里的,但是目前芯片和TV厂家都是使用的自己支持的一套。
TV业务上层应用OSD层根据用户选择进入某个通道,通过和芯片厂商的封装协议open对应通道的audio和video信号。板端根据输入信号状况,回调状态码给上层应用。如果成功打开,关闭OSD层,通过VIDEO层进行显示。
整体的交互完全靠封装私有接口进行对接,没有Framework层的参与。此时去dump af,会发现没有任何PlaybackThread的记录。因此这部分是没有办法通过TeeSink去抓取数据的。
(2)厂商的播放器优化
各个厂商或多或少都会对MediaPlayer进行优化,以提供更强大、更优化的能力和支持更多的格式。一部分厂商会保持MediaPlayer作为对外封装的播放器,只把内部实现替换成NuPlayer或者其他实现。这种做法TeeSink一般来说还是可以抓取到的。
另一些厂商替换程度更加彻底,换成内部实现的player,直接绕开Framework的一系列流程,把整体工作全部自己完成,一般对接的时候会被叫做私有通路。这种状态下,Framework不参与其中,dump af也会没有任何PlaybackThread的记录,自然就没有办法通过TeeSink抓取了。
针对上述这两种场景,其实芯片厂商会内置了一些dump命令去抓取驱动的音频数据。可以合理利用这些命令去抓取全场景的出声数据。
3.dump出文件打开为空数据
我常常会拿来排查这个情况:播放某个音频数据,有正常的MixerThread和active track记录,但是无声。
查看MixerThread和RecordThread的记录,正常dump出 pcm文件后,通过Audacity打开pcm,发现完全没有波形。这个可能是因为对应的Track被设置成静音了(没错,track其实也是有自己对应的音量的,往上层可以简单理解为某个Mediaplayer通过setVolume设置的音量)。
这种情况下,查看dump af 中对应的track中的数据,队友对应的L和R,其中最大的音量值(track默认状态就是最大,可以简单理解为UI值中的100)对应的是0,最小音量值(可以简单理解为UI值中的0)对应的是-inf。
别问这有什么场景,当年自己移植patch踩过坑;也排查过上层应用奇怪需求设置播放器音量的问题。
TeeSink排查案例
回到最开始我遇到的问题。
案例一:客户使用手机蓝牙连接机器,播放歌曲发现声音很小,需要调至很大才能听清。
拿到这个问题,先是拿了不同的手机进行对比,幸运的是三台手机表现不一致,已经能初步断定和手机有关系。
取其中两台手机连接同一机器,dump af查看track音量值参数,均为默认最大值。播放同一软件的同一首歌(为了保持音源一致),抓取teesink数据做对比,发现系统端收到的数据幅度差异很大。因此确认问题是在AudioTrack接受的数据的问题,与客户澄清。
案例二:一些TV平台出现的播放无声问题。
比如遇到A平台生产机器发现音量调大后突然出现的整机无声问题;B平台生产机器发现煲机中途突然整机无声问题。
在无声状态下先排查fw层的mute状态、音量设置通路、底层寄存器mute脚和db值状态之后,就可以开始用teesink和厂商自带的dump工具抓取播放数据排查了。
TeeSink可以作为Fw数据排查,厂商自带的是整机输出数据排查,可以确认软件端输出流程是否正常。排查完软件端后,就会交由amp功放和硬件同事继续排查其他问题。