WebRTC getStats詳解 - 從標(biāo)準(zhǔn)、調(diào)用到實(shí)現(xiàn)

前言

getStats是WebRTC一個(gè)非常重要的API守伸,用來向開發(fā)者和用戶導(dǎo)出WebRTC運(yùn)行時(shí)狀態(tài)信息绎秒,包括網(wǎng)絡(luò)數(shù)據(jù)接收和發(fā)送狀態(tài)、P2P客戶端媒體數(shù)據(jù)采集和渲染狀態(tài)等[1]尼摹。這些信息對(duì)于監(jiān)控WebRTC運(yùn)行狀態(tài)见芹、排除程序錯(cuò)誤等非常重要剂娄。<br />
本文首先描述W3C定義的getStats標(biāo)準(zhǔn),然后展示如何在JS層調(diào)用getStats玄呛,最后深入分析WebRTC源代碼中g(shù)etStats的實(shí)現(xiàn)阅懦。全文從標(biāo)準(zhǔn)到實(shí)現(xiàn),全方位透徹展示getStats的細(xì)節(jié)徘铝。<br />

一 getStats標(biāo)準(zhǔn)

getStats的標(biāo)準(zhǔn)由W3C定義耳胎,其接口很簡(jiǎn)單,但是卻返回豐富的WebRTC運(yùn)行時(shí)信息惕它。其返回信息的主要內(nèi)容如下[2]:<br />

  1. 發(fā)送端采集統(tǒng)計(jì):對(duì)應(yīng)于媒體數(shù)據(jù)的產(chǎn)生怕午,包括幀率,幀大小淹魄,媒體數(shù)據(jù)源的時(shí)鐘頻率郁惜,編解碼器名稱,等等揭北。<br />
  2. 發(fā)送端RTP統(tǒng)計(jì):對(duì)應(yīng)于媒體數(shù)據(jù)的發(fā)送扳炬,包括發(fā)送數(shù)據(jù)包數(shù),發(fā)送字節(jié)數(shù)搔体,往返時(shí)間RTT,等等半醉。<br />
  3. 接收端RTP統(tǒng)計(jì):對(duì)應(yīng)于媒體數(shù)據(jù)的接收疚俱,包括接收數(shù)據(jù)包數(shù),接收字節(jié)數(shù)缩多,丟棄數(shù)據(jù)包數(shù)呆奕,丟失數(shù)據(jù)包數(shù),網(wǎng)絡(luò)抖動(dòng)jitter衬吆,等等梁钾。<br />
  4. 接收端渲染統(tǒng)計(jì):對(duì)應(yīng)于媒體數(shù)據(jù)的渲染,包括丟棄幀數(shù)逊抡,丟失幀數(shù)姆泻,渲染幀數(shù),渲染延遲冒嫡,等等拇勃。<br />

另外還有一些雜項(xiàng)統(tǒng)計(jì),如DataChannel度量孝凌,網(wǎng)絡(luò)接口度量方咆,證書統(tǒng)計(jì)等等。在眾多信息中蟀架,有一些反映WebRTC運(yùn)行狀態(tài)的核心度量瓣赂,包括往返時(shí)間RTT榆骚,丟包率和接收端延遲等,分別表述如下:<br />

  • 往返時(shí)間RTT:表示數(shù)據(jù)在網(wǎng)絡(luò)上傳輸所用的時(shí)間煌集,一般通過RTCP 的SR/RR數(shù)據(jù)包中的相關(guān)域進(jìn)行計(jì)算寨躁。該度量直接反映網(wǎng)絡(luò)狀況的好壞。<br />
  • 丟包率影響接收端音視頻質(zhì)量牙勘,在嚴(yán)重的情況下可能導(dǎo)致聲音跳變或者視頻馬賽克职恳,從側(cè)面反映網(wǎng)絡(luò)狀況的好壞。<br />
  • 音視頻數(shù)據(jù)到達(dá)接收端之后方面,要經(jīng)歷收包放钦、解碼、渲染等過程恭金,該過程會(huì)帶來延遲操禀。接收端延遲是數(shù)據(jù)從采集到渲染單向延遲的重要組成部分。<br />

