gogoend / blog

blogs, ideas, etc.
MIT License
9 stars 2 forks source link

使用 Emscripten 将 FFMPEG 4.3编译到 WASM #42

Open gogoend opened 4 years ago

gogoend commented 4 years ago

自从上次写了尝试写一个 Web Assembly - HelloWorld示例程序(C语言)一文,并试着搞出一个HelloWorld后,笔者突然在想一个问题,既然WASM这么有意思,那是不是也可以将一些使用C语言编写的工具也编译到WASM,来使其可以在浏览器上运行?在此随便试试。 因此,笔者各种百度了一下,想试试看能否将FFMPEG移植到浏览器上跑起来。

参考资料

编译环境

Ubuntu 18.04(WSL 2) Emscripten 1.39.20(e7e39da9c81faecd9ecf44065cee864d76e4e34d)

Emscripten编译环境的搭建过程见”尝试写一个 Web Assembly - HelloWorld示例程序(C语言)“。

编译步骤

  1. 生成配置文件
    emconfigure ./configure --cc="emcc" --cxx="em++" --ar="emar" \
    --ranlib=emranlib --prefix=$(pwd)/dist --enable-cross-compile --target-os=none \
    --arch=x86_64 --cpu=generic --disable-ffplay --disable-ffprobe --disable-asm \
    --disable-doc --disable-devices --disable-indevs --disable-outdevs --disable-network \
    --disable-w32threads --disable-pthreads --enable-ffmpeg --enable-static --disable-shared \
    --enable-decoder=pcm_mulaw --enable-decoder=pcm_alaw --enable-decoder=adpcm_ima_smjpeg \
    --enable-decoder=aac --enable-decoder=hevc --enable-decoder=h264 --enable-protocol=file \
    --disable-stripping
  2. 编译文件。使用 -j 参数可以指定编译时使用CPU核心的数量,若后方无数字则使用全部核心来进行编译。
    emmake make -j 8
  3. 汇编(链接?)文件。将上一步骤产生的文件进行合并,最终产生一个可执行文件。
    emcc \
    -Llibavcodec -Llibavdevice -Llibavfilter -Llibavformat -Llibavresample -Llibavutil -Llibpostproc -Llibswscale -Llibswresample \
    -Qunused-arguments -Oz \
    -o javascript/ffmpeg-core.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 \
    -s MODULARIZE=1 \
    -s EXPORTED_FUNCTIONS="[_ffmpeg]" \
    -s EXTRA_EXPORTED_RUNTIME_METHODS="[cwrap, FS, getValue, setValue]" \
    -s TOTAL_MEMORY=33554432 \
    -s ALLOW_MEMORY_GROWTH=1

    这里最终产生的文件将会存为javascript/ffmpeg-core.js,在ffmpeg-core.js旁边的还有ffmpeg-core.wasm。事实上,ffmpeg-core.js是由Emscripten自动生成的文件,该文件包含了一些Polyfill,以及各类工具、API,用于辅助ffmpeg-core.wasm运行。例如文件系统API,可以在浏览器环境下模拟出一个虚拟文件系统,从而对文件进行存取。 这一步过后,可以新建一个HTML文件,将ffmpeg-core.js进行引入。 此时若执行代码中暴露的_ffmpeg()方法,将可以得到如下输出: image

一个可用的编译脚本

#!/bin/bash -x

set -e -o pipefail

BUILD_DIR=$PWD/build

# build_x264() {
#   cd third_party/x264
#   emconfigure ./configure \
#     --prefix=$BUILD_DIR \
#     --enable-static \
#     --disable-cli \
#     --disable-asm
#   emmake make install-lib-static
#   cd -
# }

FFMPEG_CONFIG_FLAGS_BASE=(
  --arch=x86_64
  --enable-gpl
  --enable-cross-compile 
  --disable-asm
  --disable-stripping 
  --disable-programs 
  --disable-doc 
  --disable-debug 
  --disable-runtime-cpudetect 
  --disable-autodetect
  --extra-cflags="-I$BUILD_DIR/include"
  --extra-cxxflags="-I$BUILD_DIR/include"
  --extra-ldflags="-L$BUILD_DIR/lib"
  --pkg-config-flags="--static"
  --nm="llvm-nm"
  --ar=emar
  --ranlib=emranlib
  --cc=emcc
  --cxx=em++
  --objcc=emcc
  --dep-cc=emcc
  # 解码器
  --enable-decoder=pcm_mulaw
  --enable-decoder=pcm_alaw
  --enable-decoder=adpcm_ima_smjpeg
  --enable-decoder=aac
  --enable-decoder=hevc
  --enable-decoder=h264
  --enable-protocol=file
  # 其他禁用项目
  --disable-devices
  --disable-indevs
  --disable-outdevs
  --disable-network
  --disable-w32threads
  --disable-pthreads
)

configure_ffmpeg() {
  emconfigure ./configure "${FFMPEG_CONFIG_FLAGS_BASE[@]}"
}

make_ffmpeg() {
  NPROC=$(grep -c ^processor /proc/cpuinfo)
  emmake make -j${NPROC}
}

build_ffmpegjs() {
  emcc \
    -I. -I./fftools -I$BUILD_DIR/include \
    -Llibavcodec -Llibavdevice -Llibavfilter -Llibavformat -Llibavresample -Llibavutil -Llibpostproc -Llibswscale -Llibswresample -Llibpostproc -L${BUILD_DIR}/lib \
    -Qunused-arguments -Oz \
    -o dist/ffmpeg-core.js fftools/ffmpeg_opt.c fftools/ffmpeg_filter.c fftools/ffmpeg_hw.c fftools/cmdutils.c fftools/ffmpeg.c \
    -lavdevice -lavfilter -lavformat -lavcodec -lswresample -lswscale -lavutil -lpostproc -lm \
    --closure 1 \
    -s USE_SDL=2 \
    -s MODULARIZE=1 \
    -s SINGLE_FILE=1 \
    -s EXTRA_EXPORTED_RUNTIME_METHODS="[cwrap, FS, getValue, setValue]" \
    -s EXPORTED_FUNCTIONS="[_ffmpeg]" \
    -s TOTAL_MEMORY=33554432 \
    -s ALLOW_MEMORY_GROWTH=1
}

main() {
  # build_x264
  configure_ffmpeg
  make_ffmpeg
  build_ffmpegjs
}

main "$@"
JoyJoyJoyXu commented 3 years ago

有windows编译的嘛

gogoend commented 3 years ago

有windows编译的嘛

Windows下我没编成功,可能是我比较菜

上面这个脚本执行在Windows的Linux子系统下是编成功的,用 Win10 或许可以装WSL

selmalee commented 3 years ago

编译出来的版本因为是单线程,会阻塞页面渲染,有木有考虑用web worker加载运行ffmpeg呢?

gogoend commented 3 years ago

编译出来的版本因为是单线程,会阻塞页面渲染,有木有考虑用web worker加载运行ffmpeg呢?

@seminelee 咋说呢,这个有考虑,不过我可能还没找到相关资料;其实我目前工作不涉及这个,很想挤出时间来看看,但就是想做的事情太多,有点点难挤