DecodedFrame pixels are AV_PIX_FMT_RGBA (memory order R,G,B,A), but
SDL names 32-bit formats MSB-first, so SDL_PIXELFORMAT_RGBA8888 reads
memory as A,B,G,R: the opaque alpha byte was displayed as red and the
image got a strong red tint. The correct constant is
SDL_PIXELFORMAT_ABGR8888.
Verified with solid red/green/blue synthetic RTP feeds through the
real receiver: each hue now lands on its reference YUV values
(red Y79/U87/V247, green Y148/U48/V27, blue Y34/U248/V108), where
green previously displayed as magenta.
The receiver window never appeared on Wayland. Two causes:
- The window was created on the main thread while event pumping and
presenting ran on the render thread. SDL's Wayland backend requires a
window's creation, event processing, drawing, and destruction to
happen on one thread; a cross-thread surface simply never maps, with
no error reported. The renderer now lives entirely on the render
thread, with a condition-variable handshake so ReceiverPipeline::
start() still reports renderer failures and timeouts.
- On Wayland a window is invisible until the first render commit, so
even a healthy receiver showed nothing while waiting for a stream.
The renderer presents one blank frame at init: the window is visible
immediately, black until video arrives.
Also log 'stream started (WxH)' when the first frame decodes and the
first rendering failure, which is what made the remaining debugging
observable.
Validated headlessly end-to-end: a synthetic solid-color RTP feed drove
the real receiver over localhost UDP; the window mapped, reported
'stream started (320x240)', and a grim screenshot of the window region
showed the fed color (V=198, SATAVG=74 during the red feed). Phase 5
marked complete in docs/PHASES.md; current phase is now Phase 6.
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.