2016-07-12 15 views
14

Tôi đang cố gắng tạo bản sao của CMSampleBuffer như được trả về bởi captureOutput trong AVCaptureVideoDataOutputSampleBufferDelegate.Kéo dữ liệu từ CMSampleBuffer để tạo bản sao sâu

Vì CMSampleBuffers đến từ một vùng đệm (15) đệm, nếu tôi đính kèm một tham chiếu đến chúng, chúng không thể được thu hồi. Điều này làm cho tất cả các khung còn lại bị bỏ.

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.

Rõ ràng tôi phải sao chép CMSampleBuffer nhưng CMSampleBufferCreateCopy() sẽ chỉ tạo bản sao nông. Vì vậy, tôi kết luận rằng tôi phải sử dụng CMSampleBufferCreate(). Tôi đã điền vào số 12! các tham số mà hàm tạo cần nhưng chạy vào vấn đề mà CMSampleBuffers của tôi không chứa một blockBuffer (không hoàn toàn chắc chắn đó là gì nhưng nó có vẻ quan trọng).

Câu hỏi này đã được hỏi nhiều lần nhưng không được trả lời.

Deep Copy of CMImageBuffer or CVImageBufferCreate a copy of CMSampleBuffer in Swift 2.0

Một câu trả lời có thể là "Cuối cùng tôi đã tìm ra cách sử dụng điều này để tạo ra một bản sao sâu. Tất cả các phương pháp bản sao tái sử dụng các dữ liệu trong đống mà giữ sẽ khóa AVCaptureSession. Vì vậy, tôi đã để kéo dữ liệu ra thành một đối tượng NSMutableData và sau đó tạo một bộ đệm mẫu mới. " credit to Rob on SO. Tuy nhiên, tôi không biết làm thế nào để làm điều này một cách chính xác.

Nếu bạn quan tâm, this là đầu ra của print(sampleBuffer). Không có đề cập đến blockBuffer, hay còn gọi là CMSampleBufferGetDataBuffer trả về nil. Có một imageBuffer, nhưng việc tạo ra một "bản sao" bằng cách sử dụng CMSampleBufferCreateForImageBuffer dường như không giải phóng CMSampleBuffer.


EDIT: Vì câu hỏi này đã được đăng, tôi đã thử nhiều cách hơn để sao chép bộ nhớ.

Tôi đã làm điều tương tự mà người dùng Kametrixom đã thử. This là nỗ lực của tôi theo cùng một ý tưởng, trước tiên hãy sao chép CVPixelBuffer, sau đó sử dụng CMSampleBufferCreateForImageBuffer để tạo bộ đệm mẫu cuối cùng. Tuy nhiên, điều này dẫn đến một trong hai lỗi sau:

  • EXC_BAD_ACCESS trên hướng dẫn memcpy. AKA là một segfault từ cố gắng truy cập bên ngoài bộ nhớ của ứng dụng.
  • Hoặc, bộ nhớ sẽ sao chép thành công nhưng CMSampleBufferCreateReadyWithImageBuffer() sẽ không thành công với mã kết quả -12743 "Cho biết định dạng của phương tiện đã cho không khớp với mô tả định dạng đã cho. Ví dụ: mô tả định dạng được ghép với CVImageBuffer không thành công CMVideoFormatDescriptionMatchesImageBuffer. "

Bạn có thể thấy rằng cả Kametrixom và tôi đã sử dụng CMSampleBufferGetFormatDescription(sampleBuffer) để cố gắng sao chép mô tả định dạng của bộ đệm nguồn. Do đó, tôi không chắc tại sao định dạng của phương tiện đã cho không khớp với mô tả định dạng đã cho.

+1

tôi rời [a comment] (http://stackoverflow.com/questions/35467847/create-a-copy-of-cmsamplebuffer-in-swift-2-0#comment64085484_35469364) thay cho bạn. –

+0

@JoshCaswell Bạn là một quý ông và là một học giả. – bennyty

+0

@bennyty Bạn sẽ sao chép sâu mẫu âm thanh như thế nào? –

Trả lời

8

Được rồi, tôi nghĩ cuối cùng tôi đã nhận được nó.Tôi tạo ra một phần mở rộng helper để tạo một bản sao đầy đủ của một 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 
    } 
} 

Bây giờ bạn có thể sử dụng trong didOutputSampleBuffer phương pháp của bạn:

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

let copy = pixelBuffer.copy() 

toProcess.append(copy) 

Nhưng hãy cẩn thận, một pixelBuffer như vậy chiếm khoảng 3MB bộ nhớ (1080p), có nghĩa là trong 100 khung hình bạn đã có khoảng 300MB, đó là về điểm mà tại đó iPhone nói STAHP (và treo).

Lưu ý rằng bạn không thực sự muốn sao chép CMSampleBuffer vì nó chỉ thực sự chứa CVPixelBuffer vì đó là hình ảnh.

+0

Mate. Bạn thật tuyệt. Tôi đã sử dụng tiện ích của bạn và đã thêm AVAssetWriterInputPixelBufferAdaptor. Mọi thứ đều hoạt động và tôi rất hạnh phúc. Mã trong bài đăng này có khả năng được bao gồm trong một ứng dụng nguồn mở được xuất bản; Nếu bạn muốn có một sự thừa nhận hoặc ghi công trong một bài báo được công bố tiềm năng, hãy gửi email cho tôi theo địa chỉ [email protected] và chúng tôi có thể sắp xếp chi tiết. Dù bằng cách nào, bạn sẽ được thừa nhận trong mã nguồn (bằng tên thật nếu bạn muốn, gửi email cho tôi.) – bennyty

+1

@bennyty Cảm ơn bạn! Không cần phân bổ hay gì cả, thật thú vị để làm;) – Kametrixom

+0

@Kametrixom thật tuyệt vời. Cảm ơn vì đăng. Bạn có đang sử dụng phương thức AVAssetWriterInput.appendSampleBuffer: (CMSampleBuffer) sau khi bạn sao chép CVPixelBuffer không? Tôi đã cố gắng để biến các CVPixelBuffer trở lại vào một CMSampleBuffer để sử dụng các nhà văn tài sản nhưng tiếp tục nhận được "tham số không hợp lệ không thỏa mãn: sampleBuffer! = ((Void *) 0) '". Mục tiêu cuối cùng là cùng một loại timelapse động mà apple thực hiện khi fps được chụp phụ thuộc vào độ dài của video timelapse. – Dirk

4

Đây là giải pháp Swift 3 cho câu trả lời được đánh giá hàng đầu.

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 
} 
} 
Các vấn đề liên quan