Skip to content

Instantly share code, notes, and snippets.

@2bbb
Created October 26, 2014 04:56
Show Gist options
  • Select an option

  • Save 2bbb/5dec24ad91771584255a to your computer and use it in GitHub Desktop.

Select an option

Save 2bbb/5dec24ad91771584255a to your computer and use it in GitHub Desktop.
TBTSimpleCamera
//
// 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
//
// 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