如何在 Swift 中将 CMSampleBuffer 转换为数据?

Posted

技术标签:

【中文标题】如何在 Swift 中将 CMSampleBuffer 转换为数据?【英文标题】:How to convert CMSampleBuffer to Data in Swift? 【发布时间】:2017-07-12 10:12:30 【问题描述】:

我需要将CMSampleBuffer 转换为Data 格式。我正在为音频相关任务使用一个第三方框架。该框架为我提供CMSampleBuffer 对象中的流式(即实时音频)音频。

像这样:

func didAudiostreaming(audioSample: CMSampleBuffer!) 
    //Here I need to conver this to Data format. 
    //Because I am using GRPC framework for Audio Recognization, 

请提供将CMSampleBuffer 转换为Data 的步骤。

仅供参考

    let formatDesc:CMFormatDescription? = CMSampleBufferGetFormatDescription(audioSample)

    <CMAudioFormatDescription 0x17010d890 [0x1b453ebb8]> 
    mediaType:'soun' 
    mediaSubType:'lpcm' 
    mediaSpecific: 
        ASBD: 
            mSampleRate: 16000.000000 
            mFormatID: 'lpcm' 
            mFormatFlags: 0xc 
            mBytesPerPacket: 2 
            mFramesPerPacket: 1 
            mBytesPerFrame: 2 
            mChannelsPerFrame: 1 
            mBitsPerChannel: 16      
        cookie: (null) 
        ACL: (null)
        FormatList Array: (null) 
     
    extensions: (null)

【问题讨论】:

请问你是如何以16000的采样率记录的?我已经打电话给session.setPreferredSampleRate,但我的采样率一直在 44100 【参考方案1】:

尝试下面的代码将 CMSampleBuffer 转换为 NSData。

let imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer)
CVPixelBufferLockBaseAddress(imageBuffer!, CVPixelBufferLockFlags(rawValue: 0))
let bytesPerRow = CVPixelBufferGetBytesPerRow(imageBuffer!)
let height = CVPixelBufferGetHeight(imageBuffer!)
let src_buff = CVPixelBufferGetBaseAddress(imageBuffer!)
let data = NSData(bytes: src_buff, length: bytesPerRow * height)
CVPixelBufferUnlockBaseAddress(imageBuffer!, CVPixelBufferLockFlags(rawValue: 0))

编辑-

对于 AudioBuffer 使用下面的代码 -

var audioBufferList = AudioBufferList()
var data = Data()
var blockBuffer : CMBlockBuffer?

CMSampleBufferGetAudioBufferListWithRetainedBlockBuffer(sampleBuffer, nil, &audioBufferList, MemoryLayout<AudioBufferList>.size, nil, nil, 0, &blockBuffer)

let buffers = UnsafeBufferPointer<AudioBuffer>(start: &audioBufferList.mBuffers, count: Int(audioBufferList.mNumberBuffers))

for audioBuffer in buffers 
    let frame = audioBuffer.mData?.assumingMemoryBound(to: UInt8.self)
    data.append(frame!, count: Int(audioBuffer.mDataByteSize))

【讨论】:

感谢 Nilesh,但我收到的不是视频或图像的音频样本。它适用于我的情况吗? @Sridhar 我已经编辑了我的答案,请告诉我是否适合你。 感谢 Nileash,我使用了您更新的答案,我得到了 - Error Domain=NSOSStatusErrorDomain Code=1954115647 "(null)" 这个错误来自 AudioPlayer 让 formatDesc:CMFormatDescription? = CMSampleBufferGetFormatDescription(audioSample) mediaType:'soun' mediaSubType:'lpcm' mediaSpecific: ASBD: mSampleRate: 48000.000000 mFormatID: 'lpcm' mFormatFlags: 0xc mBytesPerPacket: 4 mFrame : 2 mBitsPerChannel: 16 cookie: (null) ACL: (null) FormatList Array: (null) 扩展名: (null) 操作无法完成。 (OSStatus 错误 1954115647。)【参考方案2】:

使用CMSampleBufferGetAudioBufferListWithRetainedBlockBuffer 需要在某个时间点调用CFRelease(blockBuffer),因为缓冲区被保留,如果不释放,缓冲区池最终将变为空,并且不会生成新的CMSampleBuffer

我建议使用以下方法直接获取数据:

CMBlockBufferRef blockBuffer = CMSampleBufferGetDataBuffer(sampleBuffer);
size_t lengthAtOffset;
size_t totalLength;
char *data;
CMBlockBufferGetDataPointer(blockBuffer, 0, &lengthAtOffset, &totalLength, &data);

NSData *audioData = [NSData dataWithBytes:data length:totalLength];

【讨论】:

这是 Objective-C,有人问过 Swift 的问题 :) 你也可以添加 swift 版本

以上是关于如何在 Swift 中将 CMSampleBuffer 转换为数据?的主要内容,如果未能解决你的问题,请参考以下文章