feat: harden engine config, discovery and backpressure stats

- config: validate port/quality/fps/source/stream-index ranges on load
  and before engine start (flinger.Config.Validate)
- discovery: add Announce option to disable multicast (GUI checkbox,
  CLI --no-announce); absent JSON key keeps the default true
- backpressure: count dropped frames in the TCP sender, expose via
  engine Status and a live counter in the GUI status label
- docs: record M3/M4 decisions and X11 coverage via the portal backend
This commit is contained in:
2026-09-18 20:44:39 +01:00
parent cb27a0d63d
commit 2b3fd54934
8 changed files with 195 additions and 21 deletions
+12 -1
View File
@@ -16,6 +16,7 @@ import (
"log"
"net"
"sync"
"sync/atomic"
)
const (
@@ -34,6 +35,8 @@ type Sender struct {
listener net.Listener
port int
dropped atomic.Int64
}
// New creates an unconnected Sender.
@@ -86,7 +89,8 @@ func (s *Sender) NumConns() int {
// Send broadcasts a serialised packet to all connected receivers. If a
// receiver's buffered channel is full (> dropAt) the frame is silently
// dropped; a warning is logged at warnAt.
// dropped; a warning is logged at warnAt. Dropped frames are counted and
// exposed via Dropped.
func (s *Sender) Send(b []byte) {
s.mu.Lock()
defer s.mu.Unlock()
@@ -101,6 +105,7 @@ func (s *Sender) Send(b []byte) {
}
if len(ch) > dropAt {
s.dropped.Add(1)
continue
}
@@ -109,11 +114,17 @@ func (s *Sender) Send(b []byte) {
select {
case ch <- b:
default:
s.dropped.Add(1)
log.Printf("output: drop [%s] (queue full)", c.RemoteAddr())
}
}
}
// Dropped returns the total number of frames dropped due to backpressure.
func (s *Sender) Dropped() int64 {
return s.dropped.Load()
}
// Close shuts down the listener and waits for all writer goroutines to
// drain. After Close returns the Sender must not be reused.
func (s *Sender) Close() {