@blockcast/mmt-container 0.1.0-main.f24ff7b49949 → 0.1.0-main.f8328f38cfb6

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 (58) hide show
  1. package/dist/browser-runtime.js +102 -30
  2. package/dist/browser-runtime.js.map +2 -2
  3. package/dist/fec-manager.d.ts +0 -4
  4. package/dist/fec-manager.d.ts.map +1 -1
  5. package/dist/fec-manager.js +6 -18
  6. package/dist/fec-manager.js.map +2 -2
  7. package/dist/fec-worker-setup.js +2 -2
  8. package/dist/fec-worker-setup.js.map +1 -1
  9. package/dist/index.d.ts +1 -1
  10. package/dist/index.d.ts.map +1 -1
  11. package/dist/index.js +2 -0
  12. package/dist/index.js.map +2 -2
  13. package/dist/media-router.js.map +1 -1
  14. package/dist/mfu-reassembler.js.map +1 -1
  15. package/dist/mmtp-container-parser.js.map +2 -2
  16. package/dist/mmtp-container-parser.no-modules.js.map +2 -2
  17. package/dist/mmtp-decode-pipeline.js.map +2 -2
  18. package/dist/mmtp-router.d.ts.map +1 -1
  19. package/dist/mmtp-router.js +11 -8
  20. package/dist/mmtp-router.js.map +2 -2
  21. package/dist/multicast-manager.js +17 -26
  22. package/dist/multicast-manager.js.map +2 -2
  23. package/dist/multicast-transport.js.map +1 -1
  24. package/dist/multicast-unwrap.js.map +2 -2
  25. package/dist/no-modules.js.map +2 -2
  26. package/dist/signaling.js.map +2 -2
  27. package/dist/timestamp-codec.js.map +1 -1
  28. package/dist/track-session.d.ts +57 -0
  29. package/dist/track-session.d.ts.map +1 -1
  30. package/dist/track-session.js +83 -2
  31. package/dist/track-session.js.map +2 -2
  32. package/dist/wasm-mfu-reassembler.js.map +1 -1
  33. package/dist/wasm-mmtp-container-parser.js.map +2 -2
  34. package/node_modules/@blockcast/fec-worker/README.md +1 -1
  35. package/node_modules/@blockcast/fec-worker/dist/fec-worker-client.d.ts +2 -2
  36. package/node_modules/@blockcast/fec-worker/dist/fec-worker-client.js +2 -2
  37. package/node_modules/@blockcast/fec-worker/dist/fec-worker-types.d.ts +3 -3
  38. package/node_modules/@blockcast/fec-worker/dist/fec-worker-types.d.ts.map +1 -1
  39. package/node_modules/@blockcast/fec-worker/dist/fec-worker.js +3 -3
  40. package/node_modules/@blockcast/fec-worker/dist/fec-worker.js.map +2 -2
  41. package/node_modules/@blockcast/fec-worker/src/fec-worker-client.ts +2 -2
  42. package/node_modules/@blockcast/fec-worker/src/fec-worker-types.ts +3 -3
  43. package/node_modules/@blockcast/fec-worker/src/fec-worker.ts +6 -6
  44. package/node_modules/@blockcast/mmt-base/dist/mmtp.d.ts +6 -5
  45. package/node_modules/@blockcast/mmt-base/dist/mmtp.d.ts.map +1 -1
  46. package/node_modules/@blockcast/mmt-base/dist/mmtp.js +5 -4
  47. package/node_modules/@blockcast/mmt-base/dist/mmtp.js.map +1 -1
  48. package/node_modules/@blockcast/mmt-base/src/mmtp.ts +6 -5
  49. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/mmtp.d.ts +6 -5
  50. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/mmtp.d.ts.map +1 -1
  51. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/mmtp.js +5 -4
  52. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/mmtp.js.map +1 -1
  53. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/src/mmtp.ts +6 -5
  54. package/package.json +6 -6
  55. package/src/fec-manager.ts +16 -24
  56. package/src/index.ts +1 -0
  57. package/src/mmtp-router.ts +14 -20
  58. package/src/track-session.ts +103 -1
