A GStreamer 1.x plugin suite for end-to-end KLV metadata workflows. Implements SMPTE ST 336 (KLV encoding), MISB ST 0601.8 (UAS Datalink Local Set, 93 tags), STANAG 4609 transport conventions, and MISB ST 1402 MPEG-TS metadata signaling. Written in C11 for compatibility with the GStreamer plugin ecosystem.
Demo capture running at 2 fps on purpose, so the terminal KLV output stays readable while still showing the frame-by-frame relationship between video and metadata.
| Element | Type | Sink | Source | Description |
|---|---|---|---|---|
klvmetaenc |
GstBaseTransform |
application/json |
meta/x-klv |
Encode JSON to MISB ST 0601 KLV |
klvmetadec |
GstBaseTransform |
meta/x-klv |
application/json |
Decode KLV to JSON with INI-driven scaling |
klvframeinject |
GstElement |
video/x-h264, video/x-h265 |
video_src, klv_src |
Per-frame KLV injection synchronized to video |
tspmtrewrite |
GstBaseTransform |
video/mpegts |
video/mpegts |
Rewrite PMT to signal tsdemux-friendly KLVA metadata (stream_type 0x06 in the current implementation) |
| Standard | Scope |
|---|---|
| SMPTE ST 336 | KLV Key-Length-Value encoding (BER lengths, UL keys) |
| MISB ST 0601.8 | UAS Datalink Local Set — 93 tags, BCC-16 checksum |
| STANAG 4609 | MPEG-TS motion imagery and metadata transport |
| MISB ST 1402 | PMT metadata signaling (metadata_descriptor 0x26; current implementation uses 0x06 + KLVA for GStreamer compatibility) |
git clone https://github.com/mkassimi98/gstklvplugin.git
cd gstklvpluginIf you want a specific released state instead of the current default branch:
git clone --branch v1.0.0 --single-branch \
https://github.com/mkassimi98/gstklvplugin.git
cd gstklvplugin# Meson build
sudo apt-get install -y \
meson ninja-build \
libgstreamer1.0-dev \
libgstreamer-plugins-base1.0-dev
# Optional: C++ examples
sudo apt-get install -y g++
# Optional: repository formatting
sudo apt-get install -y clang-format
# Optional: Doxygen diagrams
sudo apt-get install -y graphvizmeson setup build
meson compile -C buildRun the Meson test suite:
meson test -C build --print-errorlogsOpen a build-local shell with the plugin already exported:
meson devenv -C buildThat is the standard Meson-style uninstalled workflow for this repo. Inside the
shell, GST_PLUGIN_PATH points at build/src and KLV_TAGS_INI points at the
source-tree registry.
# Build + tests + smoke tests + Doxygen
./scripts/check_all.sh
# Apply clang-format to all tracked C/C++ files
./scripts/run_clang_tools.sh --format
# Configure/build a local dev shell wrapper
./scripts/dev_env.sh
# Configure/build/validate/install
./scripts/install_plugin.sh --run-checks --install --prefix /usr/localmkdir -p build && cd build
cmake ..
cmake --build .With C++ examples:
cmake -S . -B build \
-DGSTKLVPLUGIN_BUILD_EXAMPLES=ON
cmake --build buildThe CMake install path now uses ${CMAKE_INSTALL_LIBDIR}/gstreamer-1.0, so it
lands in the correct multiarch plugin directory on Debian-family x86 and ARM
systems.
Formatting targets are also available from CMake:
cmake -S . -B build-cmake -DGSTKLVPLUGIN_BUILD_EXAMPLES=ON
cmake --build build-cmake --target format
cmake --build build-cmake --target format-checkRecommended Meson flow:
meson setup build --prefix /usr/local
meson compile -C build
meson test -C build --print-errorlogs
sudo meson install -C buildThe GStreamer plugin directory on the current system can be queried with:
pkg-config --variable=pluginsdir gstreamer-1.0export GST_PLUGIN_PATH="$PWD/build/src:$GST_PLUGIN_PATH"
export KLV_TAGS_INI="$PWD/data/stanag4609_tags.ini"
gst-inspect-1.0 --plugin klvplugin
gst-inspect-1.0 klvmetaenc
gst-inspect-1.0 klvmetadec
gst-inspect-1.0 klvframeinject
gst-inspect-1.0 tspmtrewriteCheck visibility without printing full details:
gst-inspect-1.0 --exists klvmetaenc && echo "klvmetaenc is available"Check whether GStreamer blacklisted the plugin:
gst-inspect-1.0 -b
gst-inspect-1.0 -b | rg 'gstklvplugin|klvplugin|gstklv'Clear the registry cache if you need to remove stale blacklist entries:
rm -f "${XDG_CACHE_HOME:-$HOME/.cache}/gstreamer-1.0"/registry.*.binSee doc/installation.md for the full install,
validation, staging, and blacklist-recovery workflow, and
doc/packaging.md for .deb packaging. For containerized
development and deployment patterns, see doc/docker.md.
Validate all 93 MISB ST 0601.8 tags locally:
export GST_PLUGIN_PATH="$PWD/build/src:$GST_PLUGIN_PATH"
python3 examples/test_93_tags.pyStart the receiver first:
python3 examples/srt-pipelines/python/srt_receiver_93tags.py \
--host 127.0.0.1 --port 5000Then start the sender:
python3 examples/srt-pipelines/python/srt_sender_93tags.py \
--host 0.0.0.0 --port 5000 --count 50Transport notes:
- Sender uses
mpegtsmux alignment=7so TS leaves in7 * 188 = 1316byte chunks. - Receiver uses
srtsrc blocksize=1316 latency=125, which is what made live H.264 decode reliably in practice. - Receiver keeps both the video sink and the KLV handoff sink clock-synchronised, so terminal prints track the displayed frame instead of arriving early.
- For the smoothest live preview, prefer
--print-summary;--print-allis heavier console I/O and can still add jitter.
Start the receiver first:
python3 examples/udp-pipelines/python/udp_receiver_93tags.py \
--host 0.0.0.0 --port 5000Then start the sender:
python3 examples/udp-pipelines/python/udp_sender_93tags.py \
--host 127.0.0.1 --port 5000 --count 50The UDP receivers use the same presentation-timed output strategy as the SRT ones, so printed KLV follows the video clock instead of a separate fast metadata path.
Build C++ examples:
cmake -S . -B build -DGSTKLVPLUGIN_BUILD_EXAMPLES=ON
cmake --build buildThe C++ example binaries are built via CMake. Meson remains the recommended path for the plugin and tests, but it does not emit the example executables.
./build/gstklv_srt_receiver --host 127.0.0.1 --port 5000
./build/gstklv_srt_sender --host 0.0.0.0 --port 5000 --count 50Like the Python receivers, the C++ SRT receiver keeps video and KLV output on the same clocked timeline.
./build/gstklv_udp_receiver --host 0.0.0.0 --port 5000
./build/gstklv_udp_sender --host 127.0.0.1 --port 5000 --count 50The C++ UDP receiver uses the same sync strategy as the SRT receiver.
python3 examples/ts/python/klv_recorder.py \
--output examples/ts/recordings/capture.ts --count 50
python3 examples/ts/python/klv_video_reader.py \
examples/ts/recordings/capture.ts --print-allThe Python TS reader now paces KLV against playback, using the video pipeline clock when GStreamer exposes one and falling back to local PTS pacing otherwise.
./build/gstklv_ts_recorder \
--output examples/ts/recordings/capture.ts --count 50
./build/gstklv_ts_reader \
--input examples/ts/recordings/capture.ts --print-allThe C++ TS reader scans the file PMT/PES directly, tolerates both legacy 0x15 and current 0x06 + KLVA captures, and paces KLV by PES PTS by default. Use --no-pace only when you want a fast metadata dump instead of playback-timed output.
KLV values are exchanged as flat JSON objects with numeric string keys:
{
"2": 1770260492651783,
"5": 34.5,
"13": 40.63919,
"14": -73.92060,
"15": 486.7
}Tags that represent local sets (nested binary structures) must be supplied as raw bytes using hex: or base64: prefixes:
{
"48": "base64:AQEA",
"73": "hex:01020304"
}See doc/standards.md for encoding and decoding rules.
flowchart LR
A["videotestsrc"] --> B["x264enc"]
B --> C["h264parse"]
C --> D["klvframeinject"]
D -->|video_src| E["queue"]
E --> F["mpegtsmux"]
D -->|klv_src| G["queue"]
G --> H["meta/x-klv"]
H --> F
F --> I["tspmtrewrite"]
I --> J["srtsink / udpsink"]
flowchart LR
A["srtsrc / udpsrc"] --> B["tsdemux"]
B -->|video/x-h264| C["h264parse"]
C --> D["decoder"]
D --> E["videosink"]
B -->|meta/x-klv| F["klvmetadec"]
F --> G["JSON output"]
tspmtrewrite rewrites the PMT after mpegtsmux and keeps the ST 1402 metadata descriptor fields configurable.
Current implementation details:
- KLV PID:
stream_type = 0x06 registration_descriptor:KLVAmetadata_descriptor (0x26): present with configurable fields- Rationale: GStreamer
tsdemuxexpects raw KLV on0x06 + KLVA; plain0x15would require metadata access-unit wrapping thatmpegtsmuxis not producing here.
Verify a captured stream:
python3 tools/capture_ts_from_srt.py \
--host 127.0.0.1 --port 5000 \
--output examples/ts/recordings/capture.ts --duration 5
python3 tools/verify_ts_klv.py examples/ts/recordings/capture.ts --list-allverify_ts_klv.py accepts both legacy 0x15 captures and the current 0x06 + KLVA signaling used by this repo.
meson setup build
meson compile -C build
meson test -C build --print-errorlogsCovers nine Meson suites by default:
- element tests for
klvmetaenc,klvmetadec,klvframeinject, andtspmtrewrite - utility tests for KLV helpers and MPEG-TS PSI helpers
- smoke tests for TS record/read and UDP loopback using the Python examples
- a staged-install smoke that validates plugin discovery from a clean registry outside the build tree
When cmake is available, the suite also adds a C++ TS roundtrip smoke for the example binaries.
doc/tests.md describes the suite layout, smoke coverage, and the GStreamer
testing APIs used (gstreamer-check, GstHarness, and pipeline-level checks
where multi-pad behaviour is under test).
Meson is the only supported test runner now. CMake remains available for building the plugin and the C++ example applications.
| Variable | Purpose |
|---|---|
GST_PLUGIN_PATH |
Directory containing gstklvplugin.so |
KLV_TAGS_INI |
Override path to stanag4609_tags.ini |
KLV_DEBUG |
Enable verbose KLV logging in klvframeinject |
GST_DEBUG |
Standard GStreamer debug level (e.g. GST_DEBUG=3) |
Tag registry locations:
- Source tree:
data/stanag4609_tags.ini - Installed builds:
${prefix}/share/gstklvplugin/stanag4609_tags.ini
| Document | Description |
|---|---|
| doc/index.md | Documentation landing page and architecture map |
| doc/installation.md | Manual and automatic build/install workflows, discovery, and blacklist recovery |
| doc/docker.md | Docker integration guide for development, runtime images, networking, and troubleshooting |
| doc/packaging.md | Debian/Raspberry Pi packaging and distribution workflow |
| doc/standards.md | Standards, encoding rules, and compliance scope |
| doc/klv_tags.md | Full MISB ST 0601.8 tag registry table |
| doc/code_reference.md | Module layout, key functions, and data flow |
| doc/design_decisions.md | Rationale behind key design choices |
| doc/compliance_appendix.md | PMT descriptor bytes and verification steps |
| doc/examples.md | Example workflows (Python and C++) |
| doc/93_tags.md | Full 93-tag ST 0601.8 workflow |
| doc/srt_pipelines.md | SRT pipeline composition and options |
| doc/tests.md | Test suite documentation |
| doc/doxygen_main.md | Doxygen API reference entry point |
| doc/plugin_usage_guide.md | Integration guide for Python and C++ applications |
./scripts/run_doxygen.sh --strict
# Open: doc/doxygen/html/index.html| File | Description |
|---|---|
LICENSE |
GNU AGPL-3.0 |
AUTHORS |
Maintainers and contributors |
CHANGELOG.md |
Release history |
CONTRIBUTING.md |
Contribution guide |
CODE_OF_CONDUCT.md |
Community standards (Contributor Covenant v2.1) |
SECURITY.md |
Vulnerability reporting policy |
meson.build |
Meson build definition |
CMakeLists.txt |
CMake build definition |
Doxyfile |
Doxygen configuration |
data/stanag4609_tags.ini |
Authoritative tag registry |
Mouhsine Kassimi Farhaoui — mouhsine98@gmail.com
