@remotion/media 4.0.351

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (94) hide show
  1. package/LICENSE.md +49 -0
  2. package/README.md +18 -0
  3. package/dist/audio/audio-for-rendering.d.ts +3 -0
  4. package/dist/audio/audio-for-rendering.js +99 -0
  5. package/dist/audio/audio.d.ts +3 -0
  6. package/dist/audio/audio.js +60 -0
  7. package/dist/audio/props.d.ts +29 -0
  8. package/dist/audio/props.js +1 -0
  9. package/dist/audio-extraction/audio-cache.d.ts +11 -0
  10. package/dist/audio-extraction/audio-cache.js +58 -0
  11. package/dist/audio-extraction/audio-iterator.d.ts +24 -0
  12. package/dist/audio-extraction/audio-iterator.js +106 -0
  13. package/dist/audio-extraction/audio-manager.d.ts +64 -0
  14. package/dist/audio-extraction/audio-manager.js +83 -0
  15. package/dist/audio-extraction/extract-audio.d.ts +10 -0
  16. package/dist/audio-extraction/extract-audio.js +77 -0
  17. package/dist/audio-for-rendering.d.ts +3 -0
  18. package/dist/audio-for-rendering.js +94 -0
  19. package/dist/audio.d.ts +3 -0
  20. package/dist/audio.js +60 -0
  21. package/dist/audiodata-to-array.d.ts +0 -0
  22. package/dist/audiodata-to-array.js +1 -0
  23. package/dist/caches.d.ts +86 -0
  24. package/dist/caches.js +15 -0
  25. package/dist/convert-audiodata/combine-audiodata.d.ts +2 -0
  26. package/dist/convert-audiodata/combine-audiodata.js +40 -0
  27. package/dist/convert-audiodata/convert-audiodata.d.ts +16 -0
  28. package/dist/convert-audiodata/convert-audiodata.js +53 -0
  29. package/dist/convert-audiodata/data-types.d.ts +1 -0
  30. package/dist/convert-audiodata/data-types.js +22 -0
  31. package/dist/convert-audiodata/is-planar-format.d.ts +1 -0
  32. package/dist/convert-audiodata/is-planar-format.js +3 -0
  33. package/dist/convert-audiodata/log-audiodata.d.ts +1 -0
  34. package/dist/convert-audiodata/log-audiodata.js +8 -0
  35. package/dist/convert-audiodata/resample-audiodata.d.ts +10 -0
  36. package/dist/convert-audiodata/resample-audiodata.js +72 -0
  37. package/dist/convert-audiodata/trim-audiodata.d.ts +0 -0
  38. package/dist/convert-audiodata/trim-audiodata.js +1 -0
  39. package/dist/deserialized-audiodata.d.ts +15 -0
  40. package/dist/deserialized-audiodata.js +26 -0
  41. package/dist/esm/index.mjs +14487 -0
  42. package/dist/extract-audio.d.ts +7 -0
  43. package/dist/extract-audio.js +98 -0
  44. package/dist/extract-frame-and-audio.d.ts +15 -0
  45. package/dist/extract-frame-and-audio.js +28 -0
  46. package/dist/extract-frame-via-broadcast-channel.d.ts +15 -0
  47. package/dist/extract-frame-via-broadcast-channel.js +104 -0
  48. package/dist/extract-frame.d.ts +27 -0
  49. package/dist/extract-frame.js +21 -0
  50. package/dist/extrct-audio.d.ts +7 -0
  51. package/dist/extrct-audio.js +94 -0
  52. package/dist/get-frames-since-keyframe.d.ts +22 -0
  53. package/dist/get-frames-since-keyframe.js +41 -0
  54. package/dist/index.d.ts +4 -0
  55. package/dist/index.js +2 -0
  56. package/dist/keyframe-bank.d.ts +25 -0
  57. package/dist/keyframe-bank.js +120 -0
  58. package/dist/keyframe-manager.d.ts +23 -0
  59. package/dist/keyframe-manager.js +170 -0
  60. package/dist/log.d.ts +10 -0
  61. package/dist/log.js +33 -0
  62. package/dist/new-video-for-rendering.d.ts +3 -0
  63. package/dist/new-video-for-rendering.js +108 -0
  64. package/dist/new-video.d.ts +3 -0
  65. package/dist/new-video.js +37 -0
  66. package/dist/props.d.ts +29 -0
  67. package/dist/props.js +1 -0
  68. package/dist/remember-actual-matroska-timestamps.d.ts +4 -0
  69. package/dist/remember-actual-matroska-timestamps.js +19 -0
  70. package/dist/serialize-videoframe.d.ts +0 -0
  71. package/dist/serialize-videoframe.js +1 -0
  72. package/dist/video/props.d.ts +28 -0
  73. package/dist/video/props.js +1 -0
  74. package/dist/video/video-for-rendering.d.ts +3 -0
  75. package/dist/video/video-for-rendering.js +115 -0
  76. package/dist/video/video.d.ts +3 -0
  77. package/dist/video/video.js +37 -0
  78. package/dist/video-extraction/extract-frame-via-broadcast-channel.d.ts +16 -0
  79. package/dist/video-extraction/extract-frame-via-broadcast-channel.js +107 -0
  80. package/dist/video-extraction/extract-frame.d.ts +9 -0
  81. package/dist/video-extraction/extract-frame.js +21 -0
  82. package/dist/video-extraction/get-frames-since-keyframe.d.ts +23 -0
  83. package/dist/video-extraction/get-frames-since-keyframe.js +42 -0
  84. package/dist/video-extraction/keyframe-bank.d.ts +25 -0
  85. package/dist/video-extraction/keyframe-bank.js +121 -0
  86. package/dist/video-extraction/keyframe-manager.d.ts +23 -0
  87. package/dist/video-extraction/keyframe-manager.js +171 -0
  88. package/dist/video-extraction/remember-actual-matroska-timestamps.d.ts +5 -0
  89. package/dist/video-extraction/remember-actual-matroska-timestamps.js +19 -0
  90. package/dist/video-for-rendering.d.ts +3 -0
  91. package/dist/video-for-rendering.js +108 -0
  92. package/dist/video.d.ts +3 -0
  93. package/dist/video.js +37 -0
  94. package/package.json +55 -0
