Skip to content

Commit b46c2a2

Browse files
fix(android): audio reroute on device change (#79)
* fix(android): keep background app audio on headset during rtc playback * fix(android): reapply preferred output device when audio devices change
1 parent 848426d commit b46c2a2

File tree

1 file changed

+48
-12
lines changed

1 file changed

+48
-12
lines changed

android/src/main/java/com/webrtc/HybridWebrtcView.kt

Lines changed: 48 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import android.view.Surface
44
import android.view.SurfaceView
55
import android.view.SurfaceHolder
66
import android.media.AudioFormat
7+
import android.media.AudioDeviceCallback
78
import android.media.AudioDeviceInfo
89
import android.media.AudioManager
910
import android.media.AudioTrack
@@ -24,6 +25,17 @@ class HybridWebrtcView(val context: ThemedReactContext) : HybridWebrtcViewSpec()
2425
private var previousMode: Int? = null
2526
private var previousSpeakerphoneOn: Boolean? = null
2627
private var communicationRouteApplied = false
28+
private var communicationModeApplied = false
29+
private var audioDeviceCallbackRegistered = false
30+
private val audioDeviceCallback = object : AudioDeviceCallback() {
31+
override fun onAudioDevicesAdded(addedDevices: Array<out AudioDeviceInfo>) {
32+
view.post { refreshAudioRouteIfNeeded() }
33+
}
34+
35+
override fun onAudioDevicesRemoved(removedDevices: Array<out AudioDeviceInfo>) {
36+
view.post { refreshAudioRouteIfNeeded() }
37+
}
38+
}
2739

2840
companion object {
2941
private var audioTrack = AudioTrack(
@@ -71,6 +83,8 @@ class HybridWebrtcView(val context: ThemedReactContext) : HybridWebrtcViewSpec()
7183

7284

7385
init {
86+
audioManager.registerAudioDeviceCallback(audioDeviceCallback, null)
87+
audioDeviceCallbackRegistered = true
7488
view.holder.addCallback(object : SurfaceHolder.Callback {
7589
override fun surfaceCreated(holder: SurfaceHolder) {
7690
updateVideoPipeId(_videoPipeId, holder.surface)
@@ -112,6 +126,10 @@ class HybridWebrtcView(val context: ThemedReactContext) : HybridWebrtcViewSpec()
112126
}
113127

114128
override fun dispose() {
129+
if (audioDeviceCallbackRegistered) {
130+
audioManager.unregisterAudioDeviceCallback(audioDeviceCallback)
131+
audioDeviceCallbackRegistered = false
132+
}
115133
if (this.audioSubscriptionId > 0) {
116134
this.unsubscribe(this.audioSubscriptionId)
117135
this.audioSubscriptionId = -1
@@ -127,12 +145,7 @@ class HybridWebrtcView(val context: ThemedReactContext) : HybridWebrtcViewSpec()
127145
}
128146
previousMode = audioManager.mode
129147
previousSpeakerphoneOn = audioManager.isSpeakerphoneOn
130-
audioManager.mode = AudioManager.MODE_IN_COMMUNICATION
131-
@Suppress("DEPRECATION")
132-
run {
133-
audioManager.isSpeakerphoneOn = false
134-
}
135-
routeAudioTrackToPreferredOutput()
148+
applyRouteForCurrentDevices()
136149
communicationRouteApplied = true
137150
}
138151

@@ -141,19 +154,42 @@ class HybridWebrtcView(val context: ThemedReactContext) : HybridWebrtcViewSpec()
141154
return
142155
}
143156
audioTrack.setPreferredDevice(null)
144-
previousSpeakerphoneOn?.let { audioManager.isSpeakerphoneOn = it }
145-
previousMode?.let { audioManager.mode = it }
157+
if (communicationModeApplied) {
158+
previousSpeakerphoneOn?.let { audioManager.isSpeakerphoneOn = it }
159+
previousMode?.let { audioManager.mode = it }
160+
}
146161
previousSpeakerphoneOn = null
147162
previousMode = null
163+
communicationModeApplied = false
148164
communicationRouteApplied = false
149165
}
150166

151-
private fun routeAudioTrackToPreferredOutput() {
152-
val target = selectPreferredOutputDevice()
153-
if (target != null) {
154-
audioTrack.setPreferredDevice(target)
167+
private fun refreshAudioRouteIfNeeded() {
168+
if (!communicationRouteApplied || audioSubscriptionId <= 0 || _audioPipeId.isNullOrEmpty()) {
169+
return
170+
}
171+
applyRouteForCurrentDevices()
172+
}
173+
174+
private fun applyRouteForCurrentDevices() {
175+
val targetOutput = selectPreferredOutputDevice()
176+
if (targetOutput != null) {
177+
audioTrack.setPreferredDevice(targetOutput)
178+
if (communicationModeApplied) {
179+
previousSpeakerphoneOn?.let { audioManager.isSpeakerphoneOn = it }
180+
previousMode?.let { audioManager.mode = it }
181+
communicationModeApplied = false
182+
}
155183
} else {
156184
audioTrack.setPreferredDevice(null)
185+
if (!communicationModeApplied) {
186+
audioManager.mode = AudioManager.MODE_IN_COMMUNICATION
187+
@Suppress("DEPRECATION")
188+
run {
189+
audioManager.isSpeakerphoneOn = false
190+
}
191+
communicationModeApplied = true
192+
}
157193
}
158194
}
159195

0 commit comments

Comments
 (0)