fegger 343c6b45e6 fix(systemd): give the receiver service its own VT instead of tty1
The receiver service never started on the Pi: systemd blocks before
exec in acquire_terminal(), waiting for a controlling terminal that
the console session or getty on tty1 already owns. Symptoms: the
status shows the main PID as "(screencast)" with Tasks:1 and ~40ms
CPU, a silent journal, and no mDNS announcement — the binary never
ran. Reproduced locally with a transient unit (the desktop's Wayland
session owns tty1) and diagnosed by gdb-attaching the stuck process.

The service now owns a dedicated free VT (tty7) so acquisition is
immediate, with a tolerant ExecStartPre chvt to make the screencast
the on-screen console at boot. tty1 keeps the console login;
Ctrl+Alt+F1/Ctrl+Alt+F7 switch between them. The previous advice to
enable Console Autologin actively caused the hang and is removed
from the unit, RUNBOOK, and install script notes.

Validated locally: the same properties via systemd-run give a
running receiver with all ~15 threads alive that announces itself
and is found by --discover; verified unit passes systemd-analyze
verify; graceful stop withdraws mDNS.
2026-09-07 13:45:47 +02:00

screen_cast

A native Linux peer-to-peer screencast application.

  • Send your desktop or a window to another Linux machine.
  • Receive a stream and render it in a window.
  • Discover receivers on the LAN via mDNS/Avahi; negotiate sessions with JSON signaling; stream H.264 over RTP/UDP.

Built with C++20, Meson, PipeWire, FFmpeg, and SDL3.

Quick start

Requirements:

  • C++20 compiler with <format> (GCC 13+, Clang 18+)
  • Meson >= 0.63, Ninja
  • FFmpeg development packages (libavcodec, libavutil, libswscale)
  • SDL3 development package (sdl3)
  • nlohmann JSON (nlohmann_json) and Avahi client (avahi-client)
  • For the sender only: PipeWire dev (libpipewire-0.3) and libportal

Build and run tests:

meson setup build
meson compile -C build
meson test -C build --print-errorlogs

Stream between two machines:

screencast --receive        # machine A: announces itself, opens a window
screencast --send          # machine B: discovers A, negotiates, streams

See docs/RUNBOOK.md for all modes, flags, and validation procedures.

Receiver on a small ARM board (e.g. Raspberry Pi Zero 2 W)

The receiver does not need the sender's PipeWire/portal capture stack. On the board, run:

sudo ./scripts/install-receiver.sh
screencast --receive

The script installs the dependencies, builds a receiver-only binary (-Dsender=false), runs the test suite, and installs to /usr/local/bin (override with SC_RECEIVER_INSTALL_DIR). It needs Raspberry Pi OS Trixie or newer (GCC 13+ for C++20 <format>), builds SDL3 from source when the distribution does not package it, and enables avahi-daemon. It also installs and enables a systemd service so the receiver starts at boot — systemctl status screencast-receiver to check on it.

Performance note: decoding is software H.264; on very small boards expect smooth playback for modest resolutions and reduced frame rates at high resolutions. Hardware decode is planned for Phase 7. On a headless console the receiver runs fullscreen automatically with aspect-preserving letterboxing.

Architecture

See docs/ARCHITECTURE.md for module boundaries and design rules.

Roadmap

Development is split into phases in docs/PHASES.md.

Current phase: Phase 7 — Resilience and polish.

License

MIT — see LICENSE (to be added).

S
Description
No description provided
Readme 1.2 MiB
Languages
C++ 60.6%
Swift 18%
Kotlin 15.2%
Shell 3.9%
Meson 2%
Other 0.2%