Skip to content

OpenVisualCloud/directview-led-software-toolkit

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

43 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Direct View LED Software Toolkit

License CI Coverity Scan Build Status Ubuntu MTL OpenSSF Scorecard

Table of Contents

Overview

Direct View LED Software Toolkit is a simplified, standalone transmitter application using FFmpeg APIs and Media Transport Library (MTL) plugin.

What It Does

dvledtx reads a video source file (e.g., MP4), decodes it using FFmpeg, and transmits uncompressed video frames over an IP network using the SMPTE ST 2110-20 standard. Multiple crop regions of the same source video can be transmitted simultaneously as independent RTP streams — enabling tiled LED wall configurations where each panel receives its own portion of the frame.

Use Case

  • LED Video Walls: Drive multiple LED panels from a single source, each panel receiving a cropped region of the full frame.

Architecture

dvledtx Architecture

System Requirements

Component Requirement
OS Validated on Ubuntu 22.04 LTS; should work on Ubuntu 24.04 and higher
CPU Intel 12th Generation (Alder Lake) or above
NIC Intel Ethernet Controller I225 or above
Memory Hugepages configured (typically 2GB+)
Kernel IOMMU and VFIO support enabled

Notices

FFmpeg

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.

Features

  • ST20P Video Transmission: Uncompressed video over SMPTE ST 2110-20
  • Multi-session Support: Multiple concurrent video streams
  • JSON Configuration: Per-session crop and network settings via JSON config
  • Memory Efficient: Uses hugepages for optimal performance

Building

Prerequisites

Hardware Requirements

  • Intel 12th Generation CPU or above
  • Intel Ethernet Controller I225 or above

Software Requirements

Note: This toolkit has been validated against Ubuntu 22.04 LTS but should work on Ubuntu 24.04 LTS and higher versions.

  • Ubuntu 22.04 or 24.04 LTS
  • Media Transport Library (MTL) v26.01+
    • Follow these steps
  • FFmpeg 7.0 with MTL Plugin
    • Screen capture support (input_mode: screen_capture) requires FFmpeg's x11grab device. It is auto-detected and compiled in by FFmpeg's ./configure script, but only if these packages are installed before building FFmpeg:
      sudo apt-get install -y libx11-dev libxcb1-dev libxcb-shm0-dev libxcb-xfixes0-dev
      After building, verify support with:
      ffmpeg -devices | grep x11grab
      If this prints nothing, FFmpeg needs to be reconfigured/rebuilt after installing the packages above — screen capture will otherwise fail at runtime with x11grab input format not found.
    • x11grab only works against an X11 (Xorg) display, not Wayland — see Ensuring an X11 session below if you're capturing from a machine's own physical desktop session.
    • Headless machines (no physical monitor) additionally need a virtual display to capture from — see Screen capture on a headless machine below, which requires:
      sudo apt-get install -y xserver-xorg-video-dummy ubuntu-desktop

Build Steps

Once all dependencies are installed, clone this repository and run the build script:

git clone https://github.com/OpenVisualCloud/directview-led-software-toolkit
cd directview-led-software-toolkit
bash scripts/build.sh

To build with MTL TX support enabled:

bash scripts/build.sh -Denable_mtl_tx=true

The built binary will be available at build/dvledtx.

Usage

Binding Ethernet Controller to DPDK PMD and Hugepage Setup

JSON Configuration

dvledtx uses a JSON config file with three sections:

Section Field Description
log_file log_file (Optional) Path/name of the log output file (e.g. dvledtx.log). If omitted, logging goes to console only.
interfaces[] name PCI BDF address of the NIC (e.g. 0000:06:00.0)
sip Source IP address
dip Destination multicast IP address
video width Source frame width in pixels
height Source frame height in pixels
input_mode (Optional) Video input mode: file (default) or screen_capture
tx_url Path to the source video file (used when input_mode=file)
screen_input (Optional) x11grab source string for screen capture (used when input_mode=screen_capture, default :0.0+0,0)
tx_video scale_width (Optional) Output width after scaling
scale_height (Optional) Output height after scaling
fps Frames per second (25, 30, 50, 60)
fmt Pixel format (see Supported Formats)
tx_sessions[] nic_index (Optional) Index into interfaces[] selecting which NIC this session uses (default: 0)
udp_port UDP port for the session
payload_type (Optional) RTP payload type — defaults to 96 if not present
crop Region to transmit: x, y, w, h in pixels

