视频采集: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的输出端相关。
相关文章
|
2月前
|
Java Android开发 Swift
安卓与iOS开发对比:平台选择对项目成功的影响
【10月更文挑战第4天】在移动应用开发的世界中,选择合适的平台是至关重要的。本文将深入探讨安卓和iOS两大主流平台的开发环境、用户基础、市场份额和开发成本等方面的差异,并分析这些差异如何影响项目的最终成果。通过比较这两个平台的优势与挑战,开发者可以更好地决定哪个平台更适合他们的项目需求。
122 1
|
3月前
|
IDE Android开发 iOS开发
探索Android与iOS开发的差异:平台选择对项目成功的影响
【9月更文挑战第27天】在移动应用开发的世界中,Android和iOS是两个主要的操作系统平台。每个系统都有其独特的开发环境、工具和用户群体。本文将深入探讨这两个平台的关键差异点,并分析这些差异如何影响应用的性能、用户体验和最终的市场表现。通过对比分析,我们将揭示选择正确的开发平台对于确保项目成功的重要作用。
|
22天前
|
IDE 开发工具 Android开发
移动应用开发之旅:探索Android和iOS平台
在这篇文章中,我们将深入探讨移动应用开发的两个主要平台——Android和iOS。我们将了解它们的操作系统、开发环境和工具,并通过代码示例展示如何在这两个平台上创建一个简单的“Hello World”应用。无论你是初学者还是有经验的开发者,这篇文章都将为你提供有价值的信息和技巧,帮助你更好地理解和掌握移动应用开发。
50 17
|
3月前
|
监控 Android开发 iOS开发
深入探索安卓与iOS的系统架构差异:理解两大移动平台的技术根基在移动技术日新月异的今天,安卓和iOS作为市场上最为流行的两个操作系统,各自拥有独特的技术特性和庞大的用户基础。本文将深入探讨这两个平台的系统架构差异,揭示它们如何支撑起各自的生态系统,并影响着全球数亿用户的使用体验。
本文通过对比分析安卓和iOS的系统架构,揭示了这两个平台在设计理念、安全性、用户体验和技术生态上的根本区别。不同于常规的技术综述,本文以深入浅出的方式,带领读者理解这些差异是如何影响应用开发、用户选择和市场趋势的。通过梳理历史脉络和未来展望,本文旨在为开发者、用户以及行业分析师提供有价值的见解,帮助大家更好地把握移动技术发展的脉络。
120 6
|
3月前
|
开发工具 Android开发 iOS开发
安卓与iOS开发环境对比:选择适合你的平台
【9月更文挑战第26天】在移动应用开发的广阔天地中,安卓和iOS是两大巨头。它们各自拥有独特的优势和挑战,影响着开发者的选择和决策。本文将深入探讨这两个平台的开发环境,帮助你理解它们的核心差异,并指导你根据个人或项目需求做出明智的选择。无论你是初学者还是资深开发者,了解这些平台的异同都至关重要。让我们一起探索,找到最适合你的那片开发天地。
|
3月前
|
IDE 开发工具 Android开发
安卓与iOS开发对比:平台选择对项目成功的影响
【9月更文挑战第10天】在移动应用开发的世界中,选择正确的平台是至关重要的。本文将深入探讨安卓和iOS这两大主要移动操作系统的开发环境,通过比较它们的市场份额、开发工具、编程语言和用户群体等方面,为开发者提供一个清晰的指南。我们将分析这两个平台的优势和劣势,并讨论如何根据项目需求和目标受众来做出最佳选择。无论你是初学者还是有经验的开发者,这篇文章都将帮助你更好地理解每个平台的特性,并指导你做出明智的决策。
|
3月前
|
开发工具 Android开发 iOS开发
安卓与iOS开发:平台选择的艺术与科学
在移动应用开发的广阔天地中,安卓与iOS两大平台如同东西方哲学的碰撞,既有共通之处又各具特色。本文将深入探讨这两个平台的设计理念、开发工具和市场定位,旨在为开发者提供一份简明扼要的指南,帮助他们在这场技术与商业的博弈中找到自己的道路。通过比较分析,我们将揭示每个平台的优势与局限,以及它们如何影响应用的性能、用户体验和市场接受度。无论你是初涉江湖的新手,还是经验丰富的老手,这篇文章都将为你的选择提供新的视角和思考。
46 5
|
3月前
|
人工智能 Android开发 iOS开发
安卓与iOS开发:平台选择的艺术
在移动应用开发的广阔天地里,安卓和iOS两大操作系统各占半壁江山。本文将深入探讨这两个平台的开发环境、工具及市场趋势,帮助开发者在选择适合自己项目的平台时做出更明智的决策。通过比较各自的优势与局限,我们不仅能更好地理解每个系统的核心特性,还能洞察未来技术发展的脉络。无论你是刚入行的新手还是资深开发者,这篇文章都将为你提供有价值的参考和启示。
56 5
|
3月前
|
Linux Android开发 iOS开发
探索Android与iOS开发:平台之战还是互补共生?
在移动应用开发的浩瀚宇宙中,Android和iOS这两大星系始终吸引着无数开发者的目光。它们各自拥有独特的引力场,引领着技术潮流的方向。本文将穿梭于这两个平台的星际空间,揭示它们背后的力量对比,以及如何在这两者之间找到平衡点,共同推动移动应用开发的进步。
46 1
|
3月前
|
移动开发 开发框架 Android开发
安卓与iOS开发:平台之战的新篇章
在移动应用开发的广阔天地中,安卓和iOS始终占据着主导地位。本文通过比较这两个平台的发展历程、技术特点及未来趋势,探讨了它们之间的竞争与合作。文章旨在为开发者提供一个清晰的平台选择指南,并预测未来移动开发的可能走向。
53 1