@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.
- package/dist/browser-runtime.js +492 -22
- package/dist/browser-runtime.js.map +2 -2
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -1
- package/dist/index.js.map +2 -2
- package/dist/mmtp-router.d.ts +69 -2
- package/dist/mmtp-router.d.ts.map +1 -1
- package/dist/mmtp-router.js +356 -13
- package/dist/mmtp-router.js.map +2 -2
- package/dist/multicast-manager.d.ts +20 -1
- package/dist/multicast-manager.d.ts.map +1 -1
- package/dist/multicast-manager.js +475 -22
- package/dist/multicast-manager.js.map +2 -2
- package/dist/repair-fetch.d.ts +10 -3
- package/dist/repair-fetch.d.ts.map +1 -1
- package/dist/repair-fetch.js +15 -0
- package/dist/repair-fetch.js.map +2 -2
- package/package.json +5 -5
- package/src/index.ts +5 -0
- package/src/mmtp-router.ts +468 -12
- package/src/multicast-manager.ts +163 -17
- package/src/repair-fetch.ts +32 -2
package/dist/mmtp-router.js
CHANGED
|
@@ -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
|
|
11221
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
12061
|
+
KEYFRAME_OVERLAY_EXTENSION_TYPE,
|
|
12062
|
+
MmtpRouter,
|
|
12063
|
+
keyframeOverlaySsId
|
|
11721
12064
|
};
|
|
11722
12065
|
//# sourceMappingURL=mmtp-router.js.map
|