使用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
相关产品推荐
相关产品推荐

