├── .gitignore ├── .gitmodules ├── Dockerfile ├── LICENSE ├── README.md ├── build.sh ├── scripts ├── build-deps.sh ├── build.sh ├── configure.sh ├── deps │ ├── x264.sh │ └── zlib.sh ├── install.sh └── optimize.sh └── videos ├── video-1080p-60fps-2s.mp4 └── video-15s.avi /.gitignore: -------------------------------------------------------------------------------- 1 | binaryen 2 | wasi-sdk 3 | 4 | build 5 | *.wasm 6 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "FFmpeg"] 2 | path = FFmpeg 3 | url = https://github.com/FFmpeg/FFmpeg.git 4 | [submodule "deps/zlib"] 5 | path = deps/zlib 6 | url = https://github.com/madler/zlib.git 7 | [submodule "deps/x264"] 8 | path = deps/x264 9 | url = https://github.com/FFmpeg-WASI/x264.git 10 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # Setup 2 | FROM ubuntu:20.04 AS setup 3 | 4 | WORKDIR /app 5 | 6 | # https://trac.ffmpeg.org/wiki/CompilationGuide/Ubuntu 7 | ENV DEBIAN_FRONTEND=noninteractive 8 | RUN apt-get update -qq && apt-get -y install \ 9 | autoconf \ 10 | automake \ 11 | build-essential \ 12 | cmake \ 13 | git-core \ 14 | libass-dev \ 15 | libfreetype6-dev \ 16 | libgnutls28-dev \ 17 | libmp3lame-dev \ 18 | libsdl2-dev \ 19 | libtool \ 20 | libva-dev \ 21 | libvdpau-dev \ 22 | libvorbis-dev \ 23 | libxcb1-dev \ 24 | libxcb-shm0-dev \ 25 | libxcb-xfixes0-dev \ 26 | meson \ 27 | ninja-build \ 28 | pkg-config \ 29 | texinfo \ 30 | wget \ 31 | yasm \ 32 | zlib1g-dev \ 33 | # Additional dependencies 34 | libx264-dev 35 | 36 | # Install script 37 | COPY scripts/install.sh scripts/install.sh 38 | RUN ./scripts/install.sh 39 | 40 | # Copy sources 41 | COPY FFmpeg FFmpeg 42 | COPY deps deps 43 | COPY scripts scripts 44 | 45 | # Build scripts 46 | FROM setup AS build 47 | RUN ./scripts/build-deps.sh 48 | RUN ./scripts/configure.sh 49 | RUN ./scripts/build.sh 50 | RUN ./scripts/optimize.sh 51 | 52 | # Export 53 | FROM scratch AS export 54 | COPY --from=build /app/ffmpeg.wasm /app/ffprobe.wasm ./ 55 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 SebastiaanYN 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # FFmpeg-WASI 2 | 3 | FFmpeg-WASI compiles FFmpeg to WASM. Unlike [ffmpeg.wasm](https://github.com/ffmpegwasm/ffmpeg.wasm) the WASM binary is standalone and required no JavaScript glue. 4 | 5 | ## Clone 6 | 7 | To get started you can clone the repository and its submodules. 8 | 9 | ```sh 10 | git clone --recursive https://github.com/SebastiaanYN/FFmpeg-WASI.git 11 | ``` 12 | 13 | ## Build 14 | 15 | Building the project requires several dependencies and steps to be executed. To make it easier to build the project a Dockerfile is provided to build `ffmpeg.wasm` and `ffprobe.wasm`. 16 | 17 | ```sh 18 | DOCKER_BUILDKIT=1 docker build -t ffmpeg-wasi --output . . 19 | # or 20 | ./build.sh 21 | ``` 22 | 23 | On Apple M1/M2 machines it might be necessary to set `DOCKER_DEFAULT_PLATFORM=linux/amd64`. 24 | 25 | ```sh 26 | DOCKER_DEFAULT_PLATFORM=linux/amd64 DOCKER_BUILDKIT=1 docker build -t ffmpeg-wasi --output . . 27 | # or 28 | DOCKER_DEFAULT_PLATFORM=linux/amd64 ./build.sh 29 | ``` 30 | 31 | ## Examples 32 | 33 | You can use any WASI compatible WASM runtime, like `wasmtime` and `wasmer`, to run the binary. 34 | 35 | Generating a thumbnail from a video. 36 | 37 | ```sh 38 | wasmtime --dir videos ffmpeg.wasm -- -i videos/video-1080p-60fps-2s.mp4 -ss 1 -vframes 1 videos/out.png 39 | ``` 40 | 41 | Converting from one video format to another. 42 | 43 | ```sh 44 | wasmtime --dir videos ffmpeg.wasm -- -i videos/video-15s.avi -c:v libx264 videos/out.mp4 45 | ``` 46 | 47 | ## Encoders 48 | 49 | Currently only `zlib` and `x264` are included in the build, but adding more should be fairly straightforward. 50 | 51 | ## Limitations 52 | 53 | Some codecs require multiple iterations over the input/output (like mp4), which means you cannot pipe the input/output. Instead you need to write the file to disk so FFmpeg can read it from there. Depending on where you're running your code this may not be possible. 54 | 55 | Additionally, WASI is still a very young technology so some FFmpeg features, like threading and networking, have to be disabled. In the future it might be possible to enable these features. 56 | 57 | ## License 58 | 59 | This project is licensed under MIT. Be aware that the licenses of FFmpeg and other dependencies may still apply. 60 | -------------------------------------------------------------------------------- /build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -euox pipefail 4 | 5 | DOCKER_BUILDKIT=1 docker build -t ffmpeg-wasi --output . . 6 | -------------------------------------------------------------------------------- /scripts/build-deps.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -euox pipefail 4 | 5 | for f in ./scripts/deps/*.sh; do 6 | bash "$f" 7 | done 8 | -------------------------------------------------------------------------------- /scripts/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -euox pipefail 4 | 5 | cd FFmpeg 6 | sed -i 's,tempnam,NULL; //tempnam,g' ./libavutil/file_open.c 7 | ../build/compile.sh 8 | cp ffmpeg ../ffmpeg.wasm 9 | cp ffprobe ../ffprobe.wasm 10 | git rev-parse --is-inside-work-tree && git reset --hard 11 | cd .. 12 | -------------------------------------------------------------------------------- /scripts/configure.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -euox pipefail 4 | 5 | FFMPEG_CONFIG_FLAGS_BASE=( 6 | --target-os=none # use none to prevent any os specific configurations 7 | --arch=x86_32 # use x86_32 to achieve minimal architectural optimization 8 | --enable-cross-compile # enable cross compile 9 | --disable-x86asm # disable x86 asm 10 | --disable-inline-asm # disable inline asm 11 | --disable-stripping # disable stripping 12 | --disable-doc # disable doc 13 | --disable-debug # disable debug info, required by closure 14 | --disable-runtime-cpudetect # disable runtime cpu detect 15 | --disable-autodetect # disable external libraries auto detect 16 | --disable-network # https://github.com/WebAssembly/wasi-sdk/issues/112 17 | --disable-pthreads 18 | --disable-w32threads 19 | --disable-os2threads 20 | --pkg-config-flags="--static" 21 | --enable-lto # use link-time optimization 22 | 23 | --nm=../wasi-sdk/bin/nm 24 | --ar=../wasi-sdk/bin/ar 25 | --ranlib=../wasi-sdk/bin/ranlib 26 | --cc=../wasi-sdk/bin/clang 27 | --cxx=../wasi-sdk/bin/clang++ 28 | --objcc=../wasi-sdk/bin/clang 29 | --dep-cc=../wasi-sdk/bin/clang 30 | 31 | --enable-gpl 32 | --enable-libx264 33 | --enable-zlib 34 | 35 | --extra-cflags="-I../build/include" 36 | --extra-ldflags="-L../build/lib" 37 | ) 38 | 39 | mkdir -p build 40 | 41 | cd FFmpeg 42 | ./configure ${FFMPEG_CONFIG_FLAGS_BASE[@]} 43 | 44 | make -n | 45 | # Add cflags that ./configure does not accept 46 | sed 's/clang /clang -D_WASI_EMULATED_PROCESS_CLOCKS -lwasi-emulated-process-clocks /g' | 47 | sed 's/clang /clang -D_WASI_EMULATED_SIGNAL -lwasi-emulated-signal /g' | 48 | sed 's/clang /clang -msimd128 /g' | 49 | cat > ../build/compile.sh 50 | chmod +x ../build/compile.sh 51 | cd .. 52 | -------------------------------------------------------------------------------- /scripts/deps/x264.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -euox pipefail 4 | 5 | cd deps/x264 6 | CC=../../wasi-sdk/bin/clang \ 7 | AR=../../wasi-sdk/bin/ar \ 8 | RANLIB=../../wasi-sdk/bin/ranlib \ 9 | ./configure \ 10 | --prefix=../../build \ 11 | --host=i686-gnu \ 12 | --enable-static \ 13 | --disable-cli \ 14 | --disable-asm \ 15 | --disable-thread \ 16 | --extra-cflags="-D_WASI_EMULATED_SIGNAL -msimd128" \ 17 | --extra-ldflags="-lwasi-emulated-signal" 18 | sed -i 's/#define HAVE_MALLOC_H 1/#define HAVE_MALLOC_H 0/g' config.h 19 | make install-lib-static 20 | cd ../.. 21 | -------------------------------------------------------------------------------- /scripts/deps/zlib.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -euox pipefail 4 | 5 | cd deps/zlib 6 | CC=../../wasi-sdk/bin/clang \ 7 | AR=../../wasi-sdk/bin/ar \ 8 | RANLIB=../../wasi-sdk/bin/ranlib \ 9 | prefix=../../build \ 10 | CFLAGS="-msimd128" \ 11 | ./configure --static 12 | make install 13 | git rev-parse --is-inside-work-tree && git reset --hard 14 | cd ../.. 15 | -------------------------------------------------------------------------------- /scripts/install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -euox pipefail 4 | 5 | WASI_VERSION=20 6 | WASI_VERSION_FULL=${WASI_VERSION}.0 7 | wget https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-${WASI_VERSION}/wasi-sdk-${WASI_VERSION_FULL}-linux.tar.gz 8 | tar xvf wasi-sdk-${WASI_VERSION_FULL}-linux.tar.gz 9 | rm wasi-sdk-${WASI_VERSION_FULL}-linux.tar.gz 10 | mv wasi-sdk-${WASI_VERSION_FULL} wasi-sdk 11 | 12 | BINARYEN_VERSION=113 13 | wget https://github.com/WebAssembly/binaryen/releases/download/version_${BINARYEN_VERSION}/binaryen-version_${BINARYEN_VERSION}-x86_64-linux.tar.gz 14 | tar xvf binaryen-version_${BINARYEN_VERSION}-x86_64-linux.tar.gz 15 | rm binaryen-version_${BINARYEN_VERSION}-x86_64-linux.tar.gz 16 | mv binaryen-version_${BINARYEN_VERSION} binaryen 17 | -------------------------------------------------------------------------------- /scripts/optimize.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -euox pipefail 4 | 5 | ./binaryen/bin/wasm-opt -O3 -o ffmpeg.wasm ffmpeg.wasm 6 | ./binaryen/bin/wasm-opt -O3 -o ffprobe.wasm ffprobe.wasm 7 | -------------------------------------------------------------------------------- /videos/video-1080p-60fps-2s.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SebastiaanYN/FFmpeg-WASI/a40b94b8878757da0b9f39fddfe5488d0f26b244/videos/video-1080p-60fps-2s.mp4 -------------------------------------------------------------------------------- /videos/video-15s.avi: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SebastiaanYN/FFmpeg-WASI/a40b94b8878757da0b9f39fddfe5488d0f26b244/videos/video-15s.avi --------------------------------------------------------------------------------