|
|
@ -128,10 +128,11 @@ public class SimpleAudioReceiver: NSObject { |
|
|
_isWriting=true |
|
|
_isWriting=true |
|
|
self.audioSourceType = audioSourceType; |
|
|
self.audioSourceType = audioSourceType; |
|
|
self.audioDataCallback = audioDataCallback |
|
|
self.audioDataCallback = audioDataCallback |
|
|
print("startAudioRecord=audioSourceType\(audioSourceType)") |
|
|
print("音源设制---------- startAudioRecord=audioSourceType:\(audioSourceType)") |
|
|
switch audioSourceType { |
|
|
switch audioSourceType { |
|
|
case .microphone: |
|
|
case .microphone: |
|
|
isReceiverHFP = true |
|
|
// 根据当前可用输入设备动态判定是否存在蓝牙HFP麦克风 |
|
|
|
|
|
isReceiverHFP = hasBluetoothHFPInput() |
|
|
runMicrophoneCapture() |
|
|
runMicrophoneCapture() |
|
|
case .external: |
|
|
case .external: |
|
|
isReceiverHFP = false |
|
|
isReceiverHFP = false |
|
|
@ -139,6 +140,21 @@ public class SimpleAudioReceiver: NSObject { |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* 检测是否存在蓝牙HFP输入设备 |
|
|
|
|
|
* 参数:无 |
|
|
|
|
|
* 返回值:存在返回true,否则返回false |
|
|
|
|
|
* 异常:无 |
|
|
|
|
|
*/ |
|
|
|
|
|
private func hasBluetoothHFPInput() -> Bool { |
|
|
|
|
|
let session = AVAudioSession.sharedInstance() |
|
|
|
|
|
if let inputs = session.availableInputs { |
|
|
|
|
|
return inputs.contains { $0.portType == .bluetoothHFP } |
|
|
|
|
|
} |
|
|
|
|
|
// 兜底使用当前路由的输入 |
|
|
|
|
|
return session.currentRoute.inputs.contains { $0.portType == .bluetoothHFP } |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
/** |
|
|
/** |
|
|
* 开启音频写入线程 |
|
|
* 开启音频写入线程 |
|
|
* 修复:使用userInitiated QoS避免优先级反转 |
|
|
* 修复:使用userInitiated QoS避免优先级反转 |
|
|
@ -263,7 +279,7 @@ public class SimpleAudioReceiver: NSObject { |
|
|
// 非通话:保持原有逻辑,但使用 .allowBluetooth 以支持录音(而不是 A2DP) |
|
|
// 非通话:保持原有逻辑,但使用 .allowBluetooth 以支持录音(而不是 A2DP) |
|
|
desiredCategory = .playAndRecord |
|
|
desiredCategory = .playAndRecord |
|
|
desiredMode = .videoChat |
|
|
desiredMode = .videoChat |
|
|
desiredOptions = [.allowBluetooth, .mixWithOthers, .defaultToSpeaker] |
|
|
desiredOptions = [.allowBluetooth, .mixWithOthers] |
|
|
//desiredOptions = [.mixWithOthers, .defaultToSpeaker] |
|
|
//desiredOptions = [.mixWithOthers, .defaultToSpeaker] |
|
|
}else{ |
|
|
}else{ |
|
|
desiredCategory = .playAndRecord |
|
|
desiredCategory = .playAndRecord |
|
|
@ -276,13 +292,13 @@ public class SimpleAudioReceiver: NSObject { |
|
|
// 通话中:录音优先,避免 A2DP(仅播放),改用 HFP(.allowBluetooth) |
|
|
// 通话中:录音优先,避免 A2DP(仅播放),改用 HFP(.allowBluetooth) |
|
|
desiredCategory = .record |
|
|
desiredCategory = .record |
|
|
desiredMode = .voiceChat |
|
|
desiredMode = .voiceChat |
|
|
desiredOptions = [.allowBluetoothA2DP] |
|
|
desiredOptions = [.allowBluetooth] |
|
|
} else { |
|
|
} else { |
|
|
if(isHeadphones){ |
|
|
if(isHeadphones){ |
|
|
// 非通话:保持原有逻辑,但使用 .allowBluetooth 以支持录音(而不是 A2DP) |
|
|
// 非通话:保持原有逻辑,但使用 .allowBluetooth 以支持录音(而不是 A2DP) |
|
|
desiredCategory = .playAndRecord |
|
|
desiredCategory = .playAndRecord |
|
|
desiredMode = .videoChat |
|
|
desiredMode = .videoChat |
|
|
desiredOptions = [.allowBluetoothA2DP, .mixWithOthers, .defaultToSpeaker] |
|
|
desiredOptions = [.allowBluetoothA2DP, .mixWithOthers] |
|
|
//desiredOptions = [.mixWithOthers, .defaultToSpeaker] |
|
|
//desiredOptions = [.mixWithOthers, .defaultToSpeaker] |
|
|
}else{ |
|
|
}else{ |
|
|
desiredCategory = .playAndRecord |
|
|
desiredCategory = .playAndRecord |
|
|
@ -307,6 +323,22 @@ public class SimpleAudioReceiver: NSObject { |
|
|
|
|
|
|
|
|
// 保持激活;这里重复 setActive(true) 一般安全,但如果你观察到重复激活也触发路由回调,可在外层控制频率 |
|
|
// 保持激活;这里重复 setActive(true) 一般安全,但如果你观察到重复激活也触发路由回调,可在外层控制频率 |
|
|
try audioSession.setActive(true, options: .notifyOthersOnDeactivation) |
|
|
try audioSession.setActive(true, options: .notifyOthersOnDeactivation) |
|
|
|
|
|
|
|
|
|
|
|
// 路由校正:有蓝牙输出时不强制外放,并优先选择蓝牙输入(HFP)或内置麦克风 |
|
|
|
|
|
let outputs = audioSession.currentRoute.outputs |
|
|
|
|
|
let hasBtOutput = outputs.contains { out in |
|
|
|
|
|
out.portType == .bluetoothA2DP || out.portType == .bluetoothHFP || out.portType == .bluetoothLE |
|
|
|
|
|
} |
|
|
|
|
|
if hasBtOutput { |
|
|
|
|
|
try audioSession.overrideOutputAudioPort(.none) |
|
|
|
|
|
} |
|
|
|
|
|
if let inputs = audioSession.availableInputs { |
|
|
|
|
|
if isReceiverHFP, let btInput = inputs.first(where: { $0.portType == .bluetoothHFP }) { |
|
|
|
|
|
try audioSession.setPreferredInput(btInput) |
|
|
|
|
|
} else if let builtInMic = inputs.first(where: { $0.portType == .builtInMic }) { |
|
|
|
|
|
try audioSession.setPreferredInput(builtInMic) |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
} catch { |
|
|
} catch { |
|
|
// 兜底:尽量保证能开始录音 |
|
|
// 兜底:尽量保证能开始录音 |
|
|
print("音频会话配置失败,尝试兜底配置: \(error)") |
|
|
print("音频会话配置失败,尝试兜底配置: \(error)") |
|
|
@ -561,5 +593,3 @@ private class LinkedBlockingQueue<T> { |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|