一般如果UI和UE在设计时只要求功能,对相机界面没什么要求的话,个人觉得调用系统相机(UIImagePickerController)就可以满足我们的需求比如照相或者录制视频,但是考虑界面美观性,有时候就需要我们自定义拍摄界面,此时系统相机已经满足不了我们的需求,跟多的是要跟AVFoundation打交道,

AVCaptureSession:媒体(音、视频)捕获会话,负责把捕获的音视频数据输出到输出设备中,一个AVCaptureSession可以有多个输入输出(前面的是比较专业的说法,然并卵,
个人理解这其实相当于一个会话,连接了音频之间的输入和输出,即从你开始拍照或者录制视频到最后出现照片或者视频,整个过程之间的数据流是由它管理的)

AVCaptureDevice:输入设备,包括麦克风、摄像头,通过该对象可以设置物理设备的一些属性(例如相机聚焦、白平衡等)

AVCaptureDeviceInput:设备输入数据管理对象,可以根据AVCaptureDevice创建对应的AVCaptureDeviceInput对象,该对象将会被添加到AVCaptureSession中管理。(即一个输入设备对应一个输入管理对象,然后把它加入会话中)

AVCaptureOutput:输出数据管理对象,用于接收各类输出数据,但是通常我们不直接用它更多的使用它的子类AVCaptureStillImageOutput、AVCaptureMovieFileOutput(相对应的它也是要加入会话的)

拍照:

