mediabunny 1.48.1 → 1.50.0

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 (100) hide show
  1. package/dist/bundles/mediabunny.cjs +503 -657
  2. package/dist/bundles/mediabunny.min.cjs +18 -104
  3. package/dist/bundles/mediabunny.min.mjs +18 -104
  4. package/dist/bundles/mediabunny.mjs +503 -657
  5. package/dist/bundles/mediabunny.node.cjs +503 -657
  6. package/dist/mediabunny.d.ts +68 -6
  7. package/dist/modules/src/codec-data.d.ts.map +1 -1
  8. package/dist/modules/src/codec-data.js +12 -6
  9. package/dist/modules/src/codec.d.ts +6 -3
  10. package/dist/modules/src/codec.d.ts.map +1 -1
  11. package/dist/modules/src/codec.js +57 -6
  12. package/dist/modules/src/conversion.d.ts.map +1 -1
  13. package/dist/modules/src/conversion.js +27 -17
  14. package/dist/modules/src/custom-coder.d.ts +8 -0
  15. package/dist/modules/src/custom-coder.d.ts.map +1 -1
  16. package/dist/modules/src/custom-coder.js +5 -4
  17. package/dist/modules/src/decode.js +1 -1
  18. package/dist/modules/src/encode.d.ts +3 -2
  19. package/dist/modules/src/encode.d.ts.map +1 -1
  20. package/dist/modules/src/encode.js +6 -5
  21. package/dist/modules/src/flac/flac-demuxer.d.ts.map +1 -1
  22. package/dist/modules/src/flac/flac-demuxer.js +23 -1
  23. package/dist/modules/src/hls/hls-muxer.d.ts.map +1 -1
  24. package/dist/modules/src/hls/hls-muxer.js +4 -3
  25. package/dist/modules/src/id3.d.ts +0 -1
  26. package/dist/modules/src/id3.d.ts.map +1 -1
  27. package/dist/modules/src/id3.js +12 -12
  28. package/dist/modules/src/index.d.ts +1 -0
  29. package/dist/modules/src/index.d.ts.map +1 -1
  30. package/dist/modules/src/index.js +3 -1
  31. package/dist/modules/src/input-format.d.ts.map +1 -1
  32. package/dist/modules/src/input-format.js +15 -1
  33. package/dist/modules/src/input-track.d.ts.map +1 -1
  34. package/dist/modules/src/input-track.js +3 -2
  35. package/dist/modules/src/isobmff/isobmff-boxes.js +3 -1
  36. package/dist/modules/src/isobmff/isobmff-demuxer.d.ts +2 -1
  37. package/dist/modules/src/isobmff/isobmff-demuxer.d.ts.map +1 -1
  38. package/dist/modules/src/isobmff/isobmff-demuxer.js +26 -19
  39. package/dist/modules/src/logging.d.ts +57 -0
  40. package/dist/modules/src/logging.d.ts.map +1 -0
  41. package/dist/modules/src/logging.js +84 -0
  42. package/dist/modules/src/matroska/ebml.d.ts +4 -4
  43. package/dist/modules/src/matroska/ebml.d.ts.map +1 -1
  44. package/dist/modules/src/matroska/ebml.js +1 -0
  45. package/dist/modules/src/matroska/matroska-demuxer.d.ts +2 -1
  46. package/dist/modules/src/matroska/matroska-demuxer.d.ts.map +1 -1
  47. package/dist/modules/src/matroska/matroska-demuxer.js +21 -4
  48. package/dist/modules/src/matroska/matroska-muxer.d.ts.map +1 -1
  49. package/dist/modules/src/matroska/matroska-muxer.js +14 -23
  50. package/dist/modules/src/media-sink.d.ts +4 -17
  51. package/dist/modules/src/media-sink.d.ts.map +1 -1
  52. package/dist/modules/src/media-sink.js +149 -245
  53. package/dist/modules/src/media-source.d.ts +2 -21
  54. package/dist/modules/src/media-source.d.ts.map +1 -1
  55. package/dist/modules/src/media-source.js +69 -289
  56. package/dist/modules/src/metadata.d.ts +4 -2
  57. package/dist/modules/src/metadata.d.ts.map +1 -1
  58. package/dist/modules/src/misc.d.ts +1 -1
  59. package/dist/modules/src/misc.d.ts.map +1 -1
  60. package/dist/modules/src/misc.js +5 -2
  61. package/dist/modules/src/mpeg-ts/mpeg-ts-demuxer.d.ts.map +1 -1
  62. package/dist/modules/src/mpeg-ts/mpeg-ts-demuxer.js +3 -1
  63. package/dist/modules/src/output.d.ts.map +1 -1
  64. package/dist/modules/src/output.js +5 -4
  65. package/dist/modules/src/sample.d.ts +1 -0
  66. package/dist/modules/src/sample.d.ts.map +1 -1
  67. package/dist/modules/src/sample.js +20 -10
  68. package/dist/modules/src/source.d.ts.map +1 -1
  69. package/dist/modules/src/source.js +4 -4
  70. package/dist/modules/src/tsconfig.tsbuildinfo +1 -1
  71. package/dist/modules/src/wave/wave-muxer.d.ts.map +1 -1
  72. package/dist/modules/src/wave/wave-muxer.js +2 -1
  73. package/package.json +2 -2
  74. package/src/codec-data.ts +11 -6
  75. package/src/codec.ts +76 -5
  76. package/src/conversion.ts +31 -18
  77. package/src/custom-coder.ts +13 -4
  78. package/src/decode.ts +1 -1
  79. package/src/encode.ts +10 -8
  80. package/src/flac/flac-demuxer.ts +28 -2
  81. package/src/hls/hls-muxer.ts +4 -3
  82. package/src/id3.ts +14 -14
  83. package/src/index.ts +8 -1
  84. package/src/input-format.ts +17 -1
  85. package/src/input-track.ts +3 -2
  86. package/src/isobmff/isobmff-boxes.ts +7 -2
  87. package/src/isobmff/isobmff-demuxer.ts +29 -18
  88. package/src/logging.ts +120 -0
  89. package/src/matroska/ebml.ts +1 -0
  90. package/src/matroska/matroska-demuxer.ts +26 -2
  91. package/src/matroska/matroska-muxer.ts +18 -27
  92. package/src/media-sink.ts +171 -306
  93. package/src/media-source.ts +82 -353
  94. package/src/metadata.ts +4 -2
  95. package/src/misc.ts +5 -2
  96. package/src/mpeg-ts/mpeg-ts-demuxer.ts +3 -1
  97. package/src/output.ts +5 -4
  98. package/src/sample.ts +24 -12
  99. package/src/source.ts +4 -4
  100. package/src/wave/wave-muxer.ts +2 -1
