什么是 RTC?
RTC 即 Real-Time Communication 的簡(jiǎn)稱是一種給行業(yè)提供高并發(fā)、低延時(shí)、高清流暢、安全可靠的全場(chǎng)景、全互動(dòng)、全實(shí)時(shí)的音視頻服務(wù)的終端服務(wù)。上面是比較官方的解釋,通俗的來講就是一種能夠?qū)崿F(xiàn)一對(duì)一、多對(duì)多音視頻通話等眾多功能的服務(wù)。目前提供該項(xiàng)服務(wù)的服務(wù)商有很多例如:聲網(wǎng)、云信、火山引擎、騰訊云等。
背景
目前云音樂旗下 APP 眾多,其中涉及到 RTC 業(yè)務(wù)的不在少數(shù),例如:常見的音視頻連麥、PK、派對(duì)房,1v1 聊天等。由于業(yè)務(wù)線不同,功能不同,開發(fā)者也不同,大家各寫一套,不斷的重復(fù)造輪子,因此為了避免重復(fù)的開發(fā)工作提升開發(fā)效率,需要有一套通用的RTC框架。
設(shè)計(jì)思路
在講具體的方案設(shè)計(jì)之前,先講一下我的設(shè)計(jì)思路:
- 功能內(nèi)聚 :需要將功能都封裝在一個(gè)容器里,對(duì)外通過接口提供方法調(diào)用
- 業(yè)務(wù)隔離 :不同的業(yè)務(wù)需要有不同的功能容器
- 統(tǒng)一調(diào)用 :所有功能容器需要有統(tǒng)一的調(diào)用入口
- 狀態(tài)維護(hù) :需要對(duì)狀態(tài)進(jìn)行精準(zhǔn)維護(hù)
- 切換無感 :進(jìn)行功能容器切換時(shí)候,無感知
- 核心可控 :對(duì)核心鏈路可監(jiān)控,故障預(yù)警
基于以上 6 點(diǎn),大致的架構(gòu)設(shè)計(jì)如圖所示,這里先不用深究圖中的模塊表示什么,后面會(huì)講到,這里只是先了解一下大致的架構(gòu):
image.png
接下來我就來講講具體的實(shí)現(xiàn)過程。
方案設(shè)計(jì)
前言:
RTC 的業(yè)務(wù)場(chǎng)景雖然很多,但本質(zhì)上卻相差無幾,都是用戶加入到一個(gè)共同的房間,然后在房間內(nèi)進(jìn)行實(shí)時(shí)的音視頻通訊。具體到實(shí)際項(xiàng)目中大致又可分為兩種:全場(chǎng)景 RTC 和部分場(chǎng)景 RTC。
- 全場(chǎng)景 RTC :整個(gè)業(yè)務(wù)都是通過 RTC 技術(shù)實(shí)現(xiàn)例如:1v1 音視頻通話、派對(duì)房等。
- 部分場(chǎng)景 RTC :即整個(gè)業(yè)務(wù)鏈路中只有一部分使用了 RTC 技術(shù),往往這種業(yè)務(wù)會(huì)涉及到引擎的切換。
不管是哪一種場(chǎng)景,承載核心功能的引擎都是必不可少的,因此我們首先就從引擎開始著手,另外為了方便描述,后續(xù)便將引擎統(tǒng)一稱作 Player。
1、Player 的封裝
在與 RTC 相關(guān)聯(lián)的業(yè)務(wù)中會(huì)涉及到不同類型的 Player,例如:主播開播(推流 Player),觀眾觀看直播(拉流 Player)以及 RTC Player等。它們的功能雖然各不相同,但用法卻有相似之處,例如都有啟動(dòng) start,終止 stop 等。因此我們可以將不同的 Player 抽象出一個(gè)共同的接口 IPlayer 相關(guān)代碼如下:
interface IPlayer<DS : IDataSource, CB : ICallback> {
fun start(ds: DS)
fun stop()
fun setParam(key: String, value: T?)
......
}
其中 IDataSource 和 ICallback 分別是啟動(dòng) Player 所需要的數(shù)據(jù)源和回調(diào),后面的文章中也會(huì)多次提到,特別是 IDataSource 它是 Player 啟動(dòng)的源頭就好比打電話時(shí)的電話號(hào)碼。
在這里遇到的一個(gè)問題點(diǎn)就是由于 Player 內(nèi)聚了所有的功能除了有一些通用方法外,也有著屬于自己特有的方法,例如:靜音,音量調(diào)節(jié)等。這些方法眾多而且各不相同無法在 IPlayer 接口中全部列出,即使能全部列出,但隨著業(yè)務(wù)的迭代 Player 中的方法肯定會(huì)不斷變化,不可能每更改一個(gè)方法就改一下接口,這顯然不符合程序設(shè)計(jì)原則。那么如何將不同的方法抽象化,讓上層通過調(diào)用同一個(gè)方法來執(zhí)行不同的操作呢?這里通過:
fun setParam(key: String, value: T?)
來實(shí)現(xiàn),其中 key 表示方法的唯一標(biāo)記,value 表示方法的入?yún)?。這樣上層只需要通過調(diào)用 setParam 傳入相應(yīng)的方法標(biāo)記和方法入?yún)⒓纯烧{(diào)用到對(duì)應(yīng)的方法了。那么如何做到呢?答案也很簡(jiǎn)單通過一個(gè)中間層建立起一一映射關(guān)系。但是 Player 的類型眾多,要是每寫一個(gè) Player 都要寫一個(gè)映射邏輯就太麻煩了。所以這里通過 APT 編譯時(shí)注解再結(jié)合 javapoet 自動(dòng)生成這個(gè)中間層并給它命名為 xxxPlayerWrapper 其內(nèi)部生成一個(gè) convert 方法,在這個(gè)方法內(nèi)部完成一一映射邏輯。接下來我們看看具體實(shí)現(xiàn)過程:
- 首先定義了兩個(gè)注解分別作用于具體的 Player 和對(duì)應(yīng)的方法例如:
@Retention(RetentionPolicy.CLASS)
@Target({ElementType.TYPE})
public @interface PlayerClass {
}
@Retention(RetentionPolicy.CLASS)
@Target({ElementType.METHOD})
public @interface PlayerMethod {
String name();
}
@PlayerClass
open class xxxPlayer : IPlayer<xxxDataSource, xxxCallback>() {
@PlayerMethod(name = "key1")
fun method1(v: String) {
....具體實(shí)現(xiàn)
}
}
- 一一映射關(guān)系建立:
xxxPlayer 和 xxxPlayerWrapper 之間是一個(gè)相互依賴關(guān)系,互為彼此的成員變量。當(dāng)調(diào)用 xxxPlayer 的接口方法 setParam(key: String, value: T?) 時(shí),會(huì)直接調(diào)用到 xxxPlayerWrapper 的 convert 方法,convert 方法會(huì)根據(jù) key 來找到其所對(duì)應(yīng)的方法名,最后直接調(diào)用到 Player 的具體方法。
image.png
-
RTC
+關(guān)注
關(guān)注
2文章
511瀏覽量
65897 -
騰訊云
+關(guān)注
關(guān)注
0文章
204瀏覽量
16704
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論