Skip to content

build(container): restrict the in-tree ffmpeg to a narrow media-codec allowlist (OPS-7665) - #11628

Closed
dmitry-tokarev-nv wants to merge 10 commits into
mainfrom
dtokarev/ops-7665-ffmpeg-decoder-allowlist
Closed

build(container): restrict the in-tree ffmpeg to a narrow media-codec allowlist (OPS-7665)#11628
dmitry-tokarev-nv wants to merge 10 commits into
mainfrom
dtokarev/ops-7665-ffmpeg-decoder-allowlist

Conversation

@dmitry-tokarev-nv

@dmitry-tokarev-nv dmitry-tokarev-nv commented Jul 13, 2026

Copy link
Copy Markdown
Contributor

Summary

The in-tree ffmpeg already restricted encoders/muxers, but it still shipped ffmpeg's full default decoder set (291 video + 212 audio decoders) in the libav*.so copied into every runtime image. Narrow it to the media formats we actually build and use.

This switches the wheel_builder ffmpeg configure to an allowlist: blanket --disable-decoders/--disable-demuxers/--disable-parsers/--disable-protocols plus exactly the components the two real paths need:

  • encode CLI (imageio): rawvideo demux/decode over a pipe → h264_nvenc/libvpx_vp9
  • Rust media-ffmpeg VideoDecoder: VP8/VP9 in mp4/webm/mkv

Image decode does not use ffmpeg (it goes through the Rust image crate), so no still-image decoders are enabled. HW NVDEC (h264_cuvid/hevc_cuvid) can be re-added explicitly if a decode feature ever needs H.264/H.265 (not wired today — noted in the media README).

Also bumps ffmpeg 8.1 → 8.1.2 (an upstream maintenance release) to pick up security fixes and trim the decode surface further.

Test fixtures are re-encoded to VP9-in-mp4; filenames and every Rust/Python test that consumes them are unchanged.

Pairs with the compliance-side gate in #11632. See OPS-7665 for background.

Validation

  • Built ffmpeg 8.1.2 with this exact allowlistconfigure accepts every component token (caught one bug: webm is not a separate demuxer token; matroska covers it) and the build succeeds LGPL-2.1.
  • Decoded all five regenerated fixtures through that restricted build to raw YUV420p; byte counts match frames × w × h × 1.5 exactly (2×2/10f, 320×240/1f/10f/100f, 3840×2160/10f).
  • container/render.py renders the sglang runtime (the enable_media_ffmpeg=true consumer) with the new configure block intact.
  • Fixtures committed as Git LFS; native_packages.yaml ffmpeg version bumped to match.

Linear: https://linear.app/nvidia/issue/OPS-7665/disable-ffmpeg-decoders-and-add-image-scan

🤖 Generated with Claude Code

@dmitry-tokarev-nv
dmitry-tokarev-nv requested review from a team as code owners July 13, 2026 22:54
@github-actions github-actions Bot added build documentation Improvements or additions to documentation frontend `python -m dynamo.frontend` and `dynamo-run in=http|text|grpc` container labels Jul 13, 2026
@github-actions

github-actions Bot commented Jul 13, 2026

Copy link
Copy Markdown
Contributor

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Devin Review found 1 potential issue.

Open in Devin Review

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🔍 Pre-existing --disable-bsfs may affect VP9 decode in MP4 containers

The --disable-bsfs flag at container/templates/wheel_builder.Dockerfile:357 was already present before this PR and disables all bitstream filters. Some demuxers/decoders use bitstream filters internally (e.g., vp9_superframe for VP9 in MP4). If VP9-in-MP4 decode fails at runtime due to missing BSFs, this pre-existing flag would be the cause. The test fixtures were regenerated with -g 1 (all keyframes), which may mask the issue since superframe handling is primarily needed for non-keyframe packets. Worth monitoring in integration tests with real-world VP9-in-MP4 content that has B-frames or superframes.

(Refers to line 357)

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

@coderabbitai

coderabbitai Bot commented Jul 13, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Walkthrough

Changes

FFmpeg version and codec policy

