// Package discovery wraps schollz/peerdiscovery to broadcast the Teleport // AnnouncePayload over UDP multicast. The payload tells OBS receivers // where to connect and what the stream carries. Only the announcer // (sender) side is implemented; receivers are OBS's job. package discovery import ( "encoding/json" "log" "os" "sync" "github.com/schollz/peerdiscovery" "teleportfling/internal/protocol" ) // Announcer periodically broadcasts AnnouncePayload on the LAN multicast // group until Stop is called. type Announcer struct { wg sync.WaitGroup ch chan struct{} stopOnce sync.Once } // Start launches the multicast announcer. It returns immediately; the // background goroutine keeps broadcasting until Stop is called. // // name is the stream display name (empty → hostname). port is the TCP // listening port advertised to receivers. func Start(name string, port int) *Announcer { a := &Announcer{ch: make(chan struct{})} if name == "" { var err error name, err = os.Hostname() if err != nil { name = "TeleportFling" } } payload := protocol.AnnouncePayload{ Name: name, Port: port, AudioAndVideo: true, Version: "0.0.0", } b, err := json.Marshal(payload) if err != nil { log.Printf("discovery: marshal announce payload: %v", err) return a } a.wg.Add(1) go func() { defer a.wg.Done() _, err := peerdiscovery.Discover(peerdiscovery.Settings{ TimeLimit: -1, StopChan: a.ch, Payload: b, }) // A nil error is expected on normal Stop(); only surface real failures. if err != nil { log.Printf("discovery: announce stopped: %v", err) } }() log.Printf("discovery: announcing %q on port %d", name, port) return a } // Stop terminates the background announcer and waits for it to exit. func (a *Announcer) Stop() { a.stopOnce.Do(func() { close(a.ch) }) a.wg.Wait() }