WebRTC: Legacy-Migrationsanleitung für getStats()

Henrik Boström
Henrik Boström

Die alte getStats() WebRTC API wird in Chrome 117 entfernt. Daher müssen Apps, die sie verwenden, zur Standard-API migriert werden. In diesem Artikel erfahren Sie, wie Sie Ihren Code migrieren und was Sie tun können, wenn Sie mehr Zeit für diese Änderung benötigen.

In der Vergangenheit gab es zwei konkurrierende Versionen der WebRTC getStats() API. Die alte getStats()-API, die aus der Zeit vor dem Standardisierungsprozess stammt und ein Callback-Argument verwendet, und die standardisierte und allgemein unterstützte API, die ein Promise zurückgibt.

Die Standard-API bietet mehr Funktionen und klar definierte Messwerte, die öffentlich in der W3C-Spezifikation Identifiers for WebRTC's Statistics API dokumentiert sind. Die Spezifikation enthält unter anderem Beschreibungen aller in diesem Leitfaden aufgeführten Messwerte.

Ab Chrome 117 löst die alte getStats() API eine Ausnahme in der stabilen Release-Version aus. Diese wird nach und nach eingeführt. Folgen Sie dieser Anleitung, um den Übergang zur Standard-API zu erleichtern.

Bisherige und Standard-Statistiktypen

Die vollständige Liste der Standard-Statistiktypen finden Sie in der RTCStatsType-Enum in der Spezifikation. Dazu gehört, welche Definition des Statistikwörterbuchs die für jeden Typ erfassten Messwerte beschreibt.

Alle Statistikobjekte haben ein ID-Attribut, das das zugrunde liegende Objekt über mehrere getStats()-Aufrufe hinweg eindeutig identifiziert. Dasselbe Objekt hat bei jedem Aufruf der Methode dieselbe ID. Dies ist nützlich, um die Änderungsrate von Messwerten zu berechnen. Im nächsten Abschnitt finden Sie ein Beispiel. Die IDs bilden auch Beziehungen von Referenzen. Beispielsweise verweist das Statistikobjekt outbound-rtp über das Attribut outbound-rtp.mediaSourceId auf das zugehörige Statistikobjekt media-source. Wenn Sie alle ...Id-Beziehungen zeichnen, erhalten Sie ein Diagramm.

Die alte API hat die folgenden Statistiktypen, die den Standardtypen wie folgt entsprechen:


Alterstyp

Standardtyp
ssrc
Steht für einen RTP-Stream und Messwerte zum zugehörigen MediaStreamTrack.


Die Standardtypen dafür sind inbound-rtp (für RTP-Empfangsstreams und die zugehörige Remote-MediaStreamTrack), outbound-rtp (für RTP-Sendestreams) und media-source (für lokale MediaStreamTrack-Messwerte, die mit einem RTP-Sendestream verknüpft sind). RTP-Stream-Messwerte enthalten auch Informationen zum Encoder oder Decoder, der vom RTP-Stream verwendet wird.
VideoBwe
Messwerte zur Bandbreitenschätzung, Ziel-Bitrate, Encoder-Bitrate und tatsächliche Bitrate. Diese Messwerte sind Teil der RTP-Messwerte (outbound-rtp und inbound-rtp) und der ICE-Kandidatenpaare (candidate-pair).
googComponent
Repräsentiert den Transport (ICE und DTLS). Die Standardversion ist transport.
localcandidate and remotecandidate
Repräsentiert einen ICE-Kandidaten. Die Standardversion ist local-candidate und remote-candidate.
googCandidatePair
Stellt ein ICE-Kandidatenpaar dar, bei dem es sich um ein Paar aus einem lokalen und einem Remote-Kandidaten handelt. Die Standardversion ist candidate-pair.
googCertificate
Stellt ein Zertifikat dar, das vom DTLS-Transport verwendet wird. Die Standardversion ist certificate.
googLibjingleSession
Steht für RTCPeerConnection. Der Inhalt ist im Standard zwar nicht zugeordnet, aber der Standard hat einen Typ, der mit RTCPeerConnection verknüpft ist: peer-connection.

Fehlt in der Legacy-API

