Android Audio代码分析27 - Strategy 优先级

status_t AudioFlinger::PlaybackThread::Track::start()
{
    status_t status = NO_ERROR;
    LOGV("start(%d), calling thread %d session %d",
            mName, IPCThreadState::self()->getCallingPid(), mSessionId);
// mThread 在 AudioFlinger::ThreadBase::TrackBase 的构造函数中被赋值
// 数值由 AudioFlinger::PlaybackThread::Track 传给其父类 ThreadBase
// 函数 AudioFlinger::PlaybackThread::createTrack_l 创建了 AudioFlinger::PlaybackThread::Track 对象
// 与 thread 对应的参数是自身的 this 指针
// 函数 AudioFlinger::openOutput 中创建了类 PlaybackThread 的子类 MixerThread 的对象,
// 并将其和一个 id 一同添加到 mPlaybackThreads 中。
// 下面使用的 thread->id() 就是此处的 id 。
// 不过,有一点困惑的是,下面使用 thread->id() 的时候,其实是把它作为一个 output 来使用的。
// id 和 output 究竟是什么关系呢?
// 在函数 AudioFlinger::openOutput 中找到了答案,调用函数 AudioFlinger::openOutput 来打开一个 output 时,得到的返回值其实是上面所说的 thread 的 id 。
// AudioPolicyManagerBase 中会将该 id 和一个 AudioOutputDescriptor 对象作为一对保存在 mOutputs 中。
    sp<ThreadBase> thread = mThread.promote();
    if (thread != 0) {
        Mutex::Autolock _l(thread->mLock);
        int state = mState;
        // here the track could be either new, or restarted
        // in both cases "unstop" the track
        if (mState == PAUSED) {
            mState = TrackBase::RESUMING;
            LOGV("PAUSED => RESUMING (%d) on thread %p", mName, this);
        } else {
            mState = TrackBase::ACTIVE;
            LOGV("? => ACTIVE (%d) on thread %p", mName, this);
        }


        if (!isOutputTrack() && state != ACTIVE && state != RESUMING) {
            thread->mLock.unlock();
// 下面的 thread->id() 对应的是调用函数 AudioPolicyService::openOutput 得到的 output 。
// 函数 AudioSystem::startOutput 的实现直接调用了函数 AudioPolicyService::startOutput 
// 函数 AudioPolicyService::startOutput 的实现就是调用函数 AudioPolicyManagerBase::startOutput
            status = AudioSystem::startOutput(thread->id(),
                                              (AudioSystem::stream_type)mStreamType,
                                              mSessionId);
            thread->mLock.lock();
        }
        if (status == NO_ERROR) {
            PlaybackThread *playbackThread = (PlaybackThread *)thread.get();
            playbackThread->addTrack_l(this);
        } else {
            mState = state;
        }
    } else {
        status = BAD_VALUE;
    }
    return status;
}


