Skip to content
Closed
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
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
77 changes: 28 additions & 49 deletions .claude/skills/build-ffmpeg-with-vmaf/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,59 +1,38 @@
---
name: build-ffmpeg-with-vmaf
description: Clone (or update) ffmpeg, apply our fork's patches under ffmpeg-patches/, configure with --enable-libvmaf against our local libvmaf.so, build, and run smoke tests on both the 'libvmaf' and 'vmaf_pre' filters.
description: Build FFmpeg from the configured released tag with the complete VMAFx patch series and smoke-test its filters in the dev container.
---

<!-- markdownlint-disable MD013 -->

# /build-ffmpeg-with-vmaf

End-to-end verification that our libvmaf (CLI, C API, tiny-AI surface) integrates
correctly with ffmpeg.
Use the dev-MCP container described in `AGENTS.md` for native integration work.
Confirm that its installed libvmaf matches the source being tested and rebuild
it when required by the container freshness rule.

First validate the full patch series with:

```bash
python3 scripts/ci/ffmpeg_patch_stack.py --check
```

## Invocation
Then run the build helper inside the container with the worktree mounted as
its repository root:

```text
/build-ffmpeg-with-vmaf [--ffmpeg-ref=master|n7.0|<sha>] [--ffmpeg-dir=/tmp/ffmpeg]
[--libvmaf-build=core/build] [--jobs=N]
[--run-filter-smoketest]
```bash
bash ffmpeg-patches/test/build-and-run.sh
```

Defaults: `master` at HEAD, `/tmp/ffmpeg` checkout, our latest `core/build`, `$(nproc)`
jobs, smoke-test enabled.

## Steps

1. Ensure `core/build/src/libvmaf.so.3.0.0` + `core/build/src/libvmaf.pc` exist.
If not, call `/build-vmaf --backend=cpu` first.
2. Clone or fetch the ffmpeg repo into `--ffmpeg-dir`. `git clean -fdx` before applying
patches (to keep idempotent).
3. Checkout `--ffmpeg-ref`.
4. Apply every `*.patch` in `ffmpeg-patches/` in lexicographic order via `git am`.
On conflict: abort the `am`, report the failing patch + hunks, suggest
`/refresh-ffmpeg-patches`.
5. Configure ffmpeg:

```text
PKG_CONFIG_PATH=$repo/core/build/src:$PKG_CONFIG_PATH \
LD_LIBRARY_PATH=$repo/core/build/src:$LD_LIBRARY_PATH \
./configure --prefix=/tmp/ffmpeg-install --enable-libvmaf --enable-gpl \
--enable-version3
```

6. `make -j$jobs && make install`.
7. Smoke test (if `--run-filter-smoketest`):
- `ffmpeg -i testdata/ref_576x324_48f.yuv -i testdata/dis_576x324_48f.yuv \
-lavfi "[0:v][1:v]libvmaf=log_path=/tmp/vmaf.xml" -f null -`
- `ffmpeg ... -lavfi "vmaf_pre=tiny_model=model/tiny/psnr_proxy.onnx"` (only if the
tiny-AI model file is present — skip silently otherwise).
- Verify `/tmp/vmaf.xml` contains a `pooled_metrics` block with `vmaf` mean ≥ 0.
8. Report: ffmpeg ref built, patches applied (N), filter test pass/fail,
resulting ffmpeg binary path.

## Notes

- Uses our local `libvmaf.so`, NOT a system-installed one. `LD_LIBRARY_PATH` scoping
is critical to avoid false positives against an older installed copy.
- If no patches exist yet (`ffmpeg-patches/` is empty or absent), skip step 4 and
configure against vanilla ffmpeg — still a valid integration test.
- Does NOT push or install ffmpeg system-wide.
The helper uses `FFMPEG_REMOTE` and `FFMPEG_TAG` from `build-config.env`, applies
`series.txt` in order, builds against the installed libvmaf, and checks the
`libvmaf` tiny-model option and `vmaf_pre` filter. Set `VMAF_PREFIX` for a
nonstandard libvmaf installation. Missing prerequisites return exit 77 and
are an unavailable result, not a pass.

The default source checkout is disposable. An explicit `FFMPEG_SRC` must be a
new path; existing checkouts are rejected intact. `KEEP_BUILD=1` retains a
successful build, and failures remain available for diagnosis. `FFMPEG_SHA`
can select another stable released tag; development and arbitrary commit refs
are rejected. An empty or failed series is not a valid integration result.

Report the source revision, installed libvmaf version, applied patch count and
actual smoke results. See [FFmpeg patch automation](../../../docs/development/ffmpeg-patch-automation.md).
69 changes: 22 additions & 47 deletions .claude/skills/refresh-ffmpeg-patches/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,56 +1,31 @@
---
name: refresh-ffmpeg-patches
description: Rebase our ffmpeg-patches/ series onto the latest ffmpeg master (or a specified ref), resolve trivial conflicts, regenerate .patch files, and surface unresolved hunks for human attention.
description: Refresh the full FFmpeg patch series against the configured stable release or the latest stable released tag, retaining conflict diagnostics.
---

<!-- markdownlint-disable MD013 -->

# /refresh-ffmpeg-patches

Keeps our ffmpeg integration patches current with upstream ffmpeg. Run this whenever
`build-ffmpeg-with-vmaf` reports a patch apply failure, or periodically (monthly).

## Invocation
The root `build-config.env` owns the maintained FFmpeg remote and release tag.
Use the same implementation as local hooks and CI:

```text
/refresh-ffmpeg-patches [--ffmpeg-ref=master|n7.0|<sha>] [--ffmpeg-dir=/tmp/ffmpeg]
[--branch=vmafx-patches]
```bash
python3 scripts/ci/ffmpeg_patch_stack.py --refresh
python3 scripts/ci/ffmpeg_patch_stack.py --check
```

