WebRTC:舊版 getStats() 遷移指南

Henrik Boström
Henrik Boström

Chrome 117 即將移除舊版 getStats() WebRTC API,因此必須使用這個 API 的應用程式必須遷移至標準 API。本文將說明如何遷移程式碼,以及需要更多時間進行這項變更。

WebRTC getStats() API 以往有兩種彼此競爭的版本。舊版 getStats() API 會預先標準化程序並採用回呼引數,以及經過標準化且廣為支援的 API,會傳回 promise。

標準 API 的功能更加豐富,並有公開定義的指標,詳情請參閱 W3C 規格的 WebRTC 統計資料 API 規格。規格包含本指南中每個指標的說明以及許多其他內容。

自 Chrome 117 版起,舊版 getStats() API 將在穩定版發布版本擲回例外狀況 (系統會逐步推出擲回例外狀況)。請按照本指南的說明,輕鬆轉換至標準 API。

舊版與標準統計資料類型

您可以查看規格中的 RTCStatsType 列舉,即可找到標準統計資料類型的完整清單。包括哪些統計資料字典定義描述了每種類型收集的指標。

統計資料物件全都具有 ID 屬性,可在多個 getStats() 呼叫中識別基礎物件。每次呼叫方法時,同一個物件都會有相同的 ID。這很適合用來計算指標變化率 (下節為例)。這些 ID 也可以構成參照的關係。舉例來說,outbound-rtp 統計資料物件透過 outbound-rtp.mediaSourceId 屬性參照相關聯的 media-source 統計資料物件。如果您繪製所有 ...Id 關係,就能取得圖表。

舊版 API 提供下列與標準類型對應的統計資料類型,如下所示:


舊版類型

標準類型
ssrc
代表 RTP 串流,以及相關聯 MediaStreamTrack 的指標。


設定的標準類型包括 inbound-rtp (用於接收 RTP 串流及其相關聯的遠端 MediaStreamTrack)、outbound-rtp (用於傳送 RTP 串流) 和 media-source (與傳送 RTP 串流相關聯的本機 MediaStreamTrack 指標)。RTP 串流指標也包含 RTP 串流使用的編碼器或解碼器相關資訊。
VideoBwe
頻寬估算指標、目標位元率、編碼器位元率和實際位元率。下列類型的指標屬於 RTP 指標 (outbound-rtpinbound-rtp) 和 ICE 候選組合指標 (candidate-pair)。
googComponent
代表傳輸 (ICE 和 DTLS)。標準版為 transport
localcandidate and remotecandidate
代表 ICE 候選人。標準版本為 local-candidateremote-candidate
googCandidatePair
代表 ICE 候選組合,此組合是當地和遠端候選人的組合。標準版為 candidate-pair
googCertificate
代表 DTLS 傳輸使用的憑證。標準版為 certificate
googLibjingleSession
代表 RTCPeerConnection。雖然其內容不會對應至標準中的任何內容,但標準有與 RTCPeerConnection 相關聯的類型:peer-connection

舊版 API 缺少

以下統計資料類型已加到不含任何對應的舊版 API 的標準 API 中:
  • codec:目前由 RTP 串流使用的轉碼器,用於編碼或解碼。這是 SDP 中已協商的一部分轉碼器。
  • remote-inbound-rtp:遠端端點的傳入 RTP 串流,與這個端點正在傳送 (outbound-rtp) 的傳出 RTP 串流對應。系統會在遠端端點測量結果,並在 RTCP 接收器報告 (RR) 或 RTCP 擴充報告 (XR) 中回報。
  • remote-outbound-rtp:遠端端點的輸出 RTP 串流,與此端點所接收的傳入 RTP 串流 (inbound-rtp) 相對應。在遠端端點測量數據,並記錄於 RTCP 傳送者報告 (SR) 中。
  • media-playout:與傳入 RTP 串流 (inbound-rtp) 相關聯的遠端 MediaStreamTrack 播放指標。
  • data-channel:代表 RTCDataChannel

舊版與標準指標對應

