@remotion/player 4.0.288 → 4.0.290

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.
@@ -38,6 +38,7 @@ export type PlayerProps<Schema extends AnyZodObject, Props extends Record<string
38
38
  readonly showPosterWhenEnded?: boolean;
39
39
  readonly showPosterWhenUnplayed?: boolean;
40
40
  readonly showPosterWhenBuffering?: boolean;
41
+ readonly showPosterWhenBufferingAndPaused?: boolean;
41
42
  readonly inFrame?: number | null;
42
43
  readonly outFrame?: number | null;
43
44
  readonly initiallyShowControls?: number | boolean;
@@ -23,7 +23,7 @@ const componentOrNullIfLazy = (props) => {
23
23
  return null;
24
24
  };
25
25
  exports.componentOrNullIfLazy = componentOrNullIfLazy;
26
- const PlayerFn = ({ durationInFrames, compositionHeight, compositionWidth, fps, inputProps, style, controls = false, loop = false, autoPlay = false, showVolumeControls = true, allowFullscreen = true, clickToPlay, doubleClickToFullscreen = false, spaceKeyToPlayOrPause = true, moveToBeginningWhenEnded = true, numberOfSharedAudioTags = 5, errorFallback = () => '⚠️', playbackRate = 1, renderLoading, className, showPosterWhenUnplayed, showPosterWhenEnded, showPosterWhenPaused, showPosterWhenBuffering, initialFrame, renderPoster, inFrame, outFrame, initiallyShowControls, renderFullscreenButton, renderPlayPauseButton, renderVolumeSlider, alwaysShowControls = false, initiallyMuted = false, showPlaybackRateControl = false, posterFillMode = 'player-size', bufferStateDelayInMilliseconds, hideControlsWhenPointerDoesntMove = true, overflowVisible = false, renderMuteButton, browserMediaControlsBehavior: passedBrowserMediaControlsBehavior, overrideInternalClassName, logLevel = 'info', noSuspense, acknowledgeRemotionLicense, ...componentProps }, ref) => {
26
+ const PlayerFn = ({ durationInFrames, compositionHeight, compositionWidth, fps, inputProps, style, controls = false, loop = false, autoPlay = false, showVolumeControls = true, allowFullscreen = true, clickToPlay, doubleClickToFullscreen = false, spaceKeyToPlayOrPause = true, moveToBeginningWhenEnded = true, numberOfSharedAudioTags = 5, errorFallback = () => '⚠️', playbackRate = 1, renderLoading, className, showPosterWhenUnplayed, showPosterWhenEnded, showPosterWhenPaused, showPosterWhenBuffering, showPosterWhenBufferingAndPaused, initialFrame, renderPoster, inFrame, outFrame, initiallyShowControls, renderFullscreenButton, renderPlayPauseButton, renderVolumeSlider, alwaysShowControls = false, initiallyMuted = false, showPlaybackRateControl = false, posterFillMode = 'player-size', bufferStateDelayInMilliseconds, hideControlsWhenPointerDoesntMove = true, overflowVisible = false, renderMuteButton, browserMediaControlsBehavior: passedBrowserMediaControlsBehavior, overrideInternalClassName, logLevel = 'info', noSuspense, acknowledgeRemotionLicense, ...componentProps }, ref) => {
27
27
  if (typeof window !== 'undefined') {
28
28
  // eslint-disable-next-line react-hooks/rules-of-hooks
29
29
  (0, react_1.useLayoutEffect)(() => {
@@ -159,7 +159,7 @@ const PlayerFn = ({ durationInFrames, compositionHeight, compositionWidth, fps,
159
159
  }, [passedBrowserMediaControlsBehavior]);
160
160
  return ((0, jsx_runtime_1.jsx)(remotion_1.Internals.IsPlayerContextProvider, { children: (0, jsx_runtime_1.jsx)(SharedPlayerContext_js_1.SharedPlayerContexts, { timelineContext: timelineContextValue, component: component, compositionHeight: compositionHeight, compositionWidth: compositionWidth, durationInFrames: durationInFrames, fps: fps, numberOfSharedAudioTags: numberOfSharedAudioTags, initiallyMuted: initiallyMuted, logLevel: logLevel, children: (0, jsx_runtime_1.jsx)(remotion_1.Internals.Timeline.SetTimelineContext.Provider, { value: setTimelineContextValue, children: (0, jsx_runtime_1.jsx)(EmitterProvider_js_1.PlayerEmitterProvider, { currentPlaybackRate: currentPlaybackRate, children: (0, jsx_runtime_1.jsx)(PlayerUI_js_1.default, { ref: rootRef, posterFillMode: posterFillMode, renderLoading: renderLoading, autoPlay: Boolean(autoPlay), loop: Boolean(loop), controls: Boolean(controls), errorFallback: errorFallback, style: style, inputProps: actualInputProps, allowFullscreen: Boolean(allowFullscreen), moveToBeginningWhenEnded: Boolean(moveToBeginningWhenEnded), clickToPlay: typeof clickToPlay === 'boolean'
161
161
  ? clickToPlay
162
- : Boolean(controls), showVolumeControls: Boolean(showVolumeControls), doubleClickToFullscreen: Boolean(doubleClickToFullscreen), spaceKeyToPlayOrPause: Boolean(spaceKeyToPlayOrPause), playbackRate: currentPlaybackRate, className: className !== null && className !== void 0 ? className : undefined, showPosterWhenUnplayed: Boolean(showPosterWhenUnplayed), showPosterWhenEnded: Boolean(showPosterWhenEnded), showPosterWhenPaused: Boolean(showPosterWhenPaused), showPosterWhenBuffering: Boolean(showPosterWhenBuffering), renderPoster: renderPoster, inFrame: inFrame !== null && inFrame !== void 0 ? inFrame : null, outFrame: outFrame !== null && outFrame !== void 0 ? outFrame : null, initiallyShowControls: initiallyShowControls !== null && initiallyShowControls !== void 0 ? initiallyShowControls : true, renderFullscreen: renderFullscreenButton !== null && renderFullscreenButton !== void 0 ? renderFullscreenButton : null, renderPlayPauseButton: renderPlayPauseButton !== null && renderPlayPauseButton !== void 0 ? renderPlayPauseButton : null, renderMuteButton: renderMuteButton !== null && renderMuteButton !== void 0 ? renderMuteButton : null, renderVolumeSlider: renderVolumeSlider !== null && renderVolumeSlider !== void 0 ? renderVolumeSlider : null, alwaysShowControls: alwaysShowControls, showPlaybackRateControl: showPlaybackRateControl, bufferStateDelayInMilliseconds: bufferStateDelayInMilliseconds !== null && bufferStateDelayInMilliseconds !== void 0 ? bufferStateDelayInMilliseconds : 300, hideControlsWhenPointerDoesntMove: hideControlsWhenPointerDoesntMove, overflowVisible: overflowVisible, browserMediaControlsBehavior: browserMediaControlsBehavior, overrideInternalClassName: overrideInternalClassName !== null && overrideInternalClassName !== void 0 ? overrideInternalClassName : undefined, noSuspense: Boolean(noSuspense) }) }) }) }) }));
162
+ : Boolean(controls), showVolumeControls: Boolean(showVolumeControls), doubleClickToFullscreen: Boolean(doubleClickToFullscreen), spaceKeyToPlayOrPause: Boolean(spaceKeyToPlayOrPause), playbackRate: currentPlaybackRate, className: className !== null && className !== void 0 ? className : undefined, showPosterWhenUnplayed: Boolean(showPosterWhenUnplayed), showPosterWhenEnded: Boolean(showPosterWhenEnded), showPosterWhenPaused: Boolean(showPosterWhenPaused), showPosterWhenBuffering: Boolean(showPosterWhenBuffering), showPosterWhenBufferingAndPaused: Boolean(showPosterWhenBufferingAndPaused), renderPoster: renderPoster, inFrame: inFrame !== null && inFrame !== void 0 ? inFrame : null, outFrame: outFrame !== null && outFrame !== void 0 ? outFrame : null, initiallyShowControls: initiallyShowControls !== null && initiallyShowControls !== void 0 ? initiallyShowControls : true, renderFullscreen: renderFullscreenButton !== null && renderFullscreenButton !== void 0 ? renderFullscreenButton : null, renderPlayPauseButton: renderPlayPauseButton !== null && renderPlayPauseButton !== void 0 ? renderPlayPauseButton : null, renderMuteButton: renderMuteButton !== null && renderMuteButton !== void 0 ? renderMuteButton : null, renderVolumeSlider: renderVolumeSlider !== null && renderVolumeSlider !== void 0 ? renderVolumeSlider : null, alwaysShowControls: alwaysShowControls, showPlaybackRateControl: showPlaybackRateControl, bufferStateDelayInMilliseconds: bufferStateDelayInMilliseconds !== null && bufferStateDelayInMilliseconds !== void 0 ? bufferStateDelayInMilliseconds : 300, hideControlsWhenPointerDoesntMove: hideControlsWhenPointerDoesntMove, overflowVisible: overflowVisible, browserMediaControlsBehavior: browserMediaControlsBehavior, overrideInternalClassName: overrideInternalClassName !== null && overrideInternalClassName !== void 0 ? overrideInternalClassName : undefined, noSuspense: Boolean(noSuspense) }) }) }) }) }));
163
163
  };
