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

Henrik Boström
Henrik Boström

La API heredada de WebRTC getStats() 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, hubo dos versiones en competencia de la API de getStats() de WebRTC. La API heredada 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 tiene más funciones y métricas bien definidas documentadas públicamente en la especificación del W3C Identifiers for WebRTC's Statistics API. La especificación incluye descripciones de cada métrica que se menciona 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 estables (la excepción se lanzará de forma gradual). Sigue esta guía para facilitar la transición a la API estándar.

Tipos de estadísticas heredados frente a estándares

Para ver la lista completa de los tipos de estadísticas estándar, consulta 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 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 IDs 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, que corresponden a los tipos estándar de la siguiente manera:


Tipo heredado

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


Los tipos estándar para esto son inbound-rtp (para recibir transmisiones de RTP y su MediaStreamTrack remoto asociado), outbound-rtp (para enviar transmisiones de RTP) y media-source (para métricas de MediaStreamTrack locales asociadas con una transmisión de RTP de envío). Las métricas de flujo de RTP también contienen información sobre el codificador o decodificador que usa el flujo de RTP.
VideoBwe
Métricas de estimación de 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 de par candidato de ICE (candidate-pair).
googComponent
Representación del transporte (ICE y DTLS) La versión estándar es transport.
localcandidate and remotecandidate
Representación de un candidato de ICE. La versión estándar es local-candidate y remote-candidate.
googCandidatePair
Representación de 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, este 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 tiene ningún tipo heredado correspondiente:
  • codec: Es un códec que usa actualmente una transmisión RTP, ya sea para codificar o decodificar. Este es un subconjunto de los códecs que se negociaron en el SDP.
  • remote-inbound-rtp: Es el flujo de RTP entrante de un extremo remoto que corresponde a un flujo de RTP saliente que este extremo envía (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 de RTP saliente de un extremo remoto que corresponde a una transmisión de RTP entrante que este extremo recibe (inbound-rtp). Se mide en el extremo remoto y se informa en un informe del remitente de RTCP (SR).
  • 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 métricas estándares

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 unidades diferentes o expresarse como un contador total en lugar de un valor instantáneo. Consulta la especificación para ver las definiciones de las métricas.
La API estándar prefiere exponer contadores totales en lugar de tasas. Esto significa que, para obtener la tasa 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 tarifas y los promedios por tu cuenta de esta manera puede parecer un paso adicional engorroso, pero tiene la ventaja de permitirte obtener promedios en cualquier intervalo de tiempo que desees. Llamar a la API estándar con menos frecuencia de la que podrías haber hecho 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 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
remotecandidate
Correspondencia estándar
remote-candidate
Igual que en localcandidate anterior. Igual que en local-candidate anterior.
Métrica heredada
googCandidatePair
Correspondencia estándar
candidate-pair
.responsesSent candidate-pair.responsesSent
.requestsReceived candidate-pair.requestsReceived
.googRemoteCandidateType remote-candidate.candidateType
(búsqueda de remote-candidate a través de
candidate-pair.remoteCandidateId)
.googReadable googReadable es un valor booleano que refleja si incrementamos recientemente candidate-pair.requestsReceived o candidate-pair.responsesReceived.
.googLocalAddress local-candidate.address
(búsqueda de 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 incrementamos candidate-pair.responsesReceived recientemente o no.
.googRemoteAddress remote-candidate.address
.googRtt candidate-pair.currentRoundTripTime
.googActiveConnection La conexión activa hace referencia al par candidato que el transporte selecciona actualmente, como en el caso de 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á en el rango [0..32768], pero la métrica estándar está en el rango [0..1].
.audioOutputLevel
inbound-rtp.audioLevel. La métrica heredada está en el rango [0..32768], pero la métrica estándar está en el 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 "type/subtype", 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 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 de otras métricas (p.ej., la resolución de envío o la velocidad de fotogramas es diferente de la resolución o la velocidad de fotogramas de origen), pero la duración a la que nos limitamos, outbound-rtp.qualityLimitationDurations, puede ser más útil que la frecuencia con la que se volvió a configurar 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: (inbound-rtp.concealedSamples - 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
Es 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 un valor instantáneo o outbound-rtp.totalEncodedBytesTarget / outbound-rtp.framesEncoded como un 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 - 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 la transmisión por RTP, candidate-pair.bytesSent para la tasa de bits por candidato de 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 SSRC único, incluso cuando usas la transmisión simultánea para enviar (por ejemplo) tres transmisiones RTP a través de tres SSRC independientes.

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 flujo de RTP de forma individual, pero también significa que, para obtener la tasa de bits total de todos los flujos de envío de RTP, deberás agregarlos por tu cuenta.

Por otro lado, las transmisiones SVC o RTP con varias capas espaciales configuradas a través de la API de scalabilityMode aún aparecen como una sola 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 de getStats() basada en devoluciones de llamada de RTCPeerConnection les brinda a los sitios web registrados más tiempo para migrar. Con un token de prueba de origen, la API heredada de getStats() se podrá seguir usando hasta la versión 121 de Chrome.