Merge pull request #1263 from abidrahmank:pyCLAHE_24
[profile/ivi/opencv.git] / modules / highgui / src / cap_ios_abstract_camera.mm
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/highgui/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 }
154
155
156 #pragma mark - Public interface
157
158
159 - (void)start;
160 {
161     if (![NSThread isMainThread]) {
162         NSLog(@"[Camera] Warning: Call start only from main thread");
163         [self performSelectorOnMainThread:@selector(start) withObject:nil waitUntilDone:NO];
164         return;
165     }
166
167     if (running == YES) {
168         return;
169     }
170     running = YES;
171
172     // TOOD update image size data before actually starting (needed for recording)
173     [self updateSize];
174
175     if (cameraAvailable) {
176         [self startCaptureSession];
177     }
178 }
179
180
181 - (void)pause;
182 {
183     running = NO;
184     [self.captureSession stopRunning];
185 }
186
187
188
189 - (void)stop;
190 {
191     running = NO;
192
193     // Release any retained subviews of the main view.
194     // e.g. self.myOutlet = nil;
195
196     [self.captureSession stopRunning];
197     self.captureSession = nil;
198     self.captureVideoPreviewLayer = nil;
199     self.videoCaptureConnection = nil;
200     captureSessionLoaded = NO;
201 }
202
203
204
205 // use front/back camera
206 - (void)switchCameras;
207 {
208     BOOL was_running = self.running;
209     if (was_running) {
210         [self stop];
211     }
212     if (self.defaultAVCaptureDevicePosition == AVCaptureDevicePositionFront) {
213         self.defaultAVCaptureDevicePosition = AVCaptureDevicePositionBack;
214     } else {
215         self.defaultAVCaptureDevicePosition  = AVCaptureDevicePositionFront;
216     }
217     if (was_running) {
218         [self start];
219     }
220 }
221
222
223
224 #pragma mark - Device Orientation Changes
225
226
227 - (void)deviceOrientationDidChange:(NSNotification*)notification
228 {
229     UIDeviceOrientation orientation = [UIDevice currentDevice].orientation;
230
231     switch (orientation)
232     {
233         case UIDeviceOrientationPortrait:
234         case UIDeviceOrientationPortraitUpsideDown:
235         case UIDeviceOrientationLandscapeLeft:
236         case UIDeviceOrientationLandscapeRight:
237             currentDeviceOrientation = orientation;
238             break;
239
240         case UIDeviceOrientationFaceUp:
241         case UIDeviceOrientationFaceDown:
242         default:
243             break;
244     }
245     NSLog(@"deviceOrientationDidChange: %d", orientation);
246
247     [self updateOrientation];
248 }
249
250
251
252 #pragma mark - Private Interface
253
254 - (void)createCaptureSession;
255 {
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];
262     } else {
263         NSLog(@"[Camera] Error: could not set session preset");
264     }
265 }
266
267 - (void)createCaptureDevice;
268 {
269     // setup the device
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");
274 }
275
276
277 - (void)createVideoPreviewLayer;
278 {
279     self.captureVideoPreviewLayer = [[AVCaptureVideoPreviewLayer alloc] initWithSession:self.captureSession];
280
281     if ([self.captureVideoPreviewLayer isOrientationSupported]) {
282         [self.captureVideoPreviewLayer setOrientation:self.defaultAVCaptureVideoOrientation];
283     }
284
285     if (parentView != nil) {
286         self.captureVideoPreviewLayer.frame = self.parentView.bounds;
287         self.captureVideoPreviewLayer.videoGravity = AVLayerVideoGravityResizeAspectFill;
288         [self.parentView.layer addSublayer:self.captureVideoPreviewLayer];
289     }
290     NSLog(@"[Camera] created AVCaptureVideoPreviewLayer");
291 }
292
293
294
295
296 - (void)setDesiredCameraPosition:(AVCaptureDevicePosition)desiredPosition;
297 {
298     for (AVCaptureDevice *device in [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo]) {
299         if ([device position] == desiredPosition) {
300             [self.captureSession beginConfiguration];
301
302             NSError* error;
303             AVCaptureDeviceInput *input = [AVCaptureDeviceInput deviceInputWithDevice:device error:&error];
304             if (!input) {
305                 NSLog(@"error creating input %@", [error localizedDescription]);
306             }
307
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];
314                 } else {
315                     NSLog(@"unable to lock device for autofocos configuration %@", [error localizedDescription]);
316                 }
317             }
318             [self.captureSession addInput:input];
319
320             for (AVCaptureInput *oldInput in self.captureSession.inputs) {
321                 [self.captureSession removeInput:oldInput];
322             }
323             [self.captureSession addInput:input];
324             [self.captureSession commitConfiguration];
325
326             break;
327         }
328     }
329 }
330
331
332
333 - (void)startCaptureSession
334 {
335     if (!cameraAvailable) {
336         return;
337     }
338
339     if (self.captureSessionLoaded == NO) {
340         [self createCaptureSession];
341         [self createCaptureDevice];
342         [self createCaptureOutput];
343
344         // setup preview layer
345         if (self.useAVCaptureVideoPreviewLayer) {
346             [self createVideoPreviewLayer];
347         } else {
348             [self createCustomVideoPreview];
349         }
350
351         captureSessionLoaded = YES;
352     }
353
354     [self.captureSession startRunning];
355 }
356
357
358 - (void)createCaptureOutput;
359 {
360     [NSException raise:NSInternalInconsistencyException
361                 format:@"You must override %@ in a subclass", NSStringFromSelector(_cmd)];
362 }
363
364 - (void)createCustomVideoPreview;
365 {
366     [NSException raise:NSInternalInconsistencyException
367                 format:@"You must override %@ in a subclass", NSStringFromSelector(_cmd)];
368 }
369
370 - (void)updateOrientation;
371 {
372     // nothing to do here
373 }
374
375
376 - (void)updateSize;
377 {
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;
403     } else {
404         self.imageWidth = 640;
405         self.imageHeight = 480;
406     }
407 }
408
409 - (void)lockFocus;
410 {
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];
417         } else {
418             NSLog(@"unable to lock device for locked focus configuration %@", [error localizedDescription]);
419         }
420     }
421 }
422
423 - (void) unlockFocus;
424 {
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];
431         } else {
432             NSLog(@"unable to lock device for autofocus configuration %@", [error localizedDescription]);
433         }
434     }
435 }
436
437 - (void)lockExposure;
438 {
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];
445         } else {
446             NSLog(@"unable to lock device for locked exposure configuration %@", [error localizedDescription]);
447         }
448     }
449 }
450
451 - (void) unlockExposure;
452 {
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];
459         } else {
460             NSLog(@"unable to lock device for autoexposure configuration %@", [error localizedDescription]);
461         }
462     }
463 }
464
465 - (void)lockBalance;
466 {
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];
473         } else {
474             NSLog(@"unable to lock device for locked white balance configuration %@", [error localizedDescription]);
475         }
476     }
477 }
478
479 - (void) unlockBalance;
480 {
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];
487         } else {
488             NSLog(@"unable to lock device for auto white balance configuration %@", [error localizedDescription]);
489         }
490     }
491 }
492
493 @end
494