Add project configuration, documentation and development tooling: - flake.nix dev shell with Go, golangci-lint, turbojpeg - .envrc sourcing sops-encrypted .secrets.env - golangci-lint v2 config - opencode.json MCP + LSP wiring - AGENTS.md development guidelines
9.2 KiB
9.2 KiB
TeleportFling - Project Overview
Summary
TeleportFling is a standalone Linux application that captures a single screen along with
audio from the host machine and transmits the combined stream over a LAN using the
Teleport protocol. The teleport stream is received
by another machine running OBS Studio with the obs-teleport
plugin installed, where it appears as a regular Teleport source.
The project is an OBS/NDI-like replacement for a minimal multi-machine streaming setup: one machine (the "flinger") produces a screen + audio feed, a second machine aggregates and streams it. No NDI compatibility of any form.
Goals
- Capture a single monitor (screen) + configurable audio source on Linux.
- Push the stream over the LAN using the Teleport protocol so an OBS +
obs-teleportreceiver can consume it with zero plugin-side configuration on the source machine. - Ship a standalone, installable application, not an OBS plugin:
- Full configuration GUI (device, audio source, quality, discovery settings).
- System tray icon for quick start/stop and status.
- Works headless-ish: capture/transmit logic is decoupled from the GUI so it can later run as a daemon or be scripted.
Non-Goals
- NDI or DistroAV compatibility.
- Software transcoding beyond the JPEG frame encoding the protocol requires.
- Synchronous multi-screen capture initially (single screen only, per the name).
- Windows/macOS support initially (Linux only).
Target Environment
| Concern | Decision |
|---|---|
| Language | Go (same language as obs-teleport) |
| Display server | Wayland primary; X11 support if it falls out cheaply |
| Audio | Configurable: system/desktop audio (default) or mic/input device |
| Screen capture | Wayland: PipeWire / xdg-desktop-portal; X11: X11 extension or PipeWire fallback |
| GUI | Fyne (config window + system tray) |
| Discovery | UDP multicast peer discovery (identical to obs-teleport) |
| Target quality | Initial release: 1920x1080 @ 30 fps, balanced JPEG quality |
| Protocol target | Latest obs-teleport release (protocol contract below) |
| Remote git | Self-hosted Gitea at http://homeserver:3050/ |
Teleport Protocol Contract
Reference implementation: https://github.com/fzwoch/obs-teleport (GPL-2.0).
- Peer discovery - UDP multicast broadcast of a JSON
AnnouncePayload:{"Name", "Port", "AudioAndVideo", "Version", "Address"}. The OBS receiver uses these announcements to populate its source list.obs-teleportusesgithub.com/schollz/peerdiscovery. - Transport - TCP. The sender binds a listener on a configurable port and accepts connections from receivers (multiple concurrent receivers supported).
- Packet header (all little-endian):
Header:Type [4]byte,Timestamp uint64,Size int32.ImageHeader(video only):ColorMatrix [16]float32,ColorRangeMin [3]float32,ColorRangeMax [3]float32(from OBS video format color parameters).WaveHeader(audio only):Format int32,SampleRate int32,Speakers int32,Frames int32.
- Video frames -
Type = "JPEG". Frames are compressed to JPEG (turbojpeg) with configurable quality. Payload = header + image header + JPEG bytes. - Audio -
Type = "WAVE". Raw interleaved PCM. Payload = header + wave header + PCM samples (16-bit stereo @ 48 kHz typical). - Ordering - Video frames are queued, encoded in order, and sent in the same order; dropped frames are counted as lagged frames. Audio is sent immediately as it arrives.
- Compatibility contract - We must remain byte-compatible with the latest
obs-teleportrelease so that stock OBS Teleport receivers can discover and decode our stream without modification.
Architecture (proposed)
┌────────────────────────────┐ ┌───────────────────────────────┐
│ Fling app │ │ OBS receiver │
│ ┌──────────┐ ┌─────────┐ │ │ │
│ │ Screen │ │ Audio │ │ │ ┌─────────────────────────┐ │
│ │ capture │ │ capture │ │ │ │ obs-teleport source │ │
│ └────┬─────┘ └────┬────┘ │ │ └───────────┬─────────────┘ │
│ │ │ │ │ │ │
│ ▼ ▼ │ │ ▼ │
│ ┌────────────────────────┐ │ │ ┌─────────────────────────┐ │
│ │ Flinger core │ │ │ │ (decode + render) │ │
│ │ · JPEG encode (video) │ ├─────┼─▶│ OBS Studio pipeline │ │
│ │ · interleave (audio) │ │ │ └─────────────────────────┘ │
│ │ · packetize │ │ │ │
│ │ · TCP sender │ │ │ │
│ │ · UDP announcer │ │ │ │
│ └───────────┬────────────┘ │ │ │
└──────────────┼──────────────┘ └───────────────────────────────┘
│ LAN (TCP + UDP multicast)
Components:
- Core library (
internal/flinger) - protocol + capture agnostic:protocol: packet types, JPEG encode, WAVE packetize, header (de)serialization.output: TCP listener, connection management (reuseobs-teleport's per-connection buffered channel approach with drop-when-overflow), framing/ordering queue.discovery: UDP multicast announcer (AnnouncePayload).capture: interfaceCaptureSource+ implementations for screen and audio.
- GUI (
cmd/teleportflingorinternal/gui) - Fyne:- Settings window: monitor picker, audio source picker, port, JPEG quality, resolution/fps targets, discovery on/off, enable/disable.
- System tray: start/stop toggle, status indicator, open settings, quit.
- Config persistence (JSON config file, e.g.
~/.config/teleportfling/config.json).
- Capture backend:
- Wayland: PipeWire via
pipewireGo bindings orxdg-desktop-portalscreen capture. - Audio: PipeWire/PulseAudio monitor source (system audio) or input device; configurable.
- X11 (optional): X11 API screen grab or PipeWire fallback where available.
- Wayland: PipeWire via
Milestones
- M1 - Protocol proof of life: static JPEG frames + synthesized audio sent over TCP, multicast announcements read successfully by OBS Teleport receiver. CLI only.
- M2 - Real capture: PipeWire screen capture + system audio capture feeding the core; confirm 1080p30 on Wayland.
- M3 - GUI + tray: Fyne settings window and system tray start/stop.
- M4 - Hardening: config persistence, frame dropping/backpressure, discovery options, X11 fallback if feasible, packaging (Nix package / AppImage / release binaries).
Development Environment
- Nix flake (
flake.nix) withdevShellworkspace. direnvmanages activating the Nix environment.- Build:
go build ./...; Lint:golangci-lint; Tests:go test ./.... - All development follows the rules in
AGENTS.md.
Open Questions
- Bitrate/bandwidth targets beyond the balanced 1080p30 default.
- Whether to support scaling/downsampling of the captured monitor in M1/M2 or only the monitor's native resolution.
- PipeWire bindings strategy (pure-Go bindings vs. cgo wrappers vs.
xdg-desktop-portal).
Decisions Made (2026-09-18)
- Name: project (and binary) is
teleportfling. - JPEG encoding: match
obs-teleportexactly - use turbojpeg via cgo (libturbojpeg). Benchmark against a pure-Go encoder during M1 anyway; use turbojpeg unless it is a blocker. - System tray: Fyne's native tray support is limited on Linux; evaluate
github.com/nicedoc/systray(or similar) when we reach M3. Do not block M1/M2 on it. - PipeWire bindings: unresolved - evaluate pure-Go bindings vs. cgo to libpipewire during M1. This drives capture backend build requirements.
- End-to-end testing: OBS +
obs-teleportare available locally on the dev machine for now (loopback/LAN testing). A remote receiver machine can be added later for further testing. - License: GPL-2.0, matching the
obs-teleportproject whose protocol we implement.