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
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 13 additions & 11 deletions components/src/dynamo/vllm/omni/output_formatter.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
import asyncio
import base64
import logging
import tempfile
import time
import uuid
from io import BytesIO
Expand All @@ -20,15 +19,17 @@
import numpy as np
import soundfile as sf
import torch
from diffusers.utils.export_utils import export_to_video

from dynamo.common.protocols.audio_protocol import AudioData, NvAudioSpeechResponse
from dynamo.common.protocols.image_protocol import ImageData, NvImagesResponse
from dynamo.common.protocols.video_protocol import NvVideosResponse, VideoData
from dynamo.common.storage import upload_to_fs
from dynamo.common.utils.engine_response import normalize_finish_reason
from dynamo.common.utils.output_modalities import RequestType
from dynamo.common.utils.video_utils import normalize_video_frames
from dynamo.common.utils.video_utils import (
encode_to_video_bytes,
normalize_video_frames,
)
from dynamo.vllm.handlers import build_prompt_tokens_details
from dynamo.vllm.omni.utils import is_empty_payload

Expand Down Expand Up @@ -129,24 +130,25 @@ async def _encode_video(
response_format: Optional[str] = None,
output_format: Optional[str] = None,
) -> Dict[str, Any] | None:
output_format = output_format or "mp4"
output_format = output_format or "webm"
response_format = response_format or "url"
if response_format not in ("url", "b64_json"):
raise ValueError(
f"Unsupported response_format: {response_format!r}; expected 'url' or 'b64_json'"
)
if output_format != "mp4":
if output_format != "webm":
raise ValueError(
f"Unsupported output_format: {output_format!r}; only 'mp4' is supported"
f"Unsupported output_format: {output_format!r}; only 'webm' is supported"
)
try:
start_time = time.time()
frame_list = normalize_video_frames(images)
with tempfile.NamedTemporaryFile(
suffix=f".{output_format}", delete=True
) as tmp:
await asyncio.to_thread(export_to_video, frame_list, tmp.name, fps)
video_bytes = tmp.read()
video_bytes = await asyncio.to_thread(
encode_to_video_bytes,
np.asarray(frame_list),
fps=fps,
output_format=output_format,
)

if response_format == "b64_json":
video_data = VideoData(
Expand Down
37 changes: 29 additions & 8 deletions components/src/dynamo/vllm/tests/omni/test_output_formatter.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

from unittest.mock import MagicMock, patch

import numpy as np
import pytest

try:
Expand Down Expand Up @@ -523,20 +524,23 @@ async def test_audio_data_carries_output_format_url_path(self):


class TestDiffusionFormatterVideoOutputFormat:
"""_encode_video always sets VideoData.output_format='mp4'."""
"""_encode_video uses WebM/VP9 so vLLM does not require H.264."""

def _patches(self):
from unittest.mock import patch as _patch

return (
_patch(
"dynamo.vllm.omni.output_formatter.normalize_video_frames",
return_value=[MagicMock()],
return_value=[np.zeros((2, 2, 3), dtype=np.uint8)],
),
_patch(
"dynamo.vllm.omni.output_formatter.encode_to_video_bytes",
return_value=b"fake-webm",
),
_patch("dynamo.vllm.omni.output_formatter.export_to_video"),
_patch(
"dynamo.vllm.omni.output_formatter.upload_to_fs",
return_value="http://x/v.mp4",
return_value="http://x/v.webm",
),
_patch(
"dynamo.vllm.omni.output_formatter.asyncio.to_thread",
Expand Down Expand Up @@ -564,8 +568,8 @@ async def test_video_url_response_format(self):
)

assert result is not None
assert result["data"][0]["output_format"] == "mp4"
assert result["data"][0]["url"] == "http://x/v.mp4"
assert result["data"][0]["output_format"] == "webm"
assert result["data"][0]["url"] == "http://x/v.webm"
assert result["data"][0].get("b64_json") is None
mock_upload.assert_called_once()

Expand All @@ -591,7 +595,7 @@ async def test_video_b64_response_format(self):
)

assert result is not None
assert result["data"][0]["output_format"] == "mp4"
assert result["data"][0]["output_format"] == "webm"
assert result["data"][0].get("url") is None
assert result["data"][0]["b64_json"] is not None
base64.b64decode(result["data"][0]["b64_json"]) # must be valid base64
Expand All @@ -614,5 +618,22 @@ async def test_video_default_response_format_is_url(self):
)

