Se você estiver trabalhando com extensões de origem de mídia (MSE, na sigla em inglês), uma coisa que
você precisará lidar é um buffer cheio. Quando isso ocorre, você
recebe o que chamamos de QuotaExceededError
. Neste artigo, vou abordar algumas
maneiras de lidar com isso.
O que é o QuotaExceededError?
Basicamente, QuotaExceededError
é o que você recebe se tentar adicionar muitos dados
ao 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 o SourceBuffer
tiver muitos dados, a chamada
SourceBuffer.appendBuffer()
vai acionar a seguinte mensagem na janela do console
do Chrome.

Há algumas coisas a serem observadas sobre isso. Primeiro, observe que o nome
QuotaExceededError
não aparece em lugar nenhum da mensagem. Para conferir isso, defina um
ponto de interrupção em um local em que você possa detectar o erro e examiná-lo na
janela de observação ou de escopo. Mostrei isso abaixo.

Em segundo lugar, não há uma maneira definitiva de descobrir quantos dados o SourceBuffer
pode processar.
Comportamento em outros navegadores
No momento em que este artigo foi escrito, o Safari não gerava uma QuotaExceededError
em muitos
dos builds. Em vez disso, ele remove frames usando um algoritmo de duas etapas, parando se
houver espaço suficiente para processar o appendBuffer()
. Primeiro, ele libera frames de
entre 0 e 30 segundos antes do tempo atual em blocos de 30 segundos. Em seguida, ele
libera frames em blocos de 30 segundos da duração para trás, até chegar a 30
segundos após currentTime
. Leia mais sobre isso em uma mudança de conjunto do WebKit de 2014.
Felizmente, o Chrome, o Edge e o Firefox geram esse erro. Se você estiver usando outro navegador, vai precisar fazer seus próprios testes. Embora provavelmente não seja o que você criaria para um media player real, o teste de limite de buffer de origem de François Beaufort pelo menos permite observar o comportamento.
Quantos dados posso anexar?
O número exato varia de acordo com o navegador. Como não é possível consultar o valor dos dados anexados, é necessário acompanhar quanto você está anexando. Quanto ao que observar, aqui estão os melhores dados que posso coletar no momento da escrita. Para o Chrome, esses números são limites máximos, o que significa que eles podem ser menores quando o sistema encontra pressão de 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 devo fazer?
Como a quantidade de dados compatíveis varia muito e você não consegue encontrar a
quantidade de dados em um SourceBuffer
, é necessário recebê-los indiretamente processando o
QuotaExceededError
. Agora vamos conferir algumas maneiras de fazer isso.
Há várias abordagens para lidar com QuotaExceededError
. Na realidade, a
combinação de uma ou mais abordagens é a melhor. Sua abordagem precisa ser baseada
no quanto você está buscando e tentando anexar além de
HTMLMediaElement.currentTime
e ajustar esse tamanho com base no
QuotaExceededError
. Além disso, o uso de um manifesto, como um arquivo mpd
(MPEG-DASH) ou um arquivo m3u8
(HLS), pode ajudar a acompanhar os dados que você está anexando ao buffer.
Agora, vamos analisar várias abordagens para lidar com o
QuotaExceededError
.
- Remova os dados desnecessários e adicione novamente.
- Anexe fragmentos menores.
- Diminuir a resolução de reprodução.
Embora eles possam ser usados em conjunto, vou falar sobre eles um por vez.
Remover dados desnecessários e anexar novamente
Na verdade, esse deveria ser chamado de "Remover dados com menos probabilidade de uso em breve e, em seguida, tentar novamente o anexo de dados com maior probabilidade de uso em breve". O título é muito longo. Você só precisa se lembrar do que eu realmente 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. Caso contrário, chame SourceBuffer.abort()
antes de remover os dados.
Há alguns pontos a serem considerados ao chamar SourceBuffer.remove()
.
- Isso pode ter um impacto negativo na reprodução. Por exemplo, se você quiser que o vídeo seja reproduzido novamente ou em loop logo, não remova o início do vídeo. Da mesma forma, se você ou o usuário procurar uma parte do vídeo em que você removeu dados, será necessário anexar esses dados novamente para satisfazer a pesquisa.
- Remova o máximo possível. Cuidado ao remover o grupo de frames
que está sendo reproduzido atualmente, começando no frame-chave em ou antes
de
currentTime
, porque isso pode causar a interrupção da reprodução. Essas informações podem precisar ser analisadas fora do fluxo de bytes pelo app da Web se não estiverem disponíveis no manifesto. Um manifesto de mídia ou o conhecimento do app sobre intervalos de keyframe na mídia pode ajudar a orientar a escolha de intervalos de remoção do app para evitar a remoção da mídia em reprodução. Não remova o grupo de fotos que está sendo reproduzido ou mesmo as primeiras fotos após ele. Em geral, não remova além do horário atual, a menos que você tenha certeza de que a mídia não é mais necessária. Se você remover o elemento perto do indicador de reprodução, poderá causar uma falha. - O Safari 9 e o Safari 10 não implementam
SourceBuffer.abort()
corretamente. Na verdade, eles geram erros que interrompem a reprodução. Felizmente, há rastreadores de bugs abertos aqui e aqui. Enquanto isso, você vai precisar contornar esse problema de alguma forma. O Shaka Player faz isso substituindo uma funçãoabort()
vazia nessas versões do Safari.
Anexar fragmentos menores
Mostrei o procedimento abaixo. Isso pode não funcionar em todos os casos, mas tem a vantagem de que o tamanho dos blocos menores pode ser ajustado de acordo com suas necessidades. Além disso, não é necessário voltar à rede, o que pode gerar custos de dados adicionais 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 à remoção de dados recentes e adição novamente. Na verdade, as duas podem ser feitas juntas, embora o exemplo abaixo mostre apenas a redução da resolução.
Há alguns pontos a considerar ao usar essa técnica:
- É necessário anexar um novo segmento de inicialização. Você precisa fazer isso sempre que mudar as representações. O novo segmento de inicialização precisa ser para os segmentos de mídia seguintes.
- O carimbo de data/hora da apresentação da mídia anexada precisa corresponder ao carimbo de data/hora dos dados no buffer o mais próximo possível, mas não pular para frente. A sobreposição dos dados em buffer pode causar uma falha ou uma breve interrupção, dependendo do navegador. Não importa o que você anexar, não sobreponha o indicador de reprodução, porque isso gera erros.
- A busca pode interromper a reprodução. Você pode querer procurar um local específico e retomar a reprodução a partir dele. Isso vai causar a interrupção da reprodução até que a busca seja concluída.