WebRTC: Guía de migración getStats() heredada

Henrik Boström
Henrik Boström

Se quitará la API heredada de getStats() de WebRTC en Chrome 117, por lo que las apps que la usen deberán migrar a la API estándar. En este artículo, se explica cómo migrar tu código y qué hacer si necesitas más tiempo para realizar este cambio.

Históricamente, ha habido dos versiones que compiten de la API de getStats() de WebRTC. La API heredada de getStats(), que es anterior al proceso de estandarización y toma un argumento de devolución de llamada, y la API estandarizada y ampliamente admitida que muestra una promesa.

La API estándar cuenta con más funciones y tiene métricas bien definidas que se documentan públicamente en la especificación de W3C Identifiers for WebRTC's Statistics API. La especificación incluye descripciones de cada métrica que aparece en esta guía y muchas más.

A partir de Chrome 117, la API de getStats() heredada arrojará una excepción en el canal de versiones estable (la excepción se lanzará de forma gradual). Sigue esta guía para facilitar tu transición a la API estándar.

Tipos de estadísticas heredados frente a los estándar

Puedes encontrar la lista completa de los tipos de estadísticas estándar en la enum RTCStatsType de la especificación. Esto incluye qué definición del diccionario de estadísticas describe las métricas recopiladas para cada tipo.

Todos los objetos de estadísticas tienen un atributo de ID que identifica de forma única el objeto subyacente en varias llamadas a getStats(). El mismo objeto tendrá el mismo ID cada vez que se llame al método. Esto es útil para calcular la tasa de cambio de las métricas (encontrarás un ejemplo en la siguiente sección). Los ID también forman relaciones de referencias. Por ejemplo, el objeto de estadísticas outbound-rtp hace referencia al objeto de estadísticas media-source asociado a través del atributo outbound-rtp.mediaSourceId. Si dibujas todas las relaciones ...Id, obtienes un gráfico.

La API heredada tiene los siguientes tipos de estadísticas, correspondientes a los tipos estándar:


Tipo heredado

Tipo estándar
ssrc
Representa una transmisión de RTP y métricas acerca del MediaStreamTrack asociado.


Los tipos estándar para esto son inbound-rtp (para recibir transmisiones RTP y su MediaStreamTrack remota asociada), outbound-rtp (para transmisiones RTP de envío) y media-source (para métricas locales MediaStreamTrack asociadas con una transmisión de RTP de envío). Las métricas de la transmisión RTP también contienen información sobre el codificador o el decodificador utilizado por la transmisión RTP.
VideoBwe
Métricas de estimación del ancho de banda, tasa de bits objetivo, tasa de bits del codificador y tasa de bits real. Estos tipos de métricas forman parte de las métricas de RTP (outbound-rtp y inbound-rtp) y de las métricas del par de candidatos de ICE (candidate-pair).
googComponent
Representa el transporte (ICE y DTLS). La versión estándar es transport.
localcandidate and remotecandidate
Representa a un candidato de ICE. La versión estándar es local-candidate y remote-candidate.
googCandidatePair
Representa un par de candidatos de ICE, que es una combinación de un candidato local y uno remoto. La versión estándar es candidate-pair.
googCertificate
Representa un certificado que usa el transporte DTLS. La versión estándar es certificate.
googLibjingleSession
Representa el RTCPeerConnection. Si bien su contenido no se asigna a ningún elemento del estándar, este tiene un tipo asociado con RTCPeerConnection: peer-connection.

Falta en la API heredada

Se agregaron estos tipos de estadísticas a la API estándar y no tienen ningún tipo heredado correspondiente:
  • codec: códec que utiliza actualmente una transmisión RTP para codificación o decodificación. Este es un subconjunto de los códecs que se negociaron en el SDP.
  • remote-inbound-rtp: Es la transmisión RTP entrante de un extremo remoto que corresponde a una transmisión RTP saliente que está enviando este extremo (outbound-rtp). Se mide en el extremo remoto y se informa en un informe del receptor de RTCP (RR) o en un informe extendido de RTCP (XR).
  • remote-outbound-rtp: Es la transmisión RTP saliente de un extremo remoto que corresponde a una transmisión RTP entrante que recibe este extremo (inbound-rtp). Se mide en el extremo remoto y se informa en un informe de remitentes (SR) de RTCP.
  • media-playout: Métricas sobre la reproducción de un MediaStreamTrack remoto asociado con una transmisión RTP entrante (inbound-rtp).
  • data-channel: Representa un RTCDataChannel.

Asignación de métricas heredadas a estándar

El objetivo de esta asignación es ayudar a los desarrolladores a encontrar qué métrica heredada corresponde a qué métrica estándar, pero ten en cuenta que la métrica correspondiente puede usar diferentes unidades o se puede expresar como un contador total en lugar de un valor instantáneo. Consulta las especificaciones para las definiciones de métricas.
La API estándar prefiere exponer los contadores totales en lugar de las tarifas. Esto significa que, para obtener la tarifa correspondiente (por ejemplo, la tasa de bits) como en la API heredada, la app debe calcular la tarifa promedio tomando el valor delta entre dos llamadas getStats(). Por ejemplo:

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

