diff --git a/FastttCamera/FastttCamera.h b/FastttCamera/FastttCamera.h index 5fb47fc..722c845 100644 --- a/FastttCamera/FastttCamera.h +++ b/FastttCamera/FastttCamera.h @@ -8,6 +8,15 @@ #import #import "FastttCameraInterface.h" +#import "IFTTTDeviceOrientation.h" +#import "FastttFocus.h" +#import "CoreGraphics/CoreGraphics.h" +#import "CoreVideo/CoreVideo.h" +#import "CoreMedia/CoreMedia.h" +#import +#import +#import +#import /** * Public class for you to use to create a standard FastttCamera! diff --git a/FastttCamera/FastttCamera.m b/FastttCamera/FastttCamera.m index 75a2213..877ea7f 100644 --- a/FastttCamera/FastttCamera.m +++ b/FastttCamera/FastttCamera.m @@ -16,7 +16,7 @@ #import "FastttZoom.h" #import "FastttCapturedImage+Process.h" -@interface FastttCamera () +@interface FastttCamera () @property (nonatomic, strong) IFTTTDeviceOrientation *deviceOrientation; @property (nonatomic, strong) FastttFocus *fastFocus; @@ -25,6 +25,7 @@ @interface FastttCamera () @property (nonatomic, strong) AVCaptureVideoPreviewLayer *previewLayer; @property (nonatomic, strong) AVCaptureStillImageOutput *stillImageOutput; @property (nonatomic, assign) BOOL deviceAuthorized; +@property(nonatomic, retain) AVCaptureMovieFileOutput *movieFileOutput; @property (nonatomic, assign) BOOL isCapturingImage; @end @@ -48,7 +49,12 @@ @implementation FastttCamera scalesImage = _scalesImage, cameraDevice = _cameraDevice, cameraFlashMode = _cameraFlashMode, - cameraTorchMode = _cameraTorchMode; + cameraTorchMode = _cameraTorchMode, + movieFileOutput = _movieFileOutput, + normalizesVideoOrientation = _normalizesVideoOrientation, + cropsVideoToVisibleAspectRatio = _cropsVideoToVisibleAspectRatio, + sessionPreset = _sessionPreset, + forcedDeviceOrientationForVideo = _forcedDeviceOrientationForVideo; - (instancetype)init { @@ -65,12 +71,16 @@ - (instancetype)init _maxScaledDimension = 0.f; _maxZoomFactor = 1.f; _normalizesImageOrientations = YES; + _normalizesVideoOrientation = YES; + _cropsVideoToVisibleAspectRatio = YES; _returnsRotatedPreview = YES; _interfaceRotatesWithOrientation = YES; _fixedInterfaceOrientation = UIDeviceOrientationPortrait; _cameraDevice = FastttCameraDeviceRear; _cameraFlashMode = FastttCameraFlashModeOff; _cameraTorchMode = FastttCameraTorchModeOff; + _sessionPreset = AVCaptureSessionPresetMedium; + _forcedDeviceOrientationForVideo = nil; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationWillEnterForeground:) @@ -302,7 +312,7 @@ - (void)setCameraDevice:(FastttCameraDevice)cameraDevice if (_cameraDevice != cameraDevice) { _cameraDevice = cameraDevice; - AVCaptureDeviceInput *oldInput = [_session.inputs lastObject]; + AVCaptureDeviceInput *oldInput = [self _currentCameraInput]; AVCaptureDeviceInput *newInput = [AVCaptureDeviceInput deviceInputWithDevice:device error:nil]; [_session beginConfiguration]; @@ -411,7 +421,8 @@ - (void)_setupCaptureSession dispatch_async(dispatch_get_main_queue(), ^{ _session = [AVCaptureSession new]; - _session.sessionPreset = AVCaptureSessionPresetPhoto; + _session.automaticallyConfiguresApplicationAudioSession = false; + _session.sessionPreset = _sessionPreset; AVCaptureDevice *device = [AVCaptureDevice cameraDevice:self.cameraDevice]; @@ -429,10 +440,12 @@ - (void)_setupCaptureSession [device unlockForConfiguration]; } + [_session beginConfiguration]; + #if !TARGET_IPHONE_SIMULATOR AVCaptureDeviceInput *deviceInput = [AVCaptureDeviceInput deviceInputWithDevice:device error:nil]; [_session addInput:deviceInput]; - + switch (device.position) { case AVCaptureDevicePositionBack: _cameraDevice = FastttCameraDeviceRear; @@ -448,14 +461,19 @@ - (void)_setupCaptureSession [self setCameraFlashMode:_cameraFlashMode]; #endif - - NSDictionary *outputSettings = @{AVVideoCodecKey:AVVideoCodecJPEG}; - + + NSDictionary *outputSettings = @{AVVideoCodecKey : AVVideoCodecJPEG}; + _stillImageOutput = [AVCaptureStillImageOutput new]; _stillImageOutput.outputSettings = outputSettings; [_session addOutput:_stillImageOutput]; + + _movieFileOutput = [AVCaptureMovieFileOutput new]; + [_session addOutput:_movieFileOutput]; + [_session commitConfiguration]; + _deviceOrientation = [IFTTTDeviceOrientation new]; if (self.isViewLoaded && self.view.window) { @@ -489,6 +507,8 @@ - (void)_teardownCaptureSession [_session removeOutput:_stillImageOutput]; _stillImageOutput = nil; + [_session removeOutput:_movieFileOutput]; + _movieFileOutput = nil; [self _removePreviewLayer]; @@ -552,6 +572,87 @@ - (void)_takePhoto #endif } +#pragma mark - Capturing Video + +- (void)startRecordingVideo { + + [_session beginConfiguration]; + for (AVCaptureInput *captureInput in [_session inputs]) { + BOOL isAudioInput = NO; + for (AVCaptureInputPort *port in [captureInput ports]) { + if ([[port mediaType] isEqual:AVMediaTypeAudio]) { + isAudioInput = YES; + break; + } + } + + if (isAudioInput) { + [_session removeInput:captureInput]; + } + } + + AVCaptureDevice *audioDevice = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeAudio]; + AVCaptureDeviceInput *audioInput = [AVCaptureDeviceInput deviceInputWithDevice:audioDevice error:nil]; + [_session addInput:audioInput]; + [_session commitConfiguration]; + + AVCaptureConnection *videoConnection = nil; + + for (AVCaptureConnection *connection in [_movieFileOutput connections]) { + for (AVCaptureInputPort *port in [connection inputPorts]) { + if ([[port mediaType] isEqual:AVMediaTypeVideo]) { + videoConnection = connection; + break; + } + } + + if (videoConnection) { + break; + } + } + + if (self.forcedDeviceOrientationForVideo) { + [videoConnection setVideoOrientation: self.forcedDeviceOrientationForVideo]; + } else if ([videoConnection isVideoOrientationSupported]) { + [videoConnection setVideoOrientation:[self _currentCaptureVideoOrientationForDevice]]; + } + + if ([videoConnection isVideoMirroringSupported]) { + [videoConnection setVideoMirrored:(_cameraDevice == FastttCameraDeviceFront)]; + } + + NSString *plistPath; + NSString *rootPath; + rootPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0]; + plistPath = [rootPath stringByAppendingPathComponent:@"temp.mov"]; + NSURL *fileURL = [[NSURL alloc] initFileURLWithPath:plistPath]; + NSFileManager *fileManager = [NSFileManager defaultManager]; + if ([fileManager fileExistsAtPath:plistPath]) { + NSError *error; + + [fileManager removeItemAtPath:[fileURL absoluteString] error:&error]; + } + [_movieFileOutput startRecordingToOutputFileURL:fileURL recordingDelegate:self]; +} + +- (void)stopRecordingVideo { + [_movieFileOutput stopRecording]; +} + +#pragma mark - AVCaptureFileOutputRecordingDelegate + +- (void)captureOutput:(AVCaptureFileOutput *)captureOutput + didFinishRecordingToOutputFileAtURL:(NSURL *)outputFileURL + fromConnections:(NSArray *)connections + error:(NSError *)error { + + if ([self.delegate respondsToSelector:@selector(cameraController:didFinishRecordingVideo:)]) { + [self.delegate cameraController:self didFinishRecordingVideo:outputFileURL]; + } +} + +#pragma mark - Capturing + #pragma mark - Processing a Photo - (void)_processCameraPhoto:(UIImage *)image needsPreviewRotation:(BOOL)needsPreviewRotation previewOrientation:(UIDeviceOrientation)previewOrientation @@ -707,9 +808,22 @@ - (void)_checkDeviceAuthorizationWithCompletion:(void (^)(BOOL isAuthorized))com #pragma mark - FastttCameraDevice +- (AVCaptureDeviceInput *)_currentCameraInput { + AVCaptureDeviceInput *currentCameraInput; + + for (AVCaptureDeviceInput *captureInput in _session.inputs) { + NSArray *mediaTypes = [captureInput.ports valueForKey:@"mediaType"]; + if ([mediaTypes containsObject:AVMediaTypeVideo]) { + currentCameraInput = captureInput; + } + } + return currentCameraInput; + +} + - (AVCaptureDevice *)_currentCameraDevice { - return [_session.inputs.lastObject device]; + return [[self _currentCameraInput] device]; } - (AVCaptureConnection *)_currentCaptureConnection diff --git a/FastttCamera/FastttCameraInterface.h b/FastttCamera/FastttCameraInterface.h index 13cd05c..1d25bb2 100644 --- a/FastttCamera/FastttCameraInterface.h +++ b/FastttCamera/FastttCameraInterface.h @@ -119,6 +119,26 @@ * Make sure to also set interfaceRotatesWithOrientation = YES, otherwise this property will be ignored. */ @property (nonatomic, assign) UIDeviceOrientation fixedInterfaceOrientation; +/** + * Defaults to YES. If this property is set to YES, the video orientation will match that of the preview video. + */ +@property(nonatomic, assign) BOOL normalizesVideoOrientation; + +/** + * Defaults to YES. If this property is set to YES, the video's size will crop to that of the preview video. + */ +@property(nonatomic, assign) BOOL cropsVideoToVisibleAspectRatio; + +/** + * Default to AVCaptureSessionPresetMedium. + */ + +@property (nonatomic, assign) AVCaptureSessionPreset sessionPreset; + +/** + * Default to nil. + */ +@property (nonatomic, assign) UIDeviceOrientation forcedDeviceOrientationForVideo; #pragma mark - Camera State @@ -222,6 +242,17 @@ */ - (void)takePicture; +#pragma mark - Take a video! + +/** + * Triggers the camera to start recording a video. + */ +- (void)startRecordingVideo; + +/** + * Triggers the camera to stop recording a video. + */ +- (void)stopRecordingVideo; #pragma mark - Process a photo @@ -359,4 +390,22 @@ */ - (void)userDeniedCameraPermissionsForCameraController:(id)cameraController; +/** + * Called when the camera controller has finished recording video + * + * @param cameraController The FastttCamera instance that captured the photo. + * + * @param videoURL Location of the video without the proper cropping and orientation + */ +- (void)cameraController:(id)cameraController didFinishRecordingVideo:(NSURL *)videoURL; + +/** + * Called when the camera controller has finished recording video + * + * @param cameraController The FastttCamera instance that captured the photo. + * + * @param videoURL Location of the video with the proper cropping and orientation + */ +- (void)cameraController:(id)cameraController didFinishNormalizedCapturedVideo:(NSURL *)videoURL; + @end