Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion crates/compositor/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ fn main() {
if let Some(v) = ff.as_ref() {
let lib_dir = Path::new(v).join("lib");
println!("cargo:rustc-link-search=native={}", lib_dir.display());
for lib in ["avformat", "avcodec", "avutil", "swscale", "swresample"] {
for lib in ["avformat", "avcodec", "avutil", "swscale", "swresample", "avfilter"] {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bloquant — avfilter devient une dépendance de chargement, mais ni le vendoring Windows ni les gardes de packaging ne le connaissent.

Cette ligne met avfilter-11.dll / libavfilter.so.11 dans la table d'import de compositor_view.node. Trois endroits, tous hors diff, n'ont pas suivi — je les signale ici faute de pouvoir commenter des fichiers non modifiés.

scripts/fetch-ffmpeg.mjs:412 — le court-circuit « déjà vendored » est une sonde d'existence (« un av*.dll quelconque est là »), pas une vérification d'ensemble :

.some((e) => e.isFile() && isSharedLib(e.name) && /^(lib)?av/i.test(e.name))

build:win appelle npm run fetch:ffmpeg sans --force. Sur toute machine de dev ou workspace CI tiède où electron/native/bin/win32-x64/ contient déjà les cinq DLL d'avant cette PR et où crates/thirdparty/ffmpeg-n8.1.2-win64-lgpl-shared existe, fetchSharedDlls sort tôt et avfilter-11.dll n'est jamais copié. require() échoue alors avec « The specified module could not be found », tryLoadAddon l'avale, et l'app part avec une preview blanche et un compositeur inerte — le symptôme du build Store 1.9.0 que build-windows-compositor-addon.mjs documente. C'est la première fois que l'ensemble requis grandit depuis l'écriture de ce garde, donc le cas n'a jamais été exercé.

scripts/before-pack.cjs — les trois listes par librairie ignorent avfilter : :134 (Linux), :237 (Windows), :79 (macOS, /^libav(codec|format|util)\.\d+\.dylib$/ avec atLeast: 3). Le commentaire de la liste Linux explique qu'elle est écrite une-entrée-par-famille précisément pour qu'une librairie manquante ne se cache pas derrière un total — une régression déjà livrée une fois. Un build propre embarque bien la librairie (le FFMPEG_SONAMES de cette PR côté Linux, otool -L côté macOS) : c'est le garde qui a régressé. Un payload issu d'un build natif périmé ou partiel passe donc beforePack et installe un addon qui meurt dans ld.so / dyld à require() — pas une dégradation vers WSOLA, mais preview et export morts.

À corriger dans la foulée : le miroir doc technical-documentation/engineering/build-and-packaging.md:207 a besoin de la même entrée, et l'en-tête de scripts/build-linux-compositor-addon.mjs:19 dit encore « the five ffmpeg sonames » pour une liste qui en compte six.


Generated by Claude Code

println!("cargo:rustc-link-lib=dylib={}", lib);
}
}
Expand Down
351 changes: 351 additions & 0 deletions crates/compositor/src/audio.rs

Large diffs are not rendered by default.

3 changes: 3 additions & 0 deletions crates/compositor/wrapper_linux.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,6 @@
#include <libavutil/pixdesc.h>
#include <libswresample/swresample.h>
#include <libswscale/swscale.h>
#include <libavfilter/avfilter.h>
#include <libavfilter/buffersrc.h>
#include <libavfilter/buffersink.h>
5 changes: 4 additions & 1 deletion crates/compositor/wrapper_macos.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,7 @@
/* Software decode path : swscale était déjà LIÉ (build.rs) sans être bindé.
Conservé identique côté macOS pour que la symétrie avec cpu_frames_windows.rs
soit claire ; le code effectif vit dans mac_frames.rs. */
#include <libswscale/swscale.h>
#include <libswscale/swscale.h>
#include <libavfilter/avfilter.h>
#include <libavfilter/buffersrc.h>
#include <libavfilter/buffersink.h>
3 changes: 3 additions & 0 deletions crates/compositor/wrapper_windows.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,6 @@
elle couvre les formats exotiques (10 bits, 4:2:2) qu'un interleave écrit à la
main casserait silencieusement. */
#include <libswscale/swscale.h>
#include <libavfilter/avfilter.h>
#include <libavfilter/buffersrc.h>
#include <libavfilter/buffersink.h>
17 changes: 10 additions & 7 deletions scripts/before-pack.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -75,13 +75,16 @@ const MAC_REQUIRED = [
breaks: "the preview and every export render nothing",
fix: FIX_MAC,
},
{
match: (name) => /^libav(codec|format|util)\.\d+\.dylib$/.test(name),
what: "the LGPL ffmpeg dylibs the compositor links",
// One requirement per library, not `atLeast: N` over a combined regex — the
// same trap LINUX_REQUIRED documents above. Several versioned copies of one
// library would satisfy a combined count while another was missing entirely,
// and the addon would still fail to load.
...["avcodec", "avformat", "avutil", "swresample", "swscale", "avfilter"].map((library) => ({
match: (name) => new RegExp(`^lib${library}\\.\\d+\\.dylib$`).test(name),
what: `the LGPL lib${library} dylib the compositor links`,
breaks: "the compositor addon cannot be loaded at all (dyld error at require())",
fix: FIX_MAC,
atLeast: 3,
},
})),
{
match: (name) => name === "whisper-stt-server",
what: "the whisper.cpp STT helper",
Expand Down Expand Up @@ -131,7 +134,7 @@ const LINUX_REQUIRED = [
// pendant qu'une autre manquait. Le paquet passait alors la garde et le
// compositeur ne chargeait pas : exactement le mode de panne que cette garde
// existe pour attraper.
...["avcodec", "avformat", "avutil", "swresample", "swscale"].map((library) => ({
...["avcodec", "avformat", "avutil", "swresample", "swscale", "avfilter"].map((library) => ({
match: (name) => new RegExp(`^lib${library}\\.so\\.\\d+$`).test(name),
what: `the symbol-renamed lib${library} shared object the compositor links`,
breaks: "the compositor addon cannot be loaded at all (ld.so error at require())",
Expand Down Expand Up @@ -234,7 +237,7 @@ const WIN_REQUIRED = [
// (avcodec-60/61/62.dll left by an earlier fetch) would satisfy a combined count
// while another library was missing entirely, and the addon would still fail to
// load.
...["avcodec", "avformat", "avutil"].map((library) => ({
...["avcodec", "avformat", "avutil", "swresample", "swscale", "avfilter"].map((library) => ({
match: (name) => new RegExp(`^${library}-\\d+\\.dll$`).test(name),
what: `the ${library} DLL the compositor links`,
breaks: "the addon cannot be loaded at all under MSIX, which ignores PATH",
Expand Down
3 changes: 2 additions & 1 deletion scripts/build-linux-compositor-addon.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
// (ensureFfmpegSharedDllsOnPath), but glibc reads LD_LIBRARY_PATH once at
// process start, so the equivalent trick cannot work after Electron is
// already running. Instead the addon is linked with `-rpath,$ORIGIN` and
// the five ffmpeg sonames are copied next to it, which makes the .node
// the six ffmpeg sonames are copied next to it, which makes the .node
// self-contained wherever it is installed — no env var, no PATH surgery.

import { spawnSync } from "node:child_process";
Expand All @@ -36,6 +36,7 @@ const FFMPEG_SONAMES = [
"libavutil.so.60",
"libswscale.so.9",
"libswresample.so.6",
"libavfilter.so.11",
];

const run = (command, args, options = {}) =>
Expand Down
30 changes: 25 additions & 5 deletions scripts/fetch-ffmpeg.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -407,13 +407,33 @@ async function fetchSharedDlls(tag, binDir) {
return;
}

// probe for any previously vendored DLL by name; re-download is driven by
// --force same as the static exe, checked once we know what we'd extract.
const alreadyVendored =
process.platform === "win32" &&
// Completeness probe, not a mere existence probe. The compositor addon now
// links six shared ffmpeg DLLs — see crates/compositor/build.rs
// (avcodec, avformat, avutil, swresample, swscale, avfilter). A warm dev/CI
// tree that already holds the five pre-avfilter DLLs would satisfy an "any
// av*.dll is present" check and let `avfilter-11.dll` go un-vendored, breaking
// require() at runtime (OpenScreen#371 review, EtienneLescot). Require all
// six explicitly so a missing one forces a re-vendor.
const REQUIRED_SHARED_DLLS = [
"avcodec",
"avformat",
"avutil",
"swresample",
"swscale",
"avfilter",
];
fs.mkdirSync(binDir, { recursive: true });
const vendoredFiles = new Set(
fs
.readdirSync(binDir, { withFileTypes: true })
.some((e) => e.isFile() && isSharedLib(e.name) && /^(lib)?av/i.test(e.name));
.filter((e) => e.isFile())
.map((e) => e.name),
);
const alreadyVendored =
process.platform === "win32" &&
REQUIRED_SHARED_DLLS.every((lib) =>
[...vendoredFiles].some((f) => new RegExp(`^${lib}-\\d+\\.dll$`).test(f)),
);
Comment thread
coderabbitai[bot] marked this conversation as resolved.
// The build-time SDK comes out of this same archive, so a tree that has the
// DLLs but not the SDK must still re-download — otherwise we skip here and
// the compositor build fails afterwards on the missing FFMPEG_DIR.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,7 @@ The hook now reads `electron/native/bin/darwin-<arch>/` — the directory `mac.e
| Required | Without it |
|---|---|
| `compositor_view.node` | preview and every export render nothing |
| `libavcodec/libavformat/libavutil.*.dylib` | the addon cannot load at all (dyld error at `require()`) |
| `libavcodec/libavformat/libavutil/libavfilter/libswresample/libswscale.*.dylib` | the addon cannot load at all (dyld error at `require()`) |
| `whisper-stt-server` | transcription and captions fail with a developer error shown to end users |
| `libggml*.dylib` | the helper dies in dyld before `main()`; STT times out with no diagnostic |
| `openscreen-screencapturekit-helper` | native screen capture unavailable |
Expand Down