I am trying to convert a mp3-audio to aac-audio and I am getting the error
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'
when using this code:
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)
}
}
}
I don't understand that, because I am actually adding the output to a reader and then I start assetReader.startReading()
. What am I doing wrong?
Your assetReader
is going out of scope. I guess the AVAssetReaderOutput
has no back pointer to its AVAssetReader
. So your code works if you add a "structural print" after appending the buffer, because this extends the AVAssetReader
scope:
assetWriterInput.append(nextBuffer)
print("structural print \(assetReader)")
Unlike AVAssetWriter
, whose lifetime is made obvious by its startWriting
and finishWriting
methods, AVAssetReader
has a startReading
but no finishReading
method, making it easy to mismanage its scope. API design is hard. You should probably bundle your AVAssetReader
and AVAssetReaderOutput
together in a class or struct along with a comment warning against refactoring the object to avoid this situation in the future.