mediabunny 1.47.0 → 1.48.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (65) hide show
  1. package/dist/bundles/mediabunny.cjs +193 -34
  2. package/dist/bundles/mediabunny.min.cjs +11 -11
  3. package/dist/bundles/mediabunny.min.mjs +11 -11
  4. package/dist/bundles/mediabunny.mjs +193 -34
  5. package/dist/bundles/mediabunny.node.cjs +193 -34
  6. package/dist/mediabunny.d.ts +72 -4
  7. package/dist/modules/src/adts/adts-demuxer.d.ts +1 -0
  8. package/dist/modules/src/adts/adts-demuxer.d.ts.map +1 -1
  9. package/dist/modules/src/adts/adts-demuxer.js +3 -0
  10. package/dist/modules/src/flac/flac-demuxer.d.ts +1 -0
  11. package/dist/modules/src/flac/flac-demuxer.d.ts.map +1 -1
  12. package/dist/modules/src/flac/flac-demuxer.js +3 -0
  13. package/dist/modules/src/hls/hls-demuxer.d.ts.map +1 -1
  14. package/dist/modules/src/hls/hls-demuxer.js +6 -1
  15. package/dist/modules/src/hls/hls-segmented-input.d.ts +1 -0
  16. package/dist/modules/src/hls/hls-segmented-input.d.ts.map +1 -1
  17. package/dist/modules/src/hls/hls-segmented-input.js +28 -11
  18. package/dist/modules/src/index.d.ts +2 -2
  19. package/dist/modules/src/index.d.ts.map +1 -1
  20. package/dist/modules/src/input-format.d.ts +21 -0
  21. package/dist/modules/src/input-format.d.ts.map +1 -1
  22. package/dist/modules/src/input-format.js +9 -0
  23. package/dist/modules/src/input-track.d.ts +17 -0
  24. package/dist/modules/src/input-track.d.ts.map +1 -1
  25. package/dist/modules/src/input-track.js +20 -0
  26. package/dist/modules/src/isobmff/isobmff-demuxer.js +3 -0
  27. package/dist/modules/src/matroska/matroska-demuxer.js +3 -0
  28. package/dist/modules/src/media-sink.d.ts +20 -1
  29. package/dist/modules/src/media-sink.d.ts.map +1 -1
  30. package/dist/modules/src/media-sink.js +26 -3
  31. package/dist/modules/src/mp3/mp3-demuxer.d.ts +1 -0
  32. package/dist/modules/src/mp3/mp3-demuxer.d.ts.map +1 -1
  33. package/dist/modules/src/mp3/mp3-demuxer.js +3 -0
  34. package/dist/modules/src/mpeg-ts/mpeg-ts-demuxer.d.ts.map +1 -1
  35. package/dist/modules/src/mpeg-ts/mpeg-ts-demuxer.js +91 -29
  36. package/dist/modules/src/ogg/ogg-demuxer.d.ts +1 -0
  37. package/dist/modules/src/ogg/ogg-demuxer.d.ts.map +1 -1
  38. package/dist/modules/src/ogg/ogg-demuxer.js +3 -0
  39. package/dist/modules/src/segmented-input.d.ts +7 -1
  40. package/dist/modules/src/segmented-input.d.ts.map +1 -1
  41. package/dist/modules/src/segmented-input.js +13 -1
  42. package/dist/modules/src/source.d.ts +13 -3
  43. package/dist/modules/src/source.d.ts.map +1 -1
  44. package/dist/modules/src/source.js +19 -3
  45. package/dist/modules/src/tsconfig.tsbuildinfo +1 -1
  46. package/dist/modules/src/wave/wave-demuxer.d.ts +1 -0
  47. package/dist/modules/src/wave/wave-demuxer.d.ts.map +1 -1
  48. package/dist/modules/src/wave/wave-demuxer.js +3 -0
  49. package/package.json +1 -1
  50. package/src/adts/adts-demuxer.ts +4 -0
  51. package/src/flac/flac-demuxer.ts +4 -0
  52. package/src/hls/hls-demuxer.ts +8 -1
  53. package/src/hls/hls-segmented-input.ts +32 -12
  54. package/src/index.ts +2 -0
  55. package/src/input-format.ts +33 -0
  56. package/src/input-track.ts +23 -0
  57. package/src/isobmff/isobmff-demuxer.ts +4 -0
  58. package/src/matroska/matroska-demuxer.ts +4 -0
  59. package/src/media-sink.ts +54 -3
  60. package/src/mp3/mp3-demuxer.ts +4 -0
  61. package/src/mpeg-ts/mpeg-ts-demuxer.ts +102 -30
  62. package/src/ogg/ogg-demuxer.ts +4 -0
  63. package/src/segmented-input.ts +23 -2
  64. package/src/source.ts +26 -5
  65. package/src/wave/wave-demuxer.ts +4 -0
