@remotion/media 4.0.362 → 4.0.364
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.js +4 -1
- package/dist/audio/audio-for-rendering.js +14 -5
- package/dist/audio/audio-iterator.d.ts +11 -0
- package/dist/audio/audio-iterator.js +24 -0
- package/dist/audio/audio-preview-iterator.d.ts +14 -0
- package/dist/audio/audio-preview-iterator.js +43 -0
- package/dist/audio/audio.js +0 -1
- package/dist/caches.js +4 -2
- package/dist/debug-overlay/preview-overlay.d.ts +5 -0
- package/dist/debug-overlay/preview-overlay.js +13 -0
- package/dist/esm/index.mjs +702 -517
- package/dist/helpers/round-to-4-digits.d.ts +1 -0
- package/dist/helpers/round-to-4-digits.js +4 -0
- package/dist/media-player.d.ts +95 -0
- package/dist/media-player.js +496 -0
- package/dist/video/props.d.ts +1 -0
- package/dist/video/video-for-preview.d.ts +3 -2
- package/dist/video/video-for-preview.js +25 -14
- package/dist/video/video-for-rendering.js +8 -6
- package/dist/video/video-preview-iterator.d.ts +14 -0
- package/dist/video/video-preview-iterator.js +122 -0
- package/dist/video/video.js +5 -4
- package/dist/video-extraction/extract-frame-via-broadcast-channel.js +3 -1
- package/dist/video-extraction/get-frames-since-keyframe.d.ts +2 -1
- package/dist/video-extraction/get-frames-since-keyframe.js +2 -1
- package/dist/video-extraction/keyframe-bank.d.ts +7 -4
- package/dist/video-extraction/keyframe-bank.js +37 -34
- package/dist/video-extraction/keyframe-manager.js +18 -7
- package/package.json +4 -4
package/dist/esm/index.mjs
CHANGED
|
@@ -10,171 +10,7 @@ import {
|
|
|
10
10
|
useCurrentFrame as useCurrentFrame2
|
|
11
11
|
} from "remotion";
|
|
12
12
|
|
|
13
|
-
// src/
|
|
14
|
-
import { useMemo } from "react";
|
|
15
|
-
import { Internals, useVideoConfig } from "remotion";
|
|
16
|
-
var useLoopDisplay = ({
|
|
17
|
-
loop,
|
|
18
|
-
mediaDurationInSeconds,
|
|
19
|
-
playbackRate,
|
|
20
|
-
trimAfter,
|
|
21
|
-
trimBefore
|
|
22
|
-
}) => {
|
|
23
|
-
const { durationInFrames: compDuration, fps } = useVideoConfig();
|
|
24
|
-
const loopDisplay = useMemo(() => {
|
|
25
|
-
if (!loop || !mediaDurationInSeconds) {
|
|
26
|
-
return;
|
|
27
|
-
}
|
|
28
|
-
const durationInFrames = Internals.calculateMediaDuration({
|
|
29
|
-
mediaDurationInFrames: mediaDurationInSeconds * fps,
|
|
30
|
-
playbackRate,
|
|
31
|
-
trimAfter,
|
|
32
|
-
trimBefore
|
|
33
|
-
});
|
|
34
|
-
const maxTimes = compDuration / durationInFrames;
|
|
35
|
-
return {
|
|
36
|
-
numberOfTimes: maxTimes,
|
|
37
|
-
startOffset: 0,
|
|
38
|
-
durationInFrames
|
|
39
|
-
};
|
|
40
|
-
}, [
|
|
41
|
-
compDuration,
|
|
42
|
-
fps,
|
|
43
|
-
loop,
|
|
44
|
-
mediaDurationInSeconds,
|
|
45
|
-
playbackRate,
|
|
46
|
-
trimAfter,
|
|
47
|
-
trimBefore
|
|
48
|
-
]);
|
|
49
|
-
return loopDisplay;
|
|
50
|
-
};
|
|
51
|
-
|
|
52
|
-
// src/use-media-in-timeline.ts
|
|
53
|
-
import { useContext, useEffect, useState } from "react";
|
|
54
|
-
import { Internals as Internals2, useCurrentFrame } from "remotion";
|
|
55
|
-
var useMediaInTimeline = ({
|
|
56
|
-
volume,
|
|
57
|
-
mediaVolume,
|
|
58
|
-
src,
|
|
59
|
-
mediaType,
|
|
60
|
-
playbackRate,
|
|
61
|
-
displayName,
|
|
62
|
-
stack,
|
|
63
|
-
showInTimeline,
|
|
64
|
-
premountDisplay,
|
|
65
|
-
postmountDisplay,
|
|
66
|
-
loopDisplay,
|
|
67
|
-
trimBefore,
|
|
68
|
-
trimAfter
|
|
69
|
-
}) => {
|
|
70
|
-
const parentSequence = useContext(Internals2.SequenceContext);
|
|
71
|
-
const startsAt = Internals2.useMediaStartsAt();
|
|
72
|
-
const { registerSequence, unregisterSequence } = useContext(Internals2.SequenceManager);
|
|
73
|
-
const [sequenceId] = useState(() => String(Math.random()));
|
|
74
|
-
const [mediaId] = useState(() => String(Math.random()));
|
|
75
|
-
const frame = useCurrentFrame();
|
|
76
|
-
const {
|
|
77
|
-
volumes,
|
|
78
|
-
duration,
|
|
79
|
-
doesVolumeChange,
|
|
80
|
-
nonce,
|
|
81
|
-
rootId,
|
|
82
|
-
isStudio,
|
|
83
|
-
finalDisplayName
|
|
84
|
-
} = Internals2.useBasicMediaInTimeline({
|
|
85
|
-
volume,
|
|
86
|
-
mediaVolume,
|
|
87
|
-
mediaType,
|
|
88
|
-
src,
|
|
89
|
-
displayName,
|
|
90
|
-
trimBefore,
|
|
91
|
-
trimAfter,
|
|
92
|
-
playbackRate
|
|
93
|
-
});
|
|
94
|
-
useEffect(() => {
|
|
95
|
-
if (!src) {
|
|
96
|
-
throw new Error("No src passed");
|
|
97
|
-
}
|
|
98
|
-
if (!isStudio && window.process?.env?.NODE_ENV !== "test") {
|
|
99
|
-
return;
|
|
100
|
-
}
|
|
101
|
-
if (!showInTimeline) {
|
|
102
|
-
return;
|
|
103
|
-
}
|
|
104
|
-
const loopIteration = loopDisplay ? Math.floor(frame / loopDisplay.durationInFrames) : 0;
|
|
105
|
-
if (loopDisplay) {
|
|
106
|
-
registerSequence({
|
|
107
|
-
type: "sequence",
|
|
108
|
-
premountDisplay,
|
|
109
|
-
postmountDisplay,
|
|
110
|
-
parent: parentSequence?.id ?? null,
|
|
111
|
-
displayName: finalDisplayName,
|
|
112
|
-
rootId,
|
|
113
|
-
showInTimeline: true,
|
|
114
|
-
nonce,
|
|
115
|
-
loopDisplay,
|
|
116
|
-
stack,
|
|
117
|
-
from: 0,
|
|
118
|
-
duration,
|
|
119
|
-
id: sequenceId
|
|
120
|
-
});
|
|
121
|
-
}
|
|
122
|
-
registerSequence({
|
|
123
|
-
type: mediaType,
|
|
124
|
-
src,
|
|
125
|
-
id: mediaId,
|
|
126
|
-
duration: loopDisplay?.durationInFrames ?? duration,
|
|
127
|
-
from: loopDisplay ? loopIteration * loopDisplay.durationInFrames : 0,
|
|
128
|
-
parent: loopDisplay ? sequenceId : parentSequence?.id ?? null,
|
|
129
|
-
displayName: finalDisplayName,
|
|
130
|
-
rootId,
|
|
131
|
-
volume: volumes,
|
|
132
|
-
showInTimeline: true,
|
|
133
|
-
nonce,
|
|
134
|
-
startMediaFrom: 0 - startsAt,
|
|
135
|
-
doesVolumeChange,
|
|
136
|
-
loopDisplay: undefined,
|
|
137
|
-
playbackRate,
|
|
138
|
-
stack,
|
|
139
|
-
premountDisplay: null,
|
|
140
|
-
postmountDisplay: null
|
|
141
|
-
});
|
|
142
|
-
return () => {
|
|
143
|
-
if (loopDisplay) {
|
|
144
|
-
unregisterSequence(sequenceId);
|
|
145
|
-
}
|
|
146
|
-
unregisterSequence(mediaId);
|
|
147
|
-
};
|
|
148
|
-
}, [
|
|
149
|
-
doesVolumeChange,
|
|
150
|
-
duration,
|
|
151
|
-
finalDisplayName,
|
|
152
|
-
isStudio,
|
|
153
|
-
loopDisplay,
|
|
154
|
-
mediaId,
|
|
155
|
-
mediaType,
|
|
156
|
-
nonce,
|
|
157
|
-
parentSequence?.id,
|
|
158
|
-
playbackRate,
|
|
159
|
-
postmountDisplay,
|
|
160
|
-
premountDisplay,
|
|
161
|
-
registerSequence,
|
|
162
|
-
rootId,
|
|
163
|
-
sequenceId,
|
|
164
|
-
showInTimeline,
|
|
165
|
-
src,
|
|
166
|
-
stack,
|
|
167
|
-
startsAt,
|
|
168
|
-
unregisterSequence,
|
|
169
|
-
volumes,
|
|
170
|
-
frame
|
|
171
|
-
]);
|
|
172
|
-
return {
|
|
173
|
-
id: mediaId
|
|
174
|
-
};
|
|
175
|
-
};
|
|
176
|
-
|
|
177
|
-
// src/video/media-player.ts
|
|
13
|
+
// src/media-player.ts
|
|
178
14
|
import {
|
|
179
15
|
ALL_FORMATS,
|
|
180
16
|
AudioBufferSink,
|
|
@@ -182,10 +18,71 @@ import {
|
|
|
182
18
|
Input,
|
|
183
19
|
UrlSource
|
|
184
20
|
} from "mediabunny";
|
|
185
|
-
import { Internals as
|
|
21
|
+
import { Internals as Internals2 } from "remotion";
|
|
22
|
+
|
|
23
|
+
// src/audio/audio-preview-iterator.ts
|
|
24
|
+
var HEALTHY_BUFFER_THRESHOLD_SECONDS = 1;
|
|
25
|
+
var makeAudioIterator = (audioSink, startFromSecond) => {
|
|
26
|
+
let destroyed = false;
|
|
27
|
+
const iterator = audioSink.buffers(startFromSecond);
|
|
28
|
+
let audioIteratorStarted = false;
|
|
29
|
+
let audioBufferHealth = 0;
|
|
30
|
+
const queuedAudioNodes = new Set;
|
|
31
|
+
const cleanupAudioQueue = () => {
|
|
32
|
+
for (const node of queuedAudioNodes) {
|
|
33
|
+
node.stop();
|
|
34
|
+
}
|
|
35
|
+
queuedAudioNodes.clear();
|
|
36
|
+
};
|
|
37
|
+
return {
|
|
38
|
+
cleanupAudioQueue,
|
|
39
|
+
destroy: () => {
|
|
40
|
+
cleanupAudioQueue();
|
|
41
|
+
destroyed = true;
|
|
42
|
+
iterator.return().catch(() => {
|
|
43
|
+
return;
|
|
44
|
+
});
|
|
45
|
+
},
|
|
46
|
+
isReadyToPlay: () => {
|
|
47
|
+
return audioIteratorStarted && audioBufferHealth > 0;
|
|
48
|
+
},
|
|
49
|
+
setAudioIteratorStarted: (started) => {
|
|
50
|
+
audioIteratorStarted = started;
|
|
51
|
+
},
|
|
52
|
+
getNext: () => {
|
|
53
|
+
return iterator.next();
|
|
54
|
+
},
|
|
55
|
+
setAudioBufferHealth: (health) => {
|
|
56
|
+
audioBufferHealth = health;
|
|
57
|
+
},
|
|
58
|
+
isDestroyed: () => {
|
|
59
|
+
return destroyed;
|
|
60
|
+
},
|
|
61
|
+
addQueuedAudioNode: (node) => {
|
|
62
|
+
queuedAudioNodes.add(node);
|
|
63
|
+
},
|
|
64
|
+
removeQueuedAudioNode: (node) => {
|
|
65
|
+
queuedAudioNodes.delete(node);
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
// src/debug-overlay/preview-overlay.ts
|
|
71
|
+
var drawPreviewOverlay = (context, stats, audioContextState, audioSyncAnchor) => {
|
|
72
|
+
context.fillStyle = "rgba(0, 0, 0, 1)";
|
|
73
|
+
context.fillRect(20, 20, 600, 180);
|
|
74
|
+
context.fillStyle = "white";
|
|
75
|
+
context.font = "24px sans-serif";
|
|
76
|
+
context.textBaseline = "top";
|
|
77
|
+
context.fillText(`Debug overlay`, 30, 30);
|
|
78
|
+
context.fillText(`Video iterators created: ${stats.videoIteratorsCreated}`, 30, 60);
|
|
79
|
+
context.fillText(`Frames rendered: ${stats.framesRendered}`, 30, 90);
|
|
80
|
+
context.fillText(`Audio context state: ${audioContextState}`, 30, 120);
|
|
81
|
+
context.fillText(`Audio time: ${audioSyncAnchor.toFixed(3)}s`, 30, 150);
|
|
82
|
+
};
|
|
186
83
|
|
|
187
84
|
// src/get-time-in-seconds.ts
|
|
188
|
-
import { Internals
|
|
85
|
+
import { Internals } from "remotion";
|
|
189
86
|
var getTimeInSeconds = ({
|
|
190
87
|
loop,
|
|
191
88
|
mediaDurationInSeconds,
|
|
@@ -200,7 +97,7 @@ var getTimeInSeconds = ({
|
|
|
200
97
|
if (mediaDurationInSeconds === null && loop && ifNoMediaDuration === "fail") {
|
|
201
98
|
throw new Error(`Could not determine duration of ${src}, but "loop" was set.`);
|
|
202
99
|
}
|
|
203
|
-
const loopDuration = loop ?
|
|
100
|
+
const loopDuration = loop ? Internals.calculateMediaDuration({
|
|
204
101
|
trimAfter,
|
|
205
102
|
mediaDurationInFrames: mediaDurationInSeconds ? mediaDurationInSeconds * fps : Infinity,
|
|
206
103
|
playbackRate: 1,
|
|
@@ -250,8 +147,135 @@ function withTimeout(promise, timeoutMs, errorMessage = "Operation timed out") {
|
|
|
250
147
|
]);
|
|
251
148
|
}
|
|
252
149
|
|
|
253
|
-
// src/
|
|
254
|
-
var
|
|
150
|
+
// src/helpers/round-to-4-digits.ts
|
|
151
|
+
var roundTo4Digits = (timestamp) => {
|
|
152
|
+
return Math.round(timestamp * 1000) / 1000;
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
// src/video/video-preview-iterator.ts
|
|
156
|
+
var createVideoIterator = (timeToSeek, videoSink) => {
|
|
157
|
+
let destroyed = false;
|
|
158
|
+
const iterator = videoSink.canvases(timeToSeek);
|
|
159
|
+
let lastReturnedFrame = null;
|
|
160
|
+
let iteratorEnded = false;
|
|
161
|
+
const getNextOrNullIfNotAvailable = async () => {
|
|
162
|
+
const next = iterator.next();
|
|
163
|
+
const result = await Promise.race([
|
|
164
|
+
next,
|
|
165
|
+
new Promise((resolve) => {
|
|
166
|
+
Promise.resolve().then(() => resolve());
|
|
167
|
+
})
|
|
168
|
+
]);
|
|
169
|
+
if (!result) {
|
|
170
|
+
return {
|
|
171
|
+
type: "need-to-wait-for-it",
|
|
172
|
+
waitPromise: async () => {
|
|
173
|
+
const res = await next;
|
|
174
|
+
if (res.value) {
|
|
175
|
+
lastReturnedFrame = res.value;
|
|
176
|
+
} else {
|
|
177
|
+
iteratorEnded = true;
|
|
178
|
+
}
|
|
179
|
+
return res.value;
|
|
180
|
+
}
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
if (result.value) {
|
|
184
|
+
lastReturnedFrame = result.value;
|
|
185
|
+
} else {
|
|
186
|
+
iteratorEnded = true;
|
|
187
|
+
}
|
|
188
|
+
return {
|
|
189
|
+
type: "got-frame-or-end",
|
|
190
|
+
frame: result.value ?? null
|
|
191
|
+
};
|
|
192
|
+
};
|
|
193
|
+
const destroy = () => {
|
|
194
|
+
destroyed = true;
|
|
195
|
+
lastReturnedFrame = null;
|
|
196
|
+
iterator.return().catch(() => {
|
|
197
|
+
return;
|
|
198
|
+
});
|
|
199
|
+
};
|
|
200
|
+
const tryToSatisfySeek = async (time) => {
|
|
201
|
+
if (lastReturnedFrame) {
|
|
202
|
+
const frameTimestamp = roundTo4Digits(lastReturnedFrame.timestamp);
|
|
203
|
+
if (roundTo4Digits(time) < frameTimestamp) {
|
|
204
|
+
return {
|
|
205
|
+
type: "not-satisfied",
|
|
206
|
+
reason: `iterator is too far, most recently returned ${frameTimestamp}`
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
const frameEndTimestamp = roundTo4Digits(lastReturnedFrame.timestamp + lastReturnedFrame.duration);
|
|
210
|
+
const timestamp = roundTo4Digits(time);
|
|
211
|
+
if (frameTimestamp <= timestamp && frameEndTimestamp > timestamp) {
|
|
212
|
+
return {
|
|
213
|
+
type: "satisfied",
|
|
214
|
+
frame: lastReturnedFrame
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
if (iteratorEnded) {
|
|
219
|
+
if (lastReturnedFrame) {
|
|
220
|
+
return {
|
|
221
|
+
type: "satisfied",
|
|
222
|
+
frame: lastReturnedFrame
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
return {
|
|
226
|
+
type: "not-satisfied",
|
|
227
|
+
reason: "iterator ended"
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
while (true) {
|
|
231
|
+
const frame = await getNextOrNullIfNotAvailable();
|
|
232
|
+
if (frame.type === "need-to-wait-for-it") {
|
|
233
|
+
return {
|
|
234
|
+
type: "not-satisfied",
|
|
235
|
+
reason: "iterator did not have frame ready"
|
|
236
|
+
};
|
|
237
|
+
}
|
|
238
|
+
if (frame.type === "got-frame-or-end") {
|
|
239
|
+
if (frame.frame === null) {
|
|
240
|
+
iteratorEnded = true;
|
|
241
|
+
if (lastReturnedFrame) {
|
|
242
|
+
return {
|
|
243
|
+
type: "satisfied",
|
|
244
|
+
frame: lastReturnedFrame
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
return {
|
|
248
|
+
type: "not-satisfied",
|
|
249
|
+
reason: "iterator ended and did not have frame ready"
|
|
250
|
+
};
|
|
251
|
+
}
|
|
252
|
+
const frameTimestamp = roundTo4Digits(frame.frame.timestamp);
|
|
253
|
+
const frameEndTimestamp = roundTo4Digits(frame.frame.timestamp + frame.frame.duration);
|
|
254
|
+
const timestamp = roundTo4Digits(time);
|
|
255
|
+
if (frameTimestamp <= timestamp && frameEndTimestamp > timestamp) {
|
|
256
|
+
return {
|
|
257
|
+
type: "satisfied",
|
|
258
|
+
frame: frame.frame
|
|
259
|
+
};
|
|
260
|
+
}
|
|
261
|
+
continue;
|
|
262
|
+
}
|
|
263
|
+
throw new Error("Unreachable");
|
|
264
|
+
}
|
|
265
|
+
};
|
|
266
|
+
return {
|
|
267
|
+
destroy,
|
|
268
|
+
getNext: () => {
|
|
269
|
+
return iterator.next();
|
|
270
|
+
},
|
|
271
|
+
isDestroyed: () => {
|
|
272
|
+
return destroyed;
|
|
273
|
+
},
|
|
274
|
+
tryToSatisfySeek
|
|
275
|
+
};
|
|
276
|
+
};
|
|
277
|
+
|
|
278
|
+
// src/media-player.ts
|
|
255
279
|
var AUDIO_BUFFER_TOLERANCE_THRESHOLD = 0.1;
|
|
256
280
|
|
|
257
281
|
class MediaPlayer {
|
|
@@ -263,10 +287,12 @@ class MediaPlayer {
|
|
|
263
287
|
audioStreamIndex;
|
|
264
288
|
canvasSink = null;
|
|
265
289
|
videoFrameIterator = null;
|
|
266
|
-
|
|
290
|
+
debugStats = {
|
|
291
|
+
videoIteratorsCreated: 0,
|
|
292
|
+
framesRendered: 0
|
|
293
|
+
};
|
|
267
294
|
audioSink = null;
|
|
268
295
|
audioBufferIterator = null;
|
|
269
|
-
queuedAudioNodes = new Set;
|
|
270
296
|
gainNode = null;
|
|
271
297
|
currentVolume = 1;
|
|
272
298
|
sharedAudioContext;
|
|
@@ -277,18 +303,15 @@ class MediaPlayer {
|
|
|
277
303
|
fps;
|
|
278
304
|
trimBefore;
|
|
279
305
|
trimAfter;
|
|
280
|
-
animationFrameId = null;
|
|
281
|
-
videoAsyncId = 0;
|
|
282
|
-
audioAsyncId = 0;
|
|
283
306
|
initialized = false;
|
|
284
307
|
totalDuration;
|
|
285
308
|
isBuffering = false;
|
|
286
309
|
onBufferingChangeCallback;
|
|
287
|
-
audioBufferHealth = 0;
|
|
288
|
-
audioIteratorStarted = false;
|
|
289
|
-
HEALTHY_BUFER_THRESHOLD_SECONDS = 1;
|
|
290
310
|
mediaEnded = false;
|
|
311
|
+
debugOverlay = false;
|
|
291
312
|
onVideoFrameCallback;
|
|
313
|
+
initializationPromise = null;
|
|
314
|
+
bufferState;
|
|
292
315
|
constructor({
|
|
293
316
|
canvas,
|
|
294
317
|
src,
|
|
@@ -299,7 +322,9 @@ class MediaPlayer {
|
|
|
299
322
|
trimAfter,
|
|
300
323
|
playbackRate,
|
|
301
324
|
audioStreamIndex,
|
|
302
|
-
fps
|
|
325
|
+
fps,
|
|
326
|
+
debugOverlay,
|
|
327
|
+
bufferState
|
|
303
328
|
}) {
|
|
304
329
|
this.canvas = canvas ?? null;
|
|
305
330
|
this.src = src;
|
|
@@ -311,6 +336,8 @@ class MediaPlayer {
|
|
|
311
336
|
this.trimAfter = trimAfter;
|
|
312
337
|
this.audioStreamIndex = audioStreamIndex ?? 0;
|
|
313
338
|
this.fps = fps;
|
|
339
|
+
this.debugOverlay = debugOverlay;
|
|
340
|
+
this.bufferState = bufferState;
|
|
314
341
|
if (canvas) {
|
|
315
342
|
const context = canvas.getContext("2d", {
|
|
316
343
|
alpha: true,
|
|
@@ -326,7 +353,7 @@ class MediaPlayer {
|
|
|
326
353
|
}
|
|
327
354
|
input = null;
|
|
328
355
|
isReady() {
|
|
329
|
-
return this.initialized && Boolean(this.sharedAudioContext);
|
|
356
|
+
return this.initialized && Boolean(this.sharedAudioContext) && !this.input?.disposed;
|
|
330
357
|
}
|
|
331
358
|
hasAudio() {
|
|
332
359
|
return Boolean(this.audioSink && this.sharedAudioContext && this.gainNode);
|
|
@@ -334,7 +361,15 @@ class MediaPlayer {
|
|
|
334
361
|
isCurrentlyBuffering() {
|
|
335
362
|
return this.isBuffering && Boolean(this.bufferingStartedAtMs);
|
|
336
363
|
}
|
|
337
|
-
|
|
364
|
+
isDisposalError() {
|
|
365
|
+
return this.input?.disposed === true;
|
|
366
|
+
}
|
|
367
|
+
initialize(startTimeUnresolved) {
|
|
368
|
+
const promise = this._initialize(startTimeUnresolved);
|
|
369
|
+
this.initializationPromise = promise;
|
|
370
|
+
return promise;
|
|
371
|
+
}
|
|
372
|
+
async _initialize(startTimeUnresolved) {
|
|
338
373
|
try {
|
|
339
374
|
const urlSource = new UrlSource(this.src);
|
|
340
375
|
const input = new Input({
|
|
@@ -342,14 +377,20 @@ class MediaPlayer {
|
|
|
342
377
|
formats: ALL_FORMATS
|
|
343
378
|
});
|
|
344
379
|
this.input = input;
|
|
380
|
+
if (input.disposed) {
|
|
381
|
+
return { type: "disposed" };
|
|
382
|
+
}
|
|
345
383
|
try {
|
|
346
|
-
await
|
|
384
|
+
await input.getFormat();
|
|
347
385
|
} catch (error) {
|
|
386
|
+
if (this.isDisposalError()) {
|
|
387
|
+
return { type: "disposed" };
|
|
388
|
+
}
|
|
348
389
|
const err = error;
|
|
349
390
|
if (isNetworkError(err)) {
|
|
350
391
|
throw error;
|
|
351
392
|
}
|
|
352
|
-
|
|
393
|
+
Internals2.Log.error({ logLevel: this.logLevel, tag: "@remotion/media" }, `[MediaPlayer] Failed to recognize format for ${this.src}`, error);
|
|
353
394
|
return { type: "unknown-container-format" };
|
|
354
395
|
}
|
|
355
396
|
const [durationInSeconds, videoTrack, audioTracks] = await Promise.all([
|
|
@@ -399,19 +440,23 @@ class MediaPlayer {
|
|
|
399
440
|
this.audioSyncAnchor = this.sharedAudioContext.currentTime - startTime;
|
|
400
441
|
}
|
|
401
442
|
this.initialized = true;
|
|
402
|
-
|
|
403
|
-
this.startAudioIterator(startTime)
|
|
404
|
-
this.startVideoIterator(startTime)
|
|
405
|
-
|
|
406
|
-
|
|
443
|
+
try {
|
|
444
|
+
this.startAudioIterator(startTime);
|
|
445
|
+
await this.startVideoIterator(startTime, this.currentSeekNonce);
|
|
446
|
+
} catch (error) {
|
|
447
|
+
if (this.isDisposalError()) {
|
|
448
|
+
return { type: "disposed" };
|
|
449
|
+
}
|
|
450
|
+
Internals2.Log.error({ logLevel: this.logLevel, tag: "@remotion/media" }, "[MediaPlayer] Failed to start audio and video iterators", error);
|
|
451
|
+
}
|
|
407
452
|
return { type: "success", durationInSeconds };
|
|
408
453
|
} catch (error) {
|
|
409
454
|
const err = error;
|
|
410
455
|
if (isNetworkError(err)) {
|
|
411
|
-
|
|
456
|
+
Internals2.Log.error({ logLevel: this.logLevel, tag: "@remotion/media" }, `[MediaPlayer] Network/CORS error for ${this.src}`, err);
|
|
412
457
|
return { type: "network-error" };
|
|
413
458
|
}
|
|
414
|
-
|
|
459
|
+
Internals2.Log.error({ logLevel: this.logLevel, tag: "@remotion/media" }, "[MediaPlayer] Failed to initialize", error);
|
|
415
460
|
throw error;
|
|
416
461
|
}
|
|
417
462
|
}
|
|
@@ -420,20 +465,19 @@ class MediaPlayer {
|
|
|
420
465
|
this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
|
|
421
466
|
}
|
|
422
467
|
}
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
node.stop();
|
|
426
|
-
}
|
|
427
|
-
this.queuedAudioNodes.clear();
|
|
428
|
-
}
|
|
429
|
-
async cleanAudioIteratorAndNodes() {
|
|
430
|
-
await this.audioBufferIterator?.return();
|
|
431
|
-
this.audioBufferIterator = null;
|
|
432
|
-
this.audioIteratorStarted = false;
|
|
433
|
-
this.audioBufferHealth = 0;
|
|
434
|
-
this.cleanupAudioQueue();
|
|
435
|
-
}
|
|
468
|
+
currentSeekNonce = 0;
|
|
469
|
+
seekPromiseChain = Promise.resolve();
|
|
436
470
|
async seekTo(time) {
|
|
471
|
+
this.currentSeekNonce++;
|
|
472
|
+
const nonce = this.currentSeekNonce;
|
|
473
|
+
await this.seekPromiseChain;
|
|
474
|
+
this.seekPromiseChain = this.seekToDoNotCallDirectly(time, nonce);
|
|
475
|
+
await this.seekPromiseChain;
|
|
476
|
+
}
|
|
477
|
+
async seekToDoNotCallDirectly(time, nonce) {
|
|
478
|
+
if (nonce !== this.currentSeekNonce) {
|
|
479
|
+
return;
|
|
480
|
+
}
|
|
437
481
|
if (!this.isReady())
|
|
438
482
|
return;
|
|
439
483
|
const newTime = getTimeInSeconds({
|
|
@@ -448,29 +492,29 @@ class MediaPlayer {
|
|
|
448
492
|
src: this.src
|
|
449
493
|
});
|
|
450
494
|
if (newTime === null) {
|
|
451
|
-
this.
|
|
452
|
-
this.
|
|
495
|
+
this.videoFrameIterator?.destroy();
|
|
496
|
+
this.videoFrameIterator = null;
|
|
453
497
|
this.clearCanvas();
|
|
454
|
-
|
|
498
|
+
this.audioBufferIterator?.destroy();
|
|
499
|
+
this.audioBufferIterator = null;
|
|
455
500
|
return;
|
|
456
501
|
}
|
|
457
502
|
const currentPlaybackTime = this.getPlaybackTime();
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
this.nextFrame = null;
|
|
461
|
-
this.audioSyncAnchor = this.sharedAudioContext.currentTime - newTime;
|
|
462
|
-
this.mediaEnded = false;
|
|
463
|
-
if (this.audioSink) {
|
|
464
|
-
await this.cleanAudioIteratorAndNodes();
|
|
465
|
-
}
|
|
466
|
-
await Promise.all([
|
|
467
|
-
this.startAudioIterator(newTime),
|
|
468
|
-
this.startVideoIterator(newTime)
|
|
469
|
-
]);
|
|
503
|
+
if (currentPlaybackTime === newTime) {
|
|
504
|
+
return;
|
|
470
505
|
}
|
|
471
|
-
|
|
472
|
-
|
|
506
|
+
const satisfyResult = await this.videoFrameIterator?.tryToSatisfySeek(newTime);
|
|
507
|
+
if (satisfyResult?.type === "satisfied") {
|
|
508
|
+
this.drawFrame(satisfyResult.frame);
|
|
509
|
+
return;
|
|
473
510
|
}
|
|
511
|
+
if (this.currentSeekNonce !== nonce) {
|
|
512
|
+
return;
|
|
513
|
+
}
|
|
514
|
+
this.mediaEnded = false;
|
|
515
|
+
this.audioSyncAnchor = this.sharedAudioContext.currentTime - newTime;
|
|
516
|
+
this.startAudioIterator(newTime);
|
|
517
|
+
this.startVideoIterator(newTime, nonce);
|
|
474
518
|
}
|
|
475
519
|
async play() {
|
|
476
520
|
if (!this.isReady())
|
|
@@ -480,13 +524,11 @@ class MediaPlayer {
|
|
|
480
524
|
await this.sharedAudioContext.resume();
|
|
481
525
|
}
|
|
482
526
|
this.playing = true;
|
|
483
|
-
this.startRenderLoop();
|
|
484
527
|
}
|
|
485
528
|
}
|
|
486
529
|
pause() {
|
|
487
530
|
this.playing = false;
|
|
488
|
-
this.cleanupAudioQueue();
|
|
489
|
-
this.stopRenderLoop();
|
|
531
|
+
this.audioBufferIterator?.cleanupAudioQueue();
|
|
490
532
|
}
|
|
491
533
|
setMuted(muted) {
|
|
492
534
|
this.muted = muted;
|
|
@@ -504,6 +546,9 @@ class MediaPlayer {
|
|
|
504
546
|
this.gainNode.gain.value = appliedVolume;
|
|
505
547
|
}
|
|
506
548
|
}
|
|
549
|
+
setDebugOverlay(debugOverlay) {
|
|
550
|
+
this.debugOverlay = debugOverlay;
|
|
551
|
+
}
|
|
507
552
|
setPlaybackRate(rate) {
|
|
508
553
|
this.playbackRate = rate;
|
|
509
554
|
}
|
|
@@ -513,12 +558,18 @@ class MediaPlayer {
|
|
|
513
558
|
setLoop(loop) {
|
|
514
559
|
this.loop = loop;
|
|
515
560
|
}
|
|
516
|
-
dispose() {
|
|
561
|
+
async dispose() {
|
|
562
|
+
this.initialized = false;
|
|
563
|
+
if (this.initializationPromise) {
|
|
564
|
+
try {
|
|
565
|
+
await this.initializationPromise;
|
|
566
|
+
} catch {}
|
|
567
|
+
}
|
|
517
568
|
this.input?.dispose();
|
|
518
|
-
this.
|
|
519
|
-
this.videoFrameIterator
|
|
520
|
-
this.
|
|
521
|
-
this.
|
|
569
|
+
this.videoFrameIterator?.destroy();
|
|
570
|
+
this.videoFrameIterator = null;
|
|
571
|
+
this.audioBufferIterator?.destroy();
|
|
572
|
+
this.audioBufferIterator = null;
|
|
522
573
|
}
|
|
523
574
|
getPlaybackTime() {
|
|
524
575
|
return this.sharedAudioContext.currentTime - this.audioSyncAnchor;
|
|
@@ -535,8 +586,8 @@ class MediaPlayer {
|
|
|
535
586
|
} else {
|
|
536
587
|
node.start(this.sharedAudioContext.currentTime, -delay);
|
|
537
588
|
}
|
|
538
|
-
this.
|
|
539
|
-
node.onended = () => this.
|
|
589
|
+
this.audioBufferIterator?.addQueuedAudioNode(node);
|
|
590
|
+
node.onended = () => this.audioBufferIterator?.removeQueuedAudioNode(node);
|
|
540
591
|
}
|
|
541
592
|
onBufferingChange(callback) {
|
|
542
593
|
this.onBufferingChangeCallback = callback;
|
|
@@ -557,225 +608,322 @@ class MediaPlayer {
|
|
|
557
608
|
}
|
|
558
609
|
};
|
|
559
610
|
}
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
startRenderLoop() {
|
|
564
|
-
if (this.animationFrameId !== null) {
|
|
565
|
-
return;
|
|
566
|
-
}
|
|
567
|
-
this.render();
|
|
568
|
-
}
|
|
569
|
-
stopRenderLoop() {
|
|
570
|
-
if (this.animationFrameId !== null) {
|
|
571
|
-
cancelAnimationFrame(this.animationFrameId);
|
|
572
|
-
this.animationFrameId = null;
|
|
573
|
-
}
|
|
574
|
-
}
|
|
575
|
-
render = () => {
|
|
576
|
-
if (this.isBuffering) {
|
|
577
|
-
this.maybeForceResumeFromBuffering();
|
|
578
|
-
}
|
|
579
|
-
if (this.shouldRenderFrame()) {
|
|
580
|
-
this.drawCurrentFrame();
|
|
581
|
-
}
|
|
582
|
-
if (this.playing) {
|
|
583
|
-
this.animationFrameId = requestAnimationFrame(this.render);
|
|
584
|
-
} else {
|
|
585
|
-
this.animationFrameId = null;
|
|
586
|
-
}
|
|
587
|
-
};
|
|
588
|
-
shouldRenderFrame() {
|
|
589
|
-
const playbackTime = this.getPlaybackTime();
|
|
590
|
-
if (playbackTime === null) {
|
|
591
|
-
return false;
|
|
592
|
-
}
|
|
593
|
-
return !this.isBuffering && this.canRenderVideo() && this.nextFrame !== null && this.nextFrame.timestamp <= playbackTime;
|
|
594
|
-
}
|
|
595
|
-
drawCurrentFrame() {
|
|
596
|
-
if (this.context && this.nextFrame) {
|
|
597
|
-
this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
|
|
598
|
-
this.context.drawImage(this.nextFrame.canvas, 0, 0);
|
|
611
|
+
drawFrame = (frame) => {
|
|
612
|
+
if (!this.context) {
|
|
613
|
+
throw new Error("Context not initialized");
|
|
599
614
|
}
|
|
615
|
+
this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
|
|
616
|
+
this.context.drawImage(frame.canvas, 0, 0);
|
|
617
|
+
this.debugStats.framesRendered++;
|
|
618
|
+
this.drawDebugOverlay();
|
|
600
619
|
if (this.onVideoFrameCallback && this.canvas) {
|
|
601
620
|
this.onVideoFrameCallback(this.canvas);
|
|
602
621
|
}
|
|
603
|
-
this.
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
startAudioIterator = async (startFromSecond) => {
|
|
622
|
+
Internals2.Log.trace({ logLevel: this.logLevel, tag: "@remotion/media" }, `[MediaPlayer] Drew frame ${frame.timestamp.toFixed(3)}s`);
|
|
623
|
+
};
|
|
624
|
+
startAudioIterator = (startFromSecond) => {
|
|
607
625
|
if (!this.hasAudio())
|
|
608
626
|
return;
|
|
609
|
-
this.
|
|
610
|
-
const currentAsyncId = this.audioAsyncId;
|
|
611
|
-
await this.audioBufferIterator?.return();
|
|
612
|
-
this.audioIteratorStarted = false;
|
|
613
|
-
this.audioBufferHealth = 0;
|
|
627
|
+
this.audioBufferIterator?.destroy();
|
|
614
628
|
try {
|
|
615
|
-
|
|
616
|
-
this.
|
|
629
|
+
const iterator = makeAudioIterator(this.audioSink, startFromSecond);
|
|
630
|
+
this.audioBufferIterator = iterator;
|
|
631
|
+
this.runAudioIterator(startFromSecond, iterator);
|
|
617
632
|
} catch (error) {
|
|
618
|
-
|
|
633
|
+
if (this.isDisposalError()) {
|
|
634
|
+
return;
|
|
635
|
+
}
|
|
636
|
+
Internals2.Log.error({ logLevel: this.logLevel, tag: "@remotion/media" }, "[MediaPlayer] Failed to start audio iterator", error);
|
|
619
637
|
}
|
|
620
638
|
};
|
|
621
|
-
|
|
639
|
+
drawDebugOverlay() {
|
|
640
|
+
if (!this.debugOverlay)
|
|
641
|
+
return;
|
|
642
|
+
if (this.context && this.canvas) {
|
|
643
|
+
drawPreviewOverlay(this.context, this.debugStats, this.sharedAudioContext.state, this.sharedAudioContext.currentTime);
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
startVideoIterator = async (timeToSeek, nonce) => {
|
|
622
647
|
if (!this.canvasSink) {
|
|
623
648
|
return;
|
|
624
649
|
}
|
|
625
|
-
this.
|
|
626
|
-
const
|
|
627
|
-
this.
|
|
650
|
+
this.videoFrameIterator?.destroy();
|
|
651
|
+
const iterator = createVideoIterator(timeToSeek, this.canvasSink);
|
|
652
|
+
this.debugStats.videoIteratorsCreated++;
|
|
653
|
+
this.videoFrameIterator = iterator;
|
|
654
|
+
const delayHandle = this.bufferState?.delayPlayback();
|
|
655
|
+
const frameResult = await iterator.getNext();
|
|
656
|
+
delayHandle?.unblock();
|
|
657
|
+
if (iterator.isDestroyed()) {
|
|
658
|
+
return;
|
|
659
|
+
}
|
|
660
|
+
if (nonce !== this.currentSeekNonce) {
|
|
628
661
|
return;
|
|
629
|
-
});
|
|
630
|
-
this.videoFrameIterator = this.canvasSink.canvases(timeToSeek);
|
|
631
|
-
try {
|
|
632
|
-
const firstFrame = (await this.videoFrameIterator.next()).value ?? null;
|
|
633
|
-
const secondFrame = (await this.videoFrameIterator.next()).value ?? null;
|
|
634
|
-
if (currentAsyncId !== this.videoAsyncId) {
|
|
635
|
-
return;
|
|
636
|
-
}
|
|
637
|
-
if (firstFrame && this.context) {
|
|
638
|
-
Internals4.Log.trace({ logLevel: this.logLevel, tag: "@remotion/media" }, `[MediaPlayer] Drew initial frame ${firstFrame.timestamp.toFixed(3)}s`);
|
|
639
|
-
this.context.drawImage(firstFrame.canvas, 0, 0);
|
|
640
|
-
if (this.onVideoFrameCallback && this.canvas) {
|
|
641
|
-
this.onVideoFrameCallback(this.canvas);
|
|
642
|
-
}
|
|
643
|
-
}
|
|
644
|
-
this.nextFrame = secondFrame ?? null;
|
|
645
|
-
if (secondFrame) {
|
|
646
|
-
Internals4.Log.trace({ logLevel: this.logLevel, tag: "@remotion/media" }, `[MediaPlayer] Buffered next frame ${secondFrame.timestamp.toFixed(3)}s`);
|
|
647
|
-
}
|
|
648
|
-
} catch (error) {
|
|
649
|
-
Internals4.Log.error({ logLevel: this.logLevel, tag: "@remotion/media" }, "[MediaPlayer] Failed to start video iterator", error);
|
|
650
662
|
}
|
|
663
|
+
if (this.videoFrameIterator.isDestroyed()) {
|
|
664
|
+
return;
|
|
665
|
+
}
|
|
666
|
+
if (frameResult.value) {
|
|
667
|
+
this.audioSyncAnchor = this.sharedAudioContext.currentTime - frameResult.value.timestamp;
|
|
668
|
+
this.drawFrame(frameResult.value);
|
|
669
|
+
} else {}
|
|
651
670
|
};
|
|
652
|
-
|
|
653
|
-
|
|
671
|
+
bufferingStartedAtMs = null;
|
|
672
|
+
minBufferingTimeoutMs = 500;
|
|
673
|
+
setBufferingState(isBuffering) {
|
|
674
|
+
if (this.isBuffering !== isBuffering) {
|
|
675
|
+
this.isBuffering = isBuffering;
|
|
676
|
+
if (isBuffering) {
|
|
677
|
+
this.bufferingStartedAtMs = performance.now();
|
|
678
|
+
this.onBufferingChangeCallback?.(true);
|
|
679
|
+
} else {
|
|
680
|
+
this.bufferingStartedAtMs = null;
|
|
681
|
+
this.onBufferingChangeCallback?.(false);
|
|
682
|
+
}
|
|
683
|
+
}
|
|
684
|
+
}
|
|
685
|
+
maybeResumeFromBuffering(currentBufferDuration) {
|
|
686
|
+
if (!this.isCurrentlyBuffering())
|
|
654
687
|
return;
|
|
688
|
+
const now = performance.now();
|
|
689
|
+
const bufferingDuration = now - this.bufferingStartedAtMs;
|
|
690
|
+
const minTimeElapsed = bufferingDuration >= this.minBufferingTimeoutMs;
|
|
691
|
+
const bufferHealthy = currentBufferDuration >= HEALTHY_BUFFER_THRESHOLD_SECONDS;
|
|
692
|
+
if (minTimeElapsed && bufferHealthy) {
|
|
693
|
+
Internals2.Log.trace({ logLevel: this.logLevel, tag: "@remotion/media" }, `[MediaPlayer] Resuming from buffering after ${bufferingDuration}ms - buffer recovered`);
|
|
694
|
+
this.setBufferingState(false);
|
|
655
695
|
}
|
|
696
|
+
}
|
|
697
|
+
runAudioIterator = async (startFromSecond, audioIterator) => {
|
|
698
|
+
if (!this.hasAudio())
|
|
699
|
+
return;
|
|
656
700
|
try {
|
|
701
|
+
let totalBufferDuration = 0;
|
|
702
|
+
let isFirstBuffer = true;
|
|
703
|
+
audioIterator.setAudioIteratorStarted(true);
|
|
657
704
|
while (true) {
|
|
658
|
-
|
|
659
|
-
|
|
705
|
+
if (audioIterator.isDestroyed()) {
|
|
706
|
+
return;
|
|
707
|
+
}
|
|
708
|
+
const BUFFERING_TIMEOUT_MS = 50;
|
|
709
|
+
let result;
|
|
710
|
+
try {
|
|
711
|
+
result = await withTimeout(audioIterator.getNext(), BUFFERING_TIMEOUT_MS, "Iterator timeout");
|
|
712
|
+
} catch (error) {
|
|
713
|
+
if (error instanceof TimeoutError && !this.mediaEnded) {
|
|
714
|
+
this.setBufferingState(true);
|
|
715
|
+
}
|
|
716
|
+
await sleep(10);
|
|
717
|
+
continue;
|
|
718
|
+
}
|
|
719
|
+
if (result.done || !result.value) {
|
|
660
720
|
this.mediaEnded = true;
|
|
661
721
|
break;
|
|
662
722
|
}
|
|
723
|
+
const { buffer, timestamp, duration } = result.value;
|
|
724
|
+
totalBufferDuration += duration;
|
|
725
|
+
audioIterator.setAudioBufferHealth(Math.max(0, totalBufferDuration / this.playbackRate));
|
|
726
|
+
this.maybeResumeFromBuffering(totalBufferDuration / this.playbackRate);
|
|
727
|
+
if (this.playing) {
|
|
728
|
+
if (isFirstBuffer) {
|
|
729
|
+
this.audioSyncAnchor = this.sharedAudioContext.currentTime - timestamp;
|
|
730
|
+
isFirstBuffer = false;
|
|
731
|
+
}
|
|
732
|
+
if (timestamp < startFromSecond - AUDIO_BUFFER_TOLERANCE_THRESHOLD) {
|
|
733
|
+
continue;
|
|
734
|
+
}
|
|
735
|
+
this.scheduleAudioChunk(buffer, timestamp);
|
|
736
|
+
}
|
|
663
737
|
const playbackTime = this.getPlaybackTime();
|
|
664
738
|
if (playbackTime === null) {
|
|
665
739
|
continue;
|
|
666
740
|
}
|
|
667
|
-
if (
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
741
|
+
if (timestamp - playbackTime >= 1) {
|
|
742
|
+
await new Promise((resolve) => {
|
|
743
|
+
const check = () => {
|
|
744
|
+
const currentPlaybackTime = this.getPlaybackTime();
|
|
745
|
+
if (currentPlaybackTime !== null && timestamp - currentPlaybackTime < 1) {
|
|
746
|
+
resolve();
|
|
747
|
+
} else {
|
|
748
|
+
requestAnimationFrame(check);
|
|
749
|
+
}
|
|
750
|
+
};
|
|
751
|
+
check();
|
|
752
|
+
});
|
|
673
753
|
}
|
|
674
754
|
}
|
|
675
755
|
} catch (error) {
|
|
676
|
-
|
|
756
|
+
if (this.isDisposalError()) {
|
|
757
|
+
return;
|
|
758
|
+
}
|
|
759
|
+
Internals2.Log.error({ logLevel: this.logLevel, tag: "@remotion/media" }, "[MediaPlayer] Failed to run audio iterator", error);
|
|
677
760
|
}
|
|
678
761
|
};
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
762
|
+
}
|
|
763
|
+
|
|
764
|
+
// src/show-in-timeline.ts
|
|
765
|
+
import { useMemo } from "react";
|
|
766
|
+
import { Internals as Internals3, useVideoConfig } from "remotion";
|
|
767
|
+
var useLoopDisplay = ({
|
|
768
|
+
loop,
|
|
769
|
+
mediaDurationInSeconds,
|
|
770
|
+
playbackRate,
|
|
771
|
+
trimAfter,
|
|
772
|
+
trimBefore
|
|
773
|
+
}) => {
|
|
774
|
+
const { durationInFrames: compDuration, fps } = useVideoConfig();
|
|
775
|
+
const loopDisplay = useMemo(() => {
|
|
776
|
+
if (!loop || !mediaDurationInSeconds) {
|
|
777
|
+
return;
|
|
778
|
+
}
|
|
779
|
+
const durationInFrames = Internals3.calculateMediaDuration({
|
|
780
|
+
mediaDurationInFrames: mediaDurationInSeconds * fps,
|
|
781
|
+
playbackRate,
|
|
782
|
+
trimAfter,
|
|
783
|
+
trimBefore
|
|
784
|
+
});
|
|
785
|
+
const maxTimes = compDuration / durationInFrames;
|
|
786
|
+
return {
|
|
787
|
+
numberOfTimes: maxTimes,
|
|
788
|
+
startOffset: 0,
|
|
789
|
+
durationInFrames
|
|
790
|
+
};
|
|
791
|
+
}, [
|
|
792
|
+
compDuration,
|
|
793
|
+
fps,
|
|
794
|
+
loop,
|
|
795
|
+
mediaDurationInSeconds,
|
|
796
|
+
playbackRate,
|
|
797
|
+
trimAfter,
|
|
798
|
+
trimBefore
|
|
799
|
+
]);
|
|
800
|
+
return loopDisplay;
|
|
801
|
+
};
|
|
802
|
+
|
|
803
|
+
// src/use-media-in-timeline.ts
|
|
804
|
+
import { useContext, useEffect, useState } from "react";
|
|
805
|
+
import { Internals as Internals4, useCurrentFrame } from "remotion";
|
|
806
|
+
var useMediaInTimeline = ({
|
|
807
|
+
volume,
|
|
808
|
+
mediaVolume,
|
|
809
|
+
src,
|
|
810
|
+
mediaType,
|
|
811
|
+
playbackRate,
|
|
812
|
+
displayName,
|
|
813
|
+
stack,
|
|
814
|
+
showInTimeline,
|
|
815
|
+
premountDisplay,
|
|
816
|
+
postmountDisplay,
|
|
817
|
+
loopDisplay,
|
|
818
|
+
trimBefore,
|
|
819
|
+
trimAfter
|
|
820
|
+
}) => {
|
|
821
|
+
const parentSequence = useContext(Internals4.SequenceContext);
|
|
822
|
+
const startsAt = Internals4.useMediaStartsAt();
|
|
823
|
+
const { registerSequence, unregisterSequence } = useContext(Internals4.SequenceManager);
|
|
824
|
+
const [sequenceId] = useState(() => String(Math.random()));
|
|
825
|
+
const [mediaId] = useState(() => String(Math.random()));
|
|
826
|
+
const frame = useCurrentFrame();
|
|
827
|
+
const {
|
|
828
|
+
volumes,
|
|
829
|
+
duration,
|
|
830
|
+
doesVolumeChange,
|
|
831
|
+
nonce,
|
|
832
|
+
rootId,
|
|
833
|
+
isStudio,
|
|
834
|
+
finalDisplayName
|
|
835
|
+
} = Internals4.useBasicMediaInTimeline({
|
|
836
|
+
volume,
|
|
837
|
+
mediaVolume,
|
|
838
|
+
mediaType,
|
|
839
|
+
src,
|
|
840
|
+
displayName,
|
|
841
|
+
trimBefore,
|
|
842
|
+
trimAfter,
|
|
843
|
+
playbackRate
|
|
844
|
+
});
|
|
845
|
+
useEffect(() => {
|
|
846
|
+
if (!src) {
|
|
847
|
+
throw new Error("No src passed");
|
|
691
848
|
}
|
|
692
|
-
|
|
693
|
-
maybeResumeFromBuffering(currentBufferDuration) {
|
|
694
|
-
if (!this.isCurrentlyBuffering())
|
|
849
|
+
if (!isStudio && window.process?.env?.NODE_ENV !== "test") {
|
|
695
850
|
return;
|
|
696
|
-
const now = performance.now();
|
|
697
|
-
const bufferingDuration = now - this.bufferingStartedAtMs;
|
|
698
|
-
const minTimeElapsed = bufferingDuration >= this.minBufferingTimeoutMs;
|
|
699
|
-
const bufferHealthy = currentBufferDuration >= this.HEALTHY_BUFER_THRESHOLD_SECONDS;
|
|
700
|
-
if (minTimeElapsed && bufferHealthy) {
|
|
701
|
-
Internals4.Log.trace({ logLevel: this.logLevel, tag: "@remotion/media" }, `[MediaPlayer] Resuming from buffering after ${bufferingDuration}ms - buffer recovered`);
|
|
702
|
-
this.setBufferingState(false);
|
|
703
851
|
}
|
|
704
|
-
|
|
705
|
-
maybeForceResumeFromBuffering() {
|
|
706
|
-
if (!this.isCurrentlyBuffering())
|
|
852
|
+
if (!showInTimeline) {
|
|
707
853
|
return;
|
|
708
|
-
const now = performance.now();
|
|
709
|
-
const bufferingDuration = now - this.bufferingStartedAtMs;
|
|
710
|
-
const forceTimeout = bufferingDuration > this.minBufferingTimeoutMs * 10;
|
|
711
|
-
if (forceTimeout) {
|
|
712
|
-
Internals4.Log.trace({ logLevel: this.logLevel, tag: "@remotion/media" }, `[MediaPlayer] Force resuming from buffering after ${bufferingDuration}ms`);
|
|
713
|
-
this.setBufferingState(false);
|
|
714
854
|
}
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
if (error instanceof TimeoutError && !this.mediaEnded) {
|
|
733
|
-
this.setBufferingState(true);
|
|
734
|
-
}
|
|
735
|
-
await sleep(10);
|
|
736
|
-
continue;
|
|
737
|
-
}
|
|
738
|
-
if (result.done || !result.value) {
|
|
739
|
-
this.mediaEnded = true;
|
|
740
|
-
break;
|
|
741
|
-
}
|
|
742
|
-
const { buffer, timestamp, duration } = result.value;
|
|
743
|
-
totalBufferDuration += duration;
|
|
744
|
-
this.audioBufferHealth = Math.max(0, totalBufferDuration / this.playbackRate);
|
|
745
|
-
this.maybeResumeFromBuffering(totalBufferDuration / this.playbackRate);
|
|
746
|
-
if (this.playing) {
|
|
747
|
-
if (isFirstBuffer) {
|
|
748
|
-
this.audioSyncAnchor = this.sharedAudioContext.currentTime - timestamp;
|
|
749
|
-
isFirstBuffer = false;
|
|
750
|
-
}
|
|
751
|
-
if (timestamp < startFromSecond - AUDIO_BUFFER_TOLERANCE_THRESHOLD) {
|
|
752
|
-
continue;
|
|
753
|
-
}
|
|
754
|
-
this.scheduleAudioChunk(buffer, timestamp);
|
|
755
|
-
}
|
|
756
|
-
const playbackTime = this.getPlaybackTime();
|
|
757
|
-
if (playbackTime === null) {
|
|
758
|
-
continue;
|
|
759
|
-
}
|
|
760
|
-
if (timestamp - playbackTime >= 1) {
|
|
761
|
-
await new Promise((resolve) => {
|
|
762
|
-
const check = () => {
|
|
763
|
-
const currentPlaybackTime = this.getPlaybackTime();
|
|
764
|
-
if (currentPlaybackTime !== null && timestamp - currentPlaybackTime < 1) {
|
|
765
|
-
resolve();
|
|
766
|
-
} else {
|
|
767
|
-
requestAnimationFrame(check);
|
|
768
|
-
}
|
|
769
|
-
};
|
|
770
|
-
check();
|
|
771
|
-
});
|
|
772
|
-
}
|
|
773
|
-
}
|
|
774
|
-
} catch (error) {
|
|
775
|
-
Internals4.Log.error({ logLevel: this.logLevel, tag: "@remotion/media" }, "[MediaPlayer] Failed to run audio iterator", error);
|
|
855
|
+
const loopIteration = loopDisplay ? Math.floor(frame / loopDisplay.durationInFrames) : 0;
|
|
856
|
+
if (loopDisplay) {
|
|
857
|
+
registerSequence({
|
|
858
|
+
type: "sequence",
|
|
859
|
+
premountDisplay,
|
|
860
|
+
postmountDisplay,
|
|
861
|
+
parent: parentSequence?.id ?? null,
|
|
862
|
+
displayName: finalDisplayName,
|
|
863
|
+
rootId,
|
|
864
|
+
showInTimeline: true,
|
|
865
|
+
nonce,
|
|
866
|
+
loopDisplay,
|
|
867
|
+
stack,
|
|
868
|
+
from: 0,
|
|
869
|
+
duration,
|
|
870
|
+
id: sequenceId
|
|
871
|
+
});
|
|
776
872
|
}
|
|
873
|
+
registerSequence({
|
|
874
|
+
type: mediaType,
|
|
875
|
+
src,
|
|
876
|
+
id: mediaId,
|
|
877
|
+
duration: loopDisplay?.durationInFrames ?? duration,
|
|
878
|
+
from: loopDisplay ? loopIteration * loopDisplay.durationInFrames : 0,
|
|
879
|
+
parent: loopDisplay ? sequenceId : parentSequence?.id ?? null,
|
|
880
|
+
displayName: finalDisplayName,
|
|
881
|
+
rootId,
|
|
882
|
+
volume: volumes,
|
|
883
|
+
showInTimeline: true,
|
|
884
|
+
nonce,
|
|
885
|
+
startMediaFrom: 0 - startsAt,
|
|
886
|
+
doesVolumeChange,
|
|
887
|
+
loopDisplay: undefined,
|
|
888
|
+
playbackRate,
|
|
889
|
+
stack,
|
|
890
|
+
premountDisplay: null,
|
|
891
|
+
postmountDisplay: null
|
|
892
|
+
});
|
|
893
|
+
return () => {
|
|
894
|
+
if (loopDisplay) {
|
|
895
|
+
unregisterSequence(sequenceId);
|
|
896
|
+
}
|
|
897
|
+
unregisterSequence(mediaId);
|
|
898
|
+
};
|
|
899
|
+
}, [
|
|
900
|
+
doesVolumeChange,
|
|
901
|
+
duration,
|
|
902
|
+
finalDisplayName,
|
|
903
|
+
isStudio,
|
|
904
|
+
loopDisplay,
|
|
905
|
+
mediaId,
|
|
906
|
+
mediaType,
|
|
907
|
+
nonce,
|
|
908
|
+
parentSequence?.id,
|
|
909
|
+
playbackRate,
|
|
910
|
+
postmountDisplay,
|
|
911
|
+
premountDisplay,
|
|
912
|
+
registerSequence,
|
|
913
|
+
rootId,
|
|
914
|
+
sequenceId,
|
|
915
|
+
showInTimeline,
|
|
916
|
+
src,
|
|
917
|
+
stack,
|
|
918
|
+
startsAt,
|
|
919
|
+
unregisterSequence,
|
|
920
|
+
volumes,
|
|
921
|
+
frame
|
|
922
|
+
]);
|
|
923
|
+
return {
|
|
924
|
+
id: mediaId
|
|
777
925
|
};
|
|
778
|
-
}
|
|
926
|
+
};
|
|
779
927
|
|
|
780
928
|
// src/audio/audio-for-preview.tsx
|
|
781
929
|
import { jsx } from "react/jsx-runtime";
|
|
@@ -878,7 +1026,9 @@ var NewAudioForPreview = ({
|
|
|
878
1026
|
fps: videoConfig.fps,
|
|
879
1027
|
canvas: null,
|
|
880
1028
|
playbackRate,
|
|
881
|
-
audioStreamIndex: audioStreamIndex ?? 0
|
|
1029
|
+
audioStreamIndex: audioStreamIndex ?? 0,
|
|
1030
|
+
debugOverlay: false,
|
|
1031
|
+
bufferState: buffer
|
|
882
1032
|
});
|
|
883
1033
|
mediaPlayerRef.current = player;
|
|
884
1034
|
player.initialize(currentTimeRef.current).then((result) => {
|
|
@@ -950,7 +1100,8 @@ var NewAudioForPreview = ({
|
|
|
950
1100
|
playbackRate,
|
|
951
1101
|
videoConfig.fps,
|
|
952
1102
|
audioStreamIndex,
|
|
953
|
-
disallowFallbackToHtml5Audio
|
|
1103
|
+
disallowFallbackToHtml5Audio,
|
|
1104
|
+
buffer
|
|
954
1105
|
]);
|
|
955
1106
|
useEffect2(() => {
|
|
956
1107
|
const audioPlayer = mediaPlayerRef.current;
|
|
@@ -1297,7 +1448,7 @@ var warnAboutMatroskaOnce = (src, logLevel) => {
|
|
|
1297
1448
|
warned[src] = true;
|
|
1298
1449
|
Internals6.Log.warn({ logLevel, tag: "@remotion/media" }, `Audio from ${src} will need to be read from the beginning. https://www.remotion.dev/docs/media/support#matroska-limitation`);
|
|
1299
1450
|
};
|
|
1300
|
-
var
|
|
1451
|
+
var makeAudioIterator2 = ({
|
|
1301
1452
|
audioSampleSink,
|
|
1302
1453
|
isMatroska,
|
|
1303
1454
|
startTimestamp,
|
|
@@ -1422,7 +1573,7 @@ var makeAudioManager = () => {
|
|
|
1422
1573
|
actualMatroskaTimestamps,
|
|
1423
1574
|
logLevel
|
|
1424
1575
|
}) => {
|
|
1425
|
-
const iterator =
|
|
1576
|
+
const iterator = makeAudioIterator2({
|
|
1426
1577
|
audioSampleSink,
|
|
1427
1578
|
isMatroska,
|
|
1428
1579
|
startTimestamp: timeInSeconds,
|
|
@@ -1584,20 +1735,43 @@ import {
|
|
|
1584
1735
|
|
|
1585
1736
|
// src/video-extraction/keyframe-bank.ts
|
|
1586
1737
|
import { Internals as Internals8 } from "remotion";
|
|
1587
|
-
var roundTo4Digits = (timestamp) => {
|
|
1588
|
-
return Math.round(timestamp * 1000) / 1000;
|
|
1589
|
-
};
|
|
1590
1738
|
var makeKeyframeBank = ({
|
|
1591
1739
|
startTimestampInSeconds,
|
|
1592
1740
|
endTimestampInSeconds,
|
|
1593
1741
|
sampleIterator,
|
|
1594
|
-
logLevel: parentLogLevel
|
|
1742
|
+
logLevel: parentLogLevel,
|
|
1743
|
+
src
|
|
1595
1744
|
}) => {
|
|
1596
1745
|
Internals8.Log.verbose({ logLevel: parentLogLevel, tag: "@remotion/media" }, `Creating keyframe bank from ${startTimestampInSeconds}sec to ${endTimestampInSeconds}sec`);
|
|
1597
1746
|
const frames = {};
|
|
1598
1747
|
const frameTimestamps = [];
|
|
1599
1748
|
let lastUsed = Date.now();
|
|
1600
1749
|
let allocationSize = 0;
|
|
1750
|
+
const deleteFramesBeforeTimestamp = ({
|
|
1751
|
+
logLevel,
|
|
1752
|
+
timestampInSeconds
|
|
1753
|
+
}) => {
|
|
1754
|
+
const deletedTimestamps = [];
|
|
1755
|
+
for (const frameTimestamp of frameTimestamps.slice()) {
|
|
1756
|
+
const isLast = frameTimestamp === frameTimestamps[frameTimestamps.length - 1];
|
|
1757
|
+
if (isLast) {
|
|
1758
|
+
continue;
|
|
1759
|
+
}
|
|
1760
|
+
if (frameTimestamp < timestampInSeconds) {
|
|
1761
|
+
if (!frames[frameTimestamp]) {
|
|
1762
|
+
continue;
|
|
1763
|
+
}
|
|
1764
|
+
allocationSize -= frames[frameTimestamp].allocationSize();
|
|
1765
|
+
frameTimestamps.splice(frameTimestamps.indexOf(frameTimestamp), 1);
|
|
1766
|
+
frames[frameTimestamp].close();
|
|
1767
|
+
delete frames[frameTimestamp];
|
|
1768
|
+
deletedTimestamps.push(frameTimestamp);
|
|
1769
|
+
}
|
|
1770
|
+
}
|
|
1771
|
+
if (deletedTimestamps.length > 0) {
|
|
1772
|
+
Internals8.Log.verbose({ logLevel, tag: "@remotion/media" }, `Deleted ${deletedTimestamps.length} frame${deletedTimestamps.length === 1 ? "" : "s"} ${renderTimestampRange(deletedTimestamps)} for src ${src} because it is lower than ${timestampInSeconds}. Remaining: ${renderTimestampRange(frameTimestamps)}`);
|
|
1773
|
+
}
|
|
1774
|
+
};
|
|
1601
1775
|
const hasDecodedEnoughForTimestamp = (timestamp) => {
|
|
1602
1776
|
const lastFrameTimestamp = frameTimestamps[frameTimestamps.length - 1];
|
|
1603
1777
|
if (!lastFrameTimestamp) {
|
|
@@ -1615,8 +1789,8 @@ var makeKeyframeBank = ({
|
|
|
1615
1789
|
allocationSize += frame.allocationSize();
|
|
1616
1790
|
lastUsed = Date.now();
|
|
1617
1791
|
};
|
|
1618
|
-
const ensureEnoughFramesForTimestamp = async (
|
|
1619
|
-
while (!hasDecodedEnoughForTimestamp(
|
|
1792
|
+
const ensureEnoughFramesForTimestamp = async (timestampInSeconds) => {
|
|
1793
|
+
while (!hasDecodedEnoughForTimestamp(timestampInSeconds)) {
|
|
1620
1794
|
const sample = await sampleIterator.next();
|
|
1621
1795
|
if (sample.value) {
|
|
1622
1796
|
addFrame(sample.value);
|
|
@@ -1624,6 +1798,10 @@ var makeKeyframeBank = ({
|
|
|
1624
1798
|
if (sample.done) {
|
|
1625
1799
|
break;
|
|
1626
1800
|
}
|
|
1801
|
+
deleteFramesBeforeTimestamp({
|
|
1802
|
+
logLevel: parentLogLevel,
|
|
1803
|
+
timestampInSeconds: timestampInSeconds - SAFE_BACK_WINDOW_IN_SECONDS
|
|
1804
|
+
});
|
|
1627
1805
|
}
|
|
1628
1806
|
lastUsed = Date.now();
|
|
1629
1807
|
};
|
|
@@ -1658,6 +1836,7 @@ var makeKeyframeBank = ({
|
|
|
1658
1836
|
}
|
|
1659
1837
|
return null;
|
|
1660
1838
|
});
|
|
1839
|
+
let framesDeleted = 0;
|
|
1661
1840
|
for (const frameTimestamp of frameTimestamps) {
|
|
1662
1841
|
if (!frames[frameTimestamp]) {
|
|
1663
1842
|
continue;
|
|
@@ -1665,34 +1844,10 @@ var makeKeyframeBank = ({
|
|
|
1665
1844
|
allocationSize -= frames[frameTimestamp].allocationSize();
|
|
1666
1845
|
frames[frameTimestamp].close();
|
|
1667
1846
|
delete frames[frameTimestamp];
|
|
1847
|
+
framesDeleted++;
|
|
1668
1848
|
}
|
|
1669
1849
|
frameTimestamps.length = 0;
|
|
1670
|
-
|
|
1671
|
-
const deleteFramesBeforeTimestamp = ({
|
|
1672
|
-
logLevel,
|
|
1673
|
-
src,
|
|
1674
|
-
timestampInSeconds
|
|
1675
|
-
}) => {
|
|
1676
|
-
const deletedTimestamps = [];
|
|
1677
|
-
for (const frameTimestamp of frameTimestamps.slice()) {
|
|
1678
|
-
const isLast = frameTimestamp === frameTimestamps[frameTimestamps.length - 1];
|
|
1679
|
-
if (isLast) {
|
|
1680
|
-
continue;
|
|
1681
|
-
}
|
|
1682
|
-
if (frameTimestamp < timestampInSeconds) {
|
|
1683
|
-
if (!frames[frameTimestamp]) {
|
|
1684
|
-
continue;
|
|
1685
|
-
}
|
|
1686
|
-
allocationSize -= frames[frameTimestamp].allocationSize();
|
|
1687
|
-
frameTimestamps.splice(frameTimestamps.indexOf(frameTimestamp), 1);
|
|
1688
|
-
frames[frameTimestamp].close();
|
|
1689
|
-
delete frames[frameTimestamp];
|
|
1690
|
-
deletedTimestamps.push(frameTimestamp);
|
|
1691
|
-
}
|
|
1692
|
-
}
|
|
1693
|
-
if (deletedTimestamps.length > 0) {
|
|
1694
|
-
Internals8.Log.verbose({ logLevel, tag: "@remotion/media" }, `Deleted ${deletedTimestamps.length} frame${deletedTimestamps.length === 1 ? "" : "s"} ${renderTimestampRange(deletedTimestamps)} for src ${src} because it is lower than ${timestampInSeconds}. Remaining: ${renderTimestampRange(frameTimestamps)}`);
|
|
1695
|
-
}
|
|
1850
|
+
return { framesDeleted };
|
|
1696
1851
|
};
|
|
1697
1852
|
const getOpenFrameCount = () => {
|
|
1698
1853
|
return {
|
|
@@ -1711,13 +1866,11 @@ var makeKeyframeBank = ({
|
|
|
1711
1866
|
queue = queue.then(() => getFrameFromTimestamp(timestamp));
|
|
1712
1867
|
return queue;
|
|
1713
1868
|
},
|
|
1714
|
-
prepareForDeletion
|
|
1715
|
-
queue = queue.then(() => prepareForDeletion(logLevel));
|
|
1716
|
-
return queue;
|
|
1717
|
-
},
|
|
1869
|
+
prepareForDeletion,
|
|
1718
1870
|
hasTimestampInSecond,
|
|
1719
1871
|
addFrame,
|
|
1720
1872
|
deleteFramesBeforeTimestamp,
|
|
1873
|
+
src,
|
|
1721
1874
|
getOpenFrameCount,
|
|
1722
1875
|
getLastUsed
|
|
1723
1876
|
};
|
|
@@ -1829,7 +1982,8 @@ var getFramesSinceKeyframe = async ({
|
|
|
1829
1982
|
packetSink,
|
|
1830
1983
|
videoSampleSink,
|
|
1831
1984
|
startPacket,
|
|
1832
|
-
logLevel
|
|
1985
|
+
logLevel,
|
|
1986
|
+
src
|
|
1833
1987
|
}) => {
|
|
1834
1988
|
const nextKeyPacket = await packetSink.getNextKeyPacket(startPacket, {
|
|
1835
1989
|
verifyKeyPackets: true
|
|
@@ -1839,7 +1993,8 @@ var getFramesSinceKeyframe = async ({
|
|
|
1839
1993
|
startTimestampInSeconds: startPacket.timestamp,
|
|
1840
1994
|
endTimestampInSeconds: nextKeyPacket ? nextKeyPacket.timestamp : Infinity,
|
|
1841
1995
|
sampleIterator,
|
|
1842
|
-
logLevel
|
|
1996
|
+
logLevel,
|
|
1997
|
+
src
|
|
1843
1998
|
});
|
|
1844
1999
|
return keyframeBank;
|
|
1845
2000
|
};
|
|
@@ -1891,6 +2046,7 @@ var makeKeyframeManager = () => {
|
|
|
1891
2046
|
const getTheKeyframeBankMostInThePast = async () => {
|
|
1892
2047
|
let mostInThePast = null;
|
|
1893
2048
|
let mostInThePastBank = null;
|
|
2049
|
+
let numberOfBanks = 0;
|
|
1894
2050
|
for (const src in sources) {
|
|
1895
2051
|
for (const b in sources[src]) {
|
|
1896
2052
|
const bank = await sources[src][b];
|
|
@@ -1899,26 +2055,38 @@ var makeKeyframeManager = () => {
|
|
|
1899
2055
|
mostInThePast = lastUsed;
|
|
1900
2056
|
mostInThePastBank = { src, bank };
|
|
1901
2057
|
}
|
|
2058
|
+
numberOfBanks++;
|
|
1902
2059
|
}
|
|
1903
2060
|
}
|
|
1904
2061
|
if (!mostInThePastBank) {
|
|
1905
2062
|
throw new Error("No keyframe bank found");
|
|
1906
2063
|
}
|
|
1907
|
-
return mostInThePastBank;
|
|
2064
|
+
return { mostInThePastBank, numberOfBanks };
|
|
1908
2065
|
};
|
|
1909
2066
|
const deleteOldestKeyframeBank = async (logLevel) => {
|
|
1910
|
-
const {
|
|
2067
|
+
const {
|
|
2068
|
+
mostInThePastBank: { bank: mostInThePastBank, src: mostInThePastSrc },
|
|
2069
|
+
numberOfBanks
|
|
2070
|
+
} = await getTheKeyframeBankMostInThePast();
|
|
2071
|
+
if (numberOfBanks < 2) {
|
|
2072
|
+
return { finish: true };
|
|
2073
|
+
}
|
|
1911
2074
|
if (mostInThePastBank) {
|
|
1912
|
-
|
|
2075
|
+
const { framesDeleted } = mostInThePastBank.prepareForDeletion(logLevel);
|
|
1913
2076
|
delete sources[mostInThePastSrc][mostInThePastBank.startTimestampInSeconds];
|
|
1914
|
-
Internals9.Log.verbose({ logLevel, tag: "@remotion/media" }, `Deleted frames for src ${mostInThePastSrc} from ${mostInThePastBank.startTimestampInSeconds}sec to ${mostInThePastBank.endTimestampInSeconds}sec to free up memory.`);
|
|
2077
|
+
Internals9.Log.verbose({ logLevel, tag: "@remotion/media" }, `Deleted ${framesDeleted} frames for src ${mostInThePastSrc} from ${mostInThePastBank.startTimestampInSeconds}sec to ${mostInThePastBank.endTimestampInSeconds}sec to free up memory.`);
|
|
1915
2078
|
}
|
|
2079
|
+
return { finish: false };
|
|
1916
2080
|
};
|
|
1917
2081
|
const ensureToStayUnderMaxCacheSize = async (logLevel) => {
|
|
1918
2082
|
let cacheStats = await getTotalCacheStats();
|
|
1919
2083
|
const maxCacheSize = getMaxVideoCacheSize(logLevel);
|
|
1920
2084
|
while (cacheStats.totalSize > maxCacheSize) {
|
|
1921
|
-
await deleteOldestKeyframeBank(logLevel);
|
|
2085
|
+
const { finish } = await deleteOldestKeyframeBank(logLevel);
|
|
2086
|
+
if (finish) {
|
|
2087
|
+
break;
|
|
2088
|
+
}
|
|
2089
|
+
Internals9.Log.verbose({ logLevel, tag: "@remotion/media" }, "Deleted oldest keyframe bank to stay under max cache size", (cacheStats.totalSize / 1024 / 1024).toFixed(1), "out of", (maxCacheSize / 1024 / 1024).toFixed(1));
|
|
1922
2090
|
cacheStats = await getTotalCacheStats();
|
|
1923
2091
|
}
|
|
1924
2092
|
};
|
|
@@ -1936,14 +2104,13 @@ var makeKeyframeManager = () => {
|
|
|
1936
2104
|
const bank = await sources[src][startTimeInSeconds];
|
|
1937
2105
|
const { endTimestampInSeconds, startTimestampInSeconds } = bank;
|
|
1938
2106
|
if (endTimestampInSeconds < threshold) {
|
|
1939
|
-
|
|
2107
|
+
bank.prepareForDeletion(logLevel);
|
|
1940
2108
|
Internals9.Log.verbose({ logLevel, tag: "@remotion/media" }, `[Video] Cleared frames for src ${src} from ${startTimestampInSeconds}sec to ${endTimestampInSeconds}sec`);
|
|
1941
2109
|
delete sources[src][startTimeInSeconds];
|
|
1942
2110
|
} else {
|
|
1943
2111
|
bank.deleteFramesBeforeTimestamp({
|
|
1944
2112
|
timestampInSeconds: threshold,
|
|
1945
|
-
logLevel
|
|
1946
|
-
src
|
|
2113
|
+
logLevel
|
|
1947
2114
|
});
|
|
1948
2115
|
}
|
|
1949
2116
|
}
|
|
@@ -1973,7 +2140,8 @@ var makeKeyframeManager = () => {
|
|
|
1973
2140
|
packetSink,
|
|
1974
2141
|
videoSampleSink,
|
|
1975
2142
|
startPacket,
|
|
1976
|
-
logLevel
|
|
2143
|
+
logLevel,
|
|
2144
|
+
src
|
|
1977
2145
|
});
|
|
1978
2146
|
addKeyframeBank({ src, bank: newKeyframeBank, startTimestampInSeconds });
|
|
1979
2147
|
return newKeyframeBank;
|
|
@@ -1988,7 +2156,8 @@ var makeKeyframeManager = () => {
|
|
|
1988
2156
|
packetSink,
|
|
1989
2157
|
videoSampleSink,
|
|
1990
2158
|
startPacket,
|
|
1991
|
-
logLevel
|
|
2159
|
+
logLevel,
|
|
2160
|
+
src
|
|
1992
2161
|
});
|
|
1993
2162
|
addKeyframeBank({ src, bank: replacementKeybank, startTimestampInSeconds });
|
|
1994
2163
|
return replacementKeybank;
|
|
@@ -2062,7 +2231,7 @@ var getTotalCacheStats = async () => {
|
|
|
2062
2231
|
};
|
|
2063
2232
|
};
|
|
2064
2233
|
var getUncachedMaxCacheSize = (logLevel) => {
|
|
2065
|
-
if (window.remotion_mediaCacheSizeInBytes !== undefined && window.remotion_mediaCacheSizeInBytes !== null) {
|
|
2234
|
+
if (typeof window !== "undefined" && window.remotion_mediaCacheSizeInBytes !== undefined && window.remotion_mediaCacheSizeInBytes !== null) {
|
|
2066
2235
|
if (window.remotion_mediaCacheSizeInBytes < 240 * 1024 * 1024) {
|
|
2067
2236
|
cancelRender(new Error(`The minimum value for the "mediaCacheSizeInBytes" prop is 240MB (${240 * 1024 * 1024}), got: ${window.remotion_mediaCacheSizeInBytes}`));
|
|
2068
2237
|
}
|
|
@@ -2072,7 +2241,7 @@ var getUncachedMaxCacheSize = (logLevel) => {
|
|
|
2072
2241
|
Internals10.Log.verbose({ logLevel, tag: "@remotion/media" }, `Using cache size set using "mediaCacheSizeInBytes": ${(window.remotion_mediaCacheSizeInBytes / 1024 / 1024).toFixed(1)} MB`);
|
|
2073
2242
|
return window.remotion_mediaCacheSizeInBytes;
|
|
2074
2243
|
}
|
|
2075
|
-
if (window.remotion_initialMemoryAvailable !== undefined && window.remotion_initialMemoryAvailable !== null) {
|
|
2244
|
+
if (typeof window !== "undefined" && window.remotion_initialMemoryAvailable !== undefined && window.remotion_initialMemoryAvailable !== null) {
|
|
2076
2245
|
const value = window.remotion_initialMemoryAvailable / 2;
|
|
2077
2246
|
if (value < 500 * 1024 * 1024) {
|
|
2078
2247
|
Internals10.Log.verbose({ logLevel, tag: "@remotion/media" }, `Using cache size set based on minimum value of 500MB (which is more than half of the available system memory!)`);
|
|
@@ -2446,7 +2615,7 @@ var extractFrameAndAudio = async ({
|
|
|
2446
2615
|
};
|
|
2447
2616
|
|
|
2448
2617
|
// src/video-extraction/extract-frame-via-broadcast-channel.ts
|
|
2449
|
-
if (window.remotion_broadcastChannel && window.remotion_isMainTab) {
|
|
2618
|
+
if (typeof window !== "undefined" && window.remotion_broadcastChannel && window.remotion_isMainTab) {
|
|
2450
2619
|
window.remotion_broadcastChannel.addEventListener("message", async (event) => {
|
|
2451
2620
|
const data = event.data;
|
|
2452
2621
|
if (data.type === "request") {
|
|
@@ -2654,7 +2823,7 @@ var AudioForRendering = ({
|
|
|
2654
2823
|
loopVolumeCurveBehavior,
|
|
2655
2824
|
delayRenderRetries,
|
|
2656
2825
|
delayRenderTimeoutInMilliseconds,
|
|
2657
|
-
logLevel
|
|
2826
|
+
logLevel,
|
|
2658
2827
|
loop,
|
|
2659
2828
|
fallbackHtml5AudioProps,
|
|
2660
2829
|
audioStreamIndex,
|
|
@@ -2712,7 +2881,7 @@ var AudioForRendering = ({
|
|
|
2712
2881
|
timeInSeconds: timestamp,
|
|
2713
2882
|
durationInSeconds,
|
|
2714
2883
|
playbackRate: playbackRate ?? 1,
|
|
2715
|
-
logLevel,
|
|
2884
|
+
logLevel: logLevel ?? window.remotion_logLevel,
|
|
2716
2885
|
includeAudio: shouldRenderAudio,
|
|
2717
2886
|
includeVideo: false,
|
|
2718
2887
|
isClientSideRendering: environment.isClientSideRendering,
|
|
@@ -2726,7 +2895,10 @@ var AudioForRendering = ({
|
|
|
2726
2895
|
if (disallowFallbackToHtml5Audio) {
|
|
2727
2896
|
cancelRender2(new Error(`Unknown container format ${src}, and 'disallowFallbackToHtml5Audio' was set. Failing the render.`));
|
|
2728
2897
|
}
|
|
2729
|
-
Internals12.Log.warn({
|
|
2898
|
+
Internals12.Log.warn({
|
|
2899
|
+
logLevel: logLevel ?? window.remotion_logLevel,
|
|
2900
|
+
tag: "@remotion/media"
|
|
2901
|
+
}, `Unknown container format for ${src} (Supported formats: https://www.remotion.dev/docs/mediabunny/formats), falling back to <Html5Audio>`);
|
|
2730
2902
|
setReplaceWithHtml5Audio(true);
|
|
2731
2903
|
return;
|
|
2732
2904
|
}
|
|
@@ -2734,7 +2906,10 @@ var AudioForRendering = ({
|
|
|
2734
2906
|
if (disallowFallbackToHtml5Audio) {
|
|
2735
2907
|
cancelRender2(new Error(`Cannot decode ${src}, and 'disallowFallbackToHtml5Audio' was set. Failing the render.`));
|
|
2736
2908
|
}
|
|
2737
|
-
Internals12.Log.warn({
|
|
2909
|
+
Internals12.Log.warn({
|
|
2910
|
+
logLevel: logLevel ?? window.remotion_logLevel,
|
|
2911
|
+
tag: "@remotion/media"
|
|
2912
|
+
}, `Cannot decode ${src}, falling back to <Html5Audio>`);
|
|
2738
2913
|
setReplaceWithHtml5Audio(true);
|
|
2739
2914
|
return;
|
|
2740
2915
|
}
|
|
@@ -2745,7 +2920,10 @@ var AudioForRendering = ({
|
|
|
2745
2920
|
if (disallowFallbackToHtml5Audio) {
|
|
2746
2921
|
cancelRender2(new Error(`Cannot decode ${src}, and 'disallowFallbackToHtml5Audio' was set. Failing the render.`));
|
|
2747
2922
|
}
|
|
2748
|
-
Internals12.Log.warn({
|
|
2923
|
+
Internals12.Log.warn({
|
|
2924
|
+
logLevel: logLevel ?? window.remotion_logLevel,
|
|
2925
|
+
tag: "@remotion/media"
|
|
2926
|
+
}, `Network error fetching ${src}, falling back to <Html5Audio>`);
|
|
2749
2927
|
setReplaceWithHtml5Audio(true);
|
|
2750
2928
|
return;
|
|
2751
2929
|
}
|
|
@@ -2861,7 +3039,14 @@ Internals13.addSequenceStackTraces(Audio);
|
|
|
2861
3039
|
import { Internals as Internals16, useRemotionEnvironment as useRemotionEnvironment4 } from "remotion";
|
|
2862
3040
|
|
|
2863
3041
|
// src/video/video-for-preview.tsx
|
|
2864
|
-
import {
|
|
3042
|
+
import {
|
|
3043
|
+
useContext as useContext4,
|
|
3044
|
+
useEffect as useEffect3,
|
|
3045
|
+
useLayoutEffect as useLayoutEffect2,
|
|
3046
|
+
useMemo as useMemo4,
|
|
3047
|
+
useRef as useRef2,
|
|
3048
|
+
useState as useState4
|
|
3049
|
+
} from "react";
|
|
2865
3050
|
import { Html5Video, Internals as Internals14, useBufferState as useBufferState2, useCurrentFrame as useCurrentFrame4 } from "remotion";
|
|
2866
3051
|
import { jsx as jsx4 } from "react/jsx-runtime";
|
|
2867
3052
|
var {
|
|
@@ -2895,7 +3080,8 @@ var VideoForPreview = ({
|
|
|
2895
3080
|
stack,
|
|
2896
3081
|
disallowFallbackToOffthreadVideo,
|
|
2897
3082
|
fallbackOffthreadVideoProps,
|
|
2898
|
-
audioStreamIndex
|
|
3083
|
+
audioStreamIndex,
|
|
3084
|
+
debugOverlay
|
|
2899
3085
|
}) => {
|
|
2900
3086
|
const src = usePreload2(unpreloadedSrc);
|
|
2901
3087
|
const canvasRef = useRef2(null);
|
|
@@ -2947,9 +3133,6 @@ var VideoForPreview = ({
|
|
|
2947
3133
|
if (!videoConfig) {
|
|
2948
3134
|
throw new Error("No video config found");
|
|
2949
3135
|
}
|
|
2950
|
-
if (!src) {
|
|
2951
|
-
throw new TypeError("No `src` was passed to <NewVideoForPreview>.");
|
|
2952
|
-
}
|
|
2953
3136
|
const currentTime = frame / videoConfig.fps;
|
|
2954
3137
|
const currentTimeRef = useRef2(currentTime);
|
|
2955
3138
|
currentTimeRef.current = currentTime;
|
|
@@ -2972,10 +3155,15 @@ var VideoForPreview = ({
|
|
|
2972
3155
|
trimBefore,
|
|
2973
3156
|
fps: videoConfig.fps,
|
|
2974
3157
|
playbackRate,
|
|
2975
|
-
audioStreamIndex
|
|
3158
|
+
audioStreamIndex,
|
|
3159
|
+
debugOverlay,
|
|
3160
|
+
bufferState: buffer
|
|
2976
3161
|
});
|
|
2977
3162
|
mediaPlayerRef.current = player;
|
|
2978
3163
|
player.initialize(currentTimeRef.current).then((result) => {
|
|
3164
|
+
if (result.type === "disposed") {
|
|
3165
|
+
return;
|
|
3166
|
+
}
|
|
2979
3167
|
if (result.type === "unknown-container-format") {
|
|
2980
3168
|
if (disallowFallbackToOffthreadVideo) {
|
|
2981
3169
|
throw new Error(`Unknown container format ${preloadedSrc}, and 'disallowFallbackToOffthreadVideo' was set.`);
|
|
@@ -3013,16 +3201,16 @@ var VideoForPreview = ({
|
|
|
3013
3201
|
setMediaDurationInSeconds(result.durationInSeconds);
|
|
3014
3202
|
}
|
|
3015
3203
|
}).catch((error) => {
|
|
3016
|
-
Internals14.Log.error({ logLevel, tag: "@remotion/media" }, "[
|
|
3204
|
+
Internals14.Log.error({ logLevel, tag: "@remotion/media" }, "[VideoForPreview] Failed to initialize MediaPlayer", error);
|
|
3017
3205
|
setShouldFallbackToNativeVideo(true);
|
|
3018
3206
|
});
|
|
3019
3207
|
} catch (error) {
|
|
3020
|
-
Internals14.Log.error({ logLevel, tag: "@remotion/media" }, "[
|
|
3208
|
+
Internals14.Log.error({ logLevel, tag: "@remotion/media" }, "[VideoForPreview] MediaPlayer initialization failed", error);
|
|
3021
3209
|
setShouldFallbackToNativeVideo(true);
|
|
3022
3210
|
}
|
|
3023
3211
|
return () => {
|
|
3024
3212
|
if (mediaPlayerRef.current) {
|
|
3025
|
-
Internals14.Log.trace({ logLevel, tag: "@remotion/media" }, `[
|
|
3213
|
+
Internals14.Log.trace({ logLevel, tag: "@remotion/media" }, `[VideoForPreview] Disposing MediaPlayer`);
|
|
3026
3214
|
mediaPlayerRef.current.dispose();
|
|
3027
3215
|
mediaPlayerRef.current = null;
|
|
3028
3216
|
}
|
|
@@ -3039,7 +3227,9 @@ var VideoForPreview = ({
|
|
|
3039
3227
|
videoConfig.fps,
|
|
3040
3228
|
playbackRate,
|
|
3041
3229
|
disallowFallbackToOffthreadVideo,
|
|
3042
|
-
audioStreamIndex
|
|
3230
|
+
audioStreamIndex,
|
|
3231
|
+
debugOverlay,
|
|
3232
|
+
buffer
|
|
3043
3233
|
]);
|
|
3044
3234
|
const classNameValue = useMemo4(() => {
|
|
3045
3235
|
return [Internals14.OBJECTFIT_CONTAIN_CLASS_NAME, className].filter(Internals14.truthy).join(" ");
|
|
@@ -3050,18 +3240,18 @@ var VideoForPreview = ({
|
|
|
3050
3240
|
return;
|
|
3051
3241
|
if (playing) {
|
|
3052
3242
|
mediaPlayer.play().catch((error) => {
|
|
3053
|
-
Internals14.Log.error({ logLevel, tag: "@remotion/media" }, "[
|
|
3243
|
+
Internals14.Log.error({ logLevel, tag: "@remotion/media" }, "[VideoForPreview] Failed to play", error);
|
|
3054
3244
|
});
|
|
3055
3245
|
} else {
|
|
3056
3246
|
mediaPlayer.pause();
|
|
3057
3247
|
}
|
|
3058
3248
|
}, [playing, logLevel, mediaPlayerReady]);
|
|
3059
|
-
|
|
3249
|
+
useLayoutEffect2(() => {
|
|
3060
3250
|
const mediaPlayer = mediaPlayerRef.current;
|
|
3061
3251
|
if (!mediaPlayer || !mediaPlayerReady)
|
|
3062
3252
|
return;
|
|
3063
3253
|
mediaPlayer.seekTo(currentTime);
|
|
3064
|
-
Internals14.Log.trace({ logLevel, tag: "@remotion/media" }, `[
|
|
3254
|
+
Internals14.Log.trace({ logLevel, tag: "@remotion/media" }, `[VideoForPreview] Updating target time to ${currentTime.toFixed(3)}s`);
|
|
3065
3255
|
}, [currentTime, logLevel, mediaPlayerReady]);
|
|
3066
3256
|
useEffect3(() => {
|
|
3067
3257
|
const mediaPlayer = mediaPlayerRef.current;
|
|
@@ -3071,11 +3261,11 @@ var VideoForPreview = ({
|
|
|
3071
3261
|
const unsubscribe = mediaPlayer.onBufferingChange((newBufferingState) => {
|
|
3072
3262
|
if (newBufferingState && !currentBlock) {
|
|
3073
3263
|
currentBlock = buffer.delayPlayback();
|
|
3074
|
-
Internals14.Log.trace({ logLevel, tag: "@remotion/media" }, "[
|
|
3264
|
+
Internals14.Log.trace({ logLevel, tag: "@remotion/media" }, "[VideoForPreview] MediaPlayer buffering - blocking Remotion playback");
|
|
3075
3265
|
} else if (!newBufferingState && currentBlock) {
|
|
3076
3266
|
currentBlock.unblock();
|
|
3077
3267
|
currentBlock = null;
|
|
3078
|
-
Internals14.Log.trace({ logLevel, tag: "@remotion/media" }, "[
|
|
3268
|
+
Internals14.Log.trace({ logLevel, tag: "@remotion/media" }, "[VideoForPreview] MediaPlayer unbuffering - unblocking Remotion playback");
|
|
3079
3269
|
}
|
|
3080
3270
|
});
|
|
3081
3271
|
return () => {
|
|
@@ -3100,6 +3290,13 @@ var VideoForPreview = ({
|
|
|
3100
3290
|
}
|
|
3101
3291
|
mediaPlayer.setVolume(userPreferredVolume);
|
|
3102
3292
|
}, [userPreferredVolume, mediaPlayerReady]);
|
|
3293
|
+
useEffect3(() => {
|
|
3294
|
+
const mediaPlayer = mediaPlayerRef.current;
|
|
3295
|
+
if (!mediaPlayer || !mediaPlayerReady) {
|
|
3296
|
+
return;
|
|
3297
|
+
}
|
|
3298
|
+
mediaPlayer.setDebugOverlay(debugOverlay);
|
|
3299
|
+
}, [debugOverlay, mediaPlayerReady]);
|
|
3103
3300
|
const effectivePlaybackRate = useMemo4(() => playbackRate * globalPlaybackRate, [playbackRate, globalPlaybackRate]);
|
|
3104
3301
|
useEffect3(() => {
|
|
3105
3302
|
const mediaPlayer = mediaPlayerRef.current;
|
|
@@ -3168,7 +3365,7 @@ var VideoForPreview = ({
|
|
|
3168
3365
|
// src/video/video-for-rendering.tsx
|
|
3169
3366
|
import {
|
|
3170
3367
|
useContext as useContext5,
|
|
3171
|
-
useLayoutEffect as
|
|
3368
|
+
useLayoutEffect as useLayoutEffect3,
|
|
3172
3369
|
useMemo as useMemo5,
|
|
3173
3370
|
useRef as useRef3,
|
|
3174
3371
|
useState as useState5
|
|
@@ -3183,26 +3380,6 @@ import {
|
|
|
3183
3380
|
useRemotionEnvironment as useRemotionEnvironment3,
|
|
3184
3381
|
useVideoConfig as useVideoConfig2
|
|
3185
3382
|
} from "remotion";
|
|
3186
|
-
|
|
3187
|
-
// ../core/src/calculate-media-duration.ts
|
|
3188
|
-
var calculateMediaDuration = ({
|
|
3189
|
-
trimAfter,
|
|
3190
|
-
mediaDurationInFrames,
|
|
3191
|
-
playbackRate,
|
|
3192
|
-
trimBefore
|
|
3193
|
-
}) => {
|
|
3194
|
-
let duration = mediaDurationInFrames;
|
|
3195
|
-
if (typeof trimAfter !== "undefined") {
|
|
3196
|
-
duration = trimAfter;
|
|
3197
|
-
}
|
|
3198
|
-
if (typeof trimBefore !== "undefined") {
|
|
3199
|
-
duration -= trimBefore;
|
|
3200
|
-
}
|
|
3201
|
-
const actualDuration = duration / playbackRate;
|
|
3202
|
-
return Math.floor(actualDuration);
|
|
3203
|
-
};
|
|
3204
|
-
|
|
3205
|
-
// src/video/video-for-rendering.tsx
|
|
3206
3383
|
import { jsx as jsx5 } from "react/jsx-runtime";
|
|
3207
3384
|
var VideoForRendering = ({
|
|
3208
3385
|
volume: volumeProp,
|
|
@@ -3245,7 +3422,9 @@ var VideoForRendering = ({
|
|
|
3245
3422
|
const { delayRender, continueRender } = useDelayRender2();
|
|
3246
3423
|
const canvasRef = useRef3(null);
|
|
3247
3424
|
const [replaceWithOffthreadVideo, setReplaceWithOffthreadVideo] = useState5(false);
|
|
3248
|
-
|
|
3425
|
+
const audioEnabled = Internals15.useAudioEnabled();
|
|
3426
|
+
const videoEnabled = Internals15.useVideoEnabled();
|
|
3427
|
+
useLayoutEffect3(() => {
|
|
3249
3428
|
if (!canvasRef.current) {
|
|
3250
3429
|
return;
|
|
3251
3430
|
}
|
|
@@ -3259,7 +3438,7 @@ var VideoForRendering = ({
|
|
|
3259
3438
|
timeoutInMilliseconds: delayRenderTimeoutInMilliseconds ?? undefined
|
|
3260
3439
|
});
|
|
3261
3440
|
const shouldRenderAudio = (() => {
|
|
3262
|
-
if (!
|
|
3441
|
+
if (!audioEnabled) {
|
|
3263
3442
|
return false;
|
|
3264
3443
|
}
|
|
3265
3444
|
if (muted) {
|
|
@@ -3274,7 +3453,7 @@ var VideoForRendering = ({
|
|
|
3274
3453
|
playbackRate,
|
|
3275
3454
|
logLevel,
|
|
3276
3455
|
includeAudio: shouldRenderAudio,
|
|
3277
|
-
includeVideo:
|
|
3456
|
+
includeVideo: videoEnabled,
|
|
3278
3457
|
isClientSideRendering: environment.isClientSideRendering,
|
|
3279
3458
|
loop,
|
|
3280
3459
|
audioStreamIndex,
|
|
@@ -3344,7 +3523,7 @@ var VideoForRendering = ({
|
|
|
3344
3523
|
context.canvas.style.aspectRatio = `${context.canvas.width} / ${context.canvas.height}`;
|
|
3345
3524
|
context.drawImage(imageBitmap, 0, 0);
|
|
3346
3525
|
imageBitmap.close();
|
|
3347
|
-
} else if (
|
|
3526
|
+
} else if (videoEnabled) {
|
|
3348
3527
|
const context = canvasRef.current?.getContext("2d", {
|
|
3349
3528
|
alpha: true
|
|
3350
3529
|
});
|
|
@@ -3412,7 +3591,9 @@ var VideoForRendering = ({
|
|
|
3412
3591
|
disallowFallbackToOffthreadVideo,
|
|
3413
3592
|
toneFrequency,
|
|
3414
3593
|
trimAfterValue,
|
|
3415
|
-
trimBeforeValue
|
|
3594
|
+
trimBeforeValue,
|
|
3595
|
+
audioEnabled,
|
|
3596
|
+
videoEnabled
|
|
3416
3597
|
]);
|
|
3417
3598
|
const classNameValue = useMemo5(() => {
|
|
3418
3599
|
return [Internals15.OBJECTFIT_CONTAIN_CLASS_NAME, className].filter(Internals15.truthy).join(" ");
|
|
@@ -3458,7 +3639,7 @@ var VideoForRendering = ({
|
|
|
3458
3639
|
}
|
|
3459
3640
|
return /* @__PURE__ */ jsx5(Loop, {
|
|
3460
3641
|
layout: "none",
|
|
3461
|
-
durationInFrames: calculateMediaDuration({
|
|
3642
|
+
durationInFrames: Internals15.calculateMediaDuration({
|
|
3462
3643
|
trimAfter: trimAfterValue,
|
|
3463
3644
|
mediaDurationInFrames: replaceWithOffthreadVideo.durationInSeconds * fps,
|
|
3464
3645
|
playbackRate,
|
|
@@ -3500,7 +3681,8 @@ var InnerVideo = ({
|
|
|
3500
3681
|
volume,
|
|
3501
3682
|
stack,
|
|
3502
3683
|
toneFrequency,
|
|
3503
|
-
showInTimeline
|
|
3684
|
+
showInTimeline,
|
|
3685
|
+
debugOverlay
|
|
3504
3686
|
}) => {
|
|
3505
3687
|
const environment = useRemotionEnvironment4();
|
|
3506
3688
|
if (typeof src !== "string") {
|
|
@@ -3561,7 +3743,8 @@ var InnerVideo = ({
|
|
|
3561
3743
|
trimBefore: trimBeforeValue,
|
|
3562
3744
|
stack: stack ?? null,
|
|
3563
3745
|
disallowFallbackToOffthreadVideo,
|
|
3564
|
-
fallbackOffthreadVideoProps
|
|
3746
|
+
fallbackOffthreadVideoProps,
|
|
3747
|
+
debugOverlay: debugOverlay ?? false
|
|
3565
3748
|
});
|
|
3566
3749
|
};
|
|
3567
3750
|
var Video = ({
|
|
@@ -3585,7 +3768,8 @@ var Video = ({
|
|
|
3585
3768
|
trimBefore,
|
|
3586
3769
|
volume,
|
|
3587
3770
|
stack,
|
|
3588
|
-
toneFrequency
|
|
3771
|
+
toneFrequency,
|
|
3772
|
+
debugOverlay
|
|
3589
3773
|
}) => {
|
|
3590
3774
|
return /* @__PURE__ */ jsx6(InnerVideo, {
|
|
3591
3775
|
audioStreamIndex: audioStreamIndex ?? 0,
|
|
@@ -3594,7 +3778,7 @@ var Video = ({
|
|
|
3594
3778
|
delayRenderTimeoutInMilliseconds: delayRenderTimeoutInMilliseconds ?? null,
|
|
3595
3779
|
disallowFallbackToOffthreadVideo: disallowFallbackToOffthreadVideo ?? false,
|
|
3596
3780
|
fallbackOffthreadVideoProps: fallbackOffthreadVideoProps ?? {},
|
|
3597
|
-
logLevel: logLevel ?? window.remotion_logLevel,
|
|
3781
|
+
logLevel: logLevel ?? (typeof window !== "undefined" ? window.remotion_logLevel : "info"),
|
|
3598
3782
|
loop: loop ?? false,
|
|
3599
3783
|
loopVolumeCurveBehavior: loopVolumeCurveBehavior ?? "repeat",
|
|
3600
3784
|
muted: muted ?? false,
|
|
@@ -3608,7 +3792,8 @@ var Video = ({
|
|
|
3608
3792
|
trimBefore,
|
|
3609
3793
|
volume: volume ?? 1,
|
|
3610
3794
|
toneFrequency: toneFrequency ?? 1,
|
|
3611
|
-
stack
|
|
3795
|
+
stack,
|
|
3796
|
+
debugOverlay: debugOverlay ?? false
|
|
3612
3797
|
});
|
|
3613
3798
|
};
|
|
3614
3799
|
Internals16.addSequenceStackTraces(Video);
|