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 migrieren. In diesem Artikel wird erläutert, wie Sie Ihren Code migrieren und was Sie tun können, wenn Sie mehr Zeit für diese Änderung benötigen.

Bisher gab es zwei konkurrierende Versionen der WebRTC getStats() API. Die alte getStats()-API, die vor der Standardisierung entwickelt wurde und ein Callback-Argument annimmt, und die standardisierte und weithin unterstützte API, die ein Promise zurückgibt.

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

Ab Chrome 117 wird in der stabilen Version eine Ausnahme für die alte getStats() API geworfen. Die Ausnahme wird nach und nach eingeführt. Folgen Sie dieser Anleitung, um die Umstellung auf die Standard-API zu erleichtern.

Legacy- und Standardstatistiktypen

Eine vollständige Liste der Standardstatistiktypen findest du in der Spezifikation im Enum RTCStatsType. Dazu gehört auch, welche Statistikwörterbuchdefinition die für jeden Typ erfassten Messwerte beschreibt.

Die Statistikobjekte haben alle ein „id“-Attribut, das das zugrunde liegende Objekt über mehrere getStats()-Aufrufe hinweg eindeutig identifiziert. Das gleiche Objekt hat jedes Mal dieselbe ID, wenn die Methode aufgerufen wird. Das ist nützlich, um die Änderungsrate von Messwerten zu berechnen. Im nächsten Abschnitt finden Sie ein Beispiel. Die IDs bilden auch Referenzbeziehungen. Beispiel: Das Statistikobjekt outbound-rtp verweist ü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 bietet die folgenden Statistiktypen, die den Standardtypen entsprechen:


Legacy-Typ

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


Die Standardtypen dafür sind inbound-rtp (für empfangene RTP-Streams und die zugehörigen Remote-MediaStreamTrack), outbound-rtp (für gesendete RTP-Streams) und media-source (für lokale MediaStreamTrack-Messwerte, die mit einem gesendeten RTP-Stream verknüpft sind). RTP-Stream-Messwerte enthalten auch Informationen zum Encoder oder Decoder, der vom RTP-Stream verwendet wird.
VideoBwe
Messwerte zur Bandbreitenschätzung, Zielbitrate, Encoder-Bitrate und tatsächliche Bitrate. Diese Messwerttypen sind Teil der RTP-Messwerte (outbound-rtp und inbound-rtp) und der ICE-Kandidatenpaar-Messwerte (candidate-pair).
googComponent
Stellt den Transport (ICE und DTLS) dar. Die Standardversion ist transport.
localcandidate and remotecandidate
Stellt einen ICE-Kandidaten dar. Die Standardversionen sind local-candidate und remote-candidate.
googCandidatePair
Stellt ein ICE-Kandidatenpaar dar, also die Kombination aus einem lokalen und einem Remote-Kandidaten. Die Standardversion ist candidate-pair.
googCertificate
Steht für ein Zertifikat, das vom DTLS-Transport verwendet wird. Die Standardversion ist certificate.
googLibjingleSession
Steht für die RTCPeerConnection. Der Inhalt entspricht zwar keinem Element im Standard, aber dem RTCPeerConnection ist ein Typ zugewiesen: peer-connection.

In der Legacy API nicht vorhanden

Diese Statistiktypen wurden der Standard-API hinzugefügt, da es keinen entsprechenden älteren Typ gibt:
  • codec: Ein Codec, der derzeit von einem RTP-Stream verwendet wird, entweder für die Codierung oder Decodierung. Dies ist eine Teilmenge der Codecs, die im SDP ausgehandelt wurden.
  • remote-inbound-rtp: Ein eingehender RTP-Stream eines Remote-Endpunkts, der einem ausgehenden RTP-Stream entspricht, den dieser Endpunkt sendet (outbound-rtp). Er wird am Remote-Endpunkt gemessen und in einem RTCP-Empfängerbericht (RR) oder RTCP-erweiterten Bericht (XR) erfasst.
  • 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-Senderbericht (SR) erfasst.
  • media-playout: Messwerte zur Wiedergabe eines Remote-MediaStreamTrack, der mit einem eingehenden RTP-Stream (inbound-rtp) verknüpft ist.
  • data-channel: Stellt einen RTCDataChannel dar.

Zuordnung von Legacy- zu Standardmesswerten

Diese Zuordnung soll Entwicklern dabei helfen, herauszufinden, welcher alte Messwert welchem Standardmesswert entspricht. Beachten Sie jedoch, dass für den entsprechenden Messwert möglicherweise andere Einheiten verwendet werden oder er als Gesamtzähler und nicht als Momentanwert ausgedrückt wird. Messwertdefinitionen finden Sie in der Spezifikation.
Die Standard-API gibt bevorzugt Gesamtzähler anstelle von Raten an. Das bedeutet, dass die App die entsprechende Rate (z. B. die Bitrate) wie in der alten API ermitteln muss, indem sie die Differenz zwischen zwei getStats()-Aufrufen berechnet. 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;

