HLS en MP4 en utilisant ffmpeg dans un navigateur

salut! Depuis plus de deux mois maintenant, pendant mon temps libre, je scie une application Web pour convertir HLS et DASH en MP4 en utilisant emscripten et ffmpeg, ce qui me donne envie de partager comment j'ai réussi à le faire.

Dans cet article, je ne citerai pas le code source des modifications et correctifs ffmpeg, comme la plupart d'entre elles ont été faites sur le genou, et je ne suis pas très bon en C. Mais maintenant il y a assez d'articles pour vous aider.

introduction


Il y a deux ans, j'avais pour objectif de combiner une piste audio et vidéo en un seul fichier mp4. Ensuite, je me suis plongé dans emscripten et pour les bases, j'ai trouvé le référentiel ffmpeg.js à partir duquel j'ai beaucoup appris. Ensuite, j'ai presque pu atteindre l'objectif, bien que j'étais très conditionnellement orienté en C.
Comprendre le code source, ffmpeg a créé un correctif pour travailler avec le système de fichiers, où la lecture d'un fichier a provoqué une fonction asynchrone en js à partir de laquelle j'ai lu le blob du fichier et transféré le tampon, et lorsque l'écriture a été appelée Fonction js qui a envoyé des données de tampon au référentiel.

Mais il y avait un problème avec les fonctions asynchrones, que je ne pouvais pas résoudre correctement, elles fonctionnaient via asyncify (fastcomp), qui ne fonctionnait pas correctement dans certains cas, à savoir, l'exécution de code dans wasm ne s'arrêtait pas, sans attendre un résultat de la fonction js, c'est tout est tombé en panne. Ce problème a été résolu via l'indicateur EMTERPRETIFY_WHITELIST, qui a apparemment déplacé le code de wasm vers asm en même temps et l'a ralenti, et il était nécessaire de déboguer la pile d'appels et d'ajouter une fonction cassée à la liste à chaque exception.

En général, avec de tels problèmes, cela ne pouvait pas être appelé une solution de travail, sur laquelle tout cela restait une petite démonstration.



Un an et demi plus tard


Après avoir regardé un rapport sur Google Dev Summit sur les nouvelles fonctionnalités de WebAssambly , je suis allé voir comment allait emscripten et j'ai vu un message:
Emscripten émet WebAssembly en utilisant le backend de wasm LLVM en amont, depuis la version 1.39.0 (octobre 2019), et l'ancien backend fastcomp est obsolète

Je voulais aller essayer de reconstruire mes formats de reconditionnement. Environ une semaine, j'ai cherché sur Google comment résoudre de nouveaux problèmes de compilation et finalement tout mettre ensemble. Les changements n'étaient pas si nombreux, mais cela n'allait pas à cause du nouveau lieur de bibliothèques, et déjà désespéré de collecter au moins quelque chose, je viens de voir les bibliothèques problématiques (comme il s'est avéré, les bibliothèques elles-mêmes sont connectées et vous n'avez plus besoin de les pointer à la main pendant l'assemblage).

Et maintenant, le moment est venu où il s'est rassemblé et a gagné! Le problème avec le code asynchrone avait disparu, il n'était pas nécessaire de déboguer quoi que ce soit, cela a fonctionné comme il se doit depuis le début.

Ici, il me semble avoir atteint mon objectif, mais ... un nouveau est apparu.

Réécrire le protocole HTTP


Une telle pensée est dans mon esprit depuis longtemps. Cela peut vous permettre de télécharger HLS ou DASH, et non seulement une liste de lecture prête à l'emploi, mais aussi un flux en direct. Et je n'ai jamais rien vu de tel sur Internet.

Cela m'a pris environ trois semaines pour faire au moins quelque chose qui fonctionne avec moi avec de courtes pauses. Je connaissais C (tout en n'ayant aucune expérience), il y avait beaucoup de problèmes avec les pointeurs (il est difficile de suivre ce qui va où, et même dans le code de quelqu'un d'autre), mais enfin quelque chose a été compilé sans erreurs. Après les premiers succès, cela a donné encore plus d'enthousiasme pour compléter l'idée.

Juste quelques semaines, et finalement j'ai réussi à faire la première itération du protocole http de travail, et cela semble être tout?

Quand le plus dur est fini


À ce stade, j'avais un framework prêt, un petit formulaire html avec un champ de saisie url et un bouton de démarrage, en gros ça fonctionnait. Mais il était toujours nécessaire d'écrire une extension pour contourner CORS et charger les données, créer un stockage qui écrirait les données en morceaux, créer une interface avec affichage de la progression, déboguer le tout pour résoudre les problèmes dans différents navigateurs. D'une manière générale, le moment est venu de permettre enfin de l'utiliser.

Fondamentalement, un script utilisateur a été créé, qui était un proxy pour les demandes de récupération de ffmpeg pour télécharger des données.

Quelques jours plus tard, une extension pour Chrome et Firefox était prête, qui à l'aide de webRequest a collecté tous les liens hls que le navigateur charge lors du visionnage d'une vidéo.

Dans Firefox, il s'est avéré que l'API d'extension ne vous permet pas de gérer l'alimentation, à partir de laquelle vous ne pouvez pas empêcher l'ordinateur de s'endormir, hélas.

L'extension ressemble à ceci:



vient d'améliorer la page sur laquelle le site était un peu, du matériel vissé-ui, a finalisé tous les endroits qui ont été fouettés.



Après avoir testé différentes façons de stocker des données, j'ai révélé un certain nombre de problèmes:

Blob - Chrome les écrit sur la RAM et tombe sur le disque lorsqu'il déborde, mais uniquement dans OSX lorsque la mémoire déborde, le système d'exploitation quitte le compte et ferme toutes les applications ouvertes. Et Firefox a généralement toujours gardé les données en mémoire.

Stockage du cache- cela fonctionne comme IndexedDb, mais après avoir écrit des données, dans le blob Chrome, ils restent dans la RAM (soit un bogue, soit une fitcha), mais il s'avère que les données sont écrites sur le stockage en cache (sur le disque) et sont également supprimées lorsque la mémoire est pleine volume sur disque en tant qu'objet blob.

IndexedDb - fonctionne comme une horloge, sait stocker le blob, écrit des données sans fioritures, mais Firefox limite strictement la quantité de 2 Go.

J'y ai travaillé un peu, j'ai réussi à interrompre le processus ffmpeg (via un pointeur), j'ai trouvé comment faire un choix de formats (ffprobe) et gérer les erreurs réseau.





Et maintenant, vous pouvez essayer le résultat vous-même ici

Pour moi, c'est une chose indispensable lorsque vous devez enregistrer un flux sur un tweet ou télécharger un VOD. Il fonctionne également avec un ordinateur portable, un mélangeur et tout autre site qui diffuse du contenu en HLS ou DASH (hélas, l'implémentation DASH dans ffmpeg est très conditionnelle et en direct peut ne pas télécharger correctement, car elle ne prend pas en compte l'intervalle de demande de fragment).

Merci pour la lecture!

Si vous avez des questions sur ffmpeg et emscripten - écrivez, je vais essayer de répondre.

All Articles