added ios camera support in highgui. turned on optimization in opencv2.framework...
authorVadim Pisarevsky <vadim.pisarevsky@itseez.com>
Mon, 20 Aug 2012 15:36:36 +0000 (19:36 +0400)
committerVadim Pisarevsky <vadim.pisarevsky@itseez.com>
Mon, 20 Aug 2012 15:36:36 +0000 (19:36 +0400)
ios/cmake/Modules/Platform/iOS.cmake
modules/core/include/opencv2/core/core.hpp
modules/highgui/CMakeLists.txt
modules/highgui/include/opencv2/highgui/cap_ios.h [new file with mode: 0644]
modules/highgui/src/cap_ios_abstract_camera.mm [new file with mode: 0644]
modules/highgui/src/cap_ios_photo_camera.mm [new file with mode: 0644]
modules/highgui/src/cap_ios_video_camera.mm [new file with mode: 0644]

index c657b80..e838979 100644 (file)
@@ -42,6 +42,8 @@ set (CMAKE_CXX_OSX_CURRENT_VERSION_FLAG "${CMAKE_C_OSX_CURRENT_VERSION_FLAG}")
 set (CMAKE_C_FLAGS "")
 set (CMAKE_CXX_FLAGS "-headerpad_max_install_names -fvisibility=hidden -fvisibility-inlines-hidden")
 
+set (CMAKE_CXX_FLAGS_RELEASE "-O3 -fomit-frame-pointer")
+
 if (HAVE_FLAG_SEARCH_PATHS_FIRST)
        set (CMAKE_C_LINK_FLAGS "-Wl,-search_paths_first ${CMAKE_C_LINK_FLAGS}")
        set (CMAKE_CXX_LINK_FLAGS "-Wl,-search_paths_first ${CMAKE_CXX_LINK_FLAGS}")
