156 lines
5.9 KiB
JavaScript
156 lines
5.9 KiB
JavaScript
"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, isPlaying } = (0, use_player_js_1.usePlayer)();
|
|
const setFrame = remotion_1.Internals.Timeline.useTimelineSetFrame();
|
|
// 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)(() => {
|
|
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 = () => {
|
|
if (hasBeenStopped) {
|
|
return;
|
|
}
|
|
if (!isPlaying()) {
|
|
return;
|
|
}
|
|
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;
|
|
}
|
|
queueNextFrame();
|
|
};
|
|
const queueNextFrame = () => {
|
|
if (context.buffering.current) {
|
|
const stopListening = context.listenForResume(() => {
|
|
stopListening.remove();
|
|
startedTime = performance.now();
|
|
framesAdvanced = 0;
|
|
queueNextFrame();
|
|
});
|
|
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),
|
|
};
|
|
return;
|
|
}
|
|
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,
|
|
context,
|
|
isPlaying,
|
|
]);
|
|
(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;
|