assert result is not None
assert result["data"][0]["url"] == "http://x/v.mp4"
assert result["data"][0]["url"] == "http://x/v.webm"
mock_upload.assert_called_once()

@pytest.mark.asyncio
async def test_video_mp4_output_is_rejected(self):
from dynamo.common.utils.output_modalities import RequestType
from dynamo.vllm.omni.output_formatter import DiffusionFormatter

f = DiffusionFormatter(model_name="test", media_fs=None, media_http_url=None)
stage = MagicMock()
stage.images = [MagicMock()]

with pytest.raises(ValueError, match="only 'webm' is supported"):
await f.format(
stage,
"r8",
request_type=RequestType.VIDEO_GENERATION,
output_format="mp4",
)
10 changes: 10 additions & 0 deletions container/deps/vllm/requirements.media-source.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: Apache-2.0
#
# Build these distributions from source against Dynamo's allowlisted FFmpeg.
# The hashes pin the PyPI source archives; binary wheels must never be used.

av==18.0.0 \
--hash=sha256:4ef7e72c3d3a872584a1215173b16e0226811037f40dcdbf75992631098df1ba
opencv-python==5.0.0.93 \
--hash=sha256:66aac3e5b5faa48d4025816592f3af19e4bfc2c68dec067bae2dbb4ca10aa9e2
213 changes: 213 additions & 0 deletions container/deps/vllm/validate_ffmpeg_provider.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,213 @@
# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: Apache-2.0

"""Validate that vLLM uses only Dynamo's allowlisted FFmpeg installation."""

import importlib
import importlib.metadata
import os
import re
import subprocess
from pathlib import Path

FFMPEG = Path("/usr/local/bin/ffmpeg")
ALLOWED_LIBRARY_ROOT = Path("/usr/local/lib")
SEARCH_ROOTS = (Path("/usr"), Path("/opt"), Path("/workspace"))
Comment on lines +14 to +15

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.

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Do not treat all of /usr/local/lib as the trusted provider.

Python site-packages reside below /usr/local/lib, so bundled paths such as .../site-packages/av.libs/libavcodec.so pass both the artifact and linkage checks. A later upstream-wheel reinstall can therefore satisfy this guard while restoring private FFmpeg libraries.

Only accept resolved libraries whose direct parent is /usr/local/lib, or compare against an explicit set of expected provider files.

Proposed tightening
+def is_allowed_media_library(path: Path) -> bool:
+    return path.resolve().parent == ALLOWED_LIBRARY_ROOT
+
-                    if not path.resolve().is_relative_to(ALLOWED_LIBRARY_ROOT):
+                    if not is_allowed_media_library(path):
                         artifacts.append(path)
...
-            if not resolved.is_relative_to(ALLOWED_LIBRARY_ROOT):
+            if not is_allowed_media_library(resolved):

Also applies to: 79-94, 106-131

🤖 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 `@container/deps/vllm/validate_ffmpeg_provider.py` around lines 14 - 15, The
validation currently trusts every library beneath ALLOWED_LIBRARY_ROOT, allowing
site-packages bundles such as av.libs to pass. Tighten the artifact and linkage
checks in the provider validation flow to accept only resolved libraries whose
immediate parent is /usr/local/lib, or an explicit expected-file allowlist;
preserve SEARCH_ROOTS for discovery without treating nested directories as
trusted.

