iOS直播开播端概述

13,591 阅读8分钟

前言

  1. 好长时间没写文章,可能排版不是很美观。
  2. 此系列文章是我对自己最近所学技术的总结和沉淀,设计到的一些技术点和实现方案并不一定是最好,如有不同意见可以在评论区留言共同探讨以及学习。
  3. 此系列文章内容大都是根据我的理解进行描述,可能大白话比较多。
  4. 下面直接进入正题。

我所理解的开播

在我的理解中,开播的整个过程在技术实现上可以理解为2条(音频 & 视频)流水线。
这2条流水线可以划分为相同的5个阶段,即:采集、业务处理、渲染、编码、推流,其中 业务处理 阶段即是我们实现App开播业务逻辑的阶段,这个阶段的复杂度跟业务的复杂度相关。
下面对视频和音频的这5个阶段分别进行讲解。

视频

基础知识概念

视频流水线上面流动到各个阶段的“物品”是什么?

并不是一段视频,而是一帧一帧的图像。至于图像也只是一个抽象概念,在各阶段可能会有各种不同的表现形式,如:
* 图片:UIImage,CIImage,CGImage 等。
* 像素数据:CVBuffer/CVImageBuffer/CVPixelBuffer,查看系统源码可以发现这3种其实是同一种类型,只是通过`typedef`重命名了而已,所以遇到了不要慌。
* 纹理:GLTexture(OpenGLES),MTLTexture(Metal)。