此對應的目的是協助開發人員找出與哪個標準指標對應的舊版指標,但請注意,對應的指標可能使用不同的單位,或是以總計數器表示,而非即時值。請參閱指標定義的規格。
標準 API 偏好顯示總數,而非費率。這表示如要取得舊版 API 中的對應費率 (例如位元率),應用程式必須計算兩個 getStats() 呼叫之間的差距,以計算平均速率。例如:

// Periodically (e.g. every second or every 10 seconds)...
const currReport = await pc.getStats();
// Calculate bitrate since the last getStats() call.
// Handling of undefined is omitted for clarity.
const currOutboundRtp = currReport.values().find(s => s.type == 'outbound-rtp');
const prevOutboundRtp = prevReport.get(currOutboundRtp.id);
const deltaBits = (currOutboundRtp.bytesSent - prevOutboundRtp.bytesSent) * 8;
const deltaSeconds = (currOutboundRtp.timestamp - prevOutboundRtp.timestamp) / 1000;
logBitrateMeasurement(deltaBits / deltaSeconds);
// Remember the report for next time.
prevReport = currReport;

必須自行計算費率和平均值,這看似很麻煩,但這麼做還是有利於您取得任意時間間隔的平均值。呼叫標準 API 的頻率低於採用舊版 API 的頻率,會帶來一些效能優勢。

