Android R音频输出问题处理随笔

Android R音频输出问题处理

背景:播放蓝牙音乐时插拔有线耳机,蓝牙音乐无声音

播放蓝牙音乐是指机器作为sink端,手机作为source端连接,手机播放的音乐通过蓝牙avrcp协议传输和播放

Android R上使用的是蓝牙协议栈已经使用了 AAudio, 系统源码路径是 system/bt/btif/src/btif_avrcp_audio_track.cc

93  void* BtifAvrcpAudioTrackCreate(int trackFreq, int bitsPerSample,
94                                  int channelCount) {
95    LOG_VERBOSE(LOG_TAG, "%s Track.cpp: btCreateTrack freq %d bps %d channel %d ",
96                __func__, trackFreq, bitsPerSample, channelCount);
97  
98    AAudioStreamBuilder* builder;
99    AAudioStream* stream;
100    aaudio_result_t result = AAudio_createStreamBuilder(&builder);
101    AAudioStreamBuilder_setSampleRate(builder, trackFreq);
102    AAudioStreamBuilder_setFormat(builder, AAUDIO_FORMAT_PCM_FLOAT);
103    AAudioStreamBuilder_setChannelCount(builder, channelCount);
104    AAudioStreamBuilder_setSessionId(builder, AAUDIO_SESSION_ID_ALLOCATE);
105    AAudioStreamBuilder_setPerformanceMode(builder,
106                                           AAUDIO_PERFORMANCE_MODE_LOW_LATENCY);
107    AAudioStreamBuilder_setErrorCallback(builder, ErrorCallback, nullptr);
108    result = AAudioStreamBuilder_openStream(builder, &stream);
109    CHECK(result == AAUDIO_OK);
110    AAudioStreamBuilder_delete(builder);
111  
112    BtifAvrcpAudioTrack* trackHolder = new BtifAvrcpAudioTrack;
113    CHECK(trackHolder != NULL);
114    trackHolder->stream = stream;
115    trackHolder->bitsPerSample = bitsPerSample;
116    trackHolder->channelCount = channelCount;
117    trackHolder->bufferLength =
118        trackHolder->channelCount * AAudioStream_getBufferSizeInFrames(stream);
119    trackHolder->buffer = new float[trackHolder->bufferLength]();
120  
121  #if (DUMP_PCM_DATA == TRUE)
122    outputPcmSampleFile = fopen(outputFilename, "ab");
123  #endif
124    s_AudioEngine.trackFreq = trackFreq;
125    s_AudioEngine.channelCount = channelCount;
126  
127    return (void*)trackHolder;
128  }

具体打印可以看

