A cota de armazenamento em buffer foi excedida

Joe medley
Joe Medley

Se você estiver trabalhando com extensões de origem de mídia (MSE, na sigla em inglês), algo com o qual você precisará lidar é com um buffer excessivo. Quando isso ocorrer, você receberá o que é conhecido como QuotaExceededError. Neste artigo, vou falar sobre algumas das maneiras de lidar com isso.

O que é o CotaExceder?

Basicamente, QuotaExceededError é o que você consegue se tentar adicionar dados demais ao seu objeto SourceBuffer. Adicionar mais objetos SourceBuffer a um elemento MediaSource pai também pode gerar esse erro. Isso está fora do escopo deste artigo.) Se SourceBuffer tiver muitos dados, chamar SourceBuffer.appendBuffer() acionará a seguinte mensagem na janela do console do Chrome.

Erro no console de cotas.

Há alguns pontos a serem observados sobre isso. Primeiro, observe que o nome QuotaExceededError não aparece em nenhum lugar da mensagem. Para ver isso, defina um ponto de interrupção em um local em que você possa capturar o erro e examiná-lo no relógio ou na janela do escopo. Veja isso abaixo.

Janela de verificação da cota.

Em segundo lugar, não há uma maneira definitiva de descobrir a quantidade de dados que a SourceBuffer pode processar.

Comportamento em outros navegadores

No momento em que este artigo foi escrito, o Safari não gerava um QuotaExceededError em muitos builds. Em vez disso, ele remove frames usando um algoritmo de duas etapas, interrompendo se houver espaço suficiente para processar o appendBuffer(). Primeiro, libera frames entre 0 e 30 segundos antes do horário atual em blocos de 30 segundos. Em seguida, isso libera frames em blocos de 30 segundos da duração voltando até 30 segundos após currentTime. Leia mais sobre isso em um conjunto de alterações de 2014 do Webkit (em inglês).

Felizmente, junto com o Chrome, o Edge e o Firefox, esse erro é gerado. Se estiver usando outro navegador, será necessário fazer seus próprios testes. Embora provavelmente não seja o que você criaria para um player de mídia real, o teste de limite de buffer da fonte de François Beaufort permite pelo menos observar o comportamento.

Quantos dados posso anexar?

O número exato varia de acordo com o navegador. Como não é possível consultar os dados anexados no momento, você precisa acompanhar quanto está anexando. Quanto ao que assistir, estes são os melhores dados que posso coletar no momento em que este artigo foi escrito. Para o Chrome, esses números são limites superiores, o que significa que podem ser menores quando o sistema encontra pressão na memória.

Chrome Chromecast* Firefox Safari Edge
Vídeo 150MB 30MB 100 MB 290MB Desconhecido
Áudio 12MB 2MB 15 MB 14MB Desconhecido
  • Ou outro dispositivo Chrome com memória limitada.

O que eu faço?

Como a quantidade de dados com suporte varia muito e não é possível encontrar a quantidade de dados em um SourceBuffer, você precisa conseguir esses dados indiretamente processando o QuotaExceededError. Agora vamos conferir algumas maneiras de fazer isso.

Há várias abordagens para lidar com QuotaExceededError. Na realidade, uma combinação de uma ou mais abordagens é melhor. A abordagem precisa ser basear o trabalho em quanto você está buscando e tentando anexar além de HTMLMediaElement.currentTime e ajustando esse tamanho com base no QuotaExceededError. Além disso, usar um manifesto de algum tipo, como um arquivo mpd (MPEG-DASH) ou um arquivo m3u8 (HLS), pode ajudar a monitorar os dados que você está anexando ao buffer.

Agora, vamos ver várias abordagens para lidar com o QuotaExceededError.

  • Remova os dados desnecessários e os anexe novamente.
  • Anexe fragmentos menores.
  • Diminua a resolução de reprodução.

Embora eles possam ser usados em conjunto, vou falar um de cada vez.

Remover dados desnecessários e anexar novamente

Na verdade, ela deve ser chamada de "Remova os dados menos propensos a serem usados em breve e, em seguida, tente anexar novamente os dados que provavelmente serão usados em breve". Esse título era muito longo. Você só precisa se lembrar do que eu quero dizer.

Remover dados recentes não é tão simples quanto chamar SourceBuffer.remove(). Para remover dados do SourceBuffer, a flag de atualização precisa ser falsa. Se não estiver, chame SourceBuffer.abort() antes de remover os dados.

Algumas considerações para chamar SourceBuffer.remove().

  • Isso pode ter um impacto negativo na reprodução. Por exemplo, se você quiser que o vídeo seja repetido em breve, não remova o início do vídeo. Da mesma forma, se você ou o usuário buscar uma parte do vídeo em que os dados foram removidos, será necessário anexar os dados novamente para atender a essa busca.
  • Remova o conteúdo com cuidado. Tenha cuidado ao remover o grupo de frames em reprodução no momento começando no frame-chave currentTime ou antes, porque isso pode causar a interrupção da reprodução. Essas informações podem precisar ser analisadas fora do bytestream pelo app da Web se não estiverem disponíveis no manifesto. Um manifesto de mídia ou conhecimento do app sobre os intervalos de frames-chave na mídia pode ajudar a orientar a escolha do app sobre os intervalos de remoção para evitar a remoção da mídia em reprodução no momento. Qualquer que seja a remoção, não remova o grupo de imagens em reprodução nem as primeiras após isso. Em geral, não remova itens além do período atual, a menos que você tenha certeza de que a mídia não é mais necessária. Se você remover perto do marcador, poderá causar uma paralisação.
  • O Safari 9 e o Safari 10 não implementam corretamente o SourceBuffer.abort(). Na verdade, eles geram erros que interrompem a reprodução. Felizmente, há rastreadores de bugs abertos aqui e aqui. Enquanto isso, você terá que resolver isso de alguma forma. O Shaka Player faz isso criando uma função abort() vazia nessas versões do Safari.

Anexar fragmentos menores

Veja o procedimento abaixo. Isso pode não funcionar em todos os casos, mas tem a vantagem de que o tamanho dos pedaços menores pode ser ajustado para atender às suas necessidades. Ela também não exige voltar à rede, o que pode gerar mais custos de dados para alguns usuários.

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

Diminuir a resolução de reprodução

Isso é semelhante a remover dados recentes e reanexar. Na verdade, os dois podem ser feitos juntos, embora o exemplo abaixo mostre apenas a diminuição da resolução.

Existem alguns pontos a serem considerados ao usar essa técnica:

  • Você precisa anexar um novo segmento de inicialização. Faça isso sempre que você mudar as representações. O novo segmento de inicialização precisa ser destinado aos segmentos de mídia a seguir.
  • O carimbo de data/hora da apresentação da mídia anexada precisa corresponder o máximo possível ao carimbo de data/hora dos dados no buffer, mas sem pular à frente. A sobreposição dos dados armazenados em buffer pode causar falhas ou breves travamentos, dependendo do navegador. Independentemente do que você anexar, não sobreponha o marcador, porque isso gerará erros.
  • Isso pode interromper a reprodução. Talvez você tenha vontade de procurar um local específico e retomar a reprodução a partir dele. Isso causa a interrupção da reprodução até que a busca seja concluída.