Se superó la cuota de almacenamiento en búfer

Joe Medley
Joe Medley

Si trabajas con extensiones de fuente de medios (MSE), con el tiempo deberás lidiar con un búfer demasiado lleno. Cuando esto suceda, recibirás lo que se denomina QuotaExceededError. En este artículo, abordaré algunas de las formas de abordarlo.

¿Qué es QuotaExceededError?

Básicamente, QuotaExceededError es lo que obtienes si intentas agregar demasiados datos a tu objeto SourceBuffer. (Agregar más objetos SourceBuffer a un elemento MediaSource superior también puede generar este error). Eso está fuera del alcance de este artículo). Si SourceBuffer tiene demasiados datos, llamar a SourceBuffer.appendBuffer() activará el siguiente mensaje en la ventana de la consola de Chrome.

Error de la consola de cuotas.

Hay algunos aspectos que debes tener en cuenta. Primero, observa que el nombre QuotaExceededError no aparece en ninguna parte del mensaje. Para verlo, establece un punto de interrupción en una ubicación en la que puedas detectar el error y examinarlo en la ventana de supervisión o alcance. Mostré esto a continuación.

Ventana de observación de cuotas.

En segundo lugar, no hay una forma definitiva de saber cuántos datos puede controlar SourceBuffer.

Comportamiento en otros navegadores

Al momento de escribir este documento, Safari no arroja un QuotaExceededError en muchas de sus compilaciones. En su lugar, quita fotogramas con un algoritmo de dos pasos y se detiene si hay suficiente espacio para controlar el appendBuffer(). Primero, libera fotogramas de entre 0 y 30 segundos antes del tiempo actual en fragmentos de 30 segundos. A continuación, libera fotogramas en fragmentos de 30 segundos desde la duración hacia atrás hasta 30 segundos después de currentTime. Puedes obtener más información sobre esto en un cambio de Webkit de 2014.

Por fortuna, junto con Chrome, Edge y Firefox arrojan este error. Si usas otro navegador, deberás realizar tus propias pruebas. Aunque es probable que no sea lo que crearías para un reproductor multimedia real, la prueba del límite de búfer de origen de François Beaufort te permite, al menos, observar el comportamiento.

¿Cuántos datos puedo adjuntar?

La cantidad exacta varía de un navegador a otro. Dado que no puedes consultar la cantidad de datos agregados en este momento, deberás hacer un seguimiento de la cantidad que agregas. En cuanto a lo que debes tener en cuenta, estos son los mejores datos que puedo recopilar en el momento de escribir este artículo. En el caso de Chrome, estos números son límites superiores, lo que significa que pueden ser más pequeños cuando el sistema encuentra presión de memoria.

Chrome Chromecast* Firefox Safari Edge
Video 150 MB 30 MB 100 MB 290 MB Desconocido
Audio 12 MB 2 MB 15 MB 14 MB Desconocido
  • O bien, cualquier otro dispositivo Chrome con memoria limitada.

¿Qué debo hacer?

Dado que la cantidad de datos admitidos varía mucho y no puedes encontrar la cantidad de datos en un SourceBuffer, debes obtenerlos de manera indirecta mediante el control de QuotaExceededError. Ahora, veamos algunas maneras de hacerlo.

Existen varios enfoques para lidiar con QuotaExceededError. En realidad, lo mejor es una combinación de uno o más enfoques. Tu enfoque debe basarse en la cantidad que recuperas y tratas de agregar más allá de HTMLMediaElement.currentTime y ajustar ese tamaño según QuotaExceededError. Además, usar un manifiesto de algún tipo, como un archivo mpd (MPEG-DASH) o un archivo m3u8 (HLS), puede ayudarte a hacer un seguimiento de los datos que agregas al búfer.

Ahora, veamos varios enfoques para abordar el QuotaExceededError.

  • Quita los datos innecesarios y vuelve a adjuntarlos.
  • Adjunta fragmentos más pequeños.
  • Disminuye la resolución de reproducción.

Aunque se pueden usar en combinación, hablaré de cada una por separado.

Quite los datos innecesarios y vuelva a agregarlos

En realidad, se debería llamar "Quita los datos que es menos probable que se usen pronto y, luego, vuelve a intentar agregar los datos que es probable que se usen pronto". Ese era un título demasiado largo. Solo tendrás que recordar lo que realmente quiero decir.

