remotion 4.0.486 → 4.0.488

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.
@@ -104,8 +104,7 @@ const HtmlInCanvasContent = (0, react_1.forwardRef)(({ width, height, effects, c
104
104
  ref(node);
105
105
  }
106
106
  else if (ref) {
107
- ref.current =
108
- node;
107
+ ref.current = node;
109
108
  }
110
109
  }, [ref]);
111
110
  const chainState = (0, use_effect_chain_state_js_1.useEffectChainState)();
@@ -319,8 +318,7 @@ const HtmlInCanvasInner = (0, react_1.forwardRef)(({ width, height, effects = []
319
318
  ref(node);
320
319
  }
321
320
  else if (ref) {
322
- ref.current =
323
- node;
321
+ ref.current = node;
324
322
  }
325
323
  }, [ref]);
326
324
  return (jsx_runtime_1.jsx(Sequence_js_1.Sequence, { durationInFrames: resolvedDuration, name: name !== null && name !== void 0 ? name : '<HtmlInCanvas>', _remotionInternalDocumentationLink: "https://www.remotion.dev/docs/remotion/html-in-canvas", controls: controls, _remotionInternalEffects: memoizedEffectDefinitions, outlineRef: actualRef, layout: "none", ...sequenceProps, children: jsx_runtime_1.jsx(HtmlInCanvasContent, { ref: setCanvasRef, width: width, height: height, effects: effects, onPaint: onPaint, onInit: onInit, pixelDensity: pixelDensity, controls: controls, style: style, children: children }) }));
