iPhone SDK 4 AVFoundation - Как правильно использовать captureStillImageAsynchronouslyFromConnection? - PullRequest
21 голосов
/ 02 октября 2010

Я пытаюсь использовать новый 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];

}

Ответы [ 5 ]

62 голосов
/ 14 апреля 2011

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

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

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

http://red -glasses.com / index.php / учебники / ios4-тейк-фото-с-жить-видео-превью-используя-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;
     }];
}
16 голосов
/ 03 октября 2010

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

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

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

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

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

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

6 голосов
/ 14 ноября 2011

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

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

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

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

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

3 голосов
/ 04 октября 2010
0 голосов
/ 11 февраля 2016

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

  1. Убедитесь, что вы import ImageIO
  2. Добавить объект private var stillImageOutput: AVCaptureStillImageOutput!
  3. Создание экземпляра 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, и вам просто нужно сделать из нее стоп-кадр, как и я.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...