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