上篇文章介绍了如何使用 UIImagePickerController 调用相机拍照或者录制视频,但是 UIImagePickerController 只提供了一种非常简单的拍照和录制视频的方法,并且只提供了很简单的UI定制,如果你想要更多关于处理相机拍摄照片或者视频的方法、或者需要完整的自定义相机界面等,那就需要使用 AVFoundation 框架
AVFoundation 相关类
AVFoundation 框架基于以下几个类实现图像捕捉,通过这些类可以访问来自相机设备的原始数据
- AVCaptureDevice 抽象化的硬件(如前置摄像头,后置摄像头等)对象,它用于控制硬件的相关特性(闪光灯,手电筒,聚焦模式等)
- AVCaptureDeviceInput 硬件设备输入流数据的管理对象
- AVCaptureOutput 一个抽象类,是输出流数据的管理对象,通常使用它的具体子类:
- AVCaptureStillImageOutput 静态图片
- AVCaptureAudioDataOutput 音频数据
- AVCaptureVideoDataOutput 视频数据,实时的为预览图提供原始帧
- AVCaptureMetadataOutput 元数据,可用于检测人脸识别、二维码等
- AVCaptureMovieFileOutput 完整的视频数据地址
- AVCapturePhotoOutput 照片(包括静态的、Live Photo 等),iOS10 新增,用于代替 AVCaptureStillImageOutput
- AVCaptureSession 管理输入与输出之间的数据流的会话对象
- AVCaptureVideoPreviewLayer 是 CALayer 的子类,用于显示相机产生的实时图像
他们之间的关系如图:

