@remotion/player
Version:
React component for embedding a Remotion preview into your app
169 lines (168 loc) • 6.51 kB
JavaScript
;
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;