Home | History | Annotate | Download | only in src
      1 /*
      2  *  cap_ios_abstract_camera.mm
      3  *  For iOS video I/O
      4  *  by Eduard Feicho on 29/07/12
      5  *  by Alexander Shishkov on 17/07/13
      6  *  Copyright 2012. All rights reserved.
      7  *
      8  * Redistribution and use in source and binary forms, with or without
      9  * modification, are permitted provided that the following conditions are met:
     10  *
     11  * 1. Redistributions of source code must retain the above copyright notice,
     12  *    this list of conditions and the following disclaimer.
     13  * 2. Redistributions in binary form must reproduce the above copyright notice,
     14  *    this list of conditions and the following disclaimer in the documentation
     15  *    and/or other materials provided with the distribution.
     16  * 3. The name of the author may not be used to endorse or promote products
     17  *    derived from this software without specific prior written permission.
     18  *
     19  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR IMPLIED
     20  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
     21  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
     22  * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     23  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
     24  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
     25  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
     26  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
     27  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
     28  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     29  *
     30  */
     31 
     32 
     33 #import "opencv2/videoio/cap_ios.h"
     34 #include "precomp.hpp"
     35 
     36 #pragma mark - Private Interface
     37 
     38 @interface CvAbstractCamera ()
     39 
     40 @property (nonatomic, retain) AVCaptureVideoPreviewLayer* captureVideoPreviewLayer;
     41 
     42 - (void)deviceOrientationDidChange:(NSNotification*)notification;
     43 - (void)startCaptureSession;
     44 
     45 - (void)setDesiredCameraPosition:(AVCaptureDevicePosition)desiredPosition;
     46 
     47 - (void)updateSize;
     48 
     49 @end
     50 
     51 
     52 #pragma mark - Implementation
     53 
     54 
     55 @implementation CvAbstractCamera
     56 
     57 
     58 
     59 #pragma mark Public
     60 
     61 @synthesize imageWidth;
     62 @synthesize imageHeight;
     63 
     64 
     65 @synthesize defaultFPS;
     66 @synthesize defaultAVCaptureDevicePosition;
     67 @synthesize defaultAVCaptureVideoOrientation;
     68 @synthesize defaultAVCaptureSessionPreset;
     69 
     70 
     71 
     72 @synthesize captureSession;
     73 @synthesize captureVideoPreviewLayer;
     74 @synthesize videoCaptureConnection;
     75 @synthesize running;
     76 @synthesize captureSessionLoaded;
     77 @synthesize useAVCaptureVideoPreviewLayer;
     78 
     79 @synthesize parentView;
     80 
     81 #pragma mark - Constructors
     82 
     83 - (id)init;
     84 {
     85     self = [super init];
     86     if (self) {
     87         // react to device orientation notifications
     88         [[NSNotificationCenter defaultCenter] addObserver:self
     89                                                  selector:@selector(deviceOrientationDidChange:)
     90                                                      name:UIDeviceOrientationDidChangeNotification
     91                                                    object:nil];
     92         [[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
     93         currentDeviceOrientation = [[UIDevice currentDevice] orientation];
     94 
     95 
     96         // check if camera available
     97         cameraAvailable = [UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera];
     98         NSLog(@"camera available: %@", (cameraAvailable == YES ? @"YES" : @"NO") );
     99 
    100         running = NO;
    101 
    102         // set camera default configuration
    103         self.defaultAVCaptureDevicePosition = AVCaptureDevicePositionFront;
    104         self.defaultAVCaptureVideoOrientation = AVCaptureVideoOrientationLandscapeLeft;
    105         self.defaultFPS = 15;
    106         self.defaultAVCaptureSessionPreset = AVCaptureSessionPreset352x288;
    107 
    108         self.parentView = nil;
    109         self.useAVCaptureVideoPreviewLayer = NO;
    110     }
    111     return self;
    112 }
    113 
    114 
    115 
    116 - (id)initWithParentView:(UIView*)parent;
    117 {
    118     self = [super init];
    119     if (self) {
    120         // react to device orientation notifications
    121         [[NSNotificationCenter defaultCenter] addObserver:self
    122                                                  selector:@selector(deviceOrientationDidChange:)
    123                                                      name:UIDeviceOrientationDidChangeNotification
    124                                                    object:nil];
    125         [[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
    126         currentDeviceOrientation = [[UIDevice currentDevice] orientation];
    127 
    128 
    129         // check if camera available
    130         cameraAvailable = [UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera];
    131         NSLog(@"camera available: %@", (cameraAvailable == YES ? @"YES" : @"NO") );
    132 
    133         running = NO;
    134 
    135         // set camera default configuration
    136         self.defaultAVCaptureDevicePosition = AVCaptureDevicePositionFront;
    137         self.defaultAVCaptureVideoOrientation = AVCaptureVideoOrientationLandscapeLeft;
    138         self.defaultFPS = 15;
    139         self.defaultAVCaptureSessionPreset = AVCaptureSessionPreset640x480;
    140 
    141         self.parentView = parent;
    142         self.useAVCaptureVideoPreviewLayer = YES;
    143     }
    144     return self;
    145 }
    146 
    147 
    148 
    149 - (void)dealloc;
    150 {
    151     [[NSNotificationCenter defaultCenter] removeObserver:self];
    152     [[UIDevice currentDevice] endGeneratingDeviceOrientationNotifications];
    153     [super dealloc];
    154 }
    155 
    156 
    157 #pragma mark - Public interface
    158 
    159 
    160 - (void)start;
    161 {
    162     if (![NSThread isMainThread]) {
    163         NSLog(@"[Camera] Warning: Call start only from main thread");
    164         [self performSelectorOnMainThread:@selector(start) withObject:nil waitUntilDone:NO];
    165         return;
    166     }
    167 
    168     if (running == YES) {
    169         return;
    170     }
    171     running = YES;
    172 
    173     // TOOD update image size data before actually starting (needed for recording)
    174     [self updateSize];
    175 
    176     if (cameraAvailable) {
    177         [self startCaptureSession];
    178     }
    179 }
    180 
    181 
    182 - (void)pause;
    183 {
    184     running = NO;
    185     [self.captureSession stopRunning];
    186 }
    187 
    188 
    189 
    190 - (void)stop;
    191 {
    192     running = NO;
    193 
    194     // Release any retained subviews of the main view.
    195     // e.g. self.myOutlet = nil;
    196     for (AVCaptureInput *input in self.captureSession.inputs) {
    197         [self.captureSession removeInput:input];
    198     }
    199 
    200     for (AVCaptureOutput *output in self.captureSession.outputs) {
    201         [self.captureSession removeOutput:output];
    202     }
    203 
    204     [self.captureSession stopRunning];
    205     self.captureSession = nil;
    206     self.captureVideoPreviewLayer = nil;
    207     self.videoCaptureConnection = nil;
    208     captureSessionLoaded = NO;
    209 }
    210 
    211 
    212 
    213 // use front/back camera
    214 - (void)switchCameras;
    215 {
    216     BOOL was_running = self.running;
    217     if (was_running) {
    218         [self stop];
    219     }
    220     if (self.defaultAVCaptureDevicePosition == AVCaptureDevicePositionFront) {
    221         self.defaultAVCaptureDevicePosition = AVCaptureDevicePositionBack;
    222     } else {
    223         self.defaultAVCaptureDevicePosition  = AVCaptureDevicePositionFront;
    224     }
    225     if (was_running) {
    226         [self start];
    227     }
    228 }
    229 
    230 
    231 
    232 #pragma mark - Device Orientation Changes
    233 
    234 
    235 - (void)deviceOrientationDidChange:(NSNotification*)notification
    236 {
    237     (void)notification;
    238     UIDeviceOrientation orientation = [UIDevice currentDevice].orientation;
    239 
    240     switch (orientation)
    241     {
    242         case UIDeviceOrientationPortrait:
    243         case UIDeviceOrientationPortraitUpsideDown:
    244         case UIDeviceOrientationLandscapeLeft:
    245         case UIDeviceOrientationLandscapeRight:
    246             currentDeviceOrientation = orientation;
    247             break;
    248 
    249         case UIDeviceOrientationFaceUp:
    250         case UIDeviceOrientationFaceDown:
    251         default:
    252             break;
    253     }
    254     NSLog(@"deviceOrientationDidChange: %d", (int)orientation);
    255 
    256     [self updateOrientation];
    257 }
    258 
    259 
    260 
    261 #pragma mark - Private Interface
    262 
    263 - (void)createCaptureSession;
    264 {
    265     // set a av capture session preset
    266     self.captureSession = [[AVCaptureSession alloc] init];
    267     if ([self.captureSession canSetSessionPreset:self.defaultAVCaptureSessionPreset]) {
    268         [self.captureSession setSessionPreset:self.defaultAVCaptureSessionPreset];
    269     } else if ([self.captureSession canSetSessionPreset:AVCaptureSessionPresetLow]) {
    270         [self.captureSession setSessionPreset:AVCaptureSessionPresetLow];
    271     } else {
    272         NSLog(@"[Camera] Error: could not set session preset");
    273     }
    274 }
    275 
    276 - (void)createCaptureDevice;
    277 {
    278     // setup the device
    279     AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
    280     [self setDesiredCameraPosition:self.defaultAVCaptureDevicePosition];
    281     NSLog(@"[Camera] device connected? %@", device.connected ? @"YES" : @"NO");
    282     NSLog(@"[Camera] device position %@", (device.position == AVCaptureDevicePositionBack) ? @"back" : @"front");
    283 }
    284 
    285 
    286 - (void)createVideoPreviewLayer;
    287 {
    288     self.captureVideoPreviewLayer = [[AVCaptureVideoPreviewLayer alloc] initWithSession:self.captureSession];
    289 
    290     if ([self.captureVideoPreviewLayer respondsToSelector:@selector(connection)])
    291     {
    292         if ([self.captureVideoPreviewLayer.connection isVideoOrientationSupported])
    293         {
    294             [self.captureVideoPreviewLayer.connection setVideoOrientation:self.defaultAVCaptureVideoOrientation];
    295         }
    296     }
    297     else
    298     {
    299         // Deprecated in 6.0; here for backward compatibility
    300         if ([self.captureVideoPreviewLayer isOrientationSupported])
    301         {
    302             [self.captureVideoPreviewLayer setOrientation:self.defaultAVCaptureVideoOrientation];
    303         }
    304     }
    305 
    306     if (parentView != nil) {
    307         self.captureVideoPreviewLayer.frame = self.parentView.bounds;
    308         self.captureVideoPreviewLayer.videoGravity = AVLayerVideoGravityResizeAspectFill;
    309         [self.parentView.layer addSublayer:self.captureVideoPreviewLayer];
    310     }
    311     NSLog(@"[Camera] created AVCaptureVideoPreviewLayer");
    312 }
    313 
    314 - (void)setDesiredCameraPosition:(AVCaptureDevicePosition)desiredPosition;
    315 {
    316     for (AVCaptureDevice *device in [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo]) {
    317         if ([device position] == desiredPosition) {
    318             [self.captureSession beginConfiguration];
    319 
    320             NSError* error = nil;
    321             AVCaptureDeviceInput *input = [AVCaptureDeviceInput deviceInputWithDevice:device error:&error];
    322             if (!input) {
    323                 NSLog(@"error creating input %@", [error localizedDescription]);
    324             }
    325 
    326             // support for autofocus
    327             if ([device isFocusModeSupported:AVCaptureFocusModeContinuousAutoFocus]) {
    328                 error = nil;
    329                 if ([device lockForConfiguration:&error]) {
    330                     device.focusMode = AVCaptureFocusModeContinuousAutoFocus;
    331                     [device unlockForConfiguration];
    332                 } else {
    333                     NSLog(@"unable to lock device for autofocos configuration %@", [error localizedDescription]);
    334                 }
    335             }
    336             [self.captureSession addInput:input];
    337 
    338             for (AVCaptureInput *oldInput in self.captureSession.inputs) {
    339                 [self.captureSession removeInput:oldInput];
    340             }
    341             [self.captureSession addInput:input];
    342             [self.captureSession commitConfiguration];
    343 
    344             break;
    345         }
    346     }
    347 }
    348 
    349 
    350 
    351 - (void)startCaptureSession
    352 {
    353     if (!cameraAvailable) {
    354         return;
    355     }
    356 
    357     if (self.captureSessionLoaded == NO) {
    358         [self createCaptureSession];
    359         [self createCaptureDevice];
    360         [self createCaptureOutput];
    361 
    362         // setup preview layer
    363         if (self.useAVCaptureVideoPreviewLayer) {
    364             [self createVideoPreviewLayer];
    365         } else {
    366             [self createCustomVideoPreview];
    367         }
    368 
    369         captureSessionLoaded = YES;
    370     }
    371 
    372     [self.captureSession startRunning];
    373 }
    374 
    375 
    376 - (void)createCaptureOutput;
    377 {
    378     [NSException raise:NSInternalInconsistencyException
    379                 format:@"You must override %@ in a subclass", NSStringFromSelector(_cmd)];
    380 }
    381 
    382 - (void)createCustomVideoPreview;
    383 {
    384     [NSException raise:NSInternalInconsistencyException
    385                 format:@"You must override %@ in a subclass", NSStringFromSelector(_cmd)];
    386 }
    387 
    388 - (void)updateOrientation;
    389 {
    390     // nothing to do here
    391 }
    392 
    393 
    394 - (void)updateSize;
    395 {
    396     if ([self.defaultAVCaptureSessionPreset isEqualToString:AVCaptureSessionPresetPhoto]) {
    397         //TODO: find the correct resolution
    398         self.imageWidth = 640;
    399         self.imageHeight = 480;
    400     } else if ([self.defaultAVCaptureSessionPreset isEqualToString:AVCaptureSessionPresetHigh]) {
    401         //TODO: find the correct resolution
    402         self.imageWidth = 640;
    403         self.imageHeight = 480;
    404     } else if ([self.defaultAVCaptureSessionPreset isEqualToString:AVCaptureSessionPresetMedium]) {
    405         //TODO: find the correct resolution
    406         self.imageWidth = 640;
    407         self.imageHeight = 480;
    408     } else if ([self.defaultAVCaptureSessionPreset isEqualToString:AVCaptureSessionPresetLow]) {
    409         //TODO: find the correct resolution
    410         self.imageWidth = 640;
    411         self.imageHeight = 480;
    412     } else if ([self.defaultAVCaptureSessionPreset isEqualToString:AVCaptureSessionPreset352x288]) {
    413         self.imageWidth = 352;
    414         self.imageHeight = 288;
    415     } else if ([self.defaultAVCaptureSessionPreset isEqualToString:AVCaptureSessionPreset640x480]) {
    416         self.imageWidth = 640;
    417         self.imageHeight = 480;
    418     } else if ([self.defaultAVCaptureSessionPreset isEqualToString:AVCaptureSessionPreset1280x720]) {
    419         self.imageWidth = 1280;
    420         self.imageHeight = 720;
    421     } else {
    422         self.imageWidth = 640;
    423         self.imageHeight = 480;
    424     }
    425 }
    426 
    427 - (void)lockFocus;
    428 {
    429     AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
    430     if ([device isFocusModeSupported:AVCaptureFocusModeLocked]) {
    431         NSError *error = nil;
    432         if ([device lockForConfiguration:&error]) {
    433             device.focusMode = AVCaptureFocusModeLocked;
    434             [device unlockForConfiguration];
    435         } else {
    436             NSLog(@"unable to lock device for locked focus configuration %@", [error localizedDescription]);
    437         }
    438     }
    439 }
    440 
    441 - (void) unlockFocus;
    442 {
    443     AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
    444     if ([device isFocusModeSupported:AVCaptureFocusModeContinuousAutoFocus]) {
    445         NSError *error = nil;
    446         if ([device lockForConfiguration:&error]) {
    447             device.focusMode = AVCaptureFocusModeContinuousAutoFocus;
    448             [device unlockForConfiguration];
    449         } else {
    450             NSLog(@"unable to lock device for autofocus configuration %@", [error localizedDescription]);
    451         }
    452     }
    453 }
    454 
    455 - (void)lockExposure;
    456 {
    457     AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
    458     if ([device isExposureModeSupported:AVCaptureExposureModeLocked]) {
    459         NSError *error = nil;
    460         if ([device lockForConfiguration:&error]) {
    461             device.exposureMode = AVCaptureExposureModeLocked;
    462             [device unlockForConfiguration];
    463         } else {
    464             NSLog(@"unable to lock device for locked exposure configuration %@", [error localizedDescription]);
    465         }
    466     }
    467 }
    468 
    469 - (void) unlockExposure;
    470 {
    471     AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
    472     if ([device isExposureModeSupported:AVCaptureExposureModeContinuousAutoExposure]) {
    473         NSError *error = nil;
    474         if ([device lockForConfiguration:&error]) {
    475             device.exposureMode = AVCaptureExposureModeContinuousAutoExposure;
    476             [device unlockForConfiguration];
    477         } else {
    478             NSLog(@"unable to lock device for autoexposure configuration %@", [error localizedDescription]);
    479         }
    480     }
    481 }
    482 
    483 - (void)lockBalance;
    484 {
    485     AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
    486     if ([device isWhiteBalanceModeSupported:AVCaptureWhiteBalanceModeLocked]) {
    487         NSError *error = nil;
    488         if ([device lockForConfiguration:&error]) {
    489             device.whiteBalanceMode = AVCaptureWhiteBalanceModeLocked;
    490             [device unlockForConfiguration];
    491         } else {
    492             NSLog(@"unable to lock device for locked white balance configuration %@", [error localizedDescription]);
    493         }
    494     }
    495 }
    496 
    497 - (void) unlockBalance;
    498 {
    499     AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
    500     if ([device isWhiteBalanceModeSupported:AVCaptureWhiteBalanceModeContinuousAutoWhiteBalance]) {
    501         NSError *error = nil;
    502         if ([device lockForConfiguration:&error]) {
    503             device.whiteBalanceMode = AVCaptureWhiteBalanceModeContinuousAutoWhiteBalance;
    504             [device unlockForConfiguration];
    505         } else {
    506             NSLog(@"unable to lock device for auto white balance configuration %@", [error localizedDescription]);
    507         }
    508     }
    509 }
    510 
    511 @end
    512