AVFoundation — Audio routing
This article is extracted from the chat log with AI. Please identify it with caution.

Apple 设备的音频路由系统详解 #

Apple 的音频路由系统是一个复杂的框架,它管理着 iOS、macOS、watchOS 和 tvOS 设备上音频信号的输入和输出路径。这个系统决定了音频从哪里来(输入)、到哪里去(输出),以及如何处理这些音频信号。

https://developer.apple.com/documentation/avfaudio/audio-routing

核心组件 #

1. AVAudioSession (iOS) / Audio HAL (macOS) #

  • iOS 上通过 AVAudioSession 类管理音频路由
  • macOS 上通过 Core Audio 的 Audio Hardware Abstraction Layer (HAL) 管理

2. 音频路由选项 #

  • 内置扬声器
  • 听筒 (iPhone)
  • 有线耳机 (3.5mm 或 Lightning/USB-C)
  • 蓝牙设备 (AirPods, Beats, 其他蓝牙耳机/音箱)
  • AirPlay 设备
  • HDMI 输出 (Apple TV, 外接显示器)
  • USB 音频接口

iOS 音频路由详解 #

AVAudioSession 关键概念 #

  1. Category (音频会话类别)

    • .playback - 仅播放 (音乐播放器)
    • .record - 仅录制 (录音应用)
    • .playAndRecord - 同时播放和录制 (VoIP 应用)
    • .ambient - 混音播放 (游戏背景音乐)
    • .multiRoute - 多路由输出 (专业音频应用)
  2. Mode (模式)

    • .default - 默认模式
    • .voiceChat - 优化语音聊天 (如 FaceTime)
    • .videoChat - 优化视频聊天
    • .gameChat - 优化游戏语音
    • .measurement - 低延迟音频测量
  3. 路由选项

    • .overrideSpeaker - 强制使用扬声器
    • .mixWithOthers - 允许与其他应用音频混音
    • .duckOthers - 临时降低其他应用音频音量

代码示例 #

import AVFoundation

// 配置音频会话
let audioSession = AVAudioSession.sharedInstance()
do {
    try audioSession.setCategory(.playAndRecord, mode: .voiceChat, options: [.allowBluetooth, .defaultToSpeaker])
    try audioSession.setActive(true)
} catch {
    print("音频会话配置失败: \(error)")
}

// 监听路由变化通知
NotificationCenter.default.addObserver(
    forName: AVAudioSession.routeChangeNotification,
    object: nil,
    queue: nil) { notification in
        guard let reasonValue = notification.userInfo?[AVAudioSessionRouteChangeReasonKey] as? UInt,
              let reason = AVAudioSession.RouteChangeReason(rawValue: reasonValue) else {
            return
        }
        
        switch reason {
        case .newDeviceAvailable:
            print("新设备可用")
        case .oldDeviceUnavailable:
            print("旧设备不可用")
        case .routeConfigurationChange:
            print("路由配置改变")
        default:
            break
        }
    }

macOS 音频路由 #

macOS 使用 Core Audio 框架管理音频路由,主要接口包括:

  1. AudioObjectGetPropertyData/AudioObjectSetPropertyData - 获取和设置音频设备属性
  2. AudioDeviceCreateIOProcID - 创建音频设备回调
  3. AudioHardwareService - 高级音频硬件服务

示例:获取当前输出设备 #

import CoreAudio

var outputDeviceID = AudioDeviceID(0)
var outputDeviceIDSize = UInt32(MemoryLayout.size(ofValue: outputDeviceID))

var address = AudioObjectPropertyAddress(
    mSelector: kAudioHardwarePropertyDefaultOutputDevice,
    mScope: kAudioObjectPropertyScopeGlobal,
    mElement: kAudioObjectPropertyElementMain)

let status = AudioObjectGetPropertyData(
    AudioObjectID(kAudioObjectSystemObject),
    &address,
    0,
    nil,
    &outputDeviceIDSize,
    &outputDeviceID)

if status == noErr {
    print("当前输出设备ID: \(outputDeviceID)")
} else {
    print("获取输出设备失败")
}

高级路由功能 #

1. 多路由输出 #

  • 允许音频同时输出到多个设备
  • 使用 AVAudioSessionCategoryMultiRoute 类别

2. 音频输入/输出切换 #

  • 动态检测和切换输入输出设备
  • 监听 AVAudioSession.routeChangeNotification 通知

3. 蓝牙设备处理 #

  • 支持 HFP (Hands-Free Profile) 和 A2DP (Advanced Audio Distribution Profile)
  • 自动切换通话和媒体音频模式

4. 音频中断处理 #

  • 电话呼入、闹钟等系统音频会中断应用音频
  • 需要实现 AVAudioSessionDelegate 处理中断

最佳实践 #

  1. 合理设置音频会话类别 - 根据应用需求选择最合适的类别
  2. 处理路由变化 - 监听路由变化通知并适当响应
  3. 管理音频中断 - 正确处理系统中断和恢复
  4. 蓝牙设备兼容性 - 测试各种蓝牙设备的兼容性
  5. 性能考虑 - 音频路由改变可能导致延迟,特别是蓝牙设备

常见问题解决方案 #

  1. 蓝牙耳机只用于通话没有媒体音频

    try audioSession.setCategory(.playAndRecord, mode: .default, options: [.allowBluetoothA2DP])
    
  2. 强制使用扬声器

    try audioSession.overrideOutputAudioPort(.speaker)
    
  3. 检测耳机是否插入

    let currentRoute = audioSession.currentRoute
    for output in currentRoute.outputs {
        if output.portType == .headphones || output.portType == .bluetoothA2DP {
            print("耳机已连接")
        }
    }
    

Apple 的音频路由系统设计精巧但复杂,正确理解和运用这些 API 可以创建出在各种场景下都能提供优秀音频体验的应用。

本文共 1092 字,创建于 Mar 30, 2025
相关标签: Xcode, ByAI, Foundation