@remotion/webcodecs 4.0.232 → 4.0.233

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.
Files changed (48) hide show
  1. package/README.md +2 -2
  2. package/dist/audio-decoder-config.d.ts +2 -1
  3. package/dist/audio-decoder-config.js +44 -3
  4. package/dist/audio-decoder.d.ts +6 -3
  5. package/dist/audio-decoder.js +20 -4
  6. package/dist/audio-encoder.d.ts +3 -2
  7. package/dist/audio-encoder.js +11 -3
  8. package/dist/browser-quirks.d.ts +1 -0
  9. package/dist/browser-quirks.js +5 -1
  10. package/dist/can-copy-video-track.d.ts +3 -1
  11. package/dist/can-copy-video-track.js +6 -1
  12. package/dist/can-reencode-audio-track.d.ts +3 -2
  13. package/dist/can-reencode-audio-track.js +2 -2
  14. package/dist/convert-media.d.ts +2 -1
  15. package/dist/convert-media.js +7 -3
  16. package/dist/convert-to-correct-videoframe.js +4 -0
  17. package/dist/default-on-video-track-handler.js +8 -2
  18. package/dist/esm/index.mjs +351 -137
  19. package/dist/get-wave-audio-decoder.d.ts +2 -0
  20. package/dist/get-wave-audio-decoder.js +29 -0
  21. package/dist/index.d.ts +7 -0
  22. package/dist/index.js +6 -1
  23. package/dist/io-manager/event-emitter.d.ts +4 -0
  24. package/dist/io-manager/event-emitter.js +1 -0
  25. package/dist/io-manager/io-synchronizer.d.ts +8 -2
  26. package/dist/io-manager/io-synchronizer.js +31 -20
  27. package/dist/io-manager/make-timeout-promise.d.ts +4 -0
  28. package/dist/io-manager/make-timeout-promise.js +18 -0
  29. package/dist/on-audio-track.d.ts +3 -2
  30. package/dist/on-audio-track.js +6 -3
  31. package/dist/on-frame.d.ts +2 -1
  32. package/dist/on-frame.js +25 -14
  33. package/dist/on-video-track-handler.d.ts +2 -0
  34. package/dist/on-video-track.d.ts +4 -2
  35. package/dist/on-video-track.js +20 -5
  36. package/dist/rotate-video-frame.d.ts +5 -0
  37. package/dist/rotate-video-frame.js +48 -0
  38. package/dist/rotate-video.d.ts +4 -0
  39. package/dist/rotate-video.js +43 -0
  40. package/dist/rotation.d.ts +8 -0
  41. package/dist/rotation.js +10 -0
  42. package/dist/select-container-creator.d.ts +1 -1
  43. package/dist/test/avi-to-mp4.test.js +15 -0
  44. package/dist/video-decoder.d.ts +3 -2
  45. package/dist/video-decoder.js +11 -3
  46. package/dist/video-encoder.d.ts +3 -2
  47. package/dist/video-encoder.js +7 -2
  48. package/package.json +5 -3
@@ -0,0 +1,2 @@
1
+ import type { CreateAudioDecoderInit, WebCodecsAudioDecoder } from './audio-decoder';
2
+ export declare const getWaveAudioDecoder: ({ onFrame, track, }: Pick<CreateAudioDecoderInit, "onFrame" | "track">) => WebCodecsAudioDecoder;
@@ -0,0 +1,29 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getWaveAudioDecoder = void 0;
4
+ // TODO: Should also be subject to throttling
5
+ const getWaveAudioDecoder = ({ onFrame, track, }) => {
6
+ let queue = Promise.resolve();
7
+ const processSample = async (audioSample) => {
8
+ await onFrame(new AudioData({
9
+ data: audioSample.data,
10
+ format: 's16',
11
+ numberOfChannels: track.numberOfChannels,
12
+ numberOfFrames: audioSample.data.byteLength / 2,
13
+ sampleRate: track.sampleRate,
14
+ timestamp: audioSample.timestamp,
15
+ }));
16
+ };
17
+ return {
18
+ close() {
19
+ return Promise.resolve();
20
+ },
21
+ processSample(audioSample) {
22
+ queue = queue.then(() => processSample(audioSample));
23
+ return queue;
24
+ },
25
+ flush: () => Promise.resolve(),
26
+ waitForFinish: () => Promise.resolve(),
27
+ };
28
+ };
29
+ exports.getWaveAudioDecoder = getWaveAudioDecoder;
package/dist/index.d.ts CHANGED
@@ -16,3 +16,10 @@ export { AudioOperation, ConvertMediaOnAudioTrackHandler, } from './on-audio-tra
16
16
  export { ConvertMediaOnVideoTrackHandler, VideoOperation, } from './on-video-track-handler';