通過以上分析可知横腿,getStats的返回信息包含WebRTC數(shù)據(jù)管線的各個(gè)階段的統(tǒng)計(jì)信息颓屑,從數(shù)據(jù)采集、編碼到發(fā)送耿焊,再到數(shù)據(jù)接收揪惦、解碼和渲染。這為監(jiān)控WebRTC應(yīng)用的運(yùn)行狀態(tài)提供第一手?jǐn)?shù)據(jù)罗侯。<br />

二 使用JS調(diào)用getStats

getStats的JS API很簡(jiǎn)單器腋, W3C規(guī)定getStats的JS API函數(shù)PTCPeerConnection.getStats需要三個(gè)參數(shù):一個(gè)可為空的MediaStreamTrack對(duì)象,一個(gè)調(diào)用成功時(shí)的回調(diào)函數(shù)和一個(gè)調(diào)用失敗時(shí)的回調(diào)函數(shù)钩杰。成功回調(diào)函數(shù)的參數(shù)為getStats得到的RTCStatsReport纫塌,主要工作就發(fā)生在解析RTCStatsReport上,拿到我們感興趣的參數(shù)讲弄,進(jìn)而分析應(yīng)用的運(yùn)行狀態(tài)措左。<br />
下面我們選取W3C標(biāo)準(zhǔn)上給出的例子作簡(jiǎn)單講解[1]糖儡。假設(shè)當(dāng)前會(huì)話的通話質(zhì)量很差吼砂,我們想知道是不是由于丟包率過大引起的薄扁。因此饰迹,我們可以通過getStats返回結(jié)果的outbound-rtp中的丟包數(shù)和收包數(shù)計(jì)算丟包率埠褪,然后進(jìn)行判斷搏存。具體代碼實(shí)現(xiàn)如下:<br />

var baselineReport, currentReport;
var selector = pc.getRemoteStreams()[0].getAudioTracks()[0];
pc.getStats(selector, function (report) {
    baselineReport = report;
}, logError);

setTimeout(function () {
    pc.getStats(selector, function (report) {
        currentReport = report;
        processStats();
    }, logError);
}, aBit);

function processStats() {
    for (var i in currentReport) {
        var now = currentReport[i];
        if (now.type != "outbund-rtp")
            continue;

        base = baselineReport[now.id];
        if (base) {
            remoteNow = currentReport[now.associateStatsId];
            remoteBase = baselineReport[base.associateStatsId];
            var packetsSent = now.packetsSent - base.packetsSent;
            var packetsReceived = remoteNow.packetsReceived – 
                   remoteBase.packetsReceived;

            // if fractionLost is > 0.3, we have probably found the culprit
            var fractionLost = (packetsSent - packetsReceived) / packetsSent;
        }
    }
}
function logError(error) {
log(error.name + ": " + error.message);
}

通過上述例子横朋,我們可以體會(huì)到從JS層調(diào)用getStats分析應(yīng)用運(yùn)行狀態(tài)的基本流程藐俺。值得注意的是赏壹,Chrome和Firefox兩款瀏覽器在調(diào)用方面有稍微差別鱼炒,具體請(qǐng)參考文檔[3]。<br />

三 getStats在WebRTC內(nèi)部的實(shí)現(xiàn)

JS層的getStats調(diào)用如何傳遞到到WebRTC內(nèi)部的實(shí)現(xiàn)函數(shù)蝌借,涉及到瀏覽器的內(nèi)部工作原理昔瞧,具體到Chrome瀏覽器來講指蚁,是由WebKit,V8自晰,Content凝化,libjingle等模塊一起協(xié)同工作實(shí)現(xiàn)。本節(jié)我們不討論這里面的細(xì)節(jié)酬荞,我們只關(guān)注getStats在WebRTC內(nèi)部的實(shí)現(xiàn)搓劫。<br />

