Android深入浅出之Audio第三部分Audio Policy[1]

转自:http://blog.csdn.net/innost/article/details/6158960

Android深入浅出之Audio第三部分Audio Policy[1]

目的

上回我们说了AudioFlinger(AF),总感觉代码里边有好多东西没说清楚,心里发毛。就看了看AF的流程,我们敢说自己深入了解了Android系统吗?AudioPolicyServiceAPS)是个什么东西?为什么要有它的存在?下层的Audio HAL层又是怎么结合到Android中来的?更有甚者,问个实在问题:插入耳机后,声音又怎么从最开始的外放变成从耳机输出了?调节音量的时候到底是调节Music的还是调节来电音量呢?这些东西,我们在AF的流程中统统都没讲到。但是这些他们又是至关重要的。从我个人理解来看,策略(Policy)比流程更复杂和难懂。

当然,遵循我们的传统分析习惯,得有一个切入点,否则我们都不知道从何入手了。

这里的切入点将是:

lAFAPS系统第一次起来后,到底干了什么。

l检测到耳机插入事件后,AFAPS的处理。

大家跟着我一步步来看,很快就发现,啊哈,APS也不是那么难嘛。

另外,这次代码分析的格式将参考《Linux内核情景分析》的样子,函数调用的解析将采用深度优先的办法,即先解释所调用的函数,然后再出来继续讲。

我曾经数度放弃分析APS,关键原因是我没找到切入点,只知道代码从头看到尾!

AFAPS的诞生

这个东西,已经说得太多了。在framework/base/media/MediaServer/Main_MediaServer中。

我们看看。

int main(int argc, char** argv)

{

sp<ProcessState> proc(ProcessState::self());

sp<IServiceManager> sm = defaultServiceManager();

//先创建AF

AudioFlinger::instantiate();

//再创建APS

AudioPolicyService::instantiate();

ProcessState::self()->startThreadPool();

IPCThreadState::self()->joinThreadPool();

}

2.1 new AudioFlinger

前面说过,instantiate内部会实例化一个对象,那直接看AF的构造函数。

AudioFlinger::AudioFlinger()

: BnAudioFlinger(),//基类构造函数

mAudioHardware(0), mMasterVolume(1.0f), mMasterMute(false), mNextThreadId(0)

{

注意mAudioHardwaremNextThreadId

mHardwareStatus = AUDIO_HW_IDLE;

//创建audioHAL代表

mAudioHardware = AudioHardwareInterface::create();

mHardwareStatus = AUDIO_HW_INIT;

//下面这些不至于会使用APS吧?APS还没创建呢!

if (mAudioHardware->initCheck() == NO_ERROR) {

setMode(AudioSystem::MODE_NORMAL);

setMasterVolume(1.0f);

setMasterMute(false);

}

}

感觉上,AF的构造函数就是创建了一个最重要的AudioHardWareHAL代表。

其他好像是没干什么策略上的事情。

不过:AF创建了一个AudioHardwareHAL对象。注意整个系统就这一个AudioHardware了。也就是说,不管是线控耳机,蓝牙耳机,麦克,外放等等,最后都会由这一个HAL统一管理。

再看APS吧。

2.2 new AudioPolicyService

AudioPolicyService::AudioPolicyService()

: BnAudioPolicyService() , mpPolicyManager(NULL)

{

//mpPolicyManager?策略管理器?可能很重要

char value[PROPERTY_VALUE_MAX];

// TonePlayback?播放铃声的?为什么放在这里?以后来看看

mTonePlaybackThread = new AudioCommandThread(String8(""));

// Audio Command?音频命令?看到Command,我就想到设计模式中的Command模式了

//Android尤其是MediaPlayerService中大量使用了这种模式。

mAudioCommandThread = new AudioCommandThread(String8("ApmCommandThread"));

#if (defined GENERIC_AUDIO) || (defined AUDIO_POLICY_TEST)

//注意AudioPolicyManagerBase的构造函数,把this传进去了。

mpPolicyManager = new AudioPolicyManagerBase(this);

//先假设我们使用GenericAudio设备吧。

#else

...

#endif

// 根据系统属性来判断摄像机是否强制使用声音。这个...为什么会放在这里?

//手机带摄像机好像刚出来的时候,为了防止偷拍,强制按快门的时候必须发出声音

//就是这个目的吧?

property_get("ro.camera.sound.forced", value, "0");

mpPolicyManager->setSystemProperty("ro.camera.sound.forced", value);

}

so easy!,不至于吧?我们不应该放过任何一个疑问!这么多疑问,先看哪个呢?这里分析的是Audio Policy,而构造函数中又创建了一个AudioPolicyManagerBase,而且不同厂商还可以实现自己的AudioPolicyManager,看来这个对于音频策略有至关重要的作用了。

