feat: implement M1 teleport protocol sender

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.
This commit is contained in:
2026-09-18 18:36:25 +01:00
parent b8b2ba8da1
commit 10fa72b228
11 changed files with 1438 additions and 0 deletions
+61
View File
@@ -0,0 +1,61 @@
package output
import (
"io"
"net"
"testing"
"time"
)
// TestSendReceive does an end-to-end round trip: a TCP client connects to
// the Sender, Send broadcasts a frame, and the client must read it back.
func TestSendReceive(t *testing.T) {
s := New()
port, err := s.Listen("127.0.0.1:0")
if err != nil {
t.Fatalf("Listen: %v", err)
}
conn, err := net.Dial("tcp", "127.0.0.1:"+itoa(port))
if err != nil {
t.Fatalf("Dial: %v", err)
}
defer func() { _ = conn.Close() }()
defer func() { _ = conn.(*net.TCPConn).SetLinger(0) }()
// Give the accept loop a moment to register the connection.
deadline := time.Now().Add(2 * time.Second)
for s.NumConns() == 0 && time.Now().Before(deadline) {
time.Sleep(5 * time.Millisecond)
}
if s.NumConns() != 1 {
t.Fatalf("NumConns = %d, want 1", s.NumConns())
}
payload := []byte("frame-one")
s.Send(payload)
got := make([]byte, len(payload))
if _, err := io.ReadFull(conn, got); err != nil {
t.Fatalf("read: %v", err)
}
if string(got) != "frame-one" {
t.Errorf("round trip mismatch: got %q", got)
}
s.Close()
}
func itoa(v int) string {
if v == 0 {
return "0"
}
var buf [20]byte
i := len(buf)
for v > 0 {
i--
buf[i] = byte('0' + v%10)
v /= 10
}
return string(buf[i:])
}