Compilation de FFmpeg en WebAssembly (= ffmpeg.js): Partie 2 - Compilation avec Emscripten



Liste des parties traduites de la série:


  1. Cuisine
  2. Compiler avec Emscripten (vous êtes ici)
  3. Convertir avi en mp4


A partir de cette partie, le matériel sera plus compliqué, alors n'hésitez pas à google pendant la lecture, si vous ne comprenez pas ce qui se passe.


De plus, je vais essayer de documenter la solution à d'éventuels problèmes afin que vous puissiez compiler la bibliothèque avec vos paramètres.


Dans cette partie, nous analyserons:


  1. Comment configurer un environnement pour Emscripten dans Docker
  2. Utiliser emconfigure et emmake
  3. Comment résoudre les problèmes lors de la compilation de FFmpeg avec Emscripten



Comment configurer un environnement pour Emscripten dans Docker


Dans la première partie, nous avons compilé FFmpeg avec gcc et pouvons passer à l'utilisation de l'image Docker avec emscripten.


J'utiliserai la version 1.38.45 de trzeci / emscripten:


$ docker pull trzeci/emscripten:1.38.45 

Étant donné que l'image prend environ 1 Go, le processus peut prendre un certain temps.


Nous allons maintenant trouver la configuration correcte pour compiler FFmpeg en emscripten par essais et erreurs, ce qui nécessitera de la persévérance et la lecture de gros volumes de documentation. Exécutez le conteneur avec emscripten et montez les sources FFmpeg dans le répertoire / src .


 # ,      FFmpeg $ docker run -it \ -v $PWD:/src \ trzeci/emscripten:1.38.45 \ /bin/bash 

À l'intérieur du conteneur, exécutez ls --color pour voir quelque chose comme ceci:



Utiliser emconfigure et emmake . Comment résoudre les problèmes de compilation


Commençons par la configuration. Dans la première partie, nous avons effectué ./configure --disable-x86asm , dans emscripten cela est réalisé avec la commande emconfigure ./configure --disable-x86asm . (Pour plus de détails sur l'utilisation d'emconfigure, voir ici )


 $ emconfigure ./configure --disable-x86asm 

Et puisque nous n'avons pas vu d'erreurs, il ne reste plus qu'à exécuter emmake make -j4 et obtenir le convoité FFmpeg.js? Malheureusement non. L'une des tâches les plus importantes pour emconfigure est de remplacer le compilateur gcc par emcc (ou g ++ par em ++), mais le résultat de ./configure produit toujours gcc.


 root@57ab95def750:/src# emconfigure ./configure --disable-x86asm emscripten sdl2-config called with /emsdk_portable/emscripten/tag-1.38.45/system/bin/sdl2-config --cflags emscripten sdl2-config called with /emsdk_portable/emscripten/tag-1.38.45/system/bin/sdl2-config --libs install prefix /usr/local source path . C compiler gcc #    emcc C library glibc ARCH x86 (generic) big-endian no runtime cpu detection yes standalone assembly no x86 assembler nasm 

Toute automatisation a ses limites, et dans ce cas, malheureusement, nous devons tout faire manuellement. Voyons s'il y a des arguments pour nous aider:


 $ ./configure --help 

Sous la section des options de la chaîne d'outils , nous voyons des arguments pour indiquer le type de compilateur.


 root@57ab95def750:/src# ./configure --help Usage: configure [options] Options: [defaults in brackets after descriptions]Help options: ... Toolchain options: ... --nm=NM use nm tool NM [nm -g] --ar=AR use archive tool AR [ar] --as=AS use assembler AS [] --ln_s=LN_S use symbolic link tool LN_S [ln -s -f] --strip=STRIP use strip tool STRIP [strip] --windres=WINDRES use windows resource compiler WINDRES [windres] --x86asmexe=EXE use nasm-compatible assembler EXE [nasm] --cc=CC use C compiler CC [gcc] --cxx=CXX use C compiler CXX [g++] --objcc=OCC use ObjC compiler OCC [gcc] --dep-cc=DEPCC use dependency generator DEPCC [gcc] --nvcc=NVCC use Nvidia CUDA compiler NVCC [nvcc] --ld=LD use linker LD [] ... 