ALLOWED_DECODERS = {"rawvideo", "vp8", "vp9"}
ALLOWED_ENCODERS = {"libvpx-vp9"}
REQUIRED_BUILD_CONFIGURATION = {
"--disable-bsfs",
"--disable-decoders",
"--disable-demuxers",
"--disable-encoders",
"--disable-muxers",
"--disable-parsers",
"--disable-protocols",
"--enable-decoder=vp8,vp9,rawvideo",
"--enable-demuxer=mov,matroska,rawvideo",
"--enable-encoder=libvpx_vp9",
"--enable-libvpx",
"--enable-muxer=matroska,webm",
"--enable-parser=vp8,vp9",
"--enable-protocol=file,pipe",
}
Comment on lines +18 to +33

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.

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Enforce the LGPL configuration explicitly.

The validator does not require --disable-gpl or --disable-nonfree, despite the runtime’s stated LGPL-only contract. A build with the expected codecs but GPL/nonfree mode enabled can pass.

Proposed fix
 REQUIRED_BUILD_CONFIGURATION = {
+    "--disable-gpl",
+    "--disable-nonfree",
     "--disable-bsfs",

Also reject contradictory --enable-gpl and --enable-nonfree tokens.

Also applies to: 145-153

🤖 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 `@container/deps/vllm/validate_ffmpeg_provider.py` around lines 18 - 29, Add
“--disable-gpl” and “--disable-nonfree” to REQUIRED_BUILD_CONFIGURATION, and
update the validator’s configuration-checking logic to reject contradictory
“--enable-gpl” and “--enable-nonfree” tokens. Ensure builds pass only when the
explicit LGPL-only flags are present and neither enabling token is configured.

FORBIDDEN_DISTRIBUTIONS = {
Comment on lines +19 to +34

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.

🔍 buildconf/decoder allowlist intentionally diverges from current main

REQUIRED_BUILD_CONFIGURATION (:19-30) requires flags such as --disable-decoders, --disable-demuxers, --disable-parsers, --disable-protocols, --enable-decoder=vp8,vp9,rawvideo, --enable-demuxer=mov,matroska,rawvideo, --enable-parser=vp8,vp9, and ALLOWED_DECODERS/ALLOWED_ENCODERS expect a narrow set. The current wheel_builder.Dockerfile FFmpeg configure (container/templates/wheel_builder.Dockerfile:333-351) does NOT pass these narrowing flags, so both the buildconf check and the decoder-set check would fail against main. This is consistent with the PR description stating the build 'fails by design' until dependent #11628 lands, so it is not flagged as a bug — but reviewers should ensure #11628 actually introduces exactly these configure flags (including the comma-ordering, since --enable-decoder=vp8,vp9,rawvideo is compared as an exact token via build_configuration.split()).

Open in Devin Review

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

"decord",
"decord2",
"opencv-python-headless",
"pynvvideocodec",
}
MEDIA_LIBRARY_RE = re.compile(
r"^(?:libav(?:codec|device|filter|format|util)|libpostproc|"
r"libsw(?:resample|scale)|libx26[45]|libopenh264|libfdk-aac|libfaac|"
r"libvo-aacenc|libaacplus).*\.so(?:\..*)?$"
)
Comment on lines +40 to +44

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.

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Track libvpx across both cleanup and validation. The provider intentionally supplies the allowlisted libvpx, but private copies outside /usr/local/lib are neither removed nor detected.

  • container/deps/vllm/validate_ffmpeg_provider.py#L36-L40: add libvpx to MEDIA_LIBRARY_RE.
  • container/templates/vllm_runtime.Dockerfile#L342-L344: add libvpx to the residual-library cleanup expression.
📍 Affects 2 files
  • container/deps/vllm/validate_ffmpeg_provider.py#L36-L40 (this comment)
  • container/templates/vllm_runtime.Dockerfile#L342-L344
🤖 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 `@container/deps/vllm/validate_ffmpeg_provider.py` around lines 36 - 40, Track
libvpx in both cleanup and validation: update MEDIA_LIBRARY_RE in
validate_ffmpeg_provider.py to match libvpx shared libraries, and update the
residual-library cleanup expression in
container/templates/vllm_runtime.Dockerfile to remove private libvpx copies
outside /usr/local/lib.

MEDIA_BINARY_RE = re.compile(r"^ff(?:mpeg|probe)(?:[-_].*)?$")
COMPONENT_RE = re.compile(r"^[A-Z.]{6}\s+(\S+)")


def run(*command: str) -> str:
"""Run a validation command and return stdout."""

return subprocess.run(
command,
check=True,
text=True,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
).stdout


def normalize_distribution_name(name: str) -> str:
return re.sub(r"[-_.]+", "-", name).lower()


def installed_distributions() -> dict[str, importlib.metadata.Distribution]:
return {
normalize_distribution_name(distribution.metadata["Name"]): distribution
for distribution in importlib.metadata.distributions()
if distribution.metadata["Name"]
}


def ffmpeg_components(kind: str) -> set[str]:
output = run(str(FFMPEG), "-hide_banner", f"-{kind}")
components = set()
for line in output.splitlines():
match = COMPONENT_RE.match(line.strip())
if match is not None:
components.add(match.group(1))
return components
Comment on lines +77 to +80

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.

🔴 Media codec safety check always fails, blocking the container image build

The tool that inventories the enabled video decoders and encoders mistakenly counts the help legend's "=" symbol as a codec name (match.group(1) at container/deps/vllm/validate_ffmpeg_provider.py:76) before comparing against the allowed set, so the exact-match comparison never succeeds and the guard rejects even a correctly built image.

Impact: The CUDA runtime image can never pass the final validation step, so the build aborts even when FFmpeg is built exactly as required.

How the legend rows are parsed as codecs

ffmpeg -hide_banner -decoders (and -encoders) prints a legend block before the actual component list, e.g.:

 V..... = Video
 .F.... = Frame-level multithreading
 ...X.. = Codec is experimental
 ------
 V....D vp8    On2 VP8

ffmpeg_components at container/deps/vllm/validate_ffmpeg_provider.py:70-77 strips each line and applies COMPONENT_RE = ^[A-Z.]{6}\s+(\S+) (:43). For a legend row like V..... = Video, the first six characters V..... match [A-Z.]{6}, \s+ consumes the space, and (\S+) captures =. Every legend row yields the same = token, so the returned set becomes {"=", "vp8", "vp9", "rawvideo"}.

In main() at :156-168, decoders != ALLOWED_DECODERS (and the encoder equivalent) then evaluates to true because of the stray =, raising RuntimeError and failing the RUN step regardless of the real decoder/encoder set. The separator line ------ is correctly ignored (dashes are not in [A-Z.]), but the legend rows are not.

A fix should ignore the legend — e.g. only collect component names after the ------ separator, or discard matches whose captured name is =.

Prompt for agents
In ffmpeg_components (container/deps/vllm/validate_ffmpeg_provider.py:70-77), the parser incorrectly treats the ffmpeg -decoders/-encoders help legend as codec entries. The legend block lists flag descriptions like ` V..... = Video`, ` .F.... = Frame-level multithreading`, etc. before a `------` separator, followed by the real component rows like ` V....D vp8 ...`. COMPONENT_RE (`^[A-Z.]{6}\s+(\S+)`) matches the legend rows too, capturing `=` as the component name. This makes the returned set contain `=`, so the strict equality checks in main() (decoders != ALLOWED_DECODERS and encoders != ALLOWED_ENCODERS) always fail and raise RuntimeError, breaking the build even for a correctly configured FFmpeg. Fix by only collecting component names that appear after the `------` separator line, or by discarding any captured name equal to `=`.
Open in Devin Review

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



def unexpected_media_artifacts() -> list[Path]:
artifacts = []
for root in SEARCH_ROOTS:
for directory, _, filenames in os.walk(root):
directory_path = Path(directory)
if directory_path.is_relative_to(Path("/usr/local/src/ffmpeg")):
continue
for filename in filenames:
path = directory_path / filename
if MEDIA_LIBRARY_RE.match(filename):
if not path.resolve().is_relative_to(ALLOWED_LIBRARY_ROOT):
artifacts.append(path)
elif MEDIA_BINARY_RE.match(filename) and os.access(path, os.X_OK):
if path.resolve() != FFMPEG:
artifacts.append(path)
return artifacts


def extension_libraries(module_name: str, pattern: str = "*.so") -> list[Path]:
module = importlib.import_module(module_name)
module_file = Path(module.__file__).resolve()
package_root = module_file if module_file.suffix == ".so" else module_file.parent
if package_root.is_file():
return [package_root]
return sorted(package_root.rglob(pattern))


def assert_links_to_dynamo_ffmpeg(module_name: str, pattern: str = "*.so") -> None:
extensions = extension_libraries(module_name, pattern)
if not extensions:
raise RuntimeError(f"{module_name} contains no extension libraries to inspect")

resolved_media_libraries = set()
for extension in extensions:
for line in run("ldd", str(extension)).splitlines():
if not re.search(r"lib(?:av|sw)", line):
continue
if "not found" in line:
raise RuntimeError(
f"unresolved FFmpeg dependency for {extension}: {line}"
)
match = re.search(r"=>\s+(/\S+)", line)
if match is None:
continue
resolved = Path(match.group(1)).resolve()
resolved_media_libraries.add(resolved)
if not resolved.is_relative_to(ALLOWED_LIBRARY_ROOT):

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

The validator trusts any FFmpeg library under /usr/local/lib, so private wheel-bundled libraries in /usr/local/lib/python.../site-packages/*.libs pass both the artifact scan and ldd checks instead of being rejected. Fix: only allow resolved FFmpeg libraries whose parent is exactly /usr/local/lib, and use that stricter helper in both unexpected_media_artifacts and assert_links_to_dynamo_ffmpeg.

🤖 AI Fix

In container/deps/vllm/validate_ffmpeg_provider.py, add an is_dynamo_ffmpeg_library(path: Path) -> bool helper that returns path.resolve().parent == ALLOWED_LIBRARY_ROOT, replace the is_relative_to(ALLOWED_LIBRARY_ROOT) checks in unexpected_media_artifacts and assert_links_to_dynamo_ffmpeg with is_dynamo_ffmpeg_library(...), and keep raising for any matched media library under site-packages.

raise RuntimeError(
f"{extension} loads FFmpeg outside {ALLOWED_LIBRARY_ROOT}: {resolved}"
)

if not resolved_media_libraries:
raise RuntimeError(f"{module_name} does not link to Dynamo's FFmpeg libraries")


def main() -> None:
if not FFMPEG.is_file():
raise RuntimeError(f"missing Dynamo FFmpeg executable: {FFMPEG}")

expected_version = os.environ["EXPECTED_FFMPEG_VERSION"]
first_version_line = run(str(FFMPEG), "-version").splitlines()[0]
if not first_version_line.startswith(f"ffmpeg version {expected_version}"):
raise RuntimeError(
f"expected FFmpeg {expected_version}, found: {first_version_line}"
)
Comment on lines +142 to +147

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 | 🟠 Major | ⚡ Quick win

Compare the FFmpeg version token exactly.

startswith() accepts unintended versions such as 8.1.20 when 8.1.2 is expected. Parse the version token and compare it for equality.

Proposed fix
     first_version_line = run(str(FFMPEG), "-version").splitlines()[0]
-    if not first_version_line.startswith(f"ffmpeg version {expected_version}"):
+    match = re.match(r"^ffmpeg version (\S+)", first_version_line)
+    if match is None or match.group(1) != expected_version:
         raise RuntimeError(
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
expected_version = os.environ["EXPECTED_FFMPEG_VERSION"]
first_version_line = run(str(FFMPEG), "-version").splitlines()[0]
if not first_version_line.startswith(f"ffmpeg version {expected_version}"):
raise RuntimeError(
f"expected FFmpeg {expected_version}, found: {first_version_line}"
)
first_version_line = run(str(FFMPEG), "-version").splitlines()[0]
match = re.match(r"^ffmpeg version (\S+)", first_version_line)
if match is None or match.group(1) != expected_version:
raise RuntimeError(
f"expected FFmpeg {expected_version}, found: {first_version_line}"
)
🤖 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 `@container/deps/vllm/validate_ffmpeg_provider.py` around lines 138 - 143,
Update the FFmpeg validation around expected_version and first_version_line to
extract the reported version token from the `ffmpeg -version` output and compare
it for exact equality with `expected_version. Preserve the existing RuntimeError
and diagnostic output for mismatches, while rejecting version prefixes such as
`8.1.20` when `8.1.2` is expected.


build_configuration = run(str(FFMPEG), "-buildconf")
missing_configuration = REQUIRED_BUILD_CONFIGURATION - set(
build_configuration.split()
)
if missing_configuration:
raise RuntimeError(
"FFmpeg is missing required allowlist configuration: "
+ ", ".join(sorted(missing_configuration))
)

decoders = ffmpeg_components("decoders")
if decoders != ALLOWED_DECODERS:
raise RuntimeError(
f"unexpected FFmpeg decoder set: expected {sorted(ALLOWED_DECODERS)}, "
f"found {sorted(decoders)}"
)

encoders = ffmpeg_components("encoders")
if encoders != ALLOWED_ENCODERS:
raise RuntimeError(
f"unexpected FFmpeg encoder set: expected {sorted(ALLOWED_ENCODERS)}, "
f"found {sorted(encoders)}"
)

distributions = installed_distributions()
unexpected_distributions = FORBIDDEN_DISTRIBUTIONS & distributions.keys()
if unexpected_distributions:
raise RuntimeError(
"codec-bearing upstream distributions remain installed: "
+ ", ".join(sorted(unexpected_distributions))
)
for required_distribution in ("av", "opencv-python", "torchcodec"):
if required_distribution not in distributions:
raise RuntimeError(
f"required distribution is missing: {required_distribution}"
)

unexpected_artifacts = unexpected_media_artifacts()
if unexpected_artifacts:
raise RuntimeError(
"FFmpeg or prohibited codec artifacts remain outside Dynamo's "
"installation:\n"
+ "\n".join(str(path) for path in sorted(unexpected_artifacts))
)

for module_name in ("av", "cv2"):
assert_links_to_dynamo_ffmpeg(module_name)
ffmpeg_major = expected_version.partition(".")[0]
assert_links_to_dynamo_ffmpeg("torchcodec", f"libtorchcodec_core{ffmpeg_major}.so")

Comment on lines +197 to +198

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.

🔍 torchcodec must ship a core lib for the exact FFmpeg major

assert_links_to_dynamo_ffmpeg("torchcodec", f"libtorchcodec_core{ffmpeg_major}.so") at container/deps/vllm/validate_ffmpeg_provider.py:193-194 derives the major from EXPECTED_FFMPEG_VERSION (e.g. 8 for 8.1.2) and requires a file named libtorchcodec_core8.so. torchcodec ships versioned core libraries per supported FFmpeg major (core4/5/6/7/...). If the pinned torchcodec build does not include a core8 library, extension_libraries returns empty and the guard raises RuntimeError("...contains no extension libraries to inspect"). This depends on the torchcodec version bundled by the base image supporting FFmpeg 8 — worth confirming before the guard is enabled with #11628.

Open in Devin Review

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

imageio_ffmpeg = importlib.import_module("imageio_ffmpeg")
selected_ffmpeg = Path(imageio_ffmpeg.get_ffmpeg_exe()).resolve()
if selected_ffmpeg != FFMPEG:
raise RuntimeError(
f"imageio-ffmpeg selected {selected_ffmpeg}, expected {FFMPEG}"
)

print(
f"validated FFmpeg {expected_version}: one provider, "
f"decoders={sorted(decoders)}, encoders={sorted(encoders)}"
)


if __name__ == "__main__":
main()
Loading
Loading