fix(codec): bound keyframe bursts with VBV and microsecond time_base
The first real sender/receiver run failed permanently: the receiver reported 'non-existing PPS 0' for every frame. Without a VBV, a 2256x1504 IDR keyframe bursts hundreds of kilobytes of back-to-back FU-A packets, overflowing the ~208KB default UDP receive buffer; the resulting sequence gap made the depacketizer drop whole keyframes including their in-band SPS/PPS, so the decoder never initialized and never recovered, because every keyframe burst overflowed again. - Encoder: add rc_max_rate = bitrate and rc_buffer_size = bitrate*2/fps, capping any single frame to about two frame periods of bytes (~40KB at the 4Mbps default). - Encoder: switch the time_base to microseconds. It was derived from the configured frame rate, which quantized capture-rate timestamps and duplicated pts; RTP timestamps are now lossless. - Transport: request a 4MB SO_RCVBUF on the receive socket (best-effort; the kernel clamps to net.core.rmem_max). meson test 4/4, valgrind clean (loopback + codec).
This commit is contained in:
+14
-7
@@ -21,6 +21,16 @@ loopback validation pending. See bottom for the run commands.
|
||||
in-band at every keyframe; a receiver now decodes from the bitstream
|
||||
alone (mid-stream join, PLI recovery-ready). `get_extradata()` is empty
|
||||
in this mode; codec round-trip test updated to match the streaming path.
|
||||
- **Burst-control fixes after the first real run** (receiver saw
|
||||
`non-existing PPS 0` forever): without VBV, a 2256x1504 IDR burst (~100s
|
||||
of KB of back-to-back FU-A packets) overflowed the ~208KB default UDP
|
||||
receive buffer; the sequence gap made drop-on-damage discard whole
|
||||
keyframes *including their in-band SPS/PPS*, so the receiver never
|
||||
recovered. Fixes: encoder VBV (`rc_max_rate = bitrate`,
|
||||
`rc_buffer_size = bitrate*2/fps` — caps any keyframe to ~2 frame periods
|
||||
of bytes), microsecond encoder time_base (kills the 25fps pts
|
||||
quantization that duplicated timestamps), and a best-effort 4MB
|
||||
SO_RCVBUF on the receive socket (kernel clamps to rmem_max).
|
||||
- **Automated validation**: `meson test` 4/4 — new `udp loopback` test
|
||||
encodes synthetic frames, packetizes, sends over a real localhost UDP
|
||||
socket, depacketizes, and decodes 10/10 frames with correct dimensions.
|
||||
@@ -69,15 +79,12 @@ None.
|
||||
|
||||
## Forward-looking review notes (for later phases)
|
||||
|
||||
- Encoder sets no VBV (`maxrate`/`buffer_size`) — ABR only; add for smoother
|
||||
UDP streaming in Phase 7.
|
||||
- Encoder PTS caveat: time_base derives from the configured frame rate
|
||||
(default 25fps) while portal frames arrive at monitor refresh (often 60Hz),
|
||||
so pts values quantize and can repeat. RTP timestamps come from the capture
|
||||
clock instead, so streaming is unaffected; revisit if decoder-side
|
||||
presentation timing ever matters.
|
||||
- `RtpTransport::start/send` return plain bools (scaffold API); error
|
||||
messages are lost — consider an error channel when signaling lands.
|
||||
- Very high bitrates can still exceed even a raised receive buffer if
|
||||
`net.core.rmem_max` is low on the receiver; the encoder VBV bounds
|
||||
bursts to ~2 frame periods, so this needs `--bitrate` ≳ 100 Mbps to
|
||||
matter. Document in RUNBOOK.
|
||||
- DMA-BUF-only portal streams are rejected with a clear message (hardware
|
||||
path is Phase 7).
|
||||
- No negative-path tests yet (bad config, bad stride, undersized buffer).
|
||||
|
||||
Reference in New Issue
Block a user