Configurando um stream de vídeo com Spring Framework e Chrome

Aug 19 2020

Estamos escrevendo um serviço Spring que disponibiliza um endpoint HTTP por meio do qual um arquivo de vídeo (ou áudio) de uma loja Amazon S3 pode ser transmitido. A ideia básica é que você digite uma url na barra de endereços do Google Chrome e o serviço irá buscar o arquivo do S3 e fazer o streaming, de forma que o usuário possa começar a assistir imediatamente sem ter que esperar um download para concluído e que o usuário pode clicar em um ponto aleatório na barra de progresso do vídeo e imediatamente começar a assistir o vídeo a partir desse ponto.

A maneira como entendo que isso deve funcionar em teoria é que o Chrome começa a baixar o arquivo. O serviço responde com HTTP 200 e inclui um Accept-Ranges: bytese um Content-Length: filesizecabeçalho. O filesizeé conhecido, porque podemos consultá-lo como metadados do S3 sem buscar o arquivo inteiro. A inclusão desses cabeçalhos faz com que o navegador cancele o download e solicite o arquivo novamente com um Range: bytes=0-whatevercabeçalho (onde whateverhá algum tamanho de bloco que o Chrome decide). O serviço então responde com HTTP 206 (conteúdo parcial) e o intervalo de bytes solicitado, que podemos determinar facilmente porque o S3 oferece suporte ao mesmo protocolo de intervalo. O Chrome solicita blocos sucessivos do serviço, até que o fluxo termine.

No lado do Spring, estamos enviando os dados em um ResponseEntity<InputStreamResource>(conforme esta resposta do SO ).

No entanto, observamos na prática que, enquanto o Chrome cancela sua primeira solicitação após algumas centenas de bytes. No entanto, ele envia uma segunda solicitação com um Range: bytes=0-cabeçalho, solicitando efetivamente o arquivo inteiro. O servidor responde com um HTTP 206. Como resultado, ele baixou apenas algumas centenas de bytes de vídeo e o vídeo obviamente não começa a ser reproduzido.

Curiosamente, no Firefox tudo funciona corretamente. Infelizmente, nosso aplicativo precisa ser compatível com o Chrome. Estamos perdendo alguma parte do protocolo?

Respostas

2 jqno Sep 01 2020 at 21:28

Acontece que tivemos um erro off-by-one no Content-Rangecabeçalho de resposta.

A sintaxe é Content-Range: bytes start-end/total. Com um totalde 10, se você deseja obter todo o intervalo, precisa especificar bytes 0-9/10, não 0-10/10, que era o que estávamos fazendo.

É claro que com os tamanhos maiores de arquivos reais e os intervalos reais de pedaços no meio de tais arquivos, esse erro foi muito mais difícil de perceber do que no exemplo artificial do parágrafo anterior... ಠ_ಠ