Example: MP4 file input (config/tx_fullhd_single_session.json)

{
  "log_file": "dvledtx.log",
  "interfaces": [
    { "name": "0000:03:10.0", "sip": "192.168.50.30", "dip": "239.168.85.20" },
    { "name": "0000:03:10.2", "sip": "192.168.50.29", "dip": "239.168.85.21" }
  ],
  "video": {
    "width": 1920, "height": 1080,
    "tx_url": "bbb_sunflower_1080p_30fps_normal.mp4"
  },
  "tx_video": {
    "scale_width": 1920, "scale_height": 1080,
    "fps": 30,
    "fmt": "yuv422p10le"
  },
  "tx_sessions": [
    { "nic_index": 0, "udp_port": 20000, "payload_type": 96, "crop": { "x": 0,   "y": 0, "w": 960, "h": 1080 } },
    { "nic_index": 1, "udp_port": 20002, "payload_type": 96, "crop": { "x": 960, "y": 0, "w": 960, "h": 1080 } }
  ]
}

Multiple sessions can be defined in tx_sessions to transmit different crop regions of the same video simultaneously (see config/tx_fullhd_multi_session.json).

Ensuring an X11 session (required for screen capture)

x11grab can only capture from a native X11 (Xorg) display — it does not work against a Wayland compositor. This mainly affects machines with a physical monitor logging into their own desktop session: current Ubuntu releases (22.04 and later) default new GDM logins to a Wayland session, which causes screen_capture to fail (or silently capture a blank/incorrect frame) even though FFmpeg was built correctly with x11grab support.

The headless setup below is unaffected by this — it starts a real Xorg server directly (Xorg :99 ...) and forces the desktop session running on top of it with XDG_SESSION_TYPE=x11, so no GDM/Wayland session is ever involved.

To capture from a machine's own physical display, make sure that desktop session is running X11, not Wayland:

  • Per-login (manual): at the GDM login screen, click the gear icon next to the password field and choose "Ubuntu on Xorg" instead of the default "Ubuntu" (Wayland) before signing in.
  • System-wide (automatic): disable Wayland in GDM so every login defaults to X11 — edit /etc/gdm3/custom.conf and uncomment/set WaylandEnable=false under the [daemon] section, or apply it directly:
    sudo cp /etc/gdm3/custom.conf /etc/gdm3/custom.conf.bak
    sudo sed -i 's/^#\?WaylandEnable=.*/WaylandEnable=false/' /etc/gdm3/custom.conf
    grep -q '^WaylandEnable=false' /etc/gdm3/custom.conf || \
      sudo sed -i '/^\[daemon\]/a WaylandEnable=false' /etc/gdm3/custom.conf
    sudo systemctl restart gdm3   # or: sudo reboot
    Restarting gdm3 logs out the current session — save your work first. After logging back in, verify the session type:
    echo $XDG_SESSION_TYPE   # should print: x11

Screen capture on a headless machine (no physical monitor)