Defaults: `master` at HEAD, `/tmp/ffmpeg`, `vmafx-patches` work branch.

## Steps

1. Clone or fetch ffmpeg into `--ffmpeg-dir`.
2. Checkout a clean work branch from `--ffmpeg-ref`: `git switch -c <branch>`.
3. Apply each `ffmpeg-patches/*.patch` via `git am --3way`.
4. If any `git am` fails:
a. Capture the failing patch name + conflicting hunks to
`/tmp/refresh-ffmpeg-report.md`.
b. Attempt a 3-way merge with the current upstream file. If the result is trivially
resolvable (new import added, no semantic overlap with our changes), resolve +
`git am --continue`. Otherwise leave for human review and move on.
5. For patches that rebased cleanly, regenerate them as a single series via:
`git format-patch --output-directory=$repo/ffmpeg-patches-new/ <base>..HEAD`.
6. Diff old vs new patch directory:
- Any patch that is byte-identical: skip (no refresh needed).
- Any patch that changed but still applies: overwrite in `ffmpeg-patches/`.
- Any patch that failed: emit a clear diff + context snippet.
7. Summary output:
- `<patches> total, <clean> clean, <refreshed> refreshed, <failed> needs-human`.
- If `<failed>` > 0, list those by filename and point to the report.
8. Stop short of committing — print the exact `git add ffmpeg-patches/ && git commit`
line the operator should run.

## Guardrails

- Does not touch `ffmpeg-patches/` until every patch has been processed.
- Does not commit. The human must inspect the diff and commit explicitly.
- Failure in one patch does not abort — process every patch, report all failures at
end, so you get a full picture of upstream drift in one pass.

## When to use

- After a `/sync-upstream`-style pull from ffmpeg upstream.
- When `build-ffmpeg-with-vmaf` fails at step 4.
- On a monthly cadence via scheduled agent run, so drift is caught early.
When explicitly updating upstream, use `--refresh --latest`. This selects the
highest stable released tag, excluding master, development, RC and snapshot
refs. Daily CI already performs this discovery and retains a proposed diff.
Ordinary commits and PRs replay only the reviewed release.

The helper fetches into disposable storage, applies every `series.txt` entry
in order and writes canonical patches/configuration mirrors only after the
complete replay and rebase succeed. Do not reset or clean existing checkouts.
If it fails, inspect the printed diagnostics directory; resolve the integration
source deliberately and replay the complete series. Later patches depend on
earlier ones, so continuing after a failed patch is not a valid series check.

Review the generated diff and validation before committing within the user's
authorized scope. Patch refresh cannot invent the semantics of a new public API.
See [FFmpeg patch automation](../../../docs/development/ffmpeg-patch-automation.md)
and [ADR-1240](../../../docs/adr/1240-ffmpeg-release-patch-lifecycle.md).
10 changes: 10 additions & 0 deletions .github/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,16 @@ check runs older than its current workflow run when selecting sibling
outcomes; otherwise stale draft-era skipped check runs on the same commit can
mask real queued or failed ready-for-review checks.

