如何在 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 转换为数据?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Swift 中将 JSON 写入 Realm

如何在 Swift 中将文本居中?

如何在 Swift 中将 TextField 添加到 UIAlertView

如何在swift中将逗号添加到数字?

如何在 Swift 中将 UITextField 文本居中

如何在 Swift 中将 UIButton 居中?