Как преобразовать CMSampleBuffer в данные в Swift?

Мне нужно преобразовать 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)}
}

person Sridhar    schedule 12.07.2017    source источник
comment
Могу я спросить, как вы записывали с частотой дискретизации 16000?? Я позвонил session.setPreferredSampleRate, но я продолжаю получать частоту дискретизации 44100   -  person Ibrahim Yildirim    schedule 21.09.2017


Ответы (2)


Попробуйте приведенный ниже код преобразовать 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))
}
person Nilesh    schedule 12.07.2017
comment
Спасибо, Нилеш, но я получаю аудио образцы, а не видео или изображения. Подойдет ли для моего случая? - person Sridhar; 12.07.2017
comment
@Sridhar Я отредактировал свой ответ, пожалуйста, сообщите, работает ли он на вас или нет. - person Nilesh; 12.07.2017
comment
Спасибо, Nileash, я использовал ваш обновленный ответ, я получаю - Error Domain = NSOSStatusErrorDomain Code = 1954115647 (null) эта ошибка исходит от AudioPlayer - person Sridhar; 12.07.2017
comment
пусть formatDesc: CMFormatDescription? = CMSampleBufferGetFormatDescription(audioSample) ‹CMAudioFormatDescription 0x17010d890 [0x1b453ebb8]› { mediaType:'soun' mediaSubType:'lpcm' mediaSpecific: { ASBD: { mSampleRate: 48000.000000 mFormatID: 'lpcm' mFormatFlags: 0xc mBytesPerPacket: 4 mFramesPerPacket: 1 mBytesPerFrame: 4 mChannelsPerFrame : 2 mBitsPerChannel: 16 } cookie: {(null)} ACL: {(null)} FormatList Array: {(null)} } extensions: {(null)} - person Sridhar; 12.07.2017
comment
Операция не может быть завершена. (Ошибка OSStatus 1954115647.) - person Sridhar; 12.07.2017
comment
Где вы получаете ошибку и почему вы выбираете formatDesc, вы должны напрямую передавать свои данные в свой аудиоплеер следующим образом - пусть player = try! AVAudioPlayer(данные: данные) \n player.play() - person Nilesh; 12.07.2017
comment
Я получаю сообщение об ошибке при воспроизведении let player = try! AVAudioPlayer(данные:данные) здесь - person Sridhar; 12.07.2017
comment
Хорошо, вместо того, чтобы напрямую инициализировать аудиоплеер с объектом данных, не могли бы вы сохранить файл в папке «Документы», а затем инициализировать плеер с URL-адресом файла. - person Nilesh; 12.07.2017
comment
Давайте продолжим обсуждение в чате. - person Sridhar; 12.07.2017
comment
@ Шридхар, это решено? Я столкнулся с той же проблемой. - person ossamacpp; 04.02.2019
comment
Я получаю предупреждение для оператора let buffers =. Я получаю Инициализация 'UnsafeBufferPointer‹AudioBuffer›' приводит к оборванному указателю буфера - person Dustin Nielson; 14.05.2020
comment
У меня такая же проблема. - person Nickkk; 06.02.2021

Использование 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];
person plamkata__    schedule 09.04.2019
comment
Это Objective-C, вопрос был задан для Swift :) Вы также можете добавить быструю версию - person iSpain17; 15.08.2019