WebRTC:旧版 getStats() 迁移指南

Henrik Boström
Henrik Boström

旧版 getStats() WebRTC API 将在 Chrome 117 中移除,因此使用该 API 的应用需要迁移到标准 API。本文介绍了如何迁移代码,以及如果您需要更多时间来进行更改,该怎么做。

WebRTC getStats() API 在过去有两个竞争版本。旧版 getStats() API(该 API 早于标准化过程并采用回调参数),以及受到广泛支持的标准化 API(会返回 promise)。

该标准 API 的功能更加丰富,并且具有明确定义的指标,详见 W3C 规范 WebRTC Statistics 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 中,它们没有任何对应的旧版类型:
  • codec:RTP 流当前正用于编码或解码的编解码器。这是 SDP 中已商定的部分编解码器。
  • remote-inbound-rtp:远程端点的入站 RTP 流,与此端点发送的出站 RTP 流 (outbound-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 编解码器名称是“type/subtype”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
虽然发送 FPS 是 outbound-rtp.framesSent 的变化率,但实际上是以 outbound-rtp.framesPerSecond(编码 FPS)的形式实现的。
.googFrameWidthReceived inbound-rtp.frameWidth
.googFrameHeightReceived inbound-rtp.frameHeight
.googFrameRateDecoded
inbound-rtp.framesDecoded的变化率
.googFrameRateOutput
inbound-rtp.framesDecoded - inbound-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
剩下的唯一一个 Google 指标。inbound-rtp.googTimingFrameInfo
.framesDecoded inbound-rtp.framesDecoded
旧版指标
VideoBwe
标准通信
outbound-rtpcandidate-pair
.googTargetEncBitrate
outbound-rtp.targetBitrate(瞬时值)或 outbound-rtp.totalEncodedBytesTarget / outbound-rtp.framesEncoded(平均值)
.googActualEncBitrate 编码器生成的字节是载荷字节(不包括重新传输):outbound-rtp.bytesSent - outbound-rtp.retransmittedBytesSent 的变化率
.googBucketDelay outbound-rtp.totalPacketSendDelay/outbound-rtp.packetsSent
.googTransmitBitrate outbound-rtp.headerBytesSent + outbound-rtp.bytesSent(对于每个 RTP 流的比特率)、candidate-pair.bytesSent(对于每个 ICE 候选比特率)或 transport.bytesSent(对于每个传输的比特率)
.googRetransmitBitrate outbound-rtp.retransmittedBytesSent的变化范围
.googAvailableSendBandwidth candidate-pair.availableOutgoingBitrate
.googAvailableReceiveBandwidth candidate-pair.availableIncomingBitrate

标准 API 具有联播感知能力

如果您使用联播,您可能已经注意到,即使您使用联播通过三个单独的 SSRC 发送(举例)三个 RTP 流,旧版 API 也只报告一个 SSRC。

标准 API 没有这种限制,会返回三个 outbound-rtp 统计信息对象,每个 SSRC 一个对象。这意味着您可以单独分析每个 RTP 流,但这也意味着要获取所有 RTP 发送流的总比特率,您需要自行聚合它们。

另一方面,带有通过 scalabilityMode API 配置多个空间层的 SVC 流或 RTP 流仍显示为单个 outbound-rtp,因为这些流通过单个 SSRC 发送。

如果您需要更多时间进行迁移

在 Chrome 117 中移除旧版 API 后,使用旧版 API 会引发异常。如果您无法及时迁移代码,不妨通过基于 RTCPeerConnection 回调的 getStats() API 的源试用为已注册网站留出更多时间进行迁移。借助源试用令牌,可在 Chrome 121 之前继续使用旧版 getStats() API。