@@ -143,8 +143,8 @@ var init_fec_worker_client = __esm({
143
143
  * Caller must not access data after this call — it is detached.
144
144
  *
145
145
  * Carries the wire-level Repair FEC Payload ID fields (ISO 23008-1 §C.5.3):
146
- * - ssStart: SS_ID of first source symbol in the block (= SBN * SSB_length)
147
- * - ssbLength: K for this block
146
+ * - ssStart: SS_ID of first source symbol in the block (= SBN * configured K)
147
+ * - ssbLength: authored source-symbol count (1..configured K)
148
148
  * - rsId: repair symbol index within the block
149
149
  */
150
150
  feedRepair(ssStart, ssbLength, rsId, data, ts) {
@@ -9322,6 +9322,24 @@ var TrackSession = class {
9322
9322
  get isFecArmed() {
9323
9323
  return __privateGet(this, _fecArmed);
9324
9324
  }
9325
+ /**
9326
+ * Start the deadline a `joining` entry deferred while the join was inactive
9327
+ * (SessionManager.joinActive). The track gets the FULL deadline from here,
9328
+ * because that is when media can first arrive. No-op outside `joining` and
9329
+ * when a deadline is already running, so it never shortens or restarts one.
9330
+ *
9331
+ * The `joinActive` guard is what makes this safe to call from anywhere: a
9332
+ * track parked in `joining` with a deferred (null) timer satisfies the other
9333
+ * two, so without it a host calling this while the join is still inactive
9334
+ * would arm the exact deadline the gate exists to defer -- silently
9335
+ * reinstating BLO-34289. It is a no-op for the internal caller, which writes
9336
+ * `#joinActive = true` before iterating the sessions.
9337
+ */
9338
+ armDeferredJoinDeadline() {
9339
+ if (__privateGet(this, _state) !== "joining" || __privateGet(this, _joinDeadlineTimer) !== null) return;
9340
+ if (!__privateGet(this, _manager).joinActive) return;
9341
+ __privateMethod(this, _TrackSession_instances, armJoinDeadline_fn).call(this, __privateGet(this, _manager).takeoverDeadlineMs);
9342
+ }
9325
9343
  dispose() {
9326
9344
  __privateMethod(this, _TrackSession_instances, clearHysteresis_fn).call(this);
9327
9345
  __privateMethod(this, _TrackSession_instances, clearJoinDeadline_fn).call(this);
@@ -9348,7 +9366,9 @@ transition_fn = function(to, opts) {
9348
9366
  const joinDeadlineMs = to === "joining" ? __privateGet(this, _manager).takeoverDeadlineMs : void 0;
9349
9367
  __privateSet(this, _state, to);
9350
9368
  if (from === "joining") __privateMethod(this, _TrackSession_instances, clearJoinDeadline_fn).call(this);
9351
- if (joinDeadlineMs !== void 0) __privateMethod(this, _TrackSession_instances, armJoinDeadline_fn).call(this, joinDeadlineMs);
9369
+ if (joinDeadlineMs !== void 0 && __privateGet(this, _manager).joinActive) {
9370
+ __privateMethod(this, _TrackSession_instances, armJoinDeadline_fn).call(this, joinDeadlineMs);
9371
+ }
9352
9372
  const fecActive = (s) => s === "joining" || s === "multicast" || s === "amt" || s === "failing" || s === "unicast-fallback";
9353
9373
  if (!fecActive(to) && !opts?.retainFec) __privateMethod(this, _TrackSession_instances, disarmFec_fn).call(this);
9354
9374
  __privateGet(this, _manager).callbacks?.onStateChange?.(this.config.trackName, from, to);
@@ -9400,13 +9420,16 @@ unsuppress_fn = function() {
9400
9420
  __privateGet(this, _manager).callbacks?.onSuppress?.(this.config.trackName, false);
9401
9421
  };
9402
9422
  var MAX_TAKEOVER_DEADLINE_MS = 2147483647;
9403
- var _takeoverDeadlineMs, _SessionManager_instances, requireTakeoverDeadlineMs_fn;
9423
+ var JOIN_DEFER_WARN_MS = 3e4;
9424
+ var _takeoverDeadlineMs, _joinActive, _joinDeferWarnTimer, _SessionManager_instances, armJoinDeferWarn_fn, clearJoinDeferWarn_fn, requireTakeoverDeadlineMs_fn;
9404
9425
  var SessionManager = class {
9405
9426
  constructor(poolOptions) {
9406
9427
  __privateAdd(this, _SessionManager_instances);
9407
9428
  this.sessions = /* @__PURE__ */ new Map();
9408
9429
  this.callbacks = null;
9409
9430
  __privateAdd(this, _takeoverDeadlineMs);
9431
+ __privateAdd(this, _joinActive, true);
9432
+ __privateAdd(this, _joinDeferWarnTimer, null);
9410
9433
  this.pool = new TransportPool(poolOptions);
9411
9434
  this.pool.onSubscriptionFailed((sub) => {
9412
9435
  for (const trackName of sub.consumers) {
@@ -9455,6 +9478,46 @@ var SessionManager = class {
9455
9478
  get hasTakeoverDeadline() {
9456
9479
  return __privateGet(this, _takeoverDeadlineMs) !== void 0;
9457
9480
  }
9481
+ /**
9482
+ * Whether the transport the joining tracks are waiting on is actually
9483
+ * subscribed. The takeover deadline is a media-arrival budget and media
9484
+ * cannot arrive before the join exists, so while this is false, entering
9485
+ * `joining` arms no deadline; flipping it true starts the FULL deadline for
9486
+ * every track already parked there.
9487
+ *
9488
+ * Default true, which is the arm-on-`joining` behaviour every host had
9489
+ * before: a host that only calls joinStarted() after its own join resolves
9490
+ * (moqtail-private's player, post-BLO-33339) is already at the right edge and
9491
+ * needs no gate. A host that arms joinStarted() up front — to have FEC
9492
+ * decoders ready before the first datagram — sets this false before addTrack()
9493
+ * and true when its subscribe settles. Charging a slow subscribe (bridge
9494
+ * reconnect, AMT relay handshake) against the media budget expired the
9495
+ * deadline before the subscription existed and abandoned the join on the
9496
+ * happy path. BLO-34289.
9497
+ *
9498
+ * The flag tracks the host's primary join, not per-track transport state. A
9499
+ * track that fails over to the next transport in its own chain while this is
9500
+ * false (transportFailed's failover branch also enters `joining`) has that
9501
+ * deadline gated on the primary join activating, not on its own transport.
9502
+ * Under the documented usage -- false once at setup, true once the subscribe
9503
+ * settles -- the two coincide.
9504
+ *
9505
+ * Leaving this false is not free: see JOIN_DEFER_WARN_MS.
9506
+ */
9507
+ get joinActive() {
9508
+ return __privateGet(this, _joinActive);
9509
+ }
9510
+ set joinActive(active) {
9511
+ const wasActive = __privateGet(this, _joinActive);
9512
+ __privateSet(this, _joinActive, active);
9513
+ if (!active) {
9514
+ if (wasActive) __privateMethod(this, _SessionManager_instances, armJoinDeferWarn_fn).call(this);
9515
+ return;
9516
+ }
9517
+ __privateMethod(this, _SessionManager_instances, clearJoinDeferWarn_fn).call(this);
9518
+ if (wasActive) return;
9519
+ for (const [, session] of this.sessions) session.armDeferredJoinDeadline();
9520
+ }
9458
9521
  /** Narrow to MMTP callbacks (onTrackEvent). Safe — optional chaining handles PIM-only consumers. */
9459
9522
  get mmtp() {
9460
9523
  return this.callbacks;
@@ -9608,6 +9671,7 @@ var SessionManager = class {
9608
9671
  // Cleanup
9609
9672
  // -----------------------------------------------------------------------
9610
9673
  dispose() {
9674
+ __privateMethod(this, _SessionManager_instances, clearJoinDeferWarn_fn).call(this);
9611
9675
  for (const [, session] of this.sessions) {
9612
9676
  session.dispose();
9613
9677
  }
@@ -9617,7 +9681,23 @@ var SessionManager = class {
9617
9681
  }
9618
9682
  };
9619
9683
  _takeoverDeadlineMs = new WeakMap();
9684
+ _joinActive = new WeakMap();
9685
+ _joinDeferWarnTimer = new WeakMap();
9620
9686
  _SessionManager_instances = new WeakSet();
9687
+ armJoinDeferWarn_fn = function() {
9688
+ __privateSet(this, _joinDeferWarnTimer, setTimeout(() => {
9689
+ __privateSet(this, _joinDeferWarnTimer, null);
9690
+ const deferred = [...this.sessions.values()].filter((s) => s.state === "joining").length;
9691
+ console.warn(
9692
+ `[SessionManager] joinActive has been false for ${JOIN_DEFER_WARN_MS} ms and ${deferred} track(s) sit in 'joining' with no takeover deadline. Set joinActive = true when the subscribe settles, on every exit path including failures, or these tracks never time out, never release their FEC decoders, and never advance to the next transport.`
9693
+ );
9694
+ }, JOIN_DEFER_WARN_MS));
9695
+ };
9696
+ clearJoinDeferWarn_fn = function() {
9697
+ if (__privateGet(this, _joinDeferWarnTimer) === null) return;
9698
+ clearTimeout(__privateGet(this, _joinDeferWarnTimer));
9699
+ __privateSet(this, _joinDeferWarnTimer, null);
9700
+ };
9621
9701
  // -----------------------------------------------------------------------
9622
9702
  // Track management
9623
9703
  // -----------------------------------------------------------------------
@@ -9965,6 +10045,7 @@ var FECManager = class {
9965
10045
  const k = this.kForPacket(packetId);
9966
10046
  const isSourceSymbol = esi >= 0 && esi < k;
9967
10047
  const isStartupPartial = __privateGet(this, _startupPartialBlocks).has(blockKey);
10048
+ const isReadyMulticastRepair = __privateGet(this, _startupRequireRepairReady) && source === "multicast" && !isSourceSymbol && __privateGet(this, _startupRepairReadyPackets).has(streamKey);
9968
10049
  const awaitingMulticastRepair = __privateGet(this, _startupRequireRepairReady) && source === "multicast" && !__privateGet(this, _startupRepairReadyPackets).has(streamKey) && !__privateGet(this, _startupRepairFailOpenPackets).has(streamKey);
9969
10050
  if (awaitingMulticastRepair) {
9970
10051
  if (!isSourceSymbol) {
@@ -9991,7 +10072,7 @@ var FECManager = class {
9991
10072
  if (isSourceSymbol && esi === 0) {
9992
10073
  __privateGet(this, _startupPartialBlocks).delete(blockKey);
9993
10074
  __privateGet(this, _startupAlignedPackets).add(streamKey);
9994
- } else if (isStartupPartial || !__privateGet(this, _startupAlignedPackets).has(streamKey)) {
10075
+ } else if (isStartupPartial && !isReadyMulticastRepair || !__privateGet(this, _startupAlignedPackets).has(streamKey) && !isReadyMulticastRepair) {
9995
10076
  __privateMethod(this, _FECManager_instances, markStartupPartial_fn).call(this, blockKey);
9996
10077
  return false;
9997
10078
  }
@@ -10406,14 +10487,10 @@ var FECManager = class {
10406
10487
  return null;
10407
10488
  }
10408
10489
  const fecData = repairData;
10409
- if (ssbLength > 1) {
10410
- this.noteBlockActivity(
10411
- Math.floor(ssStart / ssbLength),
10412
- packetId,
10413
- catalogTrackName
10414
- );
10415
- }
10416
- const sbn = ssbLength > 0 ? Math.floor(ssStart / ssbLength) : 0;
10490
+ const k = this.kForPacket(packetId);
10491
+ if (ssbLength <= 0 || ssbLength > k || ssStart % k !== 0) return null;
10492
+ const sbn = Math.floor(ssStart / k);
10493
+ this.noteBlockActivity(sbn, packetId, catalogTrackName);
10417
10494
  const blockKey = __privateMethod(this, _FECManager_instances, blockKey_fn).call(this, sbn, packetId, catalogTrackName);
10418
10495
  if (__privateGet(this, _completedBlocks).has(blockKey)) return null;
10419
10496
  const syncBlockOwner = __privateGet(this, _syncDecoderBlockOwners).get(blockKey);
@@ -10449,15 +10526,6 @@ var FECManager = class {
10449
10526
  if (recovered) __privateGet(this, _syncDecoderBlockOwners).delete(blockKey);
10450
10527
  return recovered;
10451
10528
  }
10452
- /**
10453
- * Unified symbol feeder. Dispatches to feedSourceSymbol or feedRepairSymbol.
10454
- */
10455
- feedSymbol(packetId, symbolId, data, isRepair) {
10456
- if (isRepair) {
10457
- return this.feedRepairSymbol(packetId, symbolId, 1, symbolId, data);
10458
- }
10459
- return this.feedSourceSymbol(packetId, symbolId, data);
10460
- }
10461
10529
  // -----------------------------------------------------------------------
10462
10530
  // Block lifecycle
10463
10531
  // -----------------------------------------------------------------------
@@ -13564,6 +13632,7 @@ var MmtpRouter = class {
13564
13632
  );
13565
13633
  }
13566
13634
  const repairSbn = __privateMethod(this, _MmtpRouter_instances, deriveRepairSbn_fn).call(this, parsed.ssStart, parsed.ssbLength, ownerPacketId);
13635
+ if (repairSbn === null) return;
13567
13636
  const repairEsi = __privateMethod(this, _MmtpRouter_instances, kForPacket_fn).call(this, ownerPacketId) + parsed.rsId;
13568
13637
  if (!__privateMethod(this, _MmtpRouter_instances, feedFecTransportSymbol_fn).call(this, ownerPacketId, repairSbn, repairEsi, parsed.repairData, "moq", catalogTrackName)) return;
13569
13638
  const recovered = catalogTrackName === void 0 ? __privateGet(this, _fecManager).feedRepairSymbol(
@@ -13594,7 +13663,7 @@ var MmtpRouter = class {
13594
13663
  } else {
13595
13664
  __privateGet(this, _fecManager).markBlockCompleted(repairSbn, ownerPacketId, catalogTrackName);
13596
13665
  }
13597
- __privateMethod(this, _MmtpRouter_instances, routeRecoveredBlock_fn).call(this, repairSbn, recovered, ownerPacketId, parsed.ssbLength, catalogTrackName);
13666
+ __privateMethod(this, _MmtpRouter_instances, routeRecoveredBlock_fn).call(this, repairSbn, recovered, ownerPacketId, catalogTrackName);
13598
13667
  }
13599
13668
  }
13600
13669
  }
@@ -13602,7 +13671,7 @@ var MmtpRouter = class {
13602
13671
  /** Route a recovered FEC block through the parser + dedup pipeline.
13603
13672
  * Sync WASM decoders return K fixed-width source symbols concatenated. */
13604
13673
  routeRecoveredBlock(blockId, data, packetId, catalogTrackName) {
13605
- __privateMethod(this, _MmtpRouter_instances, routeRecoveredBlock_fn).call(this, blockId, data, packetId, void 0, catalogTrackName);
13674
+ __privateMethod(this, _MmtpRouter_instances, routeRecoveredBlock_fn).call(this, blockId, data, packetId, catalogTrackName);
13606
13675
  }
13607
13676
  /** Route a single recovered source symbol through the parser + dedup pipeline.
13608
13677
  * Worker-backed decoders emit one padded source symbol per frame event. */
@@ -13761,8 +13830,9 @@ deriveSbnEsi_fn = function(ssId, packetId) {
13761
13830
  return { sbn: Math.floor(ssId / k), esi: ssId % k };
13762
13831
  };
13763
13832
  deriveRepairSbn_fn = function(ssStart, ssbLength, packetId) {
13764
- if (ssbLength > 0) return Math.floor(ssStart / ssbLength);
13765
- return __privateMethod(this, _MmtpRouter_instances, deriveSbnEsi_fn).call(this, ssStart, packetId).sbn;
13833
+ const k = __privateMethod(this, _MmtpRouter_instances, kForPacket_fn).call(this, packetId);
13834
+ if (ssbLength <= 0 || ssbLength > k || ssStart % k !== 0) return null;
13835
+ return Math.floor(ssStart / k);
13766
13836
  };
13767
13837
  resolveRepairOwnerPacketId_fn = function(packetId, ingress) {
13768
13838
  if (__privateMethod(this, _MmtpRouter_instances, classifyPacketId_fn).call(this, packetId) !== null) return packetId;
@@ -13921,6 +13991,7 @@ processMMTP_fn = function(packet, source, manifestIdentity, manifestAdmitted = f
13921
13991
  );
13922
13992
  }
13923
13993
  const repairSbn = __privateMethod(this, _MmtpRouter_instances, deriveRepairSbn_fn).call(this, parsed.ssStart, parsed.ssbLength, ownerPacketId);
13994
+ if (repairSbn === null) return;
13924
13995
  const repairEsi = __privateMethod(this, _MmtpRouter_instances, kForPacket_fn).call(this, ownerPacketId) + parsed.rsId;
13925
13996
  if (!__privateMethod(this, _MmtpRouter_instances, feedFecTransportSymbol_fn).call(this, ownerPacketId, repairSbn, repairEsi, parsed.repairData, source, catalogTrackNameForPacket(ingress, ownerPacketId))) return;
13926
13997
  const repairCatalogTrackName = catalogTrackNameForPacket(ingress, ownerPacketId);
@@ -13973,7 +14044,7 @@ processMMTP_fn = function(packet, source, manifestIdentity, manifestAdmitted = f
13973
14044
  repairCatalogTrackName
13974
14045
  );
13975
14046
  }
13976
- __privateMethod(this, _MmtpRouter_instances, routeRecoveredBlock_fn).call(this, repairSbn, recovered, ownerPacketId, parsed.ssbLength, catalogTrackNameForPacket(ingress, ownerPacketId));
14047
+ __privateMethod(this, _MmtpRouter_instances, routeRecoveredBlock_fn).call(this, repairSbn, recovered, ownerPacketId, catalogTrackNameForPacket(ingress, ownerPacketId));
13977
14048
  }
13978
14049
  }
13979
14050
  }
@@ -14089,7 +14160,7 @@ processMMTP_fn = function(packet, source, manifestIdentity, manifestAdmitted = f
14089
14160
  catalogTrackName
14090
14161
  );
14091
14162
  }
14092
- __privateMethod(this, _MmtpRouter_instances, routeRecoveredBlock_fn).call(this, sourceFec.sbn, recovered, info.packetId, void 0, catalogTrackName);
14163
+ __privateMethod(this, _MmtpRouter_instances, routeRecoveredBlock_fn).call(this, sourceFec.sbn, recovered, info.packetId, catalogTrackName);
14093
14164
  }
14094
14165
  }
14095
14166
  }
@@ -14437,8 +14508,8 @@ routeDirectRecoveredAudio_fn = function(packetId, data, meta, cb) {
14437
14508
  };
14438
14509
  // ── Private: FEC recovery routing ───────────────────────────────
14439
14510
  /** Route WASM-recovered block through appropriate path based on container kind. */
14440
- routeRecoveredBlock_fn = function(blockId, recovered, ownerPacketId, blockK, catalogTrackName) {
14441
- const k = blockK ?? __privateMethod(this, _MmtpRouter_instances, kForPacket_fn).call(this, ownerPacketId);
14511
+ routeRecoveredBlock_fn = function(blockId, recovered, ownerPacketId, catalogTrackName) {
14512
+ const k = __privateMethod(this, _MmtpRouter_instances, kForPacket_fn).call(this, ownerPacketId);
14442
14513
  const packetSymbolSize = __privateMethod(this, _MmtpRouter_instances, symbolSizeForPacket_fn).call(this, ownerPacketId);
14443
14514
  const symbolSize = packetSymbolSize > 0 ? packetSymbolSize : void 0;
14444
14515
  const result = processFecBlock(recovered, {
@@ -19946,6 +20017,7 @@ export {
19946
20017
  FEC_SBN_MAX,
19947
20018
  FecSourceInfo,
19948
20019
  FecStarvationDetector,
20020
+ JOIN_DEFER_WARN_MS,
19949
20021
  KEYFRAME_LOST_TIMEOUT_SLACK,
19950
20022
  LOCHeaderExtensionId,
19951
20023
  MAX_ENCODING_SYMBOL_ID,