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

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 (62) hide show
  1. package/README.md +29 -17
  2. package/dist/browser-runtime.js +241 -149
  3. package/dist/browser-runtime.js.map +3 -3
  4. package/dist/catalog-fec-params.d.ts +17 -1
  5. package/dist/catalog-fec-params.d.ts.map +1 -1
  6. package/dist/catalog-fec-params.js +15 -0
  7. package/dist/catalog-fec-params.js.map +2 -2
  8. package/dist/cmcd-producer.d.ts +60 -47
  9. package/dist/cmcd-producer.d.ts.map +1 -1
  10. package/dist/cmcd-producer.js +138 -109
  11. package/dist/cmcd-producer.js.map +2 -2
  12. package/dist/delivery-catalog.js +24 -15
  13. package/dist/delivery-catalog.js.map +2 -2
  14. package/dist/delta-fallback-config.js.map +1 -1
  15. package/dist/fec-manager.d.ts +3 -1
  16. package/dist/fec-manager.d.ts.map +1 -1
  17. package/dist/fec-manager.js +26 -4
  18. package/dist/fec-manager.js.map +2 -2
  19. package/dist/fec-worker-setup.d.ts +1 -1
  20. package/dist/fec-worker-setup.js +1 -1
  21. package/dist/fec-worker-setup.js.map +1 -1
  22. package/dist/index.d.ts +2 -2
  23. package/dist/index.d.ts.map +1 -1
  24. package/dist/index.js +5 -1
  25. package/dist/index.js.map +2 -2
  26. package/dist/media-router.js.map +1 -1
  27. package/dist/mmtp-container-parser.d.ts +2 -0
  28. package/dist/mmtp-container-parser.d.ts.map +1 -1
  29. package/dist/mmtp-container-parser.js +4 -0
  30. package/dist/mmtp-container-parser.js.map +2 -2
  31. package/dist/mmtp-container-parser.no-modules.js +4 -0
  32. package/dist/mmtp-container-parser.no-modules.js.map +2 -2
  33. package/dist/mmtp-decode-pipeline.js +4 -0
  34. package/dist/mmtp-decode-pipeline.js.map +2 -2
  35. package/dist/mmtp-router.d.ts.map +1 -1
  36. package/dist/mmtp-router.js +38 -15
  37. package/dist/mmtp-router.js.map +2 -2
  38. package/dist/multicast-manager.js +64 -19
  39. package/dist/multicast-manager.js.map +2 -2
  40. package/dist/multicast-transport.js +24 -15
  41. package/dist/multicast-transport.js.map +2 -2
  42. package/dist/no-modules.js +4 -0
  43. package/dist/no-modules.js.map +2 -2
  44. package/dist/repair-layer-subscriptions.d.ts +6 -2
  45. package/dist/repair-layer-subscriptions.d.ts.map +1 -1
  46. package/dist/repair-layer-subscriptions.js +6 -3
  47. package/dist/repair-layer-subscriptions.js.map +2 -2
  48. package/dist/track-session.d.ts +1 -1
  49. package/dist/track-session.js.map +1 -1
  50. package/dist/wasm-mmtp-container-parser.js +4 -0
  51. package/dist/wasm-mmtp-container-parser.js.map +2 -2
  52. package/package.json +8 -5
  53. package/schemas/catalog.schema.json +4 -0
  54. package/src/catalog-fec-params.ts +36 -1
  55. package/src/cmcd-producer.ts +189 -179
  56. package/src/fec-manager.ts +26 -4
  57. package/src/fec-worker-setup.ts +2 -2
  58. package/src/index.ts +6 -2
  59. package/src/mmtp-container-parser.ts +12 -0
  60. package/src/mmtp-router.ts +21 -0
  61. package/src/repair-layer-subscriptions.ts +13 -5
  62. package/src/track-session.ts +1 -1
