@remotion/timeline-utils 4.0.513 → 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.
@@ -97,6 +97,82 @@ var drawBars = ({
97
97
  }
98
98
  ctx.putImageData(imageData, 0, 0);
99
99
  };
100
+ // src/loop-display.ts
101
+ var shouldTileLoopDisplay = (loopDisplay) => {
102
+ return loopDisplay !== undefined && loopDisplay.numberOfTimes > 1;
103
+ };
104
+ var getLoopDisplaySegments = ({
105
+ displayDurationInFrames,
106
+ displayOffsetInFrames,
107
+ loopDurationInFrames
108
+ }) => {
109
+ if (!Number.isFinite(displayDurationInFrames) || displayDurationInFrames <= 0 || !Number.isFinite(displayOffsetInFrames) || !Number.isFinite(loopDurationInFrames) || loopDurationInFrames <= 0) {
110
+ return [];
111
+ }
112
+ const displayEndInFrames = displayOffsetInFrames + displayDurationInFrames;
113
+ const firstLoopIndex = Math.floor(displayOffsetInFrames / loopDurationInFrames);
114
+ const lastLoopIndex = Math.floor(displayEndInFrames / loopDurationInFrames);
115
+ const segments = [];
116
+ for (let loopIndex = firstLoopIndex;loopIndex <= lastLoopIndex; loopIndex++) {
117
+ const loopStartInFrames = loopIndex * loopDurationInFrames;
118
+ const segmentStart = Math.max(displayOffsetInFrames, loopStartInFrames);
119
+ const segmentEnd = Math.min(displayEndInFrames, loopStartInFrames + loopDurationInFrames);
120
+ const durationInFrames = segmentEnd - segmentStart;
121
+ if (durationInFrames <= 0) {
122
+ continue;
123
+ }
124
+ segments.push({
125
+ loopIndex,
126
+ absoluteOffsetInFrames: segmentStart,
127
+ loopOffsetInFrames: segmentStart - loopStartInFrames,
128
+ durationInFrames
129
+ });
130
+ }
131
+ return segments;
132
+ };
133
+ var getLoopDisplayWidth = ({
134
+ visualizationWidth,
135
+ loopDisplay
136
+ }) => {
137
+ if (!shouldTileLoopDisplay(loopDisplay)) {
138
+ return visualizationWidth;
139
+ }
140
+ return visualizationWidth / loopDisplay.numberOfTimes;
141
+ };
142
+
143
+ // src/audio-waveform/get-visible-waveform-volume.ts
144
+ var getVisibleWaveformVolume = ({
145
+ displayDurationInFrames,
146
+ displayOffsetInFrames,
147
+ loopDisplay,
148
+ volume
149
+ }) => {
150
+ if (!Array.isArray(volume)) {
151
+ return volume;
152
+ }
153
+ if (!Number.isFinite(displayDurationInFrames) || displayDurationInFrames <= 0) {
154
+ return [];
155
+ }
156
+ if (!shouldTileLoopDisplay(loopDisplay) || loopDisplay.durationInFrames <= 0) {
157
+ const start = Math.max(0, Math.floor(displayOffsetInFrames));
158
+ const end = Math.min(volume.length, Math.ceil(displayOffsetInFrames + displayDurationInFrames));
159
+ return volume.slice(start, end);
160
+ }
161
+ const segments = getLoopDisplaySegments({
162
+ displayDurationInFrames,
163
+ displayOffsetInFrames,
164
+ loopDurationInFrames: loopDisplay.durationInFrames
165
+ });
166
+ const result = [];
167
+ for (const segment of segments) {
168
+ const start = Math.max(0, Math.floor(segment.loopOffsetInFrames));
169
+ const end = Math.min(volume.length, Math.ceil(segment.loopOffsetInFrames + segment.durationInFrames));
170
+ for (let index = start;index < end; index++) {
171
+ result.push(volume[index]);
172
+ }
173
+ }
174
+ return result;
175
+ };
100
176
  // src/audio-waveform/load-waveform-peaks.ts
101
177
  import { ALL_FORMATS, AudioSampleSink, Input, UrlSource } from "mediabunny";
102
178
 
@@ -277,12 +353,6 @@ async function loadWaveformPeaks(url, signal, options) {
277
353
  input.dispose();
278
354
  }
279
355
  }
