@remotion/webcodecs 4.0.304 → 4.0.306

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/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/convert-encoded-chunk.d.ts +1 -1
  6. package/dist/convert-encoded-chunk.js +2 -5
  7. package/dist/convert-media.js +2 -2
  8. package/dist/copy-audio-track.d.ts +11 -0
  9. package/dist/copy-audio-track.js +31 -0
  10. package/dist/copy-video-track.d.ts +11 -0
  11. package/dist/copy-video-track.js +32 -0
  12. package/dist/create/event-emitter.d.ts +0 -1
  13. package/dist/create/iso-base-media/create-iso-base-media.js +3 -3
  14. package/dist/create/iso-base-media/example-stts.js +620 -620
  15. package/dist/create/iso-base-media/trak/mdia/minf/create-stbl.js +3 -1
  16. package/dist/create/iso-base-media/trak/mdia/minf/stbl/create-ctts.js +1 -1
  17. package/dist/create/iso-base-media/trak/mdia/minf/stbl/create-stts.js +3 -2
  18. package/dist/create/matroska/create-matroska-media.js +1 -1
  19. package/dist/create/progress-tracker.d.ts +0 -2
  20. package/dist/create/progress-tracker.js +3 -20
  21. package/dist/esm/index.mjs +583 -496
  22. package/dist/get-wave-audio-decoder.d.ts +6 -1
  23. package/dist/get-wave-audio-decoder.js +16 -11
  24. package/dist/io-manager/io-synchronizer.d.ts +6 -13
  25. package/dist/io-manager/io-synchronizer.js +31 -72
  26. package/dist/io-manager/make-timeout-promise.d.ts +1 -1
  27. package/dist/io-manager/make-timeout-promise.js +8 -4
  28. package/dist/on-audio-track.d.ts +2 -2
  29. package/dist/on-audio-track.js +15 -150
  30. package/dist/on-frame.d.ts +2 -4
  31. package/dist/on-frame.js +8 -9
  32. package/dist/on-video-track.d.ts +2 -2
  33. package/dist/on-video-track.js +18 -129
  34. package/dist/processing-queue.d.ts +19 -0
  35. package/dist/processing-queue.js +47 -0
  36. package/dist/reencode-audio-track.d.ts +18 -0
  37. package/dist/reencode-audio-track.js +164 -0
  38. package/dist/reencode-video-track.d.ts +19 -0
  39. package/dist/reencode-video-track.js +151 -0
  40. package/dist/sort-video-frames.d.ts +4 -3
  41. package/dist/sort-video-frames.js +7 -3
  42. package/dist/video-decoder.d.ts +14 -8
  43. package/dist/video-decoder.js +37 -72
  44. package/dist/video-encoder.d.ts +6 -5
  45. package/dist/video-encoder.js +16 -40
  46. package/dist/wav-audio-encoder.d.ts +4 -1
  47. package/dist/wav-audio-encoder.js +3 -2
  48. package/package.json +5 -5
@@ -1,61 +1,35 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.createVideoDecoder = void 0;
3
+ exports.createVideoDecoder = exports.internalCreateVideoDecoder = void 0;
4
4
  const io_synchronizer_1 = require("./io-manager/io-synchronizer");
5
5
  const log_1 = require("./log");
