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

Henrik Boström
Henrik Boström

La API de WebRTC de getStats() heredada se quitará 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 de la API de WebRTC getStats() de la competencia. La API heredada de getStats() es anterior al proceso de estandarización y toma un argumento de devolución de llamada, y la API estandarizada y ampliamente compatible que devuelve 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 W3C Identificadores para la API de estadísticas de WebRTC. La especificación incluye descripciones de cada métrica enumerada en esta guía y muchas más.

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

Tipos de estadísticas heredadas y estándar

La lista completa de los tipos de estadísticas estándar se puede encontrar en la enumeración RTCStatsType en 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 (hay 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 mediante el atributo outbound-rtp.mediaSourceId. Si extraes todas las relaciones ...Id, se obtiene un gráfico.

La API heredada tiene los siguientes tipos de estadísticas, que corresponden a los tipos estándar, como se indica a continuación:


Tipo heredado

Tipo estándar
ssrc
Representa una transmisión de RTP y las métricas sobre el MediaStreamTrack asociado.


Los tipos estándar son inbound-rtp (para recibir transmisiones RTP y su MediaStreamTrack remoto asociado), outbound-rtp (para enviar transmisiones RTP) y media-source (para métricas MediaStreamTrack locales asociadas con una transmisión de RTP de envío). Las métricas de transmisión de RTP también contienen información acerca del codificador o decodificador utilizado por la transmisión de 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 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 vinculació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 nada en el estándar, el estándar tiene un tipo asociado con RTCPeerConnection: peer-connection.

Falta en la API heredada

Estos tipos de estadísticas se agregaron a la API estándar que no tienen ningún tipo heredado correspondiente:
  • codec: un códec que utiliza actualmente una transmisión RTP, ya sea 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 envía 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 el extremo (inbound-rtp). Se mide en el extremo remoto y se informa en un informe de remitente de RTCP (SR).
  • media-playout: Son métricas sobre la reproducción de un MediaStreamTrack remoto asociado con una transmisión de RTP entrante (inbound-rtp).
  • data-channel: Representa un RTCDataChannel.

Asignación de métricas heredada 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 expresarse como un contador total en lugar de un valor instantáneo. Consulta la especificación para obtener definiciones de métricas.
La API estándar prefiere exponer los contadores totales en lugar de las tasas. Esto significa que, para obtener la tarifa correspondiente (por ejemplo, la tasa de bits) como en la API heredada, la app debe calcular la tasa promedio tomando el delta entre dos llamadas a 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
googCertificate
Correspondencia estándar
certificate
.googFingerprint .fingerprint
.googFingerprintAlgorithm .fingerprintAlgorithm
.googDerBase64 .base64Certificate
Métrica heredada
googComponent
Correspondencia estándar
transport
.localCertificateId .localCertificateId
.remoteCertificateId .remoteCertificateId
.selectedCandidatePairId .selectedCandidatePairId
.dtlsCipher .dtlsCipher
.srtpCipher .srtpCipher
Métrica heredada
localcandidate
Correspondencia estándar
local-candidate o candidate-pair
.stunKeepaliveRequestsSent candidate-pair.requestsSent (búsqueda inversa candidate-pair mediante 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
remotecandidate
Correspondencia estándar
remote-candidate
Igual que localcandidate más arriba. Igual que local-candidate más arriba.
Métrica heredada
googCandidatePair
Correspondencia estándar
candidate-pair
.responsesSent candidate-pair.responsesSent
.requestsReceived candidate-pair.requestsReceived
.googRemoteCandidateType remote-candidate.candidateType
(búsqueda remote-candidate a través de
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
(búsqueda local-candidate a través de
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 candidate-pair.responsesReceived recientemente.
.googRemoteAddress remote-candidate.address
.googRtt candidate-pair.currentRoundTripTime
.googActiveConnection La conexión activa hace referencia al par de candidatos que el transporte selecciona en este momento, por ejemplo, en el que 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
ssrc
Correspondencia estándar
inbound-rtp, outbound-rtp, media-source
.audioInputLevel media-source.audioLevel. La métrica heredada está dentro del rango [0..32768], pero el metrc estándar está dentro del rango [0..1].
.audioOutputLevel
inbound-rtp.audioLevel La métrica heredada está dentro del rango [0..32768], pero el metrc 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 de MIME "tipo/subtipo", 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 los FPS de envío son la tasa de cambio de outbound-rtp.framesSent, en realidad se implementan como outbound-rtp.framesPerSecond, que es la codificación de FPS.
.googFrameWidthReceived inbound-rtp.frameWidth
.googFrameHeightReceived inbound-rtp.frameHeight
.googFrameRateDecoded
La tasa de cambio de inbound-rtp.framesDecoded
.googFrameRateOutput
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 podría deducirse a partir de otras métricas (p.ej., que la resolución de envío o la velocidad de fotogramas sean diferentes de la resolución de origen o la velocidad de fotogramas), pero la duración por la que estuvimos limitado, outbound-rtp.qualityLimitationDurations, puede ser más útil que la frecuencia con la que se reconfiguraron 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 Proporción reciente de muestras ocultas mientras la transmisión no estaba en silencio: de (inbound-rtp.concealedSamples a inbound-rtp.silentConcealedSamples) / inbound-rtp.concealedSamples
.googAccelerateRate La proporción reciente de muestras que se descartaron para acelerar la velocidad de reproducción: inbound-rtp.removedSamplesForAcceleration / inbound-rtp.totalSamplesReceived
.googPreemptiveExpandRate
La 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 que queda. inbound-rtp.googTimingFrameInfo
.framesDecoded inbound-rtp.framesDecoded
Métrica heredada
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 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 por candidato por ICE y 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 admite la transmisión simultánea

Si utilizas Simulcast, es posible que hayas notado que la API heredada solo informa un único SSRC, incluso cuando usas Simulcast para enviar (por ejemplo) tres transmisiones RTP a través de tres SSRC distintas.

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 de RTP de manera individual, pero también significa que tendrás que agregar las transmisiones de RTP para obtener la tasa de bits total de todas.

Por otro lado, las transmisiones de SVC o RTP con varias capas espaciales configuradas a través de la API de scalabilityMode aún se muestran como un único outbound-rtp porque se envían a través de un único SSRC.

Si necesitas más tiempo para la migración

Cuando se quite la API heredada en Chrome 117, su uso generará una excepción. Si no puedes migrar tu código a tiempo, la prueba de origen para la API de getStats() basada en devoluciones de llamada de RTCPeerConnection les brinda a los sitios web registrados más tiempo para la migración. Con un token de prueba de origen, es posible que se siga usando la API heredada de getStats() hasta Chrome 121.