将FFmpeg编译为WebAssembly(= ffmpeg.js):第2部分-使用Emscripten进行编译



该系列的翻译部分清单:


  1. 做饭
  2. 使用Emscripten编译 (您在这里)
  3. 转换AVI到MP4


从这一部分开始,材料将更加复杂,因此,如果您不了解正在发生的事情,请在阅读过程中不要犹豫地使用google。


另外,我将尝试记录可能出现的问题的解决方案,以便您可以使用设置来编译库。


在这一部分中,我们将分析:


  1. 如何在Docker中为Emscripten设置环境
  2. 使用emconfigureemmake
  3. 用Emscripten编译FFmpeg时如何解决问题



如何在Docker中为Emscripten设置环境


第一部分中,我们使用gcc编译了FFmpeg,并且可以继续使用带有emscripten的Docker映像。


我将使用trzeci / emscripten版本1.38.45:


$ docker pull trzeci/emscripten:1.38.45 

由于映像大约需要1 GB,因此该过程可能需要一些时间。


现在,我们将找到正确的配置,可以通过反复试验将FFmpeg编译为脚本,这需要毅力并需要阅读大量文档。 使用emscripten运行容器,并将FFmpeg源安装在/ src目录中。


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

在容器内,执行ls --color以查看如下内容:



使用emconfigureemmake 。 如何解决编译问题


让我们从配置开始。 在第一部分中,我们执行了./configure --disable-x86asm ,在脚本中,这是通过emconfigure ./configure --disable-x86asm命令实现的 。 (有关使用emconfigure的详细信息,请参见此处


 $ emconfigure ./configure --disable-x86asm 

并且由于我们没有看到任何错误,因此仅执行emmake make -j4并获得令人垂涎的FFmpeg.js吗? 不幸的是,没有。 emconfigure的最重要任务之一是将gcc编译器替换为emcc(或将g ++替换为em ++),但是./configure的输出仍会生成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 

任何自动化都有其局限性,不幸的是,在这种情况下,我们必须手动执行所有操作。 让我们看看是否有任何参数可以帮助我们:


 $ ./configure --help 

在“ 工具链选项”部分下,我们看到用于指示编译器类型的参数。


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

让我们在emscripten中使用它们


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

现在执行./configure将花费更多时间,但结果是我们得到了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 

让我们看看编译如何进行。


 $ emmake make -j4 

并立即出错...


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

该消息表明该错误与asm有关。 打开./libavutil/x86/timer.h以查看问题出在x86内联汇编程序中,该汇编程序与WebAssembly不兼容,因此请将其关闭。


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

让我们尝试再次编译。


 $ emmake make -j4 

编译将继续直到下一个错误。


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

与文档生成有关的东西,我们绝对不需要,因此只需将其关闭即可。


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

我们再做一次。


 $ emmake make -j4 

现在,错误已发生在剥离阶段。


 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 

由于本机裁剪与我们的WebAssembly版本不兼容,因此我们也将其禁用。


 $ 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 

第四次尝试。


 $ emmake make -j4 

最终,该过程顺利结束,没有错误。 但是仅在输出处,我们得到了ffmpeg文件,该文件不会启动,并且不是js文件(或wasm文件)。 要获取js文件,我们需要在emcc命令中添加-o ffmpeg.js ,这可以通过两种方式完成:


  1. 更改FFmpeg本身的Makefile
  2. 添加其他编译/链接

我们将选择第二种方法,因为由于可能的副作用,我不想触摸FFmpeg源。 因此,我们发现如何使用make生成ffmpeg 。 在这里,make选项可方便地进行空运行。


 $ emmake make -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 ... 

有很多不必要的事情,因此让我们删除未使用的参数(您将在编译结束时看到),进行一些整理,并将ffmpeg_g重命名为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 

它本来可以工作,但是我们会遇到内存不足的问题。


 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 

添加参数TOTAL_MEMORY以增加内存大小(33554432字节:= 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 

最后我们得到了js和wasm文件


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

创建test.html以测试FFmpeg.js


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

让我们启动简易服务器(通过执行python2 -m SimpleHTTPServer )并打开结果页面( http:// localhost:8000 / test.html ,然后打开Chrome DevTools。



如您所见,FFmpeg的工作效率只有一半,因此现在您可以开始完善ffmpeg.js。


可以在此存储库中找到完整的构建脚本(build-with-docker.sh和build-js.sh)

Source: https://habr.com/ru/post/zh-CN473134/


All Articles