@blockcast/mmt-container 2.2.0-main.da2e7cd1dd5d → 2.2.0-main.e75bbd7576a6

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 (46) hide show
  1. package/dist/browser-runtime.js +472 -36
  2. package/dist/browser-runtime.js.map +2 -2
  3. package/dist/delivery-catalog.js +2 -1
  4. package/dist/delivery-catalog.js.map +2 -2
  5. package/dist/fec-manager.d.ts +30 -0
  6. package/dist/fec-manager.d.ts.map +1 -1
  7. package/dist/fec-manager.js +196 -2
  8. package/dist/fec-manager.js.map +2 -2
  9. package/dist/index.d.ts +1 -1
  10. package/dist/index.d.ts.map +1 -1
  11. package/dist/index.js.map +2 -2
  12. package/dist/iso-block-locator.d.ts +9 -0
  13. package/dist/iso-block-locator.d.ts.map +1 -1
  14. package/dist/iso-block-locator.js +17 -0
  15. package/dist/iso-block-locator.js.map +2 -2
  16. package/dist/iso-bmff-timing.d.ts +10 -1
  17. package/dist/iso-bmff-timing.d.ts.map +1 -1
  18. package/dist/iso-bmff-timing.js +36 -2
  19. package/dist/iso-bmff-timing.js.map +2 -2
  20. package/dist/mmtp-container-parser.d.ts +35 -2
  21. package/dist/mmtp-container-parser.d.ts.map +1 -1
  22. package/dist/mmtp-container-parser.js +82 -9
  23. package/dist/mmtp-container-parser.js.map +2 -2
  24. package/dist/mmtp-container-parser.no-modules.js +85 -11
  25. package/dist/mmtp-container-parser.no-modules.js.map +2 -2
  26. package/dist/mmtp-decode-pipeline.js +81 -9
  27. package/dist/mmtp-decode-pipeline.js.map +2 -2
  28. package/dist/mmtp-router.d.ts +18 -1
  29. package/dist/mmtp-router.d.ts.map +1 -1
  30. package/dist/mmtp-router.js +276 -34
  31. package/dist/mmtp-router.js.map +2 -2
  32. package/dist/multicast-manager.js +472 -36
  33. package/dist/multicast-manager.js.map +2 -2
  34. package/dist/multicast-transport.js +2 -1
  35. package/dist/multicast-transport.js.map +2 -2
  36. package/dist/no-modules.js +81 -9
  37. package/dist/no-modules.js.map +2 -2
  38. package/dist/wasm-mmtp-container-parser.js +81 -9
  39. package/dist/wasm-mmtp-container-parser.js.map +2 -2
  40. package/package.json +5 -5
  41. package/src/fec-manager.ts +251 -2
  42. package/src/index.ts +1 -0
  43. package/src/iso-block-locator.ts +19 -0
  44. package/src/iso-bmff-timing.ts +40 -1
  45. package/src/mmtp-container-parser.ts +84 -8
  46. package/src/mmtp-router.ts +346 -30
