import Foundation import AVFoundation import YbridOpus @available(iOS 13.0, *) class SwiftOpusAudioProcessor: NSObject { protocol AudioDataCallback: AnyObject { func onAudioDataReceived(data: Data) } private var isDecodingActive = false private weak var callback: AudioDataCallback? // Opus 解码器相关 - 使用 YbridOpus C API private var decoder: OpaquePointer? private var sampleRate: Int32 = 16000 private var channels: Int32 = 1 private let maxFrameSize: Int32 = 5760 // 120 ms @48 kHz 的最大值 override init() { super.init() } deinit { cleanup() } private func initializeOpusDecoder() -> Bool { if decoder != nil { return true } var error: Int32 = 0 decoder = opus_decoder_create(sampleRate, channels, &error) guard error == OPUS_OK, decoder != nil else { return false } return true } private func destroyOpusDecoder() { if let decoder = decoder { opus_decoder_destroy(decoder) self.decoder = nil } } func setCallback(_ callback: AudioDataCallback?) { self.callback = callback } func startDecoding(sampleRate: Int32 = 16000, channels: Int32 = 1, frameSize: Int32 = 640) -> Bool { if isDecodingActive { return true } self.sampleRate = sampleRate self.channels = channels guard initializeOpusDecoder() else { return false } isDecodingActive = true return true } func stopDecoding() -> Bool { if !isDecodingActive { return false } isDecodingActive = false destroyOpusDecoder() return true } func processAudioData(_ data: Data) { guard isDecodingActive, let decoder = decoder, data.count > 0 else { return } // 过滤明显异常的数据包 guard data.count <= 1275 else { return } DispatchQueue.global(qos: .userInitiated).async { [weak self] in self?.decodeOpusPacket(data) } } // 简化的 Opus 解码方法,重点是容错处理 private func decodeOpusPacket(_ packet: Data) { guard let decoder = decoder else { return } let maxSamples = Int(maxFrameSize) var pcmBuffer = [opus_int16](repeating: 0, count: maxSamples * Int(channels)) let decodedSamples = packet.withUnsafeBytes { packetPtr in let uint8Ptr = packetPtr.bindMemory(to: UInt8.self).baseAddress return opus_decode( decoder, uint8Ptr, opus_int32(packet.count), &pcmBuffer, Int32(maxSamples), 0 ) } // 简单的错误处理:失败时重新初始化解码器 if decodedSamples <= 0 { print("Opus解码失败,重新初始化解码器") resetDecoder() return } // 提取解码的音频数据 let samplesPerChannel = Int(decodedSamples) let totalSamples = samplesPerChannel * Int(channels) let outputPCM = Array(pcmBuffer[0...size) // 回调通知 DispatchQueue.main.async { [weak self] in self?.callback?.onAudioDataReceived(data: data) } } func isDecoding() -> Bool { return isDecodingActive } func cleanup() { stopDecoding() } // MARK: - 公共接口 /// 获取当前配置信息 func getCurrentConfig() -> (sampleRate: Int32, channels: Int32) { return (sampleRate, channels) } }