AVCapture appendSampleBuffer - PullRequest
       61

AVCapture appendSampleBuffer

7 голосов
/ 02 октября 2010

Я схожу с ума от этого - посмотрел повсюду и попробовал все, что только могу придумать.

Я создаю приложение для iPhone, которое использует AVFoundation - в частности, AVCapture для захвата видео с помощью камеры iPhone.

Мне нужно, чтобы пользовательское изображение накладывалось на видеопоток, включенный в запись.

Пока у меня настроен сеанс AVCapture, я могу отображать канал, получать доступ к кадру, сохранять его как UIImage и помечать наложенное изображение на него. Затем преобразуйте этот новый UIImage в CVPixelBufferRef. Чтобы проверить, работает ли bufferRef, я преобразовал его обратно в UIImage, и он все равно отображает изображение.

Проблема начинается, когда я пытаюсь преобразовать CVPixelBufferRef в CMSampleBufferRef для добавления к AVCaptureSessions assetWriterInput. CMSampleBufferRef всегда возвращает NULL, когда я пытаюсь его создать.

Вот функция - (void) captureOutput

- (void)captureOutput:(AVCaptureOutput *)captureOutput 
didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer 
    fromConnection:(AVCaptureConnection *)connection
{ 

 UIImage *botImage = [self imageFromSampleBuffer:sampleBuffer];
 UIImage *wheel = [self imageFromView:wheelView];

 UIImage *finalImage = [self overlaidImage:botImage :wheel];
 //[previewImage setImage:finalImage]; <- works -- the image is being merged into one UIImage

 CVPixelBufferRef pixelBuffer = NULL;
 CGImageRef cgImage = CGImageCreateCopy(finalImage.CGImage);
 CFDataRef image = CGDataProviderCopyData(CGImageGetDataProvider(cgImage));
 int status = CVPixelBufferCreateWithBytes(NULL,
             self.view.bounds.size.width,
             self.view.bounds.size.height,
             kCVPixelFormatType_32BGRA, 
             (void*)CFDataGetBytePtr(image), 
             CGImageGetBytesPerRow(cgImage), 
             NULL, 
             0,
             NULL, 
             &pixelBuffer);
 if(status == 0){
  OSStatus result = 0;

  CMVideoFormatDescriptionRef videoInfo = NULL;
  result = CMVideoFormatDescriptionCreateForImageBuffer(NULL, pixelBuffer, &videoInfo);
  NSParameterAssert(result == 0 && videoInfo != NULL);

  CMSampleBufferRef myBuffer = NULL;
  result = CMSampleBufferCreateForImageBuffer(kCFAllocatorDefault,
            pixelBuffer, true, NULL, NULL, videoInfo, NULL, &myBuffer);
  NSParameterAssert(result == 0 && myBuffer != NULL);//always null :S

  NSLog(@"Trying to append");

  if (!CMSampleBufferDataIsReady(myBuffer)){
   NSLog(@"sampleBuffer data is not ready");
   return;
  }

  if (![assetWriterInput isReadyForMoreMediaData]){
   NSLog(@"Not ready for data :(");
   return;
  }

  if (![assetWriterInput appendSampleBuffer:myBuffer]){   
   NSLog(@"Failed to append pixel buffer");
  }



 }

}

Другое решение, о котором я продолжаю слышать, - это использование AVAssetWriterInputPixelBufferAdaptor, которое устраняет необходимость в беспорядочной упаковке CMSampleBufferRef. Тем не менее, я просмотрел форумы и документы разработчиков и не могу найти четкое описание или пример того, как это настроить или как его использовать. Если у кого-то есть работающий пример этого, не могли бы вы показать мне или помочь мне решить вышеупомянутую проблему - работали над этим безостановочно в течение недели, и я в конце концов.

Дайте мне знать, если вам нужна какая-либо другая информация

Заранее спасибо,

Michael

Ответы [ 2 ]

6 голосов
/ 12 мая 2011

Вам нужен AVAssetWriterInputPixelBufferAdaptor, вот код для его создания:

    // Create dictionary for pixel buffer adaptor
NSDictionary *bufferAttributes = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithInt:kCVPixelFormatType_32BGRA], kCVPixelBufferPixelFormatTypeKey, nil];

// Create pixel buffer adaptor
m_pixelsBufferAdaptor = [[AVAssetWriterInputPixelBufferAdaptor alloc] initWithAssetWriterInput:assetWriterInput sourcePixelBufferAttributes:bufferAttributes];

И код для его использования:

// If ready to have more media data
if (m_pixelsBufferAdaptor.assetWriterInput.readyForMoreMediaData) {
    // Create a pixel buffer
    CVPixelBufferRef pixelsBuffer = NULL;
    CVPixelBufferPoolCreatePixelBuffer(NULL, m_pixelsBufferAdaptor.pixelBufferPool, &pixelsBuffer);

    // Lock pixel buffer address
    CVPixelBufferLockBaseAddress(pixelsBuffer, 0);

    // Create your function to set your pixels data in the buffer (in your case, fill with your finalImage data)
    [self yourFunctionToPutDataInPixelBuffer:CVPixelBufferGetBaseAddress(pixelsBuffer)];

    // Unlock pixel buffer address
    CVPixelBufferUnlockBaseAddress(pixelsBuffer, 0);

    // Append pixel buffer (calculate currentFrameTime with your needing, the most simplest way is to have a frame time starting at 0 and increment each time you write a frame with the time of a frame (inverse of your framerate))
    [m_pixelsBufferAdaptor appendPixelBuffer:pixelsBuffer withPresentationTime:currentFrameTime];

    // Release pixel buffer
    CVPixelBufferRelease(pixelsBuffer);
}

И не забудьте освободить свой пиксельBufferAdaptor.

1 голос
/ 19 апреля 2014

Я делаю это с помощью CMSampleBufferCreateForImageBuffer ().

OSStatus ret = 0;
CMSampleBufferRef sample = NULL;
CMVideoFormatDescriptionRef videoInfo = NULL;
CMSampleTimingInfo timingInfo = kCMTimingInfoInvalid;
timingInfo.presentationTimeStamp = pts;
timingInfo.duration = duration;

ret = CMVideoFormatDescriptionCreateForImageBuffer(NULL, pixel, &videoInfo);
if (ret != 0) {
    NSLog(@"CMVideoFormatDescriptionCreateForImageBuffer failed! %d", (int)ret);
    goto done;
}
ret = CMSampleBufferCreateForImageBuffer(kCFAllocatorDefault, pixel, true, NULL, NULL,
                                         videoInfo, &timingInfo, &sample);
if (ret != 0) {
    NSLog(@"CMSampleBufferCreateForImageBuffer failed! %d", (int)ret);
    goto done;
}
...