Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

CMReadySampleBuffer.copyPCMData() fails with -12731 (kCMSampleBufferError_RequiredParameterMissing) error

I'm trying to figure out how to use the new outputProvider API on AVAssetReader. Specifically, I want to open a media file (video or audio), extract its audio, and write it back out as PCM. I have the following code:

import Foundation
import AVFoundation

enum AudioExtractorError: Error {
    case noAudio
    case bufferAllocationFailed
}

@available(macOS 26.0, iOS 26.0, *)
class AudioExtractor {

    init(fileURL: URL) {
        self.fileURL = fileURL
    }

    func extractAudio(from fileURL: URL, to outputFolder: URL) async throws -> URL {
        let asset = AVAsset(url: fileURL)
        guard let audioTrack = try await asset.loadTracks(withMediaType: .audio).first else {
            throw AudioExtractorError.noAudio
        }

        let reader = try AVAssetReader(asset: asset)
        let outputSettings: [String: Any] = [
            AVFormatIDKey: kAudioFormatLinearPCM,
            AVLinearPCMIsBigEndianKey: false,
            AVLinearPCMBitDepthKey: 32,
            AVLinearPCMIsFloatKey: true,
            AVLinearPCMIsNonInterleaved: false,
            AVNumberOfChannelsKey: 2
        ]

        let trackOutput = AVAssetReaderTrackOutput(track: audioTrack, outputSettings: outputSettings)
        let outputProvider = reader.outputProvider(for: trackOutput)

        try reader.start()

        let outputURL = outputFolder
            .appendingPathComponent(UUID().uuidString)
            .appendingPathExtension("caf")

        let outputFile = try AVAudioFile(forWriting: outputURL, settings: outputSettings)
        while let sampleBuffer = try await outputProvider.next() {
            guard case let CMSampleBuffer.DynamicContent.dataBuffer(blockBuffer) = sampleBuffer.content else {
                continue
            }
            let blockSampleBuffer = CMReadySampleBuffer(dataBuffer: blockBuffer,
                                                        formatDescription: sampleBuffer.formatDescription!,
                                                        sampleProperties: sampleBuffer.sampleProperties)

            guard let pcmBuffer = AVAudioPCMBuffer(pcmFormat: outputFile.processingFormat, frameCapacity: AVAudioFrameCount(sampleBuffer.sampleCount)) else {
                throw AudioExtractorError.bufferAllocationFailed
            }
            try blockSampleBuffer.copyPCMData(fromRange: 0..<sampleBuffer.sampleCount, into: pcmBuffer.mutableAudioBufferList)

            try outputFile.write(from: pcmBuffer)
        }

        return outputURL
    }

    let fileURL: URL
}

When I run this, the call to blockSampleBuffer.copyPCMData() fails with error code -12731 which is kCMSampleBufferError_RequiredParameterMissing. Has anyone managed to get this API working?

like image 640
Andrew Madsen Avatar asked Aug 14 '26 20:08

Andrew Madsen


1 Answers

It's great to see a new question on here!

There are two sources of copyPCMData() returning -12731 = kCMSampleBufferError_RequiredParameterMissing in your code:

  1. The AVAudioFile initializer you used gives you an interleaved fileFormat but a deinterleaved processingFormat which does not match AVAssetReaderTrackOutput's configuration (it's not obvious, I've mentioned this before)
  2. You're not setting the AVAudioPCMBuffer.frameLength which results in a 0 AudioBuffer.mDataByteSize

With the following changes, the code works for me:

- let outputFile = try AVAudioFile(forWriting: outputURL, settings: outputSettings)
+ let outputFile = try AVAudioFile(forWriting: outputURL, settings: outputSettings, commonFormat: .pcmFormatFloat32, interleaved: true) // note the inverted senses of interleaved vs AVLinearPCMIsNonInterleaved!

...

+ pcmBuffer.frameLength = AVAudioFrameCount(sampleBuffer.sampleCount)
  try blockSampleBuffer.copyPCMData(fromRange: 0..<sampleBuffer.sampleCount,
                                         into: pcmBuffer.mutableAudioBufferList)
like image 94
Rhythmic Fistman Avatar answered Aug 16 '26 13:08

Rhythmic Fistman