该系列文章总纲链接:专题分纲目录 Android Framework 音频子系统
本章关键点总结 & 说明:
本章节主要关注➕ 以上思维导图左上 耳麦插拔 部分中的 声音通道切换 部分 即可。主要说明了声道切换的原理和声道切换的流程分析。
1 耳麦插拔 声音通道切换 原理说明
1.1 切换声音通道情景分析
这里分成两种情况进行分析,一种是USB声卡的插入,另一种是primary设备上插入耳麦。
USB声卡插入:
- 从配置文件audio_policy中可以找到usb中对应的modle,一定有outputs包含usb_accessory、usb_device这类output。插上USB声卡后会创建output和对应的playbackthread。(usb_accessory、usb_device 这两个output各对应一个)。
- 之前与板载声卡建立联系的playBackThread线程,要切换成USB创建的playBackThread,APP的AudioTrack从原来的playbackthread/output模式切换到新的playbackthread/output。在新的playBackThread中,每个APP创建对应的Track。
- 在output中选择Device,重新做一些设置,决定从耳机还是喇叭播放声音。
在primary中插上耳机:
- 无需创建output和playbackthread,因为这种情况下他们所涉及的线程并没有改变,所以不需要重新去创建output与playBackThread。
- 无需切换output。
- 在原来的output中选择Device(Headset)。
流程上相差不大,首先需要判断一下,是否需要创建output,播放的声音是否需要新的线程进行处理,最后在output中选择device。
1.2 切换声音通道 核心三步骤
硬件 插上耳麦发生中断, 在中断处理程序中设置声卡让声音从耳机中输出,驱动程序上报音频拔插事件,该事件为某个device插入或拔出,接下来把输出通道的选择权交给android系统,由Android系统进行声音通道的切换操作。Android系统切换声音通道的
3个核心步骤如下:
@1 checkOutputsForDevice
针对该device, 打开新的output, 创建新的playbackthread。从audio_policy.conf中确定"本该有多少个output"可以支持它,mOutputs表示"已经打开的output",两者对比即可确定"尚未打开的output"
@2 checkOutputForAllStrategies / checkOutputForStrategy
对所有的strategy分组声音,判断是否需要迁移到新的output, 如果需要则迁移对应Track到新的output,这里涉及2个判断
@@2.1 判断是否需要迁移:
- 对于该strategy, 得到它的oldDevice, 进而得到它的outputs (srcOutputs);
- 对于该strategy, 得到它的newDevice, 进而得到它的outputs (dstOutputs);
- 如果这2个srcOutputs、dstOutputs不相同, 表示需要迁移
@@2.2 如果迁移:
把对应的Track设置为invalidate状态即可,App写AudioTrack时发现它是invalidate状态, 就会重新创建新的Track
- audio_devices_t oldDevice = getDeviceForStrategy(strategy, true /*fromCache*/);
- audio_devices_t newDevice = getDeviceForStrategy(strategy, false /*fromCache*/);
- SortedVector<audio_io_handle_t> srcOutputs = getOutputsForDevice(oldDevice, mPreviousOutputs);
- SortedVector<audio_io_handle_t> dstOutputs = getOutputsForDevice(newDevice, mOutputs);
@3 getNewOutputDevice/setOutputDevice 这需要操作HAL层
2 耳麦插拔 声音通道切换 源码解读
这里从AudioService中的onSetWiredDeviceConnectionState开始分析,代码实现如下:
private void onSetWiredDeviceConnectionState(int device, int state, String name)
{synchronized (mConnectedDevices) {//...//关键点1:声道切换入口handleDeviceConnection((state == 1), device, (isUsb ? name : ""));if (state != 0) {//...if ((device & mSafeMediaVolumeDevices) != 0) {sendMsg(mAudioHandler,MSG_CHECK_MUSIC_ACTIVE,SENDMSG_REPLACE,0,0,null,MUSIC_ACTIVE_POLL_PERIOD_MS);}//...} else {//...}if (!isUsb && (device != AudioSystem.DEVICE_IN_WIRED_HEADSET)) {//关键点2:通过AMS上报intentsendDeviceConnectionIntent(device, state, name);}}
}
之前我们关注这里的两个关键点:声道切换入口handleDeviceConnection 和 给AMS上报intent的sendDeviceConnectionIntent。本章节我们从handleDeviceConnection开始分析,Java层AudioService的handleDeviceConnection方法最终可以直接调用到Native层AudioPolicyManager的setDeviceConnectionStateInt,setDeviceConnectionStateInt代码实现如下:
status_t AudioPolicyManager::setDeviceConnectionStateInt(audio_devices_t device,audio_policy_dev_state_t state,const char *device_address)
{if (!audio_is_output_device(device) && !audio_is_input_device(device)) return BAD_VALUE;sp<DeviceDescriptor> devDesc = getDeviceDescriptor(device, device_address);// handle output devices/*判断上报的是否为output_device*/if (audio_is_output_device(device)) {SortedVector <audio_io_handle_t> outputs;ssize_t index = mAvailableOutputDevices.indexOf(devDesc);mPreviousOutputs = mOutputs;switch (state){// handle output device connectioncase AUDIO_POLICY_DEVICE_STATE_AVAILABLE: {//代表存在直接返回,否则代表为新添加的if (index >= 0) {return INVALID_OPERATION;}//添加到可用设备index = mAvailableOutputDevices.add(devDesc);if (index >= 0) {//根据device在可用的设备列表中查找sp<HwModule> module = getModuleForDevice(device);if (module == 0) {mAvailableOutputDevices.remove(devDesc);return INVALID_OPERATION;}mAvailableOutputDevices[index]->mId = nextUniqueId();mAvailableOutputDevices[index]->mModule = module;} else {return NO_MEMORY;}//关键点1:针对该device, 打开新的output, 创建新的playbackthread.if (checkOutputsForDevice(devDesc, state, outputs, devDesc->mAddress) != NO_ERROR) {mAvailableOutputDevices.remove(devDesc);return INVALID_OPERATION;}// Set connect to HALsAudioParameter param = AudioParameter(devDesc->mAddress);param.addInt(String8(AUDIO_PARAMETER_DEVICE_CONNECT), device);mpClientInterface->setParameters(AUDIO_IO_HANDLE_NONE, param.toString());} break;// handle output device disconnectioncase AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE: {// Set Disconnect to HALsAudioParameter param = AudioParameter(devDesc->mAddress);param.addInt(String8(AUDIO_PARAMETER_DEVICE_DISCONNECT), device);mpClientInterface->setParameters(AUDIO_IO_HANDLE_NONE, param.toString());// remove device from available output devicesmAvailableOutputDevices.remove(devDesc);checkOutputsForDevice(devDesc, state, outputs, devDesc->mAddress);} break;default:ALOGE("setDeviceConnectionState() invalid state: %x", state);return BAD_VALUE;}//.../*关键点2:对所有的strategy分组声音,判断是否需要迁移*到新的output, 如果需要则迁移对应Track到新的output*/checkOutputForAllStrategies();//...for (size_t i = 0; i < mOutputs.size(); i++) {audio_io_handle_t output = mOutputs.keyAt(i);if ((mPhoneState != AUDIO_MODE_IN_CALL) || (output != mPrimaryOutput)) {audio_devices_t newDevice = getNewOutputDevice(mOutputs.keyAt(i),true /*fromCache*/);bool force = !mOutputs.valueAt(i)->isDuplicated()&& (!deviceDistinguishesOnAddress(device)// always force when disconnecting (a non-duplicated device)|| (state == AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE));setOutputDevice(output, newDevice, force, 0);}}mpClientInterface->onAudioPortListUpdate();return NO_ERROR;} // end if is output device//... Audio input 处理return BAD_VALUE;
}
接下来主要针对checkOutputsForDevice方法和checkOutputForAllStrategies方法进行分析(标志位变换),之后对 数据写入部分(AudioTrack的write函数)进行分析。
2.1 checkOutputsForDevice分析
checkOutputsForDevice的代码实现如下:
status_t AudioPolicyManager::checkOutputsForDevice(const sp<DeviceDescriptor> devDesc,audio_policy_dev_state_t state,SortedVector<audio_io_handle_t>& outputs,const String8 address)
{audio_devices_t device = devDesc->mDeviceType;//...if (state == AUDIO_POLICY_DEVICE_STATE_AVAILABLE) {//...for (ssize_t profile_index = 0; profile_index < (ssize_t)profiles.size(); profile_index++) {sp<IOProfile> profile = profiles[profile_index];// nothing to do if one output is already opened for this profile//...if (j != outputs.size()) {continue;}//...status_t status = mpClientInterface->openOutput(profile->mModule->mHandle,&output,&config,&desc->mDevice,address,&desc->mLatency,desc->mFlags);if (status == NO_ERROR) {//...if (output != AUDIO_IO_HANDLE_NONE) {addOutput(output, desc);if (deviceDistinguishesOnAddress(device) && address != "0") {//...} else if ((desc->mFlags & AUDIO_OUTPUT_FLAG_DIRECT) == 0) {//...// open a duplicating output thread for the new output and the primary outputduplicatedOutput = mpClientInterface->openDuplicateOutput(output,mPrimaryOutput);//...}}} else {output = AUDIO_IO_HANDLE_NONE;}//...}//...} else { // Disconnect//...}return NO_ERROR;
}
配置文件audio_policy.conf 中 每个output都会被描绘成一个profile,即checkOutputsForDevice会检测所有的profile(output),查找每个profile是否都存在对应的线程,如果没有则进行创建。
2.2 checkOutputForAllStrategies分析
checkOutputForAllStrategies的代码实现如下:
void AudioPolicyManager::checkOutputForAllStrategies()
{if (mForceUse[AUDIO_POLICY_FORCE_FOR_SYSTEM] == AUDIO_POLICY_FORCE_SYSTEM_ENFORCED)checkOutputForStrategy(STRATEGY_ENFORCED_AUDIBLE);checkOutputForStrategy(STRATEGY_PHONE);if (mForceUse[AUDIO_POLICY_FORCE_FOR_SYSTEM] != AUDIO_POLICY_FORCE_SYSTEM_ENFORCED)checkOutputForStrategy(STRATEGY_ENFORCED_AUDIBLE);checkOutputForStrategy(STRATEGY_SONIFICATION);checkOutputForStrategy(STRATEGY_SONIFICATION_RESPECTFUL);checkOutputForStrategy(STRATEGY_ACCESSIBILITY);checkOutputForStrategy(STRATEGY_MEDIA);checkOutputForStrategy(STRATEGY_DTMF);checkOutputForStrategy(STRATEGY_REROUTING);
}
这里详细分析下checkOutputForStrategy,代码实现如下:
void AudioPolicyManager::checkOutputForStrategy(routing_strategy strategy)
{/**对于该strategy, 得到它的oldDevice, 进而得到它的outputs (srcOutputs);*对于该strategy, 得到它的newDevice, 进而得到它的outputs (dstOutputs);*/audio_devices_t oldDevice = getDeviceForStrategy(strategy, true /*fromCache*/);audio_devices_t newDevice = getDeviceForStrategy(strategy, false /*fromCache*/);SortedVector<audio_io_handle_t> srcOutputs = getOutputsForDevice(oldDevice, mPreviousOutputs);SortedVector<audio_io_handle_t> dstOutputs = getOutputsForDevice(newDevice, mOutputs);// also take into account external policy-related changes: add all outputs which are// associated with policies in the "before" and "after" output vectorsfor (size_t i = 0 ; i < mPreviousOutputs.size() ; i++) {const sp<AudioOutputDescriptor> desc = mPreviousOutputs.valueAt(i);if (desc != 0 && desc->mPolicyMix != NULL) {srcOutputs.add(desc->mIoHandle);}}for (size_t i = 0 ; i < mOutputs.size() ; i++) {const sp<AudioOutputDescriptor> desc = mOutputs.valueAt(i);if (desc != 0 && desc->mPolicyMix != NULL) {dstOutputs.add(desc->mIoHandle);}}//如果这2个srcOutputs、dstOutputs不相同, 表示需要迁移if (!vectorsEqual(srcOutputs,dstOutputs)) {// mute strategy while moving tracks from one output to anotherfor (size_t i = 0; i < srcOutputs.size(); i++) {sp<AudioOutputDescriptor> desc = mOutputs.valueFor(srcOutputs[i]);if (desc->isStrategyActive(strategy)) {setStrategyMute(strategy, true, srcOutputs[i]);setStrategyMute(strategy, false, srcOutputs[i], MUTE_TIME_MS, newDevice);}}// Move effects associated to this strategy from previous output to new outputif (strategy == STRATEGY_MEDIA) {audio_io_handle_t fxOutput = selectOutputForEffects(dstOutputs);SortedVector<audio_io_handle_t> moved;for (size_t i = 0; i < mEffects.size(); i++) {sp<EffectDescriptor> effectDesc = mEffects.valueAt(i);if (effectDesc->mSession == AUDIO_SESSION_OUTPUT_MIX &&effectDesc->mIo != fxOutput) {if (moved.indexOf(effectDesc->mIo) < 0) {mpClientInterface->moveEffects(AUDIO_SESSION_OUTPUT_MIX, effectDesc->mIo,fxOutput);moved.add(effectDesc->mIo);}effectDesc->mIo = fxOutput;}}}// Move tracks associated to this strategy from previous output to new outputfor (int i = 0; i < AUDIO_STREAM_CNT; i++) {if (i == AUDIO_STREAM_PATCH) {continue;}if (getStrategy((audio_stream_type_t)i) == strategy) {/** 把对应的Track设置为invalidate状态即可,* App写AudioTrack时发现它是invalidate状态,* 就会重新创建新的Track*/mpClientInterface->invalidateStream((audio_stream_type_t)i);}}}
}
最后的操作invalidateStream是AudioFlinger的invalidateStream操作,代码实现如下:
status_t AudioFlinger::invalidateStream(audio_stream_type_t stream)
{Mutex::Autolock _l(mLock);for (size_t i = 0; i < mPlaybackThreads.size(); i++) {PlaybackThread *thread = mPlaybackThreads.valueAt(i).get();thread->invalidateTracks(stream);}return NO_ERROR;
}
这里 thread的invalidateTracks代码实现如下:
void AudioFlinger::PlaybackThread::invalidateTracks(audio_stream_type_t streamType)
{Mutex::Autolock _l(mLock);size_t size = mTracks.size();for (size_t i = 0; i < size; i++) {sp<Track> t = mTracks[i];if (t->streamType() == streamType) {t->invalidate();}}
}
这里 Track的invalidate 代码实现如下:
void AudioFlinger::PlaybackThread::Track::invalidate()
{// FIXME should use proxy, and needs workaudio_track_cblk_t* cblk = mCblk;//设置标志位android_atomic_or(CBLK_INVALID, &cblk->mFlags);android_atomic_release_store(0x40000000, &cblk->mFutex);// client is not in server, so FUTEX_WAKE is needed instead of FUTEX_WAKE_PRIVATE(void) syscall(__NR_futex, &cblk->mFutex, FUTEX_WAKE, INT_MAX);mIsInvalid = true;
}
实际上这个操作android_atomic_or(CBLK_INVALID, &cblk->mFlags);就是设置一个标志位,那么设置标志位之后,在APP重新写入数据就可以检测到标志位的变化,就会进行相应的操作。接下来就以APP的AudioTrack 写入数据为契机进行分析。
2.3 切换后的数据写入
根据前面的分析,执行Java层AudioTrack的write方法,一定会调用到Native层AudioTrack的obtainBuffer方法,因此这里直接从AudioTrack的obtainBuffer方法开始分析,代码如下:
status_t AudioTrack::obtainBuffer(Buffer* audioBuffer, const struct timespec *requested,struct timespec *elapsed, size_t *nonContig)
{//...do {//...newSequence = mSequence;// did previous obtainBuffer() fail due to media server death or voluntary invalidation?if (status == DEAD_OBJECT) {// re-create track, unless someone else has already done soif (newSequence == oldSequence) {status = restoreTrack_l("obtainBuffer");//...}}//...status = proxy->obtainBuffer(&buffer, requested, elapsed);} while ((status == DEAD_OBJECT) && (tryCounter-- > 0));//...return status;
}
@1 restoreTrack_l()分析
这里的restoreTrack_l()函数 代码实现如下:
status_t AudioTrack::restoreTrack_l(const char *from)
{//...result = createTrack_l();//...return result;
}
根据前面章节的分析,createTrack_l在这里就重新创建了Track。
@2 proxy的obtainBuffer分析
这里专注分析proxy的obtainBuffer的实现,代码如下:
status_t ClientProxy::obtainBuffer(Buffer* buffer, const struct timespec *requested,struct timespec *elapsed)
{//...for (;;) {int32_t flags = android_atomic_and(~CBLK_INTERRUPT, &cblk->mFlags);// check for track invalidation by server, or server death detectionif (flags & CBLK_INVALID) {ALOGV("Track invalidated");status = DEAD_OBJECT;//被设置成CBLK_INVALIDgoto end;}//...}
end://...return status;
}
这里将根据标识位CBLK_INVALID将返回的Status设置为DEAD_OBJECT。
简单总结下:写入数据的过程中干掉了 旧的Track,创建了新的Track。