直播软件搭建音视频开发中的视频采集

前言

在直播和短视频行业日益火热的发展形势下,音视频开发(采集、编解码、传输、播放、美颜)等技术也随之成为开发者们关注的重点,本系列文章就音视频开发过程中所运用到的技术和原理进行梳理和总结。

认识 AVCapture 系列

AVCapture 系列是 AVFoundation 框架为我们提供的用于管理输入设备、采集、输出、预览等一系列接口,其工作原理如下:

1. AVCaptureDevice: 信号采集硬件设备(摄像头、麦克风、屏幕等)

AVCaptureDevice 代表硬件设备,并且为 AVCaptureSession 提供 input,要想使用 AVCaptureDevice,应该先将设备支持的 device 枚举出来, 根据摄像头的位置( 前置或者后置摄像头 )获取需要用的那个摄像头, 再使用; 如果想要对 AVCaptureDevice 对象的一些属性进行设置,应该先调用 lockForConfiguration: 方法, 设置结束后,调用 unlockForConfiguration 方法;

    [self.device lockForConfiguration:&error];// 设置 ***[self.device unlockForConfiguration];
复制代码

2. AVCaptureInput: 输入数据管理

AVCaptureInput 继承自 NSObject,是向 AVCaptureSession 提供输入数据的对象的抽象超类; 要将 AVCaptureInput 对象与会话 AVCaptureSession 关联,需要 AVCaptureSession实例调用 -addInput: 方法。

由于 AVCaptureInput 是个抽象类,无法直接使用,所以我们一般使用它的子类类管理输入数据。我们常用的 AVCaptureInput 的子类有三个:

AVCaptureDeviceInput:用于从 AVCaptureDevice 对象捕获数据; AVCaptureScreenInput:从 macOS 屏幕上录制的一种捕获输入; AVCaptureMetadataInput:它为 AVCaptureSession 提供 AVMetadataItems

3. AVCaptureOutput:输出数据管理

AVCaptureOutput 继承自 NSObject,是输出数据管理,该对象将会被添加到会话AVCaptureSession中,用于接收会话AVCaptureSession各类输出数据; AVCaptureOutput提供了一个抽象接口,用于将捕获输出数据(如文件和视频预览)连接到捕获会话AVCaptureSession的实例,捕获输出可以有多个由AVCaptureConnection对象表示的连接,一个连接对应于它从捕获输入(AVCaptureInput的实例)接收的每个媒体流,捕获输出在首次创建时没有任何连接,当向捕获会话添加输出时,将创建连接,将该会话的输入的媒体数据映射到其输出,调用AVCaptureSession-addOutput:方法将AVCaptureOutputAVCaptureSession关联。

AVCaptureOutput 是个抽象类,我们必须使用它的子类,常用的 AVCaptureOutput的子类如下所示:

AVCaptureAudioDataOutput:一种捕获输出,用于记录音频,并在录制音频时提供对音频样本缓冲区的访问; AVCaptureAudioPreviewOutput :一种捕获输出,与一个核心音频输出设备相关联、可用于播放由捕获会话捕获的音频; AVCaptureDepthDataOutput :在兼容的摄像机设备上记录场景深度信息的捕获输出; AVCaptureMetadataOutput :用于处理捕获会话 AVCaptureSession 产生的定时元数据的捕获输出; AVCaptureStillImageOutput:在macOS中捕捉静止照片的捕获输出。该类在 iOS 10.0 中被弃用,并且不支持新的相机捕获功能,例如原始图像输出和实时照片,在 iOS 10.0 或更高版本中,使用 AVCapturePhotoOutput 类代替; AVCapturePhotoOutput :静态照片、动态照片和其他摄影工作流的捕获输出; AVCaptureVideoDataOutput :记录视频并提供对视频帧进行处理的捕获输出; AVCaptureFileOutput:用于捕获输出的抽象超类,可将捕获数据记录到文件中; AVCaptureMovieFileOutput :继承自 AVCaptureFileOutput,将视频和音频记录到 QuickTime 电影文件的捕获输出; AVCaptureAudioFileOutput :继承自 AVCaptureFileOutput,记录音频并将录制的音频保存到文件的捕获输出。

