开始
之前一直做直播方面的开发, 不过一直没机会接触一些推流和播放器的技术, 所以业余时间自己去写了一下推流器模块的 Demo.
自己也总结了一些常用的知识:
过程
模块构成
我将推流器看成五块知识点, 分别是: 采集, 处理, 编码, 封包, 推流.
采集模块
采集是将机器获得的图片和视频转成一帧帧图像数据返回到开发者手中.
以下是采集模块一些知识点
yuv,rgb,ycbcr 的颜色空间模型知识点:
硬知识点, 请参考:
iOS 采集知识点
iOS 下通过硬件采集到的图像数据会用 CMSampleBufferRef 来保存回调到使用者手中. CMSampleBufferRef 结构如下:(音频: 结构体内的 CVPixelBuffer 替换成 CMBlockBuffer),CVPixelBuffer 就是 RGB/YUV/YCBCR 的数据.
GPUImage 知识点
源码: https://github.com/BradLarson/GPUImage
GPUImage 是 iOS 上基于 opengl 的图像, 视频处理开源框架, 里面带有大量的滤镜, 同时也很方便在原有基础上加入自己的滤镜 Filter, 所有滤镜是基于 opengl shader 实现的, 所以滤镜效果图像处理是在 GPU 上实现的, 处理效率比较高.
GPUImage 基本架构是 chain 式结构, 主要由一个 GPUImageOutput interface 和一个 GPUImageInput protocol 串联起来, GPUImageOutput 输出 Texture,GPUImageInput 输入 Texture, 整个链式图像数据传递由 Texture 负责. camera,stillimage 等图像, 视频 sources 继承自 GPUImageOutput, 滤镜 Filters 继承自 GPUImageOutput 并实现 GPUImageInput,View,FileWriter 等 Outputs 实现 GPUImageInput.
处理模块
OpenGL ES 地址: https://www.khronos.org/opengles/
Metal 地址:
Metal 是一个和 OpenGL ES 类似的面向底层的图形编程接口, 通过使用相关的 API 可以直接操作 GPU , 最早在 2014 年的 WWDC 的时候发布, 并于今年发布了 Metal 2, 是苹果的一个亲儿子.
Metal 框架支持 GPU 硬件加速, 高级 3D 图形渲染以及大数据并行运算. 且提供了先进而精简的 API 来确保框架的细粒度(fine-grain), 并且在组织架构, 程序处理, 图形呈现, 运算指令以及指令相关数据资源的管理上都支持底层控制. 其核心目的是尽可能的减少 CPU 开销, 而将运行时产生的大部分负载交由 GPU 承担.
- (以后需要继续研究 Metal)
- GPUImageFilter
源码: https://github.com/BradLarson/GPUImage
可以参考: https://www.jianshu.com/p/4687880695a4
编码知识点
最广泛最常用的格式 H264
I 帧
帧内编码帧 ,I 帧表示关键帧, 你可以理解为这一帧画面的完整保留; 解码时只需要本帧数据就可以完成(因为包含完整画面)
帧内编码用来缩减图像的空间冗余. 为了提高 H.264 帧内编码的效率, 在给定帧中充分利用相邻宏块的空间相关性, 相邻的宏块通常含有相似的属性. 因此, 在对一给定宏块编码时, 首先可以根据周围的宏块预测(典型的是根据左上角宏块, 左边宏块和上面宏块, 因为此宏块已经被编码处理), 然后对预测值与实际值的差值进行编码, 这样, 相对于直接对该帧编码而言, 可以大大减小码率.
H.264 提供 9 种模式进行 4*4 像素宏块预测, 包括 1 种直流预测和 8 种方向预测. 在图中, 相邻块的 A 到 I 共 9 个像素均已经被编码, 可以被用以预测, 如果我们选择模式 4, 那么, a,b,c,d4 个像素被预测为与 E 相等的值, e,f,g,h4 个像素被预测为与 F 相等的值, 对于图像中含有很少空间信息的平坦区, H.264 也支持 16*16 的帧内编码.
I 帧特点:
它是一个全帧压缩编码帧. 它将全帧图像信息进行 JPEG 压缩编码及传输;
解码时仅用 I 帧的数据就可重构完整图像;
I 帧描述了图像背景和运动主体的详情;
I 帧不需要参考其他画面而生成;
I 帧是 P 帧和 B 帧的参考帧(其质量直接影响到同组中以后各帧的质量);
I 帧是帧组 GOP 的基础帧(第一帧), 在一组中只有一个 I 帧;
I 帧不需要考虑运动矢量;
I 帧所占数据的信息量比较大.
P 帧
前向预测编码帧. P 帧表示的是这一帧跟之前的一个关键帧 (或 P 帧) 的差别, 解码时需要用之前缓存的画面叠加上本帧定义的差别, 生成最终画面.(也就是差别帧, P 帧没有完整画面数据, 只有与前一帧的画面差别的数据) P 帧的预测与重构: P 帧是以 I 帧为参考帧, 在 I 帧中找出 P 帧 "某点" 的预测值和运动矢量, 取预测差值和运动矢量一起传送. 在接收端根据运动矢量从 I 帧中找出 P 帧 "某点" 的预测值并与差值相加以得到 P 帧 "某点" 样值, 从而可得到完整的 P 帧.
P 帧特点:
P 帧是 I 帧后面相隔 1~2 帧的编码帧;
P 帧采用运动补偿的方法传送它与前面的 I 或 P 帧的差值及运动矢量(预测误差);
解码时必须将 I 帧中的预测值与预测误差求和后才能重构完整的 P 帧图像;
P 帧属于前向预测的帧间编码. 它只参考前面最靠近它的 I 帧或 P 帧;
P 帧可以是其后面 P 帧的参考帧, 也可以是其前后的 B 帧的参考帧;
由于 P 帧是参考帧, 它可能造成解码错误的扩散;
由于是差值传送, P 帧的压缩比较高.
B 帧
双向预测内插编码帧. B 帧是双向差别帧, 也就是 B 帧记录的是本帧与前后帧的差别(具体比较复杂, 有 4 种情况, 但我这样说简单些), 换言之, 要解码 B 帧, 不仅要取得之前的缓存画面, 还要解码之后的画面, 通过前后画面的与本帧数据的叠加取得最终的画面. B 帧压缩率高, 但是解码时 CPU 会比较累. 所以移动端一般不解压 B 帧.
B 帧的预测与重构 B 帧以前面的 I 或 P 帧和后面的 P 帧为参考帧,"找出"B 帧 "某点" 的预测值和两个运动矢量, 并取预测差值和运动矢量传送. 接收端根据运动矢量在两个参考帧中 "找出(算出)" 预测值并与差值求和, 得到 B 帧 "某点" 样值, 从而可得到完整的 B 帧.
B 帧特点
B 帧是由前面的 I 或 P 帧和后面的 P 帧来进行预测的;
B 帧传送的是它与前面的 I 或 P 帧和后面的 P 帧之间的预测误差及运动矢量;
B 帧是双向预测编码帧;
B 帧压缩比最高, 因为它只反映丙参考帧间运动主体的变化情况, 预测比较准确;
B 帧不是参考帧, 不会造成解码错误的扩散.
VideoToolBox
iOS8 以上才支持, 硬编码库.
- VideoToolBox:
- FFMPEG
iOS 暂时支持软编码, 不过软编码适配比较多格式和机型.
## 推流知识点
Rtmp 协议
是 Adobe 的一个没有完全公开的基于 TCP 上的协议.
优点: 延迟低, 正常来说 3s 左右. 而且支持加密. 缺点: iOS, 安卓来说需要另外支持的播放器来播放, H5 也没办法直接播放 Rtmp 协议的, 所以很多 H5 也会用 HLS 来播放. 还有一点是 Rtmp 基于 TCP 协议, 所以 RTMP 会累积延时, 所以当累积到一段时候需要清除延时.
协议: https://www.adobe.com/devnet/rtmp.html
HLS 协议和 HTTP 协议
优点: 协议简单, 性能高. 缺点: 延时比较高, 而且 HLS 延时是根据分块大小决定.
- Demo
- GitHub: https://github.com/KoonChaoSo/CSMixer
来源: https://juejin.im/post/5bf7cf76e51d455bfe2605b9