mirror of https://github.com/opencv/opencv.git
Open Source Computer Vision Library
https://opencv.org/
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
445 lines
14 KiB
445 lines
14 KiB
// |
|
// CvAbstractCamera2.mm |
|
// |
|
// Created by Giles Payne on 2020/04/01. |
|
// |
|
|
|
#import "CvCamera2.h" |
|
|
|
#pragma mark - Private Interface |
|
|
|
@interface CvAbstractCamera2 () |
|
|
|
@property (nonatomic, strong) AVCaptureVideoPreviewLayer* captureVideoPreviewLayer; |
|
|
|
- (void)deviceOrientationDidChange:(NSNotification*)notification; |
|
- (void)startCaptureSession; |
|
|
|
- (void)setDesiredCameraPosition:(AVCaptureDevicePosition)desiredPosition; |
|
|
|
- (void)updateSize; |
|
|
|
@end |
|
|
|
|
|
#pragma mark - Implementation |
|
|
|
|
|
@implementation CvAbstractCamera2 |
|
|
|
#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]; |
|
self.currentDeviceOrientation = [[UIDevice currentDevice] orientation]; |
|
|
|
|
|
// check if camera available |
|
self.cameraAvailable = [UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]; |
|
NSLog(@"camera available: %@", (self.cameraAvailable ? @"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]; |
|
self.currentDeviceOrientation = [[UIDevice currentDevice] orientation]; |
|
|
|
|
|
// check if camera available |
|
self.cameraAvailable = [UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]; |
|
NSLog(@"camera available: %@", (self.cameraAvailable ? @"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 (self.running == YES) { |
|
return; |
|
} |
|
_running = YES; |
|
|
|
// TODO: update image size data before actually starting (needed for recording) |
|
[self updateSize]; |
|
|
|
if (self.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; |
|
if (self.captureSession) { |
|
for (AVCaptureInput *input in self.captureSession.inputs) { |
|
[self.captureSession removeInput:input]; |
|
} |
|
|
|
for (AVCaptureOutput *output in self.captureSession.outputs) { |
|
[self.captureSession removeOutput:output]; |
|
} |
|
|
|
[self.captureSession stopRunning]; |
|
} |
|
|
|
_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 |
|
{ |
|
(void)notification; |
|
UIDeviceOrientation orientation = [UIDevice currentDevice].orientation; |
|
|
|
switch (orientation) |
|
{ |
|
case UIDeviceOrientationPortrait: |
|
case UIDeviceOrientationPortraitUpsideDown: |
|
case UIDeviceOrientationLandscapeLeft: |
|
case UIDeviceOrientationLandscapeRight: |
|
self.currentDeviceOrientation = orientation; |
|
break; |
|
|
|
case UIDeviceOrientationFaceUp: |
|
case UIDeviceOrientationFaceDown: |
|
default: |
|
break; |
|
} |
|
NSLog(@"deviceOrientationDidChange: %d", (int)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.connection isVideoOrientationSupported]) |
|
{ |
|
[self.captureVideoPreviewLayer.connection setVideoOrientation:self.defaultAVCaptureVideoOrientation]; |
|
} |
|
|
|
if (self.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 = nil; |
|
AVCaptureDeviceInput *input = [AVCaptureDeviceInput deviceInputWithDevice:device error:&error]; |
|
if (!input) { |
|
NSLog(@"error creating input %@", [error description]); |
|
} |
|
|
|
// support for autofocus |
|
if ([device isFocusModeSupported:AVCaptureFocusModeContinuousAutoFocus]) { |
|
error = nil; |
|
if ([device lockForConfiguration:&error]) { |
|
device.focusMode = AVCaptureFocusModeContinuousAutoFocus; |
|
[device unlockForConfiguration]; |
|
} else { |
|
NSLog(@"unable to lock device for autofocus configuration %@", [error description]); |
|
} |
|
} |
|
[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 (!self.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 %s in a subclass", __FUNCTION__]; |
|
} |
|
|
|
- (void)createCustomVideoPreview; |
|
{ |
|
[NSException raise:NSInternalInconsistencyException |
|
format:@"You must override %s in a subclass", __FUNCTION__]; |
|
} |
|
|
|
- (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; |
|
} |
|
} |
|
|
|
- (void)lockFocus; |
|
{ |
|
AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo]; |
|
if ([device isFocusModeSupported:AVCaptureFocusModeLocked]) { |
|
NSError *error = nil; |
|
if ([device lockForConfiguration:&error]) { |
|
device.focusMode = AVCaptureFocusModeLocked; |
|
[device unlockForConfiguration]; |
|
} else { |
|
NSLog(@"unable to lock device for locked focus configuration %@", [error description]); |
|
} |
|
} |
|
} |
|
|
|
- (void) unlockFocus; |
|
{ |
|
AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo]; |
|
if ([device isFocusModeSupported:AVCaptureFocusModeContinuousAutoFocus]) { |
|
NSError *error = nil; |
|
if ([device lockForConfiguration:&error]) { |
|
device.focusMode = AVCaptureFocusModeContinuousAutoFocus; |
|
[device unlockForConfiguration]; |
|
} else { |
|
NSLog(@"unable to lock device for autofocus configuration %@", [error description]); |
|
} |
|
} |
|
} |
|
|
|
- (void)lockExposure; |
|
{ |
|
AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo]; |
|
if ([device isExposureModeSupported:AVCaptureExposureModeLocked]) { |
|
NSError *error = nil; |
|
if ([device lockForConfiguration:&error]) { |
|
device.exposureMode = AVCaptureExposureModeLocked; |
|
[device unlockForConfiguration]; |
|
} else { |
|
NSLog(@"unable to lock device for locked exposure configuration %@", [error description]); |
|
} |
|
} |
|
} |
|
|
|
- (void) unlockExposure; |
|
{ |
|
AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo]; |
|
if ([device isExposureModeSupported:AVCaptureExposureModeContinuousAutoExposure]) { |
|
NSError *error = nil; |
|
if ([device lockForConfiguration:&error]) { |
|
device.exposureMode = AVCaptureExposureModeContinuousAutoExposure; |
|
[device unlockForConfiguration]; |
|
} else { |
|
NSLog(@"unable to lock device for autoexposure configuration %@", [error description]); |
|
} |
|
} |
|
} |
|
|
|
- (void)lockBalance; |
|
{ |
|
AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo]; |
|
if ([device isWhiteBalanceModeSupported:AVCaptureWhiteBalanceModeLocked]) { |
|
NSError *error = nil; |
|
if ([device lockForConfiguration:&error]) { |
|
device.whiteBalanceMode = AVCaptureWhiteBalanceModeLocked; |
|
[device unlockForConfiguration]; |
|
} else { |
|
NSLog(@"unable to lock device for locked white balance configuration %@", [error description]); |
|
} |
|
} |
|
} |
|
|
|
- (void) unlockBalance; |
|
{ |
|
AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo]; |
|
if ([device isWhiteBalanceModeSupported:AVCaptureWhiteBalanceModeContinuousAutoWhiteBalance]) { |
|
NSError *error = nil; |
|
if ([device lockForConfiguration:&error]) { |
|
device.whiteBalanceMode = AVCaptureWhiteBalanceModeContinuousAutoWhiteBalance; |
|
[device unlockForConfiguration]; |
|
} else { |
|
NSLog(@"unable to lock device for auto white balance configuration %@", [error description]); |
|
} |
|
} |
|
} |
|
|
|
@end
|
|
|