概述
iOS 上「声音从哪出」由SMeetingEngine 上的一组音频路由接口控制,仅 iOS 可用 —— macOS 是独立的输入 / 输出硬件设备模型,走 设备管理 的 setAudioOutput(deviceId:)。
这套接口是底层 SRTC AudioRouteSession 的薄封装,不做二次状态缓存,因此会议层与底层不会出现两份漂移的状态。设计基准是主流 RTC SDK(声网 Agora、腾讯 TRTC、LiveKit)的通行做法,有三条需要先理解的原则,否则很容易写出「设置了但没生效」的代码。
底层机制的完整说明见 SRTC · 音频路由,本页只讲会议层怎么用。
原则一:只能切扬声器和听筒
AudioRouteTarget 只有两个值 —— .speaker(扬声器 / 免提)和 .earpiece(听筒)。
SDK 不提供「切换到蓝牙耳机 / 有线耳机」的接口。 这不是功能缺失,而是 iOS 平台限制:系统没有可靠手段让 App 指定某个具体外设。Agora、TRTC、LiveKit 同样都没有这类接口。
外接设备的行为是:插入时系统自动切过去,拔出时 SDK 回落到你设置的路由;需要让用户主动选蓝牙 / AirPlay 时,用系统提供的 AVRoutePickerView。
原则二:持久设置与临时设置分两层
优先级是 临时 > 持久。外设拔出后,SDK 按这个优先级回落。
原则三:入会即建立通话音频通道
加入会议时 SDK 就会建立通话音频通道并保持到离会,无论你有没有开麦。开不开麦只决定「推不推流」,不影响底层音频采集是否运行。 为什么必须这样做?iOS 上听筒输出只存在于可录音的会话类别(.playAndRecord)下,而「平时只播放、要听筒时再临时升级」经真机验证是切不过去的 —— 通话通道常驻是路由可控的前提。
如果用户拒绝了麦克风权限,SDK 会降级到只播放模式:仍然可以听到其他成员,但听筒路由不可用,只能外放。
建议在 Info.plist 中同时声明后台音频能力,避免切到后台时音频被系统挂起:
查询当前状态
注意区分
currentAudioRoute(实际从哪出声,五态)和 effectiveAudioRouteTarget(你要求的目标,两态)。插着耳机时前者是 .bluetooth,后者可能仍是 .speaker —— 这不是矛盾,是外设优先。UI 上要展示给用户的是 currentAudioRoute。availableAudioRoutes() 返回的是端口快照,不是可供用户选择的列表 —— 可控目标只有听筒和外放两个。监听路由变化
AudioRouteChangeEventData 带 route(变化后)、previousRoute(变化前)和 reason(系统给出的原因:.oldDeviceUnavailable 是拔出、.newDeviceAvailable 是插入、.override 是 App 主动覆盖)。排查路由问题时 reason 往往比结果本身更有价值。
音频路由事件不依赖会议状态,SDK 实例创建后就开始上报,入会前的设备检测页面也能用。
中断恢复不是一次性的:音频中断结束时如果系统电话还没真正挂断,重激活必定失败。SDK 会等到「系统通话已结束」且「App 在前台」再重试,真正恢复成功后才触发
meetingAudioRouteDidRecoverFromInterruption。业务层不需要自己处理这套时序。与 setSpeakerOutputEnabled 的关系
设备管理 里的setSpeakerOutputEnabled(_:) 与本页的 setAudioRoute(_:) 是同一机制的两种写法,都落在系统的 overrideOutputAudioPort 上:
defaultAudioRoute。不要在这两套之外自己再造第三套状态。
常见问题
切换没有生效 按顺序排查:- 当前是否走在外设上(
isExternalAudioRouteActive)—— 这种情况下切扬声器本就无效 - 是否已入会(
isAudioSessionActive)—— 音频通道未建立时设置只会被记录,等通道建立后套用 - 麦克风权限是否被拒 —— 被拒时会话降级,听筒不可用