2015-04-01 01:02:57 +00:00
|
|
|
#import "RCTCameraManager.h"
|
|
|
|
#import "RCTCamera.h"
|
|
|
|
#import "RCTBridge.h"
|
2015-04-19 22:53:30 +00:00
|
|
|
#import "RCTEventDispatcher.h"
|
2015-04-02 09:40:03 +00:00
|
|
|
#import "RCTUtils.h"
|
2015-04-11 02:56:30 +00:00
|
|
|
#import "RCTLog.h"
|
2015-04-08 20:51:31 +00:00
|
|
|
#import "UIView+React.h"
|
2015-04-11 02:56:30 +00:00
|
|
|
#import "UIImage+Resize.h"
|
2015-06-02 21:23:20 +00:00
|
|
|
#import <AssetsLibrary/ALAssetsLibrary.h>
|
2015-04-01 01:02:57 +00:00
|
|
|
#import <AVFoundation/AVFoundation.h>
|
|
|
|
|
|
|
|
@implementation RCTCameraManager
|
|
|
|
|
2015-04-11 02:56:30 +00:00
|
|
|
RCT_EXPORT_MODULE();
|
|
|
|
|
2015-04-01 01:02:57 +00:00
|
|
|
- (UIView *)view
|
|
|
|
{
|
2015-04-17 00:16:35 +00:00
|
|
|
return [[RCTCamera alloc] initWithManager:self];
|
2015-04-01 01:02:57 +00:00
|
|
|
}
|
|
|
|
|
2015-05-04 01:54:28 +00:00
|
|
|
RCT_EXPORT_VIEW_PROPERTY(aspect, NSInteger);
|
2015-04-08 20:51:31 +00:00
|
|
|
RCT_EXPORT_VIEW_PROPERTY(type, NSInteger);
|
2015-04-01 01:02:57 +00:00
|
|
|
RCT_EXPORT_VIEW_PROPERTY(orientation, NSInteger);
|
2015-05-30 16:36:55 +00:00
|
|
|
RCT_EXPORT_VIEW_PROPERTY(flashMode, NSInteger);
|
2015-04-01 01:02:57 +00:00
|
|
|
|
2015-04-01 02:27:18 +00:00
|
|
|
- (NSDictionary *)constantsToExport
|
|
|
|
{
|
|
|
|
return @{
|
2015-04-30 07:10:21 +00:00
|
|
|
@"Aspect": @{
|
2015-05-01 21:33:23 +00:00
|
|
|
@"stretch": @(RCTCameraAspectStretch),
|
|
|
|
@"fit": @(RCTCameraAspectFit),
|
|
|
|
@"fill": @(RCTCameraAspectFill)
|
2015-04-01 02:27:18 +00:00
|
|
|
},
|
2015-05-01 21:33:23 +00:00
|
|
|
@"Type": @{
|
|
|
|
@"front": @(RCTCameraTypeFront),
|
|
|
|
@"back": @(RCTCameraTypeBack)
|
2015-04-01 17:32:53 +00:00
|
|
|
},
|
2015-05-01 08:16:32 +00:00
|
|
|
@"CaptureMode": @{
|
|
|
|
@"still": @(RCTCameraCaptureModeStill),
|
|
|
|
@"video": @(RCTCameraCaptureModeVideo)
|
|
|
|
},
|
|
|
|
@"CaptureTarget": @{
|
|
|
|
@"memory": @(RCTCameraCaptureTargetMemory),
|
2015-06-02 21:23:20 +00:00
|
|
|
@"disk": @(RCTCameraCaptureTargetDisk),
|
|
|
|
@"cameraRoll": @(RCTCameraCaptureTargetCameraRoll)
|
2015-05-01 08:16:32 +00:00
|
|
|
},
|
2015-04-30 07:10:21 +00:00
|
|
|
@"Orientation": @{
|
2015-05-01 21:33:23 +00:00
|
|
|
@"auto": @(RCTCameraOrientationAuto),
|
|
|
|
@"landscapeLeft": @(RCTCameraOrientationLandscapeLeft),
|
|
|
|
@"landscapeRight": @(RCTCameraOrientationLandscapeRight),
|
|
|
|
@"portrait": @(RCTCameraOrientationPortrait),
|
|
|
|
@"portraitUpsideDown": @(RCTCameraOrientationPortraitUpsideDown)
|
2015-05-30 16:36:55 +00:00
|
|
|
},
|
|
|
|
@"FlashMode": @{
|
|
|
|
@"off": @(RCTCameraFlashModeOff),
|
|
|
|
@"on": @(RCTCameraFlashModeOn),
|
|
|
|
@"auto": @(RCTCameraFlashModeAuto)
|
2015-04-01 02:27:18 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2015-04-11 02:56:30 +00:00
|
|
|
- (id)init {
|
|
|
|
|
|
|
|
if ((self = [super init])) {
|
2015-04-17 00:16:35 +00:00
|
|
|
|
2015-04-11 02:56:30 +00:00
|
|
|
self.session = [AVCaptureSession new];
|
|
|
|
self.session.sessionPreset = AVCaptureSessionPresetHigh;
|
|
|
|
|
2015-04-17 00:16:35 +00:00
|
|
|
self.previewLayer = [AVCaptureVideoPreviewLayer layerWithSession:self.session];
|
|
|
|
self.previewLayer.needsDisplayOnBoundsChange = YES;
|
|
|
|
|
2015-04-11 02:56:30 +00:00
|
|
|
self.sessionQueue = dispatch_queue_create("cameraManagerQueue", DISPATCH_QUEUE_SERIAL);
|
|
|
|
|
|
|
|
dispatch_async(self.sessionQueue, ^{
|
|
|
|
NSError *error = nil;
|
|
|
|
|
|
|
|
if (self.presetCamera == AVCaptureDevicePositionUnspecified) {
|
|
|
|
self.presetCamera = AVCaptureDevicePositionBack;
|
|
|
|
}
|
|
|
|
|
|
|
|
AVCaptureDevice *captureDevice = [self deviceWithMediaType:AVMediaTypeVideo preferringPosition:self.presetCamera];
|
2015-04-24 18:56:16 +00:00
|
|
|
if (captureDevice != nil) {
|
|
|
|
AVCaptureDeviceInput *captureDeviceInput = [AVCaptureDeviceInput deviceInputWithDevice:captureDevice error:&error];
|
|
|
|
|
|
|
|
if (error)
|
|
|
|
{
|
|
|
|
NSLog(@"%@", error);
|
|
|
|
}
|
|
|
|
|
|
|
|
if ([self.session canAddInput:captureDeviceInput])
|
|
|
|
{
|
|
|
|
[self.session addInput:captureDeviceInput];
|
|
|
|
self.captureDeviceInput = captureDeviceInput;
|
|
|
|
}
|
2015-04-11 02:56:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
AVCaptureStillImageOutput *stillImageOutput = [[AVCaptureStillImageOutput alloc] init];
|
|
|
|
if ([self.session canAddOutput:stillImageOutput])
|
|
|
|
{
|
|
|
|
stillImageOutput.outputSettings = @{AVVideoCodecKey : AVVideoCodecJPEG};
|
|
|
|
[self.session addOutput:stillImageOutput];
|
|
|
|
self.stillImageOutput = stillImageOutput;
|
|
|
|
}
|
|
|
|
|
2015-04-19 22:53:30 +00:00
|
|
|
AVCaptureMetadataOutput *metadataOutput = [[AVCaptureMetadataOutput alloc] init];
|
|
|
|
if ([self.session canAddOutput:metadataOutput]) {
|
|
|
|
[metadataOutput setMetadataObjectsDelegate:self queue:self.sessionQueue];
|
|
|
|
[self.session addOutput:metadataOutput];
|
|
|
|
[metadataOutput setMetadataObjectTypes:metadataOutput.availableMetadataObjectTypes];
|
|
|
|
self.metadataOutput = metadataOutput;
|
|
|
|
}
|
|
|
|
|
2015-04-11 02:56:30 +00:00
|
|
|
__weak RCTCameraManager *weakSelf = self;
|
|
|
|
[self setRuntimeErrorHandlingObserver:[NSNotificationCenter.defaultCenter addObserverForName:AVCaptureSessionRuntimeErrorNotification object:self.session queue:nil usingBlock:^(NSNotification *note) {
|
|
|
|
RCTCameraManager *strongSelf = weakSelf;
|
|
|
|
dispatch_async(strongSelf.sessionQueue, ^{
|
|
|
|
// Manually restarting the session since it must have been stopped due to an error.
|
|
|
|
[strongSelf.session startRunning];
|
|
|
|
});
|
|
|
|
}]];
|
2015-05-30 16:36:55 +00:00
|
|
|
|
2015-04-11 02:56:30 +00:00
|
|
|
[self.session startRunning];
|
|
|
|
});
|
|
|
|
}
|
|
|
|
return self;
|
|
|
|
}
|
|
|
|
|
|
|
|
RCT_EXPORT_METHOD(checkDeviceAuthorizationStatus:(RCTResponseSenderBlock) callback)
|
|
|
|
{
|
2015-04-01 01:02:57 +00:00
|
|
|
NSString *mediaType = AVMediaTypeVideo;
|
2015-04-01 02:27:18 +00:00
|
|
|
|
2015-04-01 01:02:57 +00:00
|
|
|
[AVCaptureDevice requestAccessForMediaType:mediaType completionHandler:^(BOOL granted) {
|
|
|
|
callback(@[[NSNull null], @(granted)]);
|
|
|
|
}];
|
|
|
|
}
|
|
|
|
|
2015-05-30 16:36:55 +00:00
|
|
|
RCT_EXPORT_METHOD(changeFlashMode:(NSInteger)flashMode) {
|
|
|
|
AVCaptureDevice *currentCaptureDevice = [self.captureDeviceInput device];
|
|
|
|
[self setFlashMode:flashMode forDevice:currentCaptureDevice];
|
|
|
|
}
|
2015-04-02 09:40:03 +00:00
|
|
|
|
2015-04-11 02:56:30 +00:00
|
|
|
RCT_EXPORT_METHOD(changeCamera:(NSInteger)camera) {
|
|
|
|
AVCaptureDevice *currentCaptureDevice = [self.captureDeviceInput device];
|
|
|
|
AVCaptureDevicePosition position = (AVCaptureDevicePosition)camera;
|
|
|
|
AVCaptureDevice *captureDevice = [self deviceWithMediaType:AVMediaTypeVideo preferringPosition:(AVCaptureDevicePosition)position];
|
|
|
|
|
2015-04-24 18:56:16 +00:00
|
|
|
if (captureDevice == nil) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2015-04-11 02:56:30 +00:00
|
|
|
NSError *error = nil;
|
|
|
|
AVCaptureDeviceInput *captureDeviceInput = [AVCaptureDeviceInput deviceInputWithDevice:captureDevice error:&error];
|
|
|
|
|
|
|
|
if (error)
|
|
|
|
{
|
|
|
|
NSLog(@"%@", error);
|
2015-04-24 18:56:16 +00:00
|
|
|
return;
|
2015-04-11 02:56:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
[self.session beginConfiguration];
|
|
|
|
|
|
|
|
[self.session removeInput:self.captureDeviceInput];
|
|
|
|
|
|
|
|
if ([self.session canAddInput:captureDeviceInput])
|
|
|
|
{
|
|
|
|
[NSNotificationCenter.defaultCenter removeObserver:self name:AVCaptureDeviceSubjectAreaDidChangeNotification object:currentCaptureDevice];
|
|
|
|
|
|
|
|
[NSNotificationCenter.defaultCenter addObserver:self selector:@selector(subjectAreaDidChange:) name:AVCaptureDeviceSubjectAreaDidChangeNotification object:captureDevice];
|
|
|
|
[self.session addInput:captureDeviceInput];
|
|
|
|
self.captureDeviceInput = captureDeviceInput;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
[self.session addInput:self.captureDeviceInput];
|
|
|
|
}
|
|
|
|
|
|
|
|
[self.session commitConfiguration];
|
|
|
|
}
|
|
|
|
|
2015-04-17 00:16:35 +00:00
|
|
|
RCT_EXPORT_METHOD(changeAspect:(NSString *)aspect) {
|
|
|
|
self.previewLayer.videoGravity = aspect;
|
|
|
|
}
|
|
|
|
|
2015-04-11 02:56:30 +00:00
|
|
|
RCT_EXPORT_METHOD(changeOrientation:(NSInteger)orientation) {
|
2015-04-17 00:16:35 +00:00
|
|
|
self.previewLayer.connection.videoOrientation = orientation;
|
2015-04-11 02:56:30 +00:00
|
|
|
}
|
|
|
|
|
2015-05-01 08:16:32 +00:00
|
|
|
RCT_EXPORT_METHOD(capture:(NSDictionary *)options callback:(RCTResponseSenderBlock)callback) {
|
|
|
|
NSInteger captureMode = [[options valueForKey:@"mode"] intValue];
|
|
|
|
NSInteger captureTarget = [[options valueForKey:@"target"] intValue];
|
2015-05-30 16:36:55 +00:00
|
|
|
|
2015-05-01 08:16:32 +00:00
|
|
|
if (captureMode == RCTCameraCaptureModeStill) {
|
|
|
|
[self captureStill:captureTarget callback:callback];
|
|
|
|
}
|
|
|
|
else if (captureMode == RCTCameraCaptureModeVideo) {
|
|
|
|
// waiting for incoming PRs
|
|
|
|
}
|
2015-04-11 02:56:30 +00:00
|
|
|
}
|
|
|
|
|
2015-06-12 22:23:47 +00:00
|
|
|
- (void)captureStill:(NSInteger)target callback:(RCTResponseSenderBlock)callback {
|
|
|
|
if ([[[UIDevice currentDevice].model lowercaseString] rangeOfString:@"simulator"].location != NSNotFound){
|
|
|
|
|
|
|
|
CGSize size = CGSizeMake(720, 1280);
|
|
|
|
UIGraphicsBeginImageContextWithOptions(size, YES, 0);
|
|
|
|
[[UIColor whiteColor] setFill];
|
|
|
|
UIRectFill(CGRectMake(0, 0, size.width, size.height));
|
|
|
|
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
|
|
|
|
UIGraphicsEndImageContext();
|
|
|
|
|
|
|
|
[self storeImage:image target:target callback:callback];
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
[[self.stillImageOutput connectionWithMediaType:AVMediaTypeVideo] setVideoOrientation:self.previewLayer.connection.videoOrientation];
|
|
|
|
|
|
|
|
[self.stillImageOutput captureStillImageAsynchronouslyFromConnection:[self.stillImageOutput connectionWithMediaType:AVMediaTypeVideo] completionHandler:^(CMSampleBufferRef imageDataSampleBuffer, NSError *error) {
|
2015-04-30 19:40:19 +00:00
|
|
|
NSData *imageData = [AVCaptureStillImageOutput jpegStillImageNSDataRepresentation:imageDataSampleBuffer];
|
|
|
|
UIImage *image = [UIImage imageWithData:imageData];
|
2015-06-12 22:23:47 +00:00
|
|
|
if (image)
|
|
|
|
{
|
|
|
|
[self storeImage:image target:target callback:callback];
|
2015-05-01 08:16:32 +00:00
|
|
|
}
|
2015-06-12 22:23:47 +00:00
|
|
|
else {
|
|
|
|
callback(@[RCTMakeError(error.description, nil, nil)]);
|
2015-05-01 08:16:32 +00:00
|
|
|
}
|
2015-06-12 22:23:47 +00:00
|
|
|
}];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void)storeImage:(UIImage*)image target:(NSInteger)target callback:(RCTResponseSenderBlock)callback {
|
|
|
|
UIImage *rotatedImage = [image resizedImage:CGSizeMake(image.size.width, image.size.height) interpolationQuality:kCGInterpolationDefault];
|
|
|
|
|
|
|
|
NSString *responseString;
|
|
|
|
|
|
|
|
if (target == RCTCameraCaptureTargetMemory) {
|
|
|
|
responseString = [UIImageJPEGRepresentation(rotatedImage, 1.0) base64EncodedStringWithOptions:0];
|
|
|
|
}
|
|
|
|
else if (target == RCTCameraCaptureTargetDisk) {
|
|
|
|
responseString = [self saveImage:rotatedImage withName:[[NSUUID UUID] UUIDString]];
|
|
|
|
}
|
|
|
|
else if (target == RCTCameraCaptureTargetCameraRoll) {
|
|
|
|
[[[ALAssetsLibrary alloc] init] writeImageToSavedPhotosAlbum:rotatedImage.CGImage metadata:nil completionBlock:^(NSURL* url, NSError* error) {
|
|
|
|
if (error == nil) {
|
|
|
|
callback(@[[NSNull null], [url absoluteString]]);
|
2015-06-02 21:23:20 +00:00
|
|
|
}
|
2015-06-12 22:23:47 +00:00
|
|
|
else {
|
|
|
|
callback(@[RCTMakeError(error.description, nil, nil)]);
|
|
|
|
}
|
|
|
|
}];
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
callback(@[[NSNull null], responseString]);
|
2015-04-30 19:40:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
- (NSString *)saveImage:(UIImage *)image withName:(NSString *)name {
|
|
|
|
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
|
|
|
|
NSString *documentsDirectory = [paths firstObject];
|
|
|
|
|
|
|
|
NSData *data = UIImageJPEGRepresentation(image, 1.0);
|
|
|
|
NSFileManager *fileManager = [NSFileManager defaultManager];
|
|
|
|
NSString *fullPath = [documentsDirectory stringByAppendingPathComponent:name];
|
|
|
|
|
|
|
|
[fileManager createFileAtPath:fullPath contents:data attributes:nil];
|
|
|
|
return fullPath;
|
|
|
|
}
|
|
|
|
|
2015-04-19 22:53:30 +00:00
|
|
|
- (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputMetadataObjects:(NSArray *)metadataObjects fromConnection:(AVCaptureConnection *)connection {
|
|
|
|
|
|
|
|
NSArray *barcodeTypes = @[
|
|
|
|
AVMetadataObjectTypeUPCECode,
|
|
|
|
AVMetadataObjectTypeCode39Code,
|
|
|
|
AVMetadataObjectTypeCode39Mod43Code,
|
|
|
|
AVMetadataObjectTypeEAN13Code,
|
|
|
|
AVMetadataObjectTypeEAN8Code,
|
|
|
|
AVMetadataObjectTypeCode93Code,
|
|
|
|
AVMetadataObjectTypeCode128Code,
|
|
|
|
AVMetadataObjectTypePDF417Code,
|
|
|
|
AVMetadataObjectTypeQRCode,
|
|
|
|
AVMetadataObjectTypeAztecCode
|
|
|
|
];
|
|
|
|
|
|
|
|
for (AVMetadataMachineReadableCodeObject *metadata in metadataObjects) {
|
|
|
|
for (id barcodeType in barcodeTypes) {
|
|
|
|
if (metadata.type == barcodeType) {
|
|
|
|
|
|
|
|
[self.bridge.eventDispatcher sendDeviceEventWithName:@"CameraBarCodeRead"
|
|
|
|
body:@{
|
|
|
|
@"data": metadata.stringValue,
|
|
|
|
@"bounds": @{
|
|
|
|
@"origin": @{
|
|
|
|
@"x": [NSString stringWithFormat:@"%f", metadata.bounds.origin.x],
|
|
|
|
@"y": [NSString stringWithFormat:@"%f", metadata.bounds.origin.y]
|
|
|
|
},
|
|
|
|
@"size": @{
|
|
|
|
@"height": [NSString stringWithFormat:@"%f", metadata.bounds.size.height],
|
|
|
|
@"width": [NSString stringWithFormat:@"%f", metadata.bounds.size.width],
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-04-11 02:56:30 +00:00
|
|
|
|
|
|
|
- (AVCaptureDevice *)deviceWithMediaType:(NSString *)mediaType preferringPosition:(AVCaptureDevicePosition)position
|
2015-04-02 09:40:03 +00:00
|
|
|
{
|
2015-04-11 02:56:30 +00:00
|
|
|
NSArray *devices = [AVCaptureDevice devicesWithMediaType:mediaType];
|
|
|
|
AVCaptureDevice *captureDevice = [devices firstObject];
|
|
|
|
|
|
|
|
for (AVCaptureDevice *device in devices)
|
|
|
|
{
|
|
|
|
if ([device position] == position)
|
|
|
|
{
|
|
|
|
captureDevice = device;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return captureDevice;
|
2015-04-02 09:40:03 +00:00
|
|
|
}
|
|
|
|
|
2015-04-11 02:56:30 +00:00
|
|
|
|
|
|
|
- (void)setFlashMode:(AVCaptureFlashMode)flashMode forDevice:(AVCaptureDevice *)device
|
2015-04-02 09:40:03 +00:00
|
|
|
{
|
2015-04-11 02:56:30 +00:00
|
|
|
if (device.hasFlash && [device isFlashModeSupported:flashMode])
|
|
|
|
{
|
|
|
|
NSError *error = nil;
|
|
|
|
if ([device lockForConfiguration:&error])
|
|
|
|
{
|
|
|
|
[device setFlashMode:flashMode];
|
|
|
|
[device unlockForConfiguration];
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
NSLog(@"%@", error);
|
|
|
|
}
|
|
|
|
}
|
2015-04-02 09:40:03 +00:00
|
|
|
}
|
|
|
|
|
2015-04-11 02:56:30 +00:00
|
|
|
- (void)subjectAreaDidChange:(NSNotification *)notification
|
|
|
|
{
|
|
|
|
CGPoint devicePoint = CGPointMake(.5, .5);
|
|
|
|
[self focusWithMode:AVCaptureFocusModeContinuousAutoFocus exposeWithMode:AVCaptureExposureModeContinuousAutoExposure atDevicePoint:devicePoint monitorSubjectAreaChange:NO];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void)focusWithMode:(AVCaptureFocusMode)focusMode exposeWithMode:(AVCaptureExposureMode)exposureMode atDevicePoint:(CGPoint)point monitorSubjectAreaChange:(BOOL)monitorSubjectAreaChange
|
|
|
|
{
|
|
|
|
dispatch_async([self sessionQueue], ^{
|
|
|
|
AVCaptureDevice *device = [[self captureDeviceInput] device];
|
|
|
|
NSError *error = nil;
|
|
|
|
if ([device lockForConfiguration:&error])
|
|
|
|
{
|
|
|
|
if ([device isFocusPointOfInterestSupported] && [device isFocusModeSupported:focusMode])
|
|
|
|
{
|
|
|
|
[device setFocusMode:focusMode];
|
|
|
|
[device setFocusPointOfInterest:point];
|
|
|
|
}
|
|
|
|
if ([device isExposurePointOfInterestSupported] && [device isExposureModeSupported:exposureMode])
|
|
|
|
{
|
|
|
|
[device setExposureMode:exposureMode];
|
|
|
|
[device setExposurePointOfInterest:point];
|
|
|
|
}
|
|
|
|
[device setSubjectAreaChangeMonitoringEnabled:monitorSubjectAreaChange];
|
|
|
|
[device unlockForConfiguration];
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
NSLog(@"%@", error);
|
|
|
|
}
|
|
|
|
});
|
2015-04-02 09:40:03 +00:00
|
|
|
}
|
|
|
|
|
2015-04-03 01:07:22 +00:00
|
|
|
|
2015-04-01 01:02:57 +00:00
|
|
|
@end
|