@@ -5633,13 +5633,21 @@ var BlockcastMmt = (() => {
5633
5633
  function sameSample(a, b) {
5634
5634
  return a.decodeTime === b.decodeTime && a.presentationTime === b.presentationTime && a.duration === b.duration && a.timescale === b.timescale && a.size === b.size && a.bytePosition === b.bytePosition;
5635
5635
  }
5636
- var _track, _fragments, _pendingSamples, _evictedSamples, _lastMovieFragmentRejection, _IsoBmffTimingResolver_instances, evictOldestFragment_fn, clearSlot_fn;
5636
+ var _track, _fragments, _firstSamples, _delivered, _pendingSamples, _evictedSamples, _lastMovieFragmentRejection, _IsoBmffTimingResolver_instances, evictOldestFragment_fn, clearSlot_fn;
5637
5637
  var IsoBmffTimingResolver = class {
5638
5638
  constructor(maxPendingSamples) {
5639
5639
  this.maxPendingSamples = maxPendingSamples;
5640
5640
  __privateAdd(this, _IsoBmffTimingResolver_instances);
5641
5641
  __privateAdd(this, _track, null);
5642
5642
  __privateAdd(this, _fragments, /* @__PURE__ */ new Map());
5643
+ /** First sample of each pending fragment, kept in `#delivered` with its sample count. */
5644
+ __privateAdd(this, _firstSamples, /* @__PURE__ */ new Map());
5645
+ /**
5646
+ * First sample and sample count of the newest fragments whose every slot was
5647
+ * cleared. Abandonment counts: `releaseSample` clears a slot too, which
5648
+ * matches the combiner, since it retires an abandoned sample as well.
5649
+ */
5650
+ __privateAdd(this, _delivered, /* @__PURE__ */ new Map());
5643
5651
  __privateAdd(this, _pendingSamples, 0);
5644
5652
  __privateAdd(this, _evictedSamples, 0);
5645
5653
  __privateAdd(this, _lastMovieFragmentRejection, null);
@@ -5647,6 +5655,10 @@ var BlockcastMmt = (() => {
5647
5655
  throw new Error("[IsoBmffTimingResolver] maxPendingSamples must be a positive integer");
5648
5656
  }
5649
5657
  }
5658
+ /** True once an FT=0 has bound the track that FT=1 timing is read against. */
5659
+ get initialized() {
5660
+ return __privateGet(this, _track) !== null;
5661
+ }
5650
5662
  get pendingSampleCount() {
5651
5663
  return __privateGet(this, _pendingSamples);
5652
5664
  }
@@ -5670,7 +5682,14 @@ var BlockcastMmt = (() => {
5670
5682
  __privateSet(this, _track, timing);
5671
5683
  return true;
5672
5684
  }
5673
- addMovieFragment(mpuSequenceNumber, data, additionalSampleLimit = this.maxPendingSamples) {
5685
+ /**
5686
+ * `recovered` marks an FEC-recovered copy. A recovered block hands back
5687
+ * every FT=1 it holds, the received ones included, so a recovered copy of a
5688
+ * fragment whose samples are all done is a duplicate: admitting it again
5689
+ * would charge stale timing and could displace the live MPU's. A source
5690
+ * copy is still admitted again (a rendition switch re-arms timing that way).
5691
+ */
5692
+ addMovieFragment(mpuSequenceNumber, data, additionalSampleLimit = this.maxPendingSamples, recovered = false) {
5674
5693
  __privateSet(this, _lastMovieFragmentRejection, null);
5675
5694
  if (!Number.isInteger(additionalSampleLimit) || additionalSampleLimit < 0) {
5676
5695
  __privateSet(this, _lastMovieFragmentRejection, "invalid");
@@ -5696,6 +5715,8 @@ var BlockcastMmt = (() => {
5696
5715
  (sample, index) => sample !== null && sameSample(sample, parsed.samples[index])
5697
5716
  );
5698
5717
  }
5718
+ const delivered = recovered ? __privateGet(this, _delivered).get(key) : void 0;
5719
+ if (delivered && delivered.sampleCount === parsed.samples.length && sameSample(delivered.first, parsed.samples[0])) return true;
5699
5720
  let freed = 0;
5700
5721
  while (__privateGet(this, _fragments).size > 0 && (__privateGet(this, _pendingSamples) + parsed.samples.length > this.maxPendingSamples || parsed.samples.length - freed > additionalSampleLimit)) {
5701
5722
  freed += __privateMethod(this, _IsoBmffTimingResolver_instances, evictOldestFragment_fn).call(this);
@@ -5709,6 +5730,7 @@ var BlockcastMmt = (() => {
5709
5730
  return false;
5710
5731
  }
5711
5732
  __privateGet(this, _fragments).set(key, [...parsed.samples]);
5733
+ __privateGet(this, _firstSamples).set(key, parsed.samples[0]);
5712
5734
  __privateSet(this, _pendingSamples, __privateGet(this, _pendingSamples) + parsed.samples.length);
5713
5735
  return true;
5714
5736
  }
@@ -5755,11 +5777,15 @@ var BlockcastMmt = (() => {
5755
5777
  reset() {
5756
5778
  __privateSet(this, _track, null);
5757
5779
  __privateGet(this, _fragments).clear();
5780
+ __privateGet(this, _firstSamples).clear();
5781
+ __privateGet(this, _delivered).clear();
5758
5782
  __privateSet(this, _pendingSamples, 0);
5759
5783
  }
5760
5784
  };
5761
5785
  _track = new WeakMap();
5762
5786
  _fragments = new WeakMap();
5787
+ _firstSamples = new WeakMap();
5788
+ _delivered = new WeakMap();
5763
5789
  _pendingSamples = new WeakMap();
5764
5790
  _evictedSamples = new WeakMap();
5765
5791
  _lastMovieFragmentRejection = new WeakMap();
@@ -5778,6 +5804,7 @@ var BlockcastMmt = (() => {
5778
5804
  let live = 0;
5779
5805
  for (const sample of samples) if (sample !== null) live++;
5780
5806
  __privateGet(this, _fragments).delete(oldest);
5807
+ __privateGet(this, _firstSamples).delete(oldest);
5781
5808
  __privateSet(this, _pendingSamples, __privateGet(this, _pendingSamples) - live);
5782
5809
  __privateSet(this, _evictedSamples, __privateGet(this, _evictedSamples) + live);
5783
5810
  return live;
@@ -5793,7 +5820,16 @@ var BlockcastMmt = (() => {
5793
5820
  if (!samples || !samples[index]) return false;
5794
5821
  samples[index] = null;
5795
5822
  __privateWrapper(this, _pendingSamples)._--;
5796
- if (samples.every((value) => value === null)) __privateGet(this, _fragments).delete(key);
5823
+ if (samples.every((value) => value === null)) {
5824
+ __privateGet(this, _fragments).delete(key);
5825
+ const first = __privateGet(this, _firstSamples).get(key);
5826
+ __privateGet(this, _firstSamples).delete(key);
5827
+ __privateGet(this, _delivered).delete(key);
5828
+ __privateGet(this, _delivered).set(key, { first, sampleCount: samples.length });
5829
+ if (__privateGet(this, _delivered).size > this.maxPendingSamples) {
5830
+ __privateGet(this, _delivered).delete(__privateGet(this, _delivered).keys().next().value);
5831
+ }
5832
+ }
5797
5833
  return true;
5798
5834
  };
5799
5835
  function fragmentKey(mpuSequenceNumber, movieFragmentSequenceNumber) {
@@ -6255,7 +6291,7 @@ var BlockcastMmt = (() => {
6255
6291
  return FMPU_BOX_TYPES.has(type);
6256
6292
  }
6257
6293
  var MAX_AWAITING_TIMING_HEADS = 8;
6258
- var _tracks, _defaultTrack, _reassembler, _sampleCombiners, _sampleCombinerOptions, _retiredSampleCombinerStats, _timingStreams, _maxPendingTimingSamples, _pendingTimingSamples, _reclaimedTimingSamples, _evictedTimingSamples, _rejectedMovieFragments, _sequencers, _fecGate, _fecSequencer, _reorderWindow, _reassemblyPolicy, _frameCb, _initCb, _sourceSymbolCb, _trackFrameCbs, _reorderStats, _reliableMoqStartupGate, _disposed, _replayingAwaitingTiming, _fedObjects, _reassemblerFragmentsFed, _reassemblerDataUnitsReturned, _feedOutcomes, _deferredFeedOutcomes, _lastOutcome, _lastOutcomeCount, _MmtpContainerParser_instances, streamKey_fn, sampleCombinerFor_fn, releaseTimingSlot_fn, dropSampleCombiner_fn, countOutcome_fn, countDeferredOutcome_fn, drop_fn, feedCounted_fn, feed_fn, emitCombinedSample_fn, passReliableMoqStartupGate_fn, timingResolver_fn, awaitTiming_fn, replayAwaitingTiming_fn, dropTimingStream_fn, validateReassemblyPolicy_fn, buildFrameMeta_fn, sequencerFor_fn, flushSequencers_fn, emitFrame_fn2, _MmtpContainerParser_static, scanForKeyframe_fn, scanLengthPrefixedHevcForKeyframe_fn, scanAnnexBForKeyframe_fn;
6294
+ var _tracks, _defaultTrack, _reassembler, _sampleCombiners, _sampleCombinerOptions, _retiredSampleCombinerStats, _timingStreams, _maxPendingTimingSamples, _pendingTimingSamples, _reclaimedTimingSamples, _evictedTimingSamples, _rejectedMovieFragments, _recoveredHeadsAwaitingTiming, _sequencers, _fecGate, _fecSequencer, _reorderWindow, _reassemblyPolicy, _frameCb, _initCb, _sourceSymbolCb, _trackFrameCbs, _reorderStats, _reliableMoqStartupGate, _disposed, _replayingAwaitingTiming, _fedObjects, _reassemblerFragmentsFed, _reassemblerDataUnitsReturned, _feedOutcomes, _deferredFeedOutcomes, _lastOutcome, _lastOutcomeCount, _MmtpContainerParser_instances, streamKey_fn, sampleCombinerFor_fn, releaseTimingSlot_fn, dropSampleCombiner_fn, countOutcome_fn, countDeferredOutcome_fn, drop_fn, feedCounted_fn, feed_fn, emitCombinedSample_fn, passReliableMoqStartupGate_fn, timingResolver_fn, awaitTiming_fn, leaveAwaitingTiming_fn, replayAwaitingTiming_fn, dropTimingStream_fn, validateReassemblyPolicy_fn, buildFrameMeta_fn, sequencerFor_fn, flushSequencers_fn, emitFrame_fn2, _MmtpContainerParser_static, scanForKeyframe_fn, scanLengthPrefixedHevcForKeyframe_fn, scanAnnexBForKeyframe_fn;
6259
6295
  var _MmtpContainerParser = class _MmtpContainerParser {
6260
6296
  constructor(config) {
6261
6297
  __privateAdd(this, _MmtpContainerParser_instances);
@@ -6291,6 +6327,12 @@ var BlockcastMmt = (() => {
6291
6327
  __privateAdd(this, _reclaimedTimingSamples, 0);
6292
6328
  __privateAdd(this, _evictedTimingSamples, 0);
6293
6329
  __privateAdd(this, _rejectedMovieFragments, 0);
6330
+ __privateAdd(this, _recoveredHeadsAwaitingTiming, {
6331
+ held: 0,
6332
+ expired: 0,
6333
+ discarded: 0,
6334
+ superseded: 0
6335
+ });
6294
6336
  __privateAdd(this, _sequencers, /* @__PURE__ */ new Map());
6295
6337
  __privateAdd(this, _fecGate, null);
6296
6338
  __privateAdd(this, _fecSequencer, null);
@@ -6433,6 +6475,14 @@ var BlockcastMmt = (() => {
6433
6475
  rejectedMovieFragments: __privateGet(this, _rejectedMovieFragments)
6434
6476
  };
6435
6477
  }
6478
+ /** Recovered heads parked for FT=1 timing (BLO-53369). Survives dispose(). */
6479
+ get recoveredHeadsAwaitingTiming() {
6480
+ let pending = 0;
6481
+ for (const { awaitingTiming } of __privateGet(this, _timingStreams).values()) {
6482
+ for (const head of awaitingTiming) if (head.options?.recovered) pending++;
6483
+ }
6484
+ return { ...__privateGet(this, _recoveredHeadsAwaitingTiming), pending };
6485
+ }
6436
6486
  get reorderStats() {
6437
6487
  let bufferedFrames = __privateGet(this, _fecSequencer)?.buffered ?? 0;
6438
6488
  for (const { sequencer } of __privateGet(this, _sequencers).values()) bufferedFrames += sequencer.buffered;
@@ -6584,7 +6634,7 @@ var BlockcastMmt = (() => {
6584
6634
  const now = Date.now();
6585
6635
  for (const { awaitingTiming } of __privateGet(this, _timingStreams).values()) {
6586
6636
  while (awaitingTiming.length > 0 && now - awaitingTiming[0].arrivedAt > timeoutMs) {
6587
- awaitingTiming.shift();
6637
+ __privateMethod(this, _MmtpContainerParser_instances, leaveAwaitingTiming_fn).call(this, awaitingTiming.shift(), "expired");
6588
6638
  }
6589
6639
  }
6590
6640
  }
@@ -6837,6 +6887,9 @@ var BlockcastMmt = (() => {
6837
6887
  for (const { sequencer } of __privateGet(this, _sequencers).values()) sequencer.discard();
6838
6888
  __privateGet(this, _sequencers).clear();
6839
6889
  __privateGet(this, _fecSequencer)?.discard();
6890
+ for (const { awaitingTiming } of __privateGet(this, _timingStreams).values()) {
6891
+ for (const head of awaitingTiming) __privateMethod(this, _MmtpContainerParser_instances, leaveAwaitingTiming_fn).call(this, head, "discarded");
6892
+ }
6840
6893
  __privateGet(this, _timingStreams).clear();
6841
6894
  __privateSet(this, _pendingTimingSamples, 0);
6842
6895
  __privateGet(this, _reliableMoqStartupGate).clear();
@@ -6865,6 +6918,7 @@ var BlockcastMmt = (() => {
6865
6918
  _reclaimedTimingSamples = new WeakMap();
6866
6919
  _evictedTimingSamples = new WeakMap();
6867
6920
  _rejectedMovieFragments = new WeakMap();
6921
+ _recoveredHeadsAwaitingTiming = new WeakMap();
6868
6922
  _sequencers = new WeakMap();
6869
6923
  _fecGate = new WeakMap();
6870
6924
  _fecSequencer = new WeakMap();
@@ -7023,11 +7077,16 @@ var BlockcastMmt = (() => {
7023
7077
  if (mpu.fragmentationIndicator !== 0 || mpu.fragmentCounter !== 0)
7024
7078
  return __privateMethod(this, _MmtpContainerParser_instances, drop_fn).call(this, "fragment-unexpected-fragmentation");
7025
7079
  const resolver2 = __privateMethod(this, _MmtpContainerParser_instances, timingResolver_fn).call(this, streamKey, info.packetId, false);
7026
- if (!resolver2) return __privateMethod(this, _MmtpContainerParser_instances, drop_fn).call(this, "fragment-no-resolver");
7080
+ if (!resolver2?.initialized) return __privateMethod(this, _MmtpContainerParser_instances, drop_fn).call(this, "fragment-no-resolver");
7027
7081
  const pendingBefore = resolver2.pendingSampleCount;
7028
7082
  const evictedBefore = resolver2.evictedSampleCount;
7029
7083
  const remaining = __privateGet(this, _maxPendingTimingSamples) - __privateGet(this, _pendingTimingSamples);
7030
- const admitted = resolver2.addMovieFragment(mpu.mpuSequenceNumber, data, remaining);
7084
+ const admitted = resolver2.addMovieFragment(
7085
+ mpu.mpuSequenceNumber,
7086
+ data,
7087
+ remaining,
7088
+ options?.recovered === true
7089
+ );
7031
7090
  __privateSet(this, _pendingTimingSamples, __privateGet(this, _pendingTimingSamples) + (resolver2.pendingSampleCount - pendingBefore));
7032
7091
  __privateSet(this, _evictedTimingSamples, __privateGet(this, _evictedTimingSamples) + (resolver2.evictedSampleCount - evictedBefore));
7033
7092
  if (!admitted) {
@@ -7047,7 +7106,7 @@ var BlockcastMmt = (() => {
7047
7106
  };
7048
7107
  }
7049
7108
  if (fragmentType !== 2) return __privateMethod(this, _MmtpContainerParser_instances, drop_fn).call(this, "unknown-fragment-type");
7050
- const resolver = __privateMethod(this, _MmtpContainerParser_instances, timingResolver_fn).call(this, streamKey, info.packetId, false);
7109
+ const resolver = __privateMethod(this, _MmtpContainerParser_instances, timingResolver_fn).call(this, streamKey, info.packetId, true);
7051
7110
  if (!resolver) return __privateMethod(this, _MmtpContainerParser_instances, drop_fn).call(this, "mfu-no-resolver");
7052
7111
  if (!Number.isSafeInteger(data.byteLength) || data.byteLength <= 0)
7053
7112
  return __privateMethod(this, _MmtpContainerParser_instances, drop_fn).call(this, "mfu-empty-payload");
@@ -7068,6 +7127,7 @@ var BlockcastMmt = (() => {
7068
7127
  mpuSequenceNumber: mpu.mpuSequenceNumber,
7069
7128
  movieFragmentSequenceNumber: mfuDuHeader.movieFragmentSequenceNumber,
7070
7129
  sampleNumber: mfuDuHeader.sampleNumber,
7130
+ sequenceNumber: info.sequenceNumber,
7071
7131
  arrivedAt: Date.now()
7072
7132
  });
7073
7133
  return __privateMethod(this, _MmtpContainerParser_instances, drop_fn).call(this, "mfu-sample-timing-missing");
@@ -7249,10 +7309,23 @@ var BlockcastMmt = (() => {
7249
7309
  return resolver;
7250
7310
  };
7251
7311
  awaitTiming_fn = function(streamKey, head) {
7252
- const awaitingTiming = __privateGet(this, _timingStreams).get(streamKey)?.awaitingTiming;
7253
- if (!awaitingTiming) return;
7312
+ const state = __privateGet(this, _timingStreams).get(streamKey);
7313
+ if (!state) return;
7314
+ if (!head.options?.recovered) {
7315
+ const kept = state.awaitingTiming.filter((queued) => !queued.options?.recovered || queued.sequenceNumber !== head.sequenceNumber);
7316
+ __privateGet(this, _recoveredHeadsAwaitingTiming).superseded += state.awaitingTiming.length - kept.length;
7317
+ state.awaitingTiming = kept;
7318
+ }
7319
+ const awaitingTiming = state.awaitingTiming;
7254
7320
  awaitingTiming.push(head);
7255
- if (awaitingTiming.length > MAX_AWAITING_TIMING_HEADS) awaitingTiming.shift();
7321
+ if (head.options?.recovered) __privateGet(this, _recoveredHeadsAwaitingTiming).held++;
7322
+ if (awaitingTiming.length > MAX_AWAITING_TIMING_HEADS) {
7323
+ __privateMethod(this, _MmtpContainerParser_instances, leaveAwaitingTiming_fn).call(this, awaitingTiming.shift(), "expired");
7324
+ }
7325
+ };
7326
+ /** Count a recovered head leaving the queue other than by replay or its source copy. */
7327
+ leaveAwaitingTiming_fn = function(head, outcome) {
7328
+ if (head.options?.recovered) __privateGet(this, _recoveredHeadsAwaitingTiming)[outcome]++;
7256
7329
  };
7257
7330
  /**
7258
7331
  * Feed again the heads whose timing the FT=1 just admitted declares. That
@@ -7280,6 +7353,7 @@ var BlockcastMmt = (() => {
7280
7353
  const state = __privateGet(this, _timingStreams).get(streamKey);
7281
7354
  if (!state) return;
7282
7355
  __privateGet(this, _timingStreams).delete(streamKey);
7356
+ for (const head of state.awaitingTiming) __privateMethod(this, _MmtpContainerParser_instances, leaveAwaitingTiming_fn).call(this, head, "discarded");
7283
7357
  __privateSet(this, _pendingTimingSamples, __privateGet(this, _pendingTimingSamples) - state.resolver.pendingSampleCount);
7284
7358
  if (__privateGet(this, _pendingTimingSamples) < 0) {
7285
7359
  throw new Error("[MmtpContainerParser] pending timing sample accounting underflow");