Quitar datos recientes no es tan simple como llamar a SourceBuffer.remove(). Para quitar datos de SourceBuffer, su marca de actualización debe ser falsa. Si no es así, llama a SourceBuffer.abort() antes de quitar los datos.

Hay algunos aspectos que debes tener en cuenta cuando llames a SourceBuffer.remove().

  • Esto podría tener un impacto negativo en la reproducción. Por ejemplo, si quieres que el video se vuelva a reproducir o se repita pronto, no es recomendable quitar el comienzo. Del mismo modo, si tú o el usuario saltan a una parte del video en la que quitaste datos, deberás volver a agregarlos para satisfacer ese salto.
  • Quita la mayor cantidad posible de vello. Ten cuidado de quitar el grupo de fotogramas en reproducción que comienza en el fotograma clave a la(s) currentTime o antes, ya que esto podría detener la reproducción. Es posible que la app web deba analizar esa información del flujo de bytes si no está disponible en el manifiesto. Un manifiesto de contenido multimedia o el conocimiento de la app sobre los intervalos de fotogramas clave en el contenido multimedia pueden ayudar a guiar la elección de los rangos de eliminación de la app para evitar que se quite el contenido multimedia que se está reproduciendo. Lo que sea que quites, no quites el grupo de imágenes de juego actual ni las primeras imágenes. Por lo general, no quites el contenido más allá de la hora actual, a menos que tengas la seguridad de que ya no es necesario. Si lo quitas cerca del cabezal de reproducción, es posible que se produzca una detención.
  • Safari 9 y Safari 10 no implementan correctamente SourceBuffer.abort(). De hecho, arrojan errores que detienen la reproducción. Afortunadamente, hay servicios de seguimiento de errores abiertos aquí y aquí. Mientras tanto, deberás solucionar este problema de alguna manera. Shaka Player lo hace con la creación de un stub de una función abort() vacía en esas versiones de Safari.

Cómo adjuntar fragmentos más pequeños

A continuación, te muestro el procedimiento. Es posible que esto no funcione en todos los casos, pero tiene la ventaja de que el tamaño de los fragmentos más pequeños se puede ajustar para satisfacer tus necesidades. Tampoco es necesario volver a la red, lo que puede generar costos de datos adicionales para algunos usuarios.

const pieces = new Uint8Array([data]);
(function appendFragments(pieces) {
    if (sourceBuffer.updating) {
    return;
    }
    pieces.forEach(piece => {
    try {
        sourceBuffer.appendBuffer(piece);
    }
    catch e {
        if (e.name !== 'QuotaExceededError') {
        throw e;
        }

        // Reduction schedule: 80%, 60%, 40%, 20%, 16%, 12%, 8%, 4%, fail.
        const reduction = pieces[0].byteLength * 0.8;
        if (reduction / data.byteLength < 0.04) {
        throw new Error('MediaSource threw QuotaExceededError too many times');
        }
        const newPieces = [
        pieces[0].slice(0, reduction),
        pieces[0].slice(reduction, pieces[0].byteLength)
        ];
        pieces.splice(0, 1, newPieces[0], newPieces[1]);
        appendBuffer(pieces);  
    }
    });
})(pieces);

Baja la resolución de reproducción

Esto es similar a quitar los datos recientes y volver a agregarlos. De hecho, se pueden hacer juntos, aunque en el siguiente ejemplo solo se muestra cómo bajar la resolución.

Hay algunos aspectos que debes tener en cuenta cuando uses esta técnica:

  • Debes agregar un nuevo segmento de inicialización. Debes hacerlo cada vez que cambias las representaciones. El nuevo segmento de inicialización debe ser para los segmentos de contenido multimedia que siguen.
  • La marca de tiempo de presentación del contenido multimedia adjunto debe coincidir con la marca de tiempo de los datos en el búfer lo más cerca posible, pero no debe avanzar. La superposición de los datos almacenados en búfer puede causar una interrupción o una detención breve, según el navegador. Independientemente de lo que agregues, no superpongas el cabezal de reproducción, ya que se generarán errores.
  • La búsqueda podría interrumpir la reproducción. Es posible que sientas la tentación de buscar una ubicación específica y reanudar la reproducción desde allí. Ten en cuenta que esto provocará una interrupción de la reproducción hasta que se complete el salto.