Skip to content

Latest commit

 

History

1 Commit

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 

Repository files navigation

deepstream-decode Debian package recipe

Builds a single .deb that turns a stock vLLM Docker container into a GPU-accelerated video-decode-capable host.

The build script downloads the NVIDIA DeepStream SDK from NGC, extracts the minimal subset of .so files needed, bundles a pure-Python decoder wheel, and produces a single .deb.

After apt install ./deepstream-decode_<DS_VERSION>-1_amd64.deb, the container has:

  • ~14 NVIDIA DeepStream runtime libraries at /opt/nvidia/deepstream/deepstream-<X.Y>/
  • All required system symlinks (libv4l2.so.0, libv4l plugin alias, GStreamer plugin alias, unversioned deepstream symlink)
  • The full GStreamer 1.x runtime + plugin sets (pulled via apt Depends:)
  • A pure-Python deepstream_decode package installed into the container's active Python interpreter (pip-installed by the postinst)
  • pygobject and pymediainfo PyPI deps installed alongside it

Inside that container, from deepstream_decode import DecodePool works out of the box, and vLLM's modified multimodal/video.py can call into it via VLLM_VIDEO_LOADER_BACKEND=deepstream.

Quick start

# 1. Clone the recipe
git clone https://github.com/<your-org>/deepstream-decode-deb.git
cd deepstream-decode-deb

# 2. Build the .deb (downloads SDK from NGC; ~5 min)
./build.sh

# 3. Install it inside your vLLM container
sudo apt install --no-install-recommends \
    ./dist/deepstream-decode_*.deb

# 4. Verify
deepstream-decode-selftest

Hardware requirements

  • NVIDIA GPU with NVDEC support (H100, H200, A100, L40, RTX 30xx/40xx, etc.)
  • Host NVIDIA driver R570+ (CUDA 13.0+ compatible)
  • For Docker use: NVIDIA Container Toolkit
  • x86_64 Linux

Repository layout

.
├── build.sh                    ← entry-point: download + build orchestrator
├── README.md
├── .gitignore                  ← excludes downloaded tbz2, extract dir, build/
├── python_pkg/                 ← pure-Python wheel source
│   ├── pyproject.toml
│   ├── README.md
│   ├── LICENSE
│   ├── MANIFEST.in
│   └── src/deepstream_decode/
│       ├── __init__.py
│       ├── _ds_dec.py          ← decoder (pipeline, ctypes, NVDEC capture)
│       ├── _runtime.py         ← version-agnostic lib_dir locator
│       ├── _selftest.py        ← `deepstream-decode-selftest` CLI
│       └── py.typed
├── deb/                        ← Debian package recipe
│   ├── control                 ← apt Depends + metadata template
│   ├── postinst.in             ← postinst template (DS_PREFIX placeholder)
│   └── postrm.in               ← postrm template
└── dist/                       ← build output (gitignored)
    └── deepstream-decode_<DS_VERSION>-1_amd64.deb

Build prerequisites

On the build host (Ubuntu 22.04+ recommended):

sudo apt install -y dpkg-dev python3 python3-pip python3-venv tar wget binutils
# Optionally: install uv for faster wheel builds
curl -LsSf https://astral.sh/uv/install.sh | sh

binutils is required so build.sh can run objdump / readelf to auto-detect the bundled CUDA major version.

Network access to api.ngc.nvidia.com is required for the initial tarball download (one-time per DS version).

Get the DeepStream SDK from NGC

build.sh downloads the SDK tarball automatically the first time it runs. The URL it hits:

https://api.ngc.nvidia.com/v2/resources/nvidia/deepstream/versions/${DS_MAJOR_MINOR}/files/deepstream_sdk_v${DS_VERSION}_x86_64.tbz2

For DS 9.0.0 specifically:

https://api.ngc.nvidia.com/v2/resources/nvidia/deepstream/versions/9.0/files/deepstream_sdk_v9.0.0_x86_64.tbz2

Manual download (optional)

If you want to pre-download (behind a proxy, air-gapped build host, or to inspect/cache the tarball before building):

wget --content-disposition \
    "https://api.ngc.nvidia.com/v2/resources/nvidia/deepstream/versions/9.0/files/deepstream_sdk_v9.0.0_x86_64.tbz2" \
    -O deepstream_sdk_v9.0.0_x86_64.tbz2

Drop the resulting .tbz2 in the recipe root (same dir as build.sh) and run ./build.sh --no-download to skip the network step and use your local copy.

NGC catalog page

Browse the SDK and accept the license at:

https://catalog.ngc.nvidia.com/orgs/nvidia/resources/deepstream

By downloading the tarball, you accept NVIDIA's DeepStream SDK License. This recipe does not redistribute the SDK — it only orchestrates the download from NGC and packages a minimal subset of .so files into a local .deb for installation on your own machines.

Build

./build.sh                            # DS_VERSION=9.0.0 (default)
./build.sh --ds-version=9.1.0         # any other major.minor.patch
DS_VERSION=9.1.0 ./build.sh           # same, via env var

