21

我正在尝试使用新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]
                                          错误:无];

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

    captureOutput.alwaysDiscardsLateVideoFrames = YES;

    dispatch_queue_t 队列;
    queue = dispatch_queue_create("cameraQueue", NULL);
    [captureOutput setSampleBufferDelegate:self queue:queue];
    调度释放(队列);

    NSString* key = (NSString*)kCVPixelBufferPixelFormatTypeKey;
    NSNumber* 值 = [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];


    // 设置默认文件输出
    AVCaptureStillImageOutput *_stillImageOutput = [[[AVCaptureStillImageOutput alloc] init] autorelease];
    NSDictionary *outputSettings = [[NSDictionary alloc] initWithObjectsAndKeys:
                                    AVVideoCodecJPEG, AVVideoCodecKey,
                                    零];
    [_stillImageOutput setOutputSettings:outputSettings];
    [输出设置发布];
    [自我 setStillImageOutput:_stillImageOutput];   

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

    [self.captureSession 提交配置];
    [self.captureSession startRunning];

}
4

5 回答 5

62

经过大量的试验和错误,我想出了如何做到这一点。

提示: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;
     }];
}
于 2011-04-14T18:22:49.320 回答
16

当 4.0 仍处于测试阶段时,我们遇到了这个问题。我尝试了很多东西。开始:

  • AVCaptureStillImageOutput 和 AVCaptureVideoDataOutput 似乎不能很好地相互配合。如果视频输出正在运行,则图像输出似乎永远不会完成(直到您通过让手机进入睡眠状态来暂停会话;然后您似乎只输出了一个图像)。
  • AVCaptureStillImageOutput 似乎只适用于 AVCaptureSessionPresetPhoto;否则,您将有效地获得 JPEG 编码的视频帧。不妨使用更高质量的 BGRA 帧(顺便说一下,相机的原生输出似乎是 BGRA;它似乎没有 2vuy/420v 的颜色子采样)。
  • 视频(不是照片的所有内容)和照片预设似乎根本不同;如果会话处于照片模式,您将永远不会收到任何视频帧(您也不会收到错误消息)。也许他们改变了这个......
  • 您似乎没有两个捕获会话(一个带有视频预设和视频输出,一个带有照片预设和图像输出)。他们可能已经解决了这个问题。
  • 您可以停止会话,将预设更改为照片,开始会话,拍照,当照片完成时,停止,将预设更改回来,然后重新开始。这需要一段时间,视频预览层会停止并且看起来很糟糕(它会重新调整曝光级别)。这也偶尔会在 beta 中陷入僵局(在调用 -stopRunning 之后,session.running 仍然是 YES)。
  • 您也许可以禁用 AVCaptureConnection (它应该可以工作)。我记得这个僵局;他们可能已经解决了这个问题。

我最终只是捕获了视频帧。“拍照”按钮只是设置一个标志;在视频帧回调中,如果设置了标志,则返回视频帧而不是 UIImage*。这足以满足我们的图像处理需求——“拍照”在很大程度上存在,因此用户可以获得否定响应(以及提交错误报告的选项);我们实际上并不想要 2/3/5 兆像素的图像,因为它们需要很长时间才能处理。

如果视频帧不够好(即您想在高分辨率图像捕获之间捕获取景器帧),我会首先查看它们是否已使用多个 AVCapture 会话修复,因为这是您可以设置两个预设的唯一方法。

可能值得提交一个错误。我在发布 4.0 GM 时提交了一个错误;Apple 要求我提供一些示例代码,但到那时我决定使用视频框架解决方法并发布一个版本。

此外,“低”预设的分辨率非常低(并导致低分辨率、低帧率的视频预览)。如果有的话,我会选择 640x480,如果没有的话,我会选择中等。

于 2010-10-03T01:10:26.260 回答
6

这是一个巨大的帮助——我被困在杂草中很长一段时间试图遵循 AVCam 的例子。

这是一个完整的工作项目,我的评论解释了正在发生的事情。这说明了如何使用具有多个输出的捕获管理器。在此示例中,有两个输出。

第一个是上面示例的静止图像输出。

第二个提供对来自相机的视频的逐帧访问。如果您愿意,您可以添加更多代码来对框架做一些有趣的事情。在此示例中,我只是从委托回调中更新屏幕上的帧计数器。

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

于 2011-11-13T20:19:02.460 回答
3

苹果对此有一些注释和示例代码:

技术问答 QA1702:如何使用 AV Foundation 将摄像机中的视频帧捕获为图像

于 2010-10-03T21:00:53.530 回答
0

您应该使用Adam的答案,但如果您使用 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设置,并且只需要从中获取一个静止图像,就像我一样。

于 2016-02-11T00:09:30.163 回答