1小时学会:最简单的iOS直播推流(三)使用系统接口捕获音视频数据
最简单的iOS 推流代码,视频捕获,软编码(faac,x264),硬编码(aac,h264),美颜,flv编码,rtmp协议,陆续更新代码解析,你想学的知识这里都有,愿意懂直播技术的同学快来看!!
源代码:https://github.com/hardman/AWLive
通过系统相机录制视频获取音视频数据,是推流的第一步。
源码中提供2种获取音视频数据的方法:一是使用系统自带接口;二是使用GPUImage。
本篇首先介绍第一种。
网络上关于获取视频数据的代码有不少,但是为了方便代码阅读,这里简要介绍一下。
[注意]请仔细阅读代码注释
相关代码入口
整套推流代码的入口:AWAVCaptureManager,它是根据参数创建上述2种获取数据方法的一个工厂类。
可以通过设置 captureType 来决定使用哪种数据获取方式。
AWAVCaptureManager部分代码如下:
typedef enum : NSUInteger {AWAVCaptureTypeNone,AWAVCaptureTypeSystem,AWAVCaptureTypeGPUImage,
} AWAVCaptureType;@interface AWAVCaptureManager : NSObject
//视频捕获类型
@property (nonatomic, unsafe_unretained) AWAVCaptureType captureType;
@property (nonatomic, weak) AWAVCapture *avCapture;//省略其他代码
......
@end
设置了captureType之后,直接可以通过avCapture获取到正确的捕获视频数据的对象了。
AWAVCapture 是一个虚基类(c++中的说法,不会直接产生对象,只用来继承的类,java中叫做抽象类)。
它的两个子类分别是 AWSystemAVCapture 和 AWGPUImageAVCapture。
这里使用了多态。
如果 captureType设置的是 AWAVCaptureTypeSystem,avCapture获取到的真实对象就是 AWSystemAVCapture类型;
如果 captureType设置的是 AWAVCaptureTypeGPUImage,avCapture获取到的真实对象就是 AWGPUImageAVCapture类型。
AWSystemAVCapture类的功能只有一个:调用系统相机,获取音视频数据。
相机数据获取的方法
分为3步骤:
1. 初始化输入输出设备。
2. 创建AVCaptureSession,用来管理视频与数据的捕获。
3. 创建预览UI。
还包括一些其他功能:
1. 切换摄像头
2. 更改fps
在代码中对应的是 AWSystemAVCapture中的 onInit方法。只要初始化就会调用。
【注意】请仔细阅读下文代码中的注释
初始化输入设备
-(void) createCaptureDevice{// 初始化前后摄像头// 执行这几句代码后,系统会弹框提示:应用想要访问您的相机。请点击同意// 另外iOS10 需要在info.plist中添加字段NSCameraUsageDescription。否则会闪退,具体请自行baidu。NSArray *videoDevices = [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo];self.frontCamera = [AVCaptureDeviceInput deviceInputWithDevice:videoDevices.firstObject error:nil];self.backCamera =[AVCaptureDeviceInput deviceInputWithDevice:videoDevices.lastObject error:nil];// 初始化麦克风// 执行这几句代码后,系统会弹框提示:应用想要访问您的麦克风。请点击同意// 另外iOS10 需要在info.plist中添加字段NSMicrophoneUsageDescription。否则会闪退,具体请自行baidu。AVCaptureDevice *audioDevice = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeAudio];self.audioInputDevice = [AVCaptureDeviceInput deviceInputWithDevice:audioDevice error:nil];//省略其他代码...
}
初始化输出设备
-(void) createOutput{ //创建数据获取线程dispatch_queue_t captureQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);//视频数据输出self.videoDataOutput = [[AVCaptureVideoDataOutput alloc] init];//设置代理,需要当前类实现protocol:AVCaptureVideoDataOutputSampleBufferDelegate[self.videoDataOutput setSampleBufferDelegate:self queue:captureQueue];//抛弃过期帧,保证实时性[self.videoDataOutput setAlwaysDiscardsLateVideoFrames:YES];//设置输出格式为 yuv420[self.videoDataOutput setVideoSettings:@{(__bridge NSString *)kCVPixelBufferPixelFormatTypeKey:@(kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange)}];//音频数据输出self.audioDataOutput = [[AVCaptureAudioDataOutput alloc] init];//设置代理,需要当前类实现protocol:AVCaptureAudioDataOutputSampleBufferDelegate[self.audioDataOutput setSampleBufferDelegate:self queue:captureQueue];// AVCaptureVideoDataOutputSampleBufferDelegate 和 AVCaptureAudioDataOutputSampleBufferDelegate 回调方法名相同都是:// captureOutput:(AVCaptureOutput *)captureOutput didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection// 最终视频和音频数据都可以在此方法中获取。
}
创建 captureSession
// AVCaptureSession 创建逻辑很简单,它像是一个中介者,从音视频输入设备获取数据,处理后,传递给输出设备(数据代理/预览layer)。
-(void) createCaptureSession{//初始化self.captureSession = [AVCaptureSession new];//修改配置[self.captureSession beginConfiguration];//加入视频输入设备if ([self.captureSession canAddInput:self.videoInputDevice]) {[self.captureSession addInput:self.videoInputDevice];}//加入音频输入设备if ([self.captureSession canAddInput:self.audioInputDevice]) {[self.captureSession addInput:self.audioInputDevice];}//加入视频输出if([self.captureSession canAddOutput:self.videoDataOutput]){[self.captureSession addOutput:self.videoDataOutput];[self setVideoOutConfig];}//加入音频输出if([self.captureSession canAddOutput:self.audioDataOutput]){[self.captureSession addOutput:self.audioDataOutput];}//设置预览分辨率//这个分辨率有一个值得注意的点://iphone4录制视频时 前置摄像头只能支持 480*640 后置摄像头不支持 540*960 但是支持 720*1280//诸如此类的限制,所以需要写一些对分辨率进行管理的代码。//目前的处理是,对于不支持的分辨率会抛出一个异常//但是这样做是不够、不完整的,最好的方案是,根据设备,提供不同的分辨率。//如果必须要用一个不支持的分辨率,那么需要根据需求对数据和预览进行裁剪,缩放。if (![self.captureSession canSetSessionPreset:self.captureSessionPreset]) {@throw [NSException exceptionWithName:@"Not supported captureSessionPreset" reason:[NSString stringWithFormat:@"captureSessionPreset is [%@]", self.captureSessionPreset] userInfo:nil];}self.captureSession.sessionPreset = self.captureSessionPreset;//提交配置变更[self.captureSession commitConfiguration];//开始运行,此时,CaptureSession将从输入设备获取数据,处理后,传递给输出设备。[self.captureSession startRunning];
}
创建预览UI
// 其实只有一句代码:CALayer layer = [AVCaptureVideoPreviewLayer layerWithSession:self.captureSession];
// 它其实是 AVCaptureSession的一个输出方式而已。
// CaptureSession会将从input设备得到的数据,处理后,显示到此layer上。
// 我们可以将此layer变换后加入到任意UIView中。
-(void) createPreviewLayer{self.previewLayer = [AVCaptureVideoPreviewLayer layerWithSession:self.captureSession];self.previewLayer.frame = self.preview.bounds;[self.preview.layer addSublayer:self.previewLayer];
}
切换摄像头
-(void)setVideoInputDevice:(AVCaptureDeviceInput *)videoInputDevice{if ([videoInputDevice isEqual:_videoInputDevice]) {return;}//captureSession 修改配置[self.captureSession beginConfiguration];//移除当前输入设备if (_videoInputDevice) {[self.captureSession removeInput:_videoInputDevice];}//增加新的输入设备if (videoInputDevice) {[self.captureSession addInput:videoInputDevice];}//提交配置,至此前后摄像头切换完毕[self.captureSession commitConfiguration];_videoInputDevice = videoInputDevice;
}
设置fps
-(void) updateFps:(NSInteger) fps{//获取当前capture设备NSArray *videoDevices = [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo];//遍历所有设备(前后摄像头)for (AVCaptureDevice *vDevice in videoDevices) {//获取当前支持的最大fpsfloat maxRate = [(AVFrameRateRange *)[vDevice.activeFormat.videoSupportedFrameRateRanges objectAtIndex:0] maxFrameRate];//如果想要设置的fps小于或等于做大fps,就进行修改if (maxRate >= fps) {//实际修改fps的代码if ([vDevice lockForConfiguration:NULL]) {vDevice.activeVideoMinFrameDuration = CMTimeMake(10, (int)(fps * 10));vDevice.activeVideoMaxFrameDuration = vDevice.activeVideoMinFrameDuration;[vDevice unlockForConfiguration];}}}
}
获取音视频数据
-(void)captureOutput:(AVCaptureOutput *)captureOutput didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection{if (self.isCapturing) {if ([self.videoDataOutput isEqual:captureOutput]) {//捕获到视频数据,通过sendVideoSampleBuffer发送出去,后续文章会解释接下来的详细流程。[self sendVideoSampleBuffer:sampleBuffer];}else if([self.audioDataOutput isEqual:captureOutput]){//捕获到音频数据,通过sendVideoSampleBuffer发送出去[self sendAudioSampleBuffer:sampleBuffer];}}
}
至此,我们达到了所有目标:能够录制视频,预览,获取音视频数据,切换前后摄像头,修改捕获视频的fps。
文章列表
- 1小时学会:最简单的iOS直播推流(一)项目介绍
- 1小时学会:最简单的iOS直播推流(二)代码架构概述
- 1小时学会:最简单的iOS直播推流(三)使用系统接口捕获音视频
- 1小时学会:最简单的iOS直播推流(四)如何使用GPUImage,如何美颜
- 1小时学会:最简单的iOS直播推流(五)yuv、pcm数据的介绍和获取
- 1小时学会:最简单的iOS直播推流(六)h264、aac、flv介绍
- 1小时学会:最简单的iOS直播推流(七)h264/aac 硬编码
- 1小时学会:最简单的iOS直播推流(八)h264/aac 软编码
- 1小时学会:最简单的iOS直播推流(九)flv 编码与音视频时间戳同步
- 1小时学会:最简单的iOS直播推流(十)librtmp使用介绍
- 1小时学会:最简单的iOS直播推流(十一)sps&pps和AudioSpecificConfig介绍(完结)
相关文章:
什么是bower
Bower是一个客户端技术的软件包管理器,它可用于搜索、安装和卸载如JavaScript、HTML、CSS之类的网络资源。其他一些建立在Bower基础之上的开发工具,如YeoMan和Grunt,这个会在以后的文章中介绍。 准备工作 安装node环境:node.js安装Git&#x…

ES6中export及export default的区别
在ES6中,export和export default均可用于导出常量、函数、文件、模块等,你可以在其他文件或模块中通过import (常量 | 函数 | 文件 | 模块)名的方式将其导入,以便能够对其进行使用,但在一个文件或模块中,export、impo…

sm2加密算法实例_实例说明加密算法
sm2加密算法实例Cryptography, at its most basic, is the science of using codes and ciphers to protect messages. 密码学从根本上讲就是使用代码和密码保护消息的科学。 Encryption is encoding messages with the intent of only allowing the intended recipient to un…

git---远程仓库版本回滚
开发中,发现有错误版本提交带远程分支master,怎么处理? 1 简介 最近在使用git时遇到了远程分支需要版本回滚的情况,于是做了一下研究,写下这篇博客。 2 问题 如果提交了一个错误的版本,怎么回退版本&#x…

1小时学会:最简单的iOS直播推流(四)如何使用GPUImage,如何美颜
最简单的iOS 推流代码,视频捕获,软编码(faac,x264),硬编码(aac,h264),美颜,flv编码,rtmp协议,陆续更新代码解析,你想学的知识这里都有…

团队任务四(无图)
任务要求: WBS练习对团队项目进行任务分解要求所有人共同参与队长列出需求成员进行估计队长领导大家达成共识形成团队报告,发至团队博客项目分解: 一、手机监控(24h) (1)手机当前运行程序监控(用以观察用户…

react测试组件_测试驱动的开发,功能和React组件
react测试组件This article is part of my studies on how to build sustainable and consistent software. In this post, we will talk about the thinking behind the testing driven development and how to apply this knowledge to simple functions, web accessibility,…

CDOJ 1073 线段树 单点更新+区间查询 水题
H - 秋实大哥与线段树Time Limit:1000MS Memory Limit:65535KB 64bit IO Format:%lld & %llu Submit Status Practice UESTC 1073Appoint description: System Crawler (2016-04-24)Description “学习本无底,前进莫徬徨。” 秋实大哥对一旁玩手机的学…

1小时学会:最简单的iOS直播推流(五)yuv、pcm数据的介绍和获取
最简单的iOS 推流代码,视频捕获,软编码(faac,x264),硬编码(aac,h264),美颜,flv编码,rtmp协议,陆续更新代码解析,你想学的知识这里都有…

beta冲刺第一天
1、今天解决的进度 成员进度陈家权回复界面设计,由于成员变动加上和其他成员距离较远,服务器404赖晓连改进Alpha版本页面没能及时更新的问题雷晶获取提问问题时间更新到数据库林巧娜今天的任务是夜间模式功能块,没有完成,查找了很…

angular绑定数据_Angular中的数据绑定说明
angular绑定数据数据绑定 (Data Binding) 动机 (Motivation) Data often defines the look of an application. Interpreting that data into the user interface involves class logic (.component.html) and a template view (.component.ts) . Angular connects them throug…

WPF判断两个时间大小避免误差
进行查询操作的时候,经常用到判断开始时间和结束时间大小的条件,由于从控件上获取的时间除了年月日时分秒,还包括毫秒、微秒等,导致直接判断时间大小的时候会产生一些误差,如下: 结果分析:年月日…

1小时学会:最简单的iOS直播推流(六)h264、aac、flv介绍
最简单的iOS 推流代码,视频捕获,软编码(faac,x264),硬编码(aac,h264),美颜,flv编码,rtmp协议,陆续更新代码解析,你想学的知识这里都有…

分享一款Markdown的css样式
使用 本样式在这个样式的基础上做了一些修改, 主要是对于表格和代码块以及一些细节的修改。 主要目的是用在chrome的扩展 Markdown Preview Plus中, 替换其内置的样式。 由于 Markdown Preview Plus对css文件大大小有要求(小于8K)…

远程桌面怎么持续连接_如何拥有成功且可持续的远程产品管理职业
远程桌面怎么持续连接Remote work is rapidly growing in all industries. Some professionals might try to push away this new way of working, seeing it as simply a current necessity. They might not think its fit for a product manager who’s constantly managing …

1小时学会:最简单的iOS直播推流(七)h264/aac 硬编码
最简单的iOS 推流代码,视频捕获,软编码(faac,x264),硬编码(aac,h264),美颜,flv编码,rtmp协议,陆续更新代码解析,你想学的知识这里都有…

Linux日常命令记录
1、查找进程 ps -ef | grep javajps 2、杀死进程 kill -9 1827 3、进入tomcat中的日志文件夹 cd logs 4、查看日志 tail -f catalina.outtail -n 10000 catalina.out 5、查看tomcat的连接数 ss -nat|grep -i "8081"|wc -lnetstat -nat | grep -i "8081" | …

【特效】移入显示移出隐藏
移入显示移出隐藏的效果也是很常见的,例如: 如果页面有有多处地方有此效果,那么也可以合并到一块,只写一段js代码,只要注意控制样式和class名字和用于js获取元素的class名字分开设置就可以了。代码很简单,用…

web前端开发最佳实践_学习前端Web开发的最佳方法
web前端开发最佳实践为什么要进行网站开发? (Why web development?) Web development is a field that is not going anywhere anytime soon. The web is moving quickly, and there are regular improvements to the devices many people use daily. Web开发是一个…

使用C#的HttpWebRequest模拟登陆网站
很久没有写新的东西了,今天在工作中遇到的一个问题,感觉很有用,有种想记下来的冲动。 这篇文章是有关模拟登录网站方面的。 实现步骤; 启用一个web会话发送模拟数据请求(POST或者GET)获取会话的CooKie 并根…

1小时学会:最简单的iOS直播推流(番外)运行不起AWLive的demo的同学请看这里
最简单的iOS 推流代码,视频捕获,软编码(faac,x264),硬编码(aac,h264),美颜,flv编码,rtmp协议,陆续更新代码解析,你想学的知识这里都有…

学习css布局
非常经典 http://zh.learnlayout.com/ float和position:absolute都是inline-block,破坏性的。absolute根据父元素定位(static父元素除外)。div也将不再是一行的块了。 position:relative自身定位。top,left是根据自己原本位置&…
csv文件示例_如何在R中使用数据框和CSV文件-带有示例的详细介绍
csv文件示例Welcome! If you want to start diving into data science and statistics, then data frames, CSV files, and R will be essential tools for you. Lets see how you can use their amazing capabilities.欢迎! 如果您想开始研究数据科学和统计学&…

1小时学会:最简单的iOS直播推流(八)h264/aac 软编码
最简单的iOS 推流代码,视频捕获,软编码(faac,x264),硬编码(aac,h264),美颜,flv编码,rtmp协议,陆续更新代码解析,你想学的知识这里都有…

003小插曲之变量和字符串
变量:赋值(名字值);变量名:字母分大小写/数字/下划线,不能以数字开头;拼接;原始字符串r; 专业优秀的名称:teacher/num/name/test/temp >>> teacher小…

mysql插入大量数据
创建实验表: CREATE TABLE a ( id int(11) NOT NULL AUTO_INCREMENT, name char(50) NOT NULL, type char(20) NOT NULL, PRIMARY KEY (id)) ENGINEInnoDB; 创建存储语句: delimiter // create procedure insertdata() begin declare i int …

十六进制190的2进制数_十六进制数系统解释
十六进制190的2进制数Hexadecimal numbers, often shortened to “hex numbers” or “hex”, are numbers represented in base 16 as opposed to base 10 that we use for everyday arithmetic and counting.十六进制数字(通常缩写为“十六进制数字”或“十六进制”)是以16为…

初学ssm框架的信息
ssm框架,就是Spring ,SpringMVC ,mybstis 的简称,我们是从mybstis 开始学起的,mybatis的作用作为一个连接数据库的框架,可以很好配置连接好数据库, 有mybatis,我们对数据库增删改查的操作更为简便了。SSM框架ÿ…

转:YUV RGB 常见视频格式解析
转: http://www.cnblogs.com/qinjunni/archive/2012/02/23/2364446.html YUV RGB 常见视频格式解析 I420是YUV格式的一种,而YUV有packed format和planar format两种,而I420属于planar format的一种。 同时I420表示了YUV的采样比例4:2:0。4…

1小时学会:最简单的iOS直播推流(十)librtmp使用介绍
最简单的iOS 推流代码,视频捕获,软编码(faac,x264),硬编码(aac,h264),美颜,flv编码,rtmp协议,陆续更新代码解析,你想学的知识这里都有…