AVCaptureSession получает только один кадр для iPhone 3gs - PullRequest
6 голосов
/ 30 августа 2010

У меня есть фрагмент кода, который устанавливает сеанс захвата с камеры для обработки кадров с использованием OpenCV, а затем задает свойство изображения UIImageView с созданным UIImage из кадра.Когда приложение запускается, изображение представления изображения равно нулю, и никакие кадры не отображаются, пока я не вставлю другой контроллер представления в стек и не вытолкну его.Затем изображение остается прежним, пока я не сделаю это снова.Операторы NSLog показывают, что обратный вызов вызывается приблизительно с правильной частотой кадров.Есть идеи, почему он не появляется?Я уменьшил частоту кадров до 2 кадров в секунду.Разве это не обрабатывает достаточно быстро?

Вот код:

- (void)setupCaptureSession {
    NSError *error = nil;

    // Create the session
    AVCaptureSession *session = [[AVCaptureSession alloc] init];

    // Configure the session to produce lower resolution video frames, if your 
    // processing algorithm can cope. We'll specify medium quality for the
    // chosen device.
    session.sessionPreset = AVCaptureSessionPresetLow;

    // Find a suitable AVCaptureDevice
    AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];

    // Create a device input with the device and add it to the session.
    AVCaptureDeviceInput *input = [AVCaptureDeviceInput deviceInputWithDevice:device 
                                                                        error:&error];
    if (!input) {
        // Handling the error appropriately.
    }
    [session addInput:input];

    // Create a VideoDataOutput and add it to the session
    AVCaptureVideoDataOutput *output = [[[AVCaptureVideoDataOutput alloc] init] autorelease];
    output.alwaysDiscardsLateVideoFrames = YES;
    [session addOutput:output];

    // Configure your output.
    dispatch_queue_t queue = dispatch_queue_create("myQueue", NULL);
    [output setSampleBufferDelegate:self queue:queue];
    dispatch_release(queue);

    // Specify the pixel format
    output.videoSettings = 
    [NSDictionary dictionaryWithObject:
     [NSNumber numberWithInt:kCVPixelFormatType_32BGRA] 
                                forKey:(id)kCVPixelBufferPixelFormatTypeKey];


    // If you wish to cap the frame rate to a known value, such as 15 fps, set 
    // minFrameDuration.
    output.minFrameDuration = CMTimeMake(1, 1);

    // Start the session running to start the flow of data
    [session startRunning];

    // Assign session to an ivar.
    [self setSession:session];
}

// Create a UIImage from sample buffer data
- (UIImage *) imageFromSampleBuffer:(CMSampleBufferRef) sampleBuffer {
    CVImageBufferRef imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer);
    // Lock the base address of the pixel buffer
    CVPixelBufferLockBaseAddress(imageBuffer,0);

    // Get the number of bytes per row for the pixel buffer
    size_t bytesPerRow = CVPixelBufferGetBytesPerRow(imageBuffer); 
    // Get the pixel buffer width and height
    size_t width = CVPixelBufferGetWidth(imageBuffer); 
    size_t height = CVPixelBufferGetHeight(imageBuffer); 

    // Create a device-dependent RGB color space
    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); 
    if (!colorSpace) 
     {
        NSLog(@"CGColorSpaceCreateDeviceRGB failure");
        return nil;
     }

    // Get the base address of the pixel buffer
    void *baseAddress = CVPixelBufferGetBaseAddress(imageBuffer);
    // Get the data size for contiguous planes of the pixel buffer.
    size_t bufferSize = CVPixelBufferGetDataSize(imageBuffer); 

    // Create a Quartz direct-access data provider that uses data we supply
    CGDataProviderRef provider = CGDataProviderCreateWithData(NULL, baseAddress, bufferSize, 
                                                              NULL);
    // Create a bitmap image from data supplied by our data provider
    CGImageRef cgImage = 
    CGImageCreate(width,
                  height,
                  8,
                  32,
                  bytesPerRow,
                  colorSpace,
                  kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Little,
                  provider,
                  NULL,
                  true,
                  kCGRenderingIntentDefault);
    CGDataProviderRelease(provider);
    CGColorSpaceRelease(colorSpace);

    // Create and return an image object representing the specified Quartz image
    UIImage *image = [UIImage imageWithCGImage:cgImage];
    CGImageRelease(cgImage);

    CVPixelBufferUnlockBaseAddress(imageBuffer, 0);

    return image;
}


// Delegate routine that is called when a sample buffer was written
- (void)captureOutput:(AVCaptureOutput *)captureOutput 
didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer 
       fromConnection:(AVCaptureConnection *)connection {
    // Create a UIImage from the sample buffer data
    UIImage *image = [self imageFromSampleBuffer:sampleBuffer];
    [self.delegate cameraCaptureGotFrame:image];
}

Ответы [ 3 ]

6 голосов
/ 30 августа 2010

Это может быть связано с многопоточностью. Попробуйте:

[self.delegate performSelectorOnMainThread:@selector(cameraCaptureGotFrame:) withObject:image waitUntilDone:NO];
3 голосов
/ 02 октября 2010

Это похоже на проблему с многопоточностью. Вы не можете обновить свои представления в любой другой теме, кроме основной. В вашей настройке, что хорошо, функция делегата captureOutput: didOutputSampleBuffer: вызывается во вторичном потоке. Таким образом, вы не можете установить вид изображения оттуда. Ответ Арта Гиллеспи - один из способов решения этой проблемы, если вы можете избавиться от ошибки неверного доступа.

Другим способом является изменение буфера семплов в captureOutput: didOutputSampleBuffer: , который отображается путем добавления экземпляра AVCaptureVideoPreviewLayer в сеанс захвата. Это, безусловно, предпочтительный способ, если вы изменяете только небольшую часть изображения, например выделяете что-то.

Кстати: ошибка неверного доступа может возникнуть из-за того, что вы не сохраняете созданное изображение во вторичном потоке, и поэтому оно будет освобождено до вызова cameraCaptureGotFrame в основном потоке.

Обновление : Чтобы правильно сохранить изображение, увеличьте счетчик ссылок в captureOutput: didOutputSampleBuffer: (во вторичном потоке) и уменьшите его в cameraCaptureGotFrame: (в главном потоке).

// Delegate routine that is called when a sample buffer was written
- (void)captureOutput:(AVCaptureOutput *)captureOutput 
        didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer 
        fromConnection:(AVCaptureConnection *)connection
{
    // Create a UIImage from the sample buffer data
    UIImage *image = [self imageFromSampleBuffer:sampleBuffer];

    // increment ref count
    [image retain];
    [self.delegate performSelectorOnMainThread:@selector(cameraCaptureGotFrame:)
        withObject:image waitUntilDone:NO];

}

- (void) cameraCaptureGotFrame:(UIImage*)image
{
    // whatever this function does, e.g.:
    imageView.image = image;

    // decrement ref count
    [image release];
}

Если вы не увеличиваете счетчик ссылок, изображение освобождается пулом автоматического освобождения второго потока перед вызовом cameraCaptureGotFrame: в основном потоке. Если вы не уменьшите его в главном потоке, изображения никогда не будут освобождены, и вам не хватит памяти в течение нескольких секунд.

0 голосов
/ 30 августа 2010

Вы выполняете setNeedsDisplay в UIImageView после каждого нового обновления свойства изображения?

Редактировать:

Где и когда вы обновляете свойство фонового изображения в представлении изображения?

...