status_t AudioPolicyManagerBase::startOutput(audio_io_handle_t output,
                                             AudioSystem::stream_type stream,
                                             int session)
{
    LOGV("startOutput() output %d, stream %d, session %d", output, stream, session);
    ssize_t index = mOutputs.indexOfKey(output);
    if (index < 0) {
        LOGW("startOutput() unknow output %d", output);
        return BAD_VALUE;
    }


// 对象的由来:
// 函数 AudioPolicyManagerBase::AudioPolicyManagerBase 中打开了 DEVICE_OUT_SPEAKER 对应的 output ,并将其赋值给 AudioPolicyManagerBase 的成员变量 mHardwareOutput 。
// 然后为该 output 创建一个 AudioOutputDescriptor 对象 (outputDesc) ,然后将 mHardwareOutput 和 outputDesc 作为一对添加到 mOutputs 中。
// 最后调用函数 setOutputDevice 将默认的 output device 设置为 DEVICE_OUT_SPEAKER : setOutputDevice(mHardwareOutput, (uint32_t)AudioSystem::DEVICE_OUT_SPEAKER, true);


// 函数 AudioPolicyManagerBase::getOutput 中,若是 open 了一个 output ,也会为其创建一个 AudioOutputDescriptor 对象,并将它们添加到 mOutputs 中。


// 根据 output 找到其对应的 AudioOutputDescriptor 对象。
// AudioOutputDescriptor 对象在 AudioPolicyManagerBase 的构造函数或函数 AudioPolicyManagerBase::getOutput 中被创建,并和 output 作为一对添加到 mOutputs 中。
    AudioOutputDescriptor *outputDesc = mOutputs.valueAt(index);
    routing_strategy strategy = getStrategy((AudioSystem::stream_type)stream);


#ifdef WITH_A2DP
    if (mA2dpOutput != 0  && !a2dpUsedForSonification() && strategy == STRATEGY_SONIFICATION) {
        setStrategyMute(STRATEGY_MEDIA, true, mA2dpOutput);
    }
#endif


    // incremenent usage count for this stream on the requested output:
    // NOTE that the usage count is the same for duplicated output and hardware output which is
    // necassary for a correct control of hardware output routing by startOutput() and stopOutput()
// AudioOutputDescriptor 对象中有一个数组,保存了该 output 中每个 stream 的使用计数
// 函数 getNewDevice 中会使用到该计数
    outputDesc->changeRefCount(stream, 1);


// getNewDevice 函数的实现请看下面的代码
// setOutputDevice 函数的实现,后面也有分析
    setOutputDevice(output, getNewDevice(output));


    // handle special case for sonification while in call
    if (isInCall()) {
// 此处是对来电话时 sonification 策略的特殊处理。
// 若 stream 是低可见的,则将该 stream mute 掉
// 否则会 startTone
        handleIncallSonification(stream, true, false);
    }


    // apply volume rules for current stream and device if necessary
// checkAndSetVolume 函数的实现在后面有看
    checkAndSetVolume(stream, mStreams[stream].mIndexCur, output, outputDesc->device());


    return NO_ERROR;
}


// 为 output 查找一个 device 。
// 函数中涉及到的 strategy 的优先级,是为该 output 寻找哪个 strategy 对应的 device 的优先级。
uint32_t AudioPolicyManagerBase::getNewDevice(audio_io_handle_t output, bool fromCache)
{
    uint32_t device = 0;


    AudioOutputDescriptor *outputDesc = mOutputs.valueFor(output);
    // check the following by order of priority to request a routing change if necessary:
    // 1: we are in call or the strategy phone is active on the hardware output:
    //      use device for strategy phone
    // 2: the strategy sonification is active on the hardware output:
    //      use device for strategy sonification
    // 3: the strategy media is active on the hardware output:
    //      use device for strategy media
    // 4: the strategy DTMF is active on the hardware output:
    //      use device for strategy DTMF
// isInCall 函数的作用是判断当前状态是否是有电话打过来,或者正处于通话中
    if (isInCall() ||
// isUsedByStrategy 函数的功能是返回该 output 中指定 strategy 对应的所有 stream 的引用总和。
// 简单一点就是判断该 output 中是否使用了指定的 strategy 。
        outputDesc->isUsedByStrategy(STRATEGY_PHONE)) {
        device = getDeviceForStrategy(STRATEGY_PHONE, fromCache);
    } else if (outputDesc->isUsedByStrategy(STRATEGY_SONIFICATION)) {
        device = getDeviceForStrategy(STRATEGY_SONIFICATION, fromCache);
    } else if (outputDesc->isUsedByStrategy(STRATEGY_MEDIA)) {
        device = getDeviceForStrategy(STRATEGY_MEDIA, fromCache);
    } else if (outputDesc->isUsedByStrategy(STRATEGY_DTMF)) {
        device = getDeviceForStrategy(STRATEGY_DTMF, fromCache);
    }


    LOGV("getNewDevice() selected device %x", device);
    return device;
}