package/dist/cjs/Img.js CHANGED
@@ -23,6 +23,7 @@ const ImgContent = ({ onError, maxRetries = 2, src, pauseWhenLoading, delayRende
23
23
  const errors = (0, react_1.useRef)({});
24
24
  const { delayPlayback } = (0, use_buffer_state_js_1.useBufferState)();
25
25
  const sequenceContext = (0, react_1.useContext)(SequenceContext_js_1.SequenceContext);
26
+ const [isLoading, setIsLoading] = (0, react_1.useState)(false);
26
27
  const _propsValid = true;
27
28
  if (!_propsValid) {
28
29
  throw new Error('typecheck error');
@@ -59,6 +60,8 @@ const ImgContent = ({ onError, maxRetries = 2, src, pauseWhenLoading, delayRende
59
60
  }, timeout);
60
61
  }, []);
61
62
  const { delayRender, continueRender, cancelRender } = (0, use_delay_render_js_1.useDelayRender)();
63
+ const isPremounting = Boolean(sequenceContext === null || sequenceContext === void 0 ? void 0 : sequenceContext.premounting);
64
+ const isPostmounting = Boolean(sequenceContext === null || sequenceContext === void 0 ? void 0 : sequenceContext.postmounting);
62
65
  const didGetError = (0, react_1.useCallback)((e) => {
63
66
  var _a, _b, _c, _d, _e, _f, _g;
64
67
  var _h, _j, _k, _l;
@@ -89,8 +92,19 @@ const ImgContent = ({ onError, maxRetries = 2, src, pauseWhenLoading, delayRende
89
92
  }
90
93
  }, [cancelRender, maxRetries, onError, retryIn]);
91
94
  if (typeof window !== 'undefined') {
92
- const isPremounting = Boolean(sequenceContext === null || sequenceContext === void 0 ? void 0 : sequenceContext.premounting);
93
- const isPostmounting = Boolean(sequenceContext === null || sequenceContext === void 0 ? void 0 : sequenceContext.postmounting);
95
+ // eslint-disable-next-line react-hooks/rules-of-hooks
96
+ (0, react_1.useLayoutEffect)(() => {
97
+ if (!pauseWhenLoading || !isLoading || isPremounting || isPostmounting) {
98
+ return;
99
+ }
100
+ return delayPlayback().unblock;
101
+ }, [
102
+ delayPlayback,
103
+ isLoading,
104
+ isPostmounting,
105
+ isPremounting,
106
+ pauseWhenLoading,
107
+ ]);
94
108
  // eslint-disable-next-line react-hooks/rules-of-hooks
95
109
  (0, react_1.useLayoutEffect)(() => {
96
110
  var _a, _b;
@@ -104,13 +118,11 @@ const ImgContent = ({ onError, maxRetries = 2, src, pauseWhenLoading, delayRende
104
118
  if (!current) {
105
119
  return;
106
120
  }
121
+ setIsLoading(true);
107
122
  const newHandle = delayRender('Loading <Img> with src=' + (0, truncate_src_for_label_js_1.truncateSrcForLabel)(actualSrc), {
108
123
  retries: delayRenderRetries !== null && delayRenderRetries !== void 0 ? delayRenderRetries : undefined,
109
124
  timeoutInMilliseconds: delayRenderTimeoutInMilliseconds !== null && delayRenderTimeoutInMilliseconds !== void 0 ? delayRenderTimeoutInMilliseconds : undefined,
110
125
  });
111
- const unblock = pauseWhenLoading && !isPremounting && !isPostmounting
112
- ? delayPlayback().unblock
113
- : () => undefined;
114
126
  let unmounted = false;
115
127
  const onComplete = () => {
116
128
  var _a, _b, _c;
@@ -128,7 +140,7 @@ const ImgContent = ({ onError, maxRetries = 2, src, pauseWhenLoading, delayRende
128
140
  if (current) {
129
141
  onImageFrame === null || onImageFrame === void 0 ? void 0 : onImageFrame(current);
130
142
  }
131
- unblock();
143
+ setIsLoading(false);
132
144
  continueRender(newHandle);
133
145
  };
134
146
  if (!imageRef.current) {
@@ -158,17 +170,12 @@ const ImgContent = ({ onError, maxRetries = 2, src, pauseWhenLoading, delayRende
158
170
  return () => {
159
171
  unmounted = true;
160
172
  current.removeEventListener('load', onComplete);
161
- unblock();
162
173
  continueRender(newHandle);
163
174
  };
164
175
  }, [
165
176
  actualSrc,
166
- delayPlayback,
167
177
  delayRenderRetries,
168
178
  delayRenderTimeoutInMilliseconds,
169
- pauseWhenLoading,
170
- isPremounting,
171
- isPostmounting,
172
179
  onImageFrame,
173
180
  continueRender,
174
181
  delayRender,
package/dist/cjs/Still.js CHANGED
@@ -6,6 +6,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.Still = void 0;
7
7
  const react_1 = __importDefault(require("react"));
8
8
  const Composition_js_1 = require("./Composition.js");
9
+ const enable_sequence_stack_traces_js_1 = require("./enable-sequence-stack-traces.js");
9
10
  /*
10
11
  * @description A `<Still />` is a `<Composition />` that is only 1 frame long.
11
12
  * @see [Documentation](https://www.remotion.dev/docs/still)
@@ -20,3 +21,4 @@ const Still = (props) => {
20
21
  return react_1.default.createElement(Composition_js_1.Composition, newProps);
21
22
  };
22
23
  exports.Still = Still;
24
+ (0, enable_sequence_stack_traces_js_1.addSequenceStackTraces)(exports.Still);
@@ -1,12 +1,12 @@
1
- import type { MutableRefObject } from 'react';
1
+ import type { RefObject } from 'react';
2
2
  import React from 'react';
3
3
  import { type PlayableMediaTag } from './timeline-position-state';
4
4
  export type TimelineContextValue = {
5
5
  frame: Record<string, number>;
6
6
  playing: boolean;
7
7
  rootId: string;
8
- imperativePlaying: MutableRefObject<boolean>;
9
- audioAndVideoTags: MutableRefObject<PlayableMediaTag[]>;
8
+ imperativePlaying: RefObject<boolean>;
9
+ audioAndVideoTags: RefObject<PlayableMediaTag[]>;
10
10
  };
11
11
  export type PlaybackRateContextValue = {
12
12
  playbackRate: number;
@@ -1,8 +1,19 @@
1
1
  import type React from 'react';
2
2
  import type { SequenceControls } from '../CompositionManager.js';
3
3
  import type { EffectsProp } from '../effects/effect-types.js';
4
- import type { RemotionAnimatedImageProps } from './props';
5
- export declare const AnimatedImage: React.ComponentType<import("../Interactive.js").InteractiveBaseProps & RemotionAnimatedImageProps & {
4
+ export declare const AnimatedImage: React.ComponentType<import("../Interactive.js").InteractiveBaseProps & {
5
+ src: string;
6
+ width?: number | undefined;
7
+ height?: number | undefined;
8
+ onError?: ((error: Error) => void) | undefined;
9
+ fit?: import("../calculate-image-fit.js").ImageFit | undefined;
10
+ playbackRate?: number | undefined;
11
+ style?: React.CSSProperties | undefined;
12
+ loopBehavior?: import("./props").RemotionAnimatedImageLoopBehavior | undefined;
13
+ id?: string | undefined;
14
+ className?: string | undefined;
15
+ requestInit?: RequestInit | undefined;
16
+ } & React.AriaAttributes & Record<`data-${string}`, string | undefined> & {
6
17
  readonly effects?: EffectsProp | undefined;
7
18
  } & {
8
19
  readonly controls?: SequenceControls | undefined;
@@ -30,6 +30,17 @@ const animatedImageSchema = {
30
30
  },
31
31
  ...interactivity_schema_js_1.transformSchema,
32
32
  };
33
+ const getCanvasPropsFromSequenceProps = (props) => {
34
+ const canvasProps = {};
35
+ const mutableCanvasProps = canvasProps;
36
+ for (const key in props) {
37
+ if (Object.prototype.hasOwnProperty.call(props, key) &&
38
+ (key.startsWith('data-') || key.startsWith('aria-'))) {
39
+ mutableCanvasProps[key] = props[key];
40
+ }
41
+ }
42
+ return canvasProps;
43
+ };
33
44
  const AnimatedImageContent = (0, react_1.forwardRef)(({ src, width, height, onError, loopBehavior = 'loop', playbackRate = 1, fit = 'fill', requestInit, effects, controls, ...props }, canvasRef) => {
34
45
  var _a;
35
46
  const resolvedSrc = (0, resolve_image_source_1.resolveAnimatedImageSource)(src);
@@ -158,6 +169,7 @@ const AnimatedImageInner = ({ src, width, height, onError, fit, playbackRate, lo
158
169
  (0, react_1.useImperativeHandle)(ref, () => {
159
170
  return actualRef.current;
160
171
  }, []);
172
+ const canvasProps = getCanvasPropsFromSequenceProps(sequenceProps);
161
173
  const animatedImageProps = {
162
174
  src,
163
175
  width,
@@ -170,6 +182,7 @@ const AnimatedImageInner = ({ src, width, height, onError, fit, playbackRate, lo
170
182
  className,
171
183
  style,
172
184
  requestInit,
185
+ ...canvasProps,
173
186
  };
174
187
  return (jsx_runtime_1.jsx(Sequence_js_1.Sequence, { layout: "none", durationInFrames: resolvedDuration, name: "<AnimatedImage>", _remotionInternalDocumentationLink: "https://www.remotion.dev/docs/animatedimage", controls: controls, _remotionInternalEffects: memoizedEffectDefinitions, ...sequenceProps, outlineRef: actualRef, children: jsx_runtime_1.jsx(AnimatedImageContent, { ...animatedImageProps, ref: actualRef, effects: effects, controls: controls }) }));
175
188
  };
@@ -1,18 +1,15 @@
1
1
  import React from 'react';
2
2
  import type { EffectDefinitionAndStack } from '../effects/effect-types.js';
3
- import type { AnimatedImageFillMode } from './props';
4
- type Props = {
5
- readonly width?: number;
6
- readonly height?: number;
7
- readonly fit: AnimatedImageFillMode;
8
- readonly className?: string;
9
- readonly style?: React.CSSProperties;
10
- readonly effects: EffectDefinitionAndStack<unknown>[];
11
- };
12
3
  export type AnimatedImageCanvasRef = {
13
4
  readonly draw: (imageData: VideoFrame) => Promise<boolean>;
14
5
  readonly getCanvas: () => HTMLCanvasElement | null;
15
6
  clear: () => void;
16
7
  };
17
- export declare const Canvas: React.ForwardRefExoticComponent<Props & React.RefAttributes<AnimatedImageCanvasRef>>;
18
- export {};
8
+ export declare const Canvas: React.ForwardRefExoticComponent<{
9
+ readonly width?: number | undefined;
10
+ readonly height?: number | undefined;
11
+ readonly fit: import("../calculate-image-fit.js").ImageFit;
12
+ readonly className?: string | undefined;
13
+ readonly style?: React.CSSProperties | undefined;
14
+ readonly effects: EffectDefinitionAndStack<unknown>[];
15
+ } & React.AriaAttributes & Record<`data-${string}`, string | undefined> & React.RefAttributes<AnimatedImageCanvasRef>>;
@@ -39,7 +39,7 @@ const react_1 = __importStar(require("react"));
39
39
  const calculate_image_fit_js_1 = require("../calculate-image-fit.js");
40
40
  const run_effect_chain_js_1 = require("../effects/run-effect-chain.js");
41
41
  const use_effect_chain_state_js_1 = require("../effects/use-effect-chain-state.js");
42
- const CanvasRefForwardingFunction = ({ width, height, fit, className, style, effects }, ref) => {
42
+ const CanvasRefForwardingFunction = ({ width, height, fit, className, style, effects, ...props }, ref) => {
43
43
  const canvasRef = (0, react_1.useRef)(null);
44
44
  const chainState = (0, use_effect_chain_state_js_1.useEffectChainState)();
45
45
  const sourceCanvas = (0, react_1.useMemo)(() => {
@@ -101,6 +101,6 @@ const CanvasRefForwardingFunction = ({ width, height, fit, className, style, eff
101
101
  },
102
102
  };
103
103
  }, [draw]);
104
- return jsx_runtime_1.jsx("canvas", { ref: canvasRef, className: className, style: style });
104
+ return (jsx_runtime_1.jsx("canvas", { ref: canvasRef, className: className, style: style, ...props }));
105
105
  };
106
106
  exports.Canvas = react_1.default.forwardRef(CanvasRefForwardingFunction);
@@ -2,6 +2,7 @@ import type { ImageFit } from '../calculate-image-fit.js';
2
2
  import type { EffectsProp } from '../effects/effect-types.js';
3
3
  import type { InteractiveBaseProps } from '../Interactive.js';
4
4
  export type RemotionAnimatedImageLoopBehavior = 'loop' | 'pause-after-finish' | 'clear-after-finish';
5
+ export type AnimatedImageCanvasProps = React.AriaAttributes & Record<`data-${string}`, string | undefined>;
5
6
  export type RemotionAnimatedImageProps = {
6
7
  src: string;
7
8
  width?: number;
@@ -14,7 +15,7 @@ export type RemotionAnimatedImageProps = {
14
15
  id?: string;
15
16
  className?: string;
16
17
  requestInit?: RequestInit;
17
- };
18
+ } & AnimatedImageCanvasProps;
18
19
  export type AnimatedImageProps = InteractiveBaseProps & RemotionAnimatedImageProps & {
19
20
  readonly effects?: EffectsProp;
20
21
  };
@@ -33,6 +33,7 @@ export type ScheduleAudioNodeResult = {
33
33
  export type ScheduleAudioNodeOptions = {
34
34
  readonly node: AudioBufferSourceNode;
35
35
  readonly mediaTimestamp: number;
36
+ readonly sourceOffset: number;
36
37
  readonly scheduledTime: number;
37
38
  readonly originalUnloopedMediaTimestamp: number;
38
39
  readonly duration: number;
@@ -129,7 +129,7 @@ const SharedAudioContextProvider = ({ children, audioLatencyHint, audioEnabled,
129
129
  nodesToResume.current.delete(node);
130
130
  }, []);
131
131
  const scheduleAudioNode = (0, react_1.useMemo)(() => {
132
- return ({ node, mediaTimestamp, scheduledTime, duration, offset, originalUnloopedMediaTimestamp, }) => {
132
+ return ({ node, mediaTimestamp, sourceOffset, scheduledTime, duration, offset, originalUnloopedMediaTimestamp, }) => {
133
133
  if (!ctxAndGain) {
134
134
  throw new Error('Audio context not found');
135
135
  }
@@ -154,7 +154,7 @@ const SharedAudioContextProvider = ({ children, audioLatencyHint, audioEnabled,
154
154
  }
155
155
  }
156
156
  const scheduledEndTime = scheduledTime + duration / node.playbackRate.value;
157
- const mediaTime = mediaTimestamp + offset;
157
+ const mediaTime = mediaTimestamp + offset - sourceOffset;
158
158
  const mediaEndTime = mediaTime + duration;
159
159
  const latency = ctxAndGain.audioContext.baseLatency +
160
160
  ctxAndGain.audioContext.outputLatency;
@@ -17,7 +17,7 @@ type BufferManager = {
17
17
  addBlock: AddBlock;
18
18
  listenForBuffering: ListenForBuffering;
19
19
  listenForResume: ListenForResume;
20
- buffering: React.MutableRefObject<boolean>;
20
+ buffering: React.RefObject<boolean>;
21
21
  };
22
22
  export declare const BufferingContextReact: React.Context<BufferManager | null>;
23
23
  export declare const BufferingProvider: React.FC<{
@@ -90,7 +90,11 @@ const useBufferManager = (logLevel, mountTime) => {
90
90
  if (rendering) {
91
91
  return;
92
92
  }
93
- if (blocks.length > 0) {
93
+ // Only fire on the `false -> true` transition: adding a block while
94
+ // already buffering (e.g. a second media element starts loading) must
95
+ // not re-dispatch `waiting` to listeners.
96
+ if (blocks.length > 0 && !buffering.current) {
97
+ buffering.current = true;
94
98
  onBufferingCallbacks.forEach((c) => c());
95
99
  (0, playback_logging_1.playbackLogging)({
96
100
  logLevel,
@@ -110,7 +114,11 @@ const useBufferManager = (logLevel, mountTime) => {
110
114
  if (rendering) {
111
115
  return;
112
116
  }
113
- if (blocks.length === 0) {
117
+ // Only fire on the `true -> false` transition: the initial mount and
118
+ // a block that was added and removed within the same commit must not
119
+ // dispatch `resume` to listeners.
120
+ if (blocks.length === 0 && buffering.current) {
121
+ buffering.current = false;
114
122
  onResumeCallbacks.forEach((c) => c());
115
123
  (0, playback_logging_1.playbackLogging)({
116
124
  logLevel,
@@ -145,11 +153,11 @@ const useIsPlayerBuffering = (bufferManager) => {
145
153
  const onResume = () => {
146
154
  setIsBuffering(false);
147
155
  };
148
- bufferManager.listenForBuffering(onBuffer);
149
- bufferManager.listenForResume(onResume);
156
+ const buffer = bufferManager.listenForBuffering(onBuffer);
157
+ const resume = bufferManager.listenForResume(onResume);
150
158
  return () => {
151
- bufferManager.listenForBuffering(() => undefined);
152
- bufferManager.listenForResume(() => undefined);
159
+ buffer.remove();
160
+ resume.remove();
153
161
  };
154
162
  }, [bufferManager]);
155
163
  return isBuffering;
@@ -108,13 +108,18 @@ const CanvasImageContent = (0, react_1.forwardRef)(({ src, width, height, fit =
108
108
  });
109
109
  const sequenceContext = (0, react_1.useContext)(SequenceContext_js_1.SequenceContext);
110
110
  const pendingLoadDelayRef = (0, react_1.useRef)(null);
111
- const continuePendingLoadDelay = (0, react_1.useCallback)(() => {
111
+ const [isLoadPending, setIsLoadPending] = (0, react_1.useState)(false);
112
+ const isPremounting = Boolean(sequenceContext === null || sequenceContext === void 0 ? void 0 : sequenceContext.premounting);
113
+ const isPostmounting = Boolean(sequenceContext === null || sequenceContext === void 0 ? void 0 : sequenceContext.postmounting);
114
+ const continuePendingLoadDelay = (0, react_1.useCallback)(({ markAsReady }) => {
112
115
  const pending = pendingLoadDelayRef.current;
113
116
  if (!pending || pending.continued) {
114
117
  return;
115
118
  }
116
119
  pending.continued = true;
117
- pending.unblock();
120
+ if (markAsReady) {
121
+ setIsLoadPending(false);
122
+ }
118
123
  continueRender(pending.handle);
119
124
  pendingLoadDelayRef.current = null;
120
125
  }, [continueRender]);
@@ -137,23 +142,33 @@ const CanvasImageContent = (0, react_1.forwardRef)(({ src, width, height, fit =
137
142
  }
138
143
  }, [ref, refForOutline]);
139
144
  (0, react_1.useLayoutEffect)(() => {
140
- const isPremounting = Boolean(sequenceContext === null || sequenceContext === void 0 ? void 0 : sequenceContext.premounting);
141
- const isPostmounting = Boolean(sequenceContext === null || sequenceContext === void 0 ? void 0 : sequenceContext.postmounting);
145
+ if (!pauseWhenLoading ||
146
+ !isLoadPending ||
147
+ isPremounting ||
148
+ isPostmounting) {
149
+ return;
150
+ }
151
+ return delayPlayback().unblock;
152
+ }, [
153
+ delayPlayback,
154
+ isLoadPending,
155
+ isPostmounting,
156
+ isPremounting,
157
+ pauseWhenLoading,
158
+ ]);
159
+ (0, react_1.useLayoutEffect)(() => {
142
160
  const handle = delayRender(`Rendering <CanvasImage> with src="${(0, truncate_src_for_label_js_1.truncateSrcForLabel)(actualSrc)}"`, {
143
161
  retries: delayRenderRetries !== null && delayRenderRetries !== void 0 ? delayRenderRetries : undefined,
144
162
  timeoutInMilliseconds: delayRenderTimeoutInMilliseconds !== null && delayRenderTimeoutInMilliseconds !== void 0 ? delayRenderTimeoutInMilliseconds : undefined,
145
163
  });
146
- const unblock = pauseWhenLoading && !isPremounting && !isPostmounting
147
- ? delayPlayback().unblock
148
- : () => undefined;
149
164
  const controller = new AbortController();
150
165
  let cancelled = false;
151
166
  let errorCount = 0;
152
167
  let timeoutId = null;
153
168
  setLoadedImage(null);
169
+ setIsLoadPending(true);
154
170
  pendingLoadDelayRef.current = {
155
171
  handle,
156
- unblock,
157
172
  continued: false,
158
173
  };
159
174
  const attemptLoad = () => {
@@ -166,7 +181,7 @@ const CanvasImageContent = (0, react_1.forwardRef)(({ src, width, height, fit =
166
181
  })
167
182
  .catch((err) => {
168
183
  if (err.name === 'AbortError') {
169
- continuePendingLoadDelay();
184
+ continuePendingLoadDelay({ markAsReady: false });
170
185
  return;
171
186
  }
172
187
  errorCount++;
@@ -182,7 +197,7 @@ const CanvasImageContent = (0, react_1.forwardRef)(({ src, width, height, fit =
182
197
  }
183
198
  else if (onError) {
184
199
  onError(err);
185
- continuePendingLoadDelay();
200
+ continuePendingLoadDelay({ markAsReady: true });
186
201
  }
187
202
  else {
188
203
  cancelRender(err);
@@ -196,21 +211,17 @@ const CanvasImageContent = (0, react_1.forwardRef)(({ src, width, height, fit =
196
211
  clearTimeout(timeoutId);
197
212
  }
198
213
  controller.abort();
199
- continuePendingLoadDelay();
214
+ continuePendingLoadDelay({ markAsReady: false });
200
215
  };
201
216
  }, [
202
217
  actualSrc,
203
218
  cancelRender,
204
219
  continuePendingLoadDelay,
205
- delayPlayback,
206
220
  delayRender,
207
221
  delayRenderRetries,
208
222
  delayRenderTimeoutInMilliseconds,
209
223
  maxRetries,
210
224
  onError,
211
- pauseWhenLoading,
212
- sequenceContext === null || sequenceContext === void 0 ? void 0 : sequenceContext.postmounting,
213
- sequenceContext === null || sequenceContext === void 0 ? void 0 : sequenceContext.premounting,
214
225
  ]);
215
226
  (0, react_1.useLayoutEffect)(() => {
216
227
  if (!loadedImage || !outputCanvas || !sourceCanvas) {
@@ -261,7 +272,7 @@ const CanvasImageContent = (0, react_1.forwardRef)(({ src, width, height, fit =
261
272
  return;
262
273
  }
263
274
  continueRenderOnce();
264
- continuePendingLoadDelay();
275
+ continuePendingLoadDelay({ markAsReady: true });
265
276
  },
266
277
  });
267
278
  }
@@ -273,7 +284,7 @@ const CanvasImageContent = (0, react_1.forwardRef)(({ src, width, height, fit =
273
284
  if (onError) {
274
285
  onError(err);
275
286
  continueRenderOnce();
276
- continuePendingLoadDelay();
287
+ continuePendingLoadDelay({ markAsReady: true });
277
288
  }
278
289
  else {
279
290
  cancelRender(err);
@@ -0,0 +1,44 @@
1
+ export type MediaSyncAction = {
2
+ type: 'seek-due-to-shift';
3
+ shouldBeTime: number;
4
+ why: string;
5
+ bufferUntilFirstFrame: boolean;
6
+ playReason: string | null;
7
+ warnAboutNonSeekable: boolean;
8
+ } | {
9
+ type: 'seek-if-not-playing';
10
+ shouldBeTime: number;
11
+ why: string | null;
12
+ } | {
13
+ type: 'play-and-seek';
14
+ shouldBeTime: number;
15
+ why: string | null;
16
+ playReason: string;
17
+ bufferUntilFirstFrame: boolean;
18
+ } | {
19
+ type: 'none';
20
+ };
21
+ export type GetMediaSyncActionInput = {
22
+ duration: number;
23
+ currentTime: number;
24
+ paused: boolean;
25
+ ended: boolean;
26
+ desiredUnclampedTime: number;
27
+ mediaTagTime: number;
28
+ mediaTagLastUpdate: number;
29
+ rvcTime: number | null;
30
+ rvcLastUpdate: number | null;
31
+ isVariableFpsVideo: boolean;
32
+ acceptableTimeShift: number;
33
+ lastSeekDueToShift: number | null;
34
+ playing: boolean;
35
+ playbackRate: number;
36
+ mediaTagBufferingOrStalled: boolean;
37
+ playerBuffering: boolean;
38
+ absoluteFrame: number;
39
+ onlyWarnForMediaSeekingError: boolean;
40
+ isPremounting: boolean;
41
+ isPostmounting: boolean;
42
+ pauseWhenBuffering: boolean;
43
+ };
44
+ export declare const getMediaSyncAction: (input: GetMediaSyncActionInput) => MediaSyncAction;
@@ -0,0 +1,78 @@
1
+ "use strict";
2
+ // Pure decision logic for `useMediaPlayback`.
3
+ //
4
+ // Every frame, the media element's clock has to be reconciled with the
5
+ // timeline clock. This function takes a snapshot of the relevant state and
6
+ // decides *what* should happen (seek / play / buffer / nothing). The effect in
7
+ // `use-media-playback.ts` is responsible for actually executing the returned
8
+ // action. Keeping the decision separate makes the branch ordering testable
9
+ // without a DOM or a real media element.
10
+ Object.defineProperty(exports, "__esModule", { value: true });
11
+ exports.getMediaSyncAction = void 0;
12
+ const getMediaSyncAction = (input) => {
13
+ const { duration, currentTime, paused, ended, desiredUnclampedTime, mediaTagTime, mediaTagLastUpdate, rvcTime, rvcLastUpdate, isVariableFpsVideo, acceptableTimeShift, lastSeekDueToShift, playing, playbackRate, mediaTagBufferingOrStalled, playerBuffering, absoluteFrame, onlyWarnForMediaSeekingError, isPremounting, isPostmounting, pauseWhenBuffering, } = input;
14
+ const shouldBeTime = !Number.isNaN(duration) && Number.isFinite(duration)
15
+ ? Math.min(duration, desiredUnclampedTime)
16
+ : desiredUnclampedTime;
17
+ const timeShiftMediaTag = Math.abs(shouldBeTime - mediaTagTime);
18
+ const timeShiftRvcTag = rvcTime ? Math.abs(shouldBeTime - rvcTime) : null;
19
+ const mostRecentTimeshift = rvcLastUpdate && rvcTime > mediaTagLastUpdate
20
+ ? timeShiftRvcTag
21
+ : timeShiftMediaTag;
22
+ const timeShift = timeShiftRvcTag && !isVariableFpsVideo
23
+ ? mostRecentTimeshift
24
+ : timeShiftMediaTag;
25
+ if (timeShift > acceptableTimeShift && lastSeekDueToShift !== shouldBeTime) {
26
+ // If scrubbing around, adjust timing
27
+ // or if time shift is bigger than 0.45sec
28
+ return {
29
+ type: 'seek-due-to-shift',
30
+ shouldBeTime,
31
+ why: `because time shift is too big. shouldBeTime = ${shouldBeTime}, isTime = ${mediaTagTime}, requestVideoCallbackTime = ${rvcTime}, timeShift = ${timeShift}${isVariableFpsVideo ? ', isVariableFpsVideo = true' : ''}, isPremounting = ${isPremounting}, isPostmounting = ${isPostmounting}, pauseWhenBuffering = ${pauseWhenBuffering}`,
32
+ bufferUntilFirstFrame: playing && playbackRate > 0,
33
+ playReason: playing && paused
34
+ ? 'player is playing but media tag is paused, and just seeked'
35
+ : null,
36
+ warnAboutNonSeekable: !onlyWarnForMediaSeekingError,
37
+ };
38
+ }
39
+ const seekThreshold = playing ? 0.15 : 0.01;
40
+ // Only perform a seek if the time is not already the same.
41
+ // Chrome rounds to 6 digits, so 0.033333333 -> 0.033333,
42
+ // therefore a threshold is allowed.
43
+ // Refer to the https://github.com/remotion-dev/video-buffering-example
44
+ // which is fixed by only seeking conditionally.
45
+ const makesSenseToSeek = Math.abs(currentTime - shouldBeTime) > seekThreshold;
46
+ const isSomethingElseBuffering = playerBuffering && !mediaTagBufferingOrStalled;
47
+ if (!playing || isSomethingElseBuffering) {
48
+ return {
49
+ type: 'seek-if-not-playing',
50
+ shouldBeTime,
51
+ why: makesSenseToSeek
52
+ ? `not playing or something else is buffering. time offset is over seek threshold (${seekThreshold})`
53
+ : null,
54
+ };
55
+ }
56
+ if (!playing || playerBuffering) {
57
+ return { type: 'none' };
58
+ }
59
+ // We now assured we are in playing state and not buffering
60
+ const pausedCondition = paused && !ended;
61
+ const firstFrameCondition = absoluteFrame === 0;
62
+ if (pausedCondition || firstFrameCondition) {
63
+ const reason = pausedCondition
64
+ ? 'media tag is paused'
65
+ : 'absolute frame is 0';
66
+ return {
67
+ type: 'play-and-seek',
68
+ shouldBeTime,
69
+ why: makesSenseToSeek
70
+ ? `is over timeshift threshold (threshold = ${seekThreshold}) and ${reason}`
71
+ : null,
72
+ playReason: `player is playing and ${reason}`,
73
+ bufferUntilFirstFrame: !isVariableFpsVideo && playbackRate > 0,
74
+ };
75
+ }
76
+ return { type: 'none' };
77
+ };
78
+ exports.getMediaSyncAction = getMediaSyncAction;
@@ -694,7 +694,7 @@ export declare const Internals: {
694
694
  listenForResume: (callback: () => void) => {
695
695
  remove: () => void;
696
696
  };
697
- buffering: import("react").MutableRefObject<boolean>;
697
+ buffering: import("react").RefObject<boolean>;
698
698
  } | null>;
699
699
  readonly getComponentsToAddStacksTo: () => unknown[];
700
700
  readonly CurrentScaleContext: import("react").Context<import("./use-current-scale.js").CurrentScaleContextType | null>;
@@ -823,7 +823,7 @@ export declare const Internals: {
823
823
  listenForResume: (callback: () => void) => {
824
824
  remove: () => void;
825
825
  };
826
- buffering: import("react").MutableRefObject<boolean>;
826
+ buffering: import("react").RefObject<boolean>;
827
827
  }) => boolean;
828
828
  readonly TimelinePosition: typeof TimelinePosition;
829
829
  readonly DelayRenderContextType: import("react").Context<import("./delay-render.js").DelayRenderScope | null>;
@@ -0,0 +1,8 @@
1
+ import { type InterpolateOptions } from './interpolate.js';
2
+ export type InterpolateTranslateOptions = InterpolateOptions;
3
+ export declare const interpolateTranslate: (input: number, inputRange: readonly number[], outputRange: readonly string[], options?: Partial<{
4
+ easing: import("./interpolate.js").EasingFunction | readonly import("./interpolate.js").EasingFunction[];
5
+ extrapolateLeft: import("./interpolate.js").ExtrapolateType;
6
+ extrapolateRight: import("./interpolate.js").ExtrapolateType;
7
+ posterize: number;
8
+ }> | undefined) => string;