class
TGPGCamera
@property (readonly, nonatomic) PGCameraCaptureSession *captureSession;
@property (readonly, nonatomic) PGCameraDeviceAngleSampler *deviceAngleSampler;
@property (nonatomic, copy) void(^captureStarted)(bool resumed);
@property (nonatomic, copy) void(^captureStopped)(bool paused);
@property (nonatomic, copy) void(^beganModeChange)(PGCameraMode mode, void(^commitBlock)(void));
@property (nonatomic, copy) void(^finishedModeChange)(void);
@property (nonatomic, copy) void(^beganPositionChange)(bool targetPositionHasFlash, bool targetPositionHasZoom, void(^commitBlock)(void));
@property (nonatomic, copy) void(^finishedPositionChange)(void);
@property (nonatomic, copy) void(^beganAdjustingFocus)(void);
@property (nonatomic, copy) void(^finishedAdjustingFocus)(void);
@property (nonatomic, copy) void(^flashActivityChanged)(bool flashActive);
@property (nonatomic, copy) void(^flashAvailabilityChanged)(bool flashAvailable);
@property (nonatomic, copy) void(^beganVideoRecording)(bool moment);
@property (nonatomic, copy) void(^finishedVideoRecording)(bool moment);
@property (nonatomic, copy) void(^reallyBeganVideoRecording)(bool moment);
@property (nonatomic, copy) void(^captureInterrupted)(AVCaptureSessionInterruptionReason reason);
@property (nonatomic, copy) void(^onAutoStartVideoRecording)(void);
@property (nonatomic, copy) UIInterfaceOrientation(^requestedCurrentInterfaceOrientation)(bool *mirrored);
@property (nonatomic, assign) PGCameraMode cameraMode;
@property (nonatomic, assign) PGCameraFlashMode flashMode;
@property (nonatomic, readonly) bool isZoomAvailable;
@property (nonatomic, assign) CGFloat zoomLevel;
@property (nonatomic, assign) bool disableResultMirroring;
@property (nonatomic, assign) bool disabled;
@property (nonatomic, readonly) bool isCapturing;
@property (nonatomic, readonly) NSTimeInterval videoRecordingDuration;
@property (nonatomic, assign) bool autoStartVideoRecording;
- (instancetype)initWithMode:(PGCameraMode)mode position:(PGCameraPosition)position;
- (void)attachPreviewView:(TGCameraPreviewView *)previewView;
- (bool)supportsExposurePOI;
- (bool)supportsFocusPOI;
- (void)setFocusPoint:(CGPoint)focusPoint;
- (bool)supportsExposureTargetBias;
- (void)beginExposureTargetBiasChange;
- (void)setExposureTargetBias:(CGFloat)bias;
- (void)endExposureTargetBiasChange;
- (void)captureNextFrameCompletion:(void (^)(UIImage * image))completion;
- (void)takePhotoWithCompletion:(void (^)(UIImage *result, PGCameraShotMetadata *metadata))completion;
- (void)startVideoRecordingForMoment:(bool)moment completion:(void (^)(NSURL *, CGAffineTransform transform, CGSize dimensions, NSTimeInterval duration, bool success))completion;
- (void)stopVideoRecording;
- (bool)isRecordingVideo;
- (void)startCaptureForResume:(bool)resume completion:(void (^)(void))completion;
- (void)stopCaptureForPause:(bool)pause completion:(void (^)(void))completion;
- (bool)isResetNeeded;
- (void)resetSynchronous:(bool)synchronous completion:(void (^)(void))completion;
- (void)resetTerminal:(bool)terminal synchronous:(bool)synchronous completion:(void (^)(void))completion;
- (bool)hasFlash;
- (bool)flashActive;
- (bool)flashAvailable;
- (PGCameraPosition)togglePosition;
+ (bool)cameraAvailable;
+ (bool)hasFrontCamera;
+ (bool)hasRearCamera;
+ (PGCameraAuthorizationStatus)cameraAuthorizationStatus;
+ (PGMicrophoneAuthorizationStatus)microphoneAuthorizationStatus;
// init
- (instancetype)initWithMode:(PGCameraMode)mode position:(PGCameraPosition)position
{
self = [super init];
if (self != nil)
{
_captureSession = [[PGCameraCaptureSession alloc] initWithMode:mode position:position];
_deviceAngleSampler = [[PGCameraDeviceAngleSampler alloc] init];
[_deviceAngleSampler startMeasuring];
__weak TGPGCamera *weakSelf = self;
self.captureSession.requestPreviewIsMirrored = ^bool
{
__strong TGPGCamera *strongSelf = weakSelf;
if (strongSelf == nil || strongSelf->_previewView == nil)
return false;
TGCameraPreviewView *previewView = strongSelf->_previewView;
return previewView.captureConnection.videoMirrored;
};
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleEnteredBackground:) name:UIApplicationDidEnterBackgroundNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleEnteredForeground:) name:UIApplicationWillEnterForegroundNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleRuntimeError:) name:AVCaptureSessionRuntimeErrorNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleInterrupted:) name:AVCaptureSessionWasInterruptedNotification object:nil];
}
return self;
}
// handle enterBackground
- (void)handleEnteredBackground:(NSNotification *)__unused notification
{
if (self.isCapturing) {
_wasCapturingOnEnterBackground = true;
[_previewView fadeOutAnimated:false];
}
[self stopCaptureForPause:true completion:nil];
}
// handle enterForeground
- (void)handleEnteredForeground:(NSNotification *)__unused notification
{
if (_wasCapturingOnEnterBackground)
{
_wasCapturingOnEnterBackground = false;
__weak TGPGCamera *weakSelf = self;
[self startCaptureForResume:true completion:^{
__strong TGPGCamera *strongSelf = weakSelf;
if (strongSelf != nil) {
[strongSelf->_previewView fadeInAnimated:true];
}
}];
}
}
// handle runtime error
- (void)handleRuntimeError:(NSNotification *)notification
{
TGLegacyLog(@"ERROR: Camera runtime error: %@", notification.userInfo[AVCaptureSessionErrorKey]);
__weak TGPGCamera *weakSelf = self;
TGDispatchAfter(1.5f, [TGPGCamera cameraQueue]._dispatch_queue, ^
{
__strong TGPGCamera *strongSelf = weakSelf;
if (strongSelf == nil || strongSelf->_invalidated)
return;
// unsubscribe camera changes
[strongSelf _unsubscribeFromCameraChanges];
// remove all capture inputs
for (AVCaptureInput *input in strongSelf.captureSession.inputs)
[strongSelf.captureSession removeInput:input];
// remove all capture outputs
for (AVCaptureOutput *output in strongSelf.captureSession.outputs)
[strongSelf.captureSession removeOutput:output];
// resubscribe camera changes
[strongSelf.captureSession performInitialConfigurationWithCompletion:^
{
__strong TGPGCamera *strongSelf = weakSelf;
if (strongSelf != nil)
[strongSelf _subscribeForCameraChanges];
}];
});
}
// handle interruption
- (void)handleInterrupted:(NSNotification *)notification
{
if (iosMajorVersion() < 9)
return;
AVCaptureSessionInterruptionReason reason = [notification.userInfo[AVCaptureSessionInterruptionReasonKey] integerValue];
TGLegacyLog(@"WARNING: Camera was interrupted with reason %d", reason);
// callback
if (self.captureInterrupted != nil)
self.captureInterrupted(reason);
// stop video recording
if (self.isRecordingVideo)
[self stopVideoRecording];
}
// subscribe camera changes
- (void)_subscribeForCameraChanges
{
if (_subscribedForCameraChanges)
return;
_subscribedForCameraChanges = true;
[self.captureSession.videoDevice addObserver:self forKeyPath:PGCameraFlashActiveKey options:NSKeyValueObservingOptionNew context:NULL];
[self.captureSession.videoDevice addObserver:self forKeyPath:PGCameraFlashAvailableKey options:NSKeyValueObservingOptionNew context:NULL];
[self.captureSession.videoDevice addObserver:self forKeyPath:PGCameraTorchActiveKey options:NSKeyValueObservingOptionNew context:NULL];
[self.captureSession.videoDevice addObserver:self forKeyPath:PGCameraTorchAvailableKey options:NSKeyValueObservingOptionNew context:NULL];
[self.captureSession.videoDevice addObserver:self forKeyPath:PGCameraAdjustingFocusKey options:NSKeyValueObservingOptionNew context:NULL];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(subjectAreaChanged:) name:AVCaptureDeviceSubjectAreaDidChangeNotification object:self.captureSession.videoDevice];
}
// unsubscribe camera changes
- (void)_unsubscribeFromCameraChanges
{
if (!_subscribedForCameraChanges)
return;
_subscribedForCameraChanges = false;
@try {
[self.captureSession.videoDevice removeObserver:self forKeyPath:PGCameraFlashActiveKey];
[self.captureSession.videoDevice removeObserver:self forKeyPath:PGCameraFlashAvailableKey];
[self.captureSession.videoDevice removeObserver:self forKeyPath:PGCameraTorchActiveKey];
[self.captureSession.videoDevice removeObserver:self forKeyPath:PGCameraTorchAvailableKey];
[self.captureSession.videoDevice removeObserver:self forKeyPath:PGCameraAdjustingFocusKey];
[[NSNotificationCenter defaultCenter] removeObserver:self name:AVCaptureDeviceSubjectAreaDidChangeNotification object:self.captureSession.videoDevice];
} @catch(NSException *e) { }
}
// attach previewView
- (void)attachPreviewView:(TGCameraPreviewView *)previewView
{
TGCameraPreviewView *currentPreviewView = _previewView;
if (currentPreviewView != nil)
[currentPreviewView invalidate];
_previewView = previewView;
[previewView setupWithCamera:self];
__weak TGPGCamera *weakSelf = self;
[[TGPGCamera cameraQueue] dispatch:^
{
__strong TGPGCamera *strongSelf = weakSelf;
if (strongSelf == nil || strongSelf->_invalidated)
return;
[strongSelf.captureSession performInitialConfigurationWithCompletion:^
{
__strong TGPGCamera *strongSelf = weakSelf;
if (strongSelf != nil)
[strongSelf _subscribeForCameraChanges];
}];
}];
}
// capturing
- (bool)isCapturing
{
return _capturing;
}
// start capturing
// start capture session
// callback started
- (void)startCaptureForResume:(bool)resume completion:(void (^)(void))completion
{
if (_invalidated)
return;
[[TGPGCamera cameraQueue] dispatch:^
{
if (self.captureSession.isRunning)
return;
_capturing = true;
TGLegacyLog(@"Camera: start capture");
#if !TARGET_IPHONE_SIMULATOR
[self.captureSession startRunning];
#endif
if (_captureStartTime < FLT_EPSILON)
_captureStartTime = CFAbsoluteTimeGetCurrent();
TGDispatchOnMainThread(^
{
if (self.captureStarted != nil)
self.captureStarted(resume);
if (completion != nil)
completion();
});
}];
}
// stop capturing
// invalidate preview
// remove all device inputs and ouputs
// stop session
// callback stopped
- (void)stopCaptureForPause:(bool)pause completion:(void (^)(void))completion
{
if (_invalidated)
return;
if (!pause)
_invalidated = true;
TGLegacyLog(@"Camera: stop capture");
[[TGPGCamera cameraQueue] dispatch:^
{
if (_invalidated)
{
[self.captureSession beginConfiguration];
[self.captureSession resetFlashMode];
TGLegacyLog(@"Camera: stop capture invalidated");
TGCameraPreviewView *previewView = _previewView;
if (previewView != nil)
[previewView invalidate];
for (AVCaptureInput *input in self.captureSession.inputs)
[self.captureSession removeInput:input];
for (AVCaptureOutput *output in self.captureSession.outputs)
[self.captureSession removeOutput:output];
#if !TARGET_IPHONE_SIMULATOR
[self.captureSession commitConfiguration];
#endif
}
TGLegacyLog(@"Camera: stop running");
#if !TARGET_IPHONE_SIMULATOR
[self.captureSession stopRunning];
#endif
_capturing = false;
TGDispatchOnMainThread(^
{
if (_invalidated)
_previewView = nil;
if (self.captureStopped != nil)
self.captureStopped(pause);
});
if (completion != nil)
completion();
}];
}
// reset supporting
- (bool)isResetNeeded
{
return self.captureSession.isResetNeeded;
}
- (void)resetSynchronous:(bool)synchronous completion:(void (^)(void))completion
{
[self resetTerminal:false synchronous:synchronous completion:completion];
}
- (void)resetTerminal:(bool)__unused terminal synchronous:(bool)synchronous completion:(void (^)(void))completion
{
void (^block)(void) = ^
{
[self _unsubscribeFromCameraChanges];
[self.captureSession reset];
[self _subscribeForCameraChanges];
if (completion != nil)
completion();
};
if (synchronous)
[[TGPGCamera cameraQueue] dispatchSync:block];
else
[[TGPGCamera cameraQueue] dispatch:block];
}
// set frame callback
- (void)captureNextFrameCompletion:(void (^)(UIImage * image))completion
{
[self.captureSession captureNextFrameCompletion:completion];
}
// take photo
// capture a photo from AVCaptureStillImageOutput
- (void)takePhotoWithCompletion:(void (^)(UIImage *result, PGCameraShotMetadata *metadata))completion
{
bool videoMirrored = !self.disableResultMirroring ? _previewView.captureConnection.videoMirrored : false;
[[TGPGCamera cameraQueue] dispatch:^
{
if (!self.captureSession.isRunning || self.captureSession.imageOutput.isCapturingStillImage || _invalidated)
return;
void (^takePhoto)(void) = ^
{
AVCaptureConnection *imageConnection = [self.captureSession.imageOutput connectionWithMediaType:AVMediaTypeVideo];
[imageConnection setVideoMirrored:videoMirrored];
UIInterfaceOrientation orientation = UIInterfaceOrientationPortrait;
if (self.requestedCurrentInterfaceOrientation != nil)
orientation = self.requestedCurrentInterfaceOrientation(NULL);
[imageConnection setVideoOrientation:[TGPGCamera _videoOrientationForInterfaceOrientation:orientation mirrored:false]];
[self.captureSession.imageOutput captureStillImageAsynchronouslyFromConnection:self.captureSession.imageOutput.connections.firstObject completionHandler:^(CMSampleBufferRef imageDataSampleBuffer, NSError *error)
{
if (imageDataSampleBuffer != NULL && error == nil)
{
NSData *imageData = [AVCaptureStillImageOutput jpegStillImageNSDataRepresentation:imageDataSampleBuffer];
UIImage *image = [[UIImage alloc] initWithData:imageData];
if (self.cameraMode == PGCameraModeSquare)
{
CGFloat shorterSide = MIN(image.size.width, image.size.height);
CGFloat longerSide = MAX(image.size.width, image.size.height);
CGRect cropRect = CGRectMake(CGFloor((longerSide - shorterSide) / 2.0f), 0, shorterSide, shorterSide);
CGImageRef croppedCGImage = CGImageCreateWithImageInRect(image.CGImage, cropRect);
image = [UIImage imageWithCGImage:croppedCGImage scale:image.scale orientation:image.imageOrientation];
CGImageRelease(croppedCGImage);
}
PGCameraShotMetadata *metadata = [[PGCameraShotMetadata alloc] init];
metadata.deviceAngle = [PGCameraShotMetadata relativeDeviceAngleFromAngle:_deviceAngleSampler.currentDeviceAngle orientation:orientation];
image = [self normalizeImageOrientation:image];
if (completion != nil)
completion(image, metadata);
}
}];
};
NSTimeInterval delta = CFAbsoluteTimeGetCurrent() - _captureStartTime;
if (CFAbsoluteTimeGetCurrent() - _captureStartTime > 0.4)
takePhoto();
else
TGDispatchAfter(0.4 - delta, [[TGPGCamera cameraQueue] _dispatch_queue], takePhoto);
}];
}
// start recording
// callback beginRecording
- (void)startVideoRecordingForMoment:(bool)moment completion:(void (^)(NSURL *, CGAffineTransform transform, CGSize dimensions, NSTimeInterval duration, bool success))completion
{
[[PGCamera cameraQueue] dispatch:^
{
if (!self.captureSession.isRunning || _invalidated)
return;
void (^startRecording)(void) = ^
{
UIInterfaceOrientation orientation = UIInterfaceOrientationPortrait;
bool mirrored = false;
if (self.requestedCurrentInterfaceOrientation != nil)
orientation = self.requestedCurrentInterfaceOrientation(&mirrored);
_moment = moment;
[self.captureSession startVideoRecordingWithOrientation:[PGCamera _videoOrientationForInterfaceOrientation:orientation mirrored:mirrored] mirrored:mirrored completion:completion];
TGDispatchOnMainThread(^
{
if (self.reallyBeganVideoRecording != nil)
self.reallyBeganVideoRecording(moment);
});
};
NSTimeInterval delta = CFAbsoluteTimeGetCurrent() - _captureStartTime;
if (CFAbsoluteTimeGetCurrent() - _captureStartTime > 0.8)
startRecording();
else
TGDispatchAfter(0.8 - delta, [[PGCamera cameraQueue] _dispatch_queue], startRecording);
TGDispatchOnMainThread(^
{
if (self.beganVideoRecording != nil)
self.beganVideoRecording(moment);
});
}];
}
// stop recording
- (void)stopVideoRecording
{
[[PGCamera cameraQueue] dispatch:^
{
[self.captureSession stopVideoRecording];
TGDispatchOnMainThread(^
{
if (self.finishedVideoRecording != nil)
self.finishedVideoRecording(_moment);
});
}];
}
// recording status
- (bool)isRecordingVideo
{
return self.captureSession.movieWriter.isRecording;
}
- (NSTimeInterval)videoRecordingDuration
{
return self.captureSession.movieWriter.currentDuration;
}
// camera mode
- (PGCameraMode)cameraMode
{
return self.captureSession.currentMode;
}
// callback modeChange
// callback autoStartVideoRecording
- (void)setCameraMode:(PGCameraMode)cameraMode
{
if (self.disabled || self.captureSession.currentMode == cameraMode)
return;
__weak TGPGCamera *weakSelf = self;
void(^commitBlock)(void) = ^
{
__strong TGPGCamera *strongSelf = weakSelf;
if (strongSelf == nil)
return;
[[TGPGCamera cameraQueue] dispatch:^
{
strongSelf.captureSession.currentMode = cameraMode;
if (strongSelf.finishedModeChange != nil)
strongSelf.finishedModeChange();
if (strongSelf.autoStartVideoRecording && strongSelf.onAutoStartVideoRecording != nil)
{
TGDispatchAfter(0.5, dispatch_get_main_queue(), ^
{
strongSelf.onAutoStartVideoRecording();
});
}
strongSelf.autoStartVideoRecording = false;
}];
};
if (self.beganModeChange != nil)
self.beganModeChange(cameraMode, commitBlock);
}
// focus and exposure supporting
- (void)subjectAreaChanged:(NSNotification *)__unused notification
{
[self resetFocusPoint];
}
// kvo
// callback flashActivityChanged
// callback flashAvailabilityChanged
// callback beganAdjustingFocus
// callback finishedAdjustingFocus
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)__unused object change:(NSDictionary *)__unused change context:(void *)__unused context
{
TGDispatchOnMainThread(^
{
if ([keyPath isEqualToString:PGCameraAdjustingFocusKey])
{
bool adjustingFocus = [[change objectForKey:NSKeyValueChangeNewKey] isEqualToNumber:@YES];
if (adjustingFocus && self.beganAdjustingFocus != nil)
self.beganAdjustingFocus();
else if (!adjustingFocus && self.finishedAdjustingFocus != nil)
self.finishedAdjustingFocus();
}
else if ([keyPath isEqualToString:PGCameraFlashActiveKey] || [keyPath isEqualToString:PGCameraTorchActiveKey])
{
bool active = [[change objectForKey:NSKeyValueChangeNewKey] isEqualToNumber:@YES];
if (self.flashActivityChanged != nil)
self.flashActivityChanged(active);
}
else if ([keyPath isEqualToString:PGCameraFlashAvailableKey] || [keyPath isEqualToString:PGCameraTorchAvailableKey])
{
bool available = [[change objectForKey:NSKeyValueChangeNewKey] isEqualToNumber:@YES];
if (self.flashAvailabilityChanged != nil)
self.flashAvailabilityChanged(available);
}
});
}
- (bool)supportsExposurePOI
{
return [self.captureSession.videoDevice isExposurePointOfInterestSupported];
}
- (bool)supportsFocusPOI
{
return [self.captureSession.videoDevice isFocusPointOfInterestSupported];
}
- (void)resetFocusPoint
{
const CGPoint centerPoint = CGPointMake(0.5f, 0.5f);
[self _setFocusPoint:centerPoint focusMode:AVCaptureFocusModeContinuousAutoFocus exposureMode:AVCaptureExposureModeContinuousAutoExposure monitorSubjectAreaChange:false];
}
- (void)setFocusPoint:(CGPoint)point
{
[self _setFocusPoint:point focusMode:AVCaptureFocusModeAutoFocus exposureMode:AVCaptureExposureModeAutoExpose monitorSubjectAreaChange:true];
}
- (void)_setFocusPoint:(CGPoint)point focusMode:(AVCaptureFocusMode)focusMode exposureMode:(AVCaptureExposureMode)exposureMode monitorSubjectAreaChange:(bool)monitorSubjectAreaChange
{
[[TGPGCamera cameraQueue] dispatch:^
{
if (self.disabled)
return;
[self.captureSession setFocusPoint:point focusMode:focusMode exposureMode:exposureMode monitorSubjectAreaChange:monitorSubjectAreaChange];
}];
}
- (bool)supportsExposureTargetBias
{
return [self.captureSession.videoDevice respondsToSelector:@selector(setExposureTargetBias:completionHandler:)];
}
- (void)beginExposureTargetBiasChange
{
[[TGPGCamera cameraQueue] dispatch:^
{
if (self.disabled)
return;
[self.captureSession setFocusPoint:self.captureSession.focusPoint focusMode:AVCaptureFocusModeLocked exposureMode:AVCaptureExposureModeLocked monitorSubjectAreaChange:false];
}];
}
- (void)setExposureTargetBias:(CGFloat)bias
{
[[TGPGCamera cameraQueue] dispatch:^
{
if (self.disabled)
return;
[self.captureSession setExposureTargetBias:bias];
}];
}
- (void)endExposureTargetBiasChange
{
[[TGPGCamera cameraQueue] dispatch:^
{
if (self.disabled)
return;
[self.captureSession setFocusPoint:self.captureSession.focusPoint focusMode:AVCaptureFocusModeAutoFocus exposureMode:AVCaptureExposureModeAutoExpose monitorSubjectAreaChange:true];
}];
}
// flash supporting
- (bool)hasFlash
{
return self.captureSession.videoDevice.hasFlash;
}
- (bool)flashActive
{
if (self.cameraMode == PGCameraModeVideo || self.cameraMode == PGCameraModeClip)
return self.captureSession.videoDevice.torchActive;
return self.captureSession.videoDevice.flashActive;
}
- (bool)flashAvailable
{
if (self.cameraMode == PGCameraModeVideo || self.cameraMode == PGCameraModeClip)
return self.captureSession.videoDevice.torchAvailable;
return self.captureSession.videoDevice.flashAvailable;
}
- (PGCameraFlashMode)flashMode
{
return self.captureSession.currentFlashMode;
}
- (void)setFlashMode:(PGCameraFlashMode)flashMode
{
[[TGPGCamera cameraQueue] dispatch:^
{
self.captureSession.currentFlashMode = flashMode;
}];
}
// camera position supporting
- (PGCameraPosition)togglePosition
{
if ([AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo].count < 2 || self.disabled)
return self.captureSession.currentCameraPosition;
[self _unsubscribeFromCameraChanges];
PGCameraPosition targetCameraPosition = PGCameraPositionFront;
if (self.captureSession.currentCameraPosition == PGCameraPositionFront)
targetCameraPosition = PGCameraPositionRear;
AVCaptureDevice *targetDevice = [PGCameraCaptureSession _deviceWithCameraPosition:targetCameraPosition];
__weak TGPGCamera *weakSelf = self;
void(^commitBlock)(void) = ^
{
__strong TGPGCamera *strongSelf = weakSelf;
if (strongSelf == nil)
return;
[[TGPGCamera cameraQueue] dispatch:^
{
[strongSelf.captureSession setCurrentCameraPosition:targetCameraPosition];
if (strongSelf.finishedPositionChange != nil)
strongSelf.finishedPositionChange();
[strongSelf setZoomLevel:0.0f];
[strongSelf _subscribeForCameraChanges];
}];
};
if (self.beganPositionChange != nil)
self.beganPositionChange(targetDevice.hasFlash, [PGCameraCaptureSession _isZoomAvailableForDevice:targetDevice], commitBlock);
return targetCameraPosition;
}
// zoom enable
- (bool)isZoomAvailable
{
return self.captureSession.isZoomAvailable;
}
- (CGFloat)zoomLevel
{
return self.captureSession.zoomLevel;
}
- (void)setZoomLevel:(CGFloat)zoomLevel
{
zoomLevel = MAX(0.0f, MIN(1.0f, zoomLevel));
[[TGPGCamera cameraQueue] dispatch:^
{
if (self.disabled)
return;
[self.captureSession setZoomLevel:zoomLevel];
}];
}
// device angle
- (void)startDeviceAngleMeasuring
{
[_deviceAngleSampler startMeasuring];
}
- (void)stopDeviceAngleMeasuring
{
[_deviceAngleSampler stopMeasuring];
}
// device availability
+ (bool)cameraAvailable
{
#if TARGET_IPHONE_SIMULATOR
return false;
#endif
return [UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera];
}
+ (bool)hasRearCamera
{
return ([PGCameraCaptureSession _deviceWithCameraPosition:PGCameraPositionRear] != nil);
}
+ (bool)hasFrontCamera
{
return ([PGCameraCaptureSession _deviceWithCameraPosition:PGCameraPositionFront] != nil);
}
+ (AVCaptureVideoOrientation)_videoOrientationForInterfaceOrientation:(UIInterfaceOrientation)deviceOrientation mirrored:(bool)mirrored
{
switch (deviceOrientation)
{
case UIInterfaceOrientationPortraitUpsideDown:
return AVCaptureVideoOrientationPortraitUpsideDown;
case UIInterfaceOrientationLandscapeLeft:
return mirrored ? AVCaptureVideoOrientationLandscapeRight : AVCaptureVideoOrientationLandscapeLeft;
case UIInterfaceOrientationLandscapeRight:
return mirrored ? AVCaptureVideoOrientationLandscapeLeft : AVCaptureVideoOrientationLandscapeRight;
default:
return AVCaptureVideoOrientationPortrait;
}
}
+ (PGCameraAuthorizationStatus)cameraAuthorizationStatus
{
if ([AVCaptureDevice respondsToSelector:@selector(authorizationStatusForMediaType:)])
return [TGPGCamera _cameraAuthorizationStatusForAuthorizationStatus:[AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo]];
return PGCameraAuthorizationStatusAuthorized;
}
+ (PGMicrophoneAuthorizationStatus)microphoneAuthorizationStatus
{
if ([AVCaptureDevice respondsToSelector:@selector(authorizationStatusForMediaType:)])
return [TGPGCamera _microphoneAuthorizationStatusForAuthorizationStatus:[AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeAudio]];
return PGMicrophoneAuthorizationStatusAuthorized;
}
+ (PGCameraAuthorizationStatus)_cameraAuthorizationStatusForAuthorizationStatus:(AVAuthorizationStatus)authorizationStatus
{
switch (authorizationStatus)
{
case AVAuthorizationStatusRestricted:
return PGCameraAuthorizationStatusRestricted;
case AVAuthorizationStatusDenied:
return PGCameraAuthorizationStatusDenied;
case AVAuthorizationStatusAuthorized:
return PGCameraAuthorizationStatusAuthorized;
default:
return PGCameraAuthorizationStatusNotDetermined;
}
}
+ (PGMicrophoneAuthorizationStatus)_microphoneAuthorizationStatusForAuthorizationStatus:(AVAuthorizationStatus)authorizationStatus
{
switch (authorizationStatus)
{
case AVAuthorizationStatusRestricted:
return PGMicrophoneAuthorizationStatusRestricted;
case AVAuthorizationStatusDenied:
return PGMicrophoneAuthorizationStatusDenied;
case AVAuthorizationStatusAuthorized:
return PGMicrophoneAuthorizationStatusAuthorized;
default:
return PGMicrophoneAuthorizationStatusNotDetermined;
}
}