x11grab needs a real X11 display to attach to — it does not work against a raw framebuffer or DRM device. On a machine with no monitor connected, create a virtual display using Xorg with the dummy video driver and run a desktop session on it so there's actual content to capture. Unlike Xvfb, a real Xorg server claims physical input devices — your keyboard and mouse work directly on the virtual display.

  1. Install prerequisites (once): see Software Requirements for the xserver-xorg-video-dummy/ubuntu-desktop packages and the x11grab-enabled FFmpeg build.

  2. Create an Xorg config for the dummy driver:

    sudo tee /tmp/xorg-dummy.conf > /dev/null << 'EOF'
    Section "Device"
        Identifier "dummy"
        Driver "dummy"
        VideoRam 256000
    EndSection
    
    Section "Monitor"
        Identifier "monitor"
        HorizSync 28.0-80.0
        VertRefresh 48.0-75.0
        Modeline "1920x1080" 148.50 1920 2008 2052 2200 1080 1084 1089 1125 +hsync +vsync
    EndSection
    
    Section "Screen"
        Identifier "screen"
        Device "dummy"
        Monitor "monitor"
        DefaultDepth 24
        SubSection "Display"
            Depth 24
            Modes "1920x1080"
        EndSubSection
    EndSection
    
    Section "ServerLayout"
        Identifier "layout"
        Screen "screen"
        Option "AllowEmptyInput" "false"
    EndSection
    EOF
  3. Start Xorg on display :99:

    sudo Xorg :99 -config /tmp/xorg-dummy.conf -noreset &
  4. Allow root access and start a GNOME desktop session (software GL rendering is required since there is no GPU; PULSE_SERVER routes audio to the physical audio device):

    DISPLAY=:99 xhost +local:root
    mkdir -p /tmp/gnome99-runtime && chmod 700 /tmp/gnome99-runtime
    DISPLAY=:99 LIBGL_ALWAYS_SOFTWARE=1 XDG_SESSION_TYPE=x11 \
      XDG_RUNTIME_DIR=/tmp/gnome99-runtime \
      PULSE_SERVER=unix:/run/user/$(id -u)/pulse/native \
      dbus-run-session -- gnome-session --session=ubuntu &
  5. Verify the display is working (give GNOME a few seconds to start):

    DISPLAY=:99 ffmpeg -f x11grab -video_size 1920x1080 -i :99.0+0,0 -frames:v 1 -update 1 /tmp/check.png

Your physical keyboard and mouse now control the virtual display directly.

Audio: If audio is not working (e.g. PulseAudio only has a null sink), load your physical audio device manually:

# List available ALSA devices
aplay -l
# Load the desired device (e.g. USB headset on card 1)
pactl load-module module-alsa-sink device=hw:1,0 sink_name=plantronics sink_properties=device.description="Plantronics_Headset"
pactl set-default-sink plantronics

Applications launched on the virtual display (e.g. Chrome) must also have PULSE_SERVER set:

DISPLAY=:99 PULSE_SERVER=unix:/run/user/$(id -u)/pulse/native google-chrome &

Example: screen-capture input (config/tx_fullhd_screen_capture.json)

{
  "interfaces": [
    { "name": "0000:06:00.0", "sip": "192.168.50.29", "dip": "239.168.85.20" }
  ],
  "video": {
    "width": 1920,
    "height": 1080,
    "input_mode": "screen_capture",
    "screen_input": ":99.0+0,0"
  },
  "tx_video": {
    "fps": 30,
    "fmt": "yuv422p10le"
  },
  "tx_sessions": [
    { "udp_port": 20000, "crop": { "x": 0, "y": 0, "w": 1920, "h": 1080 } }
  ]
}

Example: multi-session screen capture (config/tx_fullhd_screen_capture_multi_session.json)

Splits a 1920×1080 screen capture into 3 vertical strips, each transmitted as a separate ST2110 session:

{
  "interfaces": [
    { "name": "0000:06:00.0", "sip": "192.168.50.29", "dip": "239.168.85.20" }
  ],
  "video": {
    "width": 1920,
    "height": 1080,
    "input_mode": "screen_capture",
    "screen_input": ":99.0+0,0"
  },
  "tx_video": {
    "fps": 30,
    "fmt": "yuv422p10le"
  },
  "tx_sessions": [
    { "udp_port": 20000, "payload_type": 96, "crop": { "x": 0,    "y": 0, "w": 640, "h": 1080 } },
    { "udp_port": 20002, "payload_type": 96, "crop": { "x": 640,  "y": 0, "w": 640, "h": 1080 } },
    { "udp_port": 20004, "payload_type": 96, "crop": { "x": 1280, "y": 0, "w": 640, "h": 1080 } }
  ]
}

screen_input follows FFmpeg's x11grab URL syntax: <display>[+<x>,<y>] (e.g. :99.0+0,0 captures display :99, matching the virtual display created above, starting at offset 0,0). The capture resolution/framerate are taken from the width/height/fps fields above.

Run dvledtx as usual — x11grab will capture whatever is rendered on the display (desktop, windows, applications) and transmit it, exactly as it would for a physical display:

