视频采集:iOS平台基于AVCaptureDevice的实现

简介: 入门知识AVCaptureSession在iOS平台开发中只要跟硬件相关的都要从会话开始进行配置,如果我们使用摄像头的话可以利用AVCaptureSession进行视频采集,其可以对输入和输出数据进行管理,负责协调从哪里采集数据,输出到哪里去。

入门知识
AVCaptureSession
在iOS平台开发中只要跟硬件相关的都要从会话开始进行配置,如果我们使用摄像头的话可以利用AVCaptureSession进行视频采集,其可以对输入和输出数据进行管理,负责协调从哪里采集数据,输出到哪里去。
AVCaptureDevice
一个AVCaptureDevice对应的是一个物理采集设备,我们可以通过该对象来获取和识别设备属性。
例如通过AVCaptureDevice.position检测其摄像头的方向。
AVCaptureInput
AVCaptureInput是一个抽象类,AVCaptureSession的输入端必须是AVCaptureInput的实现类。
例如利用AVCaptureDevice构建AVCaptureDeviceInput作为采集设备输入端。
AVCaptureOutput
AVCaptureOutput是一个抽象类,AVCaptureSession的输出端必须是AVCaptureOutput的实现类。
例如AVCaptureVideoDataOutput可以作为一个原始视频数据的输出端。
AVCaptureConnection
AVCaptureConnection是AVCaptureSession用来建立和维护AVCaptureInput和AVCaptureOutput之间的连接的,一个AVCaptureSession可能会有多个AVCaptureConnection实例。
采集步骤

  1. 创建AVCaptureSession并初始化。
  2. 通过前后置摄像头找到对应的AVCaptureDevice。
  3. 通过AVCaptureDevice创建输入端AVCaptureDeviceInput,并将其添加到AVCaptureSession的输入端。
  4. 创建输出端AVCaptureVideoDataOutput,并进行Format和Delgate的配置,最后添加到AVCaptureSession的输出端。
  5. 获取AVCaptureConnection,并进行相应的参数设置。
  6. 调用AVCaptureSession的startRunning和stopRunning设置采集状态。
    配置会话

创建一个AVCaptureSession很简单:
AVCaptureSession *captureSession;
captureSession = [[AVCaptureSession alloc] init];
我们可以在AVCaptureSession来配置指定所需的图像质量和分辨率,可选参数请参考AVCaptureSessionPreset.h。
在设置前需要检测是否支持该Preset是否被支持:
//指定采集1280x720分辨率大小格式
AVCaptureSessionPreset preset = AVCaptureSessionPreset1280x720;
//检查AVCaptureSession是否支持该AVCaptureSessionPreset
if ([captureSession canSetSessionPreset:preset]) {

captureSession.sessionPreset = preset;

}
else {

//错误处理,不支持该AVCaptureSessionPreset类型值

}
配置输入端
通过AVCaptureDevice的devicesWithMediaType的方法来获取摄像头,由于iOS存在多个摄像头,所以这里一般返回一个设备的数组。
根据业务需要(例如前后置摄像头),我们找到其中对应的AVCaptureDevice,并将其构造成AVCaptureDeviceInput实例。
AVCaptureDevice *device;
AVCaptureDeviceInput *captureInput;
//获取前后置摄像头的标识
AVCaptureDevicePosition position = _isFront ? AVCaptureDevicePositionFront : AVCaptureDevicePositionBack;
//获取设备的AVCaptureDevice列表
NSArray *devices = [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo];
for (AVCaptureDevice *item in devices) {

//如果找到对应的摄像头
if ([item position] == position) {
    device = item;
    break;
}

}
if (device == nil) {

//错误处理,没有找到对应的摄像头

}
//创建AVCaptureDeviceInput输入端
captureInput = [[AVCaptureDeviceInput alloc] initWithDevice:device error:nil];
配置输出端
如果我们想要获取到摄像头采集到的原始视频数据的话,需要配置一个AVCaptureVideoDataOutput作为AVCaptureSession的输出端,我们需要给其设置采集的视频格式和采集数据回调队列。

