▲ 图 1
图 1 展示了实时音视频两种不同的应用场景——连麦互动直播和线上娃娃机。虽然这两种都是互动,但是对于实时音视频的要求却不同。第一个实时连麦是语音视频流的互动,例如其中一个说了一句话,另外一个人听到了,再回复一句话,这个实时性只是对语音视频流的实时性要求很高。而第二种线上抓娃娃则对信令的延迟提出了更高的要求,操纵者无需说话,看到的是娃娃机传回来的视频流结果。
如果考量互动直播是用实时音视频的延迟,那么线上抓娃娃则是用信令和视频流的延时。随着时代的发展,我们对实时语音视频的定义会慢慢有一些不同,将来可能还有更多的因素需要考虑。
▲ 图 2
图 2 是我们互动直播的实时架构图,我们把互动直播分为两部分,一个是主播侧,需要更低的延迟,另一侧是普通观众,对延时不太敏感,但对流畅性敏感,中间通过一些旁路的服务把这两个集群(一个集群叫超低延迟集群,另外一个集群叫围观集群)连接起来。
在超低延时部分,我们提供的服务包括流状态更新、房间管理等,以及一些流媒体服务,主要起到分发的作用。我们通过超低延迟服务器集群(和观众侧不太一样),提供实时分发的功能。
此外还提供了动态调度的服务,帮助我们在现有的资源网络上找到更好的链路。后面的观众集群是另外一个集群,把它们分开是出于一些业务方和我们自己成本上的考虑,另外会提供存储、PCB 加速、分发的功能。
中间的旁路服务包括混流、转格式(主要是转码)、转协议等。为什么要混流?举一个比较简单的例子:当主播一侧有 9 个人连麦,如果没有混流服务,观众端就会同时拉 9 路音视频,这样对带宽压力很大。
普通观众是通过围观服务器集群(延迟相对大的集群)去拉这些流的,这个集群的延迟可控性相对比较弱,有可能会出现这 9 路画面之间的不同步现象,通过混流服务,观众拉的都是合成好的音视频流,就不会出现各路流之间的不同步问题。
还有转格式转码的服务,前面集群提供的是很低延迟的服务,里面一些,比如说编码码流,不能够在传统的 CDN 网络分发,如果想在传统的 CDN 网络上分发,就要服务端的转码。还有就是转协议,因为前面提供一个更低延时的服务,后面要在 CDN 网络上分发,所以协议也需要转。
▲ 图 3
图 3 是线上娃娃机的 APP 版本的架构图,这里的是特色是线上娃娃机可以实时推两路视频流,上机玩家可以随时任意去切其中一路画面去看。这两路视频流首先通过我们超低延时服务器集群,同时上机玩家也可以推一路流上去,可以给围观观众方看到这个人在抓娃娃时候的一些表情、反应、语言,增加一种互动性。此外,玩家需要通过手机远程操控娃娃机,因此还需要实时信令的分发。
▲ 图 4
接下来是娃娃机的 H5 架构图(图4)。在推流方面和 APP 版本没有太大的区别,娃娃机一侧还是走的私有协议。不同的地方是因为私有协议没有办法直接让 H5 拉到流,所以中间会加入一个媒体网关,作用是把我们的私有协议翻译成 H5 可以识别的码流格式,然后 H5 端通过 websocket 方式把这路流拉下来,这里需要媒体网关做到超低延时的转换。
简单来看,这里的网关服务器只是做了一个分发服务,好像不会引入延时,实际上不然。
因为 websocket 拉的是 TCP 的流,但是我们推的是 UDP 的,当视频帧很大的时候,一个帧数据就要切割成很多 UDP 包上行,服务器需要将这些 UDP 包攒起来,凑成一个完整的帧后才下发给 H5,这样才能保证不花屏,才能跑得通,所以这个攒包组帧的过程是会有延迟的。信令部分和 APP 部分基本是相似的。
3、实时架构的若干点思考
刚才介绍了实时音视频的两种场景,下面提出一点思考:
-
实时音视频有什么样的特征?
-
怎么样去架构一个实时音视频系统?
这是仁者见仁,智者见智的问题。你可以通过很多方式把这个系统架构起来,都会达到相对不错的效果。但是我认为,无论怎样,实时音视频都有绕不过如下几个点,只有把它们做好了,才能够在业界有更高的知名度、更好的技术储备。
第一是实时音视频是不能等的: 因为等了就不是实时音视频了。 不能等,这里会引入一个矛盾。既然不能等,例如你把实时音视频也看作一个消费模型来看,那是提前生产还是按需生产?字面上理解很简单,肯定是按需生产,需要的时候才生产,如果提前生产就是延时了。但是并不是每一个点都做成按需生产是合理的。
举一个例子,比如你要去播放一段音频,最好的做法是系统或者驱动告诉你,它需要数据了,然后去解一帧塞给它,这就是按需生产。但是为什么还有提前生产一说呢?就是系统告诉你它要数据的时候,实际上它有一个对响应周期的要求。
你现去生产可能就要等去解完一帧,但是这个时候来得及吗?如果你只有一路下行,可能就来得及。但是现在要求很多路下行,在很短的时间周期内解很多帧,对硬件性能有很高的要求。通常来讲,并不可取。这只是实时音视频中一个简单的例子。提前生产会引入延迟的,那么到底要提前多久生产,怎么样动态估计我们什么时候应该生产?这是一个开放性的问题,也是一个大家在设计系统时要重点考虑的。
第二是实时音视频不能久等: 实时音视频中有些等待是避免不了的,例如你要做音频编码,它本来一定要 20 毫秒一帧或者 40 毫秒一帧去做,给一个采样点点是编不了的。这里既然有些延迟和等待避免不了,我们当然希望系统处理的粒度越低越好,这样可能会带来更低的延时。但是处理的粒度越低,整个系统在频繁跑的时候,你可以认为它是一套循环,当循环的东西很少,这个循环就会跑很多次,对系统来说就是一个很大的开销和负担。
所以不能久等的时候,我们当然希望它处理粒度小。另外处理粒度小还有一个优势,在整个系统中并不能保证每一个环节的处理粒度是一致的。例如这个节点可能要求是 10 毫秒,下一个结点要求 15 毫秒,这是由于算法的限制,可能没有办法避免。如果在整个系统内选一个相对小的粒度,在粒度拼接的时候,例如 10-15 毫秒,要两个 10 毫秒才能够 15 毫秒,还剩下 5 毫秒,剩的就比较少。
如果粒度很粗,可能剩下的东西就很多。在粒度拼接的时候,这个剩余的量代表了整个链路中的延迟。所以我们希望处理粒度尽量小,但是又不能小到整个系统没有办法接受的粒度。
第三是实时音视频不能死等: 例如你需要接收一个网络包的时候,这个包迟迟不到,这个时候你不能完全不等,完全不等就会卡。但是在等的时候有一个超时的机制,例如这个音频包就是很久不到,就把它跳过去做一个纠帧补偿,当包最终还是到了的时候,我也只能把它扔掉,而不应该把它利用起来。
▲ 图 5
此外,实时音视频在服务器端还需要深入考虑这样几个问题:
-
第一是负载均衡;
-
第二是就近接入;
-
第三是质量评估;
-
第四是动态路由;
-
第五是算法流控。
**负载均衡:**是说让整个服务器的每一个节点都承担相对均匀的服务,不至于使得某一个节点负载过高造成一些丢包,造成网络往返时的增大,这样对任何的网络损伤来讲,对实时音视频都会造成比较大的延迟增加。
**就近接入:**这里的“近”并不是指地域上的近,而是“网络上的近”。很简单的例子,我们在深圳做推流,香港离得很近,可以推到香港的服务器,但实际上这毕竟是一个跨域的网络,有不稳定的因素在里面,所以我们宁愿推远一点。这个近指的应该是在网络质量评估意义上的近,例如网络往返时很小、往返时很平稳、分布在延迟比较大的时刻不会还具有很大的概率,丢包率很低等。
要做到就近接入,这个近要有一个很好的质量评估体系,质量评估方法有两种:
-
事后质量评估:在复盘的时候,例如这个网络平稳的运行了一个月,复盘看一下整个月中的质量怎么样,这样的质量评估可以认为是一个相对离线的评估,它能够给我们提供一个指标,最近一个月的网络和上个月相比是否有所改善。我们可以从中学习到一些经验,例如这个月和上个月的调度上有些策略上的不同。这是一个系统化的经验总结和优化的方法。
-
实时质量评估:更重要的应该是一个实时上的评估,例如我现在推流,能够实时监控到当前的质量是怎么样的,就可以做到实时动态路由。
**实时动态路由:**是说某个人推流从北京推到迪拜,有很多链路可以选,他可能根据之前的一些经验,假如他之前经验告诉你,直接推到迪拜,这个链路是很好的,但是毕竟有个例。有动态实时的质量评估,就知道这个时候推迪拜是否好,如果不好,可以在用户无感知的情况下更换,随时增减整个链路中一些路由的节点。这就是动态路由的思路。
实际情况中是结合前面这 4 个点,在我们的网络和服务器资源集中,去选出质量最优或者近似最优的链路来保证实时音视频的服务的。但是资源集是有限的,没有人可以保证你的资源集中一定可以选出的这个最优具有很好的链路特征。保证不了就要考虑第五点,我即使选出了一个认为是整个资源集中最优的链路,但是它的质量还达不到很好的标准,就要通过一些算法才能弥补。这些算法包括在一个不可靠的网络中怎么样进行可靠的音视频传输的技术,这些技术在接下来我们会和大家稍微分享一下,也包括整个链路的一些拥塞控制。
4、关于信源编码的思考
▲ 图 6
信源编码是为了减少网络中的负担,把大量的数据压缩成比较小的网络数据,来减少网络负担的方式。压缩方式有很多种,我们先以音频来看,上面画了一些图(图 6)。
我们重点看 Opus 编码器,它有几种模式在里面:
-
一种是线性预测模式;
-
还有一种是混合模式;
-
另一种是频域编码模式。
混合模式是把两种编码模式混合在一起,根据不同的情况进行选择。
图 6 是一个编码器,横轴是码率,纵轴是它的质量,中间是各种音频编解码器的表现。你会发现线性预测的方式能够在低码率上提供比较好的质量,但是在 20K 左右的时候就没有曲线了,因为它不支持那么高的码率。然后看 MDCT 编码,它可以在比较高的码率上达到近似透明的音质。音频编码器是有不同的编码原理在里面的,像这种 LP Mode 是模拟人的发声模型,既然有了数学建模,它的特征是能够在一个比较低的码率上提供一个比较可靠的质量。
但是它的特点是容易达到一种质量上的饱和,也就是说当你码率给它很高的时候,实际上它也就编的效果还是那样,因为它毕竟是一种参数化的编码。所以根据业务场景,当你需要一个很高的音质,又需要音乐场景的时候,选择它明显不合适。MDCT MODE 没有任何的模型在里面,实际上就是把信号转换成频域,直接去量化。既然没有模型化,它是比较消耗码率的,但是它可以在一个较高的码率上提供很好的质量,可是低码率的表现远远不如模型化的方法。
▲ 图 7
整体总结起来,音频包括语音和音乐两种,因此有适合语音的 codec 和适合音乐的 codec:
-
第一种 codec 适合语音,语音可以模型化,适用于语音的 codec 能够在低码率上提供很好的质量,提供一个相对高的压缩比, 但是它容易达到饱和,不能够提供一个近似于透明的音质;
-
另外一种 codec 的编码原理不一样,能够把音乐、语音都编得很好,但是特点是不能够提供太高的压缩比,指望它能够在低码率下提供很高的编码质量是做不到的。
▲ 图 8
关于视频编码,最简单的几个点有 I 帧、P 帧、B 帧。I 帧是自参考,P 帧是向前参考,它会参考历史帧的特性进行编码。B 帧是双向参考,它可以参考前面的帧,也可以参考后面的帧。B 帧可以带来更高的压缩比,提供更好的质量。但是因为它会参考将来的帧,所以会引入延迟,因此我们在实时音视频系统中是很少用到 B 帧的。
想要做好实时的音视频系统,流控是一定要做的,流控对视频的编解码有什么要求?至少有一点,编解码器的码控一定要很稳定。为什么?举例说,我现在有一个很好的拥塞控制策略,带宽估计做得很好,一点差错都没有,估计出某一个时刻可分配视频的带宽就是 500kbps,就可以让视频编码器设置成 500kbps。但是,如果码控不是很稳定,你设置 500kbps 的时候,视频编码器可能就跑到 600kbps 了,这样就会带来一些阻塞和延迟。因此,我们希望选择的 codec 具有很好的码控策略。
实际上一些开源代码都是有做码控的,但是直接拿来用并不是适合你的场景,因为这些开源代码做起来,可能或多或少的考虑其他的场景,并不只是实时音视频场景。比如说某个 codec 是用来压片的,希望半个小时或者一个小时之内达到预定的码率就可以,不会管这一秒钟或者下一秒是什么样子的,但是实时音视频就是要求要把时间窗做得很小。
另外我们希望 codec 有分层编码的能力。什么是分层编码?为什么要有分层编码?分层编码也分两种,一种是时域上的分层,一种是空域上的分层。前者是编码的时候是当前帧不参考上一帧,而是有隔帧参考的策略;后者可以认为使用较低的码率先编码一个小的画面,然后使用剩余的码率编码增量的部分,得到更高分辨率的画面。
为什么要这样做?实时音视频中并不是很多场景都是一对一的,当不是一对一,要做流控的时候,不可能因为某一路观众的下行不好,就把主播上行推流的码率降下来,因为可能还有一千个观众的网络很好,这些网络好的观众也会因为个别观众网络不好,而只能看到不那么清晰的画面。所以要分层,可以在服务器端选择给用户到底下发哪一层的,因为有分层策略,如果这个人线路不好,只要选择其中一个比较小的层次发给他就可以了,例如核心层,这样可以紧紧利用核心层把整个视频还原,可能会损伤一些细节或者帧率偏低,但是至少整体可用。
最后,我想说一下,很多人认为,视频的数据量很大,视频的延时比音频应该更高才对,实际上不是。因为很多的延迟实际上是编解码自有的延迟,如果编解码中没有 B 帧的话,你可以理解为视频编码是没有任何延迟的。但是音频编码或多或少都会参考一些将来的数据,也就是说音频编码器的延时一定是存在的。因此,通常来讲,音频的延时比视频的延时更高才对。
5、关于信道编码技术的思考
▲ 图 9
**信道编码分几个部分:**一种是根据先验知识的网络冗余编码技术——前向纠错技术。以 RS(4,6)编码为例,我要发一个分组,这个分组有六个包,其中有四个包是实际媒体数据,有两个包是冗余包。那么在解码端收到六个包中任意的四个,就可以完全恢复所有携带媒体内容的包。
例如这里 2、3 都丢了,收到了 1、4、r1、r2,也能够完全恢复 2 和 3。这样看来很好,任意两个丢掉都可以完全恢复。但是这样的算法也有它的弱点,不太适合突发性的丢包。因为这个分组不宜太大,如果分组很大,分组就有很大的延时。分组如果很小,很可能整个分组都丢掉了。
实际上这种做法就没有任何意义。所以它不太适合突发性丢包,而且它毕竟是根据先验知识去做的一种冗余,也就是说它永远是根据上一时刻网络的状态作出的判断,下一时刻网络是什么样的,是预测的东西。网络是实时发生变化的,这种预测的东西并不完全可靠。
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则近万的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:Android)
![](https://i-blog.csdnimg.cn/blog_migrate/fe3dcf332a7d2e1b0b828a907d0df92b.jpeg)
最后
简历首选内推方式,速度快,效率高啊!然后可以在拉钩,boss,脉脉,大街上看看。简历上写道熟悉什么技术就一定要去熟悉它,不然被问到不会很尴尬!做过什么项目,即使项目体量不大,但也一定要熟悉实现原理!不是你负责的部分,也可以看看同事是怎么实现的,换你来做你会怎么做?做过什么,会什么是广度问题,取决于项目内容。但做过什么,达到怎样一个境界,这是深度问题,和个人学习能力和解决问题的态度有关了。大公司看深度,小公司看广度。大公司面试你会的,小公司面试他们用到的你会不会,也就是岗位匹配度。
面试过程一定要有礼貌!即使你觉得面试官不尊重你,经常打断你的讲解,或者你觉得他不如你,问的问题缺乏专业水平,你也一定要尊重他,谁叫现在是他选择你,等你拿到offer后就是你选择他了。
另外,描述问题一定要慢!不要一下子讲一大堆,慢显得你沉稳、自信,而且你还有时间反应思路接下来怎么讲更好。现在开发过多依赖ide,所以会有个弊端,当我们在面试讲解很容易不知道某个方法怎么读,这是一个硬伤…所以一定要对常见的关键性的类名、方法名、关键字读准,有些面试官不耐烦会说“你到底说的是哪个?”这时我们会容易乱了阵脚。正确的发音+沉稳的描述+好听的嗓音决对是一个加分项!
最重要的是心态!心态!心态!重要事情说三遍!面试时间很短,在短时间内对方要摸清你的底子还是比较不现实的,所以,有时也是看眼缘,这还是个看脸的时代。
希望大家都能找到合适自己满意的工作!
进阶学习视频
附上:我们之前因为秋招收集的二十套一二线互联网公司Android面试真题 (含BAT、小米、华为、美团、滴滴)和我自己整理Android复习笔记(包含Android基础知识点、Android扩展知识点、Android源码解析、设计模式汇总、Gradle知识点、常见算法题汇总。)
《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!
阶学习视频**
[外链图片转存中…(img-zlcxWc1i-1713597255622)]
附上:我们之前因为秋招收集的二十套一二线互联网公司Android面试真题 (含BAT、小米、华为、美团、滴滴)和我自己整理Android复习笔记(包含Android基础知识点、Android扩展知识点、Android源码解析、设计模式汇总、Gradle知识点、常见算法题汇总。)
[外链图片转存中…(img-ASjnhrWV-1713597255624)]
《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!