不得不说的是,Android代码中的这些命名在关键地方上还是比较慎重和准确的。

另外,AudioPolicyManagerBase的构造函数可是把APS传进去了,看来又会有一些回调靠APS了。真绕。

2.3 AudioPolicyManagerBase

代码位置在framework/base/libs/audioflinger/AudioPolicyManagerBase.cpp

AudioPolicyManagerBase::AudioPolicyManagerBase(AudioPolicyClientInterface *clientInterface)

:

mPhoneState(AudioSystem::MODE_NORMAL), ---->这里有电话的状态?

mRingerMode(0),

mMusicStopTime(0),

mLimitRingtoneVolume(false)

{

[--->mPhoneState(AudioSystem::MODE_NORMAL)]

AudioSystem其实是窥视Android如何管理音频系统的好地方。位置在

framework/base/include/media/AudioSystem.h中,定义了大量的枚举之类的东西来表达Google对音频系统的看法。我们只能见招拆招了。

下面是audio_mode的定义。这里要注意一个地方:

这些定义都和SDK中的JAVA层定义类似。实际上应该说先有C++层的定义,然后再反映到JAVA层中。但是C++层的定义一般没有解释说明,而SDK中有。所以我们不能不面对的一个痛苦现实就是:常常需要参考SDK的说明才能搞明白到底是什么。

关于C++AudioSystem这块,SDK的说明在AudioManager中。

enum audio_mode {

//解释参考SDK说明,以下不再说明

MODE_INVALID = -2, //无效mode

MODE_CURRENT = -1,//当前mode,和音频设备的切换(路由)有关

MODE_NORMAL = 0,//正常mode,没有电话和铃声

MODE_RINGTONE,//收到来电信号了,此时会有铃声

MODE_IN_CALL,//电话mode,这里表示已经建立通话了

NUM_MODES// Android大量采用这种技巧来表示枚举结束了。

};

好,继续:

...

mPhoneState(AudioSystem::MODE_NORMAL), ---->这里有电话的状态?

mRingerMode(0),

mMusicStopTime(0),

mLimitRingtoneVolume(false)

{

mpClientInterface = clientInterface;//BT,保存APS对象。

//forceUse?这是个什么玩意儿?

for (int i = 0; i < AudioSystem::NUM_FORCE_USE; i++) {

mForceUse[i] = AudioSystem::FORCE_NONE;

}

[---->AudioSystem::FORCE_NONEAudioSystem::NUM_FORCE_USE]

注意,这里有两个枚举,太无耻了。先看看FORCE_NONE这个

enum forced_config {强制_配置,看名字好像是强制使用设备吧,比如外放,耳机,蓝牙等

FORCE_NONE,

FORCE_SPEAKER,

FORCE_HEADPHONES,

FORCE_BT_SCO,

FORCE_BT_A2DP,

FORCE_WIRED_ACCESSORY,

FORCE_BT_CAR_DOCK,

FORCE_BT_DESK_DOCK,

NUM_FORCE_CONFIG,

FORCE_DEFAULT = FORCE_NONE //这个,太无聊了。

};

再看看AudioSystem::NUM_FORCE_USE这个

enum force_use {

FOR_COMMUNICATION,//这里是for_xxx,不是force_xxx

FOR_MEDIA,

FOR_RECORD,

FOR_DOCK,

NUM_FORCE_USE

};

不懂,两个都不懂。为何?能猜出来什么吗?也不行。因为我们没找到合适的场景!那好吧,我们去SDK找找。恩

我看到AudioManager这个函数setSpeakerphoneOn (boolean on)。好吧,我

这么调用

setSpeakerphoneOn(true),看看实现。

这次我没再浪费时间了,我用一个新的工具coolfind,把搜索framework目录,寻找*.java文件,匹配字符串setSpeakerphone。终于,我在

framework/base/media/java/android/media/AudioService.java中找到了。

public void setSpeakerphoneOn(boolean on){

if (!checkAudioSettingsPermission("setSpeakerphoneOn()")) {

return;

}

if (on) {

//看到这里,是不是明白十之八九了?下面这个调用是:

//强制通话使用speaker!原来是这么个意思!

AudioSystem.setForceUse(AudioSystem.FOR_COMMUNICATION,

AudioSystem.FORCE_SPEAKER);

mForcedUseForComm = AudioSystem.FORCE_SPEAKER;

} else {

AudioSystem.setForceUse(AudioSystem.FOR_COMMUNICATION,

AudioSystem.FORCE_NONE);

mForcedUseForComm = AudioSystem.FORCE_NONE;

}

}

好了,说点题外话,既然Android源码都放开给我们了,有什么理由我们不去多搜搜呢?上网google也是搜,查源代码也是一样吗。不过我们要有目的:就是找到一个合适的使用场景。

force_useforce_config就不用我再解释了吧?

[--->AudioPolicyManagerBase::AudioPolicyManagerBase]

...

//下面这个意思就是把几种for_use的情况使用的设备全部置为NONE

//比如设置FOR_MEDIA的场景,使用的设备就是FORCE_NONE

for (int i = 0; i < AudioSystem::NUM_FORCE_USE; i++) {

mForceUse[i] = AudioSystem::FORCE_NONE;

}

// 目前可以的输出设备,耳机和外放

mAvailableOutputDevices = AudioSystem::DEVICE_OUT_EARPIECE |

AudioSystem::DEVICE_OUT_SPEAKER;

//目前可用的输入设备,内置MIC

mAvailableInputDevices = AudioSystem::DEVICE_IN_BUILTIN_MIC;

又得来看看AudioSystem是怎么定义输入输出设备的了。

[--->mAvailableOutputDevices = AudioSystem::DEVICE_OUT_EARPIECE]

enum audio_devices {

// output devices

DEVICE_OUT_EARPIECE = 0x1,

DEVICE_OUT_SPEAKER = 0x2,

DEVICE_OUT_WIRED_HEADSET = 0x4,

DEVICE_OUT_WIRED_HEADPHONE = 0x8,

DEVICE_OUT_BLUETOOTH_SCO = 0x10,

DEVICE_OUT_BLUETOOTH_SCO_HEADSET = 0x20,

DEVICE_OUT_BLUETOOTH_SCO_CARKIT = 0x40,

DEVICE_OUT_BLUETOOTH_A2DP = 0x80,

DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES = 0x100,

DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER = 0x200,

DEVICE_OUT_AUX_DIGITAL = 0x400,

DEVICE_OUT_DEFAULT = 0x8000,

DEVICE_OUT_ALL = (DEVICE_OUT_EARPIECE | DEVICE_OUT_SPEAKER |

DEVICE_OUT_WIRED_HEADSET | DEVICE_OUT_WIRED_HEADPHONE | DEVICE_OUT_BLUETOOTH_SCO | DEVICE_OUT_BLUETOOTH_SCO_HEADSET |DEVICE_OUT_BLUETOOTH_SCO_CARKIT |

DEVICE_OUT_BLUETOOTH_A2DP | DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES |

DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER | DEVICE_OUT_AUX_DIGITAL | DEVICE_OUT_DEFAULT),

DEVICE_OUT_ALL_A2DP = (DEVICE_OUT_BLUETOOTH_A2DP |

DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES |DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER),

// input devices

DEVICE_IN_COMMUNICATION = 0x10000,

DEVICE_IN_AMBIENT = 0x20000,

DEVICE_IN_BUILTIN_MIC = 0x40000,

DEVICE_IN_BLUETOOTH_SCO_HEADSET = 0x80000,

DEVICE_IN_WIRED_HEADSET = 0x100000,

DEVICE_IN_AUX_DIGITAL = 0x200000,

DEVICE_IN_VOICE_CALL = 0x400000,

DEVICE_IN_BACK_MIC = 0x800000,

DEVICE_IN_DEFAULT = 0x80000000,

DEVICE_IN_ALL = (DEVICE_IN_COMMUNICATION | DEVICE_IN_AMBIENT |

DEVICE_IN_BUILTIN_MIC |DEVICE_IN_BLUETOOTH_SCO_HEADSET | DEVICE_IN_WIRED_HEADSET |

DEVICE_IN_AUX_DIGITAL | DEVICE_IN_VOICE_CALL | DEVICE_IN_BACK_MIC |

DEVICE_IN_DEFAULT)

};

一些比较容易眼花的东西我标成红色的了。这么多东西,不过没什么我们不明白的了。

得嘞,继续走。

[--->AudioPolicyManagerBase::AudioPolicyManagerBase]

// 目前可以的输出设备,又有耳机又有外放,配置很强悍啊。

//注意这里是OR操作符,最终mAvailableOutputDevices = 0X3

mAvailableOutputDevices = AudioSystem::DEVICE_OUT_EARPIECE |

AudioSystem::DEVICE_OUT_SPEAKER;

//目前可用的输入设备,内置MICmAvailableInputDevices0x4000,不过我们不关注input

mAvailableInputDevices = AudioSystem::DEVICE_IN_BUILTIN_MIC;

...

下面东西就很少了,我们一气呵成。

//创建一个AudioOutputDescriptor,并设置它的device为外设0x2

AudioOutputDescriptor *outputDesc = new AudioOutputDescriptor();

outputDesc->mDevice = (uint32_t)AudioSystem::DEVICE_OUT_SPEAKER;

//调用APSopenOutput,得到一个mHardwareOutput东东。这是个int

//不过保不准是一个指针也不一定喔。

//而且,下面的参数都是指针类型(flags除外),难道?有人会改value吗?

mHardwareOutput = mpClientInterface->openOutput(&outputDesc->mDevice,

&outputDesc->mSamplingRate,

&outputDesc->mFormat,

&outputDesc->mChannels,

&outputDesc->mLatency,

outputDesc->mFlags);

//这个...估计是把int和指针加入到一个map了,方便管理。

addOutput(mHardwareOutput, outputDesc);

//不知道干嘛,待会看。

setOutputDevice(mHardwareOutput, (uint32_t)AudioSystem::DEVICE_OUT_SPEAKER, true);

//不知道干嘛,待会看。

updateDeviceForStrategy();

好了,上面还有一系列函数,等着我们调用呢。我们一个一个看。

提前说一下,这块可是AudioManagerBase的核心喔。

[---->AudioOutputDescriptor *outputDesc = new AudioOutputDescriptor()]

AudioOutputDescriptor是个什么?我不是神,我也得看注释。

// descriptor for audio outputs. Used to maintain current configuration of each opened audio output

// and keep track of the usage of this output by each audio stream type.

明白了么?大概意思就是它,是这么一个东西:

l描述audio输出的,可以用来保存一些配置信息。

l跟踪音频stream类型使用这个output的一些情况。

没明白吧?以后碰到场景就明白了。

它的构造函数干了如下勾当:

AudioPolicyManagerBase::AudioOutputDescriptor::AudioOutputDescriptor()

: mId(0), mSamplingRate(0), mFormat(0), mChannels(0), mLatency(0),

mFlags((AudioSystem::output_flags)0), mDevice(0), mOutput1(0), mOutput2(0)

{}

//很好,统统都置零了。上面这些东西不用我解释了吧?命名规则也可以看出来。

OKgo on.

[--->mHardwareOutput = mpClientInterface->openOutput()]:

这里调用的是APSopenOutput,看看去:

[--->AudioPolicyService::openOutput]

audio_io_handle_t AudioPolicyService::openOutput(uint32_t *pDevices,

uint32_t *pSamplingRate,

uint32_t *pFormat,

uint32_t *pChannels,

uint32_t *pLatencyMs,

AudioSystem::output_flags flags)

{

sp<IAudioFlinger> af = AudioSystem::get_audio_flinger();

//娘希匹,搞到AF去了

return af->openOutput(pDevices, pSamplingRate, (uint32_t *)pFormat, pChannels,

pLatencyMs, flags);

}

[----->AudioFlinger::openOutput]

int AudioFlinger::openOutput(uint32_t *pDevices,

uint32_t *pSamplingRate,

uint32_t *pFormat,

uint32_t *pChannels,

uint32_t *pLatencyMs,

uint32_t flags)

{

//我们思考下传进来的值吧

//*pDevices=0x2,代表外放

//其他都是0。 嘿嘿,有了值,这不就知道下面该怎么走了吗?

status_t status;

PlaybackThread *thread = NULL;

mHardwareStatus = AUDIO_HW_OUTPUT_OPEN;

uint32_t samplingRate = pSamplingRate ? *pSamplingRate : 0;

uint32_t format = pFormat ? *pFormat : 0;

uint32_t channels = pChannels ? *pChannels : 0;

uint32_t latency = pLatencyMs ? *pLatencyMs : 0;

Mutex::Autolock _l(mLock);

//HAL对象得到一个AudioStreamOut,传进去的值会改吗?

AudioStreamOut *output = mAudioHardware->openOutputStream(*pDevices,

(int *)&format,

&channels,

&samplingRate,

&status);

mHardwareStatus = AUDIO_HW_IDLE;

if (output != 0) {

//走哪个分支?我把答案告诉大家吧。

//刚才那个mAudioHardware->openOutputStream确实会更改指针对应的value

//当然,我们说了,AF使用的是GENERICAudio硬件。大家有兴趣可以去看看它的实现。

//我待会再贴出它的内容。反正到这里。

//那几个值变成:formatPCM_16_BITchannels2samplingRate44100

//这样的话,那只能走else分支了。

if ((flags & AudioSystem::OUTPUT_FLAG_DIRECT) ||

(format != AudioSystem::PCM_16_BIT) ||

(channels != AudioSystem::CHANNEL_OUT_STEREO)) {

thread = new DirectOutputThread(this, output, ++mNextThreadId);

} else {

//还记得前两节分析的同学,看到这里是不是明白了?恩,原来

//open一个Output,就会在AF中创建一个混音线程。设计得真好。

//想象下,所有设置为外放的程序,它的输出都是这个外放stream混音线程来工作

//所有设置为耳机的程序,它的输出都是这个耳机stream混音线程来完成。

//为什么对stream特加强调呢,没看见

//我们调用的是mAudioHardware->openOutputStream(0x2,,,)嘛。返回的

//是一个AudioStreamOut,可不是设备喔。Android把这些个东西都交给HAL层去实现了。

//不用自己来管理系统上有什么耳机,外设,蓝牙真实设备之类的东东,它反正用AudioStreamOut来表示它想要的就可以了。例如GenericAudio Hal只支持一个OutputStream--> only my opinion

thread = new MixerThread(this, output, ++mNextThreadId);

}

//好了,又多得了一个线程,

mPlaybackThreads.add(mNextThreadId, thread);

if (pSamplingRate) *pSamplingRate = samplingRate;

if (pFormat) *pFormat = format;

if (pChannels) *pChannels = channels;

if (pLatencyMs) *pLatencyMs = thread->latency();

//从这里返回的是混音线程的索引。

return mNextThreadId;

}

return 0;//如果没创建成功线程,则返回零。

}

好,我们回到AudioManagerBase中。

[--->AudioPolicyManagerBase::AudioPolicyManagerBase]

mHardwareOutput = mpClientInterface->openOutput(&outputDesc->mDevice,

&outputDesc->mSamplingRate,

&outputDesc->mFormat,

&outputDesc->mChannels,

&outputDesc->mLatency,

outputDesc->mFlags);

//上面实际就返回一个线程index。我有点疑惑,难道APS就只这么一个实际是线程index的东西就就行了吗?虽然它把这个index当成hardware的标识了。

//这个...估计是把int和指针加入到一个map了,方便管理。不看了。

addOutput(mHardwareOutput, outputDesc);

//不知道干嘛,待会看。

setOutputDevice(mHardwareOutput, (uint32_t)AudioSystem::DEVICE_OUT_SPEAKER, true);

[--->setOutputDevice(mHardwareOutput,...)]

这个函数,很重要!另外,再传点技巧。不要老在source insight中后退后退了,直接找到window菜单,里边列出了最近打开的文件,找到我们的AudioManagerBase.cpp,不就行了吗?

void AudioPolicyManagerBase::setOutputDevice(audio_io_handle_t output, uint32_t device, bool force, int delayMs)

{

//注意我们的参数:

// output = 1

//deviceAudioSystem::DEVICE_OUT_SPEAKER

// forcetruedelayMs用默认值0

//map吧?刚才通过addOutput已经加进去了

AudioOutputDescriptor *outputDesc = mOutputs.valueFor(output);

if (outputDesc->isDuplicated()) {

setOutputDevice(outputDesc->mOutput1->mId, device, force, delayMs);

setOutputDevice(outputDesc->mOutput2->mId, device, force, delayMs);

return;

}

//还记得addOutput前设置的device吗?对了,为0X3,外放|耳机

uint32_t prevDevice = (uint32_t)outputDesc->device();

现在设置的是外设,

if ((device == 0 || device == prevDevice) && !force) {

return;

}

//喔,设置这个outputDesc为外放

outputDesc->mDevice = device;

popCount2,因为device=0x2=0010

//另外,我对下面这个output== mHardwareOutput尤其感兴趣。还记得我们刚才的疑问吗?

// mHardwareOutput实际上是AF返回的一个线程索引,那AMB怎么根据这样一个东西来

//管理所有的线程呢?果然,这里就比较了output是不是等于最初创建的线程索引

//这就表明。虽然只有这么一个mHardwareOutput,但实际上还是能够操作其他output的!

if (output == mHardwareOutput && AudioSystem::popCount(device) == 2) {

setStrategyMute(STRATEGY_MEDIA, true, output);

usleep(outputDesc->mLatency*2*1000);

}

// 晕,又冒出来一个AudioParameter,不过意思却很明白

//说我们要设置路由,新的输出设备为外放

//等我们以后讲由外放切换到耳机,再来看这个问题。

AudioParameter param = AudioParameter();

param.addInt(String8(AudioParameter::keyRouting), (int)device);

mpClientInterface->setParameters(mHardwareOutput, param.toString(), delayMs);

// update stream volumes according to new device

applyStreamVolumes(output, device, delayMs);

// if changing from a combined headset + speaker route, unmute media streams

if (output == mHardwareOutput && AudioSystem::popCount(prevDevice) == 2) {

//这里说,把media的音量置为0。以后再说。

setStrategyMute(STRATEGY_MEDIA, false, output, delayMs);

}

}

好了,返回了。

setOutputDevice(mHardwareOutput, (uint32_t)AudioSystem::DEVICE_OUT_SPEAKER, true);

这个调研,更新了mHardwareOutput对应的输出路由设备,而且还发了一个命令给APS,说你给我更新对应混音线程的输出路由设备。

[--->AudioPolicyManagerBase::AudioPolicyManagerBase]

.....

addOutput(mHardwareOutput, outputDesc);

setOutputDevice(mHardwareOutput, (uint32_t)AudioSystem::DEVICE_OUT_SPEAKER,

true);

//只剩下最后一个函数了

updateDeviceForStrategy();

[----->updateDeviceForStrategy()]

void AudioPolicyManagerBase::updateDeviceForStrategy()

{

for (int i = 0; i < NUM_STRATEGIES; i++) {

mDeviceForStrategy[i] = getDeviceForStrategy((routing_strategy)i, false);

}

}

晕,又出来一个枚举。我们看看

[---->for (int i = 0; i < NUM_STRATEGIES; i++)]

NUM_STRATEGIEShardware/libhardware_legacy/include/hardware_legacy/

AudioPolicyManagerBase.h中定义。

enum routing_strategy {

//好像很好理解

STRATEGY_MEDIA,

STRATEGY_PHONE,//通话音吗?

STRATEGY_SONIFICATION,//除了其他三个外的,可以是铃声,提醒声等。

STRATEGY_DTMF,//好像是拨号音

NUM_STRATEGIES

};

这个,反正我在SDK上没找到对应说明,我们待到以后看看会不会柳暗花明呢?

[----->getDeviceForStrategy((routing_strategy)i, false)]

看这个函数名的意思是,为各种策略找到它对应的设备。

uint32_t AudioPolicyManagerBase::getDeviceForStrategy(routing_strategy strategy, bool fromCache)

{

//fromCachefalse

//放眼望去,这个函数好像涉及到很对策略方面的事情。

//我们大概讲解下,至于系统为什么要这么做,问Google吧。

uint32_t device = 0;

switch (strategy) {

case STRATEGY_DTMF:

if (mPhoneState != AudioSystem::MODE_IN_CALL) {

//如果在打电话过程中,你再按按键,则和MEDIA走一个设备

device = getDeviceForStrategy(STRATEGY_MEDIA, false);

break;

}

//注意这里没有break,所以在其他mode下,DTMFPHONE用一个策略

case STRATEGY_PHONE:

//还得判断用户是不是强制使用了输出设备。

switch (mForceUse[AudioSystem::FOR_COMMUNICATION]) {

case AudioSystem::FORCE_BT_SCO:

if (mPhoneState != AudioSystem::MODE_IN_CALL || strategy != STRATEGY_DTMF) {

device = mAvailableOutputDevices &

AudioSystem::DEVICE_OUT_BLUETOOTH_SCO_CARKIT;

if (device) break;

}

device = mAvailableOutputDevices &

AudioSystem::DEVICE_OUT_BLUETOOTH_SCO_HEADSET;

if (device) break;

device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_BLUETOOTH_SCO;

if (device) break;

// if SCO device is requested but no SCO device is available, fall back to default

// case

// FALL THROUGH

//我们还记得强制设置那里吗?对了,此时都是FORCE_NONE

//而且,mAvailableOutputDevices0X3 (外放|耳机)

default:// FORCE_NONE

device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_WIRED_HEADPHONE;

if (device) break;

device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_WIRED_HEADSET;

if (device) break;

//看,下面这句会成立。啥意思?如果有耳机的话,那么输出设备就是耳机

//太正确了。实际手机是不是就是这样的呢?

device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_EARPIECE;

break;

//再验证下我们刚才说的,如果强制使用外放的话,

case AudioSystem::FORCE_SPEAKER:

if (mPhoneState != AudioSystem::MODE_IN_CALL || strategy != STRATEGY_DTMF) {

device = mAvailableOutputDevices &

AudioSystem::DEVICE_OUT_BLUETOOTH_SCO_CARKIT;

if (device) break;

}

//果然,会强制使用外放。

device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_SPEAKER;

break;

}

break;

case STRATEGY_SONIFICATION://分析方法同上,我不说了。

if (mPhoneState == AudioSystem::MODE_IN_CALL) {

device = getDeviceForStrategy(STRATEGY_PHONE, false);

break;

}

device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_SPEAKER;

// 同样没有break,说明SONIFICATIONMEDIA策略影响。

case STRATEGY_MEDIA: {

uint32_t device2 = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_AUX_DIGITAL;

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 & AudioSystem::DEVICE_OUT_SPEAKER;

}

//假设我们没有从SONIFICATION下来,那么device最终会= DEVICE_OUT_SPEAKER

//假设我们从SONIFICATION下来,那么device还是等于DEVICE_OUT_SPEAKER

//奇怪,如果有耳机的话为何会走外放呢?普通耳机和线控耳机还能区分?

device |= device2;

} break;

default:

break;

}

return device;

}

好了,回到

[---->AudioPolicyManagerBase::updateDeviceForStrategy()]

void AudioPolicyManagerBase::updateDeviceForStrategy()

{

for (int i = 0; i < NUM_STRATEGIES; i++) {

mDeviceForStrategy[i] = getDeviceForStrategy((routing_strategy)i, false);

}

}

这个函数完了,表明各种策略下使用的对应设备也准备好了。

真爽,一路回去,APS的构造就完了。

留个纪念:

AudioPolicyManagerBase::AudioPolicyManagerBase(AudioPolicyClientInterface *clientInterface)

{

....

updateDeviceForStrategy();

}

AudioPolicyService::AudioPolicyService()

: BnAudioPolicyService() , mpPolicyManager(NULL)

{

#if (defined GENERIC_AUDIO) || (defined AUDIO_POLICY_TEST)

mpPolicyManager = new AudioPolicyManagerBase(this);

LOGV("build for GENERIC_AUDIO - using generic audio policy");

...

#endif

property_get("ro.camera.sound.forced", value, "0");

mpPolicyManager->setSystemProperty("ro.camera.sound.forced", value);

}

2.4总结

总结下吧,AF,APS都创建完了,得到什么了吗?下面按先后顺序说说。

lAF创建了一个代表HAL对象的东西

lAPS创建了两个AudioCommandThread,一个用来处理命令,一个用来播放tone。我们还没看。

lAPS同时会创建AudioManagerBase,做为系统默认的音频管理

lAMB集中管理了策略上面的事情,同时会在AFopenOutput中创建一个混音线程。同时,AMB会更新一些策略上的安排。

另外,我们分析的AMBGeneric的,但不同厂商可以实现自己的策略。例如我可以设置只要有耳机,所有类型声音都从耳机出。

上面关于AMB方面,我们还只是看了看它的代码,还没有一个实际例子来体会。

查看评论
12楼 alec5239 3天前 10:20发表[回复] [引用] [举报]
Hi:
你好, 请问在android下如何实现: 电话通话时音从听筒播放,同时ALARM,SYSTEM等音从扬声器同时播放吗?
我现在测试是两种流要么同时从听筒播放,要么同时从扬声器播放. 不能分开他们.
谢谢
Re: Innost 3天前 11:01发表[回复] [引用] [举报]
回复alec5239:您看明白上面的流程了吗?看明白了,就知道了。呵呵
设备路由只能针对所有数据,无法单独区别。
Re: alec5239 3天前 11:25发表[回复] [引用] [举报]
回复Innost:只是了解了一些, 好象是只能根据选择的策略,再选output设备. 没有办法根据声道不同来选择不同输出. 谢谢你
11楼 billpig 2012-05-07 11:14发表[回复] [引用] [举报]
您好,请问下,如果要使音频同时从听筒及扬声器输出,是不是无需用到DuplicatingThread,我看书上写的是DuplicatingThread是需要与蓝牙结合起来使用,但如果仅仅只是从听筒及扬声器输出,是否仅仅只用MixerThread就行?
Re: Innost 2012-05-07 11:24发表[回复] [引用] [举报]
回复billpig:您再仔细看硬件结构图,蓝牙是单独一个输出设备芯片,所以需要duplicate。耳机和外设直接通过一个芯片就可以输出了。所以无需duplicate。这是硬件决定的。mixerthread只是用于多track的混音,和输出设备无关。directthread就不用混音。
Re: billpig 2012-05-07 12:32发表[回复] [引用] [举报]
回复Innost:谢谢您的答复,我不知道我这么理解对不对:如果要使音频同时从听筒及扬声器输出,我就在相应的getDeviceForStrategy中把听筒及扬声器的设备赋值给device即可(即device = DEVICE_OUT_EARPIECE | DEVICE_OUT_SPEAKER)?
Re: Innost 2012-05-07 12:47发表[回复] [引用] [举报]
回复billpig:是的。这个操作只是通知硬件打开相应的输出通道。您只要看看Audiopolicymanagerbase的代码就可以了。其实不必要问我的。源码证明一切 呵呵。
Re: billpig 2012-05-07 13:06发表[回复] [引用] [举报]
回复Innost:谢谢您,之前试了另外一种方式:音乐从听筒播放,电话音从扬声器播放,遇到了点问题,所以才不大确定。谢谢您的指导,我再研究下!
10楼 kris_fei 2012-02-08 18:33发表[回复] [引用] [举报]
请教下: 系统设置音量,调用process__OneTrack16BitsStereoNoResampling处理音量之后,是如何调用到HAL让kernel去设置音量?谢谢。
Re: Innost 2012-02-08 21:19发表[回复] [引用] [举报]
回复kris_fei:AP层音量是软件音量,即修改buffer的值。比如我们写到audiohardward的是PCM,那么PCM值就是音量。AP这边调节音量就是修改PCM值,例如乘以0.5,你可以把值打出来看。
只有电话音量是需要发送到BP的,即modem册。你是买书看了还是只看博客?建议买书。书里边非常详细。谢谢
9楼 kris_fei 2012-02-04 15:03发表[回复] [引用] [举报]
//还记得addOutput前设置的device吗?对了,为0X3,外放|耳机
uint32_t prevDevice = (uint32_t)outputDesc->device();
这句话返回的不是speak吗,怎么是外放和耳机呢?

在AudioPolicyManagerBase构造函数的赋值如下:
outputDesc->mDevice = (uint32_t)AudioSystem::DEVICE_OUT_SPEAKER;
Re: Innost 2012-02-04 16:22发表[回复] [引用] [举报]
回复kris_fei:构造函数设置的是初始值,中间添加设备时候会修改的。不可能都是返回外放设备吧?多想想就明白了。
Re: kris_fei 2012-02-06 10:03发表[回复] [引用] [举报]
回复Innost:这样有点强词夺理了吧,您上下文的意思也是想表达初始化时的设备,如果这样像蓝牙或者其他设备都可以举例了,所以我觉得可以的话最好说明下,免得其他读者和我一样看到了迷糊。
BTW,我只是提个建议。
Re: Innost 2012-02-06 20:59发表[回复] [引用] [举报]
回复kris_fei:恩,多谢提醒。有可能是bug。你看得还挺仔细的。等下版再改。
8楼 zzobin 2011-11-08 23:37发表[回复] [引用] [举报]
我在Mediaplayer里从java层跟了下代码,_start()去调用JNI里的Imediaplayer接口的start(),只是有点好奇,这个虚拟的start()函数接下来通过什么途径运行到getOutput函数,谢谢!
Re: Innost 2011-11-09 09:22发表[回复] [引用] [举报]
回复zzobin:你trace一下代码不就行了?都到这一步了,加油!
7楼 zzobin 2011-11-08 15:30发表[回复] [引用] [举报]
楼主大大,请教个问题:getOutput函数最终由java层调用吗?
Re: Innost 2011-11-08 15:36发表[回复] [引用] [举报]
回复zzobin:啥意思?怎么会由java层调用呢?这里讲得都是native层的。连JNI层都没涉及到啊...
6楼 yanweiqi 2011-07-29 16:21发表[回复] [引用] [举报]
楼主qq是多少?交流一下。
5楼 koyyhm 2011-06-02 17:51发表[回复] [引用] [举报]
请问耳机检测或者说耳机触发的事件也是在这部分实现的吗?[e07]
Re: lightsoure 2011-06-03 16:37发表[回复] [引用] [举报]
回复 koyyhm:LZ写的东西 都很棒哦~
学习
一般都是LINUX, 音频驱动部分中对于耳机插入中断做最开始的判断。改变通道输出耳机。
Re: Innost 2011-06-03 11:27发表[回复] [引用] [举报]
回复 koyyhm:耳机检测肯定不是在这啊,我收到了广播才干这些事情。你要想知道的话,得去查是谁发出这个广播的。源码里边搜索一下就行了
4楼 lhzhang1985 2011-05-10 14:57发表[回复] [引用] [举报]
[e03]
3楼 rnmichelle 2011-04-18 16:13发表[回复] [引用] [举报]
强!学习了.[e01]
2楼 orcvoodoo 2011-02-25 15:29发表[回复] [引用] [举报]
LZ建群吧~[e01]
Re: Innost 2011-02-27 11:04发表[回复] [引用] [举报]
回复 orcvoodoo:建什么群?qq群?
1楼 Siobhan 2011-02-17 13:24发表[回复] [引用] [举报]
[e03][e01]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值