Skip to main content

pymisb — MISB ST0601 / STANAG 4609 mux, demux & streaming

pymisb

CI PyPI version

MISB ST0601 / STANAG 4609 mux, demux & live streaming library for Python.

Module What it does
mux Turn DJI drone footage + telemetry into STANAG 4609 videos with embedded KLV
mux (live) Mux a continuous MPEG-TS with telemetry dicts that arrive at their own rate
demux Extract and decode KLV telemetry from any STANAG-compliant video file
stream Read KLV in real-time from live UDP/RTP/RTSP streams
vmti Parse MISB ST 0903 VMTI target tracking metadata
pip install pymisb

Donations — If this project is useful for you, consider buying me a beer! paypal

Quick start

Mux — create a MISB video

from pymisb.common import build_klv_packets
from pymisb.mux import mux_with_ffmpeg

packets = build_klv_packets("telemetry.csv")
mux_with_ffmpeg("drone.mp4", packets, "output.ts")

Demux — read KLV from a video

from pymisb.demux import extract_klv, decode_packets

klv_data = extract_klv("output.ts")
packets = decode_packets(klv_data)
for pkt in packets:
    meta = pkt.MetadataList()
    print(meta[13])  # Sensor Latitude

Live mux — continuous video, telemetry when it arrives

The video is an ongoing MPEG-TS. The drone is polled at some rate, so most iterations have no sample. When one does arrive it is a plain dict. examples/live_mux.py simulates both and writes muxed_data.ts:

python examples/live_mux.py
from pymisb.mux import LiveMuxer

muxer = LiveMuxer()
with open("muxed_data.ts", "wb") as fh:
    for chunk in video_chunks():       # any slice of an MPEG-TS
        sample = poll_drone()          # dict, or None
        fh.write(muxer.feed(chunk, sample))
    fh.write(muxer.close())

sample can use latitude, longitude, altitude, heading and an ISO-8601 timestamp. The video has to already be MPEG-TS; an MP4 or a raw elementary stream needs ffmpeg -c copy -f mpegts first.

Stream — real-time KLV from live feeds

from pymisb.stream import open_stream

with open_stream("udp://@:5000") as reader:
    for pkt in reader:
        meta = pkt.MetadataList()
        print(meta[13])  # Sensor Latitude in real-time

VMTI — target tracking metadata

from pymisb.vmti import parse_vmti, extract_vmti

targets = parse_vmti(extract_vmti("video.ts"))
for t in targets:
    print(t["target_centroid_lat"], t["target_classification"])

Package structure

pymisb/
├── constants.py          Shared constants (keys, versions, FOV, etc.)
├── common/
│   ├── encoder.py        MISB ST0601 KLV packet builder
│   ├── dji.py            DJI CSV + binary .txt flight record parsers
│   └── ts.py             Path defaults and KLV stream writer
├── mux/
│   ├── engine.py         MPEG-TS muxing (PES/PCR/KLV injection + FFmpeg)
│   └── live.py           Live mux: MPEG-TS chunks + telemetry dicts
├── demux/
│   └── engine.py         KLV extraction and decoding from files
├── stream/
│   └── engine.py         Real-time KLV from UDP/RTP/RTSP streams
├── vmti/
│   └── engine.py         MISB ST 0903 VMTI target metadata
└── klvdata/              Vendored MISB ST0601/0903 KLV decoder

Requirements

  • Python 3.12+ (3.12, 3.13)
  • FFmpeg on the PATH (for muxing, demuxing, and streaming)

Installation

From PyPI

pip install pymisb

From source

git clone https://github.com/All4Gis/pymisb.git
cd pymisb
pip install .

Development (editable)

git clone https://github.com/All4Gis/pymisb.git
cd pymisb
pip install -e ".[test]"

Verify

python -c "from pymisb.constants import UAS_LS_KEY; print('OK')"
pymisb-mux --help
pymisb-demux --help

CLI usage

Muxing

# From DJI CSV telemetry
pymisb-mux --video drone.mp4 --csv telemetry.csv
pymisb-mux --video drone.mp4 --csv telemetry.csv --out output.ts

# From DJI binary .txt flight record
pymisb-mux --video drone.mp4 --dji-txt DJIFlightRecord.txt

# KLV only (no video muxing)
pymisb-mux --csv telemetry.csv --out output.ts --klv-only

# Or via module
python -m pymisb.mux --video drone.mp4 --csv telemetry.csv

Demuxing

# Dump all KLV packets
pymisb-demux output.ts

# Playback at the video's real rate
pymisb-demux output.ts --play

# Accelerated playback x4
pymisb-demux output.ts --play --speed 4

# Show every ST0601 tag
pymisb-demux output.ts --all

# Tag tree of the first packet
pymisb-demux output.ts --structure

Streaming (live)

# Read KLV from a live UDP stream
pymisb-stream udp://@:5000

# RTSP camera
pymisb-stream rtsp://camera.local/stream --all

# Or via module
python -m pymisb.stream udp://@:5000

VMTI (target tracking)

# Decode VMTI targets from a video
pymisb-vmti video.ts

# Show all target fields
pymisb-vmti video.ts --all

Library API

pymisb.common — encoder and parsers

from pymisb.common import (
    build_st0601_packet,       # Build a single ST0601 KLV packet
    build_klv_packets,         # Parse DJI CSV -> list of KLV packets
    build_klv_packets_from_txt, # Parse DJI .txt -> list of KLV packets
    default_output,            # Generate default output path
    write_klv_stream,          # Write packets as raw KLV file
)

