FFmpeg 8 compatibility (app-side) for transcode pipelines #9

Closed
opened 2026-06-26 22:23:39 +02:00 by timothy · 3 comments
Owner

Why this is separate from the base-image build

ersatztv-ffmpeg#4 covers building/publishing the FFmpeg 8.x base image. This issue covers the ErsatzTV application changes needed so our transcode command generation actually works against FFmpeg 8. They're distinct: a green base-image build does not guarantee ErsatzTV emits valid FFmpeg-8 command lines.

Assessment (from a codebase pass)

Scope is moderate — no architecture change, but real edits to filter chains, encoder options, and tests. Runtime capability probing (HardwareCapabilitiesFactory.GetFFmpegCapabilities()) means missing hardware filters fall back to software rather than crashing, which limits blast radius. There's already version-gated logic (e.g. NvidiaHardwareCapabilities.cs forces software decode of 10-bit H.264 below ffmpeg 8), so the version-aware pattern exists to build on.

Likely areas to touch

  • VAAPI filters — ErsatzTV.FFmpeg/Filter/Vaapi/* (esp. TonemapVaapiFilter.cs's hwupload=derive_device=...,hwmap=...,tonemap_opencl=... chain; scale_vaapi)
  • QSV filters / init — ErsatzTV.FFmpeg/Filter/Qsv/*, -init_hw_device qsv=hw
  • CUDA/Vulkan init — ErsatzTV.FFmpeg/GlobalOption/HardwareAcceleration/* (-init_hw_device cuda=..., vulkan=...)
  • Encoder options — Encoder/Vaapi/*, Encoder/Qsv/* (-rc_mode, -sei, etc. — check for deprecations)
  • Capability parsers — Capabilities/Vaapi/VaapiCapabilityParser.cs if vainfo output shifts
  • Tests — ErsatzTV.FFmpeg.Tests/PipelineBuilderBaseTests.cs and Filter/* assert exact command strings; expect to update these

How discovery happens

Most breakage is only knowable by building a test image on FFmpeg 8 and running the suite:

  • Build a test ErsatzTV image on the ersatztv-ffmpeg:8.x base (depends on ersatztv-ffmpeg#4)
  • Run dotnet test ErsatzTV.FFmpeg.Tests and fix command-string assertions
  • Review FFmpeg 8.0 release notes for deprecations affecting scale_vaapi, hwmap, hwupload/hwdownload, tonemap_opencl, derive_device, zscale, setsar, and encoder options
  • Manually verify transcodes: H.264 / HEVC / 10-bit inputs across software + each hwaccel available on jazz (Intel/VAAPI/QSV)
  • Update filter chains / encoder options as needed

Depends on

ersatztv-ffmpeg#4 (8.x base image). Do after the CI/CD pipeline (#4/#3) lands. Backlog.

## Why this is separate from the base-image build `ersatztv-ffmpeg#4` covers building/publishing the FFmpeg **8.x base image**. This issue covers the **ErsatzTV application changes** needed so our transcode command generation actually works against FFmpeg 8. They're distinct: a green base-image build does not guarantee ErsatzTV emits valid FFmpeg-8 command lines. ## Assessment (from a codebase pass) Scope is **moderate** — no architecture change, but real edits to filter chains, encoder options, and tests. Runtime capability probing (`HardwareCapabilitiesFactory.GetFFmpegCapabilities()`) means missing hardware filters fall back to software rather than crashing, which limits blast radius. There's already version-gated logic (e.g. `NvidiaHardwareCapabilities.cs` forces software decode of 10-bit H.264 below ffmpeg 8), so the version-aware pattern exists to build on. ## Likely areas to touch - **VAAPI filters** — `ErsatzTV.FFmpeg/Filter/Vaapi/*` (esp. `TonemapVaapiFilter.cs`'s `hwupload=derive_device=...,hwmap=...,tonemap_opencl=...` chain; `scale_vaapi`) - **QSV filters / init** — `ErsatzTV.FFmpeg/Filter/Qsv/*`, `-init_hw_device qsv=hw` - **CUDA/Vulkan init** — `ErsatzTV.FFmpeg/GlobalOption/HardwareAcceleration/*` (`-init_hw_device cuda=...`, `vulkan=...`) - **Encoder options** — `Encoder/Vaapi/*`, `Encoder/Qsv/*` (`-rc_mode`, `-sei`, etc. — check for deprecations) - **Capability parsers** — `Capabilities/Vaapi/VaapiCapabilityParser.cs` if `vainfo` output shifts - **Tests** — `ErsatzTV.FFmpeg.Tests/PipelineBuilderBaseTests.cs` and `Filter/*` assert exact command strings; expect to update these ## How discovery happens Most breakage is only knowable by building a test image on FFmpeg 8 and running the suite: - [ ] Build a test ErsatzTV image on the `ersatztv-ffmpeg:8.x` base (depends on `ersatztv-ffmpeg#4`) - [ ] Run `dotnet test ErsatzTV.FFmpeg.Tests` and fix command-string assertions - [ ] Review FFmpeg 8.0 release notes for deprecations affecting `scale_vaapi`, `hwmap`, `hwupload`/`hwdownload`, `tonemap_opencl`, `derive_device`, `zscale`, `setsar`, and encoder options - [ ] Manually verify transcodes: H.264 / HEVC / 10-bit inputs across software + each hwaccel available on jazz (Intel/VAAPI/QSV) - [ ] Update filter chains / encoder options as needed ## Depends on `ersatztv-ffmpeg#4` (8.x base image). Do after the CI/CD pipeline (#4/#3) lands. Backlog.
Author
Owner

Starting work on this in an isolated worktree from fresh origin/main at /private/tmp/ersatztv-issue9-ffmpeg8 on branch issue9-ffmpeg8.

Initial plan:

  • establish the current ErsatzTV.FFmpeg.Tests baseline on origin/main
  • inspect Docker base-image references and FFmpeg command-generation tests
  • switch the app image reference only after FFmpeg 8.1.2 test/smoke evidence justifies it
  • keep changes targeted to FFmpeg 8 compatibility and exact assertion updates
Starting work on this in an isolated worktree from fresh `origin/main` at `/private/tmp/ersatztv-issue9-ffmpeg8` on branch `issue9-ffmpeg8`. Initial plan: - establish the current `ErsatzTV.FFmpeg.Tests` baseline on `origin/main` - inspect Docker base-image references and FFmpeg command-generation tests - switch the app image reference only after FFmpeg 8.1.2 test/smoke evidence justifies it - keep changes targeted to FFmpeg 8 compatibility and exact assertion updates
Author
Owner

Progress update:

Findings:

  • ErsatzTV.FFmpeg.Tests baseline on fresh origin/main was clean: 18/18 passing.
  • FFmpeg 8.1.2 image exposes the QSV/VAAPI options ErsatzTV currently uses (vpp_qsv=tonemap=1, VAAPI -rc_mode, VAAPI -sei).
  • On bumblebee, the published FFmpeg 8.1.2 base resolves to digest sha256:89bf7b6b563d21d7cf49b102ca495fd143028c05e8c9779cd5c1366d9cf823a7 during app image build.
  • bumblebee currently exposes AMD VAAPI only (vendor=0x1002), so Intel QSV hardware transcode cannot be fully smoked there. The existing unqualified QSV init shape (-init_hw_device qsv=hw) fails on that host, and the app now emits FFmpeg's documented VAAPI-derived QSV device form when a render node is configured.

Changes in progress:

  • QSV global option now emits -init_hw_device vaapi=va:<render-node> -init_hw_device qsv=hw@va -filter_hw_device hw when a QSV/VAAPI render node is configured.
  • Added focused QSV global-option tests.
  • Updated amd64 app Docker base to 192.168.1.95:3000/timothy/ersatztv-ffmpeg:8.1.2.
  • Updated docker/ffmpeg-tests to use the internal 8.1.2 base and .NET 10 SDK/restore setup.
  • Updated project docs that name the FFmpeg base image.

Verification so far:

  • dotnet test ErsatzTV.FFmpeg.Tests/ErsatzTV.FFmpeg.Tests.csproj -v minimal: 20/20 passing.
  • Built app image ersatztv:issue9-ffmpeg8 on bumblebee from docker/Dockerfile successfully.
  • Built ffmpeg-test image ersatztv-ffmpeg-tests:issue9-ffmpeg8 on bumblebee successfully.
  • Built app image smoke: ffmpeg -version reports 8.1.2, software libx264 lavfi encode works, AMD VAAPI H.264 lavfi encode works.
  • The ffmpeg-tests container runtime smoke was started and shown to run real FFmpeg transcode cases, but I stopped it after several minutes because it was still working through CPU-heavy libx265 cases; build of that image is verified, full runtime completion remains for CI or a longer manual run.
Progress update: Findings: - `ErsatzTV.FFmpeg.Tests` baseline on fresh `origin/main` was clean: 18/18 passing. - FFmpeg 8.1.2 image exposes the QSV/VAAPI options ErsatzTV currently uses (`vpp_qsv=tonemap=1`, VAAPI `-rc_mode`, VAAPI `-sei`). - On `bumblebee`, the published FFmpeg 8.1.2 base resolves to digest `sha256:89bf7b6b563d21d7cf49b102ca495fd143028c05e8c9779cd5c1366d9cf823a7` during app image build. - `bumblebee` currently exposes AMD VAAPI only (`vendor=0x1002`), so Intel QSV hardware transcode cannot be fully smoked there. The existing unqualified QSV init shape (`-init_hw_device qsv=hw`) fails on that host, and the app now emits FFmpeg's documented VAAPI-derived QSV device form when a render node is configured. Changes in progress: - QSV global option now emits `-init_hw_device vaapi=va:<render-node> -init_hw_device qsv=hw@va -filter_hw_device hw` when a QSV/VAAPI render node is configured. - Added focused QSV global-option tests. - Updated amd64 app Docker base to `192.168.1.95:3000/timothy/ersatztv-ffmpeg:8.1.2`. - Updated `docker/ffmpeg-tests` to use the internal 8.1.2 base and .NET 10 SDK/restore setup. - Updated project docs that name the FFmpeg base image. Verification so far: - `dotnet test ErsatzTV.FFmpeg.Tests/ErsatzTV.FFmpeg.Tests.csproj -v minimal`: 20/20 passing. - Built app image `ersatztv:issue9-ffmpeg8` on `bumblebee` from `docker/Dockerfile` successfully. - Built ffmpeg-test image `ersatztv-ffmpeg-tests:issue9-ffmpeg8` on `bumblebee` successfully. - Built app image smoke: `ffmpeg -version` reports 8.1.2, software `libx264` lavfi encode works, AMD VAAPI H.264 lavfi encode works. - The ffmpeg-tests container runtime smoke was started and shown to run real FFmpeg transcode cases, but I stopped it after several minutes because it was still working through CPU-heavy `libx265` cases; build of that image is verified, full runtime completion remains for CI or a longer manual run.
Author
Owner

Done

What was done: Implemented app-side FFmpeg 8 compatibility for the transcode pipeline by deriving QSV from the configured VAAPI render node, updated the amd64 app Docker base to 192.168.1.95:3000/timothy/ersatztv-ffmpeg:8.1.2, updated the ffmpeg-test Docker image to the internal FFmpeg 8.1.2 base and .NET 10 setup, and refreshed docs that name the base image. Merged in PR #56.

Root cause: The FFmpeg 8.1.2 base image was published and valid, but ErsatzTV still emitted legacy/unqualified QSV init (-init_hw_device qsv=hw) and the app/test Dockerfiles were still pinned to FFmpeg 7.1.1. FFmpeg 8 is better aligned with an explicit configured render node by creating a VAAPI device and deriving QSV from it (vaapi=va:<render-node> then qsv=hw@va).

Files changed: ErsatzTV.FFmpeg/GlobalOption/HardwareAcceleration/QsvHardwareAccelerationOption.cs; ErsatzTV.FFmpeg.Tests/GlobalOption/HardwareAcceleration/QsvHardwareAccelerationOptionTests.cs; docker/Dockerfile; docker/ffmpeg-tests/Dockerfile; docs/ci-cd.md; docs/fork-strategy.md.

Verification: Baseline ErsatzTV.FFmpeg.Tests on fresh origin/main: 18/18 passing. New QSV test failed before the fix and passed after. Final local dotnet test ErsatzTV.FFmpeg.Tests/ErsatzTV.FFmpeg.Tests.csproj -v minimal: 20/20 passing. Built ersatztv:issue9-ffmpeg8 on bumblebee, resolving the FFmpeg base to digest sha256:89bf7b6b563d21d7cf49b102ca495fd143028c05e8c9779cd5c1366d9cf823a7. Built image smoke: FFmpeg 8.1.2 present, software libx264 lavfi encode passed, AMD VAAPI H.264 lavfi encode passed. Built ersatztv-ffmpeg-tests:issue9-ffmpeg8; its full runtime transcode test was started and shown to execute real FFmpeg cases but was stopped after several minutes due CPU-heavy duration. CI for PR #56 passed: Build & test and EF migration integrity succeeded; PR image-push job skipped as expected.

Deferred: Full Intel QSV hardware smoke could not be completed on bumblebee because it currently exposes AMD VAAPI only (vendor=0x1002). Full docker/ffmpeg-tests runtime completion was left to CI/longer manual execution due duration, but image build and targeted app image smokes passed.

Follow-up issues: none opened.

Docs updated: docs/ci-cd.md and docs/fork-strategy.md.

## Done **What was done**: Implemented app-side FFmpeg 8 compatibility for the transcode pipeline by deriving QSV from the configured VAAPI render node, updated the amd64 app Docker base to `192.168.1.95:3000/timothy/ersatztv-ffmpeg:8.1.2`, updated the ffmpeg-test Docker image to the internal FFmpeg 8.1.2 base and .NET 10 setup, and refreshed docs that name the base image. Merged in PR #56. **Root cause**: The FFmpeg 8.1.2 base image was published and valid, but ErsatzTV still emitted legacy/unqualified QSV init (`-init_hw_device qsv=hw`) and the app/test Dockerfiles were still pinned to FFmpeg 7.1.1. FFmpeg 8 is better aligned with an explicit configured render node by creating a VAAPI device and deriving QSV from it (`vaapi=va:<render-node>` then `qsv=hw@va`). **Files changed**: `ErsatzTV.FFmpeg/GlobalOption/HardwareAcceleration/QsvHardwareAccelerationOption.cs`; `ErsatzTV.FFmpeg.Tests/GlobalOption/HardwareAcceleration/QsvHardwareAccelerationOptionTests.cs`; `docker/Dockerfile`; `docker/ffmpeg-tests/Dockerfile`; `docs/ci-cd.md`; `docs/fork-strategy.md`. **Verification**: Baseline `ErsatzTV.FFmpeg.Tests` on fresh `origin/main`: 18/18 passing. New QSV test failed before the fix and passed after. Final local `dotnet test ErsatzTV.FFmpeg.Tests/ErsatzTV.FFmpeg.Tests.csproj -v minimal`: 20/20 passing. Built `ersatztv:issue9-ffmpeg8` on `bumblebee`, resolving the FFmpeg base to digest `sha256:89bf7b6b563d21d7cf49b102ca495fd143028c05e8c9779cd5c1366d9cf823a7`. Built image smoke: FFmpeg 8.1.2 present, software `libx264` lavfi encode passed, AMD VAAPI H.264 lavfi encode passed. Built `ersatztv-ffmpeg-tests:issue9-ffmpeg8`; its full runtime transcode test was started and shown to execute real FFmpeg cases but was stopped after several minutes due CPU-heavy duration. CI for PR #56 passed: Build & test and EF migration integrity succeeded; PR image-push job skipped as expected. **Deferred**: Full Intel QSV hardware smoke could not be completed on `bumblebee` because it currently exposes AMD VAAPI only (`vendor=0x1002`). Full `docker/ffmpeg-tests` runtime completion was left to CI/longer manual execution due duration, but image build and targeted app image smokes passed. **Follow-up issues**: none opened. **Docs updated**: `docs/ci-cd.md` and `docs/fork-strategy.md`.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: timothy/ersatztv#9