Android Audio程式碼分析27 – Strategy 優先順序

來源:互聯網
上載者:User
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 的音量進行一定的處理。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.