@remotion/webcodecs 4.0.236 → 4.0.239

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 (36) hide show
  1. package/dist/audio-decoder.js +11 -7
  2. package/dist/audio-encoder.js +4 -2
  3. package/dist/auto-select-writer.d.ts +1 -1
  4. package/dist/auto-select-writer.js +22 -5
  5. package/dist/can-copy-audio-track.d.ts +4 -3
  6. package/dist/can-copy-audio-track.js +7 -6
  7. package/dist/can-copy-video-track.d.ts +4 -3
  8. package/dist/can-copy-video-track.js +7 -6
  9. package/dist/convert-media.js +2 -2
  10. package/dist/default-on-audio-track-handler.js +9 -11
  11. package/dist/default-on-video-track-handler.js +6 -15
  12. package/dist/esm/index.mjs +188 -140
  13. package/dist/io-manager/io-synchronizer.d.ts +4 -3
  14. package/dist/io-manager/io-synchronizer.js +25 -10
  15. package/dist/on-audio-track-handler.d.ts +4 -2
  16. package/dist/on-audio-track.d.ts +2 -2
  17. package/dist/on-audio-track.js +13 -6
  18. package/dist/on-frame.js +4 -1
  19. package/dist/on-video-track-handler.d.ts +4 -2
  20. package/dist/on-video-track.d.ts +2 -2
  21. package/dist/on-video-track.js +15 -6
  22. package/dist/video-decoder.js +5 -2
  23. package/dist/video-encoder.js +4 -2
  24. package/package.json +4 -4
  25. package/dist/codec-id.d.ts +0 -10
  26. package/dist/codec-id.js +0 -38
  27. package/dist/create-aac-codecprivate.d.ts +0 -14
  28. package/dist/create-aac-codecprivate.js +0 -72
  29. package/dist/io-manager/event-emitter.d.ts +0 -31
  30. package/dist/io-manager/event-emitter.js +0 -25
  31. package/dist/rotate-video.d.ts +0 -4
  32. package/dist/rotate-video.js +0 -43
  33. package/dist/test/aac-codecprivate.test.js +0 -12
  34. package/dist/with-resolvers.d.ts +0 -10
  35. package/dist/with-resolvers.js +0 -28
  36. /package/dist/test/{aac-codecprivate.test.d.ts → avi-to-mp4.test.d.ts} +0 -0
@@ -17,10 +17,11 @@ const createAudioDecoder = ({ onFrame, onError, signal, config, logLevel, track,
17
17
  });
18
18
  let outputQueue = Promise.resolve();
