@remotion/webcodecs 4.0.305 → 4.0.308

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 (55) hide show
  1. package/dist/audio-decoder.d.ts +15 -10
  2. package/dist/audio-decoder.js +49 -52
  3. package/dist/audio-encoder.d.ts +5 -5
  4. package/dist/audio-encoder.js +20 -42
  5. package/dist/can-copy-audio-track.d.ts +3 -1
  6. package/dist/can-copy-audio-track.js +10 -1
  7. package/dist/can-copy-video-track.d.ts +3 -1
  8. package/dist/can-copy-video-track.js +10 -1
  9. package/dist/convert-media.js +2 -2
  10. package/dist/copy-audio-track.d.ts +11 -0
  11. package/dist/copy-audio-track.js +31 -0
  12. package/dist/copy-video-track.d.ts +11 -0
  13. package/dist/copy-video-track.js +32 -0
  14. package/dist/create/event-emitter.d.ts +0 -1
  15. package/dist/create/matroska/matroska-utils.d.ts +1 -1
  16. package/dist/create/progress-tracker.d.ts +0 -2
  17. package/dist/create/progress-tracker.js +3 -20
  18. package/dist/create-audio-decoder.d.ts +24 -0
  19. package/dist/create-audio-decoder.js +112 -0
  20. package/dist/create-video-decoder.d.ts +23 -0
  21. package/dist/create-video-decoder.js +89 -0
  22. package/dist/esm/index.mjs +794 -631
  23. package/dist/get-wave-audio-decoder.d.ts +7 -2
  24. package/dist/get-wave-audio-decoder.js +27 -13
  25. package/dist/index.d.ts +4 -4
  26. package/dist/index.js +5 -5
  27. package/dist/io-manager/io-synchronizer.d.ts +5 -13
  28. package/dist/io-manager/io-synchronizer.js +29 -74
  29. package/dist/io-manager/make-timeout-promise.d.ts +1 -1
  30. package/dist/io-manager/make-timeout-promise.js +8 -4
  31. package/dist/is-different-video-codec.d.ts +11 -0
  32. package/dist/is-different-video-codec.js +36 -0
  33. package/dist/on-audio-track.d.ts +2 -2
  34. package/dist/on-audio-track.js +16 -150
  35. package/dist/on-frame.d.ts +2 -4
  36. package/dist/on-frame.js +8 -9
  37. package/dist/on-video-track.d.ts +2 -2
  38. package/dist/on-video-track.js +19 -129
  39. package/dist/processing-queue.d.ts +18 -0
  40. package/dist/processing-queue.js +47 -0
  41. package/dist/reencode-audio-track.d.ts +18 -0
  42. package/dist/reencode-audio-track.js +164 -0
  43. package/dist/reencode-video-track.d.ts +19 -0
  44. package/dist/reencode-video-track.js +151 -0
  45. package/dist/sort-video-frames.d.ts +4 -3
  46. package/dist/sort-video-frames.js +7 -3
  47. package/dist/video-decoder.d.ts +14 -8
  48. package/dist/video-decoder.js +37 -72
  49. package/dist/video-encoder.d.ts +6 -5
  50. package/dist/video-encoder.js +16 -40
  51. package/dist/wav-audio-encoder.d.ts +4 -1
  52. package/dist/wav-audio-encoder.js +3 -2
  53. package/package.json +5 -5
  54. package/dist/select-container-creator.d.ts +0 -2
  55. package/dist/select-container-creator.js +0 -19