164
164
  const forward = react_1.forwardRef;
165
165
  /*
@@ -35,6 +35,7 @@ declare const _default: React.ForwardRefExoticComponent<{
35
35
  readonly showPosterWhenEnded: boolean;
36
36
  readonly showPosterWhenUnplayed: boolean;
37
37
  readonly showPosterWhenBuffering: boolean;
38
+ readonly showPosterWhenBufferingAndPaused: boolean;
38
39
  readonly inFrame: number | null;
39
40
  readonly outFrame: number | null;
40
41
  readonly initiallyShowControls: number | boolean;
@@ -51,7 +51,7 @@ if (reactVersion === '0') {
51
51
  throw new Error(`Version ${reactVersion} of "react" is not supported by Remotion`);
52
52
  }
53
53
  const doesReactVersionSupportSuspense = parseInt(reactVersion, 10) >= 18;
54
- const PlayerUI = ({ controls, style, loop, autoPlay, allowFullscreen, inputProps, clickToPlay, showVolumeControls, doubleClickToFullscreen, spaceKeyToPlayOrPause, errorFallback, playbackRate, renderLoading, renderPoster, className, moveToBeginningWhenEnded, showPosterWhenUnplayed, showPosterWhenEnded, showPosterWhenPaused, showPosterWhenBuffering, inFrame, outFrame, initiallyShowControls, renderFullscreen: renderFullscreenButton, renderPlayPauseButton, renderMuteButton, renderVolumeSlider, alwaysShowControls, showPlaybackRateControl, posterFillMode, bufferStateDelayInMilliseconds, hideControlsWhenPointerDoesntMove, overflowVisible, browserMediaControlsBehavior, overrideInternalClassName, noSuspense, }, ref) => {
54
+ const PlayerUI = ({ controls, style, loop, autoPlay, allowFullscreen, inputProps, clickToPlay, showVolumeControls, doubleClickToFullscreen, spaceKeyToPlayOrPause, errorFallback, playbackRate, renderLoading, renderPoster, className, moveToBeginningWhenEnded, showPosterWhenUnplayed, showPosterWhenEnded, showPosterWhenPaused, showPosterWhenBuffering, showPosterWhenBufferingAndPaused, inFrame, outFrame, initiallyShowControls, renderFullscreen: renderFullscreenButton, renderPlayPauseButton, renderMuteButton, renderVolumeSlider, alwaysShowControls, showPlaybackRateControl, posterFillMode, bufferStateDelayInMilliseconds, hideControlsWhenPointerDoesntMove, overflowVisible, browserMediaControlsBehavior, overrideInternalClassName, noSuspense, }, ref) => {
55
55
  var _a, _b, _c;
56
56
  const config = remotion_1.Internals.useUnsafeVideoConfig();
57
57
  const video = remotion_1.Internals.useVideo();
@@ -426,6 +426,9 @@ const PlayerUI = ({ controls, style, loop, autoPlay, allowFullscreen, inputProps
426
426
  showPosterWhenEnded && player.isLastFrame && !player.isPlaying(),
427
427
  showPosterWhenUnplayed && !player.hasPlayed && !player.isPlaying(),
428
428
  showPosterWhenBuffering && showBufferIndicator && player.isPlaying(),
429
+ showPosterWhenBufferingAndPaused &&
430
+ showBufferIndicator &&
431
+ !player.isPlaying(),
429
432
  ].some(Boolean);
430
433
  const { left, top, width, height, ...outerWithoutScale } = outer;
431
434
  const content = ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsxs)("div", { style: outer, onPointerDown: clickToPlay ? handlePointerDown : undefined, onDoubleClick: doubleClickToFullscreen ? handleDoubleClick : undefined, children: [(0, jsx_runtime_1.jsxs)("div", { style: containerStyle, className: (0, player_css_classname_js_1.playerCssClassname)(overrideInternalClassName), children: [VideoComponent ? ((0, jsx_runtime_1.jsx)(error_boundary_js_1.ErrorBoundary, { onError: onError, errorFallback: errorFallback, children: (0, jsx_runtime_1.jsx)(remotion_1.Internals.CurrentScaleContext.Provider, { value: currentScale, children: (0, jsx_runtime_1.jsx)(VideoComponent, { ...((_c = video === null || video === void 0 ? void 0 : video.props) !== null && _c !== void 0 ? _c : {}), ...(inputProps !== null && inputProps !== void 0 ? inputProps : {}) }) }) })) : null, shouldShowPoster && posterFillMode === 'composition-size' ? ((0, jsx_runtime_1.jsx)("div", { style: {
@@ -0,0 +1,171 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.usePlayback = void 0;
4
+ /* eslint-disable @typescript-eslint/no-use-before-define */
5
+ const react_1 = require("react");
6
+ const react_dom_1 = require("react-dom");
7
+ const remotion_1 = require("remotion");
8
+ const browser_mediasession_js_1 = require("./browser-mediasession.js");
9
+ const calculate_next_frame_js_1 = require("./calculate-next-frame.js");
10
+ const is_backgrounded_js_1 = require("./is-backgrounded.js");
11
+ const use_player_js_1 = require("./use-player.js");
12
+ const usePlayback = ({ loop, playbackRate, moveToBeginningWhenEnded, inFrame, outFrame, browserMediaControlsBehavior, getCurrentFrame, }) => {
13
+ const config = remotion_1.Internals.useUnsafeVideoConfig();
14
+ const frame = remotion_1.Internals.Timeline.useTimelinePosition();
15
+ const { playing, pause, emitter } = (0, use_player_js_1.usePlayer)();
16
+ const setFrame = remotion_1.Internals.Timeline.useTimelineSetFrame();
17
+ const buffering = (0, react_1.useRef)(null);
18
+ // requestAnimationFrame() does not work if the tab is not active.
19
+ // This means that audio will keep playing even if it has ended.
20
+ // In that case, we use setTimeout() instead.
21
+ const isBackgroundedRef = (0, is_backgrounded_js_1.useIsBackgrounded)();
22
+ const lastTimeUpdateEvent = (0, react_1.useRef)(null);
23
+ const context = (0, react_1.useContext)(remotion_1.Internals.BufferingContextReact);
24
+ if (!context) {
25
+ throw new Error('Missing the buffering context. Most likely you have a Remotion version mismatch.');
26
+ }
27
+ (0, browser_mediasession_js_1.useBrowserMediaSession)({
28
+ browserMediaControlsBehavior,
29
+ playbackRate,
30
+ videoConfig: config,
31
+ });
32
+ // complete code for media session API
33
+ (0, react_1.useEffect)(() => {
34
+ const onBufferClear = context.listenForBuffering(() => {
35
+ buffering.current = performance.now();
36
+ });
37
+ const onResumeClear = context.listenForResume(() => {
38
+ buffering.current = null;
39
+ });
40
+ return () => {
41
+ onBufferClear.remove();
42
+ onResumeClear.remove();
43
+ };
44
+ }, [context]);
45
+ (0, react_1.useEffect)(() => {
46
+ if (!config) {
47
+ return;
48
+ }
49
+ if (!playing) {
50
+ return;
51
+ }
52
+ let hasBeenStopped = false;
53
+ let reqAnimFrameCall = null;
54
+ let startedTime = performance.now();
55
+ let framesAdvanced = 0;
56
+ const cancelQueuedFrame = () => {
57
+ if (reqAnimFrameCall !== null) {
58
+ if (reqAnimFrameCall.type === 'raf') {
59
+ cancelAnimationFrame(reqAnimFrameCall.id);
60
+ }
61
+ else {
62
+ clearTimeout(reqAnimFrameCall.id);
63
+ }
64
+ }
65
+ };
66
+ const stop = () => {
67
+ hasBeenStopped = true;
68
+ cancelQueuedFrame();
69
+ };
70
+ const callback = () => {
71
+ const time = performance.now() - startedTime;
72
+ const actualLastFrame = outFrame !== null && outFrame !== void 0 ? outFrame : config.durationInFrames - 1;
73
+ const actualFirstFrame = inFrame !== null && inFrame !== void 0 ? inFrame : 0;
74
+ const currentFrame = getCurrentFrame();
75
+ const { nextFrame, framesToAdvance, hasEnded } = (0, calculate_next_frame_js_1.calculateNextFrame)({
76
+ time,
77
+ currentFrame,
78
+ playbackSpeed: playbackRate,
79
+ fps: config.fps,
80
+ actualFirstFrame,
81
+ actualLastFrame,
82
+ framesAdvanced,
83
+ shouldLoop: loop,
84
+ });
85
+ framesAdvanced += framesToAdvance;
86
+ if (nextFrame !== getCurrentFrame() &&
87
+ (!hasEnded || moveToBeginningWhenEnded)) {
88
+ (0, react_dom_1.flushSync)(() => {
89
+ setFrame((c) => ({ ...c, [config.id]: nextFrame }));
90
+ });
91
+ }
92
+ if (hasEnded) {
93
+ stop();
94
+ pause();
95
+ emitter.dispatchEnded();
96
+ return;
97
+ }
98
+ if (!hasBeenStopped) {
99
+ queueNextFrame();
100
+ }
101
+ };
102
+ const queueNextFrame = () => {
103
+ if (buffering.current) {
104
+ const stopListening = context.listenForResume(() => {
105
+ stopListening.remove();
106
+ if (hasBeenStopped) {
107
+ return;
108
+ }
109
+ startedTime = performance.now();
110
+ framesAdvanced = 0;
111
+ callback();
112
+ });
113
+ return;
114
+ }
115
+ if (isBackgroundedRef.current) {
116
+ reqAnimFrameCall = {
117
+ type: 'timeout',
118
+ // Note: Most likely, this will not be 1000 / fps, but the browser will throttle it to ~1/sec.
119
+ id: setTimeout(callback, 1000 / config.fps),
120
+ };
121
+ }
122
+ else {
123
+ reqAnimFrameCall = { type: 'raf', id: requestAnimationFrame(callback) };
124
+ }
125
+ };
126
+ queueNextFrame();
127
+ const onVisibilityChange = () => {
128
+ if (document.visibilityState === 'visible') {
129
+ return;
130
+ }
131
+ // If tab goes into the background, cancel requestAnimationFrame() and update immediately.
132
+ // , so the transition to setTimeout() can be fulfilled.
133
+ cancelQueuedFrame();
134
+ callback();
135
+ };
136
+ window.addEventListener('visibilitychange', onVisibilityChange);
137
+ return () => {
138
+ window.removeEventListener('visibilitychange', onVisibilityChange);
139
+ stop();
140
+ };
141
+ }, [
142
+ config,
143
+ loop,
144
+ pause,
145
+ playing,
146
+ setFrame,
147
+ emitter,
148
+ playbackRate,
149
+ inFrame,
150
+ outFrame,
151
+ moveToBeginningWhenEnded,
152
+ isBackgroundedRef,
153
+ getCurrentFrame,
154
+ buffering,
155
+ context,
156
+ ]);
157
+ (0, react_1.useEffect)(() => {
158
+ const interval = setInterval(() => {
159
+ if (lastTimeUpdateEvent.current === getCurrentFrame()) {
160
+ return;
161
+ }
162
+ emitter.dispatchTimeUpdate({ frame: getCurrentFrame() });
163
+ lastTimeUpdateEvent.current = getCurrentFrame();
164
+ }, 250);
165
+ return () => clearInterval(interval);
166
+ }, [emitter, getCurrentFrame]);
167
+ (0, react_1.useEffect)(() => {
168
+ emitter.dispatchFrameUpdate({ frame });
169
+ }, [emitter, frame]);
170
+ };
171
+ exports.usePlayback = usePlayback;
@@ -2384,6 +2384,7 @@ var PlayerUI = ({
2384
2384
  showPosterWhenEnded,
2385
2385
  showPosterWhenPaused,
2386
2386
  showPosterWhenBuffering,
2387
+ showPosterWhenBufferingAndPaused,
2387
2388
  inFrame,
2388
2389
  outFrame,
2389
2390
  initiallyShowControls,
@@ -2741,7 +2742,8 @@ var PlayerUI = ({
2741
2742
  showPosterWhenPaused && !player.isPlaying() && !seeking,
2742
2743
  showPosterWhenEnded && player.isLastFrame && !player.isPlaying(),
2743
2744
  showPosterWhenUnplayed && !player.hasPlayed && !player.isPlaying(),
2744
- showPosterWhenBuffering && showBufferIndicator && player.isPlaying()
2745
+ showPosterWhenBuffering && showBufferIndicator && player.isPlaying(),
2746
+ showPosterWhenBufferingAndPaused && showBufferIndicator && !player.isPlaying()
2745
2747
  ].some(Boolean);
2746
2748
  const { left, top, width, height, ...outerWithoutScale } = outer;
2747
2749
  const content = /* @__PURE__ */ jsxs9(Fragment3, {
@@ -3106,6 +3108,7 @@ var PlayerFn = ({
3106
3108
  showPosterWhenEnded,
3107
3109
  showPosterWhenPaused,
3108
3110
  showPosterWhenBuffering,
3111
+ showPosterWhenBufferingAndPaused,
3109
3112
  initialFrame,
3110
3113
  renderPoster,
3111
3114
  inFrame,
@@ -3286,6 +3289,7 @@ var PlayerFn = ({
3286
3289
  showPosterWhenEnded: Boolean(showPosterWhenEnded),
3287
3290
  showPosterWhenPaused: Boolean(showPosterWhenPaused),
3288
3291
  showPosterWhenBuffering: Boolean(showPosterWhenBuffering),
3292
+ showPosterWhenBufferingAndPaused: Boolean(showPosterWhenBufferingAndPaused),
3289
3293
  renderPoster,
3290
3294
  inFrame: inFrame ?? null,
3291
3295
  outFrame: outFrame ?? null,
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "url": "https://github.com/remotion-dev/remotion/tree/main/packages/player"
4
4
  },
5
5
  "name": "@remotion/player",
6
- "version": "4.0.288",
6
+ "version": "4.0.290",
7
7
  "description": "React component for embedding a Remotion preview into your app",
8
8
  "main": "dist/cjs/index.js",
9
9
  "types": "dist/cjs/index.d.ts",
@@ -28,7 +28,7 @@
28
28
  ],
29
29
  "license": "SEE LICENSE IN LICENSE.md",
30
30
  "dependencies": {
31
- "remotion": "4.0.288"
31
+ "remotion": "4.0.290"
32
32
  },
33
33
  "peerDependencies": {
34
34
  "react": ">=16.8.0",
@@ -43,7 +43,7 @@
43
43
  "webpack": "5.96.1",
44
44
  "zod": "3.22.3",
45
45
  "eslint": "9.19.0",
46
- "@remotion/eslint-config-internal": "4.0.288"
46
+ "@remotion/eslint-config-internal": "4.0.290"
47
47
  },
48
48
  "keywords": [
49
49
  "remotion",