@remotion/webcodecs 4.0.241 → 4.0.242
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-decoder.js +0 -5
- package/dist/can-copy-video-track.d.ts +5 -4
- package/dist/can-copy-video-track.js +19 -6
- package/dist/convert-encoded-chunk.js +2 -0
- package/dist/convert-media.d.ts +3 -1
- package/dist/convert-media.js +2 -1
- package/dist/default-on-video-track-handler.js +3 -2
- package/dist/esm/index.mjs +198 -43
- package/dist/index.d.ts +11 -1
- package/dist/index.js +5 -3
- package/dist/io-manager/io-synchronizer.js +4 -4
- package/dist/io-manager/make-timeout-promise.d.ts +1 -1
- package/dist/io-manager/make-timeout-promise.js +1 -1
- package/dist/on-frame.d.ts +3 -1
- package/dist/on-frame.js +5 -3
- package/dist/on-video-track-handler.d.ts +3 -0
- package/dist/on-video-track.d.ts +3 -1
- package/dist/on-video-track.js +10 -5
- package/dist/resizing/calculate-new-size.d.ts +8 -0
- package/dist/resizing/calculate-new-size.js +89 -0
- package/dist/resizing/mode.d.ts +24 -0
- package/dist/resizing/mode.js +2 -0
- package/dist/resizing/resizing.d.ts +13 -0
- package/dist/resizing/resizing.js +2 -0
- package/dist/resizing.d.ts +13 -0
- package/dist/resizing.js +2 -0
- package/dist/rotate-and-resize-video-frame.d.ts +9 -0
- package/dist/rotate-and-resize-video-frame.js +67 -0
- package/dist/rotate-video-frame.d.ts +5 -1
- package/dist/rotate-video-frame.js +9 -4
- package/dist/rotation.d.ts +11 -3
- package/dist/rotation.js +24 -5
- package/dist/scaling.d.ts +13 -0
- package/dist/scaling.js +2 -0
- package/package.json +5 -5
package/dist/audio-decoder.js
CHANGED
|
@@ -78,11 +78,6 @@ const createAudioDecoder = ({ onFrame, onError, signal, config, logLevel, track,
|
|
|
78
78
|
let queue = Promise.resolve();
|
|
79
79
|
return {
|
|
80
80
|
processSample: (sample) => {
|
|
81
|
-
// In example.avi, we have samples with 0 data
|
|
82
|
-
// Chrome fails on these
|
|
83
|
-
if (sample.data.length === 0) {
|
|
84
|
-
return queue;
|
|
85
|
-
}
|
|
86
81
|
queue = queue.then(() => processSample(sample));
|
|
87
82
|
return queue;
|
|
88
83
|
},
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { ParseMediaContainer, VideoTrack } from '@remotion/media-parser';
|
|
2
2
|
import type { ConvertMediaContainer } from './get-available-containers';
|
|
3
|
-
|
|
3
|
+
import type { ResizeOperation } from './resizing/mode';
|
|
4
|
+
export declare const canCopyVideoTrack: ({ outputContainer, rotationToApply, inputContainer, resizeOperation, inputTrack, }: {
|
|
4
5
|
inputContainer: ParseMediaContainer;
|
|
5
|
-
|
|
6
|
-
inputRotation: number;
|
|
6
|
+
inputTrack: VideoTrack;
|
|
7
7
|
rotationToApply: number;
|
|
8
8
|
outputContainer: ConvertMediaContainer;
|
|
9
|
+
resizeOperation: ResizeOperation | null;
|
|
9
10
|
}) => boolean;
|
|
@@ -1,17 +1,30 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.canCopyVideoTrack = void 0;
|
|
4
|
-
const
|
|
5
|
-
const
|
|
6
|
-
|
|
7
|
-
|
|
4
|
+
const rotate_and_resize_video_frame_1 = require("./rotate-and-resize-video-frame");
|
|
5
|
+
const rotation_1 = require("./rotation");
|
|
6
|
+
const canCopyVideoTrack = ({ outputContainer, rotationToApply, inputContainer, resizeOperation, inputTrack, }) => {
|
|
7
|
+
if ((0, rotate_and_resize_video_frame_1.normalizeVideoRotation)(inputTrack.rotation) !==
|
|
8
|
+
(0, rotate_and_resize_video_frame_1.normalizeVideoRotation)(rotationToApply)) {
|
|
9
|
+
return false;
|
|
10
|
+
}
|
|
11
|
+
const newDimensions = (0, rotation_1.calculateNewDimensionsFromRotateAndScale)({
|
|
12
|
+
height: inputTrack.height,
|
|
13
|
+
resizeOperation,
|
|
14
|
+
rotation: rotationToApply,
|
|
15
|
+
videoCodec: inputTrack.codecWithoutConfig,
|
|
16
|
+
width: inputTrack.width,
|
|
17
|
+
});
|
|
18
|
+
if (newDimensions.height !== inputTrack.height ||
|
|
19
|
+
newDimensions.width !== inputTrack.width) {
|
|
8
20
|
return false;
|
|
9
21
|
}
|
|
10
22
|
if (outputContainer === 'webm') {
|
|
11
|
-
return
|
|
23
|
+
return (inputTrack.codecWithoutConfig === 'vp8' ||
|
|
24
|
+
inputTrack.codecWithoutConfig === 'vp9');
|
|
12
25
|
}
|
|
13
26
|
if (outputContainer === 'mp4') {
|
|
14
|
-
return (
|
|
27
|
+
return (inputTrack.codecWithoutConfig === 'h264' &&
|
|
15
28
|
(inputContainer === 'mp4' || inputContainer === 'avi'));
|
|
16
29
|
}
|
|
17
30
|
if (outputContainer === 'wav') {
|
package/dist/convert-media.d.ts
CHANGED
|
@@ -8,6 +8,7 @@ import type { ConvertMediaContainer } from './get-available-containers';
|
|
|
8
8
|
import type { ConvertMediaVideoCodec } from './get-available-video-codecs';
|
|
9
9
|
import { type ConvertMediaOnAudioTrackHandler } from './on-audio-track-handler';
|
|
10
10
|
import { type ConvertMediaOnVideoTrackHandler } from './on-video-track-handler';
|
|
11
|
+
import type { ResizeOperation } from './resizing/mode';
|
|
11
12
|
export type ConvertMediaProgress = {
|
|
12
13
|
decodedVideoFrames: number;
|
|
13
14
|
decodedAudioFrames: number;
|
|
@@ -28,7 +29,7 @@ export type ConvertMediaOnVideoFrame = (options: {
|
|
|
28
29
|
frame: VideoFrame;
|
|
29
30
|
track: VideoTrack;
|
|
30
31
|
}) => Promise<VideoFrame> | VideoFrame;
|
|
31
|
-
export declare const convertMedia: <F extends Options<ParseMediaFields>>({ src, onVideoFrame, onProgress: onProgressDoNotCallDirectly, audioCodec, container, videoCodec, signal: userPassedAbortSignal, onAudioTrack: userAudioResolver, onVideoTrack: userVideoResolver, reader, fields, logLevel, writer, progressIntervalInMs, rotate, apiKey, ...more }: {
|
|
32
|
+
export declare const convertMedia: <F extends Options<ParseMediaFields>>({ src, onVideoFrame, onProgress: onProgressDoNotCallDirectly, audioCodec, container, videoCodec, signal: userPassedAbortSignal, onAudioTrack: userAudioResolver, onVideoTrack: userVideoResolver, reader, fields, logLevel, writer, progressIntervalInMs, rotate, apiKey, resize, ...more }: {
|
|
32
33
|
src: ParseMediaOptions<F>["src"];
|
|
33
34
|
container: ConvertMediaContainer;
|
|
34
35
|
onVideoFrame?: ConvertMediaOnVideoFrame;
|
|
@@ -43,5 +44,6 @@ export declare const convertMedia: <F extends Options<ParseMediaFields>>({ src,
|
|
|
43
44
|
writer?: WriterInterface;
|
|
44
45
|
progressIntervalInMs?: number;
|
|
45
46
|
rotate?: number;
|
|
47
|
+
resize?: ResizeOperation;
|
|
46
48
|
apiKey?: string | null;
|
|
47
49
|
} & ParseMediaDynamicOptions<F>) => Promise<ConvertMediaResult>;
|
package/dist/convert-media.js
CHANGED
|
@@ -19,7 +19,7 @@ const on_video_track_1 = require("./on-video-track");
|
|
|
19
19
|
const select_container_creator_1 = require("./select-container-creator");
|
|
20
20
|
const send_telemetry_event_1 = require("./send-telemetry-event");
|
|
21
21
|
const throttled_state_update_1 = require("./throttled-state-update");
|
|
22
|
-
const convertMedia = async function ({ src, onVideoFrame, onProgress: onProgressDoNotCallDirectly, audioCodec, container, videoCodec, signal: userPassedAbortSignal, onAudioTrack: userAudioResolver, onVideoTrack: userVideoResolver, reader, fields, logLevel = 'info', writer, progressIntervalInMs, rotate, apiKey, ...more }) {
|
|
22
|
+
const convertMedia = async function ({ src, onVideoFrame, onProgress: onProgressDoNotCallDirectly, audioCodec, container, videoCodec, signal: userPassedAbortSignal, onAudioTrack: userAudioResolver, onVideoTrack: userVideoResolver, reader, fields, logLevel = 'info', writer, progressIntervalInMs, rotate, apiKey, resize, ...more }) {
|
|
23
23
|
var _a, _b;
|
|
24
24
|
if (userPassedAbortSignal === null || userPassedAbortSignal === void 0 ? void 0 : userPassedAbortSignal.aborted) {
|
|
25
25
|
return Promise.reject(new error_cause_1.default('Aborted'));
|
|
@@ -92,6 +92,7 @@ const convertMedia = async function ({ src, onVideoFrame, onProgress: onProgress
|
|
|
92
92
|
outputContainer: container,
|
|
93
93
|
rotate: rotate !== null && rotate !== void 0 ? rotate : 0,
|
|
94
94
|
progress: progressTracker,
|
|
95
|
+
resizeOperation: resize !== null && resize !== void 0 ? resize : null,
|
|
95
96
|
});
|
|
96
97
|
const onAudioTrack = (0, on_audio_track_1.makeAudioTrackHandler)({
|
|
97
98
|
abortConversion,
|
|
@@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.defaultOnVideoTrackHandler = void 0;
|
|
4
4
|
const media_parser_1 = require("@remotion/media-parser");
|
|
5
5
|
const can_reencode_video_track_1 = require("./can-reencode-video-track");
|
|
6
|
-
const defaultOnVideoTrackHandler = async ({ track, defaultVideoCodec, logLevel, rotate, canCopyTrack, }) => {
|
|
6
|
+
const defaultOnVideoTrackHandler = async ({ track, defaultVideoCodec, logLevel, rotate, canCopyTrack, resizeOperation, }) => {
|
|
7
7
|
if (canCopyTrack) {
|
|
8
8
|
media_parser_1.MediaParserInternals.Log.verbose(logLevel, `Track ${track.trackId} (video): Can copy, therefore copying`);
|
|
9
9
|
return Promise.resolve({ type: 'copy' });
|
|
@@ -21,7 +21,8 @@ const defaultOnVideoTrackHandler = async ({ track, defaultVideoCodec, logLevel,
|
|
|
21
21
|
return Promise.resolve({
|
|
22
22
|
type: 'reencode',
|
|
23
23
|
videoCodec: defaultVideoCodec,
|
|
24
|
-
|
|
24
|
+
rotate: rotate - track.rotation,
|
|
25
|
+
resize: resizeOperation,
|
|
25
26
|
});
|
|
26
27
|
}
|
|
27
28
|
media_parser_1.MediaParserInternals.Log.verbose(logLevel, `Track ${track.trackId} (video): Can neither copy nor re-encode, therefore failing`);
|
package/dist/esm/index.mjs
CHANGED
|
@@ -92,38 +92,161 @@ var require_dist = __commonJS((exports) => {
|
|
|
92
92
|
} });
|
|
93
93
|
});
|
|
94
94
|
|
|
95
|
+
// src/resizing/calculate-new-size.ts
|
|
96
|
+
var ensureMultipleOfTwo = ({
|
|
97
|
+
dimensions,
|
|
98
|
+
needsToBeMultipleOfTwo
|
|
99
|
+
}) => {
|
|
100
|
+
if (!needsToBeMultipleOfTwo) {
|
|
101
|
+
return dimensions;
|
|
102
|
+
}
|
|
103
|
+
return {
|
|
104
|
+
width: Math.floor(dimensions.width / 2) * 2,
|
|
105
|
+
height: Math.floor(dimensions.height / 2) * 2
|
|
106
|
+
};
|
|
107
|
+
};
|
|
108
|
+
var calculateNewSizeAfterResizing = ({
|
|
109
|
+
dimensions,
|
|
110
|
+
resizeOperation,
|
|
111
|
+
videoCodec
|
|
112
|
+
}) => {
|
|
113
|
+
const needsToBeMultipleOfTwo = videoCodec === "h264";
|
|
114
|
+
if (resizeOperation === null) {
|
|
115
|
+
return ensureMultipleOfTwo({
|
|
116
|
+
dimensions,
|
|
117
|
+
needsToBeMultipleOfTwo
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
if (resizeOperation.mode === "width") {
|
|
121
|
+
return ensureMultipleOfTwo({
|
|
122
|
+
dimensions: {
|
|
123
|
+
width: resizeOperation.width,
|
|
124
|
+
height: Math.round(resizeOperation.width / dimensions.width * dimensions.height)
|
|
125
|
+
},
|
|
126
|
+
needsToBeMultipleOfTwo
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
if (resizeOperation.mode === "height") {
|
|
130
|
+
return ensureMultipleOfTwo({
|
|
131
|
+
dimensions: {
|
|
132
|
+
width: Math.round(resizeOperation.height / dimensions.height * dimensions.width),
|
|
133
|
+
height: resizeOperation.height
|
|
134
|
+
},
|
|
135
|
+
needsToBeMultipleOfTwo
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
if (resizeOperation.mode === "max-height") {
|
|
139
|
+
const height = Math.min(dimensions.height, resizeOperation.maxHeight);
|
|
140
|
+
return ensureMultipleOfTwo({
|
|
141
|
+
dimensions: {
|
|
142
|
+
width: Math.round(height / dimensions.height * dimensions.width),
|
|
143
|
+
height
|
|
144
|
+
},
|
|
145
|
+
needsToBeMultipleOfTwo
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
if (resizeOperation.mode === "max-width") {
|
|
149
|
+
const width = Math.min(dimensions.width, resizeOperation.maxWidth);
|
|
150
|
+
return ensureMultipleOfTwo({
|
|
151
|
+
dimensions: {
|
|
152
|
+
width,
|
|
153
|
+
height: Math.round(width / dimensions.width * dimensions.height)
|
|
154
|
+
},
|
|
155
|
+
needsToBeMultipleOfTwo
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
if (resizeOperation.mode === "max-height-width") {
|
|
159
|
+
const height = Math.min(dimensions.height, resizeOperation.maxHeight);
|
|
160
|
+
const width = Math.min(dimensions.width, resizeOperation.maxWidth);
|
|
161
|
+
const scale = Math.min(width / dimensions.width, height / dimensions.height);
|
|
162
|
+
const actualWidth = Math.round(dimensions.width * scale);
|
|
163
|
+
const actualHeight = Math.round(dimensions.height * scale);
|
|
164
|
+
return ensureMultipleOfTwo({
|
|
165
|
+
dimensions: {
|
|
166
|
+
height: actualHeight,
|
|
167
|
+
width: actualWidth
|
|
168
|
+
},
|
|
169
|
+
needsToBeMultipleOfTwo
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
if (resizeOperation.mode === "scale") {
|
|
173
|
+
if (resizeOperation.scale <= 0) {
|
|
174
|
+
throw new Error("Scale must be greater than 0");
|
|
175
|
+
}
|
|
176
|
+
const width = Math.round(dimensions.width * resizeOperation.scale);
|
|
177
|
+
const height = Math.round(dimensions.height * resizeOperation.scale);
|
|
178
|
+
return ensureMultipleOfTwo({
|
|
179
|
+
dimensions: {
|
|
180
|
+
width,
|
|
181
|
+
height
|
|
182
|
+
},
|
|
183
|
+
needsToBeMultipleOfTwo
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
throw new Error("Invalid resizing mode " + resizeOperation);
|
|
187
|
+
};
|
|
188
|
+
|
|
95
189
|
// src/rotation.ts
|
|
96
|
-
var
|
|
97
|
-
width,
|
|
190
|
+
var calculateNewDimensionsFromRotate = ({
|
|
98
191
|
height,
|
|
192
|
+
width,
|
|
99
193
|
rotation
|
|
100
194
|
}) => {
|
|
101
|
-
const
|
|
195
|
+
const normalized = normalizeVideoRotation(rotation);
|
|
196
|
+
const switchDimensions = normalized % 90 === 0 && normalized % 180 !== 0;
|
|
102
197
|
const newHeight = switchDimensions ? width : height;
|
|
103
198
|
const newWidth = switchDimensions ? height : width;
|
|
104
|
-
return {
|
|
199
|
+
return {
|
|
200
|
+
height: newHeight,
|
|
201
|
+
width: newWidth
|
|
202
|
+
};
|
|
203
|
+
};
|
|
204
|
+
var calculateNewDimensionsFromRotateAndScale = ({
|
|
205
|
+
width,
|
|
206
|
+
height,
|
|
207
|
+
rotation,
|
|
208
|
+
resizeOperation,
|
|
209
|
+
videoCodec
|
|
210
|
+
}) => {
|
|
211
|
+
const { height: newHeight, width: newWidth } = calculateNewDimensionsFromRotate({
|
|
212
|
+
height,
|
|
213
|
+
rotation,
|
|
214
|
+
width
|
|
215
|
+
});
|
|
216
|
+
return calculateNewSizeAfterResizing({
|
|
217
|
+
dimensions: { height: newHeight, width: newWidth },
|
|
218
|
+
resizeOperation,
|
|
219
|
+
videoCodec
|
|
220
|
+
});
|
|
105
221
|
};
|
|
106
222
|
|
|
107
|
-
// src/rotate-video-frame.ts
|
|
223
|
+
// src/rotate-and-resize-video-frame.ts
|
|
108
224
|
var normalizeVideoRotation = (rotation) => {
|
|
109
225
|
return (rotation % 360 + 360) % 360;
|
|
110
226
|
};
|
|
111
|
-
var
|
|
227
|
+
var rotateAndResizeVideoFrame = ({
|
|
112
228
|
frame,
|
|
113
|
-
rotation
|
|
229
|
+
rotation,
|
|
230
|
+
videoCodec,
|
|
231
|
+
resizeOperation
|
|
114
232
|
}) => {
|
|
115
233
|
const normalized = (rotation % 360 + 360) % 360;
|
|
116
|
-
if (normalized
|
|
234
|
+
if (normalized === 0 && resizeOperation === null) {
|
|
117
235
|
return frame;
|
|
118
236
|
}
|
|
119
237
|
if (normalized % 90 !== 0) {
|
|
120
238
|
throw new Error("Only 90 degree rotations are supported");
|
|
121
239
|
}
|
|
122
|
-
const { height, width } =
|
|
240
|
+
const { height, width } = calculateNewDimensionsFromRotateAndScale({
|
|
123
241
|
height: frame.displayHeight,
|
|
124
242
|
width: frame.displayWidth,
|
|
125
|
-
rotation
|
|
243
|
+
rotation,
|
|
244
|
+
videoCodec,
|
|
245
|
+
resizeOperation
|
|
126
246
|
});
|
|
247
|
+
if (normalized === 0 && height === frame.displayHeight && width === frame.displayWidth) {
|
|
248
|
+
return frame;
|
|
249
|
+
}
|
|
127
250
|
const canvas = new OffscreenCanvas(width, height);
|
|
128
251
|
const ctx = canvas.getContext("2d");
|
|
129
252
|
if (!ctx) {
|
|
@@ -138,7 +261,17 @@ var rotateVideoFrame = ({
|
|
|
138
261
|
} else if (normalized === 270) {
|
|
139
262
|
ctx.translate(0, height);
|
|
140
263
|
}
|
|
141
|
-
|
|
264
|
+
if (normalized !== 0) {
|
|
265
|
+
ctx.rotate(normalized * (Math.PI / 180));
|
|
266
|
+
}
|
|
267
|
+
if (frame.displayHeight !== height || frame.displayWidth !== width) {
|
|
268
|
+
const dimensionsAfterRotate = calculateNewDimensionsFromRotate({
|
|
269
|
+
height: frame.displayHeight,
|
|
270
|
+
rotation,
|
|
271
|
+
width: frame.displayWidth
|
|
272
|
+
});
|
|
273
|
+
ctx.scale(width / dimensionsAfterRotate.width, height / dimensionsAfterRotate.height);
|
|
274
|
+
}
|
|
142
275
|
ctx.drawImage(frame, 0, 0);
|
|
143
276
|
return new VideoFrame(canvas, {
|
|
144
277
|
displayHeight: height,
|
|
@@ -204,7 +337,7 @@ import { MediaParserInternals as MediaParserInternals2 } from "@remotion/media-p
|
|
|
204
337
|
var makeTimeoutPromise = (label, ms) => {
|
|
205
338
|
const { promise, reject, resolve } = MediaParserInternals2.withResolvers();
|
|
206
339
|
const timeout = setTimeout(() => {
|
|
207
|
-
reject(new Error(`${label} (timed out after ${ms}ms)`));
|
|
340
|
+
reject(new Error(`${label()} (timed out after ${ms}ms)`));
|
|
208
341
|
}, ms);
|
|
209
342
|
return {
|
|
210
343
|
timeoutPromise: promise,
|
|
@@ -288,14 +421,14 @@ var makeIoSynchronizer = ({
|
|
|
288
421
|
minimumProgress,
|
|
289
422
|
signal
|
|
290
423
|
}) => {
|
|
291
|
-
const { timeoutPromise, clear } = makeTimeoutPromise([
|
|
292
|
-
`Waited too long for ${label}:`,
|
|
424
|
+
const { timeoutPromise, clear } = makeTimeoutPromise(() => [
|
|
425
|
+
`Waited too long for ${label} to finish:`,
|
|
293
426
|
`${getUnemittedItems()} unemitted items`,
|
|
294
|
-
`${getUnprocessed()} unprocessed items`,
|
|
295
|
-
`minimum progress ${minimumProgress}`,
|
|
427
|
+
`${getUnprocessed()} unprocessed items: ${JSON.stringify(_unprocessed)}`,
|
|
296
428
|
`smallest progress: ${progress.getSmallestProgress()}`,
|
|
297
429
|
`inputs: ${JSON.stringify(inputs)}`,
|
|
298
|
-
`last output: ${lastOutput}
|
|
430
|
+
`last output: ${lastOutput}`,
|
|
431
|
+
`wanted: ${unemitted} unemitted items, ${unprocessed} unprocessed items, minimum progress ${minimumProgress}`
|
|
299
432
|
].join(`
|
|
300
433
|
`), 1e4);
|
|
301
434
|
signal.addEventListener("abort", clear);
|
|
@@ -420,9 +553,6 @@ var createAudioDecoder = ({
|
|
|
420
553
|
let queue = Promise.resolve();
|
|
421
554
|
return {
|
|
422
555
|
processSample: (sample) => {
|
|
423
|
-
if (sample.data.length === 0) {
|
|
424
|
-
return queue;
|
|
425
|
-
}
|
|
426
556
|
queue = queue.then(() => processSample(sample));
|
|
427
557
|
return queue;
|
|
428
558
|
},
|
|
@@ -589,20 +719,30 @@ var canCopyAudioTrack = ({
|
|
|
589
719
|
};
|
|
590
720
|
// src/can-copy-video-track.ts
|
|
591
721
|
var canCopyVideoTrack = ({
|
|
592
|
-
inputCodec,
|
|
593
722
|
outputContainer,
|
|
594
|
-
inputRotation,
|
|
595
723
|
rotationToApply,
|
|
596
|
-
inputContainer
|
|
724
|
+
inputContainer,
|
|
725
|
+
resizeOperation,
|
|
726
|
+
inputTrack
|
|
597
727
|
}) => {
|
|
598
|
-
if (normalizeVideoRotation(
|
|
728
|
+
if (normalizeVideoRotation(inputTrack.rotation) !== normalizeVideoRotation(rotationToApply)) {
|
|
729
|
+
return false;
|
|
730
|
+
}
|
|
731
|
+
const newDimensions = calculateNewDimensionsFromRotateAndScale({
|
|
732
|
+
height: inputTrack.height,
|
|
733
|
+
resizeOperation,
|
|
734
|
+
rotation: rotationToApply,
|
|
735
|
+
videoCodec: inputTrack.codecWithoutConfig,
|
|
736
|
+
width: inputTrack.width
|
|
737
|
+
});
|
|
738
|
+
if (newDimensions.height !== inputTrack.height || newDimensions.width !== inputTrack.width) {
|
|
599
739
|
return false;
|
|
600
740
|
}
|
|
601
741
|
if (outputContainer === "webm") {
|
|
602
|
-
return
|
|
742
|
+
return inputTrack.codecWithoutConfig === "vp8" || inputTrack.codecWithoutConfig === "vp9";
|
|
603
743
|
}
|
|
604
744
|
if (outputContainer === "mp4") {
|
|
605
|
-
return
|
|
745
|
+
return inputTrack.codecWithoutConfig === "h264" && (inputContainer === "mp4" || inputContainer === "avi");
|
|
606
746
|
}
|
|
607
747
|
if (outputContainer === "wav") {
|
|
608
748
|
return false;
|
|
@@ -868,7 +1008,9 @@ var convertEncodedChunk = (chunk, trackId) => {
|
|
|
868
1008
|
type: chunk.type,
|
|
869
1009
|
cts: chunk.timestamp,
|
|
870
1010
|
dts: chunk.timestamp,
|
|
871
|
-
trackId
|
|
1011
|
+
trackId,
|
|
1012
|
+
offset: 0,
|
|
1013
|
+
timescale: 1e6
|
|
872
1014
|
};
|
|
873
1015
|
};
|
|
874
1016
|
|
|
@@ -1086,7 +1228,8 @@ var defaultOnVideoTrackHandler = async ({
|
|
|
1086
1228
|
defaultVideoCodec,
|
|
1087
1229
|
logLevel,
|
|
1088
1230
|
rotate,
|
|
1089
|
-
canCopyTrack
|
|
1231
|
+
canCopyTrack,
|
|
1232
|
+
resizeOperation
|
|
1090
1233
|
}) => {
|
|
1091
1234
|
if (canCopyTrack) {
|
|
1092
1235
|
MediaParserInternals7.Log.verbose(logLevel, `Track ${track.trackId} (video): Can copy, therefore copying`);
|
|
@@ -1105,7 +1248,8 @@ var defaultOnVideoTrackHandler = async ({
|
|
|
1105
1248
|
return Promise.resolve({
|
|
1106
1249
|
type: "reencode",
|
|
1107
1250
|
videoCodec: defaultVideoCodec,
|
|
1108
|
-
|
|
1251
|
+
rotate: rotate - track.rotation,
|
|
1252
|
+
resize: resizeOperation
|
|
1109
1253
|
});
|
|
1110
1254
|
}
|
|
1111
1255
|
MediaParserInternals7.Log.verbose(logLevel, `Track ${track.trackId} (video): Can neither copy nor re-encode, therefore failing`);
|
|
@@ -1171,11 +1315,14 @@ var onFrame = async ({
|
|
|
1171
1315
|
videoEncoder,
|
|
1172
1316
|
track,
|
|
1173
1317
|
outputCodec,
|
|
1174
|
-
rotation
|
|
1318
|
+
rotation,
|
|
1319
|
+
resizeOperation
|
|
1175
1320
|
}) => {
|
|
1176
|
-
const rotated =
|
|
1321
|
+
const rotated = rotateAndResizeVideoFrame({
|
|
1177
1322
|
rotation,
|
|
1178
|
-
frame: unrotatedFrame
|
|
1323
|
+
frame: unrotatedFrame,
|
|
1324
|
+
resizeOperation,
|
|
1325
|
+
videoCodec: outputCodec
|
|
1179
1326
|
});
|
|
1180
1327
|
if (unrotatedFrame !== rotated) {
|
|
1181
1328
|
unrotatedFrame.close();
|
|
@@ -1406,17 +1553,18 @@ var makeVideoTrackHandler = ({
|
|
|
1406
1553
|
logLevel,
|
|
1407
1554
|
outputContainer,
|
|
1408
1555
|
rotate,
|
|
1409
|
-
progress
|
|
1556
|
+
progress,
|
|
1557
|
+
resizeOperation
|
|
1410
1558
|
}) => async ({ track, container: inputContainer }) => {
|
|
1411
1559
|
if (controller.signal.aborted) {
|
|
1412
1560
|
throw new error_cause_default("Aborted");
|
|
1413
1561
|
}
|
|
1414
1562
|
const canCopyTrack = canCopyVideoTrack({
|
|
1415
|
-
inputCodec: track.codecWithoutConfig,
|
|
1416
1563
|
inputContainer,
|
|
1417
|
-
inputRotation: track.rotation,
|
|
1418
1564
|
outputContainer,
|
|
1419
|
-
rotationToApply: rotate
|
|
1565
|
+
rotationToApply: rotate,
|
|
1566
|
+
inputTrack: track,
|
|
1567
|
+
resizeOperation
|
|
1420
1568
|
});
|
|
1421
1569
|
const videoOperation = await (onVideoTrack ?? defaultOnVideoTrackHandler)({
|
|
1422
1570
|
track,
|
|
@@ -1425,7 +1573,8 @@ var makeVideoTrackHandler = ({
|
|
|
1425
1573
|
outputContainer,
|
|
1426
1574
|
rotate,
|
|
1427
1575
|
inputContainer,
|
|
1428
|
-
canCopyTrack
|
|
1576
|
+
canCopyTrack,
|
|
1577
|
+
resizeOperation
|
|
1429
1578
|
});
|
|
1430
1579
|
if (videoOperation.type === "drop") {
|
|
1431
1580
|
return null;
|
|
@@ -1463,10 +1612,12 @@ var makeVideoTrackHandler = ({
|
|
|
1463
1612
|
throw new error_cause_default(`Video track with ID ${track.trackId} could not be resolved with a valid operation. Received ${JSON.stringify(videoOperation)}, but must be either "copy", "reencode", "drop" or "fail"`);
|
|
1464
1613
|
}
|
|
1465
1614
|
const rotation = (videoOperation.rotate ?? rotate) - track.rotation;
|
|
1466
|
-
const { height: newHeight, width: newWidth } =
|
|
1615
|
+
const { height: newHeight, width: newWidth } = calculateNewDimensionsFromRotateAndScale({
|
|
1467
1616
|
width: track.codedWidth,
|
|
1468
1617
|
height: track.codedHeight,
|
|
1469
|
-
rotation
|
|
1618
|
+
rotation,
|
|
1619
|
+
videoCodec: videoOperation.videoCodec,
|
|
1620
|
+
resizeOperation: videoOperation.resize ?? null
|
|
1470
1621
|
});
|
|
1471
1622
|
const videoEncoderConfig = await getVideoEncoderConfig({
|
|
1472
1623
|
codec: videoOperation.videoCodec,
|
|
@@ -1528,7 +1679,8 @@ var makeVideoTrackHandler = ({
|
|
|
1528
1679
|
videoEncoder,
|
|
1529
1680
|
onVideoFrame,
|
|
1530
1681
|
outputCodec: videoOperation.videoCodec,
|
|
1531
|
-
rotation
|
|
1682
|
+
rotation,
|
|
1683
|
+
resizeOperation: videoOperation.resize ?? null
|
|
1532
1684
|
});
|
|
1533
1685
|
},
|
|
1534
1686
|
onError: (err) => {
|
|
@@ -1657,6 +1809,7 @@ var convertMedia = async function({
|
|
|
1657
1809
|
progressIntervalInMs,
|
|
1658
1810
|
rotate,
|
|
1659
1811
|
apiKey,
|
|
1812
|
+
resize,
|
|
1660
1813
|
...more
|
|
1661
1814
|
}) {
|
|
1662
1815
|
if (userPassedAbortSignal?.aborted) {
|
|
@@ -1727,7 +1880,8 @@ var convertMedia = async function({
|
|
|
1727
1880
|
logLevel,
|
|
1728
1881
|
outputContainer: container,
|
|
1729
1882
|
rotate: rotate ?? 0,
|
|
1730
|
-
progress: progressTracker
|
|
1883
|
+
progress: progressTracker,
|
|
1884
|
+
resizeOperation: resize ?? null
|
|
1731
1885
|
});
|
|
1732
1886
|
const onAudioTrack = makeAudioTrackHandler({
|
|
1733
1887
|
abortConversion,
|
|
@@ -1833,8 +1987,9 @@ var getAvailableVideoCodecs = ({
|
|
|
1833
1987
|
};
|
|
1834
1988
|
// src/index.ts
|
|
1835
1989
|
var WebCodecsInternals = {
|
|
1836
|
-
|
|
1837
|
-
normalizeVideoRotation
|
|
1990
|
+
rotateAndResizeVideoFrame,
|
|
1991
|
+
normalizeVideoRotation,
|
|
1992
|
+
calculateNewDimensionsFromDimensions: calculateNewDimensionsFromRotateAndScale
|
|
1838
1993
|
};
|
|
1839
1994
|
setRemotionImported();
|
|
1840
1995
|
export {
|
package/dist/index.d.ts
CHANGED
|
@@ -14,12 +14,22 @@ export { getDefaultAudioCodec } from './get-default-audio-codec';
|
|
|
14
14
|
export { getDefaultVideoCodec } from './get-default-video-codec';
|
|
15
15
|
export { AudioOperation, ConvertMediaOnAudioTrackHandler, } from './on-audio-track-handler';
|
|
16
16
|
export { ConvertMediaOnVideoTrackHandler, VideoOperation, } from './on-video-track-handler';
|
|
17
|
+
export type { ResizeOperation } from './resizing/mode';
|
|
17
18
|
export { createVideoDecoder, WebCodecsVideoDecoder } from './video-decoder';
|
|
18
19
|
export { createVideoEncoder, WebCodecsVideoEncoder } from './video-encoder';
|
|
19
20
|
export declare const WebCodecsInternals: {
|
|
20
|
-
|
|
21
|
+
rotateAndResizeVideoFrame: ({ frame, rotation, videoCodec, resizeOperation, }: {
|
|
21
22
|
frame: VideoFrame;
|
|
22
23
|
rotation: number;
|
|
24
|
+
videoCodec: import("./get-available-video-codecs").ConvertMediaVideoCodec;
|
|
25
|
+
resizeOperation: import("./resizing/mode").ResizeOperation | null;
|
|
23
26
|
}) => VideoFrame;
|
|
24
27
|
normalizeVideoRotation: (rotation: number) => number;
|
|
28
|
+
calculateNewDimensionsFromDimensions: ({ width, height, rotation, resizeOperation, videoCodec, }: {
|
|
29
|
+
width: number;
|
|
30
|
+
height: number;
|
|
31
|
+
rotation: number;
|
|
32
|
+
resizeOperation: import("./resizing/mode").ResizeOperation | null;
|
|
33
|
+
videoCodec: import("./get-available-video-codecs").ConvertMediaVideoCodec | import("@remotion/media-parser").MediaParserVideoCodec;
|
|
34
|
+
}) => import("./resizing/mode").Dimensions;
|
|
25
35
|
};
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.WebCodecsInternals = exports.createVideoEncoder = exports.createVideoDecoder = exports.getDefaultVideoCodec = exports.getDefaultAudioCodec = exports.getAvailableVideoCodecs = exports.getAvailableContainers = exports.getAvailableAudioCodecs = exports.defaultOnVideoTrackHandler = exports.defaultOnAudioTrackHandler = exports.convertMedia = exports.canReencodeVideoTrack = exports.canReencodeAudioTrack = exports.canCopyVideoTrack = exports.canCopyAudioTrack = exports.createAudioEncoder = exports.createAudioDecoder = void 0;
|
|
4
|
-
const
|
|
4
|
+
const rotate_and_resize_video_frame_1 = require("./rotate-and-resize-video-frame");
|
|
5
|
+
const rotation_1 = require("./rotation");
|
|
5
6
|
const set_remotion_imported_1 = require("./set-remotion-imported");
|
|
6
7
|
var audio_decoder_1 = require("./audio-decoder");
|
|
7
8
|
Object.defineProperty(exports, "createAudioDecoder", { enumerable: true, get: function () { return audio_decoder_1.createAudioDecoder; } });
|
|
@@ -36,7 +37,8 @@ Object.defineProperty(exports, "createVideoDecoder", { enumerable: true, get: fu
|
|
|
36
37
|
var video_encoder_1 = require("./video-encoder");
|
|
37
38
|
Object.defineProperty(exports, "createVideoEncoder", { enumerable: true, get: function () { return video_encoder_1.createVideoEncoder; } });
|
|
38
39
|
exports.WebCodecsInternals = {
|
|
39
|
-
|
|
40
|
-
normalizeVideoRotation:
|
|
40
|
+
rotateAndResizeVideoFrame: rotate_and_resize_video_frame_1.rotateAndResizeVideoFrame,
|
|
41
|
+
normalizeVideoRotation: rotate_and_resize_video_frame_1.normalizeVideoRotation,
|
|
42
|
+
calculateNewDimensionsFromDimensions: rotation_1.calculateNewDimensionsFromRotateAndScale,
|
|
41
43
|
};
|
|
42
44
|
(0, set_remotion_imported_1.setRemotionImported)();
|
|
@@ -69,14 +69,14 @@ const makeIoSynchronizer = ({ logLevel, label, progress, }) => {
|
|
|
69
69
|
return promise;
|
|
70
70
|
};
|
|
71
71
|
const waitFor = async ({ unprocessed, unemitted, minimumProgress, signal, }) => {
|
|
72
|
-
const { timeoutPromise, clear } = (0, make_timeout_promise_1.makeTimeoutPromise)([
|
|
73
|
-
`Waited too long for ${label}:`,
|
|
72
|
+
const { timeoutPromise, clear } = (0, make_timeout_promise_1.makeTimeoutPromise)(() => [
|
|
73
|
+
`Waited too long for ${label} to finish:`,
|
|
74
74
|
`${getUnemittedItems()} unemitted items`,
|
|
75
|
-
`${getUnprocessed()} unprocessed items`,
|
|
76
|
-
`minimum progress ${minimumProgress}`,
|
|
75
|
+
`${getUnprocessed()} unprocessed items: ${JSON.stringify(_unprocessed)}`,
|
|
77
76
|
`smallest progress: ${progress.getSmallestProgress()}`,
|
|
78
77
|
`inputs: ${JSON.stringify(inputs)}`,
|
|
79
78
|
`last output: ${lastOutput}`,
|
|
79
|
+
`wanted: ${unemitted} unemitted items, ${unprocessed} unprocessed items, minimum progress ${minimumProgress}`,
|
|
80
80
|
].join('\n'), 10000);
|
|
81
81
|
signal.addEventListener('abort', clear);
|
|
82
82
|
await Promise.race([
|
|
@@ -5,7 +5,7 @@ const media_parser_1 = require("@remotion/media-parser");
|
|
|
5
5
|
const makeTimeoutPromise = (label, ms) => {
|
|
6
6
|
const { promise, reject, resolve } = media_parser_1.MediaParserInternals.withResolvers();
|
|
7
7
|
const timeout = setTimeout(() => {
|
|
8
|
-
reject(new Error(`${label} (timed out after ${ms}ms)`));
|
|
8
|
+
reject(new Error(`${label()} (timed out after ${ms}ms)`));
|
|
9
9
|
}, ms);
|
|
10
10
|
return {
|
|
11
11
|
timeoutPromise: promise,
|
package/dist/on-frame.d.ts
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
import type { VideoTrack } from '@remotion/media-parser';
|
|
2
2
|
import type { ConvertMediaOnVideoFrame } from './convert-media';
|
|
3
3
|
import type { ConvertMediaVideoCodec } from './get-available-video-codecs';
|
|
4
|
+
import type { ResizeOperation } from './resizing/mode';
|
|
4
5
|
import type { WebCodecsVideoEncoder } from './video-encoder';
|
|
5
|
-
export declare const onFrame: ({ frame: unrotatedFrame, onVideoFrame, videoEncoder, track, outputCodec, rotation, }: {
|
|
6
|
+
export declare const onFrame: ({ frame: unrotatedFrame, onVideoFrame, videoEncoder, track, outputCodec, rotation, resizeOperation, }: {
|
|
6
7
|
frame: VideoFrame;
|
|
7
8
|
onVideoFrame: ConvertMediaOnVideoFrame | null;
|
|
8
9
|
videoEncoder: WebCodecsVideoEncoder;
|
|
9
10
|
track: VideoTrack;
|
|
10
11
|
outputCodec: ConvertMediaVideoCodec;
|
|
11
12
|
rotation: number;
|
|
13
|
+
resizeOperation: ResizeOperation | null;
|
|
12
14
|
}) => Promise<void>;
|
package/dist/on-frame.js
CHANGED
|
@@ -3,12 +3,14 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.onFrame = void 0;
|
|
4
4
|
const browser_quirks_1 = require("./browser-quirks");
|
|
5
5
|
const convert_to_correct_videoframe_1 = require("./convert-to-correct-videoframe");
|
|
6
|
-
const
|
|
7
|
-
const onFrame = async ({ frame: unrotatedFrame, onVideoFrame, videoEncoder, track, outputCodec, rotation, }) => {
|
|
6
|
+
const rotate_and_resize_video_frame_1 = require("./rotate-and-resize-video-frame");
|
|
7
|
+
const onFrame = async ({ frame: unrotatedFrame, onVideoFrame, videoEncoder, track, outputCodec, rotation, resizeOperation, }) => {
|
|
8
8
|
var _a, _b;
|
|
9
|
-
const rotated = (0,
|
|
9
|
+
const rotated = (0, rotate_and_resize_video_frame_1.rotateAndResizeVideoFrame)({
|
|
10
10
|
rotation,
|
|
11
11
|
frame: unrotatedFrame,
|
|
12
|
+
resizeOperation,
|
|
13
|
+
videoCodec: outputCodec,
|
|
12
14
|
});
|
|
13
15
|
if (unrotatedFrame !== rotated) {
|
|
14
16
|
unrotatedFrame.close();
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import type { LogLevel, ParseMediaContainer, VideoTrack } from '@remotion/media-parser';
|
|
2
2
|
import type { ConvertMediaContainer } from './get-available-containers';
|
|
3
3
|
import type { ConvertMediaVideoCodec } from './get-available-video-codecs';
|
|
4
|
+
import type { ResizeOperation } from './resizing/mode';
|
|
4
5
|
export type VideoOperation = {
|
|
5
6
|
type: 'reencode';
|
|
6
7
|
videoCodec: ConvertMediaVideoCodec;
|
|
7
8
|
rotate?: number;
|
|
9
|
+
resize?: ResizeOperation | null;
|
|
8
10
|
} | {
|
|
9
11
|
type: 'copy';
|
|
10
12
|
} | {
|
|
@@ -18,6 +20,7 @@ export type ConvertMediaOnVideoTrackHandler = (options: {
|
|
|
18
20
|
logLevel: LogLevel;
|
|
19
21
|
outputContainer: ConvertMediaContainer;
|
|
20
22
|
rotate: number;
|
|
23
|
+
resizeOperation: ResizeOperation | null;
|
|
21
24
|
inputContainer: ParseMediaContainer;
|
|
22
25
|
canCopyTrack: boolean;
|
|
23
26
|
}) => VideoOperation | Promise<VideoOperation>;
|
package/dist/on-video-track.d.ts
CHANGED
|
@@ -4,8 +4,9 @@ import Error from './error-cause';
|
|
|
4
4
|
import type { ConvertMediaContainer } from './get-available-containers';
|
|
5
5
|
import type { ConvertMediaVideoCodec } from './get-available-video-codecs';
|
|
6
6
|
import type { ConvertMediaOnVideoTrackHandler } from './on-video-track-handler';
|
|
7
|
+
import type { ResizeOperation } from './resizing/mode';
|
|
7
8
|
import type { ConvertMediaProgressFn } from './throttled-state-update';
|
|
8
|
-
export declare const makeVideoTrackHandler: ({ state, onVideoFrame, onMediaStateUpdate, abortConversion, controller, defaultVideoCodec, onVideoTrack, logLevel, outputContainer, rotate, progress, }: {
|
|
9
|
+
export declare const makeVideoTrackHandler: ({ state, onVideoFrame, onMediaStateUpdate, abortConversion, controller, defaultVideoCodec, onVideoTrack, logLevel, outputContainer, rotate, progress, resizeOperation, }: {
|
|
9
10
|
state: MediaFn;
|
|
10
11
|
onVideoFrame: null | ConvertMediaOnVideoFrame;
|
|
11
12
|
onMediaStateUpdate: null | ConvertMediaProgressFn;
|
|
@@ -17,4 +18,5 @@ export declare const makeVideoTrackHandler: ({ state, onVideoFrame, onMediaState
|
|
|
17
18
|
outputContainer: ConvertMediaContainer;
|
|
18
19
|
rotate: number;
|
|
19
20
|
progress: ProgressTracker;
|
|
21
|
+
resizeOperation: ResizeOperation | null;
|
|
20
22
|
}) => OnVideoTrack;
|
package/dist/on-video-track.js
CHANGED
|
@@ -17,17 +17,17 @@ const video_decoder_1 = require("./video-decoder");
|
|
|
17
17
|
const video_decoder_config_1 = require("./video-decoder-config");
|
|
18
18
|
const video_encoder_1 = require("./video-encoder");
|
|
19
19
|
const video_encoder_config_1 = require("./video-encoder-config");
|
|
20
|
-
const makeVideoTrackHandler = ({ state, onVideoFrame, onMediaStateUpdate, abortConversion, controller, defaultVideoCodec, onVideoTrack, logLevel, outputContainer, rotate, progress, }) => async ({ track, container: inputContainer }) => {
|
|
21
|
-
var _a;
|
|
20
|
+
const makeVideoTrackHandler = ({ state, onVideoFrame, onMediaStateUpdate, abortConversion, controller, defaultVideoCodec, onVideoTrack, logLevel, outputContainer, rotate, progress, resizeOperation, }) => async ({ track, container: inputContainer }) => {
|
|
21
|
+
var _a, _b;
|
|
22
22
|
if (controller.signal.aborted) {
|
|
23
23
|
throw new error_cause_1.default('Aborted');
|
|
24
24
|
}
|
|
25
25
|
const canCopyTrack = (0, can_copy_video_track_1.canCopyVideoTrack)({
|
|
26
|
-
inputCodec: track.codecWithoutConfig,
|
|
27
26
|
inputContainer,
|
|
28
|
-
inputRotation: track.rotation,
|
|
29
27
|
outputContainer,
|
|
30
28
|
rotationToApply: rotate,
|
|
29
|
+
inputTrack: track,
|
|
30
|
+
resizeOperation,
|
|
31
31
|
});
|
|
32
32
|
const videoOperation = await (onVideoTrack !== null && onVideoTrack !== void 0 ? onVideoTrack : default_on_video_track_handler_1.defaultOnVideoTrackHandler)({
|
|
33
33
|
track,
|
|
@@ -37,6 +37,7 @@ const makeVideoTrackHandler = ({ state, onVideoFrame, onMediaStateUpdate, abortC
|
|
|
37
37
|
rotate,
|
|
38
38
|
inputContainer,
|
|
39
39
|
canCopyTrack,
|
|
40
|
+
resizeOperation,
|
|
40
41
|
});
|
|
41
42
|
if (videoOperation.type === 'drop') {
|
|
42
43
|
return null;
|
|
@@ -74,10 +75,12 @@ const makeVideoTrackHandler = ({ state, onVideoFrame, onMediaStateUpdate, abortC
|
|
|
74
75
|
throw new error_cause_1.default(`Video track with ID ${track.trackId} could not be resolved with a valid operation. Received ${JSON.stringify(videoOperation)}, but must be either "copy", "reencode", "drop" or "fail"`);
|
|
75
76
|
}
|
|
76
77
|
const rotation = ((_a = videoOperation.rotate) !== null && _a !== void 0 ? _a : rotate) - track.rotation;
|
|
77
|
-
const { height: newHeight, width: newWidth } = (0, rotation_1.
|
|
78
|
+
const { height: newHeight, width: newWidth } = (0, rotation_1.calculateNewDimensionsFromRotateAndScale)({
|
|
78
79
|
width: track.codedWidth,
|
|
79
80
|
height: track.codedHeight,
|
|
80
81
|
rotation,
|
|
82
|
+
videoCodec: videoOperation.videoCodec,
|
|
83
|
+
resizeOperation: (_b = videoOperation.resize) !== null && _b !== void 0 ? _b : null,
|
|
81
84
|
});
|
|
82
85
|
const videoEncoderConfig = await (0, video_encoder_config_1.getVideoEncoderConfig)({
|
|
83
86
|
codec: videoOperation.videoCodec,
|
|
@@ -134,6 +137,7 @@ const makeVideoTrackHandler = ({ state, onVideoFrame, onMediaStateUpdate, abortC
|
|
|
134
137
|
const videoDecoder = (0, video_decoder_1.createVideoDecoder)({
|
|
135
138
|
config: videoDecoderConfig,
|
|
136
139
|
onFrame: async (frame) => {
|
|
140
|
+
var _a;
|
|
137
141
|
await (0, on_frame_1.onFrame)({
|
|
138
142
|
frame,
|
|
139
143
|
track,
|
|
@@ -141,6 +145,7 @@ const makeVideoTrackHandler = ({ state, onVideoFrame, onMediaStateUpdate, abortC
|
|
|
141
145
|
onVideoFrame,
|
|
142
146
|
outputCodec: videoOperation.videoCodec,
|
|
143
147
|
rotation,
|
|
148
|
+
resizeOperation: (_a = videoOperation.resize) !== null && _a !== void 0 ? _a : null,
|
|
144
149
|
});
|
|
145
150
|
},
|
|
146
151
|
onError: (err) => {
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { MediaParserVideoCodec } from '@remotion/media-parser';
|
|
2
|
+
import type { ConvertMediaVideoCodec } from '../get-available-video-codecs';
|
|
3
|
+
import type { Dimensions, ResizeOperation } from './mode';
|
|
4
|
+
export declare const calculateNewSizeAfterResizing: ({ dimensions, resizeOperation, videoCodec, }: {
|
|
5
|
+
dimensions: Dimensions;
|
|
6
|
+
resizeOperation: ResizeOperation | null;
|
|
7
|
+
videoCodec: ConvertMediaVideoCodec | MediaParserVideoCodec;
|
|
8
|
+
}) => Dimensions;
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.calculateNewSizeAfterResizing = void 0;
|
|
4
|
+
const ensureMultipleOfTwo = ({ dimensions, needsToBeMultipleOfTwo, }) => {
|
|
5
|
+
if (!needsToBeMultipleOfTwo) {
|
|
6
|
+
return dimensions;
|
|
7
|
+
}
|
|
8
|
+
return {
|
|
9
|
+
width: Math.floor(dimensions.width / 2) * 2,
|
|
10
|
+
height: Math.floor(dimensions.height / 2) * 2,
|
|
11
|
+
};
|
|
12
|
+
};
|
|
13
|
+
const calculateNewSizeAfterResizing = ({ dimensions, resizeOperation, videoCodec, }) => {
|
|
14
|
+
const needsToBeMultipleOfTwo = videoCodec === 'h264';
|
|
15
|
+
if (resizeOperation === null) {
|
|
16
|
+
return ensureMultipleOfTwo({
|
|
17
|
+
dimensions,
|
|
18
|
+
needsToBeMultipleOfTwo,
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
if (resizeOperation.mode === 'width') {
|
|
22
|
+
return ensureMultipleOfTwo({
|
|
23
|
+
dimensions: {
|
|
24
|
+
width: resizeOperation.width,
|
|
25
|
+
height: Math.round((resizeOperation.width / dimensions.width) * dimensions.height),
|
|
26
|
+
},
|
|
27
|
+
needsToBeMultipleOfTwo,
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
if (resizeOperation.mode === 'height') {
|
|
31
|
+
return ensureMultipleOfTwo({
|
|
32
|
+
dimensions: {
|
|
33
|
+
width: Math.round((resizeOperation.height / dimensions.height) * dimensions.width),
|
|
34
|
+
height: resizeOperation.height,
|
|
35
|
+
},
|
|
36
|
+
needsToBeMultipleOfTwo,
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
if (resizeOperation.mode === 'max-height') {
|
|
40
|
+
const height = Math.min(dimensions.height, resizeOperation.maxHeight);
|
|
41
|
+
return ensureMultipleOfTwo({
|
|
42
|
+
dimensions: {
|
|
43
|
+
width: Math.round((height / dimensions.height) * dimensions.width),
|
|
44
|
+
height,
|
|
45
|
+
},
|
|
46
|
+
needsToBeMultipleOfTwo,
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
if (resizeOperation.mode === 'max-width') {
|
|
50
|
+
const width = Math.min(dimensions.width, resizeOperation.maxWidth);
|
|
51
|
+
return ensureMultipleOfTwo({
|
|
52
|
+
dimensions: {
|
|
53
|
+
width,
|
|
54
|
+
height: Math.round((width / dimensions.width) * dimensions.height),
|
|
55
|
+
},
|
|
56
|
+
needsToBeMultipleOfTwo,
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
if (resizeOperation.mode === 'max-height-width') {
|
|
60
|
+
const height = Math.min(dimensions.height, resizeOperation.maxHeight);
|
|
61
|
+
const width = Math.min(dimensions.width, resizeOperation.maxWidth);
|
|
62
|
+
const scale = Math.min(width / dimensions.width, height / dimensions.height);
|
|
63
|
+
const actualWidth = Math.round(dimensions.width * scale);
|
|
64
|
+
const actualHeight = Math.round(dimensions.height * scale);
|
|
65
|
+
return ensureMultipleOfTwo({
|
|
66
|
+
dimensions: {
|
|
67
|
+
height: actualHeight,
|
|
68
|
+
width: actualWidth,
|
|
69
|
+
},
|
|
70
|
+
needsToBeMultipleOfTwo,
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
if (resizeOperation.mode === 'scale') {
|
|
74
|
+
if (resizeOperation.scale <= 0) {
|
|
75
|
+
throw new Error('Scale must be greater than 0');
|
|
76
|
+
}
|
|
77
|
+
const width = Math.round(dimensions.width * resizeOperation.scale);
|
|
78
|
+
const height = Math.round(dimensions.height * resizeOperation.scale);
|
|
79
|
+
return ensureMultipleOfTwo({
|
|
80
|
+
dimensions: {
|
|
81
|
+
width,
|
|
82
|
+
height,
|
|
83
|
+
},
|
|
84
|
+
needsToBeMultipleOfTwo,
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
throw new Error('Invalid resizing mode ' + resizeOperation);
|
|
88
|
+
};
|
|
89
|
+
exports.calculateNewSizeAfterResizing = calculateNewSizeAfterResizing;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export type Dimensions = {
|
|
2
|
+
width: number;
|
|
3
|
+
height: number;
|
|
4
|
+
};
|
|
5
|
+
export type ResizeOperation = {
|
|
6
|
+
mode: 'width';
|
|
7
|
+
width: number;
|
|
8
|
+
} | {
|
|
9
|
+
mode: 'height';
|
|
10
|
+
height: number;
|
|
11
|
+
} | {
|
|
12
|
+
mode: 'max-height';
|
|
13
|
+
maxHeight: number;
|
|
14
|
+
} | {
|
|
15
|
+
mode: 'max-width';
|
|
16
|
+
maxWidth: number;
|
|
17
|
+
} | {
|
|
18
|
+
mode: 'max-height-width';
|
|
19
|
+
maxHeight: number;
|
|
20
|
+
maxWidth: number;
|
|
21
|
+
} | {
|
|
22
|
+
mode: 'scale';
|
|
23
|
+
scale: number;
|
|
24
|
+
};
|
package/dist/resizing.js
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { ConvertMediaVideoCodec } from './get-available-video-codecs';
|
|
2
|
+
import type { ResizeOperation } from './resizing/mode';
|
|
3
|
+
export declare const normalizeVideoRotation: (rotation: number) => number;
|
|
4
|
+
export declare const rotateAndResizeVideoFrame: ({ frame, rotation, videoCodec, resizeOperation, }: {
|
|
5
|
+
frame: VideoFrame;
|
|
6
|
+
rotation: number;
|
|
7
|
+
videoCodec: ConvertMediaVideoCodec;
|
|
8
|
+
resizeOperation: ResizeOperation | null;
|
|
9
|
+
}) => VideoFrame;
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.rotateAndResizeVideoFrame = exports.normalizeVideoRotation = void 0;
|
|
4
|
+
const rotation_1 = require("./rotation");
|
|
5
|
+
const normalizeVideoRotation = (rotation) => {
|
|
6
|
+
return ((rotation % 360) + 360) % 360;
|
|
7
|
+
};
|
|
8
|
+
exports.normalizeVideoRotation = normalizeVideoRotation;
|
|
9
|
+
const rotateAndResizeVideoFrame = ({ frame, rotation, videoCodec, resizeOperation, }) => {
|
|
10
|
+
var _a;
|
|
11
|
+
const normalized = ((rotation % 360) + 360) % 360;
|
|
12
|
+
// No resize, no rotation
|
|
13
|
+
if (normalized === 0 && resizeOperation === null) {
|
|
14
|
+
return frame;
|
|
15
|
+
}
|
|
16
|
+
if (normalized % 90 !== 0) {
|
|
17
|
+
throw new Error('Only 90 degree rotations are supported');
|
|
18
|
+
}
|
|
19
|
+
const { height, width } = (0, rotation_1.calculateNewDimensionsFromRotateAndScale)({
|
|
20
|
+
height: frame.displayHeight,
|
|
21
|
+
width: frame.displayWidth,
|
|
22
|
+
rotation,
|
|
23
|
+
videoCodec,
|
|
24
|
+
resizeOperation,
|
|
25
|
+
});
|
|
26
|
+
// No rotation, and resize turned out to be same dimensions
|
|
27
|
+
if (normalized === 0 &&
|
|
28
|
+
height === frame.displayHeight &&
|
|
29
|
+
width === frame.displayWidth) {
|
|
30
|
+
return frame;
|
|
31
|
+
}
|
|
32
|
+
const canvas = new OffscreenCanvas(width, height);
|
|
33
|
+
const ctx = canvas.getContext('2d');
|
|
34
|
+
if (!ctx) {
|
|
35
|
+
throw new Error('Could not get 2d context');
|
|
36
|
+
}
|
|
37
|
+
canvas.width = width;
|
|
38
|
+
canvas.height = height;
|
|
39
|
+
if (normalized === 90) {
|
|
40
|
+
ctx.translate(width, 0);
|
|
41
|
+
}
|
|
42
|
+
else if (normalized === 180) {
|
|
43
|
+
ctx.translate(width, height);
|
|
44
|
+
}
|
|
45
|
+
else if (normalized === 270) {
|
|
46
|
+
ctx.translate(0, height);
|
|
47
|
+
}
|
|
48
|
+
if (normalized !== 0) {
|
|
49
|
+
ctx.rotate(normalized * (Math.PI / 180));
|
|
50
|
+
}
|
|
51
|
+
if (frame.displayHeight !== height || frame.displayWidth !== width) {
|
|
52
|
+
const dimensionsAfterRotate = (0, rotation_1.calculateNewDimensionsFromRotate)({
|
|
53
|
+
height: frame.displayHeight,
|
|
54
|
+
rotation,
|
|
55
|
+
width: frame.displayWidth,
|
|
56
|
+
});
|
|
57
|
+
ctx.scale(width / dimensionsAfterRotate.width, height / dimensionsAfterRotate.height);
|
|
58
|
+
}
|
|
59
|
+
ctx.drawImage(frame, 0, 0);
|
|
60
|
+
return new VideoFrame(canvas, {
|
|
61
|
+
displayHeight: height,
|
|
62
|
+
displayWidth: width,
|
|
63
|
+
duration: (_a = frame.duration) !== null && _a !== void 0 ? _a : undefined,
|
|
64
|
+
timestamp: frame.timestamp,
|
|
65
|
+
});
|
|
66
|
+
};
|
|
67
|
+
exports.rotateAndResizeVideoFrame = rotateAndResizeVideoFrame;
|
|
@@ -1,5 +1,9 @@
|
|
|
1
|
+
import type { ConvertMediaVideoCodec } from './get-available-video-codecs';
|
|
2
|
+
import type { ResizingMode } from './resizing/mode';
|
|
1
3
|
export declare const normalizeVideoRotation: (rotation: number) => number;
|
|
2
|
-
export declare const
|
|
4
|
+
export declare const rotateAndResizeVideoFrame: ({ frame, rotation, videoCodec, resizingMode, }: {
|
|
3
5
|
frame: VideoFrame;
|
|
4
6
|
rotation: number;
|
|
7
|
+
videoCodec: ConvertMediaVideoCodec;
|
|
8
|
+
resizingMode: ResizingMode | null;
|
|
5
9
|
}) => VideoFrame;
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
3
|
+
exports.rotateAndResizeVideoFrame = exports.normalizeVideoRotation = void 0;
|
|
4
4
|
const rotation_1 = require("./rotation");
|
|
5
5
|
const normalizeVideoRotation = (rotation) => {
|
|
6
6
|
return ((rotation % 360) + 360) % 360;
|
|
7
7
|
};
|
|
8
8
|
exports.normalizeVideoRotation = normalizeVideoRotation;
|
|
9
|
-
const
|
|
9
|
+
const rotateAndResizeVideoFrame = ({ frame, rotation, videoCodec, resizingMode, }) => {
|
|
10
10
|
var _a;
|
|
11
11
|
const normalized = ((rotation % 360) + 360) % 360;
|
|
12
|
-
if (normalized % 360 === 0) {
|
|
12
|
+
if (normalized % 360 === 0 && resizingMode === null) {
|
|
13
13
|
return frame;
|
|
14
14
|
}
|
|
15
15
|
if (normalized % 90 !== 0) {
|
|
@@ -19,6 +19,8 @@ const rotateVideoFrame = ({ frame, rotation, }) => {
|
|
|
19
19
|
height: frame.displayHeight,
|
|
20
20
|
width: frame.displayWidth,
|
|
21
21
|
rotation,
|
|
22
|
+
videoCodec,
|
|
23
|
+
resizingMode,
|
|
22
24
|
});
|
|
23
25
|
const canvas = new OffscreenCanvas(width, height);
|
|
24
26
|
const ctx = canvas.getContext('2d');
|
|
@@ -37,6 +39,9 @@ const rotateVideoFrame = ({ frame, rotation, }) => {
|
|
|
37
39
|
ctx.translate(0, height);
|
|
38
40
|
}
|
|
39
41
|
ctx.rotate(normalized * (Math.PI / 180));
|
|
42
|
+
if (frame.displayHeight !== height || frame.displayWidth !== width) {
|
|
43
|
+
ctx.scale(width / frame.displayWidth, height / frame.displayHeight);
|
|
44
|
+
}
|
|
40
45
|
ctx.drawImage(frame, 0, 0);
|
|
41
46
|
return new VideoFrame(canvas, {
|
|
42
47
|
displayHeight: height,
|
|
@@ -45,4 +50,4 @@ const rotateVideoFrame = ({ frame, rotation, }) => {
|
|
|
45
50
|
timestamp: frame.timestamp,
|
|
46
51
|
});
|
|
47
52
|
};
|
|
48
|
-
exports.
|
|
53
|
+
exports.rotateAndResizeVideoFrame = rotateAndResizeVideoFrame;
|
package/dist/rotation.d.ts
CHANGED
|
@@ -1,8 +1,16 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
import type { Dimensions, MediaParserVideoCodec } from '@remotion/media-parser';
|
|
2
|
+
import type { ConvertMediaVideoCodec } from './get-available-video-codecs';
|
|
3
|
+
import type { ResizeOperation } from './resizing/mode';
|
|
4
|
+
export declare const calculateNewDimensionsFromRotate: ({ height, width, rotation, }: Dimensions & {
|
|
4
5
|
rotation: number;
|
|
5
6
|
}) => {
|
|
6
7
|
height: number;
|
|
7
8
|
width: number;
|
|
8
9
|
};
|
|
10
|
+
export declare const calculateNewDimensionsFromRotateAndScale: ({ width, height, rotation, resizeOperation, videoCodec, }: {
|
|
11
|
+
width: number;
|
|
12
|
+
height: number;
|
|
13
|
+
rotation: number;
|
|
14
|
+
resizeOperation: ResizeOperation | null;
|
|
15
|
+
videoCodec: ConvertMediaVideoCodec | MediaParserVideoCodec;
|
|
16
|
+
}) => import("./resizing/mode").Dimensions;
|
package/dist/rotation.js
CHANGED
|
@@ -1,10 +1,29 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
4
|
-
const
|
|
5
|
-
|
|
3
|
+
exports.calculateNewDimensionsFromRotateAndScale = exports.calculateNewDimensionsFromRotate = void 0;
|
|
4
|
+
const calculate_new_size_1 = require("./resizing/calculate-new-size");
|
|
5
|
+
const rotate_and_resize_video_frame_1 = require("./rotate-and-resize-video-frame");
|
|
6
|
+
const calculateNewDimensionsFromRotate = ({ height, width, rotation, }) => {
|
|
7
|
+
const normalized = (0, rotate_and_resize_video_frame_1.normalizeVideoRotation)(rotation);
|
|
8
|
+
const switchDimensions = normalized % 90 === 0 && normalized % 180 !== 0;
|
|
6
9
|
const newHeight = switchDimensions ? width : height;
|
|
7
10
|
const newWidth = switchDimensions ? height : width;
|
|
8
|
-
return {
|
|
11
|
+
return {
|
|
12
|
+
height: newHeight,
|
|
13
|
+
width: newWidth,
|
|
14
|
+
};
|
|
9
15
|
};
|
|
10
|
-
exports.
|
|
16
|
+
exports.calculateNewDimensionsFromRotate = calculateNewDimensionsFromRotate;
|
|
17
|
+
const calculateNewDimensionsFromRotateAndScale = ({ width, height, rotation, resizeOperation, videoCodec, }) => {
|
|
18
|
+
const { height: newHeight, width: newWidth } = (0, exports.calculateNewDimensionsFromRotate)({
|
|
19
|
+
height,
|
|
20
|
+
rotation,
|
|
21
|
+
width,
|
|
22
|
+
});
|
|
23
|
+
return (0, calculate_new_size_1.calculateNewSizeAfterResizing)({
|
|
24
|
+
dimensions: { height: newHeight, width: newWidth },
|
|
25
|
+
resizeOperation,
|
|
26
|
+
videoCodec,
|
|
27
|
+
});
|
|
28
|
+
};
|
|
29
|
+
exports.calculateNewDimensionsFromRotateAndScale = calculateNewDimensionsFromRotateAndScale;
|
package/dist/scaling.js
ADDED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@remotion/webcodecs",
|
|
3
|
-
"version": "4.0.
|
|
3
|
+
"version": "4.0.242",
|
|
4
4
|
"main": "dist/index.js",
|
|
5
5
|
"types": "dist/index.d.ts",
|
|
6
6
|
"module": "dist/esm/index.mjs",
|
|
@@ -17,15 +17,15 @@
|
|
|
17
17
|
"author": "Jonny Burger <jonny@remotion.dev>",
|
|
18
18
|
"license": "Remotion License (See https://remotion.dev/docs/webcodecs#license)",
|
|
19
19
|
"dependencies": {
|
|
20
|
-
"@remotion/
|
|
21
|
-
"@remotion/
|
|
20
|
+
"@remotion/licensing": "4.0.242",
|
|
21
|
+
"@remotion/media-parser": "4.0.242"
|
|
22
22
|
},
|
|
23
23
|
"peerDependencies": {},
|
|
24
24
|
"devDependencies": {
|
|
25
25
|
"@types/dom-webcodecs": "0.1.11",
|
|
26
26
|
"eslint": "9.14.0",
|
|
27
|
-
"@remotion/example-videos": "4.0.
|
|
28
|
-
"@remotion/eslint-config-internal": "4.0.
|
|
27
|
+
"@remotion/example-videos": "4.0.242",
|
|
28
|
+
"@remotion/eslint-config-internal": "4.0.242"
|
|
29
29
|
},
|
|
30
30
|
"keywords": [],
|
|
31
31
|
"publishConfig": {
|