舊版指標
googCertificate
標準通訊內容
certificate
.googFingerprint .fingerprint
.googFingerprintAlgorithm .fingerprintAlgorithm
.googDerBase64 .base64Certificate
舊版指標
googComponent
標準通訊內容
transport
.localCertificateId .localCertificateId
.remoteCertificateId .remoteCertificateId
.selectedCandidatePairId .selectedCandidatePairId
.dtlsCipher .dtlsCipher
.srtpCipher .srtpCipher
舊版指標
localcandidate
標準通訊層級:
local-candidatecandidate-pair
.stunKeepaliveRequestsSent candidate-pair.requestsSent (透過 candidate-pair.localCandidateId 反向查詢 candidate-pair)
.portNumber local-candidate.port
.networkType local-candidate.networkType
.ipAddress local-candidate.address
.stunKeepaliveResponsesReceived candidate-pair.responsesReceived
.stunKeepaliveRttTotal candidate-pair.totalRoundTripTime
.transport local-candidate.protocol
.candidateType local-candidate.candidateType
.priority local-candidate.priority
舊版指標
remotecandidate
標準通訊內容
remote-candidate
與上述 localcandidate 相同。 與上述 local-candidate 相同。
舊版指標
googCandidatePair
標準通訊內容
candidate-pair
.responsesSent candidate-pair.responsesSent
.requestsReceived candidate-pair.requestsReceived
.googRemoteCandidateType remote-candidate.candidateType
(透過
candidate-pair.remoteCandidateId 查詢「remote-candidate」)
.googReadable googReadable 是一個布林值,反映我們最近是否新增了 candidate-pair.requestsReceivedcandidate-pair.responsesReceived
.googLocalAddress local-candidate.address
(透過
candidate-pair.localCandidateId 查詢「local-candidate」)
.consentRequestsSent candidate-pair.consentRequestsSent
.googTransportType local-candidate.protocolremote-candidate.protocol 相同。
.googChannelId candidate-pair.transportId
.googLocalCandidateType local-candidate.candidateType
.googWritable googWritable 是一個布林值,反映我們最近是否調高了 candidate-pair.responsesReceived
.googRemoteAddress remote-candidate.address
.googRtt candidate-pair.currentRoundTripTime
.googActiveConnection 有效連線是指傳輸目前選取的候選組合,例如 candidate-pair.id == transport.selectedCandidatePairId
.packetsDiscardedOnSend candidate-pair.packetsDiscardedOnSend
.bytesReceived candidate-pair.bytesReceived
.responsesReceived candidate-pair.responsesReceived
.remoteCandidateId candidate-pair.remoteCandidateId
.localCandidateId candidate-pair.localCandidateId
.bytesSent candidate-pair.bytesSent
.packetsSent candidate-pair.packetsSent
.bytesReceived candidate-pair.bytesReceived
.bytesReceived candidate-pair.bytesReceived
舊版指標
ssrc
標準對應:
inbound-rtpoutbound-rtpmedia-source
.audioInputLevel media-source.audioLevel。舊版指標的值為 [0..32768],但標準規格的值為 [0..1]。
.audioOutputLevel
inbound-rtp.audioLevel。舊版指標的值為 [0..32768],但標準規格的值為 [0..1]。
.packetsLost inbound-rtp.packetsLost
.googTrackId media-source.trackIdentifier 代表本機 MediaStreamTrackinbound-rtp.trackIdentifier 代表遠端 MediaStreamTrack
.googRtt remote-inbound-rtp.roundTripTime (請參閱 outbound-rtp.remoteId)
.googEchoCancellationReturnLossEnhancement inbound-rtp.echoReturnLossEnhancement
.googCodecName 轉碼器名稱是「類型/子類型」MIME 類型的子類型 codec.mimeType (請參閱 inbound-rtp.codecIdoutbound-rtp.codecId)
.transportId inbound-rtp.transportIdoutbound-rtp.transportId
.mediaType inbound-rtp.kindoutbound-rtp.kindmedia-source.kind
.googEchoCancellationReturnLoss inbound-rtp.echoReturnLoss
.totalAudioEnergy inbound-rtp.totalAudioEnergymedia-source.totalAudioEnergy
ssrc.totalSamplesDuration inbound-rtp.totalSamplesDurationmedia-source.totalSamplesDuration
.ssrc inbound-rtp.ssrcoutbound-rtp.ssrc
.googJitterReceived inbound-rtp.jitter
.packetsSent outbound-rtp.packetsSent
.bytesSent outbound-rtp.bytesSent
.googContentType inbound-rtp.contentTypeoutbound-rtp.contentType
.googFrameWidthInput media-source.width
.googFrameHeightInput media-source.height
.googFrameRateInput media-source.framesPerSecond
.googFrameWidthSent outbound-rtp.frameWidth
.googFrameHeightSent outbound-rtp.frameHeight
.googFrameRateSent
雖然傳送每秒影格數是 outbound-rtp.framesSent 的變化率,但實際上是以 outbound-rtp.framesPerSecond 的形式實作,用於編碼 FPS。
.googFrameWidthReceived inbound-rtp.frameWidth
.googFrameHeightReceived inbound-rtp.frameHeight
.googFrameRateDecoded
inbound-rtp.framesDecoded的變化速率
.googFrameRateOutput
inbound-rtp.framesDecodedinbound-rtp.framesDropped 的變化速率
.hugeFramesSent outbound-rtp.hugeFramesSent
.qpSum

inbound-rtp.qpSumoutbound-rtp.qpSum

.framesEncoded outbound-rtp.framesEncoded
.googAvgEncodeMs

outbound-rtp.totalEncodeTime / outbound-rtp.framesEncoded

.codecImplementationName

inbound-rtp.decoderImplementationoutbound-rtp.encoderImplementation

