Android音频系统之AudioTrack(二) 【转】

1.1.1 AudioPolicyService的路由实现

我们在AudioPolicyService小节曾将其比作是一个“路由器”,不过还没有深入解析它是如何完成路由选择的。这部分的功能与使用者——AudioTrack有很大关联,所以我们特别将它的实现原理剖析放在这里,以使读者可以综合起来理解。

路由器功能由如下几个部分组成:

l  与发送方(AudioTrack)的接口

就好像路由器首先要接收到一个IP数据包,它才会去做路由处理,否则AudioPolicyService就成了“无源之水”了

l  与接收方(AudioFlinger)的接口

道理和上面是类似的,AudioPolicyService内部拥有当前系统中所有音频设备的信息,就好比一个路由器也需要预先知道它有多少个节点,才可能把音频数据发送到正确的终点一样

l  路由路径的选择策略

路径选择策略是AudioPolicyService的重点。和传统的路由器不同,它的路径选择算法并不是固定的,而是通过灵活的方式先产生一个策略制定者,然后再由它来生成具体的策略

大家应该还记得前面AudioTrack小节中,我们调用了AudioSystem::getOutput,即:

status_t AudioTrack::set(…)

{…

audio_io_handle_t output =AudioSystem::getOutput(streamType, sampleRate, format, channelMask, flags);

…}

AudioSystem只是一个中介,其中的实现还是由AudioPolicyService完成的:

audio_io_handle_t AudioSystem::getOutput(…)

{

constsp<IAudioPolicyService>& aps =AudioSystem::get_audio_policy_service();

if (aps == 0) return 0;

returnaps->getOutput(stream, samplingRate, format, channels, flags);

}

显然是直接调用了AudioPolicyService的服务接口:

audio_io_handle_t  AudioPolicyService::getOutput(...)

{   …

Mutex::Autolock _l(mLock);

return  mpAudioPolicy->get_output(mpAudioPolicy,stream, samplingRate, format, channels, flags);

}

变量mpAudioPolicy便是由策略制定者“生产”出来的Policy。在原生态的实现中它代表的是legacy_audio_policy::[email protected]_policy_hal.cpp,因而上面实际上调用的是如下函数:

static  audio_io_handle_t  ap_get_output(struct audio_policy *pol,…)

{

struct legacy_audio_policy*lap = to_lap(pol);

returnlap->apm->getOutput((AudioSystem::stream_type)stream, sampling_rate,(int) format, channels,

(AudioSystem::output_flags)flags);

}

也就是说,前面的apm->getOutput的接口实现最终是落在getOutput @ AudioPolicyManagerBase(AudioPolicyManagerDefault继承自 AudioPolicyManagerBase,而后者又继承自AudioPolicyInterface)。

我们先来看下AudioPolicyManagerBase的getOutput实现。

/*hardware/libhardware_legacy/audio/AudioPolicyManagerBase.cpp*/

audio_io_handle_t  AudioPolicyManagerBase::getOutput(AudioSystem::stream_typestream,

uint32_t samplingRate, uint32_t format,

uint32_t channelMask, AudioSystem::output_flags flags)

{

audio_io_handle_t  output = 0;

uint32_t  latency = 0;

/*Step 1. 获取stream类型对应的Strategy*/

routing_strategy strategy= getStrategy((AudioSystem::stream_type)stream);

audio_devices_t  device = getDeviceForStrategy(strategy, false/*fromCache*/);

/*Step 2. 应用策略,判断哪些Output符合用户传入的Stream类型*/

SortedVector<audio_io_handle_t>outputs = getOutputsForDevice(device);

/*Step 3. 选择一个最适合的Output*/

output =selectOutput(outputs, flags);

return output;

}

我们将这个函数分为三个步骤。

[email protected]::getOutput. 每种Stream类型都有对应的strategy,比如AudioSystem::TTS 和AudioSystem::MUSIC对应的是STRATEGY_MEDIA,AudioSystem::NOTIFICATION对应的是 STRATEGY_SONIFICATION_RESPECTFUL。具体的对应关系如下表所示:

表格 13?5 Stream类型与Strategy对照表

STREAM_TYPE

STRATEGY


VOICE_CALL


STRATEGY_PHONE