WebRTC模塊對(duì)外提供兩個(gè)重要對(duì)象:PeerConnectionFactory和PeerConnection,前者負(fù)責(zé)一系列重要對(duì)象的創(chuàng)建混巧,如MediaStream枪向,MediaSource,MediaTrack等等咧党,后者則負(fù)責(zé)P2P連接的建立和維護(hù)秘蛔,包括CreateOffer/Answer,AddStream等操作傍衡。監(jiān)控P2P連接運(yùn)行狀態(tài)GetStats函數(shù)深员,自然在PeerConnection對(duì)象中實(shí)現(xiàn),而該對(duì)象把任務(wù)委托給成員變量StatsCollector對(duì)象的UpdateStats函數(shù)來實(shí)現(xiàn):<br />

void StatsCollector::UpdateStats(PeerConnectionInterface::StatsOutputLevel level) {
  RTC_DCHECK(pc_->session()->signaling_thread()->IsCurrent());  // 由signal線程調(diào)用蛙埂;
  double time_now = GetTimeNow();
  const double kMinGatherStatsPeriod = 50;
  if (stats_gathering_started_ != 0 &&
      stats_gathering_started_ + kMinGatherStatsPeriod > time_now) {
    return;  // 調(diào)用間隔不低于50ms倦畅;
  }

  stats_gathering_started_ = time_now;
  if (pc_->session()) {
    ExtractSessionInfo();      // 收集傳輸信息;
    ExtractVoiceInfo();       // 收集VoiceChannel信息箱残;
    ExtractVideoInfo(level);   // 收集VideoChannel信息滔迈;
    ExtractSenderInfo();      // 收集PeerConnection的sender信息;
    ExtractDataInfo();        // 收集DataChannel信息被辑;
    UpdateTrackReports();    // 更新Track報(bào)告;
  }
}

由該函數(shù)我們可以看到敬惦,信息的收集是分模塊進(jìn)行的盼理,其中最重要的是四個(gè)模塊的信息:Transport,VoiceChannel俄删,VideoChannel宏怔,DataChannel。顧名思義畴椰,Transport是和網(wǎng)絡(luò)相關(guān)的統(tǒng)計(jì)信息臊诊,而其余三個(gè)是和各自MediaChannel相關(guān)的統(tǒng)計(jì)信息。<br />

Extract系列函數(shù)從相應(yīng)模塊收集到信息后斜脂,執(zhí)行后處理操作抓艳,把不同類型的信息重新組織為類型相同的StatsReport對(duì)象,存儲(chǔ)到StatsCollector的列表中帚戳。StatsReport對(duì)象結(jié)構(gòu)基本定義如下:<br />

struct StatsReport {
  const Id id_;       // 包括類型玷或,唯一標(biāo)示符等信息儡首;
  double timestamp_;  // 本次信息收集的開始時(shí)間;
  Values values_;     // 信息集合偏友,可存儲(chǔ)int, int64, string, bool, double等類型
};

下面以ExtractVideoInfo為例分析信息收集過程:<br />

void StatsCollector::ExtractVideoInfo(PeerConnectionInterface::StatsOutputLevel level) {
  cricket::VideoMediaInfo video_info;
  // 從video channel收集信息蔬胯,包括發(fā)送端,接收端和帶寬估計(jì)信息位他;
  if (!pc_->session()->video_channel()->GetStats(&video_info)) {
    return;
  }
  // 收集到的信息歸一化為StatsReport對(duì)象氛濒;
  ExtractStatsFromList(video_info.receivers, transport_id, this,
      StatsReport::kReceive);
  ExtractStatsFromList(video_info.senders, transport_id, this,
      StatsReport::kSend);
    ExtractStats(video_info.bw_estimations[0], stats_gathering_started_, level, report);
}

