remotion 4.0.465 → 4.0.467

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.
Files changed (40) hide show
  1. package/dist/cjs/CompositionManager.d.ts +1 -0
  2. package/dist/cjs/HtmlInCanvas.js +3 -1
  3. package/dist/cjs/Img.js +4 -4
  4. package/dist/cjs/Sequence.d.ts +4 -0
  5. package/dist/cjs/Sequence.js +6 -1
  6. package/dist/cjs/animated-image/AnimatedImage.js +1 -1
  7. package/dist/cjs/animated-image/canvas.js +3 -50
  8. package/dist/cjs/animated-image/props.d.ts +2 -1
  9. package/dist/cjs/audio/AudioForPreview.js +1 -0
  10. package/dist/cjs/audio/shared-audio-tags.d.ts +3 -2
  11. package/dist/cjs/audio/shared-audio-tags.js +26 -7
  12. package/dist/cjs/audio/use-audio-context.d.ts +4 -0
  13. package/dist/cjs/audio/use-audio-context.js +36 -0
  14. package/dist/cjs/calculate-image-fit.d.ts +8 -0
  15. package/dist/cjs/calculate-image-fit.js +52 -0
  16. package/dist/cjs/canvas-image/CanvasImage.d.ts +18 -0
  17. package/dist/cjs/canvas-image/CanvasImage.js +247 -0
  18. package/dist/cjs/canvas-image/index.d.ts +2 -0
  19. package/dist/cjs/canvas-image/index.js +5 -0
  20. package/dist/cjs/canvas-image/props.d.ts +26 -0
  21. package/dist/cjs/canvas-image/props.js +2 -0
  22. package/dist/cjs/effects/Solid.js +1 -1
  23. package/dist/cjs/effects/create-effect.js +2 -0
  24. package/dist/cjs/effects/effect-types.d.ts +1 -0
  25. package/dist/cjs/get-timeline-duration.js +2 -1
  26. package/dist/cjs/index.d.ts +6 -0
  27. package/dist/cjs/index.js +15 -9
  28. package/dist/cjs/internals.d.ts +9 -6
  29. package/dist/cjs/internals.js +11 -12
  30. package/dist/cjs/loop/index.js +1 -1
  31. package/dist/cjs/series/index.js +2 -2
  32. package/dist/cjs/use-media-in-timeline.d.ts +2 -1
  33. package/dist/cjs/use-media-in-timeline.js +3 -1
  34. package/dist/cjs/use-schema.d.ts +5 -0
  35. package/dist/cjs/version.d.ts +1 -1
  36. package/dist/cjs/version.js +1 -1
  37. package/dist/cjs/video/VideoForPreview.js +5 -0
  38. package/dist/esm/index.mjs +884 -502
  39. package/dist/esm/version.mjs +1 -1
  40. package/package.json +2 -2
