Compilación de FFmpeg en WebAssembly (= ffmpeg.js): Parte 2 - Compilación con Emscripten



Lista de partes traducidas de la serie:


  1. Cocinando
  2. Compilando con Emscripten (estás aquí)
  3. Convertir avi a mp4


Comenzando con esta parte, el material será más complicado, así que no dude en buscar en Google durante la lectura, si no entiende lo que está sucediendo.


Además, intentaré documentar la solución a los posibles problemas para que pueda compilar la biblioteca con su configuración.


En esta parte analizaremos:


  1. Cómo configurar un entorno para Emscripten en Docker
  2. Usando emconfigure y emmake
  3. Cómo resolver problemas al compilar FFmpeg con Emscripten



Cómo configurar un entorno para Emscripten en Docker


En la primera parte, compilamos FFmpeg con gcc y podemos seguir usando la imagen Docker con emscripten.


Usaré trzeci / emscripten versión 1.38.45:


$ docker pull trzeci/emscripten:1.38.45 

Dado que la imagen toma aproximadamente 1 GB, el proceso puede demorar un poco.


Ahora encontraremos la configuración correcta para compilar FFmpeg en emscripten por prueba y error, lo que requerirá perseverancia y lectura de grandes volúmenes de documentación. Ejecute el contenedor con emscripten y monte las fuentes FFmpeg en el directorio / src .


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

Dentro del contenedor, ejecute ls --color para ver algo como esto:



Usando emconfigure y emmake . Cómo resolver problemas de compilación


Comencemos con la configuración. En la primera parte, realizamos ./configure --disable-x86asm , en emscripten esto se logra con el comando emconfigure ./configure --disable-x86asm . (Para obtener detalles sobre el uso de emconfigure, consulte aquí )


 $ emconfigure ./configure --disable-x86asm 

Y como no vimos ningún error, solo queda ejecutar emmake make -j4 y obtener el codiciado FFmpeg.js? Lamentablemente no. Una de las tareas más importantes para emconfigure es reemplazar el compilador gcc con emcc (o g ++ con em ++), pero el resultado de ./configure aún produce 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 

Cualquier automatización tiene sus límites, y en este caso, desafortunadamente, tenemos que hacer todo manualmente. Veamos si hay argumentos para ayudarnos:


 $ ./configure --help 

En la sección de opciones de Toolchain , vemos argumentos para indicar el tipo de compilador.


 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 [] ... 

Vamos a usarlos en emscripten


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

Ahora ejecutar ./configure llevará más tiempo, pero como resultado obtenemos 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 

Veamos cómo va la compilación.


 $ emmake make -j4 

E inmediatamente un error ...


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

El mensaje muestra que el error está relacionado de alguna manera con asm. Abra ./libavutil/x86/timer.h para ver que el problema está en el ensamblador en línea x86, que es incompatible con WebAssembly, así que apáguelo.


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

Intentemos compilar nuevamente.


 $ emmake make -j4 

La compilación continúa hasta el siguiente error.


 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.... 

Algo relacionado con la generación de documentación, que absolutamente no necesitamos, así que apáguelo.


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

Lo estamos haciendo de nuevo.


 $ emmake make -j4 

Ahora el error ha ocurrido en la etapa de tira.


 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 

Dado que el recorte nativo es incompatible con nuestra versión de WebAssembly, también lo desactivaremos.


 $ 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 

Cuarto intento.


 $ emmake make -j4 

Finalmente, el proceso finalizó sin errores. Pero solo en la salida obtuvimos el archivo ffmpeg , que no se inicia, y no es un archivo js (o un archivo wasm). Para obtener el archivo js, ​​necesitamos agregar -o ffmpeg.js al comando emcc, que se puede hacer de dos maneras:


  1. Cambiar el Makefile de FFmpeg sí mismo
  2. Agregar compilación / vinculación adicional

Elegiremos la segunda forma, ya que no quiero tocar las fuentes de FFmpeg debido a los posibles efectos secundarios. Entonces encontramos cómo se genera ffmpeg usando make. Aquí es donde la opción de hacer es útil para ejecutar una ejecución en seco.


 $ emmake make -n 

Vemos el equipo de generación.


 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 ... 

Hay muchas cosas innecesarias, así que eliminemos los argumentos no utilizados (que verá al final de la compilación), limpie un poco y cambie el nombre de ffmpeg_g a 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 

Debería haber funcionado, pero nos encontraremos con el problema de la falta de memoria.


 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 

Agregue el argumento TOTAL_MEMORY para aumentar el tamaño de la memoria (33554432 Bytes: = 32 MB).


 $ 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 

Finalmente obtuvimos nuestros archivos js y wasm


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

Cree test.html para probar FFmpeg.js


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

Comencemos el servidor fácil (ejecutando python2 -m SimpleHTTPServer ) y abra la página resultante ( http: // localhost: 8000 / test.html ) , luego abra Chrome DevTools.



Como puede ver, FFmpeg funciona a la mitad con un pecado, por lo que ahora puede comenzar a pulir ffmpeg.js.


El script de compilación completo se puede encontrar en este repositorio (build-with-docker.sh y build-js.sh)

.

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


All Articles