From b8ec353d77eedf76a9bc93056e04ecb846a59c47 Mon Sep 17 00:00:00 2001 From: Michael Kirk Date: Thu, 13 Jul 2017 18:42:35 -0400 Subject: [PATCH] Use recommended approach for speakerphone mode From: https://developer.apple.com/library/content/qa/qa1754/_index.html Q: Can you explain the difference between calling the AVAudioSession method overrideOutputAudioPort: with the value AVAudioSessionPortOverrideSpeaker and using the category option AVAudioSessionCategoryOptionDefaultToSpeaker with setCategory:withOptions:error:. A: The difference is that setting the AVAudioSessionPortOverride by calling overrideOutputAudioPort: is more transient than using the category option AVAudioSessionCategoryOptionDefaultToSpeaker. Important: The use of both AVAudioSessionPortOverrideSpeaker and AVAudioSessionCategoryOptionDefaultToSpeaker are only applicable to the AVAudioSessionCategoryPlayAndRecord category. Calling overrideOutputAudioPort: and setting the AVAudioSessionPortOverride to AVAudioSessionPortOverrideSpeaker is a way of temporarily overriding the output to play to the speaker. Any route change or interruption will cause the audio to be routed back to its normal route, following the last-in wins rule. Think of using overrideOutputAudioPort: in terms of what you might use to implement a Speakerphone button where you want to be able to toggle between the speaker (AVAudioSessionPortOverrideSpeaker) and the normal output route (AVAudioSessionPortOverrideNone). Note: This property is intended to allow 3rd party applications to mimic the behavior of a Speakerphone button and therefore may change the input route as well as output route. For example, setting the AVAudioSessionPortOverride to AVAudioSessionPortOverrideSpeaker while a headset is plugged in will cause the route to change to built-in mic / built-in speaker. // FREEBIE --- Signal/src/call/CallAudioService.swift | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/Signal/src/call/CallAudioService.swift b/Signal/src/call/CallAudioService.swift index 59b6238ea..133292192 100644 --- a/Signal/src/call/CallAudioService.swift +++ b/Signal/src/call/CallAudioService.swift @@ -145,7 +145,7 @@ struct AudioSource: Hashable { ensureProperAudioSession(call: call) // It's importent to set preferred input *after* ensuring properAudioSession - // because some sources are only valid for certain categories. + // because some sources are only valid for certain category/option combinations. let session = AVAudioSession.sharedInstance() do { try session.setPreferredInput(audioSource?.portDescription) @@ -161,6 +161,8 @@ struct AudioSource: Hashable { } private func ensureProperAudioSession(call: SignalCall?) { + AssertIsOnMainThread() + guard let call = call else { setAudioSession(category: AVAudioSessionCategoryPlayback, mode: AVAudioSessionModeDefault) @@ -178,16 +180,22 @@ struct AudioSource: Hashable { setAudioSession(category: AVAudioSessionCategoryPlayAndRecord, mode: AVAudioSessionModeVideoChat, options: options) - } else if call.isSpeakerphoneEnabled { - // Ensure no bluetooth if user has specified speakerphone - setAudioSession(category: AVAudioSessionCategoryPlayAndRecord, - mode: AVAudioSessionModeVoiceChat, - options: [.defaultToSpeaker]) } else { setAudioSession(category: AVAudioSessionCategoryPlayAndRecord, mode: AVAudioSessionModeVoiceChat, options: [.allowBluetooth]) } + + let session = AVAudioSession.sharedInstance() + do { + if call.isSpeakerphoneEnabled { + try session.overrideOutputAudioPort(.speaker) + } else { + try session.overrideOutputAudioPort(.none) + } + } catch { + owsFail("\(TAG) failed overrideing output audio. isSpeakerPhoneEnabled: \(call.isSpeakerphoneEnabled)") + } } // MARK: - Service action handlers