From 9f24f00f40f0f22f0a6e06e05028cae0bef52f65 Mon Sep 17 00:00:00 2001 From: Jacob Rosenthal Date: Fri, 22 Jul 2016 14:14:53 -0700 Subject: [PATCH] fix concurrancy issue if lid closed during photo --- memoryio/memoryio/ImageSnap.m | 121 ++++++++++++++++-------------- memoryio/memoryio/TSAppDelegate.m | 49 ++++++------ 2 files changed, 87 insertions(+), 83 deletions(-) diff --git a/memoryio/memoryio/ImageSnap.m b/memoryio/memoryio/ImageSnap.m index 13324f0..cea1d7d 100644 --- a/memoryio/memoryio/ImageSnap.m +++ b/memoryio/memoryio/ImageSnap.m @@ -19,13 +19,6 @@ @interface ImageSnap() @property (nonatomic, strong) AVCaptureStillImageOutput *captureStillImageOutput; @property (nonatomic, assign) CVImageBufferRef currentImageBuffer; @property (nonatomic, strong) AVCaptureConnection *videoConnection; -@property (nonatomic, strong) NSDateFormatter *dateFormatter; - -#if OS_OBJECT_HAVE_OBJC_SUPPORT == 1 -@property (nonatomic, strong) dispatch_queue_t imageQueue; -#else -@property (nonatomic, assign) dispatch_queue_t imageQueue; -#endif @end @@ -76,11 +69,30 @@ + (AVCaptureDevice *)deviceNamed:(NSString *)name { return result; } ++ (NSURL *)NSURLfromPath:(NSString *)path andDate:(NSDate *)now{ + + NSDateFormatter *dateFormatter; + dateFormatter = [NSDateFormatter new]; + dateFormatter.dateFormat = @"yyyy-MM-dd_HH-mm-ss.SSS"; + + NSString *nowstr = [dateFormatter stringFromDate:now]; + + NSString *pathAndFilename = [NSString stringWithFormat:@"%@%@%@", path, nowstr, @".jpg"]; + + return [NSURL fileURLWithPath:pathAndFilename isDirectory:NO]; +} + + (void)saveSingleSnapshotFrom:(AVCaptureDevice *)device toPath:(NSString *)path withWarmup:(NSNumber *)warmup withCallbackBlock:(void (^)(NSURL *imageURL, NSError *error))callbackBlock{ + NSOperationQueue *queue = [NSOperationQueue new]; + queue.maxConcurrentOperationCount =1; + + NSDate *now = [NSDate date]; + NSURL *imageURL = [self NSURLfromPath:path andDate:now]; + verbose("Starting device..."); NSError *error; @@ -126,6 +138,30 @@ + (void)saveSingleSnapshotFrom:(AVCaptureDevice *)device verbose("Device started.\n"); + void (^stopSession)(void) = ^void(void) { + verbose("Stopping session...\n"); + + // Make sure we've stopped + while (captureSession != nil) { + verbose("\tCaptureSession != nil\n"); + + verbose("\tStopping CaptureSession..."); + [captureSession stopRunning]; + verbose("Done.\n"); + + if ([captureSession isRunning]) { + verbose("[captureSession isRunning]"); + [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.1]]; + } else { + verbose("\tShutting down 'stopSession(..)'" ); + + captureSession = nil; + captureDeviceInput = nil; + captureStillImageOutput = nil; + } + } + }; + if (warmup == nil) { // Skip warmup verbose("Skipping warmup period.\n"); @@ -136,61 +172,32 @@ + (void)saveSingleSnapshotFrom:(AVCaptureDevice *)device verbose("Warmup complete.\n"); } - [captureStillImageOutput captureStillImageAsynchronouslyFromConnection:videoConnection - completionHandler: - ^(CMSampleBufferRef imageDataSampleBuffer, NSError *error) { - - NSDateFormatter *dateFormatter; - dateFormatter = [NSDateFormatter new]; - dateFormatter.dateFormat = @"yyyy-MM-dd_HH-mm-ss.SSS"; - - NSDate *now = [NSDate date]; - NSString *nowstr = [dateFormatter stringFromDate:now]; - - NSString *pathAndFilename = [NSString stringWithFormat:@"%@%@%@", path, nowstr, @".jpg"]; - - NSURL *imageURL = [NSURL fileURLWithPath:pathAndFilename isDirectory:NO]; - - NSLog(@"Making exif data"); - ExifContainer *container = [[ExifContainer alloc] init]; - [container addCreationDate:now]; - [container addDigitizedDate:now]; - - NSData *rawImageData = [AVCaptureStillImageOutput jpegStillImageNSDataRepresentation:imageDataSampleBuffer]; + void(^saveImage)(CMSampleBufferRef imageDataSampleBuffer, NSError *error) = ^void(CMSampleBufferRef imageDataSampleBuffer, NSError *error) { - NSData *imageData = [NSImage getAppendedDataForImageData:rawImageData exif:container]; - - dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void) { - - [imageData writeToURL:imageURL atomically:YES]; - - verbose("Stopping session...\n" ); - - // Make sure we've stopped - while (captureSession != nil) { - verbose("\tCaptureSession != nil\n"); - - verbose("\tStopping CaptureSession..."); - [captureSession stopRunning]; - verbose("Done.\n"); + // usually happens if you close lid while its grabbing a photos + if(error){ + [queue addOperationWithBlock:stopSession]; + callbackBlock(NULL, error); + return; + } - if ([captureSession isRunning]) { - verbose("[captureSession isRunning]"); - [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.1]]; - } else { - verbose("\tShutting down 'stopSession(..)'" ); + verbose("Making exif data"); + ExifContainer *container = [[ExifContainer alloc] init]; + [container addCreationDate:now]; + [container addDigitizedDate:now]; - captureSession = nil; - captureDeviceInput = nil; - captureStillImageOutput = nil; - } - } + NSData *rawImageData = [AVCaptureStillImageOutput jpegStillImageNSDataRepresentation:imageDataSampleBuffer]; + NSData *imageData = [NSImage getAppendedDataForImageData:rawImageData exif:container]; - }); + [queue addOperationWithBlock:^void(void) { + [imageData writeToURL:imageURL atomically:YES]; + }]; + [queue addOperationWithBlock:stopSession]; + callbackBlock(imageURL, error); + }; - verbose("Callback...\n" ); - callbackBlock(imageURL, error); - }]; + [captureStillImageOutput captureStillImageAsynchronouslyFromConnection:videoConnection + completionHandler:saveImage]; } @end \ No newline at end of file diff --git a/memoryio/memoryio/TSAppDelegate.m b/memoryio/memoryio/TSAppDelegate.m index b937443..bc7cc4a 100755 --- a/memoryio/memoryio/TSAppDelegate.m +++ b/memoryio/memoryio/TSAppDelegate.m @@ -29,11 +29,11 @@ - (void)awakeFromNib { case kIOMessageDeviceHasPoweredOn : // mainly for when the display goesto sleep and wakes up - NSLog(@"powerMessageReceived: got a kIOMessageDeviceHasPoweredOn - device powered on"); + verbose("powerMessageReceived: got a kIOMessageDeviceHasPoweredOn - device powered on"); break; case kIOMessageSystemHasPoweredOn: // mainly for when the system goes to sleep and wakes up - NSLog(@"powerMessageReceived: got a kIOMessageSystemHasPoweredOn - system powered on"); + verbose("powerMessageReceived: got a kIOMessageSystemHasPoweredOn - system powered on"); [weakSelf takePhotoWithDelay:2.0f]; break; } @@ -158,7 +158,7 @@ -(IBAction)setPhoto:(NSImage*)backgroundImage - (void)applicationDidFinishLaunching:(NSNotification *)aNotification { - NSLog(@"Starting memoryio"); + verbose("Starting memoryio"); [notificationManager subscribeDisplayNotifications]; [notificationManager subscribePowerNotifications]; @@ -176,15 +176,15 @@ - (void)userNotificationCenter:(NSUserNotificationCenter *)center didActivateNot [center removeDeliveredNotification:notification]; switch (notification.activationType) { case NSUserNotificationActivationTypeActionButtonClicked: - NSLog(@"Reply Button was clicked -> quick reply"); + verbose("Reply Button was clicked -> quick reply"); break; case NSUserNotificationActivationTypeContentsClicked: - NSLog(@"Notification body was clicked -> redirect to item"); + verbose("Notification body was clicked -> redirect to item"); [self preview:nil]; [NSApp activateIgnoringOtherApps:YES]; break; default: - NSLog(@"Notfiication appears to have been dismissed!"); + verbose("Notfiication appears to have been dismissed!"); break; } } @@ -228,31 +228,28 @@ - (void) postNotification:(NSString *) informativeText withActionBoolean:(BOOL)h - (void) takePhotoWithDelay: (float) delay { - dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ + NSString *path = [NSString stringWithFormat:@"%@%@", NSHomeDirectory(), @"/Pictures/memoryIO/"]; - NSString *path = [NSString stringWithFormat:@"%@%@", NSHomeDirectory(), @"/Pictures/memoryIO/"]; + // create directory if it doesnt exist + NSFileManager *fileManager= [NSFileManager defaultManager]; + NSError *error = nil; + [fileManager createDirectoryAtPath:path withIntermediateDirectories:YES attributes:nil error:&error]; - NSFileManager *fileManager= [NSFileManager defaultManager]; - NSError *error = nil; - [fileManager createDirectoryAtPath:path withIntermediateDirectories:YES attributes:nil error:&error]; + typeof(self) __weak weakSelf = self; + [ImageSnap saveSingleSnapshotFrom:[ImageSnap defaultVideoDevice] + toPath:path + withWarmup:[NSNumber numberWithInt:delay] + withCallbackBlock:^(NSURL *imageURL, NSError *error) { - typeof(self) __weak weakSelf = self; - [ImageSnap saveSingleSnapshotFrom:[ImageSnap defaultVideoDevice] - toPath:path - withWarmup:[NSNumber numberWithInt:delay] - withCallbackBlock:^(NSURL *imageURL, NSError *error) { + if(error) + { + [weakSelf postNotification:@"There was a problem taking that shot :(" withActionBoolean:false]; + } else { + [weakSelf postNotification:@"Well, Look at you!" withActionBoolean:true]; + } - dispatch_async(dispatch_get_main_queue(), ^{ - if(error) - { - [weakSelf postNotification:@"There was a problem taking that shot :(" withActionBoolean:false]; - } else { - [weakSelf postNotification:@"Well, Look at you!" withActionBoolean:true]; - } - }); // end of dispatch_async main thread - }]; // end of callback Block - }); // end of dispatch_async main thread + }]; // end of callback Block } @end