-
Notifications
You must be signed in to change notification settings - Fork 1.5k
feat(media): actionable errors for unsupported video/audio codecs (OPS-7779) #12725
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
dmitry-tokarev-nv
merged 15 commits into
main
from
dtokarev/ops-7779-actionable-errors-for-unsupported-videoaudio-codecs
Aug 8, 2026
Merged
Changes from all commits
Commits
Show all changes
15 commits
Select commit
Hold shift + click to select a range
9f27b15
feat(media): actionable errors for unsupported video/audio codecs
dmitry-tokarev-nv 28cb337
merge: bring in merged #12051 (install_media_decoders + VALIDATED_SPECS)
dmitry-tokarev-nv a70d233
test(media): scope the no-implicit-install sweep to entrypoints
dmitry-tokarev-nv 4ef91aa
fix(media): address the review round on the actionable-error PR
dmitry-tokarev-nv 5fbce17
fix(sglang): preflight the NVDEC-disabled path too
dmitry-tokarev-nv 3761381
Merge remote-tracking branch 'origin/main' into dtokarev/ops-7779-act…
dmitry-tokarev-nv ef42823
test(sglang): hoist importlib to module scope per review
dmitry-tokarev-nv 1df5df6
Merge branch 'main' into dtokarev/ops-7779-actionable-errors-for-unsu…
nv-tusharma 81976cf
fix(media): keep media payloads out of errors; validate URLs before d…
dmitry-tokarev-nv a72b2ce
Merge branch 'dtokarev/ops-7779-actionable-errors-for-unsupported-vid…
dmitry-tokarev-nv 6eb057d
Merge remote-tracking branch 'origin/main' into dtokarev/ops-7779-act…
dmitry-tokarev-nv 35cdfa5
test(sglang): allow internal URLs in the gated video E/PD serve test
dmitry-tokarev-nv 64c646b
docs: state the media URL policy on the backend multimodal pages
dmitry-tokarev-nv a07d009
test(sglang): keep the cache unit tests off the network
dmitry-tokarev-nv bb939bd
test(sglang): stop the cache unit tests reaching the network
dmitry-tokarev-nv File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
112 changes: 112 additions & 0 deletions
112
components/src/dynamo/common/multimodal/codec_errors.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,112 @@ | ||
| # SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. | ||
| # SPDX-License-Identifier: Apache-2.0 | ||
|
|
||
| """Actionable errors for media the shipped images cannot decode. | ||
|
|
||
| The runtime images deliberately omit the software media decoders (OpenCV, | ||
| PyAV, decord) and route H.264/H.265 video to NVDEC hardware decode instead. | ||
| Any other input codec needs one of those Python packages, so without an | ||
| explicit install the failure surfaces as a bare ``ModuleNotFoundError`` from | ||
| deep inside the backend -- no codec, no remedy, and in one observed case the | ||
| whole video payload embedded in the message. The builders here name the codec, | ||
| the missing package at its validated version bounds, the installer command, | ||
| and the hardware alternative, in one place so the three backends cannot drift. | ||
|
|
||
| The version bounds come from | ||
| :mod:`dynamo.common.utils.install_media_decoders` (the explicit installer); | ||
| importing its constants here is deliberate single-sourcing -- nothing in this | ||
| module installs anything. | ||
| """ | ||
|
|
||
| from __future__ import annotations | ||
|
|
||
| from dynamo.common.multimodal.nvdec_decoder import HW_ROUTED_CODECS, nvdec_available | ||
| from dynamo.common.utils.install_media_decoders import VALIDATED_SPECS | ||
|
|
||
| INSTALLER_CMD = "python -m dynamo.common.utils.install_media_decoders" | ||
|
|
||
|
|
||
| class MissingMediaDecoderError(RuntimeError): | ||
| """A media request needs a decoder package the image does not ship. | ||
|
|
||
| Deliberately not a ``ValueError``: the input may be perfectly valid media. | ||
| The gap is deployment configuration, so handlers that map ``ValueError`` | ||
| to a client 4xx should not blame the request for it. | ||
| """ | ||
|
|
||
|
|
||
| def _install_hint(backend: str, package: str) -> str: | ||
| spec = VALIDATED_SPECS[package] | ||
| return ( | ||
| f"install the validated decoder with `pip install --no-deps '{spec}'` " | ||
| f"(or `{INSTALLER_CMD} {backend}`)" | ||
| ) | ||
|
|
||
|
|
||
| def _with_cause(message: str, cause: str | None) -> str: | ||
| """Append the underlying decoder text so diagnostics survive the wrap. | ||
|
|
||
| ``raise ... from exc`` preserves the cause for tracebacks, but handlers | ||
| that ship only ``str(exc)`` to the client (HTTP error bodies) would drop | ||
| it -- and the underlying reason is part of this error's contract. | ||
| """ | ||
| if cause: | ||
| return f"{message} (decoder reported: {cause})" | ||
| return message | ||
|
|
||
|
|
||
| def video_decoder_missing( | ||
| backend: str, | ||
| package: str, | ||
| module: str, | ||
| codec: str | None, | ||
| cause: str | None = None, | ||
| ) -> MissingMediaDecoderError: | ||
| """Build the error for a video whose decode path has no decoder. | ||
|
|
||
| Two distinct situations produce it, and the remedy differs: | ||
|
|
||
| * ``codec`` is H.264/H.265 but NVDEC is unavailable in this container -- | ||
| the primary fix is granting the ``video`` driver capability, not | ||
| installing software. | ||
| * any other codec -- NVDEC never decodes it, so the fix is the software | ||
| decoder install or re-encoding the input to H.264/H.265. | ||
| """ | ||
| codec_desc = f"codec '{codec}'" if codec else "an undetected codec" | ||
| if codec in HW_ROUTED_CODECS and not nvdec_available(): | ||
| lead = ( | ||
| f"this video ({codec_desc}) normally decodes in hardware via NVDEC, " | ||
| "but NVDEC is unavailable in this container. Grant the 'video' " | ||
| "driver capability (NVIDIA_DRIVER_CAPABILITIES) to enable it, or " | ||
| ) | ||
| else: | ||
| lead = ( | ||
| f"this video ({codec_desc}) has no decoder in this image: shipped " | ||
| "images decode only H.264/H.265 (in hardware, via NVDEC), and the " | ||
| f"software decoder '{module}' is deliberately not installed. " | ||
| "Re-encode the input to H.264/H.265, or " | ||
| ) | ||
| return MissingMediaDecoderError( | ||
| _with_cause( | ||
| "Cannot decode video: " + lead + _install_hint(backend, package) + ".", | ||
| cause, | ||
| ) | ||
| ) | ||
|
|
||
|
|
||
| def audio_decoder_missing( | ||
| backend: str, cause: str | None = None | ||
| ) -> MissingMediaDecoderError: | ||
| """Build the error for audio input with no decoder in the image. | ||
|
|
||
| NVDEC never decodes audio, so unlike video there is no hardware | ||
| alternative -- the only remedy is the PyAV install. | ||
| """ | ||
| return MissingMediaDecoderError( | ||
| _with_cause( | ||
| "Cannot decode audio: this input needs the PyAV decoder ('av'), which " | ||
| "this image deliberately does not ship, and NVDEC does not decode " | ||
| "audio. To enable audio input, " + _install_hint(backend, "av") + ".", | ||
| cause, | ||
| ) | ||
| ) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.