IPhone SDK 4 AVFoundation - Как правильно использовать captureStillImageAsynchronouslyFromConnection?

Я пытаюсь использовать новый AVFoundation framework для снятия неподвижных изображений с iPhone.

При нажатии кнопки этот метод вызывается. Я слышу звук затвора, но я не вижу выход журнала. Если я вызову этот метод несколько раз, просмотр камеры заморозится.

Есть ли там учебник, как использовать captureStillImageAsynchronouslyFromConnection?

[[self stillImageOutput] captureStillImageAsynchronouslyFromConnection:
                [[self stillImageOutput].connections objectAtIndex:0]
                     completionHandler:^(CMSampleBufferRef imageDataSampleBuffer,
                            NSError *error) {
                                              NSLog(@"inside");
                            }];
- (void)initCapture {
    AVCaptureDeviceInput *captureInput = [AVCaptureDeviceInput 
                                          deviceInputWithDevice:[AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo] 
                                          error:nil];

    AVCaptureVideoDataOutput *captureOutput = [[AVCaptureVideoDataOutput alloc] init];

    captureOutput.alwaysDiscardsLateVideoFrames = YES; 

    dispatch_queue_t queue;
    queue = dispatch_queue_create("cameraQueue", NULL);
    [captureOutput setSampleBufferDelegate:self queue:queue];
    dispatch_release(queue);

    NSString* key = (NSString*)kCVPixelBufferPixelFormatTypeKey; 
    NSNumber* value = [NSNumber numberWithUnsignedInt:kCVPixelFormatType_32BGRA]; 
    NSDictionary* videoSettings = [NSDictionary dictionaryWithObject:value forKey:key]; 
    [captureOutput setVideoSettings:videoSettings]; 

    self.captureSession = [[AVCaptureSession alloc] init];
    self.captureSession.sessionPreset = AVCaptureSessionPresetLow;

    [self.captureSession addInput:captureInput];
    [self.captureSession addOutput:captureOutput];

    self.prevLayer = [AVCaptureVideoPreviewLayer layerWithSession: self.captureSession];

    [self.prevLayer setOrientation:AVCaptureVideoOrientationLandscapeLeft];

    self.prevLayer.frame = CGRectMake(0.0, 0.0, 480.0, 320.0);
    self.prevLayer.videoGravity = AVLayerVideoGravityResizeAspectFill;

    [self.view.layer addSublayer: self.prevLayer];


    // Setup the default file outputs
    AVCaptureStillImageOutput *_stillImageOutput = [[[AVCaptureStillImageOutput alloc] init] autorelease];
    NSDictionary *outputSettings = [[NSDictionary alloc] initWithObjectsAndKeys:
                                    AVVideoCodecJPEG, AVVideoCodecKey,
                                    nil];
    [_stillImageOutput setOutputSettings:outputSettings];
    [outputSettings release];
    [self setStillImageOutput:_stillImageOutput];   

    if ([self.captureSession canAddOutput:stillImageOutput]) {
        [self.captureSession addOutput:stillImageOutput];
    }

    [self.captureSession commitConfiguration];
    [self.captureSession startRunning];

}

Ответ 1

У нас была эта проблема, когда 4.0 все еще находилась в бета-версии. Я попробовал честную кучу вещей. Здесь:

  • AVCaptureStillImageOutput и AVCaptureVideoDataOutput не кажутся хорошо играющими друг с другом. Если выход видеосигнала запущен, выход изображения никогда не будет завершен (пока вы не приостановите сеанс, поставив телефон в режим сна, а затем вы получите одно изображение).
  • AVCaptureStillImageOutput работает только с AVCaptureSessionPresetPhoto; в противном случае вы эффективно получаете видеофрагменты, закодированные в формате JPEG. Возможно также использовать более качественные рамки BGRA (кстати, выход на выходе камеры выглядит как BGRA, он не имеет цветовой подвыборки 2vuy/420v).
  • Видео (все, что не является фото) и предустановками фотографий кажется принципиально иным; вы никогда не получаете видеокадры, если сеанс находится в режиме фото (вы также не получаете ошибку). Возможно, они изменили это...
  • Кажется, у вас нет двух сеансов захвата (один с предустановленным видео и видеовыходом, один с предустановкой фото и выходом изображения). Возможно, они исправили это.
  • Вы можете остановить сеанс, изменить пресет на фотографию, начать сеанс, сделать снимок, а когда фото завершено, остановить, изменить предустановку назад и начать заново. Это занимает некоторое время, и уровень предварительного просмотра видеороликов выглядит ужасно (он корректирует уровни экспозиции). Это также иногда затормозило в бета-версии (после вызова -stopRunning, session.running по-прежнему был ДА).
  • Возможно, вы сможете отключить AVCaptureConnection (он должен работать). Я помню эту тупик; они, возможно, исправили это.

В итоге я просто захватил видеокадры. Кнопка "сфотографировать" просто устанавливает флаг; в обратном вызове видеофрагмента, если флаг установлен, он возвращает видеокадр вместо UIImage *. Этого было достаточно для наших потребностей в обработке изображений; "делать снимок" существует во многом таким образом, что пользователь может получить отрицательный ответ (и возможность представить отчет об ошибке); мы фактически не хотим 2/3/5 мегапиксельных изображений, так как они берут возраст для обработки.

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

Вероятно, стоит записать ошибку. Я подал ошибку вокруг запуска 4.0 GM; Apple спросила меня о некотором примерном коде, но к тому времени я решил использовать обходное решение для видеокадра и выпустил релиз.

