HLS no MP4 usando ffmpeg em um navegador

Olá! Há mais de dois meses, no meu tempo livre, vi um aplicativo da Web para converter HLS e DASH para MP4 usando emscripten e ffmpeg, o que me faz querer compartilhar como consegui fazer isso.

Neste artigo, não citarei o código fonte das edições e patches do ffmpeg, como a maioria foi feita no meu joelho, e eu não sou muito bom em C. Mas agora existem artigos suficientes para ajudá-lo.

Introdução


Dois anos atrás, eu tinha o objetivo de combinar uma faixa de áudio e vídeo em um arquivo mp4. Depois, mergulhei no emscripten e, no básico, encontrei o repositório ffmpeg.js com o qual aprendi muito. Então, eu quase consegui atingir o objetivo, apesar de ter uma orientação muito condicional no C.
Entendendo o código-fonte, o ffmpeg fez um patch para trabalhar com o sistema de arquivos, onde a leitura de um arquivo causava uma função assíncrona em js, da qual eu li o blob do arquivo e transferi o buffer, e quando a gravação foi chamada Função js que enviou dados do buffer para o repositório.

Mas havia um problema com funções assíncronas, que eu não conseguia resolver corretamente, elas funcionavam através do asyncify (fastcomp), que não funcionava corretamente em alguns casos, ou seja, a execução do código no wasm não parava, sem aguardar um resultado da função js, ​​isso é tudo quebrado. Esse problema foi corrigido através do sinalizador EMTERPRETIFY_WHITELIST, que aparentemente moveu o código de wasm para asm ao mesmo tempo e reduziu a velocidade, e era necessário depurar a pilha de chamadas e adicionar uma função quebrada à lista com todas as exceções.

Em geral, com esses problemas, isso não poderia ser chamado de uma solução funcional, na qual tudo isso permaneceu uma pequena demonstração.



Um ano e meio depois


Depois de assistir a um relatório no Google Dev Summit sobre novos recursos no WebAssambly , fui ver como o emscripten estava indo e vi uma mensagem:
O Emscripten emite o WebAssembly usando o back-end do LLVM wasm, desde a versão 1.39.0 (outubro de 2019), e o antigo back-end do fastcomp foi descontinuado

Eu queria tentar reconstruir meus formatos de reembalador. Cerca de uma semana, pesquisei no Google como corrigir novos problemas de compilação e finalmente juntar tudo. As mudanças não eram muitas, mas não era por causa do novo vinculador de biblioteca, e já desesperada para coletar pelo menos alguma coisa, acabei de descobrir as bibliotecas de problemas (como se viu, as próprias bibliotecas estão conectadas e você não precisa especificá-las com as mãos durante a montagem).

E agora, chegou o momento de reunir e ganhar! O problema com o código assíncrono desapareceu, não havia necessidade de depurar nada, funcionou como deveria desde o início.

Parece que aqui atingi meu objetivo, mas ... um novo apareceu.

Reescrever Protocolo HTTP


Esse pensamento está em minha mente há muito tempo. Isso permite que você baixe o HLS ou o DASH, e não apenas uma lista de reprodução pronta, mas também uma transmissão ao vivo. E nunca vi nada parecido na Internet.

Levei cerca de três semanas para fazer pelo menos algo trabalhando comigo com pequenos intervalos. Eu conhecia C (apesar de não ter experiência), havia muitos problemas com ponteiros (é difícil rastrear o que vai aonde e até no código de outra pessoa), mas finalmente algo foi compilado sem erros. Após os primeiros sucessos, isso deu ainda mais entusiasmo para concluir a ideia.

Apenas algumas semanas e finalmente consegui fazer a primeira iteração do protocolo http de trabalho, e isso parece ser tudo?

Quando o mais difícil acaba


Neste ponto, eu tinha uma estrutura pronta, um pequeno formulário html com um campo de entrada de URL e um botão Iniciar, basicamente funcionava. Mas ainda era necessário escrever uma extensão para ignorar o CORS e carregar dados, fazer um armazenamento que escrevesse dados em pedaços, criar uma interface com exibição de progresso, tudo isso foi depurado para corrigir problemas em diferentes navegadores. Em geral, chegou a hora de finalmente tornar possível usá-lo.

Basicamente, o userscript foi criado, que era um proxy para buscar solicitações do ffmpeg para baixar dados.

Alguns dias depois, estava pronta uma extensão para Chrome e Firefox, que usando o webRequest coletava todos os links hls que o navegador carrega ao assistir a um vídeo.

No Firefox, como se viu, a API de extensão não permite gerenciar energia, a partir da qual você não pode impedir que o computador adormeça.

A extensão fica assim:



Apenas melhoramos a página na qual o site era um pouco, ferrou-ui, finalizou todos os lugares que foram criados.



Depois de testar diferentes maneiras de armazenar dados, eu revelei vários problemas:

Blob - o Chrome os grava na RAM e cai no disco quando transborda, mas apenas no OSX quando a memória transborda, o sistema operacional sai da conta e fecha todos os aplicativos que estavam abertos. E o Firefox geralmente sempre mantinha os dados na memória.

Armazenamento em cache- funciona como o IndexedDb, mas depois de gravar os dados, no blob do Chrome, eles permanecem na RAM (um bug ou um fitcha), mas acontece que os dados são gravados no armazenamento em cache (no disco) e também diminuem quando a memória está cheia volume no disco como blob.

IndexedDb - funciona como um relógio, sabe como armazenar blob, grava dados sem frescuras, mas o Firefox limita estritamente a quantidade de 2 GB.

Trabalhei um pouco nisso, consegui interromper o processo ffmpeg (via ponteiro), criei como fazer a escolha de formatos (ffprobe) e o tratamento de erros de rede.





E agora, você pode experimentar o resultado aqui

Para mim, isso é algo indispensável quando você precisa gravar um fluxo em um tweet ou baixar um VOD. Também funciona com um laptop, mixer e outros sites que transmitem conteúdo em HLS ou DASH (infelizmente, a implementação do DASH no ffmpeg é muito condicional e o live pode não ser baixado corretamente, porque não leva em consideração o intervalo de solicitação de fragmento).

Obrigado pela leitura!

Se você tiver dúvidas sobre ffmpeg e emscripten - write, tentarei responder.

All Articles