1. 程式人生 > >Android藍芽音訊兩個問題

Android藍芽音訊兩個問題

1.qq通話,微信通話,打電話,鈴聲想起時,為何鈴聲只在手機端響起?而藍芽耳機裡只有嘟嘟聲?

(1)來電鈴聲播放

streamType = 2(AUDIO_STREAM_RING)
APM::AudioPolicyManager: startOutput() output 18, stream 2, session 24

(2)Engine::getStrategyForStream()函式可得stream 2對應
strategy 2 (STRATEGY_SONIFICATION)
(3)選裝置

APM::AudioPolicyEngine: getDeviceForStrategy() strategy 2
, device 82 結果為AUDIO_DEVICE_OUT_BLUETOOTH_A2DP | AUDIO_DEVICE_OUT_SPEAKER

這地方就奇怪了,明明選擇的裝置是藍芽裝置和揚聲器,為什麼只有揚聲器有鈴聲?

log中可以發現
bt_a2dp_hw: out_set_parameters: state 5
5 對應 AUDIO_A2DP_STATE_STANDBY
經過一番費力的查詢,可以發現
鈴聲響起時執行

adb shell dumpsys media.audio_flinger結果中

藍芽裝置對應的:

Output thread 0xecec0000
type 0 (MIXER): Thread name: AudioOut_441 I/O handle: 1089 TID: 2033 Standby: yes Sample rate: 44100 Hz HAL frame count: 2560 HAL format: 0x1 (pcm16) HAL buffer size: 10240 bytes Channel count: 2 Channel mask: 0x00000003 (front-left, front-right) Format: 0x1 (pcm16) Frame size: 4 bytes Pending config events: none
Output device: 0x80 (BLUETOOTH_A2DP)

小提示
多輸出裝置的時候,每個裝置對應一個Mixthread,然後對應著一個總的DuplicatingThread。

藍芽裝置對應的Output thread在鈴聲播放的時候處於Standby狀態。而這個狀態賦值的地方很好找到(Threads.cpp):

if ((!mActiveTracks.size() && systemTime() > mStandbyTimeNs) ||
                                   isSuspended()) {
                // put audio hardware into standby after short delay
                if (shouldStandby_l()) {

                    threadLoop_standby();

                    mStandby = true;
                }

經過一番尋找,真相了:

AudioPolicyManager setPhoneState會呼叫checkA2dpSuspend()函式,最終導致藍芽Thread standBy.然後整個藍芽輸出相關的MixThread都暫停工作了,此時肯定不會去呼叫out_write函式往下寫鈴聲資料,也就不會發出任何聲音了。

看官且看checkA2dpSuspend函式中註釋:

// suspend A2DP output if:
    //      (NOT already suspended) &&
    //      ((SCO device is connected &&
    //       (forced usage for communication || for record is SCO))) ||
    //      (phone state is ringing || in call)//響鈴的時候要把a2dp掛起,注意是a2dp
    //
    // restore A2DP output if:
    //      (Already suspended) &&
    //      ((SCO device is NOT connected ||
    //       (forced usage NOT for communication && NOT for record is SCO))) &&
    //      (phone state is NOT ringing && NOT in call)
    //

以在下區區四級的英文水平觀之,大意為:打電話或者is ringing(setPhonestate 0之後)的時候(sco要連線成功)要用sco,掛起a2dp.

百度搜搜sco和a2dp的區別:

2. 通話過程中,如果開啟擴音,聲音不再從藍芽裝置中輸出,而從手機端輸出,這是為什麼?

我們且看看
電話接通之後,電話應用正常會呼叫
setBluetoothScoOn()切換藍芽裝置為Sco模式

public void setBluetoothScoOnInt(boolean on) {
        if (on) {
        //通話時強制使用BT_SCO
            mForcedUseForComm = AudioSystem.FORCE_BT_SCO;
        } else if (mForcedUseForComm == AudioSystem.FORCE_BT_SCO) {
            mForcedUseForComm = AudioSystem.FORCE_NONE;
        }
        sendMsg(mAudioHandler, MSG_SET_FORCE_USE, SENDMSG_QUEUE,
                AudioSystem.FOR_COMMUNICATION, mForcedUseForComm, null, 0);
        sendMsg(mAudioHandler, MSG_SET_FORCE_USE, SENDMSG_QUEUE,
                AudioSystem.FOR_RECORD, mForcedUseForComm, null, 0);
    }

,點選“揚聲器”之後setSpeakerphoneOn()

public void setSpeakerphoneOn(boolean on){
        if (!checkAudioSettingsPermission("setSpeakerphoneOn()")) {
            return;
        }

        if (on) {
            if (mForcedUseForComm == AudioSystem.FORCE_BT_SCO) {
            //如果之前設定了通話強制使用BT_SCO
            //先取消強制使用(FORCE_NONE)
                    sendMsg(mAudioHandler, MSG_SET_FORCE_USE, SENDMSG_QUEUE,
                            AudioSystem.FOR_RECORD, AudioSystem.FORCE_NONE, null, 0);
            }
            //設定強制使用SPEAKER
            mForcedUseForComm = AudioSystem.FORCE_SPEAKER;
        } else if (mForcedUseForComm == AudioSystem.FORCE_SPEAKER){
            mForcedUseForComm = AudioSystem.FORCE_NONE;
        }

        sendMsg(mAudioHandler, MSG_SET_FORCE_USE, SENDMSG_QUEUE,
                AudioSystem.FOR_COMMUNICATION, mForcedUseForComm, null, 0);
    }
case MSG_SET_FORCE_USE:
                case MSG_SET_FORCE_BT_A2DP_USE:
                    setForceUse(msg.arg1, msg.arg2);
                    break;
//往下,AudioPolicyManager setForceUse的呼叫就略過不提了。

所以。。。就。。。不多說了