280
- // src/audio-waveform/make-audio-waveform-worker.ts
281
- var makeAudioWaveformWorker = () => {
282
- return new Worker(new URL("./audio-waveform-worker.mjs", import.meta.url), {
283
- type: "module"
284
- });
285
- };
286
356
  // src/audio-waveform/slice-waveform-peaks.ts
287
357
  var sliceWaveformPeaks = ({
288
358
  durationInFrames,
@@ -312,70 +382,197 @@ var sliceWaveformPeaks = ({
312
382
  }
313
383
  return portion;
314
384
  };
315
- // src/image-thumbnail/get-scaled-image-thumbnail-dimensions.ts
316
- var getScaledImageThumbnailDimensions = ({
317
- naturalWidth,
318
- naturalHeight,
319
- canvasHeight
385
+
386
+ // src/audio-waveform/slice-visible-waveform-peaks.ts
387
+ var sliceVisibleWaveformPeaks = ({
388
+ displayDurationInFrames,
389
+ displayOffsetInFrames,
390
+ durationInFrames,
391
+ fps,
392
+ loopDisplay,
393
+ peaks,
394
+ playbackRate,
395
+ startFrom
320
396
  }) => {
321
- if (naturalWidth === 0 || naturalHeight === 0 || canvasHeight === 0) {
322
- return {
323
- scaledWidth: 0,
324
- scaledHeight: 0
325
- };
397
+ if (!shouldTileLoopDisplay(loopDisplay) || loopDisplay.durationInFrames <= 0) {
398
+ return sliceWaveformPeaks({
399
+ durationInFrames: Math.min(displayDurationInFrames, Math.max(0, durationInFrames - displayOffsetInFrames)),
400
+ fps,
401
+ peaks,
402
+ playbackRate,
403
+ startFrom: startFrom + displayOffsetInFrames * playbackRate
404
+ });
326
405
  }
327
- const scale = canvasHeight / naturalHeight;
328
- return {
329
- scaledWidth: naturalWidth * scale,
330
- scaledHeight: canvasHeight
331
- };
406
+ const segments = getLoopDisplaySegments({
407
+ displayDurationInFrames,
408
+ displayOffsetInFrames,
409
+ loopDurationInFrames: loopDisplay.durationInFrames
410
+ });
411
+ const parts = [];
412
+ let totalLength = 0;
413
+ for (const segment of segments) {
414
+ const part = sliceWaveformPeaks({
415
+ durationInFrames: segment.durationInFrames,
416
+ fps,
417
+ peaks,
418
+ playbackRate,
419
+ startFrom: startFrom + segment.loopOffsetInFrames * playbackRate
420
+ });
421
+ parts.push(part);
422
+ totalLength += part.length;
423
+ }
424
+ if (parts.length === 1) {
425
+ return parts[0];
426
+ }
427
+ const result = new Float32Array(totalLength);
428
+ let writeOffset = 0;
429
+ for (const part of parts) {
430
+ result.set(part, writeOffset);
431
+ writeOffset += part.length;
432
+ }
433
+ return result;
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
+ });
332
440
  };
333
441
 
334
- // src/image-thumbnail/draw-repeating-image-thumbnail.ts
335
- var createOffscreenCanvas = (width, height) => {
336
- if (typeof document !== "undefined") {
337
- const canvas = document.createElement("canvas");
338
- canvas.width = width;
339
- canvas.height = height;
340
- return canvas;
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;
341
458
  }
342
- if (typeof OffscreenCanvas !== "undefined") {
343
- return new OffscreenCanvas(width, height);
459
+ for (const listener of load.listeners) {
460
+ listener.onPeaks(peaks, final);
344
461
  }
345
- throw new Error("No canvas implementation available");
346
462
  };
347
- var drawRepeatingImageThumbnail = ({
348
- canvas,
349
- image
350
- }) => {
351
- const ctx = canvas.getContext("2d");
352
- if (!ctx) {
353
- throw new Error("Failed to get canvas context");
463
+ var emitError = (load, error) => {
464
+ inFlightBySrc.delete(load.src);
465
+ if (load.requestId !== null) {
466
+ inFlightByRequestId.delete(load.requestId);
354
467
  }
355
- const { width, height } = canvas;
356
- if (width === 0 || height === 0) {
357
- return;
468
+ for (const listener of load.listeners) {
469
+ listener.onError(error);
358
470
  }
359
- const { scaledWidth, scaledHeight } = getScaledImageThumbnailDimensions({
360
- naturalWidth: image.naturalWidth,
361
- naturalHeight: image.naturalHeight,
362
- canvasHeight: height
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"));
363
486
  });
364
- if (scaledWidth === 0 || scaledHeight === 0) {
365
- return;
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);
366
496
  }
367
- const offscreen = createOffscreenCanvas(scaledWidth, scaledHeight);
368
- const offCtx = offscreen.getContext("2d");
369
- if (!offCtx) {
370
- throw new Error("Failed to get offscreen canvas context");
497
+ };
498
+ var getOrCreateWorker = () => {
499
+ if (workerFailed || typeof Worker === "undefined") {
500
+ return null;
371
501
  }
372
- offCtx.drawImage(image, 0, 0, scaledWidth, scaledHeight);
373
- const pattern = ctx.createPattern(offscreen, "repeat-x");
374
- if (!pattern) {
375
- return;
502
+ if (worker) {
503
+ return worker;
376
504
  }
377
- ctx.fillStyle = pattern;
378
- ctx.fillRect(0, 0, width, height);
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
+ };
379
576
  };
380
577
  // src/clamp-timestamps-to-duration.ts
381
578
  var LAST_FRAME_EPSILON_IN_SECONDS = 1 / 1e6;
@@ -397,6 +594,7 @@ import {
397
594
  UrlSource as UrlSource2,
398
595
  VideoSampleSink
399
596
  } from "mediabunny";
597
+ import { Internals } from "remotion";
400
598
 
401
599
  // src/get-duration-or-compute.ts
402
600
  var getDurationOrCompute = async (input) => {
@@ -413,22 +611,34 @@ async function extractFrames({
413
611
  signal,
414
612
  repeatLastFrame = false
415
613
  }) {
416
- const input = new Input2({
417
- formats: ALL_FORMATS2,
418
- source: new UrlSource2(src)
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
+ }
419
631
  });
420
- const dispose = () => {
421
- input.dispose();
422
- };
423
- if (signal) {
424
- signal.addEventListener("abort", dispose, { once: true });
425
- }
632
+ const input = lease.resource;
426
633
  try {
427
634
  const [durationInSeconds, format, videoTrack] = await Promise.all([
428
- getDurationOrCompute(input),
635
+ lease.getOrCreateValue(Internals.MEDIABUNNY_DURATION_VALUE_KEY, () => getDurationOrCompute(input)),
429
636
  input.getFormat(),
430
637
  input.getPrimaryVideoTrack()
431
638
  ]);
639
+ if (signal?.aborted) {
640
+ return;
641
+ }
432
642
  if (!videoTrack) {
433
643
  throw new Error("No video track found in the input");
434
644
  }
@@ -477,10 +687,7 @@ async function extractFrames({
477
687
  }
478
688
  throw error;
479
689
  } finally {
480
- dispose();
481
- if (signal) {
482
- signal.removeEventListener("abort", dispose);
483
- }
690
+ lease.release();
484
691
  }
485
692
  }
486
693
  // src/frame-database.ts
@@ -540,18 +747,72 @@ var addFrameToCache = (key, frame) => {
540
747
  });
541
748
  evictLRU();
542
749
  };
543
- // src/loop-display.ts
544
- var shouldTileLoopDisplay = (loopDisplay) => {
545
- return loopDisplay !== undefined && loopDisplay.numberOfTimes > 1;
750
+ // src/image-thumbnail/get-scaled-image-thumbnail-dimensions.ts
751
+ var getScaledImageThumbnailDimensions = ({
752
+ naturalWidth,
753
+ naturalHeight,
754
+ canvasHeight
755
+ }) => {
756
+ if (naturalWidth === 0 || naturalHeight === 0 || canvasHeight === 0) {
757
+ return {
758
+ scaledWidth: 0,
759
+ scaledHeight: 0
760
+ };
761
+ }
762
+ const scale = canvasHeight / naturalHeight;
763
+ return {
764
+ scaledWidth: naturalWidth * scale,
765
+ scaledHeight: canvasHeight
766
+ };
546
767
  };
547
- var getLoopDisplayWidth = ({
548
- visualizationWidth,
549
- loopDisplay
768
+
769
+ // src/image-thumbnail/draw-repeating-image-thumbnail.ts
770
+ var createOffscreenCanvas = (width, height) => {
771
+ if (typeof document !== "undefined") {
772
+ const canvas = document.createElement("canvas");
773
+ canvas.width = width;
774
+ canvas.height = height;
775
+ return canvas;
776
+ }
777
+ if (typeof OffscreenCanvas !== "undefined") {
778
+ return new OffscreenCanvas(width, height);
779
+ }
780
+ throw new Error("No canvas implementation available");
781
+ };
782
+ var drawRepeatingImageThumbnail = ({
783
+ canvas,
784
+ image,
785
+ offsetInPixels = 0
550
786
  }) => {
551
- if (!shouldTileLoopDisplay(loopDisplay)) {
552
- return visualizationWidth;
787
+ const ctx = canvas.getContext("2d");
788
+ if (!ctx) {
789
+ throw new Error("Failed to get canvas context");
553
790
  }
554
- return visualizationWidth / loopDisplay.numberOfTimes;
791
+ const { width, height } = canvas;
792
+ if (width === 0 || height === 0) {
793
+ return;
794
+ }
795
+ const { scaledWidth, scaledHeight } = getScaledImageThumbnailDimensions({
796
+ naturalWidth: image.naturalWidth,
797
+ naturalHeight: image.naturalHeight,
798
+ canvasHeight: height
799
+ });
800
+ if (scaledWidth === 0 || scaledHeight === 0) {
801
+ return;
802
+ }
803
+ const offscreen = createOffscreenCanvas(scaledWidth, scaledHeight);
804
+ const offCtx = offscreen.getContext("2d");
805
+ if (!offCtx) {
806
+ throw new Error("Failed to get offscreen canvas context");
807
+ }
808
+ offCtx.drawImage(image, 0, 0, scaledWidth, scaledHeight);
809
+ const pattern = ctx.createPattern(offscreen, "repeat-x");
810
+ if (!pattern) {
811
+ return;
812
+ }
813
+ pattern.setTransform(new DOMMatrix().translate(-offsetInPixels, 0));
814
+ ctx.fillStyle = pattern;
815
+ ctx.fillRect(0, 0, width, height);
555
816
  };
556
817
  // src/render-frame-strip.ts
557
818
  var WEBCODECS_TIMESCALE = 1e6;
@@ -891,16 +1152,19 @@ var renderFrameStripToCanvas = async ({
891
1152
  });
892
1153
  };
893
1154
  export {
1155
+ subscribeToWaveformPeaks,
894
1156
  sliceWaveformPeaks,
1157
+ sliceVisibleWaveformPeaks,
895
1158
  shouldTileLoopDisplay,
896
1159
  resizeVideoFrame,
897
1160
  renderFrameStripToCanvas,
898
1161
  makeFrameDatabaseKey,
899
- makeAudioWaveformWorker,
900
1162
  loadWaveformPeaks,
1163
+ getVisibleWaveformVolume,
901
1164
  getTimestampFromFrameDatabaseKey,
902
1165
  getScaledImageThumbnailDimensions,
903
1166
  getLoopDisplayWidth,
1167
+ getLoopDisplaySegments,
904
1168
  getFrameDatabaseKeyPrefix,
905
1169
  getDurationOfOneFrame,
906
1170
  getAspectRatioFromCache,
@@ -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 input = new mediabunny_1.Input({
10
- formats: mediabunny_1.ALL_FORMATS,
11
- source: new mediabunny_1.UrlSource(src),
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 dispose = () => {
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
- dispose();
88
- if (signal) {
89
- signal.removeEventListener('abort', dispose);
90
- }
100
+ lease.release();
91
101
  }
92
102
  }
@@ -2,8 +2,9 @@ type ImageWithNaturalDimensions = CanvasImageSource & {
2
2
  readonly naturalWidth: number;
3
3
  readonly naturalHeight: number;
4
4
  };
5
- export declare const drawRepeatingImageThumbnail: ({ canvas, image, }: {
5
+ export declare const drawRepeatingImageThumbnail: ({ canvas, image, offsetInPixels, }: {
6
6
  readonly canvas: HTMLCanvasElement | OffscreenCanvas;
7
7
  readonly image: ImageWithNaturalDimensions;
8
+ readonly offsetInPixels?: number | undefined;
8
9
  }) => void;
9
10
  export {};
@@ -14,7 +14,7 @@ const createOffscreenCanvas = (width, height) => {
14
14
  }
15
15
  throw new Error('No canvas implementation available');
16
16
  };
17
- const drawRepeatingImageThumbnail = ({ canvas, image, }) => {
17
+ const drawRepeatingImageThumbnail = ({ canvas, image, offsetInPixels = 0, }) => {
18
18
  const ctx = canvas.getContext('2d');
19
19
  if (!ctx) {
20
20
  throw new Error('Failed to get canvas context');
@@ -41,6 +41,7 @@ const drawRepeatingImageThumbnail = ({ canvas, image, }) => {
41
41
  if (!pattern) {
42
42
  return;
43
43
  }
44
+ pattern.setTransform(new DOMMatrix().translate(-offsetInPixels, 0));
44
45
  ctx.fillStyle = pattern;
45
46
  ctx.fillRect(0, 0, width, height);
46
47
  };
package/dist/index.d.ts CHANGED
@@ -1,20 +1,22 @@
1
- export type { AudioWaveformWorkerIncomingMessage, AudioWaveformWorkerOutgoingMessage, AudioWaveformWorkerRenderMessage, } from './audio-waveform/audio-waveform-worker-types';
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
+ export { getVisibleWaveformVolume } from './audio-waveform/get-visible-waveform-volume';
4
5
  export { loadWaveformPeaks } from './audio-waveform/load-waveform-peaks';
5
- export { makeAudioWaveformWorker } from './audio-waveform/make-audio-waveform-worker';
6
+ export { sliceVisibleWaveformPeaks } from './audio-waveform/slice-visible-waveform-peaks';
7
+ export { subscribeToWaveformPeaks } from './audio-waveform/subscribe-to-waveform-peaks';
6
8
  export { sliceWaveformPeaks } from './audio-waveform/slice-waveform-peaks';
7
9
  export { createWaveformPeakProcessor, emitWaveformProgress, } from './audio-waveform/waveform-peak-processor';
8
- export { drawRepeatingImageThumbnail } from './image-thumbnail/draw-repeating-image-thumbnail';
9
- export { getScaledImageThumbnailDimensions } from './image-thumbnail/get-scaled-image-thumbnail-dimensions';
10
10
  export { clampTimestampsToDuration } from './clamp-timestamps-to-duration';
11
11
  export { extractFrames } from './extract-frames';
12
12
  export type { ExtractFramesProps, ExtractFramesTimestampsInSecondsFn, } from './extract-frames';
13
13
  export { addFrameToCache, aspectRatioCache, frameDatabase, getAspectRatioFromCache, getFrameDatabaseKeyPrefix, getTimestampFromFrameDatabaseKey, makeFrameDatabaseKey, } from './frame-database';
14
14
  export type { FrameDatabaseKey } from './frame-database';
15
- export { getLoopDisplayWidth, shouldTileLoopDisplay } from './loop-display';
16
- export type { TimelineLoopDisplay } from './loop-display';
17
- export { calculateTimestampSlots, drawSlot, ensureSlots, fillFrameWhereItFits, fillWithCachedFrames, getDurationOfOneFrame, WEBCODECS_TIMESCALE, } from './render-frame-strip';
15
+ export { drawRepeatingImageThumbnail } from './image-thumbnail/draw-repeating-image-thumbnail';
16
+ export { getScaledImageThumbnailDimensions } from './image-thumbnail/get-scaled-image-thumbnail-dimensions';
17
+ export { getLoopDisplaySegments, getLoopDisplayWidth, shouldTileLoopDisplay, } from './loop-display';
18
+ export type { LoopDisplaySegment, TimelineLoopDisplay } from './loop-display';
19
+ export { WEBCODECS_TIMESCALE, calculateTimestampSlots, drawSlot, ensureSlots, fillFrameWhereItFits, fillWithCachedFrames, getDurationOfOneFrame, } from './render-frame-strip';
18
20
  export { renderFrameStripToCanvas } from './render-frame-strip-to-canvas';
19
21
  export type { RenderFrameStripToCanvasOptions } from './render-frame-strip-to-canvas';
20
22
  export { resizeVideoFrame } from './resize-video-frame';