2016-07-12 6 views
14

Я пытаюсь создать копию CMSampleBuffer, возвращенной функцией captureOutput в AVCaptureVideoDataOutputSampleBufferDelegate.Вытягивание данных из CMSampleBuffer для создания глубокой копии

Поскольку CMSampleBuffers исходят из предварительно распределенного пула (15) буферов, если я присоединяю ссылку на них, они не могут быть вспомнены. Это приведет к удалению всех оставшихся кадров.

To maintain optimal performance, some sample buffers directly reference pools of memory that may need to be reused by the device system and other capture inputs. This is frequently the case for uncompressed device native capture where memory blocks are copied as little as possible. If multiple sample buffers reference such pools of memory for too long, inputs will no longer be able to copy new samples into memory and those samples will be dropped.

If your application is causing samples to be dropped by retaining the provided CMSampleBufferRef objects for too long, but it needs access to the sample data for a long period of time, consider copying the data into a new buffer and then releasing the sample buffer (if it was previously retained) so that the memory it references can be reused.

Очевидно, что я должен скопировать CMSampleBuffer но CMSampleBufferCreateCopy() будет создавать только неполную копию. Таким образом, я заключаю, что я должен использовать CMSampleBufferCreate(). Я заполнил 12! параметры, которые требуется конструктору, но столкнулись с проблемой, что мои CMSampleBuffers не содержат blockBuffer (не совсем уверен, что это такое, но кажется важным).

Этот вопрос задан несколько раз, но не ответил.

Deep Copy of CMImageBuffer or CVImageBuffer и Create a copy of CMSampleBuffer in Swift 2.0

Один из возможных ответов: «Я наконец-то понял, как использовать это, чтобы создать глубокий клон. Все способы копирования повторно данных в куче, который удерживал бы запереть AVCaptureSession. Так что у меня было чтобы вывести данные в объект NSMutableData, а затем создать новый буфер выборок ». credit to Rob on SO. Однако я не знаю, как это сделать.

Если вас интересует, this является результатом print(sampleBuffer). Не упоминается blockBuffer, иначе CMSampleBufferGetDataBuffer возвращает nil. Существует imageBuffer, но создание «копии» с использованием CMSampleBufferCreateForImageBuffer тоже не освобождает CMSampleBuffer.


EDIT: Поскольку этот вопрос был отправлен, я пытался еще больше способов копирования памяти.

Я сделал то же самое, что и пользователь Kametrixom. This - это моя попытка одной и той же идеи, чтобы сначала скопировать CVPixelBuffer, а затем использовать CMSampleBufferCreateForImageBuffer для создания окончательного буфера выборки. Однако это приводит к одной из двух ошибок:

  • EXC_BAD_ACCESS в инструкции memcpy. AKA - segfault от попытки доступа за пределами памяти приложения.
  • Или память будет скопирована успешно, но CMSampleBufferCreateReadyWithImageBuffer() завершится с кодом результата -12743, который «Указывает, что формат данного носителя не соответствует указанному описанию формата. Например, описание формата в паре с CVImageBuffer, которое не работает CMVideoFormatDescriptionMatchesImageBuffer «.

Вы можете видеть, что и Kametrixom и я использовали CMSampleBufferGetFormatDescription(sampleBuffer), чтобы попытаться скопировать описание формата исходного буфера. Таким образом, я не уверен, почему формат данных носителей не соответствует указанному описанию формата.

+1

