什么是 rtc?rtc 即 real-time communication 的简称是一种给行业提供高并发、低延时、高清流畅、安全可靠的全场景、全互动、全实时的音视频服务的终端服务。上面是比较官方的解释,通俗的来讲就是一种能够实现一对一、多对多音视频通话等众多功能的服务。目前提供该项服务的服务商有很多例如:声网、云信、火山引擎、腾讯云等。
背景目前云音乐旗下 app 众多,其中涉及到 rtc 业务的不在少数,例如:常见的音视频连麦、pk、派对房,1v1 聊天等。由于业务线不同,功能不同,开发者也不同,大家各写一套,不断的重复造轮子,因此为了避免重复的开发工作提升开发效率,需要有一套通用的rtc框架。
设计思路在讲具体的方案设计之前,先讲一下我的设计思路:
功能内聚 :需要将功能都封装在一个容器里,对外通过接口提供方法调用业务隔离 :不同的业务需要有不同的功能容器统一调用 :所有功能容器需要有统一的调用入口状态维护 :需要对状态进行精准维护切换无感 :进行功能容器切换时候,无感知核心可控 :对核心链路可监控,故障预警基于以上 6 点,大致的架构设计如图所示,这里先不用深究图中的模块表示什么,后面会讲到,这里只是先了解一下大致的架构:
image.png
接下来我就来讲讲具体的实现过程。
方案设计前言:rtc 的业务场景虽然很多,但本质上却相差无几,都是用户加入到一个共同的房间,然后在房间内进行实时的音视频通讯。具体到实际项目中大致又可分为两种:全场景 rtc 和部分场景 rtc。
全场景 rtc :整个业务都是通过 rtc 技术实现例如:1v1 音视频通话、派对房等。部分场景 rtc :即整个业务链路中只有一部分使用了 rtc 技术,往往这种业务会涉及到引擎的切换。不管是哪一种场景,承载核心功能的引擎都是必不可少的,因此我们首先就从引擎开始着手,另外为了方便描述,后续便将引擎统一称作 player。
1、player 的封装在与 rtc 相关联的业务中会涉及到不同类型的 player,例如:主播开播(推流 player),观众观看直播(拉流 player)以及 rtc player等。它们的功能虽然各不相同,但用法却有相似之处,例如都有启动 start,终止 stop 等。因此我们可以将不同的 player 抽象出一个共同的接口 iplayer 相关代码如下:
interface iplayer { fun start(ds: ds) fun stop() fun setparam(key: string, value: t?) ......}其中 idatasource 和 icallback 分别是启动 player 所需要的数据源和回调,后面的文章中也会多次提到,特别是 idatasource 它是 player 启动的源头就好比打电话时的电话号码。
在这里遇到的一个问题点就是由于 player 内聚了所有的功能除了有一些通用方法外,也有着属于自己特有的方法,例如:静音,音量调节等。这些方法众多而且各不相同无法在 iplayer 接口中全部列出,即使能全部列出,但随着业务的迭代 player 中的方法肯定会不断变化,不可能每更改一个方法就改一下接口,这显然不符合程序设计原则。那么如何将不同的方法抽象化,让上层通过调用同一个方法来执行不同的操作呢?这里通过:
fun setparam(key: string, value: t?)来实现,其中 key 表示方法的唯一标记,value 表示方法的入参。这样上层只需要通过调用 setparam 传入相应的方法标记和方法入参即可调用到对应的方法了。那么如何做到呢?答案也很简单通过一个中间层建立起一一映射关系。但是 player 的类型众多,要是每写一个 player 都要写一个映射逻辑就太麻烦了。所以这里通过 apt 编译时注解再结合 javapoet 自动生成这个中间层并给它命名为 xxxplayerwrapper 其内部生成一个 convert 方法,在这个方法内部完成一一映射逻辑。接下来我们看看具体实现过程:
首先定义了两个注解分别作用于具体的 player 和对应的方法例如:@retention(retentionpolicy.class)@target({elementtype.type})public @interface playerclass {}@retention(retentionpolicy.class)@target({elementtype.method})public @interface playermethod { string name();}@playerclassopen class xxxplayer : iplayer() { @playermethod(name = key1) fun method1(v: string) { ....具体实现 }}一一映射关系建立:xxxplayer 和 xxxplayerwrapper 之间是一个相互依赖关系,互为彼此的成员变量。当调用 xxxplayer 的接口方法 setparam(key: string, value: t?) 时,会直接调用到 xxxplayerwrapper 的 convert 方法,convert 方法会根据 key 来找到其所对应的方法名,最后直接调用到 player 的具体方法。
image.png
使用ANDROID SMART PHONE的蓝牙可控机器人的制作
ofo无法在线退押 押金已经一去不复返
使用全新方式“照亮”无线时代的蓝牙mesh
用CD4001设计自动无线水泵控制器(附电路原理图)
荣耀V9、iPhone7Plus、小米6和vivoXplay6手机使用寿命大比拼
RTC脚手架的设计与实现(上)
食品分析仪器是什么,它的作用及特点的介绍
选好PCBA快速打样工厂的方法
iFixit 最新拆解:Mate 20 Pro 可修复性仅 4 分
通过InterBus现场总线实现汽车生产物料呼叫控制系统的设计
基于NI嵌入式控制器实现检测和分离循环肿瘤细胞研究
加密世界的用户怎样找到他们
怎样去升级工业互联网
Origin Q一周速览:新纪录!QV-4096量子计算机发布
瑞萨电子推出IPS2550传感器 将电感式位置感测产品阵容扩展至汽车电机换向应用
海尔洗衣机智慧成套展区正式重装营业 给用户带来了成套的智慧体验
对于咖啡机无刷电机的温升问题该如何解决
2017最期待十大旗舰机,iphone8第一,华为P10第七?
Nexus One 拆机揭秘,带 FM 发射/接收器
断路器和接触器的区别