@blockcast/mmt-container 2.0.0 → 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 +1 -1
- package/dist/browser-runtime.js +141 -22
- package/dist/browser-runtime.js.map +2 -2
- package/dist/delivery-catalog.js +3 -0
- package/dist/delivery-catalog.js.map +2 -2
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +2 -2
- package/dist/mmtp-container-parser.no-modules.js +19 -10
- package/dist/mmtp-container-parser.no-modules.js.map +2 -2
- package/dist/mmtp-router.d.ts +26 -1
- package/dist/mmtp-router.d.ts.map +1 -1
- package/dist/mmtp-router.js +66 -1
- package/dist/mmtp-router.js.map +2 -2
- package/dist/multicast-manager.d.ts +4 -1
- package/dist/multicast-manager.d.ts.map +1 -1
- package/dist/multicast-manager.js +141 -22
- package/dist/multicast-manager.js.map +2 -2
- package/dist/multicast-transport.d.ts +8 -1
- package/dist/multicast-transport.d.ts.map +1 -1
- package/dist/multicast-transport.js +93 -2
- package/dist/multicast-transport.js.map +4 -4
- package/dist/no-modules.js +19 -10
- package/dist/no-modules.js.map +2 -2
- package/package.json +5 -5
- package/src/index.ts +1 -0
- package/src/mmtp-router.ts +80 -1
- package/src/multicast-manager.ts +52 -16
- package/src/multicast-transport.ts +43 -4
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.
|
|
280
|
+
> `2.0.1`; see `RELEASES.md`.
|
|
281
281
|
|
|
282
282
|
#### Track-role convention (Phase 14)
|
|
283
283
|
|
package/dist/browser-runtime.js
CHANGED
|
@@ -8514,6 +8514,9 @@ var AMTGatewayManager = class {
|
|
|
8514
8514
|
*/
|
|
8515
8515
|
async subscribe(config) {
|
|
8516
8516
|
const { key } = config;
|
|
8517
|
+
if (config.protected) {
|
|
8518
|
+
throw new Error("AMTGatewayManager refuses a protected join: it reports no per-datagram sender (multicast-02 7.2.7)");
|
|
8519
|
+
}
|
|
8517
8520
|
const keyStr = this.getKeyString(key);
|
|
8518
8521
|
if (this.subscriptions.has(keyStr)) {
|
|
8519
8522
|
console.log(`[AMT] Reusing existing subscription to ${keyStr}`);
|
|
@@ -21977,7 +21980,6 @@ _RistrettoPoint.Fn = /* @__PURE__ */ (() => Fn)();
|
|
|
21977
21980
|
var BCPV2_LEAD_BOUND_MS = 5e3;
|
|
21978
21981
|
var REPLAY_WINDOW = 64n;
|
|
21979
21982
|
var BUCKET_DEPTH = 64;
|
|
21980
|
-
var BUCKET_RATE_PER_MS = 1;
|
|
21981
21983
|
var hex = (bytes) => Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join("");
|
|
21982
21984
|
function sameBytes(a, b) {
|
|
21983
21985
|
if (a.byteLength !== b.byteLength)
|
|
@@ -22006,6 +22008,10 @@ var BcProvenanceV2Receiver = class {
|
|
|
22006
22008
|
verifySig;
|
|
22007
22009
|
accepted;
|
|
22008
22010
|
queueLimit;
|
|
22011
|
+
/** Octets a held datagram is charged beyond its payload: the IPv4 or IPv6 plus UDP headers (step 2). */
|
|
22012
|
+
headerOctets;
|
|
22013
|
+
/** Budget refill, tokens per ms: V / 1000 (step 1). */
|
|
22014
|
+
bucketRatePerMs;
|
|
22009
22015
|
current = null;
|
|
22010
22016
|
previous = null;
|
|
22011
22017
|
graceStart = {};
|
|
@@ -22051,6 +22057,8 @@ var BcProvenanceV2Receiver = class {
|
|
|
22051
22057
|
multicast: this.maxAuthDelayMs * bandwidth / 8e3,
|
|
22052
22058
|
unicast: (this.maxAuthDelayMs + this.widenMs) * bandwidth / 8e3
|
|
22053
22059
|
};
|
|
22060
|
+
this.headerOctets = parseBcProvenanceV2Address(cfg.endpoint.groupAddress).byteLength === 16 ? 48 : 28;
|
|
22061
|
+
this.bucketRatePerMs = Math.max(1e3, 1.25 * bandwidth / (8 * 512 * 37)) / 1e3;
|
|
22054
22062
|
const t = this.now();
|
|
22055
22063
|
this.buckets = {
|
|
22056
22064
|
multicast: { tokens: BUCKET_DEPTH, last: t, dropped: 0 },
|
|
@@ -22149,7 +22157,7 @@ var BcProvenanceV2Receiver = class {
|
|
|
22149
22157
|
arrival: now,
|
|
22150
22158
|
sent,
|
|
22151
22159
|
digest: hex(sha2562(bytes)),
|
|
22152
|
-
pairs: record.digests.map((d, i) => ({ digest: hex(d), seq: record.firstSeq + BigInt(i), admitted: false })),
|
|
22160
|
+
pairs: record.digests.map((d, i) => ({ digest: hex(d), epoch, seq: record.firstSeq + BigInt(i), admitted: false })),
|
|
22153
22161
|
awaitingMulticast: path === "mirror"
|
|
22154
22162
|
};
|
|
22155
22163
|
this.verified.set(id, entry);
|
|
@@ -22216,12 +22224,12 @@ var BcProvenanceV2Receiver = class {
|
|
|
22216
22224
|
digest(datagram) {
|
|
22217
22225
|
return hex(bcProvenanceV2DatagramDigest(this.cfg.endpoint, datagram));
|
|
22218
22226
|
}
|
|
22219
|
-
/** Consume the
|
|
22227
|
+
/** Consume the pending entry for `digest` lowest in (epoch, sequence) order, admitting it. */
|
|
22220
22228
|
consumePending(digest, now) {
|
|
22221
22229
|
const list = this.pending.get(digest);
|
|
22222
22230
|
if (!list?.length)
|
|
22223
22231
|
return false;
|
|
22224
|
-
list.sort((a, b) => a.pair.seq < b.pair.seq ? -1 : a.pair.seq > b.pair.seq ? 1 : 0);
|
|
22232
|
+
list.sort((a, b) => a.pair.epoch - b.pair.epoch || (a.pair.seq < b.pair.seq ? -1 : a.pair.seq > b.pair.seq ? 1 : 0));
|
|
22225
22233
|
const hit = list.shift();
|
|
22226
22234
|
if (list.length === 0)
|
|
22227
22235
|
this.pending.delete(digest);
|
|
@@ -22230,14 +22238,18 @@ var BcProvenanceV2Receiver = class {
|
|
|
22230
22238
|
this.admittedUntil.set(digest, now + this.maxAuthDelayMs + this.leadMs);
|
|
22231
22239
|
return true;
|
|
22232
22240
|
}
|
|
22241
|
+
/** A held datagram's charge against its queue: payload plus IP and UDP headers. */
|
|
22242
|
+
charge(held) {
|
|
22243
|
+
return held.datagram.byteLength + this.headerOctets;
|
|
22244
|
+
}
|
|
22233
22245
|
enqueue(held, path, now) {
|
|
22234
|
-
if (this.holdBytes[path] + held
|
|
22246
|
+
if (this.holdBytes[path] + this.charge(held) > this.queueLimit[path]) {
|
|
22235
22247
|
return { verdict: "overflow", admitted: [] };
|
|
22236
22248
|
}
|
|
22237
22249
|
held.expiry = now + this.maxAuthDelayMs + (path === "unicast" ? this.widenMs : 0);
|
|
22238
22250
|
held.order = this.order++;
|
|
22239
22251
|
this.hold[path].push(held);
|
|
22240
|
-
this.holdBytes[path] += held
|
|
22252
|
+
this.holdBytes[path] += this.charge(held);
|
|
22241
22253
|
return { verdict: "held", admitted: [] };
|
|
22242
22254
|
}
|
|
22243
22255
|
/** Remove and return the oldest held datagram with `digest`, across both queues. */
|
|
@@ -22252,7 +22264,7 @@ var BcProvenanceV2Receiver = class {
|
|
|
22252
22264
|
if (!best)
|
|
22253
22265
|
return void 0;
|
|
22254
22266
|
const [held] = this.hold[best.path].splice(best.index, 1);
|
|
22255
|
-
this.holdBytes[best.path] -= held
|
|
22267
|
+
this.holdBytes[best.path] -= this.charge(held);
|
|
22256
22268
|
return held.datagram;
|
|
22257
22269
|
}
|
|
22258
22270
|
dropAdmittedHeld() {
|
|
@@ -22260,7 +22272,7 @@ var BcProvenanceV2Receiver = class {
|
|
|
22260
22272
|
this.hold[path] = this.hold[path].filter((h) => {
|
|
22261
22273
|
const keep = h.digest === void 0 || !this.admittedUntil.has(h.digest);
|
|
22262
22274
|
if (!keep)
|
|
22263
|
-
this.holdBytes[path] -= h
|
|
22275
|
+
this.holdBytes[path] -= this.charge(h);
|
|
22264
22276
|
return keep;
|
|
22265
22277
|
});
|
|
22266
22278
|
}
|
|
@@ -22344,7 +22356,7 @@ var BcProvenanceV2Receiver = class {
|
|
|
22344
22356
|
}
|
|
22345
22357
|
take(path, now) {
|
|
22346
22358
|
const b = this.buckets[path];
|
|
22347
|
-
b.tokens = Math.min(BUCKET_DEPTH, b.tokens + (now - b.last) *
|
|
22359
|
+
b.tokens = Math.min(BUCKET_DEPTH, b.tokens + (now - b.last) * this.bucketRatePerMs);
|
|
22348
22360
|
b.last = now;
|
|
22349
22361
|
if (b.tokens < 1) {
|
|
22350
22362
|
b.dropped++;
|
|
@@ -22358,7 +22370,7 @@ var BcProvenanceV2Receiver = class {
|
|
|
22358
22370
|
this.hold[path] = this.hold[path].filter((h) => {
|
|
22359
22371
|
if (h.expiry > now)
|
|
22360
22372
|
return true;
|
|
22361
|
-
this.holdBytes[path] -= h
|
|
22373
|
+
this.holdBytes[path] -= this.charge(h);
|
|
22362
22374
|
this.expired[path]++;
|
|
22363
22375
|
return false;
|
|
22364
22376
|
});
|
|
@@ -22635,6 +22647,14 @@ var MmtpRouter = class {
|
|
|
22635
22647
|
#packetIdRoles = /* @__PURE__ */ new Map();
|
|
22636
22648
|
/** Explicit catalog ownership for dedicated repair-track packet IDs. */
|
|
22637
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 };
|
|
22638
22658
|
/** Callback fired when a delta-track FEC block completes or fails. */
|
|
22639
22659
|
#onDeltaBlockCompleteCb = null;
|
|
22640
22660
|
// Debug counters
|
|
@@ -23153,13 +23173,53 @@ var MmtpRouter = class {
|
|
|
23153
23173
|
/** Replace catalog-derived repair ownership before a catalog reconfiguration. */
|
|
23154
23174
|
clearRepairPacketIdOwners() {
|
|
23155
23175
|
this.#repairOwnersByPacketId.clear();
|
|
23176
|
+
this.#overlayLegs.clear();
|
|
23177
|
+
this.#overlayRepairNames.clear();
|
|
23156
23178
|
}
|
|
23157
|
-
/** 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). */
|
|
23158
23182
|
setRepairPacketIdOwner(repairPacketId, ownerPacketId) {
|
|
23183
|
+
if (this.#overlayLegs.has(repairPacketId)) {
|
|
23184
|
+
this.#overlayStats.ownerRefused++;
|
|
23185
|
+
return;
|
|
23186
|
+
}
|
|
23159
23187
|
const owners = this.#repairOwnersByPacketId.get(repairPacketId) ?? /* @__PURE__ */ new Set();
|
|
23160
23188
|
owners.add(ownerPacketId);
|
|
23161
23189
|
this.#repairOwnersByPacketId.set(repairPacketId, owners);
|
|
23162
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
|
+
}
|
|
23163
23223
|
/**
|
|
23164
23224
|
* Register callback fired when a delta-track FEC block completes or fails.
|
|
23165
23225
|
*
|
|
@@ -23583,6 +23643,10 @@ var MmtpRouter = class {
|
|
|
23583
23643
|
return;
|
|
23584
23644
|
}
|
|
23585
23645
|
const repairPacketId = header.packetId;
|
|
23646
|
+
if (this.#isOverlayRepair(repairPacketId, repairTrackName)) {
|
|
23647
|
+
this.#dropOverlayRepair(repairPacketId);
|
|
23648
|
+
return;
|
|
23649
|
+
}
|
|
23586
23650
|
const hintedOwner = ownerPacketIdHint != null && this.#classifyPacketId(ownerPacketIdHint) !== null ? ownerPacketIdHint : null;
|
|
23587
23651
|
const ownerPacketId = hintedOwner ?? (this.#classifyPacketId(repairPacketId) !== null ? repairPacketId : null);
|
|
23588
23652
|
if (ownerPacketId === null) {
|
|
@@ -23958,6 +24022,13 @@ var MmtpRouter = class {
|
|
|
23958
24022
|
this.#packetIdRoles.delete(packetId);
|
|
23959
24023
|
this.#catalogTrackNamesByPacketId.delete(packetId);
|
|
23960
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
|
+
}
|
|
23961
24032
|
delete this.#pidCounts[packetId];
|
|
23962
24033
|
delete this.#repairPidCounts[packetId];
|
|
23963
24034
|
delete this.#moqRepairPidCounts[packetId];
|
|
@@ -24196,6 +24267,8 @@ var MmtpRouter = class {
|
|
|
24196
24267
|
this.#parserDiagnostics.clear();
|
|
24197
24268
|
this.#catalogTrackNamesByPacketId.clear();
|
|
24198
24269
|
this.#repairOwnersByPacketId.clear();
|
|
24270
|
+
this.#overlayLegs.clear();
|
|
24271
|
+
this.#overlayRepairNames.clear();
|
|
24199
24272
|
for (const { receiver } of this.#provenance.values()) receiver.release();
|
|
24200
24273
|
this.#provenance.clear();
|
|
24201
24274
|
this.#provenanceStreamFlow.clear();
|
|
@@ -24551,6 +24624,10 @@ var MmtpRouter = class {
|
|
|
24551
24624
|
}
|
|
24552
24625
|
}
|
|
24553
24626
|
if (info.isRepair) {
|
|
24627
|
+
if (this.#isOverlayRepair(info.packetId, catalogTrackNameForPacket(ingress, info.packetId))) {
|
|
24628
|
+
this.#dropOverlayRepair(info.packetId);
|
|
24629
|
+
return;
|
|
24630
|
+
}
|
|
24554
24631
|
const ownerPacketId = this.#resolveRepairOwnerPacketId(info.packetId, ingress);
|
|
24555
24632
|
if (ownerPacketId != null && this.#fecManager && hasAmbiguousCatalogTrackName(ingress, ownerPacketId)) return;
|
|
24556
24633
|
const repair = this.#readRepairObject(
|
|
@@ -26069,6 +26146,10 @@ var MulticastTransport = class {
|
|
|
26069
26146
|
#acceptedBytesByPacketId = /* @__PURE__ */ new Map();
|
|
26070
26147
|
#packetIds = null;
|
|
26071
26148
|
#protected = false;
|
|
26149
|
+
// A protected join's S, and the last sender spelling that matched it, so a
|
|
26150
|
+
// matching sender parses once per spelling rather than per datagram.
|
|
26151
|
+
#protectedSource = void 0;
|
|
26152
|
+
#matchedSource = void 0;
|
|
26072
26153
|
// The sender S this join's accepted packets came from, once known; see
|
|
26073
26154
|
// packetsDroppedForeignSender. Cleared by every join and by a silence
|
|
26074
26155
|
// failback, so a replacement publisher is not locked out.
|
|
@@ -26120,6 +26201,11 @@ var MulticastTransport = class {
|
|
|
26120
26201
|
return this.#declined(generation, "unavailable");
|
|
26121
26202
|
}
|
|
26122
26203
|
let failure = "invalid-request";
|
|
26204
|
+
const protectedSource = config.protected === true ? config.sourceAddress ?? config.senderAddress : void 0;
|
|
26205
|
+
if (config.protected === true && (protectedSource === void 0 || config.atsc3)) {
|
|
26206
|
+
console.warn("[MulticastTransport] protected join refused: the path reports no per-datagram sender to check against S");
|
|
26207
|
+
return this.#declined(generation, "invalid-request");
|
|
26208
|
+
}
|
|
26123
26209
|
try {
|
|
26124
26210
|
const packetIds = config.packetIds?.length ? new Set(config.packetIds) : null;
|
|
26125
26211
|
console.log(
|
|
@@ -26155,7 +26241,7 @@ var MulticastTransport = class {
|
|
|
26155
26241
|
this.#subscription = subscription;
|
|
26156
26242
|
const live = () => !this.#disposed && generation === this.#joinGeneration;
|
|
26157
26243
|
const anySource = !config.sourceAddress && !config.senderAddress && !config.atsc3;
|
|
26158
|
-
const packetListener = !anySource && subscriptionDeliversPacketBatches(subscription) ? {
|
|
26244
|
+
const packetListener = !anySource && protectedSource === void 0 && subscriptionDeliversPacketBatches(subscription) ? {
|
|
26159
26245
|
type: "packets",
|
|
26160
26246
|
listener: (event) => {
|
|
26161
26247
|
if (!live()) return;
|
|
@@ -26171,6 +26257,8 @@ var MulticastTransport = class {
|
|
|
26171
26257
|
this.#packetListener = packetListener;
|
|
26172
26258
|
this.#packetIds = packetIds;
|
|
26173
26259
|
this.#protected = config.protected === true;
|
|
26260
|
+
this.#protectedSource = protectedSource;
|
|
26261
|
+
this.#matchedSource = void 0;
|
|
26174
26262
|
this.#providerFilter = {
|
|
26175
26263
|
acknowledged: packetIds,
|
|
26176
26264
|
sent: 0,
|
|
@@ -26630,13 +26718,23 @@ var MulticastTransport = class {
|
|
|
26630
26718
|
this.#delivery = void 0;
|
|
26631
26719
|
this.#packetIds = null;
|
|
26632
26720
|
this.#protected = false;
|
|
26721
|
+
this.#protectedSource = void 0;
|
|
26722
|
+
this.#matchedSource = void 0;
|
|
26633
26723
|
this.#providerFilter = null;
|
|
26634
26724
|
this.#context = void 0;
|
|
26635
26725
|
this.#withdrawnPacketIds.clear();
|
|
26636
26726
|
}
|
|
26637
26727
|
/** The ingress filter; an accepted packet is counted here, once. */
|
|
26638
26728
|
#acceptPacket(packet, sourceAddress) {
|
|
26639
|
-
const
|
|
26729
|
+
const source = this.#protectedSource;
|
|
26730
|
+
if (source !== void 0 && (sourceAddress === void 0 || sourceAddress !== this.#matchedSource)) {
|
|
26731
|
+
if (sourceAddress === void 0 || !sameIpAddress(sourceAddress, source)) {
|
|
26732
|
+
this.#stats.packetsDroppedForeignSender++;
|
|
26733
|
+
return false;
|
|
26734
|
+
}
|
|
26735
|
+
this.#matchedSource = sourceAddress;
|
|
26736
|
+
}
|
|
26737
|
+
const owner = source === void 0 ? this.#lockedTo?.() ?? this.#sender : void 0;
|
|
26640
26738
|
if (sourceAddress !== void 0 && owner !== void 0 && sourceAddress !== owner) {
|
|
26641
26739
|
this.#stats.packetsDroppedForeignSender++;
|
|
26642
26740
|
return false;
|
|
@@ -27160,9 +27258,7 @@ function repairTrackNamesBySource(tracks) {
|
|
|
27160
27258
|
}
|
|
27161
27259
|
function boundRepairNames(binding) {
|
|
27162
27260
|
if (binding?.status !== "ok") return [];
|
|
27163
|
-
|
|
27164
|
-
if (binding.overlay) names.push(binding.overlay.repairTrackName);
|
|
27165
|
-
return names;
|
|
27261
|
+
return binding.layers.map((layer) => layer.repairTrackName);
|
|
27166
27262
|
}
|
|
27167
27263
|
var MulticastManager = class {
|
|
27168
27264
|
#fec;
|
|
@@ -27928,7 +28024,11 @@ var MulticastManager = class {
|
|
|
27928
28024
|
console.warn("[MulticastManager] repair group release failed", error);
|
|
27929
28025
|
}
|
|
27930
28026
|
};
|
|
27931
|
-
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
|
+
}));
|
|
27932
28032
|
this.#applyRepairOwners(repairOwners);
|
|
27933
28033
|
for (const endpoint of endpoints) {
|
|
27934
28034
|
const ingress = {
|
|
@@ -27976,9 +28076,17 @@ var MulticastManager = class {
|
|
|
27976
28076
|
return release;
|
|
27977
28077
|
}
|
|
27978
28078
|
#applyRepairOwners(owners) {
|
|
27979
|
-
for (const { packetId, ownerPacketId } of owners) {
|
|
28079
|
+
for (const { packetId, ownerPacketId, instance, ownerTrackName, repairTrackName } of owners) {
|
|
27980
28080
|
this.#router.setPacketIdRole(packetId, "repair");
|
|
27981
|
-
|
|
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
|
+
}
|
|
27982
28090
|
}
|
|
27983
28091
|
}
|
|
27984
28092
|
// ---------------------------------------------------------------------------
|
|
@@ -28242,8 +28350,8 @@ var MulticastManager = class {
|
|
|
28242
28350
|
this.#fec.setMulticastSymbolDelivery(delivery);
|
|
28243
28351
|
}
|
|
28244
28352
|
/** Process a repair packet from MoQ (reliable QUIC). */
|
|
28245
|
-
processRepairFromMoQ(packet, ownerPacketId, catalogTrackName) {
|
|
28246
|
-
this.#router.feedRepairPacket(packet, ownerPacketId, catalogTrackName);
|
|
28353
|
+
processRepairFromMoQ(packet, ownerPacketId, catalogTrackName, repairTrackName) {
|
|
28354
|
+
this.#router.feedRepairPacket(packet, ownerPacketId, catalogTrackName, repairTrackName);
|
|
28247
28355
|
}
|
|
28248
28356
|
/** Route a Worker-recovered FEC block through the MMTP parser + dedup pipeline. */
|
|
28249
28357
|
routeRecoveredBlock(blockId, data, packetId, catalogTrackName) {
|
|
@@ -28295,7 +28403,10 @@ var MulticastManager = class {
|
|
|
28295
28403
|
const ownedRepairNames = /* @__PURE__ */ new Set();
|
|
28296
28404
|
const claimed = /* @__PURE__ */ new Map();
|
|
28297
28405
|
for (const binding of bindings.values()) {
|
|
28298
|
-
|
|
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
|
+
}
|
|
28299
28410
|
}
|
|
28300
28411
|
for (const [name, count] of claimed) if (count === 1) ownedRepairNames.add(name);
|
|
28301
28412
|
const repairOwnerGroup = /* @__PURE__ */ new Map();
|
|
@@ -28426,6 +28537,14 @@ var MulticastManager = class {
|
|
|
28426
28537
|
this.#router.setRepairPacketIdOwner(repairTrack.packetId, track.packetId);
|
|
28427
28538
|
}
|
|
28428
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
|
+
}
|
|
28429
28548
|
}
|
|
28430
28549
|
const wasmDecoder = fecDecoders.get(i);
|
|
28431
28550
|
if (track.packetId !== void 0 && wasmDecoder !== void 0) {
|