UNPKG

@remotion/player

Version:

React component for embedding a Remotion preview into your app

169 lines (168 loc) 6.51 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.usePlayback = void 0; /* eslint-disable @typescript-eslint/no-use-before-define */ const react_1 = require("react"); const remotion_1 = require("remotion"); const browser_mediasession_js_1 = require("./browser-mediasession.js"); const calculate_next_frame_js_1 = require("./calculate-next-frame.js"); const is_backgrounded_js_1 = require("./is-backgrounded.js"); const use_player_js_1 = require("./use-player.js"); const usePlayback = ({ loop, playbackRate, moveToBeginningWhenEnded, inFrame, outFrame, browserMediaControlsBehavior, getCurrentFrame, }) => { const config = remotion_1.Internals.useUnsafeVideoConfig(); const frame = remotion_1.Internals.Timeline.useTimelinePosition(); const { playing, pause, emitter } = (0, use_player_js_1.usePlayer)(); const setFrame = remotion_1.Internals.Timeline.useTimelineSetFrame(); const buffering = (0, react_1.useRef)(null); // requestAnimationFrame() does not work if the tab is not active. // This means that audio will keep playing even if it has ended. // In that case, we use setTimeout() instead. const isBackgroundedRef = (0, is_backgrounded_js_1.useIsBackgrounded)(); const lastTimeUpdateEvent = (0, react_1.useRef)(null); const context = (0, react_1.useContext)(remotion_1.Internals.BufferingContextReact); if (!context) { throw new Error('Missing the buffering context. Most likely you have a Remotion version mismatch.'); } (0, browser_mediasession_js_1.useBrowserMediaSession)({ browserMediaControlsBehavior, playbackRate, videoConfig: config, }); // complete code for media session API (0, react_1.useEffect)(() => { const onBufferClear = context.listenForBuffering(() => { buffering.current = performance.now(); }); const onResumeClear = context.listenForResume(() => { buffering.current = null; }); return () => { onBufferClear.remove(); onResumeClear.remove(); }; }, [context]); (0, react_1.useEffect)(() => { if (!config) { return; } if (!playing) { return; } let hasBeenStopped = false; let reqAnimFrameCall = null; let startedTime = performance.now(); let framesAdvanced = 0; const cancelQueuedFrame = () => { if (reqAnimFrameCall !== null) { if (reqAnimFrameCall.type === 'raf') { cancelAnimationFrame(reqAnimFrameCall.id); } else { clearTimeout(reqAnimFrameCall.id); } } }; const stop = () => { hasBeenStopped = true; cancelQueuedFrame(); }; const callback = () => { const time = performance.now() - startedTime; const actualLastFrame = outFrame !== null && outFrame !== void 0 ? outFrame : config.durationInFrames - 1; const actualFirstFrame = inFrame !== null && inFrame !== void 0 ? inFrame : 0; const currentFrame = getCurrentFrame(); const { nextFrame, framesToAdvance, hasEnded } = (0, calculate_next_frame_js_1.calculateNextFrame)({ time, currentFrame, playbackSpeed: playbackRate, fps: config.fps, actualFirstFrame, actualLastFrame, framesAdvanced, shouldLoop: loop, }); framesAdvanced += framesToAdvance; if (nextFrame !== getCurrentFrame() && (!hasEnded || moveToBeginningWhenEnded)) { setFrame((c) => ({ ...c, [config.id]: nextFrame })); } if (hasEnded) { stop(); pause(); emitter.dispatchEnded(); return; } if (!hasBeenStopped) { queueNextFrame(); } }; const queueNextFrame = () => { if (buffering.current) { const stopListening = context.listenForResume(() => { stopListening.remove(); if (hasBeenStopped) { return; } startedTime = performance.now(); framesAdvanced = 0; callback(); }); return; } if (isBackgroundedRef.current) { reqAnimFrameCall = { type: 'timeout', // Note: Most likely, this will not be 1000 / fps, but the browser will throttle it to ~1/sec. id: setTimeout(callback, 1000 / config.fps), }; } else { reqAnimFrameCall = { type: 'raf', id: requestAnimationFrame(callback) }; } }; queueNextFrame(); const onVisibilityChange = () => { if (document.visibilityState === 'visible') { return; } // If tab goes into the background, cancel requestAnimationFrame() and update immediately. // , so the transition to setTimeout() can be fulfilled. cancelQueuedFrame(); callback(); }; window.addEventListener('visibilitychange', onVisibilityChange); return () => { window.removeEventListener('visibilitychange', onVisibilityChange); stop(); }; }, [ config, loop, pause, playing, setFrame, emitter, playbackRate, inFrame, outFrame, moveToBeginningWhenEnded, isBackgroundedRef, getCurrentFrame, buffering, context, ]); (0, react_1.useEffect)(() => { const interval = setInterval(() => { if (lastTimeUpdateEvent.current === getCurrentFrame()) { return; } emitter.dispatchTimeUpdate({ frame: getCurrentFrame() }); lastTimeUpdateEvent.current = getCurrentFrame(); }, 250); return () => clearInterval(interval); }, [emitter, getCurrentFrame]); (0, react_1.useEffect)(() => { emitter.dispatchFrameUpdate({ frame }); }, [emitter, frame]); }; exports.usePlayback = usePlayback;