### Go validation (ADR-1238)

`go-ci.yml` reports `go vet + go test` as required. It starts on non-draft
PRs including `ready_for_review`, master pushes, and manual dispatches,
then gates heavyweight steps on `go_checks` (`go` plus `c_core`). Preserve
its explicit documentation-only no-work result, CPU/optional-backend
settings, and CI-authority classification. The Rules job runs
`scripts/ci/test_go_workflow_contract.py` before authoring exemptions;
this test executes the aggregator script with failing Go outcomes.

### CI job display names and aggregator parity

All workflow job and matrix display names (`name:`) target $\le 30$ characters
Expand Down
16 changes: 14 additions & 2 deletions .github/ci-impact.json
Original file line number Diff line number Diff line change
Expand Up @@ -83,13 +83,16 @@
"go.sum",
"mkdocs.yml",
"noxfile.py",
"osv-scanner.toml",
"pyproject.toml",
"release-please-config.json",
"renovate.json"
"renovate.json",
"build-config.env"
],
"full_patterns": [
".github/ci-impact.json",
".github/workflows/required-aggregator.yml",
".github/workflows/go-ci.yml",
".github/workflows/lint-and-format.yml",
".github/workflows/security-scans.yml",
".github/workflows/tests-and-quality-gates.yml",
Expand All @@ -104,7 +107,9 @@
"scripts/ci/*",
"scripts/ci/**",
"build-aux/*",
"build-aux/**"
"build-aux/**",
"osv-scanner.toml",
"build-config.env"
],
"selectors": {
"c_core": {
Expand Down Expand Up @@ -246,6 +251,13 @@
"ai"
],
"patterns": []
},
"go_checks": {
"inherits": [
"go",
"c_core"
],
"patterns": []
}
}
}
13 changes: 8 additions & 5 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -243,8 +243,9 @@ jobs:
echo "deb [signed-by=/usr/share/keyrings/oneapi-archive-keyring.gpg] https://apt.repos.intel.com/oneapi all main" \
| sudo tee /etc/apt/sources.list.d/oneAPI.list
sudo apt-get update
sudo -E apt-get -yq install intel-oneapi-compiler-dpcpp-cpp-2025.3
git clone --depth=1 --branch v1.29.0 \
set -a; . ./build-config.env; set +a
sudo -E apt-get -yq install "${ONEAPI_APT_PACKAGE}"
git clone --depth=1 --branch "v${LEVEL_ZERO_VERSION}" \
https://github.com/oneapi-src/level-zero.git /tmp/level-zero
cmake -S /tmp/level-zero -B /tmp/level-zero/build -DCMAKE_INSTALL_PREFIX=/usr
cmake --build /tmp/level-zero/build -j"$(nproc)"
Expand Down Expand Up @@ -297,13 +298,15 @@ jobs:
# comparable to the apt install it replaces.
- name: Install ROCm / HIP toolchain (Linux)
if: matrix.hip && startsWith(matrix.os, 'ubuntu')
env:
ROCM_IMAGE: "rocm/dev-ubuntu-24.04@sha256:a90cf047f615abe70fbef83c64def0a2d549ef37a39c8ea545430aba4981b374"
run: |
sudo apt-get update
sudo -E apt-get -yq install --no-install-recommends jq
# The ROCm image is defined once in build-config.env (ADR-1231),
# never pinned again here. It was previously a digest literal in
# this file and a second one in libvmaf-build-matrix.yml.
set -a; . ./build-config.env; set +a
./scripts/ci/install-rocm-from-image.sh \
--image "$ROCM_IMAGE" --dest /opt/rocm
--image "$ROCM_BUILDER" --dest /opt/rocm
# Surface the ROCm tooling on PATH / pkg-config / ld so meson's
# `dependency('hip-lang')` and the link of `amdhip64` resolve
# without per-step exports.
Expand Down
1 change: 0 additions & 1 deletion .github/workflows/docker-publish-operator-node.yml
Original file line number Diff line number Diff line change
Expand Up @@ -387,7 +387,6 @@ jobs:
build-args: |
VMAFX_VERSION=${{ env.PUBLISH_TAG }}
VMAF_BUILD_JOBS=4
FFMPEG_TAG=n9.0.1

- name: Install cosign
uses: sigstore/cosign-installer@6f9f17788090df1f26f669e9d70d6ae9567deba6 # v4.1.2
Expand Down
7 changes: 6 additions & 1 deletion .github/workflows/docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ concurrency:

