Add .gitignore to exclude all node packages and lock files

This commit is contained in:
Adolfo Reyna
2026-02-23 21:56:04 -05:00
parent faae96c9ed
commit dcc5c6c044
9747 changed files with 1555105 additions and 2 deletions
@@ -0,0 +1,4 @@
import React from 'react';
import type { AllOffthreadVideoProps, RemotionOffthreadVideoProps } from './props.js';
export declare const InnerOffthreadVideo: React.FC<AllOffthreadVideoProps>;
export declare const OffthreadVideo: React.FC<RemotionOffthreadVideoProps>;
@@ -0,0 +1,55 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.OffthreadVideo = exports.InnerOffthreadVideo = void 0;
const jsx_runtime_1 = require("react/jsx-runtime");
const react_1 = require("react");
const Sequence_js_1 = require("../Sequence.js");
const enable_sequence_stack_traces_js_1 = require("../enable-sequence-stack-traces.js");
const use_remotion_environment_js_1 = require("../use-remotion-environment.js");
const validate_media_props_js_1 = require("../validate-media-props.js");
const validate_start_from_props_js_1 = require("../validate-start-from-props.js");
const OffthreadVideoForRendering_js_1 = require("./OffthreadVideoForRendering.js");
const VideoForPreview_js_1 = require("./VideoForPreview.js");
const InnerOffthreadVideo = (props) => {
// Should only destruct `startFrom` and `endAt` from props,
// rest gets drilled down
const { startFrom, endAt, trimBefore, trimAfter, name, pauseWhenBuffering, stack, showInTimeline, ...otherProps } = props;
const environment = (0, use_remotion_environment_js_1.useRemotionEnvironment)();
if (environment.isClientSideRendering) {
throw new Error('<OffthreadVideo> is not supported in @remotion/web-renderer. Use <Video> from @remotion/media instead. See https://remotion.dev/docs/client-side-rendering/limitations');
}
const onDuration = (0, react_1.useCallback)(() => undefined, []);
if (typeof props.src !== 'string') {
throw new TypeError(`The \`<OffthreadVideo>\` tag requires a string for \`src\`, but got ${JSON.stringify(props.src)} instead.`);
}
(0, validate_start_from_props_js_1.validateMediaTrimProps)({ startFrom, endAt, trimBefore, trimAfter });
const { trimBeforeValue, trimAfterValue } = (0, validate_start_from_props_js_1.resolveTrimProps)({
startFrom,
endAt,
trimBefore,
trimAfter,
});
if (typeof trimBeforeValue !== 'undefined' ||
typeof trimAfterValue !== 'undefined') {
return ((0, jsx_runtime_1.jsx)(Sequence_js_1.Sequence, { layout: "none", from: 0 - (trimBeforeValue !== null && trimBeforeValue !== void 0 ? trimBeforeValue : 0), showInTimeline: false, durationInFrames: trimAfterValue, name: name, children: (0, jsx_runtime_1.jsx)(exports.InnerOffthreadVideo, { pauseWhenBuffering: pauseWhenBuffering !== null && pauseWhenBuffering !== void 0 ? pauseWhenBuffering : false, ...otherProps, trimAfter: undefined, name: undefined, showInTimeline: showInTimeline, trimBefore: undefined, stack: undefined, startFrom: undefined, endAt: undefined }) }));
}
(0, validate_media_props_js_1.validateMediaProps)(props, 'Video');
if (environment.isRendering) {
return ((0, jsx_runtime_1.jsx)(OffthreadVideoForRendering_js_1.OffthreadVideoForRendering, { pauseWhenBuffering: pauseWhenBuffering !== null && pauseWhenBuffering !== void 0 ? pauseWhenBuffering : false, ...otherProps, trimAfter: undefined, name: undefined, showInTimeline: showInTimeline, trimBefore: undefined, stack: undefined, startFrom: undefined, endAt: undefined }));
}
const { transparent, toneMapped, onAutoPlayError, onVideoFrame, crossOrigin, delayRenderRetries, delayRenderTimeoutInMilliseconds, ...propsForPreview } = otherProps;
return ((0, jsx_runtime_1.jsx)(VideoForPreview_js_1.VideoForPreview, { _remotionInternalStack: stack !== null && stack !== void 0 ? stack : null, onDuration: onDuration, onlyWarnForMediaSeekingError: true, pauseWhenBuffering: pauseWhenBuffering !== null && pauseWhenBuffering !== void 0 ? pauseWhenBuffering : false, showInTimeline: showInTimeline !== null && showInTimeline !== void 0 ? showInTimeline : true, onAutoPlayError: onAutoPlayError !== null && onAutoPlayError !== void 0 ? onAutoPlayError : undefined, onVideoFrame: onVideoFrame !== null && onVideoFrame !== void 0 ? onVideoFrame : null, crossOrigin: crossOrigin, ...propsForPreview, _remotionInternalNativeLoopPassed: false }));
};
exports.InnerOffthreadVideo = InnerOffthreadVideo;
/*
* @description This method imports and displays a video, similar to <Html5Video />. During rendering, it extracts the exact frame from the video and displays it in an <img> tag
* @see [Documentation](https://www.remotion.dev/docs/offthreadvideo)
*/
const OffthreadVideo = ({ src, acceptableTimeShiftInSeconds, allowAmplificationDuringRender, audioStreamIndex, className, crossOrigin, delayRenderRetries, delayRenderTimeoutInMilliseconds, id, loopVolumeCurveBehavior, muted, name, onAutoPlayError, onError, onVideoFrame, pauseWhenBuffering, playbackRate, showInTimeline, style, toneFrequency, toneMapped, transparent, trimAfter, trimBefore, useWebAudioApi, volume, _remotionInternalNativeLoopPassed, endAt, stack, startFrom, imageFormat, }) => {
if (imageFormat) {
throw new TypeError(`The \`<OffthreadVideo>\` tag does no longer accept \`imageFormat\`. Use the \`transparent\` prop if you want to render a transparent video.`);
}
return ((0, jsx_runtime_1.jsx)(exports.InnerOffthreadVideo, { acceptableTimeShiftInSeconds: acceptableTimeShiftInSeconds, allowAmplificationDuringRender: allowAmplificationDuringRender !== null && allowAmplificationDuringRender !== void 0 ? allowAmplificationDuringRender : true, audioStreamIndex: audioStreamIndex !== null && audioStreamIndex !== void 0 ? audioStreamIndex : 0, className: className, crossOrigin: crossOrigin, delayRenderRetries: delayRenderRetries, delayRenderTimeoutInMilliseconds: delayRenderTimeoutInMilliseconds, id: id, loopVolumeCurveBehavior: loopVolumeCurveBehavior !== null && loopVolumeCurveBehavior !== void 0 ? loopVolumeCurveBehavior : 'repeat', muted: muted !== null && muted !== void 0 ? muted : false, name: name, onAutoPlayError: onAutoPlayError !== null && onAutoPlayError !== void 0 ? onAutoPlayError : null, onError: onError, onVideoFrame: onVideoFrame, pauseWhenBuffering: pauseWhenBuffering !== null && pauseWhenBuffering !== void 0 ? pauseWhenBuffering : true, playbackRate: playbackRate !== null && playbackRate !== void 0 ? playbackRate : 1, toneFrequency: toneFrequency !== null && toneFrequency !== void 0 ? toneFrequency : 1, showInTimeline: showInTimeline !== null && showInTimeline !== void 0 ? showInTimeline : true, src: src, stack: stack, startFrom: startFrom, _remotionInternalNativeLoopPassed: _remotionInternalNativeLoopPassed !== null && _remotionInternalNativeLoopPassed !== void 0 ? _remotionInternalNativeLoopPassed : false, endAt: endAt, style: style, toneMapped: toneMapped !== null && toneMapped !== void 0 ? toneMapped : true, transparent: transparent !== null && transparent !== void 0 ? transparent : false, trimAfter: trimAfter, trimBefore: trimBefore, useWebAudioApi: useWebAudioApi !== null && useWebAudioApi !== void 0 ? useWebAudioApi : false, volume: volume }));
};
exports.OffthreadVideo = OffthreadVideo;
(0, enable_sequence_stack_traces_js_1.addSequenceStackTraces)(exports.OffthreadVideo);
@@ -0,0 +1,3 @@
import React from 'react';
import type { AllOffthreadVideoProps } from './props.js';
export declare const OffthreadVideoForRendering: React.FC<AllOffthreadVideoProps>;
@@ -0,0 +1,211 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.OffthreadVideoForRendering = void 0;
const jsx_runtime_1 = require("react/jsx-runtime");
const react_1 = require("react");
const Img_js_1 = require("../Img.js");
const RenderAssetManager_js_1 = require("../RenderAssetManager.js");
const SequenceContext_js_1 = require("../SequenceContext.js");
const absolute_src_js_1 = require("../absolute-src.js");
const use_audio_frame_js_1 = require("../audio/use-audio-frame.js");
const cancel_render_js_1 = require("../cancel-render.js");
const default_css_js_1 = require("../default-css.js");
const random_js_1 = require("../random.js");
const timeline_position_state_js_1 = require("../timeline-position-state.js");
const truthy_js_1 = require("../truthy.js");
const use_current_frame_js_1 = require("../use-current-frame.js");
const use_delay_render_js_1 = require("../use-delay-render.js");
const use_unsafe_video_config_js_1 = require("../use-unsafe-video-config.js");
const volume_prop_js_1 = require("../volume-prop.js");
const volume_safeguard_js_1 = require("../volume-safeguard.js");
const get_current_time_js_1 = require("./get-current-time.js");
const offthread_video_source_js_1 = require("./offthread-video-source.js");
const OffthreadVideoForRendering = ({ onError, volume: volumeProp, playbackRate, src, muted, allowAmplificationDuringRender, transparent, toneMapped, toneFrequency, name, loopVolumeCurveBehavior, delayRenderRetries, delayRenderTimeoutInMilliseconds, onVideoFrame,
// Remove crossOrigin prop during rendering
// https://discord.com/channels/809501355504959528/844143007183667220/1311639632496033813
crossOrigin, audioStreamIndex, ...props }) => {
const absoluteFrame = (0, timeline_position_state_js_1.useTimelinePosition)();
const frame = (0, use_current_frame_js_1.useCurrentFrame)();
const volumePropsFrame = (0, use_audio_frame_js_1.useFrameForVolumeProp)(loopVolumeCurveBehavior);
const videoConfig = (0, use_unsafe_video_config_js_1.useUnsafeVideoConfig)();
const sequenceContext = (0, react_1.useContext)(SequenceContext_js_1.SequenceContext);
const mediaStartsAt = (0, use_audio_frame_js_1.useMediaStartsAt)();
const { registerRenderAsset, unregisterRenderAsset } = (0, react_1.useContext)(RenderAssetManager_js_1.RenderAssetManager);
if (!src) {
throw new TypeError('No `src` was passed to <OffthreadVideo>.');
}
// Generate a string that's as unique as possible for this asset
// but at the same time the same on all threads
const id = (0, react_1.useMemo)(() => `offthreadvideo-${(0, random_js_1.random)(src)}-${sequenceContext === null || sequenceContext === void 0 ? void 0 : sequenceContext.cumulatedFrom}-${sequenceContext === null || sequenceContext === void 0 ? void 0 : sequenceContext.relativeFrom}-${sequenceContext === null || sequenceContext === void 0 ? void 0 : sequenceContext.durationInFrames}`, [
src,
sequenceContext === null || sequenceContext === void 0 ? void 0 : sequenceContext.cumulatedFrom,
sequenceContext === null || sequenceContext === void 0 ? void 0 : sequenceContext.relativeFrom,
sequenceContext === null || sequenceContext === void 0 ? void 0 : sequenceContext.durationInFrames,
]);
if (!videoConfig) {
throw new Error('No video config found');
}
const volume = (0, volume_prop_js_1.evaluateVolume)({
volume: volumeProp,
frame: volumePropsFrame,
mediaVolume: 1,
});
(0, volume_safeguard_js_1.warnAboutTooHighVolume)(volume);
(0, react_1.useEffect)(() => {
var _a;
if (!src) {
throw new Error('No src passed');
}
if (!window.remotion_audioEnabled) {
return;
}
if (muted) {
return;
}
if (volume <= 0) {
return;
}
registerRenderAsset({
type: 'video',
src: (0, absolute_src_js_1.getAbsoluteSrc)(src),
id,
frame: absoluteFrame,
volume,
mediaFrame: frame,
playbackRate,
toneFrequency,
audioStartFrame: Math.max(0, -((_a = sequenceContext === null || sequenceContext === void 0 ? void 0 : sequenceContext.relativeFrom) !== null && _a !== void 0 ? _a : 0)),
audioStreamIndex,
});
return () => unregisterRenderAsset(id);
}, [
muted,
src,
registerRenderAsset,
id,
unregisterRenderAsset,
volume,
frame,
absoluteFrame,
playbackRate,
toneFrequency,
sequenceContext === null || sequenceContext === void 0 ? void 0 : sequenceContext.relativeFrom,
audioStreamIndex,
]);
const currentTime = (0, react_1.useMemo)(() => {
return ((0, get_current_time_js_1.getExpectedMediaFrameUncorrected)({
frame,
playbackRate: playbackRate || 1,
startFrom: -mediaStartsAt,
}) / videoConfig.fps);
}, [frame, mediaStartsAt, playbackRate, videoConfig.fps]);
const actualSrc = (0, react_1.useMemo)(() => {
return (0, offthread_video_source_js_1.getOffthreadVideoSource)({
src,
currentTime,
transparent,
toneMapped,
});
}, [toneMapped, currentTime, src, transparent]);
const [imageSrc, setImageSrc] = (0, react_1.useState)(null);
const { delayRender, continueRender } = (0, use_delay_render_js_1.useDelayRender)();
(0, react_1.useLayoutEffect)(() => {
if (!window.remotion_videoEnabled) {
return;
}
const cleanup = [];
setImageSrc(null);
const controller = new AbortController();
const newHandle = delayRender(`Fetching ${actualSrc} from server`, {
retries: delayRenderRetries !== null && delayRenderRetries !== void 0 ? delayRenderRetries : undefined,
timeoutInMilliseconds: delayRenderTimeoutInMilliseconds !== null && delayRenderTimeoutInMilliseconds !== void 0 ? delayRenderTimeoutInMilliseconds : undefined,
});
const execute = async () => {
try {
const res = await fetch(actualSrc, {
signal: controller.signal,
cache: 'no-store',
});
if (res.status !== 200) {
if (res.status === 500) {
const json = await res.json();
if (json.error) {
const cleanedUpErrorMessage = json.error.replace(/^Error: /, '');
throw new Error(cleanedUpErrorMessage);
}
}
throw new Error(`Server returned status ${res.status} while fetching ${actualSrc}`);
}
const blob = await res.blob();
const url = URL.createObjectURL(blob);
cleanup.push(() => URL.revokeObjectURL(url));
setImageSrc({
src: url,
handle: newHandle,
});
}
catch (err) {
// If component is unmounted, we should not throw
if (err.message.includes('aborted')) {
continueRender(newHandle);
return;
}
if (controller.signal.aborted) {
continueRender(newHandle);
return;
}
if (err.message.includes('Failed to fetch')) {
// eslint-disable-next-line no-ex-assign
err = new Error(`Failed to fetch ${actualSrc}. This could be caused by Chrome rejecting the request because the disk space is low. Consider increasing the disk size of your environment.`, { cause: err });
}
if (onError) {
onError(err);
}
else {
(0, cancel_render_js_1.cancelRender)(err);
}
}
};
execute();
cleanup.push(() => {
if (controller.signal.aborted) {
return;
}
controller.abort();
});
return () => {
cleanup.forEach((c) => c());
};
}, [
actualSrc,
delayRenderRetries,
delayRenderTimeoutInMilliseconds,
onError,
continueRender,
delayRender,
]);
const onErr = (0, react_1.useCallback)(() => {
if (onError) {
onError === null || onError === void 0 ? void 0 : onError(new Error('Failed to load image with src ' + imageSrc));
}
else {
(0, cancel_render_js_1.cancelRender)('Failed to load image with src ' + imageSrc);
}
}, [imageSrc, onError]);
const className = (0, react_1.useMemo)(() => {
return [default_css_js_1.OBJECTFIT_CONTAIN_CLASS_NAME, props.className]
.filter(truthy_js_1.truthy)
.join(' ');
}, [props.className]);
const onImageFrame = (0, react_1.useCallback)((img) => {
if (onVideoFrame) {
onVideoFrame(img);
}
}, [onVideoFrame]);
if (!imageSrc || !window.remotion_videoEnabled) {
return null;
}
continueRender(imageSrc.handle);
return ((0, jsx_runtime_1.jsx)(Img_js_1.Img, { src: imageSrc.src, delayRenderRetries: delayRenderRetries, delayRenderTimeoutInMilliseconds: delayRenderTimeoutInMilliseconds, onImageFrame: onImageFrame, ...props, onError: onErr, className: className }));
};
exports.OffthreadVideoForRendering = OffthreadVideoForRendering;
@@ -0,0 +1,53 @@
import React from 'react';
/**
* @description Wraps the native `<video>` element to include video in your component that is synchronized with Remotion's time.
* @see [Documentation](https://www.remotion.dev/docs/html5-video)
*/
export declare const Html5Video: React.ForwardRefExoticComponent<Omit<import("./props").NativeVideoProps & {
name?: string;
volume?: import("../volume-prop.js").VolumeProp;
playbackRate?: number;
acceptableTimeShiftInSeconds?: number;
allowAmplificationDuringRender?: boolean;
useWebAudioApi?: boolean;
toneFrequency?: number;
pauseWhenBuffering?: boolean;
showInTimeline?: boolean;
delayRenderTimeoutInMilliseconds?: number;
loopVolumeCurveBehavior?: import("../index.js").LoopVolumeCurveBehavior;
delayRenderRetries?: number;
onError?: (err: Error) => void;
onAutoPlayError?: null | (() => void);
audioStreamIndex?: number;
} & Partial<import("./props").CommonVideoProps> & {
/**
* @deprecated For internal use only
*/
readonly stack?: string;
}, "ref"> & React.RefAttributes<HTMLVideoElement>>;
/**
* @deprecated This component has been renamed to `Html5Video`.
* @see [Documentation](https://www.remotion.dev/docs/html5-video)
*/
export declare const Video: React.ForwardRefExoticComponent<Omit<import("./props").NativeVideoProps & {
name?: string;
volume?: import("../volume-prop.js").VolumeProp;
playbackRate?: number;
acceptableTimeShiftInSeconds?: number;
allowAmplificationDuringRender?: boolean;
useWebAudioApi?: boolean;
toneFrequency?: number;
pauseWhenBuffering?: boolean;
showInTimeline?: boolean;
delayRenderTimeoutInMilliseconds?: number;
loopVolumeCurveBehavior?: import("../index.js").LoopVolumeCurveBehavior;
delayRenderRetries?: number;
onError?: (err: Error) => void;
onAutoPlayError?: null | (() => void);
audioStreamIndex?: number;
} & Partial<import("./props").CommonVideoProps> & {
/**
* @deprecated For internal use only
*/
readonly stack?: string;
}, "ref"> & React.RefAttributes<HTMLVideoElement>>;
@@ -0,0 +1,85 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Video = exports.Html5Video = void 0;
const jsx_runtime_1 = require("react/jsx-runtime");
/* eslint-disable @typescript-eslint/no-use-before-define */
const react_1 = require("react");
const Sequence_js_1 = require("../Sequence.js");
const absolute_src_js_1 = require("../absolute-src.js");
const calculate_media_duration_js_1 = require("../calculate-media-duration.js");
const enable_sequence_stack_traces_js_1 = require("../enable-sequence-stack-traces.js");
const index_js_1 = require("../loop/index.js");
const prefetch_js_1 = require("../prefetch.js");
const use_remotion_environment_js_1 = require("../use-remotion-environment.js");
const use_video_config_js_1 = require("../use-video-config.js");
const validate_media_props_js_1 = require("../validate-media-props.js");
const validate_start_from_props_js_1 = require("../validate-start-from-props.js");
const VideoForPreview_js_1 = require("./VideoForPreview.js");
const VideoForRendering_js_1 = require("./VideoForRendering.js");
const duration_state_js_1 = require("./duration-state.js");
const VideoForwardingFunction = (props, ref) => {
var _a, _b, _c;
const { startFrom, endAt, trimBefore, trimAfter, name, pauseWhenBuffering, stack, _remotionInternalNativeLoopPassed, showInTimeline, onAutoPlayError, ...otherProps } = props;
const { loop, ...propsOtherThanLoop } = props;
const { fps } = (0, use_video_config_js_1.useVideoConfig)();
const environment = (0, use_remotion_environment_js_1.useRemotionEnvironment)();
if (environment.isClientSideRendering) {
throw new Error('<Html5Video> is not supported in @remotion/web-renderer. Use <Video> from @remotion/media instead. See https://remotion.dev/docs/client-side-rendering/limitations');
}
const { durations, setDurations } = (0, react_1.useContext)(duration_state_js_1.DurationsContext);
if (typeof ref === 'string') {
throw new Error('string refs are not supported');
}
if (typeof props.src !== 'string') {
throw new TypeError(`The \`<Html5Video>\` tag requires a string for \`src\`, but got ${JSON.stringify(props.src)} instead.`);
}
const preloadedSrc = (0, prefetch_js_1.usePreload)(props.src);
const onDuration = (0, react_1.useCallback)((src, durationInSeconds) => {
setDurations({ type: 'got-duration', durationInSeconds, src });
}, [setDurations]);
const onVideoFrame = (0, react_1.useCallback)(() => { }, []);
const durationFetched = (_a = durations[(0, absolute_src_js_1.getAbsoluteSrc)(preloadedSrc)]) !== null && _a !== void 0 ? _a : durations[(0, absolute_src_js_1.getAbsoluteSrc)(props.src)];
(0, validate_start_from_props_js_1.validateMediaTrimProps)({ startFrom, endAt, trimBefore, trimAfter });
const { trimBeforeValue, trimAfterValue } = (0, validate_start_from_props_js_1.resolveTrimProps)({
startFrom,
endAt,
trimBefore,
trimAfter,
});
if (loop && durationFetched !== undefined) {
if (!Number.isFinite(durationFetched)) {
return ((0, jsx_runtime_1.jsx)(exports.Html5Video, { ...propsOtherThanLoop, ref: ref, stack: stack, _remotionInternalNativeLoopPassed: true }));
}
const mediaDuration = durationFetched * fps;
return ((0, jsx_runtime_1.jsx)(index_js_1.Loop, { durationInFrames: (0, calculate_media_duration_js_1.calculateMediaDuration)({
trimAfter: trimAfterValue,
mediaDurationInFrames: mediaDuration,
playbackRate: (_b = props.playbackRate) !== null && _b !== void 0 ? _b : 1,
trimBefore: trimBeforeValue,
}), layout: "none", name: name, children: (0, jsx_runtime_1.jsx)(exports.Html5Video, { ...propsOtherThanLoop, ref: ref, stack: stack, _remotionInternalNativeLoopPassed: true }) }));
}
if (typeof trimBeforeValue !== 'undefined' ||
typeof trimAfterValue !== 'undefined') {
return ((0, jsx_runtime_1.jsx)(Sequence_js_1.Sequence, { layout: "none", from: 0 - (trimBeforeValue !== null && trimBeforeValue !== void 0 ? trimBeforeValue : 0), showInTimeline: false, durationInFrames: trimAfterValue === undefined
? undefined
: trimAfterValue / ((_c = props.playbackRate) !== null && _c !== void 0 ? _c : 1), name: name, children: (0, jsx_runtime_1.jsx)(exports.Html5Video, { pauseWhenBuffering: pauseWhenBuffering !== null && pauseWhenBuffering !== void 0 ? pauseWhenBuffering : false, ...otherProps, ref: ref, stack: stack }) }));
}
(0, validate_media_props_js_1.validateMediaProps)({ playbackRate: props.playbackRate, volume: props.volume }, 'Html5Video');
if (environment.isRendering) {
return ((0, jsx_runtime_1.jsx)(VideoForRendering_js_1.VideoForRendering, { onDuration: onDuration, onVideoFrame: onVideoFrame !== null && onVideoFrame !== void 0 ? onVideoFrame : null, ...otherProps, ref: ref }));
}
return ((0, jsx_runtime_1.jsx)(VideoForPreview_js_1.VideoForPreview, { onlyWarnForMediaSeekingError: false, ...otherProps, ref: ref, onVideoFrame: null,
// Proposal: Make this default to true in v5
pauseWhenBuffering: pauseWhenBuffering !== null && pauseWhenBuffering !== void 0 ? pauseWhenBuffering : false, onDuration: onDuration, _remotionInternalStack: stack !== null && stack !== void 0 ? stack : null, _remotionInternalNativeLoopPassed: _remotionInternalNativeLoopPassed !== null && _remotionInternalNativeLoopPassed !== void 0 ? _remotionInternalNativeLoopPassed : false, showInTimeline: showInTimeline !== null && showInTimeline !== void 0 ? showInTimeline : true, onAutoPlayError: onAutoPlayError !== null && onAutoPlayError !== void 0 ? onAutoPlayError : undefined }));
};
/**
* @description Wraps the native `<video>` element to include video in your component that is synchronized with Remotion's time.
* @see [Documentation](https://www.remotion.dev/docs/html5-video)
*/
exports.Html5Video = (0, react_1.forwardRef)(VideoForwardingFunction);
(0, enable_sequence_stack_traces_js_1.addSequenceStackTraces)(exports.Html5Video);
/**
* @deprecated This component has been renamed to `Html5Video`.
* @see [Documentation](https://www.remotion.dev/docs/html5-video)
*/
exports.Video = exports.Html5Video;
@@ -0,0 +1,14 @@
import React from 'react';
import type { OnVideoFrame, RemotionVideoProps } from './props';
type VideoForPreviewProps = RemotionVideoProps & {
readonly onlyWarnForMediaSeekingError: boolean;
readonly onDuration: (src: string, durationInSeconds: number) => void;
readonly pauseWhenBuffering: boolean;
readonly _remotionInternalNativeLoopPassed: boolean;
readonly _remotionInternalStack: string | null;
readonly showInTimeline: boolean;
readonly onVideoFrame: null | OnVideoFrame;
readonly crossOrigin?: '' | 'anonymous' | 'use-credentials';
};
export declare const VideoForPreview: React.ForwardRefExoticComponent<Omit<VideoForPreviewProps, "ref"> & React.RefAttributes<HTMLVideoElement>>;
export {};
@@ -0,0 +1,269 @@
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.VideoForPreview = void 0;
const jsx_runtime_1 = require("react/jsx-runtime");
const react_1 = __importStar(require("react"));
const SequenceContext_js_1 = require("../SequenceContext.js");
const SequenceManager_js_1 = require("../SequenceManager.js");
const shared_audio_tags_js_1 = require("../audio/shared-audio-tags.js");
const shared_element_source_node_js_1 = require("../audio/shared-element-source-node.js");
const use_audio_frame_js_1 = require("../audio/use-audio-frame.js");
const get_cross_origin_value_js_1 = require("../get-cross-origin-value.js");
const log_level_context_js_1 = require("../log-level-context.js");
const playback_logging_js_1 = require("../playback-logging.js");
const prefetch_js_1 = require("../prefetch.js");
const use_amplification_js_1 = require("../use-amplification.js");
const use_media_in_timeline_js_1 = require("../use-media-in-timeline.js");
const use_media_playback_js_1 = require("../use-media-playback.js");
const use_media_tag_js_1 = require("../use-media-tag.js");
const use_video_config_js_1 = require("../use-video-config.js");
const version_js_1 = require("../version.js");
const volume_position_state_js_1 = require("../volume-position-state.js");
const volume_prop_js_1 = require("../volume-prop.js");
const volume_safeguard_js_1 = require("../volume-safeguard.js");
const emit_video_frame_js_1 = require("./emit-video-frame.js");
const video_fragment_js_1 = require("./video-fragment.js");
const VideoForDevelopmentRefForwardingFunction = (props, ref) => {
var _a, _b, _c, _d, _e, _f;
const context = (0, react_1.useContext)(shared_audio_tags_js_1.SharedAudioContext);
if (!context) {
throw new Error('SharedAudioContext not found');
}
const videoRef = (0, react_1.useRef)(null);
const sharedSource = (0, react_1.useMemo)(() => {
if (!context.audioContext) {
return null;
}
return (0, shared_element_source_node_js_1.makeSharedElementSourceNode)({
audioContext: context.audioContext,
ref: videoRef,
});
}, [context.audioContext]);
/**
* Effects in React 18 fire twice, and we are looking for a way to only fire it once.
* - useInsertionEffect only fires once. If it's available we are in React 18.
* - useLayoutEffect only fires once in React 17.
*
* Need to import it from React to fix React 17 ESM support.
*/
const effectToUse = (_a = react_1.default.useInsertionEffect) !== null && _a !== void 0 ? _a : react_1.default.useLayoutEffect;
// Disconnecting the SharedElementSourceNodes if the tag unmounts to prevent leak.
// https://github.com/remotion-dev/remotion/issues/6285
// But useInsertionEffect will fire before other effects, meaning the
// nodes might still be used. Using rAF to ensure it's after other effects.
effectToUse(() => {
return () => {
requestAnimationFrame(() => {
sharedSource === null || sharedSource === void 0 ? void 0 : sharedSource.cleanup();
});
};
}, [sharedSource]);
const { volume, muted, playbackRate, onlyWarnForMediaSeekingError, src, onDuration,
// @ts-expect-error
acceptableTimeShift, acceptableTimeShiftInSeconds, toneFrequency, name, _remotionInternalNativeLoopPassed, _remotionInternalStack, style, pauseWhenBuffering, showInTimeline, loopVolumeCurveBehavior, onError, onAutoPlayError, onVideoFrame, crossOrigin, delayRenderRetries, delayRenderTimeoutInMilliseconds, allowAmplificationDuringRender, useWebAudioApi, audioStreamIndex, ...nativeProps } = props;
const _propsValid = true;
if (!_propsValid) {
throw new Error('typecheck error');
}
const volumePropFrame = (0, use_audio_frame_js_1.useFrameForVolumeProp)(loopVolumeCurveBehavior !== null && loopVolumeCurveBehavior !== void 0 ? loopVolumeCurveBehavior : 'repeat');
const { fps, durationInFrames } = (0, use_video_config_js_1.useVideoConfig)();
const parentSequence = (0, react_1.useContext)(SequenceContext_js_1.SequenceContext);
const { hidden } = (0, react_1.useContext)(SequenceManager_js_1.SequenceVisibilityToggleContext);
const logLevel = (0, log_level_context_js_1.useLogLevel)();
const mountTime = (0, log_level_context_js_1.useMountTime)();
const [timelineId] = (0, react_1.useState)(() => String(Math.random()));
const isSequenceHidden = (_b = hidden[timelineId]) !== null && _b !== void 0 ? _b : false;
if (typeof acceptableTimeShift !== 'undefined') {
throw new Error('acceptableTimeShift has been removed. Use acceptableTimeShiftInSeconds instead.');
}
const [mediaVolume] = (0, volume_position_state_js_1.useMediaVolumeState)();
const [mediaMuted] = (0, volume_position_state_js_1.useMediaMutedState)();
const userPreferredVolume = (0, volume_prop_js_1.evaluateVolume)({
frame: volumePropFrame,
volume,
mediaVolume,
});
(0, volume_safeguard_js_1.warnAboutTooHighVolume)(userPreferredVolume);
(0, use_media_in_timeline_js_1.useMediaInTimeline)({
volume,
mediaVolume,
mediaType: 'video',
src,
playbackRate: (_c = props.playbackRate) !== null && _c !== void 0 ? _c : 1,
displayName: name !== null && name !== void 0 ? name : null,
id: timelineId,
stack: _remotionInternalStack,
showInTimeline,
premountDisplay: (_d = parentSequence === null || parentSequence === void 0 ? void 0 : parentSequence.premountDisplay) !== null && _d !== void 0 ? _d : null,
postmountDisplay: (_e = parentSequence === null || parentSequence === void 0 ? void 0 : parentSequence.postmountDisplay) !== null && _e !== void 0 ? _e : null,
loopDisplay: undefined,
});
// putting playback before useVolume
// because volume looks at playbackrate
(0, use_media_playback_js_1.useMediaPlayback)({
mediaRef: videoRef,
src,
mediaType: 'video',
playbackRate: (_f = props.playbackRate) !== null && _f !== void 0 ? _f : 1,
onlyWarnForMediaSeekingError,
acceptableTimeshift: acceptableTimeShiftInSeconds !== null && acceptableTimeShiftInSeconds !== void 0 ? acceptableTimeShiftInSeconds : null,
isPremounting: Boolean(parentSequence === null || parentSequence === void 0 ? void 0 : parentSequence.premounting),
isPostmounting: Boolean(parentSequence === null || parentSequence === void 0 ? void 0 : parentSequence.postmounting),
pauseWhenBuffering,
onAutoPlayError: onAutoPlayError !== null && onAutoPlayError !== void 0 ? onAutoPlayError : null,
});
(0, use_media_tag_js_1.useMediaTag)({
id: timelineId,
isPostmounting: Boolean(parentSequence === null || parentSequence === void 0 ? void 0 : parentSequence.postmounting),
isPremounting: Boolean(parentSequence === null || parentSequence === void 0 ? void 0 : parentSequence.premounting),
mediaRef: videoRef,
mediaType: 'video',
onAutoPlayError: onAutoPlayError !== null && onAutoPlayError !== void 0 ? onAutoPlayError : null,
});
(0, use_amplification_js_1.useVolume)({
logLevel,
mediaRef: videoRef,
volume: userPreferredVolume,
source: sharedSource,
shouldUseWebAudioApi: useWebAudioApi !== null && useWebAudioApi !== void 0 ? useWebAudioApi : false,
});
const actualFrom = parentSequence ? parentSequence.relativeFrom : 0;
const duration = parentSequence
? Math.min(parentSequence.durationInFrames, durationInFrames)
: durationInFrames;
const preloadedSrc = (0, prefetch_js_1.usePreload)(src);
const actualSrc = (0, video_fragment_js_1.useAppendVideoFragment)({
actualSrc: preloadedSrc,
actualFrom,
duration,
fps,
});
(0, react_1.useImperativeHandle)(ref, () => {
return videoRef.current;
}, []);
(0, react_1.useState)(() => (0, playback_logging_js_1.playbackLogging)({
logLevel,
message: `Mounting video with source = ${actualSrc}, v=${version_js_1.VERSION}, user agent=${typeof navigator === 'undefined' ? 'server' : navigator.userAgent}`,
tag: 'video',
mountTime,
}));
(0, react_1.useEffect)(() => {
const { current } = videoRef;
if (!current) {
return;
}
const errorHandler = () => {
var _a;
if (current.error) {
// eslint-disable-next-line no-console
console.error('Error occurred in video', current === null || current === void 0 ? void 0 : current.error);
// If user is handling the error, we don't cause an unhandled exception
if (onError) {
const err = new Error(`Code ${current.error.code}: ${current.error.message}`);
onError(err);
return;
}
throw new Error(`The browser threw an error while playing the video ${src}: Code ${current.error.code} - ${(_a = current === null || current === void 0 ? void 0 : current.error) === null || _a === void 0 ? void 0 : _a.message}. See https://remotion.dev/docs/media-playback-error for help. Pass an onError() prop to handle the error.`);
}
else {
// If user is handling the error, we don't cause an unhandled exception
if (onError) {
const err = new Error(`The browser threw an error while playing the video ${src}`);
onError(err);
return;
}
throw new Error('The browser threw an error while playing the video');
}
};
current.addEventListener('error', errorHandler, { once: true });
return () => {
current.removeEventListener('error', errorHandler);
};
}, [onError, src]);
const currentOnDurationCallback = (0, react_1.useRef)(onDuration);
currentOnDurationCallback.current = onDuration;
(0, emit_video_frame_js_1.useEmitVideoFrame)({ ref: videoRef, onVideoFrame });
(0, react_1.useEffect)(() => {
var _a;
const { current } = videoRef;
if (!current) {
return;
}
if (current.duration) {
(_a = currentOnDurationCallback.current) === null || _a === void 0 ? void 0 : _a.call(currentOnDurationCallback, src, current.duration);
return;
}
const onLoadedMetadata = () => {
var _a;
(_a = currentOnDurationCallback.current) === null || _a === void 0 ? void 0 : _a.call(currentOnDurationCallback, src, current.duration);
};
current.addEventListener('loadedmetadata', onLoadedMetadata);
return () => {
current.removeEventListener('loadedmetadata', onLoadedMetadata);
};
}, [src]);
(0, react_1.useEffect)(() => {
const { current } = videoRef;
if (!current) {
return;
}
// Without this, on iOS Safari, the video cannot be seeked.
// if a seek is triggered before `loadedmetadata` is fired,
// the video will not seek, even if `loadedmetadata` is fired afterwards.
// Also, this needs to happen in a useEffect, because otherwise
// the SSR props will be applied.
if ((0, video_fragment_js_1.isIosSafari)()) {
current.preload = 'metadata';
}
else {
current.preload = 'auto';
}
}, []);
const actualStyle = (0, react_1.useMemo)(() => {
var _a;
return {
...style,
opacity: isSequenceHidden ? 0 : ((_a = style === null || style === void 0 ? void 0 : style.opacity) !== null && _a !== void 0 ? _a : 1),
};
}, [isSequenceHidden, style]);
const crossOriginValue = (0, get_cross_origin_value_js_1.getCrossOriginValue)({
crossOrigin,
requestsVideoFrame: Boolean(onVideoFrame),
isClientSideRendering: false,
});
return ((0, jsx_runtime_1.jsx)("video", { ref: videoRef, muted: muted || mediaMuted || isSequenceHidden || userPreferredVolume <= 0, playsInline: true, src: actualSrc, loop: _remotionInternalNativeLoopPassed, style: actualStyle, disableRemotePlayback: true, crossOrigin: crossOriginValue, ...nativeProps }));
};
exports.VideoForPreview = (0, react_1.forwardRef)(VideoForDevelopmentRefForwardingFunction);
@@ -0,0 +1,8 @@
import type { ForwardRefExoticComponent, RefAttributes } from 'react';
import type { OnVideoFrame, RemotionVideoProps } from './props';
type VideoForRenderingProps = RemotionVideoProps & {
readonly onDuration: (src: string, durationInSeconds: number) => void;
readonly onVideoFrame: null | OnVideoFrame;
};
export declare const VideoForRendering: ForwardRefExoticComponent<VideoForRenderingProps & RefAttributes<HTMLVideoElement>>;
export {};
@@ -0,0 +1,230 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.VideoForRendering = void 0;
const jsx_runtime_1 = require("react/jsx-runtime");
const react_1 = require("react");
const RenderAssetManager_js_1 = require("../RenderAssetManager.js");
const SequenceContext_js_1 = require("../SequenceContext.js");
const absolute_src_js_1 = require("../absolute-src.js");
const use_audio_frame_js_1 = require("../audio/use-audio-frame.js");
const is_approximately_the_same_js_1 = require("../is-approximately-the-same.js");
const log_level_context_js_1 = require("../log-level-context.js");
const random_js_1 = require("../random.js");
const timeline_position_state_js_1 = require("../timeline-position-state.js");
const use_current_frame_js_1 = require("../use-current-frame.js");
const use_delay_render_js_1 = require("../use-delay-render.js");
const use_remotion_environment_js_1 = require("../use-remotion-environment.js");
const use_unsafe_video_config_js_1 = require("../use-unsafe-video-config.js");
const volume_prop_js_1 = require("../volume-prop.js");
const volume_safeguard_js_1 = require("../volume-safeguard.js");
const get_current_time_js_1 = require("./get-current-time.js");
const seek_until_right_js_1 = require("./seek-until-right.js");
const VideoForRenderingForwardFunction = ({ onError, volume: volumeProp, allowAmplificationDuringRender, playbackRate, onDuration, toneFrequency, name, acceptableTimeShiftInSeconds, delayRenderRetries, delayRenderTimeoutInMilliseconds, loopVolumeCurveBehavior, audioStreamIndex, onVideoFrame, ...props }, ref) => {
const absoluteFrame = (0, timeline_position_state_js_1.useTimelinePosition)();
const frame = (0, use_current_frame_js_1.useCurrentFrame)();
const volumePropsFrame = (0, use_audio_frame_js_1.useFrameForVolumeProp)(loopVolumeCurveBehavior !== null && loopVolumeCurveBehavior !== void 0 ? loopVolumeCurveBehavior : 'repeat');
const videoConfig = (0, use_unsafe_video_config_js_1.useUnsafeVideoConfig)();
const videoRef = (0, react_1.useRef)(null);
const sequenceContext = (0, react_1.useContext)(SequenceContext_js_1.SequenceContext);
const mediaStartsAt = (0, use_audio_frame_js_1.useMediaStartsAt)();
const environment = (0, use_remotion_environment_js_1.useRemotionEnvironment)();
const logLevel = (0, log_level_context_js_1.useLogLevel)();
const mountTime = (0, log_level_context_js_1.useMountTime)();
const { delayRender, continueRender } = (0, use_delay_render_js_1.useDelayRender)();
const { registerRenderAsset, unregisterRenderAsset } = (0, react_1.useContext)(RenderAssetManager_js_1.RenderAssetManager);
// Generate a string that's as unique as possible for this asset
// but at the same time the same on all threads
const id = (0, react_1.useMemo)(() => {
var _a;
return `video-${(0, random_js_1.random)((_a = props.src) !== null && _a !== void 0 ? _a : '')}-${sequenceContext === null || sequenceContext === void 0 ? void 0 : sequenceContext.cumulatedFrom}-${sequenceContext === null || sequenceContext === void 0 ? void 0 : sequenceContext.relativeFrom}-${sequenceContext === null || sequenceContext === void 0 ? void 0 : sequenceContext.durationInFrames}`;
}, [
props.src,
sequenceContext === null || sequenceContext === void 0 ? void 0 : sequenceContext.cumulatedFrom,
sequenceContext === null || sequenceContext === void 0 ? void 0 : sequenceContext.relativeFrom,
sequenceContext === null || sequenceContext === void 0 ? void 0 : sequenceContext.durationInFrames,
]);
if (!videoConfig) {
throw new Error('No video config found');
}
const volume = (0, volume_prop_js_1.evaluateVolume)({
volume: volumeProp,
frame: volumePropsFrame,
mediaVolume: 1,
});
(0, volume_safeguard_js_1.warnAboutTooHighVolume)(volume);
(0, react_1.useEffect)(() => {
var _a;
if (!props.src) {
throw new Error('No src passed');
}
if (props.muted) {
return;
}
if (volume <= 0) {
return;
}
if (!window.remotion_audioEnabled) {
return;
}
registerRenderAsset({
type: 'video',
src: (0, absolute_src_js_1.getAbsoluteSrc)(props.src),
id,
frame: absoluteFrame,
volume,
mediaFrame: frame,
playbackRate: playbackRate !== null && playbackRate !== void 0 ? playbackRate : 1,
toneFrequency: toneFrequency !== null && toneFrequency !== void 0 ? toneFrequency : 1,
audioStartFrame: Math.max(0, -((_a = sequenceContext === null || sequenceContext === void 0 ? void 0 : sequenceContext.relativeFrom) !== null && _a !== void 0 ? _a : 0)),
audioStreamIndex: audioStreamIndex !== null && audioStreamIndex !== void 0 ? audioStreamIndex : 0,
});
return () => unregisterRenderAsset(id);
}, [
props.muted,
props.src,
registerRenderAsset,
id,
unregisterRenderAsset,
volume,
frame,
absoluteFrame,
playbackRate,
toneFrequency,
sequenceContext === null || sequenceContext === void 0 ? void 0 : sequenceContext.relativeFrom,
audioStreamIndex,
]);
(0, react_1.useImperativeHandle)(ref, () => {
return videoRef.current;
}, []);
(0, react_1.useEffect)(() => {
var _a, _b;
if (!window.remotion_videoEnabled) {
return;
}
const { current } = videoRef;
if (!current) {
return;
}
const currentTime = (0, get_current_time_js_1.getMediaTime)({
frame,
playbackRate: playbackRate || 1,
startFrom: -mediaStartsAt,
fps: videoConfig.fps,
});
const handle = delayRender(`Rendering <Html5Video /> with src="${props.src}" at time ${currentTime}`, {
retries: delayRenderRetries !== null && delayRenderRetries !== void 0 ? delayRenderRetries : undefined,
timeoutInMilliseconds: delayRenderTimeoutInMilliseconds !== null && delayRenderTimeoutInMilliseconds !== void 0 ? delayRenderTimeoutInMilliseconds : undefined,
});
if (((_b = (_a = window.process) === null || _a === void 0 ? void 0 : _a.env) === null || _b === void 0 ? void 0 : _b.NODE_ENV) === 'test') {
continueRender(handle);
return;
}
if ((0, is_approximately_the_same_js_1.isApproximatelyTheSame)(current.currentTime, currentTime)) {
if (current.readyState >= 2) {
continueRender(handle);
return;
}
const loadedDataHandler = () => {
continueRender(handle);
};
current.addEventListener('loadeddata', loadedDataHandler, { once: true });
return () => {
current.removeEventListener('loadeddata', loadedDataHandler);
};
}
const endedHandler = () => {
continueRender(handle);
};
const seek = (0, seek_until_right_js_1.seekToTimeMultipleUntilRight)({
element: current,
desiredTime: currentTime,
fps: videoConfig.fps,
logLevel,
mountTime,
});
seek.prom.then(() => {
continueRender(handle);
});
current.addEventListener('ended', endedHandler, { once: true });
const errorHandler = () => {
var _a;
if (current === null || current === void 0 ? void 0 : current.error) {
// eslint-disable-next-line no-console
console.error('Error occurred in video', current === null || current === void 0 ? void 0 : current.error);
// If user is handling the error, we don't cause an unhandled exception
if (onError) {
return;
}
throw new Error(`The browser threw an error while playing the video ${props.src}: Code ${current.error.code} - ${(_a = current === null || current === void 0 ? void 0 : current.error) === null || _a === void 0 ? void 0 : _a.message}. See https://remotion.dev/docs/media-playback-error for help. Pass an onError() prop to handle the error.`);
}
else {
throw new Error('The browser threw an error');
}
};
current.addEventListener('error', errorHandler, { once: true });
// If video skips to another frame or unmounts, we clear the created handle
return () => {
seek.cancel();
current.removeEventListener('ended', endedHandler);
current.removeEventListener('error', errorHandler);
continueRender(handle);
};
}, [
volumePropsFrame,
props.src,
playbackRate,
videoConfig.fps,
frame,
mediaStartsAt,
onError,
delayRenderRetries,
delayRenderTimeoutInMilliseconds,
logLevel,
mountTime,
continueRender,
delayRender,
]);
const { src } = props;
// If video source switches, make new handle
if (environment.isRendering) {
// eslint-disable-next-line react-hooks/rules-of-hooks
(0, react_1.useLayoutEffect)(() => {
var _a, _b;
if (((_b = (_a = window.process) === null || _a === void 0 ? void 0 : _a.env) === null || _b === void 0 ? void 0 : _b.NODE_ENV) === 'test') {
return;
}
const newHandle = delayRender('Loading <Html5Video> duration with src=' + src, {
retries: delayRenderRetries !== null && delayRenderRetries !== void 0 ? delayRenderRetries : undefined,
timeoutInMilliseconds: delayRenderTimeoutInMilliseconds !== null && delayRenderTimeoutInMilliseconds !== void 0 ? delayRenderTimeoutInMilliseconds : undefined,
});
const { current } = videoRef;
const didLoad = () => {
if (current === null || current === void 0 ? void 0 : current.duration) {
onDuration(src, current.duration);
}
continueRender(newHandle);
};
if (current === null || current === void 0 ? void 0 : current.duration) {
onDuration(src, current.duration);
continueRender(newHandle);
}
else {
current === null || current === void 0 ? void 0 : current.addEventListener('loadedmetadata', didLoad, { once: true });
}
// If tag gets unmounted, clear pending handles because video metadata is not going to load
return () => {
current === null || current === void 0 ? void 0 : current.removeEventListener('loadedmetadata', didLoad);
continueRender(newHandle);
};
}, [
src,
onDuration,
delayRenderRetries,
delayRenderTimeoutInMilliseconds,
continueRender,
delayRender,
]);
}
return (0, jsx_runtime_1.jsx)("video", { ref: videoRef, disableRemotePlayback: true, ...props });
};
exports.VideoForRendering = (0, react_1.forwardRef)(VideoForRenderingForwardFunction);
@@ -0,0 +1,17 @@
import React from 'react';
type DurationState = Record<string, number>;
type DurationAction = {
type: 'got-duration';
src: string;
durationInSeconds: number;
};
export declare const durationReducer: (state: DurationState, action: DurationAction) => DurationState;
type TDurationsContext = {
durations: DurationState;
setDurations: React.Dispatch<DurationAction>;
};
export declare const DurationsContext: React.Context<TDurationsContext>;
export declare const DurationsContextProvider: React.FC<{
readonly children: React.ReactNode;
}>;
export {};
@@ -0,0 +1,40 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.DurationsContextProvider = exports.DurationsContext = exports.durationReducer = void 0;
const jsx_runtime_1 = require("react/jsx-runtime");
const react_1 = require("react");
const absolute_src_js_1 = require("../absolute-src.js");
const durationReducer = (state, action) => {
switch (action.type) {
case 'got-duration': {
const absoluteSrc = (0, absolute_src_js_1.getAbsoluteSrc)(action.src);
if (state[absoluteSrc] === action.durationInSeconds) {
return state;
}
return {
...state,
[absoluteSrc]: action.durationInSeconds,
};
}
default:
return state;
}
};
exports.durationReducer = durationReducer;
exports.DurationsContext = (0, react_1.createContext)({
durations: {},
setDurations: () => {
throw new Error('context missing');
},
});
const DurationsContextProvider = ({ children }) => {
const [durations, setDurations] = (0, react_1.useReducer)(exports.durationReducer, {});
const value = (0, react_1.useMemo)(() => {
return {
durations,
setDurations,
};
}, [durations]);
return ((0, jsx_runtime_1.jsx)(exports.DurationsContext.Provider, { value: value, children: children }));
};
exports.DurationsContextProvider = DurationsContextProvider;
@@ -0,0 +1,5 @@
import type { OnVideoFrame } from './props';
export declare const useEmitVideoFrame: ({ ref, onVideoFrame, }: {
ref: React.RefObject<HTMLVideoElement | null>;
onVideoFrame: OnVideoFrame | null;
}) => void;
@@ -0,0 +1,28 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.useEmitVideoFrame = void 0;
const react_1 = require("react");
const useEmitVideoFrame = ({ ref, onVideoFrame, }) => {
(0, react_1.useEffect)(() => {
const { current } = ref;
if (!current) {
return;
}
if (!onVideoFrame) {
return;
}
let handle = 0;
const callback = () => {
if (!ref.current) {
return;
}
onVideoFrame(ref.current);
handle = ref.current.requestVideoFrameCallback(callback);
};
callback();
return () => {
current.cancelVideoFrameCallback(handle);
};
}, [onVideoFrame, ref]);
};
exports.useEmitVideoFrame = useEmitVideoFrame;
@@ -0,0 +1,11 @@
export declare const getExpectedMediaFrameUncorrected: ({ frame, playbackRate, startFrom, }: {
frame: number;
playbackRate: number;
startFrom: number;
}) => number;
export declare const getMediaTime: ({ fps, frame, playbackRate, startFrom, }: {
fps: number;
frame: number;
playbackRate: number;
startFrom: number;
}) => number;
@@ -0,0 +1,19 @@
"use strict";
// Calculate the `.currentTime` of a video or audio element
Object.defineProperty(exports, "__esModule", { value: true });
exports.getMediaTime = exports.getExpectedMediaFrameUncorrected = void 0;
const interpolate_js_1 = require("../interpolate.js");
const getExpectedMediaFrameUncorrected = ({ frame, playbackRate, startFrom, }) => {
return (0, interpolate_js_1.interpolate)(frame, [-1, startFrom, startFrom + 1], [-1, startFrom, startFrom + playbackRate]);
};
exports.getExpectedMediaFrameUncorrected = getExpectedMediaFrameUncorrected;
const getMediaTime = ({ fps, frame, playbackRate, startFrom, }) => {
const expectedFrame = (0, exports.getExpectedMediaFrameUncorrected)({
frame,
playbackRate,
startFrom,
});
const msPerFrame = 1000 / fps;
return (expectedFrame * msPerFrame) / 1000;
};
exports.getMediaTime = getMediaTime;
@@ -0,0 +1,3 @@
export { OffthreadVideo } from './OffthreadVideo.js';
export type { RemotionOffthreadVideoProps as OffthreadVideoProps, RemotionMainVideoProps, RemotionOffthreadVideoProps, RemotionVideoProps, } from './props';
export { Html5Video, Video } from './Video.js';
@@ -0,0 +1,8 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Video = exports.Html5Video = exports.OffthreadVideo = void 0;
var OffthreadVideo_js_1 = require("./OffthreadVideo.js");
Object.defineProperty(exports, "OffthreadVideo", { enumerable: true, get: function () { return OffthreadVideo_js_1.OffthreadVideo; } });
var Video_js_1 = require("./Video.js");
Object.defineProperty(exports, "Html5Video", { enumerable: true, get: function () { return Video_js_1.Html5Video; } });
Object.defineProperty(exports, "Video", { enumerable: true, get: function () { return Video_js_1.Video; } });
@@ -0,0 +1,6 @@
export declare const getOffthreadVideoSource: ({ src, transparent, currentTime, toneMapped, }: {
src: string;
transparent: boolean;
currentTime: number;
toneMapped: boolean;
}) => string;
@@ -0,0 +1,8 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.getOffthreadVideoSource = void 0;
const absolute_src_1 = require("../absolute-src");
const getOffthreadVideoSource = ({ src, transparent, currentTime, toneMapped, }) => {
return `http://localhost:${window.remotion_proxyPort}/proxy?src=${encodeURIComponent((0, absolute_src_1.getAbsoluteSrc)(src))}&time=${encodeURIComponent(Math.max(0, currentTime))}&transparent=${String(transparent)}&toneMapped=${String(toneMapped)}`;
};
exports.getOffthreadVideoSource = getOffthreadVideoSource;
@@ -0,0 +1,86 @@
import type React from 'react';
import type { LoopVolumeCurveBehavior } from '../audio/use-audio-frame.js';
import type { VolumeProp } from '../volume-prop.js';
export type CommonVideoProps = {
/**
* @deprecated `startFrom` was renamed to `trimBefore`
*/
startFrom: number | undefined;
/**
* @deprecated `endAt` was renamed to `trimAfter`
*/
endAt: number | undefined;
/**
* Trim of the video from the left (start) in frames.
*/
trimBefore: number | undefined;
/**
* Trim of the video from the right (end) in frames.
*/
trimAfter: number | undefined;
/**
* @deprecated Only for internal `transparent` use
*/
_remotionInternalNativeLoopPassed: boolean;
};
export type RemotionMainVideoProps = Partial<CommonVideoProps>;
export type NativeVideoProps = Omit<React.DetailedHTMLProps<React.VideoHTMLAttributes<HTMLVideoElement>, HTMLVideoElement>, 'autoPlay' | 'controls' | 'onEnded' | 'nonce' | 'onError' | 'disableRemotePlayback'>;
export type RemotionVideoProps = NativeVideoProps & {
name?: string;
volume?: VolumeProp;
playbackRate?: number;
acceptableTimeShiftInSeconds?: number;
allowAmplificationDuringRender?: boolean;
useWebAudioApi?: boolean;
toneFrequency?: number;
pauseWhenBuffering?: boolean;
showInTimeline?: boolean;
delayRenderTimeoutInMilliseconds?: number;
loopVolumeCurveBehavior?: LoopVolumeCurveBehavior;
delayRenderRetries?: number;
onError?: (err: Error) => void;
onAutoPlayError?: null | (() => void);
audioStreamIndex?: number;
};
type DeprecatedOffthreadVideoProps = {
/**
* @deprecated Use the `transparent` prop instead
*/
imageFormat: never;
};
type MandatoryOffthreadVideoProps = {
src: string;
};
type OptionalOffthreadVideoProps = {
className: string | undefined;
name: string | undefined;
id: string | undefined;
style: React.CSSProperties | undefined;
volume: VolumeProp | undefined;
playbackRate: number;
muted: boolean;
onError: undefined | ((err: Error) => void);
acceptableTimeShiftInSeconds: undefined | number;
allowAmplificationDuringRender: boolean;
toneFrequency: number;
transparent: boolean;
toneMapped: boolean;
pauseWhenBuffering: boolean;
loopVolumeCurveBehavior: LoopVolumeCurveBehavior;
delayRenderTimeoutInMilliseconds: number | undefined;
delayRenderRetries: number | undefined;
useWebAudioApi: boolean;
/**
* @deprecated For internal use only
*/
stack: string | undefined;
showInTimeline: boolean;
onAutoPlayError: null | (() => void);
onVideoFrame: OnVideoFrame | undefined;
crossOrigin: '' | 'anonymous' | 'use-credentials' | undefined;
audioStreamIndex: number;
};
export type AllOffthreadVideoProps = MandatoryOffthreadVideoProps & OptionalOffthreadVideoProps & CommonVideoProps;
export type RemotionOffthreadVideoProps = MandatoryOffthreadVideoProps & Partial<OptionalOffthreadVideoProps> & Partial<CommonVideoProps> & Partial<DeprecatedOffthreadVideoProps>;
export type OnVideoFrame = (frame: CanvasImageSource) => void;
export {};
@@ -0,0 +1,2 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,20 @@
import { type LogLevel } from '../log';
export declare const seekToTime: ({ element, desiredTime, logLevel, mountTime, }: {
element: HTMLVideoElement;
desiredTime: number;
logLevel: LogLevel;
mountTime: number;
}) => {
wait: Promise<number>;
cancel: () => void;
};
export declare const seekToTimeMultipleUntilRight: ({ element, desiredTime, fps, logLevel, mountTime, }: {
element: HTMLVideoElement;
desiredTime: number;
fps: number;
logLevel: LogLevel;
mountTime: number;
}) => {
prom: Promise<void>;
cancel: () => void;
};
@@ -0,0 +1,122 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.seekToTimeMultipleUntilRight = exports.seekToTime = void 0;
const is_approximately_the_same_1 = require("../is-approximately-the-same");
const seek_1 = require("../seek");
const roundTo6Commas = (num) => {
return Math.round(num * 100000) / 100000;
};
const seekToTime = ({ element, desiredTime, logLevel, mountTime, }) => {
if ((0, is_approximately_the_same_1.isApproximatelyTheSame)(element.currentTime, desiredTime)) {
return {
wait: Promise.resolve(desiredTime),
cancel: () => { },
};
}
(0, seek_1.seek)({
logLevel,
mediaRef: element,
time: desiredTime,
why: 'Seeking during rendering',
mountTime,
});
let cancel;
let cancelSeeked = null;
const prom = new Promise((resolve) => {
cancel = element.requestVideoFrameCallback((now, metadata) => {
const displayIn = metadata.expectedDisplayTime - now;
if (displayIn <= 0) {
resolve(metadata.mediaTime);
return;
}
setTimeout(() => {
resolve(metadata.mediaTime);
}, displayIn + 150);
});
});
const waitForSeekedEvent = new Promise((resolve) => {
const onDone = () => {
resolve();
};
element.addEventListener('seeked', onDone, {
once: true,
});
cancelSeeked = () => {
element.removeEventListener('seeked', onDone);
};
});
return {
wait: Promise.all([prom, waitForSeekedEvent]).then(([time]) => time),
cancel: () => {
cancelSeeked === null || cancelSeeked === void 0 ? void 0 : cancelSeeked();
element.cancelVideoFrameCallback(cancel);
},
};
};
exports.seekToTime = seekToTime;
const seekToTimeMultipleUntilRight = ({ element, desiredTime, fps, logLevel, mountTime, }) => {
const threshold = 1 / fps / 2;
let currentCancel = () => undefined;
if (Number.isFinite(element.duration) &&
element.currentTime >= element.duration &&
desiredTime >= element.duration) {
return {
prom: Promise.resolve(),
cancel: () => { },
};
}
const prom = new Promise((resolve, reject) => {
const firstSeek = (0, exports.seekToTime)({
element,
desiredTime: desiredTime + threshold,
logLevel,
mountTime,
});
firstSeek.wait.then((seekedTo) => {
const difference = Math.abs(desiredTime - seekedTo);
if (difference <= threshold) {
return resolve();
}
const sign = desiredTime > seekedTo ? 1 : -1;
const newSeek = (0, exports.seekToTime)({
element,
desiredTime: seekedTo + threshold * sign,
logLevel,
mountTime,
});
currentCancel = newSeek.cancel;
newSeek.wait
.then((newTime) => {
const newDifference = Math.abs(desiredTime - newTime);
if (roundTo6Commas(newDifference) <= roundTo6Commas(threshold)) {
return resolve();
}
const thirdSeek = (0, exports.seekToTime)({
element,
desiredTime: desiredTime + threshold,
logLevel,
mountTime,
});
currentCancel = thirdSeek.cancel;
return thirdSeek.wait
.then(() => {
resolve();
})
.catch((err) => {
reject(err);
});
})
.catch((err) => {
reject(err);
});
});
currentCancel = firstSeek.cancel;
});
return {
prom,
cancel: () => {
currentCancel();
},
};
};
exports.seekToTimeMultipleUntilRight = seekToTimeMultipleUntilRight;
@@ -0,0 +1,14 @@
export declare const isSafari: () => boolean;
export declare const isIosSafari: () => boolean;
export declare const appendVideoFragment: ({ actualSrc, actualFrom, duration, fps, }: {
actualSrc: string;
actualFrom: number;
duration: number;
fps: number;
}) => string;
export declare const useAppendVideoFragment: ({ actualSrc: initialActualSrc, actualFrom: initialActualFrom, duration: initialDuration, fps, }: {
actualSrc: string;
actualFrom: number;
duration: number;
fps: number;
}) => string;
@@ -0,0 +1,97 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.useAppendVideoFragment = exports.appendVideoFragment = exports.isIosSafari = exports.isSafari = void 0;
const react_1 = require("react");
const toSeconds = (time, fps) => {
return Math.round((time / fps) * 100) / 100;
};
const 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;
};
exports.isSafari = isSafari;
const isIosSafari = () => {
if (typeof window === 'undefined') {
return false;
}
const isIpadIPodIPhone = /iP(ad|od|hone)/i.test(window.navigator.userAgent);
return isIpadIPodIPhone && (0, exports.isSafari)();
};
exports.isIosSafari = isIosSafari;
// https://github.com/remotion-dev/remotion/issues/1655
const isIOSSafariAndBlob = (actualSrc) => {
return (0, exports.isIosSafari)() && actualSrc.startsWith('blob:');
};
const getVideoFragmentStart = ({ actualFrom, fps, }) => {
return toSeconds(Math.max(0, -actualFrom), fps);
};
const getVideoFragmentEnd = ({ duration, fps, }) => {
return toSeconds(duration, fps);
};
const appendVideoFragment = ({ actualSrc, actualFrom, duration, fps, }) => {
var _a;
if (isIOSSafariAndBlob(actualSrc)) {
return actualSrc;
}
if (actualSrc.startsWith('data:')) {
return actualSrc;
}
const existingHash = Boolean(new URL(actualSrc, (_a = (typeof window === 'undefined' ? null : window.location.href)) !== null && _a !== void 0 ? _a : '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 })}`;
};
exports.appendVideoFragment = appendVideoFragment;
const 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;
};
const useAppendVideoFragment = ({ actualSrc: initialActualSrc, actualFrom: initialActualFrom, duration: initialDuration, fps, }) => {
const actualFromRef = (0, react_1.useRef)(initialActualFrom);
const actualDuration = (0, react_1.useRef)(initialDuration);
const actualSrc = (0, react_1.useRef)(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 = (0, exports.appendVideoFragment)({
actualSrc: actualSrc.current,
actualFrom: actualFromRef.current,
duration: actualDuration.current,
fps,
});
return appended;
};
exports.useAppendVideoFragment = useAppendVideoFragment;