C++20 library to read and write MISB KLV metadata — ST 0601 (UAS Datalink Local
Set) + ST 0903 (VMTI) — from/to MPEG-TS containers via
GStreamer (file or stream; real-time
insertion via appsrc). Video passthrough can generate ST 0604 Precision Time
Stamp SEI into the H.264 stream on request
(ADR 0024; off by default,
so passthrough video is byte-identical); the rest of
ST 0604 (ES-layer timestamp reading, H.265 Nano, Commercial time code) and an
ffmpeg backend are deferred — see
ADR 0008 and
ADR 0009.
- ST 0601 UAS Datalink LS and ST 0903 VMTI — embedded (Item 74), standalone, and VTarget Series — decode and byte-exact re-encode.
- ST 1201 IMAPB float↔integer mapping (incl. structural special values), cross-checked against the standards' vectors and jmisb.
- MPEG-TS via GStreamer: extract
stream_type0x06 from a file or liveudp:/srt:source, and insert to a file or live sink (clock-paced), all with stock GStreamer — no custom plugin. - Video passthrough on insert: point the sink at a source file, RTSP URI, or explicit GStreamer pipeline and its video elementary stream is re-muxed unchanged (parsed, never decoded) alongside your KLV — one call writes a TS with both a video PID and a KLV PID.
- gst-free file extraction: pull
stream_type0x06 and 0x15 KLV from a.tsbuffer with zero dependencies (extract_ts_klv); GStreamer is only needed for live sources. - High-level API: an owned, editable
Message(typedget<T>/set, byte-exactencode) plus aKlvStream/KlvSinkread-edit-write facade — read and write share one timeline, so editing a stream doesn't re-time it; terminal read errors are checked explicitly after iteration, and a live sink's backend failure can be polled and latest video delivery observed without blocking.
#include "misbklv/stream.hpp"
using namespace misbklv;
KlvStream in("input.ts"); // a file, or "udp:127.0.0.1:5004" / "srt:..."
KlvSink out("file:output.ts");
if (out.error()) return; // open_insert failed
for (Message& m : in) {
if (auto lat = m.get<double>(tags::Uas0601::SensorLatitude))
m.set(tags::Uas0601::SensorLatitude, Value{*lat + 0.001}); // nudge ~100 m north
if (!out.emit(m)) return;
}
if (in.error()) return; // extraction or Message parse failed
if (!out.close()) return;Full walkthrough (including the gstreamer-free path) in docs/api.md.
# Core only (Message, parse, codec) — no gstreamer dependency:
find_package(misbklv REQUIRED)
target_link_libraries(app PRIVATE misbklv::misbklv)
# ...or with the streaming facade (KlvStream / KlvSink), which needs gstreamer:
find_package(misbklv REQUIRED COMPONENTS gst)
target_link_libraries(app PRIVATE misbklv::gst)# Preferred — presets (CMake ≥ 3.21, CMakePresets.json v3; same presets CI uses):
cmake --preset release # or debug / sanitize
cmake --build --preset release
ctest --test-dir build/release
# debug → build/debug (Debug + GStreamer ON); sanitize → build/sanitize
# (Debug + MISBKLV_SANITIZE=ON, MISBKLV_GSTREAMER=OFF core-only)
# Presets place builds under build/* and set jobs=6 via a hidden base (nproc 7 → 6).
# Fallback without presets:
cmake -S . -B build
cmake --build build
ctest --test-dir buildThe build and tests use project-owned synthetic fixtures. The data/ directory
is reserved for developer-provided media and is not required for a normal build.
The small generated fixtures are committed, so a normal build does not require
Python or network access.
- CMake ≥ 3.20 and a C++20 compiler (GCC ≥ 11). The core library
(
misbklv::misbklv) has no additional runtime or link dependencies. - GStreamer ≥ 1.20 — optional, only for the streaming facade
(
misbklv::gst). To build it you needpkg-configand the dev files forgstreamer-1.0,gstreamer-app-1.0, andgstreamer-codecparsers-1.0(the H.264/H.265 codecparsers; on Debian/Ubuntu this ships in the plugins-bad*-devpackage); to run it (and the gstreamer tests) you also need the runtime plugins that provide the pipeline elements — MPEG-TS mux/demux and SRT are in plugins-bad, UDP in plugins-good, app/core in plugins-base. Without GStreamer the core still builds and its tests run; the facade and its tests are skipped.
# core build + tests
sudo apt-get install -y cmake g++
# ...plus the streaming facade (misbklv::gst): dev files + runtime plugins
sudo apt-get install -y \
pkg-config libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev \
libgstreamer-plugins-bad1.0-dev \
gstreamer1.0-plugins-base gstreamer1.0-plugins-good gstreamer1.0-plugins-badNames vary by distro; you need a C++20 toolchain, CMake, and pkg-config, and —
for the facade — the GStreamer 1.x dev files (gstreamer-1.0,
gstreamer-app-1.0, gstreamer-codecparsers-1.0) plus the base/good/bad
runtime plugins. For example:
- Fedora:
gcc-c++ cmake pkgconfig gstreamer1-devel gstreamer1-plugins-base-devel gstreamer1-plugins-bad-free-devel gstreamer1-plugins-base gstreamer1-plugins-good gstreamer1-plugins-bad-free - Arch:
gcc cmake pkg-config gstreamer gst-plugins-base gst-plugins-good gst-plugins-bad
Python 3.11+ is needed only to run the optional regenerate-registry and
regenerate-synthetic-fixtures targets; their committed outputs are checked for
drift in CI.
The KLV core (ST 0601 + ST 0903) and the GStreamer media backend are implemented
and tested, and the library is installable via find_package. See
planning/ROADMAP.md and
planning/PROGRESS.md for the plan and current status,
and context/decisions/ for the architectural decisions
(ADRs).
Agent working instructions live in AGENTS.md — the one canonical,
vendor-neutral copy. CLAUDE.md is a one-line @AGENTS.md import, since Claude
Code auto-loads that filename; supporting another agent means adding another
thin pointer, never a second copy of the rules.
Apache-2.0 — see LICENSE.