03-15 10:24:44.209  1505  4561 D bt_btif_avrcp_audio_track: BtifAvrcpAudioErrorHandle AAudio Error handle: restart A2dp Sink AudioTrack
03-15 10:24:44.209  1505  4561 D AAudio  : AAudioStream_requestStart(s#8) called --------------
03-15 10:24:44.209  1505  4561 D AAudioStream: setState(s#8) from 2 to 3

前提知识点:

AudioService中会设置有线耳机的连接状态,这个是耳机接上或断连的情况是frameworks会回调给AudioService的,调用函数 setWiredDeviceConnectionState

客制化修改:

在插拔有线耳机的时候会通过音频策略的修改将输出改回默认的Speaker输出

Android 9.0上的修改如下

    private void onSetWiredDeviceConnectionState(int device, int state, String address,
            String deviceName, String caller) {
        if (DEBUG_DEVICES) {
            Slog.i(TAG, "onSetWiredDeviceConnectionState(dev:" + Integer.toHexString(device)
                    + " state:" + Integer.toHexString(state)
                    + " address:" + address
                    + " deviceName:" + deviceName
                    + " caller: " + caller + ");");
        }

        synchronized (mConnectedDevices) {
            if ((state == 0) && ((device & DEVICE_OVERRIDE_A2DP_ROUTE_ON_PLUG) != 0)) {
                setBluetoothA2dpOnInt(true, "onSetWiredDeviceConnectionState state 0");
            }

            if (!handleDeviceConnection(state == 1, device, address, deviceName)) {
                // change of connection state failed, bailout
                return;
            }
            if (state != 0) {
                if ((device & DEVICE_OVERRIDE_A2DP_ROUTE_ON_PLUG) != 0) {
                    setBluetoothA2dpOnInt(false, "onSetWiredDeviceConnectionState state not 0");
                }
                if ((device & mSafeMediaVolumeDevices) != 0) {
                    sendMsg(mAudioHandler,
                            MSG_CHECK_MUSIC_ACTIVE,
                            SENDMSG_REPLACE,
                            0,
                            0,
                            caller,
                            MUSIC_ACTIVE_POLL_PERIOD_MS);
                }
                // Television devices without CEC service apply software volume on HDMI output
                if (isPlatformTelevision() && ((device & AudioSystem.DEVICE_OUT_HDMI) != 0)) {
                    mFixedVolumeDevices |= AudioSystem.DEVICE_OUT_HDMI;
                    checkAllFixedVolumeDevices();
                    if (mHdmiManager != null) {
                        synchronized (mHdmiManager) {
                            if (mHdmiPlaybackClient != null) {
                                mHdmiCecSink = false;
                                mHdmiPlaybackClient.queryDisplayStatus(mHdmiDisplayStatusCallback);
                            }
                        }
                    }
                }
                if ((device & AudioSystem.DEVICE_OUT_HDMI) != 0) {
                    sendEnabledSurroundFormats(mContentResolver, true);
                }
            } else {
                if (isPlatformTelevision() && ((device & AudioSystem.DEVICE_OUT_HDMI) != 0)) {
                    if (mHdmiManager != null) {
                        synchronized (mHdmiManager) {
                            mHdmiCecSink = false;
                        }
                    }
                }
            }
            // My Android Patch Start
            if ((device & AudioSystem.DEVICE_OUT_WIRED_HEADPHONE) != 0) {
                int usage = AudioSystem.getForceUse(AudioSystem.FOR_MEDIA);
                if (DEBUG_DEVICES)
                    Log.d(TAG, "getForceUse media:" + usage);
                // 耳机拔出,如果当前是在耳机通道下,恢复至speaker
                if (state == 0 && (usage == AudioSystem.FORCE_HEADPHONES || usage == AudioSystem.FORCE_NONE)) {
                    if (DEBUG_DEVICES)
                        Log.d(TAG, "FORCE_SPEAKER");
                    setForceUseForMedia(AudioSystem.FORCE_SPEAKER);
                }
                // 耳机接入,将通道切换至耳机
                if (state == 1 && usage != AudioSystem.FORCE_HEADPHONES) {
                    if (DEBUG_DEVICES)
                        Log.d(TAG, "FORCE_HEADPHONES");
                    setForceUseForMedia(AudioSystem.FORCE_HEADPHONES);
                }
            }
            // My Android Patch End
            sendDeviceConnectionIntent(device, state, address, deviceName);
            updateAudioRoutes(device, state);
        }
    }

不过Android 11 上对AudioService进行了重构,抽取了AudioDeviceBroker和AudioDeviceInventory两个类出来用来操作device

    public void setWiredDeviceConnectionState(int type,
            @ConnectionState int state, String address, String name,
            String caller) {
        int newConfig = 0;

        mDeviceBroker.setWiredDeviceConnectionState(type, state, address, name, caller);
        // MyPatch
        // ......
    }

之前是在 MyPatch 这个地方将audio force use设置为Speaker或者有线耳机,这个根据是否连接的状态来确定的

断开连接的情况,如果在MyPatch 这个地方将audio force use设置为Speaker,这里就会有问题,因为 mDeviceBroker.setWiredDeviceConnectionState 这里面的操作是异步操作

我们可以往里面去看看

AudioDeviceBroker.java

    /*package*/ void setWiredDeviceConnectionState(int type,
            @AudioService.ConnectionState int state, String address, String name,
            String caller) {
        //TODO move logging here just like in setBluetooth* methods
        synchronized (mDeviceStateLock) {
            mDeviceInventory.setWiredDeviceConnectionState(type, state, address, name, caller);
        }
    }

继续往AudioDeviceInventory.java 可以看到这个是一个异步的msg操作

    /*package*/ int setWiredDeviceConnectionState(int type, @AudioService.ConnectionState int state,
                                                  String address, String name, String caller) {
        synchronized (mDevicesLock) {
            int delay = checkSendBecomingNoisyIntentInt(type, state, AudioSystem.DEVICE_NONE);
            mDeviceBroker.postSetWiredDeviceConnectionState(
                    new WiredDeviceConnectionState(type, state, address, name, caller),
                    delay);
            return delay;
        }
    }

最后异步调用到的地方是这里如下这个地方

    /*package*/ void onSetWiredDeviceConnectionState(
                            AudioDeviceInventory.WiredDeviceConnectionState wdcs) {
        AudioService.sDeviceLogger.log(new AudioServiceEvents.WiredDevConnectEvent(wdcs));

        Log.d(TAG, "onSetWiredDeviceConnectionState:" + wdcs.mType + ", state:" + wdcs.mState);
        
        synchronized (mDevicesLock) {
            if ((wdcs.mState == AudioService.CONNECTION_STATE_DISCONNECTED)
                    && DEVICE_OVERRIDE_A2DP_ROUTE_ON_PLUG_SET.contains(wdcs.mType)) {
                    // My_Note
                    mDeviceBroker.setBluetoothA2dpOnInt(true, false /*fromA2dp*/,
                        "onSetWiredDeviceConnectionState state DISCONNECTED");
            }

            if (!handleDeviceConnection(wdcs.mState == AudioService.CONNECTION_STATE_CONNECTED,
                    wdcs.mType, wdcs.mAddress, wdcs.mName)) {
                // change of connection state failed, bailout
                mmi.set(MediaMetrics.Property.EARLY_RETURN, "change of connection state failed")
                        .record();
                return;
            }
            if (wdcs.mState != AudioService.CONNECTION_STATE_DISCONNECTED) {
                if (DEVICE_OVERRIDE_A2DP_ROUTE_ON_PLUG_SET.contains(wdcs.mType)) {
                    mDeviceBroker.setBluetoothA2dpOnInt(false, false /*fromA2dp*/,
                            "onSetWiredDeviceConnectionState state not DISCONNECTED");
                }
                mDeviceBroker.checkMusicActive(wdcs.mType, wdcs.mCaller);
            }
            if (wdcs.mType == AudioSystem.DEVICE_OUT_HDMI) {
                mDeviceBroker.checkVolumeCecOnHdmiConnection(wdcs.mState, wdcs.mCaller);
            }
            sendDeviceConnectionIntent(wdcs.mType, wdcs.mState, wdcs.mAddress, wdcs.mName);
            updateAudioRoutes(wdcs.mType, wdcs.mState);
        }
        mmi.record();
        // My Android Patch Start
        int device = wdcs.mType;
        int state = wdcs.mState;
        Context context = mDeviceBroker.getContext();
        if (context == null) {
            Log.e(TAG, "change wired headphone device check context fail");
            return;
        }
        if ((device & AudioSystem.DEVICE_OUT_WIRED_HEADPHONE) != 0) {
            int usage = AudioSystem.getForceUse(AudioSystem.FOR_MEDIA);
            if (AudioService.DEBUG_DEVICES)
            // 耳机拔出,如果当前是在耳机通道下,恢复至speaker
            if (state == 0 && (usage == AudioSystem.FORCE_HEADPHONES || usage == AudioSystem.FORCE_NONE)) {
                if (AudioService.DEBUG_DEVICES)
                    Log.d(TAG, "[AudioOutput]FORCE_SPEAKER instead of " + usage);
                setForceUseForMedia(AudioSystem.FORCE_SPEAKER);
            }
            // 耳机接入,将通道切换至耳机
            if (state == 1 && usage != AudioSystem.FORCE_HEADPHONES) {
                if (AudioService.DEBUG_DEVICES)
                    Log.d(TAG, "[AudioOutput]FORCE_HEADPHONES instead of " + usage);
                setForceUseForMedia(AudioSystem.FORCE_HEADPHONES);
            }
        }
        // My Android Patch End
    }

可以看到如上Patch代码是在打在了 onSetWiredDeviceConnectionState 函数最后

为什么要在这个地方修改呢

在上述函数 My_Note (mDeviceBroker.setBluetoothA2dpOnInt)这个地方同样会去设置 force use(设为None,取默认输出设备),所以要保证最后的设置是我们设置的,不然在AudioServcie中设置就有异步的问题

所以这个问题就出在了异步的一个操作

具体可以看Audio Device的会回调给 AudioStreamLegacy

void AudioStreamLegacy::onAudioDeviceUpdate(audio_port_handle_t deviceId)
{
    // Device routing is a common source of errors and DISCONNECTS.
    // Please leave this log in place.
    ALOGD("%s() devId %d => %d", __func__, (int) getDeviceId(), (int)deviceId);
    if (getDeviceId() != AAUDIO_UNSPECIFIED && getDeviceId() != deviceId &&
            getState() != AAUDIO_STREAM_STATE_DISCONNECTED) {
        // Note that isDataCallbackActive() is affected by state so call it before DISCONNECTING.
        // If we have a data callback and the stream is active, then ask the data callback
        // to DISCONNECT and call the error callback.
        if (isDataCallbackActive()) {
            ALOGD("onAudioDeviceUpdate() request DISCONNECT in data callback due to device change");
            // If the stream is stopped before the data callback has a chance to handle the
            // request then the requestStop() and requestPause() methods will handle it after
            // the callback has stopped.
            mRequestDisconnect.request();
        } else {
            ALOGD("onAudioDeviceUpdate() DISCONNECT the stream now");
            forceDisconnect();
        }
    }
    setDeviceId(deviceId);
}

并且判断该Stream的使用情况,如果输出设备不一致,则会断开状态连接,这个会触发AduioErrorCallBack

void AudioStreamLegacy::forceDisconnect(bool errorCallbackEnabled) {
    if (getState() != AAUDIO_STREAM_STATE_DISCONNECTED) {
        setState(AAUDIO_STREAM_STATE_DISCONNECTED);
        if (errorCallbackEnabled) {
            maybeCallErrorCallback(AAUDIO_ERROR_DISCONNECTED);
        }
    }
}

以A2dp的log为例

48  void ErrorCallback(AAudioStream* stream, void* userdata, aaudio_result_t error);
49  
50  void BtifAvrcpAudioErrorHandle() {
51    AAudioStreamBuilder* builder;
52    AAudioStream* stream;
53  
54    aaudio_result_t result = AAudio_createStreamBuilder(&builder);
55    AAudioStreamBuilder_setSampleRate(builder, s_AudioEngine.trackFreq);
56    AAudioStreamBuilder_setFormat(builder, AAUDIO_FORMAT_PCM_FLOAT);
57    AAudioStreamBuilder_setChannelCount(builder, s_AudioEngine.channelCount);
58    AAudioStreamBuilder_setSessionId(builder, AAUDIO_SESSION_ID_ALLOCATE);
59    AAudioStreamBuilder_setPerformanceMode(builder,
60                                           AAUDIO_PERFORMANCE_MODE_LOW_LATENCY);
61    AAudioStreamBuilder_setErrorCallback(builder, ErrorCallback, nullptr);
62    result = AAudioStreamBuilder_openStream(builder, &stream);
63    CHECK(result == AAUDIO_OK);
64    AAudioStreamBuilder_delete(builder);
65  
66    void* handle = btif_a2dp_sink_get_audio_track();
67    BtifAvrcpAudioTrack* trackHolder = static_cast<BtifAvrcpAudioTrack*>(handle);
68  
69    trackHolder->stream = stream;
70  
71    if (trackHolder != NULL && trackHolder->stream != NULL) {
72        LOG_DEBUG(LOG_TAG, "%s AAudio Error handle: restart A2dp Sink AudioTrack", __func__);
73        AAudioStream_requestStart(trackHolder->stream);
74    }
75    s_AudioEngine.thread = nullptr;
76  }

03-15 10:24:44.183  1505  2029 D AudioStreamLegacy: onAudioDeviceUpdate() devId 2 => 43
03-15 10:24:44.183  1505  2029 D AudioStreamLegacy: onAudioDeviceUpdate() DISCONNECT the stream now
03-15 10:24:44.183  1505  2029 D AAudioStream: setState(s#7) from 4 to 13
03-15 10:24:44.184  1505  4561 I AAudio  : AAudioStreamBuilder_openStream() called ----------------------------------------
03-15 10:24:44.184  1505  4561 I AudioStreamBuilder: rate   =  44100, channels  = 2, format   = 5, sharing = SH, dir = OUTPUT
03-15 10:24:44.184  1505  4561 I AudioStreamBuilder: device =      0, sessionId = 0, perfMode = 12, callback: OFF with frames = 0
03-15 10:24:44.184  1505  4561 I AudioStreamBuilder: usage  =      0, contentType = 0, inputPreset = 0, allowedCapturePolicy = 0
03-15 10:24:44.184  1505  4561 I AudioStreamBuilder: privacy sensitive = false
03-15 10:24:44.184  1505  4561 D AudioStreamBuilder: build() MMAP not used because sessionId specified.
03-15 10:24:44.184  1505  4561 D droid.bluetoot: PlayerBase::PlayerBase()
03-15 10:24:44.185  1505  4561 D AudioStreamTrack: open(), request notificationFrames = 0, frameCount = 0
03-15 10:24:44.185  1505  4561 W AudioTrack: createTrack_l(0): AUDIO_OUTPUT_FLAG_FAST denied by client, not shared buffer and transfer = TRANSFER_SYNC
03-15 10:24:44.186   964  1124 I AS.AudioDeviceInventory: handleDeviceConnection(false dev:8 address: name:)
03-15 10:24:44.187   964  1124 I AS.AudioDeviceInventory: deviceKey:0x8:
03-15 10:24:44.187   964  1124 I AS.AudioDeviceInventory: deviceInfo:[DeviceInfo: type:0x8 (headphone) name: addr: codec: 0] is(already)Connected:true
03-15 10:24:44.192   367  1125 D MAudioPolicyManager: setDeviceConnectionState() device: 0x8, state 0, address  name  format 0x0
03-15 10:24:44.192   367  1126 D AudioFlinger: Client defaulted notificationFrames to 1992 for frameCount 3985
03-15 10:24:44.199   367  1126 D AF::TrackHandle: OpPlayAudio: track:76 usage:1 not muted
03-15 10:24:44.201   344   427 D audio_hw_primary: adev_set_parameters: disconnect=8
03-15 10:24:44.201   344   427 D audio_hw_primary: adev_set_parameters: headphone disconnect
03-15 10:24:44.201   344   427 I audio_hw: adev_set_parameters[197](0x0xf54b9240): disconnect=8
03-15 10:24:44.202   344   770 D modules.usbaudio.audio_hal: adev_set_parameters: kvpairs = disconnect=8
03-15 10:24:44.203  1505  4561 D AAudioStream: setState(s#8) from 0 to 2
03-15 10:24:44.203  1505  4561 W AudioStreamTrack: open() flags changed from 0x00000004 to 0x00000000
03-15 10:24:44.203  1505  4561 W AudioStreamTrack: open() perfMode changed from 12 to 10
03-15 10:24:44.208  1505  4561 I AAudio  : AAudioStreamBuilder_openStream() returns 0 = AAUDIO_OK for s#8 ----------------
03-15 10:24:44.209  1505  4561 D bt_btif_avrcp_audio_track: BtifAvrcpAudioErrorHandle AAudio Error handle: restart A2dp Sink AudioTrack

最后

如果想要成为架构师或想突破20~30K薪资范畴,那就不要局限在编码,业务,要会选型、扩展,提升编程思维。此外,良好的职业规划也很重要,学习的习惯很重要,但是最重要的还是要能持之以恒,任何不能坚持落实的计划都是空谈。

如果你没有方向,这里给大家分享一套由阿里高级架构师编写的《Android八大模块进阶笔记》,帮大家将杂乱、零散、碎片化的知识进行体系化的整理,让大家系统而高效地掌握Android开发的各个知识点。
在这里插入图片描述
相对于我们平时看的碎片化内容,这份笔记的知识点更系统化,更容易理解和记忆,是严格按照知识体系编排的。

全套视频资料:

一、面试合集

在这里插入图片描述
二、源码解析合集
在这里插入图片描述

三、开源框架合集
在这里插入图片描述
欢迎大家一键三连支持,若需要文中资料,直接扫描文末CSDN官方认证微信卡片免费领取↓↓↓

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值