Stream a synthetic test pattern and silent PCM audio over the OBS Teleport protocol: - protocol: wire format (Header/ImageHeader/WaveHeader), BT.709 full range colour matrix, JPEG encode via turbojpeg cgo, WAVE packet builder - output: TCP sender with per-connection buffered channels and drop-on-overflow - discovery: multicast announce via peerdiscovery - cmd: teleportfling CLI with flags, test-pattern frame generator Verified end-to-end: OBS discovers and renders the stream with correct colours and motion.
77 lines
1.8 KiB
Go
77 lines
1.8 KiB
Go
// 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()
|
|
}
|