Reconnect strategy: back off instead of retrying instantly after a stream drops #12

Closed
opened 2026-08-26 02:00:39 +02:00 by benjamin · 1 comment
Owner

Symptom

Channels regularly come up as the provider's "Stream Offline" still image. Leaving the channel and re-entering it once or twice usually fixes it.

Why it happens

The provider limits concurrent connections per subscription and does not free a session the moment the client disconnects. Two things make the app run straight into that window:

  1. Server side (fixed separately): Dispatcharr's channel_shutdown_delay was 0, so the upstream connection was torn down the instant the last viewer left, and re-entering immediately opened a brand-new one while the old session was still counted. Raised to 30 s, so returning within that window now reuses the existing connection instead of asking the provider for a second one.

  2. App side (this issue): PlayerController.onPlayerError() retries twice with no delay at allsetMediaItem / prepare / play fire back to back. If the provider is briefly rate-limiting, all three attempts land inside the same blocked window and burn the retry budget in well under a second. Worse, the "Stream Offline" still image is valid video, so ExoPlayer reports no error and the retry logic never even runs.

Suggested fix

  • Space retries out: roughly 1 s, then 3 s, then 8 s instead of three immediate attempts, and keep a "Verbindung wird wiederhergestellt…" hint on screen while it happens
  • After the last failed attempt, offer one clear action ("Erneut versuchen") rather than dropping back to the list (see #7)
  • Rate-limit re-entry into the same channel: if a channel is re-opened within a couple of seconds of leaving it, wait briefly before reconnecting so the provider can release the old session
  • Consider a per-channel failover stream in Dispatcharr for the channels that break most often — the real cure for a flaky upstream

Notes

The still image cannot be detected reliably from the client side (it decodes as normal video). The realistic levers are the connection reuse window on the server and the pacing described above.

## Symptom Channels regularly come up as the provider's "Stream Offline" still image. Leaving the channel and re-entering it once or twice usually fixes it. ## Why it happens The provider limits concurrent connections per subscription and does not free a session the moment the client disconnects. Two things make the app run straight into that window: 1. **Server side (fixed separately):** Dispatcharr's `channel_shutdown_delay` was `0`, so the upstream connection was torn down the instant the last viewer left, and re-entering immediately opened a brand-new one while the old session was still counted. Raised to 30 s, so returning within that window now reuses the existing connection instead of asking the provider for a second one. 2. **App side (this issue):** `PlayerController.onPlayerError()` retries twice **with no delay at all** — `setMediaItem` / `prepare` / `play` fire back to back. If the provider is briefly rate-limiting, all three attempts land inside the same blocked window and burn the retry budget in well under a second. Worse, the "Stream Offline" still image is *valid video*, so ExoPlayer reports no error and the retry logic never even runs. ## Suggested fix - Space retries out: roughly 1 s, then 3 s, then 8 s instead of three immediate attempts, and keep a "Verbindung wird wiederhergestellt…" hint on screen while it happens - After the last failed attempt, offer one clear action ("Erneut versuchen") rather than dropping back to the list (see #7) - Rate-limit re-entry into the *same* channel: if a channel is re-opened within a couple of seconds of leaving it, wait briefly before reconnecting so the provider can release the old session - Consider a per-channel failover stream in Dispatcharr for the channels that break most often — the real cure for a flaky upstream ## Notes The still image cannot be detected reliably from the client side (it decodes as normal video). The realistic levers are the connection reuse window on the server and the pacing described above.
Author
Owner

Fixed in dd612fc, shipped in v0.9.0 — on both sides.

Server: channel_shutdown_delay was 0, so Dispatcharr tore down the upstream connection the instant the last viewer left and opened a brand-new one on return, while the provider still counted the old session. Raised to 30 s, so returning within that window reuses the existing connection.

App:

  • Retries are spaced 1 s / 3 s / 8 s (RETRY_DELAYS_MS) instead of three immediate attempts, scheduled on a handler and cancelled on play()/stop()/release().
  • While retries run the player reports state reconnecting and the overlay shows "Verbindung wird wiederhergestellt…" instead of looking frozen.
  • After the last attempt the viewer gets "Sender gerade nicht erreichbar" with a focused Erneut versuchen (see #7) rather than being dropped back to the list.
  • Re-entering the channel that was just closed waits out a 2.5 s grace period (REENTRY_GRACE_MS in AppState.play()), which is exactly the window where the provider hands back its "Stream Offline" placeholder.

Still true and unchanged: the placeholder itself is valid video, so it cannot be detected client-side. Per-channel failover streams in Dispatcharr remain the real cure for chronically flaky upstreams.

Fixed in dd612fc, shipped in v0.9.0 — on both sides. **Server:** `channel_shutdown_delay` was `0`, so Dispatcharr tore down the upstream connection the instant the last viewer left and opened a brand-new one on return, while the provider still counted the old session. Raised to **30 s**, so returning within that window reuses the existing connection. **App:** - Retries are spaced `1 s / 3 s / 8 s` (`RETRY_DELAYS_MS`) instead of three immediate attempts, scheduled on a handler and cancelled on `play()`/`stop()`/`release()`. - While retries run the player reports state `reconnecting` and the overlay shows "Verbindung wird wiederhergestellt…" instead of looking frozen. - After the last attempt the viewer gets "Sender gerade nicht erreichbar" with a focused **Erneut versuchen** (see #7) rather than being dropped back to the list. - Re-entering the channel that was just closed waits out a 2.5 s grace period (`REENTRY_GRACE_MS` in `AppState.play()`), which is exactly the window where the provider hands back its "Stream Offline" placeholder. Still true and unchanged: the placeholder itself is valid video, so it cannot be detected client-side. Per-channel failover streams in Dispatcharr remain the real cure for chronically flaky upstreams.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: be-nj/castarr#12