AudioFramework面试题
蓝牙、usb、耳机设备连接后声音如何从该设备输出
首先会用通知AudioManager.java,告诉现在要设置usb了,然后会通过jni调到APM中到函数SetDeviceConnectionState
中.
在SetDeviceConnectionState
中会分别调用checkOutputsForDevice
、checkForDeviceAndOutputChanges
1**checkOutputsForDevice
**作用是:
当新音频设备连接时,检查系统是否支持该设备,并确保有合适的输出流可以路由到该设备。如果没有现成的输出流可用,它会尝试打开新的输出流来支持该设备。
这样就打开了一个支持该设备的输出流了。那么接下来还需要把AudioTrack与AudioFlinger关联的共享内存配置到新打开的输出流中。
2**checkForDeviceAndOutputChanges->checkOutputForAllStrategies
**作用是:
对所有的strategy分组声音,判断是否需要迁移到新的output, 如果需要则迁移对应Track到新的output,并且会调用invalidateStream将相关的Audiotrack设置成invalid,这样当再次使用AudioTrack.write时候,发现状态是invalid,会重新创建新的audiotrack绑定到新的output中。
这里涉及2个判断
判断是否需要迁移:
对于该strategy, 得到它的oldDevice, 进而得到它的outputs (srcOutputs);
对于该strategy, 得到它的newDevice, 进而得到它的outputs (dstOutputs);
如果这2个srcOutputs、dstOutputs不相同, 表示需要迁移
setOutputDevice 这需要操作HAL层
AudioTrack如何写数据到hal层
这一个过程涉及到三个进程间的数据传输,使用的数据拷贝技术是共享内存。
三个进程分别是app、audioserver、android.hardware.audio.
对于app与audioserver的通信主要设计两个模块一个是app端的AudioTrack,一个是audioserver端的AudioFlinger.
AudioTrack 在构建过程中会创建一个共享内存与AudioFlinger相关联。这样对于AudioTrack来说写数据就是使用通过obtainBuffer从共享内存中获取一块buffer,然后拷贝数据到buffer中然后在释放掉这个buffer
AudioFlinger会在playbackThread中持续的从activity的AudioTRck中获取buffer,然后读取buffer中的数据做处理,在释放掉这个buffer。 是一个生产者与消费者模型,
AudioServer与android.hardware.audio类似
AudioFlinger与AudioPolicyManager各自的功能:
简单的来说APM是策略的执行者,AF是策略的管理者。
APM 负责设备连接断开事件、管理设备路由策略、音量策略
AF负责所有音频流的混音与路由执行、管理playbackThread、recordThread等县城、直接与hal层交互、打开关闭音频设备、分配共享内存等
AudioTrack的播放流程
对于用户来说,使用AudioTrack的方式是创建一个AudioTrack对象,然后调用start方法,然后调用write方法写入数据的过程,
接下来说一下AudioTrack的构建过程。
AudioTrack的构建
- 首先会根据AuidoFormat、sampleRate、channelCount来获取一个最小buffersize。
- 然后会创建一个AudioTrack对象,传参包括audio三件套、buffersize、stream_type 还有一个Mode,这个Mode有两种模式,一种是MODE_STREAM,一种是MODE_STATIC。在AudioTrack中首先会调用getOutputForAttr获取到output,然后在AudioFlinger中创建共享内存并且会会在output对应到playbackThread中创建一个track。
- 这样AudioTrack.write数据,playbackThread中可以通过创建到track 读取到写入的数据,这样就完成了数据传输。
- 然后把所有从绑定到playbackTrread的track的数据mix在一起写入hal层。
音量设置流程
AudioManager.java
存在两个设置音量的API setStreamVolume
和adjustStreamVolume
分为两种情况:硬件支持音量增益、硬件不支持音量增益
硬件不支持音量增益
在APM中会将设置下来的音量index通过解析出来的音量曲线,映射成增益,然后计算成振幅。设置到AudioFlinger中。在AudioFlinger中会把获取到的pcm数据与振幅值做运算从而实现音量的调整。
硬件支持音量增益
如果硬件支持音量增益,那么会直接调用到hal层中的setAudioPortConfig直接设置音量值。
eswin平台
会监听音量设置的广播,当接收到广播之后调用我们AudioOutput的私有接口,来完成音量的增益,我们的方案也是分为软件与硬件。 这么实现的目的是对DTV、HDMI的数据进行软件增益。
AudioFlinger、APM启动流程
AudioFlinger启动流程
- AudioFlinger会创建EffectFactory与DeviceFactory的对象。 对于Device而言是分为devicelocal和devicehidl。在创建DeviceFactory对象中会根据字符串android.hardware.audio与IDevicesFactory来查找对应的服务,如果查找到了就dlopen一个libaudiohalxx.so.并且使用函数dlsym创建一个函数createIDeviceFactory这样我们就获取了一个hal层服务了。 到此AudioFlinger的启动流程就结束了
APM启动
- 解析audio_policy_configuration.xml文件, 然后根据解析出来的数据配置策略与音量曲线等。
- 调用函数onNewAudioModulesAvailable ,该函数主要做了两件事
1.lodaHwModel 直接调用到audio_hw.c的adev_open函数
2.openOutPut 创建输出流线程,并且打开输出通道 也就是调用audio_hw.c中openOutputStream,音频可以通过该输出通道输送个某个设备