Skip to main content

pyst2110

SMPTE ST 2110 protocol for Python: RTP and RFC 4175 headers read and written, frame boundaries, sequence-loss accounting, pgroup geometry, and SDP.

REQUIREMENTS.md — the problem. SPEC.md — the design and its rationale. ROADMAP.md — what is not built yet.

Layer Owns
A transport binding (a NIC binding, a socket, a capture file) Moving bytes
pyst2110 RTP + RFC 4175 headers, geometry, SDP, ST 2110-21 timing
The consuming runtime Pixels — packing, unpacking, colour

The layer between a transport and a raster: it says what packet headers mean and where their payloads belong, and never moves a pixel itself. numpy is the only runtime dependency — no vendor SDK, no transport binding, no NIC and no licence, so it runs anywhere CI does.

Install

A git dependency pinned by tag. Publication to PyPI waits for a consumer depending on a released version (§road:pypi):

uv add "pyst2110 @ git+https://github.com/Fuse-Technical-Group/pyst2110@v0.2.0"

Usage

from pyst2110 import parse_sdp, parse_video_format

offer = open("flow.sdp").read()
flow = parse_sdp(offer)          # destination address, port, source filter
video = parse_video_format(offer)  # width, height, rate, sampling, depth

Parsing is vectorized over whole chunks: every function takes a two-dimensional uint8 array, one row per packet, and returns one array per field — the shape a header-data-split receiver already hands out.

Header fields are reported as the wire declared them, this being a parse and not a filter. A packet is free to name a row outside the image, so fits_raster masks the descriptors that name a place inside the flow's raster and a consumer places only those:

fits = fits_raster(video, payload.line, payload.offset_samples)
starts = raster_offset(video, payload.line[fits], payload.offset_samples[fits])

Sending is the same shape in reverse. A frame's headers are built once for a format and payload size, then stamped per frame with the only two fields that move — the sequence numbers and the media timestamp:

from pyst2110 import FrameHeaders, choose_payload_size, format_sdp, max_payload_size

payload_size = choose_payload_size(video, max_payload_size(video))
frame = FrameHeaders(video, payload_size, ssrc=0x1234ABCD)
for index in range(frames):
    headers = frame.stamp(index)   # (packets, 20) uint8, one row per packet
    ...                            # send each header with frame.frame_offset_octets
offer = format_sdp(flow, video, session_name="my sender")  # what was sent

The offer carries the media type parameters ST 2110-20 section 7.2 and ST 2110-21 section 8.1 require of a sender. TP describes the pacing, which is the transport's and not this library's, so sender_type= is the caller's to set — it defaults to 2110TPN. A multicast flow names its sender or passes any_source=True; see §spec:sdp for why that is a choice rather than a default.

An ST 2022-7 redundant pair is one offer and not two. format_dup_sdp writes RFC 7104's grouping — a session-level a=group:DUP over two m=video blocks, each with its own address and sender — and parse_dup_sdp reads both legs back, in the order the group names them:

from pyst2110 import format_dup_sdp, parse_dup_sdp

offer = format_dup_sdp(red, blue, video, session_name="my sender")
first, second = parse_dup_sdp(offer)

A document whose DUP tags and media blocks disagree is refused rather than read as a single-leg offer, and parse_sdp refuses a grouped offer for the same reason: a sender that emitted one leg where two were meant sends unprotected essence and reports success.

session_name defaults to the single space RFC 4566 section 5.3 prescribes for a session with no meaningful name — and at least one transmit SDK refuses it, so name the session where a sender will read the offer back. NVIDIA Rivermax reports 'x=<token>' format not found and fails stream creation, accepting the same document once it is named.

frame_offset_octets says which octets of the frame buffer each packet carries. Moving them is the consumer's, as on the receive side.

stamp hands back the same array every time, restamped in place — that is what keeps the loop above from allocating a frame of headers per frame. So headers is only valid until the next stamp: a caller queueing two frames at once copies the first.

API

Everything is re-exported from the top-level package, and the docstrings there are the authority; help(pyst2110) is the index.

Both paths are built: SDP parsing and emit — the single-leg offer and the ST 2022-7 pair — the RFC 3550 header parse, format geometry, sequence and frame tracking, RFC 4175 payload descriptors, the transmit header block, the ST 2022-7 reconstruction of one flow from two legs, and the ST 2110-21 timing model — read schedules, sender limits, and the two leaky buckets that judge a capture's emission instants. What is not built is listed in ROADMAP.md.

Development

uv sync
bash tools/ci.sh

tools/ci.sh is the gate CI runs — ruff, mypy, pytest.

License

MIT — see LICENSE. The standards this implements are public and an implementation of them should be too.

Release files for pyst2110 0.9.1

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

Source distribution (sdist)

Source distribution for pyst2110 0.9.1
File Size Uploaded
pyst2110-0.9.1.tar.gz 150.1 kB Details

Built distribution (wheel)

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

Total release size: 214.4 kB

Release files / pyst2110-0.9.1.tar.gz

Download URL pyst2110-0.9.1.tar.gz
Size 150.1 kB
Tags Source
SHA-256 checksum
How to use checksums
cfa035006c044a12efc94598eed78755251aacea305a48d3bffad50eaf6bd309
BLAKE2b-256 checksum
How to use checksums
827c790b808b41d6f94b2846fa50c3c833cb590880f080d3700a827f7ce36974
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

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.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 18, 2026.

Transparency log

Release files / pyst2110-0.9.1-py3-none-any.whl

Download URL pyst2110-0.9.1-py3-none-any.whl
Size 64.3 kB
Tags Python 3
SHA-256 checksum
How to use checksums
f9eef2461208dcd388ff46d909809d015369122be53aa81cb271436f71b900e1
BLAKE2b-256 checksum
How to use checksums
f880996f61a95deacb9ce4c90bfdd00eb401e99b34358f546d8f41540eee75b9
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

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.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 18, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.9.1 This release

2 release files

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