01 使用 AVFoundation 构建相机
01 前言
本文是 iOS/Android 音视频开发专题 第十篇,该专题中项目代码将在 Github 进行托管,你可在微信公众号( GeekDev )后台回复 资料 获取项目地址。
在上篇文章 AVFoundation 框架介绍 一文中,我们简单介绍了 AVFoundation 的整体架构。在本篇文章中,我们将从一个简单的相机实例入手,从零开发一个 AVCam 相机App。
该相机应用支持捕获照片和录制一段视频。根据设备情况还支持深度数据,哑光人像(Portrait effects matte) 和实时照片捕获(Live Photos)。
运行 AVCam, 需要在 iOS13 或 更高版本的 iOS 设备,由于 XCode 无法访问设备的摄像头,因此该示例无法在 Simulator 中使用。
本期内容:
-
AVCaptureSession 创建与配置
-
拍摄一张 Photo
-
拍摄一张 Live Photos
-
捕获图像深度及肖像数据
-
录制视频文件
-
结束语
02 AVCaptureSession 创建与配置
流程:
-
初始化 AVCaptureSession
-
配置 AVCamPreviewView 及 AVCaptureVideoPreviewLayer
-
配置 AVCatpureDevice, AVCatpureDeviceInput 及 AVCatpureDevice Input
-
配置相机相关权限
AVCaptureSession 是负责协调沟通 AVCatpureDevice 及 AVCatpueOutput 的中间对象。 AVCaptureSession 从摄像头和麦克风 AVCatpureDevice 设备中接收采集到的输入数据,并将数据发送至 AVCatpueOutput ,最终生成一张照片或者视频文件。
在 AVCam 示例 AVCamCameraViewController 中 viewDidLoad 方法中,我们首先创建了一个 AVCaptureSession 。
如果将采集到的画面显示屏幕上,我们需要可以通过两种方式。
一种为 AVCaptureVideoPreviewLayer 设置一个 AVCaptureSession ,iOS 系统自动会将采集到的画面输出到 View 中。
另外一种方式是通过为 AVCaptureSession 添加 AVCaptureVideoDataOutput , AVCaptureVideoDataOutput 可以将采集到数据 CMSampleBufferRef 输出给客户端,我们可以通过 OpenGL ES 将画面渲染到视图上。
AVCaptureVideoDataOutput 的控制粒度更精细,我们可以在渲染到屏幕之前,对 CMSampleBufferRef 进行处理。后边我们介绍 GPUImage 时会介绍该部分内容。
AVCaptureVideoPreviewLayer 是 CALayer 的子类,可用于显示 AVCaptureSession 采集到的视频画面。
为 AVCaptureVideoPreviewLayer 的 session 属性设置 AVCaptureSession 实例,就建立了两者的关系。
在 AVCam 项目中我们使用了另外一种创建 AVCaptureVideoPreviewLayer 的方式 ,就是为我们自定义的 AVCamPreviewView 视图指定 layerClass 。这种方式方便我们进行页面布局。
03 AVCaptureSession 添加视频采集设备
为 configureSession 方法中, [self.session begconfiguration] 用于将多个配置转为原子更新,当你为 AVCaptureSession 添加或删除 input , output 或配置其他参数时,并不会立即生效,直到客户端调用 [session commitConfiguration] 时才会提交到 AVCaptureSession 中。 如果 begconfiguration 和 commitConfiguration 存在嵌套,仅当最外层调用时才会被应用。 begconfiguration , commitConfiguration 必须成对出现。
通过 sessionPresent 可以设置相机采集分辨率,该配置可以在相机运行时动态设置。在 AVCam 实例中我们取值为 AVCapturePresetPhoto ,系统会分配一个最佳的分辨率。
sessionPresent 是一个枚举值,除了 AVCapturePresetPhoto 外,我们还可以指定以下选项:
-
AVCaptureSessionPresetLow 低质量.
-
AVCaptureSessionPresetMedium 中等 质量.
-
AVCaptureSessionPresetHigh 高质量
-
AVCaptureSessionPresetPhoto
-
AVCaptureSessionPresetInputPriority
-
AVCaptureSessionPreset960x540
-
AVCaptureSessionPreset1280x720
-
AVCaptureSessionPreset1920x1080
-
AVCaptureSessionPreset3840x2160
-
AVCaptureSessionPreset320*480
-
AVCaptureSessionPreset640x480
-
AVCaptureSessionPreset652x288
AVCaptureSessionPresetInputPriority 是不是一脸懵逼? 从 iOS 7 开始,在特定的设备硬件中,iOS 支持高帧率视频采集(被称为 Slomo 视频)。
在之前采集的视频帧率一般最高在 30fps, 高帧率视频采集支持 50fps 60fps 120fps… 。在低帧率采集模式下, 通过 session . sessionPresent 和 AVCaptureDevice 的 setActiveVideoMinFrameDuration: / setActivityVideoMaxFrameDuration: 可以别设置采集分辨率及帧率。高帧率下 Apple 弃用了这种方式,要求我们为 AVCaptureDevice 指定合适的 activeFormat 格式。如果你通过 activeFormat 的方式设置采集配置,之前的 sessionPresent 将会被重置为 AVCaptureSessionPresetInputPriority。
高帧率视频的具体用法 ,在 AVCam 项目中我们会涉及到。目前 AVCam 中 sessionPresent 被设置为 AVCaptureSessionPresetHigh 。
现在 AVCaptureSession 已经被创建完成,紧接着我们的任务是为它添加具体的采集输入设备 ( AVCaptureDevice )。
在之前的文章中我们已经知道, AVCaptureDevice 是一个抽象类,每个具体的示例都会对应一个设备,例如摄像机或麦克风。
AVCaptureDevice 的创建有两种方式,第一种是通过 AVCaptureDevice 提供的类方法,另外一种是 通过 AVCaptureDeviceDiscoverySession 提供的类方法。
在这里我们暂且使用 AVCaptureDevice 创建,完整的函数签名如下:
[ AVCaptureDevic deviceWithDeviceType:AVCaptureDeviceType mediaType:AVMediaType position:AVCaptureDevicePosition];
AVCaptureDeviceType : 采集设备类型
-
AVCaptureDeviceTypeBuiltInMichrophone 麦克风设备
-
AVCaptureDeviceTypeBuiltInWideAngleCamera 内置广角相机设备
-
AVCaptureDeviceTypeBuiltInTelephotoCamera 内置的相机设备 焦距比广角相机长。 注意: 此类设备只能使用 AVCaptureDeviceDiscoverySession 发现
-
AVCaptureDeviceTypeBuiltInUltraWideCamera 比广角相机焦距短的内置相机设备。 注意: 此类设备只能使用 AVCaptureDeviceDiscoverySession 发现
-
AVCaptureDeviceTypeBuiltInDualCamera 一种由两个固定的焦距照相机组成的 设备 。一个是广角镜头(Wide),一个是远摄镜头 (Telephoto)。
-
AVCaptureDeviceTypeBuiltInDualWideCamera 一种由两个固定焦距照相机组成的 设备 。一个是超宽镜头(Ultra Wide),一个是广角镜头(Wide Angle)。
-
AVCaptureDeviceTypeBuiltInTripleCamera 一种有三个固定焦距照相机组成的 设备 。一个超宽镜头( Ultra Wide ),一个广角镜头( Wide Angle )和一个远摄镜头( Telephoto )组成。
-
AVCaptureDeviceTypeBuiltInTrueDepthCamera 一种由两台摄像机组成的设备。一台 YUV 和一台红外线。红外线摄像头可提供高质的深度信息,该信息可与 YUV 摄像头产生的帧同步并进行透视纠正。两台摄像头的分辨率可能不通透,但他们的相同的纵横比。
AVMediaType : 支持的媒体类型
-
AVMediaTypeVideo 视频
-
AVMediaTypeAudio 音频
-
AVMediaTypeText 文本
-
AVMediaTypeSubtitle 字幕
-
AVMediaTypeMetadata 元数据
AVCaptureDevicePosition : 支持摄像头位置
-
AVCaptureDevicePositionUnspecified 未指定
-
AVCaptureDevicePositionBack 后置
-
AVCaptureDevicePositionFront 前置
AVCaptureDevice 创建完成后,根据图示我们还缺一个 AVCatpureDeviceInput。
AVCatpureDeviceInput 同样为我们提供提供了类方法。
[ AVCatpureDeviceInput deviceInputWithDevice:AVCaptureDevice error:NSError];
接下来就是将 AVCaptureDeviceInput 添加到 AVCaptureSession 中, AVCaptureDeviceInput 可以从指定的 AVCatpureDevice 采集媒体数据并交由 AVCaptureSession 。
AVCaptureSession 关联采集设备
在添加设备之前,我们有必要看看 AVCatpureSession 的详细接口。
-
canSetSessionPreset:(AVCaptureSessionPreset)preset 是否支持该 preset
-
canAddInput:(AVCaptureInput *)input 是否可以添加指定的采集输入设备
-
addInput:(AVCaptureInput *)input 添加采集输入设备
-
removeIn p ut :(AVCaptureInput * ) input 移除指定的采集输入设备
-
canAddOutput:(AVCaptureOutput *)output 是 否可以添加指 定的输出接口
-
addOutput:( AVCa ptureOutput *)output 添加采集输出接口
-
removeOutput :(AVCaptureOutput * ) outp ut 移除 指定的 输出接口
-
startRuning 启动采集会话,开始采集并输出
-
stopRuning 停止采集会话
为 AVCatpureSession 添加采集设备,需使用 addInput :(AVCaptureInput *)input 函数。
添加完成后, 使用 startRuning 启动采集会话 ,就可以看到相机捕获的画面。
可是这里我们没有指定 output 啊?? 不知道你记得 AVCaptureVideoPreviewLayer , previewLayer 内部维护的就是一个 Ouput,获取到相机数据后,渲染到视图上。后边我们录制视频时,会涉及到 Ou p ut 。
04 配置相机权限
配置权限千万不要忘记,需要我们在 plist 中配置相关说明。
还需要在启动相机之前,让用户授权。
当用户授权完成后,使用 [session startRuning] 启动相机采集。
完整代码可以参考 AVCam 项目。
05 结束语
关注 GeekDev 公众号你将在第一时间获取最新内容。
如果你想了解更多信息回复 资料 获取。
往期内容:
下期预告:
使用 AVFoundation 开发一款相机 APP
◆ ◆ ◆