TV is ingested, demuxed and decoded once. Decoded audio is normalized to interleaved 48 kHz stereo F32 PCM by default, then shared with NDI and the independently controlled audio output. Audio output no longer opens a second TV subscription. The card’s native generator feeds these same output chains; it is not encoded and looped through TV software.
Both raw branches use identity single-segment=true to convert source PTS/DTS
to segment running-time. Their native clock synchronization paces file/HTTP
decoders before the bounded output queues. Both pipelines use the system
monotonic clock. This preserves source A/V offsets rather than independently
setting the first audio and video timestamps to zero. See GStreamer’s
clock and segment model.
The output schedule is:
NDI video and embedded audio = primary base time + media running-time
+ shared baseline + configured NDI delay
Separate audio = primary base time + media running-time
+ shared baseline
The shared baseline includes the source and native combiner/sink minimum
latencies plus scheduling allowance. Initial automatic latency negotiation is
retained until output has begun, then the baseline is set. NDI’s configured
delay is explicitly excluded from the separate-audio schedule. The local
appsrc PTS is mapped into its own pipeline’s base time, without changing the
original NDI buffer header or copying PCM payload. Python’s boxed
Gst.Buffer.copy() is not a writable-header copy; copy_region is required.
Global live-pipeline latency applies at synchronized sinks, so merely placing a synchronized audio bridge before a delayed queue is not sufficient. The local pipeline receives its own latency and clock mapping. See GStreamer’s latency design.
The existing ndi_delay_ms setting and its min/max clamps now apply to the
card as well as TV. Queue capacity/headroom and per-request extra buffering
also follow the same calculation. The card remains 1080p60 by default, with
native clock animation and a 1 kHz pulse once per second. Motion is installed
before PLAYING. Conversion to the configured NDI format (normally UYVY) occurs
before the delayed video queue. Progressive material keeps progressive caps;
only mixed-mode decoder caps receive the existing interleaved compatibility
override.
For USB/local outputs, keep lineout_sink_sync=true for clocked alignment.
Inferno always bypasses timestamp synchronization at the ALSA sink: the shared
handoff paces PCM and Inferno consumes it against the network PTP clock. Applying
the same timestamps again at this sink was observed to break the test-card
pulse into intermittent audio. Its actual output latency includes ALSA and
network buffering; the schedule above describes the handoff, not desk playback.
The service permits the Inferno transmitter to request FIFO priority 81 so
video processing cannot routinely delay its packet transmission.
Set desk/DSP audio delay while watching the actual NDI display and listening through the actual audio endpoint. Keep the same NDI delay, audio endpoint, receiver/display processing and output format when changing to TV.
The shared model removes the former card/TV routing mismatch. It does not fix broadcast lip-sync faults or cancel decoder, AVIO/Inferno, network, desk or display latency. Frame-rate changes and deinterlacing can alter receiver and display latency. Physical calibration is still required.
scripts/check_media_timing.py uses real GStreamer synchronized sink handoffs,
after clock waiting, rather than measuring arrival at a sink pad. It compares
100 and 500 ms using generated A/V and a synchronized Theora/Vorbis fixture
through URI decoding, at matching 320x180/60 UYVY output. It covers late audio
attachment, stop/restart, buffer-header immutability, queue limits, stalled
teardown, EOS and progressive/mixed caps. Existing lifecycle checks cover
startup cancellation and rapid starts/stops.
Measured locally on GStreamer 1.24.2: NDI-equivalent video minus separate audio was approximately 100 and 500 ms in both source modes, changing by 400 ms. Embedded A/V scheduling differed by less than 1 ms in these short controlled tests. These are software sink-boundary measurements, not screen/PA accuracy.
scripts/measure_media_device.py is an explicitly opt-in hardware check. It
pauses and restores an active installed test card, including output settings.
On 192.168.0.31 with GStreamer 1.26.2, a temporary candidate using the actual
NDI sink sustained 1080p60 multicast with zero reported NDI drops at both
delays, with separate audio off/on. Missing ALSA hardware, PTP-unready and
simulated PTP loss did not stop NDI. The installed Main release and its
original stream/settings were restored after each test.
Short candidate-process samples (10 seconds per measurement, not a soak):
| Delay | Output | CPU, % of one core | RSS, KiB |
|---|---|---|---|
| 100 ms | NDI only | 263.2 | 155808 |
| 100 ms | NDI + clocked test sink | 266.4 | 156400 |
| 500 ms | NDI only | 267.4 | 293936 |
| 500 ms | NDI + clocked test sink | 270.5 | 294064 |
The 500 ms UYVY video queue held about 120 MB (115 MiB) in the sample. RSS includes native pools and allocator retention; queue capacity is not occupancy. ALSA null also passed routing/lifecycle tests but consumed roughly an extra core in this environment, so it is not used to estimate physical audio-device CPU cost. The clocked test sink measures handoff/conversion overhead only.
At test time the unit had no mapped TV channels, no USB audio device and no ready PTP leader. Live RF/broadcast comparison, real Dante delivery, receiver screen/PA alignment, and an extended soak remain site acceptance checks. No measured percentage reduction in live TV decoding load is claimed.
On 192.168.20.105, live Dante diagnosis found two independent faults: the service’s default realtime-priority limit blocked Inferno’s FIFO request, and ALSA timestamp synchronization broke up the paced PCM. After permitting priority 81 and bypassing sink synchronization, a 30-second packet capture contained the expected tone pulse every second with no capture drops or transmit-timing warnings. The receiver subsequently reported zero late packets over 21 minutes 17 seconds, with a 4.6 ms peak against its 10 ms receive limit. These observations cover the test card and this network; they do not establish long-term clock-drift behavior or physical A/V alignment for all sources.