@@ -1,4 +1,9 @@
1
- import type { CreateAudioDecoderInit, WebCodecsAudioDecoder } from './audio-decoder';
2
- export declare const getWaveAudioDecoder: ({ onFrame, track, sampleFormat, }: Pick<CreateAudioDecoderInit, "onFrame" | "track"> & {
1
+ import type { MediaParserLogLevel } from '@remotion/media-parser';
2
+ import type { CreateAudioDecoderInit, WebCodecsAudioDecoder } from './create-audio-decoder';
3
+ import type { IoSynchronizer } from './io-manager/io-synchronizer';
4
+ export declare const getWaveAudioDecoder: ({ onFrame, config, sampleFormat, ioSynchronizer, onError, }: Pick<CreateAudioDecoderInit, "onFrame" | "config"> & {
3
5
  sampleFormat: AudioSampleFormat;
6
+ logLevel: MediaParserLogLevel;
7
+ ioSynchronizer: IoSynchronizer;
8
+ onError: (error: Error) => void;
4
9
  }) => WebCodecsAudioDecoder;
@@ -28,30 +28,44 @@ const getBytesPerSample = (sampleFormat) => {
28
28
  }
29
29
  throw new Error(`Unsupported sample format: ${sampleFormat}`);
30
30
  };
31
- // TODO: Should also be subject to throttling
32
- const getWaveAudioDecoder = ({ onFrame, track, sampleFormat, }) => {
33
- let queue = Promise.resolve();
31
+ const getAudioData = (audioSample) => {
32
+ if (audioSample instanceof EncodedAudioChunk) {
33
+ const data = new Uint8Array(audioSample.byteLength);
34
+ audioSample.copyTo(data);
35
+ return data;
36
+ }
37
+ return audioSample.data;
38
+ };
39
+ const getWaveAudioDecoder = ({ onFrame, config, sampleFormat, ioSynchronizer, onError, }) => {
34
40
  const processSample = async (audioSample) => {
35
41
  const bytesPerSample = getBytesPerSample(sampleFormat);
36
- await onFrame(new AudioData({
37
- data: audioSample.data,
42
+ const data = getAudioData(audioSample);
43
+ const audioData = new AudioData({
44
+ data,
38
45
  format: sampleFormat,
39
- numberOfChannels: track.numberOfChannels,
40
- numberOfFrames: audioSample.data.byteLength / bytesPerSample / track.numberOfChannels,
41
- sampleRate: track.sampleRate,
46
+ numberOfChannels: config.numberOfChannels,
47
+ numberOfFrames: data.byteLength / bytesPerSample / config.numberOfChannels,
48
+ sampleRate: config.sampleRate,
42
49
  timestamp: audioSample.timestamp,
43
- }));
50
+ });
51
+ try {
52
+ await onFrame(audioData);
53
+ }
54
+ catch (err) {
55
+ audioData.close();
56
+ onError(err);
57
+ }
44
58
  };
45
59
  return {
46
60
  close() {
47
61
  return Promise.resolve();
48
62
  },
49
- processSample(audioSample) {
50
- queue = queue.then(() => processSample(audioSample));
51
- return queue;
63
+ decode(audioSample) {
64
+ return processSample(audioSample);
52
65
  },
53
66
  flush: () => Promise.resolve(),
54
- waitForFinish: () => Promise.resolve(),
67
+ waitForQueueToBeLessThan: ioSynchronizer.waitForQueueSize,
68
+ reset: () => { },
55
69
  };
56
70
  };
57
71
  exports.getWaveAudioDecoder = getWaveAudioDecoder;
package/dist/index.d.ts CHANGED
@@ -1,5 +1,3 @@
1
- export { createAudioDecoder } from './audio-decoder';
2
- export type { WebCodecsAudioDecoder } from './audio-decoder';
3
1
  export { createAudioEncoder } from './audio-encoder';
4
2
  export type { WebCodecsAudioEncoder } from './audio-encoder';
5
3
  export { canCopyAudioTrack } from './can-copy-audio-track';
@@ -9,6 +7,10 @@ export { canReencodeVideoTrack } from './can-reencode-video-track';
9
7
  export { convertAudioData, ConvertAudioDataOptions } from './convert-audiodata';
10
8
  export { convertMedia } from './convert-media';
11
9
  export type { ConvertMediaOnAudioData, ConvertMediaOnProgress, ConvertMediaOnVideoFrame, ConvertMediaProgress, ConvertMediaResult, } from './convert-media';
10
+ export { createAudioDecoder } from './create-audio-decoder';
11
+ export type { WebCodecsAudioDecoder } from './create-audio-decoder';
12
+ export { createVideoDecoder } from './create-video-decoder';
13
+ export type { WebCodecsVideoDecoder } from './create-video-decoder';
12
14
  export { defaultOnAudioTrackHandler } from './default-on-audio-track-handler';
13
15
  export { defaultOnVideoTrackHandler } from './default-on-video-track-handler';
14
16
  export { getAvailableAudioCodecs } from './get-available-audio-codecs';
@@ -22,8 +24,6 @@ export { getDefaultVideoCodec } from './get-default-video-codec';
22
24
  export type { AudioOperation, ConvertMediaOnAudioTrackHandler, } from './on-audio-track-handler';
23
25
  export type { ConvertMediaOnVideoTrackHandler, VideoOperation, } from './on-video-track-handler';
24
26
  export type { ResizeOperation } from './resizing/mode';
25
- export { createVideoDecoder } from './video-decoder';
26
- export type { WebCodecsVideoDecoder } from './video-decoder';
27
27
  export { createVideoEncoder } from './video-encoder';
28
28
  export type { WebCodecsVideoEncoder } from './video-encoder';
29
29
  export { webcodecsController } from './webcodecs-controller';
package/dist/index.js CHANGED
@@ -1,11 +1,9 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.WebCodecsInternals = exports.webcodecsController = exports.createVideoEncoder = exports.createVideoDecoder = exports.getDefaultVideoCodec = exports.getDefaultAudioCodec = exports.getAvailableVideoCodecs = exports.getAvailableContainers = exports.getAvailableAudioCodecs = exports.defaultOnVideoTrackHandler = exports.defaultOnAudioTrackHandler = exports.convertMedia = exports.convertAudioData = exports.canReencodeVideoTrack = exports.canReencodeAudioTrack = exports.canCopyVideoTrack = exports.canCopyAudioTrack = exports.createAudioEncoder = exports.createAudioDecoder = void 0;
3
+ exports.WebCodecsInternals = exports.webcodecsController = exports.createVideoEncoder = exports.getDefaultVideoCodec = exports.getDefaultAudioCodec = exports.getAvailableVideoCodecs = exports.getAvailableContainers = exports.getAvailableAudioCodecs = exports.defaultOnVideoTrackHandler = exports.defaultOnAudioTrackHandler = exports.createVideoDecoder = exports.createAudioDecoder = exports.convertMedia = exports.convertAudioData = exports.canReencodeVideoTrack = exports.canReencodeAudioTrack = exports.canCopyVideoTrack = exports.canCopyAudioTrack = exports.createAudioEncoder = void 0;
4
4
  const rotate_and_resize_video_frame_1 = require("./rotate-and-resize-video-frame");
5
5
  const rotation_1 = require("./rotation");
6
6
  const set_remotion_imported_1 = require("./set-remotion-imported");
7
- var audio_decoder_1 = require("./audio-decoder");
8
- Object.defineProperty(exports, "createAudioDecoder", { enumerable: true, get: function () { return audio_decoder_1.createAudioDecoder; } });
9
7
  var audio_encoder_1 = require("./audio-encoder");
10
8
  Object.defineProperty(exports, "createAudioEncoder", { enumerable: true, get: function () { return audio_encoder_1.createAudioEncoder; } });
11
9
  var can_copy_audio_track_1 = require("./can-copy-audio-track");
@@ -20,6 +18,10 @@ var convert_audiodata_1 = require("./convert-audiodata");
20
18
  Object.defineProperty(exports, "convertAudioData", { enumerable: true, get: function () { return convert_audiodata_1.convertAudioData; } });
21
19
  var convert_media_1 = require("./convert-media");
22
20
  Object.defineProperty(exports, "convertMedia", { enumerable: true, get: function () { return convert_media_1.convertMedia; } });
21
+ var create_audio_decoder_1 = require("./create-audio-decoder");
22
+ Object.defineProperty(exports, "createAudioDecoder", { enumerable: true, get: function () { return create_audio_decoder_1.createAudioDecoder; } });
23
+ var create_video_decoder_1 = require("./create-video-decoder");
24
+ Object.defineProperty(exports, "createVideoDecoder", { enumerable: true, get: function () { return create_video_decoder_1.createVideoDecoder; } });
23
25
  var default_on_audio_track_handler_1 = require("./default-on-audio-track-handler");
24
26
  Object.defineProperty(exports, "defaultOnAudioTrackHandler", { enumerable: true, get: function () { return default_on_audio_track_handler_1.defaultOnAudioTrackHandler; } });
25
27
  var default_on_video_track_handler_1 = require("./default-on-video-track-handler");
@@ -34,8 +36,6 @@ var get_default_audio_codec_1 = require("./get-default-audio-codec");
34
36
  Object.defineProperty(exports, "getDefaultAudioCodec", { enumerable: true, get: function () { return get_default_audio_codec_1.getDefaultAudioCodec; } });
35
37
  var get_default_video_codec_1 = require("./get-default-video-codec");
36
38
  Object.defineProperty(exports, "getDefaultVideoCodec", { enumerable: true, get: function () { return get_default_video_codec_1.getDefaultVideoCodec; } });
37
- var video_decoder_1 = require("./video-decoder");
38
- Object.defineProperty(exports, "createVideoDecoder", { enumerable: true, get: function () { return video_decoder_1.createVideoDecoder; } });
39
39
  var video_encoder_1 = require("./video-encoder");
40
40
  Object.defineProperty(exports, "createVideoEncoder", { enumerable: true, get: function () { return video_encoder_1.createVideoEncoder; } });
41
41
  var webcodecs_controller_1 = require("./webcodecs-controller");
@@ -1,20 +1,12 @@
1
- import type { ProgressTracker } from '../create/progress-tracker';
2
1
  import type { LogLevel } from '../log';
3
2
  import type { WebCodecsController } from '../webcodecs-controller';
4
- export declare const makeIoSynchronizer: ({ logLevel, label, progress, }: {
3
+ export declare const makeIoSynchronizer: ({ logLevel, label, controller, }: {
5
4
  logLevel: LogLevel;
6
5
  label: string;
7
- progress: ProgressTracker;
6
+ controller: WebCodecsController | null;
8
7
  }) => {
9
- inputItem: (timestamp: number, keyFrame: boolean) => void;
8
+ inputItem: (timestamp: number) => void;
10
9
  onOutput: (timestamp: number) => void;
11
- waitFor: ({ unprocessed, unemitted, minimumProgress, controller, }: {
12
- unemitted: number;
13
- unprocessed: number;
14
- minimumProgress: number | null;
15
- controller: WebCodecsController;
16
- }) => Promise<void>;
17
- waitForFinish: (controller: WebCodecsController) => Promise<void>;
18
- onProcessed: () => void;
19
- getUnprocessed: () => number;
10
+ waitForQueueSize: (queueSize: number) => Promise<void>;
20
11
  };
12
+ export type IoSynchronizer = ReturnType<typeof makeIoSynchronizer>;
@@ -5,40 +5,25 @@ const event_emitter_1 = require("../create/event-emitter");
5
5
  const with_resolvers_1 = require("../create/with-resolvers");
6
6
  const log_1 = require("../log");
7
7
  const make_timeout_promise_1 = require("./make-timeout-promise");
8
- const makeIoSynchronizer = ({ logLevel, label, progress, }) => {
8
+ const makeIoSynchronizer = ({ logLevel, label, controller, }) => {
9
9
  const eventEmitter = new event_emitter_1.IoEventEmitter();
10
10
  let lastInput = 0;
11
- let lastInputKeyframe = 0;
12
11
  let lastOutput = 0;
13
12
  let inputsSinceLastOutput = 0;
14
13
  let inputs = [];
15
- let keyframes = [];
16
- // Once WebCodecs emits items, the user has to handle them
17
- // Let's keep count of how many items are unprocessed
18
- let _unprocessed = 0;
19
- const getUnprocessed = () => _unprocessed;
20
- const getUnemittedItems = () => {
14
+ const getQueuedItems = () => {
21
15
  inputs = inputs.filter((input) => Math.floor(input) > Math.floor(lastOutput));
22
16
  return inputs.length;
23
17
  };
24
- const getUnemittedKeyframes = () => {
25
- keyframes = keyframes.filter((keyframe) => Math.floor(keyframe) > Math.floor(lastOutput));
26
- return keyframes.length;
27
- };
28
18
  const printState = (prefix) => {
29
- log_1.Log.trace(logLevel, `[${label}] ${prefix}, state: Last input = ${lastInput} Last input keyframe = ${lastInputKeyframe} Last output = ${lastOutput} Inputs since last output = ${inputsSinceLastOutput}, Queue = ${getUnemittedItems()} (${getUnemittedKeyframes()} keyframes), Unprocessed = ${getUnprocessed()}`);
19
+ log_1.Log.trace(logLevel, `[${label}] ${prefix}, state: Last input = ${lastInput} Last output = ${lastOutput} Inputs since last output = ${inputsSinceLastOutput}, Queue = ${getQueuedItems()}`);
30
20
  };
31
- const inputItem = (timestamp, keyFrame) => {
21
+ const inputItem = (timestamp) => {
32
22
  lastInput = timestamp;
33
- if (keyFrame) {
34
- lastInputKeyframe = timestamp;
35
- keyframes.push(timestamp);
36
- }
37
23
  inputsSinceLastOutput++;
38
24
  inputs.push(timestamp);
39
25
  eventEmitter.dispatchEvent('input', {
40
26
  timestamp,
41
- keyFrame,
42
27
  });
43
28
  printState('Input item');
44
29
  };
@@ -48,7 +33,6 @@ const makeIoSynchronizer = ({ logLevel, label, progress, }) => {
48
33
  eventEmitter.dispatchEvent('output', {
49
34
  timestamp,
50
35
  });
51
- _unprocessed++;
52
36
  printState('Got output');
53
37
  };
54
38
  const waitForOutput = () => {
@@ -60,75 +44,46 @@ const makeIoSynchronizer = ({ logLevel, label, progress, }) => {
60
44
  eventEmitter.addEventListener('output', on);
61
45
  return promise;
62
46
  };
63
- const waitForProcessed = () => {
64
- const { promise, resolve } = (0, with_resolvers_1.withResolvers)();
65
- const on = () => {
66
- eventEmitter.removeEventListener('processed', on);
67
- resolve();
68
- };
69
- eventEmitter.addEventListener('processed', on);
70
- return promise;
47
+ const makeErrorBanner = () => {
48
+ return [
49
+ `Waited too long for ${label} to finish:`,
50
+ `${getQueuedItems()} queued items`,
51
+ `inputs: ${JSON.stringify(inputs)}`,
52
+ `last output: ${lastOutput}`,
53
+ ];
71
54
  };
72
- const waitFor = async ({ unprocessed, unemitted, minimumProgress, controller, }) => {
73
- await controller._internals._mediaParserController._internals.checkForAbortAndPause();
55
+ const waitForQueueSize = async (queueSize) => {
56
+ if (getQueuedItems() <= queueSize) {
57
+ return Promise.resolve();
58
+ }
74
59
  const { timeoutPromise, clear } = (0, make_timeout_promise_1.makeTimeoutPromise)({
75
60
  label: () => [
76
- `Waited too long for ${label} to finish:`,
77
- `${getUnemittedItems()} unemitted items`,
78
- `${getUnprocessed()} unprocessed items: ${JSON.stringify(_unprocessed)}`,
79
- `smallest progress: ${progress.getSmallestProgress()}`,
80
- `inputs: ${JSON.stringify(inputs)}`,
81
- `last output: ${lastOutput}`,
82
- `wanted: ${unemitted} unemitted items, ${unprocessed} unprocessed items, minimum progress ${minimumProgress}`,
61
+ ...makeErrorBanner(),
62
+ `wanted: <${queueSize} queued items`,
83
63
  `Report this at https://remotion.dev/report`,
84
64
  ].join('\n'),
85
65
  ms: 10000,
86
66
  controller,
87
67
  });
88
- controller._internals._mediaParserController._internals.signal.addEventListener('abort', clear);
68
+ if (controller) {
69
+ controller._internals._mediaParserController._internals.signal.addEventListener('abort', clear);
70
+ }
89
71
  await Promise.race([
90
72
  timeoutPromise,
91
- Promise.all([
92
- (async () => {
93
- while (getUnemittedItems() > unemitted) {
94
- await waitForOutput();
95
- }
96
- })(),
97
- (async () => {
98
- while (getUnprocessed() > unprocessed) {
99
- await waitForProcessed();
100
- }
101
- })(),
102
- minimumProgress === null || progress.getSmallestProgress() === null
103
- ? Promise.resolve()
104
- : (async () => {
105
- while (progress.getSmallestProgress() < minimumProgress) {
106
- await progress.waitForProgress();
107
- }
108
- })(),
109
- ]),
73
+ (async () => {
74
+ while (getQueuedItems() > queueSize) {
75
+ await waitForOutput();
76
+ }
77
+ })(),
110
78
  ]).finally(() => clear());
111
- controller._internals._mediaParserController._internals.signal.removeEventListener('abort', clear);
112
- };
113
- const waitForFinish = async (controller) => {
114
- await waitFor({
115
- unprocessed: 0,
116
- unemitted: 0,
117
- minimumProgress: null,
118
- controller,
119
- });
120
- };
121
- const onProcessed = () => {
122
- eventEmitter.dispatchEvent('processed', {});
123
- _unprocessed--;
79
+ if (controller) {
80
+ controller._internals._mediaParserController._internals.signal.removeEventListener('abort', clear);
81
+ }
124
82
  };
125
83
  return {
126
84
  inputItem,
127
85
  onOutput,
128
- waitFor,
129
- waitForFinish,
130
- onProcessed,
131
- getUnprocessed,
86
+ waitForQueueSize,
132
87
  };
133
88
  };
134
89
  exports.makeIoSynchronizer = makeIoSynchronizer;
@@ -2,7 +2,7 @@ import type { WebCodecsController } from '../webcodecs-controller';
2
2
  export declare const makeTimeoutPromise: ({ label, ms, controller, }: {
3
3
  label: () => string;
4
4
  ms: number;
5
- controller: WebCodecsController;
5
+ controller: WebCodecsController | null;
6
6
  }) => {
7
7
  timeoutPromise: Promise<void>;
8
8
  clear: () => void;
@@ -19,8 +19,10 @@ const makeTimeoutPromise = ({ label, ms, controller, }) => {
19
19
  const onResume = () => {
20
20
  set();
21
21
  };
22
- controller.addEventListener('pause', onPause);
23
- controller.addEventListener('resume', onResume);
22
+ if (controller) {
23
+ controller.addEventListener('pause', onPause);
24
+ controller.addEventListener('resume', onResume);
25
+ }
24
26
  return {
25
27
  timeoutPromise: promise,
26
28
  clear: () => {
@@ -28,8 +30,10 @@ const makeTimeoutPromise = ({ label, ms, controller, }) => {
28
30
  clearTimeout(timeout);
29
31
  }
30
32
  resolve();
31
- controller.removeEventListener('pause', onPause);
32
- controller.removeEventListener('resume', onResume);
33
+ if (controller) {
34
+ controller.removeEventListener('pause', onPause);
35
+ controller.removeEventListener('resume', onResume);
36
+ }
33
37
  },
34
38
  };
35
39
  };
@@ -0,0 +1,11 @@
1
+ import type { MediaParserAudioCodec, MediaParserVideoCodec } from '@remotion/media-parser';
2
+ import type { ConvertMediaAudioCodec } from './get-available-audio-codecs';
3
+ import type { ConvertMediaVideoCodec } from './get-available-video-codecs';
4
+ export declare const isSameVideoCodec: ({ inputVideoCodec, outputCodec, }: {
5
+ inputVideoCodec: MediaParserVideoCodec;
6
+ outputCodec: ConvertMediaVideoCodec;
7
+ }) => boolean;
8
+ export declare const isSameAudioCodec: ({ inputAudioCodec, outputCodec, }: {
9
+ inputAudioCodec: MediaParserAudioCodec;
10
+ outputCodec: ConvertMediaAudioCodec;
11
+ }) => boolean;
@@ -0,0 +1,36 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.isSameAudioCodec = exports.isSameVideoCodec = void 0;
4
+ const isSameVideoCodec = ({ inputVideoCodec, outputCodec, }) => {
5
+ if (outputCodec === 'h264') {
6
+ return inputVideoCodec === 'h264';
7
+ }
8
+ if (outputCodec === 'h265') {
9
+ return inputVideoCodec === 'h265';
10
+ }
11
+ if (outputCodec === 'vp8') {
12
+ return inputVideoCodec === 'vp8';
13
+ }
14
+ if (outputCodec === 'vp9') {
15
+ return inputVideoCodec === 'vp9';
16
+ }
17
+ throw new Error(`Unsupported output codec: ${outputCodec}`);
18
+ };
19
+ exports.isSameVideoCodec = isSameVideoCodec;
20
+ const isSameAudioCodec = ({ inputAudioCodec, outputCodec, }) => {
21
+ if (outputCodec === 'aac') {
22
+ return inputAudioCodec === 'aac';
23
+ }
24
+ if (outputCodec === 'opus') {
25
+ return inputAudioCodec === 'opus';
26
+ }
27
+ if (outputCodec === 'wav') {
28
+ return (inputAudioCodec === 'pcm-f32' ||
29
+ inputAudioCodec === 'pcm-s16' ||
30
+ inputAudioCodec === 'pcm-s24' ||
31
+ inputAudioCodec === 'pcm-s32' ||
32
+ inputAudioCodec === 'pcm-u8');
33
+ }
34
+ throw new Error(`Unsupported output codec: ${outputCodec}`);
35
+ };
36
+ exports.isSameAudioCodec = isSameAudioCodec;
@@ -7,7 +7,7 @@ import type { ConvertMediaContainer } from './get-available-containers';
7
7
  import type { ConvertMediaOnAudioTrackHandler } from './on-audio-track-handler';
8
8
  import type { ConvertMediaProgressFn } from './throttled-state-update';
9
9
  import type { WebCodecsController } from './webcodecs-controller';
10
- export declare const makeAudioTrackHandler: ({ state, defaultAudioCodec: audioCodec, controller, abortConversion, onMediaStateUpdate, onAudioTrack, logLevel, outputContainer, progressTracker, onAudioData, }: {
10
+ export declare const makeAudioTrackHandler: ({ state, defaultAudioCodec: audioCodec, controller, abortConversion, onMediaStateUpdate, onAudioTrack, logLevel, outputContainer, onAudioData, progressTracker, }: {
11
11
  state: MediaFn;
12
12
  defaultAudioCodec: ConvertMediaAudioCodec | null;
13
13
  controller: WebCodecsController;
@@ -16,6 +16,6 @@ export declare const makeAudioTrackHandler: ({ state, defaultAudioCodec: audioCo
16
16
  onAudioTrack: ConvertMediaOnAudioTrackHandler | null;
17
17
  logLevel: MediaParserLogLevel;
18
18
  outputContainer: ConvertMediaContainer;
19
- progressTracker: ProgressTracker;
20
19
  onAudioData: ConvertMediaOnAudioData | null;
20
+ progressTracker: ProgressTracker;
21
21
  }) => MediaParserOnAudioTrack;
@@ -1,21 +1,17 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.makeAudioTrackHandler = void 0;
4
- const media_parser_1 = require("@remotion/media-parser");
5
- const audio_decoder_1 = require("./audio-decoder");
6
- const audio_decoder_config_1 = require("./audio-decoder-config");
7
- const audio_encoder_1 = require("./audio-encoder");
8
- const audio_encoder_config_1 = require("./audio-encoder-config");
9
4
  const can_copy_audio_track_1 = require("./can-copy-audio-track");
10
- const convert_encoded_chunk_1 = require("./convert-encoded-chunk");
5
+ const copy_audio_track_1 = require("./copy-audio-track");
11
6
  const default_on_audio_track_handler_1 = require("./default-on-audio-track-handler");
12
7
  const get_default_audio_codec_1 = require("./get-default-audio-codec");
13
- const log_1 = require("./log");
14
- const makeAudioTrackHandler = ({ state, defaultAudioCodec: audioCodec, controller, abortConversion, onMediaStateUpdate, onAudioTrack, logLevel, outputContainer, progressTracker, onAudioData, }) => async ({ track, container: inputContainer }) => {
8
+ const reencode_audio_track_1 = require("./reencode-audio-track");
9
+ const makeAudioTrackHandler = ({ state, defaultAudioCodec: audioCodec, controller, abortConversion, onMediaStateUpdate, onAudioTrack, logLevel, outputContainer, onAudioData, progressTracker, }) => async ({ track, container: inputContainer }) => {
15
10
  const canCopyTrack = (0, can_copy_audio_track_1.canCopyAudioTrack)({
16
11
  inputCodec: track.codecEnum,
17
12
  outputContainer,
18
13
  inputContainer,
14
+ outputAudioCodec: audioCodec,
19
15
  });
20
16
  const audioOperation = await (onAudioTrack ?? default_on_audio_track_handler_1.defaultOnAudioTrackHandler)({
21
17
  defaultAudioCodec: audioCodec ?? (0, get_default_audio_codec_1.getDefaultAudioCodec)({ container: outputContainer }),
@@ -32,154 +28,24 @@ const makeAudioTrackHandler = ({ state, defaultAudioCodec: audioCodec, controlle
32
28
  throw new Error(`Audio track with ID ${track.trackId} resolved with {"type": "fail"}. This could mean that this audio track could neither be copied to the output container or re-encoded. You have the option to drop the track instead of failing it: https://remotion.dev/docs/webcodecs/track-transformation`);
33
29
  }
34
30
  if (audioOperation.type === 'copy') {
35
- const addedTrack = await state.addTrack({
36
- type: 'audio',
37
- codec: track.codecEnum,
38
- numberOfChannels: track.numberOfChannels,
39
- sampleRate: track.sampleRate,
40
- codecPrivate: track.codecData?.data ?? null,
41
- timescale: track.originalTimescale,
31
+ return (0, copy_audio_track_1.copyAudioTrack)({
32
+ logLevel,
33
+ onMediaStateUpdate,
34
+ state,
35
+ track,
36
+ progressTracker,
42
37
  });
43
- log_1.Log.verbose(logLevel, `Copying audio track ${track.trackId} as track ${addedTrack.trackNumber}. Timescale = ${track.originalTimescale}, codec = ${track.codecEnum} (${track.codec}) `);
44
- return async (audioSample) => {
45
- await state.addSample({
46
- chunk: audioSample,
47
- trackNumber: addedTrack.trackNumber,
48
- isVideo: false,
49
- codecPrivate: track.codecData?.data ?? null,
50
- });
51
- onMediaStateUpdate?.((prevState) => {
52
- return {
53
- ...prevState,
54
- encodedAudioFrames: prevState.encodedAudioFrames + 1,
55
- };
56
- });
57
- };
58
38
  }
59
- const audioEncoderConfig = await (0, audio_encoder_config_1.getAudioEncoderConfig)({
60
- numberOfChannels: track.numberOfChannels,
61
- sampleRate: audioOperation.sampleRate ?? track.sampleRate,
62
- codec: audioOperation.audioCodec,
63
- bitrate: audioOperation.bitrate,
64
- });
65
- const audioDecoderConfig = await (0, audio_decoder_config_1.getAudioDecoderConfig)({
66
- codec: track.codec,
67
- numberOfChannels: track.numberOfChannels,
68
- sampleRate: track.sampleRate,
69
- description: track.description,
70
- });
71
- log_1.Log.verbose(logLevel, 'Audio encoder config', audioEncoderConfig);
72
- log_1.Log.verbose(logLevel, 'Audio decoder config', audioDecoderConfig ?? track);
73
- if (!audioEncoderConfig) {
74
- abortConversion(new Error(`Could not configure audio encoder of track ${track.trackId}`));
75
- return null;
76
- }
77
- if (!audioDecoderConfig) {
78
- abortConversion(new Error(`Could not configure audio decoder of track ${track.trackId}`));
79
- return null;
80
- }
81
- const codecPrivate = audioOperation.audioCodec === 'aac'
82
- ? media_parser_1.MediaParserInternals.createAacCodecPrivate({
83
- audioObjectType: 2,
84
- sampleRate: audioOperation.sampleRate ?? audioEncoderConfig.sampleRate,
85
- channelConfiguration: audioEncoderConfig.numberOfChannels,
86
- codecPrivate: null,
87
- })
88
- : null;
89
- const { trackNumber } = await state.addTrack({
90
- type: 'audio',
91
- codec: audioOperation.audioCodec === 'wav'
92
- ? 'pcm-s16'
93
- : audioOperation.audioCodec,
94
- numberOfChannels: audioEncoderConfig.numberOfChannels,
95
- sampleRate: audioOperation.sampleRate ?? audioEncoderConfig.sampleRate,
96
- codecPrivate,
97
- timescale: track.originalTimescale,
98
- });
99
- const audioEncoder = (0, audio_encoder_1.createAudioEncoder)({
100
- // This is weird 😵‍💫
101
- // Chrome completely ignores the sample rate and uses it's own
102
- // We cannot determine it here because it depends on the system
103
- // sample rate. Unhardcode then declare it later once we know.
104
- onNewAudioSampleRate: (sampleRate) => {
105
- state.updateTrackSampleRate({ sampleRate, trackNumber });
106
- },
107
- onChunk: async (chunk) => {
108
- await state.addSample({
109
- chunk: (0, convert_encoded_chunk_1.convertEncodedChunk)(chunk),
110
- trackNumber,
111
- isVideo: false,
112
- codecPrivate,
113
- });
114
- onMediaStateUpdate?.((prevState) => {
115
- return {
116
- ...prevState,
117
- encodedAudioFrames: prevState.encodedAudioFrames + 1,
118
- };
119
- });
120
- },
121
- onError: (err) => {
122
- abortConversion(new Error(`Audio encoder of track ${track.trackId} failed (see .cause of this error)`, {
123
- cause: err,
124
- }));
125
- },
126
- codec: audioOperation.audioCodec,
127
- controller,
128
- config: audioEncoderConfig,
129
- logLevel,
130
- progressTracker,
131
- });
132
- const audioDecoder = (0, audio_decoder_1.createAudioDecoder)({
133
- onFrame: async (audioData) => {
134
- const newAudioData = onAudioData
135
- ? await onAudioData?.({ audioData, track })
136
- : audioData;
137
- if (newAudioData !== audioData) {
138
- if (newAudioData.duration !== audioData.duration) {
139
- throw new Error(`onAudioData returned a different duration than the input audio data. Original duration: ${audioData.duration}, new duration: ${newAudioData.duration}`);
140
- }
141
- if (newAudioData.numberOfChannels !== audioData.numberOfChannels) {
142
- throw new Error(`onAudioData returned a different number of channels than the input audio data. Original channels: ${audioData.numberOfChannels}, new channels: ${newAudioData.numberOfChannels}`);
143
- }
144
- if (newAudioData.sampleRate !== audioData.sampleRate) {
145
- throw new Error(`onAudioData returned a different sample rate than the input audio data. Original sample rate: ${audioData.sampleRate}, new sample rate: ${newAudioData.sampleRate}`);
146
- }
147
- if (newAudioData.format !== audioData.format) {
148
- throw new Error(`onAudioData returned a different format than the input audio data. Original format: ${audioData.format}, new format: ${newAudioData.format}`);
149
- }
150
- if (newAudioData.timestamp !== audioData.timestamp) {
151
- throw new Error(`onAudioData returned a different timestamp than the input audio data. Original timestamp: ${audioData.timestamp}, new timestamp: ${newAudioData.timestamp}`);
152
- }
153
- audioData.close();
154
- }
155
- await audioEncoder.encodeFrame(newAudioData);
156
- onMediaStateUpdate?.((prevState) => {
157
- return {
158
- ...prevState,
159
- decodedAudioFrames: prevState.decodedAudioFrames + 1,
160
- };
161
- });
162
- newAudioData.close();
163
- },
164
- onError(error) {
165
- abortConversion(new Error(`Audio decoder of track ${track.trackId} failed. Config: ${JSON.stringify(audioDecoderConfig)} (see .cause of this error)`, {
166
- cause: error,
167
- }));
168
- },
39
+ return (0, reencode_audio_track_1.reencodeAudioTrack)({
40
+ abortConversion,
169
41
  controller,
170
- config: audioDecoderConfig,
171
42
  logLevel,
43
+ onMediaStateUpdate,
44
+ audioOperation,
45
+ onAudioData,
46
+ state,
172
47
  track,
173
48
  progressTracker,
174
49
  });
175
- state.addWaitForFinishPromise(async () => {
176
- await audioDecoder.waitForFinish();
177
- await audioEncoder.waitForFinish();
178
- audioDecoder.close();
179
- audioEncoder.close();
180
- });
181
- return async (audioSample) => {
182
- await audioDecoder.processSample(audioSample);
183
- };
184
50
  };
185
51
  exports.makeAudioTrackHandler = makeAudioTrackHandler;
@@ -2,13 +2,11 @@ import type { MediaParserVideoTrack } 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 { ResizeOperation } from './resizing/mode';
5
- import type { WebCodecsVideoEncoder } from './video-encoder';
6
- export declare const onFrame: ({ frame: unrotatedFrame, onVideoFrame, videoEncoder, track, outputCodec, rotation, resizeOperation, }: {
5
+ export declare const onFrame: ({ frame: unrotatedFrame, onVideoFrame, track, outputCodec, rotation, resizeOperation, }: {
7
6
  frame: VideoFrame;
8
7
  onVideoFrame: ConvertMediaOnVideoFrame | null;
9
- videoEncoder: WebCodecsVideoEncoder;
10
8
  track: MediaParserVideoTrack;
11
9
  outputCodec: ConvertMediaVideoCodec;
12
10
  rotation: number;
13
11
  resizeOperation: ResizeOperation | null;
14
- }) => Promise<void>;
12
+ }) => Promise<VideoFrame>;