Android Framework 音频子系统(11)耳麦插拔之声音通道切换

该系列文章总纲链接:专题分纲目录 Android Framework 音频子系统​​​​​​​


本章关键点总结 & 说明:

本章节主要关注➕ 以上思维导图左上 耳麦插拔 部分中的 声音通道切换 部分 即可。主要说明了声道切换的原理和声道切换的流程分析。


1 耳麦插拔 声音通道切换 原理说明

1.1 切换声音通道情景分析

这里分成两种情况进行分析,一种是USB声卡的插入,另一种是primary设备上插入耳麦。

USB声卡插入:

  1. 从配置文件audio_policy中可以找到usb中对应的modle,一定有outputs包含usb_accessory、usb_device这类output。插上USB声卡后会创建output和对应的playbackthread。(usb_accessory、usb_device 这两个output各对应一个)。
  2. 之前与板载声卡建立联系的playBackThread线程,要切换成USB创建的playBackThread,APP的AudioTrack从原来的playbackthread/output模式切换到新的playbackthread/output。在新的playBackThread中,每个APP创建对应的Track。
  3. 在output中选择Device,重新做一些设置,决定从耳机还是喇叭播放声音。

在primary中插上耳机:

  1. 无需创建output和playbackthread,因为这种情况下他们所涉及的线程并没有改变,所以不需要重新去创建output与playBackThread。
  2. 无需切换output。
  3. 在原来的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 判断是否需要迁移:

  1. 对于该strategy, 得到它的oldDevice, 进而得到它的outputs (srcOutputs);
  2. 对于该strategy, 得到它的newDevice, 进而得到它的outputs (dstOutputs);
  3. 如果这2个srcOutputs、dstOutputs不相同, 表示需要迁移

@@2.2 如果迁移:

把对应的Track设置为invalidate状态即可,App写AudioTrack时发现它是invalidate状态, 就会重新创建新的Track

  1. audio_devices_t oldDevice = getDeviceForStrategy(strategy, true /*fromCache*/);
  2. audio_devices_t newDevice = getDeviceForStrategy(strategy, false /*fromCache*/);
  3. SortedVector<audio_io_handle_t> srcOutputs = getOutputsForDevice(oldDevice, mPreviousOutputs);
  4. 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上报intent
            sendDeviceConnectionIntent(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 connection
        case 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 HALs
            AudioParameter param = AudioParameter(devDesc->mAddress);
            param.addInt(String8(AUDIO_PARAMETER_DEVICE_CONNECT), device);
            mpClientInterface->setParameters(AUDIO_IO_HANDLE_NONE, param.toString());

            } break;
        // handle output device disconnection
        case AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE: {
            // Set Disconnect to HALs
            AudioParameter 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 devices
            mAvailableOutputDevices.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 output
                        duplicatedOutput = 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 vectors
    for (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 another
        for (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 output
        if (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 output
        for (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 work
    audio_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 so
                if (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 detection
        if (flags & CBLK_INVALID) {
            ALOGV("Track invalidated");
            status = DEAD_OBJECT;//被设置成CBLK_INVALID
            goto end;
        }
        //...
    }
end:
    //...
    return status;
}

这里将根据标识位CBLK_INVALID将返回的Status设置为DEAD_OBJECT。

简单总结下:写入数据的过程中干掉了 旧的Track,创建了新的Track。

发布了289 篇原创文章 · 获赞 47 · 访问量 3万+

猜你喜欢

转载自blog.csdn.net/vviccc/article/details/105409403
今日推荐