[Media Picker] Fix some retain cycles and fix implicit self warnings.

This commit is contained in:
SBiOSoftWhare 2019-04-24 17:56:03 +02:00
parent fca2437adc
commit 87a6d13855
2 changed files with 144 additions and 120 deletions

View file

@ -75,32 +75,10 @@
*/ */
+ (instancetype)mediaPickerViewController; + (instancetype)mediaPickerViewController;
@property (weak, nonatomic) IBOutlet UIScrollView *mainScrollView;
@property (weak, nonatomic) IBOutlet UIView *captureViewContainer;
//@property (weak, nonatomic) IBOutlet NSLayoutConstraint *captureViewContainerHeightConstraint;
@property (weak, nonatomic) IBOutlet UIView *cameraPreviewContainerView;
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *cameraPreviewContainerAspectRatio;
@property (weak, nonatomic) IBOutlet UIActivityIndicatorView *cameraActivityIndicator;
@property (weak, nonatomic) IBOutlet UIButton *closeButton;
@property (weak, nonatomic) IBOutlet UIButton *cameraSwitchButton;
@property (weak, nonatomic) IBOutlet UIButton *cameraCaptureButton;
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *cameraCaptureButtonWidthConstraint;
@property (weak, nonatomic) IBOutlet MXKPieChartView *cameraVideoCaptureProgressView;
@property (weak, nonatomic) IBOutlet UIView *recentCapturesCollectionContainerView;
@property (weak, nonatomic) IBOutlet UICollectionView *recentCapturesCollectionView;
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *recentCapturesCollectionContainerViewHeightConstraint;
@property (weak, nonatomic) IBOutlet UIView *libraryViewContainer;
@property (weak, nonatomic) IBOutlet UITableView *userAlbumsTableView;
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *libraryViewContainerViewHeightConstraint;
/** /**
The delegate for the view controller. The delegate for the view controller.
*/ */
@property (nonatomic) id<MediaPickerViewControllerDelegate> delegate; @property (nonatomic, weak) id<MediaPickerViewControllerDelegate> delegate;
/** /**
The array of the media types supported by the picker (default value is an array containing kUTTypeImage). The array of the media types supported by the picker (default value is an array containing kUTTypeImage).

View file

@ -94,6 +94,27 @@ static void *RecordingContext = &RecordingContext;
BOOL isStatusBarHidden; BOOL isStatusBarHidden;
} }
@property (weak, nonatomic) IBOutlet UIScrollView *mainScrollView;
@property (weak, nonatomic) IBOutlet UIView *captureViewContainer;
@property (weak, nonatomic) IBOutlet UIView *cameraPreviewContainerView;
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *cameraPreviewContainerAspectRatio;
@property (weak, nonatomic) IBOutlet UIActivityIndicatorView *cameraActivityIndicator;
@property (weak, nonatomic) IBOutlet UIButton *closeButton;
@property (weak, nonatomic) IBOutlet UIButton *cameraSwitchButton;
@property (weak, nonatomic) IBOutlet UIButton *cameraCaptureButton;
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *cameraCaptureButtonWidthConstraint;
@property (weak, nonatomic) IBOutlet MXKPieChartView *cameraVideoCaptureProgressView;
@property (weak, nonatomic) IBOutlet UIView *recentCapturesCollectionContainerView;
@property (weak, nonatomic) IBOutlet UICollectionView *recentCapturesCollectionView;
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *recentCapturesCollectionContainerViewHeightConstraint;
@property (weak, nonatomic) IBOutlet UIView *libraryViewContainer;
@property (weak, nonatomic) IBOutlet UITableView *userAlbumsTableView;
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *libraryViewContainerViewHeightConstraint;
@property (nonatomic) UIBackgroundTaskIdentifier backgroundRecordingID; @property (nonatomic) UIBackgroundTaskIdentifier backgroundRecordingID;
@end @end
@ -163,19 +184,24 @@ static void *RecordingContext = &RecordingContext;
[self setBackgroundRecordingID:UIBackgroundTaskInvalid]; [self setBackgroundRecordingID:UIBackgroundTaskInvalid];
MXWeakify(self);
// Observe UIApplicationWillEnterForegroundNotification to refresh captures collection when app leaves the background state. // Observe UIApplicationWillEnterForegroundNotification to refresh captures collection when app leaves the background state.
UIApplicationWillEnterForegroundNotificationObserver = [[NSNotificationCenter defaultCenter] addObserverForName:UIApplicationWillEnterForegroundNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) { UIApplicationWillEnterForegroundNotificationObserver = [[NSNotificationCenter defaultCenter] addObserverForName:UIApplicationWillEnterForegroundNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) {
MXStrongifyAndReturnIfNil(self);
[self reloadRecentCapturesCollection]; [self reloadRecentCapturesCollection];
[self reloadUserLibraryAlbums]; [self reloadUserLibraryAlbums];
}]; }];
// Observe user interface theme change. // Observe user interface theme change.
kThemeServiceDidChangeThemeNotificationObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kThemeServiceDidChangeThemeNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) { kThemeServiceDidChangeThemeNotificationObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kThemeServiceDidChangeThemeNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) {
MXStrongifyAndReturnIfNil(self);
[self userInterfaceThemeDidChange]; [self userInterfaceThemeDidChange];
}]; }];
[self userInterfaceThemeDidChange]; [self userInterfaceThemeDidChange];
} }
@ -292,7 +318,7 @@ static void *RecordingContext = &RecordingContext;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[self.cameraActivityIndicator stopAnimating]; [self.cameraActivityIndicator stopAnimating];
cameraPreviewLayer.hidden = NO; self->cameraPreviewLayer.hidden = NO;
}); });
}); });
@ -318,7 +344,7 @@ static void *RecordingContext = &RecordingContext;
if (granted) if (granted)
{ {
// Load recent captures if this is not already done // Load recent captures if this is not already done
if (!recentCaptures.count) if (!self->recentCaptures.count)
{ {
dispatch_async(dispatch_get_main_queue(), ^{ dispatch_async(dispatch_get_main_queue(), ^{
@ -564,9 +590,13 @@ static void *RecordingContext = &RecordingContext;
{ {
return; return;
} }
MXWeakify(self);
dispatch_async(userAlbumsQueue, ^{ dispatch_async(userAlbumsQueue, ^{
MXStrongifyAndReturnIfNil(self);
// List user albums which are not empty // List user albums which are not empty
PHFetchResult *albums = [PHAssetCollection fetchAssetCollectionsWithType:PHAssetCollectionTypeSmartAlbum subtype:PHAssetCollectionSubtypeAlbumRegular options:nil]; PHFetchResult *albums = [PHAssetCollection fetchAssetCollectionsWithType:PHAssetCollectionTypeSmartAlbum subtype:PHAssetCollectionSubtypeAlbumRegular options:nil];
@ -575,9 +605,9 @@ static void *RecordingContext = &RecordingContext;
// Set up fetch options. // Set up fetch options.
PHFetchOptions *options = [[PHFetchOptions alloc] init]; PHFetchOptions *options = [[PHFetchOptions alloc] init];
if ([_mediaTypes indexOfObject:(NSString *)kUTTypeImage] != NSNotFound) if ([self->_mediaTypes indexOfObject:(NSString *)kUTTypeImage] != NSNotFound)
{ {
if ([_mediaTypes indexOfObject:(NSString *)kUTTypeMovie] != NSNotFound) if ([self->_mediaTypes indexOfObject:(NSString *)kUTTypeMovie] != NSNotFound)
{ {
options.predicate = [NSPredicate predicateWithFormat:@"(mediaType == %d) || (mediaType == %d)", PHAssetMediaTypeImage, PHAssetMediaTypeVideo]; options.predicate = [NSPredicate predicateWithFormat:@"(mediaType == %d) || (mediaType == %d)", PHAssetMediaTypeImage, PHAssetMediaTypeVideo];
} }
@ -586,7 +616,7 @@ static void *RecordingContext = &RecordingContext;
options.predicate = [NSPredicate predicateWithFormat:@"mediaType == %d",PHAssetMediaTypeImage]; options.predicate = [NSPredicate predicateWithFormat:@"mediaType == %d",PHAssetMediaTypeImage];
} }
} }
else if ([_mediaTypes indexOfObject:(NSString *)kUTTypeMovie] != NSNotFound) else if ([self->_mediaTypes indexOfObject:(NSString *)kUTTypeMovie] != NSNotFound)
{ {
options.predicate = [NSPredicate predicateWithFormat:@"mediaType == %d",PHAssetMediaTypeVideo]; options.predicate = [NSPredicate predicateWithFormat:@"mediaType == %d",PHAssetMediaTypeVideo];
} }
@ -626,11 +656,11 @@ static void *RecordingContext = &RecordingContext;
dispatch_async(dispatch_get_main_queue(), ^{ dispatch_async(dispatch_get_main_queue(), ^{
userAlbums = updatedUserAlbums; self->userAlbums = updatedUserAlbums;
if (userAlbums.count) if (self->userAlbums.count)
{ {
self.userAlbumsTableView.hidden = NO; self.userAlbumsTableView.hidden = NO;
self.libraryViewContainerViewHeightConstraint.constant = (userAlbums.count * 74); self.libraryViewContainerViewHeightConstraint.constant = (self->userAlbums.count * 74);
[self.libraryViewContainer needsUpdateConstraints]; [self.libraryViewContainer needsUpdateConstraints];
[self.userAlbumsTableView reloadData]; [self.userAlbumsTableView reloadData];
@ -759,13 +789,13 @@ static void *RecordingContext = &RecordingContext;
}]; }];
} }
isValidationInProgress = NO; self->isValidationInProgress = NO;
}]; }];
} }
else else
{ {
NSLog(@"[MediaPickerVC] didSelectAsset: Failed to get image for asset"); NSLog(@"[MediaPickerVC] didSelectAsset: Failed to get image for asset");
isValidationInProgress = NO; self->isValidationInProgress = NO;
// Alert user // Alert user
NSError *error = info[@"PHImageErrorKey"]; NSError *error = info[@"PHImageErrorKey"];
@ -815,20 +845,20 @@ static void *RecordingContext = &RecordingContext;
[self.delegate mediaPickerController:self didSelectVideo:[avURLAsset URL]]; [self.delegate mediaPickerController:self didSelectVideo:[avURLAsset URL]];
} }
isValidationInProgress = NO; self->isValidationInProgress = NO;
}]; }];
} }
else else
{ {
NSLog(@"[MediaPickerVC] Selected video asset is not initialized from an URL!"); NSLog(@"[MediaPickerVC] Selected video asset is not initialized from an URL!");
isValidationInProgress = NO; self->isValidationInProgress = NO;
} }
} }
else else
{ {
NSLog(@"[MediaPickerVC] didSelectAsset: Failed to get image for asset"); NSLog(@"[MediaPickerVC] didSelectAsset: Failed to get image for asset");
isValidationInProgress = NO; self->isValidationInProgress = NO;
// Alert user // Alert user
NSError *error = info[@"PHImageErrorKey"]; NSError *error = info[@"PHImageErrorKey"];
@ -1103,8 +1133,12 @@ static void *RecordingContext = &RecordingContext;
[self.cameraActivityIndicator startAnimating]; [self.cameraActivityIndicator startAnimating];
MXWeakify(self);
dispatch_async(cameraQueue, ^{ dispatch_async(cameraQueue, ^{
MXStrongifyAndReturnIfNil(self);
// Get the Camera Device // Get the Camera Device
AVCaptureDevice *frontCamera = nil; AVCaptureDevice *frontCamera = nil;
AVCaptureDevice *backCamera = nil; AVCaptureDevice *backCamera = nil;
@ -1151,42 +1185,42 @@ static void *RecordingContext = &RecordingContext;
} }
} }
currentCameraInput = nil; self->currentCameraInput = nil;
NSError *error = nil; NSError *error = nil;
if (frontCamera) if (frontCamera)
{ {
frontCameraInput = [[AVCaptureDeviceInput alloc] initWithDevice:frontCamera error:&error]; self->frontCameraInput = [[AVCaptureDeviceInput alloc] initWithDevice:frontCamera error:&error];
if (error) if (error)
{ {
NSLog(@"[MediaPickerVC] Error: %@", error); NSLog(@"[MediaPickerVC] Error: %@", error);
} }
if (frontCameraInput == nil) if (self->frontCameraInput == nil)
{ {
NSLog(@"[MediaPickerVC] Error creating front camera capture input"); NSLog(@"[MediaPickerVC] Error creating front camera capture input");
} }
else else
{ {
currentCameraInput = frontCameraInput; self->currentCameraInput = self->frontCameraInput;
} }
} }
if (backCamera) if (backCamera)
{ {
error = nil; error = nil;
backCameraInput = [[AVCaptureDeviceInput alloc] initWithDevice:backCamera error:&error]; self->backCameraInput = [[AVCaptureDeviceInput alloc] initWithDevice:backCamera error:&error];
if (error) if (error)
{ {
NSLog(@"[MediaPickerVC] Error: %@", error); NSLog(@"[MediaPickerVC] Error: %@", error);
} }
if (backCameraInput == nil) if (self->backCameraInput == nil)
{ {
NSLog(@"[MediaPickerVC] Error creating back camera capture input"); NSLog(@"[MediaPickerVC] Error creating back camera capture input");
} }
else else
{ {
currentCameraInput = backCameraInput; self->currentCameraInput = self->backCameraInput;
} }
} }
@ -1194,38 +1228,38 @@ static void *RecordingContext = &RecordingContext;
self.cameraSwitchButton.hidden = (!frontCamera || !backCamera); self.cameraSwitchButton.hidden = (!frontCamera || !backCamera);
}); });
if (currentCameraInput) if (self->currentCameraInput)
{ {
// Create the AVCapture Session // Create the AVCapture Session
captureSession = [[AVCaptureSession alloc] init]; self->captureSession = [[AVCaptureSession alloc] init];
if (isPictureCaptureEnabled) if (self->isPictureCaptureEnabled)
{ {
[captureSession setSessionPreset:AVCaptureSessionPresetPhoto]; [self->captureSession setSessionPreset:AVCaptureSessionPresetPhoto];
} }
else if (isVideoCaptureEnabled) else if (self->isVideoCaptureEnabled)
{ {
[captureSession setSessionPreset:AVCaptureSessionPresetHigh]; [self->captureSession setSessionPreset:AVCaptureSessionPresetHigh];
} }
cameraPreviewLayer = [[AVCaptureVideoPreviewLayer alloc] initWithSession:captureSession]; self->cameraPreviewLayer = [[AVCaptureVideoPreviewLayer alloc] initWithSession:self->captureSession];
cameraPreviewLayer.masksToBounds = NO; self->cameraPreviewLayer.masksToBounds = NO;
cameraPreviewLayer.videoGravity = AVLayerVideoGravityResizeAspectFill;//AVLayerVideoGravityResizeAspect; self->cameraPreviewLayer.videoGravity = AVLayerVideoGravityResizeAspectFill;//AVLayerVideoGravityResizeAspect;
cameraPreviewLayer.backgroundColor = [[UIColor blackColor] CGColor]; self->cameraPreviewLayer.backgroundColor = [[UIColor blackColor] CGColor];
// cameraPreviewLayer.borderWidth = 2; // cameraPreviewLayer.borderWidth = 2;
dispatch_async(dispatch_get_main_queue(), ^{ dispatch_async(dispatch_get_main_queue(), ^{
[[cameraPreviewLayer connection] setVideoOrientation:(AVCaptureVideoOrientation)[[UIApplication sharedApplication] statusBarOrientation]]; [[self->cameraPreviewLayer connection] setVideoOrientation:(AVCaptureVideoOrientation)[[UIApplication sharedApplication] statusBarOrientation]];
[cameraPreviewLayer connection].videoScaleAndCropFactor = 1.0; [self->cameraPreviewLayer connection].videoScaleAndCropFactor = 1.0;
cameraPreviewLayer.frame = self.cameraPreviewContainerView.bounds; self->cameraPreviewLayer.frame = self.cameraPreviewContainerView.bounds;
cameraPreviewLayer.hidden = YES; self->cameraPreviewLayer.hidden = YES;
[self.cameraPreviewContainerView.layer addSublayer:cameraPreviewLayer]; [self.cameraPreviewContainerView.layer addSublayer:self->cameraPreviewLayer];
}); });
[captureSession addInput:currentCameraInput]; [self->captureSession addInput:self->currentCameraInput];
AVCaptureDevice *audioDevice = [[AVCaptureDevice devicesWithMediaType:AVMediaTypeAudio] firstObject]; AVCaptureDevice *audioDevice = [[AVCaptureDevice devicesWithMediaType:AVMediaTypeAudio] firstObject];
AVCaptureDeviceInput *audioDeviceInput = [AVCaptureDeviceInput deviceInputWithDevice:audioDevice error:&error]; AVCaptureDeviceInput *audioDeviceInput = [AVCaptureDeviceInput deviceInputWithDevice:audioDevice error:&error];
@ -1235,36 +1269,36 @@ static void *RecordingContext = &RecordingContext;
NSLog(@"[MediaPickerVC] Error: %@", error); NSLog(@"[MediaPickerVC] Error: %@", error);
} }
if ([captureSession canAddInput:audioDeviceInput]) if ([self->captureSession canAddInput:audioDeviceInput])
{ {
[captureSession addInput:audioDeviceInput]; [self->captureSession addInput:audioDeviceInput];
} }
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(caughtAVRuntimeError:) name:AVCaptureSessionRuntimeErrorNotification object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(caughtAVRuntimeError:) name:AVCaptureSessionRuntimeErrorNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(AVCaptureSessionDidStartRunning:) name:AVCaptureSessionDidStartRunningNotification object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(AVCaptureSessionDidStartRunning:) name:AVCaptureSessionDidStartRunningNotification object:nil];
[captureSession startRunning]; [self->captureSession startRunning];
movieFileOutput = [[AVCaptureMovieFileOutput alloc] init]; self->movieFileOutput = [[AVCaptureMovieFileOutput alloc] init];
if ([captureSession canAddOutput:movieFileOutput]) if ([self->captureSession canAddOutput:self->movieFileOutput])
{ {
[captureSession addOutput:movieFileOutput]; [self->captureSession addOutput:self->movieFileOutput];
AVCaptureConnection *connection = [movieFileOutput connectionWithMediaType:AVMediaTypeVideo]; AVCaptureConnection *connection = [self->movieFileOutput connectionWithMediaType:AVMediaTypeVideo];
if ([connection isVideoStabilizationSupported]) if ([connection isVideoStabilizationSupported])
{ {
// Available on iOS 8 and later // Available on iOS 8 and later
[connection setPreferredVideoStabilizationMode:YES]; [connection setPreferredVideoStabilizationMode:YES];
} }
} }
[movieFileOutput addObserver:self forKeyPath:@"recording" options:(NSKeyValueObservingOptionOld | NSKeyValueObservingOptionNew) context:RecordingContext]; [self->movieFileOutput addObserver:self forKeyPath:@"recording" options:(NSKeyValueObservingOptionOld | NSKeyValueObservingOptionNew) context:RecordingContext];
stillImageOutput = [[AVCaptureStillImageOutput alloc] init]; self->stillImageOutput = [[AVCaptureStillImageOutput alloc] init];
if ([captureSession canAddOutput:stillImageOutput]) if ([self->captureSession canAddOutput:self->stillImageOutput])
{ {
[stillImageOutput setOutputSettings:@{AVVideoCodecKey : AVVideoCodecJPEG}]; [self->stillImageOutput setOutputSettings:@{AVVideoCodecKey : AVVideoCodecJPEG}];
[captureSession addOutput:stillImageOutput]; [self->captureSession addOutput:self->stillImageOutput];
} }
[stillImageOutput addObserver:self forKeyPath:@"capturingStillImage" options:(NSKeyValueObservingOptionOld | NSKeyValueObservingOptionNew) context:CapturingStillImageContext]; [self->stillImageOutput addObserver:self forKeyPath:@"capturingStillImage" options:(NSKeyValueObservingOptionOld | NSKeyValueObservingOptionNew) context:CapturingStillImageContext];
} }
else else
{ {
@ -1273,7 +1307,7 @@ static void *RecordingContext = &RecordingContext;
}); });
} }
isCaptureSessionSetupInProgress = NO; self->isCaptureSessionSetupInProgress = NO;
}); });
} }
@ -1300,23 +1334,24 @@ static void *RecordingContext = &RecordingContext;
} }
dispatch_sync(cameraQueue, ^{ dispatch_sync(cameraQueue, ^{
frontCameraInput = nil;
backCameraInput = nil; self->frontCameraInput = nil;
captureSession = nil; self->backCameraInput = nil;
self->captureSession = nil;
if (movieFileOutput) if (self->movieFileOutput)
{ {
[movieFileOutput removeObserver:self forKeyPath:@"recording" context:RecordingContext]; [self->movieFileOutput removeObserver:self forKeyPath:@"recording" context:RecordingContext];
movieFileOutput = nil; self->movieFileOutput = nil;
} }
if (stillImageOutput) if (self->stillImageOutput)
{ {
[stillImageOutput removeObserver:self forKeyPath:@"capturingStillImage" context:CapturingStillImageContext]; [self->stillImageOutput removeObserver:self forKeyPath:@"capturingStillImage" context:CapturingStillImageContext];
stillImageOutput = nil; self->stillImageOutput = nil;
} }
currentCameraInput = nil; self->currentCameraInput = nil;
[[NSNotificationCenter defaultCenter] removeObserver:self name:AVCaptureSessionRuntimeErrorNotification object:nil]; [[NSNotificationCenter defaultCenter] removeObserver:self name:AVCaptureSessionRuntimeErrorNotification object:nil];
[[NSNotificationCenter defaultCenter] removeObserver:self name:AVCaptureSessionDidStartRunningNotification object:nil]; [[NSNotificationCenter defaultCenter] removeObserver:self name:AVCaptureSessionDidStartRunningNotification object:nil];
@ -1342,7 +1377,7 @@ static void *RecordingContext = &RecordingContext;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.6 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.6 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[self.cameraActivityIndicator stopAnimating]; [self.cameraActivityIndicator stopAnimating];
cameraPreviewLayer.hidden = NO; self->cameraPreviewLayer.hidden = NO;
}); });
} }
@ -1357,45 +1392,45 @@ static void *RecordingContext = &RecordingContext;
if (frontCameraInput && backCameraInput) if (frontCameraInput && backCameraInput)
{ {
dispatch_async(dispatch_get_main_queue(), ^{ dispatch_async(dispatch_get_main_queue(), ^{
if (!canToggleCamera) if (!self->canToggleCamera)
{ {
return; return;
} }
canToggleCamera = NO; self->canToggleCamera = NO;
AVCaptureDeviceInput *newInput = nil; AVCaptureDeviceInput *newInput = nil;
AVCaptureDeviceInput *oldInput = nil; AVCaptureDeviceInput *oldInput = nil;
if (currentCameraInput == frontCameraInput) if (self->currentCameraInput == self->frontCameraInput)
{ {
newInput = backCameraInput; newInput = self->backCameraInput;
oldInput = frontCameraInput; oldInput = self->frontCameraInput;
} }
else else
{ {
newInput = frontCameraInput; newInput = self->frontCameraInput;
oldInput = backCameraInput; oldInput = self->backCameraInput;
} }
dispatch_async(cameraQueue, ^{ dispatch_async(self->cameraQueue, ^{
[captureSession beginConfiguration]; [self->captureSession beginConfiguration];
[captureSession removeInput:oldInput]; [self->captureSession removeInput:oldInput];
if ([captureSession canAddInput:newInput]) { if ([self->captureSession canAddInput:newInput]) {
[captureSession addInput:newInput]; [self->captureSession addInput:newInput];
currentCameraInput = newInput; self->currentCameraInput = newInput;
} }
[captureSession commitConfiguration]; [self->captureSession commitConfiguration];
dispatch_async(dispatch_get_main_queue(), ^{ dispatch_async(dispatch_get_main_queue(), ^{
[self.cameraActivityIndicator stopAnimating]; [self.cameraActivityIndicator stopAnimating];
cameraPreviewLayer.hidden = NO; self->cameraPreviewLayer.hidden = NO;
canToggleCamera = YES; self->canToggleCamera = YES;
}); });
}); });
[self.cameraActivityIndicator startAnimating]; [self.cameraActivityIndicator startAnimating];
cameraPreviewLayer.hidden = YES; self->cameraPreviewLayer.hidden = YES;
}); });
} }
} }
@ -1404,10 +1439,15 @@ static void *RecordingContext = &RecordingContext;
{ {
self.cameraCaptureButton.enabled = NO; self.cameraCaptureButton.enabled = NO;
MXWeakify(self);
dispatch_async(cameraQueue, ^{ dispatch_async(cameraQueue, ^{
if (![movieFileOutput isRecording])
MXStrongifyAndReturnIfNil(self);
if (![self->movieFileOutput isRecording])
{ {
lockInterfaceRotation = YES; self->lockInterfaceRotation = YES;
if ([[UIDevice currentDevice] isMultitaskingSupported]) if ([[UIDevice currentDevice] isMultitaskingSupported])
{ {
@ -1425,14 +1465,14 @@ static void *RecordingContext = &RecordingContext;
} }
// Update the orientation on the movie file output video connection before starting recording. // Update the orientation on the movie file output video connection before starting recording.
[[movieFileOutput connectionWithMediaType:AVMediaTypeVideo] setVideoOrientation:[[cameraPreviewLayer connection] videoOrientation]]; [[self->movieFileOutput connectionWithMediaType:AVMediaTypeVideo] setVideoOrientation:[[self->cameraPreviewLayer connection] videoOrientation]];
// Turning OFF flash for video recording // Turning OFF flash for video recording
[MediaPickerViewController setFlashMode:AVCaptureFlashModeOff forDevice:[currentCameraInput device]]; [MediaPickerViewController setFlashMode:AVCaptureFlashModeOff forDevice:[self->currentCameraInput device]];
// Start recording to a temporary file. // Start recording to a temporary file.
NSString *outputFilePath = [NSTemporaryDirectory() stringByAppendingPathComponent:[@"movie" stringByAppendingPathExtension:@"mov"]]; NSString *outputFilePath = [NSTemporaryDirectory() stringByAppendingPathComponent:[@"movie" stringByAppendingPathExtension:@"mov"]];
[movieFileOutput startRecordingToOutputFileURL:[NSURL fileURLWithPath:outputFilePath] recordingDelegate:self]; [self->movieFileOutput startRecordingToOutputFileURL:[NSURL fileURLWithPath:outputFilePath] recordingDelegate:self];
} }
}); });
} }
@ -1440,9 +1480,10 @@ static void *RecordingContext = &RecordingContext;
- (void)stopMovieRecording - (void)stopMovieRecording
{ {
dispatch_async(cameraQueue, ^{ dispatch_async(cameraQueue, ^{
if ([movieFileOutput isRecording])
if ([self->movieFileOutput isRecording])
{ {
[movieFileOutput stopRecording]; [self->movieFileOutput stopRecording];
} }
}); });
} }
@ -1451,15 +1492,20 @@ static void *RecordingContext = &RecordingContext;
{ {
self.cameraCaptureButton.enabled = NO; self.cameraCaptureButton.enabled = NO;
MXWeakify(self);
dispatch_async(cameraQueue, ^{ dispatch_async(cameraQueue, ^{
MXStrongifyAndReturnIfNil(self);
// Update the orientation on the still image output video connection before capturing. // Update the orientation on the still image output video connection before capturing.
[[stillImageOutput connectionWithMediaType:AVMediaTypeVideo] setVideoOrientation:[[cameraPreviewLayer connection] videoOrientation]]; [[self->stillImageOutput connectionWithMediaType:AVMediaTypeVideo] setVideoOrientation:[[self->cameraPreviewLayer connection] videoOrientation]];
// Flash set to Auto for Still Capture // Flash set to Auto for Still Capture
[MediaPickerViewController setFlashMode:AVCaptureFlashModeAuto forDevice:[currentCameraInput device]]; [MediaPickerViewController setFlashMode:AVCaptureFlashModeAuto forDevice:[self->currentCameraInput device]];
// Capture a still image. // Capture a still image.
[stillImageOutput captureStillImageAsynchronouslyFromConnection:[stillImageOutput connectionWithMediaType:AVMediaTypeVideo] completionHandler:^(CMSampleBufferRef imageDataSampleBuffer, NSError *error) { [self->stillImageOutput captureStillImageAsynchronouslyFromConnection:[self->stillImageOutput connectionWithMediaType:AVMediaTypeVideo] completionHandler:^(CMSampleBufferRef imageDataSampleBuffer, NSError *error) {
if (imageDataSampleBuffer) if (imageDataSampleBuffer)
{ {
@ -1504,10 +1550,10 @@ static void *RecordingContext = &RecordingContext;
- (void)runStillImageCaptureAnimation - (void)runStillImageCaptureAnimation
{ {
dispatch_async(dispatch_get_main_queue(), ^{ dispatch_async(dispatch_get_main_queue(), ^{
[cameraPreviewLayer setOpacity:0.0]; [self->cameraPreviewLayer setOpacity:0.0];
[UIView animateWithDuration:.25 animations:^{ [UIView animateWithDuration:.25 animations:^{
[cameraPreviewLayer setOpacity:1.0]; [self->cameraPreviewLayer setOpacity:1.0];
}]; }];
}); });
} }
@ -1535,18 +1581,18 @@ static void *RecordingContext = &RecordingContext;
{ {
self.cameraSwitchButton.enabled = NO; self.cameraSwitchButton.enabled = NO;
videoRecordStartDate = [NSDate date]; self->videoRecordStartDate = [NSDate date];
self.cameraVideoCaptureProgressView.hidden = NO; self.cameraVideoCaptureProgressView.hidden = NO;
updateVideoRecordingTimer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(updateVideoRecordingDuration) userInfo:nil repeats:YES]; self->updateVideoRecordingTimer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(updateVideoRecordingDuration) userInfo:nil repeats:YES];
self.cameraCaptureButton.enabled = YES; self.cameraCaptureButton.enabled = YES;
} }
else else
{ {
self.cameraVideoCaptureProgressView.hidden = YES; self.cameraVideoCaptureProgressView.hidden = YES;
[updateVideoRecordingTimer invalidate]; [self->updateVideoRecordingTimer invalidate];
updateVideoRecordingTimer = nil; self->updateVideoRecordingTimer = nil;
self.cameraVideoCaptureProgressView.progress = 0; self.cameraVideoCaptureProgressView.progress = 0;
// The preview will be restored during captureOutput:didFinishRecordingToOutputFileAtURL: callback. // The preview will be restored during captureOutput:didFinishRecordingToOutputFileAtURL: callback.