jobs:
build:
outputs:
docs: ${{ steps.impact.outputs.docs }}
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
Expand Down Expand Up @@ -47,6 +49,9 @@ jobs:
- name: Install MkDocs Material
if: steps.impact.outputs.docs == 'true'
run: pip install -r docs/requirements.txt
- name: Check generated documentation freshness
if: steps.impact.outputs.docs == 'true'
run: make docs-fragments-check
- name: Build site
if: steps.impact.outputs.docs == 'true'
run: mkdocs build --strict
Expand All @@ -56,7 +61,7 @@ jobs:
path: build-docs/site

deploy:
if: github.event_name == 'push'
if: github.event_name == 'push' && needs.build.outputs.docs == 'true'
needs: build
runs-on: ubuntu-latest
timeout-minutes: 10
Expand Down
17 changes: 11 additions & 6 deletions .github/workflows/ffmpeg-integration.yml
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,8 @@ jobs:
run: brew install -q ninja nasm

- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Load build config
run: scripts/ci/load-build-config.sh >> "$GITHUB_ENV"

- name: Build vmaf
run: |
Expand All @@ -90,7 +92,7 @@ jobs:

- name: Build FFmpeg
run: |
git clone -q --branch n9.0.1 --depth=1 "https://github.com/FFmpeg/FFmpeg" ffmpeg
git clone -q --branch "$FFMPEG_TAG" --depth=1 "$FFMPEG_REMOTE" ffmpeg
cd ffmpeg
./configure --enable-version3 --enable-libvmaf --disable-indevs \
--cc="$CC" --cxx="$CXX" || { less ffbuild/config.log; exit 1; }
Expand All @@ -107,6 +109,9 @@ jobs:
CXX: g++
DEBIAN_FRONTEND: noninteractive
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Load build config
run: scripts/ci/load-build-config.sh >> "$GITHUB_ENV"
- name: Setup python
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
with:
Expand All @@ -119,15 +124,16 @@ jobs:
sudo -E apt-get -yq install ninja-build gcc g++ nasm gpg-agent libvpl-dev

- name: Install Intel oneAPI
# Pinned to oneAPI 2025.3 + Level Zero v1.28.0. See libvmaf-build-matrix.yml.
# oneAPI version comes from ONEAPI_APT_PACKAGE in build-config.env;
# Level Zero from LEVEL_ZERO_VERSION. See libvmaf-build-matrix.yml.
run: |
wget -qO- https://apt.repos.intel.com/intel-gpg-keys/GPG-PUB-KEY-INTEL-SW-PRODUCTS.PUB \
| sudo gpg --dearmor -o /usr/share/keyrings/oneapi-archive-keyring.gpg
echo "deb [signed-by=/usr/share/keyrings/oneapi-archive-keyring.gpg] https://apt.repos.intel.com/oneapi all main" \
| sudo tee /etc/apt/sources.list.d/oneAPI.list
sudo apt-get update
sudo -E apt-get -yq install intel-oneapi-compiler-dpcpp-cpp-2025.3 cmake
git clone --depth=1 --branch v1.28.0 \
sudo -E apt-get -yq install "${ONEAPI_APT_PACKAGE}" cmake
git clone --depth=1 --branch "v${LEVEL_ZERO_VERSION}" \
https://github.com/oneapi-src/level-zero.git /tmp/level-zero
cmake -S /tmp/level-zero -B /tmp/level-zero/build -DCMAKE_INSTALL_PREFIX=/usr
cmake --build /tmp/level-zero/build -j$(nproc)
Expand All @@ -137,7 +143,6 @@ jobs:
echo "/opt/intel/oneapi/compiler/latest/lib" | sudo tee /etc/ld.so.conf.d/oneapi.conf
sudo ldconfig

- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1

- name: Build vmaf with SYCL
env:
Expand All @@ -158,7 +163,7 @@ jobs:

- name: Build FFmpeg with SYCL patch series
run: |
git clone -q --branch n9.0.1 --depth=1 "https://github.com/FFmpeg/FFmpeg" ffmpeg
git clone -q --branch "$FFMPEG_TAG" --depth=1 "$FFMPEG_REMOTE" ffmpeg
cd ffmpeg
# Apply the patch series in series.txt order. Patch 0003 (SYCL)
# depends on fields added by 0001 (tiny-AI), so applying out of
Expand Down
Loading