@remotion/webcodecs 4.0.311 → 4.0.312

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.
@@ -6,6 +6,10 @@ export type WebCodecsAudioDecoder = {
6
6
  flush: () => Promise<void>;
7
7
  waitForQueueToBeLessThan: (items: number) => Promise<void>;
8
8
  reset: () => void;
9
+ checkReset: () => {
10
+ wasReset: () => boolean;
11
+ };
12
+ getMostRecentSampleInput: () => number | null;
9
13
  };
10
14
  export type CreateAudioDecoderInit = {
11
15
  onFrame: (frame: AudioData) => Promise<void> | void;
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.createAudioDecoder = exports.internalCreateAudioDecoder = void 0;
4
+ const flush_pending_1 = require("./flush-pending");
4
5
  const get_wave_audio_decoder_1 = require("./get-wave-audio-decoder");
5
6
  const io_synchronizer_1 = require("./io-manager/io-synchronizer");
6
7
  const internalCreateAudioDecoder = ({ onFrame, onError, controller, config, logLevel, }) => {
@@ -13,6 +14,7 @@ const internalCreateAudioDecoder = ({ onFrame, onError, controller, config, logL
13
14
  label: 'Audio decoder',
14
15
  controller,
15
16
  });
17
+ let mostRecentSampleReceived = null;
16
18
  if (config.codec === 'pcm-s16') {
17
19
  return (0, get_wave_audio_decoder_1.getWaveAudioDecoder)({
18
20
  onFrame,
@@ -67,6 +69,7 @@ const internalCreateAudioDecoder = ({ onFrame, onError, controller, config, logL
67
69
  onError(err);
68
70
  return;
69
71
  }
72
+ mostRecentSampleReceived = audioSample.timestamp;
70
73
  // Don't flush, it messes up the audio
71
74
  const chunk = audioSample instanceof EncodedAudioChunk
72
75
  ? audioSample
@@ -81,22 +84,44 @@ const internalCreateAudioDecoder = ({ onFrame, onError, controller, config, logL
81
84
  ioSynchronizer.inputItem(chunk.timestamp);
82
85
  }
83
86
  };
87
+ let flushPending = null;
88
+ const lastReset = null;
84
89
  return {
85
90
  decode,
86
91
  close,
87
- flush: async () => {
88
- // Firefox might throw "Needs to be configured first"
89
- try {
90
- await audioDecoder.flush();
92
+ flush: () => {
93
+ if (flushPending) {
94
+ throw new Error('Flush already pending');
91
95
  }
92
- catch { }
93
- await ioSynchronizer.waitForQueueSize(0);
96
+ const pendingFlush = (0, flush_pending_1.makeFlushPending)();
97
+ flushPending = pendingFlush;
98
+ Promise.resolve()
99
+ .then(() => {
100
+ return audioDecoder.flush();
101
+ })
102
+ .catch(() => {
103
+ // Firefox might throw "Needs to be configured first"
104
+ })
105
+ .finally(() => {
106
+ pendingFlush.resolve();
107
+ flushPending = null;
108
+ });
109
+ return pendingFlush.promise;
94
110
  },
95
111
  waitForQueueToBeLessThan: ioSynchronizer.waitForQueueSize,
96
112
  reset: () => {
97
113
  audioDecoder.reset();
98
114
  audioDecoder.configure(config);
99
115
  },
116
+ checkReset: () => {
117
+ const initTime = Date.now();
118
+ return {
119
+ wasReset: () => lastReset !== null && lastReset > initTime,
120
+ };
121
+ },
122
+ getMostRecentSampleInput() {
123
+ return mostRecentSampleReceived;
124
+ },
100
125
  };
101
126
  };
102
127
  exports.internalCreateAudioDecoder = internalCreateAudioDecoder;
@@ -6,6 +6,10 @@ export type WebCodecsVideoDecoder = {
6
6
  flush: () => Promise<void>;
7
7
  waitForQueueToBeLessThan: (items: number) => Promise<void>;
8
8
  reset: () => void;
9
+ checkReset: () => {
10
+ wasReset: () => boolean;
11
+ };
12
+ getMostRecentSampleInput: () => number | null;
9
13
  };
10
14
  export declare const internalCreateVideoDecoder: ({ onFrame, onError, controller, config, logLevel, }: {
11
15
  onFrame: (frame: VideoFrame) => Promise<void> | void;
@@ -1,13 +1,19 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.createVideoDecoder = exports.internalCreateVideoDecoder = void 0;
4
+ const flush_pending_1 = require("./flush-pending");
4
5
  const io_synchronizer_1 = require("./io-manager/io-synchronizer");
5
6
  const internalCreateVideoDecoder = ({ onFrame, onError, controller, config, logLevel, }) => {
7
+ if (controller &&
8
+ controller._internals._mediaParserController._internals.signal.aborted) {
9
+ throw new Error('Not creating audio decoder, already aborted');
10
+ }
6
11
  const ioSynchronizer = (0, io_synchronizer_1.makeIoSynchronizer)({
7
12
  logLevel,
8
13
  label: 'Video decoder',
9
14
  controller,
10
15
  });
16
+ let mostRecentSampleReceived = null;
11
17
  const videoDecoder = new VideoDecoder({
12
18
  async output(frame) {
13
19
  try {
@@ -52,28 +58,54 @@ const internalCreateVideoDecoder = ({ onFrame, onError, controller, config, logL
52
58
  onError(err);
53
59
  return;
54
60
  }
61
+ mostRecentSampleReceived = sample.timestamp;
55
62
  const encodedChunk = sample instanceof EncodedVideoChunk
56
63
  ? sample
57
64
  : new EncodedVideoChunk(sample);
58
65
  videoDecoder.decode(encodedChunk);
59
66
  ioSynchronizer.inputItem(sample.timestamp);
60
67
  };
68
+ let flushPending = null;
69
+ let lastReset = null;
61
70
  return {
62
71
  decode,
63
72
  close,
64
- flush: async () => {
65
- // Firefox might throw "Needs to be configured first"
66
- try {
67
- await videoDecoder.flush();
73
+ flush: () => {
74
+ if (flushPending) {
75
+ throw new Error('Flush already pending');
68
76
  }
69
- catch { }
70
- await ioSynchronizer.waitForQueueSize(0);
77
+ const pendingFlush = (0, flush_pending_1.makeFlushPending)();
78
+ flushPending = pendingFlush;
79
+ Promise.resolve()
80
+ .then(() => {
81
+ return videoDecoder.flush();
82
+ })
83
+ .catch(() => {
84
+ // Firefox might throw "Needs to be configured first"
85
+ })
86
+ .finally(() => {
87
+ pendingFlush.resolve();
88
+ flushPending = null;
89
+ });
90
+ return pendingFlush.promise;
71
91
  },
72
92
  waitForQueueToBeLessThan: ioSynchronizer.waitForQueueSize,
73
93
  reset: () => {
94
+ lastReset = Date.now();
95
+ flushPending?.resolve();
96
+ ioSynchronizer.clearQueue();
74
97
  videoDecoder.reset();
75
98
  videoDecoder.configure(config);
76
99
  },
100
+ checkReset: () => {
101
+ const initTime = Date.now();
102
+ return {
103
+ wasReset: () => lastReset !== null && lastReset > initTime,
104
+ };
105
+ },
106
+ getMostRecentSampleInput() {
107
+ return mostRecentSampleReceived;
108
+ },
77
109
  };
78
110
  };
79
111
  exports.internalCreateVideoDecoder = internalCreateVideoDecoder;
@@ -538,6 +538,7 @@ var makeIoSynchronizer = ({
538
538
  let lastOutput = 0;
539
539
  let inputsSinceLastOutput = 0;
540
540
  let inputs = [];
541
+ let resolvers = [];
541
542
  const getQueuedItems = () => {
542
543
  inputs = inputs.filter((input) => Math.floor(input) > Math.floor(lastOutput) + 1);
543
544
  return inputs.length;
@@ -567,8 +568,10 @@ var makeIoSynchronizer = ({
567
568
  const on = () => {
568
569
  eventEmitter.removeEventListener("output", on);
569
570
  resolve();
571
+ resolvers = resolvers.filter((resolver) => resolver !== resolve);
570
572
  };
571
573
  eventEmitter.addEventListener("output", on);
574
+ resolvers.push(resolve);
572
575
  return promise;
573
576
  };
574
577
  const makeErrorBanner = () => {
@@ -608,10 +611,22 @@ var makeIoSynchronizer = ({
608
611
  controller._internals._mediaParserController._internals.signal.removeEventListener("abort", clear);
609
612
  }
610
613
  };
614
+ const clearQueue = () => {
615
+ inputs.length = 0;
616
+ lastInput = 0;
617
+ lastOutput = 0;
618
+ inputsSinceLastOutput = 0;
619
+ resolvers.forEach((resolver) => {
620
+ return resolver();
621
+ });
622
+ resolvers.length = 0;
623
+ inputs.length = 0;
624
+ };
611
625
  return {
612
626
  inputItem,
613
627
  onOutput,
614
- waitForQueueSize
628
+ waitForQueueSize,
629
+ clearQueue
615
630
  };
616
631
  };
617
632
 
@@ -4135,6 +4150,16 @@ var convertEncodedChunk = (chunk) => {
4135
4150
  };
4136
4151
  };
4137
4152
 
4153
+ // src/flush-pending.ts
4154
+ var makeFlushPending = () => {
4155
+ const { promise, resolve, reject } = withResolvers();
4156
+ return {
4157
+ promise,
4158
+ resolve,
4159
+ reject
4160
+ };
4161
+ };
4162
+
4138
4163
  // src/get-wave-audio-decoder.ts
4139
4164
  var getBytesPerSample = (sampleFormat) => {
4140
4165
  if (sampleFormat === "s16") {
@@ -4196,16 +4221,25 @@ var getWaveAudioDecoder = ({
4196
4221
  onError(err);
4197
4222
  }
4198
4223
  };
4224
+ let lastReset = null;
4225
+ let mostRecentSampleInput = null;
4199
4226
  return {
4200
4227
  close() {
4201
4228
  return Promise.resolve();
4202
4229
  },
4203
4230
  decode(audioSample) {
4231
+ mostRecentSampleInput = audioSample.timestamp;
4204
4232
  return processSample(audioSample);
4205
4233
  },
4206
4234
  flush: () => Promise.resolve(),
4207
4235
  waitForQueueToBeLessThan: ioSynchronizer.waitForQueueSize,
4208
- reset: () => {}
4236
+ reset: () => {
4237
+ lastReset = Date.now();
4238
+ },
4239
+ checkReset: () => ({
4240
+ wasReset: () => lastReset !== null && lastReset > Date.now()
4241
+ }),
4242
+ getMostRecentSampleInput: () => mostRecentSampleInput
4209
4243
  };
4210
4244
  };
4211
4245
 
@@ -4225,6 +4259,7 @@ var internalCreateAudioDecoder = ({
4225
4259
  label: "Audio decoder",
4226
4260
  controller
4227
4261
  });
4262
+ let mostRecentSampleReceived = null;
4228
4263
  if (config.codec === "pcm-s16") {
4229
4264
  return getWaveAudioDecoder({
4230
4265
  onFrame,
@@ -4275,25 +4310,45 @@ var internalCreateAudioDecoder = ({
4275
4310
  onError(err);
4276
4311
  return;
4277
4312
  }
4313
+ mostRecentSampleReceived = audioSample.timestamp;
4278
4314
  const chunk = audioSample instanceof EncodedAudioChunk ? audioSample : new EncodedAudioChunk(audioSample);
4279
4315
  audioDecoder.decode(chunk);
4280
4316
  if (chunk.byteLength > 16) {
4281
4317
  ioSynchronizer.inputItem(chunk.timestamp);
4282
4318
  }
4283
4319
  };
4320
+ let flushPending = null;
4321
+ const lastReset = null;
4284
4322
  return {
4285
4323
  decode,
4286
4324
  close,
4287
- flush: async () => {
4288
- try {
4289
- await audioDecoder.flush();
4290
- } catch {}
4291
- await ioSynchronizer.waitForQueueSize(0);
4325
+ flush: () => {
4326
+ if (flushPending) {
4327
+ throw new Error("Flush already pending");
4328
+ }
4329
+ const pendingFlush = makeFlushPending();
4330
+ flushPending = pendingFlush;
4331
+ Promise.resolve().then(() => {
4332
+ return audioDecoder.flush();
4333
+ }).catch(() => {}).finally(() => {
4334
+ pendingFlush.resolve();
4335
+ flushPending = null;
4336
+ });
4337
+ return pendingFlush.promise;
4292
4338
  },
4293
4339
  waitForQueueToBeLessThan: ioSynchronizer.waitForQueueSize,
4294
4340
  reset: () => {
4295
4341
  audioDecoder.reset();
4296
4342
  audioDecoder.configure(config);
4343
+ },
4344
+ checkReset: () => {
4345
+ const initTime = Date.now();
4346
+ return {
4347
+ wasReset: () => lastReset !== null && lastReset > initTime
4348
+ };
4349
+ },
4350
+ getMostRecentSampleInput() {
4351
+ return mostRecentSampleReceived;
4297
4352
  }
4298
4353
  };
4299
4354
  };
@@ -4672,11 +4727,15 @@ var internalCreateVideoDecoder = ({
4672
4727
  config,
4673
4728
  logLevel
4674
4729
  }) => {
4730
+ if (controller && controller._internals._mediaParserController._internals.signal.aborted) {
4731
+ throw new Error("Not creating audio decoder, already aborted");
4732
+ }
4675
4733
  const ioSynchronizer = makeIoSynchronizer({
4676
4734
  logLevel,
4677
4735
  label: "Video decoder",
4678
4736
  controller
4679
4737
  });
4738
+ let mostRecentSampleReceived = null;
4680
4739
  const videoDecoder = new VideoDecoder({
4681
4740
  async output(frame) {
4682
4741
  try {
@@ -4717,23 +4776,46 @@ var internalCreateVideoDecoder = ({
4717
4776
  onError(err);
4718
4777
  return;
4719
4778
  }
4779
+ mostRecentSampleReceived = sample.timestamp;
4720
4780
  const encodedChunk = sample instanceof EncodedVideoChunk ? sample : new EncodedVideoChunk(sample);
4721
4781
  videoDecoder.decode(encodedChunk);
4722
4782
  ioSynchronizer.inputItem(sample.timestamp);
4723
4783
  };
4784
+ let flushPending = null;
4785
+ let lastReset = null;
4724
4786
  return {
4725
4787
  decode,
4726
4788
  close,
4727
- flush: async () => {
4728
- try {
4729
- await videoDecoder.flush();
4730
- } catch {}
4731
- await ioSynchronizer.waitForQueueSize(0);
4789
+ flush: () => {
4790
+ if (flushPending) {
4791
+ throw new Error("Flush already pending");
4792
+ }
4793
+ const pendingFlush = makeFlushPending();
4794
+ flushPending = pendingFlush;
4795
+ Promise.resolve().then(() => {
4796
+ return videoDecoder.flush();
4797
+ }).catch(() => {}).finally(() => {
4798
+ pendingFlush.resolve();
4799
+ flushPending = null;
4800
+ });
4801
+ return pendingFlush.promise;
4732
4802
  },
4733
4803
  waitForQueueToBeLessThan: ioSynchronizer.waitForQueueSize,
4734
4804
  reset: () => {
4805
+ lastReset = Date.now();
4806
+ flushPending?.resolve();
4807
+ ioSynchronizer.clearQueue();
4735
4808
  videoDecoder.reset();
4736
4809
  videoDecoder.configure(config);
4810
+ },
4811
+ checkReset: () => {
4812
+ const initTime = Date.now();
4813
+ return {
4814
+ wasReset: () => lastReset !== null && lastReset > initTime
4815
+ };
4816
+ },
4817
+ getMostRecentSampleInput() {
4818
+ return mostRecentSampleReceived;
4737
4819
  }
4738
4820
  };
4739
4821
  };
@@ -0,0 +1,10 @@
1
+ export type FlushPending = {
2
+ resolve: (value: void | PromiseLike<void>) => void;
3
+ reject: (reason?: any) => void;
4
+ promise: Promise<void>;
5
+ };
6
+ export declare const makeFlushPending: () => {
7
+ promise: Promise<void>;
8
+ resolve: (value: void | PromiseLike<void>) => void;
9
+ reject: (reason?: any) => void;
10
+ };
@@ -0,0 +1,13 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.makeFlushPending = void 0;
4
+ const with_resolvers_1 = require("./create/with-resolvers");
5
+ const makeFlushPending = () => {
6
+ const { promise, resolve, reject } = (0, with_resolvers_1.withResolvers)();
7
+ return {
8
+ promise,
9
+ resolve,
10
+ reject,
11
+ };
12
+ };
13
+ exports.makeFlushPending = makeFlushPending;
@@ -56,16 +56,25 @@ const getWaveAudioDecoder = ({ onFrame, config, sampleFormat, ioSynchronizer, on
56
56
  onError(err);
57
57
  }
58
58
  };
59
+ let lastReset = null;
60
+ let mostRecentSampleInput = null;
59
61
  return {
60
62
  close() {
61
63
  return Promise.resolve();
62
64
  },
63
65
  decode(audioSample) {
66
+ mostRecentSampleInput = audioSample.timestamp;
64
67
  return processSample(audioSample);
65
68
  },
66
69
  flush: () => Promise.resolve(),
67
70
  waitForQueueToBeLessThan: ioSynchronizer.waitForQueueSize,
68
- reset: () => { },
71
+ reset: () => {
72
+ lastReset = Date.now();
73
+ },
74
+ checkReset: () => ({
75
+ wasReset: () => lastReset !== null && lastReset > Date.now(),
76
+ }),
77
+ getMostRecentSampleInput: () => mostRecentSampleInput,
69
78
  };
70
79
  };
71
80
  exports.getWaveAudioDecoder = getWaveAudioDecoder;
@@ -8,5 +8,6 @@ export declare const makeIoSynchronizer: ({ logLevel, label, controller, }: {
8
8
  inputItem: (timestamp: number) => void;
9
9
  onOutput: (timestamp: number) => void;
10
10
  waitForQueueSize: (queueSize: number) => Promise<void>;
11
+ clearQueue: () => void;
11
12
  };
12
13
  export type IoSynchronizer = ReturnType<typeof makeIoSynchronizer>;
@@ -11,6 +11,7 @@ const makeIoSynchronizer = ({ logLevel, label, controller, }) => {
11
11
  let lastOutput = 0;
12
12
  let inputsSinceLastOutput = 0;
13
13
  let inputs = [];
14
+ let resolvers = [];
14
15
  const getQueuedItems = () => {
15
16
  inputs = inputs.filter(
16
17
  // In chrome, the last output sometimes shifts the timestamp by 1 macrosecond - allowing this to happen
@@ -42,8 +43,10 @@ const makeIoSynchronizer = ({ logLevel, label, controller, }) => {
42
43
  const on = () => {
43
44
  eventEmitter.removeEventListener('output', on);
44
45
  resolve();
46
+ resolvers = resolvers.filter((resolver) => resolver !== resolve);
45
47
  };
46
48
  eventEmitter.addEventListener('output', on);
49
+ resolvers.push(resolve);
47
50
  return promise;
48
51
  };
49
52
  const makeErrorBanner = () => {
@@ -82,10 +85,22 @@ const makeIoSynchronizer = ({ logLevel, label, controller, }) => {
82
85
  controller._internals._mediaParserController._internals.signal.removeEventListener('abort', clear);
83
86
  }
84
87
  };
88
+ const clearQueue = () => {
89
+ inputs.length = 0;
90
+ lastInput = 0;
91
+ lastOutput = 0;
92
+ inputsSinceLastOutput = 0;
93
+ resolvers.forEach((resolver) => {
94
+ return resolver();
95
+ });
96
+ resolvers.length = 0;
97
+ inputs.length = 0;
98
+ };
85
99
  return {
86
100
  inputItem,
87
101
  onOutput,
88
102
  waitForQueueSize,
103
+ clearQueue,
89
104
  };
90
105
  };
91
106
  exports.makeIoSynchronizer = makeIoSynchronizer;
@@ -13,6 +13,7 @@ export declare function processingQueue<T extends Processable>({ onOutput, logLe
13
13
  inputItem: (timestamp: number) => void;
14
14
  onOutput: (timestamp: number) => void;
15
15
  waitForQueueSize: (queueSize: number) => Promise<void>;
16
+ clearQueue: () => void;
16
17
  };
17
18
  };
18
19
  export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@remotion/webcodecs",
3
- "version": "4.0.311",
3
+ "version": "4.0.312",
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.311",
23
- "@remotion/licensing": "4.0.311"
22
+ "@remotion/licensing": "4.0.312",
23
+ "@remotion/media-parser": "4.0.312"
24
24
  },
25
25
  "peerDependencies": {},
26
26
  "devDependencies": {
@@ -29,8 +29,8 @@
29
29
  "vite": "5.4.19",
30
30
  "@playwright/test": "1.51.1",
31
31
  "eslint": "9.19.0",
32
- "@remotion/example-videos": "4.0.311",
33
- "@remotion/eslint-config-internal": "4.0.311"
32
+ "@remotion/example-videos": "4.0.312",
33
+ "@remotion/eslint-config-internal": "4.0.312"
34
34
  },
35
35
  "keywords": [],
36
36
  "publishConfig": {