@remotion/timeline-utils 4.0.514 → 4.0.515
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/audio-waveform-worker-types.d.ts +9 -22
- package/dist/audio-waveform/subscribe-to-waveform-peaks.d.ts +5 -0
- package/dist/audio-waveform/subscribe-to-waveform-peaks.js +148 -0
- package/dist/audio-waveform-worker.js +24 -72
- package/dist/esm/audio-waveform-worker.mjs +19 -288
- package/dist/esm/index.mjs +167 -21
- package/dist/extract-frames.js +24 -14
- package/dist/index.d.ts +2 -2
- package/dist/index.js +3 -3
- package/package.json +4 -3
|
@@ -1,31 +1,18 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
export type AudioWaveformWorkerInitMessage = {
|
|
4
|
-
readonly type: 'init';
|
|
5
|
-
readonly canvas: OffscreenCanvas;
|
|
6
|
-
};
|
|
7
|
-
export type AudioWaveformWorkerRenderMessage = {
|
|
8
|
-
readonly type: 'render';
|
|
1
|
+
export type AudioWaveformWorkerLoadMessage = {
|
|
2
|
+
readonly type: 'load';
|
|
9
3
|
readonly requestId: number;
|
|
10
4
|
readonly src: string;
|
|
11
|
-
readonly width: number;
|
|
12
|
-
readonly height: number;
|
|
13
|
-
readonly volume: WaveformVolume;
|
|
14
|
-
readonly startFrom: number;
|
|
15
|
-
readonly durationInFrames: number;
|
|
16
|
-
readonly displayOffsetInFrames: number;
|
|
17
|
-
readonly displayDurationInFrames: number;
|
|
18
|
-
readonly fps: number;
|
|
19
|
-
readonly playbackRate: number;
|
|
20
|
-
readonly loopDisplay: TimelineLoopDisplay | undefined;
|
|
21
5
|
};
|
|
22
|
-
export type
|
|
23
|
-
|
|
6
|
+
export type AudioWaveformWorkerIncomingMessage = AudioWaveformWorkerLoadMessage;
|
|
7
|
+
export type AudioWaveformWorkerPeaksMessage = {
|
|
8
|
+
readonly type: 'peaks';
|
|
9
|
+
readonly requestId: number;
|
|
10
|
+
readonly peaks: Float32Array;
|
|
11
|
+
readonly final: boolean;
|
|
24
12
|
};
|
|
25
|
-
export type AudioWaveformWorkerIncomingMessage = AudioWaveformWorkerInitMessage | AudioWaveformWorkerRenderMessage | AudioWaveformWorkerDisposeMessage;
|
|
26
13
|
export type AudioWaveformWorkerErrorMessage = {
|
|
27
14
|
readonly type: 'error';
|
|
28
15
|
readonly requestId: number;
|
|
29
16
|
readonly message: string;
|
|
30
17
|
};
|
|
31
|
-
export type AudioWaveformWorkerOutgoingMessage = AudioWaveformWorkerErrorMessage;
|
|
18
|
+
export type AudioWaveformWorkerOutgoingMessage = AudioWaveformWorkerPeaksMessage | AudioWaveformWorkerErrorMessage;
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.subscribeToWaveformPeaks = void 0;
|
|
4
|
+
const load_waveform_peaks_1 = require("./load-waveform-peaks");
|
|
5
|
+
const make_audio_waveform_worker_1 = require("./make-audio-waveform-worker");
|
|
6
|
+
// All AudioWaveform instances share one decode worker and one peaks cache,
|
|
7
|
+
// so remounts (timeline virtualization, zoom window cropping) and multiple
|
|
8
|
+
// clips with the same src never re-fetch or re-decode the audio.
|
|
9
|
+
const peaksCache = new Map();
|
|
10
|
+
const inFlightBySrc = new Map();
|
|
11
|
+
const inFlightByRequestId = new Map();
|
|
12
|
+
let worker = null;
|
|
13
|
+
let workerFailed = false;
|
|
14
|
+
let nextRequestId = 0;
|
|
15
|
+
const emitPeaks = (load, peaks, final) => {
|
|
16
|
+
if (final) {
|
|
17
|
+
peaksCache.set(load.src, peaks);
|
|
18
|
+
inFlightBySrc.delete(load.src);
|
|
19
|
+
if (load.requestId !== null) {
|
|
20
|
+
inFlightByRequestId.delete(load.requestId);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
else {
|
|
24
|
+
load.latestPeaks = peaks;
|
|
25
|
+
}
|
|
26
|
+
for (const listener of load.listeners) {
|
|
27
|
+
listener.onPeaks(peaks, final);
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
const emitError = (load, error) => {
|
|
31
|
+
inFlightBySrc.delete(load.src);
|
|
32
|
+
if (load.requestId !== null) {
|
|
33
|
+
inFlightByRequestId.delete(load.requestId);
|
|
34
|
+
}
|
|
35
|
+
for (const listener of load.listeners) {
|
|
36
|
+
listener.onError(error);
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
const startMainThreadLoad = (load) => {
|
|
40
|
+
load.requestId = null;
|
|
41
|
+
// Never aborted: even if all subscribers unsubscribe, finishing the
|
|
42
|
+
// decode fills the cache for the next mount.
|
|
43
|
+
const controller = new AbortController();
|
|
44
|
+
(0, load_waveform_peaks_1.loadWaveformPeaks)(load.src, controller.signal, {
|
|
45
|
+
onProgress: ({ peaks, final }) => {
|
|
46
|
+
if (final) {
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
// The processor mutates the same array in place; snapshot it so
|
|
50
|
+
// React state updates see a new reference.
|
|
51
|
+
emitPeaks(load, peaks.slice(), false);
|
|
52
|
+
},
|
|
53
|
+
})
|
|
54
|
+
.then((peaks) => {
|
|
55
|
+
emitPeaks(load, peaks, true);
|
|
56
|
+
})
|
|
57
|
+
.catch((error) => {
|
|
58
|
+
emitError(load, error instanceof Error
|
|
59
|
+
? error
|
|
60
|
+
: new Error('Failed to load waveform peaks'));
|
|
61
|
+
});
|
|
62
|
+
};
|
|
63
|
+
const handleWorkerFailure = () => {
|
|
64
|
+
workerFailed = true;
|
|
65
|
+
worker === null || worker === void 0 ? void 0 : worker.terminate();
|
|
66
|
+
worker = null;
|
|
67
|
+
const pending = [...inFlightByRequestId.values()];
|
|
68
|
+
inFlightByRequestId.clear();
|
|
69
|
+
for (const load of pending) {
|
|
70
|
+
startMainThreadLoad(load);
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
const getOrCreateWorker = () => {
|
|
74
|
+
if (workerFailed || typeof Worker === 'undefined') {
|
|
75
|
+
return null;
|
|
76
|
+
}
|
|
77
|
+
if (worker) {
|
|
78
|
+
return worker;
|
|
79
|
+
}
|
|
80
|
+
try {
|
|
81
|
+
worker = (0, make_audio_waveform_worker_1.makeAudioWaveformWorker)();
|
|
82
|
+
}
|
|
83
|
+
catch (_a) {
|
|
84
|
+
workerFailed = true;
|
|
85
|
+
return null;
|
|
86
|
+
}
|
|
87
|
+
worker.addEventListener('message', (event) => {
|
|
88
|
+
const message = event.data;
|
|
89
|
+
const load = inFlightByRequestId.get(message.requestId);
|
|
90
|
+
if (!load) {
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
if (message.type === 'error') {
|
|
94
|
+
emitError(load, new Error(message.message));
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
emitPeaks(load, message.peaks, message.final);
|
|
98
|
+
});
|
|
99
|
+
worker.addEventListener('error', (event) => {
|
|
100
|
+
event.preventDefault();
|
|
101
|
+
handleWorkerFailure();
|
|
102
|
+
});
|
|
103
|
+
return worker;
|
|
104
|
+
};
|
|
105
|
+
const subscribeToWaveformPeaks = ({ src, onPeaks, onError, }) => {
|
|
106
|
+
const cached = peaksCache.get(src);
|
|
107
|
+
if (cached) {
|
|
108
|
+
onPeaks(cached, true);
|
|
109
|
+
return () => undefined;
|
|
110
|
+
}
|
|
111
|
+
const listener = { onPeaks, onError };
|
|
112
|
+
const existing = inFlightBySrc.get(src);
|
|
113
|
+
if (existing) {
|
|
114
|
+
existing.listeners.add(listener);
|
|
115
|
+
if (existing.latestPeaks) {
|
|
116
|
+
onPeaks(existing.latestPeaks, false);
|
|
117
|
+
}
|
|
118
|
+
return () => {
|
|
119
|
+
existing.listeners.delete(listener);
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
const load = {
|
|
123
|
+
src,
|
|
124
|
+
listeners: new Set([listener]),
|
|
125
|
+
latestPeaks: null,
|
|
126
|
+
requestId: null,
|
|
127
|
+
};
|
|
128
|
+
inFlightBySrc.set(src, load);
|
|
129
|
+
const workerInstance = getOrCreateWorker();
|
|
130
|
+
if (workerInstance) {
|
|
131
|
+
const requestId = nextRequestId++;
|
|
132
|
+
load.requestId = requestId;
|
|
133
|
+
inFlightByRequestId.set(requestId, load);
|
|
134
|
+
const message = {
|
|
135
|
+
type: 'load',
|
|
136
|
+
requestId,
|
|
137
|
+
src,
|
|
138
|
+
};
|
|
139
|
+
workerInstance.postMessage(message);
|
|
140
|
+
}
|
|
141
|
+
else {
|
|
142
|
+
startMainThreadLoad(load);
|
|
143
|
+
}
|
|
144
|
+
return () => {
|
|
145
|
+
load.listeners.delete(listener);
|
|
146
|
+
};
|
|
147
|
+
};
|
|
148
|
+
exports.subscribeToWaveformPeaks = subscribeToWaveformPeaks;
|
|
@@ -1,14 +1,20 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
/// <reference lib="webworker" />
|
|
3
3
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
-
const draw_peaks_1 = require("./audio-waveform/draw-peaks");
|
|
5
4
|
const load_waveform_peaks_1 = require("./audio-waveform/load-waveform-peaks");
|
|
6
|
-
const
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
5
|
+
const postPeaks = (requestId, peaks, final) => {
|
|
6
|
+
// Structured cloning copies the array, so the decoder can keep
|
|
7
|
+
// mutating its buffer while the main thread reads the snapshot.
|
|
8
|
+
const payload = {
|
|
9
|
+
type: 'peaks',
|
|
10
|
+
requestId,
|
|
11
|
+
peaks,
|
|
12
|
+
final,
|
|
13
|
+
};
|
|
14
|
+
self.postMessage(payload);
|
|
15
|
+
};
|
|
10
16
|
const postError = (requestId, error) => {
|
|
11
|
-
const message = error instanceof Error ? error.message : 'Failed to
|
|
17
|
+
const message = error instanceof Error ? error.message : 'Failed to load waveform';
|
|
12
18
|
const payload = {
|
|
13
19
|
type: 'error',
|
|
14
20
|
requestId,
|
|
@@ -16,74 +22,20 @@ const postError = (requestId, error) => {
|
|
|
16
22
|
};
|
|
17
23
|
self.postMessage(payload);
|
|
18
24
|
};
|
|
19
|
-
const drawPartialWaveform = (message, peaks) => {
|
|
20
|
-
if (!canvas) {
|
|
21
|
-
return;
|
|
22
|
-
}
|
|
23
|
-
const portionPeaks = (0, slice_visible_waveform_peaks_1.sliceVisibleWaveformPeaks)({
|
|
24
|
-
displayDurationInFrames: message.displayDurationInFrames,
|
|
25
|
-
displayOffsetInFrames: message.displayOffsetInFrames,
|
|
26
|
-
durationInFrames: message.durationInFrames,
|
|
27
|
-
fps: message.fps,
|
|
28
|
-
loopDisplay: message.loopDisplay,
|
|
29
|
-
peaks,
|
|
30
|
-
playbackRate: message.playbackRate,
|
|
31
|
-
startFrom: message.startFrom,
|
|
32
|
-
});
|
|
33
|
-
(0, draw_peaks_1.drawBars)({
|
|
34
|
-
canvas,
|
|
35
|
-
peaks: portionPeaks,
|
|
36
|
-
color: 'rgba(255, 255, 255, 0.6)',
|
|
37
|
-
volume: message.volume,
|
|
38
|
-
width: message.width,
|
|
39
|
-
});
|
|
40
|
-
};
|
|
41
|
-
const renderWaveform = async (message) => {
|
|
42
|
-
if (!canvas) {
|
|
43
|
-
postError(message.requestId, new Error('Waveform canvas not initialized'));
|
|
44
|
-
return;
|
|
45
|
-
}
|
|
46
|
-
const controller = new AbortController();
|
|
47
|
-
currentController === null || currentController === void 0 ? void 0 : currentController.abort();
|
|
48
|
-
currentController = controller;
|
|
49
|
-
latestRequestId = message.requestId;
|
|
50
|
-
try {
|
|
51
|
-
canvas.width = message.width;
|
|
52
|
-
canvas.height = message.height;
|
|
53
|
-
const peaks = await (0, load_waveform_peaks_1.loadWaveformPeaks)(message.src, controller.signal, {
|
|
54
|
-
onProgress: ({ peaks: nextPeaks }) => {
|
|
55
|
-
if (controller.signal.aborted ||
|
|
56
|
-
latestRequestId !== message.requestId) {
|
|
57
|
-
return;
|
|
58
|
-
}
|
|
59
|
-
drawPartialWaveform(message, nextPeaks);
|
|
60
|
-
},
|
|
61
|
-
});
|
|
62
|
-
if (controller.signal.aborted || latestRequestId !== message.requestId) {
|
|
63
|
-
return;
|
|
64
|
-
}
|
|
65
|
-
drawPartialWaveform(message, peaks);
|
|
66
|
-
}
|
|
67
|
-
catch (error) {
|
|
68
|
-
if (controller.signal.aborted || latestRequestId !== message.requestId) {
|
|
69
|
-
return;
|
|
70
|
-
}
|
|
71
|
-
postError(message.requestId, error);
|
|
72
|
-
}
|
|
73
|
-
};
|
|
74
25
|
self.addEventListener('message', (event) => {
|
|
75
26
|
const message = event.data;
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
27
|
+
const controller = new AbortController();
|
|
28
|
+
(0, load_waveform_peaks_1.loadWaveformPeaks)(message.src, controller.signal, {
|
|
29
|
+
onProgress: ({ peaks, final }) => {
|
|
30
|
+
if (!final) {
|
|
31
|
+
postPeaks(message.requestId, peaks, false);
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
})
|
|
35
|
+
.then((peaks) => {
|
|
36
|
+
postPeaks(message.requestId, peaks, true);
|
|
37
|
+
})
|
|
38
|
+
.catch((error) => {
|
|
87
39
|
postError(message.requestId, error);
|
|
88
40
|
});
|
|
89
41
|
});
|
|
@@ -1,101 +1,3 @@
|
|
|
1
|
-
// src/audio-waveform/parse-color.ts
|
|
2
|
-
var colorCache = new Map;
|
|
3
|
-
var parseColor = (color) => {
|
|
4
|
-
const cached = colorCache.get(color);
|
|
5
|
-
if (cached)
|
|
6
|
-
return cached;
|
|
7
|
-
const ctx = new OffscreenCanvas(1, 1).getContext("2d");
|
|
8
|
-
ctx.fillStyle = color;
|
|
9
|
-
ctx.fillRect(0, 0, 1, 1);
|
|
10
|
-
const [r, g, b, a] = ctx.getImageData(0, 0, 1, 1).data;
|
|
11
|
-
const result = [r, g, b, a];
|
|
12
|
-
colorCache.set(color, result);
|
|
13
|
-
return result;
|
|
14
|
-
};
|
|
15
|
-
|
|
16
|
-
// src/audio-waveform/draw-peaks.ts
|
|
17
|
-
var CLIPPING_COLOR = "#FF7F50";
|
|
18
|
-
var getVolumeAtBar = ({
|
|
19
|
-
barIndex,
|
|
20
|
-
numBars,
|
|
21
|
-
volume
|
|
22
|
-
}) => {
|
|
23
|
-
if (typeof volume === "number") {
|
|
24
|
-
return volume;
|
|
25
|
-
}
|
|
26
|
-
if (volume.length === 0) {
|
|
27
|
-
return 1;
|
|
28
|
-
}
|
|
29
|
-
if (volume.length === 1 || numBars <= 1) {
|
|
30
|
-
return volume[0];
|
|
31
|
-
}
|
|
32
|
-
const volumeIndex = Math.round(barIndex / (numBars - 1) * (volume.length - 1));
|
|
33
|
-
return volume[volumeIndex] ?? 1;
|
|
34
|
-
};
|
|
35
|
-
var drawBars = ({
|
|
36
|
-
canvas,
|
|
37
|
-
color,
|
|
38
|
-
peaks,
|
|
39
|
-
volume,
|
|
40
|
-
width
|
|
41
|
-
}) => {
|
|
42
|
-
const ctx = canvas.getContext("2d");
|
|
43
|
-
if (!ctx) {
|
|
44
|
-
throw new Error("Failed to get canvas context");
|
|
45
|
-
}
|
|
46
|
-
const { height } = canvas;
|
|
47
|
-
const w = canvas.width;
|
|
48
|
-
if (w === 0 || height === 0) {
|
|
49
|
-
return;
|
|
50
|
-
}
|
|
51
|
-
ctx.clearRect(0, 0, w, height);
|
|
52
|
-
const [r, g, b, a] = parseColor(color);
|
|
53
|
-
const [cr, cg, cb, ca] = parseColor(CLIPPING_COLOR);
|
|
54
|
-
const imageData = ctx.createImageData(w, height);
|
|
55
|
-
const { data } = imageData;
|
|
56
|
-
const numBars = width;
|
|
57
|
-
for (let barIndex = 0;barIndex < numBars; barIndex++) {
|
|
58
|
-
const x = barIndex;
|
|
59
|
-
if (x >= w)
|
|
60
|
-
break;
|
|
61
|
-
const peakIndex = Math.floor(barIndex / numBars * peaks.length);
|
|
62
|
-
const peak = peaks[peakIndex] || 0;
|
|
63
|
-
const barVolume = getVolumeAtBar({ barIndex, numBars, volume });
|
|
64
|
-
const scaledPeak = peak * barVolume;
|
|
65
|
-
const halfBar = Math.max(0, Math.min(height / 2, scaledPeak * height / 2));
|
|
66
|
-
if (halfBar === 0)
|
|
67
|
-
continue;
|
|
68
|
-
const mid = height / 2;
|
|
69
|
-
const barY = Math.round(mid - halfBar);
|
|
70
|
-
const barEnd = Math.round(mid + halfBar);
|
|
71
|
-
const isClipping = scaledPeak > 1;
|
|
72
|
-
const clipTopEnd = isClipping ? Math.min(barY + 2, barEnd) : barY;
|
|
73
|
-
const clipBotStart = isClipping ? Math.max(barEnd - 2, barY) : barEnd;
|
|
74
|
-
for (let y = barY;y < clipTopEnd; y++) {
|
|
75
|
-
const idx = (y * w + x) * 4;
|
|
76
|
-
data[idx] = cr;
|
|
77
|
-
data[idx + 1] = cg;
|
|
78
|
-
data[idx + 2] = cb;
|
|
79
|
-
data[idx + 3] = ca;
|
|
80
|
-
}
|
|
81
|
-
for (let y = clipTopEnd;y < clipBotStart; y++) {
|
|
82
|
-
const idx = (y * w + x) * 4;
|
|
83
|
-
data[idx] = r;
|
|
84
|
-
data[idx + 1] = g;
|
|
85
|
-
data[idx + 2] = b;
|
|
86
|
-
data[idx + 3] = a;
|
|
87
|
-
}
|
|
88
|
-
for (let y = clipBotStart;y < barEnd; y++) {
|
|
89
|
-
const idx = (y * w + x) * 4;
|
|
90
|
-
data[idx] = cr;
|
|
91
|
-
data[idx + 1] = cg;
|
|
92
|
-
data[idx + 2] = cb;
|
|
93
|
-
data[idx + 3] = ca;
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
ctx.putImageData(imageData, 0, 0);
|
|
97
|
-
};
|
|
98
|
-
|
|
99
1
|
// src/audio-waveform/load-waveform-peaks.ts
|
|
100
2
|
import { ALL_FORMATS, AudioSampleSink, Input, UrlSource } from "mediabunny";
|
|
101
3
|
|
|
@@ -280,135 +182,18 @@ async function loadWaveformPeaks(url, signal, options) {
|
|
|
280
182
|
}
|
|
281
183
|
}
|
|
282
184
|
|
|
283
|
-
// src/loop-display.ts
|
|
284
|
-
var shouldTileLoopDisplay = (loopDisplay) => {
|
|
285
|
-
return loopDisplay !== undefined && loopDisplay.numberOfTimes > 1;
|
|
286
|
-
};
|
|
287
|
-
var getLoopDisplaySegments = ({
|
|
288
|
-
displayDurationInFrames,
|
|
289
|
-
displayOffsetInFrames,
|
|
290
|
-
loopDurationInFrames
|
|
291
|
-
}) => {
|
|
292
|
-
if (!Number.isFinite(displayDurationInFrames) || displayDurationInFrames <= 0 || !Number.isFinite(displayOffsetInFrames) || !Number.isFinite(loopDurationInFrames) || loopDurationInFrames <= 0) {
|
|
293
|
-
return [];
|
|
294
|
-
}
|
|
295
|
-
const displayEndInFrames = displayOffsetInFrames + displayDurationInFrames;
|
|
296
|
-
const firstLoopIndex = Math.floor(displayOffsetInFrames / loopDurationInFrames);
|
|
297
|
-
const lastLoopIndex = Math.floor(displayEndInFrames / loopDurationInFrames);
|
|
298
|
-
const segments = [];
|
|
299
|
-
for (let loopIndex = firstLoopIndex;loopIndex <= lastLoopIndex; loopIndex++) {
|
|
300
|
-
const loopStartInFrames = loopIndex * loopDurationInFrames;
|
|
301
|
-
const segmentStart = Math.max(displayOffsetInFrames, loopStartInFrames);
|
|
302
|
-
const segmentEnd = Math.min(displayEndInFrames, loopStartInFrames + loopDurationInFrames);
|
|
303
|
-
const durationInFrames = segmentEnd - segmentStart;
|
|
304
|
-
if (durationInFrames <= 0) {
|
|
305
|
-
continue;
|
|
306
|
-
}
|
|
307
|
-
segments.push({
|
|
308
|
-
loopIndex,
|
|
309
|
-
absoluteOffsetInFrames: segmentStart,
|
|
310
|
-
loopOffsetInFrames: segmentStart - loopStartInFrames,
|
|
311
|
-
durationInFrames
|
|
312
|
-
});
|
|
313
|
-
}
|
|
314
|
-
return segments;
|
|
315
|
-
};
|
|
316
|
-
var getLoopDisplayWidth = ({
|
|
317
|
-
visualizationWidth,
|
|
318
|
-
loopDisplay
|
|
319
|
-
}) => {
|
|
320
|
-
if (!shouldTileLoopDisplay(loopDisplay)) {
|
|
321
|
-
return visualizationWidth;
|
|
322
|
-
}
|
|
323
|
-
return visualizationWidth / loopDisplay.numberOfTimes;
|
|
324
|
-
};
|
|
325
|
-
|
|
326
|
-
// src/audio-waveform/slice-waveform-peaks.ts
|
|
327
|
-
var sliceWaveformPeaks = ({
|
|
328
|
-
durationInFrames,
|
|
329
|
-
fps,
|
|
330
|
-
peaks,
|
|
331
|
-
playbackRate,
|
|
332
|
-
startFrom
|
|
333
|
-
}) => {
|
|
334
|
-
if (peaks.length === 0) {
|
|
335
|
-
return peaks;
|
|
336
|
-
}
|
|
337
|
-
const startTimeInSeconds = startFrom / fps;
|
|
338
|
-
const durationInSeconds = durationInFrames / fps * playbackRate;
|
|
339
|
-
const startPeakIndex = Math.floor(startTimeInSeconds * TARGET_SAMPLE_RATE);
|
|
340
|
-
const endPeakIndex = Math.ceil((startTimeInSeconds + durationInSeconds) * TARGET_SAMPLE_RATE);
|
|
341
|
-
if (!Number.isFinite(startPeakIndex) || !Number.isFinite(endPeakIndex)) {
|
|
342
|
-
return peaks.subarray(Math.max(0, startPeakIndex), Math.min(peaks.length, endPeakIndex));
|
|
343
|
-
}
|
|
344
|
-
if (startPeakIndex >= 0 && endPeakIndex <= peaks.length) {
|
|
345
|
-
return peaks.subarray(startPeakIndex, endPeakIndex);
|
|
346
|
-
}
|
|
347
|
-
const portion = new Float32Array(Math.max(0, endPeakIndex - startPeakIndex));
|
|
348
|
-
const sourceStart = Math.max(0, startPeakIndex);
|
|
349
|
-
const sourceEnd = Math.min(peaks.length, endPeakIndex);
|
|
350
|
-
if (sourceStart < sourceEnd) {
|
|
351
|
-
portion.set(peaks.subarray(sourceStart, sourceEnd), sourceStart - startPeakIndex);
|
|
352
|
-
}
|
|
353
|
-
return portion;
|
|
354
|
-
};
|
|
355
|
-
|
|
356
|
-
// src/audio-waveform/slice-visible-waveform-peaks.ts
|
|
357
|
-
var sliceVisibleWaveformPeaks = ({
|
|
358
|
-
displayDurationInFrames,
|
|
359
|
-
displayOffsetInFrames,
|
|
360
|
-
durationInFrames,
|
|
361
|
-
fps,
|
|
362
|
-
loopDisplay,
|
|
363
|
-
peaks,
|
|
364
|
-
playbackRate,
|
|
365
|
-
startFrom
|
|
366
|
-
}) => {
|
|
367
|
-
if (!shouldTileLoopDisplay(loopDisplay) || loopDisplay.durationInFrames <= 0) {
|
|
368
|
-
return sliceWaveformPeaks({
|
|
369
|
-
durationInFrames: Math.min(displayDurationInFrames, Math.max(0, durationInFrames - displayOffsetInFrames)),
|
|
370
|
-
fps,
|
|
371
|
-
peaks,
|
|
372
|
-
playbackRate,
|
|
373
|
-
startFrom: startFrom + displayOffsetInFrames * playbackRate
|
|
374
|
-
});
|
|
375
|
-
}
|
|
376
|
-
const segments = getLoopDisplaySegments({
|
|
377
|
-
displayDurationInFrames,
|
|
378
|
-
displayOffsetInFrames,
|
|
379
|
-
loopDurationInFrames: loopDisplay.durationInFrames
|
|
380
|
-
});
|
|
381
|
-
const parts = [];
|
|
382
|
-
let totalLength = 0;
|
|
383
|
-
for (const segment of segments) {
|
|
384
|
-
const part = sliceWaveformPeaks({
|
|
385
|
-
durationInFrames: segment.durationInFrames,
|
|
386
|
-
fps,
|
|
387
|
-
peaks,
|
|
388
|
-
playbackRate,
|
|
389
|
-
startFrom: startFrom + segment.loopOffsetInFrames * playbackRate
|
|
390
|
-
});
|
|
391
|
-
parts.push(part);
|
|
392
|
-
totalLength += part.length;
|
|
393
|
-
}
|
|
394
|
-
if (parts.length === 1) {
|
|
395
|
-
return parts[0];
|
|
396
|
-
}
|
|
397
|
-
const result = new Float32Array(totalLength);
|
|
398
|
-
let writeOffset = 0;
|
|
399
|
-
for (const part of parts) {
|
|
400
|
-
result.set(part, writeOffset);
|
|
401
|
-
writeOffset += part.length;
|
|
402
|
-
}
|
|
403
|
-
return result;
|
|
404
|
-
};
|
|
405
|
-
|
|
406
185
|
// src/audio-waveform-worker.ts
|
|
407
|
-
var
|
|
408
|
-
|
|
409
|
-
|
|
186
|
+
var postPeaks = (requestId, peaks, final) => {
|
|
187
|
+
const payload = {
|
|
188
|
+
type: "peaks",
|
|
189
|
+
requestId,
|
|
190
|
+
peaks,
|
|
191
|
+
final
|
|
192
|
+
};
|
|
193
|
+
self.postMessage(payload);
|
|
194
|
+
};
|
|
410
195
|
var postError = (requestId, error) => {
|
|
411
|
-
const message = error instanceof Error ? error.message : "Failed to
|
|
196
|
+
const message = error instanceof Error ? error.message : "Failed to load waveform";
|
|
412
197
|
const payload = {
|
|
413
198
|
type: "error",
|
|
414
199
|
requestId,
|
|
@@ -416,72 +201,18 @@ var postError = (requestId, error) => {
|
|
|
416
201
|
};
|
|
417
202
|
self.postMessage(payload);
|
|
418
203
|
};
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
return;
|
|
422
|
-
}
|
|
423
|
-
const portionPeaks = sliceVisibleWaveformPeaks({
|
|
424
|
-
displayDurationInFrames: message.displayDurationInFrames,
|
|
425
|
-
displayOffsetInFrames: message.displayOffsetInFrames,
|
|
426
|
-
durationInFrames: message.durationInFrames,
|
|
427
|
-
fps: message.fps,
|
|
428
|
-
loopDisplay: message.loopDisplay,
|
|
429
|
-
peaks,
|
|
430
|
-
playbackRate: message.playbackRate,
|
|
431
|
-
startFrom: message.startFrom
|
|
432
|
-
});
|
|
433
|
-
drawBars({
|
|
434
|
-
canvas,
|
|
435
|
-
peaks: portionPeaks,
|
|
436
|
-
color: "rgba(255, 255, 255, 0.6)",
|
|
437
|
-
volume: message.volume,
|
|
438
|
-
width: message.width
|
|
439
|
-
});
|
|
440
|
-
};
|
|
441
|
-
var renderWaveform = async (message) => {
|
|
442
|
-
if (!canvas) {
|
|
443
|
-
postError(message.requestId, new Error("Waveform canvas not initialized"));
|
|
444
|
-
return;
|
|
445
|
-
}
|
|
204
|
+
self.addEventListener("message", (event) => {
|
|
205
|
+
const message = event.data;
|
|
446
206
|
const controller = new AbortController;
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
canvas.width = message.width;
|
|
452
|
-
canvas.height = message.height;
|
|
453
|
-
const peaks = await loadWaveformPeaks(message.src, controller.signal, {
|
|
454
|
-
onProgress: ({ peaks: nextPeaks }) => {
|
|
455
|
-
if (controller.signal.aborted || latestRequestId !== message.requestId) {
|
|
456
|
-
return;
|
|
457
|
-
}
|
|
458
|
-
drawPartialWaveform(message, nextPeaks);
|
|
207
|
+
loadWaveformPeaks(message.src, controller.signal, {
|
|
208
|
+
onProgress: ({ peaks, final }) => {
|
|
209
|
+
if (!final) {
|
|
210
|
+
postPeaks(message.requestId, peaks, false);
|
|
459
211
|
}
|
|
460
|
-
});
|
|
461
|
-
if (controller.signal.aborted || latestRequestId !== message.requestId) {
|
|
462
|
-
return;
|
|
463
212
|
}
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
return;
|
|
468
|
-
}
|
|
469
|
-
postError(message.requestId, error);
|
|
470
|
-
}
|
|
471
|
-
};
|
|
472
|
-
self.addEventListener("message", (event) => {
|
|
473
|
-
const message = event.data;
|
|
474
|
-
if (message.type === "init") {
|
|
475
|
-
canvas = message.canvas;
|
|
476
|
-
return;
|
|
477
|
-
}
|
|
478
|
-
if (message.type === "dispose") {
|
|
479
|
-
currentController?.abort();
|
|
480
|
-
currentController = null;
|
|
481
|
-
canvas = null;
|
|
482
|
-
return;
|
|
483
|
-
}
|
|
484
|
-
renderWaveform(message).catch((error) => {
|
|
213
|
+
}).then((peaks) => {
|
|
214
|
+
postPeaks(message.requestId, peaks, true);
|
|
215
|
+
}).catch((error) => {
|
|
485
216
|
postError(message.requestId, error);
|
|
486
217
|
});
|
|
487
218
|
});
|
package/dist/esm/index.mjs
CHANGED
|
@@ -353,12 +353,6 @@ async function loadWaveformPeaks(url, signal, options) {
|
|
|
353
353
|
input.dispose();
|
|
354
354
|
}
|
|
355
355
|
}
|
|
356
|
-
// src/audio-waveform/make-audio-waveform-worker.ts
|
|
357
|
-
var makeAudioWaveformWorker = () => {
|
|
358
|
-
return new Worker(new URL("./audio-waveform-worker.mjs", import.meta.url), {
|
|
359
|
-
type: "module"
|
|
360
|
-
});
|
|
361
|
-
};
|
|
362
356
|
// src/audio-waveform/slice-waveform-peaks.ts
|
|
363
357
|
var sliceWaveformPeaks = ({
|
|
364
358
|
durationInFrames,
|
|
@@ -438,6 +432,148 @@ var sliceVisibleWaveformPeaks = ({
|
|
|
438
432
|
}
|
|
439
433
|
return result;
|
|
440
434
|
};
|
|
435
|
+
// src/audio-waveform/make-audio-waveform-worker.ts
|
|
436
|
+
var makeAudioWaveformWorker = () => {
|
|
437
|
+
return new Worker(new URL("./audio-waveform-worker.mjs", import.meta.url), {
|
|
438
|
+
type: "module"
|
|
439
|
+
});
|
|
440
|
+
};
|
|
441
|
+
|
|
442
|
+
// src/audio-waveform/subscribe-to-waveform-peaks.ts
|
|
443
|
+
var peaksCache2 = new Map;
|
|
444
|
+
var inFlightBySrc = new Map;
|
|
445
|
+
var inFlightByRequestId = new Map;
|
|
446
|
+
var worker = null;
|
|
447
|
+
var workerFailed = false;
|
|
448
|
+
var nextRequestId = 0;
|
|
449
|
+
var emitPeaks = (load, peaks, final) => {
|
|
450
|
+
if (final) {
|
|
451
|
+
peaksCache2.set(load.src, peaks);
|
|
452
|
+
inFlightBySrc.delete(load.src);
|
|
453
|
+
if (load.requestId !== null) {
|
|
454
|
+
inFlightByRequestId.delete(load.requestId);
|
|
455
|
+
}
|
|
456
|
+
} else {
|
|
457
|
+
load.latestPeaks = peaks;
|
|
458
|
+
}
|
|
459
|
+
for (const listener of load.listeners) {
|
|
460
|
+
listener.onPeaks(peaks, final);
|
|
461
|
+
}
|
|
462
|
+
};
|
|
463
|
+
var emitError = (load, error) => {
|
|
464
|
+
inFlightBySrc.delete(load.src);
|
|
465
|
+
if (load.requestId !== null) {
|
|
466
|
+
inFlightByRequestId.delete(load.requestId);
|
|
467
|
+
}
|
|
468
|
+
for (const listener of load.listeners) {
|
|
469
|
+
listener.onError(error);
|
|
470
|
+
}
|
|
471
|
+
};
|
|
472
|
+
var startMainThreadLoad = (load) => {
|
|
473
|
+
load.requestId = null;
|
|
474
|
+
const controller = new AbortController;
|
|
475
|
+
loadWaveformPeaks(load.src, controller.signal, {
|
|
476
|
+
onProgress: ({ peaks, final }) => {
|
|
477
|
+
if (final) {
|
|
478
|
+
return;
|
|
479
|
+
}
|
|
480
|
+
emitPeaks(load, peaks.slice(), false);
|
|
481
|
+
}
|
|
482
|
+
}).then((peaks) => {
|
|
483
|
+
emitPeaks(load, peaks, true);
|
|
484
|
+
}).catch((error) => {
|
|
485
|
+
emitError(load, error instanceof Error ? error : new Error("Failed to load waveform peaks"));
|
|
486
|
+
});
|
|
487
|
+
};
|
|
488
|
+
var handleWorkerFailure = () => {
|
|
489
|
+
workerFailed = true;
|
|
490
|
+
worker?.terminate();
|
|
491
|
+
worker = null;
|
|
492
|
+
const pending = [...inFlightByRequestId.values()];
|
|
493
|
+
inFlightByRequestId.clear();
|
|
494
|
+
for (const load of pending) {
|
|
495
|
+
startMainThreadLoad(load);
|
|
496
|
+
}
|
|
497
|
+
};
|
|
498
|
+
var getOrCreateWorker = () => {
|
|
499
|
+
if (workerFailed || typeof Worker === "undefined") {
|
|
500
|
+
return null;
|
|
501
|
+
}
|
|
502
|
+
if (worker) {
|
|
503
|
+
return worker;
|
|
504
|
+
}
|
|
505
|
+
try {
|
|
506
|
+
worker = makeAudioWaveformWorker();
|
|
507
|
+
} catch {
|
|
508
|
+
workerFailed = true;
|
|
509
|
+
return null;
|
|
510
|
+
}
|
|
511
|
+
worker.addEventListener("message", (event) => {
|
|
512
|
+
const message = event.data;
|
|
513
|
+
const load = inFlightByRequestId.get(message.requestId);
|
|
514
|
+
if (!load) {
|
|
515
|
+
return;
|
|
516
|
+
}
|
|
517
|
+
if (message.type === "error") {
|
|
518
|
+
emitError(load, new Error(message.message));
|
|
519
|
+
return;
|
|
520
|
+
}
|
|
521
|
+
emitPeaks(load, message.peaks, message.final);
|
|
522
|
+
});
|
|
523
|
+
worker.addEventListener("error", (event) => {
|
|
524
|
+
event.preventDefault();
|
|
525
|
+
handleWorkerFailure();
|
|
526
|
+
});
|
|
527
|
+
return worker;
|
|
528
|
+
};
|
|
529
|
+
var subscribeToWaveformPeaks = ({
|
|
530
|
+
src,
|
|
531
|
+
onPeaks,
|
|
532
|
+
onError
|
|
533
|
+
}) => {
|
|
534
|
+
const cached = peaksCache2.get(src);
|
|
535
|
+
if (cached) {
|
|
536
|
+
onPeaks(cached, true);
|
|
537
|
+
return () => {
|
|
538
|
+
return;
|
|
539
|
+
};
|
|
540
|
+
}
|
|
541
|
+
const listener = { onPeaks, onError };
|
|
542
|
+
const existing = inFlightBySrc.get(src);
|
|
543
|
+
if (existing) {
|
|
544
|
+
existing.listeners.add(listener);
|
|
545
|
+
if (existing.latestPeaks) {
|
|
546
|
+
onPeaks(existing.latestPeaks, false);
|
|
547
|
+
}
|
|
548
|
+
return () => {
|
|
549
|
+
existing.listeners.delete(listener);
|
|
550
|
+
};
|
|
551
|
+
}
|
|
552
|
+
const load = {
|
|
553
|
+
src,
|
|
554
|
+
listeners: new Set([listener]),
|
|
555
|
+
latestPeaks: null,
|
|
556
|
+
requestId: null
|
|
557
|
+
};
|
|
558
|
+
inFlightBySrc.set(src, load);
|
|
559
|
+
const workerInstance = getOrCreateWorker();
|
|
560
|
+
if (workerInstance) {
|
|
561
|
+
const requestId = nextRequestId++;
|
|
562
|
+
load.requestId = requestId;
|
|
563
|
+
inFlightByRequestId.set(requestId, load);
|
|
564
|
+
const message = {
|
|
565
|
+
type: "load",
|
|
566
|
+
requestId,
|
|
567
|
+
src
|
|
568
|
+
};
|
|
569
|
+
workerInstance.postMessage(message);
|
|
570
|
+
} else {
|
|
571
|
+
startMainThreadLoad(load);
|
|
572
|
+
}
|
|
573
|
+
return () => {
|
|
574
|
+
load.listeners.delete(listener);
|
|
575
|
+
};
|
|
576
|
+
};
|
|
441
577
|
// src/clamp-timestamps-to-duration.ts
|
|
442
578
|
var LAST_FRAME_EPSILON_IN_SECONDS = 1 / 1e6;
|
|
443
579
|
var clampTimestampsToDuration = ({
|
|
@@ -458,6 +594,7 @@ import {
|
|
|
458
594
|
UrlSource as UrlSource2,
|
|
459
595
|
VideoSampleSink
|
|
460
596
|
} from "mediabunny";
|
|
597
|
+
import { Internals } from "remotion";
|
|
461
598
|
|
|
462
599
|
// src/get-duration-or-compute.ts
|
|
463
600
|
var getDurationOrCompute = async (input) => {
|
|
@@ -474,22 +611,34 @@ async function extractFrames({
|
|
|
474
611
|
signal,
|
|
475
612
|
repeatLastFrame = false
|
|
476
613
|
}) {
|
|
477
|
-
const
|
|
478
|
-
|
|
479
|
-
|
|
614
|
+
const lease = Internals.globalMediaResourceManager.acquire({
|
|
615
|
+
key: Internals.getMediabunnyInputResourceKey({
|
|
616
|
+
src,
|
|
617
|
+
credentials: null,
|
|
618
|
+
requestInitFingerprint: null,
|
|
619
|
+
revision: null
|
|
620
|
+
}),
|
|
621
|
+
create: () => {
|
|
622
|
+
const createdInput = new Input2({
|
|
623
|
+
formats: ALL_FORMATS2,
|
|
624
|
+
source: new UrlSource2(src)
|
|
625
|
+
});
|
|
626
|
+
return {
|
|
627
|
+
resource: createdInput,
|
|
628
|
+
dispose: () => createdInput.dispose()
|
|
629
|
+
};
|
|
630
|
+
}
|
|
480
631
|
});
|
|
481
|
-
const
|
|
482
|
-
input.dispose();
|
|
483
|
-
};
|
|
484
|
-
if (signal) {
|
|
485
|
-
signal.addEventListener("abort", dispose, { once: true });
|
|
486
|
-
}
|
|
632
|
+
const input = lease.resource;
|
|
487
633
|
try {
|
|
488
634
|
const [durationInSeconds, format, videoTrack] = await Promise.all([
|
|
489
|
-
getDurationOrCompute(input),
|
|
635
|
+
lease.getOrCreateValue(Internals.MEDIABUNNY_DURATION_VALUE_KEY, () => getDurationOrCompute(input)),
|
|
490
636
|
input.getFormat(),
|
|
491
637
|
input.getPrimaryVideoTrack()
|
|
492
638
|
]);
|
|
639
|
+
if (signal?.aborted) {
|
|
640
|
+
return;
|
|
641
|
+
}
|
|
493
642
|
if (!videoTrack) {
|
|
494
643
|
throw new Error("No video track found in the input");
|
|
495
644
|
}
|
|
@@ -538,10 +687,7 @@ async function extractFrames({
|
|
|
538
687
|
}
|
|
539
688
|
throw error;
|
|
540
689
|
} finally {
|
|
541
|
-
|
|
542
|
-
if (signal) {
|
|
543
|
-
signal.removeEventListener("abort", dispose);
|
|
544
|
-
}
|
|
690
|
+
lease.release();
|
|
545
691
|
}
|
|
546
692
|
}
|
|
547
693
|
// src/frame-database.ts
|
|
@@ -1006,13 +1152,13 @@ var renderFrameStripToCanvas = async ({
|
|
|
1006
1152
|
});
|
|
1007
1153
|
};
|
|
1008
1154
|
export {
|
|
1155
|
+
subscribeToWaveformPeaks,
|
|
1009
1156
|
sliceWaveformPeaks,
|
|
1010
1157
|
sliceVisibleWaveformPeaks,
|
|
1011
1158
|
shouldTileLoopDisplay,
|
|
1012
1159
|
resizeVideoFrame,
|
|
1013
1160
|
renderFrameStripToCanvas,
|
|
1014
1161
|
makeFrameDatabaseKey,
|
|
1015
|
-
makeAudioWaveformWorker,
|
|
1016
1162
|
loadWaveformPeaks,
|
|
1017
1163
|
getVisibleWaveformVolume,
|
|
1018
1164
|
getTimestampFromFrameDatabaseKey,
|
package/dist/extract-frames.js
CHANGED
|
@@ -2,26 +2,39 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.extractFrames = extractFrames;
|
|
4
4
|
const mediabunny_1 = require("mediabunny");
|
|
5
|
+
const remotion_1 = require("remotion");
|
|
5
6
|
const clamp_timestamps_to_duration_1 = require("./clamp-timestamps-to-duration");
|
|
6
7
|
const get_duration_or_compute_1 = require("./get-duration-or-compute");
|
|
7
8
|
async function extractFrames({ src, timestampsInSeconds, onVideoSample, signal, repeatLastFrame = false, }) {
|
|
8
9
|
var _a;
|
|
9
|
-
const
|
|
10
|
-
|
|
11
|
-
|
|
10
|
+
const lease = remotion_1.Internals.globalMediaResourceManager.acquire({
|
|
11
|
+
key: remotion_1.Internals.getMediabunnyInputResourceKey({
|
|
12
|
+
src,
|
|
13
|
+
credentials: null,
|
|
14
|
+
requestInitFingerprint: null,
|
|
15
|
+
revision: null,
|
|
16
|
+
}),
|
|
17
|
+
create: () => {
|
|
18
|
+
const createdInput = new mediabunny_1.Input({
|
|
19
|
+
formats: mediabunny_1.ALL_FORMATS,
|
|
20
|
+
source: new mediabunny_1.UrlSource(src),
|
|
21
|
+
});
|
|
22
|
+
return {
|
|
23
|
+
resource: createdInput,
|
|
24
|
+
dispose: () => createdInput.dispose(),
|
|
25
|
+
};
|
|
26
|
+
},
|
|
12
27
|
});
|
|
13
|
-
const
|
|
14
|
-
input.dispose();
|
|
15
|
-
};
|
|
16
|
-
if (signal) {
|
|
17
|
-
signal.addEventListener('abort', dispose, { once: true });
|
|
18
|
-
}
|
|
28
|
+
const input = lease.resource;
|
|
19
29
|
try {
|
|
20
30
|
const [durationInSeconds, format, videoTrack] = await Promise.all([
|
|
21
|
-
(0, get_duration_or_compute_1.getDurationOrCompute)(input),
|
|
31
|
+
lease.getOrCreateValue(remotion_1.Internals.MEDIABUNNY_DURATION_VALUE_KEY, () => (0, get_duration_or_compute_1.getDurationOrCompute)(input)),
|
|
22
32
|
input.getFormat(),
|
|
23
33
|
input.getPrimaryVideoTrack(),
|
|
24
34
|
]);
|
|
35
|
+
if (signal === null || signal === void 0 ? void 0 : signal.aborted) {
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
25
38
|
if (!videoTrack) {
|
|
26
39
|
throw new Error('No video track found in the input');
|
|
27
40
|
}
|
|
@@ -84,9 +97,6 @@ async function extractFrames({ src, timestampsInSeconds, onVideoSample, signal,
|
|
|
84
97
|
throw error;
|
|
85
98
|
}
|
|
86
99
|
finally {
|
|
87
|
-
|
|
88
|
-
if (signal) {
|
|
89
|
-
signal.removeEventListener('abort', dispose);
|
|
90
|
-
}
|
|
100
|
+
lease.release();
|
|
91
101
|
}
|
|
92
102
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
export type { AudioWaveformWorkerIncomingMessage, AudioWaveformWorkerOutgoingMessage,
|
|
1
|
+
export type { AudioWaveformWorkerIncomingMessage, AudioWaveformWorkerOutgoingMessage, } from './audio-waveform/audio-waveform-worker-types';
|
|
2
2
|
export { TARGET_SAMPLE_RATE } from './audio-waveform/constants';
|
|
3
3
|
export { drawBars, type WaveformVolume } from './audio-waveform/draw-peaks';
|
|
4
4
|
export { getVisibleWaveformVolume } from './audio-waveform/get-visible-waveform-volume';
|
|
5
5
|
export { loadWaveformPeaks } from './audio-waveform/load-waveform-peaks';
|
|
6
|
-
export { makeAudioWaveformWorker } from './audio-waveform/make-audio-waveform-worker';
|
|
7
6
|
export { sliceVisibleWaveformPeaks } from './audio-waveform/slice-visible-waveform-peaks';
|
|
7
|
+
export { subscribeToWaveformPeaks } from './audio-waveform/subscribe-to-waveform-peaks';
|
|
8
8
|
export { sliceWaveformPeaks } from './audio-waveform/slice-waveform-peaks';
|
|
9
9
|
export { createWaveformPeakProcessor, emitWaveformProgress, } from './audio-waveform/waveform-peak-processor';
|
|
10
10
|
export { clampTimestampsToDuration } from './clamp-timestamps-to-duration';
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.resizeVideoFrame = exports.renderFrameStripToCanvas = exports.getDurationOfOneFrame = exports.fillWithCachedFrames = exports.fillFrameWhereItFits = exports.ensureSlots = exports.drawSlot = exports.calculateTimestampSlots = exports.WEBCODECS_TIMESCALE = exports.shouldTileLoopDisplay = exports.getLoopDisplayWidth = exports.getLoopDisplaySegments = exports.getScaledImageThumbnailDimensions = exports.drawRepeatingImageThumbnail = exports.makeFrameDatabaseKey = exports.getTimestampFromFrameDatabaseKey = exports.getFrameDatabaseKeyPrefix = exports.getAspectRatioFromCache = exports.frameDatabase = exports.aspectRatioCache = exports.addFrameToCache = exports.extractFrames = exports.clampTimestampsToDuration = exports.emitWaveformProgress = exports.createWaveformPeakProcessor = exports.sliceWaveformPeaks = exports.
|
|
3
|
+
exports.resizeVideoFrame = exports.renderFrameStripToCanvas = exports.getDurationOfOneFrame = exports.fillWithCachedFrames = exports.fillFrameWhereItFits = exports.ensureSlots = exports.drawSlot = exports.calculateTimestampSlots = exports.WEBCODECS_TIMESCALE = exports.shouldTileLoopDisplay = exports.getLoopDisplayWidth = exports.getLoopDisplaySegments = exports.getScaledImageThumbnailDimensions = exports.drawRepeatingImageThumbnail = exports.makeFrameDatabaseKey = exports.getTimestampFromFrameDatabaseKey = exports.getFrameDatabaseKeyPrefix = exports.getAspectRatioFromCache = exports.frameDatabase = exports.aspectRatioCache = exports.addFrameToCache = exports.extractFrames = exports.clampTimestampsToDuration = exports.emitWaveformProgress = exports.createWaveformPeakProcessor = exports.sliceWaveformPeaks = exports.subscribeToWaveformPeaks = exports.sliceVisibleWaveformPeaks = exports.loadWaveformPeaks = exports.getVisibleWaveformVolume = exports.drawBars = exports.TARGET_SAMPLE_RATE = void 0;
|
|
4
4
|
const constants_1 = require("./audio-waveform/constants");
|
|
5
5
|
Object.defineProperty(exports, "TARGET_SAMPLE_RATE", { enumerable: true, get: function () { return constants_1.TARGET_SAMPLE_RATE; } });
|
|
6
6
|
const draw_peaks_1 = require("./audio-waveform/draw-peaks");
|
|
@@ -9,10 +9,10 @@ const get_visible_waveform_volume_1 = require("./audio-waveform/get-visible-wave
|
|
|
9
9
|
Object.defineProperty(exports, "getVisibleWaveformVolume", { enumerable: true, get: function () { return get_visible_waveform_volume_1.getVisibleWaveformVolume; } });
|
|
10
10
|
const load_waveform_peaks_1 = require("./audio-waveform/load-waveform-peaks");
|
|
11
11
|
Object.defineProperty(exports, "loadWaveformPeaks", { enumerable: true, get: function () { return load_waveform_peaks_1.loadWaveformPeaks; } });
|
|
12
|
-
const make_audio_waveform_worker_1 = require("./audio-waveform/make-audio-waveform-worker");
|
|
13
|
-
Object.defineProperty(exports, "makeAudioWaveformWorker", { enumerable: true, get: function () { return make_audio_waveform_worker_1.makeAudioWaveformWorker; } });
|
|
14
12
|
const slice_visible_waveform_peaks_1 = require("./audio-waveform/slice-visible-waveform-peaks");
|
|
15
13
|
Object.defineProperty(exports, "sliceVisibleWaveformPeaks", { enumerable: true, get: function () { return slice_visible_waveform_peaks_1.sliceVisibleWaveformPeaks; } });
|
|
14
|
+
const subscribe_to_waveform_peaks_1 = require("./audio-waveform/subscribe-to-waveform-peaks");
|
|
15
|
+
Object.defineProperty(exports, "subscribeToWaveformPeaks", { enumerable: true, get: function () { return subscribe_to_waveform_peaks_1.subscribeToWaveformPeaks; } });
|
|
16
16
|
const slice_waveform_peaks_1 = require("./audio-waveform/slice-waveform-peaks");
|
|
17
17
|
Object.defineProperty(exports, "sliceWaveformPeaks", { enumerable: true, get: function () { return slice_waveform_peaks_1.sliceWaveformPeaks; } });
|
|
18
18
|
const waveform_peak_processor_1 = require("./audio-waveform/waveform-peak-processor");
|
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"url": "https://github.com/remotion-dev/remotion/tree/main/packages/timeline-utils"
|
|
4
4
|
},
|
|
5
5
|
"name": "@remotion/timeline-utils",
|
|
6
|
-
"version": "4.0.
|
|
6
|
+
"version": "4.0.515",
|
|
7
7
|
"description": "Internal utilities for rendering Remotion timelines",
|
|
8
8
|
"main": "dist/index.js",
|
|
9
9
|
"scripts": {
|
|
@@ -21,11 +21,12 @@
|
|
|
21
21
|
"url": "https://github.com/remotion-dev/remotion/issues"
|
|
22
22
|
},
|
|
23
23
|
"dependencies": {
|
|
24
|
-
"mediabunny": "1.55.1"
|
|
24
|
+
"mediabunny": "1.55.1",
|
|
25
|
+
"remotion": "4.0.515"
|
|
25
26
|
},
|
|
26
27
|
"devDependencies": {
|
|
27
28
|
"@mediabunny/server": "1.55.1",
|
|
28
|
-
"@remotion/eslint-config-internal": "4.0.
|
|
29
|
+
"@remotion/eslint-config-internal": "4.0.515",
|
|
29
30
|
"eslint": "9.19.0",
|
|
30
31
|
"@typescript/native-preview": "7.0.0-dev.20260217.1"
|
|
31
32
|
},
|