@@ -7578,6 +7578,9 @@ var Mediabunny = (() => {
7578
7578
  isRelativeToUnixEpoch() {
7579
7579
  return false;
7580
7580
  }
7581
+ getUnixTimeForTimestamp() {
7582
+ return null;
7583
+ }
7581
7584
  getDisposition() {
7582
7585
  return this.internalTrack.disposition;
7583
7586
  }
@@ -10455,6 +10458,9 @@ var Mediabunny = (() => {
10455
10458
  isRelativeToUnixEpoch() {
10456
10459
  return false;
10457
10460
  }
10461
+ getUnixTimeForTimestamp() {
10462
+ return null;
10463
+ }
10458
10464
  getDisposition() {
10459
10465
  return this.internalTrack.disposition;
10460
10466
  }
@@ -11075,6 +11081,9 @@ var Mediabunny = (() => {
11075
11081
  isRelativeToUnixEpoch() {
11076
11082
  return false;
11077
11083
  }
11084
+ getUnixTimeForTimestamp() {
11085
+ return null;
11086
+ }
11078
11087
  getPairingMask() {
11079
11088
  return 1n;
11080
11089
  }
@@ -11643,6 +11652,9 @@ var Mediabunny = (() => {
11643
11652
  isRelativeToUnixEpoch() {
11644
11653
  return false;
11645
11654
  }
11655
+ getUnixTimeForTimestamp() {
11656
+ return null;
11657
+ }
11646
11658
  getPairingMask() {
11647
11659
  return 1n;
11648
11660
  }
@@ -12415,6 +12427,9 @@ var Mediabunny = (() => {
12415
12427
  isRelativeToUnixEpoch() {
12416
12428
  return false;
12417
12429
  }
12430
+ getUnixTimeForTimestamp() {
12431
+ return null;
12432
+ }
12418
12433
  getPairingMask() {
12419
12434
  return 1n;
12420
12435
  }
@@ -12713,6 +12728,9 @@ var Mediabunny = (() => {
12713
12728
  isRelativeToUnixEpoch() {
12714
12729
  return false;
12715
12730
  }
12731
+ getUnixTimeForTimestamp() {
12732
+ return null;
12733
+ }
12716
12734
  getPairingMask() {
12717
12735
  return 1n;
12718
12736
  }
@@ -13333,6 +13351,9 @@ var Mediabunny = (() => {
13333
13351
  isRelativeToUnixEpoch() {
13334
13352
  return false;
13335
13353
  }
13354
+ getUnixTimeForTimestamp() {
13355
+ return null;
13356
+ }
13336
13357
  getPairingMask() {
13337
13358
  return 1n;
13338
13359
  }
@@ -13525,6 +13546,10 @@ var Mediabunny = (() => {
13525
13546
  currentPos += this.packetStride;
13526
13547
  continue;
13527
13548
  }
13549
+ if (hasProgramMap && !this.elementaryStreams.some((x) => x.pid === packetHeader.pid)) {
13550
+ currentPos += this.packetStride;
13551
+ continue;
13552
+ }
13528
13553
  const section = await this.readSection(
13529
13554
  currentPos,
13530
13555
  true,
@@ -14201,6 +14226,9 @@ var Mediabunny = (() => {
14201
14226
  isRelativeToUnixEpoch() {
14202
14227
  return false;
14203
14228
  }
14229
+ getUnixTimeForTimestamp() {
14230
+ return null;
14231
+ }
14204
14232
  getPairingMask() {
14205
14233
  return 1n;
14206
14234
  }
@@ -14821,7 +14849,10 @@ var Mediabunny = (() => {
14821
14849
  if (codec !== "avc" && codec !== "hevc") {
14822
14850
  throw new Error("Unhandled.");
14823
14851
  }
14852
+ const nalHeaderSize = codec === "avc" ? 1 : 2;
14824
14853
  let packetStartPos = null;
14854
+ let frameStartFound = false;
14855
+ let lastFirstMacroblockInSlice = 0;
14825
14856
  while (true) {
14826
14857
  let remaining = this.ensureBuffered(CHUNK_SIZE);
14827
14858
  if (remaining instanceof Promise) remaining = await remaining;
@@ -14839,7 +14870,7 @@ var Mediabunny = (() => {
14839
14870
  }
14840
14871
  i = zeroIndex;
14841
14872
  const posBeforeZero = chunkStartPos + i;
14842
- if (i + 4 >= length) {
14873
+ if (i + 3 >= length) {
14843
14874
  this.seekTo(posBeforeZero);
14844
14875
  break;
14845
14876
  }
@@ -14847,32 +14878,68 @@ var Mediabunny = (() => {
14847
14878
  const b2 = chunk[i + 2];
14848
14879
  const b3 = chunk[i + 3];
14849
14880
  let startCodeLength = 0;
14850
- let nalUnitTypeByte = null;
14851
14881
  if (b1 === 0 && b2 === 0 && b3 === 1) {
14852
14882
  startCodeLength = 4;
14853
- nalUnitTypeByte = chunk[i + 4];
14854
14883
  } else if (b1 === 0 && b2 === 1) {
14855
14884
  startCodeLength = 3;
14856
- nalUnitTypeByte = b3;
14857
14885
  }
14858
14886
  if (startCodeLength === 0) {
14859
14887
  i++;
14860
14888
  continue;
14861
14889
  }
14862
14890
  const startCodePos = posBeforeZero;
14863
- if (packetStartPos === null) {
14864
- packetStartPos = startCodePos;
14865
- i += startCodeLength;
14866
- continue;
14891
+ packetStartPos ??= startCodePos;
14892
+ const nalHeaderStart = i + startCodeLength;
14893
+ const payloadStart = nalHeaderStart + nalHeaderSize;
14894
+ const AVC_SLICE_HEADER_PEEK_SIZE = 6;
14895
+ const bytesNeeded = payloadStart + (codec === "avc" ? AVC_SLICE_HEADER_PEEK_SIZE : 1);
14896
+ if (bytesNeeded > length) {
14897
+ this.seekTo(posBeforeZero);
14898
+ break;
14867
14899
  }
14868
- if (nalUnitTypeByte !== null) {
14869
- const nalUnitType = codec === "avc" ? extractNalUnitTypeForAvc(nalUnitTypeByte) : extractNalUnitTypeForHevc(nalUnitTypeByte);
14870
- const isAud = codec === "avc" ? nalUnitType === 9 /* AUD */ : nalUnitType === 35 /* AUD_NUT */;
14871
- if (isAud) {
14872
- const packetLength = startCodePos - packetStartPos;
14873
- this.seekTo(packetStartPos);
14874
- return this.supplyPacket(packetLength, 0);
14900
+ const headerByte0 = chunk[nalHeaderStart];
14901
+ let nalUnitType;
14902
+ let isSlice;
14903
+ let isAccessUnitStart;
14904
+ if (codec === "avc") {
14905
+ nalUnitType = extractNalUnitTypeForAvc(headerByte0);
14906
+ isSlice = nalUnitType === 1 /* NON_IDR_SLICE */ || nalUnitType === 2 /* SLICE_DPA */ || nalUnitType === 5 /* IDR */;
14907
+ isAccessUnitStart = nalUnitType === 6 /* SEI */ || nalUnitType === 7 /* SPS */ || nalUnitType === 8 /* PPS */ || nalUnitType === 9 /* AUD */;
14908
+ } else {
14909
+ nalUnitType = extractNalUnitTypeForHevc(headerByte0);
14910
+ const layerId = (headerByte0 & 1) << 5 | chunk[nalHeaderStart + 1] >> 3;
14911
+ if (layerId > 0) {
14912
+ i += startCodeLength;
14913
+ continue;
14914
+ }
14915
+ isSlice = nalUnitType <= 9 /* RASL_R */ || nalUnitType >= 16 /* BLA_W_LP */ && nalUnitType <= 21;
14916
+ isAccessUnitStart = nalUnitType >= 32 /* VPS_NUT */ && nalUnitType <= 37 || nalUnitType === 39 /* PREFIX_SEI_NUT */ || nalUnitType >= 41 && nalUnitType <= 44 || nalUnitType >= 48 && nalUnitType <= 55;
14917
+ }
14918
+ let isFrameBoundary = false;
14919
+ if (isSlice) {
14920
+ let startsNewPicture;
14921
+ if (codec === "avc") {
14922
+ const headerBytes = chunk.subarray(payloadStart, payloadStart + AVC_SLICE_HEADER_PEEK_SIZE);
14923
+ const firstMacroblockInSlice = readExpGolomb(new Bitstream(headerBytes));
14924
+ startsNewPicture = !frameStartFound || firstMacroblockInSlice <= lastFirstMacroblockInSlice;
14925
+ lastFirstMacroblockInSlice = firstMacroblockInSlice;
14926
+ } else {
14927
+ startsNewPicture = chunk[payloadStart] >> 7 === 1;
14875
14928
  }
14929
+ if (startsNewPicture) {
14930
+ if (frameStartFound) {
14931
+ isFrameBoundary = true;
14932
+ } else {
14933
+ frameStartFound = true;
14934
+ }
14935
+ }
14936
+ } else if (isAccessUnitStart && frameStartFound) {
14937
+ isFrameBoundary = true;
14938
+ }
14939
+ if (isFrameBoundary) {
14940
+ const packetLength = startCodePos - packetStartPos;
14941
+ this.seekTo(packetStartPos);
14942
+ return this.supplyPacket(packetLength, 0);
14876
14943
  }
14877
14944
  i += startCodeLength;
14878
14945
  }
@@ -14880,7 +14947,7 @@ var Mediabunny = (() => {
14880
14947
  break;
14881
14948
  }
14882
14949
  }
14883
- if (packetStartPos !== null) {
14950
+ if (packetStartPos !== null && this.endPos > packetStartPos) {
14884
14951
  const packetLength = this.endPos - packetStartPos;
14885
14952
  this.seekTo(packetStartPos);
14886
14953
  return this.supplyPacket(packetLength, 0);
@@ -15239,6 +15306,15 @@ var Mediabunny = (() => {
15239
15306
  }
15240
15307
  return lastSegment.timestamp + lastSegment.duration;
15241
15308
  }
15309
+ async getUnixTimeForTimestamp(timestamp) {
15310
+ let segment = await this.getSegmentAt(timestamp, {});
15311
+ segment ??= await this.getFirstSegment({});
15312
+ if (!segment || segment.unixEpochTimestamp === null) {
15313
+ return null;
15314
+ }
15315
+ const elapsed = timestamp - segment.timestamp;
15316
+ return segment.unixEpochTimestamp + elapsed;
15317
+ }
15242
15318
  async getTrackBackings() {
15243
15319
  return this.trackBackingsPromise ??= (async () => {
15244
15320
  const backings = [];
@@ -15396,7 +15472,10 @@ var Mediabunny = (() => {
15396
15472
  async isRelativeToUnixEpoch() {
15397
15473
  await this.hydrate();
15398
15474
  assert(this.segmentedInput.firstSegment);
15399
- return this.segmentedInput.firstSegment.relativeToUnixEpoch;
15475
+ return this.segmentedInput.firstSegment.unixEpochTimestamp === this.segmentedInput.firstSegment.timestamp;
15476
+ }
15477
+ getUnixTimeForTimestamp(timestamp) {
15478
+ return this.segmentedInput.getUnixTimeForTimestamp(timestamp);
15400
15479
  }
15401
15480
  getBitrate() {
15402
15481
  return this.delegate(() => this.firstInputTrack._backing.getBitrate());
@@ -15986,7 +16065,10 @@ var Mediabunny = (() => {
15986
16065
  throw new TypeError("options.fetchFn, when provided, must be a function.");
15987
16066
  }
15988
16067
  const urlString = url2 instanceof Request ? url2.url : url2 instanceof URL ? url2.href : url2;
15989
- super(urlString, (request) => new _UrlSource(request.path, this._options));
16068
+ super(
16069
+ urlString,
16070
+ (request) => new _UrlSource(request.path, this._options)
16071
+ );
15990
16072
  /** @internal */
15991
16073
  this._offset = 0;
15992
16074
  /** @internal */
@@ -16091,6 +16173,9 @@ var Mediabunny = (() => {
16091
16173
  if (!response.ok) {
16092
16174
  throw new Error(`Error fetching ${String(this._url)}: ${response.status} ${response.statusText}`);
16093
16175
  }
16176
+ if (response.redirected) {
16177
+ this.rootPath = response.url;
16178
+ }
16094
16179
  outer:
16095
16180
  if (this._orchestrator.fileSize === null) {
16096
16181
  const contentRange = response.headers.get("Content-Range");
@@ -16751,6 +16836,12 @@ var Mediabunny = (() => {
16751
16836
  offset: innerStart
16752
16837
  });
16753
16838
  } else {
16839
+ promise.catch((error) => {
16840
+ if (this.disposed) {
16841
+ return;
16842
+ }
16843
+ throw error;
16844
+ });
16754
16845
  }
16755
16846
  return result;
16756
16847
  }
@@ -16835,11 +16926,11 @@ var Mediabunny = (() => {
16835
16926
  if (worker.pendingSlices.length > 0) {
16836
16927
  worker.pendingSlices.forEach((x) => x.reject(error));
16837
16928
  worker.pendingSlices.length = 0;
16838
- } else {
16929
+ } else if (!worker.aborted && !this.disposed) {
16839
16930
  throw error;
16840
16931
  }
16841
16932
  }).finally(() => {
16842
- if (worker.running) {
16933
+ if (worker.running || this.workers.length >= this.options.maxWorkerCount) {
16843
16934
  return;
16844
16935
  }
16845
16936
  if (this.queuedReads.length > 0) {
@@ -17152,6 +17243,7 @@ var Mediabunny = (() => {
17152
17243
  this.streamHasEnded = false;
17153
17244
  this.lastSegmentUpdateTime = -Infinity;
17154
17245
  this.refreshInterval = 5;
17246
+ this.rootPath = path;
17155
17247
  this.demuxer = demuxer;
17156
17248
  this.nextLines = lines;
17157
17249
  }
@@ -17187,19 +17279,24 @@ var Mediabunny = (() => {
17187
17279
  if (!lines) {
17188
17280
  var _stack = [];
17189
17281
  try {
17190
- const ref = __using(_stack, await this.demuxer.input._getSourceUncached({ path: this.path, isRoot: false }));
17282
+ const ref = __using(_stack, await this.demuxer.input._getSourceUncached({ path: this.rootPath, isRoot: false }));
17191
17283
  const reader = new Reader12(ref.source);
17192
17284
  const slice = await reader.requestEntireFile();
17193
17285
  assert(slice);
17194
17286
  lines = readAllLines(slice, slice.length, { ignore: canIgnoreLine });
17287
+ if (ref.source instanceof PathedSource) {
17288
+ this.rootPath = ref.source.rootPath;
17289
+ }
17195
17290
  } catch (_) {
17196
17291
  var _error = _, _hasError = true;
17197
17292
  } finally {
17198
17293
  __callDispose(_stack, _error, _hasError);
17199
17294
  }
17200
17295
  }
17296
+ const offsetTimestampsByDateTime = this.input._formatOptions.hls?.offsetTimestampsByDateTime !== false;
17201
17297
  let headerRead = false;
17202
17298
  let accumulatedTime = 0;
17299
+ let accumulatedUnixTime = null;
17203
17300
  let nextSegmentDuration = null;
17204
17301
  let currentKey = null;
17205
17302
  let nextSequenceNumber = 0;
@@ -17235,6 +17332,7 @@ var Mediabunny = (() => {
17235
17332
  currentFirstSegment = prevLastSegment.firstSegment;
17236
17333
  currentInitSegment = prevLastSegment.initSegment;
17237
17334
  lastProgramDateTimeSeconds = prevLastSegment.lastProgramDateTimeSeconds;
17335
+ accumulatedUnixTime = prevLastSegment.unixEpochTimestamp !== null ? prevLastSegment.unixEpochTimestamp + prevLastSegment.duration : null;
17238
17336
  prevLastSegment = null;
17239
17337
  }
17240
17338
  }
@@ -17261,7 +17359,7 @@ var Mediabunny = (() => {
17261
17359
  view2.setUint32(12, nextSequenceNumber);
17262
17360
  key = { ...key, iv };
17263
17361
  }
17264
- const fullPath = joinPaths(this.path, line);
17362
+ const fullPath = joinPaths(this.rootPath, line);
17265
17363
  const location = {
17266
17364
  path: fullPath,
17267
17365
  offset: nextByteRange?.offset ?? 0,
@@ -17269,7 +17367,7 @@ var Mediabunny = (() => {
17269
17367
  };
17270
17368
  const segment = {
17271
17369
  timestamp: accumulatedTime,
17272
- relativeToUnixEpoch: lastProgramDateTimeSeconds !== null,
17370
+ unixEpochTimestamp: accumulatedUnixTime,
17273
17371
  firstSegment: currentFirstSegment,
17274
17372
  sequenceNumber: nextSequenceNumber,
17275
17373
  location,
@@ -17280,6 +17378,9 @@ var Mediabunny = (() => {
17280
17378
  };
17281
17379
  currentFirstSegment ??= segment;
17282
17380
  accumulatedTime += nextSegmentDuration;
17381
+ if (accumulatedUnixTime !== null) {
17382
+ accumulatedUnixTime += nextSegmentDuration;
17383
+ }
17283
17384
  this.segments.push(segment);
17284
17385
  } else {
17285
17386
  }
@@ -17325,7 +17426,7 @@ var Mediabunny = (() => {
17325
17426
  throw new Error("Invalid #EXT-X-MAP tag; BYTERANGE attribute must have a specified offset.");
17326
17427
  }
17327
17428
  if (!prevLastSegment) {
17328
- const fullPath = joinPaths(this.path, uri);
17429
+ const fullPath = joinPaths(this.rootPath, uri);
17329
17430
  const location = {
17330
17431
  path: fullPath,
17331
17432
  offset: parsedByteRange?.offset ?? 0,
@@ -17336,7 +17437,7 @@ var Mediabunny = (() => {
17336
17437
  }
17337
17438
  const segment = {
17338
17439
  timestamp: accumulatedTime,
17339
- relativeToUnixEpoch: lastProgramDateTimeSeconds !== null,
17440
+ unixEpochTimestamp: accumulatedUnixTime,
17340
17441
  firstSegment: null,
17341
17442
  sequenceNumber: null,
17342
17443
  location,
@@ -17386,7 +17487,7 @@ var Mediabunny = (() => {
17386
17487
  }
17387
17488
  currentKey = {
17388
17489
  method: "AES-128",
17389
- keyUri: joinPaths(this.path, uri),
17490
+ keyUri: joinPaths(this.rootPath, uri),
17390
17491
  iv,
17391
17492
  keyFormat
17392
17493
  };
@@ -17456,13 +17557,17 @@ var Mediabunny = (() => {
17456
17557
  const lastSegmentEnd = lastSegment.timestamp + lastSegment.duration;
17457
17558
  const offset = dateTimeSeconds - lastSegmentEnd;
17458
17559
  for (const segment of this.segments) {
17459
- segment.timestamp += offset;
17460
- segment.relativeToUnixEpoch = true;
17560
+ segment.unixEpochTimestamp = segment.timestamp + offset;
17561
+ if (offsetTimestampsByDateTime) {
17562
+ segment.timestamp = segment.unixEpochTimestamp;
17563
+ }
17461
17564
  }
17462
- accumulatedTime += offset;
17463
17565
  }
17464
17566
  lastProgramDateTimeSeconds = dateTimeSeconds;
17465
- accumulatedTime = dateTimeSeconds;
17567
+ accumulatedUnixTime = dateTimeSeconds;
17568
+ if (offsetTimestampsByDateTime) {
17569
+ accumulatedTime = dateTimeSeconds;
17570
+ }
17466
17571
  } else if (line === TAG_DISCONTINUITY) {
17467
17572
  currentFirstSegment = null;
17468
17573
  } else if (line.startsWith(TAG_TARGETDURATION)) {
@@ -17679,10 +17784,10 @@ var Mediabunny = (() => {
17679
17784
  readMetadata() {
17680
17785
  return this.metadataPromise ??= (async () => {
17681
17786
  assert(this.input._rootSource instanceof PathedSource);
17682
- const { rootPath } = this.input._rootSource;
17683
17787
  const slice = await this.input._reader.requestEntireFile();
17684
17788
  assert(slice);
17685
17789
  const lines = readAllLines(slice, slice.length, { ignore: canIgnoreLine });
17790
+ const { rootPath } = this.input._rootSource;
17686
17791
  const variantStreams = [];
17687
17792
  const mediaTags = [];
17688
17793
  for (let i = 1; i < lines.length; i++) {
@@ -18197,6 +18302,9 @@ var Mediabunny = (() => {
18197
18302
  isRelativeToUnixEpoch() {
18198
18303
  return this.delegate(() => this.internalTrack.backingTrack.isRelativeToUnixEpoch());
18199
18304
  }
18305
+ getUnixTimeForTimestamp(timestamp) {
18306
+ return this.delegate(() => this.internalTrack.backingTrack.getUnixTimeForTimestamp(timestamp));
18307
+ }
18200
18308
  getBitrate() {
18201
18309
  return this.internalTrack.peakBitrate;
18202
18310
  }
@@ -18785,6 +18893,14 @@ var Mediabunny = (() => {
18785
18893
  throw new TypeError(`${prefix}.isobmff.resolveKeyId, when provided, must be a function.`);
18786
18894
  }
18787
18895
  }
18896
+ if (options.hls !== void 0) {
18897
+ if (!options.hls || typeof options.hls !== "object") {
18898
+ throw new TypeError(`${prefix}.hls, when provided, must be an object.`);
18899
+ }
18900
+ if (options.hls.offsetTimestampsByDateTime !== void 0 && typeof options.hls.offsetTimestampsByDateTime !== "boolean") {
18901
+ throw new TypeError(`${prefix}.hls.offsetTimestampsByDateTime, when provided, must be a boolean.`);
18902
+ }
18903
+ }
18788
18904
  };
18789
18905
 
18790
18906
  // src/decode.ts
@@ -22845,14 +22961,29 @@ var Mediabunny = (() => {
22845
22961
  }
22846
22962
  };
22847
22963
  };
22964
+ var validateVideoSinkDecoderOptions = (decoderOptions) => {
22965
+ if (!decoderOptions || typeof decoderOptions !== "object") {
22966
+ throw new TypeError("decoderOptions must be an object.");
22967
+ }
22968
+ if (decoderOptions.hardwareAcceleration !== void 0 && !["no-preference", "prefer-hardware", "prefer-software"].includes(decoderOptions.hardwareAcceleration)) {
22969
+ throw new TypeError(
22970
+ "decoderOptions.hardwareAcceleration, when provided, must be 'no-preference', 'prefer-hardware' or 'prefer-software'."
22971
+ );
22972
+ }
22973
+ if (decoderOptions.optimizeForLatency !== void 0 && typeof decoderOptions.optimizeForLatency !== "boolean") {
22974
+ throw new TypeError("decoderOptions.optimizeForLatency, when provided, must be a boolean.");
22975
+ }
22976
+ };
22848
22977
  var VideoSampleSink = class extends BaseMediaSampleSink {
22849
22978
  /** Creates a new {@link VideoSampleSink} for the given {@link InputVideoTrack}. */
22850
- constructor(videoTrack) {
22979
+ constructor(videoTrack, decoderOptions = {}) {
22851
22980
  if (!(videoTrack instanceof InputVideoTrack)) {
22852
22981
  throw new TypeError("videoTrack must be an InputVideoTrack.");
22853
22982
  }
22983
+ validateVideoSinkDecoderOptions(decoderOptions);
22854
22984
  super();
22855
22985
  this._track = videoTrack;
22986
+ this._decoderOptions = decoderOptions;
22856
22987
  }
22857
22988
  /** @internal */
22858
22989
  async _createDecoder(onSample, onError) {
@@ -22863,9 +22994,14 @@ var Mediabunny = (() => {
22863
22994
  }
22864
22995
  const codec = await this._track.getCodec();
22865
22996
  const rotation = await this._track.getRotation();
22866
- const decoderConfig = await this._track.getDecoderConfig();
22997
+ let decoderConfig = await this._track.getDecoderConfig();
22867
22998
  const timeResolution = await this._track.getTimeResolution();
22868
22999
  assert(codec && decoderConfig);
23000
+ decoderConfig = {
23001
+ ...decoderConfig,
23002
+ hardwareAcceleration: this._decoderOptions.hardwareAcceleration,
23003
+ optimizeForLatency: this._decoderOptions.optimizeForLatency
23004
+ };
22869
23005
  return new VideoDecoderWrapper(onSample, onError, codec, decoderConfig, rotation, timeResolution);
22870
23006
  }
22871
23007
  /** @internal */
@@ -22955,11 +23091,14 @@ var Mediabunny = (() => {
22955
23091
  if (options.poolSize !== void 0 && (typeof options.poolSize !== "number" || !Number.isInteger(options.poolSize) || options.poolSize < 0)) {
22956
23092
  throw new TypeError("poolSize must be a non-negative integer.");
22957
23093
  }
23094
+ if (options.decoderOptions !== void 0) {
23095
+ validateVideoSinkDecoderOptions(options.decoderOptions);
23096
+ }
22958
23097
  this._videoTrack = videoTrack;
22959
23098
  this._alpha = options.alpha ?? false;
22960
23099
  this._options = options;
22961
23100
  this._fit = options.fit ?? "fill";
22962
- this._videoSampleSink = new VideoSampleSink(videoTrack);
23101
+ this._videoSampleSink = new VideoSampleSink(videoTrack, options.decoderOptions);
22963
23102
  this._canvasPool = Array.from({ length: options.poolSize ?? 0 }, () => null);
22964
23103
  }
22965
23104
  /** @internal */
@@ -23602,6 +23741,26 @@ var Mediabunny = (() => {
23602
23741
  async isRelativeToUnixEpoch() {
23603
23742
  return this._backing.isRelativeToUnixEpoch();
23604
23743
  }
23744
+ /**
23745
+ * Returns the Unix time (in seconds since January 1, 1970 00:00:00 UTC) that the given track timestamp (in seconds)
23746
+ * maps to, or `null` if there is no such mapping. This provides a piecewise-continuous mapping from this track's
23747
+ * timestamp space into wall-clock time. Such mapping exists, for example, for HLS playlists with
23748
+ * `#EXT-X-PROGRAM-DATE-TIME` tags present.
23749
+ *
23750
+ * This mapping can be available even when {@link InputTrack.isRelativeToUnixEpoch} is `false`, for example for HLS
23751
+ * streams with program date time information but with {@link HlsInputFormatOptions.offsetTimestampsByDateTime}
23752
+ * set to `false`.
23753
+ */
23754
+ async getUnixTimeForTimestamp(timestamp) {
23755
+ return this._backing.getUnixTimeForTimestamp(timestamp);
23756
+ }
23757
+ /**
23758
+ * Whether the track's timestamps can be mapped to Unix wall clock time via
23759
+ * {@link InputTrack.getUnixTimeForTimestamp}.
23760
+ */
23761
+ async hasUnixTimeMapping() {
23762
+ return await this._backing.getUnixTimeForTimestamp(await this.getFirstTimestamp()) !== null;
23763
+ }
23605
23764
  /** Returns the track's disposition, i.e. information about its intended usage. */
23606
23765
  async getDisposition() {
23607
23766
  return this._backing.getDisposition();