WebRTC的模塊處理機(jī)制

對于實(shí)時音視頻應(yīng)用來講,媒體數(shù)據(jù)從采集到渲染,在數(shù)據(jù)流水線上依次完成一系列處理月杉。流水線由不同的功能模塊組成周伦,彼此分工協(xié)作:數(shù)據(jù)采集模塊負(fù)責(zé)從攝像頭/麥克風(fēng)采集音視頻數(shù)據(jù),編解碼模塊負(fù)責(zé)對數(shù)據(jù)進(jìn)行編解碼狞换,RTP模塊負(fù)責(zé)數(shù)據(jù)打包和解包。數(shù)據(jù)流水線上的數(shù)據(jù)處理速度是影響應(yīng)用實(shí)時性的最重要因素汪厨。與此同時低斋,從服務(wù)質(zhì)量保證角度講蜂厅,應(yīng)用需要知道數(shù)據(jù)流水線的運(yùn)行狀態(tài),如視頻采集模塊的實(shí)時幀率膊畴、當(dāng)前網(wǎng)絡(luò)的實(shí)時速率掘猿、接收端的數(shù)據(jù)丟包率,等等唇跨。各個功能模塊可以基于這些運(yùn)行狀態(tài)信息作相應(yīng)調(diào)整稠通,從而在質(zhì)量、速度等方面優(yōu)化數(shù)據(jù)流水線的運(yùn)行买猖,實(shí)現(xiàn)更快改橘、更好的用戶體驗(yàn)。</br>

WebRTC采用模塊機(jī)制玉控,把數(shù)據(jù)流水線上功能相對獨(dú)立的處理點(diǎn)定義為模塊飞主,每個模塊專注于自己的任務(wù),模塊之間基于數(shù)據(jù)流進(jìn)行通信高诺。與此同時碌识,專有線程收集和處理模塊內(nèi)部的運(yùn)行狀態(tài)信息,并把這些信息反饋到目標(biāo)模塊懒叛,實(shí)現(xiàn)模塊運(yùn)行狀態(tài)監(jiān)控和服務(wù)質(zhì)量保證丸冕。本文在深入分析WebRTC源代碼基礎(chǔ)上,學(xué)習(xí)研究其模塊處理機(jī)制的實(shí)現(xiàn)細(xì)節(jié)薛窥,從另一個角度理解WebRTC的技術(shù)原理胖烛。</br>

1 WebRTC數(shù)據(jù)流水線

</br>
</br>我們可以把WebRTC看作是一個專注于實(shí)時音視頻通信的SDK。其對外的API主要負(fù)責(zé)PeerConnection建立诅迷、MediaStream創(chuàng)建佩番、NAT穿透、SDP協(xié)商等工作罢杉,對內(nèi)則主要集中于音視頻數(shù)據(jù)的處理趟畏,從數(shù)據(jù)采集到渲染的整個處理過程可以用一個數(shù)據(jù)流水線來描述,如圖1所示滩租。</br>

圖1 WebRTC音視頻數(shù)據(jù)流水線

音視頻數(shù)據(jù)首先從采集端進(jìn)行采集赋秀,一般來說音頻數(shù)據(jù)來自麥克風(fēng),視頻數(shù)據(jù)來自攝像頭律想。在某些應(yīng)用場景下猎莲,音頻數(shù)據(jù)來自揚(yáng)聲器,視頻數(shù)據(jù)來自桌面共享技即。采集端的輸出是音視頻Raw Data著洼。然后Raw Data到達(dá)編碼模塊,數(shù)據(jù)被編碼器編碼成符合語法規(guī)則的NAL單元,到達(dá)發(fā)送端緩沖區(qū)PacedSender處身笤。接下來PacedSender把NAL單元發(fā)送到RTP模塊打包為RTP數(shù)據(jù)包豹悬,最后經(jīng)過網(wǎng)絡(luò)模塊發(fā)送到網(wǎng)絡(luò)。</br>

在接收端液荸,RTP數(shù)據(jù)包經(jīng)過網(wǎng)絡(luò)模塊接收后進(jìn)行解包得到NAL單元瞻佛,接下來NAL單元到達(dá)接收端緩沖區(qū)(JitterBuffer或者NetEQ)進(jìn)行亂序重排和組幀。一幀完整的數(shù)據(jù)接收并組幀之后莹弊,調(diào)用解碼模塊進(jìn)行解碼涤久,得到該幀數(shù)據(jù)的Raw Data。最后Raw Data交給渲染模塊進(jìn)行播放/顯示忍弛。</br>

