diff --git a/recipes/atuq/bin/atuq b/recipes/atuq/bin/atuq index ac19b1e6..145766d0 100755 --- a/recipes/atuq/bin/atuq +++ b/recipes/atuq/bin/atuq @@ -19,6 +19,26 @@ # `exec` y no una llamada normal: así el proceso que queda es el motor, `/proc/self/exe` apunta a # `/usr/lib/atuq/atuq` y Gecko resuelve su appdir correctamente. Con un `sh` de por medio quedaría # un proceso zombi de más y el nombre en `ps` sería el del script. -LD_LIBRARY_PATH="/usr/lib/atuq${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}" +# ── ⚠ EL DIRECTORIO VA DOS VECES, Y NO ES UN DEDAZO (2026-09-07) ────────────────────────────── +# Gecko SE COME EL PRIMER ELEMENTO de `LD_LIBRARY_PATH` al lanzar el proceso RDD (el que decodifica +# vídeo). Con el appdir una sola vez —o sea, primero— el RDD arranca SIN él, no encuentra +# `libmozavcodec.so`/`libmozavutil.so` (el ffvpx bundleado, que es donde vive dav1d) y registra +# +# PlatformDecoderModule FFVPX: Link result: NoProvidedLib +# +# El navegador NO falla: se cae al ffmpeg del sistema, que cubre H.264/AAC/VP8/VP9/MP3/FLAC/Opus… +# pero **NO AV1 por software** —el decodificador `av1` de ffmpeg es sólo hwaccel y nuestra receta va +# sin dav1d—, así que un vídeo AV1 se queda en `readyState=1` para siempre. Es la forma de fallo de +# esta casa: media función apagada, cero mensajes de error. +# +# Medido con tres corridas que sólo cambian esta variable (matriz en `scripts/test-atuq-codecs.sh`): +# +# LD=/usr/lib/atuq:/usr/lib:/lib → RDD NoProvidedLib AV1 ✗ +# LD=/usr/lib/atuq:/usr/lib/atuq:/usr/lib → RDD Success AV1 ✓ +# LD=/relleno:/usr/lib/atuq:/usr/lib → RDD Success AV1 ✓ (el relleno se lo come) +# +# Repetirlo es la mitigación correcta mientras no haya `patchelf` en el corpus para grabar +# `RUNPATH=$ORIGIN` en los ELF, que es lo que hace Alpine y lo que borraría esta variable entera. +LD_LIBRARY_PATH="/usr/lib/atuq:/usr/lib/atuq${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}" export LD_LIBRARY_PATH exec /usr/lib/atuq/atuq "$@" diff --git a/scripts/fixtures/codecs/av1.mp4 b/scripts/fixtures/codecs/av1.mp4 new file mode 100644 index 00000000..3c3bc8a4 Binary files /dev/null and b/scripts/fixtures/codecs/av1.mp4 differ diff --git a/scripts/fixtures/codecs/flac.flac b/scripts/fixtures/codecs/flac.flac new file mode 100644 index 00000000..dd7b607b Binary files /dev/null and b/scripts/fixtures/codecs/flac.flac differ diff --git a/scripts/fixtures/codecs/h264-aac.mp4 b/scripts/fixtures/codecs/h264-aac.mp4 new file mode 100644 index 00000000..f77a7884 Binary files /dev/null and b/scripts/fixtures/codecs/h264-aac.mp4 differ diff --git a/scripts/fixtures/codecs/mp3.mp3 b/scripts/fixtures/codecs/mp3.mp3 new file mode 100644 index 00000000..e26a91c4 Binary files /dev/null and b/scripts/fixtures/codecs/mp3.mp3 differ diff --git a/scripts/fixtures/codecs/vp9-opus.webm b/scripts/fixtures/codecs/vp9-opus.webm new file mode 100644 index 00000000..07345145 Binary files /dev/null and b/scripts/fixtures/codecs/vp9-opus.webm differ diff --git a/scripts/test-atuq-codecs.sh b/scripts/test-atuq-codecs.sh new file mode 100755 index 00000000..c03f410f --- /dev/null +++ b/scripts/test-atuq-codecs.sh @@ -0,0 +1,143 @@ +#!/bin/sh +# test-atuq-codecs.sh — ¿qué formatos de vídeo y audio REPRODUCE atuq de verdad? +# +# scripts/test-atuq-codecs.sh # sale 0 si los seis casos reproducen +# scripts/test-atuq-codecs.sh --negative-control # rompe el enlace de ffvpx a propósito +# +# ── POR QUÉ EXISTE, Y POR QUÉ NO LO CUBRE NINGÚN OTRO GUARDIÁN ──────────────────────────────── +# `vigia-sonames.py` mira la clausura de un perfil; `test-atuq-rootfs.py` mira los NEEDED del +# rootfs hidratado y, con `--dlopen`, adivina el tercer escalón por cadenas. Ninguno de los tres +# puede responder la única pregunta que le importa a quien usa el navegador: **¿se ve el vídeo?** +# Un códec puede fallar con TODOS los ficheros en su sitio —pasó, y es lo que originó este script: +# el proceso RDD perdía `/usr/lib/atuq` del `LD_LIBRARY_PATH`, no cargaba el ffvpx bundleado, se +# caía al ffmpeg del sistema, y AV1 —que ese ffmpeg sólo decodifica por hardware— se quedaba en +# `readyState=1` para siempre. Sin un error. Sin un NEEDED faltante. Sin una cadena ausente. +# +# ── CÓMO SE MIDE: EL PROPIO MOTOR DICE EL VEREDICTO ─────────────────────────────────────────── +# La página abre cada muestra, la deja correr 6 s y mira `currentTime`. Un decodificador que se +# crea pero no entrega cuadros deja el tiempo en 0.00, que es exactamente el fallo que buscamos: +# «se creó» no es «decodifica». El resultado sale por `dump()` —con +# `browser.dom.window.dump.enabled`— al stdout del proceso, así que **no hace falta ni red ni +# servidor**: se lee del log. Headless a propósito: esto tiene que correr en el worker y en cron. +# +# ── LAS MUESTRAS VAN VERSIONADAS, NO SE GENERAN ─────────────────────────────────────────────── +# `scripts/fixtures/codecs/` son cinco ficheros de ~200 K en total, generados una vez con ffmpeg. +# Generarlos en cada corrida ataría el guardián al ffmpeg del HOST —otra versión, otros bytes, otro +# resultado— y lo volvería inejecutable donde no haya uno. Un fixture congelado es el control. +# +# ── EL CONTROL NEGATIVO NO ES ADORNO ────────────────────────────────────────────────────────── +# Un guardián que nunca falló y uno que no puede fallar se ven idénticos desde afuera. Con +# `--negative-control` se saltea el lanzador y se corre el binario con el appdir UNA sola vez en +# `LD_LIBRARY_PATH`, que es la forma exacta del bug: se EXIGE que AV1 falle. Si en ese modo AV1 +# pasa, el que está roto es este script. +set -eu +ROOT="$(cd "$(dirname "$0")/.." && pwd)"; cd "$ROOT" +RFS="${RFS:-/mnt/cosecha/escritorios/atuq-rootfs}" +ALPINE="$ROOT/.dev-fs/alpine" +CONTROL=0 +[ "${1:-}" = "--negative-control" ] && CONTROL=1 + +command -v bwrap >/dev/null || { echo "✗ falta bwrap" >&2; exit 1; } + +# El rootfs lo arma `atuq-nested.sh`, que es donde vive LA LISTA DE RAÍCES. Acá no se copia: se +# llama. Dos listas es cómo se desincronizan. +ROOTFS_ONLY=1 RFS="$RFS" "$ROOT/scripts/atuq-nested.sh" + +TMP="$(mktemp -d)" +trap 'rm -rf "$TMP"' EXIT +cp scripts/fixtures/codecs/* "$TMP/" +mkdir -p "$TMP/perfil" +cat > "$TMP/perfil/user.js" <<'JS' +// `dump()` es el canal de salida del veredicto: sin esto la página no puede hablarle al log. +user_pref("browser.dom.window.dump.enabled", true); +user_pref("devtools.console.stdout.content", true); +// Sin esto un `