• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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