Skip to content

Commit

Permalink
fix concurrancy issue if lid closed during photo
Browse files Browse the repository at this point in the history
  • Loading branch information
jacobrosenthal committed Jul 22, 2016
1 parent 596ea9f commit 9f24f00
Show file tree
Hide file tree
Showing 2 changed files with 87 additions and 83 deletions.
121 changes: 64 additions & 57 deletions memoryio/memoryio/ImageSnap.m
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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");
Expand All @@ -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
49 changes: 23 additions & 26 deletions memoryio/memoryio/TSAppDelegate.m
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down Expand Up @@ -158,7 +158,7 @@ -(IBAction)setPhoto:(NSImage*)backgroundImage

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
NSLog(@"Starting memoryio");
verbose("Starting memoryio");

[notificationManager subscribeDisplayNotifications];
[notificationManager subscribePowerNotifications];
Expand All @@ -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;
}
}
Expand Down Expand Up @@ -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

0 comments on commit 9f24f00

Please sign in to comment.