You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

239 lines
7.1 KiB

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 {
print("音频数据包太大: \(data.count) bytes,跳过处理")
return
}
// 检查最小数据包大小(Opus最小包通常至少几个字节)
guard data.count >= 3 else {
print("音频数据包太小: \(data.count) bytes,跳过处理")
return
}
// 简单的 Opus 头部验证(检查是否是有效的 Opus 数据包格式)
let firstByte = data[0]
// Opus packets start with a TOC (Table of Contents) byte
// 检查TOC字节是否在合理范围内
if firstByte == 0x00 || firstByte == 0xFF {
print("可疑的Opus数据包头部: 0x\(String(format: "%02X", firstByte)),跳过处理")
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 {
let errorCode = decodedSamples
print("Opus解码失败,错误代码: \(errorCode),重新初始化解码器")
// 根据错误类型进行不同的处理
if errorCode == -1 { // OPUS_BAD_ARG
print("Opus解码参数错误,执行完全重置")
performCompleteReset()
} else if errorCode == -2 { // OPUS_BUFFER_TOO_SMALL
print("Opus缓冲区太小")
resetDecoder()
} else if errorCode == -3 { // OPUS_INTERNAL_ERROR
print("Opus内部错误,执行完全重置")
performCompleteReset()
} else {
print("Opus其他错误,尝试简单重置")
resetDecoder()
}
return
}
// 验证解码结果的合理性
guard decodedSamples <= maxFrameSize else {
print("Opus解码样本数超出预期: \(decodedSamples) > \(maxFrameSize)")
resetDecoder()
return
}
// 提取解码的音频数据
let samplesPerChannel = Int(decodedSamples)
let totalSamples = samplesPerChannel * Int(channels)
// 验证总样本数的合理性
guard totalSamples <= pcmBuffer.count else {
print("计算的总样本数超出缓冲区: \(totalSamples) > \(pcmBuffer.count)")
resetDecoder()
return
}
let outputPCM = Array(pcmBuffer[0..<totalSamples])
if !outputPCM.isEmpty {
processPCMData(outputPCM)
}
}
// 重置解码器的简单方法
private func resetDecoder() {
print("执行简单解码器重置")
destroyOpusDecoder()
_ = initializeOpusDecoder()
}
// 执行完全重置(包括停止和重启解码)
private func performCompleteReset() {
print("执行完全解码器重置")
let wasActive = isDecodingActive
let currentSampleRate = self.sampleRate
let currentChannels = self.channels
// 完全停止解码
if wasActive {
isDecodingActive = false
destroyOpusDecoder()
}
// 重新初始化所有状态
self.sampleRate = currentSampleRate
self.channels = currentChannels
// 如果之前是活跃状态,重新启动
if wasActive {
guard initializeOpusDecoder() else {
print("完全重置后重新初始化失败")
return
}
isDecodingActive = true
print("完全重置成功,解码器已重新启动")
}
}
private func processPCMData(_ pcmData: [opus_int16]) {
// 转换为 Data
let data = Data(bytes: pcmData, count: pcmData.count * MemoryLayout<opus_int16>.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)
}
}