4. AVCaptureSession:用来管理采集数据和输出数据,它负责协调从哪里采集数据,输出到哪里,它是整个Capture的核心,类似于RunLoop,它不断的从输入源获取数据,然后分发给各个输出源

AVCaptureSession 继承自NSObject,是AVFoundation的核心类,用于管理捕获对象AVCaptureInput的视频和音频的输入,协调捕获的输出AVCaptureOutput

5. AVCaptureConnection:用于 AVCaptureSession 来建立和维护 AVCaptureInputAVCaptureOutput 之间的连接

AVCaptureConnection 是 SessionOutput 中间的控制节点,每个 OutputSession 建立连接后,都会分配一个默认的 AVCpatureConnection

6. AVCapturePreviewLayer:预览层,AVCaptureSession 的一个属性,继承自 CALayer,提供摄像头的预览功能,照片以及视频就是通过把 AVCapturePreviewLayer 添加到 UIViewlayer 上来显示

开始视频采集

1、创建并初始化输入AVCaptureInput: AVCaptureDeviceInput 和输出AVCaptureOutput: AVCaptureVideoDataOutput; 2、创建并初始化 AVCaptureSession,把 AVCaptureInputAVCaptureOutput 添加到 AVCaptureSession 中; 3、调用 AVCaptureSessionstartRunning 开启采集

初始化输入

通过 AVCaptureDevicedevicesWithMediaType: 方法获取摄像头,iPhone 都是有前后摄像头的,这里获取到的是一个设备的数组,要从数组里面拿到我们想要的前摄像头或后摄像头,然后将 AVCaptureDevice 转化为 AVCaptureDeviceInput,添加到 AVCaptureSession

        /**************************  设置输入设备  *************************/// ---  获取所有摄像头  ---NSArray *cameras = [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo];// ---  获取当前方向摄像头  ---NSArray *captureDeviceArray = [cameras filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"position == %d", _capturerParam.devicePosition]];if (captureDeviceArray.count == 0) {return nil;}// ---  转化为输入设备  ---AVCaptureDevice *camera = captureDeviceArray.firstObject;self.captureDeviceInput = [AVCaptureDeviceInput deviceInputWithDevice:cameraerror:&error];

设置视频采集参数