@@ -60,6 +60,7 @@ export type TSequence = {
60
60
  duration: number;
61
61
  id: string;
62
62
  displayName: string;
63
+ documentationLink: string | null;
63
64
  parent: string | null;
64
65
  rootId: string;
65
66
  showInTimeline: boolean;
@@ -235,7 +235,9 @@ const HtmlInCanvasInner = (0, react_1.forwardRef)(({ width, height, effects = []
235
235
  const { durationInFrames: videoDuration } = (0, use_video_config_js_1.useVideoConfig)();
236
236
  const resolvedDuration = durationInFrames !== null && durationInFrames !== void 0 ? durationInFrames : videoDuration;
237
237
  const memoizedEffectDefinitions = (0, use_memoized_effects_js_1.useMemoizedEffectDefinitions)(effects);
238
- return ((0, jsx_runtime_1.jsx)(Sequence_js_1.Sequence, { durationInFrames: resolvedDuration, name: name !== null && name !== void 0 ? name : '<HtmlInCanvas>', _experimentalControls: controls, _remotionInternalEffects: memoizedEffectDefinitions, layout: "none", ...sequenceProps, children: (0, jsx_runtime_1.jsx)(HtmlInCanvasContent, { ref: ref, width: width, height: height, effects: effects, onPaint: onPaint, onInit: onInit, controls: controls, style: style, children: children }) }));
238
+ return ((0, jsx_runtime_1.jsx)(Sequence_js_1.Sequence, { durationInFrames: resolvedDuration, name: name !== null && name !== void 0 ? name : '<HtmlInCanvas>', _remotionInternalDocumentationLink: name === undefined
239
+ ? 'https://www.remotion.dev/docs/remotion/html-in-canvas'
240
+ : undefined, _experimentalControls: controls, _remotionInternalEffects: memoizedEffectDefinitions, layout: "none", ...sequenceProps, children: (0, jsx_runtime_1.jsx)(HtmlInCanvasContent, { ref: ref, width: width, height: height, effects: effects, onPaint: onPaint, onInit: onInit, controls: controls, style: style, children: children }) }));
239
241
  });
240
242
  HtmlInCanvasInner.displayName = 'HtmlInCanvas';
241
243
  const htmlInCanvasSchema = {
package/dist/cjs/Img.js CHANGED
@@ -25,7 +25,7 @@ function truncateSrcForLabel(src) {
25
25
  }
26
26
  return src;
27
27
  }
28
- const ImgContent = ({ onError, maxRetries = 2, src, pauseWhenLoading, delayRenderRetries, delayRenderTimeoutInMilliseconds, onImageFrame, crossOrigin, ref, ...props }) => {
28
+ const ImgContent = ({ onError, maxRetries = 2, src, pauseWhenLoading, delayRenderRetries, delayRenderTimeoutInMilliseconds, onImageFrame, crossOrigin, decoding, ref, ...props }) => {
29
29
  const imageRef = (0, react_1.useRef)(null);
30
30
  const errors = (0, react_1.useRef)({});
31
31
  const { delayPlayback } = (0, use_buffer_state_js_1.useBufferState)();
@@ -172,20 +172,20 @@ const ImgContent = ({ onError, maxRetries = 2, src, pauseWhenLoading, delayRende
172
172
  delayRender,
173
173
  ]);
174
174
  }
175
- const { isClientSideRendering } = (0, use_remotion_environment_js_1.useRemotionEnvironment)();
175
+ const { isClientSideRendering, isRendering } = (0, use_remotion_environment_js_1.useRemotionEnvironment)();
176
176
  const crossOriginValue = (0, get_cross_origin_value_js_1.getCrossOriginValue)({
177
177
  crossOrigin,
178
178
  requestsVideoFrame: false,
179
179
  isClientSideRendering,
180
180
  });
181
181
  // src gets set once we've loaded and decoded the image.
182
- return ((0, jsx_runtime_1.jsx)("img", { ...props, ref: imageRef, crossOrigin: crossOriginValue, onError: didGetError, decoding: "sync" }));
182
+ return ((0, jsx_runtime_1.jsx)("img", { ...props, ref: imageRef, crossOrigin: crossOriginValue, onError: didGetError, decoding: isRendering ? 'sync' : decoding }));
183
183
  };
184
184
  const ImgInner = ({ hidden, name, stack, showInTimeline, src, from, durationInFrames, _experimentalControls: controls, ...props }) => {
185
185
  if (!src) {
186
186
  throw new Error('No "src" prop was passed to <Img>.');
187
187
  }
188
- return ((0, jsx_runtime_1.jsx)(Sequence_js_1.Sequence, { layout: "none", from: from !== null && from !== void 0 ? from : 0, durationInFrames: durationInFrames !== null && durationInFrames !== void 0 ? durationInFrames : Infinity, _remotionInternalStack: stack, _remotionInternalIsMedia: { type: 'image', src }, name: name !== null && name !== void 0 ? name : '<Img>', _experimentalControls: controls, showInTimeline: showInTimeline !== null && showInTimeline !== void 0 ? showInTimeline : true, hidden: hidden, children: (0, jsx_runtime_1.jsx)(ImgContent, { src: src, ...props }) }));
188
+ return ((0, jsx_runtime_1.jsx)(Sequence_js_1.Sequence, { layout: "none", from: from !== null && from !== void 0 ? from : 0, durationInFrames: durationInFrames !== null && durationInFrames !== void 0 ? durationInFrames : Infinity, _remotionInternalStack: stack, _remotionInternalDocumentationLink: name === undefined ? 'https://www.remotion.dev/docs/img' : undefined, _remotionInternalIsMedia: { type: 'image', src }, name: name !== null && name !== void 0 ? name : '<Img>', _experimentalControls: controls, showInTimeline: showInTimeline !== null && showInTimeline !== void 0 ? showInTimeline : true, hidden: hidden, children: (0, jsx_runtime_1.jsx)(ImgContent, { src: src, ...props }) }));
189
189
  };
190
190
  const imgSchema = {
191
191
  ...sequence_field_schema_js_1.sequenceVisualStyleSchema,
@@ -40,6 +40,10 @@ export type SequencePropsWithoutDuration = {
40
40
  * @deprecated For internal use only.
41
41
  */
42
42
  readonly _remotionInternalStack?: string;
43
+ /**
44
+ * @deprecated For internal use only.
45
+ */
46
+ readonly _remotionInternalDocumentationLink?: string;
43
47
  /**
44
48
  * @deprecated For internal use only.
45
49
  */
@@ -18,7 +18,7 @@ const use_remotion_environment_js_1 = require("./use-remotion-environment.js");
18
18
  const use_video_config_js_1 = require("./use-video-config.js");
19
19
  const v5_flag_js_1 = require("./v5-flag.js");
20
20
  const wrap_in_schema_js_1 = require("./wrap-in-schema.js");
21
- const RegularSequenceRefForwardingFunction = ({ from = 0, durationInFrames = Infinity, children, name, height, width, showInTimeline = true, hidden = false, _experimentalControls: controls, _remotionInternalEffects, _remotionInternalLoopDisplay: loopDisplay, _remotionInternalStack: stack, _remotionInternalPremountDisplay: premountDisplay, _remotionInternalPostmountDisplay: postmountDisplay, _remotionInternalIsMedia: isMedia, ...other }, ref) => {
21
+ const RegularSequenceRefForwardingFunction = ({ from = 0, durationInFrames = Infinity, children, name, height, width, showInTimeline = true, hidden = false, _experimentalControls: controls, _remotionInternalEffects, _remotionInternalLoopDisplay: loopDisplay, _remotionInternalStack: stack, _remotionInternalDocumentationLink: documentationLink, _remotionInternalPremountDisplay: premountDisplay, _remotionInternalPostmountDisplay: postmountDisplay, _remotionInternalIsMedia: isMedia, ...other }, ref) => {
22
22
  var _a;
23
23
  const { layout = 'absolute-fill' } = other;
24
24
  const [id] = (0, react_1.useState)(() => String(Math.random()));
@@ -97,6 +97,7 @@ const RegularSequenceRefForwardingFunction = ({ from = 0, durationInFrames = Inf
97
97
  const timelineClipName = (0, react_1.useMemo)(() => {
98
98
  return name !== null && name !== void 0 ? name : '';
99
99
  }, [name]);
100
+ const resolvedDocumentationLink = documentationLink !== null && documentationLink !== void 0 ? documentationLink : (name === undefined ? 'https://www.remotion.dev/docs/sequence' : null);
100
101
  const env = (0, use_remotion_environment_js_1.useRemotionEnvironment)();
101
102
  const inheritedStack = (_a = other === null || other === void 0 ? void 0 : other.stack) !== null && _a !== void 0 ? _a : null;
102
103
  // Our assumption: Stack doesnt' change. After we symbolicate we assign it a nodePath
@@ -115,6 +116,7 @@ const RegularSequenceRefForwardingFunction = ({ from = 0, durationInFrames = Inf
115
116
  controls: controls !== null && controls !== void 0 ? controls : null,
116
117
  effects: _remotionInternalEffects !== null && _remotionInternalEffects !== void 0 ? _remotionInternalEffects : [],
117
118
  displayName: timelineClipName,
119
+ documentationLink: resolvedDocumentationLink,
118
120
  duration: actualDurationInFrames,
119
121
  from,
120
122
  id,
@@ -135,6 +137,7 @@ const RegularSequenceRefForwardingFunction = ({ from = 0, durationInFrames = Inf
135
137
  controls: controls !== null && controls !== void 0 ? controls : null,
136
138
  effects: _remotionInternalEffects !== null && _remotionInternalEffects !== void 0 ? _remotionInternalEffects : [],
137
139
  displayName: timelineClipName,
140
+ documentationLink: resolvedDocumentationLink,
138
141
  doesVolumeChange: isMedia.data.doesVolumeChange,
139
142
  duration: actualDurationInFrames,
140
143
  from,
@@ -162,6 +165,7 @@ const RegularSequenceRefForwardingFunction = ({ from = 0, durationInFrames = Inf
162
165
  duration: actualDurationInFrames,
163
166
  id,
164
167
  displayName: timelineClipName,
168
+ documentationLink: resolvedDocumentationLink,
165
169
  parent: (_c = parentSequence === null || parentSequence === void 0 ? void 0 : parentSequence.id) !== null && _c !== void 0 ? _c : null,
166
170
  type: 'sequence',
167
171
  rootId,
@@ -197,6 +201,7 @@ const RegularSequenceRefForwardingFunction = ({ from = 0, durationInFrames = Inf
197
201
  controls,
198
202
  _remotionInternalEffects,
199
203
  isMedia,
204
+ resolvedDocumentationLink,
200
205
  ]);
201
206
  // Ceil to support floats
202
207
  // https://github.com/remotion-dev/remotion/issues/2958
@@ -158,7 +158,7 @@ const AnimatedImageInner = ({ src, width, height, onError, fit, playbackRate, lo
158
158
  className,
159
159
  style,
160
160
  };
161
- return ((0, jsx_runtime_1.jsx)(Sequence_js_1.Sequence, { layout: "none", durationInFrames: resolvedDuration, name: "<AnimatedImage>", _experimentalControls: controls, _remotionInternalEffects: memoizedEffectDefinitions, ...sequenceProps, children: (0, jsx_runtime_1.jsx)(AnimatedImageContent, { ...animatedImageProps, ref: ref, effects: effects, controls: controls }) }));
161
+ return ((0, jsx_runtime_1.jsx)(Sequence_js_1.Sequence, { layout: "none", durationInFrames: resolvedDuration, name: "<AnimatedImage>", _remotionInternalDocumentationLink: "https://www.remotion.dev/docs/animatedimage", _experimentalControls: controls, _remotionInternalEffects: memoizedEffectDefinitions, ...sequenceProps, children: (0, jsx_runtime_1.jsx)(AnimatedImageContent, { ...animatedImageProps, ref: ref, effects: effects, controls: controls }) }));
162
162
  };
163
163
  exports.AnimatedImage = (0, wrap_in_schema_js_1.wrapInSchema)(AnimatedImageInner, animatedImageSchema);
164
164
  exports.AnimatedImage.displayName = 'AnimatedImage';
@@ -36,56 +36,9 @@ Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.Canvas = void 0;
37
37
  const jsx_runtime_1 = require("react/jsx-runtime");
38
38
  const react_1 = __importStar(require("react"));
39
+ const calculate_image_fit_js_1 = require("../calculate-image-fit.js");
39
40
  const run_effect_chain_js_1 = require("../effects/run-effect-chain.js");
40
41
  const use_effect_chain_state_js_1 = require("../effects/use-effect-chain-state.js");
41
- const calcArgs = (fit, frameSize, canvasSize) => {
42
- switch (fit) {
43
- case 'fill': {
44
- return [
45
- 0,
46
- 0,
47
- frameSize.width,
48
- frameSize.height,
49
- 0,
50
- 0,
51
- canvasSize.width,
52
- canvasSize.height,
53
- ];
54
- }
55
- case 'contain': {
56
- const ratio = Math.min(canvasSize.width / frameSize.width, canvasSize.height / frameSize.height);
57
- const centerX = (canvasSize.width - frameSize.width * ratio) / 2;
58
- const centerY = (canvasSize.height - frameSize.height * ratio) / 2;
59
- return [
60
- 0,
61
- 0,
62
- frameSize.width,
63
- frameSize.height,
64
- centerX,
65
- centerY,
66
- frameSize.width * ratio,
67
- frameSize.height * ratio,
68
- ];
69
- }
70
- case 'cover': {
71
- const ratio = Math.max(canvasSize.width / frameSize.width, canvasSize.height / frameSize.height);
72
- const centerX = (canvasSize.width - frameSize.width * ratio) / 2;
73
- const centerY = (canvasSize.height - frameSize.height * ratio) / 2;
74
- return [
75
- 0,
76
- 0,
77
- frameSize.width,
78
- frameSize.height,
79
- centerX,
80
- centerY,
81
- frameSize.width * ratio,
82
- frameSize.height * ratio,
83
- ];
84
- }
85
- default:
86
- throw new Error('Unknown fit: ' + fit);
87
- }
88
- };
89
42
  const CanvasRefForwardingFunction = ({ width, height, fit, className, style, effects }, ref) => {
90
43
  const canvasRef = (0, react_1.useRef)(null);
91
44
  const chainState = (0, use_effect_chain_state_js_1.useEffectChainState)();
@@ -95,7 +48,7 @@ const CanvasRefForwardingFunction = ({ width, height, fit, className, style, eff
95
48
  }
96
49
  return document.createElement('canvas');
97
50
  }, []);
98
- const draw = (0, react_1.useCallback)(async (imageData) => {
51
+ const draw = (0, react_1.useCallback)((imageData) => {
99
52
  const canvas = canvasRef.current;
100
53
  const canvasWidth = width !== null && width !== void 0 ? width : imageData.displayWidth;
101
54
  const canvasHeight = height !== null && height !== void 0 ? height : imageData.displayHeight;
@@ -111,7 +64,7 @@ const CanvasRefForwardingFunction = ({ width, height, fit, className, style, eff
111
64
  if (!sourceCtx) {
112
65
  throw new Error('Could not get 2d context for source canvas');
113
66
  }
114
- sourceCtx.drawImage(imageData, ...calcArgs(fit, {
67
+ sourceCtx.drawImage(imageData, ...(0, calculate_image_fit_js_1.calculateImageFit)(fit, {
115
68
  height: imageData.displayHeight,
116
69
  width: imageData.displayWidth,
117
70
  }, {
@@ -1,3 +1,4 @@
1
+ import type { ImageFit } from '../calculate-image-fit.js';
1
2
  import type { EffectsProp } from '../effects/effect-types.js';
2
3
  import type { SequenceProps } from '../Sequence.js';
3
4
  export type RemotionAnimatedImageLoopBehavior = 'loop' | 'pause-after-finish' | 'clear-after-finish';
@@ -17,4 +18,4 @@ export type AnimatedImageProps = Omit<SequenceProps, 'children' | 'durationInFra
17
18
  readonly durationInFrames?: number;
18
19
  readonly effects?: EffectsProp;
19
20
  };
20
- export type AnimatedImageFillMode = 'contain' | 'cover' | 'fill';
21
+ export type AnimatedImageFillMode = ImageFit;
@@ -134,6 +134,7 @@ const AudioForDevelopmentForwardRefFunction = (props, ref) => {
134
134
  premountDisplay: (_a = sequenceContext === null || sequenceContext === void 0 ? void 0 : sequenceContext.premountDisplay) !== null && _a !== void 0 ? _a : null,
135
135
  postmountDisplay: (_b = sequenceContext === null || sequenceContext === void 0 ? void 0 : sequenceContext.postmountDisplay) !== null && _b !== void 0 ? _b : null,
136
136
  loopDisplay: undefined,
137
+ documentationLink: name === undefined ? 'https://www.remotion.dev/docs/html5-audio' : null,
137
138
  });
138
139
  // putting playback before useVolume
139
140
  // because volume looks at playbackrate
@@ -1,6 +1,7 @@
1
1
  import { type AudioHTMLAttributes } from 'react';
2
2
  import React from 'react';
3
3
  import type { SharedElementSourceNode } from './shared-element-source-node.js';
4
+ import type { RemotionAudioContextState } from './use-audio-context.js';
4
5
  /**
5
6
  * This functionality of Remotion will keep a certain amount
6
7
  * of <audio> tags pre-mounted and by default filled with an empty audio track.
@@ -33,7 +34,6 @@ export type ScheduleAudioNodeResult = {
33
34
  export type ScheduleAudioNodeOptions = {
34
35
  readonly node: AudioBufferSourceNode;
35
36
  readonly mediaTimestamp: number;
36
- readonly currentTime: number;
37
37
  readonly scheduledTime: number;
38
38
  readonly originalUnloopedMediaTimestamp: number;
39
39
  readonly duration: number;
@@ -49,6 +49,7 @@ export type AudioSyncAnchorEmitter = {
49
49
  };
50
50
  type SharedAudioContextValue = {
51
51
  audioContext: AudioContext | null;
52
+ getAudioContextState: () => RemotionAudioContextState | null;
52
53
  gainNode: GainNode | null;
53
54
  audioSyncAnchor: {
54
55
  value: number;
@@ -56,7 +57,7 @@ type SharedAudioContextValue = {
56
57
  audioSyncAnchorEmitter: AudioSyncAnchorEmitter;
57
58
  scheduleAudioNode: (options: ScheduleAudioNodeOptions) => ScheduleAudioNodeResult;
58
59
  resume: () => Promise<void>;
59
- suspend: () => void;
60
+ suspend: () => Promise<void>;
60
61
  getIsResumingAudioContext: () => Promise<void> | null;
61
62
  unscheduleAudioNode: (node: AudioBufferSourceNode) => void;
62
63
  };
@@ -77,6 +77,17 @@ const didPropChange = (key, newProp, prevProp) => {
77
77
  };
78
78
  exports.SharedAudioContext = (0, react_1.createContext)(null);
79
79
  exports.SharedAudioTagsContext = (0, react_1.createContext)(null);
80
+ const shouldSaveForLater = (state) => {
81
+ if (state === 'suspended' ||
82
+ state === 'running-to-suspended' ||
83
+ state === 'interrupted') {
84
+ return true;
85
+ }
86
+ if (state === 'running' || state === 'suspended-to-running') {
87
+ return false;
88
+ }
89
+ throw new Error(`Unexpected audio context state: ${state}`);
90
+ };
80
91
  const SharedAudioContextProvider = ({ children, audioLatencyHint, audioEnabled }) => {
81
92
  const logLevel = (0, log_level_context_js_1.useLogLevel)();
82
93
  const ctxAndGain = (0, use_audio_context_js_1.useSingletonAudioContext)({
@@ -110,11 +121,18 @@ const SharedAudioContextProvider = ({ children, audioLatencyHint, audioEnabled }
110
121
  nodesToResume.current.delete(node);
111
122
  }, []);
112
123
  const scheduleAudioNode = (0, react_1.useMemo)(() => {
113
- return ({ node, mediaTimestamp, currentTime, scheduledTime, duration, offset, originalUnloopedMediaTimestamp, }) => {
124
+ return ({ node, mediaTimestamp, scheduledTime, duration, offset, originalUnloopedMediaTimestamp, }) => {
114
125
  if (!ctxAndGain) {
115
126
  throw new Error('Audio context not found');
116
127
  }
117
- const saveForLater = ctxAndGain.audioContext.state === 'suspended' && !isResuming.current;
128
+ const currentState = ctxAndGain.getState();
129
+ if (currentState === 'closed') {
130
+ return {
131
+ type: 'not-started',
132
+ reason: 'audio context is closed',
133
+ };
134
+ }
135
+ const saveForLater = shouldSaveForLater(currentState);
118
136
  if (duration > 0) {
119
137
  if (saveForLater) {
120
138
  nodesToResume.current.set(node, {
@@ -145,7 +163,7 @@ const SharedAudioContextProvider = ({ children, audioLatencyHint, audioEnabled }
145
163
  : 'color: blue; font-weight: bold', duration < 0
146
164
  ? 'missed ' + Math.abs(offset).toFixed(2) + 's'
147
165
  : Math.abs(timeDiff).toFixed(2) +
148
- (timeDiff < 0 ? ' delay' : ' ahead'), '', 'current=' + currentTime.toFixed(4), 'actualcurrent=' + ctxAndGain.audioContext.currentTime.toFixed(4), 'offset=' + offset.toFixed(4), 'latency=' + latency.toFixed(4), 'state=' + ctxAndGain.audioContext.state, originalUnloopedMediaTimestamp !== mediaTime
166
+ (timeDiff < 0 ? ' delay' : ' ahead'), '', 'current=' + ctxAndGain.audioContext.currentTime.toFixed(4), 'offset=' + offset.toFixed(4), 'latency=' + latency.toFixed(4), 'state=' + ctxAndGain.audioContext.state, originalUnloopedMediaTimestamp !== mediaTime
149
167
  ? 'original_ts=' + originalUnloopedMediaTimestamp.toFixed(4)
150
168
  : '', 'action=' + (saveForLater ? 'schedule' : 'start'), '');
151
169
  prev.scheduledEndTime = scheduledEndTime;
@@ -176,7 +194,7 @@ const SharedAudioContextProvider = ({ children, audioLatencyHint, audioEnabled }
176
194
  node.start(r.scheduledTime, r.offset, r.duration);
177
195
  });
178
196
  nodesToResume.current.clear();
179
- const resumePromise = ctxAndGain.audioContext.resume();
197
+ const resumePromise = ctxAndGain.resume();
180
198
  isResuming.current = new Promise((resolve) => {
181
199
  (0, wait_until_actually_resumed_js_1.waitUntilActuallyResumed)(ctxAndGain.audioContext, logLevel).then(resolve);
182
200
  resumePromise.catch((err) => {
@@ -196,18 +214,19 @@ const SharedAudioContextProvider = ({ children, audioLatencyHint, audioEnabled }
196
214
  }, []);
197
215
  const suspend = (0, react_1.useCallback)(() => {
198
216
  if (!ctxAndGain) {
199
- return;
217
+ return Promise.resolve();
200
218
  }
201
219
  if (!audioContextIsPlayingEventually.current) {
202
- return;
220
+ return Promise.resolve();
203
221
  }
204
222
  audioContextIsPlayingEventually.current = false;
205
- ctxAndGain.audioContext.suspend();
223
+ return ctxAndGain.suspend();
206
224
  }, [ctxAndGain]);
207
225
  const audioContextValue = (0, react_1.useMemo)(() => {
208
226
  var _a, _b;
209
227
  return {
210
228
  audioContext: (_a = ctxAndGain === null || ctxAndGain === void 0 ? void 0 : ctxAndGain.audioContext) !== null && _a !== void 0 ? _a : null,
229
+ getAudioContextState: () => { var _a; return (_a = ctxAndGain === null || ctxAndGain === void 0 ? void 0 : ctxAndGain.getState()) !== null && _a !== void 0 ? _a : null; },
211
230
  gainNode: (_b = ctxAndGain === null || ctxAndGain === void 0 ? void 0 : ctxAndGain.gainNode) !== null && _b !== void 0 ? _b : null,
212
231
  audioSyncAnchor,
213
232
  audioSyncAnchorEmitter,
@@ -1,4 +1,5 @@
1
1
  import type { LogLevel } from '../log';
2
+ export type RemotionAudioContextState = AudioContextState | 'running-to-suspended' | 'suspended-to-running';
2
3
  export declare const useSingletonAudioContext: ({ logLevel, latencyHint, audioEnabled, }: {
3
4
  logLevel: LogLevel;
4
5
  latencyHint: AudioContextLatencyCategory;
@@ -6,4 +7,7 @@ export declare const useSingletonAudioContext: ({ logLevel, latencyHint, audioEn
6
7
  }) => {
7
8
  audioContext: AudioContext;
8
9
  gainNode: GainNode;
10
+ getState: () => RemotionAudioContextState;
11
+ resume: () => Promise<void>;
12
+ suspend: () => Promise<void>;
9
13
  } | null;
@@ -39,9 +39,45 @@ const useSingletonAudioContext = ({ logLevel, latencyHint, audioEnabled, }) => {
39
39
  gainNode.connect(audioContext.destination);
40
40
  log_1.Log.trace({ logLevel, tag: 'audio' }, 'Creating new audio context');
41
41
  audioContext.suspend();
42
+ // Tracks the state we are transitioning towards while resume()/suspend()
43
+ // have been called but the native state has not updated yet.
44
+ let transitionTarget = null;
45
+ const getState = () => {
46
+ const nativeState = audioContext.state;
47
+ if (transitionTarget === 'running' && nativeState !== 'running') {
48
+ return 'suspended-to-running';
49
+ }
50
+ if (transitionTarget === 'suspended' && nativeState !== 'suspended') {
51
+ return 'running-to-suspended';
52
+ }
53
+ return nativeState;
54
+ };
55
+ const resume = () => {
56
+ transitionTarget = 'running';
57
+ const promise = audioContext.resume();
58
+ promise.finally(() => {
59
+ if (transitionTarget === 'running') {
60
+ transitionTarget = null;
61
+ }
62
+ });
63
+ return promise;
64
+ };
65
+ const suspend = () => {
66
+ transitionTarget = 'suspended';
67
+ const promise = audioContext.suspend();
68
+ promise.finally(() => {
69
+ if (transitionTarget === 'suspended') {
70
+ transitionTarget = null;
71
+ }
72
+ });
73
+ return promise;
74
+ };
42
75
  return {
43
76
  audioContext,
44
77
  gainNode,
78
+ getState,
79
+ resume,
80
+ suspend,
45
81
  };
46
82
  }, [logLevel, latencyHint, env.isRendering, audioEnabled]);
47
83
  return context;
@@ -0,0 +1,8 @@
1
+ export type ImageFit = 'contain' | 'cover' | 'fill';
2
+ export declare const calculateImageFit: (fit: ImageFit, imageSize: {
3
+ width: number;
4
+ height: number;
5
+ }, canvasSize: {
6
+ width: number;
7
+ height: number;
8
+ }) => [number, number, number, number, number, number, number, number];
@@ -0,0 +1,52 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.calculateImageFit = void 0;
4
+ const calculateImageFit = (fit, imageSize, canvasSize) => {
5
+ switch (fit) {
6
+ case 'fill': {
7
+ return [
8
+ 0,
9
+ 0,
10
+ imageSize.width,
11
+ imageSize.height,
12
+ 0,
13
+ 0,
14
+ canvasSize.width,
15
+ canvasSize.height,
16
+ ];
17
+ }
18
+ case 'contain': {
19
+ const ratio = Math.min(canvasSize.width / imageSize.width, canvasSize.height / imageSize.height);
20
+ const centerX = (canvasSize.width - imageSize.width * ratio) / 2;
21
+ const centerY = (canvasSize.height - imageSize.height * ratio) / 2;
22
+ return [
23
+ 0,
24
+ 0,
25
+ imageSize.width,
26
+ imageSize.height,
27
+ centerX,
28
+ centerY,
29
+ imageSize.width * ratio,
30
+ imageSize.height * ratio,
31
+ ];
32
+ }
33
+ case 'cover': {
34
+ const ratio = Math.max(canvasSize.width / imageSize.width, canvasSize.height / imageSize.height);
35
+ const centerX = (canvasSize.width - imageSize.width * ratio) / 2;
36
+ const centerY = (canvasSize.height - imageSize.height * ratio) / 2;
37
+ return [
38
+ 0,
39
+ 0,
40
+ imageSize.width,
41
+ imageSize.height,
42
+ centerX,
43
+ centerY,
44
+ imageSize.width * ratio,
45
+ imageSize.height * ratio,
46
+ ];
47
+ }
48
+ default:
49
+ throw new Error('Unknown fit: ' + fit);
50
+ }
51
+ };
52
+ exports.calculateImageFit = calculateImageFit;
@@ -0,0 +1,18 @@
1
+ import type { EffectsProp } from '../effects/effect-types.js';
2
+ export declare const CanvasImage: import("react").ComponentType<Pick<import("../Sequence.js").SequenceProps, "hidden" | "name" | "durationInFrames" | "from" | "showInTimeline"> & {
3
+ readonly stack?: string;
4
+ } & {
5
+ readonly src: string;
6
+ readonly width?: number;
7
+ readonly height?: number;
8
+ readonly fit?: import("../calculate-image-fit.js").ImageFit;
9
+ readonly effects?: EffectsProp;
10
+ readonly className?: string;
11
+ readonly style?: React.CSSProperties;
12
+ readonly id?: string;
13
+ readonly onError?: (error: Error) => void;
14
+ readonly pauseWhenLoading?: boolean;
15
+ readonly maxRetries?: number;
16
+ readonly delayRenderRetries?: number;
17
+ readonly delayRenderTimeoutInMilliseconds?: number;
18
+ } & import("react").RefAttributes<HTMLCanvasElement>>;