Utilisons-les dans emscripten


 $ emconfigure ./configure \ --disable-x86asm \ --nm="llvm-nm -g" \ --ar=emar \ --cc=emcc \ --cxx=em++ \ --objcc=emcc \ --dep-cc=emcc 

Maintenant, exécuter ./configure prendra plus de temps, mais en conséquence nous obtenons emcc.


 root@57ab95def750:/src# emconfigure ... emscripten sdl2-config called with /emsdk_portable/emscripten/tag-1.38.45/system/bin/sdl2-config --cflags emscripten sdl2-config called with /emsdk_portable/emscripten/tag-1.38.45/system/bin/sdl2-config --libs install prefix /usr/local source path . C compiler emcc # emcc    C library ARCH x86 (generic) big-endian no runtime cpu detection yes standalone assembly no 

Voyons comment se passe la compilation.


 $ emmake make -j4 

Et immédiatement une erreur ...


 root@57ab95def750:/src# emmake make -j4 ... ./libavutil/x86/timer.h:39:24: error: invalid output constraint '=a' in asm : "=a" (a), "=d" (d)); ^ 

Le message montre que l'erreur est en quelque sorte liée à asm. Ouvrez ./libavutil/x86/timer.h pour voir que le problème est dans l'assembleur en ligne x86, ce qui est incompatible avec WebAssembly, donc désactivez-le.


 $ emconfigure ./configure \ --disable-x86asm \ --disable-inline-asm \ #   asm --nm="llvm-nm -g" \ --ar=emar \ --cc=emcc \ --cxx=em++ \ --objcc=emcc \ --dep-cc=emcc 

Essayons de compiler à nouveau.


 $ emmake make -j4 

La compilation se poursuit jusqu'à l'erreur suivante.


 root@57ab95def750:/src# emmake make -j4 ... AR libavdevice/libavdevice.a AR libavfilter/libavfilter.a AR libavformat/libavformat.a AR libavcodec/libavcodec.a AR libswresample/libswresample.a AR libswscale/libswscale.a AR libavutil/libavutil.a HOSTLD doc/print_options GENTEXI doc/avoptions_format.texi /bin/sh: 1: doc/print_options: Exec format error doc/Makefile:59: recipe for target 'doc/avoptions_format.texi' failed make: *** [doc/avoptions_format.texi] Error 2 make: *** Waiting for unfinished jobs.... 

Quelque chose lié à la génération de documentation, dont nous n'avons absolument pas besoin, alors désactivez-le.


 $ emconfigure ./configure \ --disable-x86asm \ --disable-inline-asm \ --disable-doc \ #    --nm="llvm-nm -g" \ --ar=emar \ --cc=emcc \ --cxx=em++ \ --objcc=emcc \ --dep-cc=emcc 

Nous recommençons.


 $ emmake make -j4 

Maintenant, l'erreur s'est produite au stade de la bande.


 root@57ab95def750:/src# emmake make -j4 ... STRIP ffmpeg STRIP ffprobe strip:ffmpeg_g: File format not recognized strip:ffprobe_g: File format not recognized Makefile:101: recipe for target 'ffmpeg' failed make: *** [ffmpeg] Error 1 make: *** Waiting for unfinished jobs.... Makefile:101: recipe for target 'ffprobe' failed make: *** [ffprobe] Error 1 

Le recadrage natif étant incompatible avec notre version de WebAssembly, nous le désactiverons également.


 $ emconfigure ./configure \ --disable-x86asm \ --disable-inline-asm \ --disable-doc \ --disable-stripping \ #  strip --nm="llvm-nm -g" \ --ar=emar \ --cc=emcc \ --cxx=em++ \ --objcc=emcc \ --dep-cc=emcc 

Quatrième tentative.


 $ emmake make -j4 

Enfin, le processus s'est terminé sans erreur. Mais seulement à la sortie, nous avons obtenu le fichier ffmpeg , qui ne démarre pas, et ce n'est pas un fichier js (ou un fichier wasm). Pour obtenir le fichier js, nous devons ajouter -o ffmpeg.js à la commande emcc, ce qui peut se faire de deux manières:


  1. Changer le Makefile de FFmpeg lui-même
  2. Ajouter une compilation / liaison supplémentaire

