Skip to content

svctx: --live, each NAL injected as it arrives with the producer's capture stamp - #481

Merged
josephnef merged 2 commits into
masterfrom
svctx-live
Oct 10, 2026
Merged

josephnef merged 2 commits into
masterfrom
svctx-live

Conversation

@josephnef

Copy link
Copy Markdown
Collaborator

What

Follow-up #475 from the stream-timing PR. svctx pre-read a clip and replayed it, so its per-frame timing field had no producer capture (cap=0).

  • svctx --live: records are read as they arrive through the shared stdin control escape; a CAPTURE_TS applies to the next NAL; each NAL is injected once; EOF ends the run. One stamp per NAL, claimed by its first fragment; later fragments measure from the read, so the per-NAL producer-delay check holds through fragmentation. The replay mode is unchanged and remains the default.
  • tests/gen_svc_nals.py is now a producer too: --repeat, --pace-us, --capture-ts, --capture-delay N:MS (stdlib-only stdin_ctl.py).
  • tests/stream_timing_onair.sh gains an svctx-live phase with the same 20 ms every-10th producer delay check as streamtx.

Measured (8812CU → CF-924AC witness, 20 s)

phase result
svctx-live producer delay recovered as 20.08 ms, on 6.9 % of stamped frames; 1028 frames with a hardware-clocked latency
svctx (replay) unchanged: 2347 latency frames, p50 133 µs
tests/svctx_uep_witness.sh ALL PASS — three rates on air

The 6.9 % (not 10 %) delayed share is explained in docs/stream-timing.md: the every-10th lands unevenly on the clip's NAL classes and the fast-rate enhancement frames are lost more often than the robust base frames, so the share is partly a delivery figure. The step itself is exact.

Closes #475.

🤖 Generated with Claude Code

…pture stamp

