• AVFoundation-视频录制以及拍照


    一般如果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(0, 0,Screen_width, Screen_height);
        _captureVideoPreviewLayer.position=CGPointMake(Screen_width/2, Screen_height/2);
        _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=0;
            
        }];
    }
    
    
    #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(0, 0,Screen_width, Screen_height);
        _captureVideoPreviewLayer.position=CGPointMake(Screen_width/2, Screen_height/2);
        _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=0;
            
        }];
    }
    @end

    参考文档:

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

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

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

  • 相关阅读:
    02.零成本实现WEB性能测试-基于APACHE JMETER
    Apache JMeter--网站自动测试与性能测评
    01.天幕红尘
    php-fpm介绍及配置
    Linux环境下搭建php开发环境的操作步骤
    Linux下PHP开发环境搭建
    nginx 502 Bad Gateway 错误问题收集
    Nginx配置文件详细说明
    Linux下查看MySQL的安装路径
    LINUX下YUM源配置
  • 原文地址:https://www.cnblogs.com/sunjianfei/p/6593055.html
Copyright © 2020-2023  润新知