@remotion/timeline-utils 4.0.526 → 4.0.528

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.
@@ -1,8 +1,6 @@
1
- import { type TimelineLoopDisplay } from '../loop-display';
2
1
  import type { WaveformVolume } from './draw-peaks';
3
- export declare const getVisibleWaveformVolume: ({ displayDurationInFrames, displayOffsetInFrames, loopDisplay, volume, }: {
2
+ export declare const getVisibleWaveformVolume: ({ displayDurationInFrames, displayOffsetInFrames, volume, }: {
4
3
  readonly displayDurationInFrames: number;
5
4
  readonly displayOffsetInFrames: number;
6
- readonly loopDisplay: TimelineLoopDisplay | undefined;
7
5
  readonly volume: WaveformVolume;
8
6
  }) => WaveformVolume;
@@ -1,8 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.getVisibleWaveformVolume = void 0;
4
- const loop_display_1 = require("../loop-display");
5
- const getVisibleWaveformVolume = ({ displayDurationInFrames, displayOffsetInFrames, loopDisplay, volume, }) => {
4
+ const getVisibleWaveformVolume = ({ displayDurationInFrames, displayOffsetInFrames, volume, }) => {
6
5
  if (!Array.isArray(volume)) {
7
6
  return volume;
8
7
  }
@@ -10,27 +9,10 @@ const getVisibleWaveformVolume = ({ displayDurationInFrames, displayOffsetInFram
10
9
  displayDurationInFrames <= 0) {
11
10
  return [];
12
11
  }
13
- if (!(0, loop_display_1.shouldTileLoopDisplay)(loopDisplay) ||
14
- loopDisplay.durationInFrames <= 0) {
15
- const start = Math.max(0, Math.floor(displayOffsetInFrames));
16
- const end = Math.min(volume.length, Math.ceil(displayOffsetInFrames + displayDurationInFrames));
17
- return volume.slice(start, end);
18
- }
19
- const segments = (0, loop_display_1.getLoopDisplaySegments)({
20
- displayDurationInFrames,
21
- displayOffsetInFrames,
22
- loopDurationInFrames: loopDisplay.durationInFrames,
23
- });
24
- const result = [];
25
- for (const segment of segments) {
26
- const start = Math.max(0, Math.floor(segment.loopOffsetInFrames));
27
- const end = Math.min(volume.length, Math.ceil(segment.loopOffsetInFrames + segment.durationInFrames));
28
- // Do not `push(...slice)`: Chrome throws RangeError: Invalid array length
29
- // once a loop segment is longer than ~65k frames.
30
- for (let index = start; index < end; index++) {
31
- result.push(volume[index]);
32
- }
33
- }
34
- return result;
12
+ // Registered curves already cover the visible composition timeline, including
13
+ // repeat/extend behavior and partial loops introduced by ancestor trimming.
14
+ const start = Math.max(0, Math.floor(displayOffsetInFrames));
15
+ const end = Math.min(volume.length, Math.ceil(displayOffsetInFrames + displayDurationInFrames));
16
+ return volume.slice(start, end);
35
17
  };
36
18
  exports.getVisibleWaveformVolume = getVisibleWaveformVolume;
@@ -4,8 +4,7 @@ exports.sliceVisibleWaveformPeaks = void 0;
4
4
  const loop_display_1 = require("../loop-display");
5
5
  const slice_waveform_peaks_1 = require("./slice-waveform-peaks");
6
6
  const sliceVisibleWaveformPeaks = ({ displayDurationInFrames, displayOffsetInFrames, durationInFrames, fps, loopDisplay, peaks, playbackRate, startFrom, waveformSampleRate, }) => {
7
- if (!(0, loop_display_1.shouldTileLoopDisplay)(loopDisplay) ||
8
- loopDisplay.durationInFrames <= 0) {
7
+ if (!loopDisplay || loopDisplay.durationInFrames <= 0) {
9
8
  return (0, slice_waveform_peaks_1.sliceWaveformPeaks)({
10
9
  durationInFrames: Math.min(displayDurationInFrames, Math.max(0, durationInFrames - displayOffsetInFrames)),
11
10
  fps,
@@ -8,8 +8,18 @@ const sliceWaveformPeaks = ({ durationInFrames, fps, peaks, playbackRate, startF
8
8
  }
9
9
  const startTimeInSeconds = startFrom / fps;
10
10
  const durationInSeconds = (durationInFrames / fps) * playbackRate;
11
- const startPeakIndex = Math.floor(startTimeInSeconds * waveformSampleRate);
12
- const endPeakIndex = Math.ceil((startTimeInSeconds + durationInSeconds) * waveformSampleRate);
11
+ const start = startTimeInSeconds * waveformSampleRate;
12
+ const end = (startTimeInSeconds + durationInSeconds) * waveformSampleRate;
13
+ // Fractional loop periods can round an exact trim boundary a few ULPs past
14
+ // the next sample. Do not include a peak from outside the trimmed source.
15
+ const startPeakIndex = Math.floor(start +
16
+ (Number.isFinite(start)
17
+ ? Number.EPSILON * Math.max(1, Math.abs(start)) * 4
18
+ : 0));
19
+ const endPeakIndex = Math.ceil(end -
20
+ (Number.isFinite(end)
21
+ ? Number.EPSILON * Math.max(1, Math.abs(end)) * 4
22
+ : 0));
13
23
  if (!Number.isFinite(startPeakIndex) || !Number.isFinite(endPeakIndex)) {
14
24
  return peaks.subarray(Math.max(0, startPeakIndex), Math.min(peaks.length, endPeakIndex));
15
25
  }
@@ -99,54 +99,10 @@ var drawBars = ({
99
99
  }
100
100
  ctx.putImageData(imageData, 0, 0);
101
101
  };
102
- // src/loop-display.ts
103
- var shouldTileLoopDisplay = (loopDisplay) => {
104
- return loopDisplay !== undefined && loopDisplay.numberOfTimes > 1;
105
- };
106
- var getLoopDisplaySegments = ({
107
- displayDurationInFrames,
108
- displayOffsetInFrames,
109
- loopDurationInFrames
110
- }) => {
111
- if (!Number.isFinite(displayDurationInFrames) || displayDurationInFrames <= 0 || !Number.isFinite(displayOffsetInFrames) || !Number.isFinite(loopDurationInFrames) || loopDurationInFrames <= 0) {
112
- return [];
113
- }
114
- const displayEndInFrames = displayOffsetInFrames + displayDurationInFrames;
115
- const firstLoopIndex = Math.floor(displayOffsetInFrames / loopDurationInFrames);
116
- const lastLoopIndex = Math.floor(displayEndInFrames / loopDurationInFrames);
117
- const segments = [];
118
- for (let loopIndex = firstLoopIndex;loopIndex <= lastLoopIndex; loopIndex++) {
119
- const loopStartInFrames = loopIndex * loopDurationInFrames;
120
- const segmentStart = Math.max(displayOffsetInFrames, loopStartInFrames);
121
- const segmentEnd = Math.min(displayEndInFrames, loopStartInFrames + loopDurationInFrames);
122
- const durationInFrames = segmentEnd - segmentStart;
123
- if (durationInFrames <= 0) {
124
- continue;
125
- }
126
- segments.push({
127
- loopIndex,
128
- absoluteOffsetInFrames: segmentStart,
129
- loopOffsetInFrames: segmentStart - loopStartInFrames,
130
- durationInFrames
131
- });
132
- }
133
- return segments;
134
- };
135
- var getLoopDisplayWidth = ({
136
- visualizationWidth,
137
- loopDisplay
138
- }) => {
139
- if (!shouldTileLoopDisplay(loopDisplay)) {
140
- return visualizationWidth;
141
- }
142
- return visualizationWidth / loopDisplay.numberOfTimes;
143
- };
144
-
145
102
  // src/audio-waveform/get-visible-waveform-volume.ts
146
103
  var getVisibleWaveformVolume = ({
147
104
  displayDurationInFrames,
148
105
  displayOffsetInFrames,
149
- loopDisplay,
150
106
  volume
151
107
  }) => {
152
108
  if (!Array.isArray(volume)) {
@@ -155,25 +111,9 @@ var getVisibleWaveformVolume = ({
155
111
  if (!Number.isFinite(displayDurationInFrames) || displayDurationInFrames <= 0) {
156
112
  return [];
157
113
  }
158
- if (!shouldTileLoopDisplay(loopDisplay) || loopDisplay.durationInFrames <= 0) {
159
- const start = Math.max(0, Math.floor(displayOffsetInFrames));
160
- const end = Math.min(volume.length, Math.ceil(displayOffsetInFrames + displayDurationInFrames));
161
- return volume.slice(start, end);
162
- }
163
- const segments = getLoopDisplaySegments({
164
- displayDurationInFrames,
165
- displayOffsetInFrames,
166
- loopDurationInFrames: loopDisplay.durationInFrames
167
- });
168
- const result = [];
169
- for (const segment of segments) {
170
- const start = Math.max(0, Math.floor(segment.loopOffsetInFrames));
171
- const end = Math.min(volume.length, Math.ceil(segment.loopOffsetInFrames + segment.durationInFrames));
172
- for (let index = start;index < end; index++) {
173
- result.push(volume[index]);
174
- }
175
- }
176
- return result;
114
+ const start = Math.max(0, Math.floor(displayOffsetInFrames));
115
+ const end = Math.min(volume.length, Math.ceil(displayOffsetInFrames + displayDurationInFrames));
116
+ return volume.slice(start, end);
177
117
  };
178
118
  // src/audio-waveform/load-waveform-peaks.ts
179
119
  import { ALL_FORMATS, AudioSampleSink, Input, UrlSource } from "mediabunny";
@@ -389,6 +329,49 @@ async function loadWaveformPeaks(src, signal, options) {
389
329
  input?.dispose();
390
330
  }
391
331
  }
332
+ // src/loop-display.ts
333
+ var shouldTileLoopDisplay = (loopDisplay) => {
334
+ return loopDisplay !== undefined && loopDisplay.numberOfTimes > 1;
335
+ };
336
+ var getLoopDisplaySegments = ({
337
+ displayDurationInFrames,
338
+ displayOffsetInFrames,
339
+ loopDurationInFrames
340
+ }) => {
341
+ if (!Number.isFinite(displayDurationInFrames) || displayDurationInFrames <= 0 || !Number.isFinite(displayOffsetInFrames) || !Number.isFinite(loopDurationInFrames) || loopDurationInFrames <= 0) {
342
+ return [];
343
+ }
344
+ const displayEndInFrames = displayOffsetInFrames + displayDurationInFrames;
345
+ const firstLoopIndex = Math.floor(displayOffsetInFrames / loopDurationInFrames);
346
+ const lastLoopIndex = Math.floor(displayEndInFrames / loopDurationInFrames);
347
+ const segments = [];
348
+ for (let loopIndex = firstLoopIndex;loopIndex <= lastLoopIndex; loopIndex++) {
349
+ const loopStartInFrames = loopIndex * loopDurationInFrames;
350
+ const segmentStart = Math.max(displayOffsetInFrames, loopStartInFrames);
351
+ const segmentEnd = Math.min(displayEndInFrames, loopStartInFrames + loopDurationInFrames);
352
+ const durationInFrames = segmentEnd - segmentStart;
353
+ if (durationInFrames <= 0) {
354
+ continue;
355
+ }
356
+ segments.push({
357
+ loopIndex,
358
+ absoluteOffsetInFrames: segmentStart,
359
+ loopOffsetInFrames: segmentStart - loopStartInFrames,
360
+ durationInFrames
361
+ });
362
+ }
363
+ return segments;
364
+ };
365
+ var getLoopDisplayWidth = ({
366
+ visualizationWidth,
367
+ loopDisplay
368
+ }) => {
369
+ if (!shouldTileLoopDisplay(loopDisplay)) {
370
+ return visualizationWidth;
371
+ }
372
+ return visualizationWidth / loopDisplay.numberOfTimes;
373
+ };
374
+
392
375
  // src/audio-waveform/slice-waveform-peaks.ts
393
376
  var sliceWaveformPeaks = ({
394
377
  durationInFrames,
@@ -403,8 +386,10 @@ var sliceWaveformPeaks = ({
403
386
  }
404
387
  const startTimeInSeconds = startFrom / fps;
405
388
  const durationInSeconds = durationInFrames / fps * playbackRate;
406
- const startPeakIndex = Math.floor(startTimeInSeconds * waveformSampleRate);
407
- const endPeakIndex = Math.ceil((startTimeInSeconds + durationInSeconds) * waveformSampleRate);
389
+ const start = startTimeInSeconds * waveformSampleRate;
390
+ const end = (startTimeInSeconds + durationInSeconds) * waveformSampleRate;
391
+ const startPeakIndex = Math.floor(start + (Number.isFinite(start) ? Number.EPSILON * Math.max(1, Math.abs(start)) * 4 : 0));
392
+ const endPeakIndex = Math.ceil(end - (Number.isFinite(end) ? Number.EPSILON * Math.max(1, Math.abs(end)) * 4 : 0));
408
393
  if (!Number.isFinite(startPeakIndex) || !Number.isFinite(endPeakIndex)) {
409
394
  return peaks.subarray(Math.max(0, startPeakIndex), Math.min(peaks.length, endPeakIndex));
410
395
  }
@@ -432,7 +417,7 @@ var sliceVisibleWaveformPeaks = ({
432
417
  startFrom,
433
418
  waveformSampleRate
434
419
  }) => {
435
- if (!shouldTileLoopDisplay(loopDisplay) || loopDisplay.durationInFrames <= 0) {
420
+ if (!loopDisplay || loopDisplay.durationInFrames <= 0) {
436
421
  return sliceWaveformPeaks({
437
422
  durationInFrames: Math.min(displayDurationInFrames, Math.max(0, durationInFrames - displayOffsetInFrames)),
438
423
  fps,
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.526",
6
+ "version": "4.0.528",
7
7
  "description": "Internal utilities for rendering Remotion timelines",
8
8
  "main": "dist/index.js",
9
9
  "scripts": {
@@ -22,11 +22,11 @@
22
22
  },
23
23
  "dependencies": {
24
24
  "mediabunny": "1.56.1",
25
- "remotion": "4.0.526"
25
+ "remotion": "4.0.528"
26
26
  },
27
27
  "devDependencies": {
28
28
  "@mediabunny/server": "1.56.1",
29
- "@remotion/eslint-config-internal": "4.0.526",
29
+ "@remotion/eslint-config-internal": "4.0.528",
30
30
  "eslint": "9.19.0",
31
31
  "@typescript/native-preview": "7.0.0-dev.20260217.1"
32
32
  },
@@ -1,10 +0,0 @@
1
- type ImageWithNaturalDimensions = CanvasImageSource & {
2
- readonly naturalWidth: number;
3
- readonly naturalHeight: number;
4
- };
5
- export declare const drawRepeatingImageThumbnail: ({ canvas, image, offsetInPixels, }: {
6
- readonly canvas: HTMLCanvasElement | OffscreenCanvas;
7
- readonly image: ImageWithNaturalDimensions;
8
- readonly offsetInPixels?: number | undefined;
9
- }) => void;
10
- export {};
@@ -1,48 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.drawRepeatingImageThumbnail = void 0;
4
- const get_scaled_image_thumbnail_dimensions_1 = require("./get-scaled-image-thumbnail-dimensions");
5
- const createOffscreenCanvas = (width, height) => {
6
- if (typeof document !== 'undefined') {
7
- const canvas = document.createElement('canvas');
8
- canvas.width = width;
9
- canvas.height = height;
10
- return canvas;
11
- }
12
- if (typeof OffscreenCanvas !== 'undefined') {
13
- return new OffscreenCanvas(width, height);
14
- }
15
- throw new Error('No canvas implementation available');
16
- };
17
- const drawRepeatingImageThumbnail = ({ canvas, image, offsetInPixels = 0, }) => {
18
- const ctx = canvas.getContext('2d');
19
- if (!ctx) {
20
- throw new Error('Failed to get canvas context');
21
- }
22
- const { width, height } = canvas;
23
- if (width === 0 || height === 0) {
24
- return;
25
- }
26
- const { scaledWidth, scaledHeight } = (0, get_scaled_image_thumbnail_dimensions_1.getScaledImageThumbnailDimensions)({
27
- naturalWidth: image.naturalWidth,
28
- naturalHeight: image.naturalHeight,
29
- canvasHeight: height,
30
- });
31
- if (scaledWidth === 0 || scaledHeight === 0) {
32
- return;
33
- }
34
- const offscreen = createOffscreenCanvas(scaledWidth, scaledHeight);
35
- const offCtx = offscreen.getContext('2d');
36
- if (!offCtx) {
37
- throw new Error('Failed to get offscreen canvas context');
38
- }
39
- offCtx.drawImage(image, 0, 0, scaledWidth, scaledHeight);
40
- const pattern = ctx.createPattern(offscreen, 'repeat-x');
41
- if (!pattern) {
42
- return;
43
- }
44
- pattern.setTransform(new DOMMatrix().translate(-offsetInPixels, 0));
45
- ctx.fillStyle = pattern;
46
- ctx.fillRect(0, 0, width, height);
47
- };
48
- exports.drawRepeatingImageThumbnail = drawRepeatingImageThumbnail;
@@ -1,8 +0,0 @@
1
- export declare const getScaledImageThumbnailDimensions: ({ naturalWidth, naturalHeight, canvasHeight, }: {
2
- readonly naturalWidth: number;
3
- readonly naturalHeight: number;
4
- readonly canvasHeight: number;
5
- }) => {
6
- scaledWidth: number;
7
- scaledHeight: number;
8
- };
@@ -1,17 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.getScaledImageThumbnailDimensions = void 0;
4
- const getScaledImageThumbnailDimensions = ({ naturalWidth, naturalHeight, canvasHeight, }) => {
5
- if (naturalWidth === 0 || naturalHeight === 0 || canvasHeight === 0) {
6
- return {
7
- scaledWidth: 0,
8
- scaledHeight: 0,
9
- };
10
- }
11
- const scale = canvasHeight / naturalHeight;
12
- return {
13
- scaledWidth: naturalWidth * scale,
14
- scaledHeight: canvasHeight,
15
- };
16
- };
17
- exports.getScaledImageThumbnailDimensions = getScaledImageThumbnailDimensions;