Multi-stream wfb_tx: per-stream FEC profiles, priority drain, waybeam SHM input (one process for all streams)#1
Merged
Conversation
Preparation for multi-stream mode, no behavior change: - Transmitter::get_radio_port() helper (channel_id low byte). - dump_stats() takes a stream_tag: -1 keeps the legacy TX_ANT stat line format byte-identical; >= 0 emits TX_ANT_S lines tagged with the stream's radio_port (unknown tags are ignored by the existing stats parsers). - Control-socket command handling moved out of data_source() into process_control_fd(fd, vector<Transmitter*>) so it can later address one of several per-stream transmitters; legacy commands keep operating on the first (only) stream. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
One wfb_tx process can now serve several streams, each defined by a repeatable -y spec: u=<udp_port>,p=<radio_port>[,k=][,n=][,T=][,F=] [,mcs=][,bw=][,gi=][,stbc=][,ldpc=][,vht=][,nss=]. Unset keys inherit the corresponding global option. Every stream gets its own Transmitter instance: own channel_id on the shared link_id, own FEC encoder and block state, own session key and announce timer, own fec_timeout and own radiotap header (per-stream MCS/bandwidth), exactly as N separate wfb_tx processes would - but in one event loop. Spec order is strict drain priority: ready inputs are serviced lowest index first and drained fully, so under radio saturation the kernel socket buffers of later (lower priority) streams overflow first. For layered video (e.g. SVC-T from OpenIPC waybeam_venc) the base layer is stream 0 and keeps flowing while droppable enhancement layers shed load first. Stats: per-stream PKT_S / TX_ANT_S lines tagged with the radio_port, plus the legacy aggregate PKT / TX_ANT lines so existing stdout parsers keep working unchanged. Without -y the behavior is identical to before. -y conflicts with -u/-U/-d/-I are rejected. In debug mode (-D) stream i emulates its wlans on ports debug_port + i*num_wlans + wlan_idx. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
A -y stream can now read from an OpenIPC waybeam_venc shared-memory
packet ring instead of a UDP socket: shm=<ring_name> attaches to
/dev/shm/<ring_name> (one complete RTP packet per slot), giving a
syscall-free producer path on the camera SoC.
src/venc_ring.{h,c} are vendored byte-identical from waybeam_venc
(MIT, (c) 2023 OpenIPC) apart from an attribution header.
Each shm stream runs one detached reader thread: futex-wait on the
ring, forward each packet through a SOCK_DGRAM socketpair whose other
end is polled by the multi-stream loop exactly like a UDP input - all
Transmitter state stays owned by the main thread, and a blocking
socketpair write gives natural backpressure (ring fills, producer
accounts the drops). When the producer respawns it recreates the ring
with a new epoch; on idle read timeouts the reader probes the shm name
and migrates to the new inode, logged as a re-attach.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
New control-port commands address one stream of a multi-stream wfb_tx by its radio_port: CMD_SET_FEC_STREAM, CMD_SET_RADIO_STREAM, CMD_GET_FEC_STREAM, CMD_GET_RADIO_STREAM. Unknown radio_port answers ENODEV. Legacy commands keep operating on the first stream, so existing tooling is unaffected. wfb_tx_cmd grows the matching subcommands, each taking -r radio_port: wfb_tx_cmd 7003 set_fec_stream -r 17 -k 4 -n 8 wfb_tx_cmd 7003 get_fec_stream -r 17 wfb_tx_cmd 7003 set_radio_stream -r 17 -M 3 wfb_tx_cmd 7003 get_radio_stream -r 17 This is the hook for closed-loop per-layer FEC adaptation: a link controller can retune the droppable enhancement stream's redundancy at runtime without touching the base layer session. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
wfb_ng/tests/test_multistream.py runs one wfb_tx with two -y streams against two per-stream wfb_rx aggregators through the existing no-radio debug harness and covers: per-stream delivery (incl. exact packet accounting per FEC profile), per-stream FEC recovery and loss isolation, cross-stream session isolation, per-stream control commands (set/get fec/radio, legacy command -> first stream, ENODEV for unknown radio_port). Running under TXProtocol also proves the new PKT_S / TX_ANT_S stdout lines pass the existing stats parser untouched. Changelog and README describe the feature, the strict-priority semantics (including the starvation caveat), the waybeam venc_ring shm input and the debug-port mapping. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Multi-stream
wfb_tx: one TX process serving several streams with per-stream FEC profiles, per-stream radiotap, strict drain priority, per-stream runtime control, and direct waybeam_venc SHM ring input.This is the follow-up promised in OpenIPC/waybeam_venc#85 ("single wfb-ng TX process consuming all waybeam streams with per-stream FEC profiles"). waybeam_venc 0.17.0 splits SVC-T layered video onto two transport channels so wfb-ng can protect each temporal layer differently — but with stock wfb-ng that costs one
wfb_txprocess per stream, and each FEC encoder schedules blind to the others.What it does
-ystream spec (repeatable):u=<udp_port>orshm=<venc_ring name>(input),p=<radio_port>(unique),k/n/T/F(FEC),mcs/bw/gi/stbc/ldpc/vht/nss(per-stream radiotap). Unset keys inherit the global options.Transmitterinstance per stream — own channel_id on the shared link_id, own FEC encoder/block state, own session key + announce timer, own fec_timeout, own radiotap header. Exactly what N processes do today, but in one event loop. RX side is unchanged: onewfb_rx -p <radio_port>per stream, as always.shm=input: attaches to a waybeam_venc shared-memory packet ring (/dev/shm/<name>;src/venc_ring.{h,c}vendored byte-identical from waybeam_venc, MIT, attribution headers). One detached reader thread per ring (futex wait → socketpair → main poll loop, so all Transmitter state stays single-threaded); producer respawn handled via epoch-based re-attach. Pairs with waybeam'soutgoing.server=shm://venc_wfb+outgoing.enhancePort(which createsvenc_wfb_enh).wfb_tx_cmd <port> {set,get}_{fec,radio}_stream -r <radio_port> …— the hook for closed-loop per-layer FEC adaptation. Legacy commands keep addressing the first stream; unknown radio_port →ENODEV.PKT_S/TX_ANT_Sstdout lines tagged with radio_port + the legacy aggregatePKT/TX_ANTlines. Existing parsers (wfb_ng/protocols.py) ignore unknown tags — verified by running the new tests under the stockTXProtocolparser.Commits (each independently buildable)
tx: extract control-socket handling, add stream-taggable stats— zero-behavior refactortx: multi-stream local mode with per-stream FEC and radiotap (-y)— coretx: waybeam venc_ring SHM input for multi-stream mode (shm= key)tx: per-stream control commands for multi-stream modetests + docs: multi-stream coverage, changelog and README sectionVerification
make all_binclean (the only two warnings are pre-existing on master — identical count);fec_test+libsodium_testpass.twisted.trial wfb_ng.tests→ 37 passed, 1 skipped (pre-existing tuntap skip), 0 failed — that's all 32 upstream tests (regression: legacy single-stream mode byte-compatible) plus 6 newtest_multistream.pycases:ENODEVfor unknown radio_port-Ddebug mode + socat relays): two UDP streams with different FEC, independent per-stream FEC-timeout behavior observed inPKT_Slines;set_fec_stream -r 17 -k 4 -n 8live retune while stream 16 stayed at 2/4.-y "shm=venc_test,p=16,k=2,n=4"→ ~20 pkt/s delivered;kill -9+ restart of the producer →shm ring venc_test recreated (epoch X -> Y), re-attachedand flow resumed.shm://venc_wfb+enhancePort, tcpdump radiotap verification of both channel_ids and per-stream MCS, and the full SVC-T per-layer FEC end-to-end (tracked as the POC for any future upstream discussion).Future work
wfb-server/services.pyopt-in to spawn one multi-stream TX per profile group (left out deliberately — this PR keeps the Python layer untouched).wfb_rxfor several channel_ids).🤖 Generated with Claude Code