diff --git a/crates/compositor/src/audio.rs b/crates/compositor/src/audio.rs index 3c37326f8..4def01ead 100644 --- a/crates/compositor/src/audio.rs +++ b/crates/compositor/src/audio.rs @@ -35,7 +35,9 @@ extern "C" { fn averr(ret: i32, ctx: &str) -> Result<()> { if ret < 0 { - let mut buf = [0i8; 256]; + // NOT `[0i8; 256]`: `c_char` is signed on x86_64 but UNSIGNED on aarch64, so a + // hardcoded i8 makes av_strerror's `*mut c_char` a type error on arm64. + let mut buf = [0 as ::std::os::raw::c_char; 256]; unsafe { av_strerror(ret, buf.as_mut_ptr(), buf.len()) }; let msg = unsafe { std::ffi::CStr::from_ptr(buf.as_ptr()) }.to_string_lossy(); bail!("{ctx}: {ret} ({msg})"); diff --git a/electron/native/pipewire-capture/src/ffmpeg.rs b/electron/native/pipewire-capture/src/ffmpeg.rs index 1a7103416..daeb967a1 100644 --- a/electron/native/pipewire-capture/src/ffmpeg.rs +++ b/electron/native/pipewire-capture/src/ffmpeg.rs @@ -49,7 +49,9 @@ pub const AVIO_FLAG_WRITE: i32 = 2; /// Formats an ffmpeg return code the way `av_err2str` would, since that too is a /// macro and needs a caller-supplied buffer. pub fn err_to_string(code: i32) -> String { - let mut buffer = [0i8; 256]; + // NOT `[0i8; 256]`: `c_char` is signed on x86_64 but UNSIGNED on aarch64, so a + // hardcoded i8 makes av_strerror's `*mut c_char` a type error on arm64. + let mut buffer = [0 as ::std::os::raw::c_char; 256]; // SAFETY: the buffer is the size we tell av_strerror it is, and ffmpeg // always NUL-terminates within it. let ok = unsafe { av_strerror(code, buffer.as_mut_ptr(), buffer.len()) } == 0; diff --git a/scripts/build-linux-compositor-addon.mjs b/scripts/build-linux-compositor-addon.mjs index aa0f82a55..6485b4a07 100644 --- a/scripts/build-linux-compositor-addon.mjs +++ b/scripts/build-linux-compositor-addon.mjs @@ -79,6 +79,13 @@ function resolveFfmpegDir() { ); } +/** + * Debian/Ubuntu multiarch triplet for the host. Hardcoding the x86_64 one made + * both libclang and gcc's stddef.h invisible on arm64, where the same libraries + * live under /usr/lib/aarch64-linux-gnu. + */ +const MULTIARCH = process.arch === "arm64" ? "aarch64-linux-gnu" : "x86_64-linux-gnu"; + /** * bindgen loads libclang at runtime. crates/.cargo/config.toml hardcodes a * Windows LLVM path, so on Linux we locate it ourselves rather than making @@ -88,7 +95,7 @@ function resolveLibclangDir() { if (process.env.LIBCLANG_PATH) { return process.env.LIBCLANG_PATH; } - const roots = ["/usr/lib/x86_64-linux-gnu", "/usr/lib64", "/usr/lib"]; + const roots = [`/usr/lib/${MULTIARCH}`, "/usr/lib64", "/usr/lib"]; for (const llvmRoot of ["/usr/lib"]) { if (!fs.existsSync(llvmRoot)) continue; for (const entry of fs.readdirSync(llvmRoot)) { @@ -120,7 +127,7 @@ function bindgenClangArgs() { if (process.env.BINDGEN_EXTRA_CLANG_ARGS) { return process.env.BINDGEN_EXTRA_CLANG_ARGS; } - const gccIncludeRoot = "/usr/lib/gcc/x86_64-linux-gnu"; + const gccIncludeRoot = `/usr/lib/gcc/${MULTIARCH}`; if (!fs.existsSync(gccIncludeRoot)) { return ""; } diff --git a/scripts/build-linux-pipewire-helper.mjs b/scripts/build-linux-pipewire-helper.mjs index e5c147827..03d6c333e 100644 --- a/scripts/build-linux-pipewire-helper.mjs +++ b/scripts/build-linux-pipewire-helper.mjs @@ -63,9 +63,33 @@ if (cargoVersion.status !== 0) { process.exit(1); } +/** + * build.rs runs bindgen over the ffmpeg headers, and bindgen needs clang's own + * builtin includes (limits.h, stddef.h). Ubuntu ships libclang.so.1 without the + * matching resource dir, so clang finds neither and the build dies with + * "'limits.h' file not found". Point it at gcc's copies instead — the same + * fallback scripts/build-linux-compositor-addon.mjs already applies. + */ +function bindgenClangArgs() { + if (process.env.BINDGEN_EXTRA_CLANG_ARGS) { + return process.env.BINDGEN_EXTRA_CLANG_ARGS; + } + const multiarch = process.arch === "arm64" ? "aarch64-linux-gnu" : "x86_64-linux-gnu"; + const gccIncludeRoot = `/usr/lib/gcc/${multiarch}`; + if (!fs.existsSync(gccIncludeRoot)) { + return ""; + } + const withStddef = fs + .readdirSync(gccIncludeRoot) + .map((version) => path.join(gccIncludeRoot, version, "include")) + .filter((dir) => fs.existsSync(path.join(dir, "stddef.h"))); + return withStddef.length > 0 ? `-I${withStddef[0]}` : ""; +} + const build = spawnSync("cargo", ["build", "--release", "--manifest-path", manifest], { cwd: crateDir, stdio: "inherit", + env: { ...process.env, BINDGEN_EXTRA_CLANG_ARGS: bindgenClangArgs() }, }); if (build.error) { console.error(`Failed to start cargo: ${build.error.message}`); diff --git a/scripts/fetch-ffmpeg.mjs b/scripts/fetch-ffmpeg.mjs index 9a98bcf1a..4d50e51f5 100644 --- a/scripts/fetch-ffmpeg.mjs +++ b/scripts/fetch-ffmpeg.mjs @@ -111,6 +111,10 @@ const SHARED_PINNED = { asset: "ffmpeg-n8.1.2-32-gcfa62de001-linux64-lgpl-shared-8.1.tar.xz", sha256: "74ef679aa7e4f8cdbd5193da3d99bf220a679f64d35daf078397081b789f150e", }, + "linux-arm64": { + asset: "ffmpeg-n8.1.2-32-gcfa62de001-linuxarm64-lgpl-shared-8.1.tar.xz", + sha256: "d1d632deac102b865d43ef3c1eca0d0f8c0df148b784327d1d381a39159f8285", + }, "win32-x64": { asset: "ffmpeg-n8.1.2-32-gcfa62de001-win64-lgpl-shared-8.1.zip", sha256: "23429f940316ea92e376f6946c0a1f1b9043c930f3bc068228461d65ae24f8b8",