UNPKG

remotion

Version:

Make videos programmatically

217 lines (216 loc) • 11.1 kB
"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 delay_render_js_1 = require("../delay-render.js"); const get_remotion_environment_js_1 = require("../get-remotion-environment.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_unsafe_video_config_js_1 = require("../use-unsafe-video-config.js"); const volume_prop_js_1 = require("../volume-prop.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, ...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, get_remotion_environment_js_1.getRemotionEnvironment)(); const logLevel = (0, log_level_context_js_1.useLogLevel)(); const mountTime = (0, log_level_context_js_1.useMountTime)(); 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, 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 : null, audioStartFrame: Math.max(0, -((_a = sequenceContext === null || sequenceContext === void 0 ? void 0 : sequenceContext.relativeFrom) !== null && _a !== void 0 ? _a : 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, ]); (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 = (0, delay_render_js_1.delayRender)(`Rendering <Video /> 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') { (0, delay_render_js_1.continueRender)(handle); return; } if ((0, is_approximately_the_same_js_1.isApproximatelyTheSame)(current.currentTime, currentTime)) { if (current.readyState >= 2) { (0, delay_render_js_1.continueRender)(handle); return; } const loadedDataHandler = () => { (0, delay_render_js_1.continueRender)(handle); }; current.addEventListener('loadeddata', loadedDataHandler, { once: true }); return () => { current.removeEventListener('loadeddata', loadedDataHandler); }; } const endedHandler = () => { (0, delay_render_js_1.continueRender)(handle); }; const seek = (0, seek_until_right_js_1.seekToTimeMultipleUntilRight)({ element: current, desiredTime: currentTime, fps: videoConfig.fps, logLevel, mountTime, }); seek.prom.then(() => { (0, delay_render_js_1.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); (0, delay_render_js_1.continueRender)(handle); }; }, [ volumePropsFrame, props.src, playbackRate, videoConfig.fps, frame, mediaStartsAt, onError, delayRenderRetries, delayRenderTimeoutInMilliseconds, logLevel, mountTime, ]); 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 = (0, delay_render_js_1.delayRender)('Loading <Video> 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); } (0, delay_render_js_1.continueRender)(newHandle); }; if (current === null || current === void 0 ? void 0 : current.duration) { onDuration(src, current.duration); (0, delay_render_js_1.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); (0, delay_render_js_1.continueRender)(newHandle); }; }, [src, onDuration, delayRenderRetries, delayRenderTimeoutInMilliseconds]); } return (0, jsx_runtime_1.jsx)("video", { ref: videoRef, disableRemotePlayback: true, ...props }); }; exports.VideoForRendering = (0, react_1.forwardRef)(VideoForRenderingForwardFunction);