You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.26 14:45:06