WebRTC:旧版 getStats() 迁移指南

Henrik Boström
Henrik Boström

旧版 getStats() WebRTC API 将在 Chrome 117 中移除,因此使用该 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:
  • 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.localCandidateIdcandidate-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(针对本地 MediaStreamTrack)和 inbound-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
仅剩的 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.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 发送的。

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

当旧版 API 在 Chrome 117 中被移除后,使用该 API 将会生成异常。如果您无法及时迁移代码,基于 RTCPeerConnection 回调的 getStats() API 的源代码试用可让已注册的网站有更多时间进行迁移。在 Chrome 121 之前,您可以使用源试用令牌继续使用旧版 getStats() API。