fix(ios): wrap captureStillImageAsynchronouslyFromConnection with try/catch (#2056)

* wrap captureStillImageAsynchronouslyFromConnection with try/catch

the diff may look pretty big but the only change made here is
try/catch block added for captureStillImageAsynchronouslyFromConnection
call.
For some reason, this method may throw an exception saying about
inconsistent state.

* make sure null in cameraHandle isn't passed to Native code
This commit is contained in:
Ruslan Bekenev 2019-08-08 10:32:38 -07:00 committed by Sibelius Seraphini
parent 90225a96f0
commit adac26f5cf
2 changed files with 97 additions and 84 deletions

View File

@ -378,93 +378,102 @@ static NSDictionary *defaultFaceDetectorOptions = nil;
AVCaptureConnection *connection = [self.stillImageOutput connectionWithMediaType:AVMediaTypeVideo];
[connection setVideoOrientation:orientation];
[self.stillImageOutput captureStillImageAsynchronouslyFromConnection:connection completionHandler: ^(CMSampleBufferRef imageSampleBuffer, NSError *error) {
if (imageSampleBuffer && !error) {
if ([options[@"pauseAfterCapture"] boolValue]) {
[[self.previewLayer connection] setEnabled:NO];
}
BOOL useFastMode = [options valueForKey:@"fastMode"] != nil && [options[@"fastMode"] boolValue];
if (useFastMode) {
resolve(nil);
}
NSData *imageData = [AVCaptureStillImageOutput jpegStillImageNSDataRepresentation:imageSampleBuffer];
UIImage *takenImage = [UIImage imageWithData:imageData];
CGImageRef takenCGImage = takenImage.CGImage;
CGSize previewSize;
if (UIInterfaceOrientationIsPortrait([[UIApplication sharedApplication] statusBarOrientation])) {
previewSize = CGSizeMake(self.previewLayer.frame.size.height, self.previewLayer.frame.size.width);
} else {
previewSize = CGSizeMake(self.previewLayer.frame.size.width, self.previewLayer.frame.size.height);
}
CGRect cropRect = CGRectMake(0, 0, CGImageGetWidth(takenCGImage), CGImageGetHeight(takenCGImage));
CGRect croppedSize = AVMakeRectWithAspectRatioInsideRect(previewSize, cropRect);
takenImage = [RNImageUtils cropImage:takenImage toRect:croppedSize];
if ([options[@"mirrorImage"] boolValue]) {
takenImage = [RNImageUtils mirrorImage:takenImage];
}
if ([options[@"forceUpOrientation"] boolValue]) {
takenImage = [RNImageUtils forceUpOrientation:takenImage];
}
if ([options[@"width"] integerValue]) {
takenImage = [RNImageUtils scaleImage:takenImage toWidth:[options[@"width"] integerValue]];
}
NSMutableDictionary *response = [[NSMutableDictionary alloc] init];
float quality = [options[@"quality"] floatValue];
NSData *takenImageData = UIImageJPEGRepresentation(takenImage, quality);
NSString *path = [RNFileSystem generatePathInDirectory:[[RNFileSystem cacheDirectoryPath] stringByAppendingPathComponent:@"Camera"] withExtension:@".jpg"];
if (![options[@"doNotSave"] boolValue]) {
response[@"uri"] = [RNImageUtils writeImage:takenImageData toPath:path];
}
response[@"width"] = @(takenImage.size.width);
response[@"height"] = @(takenImage.size.height);
if ([options[@"base64"] boolValue]) {
response[@"base64"] = [takenImageData base64EncodedStringWithOptions:0];
}
if ([options[@"exif"] boolValue]) {
int imageRotation;
switch (takenImage.imageOrientation) {
case UIImageOrientationLeft:
case UIImageOrientationRightMirrored:
imageRotation = 90;
break;
case UIImageOrientationRight:
case UIImageOrientationLeftMirrored:
imageRotation = -90;
break;
case UIImageOrientationDown:
case UIImageOrientationDownMirrored:
imageRotation = 180;
break;
case UIImageOrientationUpMirrored:
default:
imageRotation = 0;
break;
@try {
[self.stillImageOutput captureStillImageAsynchronouslyFromConnection:connection completionHandler: ^(CMSampleBufferRef imageSampleBuffer, NSError *error) {
if (imageSampleBuffer && !error) {
if ([options[@"pauseAfterCapture"] boolValue]) {
[[self.previewLayer connection] setEnabled:NO];
}
BOOL useFastMode = [options valueForKey:@"fastMode"] != nil && [options[@"fastMode"] boolValue];
if (useFastMode) {
resolve(nil);
}
NSData *imageData = [AVCaptureStillImageOutput jpegStillImageNSDataRepresentation:imageSampleBuffer];
UIImage *takenImage = [UIImage imageWithData:imageData];
CGImageRef takenCGImage = takenImage.CGImage;
CGSize previewSize;
if (UIInterfaceOrientationIsPortrait([[UIApplication sharedApplication] statusBarOrientation])) {
previewSize = CGSizeMake(self.previewLayer.frame.size.height, self.previewLayer.frame.size.width);
} else {
previewSize = CGSizeMake(self.previewLayer.frame.size.width, self.previewLayer.frame.size.height);
}
CGRect cropRect = CGRectMake(0, 0, CGImageGetWidth(takenCGImage), CGImageGetHeight(takenCGImage));
CGRect croppedSize = AVMakeRectWithAspectRatioInsideRect(previewSize, cropRect);
takenImage = [RNImageUtils cropImage:takenImage toRect:croppedSize];
if ([options[@"mirrorImage"] boolValue]) {
takenImage = [RNImageUtils mirrorImage:takenImage];
}
if ([options[@"forceUpOrientation"] boolValue]) {
takenImage = [RNImageUtils forceUpOrientation:takenImage];
}
if ([options[@"width"] integerValue]) {
takenImage = [RNImageUtils scaleImage:takenImage toWidth:[options[@"width"] integerValue]];
}
NSMutableDictionary *response = [[NSMutableDictionary alloc] init];
float quality = [options[@"quality"] floatValue];
NSData *takenImageData = UIImageJPEGRepresentation(takenImage, quality);
NSString *path = [RNFileSystem generatePathInDirectory:[[RNFileSystem cacheDirectoryPath] stringByAppendingPathComponent:@"Camera"] withExtension:@".jpg"];
if (![options[@"doNotSave"] boolValue]) {
response[@"uri"] = [RNImageUtils writeImage:takenImageData toPath:path];
}
response[@"width"] = @(takenImage.size.width);
response[@"height"] = @(takenImage.size.height);
if ([options[@"base64"] boolValue]) {
response[@"base64"] = [takenImageData base64EncodedStringWithOptions:0];
}
if ([options[@"exif"] boolValue]) {
int imageRotation;
switch (takenImage.imageOrientation) {
case UIImageOrientationLeft:
case UIImageOrientationRightMirrored:
imageRotation = 90;
break;
case UIImageOrientationRight:
case UIImageOrientationLeftMirrored:
imageRotation = -90;
break;
case UIImageOrientationDown:
case UIImageOrientationDownMirrored:
imageRotation = 180;
break;
case UIImageOrientationUpMirrored:
default:
imageRotation = 0;
break;
}
[RNImageUtils updatePhotoMetadata:imageSampleBuffer withAdditionalData:@{ @"Orientation": @(imageRotation) } inResponse:response]; // TODO
}
response[@"pictureOrientation"] = @([self.orientation integerValue]);
response[@"deviceOrientation"] = @([self.deviceOrientation integerValue]);
self.orientation = nil;
self.deviceOrientation = nil;
if (useFastMode) {
[self onPictureSaved:@{@"data": response, @"id": options[@"id"]}];
} else {
resolve(response);
}
[RNImageUtils updatePhotoMetadata:imageSampleBuffer withAdditionalData:@{ @"Orientation": @(imageRotation) } inResponse:response]; // TODO
}
response[@"pictureOrientation"] = @([self.orientation integerValue]);
response[@"deviceOrientation"] = @([self.deviceOrientation integerValue]);
self.orientation = nil;
self.deviceOrientation = nil;
if (useFastMode) {
[self onPictureSaved:@{@"data": response, @"id": options[@"id"]}];
} else {
resolve(response);
reject(@"E_IMAGE_CAPTURE_FAILED", @"Image could not be captured", error);
}
} else {
reject(@"E_IMAGE_CAPTURE_FAILED", @"Image could not be captured", error);
}
}];
}];
} @catch (NSException *exception) {
reject(
@"E_IMAGE_CAPTURE_FAILED",
@"Got exception while taking picture",
[NSError errorWithDomain:@"E_IMAGE_CAPTURE_FAILED" code: 500 userInfo:@{NSLocalizedDescriptionKey:exception.reason}]
);
}
}
- (void)recordWithOrientation:(NSDictionary *)options resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject{
[self.sensorOrientationChecker getDeviceOrientationWithBlock:^(UIInterfaceOrientation orientation) {

View File

@ -492,6 +492,10 @@ export default class Camera extends React.Component<PropsType, StateType> {
options.pauseAfterCapture = false;
}
if (!this._cameraHandle) {
throw 'Camera handle cannot be null';
}
return await CameraManager.takePicture(options, this._cameraHandle);
}