2 * cap_ios_abstract_camera.mm
4 * by Eduard Feicho on 29/07/12
5 * by Alexander Shishkov on 17/07/13
6 * Copyright 2012. All rights reserved.
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions are met:
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.
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.
33 #import "opencv2/highgui/cap_ios.h"
34 #include "precomp.hpp"
36 #pragma mark - Private Interface
38 @interface CvAbstractCamera ()
40 @property (nonatomic, retain) AVCaptureVideoPreviewLayer* captureVideoPreviewLayer;
42 - (void)deviceOrientationDidChange:(NSNotification*)notification;
43 - (void)startCaptureSession;
45 - (void)setDesiredCameraPosition:(AVCaptureDevicePosition)desiredPosition;
52 #pragma mark - Implementation
55 @implementation CvAbstractCamera
61 @synthesize imageWidth;
62 @synthesize imageHeight;
65 @synthesize defaultFPS;
66 @synthesize defaultAVCaptureDevicePosition;
67 @synthesize defaultAVCaptureVideoOrientation;
68 @synthesize defaultAVCaptureSessionPreset;
72 @synthesize captureSession;
73 @synthesize captureVideoPreviewLayer;
74 @synthesize videoCaptureConnection;
76 @synthesize captureSessionLoaded;
77 @synthesize useAVCaptureVideoPreviewLayer;
79 @synthesize parentView;
81 #pragma mark - Constructors
87 // react to device orientation notifications
88 [[NSNotificationCenter defaultCenter] addObserver:self
89 selector:@selector(deviceOrientationDidChange:)
90 name:UIDeviceOrientationDidChangeNotification
92 [[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
93 currentDeviceOrientation = [[UIDevice currentDevice] orientation];
96 // check if camera available
97 cameraAvailable = [UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera];
98 NSLog(@"camera available: %@", (cameraAvailable == YES ? @"YES" : @"NO") );
102 // set camera default configuration
103 self.defaultAVCaptureDevicePosition = AVCaptureDevicePositionFront;
104 self.defaultAVCaptureVideoOrientation = AVCaptureVideoOrientationLandscapeLeft;
105 self.defaultFPS = 15;
106 self.defaultAVCaptureSessionPreset = AVCaptureSessionPreset352x288;
108 self.parentView = nil;
109 self.useAVCaptureVideoPreviewLayer = NO;
116 - (id)initWithParentView:(UIView*)parent;
120 // react to device orientation notifications
121 [[NSNotificationCenter defaultCenter] addObserver:self
122 selector:@selector(deviceOrientationDidChange:)
123 name:UIDeviceOrientationDidChangeNotification
125 [[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
126 currentDeviceOrientation = [[UIDevice currentDevice] orientation];
129 // check if camera available
130 cameraAvailable = [UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera];
131 NSLog(@"camera available: %@", (cameraAvailable == YES ? @"YES" : @"NO") );
135 // set camera default configuration
136 self.defaultAVCaptureDevicePosition = AVCaptureDevicePositionFront;
137 self.defaultAVCaptureVideoOrientation = AVCaptureVideoOrientationLandscapeLeft;
138 self.defaultFPS = 15;
139 self.defaultAVCaptureSessionPreset = AVCaptureSessionPreset640x480;
141 self.parentView = parent;
142 self.useAVCaptureVideoPreviewLayer = YES;
151 [[NSNotificationCenter defaultCenter] removeObserver:self];
152 [[UIDevice currentDevice] endGeneratingDeviceOrientationNotifications];
156 #pragma mark - Public interface
161 if (![NSThread isMainThread]) {
162 NSLog(@"[Camera] Warning: Call start only from main thread");
163 [self performSelectorOnMainThread:@selector(start) withObject:nil waitUntilDone:NO];
167 if (running == YES) {
172 // TOOD update image size data before actually starting (needed for recording)
175 if (cameraAvailable) {
176 [self startCaptureSession];
184 [self.captureSession stopRunning];
193 // Release any retained subviews of the main view.
194 // e.g. self.myOutlet = nil;
196 [self.captureSession stopRunning];
197 self.captureSession = nil;
198 self.captureVideoPreviewLayer = nil;
199 self.videoCaptureConnection = nil;
200 captureSessionLoaded = NO;
205 // use front/back camera
206 - (void)switchCameras;
208 BOOL was_running = self.running;
212 if (self.defaultAVCaptureDevicePosition == AVCaptureDevicePositionFront) {
213 self.defaultAVCaptureDevicePosition = AVCaptureDevicePositionBack;
215 self.defaultAVCaptureDevicePosition = AVCaptureDevicePositionFront;
224 #pragma mark - Device Orientation Changes
227 - (void)deviceOrientationDidChange:(NSNotification*)notification
229 UIDeviceOrientation orientation = [UIDevice currentDevice].orientation;
233 case UIDeviceOrientationPortrait:
234 case UIDeviceOrientationPortraitUpsideDown:
235 case UIDeviceOrientationLandscapeLeft:
236 case UIDeviceOrientationLandscapeRight:
237 currentDeviceOrientation = orientation;
240 case UIDeviceOrientationFaceUp:
241 case UIDeviceOrientationFaceDown:
245 NSLog(@"deviceOrientationDidChange: %d", orientation);
247 [self updateOrientation];
252 #pragma mark - Private Interface
254 - (void)createCaptureSession;
256 // set a av capture session preset
257 self.captureSession = [[AVCaptureSession alloc] init];
258 if ([self.captureSession canSetSessionPreset:self.defaultAVCaptureSessionPreset]) {
259 [self.captureSession setSessionPreset:self.defaultAVCaptureSessionPreset];
260 } else if ([self.captureSession canSetSessionPreset:AVCaptureSessionPresetLow]) {
261 [self.captureSession setSessionPreset:AVCaptureSessionPresetLow];
263 NSLog(@"[Camera] Error: could not set session preset");
267 - (void)createCaptureDevice;
270 AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
271 [self setDesiredCameraPosition:self.defaultAVCaptureDevicePosition];
272 NSLog(@"[Camera] device connected? %@", device.connected ? @"YES" : @"NO");
273 NSLog(@"[Camera] device position %@", (device.position == AVCaptureDevicePositionBack) ? @"back" : @"front");
277 - (void)createVideoPreviewLayer;
279 self.captureVideoPreviewLayer = [[AVCaptureVideoPreviewLayer alloc] initWithSession:self.captureSession];
281 if ([self.captureVideoPreviewLayer isOrientationSupported]) {
282 [self.captureVideoPreviewLayer setOrientation:self.defaultAVCaptureVideoOrientation];
285 if (parentView != nil) {
286 self.captureVideoPreviewLayer.frame = self.parentView.bounds;
287 self.captureVideoPreviewLayer.videoGravity = AVLayerVideoGravityResizeAspectFill;
288 [self.parentView.layer addSublayer:self.captureVideoPreviewLayer];
290 NSLog(@"[Camera] created AVCaptureVideoPreviewLayer");
296 - (void)setDesiredCameraPosition:(AVCaptureDevicePosition)desiredPosition;
298 for (AVCaptureDevice *device in [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo]) {
299 if ([device position] == desiredPosition) {
300 [self.captureSession beginConfiguration];
303 AVCaptureDeviceInput *input = [AVCaptureDeviceInput deviceInputWithDevice:device error:&error];
305 NSLog(@"error creating input %@", [error localizedDescription]);
308 // support for autofocus
309 if ([device isFocusModeSupported:AVCaptureFocusModeContinuousAutoFocus]) {
310 NSError *error = nil;
311 if ([device lockForConfiguration:&error]) {
312 device.focusMode = AVCaptureFocusModeContinuousAutoFocus;
313 [device unlockForConfiguration];
315 NSLog(@"unable to lock device for autofocos configuration %@", [error localizedDescription]);
318 [self.captureSession addInput:input];
320 for (AVCaptureInput *oldInput in self.captureSession.inputs) {
321 [self.captureSession removeInput:oldInput];
323 [self.captureSession addInput:input];
324 [self.captureSession commitConfiguration];
333 - (void)startCaptureSession
335 if (!cameraAvailable) {
339 if (self.captureSessionLoaded == NO) {
340 [self createCaptureSession];
341 [self createCaptureDevice];
342 [self createCaptureOutput];
344 // setup preview layer
345 if (self.useAVCaptureVideoPreviewLayer) {
346 [self createVideoPreviewLayer];
348 [self createCustomVideoPreview];
351 captureSessionLoaded = YES;
354 [self.captureSession startRunning];
358 - (void)createCaptureOutput;
360 [NSException raise:NSInternalInconsistencyException
361 format:@"You must override %@ in a subclass", NSStringFromSelector(_cmd)];
364 - (void)createCustomVideoPreview;
366 [NSException raise:NSInternalInconsistencyException
367 format:@"You must override %@ in a subclass", NSStringFromSelector(_cmd)];
370 - (void)updateOrientation;
372 // nothing to do here
378 if ([self.defaultAVCaptureSessionPreset isEqualToString:AVCaptureSessionPresetPhoto]) {
379 //TODO: find the correct resolution
380 self.imageWidth = 640;
381 self.imageHeight = 480;
382 } else if ([self.defaultAVCaptureSessionPreset isEqualToString:AVCaptureSessionPresetHigh]) {
383 //TODO: find the correct resolution
384 self.imageWidth = 640;
385 self.imageHeight = 480;
386 } else if ([self.defaultAVCaptureSessionPreset isEqualToString:AVCaptureSessionPresetMedium]) {
387 //TODO: find the correct resolution
388 self.imageWidth = 640;
389 self.imageHeight = 480;
390 } else if ([self.defaultAVCaptureSessionPreset isEqualToString:AVCaptureSessionPresetLow]) {
391 //TODO: find the correct resolution
392 self.imageWidth = 640;
393 self.imageHeight = 480;
394 } else if ([self.defaultAVCaptureSessionPreset isEqualToString:AVCaptureSessionPreset352x288]) {
395 self.imageWidth = 352;
396 self.imageHeight = 288;
397 } else if ([self.defaultAVCaptureSessionPreset isEqualToString:AVCaptureSessionPreset640x480]) {
398 self.imageWidth = 640;
399 self.imageHeight = 480;
400 } else if ([self.defaultAVCaptureSessionPreset isEqualToString:AVCaptureSessionPreset1280x720]) {
401 self.imageWidth = 1280;
402 self.imageHeight = 720;
404 self.imageWidth = 640;
405 self.imageHeight = 480;
411 AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
412 if ([device isFocusModeSupported:AVCaptureFocusModeLocked]) {
413 NSError *error = nil;
414 if ([device lockForConfiguration:&error]) {
415 device.focusMode = AVCaptureFocusModeLocked;
416 [device unlockForConfiguration];
418 NSLog(@"unable to lock device for locked focus configuration %@", [error localizedDescription]);
423 - (void) unlockFocus;
425 AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
426 if ([device isFocusModeSupported:AVCaptureFocusModeContinuousAutoFocus]) {
427 NSError *error = nil;
428 if ([device lockForConfiguration:&error]) {
429 device.focusMode = AVCaptureFocusModeContinuousAutoFocus;
430 [device unlockForConfiguration];
432 NSLog(@"unable to lock device for autofocus configuration %@", [error localizedDescription]);
437 - (void)lockExposure;
439 AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
440 if ([device isExposureModeSupported:AVCaptureExposureModeLocked]) {
441 NSError *error = nil;
442 if ([device lockForConfiguration:&error]) {
443 device.exposureMode = AVCaptureExposureModeLocked;
444 [device unlockForConfiguration];
446 NSLog(@"unable to lock device for locked exposure configuration %@", [error localizedDescription]);
451 - (void) unlockExposure;
453 AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
454 if ([device isExposureModeSupported:AVCaptureExposureModeContinuousAutoExposure]) {
455 NSError *error = nil;
456 if ([device lockForConfiguration:&error]) {
457 device.exposureMode = AVCaptureExposureModeContinuousAutoExposure;
458 [device unlockForConfiguration];
460 NSLog(@"unable to lock device for autoexposure configuration %@", [error localizedDescription]);
467 AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
468 if ([device isWhiteBalanceModeSupported:AVCaptureWhiteBalanceModeLocked]) {
469 NSError *error = nil;
470 if ([device lockForConfiguration:&error]) {
471 device.whiteBalanceMode = AVCaptureWhiteBalanceModeLocked;
472 [device unlockForConfiguration];
474 NSLog(@"unable to lock device for locked white balance configuration %@", [error localizedDescription]);
479 - (void) unlockBalance;
481 AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
482 if ([device isWhiteBalanceModeSupported:AVCaptureWhiteBalanceModeContinuousAutoWhiteBalance]) {
483 NSError *error = nil;
484 if ([device lockForConfiguration:&error]) {
485 device.whiteBalanceMode = AVCaptureWhiteBalanceModeContinuousAutoWhiteBalance;
486 [device unlockForConfiguration];
488 NSLog(@"unable to lock device for auto white balance configuration %@", [error localizedDescription]);