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.
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.
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çãoabort()
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.