Diese Statistiktypen wurden der Standard-API hinzugefügt, für die es keinen entsprechenden Legacy-Typ gibt:
  • codec: Ein Codec, der derzeit von einem RTP-Stream entweder zur Codierung oder zur Decodierung verwendet wird. Dies ist eine Untergruppe der Codecs, die im SDP ausgehandelt wurden.
  • remote-inbound-rtp: Eingehender RTP-Stream eines Remote-Endpunkts, der einem ausgehenden RTP-Stream von diesem Endpunkt entspricht (outbound-rtp). Er wird am Remote-Endpunkt gemessen und in einem RTCP Receiver Report (RR) oder RTCP Extended Report (XR) gemeldet.
  • remote-outbound-rtp: Der ausgehende RTP-Stream eines Remote-Endpunkts, der einem eingehenden RTP-Stream entspricht, den dieser Endpunkt empfängt (inbound-rtp). Er wird am Remote-Endpunkt gemessen und in einem RTCP-Absenderbericht (SR) gemeldet.
  • media-playout: Messwerte zum Playout eines Remote-MediaStreamTrack, der einem eingehenden RTP-Stream (inbound-rtp) zugeordnet ist.
  • data-channel: Stellt eine RTCDataChannel dar.

Zuordnung von Legacy- zu Standardmesswerten

Diese Zuordnung soll Entwicklern helfen, herauszufinden, welcher alte Messwert welchem Standardmesswert entspricht. Für den entsprechenden Messwert können jedoch andere Einheiten verwendet oder als Gesamtzähler und nicht als momentaner Wert ausgedrückt werden. Die Messwertdefinitionen finden Sie in der Spezifikation.
Die Standard-API bevorzugt die Angabe von Gesamtzählern anstelle von Preisen. Das bedeutet: Um die entsprechende Rate (z. B. die Bitrate) wie in der Legacy-API zu erhalten, muss die App die durchschnittliche Rate anhand der Differenz zwischen zwei getStats()-Aufrufen berechnen. Beispiel:

// 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;

Raten und Durchschnittswerte selbst berechnen zu müssen, mag wie ein umständlicher zusätzlicher Schritt erscheinen, bietet aber den Vorteil, dass Sie über jedes gewünschte Zeitintervall Durchschnittswerte ermitteln können. Wenn die Standard-API seltener aufgerufen wird, als dies bei der alten API sonst der Fall wäre, hat dies einige Leistungsvorteile.