Tener que calcular las tasas y los promedios por tu cuenta puede parecer un paso adicional engorroso, pero tiene la ventaja de permitirte obtener promedios durante cualquier intervalo de tiempo deseado. Llamar a la API estándar con menos frecuencia de la que tendrías que ver con la API heredada tiene algunos beneficios de rendimiento.

Métrica heredada
de googCertificate
Correspondencia estándar
de certificate
.googFingerprint .fingerprint
.googFingerprintAlgorithm .fingerprintAlgorithm
.googDerBase64 .base64Certificate
Métrica heredada
de googComponent
Correspondencia estándar
de transport
.localCertificateId .localCertificateId
.remoteCertificateId .remoteCertificateId
.selectedCandidatePairId .selectedCandidatePairId
.dtlsCipher .dtlsCipher
.srtpCipher .srtpCipher
Métrica heredada
de localcandidate
Correspondencia estándar
local-candidate o candidate-pair
.stunKeepaliveRequestsSent candidate-pair.requestsSent (búsqueda inversa de candidate-pair a través de 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
Métrica heredada
de remotecandidate
Correspondencia estándar
de remote-candidate
Igual que el localcandidate anterior. Igual que el local-candidate anterior.
Métrica heredada
de googCandidatePair
Correspondencia estándar
de candidate-pair
.responsesSent candidate-pair.responsesSent
.requestsReceived candidate-pair.requestsReceived
.googRemoteCandidateType remote-candidate.candidateType
(busca remote-candidate mediante
candidate-pair.remoteCandidateId)
.googReadable googReadable es un valor booleano que refleja si recientemente aumentamos candidate-pair.requestsReceived o candidate-pair.responsesReceived.
.googLocalAddress local-candidate.address
(busca local-candidate mediante
candidate-pair.localCandidateId)
.consentRequestsSent candidate-pair.consentRequestsSent
.googTransportType Igual que local-candidate.protocol y remote-candidate.protocol.
.googChannelId candidate-pair.transportId
.googLocalCandidateType local-candidate.candidateType
.googWritable googWritable es un valor booleano que refleja si aumentamos o no recientemente el candidate-pair.responsesReceived.
.googRemoteAddress remote-candidate.address
.googRtt candidate-pair.currentRoundTripTime
.googActiveConnection La conexión activa hace referencia al par de candidatos que el transporte selecciona actualmente, como cuando 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
Métrica heredada
de ssrc
Correspondencia estándar
inbound-rtp, outbound-rtp y media-source
.audioInputLevel media-source.audioLevel. La métrica heredada está dentro del rango [0..32768], pero la métrica estándar está dentro del rango [0..1].
.audioOutputLevel
inbound-rtp.audioLevel La métrica heredada está dentro del rango [0..32768], pero la métrica estándar está dentro del rango [0..1].
.packetsLost inbound-rtp.packetsLost
.googTrackId media-source.trackIdentifier para MediaStreamTrack locales y inbound-rtp.trackIdentifier para MediaStreamTrack remotos
.googRtt remote-inbound-rtp.roundTripTime (consulta outbound-rtp.remoteId)
.googEchoCancellationReturnLossEnhancement inbound-rtp.echoReturnLossEnhancement
.googCodecName El nombre del códec es el subtipo del "tipo/subtipo" tipo de MIME, codec.mimeType (consulta inbound-rtp.codecId y outbound-rtp.codecId)
.transportId inbound-rtp.transportId y outbound-rtp.transportId
.mediaType inbound-rtp.kind y outbound-rtp.kind o media-source.kind
.googEchoCancellationReturnLoss inbound-rtp.echoReturnLoss
.totalAudioEnergy inbound-rtp.totalAudioEnergy y media-source.totalAudioEnergy
ssrc.totalSamplesDuration inbound-rtp.totalSamplesDuration y media-source.totalSamplesDuration
.ssrc inbound-rtp.ssrc y outbound-rtp.ssrc
.googJitterReceived inbound-rtp.jitter
.packetsSent outbound-rtp.packetsSent
.bytesSent outbound-rtp.bytesSent
.googContentType inbound-rtp.contentType y outbound-rtp.contentType
.googFrameWidthInput media-source.width
.googFrameHeightInput media-source.height
.googFrameRateInput media-source.framesPerSecond
.googFrameWidthSent outbound-rtp.frameWidth
.googFrameHeightSent outbound-rtp.frameHeight
.googFrameRateSent
Si bien el valor de FPS de envío es la tasa de cambio de outbound-rtp.framesSent, en realidad se implementa como outbound-rtp.framesPerSecond, que codifica los FPS.
.googFrameWidthReceived inbound-rtp.frameWidth
.googFrameHeightReceived inbound-rtp.frameHeight
.googFrameRateDecoded
La tasa de cambio de inbound-rtp.framesDecoded
.googFrameRateOutput
La tasa de cambio de inbound-rtp.framesDecoded - inbound-rtp.framesDropped
.hugeFramesSent outbound-rtp.hugeFramesSent
.qpSum

inbound-rtp.qpSum y outbound-rtp.qpSum

.framesEncoded outbound-rtp.framesEncoded
.googAvgEncodeMs

outbound-rtp.totalEncodeTime/outbound-rtp.framesEncoded

.codecImplementationName

inbound-rtp.decoderImplementation y outbound-rtp.encoderImplementation

.googCpuLimitedResolution
Verdadero si outbound-rtp.qualityLimitationReason == "cpu"
.googBandwidthLimitedResolution
Verdadero si outbound-rtp.qualityLimitationReason == "bandwidth"
.googAdaptationChanges
La métrica heredada cuenta la cantidad de veces que cambió la resolución o la velocidad de fotogramas por motivos relacionados con qualityLimitationReason. Esto se podría deducir a partir de otras métricas (p.ej., la resolución de envío o la velocidad de fotogramas difiere de la resolución de origen o la velocidad de fotogramas), pero la duración limitada, outbound-rtp.qualityLimitationDurations, puede ser más útil que la frecuencia con la que se reconfiguró la resolución o la velocidad de fotogramas.
.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
La proporción reciente de paquetes que contienen corrección de errores: inbound-rtp.fecPacketsReceived - inbound-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
La proporción reciente de muestras ocultas: inbound-rtp.concealedSamples / inbound-rtp.totalSamplesReceived
.googSpeechExpandRate La proporción reciente de muestras ocultas mientras la transmisión no estaba en silencio: de (inbound-rtp.concealedSamples - inbound-rtp.silentConcealedSamples) / inbound-rtp.concealedSamples
.googAccelerateRate Proporción reciente de muestras que se descartaron para acelerar la velocidad de reproducción: inbound-rtp.removedSamplesForAcceleration / inbound-rtp.totalSamplesReceived
.googPreemptiveExpandRate
Proporción reciente de muestras que se sintetizaron para desacelerar la velocidad de reproducción: 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
La única métrica de goog restante. inbound-rtp.googTimingFrameInfo
.framesDecoded inbound-rtp.framesDecoded
Métrica heredada
de VideoBwe
Correspondencia estándar
outbound-rtp y candidate-pair
.googTargetEncBitrate
outbound-rtp.targetBitrate como valor instantáneo o outbound-rtp.totalEncodedBytesTarget / outbound-rtp.framesEncoded como promedio
.googActualEncBitrate Los bytes que produce el codificador son los bytes de carga útil, sin incluir las retransmisiones: la tasa de cambio de outbound-rtp.bytesSent a outbound-rtp.retransmittedBytesSent.
.googBucketDelay outbound-rtp.totalPacketSendDelay/outbound-rtp.packetsSent
.googTransmitBitrate La tasa de cambio de outbound-rtp.headerBytesSent + outbound-rtp.bytesSent para la tasa de bits de transmisión por RTP, candidate-pair.bytesSent para la tasa de bits candidata por ICE o transport.bytesSent para la tasa de bits por transporte
.googRetransmitBitrate El rango de cambio de outbound-rtp.retransmittedBytesSent
.googAvailableSendBandwidth candidate-pair.availableOutgoingBitrate
.googAvailableReceiveBandwidth candidate-pair.availableIncomingBitrate

La API estándar es compatible con la transmisión simultánea

Si usas la transmisión simultánea, es posible que hayas notado que la API heredada solo informa un solo SSRC, incluso cuando usas la transmisión simultánea para enviar (por ejemplo) tres transmisiones de RTP a través de tres SSRC diferentes.

La API estándar no comparte esta limitación y mostrará tres objetos de estadísticas outbound-rtp, uno para cada uno de los SSRC. Esto significa que puedes analizar cada transmisión RTP individualmente, pero también significa que para obtener la tasa de bits total de todas las transmisiones de envío RTP, deberás agregarlas tú mismo.

Las transmisiones SVC o RTP con múltiples capas espaciales configuradas a través de la API de scalabilityMode, por otro lado, siguen apareciendo como un único outbound-rtp porque se envían a través de un solo SSRC.

Si necesitas más tiempo para la migración

Cuando se quite la API heredada en Chrome 117, usarla generará una excepción. Si no puedes migrar tu código a tiempo, la prueba de origen para la API getStats() basada en devoluciones de llamada de RTCPeerConnection, con más tiempo para migrar a los sitios web registrados. Con un token de prueba de origen, se puede seguir usando la API de getStats() heredada hasta Chrome 121.