2016-07-12 22 views
14

Sto tentando di creare una copia di un CMSampleBuffer come restituito da captureOutput in un AVCaptureVideoDataOutputSampleBufferDelegate.Estrazione di dati da un CMSampleBuffer per creare una copia profonda

Poiché i CMSampleBuffer provengono da un pool preallocato di (15) buffer, se allego un riferimento ad essi non possono essere raccolti. Ciò causa la caduta di tutti i fotogrammi rimanenti.

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.

Ovviamente devo copiare il CMSampleBuffer ma CMSampleBufferCreateCopy() creerà solo una copia. Quindi concludo che devo usare CMSampleBufferCreate(). Ho compilato il 12! parametri che il costruttore ha bisogno ma che ha incontrato il problema che i miei CMSampleBuffers non contengono un blockBuffer (non sono sicuro di cosa sia ma sembra importante).

Questa domanda è stata posta più volte ma non ha risposto.

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

Una risposta possibile è "ho finalmente capito come usare questo per creare un clone profonda. Tutti i metodi di copia riutilizzati i dati nel mucchio che ha mantenuto sarebbe bloccare l'AVCaptureSession. Così ho dovuto per estrarre i dati in un oggetto NSMutableData e quindi creare un nuovo buffer di esempio. " credit to Rob on SO. Tuttavia, non so come farlo correttamente.

Se si è interessati, this è l'output di print(sampleBuffer). Non si parla di blockBuffer, alias CMSampleBufferGetDataBuffer restituisce nil. C'è un imageBuffer, ma la creazione di una "copia" utilizzando CMSampleBufferCreateForImageBuffer non sembra liberare neanche il CMSampleBuffer.


EDIT: Poiché questa domanda è stata postata, ho provato ancora più modi di copiare la memoria.

Ho fatto la stessa cosa che ha provato l'utente Kametrixom. This è il mio tentativo di ideare lo stesso, per prima cosa copiare CVPixelBuffer quindi utilizzare CMSampleBufferCreateForImageBuffer per creare il buffer di esempio finale. Tuttavia, ciò si verifica in uno dei due errori:

  • A EXC_BAD_ACCESS sull'istruzione memcpy. AKA un segfault dal tentativo di accedere al di fuori della memoria dell'applicazione.
  • Oppure, la memoria verrà copiata correttamente ma il codice CMSampleBufferCreateReadyWithImageBuffer() non riuscirà con il codice risultato -12743 che "Indica che il formato del supporto specificato non corrisponde alla descrizione del formato specificato. Ad esempio, una descrizione del formato associata a un CVImageBuffer che non riesce CMVideoFormatDescriptionMatchesImageBuffer."

Si può vedere che sia Kametrixom sia io abbiamo utilizzato CMSampleBufferGetFormatDescription(sampleBuffer) per provare a copiare la descrizione del formato del buffer di origine. Pertanto, non sono sicuro del motivo per cui il formato del supporto specificato non corrisponde alla descrizione del formato data.

+1

ho lasciato [un commento] (http://stackoverflow.com/questions/35467847/create-a-copy-of-cmsamplebuffer-in-swift-2-0#comment64085484_35469364) a vostro nome. –

+0

@JoshCaswell Sei un gentiluomo e uno studioso. – bennyty

+0

@bennyty In che modo copierai l'audio campione in profondità? –

risposta

8

Ok, penso di averlo finalmente capito.Ho creato un estensione di aiuto per fare una copia completa di un 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 
    } 
} 

ora è possibile utilizzare questo nel vostro didOutputSampleBuffer metodo:

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

let copy = pixelBuffer.copy() 

toProcess.append(copy) 

ma essere consapevoli, uno di questi pixelBuffer occupa circa 3 MB di memoria (1080p), il che significa che in 100 frame hai già circa 300 MB, che è circa il punto in cui l'iPhone dice STAHP (e si blocca).

Si noti che in realtà non si desidera copiare il CMSampleBuffer in quanto contiene solo un vero CVPixelBuffer perché è un'immagine.

+0

Mate. Sei fantastico. Ho usato la tua estensione e ho aggiunto un AVAssetWriterInputPixelBufferAdaptor. Tutto funziona e sono così felice. Il codice in questo post ha la possibilità di essere incluso in un'applicazione open source pubblicata; Se si desidera un riconoscimento o un'attribuzione in un potenziale documento pubblicato, scrivetemi all'indirizzo [email protected] e possiamo organizzare i dettagli. In ogni caso, verrai riconosciuto nel codice sorgente (per nome reale, se lo desideri, mandami una email.) – bennyty

+1

@bennyty Grazie! Non c'è bisogno di attribuzione o altro, è stato divertente da fare;) – Kametrixom

+0

@Kametrixom questo è fantastico. Grazie per la pubblicazione. Stai utilizzando il metodo AVAssetWriterInput.appendSampleBuffer: (CMSampleBuffer) dopo aver copiato CVPixelBuffer? Ho cercato di trasformare il CVPixelBuffer in un CMSampleBuffer per utilizzare il programma di scrittura degli asset, ma continuo a ricevere "parametri non validi non soddisfacenti: sampleBuffer! = ((Void *) 0) '". L'obiettivo finale è lo stesso tipo di timelapse dinamico che Apple fa dove l'FPS catturato dipende dalla lunghezza del video timelapse. – Dirk

4

Questa è la soluzione Swift 3 per la risposta più votata.

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 
} 
}