上篇文章介绍了如何使用 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
本人刚开始写博客,主要是为了给自己的知识点做一个笔记,方便自己以后查阅,如果能让别人有所启发也是荣幸之至!如有错误,欢迎指正!