chore: initial project scaffolding
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
This commit is contained in:
+151
@@ -0,0 +1,151 @@
|
||||
# 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](https://github.com/fzwoch/obs-teleport). The teleport stream is received
|
||||
by another machine running [OBS Studio](https://obsproject.com) 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
|
||||
|
||||
1. Capture a single monitor (screen) + configurable audio source on Linux.
|
||||
2. Push the stream over the LAN using the Teleport protocol so an OBS + `obs-teleport`
|
||||
receiver can consume it with zero plugin-side configuration on the source machine.
|
||||
3. 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).
|
||||
|
||||
1. **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-teleport` uses
|
||||
`github.com/schollz/peerdiscovery`.
|
||||
2. **Transport** - TCP. The sender binds a listener on a configurable port and accepts
|
||||
connections from receivers (multiple concurrent receivers supported).
|
||||
3. **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`.
|
||||
4. **Video frames** - `Type = "JPEG"`. Frames are compressed to JPEG (turbojpeg) with
|
||||
configurable quality. Payload = header + image header + JPEG bytes.
|
||||
5. **Audio** - `Type = "WAVE"`. Raw interleaved PCM. Payload = header + wave header +
|
||||
PCM samples (16-bit stereo @ 48 kHz typical).
|
||||
6. **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.
|
||||
7. **Compatibility contract** - We must remain byte-compatible with the latest
|
||||
`obs-teleport` release 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:
|
||||
|
||||
1. **Core library (`internal/flinger`)** - protocol + capture agnostic:
|
||||
- `protocol`: packet types, JPEG encode, WAVE packetize, header (de)serialization.
|
||||
- `output`: TCP listener, connection management (reuse `obs-teleport`'s per-connection
|
||||
buffered channel approach with drop-when-overflow), framing/ordering queue.
|
||||
- `discovery`: UDP multicast announcer (`AnnouncePayload`).
|
||||
- `capture`: interface `CaptureSource` + implementations for screen and audio.
|
||||
2. **GUI (`cmd/teleportfling` or `internal/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`).
|
||||
3. **Capture backend**:
|
||||
- Wayland: PipeWire via `pipewire` Go bindings or `xdg-desktop-portal` screen capture.
|
||||
- Audio: PipeWire/PulseAudio monitor source (system audio) or input device; configurable.
|
||||
- X11 (optional): X11 API screen grab or PipeWire fallback where available.
|
||||
|
||||
## Milestones
|
||||
|
||||
1. **M1 - Protocol proof of life**: static JPEG frames + synthesized audio sent over TCP,
|
||||
multicast announcements read successfully by OBS Teleport receiver. CLI only.
|
||||
2. **M2 - Real capture**: PipeWire screen capture + system audio capture feeding the
|
||||
core; confirm 1080p30 on Wayland.
|
||||
3. **M3 - GUI + tray**: Fyne settings window and system tray start/stop.
|
||||
4. **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`) with `devShell` workspace.
|
||||
- `direnv` manages 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)
|
||||
|
||||
1. **Name**: project (and binary) is `teleportfling`.
|
||||
2. **JPEG encoding**: match `obs-teleport` exactly - use turbojpeg via cgo (`libturbojpeg`).
|
||||
Benchmark against a pure-Go encoder during M1 anyway; use turbojpeg unless it is a
|
||||
blocker.
|
||||
3. **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.
|
||||
4. **PipeWire bindings**: unresolved - evaluate pure-Go bindings vs. cgo to libpipewire
|
||||
during M1. This drives capture backend build requirements.
|
||||
5. **End-to-end testing**: OBS + `obs-teleport` are available locally on the dev machine
|
||||
for now (loopback/LAN testing). A remote receiver machine can be added later for
|
||||
further testing.
|
||||
6. **License**: GPL-2.0, matching the `obs-teleport` project whose protocol we implement.
|
||||
Reference in New Issue
Block a user