//
// TakephotoVC.m
// AVFoundation自定义拍照录像
//
// Created by JianF.Sun on 17/3/20.
// Copyright © 2017年 sjf. All rights reserved.
// #import "TakephotoVC.h"
#import <AVFoundation/AVFoundation.h>
#import <AssetsLibrary/AssetsLibrary.h> #define Screen_width [UIScreen mainScreen].bounds.size.width
#define Screen_height [UIScreen mainScreen].bounds.size.height typedef void(^PropertyChangeBlock)(AVCaptureDevice *captureDevice);
@interface TakephotoVC () @property (strong,nonatomic) AVCaptureSession *captureSession;//负责输入和输出设备之间的数据传递
@property (strong,nonatomic) AVCaptureDeviceInput *captureDeviceInput;//负责从AVCaptureDevice获得输入数据
@property (strong,nonatomic) AVCaptureStillImageOutput *captureStillImageOutput;//照片输出流
@property (strong,nonatomic) AVCaptureVideoPreviewLayer *captureVideoPreviewLayer;//相机拍摄预览图层
@property (weak, nonatomic) IBOutlet UIView *viewContainer;
@property (weak, nonatomic) IBOutlet UIButton *takeButton;//拍照按钮
@property (weak, nonatomic) IBOutlet UIButton *flashAutoButton;//自动闪光灯按钮
@property (weak, nonatomic) IBOutlet UIButton *flashOnButton;//打开闪光灯按钮
@property (weak, nonatomic) IBOutlet UIButton *flashOffButton;//关闭闪光灯按钮
@property (weak, nonatomic) IBOutlet UIImageView *focusCursor; //聚焦光标
@end @implementation TakephotoVC - (void)viewDidLoad {
[super viewDidLoad]; // Do any additional setup after loading the view from its nib.
// NSLog(@"%f",[UIScreen mainScreen].bounds.size.height);
}
-(void)viewWillAppear:(BOOL)animated{
[super viewWillAppear:animated];
//初始化会话
_captureSession=[[AVCaptureSession alloc]init];
if ([_captureSession canSetSessionPreset:AVCaptureSessionPreset1280x720]) {//设置分辨率
_captureSession.sessionPreset=AVCaptureSessionPreset1280x720;
}
//获得输入设备,后置摄像头
AVCaptureDevice *captureDevice=[self getCameraDeviceWithPosition:AVCaptureDevicePositionBack];//取得后置摄像头
if (!captureDevice) {
NSLog(@"取得后置摄像头时出现问题.");
return;
} NSError *error=nil;
//根据输入设备初始化设备输入对象,用于获得输入数据
_captureDeviceInput=[[AVCaptureDeviceInput alloc]initWithDevice:captureDevice error:&error];
if (error) {
NSLog(@"取得设备输入对象时出错,错误原因:%@",error.localizedDescription);
return;
}
//初始化设备输出对象,用于获得输出数据
_captureStillImageOutput=[[AVCaptureStillImageOutput alloc]init];
NSDictionary *outputSettings = @{AVVideoCodecKey:AVVideoCodecJPEG};
[_captureStillImageOutput setOutputSettings:outputSettings];//输出设置 //将设备输入添加到会话中
if ([_captureSession canAddInput:_captureDeviceInput]) {
[_captureSession addInput:_captureDeviceInput];
} //将设备输出添加到会话中
if ([_captureSession canAddOutput:_captureStillImageOutput]) {
[_captureSession addOutput:_captureStillImageOutput];
} //创建视频预览层,用于实时展示摄像头状态
_captureVideoPreviewLayer=[[AVCaptureVideoPreviewLayer alloc]initWithSession:self.captureSession]; CALayer *layer=self.viewContainer.layer;
// layer.masksToBounds=YES; _captureVideoPreviewLayer.frame=CGRectMake(, ,Screen_width, Screen_height);
_captureVideoPreviewLayer.position=CGPointMake(Screen_width/, Screen_height/);
_captureVideoPreviewLayer.videoGravity=AVLayerVideoGravityResizeAspect;//
//将视频预览层添加到界面中
//[layer addSublayer:_captureVideoPreviewLayer];
[layer insertSublayer:_captureVideoPreviewLayer below:self.focusCursor.layer]; [self addNotificationToCaptureDevice:captureDevice];
[self addGenstureRecognizer];
[self setFlashModeButtonStatus]; }
-(void)viewDidAppear:(BOOL)animated{
[super viewDidAppear:animated];
[self.captureSession startRunning];
} -(void)viewDidDisappear:(BOOL)animated{
[super viewDidDisappear:animated];
[self.captureSession stopRunning];
} #pragma mark - 通知
/**
* 给输入设备添加通知
*/
-(void)addNotificationToCaptureDevice:(AVCaptureDevice *)captureDevice{
//注意添加区域改变捕获通知必须首先设置设备允许捕获
[self changeDeviceProperty:^(AVCaptureDevice *captureDevice) {
captureDevice.subjectAreaChangeMonitoringEnabled=YES;
}];
NSNotificationCenter *notificationCenter= [NSNotificationCenter defaultCenter];
//捕获区域发生改变
[notificationCenter addObserver:self selector:@selector(areaChange:) name:AVCaptureDeviceSubjectAreaDidChangeNotification object:captureDevice];
} -(void)removeNotificationFromCaptureDevice:(AVCaptureDevice *)captureDevice{
NSNotificationCenter *notificationCenter= [NSNotificationCenter defaultCenter];
[notificationCenter removeObserver:self name:AVCaptureDeviceSubjectAreaDidChangeNotification object:captureDevice];
}
/**
* 移除所有通知
*/
-(void)removeNotification{
NSNotificationCenter *notificationCenter= [NSNotificationCenter defaultCenter];
[notificationCenter removeObserver:self];
} -(void)addNotificationToCaptureSession:(AVCaptureSession *)captureSession{
NSNotificationCenter *notificationCenter= [NSNotificationCenter defaultCenter];
//会话出错
[notificationCenter addObserver:self selector:@selector(sessionRuntimeError:) name:AVCaptureSessionRuntimeErrorNotification object:captureSession];
} #pragma mark - 私有方法 /**
* 会话出错
*
* @param notification 通知对象
*/
-(void)sessionRuntimeError:(NSNotification *)notification{
NSLog(@"会话发生错误.");
} /**
* 捕获区域改变
*
* @param notification 通知对象
*/
-(void)areaChange:(NSNotification *)notification{
NSLog(@"捕获区域改变...");
}
/**
* 取得指定位置的摄像头
*
* @param position 摄像头位置
*
* @return 摄像头设备
*/
-(AVCaptureDevice *)getCameraDeviceWithPosition:(AVCaptureDevicePosition )position{
NSArray *cameras= [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo];
for (AVCaptureDevice *camera in cameras) {
if ([camera position]==position) {
return camera;
}
}
return nil;
}
/**
* 设置闪光灯模式
*
* @param flashMode 闪光灯模式
*/
-(void)setFlashMode:(AVCaptureFlashMode )flashMode{
[self changeDeviceProperty:^(AVCaptureDevice *captureDevice) {
if ([captureDevice isFlashModeSupported:flashMode]) {
[captureDevice setFlashMode:flashMode];
}
}];
} /**
* 设置闪光灯按钮状态
*/
-(void)setFlashModeButtonStatus{
AVCaptureDevice *captureDevice=[self.captureDeviceInput device];
AVCaptureFlashMode flashMode=captureDevice.flashMode; if([captureDevice isFlashAvailable]){
self.flashAutoButton.hidden=NO;
self.flashOnButton.hidden=NO;
self.flashOffButton.hidden=NO;
self.flashAutoButton.enabled=YES;
self.flashOnButton.enabled=YES;
self.flashOffButton.enabled=YES;
switch (flashMode) {
case AVCaptureFlashModeAuto:
self.flashAutoButton.enabled=NO;
break;
case AVCaptureFlashModeOn:
self.flashOnButton.enabled=NO;
break;
case AVCaptureFlashModeOff:
self.flashOffButton.enabled=NO;
break;
default:
break;
}
}else{
self.flashAutoButton.hidden=YES;
self.flashOnButton.hidden=YES;
self.flashOffButton.hidden=YES;
}
}
/**
* 改变设备属性的统一操作方法
*
* @param propertyChange 属性改变操作
*/
-(void)changeDeviceProperty:(PropertyChangeBlock)propertyChange{
AVCaptureDevice *captureDevice= [self.captureDeviceInput device];
NSError *error;
//注意改变设备属性前一定要首先调用lockForConfiguration:调用完之后使用unlockForConfiguration方法解锁
if ([captureDevice lockForConfiguration:&error]) {
propertyChange(captureDevice);
[captureDevice unlockForConfiguration];
}else{
NSLog(@"设置设备属性过程发生错误,错误信息:%@",error.localizedDescription);
}
} /**
* 设置聚焦点
*
* @param point 聚焦点
*/
-(void)focusWithMode:(AVCaptureFocusMode)focusMode exposureMode:(AVCaptureExposureMode)exposureMode atPoint:(CGPoint)point{
[self changeDeviceProperty:^(AVCaptureDevice *captureDevice) {
if ([captureDevice isFocusModeSupported:focusMode]) {
[captureDevice setFocusMode:AVCaptureFocusModeAutoFocus];
}
if ([captureDevice isFocusPointOfInterestSupported]) {
[captureDevice setFocusPointOfInterest:point];
}
if ([captureDevice isExposureModeSupported:exposureMode]) {
[captureDevice setExposureMode:AVCaptureExposureModeAutoExpose];
}
if ([captureDevice isExposurePointOfInterestSupported]) {
[captureDevice setExposurePointOfInterest:point];
}
}];
} /**
* 添加点按手势,点按时聚焦
*/
-(void)addGenstureRecognizer{
UITapGestureRecognizer *tapGesture=[[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(tapScreen:)];
[self.viewContainer addGestureRecognizer:tapGesture];
}
-(void)tapScreen:(UITapGestureRecognizer *)tapGesture{
CGPoint point= [tapGesture locationInView:self.viewContainer];
//将UI坐标转化为摄像头坐标
CGPoint cameraPoint= [self.captureVideoPreviewLayer captureDevicePointOfInterestForPoint:point];
[self setFocusCursorWithPoint:point];
[self focusWithMode:AVCaptureFocusModeAutoFocus exposureMode:AVCaptureExposureModeAutoExpose atPoint:cameraPoint];
}
/**
* 设置聚焦光标位置
*
* @param point 光标位置
*/
-(void)setFocusCursorWithPoint:(CGPoint)point{
self.focusCursor.center=point;
self.focusCursor.transform=CGAffineTransformMakeScale(1.5, 1.5);
self.focusCursor.alpha=1.0;
[UIView animateWithDuration:1.0 animations:^{
self.focusCursor.transform=CGAffineTransformIdentity;
} completion:^(BOOL finished) {
self.focusCursor.alpha=; }];
} #pragma mark 切换前后摄像头
- (IBAction)toggleButtonClick:(UIButton *)sender {
AVCaptureDevice *currentDevice=[self.captureDeviceInput device];
AVCaptureDevicePosition currentPosition=[currentDevice position];
[self removeNotificationFromCaptureDevice:currentDevice];
AVCaptureDevice *toChangeDevice;
AVCaptureDevicePosition toChangePosition=AVCaptureDevicePositionFront;
if (currentPosition==AVCaptureDevicePositionUnspecified||currentPosition==AVCaptureDevicePositionFront) {
toChangePosition=AVCaptureDevicePositionBack;
}
toChangeDevice=[self getCameraDeviceWithPosition:toChangePosition];
[self addNotificationToCaptureDevice:toChangeDevice];
//获得要调整的设备输入对象
AVCaptureDeviceInput *toChangeDeviceInput=[[AVCaptureDeviceInput alloc]initWithDevice:toChangeDevice error:nil]; //改变会话的配置前一定要先开启配置,配置完成后提交配置改变
[self.captureSession beginConfiguration];
//移除原有输入对象
[self.captureSession removeInput:self.captureDeviceInput];
//添加新的输入对象
if ([self.captureSession canAddInput:toChangeDeviceInput]) {
[self.captureSession addInput:toChangeDeviceInput];
self.captureDeviceInput=toChangeDeviceInput;
}
//提交会话配置
[self.captureSession commitConfiguration]; [self setFlashModeButtonStatus];
}
- (IBAction)closeFlash:(UIButton *)sender { [self setFlashMode:AVCaptureFlashModeOff];
[self setFlashModeButtonStatus];
}
- (IBAction)openFlash:(UIButton *)sender { [self setFlashMode:AVCaptureFlashModeOn];
[self setFlashModeButtonStatus]; } - (IBAction)autoFlash:(UIButton *)sender {
[self setFlashMode:AVCaptureFlashModeAuto];
[self setFlashModeButtonStatus];
}
#pragma mark 拍照
- (IBAction)takeButtonClick:(UIButton *)sender {
//根据设备输出获得连接
AVCaptureConnection *captureConnection=[self.captureStillImageOutput connectionWithMediaType:AVMediaTypeVideo];
//根据连接取得设备输出的数据
[self.captureStillImageOutput captureStillImageAsynchronouslyFromConnection:captureConnection completionHandler:^(CMSampleBufferRef imageDataSampleBuffer, NSError *error) {
if (imageDataSampleBuffer) {
NSData *imageData=[AVCaptureStillImageOutput jpegStillImageNSDataRepresentation:imageDataSampleBuffer];
UIImage *image=[UIImage imageWithData:imageData]; UIAlertController *alert=[UIAlertController alertControllerWithTitle:@"拍照成功" message:@"是否要保存到照片库" preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction *saveAction=[UIAlertAction actionWithTitle:@"保存" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) { UIImageWriteToSavedPhotosAlbum(image, nil, nil, nil); [self dismissViewControllerAnimated:YES completion:^{ }]; }];
UIAlertAction *cancelAction=[UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) { [self dismissViewControllerAnimated:YES completion:^{ }]; }];
[alert addAction:cancelAction];
[alert addAction:saveAction];
[self presentViewController:alert animated:YES completion:nil]; // ALAssetsLibrary *assetsLibrary=[[ALAssetsLibrary alloc]init];
// [assetsLibrary writeImageToSavedPhotosAlbum:[image CGImage] orientation:(ALAssetOrientation)[image imageOrientation] completionBlock:nil];
} }];
} - (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
} @end