以上3种图像表现形式之间都可以相互转化,具体的转化方法可以[百度](https://www.baidu.com)。

OpenGLES / Metal 是什么?

OpenGLESMetal都是用来驱动GPU做图像处理的框架。
OpenGLESAppleOpenGL进行封装,使其能够在iOS系统使用。
MetalApple针对现代化GPU设备设计的新版图像处理框架,并且采用面向对象的思想进行封装。建议使用Metal。
当然,这两者在使用上非常相似,你只要会其中一个,那么另一个对你来说也就不难了。
但你至少得会一个😂
如果都不会,我建议直接学Metal,具体怎么学?[传送门](https://github.com/bytedance/AlphaPlayer) 研究研究,研究明白就学会了。

采集

此阶段要做的就是每隔一段时间输出一个图像(一般是CVPixelBuffer)交给下一阶段处理。
一般来说,常用的视频采集类型有以下几种:

  • 摄像头:开启摄像头采集视频帧,可以使用系统库AVFoundation的AVCaptureSession实现此功能。
  • 静态图片:由App设置一张图片(UIImage等),在此采集类中将UIImage转化成CVPixelBuffer并以自定义的频率输出。
  • 视频:由App设置一个视频文件(mp4等),可以使用系统库AVFoundation解码并读取视频的每一帧CVPixelBuffer输出即可。

业务处理

此阶段和其他阶段不同,它不是像采集这样具体的功能,而是多个子阶段的组合,而这些子阶段则是根据具体的App业务实现的。
以我们这个直播软件的业务来说,子阶段主要包括:

  • 美颜:接收当前主播的图像纹理,输出添加美颜效果后的图像纹理。
  • PK合流:接收当前主播的图像纹理 和 对方主播的图像纹理,通过OpenGLES/Metal将两个纹理合并成一个纹理并输出。
  • 趣味连麦:接收当前主播的图像纹理 和 指定趣味资源图片的图像纹理,通过OpenGLES/Metal将两个纹理合并成一个纹理并输出。

当主播处于非PK态的业务场景时,这一阶段就只需要 美颜 处理即可;
当主播处于PK态的业务场景时,这一阶段就需要 美颜+PK合流 组合起来;
当主播处于趣味连麦PK态的业务场景时,这一阶段就需要 美颜+趣味连麦+PK合流 组合到一起。


注解:

  1. 美颜阶段细分的话又可以分为其他几个子阶段,比如人脸识别、磨皮、妆容、挂件等几个阶段,但因为我们这个直播软件美颜是接入的第三方SDK,这里就不再具体分析美颜的子阶段了。后面另起文章专门讲解美颜的实现吧。
  2. PK合流和趣味连麦的实现基本一致,不同的只是片选着色器的代码中定义的纹理合并规则不同。(PK合流是将画面分别绘制在输出纹理的左右两边,趣味连麦合流是将画面绘制在上下两层)
  3. PK合流中 对方主播的图像纹理,我们是通过接入的第三方视频通话SDK中的回调 获取到的。

渲染

此阶段要做的就是接收一个图像纹理,通过OpenGLES/Metal将纹理绘制到CAEAGLLayer/MTKView上。
很简单,就几十行代码,找个demo抄一下或者自己写都行。

编码

编码说直白点就是将视频帧压缩,至于H.264和H.265等等就是一种压缩算法。

  1. 为什么一定要编码?
稍微计算一下
假设采集阶段采集的视频帧大小是`720 * 1280`,CVPixelBuffer的像素格式是BGRA,帧率是30。
则每秒钟需要发送的数据量至少是`720 * 1280 * 4 * 30`字节,约等于105M,用户的手机网速必须达到105M/s才能正常直播,显然这是非常不合理的。
  1. 如何实现视频编码?
可以使用iOS系统库VideoToolBox编码,设置好帧间隔,码率,视频大小,压缩算法等参数,然后传视频帧进去就好了。
(机械,嘎嘎,机械,我只是一个没有感情的API调用机器)

这里只介绍了编码是什么,以及使用什么工具去实现它,至于具体的压缩原理以后再另起一篇文章细说吧,如果读者好奇,可以自行去查找相关资料,网上一大堆。

推流

这个阶段就是流水线的终点了。在主播开播的时候就要和服务端建立长链接通道,当收到编码压缩后的视频帧时,将视频帧的二进制数据通过长连接通道发送给服务端。

长连接有多种协议类型,我们目前采用的是RTMP协议推流,当然还有其他种类,具体使用什么协议由你们和自己的服务端商定。
如何实现?
一般这种通用的长连接协议,都是有人封装好了代码库的。要不,,,从github上找找?

音频

音频流水线和视频流水线在阶段上差不多,没有视频处理起来那么麻烦,但是需要了解一些音频数据结构相关的知识

采集

此阶段通过麦克风录制声音并将声音存储为二进制数据输出交给下一阶段处理。
如何实现?

使用系统库AVFoundation 或 AudioUnit。

业务处理

此阶段可以根据不同的业务对音频数据做一些处理

  1. 音效:萝莉音、大叔音、机械音等。可以通过一些算法对二进制数据修改,进而就可以达到改变音色的效果。(也有一些第三方的代码库)

  2. 混音:将多路音频数据混合成一路音频数据并输出,PK业务中会使用。

这里有两种混音方案: 
* 系统库:系统提供了音频混合的API,学习怎么调用即可。
* 算法:[音频混合算法](https://blog.csdn.net/unique_no1/article/details/123520817)

渲染

音频的渲染实际上就是音频的播放
实现方案:还是调用系统API,没什么可说的。

编码

和视频一样,编码实质上就是用了压缩数据量的,虽然音频数据量没视频那么大,但是能少一点是一点 实现方案:还是调用系统API,没什么可说的。

推流

和视频的推流一样,将编码后的二进制数据通过长连接发送给服务端

  • 音视频推流的时候最好做一下时间戳对齐,以防止画面和声音对不上的情况出现。
  • 网络环境不好的时候。可以降低编码的码率,使画面变模糊,网络环境好时再恢复;也可以选择性的丢弃掉一些帧。

总结

本篇文章简要的介绍了开播端技术实现上的阶段和概念,并针对每个阶段提供了一些实现的方案。

整理一下,我们可以发现,开播最难的地方其实是业务处理阶段。因为其他的阶段,功能实现完后面基本上不会有什么变更了,而且这些阶段的实现方案大部分都是基于系统库api就可以实现,不用去找第三方的代码库。
业务处理阶段难,但其实它也不是很难,根据不同的业务场景去拼接子阶段就好了。

最后

码字不易,本文地址:https://juejin.cn/post/7180739989821456443