WebRTC: przewodnik po migracji starszej wersji getStats()

Henrik Boström
Henrik Boström

Stary interfejs API WebRTC getStats() zostanie usunięty w Chrome 117, dlatego aplikacje, które z niego korzystają, będą musiały przejść na standardowy interfejs API. Z tego artykułu dowiesz się, jak przenieść kod i co zrobić, jeśli potrzebujesz więcej czasu na wprowadzenie tej zmiany.

W przeszłości istniały 2 konkurujące wersje interfejsu API WebRTC getStats(). starszy interfejs API getStats(), który powstał przed procesem standaryzacji i przyjmuje argument funkcji wywołania zwrotnego, oraz znormalizowany i powszechnie obsługiwany interfejs API, który zwraca obietnicę.

Standardowy interfejs API jest bogatszy w funkcje i posiada dobrze zdefiniowane dane, które są publicznie udokumentowane w specyfikacji W3C Identyfikatory interfejsu Statistics API dla WebRTC. Specyfikacja zawiera opisy wszystkich danych wymienionych w tym przewodniku oraz wiele innych.

Od Chrome 117 starszy interfejs API getStats() będzie zgłaszać wyjątek na kanale stabilnym (wyjątki będą wprowadzane stopniowo). Aby ułatwić sobie przejście na standardowy interfejs API, skorzystaj z tego przewodnika.

Stare i standardowe typy statystyk

Pełną listę standardowych typów statystyk znajdziesz w specyfikacji w enumeracji RTCStatsType. Obejmuje to definicję słownika statystyk, która opisuje dane zbierane dla każdego typu.

Wszystkie obiekty stats mają atrybut id, który jednoznacznie identyfikuje obiekt bazowy w wielu wywołaniach getStats(). Ten sam obiekt będzie miał ten sam identyfikator za każdym razem, gdy wywołasz metodę. Jest to przydatne do obliczania szybkości zmian danych (w następnej sekcji znajdziesz przykład). Identyfikatory tworzą też relacje odsyłaczy. Na przykład obiekt statystyk outbound-rtp odwołuje się do powiązanego obiektu statystyk media-source za pomocą atrybutu outbound-rtp.mediaSourceId. Jeśli narysujesz wszystkie relacje ...Id, otrzymasz wykres.

Starszy interfejs API ma te typy statystyk, które odpowiadają standardowym typom:


Typ starszy

Typ standardowy
ssrc
Reprezentuje strumień RTP i dane dotyczące powiązanego MediaStreamTrack.


Standardowe typy to inbound-rtp (do odbierania strumieni RTP i powiązanych z nimi zdalnych MediaStreamTrack), outbound-rtp (do wysyłania strumieni RTP) i media-source (do lokalnych danych MediaStreamTrack powiązanych z wysyłaniem strumienia RTP). Dane strumienia RTP zawierają też informacje o kodierze lub dekoderze używanym przez ten strumień.
VideoBwe
Dane dotyczące szacowania przepustowości, docelowej szybkości transmisji bitów, szybkości transmisji bitów kodera i rzeczywistej szybkości transmisji bitów. Te typy danych są częścią danych RTP (outbound-rtpinbound-rtp) oraz danych par kandydatów ICE (candidate-pair).
googComponent
Reprezentuje transport (ICE i DTLS). Wersja standardowa to transport.
localcandidate and remotecandidate
Reprezentuje kandydata ICE. Wersja standardowa to local-candidateremote-candidate.
googCandidatePair
Reprezentuje parę kandydatów ICE, czyli parę kandydatów lokalnych i zdalnych. Wersja standardowa to candidate-pair.
googCertificate
Przedstawia certyfikat używany przez transport DTLS. Wersja standardowa to certificate.
googLibjingleSession
Reprezentuje RTCPeerConnection. Chociaż jego zawartość nie jest mapowana na nic w standardzie, standard ma typ powiązany z RTCPeerConnection: peer-connection.

Brak w starszym interfejsie API

Te typy statystyk zostały dodane do standardowego interfejsu API i nie mają odpowiadających im starszych typów:
  • codec: kodek używany obecnie przez strumień RTP do kodowania lub dekodowania. Jest to podzbiór kodeków, które zostały wynegocjowane w SDP.
  • remote-inbound-rtp: przychodzący strumień RTP zdalnego punktu końcowego odpowiadający wychodzącemu strumieniowi RTP wysyłanemu przez ten punkt końcowy (outbound-rtp). Jest on mierzony w zdalnym punkcie końcowym i zgłaszany w raporcie odbiornika RTCP (RR) lub rozszerzonym raporcie RTCP (XR).
  • remote-outbound-rtp: Wychodzący strumień RTP zdalnego punktu końcowego odpowiadający przychodzącemu strumieniowi RTP, który ten punkt końcowy odbiera (inbound-rtp). Jest on mierzony w zdalnym punkcie końcowym i zgłaszany w raporcie nadawcy RTCP (SR).
  • media-playout: dane dotyczące odtwarzania zdalnego MediaStreamTrack powiązanego z przychodzącym strumieniem RTP (inbound-rtp).
  • data-channel: reprezentuje RTCDataChannel.