@implementation VideoCapturerParam- (instancetype)init {self = [super init];if (self) {_devicePosition = AVCaptureDevicePositionFront;    // 摄像头位置,默认为前置摄像头_sessionPreset = AVCaptureSessionPreset1280x720;   // 视频分辨率 默认 AVCaptureSessionPreset1280x720_frameRate = 15;  // 帧 单位为 帧/秒,默认为15帧/秒_videoOrientation = AVCaptureVideoOrientationPortrait;   // 摄像头方向 默认为当前手机屏幕方向switch ([UIDevice currentDevice].orientation) {case UIDeviceOrientationPortrait:case UIDeviceOrientationPortraitUpsideDown:_videoOrientation = AVCaptureVideoOrientationPortrait;break;case UIDeviceOrientationLandscapeRight:_videoOrientation = AVCaptureVideoOrientationLandscapeRight;break;case UIDeviceOrientationLandscapeLeft:_videoOrientation = AVCaptureVideoOrientationLandscapeLeft;break;default:break;}}return self;
}
复制代码

初始化输出

初始化视频输出 AVCaptureVideoDataOutput,并设置视频数据格式,设置采集数据回调线程,这里视频输出格式选的是 kCVPixelFormatType_420YpCbCr8BiPlanarFullRange,YUV 数据格式

        /**************************  设置输出设备  *************************/// ---  设置视频输出  ---self.captureVideoDataOutput = [[AVCaptureVideoDataOutput alloc] init];NSDictionary *videoSetting = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithInt:kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange], kCVPixelBufferPixelFormatTypeKey, nil];   // kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange 表示输出的视频格式为NV12[self.captureVideoDataOutput setVideoSettings:videoSetting];// ---  设置输出串行队列和数据回调  ---dispatch_queue_t outputQueue = dispatch_queue_create("VideoCaptureOutputQueue", DISPATCH_QUEUE_SERIAL);// ---  设置代理  ---[self.captureVideoDataOutput setSampleBufferDelegate:self queue:outputQueue];// ---  丢弃延迟的帧  ---self.captureVideoDataOutput.alwaysDiscardsLateVideoFrames = YES;复制代码

初始化 AVCaptureSession 并设置输入输出

1、初始化 AVCaptureSession,把上面的输入和输出加进来,在添加输入和输出到 AVCaptureSession 先查询一下 AVCaptureSession 是否支持添加该输入或输出端口;

2、设置视频分辨率及图像质量(AVCaptureSessionPreset),设置之前同样需要先查询一下 AVCaptureSession 是否支持这个分辨率;

3、如果在已经开启采集的情况下需要修改分辨率或输入输出,需要用 beginConfigurationcommitConfiguration 把修改的代码包围起来。在调用 beginConfiguration 后,可以配置分辨率、输入输出等,直到调用 commitConfiguration 了才会被应用;

4、AVCaptureSession 管理了采集过程中的状态,当开始采集、停止采集、出现错误等都会发起通知,我们可以监听通知来获取 AVCaptureSession 的状态,也可以调用其属性来获取当前 AVCaptureSession 的状态, AVCaptureSession 相关的通知都是在主线程的。

前置摄像头采集到的画面是翻转的,若要解决画面翻转问题,需要设置 AVCaptureConnectionvideoMirrored 为 YES。

/**************************  初始化会话  *************************/self.captureSession = [[AVCaptureSession alloc] init];self.captureSession.usesApplicationAudioSession = NO;// ---  添加输入设备到会话  ---if ([self.captureSession canAddInput:self.captureDeviceInput]) {[self.captureSession addInput:self.captureDeviceInput];}else {NSLog(@"VideoCapture:: Add captureVideoDataInput Faild!");return nil;}// ---  添加输出设备到会话  ---if ([self.captureSession canAddOutput:self.captureVideoDataOutput]) {[self.captureSession addOutput:self.captureVideoDataOutput];}else {NSLog(@"VideoCapture:: Add captureVideoDataOutput Faild!");return nil;}// ---  设置分辨率  ---if ([self.captureSession canSetSessionPreset:self.capturerParam.sessionPreset]) {self.captureSession.sessionPreset = self.capturerParam.sessionPreset;}/**************************  初始化连接  *************************/self.captureConnection = [self.captureVideoDataOutput connectionWithMediaType:AVMediaTypeVideo];// ---  设置摄像头镜像,不设置的话前置摄像头采集出来的图像是反转的  ---if (self.capturerParam.devicePosition == AVCaptureDevicePositionFront && self.captureConnection.supportsVideoMirroring) { // supportsVideoMirroring 视频是否支持镜像self.captureConnection.videoMirrored = YES;}self.captureConnection.videoOrientation = self.capturerParam.videoOrientation;self.videoPreviewLayer = [AVCaptureVideoPreviewLayer layerWithSession:self.captureSession];self.videoPreviewLayer.connection.videoOrientation = self.capturerParam.videoOrientation;self.videoPreviewLayer.videoGravity = AVLayerVideoGravityResizeAspectFill;复制代码

采集视频 / 回调

/*** 开始采集*/
- (NSError *)startCpture {if (self.isCapturing) {return [NSError errorWithDomain:@"VideoCapture:: startCapture faild: is capturing" code:1 userInfo:nil];}// ---  摄像头权限判断  ---AVAuthorizationStatus videoAuthStatus = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo];if (videoAuthStatus != AVAuthorizationStatusAuthorized) {return [NSError errorWithDomain:@"VideoCapture:: Camera Authorizate faild!" code:1 userInfo:nil];}[self.captureSession startRunning];self.isCapturing = YES;kLOGt(@"开始采集视频");return nil;
}/*** 停止采集*/
- (NSError *)stopCapture {if (!self.isCapturing) {return [NSError errorWithDomain:@"VideoCapture:: stop capture faild! is not capturing!" code:1 userInfo:nil];}[self.captureSession stopRunning];self.isCapturing = NO;kLOGt(@"停止采集视频");return nil;
}#pragma mark ————— AVCaptureVideoDataOutputSampleBufferDelegate —————
/*** 摄像头采集数据回调@prama output       输出设备@prama sampleBuffer 帧缓存数据,描述当前帧信息@prama connection   连接*/
- (void)captureOutput:(AVCaptureOutput *)output didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection {if ([self.delagate respondsToSelector:@selector(videoCaptureOutputDataCallback:)]) {[self.delagate videoCaptureOutputDataCallback:sampleBuffer];}
}

调用 / 获取数据

调用很简单,初始化视频采集参数 VideoCapturerParam 和 视频采集器 VideoVapturer , 设置预览图层 videoPreviewLayer , 调用 startCpture 就可以开始采集了,然后实现数据采集回调的代理方法 videoCaptureOutputDataCallback 获取数据

    // --- 初始化视频采集参数  ---VideoCapturerParam *param = [[VideoCapturerParam alloc] init];// ---  初始化视频采集器  ---self.videoCapture = [[VideoVapturer alloc] initWithCaptureParam:param error:nil];self.videoCapture.delagate = self;// ---  开始采集  ---[self.videoCapture startCpture];// ---  初始化预览View  ---self.recordLayer = self.videoCapture.videoPreviewLayer;self.recordLayer.frame = CGRectMake(0, 0, CGRectGetWidth(self.view.bounds), CGRectGetHeight(self.view.bounds));[self.view.layer addSublayer:self.recordLayer];
复制代码
#pragma mark ————— VideoCapturerDelegate —————  视频采集回调
- (void)videoCaptureOutputDataCallback:(CMSampleBufferRef)sampleBuffer {NSLog(@"%@ sampleBuffer : %@ ", kLOGt(@"视频采集回调"), sampleBuffer);
}
复制代码

至此,我们就完成了视频的采集,在采集前和过程中,我们可能会对采集参数、摄像头方向、帧率等进行修改,具体的实现附上 Demo 地址:

github.com/G-Jayson/JX…

直播软件搭建音视频开发中的视频采集相关推荐

  1. 在直播软件搭建中,如何基于rtmp实现视频直播?

    最近几年直播软件搭建可谓大火,这也就导致了在视频直播领域,有不同的商家提供各种的商业解决方案,包括软硬件设备,摄像机,编码器,流媒体服务器等.本文要讲解的是在直播软件搭建中,如何基于rtmp实现视频直 ...

  2. 直播软件搭建Android音视频方向进阶路线及资源合集

    直播软件搭建Android音视频方向进阶路线及资源合集 直播软件搭建的音视频从采集到播放都经历了哪些流程呢:: 通过上面的图,我们简单的把音视频方向分为主要的两块: 媒体部分(蓝色+绿色) 传输部分( ...

  3. Android直播软件搭建中实用的录制编辑方案有哪些

    Android直播软件搭建中实用的录制编辑方案有哪些 经大量数据显示,直播已经发展成为一种全民参与.共享和生产的文化现象.它的火爆不仅丰富了大众的艺术审美水平和精神文化,而且也影响了一代人的世界观.人 ...

  4. 视频直播源码 直播软件搭建进行直播推流时,对声音进行音质优化

    视频直播源码 直播软件搭建进行直播推流时,对声音进行音质优化 最近我们在使用WebRTC进行直播推流的时候,遇到了音乐音质不好的问题,对此进行优化后,音乐音质有很大提升,因此记录下优化过程,分享出来 ...

  5. 直播软件搭建直播服务架构

    直播软件搭建直播服务架构 前言 随着移动设备的普及和4G网络建设的全面铺开,短视频和直播行业日益火爆,其代表应用抖音.头条更是火遍大江南北,逐渐成为家喻户晓的国民APP.各大厂商也纷纷入局短视频赛道, ...

  6. 直播软件搭建技术原理:CDN 与直播

    直播软件搭建技术原理:CDN 与直播 很多直播都是基于 CDN 来实现的.而通过声网的服务,或基于声网SDK与 CDN 结合,还可以实现在直播中的连麦互动.白板同步等强调实时性的场景.本文源自社区投稿 ...

  7. Android短视频开发中的sdk接入方案

    目前短视频平台非常火,云豹科技作为优质的app源码提供商,在短视频开发领域有丰富的经验和完善的技术.下面以云豹短视频为例,概述Android短视频开发中的sdk接入方案,这里我们选择腾讯云的sdk进行 ...

  8. Android直播软件搭建左滑右滑清屏控件

    Android直播软件搭建左滑右滑清屏控件 最近在迭代直播软件搭建功能时,项目中之前的左滑清屏是用ViewPager实现的.这次迭代遇到一个布局层次导致的点击失效问题,继续用ViewPager的话改动 ...

  9. iOS音视频开发八:视频编码,H.264 和 H.265 都支持

    我们将通过拆解采集 → 编码 → 封装 → 解封装 → 解码 → 渲染流程并实现 Demo 来向大家介绍如何在 iOS/Android 平台上手音视频开发. 这里是第八篇:iOS 视频编码 Demo. ...

最新文章

  1. 5s的app显示无法连接服务器,苹果5s无法连接app store解决方法汇总
  2. 安测云验证有CTA问题
  3. 学习TensorFlow、PyTorch、机器学习、深度学习和数据结构五件套!附下载链接!...
  4. python 柱状图 居中_python matplotlib模块: bar(柱状图)
  5. 高并发设计方案二(秒杀架构)
  6. ubuntu启动进程笔记
  7. 通过简单的 ResourceManager 管理 XNA 中的资源,WPXNA(二)
  8. SAP Smart Business design time = CDS view SADL
  9. 《c语言从入门到精通》看书笔记——第10章 指针
  10. spring boot之session store type is 'null'
  11. Google 作恶!99.9% 的 Android 手机 App 都在窃取隐私
  12. JS总结 循环 退出循环 函数
  13. Pivotal Cloud Foundry安全原理解析
  14. GPS信号防丢失、干扰和欺骗——基于雷达的解决方案
  15. ERP系统-应收应付子系统-付款单
  16. 办公技巧:10个WORD神操作,值得收藏
  17. Linux集群部署及搭建-----Hadoop
  18. 滑动到尽头时去掉阴影效果
  19. 沪江日语百度云视频 0-n1百度云网盘视频的swf需要特定的播放器才能播放
  20. 四川师范大学计算机科学学院周雄俊简历,润物无声,花开有时 川师附小教师让教育更纯粹...

热门文章

  1. vue中使用echarts实现动态数据绑定、获取后端接口数据
  2. 抽象代数之群同态基本定理的证明之群同态的核是群G的正规子群
  3. 为什么专业领域里外行领导能让墙倒屋塌?
  4. 法国西南部发生火车追尾事故 已造成至少40人伤
  5. 详细图解,卷帘快门(Rolling Shutter)与全局快门(Global Shutter)的区别
  6. 13:Scala语言的数据结构和算法
  7. 如何把软件上传服务器别人能下载呢
  8. 为什么我要用 Node.js? 案例逐一介绍
  9. 计算机控制技术曹立学答案,计算机控制技术(曹立学)
  10. Host管理工具 SwitchHosts