mediabunny 1.47.0 → 1.48.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 (64) hide show
  1. package/dist/bundles/mediabunny.cjs +134 -18
  2. package/dist/bundles/mediabunny.min.cjs +11 -11
  3. package/dist/bundles/mediabunny.min.mjs +11 -11
  4. package/dist/bundles/mediabunny.mjs +134 -18
  5. package/dist/bundles/mediabunny.node.cjs +134 -18
  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.js +3 -0
  35. package/dist/modules/src/ogg/ogg-demuxer.d.ts +1 -0
  36. package/dist/modules/src/ogg/ogg-demuxer.d.ts.map +1 -1
  37. package/dist/modules/src/ogg/ogg-demuxer.js +3 -0
  38. package/dist/modules/src/segmented-input.d.ts +7 -1
  39. package/dist/modules/src/segmented-input.d.ts.map +1 -1
  40. package/dist/modules/src/segmented-input.js +13 -1
  41. package/dist/modules/src/source.d.ts +13 -3
  42. package/dist/modules/src/source.d.ts.map +1 -1
  43. package/dist/modules/src/source.js +19 -3
  44. package/dist/modules/src/tsconfig.tsbuildinfo +1 -1
  45. package/dist/modules/src/wave/wave-demuxer.d.ts +1 -0
  46. package/dist/modules/src/wave/wave-demuxer.d.ts.map +1 -1
  47. package/dist/modules/src/wave/wave-demuxer.js +3 -0
  48. package/package.json +1 -1
  49. package/src/adts/adts-demuxer.ts +4 -0
  50. package/src/flac/flac-demuxer.ts +4 -0
  51. package/src/hls/hls-demuxer.ts +8 -1
  52. package/src/hls/hls-segmented-input.ts +32 -12
  53. package/src/index.ts +2 -0
  54. package/src/input-format.ts +33 -0
  55. package/src/input-track.ts +23 -0
  56. package/src/isobmff/isobmff-demuxer.ts +4 -0
  57. package/src/matroska/matroska-demuxer.ts +4 -0
  58. package/src/media-sink.ts +54 -3
  59. package/src/mp3/mp3-demuxer.ts +4 -0
  60. package/src/mpeg-ts/mpeg-ts-demuxer.ts +4 -0
  61. package/src/ogg/ogg-demuxer.ts +4 -0
  62. package/src/segmented-input.ts +23 -2
  63. package/src/source.ts +26 -5
  64. 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
  }