17
17
  export { createVideoDecoder, WebCodecsVideoDecoder } from './video-decoder';
18
18
  export { createVideoEncoder, WebCodecsVideoEncoder } from './video-encoder';
19
+ export declare const WebCodecsInternals: {
20
+ rotateVideoFrame: ({ frame, rotation, }: {
21
+ frame: VideoFrame;
22
+ rotation: number;
23
+ }) => VideoFrame;
24
+ normalizeVideoRotation: (rotation: number) => number;
25
+ };
package/dist/index.js CHANGED
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- 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;
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 rotate_video_frame_1 = require("./rotate-video-frame");
4
5
  const set_remotion_imported_1 = require("./set-remotion-imported");
5
6
  var audio_decoder_1 = require("./audio-decoder");
6
7
  Object.defineProperty(exports, "createAudioDecoder", { enumerable: true, get: function () { return audio_decoder_1.createAudioDecoder; } });
@@ -34,4 +35,8 @@ var video_decoder_1 = require("./video-decoder");
34
35
  Object.defineProperty(exports, "createVideoDecoder", { enumerable: true, get: function () { return video_decoder_1.createVideoDecoder; } });
35
36
  var video_encoder_1 = require("./video-encoder");
36
37
  Object.defineProperty(exports, "createVideoEncoder", { enumerable: true, get: function () { return video_encoder_1.createVideoEncoder; } });
38
+ exports.WebCodecsInternals = {
39
+ rotateVideoFrame: rotate_video_frame_1.rotateVideoFrame,
40
+ normalizeVideoRotation: rotate_video_frame_1.normalizeVideoRotation,
41
+ };
37
42
  (0, set_remotion_imported_1.setRemotionImported)();
@@ -6,10 +6,14 @@ type Output = {
6
6
  timestamp: number;
7
7
  };
8
8
  type Processed = {};
9
+ type Progress = {
10
+ smallestProgress: number;
11
+ };
9
12
  type IoEventMap = {
10
13
  input: Input;
11
14
  output: Output;
12
15
  processed: Processed;
16
+ progress: Progress;
13
17
  };
14
18
  export type IoEventTypes = keyof IoEventMap;