.googCpuLimitedResolution
如果為 outbound-rtp.qualityLimitationReason == "cpu",則為 true
.googBandwidthLimitedResolution
如果為 outbound-rtp.qualityLimitationReason == "bandwidth",則為 true
.googAdaptationChanges
舊版指標會計算因 qualityLimitationReason 相關原因的解析度或影格速率變更次數。這或許是根據其他指標推斷而得 (例如傳送解析度或畫面更新率與來源解析度或影格速率不同),但相較於重新設定解析度或畫面更新率的頻率,我們限制的時間長度 (outbound-rtp.qualityLimitationDurations) 可能會更實用。
.googNacksReceived inbound-rtp.nackCount
.googNacksSent inbound-rtp.nackCount
.googPlisReceived inbound-rtp.pliCount
.googPlisSent inbound-rtp.pliCount
.googFirsReceived inbound-rtp.firCount
.googFirsSent inbound-rtp.firCount
.googSecondaryDecodedRate
含有錯誤修正的封包最近比率:inbound-rtp.fecPacketsReceived - inbound-rtp.fecPacketsDiscarded
.packetsReceived inbound-rtp.packetsReceived
.googJitterBufferMs inbound-rtp.jitterBufferDelay/inbound-rtp.jitterBufferEmittedCount
.googTargetDelayMs (影片) inbound-rtp.jitterBufferTargetDelay/inbound-rtp.jitterBufferEmittedCount
.googPreferredJitterBufferMs (音訊) inbound-rtp.jitterBufferTargetDelay/inbound-rtp.jitterBufferEmittedCount
.googExpandRate
最近隱藏的樣本比率:inbound-rtp.concealedSamples / inbound-rtp.totalSamplesReceived
.googSpeechExpandRate 串流未靜音時,已隱藏樣本的比率:(inbound-rtp.concealedSamples - inbound-rtp.silentConcealedSamples) / inbound-rtp.concealedSamples
.googAccelerateRate 為加快遊戲速度,最近捨棄的樣本比率:inbound-rtp.removedSamplesForAcceleration / inbound-rtp.totalSamplesReceived
.googPreemptiveExpandRate
最近為了降低播放速度而合成的樣本比率:inbound-rtp.insertedSamplesForDeceleration / inbound-rtp.totalSamplesReceived
.googSecondaryDiscardedRate inbound-rtp.fecPacketsDiscarded
.bytesReceived inbound-rtp.bytesReceived
s.googCurrentDelayMs inbound-rtp.jitterBufferDelay + media-playout.totalPlayoutDelay
.googDecodeMs inbound-rtp.totalDecodeTime / inbound-rtp.framesDecoded
.googTimingFrameInfo
僅剩的 goog-metric。inbound-rtp.googTimingFrameInfo
.framesDecoded inbound-rtp.framesDecoded
舊版指標
VideoBwe
標準通訊端:
outbound-rtpcandidate-pair
.googTargetEncBitrate
outbound-rtp.targetBitrate 做為即時值,或 outbound-rtp.totalEncodedBytesTarget / outbound-rtp.framesEncoded 做為平均值
.googActualEncBitrate 編碼器產生的位元組是酬載位元組 (不包含重新傳輸):outbound-rtp.bytesSentoutbound-rtp.retransmittedBytesSent 的變化速率
.googBucketDelay outbound-rtp.totalPacketSendDelay/outbound-rtp.packetsSent
.googTransmitBitrate 每個 RTP 串流位元率 outbound-rtp.headerBytesSent + outbound-rtp.bytesSent 變更速率,candidate-pair.bytesSent 代表個別 ICE 候選位元率,或 transport.bytesSent (按傳輸位元率) 的變化速率
.googRetransmitBitrate outbound-rtp.retransmittedBytesSent的變化範圍
.googAvailableSendBandwidth candidate-pair.availableOutgoingBitrate
.googAvailableReceiveBandwidth candidate-pair.availableIncomingBitrate

標準 API 具備模擬感知功能

如果您使用模擬功能,可能會發現舊版 API 只會回報單一 SSRC,即使您使用模擬傳送三個不同的 SSRC 來傳送三個 RTP 串流也一樣。

標準 API 不會分享這項限制,而是會傳回三個 outbound-rtp 統計資料物件 (每個 SSRC 各一個)。換句話說,您可以個別分析每個 RTP 串流,也代表您需要自行匯總所有 RTP 傳送串流的總位元率。

另一方面,透過 scalabilityMode API 設定多個空間層的 SVC 串流或 RTP 串流仍會顯示為單一 outbound-rtp,因為這些項目是透過單一 SSRC 傳送。

如果需要更多時間遷移

在 Chrome 117 中移除舊版 API 後,使用該 API 就會產生例外狀況。如果無法及時遷移程式碼,RTCPeerConnection 回呼式 getStats() API 的來源試用可為已註冊的網站提供更多遷移時間。透過來源試用權杖,在 Chrome 121 版之前,您可以繼續使用舊版 getStats() API。