Layer / File(s) Summary
FFmpeg version metadata
container/compliance/native_packages.yaml, container/context.yaml
FFmpeg references are updated from 8.1 to 8.1.2, with comments documenting the CVE-2026-8461 fix and MagicYUV removal.
FFmpeg codec build policy
container/templates/wheel_builder.Dockerfile
The wheel builder documents codec constraints and configures allowlisted VP8, VP9, and rawvideo decoders with corresponding container and parser restrictions.
Media codec documentation
lib/llm/src/preprocessor/media/README.md, lib/llm/src/preprocessor/media/decoders/video.rs
Documentation identifies supported VP9-in-MP4 fixtures and unsupported H.264, H.265, and AAC frontend decoding.

Estimated code review effort: 2 (Simple) | ~10 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description covers the change well, but it misses the template's required 'Where should the reviewer start?' and 'Related Issues' sections. Add the missing sections, especially a reviewer-start file list and the required issue linkage or explicit 'not linked' confirmation.
✅ Passed checks (4 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title clearly summarizes the main change: restricting in-tree FFmpeg to a codec allowlist.

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@lib/llm/src/preprocessor/media/decoders/video.rs`:
- Around line 318-320: Update the ffmpeg regeneration command in the video
fixture documentation to append an MP4 output filename after the encoding
options, making the example executable while preserving its existing input,
codec, frame-count, and keyframe settings.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 040fe901-aba8-4e7a-99ee-3261d77e31a7

📥 Commits

Reviewing files that changed from the base of the PR and between 2bec928 and 9ed1a9a.

⛔ Files ignored due to path filters (5)
  • lib/llm/tests/data/media/2160p_10.mp4 is excluded by !**/*.mp4
  • lib/llm/tests/data/media/240p_1.mp4 is excluded by !**/*.mp4
  • lib/llm/tests/data/media/240p_10.mp4 is excluded by !**/*.mp4
  • lib/llm/tests/data/media/240p_100.mp4 is excluded by !**/*.mp4
  • lib/llm/tests/data/media/2p_10.mp4 is excluded by !**/*.mp4
📒 Files selected for processing (5)
  • container/compliance/native_packages.yaml
  • container/context.yaml
  • container/templates/wheel_builder.Dockerfile
  • lib/llm/src/preprocessor/media/README.md
  • lib/llm/src/preprocessor/media/decoders/video.rs

Comment on lines +318 to +320
/// Fixtures are VP9-in-mp4: the in-tree ffmpeg only decodes the royalty-free
/// allowlist (VP8/VP9), so H.264 fixtures would not decode. Regenerate with
/// `ffmpeg -f lavfi -i testsrc2=size=WxH:rate=1 -frames:v N -c:v libvpx-vp9 -g 1 -strict -2`.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect the target file around the cited lines.
sed -n '300,335p' lib/llm/src/preprocessor/media/decoders/video.rs

# Look for similar ffmpeg regeneration instructions elsewhere.
rg -n "ffmpeg -f lavfi|libvpx-vp9|testsrc2|240p_10\.mp4|Fixtures are VP9-in-mp4" lib -S

Repository: ai-dynamo/dynamo

Length of output: 2531


🏁 Script executed:

#!/bin/bash
set -euo pipefail

sed -n '310,330p' lib/llm/src/preprocessor/media/decoders/video.rs

Repository: ai-dynamo/dynamo

Length of output: 1124


Add an output path to the ffmpeg example. The regeneration command is incomplete as written; append an MP4 filename so it can actually run.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/llm/src/preprocessor/media/decoders/video.rs` around lines 318 - 320,
Update the ffmpeg regeneration command in the video fixture documentation to
append an MP4 output filename after the encoding options, making the example
executable while preserving its existing input, codec, frame-count, and keyframe
settings.

Source: Path instructions

@datadog-official

datadog-official Bot commented Jul 13, 2026

Copy link
Copy Markdown

🎯 Code Coverage (details)
Patch Coverage: 100.00%
Overall Coverage: 40.90% (-8.27%)

This comment will be updated automatically if new data arrives.
🔗 Commit SHA: 09391d2 | Docs | Give us feedback!

@dmitry-tokarev-nv dmitry-tokarev-nv changed the title build(container): restrict in-tree ffmpeg to a royalty-free codec allowlist (OPS-7665) build(container): restrict in-tree ffmpeg codec allowlist (OPS-7665) Jul 13, 2026
… allowlist (OPS-7665)

The in-tree ffmpeg restricted encoders/muxers but still shipped ffmpeg's full
default decoder set (291 video + 212 audio decoders) in the libav*.so copied
into every runtime image. Narrow it to the media formats we actually build and
use.

Switch the wheel_builder configure to an allowlist: blanket
--disable-decoders/--disable-demuxers/--disable-parsers/--disable-protocols
plus exactly the components the two real paths need:
- encode CLI (imageio): rawvideo demux/decode over a pipe -> h264_nvenc/vp9
- Rust media-ffmpeg VideoDecoder: VP8/VP9 in mp4/webm/mkv
Image decode does not use ffmpeg (Rust `image` crate), so no still-image
decoders are enabled. HW NVDEC (h264_cuvid/hevc_cuvid) can be re-added if a
decode feature needs it.

Also bump ffmpeg 8.1 -> 8.1.2 (an upstream maintenance release) to pick up
security fixes and trim the decode surface further.

Test fixtures are re-encoded to VP9-in-mp4; filenames and the Rust/Python tests
that consume them are unchanged. Validated by building ffmpeg 8.1.2 with this
exact allowlist and decoding every fixture to the expected frame counts.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Dmitry Tokarev <dtokarev@nvidia.com>
@dmitry-tokarev-nv
dmitry-tokarev-nv force-pushed the dtokarev/ops-7665-ffmpeg-decoder-allowlist branch from 9ed1a9a to 869ddaf Compare July 13, 2026 23:46
@dmitry-tokarev-nv dmitry-tokarev-nv changed the title build(container): restrict in-tree ffmpeg codec allowlist (OPS-7665) build(container): restrict the in-tree ffmpeg to a narrow media-codec allowlist (OPS-7665) Jul 13, 2026
Remove codec-bearing Python packages inherited from the SGLang base, build the Dynamo runtime without media-ffmpeg, and fail the image build if FFmpeg, H.264, H.265, or AAC libraries remain.

Signed-off-by: Harrison King Saturley-Hall <hsaturleyhal@nvidia.com>
The ARM64 Inkling base installs the distribution as decord2, whose wheel bundles FFmpeg, x264, and x265 shared libraries. Remove that package and its residual files before the final codec guard.

Signed-off-by: Harrison King Saturley-Hall <hsaturleyhal@nvidia.com>
Signed-off-by: Harrison King Saturley-Hall <hsaturleyhal@nvidia.com>
@dmitry-tokarev-nv

Copy link
Copy Markdown
Contributor Author

Harrison — thanks for the SGLang codec removal. It lines up exactly with the rc.10 codec audit I ran: SGLang had the most codec sources of the three images (in-tree ffmpeg + a vendored ai_dynamo_runtime.libs copy + PyAV av.libs + decord2.libs + opencv), so removing ffmpeg + purging those wheels is the right call. A few follow-ups on this PR:

CI test failures (root-caused):

  • sglang-runtime / Testtest_sglang_deployment[video_e_pd_qwen] fails with No module named 'decord'. SGLang's video-multimodal serving path still imports decord, which your commit (correctly) removed. The test needs to be removed/skipped — SGLang video decode is intentionally gone now.
  • trtllm-runtime / Testtest_deployment[video_diffusion] fails: h264_nvenc encode→mp4 "Broken pipe", empty ffmpeg stderr. This is the diffusion video encode path. I can't tell from the log whether it's a regression from my decoder allowlist or pre-existing NVENC flakiness (the empty stderr leans toward the latter). It needs a GPU repro — build the allowlist ffmpeg and run the exact command: ffmpeg -f rawvideo -i - -vcodec h264_nvenc -pix_fmt yuv420p out.mp4. If it is the allowlist, the likely fix is adding --enable-parser=h264 (a parser, not a codec — no royalty impact) so the mp4 muxer can pull SPS/PPS from the h264_nvenc stream. I'm happy to run this down if you'd like.

Open review nits from my original commits:

  • OPS-7665 appears in container/templates/wheel_builder.Dockerfile:303 and lib/llm/src/preprocessor/media/README.md:56 — Devin/CodeRabbit flag it (repo bans Linear IDs in source). Should drop both.
  • lib/llm/src/preprocessor/media/decoders/video.rs:318-320 — the ffmpeg regen command in the docstring is missing the output filename (CodeRabbit).

Devin's --disable-bsfs/VP9 comment is a non-issue: I validated VP9-in-mp4 decode against the exact allowlist build (which has --disable-bsfs) — all fixtures decoded to the correct frame counts. The vp9_superframe BSF isn't needed for the mov-demuxer + vp9-decoder + vp9-parser path.

Sibling PRs: #11607 (opencv removal) and #11632 (the compliance gate, which should merge last). Glad to take any of the above off your plate — just say the word.

dmitry-tokarev-nv and others added 3 commits July 17, 2026 13:31
- Drop internal ticket references from the wheel_builder + media README comments.
- Make the fixture-regeneration ffmpeg command in the video.rs docstring
  executable by adding the output filename.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: Dmitry Tokarev <dtokarev@nvidia.com>
The TRT-LLM diffusion encode path (imageio -> ffmpeg -c:v h264_nvenc -> mp4)
regressed after the decoder allowlist. The h264 *parser* (not the H.264
decoder) is needed for the mp4 muxer to package the h264_nvenc bitstream, so
add it back. A parser carries no codec implementation, so this does not
reintroduce any software H.264 encode/decode surface; h264_nvenc remains the
NVIDIA hardware encoder (the sanctioned path).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: Dmitry Tokarev <dtokarev@nvidia.com>
@dmitry-tokarev-nv

Copy link
Copy Markdown
Contributor Author

Addressed the review comments on my commits (pushed c45909b182, 642de0e3e1):

  • Removed the internal ticket references from wheel_builder.Dockerfile and the media README.md (Devin/CodeRabbit).
  • video.rs docstring ffmpeg regen command now includes the output filename so it's executable (CodeRabbit).
  • trtllm video_diffusion encode failure (h264_nvenc → mp4, broken pipe): my decoder allowlist had dropped the h264 parser, which the mp4 muxer needs to package the h264_nvenc bitstream. Added it back (--enable-parser=vp8,vp9,h264). A parser is not a codec — no software H.264 encode/decode is reintroduced; h264_nvenc stays the sanctioned HW encoder. Caveat: the failure log had an empty ffmpeg stderr, which can also indicate an NVENC/env flake, so the trtllm GPU test is the real confirmation — but the parser is a correct, zero-risk addition regardless.
  • Devin's --disable-bsfs/VP9 concern: validated a non-issue — I decoded all VP9-in-mp4 fixtures with the exact allowlist build (which has --disable-bsfs); the vp9_superframe BSF isn't needed for the mov-demuxer + vp9-decoder + vp9-parser path.

Still open (not a review comment — a consequence of the SGLang codec removal): the SGLang video_agg_qwen and video_e_pd_qwen serve tests fail with No module named 'decord'. SGLang no longer has any video decoder (decord/av/ffmpeg all removed), so these two video-multimodal tests can't pass and need to be skipped or removed. Happy to push a skip with a clear reason if you'd like, @hsaturleyhal — flagging since it's your removal + implies SGLang drops video-multimodal input.

The vllm-openai base ships PyAV, opencv, decord, torchcodec, and PyNvVideoCodec,
each bundling its own full ffmpeg with software H.264/H.265/AAC (PyAV and decord
also ship GPL libx264/libx265). Dynamo's vLLM component imports none of them, so
they are unused decode-side dead weight. Uninstall + remove them, with a guard
that fails the build if cv2/av survive.

The in-tree LGPL ffmpeg and imageio-ffmpeg are kept for the omni HW video-encode
path (h264_nvenc, the sanctioned NVIDIA hardware encoder). This drops software
video *decode* from the vLLM images (mirrors the SGLang codec removal).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: Dmitry Tokarev <dtokarev@nvidia.com>
@dmitry-tokarev-nv

Copy link
Copy Markdown
Contributor Author

Added the vLLM codec cleanup (eaabac23c6) so #11632's gate can pass on vllm-runtime:

  • Removes the video-decode wheels inherited from the vllm-openai base — av (PyAV), opencv-python(-headless), decord/decord2, torchcodec, and PyNvVideoCodec — each of which bundles a full software ffmpeg (PyAV/decord also ship GPL libx264/libx265). Dynamo's vLLM component imports none of them.
  • Keeps the in-tree LGPL ffmpeg + imageio-ffmpeg for the omni HW video-encode path (h264_nvenc, sanctioned).
  • Guards fail the build if cv2/av survive.

Lower risk than the SGLang removal: no vLLM serve/deployment test decodes video (the video_url references in the vLLM tests are request-parser unit mocks + omni output formatting, not decode), so this shouldn't add a CI test failure. It does drop software video-decode input from vLLM — the same compliance trade-off as SGLang.

Note: the #11632 scan also flagged PyNvVideoCodec — if it's present on the trtllm image too (it's new since my rc.10 audit), trtllm will need the same strip. Worth a check when trtllm's scan runs.

@dmitry-tokarev-nv

Copy link
Copy Markdown
Contributor Author

Combined into #11836 (with the vLLM codec cleanup added on top). Keeping this open for the review thread + CI. Review comments addressed (OPS refs removed, video.rs regen command fixed, Devin --disable-bsfs/VP9 validated as a non-issue). Open CI items: sglang Test still fails on the removed decord (video_agg_qwen/video_e_pd_qwen need a skip — @hsaturleyhal, your call); trtllm/vllm Test are running (the h264 parser fix should clear the trtllm encode failure).

dmitry-tokarev-nv and others added 2 commits July 17, 2026 22:09
The narrow in-tree ffmpeg protocol allowlist enabled only file,pipe. On
ffmpeg 8.x, `ffmpeg -i -` (reading rawvideo frames from stdin, as the
imageio encode path does) resolves stdin through the `fd:` protocol, not
`pipe:`. With fd disabled, opening the stdin input fails immediately with
"Error opening input: Protocol not found. Did you mean file:fd:?", breaking
the video encode path.

Add fd to the protocol allowlist. It is pure file-descriptor I/O and
carries no codec implementation, so it does not widen the media-format
surface. Reproduced and confirmed against a locally built allowlisted
ffmpeg: file,pipe fails to open stdin; file,pipe,fd opens it cleanly.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: Dmitry Tokarev <dtokarev@nvidia.com>
…ages

The vllm and sglang runtime images intentionally omit certain media-decoder
libraries (opencv for vLLM's VideoMediaIO backend decode, decord for SGLang's
video path). The video multimodal serve tests exercise those decode paths, so
without the decoder present they fail at request time ("No module named
'cv2'" / "No module named 'decord'").

Rather than skip that coverage, let a serve config opt into an ephemeral,
test-only decoder install via a reserved env key (DYN_TEST_ONLY_PIP_INSTALL).
common.run_serve_deployment installs the named package(s) into the runtime
container before the server launches, so the worker can import them; the
shipped image is unaffected. Session-deduped and a no-op unless a config sets
the key.

Wired for the three vLLM video topologies (opencv-python-headless) and the two
SGLang video configs (decord2). Verified on faithful codec-stripped rc.11
images: each decode path reproduces the exact failure when stripped and decodes
successfully once the package is installed.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: Dmitry Tokarev <dtokarev@nvidia.com>
@dmitry-tokarev-nv

Copy link
Copy Markdown
Contributor Author

superseeded by #11836

@dmitry-tokarev-nv
dmitry-tokarev-nv deleted the dtokarev/ops-7665-ffmpeg-decoder-allowlist branch August 10, 2026 16:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

build container documentation Improvements or additions to documentation frontend `python -m dynamo.frontend` and `dynamo-run in=http|text|grpc` size/L

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants