feat(player): add frame counter when scrubbing and seeking

Show a timecode + frame readout above the timeline while dragging the
playhead, and flash it for a moment on keyboard/button seeks so frame
stepping is visible too.

Position and text are written from the existing rAF/DOM path that drives
the playhead, so the readout stays smooth without extra React renders.

Two supporting fixes the count depends on:

- Seed the frame rate from the HLS manifest FRAME-RATE attribute so a
  frame number is available before playback ever starts; previously the
  rate was only ever measured from requestVideoFrameCallback and stayed
  null until the video had played.
- Snap the measured rate to the nearest broadcast standard and skip
  samples taken mid-seek. A drifting float slid the count by whole
  frames late in a long video, and re-publishing a slightly different
  float on every presented frame forced a re-render per video frame.
This commit is contained in:
yusufipk
2026-07-25 17:07:34 +07:00
parent a14eb9fb84
commit b23f3de666
3 changed files with 103 additions and 8 deletions
+6
View File
@@ -90,6 +90,7 @@ export function VideoPageContent({
const timelineRef = useRef<HTMLDivElement>(null);
const progressRef = useRef<HTMLDivElement>(null);
const playheadRef = useRef<HTMLDivElement>(null);
const scrubReadoutRef = useRef<HTMLDivElement>(null);
const videoContainerRef = useRef<HTMLDivElement>(null);
const pathname = usePathname();
const scheduleWatchProgressSaveRef = useRef<
@@ -322,6 +323,7 @@ export function VideoPageContent({
isMuted,
isFrameMode,
frameStepLabel,
showScrubReadout,
playbackSpeed,
qualityOptions,
selectedQualityLevel,
@@ -357,8 +359,10 @@ export function VideoPageContent({
timelineRef,
progressRef,
playheadRef,
scrubReadoutRef,
hlsRef,
playerRef,
formatTime,
formatBunnyQualityLabel,
speedOptions: SPEED_OPTIONS,
scheduleWatchProgressSaveRef,
@@ -783,7 +787,9 @@ export function VideoPageContent({
timelineRef={timelineRef}
progressRef={progressRef}
playheadRef={playheadRef}
scrubReadoutRef={scrubReadoutRef}
videoContainerRef={videoContainerRef}
showScrubReadout={showScrubReadout}
isFullscreenMode={isFullscreenMode}
cursorIdle={cursorIdle}
isPlaying={isPlaying}