Skip to content

Instantly share code, notes, and snippets.

@basperheim
Last active June 28, 2022 19:58
Show Gist options
  • Save basperheim/8f341eb67fbcfc52b33e72e0c24a6477 to your computer and use it in GitHub Desktop.
Save basperheim/8f341eb67fbcfc52b33e72e0c24a6477 to your computer and use it in GitHub Desktop.
x86 & Apple Silicon Cross-Compatible Docker Setup for FFmpeg

Multi-Architecture Cross-Compatible Docker Setup for FFmpeg

Here's a Docker setup I used to help speed up FFmpeg in the containers by using different compiled binaries for each respective architecture. If someone knows of a better way to do this, or a cleaner way to do this, please let me know.

I've tested this on Ubuntu and Apple Silicon host machines.

Compile FFmpeg for your Apple Silicon

You can also get some ARM builds from John Van Sickle's site if you don't want to compile it yourself. In the end I went with an ARM Linux pre-compiled build for the Alpine Docker container.

Docker Compose File

In your docker-compose.yml file make sure to mount the binaries for each architecture in the container's volume field. Make sure to do this for every container that's going to make use of the ffmpeg commands:

volumes:
  - ./ffmpeg5_x86/usr/local/bin/ffmpeg
  - ./ffmpeg5_arm/usr/local/bin/ffprobe

Dockerfile

Here's an example Dockerfile that uses an NodeJS-Alpine distro with the apk installer:

FROM node:14-alpine3.14 as base

I needed to add g++ and make when building my containers on an Apple Silicon machine. I'm guessing because Docker makes the containers compile x86 things to ARM, but I'm not sure:

# add cURL library
RUN apk --no-cache add curl nano g++ make exiftool

Then you just need to use uname to check if it's 'aarch64', and use the ffmpeg5_x86 if it isn't:

# if aarch64 (Apple Silicon) then use ARM binary of FFmpeg
ENV BASH_PROFILE="/root/.ashrc"
RUN if [[ $(uname -m) == 'aarch64' ]]; \
	then printf "\ncp /usr/local/bin/ffmpeg5_arm64 /usr/local/bin/ffmpeg" > "$BASH_PROFILE"; \
	else printf "\ncp /usr/local/bin/ffmpeg5_x86 /usr/local/bin/ffmpeg" > "$BASH_PROFILE"; fi

Obviously the above commands won't work if you're running this on an ARM single-board computer or something. You'll need to check for something else, I'm not sure.

# ERROR: cp: can't stat '/usr/local/bin/ffmpeg5_arm64': No such file or directory
# RUN sh /root/.ashrc # <-- THIS DIDN'T WORK

# append the FFmpeg installation to PATH
ENV PATH="/usr/local/bin/:${PATH}"

This last RUN command didn't work for me, and even RUN source /root/.ashrc did nothing. After building I had to docker exec -it into the containers and source the files manually, or use a NodeJS or Python script do it as a child process.

Run bash script using NodeJS

Here's some async NodeJS code that should run the script if the FFmpeg command doesn't work as a child process:

const util = require("util");
const exec = util.promisify(require("child_process").exec);

await exec("ffmpeg -version", { maxBuffer: 1024 * 500, 3000 })
    .then((stdout, stderr) => {
        if (stdout && stdout.includes("ffmpeg version")) {

        } else {
            console.log("FFmpeg command not valid. Need to cp binary from target build.");
            exec("sh /root/.ashrc") // run the command to move the correct binary file
        }
    })
    .catch((err) => {
        console.error("exec() error =>", err);
    });

After it's copied the correct target binary to the /usr/local/bin directory the ffmpeg -version command, from inside the container, should return something like this:

ffmpeg -version
ffmpeg version 5.0.1 Copyright (c) 2000-2022 the FFmpeg developers
built with Apple clang version 13.0.0 (clang-1300.0.29.30)

..or this is you used a pre-compiled johnvansickle build:

ffmpeg version 5.0.1-static https://johnvansickle.com/ffmpeg/  Copyright (c) 2000-2022 the FFmpeg developers
  built with gcc 8 (Debian 8.3.0-6)

An output like zsh: exec format error: ... means that the binary was not compiled for the Linux distro/container, and you'll need to find or build another one.

Install Python

Here's how to install Python in your NodeJS/Alpine container:

# Install the latest version of Python 3, PIP, and make 'python' available in path
RUN apk add --update --no-cache \
	python3 \
	py3-pip && \
	ln -sf python3 /usr/bin/python && \
	alias python3=/usr/bin/python

Complete Dockerfile

FROM node:14-alpine3.14 as base

# Install the latest version of Python 3, PIP, and make 'python' available in path
RUN apk add --update --no-cache \
	python3 \
	py3-pip && \
	ln -sf python3 /usr/bin/python && \
    alias python3=/usr/bin/python

# add cURL library
RUN apk --no-cache add curl nano g++ make exiftool

# if aarch64 (Apple Silicon) then use ARM binary of FFmpeg
ENV BASH_PROFILE="/root/.ashrc"
RUN if [[ $(uname -m) == 'aarch64' ]]; \
	then printf "\ncp /usr/local/bin/ffmpeg5_arm64 /usr/local/bin/ffmpeg" > "$BASH_PROFILE"; \
	else printf "\ncp /usr/local/bin/ffmpeg5_x86 /usr/local/bin/ffmpeg" > "$BASH_PROFILE"; fi

# ERROR: cp: can't stat '/usr/local/bin/ffmpeg5_arm64': No such file or directory
# RUN sh /root/.ashrc # <-- THIS DIDN'T WORK

# append the FFmpeg installation to PATH
ENV PATH="/usr/local/bin/:${PATH}"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment