I am developing a VoIP app that uses WebRTC inside a WKWebView.
Question 1: How can I monitor which audio output device WebRTC is currently using? I want to display this information in the UI for the user .
Question 2: How can I change the current audio output device for WebRTC?
I am using a JS Bridge to Objective-C code, attempting to change the audio device with the following code:
void set_speaker(int n)
{
session = [AVAudioSession sharedInstance];
NSError *err = nil;
if (n == 1) {
[session overrideOutputAudioPort:AVAudioSessionPortOverrideSpeaker error:&err];
} else {
[session overrideOutputAudioPort:AVAudioSessionPortOverrideNone error:&err];
}
}
However, this approach does not work.
I am testing on an iPhone with iOS 16.7. Is a higher iOS version required?
Question 1 — Monitoring the current audio output device
Register for AVAudioSessionRouteChangeNotification and inspect
AVAudioSession.sharedInstance.currentRoute.outputs. Each entry is an
AVAudioSessionPortDescription whose portType tells you what device
is active (e.g. AVAudioSessionPortBuiltInSpeaker,
AVAudioSessionPortBuiltInReceiver, AVAudioSessionPortBluetoothA2DP).
[[NSNotificationCenter defaultCenter]
addObserver:self
selector:@selector(audioRouteChanged:)
name:AVAudioSessionRouteChangeNotification
object:nil];
- (void)audioRouteChanged:(NSNotification *)notification {
AVAudioSessionRouteDescription *route =
[[AVAudioSession sharedInstance] currentRoute];
for (AVAudioSessionPortDescription *output in route.outputs) {
NSLog(@"Current output: %@", output.portType);
}
}
Question 2 — Changing the audio output device
There is a concrete issue in the code you posted:
overrideOutputAudioPort: is only valid when the audio session category
is set to AVAudioSessionCategoryPlayAndRecord. Without that, the call
fails. Your snippet never sets the category, and it doesn't check the
NSError output, so the failure is silent. At minimum you need:
AVAudioSession *session = [AVAudioSession sharedInstance];
NSError *err = nil;
[session setCategory:AVAudioSessionCategoryPlayAndRecord
withOptions:AVAudioSessionCategoryOptionAllowBluetooth
error:&err];
if (err) {
NSLog(@"Category error: %@", err);
return;
}
[session setActive:YES error:&err];
if (err) {
NSLog(@"Activation error: %@", err);
return;
}
[session overrideOutputAudioPort:AVAudioSessionPortOverrideSpeaker
error:&err];
if (err) {
NSLog(@"Override error: %@", err);
}
Be aware that overrideOutputAudioPort: is a temporary override by
design — the documentation states that it resets whenever a route change
occurs. If you want the speaker to be the persistent default output
(instead of the receiver) when no other audio route is connected,
set the AVAudioSessionCategoryOptionDefaultToSpeaker option when you
configure the category:
[session setCategory:AVAudioSessionCategoryPlayAndRecord
withOptions:AVAudioSessionCategoryOptionDefaultToSpeaker |
AVAudioSessionCategoryOptionAllowBluetooth
error:&err];
One additional consideration: WKWebView manages its own
AVAudioSession configuration internally when WebRTC captures audio
via getUserMedia(). That internal configuration may trigger a route
change, which would reset a prior overrideOutputAudioPort: call. If
you find that fixing the category and using DefaultToSpeaker still
doesn't give you the behavior you need, please file a feedback report
at https://feedbackassistant.apple.com with a small sample project that
demonstrates the issue, and post the feedback number here so we can
track it.