首页 > 代码库 > AVFoundation实现相机和使用ALAssetsLibrary

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

 

AVFoundation实现相机和使用ALAssetsLibrary