85453da49f
regression / regression-shards (style-16-prod style-9-prod style-17-prod iframe-render-compat variables-prod mp4-h265-sdr, shard-4) (push) Has been cancelled
regression / regression-shards (style-4-prod style-11-prod style-2-prod animejs-adapter typegpu-adapter parallel-capture-regression, shard-5) (push) Has been cancelled
regression / regression-shards (style-7-prod style-8-prod style-10-prod css-spinner-render-compat webm-transparency mp4-h264-sdr webm-vp9, shard-3) (push) Has been cancelled
regression / regression-shards (sub-composition-video style-18-prod raf-ball-render-compat font-variant-numeric sub-comp-t0 sub-comp-id-selector, shard-7) (push) Has been cancelled
Windows render verification / Detect changes (push) Has been cancelled
Windows render verification / Preflight (lint + format) (push) Has been cancelled
Windows render verification / Render on windows-latest (push) Has been cancelled
Windows render verification / Tests on windows-latest (push) Has been cancelled
CI / Detect changes (push) Has been cancelled
CI / Build (push) Has been cancelled
CI / Lint (push) Has been cancelled
CI / Fallow audit (push) Has been cancelled
CI / Format (push) Has been cancelled
CI / Typecheck (push) Has been cancelled
CI / Test (push) Has been cancelled
CI / Producer: integration tests (push) Has been cancelled
CI / Producer: unit tests (push) Has been cancelled
CI / File size check (push) Has been cancelled
CI / Test: skills (push) Has been cancelled
CI / Skills: manifest in sync (push) Has been cancelled
CI / CLI: npx shim (macos-latest) (push) Has been cancelled
CI / CLI: npx shim (ubuntu-latest) (push) Has been cancelled
CI / CLI: npx shim (windows-latest) (push) Has been cancelled
CI / SDK: unit + contract + smoke (push) Has been cancelled
CI / Test: runtime contract (push) Has been cancelled
CI / Studio: load smoke (push) Has been cancelled
CI / Smoke: global install (push) Has been cancelled
CI / CLI smoke (required) (push) Has been cancelled
CI / Semantic PR title (push) Has been cancelled
Player perf / Detect changes (push) Has been cancelled
Player perf / Preflight (lint + format) (push) Has been cancelled
Player perf / player-perf (push) Has been cancelled
Player perf / Perf: drift (push) Has been cancelled
Player perf / Perf: fps (push) Has been cancelled
Player perf / Perf: parity (push) Has been cancelled
Player perf / Perf: scrub (push) Has been cancelled
Player perf / Perf: load (push) Has been cancelled
preview-regression / Detect changes (push) Has been cancelled
preview-regression / Preflight (lint + format) (push) Has been cancelled
preview-regression / Preview parity (push) Has been cancelled
preview-regression / preview-regression (push) Has been cancelled
regression / regression (push) Has been cancelled
regression / Detect changes (push) Has been cancelled
regression / Preflight (lint + format) (push) Has been cancelled
regression / regression-shards (hdr-regression style-5-prod style-3-prod mov-prores, shard-1) (push) Has been cancelled
regression / regression-shards (overlay-montage-prod style-12-prod chat missing-host-comp-id png-sequence portrait-edge-bleed, shard-6) (push) Has been cancelled
regression / regression-shards (style-13-prod style-6-prod vignelli-stacking gsap-letters-render-compat audio-mux-parity, shard-8) (push) Has been cancelled
regression / regression-shards (style-15-prod hdr-hlg-regression style-1-prod many-cuts vfr-screen-recording render-symlinked-assets, shard-2) (push) Has been cancelled
CodeQL / Analyze (actions) (push) Has been cancelled
CodeQL / Analyze (javascript-typescript) (push) Has been cancelled
CodeQL / Analyze (python) (push) Has been cancelled
Docs / Validate docs (push) Has been cancelled
Sync skills to ClawHub / Publish changed skills (push) Has been cancelled
108 lines
3.6 KiB
Bash
Executable File
108 lines
3.6 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# frame_strip.sh — produce a side-by-side comparison strip from two videos.
|
|
#
|
|
# Used to debug failing render_diff.sh runs visually: pick a sample timestamp
|
|
# range, extract frames from both videos, lay them out as a grid for review.
|
|
#
|
|
# Usage:
|
|
# frame_strip.sh <baseline.mp4> <translated.mp4> [output-dir] [samples]
|
|
#
|
|
# Defaults: output-dir=./strip-out, samples=8 (evenly spaced across duration).
|
|
# Output:
|
|
# strip.png — single PNG with `samples` rows, each row is
|
|
# (baseline frame | translated frame) at one timestamp
|
|
# timestamps.txt — the timestamps sampled
|
|
|
|
set -euo pipefail
|
|
|
|
if [[ $# -lt 2 || $# -gt 4 ]]; then
|
|
echo "usage: $0 <baseline.mp4> <translated.mp4> [output-dir] [samples]" >&2
|
|
exit 2
|
|
fi
|
|
|
|
BASELINE="$1"
|
|
TRANSLATED="$2"
|
|
OUTDIR="${3:-./strip-out}"
|
|
SAMPLES="${4:-8}"
|
|
|
|
if ! command -v ffmpeg >/dev/null 2>&1 || ! command -v ffprobe >/dev/null 2>&1; then
|
|
echo "error: ffmpeg/ffprobe not on PATH" >&2
|
|
exit 2
|
|
fi
|
|
|
|
mkdir -p "$OUTDIR"
|
|
|
|
# Build the strip in a single ffmpeg invocation. Two inputs (baseline and
|
|
# translated) are sampled at N evenly-spaced timestamps via the `select`
|
|
# filter, then assembled with hstack (per-row pairs) + vstack (rows).
|
|
# Earlier versions spawned 3 ffmpeg calls per timestamp + a final vstack;
|
|
# this is one call regardless of N.
|
|
python3 - "$BASELINE" "$TRANSLATED" "$OUTDIR" "$SAMPLES" <<'PY'
|
|
import json
|
|
import shutil
|
|
import subprocess
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
baseline, translated, outdir, samples = sys.argv[1], sys.argv[2], Path(sys.argv[3]), int(sys.argv[4])
|
|
|
|
# Read fps + duration from the baseline so we can map timestamps to frame
|
|
# indexes for the `select` filter (frame-accurate, doesn't depend on
|
|
# keyframe alignment).
|
|
probe = subprocess.run(
|
|
["ffprobe", "-v", "error", "-select_streams", "v:0",
|
|
"-show_entries", "stream=r_frame_rate,nb_read_frames,duration",
|
|
"-show_entries", "format=duration",
|
|
"-of", "json", "-count_frames", baseline],
|
|
check=True, capture_output=True, text=True,
|
|
)
|
|
data = json.loads(probe.stdout)
|
|
stream = data["streams"][0]
|
|
num, den = stream["r_frame_rate"].split("/")
|
|
fps = float(num) / float(den)
|
|
nb_frames = int(stream.get("nb_read_frames") or 0)
|
|
if nb_frames <= 0:
|
|
duration = float(stream.get("duration") or data["format"]["duration"])
|
|
nb_frames = int(duration * fps)
|
|
|
|
# Even-spaced sample frames in the 5%-95% window (skip fade-in/out noise).
|
|
start = max(0, int(nb_frames * 0.05))
|
|
end = max(start, int(nb_frames * 0.95) - 1)
|
|
if samples == 1:
|
|
frames = [start]
|
|
else:
|
|
step = (end - start) / (samples - 1)
|
|
frames = [int(start + i * step) for i in range(samples)]
|
|
|
|
(outdir / "timestamps.txt").write_text(
|
|
"\n".join(f"{f / fps:.3f}" for f in frames) + "\n"
|
|
)
|
|
|
|
# select='eq(n,F1)+eq(n,F2)+...' picks exactly the listed frames from each
|
|
# input. We then hstack per-frame pairs and vstack the result.
|
|
select_expr = "+".join(f"eq(n,{f})" for f in frames)
|
|
n = len(frames)
|
|
filter_parts = [
|
|
f"[0:v]select='{select_expr}',setpts=N/FRAME_RATE/TB,split={n}"
|
|
+ "".join(f"[b{i}]" for i in range(n)),
|
|
f"[1:v]select='{select_expr}',setpts=N/FRAME_RATE/TB,split={n}"
|
|
+ "".join(f"[t{i}]" for i in range(n)),
|
|
]
|
|
for i in range(n):
|
|
filter_parts.append(f"[b{i}][t{i}]hstack=inputs=2[row{i}]")
|
|
filter_parts.append(
|
|
"".join(f"[row{i}]" for i in range(n)) + f"vstack=inputs={n}[out]"
|
|
)
|
|
filter_graph = ";".join(filter_parts)
|
|
|
|
cmd = [
|
|
"ffmpeg", "-y", "-hide_banner", "-loglevel", "error",
|
|
"-i", baseline, "-i", translated,
|
|
"-filter_complex", filter_graph,
|
|
"-map", "[out]", "-frames:v", "1",
|
|
str(outdir / "strip.png"),
|
|
]
|
|
subprocess.run(cmd, check=True)
|
|
print(f"wrote {outdir / 'strip.png'} ({n} samples)")
|
|
PY
|