Add .gitignore to exclude all node packages and lock files
This commit is contained in:
Generated
Vendored
+4
@@ -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>;
|
||||
Generated
Vendored
+55
@@ -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);
|
||||
Generated
Vendored
+3
@@ -0,0 +1,3 @@
|
||||
import React from 'react';
|
||||
import type { AllOffthreadVideoProps } from './props.js';
|
||||
export declare const OffthreadVideoForRendering: React.FC<AllOffthreadVideoProps>;
|
||||
Generated
Vendored
+211
@@ -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;
|
||||
+53
@@ -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>>;
|
||||
+85
@@ -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;
|
||||
Generated
Vendored
+14
@@ -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 {};
|
||||
Generated
Vendored
+269
@@ -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);
|
||||
Generated
Vendored
+8
@@ -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 {};
|
||||
Generated
Vendored
+230
@@ -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);
|
||||
Generated
Vendored
+17
@@ -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 {};
|
||||
Generated
Vendored
+40
@@ -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;
|
||||
Generated
Vendored
+5
@@ -0,0 +1,5 @@
|
||||
import type { OnVideoFrame } from './props';
|
||||
export declare const useEmitVideoFrame: ({ ref, onVideoFrame, }: {
|
||||
ref: React.RefObject<HTMLVideoElement | null>;
|
||||
onVideoFrame: OnVideoFrame | null;
|
||||
}) => void;
|
||||
Generated
Vendored
+28
@@ -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;
|
||||
Generated
Vendored
+11
@@ -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;
|
||||
Generated
Vendored
+19
@@ -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;
|
||||
+3
@@ -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';
|
||||
+8
@@ -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; } });
|
||||
Generated
Vendored
+6
@@ -0,0 +1,6 @@
|
||||
export declare const getOffthreadVideoSource: ({ src, transparent, currentTime, toneMapped, }: {
|
||||
src: string;
|
||||
transparent: boolean;
|
||||
currentTime: number;
|
||||
toneMapped: boolean;
|
||||
}) => string;
|
||||
Generated
Vendored
+8
@@ -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;
|
||||
+86
@@ -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 {};
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
Generated
Vendored
+20
@@ -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;
|
||||
};
|
||||
Generated
Vendored
+122
@@ -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;
|
||||
Generated
Vendored
+14
@@ -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;
|
||||
Generated
Vendored
+97
@@ -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;
|
||||
Reference in New Issue
Block a user