From 41bb1fa61f0458550285aa948b44f47434c04f1f Mon Sep 17 00:00:00 2001 From: Joey Parrish Date: Wed, 9 Oct 2024 10:16:33 -0700 Subject: [PATCH] feat: Separate Linux static from hardware encoding Hardware acceleration for Linux is too wrapped up in dynamic libraries and drivers, so from now on, builds for Linux that support hardware encoding will be dynamic and will only run on Ubuntu. For use in every other Linux distribution, fully static builds will be done with musl in Alpine Linux. --- .github/workflows/build.yaml | 12 ++++++-- build-matrix.json | 38 +++++++++++++++++++++++-- build-scripts/00-packages.sh | 3 +- build-scripts/90-ffmpeg.sh | 39 ++++++++++++++++---------- build-scripts/99-check-static.sh | 14 +++++++--- ffmpeg-nvenc-jammy.patch | 48 ++++++++++++++++++++++++++++++++ versions.txt | 2 +- 7 files changed, 130 insertions(+), 26 deletions(-) create mode 100644 ffmpeg-nvenc-jammy.patch diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 7785b1c..7319174 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -98,7 +98,7 @@ jobs: matrix: include: ${{ fromJSON(needs.matrix_config.outputs.MATRIX) }} - name: Build ${{ matrix.os_name }} ${{ matrix.target_arch }} + name: Build ${{ matrix.os_name }} ${{ matrix.target_arch }} ${{ matrix.container }} runs-on: ${{ matrix.os }} container: image: ${{ matrix.container }} @@ -125,7 +125,7 @@ jobs: # actions. # See https://github.com/actions/runner/issues/801#issuecomment-2394425757 - name: Patch native Alpine NodeJS into Runner environment - if: runner.os == 'Linux' + if: startsWith(matrix.container, 'alpine') run: | apk add nodejs sed -i "s:^ID=alpine:ID=NotpineForGHA:" /etc/os-release @@ -140,10 +140,16 @@ jobs: shell: sh # No bash in Alpine by default - name: Install Alpine Linux deps - if: runner.os == 'Linux' + if: startsWith(matrix.container, 'alpine') run: apk add bash npm sudo shell: sh # No bash in Alpine until after this command + - name: Install Ubuntu Linux deps + if: startsWith(matrix.container, 'ubuntu') + # Sudo is needed by the first build script, but isn't in the default + # container image for Ubuntu. + run: apt -y update && apt -y upgrade && apt -y install sudo + - uses: actions/checkout@v4 with: path: repo-src diff --git a/build-matrix.json b/build-matrix.json index 2e4ba88..4375423 100644 --- a/build-matrix.json +++ b/build-matrix.json @@ -2,12 +2,29 @@ "comment1": "runners hosted by GitHub, always enabled", "hosted": [ { + "comment": "Alpine container for static Linux binaries", "os": "ubuntu-latest", "container": "alpine:3.20", "os_name": "linux", "target_arch": "x64", "exe_ext": "" }, + { + "comment": "Ubuntu 24.04 with hardware acceleration", + "os": "ubuntu-latest", + "container": "ubuntu:24.04", + "os_name": "linux", + "target_arch": "x64", + "exe_ext": "-ubuntu-24.04" + }, + { + "comment": "Ubuntu 22.04 with hardware acceleration", + "os": "ubuntu-latest", + "container": "ubuntu:22.04", + "os_name": "linux", + "target_arch": "x64", + "exe_ext": "-ubuntu-22.04" + }, { "comment": "Explicit macOS version 13 is required for explicit x64 CPU.", "os": "macos-13", @@ -16,7 +33,7 @@ "exe_ext": "" }, { - "comment": "Latest macOS version 13 is arm64 CPU.", + "comment": "Latest macOS version is arm64 CPU.", "os": "macos-latest", "os_name": "osx", "target_arch": "arm64", @@ -33,11 +50,28 @@ "comment2": "runners hosted by the owner, enabled by the ENABLE_SELF_HOSTED variable being set on the repo", "selfHosted": [ { + "comment": "Alpine container for static Linux binaries", "os": "self-hosted-linux-arm64", - "container": "arm64v8/alpine:3.20", + "container": "alpine:3.20", "os_name": "linux", "target_arch": "arm64", "exe_ext": "" + }, + { + "comment": "Ubuntu 24.04 with hardware acceleration", + "os": "self-hosted-linux-arm64", + "container": "ubuntu:24.04", + "os_name": "linux", + "target_arch": "arm64", + "exe_ext": "-ubuntu-24.04" + }, + { + "comment": "Ubuntu 22.04 with hardware acceleration", + "os": "self-hosted-linux-arm64", + "container": "ubuntu:22.04", + "os_name": "linux", + "target_arch": "arm64", + "exe_ext": "-ubuntu-22.04" } ] } diff --git a/build-scripts/00-packages.sh b/build-scripts/00-packages.sh index bbe5ef2..dbf6e4a 100755 --- a/build-scripts/00-packages.sh +++ b/build-scripts/00-packages.sh @@ -46,12 +46,13 @@ if [[ "$RUNNER_OS" == "Linux" ]]; then sudo apt -y update sudo apt -y upgrade sudo apt -y install \ + clang \ cmake \ curl \ g++ \ git \ libffmpeg-nvenc-dev \ - libvdpau-dev \ + libva-dev \ make \ nasm \ npm \ diff --git a/build-scripts/90-ffmpeg.sh b/build-scripts/90-ffmpeg.sh index 056c2d8..c8aaf71 100755 --- a/build-scripts/90-ffmpeg.sh +++ b/build-scripts/90-ffmpeg.sh @@ -23,11 +23,27 @@ cd ffmpeg # Set some OS-specific environment variables and flags. if [[ "$RUNNER_OS" == "Linux" ]]; then - export CFLAGS="-static" - export LDFLAGS="-static" + if ../repo-src/is-alpine.sh; then + # Truly static builds are only possible in musl-based Alpine Linux. + # Go for a completely static binary, but this prevents the use of hardware + # acceleration. + export CFLAGS="-static" + export LDFLAGS="-static" + else + # We can't build a truly static binary, so we might as well enable hardware + # acceleration, which uses dynamic libraries and will depend heavily on the + # OS distribution. + PLATFORM_CONFIGURE_FLAGS="--enable-vaapi --enable-nvenc" + # TODO: Is AMF an option for us in this context? - # Enable platform-specific hardware acceleration. - PLATFORM_CONFIGURE_FLAGS="--enable-vdpau" + # This version of ffmpeg will accept NVEnc 11.5.1.3+, but not Ubuntu + # 22.04's packaged version, 11.5.1.1. This patch makes it flexible enough + # to build with the older NVEnc version in Ubuntu Jammy. + # For code archaeologists, the commits that set the minimum beyond 11.5.1.1 + # were https://github.com/ffmpeg/ffmpeg/commit/5c288a44 (released in n6.0) + # and https://github.com/ffmpeg/ffmpeg/commit/05f8b2ca (released in n6.1). + patch -p1 -i ../repo-src/ffmpeg-nvenc-jammy.patch + fi elif [[ "$RUNNER_OS" == "macOS" ]]; then export CFLAGS="-static" # You can't do a _truly_ static build on macOS except the kernel. @@ -36,11 +52,10 @@ elif [[ "$RUNNER_OS" == "macOS" ]]; then # Enable platform-specific hardware acceleration. PLATFORM_CONFIGURE_FLAGS="--enable-videotoolbox" - # Disable x86 ASM on macOS. It fails to build with an error about - # how macho64 format can't contain 32-bit assembly. I'm not sure - # how else to resolve this, and from my searches, it appears that - # others are not having this problem with ffmpeg. This is still a problem - # with n6.0. + # Disable x86 ASM on macOS. It fails to build with an error about "32-bit + # absolute addressing is not supported in 64-bit mode". I'm not sure how + # else to resolve this, and from my searches, it appears that others are not + # having this problem with ffmpeg. This is still a problem with n7.1 PLATFORM_CONFIGURE_FLAGS="$PLATFORM_CONFIGURE_FLAGS --disable-x86asm --disable-inline-asm" elif [[ "$RUNNER_OS" == "Windows" ]]; then # /usr/local/incude and /usr/local/lib are not in mingw's include @@ -55,12 +70,6 @@ elif [[ "$RUNNER_OS" == "Windows" ]]; then PLATFORM_CONFIGURE_FLAGS="--target-os=mingw64" fi -# Install a patch from https://github.com/FFmpeg/FFmpeg/commit/effadce6 to -# resolve the binutils error "operand type mismatch for shr" on Windows, -# described in https://github.com/msys2/MINGW-packages/issues/17946 -wget https://github.com/FFmpeg/FFmpeg/commit/effadce6.patch -patch -p1 -i effadce6.patch - if ! ./configure \ --pkg-config-flags="--static" \ --disable-ffplay \ diff --git a/build-scripts/99-check-static.sh b/build-scripts/99-check-static.sh index 8bfae93..c27e53a 100755 --- a/build-scripts/99-check-static.sh +++ b/build-scripts/99-check-static.sh @@ -20,10 +20,16 @@ set -x cd ffmpeg if [[ "$RUNNER_OS" == "Linux" ]]; then - # If ldd succeeds, then these are dynamic executables, so we fail - # this step if ldd succeeds. The output of ldd will still be logged. - ldd ffmpeg && exit 1 - ldd ffprobe && exit 1 + # We only check for static binaries on Alpine Linux. In other distributions, + # these are not possible due to the use of glibc. We allow glibc builds here + # because while tied to the distro, they at least give us the chance for + # hardware encoding. + if ../repo-src/is-alpine.sh; then + # If ldd succeeds, then these are dynamic executables, so we fail + # this step if ldd succeeds. The output of ldd will still be logged. + ldd ffmpeg && exit 1 + ldd ffprobe && exit 1 + fi elif [[ "$RUNNER_OS" == "Windows" ]]; then # These will still be dynamic executables. # Capture the full list of DLL dependencies. diff --git a/ffmpeg-nvenc-jammy.patch b/ffmpeg-nvenc-jammy.patch new file mode 100644 index 0000000..af29d9f --- /dev/null +++ b/ffmpeg-nvenc-jammy.patch @@ -0,0 +1,48 @@ +FFmpeg n7.1 will accept NVEnc 11.5.1.3+, but not Ubuntu 22.04's packaged +version, 11.5.1.1. This patch makes it flexible enough to build with the older +NVEnc version in Ubuntu Jammy. For code archaeologists, the commits that set +the minimum beyond 11.5.1.1 were +https://github.com/ffmpeg/ffmpeg/commit/5c288a44 (released in n6.0) and +https://github.com/ffmpeg/ffmpeg/commit/05f8b2ca (released in n6.1). + + +diff --git a/configure b/configure +index d77a55b653..c28dcffbb0 100755 +--- a/configure ++++ b/configure +@@ -6761,7 +6761,7 @@ if ! disabled ffnvcodec; then + ffnv_hdr_list="ffnvcodec/nvEncodeAPI.h ffnvcodec/dynlink_cuda.h ffnvcodec/dynlink_cuviddec.h ffnvcodec/dynlink_nvcuvid.h" + check_pkg_config ffnvcodec "ffnvcodec >= 12.1.14.0" "$ffnv_hdr_list" "" || \ + check_pkg_config ffnvcodec "ffnvcodec >= 12.0.16.1 ffnvcodec < 12.1" "$ffnv_hdr_list" "" || \ +- check_pkg_config ffnvcodec "ffnvcodec >= 11.1.5.3 ffnvcodec < 12.0" "$ffnv_hdr_list" "" || \ ++ check_pkg_config ffnvcodec "ffnvcodec >= 11.1.5.1 ffnvcodec < 12.0" "$ffnv_hdr_list" "" || \ + check_pkg_config ffnvcodec "ffnvcodec >= 11.0.10.3 ffnvcodec < 11.1" "$ffnv_hdr_list" "" || \ + check_pkg_config ffnvcodec "ffnvcodec >= 8.1.24.15 ffnvcodec < 8.2" "$ffnv_hdr_list" "" + fi +@@ -7388,7 +7388,7 @@ int main(void) { return 0; } + EOF + + if enabled nvenc; then +- check_type "ffnvcodec/nvEncodeAPI.h" "NV_ENC_PIC_PARAMS_AV1" ++ check_type "ffnvcodec/nvEncodeAPI.h" "NV_ENC_PIC_PARAMS_AV1" || add_cflags -DJAMMY + fi + + if enabled_any nvdec cuvid; then +diff --git a/libavutil/hwcontext_cuda.c b/libavutil/hwcontext_cuda.c +index 3de3847399..0815360a46 100644 +--- a/libavutil/hwcontext_cuda.c ++++ b/libavutil/hwcontext_cuda.c +@@ -363,11 +363,13 @@ static int cuda_context_init(AVHWDeviceContext *device_ctx, int flags) { + hwctx->internal->cuda_device)); + if (ret < 0) + return ret; ++#ifndef JAMMY + } else if (flags & AV_CUDA_USE_CURRENT_CONTEXT) { + ret = CHECK_CU(cu->cuCtxGetCurrent(&hwctx->cuda_ctx)); + if (ret < 0) + return ret; + av_log(device_ctx, AV_LOG_INFO, "Using current CUDA context.\n"); ++#endif + } else { + ret = CHECK_CU(cu->cuCtxCreate(&hwctx->cuda_ctx, desired_flags, + hwctx->internal->cuda_device)); diff --git a/versions.txt b/versions.txt index 1f4a823..37bd987 100644 --- a/versions.txt +++ b/versions.txt @@ -1,4 +1,4 @@ -ffmpeg: n6.0 +ffmpeg: n7.1 libvpx: v1.13.0 svt-av1: v1.7.0 x264: a8b68ebf