pymisb.mux — MPEG-TS muxing

from pymisb.mux import (
    mux_with_ffmpeg,       # Full mux pipeline (FFmpeg + KLV injection)
    inject_klv_into_ts,    # Low-level: inject KLV into existing TS
    LiveMuxer,             # Live: MPEG-TS chunks + telemetry dicts
    metadata_to_klv,       # One telemetry dict -> one ST0601 packet
)

pymisb.demux — KLV extraction and decoding from files

from pymisb.demux import (
    extract_klv,           # Extract raw KLV bytes from TS via FFmpeg
    decode_packets,        # Decode raw KLV into parsed ST0601 packets
)

pymisb.stream — real-time KLV from live streams

from pymisb.stream import open_stream

# Iterator mode — yields packets as they arrive
with open_stream("udp://@:5000") as reader:
    for pkt in reader:
        meta = pkt.MetadataList()
        print(meta[13])

# Callback mode
def on_packet(pkt):
    print(pkt.MetadataList()[13])

reader = open_stream("rtsp://camera.local/stream")
reader.start(callback=on_packet)
reader.stop()

pymisb.vmti — MISB ST 0903 VMTI target tracking

from pymisb.vmti import parse_vmti, extract_vmti, decode_vmti_stream

# From a TS file
targets = parse_vmti(extract_vmti("video.ts"))
for t in targets:
    print(t["target_centroid_lat"], t["target_classification"])

# From raw bytes
targets = decode_vmti_stream(vmti_bytes)

pymisb.constants — shared configuration

from pymisb.constants import (
    UAS_LS_KEY,            # 16-byte UAS Datalink Local Set universal key
    KLV_HEADER_KEY,        # Shifted key for DJI non-standard headers
    ST0601_VERSION,        # MISB ST0601 version (Tag 65)
    HFOV_DEG, VFOV_DEG,   # Camera field of view (degrees)
    EARTH_MEAN_RADIUS,     # WGS-84 radius in meters
)

Configuration

Edit pymisb/constants.py to adjust:

  • HFOV_DEG / VFOV_DEG — camera field of view (affects sensor footprint)
  • ST0601_VERSION — advertised Local Set version (Tag 65)

Supported standards

Standard Name Status
MISB ST 0601 UAS Datalink Local Set Full (encode + decode)
MISB ST 0903 VMTI (Video Moving Target Indicator) Decode
MISB ST 0102 Security Metadata Decode (via klvdata)
MISB EG 0104 UAV Basic Universal Metadata Decode (via klvdata)
STANAG 4609 NATO video standard Full (mux + demux)

Testing

# Install with test dependencies
pip install -e ".[test]"

# Run all tests
pytest tests/ -v

# Run specific test file
pytest tests/test_misb_common.py -v

# Run specific test
pytest tests/test_misb_common.py::TestBuildSt0601Packet -v

# With coverage
pip install pytest-cov
pytest tests/ --cov=pymisb --cov-report=term-missing
Test file Coverage
tests/test_misb_common.py ST0601 encoder, DJI parsers, BER/BCC, geometry
tests/test_misb_mux.py MPEG-TS primitives (PES, PTS, PCR, packetization)
tests/test_klvdata.py Vendored klvdata decoder (ST0601 tags, BER-TLV, integration)

CI runs on every push across Python 3.12-3.13 on Linux, macOS, and Windows.

Publishing

# Build
pip install build
python -m build

# Publish to PyPI
pip install twine
twine upload dist/*

# Or TestPyPI first
twine upload --repository testpypi dist/*
pip install --index-url https://test.pypi.org/simple/ pymisb

License

This project is licensed under the MIT License.

The vendored klvdata decoder in pymisb/klvdata/ is derived from paretech/klvdata (MIT). See THIRD_PARTY_LICENSES.md for full attribution.


"From raw drone footage to geospatial intelligence — one KLV packet at a time."

Made with passion by Fran Raga franka1986@gmail.com - June 2026.

If this saved you a few hours, give the repo a star and fly safe. Happy coding!

Metadata

Release files for pymisb 2.0.2

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for pymisb 2.0.2
File Size Uploaded Provenance Actions
pymisb-2.0.2.tar.gz 40.9 kB Without provenance Details for pymisb-2.0.2.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for pymisb 2.0.2
File Interpreter ABI Platform Actions
pymisb-2.0.2-py3-none-any.whl Python 3 none any Details for pymisb-2.0.2-py3-none-any.whl

Total release size: 91.3 kB

Release files / pymisb-2.0.2.tar.gz

Download URL pymisb-2.0.2.tar.gz
Size 40.9 kB
Tags Source
SHA-256 checksum
How to use checksums
b03f52976b6cb09b6a5b307b3186396160e57dc345cb1ee3bd6ee5ae331401fa
BLAKE2b-256 checksum
How to use checksums
118c593a7b3abf713a0e26327bd40491f96af8131211c3327b3e36288c0d8ede
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Release files / pymisb-2.0.2-py3-none-any.whl

Download URL pymisb-2.0.2-py3-none-any.whl
Size 50.4 kB
Tags Python 3
SHA-256 checksum
How to use checksums
8abb7b5bb32d5c9b6a0bf4c9265dc1c5351f1cc07c22078720b47436a2e93488
BLAKE2b-256 checksum
How to use checksums
7b9d2daebe75abcc8e64a9886a14c8679fdc647412fd2af2e73a69e6adc4011f
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Security for pymisb 2.0.2

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page