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的区别:

蓝牙一般有两种语音相关的模式是A2DP和SCO,前者是高质量音乐播放(俗称:只进不出),后者是语音通话(俗称:有进有出)。

摘自:http://blog.csdn.net/kangear/article/details/38780185(如有冒犯,请作者原谅)

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的调用就略过不提了。

所以。。。就。。。不多说了

猜你喜欢

转载自blog.csdn.net/bberdong/article/details/53036569