Mapowanie starszych danych na standardowe

Mapowanie to ma pomóc deweloperom w określaniu, które dane starsze odpowiadają danym standardowym. Pamiętaj jednak, że odpowiadające dane mogą używać innych jednostek lub być wyrażone jako całkowity licznik, a nie wartość chwilowa. Definicje danych znajdziesz w specyfikacji.
Standardowy interfejs API preferuje wyświetlanie łącznych wartości zliczników zamiast współczynników. Oznacza to, że aby uzyskać odpowiednią szybkość (np. bitrate) jak w starszym interfejsie API, aplikacja musi obliczyć średnią szybkość, biorąc pod uwagę różnicę między 2 wywołaniami funkcji getStats(). Na przykład:

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

Wymaganie samodzielnego obliczania stawek i średni może wydawać się uciążliwym dodatkowym krokiem, ale ma tę zaletę, że pozwala uzyskać średnie w dowolnym wybranym przedziale czasowym. Wywoływanie standardowego interfejsu API rzadziej niż w przypadku starszego interfejsu API przynosi pewne korzyści w zakresie wydajności.

Starszy typ danych
googCertificate
Standardowa korespondencja
certificate
.googFingerprint .fingerprint
.googFingerprintAlgorithm .fingerprintAlgorithm
.googDerBase64 .base64Certificate
Starszy typ danych
googComponent
Standardowa korespondencja
transport
.localCertificateId .localCertificateId
.remoteCertificateId .remoteCertificateId
.selectedCandidatePairId .selectedCandidatePairId
.dtlsCipher .dtlsCipher
.srtpCipher .srtpCipher
Starszy typ danych
localcandidate
Standardowa korespondencja
local-candidate lub candidate-pair
.stunKeepaliveRequestsSent candidate-pair.requestsSent (odwrotne wyszukiwanie candidate-pair przez 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
Starszy typ danych
remotecandidate
Standardowa korespondencja
remote-candidate
Jak localcandidate powyżej. Jak local-candidate powyżej.
Starszy typ danych
googCandidatePair
Standardowa korespondencja
candidate-pair
.responsesSent candidate-pair.responsesSent
.requestsReceived candidate-pair.requestsReceived
.googRemoteCandidateType remote-candidate.candidateType
(wyszukiwanie remote-candidate przez
candidate-pair.remoteCandidateId)
.googReadable googReadable to wartość logiczna wskazująca, czy ostatnio zwiększyliśmy wartość candidate-pair.requestsReceived lub candidate-pair.responsesReceived.
.googLocalAddress local-candidate.address
(wyszukiwanie local-candidate przez
candidate-pair.localCandidateId)
.consentRequestsSent candidate-pair.consentRequestsSent
.googTransportType To samo dotyczy local-candidate.protocolremote-candidate.protocol.
.googChannelId candidate-pair.transportId
.googLocalCandidateType local-candidate.candidateType
.googWritable googWritable to wartość logiczna wskazująca, czy ostatnio zwiększono wartość parametru candidate-pair.responsesReceived.
.googRemoteAddress remote-candidate.address
.googRtt candidate-pair.currentRoundTripTime
.googActiveConnection Aktywne połączenie odnosi się do pary kandydatów, która jest obecnie wybrana przez transport, np. 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
Starszy typ danych
ssrc
Standardowa korespondencja
inbound-rtp, outbound-rtp, media-source
.audioInputLevel media-source.audioLevel. Stara wersja danych mieści się w zakresie [0,32768], a nowa – w zakresie [0,1].
.audioOutputLevel
inbound-rtp.audioLevel. Stara wersja danych mieści się w zakresie [0,32768], a nowa – w zakresie [0,1].
.packetsLost inbound-rtp.packetsLost
.googTrackId media-source.trackIdentifier dla lokalnych MediaStreamTrack i inbound-rtp.trackIdentifier dla zdalnych MediaStreamTrack
.googRtt remote-inbound-rtp.roundTripTime (patrz: outbound-rtp.remoteId)
.googEchoCancellationReturnLossEnhancement inbound-rtp.echoReturnLossEnhancement
.googCodecName Nazwa kodeka to podtyp typu MIME „type/subtype”, codec.mimeType (zobacz inbound-rtp.codecId i outbound-rtp.codecId)
.transportId inbound-rtp.transportIdoutbound-rtp.transportId
.mediaType inbound-rtp.kindoutbound-rtp.kind lub media-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
Chociaż FPS wysyłania to szybkość zmiany outbound-rtp.framesSent, jest ona faktycznie implementowana jako outbound-rtp.framesPerSecond, czyli FPS kodowania.
.googFrameWidthReceived inbound-rtp.frameWidth
.googFrameHeightReceived inbound-rtp.frameHeight
.googFrameRateDecoded
Szybkość zmian inbound-rtp.framesDecoded
.googFrameRateOutput
Skok wartości 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
Prawda, jeśli outbound-rtp.qualityLimitationReason == "cpu"
.googBandwidthLimitedResolution
Prawda, jeśli outbound-rtp.qualityLimitationReason == "bandwidth"
.googAdaptationChanges
Stary rodzaj danych zlicza liczbę zmian rozdzielczości lub liczby klatek z powodu qualityLimitationReason. Można to wywnioskować z innych danych (np. rozdzielczość wysyłania lub częstotliwość klatek różni się od rozdzielczości źródłowej lub częstotliwości klatek), ale czas, przez który mieliśmy ograniczenie (outbound-rtp.qualityLimitationDurations), może być bardziej przydatny niż częstotliwość zmiany rozdzielczości lub częstotliwości klatek.
.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
Ostatnia wartość współczynnika pakietów zawierających korekcję błędów: inbound-rtp.fecPacketsReceivedinbound-rtp.fecPacketsDiscarded
.packetsReceived inbound-rtp.packetsReceived
.googJitterBufferMs inbound-rtp.jitterBufferDelay/inbound-rtp.jitterBufferEmittedCount
.googTargetDelayMs (wideo) inbound-rtp.jitterBufferTargetDelay/inbound-rtp.jitterBufferEmittedCount
.googPreferredJitterBufferMs (dźwięk) inbound-rtp.jitterBufferTargetDelay/inbound-rtp.jitterBufferEmittedCount
.googExpandRate
Ostatnie proporcje ukrytych próbek: inbound-rtp.concealedSamples / inbound-rtp.totalSamplesReceived
.googSpeechExpandRate Ostatni współczynnik ukrytych próbek, gdy transmisja nie była bezdźwięczna: (inbound-rtp.concealedSamples – inbound-rtp.silentConcealedSamples) / inbound-rtp.concealedSamples
.googAccelerateRate Ostatni stosunek próbek, które zostały odrzucone w celu przyspieszenia szybkości odtwarzania: inbound-rtp.removedSamplesForAcceleration / inbound-rtp.totalSamplesReceived
.googPreemptiveExpandRate
Ostatnia liczba próbek, które zostały zsyntetyzowane w celu spowolnienia szybkości odtwarzania: 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
Jedyna pozostała goog-metric. inbound-rtp.googTimingFrameInfo
.framesDecoded inbound-rtp.framesDecoded
Starszy typ danych
VideoBwe
Standardowa korespondencja
outbound-rtp i candidate-pair
.googTargetEncBitrate
outbound-rtp.targetBitrate jako wartość chwilowa lub outbound-rtp.totalEncodedBytesTarget / outbound-rtp.framesEncoded jako średnia
.googActualEncBitrate Bajty wygenerowane przez koder to bajty danych, z wyjątkiem retransmisji: szybkość zmiany outbound-rtp.bytesSent – outbound-rtp.retransmittedBytesSent
.googBucketDelay outbound-rtp.totalPacketSendDelay/outbound-rtp.packetsSent
.googTransmitBitrate Szybkość zmiany outbound-rtp.headerBytesSent + outbound-rtp.bytesSent w przypadku szybkości transmisji bitów strumienia RTP, candidate-pair.bytesSent w przypadku szybkości transmisji bitów kandydata ICE lub transport.bytesSent w przypadku szybkości transmisji bitów transportu.
.googRetransmitBitrate Zakres zmian outbound-rtp.retransmittedBytesSent
.googAvailableSendBandwidth candidate-pair.availableOutgoingBitrate
.googAvailableReceiveBandwidth candidate-pair.availableIncomingBitrate

Standardowy interfejs API obsługuje transmisję symultaniczną.

Jeśli korzystasz z simultanicznej transmisji, możesz zauważyć, że starszy interfejs API raportuje tylko jeden SSRC, nawet jeśli używasz tej funkcji do wysyłania (np.) 3 strumieni RTP za pomocą 3 oddzielnych SSRC.

Standardowy interfejs API nie ma tego ograniczenia i zwraca 3 obiekty statystyk outbound-rtp, po jednym dla każdego źródła danych SRC. Oznacza to, że możesz analizować każdy strumień RTP osobno, ale też, że aby uzyskać łączną szybkość transmisji bitów wszystkich strumieni wysyłania RTP, musisz je samodzielnie zsumować.

Z drugiej strony strumienie SVC lub RTP z wieloma warstwami przestrzennymi skonfigurowanymi za pomocą interfejsu scalabilityMode API nadal wyświetlają się jako pojedyncze outbound-rtp, ponieważ są wysyłane za pomocą jednego SSRC.

Jeśli potrzebujesz więcej czasu na migrację

Gdy starszy interfejs API zostanie usunięty w Chrome 117, jego użycie spowoduje wygenerowanie wyjątku. Jeśli nie możesz w czasie przenieść kodu, próbna wersja interfejsu RTCPeerConnection getStats() API daje zarejestrowanym witrynom więcej czasu na migrację. Dzięki tokenowi testowania origin interfejsu API getStats() można nadal używać do wersji Chrome 121.