Se superó la cuota de almacenamiento en búfer

Joe Medley
Jo Medley

Si trabajas con extensiones de fuente de medios (MSE), algo con lo que tendrás que lidiar en algún momento es tener un búfer demasiado lleno. Cuando esto ocurra, obtendrás lo que se denomina QuotaExceededError. En este artículo, explicaré algunas de las formas de tratarlo.

¿Qué es el QuotaExceededError?

En esencia, 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 en la consola de cuotas.

Debes tener en cuenta algunos aspectos al respecto. Primero, observa que el nombre QuotaExceededError no aparece en ninguna parte del mensaje. Para comprobarlo, establece una interrupción en una ubicación en la que puedas detectar el error y examinarlo en tu ventana de observación o alcance. Lo mostramos a continuación.

Ventana de supervisión de la cuota.

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

Comportamiento en otros navegadores

Al momento de escribir, Safari no arroja un QuotaExceededError en muchas de sus compilaciones. En cambio, quita fotogramas mediante 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 los fotogramas en fragmentos de 30 segundos desde la duración hacia atrás hasta los 30 segundos después de currentTime. Puedes obtener más información al respecto en un conjunto de cambios de Webkit de 2014.

Afortunadamente, Chrome, Edge y Firefox muestran este error. Si usas otro navegador, tendrás que hacer tus propias pruebas. Aunque es probable que no sea lo que compilarías para un reproductor multimedia de la vida real, al menos la prueba de límite de búfer de origen de François Beaufort te permite observar el comportamiento.

¿Cuántos datos puedo agregar?

El número exacto varía de un navegador a otro. Dado que no puedes consultar la cantidad de datos agregados actualmente, tendrás que hacer un seguimiento de cuántos datos agregas tú mismo. En cuanto a qué mirar, estos son los mejores datos que puedo recopilar al momento de escribir. Para Chrome, estos números son límites superiores, lo que significa que pueden ser más pequeños cuando el sistema experimenta presión de memoria.

Chrome Chromecast* Firefox Safari Conexión de integración
Video 150MB 30MB 100 MB 290MB Desconocido
Audio 12MB 2 MB 15 MB 14MB Desconocido
  • O bien, otro dispositivo Chrome con memoria limitada

Entonces, ¿qué debo hacer?

Dado que la cantidad de datos admitidos varía mucho y no puedes encontrar la cantidad de datos de una SourceBuffer, debes obtenerla indirectamente manejando la QuotaExceededError. Veamos algunas maneras de hacerlo.

Existen varios enfoques para tratar con QuotaExceededError. En realidad, lo mejor es combinar uno o más enfoques. Tu enfoque debe ser basar el trabajo en cuánto se recupera y se intenta agregar más allá de HTMLMediaElement.currentTime y se ajusta ese tamaño según el QuotaExceededError. Además, usar algún tipo de manifiesto, como un archivo mpd (MPEG-DASH) o un archivo m3u8 (HLS), puede ayudarte a realizar un seguimiento de los datos que agregas al búfer.

Ahora, veamos varios enfoques para tratar con QuotaExceededError.

  • Quita los datos innecesarios y vuelve a agregarlos.
  • Agrega fragmentos más pequeños.
  • Bajar la resolución de reproducción.

Si bien se pueden combinar, las explicaré de a uno por vez.

Quita los datos innecesarios y vuelve a agregarlos

En realidad, esto debería llamarse: "Quita los datos con menor probabilidad de usarse pronto y, luego, vuelve a adjuntar los datos que probablemente se usarán pronto". Era un título demasiado largo. Tendrás que recordar a qué me refiero realmente.

Quitar datos recientes no es tan simple como llamar a SourceBuffer.remove(). Para quitar datos de SourceBuffer, la marca de actualización debe ser falsa. De lo contrario, llama a SourceBuffer.abort() antes de quitar cualquier dato.

Debes tener en cuenta algunos aspectos cuando llames a SourceBuffer.remove().

  • Esto puede tener un impacto negativo en la reproducción. Por ejemplo, si quieres que el video se vuelva a reproducir o se repita indefinidamente, es posible que no quieras quitar el principio del video. Del mismo modo, si tú o el usuario buscan una parte del video en la que quitaste datos, tendrás que volver a agregarlos para satisfacer ese intento.
  • Quita el control de la forma más conservadora que puedas. Ten cuidado de quitar el grupo de fotogramas que se está reproduciendo actualmente y que comienzan en el fotograma clave en el valor currentTime o antes de esa fecha, ya que se podría detener la reproducción. Es posible que la app web deba analizar esa información fuera del flujo de bytes si no está disponible en el manifiesto. Un manifiesto multimedia o el conocimiento de la app sobre los intervalos de fotogramas clave del contenido multimedia pueden ayudar a guiar la elección de los rangos de eliminación de tu app para evitar que se quite el contenido multimedia que se está reproduciendo en ese momento. Sin importar lo que quites, no quites el grupo de imágenes que se está reproduciendo en ese momento ni las primeras pocas más. Por lo general, no lo quites más allá del tiempo actual, a menos que estés seguro de que ya no se necesitan los medios. Si lo quitas cerca del cabezal de reproducción, puedes provocar una detención.
  • Safari 9 y Safari 10 no implementan SourceBuffer.abort() correctamente. De hecho, muestran errores que detendrán la reproducción. Afortunadamente, hay herramientas de seguimiento de errores abiertas aquí y aquí. Mientras tanto, tendrás que solucionar esto de alguna manera. Shaka Player lo hace creando stubs en una función abort() vacía en esas versiones de Safari.

Cómo agregar fragmentos más pequeños

A continuación, te mostramos 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);

Bajar la resolución de reproducción

Esto es similar a quitar datos recientes y volver a agregarlos. De hecho, ambos pueden realizarse juntos, aunque en el siguiente ejemplo solo se muestra que se reduce la resolución.

Debes tener en cuenta algunos aspectos cuando uses esta técnica:

  • Debes agregar un nuevo segmento de inicialización. Debes hacerlo cada vez que cambies las representaciones. El nuevo segmento de inicialización debe ser para los siguientes segmentos de contenido multimedia.
  • La marca de tiempo de la presentación del contenido multimedia agregado debe coincidir con la marca de tiempo de los datos del búfer lo más posible, pero no adelantar. La superposición de los datos almacenados en búfer puede provocar un salto o una interrupción breve, según el navegador. Sin importar lo que agregues, no superpongas el cabezal de reproducción, ya que se generarán errores.
  • Es posible que se interrumpa 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 se producirá una interrupción en la reproducción hasta que se complete la búsqueda.