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

使用AVFoundation将MP3转AAC时触发NSInternalInconsistencyException错误

AVFoundation MP3转AAC时触发NSInternalInconsistencyException错误

错误信息

Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: '*** -[AVAssetReaderTrackOutput copyNextSampleBuffer] cannot copy next sample buffer before adding this output to an instance of AVAssetReader (using -addOutput:) and calling -startReading on that asset reader'

问题代码

let asset = AVAsset(url: tmpURL)
            
let assetReader = try AVAssetReader(asset: asset)
            
if let audioTrack = try await asset.loadTracks(withMediaType: .audio).first {
    let assetReaderOutput = AVAssetReaderTrackOutput(track: audioTrack,
                                                     outputSettings: [
                                                        AVFormatIDKey: Int(kAudioFormatLinearPCM)
                                                     ])
                
    assetReader.add(assetReaderOutput)
    assetReader.startReading()
                
    let assetWriter = try AVAssetWriter(outputURL: outputURL, fileType: .m4a)
    let outputSettings: [String: Any] = [
        AVFormatIDKey: kAudioFormatMPEG4AAC,
        AVSampleRateKey: 44100,
        AVEncoderBitRateKey: 256000,
        AVNumberOfChannelsKey: 2
    ]
    let assetWriterInput = AVAssetWriterInput(mediaType: AVMediaType.audio,
                                                          outputSettings: outputSettings)
    assetWriter.add(assetWriterInput)
                
    assetWriter.startWriting()
    assetWriter.startSession(atSourceTime: .zero)
                
                
    let processingQueue = DispatchQueue(label: "processingQueue")
                
    assetWriterInput.requestMediaDataWhenReady(on: processingQueue) {
        while assetWriterInput.isReadyForMoreMediaData {
            guard let nextBuffer = assetReaderOutput.copyNextSampleBuffer() else {
                assetWriterInput.markAsFinished()
                assetWriter.finishWriting(completionHandler: {
                    if assetWriter.status == .completed {
                        print("File converted successfully")
                    } else {
                        print("File conversion failed with error: \(assetWriter.error?.localizedDescription ?? "unknown error")")
                    }
                })
                break
            }
            assetWriterInput.append(nextBuffer)
        }
    }
}

问题原因及修复

问题出在AVAssetReader的初始化时机过早:你在异步加载audioTrack之前就创建了assetReader,而AVAssetReader的状态和关联的asset强绑定。在等待try await asset.loadTracks的过程中,asset的内部状态可能发生变化,导致后续添加output、调用startReading的操作无法正确关联到reader,系统最终判定output未被正确添加。

核心修复步骤

将AVAssetReader的初始化代码移动到获取到有效audioTrack之后,确保asset状态稳定后再创建reader,同时调整reader和writer的启动顺序,避免丢帧:

let asset = AVAsset(url: tmpURL)
            
if let audioTrack = try await asset.loadTracks(withMediaType: .audio).first {
    // 移至此处创建assetReader,确保asset状态稳定
    let assetReader = try AVAssetReader(asset: asset)
    
    let assetReaderOutput = AVAssetReaderTrackOutput(track: audioTrack,
                                                     outputSettings: [
                                                        AVFormatIDKey: Int(kAudioFormatLinearPCM)
                                                     ])
                
    assetReader.add(assetReaderOutput)
    
    // 先配置并启动assetWriter,确保就绪后再启动reader
    let assetWriter = try AVAssetWriter(outputURL: outputURL, fileType: .m4a)
    let outputSettings: [String: Any] = [
        AVFormatIDKey: kAudioFormatMPEG4AAC,
        AVSampleRateKey: 44100,
        AVEncoderBitRateKey: 256000,
        AVNumberOfChannelsKey: 2
    ]
    let assetWriterInput = AVAssetWriterInput(mediaType: AVMediaType.audio,
                                              outputSettings: outputSettings)
    assetWriter.add(assetWriterInput)
                
    assetWriter.startWriting()
    assetWriter.startSession(atSourceTime: .zero)
    
    // 此时再启动reader,避免数据丢失
    assetReader.startReading()
                
    let processingQueue = DispatchQueue(label: "processingQueue")
                
    assetWriterInput.requestMediaDataWhenReady(on: processingQueue) {
        while assetWriterInput.isReadyForMoreMediaData {
            guard let nextBuffer = assetReaderOutput.copyNextSampleBuffer() else {
                assetWriterInput.markAsFinished()
                assetWriter.finishWriting(completionHandler: {
                    if assetWriter.status == .completed {
                        print("File converted successfully")
                    } else {
                        print("File conversion failed with error: \(assetWriter.error?.localizedDescription ?? "unknown error")")
                    }
                })
                break
            }
            assetWriterInput.append(nextBuffer)
        }
    }
}

额外说明

原代码先启动reader再配置writer的顺序存在丢帧风险——reader已经开始读取数据,但writer还没准备好接收,调整顺序后可以确保writer就绪后再启动reader,避免数据丢失。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.30 07:25:35