Я оставил [комментарий] (http://stackoverflow.com/questions/35467847/create-a-copy-of-cmsamplebuffer-in-swift-2-0#comment64085484_35469364) от вашего имени. –

+0

@ JoshCaswell Вы джентльмен и ученый. – bennyty

+0

@bennyty Как бы вы глубоко скопировали аудио-образец? –

ответ

8

Хорошо, я думаю, что, наконец, понял.Я создал расширение помощника, чтобы сделать полную копию CVPixelBuffer:

extension CVPixelBuffer { 
    func copy() -> CVPixelBuffer { 
     precondition(CFGetTypeID(self) == CVPixelBufferGetTypeID(), "copy() cannot be called on a non-CVPixelBuffer") 

     var _copy : CVPixelBuffer? 
     CVPixelBufferCreate(
      nil, 
      CVPixelBufferGetWidth(self), 
      CVPixelBufferGetHeight(self), 
      CVPixelBufferGetPixelFormatType(self), 
      CVBufferGetAttachments(self, kCVAttachmentMode_ShouldPropagate)?.takeUnretainedValue(), 
      &_copy) 

     guard let copy = _copy else { fatalError() } 

     CVPixelBufferLockBaseAddress(self, kCVPixelBufferLock_ReadOnly) 
     CVPixelBufferLockBaseAddress(copy, 0) 

     for plane in 0..<CVPixelBufferGetPlaneCount(self) { 
      let dest = CVPixelBufferGetBaseAddressOfPlane(copy, plane) 
      let source = CVPixelBufferGetBaseAddressOfPlane(self, plane) 
      let height = CVPixelBufferGetHeightOfPlane(self, plane) 
      let bytesPerRow = CVPixelBufferGetBytesPerRowOfPlane(self, plane) 

      memcpy(dest, source, height * bytesPerRow) 
     } 

     CVPixelBufferUnlockBaseAddress(copy, 0) 
     CVPixelBufferUnlockBaseAddress(self, kCVPixelBufferLock_ReadOnly) 

     return copy 
    } 
} 

Теперь вы можете использовать это в вашем didOutputSampleBuffer метод:

guard let pixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer) else { return } 

let copy = pixelBuffer.copy() 

toProcess.append(copy) 

Но быть в курсе, одна такая pixelBuffer занимает около 3 МБ память (1080p), что означает, что в 100 кадрах вы получили уже около 300 МБ, что примерно соответствует тому, на котором iPhone говорит STAHP (и сбой).

Обратите внимание, что вы на самом деле не хотите копировать CMSampleBuffer, так как он действительно содержит только CVPixelBuffer, потому что это изображение.

+0

Mate. Ты великолепен. Я использовал ваше расширение и добавил AVAssetWriterInputPixelBufferAdaptor. Все работает, и я так счастлив. Код в этом сообщении имеет возможность быть включенным в опубликованное приложение с открытым исходным кодом; Если вы хотите получить подтверждение или атрибуцию в потенциальной опубликованной статье, напишите мне на [email protected], и мы сможем договориться о деталях. В любом случае, вы будете подтверждены в исходном коде (по настоящему имени, если хотите, напишите мне.) – bennyty

+1

@bennyty Спасибо! Нет необходимости в атрибуции или что-то еще, было интересно делать;) – Kametrixom

+0

@ Kametrixom это потрясающе. Спасибо за публикацию. Вы используете метод AVAssetWriterInput.appendSampleBuffer: (CMSampleBuffer) после копирования CVPixelBuffer? Я пытаюсь превратить CVPixelBuffer обратно в CMSampleBuffer, чтобы использовать средство записи активов, но продолжаю получать недопустимый параметр, не удовлетворяющий: sampleBuffer! = ((Void *) 0) '". Конечной целью является тот же динамический временной шкал, который делает яблоко, где захваченные fps зависят от длины видеороликов с временной шкалой. – Dirk

4

Это решение Swift 3, отвечающее самым высоким требованиям.

extension CVPixelBuffer { 
func copy() -> CVPixelBuffer { 
    precondition(CFGetTypeID(self) == CVPixelBufferGetTypeID(), "copy() cannot be called on a non-CVPixelBuffer") 

    var _copy : CVPixelBuffer? 
    CVPixelBufferCreate(
     kCFAllocatorDefault, 
     CVPixelBufferGetWidth(self), 
     CVPixelBufferGetHeight(self), 
     CVPixelBufferGetPixelFormatType(self), 
     nil, 
     &_copy) 

    guard let copy = _copy else { fatalError() } 

    CVPixelBufferLockBaseAddress(self, CVPixelBufferLockFlags.readOnly) 
    CVPixelBufferLockBaseAddress(copy, CVPixelBufferLockFlags(rawValue: 0)) 


    let copyBaseAddress = CVPixelBufferGetBaseAddress(copy) 
    let currBaseAddress = CVPixelBufferGetBaseAddress(self) 

    memcpy(copyBaseAddress, currBaseAddress, CVPixelBufferGetDataSize(self)) 

    CVPixelBufferUnlockBaseAddress(copy, CVPixelBufferLockFlags(rawValue: 0)) 
    CVPixelBufferUnlockBaseAddress(self, CVPixelBufferLockFlags.readOnly) 


    return copy 
} 
}