6
- const sort_video_frames_1 = require("./sort-video-frames");
7
- const createVideoDecoder = ({ onFrame, onError, controller, config, logLevel, progress, }) => {
6
+ const internalCreateVideoDecoder = ({ onFrame, onError, controller, config, logLevel, }) => {
8
7
  const ioSynchronizer = (0, io_synchronizer_1.makeIoSynchronizer)({
9
8
  logLevel,
10
9
  label: 'Video decoder',
11
- progress,
12
- });
13
- let outputQueue = Promise.resolve();
14
- const addToQueue = (frame) => {
15
- const cleanup = () => {
16
- frame.close();
17
- };
18
- controller._internals._mediaParserController._internals.signal.addEventListener('abort', cleanup, {
19
- once: true,
20
- });
21
- outputQueue = outputQueue
22
- .then(() => {
23
- if (controller._internals._mediaParserController._internals.signal.aborted) {
24
- return;
25
- }
26
- return onFrame(frame);
27
- })
28
- .then(() => {
29
- ioSynchronizer.onProcessed();
30
- })
31
- .catch((err) => {
32
- onError(err);
33
- })
34
- .finally(() => {
35
- controller._internals._mediaParserController._internals.signal.removeEventListener('abort', cleanup);
36
- cleanup();
37
- });
38
- return outputQueue;
39
- };
40
- const frameSorter = (0, sort_video_frames_1.videoFrameSorter)({
41
10
  controller,
42
- onRelease: async (frame) => {
43
- await addToQueue(frame);
44
- },
45
11
  });
46
12
  const videoDecoder = new VideoDecoder({
47
- output(frame) {
13
+ async output(frame) {
14
+ try {
15
+ await onFrame(frame);
16
+ }
17
+ catch (err) {
18
+ onError(err);
19
+ frame.close();
20
+ }
48
21
  ioSynchronizer.onOutput(frame.timestamp);
49
- frameSorter.inputFrame(frame);
50
22
  },
51
23
  error(error) {
52
24
  onError(error);
53
25
  },
54
26
  });
55
27
  const close = () => {
56
- controller._internals._mediaParserController._internals.signal.removeEventListener('abort',
57
- // eslint-disable-next-line @typescript-eslint/no-use-before-define
58
- onAbort);
28
+ if (controller) {
29
+ controller._internals._mediaParserController._internals.signal.removeEventListener('abort',
30
+ // eslint-disable-next-line @typescript-eslint/no-use-before-define
31
+ onAbort);
32
+ }
59
33
  if (videoDecoder.state === 'closed') {
60
34
  return;
61
35
  }
@@ -64,52 +38,43 @@ const createVideoDecoder = ({ onFrame, onError, controller, config, logLevel, pr
64
38
  const onAbort = () => {
65
39
  close();
66
40
  };
67
- controller._internals._mediaParserController._internals.signal.addEventListener('abort', onAbort);
41
+ if (controller) {
42
+ controller._internals._mediaParserController._internals.signal.addEventListener('abort', onAbort);
43
+ }
68
44
  videoDecoder.configure(config);
69
- const processSample = async (sample) => {
45
+ const decode = (sample) => {
70
46
  if (videoDecoder.state === 'closed') {
71
47
  return;
72
48
  }
73
- // @ts-expect-error - can have changed in the meanwhile
74
- if (videoDecoder.state === 'closed') {
75
- return;
76
- }
77
- progress.setPossibleLowestTimestamp(Math.min(sample.timestamp, sample.dts ?? Infinity, sample.cts ?? Infinity));
78
- await ioSynchronizer.waitFor({
79
- unemitted: 20,
80
- unprocessed: 10,
81
- minimumProgress: sample.timestamp - 10000000,
82
- controller,
83
- });
84
- // Don't flush here.
85
- // We manually keep track of the memory with the IO synchornizer.
86
- // Example of flushing breaking things:
87
- // IMG_2310.MOV has B-frames, and if we flush on a keyframe, we discard some frames that are yet to come.
88
- videoDecoder.decode(new EncodedVideoChunk(sample));
89
- ioSynchronizer.inputItem(sample.timestamp, sample.type === 'key');
49
+ const encodedChunk = sample instanceof EncodedVideoChunk
50
+ ? sample
51
+ : new EncodedVideoChunk(sample);
52
+ videoDecoder.decode(encodedChunk);
53
+ ioSynchronizer.inputItem(sample.timestamp);
90
54
  };
91
- let inputQueue = Promise.resolve();
92
55
  return {
93
- processSample: (sample) => {
94
- inputQueue = inputQueue.then(() => processSample(sample));
95
- return inputQueue;
96
- },
56
+ decode,
97
57
  waitForFinish: async () => {
98
58
  await videoDecoder.flush();
99
59
  log_1.Log.verbose(logLevel, 'Flushed video decoder');
100
- await frameSorter.flush();
101
- log_1.Log.verbose(logLevel, 'Frame sorter flushed');
102
- await ioSynchronizer.waitForFinish(controller);
60
+ await ioSynchronizer.waitForFinish();
103
61
  log_1.Log.verbose(logLevel, 'IO synchro finished');
104
- await outputQueue;
105
- log_1.Log.verbose(logLevel, 'Output queue finished');
106
- await inputQueue;
107
- log_1.Log.verbose(logLevel, 'Input queue finished');
108
62
  },
109
63
  close,
110
64
  flush: async () => {
111
65
  await videoDecoder.flush();
112
66
  },
67
+ waitForQueueToBeLessThan: ioSynchronizer.waitForQueueSize,
113
68
  };
114
69
  };
70
+ exports.internalCreateVideoDecoder = internalCreateVideoDecoder;
71
+ const createVideoDecoder = ({ onFrame, onError, controller, track, logLevel, }) => {
72
+ return (0, exports.internalCreateVideoDecoder)({
73
+ onFrame,
74
+ onError,
75
+ controller: controller ?? null,
76
+ config: track,
77
+ logLevel: logLevel ?? 'info',
78
+ });
79
+ };
115
80
  exports.createVideoDecoder = createVideoDecoder;
@@ -1,19 +1,20 @@
1
1
  import { type MediaParserLogLevel } from '@remotion/media-parser';
2
- import type { ProgressTracker } from './create/progress-tracker';
3
2
  import type { ConvertMediaVideoCodec } from './get-available-video-codecs';
3
+ import type { IoSynchronizer } from './io-manager/io-synchronizer';
4
4
  import type { WebCodecsController } from './webcodecs-controller';
5
5
  export type WebCodecsVideoEncoder = {
6
- encodeFrame: (videoFrame: VideoFrame, timestamp: number) => Promise<void>;
6
+ encode: (videoFrame: VideoFrame) => void;
7
7
  waitForFinish: () => Promise<void>;
8
8
  close: () => void;
9
9
  flush: () => Promise<void>;
10
+ ioSynchronizer: IoSynchronizer;
10
11
  };
11
- export declare const createVideoEncoder: ({ onChunk, onError, controller, config, logLevel, outputCodec, progress, }: {
12
+ export declare const createVideoEncoder: ({ onChunk, onError, controller, config, logLevel, outputCodec, keyframeInterval, }: {
12
13
  onChunk: (chunk: EncodedVideoChunk, metadata: EncodedVideoChunkMetadata | null) => Promise<void>;
13
- onError: (error: DOMException) => void;
14
+ onError: (error: Error) => void;
14
15
  controller: WebCodecsController;
15
16
  config: VideoEncoderConfig;
16
17
  logLevel: MediaParserLogLevel;
17
18
  outputCodec: ConvertMediaVideoCodec;
18
- progress: ProgressTracker;
19
+ keyframeInterval: number;
19
20
  }) => WebCodecsVideoEncoder;
@@ -5,38 +5,28 @@ const media_parser_1 = require("@remotion/media-parser");
5
5
  const convert_to_correct_videoframe_1 = require("./convert-to-correct-videoframe");
6
6
  const io_synchronizer_1 = require("./io-manager/io-synchronizer");
7
7
  const log_1 = require("./log");
8
- const createVideoEncoder = ({ onChunk, onError, controller, config, logLevel, outputCodec, progress, }) => {
8
+ const createVideoEncoder = ({ onChunk, onError, controller, config, logLevel, outputCodec, keyframeInterval, }) => {
9
9
  if (controller._internals._mediaParserController._internals.signal.aborted) {
10
10
  throw new media_parser_1.MediaParserAbortError('Not creating video encoder, already aborted');
11
11
  }
12
12
  const ioSynchronizer = (0, io_synchronizer_1.makeIoSynchronizer)({
13
13
  logLevel,
14
14
  label: 'Video encoder',
15
- progress,
15
+ controller,
16
16
  });
17
- let outputQueue = Promise.resolve();
18
17
  const encoder = new VideoEncoder({
19
18
  error(error) {
20
19
  onError(error);
21
20
  },
22
- output(chunk, metadata) {
21
+ async output(chunk, metadata) {
23
22
  const timestamp = chunk.timestamp + (chunk.duration ?? 0);
24
- ioSynchronizer.onOutput(timestamp);
25
- outputQueue = outputQueue
26
- .then(() => {
27
- if (controller._internals._mediaParserController._internals.signal
28
- .aborted) {
29
- return;
30
- }
31
- return onChunk(chunk, metadata ?? null);
32
- })
33
- .then(() => {
34
- ioSynchronizer.onProcessed();
35
- return Promise.resolve();
36
- })
37
- .catch((err) => {
23
+ try {
24
+ await onChunk(chunk, metadata ?? null);
25
+ }
26
+ catch (err) {
38
27
  onError(err);
39
- });
28
+ }
29
+ ioSynchronizer.onOutput(timestamp);
40
30
  },
41
31
  });
42
32
  const close = () => {
@@ -55,23 +45,11 @@ const createVideoEncoder = ({ onChunk, onError, controller, config, logLevel, ou
55
45
  log_1.Log.verbose(logLevel, 'Configuring video encoder', config);
56
46
  encoder.configure(config);
57
47
  let framesProcessed = 0;
58
- const encodeFrame = async (frame) => {
59
- if (encoder.state === 'closed') {
60
- return;
61
- }
62
- progress.setPossibleLowestTimestamp(frame.timestamp);
63
- await ioSynchronizer.waitFor({
64
- // Firefox stalls if too few frames are passed
65
- unemitted: 10,
66
- unprocessed: 10,
67
- minimumProgress: frame.timestamp - 10000000,
68
- controller,
69
- });
70
- // @ts-expect-error - can have changed in the meanwhile
48
+ const encodeFrame = (frame) => {
71
49
  if (encoder.state === 'closed') {
72
50
  return;
73
51
  }
74
- const keyFrame = framesProcessed % 40 === 0;
52
+ const keyFrame = framesProcessed % keyframeInterval === 0;
75
53
  encoder.encode((0, convert_to_correct_videoframe_1.convertToCorrectVideoFrame)({ videoFrame: frame, outputCodec }), {
76
54
  keyFrame,
77
55
  // @ts-expect-error
@@ -79,24 +57,22 @@ const createVideoEncoder = ({ onChunk, onError, controller, config, logLevel, ou
79
57
  quantizer: 36,
80
58
  },
81
59
  });
82
- ioSynchronizer.inputItem(frame.timestamp, keyFrame);
60
+ ioSynchronizer.inputItem(frame.timestamp);
83
61
  framesProcessed++;
84
62
  };
85
- let inputQueue = Promise.resolve();
86
63
  return {
87
- encodeFrame: (frame) => {
88
- inputQueue = inputQueue.then(() => encodeFrame(frame));
89
- return inputQueue;
64
+ encode: (frame) => {
65
+ encodeFrame(frame);
90
66
  },
91
67
  waitForFinish: async () => {
92
68
  await encoder.flush();
93
- await outputQueue;
94
- await ioSynchronizer.waitForFinish(controller);
69
+ await ioSynchronizer.waitForFinish();
95
70
  },
96
71
  close,
97
72
  flush: async () => {
98
73
  await encoder.flush();
99
74
  },
75
+ ioSynchronizer,
100
76
  };
101
77
  };
102
78
  exports.createVideoEncoder = createVideoEncoder;
@@ -1,2 +1,5 @@
1
1
  import type { AudioEncoderInit, WebCodecsAudioEncoder } from './audio-encoder';
2
- export declare const getWaveAudioEncoder: ({ onChunk, controller, config, }: Pick<AudioEncoderInit, "onChunk" | "controller" | "config">) => WebCodecsAudioEncoder;
2
+ import type { IoSynchronizer } from './io-manager/io-synchronizer';
3
+ export declare const getWaveAudioEncoder: ({ onChunk, controller, config, ioSynchronizer, }: Pick<AudioEncoderInit, "onChunk" | "controller" | "config"> & {
4
+ ioSynchronizer: IoSynchronizer;
5
+ }) => WebCodecsAudioEncoder;
@@ -2,12 +2,12 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.getWaveAudioEncoder = void 0;
4
4
  const convert_audiodata_1 = require("./convert-audiodata");
5
- const getWaveAudioEncoder = ({ onChunk, controller, config, }) => {
5
+ const getWaveAudioEncoder = ({ onChunk, controller, config, ioSynchronizer, }) => {
6
6
  return {
7
7
  close: () => {
8
8
  return Promise.resolve();
9
9
  },
10
- encodeFrame: (unconvertedAudioData) => {
10
+ encode: (unconvertedAudioData) => {
11
11
  if (controller._internals._mediaParserController._internals.signal.aborted) {
12
12
  return Promise.resolve();
13
13
  }
@@ -28,6 +28,7 @@ const getWaveAudioEncoder = ({ onChunk, controller, config, }) => {
28
28
  },
29
29
  flush: () => Promise.resolve(),
30
30
  waitForFinish: () => Promise.resolve(),
31
+ ioSynchronizer,
31
32
  };
32
33
  };
33
34
  exports.getWaveAudioEncoder = getWaveAudioEncoder;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@remotion/webcodecs",
3
- "version": "4.0.304",
3
+ "version": "4.0.306",
4
4
  "main": "dist/index.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "module": "dist/esm/index.mjs",
@@ -19,8 +19,8 @@
19
19
  "author": "Jonny Burger <jonny@remotion.dev>",
20
20
  "license": "Remotion License (See https://remotion.dev/docs/webcodecs#license)",
21
21
  "dependencies": {
22
- "@remotion/media-parser": "4.0.304",
23
- "@remotion/licensing": "4.0.304"
22
+ "@remotion/media-parser": "4.0.306",
23
+ "@remotion/licensing": "4.0.306"
24
24
  },
25
25
  "peerDependencies": {},
26
26
  "devDependencies": {
@@ -28,8 +28,8 @@
28
28
  "playwright": "1.51.1",
29
29
  "@playwright/test": "1.51.1",
30
30
  "eslint": "9.19.0",
31
- "@remotion/eslint-config-internal": "4.0.304",
32
- "@remotion/example-videos": "4.0.304"
31
+ "@remotion/example-videos": "4.0.306",
32
+ "@remotion/eslint-config-internal": "4.0.306"
33
33
  },
34
34
  "keywords": [],
35
35
  "publishConfig": {