Die Berechnung von Preisen und Durchschnittswerten auf diese Weise mag auf den ersten Blick wie ein umständlicher zusätzlicher Schritt erscheinen. Der Vorteil besteht jedoch darin, dass Sie so Durchschnittswerte für ein beliebiges gewünschtes Zeitintervall erhalten. Wenn Sie die Standard-API seltener aufrufen als mit der bisherigen API, können Sie einige Leistungsvorteile erzielen.

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 (Rückwärtssuche 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 localcandidate oben. Wie 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 candidate-pair.requestsReceived oder candidate-pair.responsesReceived in letzter Zeit erhöht wurde.
.googLocalAddress local-candidate.address
(local-candidate über
candidate-pair.localCandidateId suchen)
.consentRequestsSent candidate-pair.consentRequestsSent
.googTransportType Entspricht local-candidate.protocol und remote-candidate.protocol.
.googChannelId candidate-pair.transportId
.googLocalCandidateType local-candidate.candidateType
.googWritable googWritable ist ein boolescher Wert, der angibt, ob candidate-pair.responsesReceived vor Kurzem erhöht wurde.
.googRemoteAddress remote-candidate.address
.googRtt candidate-pair.currentRoundTripTime
.googActiveConnection Die aktive Verbindung bezieht sich auf das Kandidatenpaar, das derzeit vom 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 alte Messwert liegt im Bereich [0..32768], der Standardmesswert jedoch im Bereich [0..1].
.audioOutputLevel
inbound-rtp.audioLevel. Der alte Messwert liegt im Bereich [0..32768], der Standardmesswert jedoch 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 Untertyp 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
Die Sende-FPS ist die Änderungsrate von outbound-rtp.framesSent, wird aber tatsächlich als outbound-rtp.framesPerSecond implementiert, was die Codierungs-FPS ist.
.googFrameWidthReceived inbound-rtp.frameWidth
.googFrameHeightReceived inbound-rtp.frameHeight
.googFrameRateDecoded
Die Änderungsrate von inbound-rtp.framesDecoded
.googFrameRateOutput
Die Änderungsrate von inbound-rtp.framesDecoded – 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
Wahr, wenn outbound-rtp.qualityLimitationReason == "cpu"
.googBandwidthLimitedResolution
Wahr, wenn outbound-rtp.qualityLimitationReason == "bandwidth"
.googAdaptationChanges
Mit dem bisherigen Messwert wird gezählt, wie oft sich die Auflösung oder Framerate aus qualityLimitationReason-bezogenen Gründen geändert hat. Das lässt sich anhand anderer Messwerte ableiten (z. B. wenn sich die Sendeauflösung oder -Framerate von der Quellauflösung oder -Framerate unterscheidet). Die Dauer der Einschränkung, outbound-rtp.qualityLimitationDurations, ist jedoch möglicherweise nützlicher als die Häufigkeit, mit der die Auflösung oder Framerate geändert 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
Das aktuelle Verhältnis der Pakete mit Fehlerkorrektur: 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 der verdeckten Stichproben: inbound-rtp.concealedSamples / inbound-rtp.totalSamplesReceived
.googSpeechExpandRate Das aktuelle Verhältnis der ausgeblendeten Samples, während der Stream nicht stummgeschaltet war: (inbound-rtp.concealedSamples – inbound-rtp.silentConcealedSamples) ÷ inbound-rtp.concealedSamples
.googAccelerateRate Das aktuelle Verhältnis der Stichproben, die verworfen wurden, um die Wiedergabegeschwindigkeit zu beschleunigen: inbound-rtp.removedSamplesForAcceleration / inbound-rtp.totalSamplesReceived
.googPreemptiveExpandRate
Das aktuelle Verhältnis der Samples, die synthetisiert wurden, um die Wiedergabegeschwindigkeit zu verlangsamen: 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 Google-Messwert. inbound-rtp.googTimingFrameInfo
.framesDecoded inbound-rtp.framesDecoded
Legacy-Messwert
VideoBwe
Standardkorrespondenz
outbound-rtp und candidate-pair
.googTargetEncBitrate
outbound-rtp.targetBitrate als Momentanwert oder outbound-rtp.totalEncodedBytesTarget ÷ outbound-rtp.framesEncoded als Durchschnitt
.googActualEncBitrate Die vom Encoder erzeugten Bytes sind die Nutzlast-Byte, ausgenommen die Wiederholungen: die Änderungsrate von outbound-rtp.bytesSent − outbound-rtp.retransmittedBytesSent.
.googBucketDelay outbound-rtp.totalPacketSendDelay/outbound-rtp.packetsSent
.googTransmitBitrate Die Änderungsrate von outbound-rtp.headerBytesSent + outbound-rtp.bytesSent für die RTP-Stream-Bitrate, candidate-pair.bytesSent für die ICE-Kandidaten-Bitrate oder transport.bytesSent für die Transport-Bitrate
.googRetransmitBitrate Der Änderungsbereich von outbound-rtp.retransmittedBytesSent
.googAvailableSendBandwidth candidate-pair.availableOutgoingBitrate
.googAvailableReceiveBandwidth candidate-pair.availableIncomingBitrate

Die Standard-API unterstützt Simulcasts.

Wenn du Simulcast verwendest, hast du vielleicht bemerkt, dass die alte API nur einen einzelnen SSRC meldet, auch wenn du mit Simulcast beispielsweise drei RTP-Streams über drei separate SSRCs sendest.

Diese Einschränkung gilt nicht für die Standard-API. Sie gibt drei outbound-rtp-Statistikobjekte zurück, eines für jeden SSRC. Das bedeutet, dass du jeden RTP-Stream einzeln analysieren kannst. Wenn du jedoch die Gesamtbitrate aller RTP-Sendestreams ermitteln möchtest, musst du sie selbst zusammenfassen.

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 bei der Verwendung eine Ausnahme ausgelöst. Wenn Sie Ihren Code nicht rechtzeitig migrieren können, erhalten Sie mit dem Ursprungstest für die callbackbasierte getStats() API von RTCPeerConnection mehr Zeit für die Migration. Mit einem Ursprungstest-Token kann die alte getStats() API bis Chrome 121 weiter verwendet werden.