4
votes

I'm trying to capture continuous (multi-shot) high-res images using captureStillImageAsynchronouslyFromConnection in a loop, but it occasionally pauses to refocus. I locked the focus mode (as described in other stackoverflow postings), but that didn't prevent the camera from occasionally refocusing. My code snippet is:

// [self.session beginConfiguration];
if ([device lockForConfiguration:nil] == YES) {
    if ([device isFocusModeSupported:AVCaptureFocusModeLocked]) {
        [device setFocusMode:AVCaptureFocusModeLocked];
        NSLog(@"focus locked");
    }
    if ([device isExposureModeSupported:AVCaptureExposureModeLocked]) {
        [device setExposureMode:AVCaptureExposureModeLocked];
        NSLog(@"exposure locked");
    }
    if ([device isWhiteBalanceModeSupported:AVCaptureWhiteBalanceModeLocked]) {
        [device setWhiteBalanceMode:AVCaptureWhiteBalanceModeLocked];
        NSLog(@"white balance locked");
    }
}
// [self.session commitConfiguration];

for (int n = 0; n < 5; n++) {
    [self.stillImageOutput captureStillImageAsynchronouslyFromConnection:[self.stillImageOutput connectionWithMediaType:AVMediaTypeVideo] completionHandler:^(CMSampleBufferRef imageDataSampleBuffer, NSError *error) {

        if (imageDataSampleBuffer) {
            NSData *imageData = [AVCaptureStillImageOutput jpegStillImageNSDataRepresentation:imageDataSampleBuffer];
            UIImage *image = [[UIImage alloc] initWithData:imageData];
            [[[ALAssetsLibrary alloc] init] writeImageToSavedPhotosAlbum:[image CGImage] orientation:(ALAssetOrientation)image.imageOrientation completionBlock:nil];
        }
    }];
}

[device unlockForConfiguration]

The output log reports:

focus locked
exposure locked
white balance locked

which indicates that focus et al should have been successfully locked.

I tried wrapping the lock code with [device unlockForConfiguration] and [device unlockForConfiguration], but that didn't fix the issue.

Can someone identify an error in my code or a step that I'm missing? (I realize I can alternatively implement this using video capture instead of still capture, but I need AVCaptureSessionPresetPhoto resolution images.) Any help would be greatly appreciated. Thank you.

1

1 Answers

3
votes

Ok, I figured out the problem. [device unlockForConfiguration] was executing before all of the captureStillImageAsynchronouslyFromConnection calls completed due to the timing of threads and GCD tasks. A quick solution was a add a spinlock, for example:

if ([device lockForConfiguration:nil]) {
    if ([device isFocusModeSupported:AVCaptureFocusModeLocked])
        [device setFocusMode:AVCaptureFocusModeLocked];
    if ([device isExposureModeSupported:AVCaptureExposureModeLocked])
        [device setExposureMode:AVCaptureExposureModeLocked];
    if ([device isWhiteBalanceModeSupported:AVCaptureWhiteBalanceModeLocked])
        [device setWhiteBalanceMode:AVCaptureWhiteBalanceModeLocked];
}

__block int photoCount = 5; 
for (int n = photoCount; n > 0; n--) {
    [self.stillImageOutput captureStillImageAsynchronouslyFromConnection:[self.stillImageOutput connectionWithMediaType:AVMediaTypeVideo] completionHandler:^(CMSampleBufferRef imageDataSampleBuffer, NSError *error) {
        @synchronize(self) {
            photoCount--;
        }

        if (imageDataSampleBuffer) {
            NSData *imageData = [AVCaptureStillImageOutput jpegStillImageNSDataRepresentation:imageDataSampleBuffer];
            UIImage *image = [[UIImage alloc] initWithData:imageData];
            [[[ALAssetsLibrary alloc] init] writeImageToSavedPhotosAlbum:[image CGImage] orientation:(ALAssetOrientation)image.imageOrientation completionBlock:nil];
        }
    }];
}

while (photoCount > 0); // Spinlock until captureStillImageAsynchronouslyFromConnection captured all photos into memory
[device unlockForConfiguration]

There may be more graceful solutions out there (and I'd love to hear them), but a simple spinlock did the trick. (Plus since my code was running within a dispatch_async block, it didn't cause any problems with the UI or app responsiveness.)