3
votes

I'm attempting to take and save photos with the camera rapidly, as quickly as the iPhone can. The problem is that they don't save until the end and it then takes forever, or about 1/2 to 3/4 don't save at all (Write busy error or -[NSKeyedUnarchiver initForReadingWithData:]: data is NULL).

I bet I'm just overloading the phone's memory, but I can't think of a way to handle it efficiently. The standard iPhone camera app can handle it just fine -- snap away at almost 1 photo/second and it saves with no problem.

Any ideas on how to manage the process/memory better so that it can save as it goes but still shoot rapidly?

Here's a bit of my code. takePicture is called whenever self.readyToTake = YES.

- (void)takePicture {
    self.delegate = self;
    [super takePicture];
    self.readyToTake = NO;
}

- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info {
    self.readyToTake = YES;
    UIImage *image = [info objectForKey:UIImagePickerControllerOriginalImage];
    UIImageWriteToSavedPhotosAlbum(image, self, @selector(image:didFinishSavingWithError:contextInfo:), nil);
}

- (void)image:(UIImage*)image didFinishSavingWithError:(NSError *)error contextInfo:(NSDictionary*)info {
    if (error)
    {
        NSLog([NSString stringWithFormat:@"** ERROR SAVING PHOTO: %@", [error localizedDescription]]);
    }
}

Thank you for your help!


EDIT

If I resize the photos to much smaller dimensions before saving, like 480x640, I have no problem saving quickly. However, I'm wanting to capture and save full-size images. The native Camera app seems to handle it fine.

3

3 Answers

2
votes

you can do like this. ie., enable taking the next photo only after the previous photo is saved. Also introduce autorelease pool for memory management.

- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info {
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    UIImage *image = [info objectForKey:UIImagePickerControllerOriginalImage];
    UIImageWriteToSavedPhotosAlbum(image, self, @selector(image:didFinishSavingWithError:contextInfo:), nil);
    [pool drain];
}


- (void)image:(UIImage*)image didFinishSavingWithError:(NSError *)error contextInfo:(NSDictionary*)info {
    if (error)
    {
        NSLog([NSString stringWithFormat:@"** ERROR SAVING PHOTO: %@", [error localizedDescription]]);
    }
    self.readyToTake = YES;
}
1
votes

The first thing I'd try is offloading those calls asynchronously to a dispatch queue:

- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info {
    self.readyToTake = YES;
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
        UIImage *image = [info objectForKey:UIImagePickerControllerOriginalImage];
        UIImageWriteToSavedPhotosAlbum(image, self, @selector(image:didFinishSavingWithError:contextInfo:), nil);
    });
}

If you're still having trouble, I'd look into AVCaptureSession and friends. This gives you a lot more control and generally better performance. There's a great sample project using this method at the developer portal: SquareCam

EDIT

You could also try using AssetsLibrary instead of UIImageWriteToSavedPhotosAlbum (This is what we use in all our apps) Something like:

- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info {
    self.readyToTake = YES;
    // you probably want to create this once and keep it around as a property
    // on your controller
    ALAssetsLibrary* library = [[ALAssetsLibrary alloc] init];
    UIImage *image = [info objectForKey:UIImagePickerControllerOriginalImage];
    [library writeImageToSavedPhotosAlbum:image.CGImageRef orientation:(ALAssetOrientation)image.orientation completionBlock:^(NSURL *assetURL, NSError *error) {
         // do whatever you need to do when the image is saved
     }];
}
1
votes

there is one best example app on developer.apple.com site in which the solution for your problem is shown in it they save photos for 1second interval automatically when user tap on "timed" button here is its code it surely helps you as i can able to store 5 consicutive photos 1/second speed

Please see page MyViewController.m and after getting images in array you can save them to the photo album using some function on another queue