I'm trying to take a local m4a file and compress/down-sample this file (For the purposes of making a smaller file).
Now I stuck with error when I try to append sample buffer.
Error description:
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason:
'*** -[AVAssetWriterInput appendSampleBuffer:] Cannot append sample buffer: Input
buffer must be in an uncompressed format when outputSettings is not nil'
Code where I try compress original audio file into same file format but with lower bitrate:
@objc func nextTapped() {
let audioURL = RecordWhistleViewController.getWhistleURL()
var asset = AVAsset.init(url: audioURL)
let exportPath = URL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent("out.m4a").path
print("export PATH IS \(exportPath)")
let exportURL = URL(fileURLWithPath: exportPath)
var readerError: Error? = nil
var reader: AVAssetReader? = nil
do {
reader = try AVAssetReader(asset: asset)
} catch {
print("error in reader \(error)")
}
let track = asset.tracks(withMediaType: .audio)[0]
let readerOutput = AVAssetReaderTrackOutput(track: track, outputSettings: nil)
reader?.add(readerOutput)
var writerError: Error? = nil
var writer: AVAssetWriter? = nil
do {
writer = try AVAssetWriter(outputURL: exportURL, fileType: .m4a)
} catch {
print("ERROR IN writer \(error)")
}
var channelLayout = AudioChannelLayout()
memset(&channelLayout, 0, MemoryLayout<AudioChannelLayout>.size)
channelLayout.mChannelLayoutTag = kAudioChannelLayoutTag_Stereo
let outputSettings = [
AVFormatIDKey: Int(kAudioFormatMPEG4AAC),
AVSampleRateKey: 12000,
AVNumberOfChannelsKey: 2,
AVEncoderBitRateKey: 128000,
AVChannelLayoutKey: Data(bytes: &channelLayout, count: MemoryLayout<AudioChannelLayout>.size)
] as [String : Any]
let writerInput = AVAssetWriterInput(mediaType: .audio, outputSettings: outputSettings as? [String: Any])
writerInput.expectsMediaDataInRealTime = false
writer?.add(writerInput)
writer?.startWriting()
writer?.startSession(atSourceTime: .zero)
reader?.startReading()
let mediaInputQueue = DispatchQueue(label: "mediaInputQueue")
writerInput.requestMediaDataWhenReady(on: mediaInputQueue) {
print("Asset writer ready: \(writerInput.isReadyForMoreMediaData)")
while writerInput.isReadyForMoreMediaData {
var nextBuffer: CMSampleBuffer?
nextBuffer = readerOutput.copyNextSampleBuffer()
if nextBuffer != nil {
if let nextBuffer = nextBuffer {
print("adding buffer")
writerInput.append(nextBuffer)
}
} else {
writerInput.markAsFinished()
reader?.cancelReading()
writer?.finishWriting {
print("Asset writer finished writing")
}
break
}
}
}
}
The original audio have settings:
let settings = [
AVFormatIDKey: Int(kAudioFormatMPEG4AAC),
AVSampleRateKey: 12000,
AVNumberOfChannelsKey: 1,
AVEncoderAudioQualityKey: AVAudioQuality.high.rawValue
]
Code for URL to original file:
class func getDocumentsDirectory() -> URL {
let paths = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
let documentsDirectory = paths[0]
return documentsDirectory
}
Edited:
here is full description of Error:
assetWriter_finishBuildingAudioTrackWithSourceFormatDescription signalled err=-12413 (kFigAssetWriterError_InappropriateSourceFormat) (AssetWriter can only compress LPCM audio) at /Library/Caches/com.apple.xbs/Sources/EmbeddedCoreMedia_Sim/EmbeddedCoreMedia-2765.6/Prototypes/Export/FigAssetWriter.c:636
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** -[AVAssetWriterInput appendSampleBuffer:] Cannot append sample buffer: Input buffer must be in an uncompressed format when outputSettings is not nil'
The errors says that asset writer wants uncompressed sample buffers, but the AVAssetReaderTrackOutput
header file says:
A value of nil for outputSettings configures the output to vend samples in their original format as stored by the specified track.
Your file is an .m4a
file so the samples are likely going to be compressed as AAC.
To get uncompressed linear PCM, pass kAudioFormatLinearPCM
as the AVFormatIDKey
in outputSettings
:
let readerOutputSettings = [
AVFormatIDKey: kAudioFormatLinearPCM
]
let readerOutput = AVAssetReaderTrackOutput(track: track, outputSettings: readerOutputSettings)
Note that you are decoding and re-encoding or transcoding your file.