AVCaptureVideoDataOutput *captureOutput;
//创建一个输出端AVCaptureVideoDataOutput实例
captureOutput = [[AVCaptureVideoDataOutput new];
//配置输出的数据格式
[captureOutput setVideoSettings:@{(id)kCVPixelBufferPixelFormatTypeKey: @(kCVPixelFormatType_420YpCbCr8PlanarFullRange)}];
//设置输出代理和采集数据的队列
dispatch_queue_t outputQueue = dispatch_queue_create("ACVideoCaptureOutputQueue", DISPATCH_QUEUE_SERIAL);
[captureOutput setSampleBufferDelegate:self queue:outputQueue];
// 丢弃延迟的帧
captureOutput.alwaysDiscardsLateVideoFrames = YES;
需要注意的几个点
•    对于setVideoSettings,虽然AVCaptureVideoDataOutput提供的是一个字典设置,但是现在只支持kCVPixelBufferPixelFormatTypeKey这个key。
•    像素格式默认使用的是YUVFullRange类型,表示其YUV取值范围是0~255,而还有另外一种类型YUVVideoRange类型则是为了防止溢出,将YUV的取值范围限制为16~235。
•    setSampleBufferDelegate必须指定串行队列来确保视频数据获取委托调用的正确顺序,当然你也可以修改队列来设置视频处理的优先级别。
•    alwaysDiscardsLateVideoFrames = YES可以在你没有足够时间处理视频帧时丢弃任何延迟的视频帧而不是等待处理,如果你设置了NO并不能保证帧不会被丢弃,只是他们不会被提前有意识的丢弃而已。
配置会话的输入和输出
//添加输入设备到会话
if ([captureSession canAddInput:captureInput]) {
    [captureSession addInput:captureInput];
}
//添加输出设备到会话
if ([captureSession canAddOutput:captureOutput]) {
    [captureSession addOutput:captureOutput];
}
//获取连接并设置视频方向为竖屏方向
AVCaptureConnection *conn = [captureOutput connectionWithMediaType:AVMediaTypeVideo];
conn.videoOrientation = AVCaptureVideoOrientationPortrait;
//前置摄像头采集到的数据本来就是镜像翻转的,这里设置为镜像把画面转回来
if (device.position == AVCaptureDevicePositionFront && conn.supportsVideoMirroring) {
    conn.videoMirrored = YES;
}
如果AVCaptureSession已经开启了采集,如果这个时候需要修改分辨率、输入输出等配置。那么需要用到beginConfiguration和commitConfiguration方法把修改的代码包围起来,也就是先调用beginConfiguration启动事务,然后配置分辨率、输入输出等信息,最后调用commitConfiguration提交修改;这样才能确保相应修改作为一个事务组提交,避免状态的不一致性。
AVCaptureSession管理了采集过程中的状态,当开始采集、停止采集、出现错误等都会发起通知,我们可以监听通知来获取AVCaptureSession的状态,也可以调用其属性来获取当前AVCaptureSession的状态,值得注意一点是AVCaptureSession相关的通知都是在主线程的。
开始采集数据和数据回调
当上面的配置搞定后,调用startRunning就可以开始数据的采集了。
if (![captureSession isRunning]) {
    [captureSession startRunning];
}
停止采集只需要调用stopRunning方法即可。
if ([captureSession isRunning]) {
    [captureSession stopRunning];
}
对于采集回调的视频数据,会在[captureOutput setSampleBufferDelegate:self queue:outputQueue]设置的代理方法触发返回,
其中最重要的是CMSampleBufferRef,其中实际存储着摄像头采集到的图像。
方法原型如下:
- (void)captureOutput:(AVCaptureOutput *)output 
        didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer 
        fromConnection:(AVCaptureConnection *)connection 
切换前后摄像头
在视频采集的过程中,我们经常需要切换前后摄像头,这里我们也就是需要把AVCaptureSession的输入端改为对应的摄像头就可以了。
当然我们可以用beginConfiguration和commitConfiguration将修改逻辑包围起来,也可以先调用stopRunning方法停止采集,然后重新配置好输入和输出,再调用startRunning开启采集。
//获取摄像头列表
NSArray *devices = [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo];
//获取当前摄像头方向
AVCaptureDevicePosition currentPosition = captureInput.device.position;
//转换摄像头
if (currentPosition == AVCaptureDevicePositionBack){
    currentPosition = AVCaptureDevicePositionFront;
}
else{
    currentPosition = AVCaptureDevicePositionBack;
}
//获取到新的AVCaptureDevice
NSArray *captureDeviceArray = [devices filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"position == %d", currentPosition]];
AVCaptureDevice *device = captureDeviceArray.firstObject;
//开始配置
[captureSession beginConfiguration];
//构造一个新的AVCaptureDeviceInput的输入端
AVCaptureDeviceInput *newInput = [AVCaptureDeviceInput deviceInputWithDevice:device error:nil];
//移除掉就的AVCaptureDeviceInput
[captureSession removeInput:captureInput];
//将新的AVCaptureDeviceInput添加到AVCaptureSession中
if ([captureSession canAddInput:newInput]){
    [captureSession addInput:newInput];
    captureInput = newInput;
}
//提交配置
[captureSession commitConfiguration];
//重新获取连接并设置视频的方向、是否镜像
AVCaptureConnection *conn = [captureOutput connectionWithMediaType:AVMediaTypeVideo];
conn.videoOrientation = AVCaptureVideoOrientationPortrait;
if (device.position == AVCaptureDevicePositionFront && conn.supportsVideoMirroring){
    conn.videoMirrored = YES;
}
视频帧率
iOS默认的帧率设置是30帧,如果我们的业务场景不需要用到30帧,或者我们的处理能力达不到33ms(1000ms/30帧)的话,我们可以通过设置修改视频的输出帧率:
NSInteger fps = 15;
//获取设置支持设置的帧率范围
AVFrameRateRange *fpsRange = [captureInput.device.activeFormat.videoSupportedFrameRateRanges objectAtIndex:0];
if (fps > fpsRange.maxFrameRate || fps < fpsRange.minFrameRate) {
    //不支持该fps设置
    return;
}
// 设置输入的帧率
captureInput.device.activeVideoMinFrameDuration = CMTimeMake(1, (int)fps);
captureInput.device.activeVideoMaxFrameDuration = CMTimeMake(1, (int)fps);
简易预览
如果不想通过自己实现OpenGL渲染采集到的视频帧,当然,iOS也提供了一个预览组件AVCaptureVideoPreviewLayer,其继承于CALayer。
可以将这个layer添加到UIView上面就可以实现采集到的视频的实时预览。
//创建一个AVCaptureVideoPreviewLayer,并将AVCaptureSession传入
AVCaptureVideoPreviewLayer *previewLayer;
previewLayer = [[AVCaptureVideoPreviewLayer alloc] initWithSession:captureSession];
previewLayer.videoGravity = AVLayerVideoGravityResizeAspectFill;
previewLayer.frame = self.view.bounds;
//将其加载到UIView上面即可
[self.view.layer addSublayer:previewLayer];
PS:如果采用AVCaptureVideoPreviewLayer进行视频预览的话,那么可以不配置AVCaptureSession的输出端相关。
相关文章
|
6月前
|
数据安全/隐私保护 iOS开发
使用HBuilder平台打包并上传iOS应用到App Store的完整教程
使用HBuilder平台打包并上传iOS应用到App Store的完整教程
|
8月前
|
监控 Android开发 iOS开发
盘点一对一直播源码iOS系统维持平台稳定功能(一):弹性扩缩容
参考代码:弹性扩缩容如何实现?System.out.println("扩容:增加直播平台实例"); currentCapacity++; } private void scaleDown() { System.out.println("缩容:减少直播平台实例");
盘点一对一直播源码iOS系统维持平台稳定功能(一):弹性扩缩容
|
16天前
|
存储 缓存 iOS开发
实现iOS平台的高效图片缓存策略
【4月更文挑战第4天】在移动应用开发中,图片资源的加载与缓存是影响用户体验的关键因素之一。尤其对于iOS平台,由于设备存储和内存资源的限制,设计一个高效的图片缓存机制尤为重要。本文将深入探讨在iOS环境下,如何通过技术手段实现图片的高效加载与缓存,包括内存缓存、磁盘缓存以及网络层面的优化,旨在为用户提供流畅且稳定的图片浏览体验。
|
1月前
|
运维 监控 安全
应用研发平台EMAS常见问题之sophix ios flutter热更新如何解决
应用研发平台EMAS(Enterprise Mobile Application Service)是阿里云提供的一个全栈移动应用开发平台,集成了应用开发、测试、部署、监控和运营服务;本合集旨在总结EMAS产品在应用开发和运维过程中的常见问题及解决方案,助力开发者和企业高效解决技术难题,加速移动应用的上线和稳定运行。
77 0
|
2月前
|
监控 API Swift
用Swift开发iOS平台上的上网行为管理监控软件
在当今数字化时代,随着智能手机的普及,人们对于网络的依赖日益增加。然而,对于一些特定场景,如家庭、学校或者企业,对于iOS设备上的网络行为进行管理和监控显得尤为重要。为了满足这一需求,我们可以利用Swift语言开发一款iOS平台上的上网行为管理监控软件。
181 2
|
2月前
|
监控 API iOS开发
克魔助手 - iOS性能检测平台
众所周知,如今的用户变得越来越关心app的体验,开发者必须关注应用性能所带来的用户流失问题。目前危害较大的性能问题主要有:闪退、卡顿、发热、耗电快、网络劫持等,但是做过iOS开发的人都知道,在开发过程中我们没有一个很直观的工具可以实时的知道开发者写出来的代码会不会造成性能问题,虽然Xcode里提供了耗电量检测、内存泄漏检测等工具,但是这些工具使用效果并不理想(如Leak无法发现循环引用造成的内存泄漏)。所以这篇文章主要是介绍一款实时监控app各项性能指标的工具,包括CPU占用率、内存使用量、内存泄漏、FPS、卡顿检测,并且会分析造成这些性能问题的原因。
|
7月前
|
开发工具 Android开发 iOS开发
Android、iOS平台RTMP/RTSP播放器实现实时音量调节
介绍移动端RTMP、RTSP播放器实时音量调节之前,我们之前也写过,为什么windows播放端加这样的接口,windows端播放器在多窗口大屏显示的场景下尤其需要,尽管我们老早就有了实时静音接口,相对实时静音来说,播放端实时音量调节粒度更细,从[0, 100],用户体验更好。
128 1
|
4月前
|
API 开发工具 iOS开发
在应用研发平台EMAS中,ios的推送有没有办法在app端设置在收到通知后是否展示的逻辑
在应用研发平台EMAS中,ios的推送有没有办法在app端设置在收到通知后是否展示的逻辑
32 1
|
5月前
|
视频直播 API iOS开发
微信团队分享:详解iOS版微信视频号直播中因帧率异常导致的功耗问题
功耗优化一直是 app 性能优化中让人头疼的问题,尤其是在直播这种用户观看时长特别久的场景。怎样能在不影响主体验的前提下,进一步优化微信iOS端视频号直播的功耗占用,本文给出了一个不太一样的答案。
86 0
|
7月前
|
iOS开发 Perl
iOS平台上的AliVRPlayer的使用
iOS平台上的AliVRPlayer的使用
70 1