2016-07-12 17 views
14

Próbuję utworzyć kopię obiektu CMSampleBuffer jako zwróconą przez metodę captureOutput w AVCaptureVideoDataOutputSampleBufferDelegate.Ciągnięcie danych z CMSampleBuffer w celu utworzenia głębokiej kopii

Ponieważ CMSampleBuffers pochodzą z wcześniej przydzielonej puli (15) buforów, jeśli dołączę do nich odniesienie, nie będą mogły być ponownie zarchiwizowane. Powoduje to usunięcie wszystkich pozostałych klatek.

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.

Oczywiście muszę skopiować CMSampleBuffer ale CMSampleBufferCreateCopy() spowoduje jedynie płytką kopię. W związku z tym wnioskuję, że muszę użyć CMSampleBufferCreate(). Wypełniłem 12! parametry, które konstruktor potrzebuje, ale wystąpił problem, że moje CMSampleBuffers nie zawierają blokuBuffer (nie do końca pewny, co to jest, ale wydaje się to ważne).

To pytanie zadano kilka razy, ale wciąż nie otrzymano odpowiedzi.

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

Jedną z możliwych odpowiedzi brzmi „I w końcu zorientowali się, jak to wykorzystać, aby stworzyć głęboki klona. Wszystkie metody kopiowania ponownego wykorzystania danych w sterty, które przechowywane byłoby zablokować AVCaptureSession. Więc musiałem aby wyciągnąć dane do obiektu NSMutableData, a następnie utworzyć nowy bufor próbek. " credit to Rob on SO. Jednak nie wiem, jak to zrobić poprawnie.

Jeśli jesteś zainteresowany, this jest wyjście z print(sampleBuffer). Nie ma wzmianki o blockBuffer, inaczej CMSampleBufferGetDataBuffer zwraca zero. Istnieje obrazBuffer, ale tworzenie "kopii" za pomocą CMSampleBufferCreateForImageBuffer nie wydaje się również zwolnić CMSampleBuffer.


EDYCJA: Od czasu opublikowania tego pytania próbowałem jeszcze więcej sposobów na skopiowanie pamięci.

Zrobiłem to samo, co użytkownik, który próbował Kametrixom. This jest moją próbą tego samego pomysłu, najpierw skopiować CVPixelBuffer, a następnie użyć CMSampleBufferCreateForImageBuffer, aby utworzyć końcowy bufor próbek. Jednak powoduje to jeden z dwóch błędów:

  • EXC_BAD_ACCESS instrukcji memcpy. AKA a segfault od próby uzyskania dostępu poza pamięcią aplikacji.
  • Albo pamięć zostanie pomyślnie skopiowana, ale kod CMSampleBufferCreateReadyWithImageBuffer() zakończy się niepowodzeniem z kodem wyniku -12743, który "Wskazuje, że format podanego nośnika nie pasuje do podanego opisu formatu.Na przykład, opis formatu jest sparowany z błędem CVImageBuffer, który nie działa CMVideoFormatDescriptionMatchesImageBuffer. "

Możesz zobaczyć, że zarówno Kametrixom, jak i ja użyliśmy CMSampleBufferGetFormatDescription(sampleBuffer), aby spróbować skopiować opis formatu bufora źródłowego. Dlatego nie jestem pewien, dlaczego format danego nośnika nie pasuje do podanego opisu formatu.

+1

wyszedłem [komentarz] (http://stackoverflow.com/questions/35467847/create-a-copy-of-cmsamplebuffer-in-swift-2-0#comment64085484_35469364) w Twoim imieniu. –

+0

@JoshCaswell Jesteś dżentelmenem i uczonym. – bennyty

+0

@bennyty Jak głęboko skopiować próbkę audio? –

Odpowiedz

8

Dobra, myślę, że w końcu to dostałem.I stworzył rozszerzenie pomocnika dokonać pełnej kopii 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 
    } 
} 

Teraz można korzystać z tego w sposób didOutputSampleBuffer:

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

let copy = pixelBuffer.copy() 

toProcess.append(copy) 

ale należy pamiętać, jeden taki pixelBuffer zajmuje około 3MB pamięć (1080p), co oznacza, że ​​w 100 klatkach masz już około 300 MB, czyli tyle, ile iPhone mówi STAHP (i awarie).

Należy zauważyć, że w rzeczywistości nie ma potrzeby kopiowania pliku CMSampleBuffer, ponieważ zawiera on tylko obraz CVPixelBuffer, ponieważ jest to obraz.

+0

Mate. Jesteś niesamowity. Użyłem twojego rozszerzenia i dodałem AVAssetWriterInputPixelBufferAdaptor. Wszystko działa i jestem bardzo szczęśliwy. Kod w tym poście ma możliwość włączenia do opublikowanej aplikacji open source; Jeśli chcesz potwierdzić lub przypisać do potencjalnego opublikowanego artykułu, napisz do mnie na adres [email protected], a my możemy ustalić szczegóły. Tak czy inaczej, zostaniesz potwierdzony w kodzie źródłowym (prawdziwym imieniem, jeśli chcesz, napisz do mnie). – bennyty

+1

@bennyty Thank you! Nie trzeba przypisywać ani nic, było fajnie robić;) – Kametrixom

+0

@Kametrixom to jest niesamowite. Dzięki za publikację. Czy używasz metody AVAssetWriterInput.appendSampleBuffer: (CMSampleBuffer) po skopiowaniu CVPixelBuffer? Próbowałem przekształcić CVPixelBuffer z powrotem w CMSampleBuffer, aby użyć pisarza zasobów, ale nadal otrzymuję "niepoprawny parametr nieprawidłowy: sampleBuffer! = ((Void *) 0)" ". Ostatecznym celem jest taki sam dynamiczny timelapse, jaki robi jabłko, gdzie przechwycone klatki zależą od długości filmu poklatkowego. – Dirk

4

To jest rozwiązanie Swift 3 do najlepiej ocenianej odpowiedzi.

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 
} 
} 
Powiązane problemy