@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
|
@@ -21050,7 +21050,8 @@ var getTimelineNestedLevel = (sequence, allSequences, depth) => {
|
|
|
21050
21050
|
if (!parentSequence) {
|
|
21051
21051
|
throw new Error("has parentId but no parent");
|
|
21052
21052
|
}
|
|
21053
|
-
|
|
21053
|
+
const parentContributes = parentSequence.showInTimeline;
|
|
21054
|
+
return getTimelineNestedLevel(parentSequence, allSequences, parentContributes ? depth + 1 : depth);
|
|
21054
21055
|
};
|
|
21055
21056
|
|
|
21056
21057
|
// src/helpers/get-timeline-sequence-hash.ts
|
|
@@ -21099,6 +21100,19 @@ var getTimelineSequenceSequenceSortKey = (track, tracks, sameHashes = {}, nonceR
|
|
|
21099
21100
|
};
|
|
21100
21101
|
|
|
21101
21102
|
// src/helpers/calculate-timeline.ts
|
|
21103
|
+
var getInheritedLoopDisplay = (sequence, sequences) => {
|
|
21104
|
+
if (sequence.loopDisplay) {
|
|
21105
|
+
return sequence.loopDisplay;
|
|
21106
|
+
}
|
|
21107
|
+
if (!sequence.parent) {
|
|
21108
|
+
return;
|
|
21109
|
+
}
|
|
21110
|
+
const parent = sequences.find((s) => s.id === sequence.parent);
|
|
21111
|
+
if (!parent) {
|
|
21112
|
+
return;
|
|
21113
|
+
}
|
|
21114
|
+
return getInheritedLoopDisplay(parent, sequences);
|
|
21115
|
+
};
|
|
21102
21116
|
var calculateTimeline = ({
|
|
21103
21117
|
sequences
|
|
21104
21118
|
}) => {
|
|
@@ -21127,7 +21141,8 @@ var calculateTimeline = ({
|
|
|
21127
21141
|
sequence: {
|
|
21128
21142
|
...sequence,
|
|
21129
21143
|
from: visibleStart,
|
|
21130
|
-
duration: visibleDuration
|
|
21144
|
+
duration: visibleDuration,
|
|
21145
|
+
loopDisplay: sequence.type === "audio" || sequence.type === "video" ? getInheritedLoopDisplay(sequence, sortedSequences) : sequence.loopDisplay
|
|
21131
21146
|
},
|
|
21132
21147
|
depth: getTimelineNestedLevel(sequence, sortedSequences, 0),
|
|
21133
21148
|
hash: actualHash,
|
|
@@ -23763,6 +23778,13 @@ var useMaxMediaDuration = (s, fps) => {
|
|
|
23763
23778
|
import { useEffect as useEffect73, useMemo as useMemo119, useRef as useRef43, useState as useState79 } from "react";
|
|
23764
23779
|
import { Internals as Internals55 } from "remotion";
|
|
23765
23780
|
|
|
23781
|
+
// src/make-audio-waveform-worker.ts
|
|
23782
|
+
var makeAudioWaveformWorker = () => {
|
|
23783
|
+
return new Worker(new URL("./audio-waveform-worker.mjs", import.meta.url), {
|
|
23784
|
+
type: "module"
|
|
23785
|
+
});
|
|
23786
|
+
};
|
|
23787
|
+
|
|
23766
23788
|
// src/components/parse-color.ts
|
|
23767
23789
|
var colorCache = new Map;
|
|
23768
23790
|
var parseColor = (color) => {
|
|
@@ -23838,12 +23860,107 @@ var drawBars = (canvas, peaks, color, volume, width) => {
|
|
|
23838
23860
|
|
|
23839
23861
|
// src/components/load-waveform-peaks.ts
|
|
23840
23862
|
import { ALL_FORMATS as ALL_FORMATS3, AudioSampleSink, Input as Input3, UrlSource as UrlSource3 } from "mediabunny";
|
|
23863
|
+
|
|
23864
|
+
// src/components/waveform-peak-processor.ts
|
|
23865
|
+
var emitWaveformProgress = ({
|
|
23866
|
+
completedPeaks,
|
|
23867
|
+
final,
|
|
23868
|
+
onProgress,
|
|
23869
|
+
peaks,
|
|
23870
|
+
totalPeaks
|
|
23871
|
+
}) => {
|
|
23872
|
+
onProgress?.({
|
|
23873
|
+
peaks,
|
|
23874
|
+
completedPeaks,
|
|
23875
|
+
totalPeaks,
|
|
23876
|
+
final
|
|
23877
|
+
});
|
|
23878
|
+
};
|
|
23879
|
+
var createWaveformPeakProcessor = ({
|
|
23880
|
+
totalPeaks,
|
|
23881
|
+
samplesPerPeak,
|
|
23882
|
+
onProgress,
|
|
23883
|
+
progressIntervalInMs,
|
|
23884
|
+
now
|
|
23885
|
+
}) => {
|
|
23886
|
+
const peaks = new Float32Array(totalPeaks);
|
|
23887
|
+
let peakIndex = 0;
|
|
23888
|
+
let peakMax = 0;
|
|
23889
|
+
let sampleInPeak = 0;
|
|
23890
|
+
let lastProgressAt = 0;
|
|
23891
|
+
let lastProgressPeak = 0;
|
|
23892
|
+
const emitProgress = (force) => {
|
|
23893
|
+
const timestamp = now();
|
|
23894
|
+
if (!force && peakIndex === lastProgressPeak && sampleInPeak === 0) {
|
|
23895
|
+
return;
|
|
23896
|
+
}
|
|
23897
|
+
if (!force && timestamp - lastProgressAt < progressIntervalInMs) {
|
|
23898
|
+
return;
|
|
23899
|
+
}
|
|
23900
|
+
lastProgressAt = timestamp;
|
|
23901
|
+
lastProgressPeak = peakIndex;
|
|
23902
|
+
emitWaveformProgress({
|
|
23903
|
+
peaks,
|
|
23904
|
+
completedPeaks: peakIndex,
|
|
23905
|
+
totalPeaks,
|
|
23906
|
+
final: force,
|
|
23907
|
+
onProgress
|
|
23908
|
+
});
|
|
23909
|
+
};
|
|
23910
|
+
return {
|
|
23911
|
+
peaks,
|
|
23912
|
+
processSampleChunk: (floats, channels) => {
|
|
23913
|
+
const frameCount = Math.floor(floats.length / Math.max(1, channels));
|
|
23914
|
+
for (let frame2 = 0;frame2 < frameCount; frame2++) {
|
|
23915
|
+
let framePeak = 0;
|
|
23916
|
+
for (let channel = 0;channel < channels; channel++) {
|
|
23917
|
+
const sampleIndex = frame2 * channels + channel;
|
|
23918
|
+
const abs = Math.abs(floats[sampleIndex] ?? 0);
|
|
23919
|
+
if (abs > framePeak) {
|
|
23920
|
+
framePeak = abs;
|
|
23921
|
+
}
|
|
23922
|
+
}
|
|
23923
|
+
if (framePeak > peakMax) {
|
|
23924
|
+
peakMax = framePeak;
|
|
23925
|
+
}
|
|
23926
|
+
sampleInPeak++;
|
|
23927
|
+
if (sampleInPeak >= samplesPerPeak) {
|
|
23928
|
+
if (peakIndex < totalPeaks) {
|
|
23929
|
+
peaks[peakIndex] = peakMax;
|
|
23930
|
+
}
|
|
23931
|
+
peakIndex++;
|
|
23932
|
+
peakMax = 0;
|
|
23933
|
+
sampleInPeak = 0;
|
|
23934
|
+
}
|
|
23935
|
+
}
|
|
23936
|
+
emitProgress(false);
|
|
23937
|
+
},
|
|
23938
|
+
finalize: () => {
|
|
23939
|
+
if (sampleInPeak > 0 && peakIndex < totalPeaks) {
|
|
23940
|
+
peaks[peakIndex] = peakMax;
|
|
23941
|
+
peakIndex++;
|
|
23942
|
+
}
|
|
23943
|
+
emitProgress(true);
|
|
23944
|
+
}
|
|
23945
|
+
};
|
|
23946
|
+
};
|
|
23947
|
+
|
|
23948
|
+
// src/components/load-waveform-peaks.ts
|
|
23841
23949
|
var TARGET_SAMPLE_RATE = 100;
|
|
23950
|
+
var DEFAULT_PROGRESS_INTERVAL_IN_MS = 50;
|
|
23842
23951
|
var peaksCache = new Map;
|
|
23843
|
-
async function loadWaveformPeaks(url, signal) {
|
|
23952
|
+
async function loadWaveformPeaks(url, signal, options) {
|
|
23844
23953
|
const cached = peaksCache.get(url);
|
|
23845
|
-
if (cached)
|
|
23954
|
+
if (cached) {
|
|
23955
|
+
emitWaveformProgress({
|
|
23956
|
+
peaks: cached,
|
|
23957
|
+
completedPeaks: cached.length,
|
|
23958
|
+
totalPeaks: cached.length,
|
|
23959
|
+
final: true,
|
|
23960
|
+
onProgress: options?.onProgress
|
|
23961
|
+
});
|
|
23846
23962
|
return cached;
|
|
23963
|
+
}
|
|
23847
23964
|
const input2 = new Input3({
|
|
23848
23965
|
formats: ALL_FORMATS3,
|
|
23849
23966
|
source: new UrlSource3(url)
|
|
@@ -23857,11 +23974,14 @@ async function loadWaveformPeaks(url, signal) {
|
|
|
23857
23974
|
const durationInSeconds = await audioTrack.computeDuration();
|
|
23858
23975
|
const totalPeaks = Math.ceil(durationInSeconds * TARGET_SAMPLE_RATE);
|
|
23859
23976
|
const samplesPerPeak = Math.max(1, Math.floor(sampleRate / TARGET_SAMPLE_RATE));
|
|
23860
|
-
const peaks = new Float32Array(totalPeaks);
|
|
23861
|
-
let peakIndex = 0;
|
|
23862
|
-
let peakMax = 0;
|
|
23863
|
-
let sampleInPeak = 0;
|
|
23864
23977
|
const sink = new AudioSampleSink(audioTrack);
|
|
23978
|
+
const processor = createWaveformPeakProcessor({
|
|
23979
|
+
totalPeaks,
|
|
23980
|
+
samplesPerPeak,
|
|
23981
|
+
onProgress: options?.onProgress,
|
|
23982
|
+
progressIntervalInMs: options?.progressIntervalInMs ?? DEFAULT_PROGRESS_INTERVAL_IN_MS,
|
|
23983
|
+
now: () => Date.now()
|
|
23984
|
+
});
|
|
23865
23985
|
for await (const sample of sink.samples()) {
|
|
23866
23986
|
if (signal.aborted) {
|
|
23867
23987
|
sample.close();
|
|
@@ -23874,34 +23994,11 @@ async function loadWaveformPeaks(url, signal) {
|
|
|
23874
23994
|
const floats = new Float32Array(bytesNeeded / 4);
|
|
23875
23995
|
sample.copyTo(floats, { format: "f32", planeIndex: 0 });
|
|
23876
23996
|
const channels = Math.max(1, sample.numberOfChannels);
|
|
23877
|
-
const frames = sample.numberOfFrames;
|
|
23878
23997
|
sample.close();
|
|
23879
|
-
|
|
23880
|
-
let framePeak = 0;
|
|
23881
|
-
for (let channel = 0;channel < channels; channel++) {
|
|
23882
|
-
const sampleIndex = frame2 * channels + channel;
|
|
23883
|
-
const abs = Math.abs(floats[sampleIndex] ?? 0);
|
|
23884
|
-
if (abs > framePeak) {
|
|
23885
|
-
framePeak = abs;
|
|
23886
|
-
}
|
|
23887
|
-
}
|
|
23888
|
-
if (framePeak > peakMax) {
|
|
23889
|
-
peakMax = framePeak;
|
|
23890
|
-
}
|
|
23891
|
-
sampleInPeak++;
|
|
23892
|
-
if (sampleInPeak >= samplesPerPeak) {
|
|
23893
|
-
if (peakIndex < totalPeaks) {
|
|
23894
|
-
peaks[peakIndex] = peakMax;
|
|
23895
|
-
}
|
|
23896
|
-
peakIndex++;
|
|
23897
|
-
peakMax = 0;
|
|
23898
|
-
sampleInPeak = 0;
|
|
23899
|
-
}
|
|
23900
|
-
}
|
|
23901
|
-
}
|
|
23902
|
-
if (sampleInPeak > 0 && peakIndex < totalPeaks) {
|
|
23903
|
-
peaks[peakIndex] = peakMax;
|
|
23998
|
+
processor.processSampleChunk(floats, channels);
|
|
23904
23999
|
}
|
|
24000
|
+
processor.finalize();
|
|
24001
|
+
const { peaks } = processor;
|
|
23905
24002
|
peaksCache.set(url, peaks);
|
|
23906
24003
|
return peaks;
|
|
23907
24004
|
} finally {
|
|
@@ -23909,8 +24006,50 @@ async function loadWaveformPeaks(url, signal) {
|
|
|
23909
24006
|
}
|
|
23910
24007
|
}
|
|
23911
24008
|
|
|
24009
|
+
// src/components/looped-media-timeline.ts
|
|
24010
|
+
var shouldTileLoopDisplay = (loopDisplay) => {
|
|
24011
|
+
return loopDisplay !== undefined && loopDisplay.numberOfTimes > 1;
|
|
24012
|
+
};
|
|
24013
|
+
var getLoopDisplayWidth = ({
|
|
24014
|
+
visualizationWidth,
|
|
24015
|
+
loopDisplay
|
|
24016
|
+
}) => {
|
|
24017
|
+
if (!shouldTileLoopDisplay(loopDisplay)) {
|
|
24018
|
+
return visualizationWidth;
|
|
24019
|
+
}
|
|
24020
|
+
return visualizationWidth / loopDisplay.numberOfTimes;
|
|
24021
|
+
};
|
|
24022
|
+
|
|
24023
|
+
// src/components/slice-waveform-peaks.ts
|
|
24024
|
+
var sliceWaveformPeaks = ({
|
|
24025
|
+
durationInFrames,
|
|
24026
|
+
fps,
|
|
24027
|
+
peaks,
|
|
24028
|
+
playbackRate,
|
|
24029
|
+
startFrom
|
|
24030
|
+
}) => {
|
|
24031
|
+
if (peaks.length === 0) {
|
|
24032
|
+
return peaks;
|
|
24033
|
+
}
|
|
24034
|
+
const startTimeInSeconds = startFrom / fps;
|
|
24035
|
+
const durationInSeconds = durationInFrames / fps * playbackRate;
|
|
24036
|
+
const startPeakIndex = Math.floor(startTimeInSeconds * TARGET_SAMPLE_RATE);
|
|
24037
|
+
const endPeakIndex = Math.ceil((startTimeInSeconds + durationInSeconds) * TARGET_SAMPLE_RATE);
|
|
24038
|
+
return peaks.subarray(Math.max(0, startPeakIndex), Math.min(peaks.length, endPeakIndex));
|
|
24039
|
+
};
|
|
24040
|
+
|
|
23912
24041
|
// src/components/AudioWaveform.tsx
|
|
23913
24042
|
import { jsx as jsx212, jsxs as jsxs102 } from "react/jsx-runtime";
|
|
24043
|
+
var EMPTY_PEAKS = new Float32Array(0);
|
|
24044
|
+
var canRetryCanvasTransfer = (err) => {
|
|
24045
|
+
return err instanceof DOMException && err.name === "InvalidStateError";
|
|
24046
|
+
};
|
|
24047
|
+
var canUseAudioWaveformWorker = () => {
|
|
24048
|
+
if (typeof Worker === "undefined" || typeof OffscreenCanvas === "undefined" || typeof HTMLCanvasElement === "undefined") {
|
|
24049
|
+
return false;
|
|
24050
|
+
}
|
|
24051
|
+
return "transferControlToOffscreen" in HTMLCanvasElement.prototype;
|
|
24052
|
+
};
|
|
23914
24053
|
var container43 = {
|
|
23915
24054
|
display: "flex",
|
|
23916
24055
|
flexDirection: "row",
|
|
@@ -23929,11 +24068,41 @@ var errorMessage = {
|
|
|
23929
24068
|
opacity: 0.75
|
|
23930
24069
|
};
|
|
23931
24070
|
var waveformCanvasStyle = {
|
|
23932
|
-
pointerEvents: "none"
|
|
24071
|
+
pointerEvents: "none",
|
|
24072
|
+
width: "100%",
|
|
24073
|
+
height: "100%"
|
|
23933
24074
|
};
|
|
23934
24075
|
var volumeCanvasStyle = {
|
|
23935
24076
|
position: "absolute"
|
|
23936
24077
|
};
|
|
24078
|
+
var drawLoopedWaveform = ({
|
|
24079
|
+
canvas,
|
|
24080
|
+
peaks,
|
|
24081
|
+
volume,
|
|
24082
|
+
visualizationWidth,
|
|
24083
|
+
loopWidth
|
|
24084
|
+
}) => {
|
|
24085
|
+
const h = canvas.height;
|
|
24086
|
+
const w = Math.ceil(visualizationWidth);
|
|
24087
|
+
const targetCanvas = document.createElement("canvas");
|
|
24088
|
+
targetCanvas.width = Math.max(1, Math.ceil(loopWidth));
|
|
24089
|
+
targetCanvas.height = h;
|
|
24090
|
+
drawBars(targetCanvas, peaks, "rgba(255, 255, 255, 0.6)", volume, targetCanvas.width);
|
|
24091
|
+
canvas.width = w;
|
|
24092
|
+
canvas.height = h;
|
|
24093
|
+
const ctx = canvas.getContext("2d");
|
|
24094
|
+
if (!ctx) {
|
|
24095
|
+
throw new Error("Failed to get canvas context");
|
|
24096
|
+
}
|
|
24097
|
+
const pattern = ctx.createPattern(targetCanvas, "repeat-x");
|
|
24098
|
+
if (!pattern) {
|
|
24099
|
+
return;
|
|
24100
|
+
}
|
|
24101
|
+
pattern.setTransform(new DOMMatrix().scaleSelf(loopWidth / targetCanvas.width, 1));
|
|
24102
|
+
ctx.clearRect(0, 0, w, h);
|
|
24103
|
+
ctx.fillStyle = pattern;
|
|
24104
|
+
ctx.fillRect(0, 0, w, h);
|
|
24105
|
+
};
|
|
23937
24106
|
var AudioWaveform = ({
|
|
23938
24107
|
src,
|
|
23939
24108
|
startFrom,
|
|
@@ -23941,10 +24110,13 @@ var AudioWaveform = ({
|
|
|
23941
24110
|
visualizationWidth,
|
|
23942
24111
|
volume,
|
|
23943
24112
|
doesVolumeChange,
|
|
23944
|
-
playbackRate
|
|
24113
|
+
playbackRate,
|
|
24114
|
+
loopDisplay
|
|
23945
24115
|
}) => {
|
|
23946
24116
|
const [peaks, setPeaks] = useState79(null);
|
|
23947
24117
|
const [error, setError] = useState79(null);
|
|
24118
|
+
const [waveformCanvasKey, setWaveformCanvasKey] = useState79(0);
|
|
24119
|
+
const canUseWorkerPath = useMemo119(() => canUseAudioWaveformWorker(), []);
|
|
23948
24120
|
const vidConf = Internals55.useUnsafeVideoConfig();
|
|
23949
24121
|
if (vidConf === null) {
|
|
23950
24122
|
throw new Error("Expected video config");
|
|
@@ -23952,8 +24124,15 @@ var AudioWaveform = ({
|
|
|
23952
24124
|
const containerRef = useRef43(null);
|
|
23953
24125
|
const waveformCanvas = useRef43(null);
|
|
23954
24126
|
const volumeCanvas = useRef43(null);
|
|
24127
|
+
const waveformWorker = useRef43(null);
|
|
24128
|
+
const hasTransferredCanvas = useRef43(false);
|
|
24129
|
+
const latestRequestId = useRef43(0);
|
|
23955
24130
|
useEffect73(() => {
|
|
24131
|
+
if (canUseWorkerPath) {
|
|
24132
|
+
return;
|
|
24133
|
+
}
|
|
23956
24134
|
const controller = new AbortController;
|
|
24135
|
+
setPeaks(null);
|
|
23957
24136
|
setError(null);
|
|
23958
24137
|
loadWaveformPeaks(src, controller.signal).then((p) => {
|
|
23959
24138
|
if (!controller.signal.aborted) {
|
|
@@ -23965,30 +24144,127 @@ var AudioWaveform = ({
|
|
|
23965
24144
|
}
|
|
23966
24145
|
});
|
|
23967
24146
|
return () => controller.abort();
|
|
23968
|
-
}, [src]);
|
|
24147
|
+
}, [canUseWorkerPath, src]);
|
|
24148
|
+
useEffect73(() => {
|
|
24149
|
+
if (!canUseWorkerPath) {
|
|
24150
|
+
return;
|
|
24151
|
+
}
|
|
24152
|
+
const canvasElement = waveformCanvas.current;
|
|
24153
|
+
if (!canvasElement || hasTransferredCanvas.current) {
|
|
24154
|
+
return;
|
|
24155
|
+
}
|
|
24156
|
+
const worker = makeAudioWaveformWorker();
|
|
24157
|
+
waveformWorker.current = worker;
|
|
24158
|
+
worker.addEventListener("message", (event) => {
|
|
24159
|
+
if (event.data.type === "error") {
|
|
24160
|
+
if (event.data.requestId !== latestRequestId.current) {
|
|
24161
|
+
return;
|
|
24162
|
+
}
|
|
24163
|
+
setError(new Error(event.data.message));
|
|
24164
|
+
}
|
|
24165
|
+
});
|
|
24166
|
+
let offscreen;
|
|
24167
|
+
try {
|
|
24168
|
+
offscreen = canvasElement.transferControlToOffscreen();
|
|
24169
|
+
} catch (err) {
|
|
24170
|
+
worker.terminate();
|
|
24171
|
+
waveformWorker.current = null;
|
|
24172
|
+
if (canRetryCanvasTransfer(err)) {
|
|
24173
|
+
setWaveformCanvasKey((key4) => key4 + 1);
|
|
24174
|
+
return;
|
|
24175
|
+
}
|
|
24176
|
+
throw err;
|
|
24177
|
+
}
|
|
24178
|
+
hasTransferredCanvas.current = true;
|
|
24179
|
+
worker.postMessage({ type: "init", canvas: offscreen }, [offscreen]);
|
|
24180
|
+
return () => {
|
|
24181
|
+
worker.postMessage({ type: "dispose" });
|
|
24182
|
+
worker.terminate();
|
|
24183
|
+
waveformWorker.current = null;
|
|
24184
|
+
hasTransferredCanvas.current = false;
|
|
24185
|
+
};
|
|
24186
|
+
}, [canUseWorkerPath, waveformCanvasKey]);
|
|
23969
24187
|
const portionPeaks = useMemo119(() => {
|
|
23970
|
-
if (
|
|
24188
|
+
if (canUseWorkerPath || !peaks) {
|
|
23971
24189
|
return null;
|
|
23972
24190
|
}
|
|
23973
|
-
|
|
23974
|
-
|
|
23975
|
-
|
|
23976
|
-
|
|
23977
|
-
|
|
23978
|
-
|
|
24191
|
+
return sliceWaveformPeaks({
|
|
24192
|
+
durationInFrames: shouldTileLoopDisplay(loopDisplay) ? loopDisplay.durationInFrames : durationInFrames,
|
|
24193
|
+
fps: vidConf.fps,
|
|
24194
|
+
peaks,
|
|
24195
|
+
playbackRate,
|
|
24196
|
+
startFrom
|
|
24197
|
+
});
|
|
24198
|
+
}, [
|
|
24199
|
+
canUseWorkerPath,
|
|
24200
|
+
durationInFrames,
|
|
24201
|
+
loopDisplay,
|
|
24202
|
+
peaks,
|
|
24203
|
+
playbackRate,
|
|
24204
|
+
startFrom,
|
|
24205
|
+
vidConf.fps
|
|
24206
|
+
]);
|
|
23979
24207
|
useEffect73(() => {
|
|
23980
24208
|
const { current: canvasElement } = waveformCanvas;
|
|
23981
24209
|
const { current: containerElement } = containerRef;
|
|
23982
|
-
if (!canvasElement || !containerElement
|
|
24210
|
+
if (!canvasElement || !containerElement) {
|
|
23983
24211
|
return;
|
|
23984
24212
|
}
|
|
23985
24213
|
const h = containerElement.clientHeight;
|
|
23986
24214
|
const w = Math.ceil(visualizationWidth);
|
|
24215
|
+
const vol = typeof volume === "number" ? volume : 1;
|
|
24216
|
+
if (canUseWorkerPath) {
|
|
24217
|
+
const worker = waveformWorker.current;
|
|
24218
|
+
if (!worker || !hasTransferredCanvas.current) {
|
|
24219
|
+
return;
|
|
24220
|
+
}
|
|
24221
|
+
latestRequestId.current += 1;
|
|
24222
|
+
setError(null);
|
|
24223
|
+
const message = {
|
|
24224
|
+
type: "render",
|
|
24225
|
+
requestId: latestRequestId.current,
|
|
24226
|
+
src,
|
|
24227
|
+
width: w,
|
|
24228
|
+
height: h,
|
|
24229
|
+
volume: vol,
|
|
24230
|
+
startFrom,
|
|
24231
|
+
durationInFrames,
|
|
24232
|
+
fps: vidConf.fps,
|
|
24233
|
+
playbackRate,
|
|
24234
|
+
loopDisplay
|
|
24235
|
+
};
|
|
24236
|
+
worker.postMessage(message);
|
|
24237
|
+
return;
|
|
24238
|
+
}
|
|
23987
24239
|
canvasElement.width = w;
|
|
23988
24240
|
canvasElement.height = h;
|
|
23989
|
-
|
|
23990
|
-
|
|
23991
|
-
|
|
24241
|
+
if (shouldTileLoopDisplay(loopDisplay)) {
|
|
24242
|
+
drawLoopedWaveform({
|
|
24243
|
+
canvas: canvasElement,
|
|
24244
|
+
peaks: portionPeaks ?? EMPTY_PEAKS,
|
|
24245
|
+
volume: vol,
|
|
24246
|
+
visualizationWidth,
|
|
24247
|
+
loopWidth: getLoopDisplayWidth({
|
|
24248
|
+
visualizationWidth,
|
|
24249
|
+
loopDisplay
|
|
24250
|
+
})
|
|
24251
|
+
});
|
|
24252
|
+
} else {
|
|
24253
|
+
drawBars(canvasElement, portionPeaks ?? EMPTY_PEAKS, "rgba(255, 255, 255, 0.6)", vol, w);
|
|
24254
|
+
}
|
|
24255
|
+
}, [
|
|
24256
|
+
canUseWorkerPath,
|
|
24257
|
+
durationInFrames,
|
|
24258
|
+
loopDisplay,
|
|
24259
|
+
playbackRate,
|
|
24260
|
+
portionPeaks,
|
|
24261
|
+
src,
|
|
24262
|
+
startFrom,
|
|
24263
|
+
vidConf.fps,
|
|
24264
|
+
visualizationWidth,
|
|
24265
|
+
volume,
|
|
24266
|
+
waveformCanvasKey
|
|
24267
|
+
]);
|
|
23992
24268
|
useEffect73(() => {
|
|
23993
24269
|
const { current: volumeCanvasElement } = volumeCanvas;
|
|
23994
24270
|
const { current: containerElement } = containerRef;
|
|
@@ -24030,7 +24306,7 @@ var AudioWaveform = ({
|
|
|
24030
24306
|
})
|
|
24031
24307
|
});
|
|
24032
24308
|
}
|
|
24033
|
-
if (!peaks) {
|
|
24309
|
+
if (!canUseWorkerPath && !peaks) {
|
|
24034
24310
|
return null;
|
|
24035
24311
|
}
|
|
24036
24312
|
return /* @__PURE__ */ jsxs102("div", {
|
|
@@ -24040,7 +24316,7 @@ var AudioWaveform = ({
|
|
|
24040
24316
|
/* @__PURE__ */ jsx212("canvas", {
|
|
24041
24317
|
ref: waveformCanvas,
|
|
24042
24318
|
style: waveformCanvasStyle
|
|
24043
|
-
}),
|
|
24319
|
+
}, waveformCanvasKey),
|
|
24044
24320
|
/* @__PURE__ */ jsx212("canvas", {
|
|
24045
24321
|
ref: volumeCanvas,
|
|
24046
24322
|
style: volumeCanvasStyle
|
|
@@ -24063,7 +24339,8 @@ var width = {
|
|
|
24063
24339
|
position: "relative"
|
|
24064
24340
|
};
|
|
24065
24341
|
var icon4 = {
|
|
24066
|
-
height: 12
|
|
24342
|
+
height: 12,
|
|
24343
|
+
filter: "drop-shadow(0 0 2px rgba(0, 0, 0, 0.9)) drop-shadow(0 1px 2px rgba(0, 0, 0, 0.8))"
|
|
24067
24344
|
};
|
|
24068
24345
|
var Icon = () => /* @__PURE__ */ jsx213("svg", {
|
|
24069
24346
|
viewBox: "0 0 512 512",
|
|
@@ -24073,44 +24350,23 @@ var Icon = () => /* @__PURE__ */ jsx213("svg", {
|
|
|
24073
24350
|
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"
|
|
24074
24351
|
})
|
|
24075
24352
|
});
|
|
24076
|
-
var
|
|
24077
|
-
|
|
24078
|
-
height: 2,
|
|
24079
|
-
width: 1,
|
|
24080
|
-
background: LIGHT_COLOR
|
|
24081
|
-
};
|
|
24082
|
-
var bottomLine = {
|
|
24083
|
-
top: 0,
|
|
24084
|
-
height: 2,
|
|
24353
|
+
var verticalLine = {
|
|
24354
|
+
height: "100%",
|
|
24085
24355
|
width: 1,
|
|
24086
|
-
background:
|
|
24087
|
-
};
|
|
24088
|
-
var topContainer = {
|
|
24089
|
-
justifyContent: "flex-start",
|
|
24090
|
-
alignItems: "center"
|
|
24356
|
+
background: "rgb(255,255,255, 0.5)"
|
|
24091
24357
|
};
|
|
24092
24358
|
var centerContainer = {
|
|
24093
24359
|
justifyContent: "center",
|
|
24094
24360
|
alignItems: "center"
|
|
24095
24361
|
};
|
|
24096
|
-
var bottomContainer = {
|
|
24097
|
-
justifyContent: "flex-end",
|
|
24098
|
-
alignItems: "center"
|
|
24099
|
-
};
|
|
24100
24362
|
var LoopedIndicator = () => {
|
|
24101
24363
|
return /* @__PURE__ */ jsxs103("div", {
|
|
24102
24364
|
style: width,
|
|
24103
24365
|
children: [
|
|
24104
24366
|
/* @__PURE__ */ jsx213(AbsoluteFill5, {
|
|
24105
|
-
style:
|
|
24106
|
-
children: /* @__PURE__ */ jsx213("div", {
|
|
24107
|
-
style: topLine
|
|
24108
|
-
})
|
|
24109
|
-
}),
|
|
24110
|
-
/* @__PURE__ */ jsx213(AbsoluteFill5, {
|
|
24111
|
-
style: bottomContainer,
|
|
24367
|
+
style: centerContainer,
|
|
24112
24368
|
children: /* @__PURE__ */ jsx213("div", {
|
|
24113
|
-
style:
|
|
24369
|
+
style: verticalLine
|
|
24114
24370
|
})
|
|
24115
24371
|
}),
|
|
24116
24372
|
/* @__PURE__ */ jsx213(AbsoluteFill5, {
|
|
@@ -24582,7 +24838,8 @@ var TimelineVideoInfo = ({
|
|
|
24582
24838
|
volume,
|
|
24583
24839
|
doesVolumeChange,
|
|
24584
24840
|
premountWidth,
|
|
24585
|
-
postmountWidth
|
|
24841
|
+
postmountWidth,
|
|
24842
|
+
loopDisplay
|
|
24586
24843
|
}) => {
|
|
24587
24844
|
const { fps } = useVideoConfig5();
|
|
24588
24845
|
const ref2 = useRef45(null);
|
|
@@ -24605,25 +24862,54 @@ var TimelineVideoInfo = ({
|
|
|
24605
24862
|
return;
|
|
24606
24863
|
}
|
|
24607
24864
|
current.appendChild(canvas);
|
|
24865
|
+
const loopWidth = getLoopDisplayWidth({
|
|
24866
|
+
visualizationWidth: naturalWidth,
|
|
24867
|
+
loopDisplay
|
|
24868
|
+
});
|
|
24869
|
+
const shouldRepeatVideo = shouldTileLoopDisplay(loopDisplay);
|
|
24870
|
+
const targetCanvas = shouldRepeatVideo ? document.createElement("canvas") : canvas;
|
|
24871
|
+
targetCanvas.width = shouldRepeatVideo ? Math.max(1, Math.ceil(loopWidth)) : canvas.width;
|
|
24872
|
+
targetCanvas.height = canvas.height;
|
|
24873
|
+
const targetCtx = shouldRepeatVideo ? targetCanvas.getContext("2d") : ctx;
|
|
24874
|
+
if (!targetCtx) {
|
|
24875
|
+
current.removeChild(canvas);
|
|
24876
|
+
return;
|
|
24877
|
+
}
|
|
24878
|
+
const repeatTarget = () => {
|
|
24879
|
+
if (!shouldRepeatVideo) {
|
|
24880
|
+
return;
|
|
24881
|
+
}
|
|
24882
|
+
const pattern = ctx.createPattern(targetCanvas, "repeat-x");
|
|
24883
|
+
if (!pattern) {
|
|
24884
|
+
return;
|
|
24885
|
+
}
|
|
24886
|
+
pattern.setTransform(new DOMMatrix().scaleSelf(loopWidth / targetCanvas.width, 1));
|
|
24887
|
+
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
|
24888
|
+
ctx.fillStyle = pattern;
|
|
24889
|
+
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
|
24890
|
+
};
|
|
24608
24891
|
const filledSlots = new Map;
|
|
24609
24892
|
const fromSeconds = trimBefore / fps;
|
|
24610
|
-
const
|
|
24893
|
+
const visibleDurationInFrames = shouldRepeatVideo && loopDisplay ? loopDisplay.durationInFrames : durationInFrames;
|
|
24894
|
+
const toSeconds = fromSeconds + visibleDurationInFrames * playbackRate / fps;
|
|
24895
|
+
const targetWidth = shouldRepeatVideo ? targetCanvas.width : naturalWidth;
|
|
24611
24896
|
if (aspectRatio.current !== null) {
|
|
24612
24897
|
ensureSlots({
|
|
24613
24898
|
filledSlots,
|
|
24614
|
-
naturalWidth,
|
|
24899
|
+
naturalWidth: targetWidth,
|
|
24615
24900
|
fromSeconds,
|
|
24616
24901
|
toSeconds,
|
|
24617
24902
|
aspectRatio: aspectRatio.current
|
|
24618
24903
|
});
|
|
24619
24904
|
fillWithCachedFrames({
|
|
24620
|
-
ctx,
|
|
24621
|
-
naturalWidth,
|
|
24905
|
+
ctx: targetCtx,
|
|
24906
|
+
naturalWidth: targetWidth,
|
|
24622
24907
|
filledSlots,
|
|
24623
24908
|
src,
|
|
24624
24909
|
segmentDuration: toSeconds - fromSeconds,
|
|
24625
24910
|
fromSeconds
|
|
24626
24911
|
});
|
|
24912
|
+
repeatTarget();
|
|
24627
24913
|
const unfilled = Array.from(filledSlots.keys()).filter((timestamp) => !filledSlots.get(timestamp));
|
|
24628
24914
|
if (unfilled.length === 0) {
|
|
24629
24915
|
return () => {
|
|
@@ -24641,7 +24927,7 @@ var TimelineVideoInfo = ({
|
|
|
24641
24927
|
filledSlots,
|
|
24642
24928
|
fromSeconds,
|
|
24643
24929
|
toSeconds,
|
|
24644
|
-
naturalWidth,
|
|
24930
|
+
naturalWidth: targetWidth,
|
|
24645
24931
|
aspectRatio: aspectRatio.current
|
|
24646
24932
|
});
|
|
24647
24933
|
return Array.from(filledSlots.keys()).map((timestamp) => timestamp / WEBCODECS_TIMESCALE);
|
|
@@ -24669,17 +24955,18 @@ var TimelineVideoInfo = ({
|
|
|
24669
24955
|
filledSlots,
|
|
24670
24956
|
fromSeconds,
|
|
24671
24957
|
toSeconds,
|
|
24672
|
-
naturalWidth,
|
|
24958
|
+
naturalWidth: targetWidth,
|
|
24673
24959
|
aspectRatio: aspectRatio.current
|
|
24674
24960
|
});
|
|
24675
24961
|
fillFrameWhereItFits({
|
|
24676
|
-
ctx,
|
|
24962
|
+
ctx: targetCtx,
|
|
24677
24963
|
filledSlots,
|
|
24678
|
-
visualizationWidth:
|
|
24964
|
+
visualizationWidth: targetWidth,
|
|
24679
24965
|
frame: transformed,
|
|
24680
24966
|
segmentDuration: toSeconds - fromSeconds,
|
|
24681
24967
|
fromSeconds
|
|
24682
24968
|
});
|
|
24969
|
+
repeatTarget();
|
|
24683
24970
|
} catch (e) {
|
|
24684
24971
|
if (frame2) {
|
|
24685
24972
|
frame2.close();
|
|
@@ -24695,13 +24982,14 @@ var TimelineVideoInfo = ({
|
|
|
24695
24982
|
return;
|
|
24696
24983
|
}
|
|
24697
24984
|
fillWithCachedFrames({
|
|
24698
|
-
ctx,
|
|
24699
|
-
naturalWidth,
|
|
24985
|
+
ctx: targetCtx,
|
|
24986
|
+
naturalWidth: targetWidth,
|
|
24700
24987
|
filledSlots,
|
|
24701
24988
|
src,
|
|
24702
24989
|
segmentDuration: toSeconds - fromSeconds,
|
|
24703
24990
|
fromSeconds
|
|
24704
24991
|
});
|
|
24992
|
+
repeatTarget();
|
|
24705
24993
|
}).catch((e) => {
|
|
24706
24994
|
setError(e);
|
|
24707
24995
|
});
|
|
@@ -24713,6 +25001,7 @@ var TimelineVideoInfo = ({
|
|
|
24713
25001
|
durationInFrames,
|
|
24714
25002
|
error,
|
|
24715
25003
|
fps,
|
|
25004
|
+
loopDisplay,
|
|
24716
25005
|
naturalWidth,
|
|
24717
25006
|
playbackRate,
|
|
24718
25007
|
src,
|
|
@@ -24744,7 +25033,8 @@ var TimelineVideoInfo = ({
|
|
|
24744
25033
|
durationInFrames,
|
|
24745
25034
|
volume,
|
|
24746
25035
|
doesVolumeChange,
|
|
24747
|
-
playbackRate
|
|
25036
|
+
playbackRate,
|
|
25037
|
+
loopDisplay
|
|
24748
25038
|
})
|
|
24749
25039
|
})
|
|
24750
25040
|
]
|
|
@@ -24769,29 +25059,37 @@ var TimelineSequence = ({ s }) => {
|
|
|
24769
25059
|
var Inner4 = ({ s, windowWidth }) => {
|
|
24770
25060
|
const video = Internals56.useVideo();
|
|
24771
25061
|
const maxMediaDuration = useMaxMediaDuration(s, video?.fps ?? 30);
|
|
25062
|
+
const effectiveMaxMediaDuration = s.loopDisplay ? null : maxMediaDuration;
|
|
24772
25063
|
if (!video) {
|
|
24773
25064
|
throw new TypeError("Expected video config");
|
|
24774
25065
|
}
|
|
24775
25066
|
const frame2 = useCurrentFrame2();
|
|
24776
25067
|
const relativeFrame = frame2 - s.from;
|
|
25068
|
+
const displayDurationInFrames = s.loopDisplay ? s.loopDisplay.durationInFrames * s.loopDisplay.numberOfTimes : s.duration;
|
|
24777
25069
|
const relativeFrameWithPremount = relativeFrame + (s.premountDisplay ?? 0);
|
|
24778
|
-
const relativeFrameWithPostmount = relativeFrame -
|
|
25070
|
+
const relativeFrameWithPostmount = relativeFrame - displayDurationInFrames;
|
|
24779
25071
|
const roundedFrame = Math.round(relativeFrame * 100) / 100;
|
|
24780
|
-
const isInRange = relativeFrame >= 0 && relativeFrame <
|
|
24781
|
-
const isPremounting = relativeFrameWithPremount >= 0 && relativeFrameWithPremount <
|
|
25072
|
+
const isInRange = relativeFrame >= 0 && relativeFrame < displayDurationInFrames;
|
|
25073
|
+
const isPremounting = relativeFrameWithPremount >= 0 && relativeFrameWithPremount < displayDurationInFrames && !isInRange;
|
|
24782
25074
|
const isPostmounting = relativeFrameWithPostmount >= 0 && relativeFrameWithPostmount < (s.postmountDisplay ?? 0) && !isInRange;
|
|
24783
25075
|
const { marginLeft, width: width2, naturalWidth, premountWidth, postmountWidth } = useMemo121(() => {
|
|
24784
25076
|
return getTimelineSequenceLayout({
|
|
24785
|
-
durationInFrames:
|
|
25077
|
+
durationInFrames: displayDurationInFrames,
|
|
24786
25078
|
startFrom: s.loopDisplay ? s.from + s.loopDisplay.startOffset : s.from,
|
|
24787
25079
|
startFromMedia: s.type === "sequence" || s.type === "image" ? 0 : s.startMediaFrom,
|
|
24788
|
-
maxMediaDuration,
|
|
25080
|
+
maxMediaDuration: effectiveMaxMediaDuration,
|
|
24789
25081
|
video,
|
|
24790
25082
|
windowWidth,
|
|
24791
25083
|
premountDisplay: s.premountDisplay,
|
|
24792
25084
|
postmountDisplay: s.postmountDisplay
|
|
24793
25085
|
});
|
|
24794
|
-
}, [
|
|
25086
|
+
}, [
|
|
25087
|
+
displayDurationInFrames,
|
|
25088
|
+
effectiveMaxMediaDuration,
|
|
25089
|
+
s,
|
|
25090
|
+
video,
|
|
25091
|
+
windowWidth
|
|
25092
|
+
]);
|
|
24795
25093
|
const style11 = useMemo121(() => {
|
|
24796
25094
|
return {
|
|
24797
25095
|
background: s.type === "audio" ? AUDIO_GRADIENT : s.type === "video" ? VIDEO_GRADIENT : s.type === "image" ? IMAGE_GRADIENT : BLUE,
|
|
@@ -24806,7 +25104,7 @@ var Inner4 = ({ s, windowWidth }) => {
|
|
|
24806
25104
|
opacity: isInRange ? 1 : 0.5
|
|
24807
25105
|
};
|
|
24808
25106
|
}, [isInRange, marginLeft, s.type, width2]);
|
|
24809
|
-
if (maxMediaDuration === null) {
|
|
25107
|
+
if (maxMediaDuration === null && !s.loopDisplay) {
|
|
24810
25108
|
return null;
|
|
24811
25109
|
}
|
|
24812
25110
|
return /* @__PURE__ */ jsxs106("div", {
|
|
@@ -24849,7 +25147,8 @@ var Inner4 = ({ s, windowWidth }) => {
|
|
|
24849
25147
|
startFrom: s.startMediaFrom,
|
|
24850
25148
|
durationInFrames: s.duration,
|
|
24851
25149
|
volume: s.volume,
|
|
24852
|
-
playbackRate: s.playbackRate
|
|
25150
|
+
playbackRate: s.playbackRate,
|
|
25151
|
+
loopDisplay: s.loopDisplay
|
|
24853
25152
|
}) : null,
|
|
24854
25153
|
s.type === "video" ? /* @__PURE__ */ jsx218(TimelineVideoInfo, {
|
|
24855
25154
|
src: s.src,
|
|
@@ -24861,7 +25160,8 @@ var Inner4 = ({ s, windowWidth }) => {
|
|
|
24861
25160
|
volume: s.volume,
|
|
24862
25161
|
doesVolumeChange: s.doesVolumeChange,
|
|
24863
25162
|
premountWidth: premountWidth ?? 0,
|
|
24864
|
-
postmountWidth: postmountWidth ?? 0
|
|
25163
|
+
postmountWidth: postmountWidth ?? 0,
|
|
25164
|
+
loopDisplay: s.loopDisplay
|
|
24865
25165
|
}) : null,
|
|
24866
25166
|
s.type === "image" ? /* @__PURE__ */ jsx218(TimelineImageInfo, {
|
|
24867
25167
|
src: s.src,
|