What build.sh does:

  1. Downloads deepstream_sdk_v<DS_VERSION>_x86_64.tbz2 from NGC if not already present locally (skipped on subsequent runs).
  2. Extracts it into deepstream_sdk_v<DS_VERSION>_x86_64/.
  3. Probes for libnvbufsurface.so to locate the DS source root inside the extracted tree (handles flat or rooted layouts).
  4. Reads the NPP SONAME from libnvbufsurftransform.so to detect the bundled CUDA major version. The detected value is injected into the .deb filename (+cuda<MAJOR>) and the apt Depends: line (so apt auto-pulls a matching libnpp-X-Y).
  5. Builds the pure-Python wheel from python_pkg/ (using uv build if available, else python -m build).
  6. Stages the .so files + GStreamer plugins + libv4l plugin + C headers + the built wheel into a DEBIAN/ tree at opt/nvidia/deepstream/deepstream-<DS_MAJOR_MINOR>/.
  7. Substitutes @DS_PREFIX@ and @CUDA_MAJOR@ placeholders in the deb/ templates.
  8. Runs dpkg-deb --build to produce dist/deepstream-decode_<DS_VERSION>+cuda<MAJOR>-1_amd64.deb.

Build flags:

./build.sh --no-download     # don't download even if tbz2 missing (fail instead)
./build.sh --no-extract      # reuse existing extracted directory
./build.sh --keep-stage      # don't delete the staging tree (debugging)

Install (target machine)

In the stock vLLM container (vllm/vllm-openai or your local equivalent):

# One command — pulls GStreamer + glib + libmediainfo + NPP via apt,
# unpacks DS libs, creates symlinks, pip-installs the Python wheel.
#
# --no-install-recommends keeps the install minimal (~250 MB instead
# of ~1.3 GB; the difference is mostly compiler toolchain and X11 stack
# that the package's Recommends pull in but doesn't need at runtime).
sudo apt install --no-install-recommends \
    ./dist/deepstream-decode_9.0.0+cuda13-1_amd64.deb

# Verify
deepstream-decode-selftest

# Use
python3 -c "from deepstream_decode import DecodePool; print('ok')"

Note about paths: apt interprets a / in the argument as package/release. To pass a local .deb, prefix the path with ./ (or use an absolute path). Without that, you'll see Package 'dist' is not available.

What gets installed by the .deb

File-level

Path Origin
/opt/nvidia/deepstream/deepstream-<X.Y>/lib/*.so (11 files) bundled
/opt/nvidia/deepstream/deepstream-<X.Y>/lib/gst-plugins/*.so (2 files) bundled
/opt/nvidia/deepstream/deepstream-<X.Y>/lib/libv4l/plugins/libcuvidv4l2_plugin.so bundled
/opt/nvidia/deepstream/deepstream-<X.Y>/sources/includes/* bundled (DS C headers)
/usr/share/deepstream-decode/deepstream_decode-*.whl bundled (postinst installs from here)
<active python's site-packages>/deepstream_decode/ created by postinst pip install

Symlinks (postinst)

Link Target
/opt/nvidia/deepstream/deepstream /opt/nvidia/deepstream/deepstream-<X.Y>
/usr/lib/x86_64-linux-gnu/libv4l2.so.0.0.99999 …/lib/libnvv4l2.so
/usr/lib/x86_64-linux-gnu/libv4l2.so.0 …/libv4l2.so.0.0.99999
/usr/lib/x86_64-linux-gnu/libv4l/plugins/libcuvidv4l2_plugin.so …/lib/libv4l/plugins/libcuvidv4l2_plugin.so
/usr/lib/x86_64-linux-gnu/gstreamer-1.0/deepstream …/lib/gst-plugins

Other (postinst)

  • /etc/ld.so.conf.d/deepstream.conf registers the lib dir with the dynamic linker.
  • ldconfig is run.

Apt-pulled

Package Reason
gstreamer1.0-tools + 5 plugin sets + gstreamer1.0-rtsp GStreamer runtime
libglib2.0-0t64 / libglib2.0-0 GLib C runtime
libgirepository-1.0-1 GObject introspection (required by PyGObject)
libcairo2 required by PyGObject's pycairo binding
libmediainfo0v5 the C library pymediainfo wraps
libnpp-<CUDA_MAJOR>-X (alternatives: cuda-libraries-<CUDA_MAJOR>-X) NVIDIA NPP — required by DS plugins
python3 (>= 3.10), python3-pip Python toolchain

PyPI-pulled (by postinst's pip install)

Package Reason
pygobject Python bindings for GStreamer/GObject
pymediainfo Video metadata probe

torch is intentionally not declared as a hard pip dep — the vLLM container already provides it.

How the .deb crosses the system/venv boundary

The postinst runs python3 -m pip install ... (or uv pip install --system ... if uv is on PATH). This uses whichever Python is first on PATH at install time — in a stock vLLM container that's the container's active Python (typically the venv that vLLM itself lives in). The wheel lands in the same site-packages/ as vllm/, so from deepstream_decode import DecodePool resolves through standard sys.path lookup with no PYTHONPATH manipulation.

How version-agnosticism works

  • build.sh takes DS_VERSION=X.Y.Z, derives DS_MAJOR_MINOR=X.Y, and uses both in the URL, filenames, install paths, and .deb version.
  • CUDA major is auto-detected from the bundled libnvbufsurftransform.so by reading its libnppig.so.<N> DT_NEEDED entry. Injected into the .deb filename (+cuda<N>) and into the apt Depends: (alternatives list libnpp-<N>-0 | libnpp-<N>-1 | libnpp-<N>-2 | cuda-libraries-<N>-0 | cuda-libraries-<N>-1).
  • deb/postinst.in is a template with an @DS_PREFIX@ placeholder that build.sh substitutes to /opt/nvidia/deepstream/deepstream-<X.Y> at .deb build time.
  • _runtime.py in the wheel probes for the unversioned /opt/nvidia/deepstream/deepstream/lib symlink first (created by every version's postinst), then falls back to globbing /opt/nvidia/deepstream/deepstream-*/lib for highest version. So the same wheel works against any DS major.minor the .deb installs.

