I'm working on a VoIP app which needs to allow the user to switch between the in built ear speaker, speaker, wired headset and bluetooth head sets.
Switching between the built in ear speaker, speaker and wired headset works perfectly fine (through a combination of
overrideOutputAudioPort
and setPreferredInput
)The problem I have is switching between bluetooth devices, basically, no matter what I do, it always defaults to the last paired device.
The following is based on paring the devices in the following order
- mini503
- BeatsStudio Wireless
In this setup, the BeatsStudio Wireless always wins
The session is setup using...
let session = AVAudioSession.sharedInstance()
try session.setActive(false)
var baseOptions: AVAudioSessionCategoryOptions = [.mixWithOthers]
for option in with {
baseOptions.insert(option)
}
try session.setCategory(AVAudioSessionCategoryPlayAndRecord, with: [.mixWithOthers, .allowBluetooth, defaultToSpeaker])
try session.overrideOutputAudioPort(.speaker)
try session.setMode(AVAudioSessionModeVoiceChat)
try session.setActive(false)
I then load the available inputs using:
if let inputs = session.availableInputs {
for input in inputs {
print(">> Input: \(input)")
// Stored in array for later use
}
}
This generates the output:
>> Input: <AVAudioSessionPortDescription: 0x174003de0, type = MicrophoneBuiltIn; name = iPhone Microphone; UID = Built-In Microphone; selectedDataSource = Front>
>> Input: <AVAudioSessionPortDescription: 0x170004380, type = BluetoothHFP; name = mini503; UID = 00:12:6F:15:41:33-tsco; selectedDataSource = (null)>
>> Input: <AVAudioSessionPortDescription: 0x170004410, type = BluetoothHFP; name = BeatsStudio Wireless; UID = 04:88:E2:2B:E2:A0-tsco; selectedDataSource = (null)>
I then use session.setPrefferedInput to switch the input, when using "BeatsStudio Wireless", it will generate the following:
Current route is currently
>> Optional(<AVAudioSessionPortDescription: 0x174002aa0, type = MicrophoneBuiltIn; name = iPhone Microphone; UID = Built-In Microphone; selectedDataSource = Front>)
>> Optional(<AVAudioSessionPortDescription: 0x174002a30, type = Speaker; name = Speaker; UID = Speaker; selectedDataSource = (null)>)
Apply input: <AVAudioSessionPortDescription: 0x170004410, type = BluetoothHFP; name = BeatsStudio Wireless; UID = 04:88:E2:2B:E2:A0-tsco; selectedDataSource = (null)>
Current route is now
>> Optional(<AVAudioSessionPortDescription: 0x1740029f0, type = BluetoothHFP; name = BeatsStudio Wireless; UID = 04:88:E2:2B:E2:A0-tsco; selectedDataSource = (null)>)
>> Optional(<AVAudioSessionPortDescription: 0x174002aa0, type = BluetoothHFP; name = BeatsStudio Wireless; UID = 04:88:E2:2B:E2:A0-tsco; selectedDataSource = (null)>)
Which shows the route before the
setPrefferredInput
call and the route after it, this also generates AVAudioSessionRouteChange notification
(as expected)When I try changing to the mini503 it outputs:
Current route is currently
>> Optional(<AVAudioSessionPortDescription: 0x1740029f0, type = BluetoothHFP; name = BeatsStudio Wireless; UID = 04:88:E2:2B:E2:A0-tsco; selectedDataSource = (null)>)
>> Optional(<AVAudioSessionPortDescription: 0x174002aa0, type = BluetoothHFP; name = BeatsStudio Wireless; UID = 04:88:E2:2B:E2:A0-tsco; selectedDataSource = (null)>)
Apply input: <AVAudioSessionPortDescription: 0x170004380, type = BluetoothHFP; name = mini503; UID = 00:12:6F:15:41:33-tsco; selectedDataSource = (null)>
Current route is now
>> Optional(<AVAudioSessionPortDescription: 0x1740029f0, type = BluetoothHFP; name = BeatsStudio Wireless; UID = 04:88:E2:2B:E2:A0-tsco; selectedDataSource = (null)>)
>> Optional(<AVAudioSessionPortDescription: 0x174002aa0, type = BluetoothHFP; name = BeatsStudio Wireless; UID = 04:88:E2:2B:E2:A0-tsco; selectedDataSource = (null)>)
Which clearly shows that the route has not changed.
I've tried cycling the
active
state of the session
(turning to false
before I call setPrefferredInput
and calling true
after it) without any effectIf I change the order in which I connect the devices, the last connected device always wins.
I know it should be possible, because the phone app does this, but I can't seem to figure out how. Is there a option or category I should be using? Is there another method I should be trying?