Created
October 26, 2014 04:56
-
-
Save 2bbb/5dec24ad91771584255a to your computer and use it in GitHub Desktop.
TBTSimpleCamera
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // | |
| // TBTSimpleCamera.h | |
| // | |
| // Created by ISHII 2bit on 2014/10/08. | |
| // Copyright (c) 2014年 buffer Renaiss. All rights reserved. | |
| // | |
| #import <Foundation/Foundation.h> | |
| typedef void(^TBTDidStartRecordingCallback)(NSURL *fileURL); | |
| typedef void(^TBTDidFinishRecordingCallback)(NSURL *outputFileURL, BOOL successRecording, NSError *error); | |
| typedef void(^TBTDidFinishShootingCallback)(UIImage *image, NSDictionary *exif); | |
| @interface TBTSimpleCamera : NSObject | |
| - (void)initCaptureSession; | |
| - (void)startCapture; | |
| - (void)stopCapture; | |
| - (BOOL)startRecording:(NSString *)filePath | |
| didStartCallback:(TBTDidStartRecordingCallback)callback; | |
| - (void)stopRecording:(TBTDidFinishRecordingCallback)callback; | |
| - (void)enableTakePhoto; | |
| - (void)disableTakePhoto; | |
| - (void)shoot:(TBTDidFinishShootingCallback)callback; | |
| - (void)toggleCameraPosition; | |
| - (BOOL)isRecordingNow; | |
| @property (nonatomic, strong) UIView *videoView; | |
| @property (nonatomic, setter=setUseFrontCamera:) BOOL isUseFrontCamera; | |
| @property (nonatomic, setter=setRecordSound:) BOOL isRecordSound; | |
| @end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // | |
| // TBTSimpleCamera.m | |
| // | |
| // Created by ISHII 2bit on 2014/10/08. | |
| // Copyright (c) 2014年 buffer Renaiss. All rights reserved. | |
| // | |
| #import "TBTSimpleCamera.h" | |
| #import <AVFoundation/AVFoundation.h> | |
| #import <ImageIO/ImageIO.h> | |
| @interface TBTSimpleCamera () < | |
| AVCaptureFileOutputRecordingDelegate | |
| > { | |
| AVCaptureSession *captureSession; | |
| AVCaptureMovieFileOutput *movieFileOutput; | |
| AVCaptureStillImageOutput *stillImageOutput; | |
| TBTDidStartRecordingCallback startCallback; | |
| TBTDidFinishRecordingCallback finishCallback; | |
| TBTDidFinishShootingCallback shootCallback; | |
| } | |
| @end | |
| static const int kCMTimeUnit = 600; | |
| static const int kMaxRecordedDuration = 10 * 60; | |
| static const int kMinFreeDiskSpaceLimit = 100 * 1024 * 1024; | |
| @implementation TBTSimpleCamera | |
| @synthesize videoView; | |
| - (BOOL)isRecordingNow { | |
| return movieFileOutput && movieFileOutput.isRecording; | |
| } | |
| - (void)initCaptureSession { | |
| captureSession = [[AVCaptureSession alloc] init]; | |
| if([captureSession canSetSessionPreset:AVCaptureSessionPreset1280x720]) { | |
| [captureSession setSessionPreset:AVCaptureSessionPreset1280x720]; | |
| LogInfo(@"preset 1280 720"); | |
| } | |
| NSError *error = nil; | |
| AVCaptureDevice *captureDevice = nil; | |
| NSArray *videoDevices = [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo]; | |
| for(AVCaptureDevice *device in videoDevices) { | |
| if(device.position == (self.isUseFrontCamera ? AVCaptureDevicePositionFront : AVCaptureDevicePositionBack)) { | |
| captureDevice = device; | |
| } | |
| } | |
| AVCaptureDeviceInput *input = [AVCaptureDeviceInput deviceInputWithDevice:captureDevice | |
| error:&error]; | |
| if (!input) { | |
| LogError(@"video input error"); | |
| return; | |
| } else { | |
| [captureSession addInput:input]; | |
| } | |
| if(self.isRecordSound) { | |
| AVCaptureDevice *audioCaptureDevice = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeAudio]; | |
| AVCaptureDeviceInput *audioInput = [AVCaptureDeviceInput deviceInputWithDevice:audioCaptureDevice | |
| error:&error]; | |
| if(!audioInput) { | |
| LogError(@"audio input error"); | |
| return; | |
| } else { | |
| [captureSession addInput:audioInput]; | |
| } | |
| } | |
| if([captureDevice isFocusModeSupported:AVCaptureFocusModeAutoFocus]) { | |
| NSError *error = nil; | |
| [captureDevice lockForConfiguration:&error]; | |
| if(!error) { | |
| [captureDevice setFocusMode:AVCaptureFocusModeAutoFocus]; | |
| } | |
| [captureDevice unlockForConfiguration]; | |
| } | |
| NSDictionary *settings = @{(id)kCVPixelBufferPixelFormatTypeKey : @(kCVPixelFormatType_32BGRA)}; | |
| AVCaptureVideoDataOutput *videoOutput = AVCaptureVideoDataOutput.new; | |
| [videoOutput setAlwaysDiscardsLateVideoFrames:YES]; | |
| [captureSession addOutput:videoOutput]; | |
| [videoOutput setVideoSettings:settings]; | |
| AVCaptureVideoPreviewLayer *videoPreviewLayer = [AVCaptureVideoPreviewLayer layerWithSession:captureSession]; | |
| [videoPreviewLayer setVideoGravity:AVLayerVideoGravityResizeAspectFill]; | |
| AVCaptureConnection *connection = videoOutput.connections.firstObject; | |
| AVCaptureVideoOrientation orientation = AVCaptureVideoOrientationLandscapeRight; | |
| if([connection isVideoOrientationSupported]) { | |
| connection.videoOrientation = orientation; | |
| } | |
| videoPreviewLayer.frame = videoView.bounds; | |
| if(videoPreviewLayer.connection.isVideoOrientationSupported) { | |
| videoPreviewLayer.connection.videoOrientation = orientation; | |
| } | |
| if([connection isVideoStabilizationSupported]) { | |
| LogTrace(@"video stablization supported"); | |
| [connection enablesVideoStabilizationWhenAvailable]; | |
| } else { | |
| LogWarning(@"video stabilization not supported"); | |
| } | |
| [videoView.layer addSublayer:videoPreviewLayer]; | |
| } | |
| - (void)setUseFrontCamera:(BOOL)isUseFrontCamera { | |
| if(_isUseFrontCamera != isUseFrontCamera && captureSession) { | |
| [self toggleCameraPosition]; | |
| } | |
| _isUseFrontCamera = isUseFrontCamera; | |
| } | |
| - (void)setRecordSound:(BOOL)isRecordSound { | |
| if(_isRecordSound != isRecordSound && captureSession) { | |
| [captureSession beginConfiguration]; | |
| NSError *error = nil; | |
| AVCaptureDevice *audioCaptureDevice = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeAudio]; | |
| AVCaptureDeviceInput *audioInput = [AVCaptureDeviceInput deviceInputWithDevice:audioCaptureDevice | |
| error:&error]; | |
| if(!audioInput) { | |
| LogError(@"audio input error"); | |
| return; | |
| } else { | |
| if(isRecordSound) { | |
| [captureSession addInput:audioInput]; | |
| } else { | |
| [captureSession removeInput:audioInput]; | |
| } | |
| } | |
| [captureSession commitConfiguration]; | |
| } | |
| _isRecordSound = isRecordSound; | |
| } | |
| - (void)startCapture { | |
| if(captureSession && !captureSession.isRunning) { | |
| [captureSession startRunning]; | |
| } | |
| } | |
| - (void)stopCapture { | |
| if(captureSession && captureSession.isRunning) { | |
| [captureSession stopRunning]; | |
| } | |
| } | |
| - (BOOL)startRecording:(NSString *)filePath | |
| didStartCallback:(TBTDidStartRecordingCallback)callback | |
| { | |
| if(movieFileOutput.isRecording) return NO; | |
| startCallback = callback; | |
| [captureSession beginConfiguration]; | |
| movieFileOutput = AVCaptureMovieFileOutput.new; | |
| movieFileOutput.minFreeDiskSpaceLimit = kMinFreeDiskSpaceLimit; | |
| movieFileOutput.maxRecordedDuration = CMTimeMake(kMaxRecordedDuration * kCMTimeUnit, kCMTimeUnit); | |
| if([captureSession canAddOutput:movieFileOutput]) { | |
| [captureSession addOutput:movieFileOutput]; | |
| } | |
| // AVCaptureConnection *connection = [movieFileOutput connectionWithMediaType:AVMediaTypeVideo]; | |
| // if(connection.isVideoOrientationSupported) { | |
| // connection.videoOrientation = AVCaptureVideoOrientationLandscapeRight; | |
| // } | |
| for(AVCaptureConnection *connection in movieFileOutput.connections) { | |
| if(connection.isVideoOrientationSupported) { | |
| connection.videoOrientation = AVCaptureVideoOrientationLandscapeRight; | |
| } | |
| } | |
| [captureSession commitConfiguration]; | |
| NSURL *fileURL = filePath.toFileURL; | |
| [movieFileOutput startRecordingToOutputFileURL:fileURL | |
| recordingDelegate:self]; | |
| return YES; | |
| } | |
| - (void)stopRecording:(TBTDidFinishRecordingCallback)callback { | |
| finishCallback = callback; | |
| if(movieFileOutput.isRecording) { | |
| [movieFileOutput stopRecording]; | |
| } | |
| } | |
| - (void)enableTakePhoto { | |
| stillImageOutput = AVCaptureStillImageOutput.new; | |
| NSDictionary *outputSettings = @{AVVideoCodecKey: AVVideoCodecJPEG}; | |
| [stillImageOutput setOutputSettings:outputSettings]; | |
| [captureSession addOutput:stillImageOutput]; | |
| } | |
| - (void)disableTakePhoto { | |
| if(stillImageOutput) { | |
| [captureSession removeOutput:stillImageOutput]; | |
| } | |
| stillImageOutput = nil; | |
| } | |
| - (void)shoot:(TBTDidFinishShootingCallback)callback { | |
| if(stillImageOutput == nil) { | |
| LogError(@"still image output is nil"); | |
| return; | |
| } | |
| AVCaptureConnection *videoConnection = [stillImageOutput connectionWithMediaType:AVMediaTypeVideo]; | |
| NSLog(@"about to request a capture from: %@", stillImageOutput); | |
| [stillImageOutput captureStillImageAsynchronouslyFromConnection:videoConnection | |
| completionHandler:^(CMSampleBufferRef imageSampleBuffer, NSError *error) { | |
| CFDictionaryRef exifAttachments = CMGetAttachment(imageSampleBuffer, kCGImagePropertyExifDictionary, NULL); | |
| if (exifAttachments) { | |
| NSLog(@"attachements: %@", exifAttachments); | |
| } else { | |
| NSLog(@"no attachments"); | |
| } | |
| NSDictionary *exif = (__bridge NSDictionary *)exifAttachments; | |
| NSData *imageData = [AVCaptureStillImageOutput jpegStillImageNSDataRepresentation:imageSampleBuffer]; | |
| UIImage *image = [[UIImage alloc] initWithData:imageData]; | |
| callback(image, exif); | |
| }]; | |
| } | |
| - (void)toggleCameraPosition { | |
| [captureSession beginConfiguration]; | |
| // while(captureSession.isRunning) usleep(100); | |
| AVCaptureDevice *captureDevice = nil; | |
| NSArray *videoDevices = [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo]; | |
| if([[captureSession inputs][0] device].position == AVCaptureDevicePositionFront) { | |
| for(AVCaptureDevice *device in videoDevices) { | |
| if(device.position == AVCaptureDevicePositionBack) { | |
| captureDevice = device; | |
| _isUseFrontCamera = NO; | |
| break; | |
| } | |
| } | |
| } else { | |
| for(AVCaptureDevice *device in videoDevices) { | |
| if(device.position == AVCaptureDevicePositionFront) { | |
| captureDevice = device; | |
| _isUseFrontCamera = YES; | |
| break; | |
| } | |
| } | |
| } | |
| NSError *error = nil; | |
| AVCaptureDeviceInput *input = [AVCaptureDeviceInput deviceInputWithDevice:captureDevice error:&error]; | |
| for(AVCaptureDeviceInput *input_ in captureSession.inputs) { | |
| [captureSession removeInput:input_]; | |
| } | |
| [captureSession addInput:input]; | |
| [captureSession commitConfiguration]; | |
| } | |
| #pragma mark AVCaptureFileOutputRecordingDelegate | |
| - (void) captureOutput:(AVCaptureFileOutput *)captureOutput | |
| didStartRecordingToOutputFileAtURL:(NSURL *)fileURL | |
| fromConnections:(NSArray *)connections | |
| { | |
| LogTrace(@"start recording %@", fileURL.absoluteString); | |
| if(startCallback) startCallback(fileURL); | |
| } | |
| - (void) captureOutput:(AVCaptureFileOutput *)captureOutput | |
| didFinishRecordingToOutputFileAtURL:(NSURL *)outputFileURL | |
| fromConnections:(NSArray *)connections | |
| error:(NSError *)error | |
| { | |
| 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) { | |
| LogDebug(@"success writing to file."); | |
| } else { | |
| LogError(@"failure writing to file."); | |
| } | |
| [captureSession removeOutput:movieFileOutput]; | |
| movieFileOutput = nil; | |
| finishCallback(outputFileURL, recordedSuccessfully, error); | |
| finishCallback = nil; | |
| } | |
| @end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment