Browse Source

fix(audio): 修复蓝牙音频路由问题并优化外部音频源处理

强制禁用HFP通话音道,改为使用手机麦克风加A2DP输出
新增耳机/蓝牙输出设备检测功能
优化外部音频源的会话配置逻辑
weicu
liwei1dao 9 months ago
parent
commit
818247411b
  1. 70
      local_plugins/azure_speech/ios/azure_speech/Sources/tools/SimpleAudioReceiver.swift

70
local_plugins/azure_speech/ios/azure_speech/Sources/tools/SimpleAudioReceiver.swift

@ -123,6 +123,9 @@ public class SimpleAudioReceiver: NSObject {
/** /**
* 开始音频输入 * 开始音频输入
* 参数:audioSourceType 音频源类型(microphone 或 external);audioDataCallback 音频数据回调
* 返回值:无
* 异常:无(内部捕获并打印错误,不抛出)
*/ */
public func startAudioRecord(audioSourceType: AudioSourceType = .microphone, audioDataCallback: AudioDataCallback?) { public func startAudioRecord(audioSourceType: AudioSourceType = .microphone, audioDataCallback: AudioDataCallback?) {
_isWriting=true _isWriting=true
@ -131,8 +134,10 @@ public class SimpleAudioReceiver: NSObject {
print("音源设制---------- startAudioRecord=audioSourceType:\(audioSourceType)") print("音源设制---------- startAudioRecord=audioSourceType:\(audioSourceType)")
switch audioSourceType { switch audioSourceType {
case .microphone: case .microphone:
// 根据当前可用输入设备动态判定是否存在蓝牙HFP麦克风 // 唤醒后禁用通话音道(HFP),改为手机麦克风 + A2DP 输出
isReceiverHFP = hasBluetoothHFPInput() isReceiverHFP = false
// 动态检测是否存在耳机/蓝牙输出,用于后续路由选项计算
isHeadphones = detectHeadphonesOutput()
runMicrophoneCapture() runMicrophoneCapture()
case .external: case .external:
isReceiverHFP = false isReceiverHFP = false
@ -155,6 +160,23 @@ public class SimpleAudioReceiver: NSObject {
return session.currentRoute.inputs.contains { $0.portType == .bluetoothHFP } return session.currentRoute.inputs.contains { $0.portType == .bluetoothHFP }
} }
/**
* 检测是否存在耳机/蓝牙输出设备(用于决定是否启用A2DP而非扬声器)
* 参数:无
* 返回值:存在返回true,否则返回false
* 异常:无
*/
private func detectHeadphonesOutput() -> Bool {
let session = AVAudioSession.sharedInstance()
let outputs = session.currentRoute.outputs
return outputs.contains { out in
out.portType == .bluetoothA2DP ||
out.portType == .bluetoothHFP ||
out.portType == .bluetoothLE ||
out.portType == .headphones
}
}
/** /**
* 开启音频写入线程 * 开启音频写入线程
* 修复:使用userInitiated QoS避免优先级反转 * 修复:使用userInitiated QoS避免优先级反转
@ -246,13 +268,50 @@ public class SimpleAudioReceiver: NSObject {
private func runExternalCapture() { private func runExternalCapture() {
if audioSourceType == .external { if audioSourceType == .external {
do { do {
// 根据通话状态进行安全的音频会话配置 // 停止麦克风采集,确保不走通话音道
try safeConfigureAudioSessionForMic() micCapture.stopCapture()
// 外部模式会话配置:禁止HFP,仅允许A2DP/耳机输出
try safeConfigureAudioSessionForExternal()
} catch { } catch {
print("设置音频会话失败: \(error.localizedDescription)") print("设置音频会话失败: \(error.localizedDescription)")
} }
//pushAudioData(data: Data()) //pushAudioData(data: Data())
micCapture.stopCapture() }
}
/**
* 外部音频源的安全音频会话配置(禁止HFP通话音道)
* 参数:无
* 返回值:无
* 异常:可能抛出AVAudioSession设置错误(内部捕获并兜底)
*/
private func safeConfigureAudioSessionForExternal() throws {
let audioSession = self.audioSession
do {
// 外部模式不录麦,仅做播放路由;有耳机时只允许A2DP,禁止HFP
let outputs = audioSession.currentRoute.outputs
let hasHeadphones = outputs.contains { out in
out.portType == .bluetoothA2DP ||
out.portType == .bluetoothLE ||
out.portType == .headphones
}
let desiredCategory: AVAudioSession.Category = .playback
let desiredMode: AVAudioSession.Mode = .videoChat
let desiredOptions: AVAudioSession.CategoryOptions = hasHeadphones
? [.allowBluetoothA2DP, .mixWithOthers]
: [.mixWithOthers, .defaultToSpeaker]
let needsSetCategory = (audioSession.category != desiredCategory) || (audioSession.mode != desiredMode)
if needsSetCategory {
try audioSession.setCategory(desiredCategory, mode: desiredMode, options: desiredOptions)
}
// 取消扬声器强制,确保耳机/A2DP优先
try audioSession.overrideOutputAudioPort(.none)
try audioSession.setActive(true, options: .notifyOthersOnDeactivation)
} catch {
print("外部模式音频会话配置失败,兜底到playback: \(error)")
try audioSession.setCategory(.playback, mode: .default, options: [.mixWithOthers, .defaultToSpeaker])
try audioSession.setActive(true, options: .notifyOthersOnDeactivation)
} }
} }
/** /**
@ -592,4 +651,3 @@ private class LinkedBlockingQueue<T> {
} }
} }
} }

Loading…
Cancel
Save