Кроме того, "низкий" пресет имеет очень низкое разрешение (и обеспечивает предварительный просмотр видео с низким разрешением, с низким разрешением). Я поеду на 640x480, если таковые имеются, отступив на Medium, если нет.

Ответ 2

После большого количества проб и ошибок я разработал, как это сделать.

Подсказка: официальные документы Apple - просто - неправильные. Код, который они вам дают, на самом деле не работает.

Я написал его здесь с пошаговыми инструкциями:

http://red-glasses.com/index.php/tutorials/ios4-take-photos-with-live-video-preview-using-avfoundation/

Множество кода в ссылке, но в целом:

-(void) viewDidAppear:(BOOL)animated
{
    AVCaptureSession *session = [[AVCaptureSession alloc] init];
    session.sessionPreset = AVCaptureSessionPresetMedium;

    CALayer *viewLayer = self.vImagePreview.layer;
    NSLog(@"viewLayer = %@", viewLayer);

    AVCaptureVideoPreviewLayer *captureVideoPreviewLayer = [[AVCaptureVideoPreviewLayer alloc] initWithSession:session];

    captureVideoPreviewLayer.frame = self.vImagePreview.bounds;
    [self.vImagePreview.layer addSublayer:captureVideoPreviewLayer];

    AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];

    NSError *error = nil;
    AVCaptureDeviceInput *input = [AVCaptureDeviceInput deviceInputWithDevice:device error:&error];
    if (!input) {
        // Handle the error appropriately.
        NSLog(@"ERROR: trying to open camera: %@", error);
    }
    [session addInput:input];

stillImageOutput = [[AVCaptureStillImageOutput alloc] init];
NSDictionary *outputSettings = [[NSDictionary alloc] initWithObjectsAndKeys: AVVideoCodecJPEG, AVVideoCodecKey, nil];
[stillImageOutput setOutputSettings:outputSettings];

[session addOutput:stillImageOutput];

    [session startRunning];
}

-(IBAction) captureNow
{
    AVCaptureConnection *videoConnection = nil;
    for (AVCaptureConnection *connection in stillImageOutput.connections)
    {
        for (AVCaptureInputPort *port in [connection inputPorts])
        {
            if ([[port mediaType] isEqual:AVMediaTypeVideo] )
            {
                videoConnection = connection;
                break;
            }
        }
        if (videoConnection) { break; }
    }

    NSLog(@"about to request a capture from: %@", stillImageOutput);
    [stillImageOutput captureStillImageAsynchronouslyFromConnection:videoConnection completionHandler: ^(CMSampleBufferRef imageSampleBuffer, NSError *error)
    {
         CFDictionaryRef exifAttachments = CMGetAttachment( imageSampleBuffer, kCGImagePropertyExifDictionary, NULL);
         if (exifAttachments)
         {
            // Do something with the attachments.
            NSLog(@"attachements: %@", exifAttachments);
         }
        else
            NSLog(@"no attachments");

        NSData *imageData = [AVCaptureStillImageOutput jpegStillImageNSDataRepresentation:imageSampleBuffer];
        UIImage *image = [[UIImage alloc] initWithData:imageData];

        self.vImage.image = image;
     }];
}

Ответ 3

Это была огромная помощь - я застрял в сорняках довольно долго, пытаясь следовать примеру AVCam.

Вот полный рабочий проект с моими комментариями, которые объясняют, что происходит. Это иллюстрирует, как вы можете использовать менеджер захвата с несколькими выходами. В этом примере есть два выхода.

Первый - это вывод неподвижного изображения в примере выше.

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

https://github.com/tdsltm/iphoneStubs/tree/master/VideoCamCaptureExample-RedGlassesBlog/VideoCamCaptureExample-RedGlassesBlog

Ответ 5

Вы должны использовать ответ Адам, но если вы используете Swift (как и большинство из вас, вероятно, сейчас), здесь находится Swift 1.2 порт его кода:

  • Убедитесь, что вы import ImageIO
  • Добавить свойство private var stillImageOutput: AVCaptureStillImageOutput!
  • Выполнить stillImageOutput перед captureSession.startRunning():

Вот так:

stillImageOutput = AVCaptureStillImageOutput()
stillImageOutput.outputSettings = [AVVideoCodecKey: AVVideoCodecJPEG]
captureSession.addOutput(stillImageOutput)

Затем используйте этот код для захвата изображения:

private func captureImage() {
    var videoConnection: AVCaptureConnection?
    for connection in stillImageOutput.connections as! [AVCaptureConnection] {
        for port in connection.inputPorts {
            if port.mediaType == AVMediaTypeVideo {
                videoConnection = connection
                break
            }
        }
        if videoConnection != nil {
            break
        }
    }
    print("about to request a capture from: \(stillImageOutput)")
    stillImageOutput.captureStillImageAsynchronouslyFromConnection(videoConnection) { (imageSampleBuffer: CMSampleBuffer!, error: NSError!) -> Void in
        let exifAttachments = CMGetAttachment(imageSampleBuffer, kCGImagePropertyExifDictionary, nil)
        if let attachments = exifAttachments {
            // Do something with the attachments
            print("attachments: \(attachments)")
        } else {
            print("no attachments")
        }
        let imageData = AVCaptureStillImageOutput.jpegStillImageNSDataRepresentation(imageSampleBuffer)
        let image = UIImage(data: imageData)
        // Do something with the image
    }
}

Все это предполагает, что у вас уже есть настройка AVCaptureSession, и вам просто нужно взять с нее все еще, как и я.