Legacy-Messwert
googCertificate
Standardkorrespondenz
certificate
.googFingerprint .fingerprint
.googFingerprintAlgorithm .fingerprintAlgorithm
.googDerBase64 .base64Certificate
Legacy-Messwert
googComponent
Standardkorrespondenz
transport
.localCertificateId .localCertificateId
.remoteCertificateId .remoteCertificateId
.selectedCandidatePairId .selectedCandidatePairId
.dtlsCipher .dtlsCipher
.srtpCipher .srtpCipher
Legacy-Messwert
localcandidate
Standardkorrespondenz
local-candidate oder candidate-pair
.stunKeepaliveRequestsSent candidate-pair.requestsSent (umgekehrte Suche candidate-pair über candidate-pair.localCandidateId)
.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
Legacy-Messwert
remotecandidate
Standardkorrespondenz
remote-candidate
Wie bei localcandidate oben. Wie bei local-candidate oben.
Legacy-Messwert
googCandidatePair
Standardkorrespondenz
candidate-pair
.responsesSent candidate-pair.responsesSent
.requestsReceived candidate-pair.requestsReceived
.googRemoteCandidateType remote-candidate.candidateType
(remote-candidate über
candidate-pair.remoteCandidateId suchen)
.googReadable googReadable ist ein boolescher Wert, der angibt, ob vor Kurzem candidate-pair.requestsReceived oder candidate-pair.responsesReceived erhöht wurde.
.googLocalAddress local-candidate.address
(local-candidate über
candidate-pair.localCandidateId suchen)
.consentRequestsSent candidate-pair.consentRequestsSent
.googTransportType Wie bei local-candidate.protocol und remote-candidate.protocol.
.googChannelId candidate-pair.transportId
.googLocalCandidateType local-candidate.candidateType
.googWritable googWritable ist ein boolescher Wert, der angibt, ob kürzlich candidate-pair.responsesReceived erhöht wurde oder nicht.
.googRemoteAddress remote-candidate.address
.googRtt candidate-pair.currentRoundTripTime
.googActiveConnection Die aktive Verbindung bezieht sich auf das Kandidatenpaar, das derzeit durch den Transport ausgewählt wird, z. B. wenn 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
Legacy-Messwert
ssrc
Standardkorrespondenz
inbound-rtp, outbound-rtp, media-source
.audioInputLevel media-source.audioLevel. Der Legacy-Messwert liegt im Bereich [0–32768], der Standardmesswert im Bereich [0–1].
.audioOutputLevel
inbound-rtp.audioLevel Der Legacy-Messwert liegt im Bereich [0–32768], der Standardmesswert im Bereich [0–1].
.packetsLost inbound-rtp.packetsLost
.googTrackId media-source.trackIdentifier für lokale MediaStreamTracks und inbound-rtp.trackIdentifier für Remote-MediaStreamTracks
.googRtt remote-inbound-rtp.roundTripTime (siehe outbound-rtp.remoteId)
.googEchoCancellationReturnLossEnhancement inbound-rtp.echoReturnLossEnhancement
.googCodecName Der Codec-Name ist der Subtyp des MIME-Typs „type/subtype“, codec.mimeType (siehe inbound-rtp.codecId und outbound-rtp.codecId).
.transportId inbound-rtp.transportId und outbound-rtp.transportId
.mediaType inbound-rtp.kind und outbound-rtp.kind oder media-source.kind
.googEchoCancellationReturnLoss inbound-rtp.echoReturnLoss
.totalAudioEnergy inbound-rtp.totalAudioEnergy und media-source.totalAudioEnergy
ssrc.totalSamplesDuration inbound-rtp.totalSamplesDuration und media-source.totalSamplesDuration
.ssrc inbound-rtp.ssrc und outbound-rtp.ssrc
.googJitterReceived inbound-rtp.jitter
.packetsSent outbound-rtp.packetsSent
.bytesSent outbound-rtp.bytesSent
.googContentType inbound-rtp.contentType und outbound-rtp.contentType
.googFrameWidthInput media-source.width
.googFrameHeightInput media-source.height
.googFrameRateInput media-source.framesPerSecond
.googFrameWidthSent outbound-rtp.frameWidth
.googFrameHeightSent outbound-rtp.frameHeight
.googFrameRateSent
Obwohl die fps beim Senden die Änderungsrate von outbound-rtp.framesSent ist, wird dies tatsächlich als outbound-rtp.framesPerSecond implementiert, das die fps codiert.
.googFrameWidthReceived inbound-rtp.frameWidth
.googFrameHeightReceived inbound-rtp.frameHeight
.googFrameRateDecoded
Die Änderungsrate von inbound-rtp.framesDecoded
.googFrameRateOutput
Die Änderungsrate von inbound-rtp.framesDecoded bis inbound-rtp.framesDropped
.hugeFramesSent outbound-rtp.hugeFramesSent
.qpSum

inbound-rtp.qpSum und outbound-rtp.qpSum

.framesEncoded outbound-rtp.framesEncoded
.googAvgEncodeMs

outbound-rtp.totalEncodeTime / outbound-rtp.framesEncoded

.codecImplementationName

inbound-rtp.decoderImplementation und outbound-rtp.encoderImplementation