void AudioPolicyManagerBase::setOutputDevice(audio_io_handle_t output, uint32_t device, bool force, int delayMs)
{
    LOGV("setOutputDevice() output %d device %x delayMs %d", output, device, delayMs);
    AudioOutputDescriptor *outputDesc = mOutputs.valueFor(output);




    if (outputDesc->isDuplicated()) {
        setOutputDevice(outputDesc->mOutput1->mId, device, force, delayMs);
        setOutputDevice(outputDesc->mOutput2->mId, device, force, delayMs);
        return;
    }
#ifdef WITH_A2DP
    // filter devices according to output selected
    if (output == mA2dpOutput) {
        device &= AudioSystem::DEVICE_OUT_ALL_A2DP;
    } else {
        device &= ~AudioSystem::DEVICE_OUT_ALL_A2DP;
    }
#endif


// 函数 device 返回其成员变量 mDevice
// mDevice 在 AudioPolicyManagerBase 的构造函数或函数 AudioPolicyManagerBase::getOutput 中被赋值
    uint32_t prevDevice = (uint32_t)outputDesc->device();
    // Do not change the routing if:
    //  - the requestede device is 0
    //  - the requested device is the same as current device and force is not specified.
    // Doing this check here allows the caller to call setOutputDevice() without conditions
// 若可用的 device 只有耳机,此时正在听歌,若有电话打过来,找到的 device 也是耳机,
// 所以并不会作 route 的切换,但我们需要在打电话的时候把音乐停掉,这个处理不在这儿
    if ((device == 0 || device == prevDevice) && !force) {
        LOGV("setOutputDevice() setting same device %x or null device for output %d", device, output);
        return;
    }


    outputDesc->mDevice = device;
    // mute media streams if both speaker and headset are selected
    if (output == mHardwareOutput && AudioSystem::popCount(device) == 2) {
        setStrategyMute(STRATEGY_MEDIA, true, output);
        // wait for the PCM output buffers to empty before proceeding with the rest of the command
        usleep(outputDesc->mLatency*2*1000);
    }


    // do the routing
    AudioParameter param = AudioParameter();
    param.addInt(String8(AudioParameter::keyRouting), (int)device);
// 改变 route ,最终会掉到 ALSAControl 中的 set 函数来设置 codec 的 switch 或者 widget 。
    mpClientInterface->setParameters(mHardwareOutput, param.toString(), delayMs);
    // update stream volumes according to new device
// 设置 device 上各 stream 对应的音量
// 其中的实现是遍历各 stream ,调用函数 checkAndSetVolume 将 AudioOutputDescriptor 保存的各 stream 的音量进行设置
// checkAndSetVolume 函数的实现在后面有看
    applyStreamVolumes(output, device, delayMs);


    // if changing from a combined headset + speaker route, unmute media streams
    if (output == mHardwareOutput && AudioSystem::popCount(prevDevice) == 2) {
        setStrategyMute(STRATEGY_MEDIA, false, output, delayMs);
    }
}


status_t AudioPolicyManagerBase::checkAndSetVolume(int stream, int index, audio_io_handle_t output, uint32_t device, int delayMs, bool force)
{


    // do not change actual stream volume if the stream is muted
    if (mOutputs.valueFor(output)->mMuteCount[stream] != 0) {
        LOGV("checkAndSetVolume() stream %d muted count %d", stream, mOutputs.valueFor(output)->mMuteCount[stream]);
        return NO_ERROR;
    }


    // do not change in call volume if bluetooth is connected and vice versa
    if ((stream == AudioSystem::VOICE_CALL && mForceUse[AudioSystem::FOR_COMMUNICATION] == AudioSystem::FORCE_BT_SCO) ||
        (stream == AudioSystem::BLUETOOTH_SCO && mForceUse[AudioSystem::FOR_COMMUNICATION] != AudioSystem::FORCE_BT_SCO)) {
        LOGV("checkAndSetVolume() cannot set stream %d volume with force use = %d for comm",
             stream, mForceUse[AudioSystem::FOR_COMMUNICATION]);
        return INVALID_OPERATION;
    }


    float volume = computeVolume(stream, index, output, device);
    // We actually change the volume if:
    // - the float value returned by computeVolume() changed
    // - the force flag is set
    if (volume != mOutputs.valueFor(output)->mCurVolume[stream] ||
            force) {
        mOutputs.valueFor(output)->mCurVolume[stream] = volume;
        LOGV("setStreamVolume() for output %d stream %d, volume %f, delay %d", output, stream, volume, delayMs);
        if (stream == AudioSystem::VOICE_CALL ||
            stream == AudioSystem::DTMF ||
            stream == AudioSystem::BLUETOOTH_SCO) {
            // offset value to reflect actual hardware volume that never reaches 0
            // 1% corresponds roughly to first step in VOICE_CALL stream volume setting (see AudioService.java)
            volume = 0.01 + 0.99 * volume;
        }
        mpClientInterface->setStreamVolume((AudioSystem::stream_type)stream, volume, output, delayMs);
    }


    if (stream == AudioSystem::VOICE_CALL ||
        stream == AudioSystem::BLUETOOTH_SCO) {
        float voiceVolume;
        // Force voice volume to max for bluetooth SCO as volume is managed by the headset
        if (stream == AudioSystem::VOICE_CALL) {
            voiceVolume = (float)index/(float)mStreams[stream].mIndexMax;
        } else {
            voiceVolume = 1.0;
        }
        if (voiceVolume != mLastVoiceVolume && output == mHardwareOutput) {
            mpClientInterface->setVoiceVolume(voiceVolume, delayMs);
            mLastVoiceVolume = voiceVolume;
        }
    }


    return NO_ERROR;
}




