• AVFoundation实现相机和使用ALAssetsLibrary


    //

    //  ViewController.m

    //  AVFoundtion

    //

    //  Created by King on 16/8/26.

    //  Copyright © 2016年 King. All rights reserved.

    //

    #import "ViewController.h"

    #import <AVFoundation/AVFoundation.h>

    #import <AssetsLibrary/AssetsLibrary.h>

    typedef void(^PropertyChangeBlock) (AVCaptureDevice *captureDevice);

    @interface ViewController ()<AVCaptureFileOutputRecordingDelegate>

    /** 负责输入和输出设备之间的数据传递 */

    @property (nonatomic,strong) AVCaptureSession *captureSession;

    /** 负责从AVCaptureDevice获得输入数据 */

    @property (nonatomic,strong) AVCaptureDeviceInput *captureDeviceInput;

    /** 视频输出流 */

    @property (nonatomic,strong) AVCaptureMovieFileOutput *captureMovieFileOutput;

    /** 相机拍摄预览图层 */

    @property (nonatomic,strong) AVCaptureVideoPreviewLayer *captureVideoPreviewLayer;

    /** 是否允许屏幕旋转(在录制屏幕中禁止旋转) */

    @property (nonatomic,assign) BOOL enableRotation;

    /** 旋转前大小 */

    @property (nonatomic,assign) CGRect *lastBounds;

    /** 后台任务标识 */

    @property (nonatomic,assign) UIBackgroundTaskIdentifier backgroundTaskIdentifier;

    @property (nonatomic , strong) UIView *viewContainer;

    /** 自动闪光按钮 */

    @property (nonatomic , strong) UIButton *flashAutoButton;

    /** 打开闪光按钮 */

    @property (nonatomic , strong) UIButton *flashOnButton;

    /** 关闭闪光按钮 */

    @property (nonatomic , strong) UIButton *flashOffButton;

    /** 聚焦框 */

    @property (nonatomic , strong) UIImageView *focusCursor;

    /** 摄影按钮 */

    @property (nonatomic , strong) UIButton *takeButton1;

    @end

    @implementation ViewController

    #pragma mark - 视图控制器方法

    - (void)viewDidLoad {

        [super viewDidLoad];

        

        self.viewContainer = [[UIView alloc] initWithFrame:CGRectMake(0, 24, self.view.frame.size.width, self.view.frame.size.height - 24 - 100)];

        

        [self.view addSubview:self.viewContainer];

        self.flashAutoButton = [UIButton buttonWithType:UIButtonTypeCustom];

        

        [self.flashAutoButton setFrame:CGRectMake(10, self.viewContainer.frame.size.height + 50, 80, 40)];

        

        [self.flashAutoButton setTitle:@"自动闪光" forState:UIControlStateNormal];

        

        [self.flashAutoButton setBackgroundColor:[UIColor blackColor]];

        

        [self.flashAutoButton addTarget:self action:@selector(flashAutoClick1:) forControlEvents:UIControlEventTouchUpInside];

        

        [self.view addSubview:self.flashAutoButton];

        

        self.takeButton1 = [UIButton buttonWithType:UIButtonTypeCustom];

        

        [self.takeButton1 setFrame:CGRectMake(100, self.viewContainer.frame.size.height + 50, 80, 40)];

        

        [self.takeButton1 setTitle:@"视频录制" forState:UIControlStateNormal];

        

        [self.takeButton1 setTitle:@"结束录制" forState:UIControlStateSelected];

        

        [self.takeButton1 setBackgroundColor:[UIColor redColor]];

        

        [self.takeButton1 addTarget:self action:@selector(takeButtonClick11:) forControlEvents:UIControlEventTouchUpInside];

        

        [self.view addSubview:self.takeButton1];

        

    }

    - (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 = layer.bounds;

        _captureVideoPreviewLayer.videoGravity = AVLayerVideoGravityResize; // 填充模式显示在layer上

        // 将视频预览层添加到界面中

        [layer insertSublayer:_captureVideoPreviewLayer below:self.focusCursor.layer];

        

        _enableRotation=NO;

        [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];

    }

    - (BOOL)shouldAutorotate

    {

        return self.enableRotation;

    }

    // 旋转屏幕时调整视频预览图层的方向

    - (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 通知

    /**

     * 给输入设备添加通知

     *

     */

    - (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];

        NSLog(@"(%f,%f)",point.x,point.y);

        //将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.transform=CGAffineTransformMakeScale(1.5, 1.5);

        self.focusCursor.alpha=1.0;

        

        [self.focusCursor setBackgroundColor:[UIColor redColor]];

        [UIView animateWithDuration:1.0 animations:^{

            self.focusCursor.transform=CGAffineTransformIdentity;

        } completion:^(BOOL finished) {

            self.focusCursor.alpha=0.5;

        }];

    }

    /**

     *  设置闪光灯按钮状态

     */

    -(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;

        }

    }

    #pragma mark -按钮处理

    #pragma mark 切换前后摄像头

    - (IBAction)toggleButtonClick1:(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];

    }

    #pragma mark 录制视频

    - (void)takeButtonClick11:(UIButton *)sender {

        

        sender.selected = !sender.selected;

        

        if (sender.selected) {

        //根据设备输出获得连接

        AVCaptureConnection *captureConnection=[self.captureMovieFileOutput connectionWithMediaType:AVMediaTypeVideo];

        //根据连接取得设备输出的数据

        if (![self.captureMovieFileOutput isRecording]) {  // 如果此时没有在录屏

            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];

            NSLog(@"fileUrl:%@",fileUrl);

            [self.captureMovieFileOutput startRecordingToOutputFileURL:fileUrl recordingDelegate:self];

        }

        else{

            [self.captureMovieFileOutput stopRecording];//停止录制

        }

        }else {

            [self.captureMovieFileOutput stopRecording];//停止录制

        }

    }

    #pragma mark 自动闪光灯开启

    - (void)flashAutoClick1:(UIButton *)sender {

        [self setFlashMode:AVCaptureFlashModeAuto];

        [self setFlashModeButtonStatus];

    }

    #pragma mark 打开闪光灯

    - (void)flashOnClick1:(UIButton *)sender {

        [self setFlashMode:AVCaptureFlashModeOn];

        [self setFlashModeButtonStatus];

    }

    #pragma mark 关闭闪光灯

    - (void)flashOffClick1:(UIButton *)sender {

        [self setFlashMode:AVCaptureFlashModeOff];

        [self setFlashModeButtonStatus];

    }

    #pragma mark -AVCaptureFileOutputRecordingDelegate  视频输出代理中的方法

    // optional

    // 当数据开始写入文件的时候调用,如果数据写入错误,则该方法不会被调用

    - (void)captureOutput:(AVCaptureFileOutput *)captureOutput didStartRecordingToOutputFileAtURL:(NSURL *)fileURL fromConnections:(NSArray *)connections

    {

        NSLog(@"开始录制");

    }

    // required

    // 当数据写入完成时调用该方法

    - (void)captureOutput:(AVCaptureFileOutput *)captureOutput didFinishRecordingToOutputFileAtURL:(NSURL *)outputFileURL fromConnections:(NSArray *)connections error:(NSError *)error

    {

        NSLog(@"视频录制完成");

        //视频录制完成之后在后台将视频存储到相簿

        self.enableRotation = NO;

        UIBackgroundTaskIdentifier lastBackgroundTaskIdentifier = self.backgroundTaskIdentifier;

        self.backgroundTaskIdentifier = UIBackgroundTaskInvalid;

        ALAssetsLibrary *assetsLibrary = [[ALAssetsLibrary alloc] init];

        [assetsLibrary writeVideoAtPathToSavedPhotosAlbum:outputFileURL completionBlock:^(NSURL *assetURL, NSError *error) {

            if (error) {

                NSLog(@"保存视频到相簿的过程发生错误,错误信息:%@",error.localizedDescription);

            }

            NSLog(@"outputURL:%@",outputFileURL);

            [[NSFileManager defaultManager] removeItemAtURL:outputFileURL error:nil];

            if (lastBackgroundTaskIdentifier != UIBackgroundTaskInvalid) {

                [[UIApplication sharedApplication] endBackgroundTask:lastBackgroundTaskIdentifier];

            }

            NSLog(@"成功保存视频到相簿");

        }];

    }

    - (void)didReceiveMemoryWarning {

        [super didReceiveMemoryWarning];

        // Dispose of any resources that can be recreated.

    }

    @end

  • 相关阅读:
    swing如何让控件在窗口打开时,自动获得焦点
    获取list集合中重复的元素
    IDEA安装流程(IDEA安装步骤)
    tmux切换页,鼠标滚动
    prefix
    python换行
    安装pytorch方法,直接上官网!
    解压tar.gz文件
    安装boost
    pytorch将其他类型转为tensor torch.as_tensor()、torch.from_numpy()
  • 原文地址:https://www.cnblogs.com/var-king/p/5811326.html
Copyright © 2020-2023  润新知