@blockcast/mmt-container 0.5.0-main.dd44e35d4e29 → 0.5.0-main.ed11aec88d91
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 +206 -8
- package/dist/browser-runtime.js.map +2 -2
- package/dist/fec-manager.d.ts.map +1 -1
- package/dist/fec-manager.js +20 -0
- package/dist/fec-manager.js.map +2 -2
- package/dist/mmtp-router.d.ts +15 -1
- package/dist/mmtp-router.d.ts.map +1 -1
- package/dist/mmtp-router.js +178 -8
- package/dist/mmtp-router.js.map +2 -2
- package/dist/multicast-manager.d.ts +5 -1
- package/dist/multicast-manager.d.ts.map +1 -1
- package/dist/multicast-manager.js +206 -8
- package/dist/multicast-manager.js.map +2 -2
- package/package.json +5 -5
- package/src/fec-manager.ts +23 -0
- package/src/mmtp-router.ts +259 -11
- package/src/multicast-manager.ts +17 -1
package/dist/browser-runtime.js
CHANGED
|
@@ -13555,6 +13555,11 @@ var FECManager = class _FECManager {
|
|
|
13555
13555
|
// Worker-recovered blocks a recovered packet of which admission refused,
|
|
13556
13556
|
// until the decode's final symbol (7.2.7: the block is unrecovered).
|
|
13557
13557
|
this.#refusedRecoveries = /* @__PURE__ */ new Set();
|
|
13558
|
+
// Worker-recovered blocks: the next ESI the decode's ordered emit owes. The
|
|
13559
|
+
// Worker emits every ESI 0..SSB_length-1 in order and drops only a symbol it
|
|
13560
|
+
// refuses (a malformed ssbg_mode1 symbol), so a skipped ESI is a dropped
|
|
13561
|
+
// recovered packet and the decode recovered nothing.
|
|
13562
|
+
this.#workerRecoveredNextEsi = /* @__PURE__ */ new Map();
|
|
13558
13563
|
// How the multicast leg delivers symbols (see MulticastSymbolDelivery). The
|
|
13559
13564
|
// provider declares it per transport join; absence keeps datagram semantics.
|
|
13560
13565
|
this.#multicastSymbolDelivery = "datagram";
|
|
@@ -13764,6 +13769,7 @@ var FECManager = class _FECManager {
|
|
|
13764
13769
|
#repairDeadlineAtMs;
|
|
13765
13770
|
#workerRecoveredBlocks;
|
|
13766
13771
|
#refusedRecoveries;
|
|
13772
|
+
#workerRecoveredNextEsi;
|
|
13767
13773
|
#multicastSymbolDelivery;
|
|
13768
13774
|
#orderedFrontiers;
|
|
13769
13775
|
#orderedPendingBlocks;
|
|
@@ -13904,6 +13910,7 @@ var FECManager = class _FECManager {
|
|
|
13904
13910
|
this.#cleanlyCompletedBlocks.delete(record.key);
|
|
13905
13911
|
this.#workerRecoveredBlocks.delete(record.key);
|
|
13906
13912
|
this.#refusedRecoveries.delete(record.key);
|
|
13913
|
+
this.#workerRecoveredNextEsi.delete(record.key);
|
|
13907
13914
|
this.#failedBlocks.delete(record.key);
|
|
13908
13915
|
this.#resolvedBlockClasses.delete(record.key);
|
|
13909
13916
|
this.#repairedBlocks.delete(record.key);
|
|
@@ -15059,6 +15066,7 @@ var FECManager = class _FECManager {
|
|
|
15059
15066
|
resetSet(this.#repairRequestedBlocks);
|
|
15060
15067
|
resetSet(this.#workerRecoveredBlocks);
|
|
15061
15068
|
resetSet(this.#refusedRecoveries);
|
|
15069
|
+
resetMap(this.#workerRecoveredNextEsi);
|
|
15062
15070
|
resetSet(this.#startupAlignedPackets);
|
|
15063
15071
|
resetSet(this.#startupPartialBlocks);
|
|
15064
15072
|
resetSet(this.#startupRepairReadyPackets);
|
|
@@ -15186,6 +15194,7 @@ var FECManager = class _FECManager {
|
|
|
15186
15194
|
dropSet(this.#repairRequestedBlocks);
|
|
15187
15195
|
dropSet(this.#workerRecoveredBlocks);
|
|
15188
15196
|
dropSet(this.#refusedRecoveries);
|
|
15197
|
+
dropMap(this.#workerRecoveredNextEsi);
|
|
15189
15198
|
dropSet(this.#startupAlignedPackets);
|
|
15190
15199
|
dropSet(this.#startupPartialBlocks);
|
|
15191
15200
|
dropSet(this.#startupRepairReadyPackets);
|
|
@@ -15973,6 +15982,16 @@ var FECManager = class _FECManager {
|
|
|
15973
15982
|
if (!this.#claimDecoderBlock(client, meta.sbn, recoveredCatalogTrackName)) return;
|
|
15974
15983
|
const blockKey = this.#blockKey(meta.sbn, packetId, recoveredCatalogTrackName);
|
|
15975
15984
|
const cleanlyCompleted = this.#cleanlyCompletedBlocks.has(blockKey);
|
|
15985
|
+
if (esi !== (this.#workerRecoveredNextEsi.get(blockKey) ?? 0)) {
|
|
15986
|
+
this.#refusedRecoveries.add(blockKey);
|
|
15987
|
+
if (this.#refusedRecoveries.size > 1e3) dropOldest(this.#refusedRecoveries, 500);
|
|
15988
|
+
}
|
|
15989
|
+
if (esi >= meta.ssbLength - 1) {
|
|
15990
|
+
this.#workerRecoveredNextEsi.delete(blockKey);
|
|
15991
|
+
} else {
|
|
15992
|
+
this.#workerRecoveredNextEsi.set(blockKey, esi + 1);
|
|
15993
|
+
if (this.#workerRecoveredNextEsi.size > 1e3) dropOldest(this.#workerRecoveredNextEsi, 500);
|
|
15994
|
+
}
|
|
15976
15995
|
if (!cleanlyCompleted) {
|
|
15977
15996
|
const symbolKey = `${blockKey}:${esi}`;
|
|
15978
15997
|
if (!this.#seenSymbols.has(symbolKey)) {
|
|
@@ -16782,6 +16801,7 @@ var FECManager = class _FECManager {
|
|
|
16782
16801
|
this.#repairRequestedBlocks.clear();
|
|
16783
16802
|
this.#workerRecoveredBlocks.clear();
|
|
16784
16803
|
this.#refusedRecoveries.clear();
|
|
16804
|
+
this.#workerRecoveredNextEsi.clear();
|
|
16785
16805
|
this.#syncDecoderBlockOwners.clear();
|
|
16786
16806
|
this.#startupAlignedPackets.clear();
|
|
16787
16807
|
this.#startupPartialBlocks.clear();
|
|
@@ -19459,6 +19479,10 @@ var NO_BLOCK_SPAN_HORIZON = Object.freeze({
|
|
|
19459
19479
|
reason: "no-block-span"
|
|
19460
19480
|
});
|
|
19461
19481
|
var DIRECT_RECOVERED_VIDEO_QUEUE_LIMIT = 512;
|
|
19482
|
+
var ASSIST_BLOCK_LIMIT = 64;
|
|
19483
|
+
var ASSIST_FAILURE_LEDGER_LIMIT = 4 * ASSIST_BLOCK_LIMIT;
|
|
19484
|
+
var ASSIST_FAILED_DECODES_MAX = 2;
|
|
19485
|
+
var assistKey = (ownerPacketId, catalogTrackName, blockStart) => `${ownerPacketId}/${catalogTrackName ?? ""}/${blockStart}`;
|
|
19462
19486
|
var DIRECT_RECOVERED_AUDIO_QUEUE_LIMIT = 512;
|
|
19463
19487
|
function sourceSymbolEvidence(packet, header, rapFlag, packetSequenceNumber) {
|
|
19464
19488
|
const identity = MmtpContainerParser.sourcePacketIdentity(packet, header);
|
|
@@ -19483,6 +19507,12 @@ var MmtpRouter = class {
|
|
|
19483
19507
|
#provenance = /* @__PURE__ */ new Map();
|
|
19484
19508
|
#onFlowProtected = null;
|
|
19485
19509
|
#unadmittedListedDrops = 0;
|
|
19510
|
+
#unlistedRepairSetAside = 0;
|
|
19511
|
+
#assistBlocks = /* @__PURE__ */ new Map();
|
|
19512
|
+
#assistFailures = /* @__PURE__ */ new Map();
|
|
19513
|
+
#scratchDecoderFactory = null;
|
|
19514
|
+
#unlistedAssistedRecoveries = 0;
|
|
19515
|
+
#unlistedAssistedRefusals = 0;
|
|
19486
19516
|
/** The ingress a held datagram arrived with; the receiver returns the same bytes on release. */
|
|
19487
19517
|
#heldIngress = /* @__PURE__ */ new WeakMap();
|
|
19488
19518
|
#unicastRepair = false;
|
|
@@ -20198,14 +20228,13 @@ var MmtpRouter = class {
|
|
|
20198
20228
|
updateProvenanceFlowIngress(ingress) {
|
|
20199
20229
|
const entry = this.#provenance.get(ingress.flow);
|
|
20200
20230
|
if (!entry) return;
|
|
20231
|
+
const added = entry.protected ? ingress.tracks.filter(({ name, packetId }) => name && !entry.ingress.tracks.some((t) => t.name === name && t.packetId === packetId)) : [];
|
|
20232
|
+
const governed = added.map(({ packetId }) => [...this.#repairOwnersByPacketId.get(packetId) ?? [packetId]].some((owner) => this.#governs(owner)));
|
|
20201
20233
|
this.#provenance.set(ingress.flow, { ...entry, ingress });
|
|
20202
|
-
|
|
20203
|
-
for (const
|
|
20204
|
-
|
|
20205
|
-
|
|
20206
|
-
}
|
|
20207
|
-
}
|
|
20208
|
-
}
|
|
20234
|
+
added.forEach(({ name, packetId }, i) => {
|
|
20235
|
+
if (governed[i]) for (const block of this.#assistBlocks.values()) block.pooled.clear();
|
|
20236
|
+
else this.#fecManager?.resetTrack?.(packetId, name);
|
|
20237
|
+
});
|
|
20209
20238
|
}
|
|
20210
20239
|
/**
|
|
20211
20240
|
* Called once per protection of a flow: at the admission that makes this
|
|
@@ -20226,11 +20255,154 @@ var MmtpRouter = class {
|
|
|
20226
20255
|
if (!entry) return;
|
|
20227
20256
|
entry.receiver.release();
|
|
20228
20257
|
entry.protected = false;
|
|
20258
|
+
this.#assistBlocks.clear();
|
|
20259
|
+
this.#assistFailures.clear();
|
|
20229
20260
|
}
|
|
20230
20261
|
/** MoQ packets of a track a protected flow lists, dropped on its unicast path: duplicate, overflow, or listed by more than one flow. */
|
|
20231
20262
|
get unadmittedListedDropCount() {
|
|
20232
20263
|
return this.#unadmittedListedDrops;
|
|
20233
20264
|
}
|
|
20265
|
+
/** MoQ repair symbols of an unlisted track kept out of a governed FEC instance (7.2.7 step 2). */
|
|
20266
|
+
get unlistedRepairSetAsideCount() {
|
|
20267
|
+
return this.#unlistedRepairSetAside;
|
|
20268
|
+
}
|
|
20269
|
+
/** Governed blocks recovered with the help of unlisted repair (7.2.7 step 2). */
|
|
20270
|
+
get unlistedAssistedRecoveryCount() {
|
|
20271
|
+
return this.#unlistedAssistedRecoveries;
|
|
20272
|
+
}
|
|
20273
|
+
/** Unlisted-assisted decodes refused because two already failed for the block (7.2.7 step 2). */
|
|
20274
|
+
get unlistedAssistedRefusalCount() {
|
|
20275
|
+
return this.#unlistedAssistedRefusals;
|
|
20276
|
+
}
|
|
20277
|
+
/** Unlisted repair symbols pooled now, across every governed block (7.2.7 step 2). */
|
|
20278
|
+
get unlistedPoolSymbolCount() {
|
|
20279
|
+
let n = 0;
|
|
20280
|
+
for (const block of this.#assistBlocks.values()) n += block.pooled.size;
|
|
20281
|
+
return n;
|
|
20282
|
+
}
|
|
20283
|
+
/**
|
|
20284
|
+
* Wire the decoder an unlisted-assisted decode runs in: a fresh, configured
|
|
20285
|
+
* decoder of the owner's track, used once and discarded, never the live one.
|
|
20286
|
+
* Without it unlisted repair on a governed instance is set aside and dropped.
|
|
20287
|
+
*/
|
|
20288
|
+
setScratchFecDecoderFactory(factory) {
|
|
20289
|
+
this.#scratchDecoderFactory = factory;
|
|
20290
|
+
if (!factory) {
|
|
20291
|
+
this.#assistBlocks.clear();
|
|
20292
|
+
this.#assistFailures.clear();
|
|
20293
|
+
}
|
|
20294
|
+
}
|
|
20295
|
+
/** Whether a protected flow lists `ownerPacketId`: its FEC instance is governed (7.2.7). */
|
|
20296
|
+
#governs(ownerPacketId) {
|
|
20297
|
+
for (const entry of this.#provenance.values()) {
|
|
20298
|
+
if (entry.protected && ingressHasPacketId(entry.ingress, ownerPacketId)) return true;
|
|
20299
|
+
}
|
|
20300
|
+
return false;
|
|
20301
|
+
}
|
|
20302
|
+
/**
|
|
20303
|
+
* A MoQ repair symbol no protected flow lists, owned by a source track one
|
|
20304
|
+
* does: its FEC instance is governed (7.2.7 step 2) and takes only admitted
|
|
20305
|
+
* symbols, so the symbol is set aside: pooled for an unlisted-assisted
|
|
20306
|
+
* decode of a block admitted repair already delimits, else dropped. It
|
|
20307
|
+
* never states a block, so the live locator and decoder are unchanged.
|
|
20308
|
+
* Un-listing needs no hook: an unprotected or released flow lists nothing.
|
|
20309
|
+
*/
|
|
20310
|
+
#setAsideUnlistedRepair(ownerPacketId, catalogTrackName, repair) {
|
|
20311
|
+
if (!this.#governs(ownerPacketId)) return false;
|
|
20312
|
+
this.#unlistedRepairSetAside++;
|
|
20313
|
+
const key = assistKey(ownerPacketId, catalogTrackName, repair.ssStart);
|
|
20314
|
+
const block = this.#assistBlocks.get(key);
|
|
20315
|
+
if (block !== void 0 && block.ssbLength === repair.ssbLength && !block.repairs.has(repair.rsId) && block.pooled.size < block.ssbLength && repair.symbolData.byteLength === this.#symbolSizeForPacket(ownerPacketId) && this.#fecManager?.admitRepairBlockLength?.(ownerPacketId, repair.ssbLength, repair.rsbLength, repair.rsId) !== false) {
|
|
20316
|
+
block.pooled.set(repair.rsId, repair.symbolData);
|
|
20317
|
+
this.#tryAssistedDecode(key, block, ownerPacketId, catalogTrackName, repair.ssStart);
|
|
20318
|
+
}
|
|
20319
|
+
return true;
|
|
20320
|
+
}
|
|
20321
|
+
/**
|
|
20322
|
+
* An admitted repair placed a governed block (its SSB_length delimits it):
|
|
20323
|
+
* open the block's record, which the symbols its live decoder takes join.
|
|
20324
|
+
*/
|
|
20325
|
+
#openAssistBlock(ownerPacketId, catalogTrackName, blockStart, ssbLength) {
|
|
20326
|
+
const key = assistKey(ownerPacketId, catalogTrackName, blockStart);
|
|
20327
|
+
if (!this.#scratchDecoderFactory || this.#assistBlocks.has(key) || !this.#governs(ownerPacketId)) return;
|
|
20328
|
+
this.#assistBlocks.set(key, { ssbLength, sources: /* @__PURE__ */ new Map(), repairs: /* @__PURE__ */ new Map(), pooled: /* @__PURE__ */ new Map() });
|
|
20329
|
+
for (const oldest of this.#assistBlocks.keys()) {
|
|
20330
|
+
if (this.#assistBlocks.size <= ASSIST_BLOCK_LIMIT) break;
|
|
20331
|
+
this.#assistBlocks.delete(oldest);
|
|
20332
|
+
}
|
|
20333
|
+
}
|
|
20334
|
+
/** A symbol fed to an open governed block's live decoder that did not recover it. */
|
|
20335
|
+
#noteAssistAdmitted(ownerPacketId, catalogTrackName, blockStart, symbol, data) {
|
|
20336
|
+
const key = assistKey(ownerPacketId, catalogTrackName, blockStart);
|
|
20337
|
+
const block = this.#assistBlocks.get(key);
|
|
20338
|
+
if (block === void 0) return;
|
|
20339
|
+
if ("rsId" in symbol) {
|
|
20340
|
+
block.repairs.set(symbol.rsId, data);
|
|
20341
|
+
block.pooled.delete(symbol.rsId);
|
|
20342
|
+
} else {
|
|
20343
|
+
block.sources.set(symbol.esi, data);
|
|
20344
|
+
}
|
|
20345
|
+
this.#tryAssistedDecode(key, block, ownerPacketId, catalogTrackName, blockStart);
|
|
20346
|
+
}
|
|
20347
|
+
/**
|
|
20348
|
+
* 7.2.7 step 2, phase two: a governed block its admitted symbols cannot
|
|
20349
|
+
* decode (the live decoder already had them all) is decoded in a scratch
|
|
20350
|
+
* decoder from its admitted symbols plus pooled unlisted repair, only as
|
|
20351
|
+
* many as close the shortfall. What it recovers is released only through
|
|
20352
|
+
* admission (#routeRecoveredBlock), so the live decoder is never touched and
|
|
20353
|
+
* a refused recovery leaves the block to fail at its FEC deadline. At most
|
|
20354
|
+
* two such decodes may fail per block; a third is refused.
|
|
20355
|
+
*/
|
|
20356
|
+
#tryAssistedDecode(key, block, ownerPacketId, catalogTrackName, blockStart) {
|
|
20357
|
+
const fec = this.#fecManager;
|
|
20358
|
+
const admitted = block.sources.size + block.repairs.size;
|
|
20359
|
+
if (!fec || block.pooled.size === 0) return;
|
|
20360
|
+
if (admitted >= block.ssbLength || admitted + block.pooled.size < block.ssbLength) return;
|
|
20361
|
+
if (fec.isBlockCompleted(blockStart, ownerPacketId, catalogTrackName)) {
|
|
20362
|
+
this.#assistBlocks.delete(key);
|
|
20363
|
+
return;
|
|
20364
|
+
}
|
|
20365
|
+
if ((this.#assistFailures.get(key) ?? 0) >= ASSIST_FAILED_DECODES_MAX) {
|
|
20366
|
+
this.#unlistedAssistedRefusals++;
|
|
20367
|
+
return;
|
|
20368
|
+
}
|
|
20369
|
+
const decoder = this.#scratchDecoderFactory?.(ownerPacketId, catalogTrackName);
|
|
20370
|
+
if (!decoder) return;
|
|
20371
|
+
let recovered = null;
|
|
20372
|
+
const fed = [];
|
|
20373
|
+
try {
|
|
20374
|
+
for (const [esi, data] of block.sources) decoder.add_source(serial32Add(blockStart, esi), data, 0);
|
|
20375
|
+
for (const [rsId, data] of block.repairs) {
|
|
20376
|
+
recovered ??= decoder.add_repair(blockStart, block.ssbLength, rsId, data, 0);
|
|
20377
|
+
}
|
|
20378
|
+
for (const [rsId, data] of block.pooled) {
|
|
20379
|
+
if (recovered || fed.length >= block.ssbLength - admitted) break;
|
|
20380
|
+
fed.push(rsId);
|
|
20381
|
+
recovered = decoder.add_repair(blockStart, block.ssbLength, rsId, data, 0);
|
|
20382
|
+
}
|
|
20383
|
+
} catch {
|
|
20384
|
+
recovered = null;
|
|
20385
|
+
} finally {
|
|
20386
|
+
decoder.free?.();
|
|
20387
|
+
}
|
|
20388
|
+
if (recovered && this.#routeRecoveredBlock(blockStart, recovered, ownerPacketId, catalogTrackName, () => {
|
|
20389
|
+
fec.cancelBlockTimeout(blockStart, ownerPacketId, catalogTrackName);
|
|
20390
|
+
fec.countRecovery(ownerPacketId, blockStart, catalogTrackName);
|
|
20391
|
+
fec.markBlockCompleted(blockStart, ownerPacketId, catalogTrackName);
|
|
20392
|
+
})) {
|
|
20393
|
+
this.#unlistedAssistedRecoveries++;
|
|
20394
|
+
this.#assistBlocks.delete(key);
|
|
20395
|
+
return;
|
|
20396
|
+
}
|
|
20397
|
+
for (const rsId of fed) block.pooled.delete(rsId);
|
|
20398
|
+
const failed = (this.#assistFailures.get(key) ?? 0) + 1;
|
|
20399
|
+
this.#assistFailures.delete(key);
|
|
20400
|
+
this.#assistFailures.set(key, failed);
|
|
20401
|
+
for (const oldest of this.#assistFailures.keys()) {
|
|
20402
|
+
if (this.#assistFailures.size <= ASSIST_FAILURE_LEDGER_LIMIT) break;
|
|
20403
|
+
this.#assistFailures.delete(oldest);
|
|
20404
|
+
}
|
|
20405
|
+
}
|
|
20234
20406
|
/**
|
|
20235
20407
|
* The admission that makes this router protected under `flow` (7.2.7):
|
|
20236
20408
|
* its FEC instances discard what they hold of the flow's listed tracks so
|
|
@@ -20406,8 +20578,9 @@ var MmtpRouter = class {
|
|
|
20406
20578
|
);
|
|
20407
20579
|
}
|
|
20408
20580
|
if (ownerPacketId != null && !admitted) {
|
|
20409
|
-
const admit = this.#admitUnicast(
|
|
20581
|
+
const admit = this.#admitUnicast(repairPacketId, packet, () => this.#feedRepairPacket(packet, ownerPacketIdHint, catalogTrackName, repairTrackName, true));
|
|
20410
20582
|
if (admit === "held" || admit === "dropped") return;
|
|
20583
|
+
if (admit === "unlisted" && this.#fecManager && this.#setAsideUnlistedRepair(ownerPacketId, catalogTrackName, parsed)) return;
|
|
20411
20584
|
}
|
|
20412
20585
|
if (ownerPacketId != null && this.#fecManager) {
|
|
20413
20586
|
this.#moqRepairPidCounts[repairPacketId] = (this.#moqRepairPidCounts[repairPacketId] || 0) + 1;
|
|
@@ -20527,6 +20700,7 @@ var MmtpRouter = class {
|
|
|
20527
20700
|
const placement = fec.placeIsoRepair(ownerPacketId, catalogTrackName, repair.ssStart, repair.ssbLength);
|
|
20528
20701
|
if (placement.status !== "placed") return;
|
|
20529
20702
|
const blockStart = placement.block.start;
|
|
20703
|
+
this.#openAssistBlock(ownerPacketId, catalogTrackName, blockStart, repair.ssbLength);
|
|
20530
20704
|
if (placement.span !== void 0) {
|
|
20531
20705
|
this.#replayIsoSources(ownerPacketId, catalogTrackName, placement.span.block.start, placement.span.placed);
|
|
20532
20706
|
}
|
|
@@ -20557,6 +20731,8 @@ var MmtpRouter = class {
|
|
|
20557
20731
|
fec.countRecovery(ownerPacketId, blockStart, catalogTrackName);
|
|
20558
20732
|
fec.markBlockCompleted(blockStart, ownerPacketId, catalogTrackName);
|
|
20559
20733
|
});
|
|
20734
|
+
} else if (!recovered) {
|
|
20735
|
+
this.#noteAssistAdmitted(ownerPacketId, catalogTrackName, blockStart, { rsId: repair.rsId }, repair.symbolData);
|
|
20560
20736
|
}
|
|
20561
20737
|
}
|
|
20562
20738
|
/**
|
|
@@ -20596,6 +20772,7 @@ var MmtpRouter = class {
|
|
|
20596
20772
|
if (payload.oversized) continue;
|
|
20597
20773
|
const alreadyCompleted = fec.isBlockCompleted(blockStart, ownerPacketId, catalogTrackName);
|
|
20598
20774
|
const recovered = fec.feedSourceSymbol(ownerPacketId, ssId, payload.symbolData, catalogTrackName);
|
|
20775
|
+
if (!recovered) this.#noteAssistAdmitted(ownerPacketId, catalogTrackName, blockStart, { esi }, payload.symbolData);
|
|
20599
20776
|
if (!recovered || alreadyCompleted) continue;
|
|
20600
20777
|
this.#routeRecoveredBlock(blockStart, recovered, ownerPacketId, catalogTrackName, () => {
|
|
20601
20778
|
fec.cancelBlockTimeout(blockStart, ownerPacketId, catalogTrackName);
|
|
@@ -20666,6 +20843,9 @@ var MmtpRouter = class {
|
|
|
20666
20843
|
}
|
|
20667
20844
|
}
|
|
20668
20845
|
this.#parser.resetTrack(packetId, catalogTrackName);
|
|
20846
|
+
const assistPrefix = assistKey(packetId, catalogTrackName, 0).slice(0, -1);
|
|
20847
|
+
for (const key of this.#assistBlocks.keys()) if (key.startsWith(assistPrefix)) this.#assistBlocks.delete(key);
|
|
20848
|
+
for (const key of this.#assistFailures.keys()) if (key.startsWith(assistPrefix)) this.#assistFailures.delete(key);
|
|
20669
20849
|
const streamKey = this.#mediaStreamKey(packetId, catalogTrackName);
|
|
20670
20850
|
this.#trackSynced.delete(streamKey);
|
|
20671
20851
|
this.#droppedPreSyncVideo.delete(streamKey);
|
|
@@ -21326,6 +21506,7 @@ var MmtpRouter = class {
|
|
|
21326
21506
|
const ownerPacketId = this.#resolveRepairOwnerPacketId(info.packetId, ingress);
|
|
21327
21507
|
if (ownerPacketId != null && this.#fecManager) {
|
|
21328
21508
|
if (hasAmbiguousCatalogTrackName(ingress, ownerPacketId)) return;
|
|
21509
|
+
if (source === "moq" && !manifestAdmitted && this.#setAsideUnlistedRepair(ownerPacketId, catalogTrackNameForPacket(ingress, ownerPacketId), repair)) return;
|
|
21329
21510
|
this.#repairPidCounts[info.packetId] = (this.#repairPidCounts[info.packetId] || 0) + 1;
|
|
21330
21511
|
const count = this.#repairPidCounts[info.packetId];
|
|
21331
21512
|
if (count <= 2 || count % 500 === 0) {
|
|
@@ -21493,6 +21674,15 @@ var MmtpRouter = class {
|
|
|
21493
21674
|
sourceFec.symbolData,
|
|
21494
21675
|
catalogTrackName
|
|
21495
21676
|
);
|
|
21677
|
+
if (!recovered) {
|
|
21678
|
+
this.#noteAssistAdmitted(
|
|
21679
|
+
info.packetId,
|
|
21680
|
+
catalogTrackName,
|
|
21681
|
+
sourceFec.sbn,
|
|
21682
|
+
{ esi: serial32Distance(sourceFec.sbn, sourceFec.ssId) },
|
|
21683
|
+
sourceFec.symbolData
|
|
21684
|
+
);
|
|
21685
|
+
}
|
|
21496
21686
|
if (recovered && !alreadyCompleted) {
|
|
21497
21687
|
const fec = this.#fecManager;
|
|
21498
21688
|
const sbn = sourceFec.sbn;
|
|
@@ -28120,6 +28310,14 @@ var MulticastManager = class {
|
|
|
28120
28310
|
}
|
|
28121
28311
|
}
|
|
28122
28312
|
}
|
|
28313
|
+
const factory = typeof wasmDecoderSource === "function" ? wasmDecoderSource : null;
|
|
28314
|
+
const fecTracks = fecTrackIndexes.map((i) => catalog.tracks[i]);
|
|
28315
|
+
this.#router.setScratchFecDecoderFactory(factory && ((ownerPacketId, catalogTrackName) => {
|
|
28316
|
+
const track = fecTracks.find((t) => t.packetId === ownerPacketId && t.name === catalogTrackName);
|
|
28317
|
+
if (!track) return null;
|
|
28318
|
+
const decoder = factory(track);
|
|
28319
|
+
return [...fecDecoders.values()].includes(decoder) ? null : decoder;
|
|
28320
|
+
}));
|
|
28123
28321
|
this.#switchingSets.clear();
|
|
28124
28322
|
this.#repairPacketIds.clear();
|
|
28125
28323
|
this.#router.clearRepairPacketIdOwners();
|