Commit Graph
9 Commits
Author SHA1 Message Date
edde746 e6a0840fbe fix(player): stop rebuilding the whole controls tree on every tick
Nothing sat between the full-screen `RasterizedGradient` scrim and the
position-driven leaves, so `markNeedsPaint` from the timeline slider walked past
the gradient and re-rasterized the entire chrome -- top bar, button row,
timestamps, timeline -- on every 250 ms position tick. That also partly defeated
`RasterizedGradient`, which exists because Skia gradients cost ~10 ms per
full-screen pass on this GPU class.

Around that, four things drove root `setState` calls that only needed to change a
leaf:

- Held D-pad seek called a root `setState` per key repeat, even when the readout
  was already visible in the same direction and only the accumulated seconds had
  changed. The seek amount now flows through a `ValueNotifier` that
  `DoubleTapFeedback` reads, so a burst updates one `Text`.
- `DoubleTapFeedback`'s drift chevron ran `..repeat()` with no stop condition, and
  kept ticking at 60 Hz for the ~300 ms the readout spends at zero opacity after
  every burst. The controller is now gated on visibility.
- The auto-skip countdown ran a 5 Hz timer that root-`setState`d the controls tree
  for the whole intro/credits window, to animate one progress ring.
- The play-next and still-watching countdowns root-`setState`d the player screen
  once a second, which re-created `PlexVideoControls` with its ~50 props and
  rebuilt the entire chrome to change one digit. Prompt open/close deliberately
  stays on the state path, because `playNextFocusNode` is threaded through those
  props and D-pad focus depends on it.

Two of the four position subscribers existed only to recompute pointer-hover
tooltips that a D-pad viewer never sees, rebuilding a button subtree 4x/s and
rescanning the chapter list each time; the "ends at" readout reformatted a string
4x/s for a value that changes once a minute. All three now dedupe.

Finally, the buffer path did two sequential `copyWith` calls on a 15-field
`PlayerState` and handed `BufferRangePainter` a freshly allocated list every tick.
`List` has no value equality, so `shouldRepaint` was unconditionally true.

Measured on device during playback with the chrome raised: 6 of 344 UI frames over
budget, p50 4.39 ms, layout p90 1.09 ms. During a held D-pad seek -- the worst
frame-time window on the TV path, because the readout and the decoder flush land in
the same frames -- 1 of 354 frames over budget, p50 4.28 ms, zero raster frames over
budget.
2026-08-23 18:13:39 +02:00
edde746 ed4be7b96d refactor: strip obvious comments 2026-05-04 22:40:18 +02:00
edde746 31d2d9dc98 feat: jellyfin 2026-05-01 01:20:36 +02:00
edde746 3da51f9d64 chore: pre-commit ci hook, dart format 2026-04-18 12:40:35 +02:00
edde746 b46f1cccb1 remove chapter_marker_painter 2026-04-04 01:36:03 +02:00
edde746 7d35132509 feat: M3 2024 slider style for video player timelines
Use GappedTrackShape (no stop indicator) globally.
Timeline/volume sliders get local overrides for thinner
track, zero padding, and no overlay. Chapter markers
rendered as track gaps. Live TV timeline matches style.
Timeline thumb hidden when unfocused in dpad mode.
2026-04-03 23:14:11 +02:00
edde746 0159c18497 feat: discontinuous buffer ranges on timeline 2026-02-21 04:50:10 +01:00
edde746 3c89d0eba8 refactor: update formatting 2025-12-15 02:29:03 +01:00
edde746 b30c9c6420 refactor: split up video controls 2025-11-05 13:52:21 +01:00