@bycrux/editor 0.5.1 → 0.5.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json
CHANGED
|
@@ -50,6 +50,10 @@ export function useVideoPlayback(
|
|
|
50
50
|
const loopOffsetRef = useRef(0)
|
|
51
51
|
const rafRef = useRef<number | null>(null)
|
|
52
52
|
const rafLastMs = useRef<number | null>(null)
|
|
53
|
+
// rAF clock for VIDEO projects — drives clip-boundary detection at ~60Hz
|
|
54
|
+
// instead of the <video> element's coarse `timeupdate` event (~4Hz). See the
|
|
55
|
+
// effect below for why.
|
|
56
|
+
const videoRafRef = useRef<number | null>(null)
|
|
53
57
|
const audioRefsMap = useRef<Map<string, HTMLAudioElement>>(new Map())
|
|
54
58
|
const audioSrcMap = useRef<Map<string, string>>(new Map())
|
|
55
59
|
// Web Audio API: GainNode per audio track allows volume > 1.0 (amplification).
|
|
@@ -693,6 +697,39 @@ export function useVideoPlayback(
|
|
|
693
697
|
handleTimeUpdate()
|
|
694
698
|
}, [handleTimeUpdate])
|
|
695
699
|
|
|
700
|
+
// ── Video boundary clock ─────────────────────────────────────────────────
|
|
701
|
+
// Drive clip-boundary detection from requestAnimationFrame (~60Hz) rather
|
|
702
|
+
// than relying on the <video> element's `timeupdate` event, which only fires
|
|
703
|
+
// ~every 250ms. Under timeupdate-gating the active clip plays up to a full
|
|
704
|
+
// ~250ms PAST its outPoint before the swap fires; on a silence-trimmed
|
|
705
|
+
// single-source timeline that overshoot is trimmed-out footage playing past
|
|
706
|
+
// the cut — the "underlying video keeps playing in the background" bug.
|
|
707
|
+
// Polling currentTime every frame tightens the boundary to ~16ms.
|
|
708
|
+
// handleTimeUpdate is idempotent (preload + swap are guarded), so the
|
|
709
|
+
// timeupdate event firing in addition to this is harmless. Canvas projects
|
|
710
|
+
// advance time via their own rAF above and are excluded here.
|
|
711
|
+
useEffect(() => {
|
|
712
|
+
if (isCanvasProject || !isPlaying) {
|
|
713
|
+
if (videoRafRef.current !== null) {
|
|
714
|
+
cancelAnimationFrame(videoRafRef.current)
|
|
715
|
+
videoRafRef.current = null
|
|
716
|
+
}
|
|
717
|
+
return
|
|
718
|
+
}
|
|
719
|
+
function pump() {
|
|
720
|
+
// The gap clock owns time during gaps; handleTimeUpdate no-ops then.
|
|
721
|
+
if (!inGapRef.current) handleTimeUpdate()
|
|
722
|
+
videoRafRef.current = requestAnimationFrame(pump)
|
|
723
|
+
}
|
|
724
|
+
videoRafRef.current = requestAnimationFrame(pump)
|
|
725
|
+
return () => {
|
|
726
|
+
if (videoRafRef.current !== null) {
|
|
727
|
+
cancelAnimationFrame(videoRafRef.current)
|
|
728
|
+
videoRafRef.current = null
|
|
729
|
+
}
|
|
730
|
+
}
|
|
731
|
+
}, [isPlaying, isCanvasProject, handleTimeUpdate])
|
|
732
|
+
|
|
696
733
|
function togglePlay() {
|
|
697
734
|
// GESTURE-ANCHORED: same rationale as the keydown handler — resume the
|
|
698
735
|
// AudioContext synchronously inside this user-gesture call so a wired
|