/WHAMBUSH/PBJVision/Source/PBJVision.m
Objective C | 1401 lines | 1029 code | 281 blank | 91 comment | 237 complexity | 0c954ee4977abf3f477e3bd4020d55fe MD5 | raw file
- //
- // PBJVision.m
- // PBJVision
- //
- // Created by Patrick Piemonte on 4/30/13.
- // Copyright (c) 2013-present, Patrick Piemonte, http://patrickpiemonte.com
- //
- // Permission is hereby granted, free of charge, to any person obtaining a copy of
- // this software and associated documentation files (the "Software"), to deal in
- // the Software without restriction, including without limitation the rights to
- // use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
- // the Software, and to permit persons to whom the Software is furnished to do so,
- // subject to the following conditions:
- //
- // The above copyright notice and this permission notice shall be included in all
- // copies or substantial portions of the Software.
- //
- // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
- // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
- // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
- // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
- // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
- //
- #import "PBJVision.h"
- #import "PBJVisionUtilities.h"
- #import "PBJMediaWriter.h"
- #import "PBJGLProgram.h"
- #import <CoreImage/CoreImage.h>
- #import <ImageIO/ImageIO.h>
- #import <OpenGLES/EAGL.h>
- #define LOG_VISION 1
- #ifndef DLog
- #if !defined(NDEBUG) && LOG_VISION
- # define DLog(fmt, ...) NSLog((@"VISION: " fmt), ##__VA_ARGS__);
- #else
- # define DLog(...)
- #endif
- #endif
- NSString * const PBJVisionErrorDomain = @"PBJVisionErrorDomain";
- static uint64_t const PBJVisionRequiredMinimumDiskSpaceInBytes = 49999872; // ~ 47 MB
- static CGFloat const PBJVisionThumbnailWidth = 160.0f;
- // KVO contexts
- static NSString * const PBJVisionFocusObserverContext = @"PBJVisionFocusObserverContext";
- static NSString * const PBJVisionExposureObserverContext = @"PBJVisionExposureObserverContext";
- static NSString * const PBJVisionWhiteBalanceObserverContext = @"PBJVisionWhiteBalanceObserverContext";
- static NSString * const PBJVisionFlashModeObserverContext = @"PBJVisionFlashModeObserverContext";
- static NSString * const PBJVisionTorchModeObserverContext = @"PBJVisionTorchModeObserverContext";
- static NSString * const PBJVisionFlashAvailabilityObserverContext = @"PBJVisionFlashAvailabilityObserverContext";
- static NSString * const PBJVisionTorchAvailabilityObserverContext = @"PBJVisionTorchAvailabilityObserverContext";
- static NSString * const PBJVisionCaptureStillImageIsCapturingStillImageObserverContext = @"PBJVisionCaptureStillImageIsCapturingStillImageObserverContext";
- // photo dictionary key definitions
- NSString * const PBJVisionPhotoMetadataKey = @"PBJVisionPhotoMetadataKey";
- NSString * const PBJVisionPhotoJPEGKey = @"PBJVisionPhotoJPEGKey";
- NSString * const PBJVisionPhotoImageKey = @"PBJVisionPhotoImageKey";
- NSString * const PBJVisionPhotoThumbnailKey = @"PBJVisionPhotoThumbnailKey";
- // video dictionary key definitions
- NSString * const PBJVisionVideoPathKey = @"PBJVisionVideoPathKey";
- NSString * const PBJVisionVideoThumbnailKey = @"PBJVisionVideoThumbnailKey";
- NSString * const PBJVisionVideoThumbnailArrayKey = @"PBJVisionVideoThumbnailArrayKey";
- NSString * const PBJVisionVideoCapturedDurationKey = @"PBJVisionVideoCapturedDurationKey";
- // PBJGLProgram shader uniforms for pixel format conversion on the GPU
- typedef NS_ENUM(GLint, PBJVisionUniformLocationTypes)
- {
- PBJVisionUniformY,
- PBJVisionUniformUV,
- PBJVisionUniformCount
- };
- ///
- @interface PBJVision () <
- AVCaptureAudioDataOutputSampleBufferDelegate,
- AVCaptureVideoDataOutputSampleBufferDelegate,
- PBJMediaWriterDelegate>
- {
- // AV
- AVCaptureSession *_captureSession;
-
- AVCaptureDevice *_captureDeviceFront;
- AVCaptureDevice *_captureDeviceBack;
- AVCaptureDevice *_captureDeviceAudio;
-
- AVCaptureDeviceInput *_captureDeviceInputFront;
- AVCaptureDeviceInput *_captureDeviceInputBack;
- AVCaptureDeviceInput *_captureDeviceInputAudio;
- AVCaptureStillImageOutput *_captureOutputPhoto;
- AVCaptureAudioDataOutput *_captureOutputAudio;
- AVCaptureVideoDataOutput *_captureOutputVideo;
- // vision core
- PBJMediaWriter *_mediaWriter;
- dispatch_queue_t _captureSessionDispatchQueue;
- dispatch_queue_t _captureCaptureDispatchQueue;
- PBJCameraDevice _cameraDevice;
- PBJCameraMode _cameraMode;
- PBJCameraOrientation _cameraOrientation;
- PBJCameraOrientation _previewOrientation;
- BOOL _autoUpdatePreviewOrientation;
- BOOL _autoFreezePreviewDuringCapture;
- BOOL _usesApplicationAudioSession;
- PBJFocusMode _focusMode;
- PBJExposureMode _exposureMode;
- PBJFlashMode _flashMode;
- PBJMirroringMode _mirroringMode;
- NSString *_captureSessionPreset;
- NSString *_captureDirectory;
- PBJOutputFormat _outputFormat;
- NSMutableSet* _captureThumbnailTimes;
- NSMutableSet* _captureThumbnailFrames;
-
- CGFloat _videoBitRate;
- NSInteger _audioBitRate;
- NSInteger _videoFrameRate;
- NSDictionary *_additionalCompressionProperties;
-
- AVCaptureDevice *_currentDevice;
- AVCaptureDeviceInput *_currentInput;
- AVCaptureOutput *_currentOutput;
-
- AVCaptureVideoPreviewLayer *_previewLayer;
- CGRect _cleanAperture;
- CMTime _startTimestamp;
- CMTime _timeOffset;
- CMTime _maximumCaptureDuration;
- // sample buffer rendering
- PBJCameraDevice _bufferDevice;
- PBJCameraOrientation _bufferOrientation;
- size_t _bufferWidth;
- size_t _bufferHeight;
- CGRect _presentationFrame;
- EAGLContext *_context;
- PBJGLProgram *_program;
- CVOpenGLESTextureRef _lumaTexture;
- CVOpenGLESTextureRef _chromaTexture;
- CVOpenGLESTextureCacheRef _videoTextureCache;
-
- CIContext *_ciContext;
-
- // flags
-
- struct {
- unsigned int previewRunning:1;
- unsigned int changingModes:1;
- unsigned int recording:1;
- unsigned int paused:1;
- unsigned int interrupted:1;
- unsigned int videoWritten:1;
- unsigned int videoRenderingEnabled:1;
- unsigned int audioCaptureEnabled:1;
- unsigned int thumbnailEnabled:1;
- unsigned int defaultVideoThumbnails:1;
- unsigned int videoCaptureFrame:1;
- } __block _flags;
- }
- @property (nonatomic) AVCaptureDevice *currentDevice;
- @end
- @implementation PBJVision
- @synthesize delegate = _delegate;
- @synthesize currentDevice = _currentDevice;
- @synthesize previewLayer = _previewLayer;
- @synthesize cleanAperture = _cleanAperture;
- @synthesize cameraOrientation = _cameraOrientation;
- @synthesize previewOrientation = _previewOrientation;
- @synthesize autoUpdatePreviewOrientation = _autoUpdatePreviewOrientation;
- @synthesize autoFreezePreviewDuringCapture = _autoFreezePreviewDuringCapture;
- @synthesize usesApplicationAudioSession = _usesApplicationAudioSession;
- @synthesize cameraDevice = _cameraDevice;
- @synthesize cameraMode = _cameraMode;
- @synthesize focusMode = _focusMode;
- @synthesize exposureMode = _exposureMode;
- @synthesize flashMode = _flashMode;
- @synthesize mirroringMode = _mirroringMode;
- @synthesize outputFormat = _outputFormat;
- @synthesize context = _context;
- @synthesize presentationFrame = _presentationFrame;
- @synthesize captureSessionPreset = _captureSessionPreset;
- @synthesize captureDirectory = _captureDirectory;
- @synthesize audioBitRate = _audioBitRate;
- @synthesize videoBitRate = _videoBitRate;
- @synthesize additionalCompressionProperties = _additionalCompressionProperties;
- @synthesize maximumCaptureDuration = _maximumCaptureDuration;
- #pragma mark - singleton
- + (PBJVision *)sharedInstance
- {
- static PBJVision *singleton = nil;
- static dispatch_once_t once = 0;
- dispatch_once(&once, ^{
- singleton = [[PBJVision alloc] init];
- });
- return singleton;
- }
- #pragma mark - getters/setters
- - (BOOL)isCaptureSessionActive
- {
- return ([_captureSession isRunning]);
- }
- - (BOOL)isRecording
- {
- return _flags.recording;
- }
- - (BOOL)isPaused
- {
- return _flags.paused;
- }
- - (void)setVideoRenderingEnabled:(BOOL)videoRenderingEnabled
- {
- _flags.videoRenderingEnabled = (unsigned int)videoRenderingEnabled;
- }
- - (BOOL)isVideoRenderingEnabled
- {
- return _flags.videoRenderingEnabled;
- }
- - (void)setAudioCaptureEnabled:(BOOL)audioCaptureEnabled
- {
- _flags.audioCaptureEnabled = (unsigned int)audioCaptureEnabled;
- }
- - (BOOL)isAudioCaptureEnabled
- {
- return _flags.audioCaptureEnabled;
- }
- - (void)setThumbnailEnabled:(BOOL)thumbnailEnabled
- {
- _flags.thumbnailEnabled = (unsigned int)thumbnailEnabled;
- }
- - (BOOL)thumbnailEnabled
- {
- return _flags.thumbnailEnabled;
- }
- - (void)setDefaultVideoThumbnails:(BOOL)defaultVideoThumbnails
- {
- _flags.defaultVideoThumbnails = (unsigned int)defaultVideoThumbnails;
- }
- - (BOOL)defaultVideoThumbnails
- {
- return _flags.defaultVideoThumbnails;
- }
- - (Float64)capturedAudioSeconds
- {
- if (_mediaWriter && CMTIME_IS_VALID(_mediaWriter.audioTimestamp)) {
- return CMTimeGetSeconds(CMTimeSubtract(_mediaWriter.audioTimestamp, _startTimestamp));
- } else {
- return 0.0;
- }
- }
- - (Float64)capturedVideoSeconds
- {
- if (_mediaWriter && CMTIME_IS_VALID(_mediaWriter.videoTimestamp)) {
- return CMTimeGetSeconds(CMTimeSubtract(_mediaWriter.videoTimestamp, _startTimestamp));
- } else {
- return 0.0;
- }
- }
- - (void)setCameraOrientation:(PBJCameraOrientation)cameraOrientation
- {
- if (cameraOrientation == _cameraOrientation)
- return;
- _cameraOrientation = cameraOrientation;
- if (self.autoUpdatePreviewOrientation) {
- [self setPreviewOrientation:cameraOrientation];
- }
- }
- - (void)setPreviewOrientation:(PBJCameraOrientation)previewOrientation {
- if (previewOrientation == _previewOrientation)
- return;
- if ([_previewLayer.connection isVideoOrientationSupported]) {
- _previewOrientation = previewOrientation;
- [self _setOrientationForConnection:_previewLayer.connection];
- }
- }
- - (void)_setOrientationForConnection:(AVCaptureConnection *)connection
- {
- if (!connection || ![connection isVideoOrientationSupported])
- return;
- AVCaptureVideoOrientation orientation = AVCaptureVideoOrientationPortrait;
- switch (_cameraOrientation) {
- case PBJCameraOrientationPortraitUpsideDown:
- orientation = AVCaptureVideoOrientationPortraitUpsideDown;
- break;
- case PBJCameraOrientationLandscapeRight:
- orientation = AVCaptureVideoOrientationLandscapeRight;
- break;
- case PBJCameraOrientationLandscapeLeft:
- orientation = AVCaptureVideoOrientationLandscapeLeft;
- break;
- case PBJCameraOrientationPortrait:
- default:
- break;
- }
- [connection setVideoOrientation:orientation];
- }
- - (void)_setCameraMode:(PBJCameraMode)cameraMode cameraDevice:(PBJCameraDevice)cameraDevice outputFormat:(PBJOutputFormat)outputFormat
- {
- BOOL changeDevice = (_cameraDevice != cameraDevice);
- BOOL changeMode = (_cameraMode != cameraMode);
- BOOL changeOutputFormat = (_outputFormat != outputFormat);
-
- DLog(@"change device (%d) mode (%d) format (%d)", changeDevice, changeMode, changeOutputFormat);
-
- if (!changeMode && !changeDevice && !changeOutputFormat) {
- return;
- }
- if (changeDevice && [_delegate respondsToSelector:@selector(visionCameraDeviceWillChange:)]) {
- #pragma clang diagnostic push
- #pragma clang diagnostic ignored "-Warc-performSelector-leaks"
- [_delegate performSelector:@selector(visionCameraDeviceWillChange:) withObject:self];
- #pragma clang diagnostic pop
- }
- if (changeMode && [_delegate respondsToSelector:@selector(visionCameraModeWillChange:)]) {
- #pragma clang diagnostic push
- #pragma clang diagnostic ignored "-Warc-performSelector-leaks"
- [_delegate performSelector:@selector(visionCameraModeWillChange:) withObject:self];
- #pragma clang diagnostic pop
- }
- if (changeOutputFormat && [_delegate respondsToSelector:@selector(visionOutputFormatWillChange:)]) {
- #pragma clang diagnostic push
- #pragma clang diagnostic ignored "-Warc-performSelector-leaks"
- [_delegate performSelector:@selector(visionOutputFormatWillChange:) withObject:self];
- #pragma clang diagnostic pop
- }
-
- _flags.changingModes = YES;
-
- _cameraDevice = cameraDevice;
- _cameraMode = cameraMode;
- _outputFormat = outputFormat;
- PBJVisionBlock didChangeBlock = ^{
- _flags.changingModes = NO;
-
- if (changeDevice && [_delegate respondsToSelector:@selector(visionCameraDeviceDidChange:)]) {
- #pragma clang diagnostic push
- #pragma clang diagnostic ignored "-Warc-performSelector-leaks"
- [_delegate performSelector:@selector(visionCameraDeviceDidChange:) withObject:self];
- #pragma clang diagnostic pop
- }
- if (changeMode && [_delegate respondsToSelector:@selector(visionCameraModeDidChange:)]) {
- #pragma clang diagnostic push
- #pragma clang diagnostic ignored "-Warc-performSelector-leaks"
- [_delegate performSelector:@selector(visionCameraModeDidChange:) withObject:self];
- #pragma clang diagnostic pop
- }
- if (changeOutputFormat && [_delegate respondsToSelector:@selector(visionOutputFormatDidChange:)]) {
- #pragma clang diagnostic push
- #pragma clang diagnostic ignored "-Warc-performSelector-leaks"
- [_delegate performSelector:@selector(visionOutputFormatDidChange:) withObject:self];
- #pragma clang diagnostic pop
- }
- };
- // since there is no session in progress, set and bail
- if (!_captureSession) {
- _flags.changingModes = NO;
-
- didChangeBlock();
-
- return;
- }
-
- [self _enqueueBlockOnCaptureSessionQueue:^{
- // camera is already setup, no need to call _setupCamera
- [self _setupSession];
- [self setMirroringMode:_mirroringMode];
-
- [self _enqueueBlockOnMainQueue:didChangeBlock];
- }];
- }
- - (void)setCameraDevice:(PBJCameraDevice)cameraDevice
- {
- [self _setCameraMode:_cameraMode cameraDevice:cameraDevice outputFormat:_outputFormat];
- }
- - (void)setCaptureSessionPreset:(NSString *)captureSessionPreset
- {
- _captureSessionPreset = captureSessionPreset;
- if ([_captureSession canSetSessionPreset:captureSessionPreset]){
- [self _commitBlock:^{
- [_captureSession setSessionPreset:captureSessionPreset];
- }];
- }
- }
- - (void)setCameraMode:(PBJCameraMode)cameraMode
- {
- [self _setCameraMode:cameraMode cameraDevice:_cameraDevice outputFormat:_outputFormat];
- }
- - (void)setOutputFormat:(PBJOutputFormat)outputFormat
- {
- [self _setCameraMode:_cameraMode cameraDevice:_cameraDevice outputFormat:outputFormat];
- }
- - (BOOL)isCameraDeviceAvailable:(PBJCameraDevice)cameraDevice
- {
- return [UIImagePickerController isCameraDeviceAvailable:(UIImagePickerControllerCameraDevice)cameraDevice];
- }
- - (BOOL)isFocusPointOfInterestSupported
- {
- return [_currentDevice isFocusPointOfInterestSupported];
- }
- - (BOOL)isFocusLockSupported
- {
- return [_currentDevice isFocusModeSupported:AVCaptureFocusModeLocked];
- }
- - (void)setFocusMode:(PBJFocusMode)focusMode
- {
- BOOL shouldChangeFocusMode = (_focusMode != focusMode);
- if (![_currentDevice isFocusModeSupported:(AVCaptureFocusMode)focusMode] || !shouldChangeFocusMode)
- return;
-
- _focusMode = focusMode;
-
- NSError *error = nil;
- if (_currentDevice && [_currentDevice lockForConfiguration:&error]) {
- [_currentDevice setFocusMode:(AVCaptureFocusMode)focusMode];
- [_currentDevice unlockForConfiguration];
- } else if (error) {
- DLog(@"error locking device for focus mode change (%@)", error);
- }
- }
- - (BOOL)isExposureLockSupported
- {
- return [_currentDevice isExposureModeSupported:AVCaptureExposureModeLocked];
- }
- - (void)setExposureMode:(PBJExposureMode)exposureMode
- {
- BOOL shouldChangeExposureMode = (_exposureMode != exposureMode);
- if (![_currentDevice isExposureModeSupported:(AVCaptureExposureMode)exposureMode] || !shouldChangeExposureMode)
- return;
-
- _exposureMode = exposureMode;
-
- NSError *error = nil;
- if (_currentDevice && [_currentDevice lockForConfiguration:&error]) {
- [_currentDevice setExposureMode:(AVCaptureExposureMode)exposureMode];
- [_currentDevice unlockForConfiguration];
- } else if (error) {
- DLog(@"error locking device for exposure mode change (%@)", error);
- }
- }
- - (BOOL)isFlashAvailable
- {
- return (_currentDevice && [_currentDevice hasFlash]);
- }
- - (void)setFlashMode:(PBJFlashMode)flashMode
- {
- BOOL shouldChangeFlashMode = (_flashMode != flashMode);
- if (![_currentDevice hasFlash] || !shouldChangeFlashMode)
- return;
- _flashMode = flashMode;
-
- NSError *error = nil;
- if (_currentDevice && [_currentDevice lockForConfiguration:&error]) {
-
- switch (_cameraMode) {
- case PBJCameraModePhoto:
- {
- if ([_currentDevice isFlashModeSupported:(AVCaptureFlashMode)_flashMode]) {
- [_currentDevice setFlashMode:(AVCaptureFlashMode)_flashMode];
- }
- break;
- }
- case PBJCameraModeVideo:
- {
- if ([_currentDevice isFlashModeSupported:(AVCaptureFlashMode)_flashMode]) {
- [_currentDevice setFlashMode:AVCaptureFlashModeOff];
- }
-
- if ([_currentDevice isTorchModeSupported:(AVCaptureTorchMode)_flashMode]) {
- [_currentDevice setTorchMode:(AVCaptureTorchMode)_flashMode];
- }
- break;
- }
- default:
- break;
- }
-
- [_currentDevice unlockForConfiguration];
-
- } else if (error) {
- DLog(@"error locking device for flash mode change (%@)", error);
- }
- }
- // framerate
- - (void)setVideoFrameRate:(NSInteger)videoFrameRate
- {
- if (![self supportsVideoFrameRate:videoFrameRate]) {
- DLog(@"frame rate range not supported for current device format");
- return;
- }
-
- BOOL isRecording = _flags.recording;
- if (isRecording) {
- [self pauseVideoCapture];
- }
- CMTime fps = CMTimeMake(1, (int32_t)videoFrameRate);
- AVCaptureDevice *videoDevice = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
- AVCaptureDeviceFormat *supportingFormat = nil;
- int32_t maxWidth = 0;
- NSArray *formats = [videoDevice formats];
- for (AVCaptureDeviceFormat *format in formats) {
- NSArray *videoSupportedFrameRateRanges = format.videoSupportedFrameRateRanges;
- for (AVFrameRateRange *range in videoSupportedFrameRateRanges) {
- CMFormatDescriptionRef desc = format.formatDescription;
- CMVideoDimensions dimensions = CMVideoFormatDescriptionGetDimensions(desc);
- int32_t width = dimensions.width;
- if (range.minFrameRate <= videoFrameRate && videoFrameRate <= range.maxFrameRate && width >= maxWidth) {
- supportingFormat = format;
- maxWidth = width;
- }
-
- }
- }
-
- if (supportingFormat) {
- NSError *error = nil;
- [_captureSession beginConfiguration]; // the session to which the receiver's AVCaptureDeviceInput is added.
- if ([_currentDevice lockForConfiguration:&error]) {
- [_currentDevice setActiveFormat:supportingFormat];
- _currentDevice.activeVideoMinFrameDuration = fps;
- _currentDevice.activeVideoMaxFrameDuration = fps;
- _videoFrameRate = videoFrameRate;
- [_currentDevice unlockForConfiguration];
- } else if (error) {
- DLog(@"error locking device for frame rate change (%@)", error);
- }
- }
- [_captureSession commitConfiguration];
- [self _enqueueBlockOnMainQueue:^{
- if ([_delegate respondsToSelector:@selector(visionDidChangeVideoFormatAndFrameRate:)])
- [_delegate visionDidChangeVideoFormatAndFrameRate:self];
- }];
- if (isRecording) {
- [self resumeVideoCapture];
- }
- }
- - (NSInteger)videoFrameRate
- {
- if (!_currentDevice)
- return 0;
- return _currentDevice.activeVideoMaxFrameDuration.timescale;
- }
- - (BOOL)supportsVideoFrameRate:(NSInteger)videoFrameRate
- {
- AVCaptureDevice *videoDevice = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
- NSArray *formats = [videoDevice formats];
- for (AVCaptureDeviceFormat *format in formats) {
- NSArray *videoSupportedFrameRateRanges = [format videoSupportedFrameRateRanges];
- for (AVFrameRateRange *frameRateRange in videoSupportedFrameRateRanges) {
- if ( (frameRateRange.minFrameRate <= videoFrameRate) && (videoFrameRate <= frameRateRange.maxFrameRate) ) {
- return YES;
- }
- }
- }
- return NO;
- }
- #pragma mark - init
- - (id)init
- {
- self = [super init];
- if (self) {
-
- // setup GLES
- _context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
- if (!_context) {
- DLog(@"failed to create GL context");
- }
- [self _setupGL];
-
- _captureSessionPreset = AVCaptureSessionPresetMedium;
- _captureDirectory = nil;
- _autoUpdatePreviewOrientation = YES;
- _autoFreezePreviewDuringCapture = YES;
- _usesApplicationAudioSession = NO;
- // Average bytes per second based on video dimensions
- // lower the bitRate, higher the compression
- _videoBitRate = PBJVideoBitRate640x480;
- // default audio/video configuration
- _audioBitRate = 64000;
-
- // default flags
- _flags.thumbnailEnabled = YES;
- _flags.defaultVideoThumbnails = YES;
- _flags.audioCaptureEnabled = YES;
- // setup queues
- _captureSessionDispatchQueue = dispatch_queue_create("PBJVisionSession", DISPATCH_QUEUE_SERIAL); // protects session
- _captureCaptureDispatchQueue = dispatch_queue_create("PBJVisionCapture", DISPATCH_QUEUE_SERIAL); // protects capture
-
- _previewLayer = [[AVCaptureVideoPreviewLayer alloc] initWithSession:nil];
-
- _maximumCaptureDuration = kCMTimeInvalid;
- [self setMirroringMode:PBJMirroringAuto];
- [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_applicationWillEnterForeground:) name:UIApplicationWillEnterForegroundNotification object:[UIApplication sharedApplication]];
- [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_applicationDidEnterBackground:) name:UIApplicationDidEnterBackgroundNotification object:[UIApplication sharedApplication]];
- }
- return self;
- }
- - (void)dealloc
- {
- [[NSNotificationCenter defaultCenter] removeObserver:self];
- _delegate = nil;
- [self _cleanUpTextures];
-
- if (_videoTextureCache) {
- CFRelease(_videoTextureCache);
- _videoTextureCache = NULL;
- }
-
- [self _destroyGL];
- [self _destroyCamera];
- }
- #pragma mark - queue helper methods
- typedef void (^PBJVisionBlock)();
- - (void)_enqueueBlockOnCaptureSessionQueue:(PBJVisionBlock)block
- {
- dispatch_async(_captureSessionDispatchQueue, ^{
- block();
- });
- }
- - (void)_enqueueBlockOnCaptureVideoQueue:(PBJVisionBlock)block
- {
- dispatch_async(_captureCaptureDispatchQueue, ^{
- block();
- });
- }
- - (void)_enqueueBlockOnMainQueue:(PBJVisionBlock)block
- {
- dispatch_async(dispatch_get_main_queue(), ^{
- block();
- });
- }
- - (void)_executeBlockOnMainQueue:(PBJVisionBlock)block
- {
- dispatch_sync(dispatch_get_main_queue(), ^{
- block();
- });
- }
- - (void)_commitBlock:(PBJVisionBlock)block
- {
- [_captureSession beginConfiguration];
- block();
- [_captureSession commitConfiguration];
- }
- #pragma mark - camera
- // only call from the session queue
- - (void)_setupCamera
- {
- if (_captureSession)
- return;
-
- #if COREVIDEO_USE_EAGLCONTEXT_CLASS_IN_API
- CVReturn cvError = CVOpenGLESTextureCacheCreate(kCFAllocatorDefault, NULL, _context, NULL, &_videoTextureCache);
- #else
- CVReturn cvError = CVOpenGLESTextureCacheCreate(kCFAllocatorDefault, NULL, (__bridge void *)_context, NULL, &_videoTextureCache);
- #endif
- if (cvError) {
- NSLog(@"error CVOpenGLESTextureCacheCreate (%d)", cvError);
- }
- // create session
- _captureSession = [[AVCaptureSession alloc] init];
- if (_usesApplicationAudioSession) {
- _captureSession.usesApplicationAudioSession = YES;
- }
- // capture devices
- _captureDeviceFront = [PBJVisionUtilities captureDeviceForPosition:AVCaptureDevicePositionFront];
- _captureDeviceBack = [PBJVisionUtilities captureDeviceForPosition:AVCaptureDevicePositionBack];
- // capture device inputs
- NSError *error = nil;
- _captureDeviceInputFront = [AVCaptureDeviceInput deviceInputWithDevice:_captureDeviceFront error:&error];
- if (error) {
- DLog(@"error setting up front camera input (%@)", error);
- error = nil;
- }
-
- _captureDeviceInputBack = [AVCaptureDeviceInput deviceInputWithDevice:_captureDeviceBack error:&error];
- if (error) {
- DLog(@"error setting up back camera input (%@)", error);
- error = nil;
- }
-
- if (_cameraMode != PBJCameraModePhoto && _flags.audioCaptureEnabled) {
- _captureDeviceAudio = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeAudio];
- _captureDeviceInputAudio = [AVCaptureDeviceInput deviceInputWithDevice:_captureDeviceAudio error:&error];
- if (error) {
- DLog(@"error setting up audio input (%@)", error);
- }
- }
-
- // capture device ouputs
- _captureOutputPhoto = [[AVCaptureStillImageOutput alloc] init];
- if (_cameraMode != PBJCameraModePhoto && _flags.audioCaptureEnabled) {
- _captureOutputAudio = [[AVCaptureAudioDataOutput alloc] init];
- }
- _captureOutputVideo = [[AVCaptureVideoDataOutput alloc] init];
-
- if (_cameraMode != PBJCameraModePhoto && _flags.audioCaptureEnabled) {
- [_captureOutputAudio setSampleBufferDelegate:self queue:_captureCaptureDispatchQueue];
- }
- [_captureOutputVideo setSampleBufferDelegate:self queue:_captureCaptureDispatchQueue];
- // capture device initial settings
- _videoFrameRate = 30;
- // add notification observers
- NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
-
- // session notifications
- [notificationCenter addObserver:self selector:@selector(_sessionRuntimeErrored:) name:AVCaptureSessionRuntimeErrorNotification object:_captureSession];
- [notificationCenter addObserver:self selector:@selector(_sessionStarted:) name:AVCaptureSessionDidStartRunningNotification object:_captureSession];
- [notificationCenter addObserver:self selector:@selector(_sessionStopped:) name:AVCaptureSessionDidStopRunningNotification object:_captureSession];
- [notificationCenter addObserver:self selector:@selector(_sessionWasInterrupted:) name:AVCaptureSessionWasInterruptedNotification object:_captureSession];
- [notificationCenter addObserver:self selector:@selector(_sessionInterruptionEnded:) name:AVCaptureSessionInterruptionEndedNotification object:_captureSession];
-
- // capture input notifications
- [notificationCenter addObserver:self selector:@selector(_inputPortFormatDescriptionDidChange:) name:AVCaptureInputPortFormatDescriptionDidChangeNotification object:nil];
-
- // capture device notifications
- [notificationCenter addObserver:self selector:@selector(_deviceSubjectAreaDidChange:) name:AVCaptureDeviceSubjectAreaDidChangeNotification object:nil];
- // current device KVO notifications
- [self addObserver:self forKeyPath:@"currentDevice.adjustingFocus" options:NSKeyValueObservingOptionNew context:(__bridge void *)PBJVisionFocusObserverContext];
- [self addObserver:self forKeyPath:@"currentDevice.adjustingExposure" options:NSKeyValueObservingOptionNew context:(__bridge void *)PBJVisionExposureObserverContext];
- [self addObserver:self forKeyPath:@"currentDevice.adjustingWhiteBalance" options:NSKeyValueObservingOptionNew context:(__bridge void *)PBJVisionWhiteBalanceObserverContext];
- [self addObserver:self forKeyPath:@"currentDevice.flashMode" options:NSKeyValueObservingOptionNew context:(__bridge void *)PBJVisionFlashModeObserverContext];
- [self addObserver:self forKeyPath:@"currentDevice.torchMode" options:NSKeyValueObservingOptionNew context:(__bridge void *)PBJVisionTorchModeObserverContext];
- [self addObserver:self forKeyPath:@"currentDevice.flashAvailable" options:NSKeyValueObservingOptionNew context:(__bridge void *)PBJVisionFlashAvailabilityObserverContext];
- [self addObserver:self forKeyPath:@"currentDevice.torchAvailable" options:NSKeyValueObservingOptionNew context:(__bridge void *)PBJVisionTorchAvailabilityObserverContext];
- // KVO is only used to monitor focus and capture events
- [_captureOutputPhoto addObserver:self forKeyPath:@"capturingStillImage" options:NSKeyValueObservingOptionNew context:(__bridge void *)(PBJVisionCaptureStillImageIsCapturingStillImageObserverContext)];
-
- DLog(@"camera setup");
- }
- // only call from the session queue
- - (void)_destroyCamera
- {
- if (!_captureSession)
- return;
-
- // current device KVO notifications
- [self removeObserver:self forKeyPath:@"currentDevice.adjustingFocus"];
- [self removeObserver:self forKeyPath:@"currentDevice.adjustingExposure"];
- [self removeObserver:self forKeyPath:@"currentDevice.adjustingWhiteBalance"];
- [self removeObserver:self forKeyPath:@"currentDevice.flashMode"];
- [self removeObserver:self forKeyPath:@"currentDevice.torchMode"];
- [self removeObserver:self forKeyPath:@"currentDevice.flashAvailable"];
- [self removeObserver:self forKeyPath:@"currentDevice.torchAvailable"];
-
- // capture events KVO notifications
- [_captureOutputPhoto removeObserver:self forKeyPath:@"capturingStillImage"];
- // remove notification observers (we don't want to just 'remove all' because we're also observing background notifications
- NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
- // session notifications
- [notificationCenter removeObserver:self name:AVCaptureSessionRuntimeErrorNotification object:_captureSession];
- [notificationCenter removeObserver:self name:AVCaptureSessionDidStartRunningNotification object:_captureSession];
- [notificationCenter removeObserver:self name:AVCaptureSessionDidStopRunningNotification object:_captureSession];
- [notificationCenter removeObserver:self name:AVCaptureSessionWasInterruptedNotification object:_captureSession];
- [notificationCenter removeObserver:self name:AVCaptureSessionInterruptionEndedNotification object:_captureSession];
-
- // capture input notifications
- [notificationCenter removeObserver:self name:AVCaptureInputPortFormatDescriptionDidChangeNotification object:nil];
-
- // capture device notifications
- [notificationCenter removeObserver:self name:AVCaptureDeviceSubjectAreaDidChangeNotification object:nil];
- _captureOutputPhoto = nil;
- _captureOutputAudio = nil;
- _captureOutputVideo = nil;
-
- _captureDeviceAudio = nil;
- _captureDeviceInputAudio = nil;
- _captureDeviceInputFront = nil;
- _captureDeviceInputBack = nil;
- _captureDeviceFront = nil;
- _captureDeviceBack = nil;
- _captureSession = nil;
- _currentDevice = nil;
- _currentInput = nil;
- _currentOutput = nil;
-
- DLog(@"camera destroyed");
- }
- #pragma mark - AVCaptureSession
- - (BOOL)_canSessionCaptureWithOutput:(AVCaptureOutput *)captureOutput
- {
- BOOL sessionContainsOutput = [[_captureSession outputs] containsObject:captureOutput];
- BOOL outputHasConnection = ([captureOutput connectionWithMediaType:AVMediaTypeVideo] != nil);
- return (sessionContainsOutput && outputHasConnection);
- }
- // _setupSession is always called from the captureSession queue
- - (void)_setupSession
- {
- if (!_captureSession) {
- DLog(@"error, no session running to setup");
- return;
- }
-
- BOOL shouldSwitchDevice = (_currentDevice == nil) ||
- ((_currentDevice == _captureDeviceFront) && (_cameraDevice != PBJCameraDeviceFront)) ||
- ((_currentDevice == _captureDeviceBack) && (_cameraDevice != PBJCameraDeviceBack));
-
- BOOL shouldSwitchMode = (_currentOutput == nil) ||
- ((_currentOutput == _captureOutputPhoto) && (_cameraMode != PBJCameraModePhoto)) ||
- ((_currentOutput == _captureOutputVideo) && (_cameraMode != PBJCameraModeVideo));
-
- DLog(@"switchDevice %d switchMode %d", shouldSwitchDevice, shouldSwitchMode);
- if (!shouldSwitchDevice && !shouldSwitchMode)
- return;
-
- AVCaptureDeviceInput *newDeviceInput = nil;
- AVCaptureOutput *newCaptureOutput = nil;
- AVCaptureDevice *newCaptureDevice = nil;
-
- [_captureSession beginConfiguration];
-
- // setup session device
-
- if (shouldSwitchDevice) {
- switch (_cameraDevice) {
- case PBJCameraDeviceFront:
- {
- if (_captureDeviceInputBack)
- [_captureSession removeInput:_captureDeviceInputBack];
-
- if (_captureDeviceInputFront && [_captureSession canAddInput:_captureDeviceInputFront]) {
- [_captureSession addInput:_captureDeviceInputFront];
- newDeviceInput = _captureDeviceInputFront;
- newCaptureDevice = _captureDeviceFront;
- }
- break;
- }
- case PBJCameraDeviceBack:
- {
- if (_captureDeviceInputFront)
- [_captureSession removeInput:_captureDeviceInputFront];
-
- if (_captureDeviceInputBack && [_captureSession canAddInput:_captureDeviceInputBack]) {
- [_captureSession addInput:_captureDeviceInputBack];
- newDeviceInput = _captureDeviceInputBack;
- newCaptureDevice = _captureDeviceBack;
- }
- break;
- }
- default:
- break;
- }
-
- } // shouldSwitchDevice
-
- // setup session input/output
-
- if (shouldSwitchMode) {
-
- // disable audio when in use for photos, otherwise enable it
-
- if (self.cameraMode == PBJCameraModePhoto) {
- if (_captureDeviceInputAudio)
- [_captureSession removeInput:_captureDeviceInputAudio];
-
- if (_captureOutputAudio)
- [_captureSession removeOutput:_captureOutputAudio];
-
- } else if (!_captureDeviceAudio && !_captureDeviceInputAudio && !_captureOutputAudio && _flags.audioCaptureEnabled) {
-
- NSError *error = nil;
- _captureDeviceAudio = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeAudio];
- _captureDeviceInputAudio = [AVCaptureDeviceInput deviceInputWithDevice:_captureDeviceAudio error:&error];
- if (error) {
- DLog(@"error setting up audio input (%@)", error);
- }
- _captureOutputAudio = [[AVCaptureAudioDataOutput alloc] init];
- [_captureOutputAudio setSampleBufferDelegate:self queue:_captureCaptureDispatchQueue];
-
- }
-
- [_captureSession removeOutput:_captureOutputVideo];
- [_captureSession removeOutput:_captureOutputPhoto];
-
- switch (_cameraMode) {
- case PBJCameraModeVideo:
- {
- // audio input
- if ([_captureSession canAddInput:_captureDeviceInputAudio]) {
- [_captureSession addInput:_captureDeviceInputAudio];
- }
- // audio output
- if ([_captureSession canAddOutput:_captureOutputAudio]) {
- [_captureSession addOutput:_captureOutputAudio];
- }
- // vidja output
- if ([_captureSession canAddOutput:_captureOutputVideo]) {
- [_captureSession addOutput:_captureOutputVideo];
- newCaptureOutput = _captureOutputVideo;
- }
- break;
- }
- case PBJCameraModePhoto:
- {
- // photo output
- if ([_captureSession canAddOutput:_captureOutputPhoto]) {
- [_captureSession addOutput:_captureOutputPhoto];
- newCaptureOutput = _captureOutputPhoto;
- }
- break;
- }
- default:
- break;
- }
-
- } // shouldSwitchMode
-
- if (!newCaptureDevice)
- newCaptureDevice = _currentDevice;
- if (!newCaptureOutput)
- newCaptureOutput = _currentOutput;
- // setup video connection
- AVCaptureConnection *videoConnection = [_captureOutputVideo connectionWithMediaType:AVMediaTypeVideo];
-
- // setup input/output
-
- NSString *sessionPreset = _captureSessionPreset;
- if ( newCaptureOutput && (newCaptureOutput == _captureOutputVideo) && videoConnection ) {
-
- // setup video orientation
- [self _setOrientationForConnection:videoConnection];
-
- // setup video stabilization, if available
- if ([videoConnection isVideoStabilizationSupported]) {
- if ([videoConnection respondsToSelector:@selector(setPreferredVideoStabilizationMode:)]) {
- [videoConnection setPreferredVideoStabilizationMode:AVCaptureVideoStabilizationModeAuto];
- } else {
- [videoConnection setEnablesVideoStabilizationWhenAvailable:YES];
- }
- }
-
- // discard late frames
- [_captureOutputVideo setAlwaysDiscardsLateVideoFrames:YES];
-
- // specify video preset
- sessionPreset = _captureSessionPreset;
- // setup video settings
- // kCVPixelFormatType_420YpCbCr8BiPlanarFullRange Bi-Planar Component Y'CbCr 8-bit 4:2:0, full-range (luma=[0,255] chroma=[1,255])
- // baseAddr points to a big-endian CVPlanarPixelBufferInfo_YCbCrBiPlanar struct
- BOOL supportsFullRangeYUV = NO;
- BOOL supportsVideoRangeYUV = NO;
- NSArray *supportedPixelFormats = _captureOutputVideo.availableVideoCVPixelFormatTypes;
- for (NSNumber *currentPixelFormat in supportedPixelFormats) {
- if ([currentPixelFormat intValue] == kCVPixelFormatType_420YpCbCr8BiPlanarFullRange) {
- supportsFullRangeYUV = YES;
- }
- if ([currentPixelFormat intValue] == kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange) {
- supportsVideoRangeYUV = YES;
- }
- }
- NSDictionary *videoSettings = nil;
- if (supportsFullRangeYUV) {
- videoSettings = @{ (id)kCVPixelBufferPixelFormatTypeKey : @(kCVPixelFormatType_420YpCbCr8BiPlanarFullRange) };
- } else if (supportsVideoRangeYUV) {
- videoSettings = @{ (id)kCVPixelBufferPixelFormatTypeKey : @(kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange) };
- }
- if (videoSettings) {
- [_captureOutputVideo setVideoSettings:videoSettings];
- }
-
- // setup video device configuration
- NSError *error = nil;
- if ([newCaptureDevice lockForConfiguration:&error]) {
-
- // smooth autofocus for videos
- if ([newCaptureDevice isSmoothAutoFocusSupported])
- [newCaptureDevice setSmoothAutoFocusEnabled:YES];
-
- [newCaptureDevice unlockForConfiguration];
-
- } else if (error) {
- DLog(@"error locking device for video device configuration (%@)", error);
- }
-
- } else if ( newCaptureOutput && (newCaptureOutput == _captureOutputPhoto) ) {
-
- // specify photo preset
- sessionPreset = AVCaptureSessionPresetPhoto;
-
- // setup photo settings
- NSDictionary *photoSettings = @{AVVideoCodecKey : AVVideoCodecJPEG};
- [_captureOutputPhoto setOutputSettings:photoSettings];
-
- // setup photo device configuration
- NSError *error = nil;
- if ([newCaptureDevice lockForConfiguration:&error]) {
-
- if ([newCaptureDevice isLowLightBoostSupported])
- [newCaptureDevice setAutomaticallyEnablesLowLightBoostWhenAvailable:YES];
-
- [newCaptureDevice unlockForConfiguration];
-
- } else if (error) {
- DLog(@"error locking device for photo device configuration (%@)", error);
- }
-
- }
- // apply presets
- if ([_captureSession canSetSessionPreset:sessionPreset])
- [_captureSession setSessionPreset:sessionPreset];
- if (newDeviceInput)
- _currentInput = newDeviceInput;
-
- if (newCaptureOutput)
- _currentOutput = newCaptureOutput;
- // ensure there is a capture device setup
- if (_currentInput) {
- AVCaptureDevice *device = [_currentInput device];
- if (device) {
- [self willChangeValueForKey:@"currentDevice"];
- _currentDevice = device;
- [self didChangeValueForKey:@"currentDevice"];
- }
- }
- [_captureSession commitConfiguration];
-
- DLog(@"capture session setup");
- }
- #pragma mark - preview
- - (void)startPreview
- {
- [self _enqueueBlockOnCaptureSessionQueue:^{
- if (!_captureSession) {
- [self _setupCamera];
- [self _setupSession];
- }
- [self setMirroringMode:_mirroringMode];
-
- if (_previewLayer && _previewLayer.session != _captureSession) {
- _previewLayer.session = _captureSession;
- [self _setOrientationForConnection:_previewLayer.connection];
- }
-
- if (_previewLayer)
- _previewLayer.connection.enabled = YES;
-
- if (![_captureSession isRunning]) {
- [_captureSession startRunning];
-
- [self _enqueueBlockOnMainQueue:^{
- if ([_delegate respondsToSelector:@selector(visionSessionDidStartPreview:)]) {
- [_delegate visionSessionDidStartPreview:self];
- }
- }];
- DLog(@"capture session running");
- }
- _flags.previewRunning = YES;
- }];
- }
- - (void)stopPreview
- {
- [self _enqueueBlockOnCaptureSessionQueue:^{
- if (!_flags.previewRunning)
- return;
- if (_previewLayer)
- _previewLayer.connection.enabled = NO;
- if ([_captureSession isRunning])
- [_captureSession stopRunning];
- [self _executeBlockOnMainQueue:^{
- if ([_delegate respondsToSelector:@selector(visionSessionDidStopPreview:)]) {
- [_delegate visionSessionDidStopPreview:self];
- }
- }];
- DLog(@"capture session stopped");
- _flags.previewRunning = NO;
- }];
- }
- - (void)freezePreview
- {
- if (_previewLayer)
- _previewLayer.connection.enabled = NO;
- }
- - (void)unfreezePreview
- {
- if (_previewLayer)
- _previewLayer.connection.enabled = YES;
- }
- #pragma mark - focus, exposure, white balance
- - (void)_focusStarted
- {
- // DLog(@"focus started");
- if ([_delegate respondsToSelector:@selector(visionWillStartFocus:)])
- [_delegate visionWillStartFocus:self];
- }
- - (void)_focusEnded
- {
- AVCaptureFocusMode focusMode = [_currentDevice focusMode];
- BOOL isFocusing = [_currentDevice isAdjustingFocus];
- BOOL isAutoFocusEnabled = (focusMode == AVCaptureFocusModeAutoFocus ||
- focusMode == AVCaptureFocusModeContinuousAutoFocus);
- if (!isFocusing && isAutoFocusEnabled) {
- NSError *error = nil;
- if ([_currentDevice lockForConfiguration:&error]) {
-
- [_currentDevice setSubjectAreaChangeMonitoringEnabled:YES];
- [_currentDevice unlockForConfiguration];
-
- } else if (error) {
- DLog(@"error locking device post exposure for subject area change monitoring (%@)", error);
- }
- }
- if ([_delegate respondsToSelector:@selector(visionDidStopFocus:)])
- [_delegate visionDidStopFocus:self];
- // DLog(@"focus ended");
- }
- - (void)_exposureChangeStarted
- {
- // DLog(@"exposure change started");
- if ([_delegate respondsToSelector:@selector(visionWillChangeExposure:)])
- [_delegate visionWillChangeExposure:self];
- }
- - (void)_exposureChangeEnded
- {
- BOOL isContinuousAutoExposureEnabled = [_currentDevice exposureMode] == AVCaptureExposureModeContinuousAutoExposure;
- BOOL isExposing = [_currentDevice isAdjustingExposure];
- BOOL isFocusSupported = [_currentDevice isFocusModeSupported:AVCaptureFocusModeContinuousAutoFocus];
- if (isContinuousAutoExposureEnabled && !isExposing && !isFocusSupported) {
- NSError *error = nil;
- if ([_currentDevice lockForConfiguration:&error]) {
-
- [_currentDevice setSubjectAreaChangeMonitoringEnabled:YES];
- [_currentDevice unlockForConfiguration];
-
- } else if (error) {
- DLog(@"error locking device post exposure for subject area change monitoring (%@)", error);
- }
- }
- if ([_delegate respondsToSelector:@selector(visionDidChangeExposure:)])
- [_delegate visionDidChangeExposure:self];
- // DLog(@"exposure change ended");
- }
- - (void)_whiteBalanceChangeStarted
- {
- }
- - (void)_whiteBalanceChangeEnded
- {
- }
- - (void)focusAtAdjustedPointOfInterest:(CGPoint)adjustedPoint
- {
- if ([_currentDevice isAdjustingFocus] || [_currentDevice isAdjustingExposure])
- return;
- NSError *error = nil;
- if ([_currentDevice lockForConfiguration:&error]) {
-
- BOOL isFocusAtPointSupported = [_currentDevice isFocusPointOfInterestSupported];
-
- if (isFocusAtPointSupported && [_currentDevice isFocusModeSupported:AVCaptureFocusModeAutoFocus]) {
- AVCaptureFocusMode fm = [_currentDevice focusMode];
- [_currentDevice setFocusPointOfInterest:adjustedPoint];
- [_currentDevice setFocusMode:fm];
- }
- [_currentDevice unlockForConfiguration];
-
- } else if (error) {
- DLog(@"error locking device for focus adjustment (%@)", error);
- }
- }
- - (BOOL)isAdjustingFocus
- {
- return [_currentDevice isAdjustingFocus];
- }
- - (void)exposeAtAdjustedPointOfInterest:(CGPoint)adjustedPoint
- {
- if ([_currentDevice isAdjustingExposure])
- return;
- NSError *error = nil;
- if ([_currentDevice lockForConfiguration:&error]) {
-
- BOOL isExposureAtPointSupported = [_currentDevice isExposurePointOfInterestSupported];
- if (isExposureAtPointSupported && [_currentDevice isExposureModeSupported:AVCaptureExposureModeContinuousAutoExposure]) {
- AVCaptureExposureMode em = [_currentDevice exposureMode];
- [_currentDevice setExposurePointOfInterest:adjustedPoint];
- [_currentDevice setExposureMode:em];
- }
- [_currentDevice unlockForConfiguration];
-
- } else if (error) {
- DLog(@"error locking device for exposure adjustment (%@)", error);
- }
- }
- - (BOOL)isAdjustingExposure
- {
- return [_currentDevice isAdjustingExposure];
- }
- - (void)_adjustFocusExposureAndWhiteBalance
- {
- if ([_currentDevice isAdjustingFocus] || [_currentDevice isAdjustingExposure])
- return;
- // only notify clients when focus is triggered from an event
- if ([_delegate respondsToSelector:@selector(visionWillStartFocus:)])
- [_delegate visionWillStartFocus:self];
- CGPoint focusPoint = CGPointMake(0.5f, 0.5f);
- [self focusAtAdjustedPointOfInterest:focusPoint];
- }
- // focusExposeAndAdjustWhiteBalanceAtAdjustedPoint: will put focus and exposure into auto
- - (void)focusExposeAndAdjustWhiteBalanceAtAdjustedPoint:(CGPoint)adjustedPoint
- {
- if ([_currentDevice isAdjustingFocus] || [_currentDevice isAdjustingExposure])
- return;
- NSError *error = nil;
- if ([_currentDevice lockForConfiguration:&error]) {
-
- BOOL isFocusAtPointSupported = [_currentDevice isFocusPointOfInterestSupported];
- BOOL isExposureAtPointSupported = [_currentDevice isExposurePointOfInterestSupported];
- BOOL isWhiteBalanceModeSupported = [_currentDevice isWhiteBalanceModeSupported:AVCaptureWhiteBalanceModeContinuousAutoWhiteBalance];
-
- if (isFocusAtPointSupported && [_currentDevice isFocusModeSupported:AVCaptureFocusModeAutoFocus]) {
- [_currentDevice setFocusPointOfInterest:adjustedPoint];
- [_currentDevice setFocusMode:AVCaptureFocusModeAutoFocus];
- }
-
- if (isExposureAtPointSupported && [_currentDevice isExposureModeSupported:AVCaptureExposureModeContinuousAutoExposure]) {
- [_currentDevice setExposurePointOfInterest:adjustedPoint];
- [_currentDevice setExposureMode:AVCaptureExposureModeContinuousAutoExposure];
- }
-
- if (isWhiteBalanceModeSupported) {
- [_currentDevice setWhiteBalanceMode:AVCaptureWhiteBalanceModeContinuousAutoWhiteBalance];
- }
-
- [_currentDevice setSubjectAreaChangeMonitoringEnabled:NO];
-
- [_currentDevice unlockForConfiguration];
-
- } else if (error) {
- DLog(@"error locking device for focus / exposure / white-balance adjustment (%@)", error);
- }
- }
- #pragma mark - mirroring
- - (void)setMirroringMode:(PBJMirroringMode)mirroringMode
- {
- _mirroringMode = mirroringMode;
-
- AVCaptureConnection *videoConnection = [_currentOutput connectionWithMediaType:AVMediaTypeVideo];
- AVCaptureConnection *previewConnection = [_previewLayer connection];
-
- switch (_mirroringMode) {
- case PBJMirroringOff:
- {
- if ([videoConnection isVideoMirroringSupported]) {
- [videoConnection setVideoMirrored:NO];
- }
- if ([previewConnection isVideoMirroringSupported]) {
- [previewConnection setAutomaticallyAdjustsVideoMirroring:NO];
- [previewConnection setVideoMirrored:NO];
-