Files
TeleportFling/cmd/teleportfling/main.go
T
petere ba423eb944 refactor: extract streaming engine into internal/flinger
Move the capture/encode/send pipeline out of cmd/teleportfling into a
GUI-free Engine so the CLI and the desktop app share one implementation:
- flinger: Config/Engine/Status, audio+video+stats loops, pattern source
- config: JSON persistence at ~/.config/teleportfling/config.json
- cmd/teleportfling: thin CLI wrapper around the engine (same flags)
- golangci: extend exclusions to flinger/config
2026-09-18 20:32:45 +01:00

86 lines
2.5 KiB
Go

// Command teleportfling is a standalone sender for the Teleport protocol.
//
// It captures a Wayland screen (PipeWire via xdg-desktop-portal) plus the
// system's default audio output and streams them over TCP as the Teleport
// protocol, announcing itself on the LAN multicast group so an OBS instance
// with the obs-teleport plugin can discover and decode the stream.
//
// This is the headless/CLI entry point; the engine lives in
// internal/flinger. A desktop GUI with settings and a system tray is in
// cmd/teleportfling-gui.
//
// Usage:
//
// teleportfling [--name NAME] [--port PORT] [--quality 1..100]
// [--fps N] [--source screen|pattern] [--audio]
// [--stream-index N] [--duration SECONDS]
//
// --source pattern selects the M1 synthetic test pattern (colour bars with a
// moving box) instead of real screen capture, which is useful for testing
// without granting screen-share permission.
package main
import (
"flag"
"log"
"os"
"os/signal"
"syscall"
"time"
"teleportfling/internal/flinger"
)
func main() {
var (
name = flag.String("name", "", "announce name (default: hostname)")
port = flag.Int("port", 9756, "TCP listening port")
quality = flag.Int("quality", 80, "JPEG quality 1..100")
fps = flag.Int("fps", 30, "video frames per second")
source = flag.String("source", "screen", "capture source: screen or pattern")
withAudio = flag.Bool("audio", true, "capture and stream system audio")
streamIndex = flag.Int("stream-index", 0, "monitor index to capture (screen source)")
duration = flag.Duration("duration", 0, "stream duration (0 = run until interrupted)")
)
flag.Parse()
cfg := flinger.Config{
Name: *name,
Port: *port,
Quality: *quality,
FPS: *fps,
Source: *source,
Audio: *withAudio,
StreamIndex: *streamIndex,
}
eng, err := flinger.New(cfg)
if err != nil {
log.Fatalf("flinger: %v", err)
}
eng.Start()
log.Printf("teleportfling: streaming from source %q", cfg.Source)
// Interrupt / SIGTERM handling.
sigc := make(chan os.Signal, 1)
signal.Notify(sigc, syscall.SIGINT, syscall.SIGTERM)
select {
case <-sigc:
log.Printf("teleportfling: stopping…")
case <-func() <-chan struct{} {
if *duration > 0 {
ch := make(chan struct{})
time.AfterFunc(*duration, func() { close(ch) })
return ch
}
return nil
}():
log.Printf("teleportfling: duration reached")
}
eng.Stop()
log.Printf("teleportfling: stopped")
}