@@ -2292,6 +2292,10 @@ var FECManager = class _FECManager {
2292
2292
  // ── Keyframe-protected repair policy (block-policy.ts) ──
2293
2293
  // One classifier per physical stream, built from that stream's geometry.
2294
2294
  this.#classifiers = /* @__PURE__ */ new Map();
2295
+ // A stream without catalog timing has no classifier, so the last RAP it
2296
+ // released stands in for one: "an MPU boundary bounds its RAPs" holds until
2297
+ // a second RAP is released inside one MPU (BLO-52085).
2298
+ this.#noSpanRaps = /* @__PURE__ */ new Map();
2295
2299
  // Geometry a stream-scoped configuration gave one stream (streamKey). ABR
2296
2300
  // rungs can share a packetId with a different framerate or GOP, so the
2297
2301
  // packet-level geometry is only the geometry of a packet whose streams
@@ -2491,6 +2495,7 @@ var FECManager = class _FECManager {
2491
2495
  #orderedFrontiers;
2492
2496
  #orderedPendingBlocks;
2493
2497
  #classifiers;
2498
+ #noSpanRaps;
2494
2499
  #streamGeometries;
2495
2500
  #failedBlocks;
2496
2501
  #resolvedBlockClasses;
@@ -3152,6 +3157,7 @@ var FECManager = class _FECManager {
3152
3157
  };
3153
3158
  const discontinuity = () => {
3154
3159
  this.#classifiers.get(streamKey)?.noteSourceDiscontinuity();
3160
+ this.#noSpanRaps.delete(streamKey);
3155
3161
  this.#highestSbn.set(streamKey, { sbn, atMs: now });
3156
3162
  this.#sourceFrontiers.set(streamKey, { sbn, atMs: now });
3157
3163
  if (psn === void 0) this.#sourceAnchors.delete(streamKey);
@@ -3564,6 +3570,9 @@ var FECManager = class _FECManager {
3564
3570
  }
3565
3571
  if (policyChanged) {
3566
3572
  this.#bumpEpoch(packetId);
3573
+ for (const key of [...this.#noSpanRaps.keys()]) {
3574
+ if (this.#belongsToPacket(key, packetId)) this.#noSpanRaps.delete(key);
3575
+ }
3567
3576
  for (const [key, classifier] of [...this.#classifiers]) {
3568
3577
  if (!this.#belongsToPacket(key, packetId)) continue;
3569
3578
  const stream = this.#parseStreamKey(key);
@@ -3799,6 +3808,7 @@ var FECManager = class _FECManager {
3799
3808
  resetMap(this.#isoLocators);
3800
3809
  resetMap(this.#sourceAnchors);
3801
3810
  this.#classifiers.delete(this.#streamKey(packetId, catalogTrackName));
3811
+ this.#noSpanRaps.delete(this.#streamKey(packetId, catalogTrackName));
3802
3812
  this.#bumpEpoch(packetId);
3803
3813
  for (const [sbn, keys] of this.#blockKeysBySbn) {
3804
3814
  for (const key of keys.keys()) {
@@ -3905,6 +3915,7 @@ var FECManager = class _FECManager {
3905
3915
  dropMap(this.#isoLocators);
3906
3916
  dropMap(this.#sourceAnchors);
3907
3917
  dropMap(this.#classifiers);
3918
+ dropMap(this.#noSpanRaps);
3908
3919
  dropMap(this.#streamGeometries);
3909
3920
  dropSet(this.#pendingWorkerCleanups);
3910
3921
  dropSet(this.#completedBlocks);
@@ -4535,11 +4546,14 @@ var FECManager = class _FECManager {
4535
4546
  /**
4536
4547
  * Whether an MPU boundary still bounds this stream's RAPs (see
4537
4548
  * `BlockClassifier.mpuBoundsRaps`). Gap decisions drop MPU evidence once
4538
- * it does not. A stream with no classifier yet has shown nothing against it.
4549
+ * it does not. A stream with no classifier yet has shown nothing against it,
4550
+ * and neither has a stream without catalog timing until it releases two
4551
+ * RAPs inside one MPU.
4539
4552
  */
4540
4553
  mpuBoundsRapsFor(packetId, catalogTrackName) {
4541
- if (!this.#hasCatalogTimingForPacket(packetId)) return false;
4542
- return this.#classifiers.get(this.#streamKey(packetId, catalogTrackName))?.mpuBoundsRaps ?? true;
4554
+ const streamKey = this.#streamKey(packetId, catalogTrackName);
4555
+ if (!this.#hasCatalogTimingForPacket(packetId)) return this.#noSpanRaps.get(streamKey)?.inside !== true;
4556
+ return this.#classifiers.get(streamKey)?.mpuBoundsRaps ?? true;
4543
4557
  }
4544
4558
  /** A block's class now, or frozen at its resolution. */
4545
4559
  blockClassFor(sbn, packetId, catalogTrackName) {
@@ -4565,7 +4579,14 @@ var FECManager = class _FECManager {
4565
4579
  * invariant (consecutive MPUs exactly one keyframe interval apart).
4566
4580
  */
4567
4581
  noteReleasedRap(packetId, catalogTrackName, mpu, presentationTime, mediaTimescale) {
4568
- if (!this.#trackConfigs.has(packetId) || !this.#hasCatalogTimingForPacket(packetId)) return;
4582
+ if (!this.#trackConfigs.has(packetId)) return;
4583
+ if (!this.#hasCatalogTimingForPacket(packetId)) {
4584
+ const streamKey = this.#streamKey(packetId, catalogTrackName);
4585
+ const last = this.#noSpanRaps.get(streamKey);
4586
+ const inside = last?.inside === true || last?.mpu === mpu && last.presentationTime !== presentationTime;
4587
+ this.#noSpanRaps.set(streamKey, { mpu, presentationTime, inside });
4588
+ return;
4589
+ }
4569
4590
  this.#applyClassifierChanges(
4570
4591
  packetId,
4571
4592
  catalogTrackName,
@@ -5580,6 +5601,7 @@ var FECManager = class _FECManager {
5580
5601
  this.#blockAnchorsMs.clear();
5581
5602
  this.#blockCatalogTrackNames.clear();
5582
5603
  this.#classifiers.clear();
5604
+ this.#noSpanRaps.clear();
5583
5605
  this.#streamGeometries.clear();
5584
5606
  this.#failedBlocks.clear();
5585
5607
  this.#resolvedBlockClasses.clear();
@@ -7057,11 +7079,11 @@ var MOQT_PUBLISHER_PRIORITY = Object.freeze({
7057
7079
  var AMTGatewayManager = class {
7058
7080
  subscriptions = /* @__PURE__ */ new Map();
7059
7081
  messageHandler;
7082
+ messageListenerRegistered = false;
7060
7083
  pendingPackets = /* @__PURE__ */ new Map();
7061
7084
  constructor() {
7062
7085
  this.messageHandler = (event) => {
7063
7086
  const data = event.data;
7064
- console.log(`[AMT] SW message received:`, data.type);
7065
7087
  if (data.type === "UDP_PACKET") {
7066
7088
  this.handlePacket(data);
7067
7089
  } else if (data.type === "SOCKET_ERROR") {
@@ -7070,12 +7092,22 @@ var AMTGatewayManager = class {
7070
7092
  console.log(`[AMT SW Debug] ${data.message}`);
7071
7093
  }
7072
7094
  };
7073
- if (typeof navigator !== "undefined" && navigator.serviceWorker) {
7074
- navigator.serviceWorker.addEventListener("message", this.messageHandler);
7075
- console.log("[AMT] Registered service worker message handler");
7076
- } else {
7077
- console.warn("[AMT] Service worker not available");
7095
+ }
7096
+ ensureMessageListener() {
7097
+ if (this.messageListenerRegistered || typeof navigator === "undefined" || !navigator.serviceWorker || !this.messageHandler) {
7098
+ return;
7078
7099
  }
7100
+ navigator.serviceWorker.addEventListener("message", this.messageHandler);
7101
+ this.messageListenerRegistered = true;
7102
+ console.log("[AMT] Registered service worker message handler");
7103
+ }
7104
+ removeMessageListener() {
7105
+ if (!this.messageListenerRegistered)
7106
+ return;
7107
+ if (this.messageHandler && typeof navigator !== "undefined" && navigator.serviceWorker) {
7108
+ navigator.serviceWorker.removeEventListener("message", this.messageHandler);
7109
+ }
7110
+ this.messageListenerRegistered = false;
7079
7111
  }
7080
7112
  /**
7081
7113
  * Subscribe via AMT tunnel
@@ -7085,6 +7117,7 @@ var AMTGatewayManager = class {
7085
7117
  if (config.protected) {
7086
7118
  throw new Error("AMTGatewayManager refuses a protected join: it reports no per-datagram sender (multicast-02 7.2.7)");
7087
7119
  }
7120
+ this.ensureMessageListener();
7088
7121
  const keyStr = this.getKeyString(key);
7089
7122
  if (this.subscriptions.has(keyStr)) {
7090
7123
  console.log(`[AMT] Reusing existing subscription to ${keyStr}`);
@@ -7120,6 +7153,8 @@ var AMTGatewayManager = class {
7120
7153
  this.subscriptions.delete(keyStr);
7121
7154
  this.pendingPackets.delete(subscription.subscriptionId);
7122
7155
  }
7156
+ if (this.subscriptions.size === 0)
7157
+ this.removeMessageListener();
7123
7158
  throw new Error(`Failed to subscribe via AMT: ${error instanceof Error ? error.message : String(error)}`);
7124
7159
  }
7125
7160
  }
@@ -7141,6 +7176,8 @@ var AMTGatewayManager = class {
7141
7176
  } finally {
7142
7177
  this.subscriptions.delete(handle);
7143
7178
  this.pendingPackets.delete(subscription.subscriptionId);
7179
+ if (this.subscriptions.size === 0)
7180
+ this.removeMessageListener();
7144
7181
  }
7145
7182
  }
7146
7183
  /**
@@ -7157,10 +7194,8 @@ var AMTGatewayManager = class {
7157
7194
  if (!subscriptionId)
7158
7195
  return;
7159
7196
  const subscription = this.subscriptions.get(subscriptionId);
7160
- if (!subscription) {
7161
- console.warn(`[AMT] Received packet for unknown subscription: ${subscriptionId}`);
7197
+ if (!subscription)
7162
7198
  return;
7163
- }
7164
7199
  if (!subscription.handshakeComplete) {
7165
7200
  const queue = this.pendingPackets.get(subscriptionId);
7166
7201
  const packetData2 = data;
@@ -7190,19 +7225,15 @@ var AMTGatewayManager = class {
7190
7225
  if (!subscriptionId)
7191
7226
  return;
7192
7227
  const subscription = this.subscriptions.get(subscriptionId);
7193
- if (!subscription) {
7194
- console.warn(`[AMT] Received error for unknown subscription: ${subscriptionId}`);
7228
+ if (!subscription)
7195
7229
  return;
7196
- }
7197
7230
  subscription.config.onError(new Error(error || "Unknown error"));
7198
7231
  }
7199
7232
  /**
7200
7233
  * Cleanup on destroy
7201
7234
  */
7202
7235
  destroy() {
7203
- if (this.messageHandler && typeof navigator !== "undefined" && navigator.serviceWorker) {
7204
- navigator.serviceWorker.removeEventListener("message", this.messageHandler);
7205
- }
7236
+ this.removeMessageListener();
7206
7237
  const handles = Array.from(this.subscriptions.keys());
7207
7238
  for (const handle of handles) {
7208
7239
  this.unsubscribe(handle).catch((err) => {
@@ -14622,6 +14653,10 @@ var MmtpContainerParser = class _MmtpContainerParser {
14622
14653
  }
14623
14654
  this.#emitFrame(data, meta);
14624
14655
  }
14656
+ /** Whether FT=1 metadata already names this sample, without claiming it. */
14657
+ hasAuthoredTimingFor(packetId, mpuSequenceNumber, movieFragmentSequenceNumber, sampleNumber, catalogTrackName) {
14658
+ return this.#timingResolver(this.#streamKey(packetId, catalogTrackName), packetId, false)?.sampleTiming(mpuSequenceNumber, movieFragmentSequenceNumber, sampleNumber) != null;
14659
+ }
14625
14660
  /**
14626
14661
  * Claim and return the authored timing for a recovered sample. Recovery does
14627
14662
  * not pass through the ordinary `feed()` completion path, so it must retire
@@ -21145,6 +21180,16 @@ var MmtpRouter = class {
21145
21180
  }
21146
21181
  );
21147
21182
  }
21183
+ result = {
21184
+ ...result,
21185
+ symbols: result.symbols.filter((sym) => sym.fragmentType !== 2 || sym.fragment.fragmentationIndicator !== 1 || sym.fragment.movieFragmentSequenceNumber === void 0 || sym.fragment.sampleNumber === void 0 || this.#parser.hasAuthoredTimingFor(
21186
+ ownerPacketId,
21187
+ sym.fragment.mpuSequenceNumber,
21188
+ sym.fragment.movieFragmentSequenceNumber,
21189
+ sym.fragment.sampleNumber,
21190
+ catalogTrackName
21191
+ ))
21192
+ };
21148
21193
  const gated = this.#isFecTrack(ownerPacketId);
21149
21194
  const assembler = this.#mseAssemblerFor(ownerPacketId, catalogTrackName);
21150
21195
  if (assembler) {