float AudioPolicyManagerBase::computeVolume(int stream, int index, audio_io_handle_t output, uint32_t device)
{
    float volume = 1.0;
    AudioOutputDescriptor *outputDesc = mOutputs.valueFor(output);
    StreamDescriptor &streamDesc = mStreams[stream];


    if (device == 0) {
        device = outputDesc->device();
    }


    int volInt = (100 * (index - streamDesc.mIndexMin)) / (streamDesc.mIndexMax - streamDesc.mIndexMin);
    volume = AudioSystem::linearToLog(volInt);


    // if a headset is connected, apply the following rules to ring tones and notifications
    // to avoid sound level bursts in user's ears:
    // - always attenuate ring tones and notifications volume by 6dB
    // - if music is playing, always limit the volume to current music volume,
    // with a minimum threshold at -36dB so that notification is always perceived.
    if ((device &
        (AudioSystem::DEVICE_OUT_BLUETOOTH_A2DP |
        AudioSystem::DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES |
        AudioSystem::DEVICE_OUT_WIRED_HEADSET |
        AudioSystem::DEVICE_OUT_WIRED_HEADPHONE)) &&
        ((getStrategy((AudioSystem::stream_type)stream) == STRATEGY_SONIFICATION) ||
         (stream == AudioSystem::SYSTEM)) &&
        streamDesc.mCanBeMuted) {
        volume *= SONIFICATION_HEADSET_VOLUME_FACTOR;
        // when the phone is ringing we must consider that music could have been paused just before
        // by the music application and behave as if music was active if the last music track was
        // just stopped
        if (outputDesc->mRefCount[AudioSystem::MUSIC] || mLimitRingtoneVolume) {
            float musicVol = computeVolume(AudioSystem::MUSIC, mStreams[AudioSystem::MUSIC].mIndexCur, output, device);
            float minVol = (musicVol > SONIFICATION_HEADSET_VOLUME_MIN) ? musicVol : SONIFICATION_HEADSET_VOLUME_MIN;
            if (volume > minVol) {
                volume = minVol;
                LOGV("computeVolume limiting volume to %f musicVol %f", minVol, musicVol);
            }
        }
    }


    return volume;
}



1、 strategy 之间的优先级只有在不同的 strategy 可以找到不同的可以 device 时才会发挥作用。
如,原来在听音乐,使用的是 speaker ,此时有电话打过来,使用的是另外一个 device ,此时就需要改变 route 。
如果原来在打电话,此时打开音乐,使用的设备其实是 phone strategy 使用的 device 。
至于有电话打进的时候,正在播放的音乐是停止还是 mute ,native 代码中好像没有做处理。

2、 native 中对各 stream 的音量并没有做优先级的处理。
只有在连接耳机的时候会对 STRATEGY_SONIFICATION 和 SYSTEM stream 的音量进行一定的处理。









  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值