From 43d43084360ce557c1d8bb7a44279567219a49d9 Mon Sep 17 00:00:00 2001 From: Jonny Burger Date: Mon, 23 Feb 2026 09:39:01 +0100 Subject: [PATCH] Fix x265 SIGSEGV by patching FFmpeg x265 API guard x265 build 210 changed x265_encoder_encode() to accept an array of picture pointers, then build 213 reverted this. FFmpeg n7.1 only checks `#if X265_BUILD >= 210`, so with our x265 build 215 it uses the wrong calling convention, causing a SIGSEGV during h265 encoding. Apply upstream FFmpeg fix (099f88b864) that narrows the guard to `(X265_BUILD >= 210) && (X265_BUILD < 213)`. Also adds x265 encoding test to CI to prevent regressions. Closes https://github.com/remotion-dev/remotion/issues/6630 Co-Authored-By: Claude Opus 4.6 --- Dockerfile | 1 + Dockerfile-aws | 1 + compile-ffmpeg.mjs | 6 ++++++ test-ffmpeg.mjs | 22 ++++++++++++++++++++++ x265-api.patch | 31 +++++++++++++++++++++++++++++++ 5 files changed, 61 insertions(+) create mode 100644 x265-api.patch diff --git a/Dockerfile b/Dockerfile index 68790b4..f7f427c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -13,6 +13,7 @@ COPY sample.mp4 app/sample.mp4 COPY sample-av1.webm app/sample-av1.webm COPY aac.patch app/aac.patch COPY hevc_ps.patch app/hevc_ps.patch +COPY x265-api.patch app/x265-api.patch RUN apk add curl RUN curl https://sh.rustup.rs -sSf | sh -s -- -y diff --git a/Dockerfile-aws b/Dockerfile-aws index 4364962..c7edec3 100644 --- a/Dockerfile-aws +++ b/Dockerfile-aws @@ -13,6 +13,7 @@ COPY sample.mp4 app/sample.mp4 COPY sample-av1.webm app/sample-av1.webm COPY aac.patch app/aac.patch COPY hevc_ps.patch app/hevc_ps.patch +COPY x265-api.patch app/x265-api.patch RUN curl https://sh.rustup.rs -sSf | sh -s -- -y RUN source "$HOME/.cargo/env" diff --git a/compile-ffmpeg.mjs b/compile-ffmpeg.mjs index 8e67daf..bd7de15 100644 --- a/compile-ffmpeg.mjs +++ b/compile-ffmpeg.mjs @@ -159,6 +159,9 @@ if (fs.existsSync("ffmpeg")) { execSync("git apply hevc_ps.patch --directory ffmpeg", { stdio: "inherit", }); + execSync("git apply x265-api.patch --directory ffmpeg", { + stdio: "inherit", + }); } else { execSync("git clone https://github.com/ffmpeg/ffmpeg.git", { stdio: "inherit", @@ -173,6 +176,9 @@ if (fs.existsSync("ffmpeg")) { execSync("git apply hevc_ps.patch --directory ffmpeg", { stdio: "inherit", }); + execSync("git apply x265-api.patch --directory ffmpeg", { + stdio: "inherit", + }); } const extraCFlags = [ diff --git a/test-ffmpeg.mjs b/test-ffmpeg.mjs index 3b3c5bf..507ab53 100644 --- a/test-ffmpeg.mjs +++ b/test-ffmpeg.mjs @@ -101,6 +101,28 @@ const exit5 = spawnSync( ); assert(exit5.status === 0); +const exit_x265 = spawnSync( + ffmpegBinary, + [ + "-i", + "sample.mp4", + "-t", + "1", + "-c:v", + "libx265", + "out-test-x265.mp4", + "-y", + ], + { + env, + stdio: "inherit", + } +); +if (exit_x265.status !== 0) { + console.log("x265 encoding test failed with status", exit_x265.status, "signal", exit_x265.signal); +} +assert(exit_x265.status === 0); + const exit6 = spawnSync( ffmpegBinary, [ diff --git a/x265-api.patch b/x265-api.patch new file mode 100644 index 0000000..9de8484 --- /dev/null +++ b/x265-api.patch @@ -0,0 +1,31 @@ +diff --git a/libavcodec/libx265.c b/libavcodec/libx265.c +index 38f0ab6b15..5d4fc43e92 100644 +--- a/libavcodec/libx265.c ++++ b/libavcodec/libx265.c +@@ -661,7 +661,7 @@ static int libx265_encode_frame(AVCodecContext *avctx, AVPacket *pkt, + { + libx265Context *ctx = avctx->priv_data; + x265_picture x265pic; +-#if X265_BUILD >= 210 ++#if (X265_BUILD >= 210) && (X265_BUILD < 213) + x265_picture x265pic_layers_out[MAX_SCALABLE_LAYERS]; + x265_picture* x265pic_lyrptr_out[MAX_SCALABLE_LAYERS]; + #else +@@ -805,7 +805,7 @@ static int libx265_encode_frame(AVCodecContext *avctx, AVPacket *pkt, + #endif + } + +-#if X265_BUILD >= 210 ++#if (X265_BUILD >= 210) && (X265_BUILD < 213) + for (i = 0; i < MAX_SCALABLE_LAYERS; i++) + x265pic_lyrptr_out[i] = &x265pic_layers_out[i]; + +@@ -844,7 +844,7 @@ static int libx265_encode_frame(AVCodecContext *avctx, AVPacket *pkt, + pkt->flags |= AV_PKT_FLAG_KEY; + } + +-#if X265_BUILD >= 210 ++#if (X265_BUILD >= 210) && (X265_BUILD < 213) + x265pic_out = x265pic_lyrptr_out[0]; + #else + x265pic_out = &x265pic_solo_out;