@blockcast/mmt-container 2.0.1 → 2.1.0-main.5b10b5454faf

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.
@@ -8423,6 +8423,14 @@ function isRouterDebugEnabled() {
8423
8423
  function debugRouter(message) {
8424
8424
  if (isRouterDebugEnabled()) console.debug(message);
8425
8425
  }
8426
+ var KEYFRAME_OVERLAY_EXTENSION_TYPE = 35585;
8427
+ function keyframeOverlaySsId(header) {
8428
+ const ext = header.extension;
8429
+ if (ext?.type !== KEYFRAME_OVERLAY_EXTENSION_TYPE || ext.data.length !== 4) return void 0;
8430
+ return new DataView(ext.data.buffer, ext.data.byteOffset, 4).getUint32(0);
8431
+ }
8432
+ var OVERLAY_OFFSET_MPUS = 16;
8433
+ var OVERLAY_MOQ_HORIZON_BLOCKS = 64;
8426
8434
  function assertStreamIdentity(role, stream) {
8427
8435
  if (stream === void 0 || !Number.isInteger(stream.packetId) || stream.packetId <= MMTP_SIGNALING_PACKET_ID || typeof stream.catalogTrackName !== "string" || stream.catalogTrackName.length === 0) {
8428
8436
  throw new Error(
@@ -8639,6 +8647,26 @@ var MmtpRouter = class {
8639
8647
  #packetIdRoles = /* @__PURE__ */ new Map();
8640
8648
  /** Explicit catalog ownership for dedicated repair-track packet IDs. */
8641
8649
  #repairOwnersByPacketId = /* @__PURE__ */ new Map();
8650
+ /** Keyframe-overlay repair PID → the source its FEC instance protects
8651
+ * (draft-ramadan-mmt-fec-02 7.4.1). Never an owner in
8652
+ * #repairOwnersByPacketId: the overlay is decoded as its own instance. */
8653
+ #overlayLegs = /* @__PURE__ */ new Map();
8654
+ /** Keyframe-overlay repair track names, which name a leg too: a catalog
8655
+ * may state no PID for it (learned at subscribe time). */
8656
+ #overlayRepairNames = /* @__PURE__ */ new Map();
8657
+ /** Installed overlay instances, by overlay repair PID (opt-in only). */
8658
+ #overlays = /* @__PURE__ */ new Map();
8659
+ #overlayRecoveredObservers = /* @__PURE__ */ new Set();
8660
+ /** Streams whose recovered sequence the base path ever re-stamped. */
8661
+ #restampedStreams = /* @__PURE__ */ new Set();
8662
+ #overlayStats = {
8663
+ repairDropped: 0,
8664
+ ownerRefused: 0,
8665
+ recovered: 0,
8666
+ restarts: 0,
8667
+ unsequencedDropped: 0,
8668
+ protectedDropped: 0
8669
+ };
8642
8670
  /** Callback fired when a delta-track FEC block completes or fails. */
8643
8671
  #onDeltaBlockCompleteCb = null;
8644
8672
  // Debug counters
@@ -8835,6 +8863,7 @@ var MmtpRouter = class {
8835
8863
  this.#fecManager.onSourceDiscontinuity = (packetId, catalogTrackName) => {
8836
8864
  this.#parser.markFecDiscontinuity(packetId, catalogTrackName);
8837
8865
  this.#requestFecPoke(packetId, catalogTrackName);
8866
+ this.resetKeyframeOverlays(packetId, catalogTrackName);
8838
8867
  };
8839
8868
  this.#fecManager.onBlockComplete = (sbn, packetId, catalogTrackName) => {
8840
8869
  this.#mseAssemblerFor(packetId, catalogTrackName)?.completeBlock(sbn);
@@ -9157,13 +9186,287 @@ var MmtpRouter = class {
9157
9186
  /** Replace catalog-derived repair ownership before a catalog reconfiguration. */
9158
9187
  clearRepairPacketIdOwners() {
9159
9188
  this.#repairOwnersByPacketId.clear();
9189
+ this.#overlayLegs.clear();
9190
+ this.#overlayRepairNames.clear();
9160
9191
  }
9161
- /** Bind one dedicated repair-track PID to its authoritative media PID. */
9192
+ /** Bind one dedicated repair-track PID to its authoritative media PID. A
9193
+ * known keyframe-overlay PID is refused, and counted: its symbols belong to
9194
+ * the overlay instance, never to the source's base decoder (mmt-02 7.4.4). */
9162
9195
  setRepairPacketIdOwner(repairPacketId, ownerPacketId) {
9196
+ if (this.#overlayLegs.has(repairPacketId)) {
9197
+ this.#overlayStats.ownerRefused++;
9198
+ return;
9199
+ }
9163
9200
  const owners = this.#repairOwnersByPacketId.get(repairPacketId) ?? /* @__PURE__ */ new Set();
9164
9201
  owners.add(ownerPacketId);
9165
9202
  this.#repairOwnersByPacketId.set(repairPacketId, owners);
9166
9203
  }
9204
+ /**
9205
+ * Record a keyframe-overlay repair leg (draft-ramadan-mmt-fec-02 7.4): its
9206
+ * PID, the source it protects, and its repair track name. Either one names
9207
+ * the leg, so a catalog that states no PID (learned at subscribe time) is
9208
+ * covered by the name. Overlay repair is then never resolved to a base
9209
+ * owner, on multicast or MoQ. Cleared with the catalog owners
9210
+ * (`clearRepairPacketIdOwners`).
9211
+ */
9212
+ setKeyframeOverlayLeg(repairPacketId, leg) {
9213
+ if (leg.repairTrackName !== void 0) this.#overlayRepairNames.set(leg.repairTrackName, { ...leg });
9214
+ if (repairPacketId === void 0) return;
9215
+ this.#repairOwnersByPacketId.delete(repairPacketId);
9216
+ this.#overlayLegs.set(repairPacketId, { ...leg });
9217
+ }
9218
+ /** The keyframe-overlay leg registered on `repairPacketId`, if any. */
9219
+ keyframeOverlayLeg(repairPacketId) {
9220
+ return this.#overlayLegs.get(repairPacketId);
9221
+ }
9222
+ /**
9223
+ * Overlay counters: repair dropped for want of an installed instance, base
9224
+ * owners refused on an overlay PID, overlay blocks recovered, overlay
9225
+ * restarts, and recovered packets dropped for want of a wire sequence or
9226
+ * for a MoQ symbol on a protected flow (multicast-02 7.2.7).
9227
+ */
9228
+ get keyframeOverlayStats() {
9229
+ return { ...this.#overlayStats };
9230
+ }
9231
+ #isOverlayRepair(repairPacketId, repairTrackName) {
9232
+ return this.#overlayLegs.has(repairPacketId) || repairTrackName !== void 0 && this.#overlayRepairNames.has(repairTrackName);
9233
+ }
9234
+ /**
9235
+ * Install the keyframe overlay decoded on `fec` for a known overlay leg
9236
+ * (draft-ramadan-mmt-fec-02 7.4.4: decoded independently of the base). `fec`
9237
+ * holds the overlay stream under `repairPacketId` and `streamName`. Replaces
9238
+ * an earlier install on the same leg; the returned disposer removes only
9239
+ * this one.
9240
+ */
9241
+ installKeyframeOverlay(repairPacketId, fec, k, streamName) {
9242
+ const leg = this.#overlayLegs.get(repairPacketId);
9243
+ if (!leg) throw new Error(`[MmtpRouter] packetId=${repairPacketId} is no known keyframe-overlay leg`);
9244
+ const inst = {
9245
+ repairPacketId,
9246
+ streamName,
9247
+ leg: { ...leg },
9248
+ fec,
9249
+ k,
9250
+ offsets: /* @__PURE__ */ new Map(),
9251
+ moqBlocks: /* @__PURE__ */ new Set()
9252
+ };
9253
+ this.#overlays.set(repairPacketId, inst);
9254
+ return () => {
9255
+ if (this.#overlays.get(repairPacketId) === inst) this.#overlays.delete(repairPacketId);
9256
+ };
9257
+ }
9258
+ /**
9259
+ * Observe each base block whose RAP MFU fragments a keyframe-overlay
9260
+ * recovery supplied, with the keyframe's FT=0 and FT=1 held
9261
+ * (fec-multicast-00 §6.6). The block is named by the recovered packets'
9262
+ * restored wire sequence through the base SS_ID offset, never by an overlay
9263
+ * coordinate. Called synchronously from the recovered-block route.
9264
+ */
9265
+ addKeyframeOverlayRecoveredObserver(observer) {
9266
+ this.#overlayRecoveredObservers.add(observer);
9267
+ return () => {
9268
+ this.#overlayRecoveredObservers.delete(observer);
9269
+ };
9270
+ }
9271
+ /** Whether an overlay is installed on `repairPacketId`. */
9272
+ hasKeyframeOverlay(repairPacketId) {
9273
+ return this.#overlays.has(repairPacketId);
9274
+ }
9275
+ /**
9276
+ * Start a new epoch of every overlay protecting `sourcePacketId` (its
9277
+ * source restarted): empty overlay stream, no coverage, no offsets.
9278
+ */
9279
+ resetKeyframeOverlays(sourcePacketId, catalogTrackName) {
9280
+ for (const inst of this.#overlaysForSource(sourcePacketId, catalogTrackName)) this.#resetOverlay(inst);
9281
+ }
9282
+ #resetOverlay(inst) {
9283
+ inst.fec.resetTrack(inst.repairPacketId, inst.streamName);
9284
+ inst.last = void 0;
9285
+ inst.offsets.clear();
9286
+ inst.moqBlocks.clear();
9287
+ }
9288
+ #overlaysForSource(sourcePacketId, catalogTrackName) {
9289
+ return [...this.#overlays.values()].filter(({ leg }) => leg.sourcePacketId === sourcePacketId && (catalogTrackName === void 0 || leg.sourceCatalogTrackName === void 0 || leg.sourceCatalogTrackName === catalogTrackName));
9290
+ }
9291
+ /**
9292
+ * The overlay instance a repair packet belongs to, by the track it arrived
9293
+ * on (7.4.2: its PID or its repair track name); null for overlay repair
9294
+ * with no instance installed; undefined for repair of no overlay.
9295
+ */
9296
+ #overlayRepairTarget(repairPacketId, repairTrackName) {
9297
+ const inst = this.#overlays.get(repairPacketId) ?? (repairTrackName === void 0 ? void 0 : [...this.#overlays.values()].find(({ leg }) => leg.repairTrackName === repairTrackName));
9298
+ if (inst) return inst;
9299
+ return this.#isOverlayRepair(repairPacketId, repairTrackName) ? null : void 0;
9300
+ }
9301
+ /** Overlay repair goes to its own instance, never to a base owner, locator
9302
+ * or decoder; with none installed it is dropped and counted. */
9303
+ #routeOverlayRepair(inst, repairPacketId, packet, header, source) {
9304
+ const repair = inst ? parseMoqRepairFrameWithHeader(packet, header) : null;
9305
+ if (!inst || !repair) {
9306
+ this.#overlayStats.repairDropped++;
9307
+ debugRouter(`[MmtpRouter] Dropping keyframe-overlay repair pid=${repairPacketId}: no overlay instance`);
9308
+ return;
9309
+ }
9310
+ const { fec, repairPacketId: pid, streamName: name } = inst;
9311
+ if (!fec.admitRepairBlockLength(pid, repair.ssbLength, repair.rsbLength, repair.rsId)) return;
9312
+ if (!fec.admitRepairSymbolSize(pid, repair.ssStart, repair.ssbLength, repair.rsId, repair.symbolData.byteLength)) return;
9313
+ const placement = fec.placeIsoRepair(pid, name, repair.ssStart, repair.ssbLength);
9314
+ if (placement.status !== "placed") return;
9315
+ const blockStart = placement.block.start;
9316
+ if (placement.span !== void 0) this.#replayOverlaySources(inst, placement.span.block.start, placement.span.placed);
9317
+ this.#replayOverlaySources(inst, blockStart, placement.placed);
9318
+ if (source === "moq") this.#noteOverlayMoqBlock(inst, blockStart);
9319
+ const alreadyCompleted = fec.isBlockCompleted(blockStart, pid, name);
9320
+ const recovered = fec.feedRepairSymbol(pid, repair.ssStart, repair.ssbLength, repair.rsId, repair.symbolData, name);
9321
+ if (recovered && !alreadyCompleted) this.#routeOverlayRecovered(inst, blockStart, recovered);
9322
+ }
9323
+ #replayOverlaySources(inst, blockStart, placed) {
9324
+ const { fec, repairPacketId: pid, streamName: name } = inst;
9325
+ for (const { ssId, payload } of placed) {
9326
+ if (payload.source === "moq") this.#noteOverlayMoqBlock(inst, blockStart);
9327
+ if (payload.oversized) continue;
9328
+ const alreadyCompleted = fec.isBlockCompleted(blockStart, pid, name);
9329
+ const recovered = fec.feedSourceSymbol(pid, ssId, payload.symbolData, name);
9330
+ if (recovered && !alreadyCompleted) this.#routeOverlayRecovered(inst, blockStart, recovered);
9331
+ }
9332
+ }
9333
+ /**
9334
+ * A source packet that carries a 0x8B01 overlay SS_ID is also a symbol of
9335
+ * every overlay installed for its source, at that SS_ID, with the bytes the
9336
+ * base protects: the packet minus its 4-byte SS_ID trailer, extension
9337
+ * included (7.1, 7.4.3).
9338
+ */
9339
+ #feedOverlaySource(packetId, catalogTrackName, packet, header, sequenceNumber, rapFlag, source) {
9340
+ if (this.#overlays.size === 0) return;
9341
+ const ssId = keyframeOverlaySsId(header);
9342
+ if (ssId === void 0) return;
9343
+ for (const inst of this.#overlaysForSource(packetId, catalogTrackName)) {
9344
+ this.#noteOverlaySsId(inst, ssId, sequenceNumber);
9345
+ const identity = MmtpContainerParser.sourcePacketIdentity(packet, header);
9346
+ if (identity !== null) {
9347
+ inst.offsets.delete(identity.mpuSequenceNumber);
9348
+ inst.offsets.set(identity.mpuSequenceNumber, ssId - sequenceNumber >>> 0);
9349
+ if (inst.offsets.size > OVERLAY_OFFSET_MPUS) inst.offsets.delete(inst.offsets.keys().next().value);
9350
+ }
9351
+ const { fec, repairPacketId: pid, streamName: name } = inst;
9352
+ const symbolData = packet.subarray(0, packet.length - 4);
9353
+ const oversized = !fec.isoSourceFits(pid, symbolData.length);
9354
+ const coords = fec.locateIsoSource(pid, name, ssId);
9355
+ if (coords === null) {
9356
+ fec.holdIsoSource(pid, name, ssId, {
9357
+ symbolData,
9358
+ source,
9359
+ oversized,
9360
+ evidence: sourceSymbolEvidence(packet, header, rapFlag, sequenceNumber)
9361
+ });
9362
+ continue;
9363
+ }
9364
+ if (source === "moq") this.#noteOverlayMoqBlock(inst, coords.sbn);
9365
+ if (oversized) continue;
9366
+ const alreadyCompleted = fec.isBlockCompleted(coords.sbn, pid, name);
9367
+ const recovered = fec.feedSourceSymbol(pid, ssId, symbolData, name);
9368
+ if (recovered && !alreadyCompleted) this.#routeOverlayRecovered(inst, coords.sbn, recovered);
9369
+ }
9370
+ }
9371
+ #noteOverlayMoqBlock(inst, blockStart) {
9372
+ inst.moqBlocks.add(blockStart);
9373
+ const newest = inst.last?.ssId;
9374
+ if (newest === void 0) return;
9375
+ for (const start of inst.moqBlocks) {
9376
+ if ((newest - start | 0) > OVERLAY_MOQ_HORIZON_BLOCKS * inst.k) inst.moqBlocks.delete(start);
9377
+ }
9378
+ }
9379
+ /**
9380
+ * An overlay SS_ID inconsistent with the source's packet_sequence_number
9381
+ * advance since the last one is an overlay restart (an FFmpeg muxer start,
9382
+ * a bridge swap that keeps psn continuous): it moved the other way, or
9383
+ * further than the psn advance plus K_overlay. Reordering keeps the two
9384
+ * consistent and is no restart.
9385
+ */
9386
+ #noteOverlaySsId(inst, ssId, psn) {
9387
+ const last = inst.last;
9388
+ if (last !== void 0) {
9389
+ const dSs = ssId - last.ssId | 0;
9390
+ const dPsn = psn - last.psn | 0;
9391
+ const consistent = dSs === 0 ? dPsn === 0 : Math.sign(dSs) === Math.sign(dPsn) && Math.abs(dSs) <= Math.abs(dPsn) + inst.k;
9392
+ if (!consistent) {
9393
+ this.#overlayStats.restarts++;
9394
+ this.#resetOverlay(inst);
9395
+ } else if (dPsn < 0) {
9396
+ return;
9397
+ }
9398
+ }
9399
+ inst.last = { ssId, psn };
9400
+ }
9401
+ /**
9402
+ * Route an overlay-recovered block's packets as source media of the
9403
+ * overlay's source (7.2: an overlay coordinate yields no base SS_ID). No
9404
+ * base-SS_ID derivation runs: no release-gate anchor, no parse ledger, no
9405
+ * base re-stamp, no block advance. Each packet's wire sequence is restored
9406
+ * from its keyframe's own offset; without one, a stream the base never
9407
+ * re-stamped keeps the header's sequence and any other drops the packet.
9408
+ */
9409
+ #routeOverlayRecovered(inst, blockStart, recovered) {
9410
+ const { fec, repairPacketId: pid, streamName: name } = inst;
9411
+ fec.markBlockCompleted(blockStart, pid, name);
9412
+ const sourcePacketId = inst.leg.sourcePacketId;
9413
+ const sourceName = inst.leg.sourceCatalogTrackName;
9414
+ if (this.#classifyPacketId(sourcePacketId) === null) return;
9415
+ const receiver = this.#provenanceFor(sourcePacketId, sourceName);
9416
+ if (receiver !== void 0 && inst.moqBlocks.has(blockStart)) {
9417
+ this.#overlayStats.protectedDropped++;
9418
+ return;
9419
+ }
9420
+ const blockProfile = fec.blockProfileForPacket(pid);
9421
+ const symbolSize = fec.symbolSizeForPacket(pid);
9422
+ if (blockProfile === void 0 || symbolSize === void 0 || !(symbolSize > 0)) return;
9423
+ const result = processFecBlock(recovered, { symbolSize, ownerPacketId: sourcePacketId, blockProfile });
9424
+ const streamKey = this.#mediaStreamKey(sourcePacketId, sourceName);
9425
+ const restamped = this.#restampedStreams.has(streamKey);
9426
+ const restored = /* @__PURE__ */ new Set();
9427
+ const symbols = result.symbols.flatMap((sym) => {
9428
+ const offset = inst.offsets.get(sym.fragment.mpuSequenceNumber);
9429
+ if (offset !== void 0) {
9430
+ const packetSequenceNumber = serial32Add(blockStart, sym.index) - offset >>> 0;
9431
+ restored.add(packetSequenceNumber);
9432
+ return [{ ...sym, packetSequenceNumber }];
9433
+ }
9434
+ if (!restamped) return [sym];
9435
+ this.#overlayStats.unsequencedDropped++;
9436
+ return [];
9437
+ });
9438
+ this.#overlayStats.recovered++;
9439
+ this.#routeProcessedFecSymbols(blockStart, { ...result, symbols }, sourcePacketId, sourceName, {
9440
+ deliveryProvenance: inst.moqBlocks.has(blockStart) ? "mixed" : "multicast"
9441
+ });
9442
+ this.#noteOverlayRecoveredRaps(sourcePacketId, sourceName, streamKey, symbols.filter((sym) => restored.has(sym.packetSequenceNumber)));
9443
+ }
9444
+ /** fec-multicast-00 §6.6: report each base block whose RAP MFU fragments
9445
+ * the overlay restored, when the keyframe's FT=0 and FT=1 are held too. A
9446
+ * packet whose wire sequence was not restored names no base block. */
9447
+ #noteOverlayRecoveredRaps(packetId, catalogTrackName, streamKey, symbols) {
9448
+ const ssIdSeqOffset = this.#ssIdSeqOffsetByStream.get(streamKey);
9449
+ if (ssIdSeqOffset === void 0 || this.#overlayRecoveredObservers.size === 0) return;
9450
+ const withMeta = (type) => new Set(symbols.filter((sym) => sym.fragmentType === type).map((sym) => sym.fragment.mpuSequenceNumber));
9451
+ const ft0 = withMeta(0);
9452
+ const ft1 = withMeta(1);
9453
+ const rapMfu = (fi) => new Set(symbols.filter((sym) => sym.fragmentType === 2 && sym.fragment.rapFlag && sym.fragment.fragmentationIndicator === fi).map((sym) => sym.fragment.mpuSequenceNumber));
9454
+ const single = rapMfu(0);
9455
+ const first = rapMfu(1);
9456
+ const last = rapMfu(3);
9457
+ const blocks = /* @__PURE__ */ new Set();
9458
+ for (const sym of symbols) {
9459
+ const mpu = sym.fragment.mpuSequenceNumber;
9460
+ if (sym.fragmentType !== 2 || !sym.fragment.rapFlag || !ft0.has(mpu) || !ft1.has(mpu)) continue;
9461
+ if (!single.has(mpu) && !(first.has(mpu) && last.has(mpu))) continue;
9462
+ const ssId = sym.packetSequenceNumber + ssIdSeqOffset >>> 0;
9463
+ const sbn = this.#fecManager?.locateIsoSource?.(packetId, catalogTrackName, ssId)?.sbn;
9464
+ if (sbn !== void 0) blocks.add(sbn);
9465
+ }
9466
+ for (const sbn of blocks) {
9467
+ for (const observer of this.#overlayRecoveredObservers) observer(packetId, catalogTrackName, sbn);
9468
+ }
9469
+ }
9167
9470
  /**
9168
9471
  * Register callback fired when a delta-track FEC block completes or fails.
9169
9472
  *
@@ -9587,6 +9890,11 @@ var MmtpRouter = class {
9587
9890
  return;
9588
9891
  }
9589
9892
  const repairPacketId = header.packetId;
9893
+ const overlay = this.#overlayRepairTarget(repairPacketId, repairTrackName);
9894
+ if (overlay !== void 0) {
9895
+ this.#routeOverlayRepair(overlay, repairPacketId, packet, header, "moq");
9896
+ return;
9897
+ }
9590
9898
  const hintedOwner = ownerPacketIdHint != null && this.#classifyPacketId(ownerPacketIdHint) !== null ? ownerPacketIdHint : null;
9591
9899
  const ownerPacketId = hintedOwner ?? (this.#classifyPacketId(repairPacketId) !== null ? repairPacketId : null);
9592
9900
  if (ownerPacketId === null) {
@@ -9876,6 +10184,8 @@ var MmtpRouter = class {
9876
10184
  }
9877
10185
  }
9878
10186
  this.#parser.resetTrack(packetId, catalogTrackName);
10187
+ this.resetKeyframeOverlays(packetId, catalogTrackName);
10188
+ this.#restampedStreams.delete(this.#mediaStreamKey(packetId, catalogTrackName));
9879
10189
  const assistPrefix = assistKey(packetId, catalogTrackName, 0).slice(0, -1);
9880
10190
  for (const key of this.#assistBlocks.keys()) if (key.startsWith(assistPrefix)) this.#assistBlocks.delete(key);
9881
10191
  for (const key of this.#assistFailures.keys()) if (key.startsWith(assistPrefix)) this.#assistFailures.delete(key);
@@ -9962,6 +10272,17 @@ var MmtpRouter = class {
9962
10272
  this.#packetIdRoles.delete(packetId);
9963
10273
  this.#catalogTrackNamesByPacketId.delete(packetId);
9964
10274
  this.#repairOwnersByPacketId.delete(packetId);
10275
+ this.#overlayLegs.delete(packetId);
10276
+ for (const [repairPacketId, leg] of [...this.#overlayLegs]) {
10277
+ if (leg.sourcePacketId === packetId) this.#overlayLegs.delete(repairPacketId);
10278
+ }
10279
+ for (const [name, leg] of [...this.#overlayRepairNames]) {
10280
+ if (leg.sourcePacketId === packetId) this.#overlayRepairNames.delete(name);
10281
+ }
10282
+ this.#overlays.delete(packetId);
10283
+ for (const [repairPacketId, inst] of [...this.#overlays]) {
10284
+ if (inst.leg.sourcePacketId === packetId) this.#overlays.delete(repairPacketId);
10285
+ }
9965
10286
  delete this.#pidCounts[packetId];
9966
10287
  delete this.#repairPidCounts[packetId];
9967
10288
  delete this.#moqRepairPidCounts[packetId];
@@ -10200,6 +10521,9 @@ var MmtpRouter = class {
10200
10521
  this.#parserDiagnostics.clear();
10201
10522
  this.#catalogTrackNamesByPacketId.clear();
10202
10523
  this.#repairOwnersByPacketId.clear();
10524
+ this.#overlayLegs.clear();
10525
+ this.#overlayRepairNames.clear();
10526
+ this.#overlays.clear();
10203
10527
  for (const { receiver } of this.#provenance.values()) receiver.release();
10204
10528
  this.#provenance.clear();
10205
10529
  this.#provenanceStreamFlow.clear();
@@ -10555,6 +10879,11 @@ var MmtpRouter = class {
10555
10879
  }
10556
10880
  }
10557
10881
  if (info.isRepair) {
10882
+ const overlay = this.#overlayRepairTarget(info.packetId, catalogTrackNameForPacket(ingress, info.packetId));
10883
+ if (overlay !== void 0) {
10884
+ this.#routeOverlayRepair(overlay, info.packetId, packet, parsed.header, source);
10885
+ return;
10886
+ }
10558
10887
  const ownerPacketId = this.#resolveRepairOwnerPacketId(info.packetId, ingress);
10559
10888
  if (ownerPacketId != null && this.#fecManager && hasAmbiguousCatalogTrackName(ingress, ownerPacketId)) return;
10560
10889
  const repair = this.#readRepairObject(
@@ -10761,6 +11090,17 @@ var MmtpRouter = class {
10761
11090
  });
10762
11091
  }
10763
11092
  }
11093
+ if (!overlapCopy && this.#classifyPacketId(info.packetId) !== null) {
11094
+ this.#feedOverlaySource(
11095
+ info.packetId,
11096
+ catalogTrackName,
11097
+ packet,
11098
+ parsed.header,
11099
+ info.sequenceNumber,
11100
+ info.rapFlag,
11101
+ source
11102
+ );
11103
+ }
10764
11104
  }
10765
11105
  }
10766
11106
  /**
@@ -11214,12 +11554,12 @@ var MmtpRouter = class {
11214
11554
  }
11215
11555
  return match;
11216
11556
  }
11217
- #routeProcessedFecSymbols(blockId, result, ownerPacketId, catalogTrackName) {
11557
+ #routeProcessedFecSymbols(blockId, result, ownerPacketId, catalogTrackName, overlay) {
11218
11558
  if (ownerPacketId == null) return;
11219
11559
  if (this.#classifyPacketId(ownerPacketId) === null) return;
11220
- const ssIdSeqOffset = this.#ssIdSeqOffsetByStream.get(
11221
- this.#mediaStreamKey(ownerPacketId, catalogTrackName)
11222
- );
11560
+ const fecSbnOf = (index) => overlay ? {} : { fecSbn: this.#fecAnchorForRecovered(blockId, index) };
11561
+ const streamKey = this.#mediaStreamKey(ownerPacketId, catalogTrackName);
11562
+ const ssIdSeqOffset = overlay ? void 0 : this.#ssIdSeqOffsetByStream.get(streamKey);
11223
11563
  if (ssIdSeqOffset !== void 0) {
11224
11564
  result = {
11225
11565
  ...result,
@@ -11227,12 +11567,13 @@ var MmtpRouter = class {
11227
11567
  const wire = this.#fecAnchorForRecovered(blockId, sym.index) - ssIdSeqOffset >>> 0;
11228
11568
  if (wire === sym.packetSequenceNumber) return sym;
11229
11569
  this.#restampedRecoveredSeqs++;
11570
+ this.#restampedStreams.add(streamKey);
11230
11571
  return { ...sym, packetSequenceNumber: wire };
11231
11572
  })
11232
11573
  };
11233
11574
  }
11234
- this.#stream(ownerPacketId, catalogTrackName).duplicates.advance(blockId);
11235
- const deliveryProvenance = this.#fecManager?.blockDeliveryProvenance?.(
11575
+ if (!overlay) this.#stream(ownerPacketId, catalogTrackName).duplicates.advance(blockId);
11576
+ const deliveryProvenance = overlay?.deliveryProvenance ?? this.#fecManager?.blockDeliveryProvenance?.(
11236
11577
  blockId,
11237
11578
  ownerPacketId,
11238
11579
  catalogTrackName
@@ -11310,7 +11651,7 @@ var MmtpRouter = class {
11310
11651
  rapFlag: sym.fragment.rapFlag,
11311
11652
  recovered: true,
11312
11653
  deliveryProvenance,
11313
- fecSbn: this.#fecAnchorForRecovered(blockId, sym.index),
11654
+ ...fecSbnOf(sym.index),
11314
11655
  ...catalogTrackName === void 0 ? {} : { catalogTrackName }
11315
11656
  });
11316
11657
  continue;
@@ -11422,7 +11763,7 @@ var MmtpRouter = class {
11422
11763
  rapFlag: assembled.rapFlag,
11423
11764
  recovered: true,
11424
11765
  ...assembled.deliveryProvenance === void 0 ? {} : { deliveryProvenance: assembled.deliveryProvenance },
11425
- fecSbn: this.#fecAnchorForRecovered(blockId, sym.index),
11766
+ ...fecSbnOf(sym.index),
11426
11767
  ...recoveredCatalogTrackName === void 0 ? {} : { catalogTrackName: recoveredCatalogTrackName }
11427
11768
  });
11428
11769
  continue;
@@ -11559,7 +11900,7 @@ var MmtpRouter = class {
11559
11900
  rapFlag: sym.fragment.rapFlag,
11560
11901
  recovered: true,
11561
11902
  deliveryProvenance,
11562
- fecSbn: this.#fecAnchorForRecovered(blockId, sym.index),
11903
+ ...fecSbnOf(sym.index),
11563
11904
  ...catalogTrackName === void 0 ? {} : { catalogTrackName }
11564
11905
  });
11565
11906
  } else {
@@ -11638,7 +11979,7 @@ var MmtpRouter = class {
11638
11979
  rapFlag: assembled.rapFlag,
11639
11980
  recovered: true,
11640
11981
  deliveryProvenance,
11641
- fecSbn: this.#fecAnchorForRecovered(blockId, sym.index),
11982
+ ...fecSbnOf(sym.index),
11642
11983
  ...assembled.catalogTrackName === void 0 ? {} : { catalogTrackName: assembled.catalogTrackName }
11643
11984
  });
11644
11985
  }
@@ -11673,7 +12014,7 @@ var MmtpRouter = class {
11673
12014
  ...sym.fragment.sampleNumber === void 0 ? {} : { sampleNumber: sym.fragment.sampleNumber },
11674
12015
  recovered: true,
11675
12016
  deliveryProvenance,
11676
- fecSbn: this.#fecAnchorForRecovered(blockId, sym.index),
12017
+ ...fecSbnOf(sym.index),
11677
12018
  ...catalogTrackName === void 0 ? {} : { catalogTrackName }
11678
12019
  });
11679
12020
  continue;
@@ -11717,6 +12058,8 @@ var MmtpRouter = class {
11717
12058
  };
11718
12059
  export {
11719
12060
  KEYFRAME_LOST_TIMEOUT_SLACK,
11720
- MmtpRouter
12061
+ KEYFRAME_OVERLAY_EXTENSION_TYPE,
12062
+ MmtpRouter,
12063
+ keyframeOverlaySsId
11721
12064
  };
11722
12065
  //# sourceMappingURL=mmtp-router.js.map