@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
package/dist/on-frame.js CHANGED
@@ -4,7 +4,7 @@ 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
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, }) => {
7
+ const onFrame = async ({ frame: unrotatedFrame, onVideoFrame, track, outputCodec, rotation, resizeOperation, }) => {
8
8
  const rotated = (0, rotate_and_resize_video_frame_1.rotateAndResizeVideoFrame)({
9
9
  rotation,
10
10
  frame: unrotatedFrame,
@@ -18,10 +18,10 @@ const onFrame = async ({ frame: unrotatedFrame, onVideoFrame, videoEncoder, trac
18
18
  ? await onVideoFrame({ frame: rotated, track })
19
19
  : rotated;
20
20
  if (userProcessedFrame.displayWidth !== rotated.displayWidth) {
21
- throw new Error(`Returned VideoFrame of track ${track.trackId} has different displayWidth (${userProcessedFrame.displayWidth}) than the input frame (${userProcessedFrame.displayHeight})`);
21
+ throw new Error(`Returned VideoFrame of track ${track.trackId} has different displayWidth (${userProcessedFrame.displayWidth}) than the input frame (${rotated.displayWidth})`);
22
22
  }
23
23
  if (userProcessedFrame.displayHeight !== rotated.displayHeight) {
24
- throw new Error(`Returned VideoFrame of track ${track.trackId} has different displayHeight (${userProcessedFrame.displayHeight}) than the input frame (${userProcessedFrame.displayHeight})`);
24
+ throw new Error(`Returned VideoFrame of track ${track.trackId} has different displayHeight (${userProcessedFrame.displayHeight}) than the input frame (${rotated.displayHeight})`);
25
25
  }
26
26
  // In Safari, calling new VideoFrame() might change the timestamp
27
27
  // In flipVideo test from 803000 to 803299
@@ -31,17 +31,16 @@ const onFrame = async ({ frame: unrotatedFrame, onVideoFrame, videoEncoder, trac
31
31
  if ((userProcessedFrame.duration ?? 0) !== (rotated.duration ?? 0)) {
32
32
  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`);
33
33
  }
34
+ if (rotated !== userProcessedFrame) {
35
+ rotated.close();
36
+ }
34
37
  const fixedFrame = (0, convert_to_correct_videoframe_1.convertToCorrectVideoFrame)({
35
38
  videoFrame: userProcessedFrame,
36
39
  outputCodec,
37
40
  });
38
- await videoEncoder.encodeFrame(fixedFrame, fixedFrame.timestamp);
39
- fixedFrame.close();
40
- if (rotated !== userProcessedFrame) {
41
- rotated.close();
42
- }
43
41
  if (fixedFrame !== userProcessedFrame) {
44
- fixedFrame.close();
42
+ userProcessedFrame.close();
45
43
  }
44
+ return fixedFrame;
46
45
  };
47
46
  exports.onFrame = onFrame;
@@ -8,7 +8,7 @@ import type { ConvertMediaOnVideoTrackHandler } from './on-video-track-handler';
8
8
  import type { ResizeOperation } from './resizing/mode';
9
9
  import type { ConvertMediaProgressFn } from './throttled-state-update';
10
10
  import type { WebCodecsController } from './webcodecs-controller';
11
- export declare const makeVideoTrackHandler: ({ state, onVideoFrame, onMediaStateUpdate, abortConversion, controller, defaultVideoCodec, onVideoTrack, logLevel, outputContainer, rotate, progress, resizeOperation, }: {
11
+ export declare const makeVideoTrackHandler: ({ state, onVideoFrame, onMediaStateUpdate, abortConversion, controller, defaultVideoCodec, onVideoTrack, logLevel, outputContainer, rotate, resizeOperation, progressTracker, }: {
12
12
  state: MediaFn;
13
13
  onVideoFrame: null | ConvertMediaOnVideoFrame;
14
14
  onMediaStateUpdate: null | ConvertMediaProgressFn;
@@ -19,6 +19,6 @@ export declare const makeVideoTrackHandler: ({ state, onVideoFrame, onMediaState
19
19
  logLevel: MediaParserLogLevel;
20
20
  outputContainer: ConvertMediaContainer;
21
21
  rotate: number;
22
- progress: ProgressTracker;
23
22
  resizeOperation: ResizeOperation | null;
23
+ progressTracker: ProgressTracker;
24
24
  }) => MediaParserOnVideoTrack;
@@ -1,19 +1,12 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.makeVideoTrackHandler = void 0;
4
- const arraybuffer_to_uint8_array_1 = require("./arraybuffer-to-uint8-array");
5
4
  const can_copy_video_track_1 = require("./can-copy-video-track");
6
- const convert_encoded_chunk_1 = require("./convert-encoded-chunk");
5
+ const copy_video_track_1 = require("./copy-video-track");
7
6
  const default_on_video_track_handler_1 = require("./default-on-video-track-handler");
8
7
  const get_default_video_codec_1 = require("./get-default-video-codec");
9
- const log_1 = require("./log");
10
- const on_frame_1 = require("./on-frame");
11
- const rotation_1 = require("./rotation");
12
- const video_decoder_1 = require("./video-decoder");
13
- const video_decoder_config_1 = require("./video-decoder-config");
14
- const video_encoder_1 = require("./video-encoder");
15
- const video_encoder_config_1 = require("./video-encoder-config");
16
- const makeVideoTrackHandler = ({ state, onVideoFrame, onMediaStateUpdate, abortConversion, controller, defaultVideoCodec, onVideoTrack, logLevel, outputContainer, rotate, progress, resizeOperation, }) => async ({ track, container: inputContainer }) => {
8
+ const reencode_video_track_1 = require("./reencode-video-track");
9
+ const makeVideoTrackHandler = ({ state, onVideoFrame, onMediaStateUpdate, abortConversion, controller, defaultVideoCodec, onVideoTrack, logLevel, outputContainer, rotate, resizeOperation, progressTracker, }) => async ({ track, container: inputContainer }) => {
17
10
  if (controller._internals._mediaParserController._internals.signal.aborted) {
18
11
  throw new Error('Aborted');
19
12
  }
@@ -23,6 +16,7 @@ const makeVideoTrackHandler = ({ state, onVideoFrame, onMediaStateUpdate, abortC
23
16
  rotationToApply: rotate,
24
17
  inputTrack: track,
25
18
  resizeOperation,
19
+ outputVideoCodec: defaultVideoCodec,
26
20
  });
27
21
  const videoOperation = await (onVideoTrack ?? default_on_video_track_handler_1.defaultOnVideoTrackHandler)({
28
22
  track,
@@ -41,129 +35,25 @@ const makeVideoTrackHandler = ({ state, onVideoFrame, onMediaStateUpdate, abortC
41
35
  throw new Error(`Video track with ID ${track.trackId} resolved with {"type": "fail"}. This could mean that this video 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`);
42
36
  }
43
37
  if (videoOperation.type === 'copy') {
44
- log_1.Log.verbose(logLevel, `Copying video track with codec ${track.codec} and timescale ${track.originalTimescale}`);
45
- const videoTrack = await state.addTrack({
46
- type: 'video',
47
- color: track.advancedColor,
48
- width: track.codedWidth,
49
- height: track.codedHeight,
50
- codec: track.codecEnum,
51
- codecPrivate: track.codecData?.data ?? null,
52
- timescale: track.originalTimescale,
38
+ return (0, copy_video_track_1.copyVideoTrack)({
39
+ logLevel,
40
+ onMediaStateUpdate,
41
+ state,
42
+ track,
43
+ progressTracker,
53
44
  });
54
- return async (sample) => {
55
- await state.addSample({
56
- chunk: sample,
57
- trackNumber: videoTrack.trackNumber,
58
- isVideo: true,
59
- codecPrivate: track.codecData?.data ?? null,
60
- });
61
- onMediaStateUpdate?.((prevState) => {
62
- return {
63
- ...prevState,
64
- decodedVideoFrames: prevState.decodedVideoFrames + 1,
65
- };
66
- });
67
- };
68
45
  }
69
- if (videoOperation.type !== 'reencode') {
70
- throw new Error(`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"`);
71
- }
72
- const rotation = (videoOperation.rotate ?? rotate) - track.rotation;
73
- const { height: newHeight, width: newWidth } = (0, rotation_1.calculateNewDimensionsFromRotateAndScale)({
74
- width: track.codedWidth,
75
- height: track.codedHeight,
76
- rotation,
77
- videoCodec: videoOperation.videoCodec,
78
- resizeOperation: videoOperation.resize ?? null,
79
- });
80
- const videoEncoderConfig = await (0, video_encoder_config_1.getVideoEncoderConfig)({
81
- codec: videoOperation.videoCodec,
82
- height: newHeight,
83
- width: newWidth,
84
- fps: track.fps,
85
- });
86
- const videoDecoderConfig = await (0, video_decoder_config_1.getVideoDecoderConfigWithHardwareAcceleration)(track);
87
- log_1.Log.verbose(logLevel, 'Video encoder config', videoEncoderConfig);
88
- log_1.Log.verbose(logLevel, 'Video decoder config', videoDecoderConfig ?? track);
89
- if (videoEncoderConfig === null) {
90
- abortConversion(new Error(`Could not configure video encoder of track ${track.trackId}`));
91
- return null;
92
- }
93
- if (videoDecoderConfig === null) {
94
- abortConversion(new Error(`Could not configure video decoder of track ${track.trackId}`));
95
- return null;
96
- }
97
- const { trackNumber } = await state.addTrack({
98
- type: 'video',
99
- color: track.advancedColor,
100
- width: newWidth,
101
- height: newHeight,
102
- codec: videoOperation.videoCodec,
103
- codecPrivate: null,
104
- timescale: track.originalTimescale,
105
- });
106
- log_1.Log.verbose(logLevel, `Created new video track with ID ${trackNumber}, codec ${videoOperation.videoCodec} and timescale ${track.originalTimescale}`);
107
- const videoEncoder = (0, video_encoder_1.createVideoEncoder)({
108
- onChunk: async (chunk, metadata) => {
109
- await state.addSample({
110
- chunk: (0, convert_encoded_chunk_1.convertEncodedChunk)(chunk),
111
- trackNumber,
112
- isVideo: true,
113
- codecPrivate: (0, arraybuffer_to_uint8_array_1.arrayBufferToUint8Array)((metadata?.decoderConfig?.description ??
114
- null)),
115
- });
116
- onMediaStateUpdate?.((prevState) => {
117
- return {
118
- ...prevState,
119
- encodedVideoFrames: prevState.encodedVideoFrames + 1,
120
- };
121
- });
122
- },
123
- onError: (err) => {
124
- abortConversion(new Error(`Video encoder of track ${track.trackId} failed (see .cause of this error)`, {
125
- cause: err,
126
- }));
127
- },
128
- controller,
129
- config: videoEncoderConfig,
130
- logLevel,
131
- outputCodec: videoOperation.videoCodec,
132
- progress,
133
- });
134
- const videoDecoder = (0, video_decoder_1.createVideoDecoder)({
135
- config: videoDecoderConfig,
136
- onFrame: async (frame) => {
137
- await (0, on_frame_1.onFrame)({
138
- frame,
139
- track,
140
- videoEncoder,
141
- onVideoFrame,
142
- outputCodec: videoOperation.videoCodec,
143
- rotation,
144
- resizeOperation: videoOperation.resize ?? null,
145
- });
146
- },
147
- onError: (err) => {
148
- abortConversion(new Error(`Video decoder of track ${track.trackId} failed (see .cause of this error)`, {
149
- cause: err,
150
- }));
151
- },
46
+ return (0, reencode_video_track_1.reencodeVideoTrack)({
47
+ videoOperation,
48
+ abortConversion,
152
49
  controller,
153
50
  logLevel,
154
- progress,
155
- });
156
- state.addWaitForFinishPromise(async () => {
157
- log_1.Log.verbose(logLevel, 'Waiting for video decoder to finish');
158
- await videoDecoder.waitForFinish();
159
- videoDecoder.close();
160
- log_1.Log.verbose(logLevel, 'Video decoder finished. Waiting for encoder to finish');
161
- await videoEncoder.waitForFinish();
162
- videoEncoder.close();
163
- log_1.Log.verbose(logLevel, 'Encoder finished');
51
+ rotate,
52
+ track,
53
+ onVideoFrame,
54
+ state,
55
+ onMediaStateUpdate,
56
+ progressTracker,
164
57
  });
165
- return async (chunk) => {
166
- await videoDecoder.processSample(chunk);
167
- };
168
58
  };
169
59
  exports.makeVideoTrackHandler = makeVideoTrackHandler;
@@ -0,0 +1,18 @@
1
+ import type { LogLevel } from './log';
2
+ import type { WebCodecsController } from './webcodecs-controller';
3
+ type Processable = EncodedAudioChunk | EncodedVideoChunk | AudioData | VideoFrame;
4
+ export declare function processingQueue<T extends Processable>({ onOutput, logLevel, label, onError, controller, }: {
5
+ onOutput: (item: T) => Promise<void>;
6
+ onError: (error: Error) => void;
7
+ logLevel: LogLevel;
8
+ label: string;
9
+ controller: WebCodecsController;
10
+ }): {
11
+ input: (item: T) => void;
12
+ ioSynchronizer: {
13
+ inputItem: (timestamp: number) => void;
14
+ onOutput: (timestamp: number) => void;
15
+ waitForQueueSize: (queueSize: number) => Promise<void>;
16
+ };
17
+ };
18
+ export {};
@@ -0,0 +1,47 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.processingQueue = processingQueue;
4
+ const io_synchronizer_1 = require("./io-manager/io-synchronizer");
5
+ function processingQueue({ onOutput, logLevel, label, onError, controller, }) {
6
+ const ioSynchronizer = (0, io_synchronizer_1.makeIoSynchronizer)({
7
+ logLevel,
8
+ label,
9
+ controller,
10
+ });
11
+ let queue = Promise.resolve();
12
+ let stopped = false;
13
+ const input = (item) => {
14
+ if (stopped) {
15
+ return;
16
+ }
17
+ if (controller._internals._mediaParserController._internals.signal.aborted) {
18
+ stopped = true;
19
+ return;
20
+ }
21
+ const { timestamp } = item; // Saving in variable, because timestamp might become nulled
22
+ ioSynchronizer.inputItem(timestamp);
23
+ queue = queue
24
+ .then(() => {
25
+ if (stopped) {
26
+ return;
27
+ }
28
+ if (controller._internals._mediaParserController._internals.signal.aborted) {
29
+ stopped = true;
30
+ return;
31
+ }
32
+ return onOutput(item);
33
+ })
34
+ .then(() => {
35
+ ioSynchronizer.onOutput(timestamp);
36
+ return Promise.resolve();
37
+ })
38
+ .catch((err) => {
39
+ stopped = true;
40
+ onError(err);
41
+ });
42
+ };
43
+ return {
44
+ input,
45
+ ioSynchronizer,
46
+ };
47
+ }
@@ -0,0 +1,18 @@
1
+ import type { MediaParserAudioTrack, MediaParserLogLevel, MediaParserOnAudioSample } from '@remotion/media-parser';
2
+ import type { ConvertMediaOnAudioData } from './convert-media';
3
+ import type { MediaFn } from './create/media-fn';
4
+ import type { ProgressTracker } from './create/progress-tracker';
5
+ import type { AudioOperation } from './on-audio-track-handler';
6
+ import type { ConvertMediaProgressFn } from './throttled-state-update';
7
+ import type { WebCodecsController } from './webcodecs-controller';
8
+ export declare const reencodeAudioTrack: ({ audioOperation, track, logLevel, abortConversion, state, controller, onMediaStateUpdate, onAudioData, progressTracker, }: {
9
+ audioOperation: AudioOperation;
10
+ track: MediaParserAudioTrack;
11
+ logLevel: MediaParserLogLevel;
12
+ abortConversion: (errCause: Error) => void;
13
+ state: MediaFn;
14
+ controller: WebCodecsController;
15
+ onMediaStateUpdate: null | ConvertMediaProgressFn;
16
+ onAudioData: ConvertMediaOnAudioData | null;
17
+ progressTracker: ProgressTracker;
18
+ }) => Promise<MediaParserOnAudioSample | null>;
@@ -0,0 +1,164 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.reencodeAudioTrack = void 0;
4
+ const media_parser_1 = require("@remotion/media-parser");
5
+ const audio_decoder_config_1 = require("./audio-decoder-config");
6
+ const audio_encoder_1 = require("./audio-encoder");
7
+ const audio_encoder_config_1 = require("./audio-encoder-config");
8
+ const convert_encoded_chunk_1 = require("./convert-encoded-chunk");
9
+ const create_audio_decoder_1 = require("./create-audio-decoder");
10
+ const log_1 = require("./log");
11
+ const processing_queue_1 = require("./processing-queue");
12
+ const reencodeAudioTrack = async ({ audioOperation, track, logLevel, abortConversion, state, controller, onMediaStateUpdate, onAudioData, progressTracker, }) => {
13
+ if (audioOperation.type !== 'reencode') {
14
+ throw new Error(`Audio track with ID ${track.trackId} could not be resolved with a valid operation. Received ${JSON.stringify(audioOperation)}, but must be either "copy", "reencode", "drop" or "fail"`);
15
+ }
16
+ const audioEncoderConfig = await (0, audio_encoder_config_1.getAudioEncoderConfig)({
17
+ numberOfChannels: track.numberOfChannels,
18
+ sampleRate: audioOperation.sampleRate ?? track.sampleRate,
19
+ codec: audioOperation.audioCodec,
20
+ bitrate: audioOperation.bitrate,
21
+ });
22
+ const audioDecoderConfig = await (0, audio_decoder_config_1.getAudioDecoderConfig)({
23
+ codec: track.codec,
24
+ numberOfChannels: track.numberOfChannels,
25
+ sampleRate: track.sampleRate,
26
+ description: track.description,
27
+ });
28
+ log_1.Log.verbose(logLevel, 'Audio encoder config', audioEncoderConfig);
29
+ log_1.Log.verbose(logLevel, 'Audio decoder config', audioDecoderConfig ?? track);
30
+ if (!audioEncoderConfig) {
31
+ abortConversion(new Error(`Could not configure audio encoder of track ${track.trackId}`));
32
+ return null;
33
+ }
34
+ if (!audioDecoderConfig) {
35
+ abortConversion(new Error(`Could not configure audio decoder of track ${track.trackId}`));
36
+ return null;
37
+ }
38
+ const codecPrivate = audioOperation.audioCodec === 'aac'
39
+ ? media_parser_1.MediaParserInternals.createAacCodecPrivate({
40
+ audioObjectType: 2,
41
+ sampleRate: audioOperation.sampleRate ?? audioEncoderConfig.sampleRate,
42
+ channelConfiguration: audioEncoderConfig.numberOfChannels,
43
+ codecPrivate: null,
44
+ })
45
+ : null;
46
+ const { trackNumber } = await state.addTrack({
47
+ type: 'audio',
48
+ codec: audioOperation.audioCodec === 'wav'
49
+ ? 'pcm-s16'
50
+ : audioOperation.audioCodec,
51
+ numberOfChannels: audioEncoderConfig.numberOfChannels,
52
+ sampleRate: audioOperation.sampleRate ?? audioEncoderConfig.sampleRate,
53
+ codecPrivate,
54
+ timescale: track.originalTimescale,
55
+ });
56
+ const audioEncoder = (0, audio_encoder_1.createAudioEncoder)({
57
+ // This is weird 😵‍💫
58
+ // Chrome completely ignores the sample rate and uses it's own
59
+ // We cannot determine it here because it depends on the system
60
+ // sample rate. Unhardcode then declare it later once we know.
61
+ onNewAudioSampleRate: (sampleRate) => {
62
+ state.updateTrackSampleRate({ sampleRate, trackNumber });
63
+ },
64
+ onChunk: async (chunk) => {
65
+ await state.addSample({
66
+ chunk: (0, convert_encoded_chunk_1.convertEncodedChunk)(chunk),
67
+ trackNumber,
68
+ isVideo: false,
69
+ codecPrivate,
70
+ });
71
+ onMediaStateUpdate?.((prevState) => {
72
+ return {
73
+ ...prevState,
74
+ encodedAudioFrames: prevState.encodedAudioFrames + 1,
75
+ };
76
+ });
77
+ },
78
+ onError: (err) => {
79
+ abortConversion(new Error(`Audio encoder of track ${track.trackId} failed (see .cause of this error)`, {
80
+ cause: err,
81
+ }));
82
+ },
83
+ codec: audioOperation.audioCodec,
84
+ controller,
85
+ config: audioEncoderConfig,
86
+ logLevel,
87
+ });
88
+ const audioProcessingQueue = (0, processing_queue_1.processingQueue)({
89
+ controller,
90
+ label: 'AudioData processing queue',
91
+ logLevel,
92
+ onError(error) {
93
+ abortConversion(new Error(`Audio decoder of track ${track.trackId} failed. Config: ${JSON.stringify(audioDecoderConfig)} (see .cause of this error)`, {
94
+ cause: error,
95
+ }));
96
+ },
97
+ onOutput: async (audioData) => {
98
+ const newAudioData = onAudioData
99
+ ? await onAudioData?.({ audioData, track })
100
+ : audioData;
101
+ if (newAudioData !== audioData) {
102
+ if (newAudioData.duration !== audioData.duration) {
103
+ throw new Error(`onAudioData returned a different duration than the input audio data. Original duration: ${audioData.duration}, new duration: ${newAudioData.duration}`);
104
+ }
105
+ if (newAudioData.numberOfChannels !== audioData.numberOfChannels) {
106
+ throw new Error(`onAudioData returned a different number of channels than the input audio data. Original channels: ${audioData.numberOfChannels}, new channels: ${newAudioData.numberOfChannels}`);
107
+ }
108
+ if (newAudioData.sampleRate !== audioData.sampleRate) {
109
+ throw new Error(`onAudioData returned a different sample rate than the input audio data. Original sample rate: ${audioData.sampleRate}, new sample rate: ${newAudioData.sampleRate}`);
110
+ }
111
+ if (newAudioData.format !== audioData.format) {
112
+ throw new Error(`onAudioData returned a different format than the input audio data. Original format: ${audioData.format}, new format: ${newAudioData.format}`);
113
+ }
114
+ if (newAudioData.timestamp !== audioData.timestamp) {
115
+ throw new Error(`onAudioData returned a different timestamp than the input audio data. Original timestamp: ${audioData.timestamp}, new timestamp: ${newAudioData.timestamp}`);
116
+ }
117
+ audioData.close();
118
+ }
119
+ await controller._internals._mediaParserController._internals.checkForAbortAndPause();
120
+ await audioEncoder.ioSynchronizer.waitForQueueSize(10);
121
+ await controller._internals._mediaParserController._internals.checkForAbortAndPause();
122
+ audioEncoder.encode(newAudioData);
123
+ onMediaStateUpdate?.((prevState) => {
124
+ return {
125
+ ...prevState,
126
+ decodedAudioFrames: prevState.decodedAudioFrames + 1,
127
+ };
128
+ });
129
+ newAudioData.close();
130
+ },
131
+ });
132
+ const audioDecoder = (0, create_audio_decoder_1.internalCreateAudioDecoder)({
133
+ onFrame: async (audioData) => {
134
+ await controller._internals._mediaParserController._internals.checkForAbortAndPause();
135
+ await audioProcessingQueue.ioSynchronizer.waitForQueueSize(10);
136
+ audioProcessingQueue.input(audioData);
137
+ },
138
+ onError(error) {
139
+ abortConversion(new Error(`Audio decoder of track ${track.trackId} failed. Config: ${JSON.stringify(audioDecoderConfig)} (see .cause of this error)`, {
140
+ cause: error,
141
+ }));
142
+ },
143
+ controller,
144
+ config: audioDecoderConfig,
145
+ logLevel,
146
+ });
147
+ state.addWaitForFinishPromise(async () => {
148
+ await audioDecoder.waitForQueueToBeLessThan(0);
149
+ log_1.Log.verbose(logLevel, 'Audio decoder finished');
150
+ audioDecoder.close();
151
+ await audioProcessingQueue.ioSynchronizer.waitForQueueSize(0);
152
+ log_1.Log.verbose(logLevel, 'Audio processing queue finished');
153
+ await audioEncoder.waitForFinish();
154
+ log_1.Log.verbose(logLevel, 'Audio encoder finished');
155
+ audioEncoder.close();
156
+ });
157
+ return async (audioSample) => {
158
+ progressTracker.setPossibleLowestTimestamp(Math.min(audioSample.timestamp, audioSample.decodingTimestamp ?? Infinity));
159
+ await controller._internals._mediaParserController._internals.checkForAbortAndPause();
160
+ await audioDecoder.waitForQueueToBeLessThan(10);
161
+ audioDecoder.decode(audioSample);
162
+ };
163
+ };
164
+ exports.reencodeAudioTrack = reencodeAudioTrack;
@@ -0,0 +1,19 @@
1
+ import type { MediaParserLogLevel, MediaParserOnVideoSample, MediaParserVideoTrack } from '@remotion/media-parser';
2
+ import type { ConvertMediaOnVideoFrame } from './convert-media';
3
+ import type { MediaFn } from './create/media-fn';
4
+ import type { ProgressTracker } from './create/progress-tracker';
5
+ import type { VideoOperation } from './on-video-track-handler';
6
+ import type { ConvertMediaProgressFn } from './throttled-state-update';
7
+ import type { WebCodecsController } from './webcodecs-controller';
8
+ export declare const reencodeVideoTrack: ({ videoOperation, rotate, track, logLevel, abortConversion, onMediaStateUpdate, controller, onVideoFrame, state, progressTracker, }: {
9
+ videoOperation: VideoOperation;
10
+ rotate: number;
11
+ track: MediaParserVideoTrack;
12
+ logLevel: MediaParserLogLevel;
13
+ abortConversion: (errCause: Error) => void;
14
+ onMediaStateUpdate: null | ConvertMediaProgressFn;
15
+ controller: WebCodecsController;
16
+ onVideoFrame: ConvertMediaOnVideoFrame | null;
17
+ state: MediaFn;
18
+ progressTracker: ProgressTracker;
19
+ }) => Promise<MediaParserOnVideoSample | null>;
@@ -0,0 +1,151 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.reencodeVideoTrack = void 0;
4
+ const arraybuffer_to_uint8_array_1 = require("./arraybuffer-to-uint8-array");
5
+ const convert_encoded_chunk_1 = require("./convert-encoded-chunk");
6
+ const create_video_decoder_1 = require("./create-video-decoder");
7
+ const log_1 = require("./log");
8
+ const on_frame_1 = require("./on-frame");
9
+ const processing_queue_1 = require("./processing-queue");
10
+ const rotation_1 = require("./rotation");
11
+ const sort_video_frames_1 = require("./sort-video-frames");
12
+ const video_decoder_config_1 = require("./video-decoder-config");
13
+ const video_encoder_1 = require("./video-encoder");
14
+ const video_encoder_config_1 = require("./video-encoder-config");
15
+ const reencodeVideoTrack = async ({ videoOperation, rotate, track, logLevel, abortConversion, onMediaStateUpdate, controller, onVideoFrame, state, progressTracker, }) => {
16
+ if (videoOperation.type !== 'reencode') {
17
+ throw new Error(`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"`);
18
+ }
19
+ const rotation = (videoOperation.rotate ?? rotate) - track.rotation;
20
+ const { height: newHeight, width: newWidth } = (0, rotation_1.calculateNewDimensionsFromRotateAndScale)({
21
+ width: track.codedWidth,
22
+ height: track.codedHeight,
23
+ rotation,
24
+ videoCodec: videoOperation.videoCodec,
25
+ resizeOperation: videoOperation.resize ?? null,
26
+ });
27
+ const videoEncoderConfig = await (0, video_encoder_config_1.getVideoEncoderConfig)({
28
+ codec: videoOperation.videoCodec,
29
+ height: newHeight,
30
+ width: newWidth,
31
+ fps: track.fps,
32
+ });
33
+ const videoDecoderConfig = await (0, video_decoder_config_1.getVideoDecoderConfigWithHardwareAcceleration)(track);
34
+ log_1.Log.verbose(logLevel, 'Video encoder config', videoEncoderConfig);
35
+ log_1.Log.verbose(logLevel, 'Video decoder config', videoDecoderConfig ?? track);
36
+ if (videoEncoderConfig === null) {
37
+ abortConversion(new Error(`Could not configure video encoder of track ${track.trackId}`));
38
+ return null;
39
+ }
40
+ if (videoDecoderConfig === null) {
41
+ abortConversion(new Error(`Could not configure video decoder of track ${track.trackId}`));
42
+ return null;
43
+ }
44
+ const { trackNumber } = await state.addTrack({
45
+ type: 'video',
46
+ color: track.advancedColor,
47
+ width: newWidth,
48
+ height: newHeight,
49
+ codec: videoOperation.videoCodec,
50
+ codecPrivate: null,
51
+ timescale: track.originalTimescale,
52
+ });
53
+ log_1.Log.verbose(logLevel, `Created new video track with ID ${trackNumber}, codec ${videoOperation.videoCodec} and timescale ${track.originalTimescale}`);
54
+ const videoEncoder = (0, video_encoder_1.createVideoEncoder)({
55
+ onChunk: async (chunk, metadata) => {
56
+ await state.addSample({
57
+ chunk: (0, convert_encoded_chunk_1.convertEncodedChunk)(chunk),
58
+ trackNumber,
59
+ isVideo: true,
60
+ codecPrivate: (0, arraybuffer_to_uint8_array_1.arrayBufferToUint8Array)((metadata?.decoderConfig?.description ?? null)),
61
+ });
62
+ onMediaStateUpdate?.((prevState) => {
63
+ return {
64
+ ...prevState,
65
+ encodedVideoFrames: prevState.encodedVideoFrames + 1,
66
+ };
67
+ });
68
+ },
69
+ onError: (err) => {
70
+ abortConversion(new Error(`Video encoder of track ${track.trackId} failed (see .cause of this error)`, {
71
+ cause: err,
72
+ }));
73
+ },
74
+ controller,
75
+ config: videoEncoderConfig,
76
+ logLevel,
77
+ outputCodec: videoOperation.videoCodec,
78
+ keyframeInterval: 40,
79
+ });
80
+ const videoProcessingQueue = (0, processing_queue_1.processingQueue)({
81
+ controller,
82
+ label: 'VideoFrame processing queue',
83
+ logLevel,
84
+ onError: (err) => {
85
+ abortConversion(new Error(`VideoFrame processing queue of track ${track.trackId} failed (see .cause of this error)`, {
86
+ cause: err,
87
+ }));
88
+ },
89
+ onOutput: async (frame) => {
90
+ await controller._internals._mediaParserController._internals.checkForAbortAndPause();
91
+ const processedFrame = await (0, on_frame_1.onFrame)({
92
+ frame,
93
+ track,
94
+ onVideoFrame,
95
+ outputCodec: videoOperation.videoCodec,
96
+ rotation,
97
+ resizeOperation: videoOperation.resize ?? null,
98
+ });
99
+ await controller._internals._mediaParserController._internals.checkForAbortAndPause();
100
+ await videoEncoder.ioSynchronizer.waitForQueueSize(10);
101
+ await controller._internals._mediaParserController._internals.checkForAbortAndPause();
102
+ videoEncoder.encode(processedFrame);
103
+ processedFrame.close();
104
+ },
105
+ });
106
+ const frameSorter = (0, sort_video_frames_1.videoFrameSorter)({
107
+ controller,
108
+ onOutput: async (frame) => {
109
+ await controller._internals._mediaParserController._internals.checkForAbortAndPause();
110
+ await videoProcessingQueue.ioSynchronizer.waitForQueueSize(10);
111
+ videoProcessingQueue.input(frame);
112
+ },
113
+ });
114
+ const videoDecoder = (0, create_video_decoder_1.createVideoDecoder)({
115
+ track: videoDecoderConfig,
116
+ onFrame: async (frame) => {
117
+ await frameSorter.waitUntilProcessed();
118
+ frameSorter.inputFrame(frame);
119
+ },
120
+ onError: (err) => {
121
+ abortConversion(new Error(`Video decoder of track ${track.trackId} failed (see .cause of this error)`, {
122
+ cause: err,
123
+ }));
124
+ },
125
+ controller,
126
+ logLevel,
127
+ });
128
+ state.addWaitForFinishPromise(async () => {
129
+ log_1.Log.verbose(logLevel, 'Waiting for video decoder to finish');
130
+ await videoDecoder.waitForQueueToBeLessThan(0);
131
+ videoDecoder.close();
132
+ log_1.Log.verbose(logLevel, 'Video decoder finished. Waiting for encoder to finish');
133
+ await frameSorter.flush();
134
+ log_1.Log.verbose(logLevel, 'Frame sorter flushed');
135
+ await videoProcessingQueue.ioSynchronizer.waitForQueueSize(0);
136
+ log_1.Log.verbose(logLevel, 'Video processing queue finished');
137
+ await videoEncoder.waitForFinish();
138
+ videoEncoder.close();
139
+ log_1.Log.verbose(logLevel, 'Video encoder finished');
140
+ });
141
+ return async (chunk) => {
142
+ progressTracker.setPossibleLowestTimestamp(Math.min(chunk.timestamp, chunk.decodingTimestamp ?? Infinity));
143
+ await controller._internals._mediaParserController._internals.checkForAbortAndPause();
144
+ await videoDecoder.waitForQueueToBeLessThan(10);
145
+ if (chunk.type === 'key') {
146
+ await videoDecoder.flush();
147
+ }
148
+ videoDecoder.decode(chunk);
149
+ };
150
+ };
151
+ exports.reencodeVideoTrack = reencodeVideoTrack;