在數(shù)據(jù)流水線上,還有一系列模塊負(fù)責(zé)服務(wù)質(zhì)量監(jiān)控考抄,如丟幀策略细疚,丟包策略,編碼器過度使用保護(hù)川梅,碼率估計疯兼,前向糾錯,丟包重傳贫途,等等吧彪。</br>

WebRTC數(shù)據(jù)流水線上的功能單元被定義為模塊,每個模塊從上游模塊獲取輸入數(shù)據(jù)丢早,在本模塊進(jìn)行加工后得到輸出數(shù)據(jù)姨裸,交給下游模塊進(jìn)行下一步處理。WebRTC的模塊處理機(jī)制包括模塊和模塊處理線程怨酝,前者把WebRTC數(shù)據(jù)流水線上的功能部件封裝為模塊傀缩,后者驅(qū)動模塊內(nèi)部狀態(tài)更新和模塊之間狀態(tài)傳遞。模塊一般掛載到模塊處理線程上农猬,由處理線程驅(qū)動模塊的處理函數(shù)赡艰。下面分別描述之。</br>

2 WebRTC模塊

</br>
</br>WebRTC模塊虛基類Module定義在webrtc/modules/include/modue.h中斤葱,如圖2所示慷垮。</br>

圖2 模塊虛基類Module定義

Module虛基類對外提供三個函數(shù)作為API:TimeUntilNextProcess()用來計算距下次調(diào)用處理函數(shù)Process()的時間間隔;Process()是模塊的處理函數(shù)揍堕,負(fù)責(zé)模塊內(nèi)部運(yùn)行監(jiān)控料身、狀態(tài)更新和模塊間通信;ProcessThreadAttached()用來把模塊掛載到模塊處理線程鹤啡,或者從模塊處理線程分離出來惯驼,實(shí)際實(shí)現(xiàn)中這個函數(shù)暫時沒有被用到。</br>

Module的派生類分布在WebRTC數(shù)據(jù)流水線上的不同部分,各自承擔(dān)自己的數(shù)據(jù)處理和服務(wù)質(zhì)量保證任務(wù)祟牲。</br>

3 WebRTC模塊處理線程

</br>
</br>WebRTC模塊處理線程是模塊處理機(jī)制的驅(qū)動器隙畜,它的核心作用是對所有掛載在本線程下的模塊,周期性調(diào)用其Process()處理函數(shù)處理模塊內(nèi)部事務(wù)说贝,并處理異步任務(wù)议惰。其虛基類ProcessThread和派生類ProcessThreadImpl如圖3所示。</br>

圖3 模塊處理線程虛基類ProcessThread及派生類ProcessThreadImpl

ProcessThread基類提供一系列API完成線程功能:Start()/Stop()函數(shù)用來啟動和結(jié)束線程乡恕;WakeUp()函數(shù)用來喚醒掛載在本線程下的某個模塊言询,使得該模塊有機(jī)會馬上執(zhí)行其Process()處理函數(shù);PostTask()函數(shù)用來郵遞一個任務(wù)給本線程傲宜;RegisterModule()和DeRegisterModule()用來向線程注冊/注銷模塊运杭。</br>

WebRTC基于ProcessThread線程實(shí)現(xiàn)派生類ProcessThreadImpl,如圖3所示函卒。在成員變量方面辆憔,wake_up_用來喚醒處于等待狀態(tài)的線程;thread_是平臺相關(guān)的線程實(shí)現(xiàn)如POSIX線程报嵌;modules_是注冊在本線程下的模塊集合虱咧;queue_是郵遞給本線程的任務(wù)集合;thread_name_是線程名字锚国。在成員函數(shù)方面腕巡,Process()完成ProcessThread的核心任務(wù),其偽代碼如下所示血筑。</br>

bool ProcessThreadImpl::Process() {
    for (ModuleCallback& m : modules_) {
      if (m.next_callback == 0)
        m.next_callback = GetNextCallbackTime(m.module, now);

      if (m.next_callback <= now || m.next_callback == kCallProcessImmediately) {
        m.module->Process();
        m.next_callback = GetNextCallbackTime(m.module, rtc::TimeMillis(););
      }

      if (m.next_callback < next_checkpoint)
        next_checkpoint = m.next_callback;
    }

    while (!queue_.empty()) {
      ProcessTask* task = queue_.front();
      queue_.pop();
      task->Run();
      delete task;
    }
  }

  int64_t time_to_wait = next_checkpoint - rtc::TimeMillis();
  if (time_to_wait > 0)
    wake_up_->Wait(static_cast<unsigned long>(time_to_wait));

  return true;
}

