@remotion/webcodecs 4.0.226 → 4.0.228

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.
@@ -0,0 +1,25 @@
1
+ type Input = {
2
+ timestamp: number;
3
+ keyframe: boolean;
4
+ };
5
+ type Output = {
6
+ timestamp: number;
7
+ };
8
+ type IoEventMap = {
9
+ input: Input;
10
+ output: Output;
11
+ };
12
+ export type IoEventTypes = keyof IoEventMap;
13
+ export type CallbackListener<T extends IoEventTypes> = (data: {
14
+ detail: IoEventMap[T];
15
+ }) => void;
16
+ type IoListeners = {
17
+ [EventType in IoEventTypes]: CallbackListener<EventType>[];
18
+ };
19
+ export declare class IoEventEmitter {
20
+ listeners: IoListeners;
21
+ addEventListener<Q extends IoEventTypes>(name: Q, callback: CallbackListener<Q>): void;
22
+ removeEventListener<Q extends IoEventTypes>(name: Q, callback: CallbackListener<Q>): void;
23
+ dispatchEvent<T extends IoEventTypes>(dispatchName: T, context: IoEventMap[T]): void;
24
+ }
25
+ export {};
@@ -0,0 +1,23 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.IoEventEmitter = void 0;
4
+ class IoEventEmitter {
5
+ constructor() {
6
+ this.listeners = {
7
+ input: [],
8
+ output: [],
9
+ };
10
+ }
11
+ addEventListener(name, callback) {
12
+ this.listeners[name].push(callback);
13
+ }
14
+ removeEventListener(name, callback) {
15
+ this.listeners[name] = this.listeners[name].filter((l) => l !== callback);
16
+ }
17
+ dispatchEvent(dispatchName, context) {
18
+ this.listeners[dispatchName].forEach((callback) => {
19
+ callback({ detail: context });
20
+ });
21
+ }
22
+ }
23
+ exports.IoEventEmitter = IoEventEmitter;
@@ -0,0 +1,27 @@
1
+ type Input = {
2
+ timestamp: number;
3
+ keyFrame: boolean;
4
+ };
5
+ type Output = {
6
+ timestamp: number;
7
+ };
8
+ type Processed = {};
9
+ type IoEventMap = {
10
+ input: Input;
11
+ output: Output;
12
+ processed: Processed;
13
+ };
14
+ export type IoEventTypes = keyof IoEventMap;
15
+ export type CallbackListener<T extends IoEventTypes> = (data: {
16
+ detail: IoEventMap[T];
17
+ }) => void;
18
+ type IoListeners = {
19
+ [EventType in IoEventTypes]: CallbackListener<EventType>[];
20
+ };
21
+ export declare class IoEventEmitter {
22
+ listeners: IoListeners;
23
+ addEventListener<Q extends IoEventTypes>(name: Q, callback: CallbackListener<Q>): void;
24
+ removeEventListener<Q extends IoEventTypes>(name: Q, callback: CallbackListener<Q>): void;
25
+ dispatchEvent<T extends IoEventTypes>(dispatchName: T, context: IoEventMap[T]): void;
26
+ }
27
+ export {};
@@ -0,0 +1,24 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.IoEventEmitter = void 0;
4
+ class IoEventEmitter {
5
+ constructor() {
6
+ this.listeners = {
7
+ input: [],
8
+ output: [],
9
+ processed: [],
10
+ };
11
+ }
12
+ addEventListener(name, callback) {
13
+ this.listeners[name].push(callback);
14
+ }
15
+ removeEventListener(name, callback) {
16
+ this.listeners[name] = this.listeners[name].filter((l) => l !== callback);
17
+ }
18
+ dispatchEvent(dispatchName, context) {
19
+ this.listeners[dispatchName].forEach((callback) => {
20
+ callback({ detail: context });
21
+ });
22
+ }
23
+ }
24
+ exports.IoEventEmitter = IoEventEmitter;
@@ -0,0 +1,12 @@
1
+ import type { LogLevel } from '../log';
2
+ export declare const makeIoSynchronizer: (logLevel: LogLevel, label: string) => {
3
+ inputItem: (timestamp: number, keyFrame: boolean) => void;
4
+ onOutput: (timestamp: number) => void;
5
+ waitFor: ({ _unprocessed, unemitted, }: {
6
+ unemitted: number;
7
+ _unprocessed: number;
8
+ }) => Promise<void>;
9
+ waitForFinish: () => Promise<void>;
10
+ onProcessed: () => void;
11
+ getUnprocessed: () => number;
12
+ };
@@ -0,0 +1,95 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.makeIoSynchronizer = void 0;
4
+ const log_1 = require("../log");
5
+ const with_resolvers_1 = require("../with-resolvers");
6
+ const event_emitter_1 = require("./event-emitter");
7
+ const makeIoSynchronizer = (logLevel, label) => {
8
+ const eventEmitter = new event_emitter_1.IoEventEmitter();
9
+ let lastInput = 0;
10
+ let lastInputKeyframe = 0;
11
+ let lastOutput = 0;
12
+ let inputsSinceLastOutput = 0;
13
+ let inputs = [];
14
+ let keyframes = [];
15
+ // Once WebCodecs emits items, the user has to handle them
16
+ // Let's keep count of how many items are unprocessed
17
+ let unprocessed = 0;
18
+ const getUnprocessed = () => unprocessed;
19
+ const getUnemittedItems = () => {
20
+ inputs = inputs.filter((input) => input > lastOutput);
21
+ return inputs.length;
22
+ };
23
+ const getUnemittedKeyframes = () => {
24
+ keyframes = keyframes.filter((keyframe) => keyframe > lastOutput);
25
+ return keyframes.length;
26
+ };
27
+ const printState = (prefix) => {
28
+ 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()}`);
29
+ };
30
+ const inputItem = (timestamp, keyFrame) => {
31
+ lastInput = timestamp;
32
+ if (keyFrame) {
33
+ lastInputKeyframe = timestamp;
34
+ keyframes.push(timestamp);
35
+ }
36
+ inputsSinceLastOutput++;
37
+ inputs.push(timestamp);
38
+ eventEmitter.dispatchEvent('input', {
39
+ timestamp,
40
+ keyFrame,
41
+ });
42
+ printState('Input item');
43
+ };
44
+ const onOutput = (timestamp) => {
45
+ lastOutput = timestamp;
46
+ inputsSinceLastOutput = 0;
47
+ eventEmitter.dispatchEvent('output', {
48
+ timestamp,
49
+ });
50
+ unprocessed++;
51
+ printState('Got output');
52
+ };
53
+ const waitForOutput = () => {
54
+ const { promise, resolve } = (0, with_resolvers_1.withResolvers)();
55
+ const on = () => {
56
+ eventEmitter.removeEventListener('output', on);
57
+ resolve();
58
+ };
59
+ eventEmitter.addEventListener('output', on);
60
+ return promise;
61
+ };
62
+ const waitForProcessed = () => {
63
+ const { promise, resolve } = (0, with_resolvers_1.withResolvers)();
64
+ const on = () => {
65
+ eventEmitter.removeEventListener('processed', on);
66
+ resolve();
67
+ };
68
+ eventEmitter.addEventListener('processed', on);
69
+ return promise;
70
+ };
71
+ const waitFor = async ({ _unprocessed, unemitted, }) => {
72
+ while (getUnemittedItems() > unemitted) {
73
+ await waitForOutput();
74
+ }
75
+ while (getUnprocessed() > _unprocessed) {
76
+ await waitForProcessed();
77
+ }
78
+ };
79
+ const waitForFinish = async () => {
80
+ await waitFor({ _unprocessed: 0, unemitted: 0 });
81
+ };
82
+ const onProcessed = () => {
83
+ eventEmitter.dispatchEvent('processed', {});
84
+ unprocessed--;
85
+ };
86
+ return {
87
+ inputItem,
88
+ onOutput,
89
+ waitFor,
90
+ waitForFinish,
91
+ onProcessed,
92
+ getUnprocessed,
93
+ };
94
+ };
95
+ exports.makeIoSynchronizer = makeIoSynchronizer;
package/dist/log.d.ts ADDED
@@ -0,0 +1,10 @@
1
+ import type { LogLevel } from '@remotion/media-parser';
2
+ declare const Log: {
3
+ trace: (logLevel: LogLevel, ...args: Parameters<typeof console.log>) => void;
4
+ verbose: (logLevel: LogLevel, ...args: Parameters<typeof console.log>) => void;
5
+ info: (logLevel: LogLevel, ...args: Parameters<typeof console.log>) => void;
6
+ warn: (logLevel: LogLevel, ...args: Parameters<typeof console.log>) => void;
7
+ error: (...args: Parameters<typeof console.log>) => void;
8
+ };
9
+ export { Log };
10
+ export type { LogLevel };
package/dist/log.js ADDED
@@ -0,0 +1,6 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Log = void 0;
4
+ const media_parser_1 = require("@remotion/media-parser");
5
+ const { Log } = media_parser_1.MediaParserInternals;
6
+ exports.Log = Log;
@@ -1,9 +1,9 @@
1
- import type { MediaFn, OnAudioTrack } from '@remotion/media-parser';
1
+ import type { LogLevel, MediaFn, OnAudioTrack } from '@remotion/media-parser';
2
2
  import type { ConvertMediaAudioCodec } from './codec-id';
3
3
  import type { ConvertMediaState } from './convert-media';
4
4
  import Error from './error-cause';
5
5
  import type { ResolveAudioActionFn } from './resolve-audio-action';
6
- export declare const makeAudioTrackHandler: ({ state, audioCodec, convertMediaState, controller, abortConversion, onMediaStateUpdate, onAudioTrack, bitrate, }: {
6
+ export declare const makeAudioTrackHandler: ({ state, audioCodec, convertMediaState, controller, abortConversion, onMediaStateUpdate, onAudioTrack, bitrate, logLevel, }: {
7
7
  state: MediaFn;
8
8
  audioCodec: ConvertMediaAudioCodec;
9
9
  convertMediaState: ConvertMediaState;
@@ -12,4 +12,5 @@ export declare const makeAudioTrackHandler: ({ state, audioCodec, convertMediaSt
12
12
  onMediaStateUpdate: null | ((state: ConvertMediaState) => void);
13
13
  onAudioTrack: ResolveAudioActionFn;
14
14
  bitrate: number;
15
+ logLevel: LogLevel;
15
16
  }) => OnAudioTrack;
@@ -10,7 +10,7 @@ const audio_encoder_1 = require("./audio-encoder");
10
10
  const audio_encoder_config_1 = require("./audio-encoder-config");
11
11
  const error_cause_1 = __importDefault(require("./error-cause"));
12
12
  const resolve_audio_action_1 = require("./resolve-audio-action");
13
- const makeAudioTrackHandler = ({ state, audioCodec, convertMediaState, controller, abortConversion, onMediaStateUpdate, onAudioTrack, bitrate, }) => async (track) => {
13
+ const makeAudioTrackHandler = ({ state, audioCodec, convertMediaState, controller, abortConversion, onMediaStateUpdate, onAudioTrack, bitrate, logLevel, }) => async (track) => {
14
14
  const audioEncoderConfig = await (0, audio_encoder_config_1.getAudioEncoderConfig)({
15
15
  codec: audioCodec,
16
16
  numberOfChannels: track.numberOfChannels,
@@ -76,6 +76,7 @@ const makeAudioTrackHandler = ({ state, audioCodec, convertMediaState, controlle
76
76
  codec: audioCodec,
77
77
  signal: controller.signal,
78
78
  config: audioEncoderConfig,
79
+ logLevel,
79
80
  });
80
81
  const audioDecoder = (0, audio_decoder_1.createAudioDecoder)({
81
82
  onFrame: async (frame) => {
@@ -91,6 +92,7 @@ const makeAudioTrackHandler = ({ state, audioCodec, convertMediaState, controlle
91
92
  },
92
93
  signal: controller.signal,
93
94
  config: audioDecoderConfig,
95
+ logLevel,
94
96
  });
95
97
  state.addWaitForFinishPromise(async () => {
96
98
  await audioDecoder.waitForFinish();
@@ -1,9 +1,9 @@
1
- import type { MediaFn, OnVideoTrack, VideoTrack } from '@remotion/media-parser';
1
+ import type { LogLevel, MediaFn, OnVideoTrack, VideoTrack } from '@remotion/media-parser';
2
2
  import type { ConvertMediaVideoCodec } from './codec-id';
3
3
  import type { ConvertMediaState } from './convert-media';
4
4
  import Error from './error-cause';
5
5
  import type { ResolveVideoActionFn } from './resolve-video-action';
6
- export declare const makeVideoTrackHandler: ({ state, onVideoFrame, onMediaStateUpdate, abortConversion, convertMediaState, controller, videoCodec, onVideoTrack, }: {
6
+ export declare const makeVideoTrackHandler: ({ state, onVideoFrame, onMediaStateUpdate, abortConversion, convertMediaState, controller, videoCodec, onVideoTrack, logLevel, }: {
7
7
  state: MediaFn;
8
8
  onVideoFrame: null | ((frame: VideoFrame, track: VideoTrack) => Promise<void>);
9
9
  onMediaStateUpdate: null | ((state: ConvertMediaState) => void);
@@ -12,4 +12,5 @@ export declare const makeVideoTrackHandler: ({ state, onVideoFrame, onMediaState
12
12
  controller: AbortController;
13
13
  videoCodec: ConvertMediaVideoCodec;
14
14
  onVideoTrack: ResolveVideoActionFn;
15
+ logLevel: LogLevel;
15
16
  }) => OnVideoTrack;
@@ -10,7 +10,10 @@ const video_decoder_1 = require("./video-decoder");
10
10
  const video_decoder_config_1 = require("./video-decoder-config");
11
11
  const video_encoder_1 = require("./video-encoder");
12
12
  const video_encoder_config_1 = require("./video-encoder-config");
13
- const makeVideoTrackHandler = ({ state, onVideoFrame, onMediaStateUpdate, abortConversion, convertMediaState, controller, videoCodec, onVideoTrack, }) => async (track) => {
13
+ const makeVideoTrackHandler = ({ state, onVideoFrame, onMediaStateUpdate, abortConversion, convertMediaState, controller, videoCodec, onVideoTrack, logLevel, }) => async (track) => {
14
+ if (controller.signal.aborted) {
15
+ throw new error_cause_1.default('Aborted');
16
+ }
14
17
  const videoEncoderConfig = await (0, video_encoder_config_1.getVideoEncoderConfig)({
15
18
  codec: videoCodec === 'vp9' ? 'vp09.00.10.08' : videoCodec,
16
19
  height: track.displayAspectHeight,
@@ -36,8 +39,8 @@ const makeVideoTrackHandler = ({ state, onVideoFrame, onMediaStateUpdate, abortC
36
39
  codec: track.codecWithoutConfig,
37
40
  codecPrivate: track.codecPrivate,
38
41
  });
39
- return (sample) => {
40
- state.addSample(new EncodedVideoChunk(sample), videoTrack.trackNumber, true);
42
+ return async (sample) => {
43
+ await state.addSample(new EncodedVideoChunk(sample), videoTrack.trackNumber, true);
41
44
  convertMediaState.decodedVideoFrames++;
42
45
  onMediaStateUpdate === null || onMediaStateUpdate === void 0 ? void 0 : onMediaStateUpdate({ ...convertMediaState });
43
46
  };
@@ -71,6 +74,7 @@ const makeVideoTrackHandler = ({ state, onVideoFrame, onMediaStateUpdate, abortC
71
74
  },
72
75
  signal: controller.signal,
73
76
  config: videoEncoderConfig,
77
+ logLevel,
74
78
  });
75
79
  const videoDecoder = (0, video_decoder_1.createVideoDecoder)({
76
80
  config: videoDecoderConfig,
@@ -87,6 +91,7 @@ const makeVideoTrackHandler = ({ state, onVideoFrame, onMediaStateUpdate, abortC
87
91
  }));
88
92
  },
89
93
  signal: controller.signal,
94
+ logLevel,
90
95
  });
91
96
  state.addWaitForFinishPromise(async () => {
92
97
  await videoDecoder.waitForFinish();
@@ -1,14 +1,14 @@
1
- import type { VideoSample } from '@remotion/media-parser';
1
+ import type { LogLevel, VideoSample } from '@remotion/media-parser';
2
2
  export type WebCodecsVideoDecoder = {
3
3
  processSample: (videoSample: VideoSample) => Promise<void>;
4
4
  waitForFinish: () => Promise<void>;
5
5
  close: () => void;
6
- getQueueSize: () => number;
7
6
  flush: () => Promise<void>;
8
7
  };
9
- export declare const createVideoDecoder: ({ onFrame, onError, signal, config, }: {
8
+ export declare const createVideoDecoder: ({ onFrame, onError, signal, config, logLevel, }: {
10
9
  onFrame: (frame: VideoFrame) => Promise<void>;
11
10
  onError: (error: DOMException) => void;
12
11
  signal: AbortSignal;
13
12
  config: VideoDecoderConfig;
13
+ logLevel: LogLevel;
14
14
  }) => WebCodecsVideoDecoder;
@@ -1,19 +1,32 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.createVideoDecoder = void 0;
4
- const createVideoDecoder = ({ onFrame, onError, signal, config, }) => {
4
+ const io_synchronizer_1 = require("./io-manager/io-synchronizer");
5
+ const createVideoDecoder = ({ onFrame, onError, signal, config, logLevel, }) => {
6
+ const ioSynchronizer = (0, io_synchronizer_1.makeIoSynchronizer)(logLevel, 'Video decoder');
5
7
  let outputQueue = Promise.resolve();
6
- let outputQueueSize = 0;
7
- let dequeueResolver = () => { };
8
8
  const videoDecoder = new VideoDecoder({
9
9
  output(inputFrame) {
10
- outputQueueSize++;
10
+ ioSynchronizer.onOutput(inputFrame.timestamp);
11
+ const abortHandler = () => {
12
+ inputFrame.close();
13
+ };
14
+ signal.addEventListener('abort', abortHandler, { once: true });
11
15
  outputQueue = outputQueue
12
- .then(() => onFrame(inputFrame))
13
16
  .then(() => {
14
- outputQueueSize--;
15
- dequeueResolver();
17
+ if (signal.aborted) {
18
+ return;
19
+ }
20
+ return onFrame(inputFrame);
21
+ })
22
+ .then(() => {
23
+ ioSynchronizer.onProcessed();
24
+ signal.removeEventListener('abort', abortHandler);
16
25
  return Promise.resolve();
26
+ })
27
+ .catch((err) => {
28
+ inputFrame.close();
29
+ onError(err);
17
30
  });
18
31
  },
19
32
  error(error) {
@@ -32,38 +45,21 @@ const createVideoDecoder = ({ onFrame, onError, signal, config, }) => {
32
45
  close();
33
46
  };
34
47
  signal.addEventListener('abort', onAbort);
35
- const getQueueSize = () => {
36
- return videoDecoder.decodeQueueSize + outputQueueSize;
37
- };
38
48
  videoDecoder.configure(config);
39
- const waitForDequeue = async () => {
40
- await new Promise((r) => {
41
- dequeueResolver = r;
42
- videoDecoder.addEventListener('dequeue', () => r(), {
43
- once: true,
44
- });
45
- });
46
- };
47
- const waitForFinish = async () => {
48
- while (getQueueSize() > 0) {
49
- await waitForDequeue();
50
- }
51
- };
52
49
  const processSample = async (sample) => {
53
50
  if (videoDecoder.state === 'closed') {
54
51
  return;
55
52
  }
56
- while (getQueueSize() > 10) {
57
- await waitForDequeue();
58
- }
59
53
  // @ts-expect-error - can have changed in the meanwhile
60
54
  if (videoDecoder.state === 'closed') {
61
55
  return;
62
56
  }
57
+ await ioSynchronizer.waitFor({ unemitted: 20, _unprocessed: 2 });
63
58
  if (sample.type === 'key') {
64
59
  await videoDecoder.flush();
65
60
  }
66
61
  videoDecoder.decode(new EncodedVideoChunk(sample));
62
+ ioSynchronizer.inputItem(sample.timestamp, sample.type === 'key');
67
63
  };
68
64
  let inputQueue = Promise.resolve();
69
65
  return {
@@ -73,12 +69,11 @@ const createVideoDecoder = ({ onFrame, onError, signal, config, }) => {
73
69
  },
74
70
  waitForFinish: async () => {
75
71
  await videoDecoder.flush();
76
- await waitForFinish();
72
+ await ioSynchronizer.waitForFinish();
77
73
  await outputQueue;
78
74
  await inputQueue;
79
75
  },
80
76
  close,
81
- getQueueSize,
82
77
  flush: async () => {
83
78
  await videoDecoder.flush();
84
79
  },
@@ -1,13 +1,14 @@
1
+ import type { LogLevel } from '@remotion/media-parser';
1
2
  export type WebCodecsVideoEncoder = {
2
3
  encodeFrame: (videoFrame: VideoFrame) => Promise<void>;
3
4
  waitForFinish: () => Promise<void>;
4
5
  close: () => void;
5
- getQueueSize: () => number;
6
6
  flush: () => Promise<void>;
7
7
  };
8
- export declare const createVideoEncoder: ({ onChunk, onError, signal, config, }: {
8
+ export declare const createVideoEncoder: ({ onChunk, onError, signal, config, logLevel, }: {
9
9
  onChunk: (chunk: EncodedVideoChunk) => Promise<void>;
10
10
  onError: (error: DOMException) => void;
11
11
  signal: AbortSignal;
12
12
  config: VideoEncoderConfig;
13
+ logLevel: LogLevel;
13
14
  }) => WebCodecsVideoEncoder;
@@ -1,25 +1,36 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.createVideoEncoder = void 0;
4
- const createVideoEncoder = ({ onChunk, onError, signal, config, }) => {
4
+ const io_synchronizer_1 = require("./io-manager/io-synchronizer");
5
+ const createVideoEncoder = ({ onChunk, onError, signal, config, logLevel, }) => {
5
6
  if (signal.aborted) {
6
7
  throw new Error('Not creating video encoder, already aborted');
7
8
  }
9
+ const ioSynchronizer = (0, io_synchronizer_1.makeIoSynchronizer)(logLevel, 'Video encoder');
8
10
  let outputQueue = Promise.resolve();
9
- let outputQueueSize = 0;
10
- let dequeueResolver = () => { };
11
11
  const encoder = new VideoEncoder({
12
12
  error(error) {
13
13
  onError(error);
14
14
  },
15
15
  output(chunk) {
16
- outputQueueSize++;
16
+ if (chunk.duration === null) {
17
+ throw new Error('Duration is null');
18
+ }
19
+ const timestamp = chunk.timestamp + chunk.duration;
20
+ ioSynchronizer.onOutput(timestamp);
17
21
  outputQueue = outputQueue
18
- .then(() => onChunk(chunk))
19
22
  .then(() => {
20
- outputQueueSize--;
21
- dequeueResolver();
23
+ if (signal.aborted) {
24
+ return;
25
+ }
26
+ return onChunk(chunk);
27
+ })
28
+ .then(() => {
29
+ ioSynchronizer.onProcessed();
22
30
  return Promise.resolve();
31
+ })
32
+ .catch((err) => {
33
+ onError(err);
23
34
  });
24
35
  },
25
36
  });
@@ -35,38 +46,25 @@ const createVideoEncoder = ({ onChunk, onError, signal, config, }) => {
35
46
  close();
36
47
  };
37
48
  signal.addEventListener('abort', onAbort);
38
- const getQueueSize = () => {
39
- return encoder.encodeQueueSize + outputQueueSize;
40
- };
41
49
  encoder.configure(config);
42
50
  let framesProcessed = 0;
43
- const waitForDequeue = async () => {
44
- await new Promise((r) => {
45
- dequeueResolver = r;
46
- encoder.addEventListener('dequeue', () => r(), {
47
- once: true,
48
- });
49
- });
50
- };
51
- const waitForFinish = async () => {
52
- while (getQueueSize() > 0) {
53
- await waitForDequeue();
54
- }
55
- };
56
51
  const encodeFrame = async (frame) => {
57
52
  if (encoder.state === 'closed') {
58
53
  return;
59
54
  }
60
- while (getQueueSize() > 10) {
61
- await waitForDequeue();
62
- }
55
+ await ioSynchronizer.waitFor({
56
+ unemitted: 2,
57
+ _unprocessed: 2,
58
+ });
63
59
  // @ts-expect-error - can have changed in the meanwhile
64
60
  if (encoder.state === 'closed') {
65
61
  return;
66
62
  }
63
+ const keyFrame = framesProcessed % 40 === 0;
67
64
  encoder.encode(frame, {
68
- keyFrame: framesProcessed % 40 === 0,
65
+ keyFrame,
69
66
  });
67
+ ioSynchronizer.inputItem(frame.timestamp, keyFrame);
70
68
  framesProcessed++;
71
69
  };
72
70
  let inputQueue = Promise.resolve();
@@ -78,10 +76,9 @@ const createVideoEncoder = ({ onChunk, onError, signal, config, }) => {
78
76
  waitForFinish: async () => {
79
77
  await encoder.flush();
80
78
  await outputQueue;
81
- await waitForFinish();
79
+ await ioSynchronizer.waitForFinish();
82
80
  },
83
81
  close,
84
- getQueueSize,
85
82
  flush: async () => {
86
83
  await encoder.flush();
87
84
  },
@@ -0,0 +1,4 @@
1
+ export declare const waitUntilReturn: () => {
2
+ waitForReturn: () => any;
3
+ isReturning: () => void;
4
+ };
@@ -0,0 +1,14 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.waitUntilReturn = void 0;
4
+ const with_resolvers_1 = require("./with-resolvers");
5
+ const waitUntilReturn = () => {
6
+ const { promise, resolve } = (0, with_resolvers_1.withResolvers)();
7
+ return {
8
+ waitForReturn: () => promise,
9
+ isReturning: () => {
10
+ resolve(undefined);
11
+ },
12
+ };
13
+ };
14
+ exports.waitUntilReturn = waitUntilReturn;
@@ -3,3 +3,8 @@ export declare const withResolvers: <T>() => {
3
3
  resolve: (value: T | PromiseLike<T>) => void;
4
4
  reject: (reason?: any) => void;
5
5
  };
6
+ export declare const withResolversAndWaitForReturn: <T>() => {
7
+ getPromiseToImmediatelyReturn: () => Promise<T>;
8
+ reject: (reason: unknown) => void;
9
+ resolve: (value: T | PromiseLike<T>) => void;
10
+ };
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.withResolvers = void 0;
3
+ exports.withResolversAndWaitForReturn = exports.withResolvers = void 0;
4
4
  const withResolvers = function () {
5
5
  let resolve;
6
6
  let reject;
@@ -11,3 +11,18 @@ const withResolvers = function () {
11
11
  return { promise, resolve: resolve, reject: reject };
12
12
  };
13
13
  exports.withResolvers = withResolvers;
14
+ const withResolversAndWaitForReturn = () => {
15
+ const { promise, reject, resolve } = (0, exports.withResolvers)();
16
+ const { promise: returnPromise, resolve: resolveReturn } = (0, exports.withResolvers)();
17
+ return {
18
+ getPromiseToImmediatelyReturn: () => {
19
+ resolveReturn(undefined);
20
+ return promise;
21
+ },
22
+ reject: (reason) => {
23
+ returnPromise.then(() => reject(reason));
24
+ },
25
+ resolve,
26
+ };
27
+ };
28
+ exports.withResolversAndWaitForReturn = withResolversAndWaitForReturn;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@remotion/webcodecs",
3
- "version": "4.0.226",
3
+ "version": "4.0.228",
4
4
  "main": "dist/index.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "module": "dist/esm/index.mjs",
@@ -17,11 +17,13 @@
17
17
  "author": "Jonny Burger <jonny@remotion.dev>",
18
18
  "license": "SEE LICENSE IN LICENSE.md",
19
19
  "dependencies": {
20
- "@remotion/media-parser": "4.0.226"
20
+ "@remotion/media-parser": "4.0.228"
21
21
  },
22
22
  "peerDependencies": {},
23
23
  "devDependencies": {
24
- "@types/dom-webcodecs": "0.1.11"
24
+ "@types/dom-webcodecs": "0.1.11",
25
+ "eslint": "9.14.0",
26
+ "@remotion/eslint-config-internal": "4.0.228"
25
27
  },
26
28
  "keywords": [],
27
29
  "publishConfig": {
@@ -30,8 +32,8 @@
30
32
  "description": "Media conversion in the browser",
31
33
  "scripts": {
32
34
  "formatting": "prettier src --check",
33
- "lint": "eslint src --ext ts,tsx",
34
- "make": "tsc -d && bun --env-file=../.env.bundle bundle.ts",
35
- "watch": "tsc -w"
35
+ "lint": "eslint src",
36
+ "watch": "tsc -w",
37
+ "make": "tsc -d && bun --env-file=../.env.bundle bundle.ts"
36
38
  }
37
39
  }