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