@remotion/media 4.0.356 → 4.0.357
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.
- package/dist/audio/audio-for-preview.d.ts +30 -0
- package/dist/audio/audio-for-preview.js +213 -0
- package/dist/audio/audio-for-rendering.js +32 -15
- package/dist/audio/audio.js +7 -49
- package/dist/audio/props.d.ts +8 -14
- package/dist/audio-extraction/audio-cache.d.ts +1 -1
- package/dist/audio-extraction/audio-cache.js +5 -1
- package/dist/audio-extraction/audio-iterator.d.ts +4 -1
- package/dist/audio-extraction/audio-iterator.js +22 -10
- package/dist/audio-extraction/audio-manager.d.ts +8 -37
- package/dist/audio-extraction/audio-manager.js +35 -8
- package/dist/audio-extraction/extract-audio.d.ts +9 -2
- package/dist/audio-extraction/extract-audio.js +28 -15
- package/dist/caches.d.ts +9 -44
- package/dist/convert-audiodata/apply-tonefrequency.js +0 -1
- package/dist/convert-audiodata/combine-audiodata.js +2 -23
- package/dist/convert-audiodata/convert-audiodata.d.ts +1 -5
- package/dist/convert-audiodata/convert-audiodata.js +16 -24
- package/dist/convert-audiodata/wsola.js +1 -1
- package/dist/esm/index.mjs +2681 -2162
- package/dist/extract-frame-and-audio.d.ts +6 -7
- package/dist/extract-frame-and-audio.js +28 -19
- package/dist/get-sink-weak.d.ts +1 -1
- package/dist/get-sink-weak.js +3 -11
- package/dist/get-sink.d.ts +13 -0
- package/dist/get-sink.js +15 -0
- package/dist/get-time-in-seconds.d.ts +10 -0
- package/dist/get-time-in-seconds.js +25 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/is-network-error.d.ts +6 -0
- package/dist/is-network-error.js +17 -0
- package/dist/render-timestamp-range.d.ts +1 -0
- package/dist/render-timestamp-range.js +9 -0
- package/dist/video/media-player.d.ts +28 -7
- package/dist/video/media-player.js +123 -58
- package/dist/video/props.d.ts +1 -0
- package/dist/video/resolve-playback-time.d.ts +8 -0
- package/dist/video/resolve-playback-time.js +22 -0
- package/dist/video/video-for-preview.d.ts +8 -0
- package/dist/video/video-for-preview.js +113 -90
- package/dist/video/video-for-rendering.d.ts +3 -0
- package/dist/video/video-for-rendering.js +58 -25
- package/dist/video/video.js +6 -10
- package/dist/video-extraction/extract-frame-via-broadcast-channel.d.ts +18 -6
- package/dist/video-extraction/extract-frame-via-broadcast-channel.js +21 -7
- package/dist/video-extraction/extract-frame.d.ts +20 -2
- package/dist/video-extraction/extract-frame.js +40 -9
- package/dist/video-extraction/get-frames-since-keyframe.d.ts +5 -3
- package/dist/video-extraction/get-frames-since-keyframe.js +7 -4
- package/dist/video-extraction/keyframe-bank.d.ts +3 -2
- package/dist/video-extraction/keyframe-bank.js +32 -12
- package/dist/video-extraction/keyframe-manager.d.ts +3 -8
- package/dist/video-extraction/keyframe-manager.js +25 -10
- package/package.json +4 -4
|
@@ -1,50 +1,53 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import { ALL_FORMATS, Input, UrlSource } from 'mediabunny';
|
|
3
2
|
import { useContext, useEffect, useMemo, useRef, useState } from 'react';
|
|
4
|
-
import { Internals,
|
|
3
|
+
import { Internals, useBufferState, useCurrentFrame, Video } from 'remotion';
|
|
5
4
|
import { MediaPlayer } from './media-player';
|
|
6
|
-
const { useUnsafeVideoConfig, Timeline, SharedAudioContext, useMediaMutedState, useMediaVolumeState, useFrameForVolumeProp, evaluateVolume, warnAboutTooHighVolume, usePreload, } = Internals;
|
|
7
|
-
const
|
|
8
|
-
let duration = mediaDuration;
|
|
9
|
-
if (typeof endAt !== 'undefined') {
|
|
10
|
-
duration = endAt;
|
|
11
|
-
}
|
|
12
|
-
if (typeof startFrom !== 'undefined') {
|
|
13
|
-
duration -= startFrom;
|
|
14
|
-
}
|
|
15
|
-
const actualDuration = duration / playbackRate;
|
|
16
|
-
return Math.floor(actualDuration);
|
|
17
|
-
};
|
|
18
|
-
const NewVideoForPreview = ({ src, style, playbackRate, logLevel, className, muted, volume, loopVolumeCurveBehavior, onVideoFrame, }) => {
|
|
5
|
+
const { useUnsafeVideoConfig, Timeline, SharedAudioContext, useMediaMutedState, useMediaVolumeState, useFrameForVolumeProp, evaluateVolume, warnAboutTooHighVolume, usePreload, useMediaInTimeline, SequenceContext, } = Internals;
|
|
6
|
+
const NewVideoForPreview = ({ src, style, playbackRate, logLevel, className, muted, volume, loopVolumeCurveBehavior, onVideoFrame, showInTimeline, loop, name, trimAfter, trimBefore, stack, disallowFallbackToOffthreadVideo, fallbackOffthreadVideoProps, audioStreamIndex, }) => {
|
|
19
7
|
const canvasRef = useRef(null);
|
|
20
8
|
const videoConfig = useUnsafeVideoConfig();
|
|
21
9
|
const frame = useCurrentFrame();
|
|
22
10
|
const mediaPlayerRef = useRef(null);
|
|
23
11
|
const [mediaPlayerReady, setMediaPlayerReady] = useState(false);
|
|
12
|
+
const [shouldFallbackToNativeVideo, setShouldFallbackToNativeVideo] = useState(false);
|
|
24
13
|
const [playing] = Timeline.usePlayingState();
|
|
25
14
|
const timelineContext = useContext(Timeline.TimelineContext);
|
|
26
15
|
const globalPlaybackRate = timelineContext.playbackRate;
|
|
27
16
|
const sharedAudioContext = useContext(SharedAudioContext);
|
|
28
17
|
const buffer = useBufferState();
|
|
29
|
-
const delayHandleRef = useRef(null);
|
|
30
18
|
const [mediaMuted] = useMediaMutedState();
|
|
31
19
|
const [mediaVolume] = useMediaVolumeState();
|
|
32
|
-
const volumePropFrame = useFrameForVolumeProp(loopVolumeCurveBehavior
|
|
20
|
+
const volumePropFrame = useFrameForVolumeProp(loopVolumeCurveBehavior);
|
|
33
21
|
const userPreferredVolume = evaluateVolume({
|
|
34
22
|
frame: volumePropFrame,
|
|
35
23
|
volume,
|
|
36
24
|
mediaVolume,
|
|
37
25
|
});
|
|
38
26
|
warnAboutTooHighVolume(userPreferredVolume);
|
|
27
|
+
const [timelineId] = useState(() => String(Math.random()));
|
|
28
|
+
const parentSequence = useContext(SequenceContext);
|
|
29
|
+
useMediaInTimeline({
|
|
30
|
+
volume,
|
|
31
|
+
mediaVolume,
|
|
32
|
+
mediaType: 'video',
|
|
33
|
+
src,
|
|
34
|
+
playbackRate,
|
|
35
|
+
displayName: name ?? null,
|
|
36
|
+
id: timelineId,
|
|
37
|
+
stack,
|
|
38
|
+
showInTimeline,
|
|
39
|
+
premountDisplay: parentSequence?.premountDisplay ?? null,
|
|
40
|
+
postmountDisplay: parentSequence?.postmountDisplay ?? null,
|
|
41
|
+
});
|
|
39
42
|
if (!videoConfig) {
|
|
40
43
|
throw new Error('No video config found');
|
|
41
44
|
}
|
|
42
45
|
if (!src) {
|
|
43
46
|
throw new TypeError('No `src` was passed to <NewVideoForPreview>.');
|
|
44
47
|
}
|
|
45
|
-
const
|
|
46
|
-
const
|
|
47
|
-
|
|
48
|
+
const currentTime = frame / videoConfig.fps;
|
|
49
|
+
const currentTimeRef = useRef(currentTime);
|
|
50
|
+
currentTimeRef.current = currentTime;
|
|
48
51
|
const preloadedSrc = usePreload(src);
|
|
49
52
|
useEffect(() => {
|
|
50
53
|
if (!canvasRef.current)
|
|
@@ -59,34 +62,84 @@ const NewVideoForPreview = ({ src, style, playbackRate, logLevel, className, mut
|
|
|
59
62
|
src: preloadedSrc,
|
|
60
63
|
logLevel,
|
|
61
64
|
sharedAudioContext: sharedAudioContext.audioContext,
|
|
65
|
+
loop,
|
|
66
|
+
trimAfterSeconds: trimAfter ? trimAfter / videoConfig.fps : undefined,
|
|
67
|
+
trimBeforeSeconds: trimBefore
|
|
68
|
+
? trimBefore / videoConfig.fps
|
|
69
|
+
: undefined,
|
|
70
|
+
playbackRate,
|
|
71
|
+
audioStreamIndex,
|
|
62
72
|
});
|
|
63
73
|
mediaPlayerRef.current = player;
|
|
64
74
|
player
|
|
65
|
-
.initialize(
|
|
66
|
-
.then(() => {
|
|
67
|
-
|
|
68
|
-
|
|
75
|
+
.initialize(currentTimeRef.current)
|
|
76
|
+
.then((result) => {
|
|
77
|
+
if (result.type === 'unknown-container-format') {
|
|
78
|
+
if (disallowFallbackToOffthreadVideo) {
|
|
79
|
+
throw new Error(`Unknown container format ${preloadedSrc}, and 'disallowFallbackToOffthreadVideo' was set.`);
|
|
80
|
+
}
|
|
81
|
+
Internals.Log.warn({ logLevel, tag: '@remotion/media' }, `Unknown container format for ${preloadedSrc} (Supported formats: https://www.remotion.dev/docs/mediabunny/formats), falling back to <OffthreadVideo>`);
|
|
82
|
+
setShouldFallbackToNativeVideo(true);
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
if (result.type === 'network-error') {
|
|
86
|
+
if (disallowFallbackToOffthreadVideo) {
|
|
87
|
+
throw new Error(`Network error fetching ${preloadedSrc}, and 'disallowFallbackToOffthreadVideo' was set.`);
|
|
88
|
+
}
|
|
89
|
+
Internals.Log.warn({ logLevel, tag: '@remotion/media' }, `Network error fetching ${preloadedSrc}, falling back to <OffthreadVideo>`);
|
|
90
|
+
setShouldFallbackToNativeVideo(true);
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
if (result.type === 'cannot-decode') {
|
|
94
|
+
if (disallowFallbackToOffthreadVideo) {
|
|
95
|
+
throw new Error(`Cannot decode ${preloadedSrc}, and 'disallowFallbackToOffthreadVideo' was set.`);
|
|
96
|
+
}
|
|
97
|
+
Internals.Log.warn({ logLevel, tag: '@remotion/media' }, `Cannot decode ${preloadedSrc}, falling back to <OffthreadVideo>`);
|
|
98
|
+
setShouldFallbackToNativeVideo(true);
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
if (result.type === 'no-tracks') {
|
|
102
|
+
if (disallowFallbackToOffthreadVideo) {
|
|
103
|
+
throw new Error(`No video or audio tracks found for ${preloadedSrc}, and 'disallowFallbackToOffthreadVideo' was set.`);
|
|
104
|
+
}
|
|
105
|
+
Internals.Log.warn({ logLevel, tag: '@remotion/media' }, `No video or audio tracks found for ${preloadedSrc}, falling back to <OffthreadVideo>`);
|
|
106
|
+
setShouldFallbackToNativeVideo(true);
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
if (result.type === 'success') {
|
|
110
|
+
setMediaPlayerReady(true);
|
|
111
|
+
}
|
|
69
112
|
})
|
|
70
113
|
.catch((error) => {
|
|
71
114
|
Internals.Log.error({ logLevel, tag: '@remotion/media' }, '[NewVideoForPreview] Failed to initialize MediaPlayer', error);
|
|
115
|
+
setShouldFallbackToNativeVideo(true);
|
|
72
116
|
});
|
|
73
117
|
}
|
|
74
118
|
catch (error) {
|
|
75
119
|
Internals.Log.error({ logLevel, tag: '@remotion/media' }, '[NewVideoForPreview] MediaPlayer initialization failed', error);
|
|
120
|
+
setShouldFallbackToNativeVideo(true);
|
|
76
121
|
}
|
|
77
122
|
return () => {
|
|
78
|
-
if (delayHandleRef.current) {
|
|
79
|
-
delayHandleRef.current.unblock();
|
|
80
|
-
delayHandleRef.current = null;
|
|
81
|
-
}
|
|
82
123
|
if (mediaPlayerRef.current) {
|
|
83
124
|
Internals.Log.trace({ logLevel, tag: '@remotion/media' }, `[NewVideoForPreview] Disposing MediaPlayer`);
|
|
84
125
|
mediaPlayerRef.current.dispose();
|
|
85
126
|
mediaPlayerRef.current = null;
|
|
86
127
|
}
|
|
87
128
|
setMediaPlayerReady(false);
|
|
129
|
+
setShouldFallbackToNativeVideo(false);
|
|
88
130
|
};
|
|
89
|
-
}, [
|
|
131
|
+
}, [
|
|
132
|
+
preloadedSrc,
|
|
133
|
+
logLevel,
|
|
134
|
+
sharedAudioContext,
|
|
135
|
+
loop,
|
|
136
|
+
trimAfter,
|
|
137
|
+
trimBefore,
|
|
138
|
+
videoConfig.fps,
|
|
139
|
+
playbackRate,
|
|
140
|
+
disallowFallbackToOffthreadVideo,
|
|
141
|
+
audioStreamIndex,
|
|
142
|
+
]);
|
|
90
143
|
const classNameValue = useMemo(() => {
|
|
91
144
|
return [Internals.OBJECTFIT_CONTAIN_CLASS_NAME, className]
|
|
92
145
|
.filter(Internals.truthy)
|
|
@@ -116,17 +169,25 @@ const NewVideoForPreview = ({ src, style, playbackRate, logLevel, className, mut
|
|
|
116
169
|
const mediaPlayer = mediaPlayerRef.current;
|
|
117
170
|
if (!mediaPlayer || !mediaPlayerReady)
|
|
118
171
|
return;
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
172
|
+
let currentBlock = null;
|
|
173
|
+
const unsubscribe = mediaPlayer.onBufferingChange((newBufferingState) => {
|
|
174
|
+
if (newBufferingState && !currentBlock) {
|
|
175
|
+
currentBlock = buffer.delayPlayback();
|
|
122
176
|
Internals.Log.trace({ logLevel, tag: '@remotion/media' }, '[NewVideoForPreview] MediaPlayer buffering - blocking Remotion playback');
|
|
123
177
|
}
|
|
124
|
-
else if (!newBufferingState &&
|
|
125
|
-
|
|
126
|
-
|
|
178
|
+
else if (!newBufferingState && currentBlock) {
|
|
179
|
+
currentBlock.unblock();
|
|
180
|
+
currentBlock = null;
|
|
127
181
|
Internals.Log.trace({ logLevel, tag: '@remotion/media' }, '[NewVideoForPreview] MediaPlayer unbuffering - unblocking Remotion playback');
|
|
128
182
|
}
|
|
129
183
|
});
|
|
184
|
+
return () => {
|
|
185
|
+
unsubscribe();
|
|
186
|
+
if (currentBlock) {
|
|
187
|
+
currentBlock.unblock();
|
|
188
|
+
currentBlock = null;
|
|
189
|
+
}
|
|
190
|
+
};
|
|
130
191
|
}, [mediaPlayerReady, buffer, logLevel]);
|
|
131
192
|
const effectiveMuted = muted || mediaMuted || userPreferredVolume <= 0;
|
|
132
193
|
useEffect(() => {
|
|
@@ -148,71 +209,33 @@ const NewVideoForPreview = ({ src, style, playbackRate, logLevel, className, mut
|
|
|
148
209
|
if (!mediaPlayer || !mediaPlayerReady) {
|
|
149
210
|
return;
|
|
150
211
|
}
|
|
151
|
-
mediaPlayer.setPlaybackRate(effectivePlaybackRate)
|
|
152
|
-
Internals.Log.error({ logLevel, tag: '@remotion/media' }, '[NewVideoForPreview] Failed to set playback rate', error);
|
|
153
|
-
});
|
|
212
|
+
mediaPlayer.setPlaybackRate(effectivePlaybackRate);
|
|
154
213
|
}, [effectivePlaybackRate, mediaPlayerReady, logLevel]);
|
|
155
214
|
useEffect(() => {
|
|
156
215
|
const mediaPlayer = mediaPlayerRef.current;
|
|
157
216
|
if (!mediaPlayer || !mediaPlayerReady) {
|
|
158
217
|
return;
|
|
159
218
|
}
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
}
|
|
163
|
-
}, [onVideoFrame, mediaPlayerReady]);
|
|
164
|
-
return (_jsx("canvas", { ref: canvasRef, width: videoConfig.width, height: videoConfig.height, style: style, className: classNameValue }));
|
|
165
|
-
};
|
|
166
|
-
const VideoForPreviewWithDuration = ({ className, durationInSeconds, logLevel, loopVolumeCurveBehavior, muted, onVideoFrame, playbackRate, src, style, volume, loop, name, trimAfter, trimBefore, }) => {
|
|
167
|
-
const { fps } = useVideoConfig();
|
|
168
|
-
if (loop) {
|
|
169
|
-
if (!Number.isFinite(durationInSeconds) || durationInSeconds === null) {
|
|
170
|
-
return (_jsx(VideoForPreviewWithDuration, { loop: false, className: className, durationInSeconds: durationInSeconds, logLevel: logLevel, loopVolumeCurveBehavior: loopVolumeCurveBehavior, muted: muted, onVideoFrame: onVideoFrame, playbackRate: playbackRate, src: src, style: style, volume: volume, name: name, trimAfter: trimAfter, trimBefore: trimBefore }));
|
|
171
|
-
}
|
|
172
|
-
const mediaDuration = durationInSeconds * fps;
|
|
173
|
-
return (_jsx(Loop, { durationInFrames: calculateLoopDuration({
|
|
174
|
-
endAt: trimAfter,
|
|
175
|
-
mediaDuration,
|
|
176
|
-
playbackRate: playbackRate ?? 1,
|
|
177
|
-
startFrom: trimBefore,
|
|
178
|
-
}), layout: "none", name: name, children: _jsx(VideoForPreviewWithDuration, { loop: false, className: className, durationInSeconds: durationInSeconds, logLevel: logLevel, loopVolumeCurveBehavior: loopVolumeCurveBehavior, muted: muted, onVideoFrame: onVideoFrame, playbackRate: playbackRate, src: src, style: style, volume: volume, name: name, trimAfter: trimAfter, trimBefore: trimBefore }) }));
|
|
179
|
-
}
|
|
180
|
-
return (_jsx(NewVideoForPreview, { src: src, style: style, playbackRate: playbackRate, logLevel: logLevel, muted: muted, volume: volume, loopVolumeCurveBehavior: loopVolumeCurveBehavior, onVideoFrame: onVideoFrame, className: className }));
|
|
181
|
-
};
|
|
182
|
-
export const VideoForPreview = ({ className, loop, src, logLevel, muted, name, volume, loopVolumeCurveBehavior, onVideoFrame, playbackRate, style, }) => {
|
|
183
|
-
const preloadedSrc = usePreload(src);
|
|
184
|
-
const [durationInSeconds, setDurationInSeconds] = useState(null);
|
|
219
|
+
mediaPlayer.setLoop(loop);
|
|
220
|
+
}, [loop, mediaPlayerReady]);
|
|
185
221
|
useEffect(() => {
|
|
186
|
-
|
|
222
|
+
const mediaPlayer = mediaPlayerRef.current;
|
|
223
|
+
if (!mediaPlayer || !mediaPlayerReady || !onVideoFrame) {
|
|
187
224
|
return;
|
|
188
225
|
}
|
|
189
|
-
|
|
190
|
-
const computeDuration = async () => {
|
|
191
|
-
const urlSource = new UrlSource(preloadedSrc);
|
|
192
|
-
const input = new Input({
|
|
193
|
-
source: urlSource,
|
|
194
|
-
formats: ALL_FORMATS,
|
|
195
|
-
});
|
|
196
|
-
try {
|
|
197
|
-
const duration = await input.computeDuration();
|
|
198
|
-
if (!cancelled) {
|
|
199
|
-
setDurationInSeconds(duration);
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
catch (error) {
|
|
203
|
-
Internals.Log.error({ logLevel, tag: '@remotion/media' }, '[VideoForPreview] Failed to compute duration', error);
|
|
204
|
-
}
|
|
205
|
-
finally {
|
|
206
|
-
input.dispose();
|
|
207
|
-
}
|
|
208
|
-
};
|
|
209
|
-
computeDuration();
|
|
226
|
+
const unsubscribe = mediaPlayer.onVideoFrame(onVideoFrame);
|
|
210
227
|
return () => {
|
|
211
|
-
|
|
228
|
+
unsubscribe();
|
|
212
229
|
};
|
|
213
|
-
}, [
|
|
214
|
-
if (
|
|
215
|
-
|
|
230
|
+
}, [onVideoFrame, mediaPlayerReady]);
|
|
231
|
+
if (shouldFallbackToNativeVideo && !disallowFallbackToOffthreadVideo) {
|
|
232
|
+
// <Video> will fallback to <VideoForPreview> anyway
|
|
233
|
+
// not using <OffthreadVideo> because it does not support looping
|
|
234
|
+
return (_jsx(Video, { src: src, style: style, className: className, muted: muted, volume: volume, trimAfter: trimAfter, trimBefore: trimBefore, playbackRate: playbackRate, loopVolumeCurveBehavior: loopVolumeCurveBehavior, name: name, loop: loop, showInTimeline: showInTimeline, stack: stack ?? undefined, ...fallbackOffthreadVideoProps }));
|
|
216
235
|
}
|
|
217
|
-
return (_jsx(
|
|
236
|
+
return (_jsx("canvas", { ref: canvasRef, width: videoConfig.width, height: videoConfig.height, style: style, className: classNameValue }));
|
|
237
|
+
};
|
|
238
|
+
export const VideoForPreview = ({ className, loop, src, logLevel, muted, name, volume, loopVolumeCurveBehavior, onVideoFrame, playbackRate, style, showInTimeline, trimAfter, trimBefore, stack, disallowFallbackToOffthreadVideo, fallbackOffthreadVideoProps, audioStreamIndex, }) => {
|
|
239
|
+
const preloadedSrc = usePreload(src);
|
|
240
|
+
return (_jsx(NewVideoForPreview, { className: className, logLevel: logLevel, muted: muted, onVideoFrame: onVideoFrame, playbackRate: playbackRate, src: preloadedSrc, style: style, volume: volume, name: name, trimAfter: trimAfter, trimBefore: trimBefore, loop: loop, loopVolumeCurveBehavior: loopVolumeCurveBehavior, showInTimeline: showInTimeline, stack: stack, disallowFallbackToOffthreadVideo: disallowFallbackToOffthreadVideo, fallbackOffthreadVideoProps: fallbackOffthreadVideoProps, audioStreamIndex: audioStreamIndex }));
|
|
218
241
|
};
|
|
@@ -19,6 +19,9 @@ type InnerVideoProps = {
|
|
|
19
19
|
readonly audioStreamIndex: number;
|
|
20
20
|
readonly disallowFallbackToOffthreadVideo: boolean;
|
|
21
21
|
readonly stack: string | undefined;
|
|
22
|
+
readonly toneFrequency: number;
|
|
23
|
+
readonly trimBeforeValue: number | undefined;
|
|
24
|
+
readonly trimAfterValue: number | undefined;
|
|
22
25
|
};
|
|
23
26
|
export declare const VideoForRendering: React.FC<InnerVideoProps>;
|
|
24
27
|
export {};
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import { useContext, useLayoutEffect, useMemo, useRef, useState, } from 'react';
|
|
3
|
-
import { cancelRender, Internals, useCurrentFrame, useDelayRender, useRemotionEnvironment, useVideoConfig, } from 'remotion';
|
|
3
|
+
import { cancelRender, Internals, Loop, random, useCurrentFrame, useDelayRender, useRemotionEnvironment, useVideoConfig, } from 'remotion';
|
|
4
|
+
import { calculateLoopDuration } from '../../../core/src/calculate-loop';
|
|
4
5
|
import { applyVolume } from '../convert-audiodata/apply-volume';
|
|
6
|
+
import { TARGET_SAMPLE_RATE } from '../convert-audiodata/resample-audiodata';
|
|
5
7
|
import { frameForVolumeProp } from '../looped-frame';
|
|
6
8
|
import { extractFrameViaBroadcastChannel } from '../video-extraction/extract-frame-via-broadcast-channel';
|
|
7
|
-
export const VideoForRendering = ({ volume: volumeProp, playbackRate, src, muted, loopVolumeCurveBehavior, delayRenderRetries, delayRenderTimeoutInMilliseconds, onVideoFrame, logLevel, loop, style, className, fallbackOffthreadVideoProps, audioStreamIndex, name, disallowFallbackToOffthreadVideo, stack, }) => {
|
|
9
|
+
export const VideoForRendering = ({ volume: volumeProp, playbackRate, src, muted, loopVolumeCurveBehavior, delayRenderRetries, delayRenderTimeoutInMilliseconds, onVideoFrame, logLevel, loop, style, className, fallbackOffthreadVideoProps, audioStreamIndex, name, disallowFallbackToOffthreadVideo, stack, toneFrequency, trimAfterValue, trimBeforeValue, }) => {
|
|
8
10
|
if (!src) {
|
|
9
11
|
throw new TypeError('No `src` was passed to <Video>.');
|
|
10
12
|
}
|
|
@@ -13,7 +15,15 @@ export const VideoForRendering = ({ volume: volumeProp, playbackRate, src, muted
|
|
|
13
15
|
const { fps } = useVideoConfig();
|
|
14
16
|
const { registerRenderAsset, unregisterRenderAsset } = useContext(Internals.RenderAssetManager);
|
|
15
17
|
const startsAt = Internals.useMediaStartsAt();
|
|
16
|
-
const
|
|
18
|
+
const sequenceContext = useContext(Internals.SequenceContext);
|
|
19
|
+
// Generate a string that's as unique as possible for this asset
|
|
20
|
+
// but at the same time the same on all threads
|
|
21
|
+
const id = useMemo(() => `media-video-${random(src)}-${sequenceContext?.cumulatedFrom}-${sequenceContext?.relativeFrom}-${sequenceContext?.durationInFrames}`, [
|
|
22
|
+
src,
|
|
23
|
+
sequenceContext?.cumulatedFrom,
|
|
24
|
+
sequenceContext?.relativeFrom,
|
|
25
|
+
sequenceContext?.durationInFrames,
|
|
26
|
+
]);
|
|
17
27
|
const environment = useRemotionEnvironment();
|
|
18
28
|
const { delayRender, continueRender } = useDelayRender();
|
|
19
29
|
const canvasRef = useRef(null);
|
|
@@ -25,10 +35,9 @@ export const VideoForRendering = ({ volume: volumeProp, playbackRate, src, muted
|
|
|
25
35
|
if (replaceWithOffthreadVideo) {
|
|
26
36
|
return;
|
|
27
37
|
}
|
|
28
|
-
const
|
|
29
|
-
const
|
|
30
|
-
const
|
|
31
|
-
const newHandle = delayRender(`Extracting frame number ${frame}`, {
|
|
38
|
+
const timestamp = frame / fps;
|
|
39
|
+
const durationInSeconds = 1 / fps;
|
|
40
|
+
const newHandle = delayRender(`Extracting frame at time ${timestamp}`, {
|
|
32
41
|
retries: delayRenderRetries ?? undefined,
|
|
33
42
|
timeoutInMilliseconds: delayRenderTimeoutInMilliseconds ?? undefined,
|
|
34
43
|
});
|
|
@@ -45,43 +54,48 @@ export const VideoForRendering = ({ volume: volumeProp, playbackRate, src, muted
|
|
|
45
54
|
src,
|
|
46
55
|
timeInSeconds: timestamp,
|
|
47
56
|
durationInSeconds,
|
|
48
|
-
playbackRate
|
|
49
|
-
logLevel
|
|
57
|
+
playbackRate,
|
|
58
|
+
logLevel,
|
|
50
59
|
includeAudio: shouldRenderAudio,
|
|
51
60
|
includeVideo: window.remotion_videoEnabled,
|
|
52
61
|
isClientSideRendering: environment.isClientSideRendering,
|
|
53
|
-
loop
|
|
54
|
-
audioStreamIndex
|
|
62
|
+
loop,
|
|
63
|
+
audioStreamIndex,
|
|
64
|
+
trimAfter: trimAfterValue,
|
|
65
|
+
trimBefore: trimBeforeValue,
|
|
66
|
+
fps,
|
|
55
67
|
})
|
|
56
68
|
.then((result) => {
|
|
57
|
-
if (result === 'unknown-container-format') {
|
|
69
|
+
if (result.type === 'unknown-container-format') {
|
|
58
70
|
if (disallowFallbackToOffthreadVideo) {
|
|
59
71
|
cancelRender(new Error(`Unknown container format ${src}, and 'disallowFallbackToOffthreadVideo' was set. Failing the render.`));
|
|
60
72
|
}
|
|
61
73
|
if (window.remotion_isMainTab) {
|
|
62
74
|
Internals.Log.info({ logLevel, tag: '@remotion/media' }, `Unknown container format for ${src} (Supported formats: https://www.remotion.dev/docs/mediabunny/formats), falling back to <OffthreadVideo>`);
|
|
63
75
|
}
|
|
64
|
-
setReplaceWithOffthreadVideo(
|
|
76
|
+
setReplaceWithOffthreadVideo({ durationInSeconds: null });
|
|
65
77
|
return;
|
|
66
78
|
}
|
|
67
|
-
if (result === 'cannot-decode') {
|
|
79
|
+
if (result.type === 'cannot-decode') {
|
|
68
80
|
if (disallowFallbackToOffthreadVideo) {
|
|
69
81
|
cancelRender(new Error(`Cannot decode ${src}, and 'disallowFallbackToOffthreadVideo' was set. Failing the render.`));
|
|
70
82
|
}
|
|
71
83
|
if (window.remotion_isMainTab) {
|
|
72
84
|
Internals.Log.info({ logLevel, tag: '@remotion/media' }, `Cannot decode ${src}, falling back to <OffthreadVideo>`);
|
|
73
85
|
}
|
|
74
|
-
setReplaceWithOffthreadVideo(
|
|
86
|
+
setReplaceWithOffthreadVideo({
|
|
87
|
+
durationInSeconds: result.durationInSeconds,
|
|
88
|
+
});
|
|
75
89
|
return;
|
|
76
90
|
}
|
|
77
|
-
if (result === 'network-error') {
|
|
91
|
+
if (result.type === 'network-error') {
|
|
78
92
|
if (disallowFallbackToOffthreadVideo) {
|
|
79
93
|
cancelRender(new Error(`Cannot decode ${src}, and 'disallowFallbackToOffthreadVideo' was set. Failing the render.`));
|
|
80
94
|
}
|
|
81
95
|
if (window.remotion_isMainTab) {
|
|
82
96
|
Internals.Log.info({ logLevel, tag: '@remotion/media' }, `Network error fetching ${src}, falling back to <OffthreadVideo>`);
|
|
83
97
|
}
|
|
84
|
-
setReplaceWithOffthreadVideo(
|
|
98
|
+
setReplaceWithOffthreadVideo({ durationInSeconds: null });
|
|
85
99
|
return;
|
|
86
100
|
}
|
|
87
101
|
const { frame: imageBitmap, audio, durationInSeconds: assetDurationInSeconds, } = result;
|
|
@@ -104,11 +118,17 @@ export const VideoForRendering = ({ volume: volumeProp, playbackRate, src, muted
|
|
|
104
118
|
imageBitmap.close();
|
|
105
119
|
}
|
|
106
120
|
else if (window.remotion_videoEnabled) {
|
|
107
|
-
|
|
121
|
+
// In the case of https://discord.com/channels/809501355504959528/809501355504959531/1424400511070765086
|
|
122
|
+
// A video that only starts at time 0.033sec
|
|
123
|
+
// we shall not crash here but clear the canvas
|
|
124
|
+
const context = canvasRef.current?.getContext('2d');
|
|
125
|
+
if (context) {
|
|
126
|
+
context.clearRect(0, 0, context.canvas.width, context.canvas.height);
|
|
127
|
+
}
|
|
108
128
|
}
|
|
109
129
|
const volumePropsFrame = frameForVolumeProp({
|
|
110
|
-
behavior: loopVolumeCurveBehavior
|
|
111
|
-
loop
|
|
130
|
+
behavior: loopVolumeCurveBehavior,
|
|
131
|
+
loop,
|
|
112
132
|
assetDurationInSeconds: assetDurationInSeconds ?? 0,
|
|
113
133
|
fps,
|
|
114
134
|
frame,
|
|
@@ -126,11 +146,10 @@ export const VideoForRendering = ({ volume: volumeProp, playbackRate, src, muted
|
|
|
126
146
|
type: 'inline-audio',
|
|
127
147
|
id,
|
|
128
148
|
audio: Array.from(audio.data),
|
|
129
|
-
sampleRate: audio.sampleRate,
|
|
130
|
-
numberOfChannels: audio.numberOfChannels,
|
|
131
149
|
frame: absoluteFrame,
|
|
132
150
|
timestamp: audio.timestamp,
|
|
133
|
-
duration: (audio.numberOfFrames /
|
|
151
|
+
duration: (audio.numberOfFrames / TARGET_SAMPLE_RATE) * 1000000,
|
|
152
|
+
toneFrequency,
|
|
134
153
|
});
|
|
135
154
|
}
|
|
136
155
|
continueRender(newHandle);
|
|
@@ -166,6 +185,9 @@ export const VideoForRendering = ({ volume: volumeProp, playbackRate, src, muted
|
|
|
166
185
|
replaceWithOffthreadVideo,
|
|
167
186
|
audioStreamIndex,
|
|
168
187
|
disallowFallbackToOffthreadVideo,
|
|
188
|
+
toneFrequency,
|
|
189
|
+
trimAfterValue,
|
|
190
|
+
trimBeforeValue,
|
|
169
191
|
]);
|
|
170
192
|
const classNameValue = useMemo(() => {
|
|
171
193
|
return [Internals.OBJECTFIT_CONTAIN_CLASS_NAME, className]
|
|
@@ -173,10 +195,21 @@ export const VideoForRendering = ({ volume: volumeProp, playbackRate, src, muted
|
|
|
173
195
|
.join(' ');
|
|
174
196
|
}, [className]);
|
|
175
197
|
if (replaceWithOffthreadVideo) {
|
|
176
|
-
|
|
177
|
-
return (_jsx(Internals.InnerOffthreadVideo, { src: src, playbackRate: playbackRate ?? 1, muted: muted ?? false, acceptableTimeShiftInSeconds: fallbackOffthreadVideoProps?.acceptableTimeShiftInSeconds, loopVolumeCurveBehavior: loopVolumeCurveBehavior ?? 'repeat', delayRenderRetries: delayRenderRetries ?? undefined, delayRenderTimeoutInMilliseconds: delayRenderTimeoutInMilliseconds ?? undefined, style: style, allowAmplificationDuringRender: true, transparent: fallbackOffthreadVideoProps?.transparent ?? false, toneMapped: fallbackOffthreadVideoProps?.toneMapped ?? true, audioStreamIndex: audioStreamIndex ?? 0, name: name, className: className, onVideoFrame: onVideoFrame, volume: volumeProp, id: id, onError: fallbackOffthreadVideoProps?.onError, toneFrequency: fallbackOffthreadVideoProps?.toneFrequency ?? 1,
|
|
198
|
+
const fallback = (_jsx(Internals.InnerOffthreadVideo, { src: src, playbackRate: playbackRate ?? 1, muted: muted ?? false, acceptableTimeShiftInSeconds: fallbackOffthreadVideoProps?.acceptableTimeShiftInSeconds, loopVolumeCurveBehavior: loopVolumeCurveBehavior ?? 'repeat', delayRenderRetries: delayRenderRetries ?? undefined, delayRenderTimeoutInMilliseconds: delayRenderTimeoutInMilliseconds ?? undefined, style: style, allowAmplificationDuringRender: true, transparent: fallbackOffthreadVideoProps?.transparent ?? false, toneMapped: fallbackOffthreadVideoProps?.toneMapped ?? true, audioStreamIndex: audioStreamIndex ?? 0, name: name, className: className, onVideoFrame: onVideoFrame, volume: volumeProp, id: id, onError: fallbackOffthreadVideoProps?.onError, toneFrequency: fallbackOffthreadVideoProps?.toneFrequency ?? 1,
|
|
178
199
|
// these shouldn't matter during rendering / should not appear at all
|
|
179
200
|
showInTimeline: false, crossOrigin: undefined, onAutoPlayError: () => undefined, pauseWhenBuffering: false, trimAfter: undefined, trimBefore: undefined, useWebAudioApi: false, startFrom: undefined, endAt: undefined, stack: stack, _remotionInternalNativeLoopPassed: false }));
|
|
201
|
+
if (loop) {
|
|
202
|
+
if (!replaceWithOffthreadVideo.durationInSeconds) {
|
|
203
|
+
cancelRender(new Error(`Cannot render video ${src}: @remotion/media was unable to render, and fell back to <OffthreadVideo>. Also, "loop" was set, but <OffthreadVideo> does not support looping and @remotion/media could also not determine the duration of the video.`));
|
|
204
|
+
}
|
|
205
|
+
return (_jsx(Loop, { layout: "none", durationInFrames: calculateLoopDuration({
|
|
206
|
+
trimAfter: trimAfterValue,
|
|
207
|
+
mediaDurationInFrames: replaceWithOffthreadVideo.durationInSeconds * fps,
|
|
208
|
+
playbackRate,
|
|
209
|
+
trimBefore: trimBeforeValue,
|
|
210
|
+
}), children: fallback }));
|
|
211
|
+
}
|
|
212
|
+
return fallback;
|
|
180
213
|
}
|
|
181
214
|
return _jsx("canvas", { ref: canvasRef, style: style, className: classNameValue });
|
|
182
215
|
};
|
package/dist/video/video.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import { Internals,
|
|
2
|
+
import { Internals, useRemotionEnvironment } from 'remotion';
|
|
3
3
|
import { VideoForPreview } from './video-for-preview';
|
|
4
4
|
import { VideoForRendering } from './video-for-rendering';
|
|
5
5
|
const { validateMediaTrimProps, resolveTrimProps, validateMediaProps } = Internals;
|
|
6
|
-
const InnerVideo = ({ src, audioStreamIndex, className, delayRenderRetries, delayRenderTimeoutInMilliseconds, disallowFallbackToOffthreadVideo, fallbackOffthreadVideoProps, logLevel, loop, loopVolumeCurveBehavior, muted, name, onVideoFrame, playbackRate, style, trimAfter, trimBefore, volume,
|
|
6
|
+
const InnerVideo = ({ src, audioStreamIndex, className, delayRenderRetries, delayRenderTimeoutInMilliseconds, disallowFallbackToOffthreadVideo, fallbackOffthreadVideoProps, logLevel, loop, loopVolumeCurveBehavior, muted, name, onVideoFrame, playbackRate, style, trimAfter, trimBefore, volume, stack, toneFrequency, showInTimeline, }) => {
|
|
7
7
|
const environment = useRemotionEnvironment();
|
|
8
8
|
if (typeof src !== 'string') {
|
|
9
9
|
throw new TypeError(`The \`<Video>\` tag requires a string for \`src\`, but got ${JSON.stringify(src)} instead.`);
|
|
@@ -20,17 +20,13 @@ const InnerVideo = ({ src, audioStreamIndex, className, delayRenderRetries, dela
|
|
|
20
20
|
trimBefore,
|
|
21
21
|
trimAfter,
|
|
22
22
|
});
|
|
23
|
-
if (typeof trimBeforeValue !== 'undefined' ||
|
|
24
|
-
typeof trimAfterValue !== 'undefined') {
|
|
25
|
-
return (_jsx(Sequence, { layout: "none", from: 0 - (trimBeforeValue ?? 0), showInTimeline: false, durationInFrames: trimAfterValue, name: name, children: _jsx(InnerVideo, { audioStreamIndex: audioStreamIndex, className: className, delayRenderRetries: delayRenderRetries, delayRenderTimeoutInMilliseconds: delayRenderTimeoutInMilliseconds, disallowFallbackToOffthreadVideo: disallowFallbackToOffthreadVideo, name: name, fallbackOffthreadVideoProps: fallbackOffthreadVideoProps, logLevel: logLevel, loop: loop, loopVolumeCurveBehavior: loopVolumeCurveBehavior, muted: muted, onVideoFrame: onVideoFrame, playbackRate: playbackRate, src: src, stack: stack, style: style, volume: volume, trimAfter: undefined, trimBefore: undefined, showInTimeline: showInTimeline }) }));
|
|
26
|
-
}
|
|
27
23
|
validateMediaProps({ playbackRate, volume }, 'Video');
|
|
28
24
|
if (environment.isRendering) {
|
|
29
|
-
return (_jsx(VideoForRendering, { audioStreamIndex: audioStreamIndex ?? 0, className: className, delayRenderRetries: delayRenderRetries ?? null, delayRenderTimeoutInMilliseconds: delayRenderTimeoutInMilliseconds ?? null, disallowFallbackToOffthreadVideo: disallowFallbackToOffthreadVideo ?? false, name: name, fallbackOffthreadVideoProps: fallbackOffthreadVideoProps, logLevel: logLevel, loop: loop, loopVolumeCurveBehavior: loopVolumeCurveBehavior, muted: muted, onVideoFrame: onVideoFrame, playbackRate: playbackRate, src: src, stack: stack, style: style, volume: volume }));
|
|
25
|
+
return (_jsx(VideoForRendering, { audioStreamIndex: audioStreamIndex ?? 0, className: className, delayRenderRetries: delayRenderRetries ?? null, delayRenderTimeoutInMilliseconds: delayRenderTimeoutInMilliseconds ?? null, disallowFallbackToOffthreadVideo: disallowFallbackToOffthreadVideo ?? false, name: name, fallbackOffthreadVideoProps: fallbackOffthreadVideoProps, logLevel: logLevel, loop: loop, loopVolumeCurveBehavior: loopVolumeCurveBehavior, muted: muted, onVideoFrame: onVideoFrame, playbackRate: playbackRate, src: src, stack: stack, style: style, volume: volume, toneFrequency: toneFrequency, trimAfterValue: trimAfterValue, trimBeforeValue: trimBeforeValue }));
|
|
30
26
|
}
|
|
31
|
-
return (_jsx(VideoForPreview, { className: className, name: name, logLevel: logLevel, loop: loop, loopVolumeCurveBehavior: loopVolumeCurveBehavior, muted: muted, onVideoFrame: onVideoFrame, playbackRate: playbackRate, src: src, style: style, volume: volume }));
|
|
27
|
+
return (_jsx(VideoForPreview, { audioStreamIndex: audioStreamIndex ?? 0, className: className, name: name, logLevel: logLevel, loop: loop, loopVolumeCurveBehavior: loopVolumeCurveBehavior, muted: muted, onVideoFrame: onVideoFrame, playbackRate: playbackRate, src: src, style: style, volume: volume, showInTimeline: showInTimeline, trimAfter: trimAfterValue, trimBefore: trimBeforeValue, stack: stack ?? null, disallowFallbackToOffthreadVideo: disallowFallbackToOffthreadVideo, fallbackOffthreadVideoProps: fallbackOffthreadVideoProps }));
|
|
32
28
|
};
|
|
33
|
-
export const Video = ({ src, audioStreamIndex, className, delayRenderRetries, delayRenderTimeoutInMilliseconds, disallowFallbackToOffthreadVideo, fallbackOffthreadVideoProps, logLevel, loop, loopVolumeCurveBehavior, muted, name, onVideoFrame, playbackRate, showInTimeline, style, trimAfter, trimBefore, volume, stack, }) => {
|
|
34
|
-
return (_jsx(InnerVideo, { audioStreamIndex: audioStreamIndex ?? 0, className: className, delayRenderRetries: delayRenderRetries ?? null, delayRenderTimeoutInMilliseconds: delayRenderTimeoutInMilliseconds ?? null, disallowFallbackToOffthreadVideo: disallowFallbackToOffthreadVideo ?? false, fallbackOffthreadVideoProps: fallbackOffthreadVideoProps ?? {}, logLevel: logLevel ??
|
|
29
|
+
export const Video = ({ src, audioStreamIndex, className, delayRenderRetries, delayRenderTimeoutInMilliseconds, disallowFallbackToOffthreadVideo, fallbackOffthreadVideoProps, logLevel, loop, loopVolumeCurveBehavior, muted, name, onVideoFrame, playbackRate, showInTimeline, style, trimAfter, trimBefore, volume, stack, toneFrequency, }) => {
|
|
30
|
+
return (_jsx(InnerVideo, { audioStreamIndex: audioStreamIndex ?? 0, className: className, delayRenderRetries: delayRenderRetries ?? null, delayRenderTimeoutInMilliseconds: delayRenderTimeoutInMilliseconds ?? null, disallowFallbackToOffthreadVideo: disallowFallbackToOffthreadVideo ?? false, fallbackOffthreadVideoProps: fallbackOffthreadVideoProps ?? {}, logLevel: logLevel ?? window.remotion_logLevel, loop: loop ?? false, loopVolumeCurveBehavior: loopVolumeCurveBehavior ?? 'repeat', muted: muted ?? false, name: name, onVideoFrame: onVideoFrame, playbackRate: playbackRate ?? 1, showInTimeline: showInTimeline ?? true, src: src, style: style ?? {}, trimAfter: trimAfter, trimBefore: trimBefore, volume: volume ?? 1, toneFrequency: toneFrequency ?? 1, stack: stack }));
|
|
35
31
|
};
|
|
36
32
|
Internals.addSequenceStackTraces(Video);
|
|
@@ -1,6 +1,19 @@
|
|
|
1
1
|
import { type LogLevel } from 'remotion';
|
|
2
2
|
import type { PcmS16AudioData } from '../convert-audiodata/convert-audiodata';
|
|
3
|
-
export
|
|
3
|
+
export type ExtractFrameViaBroadcastChannelResult = {
|
|
4
|
+
type: 'success';
|
|
5
|
+
frame: ImageBitmap | VideoFrame | null;
|
|
6
|
+
audio: PcmS16AudioData | null;
|
|
7
|
+
durationInSeconds: number | null;
|
|
8
|
+
} | {
|
|
9
|
+
type: 'cannot-decode';
|
|
10
|
+
durationInSeconds: number | null;
|
|
11
|
+
} | {
|
|
12
|
+
type: 'network-error';
|
|
13
|
+
} | {
|
|
14
|
+
type: 'unknown-container-format';
|
|
15
|
+
};
|
|
16
|
+
export declare const extractFrameViaBroadcastChannel: ({ src, timeInSeconds, logLevel, durationInSeconds, playbackRate, includeAudio, includeVideo, isClientSideRendering, loop, audioStreamIndex, trimAfter, trimBefore, fps, }: {
|
|
4
17
|
src: string;
|
|
5
18
|
timeInSeconds: number;
|
|
6
19
|
durationInSeconds: number;
|
|
@@ -11,8 +24,7 @@ export declare const extractFrameViaBroadcastChannel: ({ src, timeInSeconds, log
|
|
|
11
24
|
isClientSideRendering: boolean;
|
|
12
25
|
loop: boolean;
|
|
13
26
|
audioStreamIndex: number;
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
} | "cannot-decode" | "network-error" | "unknown-container-format">;
|
|
27
|
+
trimAfter: number | undefined;
|
|
28
|
+
trimBefore: number | undefined;
|
|
29
|
+
fps: number;
|
|
30
|
+
}) => Promise<ExtractFrameViaBroadcastChannelResult>;
|
|
@@ -15,16 +15,20 @@ if (window.remotion_broadcastChannel && window.remotion_isMainTab) {
|
|
|
15
15
|
includeVideo: data.includeVideo,
|
|
16
16
|
loop: data.loop,
|
|
17
17
|
audioStreamIndex: data.audioStreamIndex,
|
|
18
|
+
trimAfter: data.trimAfter,
|
|
19
|
+
trimBefore: data.trimBefore,
|
|
20
|
+
fps: data.fps,
|
|
18
21
|
});
|
|
19
|
-
if (result === 'cannot-decode') {
|
|
22
|
+
if (result.type === 'cannot-decode') {
|
|
20
23
|
const cannotDecodeResponse = {
|
|
21
24
|
type: 'response-cannot-decode',
|
|
22
25
|
id: data.id,
|
|
26
|
+
durationInSeconds: result.durationInSeconds,
|
|
23
27
|
};
|
|
24
28
|
window.remotion_broadcastChannel.postMessage(cannotDecodeResponse);
|
|
25
29
|
return;
|
|
26
30
|
}
|
|
27
|
-
if (result === 'network-error') {
|
|
31
|
+
if (result.type === 'network-error') {
|
|
28
32
|
const networkErrorResponse = {
|
|
29
33
|
type: 'response-network-error',
|
|
30
34
|
id: data.id,
|
|
@@ -32,7 +36,7 @@ if (window.remotion_broadcastChannel && window.remotion_isMainTab) {
|
|
|
32
36
|
window.remotion_broadcastChannel.postMessage(networkErrorResponse);
|
|
33
37
|
return;
|
|
34
38
|
}
|
|
35
|
-
if (result === 'unknown-container-format') {
|
|
39
|
+
if (result.type === 'unknown-container-format') {
|
|
36
40
|
const unknownContainerFormatResponse = {
|
|
37
41
|
type: 'response-unknown-container-format',
|
|
38
42
|
id: data.id,
|
|
@@ -72,7 +76,7 @@ if (window.remotion_broadcastChannel && window.remotion_isMainTab) {
|
|
|
72
76
|
}
|
|
73
77
|
});
|
|
74
78
|
}
|
|
75
|
-
export const extractFrameViaBroadcastChannel = ({ src, timeInSeconds, logLevel, durationInSeconds, playbackRate, includeAudio, includeVideo, isClientSideRendering, loop, audioStreamIndex, }) => {
|
|
79
|
+
export const extractFrameViaBroadcastChannel = ({ src, timeInSeconds, logLevel, durationInSeconds, playbackRate, includeAudio, includeVideo, isClientSideRendering, loop, audioStreamIndex, trimAfter, trimBefore, fps, }) => {
|
|
76
80
|
if (isClientSideRendering || window.remotion_isMainTab) {
|
|
77
81
|
return extractFrameAndAudio({
|
|
78
82
|
logLevel,
|
|
@@ -84,6 +88,9 @@ export const extractFrameViaBroadcastChannel = ({ src, timeInSeconds, logLevel,
|
|
|
84
88
|
includeVideo,
|
|
85
89
|
loop,
|
|
86
90
|
audioStreamIndex,
|
|
91
|
+
trimAfter,
|
|
92
|
+
trimBefore,
|
|
93
|
+
fps,
|
|
87
94
|
});
|
|
88
95
|
}
|
|
89
96
|
const requestId = crypto.randomUUID();
|
|
@@ -98,6 +105,7 @@ export const extractFrameViaBroadcastChannel = ({ src, timeInSeconds, logLevel,
|
|
|
98
105
|
}
|
|
99
106
|
if (data.type === 'response-success') {
|
|
100
107
|
resolve({
|
|
108
|
+
type: 'success',
|
|
101
109
|
frame: data.frame ? data.frame : null,
|
|
102
110
|
audio: data.audio ? data.audio : null,
|
|
103
111
|
durationInSeconds: data.durationInSeconds
|
|
@@ -113,17 +121,20 @@ export const extractFrameViaBroadcastChannel = ({ src, timeInSeconds, logLevel,
|
|
|
113
121
|
return;
|
|
114
122
|
}
|
|
115
123
|
if (data.type === 'response-cannot-decode') {
|
|
116
|
-
resolve(
|
|
124
|
+
resolve({
|
|
125
|
+
type: 'cannot-decode',
|
|
126
|
+
durationInSeconds: data.durationInSeconds,
|
|
127
|
+
});
|
|
117
128
|
window.remotion_broadcastChannel.removeEventListener('message', onMessage);
|
|
118
129
|
return;
|
|
119
130
|
}
|
|
120
131
|
if (data.type === 'response-network-error') {
|
|
121
|
-
resolve('network-error');
|
|
132
|
+
resolve({ type: 'network-error' });
|
|
122
133
|
window.remotion_broadcastChannel.removeEventListener('message', onMessage);
|
|
123
134
|
return;
|
|
124
135
|
}
|
|
125
136
|
if (data.type === 'response-unknown-container-format') {
|
|
126
|
-
resolve('unknown-container-format');
|
|
137
|
+
resolve({ type: 'unknown-container-format' });
|
|
127
138
|
window.remotion_broadcastChannel.removeEventListener('message', onMessage);
|
|
128
139
|
return;
|
|
129
140
|
}
|
|
@@ -143,6 +154,9 @@ export const extractFrameViaBroadcastChannel = ({ src, timeInSeconds, logLevel,
|
|
|
143
154
|
includeVideo,
|
|
144
155
|
loop,
|
|
145
156
|
audioStreamIndex,
|
|
157
|
+
trimAfter,
|
|
158
|
+
trimBefore,
|
|
159
|
+
fps,
|
|
146
160
|
};
|
|
147
161
|
window.remotion_broadcastChannel.postMessage(request);
|
|
148
162
|
let timeoutId;
|