如何使用CMSampleBuffer创建AVAudioPCMBuffer供AVAudioPlayerNode调度
CMSampleBuffer转AVAudioPCMBuffer实现方案
核心实现逻辑
- 第一步:校验CMSampleBuffer有效性
先调用CMSampleBufferIsValid判断buffer是否有效,再用CMSampleBufferGetNumSamples获取采样数,如果采样数为0直接返回空 - 第二步:获取音频格式信息
从CMSampleBuffer中取出CMAudioFormatDescriptionRef,再用AVAudioFormat的初始化方法initWithFormatDescription:生成对应的PCM格式对象 - 第三步:初始化AVAudioPCMBuffer
用拿到的AVAudioFormat和采样数作为参数,调用AVAudioPCMBuffer的initWithPCMFormat:frameCapacity:方法创建实例,注意frameCapacity要等于CMSampleBuffer的采样数 - 第四步:拷贝采样数据
调用CMSampleBufferCopyPCMDataIntoAudioBufferList把CMSampleBuffer里的PCM数据拷贝到AVAudioPCMBuffer的audioBufferList指针指向的内存中,同时手动设置PCMBuffer的frameLength为实际的采样数
注意事项
- 必须保证AVAssetReaderAudioMixOutput的输出格式是PCM格式,如果输出是压缩格式(如AAC)无法直接转换,需要先做解码,建议在初始化AVAssetReaderAudioMixOutput时直接指定输出为线性PCM格式:采样率推荐44100/48000,位深16bit,非交错存储,对应格式配置可以直接用
AVLinearPCMIsNonInterleaved为YES、AVLinearPCMBitDepthKey为@(16)的参数生成 - 转换完成后如果需要保留原始时间戳,可以通过
CMSampleBufferGetPresentationTimeStamp获取CMSampleBuffer的原始pts,在调用scheduleBuffer:atTime:options:completionHandler:时传入对应的AVAudioTime对象即可实现精准时序播放
完整代码示例(Swift)
func convert(sampleBuffer: CMSampleBuffer) -> AVAudioPCMBuffer? { // 校验有效性 guard CMSampleBufferIsValid(sampleBuffer) else { return nil } let sampleCount = CMSampleBufferGetNumSamples(sampleBuffer) guard sampleCount > 0 else { return nil } // 获取格式描述 guard let formatDesc = CMSampleBufferGetFormatDescription(sampleBuffer) else { return nil } guard let audioFormat = AVAudioFormat(formatDescription: formatDesc) else { return nil } // 初始化PCMBuffer guard let pcmBuffer = AVAudioPCMBuffer(pcmFormat: audioFormat, frameCapacity: AVAudioFrameCount(sampleCount)) else { return nil } // 拷贝数据 let status = CMSampleBufferCopyPCMDataIntoAudioBufferList( sampleBuffer, at: 0, frameCount: Int32(sampleCount), into: pcmBuffer.mutableAudioBufferList ) guard status == noErr else { return nil } pcmBuffer.frameLength = AVAudioFrameCount(sampleCount) return pcmBuffer }
完整代码示例(Objective-C)
- (AVAudioPCMBuffer *)convertSampleBufferToPCMBuffer:(CMSampleBufferRef)sampleBuffer { // 校验有效性 if (!CMSampleBufferIsValid(sampleBuffer)) return nil; CMItemCount sampleCount = CMSampleBufferGetNumSamples(sampleBuffer); if (sampleCount <= 0) return nil; // 获取格式描述 CMAudioFormatDescriptionRef formatDesc = CMSampleBufferGetFormatDescription(sampleBuffer); if (!formatDesc) return nil; AVAudioFormat *audioFormat = [[AVAudioFormat alloc] initWithFormatDescription:formatDesc]; if (!audioFormat) return nil; // 初始化PCMBuffer AVAudioPCMBuffer *pcmBuffer = [[AVAudioPCMBuffer alloc] initWithPCMFormat:audioFormat frameCapacity:(AVAudioFrameCount)sampleCount]; if (!pcmBuffer) return nil; // 拷贝数据 OSStatus status = CMSampleBufferCopyPCMDataIntoAudioBufferList( sampleBuffer, 0, (int32_t)sampleCount, pcmBuffer.mutableAudioBufferList ); if (status != noErr) return nil; pcmBuffer.frameLength = (AVAudioFrameCount)sampleCount; return pcmBuffer; }
内容的提问来源于stack exchange,提问作者luckysmg
相关产品推荐
相关产品推荐