./build/dvledtx --config config/tx_fullhd_screen_capture.json

Tear down the virtual display when done:

pkill -f "gnome-session --session=ubuntu"
sudo pkill -f "Xorg :99"

Logging

dvledtx includes a built-in logger with configurable output targets and log levels.

Log File

Specify a log file in the JSON config with the top-level log_file field:

{
  "log_file": "dvledtx.log",
  ...
}

When log_file is set, log output is written to that file in addition to the console. If the field is omitted, output goes to the console only.

Log Levels

Level Description
ERROR Critical failures only
WARN Non-fatal warnings
INFO General operational messages (default)
DEBUG Verbose diagnostic output

Examples

Using JSON Configuration (recommended)

./build/dvledtx --config config/tx_fullhd_single_session.json

Command-Line Options

Option Description
-C, --config <file> JSON config file (required)
-v, --version Show version and exit
--help Show help message

Show Version

./build/dvledtx --version
# or
./build/dvledtx -v

Supported Formats

Video Formats

Format Chroma Bit Depth Color Space
yuv422p10le 4:2:2 10-bit YUV
yuv420 4:2:0 8-bit YUV
yuv444p10le 4:4:4 10-bit YUV
gbrp10le 4:4:4 10-bit RGB
yuv422p12le 4:2:2 12-bit YUV
yuv444p12le 4:4:4 12-bit YUV
gbrp12le 4:4:4 12-bit RGB

Frame Rates

  • 25 fps
  • 30 fps
  • 50 fps
  • 60 fps

Resolutions

Resolution Dimensions Description
1080p (Full HD) 1920x1080 Standard HD resolution
2K (QHD) 2560x1440 Quad HD / 2K resolution
4K (UHD) 3840x2160 Ultra HD / 4K resolution

Note: Maximum supported resolution is 3840x2160. Width must be even for YUV format alignment.

Video Scaling

dvledtx supports upscaling and downscaling via optional scale_width and scale_height fields in the video configuration block. When set, the decoded source video is scaled to the specified dimensions before crop regions are applied.

Feature Description
Upscale Scale smaller source (e.g. 1080p) to larger output (e.g. 4K)
Downscale Scale larger source (e.g. 4K) to smaller output (e.g. 1080p)
Single session Full scaled frame transmitted as one stream
Multi-session Crop regions applied to scaled frame for tiled LED walls
Max output 3840x2160 (4K UHD)

Example — upscale 1080p source to 4K output:

{
  "video": {
    "width": 1920, "height": 1080,
    "tx_url": "source_1080p.mp4"
  },
  "tx_video": {
    "scale_width": 3840, "scale_height": 2160,
    "fps": 30, "fmt": "yuv422p10le"
  },
  "tx_sessions": [
    { "udp_port": 20000, "payload_type": 96, "crop": { "x": 0, "y": 0, "w": 3840, "h": 2160 } }
  ]
}

Log output when scaling is active:

[INFO ] Video: 1920x1080 -> scale 3840x2160 30fps yuv422p10le  tx_url=source_1080p.mp4
[INFO ]   Session 0: udp_port=20000 pt=96 crop=[0,0 3840x2160]

Note: When scale_width/scale_height are set, crop bounds are validated against the scaled dimensions, not the source dimensions. Both fields must be specified together, and the scaled dimensions must satisfy the pixel format's chroma-alignment constraints (e.g. yuv420 requires even width and height; yuv422* requires even width).

Performance Considerations

Optimization Features

  • Hugepage Memory: All buffers allocated on hugepages
  • Zero-copy Design: Minimal memory copying
  • Hardware Acceleration: Uses MTL's hardware features
  • Efficient Threading: Minimal context switching

Running Unit Tests

Install dependencies if not present

sudo apt install libcmocka-dev
pip install gcovr

To build and run all unit tests with coverage:

bash scripts/test.sh

To run tests without generating a coverage report:

bash scripts/test.sh --no-coverage

Troubleshooting

IOMMU / VFIO Kernel Parameters (GRUB)

MTL requires IOMMU and VFIO kernel parameters to be set. If MTL fails to initialize or VFIO devices are not accessible, check that the following parameters are present in /etc/default/grub:

