mediabunny 1.45.4 → 1.45.5

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 (34) hide show
  1. package/dist/bundles/mediabunny.cjs +132 -33
  2. package/dist/bundles/mediabunny.min.cjs +11 -11
  3. package/dist/bundles/mediabunny.min.mjs +11 -11
  4. package/dist/bundles/mediabunny.mjs +132 -33
  5. package/dist/bundles/mediabunny.node.cjs +132 -33
  6. package/dist/mediabunny.d.ts +5 -5
  7. package/dist/modules/src/input-track.js +1 -1
  8. package/dist/modules/src/isobmff/isobmff-boxes.d.ts.map +1 -1
  9. package/dist/modules/src/isobmff/isobmff-boxes.js +12 -8
  10. package/dist/modules/src/isobmff/isobmff-demuxer.d.ts.map +1 -1
  11. package/dist/modules/src/isobmff/isobmff-demuxer.js +6 -3
  12. package/dist/modules/src/isobmff/isobmff-muxer.d.ts +1 -0
  13. package/dist/modules/src/isobmff/isobmff-muxer.d.ts.map +1 -1
  14. package/dist/modules/src/isobmff/isobmff-muxer.js +22 -0
  15. package/dist/modules/src/media-sink.d.ts.map +1 -1
  16. package/dist/modules/src/media-sink.js +7 -1
  17. package/dist/modules/src/misc.d.ts +2 -0
  18. package/dist/modules/src/misc.d.ts.map +1 -1
  19. package/dist/modules/src/misc.js +1 -1
  20. package/dist/modules/src/reader.d.ts.map +1 -1
  21. package/dist/modules/src/reader.js +4 -4
  22. package/dist/modules/src/source.d.ts +5 -5
  23. package/dist/modules/src/source.d.ts.map +1 -1
  24. package/dist/modules/src/source.js +87 -18
  25. package/dist/modules/src/tsconfig.tsbuildinfo +1 -1
  26. package/package.json +1 -1
  27. package/src/input-track.ts +1 -1
  28. package/src/isobmff/isobmff-boxes.ts +12 -8
  29. package/src/isobmff/isobmff-demuxer.ts +7 -3
  30. package/src/isobmff/isobmff-muxer.ts +28 -1
  31. package/src/media-sink.ts +9 -0
  32. package/src/misc.ts +1 -1
  33. package/src/reader.ts +5 -4
  34. package/src/source.ts +112 -20
