@remotion/timeline-utils 4.0.485 → 4.0.487
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/esm/index.mjs +128 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +3 -1
- package/dist/render-frame-strip-to-canvas.d.ts +13 -0
- package/dist/render-frame-strip-to-canvas.js +126 -0
- package/package.json +4 -4
package/dist/esm/index.mjs
CHANGED
|
@@ -734,10 +734,138 @@ var resizeVideoFrame = ({
|
|
|
734
734
|
timestamp: frame.timestamp
|
|
735
735
|
});
|
|
736
736
|
};
|
|
737
|
+
|
|
738
|
+
// src/render-frame-strip-to-canvas.ts
|
|
739
|
+
var getContext = (canvas) => {
|
|
740
|
+
const ctx = canvas.getContext("2d");
|
|
741
|
+
if (!ctx) {
|
|
742
|
+
throw new Error("Could not get 2d context");
|
|
743
|
+
}
|
|
744
|
+
return ctx;
|
|
745
|
+
};
|
|
746
|
+
var renderFrameStripToCanvas = async ({
|
|
747
|
+
canvas,
|
|
748
|
+
src,
|
|
749
|
+
fromSeconds,
|
|
750
|
+
toSeconds,
|
|
751
|
+
width,
|
|
752
|
+
frameHeight,
|
|
753
|
+
devicePixelRatio,
|
|
754
|
+
signal
|
|
755
|
+
}) => {
|
|
756
|
+
const naturalWidth = Math.max(1, Math.ceil(width));
|
|
757
|
+
const naturalHeight = Math.max(1, Math.ceil(frameHeight));
|
|
758
|
+
const segmentDuration = toSeconds - fromSeconds;
|
|
759
|
+
canvas.width = naturalWidth;
|
|
760
|
+
canvas.height = naturalHeight;
|
|
761
|
+
if (segmentDuration <= 0 || signal?.aborted) {
|
|
762
|
+
return;
|
|
763
|
+
}
|
|
764
|
+
const ctx = getContext(canvas);
|
|
765
|
+
ctx.clearRect(0, 0, naturalWidth, naturalHeight);
|
|
766
|
+
const filledSlots = new Map;
|
|
767
|
+
let aspectRatio = getAspectRatioFromCache(src);
|
|
768
|
+
if (aspectRatio !== null) {
|
|
769
|
+
ensureSlots({
|
|
770
|
+
filledSlots,
|
|
771
|
+
naturalWidth,
|
|
772
|
+
fromSeconds,
|
|
773
|
+
toSeconds,
|
|
774
|
+
aspectRatio,
|
|
775
|
+
frameHeight
|
|
776
|
+
});
|
|
777
|
+
fillWithCachedFrames({
|
|
778
|
+
ctx,
|
|
779
|
+
naturalWidth,
|
|
780
|
+
filledSlots,
|
|
781
|
+
src,
|
|
782
|
+
segmentDuration,
|
|
783
|
+
fromSeconds,
|
|
784
|
+
devicePixelRatio,
|
|
785
|
+
frameHeight
|
|
786
|
+
});
|
|
787
|
+
const hasUnfilledSlots = Array.from(filledSlots.values()).some((value) => value === undefined);
|
|
788
|
+
if (!hasUnfilledSlots) {
|
|
789
|
+
return;
|
|
790
|
+
}
|
|
791
|
+
}
|
|
792
|
+
await extractFrames({
|
|
793
|
+
src,
|
|
794
|
+
signal,
|
|
795
|
+
timestampsInSeconds: ({ track }) => {
|
|
796
|
+
aspectRatio = track.width / track.height;
|
|
797
|
+
aspectRatioCache.set(src, aspectRatio);
|
|
798
|
+
ensureSlots({
|
|
799
|
+
filledSlots,
|
|
800
|
+
naturalWidth,
|
|
801
|
+
fromSeconds,
|
|
802
|
+
toSeconds,
|
|
803
|
+
aspectRatio,
|
|
804
|
+
frameHeight
|
|
805
|
+
});
|
|
806
|
+
return Array.from(filledSlots.keys()).map((timestamp) => timestamp / WEBCODECS_TIMESCALE);
|
|
807
|
+
},
|
|
808
|
+
onVideoSample: (sample) => {
|
|
809
|
+
let frame;
|
|
810
|
+
try {
|
|
811
|
+
frame = sample.toVideoFrame();
|
|
812
|
+
const scale = frameHeight / frame.displayHeight * devicePixelRatio;
|
|
813
|
+
const transformed = resizeVideoFrame({ frame, scale });
|
|
814
|
+
if (transformed !== frame) {
|
|
815
|
+
frame.close();
|
|
816
|
+
}
|
|
817
|
+
frame = undefined;
|
|
818
|
+
addFrameToCache(makeFrameDatabaseKey(src, transformed.timestamp), transformed);
|
|
819
|
+
if (aspectRatio === null) {
|
|
820
|
+
throw new Error("Aspect ratio is not set");
|
|
821
|
+
}
|
|
822
|
+
ensureSlots({
|
|
823
|
+
filledSlots,
|
|
824
|
+
naturalWidth,
|
|
825
|
+
fromSeconds,
|
|
826
|
+
toSeconds,
|
|
827
|
+
aspectRatio,
|
|
828
|
+
frameHeight
|
|
829
|
+
});
|
|
830
|
+
fillFrameWhereItFits({
|
|
831
|
+
ctx,
|
|
832
|
+
filledSlots,
|
|
833
|
+
visualizationWidth: naturalWidth,
|
|
834
|
+
frame: transformed,
|
|
835
|
+
segmentDuration,
|
|
836
|
+
fromSeconds,
|
|
837
|
+
devicePixelRatio,
|
|
838
|
+
frameHeight
|
|
839
|
+
});
|
|
840
|
+
} catch (e) {
|
|
841
|
+
if (frame) {
|
|
842
|
+
frame.close();
|
|
843
|
+
}
|
|
844
|
+
throw e;
|
|
845
|
+
} finally {
|
|
846
|
+
sample.close();
|
|
847
|
+
}
|
|
848
|
+
}
|
|
849
|
+
});
|
|
850
|
+
if (signal?.aborted) {
|
|
851
|
+
return;
|
|
852
|
+
}
|
|
853
|
+
fillWithCachedFrames({
|
|
854
|
+
ctx,
|
|
855
|
+
naturalWidth,
|
|
856
|
+
filledSlots,
|
|
857
|
+
src,
|
|
858
|
+
segmentDuration,
|
|
859
|
+
fromSeconds,
|
|
860
|
+
devicePixelRatio,
|
|
861
|
+
frameHeight
|
|
862
|
+
});
|
|
863
|
+
};
|
|
737
864
|
export {
|
|
738
865
|
sliceWaveformPeaks,
|
|
739
866
|
shouldTileLoopDisplay,
|
|
740
867
|
resizeVideoFrame,
|
|
868
|
+
renderFrameStripToCanvas,
|
|
741
869
|
makeFrameDatabaseKey,
|
|
742
870
|
makeAudioWaveformWorker,
|
|
743
871
|
loadWaveformPeaks,
|
package/dist/index.d.ts
CHANGED
|
@@ -14,4 +14,6 @@ export type { FrameDatabaseKey } from './frame-database';
|
|
|
14
14
|
export { getLoopDisplayWidth, shouldTileLoopDisplay } from './loop-display';
|
|
15
15
|
export type { TimelineLoopDisplay } from './loop-display';
|
|
16
16
|
export { calculateTimestampSlots, drawSlot, ensureSlots, fillFrameWhereItFits, fillWithCachedFrames, getDurationOfOneFrame, WEBCODECS_TIMESCALE, } from './render-frame-strip';
|
|
17
|
+
export { renderFrameStripToCanvas } from './render-frame-strip-to-canvas';
|
|
18
|
+
export type { RenderFrameStripToCanvasOptions } from './render-frame-strip-to-canvas';
|
|
17
19
|
export { resizeVideoFrame } from './resize-video-frame';
|
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.WEBCODECS_TIMESCALE = exports.getDurationOfOneFrame = exports.fillWithCachedFrames = exports.fillFrameWhereItFits = exports.ensureSlots = exports.drawSlot = exports.calculateTimestampSlots = exports.shouldTileLoopDisplay = exports.getLoopDisplayWidth = exports.makeFrameDatabaseKey = exports.getTimestampFromFrameDatabaseKey = exports.getFrameDatabaseKeyPrefix = exports.getAspectRatioFromCache = exports.frameDatabase = exports.aspectRatioCache = exports.addFrameToCache = exports.extractFrames = exports.getScaledImageThumbnailDimensions = exports.drawRepeatingImageThumbnail = exports.emitWaveformProgress = exports.createWaveformPeakProcessor = exports.sliceWaveformPeaks = exports.makeAudioWaveformWorker = exports.loadWaveformPeaks = exports.drawBars = exports.TARGET_SAMPLE_RATE = void 0;
|
|
3
|
+
exports.resizeVideoFrame = exports.renderFrameStripToCanvas = exports.WEBCODECS_TIMESCALE = exports.getDurationOfOneFrame = exports.fillWithCachedFrames = exports.fillFrameWhereItFits = exports.ensureSlots = exports.drawSlot = exports.calculateTimestampSlots = exports.shouldTileLoopDisplay = exports.getLoopDisplayWidth = exports.makeFrameDatabaseKey = exports.getTimestampFromFrameDatabaseKey = exports.getFrameDatabaseKeyPrefix = exports.getAspectRatioFromCache = exports.frameDatabase = exports.aspectRatioCache = exports.addFrameToCache = exports.extractFrames = exports.getScaledImageThumbnailDimensions = exports.drawRepeatingImageThumbnail = exports.emitWaveformProgress = exports.createWaveformPeakProcessor = exports.sliceWaveformPeaks = exports.makeAudioWaveformWorker = exports.loadWaveformPeaks = 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");
|
|
@@ -39,5 +39,7 @@ Object.defineProperty(exports, "fillFrameWhereItFits", { enumerable: true, get:
|
|
|
39
39
|
Object.defineProperty(exports, "fillWithCachedFrames", { enumerable: true, get: function () { return render_frame_strip_1.fillWithCachedFrames; } });
|
|
40
40
|
Object.defineProperty(exports, "getDurationOfOneFrame", { enumerable: true, get: function () { return render_frame_strip_1.getDurationOfOneFrame; } });
|
|
41
41
|
Object.defineProperty(exports, "WEBCODECS_TIMESCALE", { enumerable: true, get: function () { return render_frame_strip_1.WEBCODECS_TIMESCALE; } });
|
|
42
|
+
const render_frame_strip_to_canvas_1 = require("./render-frame-strip-to-canvas");
|
|
43
|
+
Object.defineProperty(exports, "renderFrameStripToCanvas", { enumerable: true, get: function () { return render_frame_strip_to_canvas_1.renderFrameStripToCanvas; } });
|
|
42
44
|
const resize_video_frame_1 = require("./resize-video-frame");
|
|
43
45
|
Object.defineProperty(exports, "resizeVideoFrame", { enumerable: true, get: function () { return resize_video_frame_1.resizeVideoFrame; } });
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
type Canvas = HTMLCanvasElement | OffscreenCanvas;
|
|
2
|
+
export type RenderFrameStripToCanvasOptions = {
|
|
3
|
+
readonly canvas: Canvas;
|
|
4
|
+
readonly src: string;
|
|
5
|
+
readonly fromSeconds: number;
|
|
6
|
+
readonly toSeconds: number;
|
|
7
|
+
readonly width: number;
|
|
8
|
+
readonly frameHeight: number;
|
|
9
|
+
readonly devicePixelRatio: number;
|
|
10
|
+
readonly signal?: AbortSignal;
|
|
11
|
+
};
|
|
12
|
+
export declare const renderFrameStripToCanvas: ({ canvas, src, fromSeconds, toSeconds, width, frameHeight, devicePixelRatio, signal, }: RenderFrameStripToCanvasOptions) => Promise<void>;
|
|
13
|
+
export {};
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.renderFrameStripToCanvas = void 0;
|
|
4
|
+
const extract_frames_1 = require("./extract-frames");
|
|
5
|
+
const frame_database_1 = require("./frame-database");
|
|
6
|
+
const render_frame_strip_1 = require("./render-frame-strip");
|
|
7
|
+
const resize_video_frame_1 = require("./resize-video-frame");
|
|
8
|
+
const getContext = (canvas) => {
|
|
9
|
+
const ctx = canvas.getContext('2d');
|
|
10
|
+
if (!ctx) {
|
|
11
|
+
throw new Error('Could not get 2d context');
|
|
12
|
+
}
|
|
13
|
+
return ctx;
|
|
14
|
+
};
|
|
15
|
+
const renderFrameStripToCanvas = async ({ canvas, src, fromSeconds, toSeconds, width, frameHeight, devicePixelRatio, signal, }) => {
|
|
16
|
+
const naturalWidth = Math.max(1, Math.ceil(width));
|
|
17
|
+
const naturalHeight = Math.max(1, Math.ceil(frameHeight));
|
|
18
|
+
const segmentDuration = toSeconds - fromSeconds;
|
|
19
|
+
canvas.width = naturalWidth;
|
|
20
|
+
canvas.height = naturalHeight;
|
|
21
|
+
if (segmentDuration <= 0 || (signal === null || signal === void 0 ? void 0 : signal.aborted)) {
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
const ctx = getContext(canvas);
|
|
25
|
+
ctx.clearRect(0, 0, naturalWidth, naturalHeight);
|
|
26
|
+
const filledSlots = new Map();
|
|
27
|
+
let aspectRatio = (0, frame_database_1.getAspectRatioFromCache)(src);
|
|
28
|
+
if (aspectRatio !== null) {
|
|
29
|
+
(0, render_frame_strip_1.ensureSlots)({
|
|
30
|
+
filledSlots,
|
|
31
|
+
naturalWidth,
|
|
32
|
+
fromSeconds,
|
|
33
|
+
toSeconds,
|
|
34
|
+
aspectRatio,
|
|
35
|
+
frameHeight,
|
|
36
|
+
});
|
|
37
|
+
(0, render_frame_strip_1.fillWithCachedFrames)({
|
|
38
|
+
ctx,
|
|
39
|
+
naturalWidth,
|
|
40
|
+
filledSlots,
|
|
41
|
+
src,
|
|
42
|
+
segmentDuration,
|
|
43
|
+
fromSeconds,
|
|
44
|
+
devicePixelRatio,
|
|
45
|
+
frameHeight,
|
|
46
|
+
});
|
|
47
|
+
const hasUnfilledSlots = Array.from(filledSlots.values()).some((value) => value === undefined);
|
|
48
|
+
if (!hasUnfilledSlots) {
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
await (0, extract_frames_1.extractFrames)({
|
|
53
|
+
src,
|
|
54
|
+
signal,
|
|
55
|
+
timestampsInSeconds: ({ track }) => {
|
|
56
|
+
aspectRatio = track.width / track.height;
|
|
57
|
+
frame_database_1.aspectRatioCache.set(src, aspectRatio);
|
|
58
|
+
(0, render_frame_strip_1.ensureSlots)({
|
|
59
|
+
filledSlots,
|
|
60
|
+
naturalWidth,
|
|
61
|
+
fromSeconds,
|
|
62
|
+
toSeconds,
|
|
63
|
+
aspectRatio,
|
|
64
|
+
frameHeight,
|
|
65
|
+
});
|
|
66
|
+
return Array.from(filledSlots.keys()).map((timestamp) => timestamp / render_frame_strip_1.WEBCODECS_TIMESCALE);
|
|
67
|
+
},
|
|
68
|
+
onVideoSample: (sample) => {
|
|
69
|
+
let frame;
|
|
70
|
+
try {
|
|
71
|
+
frame = sample.toVideoFrame();
|
|
72
|
+
const scale = (frameHeight / frame.displayHeight) * devicePixelRatio;
|
|
73
|
+
const transformed = (0, resize_video_frame_1.resizeVideoFrame)({ frame, scale });
|
|
74
|
+
if (transformed !== frame) {
|
|
75
|
+
frame.close();
|
|
76
|
+
}
|
|
77
|
+
frame = undefined;
|
|
78
|
+
(0, frame_database_1.addFrameToCache)((0, frame_database_1.makeFrameDatabaseKey)(src, transformed.timestamp), transformed);
|
|
79
|
+
if (aspectRatio === null) {
|
|
80
|
+
throw new Error('Aspect ratio is not set');
|
|
81
|
+
}
|
|
82
|
+
(0, render_frame_strip_1.ensureSlots)({
|
|
83
|
+
filledSlots,
|
|
84
|
+
naturalWidth,
|
|
85
|
+
fromSeconds,
|
|
86
|
+
toSeconds,
|
|
87
|
+
aspectRatio,
|
|
88
|
+
frameHeight,
|
|
89
|
+
});
|
|
90
|
+
(0, render_frame_strip_1.fillFrameWhereItFits)({
|
|
91
|
+
ctx,
|
|
92
|
+
filledSlots,
|
|
93
|
+
visualizationWidth: naturalWidth,
|
|
94
|
+
frame: transformed,
|
|
95
|
+
segmentDuration,
|
|
96
|
+
fromSeconds,
|
|
97
|
+
devicePixelRatio,
|
|
98
|
+
frameHeight,
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
catch (e) {
|
|
102
|
+
if (frame) {
|
|
103
|
+
frame.close();
|
|
104
|
+
}
|
|
105
|
+
throw e;
|
|
106
|
+
}
|
|
107
|
+
finally {
|
|
108
|
+
sample.close();
|
|
109
|
+
}
|
|
110
|
+
},
|
|
111
|
+
});
|
|
112
|
+
if (signal === null || signal === void 0 ? void 0 : signal.aborted) {
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
(0, render_frame_strip_1.fillWithCachedFrames)({
|
|
116
|
+
ctx,
|
|
117
|
+
naturalWidth,
|
|
118
|
+
filledSlots,
|
|
119
|
+
src,
|
|
120
|
+
segmentDuration,
|
|
121
|
+
fromSeconds,
|
|
122
|
+
devicePixelRatio,
|
|
123
|
+
frameHeight,
|
|
124
|
+
});
|
|
125
|
+
};
|
|
126
|
+
exports.renderFrameStripToCanvas = renderFrameStripToCanvas;
|
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.487",
|
|
7
7
|
"description": "Internal utilities for rendering Remotion timelines",
|
|
8
8
|
"main": "dist/index.js",
|
|
9
9
|
"scripts": {
|
|
@@ -21,11 +21,11 @@
|
|
|
21
21
|
"url": "https://github.com/remotion-dev/remotion/issues"
|
|
22
22
|
},
|
|
23
23
|
"dependencies": {
|
|
24
|
-
"mediabunny": "1.50.
|
|
24
|
+
"mediabunny": "1.50.7"
|
|
25
25
|
},
|
|
26
26
|
"devDependencies": {
|
|
27
|
-
"@mediabunny/server": "1.50.
|
|
28
|
-
"@remotion/eslint-config-internal": "4.0.
|
|
27
|
+
"@mediabunny/server": "1.50.7",
|
|
28
|
+
"@remotion/eslint-config-internal": "4.0.487",
|
|
29
29
|
"eslint": "9.19.0",
|
|
30
30
|
"@typescript/native-preview": "7.0.0-dev.20260217.1"
|
|
31
31
|
},
|