diff --git a/Camera.ios.js b/Camera.ios.js index 5ab85fe..db428ba 100644 --- a/Camera.ios.js +++ b/Camera.ios.js @@ -182,6 +182,10 @@ var Camera = React.createClass({ } NativeModules.CameraManager.capture(options, cb); + }, + + stopCapture() { + NativeModules.CameraManager.stopCapture(); } }); diff --git a/RCTCameraManager.h b/RCTCameraManager.h index a8e4d6f..04fffca 100644 --- a/RCTCameraManager.h +++ b/RCTCameraManager.h @@ -45,6 +45,7 @@ typedef NS_ENUM(NSInteger, RCTCameraFlashMode) { @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; @@ -56,5 +57,6 @@ typedef NS_ENUM(NSInteger, RCTCameraFlashMode) { - (void)changeFlashMode:(NSInteger)flashMode; - (AVCaptureDevice *)deviceWithMediaType:(NSString *)mediaType preferringPosition:(AVCaptureDevicePosition)position; - (void)capture:(NSDictionary*)options callback:(RCTResponseSenderBlock)callback; +- (void)stopCapture; @end diff --git a/RCTCameraManager.m b/RCTCameraManager.m index c560aea..dfbee60 100644 --- a/RCTCameraManager.m +++ b/RCTCameraManager.m @@ -9,7 +9,10 @@ #import #import -@implementation RCTCameraManager +@implementation RCTCameraManager { + NSInteger videoTarget; + RCTResponseSenderBlock videoCallback; +} RCT_EXPORT_MODULE(); @@ -93,6 +96,21 @@ RCT_EXPORT_VIEW_PROPERTY(flashMode, NSInteger); 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]) @@ -101,6 +119,13 @@ RCT_EXPORT_VIEW_PROPERTY(flashMode, NSInteger); [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]) { @@ -193,7 +218,23 @@ RCT_EXPORT_METHOD(capture:(NSDictionary *)options callback:(RCTResponseSenderBlo [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]; + int32_t preferredTimeScale = [[options valueForKey:@"preferredTimeScale"] intValue]; + CMTime maxDuration = CMTimeMakeWithSeconds(totalSeconds || 60, preferredTimeScale | 30); + self.movieFileOutput.maxRecordedDuration = maxDuration; + + [self captureVideo:captureTarget callback:callback]; + } +} + +RCT_EXPORT_METHOD(stopCapture) { + if (self.movieFileOutput.recording) { + [self.movieFileOutput stopRecording]; } } @@ -234,6 +275,93 @@ RCT_EXPORT_METHOD(capture:(NSDictionary *)options callback:(RCTResponseSenderBlo }]; } +-(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]; + + videoCallback = callback; + videoTarget = target; +} + +- (void)captureOutput:(AVCaptureFileOutput *)captureOutput +didFinishRecordingToOutputFileAtURL:(NSURL *)outputFileURL + fromConnections:(NSArray *)connections + error:(NSError *)error +{ + + NSLog(@"didFinishRecordingToOutputFileAtURL - enter"); + + BOOL RecordedSuccessfully = YES; + if ([error code] != noErr) + { + // A problem occurred: Find out if the recording was successful. + id value = [[error userInfo] objectForKey:AVErrorRecordingSuccessfullyFinishedKey]; + if (value) + { + RecordedSuccessfully = [value boolValue]; + } + } + if (RecordedSuccessfully) + { + //----- RECORDED SUCESSFULLY ----- + NSLog(@"didFinishRecordingToOutputFileAtURL - success"); + + if (videoTarget == RCTCameraCaptureTargetCameraRoll) { + ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init]; + if ([library videoAtPathIsCompatibleWithSavedPhotosAlbum:outputFileURL]) + { + [library writeVideoAtPathToSavedPhotosAlbum:outputFileURL + completionBlock:^(NSURL *assetURL, NSError *error) + { + if (error) + { + videoCallback(@[RCTMakeError(error.description, nil, nil)]); + return; + } + + videoCallback(@[[NSNull null], [assetURL absoluteString]]); + }]; + } + } + else if (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 ]) ) + { + videoCallback(@[RCTMakeError(error.description, nil, nil)]); + return; + } + videoCallback(@[[NSNull null], fullPath]); + } + else { + videoCallback(@[RCTMakeError(@"Target not supported", nil, nil)]); + } + + } else { + videoCallback(@[RCTMakeError(@"Error while recording", nil, nil)]); + } +} + - (NSString *)saveImage:(UIImage *)image withName:(NSString *)name { NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); NSString *documentsDirectory = [paths firstObject];