Search code examples
iosswiftavfoundationaudiotoolbox

How to create AVAudioPCMBuffer with CMSampleBuffer?


I have An AVAsset and I use AVAssetReaderAudioMixOutput to get CMSampleBuffer,and I want to use this CMSampleBuffer to create the AVAudioPlayerNode to scheduleBuffer

How to do it,anyone help?


Solution

  • extension AVAudioPCMBuffer {
        static func create(from sampleBuffer: CMSampleBuffer) -> AVAudioPCMBuffer? {
            
            guard let description: CMFormatDescription = CMSampleBufferGetFormatDescription(sampleBuffer),
                  let sampleRate: Float64 = description.audioStreamBasicDescription?.mSampleRate,
                  let channelsPerFrame: UInt32 = description.audioStreamBasicDescription?.mChannelsPerFrame /*,
                                                                                                             let numberOfChannels = description.audioChannelLayout?.numberOfChannels */
            else { return nil }
            
            guard let blockBuffer: CMBlockBuffer = CMSampleBufferGetDataBuffer(sampleBuffer) else {
                return nil
            }
            
            let samplesCount = CMSampleBufferGetNumSamples(sampleBuffer)
            
            //let length: Int = CMBlockBufferGetDataLength(blockBuffer)
            
            let audioFormat = AVAudioFormat(commonFormat: .pcmFormatFloat32, sampleRate: sampleRate, channels: AVAudioChannelCount(1), interleaved: false)
            
            let buffer = AVAudioPCMBuffer(pcmFormat: audioFormat!, frameCapacity: AVAudioFrameCount(samplesCount))!
            buffer.frameLength = buffer.frameCapacity
            
            // GET BYTES
            var dataPointer: UnsafeMutablePointer<Int8>?
            CMBlockBufferGetDataPointer(blockBuffer, atOffset: 0, lengthAtOffsetOut: nil, totalLengthOut: nil, dataPointerOut: &dataPointer)
            
            guard var channel: UnsafeMutablePointer<Float> = buffer.floatChannelData?[0],
                  let data = dataPointer else { return nil }
            
            var data16 = UnsafeRawPointer(data).assumingMemoryBound(to: Int16.self)
            
            for _ in 0...samplesCount - 1 {
                channel.pointee = Float32(data16.pointee) / Float32(Int16.max)
                channel += 1
                for _ in 0...channelsPerFrame - 1 {
                    data16 += 1
                }
                
            }
            
            return buffer
        }
    }