Nous choisirons la deuxième voie, car je ne veux pas toucher aux sources FFmpeg à cause des effets secondaires possibles. Nous trouvons donc comment ffmpeg est généré à l'aide de make. C'est là que l'option make est pratique pour exécuter un essai à sec.


 $ emmake make -n 

Nous voyons l'équipe de génération.


 root@57ab95def750:/src# emmake make -n ... printf "LD\t%s\n" ffmpeg_g; emcc -Llibavcodec -Llibavdevice -Llibavfilter -Llibavformat -Llibavresample -Llibavutil -Llibpostproc -Llibswscale -Llibswresample -Wl,--as-needed -Wl,-z,noexecstack -Wl,--warn-common -Wl,-rpath-link=libpostproc:libswresample:libswscale:libavfilter:libavdevice:libavformat:libavcodec:libavutil:libavresample -Qunused-arguments -o ffmpeg_g fftools/ffmpeg_opt.o fftools/ffmpeg_filter.o fftools/ffmpeg_hw.o fftools/cmdutils.o fftools/ffmpeg.o -lavdevice -lavfilter -lavformat -lavcodec -lswresample -lswscale -lavutil -lm -pthread -lm -lm -pthread -lm -lm -lm -pthread -lm printf "CP\t%s\n" ffmpeg; cp -p ffmpeg_g ffmpeg ... 

Il y a beaucoup de choses inutiles, alors supprimons les arguments inutilisés (que vous verrez à la fin de la compilation), nettoyez un peu et renommez ffmpeg_g en ffmpeg.js .


 $ emcc \ -Llibavcodec -Llibavdevice -Llibavfilter -Llibavformat -Llibavresample -Llibavutil -Llibpostproc -Llibswscale -Llibswresample \ -Qunused-arguments \ -o ffmpeg.js fftools/ffmpeg_opt.o fftools/ffmpeg_filter.o fftools/ffmpeg_hw.o fftools/cmdutils.o fftools/ffmpeg.o \ -lavdevice -lavfilter -lavformat -lavcodec -lswresample -lswscale -lavutil -lm -pthread 

Cela aurait dû fonctionner, mais nous rencontrerons le problème du manque de mémoire.


 root@57ab95def750:/src# emcc ... shared:ERROR: Memory is not large enough for static data (11794000) plus the stack (5242880), please increase TOTAL_MEMORY (16777216) to at least 17037904 

Ajoutez l'argument TOTAL_MEMORY pour augmenter la taille de la mémoire (33554432 octets: = 32 Mo).


 $ emcc \ -Llibavcodec -Llibavdevice -Llibavfilter -Llibavformat -Llibavresample -Llibavutil -Llibpostproc -Llibswscale -Llibswresample \ -Qunused-arguments \ -o ffmpeg.js fftools/ffmpeg_opt.o fftools/ffmpeg_filter.o fftools/ffmpeg_hw.o fftools/cmdutils.o fftools/ffmpeg.o \ -lavdevice -lavfilter -lavformat -lavcodec -lswresample -lswscale -lavutil -lm -pthread \ -s TOTAL_MEMORY=33554432 

Enfin, nous avons obtenu nos fichiers js et wasm


 root@57ab95def750:/src# ls ffmpeg* ffmpeg ffmpeg.js ffmpeg.js.mem ffmpeg.wasm ffmpeg.worker.js ffmpeg_g 

Créez test.html pour tester FFmpeg.js


 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title></title> <script src="./ffmpeg.js"></script> </head> <body> </body> </html> 

Commençons le serveur facile (en exécutant python2 -m SimpleHTTPServer ) et ouvrons la page résultante ( http: // localhost: 8000 / test.html ) , puis ouvrons Chrome DevTools.



Comme vous pouvez le voir, FFmpeg fonctionne à moitié avec un péché, vous pouvez donc maintenant commencer à polir ffmpeg.js.


Le script de construction complet se trouve dans ce référentiel (build-with-docker.sh et build-js.sh)

.

Source: https://habr.com/ru/post/fr473134/


All Articles