I'm trying to record video, with audio input coming from a connected Bluetooth headset. However I haven't been able to get an AVCaptureDevice that represents the headset, so I can't add it to my AVCaptureSession.
I tried to find it using a discovery session, but it never finds the headset-- only the built in microphone. That may not be surprising since I'm asking for .builtInMicrophone, but the enumeration has no members for external audio inputs. I tried leaving the device type array empty but that finds no devices.
let discoverySession = AVCaptureDevice.DiscoverySession(deviceTypes: [.builtInMicrophone], mediaType: AVMediaType.audio, position: .unspecified)
print("Found \(discoverySession.devices.count) devices")
for device in discoverySession.devices {
print("Device: \(device)")
}
After doing some searching I tried setting up the AVAudioSession to specifically allow Bluetooth. However this has had no effect on the above.
private let session = AVCaptureSession()
// later...
session.usesApplicationAudioSession = true
session.automaticallyConfiguresApplicationAudioSession = false
do {
try AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayAndRecord, with: [.allowBluetooth])
try AVAudioSession.sharedInstance().setActive(true)
} catch {
print("Error messing with audio session: \(error)")
}
For completeness I also tried the deprecated AVCaptureDevice.devices() method, but it doesn't find the Bluetooth headset either.
I know that the headset is available because AVAudioSession can see it. However I haven't been able to get from the availableInputs array to something I can use in an AVCaptureSession. The following code finds the headset, but what would I do with the result? It's not an AVCaptureDevice nor can I construct one from the entries in the array. Setting the "preferred" input doesn't have any effect that I know how to use.
if let availableInputs = AVAudioSession.sharedInstance().availableInputs {
print("Found \(availableInputs.count) inputs")
for input in availableInputs {
print("Input: \(input)")
if input.portType == AVAudioSessionPortBluetoothHFP {
print("Setting preferred input")
do {
try AVAudioSession.sharedInstance().setPreferredInput(input)
} catch {
print("Error setting preferred input: \(error)")
}
}
}
}
Given that the Bluetooth headset is connected and available, how do I set it as the audio input for my capture session?
This seems to be a case of the SDK giving confusing information while doing the right thing.
If you configure the audio session as above with .allowBluetooth and a Bluetooth headset is connected, then you'll get Buetooth audio. However, the discovery session will still claim to be using the iPhone's built-in microphone. Essentially you need to pay attention to AVAudioSession's idea about what its preferred input is while ignoring the fact that AVCaptureDevice still says it's using the built-in mic. I spent a lot of time trying to figure out how to get an AVCaptureDevice that refers to the Bluetooth input without realizing that it's not only impossible but unnecessary.