.googCpuLimitedResolution
„True“, wenn outbound-rtp.qualityLimitationReason == "cpu"
.googBandwidthLimitedResolution
„True“, wenn outbound-rtp.qualityLimitationReason == "bandwidth"
.googAdaptationChanges
Mit dem Legacy-Messwert wird gezählt, wie oft sich die Auflösung oder Framerate aus qualityLimitationReason Gründen geändert hat. Aus anderen Messwerten lässt sich schließen, z. B. die Auflösung oder die Framerate beim Senden, die von der Quellauflösung oder Framerate abweicht. Allerdings ist die von uns beschränkte Dauer (outbound-rtp.qualityLimitationDurations) möglicherweise nützlicher als die Häufigkeit, mit der die Auflösung oder die Framerate neu konfiguriert wurde.
.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
Der aktuelle Anteil der Pakete mit Fehlerkorrekturen: inbound-rtp.fecPacketsReceivedinbound-rtp.fecPacketsDiscarded
.packetsReceived inbound-rtp.packetsReceived
.googJitterBufferMs inbound-rtp.jitterBufferDelay / inbound-rtp.jitterBufferEmittedCount
.googTargetDelayMs (Video) inbound-rtp.jitterBufferTargetDelay / inbound-rtp.jitterBufferEmittedCount
.googPreferredJitterBufferMs (Audio) inbound-rtp.jitterBufferTargetDelay / inbound-rtp.jitterBufferEmittedCount
.googExpandRate
Das aktuelle Verhältnis von verborgenen Beispielen: inbound-rtp.concealedSamples ÷ inbound-rtp.totalSamplesReceived
.googSpeechExpandRate Das aktuelle Verhältnis von verdeckten Samples, während der Stream nicht lautlos war: von (inbound-rtp.concealedSamplesinbound-rtp.silentConcealedSamples) / inbound-rtp.concealedSamples
.googAccelerateRate Das aktuelle Verhältnis von Stichproben, die verworfen wurden, um die Spielgeschwindigkeit zu beschleunigen: inbound-rtp.removedSamplesForAcceleration ÷ inbound-rtp.totalSamplesReceived
.googPreemptiveExpandRate
Das aktuelle Verhältnis von Samples, die zur Verlangsamung der Playout-Geschwindigkeit synthetisiert wurden: 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
Der einzige verbleibende goog-Messwert inbound-rtp.googTimingFrameInfo
.framesDecoded inbound-rtp.framesDecoded
Legacy-Messwert
VideoBwe
Standardkorrespondenz
outbound-rtp und candidate-pair
.googTargetEncBitrate
outbound-rtp.targetBitrate als sofortiger Wert oder outbound-rtp.totalEncodedBytesTarget / outbound-rtp.framesEncoded als Durchschnitt
.googActualEncBitrate Die vom Encoder erzeugten Byte sind die Nutzlastbyte ohne erneute Übertragungen: die Änderungsrate von outbound-rtp.bytesSentoutbound-rtp.retransmittedBytesSent
.googBucketDelay outbound-rtp.totalPacketSendDelay / outbound-rtp.packetsSent
.googTransmitBitrate Die Änderungsrate von outbound-rtp.headerBytesSent + outbound-rtp.bytesSent für die Bitrate pro RTP-Stream, candidate-pair.bytesSent für die Bitrate pro ICE-Kandidaten oder transport.bytesSent für die Bitrate pro Transport
.googRetransmitBitrate Der Änderungsbereich von outbound-rtp.retransmittedBytesSent
.googAvailableSendBandwidth candidate-pair.availableOutgoingBitrate
.googAvailableReceiveBandwidth candidate-pair.availableIncomingBitrate

Die Standard-API ist Simulcast-fähig

Wenn Sie Simulcast verwenden, haben Sie vielleicht bemerkt, dass die Legacy-API nur einen einzelnen SSRC meldet, auch wenn Sie Simulcast verwenden, um zum Beispiel drei RTP-Streams über drei separate SSRCs zu senden.

Die Standard-API teilt diese Einschränkung nicht und gibt drei outbound-rtp-Statistikobjekte zurück, eines für jeden der SSRCs. Das bedeutet, dass Sie jeden RTP-Stream einzeln analysieren können. Um die Gesamtbitrate aller RTP-Sendestreams zu erhalten, müssen Sie sie aber selbst aggregieren.

SVC-Streams oder RTP-Streams mit mehreren über die scalabilityMode API konfigurierten räumlichen Ebenen werden dagegen weiterhin als einzelne outbound-rtp angezeigt, da sie über einen einzelnen SSRC gesendet werden.

Wenn Sie mehr Zeit für die Migration benötigen

Wenn die alte API in Chrome 117 entfernt wird, wird durch ihre Verwendung eine Ausnahme generiert. Wenn Sie Ihren Code nicht rechtzeitig migrieren können, gibt der Ursprungstest für die RTCPeerConnection-Callback-basierte getStats() API registrierten Websites mehr Zeit für die Migration. Mit einem Ursprungstesttoken kann die alte getStats() API bis Chrome 121 weiter verwendet werden.