@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
package/dist/esm/internals.mjs
CHANGED
|
@@ -20458,7 +20458,8 @@ var getTimelineNestedLevel = (sequence, allSequences, depth) => {
|
|
|
20458
20458
|
if (!parentSequence) {
|
|
20459
20459
|
throw new Error("has parentId but no parent");
|
|
20460
20460
|
}
|
|
20461
|
-
|
|
20461
|
+
const parentContributes = parentSequence.showInTimeline;
|
|
20462
|
+
return getTimelineNestedLevel(parentSequence, allSequences, parentContributes ? depth + 1 : depth);
|
|
20462
20463
|
};
|
|
20463
20464
|
|
|
20464
20465
|
// src/helpers/get-timeline-sequence-hash.ts
|
|
@@ -20507,6 +20508,19 @@ var getTimelineSequenceSequenceSortKey = (track, tracks, sameHashes = {}, nonceR
|
|
|
20507
20508
|
};
|
|
20508
20509
|
|
|
20509
20510
|
// src/helpers/calculate-timeline.ts
|
|
20511
|
+
var getInheritedLoopDisplay = (sequence, sequences) => {
|
|
20512
|
+
if (sequence.loopDisplay) {
|
|
20513
|
+
return sequence.loopDisplay;
|
|
20514
|
+
}
|
|
20515
|
+
if (!sequence.parent) {
|
|
20516
|
+
return;
|
|
20517
|
+
}
|
|
20518
|
+
const parent = sequences.find((s) => s.id === sequence.parent);
|
|
20519
|
+
if (!parent) {
|
|
20520
|
+
return;
|
|
20521
|
+
}
|
|
20522
|
+
return getInheritedLoopDisplay(parent, sequences);
|
|
20523
|
+
};
|
|
20510
20524
|
var calculateTimeline = ({
|
|
20511
20525
|
sequences
|
|
20512
20526
|
}) => {
|
|
@@ -20535,7 +20549,8 @@ var calculateTimeline = ({
|
|
|
20535
20549
|
sequence: {
|
|
20536
20550
|
...sequence,
|
|
20537
20551
|
from: visibleStart,
|
|
20538
|
-
duration: visibleDuration
|
|
20552
|
+
duration: visibleDuration,
|
|
20553
|
+
loopDisplay: sequence.type === "audio" || sequence.type === "video" ? getInheritedLoopDisplay(sequence, sortedSequences) : sequence.loopDisplay
|
|
20539
20554
|
},
|
|
20540
20555
|
depth: getTimelineNestedLevel(sequence, sortedSequences, 0),
|
|
20541
20556
|
hash: actualHash,
|
|
@@ -23171,6 +23186,13 @@ var useMaxMediaDuration = (s, fps) => {
|
|
|
23171
23186
|
import { useEffect as useEffect72, useMemo as useMemo119, useRef as useRef43, useState as useState77 } from "react";
|
|
23172
23187
|
import { Internals as Internals55 } from "remotion";
|
|
23173
23188
|
|
|
23189
|
+
// src/make-audio-waveform-worker.ts
|
|
23190
|
+
var makeAudioWaveformWorker = () => {
|
|
23191
|
+
return new Worker(new URL("./audio-waveform-worker.mjs", import.meta.url), {
|
|
23192
|
+
type: "module"
|
|
23193
|
+
});
|
|
23194
|
+
};
|
|
23195
|
+
|
|
23174
23196
|
// src/components/parse-color.ts
|
|
23175
23197
|
var colorCache = new Map;
|
|
23176
23198
|
var parseColor = (color) => {
|
|
@@ -23246,12 +23268,107 @@ var drawBars = (canvas, peaks, color, volume, width) => {
|
|
|
23246
23268
|
|
|
23247
23269
|
// src/components/load-waveform-peaks.ts
|
|
23248
23270
|
import { ALL_FORMATS as ALL_FORMATS3, AudioSampleSink, Input as Input3, UrlSource as UrlSource3 } from "mediabunny";
|
|
23271
|
+
|
|
23272
|
+
// src/components/waveform-peak-processor.ts
|
|
23273
|
+
var emitWaveformProgress = ({
|
|
23274
|
+
completedPeaks,
|
|
23275
|
+
final,
|
|
23276
|
+
onProgress,
|
|
23277
|
+
peaks,
|
|
23278
|
+
totalPeaks
|
|
23279
|
+
}) => {
|
|
23280
|
+
onProgress?.({
|
|
23281
|
+
peaks,
|
|
23282
|
+
completedPeaks,
|
|
23283
|
+
totalPeaks,
|
|
23284
|
+
final
|
|
23285
|
+
});
|
|
23286
|
+
};
|
|
23287
|
+
var createWaveformPeakProcessor = ({
|
|
23288
|
+
totalPeaks,
|
|
23289
|
+
samplesPerPeak,
|
|
23290
|
+
onProgress,
|
|
23291
|
+
progressIntervalInMs,
|
|
23292
|
+
now
|
|
23293
|
+
}) => {
|
|
23294
|
+
const peaks = new Float32Array(totalPeaks);
|
|
23295
|
+
let peakIndex = 0;
|
|
23296
|
+
let peakMax = 0;
|
|
23297
|
+
let sampleInPeak = 0;
|
|
23298
|
+
let lastProgressAt = 0;
|
|
23299
|
+
let lastProgressPeak = 0;
|
|
23300
|
+
const emitProgress = (force) => {
|
|
23301
|
+
const timestamp = now();
|
|
23302
|
+
if (!force && peakIndex === lastProgressPeak && sampleInPeak === 0) {
|
|
23303
|
+
return;
|
|
23304
|
+
}
|
|
23305
|
+
if (!force && timestamp - lastProgressAt < progressIntervalInMs) {
|
|
23306
|
+
return;
|
|
23307
|
+
}
|
|
23308
|
+
lastProgressAt = timestamp;
|
|
23309
|
+
lastProgressPeak = peakIndex;
|
|
23310
|
+
emitWaveformProgress({
|
|
23311
|
+
peaks,
|
|
23312
|
+
completedPeaks: peakIndex,
|
|
23313
|
+
totalPeaks,
|
|
23314
|
+
final: force,
|
|
23315
|
+
onProgress
|
|
23316
|
+
});
|
|
23317
|
+
};
|
|
23318
|
+
return {
|
|
23319
|
+
peaks,
|
|
23320
|
+
processSampleChunk: (floats, channels) => {
|
|
23321
|
+
const frameCount = Math.floor(floats.length / Math.max(1, channels));
|
|
23322
|
+
for (let frame2 = 0;frame2 < frameCount; frame2++) {
|
|
23323
|
+
let framePeak = 0;
|
|
23324
|
+
for (let channel = 0;channel < channels; channel++) {
|
|
23325
|
+
const sampleIndex = frame2 * channels + channel;
|
|
23326
|
+
const abs = Math.abs(floats[sampleIndex] ?? 0);
|
|
23327
|
+
if (abs > framePeak) {
|
|
23328
|
+
framePeak = abs;
|
|
23329
|
+
}
|
|
23330
|
+
}
|
|
23331
|
+
if (framePeak > peakMax) {
|
|
23332
|
+
peakMax = framePeak;
|
|
23333
|
+
}
|
|
23334
|
+
sampleInPeak++;
|
|
23335
|
+
if (sampleInPeak >= samplesPerPeak) {
|
|
23336
|
+
if (peakIndex < totalPeaks) {
|
|
23337
|
+
peaks[peakIndex] = peakMax;
|
|
23338
|
+
}
|
|
23339
|
+
peakIndex++;
|
|
23340
|
+
peakMax = 0;
|
|
23341
|
+
sampleInPeak = 0;
|
|
23342
|
+
}
|
|
23343
|
+
}
|
|
23344
|
+
emitProgress(false);
|
|
23345
|
+
},
|
|
23346
|
+
finalize: () => {
|
|
23347
|
+
if (sampleInPeak > 0 && peakIndex < totalPeaks) {
|
|
23348
|
+
peaks[peakIndex] = peakMax;
|
|
23349
|
+
peakIndex++;
|
|
23350
|
+
}
|
|
23351
|
+
emitProgress(true);
|
|
23352
|
+
}
|
|
23353
|
+
};
|
|
23354
|
+
};
|
|
23355
|
+
|
|
23356
|
+
// src/components/load-waveform-peaks.ts
|
|
23249
23357
|
var TARGET_SAMPLE_RATE = 100;
|
|
23358
|
+
var DEFAULT_PROGRESS_INTERVAL_IN_MS = 50;
|
|
23250
23359
|
var peaksCache = new Map;
|
|
23251
|
-
async function loadWaveformPeaks(url, signal) {
|
|
23360
|
+
async function loadWaveformPeaks(url, signal, options) {
|
|
23252
23361
|
const cached = peaksCache.get(url);
|
|
23253
|
-
if (cached)
|
|
23362
|
+
if (cached) {
|
|
23363
|
+
emitWaveformProgress({
|
|
23364
|
+
peaks: cached,
|
|
23365
|
+
completedPeaks: cached.length,
|
|
23366
|
+
totalPeaks: cached.length,
|
|
23367
|
+
final: true,
|
|
23368
|
+
onProgress: options?.onProgress
|
|
23369
|
+
});
|
|
23254
23370
|
return cached;
|
|
23371
|
+
}
|
|
23255
23372
|
const input2 = new Input3({
|
|
23256
23373
|
formats: ALL_FORMATS3,
|
|
23257
23374
|
source: new UrlSource3(url)
|
|
@@ -23265,11 +23382,14 @@ async function loadWaveformPeaks(url, signal) {
|
|
|
23265
23382
|
const durationInSeconds = await audioTrack.computeDuration();
|
|
23266
23383
|
const totalPeaks = Math.ceil(durationInSeconds * TARGET_SAMPLE_RATE);
|
|
23267
23384
|
const samplesPerPeak = Math.max(1, Math.floor(sampleRate / TARGET_SAMPLE_RATE));
|
|
23268
|
-
const peaks = new Float32Array(totalPeaks);
|
|
23269
|
-
let peakIndex = 0;
|
|
23270
|
-
let peakMax = 0;
|
|
23271
|
-
let sampleInPeak = 0;
|
|
23272
23385
|
const sink = new AudioSampleSink(audioTrack);
|
|
23386
|
+
const processor = createWaveformPeakProcessor({
|
|
23387
|
+
totalPeaks,
|
|
23388
|
+
samplesPerPeak,
|
|
23389
|
+
onProgress: options?.onProgress,
|
|
23390
|
+
progressIntervalInMs: options?.progressIntervalInMs ?? DEFAULT_PROGRESS_INTERVAL_IN_MS,
|
|
23391
|
+
now: () => Date.now()
|
|
23392
|
+
});
|
|
23273
23393
|
for await (const sample of sink.samples()) {
|
|
23274
23394
|
if (signal.aborted) {
|
|
23275
23395
|
sample.close();
|
|
@@ -23282,34 +23402,11 @@ async function loadWaveformPeaks(url, signal) {
|
|
|
23282
23402
|
const floats = new Float32Array(bytesNeeded / 4);
|
|
23283
23403
|
sample.copyTo(floats, { format: "f32", planeIndex: 0 });
|
|
23284
23404
|
const channels = Math.max(1, sample.numberOfChannels);
|
|
23285
|
-
const frames = sample.numberOfFrames;
|
|
23286
23405
|
sample.close();
|
|
23287
|
-
|
|
23288
|
-
let framePeak = 0;
|
|
23289
|
-
for (let channel = 0;channel < channels; channel++) {
|
|
23290
|
-
const sampleIndex = frame2 * channels + channel;
|
|
23291
|
-
const abs = Math.abs(floats[sampleIndex] ?? 0);
|
|
23292
|
-
if (abs > framePeak) {
|
|
23293
|
-
framePeak = abs;
|
|
23294
|
-
}
|
|
23295
|
-
}
|
|
23296
|
-
if (framePeak > peakMax) {
|
|
23297
|
-
peakMax = framePeak;
|
|
23298
|
-
}
|
|
23299
|
-
sampleInPeak++;
|
|
23300
|
-
if (sampleInPeak >= samplesPerPeak) {
|
|
23301
|
-
if (peakIndex < totalPeaks) {
|
|
23302
|
-
peaks[peakIndex] = peakMax;
|
|
23303
|
-
}
|
|
23304
|
-
peakIndex++;
|
|
23305
|
-
peakMax = 0;
|
|
23306
|
-
sampleInPeak = 0;
|
|
23307
|
-
}
|
|
23308
|
-
}
|
|
23309
|
-
}
|
|
23310
|
-
if (sampleInPeak > 0 && peakIndex < totalPeaks) {
|
|
23311
|
-
peaks[peakIndex] = peakMax;
|
|
23406
|
+
processor.processSampleChunk(floats, channels);
|
|
23312
23407
|
}
|
|
23408
|
+
processor.finalize();
|
|
23409
|
+
const { peaks } = processor;
|
|
23313
23410
|
peaksCache.set(url, peaks);
|
|
23314
23411
|
return peaks;
|
|
23315
23412
|
} finally {
|
|
@@ -23317,8 +23414,50 @@ async function loadWaveformPeaks(url, signal) {
|
|
|
23317
23414
|
}
|
|
23318
23415
|
}
|
|
23319
23416
|
|
|
23417
|
+
// src/components/looped-media-timeline.ts
|
|
23418
|
+
var shouldTileLoopDisplay = (loopDisplay) => {
|
|
23419
|
+
return loopDisplay !== undefined && loopDisplay.numberOfTimes > 1;
|
|
23420
|
+
};
|
|
23421
|
+
var getLoopDisplayWidth = ({
|
|
23422
|
+
visualizationWidth,
|
|
23423
|
+
loopDisplay
|
|
23424
|
+
}) => {
|
|
23425
|
+
if (!shouldTileLoopDisplay(loopDisplay)) {
|
|
23426
|
+
return visualizationWidth;
|
|
23427
|
+
}
|
|
23428
|
+
return visualizationWidth / loopDisplay.numberOfTimes;
|
|
23429
|
+
};
|
|
23430
|
+
|
|
23431
|
+
// src/components/slice-waveform-peaks.ts
|
|
23432
|
+
var sliceWaveformPeaks = ({
|
|
23433
|
+
durationInFrames,
|
|
23434
|
+
fps,
|
|
23435
|
+
peaks,
|
|
23436
|
+
playbackRate,
|
|
23437
|
+
startFrom
|
|
23438
|
+
}) => {
|
|
23439
|
+
if (peaks.length === 0) {
|
|
23440
|
+
return peaks;
|
|
23441
|
+
}
|
|
23442
|
+
const startTimeInSeconds = startFrom / fps;
|
|
23443
|
+
const durationInSeconds = durationInFrames / fps * playbackRate;
|
|
23444
|
+
const startPeakIndex = Math.floor(startTimeInSeconds * TARGET_SAMPLE_RATE);
|
|
23445
|
+
const endPeakIndex = Math.ceil((startTimeInSeconds + durationInSeconds) * TARGET_SAMPLE_RATE);
|
|
23446
|
+
return peaks.subarray(Math.max(0, startPeakIndex), Math.min(peaks.length, endPeakIndex));
|
|
23447
|
+
};
|
|
23448
|
+
|
|
23320
23449
|
// src/components/AudioWaveform.tsx
|
|
23321
23450
|
import { jsx as jsx209, jsxs as jsxs101 } from "react/jsx-runtime";
|
|
23451
|
+
var EMPTY_PEAKS = new Float32Array(0);
|
|
23452
|
+
var canRetryCanvasTransfer = (err) => {
|
|
23453
|
+
return err instanceof DOMException && err.name === "InvalidStateError";
|
|
23454
|
+
};
|
|
23455
|
+
var canUseAudioWaveformWorker = () => {
|
|
23456
|
+
if (typeof Worker === "undefined" || typeof OffscreenCanvas === "undefined" || typeof HTMLCanvasElement === "undefined") {
|
|
23457
|
+
return false;
|
|
23458
|
+
}
|
|
23459
|
+
return "transferControlToOffscreen" in HTMLCanvasElement.prototype;
|
|
23460
|
+
};
|
|
23322
23461
|
var container42 = {
|
|
23323
23462
|
display: "flex",
|
|
23324
23463
|
flexDirection: "row",
|
|
@@ -23337,11 +23476,41 @@ var errorMessage = {
|
|
|
23337
23476
|
opacity: 0.75
|
|
23338
23477
|
};
|
|
23339
23478
|
var waveformCanvasStyle = {
|
|
23340
|
-
pointerEvents: "none"
|
|
23479
|
+
pointerEvents: "none",
|
|
23480
|
+
width: "100%",
|
|
23481
|
+
height: "100%"
|
|
23341
23482
|
};
|
|
23342
23483
|
var volumeCanvasStyle = {
|
|
23343
23484
|
position: "absolute"
|
|
23344
23485
|
};
|
|
23486
|
+
var drawLoopedWaveform = ({
|
|
23487
|
+
canvas,
|
|
23488
|
+
peaks,
|
|
23489
|
+
volume,
|
|
23490
|
+
visualizationWidth,
|
|
23491
|
+
loopWidth
|
|
23492
|
+
}) => {
|
|
23493
|
+
const h = canvas.height;
|
|
23494
|
+
const w = Math.ceil(visualizationWidth);
|
|
23495
|
+
const targetCanvas = document.createElement("canvas");
|
|
23496
|
+
targetCanvas.width = Math.max(1, Math.ceil(loopWidth));
|
|
23497
|
+
targetCanvas.height = h;
|
|
23498
|
+
drawBars(targetCanvas, peaks, "rgba(255, 255, 255, 0.6)", volume, targetCanvas.width);
|
|
23499
|
+
canvas.width = w;
|
|
23500
|
+
canvas.height = h;
|
|
23501
|
+
const ctx = canvas.getContext("2d");
|
|
23502
|
+
if (!ctx) {
|
|
23503
|
+
throw new Error("Failed to get canvas context");
|
|
23504
|
+
}
|
|
23505
|
+
const pattern = ctx.createPattern(targetCanvas, "repeat-x");
|
|
23506
|
+
if (!pattern) {
|
|
23507
|
+
return;
|
|
23508
|
+
}
|
|
23509
|
+
pattern.setTransform(new DOMMatrix().scaleSelf(loopWidth / targetCanvas.width, 1));
|
|
23510
|
+
ctx.clearRect(0, 0, w, h);
|
|
23511
|
+
ctx.fillStyle = pattern;
|
|
23512
|
+
ctx.fillRect(0, 0, w, h);
|
|
23513
|
+
};
|
|
23345
23514
|
var AudioWaveform = ({
|
|
23346
23515
|
src,
|
|
23347
23516
|
startFrom,
|
|
@@ -23349,10 +23518,13 @@ var AudioWaveform = ({
|
|
|
23349
23518
|
visualizationWidth,
|
|
23350
23519
|
volume,
|
|
23351
23520
|
doesVolumeChange,
|
|
23352
|
-
playbackRate
|
|
23521
|
+
playbackRate,
|
|
23522
|
+
loopDisplay
|
|
23353
23523
|
}) => {
|
|
23354
23524
|
const [peaks, setPeaks] = useState77(null);
|
|
23355
23525
|
const [error, setError] = useState77(null);
|
|
23526
|
+
const [waveformCanvasKey, setWaveformCanvasKey] = useState77(0);
|
|
23527
|
+
const canUseWorkerPath = useMemo119(() => canUseAudioWaveformWorker(), []);
|
|
23356
23528
|
const vidConf = Internals55.useUnsafeVideoConfig();
|
|
23357
23529
|
if (vidConf === null) {
|
|
23358
23530
|
throw new Error("Expected video config");
|
|
@@ -23360,8 +23532,15 @@ var AudioWaveform = ({
|
|
|
23360
23532
|
const containerRef = useRef43(null);
|
|
23361
23533
|
const waveformCanvas = useRef43(null);
|
|
23362
23534
|
const volumeCanvas = useRef43(null);
|
|
23535
|
+
const waveformWorker = useRef43(null);
|
|
23536
|
+
const hasTransferredCanvas = useRef43(false);
|
|
23537
|
+
const latestRequestId = useRef43(0);
|
|
23363
23538
|
useEffect72(() => {
|
|
23539
|
+
if (canUseWorkerPath) {
|
|
23540
|
+
return;
|
|
23541
|
+
}
|
|
23364
23542
|
const controller = new AbortController;
|
|
23543
|
+
setPeaks(null);
|
|
23365
23544
|
setError(null);
|
|
23366
23545
|
loadWaveformPeaks(src, controller.signal).then((p) => {
|
|
23367
23546
|
if (!controller.signal.aborted) {
|
|
@@ -23373,30 +23552,127 @@ var AudioWaveform = ({
|
|
|
23373
23552
|
}
|
|
23374
23553
|
});
|
|
23375
23554
|
return () => controller.abort();
|
|
23376
|
-
}, [src]);
|
|
23555
|
+
}, [canUseWorkerPath, src]);
|
|
23556
|
+
useEffect72(() => {
|
|
23557
|
+
if (!canUseWorkerPath) {
|
|
23558
|
+
return;
|
|
23559
|
+
}
|
|
23560
|
+
const canvasElement = waveformCanvas.current;
|
|
23561
|
+
if (!canvasElement || hasTransferredCanvas.current) {
|
|
23562
|
+
return;
|
|
23563
|
+
}
|
|
23564
|
+
const worker = makeAudioWaveformWorker();
|
|
23565
|
+
waveformWorker.current = worker;
|
|
23566
|
+
worker.addEventListener("message", (event) => {
|
|
23567
|
+
if (event.data.type === "error") {
|
|
23568
|
+
if (event.data.requestId !== latestRequestId.current) {
|
|
23569
|
+
return;
|
|
23570
|
+
}
|
|
23571
|
+
setError(new Error(event.data.message));
|
|
23572
|
+
}
|
|
23573
|
+
});
|
|
23574
|
+
let offscreen;
|
|
23575
|
+
try {
|
|
23576
|
+
offscreen = canvasElement.transferControlToOffscreen();
|
|
23577
|
+
} catch (err) {
|
|
23578
|
+
worker.terminate();
|
|
23579
|
+
waveformWorker.current = null;
|
|
23580
|
+
if (canRetryCanvasTransfer(err)) {
|
|
23581
|
+
setWaveformCanvasKey((key4) => key4 + 1);
|
|
23582
|
+
return;
|
|
23583
|
+
}
|
|
23584
|
+
throw err;
|
|
23585
|
+
}
|
|
23586
|
+
hasTransferredCanvas.current = true;
|
|
23587
|
+
worker.postMessage({ type: "init", canvas: offscreen }, [offscreen]);
|
|
23588
|
+
return () => {
|
|
23589
|
+
worker.postMessage({ type: "dispose" });
|
|
23590
|
+
worker.terminate();
|
|
23591
|
+
waveformWorker.current = null;
|
|
23592
|
+
hasTransferredCanvas.current = false;
|
|
23593
|
+
};
|
|
23594
|
+
}, [canUseWorkerPath, waveformCanvasKey]);
|
|
23377
23595
|
const portionPeaks = useMemo119(() => {
|
|
23378
|
-
if (
|
|
23596
|
+
if (canUseWorkerPath || !peaks) {
|
|
23379
23597
|
return null;
|
|
23380
23598
|
}
|
|
23381
|
-
|
|
23382
|
-
|
|
23383
|
-
|
|
23384
|
-
|
|
23385
|
-
|
|
23386
|
-
|
|
23599
|
+
return sliceWaveformPeaks({
|
|
23600
|
+
durationInFrames: shouldTileLoopDisplay(loopDisplay) ? loopDisplay.durationInFrames : durationInFrames,
|
|
23601
|
+
fps: vidConf.fps,
|
|
23602
|
+
peaks,
|
|
23603
|
+
playbackRate,
|
|
23604
|
+
startFrom
|
|
23605
|
+
});
|
|
23606
|
+
}, [
|
|
23607
|
+
canUseWorkerPath,
|
|
23608
|
+
durationInFrames,
|
|
23609
|
+
loopDisplay,
|
|
23610
|
+
peaks,
|
|
23611
|
+
playbackRate,
|
|
23612
|
+
startFrom,
|
|
23613
|
+
vidConf.fps
|
|
23614
|
+
]);
|
|
23387
23615
|
useEffect72(() => {
|
|
23388
23616
|
const { current: canvasElement } = waveformCanvas;
|
|
23389
23617
|
const { current: containerElement } = containerRef;
|
|
23390
|
-
if (!canvasElement || !containerElement
|
|
23618
|
+
if (!canvasElement || !containerElement) {
|
|
23391
23619
|
return;
|
|
23392
23620
|
}
|
|
23393
23621
|
const h = containerElement.clientHeight;
|
|
23394
23622
|
const w = Math.ceil(visualizationWidth);
|
|
23623
|
+
const vol = typeof volume === "number" ? volume : 1;
|
|
23624
|
+
if (canUseWorkerPath) {
|
|
23625
|
+
const worker = waveformWorker.current;
|
|
23626
|
+
if (!worker || !hasTransferredCanvas.current) {
|
|
23627
|
+
return;
|
|
23628
|
+
}
|
|
23629
|
+
latestRequestId.current += 1;
|
|
23630
|
+
setError(null);
|
|
23631
|
+
const message = {
|
|
23632
|
+
type: "render",
|
|
23633
|
+
requestId: latestRequestId.current,
|
|
23634
|
+
src,
|
|
23635
|
+
width: w,
|
|
23636
|
+
height: h,
|
|
23637
|
+
volume: vol,
|
|
23638
|
+
startFrom,
|
|
23639
|
+
durationInFrames,
|
|
23640
|
+
fps: vidConf.fps,
|
|
23641
|
+
playbackRate,
|
|
23642
|
+
loopDisplay
|
|
23643
|
+
};
|
|
23644
|
+
worker.postMessage(message);
|
|
23645
|
+
return;
|
|
23646
|
+
}
|
|
23395
23647
|
canvasElement.width = w;
|
|
23396
23648
|
canvasElement.height = h;
|
|
23397
|
-
|
|
23398
|
-
|
|
23399
|
-
|
|
23649
|
+
if (shouldTileLoopDisplay(loopDisplay)) {
|
|
23650
|
+
drawLoopedWaveform({
|
|
23651
|
+
canvas: canvasElement,
|
|
23652
|
+
peaks: portionPeaks ?? EMPTY_PEAKS,
|
|
23653
|
+
volume: vol,
|
|
23654
|
+
visualizationWidth,
|
|
23655
|
+
loopWidth: getLoopDisplayWidth({
|
|
23656
|
+
visualizationWidth,
|
|
23657
|
+
loopDisplay
|
|
23658
|
+
})
|
|
23659
|
+
});
|
|
23660
|
+
} else {
|
|
23661
|
+
drawBars(canvasElement, portionPeaks ?? EMPTY_PEAKS, "rgba(255, 255, 255, 0.6)", vol, w);
|
|
23662
|
+
}
|
|
23663
|
+
}, [
|
|
23664
|
+
canUseWorkerPath,
|
|
23665
|
+
durationInFrames,
|
|
23666
|
+
loopDisplay,
|
|
23667
|
+
playbackRate,
|
|
23668
|
+
portionPeaks,
|
|
23669
|
+
src,
|
|
23670
|
+
startFrom,
|
|
23671
|
+
vidConf.fps,
|
|
23672
|
+
visualizationWidth,
|
|
23673
|
+
volume,
|
|
23674
|
+
waveformCanvasKey
|
|
23675
|
+
]);
|
|
23400
23676
|
useEffect72(() => {
|
|
23401
23677
|
const { current: volumeCanvasElement } = volumeCanvas;
|
|
23402
23678
|
const { current: containerElement } = containerRef;
|
|
@@ -23438,7 +23714,7 @@ var AudioWaveform = ({
|
|
|
23438
23714
|
})
|
|
23439
23715
|
});
|
|
23440
23716
|
}
|
|
23441
|
-
if (!peaks) {
|
|
23717
|
+
if (!canUseWorkerPath && !peaks) {
|
|
23442
23718
|
return null;
|
|
23443
23719
|
}
|
|
23444
23720
|
return /* @__PURE__ */ jsxs101("div", {
|
|
@@ -23448,7 +23724,7 @@ var AudioWaveform = ({
|
|
|
23448
23724
|
/* @__PURE__ */ jsx209("canvas", {
|
|
23449
23725
|
ref: waveformCanvas,
|
|
23450
23726
|
style: waveformCanvasStyle
|
|
23451
|
-
}),
|
|
23727
|
+
}, waveformCanvasKey),
|
|
23452
23728
|
/* @__PURE__ */ jsx209("canvas", {
|
|
23453
23729
|
ref: volumeCanvas,
|
|
23454
23730
|
style: volumeCanvasStyle
|
|
@@ -23471,7 +23747,8 @@ var width = {
|
|
|
23471
23747
|
position: "relative"
|
|
23472
23748
|
};
|
|
23473
23749
|
var icon4 = {
|
|
23474
|
-
height: 12
|
|
23750
|
+
height: 12,
|
|
23751
|
+
filter: "drop-shadow(0 0 2px rgba(0, 0, 0, 0.9)) drop-shadow(0 1px 2px rgba(0, 0, 0, 0.8))"
|
|
23475
23752
|
};
|
|
23476
23753
|
var Icon = () => /* @__PURE__ */ jsx210("svg", {
|
|
23477
23754
|
viewBox: "0 0 512 512",
|
|
@@ -23481,44 +23758,23 @@ var Icon = () => /* @__PURE__ */ jsx210("svg", {
|
|
|
23481
23758
|
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"
|
|
23482
23759
|
})
|
|
23483
23760
|
});
|
|
23484
|
-
var
|
|
23485
|
-
|
|
23486
|
-
height: 2,
|
|
23487
|
-
width: 1,
|
|
23488
|
-
background: LIGHT_COLOR
|
|
23489
|
-
};
|
|
23490
|
-
var bottomLine = {
|
|
23491
|
-
top: 0,
|
|
23492
|
-
height: 2,
|
|
23761
|
+
var verticalLine = {
|
|
23762
|
+
height: "100%",
|
|
23493
23763
|
width: 1,
|
|
23494
|
-
background:
|
|
23495
|
-
};
|
|
23496
|
-
var topContainer = {
|
|
23497
|
-
justifyContent: "flex-start",
|
|
23498
|
-
alignItems: "center"
|
|
23764
|
+
background: "rgb(255,255,255, 0.5)"
|
|
23499
23765
|
};
|
|
23500
23766
|
var centerContainer = {
|
|
23501
23767
|
justifyContent: "center",
|
|
23502
23768
|
alignItems: "center"
|
|
23503
23769
|
};
|
|
23504
|
-
var bottomContainer = {
|
|
23505
|
-
justifyContent: "flex-end",
|
|
23506
|
-
alignItems: "center"
|
|
23507
|
-
};
|
|
23508
23770
|
var LoopedIndicator = () => {
|
|
23509
23771
|
return /* @__PURE__ */ jsxs102("div", {
|
|
23510
23772
|
style: width,
|
|
23511
23773
|
children: [
|
|
23512
23774
|
/* @__PURE__ */ jsx210(AbsoluteFill3, {
|
|
23513
|
-
style:
|
|
23514
|
-
children: /* @__PURE__ */ jsx210("div", {
|
|
23515
|
-
style: topLine
|
|
23516
|
-
})
|
|
23517
|
-
}),
|
|
23518
|
-
/* @__PURE__ */ jsx210(AbsoluteFill3, {
|
|
23519
|
-
style: bottomContainer,
|
|
23775
|
+
style: centerContainer,
|
|
23520
23776
|
children: /* @__PURE__ */ jsx210("div", {
|
|
23521
|
-
style:
|
|
23777
|
+
style: verticalLine
|
|
23522
23778
|
})
|
|
23523
23779
|
}),
|
|
23524
23780
|
/* @__PURE__ */ jsx210(AbsoluteFill3, {
|
|
@@ -23990,7 +24246,8 @@ var TimelineVideoInfo = ({
|
|
|
23990
24246
|
volume,
|
|
23991
24247
|
doesVolumeChange,
|
|
23992
24248
|
premountWidth,
|
|
23993
|
-
postmountWidth
|
|
24249
|
+
postmountWidth,
|
|
24250
|
+
loopDisplay
|
|
23994
24251
|
}) => {
|
|
23995
24252
|
const { fps } = useVideoConfig5();
|
|
23996
24253
|
const ref2 = useRef45(null);
|
|
@@ -24013,25 +24270,54 @@ var TimelineVideoInfo = ({
|
|
|
24013
24270
|
return;
|
|
24014
24271
|
}
|
|
24015
24272
|
current.appendChild(canvas);
|
|
24273
|
+
const loopWidth = getLoopDisplayWidth({
|
|
24274
|
+
visualizationWidth: naturalWidth,
|
|
24275
|
+
loopDisplay
|
|
24276
|
+
});
|
|
24277
|
+
const shouldRepeatVideo = shouldTileLoopDisplay(loopDisplay);
|
|
24278
|
+
const targetCanvas = shouldRepeatVideo ? document.createElement("canvas") : canvas;
|
|
24279
|
+
targetCanvas.width = shouldRepeatVideo ? Math.max(1, Math.ceil(loopWidth)) : canvas.width;
|
|
24280
|
+
targetCanvas.height = canvas.height;
|
|
24281
|
+
const targetCtx = shouldRepeatVideo ? targetCanvas.getContext("2d") : ctx;
|
|
24282
|
+
if (!targetCtx) {
|
|
24283
|
+
current.removeChild(canvas);
|
|
24284
|
+
return;
|
|
24285
|
+
}
|
|
24286
|
+
const repeatTarget = () => {
|
|
24287
|
+
if (!shouldRepeatVideo) {
|
|
24288
|
+
return;
|
|
24289
|
+
}
|
|
24290
|
+
const pattern = ctx.createPattern(targetCanvas, "repeat-x");
|
|
24291
|
+
if (!pattern) {
|
|
24292
|
+
return;
|
|
24293
|
+
}
|
|
24294
|
+
pattern.setTransform(new DOMMatrix().scaleSelf(loopWidth / targetCanvas.width, 1));
|
|
24295
|
+
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
|
24296
|
+
ctx.fillStyle = pattern;
|
|
24297
|
+
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
|
24298
|
+
};
|
|
24016
24299
|
const filledSlots = new Map;
|
|
24017
24300
|
const fromSeconds = trimBefore / fps;
|
|
24018
|
-
const
|
|
24301
|
+
const visibleDurationInFrames = shouldRepeatVideo && loopDisplay ? loopDisplay.durationInFrames : durationInFrames;
|
|
24302
|
+
const toSeconds = fromSeconds + visibleDurationInFrames * playbackRate / fps;
|
|
24303
|
+
const targetWidth = shouldRepeatVideo ? targetCanvas.width : naturalWidth;
|
|
24019
24304
|
if (aspectRatio.current !== null) {
|
|
24020
24305
|
ensureSlots({
|
|
24021
24306
|
filledSlots,
|
|
24022
|
-
naturalWidth,
|
|
24307
|
+
naturalWidth: targetWidth,
|
|
24023
24308
|
fromSeconds,
|
|
24024
24309
|
toSeconds,
|
|
24025
24310
|
aspectRatio: aspectRatio.current
|
|
24026
24311
|
});
|
|
24027
24312
|
fillWithCachedFrames({
|
|
24028
|
-
ctx,
|
|
24029
|
-
naturalWidth,
|
|
24313
|
+
ctx: targetCtx,
|
|
24314
|
+
naturalWidth: targetWidth,
|
|
24030
24315
|
filledSlots,
|
|
24031
24316
|
src,
|
|
24032
24317
|
segmentDuration: toSeconds - fromSeconds,
|
|
24033
24318
|
fromSeconds
|
|
24034
24319
|
});
|
|
24320
|
+
repeatTarget();
|
|
24035
24321
|
const unfilled = Array.from(filledSlots.keys()).filter((timestamp) => !filledSlots.get(timestamp));
|
|
24036
24322
|
if (unfilled.length === 0) {
|
|
24037
24323
|
return () => {
|
|
@@ -24049,7 +24335,7 @@ var TimelineVideoInfo = ({
|
|
|
24049
24335
|
filledSlots,
|
|
24050
24336
|
fromSeconds,
|
|
24051
24337
|
toSeconds,
|
|
24052
|
-
naturalWidth,
|
|
24338
|
+
naturalWidth: targetWidth,
|
|
24053
24339
|
aspectRatio: aspectRatio.current
|
|
24054
24340
|
});
|
|
24055
24341
|
return Array.from(filledSlots.keys()).map((timestamp) => timestamp / WEBCODECS_TIMESCALE);
|
|
@@ -24077,17 +24363,18 @@ var TimelineVideoInfo = ({
|
|
|
24077
24363
|
filledSlots,
|
|
24078
24364
|
fromSeconds,
|
|
24079
24365
|
toSeconds,
|
|
24080
|
-
naturalWidth,
|
|
24366
|
+
naturalWidth: targetWidth,
|
|
24081
24367
|
aspectRatio: aspectRatio.current
|
|
24082
24368
|
});
|
|
24083
24369
|
fillFrameWhereItFits({
|
|
24084
|
-
ctx,
|
|
24370
|
+
ctx: targetCtx,
|
|
24085
24371
|
filledSlots,
|
|
24086
|
-
visualizationWidth:
|
|
24372
|
+
visualizationWidth: targetWidth,
|
|
24087
24373
|
frame: transformed,
|
|
24088
24374
|
segmentDuration: toSeconds - fromSeconds,
|
|
24089
24375
|
fromSeconds
|
|
24090
24376
|
});
|
|
24377
|
+
repeatTarget();
|
|
24091
24378
|
} catch (e) {
|
|
24092
24379
|
if (frame2) {
|
|
24093
24380
|
frame2.close();
|
|
@@ -24103,13 +24390,14 @@ var TimelineVideoInfo = ({
|
|
|
24103
24390
|
return;
|
|
24104
24391
|
}
|
|
24105
24392
|
fillWithCachedFrames({
|
|
24106
|
-
ctx,
|
|
24107
|
-
naturalWidth,
|
|
24393
|
+
ctx: targetCtx,
|
|
24394
|
+
naturalWidth: targetWidth,
|
|
24108
24395
|
filledSlots,
|
|
24109
24396
|
src,
|
|
24110
24397
|
segmentDuration: toSeconds - fromSeconds,
|
|
24111
24398
|
fromSeconds
|
|
24112
24399
|
});
|
|
24400
|
+
repeatTarget();
|
|
24113
24401
|
}).catch((e) => {
|
|
24114
24402
|
setError(e);
|
|
24115
24403
|
});
|
|
@@ -24121,6 +24409,7 @@ var TimelineVideoInfo = ({
|
|
|
24121
24409
|
durationInFrames,
|
|
24122
24410
|
error,
|
|
24123
24411
|
fps,
|
|
24412
|
+
loopDisplay,
|
|
24124
24413
|
naturalWidth,
|
|
24125
24414
|
playbackRate,
|
|
24126
24415
|
src,
|
|
@@ -24152,7 +24441,8 @@ var TimelineVideoInfo = ({
|
|
|
24152
24441
|
durationInFrames,
|
|
24153
24442
|
volume,
|
|
24154
24443
|
doesVolumeChange,
|
|
24155
|
-
playbackRate
|
|
24444
|
+
playbackRate,
|
|
24445
|
+
loopDisplay
|
|
24156
24446
|
})
|
|
24157
24447
|
})
|
|
24158
24448
|
]
|
|
@@ -24177,29 +24467,37 @@ var TimelineSequence = ({ s }) => {
|
|
|
24177
24467
|
var Inner4 = ({ s, windowWidth }) => {
|
|
24178
24468
|
const video = Internals56.useVideo();
|
|
24179
24469
|
const maxMediaDuration = useMaxMediaDuration(s, video?.fps ?? 30);
|
|
24470
|
+
const effectiveMaxMediaDuration = s.loopDisplay ? null : maxMediaDuration;
|
|
24180
24471
|
if (!video) {
|
|
24181
24472
|
throw new TypeError("Expected video config");
|
|
24182
24473
|
}
|
|
24183
24474
|
const frame2 = useCurrentFrame2();
|
|
24184
24475
|
const relativeFrame = frame2 - s.from;
|
|
24476
|
+
const displayDurationInFrames = s.loopDisplay ? s.loopDisplay.durationInFrames * s.loopDisplay.numberOfTimes : s.duration;
|
|
24185
24477
|
const relativeFrameWithPremount = relativeFrame + (s.premountDisplay ?? 0);
|
|
24186
|
-
const relativeFrameWithPostmount = relativeFrame -
|
|
24478
|
+
const relativeFrameWithPostmount = relativeFrame - displayDurationInFrames;
|
|
24187
24479
|
const roundedFrame = Math.round(relativeFrame * 100) / 100;
|
|
24188
|
-
const isInRange = relativeFrame >= 0 && relativeFrame <
|
|
24189
|
-
const isPremounting = relativeFrameWithPremount >= 0 && relativeFrameWithPremount <
|
|
24480
|
+
const isInRange = relativeFrame >= 0 && relativeFrame < displayDurationInFrames;
|
|
24481
|
+
const isPremounting = relativeFrameWithPremount >= 0 && relativeFrameWithPremount < displayDurationInFrames && !isInRange;
|
|
24190
24482
|
const isPostmounting = relativeFrameWithPostmount >= 0 && relativeFrameWithPostmount < (s.postmountDisplay ?? 0) && !isInRange;
|
|
24191
24483
|
const { marginLeft, width: width2, naturalWidth, premountWidth, postmountWidth } = useMemo121(() => {
|
|
24192
24484
|
return getTimelineSequenceLayout({
|
|
24193
|
-
durationInFrames:
|
|
24485
|
+
durationInFrames: displayDurationInFrames,
|
|
24194
24486
|
startFrom: s.loopDisplay ? s.from + s.loopDisplay.startOffset : s.from,
|
|
24195
24487
|
startFromMedia: s.type === "sequence" || s.type === "image" ? 0 : s.startMediaFrom,
|
|
24196
|
-
maxMediaDuration,
|
|
24488
|
+
maxMediaDuration: effectiveMaxMediaDuration,
|
|
24197
24489
|
video,
|
|
24198
24490
|
windowWidth,
|
|
24199
24491
|
premountDisplay: s.premountDisplay,
|
|
24200
24492
|
postmountDisplay: s.postmountDisplay
|
|
24201
24493
|
});
|
|
24202
|
-
}, [
|
|
24494
|
+
}, [
|
|
24495
|
+
displayDurationInFrames,
|
|
24496
|
+
effectiveMaxMediaDuration,
|
|
24497
|
+
s,
|
|
24498
|
+
video,
|
|
24499
|
+
windowWidth
|
|
24500
|
+
]);
|
|
24203
24501
|
const style11 = useMemo121(() => {
|
|
24204
24502
|
return {
|
|
24205
24503
|
background: s.type === "audio" ? AUDIO_GRADIENT : s.type === "video" ? VIDEO_GRADIENT : s.type === "image" ? IMAGE_GRADIENT : BLUE,
|
|
@@ -24214,7 +24512,7 @@ var Inner4 = ({ s, windowWidth }) => {
|
|
|
24214
24512
|
opacity: isInRange ? 1 : 0.5
|
|
24215
24513
|
};
|
|
24216
24514
|
}, [isInRange, marginLeft, s.type, width2]);
|
|
24217
|
-
if (maxMediaDuration === null) {
|
|
24515
|
+
if (maxMediaDuration === null && !s.loopDisplay) {
|
|
24218
24516
|
return null;
|
|
24219
24517
|
}
|
|
24220
24518
|
return /* @__PURE__ */ jsxs105("div", {
|
|
@@ -24257,7 +24555,8 @@ var Inner4 = ({ s, windowWidth }) => {
|
|
|
24257
24555
|
startFrom: s.startMediaFrom,
|
|
24258
24556
|
durationInFrames: s.duration,
|
|
24259
24557
|
volume: s.volume,
|
|
24260
|
-
playbackRate: s.playbackRate
|
|
24558
|
+
playbackRate: s.playbackRate,
|
|
24559
|
+
loopDisplay: s.loopDisplay
|
|
24261
24560
|
}) : null,
|
|
24262
24561
|
s.type === "video" ? /* @__PURE__ */ jsx215(TimelineVideoInfo, {
|
|
24263
24562
|
src: s.src,
|
|
@@ -24269,7 +24568,8 @@ var Inner4 = ({ s, windowWidth }) => {
|
|
|
24269
24568
|
volume: s.volume,
|
|
24270
24569
|
doesVolumeChange: s.doesVolumeChange,
|
|
24271
24570
|
premountWidth: premountWidth ?? 0,
|
|
24272
|
-
postmountWidth: postmountWidth ?? 0
|
|
24571
|
+
postmountWidth: postmountWidth ?? 0,
|
|
24572
|
+
loopDisplay: s.loopDisplay
|
|
24273
24573
|
}) : null,
|
|
24274
24574
|
s.type === "image" ? /* @__PURE__ */ jsx215(TimelineImageInfo, {
|
|
24275
24575
|
src: s.src,
|