devlab.py spins up self-contained upstream servers (no external network) that
rtp2httpd can proxy, so you can develop the web player against the scenarios it
needs to support:
| Scenario | Delivery | rtp2httpd proxy | Codecs |
|---|---|---|---|
| HLS-TS live | HTTP .m3u8 + .ts |
/http |
h264-mp2, hevc-aac |
| HLS-fMP4 live | HTTP .m3u8 + .m4s |
/http |
h264-aac, hevc-aac |
| HLS catchup | HTTP HLS VOD (playseek) |
/http |
all of the above |
| mpegts (RTSP) | RTSP TS live + catchup | /rtsp |
h264-mp2, hevc-aac |
| mpegts (multicast) | RTP multicast live | /rtp |
h264-mp2, hevc-ac3, hevc-eac3 |
| mpegts (scan) | RTP multicast live | /rtp |
1080i / 1080p / 2160p (see below) |
| external file | RTP multicast (looped) | /rtp |
whatever the .ts file contains |
h264-mp2= H.264 video + MPEG-1/2 Layer II audiohevc-aac= H.265/HEVC video + AAC audiohevc-ac3= H.265/HEVC video + AC-3 audiohevc-eac3= H.265/HEVC video + E-AC-3 audio
HLS live is offered in both segment specs: HLS-TS (MPEG-TS .ts segments)
and HLS-fMP4 (an init.mp4 referenced via #EXT-X-MAP + .m4s fragments).
fMP4 carries AAC audio (MP2-in-MP4 is unsupported), so fMP4 channels use AAC.
HLS catchup is a real HLS VOD: each playseek window returns an
index.m3u8 (#EXT-X-PLAYLIST-TYPE:VOD) listing fixed-duration .ts slices.
The playlist is produced instantly; each slice is encoded lazily on first
request with that slice's absolute wall-clock time burned in, so even a
multi-hour window is time-correct without pre-encoding the whole thing.
The generated channel list names RTSP MPEG-TS channels as mpegts (RTSP) and
multicast MPEG-TS channels as mpegts (multicast), matching the separate
RTSP_PROFILES and MCAST_PROFILES sets in devlab.py.
Multicast channels use groups 239.255.0.20+ on --mcast-port (default 5004).
ffmpeg sends them via the OS default multicast route, and rtp2httpd joins them
without an explicit upstream interface.
Channels for the web player's automatic deinterlacing (MCAST_SCAN_CHANNELS):
- mcast 1080i (h264-mp2) — true interlaced TFF H.264: testsrc2 generated at
50 fps,
tinterlace=interleave_topweaves adjacent frames into the fields of one 25 fps frame, so the moving pattern combs on every motion. The player's heuristic detector must activate on this channel and the combing must disappear. - mcast 1080p-combed (h264-mp2) — same weaved TFF content but encoded and
flagged as progressive (no
fieldorder, no interlaced encoder flags), so the codec-metadata hint stays silent. Only the heuristic comb detector can activate deinterlacing — this validates the heuristic path end to end. - mcast 1080p (h264-mp2) — progressive control at the resolution gate; the detector must NOT activate (no false positive).
- mcast 2160p (hevc-aac) — above-1080 control; deinterlacing is gated off regardless of content (browser HEVC support permitting, see Notes).
Publish any external .ts as a multicast live channel (stream-copied, so the
exact codecs/bitstream are relayed) — useful for reproducing a stream attached
to a bug report:
uv run python tools/devlab/devlab.py --ts-file /path/to/user-report.ts
# adds a "file <name>" channel proxied through rtp2httpdCatchup video burns the requested playseek begin time into every frame
(SEEK <yyyy-mm-dd hh-mm-ss> UTC) plus an advancing elapsed counter. Request a
playseek starting at 08:30:00 and the picture shows SEEK 2026-06-30 08-30-00 UTC — proving the time → picture mapping end to end.
# 1. start the upstreams (writes /tmp/r2h-devlab.conf)
uv run python tools/devlab/devlab.py
# The script prints the rtp2httpd command.
# 2. in another shell, run rtp2httpd against the generated config
./build/rtp2httpd -c /tmp/r2h-devlab.conf
# 3. open the player and pick a channel
# http://127.0.0.1:5140/playerRequires ffmpeg on PATH with drawtext, libx264, libx265, aac, and
mp2. On macOS with Homebrew, install a full build:
brew install ffmpeg-fullVerify drawtext before starting the lab:
ffmpeg -hide_banner -filters | grep drawtextDefaults: HTTP origin 127.0.0.1:8881, RTSP origin 127.0.0.1:8554,
rtp2httpd port 5140. Run uv run python tools/devlab/devlab.py --help for
options.
# live codecs
ffprobe -v error -show_entries stream=codec_type,codec_name \
"http://127.0.0.1:5140/HLS/HLS%20%28hevc-aac%29"
# catchup: extract a frame and confirm the burned SEEK time matches the request
ffmpeg -ss 9 -i "http://127.0.0.1:5140/mpegts%20%28RTSP%29/mpegts%20%28RTSP%29%20%28h264-mp2%29/catchup?playseek=20260630083000-20260630093000" \
-frames:v 1 -y /tmp/seek.png- HEVC video and AC-3/E-AC-3 audio playback in the browser depend on the
browser's Media Source Extensions support; Chromium on Linux usually lacks
both even though the stream/relay is correct. Use
ffprobe/frame extraction to validate the relay (thehevc-ac3/hevc-eac3channels are mainly for relay/transmux development and capable players/devices). - HLS catchup is an HLS VOD (
m3u8+.tsslices); the player expandscatchup-sourceper window viabuildCatchupSegmentsand the loader detects the nested playlist (HLS mode). mpegts/RTSP catchup instead streams continuous TS per window.