intel_iommu=on iommu=pt pcie_aspm=off pcie_port_pm=off vfio-pci.disable_idle_d3=1

To add them manually:

  1. Open /etc/default/grub in a text editor:

    sudo nano /etc/default/grub
  2. Append the missing parameters to GRUB_CMDLINE_LINUX_DEFAULT, for example:

    GRUB_CMDLINE_LINUX_DEFAULT="quiet splash intel_iommu=on iommu=pt pcie_aspm=off pcie_port_pm=off vfio-pci.disable_idle_d3=1"
    
  3. Update GRUB and reboot:

    sudo update-grub
    sudo reboot

Note: A reboot is required for the IOMMU/VFIO parameters to take effect.

VFIO Group Setup

MTL uses VFIO to access the NIC. The current user must belong to the vfio group.

  1. Create the vfio group if it does not exist:

    sudo groupadd vfio
  2. Add your user to the group:

    sudo usermod -aG vfio $USER
  3. Apply the group membership without logging out:

    newgrp vfio

    Or log out and back in for the change to take effect permanently.

  4. Verify group membership:

    id -nG $USER

Killing the Application

If dvledtx becomes unresponsive or needs to be force-stopped:

sudo pkill -9 -f dvledtx

Common Issues

  1. MTL Initialization Failed

    • Check network port PCI address matches your hardware:
      lspci | grep Ethernet
      # Example output: 06:00.0 Ethernet controller: Intel Corporation ...
      # Use BDF format in config: "0000:06:00.0"
    • Verify MTL library is properly installed:
      # Check MTL shared library is present
      ldconfig -p | grep mtl
      # Expected: libmtl.so (libc6,x86-64) => /usr/local/lib/x86_64-linux-gnu/libmtl.so
      
      # Check MTL pkg-config file is available
      pkg-config --modversion mtl
      # Expected: 26.01.0.REL (or higher)
      
      # Check MTL header files exist
      ls /usr/local/include/mtl/mtl_api.h
    • Verify DPDK is installed and the NIC is bound to VFIO:
      # Check DPDK installation
      pkg-config --modversion libdpdk
      
      # Check NIC binding status
      sudo $mtl_source_code/script/nicctl.sh list
      # 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):
      dmesg | grep -i iommu
      # Should show "DMAR: IOMMU enabled"
      cat /proc/cmdline | grep intel_iommu
    • Verify network card is supported (Intel I225, I226, E810 series)
  2. Cannot Load Source File

    • Check file exists and is readable:
      ls -la /path/to/video.mp4
    • Verify the file is a valid video (not corrupted):
      ffprobe /path/to/video.mp4
      # Should show video stream info without errors
    • Ensure sufficient disk space:
      df -h .
    • Verify file format matches config parameters (resolution, pixel format)
    • Note: Symlinked files are rejected for security reasons
  3. Network Transmission Issues

    • Ensure network card supports required bandwidth (uncompressed 1080p30 ≈ 1.8 Gbps)
    • Verify hugepages are allocated:
      cat /proc/meminfo | grep HugePages
      # HugePages_Free should be > 0
  4. Hugepage Allocation Failure

    • Check current hugepage status:
      cat /proc/meminfo | grep Huge
    • Allocate hugepages (requires root):
      sudo sysctl -w vm.nr_hugepages=2048
      # Or make persistent in /etc/sysctl.conf
  5. Permission Denied (VFIO)

    • Ensure user is in the vfio group (see VFIO Group Setup)
    • Check VFIO device permissions:
      ls -la /dev/vfio/
      # Group should be 'vfio' with rw permissions

Contributing

Contributions are welcome. Please open an issue or submit a pull request on GitHub.

License

This project is licensed under the BSD 3-Clause License. See LICENSE for details.

About

This repository contains the Direct View LED Software Toolkit — an open-source transmitter application developed by Intel as part of the Open Visual Cloud initiative. It is designed for professional AV-over-IP installations that drive direct-view LED video walls using the SMPTE ST 2110 suite of standards.

Topics

Resources

License

Security policy

Stars

2 stars

Watchers

1 watching

Forks

Packages

 
 
 

Contributors