@@ -0,0 +1,170 @@
1
+ import { getFramesSinceKeyframe } from './get-frames-since-keyframe';
2
+ import { Log } from './log';
3
+ const MAX_CACHE_SIZE = 1000 * 1000 * 1000; // 1GB
4
+ export const makeKeyframeManager = () => {
5
+ // src => {[startTimestampInSeconds]: KeyframeBank
6
+ const sources = {};
7
+ const addKeyframeBank = ({ src, bank, startTimestampInSeconds, }) => {
8
+ sources[src] = sources[src] ?? {};
9
+ sources[src][startTimestampInSeconds] = bank;
10
+ };
11
+ const logCacheStats = async (logLevel) => {
12
+ let count = 0;
13
+ let totalSize = 0;
14
+ for (const src in sources) {
15
+ for (const bank in sources[src]) {
16
+ const v = await sources[src][bank];
17
+ const { size, timestamps } = v.getOpenFrameCount();
18
+ count += timestamps.length;
19
+ totalSize += size;
20
+ if (size === 0) {
21
+ continue;
22
+ }
23
+ Log.verbose(logLevel, `[Video] Open frames for src ${src}: ${timestamps.join(', ')}}`);
24
+ }
25
+ }
26
+ Log.verbose(logLevel, `[Video] Cache stats: ${count} open frames, ${totalSize} bytes`);
27
+ };
28
+ const getCacheStats = async () => {
29
+ let count = 0;
30
+ let totalSize = 0;
31
+ for (const src in sources) {
32
+ for (const bank in sources[src]) {
33
+ const v = await sources[src][bank];
34
+ const { timestamps, size } = v.getOpenFrameCount();
35
+ count += timestamps.length;
36
+ totalSize += size;
37
+ if (size === 0) {
38
+ continue;
39
+ }
40
+ }
41
+ }
42
+ return { count, totalSize };
43
+ };
44
+ const getTheKeyframeBankMostInThePast = async () => {
45
+ let mostInThePast = null;
46
+ let mostInThePastBank = null;
47
+ for (const src in sources) {
48
+ for (const b in sources[src]) {
49
+ const bank = await sources[src][b];
50
+ const lastUsed = bank.getLastUsed();
51
+ if (mostInThePast === null || lastUsed < mostInThePast) {
52
+ mostInThePast = lastUsed;
53
+ mostInThePastBank = { src, bank };
54
+ }
55
+ }
56
+ }
57
+ if (!mostInThePastBank) {
58
+ throw new Error('No keyframe bank found');
59
+ }
60
+ return mostInThePastBank;
61
+ };
62
+ const ensureToStayUnderMaxCacheSize = async (logLevel) => {
63
+ let cacheStats = await getCacheStats();
64
+ while (cacheStats.totalSize > MAX_CACHE_SIZE) {
65
+ const { bank: mostInThePastBank, src: mostInThePastSrc } = await getTheKeyframeBankMostInThePast();
66
+ if (mostInThePastBank) {
67
+ await mostInThePastBank.prepareForDeletion();
68
+ delete sources[mostInThePastSrc][mostInThePastBank.startTimestampInSeconds];
69
+ Log.verbose(logLevel, `[Video] Deleted frames for src ${mostInThePastSrc} from ${mostInThePastBank.startTimestampInSeconds}sec to ${mostInThePastBank.endTimestampInSeconds}sec to free up memory.`);
70
+ }
71
+ cacheStats = await getCacheStats();
72
+ }
73
+ };
74
+ const clearKeyframeBanksBeforeTime = async ({ timestampInSeconds, src, logLevel, }) => {
75
+ // TODO: make it dependent on the fps and concurrency
76
+ const SAFE_BACK_WINDOW_IN_SECONDS = 1;
77
+ const threshold = timestampInSeconds - SAFE_BACK_WINDOW_IN_SECONDS;
78
+ if (!sources[src]) {
79
+ return;
80
+ }
81
+ const banks = Object.keys(sources[src]);
82
+ for (const startTimeInSeconds of banks) {
83
+ const bank = await sources[src][startTimeInSeconds];
84
+ const { endTimestampInSeconds, startTimestampInSeconds } = bank;
85
+ if (endTimestampInSeconds < threshold) {
86
+ await bank.prepareForDeletion();
87
+ Log.verbose(logLevel, `[Video] Cleared frames for src ${src} from ${startTimestampInSeconds}sec to ${endTimestampInSeconds}sec`);
88
+ delete sources[src][startTimeInSeconds];
89
+ }
90
+ else {
91
+ bank.deleteFramesBeforeTimestamp({
92
+ timestampInSeconds: threshold,
93
+ logLevel,
94
+ src,
95
+ });
96
+ }
97
+ }
98
+ await logCacheStats(logLevel);
99
+ };
100
+ const getKeyframeBankOrRefetch = async ({ packetSink, timestamp, videoSampleSink, src, logLevel, }) => {
101
+ const startPacket = await packetSink.getKeyPacket(timestamp, {
102
+ verifyKeyPackets: true,
103
+ });
104
+ if (!startPacket) {
105
+ throw new Error(`No key packet found for timestamp ${timestamp}`);
106
+ }
107
+ const startTimestampInSeconds = startPacket.timestamp;
108
+ const existingBank = sources[src]?.[startTimestampInSeconds];
109
+ // Bank does not yet exist, we need to fetch
110
+ if (!existingBank) {
111
+ const newKeyframeBank = getFramesSinceKeyframe({
112
+ packetSink,
113
+ videoSampleSink,
114
+ startPacket,
115
+ });
116
+ addKeyframeBank({ src, bank: newKeyframeBank, startTimestampInSeconds });
117
+ return newKeyframeBank;
118
+ }
119
+ // Bank exists and still has the frame we want
120
+ if (await (await existingBank).hasTimestampInSecond(timestamp)) {
121
+ return existingBank;
122
+ }
123
+ Log.verbose(logLevel, `[Video] Bank exists but frames have already been evicted!`);
124
+ // Bank exists but frames have already been evicted!
125
+ // First delete it entirely
126
+ await (await existingBank).prepareForDeletion();
127
+ delete sources[src][startTimestampInSeconds];
128
+ // Then refetch
129
+ const replacementKeybank = getFramesSinceKeyframe({
130
+ packetSink,
131
+ videoSampleSink,
132
+ startPacket,
133
+ });
134
+ addKeyframeBank({ src, bank: replacementKeybank, startTimestampInSeconds });
135
+ return replacementKeybank;
136
+ };
137
+ const requestKeyframeBank = async ({ packetSink, timestamp, videoSampleSink, src, logLevel, }) => {
138
+ await ensureToStayUnderMaxCacheSize(logLevel);
139
+ await clearKeyframeBanksBeforeTime({
140
+ timestampInSeconds: timestamp,
141
+ src,
142
+ logLevel,
143
+ });
144
+ const keyframeBank = await getKeyframeBankOrRefetch({
145
+ packetSink,
146
+ timestamp,
147
+ videoSampleSink,
148
+ src,
149
+ logLevel,
150
+ });
151
+ return keyframeBank;
152
+ };
153
+ const clearAll = async () => {
154
+ const srcs = Object.keys(sources);
155
+ for (const src of srcs) {
156
+ const banks = Object.keys(sources[src]);
157
+ for (const startTimeInSeconds of banks) {
158
+ const bank = await sources[src][startTimeInSeconds];
159
+ await bank.prepareForDeletion();
160
+ delete sources[src][startTimeInSeconds];
161
+ }
162
+ }
163
+ };
164
+ return {
165
+ requestKeyframeBank,
166
+ addKeyframeBank,
167
+ getCacheStats,
168
+ clearAll,
169
+ };
170
+ };
package/dist/log.d.ts ADDED
@@ -0,0 +1,10 @@
1
+ export declare const logLevels: readonly ["trace", "verbose", "info", "warn", "error"];
2
+ export type LogLevel = (typeof logLevels)[number];
3
+ export declare const isEqualOrBelowLogLevel: (currentLevel: LogLevel, level: LogLevel) => boolean;
4
+ export declare const Log: {
5
+ trace: (logLevel: LogLevel, message?: any, ...optionalParams: any[]) => void;
6
+ verbose: (logLevel: LogLevel, message?: any, ...optionalParams: any[]) => void;
7
+ info: (logLevel: LogLevel, message?: any, ...optionalParams: any[]) => void;
8
+ warn: (logLevel: LogLevel, message?: any, ...optionalParams: any[]) => void;
9
+ error: (message?: any, ...optionalParams: any[]) => void;
10
+ };
package/dist/log.js ADDED
@@ -0,0 +1,33 @@
1
+ /* eslint-disable no-console */
2
+ export const logLevels = ['trace', 'verbose', 'info', 'warn', 'error'];
3
+ const getNumberForLogLevel = (level) => {
4
+ return logLevels.indexOf(level);
5
+ };
6
+ export const isEqualOrBelowLogLevel = (currentLevel, level) => {
7
+ return getNumberForLogLevel(currentLevel) <= getNumberForLogLevel(level);
8
+ };
9
+ export const Log = {
10
+ trace: (logLevel, ...args) => {
11
+ if (isEqualOrBelowLogLevel(logLevel, 'trace')) {
12
+ return console.log(...args);
13
+ }
14
+ },
15
+ verbose: (logLevel, ...args) => {
16
+ if (isEqualOrBelowLogLevel(logLevel, 'verbose')) {
17
+ return console.log(...args);
18
+ }
19
+ },
20
+ info: (logLevel, ...args) => {
21
+ if (isEqualOrBelowLogLevel(logLevel, 'info')) {
22
+ return console.log(...args);
23
+ }
24
+ },
25
+ warn: (logLevel, ...args) => {
26
+ if (isEqualOrBelowLogLevel(logLevel, 'warn')) {
27
+ return console.warn(...args);
28
+ }
29
+ },
30
+ error: (...args) => {
31
+ return console.error(...args);
32
+ },
33
+ };
@@ -0,0 +1,3 @@
1
+ import React from 'react';
2
+ import type { VideoProps } from './props';
3
+ export declare const VideoForRendering: React.FC<VideoProps>;
@@ -0,0 +1,108 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { useContext, useLayoutEffect, useMemo, useRef, useState, } from 'react';
3
+ import { cancelRender, Internals, useCurrentFrame, useDelayRender, useRemotionEnvironment, } from 'remotion';
4
+ import { extractFrameViaBroadcastChannel } from './extract-frame-via-broadcast-channel';
5
+ export const VideoForRendering = ({ volume: volumeProp, playbackRate, src, muted, loopVolumeCurveBehavior, delayRenderRetries, delayRenderTimeoutInMilliseconds,
6
+ // call when a frame of the video, i.e. frame drawn on canvas
7
+ onVideoFrame, logLevel = window.remotion_logLevel, }) => {
8
+ const absoluteFrame = Internals.useTimelinePosition();
9
+ const videoConfig = Internals.useUnsafeVideoConfig();
10
+ const canvasRef = useRef(null);
11
+ const { registerRenderAsset, unregisterRenderAsset } = useContext(Internals.RenderAssetManager);
12
+ const frame = useCurrentFrame();
13
+ const volumePropsFrame = Internals.useFrameForVolumeProp(loopVolumeCurveBehavior ?? 'repeat');
14
+ const environment = useRemotionEnvironment();
15
+ const [id] = useState(() => `${Math.random()}`.replace('0.', ''));
16
+ if (!videoConfig) {
17
+ throw new Error('No video config found');
18
+ }
19
+ if (!src) {
20
+ throw new TypeError('No `src` was passed to <Video>.');
21
+ }
22
+ const volume = Internals.evaluateVolume({
23
+ volume: volumeProp,
24
+ frame: volumePropsFrame,
25
+ mediaVolume: 1,
26
+ });
27
+ Internals.warnAboutTooHighVolume(volume);
28
+ const shouldRenderAudio = useMemo(() => {
29
+ if (!window.remotion_audioEnabled) {
30
+ return false;
31
+ }
32
+ if (muted) {
33
+ return false;
34
+ }
35
+ if (volume <= 0) {
36
+ return false;
37
+ }
38
+ return true;
39
+ }, [muted, volume]);
40
+ const { fps } = videoConfig;
41
+ const { delayRender, continueRender } = useDelayRender();
42
+ useLayoutEffect(() => {
43
+ if (!canvasRef.current) {
44
+ return;
45
+ }
46
+ const actualFps = playbackRate ? fps / playbackRate : fps;
47
+ const timestamp = frame / actualFps;
48
+ const durationInSeconds = 1 / actualFps;
49
+ const newHandle = delayRender(`Extracting frame number ${frame}`, {
50
+ retries: delayRenderRetries ?? undefined,
51
+ timeoutInMilliseconds: delayRenderTimeoutInMilliseconds ?? undefined,
52
+ });
53
+ extractFrameViaBroadcastChannel({
54
+ src,
55
+ timeInSeconds: timestamp,
56
+ durationInSeconds,
57
+ logLevel: logLevel ?? 'info',
58
+ shouldRenderAudio,
59
+ isClientSideRendering: environment.isClientSideRendering,
60
+ })
61
+ .then(({ frame: imageBitmap, audio }) => {
62
+ if (!imageBitmap) {
63
+ cancelRender(new Error('No video frame found'));
64
+ }
65
+ onVideoFrame?.(imageBitmap);
66
+ canvasRef.current?.getContext('2d')?.drawImage(imageBitmap, 0, 0);
67
+ imageBitmap.close();
68
+ if (audio) {
69
+ registerRenderAsset({
70
+ type: 'inline-audio',
71
+ id,
72
+ audio: Array.from(audio.data),
73
+ sampleRate: audio.sampleRate,
74
+ numberOfChannels: audio.numberOfChannels,
75
+ frame: absoluteFrame,
76
+ timestamp: audio.timestamp,
77
+ duration: (audio.numberOfFrames / audio.sampleRate) * 1000000,
78
+ });
79
+ }
80
+ continueRender(newHandle);
81
+ })
82
+ .catch((error) => {
83
+ cancelRender(error);
84
+ });
85
+ return () => {
86
+ continueRender(newHandle);
87
+ unregisterRenderAsset(id);
88
+ };
89
+ }, [
90
+ absoluteFrame,
91
+ continueRender,
92
+ delayRender,
93
+ delayRenderRetries,
94
+ delayRenderTimeoutInMilliseconds,
95
+ environment.isClientSideRendering,
96
+ fps,
97
+ frame,
98
+ id,
99
+ logLevel,
100
+ onVideoFrame,
101
+ playbackRate,
102
+ registerRenderAsset,
103
+ shouldRenderAudio,
104
+ src,
105
+ unregisterRenderAsset,
106
+ ]);
107
+ return (_jsx("canvas", { ref: canvasRef, width: videoConfig.width, height: videoConfig.height }));
108
+ };
@@ -0,0 +1,3 @@
1
+ import React from 'react';
2
+ import type { VideoProps } from './props';
3
+ export declare const Video: React.FC<VideoProps>;
@@ -0,0 +1,37 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { useCallback } from 'react';
3
+ import { Internals, Sequence, useRemotionEnvironment } from 'remotion';
4
+ import { VideoForRendering } from './new-video-for-rendering';
5
+ const { validateMediaTrimProps, resolveTrimProps, validateMediaProps, VideoForPreview, } = Internals;
6
+ export const Video = (props) => {
7
+ // Should only destruct `startFrom` and `endAt` from props,
8
+ // rest gets drilled down
9
+ const { trimBefore, trimAfter, name, pauseWhenBuffering, stack, showInTimeline, ...otherProps } = props;
10
+ const environment = useRemotionEnvironment();
11
+ const onDuration = useCallback(() => undefined, []);
12
+ if (typeof props.src !== 'string') {
13
+ throw new TypeError(`The \`<Video>\` tag requires a string for \`src\`, but got ${JSON.stringify(props.src)} instead.`);
14
+ }
15
+ validateMediaTrimProps({
16
+ startFrom: undefined,
17
+ endAt: undefined,
18
+ trimBefore,
19
+ trimAfter,
20
+ });
21
+ const { trimBeforeValue, trimAfterValue } = resolveTrimProps({
22
+ startFrom: undefined,
23
+ endAt: undefined,
24
+ trimBefore,
25
+ trimAfter,
26
+ });
27
+ if (typeof trimBeforeValue !== 'undefined' ||
28
+ typeof trimAfterValue !== 'undefined') {
29
+ return (_jsx(Sequence, { layout: "none", from: 0 - (trimBeforeValue ?? 0), showInTimeline: false, durationInFrames: trimAfterValue, name: name, children: _jsx(Video, { pauseWhenBuffering: pauseWhenBuffering ?? false, ...otherProps }) }));
30
+ }
31
+ validateMediaProps(props, 'Video');
32
+ if (environment.isRendering) {
33
+ return _jsx(VideoForRendering, { ...otherProps });
34
+ }
35
+ const { onAutoPlayError, onVideoFrame, crossOrigin, delayRenderRetries, delayRenderTimeoutInMilliseconds, ...propsForPreview } = otherProps;
36
+ return (_jsx(VideoForPreview, { _remotionInternalStack: stack ?? null, _remotionInternalNativeLoopPassed: false, onDuration: onDuration, onlyWarnForMediaSeekingError: true, pauseWhenBuffering: pauseWhenBuffering ?? false, showInTimeline: showInTimeline ?? true, onAutoPlayError: onAutoPlayError ?? undefined, onVideoFrame: onVideoFrame ?? null, crossOrigin: crossOrigin, ...propsForPreview }));
37
+ };
@@ -0,0 +1,29 @@
1
+ import type { LoopVolumeCurveBehavior, VolumeProp } from 'remotion';
2
+ import type { LogLevel } from './log';
3
+ export type AudioProps = {
4
+ src: string;
5
+ trimBefore?: number;
6
+ trimAfter?: number;
7
+ volume?: VolumeProp;
8
+ loopVolumeCurveBehavior?: LoopVolumeCurveBehavior;
9
+ name?: string;
10
+ pauseWhenBuffering?: boolean;
11
+ showInTimeline?: boolean;
12
+ onAutoPlayError?: null | (() => void);
13
+ playbackRate?: number;
14
+ muted?: boolean;
15
+ delayRenderRetries?: number;
16
+ delayRenderTimeoutInMilliseconds?: number;
17
+ crossOrigin?: '' | 'anonymous' | 'use-credentials';
18
+ style?: React.CSSProperties;
19
+ onError?: (err: Error) => void;
20
+ useWebAudioApi?: boolean;
21
+ acceptableTimeShiftInSeconds?: number;
22
+ /**
23
+ * @deprecated For internal use only
24
+ */
25
+ stack?: string;
26
+ logLevel?: LogLevel;
27
+ loop?: boolean;
28
+ _remotionInternalNativeLoopPassed?: boolean;
29
+ };
package/dist/props.js ADDED
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,4 @@
1
+ export declare const rememberActualMatroskaTimestamps: (isMatroska: boolean) => {
2
+ observeTimestamp: (startTime: number) => void;
3
+ getRealTimestamp: (observedTimestamp: number) => number | null;
4
+ };
@@ -0,0 +1,19 @@
1
+ export const rememberActualMatroskaTimestamps = (isMatroska) => {
2
+ const observations = [];
3
+ const observeTimestamp = (startTime) => {
4
+ if (!isMatroska) {
5
+ return;
6
+ }
7
+ observations.push(startTime);
8
+ };
9
+ const getRealTimestamp = (observedTimestamp) => {
10
+ if (!isMatroska) {
11
+ return observedTimestamp;
12
+ }
13
+ return (observations.find((observation) => Math.abs(observedTimestamp - observation) < 0.001) ?? null);
14
+ };
15
+ return {
16
+ observeTimestamp,
17
+ getRealTimestamp,
18
+ };
19
+ };
File without changes
@@ -0,0 +1 @@
1
+ "use strict";
@@ -0,0 +1,28 @@
1
+ import type { LogLevel, LoopVolumeCurveBehavior, OnVideoFrame, VolumeProp } from 'remotion';
2
+ export type VideoProps = {
3
+ src: string;
4
+ trimBefore?: number;
5
+ trimAfter?: number;
6
+ volume?: VolumeProp;
7
+ loopVolumeCurveBehavior?: LoopVolumeCurveBehavior;
8
+ name?: string;
9
+ pauseWhenBuffering?: boolean;
10
+ showInTimeline?: boolean;
11
+ onAutoPlayError?: null | (() => void);
12
+ onVideoFrame?: OnVideoFrame;
13
+ playbackRate?: number;
14
+ muted?: boolean;
15
+ delayRenderRetries?: number;
16
+ delayRenderTimeoutInMilliseconds?: number;
17
+ crossOrigin?: '' | 'anonymous' | 'use-credentials';
18
+ style?: React.CSSProperties;
19
+ onError?: (err: Error) => void;
20
+ useWebAudioApi?: boolean;
21
+ acceptableTimeShiftInSeconds?: number;
22
+ /**
23
+ * @deprecated For internal use only
24
+ */
25
+ stack?: string;
26
+ logLevel?: LogLevel;
27
+ loop?: boolean;
28
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,3 @@
1
+ import React from 'react';
2
+ import type { VideoProps } from './props';
3
+ export declare const VideoForRendering: React.FC<VideoProps>;
@@ -0,0 +1,115 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { useContext, useLayoutEffect, useMemo, useRef, useState, } from 'react';
3
+ import { cancelRender, Internals, useCurrentFrame, useDelayRender, useRemotionEnvironment, } from 'remotion';
4
+ import { extractFrameViaBroadcastChannel } from '../video-extraction/extract-frame-via-broadcast-channel';
5
+ export const VideoForRendering = ({ volume: volumeProp, playbackRate, src, muted, loopVolumeCurveBehavior, delayRenderRetries, delayRenderTimeoutInMilliseconds,
6
+ // call when a frame of the video, i.e. frame drawn on canvas
7
+ onVideoFrame, logLevel = window.remotion_logLevel, loop, style, }) => {
8
+ const absoluteFrame = Internals.useTimelinePosition();
9
+ const videoConfig = Internals.useUnsafeVideoConfig();
10
+ const canvasRef = useRef(null);
11
+ const { registerRenderAsset, unregisterRenderAsset } = useContext(Internals.RenderAssetManager);
12
+ const frame = useCurrentFrame();
13
+ const volumePropsFrame = Internals.useFrameForVolumeProp(loopVolumeCurveBehavior ?? 'repeat');
14
+ const environment = useRemotionEnvironment();
15
+ const [id] = useState(() => `${Math.random()}`.replace('0.', ''));
16
+ if (!videoConfig) {
17
+ throw new Error('No video config found');
18
+ }
19
+ if (!src) {
20
+ throw new TypeError('No `src` was passed to <Video>.');
21
+ }
22
+ const volume = Internals.evaluateVolume({
23
+ volume: volumeProp,
24
+ frame: volumePropsFrame,
25
+ mediaVolume: 1,
26
+ });
27
+ Internals.warnAboutTooHighVolume(volume);
28
+ const shouldRenderAudio = useMemo(() => {
29
+ if (!window.remotion_audioEnabled) {
30
+ return false;
31
+ }
32
+ if (muted) {
33
+ return false;
34
+ }
35
+ if (volume <= 0) {
36
+ return false;
37
+ }
38
+ return true;
39
+ }, [muted, volume]);
40
+ const { fps } = videoConfig;
41
+ const { delayRender, continueRender } = useDelayRender();
42
+ useLayoutEffect(() => {
43
+ if (!canvasRef.current) {
44
+ return;
45
+ }
46
+ const actualFps = playbackRate ? fps / playbackRate : fps;
47
+ const timestamp = frame / actualFps;
48
+ const durationInSeconds = 1 / actualFps;
49
+ const newHandle = delayRender(`Extracting frame number ${frame}`, {
50
+ retries: delayRenderRetries ?? undefined,
51
+ timeoutInMilliseconds: delayRenderTimeoutInMilliseconds ?? undefined,
52
+ });
53
+ extractFrameViaBroadcastChannel({
54
+ src,
55
+ timeInSeconds: timestamp,
56
+ durationInSeconds,
57
+ logLevel: logLevel ?? 'info',
58
+ includeAudio: shouldRenderAudio,
59
+ includeVideo: window.remotion_videoEnabled,
60
+ isClientSideRendering: environment.isClientSideRendering,
61
+ volume,
62
+ loop: loop ?? false,
63
+ })
64
+ .then(({ frame: imageBitmap, audio }) => {
65
+ if (imageBitmap) {
66
+ onVideoFrame?.(imageBitmap);
67
+ canvasRef.current?.getContext('2d')?.drawImage(imageBitmap, 0, 0);
68
+ imageBitmap.close();
69
+ }
70
+ else if (window.remotion_videoEnabled) {
71
+ cancelRender(new Error('No video frame found'));
72
+ }
73
+ if (audio) {
74
+ registerRenderAsset({
75
+ type: 'inline-audio',
76
+ id,
77
+ audio: Array.from(audio.data),
78
+ sampleRate: audio.sampleRate,
79
+ numberOfChannels: audio.numberOfChannels,
80
+ frame: absoluteFrame,
81
+ timestamp: audio.timestamp,
82
+ duration: (audio.numberOfFrames / audio.sampleRate) * 1000000,
83
+ });
84
+ }
85
+ continueRender(newHandle);
86
+ })
87
+ .catch((error) => {
88
+ cancelRender(error);
89
+ });
90
+ return () => {
91
+ continueRender(newHandle);
92
+ unregisterRenderAsset(id);
93
+ };
94
+ }, [
95
+ absoluteFrame,
96
+ continueRender,
97
+ delayRender,
98
+ delayRenderRetries,
99
+ delayRenderTimeoutInMilliseconds,
100
+ environment.isClientSideRendering,
101
+ fps,
102
+ frame,
103
+ id,
104
+ logLevel,
105
+ onVideoFrame,
106
+ playbackRate,
107
+ registerRenderAsset,
108
+ shouldRenderAudio,
109
+ src,
110
+ unregisterRenderAsset,
111
+ volume,
112
+ loop,
113
+ ]);
114
+ return (_jsx("canvas", { ref: canvasRef, style: style, width: videoConfig.width, height: videoConfig.height }));
115
+ };
@@ -0,0 +1,3 @@
1
+ import React from 'react';
2
+ import type { VideoProps } from './props';
3
+ export declare const Video: React.FC<VideoProps>;
@@ -0,0 +1,37 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { useCallback } from 'react';
3
+ import { Internals, Sequence, useRemotionEnvironment } from 'remotion';
4
+ import { VideoForRendering } from './video-for-rendering';
5
+ const { validateMediaTrimProps, resolveTrimProps, validateMediaProps, VideoForPreview, } = Internals;
6
+ export const Video = (props) => {
7
+ // Should only destruct `trimBefore` and `trimAfter` from props,
8
+ // rest gets drilled down
9
+ const { trimBefore, trimAfter, name, pauseWhenBuffering, stack, showInTimeline, ...otherProps } = props;
10
+ const environment = useRemotionEnvironment();
11
+ const onDuration = useCallback(() => undefined, []);
12
+ if (typeof props.src !== 'string') {
13
+ throw new TypeError(`The \`<Video>\` tag requires a string for \`src\`, but got ${JSON.stringify(props.src)} instead.`);
14
+ }
15
+ validateMediaTrimProps({
16
+ startFrom: undefined,
17
+ endAt: undefined,
18
+ trimBefore,
19
+ trimAfter,
20
+ });
21
+ const { trimBeforeValue, trimAfterValue } = resolveTrimProps({
22
+ startFrom: undefined,
23
+ endAt: undefined,
24
+ trimBefore,
25
+ trimAfter,
26
+ });
27
+ if (typeof trimBeforeValue !== 'undefined' ||
28
+ typeof trimAfterValue !== 'undefined') {
29
+ return (_jsx(Sequence, { layout: "none", from: 0 - (trimBeforeValue ?? 0), showInTimeline: false, durationInFrames: trimAfterValue, name: name, children: _jsx(Video, { pauseWhenBuffering: pauseWhenBuffering ?? false, ...otherProps }) }));
30
+ }
31
+ validateMediaProps(props, 'Video');
32
+ if (environment.isRendering) {
33
+ return _jsx(VideoForRendering, { ...otherProps });
34
+ }
35
+ const { onAutoPlayError, onVideoFrame, crossOrigin, delayRenderRetries, delayRenderTimeoutInMilliseconds, ...propsForPreview } = otherProps;
36
+ return (_jsx(VideoForPreview, { _remotionInternalStack: stack ?? null, _remotionInternalNativeLoopPassed: false, onDuration: onDuration, onlyWarnForMediaSeekingError: true, pauseWhenBuffering: pauseWhenBuffering ?? false, showInTimeline: showInTimeline ?? true, onAutoPlayError: onAutoPlayError ?? undefined, onVideoFrame: onVideoFrame ?? null, crossOrigin: crossOrigin, ...propsForPreview }));
37
+ };
@@ -0,0 +1,16 @@
1
+ import type { PcmS16AudioData } from '../convert-audiodata/convert-audiodata';
2
+ import type { LogLevel } from '../log';
3
+ export declare const extractFrameViaBroadcastChannel: ({ src, timeInSeconds, logLevel, durationInSeconds, includeAudio, includeVideo, isClientSideRendering, volume, loop, }: {
4
+ src: string;
5
+ timeInSeconds: number;
6
+ durationInSeconds: number;
7
+ logLevel: LogLevel;
8
+ includeAudio: boolean;
9
+ includeVideo: boolean;
10
+ isClientSideRendering: boolean;
11
+ volume: number;
12
+ loop: boolean;
13
+ }) => Promise<{
14
+ frame: ImageBitmap | VideoFrame | null;
15
+ audio: PcmS16AudioData | null;
16
+ }>;