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.
- package/dist/cjs/HtmlInCanvas.js +2 -4
- package/dist/cjs/Img.js +18 -11
- package/dist/cjs/Still.js +2 -0
- package/dist/cjs/TimelineContext.d.ts +3 -3
- package/dist/cjs/animated-image/AnimatedImage.d.ts +13 -2
- package/dist/cjs/animated-image/AnimatedImage.js +13 -0
- package/dist/cjs/animated-image/canvas.d.ts +8 -11
- package/dist/cjs/animated-image/canvas.js +2 -2
- package/dist/cjs/animated-image/props.d.ts +2 -1
- package/dist/cjs/audio/shared-audio-tags.d.ts +1 -0
- package/dist/cjs/audio/shared-audio-tags.js +2 -2
- package/dist/cjs/buffering.d.ts +1 -1
- package/dist/cjs/buffering.js +14 -6
- package/dist/cjs/canvas-image/CanvasImage.js +28 -17
- package/dist/cjs/get-media-sync-action.d.ts +44 -0
- package/dist/cjs/get-media-sync-action.js +78 -0
- package/dist/cjs/internals.d.ts +2 -2
- package/dist/cjs/interpolate-translate.d.ts +8 -0
- package/dist/cjs/interpolate-translate.js +70 -0
- package/dist/cjs/timeline-position-state.d.ts +2 -2
- package/dist/cjs/use-media-playback.js +70 -82
- package/dist/cjs/use-request-video-callback-time.d.ts +1 -1
- package/dist/cjs/version.d.ts +1 -1
- package/dist/cjs/version.js +1 -1
- package/dist/cjs/wrap-in-schema.d.ts +3 -3
- package/dist/cjs/wrap-in-schema.js +2 -2
- package/dist/cjs/wrap-remotion-context.d.ts +1 -1
- package/dist/esm/index.mjs +224 -119
- package/dist/esm/version.mjs +1 -1
- package/package.json +2 -2
package/dist/cjs/HtmlInCanvas.js
CHANGED
|
@@ -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
|
-
|
|
93
|
-
|
|
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
|
-
|
|
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 {
|
|
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:
|
|
9
|
-
audioAndVideoTags:
|
|
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
|
-
|
|
5
|
-
|
|
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<
|
|
18
|
-
|
|
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;
|
package/dist/cjs/buffering.d.ts
CHANGED
|
@@ -17,7 +17,7 @@ type BufferManager = {
|
|
|
17
17
|
addBlock: AddBlock;
|
|
18
18
|
listenForBuffering: ListenForBuffering;
|
|
19
19
|
listenForResume: ListenForResume;
|
|
20
|
-
buffering: React.
|
|
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<{
|
package/dist/cjs/buffering.js
CHANGED
|
@@ -90,7 +90,11 @@ const useBufferManager = (logLevel, mountTime) => {
|
|
|
90
90
|
if (rendering) {
|
|
91
91
|
return;
|
|
92
92
|
}
|
|
93
|
-
|
|
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
|
-
|
|
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
|
-
|
|
152
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
141
|
-
|
|
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;
|
package/dist/cjs/internals.d.ts
CHANGED
|
@@ -694,7 +694,7 @@ export declare const Internals: {
|
|
|
694
694
|
listenForResume: (callback: () => void) => {
|
|
695
695
|
remove: () => void;
|
|
696
696
|
};
|
|
697
|
-
buffering: import("react").
|
|
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").
|
|
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;
|