Add .gitignore to exclude all node packages and lock files
This commit is contained in:
Generated
Vendored
+304
@@ -0,0 +1,304 @@
|
||||
import { AudioSampleSource, BufferTarget, Output, StreamTarget, VideoSampleSource, } from 'mediabunny';
|
||||
import { Internals } from 'remotion';
|
||||
import { addAudioSample, addVideoSampleAndCloseFrame } from './add-sample';
|
||||
import { handleArtifacts } from './artifact';
|
||||
import { onlyInlineAudio } from './audio';
|
||||
import { canUseWebFsWriter } from './can-use-webfs-target';
|
||||
import { createScaffold } from './create-scaffold';
|
||||
import { getRealFrameRange } from './frame-range';
|
||||
import { getDefaultAudioEncodingConfig } from './get-audio-encoding-config';
|
||||
import { makeInternalState } from './internal-state';
|
||||
import { codecToMediabunnyCodec, containerToMediabunnyContainer, getDefaultVideoCodecForContainer, getMimeType, getQualityForWebRendererQuality, } from './mediabunny-mappings';
|
||||
import { onlyOneRenderAtATimeQueue } from './render-operations-queue';
|
||||
import { sendUsageEvent } from './send-telemetry-event';
|
||||
import { createFrame } from './take-screenshot';
|
||||
import { createThrottledProgressCallback } from './throttle-progress';
|
||||
import { validateVideoFrame } from './validate-video-frame';
|
||||
import { waitForReady } from './wait-for-ready';
|
||||
import { cleanupStaleOpfsFiles, createWebFsTarget } from './web-fs-target';
|
||||
// TODO: More containers
|
||||
// TODO: Audio
|
||||
// TODO: Metadata
|
||||
// TODO: Validating inputs
|
||||
// TODO: Apply defaultCodec
|
||||
const internalRenderMediaOnWeb = async ({ composition, inputProps, delayRenderTimeoutInMilliseconds, logLevel, mediaCacheSizeInBytes, schema, videoCodec: codec, container, signal, onProgress, hardwareAcceleration, keyframeIntervalInSeconds, videoBitrate, frameRange, transparent, onArtifact, onFrame, outputTarget: userDesiredOutputTarget, licenseKey, muted, }) => {
|
||||
var _a, _b, _c, _d, _e, _f, _g;
|
||||
const outputTarget = userDesiredOutputTarget === null
|
||||
? (await canUseWebFsWriter())
|
||||
? 'web-fs'
|
||||
: 'arraybuffer'
|
||||
: userDesiredOutputTarget;
|
||||
if (outputTarget === 'web-fs') {
|
||||
await cleanupStaleOpfsFiles();
|
||||
}
|
||||
const cleanupFns = [];
|
||||
const format = containerToMediabunnyContainer(container);
|
||||
if (codec &&
|
||||
!format.getSupportedCodecs().includes(codecToMediabunnyCodec(codec))) {
|
||||
return Promise.reject(new Error(`Codec ${codec} is not supported for container ${container}`));
|
||||
}
|
||||
const resolved = await Internals.resolveVideoConfig({
|
||||
calculateMetadata: (_a = composition.calculateMetadata) !== null && _a !== void 0 ? _a : null,
|
||||
signal: signal !== null && signal !== void 0 ? signal : new AbortController().signal,
|
||||
defaultProps: (_b = composition.defaultProps) !== null && _b !== void 0 ? _b : {},
|
||||
inputProps: inputProps !== null && inputProps !== void 0 ? inputProps : {},
|
||||
compositionId: composition.id,
|
||||
compositionDurationInFrames: (_c = composition.durationInFrames) !== null && _c !== void 0 ? _c : null,
|
||||
compositionFps: (_d = composition.fps) !== null && _d !== void 0 ? _d : null,
|
||||
compositionHeight: (_e = composition.height) !== null && _e !== void 0 ? _e : null,
|
||||
compositionWidth: (_f = composition.width) !== null && _f !== void 0 ? _f : null,
|
||||
});
|
||||
const realFrameRange = getRealFrameRange(resolved.durationInFrames, frameRange);
|
||||
if (signal === null || signal === void 0 ? void 0 : signal.aborted) {
|
||||
return Promise.reject(new Error('renderMediaOnWeb() was cancelled'));
|
||||
}
|
||||
const { delayRenderScope, div, cleanupScaffold, timeUpdater, collectAssets } = await createScaffold({
|
||||
width: resolved.width,
|
||||
height: resolved.height,
|
||||
fps: resolved.fps,
|
||||
durationInFrames: resolved.durationInFrames,
|
||||
Component: composition.component,
|
||||
resolvedProps: resolved.props,
|
||||
id: resolved.id,
|
||||
delayRenderTimeoutInMilliseconds,
|
||||
logLevel,
|
||||
mediaCacheSizeInBytes,
|
||||
schema: schema !== null && schema !== void 0 ? schema : null,
|
||||
audioEnabled: !muted,
|
||||
videoEnabled: true,
|
||||
initialFrame: 0,
|
||||
defaultCodec: resolved.defaultCodec,
|
||||
defaultOutName: resolved.defaultOutName,
|
||||
});
|
||||
const internalState = makeInternalState();
|
||||
const artifactsHandler = handleArtifacts();
|
||||
cleanupFns.push(() => {
|
||||
cleanupScaffold();
|
||||
});
|
||||
const webFsTarget = outputTarget === 'web-fs' ? await createWebFsTarget() : null;
|
||||
const target = webFsTarget
|
||||
? new StreamTarget(webFsTarget.stream)
|
||||
: new BufferTarget();
|
||||
const output = new Output({
|
||||
format,
|
||||
target,
|
||||
});
|
||||
try {
|
||||
if (signal === null || signal === void 0 ? void 0 : signal.aborted) {
|
||||
throw new Error('renderMediaOnWeb() was cancelled');
|
||||
}
|
||||
await waitForReady({
|
||||
timeoutInMilliseconds: delayRenderTimeoutInMilliseconds,
|
||||
scope: delayRenderScope,
|
||||
signal,
|
||||
apiName: 'renderMediaOnWeb',
|
||||
internalState,
|
||||
});
|
||||
if (signal === null || signal === void 0 ? void 0 : signal.aborted) {
|
||||
throw new Error('renderMediaOnWeb() was cancelled');
|
||||
}
|
||||
cleanupFns.push(() => {
|
||||
if (output.state === 'finalized' || output.state === 'canceled') {
|
||||
return;
|
||||
}
|
||||
output.cancel();
|
||||
});
|
||||
const videoSampleSource = new VideoSampleSource({
|
||||
codec: codecToMediabunnyCodec(codec),
|
||||
bitrate: typeof videoBitrate === 'number'
|
||||
? videoBitrate
|
||||
: getQualityForWebRendererQuality(videoBitrate),
|
||||
sizeChangeBehavior: 'deny',
|
||||
hardwareAcceleration,
|
||||
latencyMode: 'quality',
|
||||
keyFrameInterval: keyframeIntervalInSeconds,
|
||||
alpha: transparent ? 'keep' : 'discard',
|
||||
});
|
||||
cleanupFns.push(() => {
|
||||
videoSampleSource.close();
|
||||
});
|
||||
output.addVideoTrack(videoSampleSource);
|
||||
// TODO: Should be able to customize
|
||||
let audioSampleSource = null;
|
||||
if (!muted) {
|
||||
const defaultAudioEncodingConfig = await getDefaultAudioEncodingConfig();
|
||||
if (!defaultAudioEncodingConfig) {
|
||||
return Promise.reject(new Error('No default audio encoding config found'));
|
||||
}
|
||||
audioSampleSource = new AudioSampleSource(defaultAudioEncodingConfig);
|
||||
cleanupFns.push(() => {
|
||||
audioSampleSource === null || audioSampleSource === void 0 ? void 0 : audioSampleSource.close();
|
||||
});
|
||||
output.addAudioTrack(audioSampleSource);
|
||||
}
|
||||
await output.start();
|
||||
if (signal === null || signal === void 0 ? void 0 : signal.aborted) {
|
||||
throw new Error('renderMediaOnWeb() was cancelled');
|
||||
}
|
||||
const progress = {
|
||||
renderedFrames: 0,
|
||||
encodedFrames: 0,
|
||||
};
|
||||
const throttledOnProgress = createThrottledProgressCallback(onProgress);
|
||||
for (let frame = realFrameRange[0]; frame <= realFrameRange[1]; frame++) {
|
||||
if (signal === null || signal === void 0 ? void 0 : signal.aborted) {
|
||||
throw new Error('renderMediaOnWeb() was cancelled');
|
||||
}
|
||||
(_g = timeUpdater.current) === null || _g === void 0 ? void 0 : _g.update(frame);
|
||||
await waitForReady({
|
||||
timeoutInMilliseconds: delayRenderTimeoutInMilliseconds,
|
||||
scope: delayRenderScope,
|
||||
signal,
|
||||
apiName: 'renderMediaOnWeb',
|
||||
internalState,
|
||||
});
|
||||
if (signal === null || signal === void 0 ? void 0 : signal.aborted) {
|
||||
throw new Error('renderMediaOnWeb() was cancelled');
|
||||
}
|
||||
const createFrameStart = performance.now();
|
||||
const imageData = await createFrame({
|
||||
div,
|
||||
width: resolved.width,
|
||||
height: resolved.height,
|
||||
logLevel,
|
||||
internalState,
|
||||
});
|
||||
internalState.addCreateFrameTime(performance.now() - createFrameStart);
|
||||
if (signal === null || signal === void 0 ? void 0 : signal.aborted) {
|
||||
throw new Error('renderMediaOnWeb() was cancelled');
|
||||
}
|
||||
const assets = collectAssets.current.collectAssets();
|
||||
if (onArtifact) {
|
||||
await artifactsHandler.handle({
|
||||
imageData,
|
||||
frame,
|
||||
assets,
|
||||
onArtifact,
|
||||
});
|
||||
}
|
||||
if (signal === null || signal === void 0 ? void 0 : signal.aborted) {
|
||||
throw new Error('renderMediaOnWeb() was cancelled');
|
||||
}
|
||||
const audio = muted
|
||||
? null
|
||||
: onlyInlineAudio({ assets, fps: resolved.fps, frame });
|
||||
const timestamp = Math.round(((frame - realFrameRange[0]) / resolved.fps) * 1000000);
|
||||
const videoFrame = new VideoFrame(imageData, {
|
||||
timestamp,
|
||||
});
|
||||
progress.renderedFrames++;
|
||||
throttledOnProgress === null || throttledOnProgress === void 0 ? void 0 : throttledOnProgress({ ...progress });
|
||||
// Process frame through onFrame callback if provided
|
||||
let frameToEncode = videoFrame;
|
||||
if (onFrame) {
|
||||
const returnedFrame = await onFrame(videoFrame);
|
||||
if (signal === null || signal === void 0 ? void 0 : signal.aborted) {
|
||||
throw new Error('renderMediaOnWeb() was cancelled');
|
||||
}
|
||||
frameToEncode = validateVideoFrame({
|
||||
originalFrame: videoFrame,
|
||||
returnedFrame,
|
||||
expectedWidth: resolved.width,
|
||||
expectedHeight: resolved.height,
|
||||
expectedTimestamp: timestamp,
|
||||
});
|
||||
}
|
||||
const addSampleStart = performance.now();
|
||||
await Promise.all([
|
||||
addVideoSampleAndCloseFrame(frameToEncode, videoSampleSource),
|
||||
audio && audioSampleSource
|
||||
? addAudioSample(audio, audioSampleSource)
|
||||
: Promise.resolve(),
|
||||
]);
|
||||
internalState.addAddSampleTime(performance.now() - addSampleStart);
|
||||
progress.encodedFrames++;
|
||||
throttledOnProgress === null || throttledOnProgress === void 0 ? void 0 : throttledOnProgress({ ...progress });
|
||||
if (signal === null || signal === void 0 ? void 0 : signal.aborted) {
|
||||
throw new Error('renderMediaOnWeb() was cancelled');
|
||||
}
|
||||
}
|
||||
// Call progress one final time to ensure final state is reported
|
||||
onProgress === null || onProgress === void 0 ? void 0 : onProgress({ ...progress });
|
||||
videoSampleSource.close();
|
||||
audioSampleSource === null || audioSampleSource === void 0 ? void 0 : audioSampleSource.close();
|
||||
await output.finalize();
|
||||
Internals.Log.verbose({ logLevel, tag: 'web-renderer' }, `Render timings: waitForReady=${internalState.getWaitForReadyTime().toFixed(2)}ms, createFrame=${internalState.getCreateFrameTime().toFixed(2)}ms, addSample=${internalState.getAddSampleTime().toFixed(2)}ms`);
|
||||
const mimeType = getMimeType(container);
|
||||
if (webFsTarget) {
|
||||
sendUsageEvent({
|
||||
licenseKey: licenseKey !== null && licenseKey !== void 0 ? licenseKey : null,
|
||||
succeeded: true,
|
||||
apiName: 'renderMediaOnWeb',
|
||||
});
|
||||
await webFsTarget.close();
|
||||
return {
|
||||
getBlob: () => {
|
||||
return webFsTarget.getBlob();
|
||||
},
|
||||
internalState,
|
||||
};
|
||||
}
|
||||
if (!(target instanceof BufferTarget)) {
|
||||
throw new Error('Expected target to be a BufferTarget');
|
||||
}
|
||||
sendUsageEvent({
|
||||
licenseKey: licenseKey !== null && licenseKey !== void 0 ? licenseKey : null,
|
||||
succeeded: true,
|
||||
apiName: 'renderMediaOnWeb',
|
||||
});
|
||||
return {
|
||||
getBlob: () => {
|
||||
if (!target.buffer) {
|
||||
throw new Error('The resulting buffer is empty');
|
||||
}
|
||||
return Promise.resolve(new Blob([target.buffer], { type: mimeType }));
|
||||
},
|
||||
internalState,
|
||||
};
|
||||
}
|
||||
catch (err) {
|
||||
sendUsageEvent({
|
||||
succeeded: false,
|
||||
licenseKey: licenseKey !== null && licenseKey !== void 0 ? licenseKey : null,
|
||||
apiName: 'renderMediaOnWeb',
|
||||
}).catch((err2) => {
|
||||
Internals.Log.error({ logLevel: 'error', tag: 'web-renderer' }, 'Failed to send usage event', err2);
|
||||
});
|
||||
throw err;
|
||||
}
|
||||
finally {
|
||||
cleanupFns.forEach((fn) => fn());
|
||||
}
|
||||
};
|
||||
export const renderMediaOnWeb = (options) => {
|
||||
var _a, _b;
|
||||
const container = (_a = options.container) !== null && _a !== void 0 ? _a : 'mp4';
|
||||
const codec = (_b = options.videoCodec) !== null && _b !== void 0 ? _b : getDefaultVideoCodecForContainer(container);
|
||||
onlyOneRenderAtATimeQueue.ref = onlyOneRenderAtATimeQueue.ref
|
||||
.catch(() => Promise.resolve())
|
||||
.then(() => {
|
||||
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s;
|
||||
return internalRenderMediaOnWeb({
|
||||
...options,
|
||||
delayRenderTimeoutInMilliseconds: (_a = options.delayRenderTimeoutInMilliseconds) !== null && _a !== void 0 ? _a : 30000,
|
||||
logLevel: (_c = (_b = options.logLevel) !== null && _b !== void 0 ? _b : window.remotion_logLevel) !== null && _c !== void 0 ? _c : 'info',
|
||||
schema: (_d = options.schema) !== null && _d !== void 0 ? _d : undefined,
|
||||
mediaCacheSizeInBytes: (_e = options.mediaCacheSizeInBytes) !== null && _e !== void 0 ? _e : null,
|
||||
videoCodec: codec,
|
||||
container,
|
||||
signal: (_f = options.signal) !== null && _f !== void 0 ? _f : null,
|
||||
onProgress: (_g = options.onProgress) !== null && _g !== void 0 ? _g : null,
|
||||
hardwareAcceleration: (_h = options.hardwareAcceleration) !== null && _h !== void 0 ? _h : 'no-preference',
|
||||
keyframeIntervalInSeconds: (_j = options.keyframeIntervalInSeconds) !== null && _j !== void 0 ? _j : 5,
|
||||
videoBitrate: (_k = options.videoBitrate) !== null && _k !== void 0 ? _k : 'medium',
|
||||
frameRange: (_l = options.frameRange) !== null && _l !== void 0 ? _l : null,
|
||||
transparent: (_m = options.transparent) !== null && _m !== void 0 ? _m : false,
|
||||
onArtifact: (_o = options.onArtifact) !== null && _o !== void 0 ? _o : null,
|
||||
onFrame: (_p = options.onFrame) !== null && _p !== void 0 ? _p : null,
|
||||
outputTarget: (_q = options.outputTarget) !== null && _q !== void 0 ? _q : null,
|
||||
licenseKey: (_r = options.licenseKey) !== null && _r !== void 0 ? _r : undefined,
|
||||
muted: (_s = options.muted) !== null && _s !== void 0 ? _s : false,
|
||||
});
|
||||
});
|
||||
return onlyOneRenderAtATimeQueue.ref;
|
||||
};
|
||||
Reference in New Issue
Block a user