AVFoundation 的使用
使用 AVFoundation 自定义相机需要进行如下几个步骤:
- 创建捕捉会话(
captureSession) - 选择并添加输入(相机、麦克风等)
- 创建并设置输出
- 通过添加输入、输出配置捕捉会话
- 创建和添加预览视图
- 启动捕捉会话
下面看具体步骤介绍
AVCaptureSession
AVCaptureSession 负责调度影音输入与输出之间的数据流
|
|
在创建 AVCaptureSession 的时候,我们一般会设定一个 sessionPreset 预设,设置于是可以让用户获取不同规格的数据,一共有如下12种不同的预设:
|
|
AVCaptureSessionPresetPhoto 用于获取静态照片和 LivePhoto,它会为我们选择最合适的照片配置,如果需要设置更多细节(静态图片的分辨率、感光度、曝光时间灯)可以从 AVCaptureDevice.formats 获取到设备设置支持的参数,并且可以自定义参数赋值给 AVCaptureDevice.activeFormat 属性,接下来的10个预设和上一篇博客中 UIImagePickerController 设置的 videoQuality 的 UIImagePickerControllerQualityType 类似,都是设置视频相关的预设,最后一个 AVCaptureSessionPresetInputPriority 表示 captureSession 不去控制音频与视频输出设置,而是通过已连接的捕获设备的 activeFormat 反过来控制 captureSession 的输出质量等级
注意:所有对
AVCaptureSession的调用都是线程阻塞的,所以我们最好创建一条线程异步调用
AVCaptureDevice 和 AVCaptureDeviceInput
AVCaptureDevice 表示抽象化的 iPhone 输入硬件,包括相机、麦克风等,AVCaptureDeviceInput 是通过 AVCaptureDevice 创建的输入源对象,并且需要添加到 captureSession 中
|
|
上面定义的方法是通过传入一个设备的 AVCaptureDevicePosition 以及 mediaType 获取前置或者后置相机的 AVCaptureDevice 对象,如果 mediaType 为 AVMediaTypeAudio 可以获取到麦克风的 AVCaptureDevice 对象(iPhone 内部前后有多个麦克风),iOS 10 之后苹果提供了一个专门的 AVCaptureDeviceDiscoverySession 类来获取 AVCaptureDevice 设备,iPhone 7 的双摄像头设备必须使用这个类才能获取到
接下来是是把设备添加到 AVCaptureDeviceInput 对象中去,再把输入对象添加到 captureSession 中
|
|
注意当 App 第一次运行时,第一次调用 AVCaptureDeviceInput.init(device: AVCaptureDevice!) 会触发系统提示向用户请求允许访问设备,所以你的 App需要访问相机、麦克风或者相册等的时候,所以记得在info.plist添加相应配置权限
如果希望体验更好,我们可以先确认设备当前的授权状态,要是在授权还没有确定的情况下 (也就是说用户还没有看过弹出的授权对话框时),我们应该明确地发起授权请求,如果用户拒绝过授权请求也可以提示用户
|
|
AVCaptureOutput
输出对象我们一般使用 AVCaptureOutput 的子类:AVCaptureVideoDataOutput, AVCaptureAudioDataOutput,AVCaptureMovieFileOutput, AVCaptureStillImageOutput(AVCapturePhotoOutput), AVCaptureMetadataOutput, 他们都有相对应的代理方法,也就是说数据输出都是通过代理来获取的
|
|
iOS10 之前输出照片这里使用 AVCaptureStillImageOutput ,iOS 10 之后苹果推出一个替代它的类:AVCapturePhotoOutput,它不仅支持简单的静态图片拍摄,还支持LivePhoto照片和RAW格式的照片拍摄,AVCapturePhotoOutput 还可以通过代理协议通知拍照进度(照片即将被捕获,照片已被捕获但尚未处理,LivePhoto 已准备就绪等),当输出 LivePhoto 的时候不能和 AVCaptureMovieFileOutput 一起使用,如果把 AVCaptureMovieFileOutput 和 AVCapturePhotoOutput 添加到会话中,AVCapturePhotoOutput 的livePhotoCaptureSupported 属性将返回 NO,AVCapturePhotoOutput 支持和 AVCaptureVideoDataOutput 一起添加到会话中使用,这样就可以同时支持 LivePhoto 和 视频拍摄
本文的 Demo 代码 iOS 10 之前相机的照片输出和视频输出使用 AVCaptureStillImageOutput 和AVCaptureMovieFileOutput,iOS 10 以后,相机的照片输出和视频输出使用 AVCapturePhotoOutput 和 AVCaptureVideoDataOutput
AVCaptureVideoPreviewLayer
AVCaptureVideoPreviewLayer 是 CALayer 的一个子类,用来做为AVCaptureSession 预览视频输出,简单来说就是来把相机捕获的画面实时呈现出来的一个layer
|
|
当上面的 captureSession 全部配置完成之后就可以调用 captureSession 的 startRunning() 方法启动会话了,另外记住退出相机的时候调用stopRunning() 关闭会话
上面配置的 captureSession 定义的相机是只能拍摄照片,如果需要拍摄视频还需要往输入里添加麦克风设备,并且使用 AVCaptureMovieFileOutput 或者 AVCaptureVideoDataOutput 输出视频,为了让代码更加清晰 Demo 代码里把拍照和录制视频放在了两个控制器里面,实际开发中自定义相机可能既可以拍照又能录制视频
|
|
注意:配置或者更新(添加或删除输出,更改sessionPreset,或配置各个AVCaptureInput或Output属性)
captureSession之前需要先调用beginConfiguration(),完成之后再调用commitConfiguration()
操作相机
上面的工作完成之后就可以对自己定义的相机进行操作了,下面看一下如何在自定义相机下进行拍照、录制视频、保存等操作,首先是拍照,先看代码
|
|
如果使用 AVCaptureStillImageOutput 输出照片可以直接使用 captureStillImageAsynchronously 方法在闭包中异步获取到当前照片,AVCapturePhotoOutput 则需要实现 AVCapturePhotoCaptureDelegate的协议方法,在协议方法里面获取拍摄的照片数据,LivePhoto(仅支持iPhone6s及以上)本质就是一张照片和拍摄这张照片前后1.5秒的视频组成的“照片”,所以要在相册里面保存一张 LivePhoto,必须包含两部分:LivePhoto 的图片Data数据和 LivePhoto 视频临时地址,使用PHPhotoLibrary 把这个两部分保存到相册就生成了一张 LivePhoto
注意:实际上只有
AVCaptureSession的预设只有AVCaptureSessionPresetPhoto才支持 LivePhoto,其他视频相关的预设都不支持
使用 AVCaptureDevice 设置硬件属性(FocusMode、TorchMode、FlashMode、ExposureMode等)需要先锁定设备,设置完成之后再解锁
设置闪关灯
|
|
设置手电筒
|
|
另外需要注意 TorchMode 和 FlashMode 的区别:前者表示手电筒模式,它的使用效果就和 iPhone 控制中心里手电筒开关效果一致,并且可以对手电筒的亮度进行设置(iOS 11 已经可以在控制中心之间调节亮度了),后者表示摄像头的闪光灯模式,用于拍照时候补光,这两者的使用之前都需要判断当前设备是否支持,这篇博客)可以参考更多的操作相机的功能
注意:使用
AVCapturePhotoOutput之后,必须通过AVCapturePhotoSettings去设置相机的闪光灯模式,之前使用AVCaptureDevice的属性设置闪关灯模式会被忽略
接下来看如何用自定义的相机拍摄视频
|
|
使用 AVCaptureMovieFileOutput 输出视频需要AVCaptureFileOutputRecordingDelegate 的相关协议方法
|
|
AVCaptureMovieFileOutput 只能用来写入 QuickTime 视频类型(.mov)的媒体文件,而且也不能实现暂停录制、不能定义视频文件的类型,不过AVCaptureMovieFileOutput 还有一些其他的配置选项,比如在某段时间后,在达到某个指定的文件尺寸时,或者当设备的最小磁盘剩余空间达到某个阈值时停止录制等
很多时候我们都是使用灵活性更强的 AVCaptureVideoDataOutput 和AVCaptureAudioDataOutput 来实现视频的录制,使用 AVCaptureVideoDataOutput 输出视频需要实现AVCaptureVideoDataOutputSampleBufferDelegate 的相关协议方法,并且在协议方法里面使用 AVAssetWriter 合成视频
|
|
从上面的流程可以看出 AVCaptureMovieFileOutput 与 AVCaptureVideoDataOutput 和 AVCaptureAudioDataOutput 的区别,前者只需要一个输出即可,指定一个文件路径后,视频和音频会写入到指定路径,不需要其他复杂的操作;后者则需要使用 AVAssetWriter 把 AVCaptureVideoDataOutput 和 AVCaptureAudioDataOutput 的输出数据通过 AVAssetWriterInput 写入视频文件,详细请参考 Demo 代码
上面就是 AVFoundation 自定义相机的基本使用,当然 AVFoundation的作用不仅仅是自定义相机,AVFoundation 是一个多媒体框架,可以使用它来播放和创建基于时间的音视频资源,AVFoundation 提供的接口可以精确地处理基于时间的音视频媒体数据,比如媒体文件的查找、创建、编辑甚至二次编码操作都可以使用 AVFoundation框架完成,一本书都可能讲解不完里面的全部知识,本人才疏学浅,也只是略知皮毛而已,还学习多多的学习
以上です
参考
Photo Capture Programming Guide
本人刚开始写博客,主要是为了给自己的知识点做一个笔记,方便自己以后查阅,如果能让别人有所启发也是荣幸之至!如有错误,欢迎指正!