mediabunny 1.57.0 → 1.58.1

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.
@@ -17527,7 +17527,8 @@ var Mediabunny = (() => {
17527
17527
  }
17528
17528
  this._orchestrator.signalWorkerStoppedRunning(worker);
17529
17529
  if (worker.aborted) {
17530
- await reader?.cancel();
17530
+ await reader?.cancel().catch(() => {
17531
+ });
17531
17532
  }
17532
17533
  }
17533
17534
  /** @internal */
@@ -17610,6 +17611,10 @@ var Mediabunny = (() => {
17610
17611
  * @internal
17611
17612
  */
17612
17613
  this._sequentialBacking = null;
17614
+ /** @internal */
17615
+ this._abortControllers = /* @__PURE__ */ new Map();
17616
+ /** @internal */
17617
+ this._sequentialAbortController = null;
17613
17618
  this._url = url2;
17614
17619
  this._options = options;
17615
17620
  this._getRetryDelay = options.getRetryDelay ?? DEFAULT_RETRY_DELAY;
@@ -17645,6 +17650,7 @@ var Mediabunny = (() => {
17645
17650
  maxCacheSize: options.maxCacheSize ?? 64 * 2 ** 20,
17646
17651
  maxWorkerCount: options.parallelism ?? DEFAULT_PARALLELISM,
17647
17652
  runWorker: this._runWorker.bind(this),
17653
+ onIdleWorkerRemoved: (worker) => this._abortControllers.delete(worker),
17648
17654
  prefetchProfile: PREFETCH_PROFILES.network,
17649
17655
  handleUnhandledError: options.handleUnhandledError
17650
17656
  });
@@ -17688,7 +17694,12 @@ var Mediabunny = (() => {
17688
17694
  /** @internal */
17689
17695
  async _runWorker(worker) {
17690
17696
  while (true) {
17697
+ if (worker.aborted) {
17698
+ this._orchestrator.signalWorkerStoppedRunning(worker);
17699
+ return;
17700
+ }
17691
17701
  const abortController = new AbortController();
17702
+ this._abortControllers.set(worker, abortController);
17692
17703
  const response = await retriedFetch(
17693
17704
  this._options.fetchFn ?? fetch,
17694
17705
  this._url,
@@ -17700,7 +17711,7 @@ var Mediabunny = (() => {
17700
17711
  signal: abortController.signal
17701
17712
  }),
17702
17713
  this._getRetryDelay,
17703
- () => this._disposed
17714
+ () => abortController.signal.aborted
17704
17715
  );
17705
17716
  if (!response.ok) {
17706
17717
  throw new Error(`Error fetching ${String(this._url)}: ${response.status} ${response.statusText}`);
@@ -17709,7 +17720,7 @@ var Mediabunny = (() => {
17709
17720
  this.rootPath = response.url;
17710
17721
  }
17711
17722
  outer:
17712
- if (this._orchestrator.fileSize === null && (response.status === 206 || response.type === "basic" && !response.headers.has("Content-Encoding"))) {
17723
+ if (this._orchestrator.fileSize === null && !response.headers.has("Content-Encoding") && (response.status === 206 || response.type === "basic")) {
17713
17724
  const contentRange = response.headers.get("Content-Range");
17714
17725
  if (contentRange) {
17715
17726
  const match = /\/(\d+)/.exec(contentRange);
@@ -17732,7 +17743,8 @@ var Mediabunny = (() => {
17732
17743
  }
17733
17744
  if (response.status !== 206) {
17734
17745
  if (this._sequentialBacking) {
17735
- void response.body.cancel();
17746
+ void response.body.cancel().catch(() => {
17747
+ });
17736
17748
  return;
17737
17749
  }
17738
17750
  if (!this._usedForHls) {
@@ -17749,7 +17761,8 @@ var Mediabunny = (() => {
17749
17761
  }
17750
17762
  }
17751
17763
  }
17752
- this._transitionToSequentialMode(response.body);
17764
+ this._abortControllers.delete(worker);
17765
+ this._transitionToSequentialMode(response.body, abortController);
17753
17766
  return;
17754
17767
  }
17755
17768
  const reader = response.body.getReader();
@@ -17763,7 +17776,7 @@ var Mediabunny = (() => {
17763
17776
  try {
17764
17777
  readResult = await reader.read();
17765
17778
  } catch (error) {
17766
- if (this._disposed) {
17779
+ if (abortController.signal.aborted) {
17767
17780
  throw error;
17768
17781
  }
17769
17782
  const retryDelayInSeconds = this._getRetryDelay(1, error, this._url);
@@ -17797,7 +17810,8 @@ var Mediabunny = (() => {
17797
17810
  }
17798
17811
  }
17799
17812
  /** @internal */
17800
- _transitionToSequentialMode(body) {
17813
+ _transitionToSequentialMode(body, abortController) {
17814
+ this._sequentialAbortController = abortController;
17801
17815
  let currentReader = body.getReader();
17802
17816
  let streamPosition = 0;
17803
17817
  let skipRemaining = 0;
@@ -17817,6 +17831,10 @@ var Mediabunny = (() => {
17817
17831
  }
17818
17832
  Logging._error("Error while reading response stream. Attempting to resume.", error);
17819
17833
  await wait(1e3 * retryDelayInSeconds);
17834
+ this._sequentialAbortController = new AbortController();
17835
+ if (this._disposed) {
17836
+ this._sequentialAbortController.abort();
17837
+ }
17820
17838
  const newResponse = await retriedFetch(
17821
17839
  this._options.fetchFn ?? fetch,
17822
17840
  this._url,
@@ -17824,7 +17842,8 @@ var Mediabunny = (() => {
17824
17842
  headers: {
17825
17843
  // Who knows, maybe the server honors range requests this time
17826
17844
  Range: `bytes=${streamPosition}-`
17827
- }
17845
+ },
17846
+ signal: this._sequentialAbortController.signal
17828
17847
  }),
17829
17848
  this._getRetryDelay,
17830
17849
  () => this._disposed
@@ -17862,7 +17881,8 @@ var Mediabunny = (() => {
17862
17881
  return;
17863
17882
  }
17864
17883
  },
17865
- cancel: () => currentReader.cancel()
17884
+ cancel: () => currentReader.cancel().catch(() => {
17885
+ })
17866
17886
  });
17867
17887
  const backing = new ReadableStreamSource(wrappedStream, {
17868
17888
  maxCacheSize: this._orchestrator.options.maxCacheSize,
@@ -17887,6 +17907,10 @@ var Mediabunny = (() => {
17887
17907
  }
17888
17908
  this._orchestrator.workers.length = 0;
17889
17909
  this._orchestrator.queuedReads.length = 0;
17910
+ for (const [, otherAbortController] of this._abortControllers) {
17911
+ otherAbortController.abort();
17912
+ }
17913
+ this._abortControllers.clear();
17890
17914
  for (const slice of uniqueSlices) {
17891
17915
  const result = backing._read(slice.start, slice.start + slice.bytes.length);
17892
17916
  if (isThenable(result)) {
@@ -17907,6 +17931,11 @@ var Mediabunny = (() => {
17907
17931
  /** @internal */
17908
17932
  _dispose() {
17909
17933
  this._orchestrator.dispose();
17934
+ for (const [, abortController] of this._abortControllers) {
17935
+ abortController.abort();
17936
+ }
17937
+ this._abortControllers.clear();
17938
+ this._sequentialAbortController?.abort();
17910
17939
  if (this._sequentialBacking) {
17911
17940
  this._sequentialBacking._disposed = true;
17912
17941
  this._sequentialBacking._dispose();
@@ -18062,7 +18091,8 @@ var Mediabunny = (() => {
18062
18091
  if (isThenable(data)) data = await data;
18063
18092
  if (worker.aborted) {
18064
18093
  if (data instanceof ReadableStream) {
18065
- await data.cancel();
18094
+ await data.cancel().catch(() => {
18095
+ });
18066
18096
  }
18067
18097
  break;
18068
18098
  }
@@ -18307,7 +18337,8 @@ var Mediabunny = (() => {
18307
18337
  }
18308
18338
  this._pendingSlices.length = 0;
18309
18339
  this._cache.length = 0;
18310
- void this._reader?.cancel();
18340
+ void this._reader?.cancel().catch(() => {
18341
+ });
18311
18342
  }
18312
18343
  };
18313
18344
  var PREFETCH_PROFILES = {
@@ -22860,13 +22891,13 @@ var Mediabunny = (() => {
22860
22891
  switch (format) {
22861
22892
  case "u8":
22862
22893
  case "u8-planar":
22863
- return (view2, offset, value) => view2.setUint8(offset, clamp((value + 1) * 127.5, 0, 255));
22894
+ return (view2, offset, value) => view2.setUint8(offset, clamp(Math.round(value * 128) + 128, 0, 255));
22864
22895
  case "s16":
22865
22896
  case "s16-planar":
22866
- return (view2, offset, value) => view2.setInt16(offset, clamp(Math.round(value * 32767), -32768, 32767), true);
22897
+ return (view2, offset, value) => view2.setInt16(offset, clamp(Math.round(value * 32768), -32768, 32767), true);
22867
22898
  case "s32":
22868
22899
  case "s32-planar":
22869
- return (view2, offset, value) => view2.setInt32(offset, clamp(Math.round(value * 2147483647), -2147483648, 2147483647), true);
22900
+ return (view2, offset, value) => view2.setInt32(offset, clamp(Math.round(value * 2147483648), -2147483648, 2147483647), true);
22870
22901
  case "f32":
22871
22902
  case "f32-planar":
22872
22903
  return (view2, offset, value) => view2.setFloat32(offset, value, true);
@@ -23780,7 +23811,7 @@ var Mediabunny = (() => {
23780
23811
  var toUlaw = (s16) => {
23781
23812
  const MULAW_MAX = 8191;
23782
23813
  const MULAW_BIAS = 33;
23783
- let number = s16;
23814
+ let number = s16 >> 2;
23784
23815
  let mask = 4096;
23785
23816
  let sign = 0;
23786
23817
  let position = 12;
@@ -23811,7 +23842,7 @@ var Mediabunny = (() => {
23811
23842
  }
23812
23843
  position = ((number & 240) >> 4) + 5;
23813
23844
  const decoded = (1 << position | (number & 15) << position - 4 | 1 << position - 5) - MULAW_BIAS;
23814
- return sign === 0 ? decoded : -decoded;
23845
+ return (sign === 0 ? decoded : -decoded) << 2;
23815
23846
  };
23816
23847
  var toAlaw = (s16) => {
23817
23848
  const ALAW_MAX = 4095;
@@ -23819,9 +23850,10 @@ var Mediabunny = (() => {
23819
23850
  let sign = 0;
23820
23851
  let position = 11;
23821
23852
  let lsb = 0;
23822
- let number = s16;
23853
+ let number = s16 >> 3;
23823
23854
  if (number < 0) {
23824
- number = -number;
23855
+ number = -number - 1;
23856
+ } else {
23825
23857
  sign = 128;
23826
23858
  }
23827
23859
  if (number > ALAW_MAX) {
@@ -23849,7 +23881,7 @@ var Mediabunny = (() => {
23849
23881
  } else {
23850
23882
  decoded = number << 1 | 1;
23851
23883
  }
23852
- return sign === 0 ? decoded : -decoded;
23884
+ return (sign === 0 ? -decoded : decoded) << 3;
23853
23885
  };
23854
23886
 
23855
23887
  // src/media-sink.ts
@@ -26463,7 +26495,7 @@ var Mediabunny = (() => {
26463
26495
  if (customAudioDecoders.some((x) => x.supports(codec, decoderConfig))) {
26464
26496
  return true;
26465
26497
  }
26466
- if (decoderConfig.codec.startsWith("pcm-")) {
26498
+ if (PCM_AUDIO_CODECS.includes(decoderConfig.codec)) {
26467
26499
  return true;
26468
26500
  } else {
26469
26501
  if (typeof AudioDecoder === "undefined") {
@@ -36596,17 +36628,20 @@ ${cue.notes ?? ""}`;
36596
36628
  const outputView = new DataView(outputBuffer);
36597
36629
  outputs.push({ frameCount, view: outputView });
36598
36630
  }
36599
- const allocationSize = audioSample.allocationSize({ planeIndex: 0, format: "f32-planar" });
36600
- const floats = new Float32Array(allocationSize / Float32Array.BYTES_PER_ELEMENT);
36631
+ const isS32 = audioSample.format === "s32" || audioSample.format === "s32-planar";
36632
+ const readFormat = isS32 ? "s32-planar" : "f32-planar";
36633
+ const scale = isS32 ? 1 / 2147483648 : 1;
36634
+ const allocationSize = audioSample.allocationSize({ planeIndex: 0, format: readFormat });
36635
+ const values = isS32 ? new Int32Array(allocationSize / 4) : new Float32Array(allocationSize / 4);
36601
36636
  for (let i = 0; i < numberOfChannels; i++) {
36602
- audioSample.copyTo(floats, { planeIndex: i, format: "f32-planar" });
36637
+ audioSample.copyTo(values, { planeIndex: i, format: readFormat });
36603
36638
  for (let j = 0; j < outputs.length; j++) {
36604
36639
  const { frameCount, view: view2 } = outputs[j];
36605
36640
  for (let k = 0; k < frameCount; k++) {
36606
36641
  this.writeOutputValue(
36607
36642
  view2,
36608
36643
  (k * numberOfChannels + i) * this.outputSampleSize,
36609
- floats[j * CHUNK_SIZE + k]
36644
+ values[j * CHUNK_SIZE + k] * scale
36610
36645
  );
36611
36646
  }
36612
36647
  }
@@ -36742,19 +36777,19 @@ ${cue.notes ?? ""}`;
36742
36777
  case 1:
36743
36778
  {
36744
36779
  if (dataType === "unsigned") {
36745
- this.writeOutputValue = (view2, byteOffset, value) => view2.setUint8(byteOffset, clamp((value + 1) * 127.5, 0, 255));
36780
+ this.writeOutputValue = (view2, byteOffset, value) => view2.setUint8(byteOffset, clamp(Math.round(value * 128) + 128, 0, 255));
36746
36781
  } else if (dataType === "signed") {
36747
36782
  this.writeOutputValue = (view2, byteOffset, value) => {
36748
36783
  view2.setInt8(byteOffset, clamp(Math.round(value * 128), -128, 127));
36749
36784
  };
36750
36785
  } else if (dataType === "ulaw") {
36751
36786
  this.writeOutputValue = (view2, byteOffset, value) => {
36752
- const int16 = clamp(Math.floor(value * 32767), -32768, 32767);
36787
+ const int16 = clamp(Math.round(value * 32768), -32768, 32767);
36753
36788
  view2.setUint8(byteOffset, toUlaw(int16));
36754
36789
  };
36755
36790
  } else if (dataType === "alaw") {
36756
36791
  this.writeOutputValue = (view2, byteOffset, value) => {
36757
- const int16 = clamp(Math.floor(value * 32767), -32768, 32767);
36792
+ const int16 = clamp(Math.round(value * 32768), -32768, 32767);
36758
36793
  view2.setUint8(byteOffset, toAlaw(int16));
36759
36794
  };
36760
36795
  } else {
@@ -36766,9 +36801,9 @@ ${cue.notes ?? ""}`;
36766
36801
  case 2:
36767
36802
  {
36768
36803
  if (dataType === "unsigned") {
36769
- this.writeOutputValue = (view2, byteOffset, value) => view2.setUint16(byteOffset, clamp((value + 1) * 32767.5, 0, 65535), littleEndian);
36804
+ this.writeOutputValue = (view2, byteOffset, value) => view2.setUint16(byteOffset, clamp(Math.round(value * 32768) + 32768, 0, 65535), littleEndian);
36770
36805
  } else if (dataType === "signed") {
36771
- this.writeOutputValue = (view2, byteOffset, value) => view2.setInt16(byteOffset, clamp(Math.round(value * 32767), -32768, 32767), littleEndian);
36806
+ this.writeOutputValue = (view2, byteOffset, value) => view2.setInt16(byteOffset, clamp(Math.round(value * 32768), -32768, 32767), littleEndian);
36772
36807
  } else {
36773
36808
  assert(false);
36774
36809
  }
@@ -36778,12 +36813,17 @@ ${cue.notes ?? ""}`;
36778
36813
  case 3:
36779
36814
  {
36780
36815
  if (dataType === "unsigned") {
36781
- this.writeOutputValue = (view2, byteOffset, value) => setUint24(view2, byteOffset, clamp((value + 1) * 83886075e-1, 0, 16777215), littleEndian);
36816
+ this.writeOutputValue = (view2, byteOffset, value) => setUint24(
36817
+ view2,
36818
+ byteOffset,
36819
+ clamp(Math.round(value * 8388608) + 8388608, 0, 16777215),
36820
+ littleEndian
36821
+ );
36782
36822
  } else if (dataType === "signed") {
36783
36823
  this.writeOutputValue = (view2, byteOffset, value) => setInt24(
36784
36824
  view2,
36785
36825
  byteOffset,
36786
- clamp(Math.round(value * 8388607), -8388608, 8388607),
36826
+ clamp(Math.round(value * 8388608), -8388608, 8388607),
36787
36827
  littleEndian
36788
36828
  );
36789
36829
  } else {
@@ -36795,11 +36835,15 @@ ${cue.notes ?? ""}`;
36795
36835
  case 4:
36796
36836
  {
36797
36837
  if (dataType === "unsigned") {
36798
- this.writeOutputValue = (view2, byteOffset, value) => view2.setUint32(byteOffset, clamp((value + 1) * 21474836475e-1, 0, 4294967295), littleEndian);
36838
+ this.writeOutputValue = (view2, byteOffset, value) => view2.setUint32(
36839
+ byteOffset,
36840
+ clamp(Math.round(value * 2147483648) + 2147483648, 0, 4294967295),
36841
+ littleEndian
36842
+ );
36799
36843
  } else if (dataType === "signed") {
36800
36844
  this.writeOutputValue = (view2, byteOffset, value) => view2.setInt32(
36801
36845
  byteOffset,
36802
- clamp(Math.round(value * 2147483647), -2147483648, 2147483647),
36846
+ clamp(Math.round(value * 2147483648), -2147483648, 2147483647),
36803
36847
  littleEndian
36804
36848
  );
36805
36849
  } else if (dataType === "float") {
@@ -39984,6 +40028,9 @@ ${cue.notes ?? ""}`;
39984
40028
  "options.copy.boundaryPolicy, when provided, must be 'expand' or 'shrink'."
39985
40029
  );
39986
40030
  }
40031
+ if (options.copy.boundaryTolerance !== void 0 && (!isNumber(options.copy.boundaryTolerance) || options.copy.boundaryTolerance < 0)) {
40032
+ throw new TypeError("options.copy.boundaryTolerance, when provided, must be a non-negative number.");
40033
+ }
39987
40034
  }
39988
40035
  const composable = options.composable ?? false;
39989
40036
  if (!composable) {
@@ -40045,6 +40092,7 @@ ${cue.notes ?? ""}`;
40045
40092
  this._copyMode = options.copy === false ? false : options.copy?.mode ?? "preferred";
40046
40093
  this._copyTimestampShiftTolerance = options.copy === false ? 0 : options.copy?.shiftTolerance ?? 0;
40047
40094
  this._copyBoundaryPolicy = options.copy === false ? "expand" : options.copy?.boundaryPolicy ?? "expand";
40095
+ this._copyBoundaryTolerance = options.copy === false ? Infinity : options.copy?.boundaryTolerance ?? Infinity;
40048
40096
  this._composable = composable;
40049
40097
  this.input = options.input;
40050
40098
  this.output = options.output;
@@ -40514,7 +40562,10 @@ The @mediabunny/mp3-encoder extension package provides support for encoding MP3.
40514
40562
  copyStartPacket = startPacket;
40515
40563
  if (startPacket) {
40516
40564
  const effectiveStartTimestamp = this._copyBoundaryPolicy === "shrink" ? Math.max(startPacket.timestamp, this._startTimestamp) : startPacket.timestamp;
40517
- if (!this.output.format.supportsTimestampedMediaData) {
40565
+ const boundaryDeviation = this._copyBoundaryPolicy === "shrink" ? effectiveStartTimestamp - this._startTimestamp : Math.max(this._startTimestamp - effectiveStartTimestamp, 0);
40566
+ if (boundaryDeviation > this._copyBoundaryTolerance) {
40567
+ needsTranscode = true;
40568
+ } else if (!this.output.format.supportsTimestampedMediaData) {
40518
40569
  if (this._timestampOffsetAdjusted) {
40519
40570
  const isValid = effectiveStartTimestamp + this._timestampOffset === 0;
40520
40571
  if (!isValid) {
@@ -40565,6 +40616,7 @@ The @mediabunny/mp3-encoder extension package provides support for encoding MP3.
40565
40616
  const sink = new EncodedPacketSink(track);
40566
40617
  const decoderConfig = await track.getDecoderConfig();
40567
40618
  const meta = { decoderConfig: decoderConfig ?? void 0 };
40619
+ let maxTimestamp = null;
40568
40620
  if (copyStartPacket) for await (const packet of sink.packets(
40569
40621
  copyStartPacket,
40570
40622
  void 0,
@@ -40605,10 +40657,16 @@ The @mediabunny/mp3-encoder extension package provides support for encoding MP3.
40605
40657
  }
40606
40658
  packetStartTimestamp += this._timestampOffset;
40607
40659
  packetEndTimestamp += this._timestampOffset;
40660
+ let packetType = packet.type;
40661
+ if (packetType === "key" && maxTimestamp !== null && packetStartTimestamp < maxTimestamp) {
40662
+ packetType = "delta";
40663
+ }
40664
+ maxTimestamp = Math.max(maxTimestamp ?? -Infinity, packetStartTimestamp);
40608
40665
  const modifiedPacket = packet.clone({
40609
40666
  timestamp: packetStartTimestamp,
40610
40667
  duration: packetEndTimestamp - packetStartTimestamp,
40611
- sideData: alpha === "discard" ? {} : packet.sideData
40668
+ sideData: alpha === "discard" ? {} : packet.sideData,
40669
+ type: packetType
40612
40670
  });
40613
40671
  this._reportProgress(outputTrackId, modifiedPacket.timestamp + modifiedPacket.duration);
40614
40672
  await source.add(modifiedPacket, meta);
@@ -40815,7 +40873,10 @@ The @mediabunny/mp3-encoder extension package provides support for encoding MP3.
40815
40873
  }
40816
40874
  copyStartPacket = startPacket;
40817
40875
  if (startPacket) {
40818
- if (!this.output.format.supportsTimestampedMediaData) {
40876
+ const boundaryDeviation = this._copyBoundaryPolicy === "shrink" ? Math.max(startPacket.timestamp - this._startTimestamp, 0) : Math.max(this._startTimestamp - startPacket.timestamp, 0);
40877
+ if (boundaryDeviation > this._copyBoundaryTolerance) {
40878
+ needsTranscode = true;
40879
+ } else if (!this.output.format.supportsTimestampedMediaData) {
40819
40880
  if (this._timestampOffsetAdjusted) {
40820
40881
  const isValid = startPacket.timestamp + this._timestampOffset === 0;
40821
40882
  if (!isValid) {