BLUETOOTH_SCO


RING


STRATEGY_SONIFICATION


ALARM


NOTIFICATION


STRATEGY_SONIFICATION_RESPECTFUL


DTMF


STRATEGY_DTMF


SYSTEM


STRATEGY_MEDIA


TTS


MUSIC


ENFORCED_AUDIBLE


STRATEGY_ENFORCED_AUDIBLE

不同的Stream类型有可能会被划归同一个Strategy,比如TTS、MUSIC及SYSTEM类型的音频,它们在路由策略上都遵循STRATEGY_MEDIA。当然我们也可以通过重载getStrategy来按自己的要求划分Strategy。

当找到某Stream类型对应的Strategy后,接下来getDeviceForStrategy进一步为这一Strategy查找最佳匹配的音频设备(以STRATEGY_MEDIA为例):

audio_devices_tAudioPolicyManagerBase::getDeviceForStrategy(routing_strategy strategy, boolfromCache)

{

uint32_t device = 0;

switch(strategy) {

case  STRATEGY_MEDIA: {

uint32_t device2 = 0;

if (mHasA2dp&& (mForceUse[AudioSystem::FOR_MEDIA] !=

AudioSystem::FORCE_NO_BT_A2DP)&& (getA2dpOutput() != 0) && !mA2dpSuspended) {

device2 =mAvailableOutputDevices & AudioSystem::DEVICE_OUT_BLUETOOTH_A2DP;

if (device2 == 0){

device2 =mAvailableOutputDevices &

AudioSystem::DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES;

}

if (device2 == 0){

device2 =mAvailableOutputDevices &

AudioSystem::DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER;

}

}

if (device2 == 0) {

device2 =mAvailableOutputDevices & AudioSystem::DEVICE_OUT_WIRED_HEADPHONE;

}

if (device2 == 0) {

device2 =mAvailableOutputDevices & AudioSystem::DEVICE_OUT_WIRED_HEADSET;

}

if (device2 == 0) {

device2 =mAvailableOutputDevices & AUDIO_DEVICE_OUT_USB_ACCESSORY;

}

if (device2 == 0) {

device2 =mAvailableOutputDevices & AUDIO_DEVICE_OUT_USB_DEVICE;

}

device |= device2;

if (device) break;

device =mDefaultOutputDevice;

if (device == 0) {

ALOGE("getDeviceForStrategy() no device found forSTRATEGY_MEDIA");

}

} break;

上面的代码看上去很长,但逻辑比较简单——按照一定的优先级来匹配系统中已经存在的音频设备。这个优先级的设定因Strategy不同而有所差异。在STRATEGY_MEDIA这种情况下,其优先级如下所示:

²  在有蓝牙A2dp的平台上,且设备可以正常打开,没有挂起,当前也没有强制不使用A2dp,那么通过匹配mAvailableOutputDevices来寻找合适的A2dp设备,比如A2dp_headphone、A2dp_Speaker

²  要注意的是,只有在上一步匹配失败(即找不到合适的设备,变量device2为0)的情况下,才会继续执行下一优先级的判断。这里处于第二等级的是wired headphone

²  继续寻找是否有wired headset

²  寻找是否有usb accessory

²  寻找是否有usb device

等等。。。

正常情况下getDeviceForStrategy都能获得符合要求的device。我们再回到前面的getOutput,看下接下来步骤的执行。

[email protected]::getOutput

SortedVector<audio_io_handle_t> AudioPolicyManagerBase::getOutputsForDevice(audio_devices_tdevice)

{

SortedVector<audio_io_handle_t> outputs;

for (size_t i = 0; i <mOutputs.size(); i++) {

if ((device &mOutputs.valueAt(i)->supportedDevices()) == device) {

outputs.add(mOutputs.keyAt(i));

}

}

return outputs;

}

这个函数用于获得所有支持device设备的Output,并添加到outputs中。Output是AudioFlinger::openOutput 得到的结果,AudioPolicyService会把它们存储到mOutputs键值对中。因为每个Output通常都支持若干种音频设备,不同的 Output支持的音频设备类型也是不限的,所以系统中很可能存在多个支持device的Output。

Step [email protected]::getOutput. 到目前为止,符合要求的Output可能不止一个,所以要选择一个最适合的。

audio_io_handle_t AudioPolicyManagerBase::selectOutput(constSortedVector<audio_io_handle_t>& outputs,

AudioSystem::output_flags  flags)

{

/*Step 1. 处理一些特殊情况*/

if (outputs.size() == 0) {

return 0;

}

if (outputs.size() == 1) {

return outputs[0];

}

先处理一些特殊情况,比如没有任何output存在的情况下只能返回空;同样的如果只有一个output的情况也没得选择,直接返回该output。

/*Step 2. 开始判断择优*/

int maxCommonFlags = 0;

audio_io_handle_toutputFlags = 0;

audio_io_handle_t  outputPrimary = 0;

for (size_t i = 0; i <outputs.size(); i++) {

AudioOutputDescriptor*outputDesc = mOutputs.valueFor(outputs[i]);

if(!outputDesc->isDuplicated()) {

int commonFlags =(int)AudioSystem::popCount(outputDesc->mProfile->mFlags & flags);

if (commonFlags > maxCommonFlags) {

outputFlags =outputs[i];

maxCommonFlags= commonFlags;

ALOGV("selectOutput() commonFlags foroutput %d, %04x", outputs[i], commonFlags);

}

if(outputDesc->mProfile->mFlags & AUDIO_OUTPUT_FLAG_PRIMARY) {

outputPrimary= outputs[i];

}

}

}

这个循环是整个函数的核心,我们来分析下它的决策准绳是什么。大家只要仔细看下循环中的判断语句,就可以发现它实际上是在寻找最大值maxCommonFlags,所以问题就转化为,什么东西的最大值?

int commonFlags =(int)AudioSystem::popCount(outputDesc->mProfile->mFlags & flags);

上面这句代码用通俗的话来讲,就是计算outputDesc->mProfile->mFlags与入参flags的相似度有多大。可选的Flags如下所示:

AUDIO_OUTPUT_FLAG_NONE = 0x0,

AUDIO_OUTPUT_FLAG_DIRECT = 0x1,  //output直接把track导向一个output stream,没有混音器

AUDIO_OUTPUT_FLAG_PRIMARY = 0x2,  //primary output,它是唯一的并且必需存在

AUDIO_OUTPUT_FLAG_FAST = 0x4,     //支持fasttracks的output

AUDIO_OUTPUT_FLAG_DEEP_BUFFER = 0x8 //使用deepaudio buffer的output

音频系统中名为output_flags的数据类型非常多,不过仔细回溯的话,可以发现这个flags是在AudioTrack的set函数中指定的。另外,如果在查找过程中发现primaryoutput,则用outputPrimary表示,这在后面会用到。

/*Step 3. 根据优先级做出选择*/

if (outputFlags != 0) {

return outputFlags;

}

if (outputPrimary != 0) {

return outputPrimary;

}

return outputs[0];

}

优先级的排列很简单,即:

²  Flags与要求相似度高的output

²  Primaryoutput

²  如果上面两种都找不到,则默认返回第一个output

这样子AudioPolicyService就完成了整个路由路径的选择,AudioTrack则是通过AudioSystem::getOutput间接调用到AudioPolicyService的这一功能。

时间: 01-07

Android音频系统之AudioTrack(二) 【转】的相关文章

Android音频系统之AudioTrack(一) 【转】

1.1 AudioTrack 1.1.1 AudioTrack应用实例 对于Android应用开发人员来讲,音频回放最熟悉的莫过于MediaPlayer,而AudioTrack相信用的人相对会少很多.这是因为 MediaPlayer提供了更完整的封装和状态控制,使得我们用很少的代码就可以实现一个简单的音乐播放器.而相比MediaPlayer,AudioTrack更为精练.高效,实际上MediaPlayerService的内部实现就是使用了AudioTrack. AudioTrack被用于PCM音

Android音频系统之AudioFlinger(二) 【转】

1.1.1 音频设备的管理 虽然AudioFlinger实体已经成功创建并初始化,但到目前为止它还是一块静态的内存空间,没有涉及到具体的工作. 从职能分布上来讲,AudioPolicyService是策略的制定者,比如什么时候打开音频接口设备.某种Stream类型的音频对应什么设备等等.而AudioFlinger则是策略的执行者,例如具体如何与音频设备通信,如何维护现有系统中的音频设备,以及多个音频流的混音如何处理等等都得由它来完成. 目前Audio系统中支持的音频设备接口(Audio Inte

Android音频系统之AudioFlinger(二)

1.1.1 音频设备的管理 虽然AudioFlinger实体已经成功创建并初始化,但到目前为止它还是一块静态的内存空间,没有涉及到具体的工作. 从职能分布上来讲,AudioPolicyService是策略的制定者,比如什么时候打开音频接口设备.某种Stream类型的音频对应什么设备等等.而AudioFlinger则是策略的执行者,例如具体如何与音频设备通信,如何维护现有系统中的音频设备,以及多个音频流的混音如何处理等等都得由它来完成. 目前Audio系统中支持的音频设备接口(Audio Inte

Android 音频系统:从 AudioTrack 到 AudioFlinger

1. Android 音频框架概述 Audio 是整个 Android 平台非常重要的一个组成部分,负责音频数据的采集和输出.音频流的控制.音频设备的管理.音量调节等,主要包括如下部分: Audio Application Framework:音频应用框架 AudioTrack:负责回放数据的输出,属 Android 应用框架 API 类 AudioRecord:负责录音数据的采集,属 Android 应用框架 API 类 AudioSystem: 负责音频事务的综合管理,属 Android 应

Android音频系统之音频框架

http://blog.csdn.net/xuesen_lin/article/details/8796492 我们可以结合目前已有的知识,想一下每一个层次都会包含哪些模块(先不考虑蓝牙音频部分)? ·        APP 这是整个音频体系的最上层,因而并不是Android系统实现的重点.比如厂商根据特定需求自己写的一个音乐播放器,游戏中使用到声音,或者调节音频的一类软件等等. ·        Framework 相信大家可以马上想到MediaPlayer和MediaRecorder,因为这

Android音频: 如何使用AudioTrack播放一个WAV格式文件?

翻译 By Long Luo 原文链接:Android Audio: Play a WAV file on an AudioTrack 译者注: 1. 由于这是技术文章,所以有些词句使用原文,表达更准确. 2. 由于水平有效,有些地方可能翻译的不够准确,如有不当之处,敬请批评指正. 3. 针对某些语句,适当补充了上下文及更适合中文阅读,尽量做到信达雅. 如果你已经成功地了解了关于AudioTrack的一些话题,那么你可能享受它带来的好处,例如低延迟(在STATIC(静态)模式),能够生成流式音频

Android音频系统之音频框架(转http://blog.csdn.net/uiop78uiop78/article/details/8796492)

1.1 音频框架 转载请注明,From LXS, http://blog.csdn.net/uiop78uiop78/article/details/8796492 Android的音频系统在很长一段时间内都是外界诟病的焦点.的确,早期的Android系统在音频处理上相比于IOS有一定的差距,这也是很多专业的 音乐播放软件开发商没有推出Android平台产品的一个重要原因.但这并不代表它的音频框架一无是处,相反,基于Linux系统的Android平台有 很多值得我们学习的地方. 1.1.1 Li

Android Touch系统简介(二):实例详解onInterceptTouchEvent与onTouchEvent的调用过程

上一篇文章主要讲述了Android的TouchEvent的分发过程,其中有两个重要的函数:onInterceptTouchEvent和onTouchEvent,这两个函数可被重装以完成特定的逻辑.onInterceptTouchEvent的定义为于ViewGroup中,默认返回值为false,表示不拦截TouchEvent.onTouchEvent的定义位于View中,当ViewGroup要调用onTouchEvent时,会利用super.onTouchEvent.ViewGroup调用onTo

Android音频系统之AudioFlinger(四)【转】

Android音频系统之AudioFlinger(四) 分类: ALSA/Audio 2014-06-12 17:37 195人阅读 评论(0) 收藏 举报 1.1.1 AudioMixer 每一个MixerThread都有一个唯一对应的AudioMixer(在MixerThread中用mAudioMixer表示),它的作用如其名所表示的,就是为了完成音频的混音操作.   图 13?14 MixerThread示意图 如上图,MixerThread对外开放的接口主要涉及到Parameter(比如