Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

AVCaptureVideoPreviewLayer: taking a snapshot

I'm trying to emulate the animation seen in the default camera app, where a snapshot of the cameras viewfinder is animated into the corner of the apps display.

The AVCaptureVideoPreviewLayer object that holds the key to solving this problem isn't very open to these requirements: trying to create a copy of it in a new layer with ..

- (id)initWithLayer:(id)layer

.. returns an empty layer, without the image snapshot, so clearly there is some deeper magic going on here.

Your clues/boos are most welcome.

M.

like image 648
Martin Cowie Avatar asked Aug 03 '10 15:08

Martin Cowie


2 Answers

facing the same woes, from a slightly different angle.

Here are possible solutions, that none are too great IMO:

  • You can add to an AVCaptureSession both an AVCaptureStillImageOutput and an AVCaptureVideoDataOutput. When you set the sessionPreset to AVCaptureSessionPresetHigh you'll start getting frames by the API, and when you switch to AVCaptureSessionPresetPhoto you can take real images. So right before taking the picture, you can switch to video, get a frame, and then return to camera. Major caveat is that it takes a "long" time (couple of seconds) for the camera to switch between the video camera and picture camera.

  • Another option would be to use only the camera output (AVCaptureStillImageOutput), and use UIGetScreenImage to get a screen capture of the phone. You could then crop out the controls and leave only the image. This gets complicated if you're showing UI controls over the image. Also, according to this post, Apple started rejecting apps that use this function (it was always iffy).

  • Aside from these I also tried playing with AVCaptureVideoPreviewLayer. There's this post to save a UIView or CALayer to a UIImage. But it all produces clear or white images. I tried accessing the layer, the view's layer, the superlayer, the presentationLayer, the modelLayer, but to no avail. I guess the data in AVCaptureVideoPreviewLayer is very internal, and not really part of the regular layer infrastructure.

Hope this helps, Oded.

like image 106
Oded Ben Dov Avatar answered Sep 19 '22 02:09

Oded Ben Dov


I think you should add an AVCaptureVideoDataOutput to the current session with:

AVCaptureVideoDataOutput *videoOutput = [[AVCaptureVideoDataOutput alloc] init];
videoOutput.videoSettings = @{ (NSString *)kCVPixelBufferPixelFormatTypeKey : @(kCVPixelFormatType_32BGRA) };
[session addOutput:videoOutput];

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

Then, implement the delegate method below to get your image snapshot:

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

    UIImage *image = [self imageFromSampleBuffer:sampleBuffer];
    // Add your code here that uses the image.
    dispatch_async(dispatch_get_main_queue(), ^{
        _imageView.image = image;
    });   
}

This will consume memory and reduce the performance of the app. To improve, you can also optimize your AVCaptureVideoDataOutput with:

videoOutput.minFrameDuration = CMTimeMake(1, 15);

You can also use alwaysDiscardsLateVideoFrames.

like image 39
Phien Tram Avatar answered Sep 20 '22 02:09

Phien Tram