Process()函數(shù)首先處理掛載在本線程下的模塊绘沉,這也是模塊處理線程的核心任務(wù):針對每個模塊,計算其下次調(diào)用模塊Process()處理函數(shù)的時刻(調(diào)用該模塊的TimeUntilNextProcess()函數(shù))云挟。如果時刻是當(dāng)前時刻梆砸,則調(diào)用模塊的Process()處理函數(shù),并更新下次調(diào)用時刻园欣。需要注意帖世,不同模塊的執(zhí)行頻率不一樣,線程在本輪調(diào)用末尾的等待時間和本線程下所有模塊的最近下次調(diào)用時刻相關(guān)沸枯。</br>

接下來線程Process()函數(shù)處理ProcessTask隊(duì)列中的異步任務(wù)日矫,針對每個任務(wù)調(diào)用Run()函數(shù),然后任務(wù)出隊(duì)列并銷毀绑榴。等模塊調(diào)用和任務(wù)都處理完后哪轿,則把本次時間片的剩余時間等待完畢,然后返回翔怎。如果在等待期間其他線程向本線程Wakeup模塊或者郵遞一個任務(wù)窃诉,則線程被立即喚醒并返回杨耙,進(jìn)行下一輪時間片的執(zhí)行。</br>

至此飘痛,關(guān)于WebRTC的模塊和模塊處理線程的基本實(shí)現(xiàn)分析完畢珊膜,下一節(jié)將對WebRTC SDK內(nèi)模塊實(shí)例和模塊處理線程實(shí)例進(jìn)行詳細(xì)分析。</br>

4 WebRTC模塊處理線程實(shí)例

</br>
</br>WebRTC關(guān)于模塊和處理線程的實(shí)現(xiàn)在webrtc/modules目錄下宣脉,該目錄匯集了所有派生類模塊和模塊處理線程的實(shí)現(xiàn)及實(shí)例分布车柠。本節(jié)對這些內(nèi)容進(jìn)行總結(jié)。</br>

WebRTC目前創(chuàng)建三個ProcessThreadImpl線程實(shí)例塑猖,分別是負(fù)責(zé)處理音頻的VoiceProcessTread竹祷,負(fù)責(zé)處理視頻和音視頻同步的ModuleProcessThread,以及負(fù)責(zé)數(shù)據(jù)平滑發(fā)送的PacerThread羊苟。這三個線程和掛載在線程下的模塊如圖4所示塑陵。</br>

圖4 模塊處理線程實(shí)例

VoiceProcessThread線程由Worker線程在創(chuàng)建VoiceEngine時創(chuàng)建,負(fù)責(zé)音頻端模塊的處理蜡励。掛載在該線程下的模塊如圖4所示猿妈,其中MonitorModule負(fù)責(zé)對音頻數(shù)據(jù)混音處理過程中產(chǎn)生的警告和錯誤進(jìn)行處理,AudioDeviceModuleImpl負(fù)責(zé)對音頻設(shè)備采集和播放音頻數(shù)據(jù)時產(chǎn)生的警告和錯誤進(jìn)行處理巍虫,ModuleRtpRtcpImpl負(fù)責(zé)音頻RTP數(shù)據(jù)包發(fā)送過程中的碼率計算、RTT更新鳍刷、RTCP報文發(fā)送等內(nèi)容占遥。</br>

ModuleProcessThread線程由Worker線程在創(chuàng)建VideoChannel時創(chuàng)建,負(fù)責(zé)視頻端模塊的處理输瓜。掛載在該線程下的模塊如圖4所示瓦胎,其中CallStats負(fù)責(zé)Call對象統(tǒng)計數(shù)據(jù)(如RTT)的更新,CongestionController負(fù)責(zé)擁塞控制[1][2]尤揣,VideoSender負(fù)責(zé)視頻發(fā)送端統(tǒng)計數(shù)據(jù)的更新搔啊,VideoReceiver負(fù)責(zé)視頻接收端統(tǒng)計數(shù)據(jù)更新和處理狀態(tài)反饋(如請求關(guān)鍵幀),ModuleRtpRtcpImpl負(fù)責(zé)視頻RTP數(shù)據(jù)包發(fā)送過程中的碼率計算北戏、RTT更新负芋、RTCP報文發(fā)送等內(nèi)容,OveruseFrameDetector負(fù)責(zé)視頻幀采集端過載監(jiān)控嗜愈,ReceiveStatisticsImpl負(fù)責(zé)由接收端統(tǒng)計數(shù)據(jù)觸發(fā)的碼率更新過程旧蛾,ViESyncModule負(fù)責(zé)音視頻同步。</br>

