You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Swift中音频转AAC遇NSOSStatusErrorDomain -10865错误求助

错误原因排查与修复方案

错误核心原因

错误码 -10865 对应苹果音频框架的 kAudioFormatUnsupportedDataFormatError,本质是音频格式不兼容:你在使用 AVAudioEngine 时,混音节点的输出格式与要写入的AAC文件格式不匹配,导致系统无法设置音频格式抛出致命异常。

问题细节分析

  1. 格式不匹配:你安装混音节点Tap时使用了 audioEngine.mainMixerNode.outputFormat(forBus: 0),这个格式是系统默认的音频输出格式(通常是立体声、PCM浮点格式),但你要生成的AAC是单声道、MPEG4AAC编码格式,两者参数不兼容。
  2. 冗余的转换逻辑:用AudioEngine播放+录屏的方式转换音频完全没必要,这种方式容易出现格式同步问题,代码复杂度高且不稳定。

修复方案

方案一:修复现有AudioEngine逻辑

调整Tap的格式为输出文件对应的PCM格式,确保整个音频链路格式一致:

func convertToAAC() {
    guard let audioFileURL = audioFileURL else {
        print("No audio file to convert")
        return
    }

    do {
        let file = try AVAudioFile(forReading: audioFileURL)
        let duration = Double(file.length) / file.fileFormat.sampleRate
        print("Audio file duration: \(duration) seconds")

        if duration == 0.0 {
            print("Audio file has zero duration, skipping conversion.")
            return
        }

        guard let buffer = AVAudioPCMBuffer(pcmFormat: file.processingFormat, frameCapacity: AVAudioFrameCount(file.length)), buffer.frameCapacity != 0 else {
            print("Failed to create audio buffer or buffer frame capacity is zero")
            return
        }
        try file.read(into: buffer)
        print("Audio buffer read successfully")

        let outputFileName = "converted.m4a"
        let outputFilePath = getDocumentsDirectory().appendingPathComponent(outputFileName)
        
        // 定义输出设置,同时生成对应的PCM格式供Tap使用
        let outputSettings = [
            AVFormatIDKey: Int(kAudioFormatMPEG4AAC),
            AVSampleRateKey: 44100,
            AVNumberOfChannelsKey: 1,
            AVEncoderAudioQualityKey: AVAudioQuality.high.rawValue
        ] as [String : Any]
        guard let outputFormat = AVAudioFormat(settings: outputSettings) else {
            print("Failed to create output format")
            return
        }
        
        let outputFile = try AVAudioFile(forWriting: outputFilePath, settings: outputSettings, commonFormat: .pcmFormatInt16, interleaved: false)

        let audioEngine = AVAudioEngine()
        let playerNode = AVAudioPlayerNode()
        audioEngine.attach(playerNode)
        
        // 用buffer格式连接播放器与混音器
        audioEngine.connect(playerNode, to: audioEngine.mainMixerNode, format: buffer.format)

        audioEngine.prepare()
        try audioEngine.start()

        var isConversionComplete = false
        
        playerNode.scheduleBuffer(buffer, at: nil) {
            DispatchQueue.main.async {
                isConversionComplete = true
                print("Conversion to AAC completed: \(outputFilePath)")
            }
        }

        playerNode.play()
        
        // 使用输出文件对应的PCM格式安装Tap
        audioEngine.mainMixerNode.installTap(onBus: 0, bufferSize: 1024, format: outputFormat) { (buffer, time) in
            if !isConversionComplete {
                do {
                    try outputFile.write(from: buffer)
                } catch {
                    print("Error writing buffer to file: \(error.localizedDescription)")
                }
            } else {
                playerNode.stop()
                audioEngine.stop()
                audioEngine.mainMixerNode.removeTap(onBus: 0)
            }
        }
    } catch {
        print("Error converting audio file: \(error.localizedDescription)")
    }
}

方案二:使用AVAssetExportSession(推荐)

直接用系统提供的导出会话转换格式,避免AudioEngine的格式兼容问题,代码简洁可靠:

func convertToAAC() {
    guard let audioFileURL = audioFileURL else {
        print("No audio file to convert")
        return
    }

    let outputFileName = "converted.m4a"
    let outputFilePath = getDocumentsDirectory().appendingPathComponent(outputFileName)
    
    // 清理已存在的输出文件
    if FileManager.default.fileExists(atPath: outputFilePath.path) {
        try? FileManager.default.removeItem(at: outputFilePath)
    }

    let asset = AVURLAsset(url: audioFileURL)
    guard let exportSession = AVAssetExportSession(asset: asset, presetName: AVAssetExportPresetAppleM4A) else {
        print("Failed to create export session")
        return
    }
    
    exportSession.outputURL = outputFilePath
    exportSession.outputFileType = .m4a
    exportSession.shouldOptimizeForNetworkUse = true
    
    exportSession.exportAsynchronously {
        DispatchQueue.main.async {
            switch exportSession.status {
            case .completed:
                print("Conversion to AAC completed: \(outputFilePath)")
                // 直接回放转换后的文件
                self.playAudio(at: outputFilePath)
            case .failed:
                print("Export failed: \(exportSession.error?.localizedDescription ?? "Unknown error")")
            case .cancelled:
                print("Export cancelled")
            default:
                break
            }
        }
    }
}

// 新增简单的音频回放方法
func playAudio(at url: URL) {
    do {
        let audioPlayer = try AVAudioPlayer(contentsOf: url)
        audioPlayer.play()
    } catch {
        print("Failed to play audio: \(error.localizedDescription)")
    }
}

额外注意事项

  1. 确保在Info.plist中添加NSMicrophoneUsageDescription权限描述,否则音频录制会直接失败。
  2. 录制完成后,audioRecorder = nil的操作是正确的,确保录音文件被完全释放,避免后续转换时文件被占用。

内容的提问来源于stack exchange,提问作者narner

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.22 03:54:51