feat(venc): SVC-T per-layer FEC split — outgoing.enhancePort + thinEnhance (both backends)#85
Closed
josephnef wants to merge 1 commit into
Closed
Conversation
…hance (both backends) Route SVC-T enhancement-layer frames (refType ENHANCE_*) through a second transport channel — same host, dedicated UDP port, or a "<name>_enh" SHM ring — while base frames stay on outgoing.server. Both channels carry ONE RTP session (shared SSRC + seq space), so the ground station forwards both wfb_rx outputs to a single decoder port and RTP reordering merges them. This enables unequal error protection on wifibroadcast links: wfb-ng applies FEC per stream, so the always-decodable base layer can ride strong FEC while the droppable enhancement layer rides light FEC. - outgoing.enhancePort (MUT_RESTART, 0=off): gated on a refPred resilience preset (rally/range/fpv); non-fatal bring-up; port-collision validation; live outgoing.server retargets follow on both channels or disable the split with a warning. - outgoing.thinEnhance (MUT_LIVE): drop enhancement frames pre- packetization (seq not consumed, mirror-mode recorder keeps full rate); IDR issued on resume. - include/venc_svct.h: shared refType constants + classifiers replace the duplicated per-backend NOTFORREF defines; TRAIL_N rewrite unchanged. - Sidecar transport trailer now describes the channel each frame used. - HTTP API contract 0.11.0; README per-layer FEC example; tests for the classifier, URI derivation, config layers, collision 409s, live toggle. Host tests: 1699 passed. make verify + pre-pr green. Bench validation pending (devices offline); refType==3 classification flagged for device confirmation on enhance>1 presets. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Author
|
The single-process wfb-ng counterpart mentioned in the description is no longer a future item — it's implemented and merged in josephnef/wfb-ng#1 (multi-stream Real-radio test reports are very welcome — especially saturation behavior (the enhancement stream should shed load first; per-stream |
Collaborator
|
Thanks for the PR and the suggestion, this is out of scope for waybeam venc to implement . |
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
waybeam already encodes SVC-T temporal layers (resilience presets
rally/range/fpv→MI_VENC_SetRefParam) and marks droppable frames TRAIL_N in the bitstream — but every frame still left on one UDP socket/SHM ring, so wfb-ng could only apply one FEC setting to everything. The README even called this out: "Real-world refPred benefit on a lossy link depends on the sender applying per-layer FEC priority… Without that integration the pyramid is roughly neutral."This PR is that integration: it splits the stream by temporal layer at the transport level, so the wifibroadcast link can protect each layer differently.
What end-users get
rally, deeper forrange/fpv). Video keeps flowing; it just gets smoother→sparser instead of breaking.outgoing.thinEnhance(live toggle): enhancement frames aren't sent at all, while mirror-mode SD recording keeps the full frame rate (record full, stream base).wfb_rxoutputs to the same decoder port; standard RTP reordering merges them. Any existing player (pixelpilot, gstreamer, ffplay) just works.enhancePort=0keeps today's single-channel behavior byte-for-byte. The enhance channel is auxiliary: if it fails to open (or a live server retarget can't host it), waybeam logs a warning and continues single-channel — the base video link never dies because of it.How FEC with wfb-ng works now (with this PR)
wfb-ng applies FEC per stream (per UDP port → radio_port). The split maps temporal layers onto that model 1:1 — per-layer FEC becomes pure configuration:
With
shm://output, a nonzeroenhancePortinstead creates a second ring/dev/shm/<name>_enhfor a secondwfb_txinstance (zero-syscall path preserved for both layers).Companion: single wfb-ng TX process for all streams — implemented, please test
Stock wfb-ng needs one wfb_tx/wfb_rx pair per stream — with this PR a typical camera already runs base + enhance (+ audio + sidecar = up to four pairs), and each FEC encoder schedules its blocks blind to the others. The single-process counterpart is now implemented and merged in a wfb-ng fork:
→ josephnef/wfb-ng#1 — Multi-stream wfb_tx: per-stream FEC profiles, priority drain, waybeam SHM input (merged into the fork's
master; full design, commit-by-commit notes and verification there)One
wfb_txserves all streams: per-stream radio_port + FECk/n+ session key + radiotap (per-stream MCS/BW), strict drain priority (base > enhance — under radio saturation the enhancement stream sheds load first), directvenc_wfbSHM ring input (vendoredvenc_ring, survives waybeam respawns), and per-stream runtime control (wfb_tx_cmd ... set_fec_stream -r <radio_port>). Without-ythe binary behaves exactly like upstream.How to test with this PR
Verified so far: full wfb-ng test suite (37/38, incl. 6 new multi-stream cases), loopback runs, SHM producer kill/respawn re-attach. Real-radio reports very welcome — especially the saturation behavior (enhancement stream should shed first; watch the per-stream
PKT_Slines) and decoder-side merge quality.Also in the fork: josephnef/wfb-ng#2 (draft) — upstream wfb-ng PR #450 (driver TX-buffer traffic shaper, by tipoman9) rebased onto the multi-stream base, exploring how buffer-level backpressure composes with priority drain.
Upstreaming to svpcom/wfb-ng is a separate step (their policy is Telegram-first discussion with bench results); test reports from this PR feed exactly that.
Other follow-ups carried from the plan: 3-way per-sublayer split (T1 vs T2 → three FEC classes), per-layer split for the dual-stream ch1, enhance-channel stats in
/api/v1/transport/status, and sidecar layer-tagging.Implementation notes
outgoing.enhancePort(uint16,MUT_RESTART, default 0=off): secondStar6eOutput/MarukoOutputinstance derived from the base URI (venc_config_derive_enhance_uri()— UDP port override / SHM_enhsuffix /unix://rejected). Gated onref_base > 0(without refPred no frame classifies as enhancement — logged). Validated against collisions with server/audio/sidecar ports (HTTP 409). Liveoutgoing.serverretargets follow on both channels or disable the split.outgoing.thinEnhance(bool,MUT_LIVE): gates the send before packetization — RTP seq not consumed (receiver sees a gapless base stream), recorder writes are downstream of the gate and unaffected. IDR on resume so the re-appearing layer starts from a clean reference chain.include/venc_svct.h: shared refType constants +venc_svct_frame_is_enhance()/_is_notforref()classifiers replace the duplicated per-backend*_REFTYPE_ENHANCE_P_NOTFORREFdefines. TRAIL_N rewriting semantics unchanged (NOTFORREF only).Star6eVideoState/MarukoStreamRuntime), which is what keeps one SSRC/seq space across both ports for free. Sidecar pressure observation + transport trailer now describe the channel each frame actually used.g_fields[]+aliases/WebUI/default JSON); HTTP API contract bumped to 0.11.0.Validation
make build SOC_BUILD=star6emake build SOC_BUILD=marukomake test— 1699 passed, 0 failed (new: classifier truth table, URI derivation, config load/save/roundtrip incl. byte-equal layout, collision 409s, thinEnhance 501/200 live-set, maruko config mirror)make verify(both backends + webui-check) andmake pre-prPending bench validation (devices were offline): rally +⚠️ One open assumption: refType==3 (
enhancePort=5605→ tcpdump expects ~50/50 frame split, base port only IDR/base frames; merged-forwarding decodes at full rate, base-only at ½ rate;thinEnhancelive toggle + IDR-on-resume; live server retarget follows on both ports;enhancePort=0regression.ENHANCE_P_REFBYENHANCE) follows the vendor enum layout but only appears withref_enhance > 1(range/fpv) — flagged in code; if base frames ever classify as enhance on the bench, the classifier narrows to==4only.Checklist
VERSIONupdated for this PR (0.16.0 → 0.17.0).HISTORY.mdupdated with this PR's user-visible changes.HTTP API Contract (required when HTTP behavior changes)
documentation/HTTP_API_CONTRACT.mdupdated in this PR (new subsection + capability rows + changelog).HTTP API Design Guardrails
/api/v1/set, no new endpoints).Notes
🤖 Generated with Claude Code