Rebuild / iteration

# Change DS version:
./build.sh --ds-version=9.1.0

# Source code change to _ds_dec.py / _runtime.py / _selftest.py:
./build.sh --no-extract            # skip slow tar extract (reuse extracted dir)

# Inspect the produced .deb before installing:
dpkg-deb -c dist/deepstream-decode_<version>-1_amd64.deb

# Test in a throwaway container:
docker run --rm -it --gpus all -v "$PWD/dist:/dist" vllm/vllm-openai bash
# inside the container:
apt update && apt install --no-install-recommends /dist/deepstream-decode_*.deb
deepstream-decode-selftest

Uninstall

sudo apt remove deepstream-decode      # removes libs, symlinks, ldconfig entry
                                        # + uninstalls the Python wheel
sudo apt autoremove                    # remove transitively-pulled apt deps
                                        # (GStreamer plugin sets, glib, etc.)

Troubleshooting

Package 'dist' is not available when running apt install

apt is interpreting dist/... as a package/release argument, not a local file path. Prefix with ./:

sudo apt install ./dist/deepstream-decode_*.deb

ImportError: libnppig.so.13: cannot open shared object file

The vLLM container is based on a stripped CUDA image (nvidia/cuda:*-base-*) that lacks NPP. The .deb declares NPP as a dependency, but apt install may have missed it if --no-install-recommends was used without apt-get update first. Force it:

sudo apt-get update
sudo apt install -y --no-install-recommends libnpp-13-1   # or libnpp-13-0
sudo ldconfig
deepstream-decode-selftest

externally-managed-environment error during postinst

The vLLM container's Python has PEP 668 enabled (Ubuntu 24.04+). The postinst is designed to handle this via --break-system-packages. If you see this error, the postinst was interrupted; finish it manually:

sudo python3 -m pip install --break-system-packages --no-deps \
    /usr/share/deepstream-decode/deepstream_decode-*.whl
sudo python3 -m pip install --break-system-packages pygobject pymediainfo
sudo dpkg --configure deepstream-decode

Build fails on the NGC download

Either the tarball name doesn't exist for the requested DS_VERSION on NGC, or NGC has rate-limited you. The script prints the exact URL it tries. Hit it directly with wget to see what NGC returns. If NGC ever requires authentication for DeepStream downloads, pre-download the tarball manually (see "Manual download" above) and re-run with ./build.sh --no-download.

Selftest reports inside a venv at: but the wheel doesn't import

The wheel was pip-installed into a different Python than your shell is using. Check which python3 matches the interpreter that ran the postinst's pip install. The simplest fix is to rerun the postinst:

sudo dpkg --configure deepstream-decode

_C.abi3.so symbol unresolved at vLLM startup

This is a vLLM / CUDA compatibility issue, not a deepstream-decode one. Your vLLM image was likely compiled for a different CUDA major than the one the .deb was built against. Confirm both use the same CUDA major:

# Inside the container
ldd /usr/local/lib/python3.12/dist-packages/vllm/_C.abi3.so | grep cudart
# vs
ldd /opt/nvidia/deepstream/deepstream/lib/libnvbufsurface.so | grep cudart

Both should point at the same libcudart.so.<MAJOR>.

License

  • Recipe + Python decoder code: Apache-2.0 (see python_pkg/LICENSE).
  • Bundled DeepStream .so files (assembled into the .deb at build time): NVIDIA proprietary, distribution restricted by NVIDIA's DeepStream SDK License. The .deb produced by this recipe should be distributed only within the scope NVIDIA's license permits. This repo does not redistribute any NVIDIA-proprietary files.

About

Recipe to build a Debian package for NVIDIA DeepStream-backed video decode

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages