【如何快速的开发一个完整的iOS直播app】(采集篇)
前言
在看这篇之前,如果您还不了解直播原理,请查看这篇文章如何快速的开发一个完整的iOS直播app(原理篇)
开发一款直播app,首先需要采集主播的视频和音频,然后传入流媒体服务器,本篇主要讲解如何采集主播的视频和音频,当前可以切换前置后置摄像头和焦点光标
,但是美颜功能还没做,可以看见素颜的你,后续还会有直播的其他功能文章陆续发布。
如果喜欢我的文章,可以关注我微博:袁峥Seemygo
效果
为了采集效果图,我也是豁出去了,请忽略人物,关注技术。

基本知识介绍
AVFoundation
: 音视频数据采集需要用AVFoundation框架.AVCaptureDevice
:硬件设备,包括麦克风、摄像头,通过该对象可以设置物理设备的一些属性(例如相机聚焦、白平衡等)AVCaptureDeviceInput
:硬件输入对象,可以根据AVCaptureDevice创建对应的AVCaptureDeviceInput对象,用于管理硬件输入数据。AVCaptureOutput
:硬件输出对象,用于接收各类输出数据,通常使用对应的子类AVCaptureAudioDataOutput(声音数据输出对象)、AVCaptureVideoDataOutput(视频数据输出对象)AVCaptionConnection
:当把一个输入和输出添加到AVCaptureSession之后,AVCaptureSession就会在输入、输出设备之间建立连接,而且通过AVCaptureOutput可以获取这个连接对象。AVCaptureVideoPreviewLayer
:相机拍摄预览图层,能实时查看拍照或视频录制效果,创建该对象需要指定对应的AVCaptureSession对象,因为AVCaptureSession包含视频输入数据,有视频数据才能展示。AVCaptureSession
:协调输入与输出之间传输数据
- 系统作用:可以操作硬件设备
- 工作原理:让App与系统之间产生一个捕获会话,相当于App与硬件设备有联系了, 我们只需要把硬件输入对象和输出对象添加到会话中,会话就会自动把硬件输入对象和输出产生连接,这样硬件输入与输出设备就能传输音视频数据。
- 现实生活场景:租客(输入钱),中介(会话),房东(输出房),租客和房东都在中介登记,中介就会让租客与房东之间产生联系,以后租客就能直接和房东联系了。
捕获音视频步骤:官方文档
1.
创建AVCaptureSession对象2.
获取AVCaptureDevicel录像设备(摄像头),录音设备(麦克风),注意不具备输入数据功能,只是用来调节硬件设备的配置。3.
根据音频/视频硬件设备(AVCaptureDevice)创建音频/视频硬件输入数据对象(AVCaptureDeviceInput),专门管理数据输入。4.
创建视频输出数据管理对象(AVCaptureVideoDataOutput),并且设置样品缓存代理(setSampleBufferDelegate)就可以通过它拿到采集到的视频数据5.
创建音频输出数据管理对象(AVCaptureAudioDataOutput),并且设置样品缓存代理(setSampleBufferDelegate)就可以通过它拿到采集到的音频数据6.
将数据输入对象AVCaptureDeviceInput、数据输出对象AVCaptureOutput添加到媒体会话管理对象AVCaptureSession中,就会自动让音频输入与输出和视频输入与输出产生连接.7.
创建视频预览图层AVCaptureVideoPreviewLayer并指定媒体会话,添加图层到显示容器layer中8.
启动AVCaptureSession,只有开启,才会开始输入到输出数据流传输。
// 捕获音视频
- (void)setupCaputureVideo
{// 1.创建捕获会话,必须要强引用,否则会被释放AVCaptureSession *captureSession = [[AVCaptureSession alloc] init];_captureSession = captureSession;// 2.获取摄像头设备,默认是后置摄像头AVCaptureDevice *videoDevice = [self getVideoDevice:AVCaptureDevicePositionFront];// 3.获取声音设备AVCaptureDevice *audioDevice = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeAudio];// 4.创建对应视频设备输入对象AVCaptureDeviceInput *videoDeviceInput = [AVCaptureDeviceInput deviceInputWithDevice:videoDevice error:nil];_currentVideoDeviceInput = videoDeviceInput;// 5.创建对应音频设备输入对象AVCaptureDeviceInput *audioDeviceInput = [AVCaptureDeviceInput deviceInputWithDevice:audioDevice error:nil];// 6.添加到会话中// 注意“最好要判断是否能添加输入,会话不能添加空的// 6.1 添加视频if ([captureSession canAddInput:videoDeviceInput]) {[captureSession addInput:videoDeviceInput];}// 6.2 添加音频if ([captureSession canAddInput:audioDeviceInput]) {[captureSession addInput:audioDeviceInput];}// 7.获取视频数据输出设备AVCaptureVideoDataOutput *videoOutput = [[AVCaptureVideoDataOutput alloc] init];// 7.1 设置代理,捕获视频样品数据// 注意:队列必须是串行队列,才能获取到数据,而且不能为空dispatch_queue_t videoQueue = dispatch_queue_create("Video Capture Queue", DISPATCH_QUEUE_SERIAL);[videoOutput setSampleBufferDelegate:self queue:videoQueue];if ([captureSession canAddOutput:videoOutput]) {[captureSession addOutput:videoOutput];}// 8.获取音频数据输出设备AVCaptureAudioDataOutput *audioOutput = [[AVCaptureAudioDataOutput alloc] init];// 8.2 设置代理,捕获视频样品数据// 注意:队列必须是串行队列,才能获取到数据,而且不能为空dispatch_queue_t audioQueue = dispatch_queue_create("Audio Capture Queue", DISPATCH_QUEUE_SERIAL);[audioOutput setSampleBufferDelegate:self queue:audioQueue];if ([captureSession canAddOutput:audioOutput]) {[captureSession addOutput:audioOutput];}// 9.获取视频输入与输出连接,用于分辨音视频数据_videoConnection = [videoOutput connectionWithMediaType:AVMediaTypeVideo];// 10.添加视频预览图层AVCaptureVideoPreviewLayer *previedLayer = [AVCaptureVideoPreviewLayer layerWithSession:captureSession];previedLayer.frame = [UIScreen mainScreen].bounds;[self.view.layer insertSublayer:previedLayer atIndex:0];_previedLayer = previedLayer;// 11.启动会话[captureSession startRunning];
}// 指定摄像头方向获取摄像头
- (AVCaptureDevice *)getVideoDevice:(AVCaptureDevicePosition)position
{NSArray *devices = [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo];for (AVCaptureDevice *device in devices) {if (device.position == position) {return device;}}return nil;
}#pragma mark - AVCaptureVideoDataOutputSampleBufferDelegate
// 获取输入设备数据,有可能是音频有可能是视频
- (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection
{if (_videoConnection == connection) {NSLog(@"采集到视频数据");} else {NSLog(@"采集到音频数据");}
}
视频采集额外功能一(切换摄像头)
- 切换摄像头步骤
1.
获取当前视频设备输入对象2.
判断当前视频设备是前置还是后置3.
确定切换摄像头的方向4.
根据摄像头方向获取对应的摄像头设备5.
创建对应的摄像头输入对象6.
从会话中移除之前的视频输入对象7.
添加新的视频输入对象到会话中
// 切换摄像头
- (IBAction)toggleCapture:(id)sender {// 获取当前设备方向AVCaptureDevicePosition curPosition = _currentVideoDeviceInput.device.position;// 获取需要改变的方向AVCaptureDevicePosition togglePosition = curPosition == AVCaptureDevicePositionFront?AVCaptureDevicePositionBack:AVCaptureDevicePositionFront;// 获取改变的摄像头设备AVCaptureDevice *toggleDevice = [self getVideoDevice:togglePosition];// 获取改变的摄像头输入设备AVCaptureDeviceInput *toggleDeviceInput = [AVCaptureDeviceInput deviceInputWithDevice:toggleDevice error:nil];// 移除之前摄像头输入设备[_captureSession removeInput:_currentVideoDeviceInput];// 添加新的摄像头输入设备[_captureSession addInput:toggleDeviceInput];// 记录当前摄像头输入设备_currentVideoDeviceInput = toggleDeviceInput;}
视频采集额外功能二(聚焦光标)
- 聚焦光标步骤
1.
监听屏幕的点击2.
获取点击的点位置,转换为摄像头上的点,必须通过视频预览图层(AVCaptureVideoPreviewLayer
)转3.
设置聚焦光标图片的位置,并做动画4.
设置摄像头设备聚焦模式和曝光模式(注意:这里设置一定要锁定配置lockForConfiguration
,否则报错)
// 点击屏幕,出现聚焦视图
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{// 获取点击位置UITouch *touch = [touches anyObject];CGPoint point = [touch locationInView:self.view];// 把当前位置转换为摄像头点上的位置CGPoint cameraPoint = [_previedLayer captureDevicePointOfInterestForPoint:point];// 设置聚焦点光标位置[self setFocusCursorWithPoint:point];// 设置聚焦[self focusWithMode:AVCaptureFocusModeAutoFocus exposureMode:AVCaptureExposureModeAutoExpose atPoint:cameraPoint];
}/*** 设置聚焦光标位置** @param point 光标位置*/
-(void)setFocusCursorWithPoint:(CGPoint)point{self.focusCursorImageView.center=point;self.focusCursorImageView.transform=CGAffineTransformMakeScale(1.5, 1.5);self.focusCursorImageView.alpha=1.0;[UIView animateWithDuration:1.0 animations:^{self.focusCursorImageView.transform=CGAffineTransformIdentity;} completion:^(BOOL finished) {self.focusCursorImageView.alpha=0;}];
}/*** 设置聚焦*/
-(void)focusWithMode:(AVCaptureFocusMode)focusMode exposureMode:(AVCaptureExposureMode)exposureMode atPoint:(CGPoint)point{AVCaptureDevice *captureDevice = _currentVideoDeviceInput.device;// 锁定配置[captureDevice lockForConfiguration:nil];// 设置聚焦if ([captureDevice isFocusModeSupported:AVCaptureFocusModeAutoFocus]) {[captureDevice setFocusMode:AVCaptureFocusModeAutoFocus];}if ([captureDevice isFocusPointOfInterestSupported]) {[captureDevice setFocusPointOfInterest:point];}// 设置曝光if ([captureDevice isExposureModeSupported:AVCaptureExposureModeAutoExpose]) {[captureDevice setExposureMode:AVCaptureExposureModeAutoExpose];}if ([captureDevice isExposurePointOfInterestSupported]) {[captureDevice setExposurePointOfInterest:point];}// 解锁配置[captureDevice unlockForConfiguration];
}
结束语
后续还会更新更多有关直播的资料,希望做到教会每一个朋友从零开始做一款直播app,并且Demo也会慢慢完善.
Demo点击下载
- 由于FFMPEG库比较大,大概100M。
- 本来想自己上传所有代码了,上传了1个小时,还没成功,就放弃了。
- 提供另外一种方案,需要你们自己导入IJKPlayer库
具体步骤:
- 下载Demo后,打开YZLiveApp.xcworkspace问题

- pod install就能解决

- 下载jkplayer库,点击下载
- 把jkplayer直接拖入到与Classes同一级目录下,直接运行程序,就能成功了

- 注意不需要
打开工程,把jkplayer拖入到工程中
,而是直接把jkplayer库拷贝到与Classes同一级目录下就可以了。 - 错误示范:
不要向下面这样操作

作者:袁峥
链接:https://www.jianshu.com/p/c71bfda055fa
來源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
相关文章:

easyui 报表合并单元格
前段时间工作中碰到有需求,要求数据按下图所示格式来显示,当时在园子里看到了一篇文章(时间久了,想不起是哪一篇),研究了后做出了如下的DEMO,在此当作学习笔记,简单记录一下。 首先是…

HDU2594 KMP next数组的应用
这道题就是给你两个串s1, s2让你求出s1 s2的最长相同前缀和后缀, 我们直接将s1 s2连接到一起然后处理一下next数组即可, 注意答案应该是min(len(s1), len(s2) , next[len]), 代码如下: #include <cstdio> #include <cstring> #in…

c语言中浮点数和整数转换_C中的数据类型-整数,浮点数和空隙说明
c语言中浮点数和整数转换C中的数据类型 (Data Types in C) There are several different ways to store data in C, and they are all unique from each other. The types of data that information can be stored as are called data types. C is much less forgiving about d…

【如何快速的开发一个完整的iOS直播app】(美颜篇)
前言在看这篇之前,如果您还不了解直播原理,请查看这篇文章如何快速的开发一个完整的iOS直播app(原理篇)开发一款直播app,美颜功能是很重要的,如果没有美颜功能,可能分分钟钟掉粉千万,本篇主要讲解直播中美颜…

Linux内核分析——第五章 系统调用
第五章 系统调用 5.1 与内核通信 1、系统调用在用户空间进程和硬件设备之间添加了一个中间层,该层主要作用有三个: (1)为用户空间提供了一种硬件的抽象接口 (2)系统调用保证了系统的稳定和安全 (…

BZOJ 3110
http://www.lydsy.com/JudgeOnline/problem.php?id3110 整体二分区间修改树状数组维护 #include<cstdio> #define FOR(i,s,t) for(register int is;i<t;i) inline int max(int a,int b){return a>b?a:b;} inline int min(int a,int b){return a<b?a:b;} type…

css 选择器 伪元素_CSS伪元素-解释选择器之前和之后
css 选择器 伪元素选择器之前 (Before Selector) The CSS ::before selector can be used to insert content before the content of the selected element or elements. It is used by attaching ::before to the element it is to be used on.CSS ::before选择器可用于在选定…

各种面试题啊1
技术 基础 1.为什么说Objective-C是一门动态的语言? 什么叫动态静态 静态、动态是相对的,这里动态语言指的是不需要在编译时确定所有的东西,在运行时还可以动态的添加变量、方法和类 Objective-C 可以通过Runtime 这个运行时机制,…

PEP8 Python
写在前面 对于代码而言,相比于写,它更多是读的。 pep8 一、代码编排 缩进,4个空格的缩进,编辑器都可以完成此功能;每行最大长度79,换行可以使用反斜杠,换行点要在操作符的后边。类和top-level函…

粒子滤波 应用_如何使用NativeScript开发粒子物联网应用
粒子滤波 应用If youre developing any type of IoT product, inevitably youll need some type of mobile app. While there are easy ways, theyre not for production use.如果您要开发任何类型的物联网产品,则不可避免地需要某种类型的移动应用程序。 尽管有简单…

wkwebView基本使用方法
WKWebView有两个delegate,WKUIDelegate 和 WKNavigationDelegate。WKNavigationDelegate主要处理一些跳转、加载处理操作,WKUIDelegate主要处理JS脚本,确认框,警告框等。因此WKNavigationDelegate更加常用。 比较常用的方法: #p…

引用类型(一):Object类型
对象表示方式 1、第一种方式:使用new操作符后跟Object构造函数 var person new Object();<br/> person.name Nicholas;<br/> person.age 29; 2、对象字面量表示法 var person {name:Nicholas,age:29 } *:在age属性的值29的后面不能添加逗号…

(第四周)要开工了
忙碌的一周又过去了,这周的时间很紧,但是把时间分配的比较均匀,考研复习和各门功课都投入了一定的精力,所以不像前三周一样把大多数时间都花费在了软件工程上。也因为结对项目刚开始,我们刚刚进行任务分工以及查找资料…

统计数字,空白符,制表符_为什么您应该在HTML中使用制表符空间而不是多个非空白空间(nbsp)...
统计数字,空白符,制表符There are a number of ways to insert spaces in HTML. The easiest way is by simply adding spaces or multiple character entities before and after the target text. Of course, that isnt the DRYest method.有多种方法可以在HTML中插入空格。…

Python20-Day02
1、数据 数据为什么要分不同的类型 数据是用来表示状态的,不同的状态就应该用不同类型的数据表示; 数据类型 数字(整形,长整形,浮点型,复数),字符串,列表,元组…

Android网络框架-OkHttp3.0总结
一、概述 OkHttp是Square公司开发的一款服务于android的一个网络框架,主要包含: 一般的get请求一般的post请求基于Http的文件上传文件下载加载图片支持请求回调,直接返回对象、对象集合支持session的保持github地址:https://githu…

第一天写,希望能坚持下去。
该想的都想完了,不该想的似乎也已经尘埃落定了。有些事情,终究不是靠努力或者不努力获得的。顺其自然才是正理。 以前很多次想过要努力,学习一些东西,总是不能成,原因很多: 1.心中烦恼,不想学…

mac gource_如何使用Gource显示项目的时间表
mac gourceThe first time I heard about Gource was in 2013. At the time I watched this cool video showing Ruby on Rails source code evolution:我第一次听说Gource是在2013年 。 当时,我观看了这段很酷的视频,展示了Ruby on Rails源代码的演变&a…

insert语句让我学会的两个MySQL函数
我们要保存数据到数据库,插入数据是必须的,但是在业务中可能会出于某种业务要求,要在数据库中设计唯一索引;这时如果不小心插入一条业务上已经存在同样key的数据时,就会出现异常。 大部分的需求要求我们出现唯一键冲突…

对PInvoke函数函数调用导致堆栈不对称。原因可能是托管的 PInvoke 签名与非托管的目标签名不匹配。...
C#引入外部非托管类库时,有时候会出现“对PInvoke函数调用导致堆栈不对称。原因可能是托管的 PInvoke 签名与非托管的目标签名不匹配”的报错。 通常在DllImport标签内加入属性CallingConventionCallingConvention.Cdecl即可解决该问题。 如: [Dll…

Python字符串方法用示例解释
字符串查找方法 (String Find Method) There are two options for finding a substring within a string in Python, find() and rfind().在Python中的字符串中有两个选项可以找到子字符串: find()和rfind() 。 Each will return the position that the substring …

关于命名空间namespace
虽然任意合法的PHP代码都可以包含在命名空间中,但只有以下类型的代码受命名空间的影响,它们是:类(包括抽象类和traits)、接口、函数和常量。在声明命名空间之前唯一合法的代码是用于定义源文件编码方式的 declare 语句…

一 梳理 从 HDFS 到 MR。
MapReduce 不仅仅是一个工具,更是一个框架。我们必须拿问题解决方案去适配框架的 map 和 reduce 过程很多情况下,需要关注 MapReduce 作业所需要的系统资源,尤其是集群内部网络资源的使用情况。这是MapReduce 框架在设计上的取舍,…

huffman树和huffman编码
不知道为什么,我写的代码都是又臭又长。 直接上代码: #include <iostream> #include <cstdarg> using namespace std; class Node{ public:int weight;int parent, lChildren, rChildren;Node(int weight, int parent, int lChildren, int …

react 监听组合键_投资组合中需要的5个React项目
react 监听组合键Youve put in the work and now you have a solid understanding of the React library.您已经完成工作,现在对React库有了扎实的了解。 On top of that, you have a good grasp of JavaScript and are putting its most helpful features to use …

Unity 单元测试(PLUnitTest工具)
代码测试的由来 上几个星期上面分配给我一个装备系统,我经过了几个星期的战斗写完90%的代码. 后来策划告诉我需求有一定的改动,我就随着策划的意思修改了代码. 但是测试(Xu)告诉我装备系统很多功能都用不上了. Xu: 我有300多项测试用例,现在有很多项都无法运行了. 你修改了部分…

Best Time to Buy and Sell Stock II
题目: Say you have an array for which the i th element is the price of a given stock on day i. Design an algorithm to find the maximum profit. You may complete as many transactions as you like (ie, buy one and sell one share of the stock multipl…

求给定集合的幂集
数据结构中说这个问题可以用类似8皇后的状态树解法。 把求解过程看成是一棵二叉树,空集作为root,然后遍历给定集合中的元素,左子树表示取该元素,右子树表示舍该元素。 然后,root的左右元素分别重复上述过程。 就形成…

angular 命令行项目_Angular命令行界面介绍
angular 命令行项目Angular is closely associated with its command-line interface (CLI). The CLI streamlines generation of the Angular file system. It deals with most of the configuration behind the scenes so developers can start coding. The CLI also has a l…