The receiver decoded H.264 to YUV420P, converted it to RGBA via a
CPU-intensive swscale pass, then uploaded 4 bytes/pixel to an SDL
texture — only for the GPU to convert back to RGB during rendering.
This eliminated the swscale pass entirely (40-60% of receiver CPU at
1080p) and cut the texture upload by 62%.
- DecodedFrame now carries three YUV420P planes with their strides
instead of a packed RGBA buffer; the decoder copies the planes
directly from the AVFrame (zero conversion for the common software
path). Non-YUV420P decoder output (e.g. NV12 from v4l2m2m) is
converted once to YUV420P.
- The SDL renderer uploads via SDL_UpdateYUVTexture with
SDL_PIXELFORMAT_IYUV; the GPU does the YUV→RGB conversion during
rendering.
- Decoder threading: slice-level with 4 threads (parallelizes within a
frame, no added latency), not frame-level (which buffers multiple
frames — the initial thread_count=0 broke the loopback test because
the H.264 decoder introduced a multi-frame delay before producing
output).
- The round-trip test converts decoded YUV back to RGBA for pixel
comparison via a test-local swscale call (the pipeline itself never
converts).
meson test 5/5 in both configurations, valgrind clean.
Wire the first end-to-end pipeline: capture -> encode -> packetize ->
UDP -> depacketize -> decode -> render.
- UdpRtpTransport: raw POSIX UDP sockets (IPv4 via getaddrinfo), a
receive jthread woken by socket close on stop; port 0 skips binding
so the sender uses an OS-assigned source port. ASIO stays deferred
to the signaling phase per ARCHITECTURE.md.
- SdlRenderer: SDL3 window/renderer with RGBA texture upload; the
texture is recreated on resolution change. RendererFactory now
returns RendererResult so SDL init failures carry a message,
mirroring the codec/capture error patterns.
- screencast binary: parse_cli plus SenderPipeline/ReceiverPipeline
per the app scaffolds; the sender creates its encoder once capture
reports real dimensions, the receiver keeps a bounded 3-frame queue
to hold latency down and renders on its own thread until the window
closes. cli argv signature fixed to 'const char* const*' so main's
argv converts implicitly.
- Encoder: drop AV_CODEC_FLAG_GLOBAL_HEADER so libx264 repeats SPS/PPS
in-band at each keyframe -- the receiver decodes from the bitstream
alone, which also makes mid-stream joins and later PLI recovery
work without out-of-band parameter negotiation. The round-trip test
now exercises exactly that path.
- tests: new udp-loopback integration test pushes synthetic frames
through a real localhost socket and decodes 10/10 frames with the
right dimensions; valgrind clean (loopback + codec). meson test 4/4.
Manual validation on the desktop (receiver window shows the captured
desktop) is documented in docs/RUNBOOK.md.
Add FFmpeg-based encoder/decoder with SPS/PPS extradata, Annex-B output
normalization, low-latency libx264 settings, and a round-trip unit test.
Includes review hardening: cached SwsContext, bitrate-only rate control,
std::byte/uin8_t cast helpers, and richer test assertions.