Commit Graph

10 Commits

Author SHA1 Message Date
fegger 10870bc6c9 feat(app): implement Phase 5 local UDP sender->receiver loopback
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.
2026-09-07 11:02:40 +02:00
fegger b5e8d7174c feat(network): implement Phase 4 RTP framing with FU-A fragmentation
Add the sc_network library: RFC 3550 RtpHeader/RtpPacket serialize and
parse (the receiver tolerates CSRC lists, extension headers, and
padding by skipping/stripping them) and RFC 6184 H.264 payloading via
H264Packetizer/H264Depacketizer.

The packetizer splits Annex-B frames into NAL units (3- and 4-byte
start codes), emitting single-NAL packets or FU-A fragments within the
configured MTU, with the marker bit closing each frame and randomized
SSRC/sequence by default. The depacketizer reassembles access units
with 3-byte start codes, so both start-code widths round-trip
byte-exactly; frames damaged by sequence gaps or missing fragments
are dropped until the Phase 7 loss-recovery work.

test_rtp covers header and packet round-trips, malformed-input
rejections, splitter behavior, FU-A chunk bounds, full packetize ->
depacketize round-trip, gap dropping, marker-only frame separation,
sequence wrap, and empty inputs. meson test 3/3, valgrind clean.
2026-09-07 10:48:17 +02:00
fegger ce52f64e52 feat(capture): implement Phase 3 PipeWire/portal desktop capture
Implement the xdg-desktop-portal ScreenCast backend via libportal: a
blocking portal handshake (interactive source picker), a PipeWire stream
on the portal's node enumerating BGRx/BGRA/RGBx/RGBA, and a latest-frame
slot handing frames to next_frame(). stop() is thread-safe; teardown
follows the order PipeWire requires. All proxy operations run under the
thread-loop lock to satisfy the protocol extension context checks
('impl_ext_end_proxy called from wrong context' otherwise).

The encoder now accepts padded strides for packed RGB inputs (real
PipeWire row pitches) and maps the new PixelFormat::Bgrx to
AV_PIX_FMT_BGRA.

Add tools/capture_smoke: a manual smoke tool (interactive, not in
meson test) that captures N frames, encodes them, and writes a
self-contained Annex-B elementary stream with prepended SPS/PPS.

Validated manually on Wayland/Hyprland: 2256x1504 H.264 elementary
stream, ffprobe clean. Phase 3 marked complete in docs/PHASES.md.
2026-09-07 10:37:14 +02:00
fegger ac9dc02b51 feat(capture): give CaptureFactory a proper error channel
Replace the nullable unique_ptr returned by CaptureFactory::create()
with CaptureResult<std::unique_ptr<CaptureSession>> using the new
CaptureError/CaptureResult pattern, mirroring codec/error.h. The stub
now reports 'not implemented yet' as an error instead of returning
nullptr. Update handoff memory with the review outcome and
forward-looking notes for phases 3, 5, and 7.
2026-09-07 10:17:59 +02:00
fegger c6c062250e fix(codec): fix decoder packet leak and extradata over-read
Allocate packet payloads with av_new_packet so they are freed through the
owning AVBufferRef instead of leaking on every decode. Pad extradata with
AV_INPUT_BUFFER_PADDING_SIZE for FFmpeg's bitstream parsers. Reject
oversized frames before the int cast, handle unexpected EOF in the
EAGAIN-retry loops, and add the missing <limits> include. Document the
valgrind codec check in docs/RUNBOOK.md.

Validated with valgrind: 0 bytes definitely lost, 0 invalid reads.
2026-09-07 10:17:59 +02:00
fegger 6f4c670aa8 feat(capture): add Phase 3 PipeWire capture stub and config string safety
Add a linkable CaptureFactory stub so the capture API can be consumed

without unresolved symbols. Change remaining config string_view fields to

std::string to prevent dangling references. Update handoff memory.
2026-09-07 10:08:02 +02:00
fegger 71218b1b1f feat(codec): implement software H.264 encode/decode round-trip
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.
2026-09-07 10:07:50 +02:00
fegger 50369995c7 style: add clang-format config and reformat scaffolding files 2026-09-07 10:07:26 +02:00
fegger 975e3b2974 Complete Phase 1: add clock utility, unit test, build targets, and docs 2026-08-28 21:57:43 +02:00
fegger 742611b841 Scaffold C++20 screencast project with Meson, agent workflow, and phase plan 2026-08-28 21:54:32 +02:00