录像:

//
// RecordVideoVC.m
// AVFoundation自定义拍照录像
//
// Created by JianF.Sun on 17/3/21.
// Copyright © 2017年 sjf. All rights reserved.
// #import "RecordVideoVC.h"
#import <AVFoundation/AVFoundation.h> #import <AssetsLibrary/AssetsLibrary.h> #define Screen_width [UIScreen mainScreen].bounds.size.width
#define Screen_height [UIScreen mainScreen].bounds.size.height
typedef void (^PropertyChangeBlock)(AVCaptureDevice *captureDevice); @interface RecordVideoVC () @property (strong,nonatomic) AVCaptureSession *captureSession;//负责输入和输出设备之间的数据传递
@property (strong,nonatomic) AVCaptureDeviceInput *captureDeviceInput;//负责从AVCaptureDevice获得输入数据
@property (strong,nonatomic) AVCaptureMovieFileOutput *captureMovieFileOutput;//视频输出流
@property (strong,nonatomic) AVCaptureVideoPreviewLayer *captureVideoPreviewLayer;//相机拍摄预览图层
@property (assign,nonatomic) BOOL enableRotation;//是否允许旋转(注意在视频录制过程中禁止屏幕旋转)
@property (assign,nonatomic) CGRect *lastBounds;//旋转的前大小
@property (assign,nonatomic) UIBackgroundTaskIdentifier backgroundTaskIdentifier;//后台任务标识
@property (weak, nonatomic) IBOutlet UIView *viewContainer;
@property (weak, nonatomic) IBOutlet UIButton *takeButton;//拍照按钮
@property (weak, nonatomic) IBOutlet UIImageView *focusCursor; //聚焦光标 @end @implementation RecordVideoVC #pragma mark - 控制器视图方法
- (void)viewDidLoad {
[super viewDidLoad];
} -(void)viewWillAppear:(BOOL)animated{
[super viewWillAppear:animated];
//初始化会话
_captureSession=[[AVCaptureSession alloc]init];
if ([_captureSession canSetSessionPreset:AVCaptureSessionPreset1280x720]) {//设置分辨率
_captureSession.sessionPreset=AVCaptureSessionPreset1280x720;
}
//获得输入设备
AVCaptureDevice *captureDevice=[self getCameraDeviceWithPosition:AVCaptureDevicePositionBack];//取得后置摄像头
if (!captureDevice) {
NSLog(@"取得后置摄像头时出现问题.");
return;
}
//添加一个音频输入设备
AVCaptureDevice *audioCaptureDevice=[[AVCaptureDevice devicesWithMediaType:AVMediaTypeAudio] firstObject]; NSError *error=nil;
//根据输入设备初始化设备输入对象,用于获得输入数据
_captureDeviceInput=[[AVCaptureDeviceInput alloc]initWithDevice:captureDevice error:&error];
if (error) {
NSLog(@"取得设备输入对象时出错,错误原因:%@",error.localizedDescription);
return;
}
AVCaptureDeviceInput *audioCaptureDeviceInput=[[AVCaptureDeviceInput alloc]initWithDevice:audioCaptureDevice error:&error];
if (error) {
NSLog(@"取得设备输入对象时出错,错误原因:%@",error.localizedDescription);
return;
} //初始化设备输出对象,用于获得输出数据
_captureMovieFileOutput=[[AVCaptureMovieFileOutput alloc]init]; //将设备输入添加到会话中
if ([_captureSession canAddInput:_captureDeviceInput]) {
[_captureSession addInput:_captureDeviceInput];
[_captureSession addInput:audioCaptureDeviceInput];
AVCaptureConnection *captureConnection=[_captureMovieFileOutput connectionWithMediaType:AVMediaTypeVideo];
if ([captureConnection isVideoStabilizationSupported ]) {
captureConnection.preferredVideoStabilizationMode=AVCaptureVideoStabilizationModeAuto;
}
} //将设备输出添加到会话中
if ([_captureSession canAddOutput:_captureMovieFileOutput]) {
[_captureSession addOutput:_captureMovieFileOutput];
} //创建视频预览层,用于实时展示摄像头状态
_captureVideoPreviewLayer=[[AVCaptureVideoPreviewLayer alloc]initWithSession:self.captureSession]; CALayer *layer=self.viewContainer.layer;
layer.masksToBounds=YES; _captureVideoPreviewLayer.frame=CGRectMake(, ,Screen_width, Screen_height);
_captureVideoPreviewLayer.position=CGPointMake(Screen_width/, Screen_height/);
_captureVideoPreviewLayer.videoGravity=AVLayerVideoGravityResizeAspect;//
//将视频预览层添加到界面中
//[layer addSublayer:_captureVideoPreviewLayer];
[layer insertSublayer:_captureVideoPreviewLayer below:self.focusCursor.layer]; _enableRotation=YES; [self addNotificationToCaptureDevice:captureDevice];
[self addGenstureRecognizer];
} -(void)viewDidAppear:(BOOL)animated{
[super viewDidAppear:animated];
[self.captureSession startRunning];
} -(void)viewDidDisappear:(BOOL)animated{
[super viewDidDisappear:animated];
[self.captureSession stopRunning];
} -(BOOL)shouldAutorotate{
return self.enableRotation;
} ////屏幕旋转时调整视频预览图层的方向
//-(void)willTransitionToTraitCollection:(UITraitCollection *)newCollection withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator{
// [super willTransitionToTraitCollection:newCollection withTransitionCoordinator:coordinator];
//// NSLog(@"%i,%i",newCollection.verticalSizeClass,newCollection.horizontalSizeClass);
// UIInterfaceOrientation orientation = [[UIApplication sharedApplication] statusBarOrientation];
// NSLog(@"%i",orientation);
// AVCaptureConnection *captureConnection=[self.captureVideoPreviewLayer connection];
// captureConnection.videoOrientation=orientation;
//
//}
//屏幕旋转时调整视频预览图层的方向
-(void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration{
AVCaptureConnection *captureConnection=[self.captureVideoPreviewLayer connection];
captureConnection.videoOrientation=(AVCaptureVideoOrientation)toInterfaceOrientation;
}
//旋转后重新设置大小
-(void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation{
_captureVideoPreviewLayer.frame=self.viewContainer.bounds;
} -(void)dealloc{
[self removeNotification];
}
#pragma mark - UI方法
#pragma mark 视频录制
- (IBAction)takeButtonClick:(UIButton *)sender {
//根据设备输出获得连接
AVCaptureConnection *captureConnection=[self.captureMovieFileOutput connectionWithMediaType:AVMediaTypeVideo];
//根据连接取得设备输出的数据
if (![self.captureMovieFileOutput isRecording]) {
[self.takeButton setTitle:@"正在录制..." forState:UIControlStateNormal];
self.enableRotation=NO;
//如果支持多任务则则开始多任务
if ([[UIDevice currentDevice] isMultitaskingSupported]) {
self.backgroundTaskIdentifier=[[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:nil];
}
//预览图层和视频方向保持一致
captureConnection.videoOrientation=[self.captureVideoPreviewLayer connection].videoOrientation;
NSString *outputFielPath=[NSTemporaryDirectory() stringByAppendingString:@"myMovie.mov"];
NSLog(@"save path is :%@",outputFielPath);
NSURL *fileUrl=[NSURL fileURLWithPath:outputFielPath];
[self.captureMovieFileOutput startRecordingToOutputFileURL:fileUrl recordingDelegate:self];
}
else{
[self.takeButton setTitle:@"开始录制" forState:UIControlStateNormal];
[self.captureMovieFileOutput stopRecording];//停止录制
}
}
#pragma mark 切换前后摄像头
- (IBAction)toggleButtonClick:(UIButton *)sender {
AVCaptureDevice *currentDevice=[self.captureDeviceInput device];
AVCaptureDevicePosition currentPosition=[currentDevice position];
[self removeNotificationFromCaptureDevice:currentDevice];
AVCaptureDevice *toChangeDevice;
AVCaptureDevicePosition toChangePosition=AVCaptureDevicePositionFront;
if (currentPosition==AVCaptureDevicePositionUnspecified||currentPosition==AVCaptureDevicePositionFront) {
toChangePosition=AVCaptureDevicePositionBack;
}
toChangeDevice=[self getCameraDeviceWithPosition:toChangePosition];
[self addNotificationToCaptureDevice:toChangeDevice];
//获得要调整的设备输入对象
AVCaptureDeviceInput *toChangeDeviceInput=[[AVCaptureDeviceInput alloc]initWithDevice:toChangeDevice error:nil]; //改变会话的配置前一定要先开启配置,配置完成后提交配置改变
[self.captureSession beginConfiguration];
//移除原有输入对象
[self.captureSession removeInput:self.captureDeviceInput];
//添加新的输入对象
if ([self.captureSession canAddInput:toChangeDeviceInput]) {
[self.captureSession addInput:toChangeDeviceInput];
self.captureDeviceInput=toChangeDeviceInput;
}
//提交会话配置
[self.captureSession commitConfiguration]; } #pragma mark - 视频输出代理
-(void)captureOutput:(AVCaptureFileOutput *)captureOutput didStartRecordingToOutputFileAtURL:(NSURL *)fileURL fromConnections:(NSArray *)connections{
NSLog(@"开始录制...");
}
-(void)captureOutput:(AVCaptureFileOutput *)captureOutput didFinishRecordingToOutputFileAtURL:(NSURL *)outputFileURL fromConnections:(NSArray *)connections error:(NSError *)error{
NSLog(@"视频录制完成.");
//视频录入完成之后在后台将视频存储到相簿
self.enableRotation=YES;
UIBackgroundTaskIdentifier lastBackgroundTaskIdentifier=self.backgroundTaskIdentifier;
self.backgroundTaskIdentifier=UIBackgroundTaskInvalid;
ALAssetsLibrary *assetsLibrary=[[ALAssetsLibrary alloc]init]; UIAlertController *alert=[UIAlertController alertControllerWithTitle:@"视频录制完成" message:@"是否要保存到照片库" preferredStyle:UIAlertControllerStyleAlert]; UIAlertAction *saveAction=[UIAlertAction actionWithTitle:@"保存" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) { [assetsLibrary writeVideoAtPathToSavedPhotosAlbum:outputFileURL completionBlock:^(NSURL *assetURL, NSError *error) {
if (error) {
NSLog(@"保存视频到相簿过程中发生错误,错误信息:%@",error.localizedDescription);
}
if (lastBackgroundTaskIdentifier!=UIBackgroundTaskInvalid) {
[[UIApplication sharedApplication] endBackgroundTask:lastBackgroundTaskIdentifier];
}
NSLog(@"成功保存视频到相簿.");
}]; [self dismissViewControllerAnimated:YES completion:^{ }];
}]; UIAlertAction *cancelAction=[UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) {
[self dismissViewControllerAnimated:YES completion:^{ }];
}];
[alert addAction:cancelAction];
[alert addAction:saveAction];
[self presentViewController:alert animated:YES completion:nil]; } #pragma mark - 通知
/**
* 给输入设备添加通知
*/
-(void)addNotificationToCaptureDevice:(AVCaptureDevice *)captureDevice{
//注意添加区域改变捕获通知必须首先设置设备允许捕获
[self changeDeviceProperty:^(AVCaptureDevice *captureDevice) {
captureDevice.subjectAreaChangeMonitoringEnabled=YES;
}];
NSNotificationCenter *notificationCenter= [NSNotificationCenter defaultCenter];
//捕获区域发生改变
[notificationCenter addObserver:self selector:@selector(areaChange:) name:AVCaptureDeviceSubjectAreaDidChangeNotification object:captureDevice];
}
-(void)removeNotificationFromCaptureDevice:(AVCaptureDevice *)captureDevice{
NSNotificationCenter *notificationCenter= [NSNotificationCenter defaultCenter];
[notificationCenter removeObserver:self name:AVCaptureDeviceSubjectAreaDidChangeNotification object:captureDevice];
}
/**
* 移除所有通知
*/
-(void)removeNotification{
NSNotificationCenter *notificationCenter= [NSNotificationCenter defaultCenter];
[notificationCenter removeObserver:self];
} -(void)addNotificationToCaptureSession:(AVCaptureSession *)captureSession{
NSNotificationCenter *notificationCenter= [NSNotificationCenter defaultCenter];
//会话出错
[notificationCenter addObserver:self selector:@selector(sessionRuntimeError:) name:AVCaptureSessionRuntimeErrorNotification object:captureSession];
} /**
* 设备连接成功
*
* @param notification 通知对象
*/
-(void)deviceConnected:(NSNotification *)notification{
NSLog(@"设备已连接...");
}
/**
* 设备连接断开
*
* @param notification 通知对象
*/
-(void)deviceDisconnected:(NSNotification *)notification{
NSLog(@"设备已断开.");
}
/**
* 捕获区域改变
*
* @param notification 通知对象
*/
-(void)areaChange:(NSNotification *)notification{
NSLog(@"捕获区域改变...");
} /**
* 会话出错
*
* @param notification 通知对象
*/
-(void)sessionRuntimeError:(NSNotification *)notification{
NSLog(@"会话发生错误.");
} #pragma mark - 私有方法 /**
* 取得指定位置的摄像头
*
* @param position 摄像头位置
*
* @return 摄像头设备
*/
-(AVCaptureDevice *)getCameraDeviceWithPosition:(AVCaptureDevicePosition )position{
NSArray *cameras= [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo];
for (AVCaptureDevice *camera in cameras) {
if ([camera position]==position) {
return camera;
}
}
return nil;
} /**
* 改变设备属性的统一操作方法
*
* @param propertyChange 属性改变操作
*/
-(void)changeDeviceProperty:(PropertyChangeBlock)propertyChange{
AVCaptureDevice *captureDevice= [self.captureDeviceInput device];
NSError *error;
//注意改变设备属性前一定要首先调用lockForConfiguration:调用完之后使用unlockForConfiguration方法解锁
if ([captureDevice lockForConfiguration:&error]) {
propertyChange(captureDevice);
[captureDevice unlockForConfiguration];
}else{
NSLog(@"设置设备属性过程发生错误,错误信息:%@",error.localizedDescription);
}
} /**
* 设置闪光灯模式
*
* @param flashMode 闪光灯模式
*/
-(void)setFlashMode:(AVCaptureFlashMode )flashMode{
[self changeDeviceProperty:^(AVCaptureDevice *captureDevice) {
if ([captureDevice isFlashModeSupported:flashMode]) {
[captureDevice setFlashMode:flashMode];
}
}];
}
/**
* 设置聚焦模式
*
* @param focusMode 聚焦模式
*/
-(void)setFocusMode:(AVCaptureFocusMode )focusMode{
[self changeDeviceProperty:^(AVCaptureDevice *captureDevice) {
if ([captureDevice isFocusModeSupported:focusMode]) {
[captureDevice setFocusMode:focusMode];
}
}];
}
/**
* 设置曝光模式
*
* @param exposureMode 曝光模式
*/
-(void)setExposureMode:(AVCaptureExposureMode)exposureMode{
[self changeDeviceProperty:^(AVCaptureDevice *captureDevice) {
if ([captureDevice isExposureModeSupported:exposureMode]) {
[captureDevice setExposureMode:exposureMode];
}
}];
}
/**
* 设置聚焦点
*
* @param point 聚焦点
*/
-(void)focusWithMode:(AVCaptureFocusMode)focusMode exposureMode:(AVCaptureExposureMode)exposureMode atPoint:(CGPoint)point{
[self changeDeviceProperty:^(AVCaptureDevice *captureDevice) {
if ([captureDevice isFocusModeSupported:focusMode]) {
[captureDevice setFocusMode:AVCaptureFocusModeAutoFocus];
}
if ([captureDevice isFocusPointOfInterestSupported]) {
[captureDevice setFocusPointOfInterest:point];
}
if ([captureDevice isExposureModeSupported:exposureMode]) {
[captureDevice setExposureMode:AVCaptureExposureModeAutoExpose];
}
if ([captureDevice isExposurePointOfInterestSupported]) {
[captureDevice setExposurePointOfInterest:point];
}
}];
} /**
* 添加点按手势,点按时聚焦
*/
-(void)addGenstureRecognizer{
UITapGestureRecognizer *tapGesture=[[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(tapScreen:)];
[self.viewContainer addGestureRecognizer:tapGesture];
}
-(void)tapScreen:(UITapGestureRecognizer *)tapGesture{
CGPoint point= [tapGesture locationInView:self.viewContainer];
//将UI坐标转化为摄像头坐标
CGPoint cameraPoint= [self.captureVideoPreviewLayer captureDevicePointOfInterestForPoint:point];
[self setFocusCursorWithPoint:point];
[self focusWithMode:AVCaptureFocusModeAutoFocus exposureMode:AVCaptureExposureModeAutoExpose atPoint:cameraPoint];
} /**
* 设置聚焦光标位置
*
* @param point 光标位置
*/
-(void)setFocusCursorWithPoint:(CGPoint)point{
self.focusCursor.center=point;
self.focusCursor.transform=CGAffineTransformMakeScale(1.5, 1.5);
self.focusCursor.alpha=1.0;
[UIView animateWithDuration:1.0 animations:^{
self.focusCursor.transform=CGAffineTransformIdentity;
} completion:^(BOOL finished) {
self.focusCursor.alpha=; }];
}
@end

参考文档:

http://www.jianshu.com/p/b518779c4790,

http://www.cnblogs.com/kenshincui/p/4186022.html#camera,

http://www.jianshu.com/p/8c7ca1dd7f02

最新文章

  1. Swift小练习-引导页
  2. 导出特定内容成insert语句
  3. Window I/O 完成端口 (Windows I/O Completion Port (IOCP))
  4. 54. 八皇后问题[eight queens puzzle]
  5. 6.1.2Linux下Socket编程
  6. Unity3D Script Execution Order ——Question
  7. 优化SQL
  8. Json之语法,格式
  9. Motion control encoder extrapolation
  10. poj-1131-(大数)八进制转化成十进制
  11. cshtml中正则表达式使用后台代码
  12. python之数据类型补充、集合、深浅copy
  13. Spring IOC容器对bean的生命周期进行管理的过程
  14. 抓包工具Charles的使用心得
  15. 深入理解java虚拟机---虚拟机工具jps与jstat(十四)
  16. Centos7解决中文乱码问题
  17. HDU 1950(LIS)
  18. JavaScript中的apply()和call()
  19. CF 248B 前缀和
  20. spring boot之访问静态页面

热门文章

  1. WinFrom下Webbrowser加载自定义页面的技巧
  2. Android代码编译出现的错误
  3. httprunner 使用总结
  4. Good Bye 2017(送命场)
  5. jvm内存结构(二)(栈的变化,机器指令的格式/执行模式)
  6. SpringBoot学习笔记(一)基础
  7. HTML 遍历
  8. 【NOIP2017】逛公园 最短路+DP
  9. 【NOIP2013】转圈游戏 快速幂
  10. 【BZOJ3217】ALOEXT 分块