從videochannel收集到的數(shù)據(jù)來自三個(gè)模塊:VideoSendStream,VideoReceiveStream 和Call鹅髓,這三個(gè)模塊分別從自己的信息統(tǒng)計(jì)對(duì)象中獲得統(tǒng)計(jì)數(shù)據(jù)舞竿,最后匯總為VideoMediaInfo對(duì)象,由ExtractStatsFromXX系列函數(shù)歸一化為StatsReport對(duì)象迈勋。<br />

以上分析的即為getStats函數(shù)的內(nèi)部實(shí)現(xiàn)細(xì)節(jié)炬灭。需要注意的是,getStats只負(fù)責(zé)拉取統(tǒng)計(jì)數(shù)據(jù)靡菇,而統(tǒng)計(jì)數(shù)據(jù)本身則由WebRTC內(nèi)部各個(gè)模塊周期性更新重归,這個(gè)過程是異步的。例如厦凤,傳輸層的RTT是由網(wǎng)絡(luò)線程收到數(shù)據(jù)包后實(shí)時(shí)更新鼻吮,而帶寬估計(jì)信息則是在受到RTCP報(bào)文后解析計(jì)算得到。下面以VideoReceiveStream統(tǒng)計(jì)信息的更新過程為例较鼓,深入分析這部分是如何協(xié)同工作的:<br />

VideoReceiveStream的數(shù)據(jù)更新和拉取.png

在Video接收端椎木,network/decoder/render三個(gè)線程在各自工作完成后,都會(huì)更新相應(yīng)的統(tǒng)計(jì)數(shù)據(jù)到timing對(duì)象中博烂。而module process線程則周期性更新Stats proxy對(duì)象香椎,該對(duì)象從timing對(duì)象中拉取數(shù)據(jù),保存在自己的stats成員變量中禽篱。最后畜伐,getstats線程調(diào)用流程到達(dá)stats proxy對(duì)象,獲取stats數(shù)據(jù)而返躺率。工作線程玛界、更新線程和拉取線程共同協(xié)同工作完成統(tǒng)計(jì)數(shù)據(jù)的產(chǎn)生、更新和拉取悼吱。<br />

四 總結(jié)

本文從標(biāo)準(zhǔn)慎框、使用和實(shí)現(xiàn)三個(gè)方面全方位分析了WebRTC的getStats API,這對(duì)WebRTC應(yīng)用的運(yùn)行時(shí)監(jiān)控和狀態(tài)分析排錯(cuò)具有重要意義后添,我們從另一角度對(duì)WebRTC有了更深入的理解笨枯。<br />
<br />

參考文獻(xiàn)

