diff --git a/internal/daemon/daemon.go b/internal/daemon/daemon.go index 1b8de44..20afde7 100644 --- a/internal/daemon/daemon.go +++ b/internal/daemon/daemon.go @@ -232,8 +232,10 @@ func (d *Daemon) healthLoop(ctx context.Context) { continue } stalls[p.Slot]++ - if stalls[p.Slot] >= 2 { - d.log.Warn("stream stalled, forcing restart", "slot", p.Slot, "camera", p.Name) + // Require three consecutive unresponsive probes (~60s) before + // forcing a restart, so a brief IPC hiccup never bounces a stream. + if stalls[p.Slot] >= 3 { + d.log.Warn("stream hung, forcing restart", "slot", p.Slot, "camera", p.Name, "misses", stalls[p.Slot]) p.Stop() // Supervise relaunches stalls[p.Slot] = 0 } diff --git a/internal/player/player.go b/internal/player/player.go index 75b66e9..6368325 100644 --- a/internal/player/player.go +++ b/internal/player/player.go @@ -212,14 +212,17 @@ func (p *Player) Command(args ...any) (map[string]any, error) { return nil, fmt.Errorf("no reply from mpv ipc") } -// Healthy probes mpv over IPC and reports whether it is actively playing (has -// a finite time position advancing). A false result signals the daemon to -// consider restarting the slot even if the process is still alive (frozen). +// Healthy probes mpv over IPC and reports whether it is responsive. It queries +// a property that always exists (mpv-version) rather than time-pos, because a +// live stream with caching disabled can legitimately report time-pos as +// unavailable while playing fine — using it here caused false "unhealthy" +// verdicts and needless restarts. A false result now means mpv's IPC didn't +// answer at all, i.e. the process is genuinely hung. func (p *Player) Healthy() bool { if !p.Running() { return false } - reply, err := p.Command("get_property", "time-pos") + reply, err := p.Command("get_property", "mpv-version") if err != nil { return false }