Default stays the replay loop (read the clip, inject forever: a stable rate
histogram for a witness). --live reads records as they arrive through the
shared stdin control escape, takes CAPTURE_TS for the next NAL, injects it
once and ends on EOF. One stamp per NAL: its first fragment claims it
(has_capture=1, capture->send from the producer's instant); later fragments
measure from the read, so the per-NAL delay check holds through
fragmentation. SIGINT/SIGTERM end either mode through the ordinary exit path.

tests/gen_svc_nals.py becomes a producer as well as a dump: --repeat,
--pace-us (flush per NAL), --capture-ts / --capture-delay via
tools/precoder/stdin_ctl.py (stdlib only). The harness gains an svctx-live
phase: a paced, stamping producer with the 20 ms every-10th delay.

On air (8812CU -> CF-924AC witness, 20 s): svctx-live recovers the producer
delay as 20.08 ms on 6.9% of stamped frames (the every-10th lands unevenly on
the clip's NAL classes and fast-rate frames are lost more often than robust
ones); replay-mode svctx unchanged (lat p50 133 us, 2347 frames);
tests/svctx_uep_witness.sh ALL PASS (three rates on air).

Closes #475.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@qodo-free-for-open-source-projects

Copy link
Copy Markdown

PR Summary by Qodo

Add live, capture-stamped NAL injection to svctx

✨ Enhancement 🧪 Tests 📝 Documentation 🕐 20-40 Minutes

Grey Divider

AI Description

• Add live NAL injection with producer capture timestamps while preserving replay as the default.
• Extend the synthetic NAL producer with pacing, repetition, and capture-delay controls.
• Validate producer-delay telemetry on air and document the measured results.
Diagram

graph TD
  Producer["NAL producer"] --> Mode{"TX mode"} --> Live["Live reader"] --> Inject["SVC injector"] --> Timing["Timing stamp"] --> Radio["Radio TX"] --> Witness["RX witness"]
  Mode --> Replay["Clip replay"] --> Inject
Loading
High-Level Assessment

Keep the separate live and replay paths with a shared injector. This preserves replay behavior while reusing the existing stdin-control and timing facilities; routing NALs through streamtx instead would lose svctx's layer-specific rate policy.

Files changed (5) +178 / -65

Enhancement (2) +138 / -60
main.cppInject arriving NALs with capture timing in live mode +106/-53

Inject arriving NALs with capture timing in live mode

• Adds --live to consume stdin controls and inject each NAL once until EOF, while retaining default clip replay. A shared injector preserves layer-rate selection and fragmentation; only a NAL's first fragment claims its producer capture stamp.

examples/svctx/main.cpp

gen_svc_nals.pyAdd paced, repeatable capture-stamped NAL output +32/-7

Add paced, repeatable capture-stamped NAL output

• Adds repeat and pacing options and uses the existing stdlib-only stdin control helper for capture timestamps and scheduled producer delays. Retains the positional clip-size arguments.

tests/gen_svc_nals.py

Tests (1) +23 / -0
stream_timing_onair.shValidate live SVC capture timing on air +23/-0

Validate live SVC capture timing on air

• Adds an opt-in svctx-live phase that feeds paced, stamped NALs through the transmitter and checks the witness capture for the configured producer-delay step.

tests/stream_timing_onair.sh

Documentation (2) +17 / -5
stream-timing.mdDocument live svctx timing results +14/-3

Document live svctx timing results

• Distinguishes unstamped replay from stamped live injection and records the measured 20.08 ms producer-delay step. Explains why the received delayed-frame share differs from the producer's every-tenth schedule.

docs/stream-timing.md

README.mdDescribe the svctx-live on-air phase +3/-2

Describe the svctx-live on-air phase

• Adds the live SVC phase to the timing harness documentation and identifies its paced, capture-stamped producer-delay check.

tests/README.md

@qodo-free-for-open-source-projects

qodo-free-for-open-source-projects Bot commented Oct 10, 2026 •

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (0) 📘 Rule violations (0) 📎 Requirement gaps (0) 🎨 UX issues (0) 🔗 Cross-repo conflicts (0) 📜 Skill insights (0)

Grey Divider


Remediation recommended

1. A final capture stamp goes uncounted ✓ Resolved
Description
svctx breaks on EOF without clearing or counting a pending capture stamp, then reports
timing.capture_dropped() in stream.done. If a producer sends CAPTURE_TS and closes before
sending its NAL, the reported count stays zero even though that stamp had no following record.
Code

examples/svctx/main.cpp[281]

+      if (r == stream_stdin::RecordResult::Eof) break;
Evidence
capture_stamp() increments the dropped count only when another stamp replaces a pending one;
take_capture() clears the pending stamp only when a data record arrives. The new EOF branch does
neither before reporting the count, while the control protocol explicitly describes a stamp followed
by EOF as dropped.

examples/svctx/main.cpp[279-302]
examples/common/stream_timing_tx.h[191-205]
examples/common/stream_stdin.h[123-132]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
A valid capture stamp followed by EOF is omitted from the live run's `capture_dropped` count.
## Fix Focus Areas
- examples/svctx/main.cpp[279-302]
- examples/common/stream_timing_tx.h[191-205]
## Recommended Fix
Add a timing method that discards and counts a pending capture stamp, and call it when the live input ends before emitting `stream.done`.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Tip of the day
💡 Did you know, you can copy the agent prompt from any finding and feed it to your IDE agent

More tips ↗ | Customize Qodo ↗ | Qodo docs ↗

Grey Divider

Qodo Logo

Comment thread examples/svctx/main.cpp
…opped

StreamTimingTx::input_ended() folds a stamp that no record followed into
capture_dropped; svctx --live and streamtx call it before stream.done. A
stamp-only stdin into svctx --live now reports capture_dropped:1.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@josephnef
josephnef merged commit c7f9b6d into master Oct 10, 2026
29 checks passed
@josephnef
josephnef deleted the svctx-live branch October 10, 2026 11:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

svctx: live stdin mode with the CAPTURE_TS control TLV

1 participant