[1] Identifiers for WebRTC's Statistics API:
???https://www.w3.org/TR/webrtc-stats/
[2] Basics of WebRTC getStats() API:
???https://www.callstats.io/2015/07/06/basics-webrtc-getstats-api/
[3] RTCPeerConnection.getStats: Chrome VS Firefox:
http://blog.telenor.io/webrtc/2015/06/11/getstats-chrome-vs-firefox.html

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
禁止轉(zhuǎn)載,如需轉(zhuǎn)載請(qǐng)通過簡(jiǎn)信或評(píng)論聯(lián)系作者。
  • 序言:七十年代末猎醇,一起剝皮案震驚了整個(gè)濱河市窥突,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌硫嘶,老刑警劉巖阻问,帶你破解...
    沈念sama閱讀 216,402評(píng)論 6 499
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異沦疾,居然都是意外死亡称近,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,377評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門哮塞,熙熙樓的掌柜王于貴愁眉苦臉地迎上來刨秆,“玉大人,你說我怎么就攤上這事忆畅『馕矗” “怎么了?”我有些...
    開封第一講書人閱讀 162,483評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵家凯,是天一觀的道長(zhǎng)缓醋。 經(jīng)常有香客問我,道長(zhǎng)绊诲,這世上最難降的妖魔是什么送粱? 我笑而不...
    開封第一講書人閱讀 58,165評(píng)論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮掂之,結(jié)果婚禮上抗俄,老公的妹妹穿的比我還像新娘。我一直安慰自己世舰,他們只是感情好动雹,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,176評(píng)論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著跟压,像睡著了一般洽胶。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上裆馒,一...
    開封第一講書人閱讀 51,146評(píng)論 1 297
  • 那天,我揣著相機(jī)與錄音丐怯,去河邊找鬼喷好。 笑死,一個(gè)胖子當(dāng)著我的面吹牛读跷,可吹牛的內(nèi)容都是我干的梗搅。 我是一名探鬼主播,決...
    沈念sama閱讀 40,032評(píng)論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼无切!你這毒婦竟也來了荡短?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,896評(píng)論 0 274
  • 序言:老撾萬榮一對(duì)情侶失蹤哆键,失蹤者是張志新(化名)和其女友劉穎掘托,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體籍嘹,經(jīng)...
    沈念sama閱讀 45,311評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡闪盔,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,536評(píng)論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了辱士。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片泪掀。...
    茶點(diǎn)故事閱讀 39,696評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖颂碘,靈堂內(nèi)的尸體忽然破棺而出异赫,到底是詐尸還是另有隱情,我是刑警寧澤头岔,帶...
    沈念sama閱讀 35,413評(píng)論 5 343
  • 正文 年R本政府宣布塔拳,位于F島的核電站,受9級(jí)特大地震影響切油,放射性物質(zhì)發(fā)生泄漏蝙斜。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,008評(píng)論 3 325
  • 文/蒙蒙 一澎胡、第九天 我趴在偏房一處隱蔽的房頂上張望孕荠。 院中可真熱鬧,春花似錦攻谁、人聲如沸稚伍。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)个曙。三九已至,卻和暖如春受楼,著一層夾襖步出監(jiān)牢的瞬間垦搬,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,815評(píng)論 1 269
  • 我被黑心中介騙來泰國(guó)打工艳汽, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留猴贰,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,698評(píng)論 2 368
  • 正文 我出身青樓河狐,卻偏偏與公主長(zhǎng)得像米绕,于是被迫代替她去往敵國(guó)和親瑟捣。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,592評(píng)論 2 353

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

  • 翻譯?http://www.html5rocks.com/en/tutorials/webrtc/infrastr...
    bktmkd閱讀 6,310評(píng)論 1 28
  • 一 前言 RTP/RTCP協(xié)議是流媒體通信的基石栅干。RTP協(xié)議定義流媒體數(shù)據(jù)在互聯(lián)網(wǎng)上傳輸?shù)臄?shù)據(jù)包格式迈套,而RTCP協(xié)...
    weizhenwei閱讀 33,310評(píng)論 4 48
  • 本文在文章[1]的基礎(chǔ)上,從源代碼實(shí)現(xiàn)角度對(duì)WebRTC的GCC算法進(jìn)行分析碱鳞。主要內(nèi)容包括: RTCP RR的數(shù)據(jù)...
    weizhenwei閱讀 11,845評(píng)論 8 20
  • 對(duì)于實(shí)時(shí)音視頻應(yīng)用來講桑李,媒體數(shù)據(jù)從采集到渲染,在數(shù)據(jù)流水線上依次完成一系列處理劫笙。流水線由不同的功能模塊組成芙扎,彼此分...
    weizhenwei閱讀 11,061評(píng)論 4 29
  • 不知如何下筆戒洼,但感覺只有文字才能詮釋我現(xiàn)在的心情,我感覺我的婚姻是因?yàn)楹⒆釉趽沃驶瑑蓚€(gè)人之間出了xx會(huì)摟在一起圈浇,其...
    默念曾經(jīng)閱讀 148評(píng)論 0 0