@remotion/studio 4.0.452 → 4.0.453
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-waveform-worker.d.ts +1 -0
- package/dist/audio-waveform-worker.js +102 -0
- package/dist/components/AudioWaveform.d.ts +2 -0
- package/dist/components/AudioWaveform.js +166 -18
- package/dist/components/Timeline/LoopedIndicator.js +5 -19
- package/dist/components/Timeline/TimelineSequence.js +18 -10
- package/dist/components/Timeline/TimelineVideoInfo.d.ts +2 -0
- package/dist/components/Timeline/TimelineVideoInfo.js +51 -12
- package/dist/components/audio-waveform-worker-types.d.ts +28 -0
- package/dist/components/audio-waveform-worker-types.js +2 -0
- package/dist/components/draw-peaks.d.ts +1 -1
- package/dist/components/load-waveform-peaks.d.ts +11 -1
- package/dist/components/load-waveform-peaks.js +22 -33
- package/dist/components/looped-media-timeline.d.ts +6 -0
- package/dist/components/looped-media-timeline.js +14 -0
- package/dist/components/slice-waveform-peaks.d.ts +7 -0
- package/dist/components/slice-waveform-peaks.js +15 -0
- package/dist/components/waveform-peak-processor.d.ts +23 -0
- package/dist/components/waveform-peak-processor.js +77 -0
- package/dist/esm/audio-waveform-worker.mjs +345 -0
- package/dist/esm/{chunk-hxr6txpe.js → chunk-hn4803e7.js} +398 -98
- package/dist/esm/internals.mjs +398 -98
- package/dist/esm/previewEntry.mjs +398 -98
- package/dist/esm/renderEntry.mjs +1 -1
- package/dist/helpers/calculate-timeline.js +16 -0
- package/dist/helpers/get-timeline-nestedness.js +2 -1
- package/dist/make-audio-waveform-worker.d.ts +1 -0
- package/dist/make-audio-waveform-worker.js +10 -0
- package/package.json +18 -9
|
@@ -20439,7 +20439,8 @@ var getTimelineNestedLevel = (sequence, allSequences, depth) => {
|
|
|
20439
20439
|
if (!parentSequence) {
|
|
20440
20440
|
throw new Error("has parentId but no parent");
|
|
20441
20441
|
}
|
|
20442
|
-
|
|
20442
|
+
const parentContributes = parentSequence.showInTimeline;
|
|
20443
|
+
return getTimelineNestedLevel(parentSequence, allSequences, parentContributes ? depth + 1 : depth);
|
|
20443
20444
|
};
|
|
20444
20445
|
|
|
20445
20446
|
// src/helpers/get-timeline-sequence-hash.ts
|
|
@@ -20488,6 +20489,19 @@ var getTimelineSequenceSequenceSortKey = (track, tracks, sameHashes = {}, nonceR
|
|
|
20488
20489
|
};
|
|
20489
20490
|
|
|
20490
20491
|
// src/helpers/calculate-timeline.ts
|
|
20492
|
+
var getInheritedLoopDisplay = (sequence, sequences) => {
|
|
20493
|
+
if (sequence.loopDisplay) {
|
|
20494
|
+
return sequence.loopDisplay;
|
|
20495
|
+
}
|
|
20496
|
+
if (!sequence.parent) {
|
|
20497
|
+
return;
|
|
20498
|
+
}
|
|
20499
|
+
const parent = sequences.find((s) => s.id === sequence.parent);
|
|
20500
|
+
if (!parent) {
|
|
20501
|
+
return;
|
|
20502
|
+
}
|
|
20503
|
+
return getInheritedLoopDisplay(parent, sequences);
|
|
20504
|
+
};
|
|
20491
20505
|
var calculateTimeline = ({
|
|
20492
20506
|
sequences
|
|
20493
20507
|
}) => {
|
|
@@ -20516,7 +20530,8 @@ var calculateTimeline = ({
|
|
|
20516
20530
|
sequence: {
|
|
20517
20531
|
...sequence,
|
|
20518
20532
|
from: visibleStart,
|
|
20519
|
-
duration: visibleDuration
|
|
20533
|
+
duration: visibleDuration,
|
|
20534
|
+
loopDisplay: sequence.type === "audio" || sequence.type === "video" ? getInheritedLoopDisplay(sequence, sortedSequences) : sequence.loopDisplay
|
|
20520
20535
|
},
|
|
20521
20536
|
depth: getTimelineNestedLevel(sequence, sortedSequences, 0),
|
|
20522
20537
|
hash: actualHash,
|
|
@@ -23152,6 +23167,13 @@ var useMaxMediaDuration = (s, fps) => {
|
|
|
23152
23167
|
import { useEffect as useEffect72, useMemo as useMemo119, useRef as useRef43, useState as useState77 } from "react";
|
|
23153
23168
|
import { Internals as Internals55 } from "remotion";
|
|
23154
23169
|
|
|
23170
|
+
// src/make-audio-waveform-worker.ts
|
|
23171
|
+
var makeAudioWaveformWorker = () => {
|
|
23172
|
+
return new Worker(new URL("./audio-waveform-worker.mjs", import.meta.url), {
|
|
23173
|
+
type: "module"
|
|
23174
|
+
});
|
|
23175
|
+
};
|
|
23176
|
+
|
|
23155
23177
|
// src/components/parse-color.ts
|
|
23156
23178
|
var colorCache = new Map;
|
|
23157
23179
|
var parseColor = (color) => {
|
|
@@ -23227,12 +23249,107 @@ var drawBars = (canvas, peaks, color, volume, width) => {
|
|
|
23227
23249
|
|
|
23228
23250
|
// src/components/load-waveform-peaks.ts
|
|
23229
23251
|
import { ALL_FORMATS as ALL_FORMATS3, AudioSampleSink, Input as Input3, UrlSource as UrlSource3 } from "mediabunny";
|
|
23252
|
+
|
|
23253
|
+
// src/components/waveform-peak-processor.ts
|
|
23254
|
+
var emitWaveformProgress = ({
|
|
23255
|
+
completedPeaks,
|
|
23256
|
+
final,
|
|
23257
|
+
onProgress,
|
|
23258
|
+
peaks,
|
|
23259
|
+
totalPeaks
|
|
23260
|
+
}) => {
|
|
23261
|
+
onProgress?.({
|
|
23262
|
+
peaks,
|
|
23263
|
+
completedPeaks,
|
|
23264
|
+
totalPeaks,
|
|
23265
|
+
final
|
|
23266
|
+
});
|
|
23267
|
+
};
|
|
23268
|
+
var createWaveformPeakProcessor = ({
|
|
23269
|
+
totalPeaks,
|
|
23270
|
+
samplesPerPeak,
|
|
23271
|
+
onProgress,
|
|
23272
|
+
progressIntervalInMs,
|
|
23273
|
+
now
|
|
23274
|
+
}) => {
|
|
23275
|
+
const peaks = new Float32Array(totalPeaks);
|
|
23276
|
+
let peakIndex = 0;
|
|
23277
|
+
let peakMax = 0;
|
|
23278
|
+
let sampleInPeak = 0;
|
|
23279
|
+
let lastProgressAt = 0;
|
|
23280
|
+
let lastProgressPeak = 0;
|
|
23281
|
+
const emitProgress = (force) => {
|
|
23282
|
+
const timestamp = now();
|
|
23283
|
+
if (!force && peakIndex === lastProgressPeak && sampleInPeak === 0) {
|
|
23284
|
+
return;
|
|
23285
|
+
}
|
|
23286
|
+
if (!force && timestamp - lastProgressAt < progressIntervalInMs) {
|
|
23287
|
+
return;
|
|
23288
|
+
}
|
|
23289
|
+
lastProgressAt = timestamp;
|
|
23290
|
+
lastProgressPeak = peakIndex;
|
|
23291
|
+
emitWaveformProgress({
|
|
23292
|
+
peaks,
|
|
23293
|
+
completedPeaks: peakIndex,
|
|
23294
|
+
totalPeaks,
|
|
23295
|
+
final: force,
|
|
23296
|
+
onProgress
|
|
23297
|
+
});
|
|
23298
|
+
};
|
|
23299
|
+
return {
|
|
23300
|
+
peaks,
|
|
23301
|
+
processSampleChunk: (floats, channels) => {
|
|
23302
|
+
const frameCount = Math.floor(floats.length / Math.max(1, channels));
|
|
23303
|
+
for (let frame2 = 0;frame2 < frameCount; frame2++) {
|
|
23304
|
+
let framePeak = 0;
|
|
23305
|
+
for (let channel = 0;channel < channels; channel++) {
|
|
23306
|
+
const sampleIndex = frame2 * channels + channel;
|
|
23307
|
+
const abs = Math.abs(floats[sampleIndex] ?? 0);
|
|
23308
|
+
if (abs > framePeak) {
|
|
23309
|
+
framePeak = abs;
|
|
23310
|
+
}
|
|
23311
|
+
}
|
|
23312
|
+
if (framePeak > peakMax) {
|
|
23313
|
+
peakMax = framePeak;
|
|
23314
|
+
}
|
|
23315
|
+
sampleInPeak++;
|
|
23316
|
+
if (sampleInPeak >= samplesPerPeak) {
|
|
23317
|
+
if (peakIndex < totalPeaks) {
|
|
23318
|
+
peaks[peakIndex] = peakMax;
|
|
23319
|
+
}
|
|
23320
|
+
peakIndex++;
|
|
23321
|
+
peakMax = 0;
|
|
23322
|
+
sampleInPeak = 0;
|
|
23323
|
+
}
|
|
23324
|
+
}
|
|
23325
|
+
emitProgress(false);
|
|
23326
|
+
},
|
|
23327
|
+
finalize: () => {
|
|
23328
|
+
if (sampleInPeak > 0 && peakIndex < totalPeaks) {
|
|
23329
|
+
peaks[peakIndex] = peakMax;
|
|
23330
|
+
peakIndex++;
|
|
23331
|
+
}
|
|
23332
|
+
emitProgress(true);
|
|
23333
|
+
}
|
|
23334
|
+
};
|
|
23335
|
+
};
|
|
23336
|
+
|
|
23337
|
+
// src/components/load-waveform-peaks.ts
|
|
23230
23338
|
var TARGET_SAMPLE_RATE = 100;
|
|
23339
|
+
var DEFAULT_PROGRESS_INTERVAL_IN_MS = 50;
|
|
23231
23340
|
var peaksCache = new Map;
|
|
23232
|
-
async function loadWaveformPeaks(url, signal) {
|
|
23341
|
+
async function loadWaveformPeaks(url, signal, options) {
|
|
23233
23342
|
const cached = peaksCache.get(url);
|
|
23234
|
-
if (cached)
|
|
23343
|
+
if (cached) {
|
|
23344
|
+
emitWaveformProgress({
|
|
23345
|
+
peaks: cached,
|
|
23346
|
+
completedPeaks: cached.length,
|
|
23347
|
+
totalPeaks: cached.length,
|
|
23348
|
+
final: true,
|
|
23349
|
+
onProgress: options?.onProgress
|
|
23350
|
+
});
|
|
23235
23351
|
return cached;
|
|
23352
|
+
}
|
|
23236
23353
|
const input2 = new Input3({
|
|
23237
23354
|
formats: ALL_FORMATS3,
|
|
23238
23355
|
source: new UrlSource3(url)
|
|
@@ -23246,11 +23363,14 @@ async function loadWaveformPeaks(url, signal) {
|
|
|
23246
23363
|
const durationInSeconds = await audioTrack.computeDuration();
|
|
23247
23364
|
const totalPeaks = Math.ceil(durationInSeconds * TARGET_SAMPLE_RATE);
|
|
23248
23365
|
const samplesPerPeak = Math.max(1, Math.floor(sampleRate / TARGET_SAMPLE_RATE));
|
|
23249
|
-
const peaks = new Float32Array(totalPeaks);
|
|
23250
|
-
let peakIndex = 0;
|
|
23251
|
-
let peakMax = 0;
|
|
23252
|
-
let sampleInPeak = 0;
|
|
23253
23366
|
const sink = new AudioSampleSink(audioTrack);
|
|
23367
|
+
const processor = createWaveformPeakProcessor({
|
|
23368
|
+
totalPeaks,
|
|
23369
|
+
samplesPerPeak,
|
|
23370
|
+
onProgress: options?.onProgress,
|
|
23371
|
+
progressIntervalInMs: options?.progressIntervalInMs ?? DEFAULT_PROGRESS_INTERVAL_IN_MS,
|
|
23372
|
+
now: () => Date.now()
|
|
23373
|
+
});
|
|
23254
23374
|
for await (const sample of sink.samples()) {
|
|
23255
23375
|
if (signal.aborted) {
|
|
23256
23376
|
sample.close();
|
|
@@ -23263,34 +23383,11 @@ async function loadWaveformPeaks(url, signal) {
|
|
|
23263
23383
|
const floats = new Float32Array(bytesNeeded / 4);
|
|
23264
23384
|
sample.copyTo(floats, { format: "f32", planeIndex: 0 });
|
|
23265
23385
|
const channels = Math.max(1, sample.numberOfChannels);
|
|
23266
|
-
const frames = sample.numberOfFrames;
|
|
23267
23386
|
sample.close();
|
|
23268
|
-
|
|
23269
|
-
let framePeak = 0;
|
|
23270
|
-
for (let channel = 0;channel < channels; channel++) {
|
|
23271
|
-
const sampleIndex = frame2 * channels + channel;
|
|
23272
|
-
const abs = Math.abs(floats[sampleIndex] ?? 0);
|
|
23273
|
-
if (abs > framePeak) {
|
|
23274
|
-
framePeak = abs;
|
|
23275
|
-
}
|
|
23276
|
-
}
|
|
23277
|
-
if (framePeak > peakMax) {
|
|
23278
|
-
peakMax = framePeak;
|
|
23279
|
-
}
|
|
23280
|
-
sampleInPeak++;
|
|
23281
|
-
if (sampleInPeak >= samplesPerPeak) {
|
|
23282
|
-
if (peakIndex < totalPeaks) {
|
|
23283
|
-
peaks[peakIndex] = peakMax;
|
|
23284
|
-
}
|
|
23285
|
-
peakIndex++;
|
|
23286
|
-
peakMax = 0;
|
|
23287
|
-
sampleInPeak = 0;
|
|
23288
|
-
}
|
|
23289
|
-
}
|
|
23290
|
-
}
|
|
23291
|
-
if (sampleInPeak > 0 && peakIndex < totalPeaks) {
|
|
23292
|
-
peaks[peakIndex] = peakMax;
|
|
23387
|
+
processor.processSampleChunk(floats, channels);
|
|
23293
23388
|
}
|
|
23389
|
+
processor.finalize();
|
|
23390
|
+
const { peaks } = processor;
|
|
23294
23391
|
peaksCache.set(url, peaks);
|
|
23295
23392
|
return peaks;
|
|
23296
23393
|
} finally {
|
|
@@ -23298,8 +23395,50 @@ async function loadWaveformPeaks(url, signal) {
|
|
|
23298
23395
|
}
|
|
23299
23396
|
}
|
|
23300
23397
|
|
|
23398
|
+
// src/components/looped-media-timeline.ts
|
|
23399
|
+
var shouldTileLoopDisplay = (loopDisplay) => {
|
|
23400
|
+
return loopDisplay !== undefined && loopDisplay.numberOfTimes > 1;
|
|
23401
|
+
};
|
|
23402
|
+
var getLoopDisplayWidth = ({
|
|
23403
|
+
visualizationWidth,
|
|
23404
|
+
loopDisplay
|
|
23405
|
+
}) => {
|
|
23406
|
+
if (!shouldTileLoopDisplay(loopDisplay)) {
|
|
23407
|
+
return visualizationWidth;
|
|
23408
|
+
}
|
|
23409
|
+
return visualizationWidth / loopDisplay.numberOfTimes;
|
|
23410
|
+
};
|
|
23411
|
+
|
|
23412
|
+
// src/components/slice-waveform-peaks.ts
|
|
23413
|
+
var sliceWaveformPeaks = ({
|
|
23414
|
+
durationInFrames,
|
|
23415
|
+
fps,
|
|
23416
|
+
peaks,
|
|
23417
|
+
playbackRate,
|
|
23418
|
+
startFrom
|
|
23419
|
+
}) => {
|
|
23420
|
+
if (peaks.length === 0) {
|
|
23421
|
+
return peaks;
|
|
23422
|
+
}
|
|
23423
|
+
const startTimeInSeconds = startFrom / fps;
|
|
23424
|
+
const durationInSeconds = durationInFrames / fps * playbackRate;
|
|
23425
|
+
const startPeakIndex = Math.floor(startTimeInSeconds * TARGET_SAMPLE_RATE);
|
|
23426
|
+
const endPeakIndex = Math.ceil((startTimeInSeconds + durationInSeconds) * TARGET_SAMPLE_RATE);
|
|
23427
|
+
return peaks.subarray(Math.max(0, startPeakIndex), Math.min(peaks.length, endPeakIndex));
|
|
23428
|
+
};
|
|
23429
|
+
|
|
23301
23430
|
// src/components/AudioWaveform.tsx
|
|
23302
23431
|
import { jsx as jsx209, jsxs as jsxs101 } from "react/jsx-runtime";
|
|
23432
|
+
var EMPTY_PEAKS = new Float32Array(0);
|
|
23433
|
+
var canRetryCanvasTransfer = (err) => {
|
|
23434
|
+
return err instanceof DOMException && err.name === "InvalidStateError";
|
|
23435
|
+
};
|
|
23436
|
+
var canUseAudioWaveformWorker = () => {
|
|
23437
|
+
if (typeof Worker === "undefined" || typeof OffscreenCanvas === "undefined" || typeof HTMLCanvasElement === "undefined") {
|
|
23438
|
+
return false;
|
|
23439
|
+
}
|
|
23440
|
+
return "transferControlToOffscreen" in HTMLCanvasElement.prototype;
|
|
23441
|
+
};
|
|
23303
23442
|
var container42 = {
|
|
23304
23443
|
display: "flex",
|
|
23305
23444
|
flexDirection: "row",
|
|
@@ -23318,11 +23457,41 @@ var errorMessage = {
|
|
|
23318
23457
|
opacity: 0.75
|
|
23319
23458
|
};
|
|
23320
23459
|
var waveformCanvasStyle = {
|
|
23321
|
-
pointerEvents: "none"
|
|
23460
|
+
pointerEvents: "none",
|
|
23461
|
+
width: "100%",
|
|
23462
|
+
height: "100%"
|
|
23322
23463
|
};
|
|
23323
23464
|
var volumeCanvasStyle = {
|
|
23324
23465
|
position: "absolute"
|
|
23325
23466
|
};
|
|
23467
|
+
var drawLoopedWaveform = ({
|
|
23468
|
+
canvas,
|
|
23469
|
+
peaks,
|
|
23470
|
+
volume,
|
|
23471
|
+
visualizationWidth,
|
|
23472
|
+
loopWidth
|
|
23473
|
+
}) => {
|
|
23474
|
+
const h = canvas.height;
|
|
23475
|
+
const w = Math.ceil(visualizationWidth);
|
|
23476
|
+
const targetCanvas = document.createElement("canvas");
|
|
23477
|
+
targetCanvas.width = Math.max(1, Math.ceil(loopWidth));
|
|
23478
|
+
targetCanvas.height = h;
|
|
23479
|
+
drawBars(targetCanvas, peaks, "rgba(255, 255, 255, 0.6)", volume, targetCanvas.width);
|
|
23480
|
+
canvas.width = w;
|
|
23481
|
+
canvas.height = h;
|
|
23482
|
+
const ctx = canvas.getContext("2d");
|
|
23483
|
+
if (!ctx) {
|
|
23484
|
+
throw new Error("Failed to get canvas context");
|
|
23485
|
+
}
|
|
23486
|
+
const pattern = ctx.createPattern(targetCanvas, "repeat-x");
|
|
23487
|
+
if (!pattern) {
|
|
23488
|
+
return;
|
|
23489
|
+
}
|
|
23490
|
+
pattern.setTransform(new DOMMatrix().scaleSelf(loopWidth / targetCanvas.width, 1));
|
|
23491
|
+
ctx.clearRect(0, 0, w, h);
|
|
23492
|
+
ctx.fillStyle = pattern;
|
|
23493
|
+
ctx.fillRect(0, 0, w, h);
|
|
23494
|
+
};
|
|
23326
23495
|
var AudioWaveform = ({
|
|
23327
23496
|
src,
|
|
23328
23497
|
startFrom,
|
|
@@ -23330,10 +23499,13 @@ var AudioWaveform = ({
|
|
|
23330
23499
|
visualizationWidth,
|
|
23331
23500
|
volume,
|
|
23332
23501
|
doesVolumeChange,
|
|
23333
|
-
playbackRate
|
|
23502
|
+
playbackRate,
|
|
23503
|
+
loopDisplay
|
|
23334
23504
|
}) => {
|
|
23335
23505
|
const [peaks, setPeaks] = useState77(null);
|
|
23336
23506
|
const [error, setError] = useState77(null);
|
|
23507
|
+
const [waveformCanvasKey, setWaveformCanvasKey] = useState77(0);
|
|
23508
|
+
const canUseWorkerPath = useMemo119(() => canUseAudioWaveformWorker(), []);
|
|
23337
23509
|
const vidConf = Internals55.useUnsafeVideoConfig();
|
|
23338
23510
|
if (vidConf === null) {
|
|
23339
23511
|
throw new Error("Expected video config");
|
|
@@ -23341,8 +23513,15 @@ var AudioWaveform = ({
|
|
|
23341
23513
|
const containerRef = useRef43(null);
|
|
23342
23514
|
const waveformCanvas = useRef43(null);
|
|
23343
23515
|
const volumeCanvas = useRef43(null);
|
|
23516
|
+
const waveformWorker = useRef43(null);
|
|
23517
|
+
const hasTransferredCanvas = useRef43(false);
|
|
23518
|
+
const latestRequestId = useRef43(0);
|
|
23344
23519
|
useEffect72(() => {
|
|
23520
|
+
if (canUseWorkerPath) {
|
|
23521
|
+
return;
|
|
23522
|
+
}
|
|
23345
23523
|
const controller = new AbortController;
|
|
23524
|
+
setPeaks(null);
|
|
23346
23525
|
setError(null);
|
|
23347
23526
|
loadWaveformPeaks(src, controller.signal).then((p) => {
|
|
23348
23527
|
if (!controller.signal.aborted) {
|
|
@@ -23354,30 +23533,127 @@ var AudioWaveform = ({
|
|
|
23354
23533
|
}
|
|
23355
23534
|
});
|
|
23356
23535
|
return () => controller.abort();
|
|
23357
|
-
}, [src]);
|
|
23536
|
+
}, [canUseWorkerPath, src]);
|
|
23537
|
+
useEffect72(() => {
|
|
23538
|
+
if (!canUseWorkerPath) {
|
|
23539
|
+
return;
|
|
23540
|
+
}
|
|
23541
|
+
const canvasElement = waveformCanvas.current;
|
|
23542
|
+
if (!canvasElement || hasTransferredCanvas.current) {
|
|
23543
|
+
return;
|
|
23544
|
+
}
|
|
23545
|
+
const worker = makeAudioWaveformWorker();
|
|
23546
|
+
waveformWorker.current = worker;
|
|
23547
|
+
worker.addEventListener("message", (event) => {
|
|
23548
|
+
if (event.data.type === "error") {
|
|
23549
|
+
if (event.data.requestId !== latestRequestId.current) {
|
|
23550
|
+
return;
|
|
23551
|
+
}
|
|
23552
|
+
setError(new Error(event.data.message));
|
|
23553
|
+
}
|
|
23554
|
+
});
|
|
23555
|
+
let offscreen;
|
|
23556
|
+
try {
|
|
23557
|
+
offscreen = canvasElement.transferControlToOffscreen();
|
|
23558
|
+
} catch (err) {
|
|
23559
|
+
worker.terminate();
|
|
23560
|
+
waveformWorker.current = null;
|
|
23561
|
+
if (canRetryCanvasTransfer(err)) {
|
|
23562
|
+
setWaveformCanvasKey((key4) => key4 + 1);
|
|
23563
|
+
return;
|
|
23564
|
+
}
|
|
23565
|
+
throw err;
|
|
23566
|
+
}
|
|
23567
|
+
hasTransferredCanvas.current = true;
|
|
23568
|
+
worker.postMessage({ type: "init", canvas: offscreen }, [offscreen]);
|
|
23569
|
+
return () => {
|
|
23570
|
+
worker.postMessage({ type: "dispose" });
|
|
23571
|
+
worker.terminate();
|
|
23572
|
+
waveformWorker.current = null;
|
|
23573
|
+
hasTransferredCanvas.current = false;
|
|
23574
|
+
};
|
|
23575
|
+
}, [canUseWorkerPath, waveformCanvasKey]);
|
|
23358
23576
|
const portionPeaks = useMemo119(() => {
|
|
23359
|
-
if (
|
|
23577
|
+
if (canUseWorkerPath || !peaks) {
|
|
23360
23578
|
return null;
|
|
23361
23579
|
}
|
|
23362
|
-
|
|
23363
|
-
|
|
23364
|
-
|
|
23365
|
-
|
|
23366
|
-
|
|
23367
|
-
|
|
23580
|
+
return sliceWaveformPeaks({
|
|
23581
|
+
durationInFrames: shouldTileLoopDisplay(loopDisplay) ? loopDisplay.durationInFrames : durationInFrames,
|
|
23582
|
+
fps: vidConf.fps,
|
|
23583
|
+
peaks,
|
|
23584
|
+
playbackRate,
|
|
23585
|
+
startFrom
|
|
23586
|
+
});
|
|
23587
|
+
}, [
|
|
23588
|
+
canUseWorkerPath,
|
|
23589
|
+
durationInFrames,
|
|
23590
|
+
loopDisplay,
|
|
23591
|
+
peaks,
|
|
23592
|
+
playbackRate,
|
|
23593
|
+
startFrom,
|
|
23594
|
+
vidConf.fps
|
|
23595
|
+
]);
|
|
23368
23596
|
useEffect72(() => {
|
|
23369
23597
|
const { current: canvasElement } = waveformCanvas;
|
|
23370
23598
|
const { current: containerElement } = containerRef;
|
|
23371
|
-
if (!canvasElement || !containerElement
|
|
23599
|
+
if (!canvasElement || !containerElement) {
|
|
23372
23600
|
return;
|
|
23373
23601
|
}
|
|
23374
23602
|
const h = containerElement.clientHeight;
|
|
23375
23603
|
const w = Math.ceil(visualizationWidth);
|
|
23604
|
+
const vol = typeof volume === "number" ? volume : 1;
|
|
23605
|
+
if (canUseWorkerPath) {
|
|
23606
|
+
const worker = waveformWorker.current;
|
|
23607
|
+
if (!worker || !hasTransferredCanvas.current) {
|
|
23608
|
+
return;
|
|
23609
|
+
}
|
|
23610
|
+
latestRequestId.current += 1;
|
|
23611
|
+
setError(null);
|
|
23612
|
+
const message = {
|
|
23613
|
+
type: "render",
|
|
23614
|
+
requestId: latestRequestId.current,
|
|
23615
|
+
src,
|
|
23616
|
+
width: w,
|
|
23617
|
+
height: h,
|
|
23618
|
+
volume: vol,
|
|
23619
|
+
startFrom,
|
|
23620
|
+
durationInFrames,
|
|
23621
|
+
fps: vidConf.fps,
|
|
23622
|
+
playbackRate,
|
|
23623
|
+
loopDisplay
|
|
23624
|
+
};
|
|
23625
|
+
worker.postMessage(message);
|
|
23626
|
+
return;
|
|
23627
|
+
}
|
|
23376
23628
|
canvasElement.width = w;
|
|
23377
23629
|
canvasElement.height = h;
|
|
23378
|
-
|
|
23379
|
-
|
|
23380
|
-
|
|
23630
|
+
if (shouldTileLoopDisplay(loopDisplay)) {
|
|
23631
|
+
drawLoopedWaveform({
|
|
23632
|
+
canvas: canvasElement,
|
|
23633
|
+
peaks: portionPeaks ?? EMPTY_PEAKS,
|
|
23634
|
+
volume: vol,
|
|
23635
|
+
visualizationWidth,
|
|
23636
|
+
loopWidth: getLoopDisplayWidth({
|
|
23637
|
+
visualizationWidth,
|
|
23638
|
+
loopDisplay
|
|
23639
|
+
})
|
|
23640
|
+
});
|
|
23641
|
+
} else {
|
|
23642
|
+
drawBars(canvasElement, portionPeaks ?? EMPTY_PEAKS, "rgba(255, 255, 255, 0.6)", vol, w);
|
|
23643
|
+
}
|
|
23644
|
+
}, [
|
|
23645
|
+
canUseWorkerPath,
|
|
23646
|
+
durationInFrames,
|
|
23647
|
+
loopDisplay,
|
|
23648
|
+
playbackRate,
|
|
23649
|
+
portionPeaks,
|
|
23650
|
+
src,
|
|
23651
|
+
startFrom,
|
|
23652
|
+
vidConf.fps,
|
|
23653
|
+
visualizationWidth,
|
|
23654
|
+
volume,
|
|
23655
|
+
waveformCanvasKey
|
|
23656
|
+
]);
|
|
23381
23657
|
useEffect72(() => {
|
|
23382
23658
|
const { current: volumeCanvasElement } = volumeCanvas;
|
|
23383
23659
|
const { current: containerElement } = containerRef;
|
|
@@ -23419,7 +23695,7 @@ var AudioWaveform = ({
|
|
|
23419
23695
|
})
|
|
23420
23696
|
});
|
|
23421
23697
|
}
|
|
23422
|
-
if (!peaks) {
|
|
23698
|
+
if (!canUseWorkerPath && !peaks) {
|
|
23423
23699
|
return null;
|
|
23424
23700
|
}
|
|
23425
23701
|
return /* @__PURE__ */ jsxs101("div", {
|
|
@@ -23429,7 +23705,7 @@ var AudioWaveform = ({
|
|
|
23429
23705
|
/* @__PURE__ */ jsx209("canvas", {
|
|
23430
23706
|
ref: waveformCanvas,
|
|
23431
23707
|
style: waveformCanvasStyle
|
|
23432
|
-
}),
|
|
23708
|
+
}, waveformCanvasKey),
|
|
23433
23709
|
/* @__PURE__ */ jsx209("canvas", {
|
|
23434
23710
|
ref: volumeCanvas,
|
|
23435
23711
|
style: volumeCanvasStyle
|
|
@@ -23452,7 +23728,8 @@ var width = {
|
|
|
23452
23728
|
position: "relative"
|
|
23453
23729
|
};
|
|
23454
23730
|
var icon4 = {
|
|
23455
|
-
height: 12
|
|
23731
|
+
height: 12,
|
|
23732
|
+
filter: "drop-shadow(0 0 2px rgba(0, 0, 0, 0.9)) drop-shadow(0 1px 2px rgba(0, 0, 0, 0.8))"
|
|
23456
23733
|
};
|
|
23457
23734
|
var Icon = () => /* @__PURE__ */ jsx210("svg", {
|
|
23458
23735
|
viewBox: "0 0 512 512",
|
|
@@ -23462,44 +23739,23 @@ var Icon = () => /* @__PURE__ */ jsx210("svg", {
|
|
|
23462
23739
|
d: "M512 256c0 88.224-71.775 160-160 160H170.067l34.512 32.419c9.875 9.276 10.119 24.883.539 34.464l-10.775 10.775c-9.373 9.372-24.568 9.372-33.941 0l-92.686-92.686c-9.373-9.373-9.373-24.568 0-33.941l92.686-92.686c9.373-9.373 24.568-9.373 33.941 0l10.775 10.775c9.581 9.581 9.337 25.187-.539 34.464L170.067 352H352c52.935 0 96-43.065 96-96 0-13.958-2.996-27.228-8.376-39.204-4.061-9.039-2.284-19.626 4.723-26.633l12.183-12.183c11.499-11.499 30.965-8.526 38.312 5.982C505.814 205.624 512 230.103 512 256zM72.376 295.204C66.996 283.228 64 269.958 64 256c0-52.935 43.065-96 96-96h181.933l-34.512 32.419c-9.875 9.276-10.119 24.883-.539 34.464l10.775 10.775c9.373 9.372 24.568 9.372 33.941 0l92.686-92.686c9.373-9.373 9.373-24.568 0-33.941l-92.686-92.686c-9.373-9.373-24.568-9.373-33.941 0L306.882 29.12c-9.581 9.581-9.337 25.187.539 34.464L341.933 96H160C71.775 96 0 167.776 0 256c0 25.897 6.186 50.376 17.157 72.039 7.347 14.508 26.813 17.481 38.312 5.982l12.183-12.183c7.008-7.008 8.786-17.595 4.724-26.634z"
|
|
23463
23740
|
})
|
|
23464
23741
|
});
|
|
23465
|
-
var
|
|
23466
|
-
|
|
23467
|
-
height: 2,
|
|
23468
|
-
width: 1,
|
|
23469
|
-
background: LIGHT_COLOR
|
|
23470
|
-
};
|
|
23471
|
-
var bottomLine = {
|
|
23472
|
-
top: 0,
|
|
23473
|
-
height: 2,
|
|
23742
|
+
var verticalLine = {
|
|
23743
|
+
height: "100%",
|
|
23474
23744
|
width: 1,
|
|
23475
|
-
background:
|
|
23476
|
-
};
|
|
23477
|
-
var topContainer = {
|
|
23478
|
-
justifyContent: "flex-start",
|
|
23479
|
-
alignItems: "center"
|
|
23745
|
+
background: "rgb(255,255,255, 0.5)"
|
|
23480
23746
|
};
|
|
23481
23747
|
var centerContainer = {
|
|
23482
23748
|
justifyContent: "center",
|
|
23483
23749
|
alignItems: "center"
|
|
23484
23750
|
};
|
|
23485
|
-
var bottomContainer = {
|
|
23486
|
-
justifyContent: "flex-end",
|
|
23487
|
-
alignItems: "center"
|
|
23488
|
-
};
|
|
23489
23751
|
var LoopedIndicator = () => {
|
|
23490
23752
|
return /* @__PURE__ */ jsxs102("div", {
|
|
23491
23753
|
style: width,
|
|
23492
23754
|
children: [
|
|
23493
23755
|
/* @__PURE__ */ jsx210(AbsoluteFill3, {
|
|
23494
|
-
style:
|
|
23495
|
-
children: /* @__PURE__ */ jsx210("div", {
|
|
23496
|
-
style: topLine
|
|
23497
|
-
})
|
|
23498
|
-
}),
|
|
23499
|
-
/* @__PURE__ */ jsx210(AbsoluteFill3, {
|
|
23500
|
-
style: bottomContainer,
|
|
23756
|
+
style: centerContainer,
|
|
23501
23757
|
children: /* @__PURE__ */ jsx210("div", {
|
|
23502
|
-
style:
|
|
23758
|
+
style: verticalLine
|
|
23503
23759
|
})
|
|
23504
23760
|
}),
|
|
23505
23761
|
/* @__PURE__ */ jsx210(AbsoluteFill3, {
|
|
@@ -23971,7 +24227,8 @@ var TimelineVideoInfo = ({
|
|
|
23971
24227
|
volume,
|
|
23972
24228
|
doesVolumeChange,
|
|
23973
24229
|
premountWidth,
|
|
23974
|
-
postmountWidth
|
|
24230
|
+
postmountWidth,
|
|
24231
|
+
loopDisplay
|
|
23975
24232
|
}) => {
|
|
23976
24233
|
const { fps } = useVideoConfig5();
|
|
23977
24234
|
const ref2 = useRef45(null);
|
|
@@ -23994,25 +24251,54 @@ var TimelineVideoInfo = ({
|
|
|
23994
24251
|
return;
|
|
23995
24252
|
}
|
|
23996
24253
|
current.appendChild(canvas);
|
|
24254
|
+
const loopWidth = getLoopDisplayWidth({
|
|
24255
|
+
visualizationWidth: naturalWidth,
|
|
24256
|
+
loopDisplay
|
|
24257
|
+
});
|
|
24258
|
+
const shouldRepeatVideo = shouldTileLoopDisplay(loopDisplay);
|
|
24259
|
+
const targetCanvas = shouldRepeatVideo ? document.createElement("canvas") : canvas;
|
|
24260
|
+
targetCanvas.width = shouldRepeatVideo ? Math.max(1, Math.ceil(loopWidth)) : canvas.width;
|
|
24261
|
+
targetCanvas.height = canvas.height;
|
|
24262
|
+
const targetCtx = shouldRepeatVideo ? targetCanvas.getContext("2d") : ctx;
|
|
24263
|
+
if (!targetCtx) {
|
|
24264
|
+
current.removeChild(canvas);
|
|
24265
|
+
return;
|
|
24266
|
+
}
|
|
24267
|
+
const repeatTarget = () => {
|
|
24268
|
+
if (!shouldRepeatVideo) {
|
|
24269
|
+
return;
|
|
24270
|
+
}
|
|
24271
|
+
const pattern = ctx.createPattern(targetCanvas, "repeat-x");
|
|
24272
|
+
if (!pattern) {
|
|
24273
|
+
return;
|
|
24274
|
+
}
|
|
24275
|
+
pattern.setTransform(new DOMMatrix().scaleSelf(loopWidth / targetCanvas.width, 1));
|
|
24276
|
+
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
|
24277
|
+
ctx.fillStyle = pattern;
|
|
24278
|
+
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
|
24279
|
+
};
|
|
23997
24280
|
const filledSlots = new Map;
|
|
23998
24281
|
const fromSeconds = trimBefore / fps;
|
|
23999
|
-
const
|
|
24282
|
+
const visibleDurationInFrames = shouldRepeatVideo && loopDisplay ? loopDisplay.durationInFrames : durationInFrames;
|
|
24283
|
+
const toSeconds = fromSeconds + visibleDurationInFrames * playbackRate / fps;
|
|
24284
|
+
const targetWidth = shouldRepeatVideo ? targetCanvas.width : naturalWidth;
|
|
24000
24285
|
if (aspectRatio.current !== null) {
|
|
24001
24286
|
ensureSlots({
|
|
24002
24287
|
filledSlots,
|
|
24003
|
-
naturalWidth,
|
|
24288
|
+
naturalWidth: targetWidth,
|
|
24004
24289
|
fromSeconds,
|
|
24005
24290
|
toSeconds,
|
|
24006
24291
|
aspectRatio: aspectRatio.current
|
|
24007
24292
|
});
|
|
24008
24293
|
fillWithCachedFrames({
|
|
24009
|
-
ctx,
|
|
24010
|
-
naturalWidth,
|
|
24294
|
+
ctx: targetCtx,
|
|
24295
|
+
naturalWidth: targetWidth,
|
|
24011
24296
|
filledSlots,
|
|
24012
24297
|
src,
|
|
24013
24298
|
segmentDuration: toSeconds - fromSeconds,
|
|
24014
24299
|
fromSeconds
|
|
24015
24300
|
});
|
|
24301
|
+
repeatTarget();
|
|
24016
24302
|
const unfilled = Array.from(filledSlots.keys()).filter((timestamp) => !filledSlots.get(timestamp));
|
|
24017
24303
|
if (unfilled.length === 0) {
|
|
24018
24304
|
return () => {
|
|
@@ -24030,7 +24316,7 @@ var TimelineVideoInfo = ({
|
|
|
24030
24316
|
filledSlots,
|
|
24031
24317
|
fromSeconds,
|
|
24032
24318
|
toSeconds,
|
|
24033
|
-
naturalWidth,
|
|
24319
|
+
naturalWidth: targetWidth,
|
|
24034
24320
|
aspectRatio: aspectRatio.current
|
|
24035
24321
|
});
|
|
24036
24322
|
return Array.from(filledSlots.keys()).map((timestamp) => timestamp / WEBCODECS_TIMESCALE);
|
|
@@ -24058,17 +24344,18 @@ var TimelineVideoInfo = ({
|
|
|
24058
24344
|
filledSlots,
|
|
24059
24345
|
fromSeconds,
|
|
24060
24346
|
toSeconds,
|
|
24061
|
-
naturalWidth,
|
|
24347
|
+
naturalWidth: targetWidth,
|
|
24062
24348
|
aspectRatio: aspectRatio.current
|
|
24063
24349
|
});
|
|
24064
24350
|
fillFrameWhereItFits({
|
|
24065
|
-
ctx,
|
|
24351
|
+
ctx: targetCtx,
|
|
24066
24352
|
filledSlots,
|
|
24067
|
-
visualizationWidth:
|
|
24353
|
+
visualizationWidth: targetWidth,
|
|
24068
24354
|
frame: transformed,
|
|
24069
24355
|
segmentDuration: toSeconds - fromSeconds,
|
|
24070
24356
|
fromSeconds
|
|
24071
24357
|
});
|
|
24358
|
+
repeatTarget();
|
|
24072
24359
|
} catch (e) {
|
|
24073
24360
|
if (frame2) {
|
|
24074
24361
|
frame2.close();
|
|
@@ -24084,13 +24371,14 @@ var TimelineVideoInfo = ({
|
|
|
24084
24371
|
return;
|
|
24085
24372
|
}
|
|
24086
24373
|
fillWithCachedFrames({
|
|
24087
|
-
ctx,
|
|
24088
|
-
naturalWidth,
|
|
24374
|
+
ctx: targetCtx,
|
|
24375
|
+
naturalWidth: targetWidth,
|
|
24089
24376
|
filledSlots,
|
|
24090
24377
|
src,
|
|
24091
24378
|
segmentDuration: toSeconds - fromSeconds,
|
|
24092
24379
|
fromSeconds
|
|
24093
24380
|
});
|
|
24381
|
+
repeatTarget();
|
|
24094
24382
|
}).catch((e) => {
|
|
24095
24383
|
setError(e);
|
|
24096
24384
|
});
|
|
@@ -24102,6 +24390,7 @@ var TimelineVideoInfo = ({
|
|
|
24102
24390
|
durationInFrames,
|
|
24103
24391
|
error,
|
|
24104
24392
|
fps,
|
|
24393
|
+
loopDisplay,
|
|
24105
24394
|
naturalWidth,
|
|
24106
24395
|
playbackRate,
|
|
24107
24396
|
src,
|
|
@@ -24133,7 +24422,8 @@ var TimelineVideoInfo = ({
|
|
|
24133
24422
|
durationInFrames,
|
|
24134
24423
|
volume,
|
|
24135
24424
|
doesVolumeChange,
|
|
24136
|
-
playbackRate
|
|
24425
|
+
playbackRate,
|
|
24426
|
+
loopDisplay
|
|
24137
24427
|
})
|
|
24138
24428
|
})
|
|
24139
24429
|
]
|
|
@@ -24158,29 +24448,37 @@ var TimelineSequence = ({ s }) => {
|
|
|
24158
24448
|
var Inner4 = ({ s, windowWidth }) => {
|
|
24159
24449
|
const video = Internals56.useVideo();
|
|
24160
24450
|
const maxMediaDuration = useMaxMediaDuration(s, video?.fps ?? 30);
|
|
24451
|
+
const effectiveMaxMediaDuration = s.loopDisplay ? null : maxMediaDuration;
|
|
24161
24452
|
if (!video) {
|
|
24162
24453
|
throw new TypeError("Expected video config");
|
|
24163
24454
|
}
|
|
24164
24455
|
const frame2 = useCurrentFrame2();
|
|
24165
24456
|
const relativeFrame = frame2 - s.from;
|
|
24457
|
+
const displayDurationInFrames = s.loopDisplay ? s.loopDisplay.durationInFrames * s.loopDisplay.numberOfTimes : s.duration;
|
|
24166
24458
|
const relativeFrameWithPremount = relativeFrame + (s.premountDisplay ?? 0);
|
|
24167
|
-
const relativeFrameWithPostmount = relativeFrame -
|
|
24459
|
+
const relativeFrameWithPostmount = relativeFrame - displayDurationInFrames;
|
|
24168
24460
|
const roundedFrame = Math.round(relativeFrame * 100) / 100;
|
|
24169
|
-
const isInRange = relativeFrame >= 0 && relativeFrame <
|
|
24170
|
-
const isPremounting = relativeFrameWithPremount >= 0 && relativeFrameWithPremount <
|
|
24461
|
+
const isInRange = relativeFrame >= 0 && relativeFrame < displayDurationInFrames;
|
|
24462
|
+
const isPremounting = relativeFrameWithPremount >= 0 && relativeFrameWithPremount < displayDurationInFrames && !isInRange;
|
|
24171
24463
|
const isPostmounting = relativeFrameWithPostmount >= 0 && relativeFrameWithPostmount < (s.postmountDisplay ?? 0) && !isInRange;
|
|
24172
24464
|
const { marginLeft, width: width2, naturalWidth, premountWidth, postmountWidth } = useMemo121(() => {
|
|
24173
24465
|
return getTimelineSequenceLayout({
|
|
24174
|
-
durationInFrames:
|
|
24466
|
+
durationInFrames: displayDurationInFrames,
|
|
24175
24467
|
startFrom: s.loopDisplay ? s.from + s.loopDisplay.startOffset : s.from,
|
|
24176
24468
|
startFromMedia: s.type === "sequence" || s.type === "image" ? 0 : s.startMediaFrom,
|
|
24177
|
-
maxMediaDuration,
|
|
24469
|
+
maxMediaDuration: effectiveMaxMediaDuration,
|
|
24178
24470
|
video,
|
|
24179
24471
|
windowWidth,
|
|
24180
24472
|
premountDisplay: s.premountDisplay,
|
|
24181
24473
|
postmountDisplay: s.postmountDisplay
|
|
24182
24474
|
});
|
|
24183
|
-
}, [
|
|
24475
|
+
}, [
|
|
24476
|
+
displayDurationInFrames,
|
|
24477
|
+
effectiveMaxMediaDuration,
|
|
24478
|
+
s,
|
|
24479
|
+
video,
|
|
24480
|
+
windowWidth
|
|
24481
|
+
]);
|
|
24184
24482
|
const style11 = useMemo121(() => {
|
|
24185
24483
|
return {
|
|
24186
24484
|
background: s.type === "audio" ? AUDIO_GRADIENT : s.type === "video" ? VIDEO_GRADIENT : s.type === "image" ? IMAGE_GRADIENT : BLUE,
|
|
@@ -24195,7 +24493,7 @@ var Inner4 = ({ s, windowWidth }) => {
|
|
|
24195
24493
|
opacity: isInRange ? 1 : 0.5
|
|
24196
24494
|
};
|
|
24197
24495
|
}, [isInRange, marginLeft, s.type, width2]);
|
|
24198
|
-
if (maxMediaDuration === null) {
|
|
24496
|
+
if (maxMediaDuration === null && !s.loopDisplay) {
|
|
24199
24497
|
return null;
|
|
24200
24498
|
}
|
|
24201
24499
|
return /* @__PURE__ */ jsxs105("div", {
|
|
@@ -24238,7 +24536,8 @@ var Inner4 = ({ s, windowWidth }) => {
|
|
|
24238
24536
|
startFrom: s.startMediaFrom,
|
|
24239
24537
|
durationInFrames: s.duration,
|
|
24240
24538
|
volume: s.volume,
|
|
24241
|
-
playbackRate: s.playbackRate
|
|
24539
|
+
playbackRate: s.playbackRate,
|
|
24540
|
+
loopDisplay: s.loopDisplay
|
|
24242
24541
|
}) : null,
|
|
24243
24542
|
s.type === "video" ? /* @__PURE__ */ jsx215(TimelineVideoInfo, {
|
|
24244
24543
|
src: s.src,
|
|
@@ -24250,7 +24549,8 @@ var Inner4 = ({ s, windowWidth }) => {
|
|
|
24250
24549
|
volume: s.volume,
|
|
24251
24550
|
doesVolumeChange: s.doesVolumeChange,
|
|
24252
24551
|
premountWidth: premountWidth ?? 0,
|
|
24253
|
-
postmountWidth: postmountWidth ?? 0
|
|
24552
|
+
postmountWidth: postmountWidth ?? 0,
|
|
24553
|
+
loopDisplay: s.loopDisplay
|
|
24254
24554
|
}) : null,
|
|
24255
24555
|
s.type === "image" ? /* @__PURE__ */ jsx215(TimelineImageInfo, {
|
|
24256
24556
|
src: s.src,
|