@@ -145,6 +145,8 @@ var Mediabunny = (() => {
145
145
  InputVideoTrack: () => InputVideoTrack,
146
146
  IsobmffInputFormat: () => IsobmffInputFormat,
147
147
  IsobmffOutputFormat: () => IsobmffOutputFormat2,
148
+ LogLevel: () => LogLevel,
149
+ Logging: () => Logging,
148
150
  MATROSKA: () => MATROSKA,
149
151
  MP3: () => MP3,
150
152
  MP4: () => MP4,
@@ -640,7 +642,7 @@ var Mediabunny = (() => {
640
642
  if (retryDelayInSeconds === null) {
641
643
  throw error;
642
644
  }
643
- console.error("Retrying failed fetch. Error:", error);
645
+ Logging._error("Retrying failed fetch. Error:", error);
644
646
  if (!Number.isFinite(retryDelayInSeconds) || retryDelayInSeconds < 0) {
645
647
  throw new TypeError("Retry delay must be a non-negative finite number.");
646
648
  }
@@ -1038,7 +1040,7 @@ var Mediabunny = (() => {
1038
1040
  /** @internal */
1039
1041
  this._listeners = /* @__PURE__ */ new Map();
1040
1042
  }
1041
- /** Registers a listener for the given event. */
1043
+ /** Registers a listener for the given event. Returns a function that, when called, removes the listener again. */
1042
1044
  on(event, listener, options) {
1043
1045
  if (!this._listeners.has(event)) {
1044
1046
  this._listeners.set(event, /* @__PURE__ */ new Set());
@@ -1112,6 +1114,63 @@ var Mediabunny = (() => {
1112
1114
  return value !== null && typeof value === "object" && Object.getPrototypeOf(value) === Object.prototype && Object.values(value).every((x) => typeof x === "string");
1113
1115
  };
1114
1116
 
1117
+ // src/logging.ts
1118
+ var LogLevel = /* @__PURE__ */ ((LogLevel2) => {
1119
+ LogLevel2[LogLevel2["Silent"] = 0] = "Silent";
1120
+ LogLevel2[LogLevel2["Errors"] = 1] = "Errors";
1121
+ LogLevel2[LogLevel2["Warnings"] = 2] = "Warnings";
1122
+ LogLevel2[LogLevel2["Info"] = 3] = "Info";
1123
+ return LogLevel2;
1124
+ })(LogLevel || {});
1125
+ var _Logging = class _Logging {
1126
+ constructor() {
1127
+ }
1128
+ /** The current log level. Defaults to {@link LogLevel.Info}. */
1129
+ static get level() {
1130
+ return _Logging._level;
1131
+ }
1132
+ static set level(value) {
1133
+ if (value !== 0 /* Silent */ && value !== 1 /* Errors */ && value !== 2 /* Warnings */ && value !== 3 /* Info */) {
1134
+ throw new TypeError("Invalid log level. Use one of the values of the LogLevel enum.");
1135
+ }
1136
+ _Logging._level = value;
1137
+ }
1138
+ /** @internal */
1139
+ static get _emitter() {
1140
+ return _Logging._emitterInstance ??= new EventEmitter();
1141
+ }
1142
+ /** Registers a listener for a log event. Returns a function that, when called, removes the listener again. */
1143
+ static on(event, listener, options) {
1144
+ return _Logging._emitter.on(event, listener, options);
1145
+ }
1146
+ /** @internal */
1147
+ static _error(...args) {
1148
+ _Logging._emitter._emit("error", args);
1149
+ if (_Logging._level >= 1 /* Errors */) {
1150
+ console.error(...args);
1151
+ }
1152
+ }
1153
+ /** @internal */
1154
+ static _warn(...args) {
1155
+ _Logging._emitter._emit("warn", args);
1156
+ if (_Logging._level >= 2 /* Warnings */) {
1157
+ console.warn(...args);
1158
+ }
1159
+ }
1160
+ /** @internal */
1161
+ static _info(...args) {
1162
+ _Logging._emitter._emit("info", args);
1163
+ if (_Logging._level >= 3 /* Info */) {
1164
+ console.info(...args);
1165
+ }
1166
+ }
1167
+ };
1168
+ /** @internal */
1169
+ _Logging._level = 3 /* Info */;
1170
+ /** @internal */
1171
+ _Logging._emitterInstance = null;
1172
+ var Logging = _Logging;
1173
+
1115
1174
  // src/metadata.ts
1116
1175
  var RichImageData = class {
1117
1176
  /** Creates a new {@link RichImageData}. */
@@ -1439,7 +1498,8 @@ var Mediabunny = (() => {
1439
1498
  "hevc",
1440
1499
  "vp9",
1441
1500
  "av1",
1442
- "vp8"
1501
+ "vp8",
1502
+ "prores"
1443
1503
  ];
1444
1504
  var PCM_AUDIO_CODECS = [
1445
1505
  "pcm-s16",
@@ -1640,7 +1700,35 @@ var Mediabunny = (() => {
1640
1700
  ];
1641
1701
  var VP9_DEFAULT_SUFFIX = ".01.01.01.01.00";
1642
1702
  var AV1_DEFAULT_SUFFIX = ".0.110.01.01.01.0";
1643
- var buildVideoCodecString = (codec, width, height, bitrate) => {
1703
+ var PRORES_FOURCCS = [
1704
+ "ap4x",
1705
+ // ProRes 4444 XQ
1706
+ "ap4h",
1707
+ // ProRes 4444
1708
+ "apch",
1709
+ // ProRes 422 High Quality
1710
+ "apcn",
1711
+ // ProRes 422 Standard Definition
1712
+ "apcs",
1713
+ // ProRes 422 LT
1714
+ "apco"
1715
+ // ProRes 422 Proxy
1716
+ ];
1717
+ var PRORES_PROFILE_TARGET_BITRATES = [
1718
+ { fourCc: "apco", bitrate: 45e6, alpha: false },
1719
+ // 422 Proxy
1720
+ { fourCc: "apcs", bitrate: 102e6, alpha: false },
1721
+ // 422 LT
1722
+ { fourCc: "apcn", bitrate: 147e6, alpha: false },
1723
+ // 422 Standard
1724
+ { fourCc: "apch", bitrate: 22e7, alpha: false },
1725
+ // 422 HQ
1726
+ { fourCc: "ap4h", bitrate: 33e7, alpha: true },
1727
+ // 4444
1728
+ { fourCc: "ap4x", bitrate: 5e8, alpha: true }
1729
+ // 4444 XQ
1730
+ ];
1731
+ var buildVideoCodecString = (codec, width, height, bitrate, alpha) => {
1644
1732
  if (codec === "avc") {
1645
1733
  const profileIndication = 100;
1646
1734
  const totalMacroblocks = Math.ceil(width / 16) * Math.ceil(height / 16);
@@ -1681,8 +1769,24 @@ var Mediabunny = (() => {
1681
1769
  const level = levelInfo.level.toString().padStart(2, "0");
1682
1770
  const bitDepth = "08";
1683
1771
  return `av01.${profile}.${level}${levelInfo.tier}.${bitDepth}`;
1772
+ } else if (codec === "prores") {
1773
+ const referencePixels = 1920 * 1080;
1774
+ const scaleFactor = Math.pow(width * height / referencePixels, 0.95);
1775
+ const candidates = PRORES_PROFILE_TARGET_BITRATES.filter((x) => x.alpha === alpha);
1776
+ let bestFourCc = candidates[0].fourCc;
1777
+ let smallestDifference = Infinity;
1778
+ for (const { fourCc, bitrate: targetBitrate } of candidates) {
1779
+ const difference = Math.abs(targetBitrate * scaleFactor - bitrate);
1780
+ if (difference < smallestDifference) {
1781
+ smallestDifference = difference;
1782
+ bestFourCc = fourCc;
1783
+ }
1784
+ }
1785
+ return bestFourCc;
1786
+ } else {
1787
+ assertNever(codec);
1684
1788
  }
1685
- throw new TypeError(`Unhandled codec '${codec}'.`);
1789
+ throw new TypeError(`Unhandled codec '${String(codec)}'.`);
1686
1790
  };
1687
1791
  var generateVp9CodecConfigurationFromCodecString = (codecString) => {
1688
1792
  const parts = codecString.split(".");
@@ -1728,7 +1832,16 @@ var Mediabunny = (() => {
1728
1832
  return [firstByte, secondByte, thirdByte, fourthByte];
1729
1833
  };
1730
1834
  var extractVideoCodecString = (trackInfo) => {
1731
- const { codec, codecDescription, colorSpace, avcCodecInfo, hevcCodecInfo, vp9CodecInfo, av1CodecInfo } = trackInfo;
1835
+ const {
1836
+ codec,
1837
+ codecDescription,
1838
+ colorSpace,
1839
+ avcCodecInfo,
1840
+ hevcCodecInfo,
1841
+ vp9CodecInfo,
1842
+ av1CodecInfo,
1843
+ proresFormat
1844
+ } = trackInfo;
1732
1845
  if (codec === "avc") {
1733
1846
  assert(trackInfo.avcType !== null);
1734
1847
  if (avcCodecInfo) {
@@ -1848,6 +1961,10 @@ var Mediabunny = (() => {
1848
1961
  string = string.slice(0, -AV1_DEFAULT_SUFFIX.length);
1849
1962
  }
1850
1963
  return string;
1964
+ } else if (codec === "prores") {
1965
+ return proresFormat ?? "apch";
1966
+ } else if (codec !== null) {
1967
+ assertNever(codec);
1851
1968
  }
1852
1969
  throw new TypeError(`Unhandled codec '${codec}'.`);
1853
1970
  };
@@ -1977,6 +2094,8 @@ var Mediabunny = (() => {
1977
2094
  return "vp9";
1978
2095
  } else if (codecString.startsWith("av01")) {
1979
2096
  return "av1";
2097
+ } else if (PRORES_FOURCCS.includes(codecString)) {
2098
+ return "prores";
1980
2099
  }
1981
2100
  if (codecString === "mp3" || codecString === "mp4a.69" || codecString === "mp4a.6B" || codecString === "mp4a.6b" || codecString === "mp4a.40.34") {
1982
2101
  return "mp3";
@@ -2039,7 +2158,7 @@ var Mediabunny = (() => {
2039
2158
  }
2040
2159
  return {};
2041
2160
  };
2042
- var VALID_VIDEO_CODEC_STRING_PREFIXES = ["avc1", "avc3", "hev1", "hvc1", "vp8", "vp09", "av01"];
2161
+ var VALID_VIDEO_CODEC_STRING_PREFIXES = ["avc1", "avc3", "hev1", "hvc1", "vp8", "vp09", "av01", ...PRORES_FOURCCS];
2043
2162
  var AVC_CODEC_STRING_REGEX = /^(avc1|avc3)\.[0-9a-fA-F]{6}$/;
2044
2163
  var HEVC_CODEC_STRING_REGEX = /^(hev1|hvc1)\.(?:[ABC]?\d+)\.[0-9a-fA-F]{1,8}\.[LH]\d+(?:\.[0-9a-fA-F]{1,2}){0,6}$/;
2045
2164
  var VP9_CODEC_STRING_REGEX = /^vp09(?:\.\d{2}){3}(?:(?:\.\d{2}){5})?$/;
@@ -2156,6 +2275,12 @@ var Mediabunny = (() => {
2156
2275
  'Video chunk metadata decoder configuration codec string for AV1 must be a valid AV1 codec string as specified in Section "Codecs Parameter String" of https://aomediacodec.github.io/av1-isobmff/.'
2157
2276
  );
2158
2277
  }
2278
+ } else if (PRORES_FOURCCS.some((x) => metadata.decoderConfig.codec.startsWith(x))) {
2279
+ if (!PRORES_FOURCCS.some((x) => metadata.decoderConfig.codec === x)) {
2280
+ throw new TypeError(
2281
+ `Video chunk metadata decoder configuration codec string for ProRes must be one of the valid ProRes four-character codes: ${PRORES_FOURCCS.join(", ")}.`
2282
+ );
2283
+ }
2159
2284
  }
2160
2285
  };
2161
2286
  var VALID_AUDIO_CODEC_STRING_PREFIXES = [
@@ -2735,7 +2860,7 @@ var Mediabunny = (() => {
2735
2860
  sequenceParameterSetExt: hasExtendedData ? spsExtUnits : null
2736
2861
  };
2737
2862
  } catch (error) {
2738
- console.error("Error building AVC Decoder Configuration Record:", error);
2863
+ Logging._error("Error building AVC Decoder Configuration Record:", error);
2739
2864
  return null;
2740
2865
  }
2741
2866
  };
@@ -2841,7 +2966,7 @@ var Mediabunny = (() => {
2841
2966
  }
2842
2967
  return record;
2843
2968
  } catch (error) {
2844
- console.error("Error deserializing AVC Decoder Configuration Record:", error);
2969
+ Logging._error("Error deserializing AVC Decoder Configuration Record:", error);
2845
2970
  return null;
2846
2971
  }
2847
2972
  };
@@ -3070,7 +3195,7 @@ var Mediabunny = (() => {
3070
3195
  maxDecFrameBuffering
3071
3196
  };
3072
3197
  } catch (error) {
3073
- console.error("Error parsing AVC SPS:", error);
3198
+ Logging._error("Error parsing AVC SPS:", error);
3074
3199
  return null;
3075
3200
  }
3076
3201
  };
@@ -3234,7 +3359,7 @@ var Mediabunny = (() => {
3234
3359
  minSpatialSegmentationIdc
3235
3360
  };
3236
3361
  } catch (error) {
3237
- console.error("Error parsing HEVC SPS:", error);
3362
+ Logging._error("Error parsing HEVC SPS:", error);
3238
3363
  return null;
3239
3364
  }
3240
3365
  };
@@ -3345,7 +3470,7 @@ var Mediabunny = (() => {
3345
3470
  };
3346
3471
  return record;
3347
3472
  } catch (error) {
3348
- console.error("Error building HEVC Decoder Configuration Record:", error);
3473
+ Logging._error("Error building HEVC Decoder Configuration Record:", error);
3349
3474
  return null;
3350
3475
  }
3351
3476
  };
@@ -3693,7 +3818,7 @@ var Mediabunny = (() => {
3693
3818
  arrays
3694
3819
  };
3695
3820
  } catch (error) {
3696
- console.error("Error deserializing HEVC Decoder Configuration Record:", error);
3821
+ Logging._error("Error deserializing HEVC Decoder Configuration Record:", error);
3697
3822
  return null;
3698
3823
  }
3699
3824
  };
@@ -4279,6 +4404,11 @@ var Mediabunny = (() => {
4279
4404
  return null;
4280
4405
  }
4281
4406
  ;
4407
+ case "prores":
4408
+ {
4409
+ return "key";
4410
+ }
4411
+ ;
4282
4412
  default:
4283
4413
  {
4284
4414
  assertNever(codec);
@@ -5877,7 +6007,7 @@ var Mediabunny = (() => {
5877
6007
  continue;
5878
6008
  }
5879
6009
  if (relevantEntryFound) {
5880
- console.warn(
6010
+ Logging._warn(
5881
6011
  "Unsupported edit list: multiple edits are not currently supported. Only using first edit."
5882
6012
  );
5883
6013
  break;
@@ -5887,7 +6017,7 @@ var Mediabunny = (() => {
5887
6017
  continue;
5888
6018
  }
5889
6019
  if (mediaRate !== 1) {
5890
- console.warn("Unsupported edit list entry: media rate must be 1.");
6020
+ Logging._warn("Unsupported edit list entry: media rate must be 1.");
5891
6021
  break;
5892
6022
  }
5893
6023
  track.editListPreviousSegmentDurations = previousSegmentDurations;
@@ -5950,7 +6080,8 @@ var Mediabunny = (() => {
5950
6080
  avcCodecInfo: null,
5951
6081
  hevcCodecInfo: null,
5952
6082
  vp9CodecInfo: null,
5953
- av1CodecInfo: null
6083
+ av1CodecInfo: null,
6084
+ proresFormat: null
5954
6085
  };
5955
6086
  } else if (handlerType === "soun") {
5956
6087
  track.info = {
@@ -6025,10 +6156,13 @@ var Mediabunny = (() => {
6025
6156
  track.info.codec = "vp9";
6026
6157
  } else if (codecName === "av01") {
6027
6158
  track.info.codec = "av1";
6159
+ } else if (PRORES_FOURCCS.includes(lowercaseBoxName)) {
6160
+ track.info.codec = "prores";
6161
+ track.info.proresFormat = lowercaseBoxName;
6028
6162
  } else if (codecName === null) {
6029
- console.warn(`Unknown encrypted video codec due to missing frma box.`);
6163
+ Logging._warn(`Unknown encrypted video codec due to missing frma box.`);
6030
6164
  } else {
6031
- console.warn(`Unsupported video codec (sample entry type '${sampleBoxInfo.name}').`);
6165
+ Logging._warn(`Unsupported video codec (sample entry type '${sampleBoxInfo.name}').`);
6032
6166
  }
6033
6167
  } else {
6034
6168
  slice.skip(6 * 1 + 2);
@@ -6085,7 +6219,7 @@ var Mediabunny = (() => {
6085
6219
  } else if (sampleSize === 16) {
6086
6220
  track.info.codec = track.info.pcmLittleEndian ? "pcm-s16" : "pcm-s16be";
6087
6221
  } else {
6088
- console.warn(`Unsupported sample size ${sampleSize} for codec 'twos'.`);
6222
+ Logging._warn(`Unsupported sample size ${sampleSize} for codec 'twos'.`);
6089
6223
  track.info.codec = null;
6090
6224
  }
6091
6225
  } else if (codecName === "sowt") {
@@ -6094,7 +6228,7 @@ var Mediabunny = (() => {
6094
6228
  } else if (sampleSize === 16) {
6095
6229
  track.info.codec = "pcm-s16";
6096
6230
  } else {
6097
- console.warn(`Unsupported sample size ${sampleSize} for codec 'sowt'.`);
6231
+ Logging._warn(`Unsupported sample size ${sampleSize} for codec 'sowt'.`);
6098
6232
  track.info.codec = null;
6099
6233
  }
6100
6234
  } else if (codecName === "raw ") {
@@ -6117,7 +6251,7 @@ var Mediabunny = (() => {
6117
6251
  } else if (pcmSampleSize === 32) {
6118
6252
  track.info.codec = "pcm-s32";
6119
6253
  } else {
6120
- console.warn(`Invalid ipcm sample size ${pcmSampleSize}.`);
6254
+ Logging._warn(`Invalid ipcm sample size ${pcmSampleSize}.`);
6121
6255
  track.info.codec = null;
6122
6256
  }
6123
6257
  } else {
@@ -6128,7 +6262,7 @@ var Mediabunny = (() => {
6128
6262
  } else if (pcmSampleSize === 32) {
6129
6263
  track.info.codec = "pcm-s32be";
6130
6264
  } else {
6131
- console.warn(`Invalid ipcm sample size ${pcmSampleSize}.`);
6265
+ Logging._warn(`Invalid ipcm sample size ${pcmSampleSize}.`);
6132
6266
  track.info.codec = null;
6133
6267
  }
6134
6268
  }
@@ -6140,7 +6274,7 @@ var Mediabunny = (() => {
6140
6274
  } else if (pcmSampleSize === 64) {
6141
6275
  track.info.codec = "pcm-f64";
6142
6276
  } else {
6143
- console.warn(`Invalid fpcm sample size ${pcmSampleSize}.`);
6277
+ Logging._warn(`Invalid fpcm sample size ${pcmSampleSize}.`);
6144
6278
  track.info.codec = null;
6145
6279
  }
6146
6280
  } else {
@@ -6149,7 +6283,7 @@ var Mediabunny = (() => {
6149
6283
  } else if (pcmSampleSize === 64) {
6150
6284
  track.info.codec = "pcm-f64be";
6151
6285
  } else {
6152
- console.warn(`Invalid fpcm sample size ${pcmSampleSize}.`);
6286
+ Logging._warn(`Invalid fpcm sample size ${pcmSampleSize}.`);
6153
6287
  track.info.codec = null;
6154
6288
  }
6155
6289
  }
@@ -6182,12 +6316,12 @@ var Mediabunny = (() => {
6182
6316
  }
6183
6317
  }
6184
6318
  if (track.info.codec === null) {
6185
- console.warn("Unsupported PCM format.");
6319
+ Logging._warn("Unsupported PCM format.");
6186
6320
  }
6187
6321
  } else if (codecName === null) {
6188
- console.warn(`Unknown encrypted audio codec due to missing frma box.`);
6322
+ Logging._warn(`Unknown encrypted audio codec due to missing frma box.`);
6189
6323
  } else {
6190
- console.warn(`Unsupported audio codec (sample entry type '${sampleBoxInfo.name}').`);
6324
+ Logging._warn(`Unsupported audio codec (sample entry type '${sampleBoxInfo.name}').`);
6191
6325
  }
6192
6326
  }
6193
6327
  slice.filePos = sampleBoxStartPos + sampleBoxInfo.totalSize;
@@ -6226,7 +6360,7 @@ var Mediabunny = (() => {
6226
6360
  defaultSkipByteBlock: null
6227
6361
  };
6228
6362
  } else {
6229
- console.warn(`Unsupported encryption scheme '${schemeType}'.`);
6363
+ Logging._warn(`Unsupported encryption scheme '${schemeType}'.`);
6230
6364
  }
6231
6365
  }
6232
6366
  ;
@@ -6439,7 +6573,7 @@ var Mediabunny = (() => {
6439
6573
  } else if (objectTypeIndication === 221) {
6440
6574
  track.info.codec = "vorbis";
6441
6575
  } else {
6442
- console.warn(
6576
+ Logging._warn(
6443
6577
  `Unsupported audio codec (objectTypeIndication ${objectTypeIndication}) - discarding track.`
6444
6578
  );
6445
6579
  }
@@ -6593,7 +6727,7 @@ var Mediabunny = (() => {
6593
6727
  const bytes2 = readBytes(slice, boxInfo.contentSize);
6594
6728
  const config = parseEac3Config(bytes2);
6595
6729
  if (!config) {
6596
- console.warn("Invalid dec3 box contents, ignoring.");
6730
+ Logging._warn("Invalid dec3 box contents, ignoring.");
6597
6731
  break;
6598
6732
  }
6599
6733
  const sampleRate = getEac3SampleRate(config);
@@ -7152,7 +7286,7 @@ var Mediabunny = (() => {
7152
7286
  break;
7153
7287
  }
7154
7288
  if (entryCount > 1) {
7155
- console.warn("Multiple saio entries are not supported; using the first offset only.");
7289
+ Logging._warn("Multiple saio entries are not supported; using the first offset only.");
7156
7290
  }
7157
7291
  let offset = version === 0 ? readU32Be(slice) : Number(readU64Be(slice));
7158
7292
  if (this.currentFragment) {
@@ -7987,7 +8121,7 @@ var Mediabunny = (() => {
7987
8121
  };
7988
8122
  }
7989
8123
  async canBeTransparent() {
7990
- return false;
8124
+ return this.internalTrack.info.codec === "prores" && (this.internalTrack.info.proresFormat === "ap4h" || this.internalTrack.info.proresFormat === "ap4x");
7991
8125
  }
7992
8126
  async getDecoderConfig() {
7993
8127
  if (!this.internalTrack.info.codec) {
@@ -8882,6 +9016,7 @@ var Mediabunny = (() => {
8882
9016
  "vp8": "V_VP8",
8883
9017
  "vp9": "V_VP9",
8884
9018
  "av1": "V_AV1",
9019
+ "prores": "V_PRORES",
8885
9020
  "aac": "A_AAC",
8886
9021
  "mp3": "A_MPEG/L3",
8887
9022
  "opus": "A_OPUS",
@@ -9509,7 +9644,7 @@ var Mediabunny = (() => {
9509
9644
  if (this.currentTrack.decodingInstructions.some((instruction) => {
9510
9645
  return instruction.data?.type !== "decompress" || instruction.scope !== 1 /* Block */ || instruction.data.algorithm !== 3 /* HeaderStripping */;
9511
9646
  })) {
9512
- console.warn(`Track #${this.currentTrack.id} has an unsupported content encoding; dropping.`);
9647
+ Logging._warn(`Track #${this.currentTrack.id} has an unsupported content encoding; dropping.`);
9513
9648
  this.currentTrack = null;
9514
9649
  }
9515
9650
  if (this.currentTrack && this.currentTrack.id !== -1 && this.currentTrack.codecId && this.currentTrack.info) {
@@ -9545,6 +9680,13 @@ var Mediabunny = (() => {
9545
9680
  this.currentTrack.info.codec = "vp9";
9546
9681
  } else if (codecIdWithoutSuffix === CODEC_STRING_MAP.av1) {
9547
9682
  this.currentTrack.info.codec = "av1";
9683
+ } else if (codecIdWithoutSuffix === CODEC_STRING_MAP.prores) {
9684
+ const format = this.currentTrack.codecPrivate ? textDecoder.decode(this.currentTrack.codecPrivate) : "";
9685
+ if (PRORES_FOURCCS.includes(format)) {
9686
+ this.currentTrack.info.codec = "prores";
9687
+ this.currentTrack.info.proresFormat = format;
9688
+ } else {
9689
+ }
9548
9690
  }
9549
9691
  const videoTrack = this.currentTrack;
9550
9692
  this.currentTrack.trackBacking = new MatroskaVideoTrackBacking(videoTrack);
@@ -9636,7 +9778,8 @@ var Mediabunny = (() => {
9636
9778
  codec: null,
9637
9779
  codecDescription: null,
9638
9780
  colorSpace: null,
9639
- alphaMode: false
9781
+ alphaMode: false,
9782
+ proresFormat: null
9640
9783
  };
9641
9784
  } else if (type === 2) {
9642
9785
  this.currentTrack.info = {
@@ -10819,7 +10962,7 @@ var Mediabunny = (() => {
10819
10962
  };
10820
10963
  }
10821
10964
  async canBeTransparent() {
10822
- return this.internalTrack.info.alphaMode;
10965
+ return this.internalTrack.info.alphaMode || this.internalTrack.info.codec === "prores" && (this.internalTrack.info.proresFormat === "ap4h" || this.internalTrack.info.proresFormat === "ap4x");
10823
10966
  }
10824
10967
  async getDecoderConfig() {
10825
10968
  if (!this.internalTrack.info.codec) {
@@ -10843,7 +10986,8 @@ var Mediabunny = (() => {
10843
10986
  avcCodecInfo: this.internalTrack.info.codec === "avc" && firstPacket ? extractAvcDecoderConfigurationRecord(firstPacket.data) : null,
10844
10987
  hevcCodecInfo: this.internalTrack.info.codec === "hevc" && firstPacket ? extractHevcDecoderConfigurationRecord(firstPacket.data) : null,
10845
10988
  vp9CodecInfo: this.internalTrack.info.codec === "vp9" && firstPacket ? extractVp9CodecInfoFromPacket(firstPacket.data) : null,
10846
- av1CodecInfo: this.internalTrack.info.codec === "av1" && firstPacket ? extractAv1CodecInfoFromPacket(firstPacket.data) : null
10989
+ av1CodecInfo: this.internalTrack.info.codec === "av1" && firstPacket ? extractAv1CodecInfoFromPacket(firstPacket.data) : null,
10990
+ proresFormat: this.internalTrack.info.proresFormat
10847
10991
  }),
10848
10992
  codedWidth: this.internalTrack.info.width,
10849
10993
  codedHeight: this.internalTrack.info.height,
@@ -13024,8 +13168,26 @@ var Mediabunny = (() => {
13024
13168
  return "audio/flac";
13025
13169
  }
13026
13170
  async readMetadata() {
13027
- let currentPos = 4;
13028
13171
  return this.metadataPromise ??= (async () => {
13172
+ let currentPos = 0;
13173
+ while (true) {
13174
+ let headerSlice = this.reader.requestSlice(currentPos, ID3_V2_HEADER_SIZE);
13175
+ if (headerSlice instanceof Promise) headerSlice = await headerSlice;
13176
+ if (!headerSlice) {
13177
+ this.lastSampleLoaded = true;
13178
+ return;
13179
+ }
13180
+ const id3V2Header = readId3V2Header(headerSlice);
13181
+ if (!id3V2Header) {
13182
+ break;
13183
+ }
13184
+ let contentSlice = this.reader.requestSlice(headerSlice.filePos, id3V2Header.size);
13185
+ if (contentSlice instanceof Promise) contentSlice = await contentSlice;
13186
+ assert(contentSlice);
13187
+ parseId3V2Tag(contentSlice, id3V2Header, this.metadataTags);
13188
+ currentPos = headerSlice.filePos + id3V2Header.size;
13189
+ }
13190
+ currentPos += 4;
13029
13191
  while (this.reader.fileSize === null || currentPos < this.reader.fileSize) {
13030
13192
  let sizeSlice = this.reader.requestSlice(currentPos, 4);
13031
13193
  if (sizeSlice instanceof Promise) sizeSlice = await sizeSlice;
@@ -13707,7 +13869,7 @@ var Mediabunny = (() => {
13707
13869
  break;
13708
13870
  default: {
13709
13871
  if (!ignoredStreamTypes.has(streamType)) {
13710
- console.warn(
13872
+ Logging._warn(
13711
13873
  `Note: MPEG-TS streams with stream_type 0x${streamType.toString(16)} are not currently supported.`
13712
13874
  );
13713
13875
  ignoredStreamTypes.add(streamType);
@@ -13845,7 +14007,8 @@ var Mediabunny = (() => {
13845
14007
  avcCodecInfo: elementaryStream.info.avcCodecInfo,
13846
14008
  hevcCodecInfo: elementaryStream.info.hevcCodecInfo,
13847
14009
  vp9CodecInfo: null,
13848
- av1CodecInfo: null
14010
+ av1CodecInfo: null,
14011
+ proresFormat: null
13849
14012
  }),
13850
14013
  codedWidth: elementaryStream.info.width,
13851
14014
  codedHeight: elementaryStream.info.height,
@@ -16026,7 +16189,7 @@ var Mediabunny = (() => {
16026
16189
  }
16027
16190
  const isOnline = typeof navigator !== "undefined" && typeof navigator.onLine === "boolean" ? navigator.onLine : true;
16028
16191
  if (isOnline && originOfSrc !== null && originOfSrc !== window.location.origin) {
16029
- console.warn(
16192
+ Logging._warn(
16030
16193
  `Request will not be retried because a CORS error was suspected due to different origins. You can modify this behavior by providing your own function for the 'getRetryDelay' option.`
16031
16194
  );
16032
16195
  return null;
@@ -16200,8 +16363,7 @@ var Mediabunny = (() => {
16200
16363
  );
16201
16364
  if (url2.origin !== "null" && !(url2.pathname.endsWith(".m3u8") || url2.pathname.endsWith(".m3u"))) {
16202
16365
  if (!warnedOrigins.has(url2.origin)) {
16203
- console.log(this._usedForHls, this._url, url2.pathname);
16204
- console.warn(
16366
+ Logging._warn(
16205
16367
  `HTTP server (origin ${url2.origin}) did not respond to a range request with 206 Partial Content, meaning the entire resource will now be downloaded. To enable efficient media file streaming across a network, please make sure your server supports range requests.`
16206
16368
  );
16207
16369
  warnedOrigins.add(url2.origin);
@@ -16239,7 +16401,7 @@ var Mediabunny = (() => {
16239
16401
  }
16240
16402
  const retryDelayInSeconds = this._getRetryDelay(1, error, this._url);
16241
16403
  if (retryDelayInSeconds !== null) {
16242
- console.error("Error while reading response stream. Attempting to resume.", error);
16404
+ Logging._error("Error while reading response stream. Attempting to resume.", error);
16243
16405
  await wait(1e3 * retryDelayInSeconds);
16244
16406
  break;
16245
16407
  } else {
@@ -18747,7 +18909,18 @@ var Mediabunny = (() => {
18747
18909
  var FlacInputFormat = class extends InputFormat {
18748
18910
  /** @internal */
18749
18911
  async _canReadInput(input) {
18750
- let slice = input._reader.requestSlice(0, 4);
18912
+ let currentPos = 0;
18913
+ while (true) {
18914
+ let slice2 = input._reader.requestSlice(currentPos, ID3_V2_HEADER_SIZE);
18915
+ if (slice2 instanceof Promise) slice2 = await slice2;
18916
+ if (!slice2) break;
18917
+ const id3V2Header = readId3V2Header(slice2);
18918
+ if (!id3V2Header) {
18919
+ break;
18920
+ }
18921
+ currentPos = slice2.filePos + id3V2Header.size;
18922
+ }
18923
+ let slice = input._reader.requestSlice(currentPos, 4);
18751
18924
  if (slice instanceof Promise) slice = await slice;
18752
18925
  if (!slice) return false;
18753
18926
  return readAscii(slice, 4) === "fLaC";
@@ -18977,7 +19150,7 @@ var Mediabunny = (() => {
18977
19150
  ...options,
18978
19151
  codedWidth: options.codedWidth ?? 1280,
18979
19152
  codedHeight: options.codedHeight ?? 720,
18980
- codec: options.codec ?? buildVideoCodecString(codec, 1280, 720, 1e6)
19153
+ codec: options.codec ?? buildVideoCodecString(codec, 1280, 720, 1e6, false)
18981
19154
  };
18982
19155
  resolvedOptions.description ??= guessDescriptionForVideo(resolvedOptions);
18983
19156
  const key = JSON.stringify(resolvedOptions);
@@ -19063,7 +19236,7 @@ var Mediabunny = (() => {
19063
19236
  const now = performance.now();
19064
19237
  if (value.type === "video") {
19065
19238
  if (now - lastVideoGcErrorLog >= 1e3) {
19066
- console.error(
19239
+ Logging._error(
19067
19240
  `A VideoSample was garbage collected without first being closed. For proper resource management, make sure to call close() on all your VideoSamples as soon as you're done using them.`
19068
19241
  );
19069
19242
  lastVideoGcErrorLog = now;
@@ -19073,7 +19246,7 @@ var Mediabunny = (() => {
19073
19246
  }
19074
19247
  } else {
19075
19248
  if (now - lastAudioGcErrorLog >= 1e3) {
19076
- console.error(
19249
+ Logging._error(
19077
19250
  `An AudioSample was garbage collected without first being closed. For proper resource management, make sure to call close() on all your AudioSamples as soon as you're done using them.`
19078
19251
  );
19079
19252
  lastAudioGcErrorLog = now;
@@ -19185,12 +19358,11 @@ var Mediabunny = (() => {
19185
19358
  "init.displayWidth and init.displayHeight must be either both provided or both omitted."
19186
19359
  );
19187
19360
  }
19188
- this._data = init._doNotCopy ? toUint8Array(data) : toUint8Array(data).slice();
19189
- this._layout = init.layout ?? createDefaultPlaneLayout(init.format, init.codedWidth, init.codedHeight);
19190
19361
  this.format = init.format;
19191
19362
  this.rotation = init.rotation ?? 0;
19192
19363
  this.timestamp = init.timestamp;
19193
19364
  this.duration = init.duration ?? 0;
19365
+ const layout = init.layout ?? createDefaultPlaneLayout(init.format, init.codedWidth, init.codedHeight);
19194
19366
  let colorSpaceInit = init.colorSpace ?? null;
19195
19367
  if (colorSpaceInit === null) {
19196
19368
  if (this.format === "RGBA" || this.format === "RGBX" || this.format === "BGRA" || this.format === "BGRX") {
@@ -19209,7 +19381,6 @@ var Mediabunny = (() => {
19209
19381
  };
19210
19382
  }
19211
19383
  }
19212
- this.colorSpace = new VideoSampleColorSpace(colorSpaceInit);
19213
19384
  this.visibleRect = {
19214
19385
  left: init.visibleRect?.left ?? 0,
19215
19386
  top: init.visibleRect?.top ?? 0,
@@ -19223,6 +19394,9 @@ var Mediabunny = (() => {
19223
19394
  this.squarePixelWidth = this.visibleRect.width;
19224
19395
  this.squarePixelHeight = this.visibleRect.height;
19225
19396
  }
19397
+ this._data = init._doNotCopy ? toUint8Array(data) : toUint8Array(data).slice();
19398
+ this._layout = layout;
19399
+ this.colorSpace = new VideoSampleColorSpace(colorSpaceInit);
19226
19400
  } else if (typeof VideoFrame !== "undefined" && data instanceof VideoFrame) {
19227
19401
  if (init?.rotation !== void 0 && ![0, 90, 180, 270].includes(init.rotation)) {
19228
19402
  throw new TypeError("init.rotation, when provided, must be 0, 90, 180, or 270.");
@@ -19681,6 +19855,7 @@ var Mediabunny = (() => {
19681
19855
  timestamp: this.microsecondTimestamp,
19682
19856
  duration: this.microsecondDuration,
19683
19857
  colorSpace: this.colorSpace,
19858
+ visibleRect: this.visibleRect,
19684
19859
  displayWidth: this.squarePixelWidth,
19685
19860
  // Not display* since we're not passing rotation
19686
19861
  displayHeight: this.squarePixelHeight
@@ -19692,13 +19867,18 @@ var Mediabunny = (() => {
19692
19867
  // Drag 0 duration to undefined, glitches some codecs
19693
19868
  });
19694
19869
  } else if (this._data instanceof Uint8Array) {
19870
+ assert(this._layout);
19695
19871
  return new VideoFrame(this._data, {
19696
19872
  format: this.format,
19697
19873
  codedWidth: this.codedWidth,
19874
+ // This is technically wrong! codedWidth is a lie technically. But, since
19698
19875
  codedHeight: this.codedHeight,
19876
+ // we pass the layout (which contains the true coded width), we're good.
19877
+ layout: this._layout,
19699
19878
  timestamp: this.microsecondTimestamp,
19700
19879
  duration: this.microsecondDuration || void 0,
19701
19880
  colorSpace: this.colorSpace,
19881
+ visibleRect: this.visibleRect,
19702
19882
  displayWidth: this.squarePixelWidth,
19703
19883
  // Not display* since we're not passing rotation
19704
19884
  displayHeight: this.squarePixelHeight
@@ -21216,7 +21396,8 @@ var Mediabunny = (() => {
21216
21396
  options.codec,
21217
21397
  options.width,
21218
21398
  options.height,
21219
- resolvedBitrate
21399
+ resolvedBitrate,
21400
+ options.alpha === "keep"
21220
21401
  ),
21221
21402
  width: options.width,
21222
21403
  height: options.height,
@@ -21313,6 +21494,10 @@ var Mediabunny = (() => {
21313
21494
  /** @internal */
21314
21495
  _toVideoBitrate(codec, width, height) {
21315
21496
  const pixels = width * height;
21497
+ const referencePixels = 1920 * 1080;
21498
+ const referenceBitrate = 3e6;
21499
+ const scaleFactor = Math.pow(pixels / referencePixels, 0.95);
21500
+ const baseBitrate = referenceBitrate * scaleFactor;
21316
21501
  const codecEfficiencyFactors = {
21317
21502
  avc: 1,
21318
21503
  // H.264/AVC (baseline)
@@ -21322,13 +21507,11 @@ var Mediabunny = (() => {
21322
21507
  // Similar to HEVC
21323
21508
  av1: 0.4,
21324
21509
  // ~60% more efficient than AVC
21325
- vp8: 1.2
21510
+ vp8: 1.2,
21326
21511
  // Slightly less efficient than AVC
21512
+ prores: 22e7 / referenceBitrate
21513
+ // Apple ProRes white paper claims 220 Mbps for 1080p 422 HQ @30Hz
21327
21514
  };
21328
- const referencePixels = 1920 * 1080;
21329
- const referenceBitrate = 3e6;
21330
- const scaleFactor = Math.pow(pixels / referencePixels, 0.95);
21331
- const baseBitrate = referenceBitrate * scaleFactor;
21332
21515
  const codecAdjustedBitrate = baseBitrate * codecEfficiencyFactors[codec];
21333
21516
  const finalBitrate = codecAdjustedBitrate * this._factor;
21334
21517
  return Math.ceil(finalBitrate / 1e3) * 1e3;
@@ -21621,7 +21804,7 @@ var Mediabunny = (() => {
21621
21804
  if (decoder.prototype instanceof CustomVideoDecoder) {
21622
21805
  const casted = decoder;
21623
21806
  if (customVideoDecoders.includes(casted)) {
21624
- console.warn("Video decoder already registered.");
21807
+ Logging._warn("Video decoder already registered.");
21625
21808
  return;
21626
21809
  }
21627
21810
  customVideoDecoders.push(casted);
@@ -21629,7 +21812,7 @@ var Mediabunny = (() => {
21629
21812
  } else if (decoder.prototype instanceof CustomAudioDecoder) {
21630
21813
  const casted = decoder;
21631
21814
  if (customAudioDecoders.includes(casted)) {
21632
- console.warn("Audio decoder already registered.");
21815
+ Logging._warn("Audio decoder already registered.");
21633
21816
  return;
21634
21817
  }
21635
21818
  customAudioDecoders.push(casted);
@@ -21642,7 +21825,7 @@ var Mediabunny = (() => {
21642
21825
  if (encoder.prototype instanceof CustomVideoEncoder) {
21643
21826
  const casted = encoder;
21644
21827
  if (customVideoEncoders.includes(casted)) {
21645
- console.warn("Video encoder already registered.");
21828
+ Logging._warn("Video encoder already registered.");
21646
21829
  return;
21647
21830
  }
21648
21831
  customVideoEncoders.push(casted);
@@ -21650,7 +21833,7 @@ var Mediabunny = (() => {
21650
21833
  } else if (encoder.prototype instanceof CustomAudioEncoder) {
21651
21834
  const casted = encoder;
21652
21835
  if (customAudioEncoders.includes(casted)) {
21653
- console.warn("Audio encoder already registered.");
21836
+ Logging._warn("Audio encoder already registered.");
21654
21837
  return;
21655
21838
  }
21656
21839
  customAudioEncoders.push(casted);
@@ -21922,6 +22105,7 @@ var Mediabunny = (() => {
21922
22105
  let ended = false;
21923
22106
  let terminated = false;
21924
22107
  let outOfBandError = null;
22108
+ let hasOutOfBandError = false;
21925
22109
  const timestamps = [];
21926
22110
  const maxQueueSize = () => Math.max(2, timestamps.length);
21927
22111
  (async () => {
@@ -21943,8 +22127,9 @@ var Mediabunny = (() => {
21943
22127
  ended = true;
21944
22128
  onQueueNotEmpty();
21945
22129
  })().catch((error) => {
21946
- if (!outOfBandError) {
22130
+ if (!hasOutOfBandError) {
21947
22131
  outOfBandError = error;
22132
+ hasOutOfBandError = true;
21948
22133
  onQueueNotEmpty();
21949
22134
  }
21950
22135
  });
@@ -21956,7 +22141,7 @@ var Mediabunny = (() => {
21956
22141
  throw new InputDisposedError();
21957
22142
  } else if (terminated) {
21958
22143
  return { value: void 0, done: true };
21959
- } else if (outOfBandError) {
22144
+ } else if (hasOutOfBandError) {
21960
22145
  throw outOfBandError;
21961
22146
  } else if (packetQueue.length > 0) {
21962
22147
  const value = packetQueue.shift();
@@ -22009,6 +22194,7 @@ var Mediabunny = (() => {
22009
22194
  let ended = false;
22010
22195
  let terminated = false;
22011
22196
  let outOfBandError = null;
22197
+ let hasOutOfBandError = false;
22012
22198
  const packetRetrievalOptions = {
22013
22199
  ...options,
22014
22200
  verifyKeyPackets: true,
@@ -22042,8 +22228,9 @@ var Mediabunny = (() => {
22042
22228
  ({ promise: queueNotEmpty, resolve: onQueueNotEmpty } = promiseWithResolvers());
22043
22229
  }
22044
22230
  }, (error) => {
22045
- if (!outOfBandError) {
22231
+ if (!hasOutOfBandError) {
22046
22232
  outOfBandError = error;
22233
+ hasOutOfBandError = true;
22047
22234
  onQueueNotEmpty();
22048
22235
  }
22049
22236
  });
@@ -22078,8 +22265,9 @@ var Mediabunny = (() => {
22078
22265
  decoderIsFlushed = true;
22079
22266
  onQueueNotEmpty();
22080
22267
  })().catch((error) => {
22081
- if (!outOfBandError) {
22268
+ if (!hasOutOfBandError) {
22082
22269
  outOfBandError = error;
22270
+ hasOutOfBandError = true;
22083
22271
  onQueueNotEmpty();
22084
22272
  }
22085
22273
  });
@@ -22098,7 +22286,7 @@ var Mediabunny = (() => {
22098
22286
  throw new InputDisposedError();
22099
22287
  } else if (terminated) {
22100
22288
  return { value: void 0, done: true };
22101
- } else if (outOfBandError) {
22289
+ } else if (hasOutOfBandError) {
22102
22290
  closeSamples();
22103
22291
  throw outOfBandError;
22104
22292
  } else if (sampleQueue.length > 0) {
@@ -22139,6 +22327,7 @@ var Mediabunny = (() => {
22139
22327
  let decoderIsFlushed = false;
22140
22328
  let terminated = false;
22141
22329
  let outOfBandError = null;
22330
+ let hasOutOfBandError = false;
22142
22331
  const pushToQueue = (sample) => {
22143
22332
  sampleQueue.push(sample);
22144
22333
  onQueueNotEmpty();
@@ -22169,8 +22358,9 @@ var Mediabunny = (() => {
22169
22358
  sample.close();
22170
22359
  }
22171
22360
  }, (error) => {
22172
- if (!outOfBandError) {
22361
+ if (!hasOutOfBandError) {
22173
22362
  outOfBandError = error;
22363
+ hasOutOfBandError = true;
22174
22364
  onQueueNotEmpty();
22175
22365
  }
22176
22366
  });
@@ -22240,8 +22430,9 @@ var Mediabunny = (() => {
22240
22430
  decoderIsFlushed = true;
22241
22431
  onQueueNotEmpty();
22242
22432
  })().catch((error) => {
22243
- if (!outOfBandError) {
22433
+ if (!hasOutOfBandError) {
22244
22434
  outOfBandError = error;
22435
+ hasOutOfBandError = true;
22245
22436
  onQueueNotEmpty();
22246
22437
  }
22247
22438
  });
@@ -22259,7 +22450,7 @@ var Mediabunny = (() => {
22259
22450
  throw new InputDisposedError();
22260
22451
  } else if (terminated) {
22261
22452
  return { value: void 0, done: true };
22262
- } else if (outOfBandError) {
22453
+ } else if (hasOutOfBandError) {
22263
22454
  closeSamples();
22264
22455
  throw outOfBandError;
22265
22456
  } else if (sampleQueue.length > 0) {
@@ -22324,7 +22515,8 @@ var Mediabunny = (() => {
22324
22515
  this.currentAlphaPacketIndex = 0;
22325
22516
  this.alphaRaslSkipped = false;
22326
22517
  // For HEVC stuff
22327
- this.frameHandlerSerializer = new CallSerializer();
22518
+ this.finalSamples = [];
22519
+ this.mergeAlphaPromises = [];
22328
22520
  const MatchingCustomDecoder = customVideoDecoders.find((x) => x.supports(codec, decoderConfig));
22329
22521
  if (MatchingCustomDecoder) {
22330
22522
  this.customDecoder = new MatchingCustomDecoder();
@@ -22336,18 +22528,19 @@ var Mediabunny = (() => {
22336
22528
  }
22337
22529
  this.finalizeAndEmitSample(sample);
22338
22530
  };
22339
- void this.customDecoderCallSerializer.call(() => this.customDecoder.init());
22531
+ this.customDecoder.onError = (error) => {
22532
+ onError(error);
22533
+ };
22534
+ void this.customDecoderCallSerializer.call(() => this.customDecoder.init()).catch((error) => onError(error));
22340
22535
  } else {
22341
22536
  const colorHandler = (frame) => {
22342
- this.frameHandlerSerializer.call(async () => {
22343
- if (this.alphaQueue.length > 0) {
22344
- const alphaFrame = this.alphaQueue.shift();
22345
- assert(alphaFrame !== void 0);
22346
- await this.mergeAlpha(frame, alphaFrame);
22347
- } else {
22348
- this.colorQueue.push(frame);
22349
- }
22350
- }).catch((error) => this.onError(error));
22537
+ if (this.alphaQueue.length > 0) {
22538
+ const alphaFrame = this.alphaQueue.shift();
22539
+ assert(alphaFrame !== void 0);
22540
+ void this.mergeAlpha(frame, alphaFrame);
22541
+ } else {
22542
+ this.colorQueue.push(frame);
22543
+ }
22351
22544
  };
22352
22545
  if (codec === "avc" && this.decoderConfig.description && isChromium()) {
22353
22546
  const record = deserializeAvcDecoderConfigurationRecord(toUint8Array(this.decoderConfig.description));
@@ -22398,7 +22591,7 @@ var Mediabunny = (() => {
22398
22591
  }
22399
22592
  if (this.customDecoder) {
22400
22593
  this.customDecoderQueueSize++;
22401
- void this.customDecoderCallSerializer.call(() => this.customDecoder.decode(packet)).then(() => this.customDecoderQueueSize--);
22594
+ void this.customDecoderCallSerializer.call(() => this.customDecoder.decode(packet)).catch((error) => this.onError(error)).finally(() => this.customDecoderQueueSize--);
22402
22595
  } else {
22403
22596
  assert(this.decoder);
22404
22597
  if (!isWebKit()) {
@@ -22440,27 +22633,25 @@ var Mediabunny = (() => {
22440
22633
  }
22441
22634
  if (!this.alphaDecoder) {
22442
22635
  const alphaHandler = (frame) => {
22443
- this.frameHandlerSerializer.call(async () => {
22636
+ if (this.colorQueue.length > 0) {
22637
+ const colorFrame = this.colorQueue.shift();
22638
+ assert(colorFrame !== void 0);
22639
+ void this.mergeAlpha(colorFrame, frame);
22640
+ } else {
22641
+ this.alphaQueue.push(frame);
22642
+ }
22643
+ this.decodedAlphaChunkCount++;
22644
+ while (this.nullAlphaFrameQueue.length > 0 && this.nullAlphaFrameQueue[0] === this.decodedAlphaChunkCount) {
22645
+ this.nullAlphaFrameQueue.shift();
22444
22646
  if (this.colorQueue.length > 0) {
22445
22647
  const colorFrame = this.colorQueue.shift();
22446
22648
  assert(colorFrame !== void 0);
22447
- await this.mergeAlpha(colorFrame, frame);
22649
+ void this.mergeAlpha(colorFrame, null);
22448
22650
  } else {
22449
- this.alphaQueue.push(frame);
22450
- }
22451
- this.decodedAlphaChunkCount++;
22452
- while (this.nullAlphaFrameQueue.length > 0 && this.nullAlphaFrameQueue[0] === this.decodedAlphaChunkCount) {
22453
- this.nullAlphaFrameQueue.shift();
22454
- if (this.colorQueue.length > 0) {
22455
- const colorFrame = this.colorQueue.shift();
22456
- assert(colorFrame !== void 0);
22457
- await this.mergeAlpha(colorFrame, null);
22458
- } else {
22459
- this.alphaQueue.push(null);
22460
- }
22651
+ this.alphaQueue.push(null);
22461
22652
  }
22462
- this.alphaDecoderQueueSize--;
22463
- }).catch((error) => this.onError(error));
22653
+ }
22654
+ this.alphaDecoderQueueSize--;
22464
22655
  };
22465
22656
  const stack = new Error("Decoding error").stack;
22466
22657
  this.alphaDecoder = new VideoDecoder({
@@ -22543,15 +22734,29 @@ var Mediabunny = (() => {
22543
22734
  this.onSample(sample);
22544
22735
  }
22545
22736
  async mergeAlpha(color, alpha) {
22546
- if (!alpha) {
22547
- const finalSample2 = new VideoSample(color);
22548
- this.sampleHandler(finalSample2);
22549
- return;
22737
+ const resolver = promiseWithResolvers();
22738
+ this.mergeAlphaPromises.push(resolver.promise);
22739
+ const result = { sample: null };
22740
+ this.finalSamples.push(result);
22741
+ try {
22742
+ if (!alpha) {
22743
+ result.sample = new VideoSample(color);
22744
+ } else {
22745
+ assert(this.merger);
22746
+ const finalFrame = await this.merger.merge(color, alpha);
22747
+ result.sample = new VideoSample(finalFrame);
22748
+ }
22749
+ while (this.finalSamples.length > 0 && this.finalSamples[0].sample !== null) {
22750
+ const next = this.finalSamples.shift();
22751
+ this.sampleHandler(next.sample);
22752
+ }
22753
+ } catch (error) {
22754
+ removeItem(this.finalSamples, result);
22755
+ this.onError(error);
22756
+ } finally {
22757
+ removeItem(this.mergeAlphaPromises, resolver.promise);
22758
+ resolver.resolve();
22550
22759
  }
22551
- assert(this.merger);
22552
- const finalFrame = await this.merger.update(color, alpha);
22553
- const finalSample = new VideoSample(finalFrame);
22554
- this.sampleHandler(finalSample);
22555
22760
  }
22556
22761
  async flush() {
22557
22762
  if (this.customDecoder) {
@@ -22562,7 +22767,7 @@ var Mediabunny = (() => {
22562
22767
  this.decoder.flush(),
22563
22768
  this.alphaDecoder?.flush()
22564
22769
  ]);
22565
- await this.frameHandlerSerializer.currentPromise;
22770
+ await Promise.all(this.mergeAlphaPromises);
22566
22771
  this.colorQueue.forEach((x) => x.close());
22567
22772
  this.colorQueue.length = 0;
22568
22773
  this.alphaQueue.forEach((x) => x?.close());
@@ -22602,205 +22807,62 @@ var Mediabunny = (() => {
22602
22807
  this.sampleQueue.length = 0;
22603
22808
  }
22604
22809
  };
22605
- var mergerGpuUnavailable = false;
22606
- var _ColorAlphaMerger = class _ColorAlphaMerger {
22810
+ var mergerWorkerUrl = null;
22811
+ var ColorAlphaMerger = class {
22607
22812
  constructor() {
22608
- this.canvas = null;
22609
- this.gl = null;
22610
- this.program = null;
22611
- this.vao = null;
22612
- this.colorTexture = null;
22613
- this.alphaTexture = null;
22614
- this.worker = null;
22813
+ this.workers = [];
22814
+ this.nextWorkerIndex = 0;
22615
22815
  this.pendingRequests = /* @__PURE__ */ new Map();
22616
22816
  this.nextRequestId = 0;
22617
- const canMakeCanvas = typeof OffscreenCanvas !== "undefined" || typeof document !== "undefined" && typeof document.createElement === "function";
22618
- if (!_ColorAlphaMerger.forceCpu && canMakeCanvas && !mergerGpuUnavailable) {
22619
- try {
22620
- if (typeof OffscreenCanvas !== "undefined") {
22621
- this.canvas = new OffscreenCanvas(300, 150);
22622
- } else {
22623
- this.canvas = document.createElement("canvas");
22624
- }
22625
- const gl = this.canvas.getContext("webgl2", {
22626
- premultipliedAlpha: false
22817
+ }
22818
+ merge(color, alpha) {
22819
+ if (this.workers.length === 0) {
22820
+ if (!mergerWorkerUrl) {
22821
+ const blob = new Blob(
22822
+ [`(${colorAlphaMergerWorkerCode.toString()})()`],
22823
+ { type: "application/javascript" }
22824
+ );
22825
+ mergerWorkerUrl = URL.createObjectURL(blob);
22826
+ }
22827
+ const poolSize = clamp(navigator.hardwareConcurrency, 1, 4);
22828
+ for (let i = 0; i < poolSize; i++) {
22829
+ const worker2 = new Worker(mergerWorkerUrl);
22830
+ worker2.addEventListener("message", (event) => {
22831
+ const data = event.data;
22832
+ const pending2 = this.pendingRequests.get(data.id);
22833
+ if (!pending2) {
22834
+ return;
22835
+ }
22836
+ this.pendingRequests.delete(data.id);
22837
+ if ("error" in data) {
22838
+ pending2.reject(new Error(data.error));
22839
+ } else {
22840
+ pending2.resolve(data.frame);
22841
+ }
22627
22842
  });
22628
- if (!gl) {
22629
- throw new Error("Couldn't acquire WebGL 2 context.");
22630
- }
22631
- this.gl = gl;
22632
- this.program = this.createProgram();
22633
- this.vao = this.createVAO();
22634
- this.colorTexture = this.createTexture();
22635
- this.alphaTexture = this.createTexture();
22636
- this.gl.useProgram(this.program);
22637
- this.gl.uniform1i(this.gl.getUniformLocation(this.program, "u_colorTexture"), 0);
22638
- this.gl.uniform1i(this.gl.getUniformLocation(this.program, "u_alphaTexture"), 1);
22639
- } catch (error) {
22640
- this.gl = null;
22641
- this.canvas = null;
22642
- mergerGpuUnavailable = true;
22643
- console.warn("Falling back to CPU for color/alpha merging.", error);
22843
+ worker2.addEventListener("error", (event) => {
22844
+ const error = new Error(event.message || "Color/alpha merge worker error.");
22845
+ for (const pending2 of this.pendingRequests.values()) {
22846
+ pending2.reject(error);
22847
+ }
22848
+ this.pendingRequests.clear();
22849
+ });
22850
+ this.workers.push(worker2);
22644
22851
  }
22645
22852
  }
22646
- }
22647
- async update(color, alpha) {
22648
- if (this.gl) {
22649
- return this.updateGpu(color, alpha);
22650
- } else {
22651
- return this.updateCpu(color, alpha);
22652
- }
22653
- }
22654
- createProgram() {
22655
- assert(this.gl);
22656
- const vertexShader = this.createShader(this.gl.VERTEX_SHADER, `#version 300 es
22657
- in vec2 a_position;
22658
- in vec2 a_texCoord;
22659
- out vec2 v_texCoord;
22660
-
22661
- void main() {
22662
- gl_Position = vec4(a_position, 0.0, 1.0);
22663
- v_texCoord = a_texCoord;
22664
- }
22665
- `);
22666
- const fragmentShader = this.createShader(this.gl.FRAGMENT_SHADER, `#version 300 es
22667
- precision highp float;
22668
-
22669
- uniform sampler2D u_colorTexture;
22670
- uniform sampler2D u_alphaTexture;
22671
- in vec2 v_texCoord;
22672
- out vec4 fragColor;
22673
-
22674
- void main() {
22675
- vec3 color = texture(u_colorTexture, v_texCoord).rgb;
22676
- float alpha = texture(u_alphaTexture, v_texCoord).r;
22677
- fragColor = vec4(color, alpha);
22678
- }
22679
- `);
22680
- const program = this.gl.createProgram();
22681
- this.gl.attachShader(program, vertexShader);
22682
- this.gl.attachShader(program, fragmentShader);
22683
- this.gl.linkProgram(program);
22684
- return program;
22685
- }
22686
- createShader(type, source) {
22687
- assert(this.gl);
22688
- const shader = this.gl.createShader(type);
22689
- this.gl.shaderSource(shader, source);
22690
- this.gl.compileShader(shader);
22691
- return shader;
22692
- }
22693
- createVAO() {
22694
- assert(this.gl);
22695
- assert(this.program);
22696
- const vao = this.gl.createVertexArray();
22697
- this.gl.bindVertexArray(vao);
22698
- const vertices = new Float32Array([
22699
- -1,
22700
- -1,
22701
- 0,
22702
- 1,
22703
- 1,
22704
- -1,
22705
- 1,
22706
- 1,
22707
- -1,
22708
- 1,
22709
- 0,
22710
- 0,
22711
- 1,
22712
- 1,
22713
- 1,
22714
- 0
22715
- ]);
22716
- const buffer = this.gl.createBuffer();
22717
- this.gl.bindBuffer(this.gl.ARRAY_BUFFER, buffer);
22718
- this.gl.bufferData(this.gl.ARRAY_BUFFER, vertices, this.gl.STATIC_DRAW);
22719
- const positionLocation = this.gl.getAttribLocation(this.program, "a_position");
22720
- const texCoordLocation = this.gl.getAttribLocation(this.program, "a_texCoord");
22721
- this.gl.enableVertexAttribArray(positionLocation);
22722
- this.gl.vertexAttribPointer(positionLocation, 2, this.gl.FLOAT, false, 16, 0);
22723
- this.gl.enableVertexAttribArray(texCoordLocation);
22724
- this.gl.vertexAttribPointer(texCoordLocation, 2, this.gl.FLOAT, false, 16, 8);
22725
- return vao;
22726
- }
22727
- createTexture() {
22728
- assert(this.gl);
22729
- const texture = this.gl.createTexture();
22730
- this.gl.bindTexture(this.gl.TEXTURE_2D, texture);
22731
- this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_WRAP_S, this.gl.CLAMP_TO_EDGE);
22732
- this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_WRAP_T, this.gl.CLAMP_TO_EDGE);
22733
- this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_MIN_FILTER, this.gl.LINEAR);
22734
- this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_MAG_FILTER, this.gl.LINEAR);
22735
- return texture;
22736
- }
22737
- updateGpu(color, alpha) {
22738
- assert(this.gl);
22739
- assert(this.canvas);
22740
- if (color.displayWidth !== this.canvas.width || color.displayHeight !== this.canvas.height) {
22741
- this.canvas.width = color.displayWidth;
22742
- this.canvas.height = color.displayHeight;
22743
- }
22744
- this.gl.activeTexture(this.gl.TEXTURE0);
22745
- this.gl.bindTexture(this.gl.TEXTURE_2D, this.colorTexture);
22746
- this.gl.texImage2D(this.gl.TEXTURE_2D, 0, this.gl.RGBA, this.gl.RGBA, this.gl.UNSIGNED_BYTE, color);
22747
- this.gl.activeTexture(this.gl.TEXTURE1);
22748
- this.gl.bindTexture(this.gl.TEXTURE_2D, this.alphaTexture);
22749
- this.gl.texImage2D(this.gl.TEXTURE_2D, 0, this.gl.RGBA, this.gl.RGBA, this.gl.UNSIGNED_BYTE, alpha);
22750
- this.gl.viewport(0, 0, this.canvas.width, this.canvas.height);
22751
- this.gl.clear(this.gl.COLOR_BUFFER_BIT);
22752
- this.gl.bindVertexArray(this.vao);
22753
- this.gl.drawArrays(this.gl.TRIANGLE_STRIP, 0, 4);
22754
- const finalFrame = new VideoFrame(this.canvas, {
22755
- timestamp: color.timestamp,
22756
- duration: color.duration ?? void 0
22757
- });
22758
- color.close();
22759
- alpha.close();
22760
- return finalFrame;
22761
- }
22762
- updateCpu(color, alpha) {
22763
- if (!this.worker) {
22764
- const blob = new Blob(
22765
- [`(${colorAlphaMergerWorkerCode.toString()})()`],
22766
- { type: "application/javascript" }
22767
- );
22768
- const url2 = URL.createObjectURL(blob);
22769
- this.worker = new Worker(url2);
22770
- URL.revokeObjectURL(url2);
22771
- this.worker.addEventListener("message", (event) => {
22772
- const data = event.data;
22773
- const pending2 = this.pendingRequests.get(data.id);
22774
- if (!pending2) {
22775
- return;
22776
- }
22777
- this.pendingRequests.delete(data.id);
22778
- if ("error" in data) {
22779
- pending2.reject(new Error(data.error));
22780
- } else {
22781
- pending2.resolve(data.frame);
22782
- }
22783
- });
22784
- this.worker.addEventListener("error", (event) => {
22785
- const error = new Error(event.message || "Color/alpha merge worker error.");
22786
- for (const pending2 of this.pendingRequests.values()) {
22787
- pending2.reject(error);
22788
- }
22789
- this.pendingRequests.clear();
22790
- });
22791
- }
22792
22853
  const id = this.nextRequestId++;
22793
22854
  const pending = promiseWithResolvers();
22794
22855
  this.pendingRequests.set(id, pending);
22795
- this.worker.postMessage({ id, color, alpha }, { transfer: [color, alpha] });
22856
+ const worker = this.workers[this.nextWorkerIndex];
22857
+ this.nextWorkerIndex = (this.nextWorkerIndex + 1) % this.workers.length;
22858
+ worker.postMessage({ id, color, alpha }, { transfer: [color, alpha] });
22796
22859
  return pending.promise;
22797
22860
  }
22798
22861
  close() {
22799
- this.gl?.getExtension("WEBGL_lose_context")?.loseContext();
22800
- this.gl = null;
22801
- this.canvas = null;
22802
- this.worker?.terminate();
22803
- this.worker = null;
22862
+ for (const worker of this.workers) {
22863
+ worker.terminate();
22864
+ }
22865
+ this.workers.length = 0;
22804
22866
  const error = new Error("Color/alpha merger closed.");
22805
22867
  for (const pending of this.pendingRequests.values()) {
22806
22868
  pending.reject(error);
@@ -22808,8 +22870,6 @@ var Mediabunny = (() => {
22808
22870
  this.pendingRequests.clear();
22809
22871
  }
22810
22872
  };
22811
- _ColorAlphaMerger.forceCpu = true;
22812
- var ColorAlphaMerger = _ColorAlphaMerger;
22813
22873
  var colorAlphaMergerWorkerCode = () => {
22814
22874
  let cpuAlphaBuffer = null;
22815
22875
  let cpuColorBuffer = null;
@@ -22843,18 +22903,18 @@ var Mediabunny = (() => {
22843
22903
  `CPU color/alpha merging requires the alpha frame to have the same bit depth as the color frame (color: '${format}', alpha: '${alphaFormat}').`
22844
22904
  );
22845
22905
  }
22846
- const width = color.codedWidth;
22847
- const height = color.codedHeight;
22848
22906
  if (format === "RGBX" || format === "RGBA" || format === "BGRX" || format === "BGRA") {
22849
- return await mergeInterleavedRgba(color, alpha, width, height, format);
22907
+ return await mergeInterleavedRgba(color, alpha, format);
22850
22908
  } else if (format === "I420" || format === "I420P10" || format === "I420P12" || format === "I422" || format === "I422P10" || format === "I422P12" || format === "I444" || format === "I444P10" || format === "I444P12") {
22851
- return await mergePlanarYuv(color, alpha, width, height, format);
22909
+ return await mergePlanarYuv(color, alpha, format);
22852
22910
  } else if (format === "NV12") {
22853
- return await mergeNv12(color, alpha, width, height);
22911
+ return await mergeNv12(color, alpha);
22854
22912
  }
22855
22913
  throw new Error(`CPU color/alpha merging does not support format '${format}'.`);
22856
22914
  };
22857
- const mergeInterleavedRgba = async (color, alpha, width, height, format) => {
22915
+ const mergeInterleavedRgba = async (color, alpha, format) => {
22916
+ const width = color.visibleRect?.width ?? color.codedWidth;
22917
+ const height = color.visibleRect?.height ?? color.codedHeight;
22858
22918
  const pixelCount = width * height;
22859
22919
  const output = new Uint8Array(pixelCount * 4);
22860
22920
  await color.copyTo(output);
@@ -22873,7 +22933,9 @@ var Mediabunny = (() => {
22873
22933
  };
22874
22934
  return new VideoFrame(output, init);
22875
22935
  };
22876
- const mergePlanarYuv = async (color, alpha, width, height, format) => {
22936
+ const mergePlanarYuv = async (color, alpha, format) => {
22937
+ const width = color.visibleRect?.width ?? color.codedWidth;
22938
+ const height = color.visibleRect?.height ?? color.codedHeight;
22877
22939
  const is10 = format.includes("P10");
22878
22940
  const is12 = format.includes("P12");
22879
22941
  const bytesPerSample = is10 || is12 ? 2 : 1;
@@ -22911,7 +22973,9 @@ var Mediabunny = (() => {
22911
22973
  };
22912
22974
  return new VideoFrame(output, init);
22913
22975
  };
22914
- const mergeNv12 = async (color, alpha, width, height) => {
22976
+ const mergeNv12 = async (color, alpha) => {
22977
+ const width = color.visibleRect?.width ?? color.codedWidth;
22978
+ const height = color.visibleRect?.height ?? color.codedHeight;
22915
22979
  const ySize = width * height;
22916
22980
  const chromaW = Math.ceil(width / 2);
22917
22981
  const chromaH = Math.ceil(height / 2);
@@ -23274,7 +23338,10 @@ var Mediabunny = (() => {
23274
23338
  }
23275
23339
  sampleHandler(sample);
23276
23340
  };
23277
- void this.customDecoderCallSerializer.call(() => this.customDecoder.init());
23341
+ this.customDecoder.onError = (error) => {
23342
+ onError(error);
23343
+ };
23344
+ void this.customDecoderCallSerializer.call(() => this.customDecoder.init()).catch((error) => onError(error));
23278
23345
  } else {
23279
23346
  const stack = new Error("Decoding error").stack;
23280
23347
  this.decoder = new AudioDecoder({
@@ -23304,7 +23371,7 @@ var Mediabunny = (() => {
23304
23371
  decode(packet) {
23305
23372
  if (this.customDecoder) {
23306
23373
  this.customDecoderQueueSize++;
23307
- void this.customDecoderCallSerializer.call(() => this.customDecoder.decode(packet)).then(() => this.customDecoderQueueSize--);
23374
+ void this.customDecoderCallSerializer.call(() => this.customDecoder.decode(packet)).catch((error) => this.onError(error)).finally(() => this.customDecoderQueueSize--);
23308
23375
  } else {
23309
23376
  assert(this.decoder);
23310
23377
  this.expectedFirstTimestamp ??= packet.timestamp;
@@ -24180,7 +24247,7 @@ var Mediabunny = (() => {
24180
24247
  const support = await VideoDecoder.isConfigSupported(decoderConfig);
24181
24248
  return support.supported === true;
24182
24249
  } catch (error) {
24183
- console.error("Error during decodability check:", error);
24250
+ Logging._error("Error during decodability check:", error);
24184
24251
  return false;
24185
24252
  }
24186
24253
  }
@@ -24282,7 +24349,7 @@ var Mediabunny = (() => {
24282
24349
  return support.supported === true;
24283
24350
  }
24284
24351
  } catch (error) {
24285
- console.error("Error during decodability check:", error);
24352
+ Logging._error("Error during decodability check:", error);
24286
24353
  return false;
24287
24354
  }
24288
24355
  }
@@ -25323,19 +25390,20 @@ var Mediabunny = (() => {
25323
25390
  slice.filePos = startPos;
25324
25391
  return null;
25325
25392
  }
25326
- const size = decodeSynchsafe(sizeRaw);
25393
+ let size = decodeSynchsafe(sizeRaw);
25394
+ if (flags & 16 /* Footer */) {
25395
+ size += ID3_V2_HEADER_SIZE;
25396
+ }
25327
25397
  return { majorVersion, revision, flags, size };
25328
25398
  };
25329
25399
  var parseId3V2Tag = (slice, header, tags) => {
25330
25400
  if (![2, 3, 4].includes(header.majorVersion)) {
25331
- console.warn(`Unsupported ID3v2 major version: ${header.majorVersion}`);
25401
+ Logging._warn(`Unsupported ID3v2 major version: ${header.majorVersion}`);
25332
25402
  return;
25333
25403
  }
25334
- const bytes2 = readBytes(slice, header.size);
25404
+ const dataSize = header.flags & 16 /* Footer */ ? header.size - ID3_V2_HEADER_SIZE : header.size;
25405
+ const bytes2 = readBytes(slice, dataSize);
25335
25406
  const reader = new Id3V2Reader(header, bytes2);
25336
- if (header.flags & 16 /* Footer */) {
25337
- reader.removeFooter();
25338
- }
25339
25407
  if (header.flags & 128 /* Unsynchronisation */ && header.majorVersion === 3) {
25340
25408
  reader.ununsynchronizeAll();
25341
25409
  }
@@ -25366,12 +25434,12 @@ var Mediabunny = (() => {
25366
25434
  frameUnsynchronized = !!(frame.flags & 1 << 1) || !!(header.flags & 128 /* Unsynchronisation */);
25367
25435
  }
25368
25436
  if (frameEncrypted) {
25369
- console.warn(`Skipping encrypted ID3v2 frame ${frame.id}`);
25437
+ Logging._warn(`Skipping encrypted ID3v2 frame ${frame.id}`);
25370
25438
  reader.pos = frameEndPos;
25371
25439
  continue;
25372
25440
  }
25373
25441
  if (frameCompressed) {
25374
- console.warn(`Skipping compressed ID3v2 frame ${frame.id}`);
25442
+ Logging._warn(`Skipping compressed ID3v2 frame ${frame.id}`);
25375
25443
  reader.pos = frameEndPos;
25376
25444
  continue;
25377
25445
  }
@@ -25607,10 +25675,6 @@ var Mediabunny = (() => {
25607
25675
  this.bytes.set(after, before.length + newBytes.length);
25608
25676
  this.view = new DataView(this.bytes.buffer);
25609
25677
  }
25610
- removeFooter() {
25611
- this.bytes = this.bytes.subarray(0, this.bytes.length - ID3_V2_HEADER_SIZE);
25612
- this.view = new DataView(this.bytes.buffer);
25613
- }
25614
25678
  readBytes(length) {
25615
25679
  const slice = this.bytes.subarray(this.pos, this.pos + length);
25616
25680
  this.pos += length;
@@ -27045,7 +27109,7 @@ var Mediabunny = (() => {
27045
27109
  i16(65535)
27046
27110
  // Pre-defined
27047
27111
  ], [
27048
- VIDEO_CODEC_TO_CONFIGURATION_BOX[trackData.track.source._codec](trackData),
27112
+ VIDEO_CODEC_TO_CONFIGURATION_BOX[trackData.track.source._codec]?.(trackData) ?? null,
27049
27113
  pasp(trackData),
27050
27114
  colorSpaceIsComplete(trackData.info.decoderConfig.colorSpace) ? colr(trackData) : null
27051
27115
  ]);
@@ -28040,6 +28104,8 @@ var Mediabunny = (() => {
28040
28104
  return "vp09";
28041
28105
  case "av1":
28042
28106
  return "av01";
28107
+ case "prores":
28108
+ return fullCodecString;
28043
28109
  }
28044
28110
  };
28045
28111
  var VIDEO_CODEC_TO_CONFIGURATION_BOX = {
@@ -28047,7 +28113,8 @@ var Mediabunny = (() => {
28047
28113
  hevc: hvcC,
28048
28114
  vp8: vpcC,
28049
28115
  vp9: vpcC,
28050
- av1: av1C
28116
+ av1: av1C,
28117
+ prores: null
28051
28118
  };
28052
28119
  var audioCodecToBoxName = (codec, isQuickTime) => {
28053
28120
  switch (codec) {
@@ -30004,6 +30071,7 @@ var Mediabunny = (() => {
30004
30071
  { id: 156 /* FlagLacing */, data: 0 },
30005
30072
  { id: 2274716 /* Language */, data: trackData.track.metadata.languageCode ?? UNDETERMINED_LANGUAGE },
30006
30073
  { id: 134 /* CodecID */, data: codecId },
30074
+ trackData.codecPrivate ? { id: 25506 /* CodecPrivate */, data: toUint8Array(trackData.codecPrivate) } : null,
30007
30075
  { id: 22186 /* CodecDelay */, data: 0 },
30008
30076
  { id: 22203 /* SeekPreRoll */, data: seekPreRollNs },
30009
30077
  trackData.track.metadata.name !== void 0 ? { id: 21358 /* Name */, data: new EBMLUnicodeString(trackData.track.metadata.name) } : null,
@@ -30016,10 +30084,6 @@ var Mediabunny = (() => {
30016
30084
  videoSpecificTrackInfo(trackData) {
30017
30085
  const { frameRate, rotation } = trackData.track.metadata;
30018
30086
  const elements = [
30019
- trackData.info.decoderConfig.description ? {
30020
- id: 25506 /* CodecPrivate */,
30021
- data: toUint8Array(trackData.info.decoderConfig.description)
30022
- } : null,
30023
30087
  frameRate ? {
30024
30088
  id: 2352003 /* DefaultDuration */,
30025
30089
  data: 1e9 / frameRate
@@ -30079,10 +30143,6 @@ var Mediabunny = (() => {
30079
30143
  audioSpecificTrackInfo(trackData) {
30080
30144
  const pcmInfo = PCM_AUDIO_CODECS.includes(trackData.track.source._codec) ? parsePcmCodec(trackData.track.source._codec) : null;
30081
30145
  return [
30082
- trackData.info.decoderConfig.description ? {
30083
- id: 25506 /* CodecPrivate */,
30084
- data: toUint8Array(trackData.info.decoderConfig.description)
30085
- } : null,
30086
30146
  { id: 225 /* Audio */, data: [
30087
30147
  { id: 181 /* SamplingFrequency */, data: new EBMLFloat32(trackData.info.sampleRate) },
30088
30148
  { id: 159 /* Channels */, data: trackData.info.numberOfChannels },
@@ -30090,10 +30150,9 @@ var Mediabunny = (() => {
30090
30150
  ] }
30091
30151
  ];
30092
30152
  }
30153
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
30093
30154
  subtitleSpecificTrackInfo(trackData) {
30094
- return [
30095
- { id: 25506 /* CodecPrivate */, data: textEncoder.encode(trackData.info.config.description) }
30096
- ];
30155
+ return [];
30097
30156
  }
30098
30157
  maybeCreateTags() {
30099
30158
  const simpleTags = [];
@@ -30382,22 +30441,19 @@ var Mediabunny = (() => {
30382
30441
  },
30383
30442
  chunkQueue: [],
30384
30443
  lastWrittenMsTimestamp: null,
30444
+ codecPrivate: meta.decoderConfig.description ?? null,
30385
30445
  closed: false
30386
30446
  };
30387
30447
  if (track.source._codec === "vp9") {
30388
- newTrackData.info.decoderConfig = {
30389
- ...newTrackData.info.decoderConfig,
30390
- description: new Uint8Array(
30391
- generateVp9CodecConfigurationFromCodecString(newTrackData.info.decoderConfig.codec)
30392
- )
30393
- };
30448
+ newTrackData.codecPrivate = new Uint8Array(
30449
+ generateVp9CodecConfigurationFromCodecString(newTrackData.info.decoderConfig.codec)
30450
+ );
30394
30451
  } else if (track.source._codec === "av1") {
30395
- newTrackData.info.decoderConfig = {
30396
- ...newTrackData.info.decoderConfig,
30397
- description: new Uint8Array(
30398
- generateAv1CodecConfigurationFromCodecString(newTrackData.info.decoderConfig.codec)
30399
- )
30400
- };
30452
+ newTrackData.codecPrivate = new Uint8Array(
30453
+ generateAv1CodecConfigurationFromCodecString(newTrackData.info.decoderConfig.codec)
30454
+ );
30455
+ } else if (track.source._codec === "prores") {
30456
+ newTrackData.codecPrivate = textEncoder.encode(meta.decoderConfig.codec);
30401
30457
  }
30402
30458
  this.trackDatas.push(newTrackData);
30403
30459
  this.trackDatas.sort((a, b) => a.track.id - b.track.id);
@@ -30446,6 +30502,7 @@ var Mediabunny = (() => {
30446
30502
  },
30447
30503
  chunkQueue: [],
30448
30504
  lastWrittenMsTimestamp: null,
30505
+ codecPrivate: decoderConfig.description ?? null,
30449
30506
  closed: false
30450
30507
  };
30451
30508
  this.trackDatas.push(newTrackData);
@@ -30471,6 +30528,7 @@ var Mediabunny = (() => {
30471
30528
  },
30472
30529
  chunkQueue: [],
30473
30530
  lastWrittenMsTimestamp: null,
30531
+ codecPrivate: textEncoder.encode(meta.config.description),
30474
30532
  closed: false
30475
30533
  };
30476
30534
  this.trackDatas.push(newTrackData);
@@ -32066,7 +32124,7 @@ ${cue.notes ?? ""}`;
32066
32124
  const writtenTags = /* @__PURE__ */ new Set();
32067
32125
  const writeInfoTag = (tag, value) => {
32068
32126
  if (!isIso88591Compatible(value)) {
32069
- console.warn(`Didn't write tag '${tag}' because '${value}' is not ISO 8859-1-compatible.`);
32127
+ Logging._warn(`Didn't write tag '${tag}' because '${value}' is not ISO 8859-1-compatible.`);
32070
32128
  return;
32071
32129
  }
32072
32130
  const size = value.length + 1;
@@ -32544,8 +32602,15 @@ ${cue.notes ?? ""}`;
32544
32602
  * So, we keep track of the encoder error and throw it as soon as we get the chance.
32545
32603
  */
32546
32604
  this.error = null;
32547
- this.closed = false;
32605
+ this.errorSet = false;
32548
32606
  this.lastMuxerPromise = Promise.resolve();
32607
+ this.closed = false;
32608
+ }
32609
+ setError(error) {
32610
+ if (!this.errorSet) {
32611
+ this.error = error;
32612
+ this.errorSet = true;
32613
+ }
32549
32614
  }
32550
32615
  async add(videoSample, shouldClose, encodeOptions) {
32551
32616
  const originalSample = videoSample;
@@ -32696,7 +32761,8 @@ ${cue.notes ?? ""}`;
32696
32761
  if (this.customEncoder) {
32697
32762
  this.customEncoderQueueSize++;
32698
32763
  const clonedSample = sampleToEncode.clone();
32699
- const promise = this.customEncoderCallSerializer.call(() => this.customEncoder.encode(clonedSample, finalEncodeOptions)).then(() => this.customEncoderQueueSize--).catch((error) => this.error ??= error).finally(() => {
32764
+ const promise = this.customEncoderCallSerializer.call(() => this.customEncoder.encode(clonedSample, finalEncodeOptions)).catch((error) => this.setError(error)).finally(() => {
32765
+ this.customEncoderQueueSize--;
32700
32766
  clonedSample.close();
32701
32767
  });
32702
32768
  if (this.customEncoderQueueSize >= 4) {
@@ -32740,12 +32806,10 @@ ${cue.notes ?? ""}`;
32740
32806
  this.encoder.encode(videoFrame, finalEncodeOptions);
32741
32807
  videoFrame.close();
32742
32808
  } else {
32743
- const width = videoFrame.displayWidth;
32744
- const height = videoFrame.displayHeight;
32745
32809
  if (!this.splitter) {
32746
- this.splitter = new ColorAlphaSplitter(width, height);
32810
+ this.splitter = new ColorAlphaSplitter();
32747
32811
  }
32748
- const { colorFrame, alphaFrame } = await this.splitter.update(videoFrame);
32812
+ const { colorFrame, alphaFrame } = await this.splitter.split(videoFrame);
32749
32813
  this.alphaFrameQueue.push(alphaFrame);
32750
32814
  this.encoder.encode(colorFrame, finalEncodeOptions);
32751
32815
  colorFrame.close();
@@ -32809,9 +32873,12 @@ ${cue.notes ?? ""}`;
32809
32873
  maybeEnsureIsKeyPacket(this.source._connectedTrack, packet);
32810
32874
  this.encodingConfig.onEncodedPacket?.(packet, meta);
32811
32875
  this.lastMuxerPromise = this.muxer.addEncodedVideoPacket(this.source._connectedTrack, packet, meta).catch((error) => {
32812
- this.error ??= error;
32876
+ this.setError(error);
32813
32877
  });
32814
32878
  };
32879
+ this.customEncoder.onError = (error) => {
32880
+ this.setError(error);
32881
+ };
32815
32882
  await this.customEncoder.init();
32816
32883
  } else {
32817
32884
  if (typeof VideoEncoder === "undefined") {
@@ -32869,7 +32936,7 @@ ${cue.notes ?? ""}`;
32869
32936
  maybeEnsureIsKeyPacket(this.source._connectedTrack, packet);
32870
32937
  this.encodingConfig.onEncodedPacket?.(packet, meta);
32871
32938
  this.lastMuxerPromise = this.muxer.addEncodedVideoPacket(this.source._connectedTrack, packet, meta).catch((error) => {
32872
- this.error ??= error;
32939
+ this.setError(error);
32873
32940
  });
32874
32941
  this.emittedEncoderPackets++;
32875
32942
  };
@@ -32903,7 +32970,7 @@ ${cue.notes ?? ""}`;
32903
32970
  },
32904
32971
  error: (error) => {
32905
32972
  error.stack = stack;
32906
- this.error ??= error;
32973
+ this.setError(error);
32907
32974
  }
32908
32975
  });
32909
32976
  this.encoder.configure(encoderConfig);
@@ -32927,7 +32994,7 @@ ${cue.notes ?? ""}`;
32927
32994
  },
32928
32995
  error: (error) => {
32929
32996
  error.stack = stack2;
32930
- this.error ??= error;
32997
+ this.setError(error);
32931
32998
  }
32932
32999
  });
32933
33000
  this.alphaEncoder.configure(encoderConfig);
@@ -32981,274 +33048,28 @@ ${cue.notes ?? ""}`;
32981
33048
  }
32982
33049
  }
32983
33050
  checkForEncoderError() {
32984
- if (this.error) {
33051
+ if (this.errorSet) {
32985
33052
  throw this.error;
32986
33053
  }
32987
33054
  }
32988
33055
  };
32989
- var splitterGpuUnavailable = false;
32990
- var _ColorAlphaSplitter = class _ColorAlphaSplitter {
32991
- constructor(initialWidth, initialHeight) {
32992
- this.canvas = null;
32993
- this.gl = null;
32994
- this.colorProgram = null;
32995
- this.alphaProgram = null;
32996
- this.vao = null;
32997
- this.sourceTexture = null;
32998
- this.alphaResolutionLocation = null;
33056
+ var splitterWorkerUrl = null;
33057
+ var ColorAlphaSplitter = class {
33058
+ constructor() {
32999
33059
  this.worker = null;
33000
33060
  this.pendingRequests = /* @__PURE__ */ new Map();
33001
33061
  this.nextRequestId = 0;
33002
- const canMakeCanvas = typeof OffscreenCanvas !== "undefined" || typeof document !== "undefined" && typeof document.createElement === "function";
33003
- if (!_ColorAlphaSplitter.forceCpu && canMakeCanvas && !splitterGpuUnavailable) {
33004
- try {
33005
- if (typeof OffscreenCanvas !== "undefined") {
33006
- this.canvas = new OffscreenCanvas(initialWidth, initialHeight);
33007
- } else {
33008
- this.canvas = document.createElement("canvas");
33009
- this.canvas.width = initialWidth;
33010
- this.canvas.height = initialHeight;
33011
- }
33012
- const gl = this.canvas.getContext("webgl2", {
33013
- alpha: true
33014
- // Needed due to the YUV thing we do for alpha
33015
- });
33016
- if (!gl) {
33017
- throw new Error("Couldn't acquire WebGL 2 context.");
33018
- }
33019
- this.gl = gl;
33020
- this.colorProgram = this.createColorProgram();
33021
- this.alphaProgram = this.createAlphaProgram();
33022
- this.vao = this.createVAO();
33023
- this.sourceTexture = this.createTexture();
33024
- this.alphaResolutionLocation = this.gl.getUniformLocation(this.alphaProgram, "u_resolution");
33025
- this.gl.useProgram(this.colorProgram);
33026
- this.gl.uniform1i(this.gl.getUniformLocation(this.colorProgram, "u_sourceTexture"), 0);
33027
- this.gl.useProgram(this.alphaProgram);
33028
- this.gl.uniform1i(this.gl.getUniformLocation(this.alphaProgram, "u_sourceTexture"), 0);
33029
- } catch (error) {
33030
- this.gl = null;
33031
- this.canvas = null;
33032
- splitterGpuUnavailable = true;
33033
- console.warn("Falling back to CPU for color/alpha splitting.", error);
33034
- }
33035
- }
33036
- }
33037
- async update(sourceFrame) {
33038
- if (this.gl) {
33039
- return this.updateGpu(sourceFrame);
33040
- } else {
33041
- return this.updateCpu(sourceFrame);
33042
- }
33043
- }
33044
- updateGpu(sourceFrame) {
33045
- assert(this.gl);
33046
- assert(this.canvas);
33047
- if (sourceFrame.displayWidth !== this.canvas.width || sourceFrame.displayHeight !== this.canvas.height) {
33048
- this.canvas.width = sourceFrame.displayWidth;
33049
- this.canvas.height = sourceFrame.displayHeight;
33050
- }
33051
- this.gl.activeTexture(this.gl.TEXTURE0);
33052
- this.gl.bindTexture(this.gl.TEXTURE_2D, this.sourceTexture);
33053
- this.gl.texImage2D(this.gl.TEXTURE_2D, 0, this.gl.RGBA, this.gl.RGBA, this.gl.UNSIGNED_BYTE, sourceFrame);
33054
- const colorFrame = this.runColorProgram(sourceFrame);
33055
- const alphaFrame = this.runAlphaProgram(sourceFrame);
33056
- sourceFrame.close();
33057
- return { colorFrame, alphaFrame };
33058
- }
33059
- createVertexShader() {
33060
- assert(this.gl);
33061
- return this.createShader(this.gl.VERTEX_SHADER, `#version 300 es
33062
- in vec2 a_position;
33063
- in vec2 a_texCoord;
33064
- out vec2 v_texCoord;
33065
-
33066
- void main() {
33067
- gl_Position = vec4(a_position, 0.0, 1.0);
33068
- v_texCoord = a_texCoord;
33069
- }
33070
- `);
33071
- }
33072
- createColorProgram() {
33073
- assert(this.gl);
33074
- const vertexShader = this.createVertexShader();
33075
- const fragmentShader = this.createShader(this.gl.FRAGMENT_SHADER, `#version 300 es
33076
- precision highp float;
33077
-
33078
- uniform sampler2D u_sourceTexture;
33079
- in vec2 v_texCoord;
33080
- out vec4 fragColor;
33081
-
33082
- void main() {
33083
- vec4 source = texture(u_sourceTexture, v_texCoord);
33084
- fragColor = vec4(source.rgb, 1.0);
33085
- }
33086
- `);
33087
- const program = this.gl.createProgram();
33088
- this.gl.attachShader(program, vertexShader);
33089
- this.gl.attachShader(program, fragmentShader);
33090
- this.gl.linkProgram(program);
33091
- return program;
33092
- }
33093
- createAlphaProgram() {
33094
- assert(this.gl);
33095
- const vertexShader = this.createVertexShader();
33096
- const fragmentShader = this.createShader(this.gl.FRAGMENT_SHADER, `#version 300 es
33097
- precision highp float;
33098
-
33099
- uniform sampler2D u_sourceTexture;
33100
- uniform vec2 u_resolution; // The width and height of the canvas
33101
- in vec2 v_texCoord;
33102
- out vec4 fragColor;
33103
-
33104
- // This function determines the value for a single byte in the YUV stream
33105
- float getByteValue(float byteOffset) {
33106
- float width = u_resolution.x;
33107
- float height = u_resolution.y;
33108
-
33109
- float yPlaneSize = width * height;
33110
-
33111
- if (byteOffset < yPlaneSize) {
33112
- // This byte is in the luma plane. Find the corresponding pixel coordinates to sample from
33113
- float y = floor(byteOffset / width);
33114
- float x = mod(byteOffset, width);
33115
-
33116
- // Add 0.5 to sample the center of the texel
33117
- vec2 sampleCoord = (vec2(x, y) + 0.5) / u_resolution;
33118
-
33119
- // The luma value is the alpha from the source texture
33120
- return texture(u_sourceTexture, sampleCoord).a;
33121
- } else {
33122
- // Write a fixed value for chroma and beyond
33123
- return 128.0 / 255.0;
33124
- }
33125
- }
33126
-
33127
- void main() {
33128
- // Each fragment writes 4 bytes (R, G, B, A)
33129
- float pixelIndex = floor(gl_FragCoord.y) * u_resolution.x + floor(gl_FragCoord.x);
33130
- float baseByteOffset = pixelIndex * 4.0;
33131
-
33132
- vec4 result;
33133
- for (int i = 0; i < 4; i++) {
33134
- float currentByteOffset = baseByteOffset + float(i);
33135
- result[i] = getByteValue(currentByteOffset);
33136
- }
33137
-
33138
- fragColor = result;
33139
- }
33140
- `);
33141
- const program = this.gl.createProgram();
33142
- this.gl.attachShader(program, vertexShader);
33143
- this.gl.attachShader(program, fragmentShader);
33144
- this.gl.linkProgram(program);
33145
- return program;
33146
- }
33147
- createShader(type, source) {
33148
- assert(this.gl);
33149
- const shader = this.gl.createShader(type);
33150
- this.gl.shaderSource(shader, source);
33151
- this.gl.compileShader(shader);
33152
- if (!this.gl.getShaderParameter(shader, this.gl.COMPILE_STATUS)) {
33153
- console.error("Shader compile error:", this.gl.getShaderInfoLog(shader));
33154
- }
33155
- return shader;
33156
- }
33157
- createVAO() {
33158
- assert(this.gl);
33159
- assert(this.colorProgram);
33160
- const vao = this.gl.createVertexArray();
33161
- this.gl.bindVertexArray(vao);
33162
- const vertices = new Float32Array([
33163
- -1,
33164
- -1,
33165
- 0,
33166
- 1,
33167
- 1,
33168
- -1,
33169
- 1,
33170
- 1,
33171
- -1,
33172
- 1,
33173
- 0,
33174
- 0,
33175
- 1,
33176
- 1,
33177
- 1,
33178
- 0
33179
- ]);
33180
- const buffer = this.gl.createBuffer();
33181
- this.gl.bindBuffer(this.gl.ARRAY_BUFFER, buffer);
33182
- this.gl.bufferData(this.gl.ARRAY_BUFFER, vertices, this.gl.STATIC_DRAW);
33183
- const positionLocation = this.gl.getAttribLocation(this.colorProgram, "a_position");
33184
- const texCoordLocation = this.gl.getAttribLocation(this.colorProgram, "a_texCoord");
33185
- this.gl.enableVertexAttribArray(positionLocation);
33186
- this.gl.vertexAttribPointer(positionLocation, 2, this.gl.FLOAT, false, 16, 0);
33187
- this.gl.enableVertexAttribArray(texCoordLocation);
33188
- this.gl.vertexAttribPointer(texCoordLocation, 2, this.gl.FLOAT, false, 16, 8);
33189
- return vao;
33190
- }
33191
- createTexture() {
33192
- assert(this.gl);
33193
- const texture = this.gl.createTexture();
33194
- this.gl.bindTexture(this.gl.TEXTURE_2D, texture);
33195
- this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_WRAP_S, this.gl.CLAMP_TO_EDGE);
33196
- this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_WRAP_T, this.gl.CLAMP_TO_EDGE);
33197
- this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_MIN_FILTER, this.gl.LINEAR);
33198
- this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_MAG_FILTER, this.gl.LINEAR);
33199
- return texture;
33200
- }
33201
- runColorProgram(sourceFrame) {
33202
- assert(this.gl);
33203
- assert(this.canvas);
33204
- this.gl.useProgram(this.colorProgram);
33205
- this.gl.viewport(0, 0, this.canvas.width, this.canvas.height);
33206
- this.gl.clear(this.gl.COLOR_BUFFER_BIT);
33207
- this.gl.bindVertexArray(this.vao);
33208
- this.gl.drawArrays(this.gl.TRIANGLE_STRIP, 0, 4);
33209
- return new VideoFrame(this.canvas, {
33210
- timestamp: sourceFrame.timestamp,
33211
- duration: sourceFrame.duration ?? void 0,
33212
- alpha: "discard"
33213
- });
33214
- }
33215
- runAlphaProgram(sourceFrame) {
33216
- assert(this.gl);
33217
- assert(this.canvas);
33218
- this.gl.useProgram(this.alphaProgram);
33219
- this.gl.uniform2f(this.alphaResolutionLocation, this.canvas.width, this.canvas.height);
33220
- this.gl.viewport(0, 0, this.canvas.width, this.canvas.height);
33221
- this.gl.clear(this.gl.COLOR_BUFFER_BIT);
33222
- this.gl.bindVertexArray(this.vao);
33223
- this.gl.drawArrays(this.gl.TRIANGLE_STRIP, 0, 4);
33224
- const { width, height } = this.canvas;
33225
- const chromaSamples = Math.ceil(width / 2) * Math.ceil(height / 2);
33226
- const yuvSize = width * height + chromaSamples * 2;
33227
- const requiredHeight = Math.ceil(yuvSize / (width * 4));
33228
- let yuv = new Uint8Array(4 * width * requiredHeight);
33229
- this.gl.readPixels(0, 0, width, requiredHeight, this.gl.RGBA, this.gl.UNSIGNED_BYTE, yuv);
33230
- yuv = yuv.subarray(0, yuvSize);
33231
- assert(yuv[width * height] === 128);
33232
- assert(yuv[yuv.length - 1] === 128);
33233
- const init = {
33234
- format: "I420",
33235
- codedWidth: width,
33236
- codedHeight: height,
33237
- timestamp: sourceFrame.timestamp,
33238
- duration: sourceFrame.duration ?? void 0,
33239
- transfer: [yuv.buffer]
33240
- };
33241
- return new VideoFrame(yuv, init);
33242
33062
  }
33243
- updateCpu(sourceFrame) {
33063
+ split(sourceFrame) {
33244
33064
  if (!this.worker) {
33245
- const blob = new Blob(
33246
- [`(${colorAlphaSplitterWorkerCode.toString()})()`],
33247
- { type: "application/javascript" }
33248
- );
33249
- const url2 = URL.createObjectURL(blob);
33250
- this.worker = new Worker(url2);
33251
- URL.revokeObjectURL(url2);
33065
+ if (!splitterWorkerUrl) {
33066
+ const blob = new Blob(
33067
+ [`(${colorAlphaSplitterWorkerCode.toString()})()`],
33068
+ { type: "application/javascript" }
33069
+ );
33070
+ splitterWorkerUrl = URL.createObjectURL(blob);
33071
+ }
33072
+ this.worker = new Worker(splitterWorkerUrl);
33252
33073
  this.worker.addEventListener("message", (event) => {
33253
33074
  const data = event.data;
33254
33075
  const pending2 = this.pendingRequests.get(data.id);
@@ -33277,9 +33098,6 @@ ${cue.notes ?? ""}`;
33277
33098
  return pending.promise;
33278
33099
  }
33279
33100
  close() {
33280
- this.gl?.getExtension("WEBGL_lose_context")?.loseContext();
33281
- this.gl = null;
33282
- this.canvas = null;
33283
33101
  this.worker?.terminate();
33284
33102
  this.worker = null;
33285
33103
  const error = new Error("Color/alpha splitter closed.");
@@ -33289,8 +33107,6 @@ ${cue.notes ?? ""}`;
33289
33107
  this.pendingRequests.clear();
33290
33108
  }
33291
33109
  };
33292
- _ColorAlphaSplitter.forceCpu = true;
33293
- var ColorAlphaSplitter = _ColorAlphaSplitter;
33294
33110
  var colorAlphaSplitterWorkerCode = () => {
33295
33111
  let cpuSourceBuffer = null;
33296
33112
  let chain = Promise.resolve();
@@ -33312,21 +33128,21 @@ ${cue.notes ?? ""}`;
33312
33128
  if (!format) {
33313
33129
  throw new Error("CPU color/alpha splitting requires a known VideoFrame format.");
33314
33130
  }
33315
- const width = sourceFrame.codedWidth;
33316
- const height = sourceFrame.codedHeight;
33317
33131
  const sourceSize = sourceFrame.allocationSize();
33318
33132
  if (!cpuSourceBuffer || cpuSourceBuffer.byteLength !== sourceSize) {
33319
33133
  cpuSourceBuffer = new Uint8Array(sourceSize);
33320
33134
  }
33321
33135
  await sourceFrame.copyTo(cpuSourceBuffer);
33322
33136
  if (format === "RGBA" || format === "BGRA") {
33323
- return splitInterleavedRgba(cpuSourceBuffer, width, height, format, sourceFrame);
33137
+ return splitInterleavedRgba(cpuSourceBuffer, format, sourceFrame);
33324
33138
  } else if (format === "I420A" || format === "I420AP10" || format === "I420AP12" || format === "I422A" || format === "I422AP10" || format === "I422AP12" || format === "I444A" || format === "I444AP10" || format === "I444AP12") {
33325
- return splitPlanarYuvA(cpuSourceBuffer, width, height, format, sourceFrame);
33139
+ return splitPlanarYuvA(cpuSourceBuffer, format, sourceFrame);
33326
33140
  }
33327
33141
  throw new Error(`CPU color/alpha splitting does not support format '${format}'.`);
33328
33142
  };
33329
- const splitInterleavedRgba = (source, width, height, format, sourceFrame) => {
33143
+ const splitInterleavedRgba = (source, format, sourceFrame) => {
33144
+ const width = sourceFrame.visibleRect?.width ?? sourceFrame.codedWidth;
33145
+ const height = sourceFrame.visibleRect?.height ?? sourceFrame.codedHeight;
33330
33146
  const pixelCount = width * height;
33331
33147
  const chromaW = Math.ceil(width / 2);
33332
33148
  const chromaH = Math.ceil(height / 2);
@@ -33355,7 +33171,9 @@ ${cue.notes ?? ""}`;
33355
33171
  const alphaFrame = new VideoFrame(alphaBuffer, alphaInit);
33356
33172
  return { colorFrame, alphaFrame };
33357
33173
  };
33358
- const splitPlanarYuvA = (source, width, height, format, sourceFrame) => {
33174
+ const splitPlanarYuvA = (source, format, sourceFrame) => {
33175
+ const width = sourceFrame.visibleRect?.width ?? sourceFrame.codedWidth;
33176
+ const height = sourceFrame.visibleRect?.height ?? sourceFrame.codedHeight;
33359
33177
  const is10 = format.includes("P10");
33360
33178
  const is12 = format.includes("P12");
33361
33179
  const bytesPerSample = is10 || is12 ? 2 : 1;
@@ -33540,7 +33358,7 @@ ${cue.notes ?? ""}`;
33540
33358
  /** @internal */
33541
33359
  async _start() {
33542
33360
  if (!this._errorPromiseAccessed) {
33543
- console.warn(
33361
+ Logging._warn(
33544
33362
  "Make sure not to ignore the `errorPromise` field on MediaStreamVideoTrackSource, so that any internal errors get bubbled up properly."
33545
33363
  );
33546
33364
  }
@@ -33820,9 +33638,16 @@ ${cue.notes ?? ""}`;
33820
33638
  * So, we keep track of the encoder error and throw it as soon as we get the chance.
33821
33639
  */
33822
33640
  this.error = null;
33641
+ this.errorSet = false;
33823
33642
  this.lastMuxerPromise = Promise.resolve();
33824
33643
  this.closed = false;
33825
33644
  }
33645
+ setError(error) {
33646
+ if (!this.errorSet) {
33647
+ this.error = error;
33648
+ this.errorSet = true;
33649
+ }
33650
+ }
33826
33651
  async add(audioSample, shouldClose) {
33827
33652
  try {
33828
33653
  this.checkForEncoderError();
@@ -33944,7 +33769,8 @@ ${cue.notes ?? ""}`;
33944
33769
  if (this.customEncoder) {
33945
33770
  this.customEncoderQueueSize++;
33946
33771
  const clonedSample = audioSample.clone();
33947
- const promise = this.customEncoderCallSerializer.call(() => this.customEncoder.encode(clonedSample)).then(() => this.customEncoderQueueSize--).catch((error) => this.error ??= error).finally(() => {
33772
+ const promise = this.customEncoderCallSerializer.call(() => this.customEncoder.encode(clonedSample)).catch((error) => this.setError(error)).finally(() => {
33773
+ this.customEncoderQueueSize--;
33948
33774
  clonedSample.close();
33949
33775
  });
33950
33776
  if (this.customEncoderQueueSize >= 4) {
@@ -34050,9 +33876,12 @@ ${cue.notes ?? ""}`;
34050
33876
  }
34051
33877
  this.encodingConfig.onEncodedPacket?.(packet, meta);
34052
33878
  this.lastMuxerPromise = this.muxer.addEncodedAudioPacket(this.source._connectedTrack, packet, meta).catch((error) => {
34053
- this.error ??= error;
33879
+ this.setError(error);
34054
33880
  });
34055
33881
  };
33882
+ this.customEncoder.onError = (error) => {
33883
+ this.setError(error);
33884
+ };
34056
33885
  await this.customEncoder.init();
34057
33886
  } else if (PCM_AUDIO_CODECS.includes(this.encodingConfig.codec)) {
34058
33887
  this.initPcmEncoder();
@@ -34095,12 +33924,12 @@ ${cue.notes ?? ""}`;
34095
33924
  });
34096
33925
  this.encodingConfig.onEncodedPacket?.(packet, meta);
34097
33926
  this.lastMuxerPromise = this.muxer.addEncodedAudioPacket(this.source._connectedTrack, packet, meta).catch((error) => {
34098
- this.error ??= error;
33927
+ this.setError(error);
34099
33928
  });
34100
33929
  },
34101
33930
  error: (error) => {
34102
33931
  error.stack = stack;
34103
- this.error ??= error;
33932
+ this.setError(error);
34104
33933
  }
34105
33934
  });
34106
33935
  this.encoder.configure(encoderConfig);
@@ -34241,7 +34070,7 @@ ${cue.notes ?? ""}`;
34241
34070
  }
34242
34071
  }
34243
34072
  checkForEncoderError() {
34244
- if (this.error) {
34073
+ if (this.errorSet) {
34245
34074
  throw this.error;
34246
34075
  }
34247
34076
  }
@@ -34357,7 +34186,7 @@ ${cue.notes ?? ""}`;
34357
34186
  /** @internal */
34358
34187
  async _start() {
34359
34188
  if (!this._errorPromiseAccessed) {
34360
- console.warn(
34189
+ Logging._warn(
34361
34190
  "Make sure not to ignore the `errorPromise` field on MediaStreamAudioTrackSource, so that any internal errors get bubbled up properly."
34362
34191
  );
34363
34192
  }
@@ -34601,12 +34430,14 @@ ${cue.notes ?? ""}`;
34601
34430
  /** @internal */
34602
34431
  this._error = null;
34603
34432
  /** @internal */
34433
+ this._errorSet = false;
34434
+ /** @internal */
34604
34435
  this._lastMuxerPromise = Promise.resolve();
34605
34436
  this._parser = new SubtitleParser({
34606
34437
  codec,
34607
34438
  output: (cue, metadata) => {
34608
34439
  this._lastMuxerPromise = this._connectedTrack.output._muxer.addSubtitleCue(this._connectedTrack, cue, metadata).catch((error) => {
34609
- this._error ??= error;
34440
+ this._setError(error);
34610
34441
  });
34611
34442
  }
34612
34443
  });
@@ -34628,8 +34459,15 @@ ${cue.notes ?? ""}`;
34628
34459
  return this._lastMuxerPromise;
34629
34460
  }
34630
34461
  /** @internal */
34462
+ _setError(error) {
34463
+ if (!this._errorSet) {
34464
+ this._error = error;
34465
+ this._errorSet = true;
34466
+ }
34467
+ }
34468
+ /** @internal */
34631
34469
  _checkForError() {
34632
- if (this._error) {
34470
+ if (this._errorSet) {
34633
34471
  throw this._error;
34634
34472
  }
34635
34473
  }
@@ -34692,7 +34530,7 @@ ${cue.notes ?? ""}`;
34692
34530
  }
34693
34531
  if (track.type === otherTrack.type) {
34694
34532
  if (!illegalPairingDetected) {
34695
- console.warn(
34533
+ Logging._warn(
34696
34534
  `Illegal pairing of two ${track.type} tracks detected, which is not possible in HLS; treating them as unpaired.`
34697
34535
  );
34698
34536
  illegalPairingDetected = true;
@@ -34701,7 +34539,7 @@ ${cue.notes ?? ""}`;
34701
34539
  }
34702
34540
  if (track.isVideoTrack() && track.metadata.hasOnlyKeyPackets || otherTrack.isVideoTrack() && otherTrack.metadata.hasOnlyKeyPackets) {
34703
34541
  if (!keyPacketsOnlyPairingWarned) {
34704
- console.warn(
34542
+ Logging._warn(
34705
34543
  `A key-packets-only video track is pairable with another track, which is not possible in HLS; treating them as unpaired.`
34706
34544
  );
34707
34545
  keyPacketsOnlyPairingWarned = true;
@@ -35555,7 +35393,7 @@ ${cue.notes ?? ""}`;
35555
35393
  groupIdTrackCount++;
35556
35394
  masterPlaylistText += `#EXT-X-MEDIA:TYPE=${type.toUpperCase()},GROUP-ID="${decl.groupId}"`;
35557
35395
  if (name !== null && /[\n\r"]/.test(name)) {
35558
- console.warn(
35396
+ Logging._warn(
35559
35397
  "Dropping track name since it includes a line feed, carriage return, or double quote character, which are not allowed in HLS playlist attributes."
35560
35398
  );
35561
35399
  name = null;
@@ -36789,7 +36627,7 @@ ${cue.notes ?? ""}`;
36789
36627
  throw new Error("Output has been canceled.");
36790
36628
  }
36791
36629
  if (this._startPromise) {
36792
- console.warn("Output has already been started.");
36630
+ Logging._warn("Output has already been started.");
36793
36631
  return this._startPromise;
36794
36632
  }
36795
36633
  return this._startPromise = (async () => {
@@ -36820,11 +36658,11 @@ ${cue.notes ?? ""}`;
36820
36658
  */
36821
36659
  async cancel() {
36822
36660
  if (this._cancelPromise) {
36823
- console.warn("Output has already been canceled.");
36661
+ Logging._warn("Output has already been canceled.");
36824
36662
  return this._cancelPromise;
36825
36663
  } else if (this.state === "finalizing" || this.state === "finalized") {
36826
36664
  if (this.state === "finalized") {
36827
- console.warn("Output has already been finalized.");
36665
+ Logging._warn("Output has already been finalized.");
36828
36666
  }
36829
36667
  return;
36830
36668
  }
@@ -36853,7 +36691,7 @@ ${cue.notes ?? ""}`;
36853
36691
  throw new Error("Cannot finalize after canceling.");
36854
36692
  }
36855
36693
  if (this._finalizePromise) {
36856
- console.warn("Output has already been finalized.");
36694
+ Logging._warn("Output has already been finalized.");
36857
36695
  return this._finalizePromise;
36858
36696
  }
36859
36697
  return this._finalizePromise = (async () => {
@@ -37301,7 +37139,7 @@ ${cue.notes ?? ""}`;
37301
37139
  warnElements.push(this._getInvalidityExplanation().join(""));
37302
37140
  }
37303
37141
  if (warnElements.length > 0) {
37304
- console.warn(...warnElements);
37142
+ Logging._warn(...warnElements);
37305
37143
  }
37306
37144
  }
37307
37145
  }
@@ -37434,7 +37272,7 @@ The @mediabunny/mp3-encoder extension package provides support for encoding MP3.
37434
37272
  return;
37435
37273
  }
37436
37274
  if (this._canceled) {
37437
- console.warn("Conversion already canceled.");
37275
+ Logging._warn("Conversion already canceled.");
37438
37276
  return;
37439
37277
  }
37440
37278
  this._canceled = true;
@@ -37565,7 +37403,7 @@ The @mediabunny/mp3-encoder extension package provides support for encoding MP3.
37565
37403
  firstSample.close();
37566
37404
  await tempOutput.finalize();
37567
37405
  } catch (error) {
37568
- console.info("Error when probing encoder support. Falling back to rerender path.", error);
37406
+ Logging._info("Error when probing encoder support. Falling back to rerender path.", error);
37569
37407
  needsRerender = true;
37570
37408
  void tempOutput.cancel();
37571
37409
  }
@@ -37655,7 +37493,7 @@ The @mediabunny/mp3-encoder extension package provides support for encoding MP3.
37655
37493
  let numberOfChannels = trackOptions.numberOfChannels ?? originalNumberOfChannels;
37656
37494
  let sampleRate = trackOptions.sampleRate ?? originalSampleRate;
37657
37495
  const needsTrimming = firstTimestamp < this._startTimestamp;
37658
- const needsPadding = firstTimestamp > this._startTimestamp && !this.output.format.supportsTimestampedMediaData;
37496
+ let needsPadding = firstTimestamp > this._startTimestamp && !this.output.format.supportsTimestampedMediaData;
37659
37497
  let audioCodecs = this.output.format.getSupportedAudioCodecs();
37660
37498
  if (!trackOptions.forceTranscode && !trackOptions.bitrate && numberOfChannels === originalNumberOfChannels && sampleRate === originalSampleRate && !needsTrimming && !needsPadding && audioCodecs.includes(sourceCodec) && (!trackOptions.codec || trackOptions.codec === sourceCodec) && !trackOptions.process && trackOptions.sampleFormat === void 0) {
37661
37499
  const source = new EncodedAudioPacketSource(sourceCodec);
@@ -37751,24 +37589,32 @@ The @mediabunny/mp3-encoder extension package provides support for encoding MP3.
37751
37589
  audioSource = source;
37752
37590
  this._trackPromises.push((async () => {
37753
37591
  await this._started;
37754
- if (needsPadding) {
37755
- const paddingLength = firstTimestamp - this._startTimestamp;
37756
- const paddingLengthSamples = Math.round(paddingLength * originalSampleRate);
37757
- const silentSample = new AudioSample({
37758
- data: new Float32Array(paddingLengthSamples * originalNumberOfChannels),
37759
- format: "f32-planar",
37760
- numberOfChannels: originalNumberOfChannels,
37761
- sampleRate: originalSampleRate,
37762
- timestamp: 0
37763
- });
37764
- await this._registerAudioSample(silentSample, source, outputTrackId, () => lastSampleTimestamp);
37765
- }
37766
37592
  const sink = new AudioSampleSink(track);
37767
37593
  for await (let sample of sink.samples(this._startTimestamp, this._endTimestamp)) {
37768
37594
  if (this._canceled) {
37769
37595
  sample.close();
37770
37596
  return;
37771
37597
  }
37598
+ if (needsPadding) {
37599
+ const paddingLength = firstTimestamp - this._startTimestamp;
37600
+ const paddingLengthSamples = Math.round(paddingLength * originalSampleRate);
37601
+ const bytesPerSample = getBytesPerSample(sample.format);
37602
+ const data = new Uint8Array(bytesPerSample * paddingLengthSamples * originalNumberOfChannels);
37603
+ if (sample.format === "u8" || sample.format === "u8-planar") {
37604
+ data.fill(2 ** 7);
37605
+ }
37606
+ const silentSample = new AudioSample({
37607
+ data,
37608
+ // Use the same format the decoder is spitting out. This avoids feeding changing sample
37609
+ // formats to the audio encoder.
37610
+ format: sample.format,
37611
+ numberOfChannels: originalNumberOfChannels,
37612
+ sampleRate: originalSampleRate,
37613
+ timestamp: 0
37614
+ });
37615
+ await this._registerAudioSample(silentSample, source, outputTrackId, () => lastSampleTimestamp);
37616
+ needsPadding = false;
37617
+ }
37772
37618
  let startFrame = 0;
37773
37619
  let endFrame = sample.numberOfFrames;
37774
37620
  if (sample.timestamp < this._startTimestamp) {
@@ -37897,7 +37743,7 @@ The @mediabunny/mp3-encoder extension package provides support for encoding MP3.
37897
37743
  // src/index.ts
37898
37744
  var MEDIABUNNY_LOADED_SYMBOL = Symbol.for("mediabunny loaded");
37899
37745
  if (globalThis[MEDIABUNNY_LOADED_SYMBOL]) {
37900
- console.error(
37746
+ Logging._error(
37901
37747
  "[WARNING]\nMediabunny was loaded twice. This will likely cause Mediabunny not to work correctly. Check if multiple dependencies are importing different versions of Mediabunny, or if something is being bundled incorrectly."
37902
37748
  );
37903
37749
  }