Place windows at map time so stream restarts never flash

Diagnosis: individual tiles flickered when their stream died and mpv
relaunched (confirmed via status: a PID dropping to 0 and coming back).
The restarted window mapped at the video's native size wherever sway
dropped it, and the corrective loop only snapped it into its cell on the
next tick — up to 2s later on the relaxed cadence.

Fix, at the source instead of racing the map:
- Pre-install a per-slot for_window rule (matched on each mpv's unique
  window title, anchored so slot-1 never matches slot-10) so sway
  positions and sizes the window synchronously the moment it maps.
- Pass --geometry=WxH per tile so mpv opens at the tile size rather
  than resizing itself to the video's native size on load.

The placeLoop remains as a corrective backstop for mid-life drift.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Levi Woodard
2026-07-02 09:50:39 -05:00
parent 98ddf1645c
commit 9985a93851
3 changed files with 31 additions and 0 deletions

View File

@@ -25,6 +25,10 @@ type Player struct {
Slot int // grid cell index (row-major)
Name string // camera name, for logs/titles
URL string // RTSP(S) source
// TileW/TileH, when set (before Supervise), are passed as --geometry so
// mpv opens its window at the tile size instead of the video's native
// size — a restarting stream then maps already-sized for its cell.
TileW, TileH int
cfg config.Player
ipcPath string
@@ -94,6 +98,12 @@ func (p *Player) args() []string {
// the audio decoder saves CPU per stream on the Pi.
args = append(args, "--no-audio")
}
if p.TileW > 0 && p.TileH > 0 {
// Open the window at the tile size; --geometry overrides mpv's
// resize-to-video-size on load, so the compositor never has to snap
// the window back into its cell.
args = append(args, fmt.Sprintf("--geometry=%dx%d", p.TileW, p.TileH))
}
if p.cfg.MaxFPS > 0 {
args = append(args, fmt.Sprintf("--vf=fps=%d", p.cfg.MaxFPS))
}