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

开发一款直播app,首先需要采集主播的视频和音频,然后传入流媒体服务器,本篇主要讲解如何采集主播的视频和音频,当前可以切换前置后置摄像头和焦点光标,但是美颜功能还没做,可以看见素颜的你,后续还会有直播的其他功能文章陆续发布。

基本知识介绍

  • 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同一级目录下就可以了。
  • 错误示范:不要向下面这样操作

如何快速的开发一个完整的iOS直播app(采集篇)的更多相关文章

  1. 如何快速的开发一个完整的iOS直播app(美颜篇)

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

  2. 如何快速的开发一个完整的iOS直播app(原理篇)

    目录 [如何快速的开发一个完整的iOS直播app](原理篇) [如何快速的开发一个完整的iOS直播app](播放篇) [如何快速的开发一个完整的iOS直播app](采集篇) 前言 大半年没写博客了,但 ...

  3. 【如何快速的开发一个完整的iOS直播app】(美颜篇)

    原文转自:袁峥Seemygo    感谢分享.自我学习 前言 在看这篇之前,如果您还不了解直播原理,请查看这篇文章如何快速的开发一个完整的iOS直播app(原理篇) 开发一款直播app,美颜功能是很重 ...

  4. 【如何快速的开发一个完整的iOS直播app】(采集篇)

    原文转自:袁峥Seemygo    感谢分享.自我学习 前言 在看这篇之前,如果您还不了解直播原理,请查看这篇文章如何快速的开发一个完整的iOS直播app(原理篇) 开发一款直播app,首先需要采集主 ...

  5. 【如何快速的开发一个完整的iOS直播app】(播放篇)

    原文转自:袁峥Seemygo    感谢分享.自我学习 前言 在看这篇之前,如果您还不了解直播原理,请查看上篇文章如何快速的开发一个完整的iOS直播app(原理篇) 开发一款直播app,集成ijkpl ...

  6. 【如何快速的开发一个完整的iOS直播app】(推流篇)

    前言 在看这篇之前,如果您还不了解直播原理,请查看这篇文章如何快速的开发一个完整的iOS直播app(原理篇) 开发一款直播app,肯定需要流媒体服务器,本篇主要讲解直播中流媒体服务器搭建,并且讲解了如 ...

  7. 【如何快速的开发一个完整的 iOS 直播 app】(美颜篇)

    来源:袁峥Seemygo 链接:http://www.jianshu.com/p/4646894245ba 前言 在看这篇之前,如果您还不了解直播原理,请查看这篇文章如何快速的开发一个完整的iOS直播 ...

  8. 【如何快速的开发一个完整的iOS直播app】(原理篇)

    原文转自:袁峥Seemygo    感谢分享.自我学习 目录 [如何快速的开发一个完整的iOS直播app](原理篇) [如何快速的开发一个完整的iOS直播app](播放篇) [如何快速的开发一个完整的 ...

  9. 【如何快速的开发一个简单的iOS直播app】(代码篇)

    开篇([如何快速的开发一个完整的iOS直播app](原理篇)) 好久没写简书,因为好奇的我跑去学习直播了,今天就分享一下我的感慨. 目前为止直播还是比较热点的技术的,简书,git上有几篇阅读量和含金量 ...

  10. 抖音短视频爆火的背后到底是什么——如何快速的开发一个完整的直播app

    前言 今年移动直播行业的兴起,诞生了一大批网红,甚至明星也开始直播了,因此不得不跟上时代的步伐,由于第一次接触的原因,因此花了很多时间了解直播,今天我来教你从零开始搭建一个完整的直播app,希望能帮助 ...

随机推荐

  1. C++ STL之map、multimap

    map和multimap是C++ STL(Standard Template Library)中的关联容器,它们提供键值对的存储和访问. map是一个有序关联容器,它存储一组键值对,其中每个键都是唯一 ...

  2. C++递归创建文件夹

    根据传入的参数递归进行目录的创建. 函数描述: 递归创建目录. 入参: 所要创建的目录. 返回值: 创建成功,返回TRUE:否则返回FALSE. 1 BOOL CreateDirTree(LPCTST ...

  3. 盘点Air780E的FTP应用,你了解吗?

    ​ 一.FTP 概述 FTP(File Transfer Protocol,文件传输协议) 是 TCP/IP 协议组中的协议之一. FTP协议包括两个组成部分,其一为FTP服务器,其二为FTP客户端. ...

  4. 关于Requests交互超时的处理方式

    拿 https://aes.cryptohack.org/ecb_oracle 这道题来做示范. 由于这个链接的本地服务器在外国,因此我们很容易就会连接超时. 如下一个脚本: import reque ...

  5. 1.TP6的入门-安装

    打开官网,找到这里点击手册 或者直接访问 这里 可以看到TP6已经有了赞助商 然后往后面阅读,发现他推荐我们读这个 这个入门必读还是不错的,简单的看看就行 后面就开始安装吧 首先注意自己的环境php版 ...

  6. CodeForces - 1353D Constructing the Array

    CodeForces - 1353D 这道题也可能比较简单,主要是要想到优先队列要怎么使用,这一点如果用递归会写不了 但是因为对优先队列不太熟悉,只有被提示可以用优先队列才想到要怎么用, 还是很重要的 ...

  7. .NET Core 线程(Thread)底层原理浅谈

    简介 线程,进程,协程基本概念不再赘述. 原生线程和用户线程 原生线程 在内核态中创建的线程,只服务于内核态 用户线程 由User Application创建的线程,该线程会在内核态与用户态中间来回穿 ...

  8. Mobexterm 串口换行错误

    问题描述 使用 Mobexterm 连接串口时,在回显中存在换行错误问题,严重影响阅读串口数据. 解决方法 在左侧操作窗口中右击发生 换行错误问题 的窗口,比如我需要右击 COM9 右击后,在下拉列表 ...

  9. golang之命令行工具Cobra

    github地址: https://github.com/spf13/cobra [安装] go get -u github.com/spf13/cobra@latest 使用cobra_cli工具 ...

  10. 【集成-Jedis】SpringBoot集成Jedis

    将jedis的依赖放进Maven <dependency> <groupId>redis.clients</groupId> <artifactId>j ...