19
19
  const audioDecoder = new AudioDecoder({
20
- output(inputFrame) {
21
- ioSynchronizer.onOutput(inputFrame.timestamp);
20
+ output(frame) {
21
+ var _a;
22
+ ioSynchronizer.onOutput(frame.timestamp + ((_a = frame.duration) !== null && _a !== void 0 ? _a : 0));
22
23
  const abortHandler = () => {
23
- inputFrame.close();
24
+ frame.close();
24
25
  };
25
26
  signal.addEventListener('abort', abortHandler, { once: true });
26
27
  outputQueue = outputQueue
@@ -28,7 +29,7 @@ const createAudioDecoder = ({ onFrame, onError, signal, config, logLevel, track,
28
29
  if (signal.aborted) {
29
30
  return;
30
31
  }
31
- return onFrame(inputFrame);
32
+ return onFrame(frame);
32
33
  })
33
34
  .then(() => {
34
35
  ioSynchronizer.onProcessed();
@@ -36,7 +37,7 @@ const createAudioDecoder = ({ onFrame, onError, signal, config, logLevel, track,
36
37
  return Promise.resolve();
37
38
  })
38
39
  .catch((err) => {
39
- inputFrame.close();
40
+ frame.close();
40
41
  onError(err);
41
42
  });
42
43
  },
@@ -58,13 +59,16 @@ const createAudioDecoder = ({ onFrame, onError, signal, config, logLevel, track,
58
59
  signal.addEventListener('abort', onAbort);
59
60
  audioDecoder.configure(config);
60
61
  const processSample = async (audioSample) => {
62
+ var _a, _b;
61
63
  if (audioDecoder.state === 'closed') {
62
64
  return;
63
65
  }
66
+ progressTracker.setPossibleLowestTimestamp(Math.min(audioSample.timestamp, (_a = audioSample.dts) !== null && _a !== void 0 ? _a : Infinity, (_b = audioSample.cts) !== null && _b !== void 0 ? _b : Infinity));
64
67
  await ioSynchronizer.waitFor({
65
68
  unemitted: 20,
66
- _unprocessed: 20,
69
+ unprocessed: 20,
67
70
  minimumProgress: audioSample.timestamp - 10000000,
71
+ signal,
68
72
  });
69
73
  // Don't flush, it messes up the audio
70
74
  const chunk = new EncodedAudioChunk(audioSample);
@@ -89,7 +93,7 @@ const createAudioDecoder = ({ onFrame, onError, signal, config, logLevel, track,
89
93
  }
90
94
  catch (_a) { }
91
95
  await queue;
92
- await ioSynchronizer.waitForFinish();
96
+ await ioSynchronizer.waitForFinish(signal);
93
97
  await outputQueue;
94
98
  },
95
99
  close,
@@ -58,10 +58,12 @@ const createAudioEncoder = ({ onChunk, onError, codec, signal, config: audioEnco
58
58
  if (encoder.state === 'closed') {
59
59
  return;
60
60
  }
61
+ progressTracker.setPossibleLowestTimestamp(audioData.timestamp);
61
62
  await ioSynchronizer.waitFor({
62
63
  unemitted: 20,
63
- _unprocessed: 20,
64
+ unprocessed: 20,
64
65
  minimumProgress: audioData.timestamp - 10000000,
66
+ signal,
65
67
  });
66
68
  // @ts-expect-error - can have changed in the meanwhile
67
69
  if (encoder.state === 'closed') {
@@ -90,7 +92,7 @@ const createAudioEncoder = ({ onChunk, onError, codec, signal, config: audioEnco
90
92
  },
91
93
  waitForFinish: async () => {
92
94
  await encoder.flush();
93
- await ioSynchronizer.waitForFinish();
95
+ await ioSynchronizer.waitForFinish(signal);
94
96
  await prom;
95
97
  },
96
98
  close,
@@ -1,3 +1,3 @@
1
- import type { WriterInterface } from '@remotion/media-parser';
1
+ import { type WriterInterface } from '@remotion/media-parser';
2
2
  import type { LogLevel } from './log';
3
3
  export declare const autoSelectWriter: (writer: WriterInterface | undefined, logLevel: LogLevel) => Promise<WriterInterface>;
@@ -1,8 +1,9 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.autoSelectWriter = void 0;
4
- const buffer_1 = require("@remotion/media-parser/buffer");
5
4
  const web_fs_1 = require("@remotion/media-parser/web-fs");
5
+ const media_parser_1 = require("@remotion/media-parser");
6
+ const buffer_1 = require("@remotion/media-parser/buffer");
6
7
  const log_1 = require("./log");
7
8
  const autoSelectWriter = async (writer, logLevel) => {
8
9
  if (writer) {
@@ -10,11 +11,27 @@ const autoSelectWriter = async (writer, logLevel) => {
10
11
  return writer;
11
12
  }
12
13
  log_1.Log.verbose(logLevel, 'Determining best writer');
13
- if (await (0, web_fs_1.canUseWebFsWriter)()) {
14
- log_1.Log.verbose(logLevel, 'Using WebFS writer because it is supported');
15
- return web_fs_1.webFsWriter;
14
+ // Check if we're offline using the navigator API
15
+ const isOffline = !navigator.onLine;
16
+ if (isOffline) {
17
+ log_1.Log.verbose(logLevel, 'Offline mode detected, using buffer writer');
18
+ return buffer_1.bufferWriter;
19
+ }
20
+ try {
21
+ const { promise: timeout, reject, resolve, } = media_parser_1.MediaParserInternals.withResolvers();
22
+ const time = setTimeout(() => reject(new Error('WebFS check timeout')), 2000);
23
+ const webFsSupported = await Promise.race([(0, web_fs_1.canUseWebFsWriter)(), timeout]);
24
+ resolve();
25
+ clearTimeout(time);
26
+ if (webFsSupported) {
27
+ log_1.Log.verbose(logLevel, 'Using WebFS writer because it is supported');
28
+ return web_fs_1.webFsWriter;
29
+ }
30
+ }
31
+ catch (err) {
32
+ log_1.Log.verbose(logLevel, `WebFS check failed: ${err}. Falling back to buffer writer`);
16
33
  }
17
- log_1.Log.verbose(logLevel, 'Using buffer writer because WebFS writer is not supported');
34
+ log_1.Log.verbose(logLevel, 'Using buffer writer because WebFS writer is not supported or unavailable');
18
35
  return buffer_1.bufferWriter;
19
36
  };
20
37
  exports.autoSelectWriter = autoSelectWriter;
@@ -1,6 +1,7 @@
1
- import type { MediaParserAudioCodec } from '@remotion/media-parser';
1
+ import type { MediaParserAudioCodec, ParseMediaContainer } from '@remotion/media-parser';
2
2
  import type { ConvertMediaContainer } from './get-available-containers';
3
- export declare const canCopyAudioTrack: ({ inputCodec, container, }: {
3
+ export declare const canCopyAudioTrack: ({ inputCodec, outputContainer, inputContainer, }: {
4
4
  inputCodec: MediaParserAudioCodec;
5
- container: ConvertMediaContainer;
5
+ outputContainer: ConvertMediaContainer;
6
+ inputContainer: ParseMediaContainer;
6
7
  }) => boolean;
@@ -1,16 +1,17 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.canCopyAudioTrack = void 0;
4
- const canCopyAudioTrack = ({ inputCodec, container, }) => {
5
- if (container === 'webm') {
4
+ const canCopyAudioTrack = ({ inputCodec, outputContainer, inputContainer, }) => {
5
+ if (outputContainer === 'webm') {
6
6
  return inputCodec === 'opus';
7
7
  }
8
- if (container === 'mp4') {
9
- return inputCodec === 'aac';
8
+ if (outputContainer === 'mp4') {
9
+ return (inputCodec === 'aac' &&
10
+ (inputContainer === 'mp4' || inputContainer === 'avi'));
10
11
  }
11
- if (container === 'wav') {
12
+ if (outputContainer === 'wav') {
12
13
  return false;
13
14
  }
14
- throw new Error(`Unhandled codec: ${container}`);
15
+ throw new Error(`Unhandled container: ${outputContainer}`);
15
16
  };
16
17
  exports.canCopyAudioTrack = canCopyAudioTrack;
@@ -1,8 +1,9 @@
1
- import type { MediaParserVideoCodec } from '@remotion/media-parser';
1
+ import type { MediaParserVideoCodec, ParseMediaContainer } from '@remotion/media-parser';
2
2
  import type { ConvertMediaContainer } from './get-available-containers';
3
- export declare const canCopyVideoTrack: ({ inputCodec, container, inputRotation, rotationToApply, }: {
3
+ export declare const canCopyVideoTrack: ({ inputCodec, outputContainer, inputRotation, rotationToApply, inputContainer, }: {
4
+ inputContainer: ParseMediaContainer;
4
5
  inputCodec: MediaParserVideoCodec;
5
6
  inputRotation: number;
6
7
  rotationToApply: number;
7
- container: ConvertMediaContainer;
8
+ outputContainer: ConvertMediaContainer;
8
9
  }) => boolean;
@@ -2,20 +2,21 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.canCopyVideoTrack = void 0;
4
4
  const rotate_video_frame_1 = require("./rotate-video-frame");
5
- const canCopyVideoTrack = ({ inputCodec, container, inputRotation, rotationToApply, }) => {
5
+ const canCopyVideoTrack = ({ inputCodec, outputContainer, inputRotation, rotationToApply, inputContainer, }) => {
6
6
  if ((0, rotate_video_frame_1.normalizeVideoRotation)(inputRotation) !==
7
7
  (0, rotate_video_frame_1.normalizeVideoRotation)(rotationToApply)) {
8
8
  return false;
9
9
  }
10
- if (container === 'webm') {
10
+ if (outputContainer === 'webm') {
11
11
  return inputCodec === 'vp8' || inputCodec === 'vp9';
12
12
  }
13
- if (container === 'mp4') {
14
- return inputCodec === 'h264';
13
+ if (outputContainer === 'mp4') {
14
+ return (inputCodec === 'h264' &&
15
+ (inputContainer === 'mp4' || inputContainer === 'avi'));
15
16
  }
16
- if (container === 'wav') {
17
+ if (outputContainer === 'wav') {
17
18
  return false;
18
19
  }
19
- throw new Error(`Unhandled codec: ${container}`);
20
+ throw new Error(`Unhandled codec: ${outputContainer}`);
20
21
  };
21
22
  exports.canCopyVideoTrack = canCopyVideoTrack;
@@ -87,7 +87,7 @@ const convertMedia = async function ({ src, onVideoFrame, onProgress: onProgress
87
87
  defaultVideoCodec: videoCodec !== null && videoCodec !== void 0 ? videoCodec : null,
88
88
  onVideoTrack: userVideoResolver !== null && userVideoResolver !== void 0 ? userVideoResolver : null,
89
89
  logLevel,
90
- container,
90
+ outputContainer: container,
91
91
  rotate: rotate !== null && rotate !== void 0 ? rotate : 0,
92
92
  progress: progressTracker,
93
93
  });
@@ -99,7 +99,7 @@ const convertMedia = async function ({ src, onVideoFrame, onProgress: onProgress
99
99
  state,
100
100
  onAudioTrack: userAudioResolver !== null && userAudioResolver !== void 0 ? userAudioResolver : null,
101
101
  logLevel,
102
- container,
102
+ outputContainer: container,
103
103
  progressTracker,
104
104
  });
105
105
  (0, media_parser_1.parseMedia)({
@@ -2,23 +2,21 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.defaultOnAudioTrackHandler = void 0;
4
4
  const media_parser_1 = require("@remotion/media-parser");
5
- const can_copy_audio_track_1 = require("./can-copy-audio-track");
6
5
  const can_reencode_audio_track_1 = require("./can-reencode-audio-track");
7
- const get_default_audio_codec_1 = require("./get-default-audio-codec");
8
6
  const DEFAULT_BITRATE = 128000;
9
- const defaultOnAudioTrackHandler = async ({ track, defaultAudioCodec, logLevel, container, }) => {
7
+ const defaultOnAudioTrackHandler = async ({ track, defaultAudioCodec, logLevel, canCopyTrack, }) => {
10
8
  const bitrate = DEFAULT_BITRATE;
11
- const canCopy = (0, can_copy_audio_track_1.canCopyAudioTrack)({
12
- inputCodec: track.codecWithoutConfig,
13
- container,
14
- });
15
- if (canCopy) {
9
+ if (canCopyTrack) {
16
10
  media_parser_1.MediaParserInternals.Log.verbose(logLevel, `Track ${track.trackId} (audio): Can copy track, therefore copying`);
17
11
  return Promise.resolve({ type: 'copy' });
18
12
  }
19
- const audioCodec = defaultAudioCodec !== null && defaultAudioCodec !== void 0 ? defaultAudioCodec : (0, get_default_audio_codec_1.getDefaultAudioCodec)({ container });
13
+ // The idea is that for example for GIFs, we will return defaultAudioCodec = null
14
+ if (defaultAudioCodec === null) {
15
+ media_parser_1.MediaParserInternals.Log.verbose(logLevel, `Track ${track.trackId} (audio): Container does not support audio, dropping audio`);
16
+ return Promise.resolve({ type: 'drop' });
17
+ }
20
18
  const canReencode = await (0, can_reencode_audio_track_1.canReencodeAudioTrack)({
21
- audioCodec,
19
+ audioCodec: defaultAudioCodec,
22
20
  track,
23
21
  bitrate,
24
22
  });
@@ -27,7 +25,7 @@ const defaultOnAudioTrackHandler = async ({ track, defaultAudioCodec, logLevel,
27
25
  return Promise.resolve({
28
26
  type: 'reencode',
29
27
  bitrate,
30
- audioCodec,
28
+ audioCodec: defaultAudioCodec,
31
29
  });
32
30
  }
33
31
  media_parser_1.MediaParserInternals.Log.verbose(logLevel, `Track ${track.trackId} (audio): Can neither re-encode nor copy, failing render`);
@@ -2,34 +2,25 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.defaultOnVideoTrackHandler = void 0;
4
4
  const media_parser_1 = require("@remotion/media-parser");
5
- const can_copy_video_track_1 = require("./can-copy-video-track");
6
5
  const can_reencode_video_track_1 = require("./can-reencode-video-track");
7
- const get_default_video_codec_1 = require("./get-default-video-codec");
8
- const defaultOnVideoTrackHandler = async ({ track, defaultVideoCodec, logLevel, container, rotate, }) => {
9
- const canCopy = (0, can_copy_video_track_1.canCopyVideoTrack)({
10
- inputCodec: track.codecWithoutConfig,
11
- container,
12
- inputRotation: track.rotation,
13
- rotationToApply: rotate,
14
- });
15
- if (canCopy) {
6
+ const defaultOnVideoTrackHandler = async ({ track, defaultVideoCodec, logLevel, rotate, canCopyTrack, }) => {
7
+ if (canCopyTrack) {
16
8
  media_parser_1.MediaParserInternals.Log.verbose(logLevel, `Track ${track.trackId} (video): Can copy, therefore copying`);
17
9
  return Promise.resolve({ type: 'copy' });
18
10
  }
19
- const videoCodec = defaultVideoCodec !== null && defaultVideoCodec !== void 0 ? defaultVideoCodec : (0, get_default_video_codec_1.getDefaultVideoCodec)({ container });
20
- if (videoCodec === null) {
21
- media_parser_1.MediaParserInternals.Log.verbose(logLevel, `Track ${track.trackId} (video): No default video codec, therefore dropping`);
11
+ if (defaultVideoCodec === null) {
12
+ media_parser_1.MediaParserInternals.Log.verbose(logLevel, `Track ${track.trackId} (video): Is audio container, therefore dropping video`);
22
13
  return Promise.resolve({ type: 'drop' });
23
14
  }
24
15
  const canReencode = await (0, can_reencode_video_track_1.canReencodeVideoTrack)({
25
- videoCodec,
16
+ videoCodec: defaultVideoCodec,
26
17
  track,
27
18
  });
28
19
  if (canReencode) {
29
20
  media_parser_1.MediaParserInternals.Log.verbose(logLevel, `Track ${track.trackId} (video): Cannot copy, but re-enconde, therefore re-encoding`);
30
21
  return Promise.resolve({
31
22
  type: 'reencode',
32
- videoCodec,
23
+ videoCodec: defaultVideoCodec,
33
24
  rotation: rotate - track.rotation,
34
25
  });
35
26
  }