Skip to content

Commit

Permalink
Merge remote-tracking branch 'upstream/master'
Browse files Browse the repository at this point in the history
  • Loading branch information
vorjdux committed Jul 9, 2015
2 parents befa5fb + 87a8389 commit 71fd837
Show file tree
Hide file tree
Showing 4 changed files with 182 additions and 26 deletions.
33 changes: 26 additions & 7 deletions Camera.ios.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ var merge = require('merge');

var constants = {
Aspect: NativeModules.CameraManager.Aspect,
BarCodeType: NativeModules.CameraManager.BarCodeType,
Type: NativeModules.CameraManager.Type,
CaptureMode: NativeModules.CameraManager.CaptureMode,
CaptureTarget: NativeModules.CameraManager.CaptureTarget,
Expand Down Expand Up @@ -73,7 +74,8 @@ var Camera = React.createClass({

getInitialState() {
return {
isAuthorized: false
isAuthorized: false,
isRecording: false
};
},

Expand All @@ -87,6 +89,10 @@ var Camera = React.createClass({

componentWillUnmount() {
this.cameraBarCodeReadListener.remove();

if (this.state.isRecording) {
this.stopRecording();
}
},

render() {
Expand All @@ -113,18 +119,12 @@ var Camera = React.createClass({
type: {
Front: 'front',
Back: 'back'
},
flashMode: {
Off: 'off',
On: 'on',
Auto: 'auto'
}
};

var foundLegacyAspect = legacyProps.aspect[aspect];
var foundLegacyOrientation = legacyProps.orientation[orientation];
var foundLegacyType = legacyProps.type[type];
var foundLegacyFlashMode = legacyProps.flashMode[flashMode];

if (__DEV__) {
if (foundLegacyAspect) {
Expand All @@ -144,10 +144,18 @@ var Camera = React.createClass({
if (typeof aspect === 'string') {
aspect = constants.Aspect[aspect];
}

if (typeof flashMode === 'string') {
flashMode = constants.FlashMode[flashMode];
}

if (typeof orientation === 'string') {
orientation = constants.Orientation[orientation];
}

if (typeof torchMode === 'string') {
torchMode = constants.TorchMode[torchMode];
}

if (typeof type === 'string') {
type = constants.Type[type];
Expand Down Expand Up @@ -184,12 +192,23 @@ var Camera = React.createClass({
if (typeof options.mode === 'string') {
options.mode = constants.CaptureMode[options.mode];
}

if (options.mode === constants.CaptureMode.video) {
options.totalSeconds = (options.totalSeconds > -1 ? options.totalSeconds : -1);
options.preferredTimeScale = options.preferredTimeScale || 30;
this.setState({ isRecording: true });
}

if (typeof options.target === 'string') {
options.target = constants.CaptureTarget[options.target];
}

NativeModules.CameraManager.capture(options, cb);
},

stopCapture() {
this.setState({ isRecording: false });
NativeModules.CameraManager.stopCapture();
}

});
Expand Down
6 changes: 5 additions & 1 deletion RCTCameraManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,16 +45,19 @@ typedef NS_ENUM(NSInteger, RCTCameraTorchMode) {
RCTCameraTorchModeAuto = AVCaptureTorchModeAuto
};

@interface RCTCameraManager : RCTViewManager<AVCaptureMetadataOutputObjectsDelegate>
@interface RCTCameraManager : RCTViewManager<AVCaptureMetadataOutputObjectsDelegate, AVCaptureFileOutputRecordingDelegate>

@property (nonatomic) dispatch_queue_t sessionQueue;
@property (nonatomic) AVCaptureSession *session;
@property (nonatomic) AVCaptureDeviceInput *captureDeviceInput;
@property (nonatomic) AVCaptureStillImageOutput *stillImageOutput;
@property (nonatomic) AVCaptureMovieFileOutput *movieFileOutput;
@property (nonatomic) AVCaptureMetadataOutput *metadataOutput;
@property (nonatomic) id runtimeErrorHandlingObserver;
@property (nonatomic) NSInteger presetCamera;
@property (nonatomic) AVCaptureVideoPreviewLayer *previewLayer;
@property (nonatomic) NSInteger videoTarget;
@property (nonatomic, strong) RCTResponseSenderBlock videoCallback;

- (void)changeAspect:(NSString *)aspect;
- (void)changeCamera:(NSInteger)camera;
Expand All @@ -63,5 +66,6 @@ typedef NS_ENUM(NSInteger, RCTCameraTorchMode) {
- (void)changeTorchMode:(NSInteger)torchMode;
- (AVCaptureDevice *)deviceWithMediaType:(NSString *)mediaType preferringPosition:(AVCaptureDevicePosition)position;
- (void)capture:(NSDictionary*)options callback:(RCTResponseSenderBlock)callback;
- (void)stopCapture;

@end
167 changes: 150 additions & 17 deletions RCTCameraManager.m
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,18 @@ - (NSDictionary *)constantsToExport
@"fit": @(RCTCameraAspectFit),
@"fill": @(RCTCameraAspectFill)
},
@"BarCodeType": @{
@"upce": AVMetadataObjectTypeUPCECode,
@"code39": AVMetadataObjectTypeCode39Code,
@"code39mod43": AVMetadataObjectTypeCode39Mod43Code,
@"ean13": AVMetadataObjectTypeEAN13Code,
@"ean8": AVMetadataObjectTypeEAN8Code,
@"code93": AVMetadataObjectTypeCode93Code,
@"code138": AVMetadataObjectTypeCode128Code,
@"pdf417": AVMetadataObjectTypePDF417Code,
@"qr": AVMetadataObjectTypeQRCode,
@"aztec": AVMetadataObjectTypeAztecCode
},
@"Type": @{
@"front": @(RCTCameraTypeFront),
@"back": @(RCTCameraTypeBack)
Expand Down Expand Up @@ -99,6 +111,21 @@ - (id)init {
self.captureDeviceInput = captureDeviceInput;
}
}

AVCaptureDevice *audioCaptureDevice = [self deviceWithMediaType:AVMediaTypeAudio preferringPosition:self.presetCamera];
if (audioCaptureDevice != nil) {
AVCaptureDeviceInput *audioInput = [AVCaptureDeviceInput deviceInputWithDevice:audioCaptureDevice error:&error];

if (error)
{
NSLog(@"%@", error);
}

if ([self.session canAddInput:audioInput])
{
[self.session addInput:audioInput];
}
}

AVCaptureStillImageOutput *stillImageOutput = [[AVCaptureStillImageOutput alloc] init];
if ([self.session canAddOutput:stillImageOutput])
Expand All @@ -107,6 +134,13 @@ - (id)init {
[self.session addOutput:stillImageOutput];
self.stillImageOutput = stillImageOutput;
}

AVCaptureMovieFileOutput *movieFileOutput = [[AVCaptureMovieFileOutput alloc] init];
if ([self.session canAddOutput:movieFileOutput])
{
[self.session addOutput:movieFileOutput];
self.movieFileOutput = movieFileOutput;
}

AVCaptureMetadataOutput *metadataOutput = [[AVCaptureMetadataOutput alloc] init];
if ([self.session canAddOutput:metadataOutput]) {
Expand Down Expand Up @@ -140,11 +174,6 @@ - (id)init {
}];
}

RCT_EXPORT_METHOD(changeFlashMode:(NSInteger)flashMode) {
AVCaptureDevice *currentCaptureDevice = [self.captureDeviceInput device];
[self setFlashMode:flashMode forDevice:currentCaptureDevice];
}

RCT_EXPORT_METHOD(changeCamera:(NSInteger)camera) {
AVCaptureDevice *currentCaptureDevice = [self.captureDeviceInput device];
AVCaptureDevicePosition position = (AVCaptureDevicePosition)camera;
Expand Down Expand Up @@ -187,6 +216,19 @@ - (id)init {
self.previewLayer.videoGravity = aspect;
}

RCT_EXPORT_METHOD(changeFlashMode:(NSInteger)flashMode) {
AVCaptureDevice *device = [self.captureDeviceInput device];
NSError *error = nil;

if (![device hasFlash]) return;
if (![device lockForConfiguration:&error]) {
NSLog(@"%@", error);
return;
}
[self setFlashMode:flashMode forDevice:device];
[device unlockForConfiguration];
}

RCT_EXPORT_METHOD(changeOrientation:(NSInteger)orientation) {
self.previewLayer.connection.videoOrientation = orientation;
}
Expand All @@ -195,17 +237,13 @@ - (id)init {
AVCaptureDevice *device = [self.captureDeviceInput device];
NSError *error = nil;

if ([device hasTorch]) {
if ([device lockForConfiguration:&error])
{
[device setTorchMode: torchMode];
[device unlockForConfiguration];
}
else
{
NSLog(@"%@", error);
}
if (![device hasTorch]) return;
if (![device lockForConfiguration:&error]) {
NSLog(@"%@", error);
return;
}
[device setTorchMode: torchMode];
[device unlockForConfiguration];
}

RCT_EXPORT_METHOD(capture:(NSDictionary *)options callback:(RCTResponseSenderBlock)callback) {
Expand All @@ -216,7 +254,25 @@ - (id)init {
[self captureStill:captureTarget callback:callback];
}
else if (captureMode == RCTCameraCaptureModeVideo) {
// waiting for incoming PRs
if (self.movieFileOutput.recording) {
callback(@[RCTMakeError(@"Already Recording", nil, nil)]);
return;
}

Float64 totalSeconds = [[options valueForKey:@"totalSeconds"] floatValue];
if (totalSeconds > -1) {
int32_t preferredTimeScale = [[options valueForKey:@"preferredTimeScale"] intValue];
CMTime maxDuration = CMTimeMakeWithSeconds(totalSeconds, preferredTimeScale);
self.movieFileOutput.maxRecordedDuration = maxDuration;
}

[self captureVideo:captureTarget callback:callback];
}
}

RCT_EXPORT_METHOD(stopCapture) {
if (self.movieFileOutput.recording) {
[self.movieFileOutput stopRecording];
}
}

Expand Down Expand Up @@ -275,13 +331,89 @@ - (void)storeImage:(UIImage*)image target:(NSInteger)target callback:(RCTRespons
callback(@[[NSNull null], responseString]);
}

-(void)captureVideo:(NSInteger)target callback:(RCTResponseSenderBlock)callback {

[[self.movieFileOutput connectionWithMediaType:AVMediaTypeVideo] setVideoOrientation:self.previewLayer.connection.videoOrientation];

//Create temporary URL to record to
NSString *outputPath = [[NSString alloc] initWithFormat:@"%@%@", NSTemporaryDirectory(), @"output.mov"];
NSURL *outputURL = [[NSURL alloc] initFileURLWithPath:outputPath];
NSFileManager *fileManager = [NSFileManager defaultManager];
if ([fileManager fileExistsAtPath:outputPath]) {
NSError *error;
if ([fileManager removeItemAtPath:outputPath error:&error] == NO) {
callback(@[RCTMakeError(error.description, nil, nil)]);
return;
}
}

//Start recording
[self.movieFileOutput startRecordingToOutputFileURL:outputURL recordingDelegate:self];

self.videoCallback = callback;
self.videoTarget = target;
}

- (void)captureOutput:(AVCaptureFileOutput *)captureOutput
didFinishRecordingToOutputFileAtURL:(NSURL *)outputFileURL
fromConnections:(NSArray *)connections
error:(NSError *)error
{

BOOL recordSuccess = YES;
if ([error code] != noErr) {
// A problem occurred: Find out if the recording was successful.
id value = [[error userInfo] objectForKey:AVErrorRecordingSuccessfullyFinishedKey];
if (value) {
recordSuccess = [value boolValue];
}
}
if (!recordSuccess) {
self.videoCallback(@[RCTMakeError(@"Error while recording", nil, nil)]);
return;
}

if (self.videoTarget == RCTCameraCaptureTargetCameraRoll) {
ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init];
if ([library videoAtPathIsCompatibleWithSavedPhotosAlbum:outputFileURL]) {
[library writeVideoAtPathToSavedPhotosAlbum:outputFileURL
completionBlock:^(NSURL *assetURL, NSError *error) {
if (error) {
self.videoCallback(@[RCTMakeError(error.description, nil, nil)]);
return;
}

self.videoCallback(@[[NSNull null], [assetURL absoluteString]]);
}];
}
}
else if (self.videoTarget == RCTCameraCaptureTargetDisk) {
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths firstObject];
NSString *fullPath = [[documentsDirectory stringByAppendingPathComponent:[[NSUUID UUID] UUIDString]] stringByAppendingPathExtension:@"mov"];

NSFileManager * fileManager = [NSFileManager defaultManager];
NSError * error = nil;

//copying destination
if (!([fileManager copyItemAtPath:[outputFileURL path] toPath:fullPath error:&error])) {
self.videoCallback(@[RCTMakeError(error.description, nil, nil)]);
return;
}
self.videoCallback(@[[NSNull null], fullPath]);
}
else {
self.videoCallback(@[RCTMakeError(@"Target not supported", nil, nil)]);
}
}

- (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];
NSString *fullPath = [[documentsDirectory stringByAppendingPathComponent:name] stringByAppendingPathExtension:@"jpg"];

[fileManager createFileAtPath:fullPath contents:data attributes:nil];
return fullPath;
Expand All @@ -308,6 +440,7 @@ - (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputMetadataObjects:

[self.bridge.eventDispatcher sendDeviceEventWithName:@"CameraBarCodeRead"
body:@{
@"type": metadata.type,
@"data": metadata.stringValue,
@"bounds": @{
@"origin": @{
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
"author": "Lochlan Wansbrough <[email protected]> (http://lwansbrough.com)",
"nativePackage": true,
"peerDependencies": {
"react-native": ">=0.4.3"
"react-native": "*"
},
"keywords": [
"react-native",
Expand Down

0 comments on commit 71fd837

Please sign in to comment.