var __defProp = Object.defineProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true, configurable: true, set: (newValue) => all[name] = () => newValue }); }; // src/_check-rsc.ts import { createContext } from "react"; if (typeof createContext !== "function") { const err = [ 'Remotion requires React.createContext, but it is "undefined".', 'If you are in a React Server Component, turn it into a client component by adding "use client" at the top of the file.', "", "Before:", ' import {useCurrentFrame} from "remotion";', "", "After:", ' "use client";', ' import {useCurrentFrame} from "remotion";' ]; throw new Error(err.join(` `)); } // src/Clipper.tsx var Clipper = () => { throw new Error(" has been removed as of Remotion v4.0.228. The native clipping APIs were experimental and subject to removal at any time. We removed them because they were sparingly used and made rendering often slower rather than faster."); }; // src/enable-sequence-stack-traces.ts import React from "react"; import JsxRuntime from "react/jsx-runtime"; // src/get-remotion-environment.ts function getNodeEnvString() { return ["NOD", "E_EN", "V"].join(""); } var getEnvString = () => { return ["e", "nv"].join(""); }; var getRemotionEnvironment = () => { const isPlayer = typeof window !== "undefined" && window.remotion_isPlayer; const isRendering = typeof window !== "undefined" && typeof window.process !== "undefined" && typeof window.process.env !== "undefined" && (window.process[getEnvString()][getNodeEnvString()] === "test" || window.process[getEnvString()][getNodeEnvString()] === "production" && typeof window !== "undefined" && typeof window.remotion_puppeteerTimeout !== "undefined"); const isStudio = typeof window !== "undefined" && window.remotion_isStudio; const isReadOnlyStudio = typeof window !== "undefined" && window.remotion_isReadOnlyStudio; return { isStudio, isRendering, isPlayer, isReadOnlyStudio, isClientSideRendering: false }; }; // src/enable-sequence-stack-traces.ts var originalCreateElement = React.createElement; var originalJsx = JsxRuntime.jsx; var componentsToAddStacksTo = []; var enableProxy = (api) => { return new Proxy(api, { apply(target, thisArg, argArray) { if (componentsToAddStacksTo.includes(argArray[0])) { const [first, props, ...rest] = argArray; const newProps = props.stack ? props : { ...props ?? {}, stack: new Error().stack }; return Reflect.apply(target, thisArg, [first, newProps, ...rest]); } return Reflect.apply(target, thisArg, argArray); } }); }; var enableSequenceStackTraces = () => { if (!getRemotionEnvironment().isStudio) { return; } React.createElement = enableProxy(originalCreateElement); JsxRuntime.jsx = enableProxy(originalJsx); }; var addSequenceStackTraces = (component) => { componentsToAddStacksTo.push(component); enableSequenceStackTraces(); }; // src/is-player.tsx import { createContext as createContext2, useContext } from "react"; import { jsx } from "react/jsx-runtime"; var IsPlayerContext = createContext2(false); var IsPlayerContextProvider = ({ children }) => { return /* @__PURE__ */ jsx(IsPlayerContext.Provider, { value: true, children }); }; var useIsPlayer = () => { return useContext(IsPlayerContext); }; // src/truthy.ts function truthy(value) { return Boolean(value); } // src/version.ts var VERSION = "4.0.423"; // src/multiple-versions-warning.ts var checkMultipleRemotionVersions = () => { if (typeof globalThis === "undefined") { return; } const set = () => { globalThis.remotion_imported = VERSION; if (typeof window !== "undefined") { window.remotion_imported = VERSION; } }; const alreadyImported = globalThis.remotion_imported || typeof window !== "undefined" && window.remotion_imported; if (alreadyImported) { if (alreadyImported === VERSION) { return; } if (typeof alreadyImported === "string" && alreadyImported.includes("webcodecs")) { set(); return; } throw new TypeError(`\uD83D\uDEA8 Multiple versions of Remotion detected: ${[ VERSION, typeof alreadyImported === "string" ? alreadyImported : "an older version" ].filter(truthy).join(" and ")}. This will cause things to break in an unexpected way. Check that all your Remotion packages are on the same version. If your dependencies depend on Remotion, make them peer dependencies. You can also run \`npx remotion versions\` from your terminal to see which versions are mismatching.`); } set(); }; // src/Null.tsx var Null = () => { throw new Error(" has been removed as of Remotion v4.0.228. The native clipping APIs were experimental and subject to removal at any time. We removed them because they were sparingly used and made rendering often slower rather than faster."); }; // src/Sequence.tsx import { forwardRef as forwardRef2, useContext as useContext13, useEffect as useEffect2, useMemo as useMemo10, useState as useState5 } from "react"; // src/AbsoluteFill.tsx import { forwardRef, useMemo } from "react"; import { jsx as jsx2 } from "react/jsx-runtime"; var hasTailwindClassName = ({ className, classPrefix, type }) => { if (!className) { return false; } if (type === "exact") { const split = className.split(" "); return classPrefix.some((token) => { return split.some((part) => { return part.trim() === token || part.trim().endsWith(`:${token}`) || part.trim().endsWith(`!${token}`); }); }); } return classPrefix.some((prefix) => { return className.startsWith(prefix) || className.includes(` ${prefix}`) || className.includes(`!${prefix}`) || className.includes(`:${prefix}`); }); }; var AbsoluteFillRefForwarding = (props, ref) => { const { style, ...other } = props; const actualStyle = useMemo(() => { return { position: "absolute", top: hasTailwindClassName({ className: other.className, classPrefix: ["top-", "inset-"], type: "prefix" }) ? undefined : 0, left: hasTailwindClassName({ className: other.className, classPrefix: ["left-", "inset-"], type: "prefix" }) ? undefined : 0, right: hasTailwindClassName({ className: other.className, classPrefix: ["right-", "inset-"], type: "prefix" }) ? undefined : 0, bottom: hasTailwindClassName({ className: other.className, classPrefix: ["bottom-", "inset-"], type: "prefix" }) ? undefined : 0, width: hasTailwindClassName({ className: other.className, classPrefix: ["w-"], type: "prefix" }) ? undefined : "100%", height: hasTailwindClassName({ className: other.className, classPrefix: ["h-"], type: "prefix" }) ? undefined : "100%", display: hasTailwindClassName({ className: other.className, classPrefix: [ "block", "inline-block", "inline", "flex", "inline-flex", "flow-root", "grid", "inline-grid", "contents", "list-item", "hidden" ], type: "exact" }) ? undefined : "flex", flexDirection: hasTailwindClassName({ className: other.className, classPrefix: [ "flex-row", "flex-col", "flex-row-reverse", "flex-col-reverse" ], type: "exact" }) ? undefined : "column", ...style }; }, [other.className, style]); return /* @__PURE__ */ jsx2("div", { ref, style: actualStyle, ...other }); }; var AbsoluteFill = forwardRef(AbsoluteFillRefForwarding); // src/SequenceContext.tsx import { createContext as createContext3 } from "react"; var SequenceContext = createContext3(null); // src/SequenceManager.tsx import React3, { useCallback, useMemo as useMemo2, useState } from "react"; import { jsx as jsx3 } from "react/jsx-runtime"; var SequenceManager = React3.createContext({ registerSequence: () => { throw new Error("SequenceManagerContext not initialized"); }, unregisterSequence: () => { throw new Error("SequenceManagerContext not initialized"); }, sequences: [] }); var SequenceVisibilityToggleContext = React3.createContext({ hidden: {}, setHidden: () => { throw new Error("SequenceVisibilityToggle not initialized"); } }); var SequenceManagerProvider = ({ children }) => { const [sequences, setSequences] = useState([]); const [hidden, setHidden] = useState({}); const registerSequence = useCallback((seq) => { setSequences((seqs) => { return [...seqs, seq]; }); }, []); const unregisterSequence = useCallback((seq) => { setSequences((seqs) => seqs.filter((s) => s.id !== seq)); }, []); const sequenceContext = useMemo2(() => { return { registerSequence, sequences, unregisterSequence }; }, [registerSequence, sequences, unregisterSequence]); const hiddenContext = useMemo2(() => { return { hidden, setHidden }; }, [hidden]); return /* @__PURE__ */ jsx3(SequenceManager.Provider, { value: sequenceContext, children: /* @__PURE__ */ jsx3(SequenceVisibilityToggleContext.Provider, { value: hiddenContext, children }) }); }; // src/TimelineContext.tsx import { createContext as createContext9, useLayoutEffect, useMemo as useMemo7, useRef, useState as useState3 } from "react"; // src/random.ts function mulberry32(a) { let t = a + 1831565813; t = Math.imul(t ^ t >>> 15, t | 1); t ^= t + Math.imul(t ^ t >>> 7, t | 61); return ((t ^ t >>> 14) >>> 0) / 4294967296; } function hashCode(str) { let i = 0; let chr = 0; let hash = 0; for (i = 0;i < str.length; i++) { chr = str.charCodeAt(i); hash = (hash << 5) - hash + chr; hash |= 0; } return hash; } var random = (seed, dummy) => { if (dummy !== undefined) { throw new TypeError("random() takes only one argument"); } if (seed === null) { return Math.random(); } if (typeof seed === "string") { return mulberry32(hashCode(seed)); } if (typeof seed === "number") { return mulberry32(seed * 10000000000); } throw new Error("random() argument must be a number or a string"); }; // src/timeline-position-state.ts var exports_timeline_position_state = {}; __export(exports_timeline_position_state, { useTimelineSetFrame: () => useTimelineSetFrame, useTimelinePosition: () => useTimelinePosition, usePlayingState: () => usePlayingState, persistCurrentFrame: () => persistCurrentFrame, getInitialFrameState: () => getInitialFrameState, getFrameForComposition: () => getFrameForComposition }); import { useContext as useContext5, useMemo as useMemo6 } from "react"; // src/use-remotion-environment.ts import { useContext as useContext2, useState as useState2 } from "react"; // src/remotion-environment-context.ts import React4 from "react"; var RemotionEnvironmentContext = React4.createContext(null); // src/use-remotion-environment.ts var useRemotionEnvironment = () => { const context = useContext2(RemotionEnvironmentContext); const [env] = useState2(() => getRemotionEnvironment()); return context ?? env; }; // src/use-video.ts import { useContext as useContext4, useMemo as useMemo5 } from "react"; // src/CompositionManagerContext.tsx import { createContext as createContext4 } from "react"; var CompositionManager = createContext4({ compositions: [], folders: [], currentCompositionMetadata: null, canvasContent: null }); var CompositionSetters = createContext4({ registerComposition: () => { return; }, unregisterComposition: () => { return; }, registerFolder: () => { return; }, unregisterFolder: () => { return; }, setCanvasContent: () => { return; }, updateCompositionDefaultProps: () => { return; }, onlyRenderComposition: null }); // src/ResolveCompositionConfig.tsx import { createContext as createContext6, createRef, useContext as useContext3, useMemo as useMemo4 } from "react"; // src/EditorProps.tsx import React5, { createContext as createContext5, useCallback as useCallback2, useImperativeHandle, useMemo as useMemo3 } from "react"; import { jsx as jsx4 } from "react/jsx-runtime"; var EditorPropsContext = createContext5({ props: {}, updateProps: () => { throw new Error("Not implemented"); }, resetUnsaved: () => { throw new Error("Not implemented"); } }); var editorPropsProviderRef = React5.createRef(); var timeValueRef = React5.createRef(); var EditorPropsProvider = ({ children }) => { const [props, setProps] = React5.useState({}); const updateProps = useCallback2(({ defaultProps, id, newProps }) => { setProps((prev) => { return { ...prev, [id]: typeof newProps === "function" ? newProps(prev[id] ?? defaultProps) : newProps }; }); }, []); const resetUnsaved = useCallback2((compositionId) => { setProps((prev) => { if (prev[compositionId]) { const newProps = { ...prev }; delete newProps[compositionId]; return newProps; } return prev; }); }, []); useImperativeHandle(editorPropsProviderRef, () => { return { getProps: () => props, setProps }; }, [props]); const ctx = useMemo3(() => { return { props, updateProps, resetUnsaved }; }, [props, resetUnsaved, updateProps]); return /* @__PURE__ */ jsx4(EditorPropsContext.Provider, { value: ctx, children }); }; // src/input-props-override.ts var getKey = () => { return `remotion_inputPropsOverride` + window.location.origin; }; var getInputPropsOverride = () => { if (typeof localStorage === "undefined") return null; const override = localStorage.getItem(getKey()); if (!override) return null; return JSON.parse(override); }; var setInputPropsOverride = (override) => { if (typeof localStorage === "undefined") return; if (override === null) { localStorage.removeItem(getKey()); return; } localStorage.setItem(getKey(), JSON.stringify(override)); }; // src/input-props-serialization.ts var DATE_TOKEN = "remotion-date:"; var FILE_TOKEN = "remotion-file:"; var serializeJSONWithSpecialTypes = ({ data, indent, staticBase }) => { let customDateUsed = false; let customFileUsed = false; let mapUsed = false; let setUsed = false; try { const serializedString = JSON.stringify(data, function(key, value) { const item = this[key]; if (item instanceof Date) { customDateUsed = true; return `${DATE_TOKEN}${item.toISOString()}`; } if (item instanceof Map) { mapUsed = true; return value; } if (item instanceof Set) { setUsed = true; return value; } if (typeof item === "string" && staticBase !== null && item.startsWith(staticBase)) { customFileUsed = true; return `${FILE_TOKEN}${item.replace(staticBase + "/", "")}`; } return value; }, indent); return { serializedString, customDateUsed, customFileUsed, mapUsed, setUsed }; } catch (err) { throw new Error("Could not serialize the passed input props to JSON: " + err.message); } }; var deserializeJSONWithSpecialTypes = (data) => { return JSON.parse(data, (_, value) => { if (typeof value === "string" && value.startsWith(DATE_TOKEN)) { return new Date(value.replace(DATE_TOKEN, "")); } if (typeof value === "string" && value.startsWith(FILE_TOKEN)) { return `${window.remotion_staticBase}/${value.replace(FILE_TOKEN, "")}`; } return value; }); }; var serializeThenDeserialize = (props) => { return deserializeJSONWithSpecialTypes(serializeJSONWithSpecialTypes({ data: props, indent: 2, staticBase: window.remotion_staticBase }).serializedString); }; var serializeThenDeserializeInStudio = (props) => { if (getRemotionEnvironment().isStudio) { return serializeThenDeserialize(props); } return props; }; // src/config/input-props.ts var didWarnSSRImport = false; var warnOnceSSRImport = () => { if (didWarnSSRImport) { return; } didWarnSSRImport = true; console.warn("Called `getInputProps()` on the server. This function is not available server-side and has returned an empty object."); console.warn("To hide this warning, don't call this function on the server:"); console.warn(" typeof window === 'undefined' ? {} : getInputProps()"); }; var getInputProps = () => { if (typeof window === "undefined") { warnOnceSSRImport(); return {}; } if (getRemotionEnvironment().isPlayer) { throw new Error("You cannot call `getInputProps()` from a . Instead, the props are available as React props from component that you passed as `component` prop."); } const override = getInputPropsOverride(); if (override) { return override; } if (typeof window === "undefined" || typeof window.remotion_inputProps === "undefined") { throw new Error("Cannot call `getInputProps()` - window.remotion_inputProps is not set. This API is only available if you are in the Studio, or while you are rendering server-side."); } const param = window.remotion_inputProps; if (!param) { return {}; } const parsed = deserializeJSONWithSpecialTypes(param); return parsed; }; // src/validation/validate-dimensions.ts function validateDimension(amount, nameOfProp, location) { if (typeof amount !== "number") { throw new Error(`The "${nameOfProp}" prop ${location} must be a number, but you passed a value of type ${typeof amount}`); } if (isNaN(amount)) { throw new TypeError(`The "${nameOfProp}" prop ${location} must not be NaN, but is NaN.`); } if (!Number.isFinite(amount)) { throw new TypeError(`The "${nameOfProp}" prop ${location} must be finite, but is ${amount}.`); } if (amount % 1 !== 0) { throw new TypeError(`The "${nameOfProp}" prop ${location} must be an integer, but is ${amount}.`); } if (amount <= 0) { throw new TypeError(`The "${nameOfProp}" prop ${location} must be positive, but got ${amount}.`); } } // src/validation/validate-duration-in-frames.ts function validateDurationInFrames(durationInFrames, options) { const { allowFloats, component } = options; if (typeof durationInFrames === "undefined") { throw new Error(`The "durationInFrames" prop ${component} is missing.`); } if (typeof durationInFrames !== "number") { throw new Error(`The "durationInFrames" prop ${component} must be a number, but you passed a value of type ${typeof durationInFrames}`); } if (durationInFrames <= 0) { throw new TypeError(`The "durationInFrames" prop ${component} must be positive, but got ${durationInFrames}.`); } if (!allowFloats && durationInFrames % 1 !== 0) { throw new TypeError(`The "durationInFrames" prop ${component} must be an integer, but got ${durationInFrames}.`); } if (!Number.isFinite(durationInFrames)) { throw new TypeError(`The "durationInFrames" prop ${component} must be finite, but got ${durationInFrames}.`); } } // src/validation/validate-fps.ts function validateFps(fps, location, isGif) { if (typeof fps !== "number") { throw new Error(`"fps" must be a number, but you passed a value of type ${typeof fps} ${location}`); } if (!Number.isFinite(fps)) { throw new Error(`"fps" must be a finite, but you passed ${fps} ${location}`); } if (isNaN(fps)) { throw new Error(`"fps" must not be NaN, but got ${fps} ${location}`); } if (fps <= 0) { throw new TypeError(`"fps" must be positive, but got ${fps} ${location}`); } if (isGif && fps > 50) { throw new TypeError(`The FPS for a GIF cannot be higher than 50. Use the --every-nth-frame option to lower the FPS: https://remotion.dev/docs/render-as-gif`); } } // src/ResolveCompositionConfig.tsx var ResolveCompositionContext = createContext6(null); var resolveCompositionsRef = createRef(); var needsResolution = (composition) => { return Boolean(composition.calculateMetadata); }; var PROPS_UPDATED_EXTERNALLY = "remotion.propsUpdatedExternally"; var useResolvedVideoConfig = (preferredCompositionId) => { const context = useContext3(ResolveCompositionContext); const { props: allEditorProps } = useContext3(EditorPropsContext); const { compositions, canvasContent, currentCompositionMetadata } = useContext3(CompositionManager); const currentComposition = canvasContent?.type === "composition" ? canvasContent.compositionId : null; const compositionId = preferredCompositionId ?? currentComposition; const composition = compositions.find((c) => c.id === compositionId); const selectedEditorProps = useMemo4(() => { return composition ? allEditorProps[composition.id] ?? {} : {}; }, [allEditorProps, composition]); const env = useRemotionEnvironment(); return useMemo4(() => { if (!composition) { return null; } if (currentCompositionMetadata) { return { type: "success", result: { ...currentCompositionMetadata, id: composition.id, defaultProps: composition.defaultProps ?? {} } }; } if (!needsResolution(composition)) { validateDurationInFrames(composition.durationInFrames, { allowFloats: false, component: `in ` }); validateFps(composition.fps, `in `, false); validateDimension(composition.width, "width", `in `); validateDimension(composition.height, "height", `in `); return { type: "success", result: { width: composition.width, height: composition.height, fps: composition.fps, id: composition.id, durationInFrames: composition.durationInFrames, defaultProps: composition.defaultProps ?? {}, props: { ...composition.defaultProps ?? {}, ...selectedEditorProps ?? {}, ...typeof window === "undefined" || env.isPlayer || !window.remotion_inputProps ? {} : getInputProps() ?? {} }, defaultCodec: null, defaultOutName: null, defaultVideoImageFormat: null, defaultPixelFormat: null, defaultProResProfile: null } }; } if (!context) { return null; } if (!context[composition.id]) { return null; } return context[composition.id]; }, [ composition, context, currentCompositionMetadata, selectedEditorProps, env.isPlayer ]); }; // src/use-video.ts var useVideo = () => { const { canvasContent, compositions, currentCompositionMetadata } = useContext4(CompositionManager); const selected = compositions.find((c) => { return canvasContent?.type === "composition" && c.id === canvasContent.compositionId; }); const resolved = useResolvedVideoConfig(selected?.id ?? null); return useMemo5(() => { if (!resolved) { return null; } if (resolved.type === "error") { return null; } if (resolved.type === "loading") { return null; } if (!selected) { return null; } return { ...resolved.result, defaultProps: selected.defaultProps ?? {}, id: selected.id, ...currentCompositionMetadata ?? {}, component: selected.component }; }, [currentCompositionMetadata, resolved, selected]); }; // src/timeline-position-state.ts var makeKey = () => { return `remotion.time-all`; }; var persistCurrentFrame = (time) => { localStorage.setItem(makeKey(), JSON.stringify(time)); }; var getInitialFrameState = () => { const item = localStorage.getItem(makeKey()) ?? "{}"; const obj = JSON.parse(item); return obj; }; var getFrameForComposition = (composition) => { const item = localStorage.getItem(makeKey()) ?? "{}"; const obj = JSON.parse(item); if (obj[composition] !== undefined) { return Number(obj[composition]); } if (typeof window === "undefined") { return 0; } return window.remotion_initialFrame ?? 0; }; var useTimelinePosition = () => { const videoConfig = useVideo(); const state = useContext5(TimelineContext); const env = useRemotionEnvironment(); if (!videoConfig) { return typeof window === "undefined" ? 0 : window.remotion_initialFrame ?? 0; } const unclamped = state.frame[videoConfig.id] ?? (env.isPlayer ? 0 : getFrameForComposition(videoConfig.id)); return Math.min(videoConfig.durationInFrames - 1, unclamped); }; var useTimelineSetFrame = () => { const { setFrame } = useContext5(SetTimelineContext); return setFrame; }; var usePlayingState = () => { const { playing, imperativePlaying } = useContext5(TimelineContext); const { setPlaying } = useContext5(SetTimelineContext); return useMemo6(() => [playing, setPlaying, imperativePlaying], [imperativePlaying, playing, setPlaying]); }; // src/use-delay-render.tsx import { createContext as createContext8, useCallback as useCallback3, useContext as useContext7 } from "react"; // src/cancel-render.ts var getErrorStackWithMessage = (error) => { const stack = error.stack ?? ""; return stack.startsWith("Error:") ? stack : `${error.message} ${stack}`; }; var isErrorLike = (err) => { if (err instanceof Error) { return true; } if (err === null) { return false; } if (typeof err !== "object") { return false; } if (!("stack" in err)) { return false; } if (typeof err.stack !== "string") { return false; } if (!("message" in err)) { return false; } if (typeof err.message !== "string") { return false; } return true; }; function cancelRenderInternal(scope, err) { let error; if (isErrorLike(err)) { error = err; if (!error.stack) { error.stack = new Error(error.message).stack; } } else if (typeof err === "string") { error = Error(err); } else { error = Error("Rendering was cancelled"); } if (scope) { scope.remotion_cancelledError = getErrorStackWithMessage(error); } throw error; } function cancelRender(err) { return cancelRenderInternal(typeof window !== "undefined" ? window : undefined, err); } // src/log.ts var logLevels = ["trace", "verbose", "info", "warn", "error"]; var getNumberForLogLevel = (level) => { return logLevels.indexOf(level); }; var isEqualOrBelowLogLevel = (currentLevel, level) => { return getNumberForLogLevel(currentLevel) <= getNumberForLogLevel(level); }; var transformArgs = ({ args, logLevel, tag }) => { const arr = [...args]; if (getRemotionEnvironment().isRendering && !getRemotionEnvironment().isClientSideRendering) { arr.unshift(Symbol.for(`__remotion_level_${logLevel}`)); } if (tag && getRemotionEnvironment().isRendering && !getRemotionEnvironment().isClientSideRendering) { arr.unshift(Symbol.for(`__remotion_tag_${tag}`)); } return arr; }; var verbose = (options, ...args) => { if (isEqualOrBelowLogLevel(options.logLevel, "verbose")) { return console.debug(...transformArgs({ args, logLevel: "verbose", tag: options.tag })); } }; var trace = (options, ...args) => { if (isEqualOrBelowLogLevel(options.logLevel, "trace")) { return console.debug(...transformArgs({ args, logLevel: "trace", tag: options.tag })); } }; var info = (options, ...args) => { if (isEqualOrBelowLogLevel(options.logLevel, "info")) { return console.log(...transformArgs({ args, logLevel: "info", tag: options.tag })); } }; var warn = (options, ...args) => { if (isEqualOrBelowLogLevel(options.logLevel, "warn")) { return console.warn(...transformArgs({ args, logLevel: "warn", tag: options.tag })); } }; var error = (options, ...args) => { return console.error(...transformArgs({ args, logLevel: "error", tag: options.tag })); }; var Log = { trace, verbose, info, warn, error }; // src/delay-render.ts if (typeof window !== "undefined") { window.remotion_renderReady = false; if (!window.remotion_delayRenderTimeouts) { window.remotion_delayRenderTimeouts = {}; } window.remotion_delayRenderHandles = []; } var DELAY_RENDER_CALLSTACK_TOKEN = "The delayRender was called:"; var DELAY_RENDER_RETRIES_LEFT = "Retries left: "; var DELAY_RENDER_RETRY_TOKEN = "- Rendering the frame will be retried."; var DELAY_RENDER_CLEAR_TOKEN = "handle was cleared after"; var defaultTimeout = 30000; var delayRenderInternal = ({ scope, environment, label, options }) => { if (typeof label !== "string" && label !== null) { throw new Error("The label parameter of delayRender() must be a string or undefined, got: " + JSON.stringify(label)); } const handle = Math.random(); scope.remotion_delayRenderHandles.push(handle); const called = Error().stack?.replace(/^Error/g, "") ?? ""; if (environment.isRendering) { const timeoutToUse = (options?.timeoutInMilliseconds ?? scope.remotion_puppeteerTimeout ?? defaultTimeout) - 2000; const retriesLeft = (options?.retries ?? 0) - (scope.remotion_attempt - 1); scope.remotion_delayRenderTimeouts[handle] = { label: label ?? null, startTime: Date.now(), timeout: setTimeout(() => { const message = [ `A delayRender()`, label ? `"${label}"` : null, `was called but not cleared after ${timeoutToUse}ms. See https://remotion.dev/docs/timeout for help.`, retriesLeft > 0 ? DELAY_RENDER_RETRIES_LEFT + retriesLeft : null, retriesLeft > 0 ? DELAY_RENDER_RETRY_TOKEN : null, DELAY_RENDER_CALLSTACK_TOKEN, called ].filter(truthy).join(" "); if (environment.isClientSideRendering) { scope.remotion_cancelledError = getErrorStackWithMessage(Error(message)); } else { cancelRenderInternal(scope, Error(message)); } }, timeoutToUse) }; } scope.remotion_renderReady = false; return handle; }; var delayRender = (label, options) => { if (typeof window === "undefined") { return Math.random(); } return delayRenderInternal({ scope: window, environment: getRemotionEnvironment(), label: label ?? null, options: options ?? {} }); }; var continueRenderInternal = ({ scope, handle, environment, logLevel }) => { if (typeof handle === "undefined") { throw new TypeError("The continueRender() method must be called with a parameter that is the return value of delayRender(). No value was passed."); } if (typeof handle !== "number") { throw new TypeError("The parameter passed into continueRender() must be the return value of delayRender() which is a number. Got: " + JSON.stringify(handle)); } scope.remotion_delayRenderHandles = scope.remotion_delayRenderHandles.filter((h) => { if (h === handle) { if (environment.isRendering && scope !== undefined) { if (!scope.remotion_delayRenderTimeouts[handle]) { return false; } const { label, startTime, timeout } = scope.remotion_delayRenderTimeouts[handle]; clearTimeout(timeout); const message = [ label ? `"${label}"` : "A handle", DELAY_RENDER_CLEAR_TOKEN, `${Date.now() - startTime}ms` ].filter(truthy).join(" "); Log.verbose({ logLevel, tag: "delayRender()" }, message); delete scope.remotion_delayRenderTimeouts[handle]; } return false; } return true; }); if (scope.remotion_delayRenderHandles.length === 0) { scope.remotion_renderReady = true; } }; var continueRender = (handle) => { if (typeof window === "undefined") { return; } continueRenderInternal({ scope: window, handle, environment: getRemotionEnvironment(), logLevel: window.remotion_logLevel ?? "info" }); }; // src/log-level-context.tsx import { createContext as createContext7 } from "react"; import * as React6 from "react"; var LogLevelContext = createContext7({ logLevel: "info", mountTime: 0 }); var useLogLevel = () => { const { logLevel } = React6.useContext(LogLevelContext); if (logLevel === null) { throw new Error("useLogLevel must be used within a LogLevelProvider"); } return logLevel; }; var useMountTime = () => { const { mountTime } = React6.useContext(LogLevelContext); if (mountTime === null) { throw new Error("useMountTime must be used within a LogLevelProvider"); } return mountTime; }; // src/use-delay-render.tsx var DelayRenderContextType = createContext8(null); var useDelayRender = () => { const environment = useRemotionEnvironment(); const scope = useContext7(DelayRenderContextType) ?? (typeof window !== "undefined" ? window : undefined); const logLevel = useLogLevel(); const delayRender2 = useCallback3((label, options) => { if (!scope) { return Math.random(); } return delayRenderInternal({ scope, environment, label: label ?? null, options: options ?? {} }); }, [environment, scope]); const continueRender2 = useCallback3((handle) => { if (!scope) { return; } continueRenderInternal({ scope, handle, environment, logLevel }); }, [environment, logLevel, scope]); const cancelRender2 = useCallback3((err) => { return cancelRenderInternal(scope ?? (typeof window !== "undefined" ? window : undefined), err); }, [scope]); return { delayRender: delayRender2, continueRender: continueRender2, cancelRender: cancelRender2 }; }; // src/TimelineContext.tsx import { jsx as jsx5 } from "react/jsx-runtime"; var SetTimelineContext = createContext9({ setFrame: () => { throw new Error("default"); }, setPlaying: () => { throw new Error("default"); } }); var TimelineContext = createContext9({ frame: {}, playing: false, playbackRate: 1, rootId: "", imperativePlaying: { current: false }, setPlaybackRate: () => { throw new Error("default"); }, audioAndVideoTags: { current: [] } }); var TimelineContextProvider = ({ children, frameState }) => { const [playing, setPlaying] = useState3(false); const imperativePlaying = useRef(false); const [playbackRate, setPlaybackRate] = useState3(1); const audioAndVideoTags = useRef([]); const [remotionRootId] = useState3(() => String(random(null))); const [_frame, setFrame] = useState3(() => getInitialFrameState()); const frame = frameState ?? _frame; const { delayRender: delayRender2, continueRender: continueRender2 } = useDelayRender(); if (typeof window !== "undefined") { useLayoutEffect(() => { window.remotion_setFrame = (f, composition, attempt) => { window.remotion_attempt = attempt; const id = delayRender2(`Setting the current frame to ${f}`); let asyncUpdate = true; setFrame((s) => { const currentFrame = s[composition] ?? window.remotion_initialFrame; if (currentFrame === f) { asyncUpdate = false; return s; } return { ...s, [composition]: f }; }); if (asyncUpdate) { requestAnimationFrame(() => continueRender2(id)); } else { continueRender2(id); } }; window.remotion_isPlayer = false; }, [continueRender2, delayRender2]); } const timelineContextValue = useMemo7(() => { return { frame, playing, imperativePlaying, rootId: remotionRootId, playbackRate, setPlaybackRate, audioAndVideoTags }; }, [frame, playbackRate, playing, remotionRootId]); const setTimelineContextValue = useMemo7(() => { return { setFrame, setPlaying }; }, []); return /* @__PURE__ */ jsx5(TimelineContext.Provider, { value: timelineContextValue, children: /* @__PURE__ */ jsx5(SetTimelineContext.Provider, { value: setTimelineContextValue, children }) }); }; // src/nonce.ts import { createContext as createContext10, useContext as useContext8, useEffect, useRef as useRef2, useState as useState4 } from "react"; var NonceContext = createContext10({ getNonce: () => 0 }); var useNonce = () => { const context = useContext8(NonceContext); const [nonce, setNonce] = useState4(() => context.getNonce()); const lastContext = useRef2(context); useEffect(() => { if (lastContext.current === context) { return; } lastContext.current = context; setNonce(context.getNonce); }, [context]); return nonce; }; // src/use-video-config.ts import { useContext as useContext10 } from "react"; // src/CanUseRemotionHooks.tsx import { createContext as createContext11 } from "react"; import { jsx as jsx6 } from "react/jsx-runtime"; var CanUseRemotionHooks = createContext11(false); var CanUseRemotionHooksProvider = ({ children }) => { return /* @__PURE__ */ jsx6(CanUseRemotionHooks.Provider, { value: true, children }); }; // src/use-unsafe-video-config.ts import { useContext as useContext9, useMemo as useMemo8 } from "react"; var useUnsafeVideoConfig = () => { const context = useContext9(SequenceContext); const ctxWidth = context?.width ?? null; const ctxHeight = context?.height ?? null; const ctxDuration = context?.durationInFrames ?? null; const video = useVideo(); return useMemo8(() => { if (!video) { return null; } const { id, durationInFrames, fps, height, width, defaultProps, props, defaultCodec, defaultOutName, defaultVideoImageFormat, defaultPixelFormat, defaultProResProfile } = video; return { id, width: ctxWidth ?? width, height: ctxHeight ?? height, fps, durationInFrames: ctxDuration ?? durationInFrames, defaultProps, props, defaultCodec, defaultOutName, defaultVideoImageFormat, defaultPixelFormat, defaultProResProfile }; }, [ctxDuration, ctxHeight, ctxWidth, video]); }; // src/use-video-config.ts var useVideoConfig = () => { const videoConfig = useUnsafeVideoConfig(); const context = useContext10(CanUseRemotionHooks); const isPlayer = useIsPlayer(); if (!videoConfig) { if (typeof window !== "undefined" && window.remotion_isPlayer || isPlayer) { throw new Error([ "No video config found. Likely reasons:", "- You are probably calling useVideoConfig() from outside the component passed to . See https://www.remotion.dev/docs/player/examples for how to set up the Player correctly.", "- You have multiple versions of Remotion installed which causes the React context to get lost." ].join("-")); } throw new Error("No video config found. You are probably calling useVideoConfig() from a component which has not been registered as a . See https://www.remotion.dev/docs/the-fundamentals#defining-compositions for more information."); } if (!context) { throw new Error("Called useVideoConfig() outside a Remotion composition."); } return videoConfig; }; // src/freeze.tsx import { useContext as useContext12, useMemo as useMemo9 } from "react"; // src/use-current-frame.ts import { useContext as useContext11 } from "react"; var useCurrentFrame = () => { const canUseRemotionHooks = useContext11(CanUseRemotionHooks); const env = useRemotionEnvironment(); if (!canUseRemotionHooks) { if (env.isPlayer) { throw new Error(`useCurrentFrame can only be called inside a component that was passed to . See: https://www.remotion.dev/docs/player/examples`); } throw new Error(`useCurrentFrame() can only be called inside a component that was registered as a composition. See https://www.remotion.dev/docs/the-fundamentals#defining-compositions`); } const frame = useTimelinePosition(); const context = useContext11(SequenceContext); const contextOffset = context ? context.cumulatedFrom + context.relativeFrom : 0; return frame - contextOffset; }; // src/freeze.tsx import { jsx as jsx7 } from "react/jsx-runtime"; var Freeze = ({ frame: frameToFreeze, children, active = true }) => { const frame = useCurrentFrame(); const videoConfig = useVideoConfig(); if (typeof frameToFreeze === "undefined") { throw new Error(`The component requires a 'frame' prop, but none was passed.`); } if (typeof frameToFreeze !== "number") { throw new Error(`The 'frame' prop of must be a number, but is of type ${typeof frameToFreeze}`); } if (Number.isNaN(frameToFreeze)) { throw new Error(`The 'frame' prop of must be a real number, but it is NaN.`); } if (!Number.isFinite(frameToFreeze)) { throw new Error(`The 'frame' prop of must be a finite number, but it is ${frameToFreeze}.`); } const isActive = useMemo9(() => { if (typeof active === "boolean") { return active; } if (typeof active === "function") { return active(frame); } }, [active, frame]); const timelineContext = useContext12(TimelineContext); const sequenceContext = useContext12(SequenceContext); const relativeFrom = sequenceContext?.relativeFrom ?? 0; const timelineValue = useMemo9(() => { if (!isActive) { return timelineContext; } return { ...timelineContext, playing: false, imperativePlaying: { current: false }, frame: { [videoConfig.id]: frameToFreeze + relativeFrom } }; }, [isActive, timelineContext, videoConfig.id, frameToFreeze, relativeFrom]); const newSequenceContext = useMemo9(() => { if (!sequenceContext) { return null; } if (!isActive) { return sequenceContext; } return { ...sequenceContext, cumulatedFrom: 0 }; }, [sequenceContext, isActive]); return /* @__PURE__ */ jsx7(TimelineContext.Provider, { value: timelineValue, children: /* @__PURE__ */ jsx7(SequenceContext.Provider, { value: newSequenceContext, children }) }); }; // src/Sequence.tsx import { jsx as jsx8 } from "react/jsx-runtime"; var RegularSequenceRefForwardingFunction = ({ from = 0, durationInFrames = Infinity, children, name, height, width, showInTimeline = true, _remotionInternalLoopDisplay: loopDisplay, _remotionInternalStack: stack, _remotionInternalPremountDisplay: premountDisplay, _remotionInternalPostmountDisplay: postmountDisplay, ...other }, ref) => { const { layout = "absolute-fill" } = other; const [id] = useState5(() => String(Math.random())); const parentSequence = useContext13(SequenceContext); const { rootId } = useContext13(TimelineContext); const cumulatedFrom = parentSequence ? parentSequence.cumulatedFrom + parentSequence.relativeFrom : 0; const nonce = useNonce(); if (layout !== "absolute-fill" && layout !== "none") { throw new TypeError(`The layout prop of expects either "absolute-fill" or "none", but you passed: ${layout}`); } if (layout === "none" && typeof other.style !== "undefined") { throw new TypeError('If layout="none", you may not pass a style.'); } if (typeof durationInFrames !== "number") { throw new TypeError(`You passed to durationInFrames an argument of type ${typeof durationInFrames}, but it must be a number.`); } if (durationInFrames <= 0) { throw new TypeError(`durationInFrames must be positive, but got ${durationInFrames}`); } if (typeof from !== "number") { throw new TypeError(`You passed to the "from" props of your an argument of type ${typeof from}, but it must be a number.`); } if (!Number.isFinite(from)) { throw new TypeError(`The "from" prop of a sequence must be finite, but got ${from}.`); } const absoluteFrame = useTimelinePosition(); const videoConfig = useVideoConfig(); const parentSequenceDuration = parentSequence ? Math.min(parentSequence.durationInFrames - from, durationInFrames) : durationInFrames; const actualDurationInFrames = Math.max(0, Math.min(videoConfig.durationInFrames - from, parentSequenceDuration)); const { registerSequence, unregisterSequence } = useContext13(SequenceManager); const { hidden } = useContext13(SequenceVisibilityToggleContext); const premounting = useMemo10(() => { return parentSequence?.premounting || Boolean(other._remotionInternalIsPremounting); }, [other._remotionInternalIsPremounting, parentSequence?.premounting]); const postmounting = useMemo10(() => { return parentSequence?.postmounting || Boolean(other._remotionInternalIsPostmounting); }, [other._remotionInternalIsPostmounting, parentSequence?.postmounting]); const contextValue = useMemo10(() => { return { cumulatedFrom, relativeFrom: from, durationInFrames: actualDurationInFrames, parentFrom: parentSequence?.relativeFrom ?? 0, id, height: height ?? parentSequence?.height ?? null, width: width ?? parentSequence?.width ?? null, premounting, postmounting, premountDisplay: premountDisplay ?? null, postmountDisplay: postmountDisplay ?? null }; }, [ cumulatedFrom, from, actualDurationInFrames, parentSequence, id, height, width, premounting, postmounting, premountDisplay, postmountDisplay ]); const timelineClipName = useMemo10(() => { return name ?? ""; }, [name]); const env = useRemotionEnvironment(); useEffect2(() => { if (!env.isStudio) { return; } registerSequence({ from, duration: actualDurationInFrames, id, displayName: timelineClipName, parent: parentSequence?.id ?? null, type: "sequence", rootId, showInTimeline, nonce, loopDisplay, stack: stack ?? null, premountDisplay: premountDisplay ?? null, postmountDisplay: postmountDisplay ?? null }); return () => { unregisterSequence(id); }; }, [ durationInFrames, id, name, registerSequence, timelineClipName, unregisterSequence, parentSequence?.id, actualDurationInFrames, rootId, from, showInTimeline, nonce, loopDisplay, stack, premountDisplay, postmountDisplay, env.isStudio ]); const endThreshold = Math.ceil(cumulatedFrom + from + durationInFrames - 1); const content = absoluteFrame < cumulatedFrom + from ? null : absoluteFrame > endThreshold ? null : children; const styleIfThere = other.layout === "none" ? undefined : other.style; const defaultStyle = useMemo10(() => { return { flexDirection: undefined, ...width ? { width } : {}, ...height ? { height } : {}, ...styleIfThere ?? {} }; }, [height, styleIfThere, width]); if (ref !== null && layout === "none") { throw new TypeError('It is not supported to pass both a `ref` and `layout="none"` to .'); } const isSequenceHidden = hidden[id] ?? false; if (isSequenceHidden) { return null; } return /* @__PURE__ */ jsx8(SequenceContext.Provider, { value: contextValue, children: content === null ? null : other.layout === "none" ? content : /* @__PURE__ */ jsx8(AbsoluteFill, { ref, style: defaultStyle, className: other.className, children: content }) }); }; var RegularSequence = forwardRef2(RegularSequenceRefForwardingFunction); var PremountedPostmountedSequenceRefForwardingFunction = (props, ref) => { const frame = useCurrentFrame(); if (props.layout === "none") { throw new Error('`` with `premountFor` and `postmountFor` props does not support layout="none"'); } const { style: passedStyle, from = 0, durationInFrames = Infinity, premountFor = 0, postmountFor = 0, styleWhilePremounted, styleWhilePostmounted, ...otherProps } = props; const endThreshold = Math.ceil(from + durationInFrames - 1); const premountingActive = frame < from && frame >= from - premountFor; const postmountingActive = frame > endThreshold && frame <= endThreshold + postmountFor; const freezeFrame = premountingActive ? from : postmountingActive ? from + durationInFrames - 1 : 0; const isFreezingActive = premountingActive || postmountingActive; const style = useMemo10(() => { return { ...passedStyle, opacity: premountingActive || postmountingActive ? 0 : 1, pointerEvents: premountingActive || postmountingActive ? "none" : passedStyle?.pointerEvents ?? undefined, ...premountingActive ? styleWhilePremounted : {}, ...postmountingActive ? styleWhilePostmounted : {} }; }, [ passedStyle, premountingActive, postmountingActive, styleWhilePremounted, styleWhilePostmounted ]); return /* @__PURE__ */ jsx8(Freeze, { frame: freezeFrame, active: isFreezingActive, children: /* @__PURE__ */ jsx8(Sequence, { ref, from, durationInFrames, style, _remotionInternalPremountDisplay: premountFor, _remotionInternalPostmountDisplay: postmountFor, _remotionInternalIsPremounting: premountingActive, _remotionInternalIsPostmounting: postmountingActive, ...otherProps }) }); }; var PremountedPostmountedSequence = forwardRef2(PremountedPostmountedSequenceRefForwardingFunction); var SequenceRefForwardingFunction = (props, ref) => { const env = useRemotionEnvironment(); if (props.layout !== "none" && !env.isRendering) { if (props.premountFor || props.postmountFor) { return /* @__PURE__ */ jsx8(PremountedPostmountedSequence, { ...props, ref }); } } return /* @__PURE__ */ jsx8(RegularSequence, { ...props, ref }); }; var Sequence = forwardRef2(SequenceRefForwardingFunction); // src/animated-image/AnimatedImage.tsx import { forwardRef as forwardRef3, useEffect as useEffect3, useImperativeHandle as useImperativeHandle3, useLayoutEffect as useLayoutEffect2, useRef as useRef4, useState as useState6 } from "react"; // src/animated-image/canvas.tsx import React11, { useCallback as useCallback4, useImperativeHandle as useImperativeHandle2, useRef as useRef3 } from "react"; import { jsx as jsx9 } from "react/jsx-runtime"; var calcArgs = (fit, frameSize, canvasSize) => { switch (fit) { case "fill": { return [ 0, 0, frameSize.width, frameSize.height, 0, 0, canvasSize.width, canvasSize.height ]; } case "contain": { const ratio = Math.min(canvasSize.width / frameSize.width, canvasSize.height / frameSize.height); const centerX = (canvasSize.width - frameSize.width * ratio) / 2; const centerY = (canvasSize.height - frameSize.height * ratio) / 2; return [ 0, 0, frameSize.width, frameSize.height, centerX, centerY, frameSize.width * ratio, frameSize.height * ratio ]; } case "cover": { const ratio = Math.max(canvasSize.width / frameSize.width, canvasSize.height / frameSize.height); const centerX = (canvasSize.width - frameSize.width * ratio) / 2; const centerY = (canvasSize.height - frameSize.height * ratio) / 2; return [ 0, 0, frameSize.width, frameSize.height, centerX, centerY, frameSize.width * ratio, frameSize.height * ratio ]; } default: throw new Error("Unknown fit: " + fit); } }; var CanvasRefForwardingFunction = ({ width, height, fit, className, style }, ref) => { const canvasRef = useRef3(null); const draw = useCallback4((imageData) => { const canvas = canvasRef.current; const canvasWidth = width ?? imageData.displayWidth; const canvasHeight = height ?? imageData.displayHeight; if (!canvas) { throw new Error("Canvas ref is not set"); } const ctx = canvasRef.current?.getContext("2d"); if (!ctx) { throw new Error("Could not get 2d context"); } canvas.width = canvasWidth; canvas.height = canvasHeight; ctx.drawImage(imageData, ...calcArgs(fit, { height: imageData.displayHeight, width: imageData.displayWidth }, { width: canvasWidth, height: canvasHeight })); }, [fit, height, width]); useImperativeHandle2(ref, () => { return { draw, getCanvas: () => { if (!canvasRef.current) { throw new Error("Canvas ref is not set"); } return canvasRef.current; }, clear: () => { const ctx = canvasRef.current?.getContext("2d"); if (!ctx) { throw new Error("Could not get 2d context"); } ctx.clearRect(0, 0, canvasRef.current.width, canvasRef.current.height); } }; }, [draw]); return /* @__PURE__ */ jsx9("canvas", { ref: canvasRef, className, style }); }; var Canvas = React11.forwardRef(CanvasRefForwardingFunction); // src/animated-image/decode-image.ts var CACHE_SIZE = 5; var getActualTime = ({ loopBehavior, durationFound, timeInSec }) => { return loopBehavior === "loop" ? durationFound ? timeInSec % durationFound : timeInSec : Math.min(timeInSec, durationFound || Infinity); }; var decodeImage = async ({ resolvedSrc, signal, currentTime, initialLoopBehavior }) => { if (typeof ImageDecoder === "undefined") { throw new Error("Your browser does not support the WebCodecs ImageDecoder API."); } const res = await fetch(resolvedSrc, { signal }); const { body } = res; if (!body) { throw new Error("Got no body"); } const decoder = new ImageDecoder({ data: body, type: res.headers.get("Content-Type") || "image/gif" }); await decoder.completed; const { selectedTrack } = decoder.tracks; if (!selectedTrack) { throw new Error("No selected track"); } const cache = []; let durationFound = null; const getFrameByIndex = async (frameIndex) => { const foundInCache = cache.find((c) => c.frameIndex === frameIndex); if (foundInCache && foundInCache.frame) { return foundInCache; } const frame = await decoder.decode({ frameIndex, completeFramesOnly: true }); if (foundInCache) { foundInCache.frame = frame.image; } else { cache.push({ frame: frame.image, frameIndex, timeInSeconds: frame.image.timestamp / 1e6 }); } return { frame: frame.image, frameIndex, timeInSeconds: frame.image.timestamp / 1e6 }; }; const clearCache = (closeToTimeInSec) => { const itemsInCache = cache.filter((c) => c.frame); const sortByClosestToCurrentTime = itemsInCache.sort((a, b) => { const aDiff = Math.abs(a.timeInSeconds - closeToTimeInSec); const bDiff = Math.abs(b.timeInSeconds - closeToTimeInSec); return aDiff - bDiff; }); for (let i = 0;i < sortByClosestToCurrentTime.length; i++) { if (i < CACHE_SIZE) { continue; } const item = sortByClosestToCurrentTime[i]; item.frame = null; } }; const ensureFrameBeforeAndAfter = async ({ timeInSec, loopBehavior }) => { const actualTimeInSec = getActualTime({ durationFound, loopBehavior, timeInSec }); const framesBefore = cache.filter((c) => c.timeInSeconds <= actualTimeInSec); const biggestIndex = framesBefore.map((c) => c.frameIndex).reduce((a, b) => Math.max(a, b), 0); let i = biggestIndex; while (true) { const f = await getFrameByIndex(i); i++; if (!f.frame) { throw new Error("No frame found"); } if (!f.frame.duration) { break; } if (i === selectedTrack.frameCount && durationFound === null) { const duration = (f.frame.timestamp + f.frame.duration) / 1e6; durationFound = duration; } if (f.timeInSeconds > actualTimeInSec || i === selectedTrack.frameCount) { break; } } if (selectedTrack.frameCount - biggestIndex < 3 && loopBehavior === "loop") { await getFrameByIndex(0); } clearCache(actualTimeInSec); }; await ensureFrameBeforeAndAfter({ timeInSec: currentTime, loopBehavior: initialLoopBehavior }); await ensureFrameBeforeAndAfter({ timeInSec: currentTime, loopBehavior: initialLoopBehavior }); const getFrame = async (timeInSec, loopBehavior) => { if (durationFound !== null && timeInSec > durationFound && loopBehavior === "clear-after-finish") { return null; } const actualTimeInSec = getActualTime({ loopBehavior, durationFound, timeInSec }); await ensureFrameBeforeAndAfter({ timeInSec: actualTimeInSec, loopBehavior }); const itemsInCache = cache.filter((c) => c.frame); const closest = itemsInCache.reduce((a, b) => { const aDiff = Math.abs(a.timeInSeconds - actualTimeInSec); const bDiff = Math.abs(b.timeInSeconds - actualTimeInSec); return aDiff < bDiff ? a : b; }); if (!closest.frame) { throw new Error("No frame found"); } return closest; }; return { getFrame, frameCount: selectedTrack.frameCount }; }; // src/animated-image/resolve-image-source.tsx var resolveAnimatedImageSource = (src) => { if (typeof window === "undefined") { return src; } return new URL(src, window.origin).href; }; // src/animated-image/AnimatedImage.tsx import { jsx as jsx10 } from "react/jsx-runtime"; var AnimatedImage = forwardRef3(({ src, width, height, onError, loopBehavior = "loop", playbackRate = 1, fit = "fill", ...props }, canvasRef) => { const mountState = useRef4({ isMounted: true }); useEffect3(() => { const { current } = mountState; current.isMounted = true; return () => { current.isMounted = false; }; }, []); const resolvedSrc = resolveAnimatedImageSource(src); const [imageDecoder, setImageDecoder] = useState6(null); const { delayRender: delayRender2, continueRender: continueRender2 } = useDelayRender(); const [decodeHandle] = useState6(() => delayRender2(`Rendering with src="${resolvedSrc}"`)); const frame = useCurrentFrame(); const { fps } = useVideoConfig(); const currentTime = frame / playbackRate / fps; const currentTimeRef = useRef4(currentTime); currentTimeRef.current = currentTime; const ref = useRef4(null); useImperativeHandle3(canvasRef, () => { const c = ref.current?.getCanvas(); if (!c) { throw new Error("Canvas ref is not set"); } return c; }, []); const [initialLoopBehavior] = useState6(() => loopBehavior); useEffect3(() => { const controller = new AbortController; decodeImage({ resolvedSrc, signal: controller.signal, currentTime: currentTimeRef.current, initialLoopBehavior }).then((d) => { setImageDecoder(d); continueRender2(decodeHandle); }).catch((err) => { if (err.name === "AbortError") { continueRender2(decodeHandle); return; } if (onError) { onError?.(err); continueRender2(decodeHandle); } else { cancelRender(err); } }); return () => { controller.abort(); }; }, [ resolvedSrc, decodeHandle, onError, initialLoopBehavior, continueRender2 ]); useLayoutEffect2(() => { if (!imageDecoder) { return; } const delay = delayRender2(`Rendering frame at ${currentTime} of `); imageDecoder.getFrame(currentTime, loopBehavior).then((videoFrame) => { if (mountState.current.isMounted) { if (videoFrame === null) { ref.current?.clear(); } else { ref.current?.draw(videoFrame.frame); } } continueRender2(delay); }).catch((err) => { if (onError) { onError(err); continueRender2(delay); } else { cancelRender(err); } }); }, [ currentTime, imageDecoder, loopBehavior, onError, src, continueRender2, delayRender2 ]); return /* @__PURE__ */ jsx10(Canvas, { ref, width, height, fit, ...props }); }); // src/Artifact.tsx import { useContext as useContext14, useLayoutEffect as useLayoutEffect4, useState as useState8 } from "react"; // src/RenderAssetManager.tsx import { createContext as createContext12, useCallback as useCallback5, useImperativeHandle as useImperativeHandle4, useLayoutEffect as useLayoutEffect3, useMemo as useMemo11, useRef as useRef5, useState as useState7 } from "react"; // src/validation/validate-artifact.ts var validateArtifactFilename = (filename) => { if (typeof filename !== "string") { throw new TypeError(`The "filename" must be a string, but you passed a value of type ${typeof filename}`); } if (filename.trim() === "") { throw new Error("The `filename` must not be empty"); } if (!filename.match(/^([0-9a-zA-Z-!_.*'()/:&$@=;+,?]+)/g)) { throw new Error('The `filename` must match "/^([0-9a-zA-Z-!_.*\'()/:&$@=;+,?]+)/g". Use forward slashes only, even on Windows.'); } }; var validateContent = (content) => { if (typeof content !== "string" && !(content instanceof Uint8Array)) { throw new TypeError(`The "content" must be a string or Uint8Array, but you passed a value of type ${typeof content}`); } if (typeof content === "string" && content.trim() === "") { throw new Error("The `content` must not be empty"); } }; var validateRenderAsset = (artifact) => { if (artifact.type !== "artifact") { return; } validateArtifactFilename(artifact.filename); if (artifact.contentType === "thumbnail") { return; } validateContent(artifact.content); }; // src/RenderAssetManager.tsx import { jsx as jsx11 } from "react/jsx-runtime"; var RenderAssetManager = createContext12({ registerRenderAsset: () => { return; }, unregisterRenderAsset: () => { return; }, renderAssets: [] }); var RenderAssetManagerProvider = ({ children, collectAssets }) => { const [renderAssets, setRenderAssets] = useState7([]); const renderAssetsRef = useRef5([]); const registerRenderAsset = useCallback5((renderAsset) => { validateRenderAsset(renderAsset); renderAssetsRef.current = [...renderAssetsRef.current, renderAsset]; setRenderAssets(renderAssetsRef.current); }, []); if (collectAssets) { useImperativeHandle4(collectAssets, () => { return { collectAssets: () => { const assets = renderAssetsRef.current; renderAssetsRef.current = []; setRenderAssets([]); return assets; } }; }, []); } const unregisterRenderAsset = useCallback5((id) => { renderAssetsRef.current = renderAssetsRef.current.filter((a) => a.id !== id); setRenderAssets(renderAssetsRef.current); }, []); useLayoutEffect3(() => { if (typeof window !== "undefined") { window.remotion_collectAssets = () => { const assets = renderAssetsRef.current; renderAssetsRef.current = []; setRenderAssets([]); return assets; }; } }, []); const contextValue = useMemo11(() => { return { registerRenderAsset, unregisterRenderAsset, renderAssets }; }, [renderAssets, registerRenderAsset, unregisterRenderAsset]); return /* @__PURE__ */ jsx11(RenderAssetManager.Provider, { value: contextValue, children }); }; // src/Artifact.tsx var ArtifactThumbnail = Symbol("Thumbnail"); var Artifact = ({ filename, content, downloadBehavior }) => { const { registerRenderAsset, unregisterRenderAsset } = useContext14(RenderAssetManager); const env = useRemotionEnvironment(); const frame = useCurrentFrame(); const [id] = useState8(() => { return String(Math.random()); }); useLayoutEffect4(() => { if (!env.isRendering) { return; } if (content instanceof Uint8Array) { registerRenderAsset({ type: "artifact", id, content: btoa(new TextDecoder("utf8").decode(content)), filename, frame, contentType: "binary", downloadBehavior: downloadBehavior ?? null }); } else if (content === ArtifactThumbnail) { registerRenderAsset({ type: "artifact", id, filename, frame, contentType: "thumbnail", downloadBehavior: downloadBehavior ?? null }); } else { registerRenderAsset({ type: "artifact", id, content, filename, frame, contentType: "text", downloadBehavior: downloadBehavior ?? null }); } return () => { return unregisterRenderAsset(id); }; }, [ content, env.isRendering, filename, frame, id, registerRenderAsset, unregisterRenderAsset, downloadBehavior ]); return null; }; Artifact.Thumbnail = ArtifactThumbnail; // src/audio/Audio.tsx import { forwardRef as forwardRef6, useCallback as useCallback10, useContext as useContext27 } from "react"; // src/absolute-src.ts var getAbsoluteSrc = (relativeSrc) => { if (typeof window === "undefined") { return relativeSrc; } if (relativeSrc.startsWith("http://") || relativeSrc.startsWith("https://") || relativeSrc.startsWith("file://") || relativeSrc.startsWith("blob:") || relativeSrc.startsWith("data:")) { return relativeSrc; } return new URL(relativeSrc, window.origin).href; }; // src/calculate-media-duration.ts var calculateMediaDuration = ({ trimAfter, mediaDurationInFrames, playbackRate, trimBefore }) => { let duration = mediaDurationInFrames; if (typeof trimAfter !== "undefined") { duration = trimAfter; } if (typeof trimBefore !== "undefined") { duration -= trimBefore; } const actualDuration = duration / playbackRate; return Math.floor(actualDuration); }; // src/loop/index.tsx import React12, { createContext as createContext13, useMemo as useMemo12 } from "react"; import { jsx as jsx12 } from "react/jsx-runtime"; var LoopContext = createContext13(null); var useLoop = () => { return React12.useContext(LoopContext); }; var Loop = ({ durationInFrames, times = Infinity, children, name, ...props }) => { const currentFrame = useCurrentFrame(); const { durationInFrames: compDuration } = useVideoConfig(); validateDurationInFrames(durationInFrames, { component: "of the component", allowFloats: true }); if (typeof times !== "number") { throw new TypeError(`You passed to "times" an argument of type ${typeof times}, but it must be a number.`); } if (times !== Infinity && times % 1 !== 0) { throw new TypeError(`The "times" prop of a loop must be an integer, but got ${times}.`); } if (times < 0) { throw new TypeError(`The "times" prop of a loop must be at least 0, but got ${times}`); } const maxTimes = Math.ceil(compDuration / durationInFrames); const actualTimes = Math.min(maxTimes, times); const style = props.layout === "none" ? undefined : props.style; const maxFrame = durationInFrames * (actualTimes - 1); const iteration = Math.floor(currentFrame / durationInFrames); const start = iteration * durationInFrames; const from = Math.min(start, maxFrame); const loopDisplay = useMemo12(() => { return { numberOfTimes: Math.min(compDuration / durationInFrames, times), startOffset: -from, durationInFrames }; }, [compDuration, durationInFrames, from, times]); const loopContext = useMemo12(() => { return { iteration: Math.floor(currentFrame / durationInFrames), durationInFrames }; }, [currentFrame, durationInFrames]); return /* @__PURE__ */ jsx12(LoopContext.Provider, { value: loopContext, children: /* @__PURE__ */ jsx12(Sequence, { durationInFrames, from, name: name ?? "", _remotionInternalLoopDisplay: loopDisplay, layout: props.layout, style, children }) }); }; Loop.useLoop = useLoop; // src/prefetch.ts import { useContext as useContext15 } from "react"; // src/playback-logging.ts var playbackLogging = ({ logLevel, tag, message, mountTime }) => { const tags = [mountTime ? Date.now() - mountTime + "ms " : null, tag].filter(Boolean).join(" "); Log.trace({ logLevel, tag: null }, `[${tags}]`, message); }; // src/prefetch-state.tsx import { createContext as createContext14, useEffect as useEffect4, useState as useState9 } from "react"; import { jsx as jsx13 } from "react/jsx-runtime"; var PreloadContext = createContext14({}); var preloads = {}; var updaters = []; var setPreloads = (updater) => { preloads = updater(preloads); updaters.forEach((u) => u()); }; var PrefetchProvider = ({ children }) => { const [_preloads, _setPreloads] = useState9(() => preloads); useEffect4(() => { const updaterFunction = () => { _setPreloads(preloads); }; updaters.push(updaterFunction); return () => { updaters = updaters.filter((u) => u !== updaterFunction); }; }, []); return /* @__PURE__ */ jsx13(PreloadContext.Provider, { value: _preloads, children }); }; // src/prefetch.ts var removeAndGetHashFragment = (src) => { const hashIndex = src.indexOf("#"); if (hashIndex === -1) { return null; } return hashIndex; }; var getSrcWithoutHash = (src) => { const hashIndex = removeAndGetHashFragment(src); if (hashIndex === null) { return src; } return src.slice(0, hashIndex); }; var usePreload = (src) => { const preloads2 = useContext15(PreloadContext); const hashFragmentIndex = removeAndGetHashFragment(src); const withoutHashFragment = getSrcWithoutHash(src); if (!preloads2[withoutHashFragment]) { return src; } if (hashFragmentIndex !== null) { return preloads2[withoutHashFragment] + src.slice(hashFragmentIndex); } return preloads2[withoutHashFragment]; }; var blobToBase64 = function(blob) { const reader = new FileReader; return new Promise((resolve, reject) => { reader.onload = function() { const dataUrl = reader.result; resolve(dataUrl); }; reader.onerror = (err) => { return reject(err); }; reader.readAsDataURL(blob); }); }; var getBlobFromReader = async ({ reader, contentType, contentLength, onProgress }) => { let receivedLength = 0; const chunks = []; while (true) { const { done, value } = await reader.read(); if (done) { break; } chunks.push(value); receivedLength += value.length; if (onProgress) { onProgress({ loadedBytes: receivedLength, totalBytes: contentLength }); } } const chunksAll = new Uint8Array(receivedLength); let position = 0; for (const chunk of chunks) { chunksAll.set(chunk, position); position += chunk.length; } return new Blob([chunksAll], { type: contentType ?? undefined }); }; var prefetch = (src, options) => { const method = options?.method ?? "blob-url"; const logLevel = options?.logLevel ?? "info"; const srcWithoutHash = getSrcWithoutHash(src); if (getRemotionEnvironment().isRendering) { return { free: () => { return; }, waitUntilDone: () => Promise.resolve(srcWithoutHash) }; } Log.verbose({ logLevel, tag: "prefetch" }, `Starting prefetch ${srcWithoutHash}`); let canceled = false; let objectUrl = null; let resolve = () => { return; }; let reject = () => { return; }; const waitUntilDone = new Promise((res, rej) => { resolve = res; reject = rej; }); const controller = new AbortController; let canBeAborted = true; fetch(srcWithoutHash, { signal: controller.signal, credentials: options?.credentials ?? undefined }).then((res) => { canBeAborted = false; if (canceled) { return null; } if (!res.ok) { throw new Error(`HTTP error, status = ${res.status}`); } const headerContentType = res.headers.get("Content-Type"); const contentType = options?.contentType ?? headerContentType; const hasProperContentType = contentType && (contentType.startsWith("video/") || contentType.startsWith("audio/") || contentType.startsWith("image/")); if (!hasProperContentType) { console.warn(`Called prefetch() on ${srcWithoutHash} which returned a "Content-Type" of ${headerContentType}. Prefetched content should have a proper content type (video/... or audio/...) or a contentType passed the options of prefetch(). Otherwise, prefetching will not work properly in all browsers.`); } if (!res.body) { throw new Error(`HTTP response of ${srcWithoutHash} has no body`); } const reader = res.body.getReader(); return getBlobFromReader({ reader, contentType: options?.contentType ?? headerContentType ?? null, contentLength: res.headers.get("Content-Length") ? parseInt(res.headers.get("Content-Length"), 10) : null, onProgress: options?.onProgress }); }).then((buf) => { if (!buf) { return; } const actualBlob = options?.contentType ? new Blob([buf], { type: options.contentType }) : buf; if (method === "base64") { return blobToBase64(actualBlob); } return URL.createObjectURL(actualBlob); }).then((url) => { if (canceled) { return; } playbackLogging({ logLevel, tag: "prefetch", message: `Finished prefetch ${srcWithoutHash} with method ${method}`, mountTime: null }); objectUrl = url; setPreloads((p) => ({ ...p, [srcWithoutHash]: objectUrl })); resolve(objectUrl); }).catch((err) => { if (err?.message.includes("free() called")) { return; } reject(err); }); return { free: () => { playbackLogging({ logLevel, tag: "prefetch", message: `Freeing ${srcWithoutHash}`, mountTime: null }); if (objectUrl) { if (method === "blob-url") { URL.revokeObjectURL(objectUrl); } setPreloads((p) => { const copy = { ...p }; delete copy[srcWithoutHash]; return copy; }); } else { canceled = true; if (canBeAborted) { try { controller.abort(new Error("free() called")); } catch {} } } }, waitUntilDone: () => { return waitUntilDone; } }; }; // src/validate-media-props.ts var validateMediaProps = (props, component) => { if (typeof props.volume !== "number" && typeof props.volume !== "function" && typeof props.volume !== "undefined") { throw new TypeError(`You have passed a volume of type ${typeof props.volume} to your <${component} /> component. Volume must be a number or a function with the signature '(frame: number) => number' undefined.`); } if (typeof props.volume === "number" && props.volume < 0) { throw new TypeError(`You have passed a volume below 0 to your <${component} /> component. Volume must be between 0 and 1`); } if (typeof props.playbackRate !== "number" && typeof props.playbackRate !== "undefined") { throw new TypeError(`You have passed a playbackRate of type ${typeof props.playbackRate} to your <${component} /> component. Playback rate must a real number or undefined.`); } if (typeof props.playbackRate === "number" && (isNaN(props.playbackRate) || !Number.isFinite(props.playbackRate) || props.playbackRate <= 0)) { throw new TypeError(`You have passed a playbackRate of ${props.playbackRate} to your <${component} /> component. Playback rate must be a real number above 0.`); } }; // src/validate-start-from-props.ts var validateStartFromProps = (startFrom, endAt) => { if (typeof startFrom !== "undefined") { if (typeof startFrom !== "number") { throw new TypeError(`type of startFrom prop must be a number, instead got type ${typeof startFrom}.`); } if (isNaN(startFrom) || startFrom === Infinity) { throw new TypeError("startFrom prop can not be NaN or Infinity."); } if (startFrom < 0) { throw new TypeError(`startFrom must be greater than equal to 0 instead got ${startFrom}.`); } } if (typeof endAt !== "undefined") { if (typeof endAt !== "number") { throw new TypeError(`type of endAt prop must be a number, instead got type ${typeof endAt}.`); } if (isNaN(endAt)) { throw new TypeError("endAt prop can not be NaN."); } if (endAt <= 0) { throw new TypeError(`endAt must be a positive number, instead got ${endAt}.`); } } if (endAt < startFrom) { throw new TypeError("endAt prop must be greater than startFrom prop."); } }; var validateTrimProps = (trimBefore, trimAfter) => { if (typeof trimBefore !== "undefined") { if (typeof trimBefore !== "number") { throw new TypeError(`type of trimBefore prop must be a number, instead got type ${typeof trimBefore}.`); } if (isNaN(trimBefore) || trimBefore === Infinity) { throw new TypeError("trimBefore prop can not be NaN or Infinity."); } if (trimBefore < 0) { throw new TypeError(`trimBefore must be greater than equal to 0 instead got ${trimBefore}.`); } } if (typeof trimAfter !== "undefined") { if (typeof trimAfter !== "number") { throw new TypeError(`type of trimAfter prop must be a number, instead got type ${typeof trimAfter}.`); } if (isNaN(trimAfter)) { throw new TypeError("trimAfter prop can not be NaN."); } if (trimAfter <= 0) { throw new TypeError(`trimAfter must be a positive number, instead got ${trimAfter}.`); } } if (trimAfter <= trimBefore) { throw new TypeError("trimAfter prop must be greater than trimBefore prop."); } }; var validateMediaTrimProps = ({ startFrom, endAt, trimBefore, trimAfter }) => { if (typeof startFrom !== "undefined" && typeof trimBefore !== "undefined") { throw new TypeError("Cannot use both startFrom and trimBefore props. Use trimBefore instead as startFrom is deprecated."); } if (typeof endAt !== "undefined" && typeof trimAfter !== "undefined") { throw new TypeError("Cannot use both endAt and trimAfter props. Use trimAfter instead as endAt is deprecated."); } const hasNewProps = typeof trimBefore !== "undefined" || typeof trimAfter !== "undefined"; const hasOldProps = typeof startFrom !== "undefined" || typeof endAt !== "undefined"; if (hasNewProps) { validateTrimProps(trimBefore, trimAfter); } else if (hasOldProps) { validateStartFromProps(startFrom, endAt); } }; var resolveTrimProps = ({ startFrom, endAt, trimBefore, trimAfter }) => { const trimBeforeValue = trimBefore ?? startFrom ?? undefined; const trimAfterValue = trimAfter ?? endAt ?? undefined; return { trimBeforeValue, trimAfterValue }; }; // src/video/duration-state.tsx import { createContext as createContext15, useMemo as useMemo13, useReducer } from "react"; import { jsx as jsx14 } from "react/jsx-runtime"; var durationReducer = (state, action) => { switch (action.type) { case "got-duration": { const absoluteSrc = getAbsoluteSrc(action.src); if (state[absoluteSrc] === action.durationInSeconds) { return state; } return { ...state, [absoluteSrc]: action.durationInSeconds }; } default: return state; } }; var DurationsContext = createContext15({ durations: {}, setDurations: () => { throw new Error("context missing"); } }); var DurationsContextProvider = ({ children }) => { const [durations, setDurations] = useReducer(durationReducer, {}); const value = useMemo13(() => { return { durations, setDurations }; }, [durations]); return /* @__PURE__ */ jsx14(DurationsContext.Provider, { value, children }); }; // src/audio/AudioForPreview.tsx import React18, { forwardRef as forwardRef4, useContext as useContext25, useEffect as useEffect11, useImperativeHandle as useImperativeHandle5, useMemo as useMemo21, useRef as useRef13, useState as useState14 } from "react"; // src/get-cross-origin-value.ts var getCrossOriginValue = ({ crossOrigin, requestsVideoFrame, isClientSideRendering }) => { if (crossOrigin !== undefined && crossOrigin !== null) { return crossOrigin; } if (isClientSideRendering) { return "anonymous"; } if (requestsVideoFrame) { return "anonymous"; } return; }; // src/use-amplification.ts import { useContext as useContext17, useLayoutEffect as useLayoutEffect5, useRef as useRef8 } from "react"; // src/audio/shared-audio-tags.tsx import React15, { createContext as createContext16, createRef as createRef2, useCallback as useCallback6, useContext as useContext16, useMemo as useMemo15, useRef as useRef6, useState as useState10 } from "react"; // src/play-and-handle-not-allowed-error.ts var playAndHandleNotAllowedError = ({ mediaRef, mediaType, onAutoPlayError, logLevel, mountTime, reason, isPlayer }) => { const { current } = mediaRef; if (!current) { return; } playbackLogging({ logLevel, tag: "play", message: `Attempting to play ${current.src}. Reason: ${reason}`, mountTime }); const prom = current.play(); if (!prom.catch) { return; } prom.catch((err) => { if (!current) { return; } if (err.message.includes("request was interrupted by a call to pause")) { return; } if (err.message.includes("The operation was aborted.")) { return; } if (err.message.includes("The fetching process for the media resource was aborted by the user agent")) { return; } if (err.message.includes("request was interrupted by a new load request")) { return; } if (err.message.includes("because the media was removed from the document")) { return; } if (err.message.includes("user didn't interact with the document") && current.muted) { return; } console.log(`Could not play ${mediaType} due to following error: `, err); if (!current.muted) { if (onAutoPlayError) { onAutoPlayError(); return; } if (mediaType === "video" && isPlayer) { Log.info({ logLevel, tag: "<" + mediaType + ">" }, `The video will be muted and we'll retry playing it.`); Log.info({ logLevel, tag: "<" + mediaType + ">" }, "Use onAutoPlayError() to handle this error yourself."); current.muted = true; current.play(); } } }); }; // src/audio/shared-element-source-node.ts var makeSharedElementSourceNode = ({ audioContext, ref }) => { let connected = null; let disposed = false; return { attemptToConnect: () => { if (disposed) { throw new Error("SharedElementSourceNode has been disposed"); } if (!connected && ref.current) { const mediaElementSourceNode = audioContext.createMediaElementSource(ref.current); connected = mediaElementSourceNode; } }, get: () => { if (!connected) { throw new Error("Audio element not connected"); } return connected; }, cleanup: () => { if (connected) { connected.disconnect(); connected = null; } disposed = true; } }; }; // src/audio/use-audio-context.ts import { useMemo as useMemo14 } from "react"; var warned = false; var warnOnce = (logLevel) => { if (warned) { return; } warned = true; if (typeof window !== "undefined") { Log.warn({ logLevel, tag: null }, "AudioContext is not supported in this browser"); } }; var useSingletonAudioContext = ({ logLevel, latencyHint, audioEnabled }) => { const env = useRemotionEnvironment(); const audioContext = useMemo14(() => { if (env.isRendering) { return null; } if (!audioEnabled) { return null; } if (typeof AudioContext === "undefined") { warnOnce(logLevel); return null; } return new AudioContext({ latencyHint, sampleRate: 48000 }); }, [logLevel, latencyHint, env.isRendering, audioEnabled]); return audioContext; }; // src/audio/shared-audio-tags.tsx import { jsx as jsx15, jsxs } from "react/jsx-runtime"; var EMPTY_AUDIO = "data:audio/mp3;base64,/+MYxAAJcAV8AAgAABn//////+/gQ5BAMA+D4Pg+BAQBAEAwD4Pg+D4EBAEAQDAPg++hYBH///hUFQVBUFREDQNHmf///////+MYxBUGkAGIMAAAAP/29Xt6lUxBTUUzLjEwMFVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV/+MYxDUAAANIAAAAAFVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV"; var compareProps = (obj1, obj2) => { const keysA = Object.keys(obj1).sort(); const keysB = Object.keys(obj2).sort(); if (keysA.length !== keysB.length) { return false; } for (let i = 0;i < keysA.length; i++) { if (keysA[i] !== keysB[i]) { return false; } if (obj1[keysA[i]] !== obj2[keysB[i]]) { return false; } } return true; }; var didPropChange = (key, newProp, prevProp) => { if (key === "src" && !prevProp.startsWith("data:") && !newProp.startsWith("data:")) { return new URL(prevProp, window.origin).toString() !== new URL(newProp, window.origin).toString(); } if (prevProp === newProp) { return false; } return true; }; var SharedAudioContext = createContext16(null); var SharedAudioContextProvider = ({ children, numberOfAudioTags, audioLatencyHint, audioEnabled }) => { const audios = useRef6([]); const [initialNumberOfAudioTags] = useState10(numberOfAudioTags); if (numberOfAudioTags !== initialNumberOfAudioTags) { throw new Error("The number of shared audio tags has changed dynamically. Once you have set this property, you cannot change it afterwards."); } const logLevel = useLogLevel(); const audioContext = useSingletonAudioContext({ logLevel, latencyHint: audioLatencyHint, audioEnabled }); const refs = useMemo15(() => { return new Array(numberOfAudioTags).fill(true).map(() => { const ref = createRef2(); return { id: Math.random(), ref, mediaElementSourceNode: audioContext ? makeSharedElementSourceNode({ audioContext, ref }) : null }; }); }, [audioContext, numberOfAudioTags]); const effectToUse = React15.useInsertionEffect ?? React15.useLayoutEffect; effectToUse(() => { return () => { requestAnimationFrame(() => { refs.forEach(({ mediaElementSourceNode }) => { mediaElementSourceNode?.cleanup(); }); }); }; }, [refs]); const takenAudios = useRef6(new Array(numberOfAudioTags).fill(false)); const rerenderAudios = useCallback6(() => { refs.forEach(({ ref, id }) => { const data = audios.current?.find((a) => a.id === id); const { current } = ref; if (!current) { return; } if (data === undefined) { current.src = EMPTY_AUDIO; return; } if (!data) { throw new TypeError("Expected audio data to be there"); } Object.keys(data.props).forEach((key) => { if (didPropChange(key, data.props[key], current[key])) { current[key] = data.props[key]; } }); }); }, [refs]); const registerAudio = useCallback6((options) => { const { aud, audioId, premounting, postmounting } = options; const found = audios.current?.find((a) => a.audioId === audioId); if (found) { return found; } const firstFreeAudio = takenAudios.current.findIndex((a) => a === false); if (firstFreeAudio === -1) { throw new Error(`Tried to simultaneously mount ${numberOfAudioTags + 1} tags at the same time. With the current settings, the maximum amount of tags is limited to ${numberOfAudioTags} at the same time. Remotion pre-mounts silent audio tags to help avoid browser autoplay restrictions. See https://remotion.dev/docs/player/autoplay#using-the-numberofsharedaudiotags-prop for more information on how to increase this limit.`); } const { id, ref, mediaElementSourceNode } = refs[firstFreeAudio]; const cloned = [...takenAudios.current]; cloned[firstFreeAudio] = id; takenAudios.current = cloned; const newElem = { props: aud, id, el: ref, audioId, mediaElementSourceNode, premounting, audioMounted: Boolean(ref.current), postmounting, cleanupOnMediaTagUnmount: () => {} }; audios.current?.push(newElem); rerenderAudios(); return newElem; }, [numberOfAudioTags, refs, rerenderAudios]); const unregisterAudio = useCallback6((id) => { const cloned = [...takenAudios.current]; const index = refs.findIndex((r) => r.id === id); if (index === -1) { throw new TypeError("Error occured in "); } cloned[index] = false; takenAudios.current = cloned; audios.current = audios.current?.filter((a) => a.id !== id); rerenderAudios(); }, [refs, rerenderAudios]); const updateAudio = useCallback6(({ aud, audioId, id, premounting, postmounting }) => { let changed = false; audios.current = audios.current?.map((prevA) => { const audioMounted = Boolean(prevA.el.current); if (prevA.audioMounted !== audioMounted) { changed = true; } if (prevA.id === id) { const isTheSame = compareProps(aud, prevA.props) && prevA.premounting === premounting && prevA.postmounting === postmounting; if (isTheSame) { return prevA; } changed = true; return { ...prevA, props: aud, premounting, postmounting, audioId, audioMounted }; } return prevA; }); if (changed) { rerenderAudios(); } }, [rerenderAudios]); const mountTime = useMountTime(); const env = useRemotionEnvironment(); const playAllAudios = useCallback6(() => { refs.forEach((ref) => { const audio = audios.current.find((a) => a.el === ref.ref); if (audio?.premounting) { return; } playAndHandleNotAllowedError({ mediaRef: ref.ref, mediaType: "audio", onAutoPlayError: null, logLevel, mountTime, reason: "playing all audios", isPlayer: env.isPlayer }); }); audioContext?.resume(); }, [audioContext, logLevel, mountTime, refs, env.isPlayer]); const value = useMemo15(() => { return { registerAudio, unregisterAudio, updateAudio, playAllAudios, numberOfAudioTags, audioContext }; }, [ numberOfAudioTags, playAllAudios, registerAudio, unregisterAudio, updateAudio, audioContext ]); return /* @__PURE__ */ jsxs(SharedAudioContext.Provider, { value, children: [ refs.map(({ id, ref }) => { return /* @__PURE__ */ jsx15("audio", { ref, preload: "metadata", src: EMPTY_AUDIO }, id); }), children ] }); }; var useSharedAudio = ({ aud, audioId, premounting, postmounting }) => { const ctx = useContext16(SharedAudioContext); const [elem] = useState10(() => { if (ctx && ctx.numberOfAudioTags > 0) { return ctx.registerAudio({ aud, audioId, premounting, postmounting }); } const el = React15.createRef(); const mediaElementSourceNode = ctx?.audioContext ? makeSharedElementSourceNode({ audioContext: ctx.audioContext, ref: el }) : null; return { el, id: Math.random(), props: aud, audioId, mediaElementSourceNode, premounting, audioMounted: Boolean(el.current), postmounting, cleanupOnMediaTagUnmount: () => { mediaElementSourceNode?.cleanup(); } }; }); const effectToUse = React15.useInsertionEffect ?? React15.useLayoutEffect; if (typeof document !== "undefined") { effectToUse(() => { if (ctx && ctx.numberOfAudioTags > 0) { ctx.updateAudio({ id: elem.id, aud, audioId, premounting, postmounting }); } }, [aud, ctx, elem.id, audioId, premounting, postmounting]); effectToUse(() => { return () => { if (ctx && ctx.numberOfAudioTags > 0) { ctx.unregisterAudio(elem.id); } }; }, [ctx, elem.id]); } return elem; }; // src/is-approximately-the-same.ts var FLOATING_POINT_ERROR_THRESHOLD = 0.00001; var isApproximatelyTheSame = (num1, num2) => { return Math.abs(num1 - num2) < FLOATING_POINT_ERROR_THRESHOLD; }; // src/video/video-fragment.ts import { useRef as useRef7 } from "react"; var toSeconds = (time, fps) => { return Math.round(time / fps * 100) / 100; }; var isSafari = () => { if (typeof window === "undefined") { return false; } const isAppleWebKit = /AppleWebKit/.test(window.navigator.userAgent); if (!isAppleWebKit) { return false; } const isNotChrome = !window.navigator.userAgent.includes("Chrome/"); return isNotChrome; }; var isIosSafari = () => { if (typeof window === "undefined") { return false; } const isIpadIPodIPhone = /iP(ad|od|hone)/i.test(window.navigator.userAgent); return isIpadIPodIPhone && isSafari(); }; var isIOSSafariAndBlob = (actualSrc) => { return isIosSafari() && actualSrc.startsWith("blob:"); }; var getVideoFragmentStart = ({ actualFrom, fps }) => { return toSeconds(Math.max(0, -actualFrom), fps); }; var getVideoFragmentEnd = ({ duration, fps }) => { return toSeconds(duration, fps); }; var appendVideoFragment = ({ actualSrc, actualFrom, duration, fps }) => { if (isIOSSafariAndBlob(actualSrc)) { return actualSrc; } if (actualSrc.startsWith("data:")) { return actualSrc; } const existingHash = Boolean(new URL(actualSrc, (typeof window === "undefined" ? null : window.location.href) ?? "http://localhost:3000").hash); if (existingHash) { return actualSrc; } if (!Number.isFinite(actualFrom)) { return actualSrc; } const withStartHash = `${actualSrc}#t=${getVideoFragmentStart({ actualFrom, fps })}`; if (!Number.isFinite(duration)) { return withStartHash; } return `${withStartHash},${getVideoFragmentEnd({ duration, fps })}`; }; var isSubsetOfDuration = ({ prevStartFrom, newStartFrom, prevDuration, newDuration, fps }) => { const previousFrom = getVideoFragmentStart({ actualFrom: prevStartFrom, fps }); const newFrom = getVideoFragmentStart({ actualFrom: newStartFrom, fps }); const previousEnd = getVideoFragmentEnd({ duration: prevDuration, fps }); const newEnd = getVideoFragmentEnd({ duration: newDuration, fps }); if (newFrom < previousFrom) { return false; } if (newEnd > previousEnd) { return false; } return true; }; var useAppendVideoFragment = ({ actualSrc: initialActualSrc, actualFrom: initialActualFrom, duration: initialDuration, fps }) => { const actualFromRef = useRef7(initialActualFrom); const actualDuration = useRef7(initialDuration); const actualSrc = useRef7(initialActualSrc); if (!isSubsetOfDuration({ prevStartFrom: actualFromRef.current, newStartFrom: initialActualFrom, prevDuration: actualDuration.current, newDuration: initialDuration, fps }) || initialActualSrc !== actualSrc.current) { actualFromRef.current = initialActualFrom; actualDuration.current = initialDuration; actualSrc.current = initialActualSrc; } const appended = appendVideoFragment({ actualSrc: actualSrc.current, actualFrom: actualFromRef.current, duration: actualDuration.current, fps }); return appended; }; // src/use-amplification.ts var warned2 = false; var warnSafariOnce = (logLevel) => { if (warned2) { return; } warned2 = true; Log.warn({ logLevel, tag: null }, "In Safari, setting a volume and a playback rate at the same time is buggy."); Log.warn({ logLevel, tag: null }, "In Desktop Safari, only volumes <= 1 will be applied."); Log.warn({ logLevel, tag: null }, logLevel, "In Mobile Safari, the volume will be ignored and set to 1 if a playbackRate is set."); }; var useVolume = ({ mediaRef, volume, logLevel, source, shouldUseWebAudioApi }) => { const audioStuffRef = useRef8(null); const currentVolumeRef = useRef8(volume); currentVolumeRef.current = volume; const sharedAudioContext = useContext17(SharedAudioContext); if (!sharedAudioContext) { throw new Error("useAmplification must be used within a SharedAudioContext"); } const { audioContext } = sharedAudioContext; if (typeof window !== "undefined") { useLayoutEffect5(() => { if (!audioContext) { return; } if (!mediaRef.current) { return; } if (!shouldUseWebAudioApi) { return; } if (mediaRef.current.playbackRate !== 1 && isSafari()) { warnSafariOnce(logLevel); return; } if (!source) { return; } const gainNode = new GainNode(audioContext, { gain: currentVolumeRef.current }); source.attemptToConnect(); source.get().connect(gainNode); gainNode.connect(audioContext.destination); audioStuffRef.current = { gainNode }; Log.trace({ logLevel, tag: null }, `Starting to amplify ${mediaRef.current?.src}. Gain = ${currentVolumeRef.current}, playbackRate = ${mediaRef.current?.playbackRate}`); return () => { audioStuffRef.current = null; gainNode.disconnect(); source.get().disconnect(); }; }, [logLevel, mediaRef, audioContext, source, shouldUseWebAudioApi]); } if (audioStuffRef.current) { const valueToSet = volume; if (!isApproximatelyTheSame(audioStuffRef.current.gainNode.gain.value, valueToSet)) { audioStuffRef.current.gainNode.gain.value = valueToSet; Log.trace({ logLevel, tag: null }, `Setting gain to ${valueToSet} for ${mediaRef.current?.src}`); } } const safariCase = isSafari() && mediaRef.current && mediaRef.current?.playbackRate !== 1; const shouldUseTraditionalVolume = safariCase || !shouldUseWebAudioApi; if (shouldUseTraditionalVolume && mediaRef.current && !isApproximatelyTheSame(volume, mediaRef.current?.volume)) { mediaRef.current.volume = Math.min(volume, 1); } return audioStuffRef; }; // src/use-media-in-timeline.ts import { useContext as useContext19, useEffect as useEffect5, useMemo as useMemo16, useState as useState11 } from "react"; // src/audio/use-audio-frame.ts import { useContext as useContext18 } from "react"; var useMediaStartsAt = () => { const parentSequence = useContext18(SequenceContext); const startsAt = Math.min(0, parentSequence?.relativeFrom ?? 0); return startsAt; }; var useFrameForVolumeProp = (behavior) => { const loop = Loop.useLoop(); const frame = useCurrentFrame(); const startsAt = useMediaStartsAt(); if (behavior === "repeat" || loop === null) { return frame + startsAt; } return frame + startsAt + loop.durationInFrames * loop.iteration; }; // src/get-asset-file-name.ts var getAssetDisplayName = (filename) => { if (/data:|blob:/.test(filename.substring(0, 5))) { return "Data URL"; } const splitted = filename.split("/").map((s) => s.split("\\")).flat(1); return splitted[splitted.length - 1]; }; // src/volume-prop.ts var evaluateVolume = ({ frame, volume, mediaVolume = 1 }) => { if (typeof volume === "number") { return volume * mediaVolume; } if (typeof volume === "undefined") { return Number(mediaVolume); } const evaluated = volume(frame) * mediaVolume; if (typeof evaluated !== "number") { throw new TypeError(`You passed in a a function to the volume prop but it did not return a number but a value of type ${typeof evaluated} for frame ${frame}`); } if (Number.isNaN(evaluated)) { throw new TypeError(`You passed in a function to the volume prop but it returned NaN for frame ${frame}.`); } if (!Number.isFinite(evaluated)) { throw new TypeError(`You passed in a function to the volume prop but it returned a non-finite number for frame ${frame}.`); } return Math.max(0, evaluated); }; // src/use-media-in-timeline.ts var didWarn = {}; var warnOnce2 = (message) => { if (didWarn[message]) { return; } console.warn(message); didWarn[message] = true; }; var useBasicMediaInTimeline = ({ volume, mediaVolume, mediaType, src, displayName, trimBefore, trimAfter, playbackRate }) => { if (!src) { throw new Error("No src passed"); } const startsAt = useMediaStartsAt(); const parentSequence = useContext19(SequenceContext); const videoConfig = useVideoConfig(); const [initialVolume] = useState11(() => volume); const mediaDuration = calculateMediaDuration({ mediaDurationInFrames: videoConfig.durationInFrames, playbackRate, trimBefore, trimAfter }); const duration = parentSequence ? Math.min(parentSequence.durationInFrames, mediaDuration) : mediaDuration; const volumes = useMemo16(() => { if (typeof volume === "number") { return volume; } return new Array(Math.floor(Math.max(0, duration + startsAt))).fill(true).map((_, i) => { return evaluateVolume({ frame: i + startsAt, volume, mediaVolume }); }).join(","); }, [duration, startsAt, volume, mediaVolume]); useEffect5(() => { if (typeof volume === "number" && volume !== initialVolume) { warnOnce2(`Remotion: The ${mediaType} with src ${src} has changed it's volume. Prefer the callback syntax for setting volume to get better timeline display: https://www.remotion.dev/docs/audio/volume`); } }, [initialVolume, mediaType, src, volume]); const doesVolumeChange = typeof volume === "function"; const nonce = useNonce(); const { rootId } = useContext19(TimelineContext); const env = useRemotionEnvironment(); return { volumes, duration, doesVolumeChange, nonce, rootId, isStudio: env.isStudio, finalDisplayName: displayName ?? getAssetDisplayName(src) }; }; var useMediaInTimeline = ({ volume, mediaVolume, src, mediaType, playbackRate, displayName, id, stack, showInTimeline, premountDisplay, postmountDisplay, loopDisplay }) => { const parentSequence = useContext19(SequenceContext); const startsAt = useMediaStartsAt(); const { registerSequence, unregisterSequence } = useContext19(SequenceManager); const { volumes, duration, doesVolumeChange, nonce, rootId, isStudio, finalDisplayName } = useBasicMediaInTimeline({ volume, mediaVolume, mediaType, src, displayName, trimAfter: undefined, trimBefore: undefined, playbackRate }); useEffect5(() => { if (!src) { throw new Error("No src passed"); } if (!isStudio && window.process?.env?.NODE_ENV !== "test") { return; } if (!showInTimeline) { return; } registerSequence({ type: mediaType, src, id, duration, from: 0, parent: parentSequence?.id ?? null, displayName: finalDisplayName, rootId, volume: volumes, showInTimeline: true, nonce, startMediaFrom: 0 - startsAt, doesVolumeChange, loopDisplay, playbackRate, stack, premountDisplay, postmountDisplay }); return () => { unregisterSequence(id); }; }, [ duration, id, parentSequence, src, registerSequence, unregisterSequence, volumes, doesVolumeChange, nonce, mediaType, startsAt, playbackRate, stack, showInTimeline, premountDisplay, postmountDisplay, isStudio, loopDisplay, rootId, finalDisplayName ]); }; // src/use-media-playback.ts import { useCallback as useCallback9, useContext as useContext22, useEffect as useEffect9, useLayoutEffect as useLayoutEffect7, useRef as useRef12 } from "react"; // src/buffer-until-first-frame.ts import { useCallback as useCallback8, useMemo as useMemo19, useRef as useRef10 } from "react"; // src/use-buffer-state.ts import { useContext as useContext21, useMemo as useMemo18 } from "react"; // src/buffering.tsx import React16, { useCallback as useCallback7, useContext as useContext20, useEffect as useEffect6, useLayoutEffect as useLayoutEffect6, useMemo as useMemo17, useRef as useRef9, useState as useState12 } from "react"; import { jsx as jsx16 } from "react/jsx-runtime"; var useBufferManager = (logLevel, mountTime) => { const [blocks, setBlocks] = useState12([]); const [onBufferingCallbacks, setOnBufferingCallbacks] = useState12([]); const [onResumeCallbacks, setOnResumeCallbacks] = useState12([]); const env = useRemotionEnvironment(); const rendering = env.isRendering; const buffering = useRef9(false); const addBlock = useCallback7((block) => { if (rendering) { return { unblock: () => { return; } }; } setBlocks((b) => [...b, block]); return { unblock: () => { setBlocks((b) => { const newArr = b.filter((bx) => bx !== block); if (newArr.length === b.length) { return b; } return newArr; }); } }; }, [rendering]); const listenForBuffering = useCallback7((callback) => { setOnBufferingCallbacks((c) => [...c, callback]); return { remove: () => { setOnBufferingCallbacks((c) => c.filter((cb) => cb !== callback)); } }; }, []); const listenForResume = useCallback7((callback) => { setOnResumeCallbacks((c) => [...c, callback]); return { remove: () => { setOnResumeCallbacks((c) => c.filter((cb) => cb !== callback)); } }; }, []); useEffect6(() => { if (rendering) { return; } if (blocks.length > 0) { onBufferingCallbacks.forEach((c) => c()); playbackLogging({ logLevel, message: "Player is entering buffer state", mountTime, tag: "player" }); } }, [blocks]); if (typeof window !== "undefined") { useLayoutEffect6(() => { if (rendering) { return; } if (blocks.length === 0) { onResumeCallbacks.forEach((c) => c()); playbackLogging({ logLevel, message: "Player is exiting buffer state", mountTime, tag: "player" }); } }, [blocks]); } return useMemo17(() => { return { addBlock, listenForBuffering, listenForResume, buffering }; }, [addBlock, buffering, listenForBuffering, listenForResume]); }; var BufferingContextReact = React16.createContext(null); var BufferingProvider = ({ children }) => { const { logLevel, mountTime } = useContext20(LogLevelContext); const bufferManager = useBufferManager(logLevel ?? "info", mountTime); return /* @__PURE__ */ jsx16(BufferingContextReact.Provider, { value: bufferManager, children }); }; var useIsPlayerBuffering = (bufferManager) => { const [isBuffering, setIsBuffering] = useState12(bufferManager.buffering.current); useEffect6(() => { const onBuffer = () => { setIsBuffering(true); }; const onResume = () => { setIsBuffering(false); }; bufferManager.listenForBuffering(onBuffer); bufferManager.listenForResume(onResume); return () => { bufferManager.listenForBuffering(() => { return; }); bufferManager.listenForResume(() => { return; }); }; }, [bufferManager]); return isBuffering; }; // src/use-buffer-state.ts var useBufferState = () => { const buffer = useContext21(BufferingContextReact); const addBlock = buffer ? buffer.addBlock : null; return useMemo18(() => ({ delayPlayback: () => { if (!addBlock) { throw new Error("Tried to enable the buffering state, but a Remotion context was not found. This API can only be called in a component that was passed to the Remotion Player or a . Or you might have experienced a version mismatch - run `npx remotion versions` and ensure all packages have the same version. This error is thrown by the buffer state https://remotion.dev/docs/player/buffer-state"); } const { unblock } = addBlock({ id: String(Math.random()) }); return { unblock }; } }), [addBlock]); }; // src/buffer-until-first-frame.ts var isSafariWebkit = () => { const isSafari2 = /^((?!chrome|android).)*safari/i.test(window.navigator.userAgent); return isSafari2; }; var useBufferUntilFirstFrame = ({ mediaRef, mediaType, onVariableFpsVideoDetected, pauseWhenBuffering, logLevel, mountTime }) => { const bufferingRef = useRef10(false); const { delayPlayback } = useBufferState(); const bufferUntilFirstFrame = useCallback8((requestedTime) => { if (mediaType !== "video") { return; } if (!pauseWhenBuffering) { return; } const current = mediaRef.current; if (!current) { return; } if (current.readyState >= current.HAVE_FUTURE_DATA && !isSafariWebkit()) { playbackLogging({ logLevel, message: `Not using buffer until first frame, because readyState is ${current.readyState} and is not Safari or Desktop Chrome`, mountTime, tag: "buffer" }); return; } if (!current.requestVideoFrameCallback) { playbackLogging({ logLevel, message: `Not using buffer until first frame, because requestVideoFrameCallback is not supported`, mountTime, tag: "buffer" }); return; } bufferingRef.current = true; playbackLogging({ logLevel, message: `Buffering ${mediaRef.current?.src} until the first frame is received`, mountTime, tag: "buffer" }); const playback = delayPlayback(); const unblock = () => { playback.unblock(); current.removeEventListener("ended", unblock, { once: true }); current.removeEventListener("pause", unblock, { once: true }); bufferingRef.current = false; }; const onEndedOrPauseOrCanPlay = () => { unblock(); }; current.requestVideoFrameCallback((_, info2) => { const differenceFromRequested = Math.abs(info2.mediaTime - requestedTime); if (differenceFromRequested > 0.5) { onVariableFpsVideoDetected(); } unblock(); }); current.addEventListener("ended", onEndedOrPauseOrCanPlay, { once: true }); current.addEventListener("pause", onEndedOrPauseOrCanPlay, { once: true }); current.addEventListener("canplay", onEndedOrPauseOrCanPlay, { once: true }); }, [ delayPlayback, logLevel, mediaRef, mediaType, mountTime, onVariableFpsVideoDetected, pauseWhenBuffering ]); return useMemo19(() => { return { isBuffering: () => bufferingRef.current, bufferUntilFirstFrame }; }, [bufferUntilFirstFrame]); }; // src/media-tag-current-time-timestamp.ts import React17 from "react"; var useCurrentTimeOfMediaTagWithUpdateTimeStamp = (mediaRef) => { const lastUpdate = React17.useRef({ time: mediaRef.current?.currentTime ?? 0, lastUpdate: performance.now() }); const nowCurrentTime = mediaRef.current?.currentTime ?? null; if (nowCurrentTime !== null) { if (lastUpdate.current.time !== nowCurrentTime) { lastUpdate.current.time = nowCurrentTime; lastUpdate.current.lastUpdate = performance.now(); } } return lastUpdate; }; // src/seek.ts var seek = ({ mediaRef, time, logLevel, why, mountTime }) => { const timeToSet = isIosSafari() ? Number(time.toFixed(1)) : time; playbackLogging({ logLevel, tag: "seek", message: `Seeking from ${mediaRef.currentTime} to ${timeToSet}. src= ${mediaRef.src} Reason: ${why}`, mountTime }); mediaRef.currentTime = timeToSet; return timeToSet; }; // src/use-media-buffering.ts import { useEffect as useEffect7, useState as useState13 } from "react"; var useMediaBuffering = ({ element, shouldBuffer, isPremounting, isPostmounting, logLevel, mountTime, src }) => { const buffer = useBufferState(); const [isBuffering, setIsBuffering] = useState13(false); useEffect7(() => { let cleanupFns = []; const { current } = element; if (!current) { return; } if (!shouldBuffer) { return; } if (isPremounting || isPostmounting) { if ((isPremounting || isPostmounting) && current.readyState < current.HAVE_FUTURE_DATA) { if (!navigator.userAgent.includes("Firefox/")) { playbackLogging({ logLevel, message: `Calling .load() on ${current.src} because readyState is ${current.readyState} and it is not Firefox. Element is premounted ${current.playbackRate}`, tag: "load", mountTime }); const previousPlaybackRate = current.playbackRate; current.load(); current.playbackRate = previousPlaybackRate; } } return; } const cleanup = (reason) => { let didDoSomething = false; cleanupFns.forEach((fn) => { fn(reason); didDoSomething = true; }); cleanupFns = []; setIsBuffering((previous) => { if (previous) { didDoSomething = true; } return false; }); if (didDoSomething) { playbackLogging({ logLevel, message: `Unmarking as buffering: ${current.src}. Reason: ${reason}`, tag: "buffer", mountTime }); } }; const blockMedia = (reason) => { setIsBuffering(true); playbackLogging({ logLevel, message: `Marking as buffering: ${current.src}. Reason: ${reason}`, tag: "buffer", mountTime }); const { unblock } = buffer.delayPlayback(); const onCanPlay = () => { cleanup('"canplay" was fired'); init(); }; const onError = () => { cleanup('"error" event was occurred'); init(); }; current.addEventListener("canplay", onCanPlay, { once: true }); cleanupFns.push(() => { current.removeEventListener("canplay", onCanPlay); }); current.addEventListener("error", onError, { once: true }); cleanupFns.push(() => { current.removeEventListener("error", onError); }); cleanupFns.push((cleanupReason) => { playbackLogging({ logLevel, message: `Unblocking ${current.src} from buffer. Reason: ${cleanupReason}`, tag: "buffer", mountTime }); unblock(); }); }; const init = () => { if (current.readyState < current.HAVE_FUTURE_DATA) { blockMedia(`readyState is ${current.readyState}, which is less than HAVE_FUTURE_DATA`); if (!navigator.userAgent.includes("Firefox/")) { playbackLogging({ logLevel, message: `Calling .load() on ${src} because readyState is ${current.readyState} and it is not Firefox. ${current.playbackRate}`, tag: "load", mountTime }); const previousPlaybackRate = current.playbackRate; current.load(); current.playbackRate = previousPlaybackRate; } } else { const onWaiting = () => { blockMedia('"waiting" event was fired'); }; current.addEventListener("waiting", onWaiting); cleanupFns.push(() => { current.removeEventListener("waiting", onWaiting); }); } }; init(); return () => { cleanup("element was unmounted or prop changed"); }; }, [ buffer, src, element, isPremounting, isPostmounting, logLevel, shouldBuffer, mountTime ]); return isBuffering; }; // src/use-request-video-callback-time.ts import { useEffect as useEffect8, useRef as useRef11 } from "react"; var useRequestVideoCallbackTime = ({ mediaRef, mediaType, lastSeek, onVariableFpsVideoDetected }) => { const currentTime = useRef11(null); useEffect8(() => { const { current } = mediaRef; if (current) { currentTime.current = { time: current.currentTime, lastUpdate: performance.now() }; } else { currentTime.current = null; return; } if (mediaType !== "video") { currentTime.current = null; return; } const videoTag = current; if (!videoTag.requestVideoFrameCallback) { return; } let cancel = () => { return; }; const request = () => { if (!videoTag) { return; } const cb = videoTag.requestVideoFrameCallback((_, info2) => { if (currentTime.current !== null) { const difference = Math.abs(currentTime.current.time - info2.mediaTime); const differenceToLastSeek = Math.abs(lastSeek.current === null ? Infinity : info2.mediaTime - lastSeek.current); if (difference > 0.5 && differenceToLastSeek > 0.5 && info2.mediaTime > currentTime.current.time) { onVariableFpsVideoDetected(); } } currentTime.current = { time: info2.mediaTime, lastUpdate: performance.now() }; request(); }); cancel = () => { videoTag.cancelVideoFrameCallback(cb); cancel = () => { return; }; }; }; request(); return () => { cancel(); }; }, [lastSeek, mediaRef, mediaType, onVariableFpsVideoDetected]); return currentTime; }; // src/interpolate.ts function interpolateFunction(input, inputRange, outputRange, options) { const { extrapolateLeft, extrapolateRight, easing } = options; let result = input; const [inputMin, inputMax] = inputRange; const [outputMin, outputMax] = outputRange; if (result < inputMin) { if (extrapolateLeft === "identity") { return result; } if (extrapolateLeft === "clamp") { result = inputMin; } else if (extrapolateLeft === "wrap") { const range = inputMax - inputMin; result = ((result - inputMin) % range + range) % range + inputMin; } else if (extrapolateLeft === "extend") {} } if (result > inputMax) { if (extrapolateRight === "identity") { return result; } if (extrapolateRight === "clamp") { result = inputMax; } else if (extrapolateRight === "wrap") { const range = inputMax - inputMin; result = ((result - inputMin) % range + range) % range + inputMin; } else if (extrapolateRight === "extend") {} } if (outputMin === outputMax) { return outputMin; } result = (result - inputMin) / (inputMax - inputMin); result = easing(result); result = result * (outputMax - outputMin) + outputMin; return result; } function findRange(input, inputRange) { let i; for (i = 1;i < inputRange.length - 1; ++i) { if (inputRange[i] >= input) { break; } } return i - 1; } function checkValidInputRange(arr) { for (let i = 1;i < arr.length; ++i) { if (!(arr[i] > arr[i - 1])) { throw new Error(`inputRange must be strictly monotonically increasing but got [${arr.join(",")}]`); } } } function checkInfiniteRange(name, arr) { if (arr.length < 2) { throw new Error(name + " must have at least 2 elements"); } for (const element of arr) { if (typeof element !== "number") { throw new Error(`${name} must contain only numbers`); } if (!Number.isFinite(element)) { throw new Error(`${name} must contain only finite numbers, but got [${arr.join(",")}]`); } } } function interpolate(input, inputRange, outputRange, options) { if (typeof input === "undefined") { throw new Error("input can not be undefined"); } if (typeof inputRange === "undefined") { throw new Error("inputRange can not be undefined"); } if (typeof outputRange === "undefined") { throw new Error("outputRange can not be undefined"); } if (inputRange.length !== outputRange.length) { throw new Error("inputRange (" + inputRange.length + ") and outputRange (" + outputRange.length + ") must have the same length"); } checkInfiniteRange("inputRange", inputRange); checkInfiniteRange("outputRange", outputRange); checkValidInputRange(inputRange); const easing = options?.easing ?? ((num) => num); let extrapolateLeft = "extend"; if (options?.extrapolateLeft !== undefined) { extrapolateLeft = options.extrapolateLeft; } let extrapolateRight = "extend"; if (options?.extrapolateRight !== undefined) { extrapolateRight = options.extrapolateRight; } if (typeof input !== "number") { throw new TypeError("Cannot interpolate an input which is not a number"); } const range = findRange(input, inputRange); return interpolateFunction(input, [inputRange[range], inputRange[range + 1]], [outputRange[range], outputRange[range + 1]], { easing, extrapolateLeft, extrapolateRight }); } // src/video/get-current-time.ts var getExpectedMediaFrameUncorrected = ({ frame, playbackRate, startFrom }) => { return interpolate(frame, [-1, startFrom, startFrom + 1], [-1, startFrom, startFrom + playbackRate]); }; var getMediaTime = ({ fps, frame, playbackRate, startFrom }) => { const expectedFrame = getExpectedMediaFrameUncorrected({ frame, playbackRate, startFrom }); const msPerFrame = 1000 / fps; return expectedFrame * msPerFrame / 1000; }; // src/warn-about-non-seekable-media.ts var alreadyWarned = {}; var warnAboutNonSeekableMedia = (ref, type) => { if (ref === null) { return; } if (ref.seekable.length === 0) { return; } if (ref.seekable.length > 1) { return; } if (alreadyWarned[ref.src]) { return; } const range = { start: ref.seekable.start(0), end: ref.seekable.end(0) }; if (range.start === 0 && range.end === 0) { const msg = [ `The media ${ref.src} cannot be seeked. This could be one of few reasons:`, "1) The media resource was replaced while the video is playing but it was not loaded yet.", "2) The media does not support seeking.", "3) The media was loaded with security headers prventing it from being included.", "Please see https://remotion.dev/docs/non-seekable-media for assistance." ].join(` `); if (type === "console-error") { console.error(msg); } else if (type === "console-warning") { console.warn(`The media ${ref.src} does not support seeking. The video will render fine, but may not play correctly in the Remotion Studio and in the . See https://remotion.dev/docs/non-seekable-media for an explanation.`); } else { throw new Error(msg); } alreadyWarned[ref.src] = true; } }; // src/use-media-playback.ts var useMediaPlayback = ({ mediaRef, src, mediaType, playbackRate: localPlaybackRate, onlyWarnForMediaSeekingError, acceptableTimeshift, pauseWhenBuffering, isPremounting, isPostmounting, onAutoPlayError }) => { const { playbackRate: globalPlaybackRate } = useContext22(TimelineContext); const frame = useCurrentFrame(); const absoluteFrame = useTimelinePosition(); const [playing] = usePlayingState(); const buffering = useContext22(BufferingContextReact); const { fps } = useVideoConfig(); const mediaStartsAt = useMediaStartsAt(); const lastSeekDueToShift = useRef12(null); const lastSeek = useRef12(null); const logLevel = useLogLevel(); const mountTime = useMountTime(); if (!buffering) { throw new Error("useMediaPlayback must be used inside a "); } const isVariableFpsVideoMap = useRef12({}); const onVariableFpsVideoDetected = useCallback9(() => { if (!src) { return; } if (isVariableFpsVideoMap.current[src]) { return; } Log.verbose({ logLevel, tag: null }, `Detected ${src} as a variable FPS video. Disabling buffering while seeking.`); isVariableFpsVideoMap.current[src] = true; }, [logLevel, src]); const rvcCurrentTime = useRequestVideoCallbackTime({ mediaRef, mediaType, lastSeek, onVariableFpsVideoDetected }); const mediaTagCurrentTime = useCurrentTimeOfMediaTagWithUpdateTimeStamp(mediaRef); const desiredUnclampedTime = getMediaTime({ frame, playbackRate: localPlaybackRate, startFrom: -mediaStartsAt, fps }); const isMediaTagBuffering = useMediaBuffering({ element: mediaRef, shouldBuffer: pauseWhenBuffering, isPremounting, isPostmounting, logLevel, mountTime, src: src ?? null }); const { bufferUntilFirstFrame, isBuffering } = useBufferUntilFirstFrame({ mediaRef, mediaType, onVariableFpsVideoDetected, pauseWhenBuffering, logLevel, mountTime }); const playbackRate = localPlaybackRate * globalPlaybackRate; const acceptableTimeShiftButLessThanDuration = (() => { const DEFAULT_ACCEPTABLE_TIMESHIFT_WITH_NORMAL_PLAYBACK = 0.45; const DEFAULT_ACCEPTABLE_TIMESHIFT_WITH_AMPLIFICATION = DEFAULT_ACCEPTABLE_TIMESHIFT_WITH_NORMAL_PLAYBACK + 0.2; const defaultAcceptableTimeshift = DEFAULT_ACCEPTABLE_TIMESHIFT_WITH_AMPLIFICATION; if (mediaRef.current?.duration) { return Math.min(mediaRef.current.duration, acceptableTimeshift ?? defaultAcceptableTimeshift); } return acceptableTimeshift ?? defaultAcceptableTimeshift; })(); const isPlayerBuffering = useIsPlayerBuffering(buffering); useEffect9(() => { if (mediaRef.current?.paused) { return; } if (!playing) { playbackLogging({ logLevel, tag: "pause", message: `Pausing ${mediaRef.current?.src} because ${isPremounting ? "media is premounting" : isPostmounting ? "media is postmounting" : "Player is not playing"}`, mountTime }); mediaRef.current?.pause(); return; } const isMediaTagBufferingOrStalled = isMediaTagBuffering || isBuffering(); const playerBufferingNotStateButLive = buffering.buffering.current; if (playerBufferingNotStateButLive && !isMediaTagBufferingOrStalled) { playbackLogging({ logLevel, tag: "pause", message: `Pausing ${mediaRef.current?.src} because player is buffering but media tag is not`, mountTime }); mediaRef.current?.pause(); } }, [ isBuffering, isMediaTagBuffering, buffering, isPlayerBuffering, isPremounting, logLevel, mediaRef, mediaType, mountTime, playing, isPostmounting ]); const env = useRemotionEnvironment(); useLayoutEffect7(() => { const playbackRateToSet = Math.max(0, playbackRate); if (mediaRef.current && mediaRef.current.playbackRate !== playbackRateToSet) { mediaRef.current.playbackRate = playbackRateToSet; } }, [mediaRef, playbackRate]); useEffect9(() => { const tagName = mediaType === "audio" ? "" : ""; if (!mediaRef.current) { throw new Error(`No ${mediaType} ref found`); } if (!src) { throw new Error(`No 'src' attribute was passed to the ${tagName} element.`); } const { duration } = mediaRef.current; const shouldBeTime = !Number.isNaN(duration) && Number.isFinite(duration) ? Math.min(duration, desiredUnclampedTime) : desiredUnclampedTime; const mediaTagTime = mediaTagCurrentTime.current.time; const rvcTime = rvcCurrentTime.current?.time ?? null; const isVariableFpsVideo = isVariableFpsVideoMap.current[src]; const timeShiftMediaTag = Math.abs(shouldBeTime - mediaTagTime); const timeShiftRvcTag = rvcTime ? Math.abs(shouldBeTime - rvcTime) : null; const mostRecentTimeshift = rvcCurrentTime.current?.lastUpdate && rvcCurrentTime.current.time > mediaTagCurrentTime.current.lastUpdate ? timeShiftRvcTag : timeShiftMediaTag; const timeShift = timeShiftRvcTag && !isVariableFpsVideo ? mostRecentTimeshift : timeShiftMediaTag; if (timeShift > acceptableTimeShiftButLessThanDuration && lastSeekDueToShift.current !== shouldBeTime) { lastSeek.current = seek({ mediaRef: mediaRef.current, time: shouldBeTime, logLevel, why: `because time shift is too big. shouldBeTime = ${shouldBeTime}, isTime = ${mediaTagTime}, requestVideoCallbackTime = ${rvcTime}, timeShift = ${timeShift}${isVariableFpsVideo ? ", isVariableFpsVideo = true" : ""}, isPremounting = ${isPremounting}, isPostmounting = ${isPostmounting}, pauseWhenBuffering = ${pauseWhenBuffering}`, mountTime }); lastSeekDueToShift.current = lastSeek.current; if (playing) { if (playbackRate > 0) { bufferUntilFirstFrame(shouldBeTime); } if (mediaRef.current.paused) { playAndHandleNotAllowedError({ mediaRef, mediaType, onAutoPlayError, logLevel, mountTime, reason: "player is playing but media tag is paused, and just seeked", isPlayer: env.isPlayer }); } } if (!onlyWarnForMediaSeekingError) { warnAboutNonSeekableMedia(mediaRef.current, onlyWarnForMediaSeekingError ? "console-warning" : "console-error"); } return; } const seekThreshold = playing ? 0.15 : 0.01; const makesSenseToSeek = Math.abs(mediaRef.current.currentTime - shouldBeTime) > seekThreshold; const isMediaTagBufferingOrStalled = isMediaTagBuffering || isBuffering(); const isSomethingElseBuffering = buffering.buffering.current && !isMediaTagBufferingOrStalled; if (!playing || isSomethingElseBuffering) { if (makesSenseToSeek) { lastSeek.current = seek({ mediaRef: mediaRef.current, time: shouldBeTime, logLevel, why: `not playing or something else is buffering. time offset is over seek threshold (${seekThreshold})`, mountTime }); } return; } if (!playing || buffering.buffering.current) { return; } const pausedCondition = mediaRef.current.paused && !mediaRef.current.ended; const firstFrameCondition = absoluteFrame === 0; if (pausedCondition || firstFrameCondition) { const reason = pausedCondition ? "media tag is paused" : "absolute frame is 0"; if (makesSenseToSeek) { lastSeek.current = seek({ mediaRef: mediaRef.current, time: shouldBeTime, logLevel, why: `is over timeshift threshold (threshold = ${seekThreshold}) and ${reason}`, mountTime }); } playAndHandleNotAllowedError({ mediaRef, mediaType, onAutoPlayError, logLevel, mountTime, reason: `player is playing and ${reason}`, isPlayer: env.isPlayer }); if (!isVariableFpsVideo && playbackRate > 0) { bufferUntilFirstFrame(shouldBeTime); } } }, [ absoluteFrame, acceptableTimeShiftButLessThanDuration, bufferUntilFirstFrame, buffering.buffering, rvcCurrentTime, logLevel, desiredUnclampedTime, isBuffering, isMediaTagBuffering, mediaRef, mediaType, onlyWarnForMediaSeekingError, playbackRate, playing, src, onAutoPlayError, isPremounting, isPostmounting, pauseWhenBuffering, mountTime, mediaTagCurrentTime, env.isPlayer ]); }; // src/use-media-tag.ts import { useContext as useContext23, useEffect as useEffect10 } from "react"; var useMediaTag = ({ mediaRef, id, mediaType, onAutoPlayError, isPremounting, isPostmounting }) => { const { audioAndVideoTags, imperativePlaying } = useContext23(TimelineContext); const logLevel = useLogLevel(); const mountTime = useMountTime(); const env = useRemotionEnvironment(); useEffect10(() => { const tag = { id, play: (reason) => { if (!imperativePlaying.current) { return; } if (isPremounting || isPostmounting) { return; } return playAndHandleNotAllowedError({ mediaRef, mediaType, onAutoPlayError, logLevel, mountTime, reason, isPlayer: env.isPlayer }); } }; audioAndVideoTags.current.push(tag); return () => { audioAndVideoTags.current = audioAndVideoTags.current.filter((a) => a.id !== id); }; }, [ audioAndVideoTags, id, mediaRef, mediaType, onAutoPlayError, imperativePlaying, isPremounting, isPostmounting, logLevel, mountTime, env.isPlayer ]); }; // src/volume-position-state.ts import { createContext as createContext17, useContext as useContext24, useMemo as useMemo20 } from "react"; var MediaVolumeContext = createContext17({ mediaMuted: false, mediaVolume: 1 }); var SetMediaVolumeContext = createContext17({ setMediaMuted: () => { throw new Error("default"); }, setMediaVolume: () => { throw new Error("default"); } }); var useMediaVolumeState = () => { const { mediaVolume } = useContext24(MediaVolumeContext); const { setMediaVolume } = useContext24(SetMediaVolumeContext); return useMemo20(() => { return [mediaVolume, setMediaVolume]; }, [mediaVolume, setMediaVolume]); }; var useMediaMutedState = () => { const { mediaMuted } = useContext24(MediaVolumeContext); const { setMediaMuted } = useContext24(SetMediaVolumeContext); return useMemo20(() => { return [mediaMuted, setMediaMuted]; }, [mediaMuted, setMediaMuted]); }; // src/volume-safeguard.ts var warnAboutTooHighVolume = (volume) => { if (volume >= 100) { throw new Error(`Volume was set to ${volume}, but regular volume is 1, not 100. Did you forget to divide by 100? Set a volume of less than 100 to dismiss this error.`); } }; // src/audio/AudioForPreview.tsx import { jsx as jsx17 } from "react/jsx-runtime"; var AudioForDevelopmentForwardRefFunction = (props, ref) => { const [initialShouldPreMountAudioElements] = useState14(props.shouldPreMountAudioTags); if (props.shouldPreMountAudioTags !== initialShouldPreMountAudioElements) { throw new Error("Cannot change the behavior for pre-mounting audio tags dynamically."); } const logLevel = useLogLevel(); const { volume, muted, playbackRate, shouldPreMountAudioTags, src, onDuration, acceptableTimeShiftInSeconds, _remotionInternalNeedsDurationCalculation, _remotionInternalNativeLoopPassed, _remotionInternalStack, allowAmplificationDuringRender, name, pauseWhenBuffering, showInTimeline, loopVolumeCurveBehavior, stack, crossOrigin, delayRenderRetries, delayRenderTimeoutInMilliseconds, toneFrequency, useWebAudioApi, onError, onNativeError, audioStreamIndex, ...nativeProps } = props; const _propsValid = true; if (!_propsValid) { throw new Error("typecheck error"); } const [mediaVolume] = useMediaVolumeState(); const [mediaMuted] = useMediaMutedState(); const volumePropFrame = useFrameForVolumeProp(loopVolumeCurveBehavior ?? "repeat"); const { hidden } = useContext25(SequenceVisibilityToggleContext); if (!src) { throw new TypeError("No 'src' was passed to ."); } const preloadedSrc = usePreload(src); const sequenceContext = useContext25(SequenceContext); const [timelineId] = useState14(() => String(Math.random())); const isSequenceHidden = hidden[timelineId] ?? false; const userPreferredVolume = evaluateVolume({ frame: volumePropFrame, volume, mediaVolume }); warnAboutTooHighVolume(userPreferredVolume); const crossOriginValue = getCrossOriginValue({ crossOrigin, requestsVideoFrame: false, isClientSideRendering: false }); const propsToPass = useMemo21(() => { return { muted: muted || mediaMuted || isSequenceHidden || userPreferredVolume <= 0, src: preloadedSrc, loop: _remotionInternalNativeLoopPassed, crossOrigin: crossOriginValue, ...nativeProps }; }, [ _remotionInternalNativeLoopPassed, isSequenceHidden, mediaMuted, muted, nativeProps, preloadedSrc, userPreferredVolume, crossOriginValue ]); const id = useMemo21(() => `audio-${random(src ?? "")}-${sequenceContext?.relativeFrom}-${sequenceContext?.cumulatedFrom}-${sequenceContext?.durationInFrames}-muted:${props.muted}-loop:${props.loop}`, [ src, sequenceContext?.relativeFrom, sequenceContext?.cumulatedFrom, sequenceContext?.durationInFrames, props.muted, props.loop ]); const { el: audioRef, mediaElementSourceNode, cleanupOnMediaTagUnmount } = useSharedAudio({ aud: propsToPass, audioId: id, premounting: Boolean(sequenceContext?.premounting), postmounting: Boolean(sequenceContext?.postmounting) }); useMediaInTimeline({ volume, mediaVolume, src, mediaType: "audio", playbackRate: playbackRate ?? 1, displayName: name ?? null, id: timelineId, stack: _remotionInternalStack, showInTimeline, premountDisplay: sequenceContext?.premountDisplay ?? null, postmountDisplay: sequenceContext?.postmountDisplay ?? null, loopDisplay: undefined }); useMediaPlayback({ mediaRef: audioRef, src, mediaType: "audio", playbackRate: playbackRate ?? 1, onlyWarnForMediaSeekingError: false, acceptableTimeshift: acceptableTimeShiftInSeconds ?? null, isPremounting: Boolean(sequenceContext?.premounting), isPostmounting: Boolean(sequenceContext?.postmounting), pauseWhenBuffering, onAutoPlayError: null }); useMediaTag({ id: timelineId, isPostmounting: Boolean(sequenceContext?.postmounting), isPremounting: Boolean(sequenceContext?.premounting), mediaRef: audioRef, mediaType: "audio", onAutoPlayError: null }); useVolume({ logLevel, mediaRef: audioRef, source: mediaElementSourceNode, volume: userPreferredVolume, shouldUseWebAudioApi: useWebAudioApi ?? false }); const effectToUse = React18.useInsertionEffect ?? React18.useLayoutEffect; effectToUse(() => { return () => { requestAnimationFrame(() => { cleanupOnMediaTagUnmount(); }); }; }, [cleanupOnMediaTagUnmount]); useImperativeHandle5(ref, () => { return audioRef.current; }, [audioRef]); const currentOnDurationCallback = useRef13(onDuration); currentOnDurationCallback.current = onDuration; useEffect11(() => { const { current } = audioRef; if (!current) { return; } if (current.duration) { currentOnDurationCallback.current?.(current.src, current.duration); return; } const onLoadedMetadata = () => { currentOnDurationCallback.current?.(current.src, current.duration); }; current.addEventListener("loadedmetadata", onLoadedMetadata); return () => { current.removeEventListener("loadedmetadata", onLoadedMetadata); }; }, [audioRef, src]); if (initialShouldPreMountAudioElements) { return null; } return /* @__PURE__ */ jsx17("audio", { ref: audioRef, preload: "metadata", crossOrigin: crossOriginValue, ...propsToPass }); }; var AudioForPreview = forwardRef4(AudioForDevelopmentForwardRefFunction); // src/audio/AudioForRendering.tsx import { forwardRef as forwardRef5, useContext as useContext26, useEffect as useEffect12, useImperativeHandle as useImperativeHandle6, useLayoutEffect as useLayoutEffect8, useMemo as useMemo22, useRef as useRef14 } from "react"; import { jsx as jsx18 } from "react/jsx-runtime"; var AudioForRenderingRefForwardingFunction = (props, ref) => { const audioRef = useRef14(null); const { volume: volumeProp, playbackRate, allowAmplificationDuringRender, onDuration, toneFrequency, _remotionInternalNeedsDurationCalculation, _remotionInternalNativeLoopPassed, acceptableTimeShiftInSeconds, name, onNativeError, delayRenderRetries, delayRenderTimeoutInMilliseconds, loopVolumeCurveBehavior, pauseWhenBuffering, audioStreamIndex, ...nativeProps } = props; const absoluteFrame = useTimelinePosition(); const volumePropFrame = useFrameForVolumeProp(loopVolumeCurveBehavior ?? "repeat"); const frame = useCurrentFrame(); const sequenceContext = useContext26(SequenceContext); const { registerRenderAsset, unregisterRenderAsset } = useContext26(RenderAssetManager); const { delayRender: delayRender2, continueRender: continueRender2 } = useDelayRender(); const id = useMemo22(() => `audio-${random(props.src ?? "")}-${sequenceContext?.relativeFrom}-${sequenceContext?.cumulatedFrom}-${sequenceContext?.durationInFrames}`, [ props.src, sequenceContext?.relativeFrom, sequenceContext?.cumulatedFrom, sequenceContext?.durationInFrames ]); const volume = evaluateVolume({ volume: volumeProp, frame: volumePropFrame, mediaVolume: 1 }); warnAboutTooHighVolume(volume); useImperativeHandle6(ref, () => { return audioRef.current; }, []); useEffect12(() => { if (!props.src) { throw new Error("No src passed"); } if (!window.remotion_audioEnabled) { return; } if (props.muted) { return; } if (volume <= 0) { return; } registerRenderAsset({ type: "audio", src: getAbsoluteSrc(props.src), id, frame: absoluteFrame, volume, mediaFrame: frame, playbackRate: props.playbackRate ?? 1, toneFrequency: toneFrequency ?? 1, audioStartFrame: Math.max(0, -(sequenceContext?.relativeFrom ?? 0)), audioStreamIndex: audioStreamIndex ?? 0 }); return () => unregisterRenderAsset(id); }, [ props.muted, props.src, registerRenderAsset, absoluteFrame, id, unregisterRenderAsset, volume, volumePropFrame, frame, playbackRate, props.playbackRate, toneFrequency, sequenceContext?.relativeFrom, audioStreamIndex ]); const { src } = props; const needsToRenderAudioTag = ref || _remotionInternalNeedsDurationCalculation; useLayoutEffect8(() => { if (window.process?.env?.NODE_ENV === "test") { return; } if (!needsToRenderAudioTag) { return; } const newHandle = delayRender2("Loading duration with src=" + src, { retries: delayRenderRetries ?? undefined, timeoutInMilliseconds: delayRenderTimeoutInMilliseconds ?? undefined }); const { current } = audioRef; const didLoad = () => { if (current?.duration) { onDuration(current.src, current.duration); } continueRender2(newHandle); }; if (current?.duration) { onDuration(current.src, current.duration); continueRender2(newHandle); } else { current?.addEventListener("loadedmetadata", didLoad, { once: true }); } return () => { current?.removeEventListener("loadedmetadata", didLoad); continueRender2(newHandle); }; }, [ src, onDuration, needsToRenderAudioTag, delayRenderRetries, delayRenderTimeoutInMilliseconds, continueRender2, delayRender2 ]); if (!needsToRenderAudioTag) { return null; } return /* @__PURE__ */ jsx18("audio", { ref: audioRef, ...nativeProps, onError: onNativeError }); }; var AudioForRendering = forwardRef5(AudioForRenderingRefForwardingFunction); // src/audio/Audio.tsx import { jsx as jsx19 } from "react/jsx-runtime"; var AudioRefForwardingFunction = (props, ref) => { const audioContext = useContext27(SharedAudioContext); const { startFrom, endAt, trimBefore, trimAfter, name, stack, pauseWhenBuffering, showInTimeline, onError: onRemotionError, ...otherProps } = props; const { loop, ...propsOtherThanLoop } = props; const { fps } = useVideoConfig(); const environment = useRemotionEnvironment(); if (environment.isClientSideRendering) { throw new Error(" is not supported in @remotion/web-renderer. Use