15
19
  export type CallbackListener<T extends IoEventTypes> = (data: {
@@ -7,6 +7,7 @@ class IoEventEmitter {
7
7
  input: [],
8
8
  output: [],
9
9
  processed: [],
10
+ progress: [],
10
11
  };
11
12
  }
12
13
  addEventListener(name, callback) {
@@ -1,10 +1,16 @@
1
+ import type { ProgressTracker } from '@remotion/media-parser';
1
2
  import type { LogLevel } from '../log';
2
- export declare const makeIoSynchronizer: (logLevel: LogLevel, label: string) => {
3
+ export declare const makeIoSynchronizer: ({ logLevel, label, progress, }: {
4
+ logLevel: LogLevel;
5
+ label: string;
6
+ progress: ProgressTracker;
7
+ }) => {
3
8
  inputItem: (timestamp: number, keyFrame: boolean) => void;
4
9
  onOutput: (timestamp: number) => void;
5
- waitFor: ({ _unprocessed, unemitted, }: {
10
+ waitFor: ({ _unprocessed, unemitted, minimumProgress, }: {
6
11
  unemitted: number;
7
12
  _unprocessed: number;
13
+ minimumProgress: number | null;
8
14
  }) => Promise<void>;
9
15
  waitForFinish: () => Promise<void>;
10
16
  onProcessed: () => void;
@@ -1,11 +1,11 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.makeIoSynchronizer = void 0;
4
+ const media_parser_1 = require("@remotion/media-parser");
4
5
  const log_1 = require("../log");
5
- const with_resolvers_1 = require("../with-resolvers");
6
- const event_emitter_1 = require("./event-emitter");
7
- const makeIoSynchronizer = (logLevel, label) => {
8
- const eventEmitter = new event_emitter_1.IoEventEmitter();
6
+ const make_timeout_promise_1 = require("./make-timeout-promise");
7
+ const makeIoSynchronizer = ({ logLevel, label, progress, }) => {
8
+ const eventEmitter = new media_parser_1.MediaParserInternals.IoEventEmitter();
9
9
  let lastInput = 0;
10
10
  let lastInputKeyframe = 0;
11
11
  let lastOutput = 0;
@@ -51,7 +51,7 @@ const makeIoSynchronizer = (logLevel, label) => {
51
51
  printState('Got output');
52
52
  };
53
53
  const waitForOutput = () => {
54
- const { promise, resolve } = (0, with_resolvers_1.withResolvers)();
54
+ const { promise, resolve } = media_parser_1.MediaParserInternals.withResolvers();
55
55
  const on = () => {
56
56
  eventEmitter.removeEventListener('output', on);
57
57
  resolve();
@@ -60,7 +60,7 @@ const makeIoSynchronizer = (logLevel, label) => {
60
60
  return promise;
61
61
  };
62
62
  const waitForProcessed = () => {
63
- const { promise, resolve } = (0, with_resolvers_1.withResolvers)();
63
+ const { promise, resolve } = media_parser_1.MediaParserInternals.withResolvers();
64
64
  const on = () => {
65
65
  eventEmitter.removeEventListener('processed', on);
66
66
  resolve();
@@ -68,22 +68,33 @@ const makeIoSynchronizer = (logLevel, label) => {
68
68
  eventEmitter.addEventListener('processed', on);
69
69
  return promise;
70
70
  };
71
- const waitFor = async ({ _unprocessed, unemitted, }) => {
72
- await Promise.all([
73
- async () => {
74
- while (getUnemittedItems() > unemitted) {
75
- await waitForOutput();
76
- }
77
- },
78
- async () => {
79
- while (getUnprocessed() > _unprocessed) {
80
- await waitForProcessed();
81
- }
82
- },
83
- ]);
71
+ const waitFor = async ({ _unprocessed, unemitted, minimumProgress, }) => {
72
+ const { timeoutPromise, clear } = (0, make_timeout_promise_1.makeTimeoutPromise)(`Waited too long for ${label}`, 10000);
73
+ await Promise.race([
74
+ timeoutPromise,
75
+ Promise.all([
76
+ (async () => {
77
+ while (getUnemittedItems() > unemitted) {
78
+ await waitForOutput();
79
+ }
80
+ })(),
81
+ (async () => {
82
+ while (getUnprocessed() > _unprocessed) {
83
+ await waitForProcessed();
84
+ }
85
+ })(),
86
+ minimumProgress === null
87
+ ? Promise.resolve()
88
+ : (async () => {
89
+ while (progress.getSmallestProgress() < minimumProgress) {
90
+ await progress.waitForProgress();
91
+ }
92
+ })(),
93
+ ]),
94
+ ]).finally(() => clear());
84
95
  };
85
96
  const waitForFinish = async () => {
86
- await waitFor({ _unprocessed: 0, unemitted: 0 });
97
+ await waitFor({ _unprocessed: 0, unemitted: 0, minimumProgress: null });
87
98
  };
88
99
  const onProcessed = () => {
89
100
  eventEmitter.dispatchEvent('processed', {});
@@ -0,0 +1,4 @@
1
+ export declare const makeTimeoutPromise: (label: string, ms: number) => {
2
+ timeoutPromise: Promise<void>;
3
+ clear: () => void;
4
+ };
@@ -0,0 +1,18 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.makeTimeoutPromise = void 0;
4
+ const media_parser_1 = require("@remotion/media-parser");
5
+ const makeTimeoutPromise = (label, ms) => {
6
+ const { promise, reject, resolve } = media_parser_1.MediaParserInternals.withResolvers();
7
+ const timeout = setTimeout(() => {
8
+ reject(new Error(`${label} (timed out after ${ms}ms)`));
9
+ }, ms);
10
+ return {
11
+ timeoutPromise: promise,
12
+ clear: () => {
13
+ clearTimeout(timeout);
14
+ resolve();
15
+ },
16
+ };
17
+ };
18
+ exports.makeTimeoutPromise = makeTimeoutPromise;
@@ -1,10 +1,10 @@
1
- import type { LogLevel, MediaFn, OnAudioTrack } from '@remotion/media-parser';
1
+ import type { LogLevel, MediaFn, OnAudioTrack, ProgressTracker } from '@remotion/media-parser';
2
2
  import Error from './error-cause';
3
3
  import type { ConvertMediaAudioCodec } from './get-available-audio-codecs';
4
4
  import type { ConvertMediaContainer } from './get-available-containers';
5
5
  import type { ConvertMediaOnAudioTrackHandler } from './on-audio-track-handler';
6
6
  import type { ConvertMediaProgressFn } from './throttled-state-update';
7
- export declare const makeAudioTrackHandler: ({ state, defaultAudioCodec: audioCodec, controller, abortConversion, onMediaStateUpdate, onAudioTrack, logLevel, container, }: {
7
+ export declare const makeAudioTrackHandler: ({ state, defaultAudioCodec: audioCodec, controller, abortConversion, onMediaStateUpdate, onAudioTrack, logLevel, container, progressTracker, }: {
8
8
  state: MediaFn;
9
9
  defaultAudioCodec: ConvertMediaAudioCodec | null;
10
10
  controller: AbortController;
@@ -13,4 +13,5 @@ export declare const makeAudioTrackHandler: ({ state, defaultAudioCodec: audioCo
13
13
  onAudioTrack: ConvertMediaOnAudioTrackHandler | null;
14
14
  logLevel: LogLevel;
15
15
  container: ConvertMediaContainer;
16
+ progressTracker: ProgressTracker;
16
17
  }) => OnAudioTrack;
@@ -12,7 +12,7 @@ const convert_encoded_chunk_1 = require("./convert-encoded-chunk");
12
12
  const default_on_audio_track_handler_1 = require("./default-on-audio-track-handler");
13
13
  const error_cause_1 = __importDefault(require("./error-cause"));
14
14
  const log_1 = require("./log");
15
- const makeAudioTrackHandler = ({ state, defaultAudioCodec: audioCodec, controller, abortConversion, onMediaStateUpdate, onAudioTrack, logLevel, container, }) => async (track) => {
15
+ const makeAudioTrackHandler = ({ state, defaultAudioCodec: audioCodec, controller, abortConversion, onMediaStateUpdate, onAudioTrack, logLevel, container, progressTracker, }) => async (track) => {
16
16
  const audioOperation = await (onAudioTrack !== null && onAudioTrack !== void 0 ? onAudioTrack : default_on_audio_track_handler_1.defaultOnAudioTrackHandler)({
17
17
  defaultAudioCodec: audioCodec,
18
18
  track,
@@ -62,7 +62,7 @@ const makeAudioTrackHandler = ({ state, defaultAudioCodec: audioCodec, controlle
62
62
  numberOfChannels: track.numberOfChannels,
63
63
  sampleRate: track.sampleRate,
64
64
  description: track.description,
65
- });
65
+ }, logLevel);
66
66
  if (!audioEncoderConfig) {
67
67
  abortConversion(new error_cause_1.default(`Could not configure audio encoder of track ${track.trackId}`));
68
68
  return null;
@@ -114,6 +114,7 @@ const makeAudioTrackHandler = ({ state, defaultAudioCodec: audioCodec, controlle
114
114
  signal: controller.signal,
115
115
  config: audioEncoderConfig,
116
116
  logLevel,
117
+ progressTracker,
117
118
  });
118
119
  const audioDecoder = (0, audio_decoder_1.createAudioDecoder)({
119
120
  onFrame: async (frame) => {
@@ -127,13 +128,15 @@ const makeAudioTrackHandler = ({ state, defaultAudioCodec: audioCodec, controlle
127
128
  frame.close();
128
129
  },
129
130
  onError(error) {
130
- abortConversion(new error_cause_1.default(`Audio decoder of track ${track.trackId} failed (see .cause of this error)`, {
131
+ abortConversion(new error_cause_1.default(`Audio decoder of track ${track.trackId} failed. Config: ${JSON.stringify(audioDecoderConfig)} (see .cause of this error)`, {
131
132
  cause: error,
132
133
  }));
133
134
  },
134
135
  signal: controller.signal,
135
136
  config: audioDecoderConfig,
136
137
  logLevel,
138
+ track,
139
+ progressTracker,
137
140
  });
138
141
  state.addWaitForFinishPromise(async () => {
139
142
  await audioDecoder.waitForFinish();
@@ -2,10 +2,11 @@ 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
4
  import type { WebCodecsVideoEncoder } from './video-encoder';
5
- export declare const onFrame: ({ frame, onVideoFrame, videoEncoder, track, outputCodec, }: {
5
+ export declare const onFrame: ({ frame: unrotatedFrame, onVideoFrame, videoEncoder, track, outputCodec, rotation, }: {
6
6
  frame: VideoFrame;
7
7
  onVideoFrame: ConvertMediaOnVideoFrame | null;
8
8
  videoEncoder: WebCodecsVideoEncoder;
9
9
  track: VideoTrack;
10
10
  outputCodec: ConvertMediaVideoCodec;
11
+ rotation: number;
11
12
  }) => Promise<void>;
package/dist/on-frame.js CHANGED
@@ -2,30 +2,41 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.onFrame = void 0;
4
4
  const convert_to_correct_videoframe_1 = require("./convert-to-correct-videoframe");
5
- const onFrame = async ({ frame, onVideoFrame, videoEncoder, track, outputCodec, }) => {
6
- const newFrame = onVideoFrame ? await onVideoFrame({ frame, track }) : frame;
7
- if (newFrame.displayWidth !== frame.displayWidth) {
8
- throw new Error(`Returned VideoFrame of track ${track.trackId} has different displayWidth (${newFrame.displayWidth}) than the input frame (${newFrame.displayHeight})`);
5
+ const rotate_video_frame_1 = require("./rotate-video-frame");
6
+ const onFrame = async ({ frame: unrotatedFrame, onVideoFrame, videoEncoder, track, outputCodec, rotation, }) => {
7
+ var _a, _b;
8
+ const rotated = (0, rotate_video_frame_1.rotateVideoFrame)({
9
+ rotation,
10
+ frame: unrotatedFrame,
11
+ });
12
+ if (unrotatedFrame !== rotated) {
13
+ unrotatedFrame.close();
14
+ }
15
+ const userProcessedFrame = onVideoFrame
16
+ ? await onVideoFrame({ frame: rotated, track })
17
+ : rotated;
18
+ if (userProcessedFrame.displayWidth !== rotated.displayWidth) {
19
+ throw new Error(`Returned VideoFrame of track ${track.trackId} has different displayWidth (${userProcessedFrame.displayWidth}) than the input frame (${userProcessedFrame.displayHeight})`);
9
20
  }
10
- if (newFrame.displayHeight !== frame.displayHeight) {
11
- throw new Error(`Returned VideoFrame of track ${track.trackId} has different displayHeight (${newFrame.displayHeight}) than the input frame (${newFrame.displayHeight})`);
21
+ if (userProcessedFrame.displayHeight !== rotated.displayHeight) {
22
+ throw new Error(`Returned VideoFrame of track ${track.trackId} has different displayHeight (${userProcessedFrame.displayHeight}) than the input frame (${userProcessedFrame.displayHeight})`);
12
23
  }
13
- if (newFrame.timestamp !== frame.timestamp) {
14
- throw new Error(`Returned VideoFrame of track ${track.trackId} has different timestamp (${newFrame.timestamp}) than the input frame (${newFrame.timestamp}). When calling new VideoFrame(), pass {timestamp: frame.timestamp} as second argument`);
24
+ if (userProcessedFrame.timestamp !== rotated.timestamp) {
25
+ throw new Error(`Returned VideoFrame of track ${track.trackId} has different timestamp (${userProcessedFrame.timestamp}) than the input frame (${rotated.timestamp}). When calling new VideoFrame(), pass {timestamp: frame.timestamp} as second argument`);
15
26
  }
16
- if (newFrame.duration !== frame.duration) {
17
- throw new Error(`Returned VideoFrame of track ${track.trackId} has different duration (${newFrame.duration}) than the input frame (${newFrame.duration}). When calling new VideoFrame(), pass {duration: frame.duration} as second argument`);
27
+ if (((_a = userProcessedFrame.duration) !== null && _a !== void 0 ? _a : 0) !== ((_b = rotated.duration) !== null && _b !== void 0 ? _b : 0)) {
28
+ throw new Error(`Returned VideoFrame of track ${track.trackId} has different duration (${userProcessedFrame.duration}) than the input frame (${rotated.duration}). When calling new VideoFrame(), pass {duration: frame.duration} as second argument`);
18
29
  }
19
30
  const fixedFrame = (0, convert_to_correct_videoframe_1.convertToCorrectVideoFrame)({
20
- videoFrame: newFrame,
31
+ videoFrame: userProcessedFrame,
21
32
  outputCodec,
22
33
  });
23
34
  await videoEncoder.encodeFrame(fixedFrame, fixedFrame.timestamp);
24
35
  fixedFrame.close();
25
- if (frame !== newFrame) {
26
- frame.close();
36
+ if (rotated !== userProcessedFrame) {
37
+ rotated.close();
27
38
  }
28
- if (fixedFrame !== newFrame) {
39
+ if (fixedFrame !== userProcessedFrame) {
29
40
  fixedFrame.close();
30
41
  }
31
42
  };
@@ -4,6 +4,7 @@ import type { ConvertMediaVideoCodec } from './get-available-video-codecs';
4
4
  export type VideoOperation = {
5
5
  type: 'reencode';
6
6
  videoCodec: ConvertMediaVideoCodec;
7
+ rotate?: number;
7
8
  } | {
8
9
  type: 'copy';
9
10
  } | {
@@ -16,4 +17,5 @@ export type ConvertMediaOnVideoTrackHandler = (options: {
16
17
  track: VideoTrack;
17
18
  logLevel: LogLevel;
18
19
  container: ConvertMediaContainer;
20
+ rotate: number;
19
21
  }) => VideoOperation | Promise<VideoOperation>;
@@ -1,11 +1,11 @@
1
- import type { LogLevel, MediaFn, OnVideoTrack } from '@remotion/media-parser';
1
+ import type { LogLevel, MediaFn, OnVideoTrack, ProgressTracker } from '@remotion/media-parser';
2
2
  import type { ConvertMediaOnVideoFrame } from './convert-media';
3
3
  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
7
  import type { ConvertMediaProgressFn } from './throttled-state-update';
8
- export declare const makeVideoTrackHandler: ({ state, onVideoFrame, onMediaStateUpdate, abortConversion, controller, defaultVideoCodec, onVideoTrack, logLevel, container, }: {
8
+ export declare const makeVideoTrackHandler: ({ state, onVideoFrame, onMediaStateUpdate, abortConversion, controller, defaultVideoCodec, onVideoTrack, logLevel, container, rotate, progress, }: {
9
9
  state: MediaFn;
10
10
  onVideoFrame: null | ConvertMediaOnVideoFrame;
11
11
  onMediaStateUpdate: null | ConvertMediaProgressFn;
@@ -15,4 +15,6 @@ export declare const makeVideoTrackHandler: ({ state, onVideoFrame, onMediaState
15
15
  onVideoTrack: ConvertMediaOnVideoTrackHandler | null;
16
16
  logLevel: LogLevel;
17
17
  container: ConvertMediaContainer;
18
+ rotate: number;
19
+ progress: ProgressTracker;
18
20
  }) => OnVideoTrack;
@@ -10,11 +10,13 @@ const default_on_video_track_handler_1 = require("./default-on-video-track-handl
10
10
  const error_cause_1 = __importDefault(require("./error-cause"));
11
11
  const log_1 = require("./log");
12
12
  const on_frame_1 = require("./on-frame");
13
+ const rotation_1 = require("./rotation");
13
14
  const video_decoder_1 = require("./video-decoder");
14
15
  const video_decoder_config_1 = require("./video-decoder-config");
15
16
  const video_encoder_1 = require("./video-encoder");
16
17
  const video_encoder_config_1 = require("./video-encoder-config");
17
- const makeVideoTrackHandler = ({ state, onVideoFrame, onMediaStateUpdate, abortConversion, controller, defaultVideoCodec, onVideoTrack, logLevel, container, }) => async (track) => {
18
+ const makeVideoTrackHandler = ({ state, onVideoFrame, onMediaStateUpdate, abortConversion, controller, defaultVideoCodec, onVideoTrack, logLevel, container, rotate, progress, }) => async (track) => {
19
+ var _a;
18
20
  if (controller.signal.aborted) {
19
21
  throw new error_cause_1.default('Aborted');
20
22
  }
@@ -23,6 +25,7 @@ const makeVideoTrackHandler = ({ state, onVideoFrame, onMediaStateUpdate, abortC
23
25
  defaultVideoCodec,
24
26
  logLevel,
25
27
  container,
28
+ rotate,
26
29
  });
27
30
  if (videoOperation.type === 'drop') {
28
31
  return null;
@@ -57,10 +60,19 @@ const makeVideoTrackHandler = ({ state, onVideoFrame, onMediaStateUpdate, abortC
57
60
  });
58
61
  };
59
62
  }
63
+ if (videoOperation.type !== 'reencode') {
64
+ 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"`);
65
+ }
66
+ const rotation = (_a = videoOperation.rotate) !== null && _a !== void 0 ? _a : -track.rotation;
67
+ const { height: newHeight, width: newWidth } = (0, rotation_1.calculateNewDimensionsFromDimensions)({
68
+ width: track.codedWidth,
69
+ height: track.codedHeight,
70
+ rotation,
71
+ });
60
72
  const videoEncoderConfig = await (0, video_encoder_config_1.getVideoEncoderConfig)({
61
73
  codec: videoOperation.videoCodec,
62
- height: track.displayAspectHeight,
63
- width: track.displayAspectWidth,
74
+ height: newHeight,
75
+ width: newWidth,
64
76
  fps: track.fps,
65
77
  });
66
78
  const videoDecoderConfig = await (0, video_decoder_config_1.getVideoDecoderConfigWithHardwareAcceleration)(track);
@@ -75,8 +87,8 @@ const makeVideoTrackHandler = ({ state, onVideoFrame, onMediaStateUpdate, abortC
75
87
  const { trackNumber } = await state.addTrack({
76
88
  type: 'video',
77
89
  color: track.color,
78
- width: track.codedWidth,
79
- height: track.codedHeight,
90
+ width: newWidth,
91
+ height: newHeight,
80
92
  codec: videoOperation.videoCodec,
81
93
  codecPrivate: null,
82
94
  timescale: track.timescale,
@@ -108,6 +120,7 @@ const makeVideoTrackHandler = ({ state, onVideoFrame, onMediaStateUpdate, abortC
108
120
  config: videoEncoderConfig,
109
121
  logLevel,
110
122
  outputCodec: videoOperation.videoCodec,
123
+ progress,
111
124
  });
112
125
  const videoDecoder = (0, video_decoder_1.createVideoDecoder)({
113
126
  config: videoDecoderConfig,
@@ -118,6 +131,7 @@ const makeVideoTrackHandler = ({ state, onVideoFrame, onMediaStateUpdate, abortC
118
131
  videoEncoder,
119
132
  onVideoFrame,
120
133
  outputCodec: videoOperation.videoCodec,
134
+ rotation,
121
135
  });
122
136
  },
123
137
  onError: (err) => {
@@ -127,6 +141,7 @@ const makeVideoTrackHandler = ({ state, onVideoFrame, onMediaStateUpdate, abortC
127
141
  },
128
142
  signal: controller.signal,
129
143
  logLevel,
144
+ progress,
130
145
  });
131
146
  state.addWaitForFinishPromise(async () => {
132
147
  log_1.Log.verbose(logLevel, 'Waiting for video decoder to finish');
@@ -0,0 +1,5 @@
1
+ export declare const normalizeVideoRotation: (rotation: number) => number;
2
+ export declare const rotateVideoFrame: ({ frame, rotation, }: {
3
+ frame: VideoFrame;
4
+ rotation: number;
5
+ }) => VideoFrame;
@@ -0,0 +1,48 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.rotateVideoFrame = 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 rotateVideoFrame = ({ frame, rotation, }) => {
10
+ var _a;
11
+ const normalized = ((rotation % 360) + 360) % 360;
12
+ if (normalized % 360 === 0) {
13
+ return frame;
14
+ }
15
+ if (normalized % 90 !== 0) {
16
+ throw new Error('Only 90 degree rotations are supported');
17
+ }
18
+ const { height, width } = (0, rotation_1.calculateNewDimensionsFromDimensions)({
19
+ height: frame.displayHeight,
20
+ width: frame.displayWidth,
21
+ rotation,
22
+ });
23
+ const canvas = new OffscreenCanvas(width, height);
24
+ const ctx = canvas.getContext('2d');
25
+ if (!ctx) {
26
+ throw new Error('Could not get 2d context');
27
+ }
28
+ canvas.width = width;
29
+ canvas.height = height;
30
+ if (normalized === 90) {
31
+ ctx.translate(width, 0);
32
+ }
33
+ else if (normalized === 180) {
34
+ ctx.translate(width, height);
35
+ }
36
+ else if (normalized === 270) {
37
+ ctx.translate(0, height);
38
+ }
39
+ ctx.rotate(normalized * (Math.PI / 180));
40
+ ctx.drawImage(frame, 0, 0);
41
+ return new VideoFrame(canvas, {
42
+ displayHeight: height,
43
+ displayWidth: width,
44
+ duration: (_a = frame.duration) !== null && _a !== void 0 ? _a : undefined,
45
+ timestamp: frame.timestamp,
46
+ });
47
+ };
48
+ exports.rotateVideoFrame = rotateVideoFrame;
@@ -0,0 +1,4 @@
1
+ export declare const rotateVideo: ({ frame, rotation, }: {
2
+ frame: VideoFrame;
3
+ rotation: number;
4
+ }) => VideoFrame;
@@ -0,0 +1,43 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.rotateVideo = void 0;
4
+ const rotation_1 = require("./rotation");
5
+ const rotateVideo = ({ frame, rotation, }) => {
6
+ const normalized = ((rotation % 360) + 360) % 360;
7
+ if (normalized % 360 === 0) {
8
+ return frame;
9
+ }
10
+ if (normalized % 90 !== 0) {
11
+ throw new Error('Only 90 degree rotations are supported');
12
+ }
13
+ const { height, width } = (0, rotation_1.calculateNewDimensionsFromDimensions)({
14
+ height: frame.displayHeight,
15
+ width: frame.displayWidth,
16
+ rotation,
17
+ });
18
+ const canvas = new OffscreenCanvas(width, height);
19
+ const ctx = canvas.getContext('2d');
20
+ if (!ctx) {
21
+ throw new Error('Could not get 2d context');
22
+ }
23
+ canvas.width = width;
24
+ canvas.height = height;
25
+ if (normalized === 90) {
26
+ ctx.translate(width, 0);
27
+ }
28
+ else if (normalized === 180) {
29
+ ctx.translate(width, height);
30
+ }
31
+ else if (normalized === 270) {
32
+ ctx.translate(0, height);
33
+ }
34
+ ctx.rotate(normalized * (Math.PI / 180));
35
+ ctx.drawImage(frame, 0, 0);
36
+ return new VideoFrame(canvas, {
37
+ displayHeight: height,
38
+ displayWidth: width,
39
+ duration: frame.duration,
40
+ timestamp: frame.timestamp,
41
+ });
42
+ };
43
+ exports.rotateVideo = rotateVideo;
@@ -0,0 +1,8 @@
1
+ export declare const calculateNewDimensionsFromDimensions: ({ width, height, rotation, }: {
2
+ width: number;
3
+ height: number;
4
+ rotation: number;
5
+ }) => {
6
+ height: number;
7
+ width: number;
8
+ };
@@ -0,0 +1,10 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.calculateNewDimensionsFromDimensions = void 0;
4
+ const calculateNewDimensionsFromDimensions = ({ width, height, rotation, }) => {
5
+ const switchDimensions = rotation % 90 === 0 && rotation % 180 !== 0;
6
+ const newHeight = switchDimensions ? width : height;
7
+ const newWidth = switchDimensions ? height : width;
8
+ return { height: newHeight, width: newWidth };
9
+ };
10
+ exports.calculateNewDimensionsFromDimensions = calculateNewDimensionsFromDimensions;
@@ -1,2 +1,2 @@
1
1
  import type { ConvertMediaContainer } from './get-available-containers';
2
- export declare const selectContainerCreator: (container: ConvertMediaContainer) => ({ writer, onBytesProgress, onMillisecondsProgress, logLevel, filename, }: import("@remotion/media-parser/dist/create/media-fn").MediaFnGeneratorInput) => Promise<import("@remotion/media-parser").MediaFn>;
2
+ export declare const selectContainerCreator: (container: ConvertMediaContainer) => ({ writer, onBytesProgress, onMillisecondsProgress, logLevel, filename, progressTracker, }: import("@remotion/media-parser/dist/create/media-fn").MediaFnGeneratorInput) => Promise<import("@remotion/media-parser").MediaFn>;
@@ -0,0 +1,15 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const example_videos_1 = require("@remotion/example-videos");
4
+ const node_1 = require("@remotion/media-parser/node");
5
+ const bun_test_1 = require("bun:test");
6
+ const convert_media_1 = require("../convert-media");
7
+ (0, bun_test_1.test)('Convert AVI to MP4', async () => {
8
+ const output = await (0, convert_media_1.convertMedia)({
9
+ src: example_videos_1.exampleVideos.avi,
10
+ container: 'mp4',
11
+ reader: node_1.nodeReader,
12
+ });
13
+ const f = await output.save();
14
+ (0, bun_test_1.expect)(f.size).toBeGreaterThan(0);
15
+ });
@@ -1,14 +1,15 @@
1
- import type { AudioOrVideoSample, LogLevel } from '@remotion/media-parser';
1
+ import type { AudioOrVideoSample, LogLevel, ProgressTracker } from '@remotion/media-parser';
2
2
  export type WebCodecsVideoDecoder = {
3
3
  processSample: (videoSample: AudioOrVideoSample) => Promise<void>;
4
4
  waitForFinish: () => Promise<void>;
5
5
  close: () => void;
6
6
  flush: () => Promise<void>;
7
7
  };
8
- export declare const createVideoDecoder: ({ onFrame, onError, signal, config, logLevel, }: {
8
+ export declare const createVideoDecoder: ({ onFrame, onError, signal, config, logLevel, progress, }: {
9
9
  onFrame: (frame: VideoFrame) => Promise<void>;
10
10
  onError: (error: DOMException) => void;
11
11
  signal: AbortSignal;
12
12
  config: VideoDecoderConfig;
13
13
  logLevel: LogLevel;
14
+ progress: ProgressTracker;
14
15
  }) => WebCodecsVideoDecoder;