@blockcast/mmt-container 2.0.0-main.609e08d7e29f → 2.0.1-main.0f2de6af487b

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.
package/README.md CHANGED
@@ -277,7 +277,7 @@ per-commit `0.1.0-main.<sha>` prerelease from `stamp-prerelease.mjs`), and a
277
277
  > Superseded by BLO-40665. Every package is now bumped in lockstep by a `v*`
278
278
  > tag and the sibling ranges move with it, so the objection above no longer
279
279
  > applies -- it was an argument against bumping *one* package. The set is at
280
- > `2.0.0`; see `RELEASES.md`.
280
+ > `2.0.1`; see `RELEASES.md`.
281
281
 
282
282
  #### Track-role convention (Phase 14)
283
283
 
@@ -22647,6 +22647,14 @@ var MmtpRouter = class {
22647
22647
  #packetIdRoles = /* @__PURE__ */ new Map();
22648
22648
  /** Explicit catalog ownership for dedicated repair-track packet IDs. */
22649
22649
  #repairOwnersByPacketId = /* @__PURE__ */ new Map();
22650
+ /** Keyframe-overlay repair PID → the source its FEC instance protects
22651
+ * (draft-ramadan-mmt-fec-02 7.4.1). Never an owner in
22652
+ * #repairOwnersByPacketId: the overlay is decoded as its own instance. */
22653
+ #overlayLegs = /* @__PURE__ */ new Map();
22654
+ /** Keyframe-overlay repair track names, which name a leg too: a catalog
22655
+ * may state no PID for it (learned at subscribe time). */
22656
+ #overlayRepairNames = /* @__PURE__ */ new Map();
22657
+ #overlayStats = { repairDropped: 0, ownerRefused: 0 };
22650
22658
  /** Callback fired when a delta-track FEC block completes or fails. */
22651
22659
  #onDeltaBlockCompleteCb = null;
22652
22660
  // Debug counters
@@ -23165,13 +23173,53 @@ var MmtpRouter = class {
23165
23173
  /** Replace catalog-derived repair ownership before a catalog reconfiguration. */
23166
23174
  clearRepairPacketIdOwners() {
23167
23175
  this.#repairOwnersByPacketId.clear();
23176
+ this.#overlayLegs.clear();
23177
+ this.#overlayRepairNames.clear();
23168
23178
  }
23169
- /** Bind one dedicated repair-track PID to its authoritative media PID. */
23179
+ /** Bind one dedicated repair-track PID to its authoritative media PID. A
23180
+ * known keyframe-overlay PID is refused, and counted: its symbols belong to
23181
+ * the overlay instance, never to the source's base decoder (mmt-02 7.4.4). */
23170
23182
  setRepairPacketIdOwner(repairPacketId, ownerPacketId) {
23183
+ if (this.#overlayLegs.has(repairPacketId)) {
23184
+ this.#overlayStats.ownerRefused++;
23185
+ return;
23186
+ }
23171
23187
  const owners = this.#repairOwnersByPacketId.get(repairPacketId) ?? /* @__PURE__ */ new Set();
23172
23188
  owners.add(ownerPacketId);
23173
23189
  this.#repairOwnersByPacketId.set(repairPacketId, owners);
23174
23190
  }
23191
+ /**
23192
+ * Record a keyframe-overlay repair leg (draft-ramadan-mmt-fec-02 7.4): its
23193
+ * PID, the source it protects, and its repair track name. Either one names
23194
+ * the leg, so a catalog that states no PID (learned at subscribe time) is
23195
+ * covered by the name. Overlay repair is then never resolved to a base
23196
+ * owner, on multicast or MoQ. Cleared with the catalog owners
23197
+ * (`clearRepairPacketIdOwners`).
23198
+ */
23199
+ setKeyframeOverlayLeg(repairPacketId, leg) {
23200
+ if (leg.repairTrackName !== void 0) this.#overlayRepairNames.set(leg.repairTrackName, { ...leg });
23201
+ if (repairPacketId === void 0) return;
23202
+ this.#repairOwnersByPacketId.delete(repairPacketId);
23203
+ this.#overlayLegs.set(repairPacketId, { ...leg });
23204
+ }
23205
+ /** The keyframe-overlay leg registered on `repairPacketId`, if any. */
23206
+ keyframeOverlayLeg(repairPacketId) {
23207
+ return this.#overlayLegs.get(repairPacketId);
23208
+ }
23209
+ /** Overlay repair dropped for want of an overlay instance, and base-owner
23210
+ * registrations refused on an overlay PID. */
23211
+ get keyframeOverlayStats() {
23212
+ return { ...this.#overlayStats };
23213
+ }
23214
+ #isOverlayRepair(repairPacketId, repairTrackName) {
23215
+ return this.#overlayLegs.has(repairPacketId) || repairTrackName !== void 0 && this.#overlayRepairNames.has(repairTrackName);
23216
+ }
23217
+ /** Overlay repair has no decoder until the overlay instance lands
23218
+ * (BLO-52077 PR 2): drop it before any base owner, locator or decoder. */
23219
+ #dropOverlayRepair(repairPacketId) {
23220
+ this.#overlayStats.repairDropped++;
23221
+ debugRouter(`[MmtpRouter] Dropping keyframe-overlay repair pid=${repairPacketId}: no overlay instance`);
23222
+ }
23175
23223
  /**
23176
23224
  * Register callback fired when a delta-track FEC block completes or fails.
23177
23225
  *
@@ -23595,6 +23643,10 @@ var MmtpRouter = class {
23595
23643
  return;
23596
23644
  }
23597
23645
  const repairPacketId = header.packetId;
23646
+ if (this.#isOverlayRepair(repairPacketId, repairTrackName)) {
23647
+ this.#dropOverlayRepair(repairPacketId);
23648
+ return;
23649
+ }
23598
23650
  const hintedOwner = ownerPacketIdHint != null && this.#classifyPacketId(ownerPacketIdHint) !== null ? ownerPacketIdHint : null;
23599
23651
  const ownerPacketId = hintedOwner ?? (this.#classifyPacketId(repairPacketId) !== null ? repairPacketId : null);
23600
23652
  if (ownerPacketId === null) {
@@ -23970,6 +24022,13 @@ var MmtpRouter = class {
23970
24022
  this.#packetIdRoles.delete(packetId);
23971
24023
  this.#catalogTrackNamesByPacketId.delete(packetId);
23972
24024
  this.#repairOwnersByPacketId.delete(packetId);
24025
+ this.#overlayLegs.delete(packetId);
24026
+ for (const [repairPacketId, leg] of [...this.#overlayLegs]) {
24027
+ if (leg.sourcePacketId === packetId) this.#overlayLegs.delete(repairPacketId);
24028
+ }
24029
+ for (const [name, leg] of [...this.#overlayRepairNames]) {
24030
+ if (leg.sourcePacketId === packetId) this.#overlayRepairNames.delete(name);
24031
+ }
23973
24032
  delete this.#pidCounts[packetId];
23974
24033
  delete this.#repairPidCounts[packetId];
23975
24034
  delete this.#moqRepairPidCounts[packetId];
@@ -24208,6 +24267,8 @@ var MmtpRouter = class {
24208
24267
  this.#parserDiagnostics.clear();
24209
24268
  this.#catalogTrackNamesByPacketId.clear();
24210
24269
  this.#repairOwnersByPacketId.clear();
24270
+ this.#overlayLegs.clear();
24271
+ this.#overlayRepairNames.clear();
24211
24272
  for (const { receiver } of this.#provenance.values()) receiver.release();
24212
24273
  this.#provenance.clear();
24213
24274
  this.#provenanceStreamFlow.clear();
@@ -24563,6 +24624,10 @@ var MmtpRouter = class {
24563
24624
  }
24564
24625
  }
24565
24626
  if (info.isRepair) {
24627
+ if (this.#isOverlayRepair(info.packetId, catalogTrackNameForPacket(ingress, info.packetId))) {
24628
+ this.#dropOverlayRepair(info.packetId);
24629
+ return;
24630
+ }
24566
24631
  const ownerPacketId = this.#resolveRepairOwnerPacketId(info.packetId, ingress);
24567
24632
  if (ownerPacketId != null && this.#fecManager && hasAmbiguousCatalogTrackName(ingress, ownerPacketId)) return;
24568
24633
  const repair = this.#readRepairObject(
@@ -27193,9 +27258,7 @@ function repairTrackNamesBySource(tracks) {
27193
27258
  }
27194
27259
  function boundRepairNames(binding) {
27195
27260
  if (binding?.status !== "ok") return [];
27196
- const names = binding.layers.map((layer) => layer.repairTrackName);
27197
- if (binding.overlay) names.push(binding.overlay.repairTrackName);
27198
- return names;
27261
+ return binding.layers.map((layer) => layer.repairTrackName);
27199
27262
  }
27200
27263
  var MulticastManager = class {
27201
27264
  #fec;
@@ -27961,7 +28024,11 @@ var MulticastManager = class {
27961
28024
  console.warn("[MulticastManager] repair group release failed", error);
27962
28025
  }
27963
28026
  };
27964
- const repairOwners = endpoints.flatMap((endpoint) => endpoint.repairOwners);
28027
+ const repairOwners = endpoints.flatMap((endpoint) => endpoint.repairOwners.map((owner) => owner.instance !== "keyframe" ? owner : {
28028
+ ...owner,
28029
+ ownerTrackName: endpoint.tracks.find((t) => t.packetId === owner.ownerPacketId)?.name,
28030
+ repairTrackName: endpoint.tracks.find((t) => t.packetId === owner.packetId)?.name
28031
+ }));
27965
28032
  this.#applyRepairOwners(repairOwners);
27966
28033
  for (const endpoint of endpoints) {
27967
28034
  const ingress = {
@@ -28009,9 +28076,17 @@ var MulticastManager = class {
28009
28076
  return release;
28010
28077
  }
28011
28078
  #applyRepairOwners(owners) {
28012
- for (const { packetId, ownerPacketId } of owners) {
28079
+ for (const { packetId, ownerPacketId, instance, ownerTrackName, repairTrackName } of owners) {
28013
28080
  this.#router.setPacketIdRole(packetId, "repair");
28014
- this.#router.setRepairPacketIdOwner(packetId, ownerPacketId);
28081
+ if (instance === "keyframe") {
28082
+ this.#router.setKeyframeOverlayLeg(packetId, {
28083
+ sourcePacketId: ownerPacketId,
28084
+ ...ownerTrackName === void 0 ? {} : { sourceCatalogTrackName: ownerTrackName },
28085
+ ...repairTrackName === void 0 ? {} : { repairTrackName }
28086
+ });
28087
+ } else {
28088
+ this.#router.setRepairPacketIdOwner(packetId, ownerPacketId);
28089
+ }
28015
28090
  }
28016
28091
  }
28017
28092
  // ---------------------------------------------------------------------------
@@ -28275,8 +28350,8 @@ var MulticastManager = class {
28275
28350
  this.#fec.setMulticastSymbolDelivery(delivery);
28276
28351
  }
28277
28352
  /** Process a repair packet from MoQ (reliable QUIC). */
28278
- processRepairFromMoQ(packet, ownerPacketId, catalogTrackName) {
28279
- this.#router.feedRepairPacket(packet, ownerPacketId, catalogTrackName);
28353
+ processRepairFromMoQ(packet, ownerPacketId, catalogTrackName, repairTrackName) {
28354
+ this.#router.feedRepairPacket(packet, ownerPacketId, catalogTrackName, repairTrackName);
28280
28355
  }
28281
28356
  /** Route a Worker-recovered FEC block through the MMTP parser + dedup pipeline. */
28282
28357
  routeRecoveredBlock(blockId, data, packetId, catalogTrackName) {
@@ -28328,7 +28403,10 @@ var MulticastManager = class {
28328
28403
  const ownedRepairNames = /* @__PURE__ */ new Set();
28329
28404
  const claimed = /* @__PURE__ */ new Map();
28330
28405
  for (const binding of bindings.values()) {
28331
- for (const name of boundRepairNames(binding)) claimed.set(name, (claimed.get(name) ?? 0) + 1);
28406
+ const overlay = binding.status === "ok" ? binding.overlay?.repairTrackName : void 0;
28407
+ for (const name of [...boundRepairNames(binding), ...overlay === void 0 ? [] : [overlay]]) {
28408
+ claimed.set(name, (claimed.get(name) ?? 0) + 1);
28409
+ }
28332
28410
  }
28333
28411
  for (const [name, count] of claimed) if (count === 1) ownedRepairNames.add(name);
28334
28412
  const repairOwnerGroup = /* @__PURE__ */ new Map();
@@ -28459,6 +28537,14 @@ var MulticastManager = class {
28459
28537
  this.#router.setRepairPacketIdOwner(repairTrack.packetId, track.packetId);
28460
28538
  }
28461
28539
  }
28540
+ const overlayName = binding.overlay?.repairTrackName;
28541
+ if (overlayName !== void 0 && ownedRepairNames.has(overlayName)) {
28542
+ this.#router.setKeyframeOverlayLeg(catalogTracksByName.get(overlayName)?.packetId, {
28543
+ sourcePacketId: track.packetId,
28544
+ sourceCatalogTrackName: track.name,
28545
+ repairTrackName: overlayName
28546
+ });
28547
+ }
28462
28548
  }
28463
28549
  const wasmDecoder = fecDecoders.get(i);
28464
28550
  if (track.packetId !== void 0 && wasmDecoder !== void 0) {