index fbaf137..5fd9272 100644 (file)
@@ -440,7 +440,7 @@ template<typename _Tp, int m, int n> class CV_EXPORTS Matx
 {
 public:
     typedef _Tp value_type;
-    typedef Matx<_Tp, MIN(m, n), 1> diag_type;
+    typedef Matx<_Tp, (m < n ? m : n), 1> diag_type;
     typedef Matx<_Tp, m, n> mat_type;
     enum { depth = DataDepth<_Tp>::value, rows = m, cols = n, channels = rows*cols,
            type = CV_MAKETYPE(depth, channels) };
index 6b9341b..d53c17c 100644 (file)
@@ -193,6 +193,12 @@ elseif(APPLE)
   endif()
 endif()
 
+if(IOS)
+  add_definitions(-DHAVE_IOS=1)
+  list(APPEND highgui_srcs src/cap_ios_abstract_camera.mm src/cap_ios_photo_camera.mm src/cap_ios_video_camera.mm)
+  list(APPEND HIGHGUI_LIBRARIES "-framework Accelerate" "-framework AVFoundation" "-framework CoreGraphics" "-framework CoreImage" "-framework CoreMedia" "-framework CoreVideo" "-framework QuartzCore" "-framework AssetsLibrary")
+endif()
+
 if(WIN32)
   link_directories("${OpenCV_SOURCE_DIR}/3rdparty/lib") # for ffmpeg wrapper only
   include_directories(AFTER SYSTEM "${OpenCV_SOURCE_DIR}/3rdparty/include") # for directshow in VS2005 and multi-monitor support on MinGW
diff --git a/modules/highgui/include/opencv2/highgui/cap_ios.h b/modules/highgui/include/opencv2/highgui/cap_ios.h
new file mode 100644 (file)
index 0000000..cc6668a
--- /dev/null
@@ -0,0 +1,160 @@
+/*
+ *  cap_ios.h
+ *  For iOS video I/O
+ *  by Eduard Feicho on 29/07/12
+ *  Copyright 2012. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#import <UIKit/UIKit.h>
+#import <Accelerate/Accelerate.h>
+#import <AVFoundation/AVFoundation.h>
+#import <ImageIO/ImageIO.h>
+#include "opencv2/core/core.hpp"
+
+/////////////////////////////////////// CvAbstractCamera /////////////////////////////////////
+
+@class CvAbstractCamera;
+
+@interface CvAbstractCamera : NSObject
+{
+       AVCaptureSession* captureSession;
+       AVCaptureConnection* videoCaptureConnection;
+       AVCaptureVideoPreviewLayer *captureVideoPreviewLayer;
+       
+       UIDeviceOrientation currentDeviceOrientation;
+       
+       BOOL cameraAvailable;
+       BOOL captureSessionLoaded;
+       BOOL running;
+       BOOL useAVCaptureVideoPreviewLayer;
+       
+       AVCaptureDevicePosition defaultAVCaptureDevicePosition;
+       AVCaptureVideoOrientation defaultAVCaptureVideoOrientation;
+       NSString *const defaultAVCaptureSessionPreset;
+       
+       int defaultFPS;
+       
+       UIView* parentView;
+    
+    int imageWidth;
+    int imageHeight;
+}
+
+@property (nonatomic, retain) AVCaptureSession* captureSession;
+@property (nonatomic, retain) AVCaptureConnection* videoCaptureConnection;
+
+@property (nonatomic, readonly) BOOL running;
+@property (nonatomic, readonly) BOOL captureSessionLoaded;
+
+@property (nonatomic, assign) int defaultFPS;
+@property (nonatomic, assign) AVCaptureDevicePosition defaultAVCaptureDevicePosition;
+@property (nonatomic, assign) AVCaptureVideoOrientation defaultAVCaptureVideoOrientation;
+@property (nonatomic, assign) BOOL useAVCaptureVideoPreviewLayer;
+@property (nonatomic, strong) NSString *const defaultAVCaptureSessionPreset;
+
+@property (nonatomic, assign) int imageWidth;
+@property (nonatomic, assign) int imageHeight;
+
+@property (nonatomic, retain) UIView* parentView;
+
+- (void)pause;
+- (void)start;
+- (void)stop;
+- (void)switchCameras;
+
+- (id)initWithParentView:(UIView*)parent;
+
+- (void)createCaptureOutput;
+- (void)createVideoPreviewLayer;
+- (void)updateOrientation;
+
+
+@end
+
+///////////////////////////////// CvVideoCamera ///////////////////////////////////////////
+
+@class CvVideoCamera;
+
+@protocol CvVideoCameraDelegate <NSObject>
+
+#ifdef __cplusplus
+// delegate method for processing image frames
+- (void)processImage:(cv::Mat&)image;
+#endif
+
+@end
+
+@interface CvVideoCamera : CvAbstractCamera<AVCaptureVideoDataOutputSampleBufferDelegate>
+{
+       AVCaptureVideoDataOutput *videoDataOutput;
+       
+       dispatch_queue_t videoDataOutputQueue;
+       CALayer *customPreviewLayer;
+       
+       BOOL grayscaleMode;
+    
+    BOOL recordVideo;
+    AVAssetWriterInput* recordAssetWriterInput;
+    AVAssetWriterInputPixelBufferAdaptor* recordPixelBufferAdaptor;
+    AVAssetWriter* recordAssetWriter;
+    
+}
+
+@property (nonatomic, assign) id<CvVideoCameraDelegate> delegate;
+@property (nonatomic, assign) BOOL grayscaleMode;
+
+@property (nonatomic, assign) BOOL recordVideo;
+@property (nonatomic, retain) AVAssetWriterInput* recordAssetWriterInput;
+@property (nonatomic, retain) AVAssetWriterInputPixelBufferAdaptor* recordPixelBufferAdaptor;
+@property (nonatomic, retain) AVAssetWriter* recordAssetWriter;
+
+- (void)adjustLayoutToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation;
+- (void)layoutPreviewLayer;
+- (void)saveVideo;
+
+@end
+
+///////////////////////////////// CvPhotoCamera ///////////////////////////////////////////
+
+@class CvPhotoCamera;
+
+@protocol CvPhotoCameraDelegate <NSObject>
+
+- (void)photoCamera:(CvPhotoCamera*)photoCamera capturedImage:(UIImage *)image;
+- (void)photoCameraCancel:(CvPhotoCamera*)photoCamera;
+
+@end
+
+@interface CvPhotoCamera : CvAbstractCamera
+{
+       AVCaptureStillImageOutput *stillImageOutput;
+}
+
+@property (nonatomic, assign) id<CvPhotoCameraDelegate> delegate;
+
+- (void)takePicture;
+
+@end
diff --git a/modules/highgui/src/cap_ios_abstract_camera.mm b/modules/highgui/src/cap_ios_abstract_camera.mm
new file mode 100644 (file)
index 0000000..45b53b0
--- /dev/null
@@ -0,0 +1,408 @@
+/*
+ *  cap_ios_abstract_camera.mm
+ *  For iOS video I/O
+ *  by Eduard Feicho on 29/07/12
+ *  Copyright 2012. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+
+#import "opencv2/highgui/cap_ios.h"
+#include "precomp.hpp"
+
+#pragma mark - Private Interface
+
+@interface CvAbstractCamera ()
+
+@property (nonatomic, retain) AVCaptureVideoPreviewLayer* captureVideoPreviewLayer;
+
+- (void)deviceOrientationDidChange:(NSNotification*)notification;
+- (void)startCaptureSession;
+
+- (void)setDesiredCameraPosition:(AVCaptureDevicePosition)desiredPosition;
+
+- (void)updateSize;
+
+@end
+
+
+#pragma mark - Implementation
+
+
+@implementation CvAbstractCamera
+
+
+
+#pragma mark Public
+
+@synthesize imageWidth;
+@synthesize imageHeight;
+
+
+@synthesize defaultFPS;
+@synthesize defaultAVCaptureDevicePosition;
+@synthesize defaultAVCaptureVideoOrientation;
+@synthesize defaultAVCaptureSessionPreset;
+
+
+
+@synthesize captureSession;
+@synthesize captureVideoPreviewLayer;
+@synthesize videoCaptureConnection;
+@synthesize running;
+@synthesize captureSessionLoaded;
+@synthesize useAVCaptureVideoPreviewLayer;
+
+@synthesize parentView;
+
+#pragma mark - Constructors
+
+- (id)init;
+{
+       self = [super init];
+       if (self) {
+               // react to device orientation notifications
+               [[NSNotificationCenter defaultCenter] addObserver:self
+                                                                                                selector:@selector(deviceOrientationDidChange:)
+                                                                                                        name:UIDeviceOrientationDidChangeNotification
+                                                                                                  object:nil];
+               [[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
+               currentDeviceOrientation = [[UIDevice currentDevice] orientation];
+               
+               
+               // check if camera available
+               cameraAvailable = [UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera];
+               NSLog(@"camera available: %@", (cameraAvailable == YES ? @"YES" : @"NO") );
+               
+               running = NO;
+               
+               // set camera default configuration
+               self.defaultAVCaptureDevicePosition = AVCaptureDevicePositionFront;
+               self.defaultAVCaptureVideoOrientation = AVCaptureVideoOrientationLandscapeLeft;
+               self.defaultFPS = 15;
+               self.defaultAVCaptureSessionPreset = AVCaptureSessionPreset352x288;
+               
+               self.parentView = nil;
+               self.useAVCaptureVideoPreviewLayer = NO;
+    }
+       return self;
+}
+
+
+
+- (id)initWithParentView:(UIView*)parent;
+{
+       self = [super init];
+       if (self) {
+               // react to device orientation notifications
+               [[NSNotificationCenter defaultCenter] addObserver:self
+                                                                                                selector:@selector(deviceOrientationDidChange:)
+                                                                                                        name:UIDeviceOrientationDidChangeNotification
+                                                                                                  object:nil];
+               [[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
+               currentDeviceOrientation = [[UIDevice currentDevice] orientation];
+               
+               
+               // check if camera available
+               cameraAvailable = [UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera];
+               NSLog(@"camera available: %@", (cameraAvailable == YES ? @"YES" : @"NO") );
+               
+               running = NO;
+               
+               // set camera default configuration
+               self.defaultAVCaptureDevicePosition = AVCaptureDevicePositionFront;
+               self.defaultAVCaptureVideoOrientation = AVCaptureVideoOrientationLandscapeLeft;
+               self.defaultFPS = 15;
+               self.defaultAVCaptureSessionPreset = AVCaptureSessionPreset640x480;
+               
+               self.parentView = parent;
+               self.useAVCaptureVideoPreviewLayer = YES;
+       }
+       return self;
+}
+
+
+
+- (void)dealloc;
+{
+       [[NSNotificationCenter defaultCenter] removeObserver:self];
+       [[UIDevice currentDevice] endGeneratingDeviceOrientationNotifications];
+}
+
+
+#pragma mark - Public interface
+
+
+- (void)start;
+{
+    if (![NSThread isMainThread]) {
+        NSLog(@"[Camera] Warning: Call start only from main thread");
+        [self performSelectorOnMainThread:@selector(start) withObject:nil waitUntilDone:NO];
+        return;
+    }
+    
+       if (running == YES) {
+               return;
+       }
+       running = YES;
+    
+    // TOOD update image size data before actually starting (needed for recording)
+    [self updateSize];
+    
+       if (cameraAvailable) {
+               [self startCaptureSession];
+       }
+}
+
+
+- (void)pause;
+{
+       running = NO;
+       [self.captureSession stopRunning];
+}
+
+
+
+- (void)stop;
+{
+       running = NO;
+       
+       // Release any retained subviews of the main view.
+       // e.g. self.myOutlet = nil;
+       
+       [self.captureSession stopRunning];
+       self.captureSession = nil;
+       self.captureVideoPreviewLayer = nil;
+       self.videoCaptureConnection = nil;
+       captureSessionLoaded = NO;
+}
+
+
+
+// use front/back camera
+- (void)switchCameras;
+{
+       BOOL was_running = self.running;
+       if (was_running) {
+               [self stop];
+       }
+       if (self.defaultAVCaptureDevicePosition == AVCaptureDevicePositionFront) {
+               self.defaultAVCaptureDevicePosition = AVCaptureDevicePositionBack;
+       } else {
+               self.defaultAVCaptureDevicePosition  = AVCaptureDevicePositionFront;
+       }
+       if (was_running) {
+               [self start];
+       }
+}
+
+
+
+#pragma mark - Device Orientation Changes
+
+
+- (void)deviceOrientationDidChange:(NSNotification*)notification
+{
+       UIDeviceOrientation orientation = [UIDevice currentDevice].orientation;
+       
+       switch (orientation)
+       {
+               case UIDeviceOrientationPortrait:
+               case UIDeviceOrientationPortraitUpsideDown:
+               case UIDeviceOrientationLandscapeLeft:
+               case UIDeviceOrientationLandscapeRight:
+                       currentDeviceOrientation = orientation;
+                       break;
+                       
+               case UIDeviceOrientationFaceUp:
+               case UIDeviceOrientationFaceDown:
+               default:
+                       break;
+       }
+       NSLog(@"deviceOrientationDidChange: %d", orientation);
+       
+       [self updateOrientation];
+}
+
+
+
+#pragma mark - Private Interface
+
+- (void)createCaptureSession;
+{
+       // set a av capture session preset
+       self.captureSession = [[AVCaptureSession alloc] init];
+       if ([self.captureSession canSetSessionPreset:self.defaultAVCaptureSessionPreset]) {
+               [self.captureSession setSessionPreset:self.defaultAVCaptureSessionPreset];
+       } else if ([self.captureSession canSetSessionPreset:AVCaptureSessionPresetLow]) {
+               [self.captureSession setSessionPreset:AVCaptureSessionPresetLow];
+       } else {
+               NSLog(@"[Camera] Error: could not set session preset");
+       }
+}
+
+- (void)createCaptureDevice;
+{
+       // setup the device
+       AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
+       [self setDesiredCameraPosition:self.defaultAVCaptureDevicePosition];
+       NSLog(@"[Camera] device connected? %@", device.connected ? @"YES" : @"NO");
+       NSLog(@"[Camera] device position %@", (device.position == AVCaptureDevicePositionBack) ? @"back" : @"front");
+}
+
+
+- (void)createVideoPreviewLayer;
+{
+       self.captureVideoPreviewLayer = [[AVCaptureVideoPreviewLayer alloc] initWithSession:self.captureSession];
+       
+       if ([self.captureVideoPreviewLayer isOrientationSupported]) {
+               [self.captureVideoPreviewLayer setOrientation:self.defaultAVCaptureVideoOrientation];
+       }
+       
+       if (parentView != nil) {
+               self.captureVideoPreviewLayer.frame = self.parentView.bounds;
+               self.captureVideoPreviewLayer.videoGravity = AVLayerVideoGravityResizeAspectFill;
+               [self.parentView.layer addSublayer:self.captureVideoPreviewLayer];
+       }
+       NSLog(@"[Camera] created AVCaptureVideoPreviewLayer");
+}
+
+
+
+
+- (void)setDesiredCameraPosition:(AVCaptureDevicePosition)desiredPosition;
+{
+       for (AVCaptureDevice *device in [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo]) {
+               if ([device position] == desiredPosition) {
+                       [self.captureSession beginConfiguration];
+                       
+                       NSError* error;
+                       AVCaptureDeviceInput *input = [AVCaptureDeviceInput deviceInputWithDevice:device error:&error];
+                       if (!input) {
+                               NSLog(@"error creating input %@", [error localizedDescription]);
+                       }
+                       
+                       // support for autofocus
+                       if ([device isFocusModeSupported:AVCaptureFocusModeContinuousAutoFocus]) {
+                               NSError *error = nil;
+                               if ([device lockForConfiguration:&error]) {
+                                       device.focusMode = AVCaptureFocusModeContinuousAutoFocus;
+                                       [device unlockForConfiguration];
+                               } else {
+                                       NSLog(@"unable to lock device for autofocos configuration %@", [error localizedDescription]);
+                               }
+                       }
+                       [self.captureSession addInput:input];
+                       
+                       for (AVCaptureInput *oldInput in self.captureSession.inputs) {
+                               [self.captureSession removeInput:oldInput];
+                       }
+                       [self.captureSession addInput:input];
+                       [self.captureSession commitConfiguration];
+                       
+                       break;
+               }
+       }
+}
+
+
+
+- (void)startCaptureSession
+{
+       if (!cameraAvailable) {
+               return;
+       }
+       
+       if (self.captureSessionLoaded == NO) {
+               [self createCaptureSession];
+               [self createCaptureDevice];
+               [self createCaptureOutput];
+               
+               // setup preview layer
+               if (self.useAVCaptureVideoPreviewLayer) {
+                       [self createVideoPreviewLayer];
+               } else {
+                       [self createCustomVideoPreview];
+               }
+               
+               captureSessionLoaded = YES;
+       }
+       
+       [self.captureSession startRunning];
+}
+
+
+- (void)createCaptureOutput;
+{
+       [NSException raise:NSInternalInconsistencyException
+                               format:@"You must override %@ in a subclass", NSStringFromSelector(_cmd)];
+}
+
+- (void)createCustomVideoPreview;
+{
+       [NSException raise:NSInternalInconsistencyException
+                               format:@"You must override %@ in a subclass", NSStringFromSelector(_cmd)];
+}
+
+- (void)updateOrientation;
+{
+       // nothing to do here
+}
+
+
+- (void)updateSize;
+{
+    if ([self.defaultAVCaptureSessionPreset isEqualToString:AVCaptureSessionPresetPhoto]) {
+        //TODO: find the correct resolution
+        self.imageWidth = 640;
+        self.imageHeight = 480;
+    } else if ([self.defaultAVCaptureSessionPreset isEqualToString:AVCaptureSessionPresetHigh]) {
+        //TODO: find the correct resolution
+        self.imageWidth = 640;
+        self.imageHeight = 480;
+    } else if ([self.defaultAVCaptureSessionPreset isEqualToString:AVCaptureSessionPresetMedium]) {
+        //TODO: find the correct resolution
+        self.imageWidth = 640;
+        self.imageHeight = 480;        
+    } else if ([self.defaultAVCaptureSessionPreset isEqualToString:AVCaptureSessionPresetLow]) {
+        //TODO: find the correct resolution
+        self.imageWidth = 640;
+        self.imageHeight = 480;
+    } else if ([self.defaultAVCaptureSessionPreset isEqualToString:AVCaptureSessionPreset352x288]) {
+        self.imageWidth = 352;
+        self.imageHeight = 288;
+    } else if ([self.defaultAVCaptureSessionPreset isEqualToString:AVCaptureSessionPreset640x480]) {
+        self.imageWidth = 640;
+        self.imageHeight = 480;
+    } else if ([self.defaultAVCaptureSessionPreset isEqualToString:AVCaptureSessionPreset1280x720]) {
+        self.imageWidth = 1280;
+        self.imageHeight = 720;
+    } else {
+        self.imageWidth = 640;
+        self.imageHeight = 480;
+    }
+}
+
+@end
diff --git a/modules/highgui/src/cap_ios_photo_camera.mm b/modules/highgui/src/cap_ios_photo_camera.mm
new file mode 100644 (file)
index 0000000..51e5ce2
--- /dev/null
@@ -0,0 +1,165 @@
+/*
+ *  cap_ios_photo_camera.mm
+ *  For iOS video I/O
+ *  by Eduard Feicho on 29/07/12
+ *  Copyright 2012. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+
+#import "opencv2/highgui/cap_ios.h"
+#include "precomp.hpp"
+
+#pragma mark - Private Interface
+
+
+@interface CvPhotoCamera ()
+
+@property (nonatomic, retain) AVCaptureStillImageOutput* stillImageOutput;
+
+@end
+
+
+
+#pragma mark - Implementation
+
+
+@implementation CvPhotoCamera
+
+
+
+#pragma mark Public
+
+@synthesize stillImageOutput;
+@synthesize delegate;
+
+
+#pragma mark - Public interface
+
+
+- (void)takePicture
+{
+       if (cameraAvailable == NO) {
+               return;
+       }
+       cameraAvailable = NO;
+       
+       
+       [self.stillImageOutput captureStillImageAsynchronouslyFromConnection:self.videoCaptureConnection
+                                                                                                          completionHandler:
+        ^(CMSampleBufferRef imageSampleBuffer, NSError *error)
+        {
+                if (error == nil && imageSampleBuffer != NULL)
+                {
+                        // TODO check
+                        //                      NSNumber* imageOrientation = [UIImage cgImageOrientationForUIDeviceOrientation:currentDeviceOrientation];
+                        //                      CMSetAttachment(imageSampleBuffer, kCGImagePropertyOrientation, imageOrientation, 1);
+                        
+                        NSData *jpegData = [AVCaptureStillImageOutput jpegStillImageNSDataRepresentation:imageSampleBuffer];
+                        
+                        dispatch_async(dispatch_get_main_queue(), ^{
+                                [self.captureSession stopRunning];
+                                
+                                // Make sure we create objects on the main thread in the main context
+                                UIImage* newImage = [UIImage imageWithData:jpegData];
+                                
+                                //UIImageOrientation orientation = [newImage imageOrientation];
+                                
+                                // TODO: only apply rotation, don't scale, since we can set this directly in the camera
+                                /*
+                                 switch (orientation) {
+                                 case UIImageOrientationUp:
+                                 case UIImageOrientationDown:
+                                 newImage = [newImage imageWithAppliedRotationAndMaxSize:CGSizeMake(640.0, 480.0)];
+                                 break;
+                                 case UIImageOrientationLeft:
+                                 case UIImageOrientationRight:
+                                 newImage = [newImage imageWithMaxSize:CGSizeMake(640.0, 480.0)];
+                                 default:
+                                 break;
+                                 }
+                                 */
+                                
+                                // We have captured the image, we can allow the user to take another picture
+                                cameraAvailable = YES;
+                                
+                                NSLog(@"CvPhotoCamera captured image");
+                                if (self.delegate) {
+                                        [self.delegate photoCamera:self capturedImage:newImage];
+                                }
+                                
+                                [self.captureSession startRunning];
+                        });
+                }
+        }];
+       
+
+}
+
+- (void)stop;
+{
+       [super stop];
+       self.stillImageOutput = nil;
+}
+
+
+#pragma mark - Private Interface
+
+
+- (void)createStillImageOutput;
+{
+       // setup still image output with jpeg codec
+       self.stillImageOutput = [[AVCaptureStillImageOutput alloc] init];
+       NSDictionary *outputSettings = [NSDictionary dictionaryWithObjectsAndKeys:AVVideoCodecJPEG, AVVideoCodecKey, nil];
+       [self.stillImageOutput setOutputSettings:outputSettings];
+       [self.captureSession addOutput:self.stillImageOutput];
+       
+       for (AVCaptureConnection *connection in self.stillImageOutput.connections) {
+               for (AVCaptureInputPort *port in [connection inputPorts]) {
+                       if ([port.mediaType isEqual:AVMediaTypeVideo]) {
+                               self.videoCaptureConnection = connection;
+                               break;
+                       }
+               }
+               if (self.videoCaptureConnection) {
+                       break;
+               }
+       }
+       NSLog(@"[Camera] still image output created");
+}
+
+
+- (void)createCaptureOutput;
+{
+       [self createStillImageOutput];
+}
+
+- (void)createCustomVideoPreview;
+{
+       //do nothing, always use AVCaptureVideoPreviewLayer
+}
+
+
+@end
diff --git a/modules/highgui/src/cap_ios_video_camera.mm b/modules/highgui/src/cap_ios_video_camera.mm
new file mode 100644 (file)
index 0000000..bed17e1
--- /dev/null
@@ -0,0 +1,656 @@
+/*
+ *  cap_ios_video_camera.mm
+ *  For iOS video I/O
+ *  by Eduard Feicho on 29/07/12
+ *  Copyright 2012. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#import "opencv2/highgui/cap_ios.h"
+#include "precomp.hpp"
+
+#import <AssetsLibrary/AssetsLibrary.h>
+
+
+static CGFloat DegreesToRadians(CGFloat degrees) {return degrees * M_PI / 180;};
+
+#pragma mark - Private Interface
+
+
+
+
+@interface CvVideoCamera ()
+
+- (void)createVideoDataOutput;
+- (void)createVideoFileOutput;
+
+
+@property (nonatomic, retain) CALayer *customPreviewLayer;
+@property (nonatomic, retain) AVCaptureVideoDataOutput *videoDataOutput;
+
+@end
+
+
+
+#pragma mark - Implementation
+
+
+
+@implementation CvVideoCamera
+
+
+
+
+@synthesize delegate;
+@synthesize grayscaleMode;
+
+@synthesize customPreviewLayer;
+@synthesize videoDataOutput;
+
+@synthesize recordVideo;
+//@synthesize videoFileOutput;
+@synthesize recordAssetWriterInput;
+@synthesize recordPixelBufferAdaptor;
+@synthesize recordAssetWriter;
+
+
+
+#pragma mark - Constructors
+
+- (id)initWithParentView:(UIView*)parent;
+{
+       self = [super initWithParentView:parent];
+       if (self) {
+               self.useAVCaptureVideoPreviewLayer = NO;
+               self.recordVideo = NO;
+       }
+       return self;
+}
+
+
+
+#pragma mark - Public interface
+
+
+- (void)start;
+{
+    [super start];
+    
+       if (self.recordVideo == YES) {
+//             [self.videoFileOutput startRecordingToOutputFileURL:[self tempFileURL] recordingDelegate:self];
+               
+        NSError* error;
+        if ([[NSFileManager defaultManager] fileExistsAtPath:[self tempFileString]]) [[NSFileManager defaultManager] removeItemAtPath:[self tempFileString] error:&error];
+        if (error == nil) {
+            NSLog(@"[Camera] Delete file %@", [self tempFileString]);
+        }
+
+        
+               BOOL started = [self.recordAssetWriter startWriting];
+               [self.recordAssetWriter startSessionAtSourceTime:kCMTimeZero];
+        
+        NSLog(@"[Camera] Session started? %d", started);
+        
+        if (self.recordAssetWriter.status == AVAssetWriterStatusUnknown) {
+            NSLog(@"AVAssetWriter status: unknown");
+        } else if (self.recordAssetWriter.status == AVAssetWriterStatusWriting) {
+            NSLog(@"AVAssetWriter status: writing");
+        } else if (self.recordAssetWriter.status == AVAssetWriterStatusCompleted) {
+            NSLog(@"AVAssetWriter status: completed");
+        } else if (self.recordAssetWriter.status == AVAssetWriterStatusFailed) {
+            NSLog(@"AVAssetWriter status: failed");
+        } else if (self.recordAssetWriter.status == AVAssetWriterStatusCancelled) {
+            NSLog(@"AVAssetWriter status: cancelled");
+        }
+        
+        if (self.recordAssetWriter.status != AVAssetWriterStatusWriting) {
+            NSLog(@"[Camera] Recording Error: asset writer status is not writing: %@", self.recordAssetWriter.error);
+        } else {
+            NSLog(@"[Camera] Recording started");
+        }
+       }
+}
+
+
+
+- (void)pause;
+{
+       [super pause];
+       if (self.recordVideo == YES) {
+//             [self.videoFileOutput stopRecording];
+               
+
+        if (self.recordAssetWriter.status == AVAssetWriterStatusUnknown) {
+            NSLog(@"AVAssetWriter status: unknown");
+        } else if (self.recordAssetWriter.status == AVAssetWriterStatusWriting) {
+            NSLog(@"AVAssetWriter status: writing");
+        } else if (self.recordAssetWriter.status == AVAssetWriterStatusCompleted) {
+            NSLog(@"AVAssetWriter status: completed");
+        } else if (self.recordAssetWriter.status == AVAssetWriterStatusFailed) {
+            NSLog(@"AVAssetWriter status: failed");
+        } else if (self.recordAssetWriter.status == AVAssetWriterStatusCancelled) {
+            NSLog(@"AVAssetWriter status: cancelled");
+        }
+        
+        if (self.recordAssetWriter.status == AVAssetWriterStatusWriting) {
+            [self.recordAssetWriter finishWriting];
+            NSLog(@"[Camera] recording stopped");
+        } else {
+            NSLog(@"[Camera] Recording Error: asset writer status is not writing");
+        }
+       }
+}
+
+
+- (void)stop;
+{
+       [super stop];
+    
+    if (self.recordVideo == YES) {
+        NSLog(@"recording stop");
+        if (self.recordAssetWriter.status == AVAssetWriterStatusUnknown) {
+            NSLog(@"AVAssetWriter status: unknown");
+        } else if (self.recordAssetWriter.status == AVAssetWriterStatusWriting) {
+            NSLog(@"AVAssetWriter status: writing");
+        } else if (self.recordAssetWriter.status == AVAssetWriterStatusCompleted) {
+            NSLog(@"AVAssetWriter status: completed");
+        } else if (self.recordAssetWriter.status == AVAssetWriterStatusFailed) {
+            NSLog(@"AVAssetWriter status: failed");
+        } else if (self.recordAssetWriter.status == AVAssetWriterStatusCancelled) {
+            NSLog(@"AVAssetWriter status: cancelled");
+        }
+               
+        
+        if (self.recordAssetWriter.status == AVAssetWriterStatusWriting) {
+            [self.recordAssetWriter finishWriting];
+            NSLog(@"[Camera] recording stopped");
+        } else {
+            NSLog(@"[Camera] Recording Error: asset writer status is not writing");
+        }
+        
+        self.recordAssetWriter = nil;
+        self.recordAssetWriterInput = nil;
+        self.recordPixelBufferAdaptor = nil;
+       }
+    
+       self.videoDataOutput = nil;
+       if (videoDataOutputQueue) {
+               dispatch_release(videoDataOutputQueue);
+       }
+       
+       [self.customPreviewLayer removeFromSuperlayer];
+       self.customPreviewLayer = nil;
+}
+
+// TODO fix
+- (void)adjustLayoutToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation;
+{      
+       
+       NSLog(@"layout preview layer");
+       if (self.parentView != nil) {
+               
+               CALayer* layer = self.customPreviewLayer;
+               CGRect bounds = self.customPreviewLayer.bounds;
+               int rotation_angle = 0;
+               bool flip_bounds = false;
+               
+               switch (interfaceOrientation) {
+            case UIInterfaceOrientationPortrait:
+                               NSLog(@"to Portrait");
+                rotation_angle = 270;
+                break;
+            case UIInterfaceOrientationPortraitUpsideDown:
+                rotation_angle = 90;
+                               NSLog(@"to UpsideDown");
+                               break;
+            case UIInterfaceOrientationLandscapeLeft:
+                rotation_angle = 0;
+                               NSLog(@"to LandscapeLeft");
+                               break;
+            case UIInterfaceOrientationLandscapeRight:
+                rotation_angle = 180;
+                               NSLog(@"to LandscapeRight");
+                               break;
+            default:
+                break; // leave the layer in its last known orientation
+        }
+               
+               switch (defaultAVCaptureVideoOrientation) {
+                       case AVCaptureVideoOrientationLandscapeRight:
+                               rotation_angle += 180;
+                               break;
+                       case AVCaptureVideoOrientationPortraitUpsideDown:
+                               rotation_angle += 270;
+                               break;
+                       case AVCaptureVideoOrientationPortrait:
+                               rotation_angle += 90;
+                       case AVCaptureVideoOrientationLandscapeLeft:
+                               break;
+                       default:
+                               break;
+               }
+               rotation_angle = rotation_angle % 360;
+               
+               if (rotation_angle == 90 || rotation_angle == 270) {
+                       flip_bounds = true;
+               }
+               
+               if (flip_bounds) {
+                       NSLog(@"flip bounds");
+                       bounds = CGRectMake(0, 0, bounds.size.height, bounds.size.width);
+               }
+               
+               layer.position = CGPointMake(self.parentView.frame.size.width/2., self.parentView.frame.size.height/2.);
+               self.customPreviewLayer.bounds = CGRectMake(0, 0, self.parentView.frame.size.width, self.parentView.frame.size.height);
+               
+               layer.affineTransform = CGAffineTransformMakeRotation( DegreesToRadians(rotation_angle) );
+               layer.bounds = bounds;
+       }
+
+}
+
+// TODO fix
+- (void)layoutPreviewLayer;
+{
+       NSLog(@"layout preview layer");
+       if (self.parentView != nil) {
+               
+               CALayer* layer = self.customPreviewLayer;
+               CGRect bounds = self.customPreviewLayer.bounds;
+               int rotation_angle = 0;
+               bool flip_bounds = false;
+               
+               switch (currentDeviceOrientation) {
+            case UIDeviceOrientationPortrait:
+                rotation_angle = 270;
+                break;
+            case UIDeviceOrientationPortraitUpsideDown:
+                rotation_angle = 90;
+                               break;
+            case UIDeviceOrientationLandscapeLeft:
+                               NSLog(@"left");
+                rotation_angle = 180;
+                               break;
+            case UIDeviceOrientationLandscapeRight:
+                               NSLog(@"right");
+                rotation_angle = 0;
+                               break;
+            case UIDeviceOrientationFaceUp:
+            case UIDeviceOrientationFaceDown:
+            default:
+                break; // leave the layer in its last known orientation
+        }
+               
+               switch (defaultAVCaptureVideoOrientation) {
+                       case AVCaptureVideoOrientationLandscapeRight:
+                               rotation_angle += 180;
+                               break;
+                       case AVCaptureVideoOrientationPortraitUpsideDown:
+                               rotation_angle += 270;
+                               break;
+                       case AVCaptureVideoOrientationPortrait:
+                               rotation_angle += 90;
+                       case AVCaptureVideoOrientationLandscapeLeft:
+                               break;
+                       default:
+                               break;
+               }
+               rotation_angle = rotation_angle % 360;
+               
+               if (rotation_angle == 90 || rotation_angle == 270) {
+                       flip_bounds = true;
+               }
+               
+               if (flip_bounds) {
+                       NSLog(@"flip bounds");
+                       bounds = CGRectMake(0, 0, bounds.size.height, bounds.size.width);
+               }
+               
+               layer.position = CGPointMake(self.parentView.frame.size.width/2., self.parentView.frame.size.height/2.);
+               layer.affineTransform = CGAffineTransformMakeRotation( DegreesToRadians(rotation_angle) );
+               layer.bounds = bounds;
+       }
+       
+}
+
+
+
+
+#pragma mark - Private Interface
+
+
+
+- (void)createVideoDataOutput;
+{
+       // Make a video data output
+       self.videoDataOutput = [AVCaptureVideoDataOutput new];
+       
+       // In grayscale mode we want YUV (YpCbCr 4:2:0) so we can directly access the graylevel intensity values (Y component)
+       // In color mode we, BGRA format is used
+       OSType format = self.grayscaleMode ? kCVPixelFormatType_420YpCbCr8BiPlanarFullRange : kCVPixelFormatType_32BGRA;
+       
+       self.videoDataOutput.videoSettings  = [NSDictionary dictionaryWithObject:[NSNumber numberWithUnsignedInt:format]
+                                                                      forKey:(id)kCVPixelBufferPixelFormatTypeKey];
+       
+       // discard if the data output queue is blocked (as we process the still image)
+       [self.videoDataOutput setAlwaysDiscardsLateVideoFrames:YES];
+       
+       if ( [self.captureSession canAddOutput:self.videoDataOutput] ) {
+               [self.captureSession addOutput:self.videoDataOutput];
+       }
+       [[self.videoDataOutput connectionWithMediaType:AVMediaTypeVideo] setEnabled:YES];
+               
+       
+       // set default FPS
+       if ([self.videoDataOutput connectionWithMediaType:AVMediaTypeVideo].supportsVideoMinFrameDuration) {
+               [self.videoDataOutput connectionWithMediaType:AVMediaTypeVideo].videoMinFrameDuration = CMTimeMake(1, self.defaultFPS);
+       }
+       if ([self.videoDataOutput connectionWithMediaType:AVMediaTypeVideo].supportsVideoMaxFrameDuration) {
+               [self.videoDataOutput connectionWithMediaType:AVMediaTypeVideo].videoMaxFrameDuration = CMTimeMake(1, self.defaultFPS);
+       }
+       
+       // set video mirroring for front camera (more intuitive)
+       if ([self.videoDataOutput connectionWithMediaType:AVMediaTypeVideo].supportsVideoMirroring) {
+               if (self.defaultAVCaptureDevicePosition == AVCaptureDevicePositionFront) {
+                       [self.videoDataOutput connectionWithMediaType:AVMediaTypeVideo].videoMirrored = YES;
+               } else {
+                       [self.videoDataOutput connectionWithMediaType:AVMediaTypeVideo].videoMirrored = NO;
+               }
+       }
+       
+       // set default video orientation
+       if ([self.videoDataOutput connectionWithMediaType:AVMediaTypeVideo].supportsVideoOrientation) {
+               [self.videoDataOutput connectionWithMediaType:AVMediaTypeVideo].videoOrientation = self.defaultAVCaptureVideoOrientation;
+       }
+       
+       
+       // create a custom preview layer
+       self.customPreviewLayer = [CALayer layer];
+       self.customPreviewLayer.bounds = CGRectMake(0, 0, self.parentView.frame.size.width, self.parentView.frame.size.height);
+       [self layoutPreviewLayer];
+       
+       // create a serial dispatch queue used for the sample buffer delegate as well as when a still image is captured
+       // a serial dispatch queue must be used to guarantee that video frames will be delivered in order
+       // see the header doc for setSampleBufferDelegate:queue: for more information
+       videoDataOutputQueue = dispatch_queue_create("VideoDataOutputQueue", DISPATCH_QUEUE_SERIAL);
+       [self.videoDataOutput setSampleBufferDelegate:self queue:videoDataOutputQueue];
+       
+       
+       NSLog(@"[Camera] created AVCaptureVideoDataOutput at %d FPS", self.defaultFPS);
+}
+
+
+
+- (void)createVideoFileOutput;
+{
+       /*
+       if (self.recordVideo == YES) {
+               self.videoFileOutput = [[AVCaptureMovieFileOutput alloc] init];
+               if ( [self.captureSession canAddOutput:self.videoFileOutput] ) {
+                       [self.captureSession addOutput:self.videoFileOutput];
+               }
+       }
+       */
+       
+       /* Video File Output in H.264, via AVAsserWriter */
+    NSLog(@"Create Video with dimensions %dx%d", self.imageWidth, self.imageHeight);
+    
+       NSDictionary *outputSettings
+        = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithInt:self.imageWidth], AVVideoWidthKey,
+                                                                                                 [NSNumber numberWithInt:self.imageHeight], AVVideoHeightKey,
+                                                                                                 AVVideoCodecH264, AVVideoCodecKey,
+                                                                                                 nil
+        ];
+    
+       
+       self.recordAssetWriterInput = [AVAssetWriterInput assetWriterInputWithMediaType:AVMediaTypeVideo outputSettings:outputSettings];
+    
+       
+       /* I'm going to push pixel buffers to it, so will need a 
+          AVAssetWriterPixelBufferAdaptor, to expect the same 32BGRA input as I've
+          asked the AVCaptureVideDataOutput to supply */
+       int pixelBufferFormat = (self.grayscaleMode == YES) ? kCVPixelFormatType_420YpCbCr8BiPlanarFullRange : kCVPixelFormatType_32BGRA;
+       
+       self.recordPixelBufferAdaptor =
+                  [[AVAssetWriterInputPixelBufferAdaptor alloc] 
+                       initWithAssetWriterInput:self.recordAssetWriterInput 
+                       sourcePixelBufferAttributes:[NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithInt:pixelBufferFormat], kCVPixelBufferPixelFormatTypeKey,nil]];
+       
+       NSError* error = nil;
+    NSLog(@"Create AVAssetWriter with url: %@", [self tempFileURL]);
+       self.recordAssetWriter = [AVAssetWriter assetWriterWithURL:[self tempFileURL]
+                                                      fileType:AVFileTypeMPEG4
+                                                         error:&error];
+       if (error != nil) {
+               NSLog(@"[Camera] Unable to create AVAssetWriter: %@", error);
+       }
+       
+       [self.recordAssetWriter addInput:self.recordAssetWriterInput];
+       self.recordAssetWriterInput.expectsMediaDataInRealTime = NO;
+    
+    NSLog(@"[Camera] created AVAssetWriter");
+
+}
+
+
+- (void)createCaptureOutput;
+{
+       [self createVideoDataOutput];
+       if (self.recordVideo == YES) {
+               [self createVideoFileOutput];
+       }
+}
+
+- (void)createCustomVideoPreview;
+{
+       [self.parentView.layer addSublayer:self.customPreviewLayer];
+}
+
+
+#pragma mark - Protocol AVCaptureVideoDataOutputSampleBufferDelegate
+
+
+- (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection
+{
+       if (self.delegate) {
+               
+               // convert from Core Media to Core Video
+               CVImageBufferRef imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer);
+               CVPixelBufferLockBaseAddress(imageBuffer, 0);
+               
+               void* bufferAddress;
+               size_t width;
+               size_t height;
+               size_t bytesPerRow;
+               
+               CGColorSpaceRef colorSpace;
+               CGContextRef context;
+               
+               int format_opencv;
+               
+               OSType format = CVPixelBufferGetPixelFormatType(imageBuffer);
+               if (format == kCVPixelFormatType_420YpCbCr8BiPlanarFullRange) {
+                       
+                       format_opencv = CV_8UC1;
+                       
+                       bufferAddress = CVPixelBufferGetBaseAddressOfPlane(imageBuffer, 0);
+                       width = CVPixelBufferGetWidthOfPlane(imageBuffer, 0);
+                       height = CVPixelBufferGetHeightOfPlane(imageBuffer, 0);
+                       bytesPerRow = CVPixelBufferGetBytesPerRowOfPlane(imageBuffer, 0);
+                       
+               } else { // expect kCVPixelFormatType_32BGRA
+                       
+                       format_opencv = CV_8UC4;
+                       
+                       bufferAddress = CVPixelBufferGetBaseAddress(imageBuffer);
+                       width = CVPixelBufferGetWidth(imageBuffer);
+                       height = CVPixelBufferGetHeight(imageBuffer);
+                       bytesPerRow = CVPixelBufferGetBytesPerRow(imageBuffer);
+                       
+               }
+               
+               // delegate image processing to the delegate
+               cv::Mat image(height, width, format_opencv, bufferAddress, bytesPerRow);
+               
+               cv::Mat* result = NULL;
+               CGImage* dstImage;
+               
+               if ([self.delegate respondsToSelector:@selector(processImage:)]) {
+                       [self.delegate processImage:image];
+               }
+               
+               // check if matrix data pointer or dimensions were changed by the delegate
+               bool iOSimage = true;
+               if (height == image.rows && width == image.cols && format_opencv == image.type() && bufferAddress == image.data && bytesPerRow == image.step) {
+                       iOSimage = false;
+               }
+               
+               
+               // (create color space, create graphics context, render buffer)
+               CGBitmapInfo bitmapInfo;
+               
+               // basically we decide if it's a grayscale, rgb or rgba image
+               if (image.channels() == 1) {
+                       colorSpace = CGColorSpaceCreateDeviceGray();
+                       bitmapInfo = kCGImageAlphaNone;
+               } else if (image.channels() == 3) {
+                       colorSpace = CGColorSpaceCreateDeviceRGB();
+                       bitmapInfo = kCGImageAlphaNone;
+                       if (iOSimage) {
+                               bitmapInfo |= kCGBitmapByteOrder32Little;
+                       } else {
+                               bitmapInfo |= kCGBitmapByteOrder32Big;
+                       }
+               } else {
+                       colorSpace = CGColorSpaceCreateDeviceRGB();
+                       bitmapInfo = kCGImageAlphaPremultipliedFirst;
+                       if (iOSimage) {
+                               bitmapInfo |= kCGBitmapByteOrder32Little;
+                       } else {
+                               bitmapInfo |= kCGBitmapByteOrder32Big;
+                       }
+               }
+               
+               if (iOSimage) {
+                       context = CGBitmapContextCreate(bufferAddress, width, height, 8, bytesPerRow, colorSpace, bitmapInfo);
+                       dstImage = CGBitmapContextCreateImage(context);
+                       CGContextRelease(context);
+               } else {
+                       
+                       NSData *data = [NSData dataWithBytes:image.data length:image.elemSize()*image.total()];
+                       CGDataProviderRef provider = CGDataProviderCreateWithCFData((__bridge CFDataRef)data);
+                       
+                       // Creating CGImage from cv::Mat
+                       dstImage = CGImageCreate(image.cols,                                 // width
+                                                                        image.rows,                                 // height
+                                                                        8,                                          // bits per component
+                                                                        8 * image.elemSize(),                       // bits per pixel
+                                                                        image.step,                                 // bytesPerRow
+                                                                        colorSpace,                                 // colorspace
+                                                                        bitmapInfo,                                 // bitmap info
+                                                                        provider,                                   // CGDataProviderRef
+                                                                        NULL,                                       // decode
+                                                                        false,                                      // should interpolate
+                                                                        kCGRenderingIntentDefault                   // intent
+                                                                        );
+                       
+                       CGDataProviderRelease(provider);
+               }
+               
+               
+               
+               
+               // render buffer
+               dispatch_sync(dispatch_get_main_queue(), ^{
+                       self.customPreviewLayer.contents = (__bridge id)dstImage;
+               });
+               
+               
+               if (self.recordVideo == YES) {
+                       // a very dense way to keep track of the time at which this frame
+                       // occurs relative to the output stream, but it's just an example!
+                       
+                       // TODO reset frame number
+                       static int64_t frameNumber = 0;
+                       if (self.recordAssetWriterInput.readyForMoreMediaData) {
+                               [self.recordPixelBufferAdaptor appendPixelBuffer:imageBuffer
+                                               withPresentationTime:CMTimeMake(frameNumber, self.defaultFPS)];
+                       }
+                       frameNumber++;
+               }
+               
+               
+               // cleanup
+               CGImageRelease(dstImage);
+               
+               CGColorSpaceRelease(colorSpace);
+               
+               CVPixelBufferUnlockBaseAddress(imageBuffer, 0);
+       }
+}
+
+
+- (void)updateOrientation;
+{
+       NSLog(@"rotate..");
+       self.customPreviewLayer.bounds = CGRectMake(0, 0, self.parentView.frame.size.width, self.parentView.frame.size.height);
+       [self layoutPreviewLayer];
+}
+
+
+- (void)saveVideo;
+{
+    if (self.recordVideo == NO) {
+        return;
+    }
+    
+       ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init];
+    if ([library videoAtPathIsCompatibleWithSavedPhotosAlbum:[self tempFileURL]]) {
+        [library writeVideoAtPathToSavedPhotosAlbum:[self tempFileURL]
+                                    completionBlock:^(NSURL *assetURL, NSError *error){}];
+    }
+}
+
+
+- (NSURL *)tempFileURL;
+{
+    NSString *outputPath = [[NSString alloc] initWithFormat:@"%@%@", NSTemporaryDirectory(), @"output.mov"];
+    NSURL *outputURL = [NSURL fileURLWithPath:outputPath];
+    NSFileManager *fileManager = [NSFileManager defaultManager];
+    if ([fileManager fileExistsAtPath:outputPath]) {
+        NSLog(@"file exists");
+    }
+    return outputURL;
+}
+
+
+
+- (NSString *)tempFileString;
+{
+    NSString *outputPath = [[NSString alloc] initWithFormat:@"%@%@", NSTemporaryDirectory(), @"output.mov"];
+    return outputPath;
+}
+
+@end