- Overview
- Documentation Map
- Notices
- Common Installation: MTL and DPDK
- Dependencies for dvledtx (TX)
- Dependencies for dvledrx (RX)
- Verify the Installation
- Build
- Host Setup
- Run
- Running Unit Tests
- Troubleshooting
- Contributing
- License
The Direct View LED Software Toolkit drives direct-view LED walls with uncompressed video over IP using SMPTE ST 2110-20. It is built on FFmpeg and the Media Transport Library (MTL) and contains two applications:
| Application | Role | Details |
|---|---|---|
| dvledtx | Transmitter. Decodes a video file (or captures an X11 screen), optionally scales it, splits it into crop regions and sends each region as an independent ST 2110-20 stream. | dvledtx/README.md |
| dvledrx | Receiver. Receives one or more ST 2110-20 streams and renders them on X11 displays — one window, a composite of all streams, or one window per physical output (HDMI/DP). | dvledrx/README.md |
Use case — LED video walls: drive multiple LED panels from a single source, with each panel (or LED controller) receiving its own cropped portion of the frame.
| Component | Requirement |
|---|---|
| OS | Ubuntu 22.04 LTS and Ubuntu 24.04 LTS |
| CPU | Intel 12th Gen Core processors or above, including Intel Core Ultra Series 3 |
| NIC | Intel Ethernet Controller I225 or above (I225, I226, E610 and E810 series) |
| Memory | Hugepages configured (typically 2 GB+) |
| Kernel | IOMMU and VFIO support enabled |
| Display (RX only) | An X11 (Xorg) session; Wayland is not supported |
dvledtx and dvledrx follow a layered security model. The applications validate and bound their inputs — including configuration parsing, log file destinations, decoder behaviour and resource limits — and are covered by static analysis, AFL/libFuzzer fuzzing and dependency CVE tracking in CI. Transport-level security is a separate layer: SMPTE ST 2110-20 and the PTP timing it depends on do not define authentication, encryption or integrity protection, which is a property of the standards common to all ST 2110 equipment. The deployment architecture supplies that layer.
We recommend that all hosts in a deployment are air-gapped. The TX host, a dedicated L2 switch and the receivers sit inside a locked cabinet with no network path to any other network and no in-band administrative access — no SSH, no management NIC, no remote console, BMC disabled — with administration by physical KVM inside the cabinet. Because "isolated" and "restricted" are used loosely, note that a VLAN on a shared switch, a firewall or a jump host does not meet this definition.
We also recommend hardening the dedicated switch, since its configuration carries the boundary: disable and blackhole every unused port, enable port security (802.1X or sticky MAC) with a shutdown violation action, disable dynamic trunking, and enable IGMP snooping, storm control, BPDU guard and DHCP snooping.
See Deployment Assumptions and Security Model for the trust boundary, deployment assumptions, switch hardening recommendations, controlled ingress of configuration and video content, the controls the toolkit implements, the residual risk, and a commissioning validation checklist.
| Topic | Document |
|---|---|
| Common installation (TX and RX) | Common Installation: MTL and DPDK |
| TX dependencies: screen capture, VA-API, FFmpeg with the MTL plugin | Dependencies for dvledtx (TX) |
| RX dependencies | Dependencies for dvledrx (RX) |
| Verify the installation | Verify the Installation |
| Build both applications | Build |
| Host setup (TX and RX) | Host Setup |
| Run (TX and RX) | Run |
| Transmitter: JSON configuration, formats, scaling, PTP, VA-API decode, screen capture, logging | dvledtx/README.md |
| Receiver: JSON configuration, display modes, multi-monitor walls, PTP, logging | dvledrx/README.md |
| Deployment assumptions and security model | docs/assumptions.md |
| Reporting security issues | Security.md |
FFmpeg is an open source project licensed under LGPL and GPL. See https://www.ffmpeg.org/legal.html. You are solely responsible for determining if your use of FFmpeg requires any additional licenses. Intel is not responsible for obtaining any such licenses, nor liable for any licensing fees due, in connection with your use of FFmpeg.
Both applications are built on the Media Transport Library (MTL) and DPDK. Install them on every TX and RX host by following MTL's own guides at the pinned commit.
| Dependency | Version | Source |
|---|---|---|
| Media Transport Library | MTL 26.01+, commit 7bee2423 (26.01.0.DEV) |
MTL build guide |
| DPDK | 26.03 (DPDK_VER) with the MTL patches |
versions.env at the pinned commit |
These are the versions CI builds against. Supported OS: Ubuntu 22.04 and 24.04 LTS.
Clone MTL at the pinned commit:
git clone https://github.com/OpenVisualCloud/Media-Transport-Library.git
cd Media-Transport-Library
git checkout 7bee24231c864d12da6db2c2ffbfd3986717af5a
export mtl_source_code=${PWD}
cd ..Then follow the MTL build guide at that commit:
DPDK version: use the
DPDK_VERfromversions.env(26.03) and the matching${mtl_source_code}/patches/dpdk/26.03/patches. The example commands in the guide at this commit still show 25.11.
dvledtx decodes and transmits through FFmpeg with the MTL plugin. Install the packages below
before building FFmpeg: FFmpeg's ./configure only enables optional features whose packages are
already installed.
Needed for input_mode: screen_capture. FFmpeg's x11grab device is only compiled in when these
packages are present.
sudo apt-get install -y libxcb1-dev libxcb-shm0-dev libxcb-xfixes0-devNeeded only for "hwaccel": true. Also add --enable-vaapi to FFmpeg's ./configure line.
sudo apt-get install -y libva-dev vainfo intel-media-va-driver-non-freeFollow the
MTL FFmpeg plugin build guide
at the pinned commit. It builds FFmpeg 7.0 (FFMPEG_VERSION in
versions.env).
- 12-bit formats (
yuv422p12le,yuv444p12le,gbrp12le) need MTL and FFmpeg built from the same pinned commit. The plugin sources are compiled into FFmpeg'slibavdevice, so rebuilding MTL alone is not enough. - Up to 8 NICs on the default dvledtx FFmpeg TX path rely on the
p2_port..p7_portAVOptions present in the plugin at the pinned commit.
| Packages | Used for |
|---|---|
libxcb1-dev, libxcb-shm0-dev, libxcb-xfixes0-dev |
FFmpeg x11grab, for input_mode: screen_capture |
libva-dev, vainfo, intel-media-va-driver-non-free (optional) |
VA-API hardware decode, for "hwaccel": true |
| FFmpeg 7.0 with the MTL plugin | Decoding, scaling and ST 2110-20 transmission |
dvledrx receives directly through MTL and renders with X11; it does not use FFmpeg.
sudo apt-get install -y libx11-dev libxext-dev libxrandr-dev zlib1g-dev| Packages | Used for |
|---|---|
libx11-dev, libxext-dev, libxrandr-dev |
X11 rendering, MIT-SHM and XRandR output placement |
zlib1g-dev |
Log file compression |
dvledrx also uses libjson-c-dev, which is already installed with the MTL prerequisites.
Note:
scripts/build.shbuilds both applications from one Meson project, which checks for the FFmpeg libraries. To build on an RX-only host, FFmpeg must still be installed. The MTL plugin and the optional TX packages are not needed.
# Common: MTL and DPDK
pkg-config --modversion libdpdk # expected: 26.03.x
pkg-config --modversion mtl # expected: 26.01.0.DEV
ls /usr/local/include/mtl/mtl_api.h # MTL headers present
ldconfig -p | grep libmtl # libmtl.so => /usr/local/lib/x86_64-linux-gnu/libmtl.so
# dvledtx: FFmpeg with the MTL plugin
pkg-config --modversion libavformat # FFmpeg 7.0 (libavformat 61.x)
ffmpeg -hide_banner -muxers | grep mtl_st20p # MTL plugin present
ffmpeg -hide_banner -devices | grep x11grab # screen capture
ffmpeg -hide_banner -hwaccels | grep vaapi # optional: hardware decode
# dvledrx: X11 libraries
pkg-config --modversion x11 xext xrandrWith the dependencies installed, clone the repository and build both applications:
git clone https://github.com/OpenVisualCloud/directview-led-software-toolkit
cd directview-led-software-toolkit
bash scripts/build.shThe script wipes and recreates build/, then produces:
| Binary | Path |
|---|---|
| Transmitter | build/dvledtx/dvledtx |
| Receiver | build/dvledrx/dvledrx |
Extra Meson options are passed straight through. The only project option selects the dvledtx TX path (it does not affect dvledrx):
| Option | Default | Description |
|---|---|---|
enable_mtl_tx |
false |
false: transmit through FFmpeg's mtl_st20p muxer. true: transmit with MTL's pipeline API (st20p_tx_*) directly. |
bash scripts/build.sh -Denable_mtl_tx=trueManual equivalent:
meson setup build [-Denable_mtl_tx=true]
ninja -C buildThese steps are required on both the TX and RX hosts. They only need to be done once (hugepages and NIC binding must be repeated after each reboot unless made persistent).
MTL requires IOMMU and VFIO. Ensure these parameters are present in GRUB_CMDLINE_LINUX_DEFAULT
in /etc/default/grub:
intel_iommu=on iommu=pt pcie_aspm=off pcie_port_pm=off vfio-pci.disable_idle_d3=1
For example:
sudo nano /etc/default/grub
# GRUB_CMDLINE_LINUX_DEFAULT="quiet splash intel_iommu=on iommu=pt pcie_aspm=off pcie_port_pm=off vfio-pci.disable_idle_d3=1"
sudo update-grub
sudo rebootVerify after reboot:
cat /proc/cmdline | grep intel_iommu
dmesg | grep -i iommu # should show "DMAR: IOMMU enabled"MTL accesses the NIC through VFIO; the user running the applications must belong to the vfio
group:
sudo groupadd vfio # if it does not already exist
sudo usermod -aG vfio $USER
newgrp vfio # or log out and back in
id -nG $USER # verifyFollow the MTL run guide for the pinned commit:
- DPDK PMD setup — bind the NIC (or create and bind VFs) to
vfio-pci. - Hugepage setup.
Quick reference:
# Find the NIC PCI address (use the BDF form "0000:06:00.0" in the JSON configs)
lspci | grep Ethernet
# Either bind a PF to vfio-pci (TX or RX)
sudo ${mtl_source_code}/script/nicctl.sh bind_pmd 0000:06:00.0
# Or use VFs:
# TX host: plain VFs are enough
sudo ${mtl_source_code}/script/nicctl.sh create_vf 0000:06:00.0
# RX host: trusted VFs plus a promiscuous PF (see dvledrx/README.md, Virtual Functions)
sudo ${mtl_source_code}/script/nicctl.sh create_tvf 0000:06:00.0 <num_vfs>
sudo ip link set <pf_iface> promisc on
dpdk-devbind.py --status # target NIC/VFs should show drv=vfio-pci
# Allocate hugepages
sudo sysctl -w vm.nr_hugepages=2048
grep Huge /proc/meminfoVF settings, PF promiscuous mode and hugepages are lost at reboot. Re-apply them at every boot, for example from a startup script or systemd unit. For RX VF details see dvledrx — Virtual Functions.
The applications run as a normal user, so DPDK needs a high enough locked-memory limit
(RLIMIT_MEMLOCK). If it is too low, start-up fails with:
EAL: Cannot set up DMA remapping, error 12 (Cannot allocate memory)
Raise the limit as described in the
MTL run guide: notes for non-root run,
for example by adding these lines to /etc/security/limits.conf and rebooting:
<user> hard memlock unlimited
<user> soft memlock unlimited
Check with ulimit -l; it should print unlimited.
Both applications are configured entirely from a JSON file and share the same command line.
Before running, make sure the host setup is complete and edit the sample config so
interfaces[].name matches your NIC PCI address and the IP addresses match your network.
| Option | Description |
|---|---|
-C, --config <file> |
JSON configuration file (required) |
-t, --test-time <secs> |
Run for N seconds (1..86400), then exit cleanly |
-v, --version |
Show the version and exit |
--help |
Show usage |
./build/dvledtx/dvledtx --config dvledtx/config/tx_fullhd_single_session.jsonSet video.tx_url in the config to your source video file (or use input_mode: screen_capture). Configuration reference:
dvledtx/README.md — JSON Configuration.
dvledrx renders through X11, so it needs a running Xorg session and DISPLAY set:
export DISPLAY=:0
./build/dvledrx/dvledrx --config dvledrx/config/rx_fullhd_1session.jsonConfiguration reference: dvledrx/README.md — JSON Configuration.
A TX session and the RX session that receives it must agree on:
| Must match | TX config | RX config |
|---|---|---|
| Multicast group | interfaces[].dip |
interfaces[].dip |
| UDP port | tx_sessions[].udp_port |
rx_sessions[].udp_port |
| RTP payload type | tx_sessions[].payload_type |
rx_sessions[].payload_type |
| Pixel format | tx_video.fmt |
video.fmt |
| Frame rate | tx_video.fps |
video.fps |
| Stream size | tx_sessions[].crop.w × crop.h |
rx_sessions[].crop.w × crop.h |
For example, a Full HD frame split into three 640×1080 strips:
# TX host
./build/dvledtx/dvledtx --config dvledtx/config/tx_fullhd_multi_session.json
# RX host (composite window of the three strips)
export DISPLAY=:0
./build/dvledrx/dvledrx --config dvledrx/config/rx_crop_3sessions.jsonThe shipped samples are independent examples: before pairing them, align the multicast dip
and fmt values (here tx_fullhd_multi_session.json uses 239.168.85.21 / yuv422p10le and
rx_crop_3sessions.json uses 239.168.85.20 / gbrp12le).
Press Ctrl+C (or send SIGTERM) for a clean shutdown; --test-time stops automatically. Closing
a dvledrx window also stops the receiver. If an application becomes unresponsive:
pkill -9 -f dvledtx
pkill -9 -f dvledrxsudo apt install -y libcmocka-dev xvfb xauth
pip install gcovr
bash scripts/test.sh # TX + RX unit tests with a coverage report
bash scripts/test.sh --no-coverage # tests onlyThe script builds into build-tests/ and runs every test with meson test. dvledrx X11/session
tests run under xvfb-run. dvledtx screen-capture success tests run only when a live X display is
detected (or DVLED_TEST_DISPLAY is set to an x11grab source such as :99.0+0,0).
Application-specific issues are covered in dvledtx/README.md and dvledrx/README.md. Common issues:
-
MTL initialization failed
- Check that the PCI address in the config matches your hardware:
lspci | grep Ethernet # Example output: 06:00.0 Ethernet controller: Intel Corporation ... # Use BDF format in config: "0000:06:00.0"
- Check MTL, DPDK and FFmpeg are installed at the expected versions — see Verify the Installation.
- Verify the NIC is bound to VFIO:
sudo ${mtl_source_code}/script/nicctl.sh list all # Or use dpdk-devbind.py: dpdk-devbind.py --status # The target NIC should show "drv=vfio-pci"
- Ensure IOMMU is enabled — see IOMMU / VFIO Kernel Parameters.
- Verify the network card is supported (Intel I225, I226, E610, E810 series).
- Check that the PCI address in the config matches your hardware:
-
Hugepage allocation failure / transmission issues
grep Huge /proc/meminfo # HugePages_Free should be > 0 sudo sysctl -w vm.nr_hugepages=2048 # make persistent in /etc/sysctl.conf
Also check the NIC has enough bandwidth (uncompressed 1080p30 4:2:2 10-bit ≈ 1.3 Gb/s per stream, more for 4:4:4 / 12-bit).
-
Permission denied (VFIO)
- Ensure the user is in the
vfiogroup — see VFIO Group Setup. - Check
ls -la /dev/vfio/— group should bevfiowith rw permissions.
- Ensure the user is in the
-
pip installfails withexternally-managed-environment(Ubuntu 24.04, PEP 668)- See the note in the
MTL build guide,
or install with
pip install --break-system-packages <package>.
- See the note in the
MTL build guide,
or install with
-
FFmpeg
./configurefails withnasm/yasm not found or too oldsudo apt install -y nasm
Then re-run
./configure. -
Build fails with
Dependency "mtl" not foundor missinglibav*- Make sure
/usr/localis on the pkg-config path:export PKG_CONFIG_PATH=/usr/local/lib/pkgconfig:/usr/local/lib/x86_64-linux-gnu/pkgconfig:$PKG_CONFIG_PATH
- Make sure
Contributions are welcome. Please open an issue or submit a pull request on GitHub.
This project is licensed under the BSD 3-Clause License. See LICENSE for details.