PacerThread線程由Worker線程在創(chuàng)建VideoChannel時創(chuàng)建蠕嫁,負(fù)責(zé)數(shù)據(jù)平滑發(fā)送锨天。掛載在該線程下的PacedSender負(fù)責(zé)發(fā)送端數(shù)據(jù)平滑發(fā)送;RemoteEstimatorProxy派生自RemoteBitrateEstimator剃毒,負(fù)責(zé)在啟用發(fā)送端碼率估計的情況下把接收端收集到的反饋信息發(fā)送回發(fā)送端病袄。</br>

由以上分析可知搂赋,WebRTC創(chuàng)建的模塊處理線程實(shí)例基本上涵蓋了音視頻數(shù)據(jù)從采集到渲染過程中的大部分?jǐn)?shù)據(jù)操作。但還有一些模塊不依賴于模塊線程工作益缠,這部分模塊是少數(shù)脑奠,本文不展開具體的描述。</br>

5 總結(jié)

</br>
</br>本文在深入分析WebRTC源代碼基礎(chǔ)上左刽,學(xué)習(xí)研究其模塊處理機(jī)制的實(shí)現(xiàn)細(xì)節(jié)捺信,為進(jìn)一步全面理解WebRTC的技術(shù)原理奠定基礎(chǔ)。</br>

</br>
</br>

參考文獻(xiàn)

[1] WebRTC基于GCC的擁塞控制(上) – 算法分析
http://www.reibang.com/p/0f7ee0e0b3be
[2] WebRTC基于GCC的擁塞控制(下) - 實(shí)現(xiàn)分析
http://www.reibang.com/p/5259a8659112

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
禁止轉(zhuǎn)載欠痴,如需轉(zhuǎn)載請通過簡信或評論聯(lián)系作者迄靠。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市喇辽,隨后出現(xiàn)的幾起案子掌挚,更是在濱河造成了極大的恐慌,老刑警劉巖菩咨,帶你破解...
    沈念sama閱讀 216,544評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件吠式,死亡現(xiàn)場離奇詭異,居然都是意外死亡抽米,警方通過查閱死者的電腦和手機(jī)特占,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,430評論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來云茸,“玉大人是目,你說我怎么就攤上這事”贽啵” “怎么了懊纳?”我有些...
    開封第一講書人閱讀 162,764評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長亡容。 經(jīng)常有香客問我嗤疯,道長,這世上最難降的妖魔是什么闺兢? 我笑而不...
    開封第一講書人閱讀 58,193評論 1 292
  • 正文 為了忘掉前任茂缚,我火速辦了婚禮,結(jié)果婚禮上列敲,老公的妹妹穿的比我還像新娘阱佛。我一直安慰自己,他們只是感情好戴而,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,216評論 6 388
  • 文/花漫 我一把揭開白布凑术。 她就那樣靜靜地躺著,像睡著了一般所意。 火紅的嫁衣襯著肌膚如雪淮逊。 梳的紋絲不亂的頭發(fā)上催首,一...
    開封第一講書人閱讀 51,182評論 1 299
  • 那天,我揣著相機(jī)與錄音泄鹏,去河邊找鬼郎任。 笑死,一個胖子當(dāng)著我的面吹牛备籽,可吹牛的內(nèi)容都是我干的舶治。 我是一名探鬼主播,決...
    沈念sama閱讀 40,063評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼车猬,長吁一口氣:“原來是場噩夢啊……” “哼霉猛!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起珠闰,我...
    開封第一講書人閱讀 38,917評論 0 274
  • 序言:老撾萬榮一對情侶失蹤惜浅,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后伏嗜,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體坛悉,經(jīng)...
    沈念sama閱讀 45,329評論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,543評論 2 332
  • 正文 我和宋清朗相戀三年承绸,在試婚紗的時候發(fā)現(xiàn)自己被綠了裸影。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,722評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡军熏,死狀恐怖空民,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情羞迷,我是刑警寧澤,帶...
    沈念sama閱讀 35,425評論 5 343
  • 正文 年R本政府宣布画饥,位于F島的核電站衔瓮,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏抖甘。R本人自食惡果不足惜热鞍,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,019評論 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望衔彻。 院中可真熱鬧薇宠,春花似錦、人聲如沸艰额。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,671評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽柄沮。三九已至回梧,卻和暖如春废岂,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背狱意。 一陣腳步聲響...
    開封第一講書人閱讀 32,825評論 1 269
  • 我被黑心中介騙來泰國打工湖苞, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人详囤。 一個月前我還...
    沈念sama閱讀 47,729評論 2 368
  • 正文 我出身青樓财骨,卻偏偏與公主長得像,于是被迫代替她去往敵國和親藏姐。 傳聞我的和親對象是個殘疾皇子隆箩,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,614評論 2 353

推薦閱讀更多精彩內(nèi)容