@@ -14201,6 +14222,9 @@ var Mediabunny = (() => {
14201
14222
  isRelativeToUnixEpoch() {
14202
14223
  return false;
14203
14224
  }
14225
+ getUnixTimeForTimestamp() {
14226
+ return null;
14227
+ }
14204
14228
  getPairingMask() {
14205
14229
  return 1n;
14206
14230
  }
@@ -15239,6 +15263,15 @@ var Mediabunny = (() => {
15239
15263
  }
15240
15264
  return lastSegment.timestamp + lastSegment.duration;
15241
15265
  }
15266
+ async getUnixTimeForTimestamp(timestamp) {
15267
+ let segment = await this.getSegmentAt(timestamp, {});
15268
+ segment ??= await this.getFirstSegment({});
15269
+ if (!segment || segment.unixEpochTimestamp === null) {
15270
+ return null;
15271
+ }
15272
+ const elapsed = timestamp - segment.timestamp;
15273
+ return segment.unixEpochTimestamp + elapsed;
15274
+ }
15242
15275
  async getTrackBackings() {
15243
15276
  return this.trackBackingsPromise ??= (async () => {
15244
15277
  const backings = [];
@@ -15396,7 +15429,10 @@ var Mediabunny = (() => {
15396
15429
  async isRelativeToUnixEpoch() {
15397
15430
  await this.hydrate();
15398
15431
  assert(this.segmentedInput.firstSegment);
15399
- return this.segmentedInput.firstSegment.relativeToUnixEpoch;
15432
+ return this.segmentedInput.firstSegment.unixEpochTimestamp === this.segmentedInput.firstSegment.timestamp;
15433
+ }
15434
+ getUnixTimeForTimestamp(timestamp) {
15435
+ return this.segmentedInput.getUnixTimeForTimestamp(timestamp);
15400
15436
  }
15401
15437
  getBitrate() {
15402
15438
  return this.delegate(() => this.firstInputTrack._backing.getBitrate());
@@ -15986,7 +16022,10 @@ var Mediabunny = (() => {
15986
16022
  throw new TypeError("options.fetchFn, when provided, must be a function.");
15987
16023
  }
15988
16024
  const urlString = url2 instanceof Request ? url2.url : url2 instanceof URL ? url2.href : url2;
15989
- super(urlString, (request) => new _UrlSource(request.path, this._options));
16025
+ super(
16026
+ urlString,
16027
+ (request) => new _UrlSource(request.path, this._options)
16028
+ );
15990
16029
  /** @internal */
15991
16030
  this._offset = 0;
15992
16031
  /** @internal */
@@ -16091,6 +16130,9 @@ var Mediabunny = (() => {
16091
16130
  if (!response.ok) {
16092
16131
  throw new Error(`Error fetching ${String(this._url)}: ${response.status} ${response.statusText}`);
16093
16132
  }
16133
+ if (response.redirected) {
16134
+ this.rootPath = response.url;
16135
+ }
16094
16136
  outer:
16095
16137
  if (this._orchestrator.fileSize === null) {
16096
16138
  const contentRange = response.headers.get("Content-Range");
@@ -16751,6 +16793,12 @@ var Mediabunny = (() => {
16751
16793
  offset: innerStart
16752
16794
  });
16753
16795
  } else {
16796
+ promise.catch((error) => {
16797
+ if (this.disposed) {
16798
+ return;
16799
+ }
16800
+ throw error;
16801
+ });
16754
16802
  }
16755
16803
  return result;
16756
16804
  }
@@ -16835,11 +16883,11 @@ var Mediabunny = (() => {
16835
16883
  if (worker.pendingSlices.length > 0) {
16836
16884
  worker.pendingSlices.forEach((x) => x.reject(error));
16837
16885
  worker.pendingSlices.length = 0;
16838
- } else {
16886
+ } else if (!worker.aborted && !this.disposed) {
16839
16887
  throw error;
16840
16888
  }
16841
16889
  }).finally(() => {
16842
- if (worker.running) {
16890
+ if (worker.running || this.workers.length >= this.options.maxWorkerCount) {
16843
16891
  return;
16844
16892
  }
16845
16893
  if (this.queuedReads.length > 0) {
@@ -17152,6 +17200,7 @@ var Mediabunny = (() => {
17152
17200
  this.streamHasEnded = false;
17153
17201
  this.lastSegmentUpdateTime = -Infinity;
17154
17202
  this.refreshInterval = 5;
17203
+ this.rootPath = path;
17155
17204
  this.demuxer = demuxer;
17156
17205
  this.nextLines = lines;
17157
17206
  }
@@ -17187,19 +17236,24 @@ var Mediabunny = (() => {
17187
17236
  if (!lines) {
17188
17237
  var _stack = [];
17189
17238
  try {
17190
- const ref = __using(_stack, await this.demuxer.input._getSourceUncached({ path: this.path, isRoot: false }));
17239
+ const ref = __using(_stack, await this.demuxer.input._getSourceUncached({ path: this.rootPath, isRoot: false }));
17191
17240
  const reader = new Reader12(ref.source);
17192
17241
  const slice = await reader.requestEntireFile();
17193
17242
  assert(slice);
17194
17243
  lines = readAllLines(slice, slice.length, { ignore: canIgnoreLine });
17244
+ if (ref.source instanceof PathedSource) {
17245
+ this.rootPath = ref.source.rootPath;
17246
+ }
17195
17247
  } catch (_) {
17196
17248
  var _error = _, _hasError = true;
17197
17249
  } finally {
17198
17250
  __callDispose(_stack, _error, _hasError);
17199
17251
  }
17200
17252
  }
17253
+ const offsetTimestampsByDateTime = this.input._formatOptions.hls?.offsetTimestampsByDateTime !== false;
17201
17254
  let headerRead = false;
17202
17255
  let accumulatedTime = 0;
17256
+ let accumulatedUnixTime = null;
17203
17257
  let nextSegmentDuration = null;
17204
17258
  let currentKey = null;
17205
17259
  let nextSequenceNumber = 0;
@@ -17235,6 +17289,7 @@ var Mediabunny = (() => {
17235
17289
  currentFirstSegment = prevLastSegment.firstSegment;
17236
17290
  currentInitSegment = prevLastSegment.initSegment;
17237
17291
  lastProgramDateTimeSeconds = prevLastSegment.lastProgramDateTimeSeconds;
17292
+ accumulatedUnixTime = prevLastSegment.unixEpochTimestamp !== null ? prevLastSegment.unixEpochTimestamp + prevLastSegment.duration : null;
17238
17293
  prevLastSegment = null;
17239
17294
  }
17240
17295
  }
@@ -17261,7 +17316,7 @@ var Mediabunny = (() => {
17261
17316
  view2.setUint32(12, nextSequenceNumber);
17262
17317
  key = { ...key, iv };
17263
17318
  }
17264
- const fullPath = joinPaths(this.path, line);
17319
+ const fullPath = joinPaths(this.rootPath, line);
17265
17320
  const location = {
17266
17321
  path: fullPath,
17267
17322
  offset: nextByteRange?.offset ?? 0,
@@ -17269,7 +17324,7 @@ var Mediabunny = (() => {
17269
17324
  };
17270
17325
  const segment = {
17271
17326
  timestamp: accumulatedTime,
17272
- relativeToUnixEpoch: lastProgramDateTimeSeconds !== null,
17327
+ unixEpochTimestamp: accumulatedUnixTime,
17273
17328
  firstSegment: currentFirstSegment,
17274
17329
  sequenceNumber: nextSequenceNumber,
17275
17330
  location,
@@ -17280,6 +17335,9 @@ var Mediabunny = (() => {
17280
17335
  };
17281
17336
  currentFirstSegment ??= segment;
17282
17337
  accumulatedTime += nextSegmentDuration;
17338
+ if (accumulatedUnixTime !== null) {
17339
+ accumulatedUnixTime += nextSegmentDuration;
17340
+ }
17283
17341
  this.segments.push(segment);
17284
17342
  } else {
17285
17343
  }
@@ -17325,7 +17383,7 @@ var Mediabunny = (() => {
17325
17383
  throw new Error("Invalid #EXT-X-MAP tag; BYTERANGE attribute must have a specified offset.");
17326
17384
  }
17327
17385
  if (!prevLastSegment) {
17328
- const fullPath = joinPaths(this.path, uri);
17386
+ const fullPath = joinPaths(this.rootPath, uri);
17329
17387
  const location = {
17330
17388
  path: fullPath,
17331
17389
  offset: parsedByteRange?.offset ?? 0,
@@ -17336,7 +17394,7 @@ var Mediabunny = (() => {
17336
17394
  }
17337
17395
  const segment = {
17338
17396
  timestamp: accumulatedTime,
17339
- relativeToUnixEpoch: lastProgramDateTimeSeconds !== null,
17397
+ unixEpochTimestamp: accumulatedUnixTime,
17340
17398
  firstSegment: null,
17341
17399
  sequenceNumber: null,
17342
17400
  location,
@@ -17386,7 +17444,7 @@ var Mediabunny = (() => {
17386
17444
  }
17387
17445
  currentKey = {
17388
17446
  method: "AES-128",
17389
- keyUri: joinPaths(this.path, uri),
17447
+ keyUri: joinPaths(this.rootPath, uri),
17390
17448
  iv,
17391
17449
  keyFormat
17392
17450
  };
@@ -17456,13 +17514,17 @@ var Mediabunny = (() => {
17456
17514
  const lastSegmentEnd = lastSegment.timestamp + lastSegment.duration;
17457
17515
  const offset = dateTimeSeconds - lastSegmentEnd;
17458
17516
  for (const segment of this.segments) {
17459
- segment.timestamp += offset;
17460
- segment.relativeToUnixEpoch = true;
17517
+ segment.unixEpochTimestamp = segment.timestamp + offset;
17518
+ if (offsetTimestampsByDateTime) {
17519
+ segment.timestamp = segment.unixEpochTimestamp;
17520
+ }
17461
17521
  }
17462
- accumulatedTime += offset;
17463
17522
  }
17464
17523
  lastProgramDateTimeSeconds = dateTimeSeconds;
17465
- accumulatedTime = dateTimeSeconds;
17524
+ accumulatedUnixTime = dateTimeSeconds;
17525
+ if (offsetTimestampsByDateTime) {
17526
+ accumulatedTime = dateTimeSeconds;
17527
+ }
17466
17528
  } else if (line === TAG_DISCONTINUITY) {
17467
17529
  currentFirstSegment = null;
17468
17530
  } else if (line.startsWith(TAG_TARGETDURATION)) {
@@ -17679,10 +17741,10 @@ var Mediabunny = (() => {
17679
17741
  readMetadata() {
17680
17742
  return this.metadataPromise ??= (async () => {
17681
17743
  assert(this.input._rootSource instanceof PathedSource);
17682
- const { rootPath } = this.input._rootSource;
17683
17744
  const slice = await this.input._reader.requestEntireFile();
17684
17745
  assert(slice);
17685
17746
  const lines = readAllLines(slice, slice.length, { ignore: canIgnoreLine });
17747
+ const { rootPath } = this.input._rootSource;
17686
17748
  const variantStreams = [];
17687
17749
  const mediaTags = [];
17688
17750
  for (let i = 1; i < lines.length; i++) {
@@ -18197,6 +18259,9 @@ var Mediabunny = (() => {
18197
18259
  isRelativeToUnixEpoch() {
18198
18260
  return this.delegate(() => this.internalTrack.backingTrack.isRelativeToUnixEpoch());
18199
18261
  }
18262
+ getUnixTimeForTimestamp(timestamp) {
18263
+ return this.delegate(() => this.internalTrack.backingTrack.getUnixTimeForTimestamp(timestamp));
18264
+ }
18200
18265
  getBitrate() {
18201
18266
  return this.internalTrack.peakBitrate;
18202
18267
  }
@@ -18785,6 +18850,14 @@ var Mediabunny = (() => {
18785
18850
  throw new TypeError(`${prefix}.isobmff.resolveKeyId, when provided, must be a function.`);
18786
18851
  }
18787
18852
  }
18853
+ if (options.hls !== void 0) {
18854
+ if (!options.hls || typeof options.hls !== "object") {
18855
+ throw new TypeError(`${prefix}.hls, when provided, must be an object.`);
18856
+ }
18857
+ if (options.hls.offsetTimestampsByDateTime !== void 0 && typeof options.hls.offsetTimestampsByDateTime !== "boolean") {
18858
+ throw new TypeError(`${prefix}.hls.offsetTimestampsByDateTime, when provided, must be a boolean.`);
18859
+ }
18860
+ }
18788
18861
  };
18789
18862
 
18790
18863
  // src/decode.ts
@@ -22845,14 +22918,29 @@ var Mediabunny = (() => {
22845
22918
  }
22846
22919
  };
22847
22920
  };
22921
+ var validateVideoSinkDecoderOptions = (decoderOptions) => {
22922
+ if (!decoderOptions || typeof decoderOptions !== "object") {
22923
+ throw new TypeError("decoderOptions must be an object.");
22924
+ }
22925
+ if (decoderOptions.hardwareAcceleration !== void 0 && !["no-preference", "prefer-hardware", "prefer-software"].includes(decoderOptions.hardwareAcceleration)) {
22926
+ throw new TypeError(
22927
+ "decoderOptions.hardwareAcceleration, when provided, must be 'no-preference', 'prefer-hardware' or 'prefer-software'."
22928
+ );
22929
+ }
22930
+ if (decoderOptions.optimizeForLatency !== void 0 && typeof decoderOptions.optimizeForLatency !== "boolean") {
22931
+ throw new TypeError("decoderOptions.optimizeForLatency, when provided, must be a boolean.");
22932
+ }
22933
+ };
22848
22934
  var VideoSampleSink = class extends BaseMediaSampleSink {
22849
22935
  /** Creates a new {@link VideoSampleSink} for the given {@link InputVideoTrack}. */
22850
- constructor(videoTrack) {
22936
+ constructor(videoTrack, decoderOptions = {}) {
22851
22937
  if (!(videoTrack instanceof InputVideoTrack)) {
22852
22938
  throw new TypeError("videoTrack must be an InputVideoTrack.");
22853
22939
  }
22940
+ validateVideoSinkDecoderOptions(decoderOptions);
22854
22941
  super();
22855
22942
  this._track = videoTrack;
22943
+ this._decoderOptions = decoderOptions;
22856
22944
  }
22857
22945
  /** @internal */
22858
22946
  async _createDecoder(onSample, onError) {
@@ -22863,9 +22951,14 @@ var Mediabunny = (() => {
22863
22951
  }
22864
22952
  const codec = await this._track.getCodec();
22865
22953
  const rotation = await this._track.getRotation();
22866
- const decoderConfig = await this._track.getDecoderConfig();
22954
+ let decoderConfig = await this._track.getDecoderConfig();
22867
22955
  const timeResolution = await this._track.getTimeResolution();
22868
22956
  assert(codec && decoderConfig);
22957
+ decoderConfig = {
22958
+ ...decoderConfig,
22959
+ hardwareAcceleration: this._decoderOptions.hardwareAcceleration,
22960
+ optimizeForLatency: this._decoderOptions.optimizeForLatency
22961
+ };
22869
22962
  return new VideoDecoderWrapper(onSample, onError, codec, decoderConfig, rotation, timeResolution);
22870
22963
  }
22871
22964
  /** @internal */
@@ -22955,11 +23048,14 @@ var Mediabunny = (() => {
22955
23048
  if (options.poolSize !== void 0 && (typeof options.poolSize !== "number" || !Number.isInteger(options.poolSize) || options.poolSize < 0)) {
22956
23049
  throw new TypeError("poolSize must be a non-negative integer.");
22957
23050
  }
23051
+ if (options.decoderOptions !== void 0) {
23052
+ validateVideoSinkDecoderOptions(options.decoderOptions);
23053
+ }
22958
23054
  this._videoTrack = videoTrack;
22959
23055
  this._alpha = options.alpha ?? false;
22960
23056
  this._options = options;
22961
23057
  this._fit = options.fit ?? "fill";
22962
- this._videoSampleSink = new VideoSampleSink(videoTrack);
23058
+ this._videoSampleSink = new VideoSampleSink(videoTrack, options.decoderOptions);
22963
23059
  this._canvasPool = Array.from({ length: options.poolSize ?? 0 }, () => null);
22964
23060
  }
22965
23061
  /** @internal */
@@ -23602,6 +23698,26 @@ var Mediabunny = (() => {
23602
23698
  async isRelativeToUnixEpoch() {
23603
23699
  return this._backing.isRelativeToUnixEpoch();
23604
23700
  }
23701
+ /**
23702
+ * Returns the Unix time (in seconds since January 1, 1970 00:00:00 UTC) that the given track timestamp (in seconds)
23703
+ * maps to, or `null` if there is no such mapping. This provides a piecewise-continuous mapping from this track's
23704
+ * timestamp space into wall-clock time. Such mapping exists, for example, for HLS playlists with
23705
+ * `#EXT-X-PROGRAM-DATE-TIME` tags present.
23706
+ *
23707
+ * This mapping can be available even when {@link InputTrack.isRelativeToUnixEpoch} is `false`, for example for HLS
23708
+ * streams with program date time information but with {@link HlsInputFormatOptions.offsetTimestampsByDateTime}
23709
+ * set to `false`.
23710
+ */
23711
+ async getUnixTimeForTimestamp(timestamp) {
23712
+ return this._backing.getUnixTimeForTimestamp(timestamp);
23713
+ }
23714
+ /**
23715
+ * Whether the track's timestamps can be mapped to Unix wall clock time via
23716
+ * {@link InputTrack.getUnixTimeForTimestamp}.
23717
+ */
23718
+ async hasUnixTimeMapping() {
23719
+ return await this._backing.getUnixTimeForTimestamp(await this.getFirstTimestamp()) !== null;
23720
+ }
23605
23721
  /** Returns the track's disposition, i.e. information about its intended usage. */
23606
23722
  async getDisposition() {
23607
23723
  return this._backing.getDisposition();