title: iPhone take augmented reality screenshot with AVCaptureVideoPreviewLayer
body: <p>I have a small augmented reality app that I'm developing and would like to know how to save a screenshot of what the user sees with a tap of a button or a timer.</p>
  
  <p>The app works by overlaying live camera feed above another UIView. I can save screenshots by using power button +home button, these are saved to camera roll. However, Apple will not render the AVCaptureVideoPreviewLayer, even if I ask the window to save itself. It will create a transparent piece of canvas where the preview layer is.</p>
  
  <p><strong>What's the proper way for an augmented reality app to save screenshots, including transparency and subviews?</strong> </p>
  
  <pre><code>//displaying a live preview on one of the views
      -(void)startCapture
  {
          captureSession = [[AVCaptureSession alloc] init];
          AVCaptureDevice *audioCaptureDevice = nil;
  
          //           AVCaptureDevice *audioCaptureDevice = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
  
          NSArray *videoDevices = [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo];
  
          for (AVCaptureDevice *device in videoDevices) {
  
              if(useFrontCamera){
                  if (device.position == AVCaptureDevicePositionFront) {
                      //FRONT-FACING CAMERA EXISTS
                      audioCaptureDevice = device;
                      break;
                  }
              }else{
                  if (device.position == AVCaptureDevicePositionBack) {
                      //Rear-FACING CAMERA EXISTS
                      audioCaptureDevice = device;
                      break;
                  }
              }
  
          }
  
  
          NSError *error = nil;
          AVCaptureDeviceInput *audioInput = [AVCaptureDeviceInput deviceInputWithDevice:audioCaptureDevice error:&amp;error];
          if (audioInput) {
              [captureSession addInput:audioInput];
          }
          else {
              // Handle the failure.
          }
  
  
  if([captureSession canAddOutput:captureOutput]){
   captureOutput = [[AVCaptureVideoDataOutput alloc] init];
      [captureOutput setAlwaysDiscardsLateVideoFrames:YES];
      [captureOutput setSampleBufferDelegate:self queue:queue];
      [captureOutput setVideoSettings:videoSettings];
  
      dispatch_release(queue);
  }else{
  //handle failure
  }
  
          previewLayer = [AVCaptureVideoPreviewLayer layerWithSession:captureSession];
          UIView *aView = arOverlayView;
          previewLayer.frame =CGRectMake(0,0, arOverlayView.frame.size.width,arOverlayView.frame.size.height); // Assume you want the preview layer to fill the view.
  
          [aView.layer addSublayer:previewLayer];
          [captureSession startRunning];
  
  
  }
  
  //ask the entire window to draw itself in a graphics context. This call will not render
  </code></pre>
  
  <p>//the AVCaptureVideoPreviewLayer . It has to be replaced with a UIImageView or GL based view. 
  //see following code for creating a dynamically updating UIImageView
      -(void)saveScreenshot
      {</p>
  
  <pre><code>   UIGraphicsBeginImageContext(appDelegate.window.bounds.size);
  
      [appDelegate.window.layer renderInContext:UIGraphicsGetCurrentContext()];
  
      UIImage *screenshot = UIGraphicsGetImageFromCurrentImageContext();
      UIGraphicsEndImageContext();
  
      UIImageWriteToSavedPhotosAlbum(screenshot, self, 
                                     @selector(image:didFinishSavingWithError:contextInfo:), nil);
  
  
  }
  
  
  //image saved to camera roll callback
  - (void)image:(UIImage *)image didFinishSavingWithError:(NSError *)error 
    contextInfo:(void *)contextInfo
  {
      // Was there an error?
      if (error != NULL)
      {
          // Show error message...
           NSLog(@"save failed");
  
      }
      else  // No errors
      {
           NSLog(@"save successful");
          // Show message image successfully saved
      }
  }
  </code></pre>
  
  <p>Here's the code for creating the image:</p>
  
  <p>//you need to add your view controller as a delegate to the camera output to be notified of buffereed data</p>
  
  <pre><code>-(void)activateCameraFeed
  {
  //this is the code responsible for capturing feed for still image processing
   dispatch_queue_t queue = dispatch_queue_create("com.AugmentedRealityGlamour.ImageCaptureQueue", NULL);
  
      captureOutput = [[AVCaptureVideoDataOutput alloc] init];
      [captureOutput setAlwaysDiscardsLateVideoFrames:YES];
      [captureOutput setSampleBufferDelegate:self queue:queue];
      [captureOutput setVideoSettings:videoSettings];
  
      dispatch_release(queue);
  
  //......configure audio feed, add inputs and outputs
  
  }
  
  //buffer delegate callback
  - (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection
  {
      if ( ignoreImageStream )
          return;
         [self performImageCaptureFrom:sampleBuffer];
  
  } 
  </code></pre>
  
  <p>Create a UIImage:</p>
  
  <pre><code>- (void) performImageCaptureFrom:(CMSampleBufferRef)sampleBuffer
  {
      CVImageBufferRef imageBuffer;
  
      if ( CMSampleBufferGetNumSamples(sampleBuffer) != 1 )
          return;
      if ( !CMSampleBufferIsValid(sampleBuffer) )
          return;
      if ( !CMSampleBufferDataIsReady(sampleBuffer) )
          return;
  
      imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer); 
  
      if ( CVPixelBufferGetPixelFormatType(imageBuffer) != kCVPixelFormatType_32BGRA )
          return;
  
      CVPixelBufferLockBaseAddress(imageBuffer,0); 
  
      uint8_t *baseAddress = (uint8_t *)CVPixelBufferGetBaseAddress(imageBuffer);
      size_t bytesPerRow = CVPixelBufferGetBytesPerRow(imageBuffer);
      size_t width = CVPixelBufferGetWidth(imageBuffer);
      size_t height = CVPixelBufferGetHeight(imageBuffer); 
  
      CGImageRef newImage = nil;
  
      if ( cameraDeviceSetting == CameraDeviceSetting640x480 )
      {
          CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
          CGContextRef newContext = CGBitmapContextCreate(baseAddress, width, height, 8, bytesPerRow, colorSpace, kCGBitmapByteOrder32Little | kCGImageAlphaPremultipliedFirst);
          newImage = CGBitmapContextCreateImage(newContext);
          CGColorSpaceRelease( colorSpace );
          CGContextRelease(newContext);
      }
      else
      {
          uint8_t *tempAddress = malloc( 640 * 4 * 480 );
          memcpy( tempAddress, baseAddress, bytesPerRow * height );
          baseAddress = tempAddress;
          CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
          CGContextRef newContext = CGBitmapContextCreate(baseAddress, width, height, 8, bytesPerRow, colorSpace,  kCGBitmapByteOrder32Little | kCGImageAlphaNoneSkipFirst);
          newImage = CGBitmapContextCreateImage(newContext);
          CGContextRelease(newContext);
          newContext = CGBitmapContextCreate(baseAddress, 640, 480, 8, 640*4, colorSpace, kCGBitmapByteOrder32Little | kCGImageAlphaPremultipliedFirst);
          CGContextScaleCTM( newContext, (CGFloat)640/(CGFloat)width, (CGFloat)480/(CGFloat)height );
          CGContextDrawImage(newContext, CGRectMake(0,0,640,480), newImage);
          CGImageRelease(newImage);
          newImage = CGBitmapContextCreateImage(newContext);
          CGColorSpaceRelease( colorSpace );
          CGContextRelease(newContext);
          free( tempAddress );
      }
  
      if ( newImage != nil )
      {
  
  //modified for iOS5.0 with ARC    
      tempImage =  [[UIImage alloc] initWithCGImage:newImage scale:(CGFloat)1.0 orientation:cameraImageOrientation];
      CGImageRelease(newImage);
  
  //this call creates the illusion of a preview layer, while we are actively switching images created with this method
          [self performSelectorOnMainThread:@selector(newCameraImageNotification:) withObject:tempImage waitUntilDone:YES];
      }
  
      CVPixelBufferUnlockBaseAddress(imageBuffer,0);
  }
  </code></pre>
  
  <p>update the interface with a UIView that can actually be rendered in a graphics context:</p>
  
  <pre><code>- (void) newCameraImageNotification:(UIImage*)newImage
  {
      if ( newImage == nil )
          return;
  
          [arOverlayView setImage:newImage];
  //or do more advanced processing of the image
  }
  </code></pre>