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:
@@ -23,11 +23,15 @@ type Config struct {
|
||||
Source string `json:"source"`
|
||||
Audio bool `json:"audio"`
|
||||
StreamIndex int `json:"stream_index"`
|
||||
// Announce is a *bool so an absent JSON key (older config files) keeps
|
||||
// the default instead of silently disabling announcements.
|
||||
Announce *bool `json:"announce"`
|
||||
}
|
||||
|
||||
// Default returns the default configuration.
|
||||
func Default() Config {
|
||||
c := flinger.DefaultConfig()
|
||||
announce := c.Announce
|
||||
return Config{
|
||||
Name: c.Name,
|
||||
Port: c.Port,
|
||||
@@ -36,11 +40,16 @@ func Default() Config {
|
||||
Source: c.Source,
|
||||
Audio: c.Audio,
|
||||
StreamIndex: c.StreamIndex,
|
||||
Announce: &announce,
|
||||
}
|
||||
}
|
||||
|
||||
// ToFlinger converts a persisted config to the engine config.
|
||||
func (c Config) ToFlinger() flinger.Config {
|
||||
announce := true
|
||||
if c.Announce != nil {
|
||||
announce = *c.Announce
|
||||
}
|
||||
return flinger.Config{
|
||||
Name: c.Name,
|
||||
Port: c.Port,
|
||||
@@ -49,6 +58,7 @@ func (c Config) ToFlinger() flinger.Config {
|
||||
Source: c.Source,
|
||||
Audio: c.Audio,
|
||||
StreamIndex: c.StreamIndex,
|
||||
Announce: announce,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -96,6 +106,10 @@ func Load() (Config, error) {
|
||||
if c.Source == "" {
|
||||
c.Source = d.Source
|
||||
}
|
||||
if c.Announce == nil {
|
||||
announce := true
|
||||
c.Announce = &announce
|
||||
}
|
||||
return c, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -29,6 +29,7 @@ func TestSaveLoadRoundTrip(t *testing.T) {
|
||||
pathVar = filepath.Join(t.TempDir(), "config.json")
|
||||
defer func() { pathVar = old }()
|
||||
|
||||
announce := false
|
||||
want := Config{
|
||||
Name: "Studio",
|
||||
Port: 9898,
|
||||
@@ -37,6 +38,7 @@ func TestSaveLoadRoundTrip(t *testing.T) {
|
||||
Source: "pattern",
|
||||
Audio: false,
|
||||
StreamIndex: 1,
|
||||
Announce: &announce,
|
||||
}
|
||||
if err := Save(want); err != nil {
|
||||
t.Fatalf("Save: %v", err)
|
||||
@@ -46,7 +48,10 @@ func TestSaveLoadRoundTrip(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatalf("Load: %v", err)
|
||||
}
|
||||
if got != want {
|
||||
if got.Name != want.Name || got.Port != want.Port || got.Quality != want.Quality ||
|
||||
got.FPS != want.FPS || got.Source != want.Source || got.Audio != want.Audio ||
|
||||
got.StreamIndex != want.StreamIndex || got.Announce == nil || want.Announce == nil ||
|
||||
*got.Announce != *want.Announce {
|
||||
t.Errorf("round trip mismatch:\n got %+v\nwant %+v", got, want)
|
||||
}
|
||||
}
|
||||
@@ -74,4 +79,16 @@ func TestLoadFillsZeroValues(t *testing.T) {
|
||||
if c.Quality != 80 {
|
||||
t.Errorf("quality = %d, want filled default 80", c.Quality)
|
||||
}
|
||||
// Absent "announce" key must default to true (not false).
|
||||
if c.Announce == nil || !*c.Announce {
|
||||
t.Errorf("announce = %v, want default true", c.Announce)
|
||||
}
|
||||
}
|
||||
|
||||
// TestToFlingerAnnounceDefault checks the announce default survives conversion.
|
||||
func TestToFlingerAnnounceDefault(t *testing.T) {
|
||||
c := Config{}
|
||||
if !c.ToFlinger().Announce {
|
||||
t.Error("ToFlinger announce default should be true")
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user