@@ -6353,18 +6353,21 @@ var Mediabunny = (() => {
6353
6353
  }
6354
6354
  assert(track.info?.type === "video");
6355
6355
  const colourType = readAscii(slice, 4);
6356
- if (colourType !== "nclx") {
6356
+ if (colourType !== "nclx" && colourType !== "nclc") {
6357
6357
  break;
6358
6358
  }
6359
6359
  const colourPrimaries = readU16Be(slice);
6360
6360
  const transferCharacteristics = readU16Be(slice);
6361
6361
  const matrixCoefficients = readU16Be(slice);
6362
- const fullRangeFlag = Boolean(readU8(slice) & 128);
6362
+ let fullRange = void 0;
6363
+ if (colourType === "nclx") {
6364
+ fullRange = Boolean(readU8(slice) & 128);
6365
+ }
6363
6366
  track.info.colorSpace = {
6364
6367
  primaries: COLOR_PRIMARIES_MAP_INVERSE[colourPrimaries],
6365
6368
  transfer: TRANSFER_CHARACTERISTICS_MAP_INVERSE[transferCharacteristics],
6366
6369
  matrix: MATRIX_COEFFICIENTS_MAP_INVERSE[matrixCoefficients],
6367
- fullRange: fullRangeFlag
6370
+ fullRange
6368
6371
  };
6369
6372
  }
6370
6373
  ;
@@ -15956,8 +15959,8 @@ var Mediabunny = (() => {
15956
15959
  /**
15957
15960
  * Creates a new {@link UrlSource} backed by the resource at the specified URL.
15958
15961
  *
15959
- * When passing a `Request` instance, note that the `signal` and `headers.Range` options will be overridden by
15960
- * Mediabunny. If you want to cancel ongoing requests, use {@link Input.dispose}.
15962
+ * When passing a `Request` instance, note that its `signal` will be overridden by Mediabunny; if you want to cancel
15963
+ * ongoing requests, use {@link Input.dispose}.
15961
15964
  */
15962
15965
  constructor(url2, options = {}) {
15963
15966
  if (typeof url2 !== "string" && !(url2 instanceof URL) && !(typeof Request !== "undefined" && url2 instanceof Request)) {
@@ -15983,6 +15986,10 @@ var Mediabunny = (() => {
15983
15986
  }
15984
15987
  const urlString = url2 instanceof Request ? url2.url : url2 instanceof URL ? url2.href : url2;
15985
15988
  super(urlString, (request) => new _UrlSource(request.path, this._options));
15989
+ /** @internal */
15990
+ this._offset = 0;
15991
+ /** @internal */
15992
+ this._length = null;
15986
15993
  /**
15987
15994
  * Note that this value being true does NOT mean the file size can't change anymore; it just signals that we have at
15988
15995
  * least checked if we know the file size or not.
@@ -15992,6 +15999,33 @@ var Mediabunny = (() => {
15992
15999
  this._url = url2;
15993
16000
  this._options = options;
15994
16001
  this._getRetryDelay = options.getRetryDelay ?? DEFAULT_RETRY_DELAY;
16002
+ this._requestInit = { ...options.requestInit };
16003
+ let rangeHeaderValue = null;
16004
+ if (options.requestInit?.headers) {
16005
+ const headers = { ...normalizeHeaders(options.requestInit.headers) };
16006
+ const rangeKey = Object.keys(headers).find((key) => key.toLowerCase() === "range");
16007
+ if (rangeKey !== void 0) {
16008
+ rangeHeaderValue = headers[rangeKey];
16009
+ delete headers[rangeKey];
16010
+ this._requestInit.headers = headers;
16011
+ }
16012
+ }
16013
+ if (url2 instanceof Request) {
16014
+ const requestRange = url2.headers.get("Range");
16015
+ if (requestRange !== null) {
16016
+ rangeHeaderValue ??= requestRange;
16017
+ const strippedRequest = new Request(url2);
16018
+ strippedRequest.headers.delete("Range");
16019
+ this._url = strippedRequest;
16020
+ }
16021
+ }
16022
+ if (rangeHeaderValue !== null) {
16023
+ const parsed = parseByteRangeHeader(rangeHeaderValue);
16024
+ if (parsed) {
16025
+ this._offset = parsed.offset;
16026
+ this._length = parsed.length;
16027
+ }
16028
+ }
15995
16029
  const DEFAULT_PARALLELISM = 2;
15996
16030
  this._orchestrator = new ReadOrchestrator({
15997
16031
  maxCacheSize: options.maxCacheSize ?? 64 * 2 ** 20,
@@ -16002,11 +16036,39 @@ var Mediabunny = (() => {
16002
16036
  }
16003
16037
  /** @internal */
16004
16038
  _getFileSize() {
16005
- return this._fileSizeDetermined ? this._orchestrator.fileSize : void 0;
16039
+ if (!this._fileSizeDetermined) {
16040
+ return this._length !== null ? this._length : void 0;
16041
+ }
16042
+ const baseSize = this._orchestrator.fileSize;
16043
+ if (baseSize === null) {
16044
+ return this._length !== null ? this._length : null;
16045
+ }
16046
+ return clamp(baseSize - this._offset, 0, this._length ?? Infinity);
16006
16047
  }
16007
16048
  /** @internal */
16008
16049
  _read(start, end, minReadPosition, maxReadPosition) {
16009
- return this._orchestrator.read(start, end, minReadPosition, maxReadPosition);
16050
+ if (this._length !== null && end > this._length) {
16051
+ return null;
16052
+ }
16053
+ const offset = this._offset;
16054
+ const result = this._orchestrator.read(
16055
+ offset + start,
16056
+ offset + end,
16057
+ Math.max(offset + minReadPosition, offset),
16058
+ offset + Math.min(maxReadPosition, this._length ?? Infinity)
16059
+ );
16060
+ const processResult = (result2) => {
16061
+ if (!result2) {
16062
+ return null;
16063
+ }
16064
+ result2.offset -= this._offset;
16065
+ return result2;
16066
+ };
16067
+ if (result instanceof Promise) {
16068
+ return result.then(processResult);
16069
+ } else {
16070
+ return processResult(result);
16071
+ }
16010
16072
  }
16011
16073
  /** @internal */
16012
16074
  async _runWorker(worker) {
@@ -16015,7 +16077,7 @@ var Mediabunny = (() => {
16015
16077
  const response = await retriedFetch(
16016
16078
  this._options.fetchFn ?? fetch,
16017
16079
  this._url,
16018
- mergeRequestInit(this._options.requestInit ?? {}, {
16080
+ mergeRequestInit(this._requestInit, {
16019
16081
  headers: {
16020
16082
  // Always sending a range request is a good way to probe if the server supports them
16021
16083
  Range: `bytes=${worker.currentPos}-`
@@ -16124,6 +16186,22 @@ var Mediabunny = (() => {
16124
16186
  this._orchestrator.dispose();
16125
16187
  }
16126
16188
  };
16189
+ var BYTE_RANGE_REGEX = /^bytes=(\d+)-(\d*)$/;
16190
+ var parseByteRangeHeader = (value) => {
16191
+ const match = BYTE_RANGE_REGEX.exec(value.trim());
16192
+ if (!match) {
16193
+ return null;
16194
+ }
16195
+ const offset = Number(match[1]);
16196
+ const end = match[2] === "" ? null : Number(match[2]);
16197
+ if (end !== null && end < offset) {
16198
+ return null;
16199
+ }
16200
+ return {
16201
+ offset,
16202
+ length: end !== null ? end - offset + 1 : null
16203
+ };
16204
+ };
16127
16205
  var FilePathSource = class _FilePathSource extends PathedSource {
16128
16206
  /** Creates a new {@link FilePathSource} backed by the file at the specified file path. */
16129
16207
  constructor(filePath, options = {}) {
@@ -17037,20 +17115,17 @@ var Mediabunny = (() => {
17037
17115
  this._offset + minReadPosition,
17038
17116
  this._offset + maxReadPosition
17039
17117
  );
17040
- if (result instanceof Promise) {
17041
- return result.then((result2) => {
17042
- if (!result2) {
17043
- return null;
17044
- }
17045
- result2.offset -= this._offset;
17046
- return result2;
17047
- });
17048
- } else {
17049
- if (!result) {
17118
+ const processResult = (result2) => {
17119
+ if (!result2) {
17050
17120
  return null;
17051
17121
  }
17052
- result.offset -= this._offset;
17053
- return result;
17122
+ result2.offset -= this._offset;
17123
+ return result2;
17124
+ };
17125
+ if (result instanceof Promise) {
17126
+ return result.then(processResult);
17127
+ } else {
17128
+ return processResult(result);
17054
17129
  }
17055
17130
  }
17056
17131
  /** @internal */
@@ -22130,6 +22205,9 @@ var Mediabunny = (() => {
22130
22205
  const filteredNalUnits = [];
22131
22206
  for (const loc of iterateAvcNalUnits(packet.data, this.decoderConfig)) {
22132
22207
  const type = extractNalUnitTypeForAvc(packet.data[loc.offset]);
22208
+ if (type === 9 /* AUD */) {
22209
+ filteredNalUnits.length = 0;
22210
+ }
22133
22211
  if (!(type >= 20 && type <= 31)) {
22134
22212
  filteredNalUnits.push(packet.data.subarray(loc.offset, loc.offset + loc.length));
22135
22213
  }
@@ -23816,7 +23894,7 @@ var Mediabunny = (() => {
23816
23894
  /** If this method returns true, the track's samples use a high dynamic range (HDR). */
23817
23895
  async hasHighDynamicRange() {
23818
23896
  const colorSpace = await this._backing.getColorSpace();
23819
- return colorSpace.primaries === "bt2020" || colorSpace.primaries === "smpte432" || colorSpace.transfer === "pg" || colorSpace.transfer === "hlg" || colorSpace.matrix === "bt2020-ncl";
23897
+ return colorSpace.primaries === "bt2020" || colorSpace.primaries === "smpte432" || colorSpace.transfer === "pq" || colorSpace.transfer === "hlg" || colorSpace.matrix === "bt2020-ncl";
23820
23898
  }
23821
23899
  /** Checks if this track may contain transparent samples with alpha data. */
23822
23900
  async canBeTransparent() {
@@ -24550,13 +24628,13 @@ var Mediabunny = (() => {
24550
24628
  if (chunks.length === 1 && this.fileSizeNonStrict !== null) {
24551
24629
  return this.requestSlice(0, this.fileSizeNonStrict);
24552
24630
  }
24553
- const startOffset = chunks.length * CHUNK_SIZE;
24554
- let slice = this.requestSliceRange(startOffset, 0, CHUNK_SIZE);
24631
+ let slice = this.requestSliceRange(currentSize, 0, CHUNK_SIZE);
24555
24632
  if (slice instanceof Promise) slice = await slice;
24556
- if (!slice) {
24633
+ if (!slice || slice.length === 0) {
24557
24634
  break;
24558
24635
  }
24559
- chunks.push(readBytes(slice, slice.length));
24636
+ const chunk = readBytes(slice, slice.length);
24637
+ chunks.push(chunk);
24560
24638
  currentSize += slice.length;
24561
24639
  }
24562
24640
  const joined = new Uint8Array(currentSize);
@@ -26413,12 +26491,14 @@ var Mediabunny = (() => {
26413
26491
  };
26414
26492
  var mdat = (reserveLargeSize) => ({ type: "mdat", largeSize: reserveLargeSize });
26415
26493
  var free = (size) => ({ type: "free", size });
26416
- var moov = (muxer) => box("moov", void 0, [
26417
- mvhd(muxer.creationTime, muxer.trackDatas),
26418
- ...muxer.trackDatas.map((x) => trak(x, muxer.creationTime)),
26419
- muxer.isFragmented ? mvex(muxer.trackDatas) : null,
26420
- udta(muxer)
26421
- ]);
26494
+ var moov = (muxer) => {
26495
+ return box("moov", void 0, [
26496
+ mvhd(muxer.creationTime, muxer.trackDatas),
26497
+ ...muxer.trackDatas.map((x) => trak(x, muxer.creationTime)),
26498
+ muxer.isFragmented ? mvex(muxer.trackDatas) : null,
26499
+ udta(muxer)
26500
+ ]);
26501
+ };
26422
26502
  var mvhd = (creationTime, trackDatas) => {
26423
26503
  const duration = Math.max(
26424
26504
  0,
@@ -26729,7 +26809,7 @@ var Mediabunny = (() => {
26729
26809
  ]);
26730
26810
  };
26731
26811
  var colr = (trackData) => box("colr", [
26732
- ascii("nclx"),
26812
+ ascii(trackData.muxer.isQuickTime ? "nclc" : "nclx"),
26733
26813
  // Colour type
26734
26814
  u16(COLOR_PRIMARIES_MAP[trackData.info.decoderConfig.colorSpace.primaries]),
26735
26815
  // Colour primaries
@@ -26737,7 +26817,7 @@ var Mediabunny = (() => {
26737
26817
  // Transfer characteristics
26738
26818
  u16(MATRIX_COEFFICIENTS_MAP[trackData.info.decoderConfig.colorSpace.matrix]),
26739
26819
  // Matrix coefficients
26740
- u8((trackData.info.decoderConfig.colorSpace.fullRange ? 1 : 0) << 7)
26820
+ trackData.muxer.isQuickTime ? [] : u8((trackData.info.decoderConfig.colorSpace.fullRange ? 1 : 0) << 7)
26741
26821
  // Full range flag
26742
26822
  ]);
26743
26823
  var avcC = (trackData) => trackData.info.decoderConfig && box("avcC", [
@@ -29237,6 +29317,7 @@ var Mediabunny = (() => {
29237
29317
  if (this.format._options.onMoov) {
29238
29318
  boxWriter.writer.startTrackingWrites();
29239
29319
  }
29320
+ this.ensureOneEnabledTrack();
29240
29321
  const movieBox = moov(this);
29241
29322
  boxWriter.writeBox(movieBox);
29242
29323
  if (this.format._options.onMoov) {
@@ -29319,6 +29400,7 @@ var Mediabunny = (() => {
29319
29400
  assert(this.boxWriter);
29320
29401
  if (this.allTracksAreKnown()) {
29321
29402
  if (!this.mdat) {
29403
+ this.ensureOneEnabledTrack();
29322
29404
  const moovBox = moov(this);
29323
29405
  const moovSize = this.boxWriter.measureBox(moovBox);
29324
29406
  const reservedSize = moovSize + this.computeSampleTableSizeUpperBound() + 4096;
@@ -29375,10 +29457,27 @@ var Mediabunny = (() => {
29375
29457
  }
29376
29458
  release();
29377
29459
  }
29460
+ ensureOneEnabledTrack() {
29461
+ for (const type of ["video", "audio", "subtitle"]) {
29462
+ const tracks = this.trackDatas.filter((t) => t.type === type);
29463
+ if (tracks.length === 0) {
29464
+ continue;
29465
+ }
29466
+ const hasEnabled = tracks.some((t) => t.track.metadata.disposition?.default !== false);
29467
+ if (!hasEnabled) {
29468
+ const firstTrack = tracks[0];
29469
+ firstTrack.track.metadata.disposition = {
29470
+ ...firstTrack.track.metadata.disposition,
29471
+ default: true
29472
+ };
29473
+ }
29474
+ }
29475
+ }
29378
29476
  /** Finalizes the file, making it ready for use. Must be called after all video and audio chunks have been added. */
29379
29477
  async finalize() {
29380
29478
  const release = await this.mutex.acquire();
29381
29479
  this.allTracksKnown.resolve();
29480
+ this.ensureOneEnabledTrack();
29382
29481
  for (const trackData of this.trackDatas) {
29383
29482
  trackData.closed = true;
29384
29483
  if (trackData.type === "subtitle" && trackData.track.source._codec === "webvtt") {