@blockcast/mmt-container 2.1.0-main.36792a9d6614 → 2.1.0
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 +42 -325
- package/dist/browser-runtime.js.map +4 -4
- package/dist/cmcd-producer.d.ts +0 -5
- package/dist/cmcd-producer.d.ts.map +1 -1
- package/dist/cmcd-producer.js.map +2 -2
- package/dist/mmtp-router.d.ts +1 -9
- package/dist/mmtp-router.d.ts.map +1 -1
- package/dist/mmtp-router.js +14 -1456
- package/dist/mmtp-router.js.map +4 -4
- package/dist/multicast-manager.d.ts +2 -13
- package/dist/multicast-manager.d.ts.map +1 -1
- package/dist/multicast-manager.js +5297 -5580
- package/dist/multicast-manager.js.map +4 -4
- package/dist/multicast-transport.d.ts +3 -11
- package/dist/multicast-transport.d.ts.map +1 -1
- package/dist/multicast-transport.js +19 -255
- package/dist/multicast-transport.js.map +4 -4
- package/package.json +5 -5
- package/src/cmcd-producer.ts +0 -5
- package/src/mmtp-router.ts +16 -81
- package/src/multicast-manager.ts +5 -23
- package/src/multicast-transport.ts +20 -44
package/dist/browser-runtime.js
CHANGED
|
@@ -7457,33 +7457,11 @@ var AdaptiveBitrateController = class {
|
|
|
7457
7457
|
};
|
|
7458
7458
|
|
|
7459
7459
|
// node_modules/@blockcast/mmt-base/dist/mmtp.js
|
|
7460
|
-
var MMTP_HEADER_SIZE2 = 12;
|
|
7461
7460
|
var MPU_HEADER_SIZE2 = 8;
|
|
7462
7461
|
var MPU_LENGTH_FIELD_SIZE2 = 2;
|
|
7463
7462
|
var MPU_HEADER_REMAINDER_SIZE2 = MPU_HEADER_SIZE2 - MPU_LENGTH_FIELD_SIZE2;
|
|
7464
7463
|
var MMTP_SIGNALING_PACKET_ID2 = 0;
|
|
7465
7464
|
var MMTP_PACKET_ID_MAX2 = 65535;
|
|
7466
|
-
function readMMTPPacketId2(buffer) {
|
|
7467
|
-
if (buffer.length < MMTP_HEADER_SIZE2)
|
|
7468
|
-
return null;
|
|
7469
|
-
const flags = buffer[0];
|
|
7470
|
-
if ((flags >> 6 & 3) !== 0)
|
|
7471
|
-
return null;
|
|
7472
|
-
let offset = MMTP_HEADER_SIZE2;
|
|
7473
|
-
if ((flags & 32) !== 0) {
|
|
7474
|
-
if (offset + 4 > buffer.length)
|
|
7475
|
-
return null;
|
|
7476
|
-
offset += 4;
|
|
7477
|
-
}
|
|
7478
|
-
if ((flags & 2) !== 0) {
|
|
7479
|
-
if (offset + 4 > buffer.length)
|
|
7480
|
-
return null;
|
|
7481
|
-
const length = buffer[offset + 2] << 8 | buffer[offset + 3];
|
|
7482
|
-
if (offset + 4 + length > buffer.length)
|
|
7483
|
-
return null;
|
|
7484
|
-
}
|
|
7485
|
-
return buffer[2] << 8 | buffer[3];
|
|
7486
|
-
}
|
|
7487
7465
|
|
|
7488
7466
|
// node_modules/@blockcast/mmt-base/dist/net-constants.js
|
|
7489
7467
|
var DEFAULT_AMT_RELAY_PORT2 = 2268;
|
|
@@ -8469,205 +8447,6 @@ var TransportManager = class {
|
|
|
8469
8447
|
};
|
|
8470
8448
|
var transportManager = new TransportManager();
|
|
8471
8449
|
|
|
8472
|
-
// node_modules/@blockcast/mmt-transport/dist/multicast-receiver-gateway.js
|
|
8473
|
-
var REGISTRY = /* @__PURE__ */ Symbol.for("@blockcast/mmt-transport/native-provider-registry/v1");
|
|
8474
|
-
var USER_AGENT_VERIFIED = /* @__PURE__ */ Symbol.for("@blockcast/mmt-transport/user-agent-verified/v1");
|
|
8475
|
-
var INTEGRITY_SCHEME = "manifest-ed25519-2";
|
|
8476
|
-
var MulticastError = class extends Error {
|
|
8477
|
-
code;
|
|
8478
|
-
constructor(code, message, cause) {
|
|
8479
|
-
super(message, cause === void 0 ? void 0 : { cause });
|
|
8480
|
-
this.name = "MulticastError";
|
|
8481
|
-
this.code = code;
|
|
8482
|
-
}
|
|
8483
|
-
};
|
|
8484
|
-
function datagramsOf(chunk) {
|
|
8485
|
-
const list = Array.isArray(chunk) ? chunk : chunk?.datagrams ?? [];
|
|
8486
|
-
const out = [];
|
|
8487
|
-
for (const d of list) {
|
|
8488
|
-
const data = d instanceof Uint8Array || d instanceof ArrayBuffer ? d : d?.data;
|
|
8489
|
-
if (data instanceof Uint8Array)
|
|
8490
|
-
out.push(data);
|
|
8491
|
-
else if (data instanceof ArrayBuffer)
|
|
8492
|
-
out.push(new Uint8Array(data));
|
|
8493
|
-
}
|
|
8494
|
-
return out;
|
|
8495
|
-
}
|
|
8496
|
-
function reasonOf(value) {
|
|
8497
|
-
return typeof value === "object" && value !== null && "reason" in value ? value.reason : value;
|
|
8498
|
-
}
|
|
8499
|
-
function registry() {
|
|
8500
|
-
const g = globalThis;
|
|
8501
|
-
if (g[REGISTRY] === void 0) {
|
|
8502
|
-
const providers = /* @__PURE__ */ new WeakSet();
|
|
8503
|
-
const byScope = /* @__PURE__ */ new WeakMap();
|
|
8504
|
-
Object.defineProperty(g, REGISTRY, {
|
|
8505
|
-
value: Object.freeze({
|
|
8506
|
-
isProvider: (x) => typeof x === "object" && x !== null && providers.has(x),
|
|
8507
|
-
providerFor: (scope) => {
|
|
8508
|
-
let provider = byScope.get(scope);
|
|
8509
|
-
if (provider === void 0) {
|
|
8510
|
-
provider = createProvider(scope);
|
|
8511
|
-
providers.add(provider);
|
|
8512
|
-
byScope.set(scope, provider);
|
|
8513
|
-
}
|
|
8514
|
-
return provider;
|
|
8515
|
-
}
|
|
8516
|
-
}),
|
|
8517
|
-
writable: false,
|
|
8518
|
-
configurable: false,
|
|
8519
|
-
enumerable: false
|
|
8520
|
-
});
|
|
8521
|
-
}
|
|
8522
|
-
return g[REGISTRY];
|
|
8523
|
-
}
|
|
8524
|
-
function resolveMulticastGateway(scope = globalThis) {
|
|
8525
|
-
const s = scope;
|
|
8526
|
-
if (typeof s.MulticastReceiver === "function")
|
|
8527
|
-
return registry().providerFor(scope);
|
|
8528
|
-
if (typeof s.multicast?.subscribe === "function")
|
|
8529
|
-
return s.multicast;
|
|
8530
|
-
return void 0;
|
|
8531
|
-
}
|
|
8532
|
-
function isUserAgentVerifiedGateway(gateway) {
|
|
8533
|
-
return registry().isProvider(gateway);
|
|
8534
|
-
}
|
|
8535
|
-
function userAgentVerifiedIngress(ingress, gateway) {
|
|
8536
|
-
if (isUserAgentVerifiedGateway(gateway))
|
|
8537
|
-
return { ...ingress, admittedBy: USER_AGENT_VERIFIED };
|
|
8538
|
-
if (!("admittedBy" in ingress))
|
|
8539
|
-
return ingress;
|
|
8540
|
-
const { admittedBy: _, ...rest } = ingress;
|
|
8541
|
-
return rest;
|
|
8542
|
-
}
|
|
8543
|
-
function unsupported(message, cause) {
|
|
8544
|
-
return new MulticastError("capability-unsupported", message, cause);
|
|
8545
|
-
}
|
|
8546
|
-
function createProvider(scope) {
|
|
8547
|
-
const Receiver = scope.MulticastReceiver;
|
|
8548
|
-
let gate = null;
|
|
8549
|
-
return {
|
|
8550
|
-
capabilities: () => Promise.resolve({ consumeModes: ["bytes"] }),
|
|
8551
|
-
subscribe: async (config) => {
|
|
8552
|
-
const c = config;
|
|
8553
|
-
if (c.consume !== "bytes")
|
|
8554
|
-
throw unsupported(`consume mode ${String(c.consume)} is not supported`);
|
|
8555
|
-
const origin = c.origin ?? "ip-multicast";
|
|
8556
|
-
if (origin !== "ip-multicast" && !(origin === "amt" && c.discovery === "driad")) {
|
|
8557
|
-
throw unsupported(`origin ${String(origin)} is not supported by MulticastReceiver`);
|
|
8558
|
-
}
|
|
8559
|
-
if (c.relay !== void 0)
|
|
8560
|
-
throw unsupported("MulticastReceiver picks its own AMT relay");
|
|
8561
|
-
if (!c.source)
|
|
8562
|
-
throw unsupported("MulticastReceiver needs an SSM source");
|
|
8563
|
-
if (c.senderAddress !== void 0 && c.senderAddress !== c.source) {
|
|
8564
|
-
throw unsupported("MulticastReceiver filters by source only");
|
|
8565
|
-
}
|
|
8566
|
-
const options = { sourceAddress: c.source, groupAddress: c.group, localPort: c.port };
|
|
8567
|
-
if (gate !== null)
|
|
8568
|
-
await gate;
|
|
8569
|
-
let rx;
|
|
8570
|
-
try {
|
|
8571
|
-
rx = new Receiver(options);
|
|
8572
|
-
} catch (error) {
|
|
8573
|
-
throw unsupported("MulticastReceiver refused the request", error);
|
|
8574
|
-
}
|
|
8575
|
-
const opened = rx.opened;
|
|
8576
|
-
if (gate === null)
|
|
8577
|
-
gate = opened.then(() => void 0, () => void 0);
|
|
8578
|
-
return openSubscription(rx, opened, options, c.packetIds);
|
|
8579
|
-
}
|
|
8580
|
-
};
|
|
8581
|
-
}
|
|
8582
|
-
async function openSubscription(rx, openedPromise, options, packetIds) {
|
|
8583
|
-
let opened;
|
|
8584
|
-
try {
|
|
8585
|
-
opened = await openedPromise;
|
|
8586
|
-
} catch (error) {
|
|
8587
|
-
throw new MulticastError("subscription-failed", "MulticastReceiver did not open", error);
|
|
8588
|
-
}
|
|
8589
|
-
if (rx.integrityScheme !== INTEGRITY_SCHEME) {
|
|
8590
|
-
await rx.close();
|
|
8591
|
-
throw unsupported(`integrity scheme ${String(rx.integrityScheme)} is not ${INTEGRITY_SCHEME}`);
|
|
8592
|
-
}
|
|
8593
|
-
const events = new EventTarget();
|
|
8594
|
-
let state = "connected";
|
|
8595
|
-
let filter = packetIds?.length ? new Set(packetIds) : null;
|
|
8596
|
-
let closing = false;
|
|
8597
|
-
const setState = (next) => {
|
|
8598
|
-
if (state === next)
|
|
8599
|
-
return;
|
|
8600
|
-
state = next;
|
|
8601
|
-
events.dispatchEvent(new CustomEvent("state", { detail: next }));
|
|
8602
|
-
};
|
|
8603
|
-
const sourceAddress = options.sourceAddress;
|
|
8604
|
-
const subscription = {
|
|
8605
|
-
id: `${options.sourceAddress}|${options.groupAddress}|${options.localPort}`,
|
|
8606
|
-
get state() {
|
|
8607
|
-
return state;
|
|
8608
|
-
},
|
|
8609
|
-
delivery: "datagram",
|
|
8610
|
-
eventTypes: Object.freeze(["bytes", "packets", "state", "error"]),
|
|
8611
|
-
setPacketIds(ids) {
|
|
8612
|
-
filter = ids.length ? new Set(ids) : null;
|
|
8613
|
-
},
|
|
8614
|
-
addEventListener: events.addEventListener.bind(events),
|
|
8615
|
-
removeEventListener: events.removeEventListener.bind(events),
|
|
8616
|
-
async unsubscribe() {
|
|
8617
|
-
closing = true;
|
|
8618
|
-
await rx.close();
|
|
8619
|
-
setState("closed");
|
|
8620
|
-
}
|
|
8621
|
-
};
|
|
8622
|
-
void (async () => {
|
|
8623
|
-
const reader = opened.readable.getReader();
|
|
8624
|
-
try {
|
|
8625
|
-
for (; ; ) {
|
|
8626
|
-
const { value, done } = await reader.read();
|
|
8627
|
-
if (done)
|
|
8628
|
-
break;
|
|
8629
|
-
const packets = [];
|
|
8630
|
-
for (const data of datagramsOf(value)) {
|
|
8631
|
-
if (filter !== null) {
|
|
8632
|
-
const id = readMMTPPacketId2(data);
|
|
8633
|
-
if (id === null || !filter.has(id))
|
|
8634
|
-
continue;
|
|
8635
|
-
}
|
|
8636
|
-
const copy = data.slice().buffer;
|
|
8637
|
-
packets.push(copy);
|
|
8638
|
-
events.dispatchEvent(Object.assign(new CustomEvent("bytes", { detail: copy }), { sourceAddress }));
|
|
8639
|
-
}
|
|
8640
|
-
if (packets.length > 0)
|
|
8641
|
-
events.dispatchEvent(new CustomEvent("packets", { detail: packets }));
|
|
8642
|
-
}
|
|
8643
|
-
} catch {
|
|
8644
|
-
}
|
|
8645
|
-
})();
|
|
8646
|
-
void rx.closed.then((value) => {
|
|
8647
|
-
const reason = reasonOf(value);
|
|
8648
|
-
if (!closing) {
|
|
8649
|
-
if (reason === "consent-withdrawn") {
|
|
8650
|
-
events.dispatchEvent(new CustomEvent("error", {
|
|
8651
|
-
detail: new MulticastError("origin-not-trusted", "multicast consent withdrawn")
|
|
8652
|
-
}));
|
|
8653
|
-
} else if (reason === "oversubscribed" || reason === "network") {
|
|
8654
|
-
events.dispatchEvent(new CustomEvent("error", {
|
|
8655
|
-
detail: new MulticastError("subscription-failed", `MulticastReceiver closed: ${reason}`)
|
|
8656
|
-
}));
|
|
8657
|
-
}
|
|
8658
|
-
}
|
|
8659
|
-
setState("closed");
|
|
8660
|
-
}, (error) => {
|
|
8661
|
-
if (!closing) {
|
|
8662
|
-
events.dispatchEvent(new CustomEvent("error", {
|
|
8663
|
-
detail: new MulticastError("subscription-failed", "MulticastReceiver failed", error)
|
|
8664
|
-
}));
|
|
8665
|
-
}
|
|
8666
|
-
setState("closed");
|
|
8667
|
-
});
|
|
8668
|
-
return subscription;
|
|
8669
|
-
}
|
|
8670
|
-
|
|
8671
8450
|
// node_modules/@blockcast/mmt-transport/dist/moqt-priority.js
|
|
8672
8451
|
var MAX_MOQT_PRIORITY = 255;
|
|
8673
8452
|
function moqtPriority(value, field) {
|
|
@@ -8954,9 +8733,9 @@ function validateTemplatePair(mediaUriTemplate, mediaObjectTemplate, path) {
|
|
|
8954
8733
|
}
|
|
8955
8734
|
function validateMmtpDashTemplate(template, path) {
|
|
8956
8735
|
const variables = [...template.matchAll(TEMPLATE_VARIABLE)].map((match) => match[1]);
|
|
8957
|
-
const
|
|
8958
|
-
if (
|
|
8959
|
-
throw new Error(`${path} contains unsupported MMTP-backed DASH template variable $${
|
|
8736
|
+
const unsupported = variables.find((variable) => variable !== "Number" && variable !== "RepresentationID");
|
|
8737
|
+
if (unsupported !== void 0) {
|
|
8738
|
+
throw new Error(`${path} contains unsupported MMTP-backed DASH template variable $${unsupported}$`);
|
|
8960
8739
|
}
|
|
8961
8740
|
if (variables.filter((variable) => variable === "Number").length !== 1) {
|
|
8962
8741
|
throw new Error(`${path} must contain exactly one $Number$ for MMTP-backed DASH`);
|
|
@@ -17319,17 +17098,17 @@ var FECManager = class _FECManager {
|
|
|
17319
17098
|
|
|
17320
17099
|
// src/mfu-reassembly-policy.ts
|
|
17321
17100
|
function validateReliableMoqOptionShape(value) {
|
|
17322
|
-
const
|
|
17101
|
+
const unsupported = Object.keys(value).filter(
|
|
17323
17102
|
(key) => key !== "transport" && key !== "startsAtRandomAccessGroup"
|
|
17324
17103
|
);
|
|
17325
|
-
if (
|
|
17326
|
-
if (
|
|
17104
|
+
if (unsupported.length > 0) {
|
|
17105
|
+
if (unsupported.includes("maxRacingGroups")) {
|
|
17327
17106
|
throw new Error(
|
|
17328
17107
|
"[MFU reassembly] maxRacingGroups was removed; reliable MoQ completion is transport-owned"
|
|
17329
17108
|
);
|
|
17330
17109
|
}
|
|
17331
17110
|
throw new Error(
|
|
17332
|
-
`[MFU reassembly] unsupported reliable MoQ option(s): ${
|
|
17111
|
+
`[MFU reassembly] unsupported reliable MoQ option(s): ${unsupported.join(", ")}`
|
|
17333
17112
|
);
|
|
17334
17113
|
}
|
|
17335
17114
|
const startsAtRandomAccessGroup = value["startsAtRandomAccessGroup"];
|
|
@@ -17373,12 +17152,12 @@ function assertValidMfuReassemblyPolicy(policy) {
|
|
|
17373
17152
|
return;
|
|
17374
17153
|
}
|
|
17375
17154
|
if (value["transport"] === "datagram") {
|
|
17376
|
-
const
|
|
17155
|
+
const unsupported = Object.keys(value).filter(
|
|
17377
17156
|
(key) => key !== "transport" && key !== "deadlineMs"
|
|
17378
17157
|
);
|
|
17379
|
-
if (
|
|
17158
|
+
if (unsupported.length > 0) {
|
|
17380
17159
|
throw new Error(
|
|
17381
|
-
`[MFU reassembly] unsupported datagram option(s): ${
|
|
17160
|
+
`[MFU reassembly] unsupported datagram option(s): ${unsupported.join(", ")}`
|
|
17382
17161
|
);
|
|
17383
17162
|
}
|
|
17384
17163
|
assertValidDatagramDeadline(value["deadlineMs"]);
|
|
@@ -23800,11 +23579,10 @@ var MmtpRouter = class {
|
|
|
23800
23579
|
if (!entry) return;
|
|
23801
23580
|
entry.receiver.release();
|
|
23802
23581
|
entry.protected = false;
|
|
23803
|
-
entry.userAgent = false;
|
|
23804
23582
|
this.#assistBlocks.clear();
|
|
23805
23583
|
this.#assistFailures.clear();
|
|
23806
23584
|
}
|
|
23807
|
-
/**
|
|
23585
|
+
/** MoQ packets of a track a protected flow lists, dropped on its unicast path: duplicate, overflow, or listed by more than one flow. */
|
|
23808
23586
|
get unadmittedListedDropCount() {
|
|
23809
23587
|
return this.#unadmittedListedDrops;
|
|
23810
23588
|
}
|
|
@@ -23845,13 +23623,6 @@ var MmtpRouter = class {
|
|
|
23845
23623
|
}
|
|
23846
23624
|
return false;
|
|
23847
23625
|
}
|
|
23848
|
-
/** Whether a protected flow the user agent admitted lists `packetId`. */
|
|
23849
|
-
#userAgentGoverns(packetId) {
|
|
23850
|
-
for (const entry of this.#provenance.values()) {
|
|
23851
|
-
if (entry.protected && entry.userAgent && ingressHasPacketId(entry.ingress, packetId)) return true;
|
|
23852
|
-
}
|
|
23853
|
-
return false;
|
|
23854
|
-
}
|
|
23855
23626
|
/**
|
|
23856
23627
|
* A MoQ repair symbol no protected flow lists, owned by a source track one
|
|
23857
23628
|
* does: its FEC instance is governed (7.2.7 step 2) and takes only admitted
|
|
@@ -23942,7 +23713,7 @@ var MmtpRouter = class {
|
|
|
23942
23713
|
fec.cancelBlockTimeout(blockStart, ownerPacketId, catalogTrackName);
|
|
23943
23714
|
fec.countRecovery(ownerPacketId, blockStart, catalogTrackName);
|
|
23944
23715
|
fec.markBlockCompleted(blockStart, ownerPacketId, catalogTrackName);
|
|
23945
|
-
}
|
|
23716
|
+
})) {
|
|
23946
23717
|
this.#unlistedAssistedRecoveries++;
|
|
23947
23718
|
this.#assistBlocks.delete(key);
|
|
23948
23719
|
return;
|
|
@@ -25098,12 +24869,6 @@ var MmtpRouter = class {
|
|
|
25098
24869
|
debugRouter(`[MmtpRouter] Dropping multicast packet pid=${info.packetId}: flow ${ingress.flow} has no receiver`);
|
|
25099
24870
|
return;
|
|
25100
24871
|
}
|
|
25101
|
-
if (ingress.admittedBy === USER_AGENT_VERIFIED) {
|
|
25102
|
-
this.#noteFlowAdmitted(ingress.flow);
|
|
25103
|
-
protectedFlow.userAgent = true;
|
|
25104
|
-
this.#processMMTP(packet, source, true, ingress, overlapCopy);
|
|
25105
|
-
return;
|
|
25106
|
-
}
|
|
25107
24872
|
const { verdict } = protectedFlow.receiver.onDatagram(packet, "multicast");
|
|
25108
24873
|
if (verdict === "admitted") {
|
|
25109
24874
|
this.#noteFlowAdmitted(ingress.flow);
|
|
@@ -25121,11 +24886,6 @@ var MmtpRouter = class {
|
|
|
25121
24886
|
return;
|
|
25122
24887
|
}
|
|
25123
24888
|
}
|
|
25124
|
-
const flowless = source === "multicast" && !manifestAdmitted && ingress?.flow === void 0;
|
|
25125
|
-
if (flowless && !info.isRepair && this.#userAgentGoverns(info.packetId)) {
|
|
25126
|
-
this.#unadmittedListedDrops++;
|
|
25127
|
-
return;
|
|
25128
|
-
}
|
|
25129
24889
|
if (info.isRepair) {
|
|
25130
24890
|
const overlay = this.#overlayRepairTarget(info.packetId, catalogTrackNameForPacket(ingress, info.packetId));
|
|
25131
24891
|
if (overlay !== void 0) {
|
|
@@ -25143,7 +24903,7 @@ var MmtpRouter = class {
|
|
|
25143
24903
|
);
|
|
25144
24904
|
if (!repair) return;
|
|
25145
24905
|
if (ownerPacketId != null && this.#fecManager) {
|
|
25146
|
-
if (
|
|
24906
|
+
if (source === "moq" && !manifestAdmitted && this.#setAsideUnlistedRepair(ownerPacketId, catalogTrackNameForPacket(ingress, ownerPacketId), repair)) return;
|
|
25147
24907
|
this.#repairPidCounts[info.packetId] = (this.#repairPidCounts[info.packetId] || 0) + 1;
|
|
25148
24908
|
const count = this.#repairPidCounts[info.packetId];
|
|
25149
24909
|
if (count <= 2 || count % 500 === 0) {
|
|
@@ -25722,7 +25482,7 @@ var MmtpRouter = class {
|
|
|
25722
25482
|
}
|
|
25723
25483
|
// ── Private: FEC recovery routing ───────────────────────────────
|
|
25724
25484
|
/** Route WASM-recovered block through appropriate path based on container kind. */
|
|
25725
|
-
#routeRecoveredBlock(blockId, recovered, ownerPacketId, catalogTrackName, complete
|
|
25485
|
+
#routeRecoveredBlock(blockId, recovered, ownerPacketId, catalogTrackName, complete) {
|
|
25726
25486
|
const packetSymbolSize = this.#symbolSizeForPacket(ownerPacketId);
|
|
25727
25487
|
if (ownerPacketId === void 0) {
|
|
25728
25488
|
throw new Error("[MmtpRouter] a recovered FEC block needs its owner packetId to read its fec.blockProfile");
|
|
@@ -25739,7 +25499,7 @@ var MmtpRouter = class {
|
|
|
25739
25499
|
ownerPacketId,
|
|
25740
25500
|
blockProfile
|
|
25741
25501
|
});
|
|
25742
|
-
return this.#routeVerifiedFecSymbols(blockId, result, ownerPacketId, catalogTrackName, complete
|
|
25502
|
+
return this.#routeVerifiedFecSymbols(blockId, result, ownerPacketId, catalogTrackName, complete);
|
|
25743
25503
|
}
|
|
25744
25504
|
/**
|
|
25745
25505
|
* A recovered source packet of a protected flow is admitted on its own
|
|
@@ -25751,24 +25511,9 @@ var MmtpRouter = class {
|
|
|
25751
25511
|
* recovered packets is refused is unrecovered (7.2.7): its caller leaves it
|
|
25752
25512
|
* open, so it reaches its FEC deadline and fails by class. Always true
|
|
25753
25513
|
* outside a protected flow.
|
|
25754
|
-
|
|
25755
|
-
|
|
25756
|
-
|
|
25757
|
-
* symbols the user agent or a record verified (a flow-less multicast
|
|
25758
|
-
* datagram of a listed track is dropped or set aside in #processMMTP), so
|
|
25759
|
-
* what it recovers is as verified as they are. An `assisted` (scratch-decoder) recovery mixes in
|
|
25760
|
-
* unlisted MoQ repair and still needs a record.
|
|
25761
|
-
* ponytail: per-flow flag; per-block provenance only if a protected flow
|
|
25762
|
-
* ever mixes verified and unverified live inputs.
|
|
25763
|
-
* ponytail: a worker frame computed before the protection reset can still
|
|
25764
|
-
* arrive after it (frames carry no epoch); its inputs are MoQ copies read
|
|
25765
|
-
* and played before protection, so it admits nothing new. Drop pre-reset
|
|
25766
|
-
* frames by epoch only if pre-protection input ever matters.
|
|
25767
|
-
*/
|
|
25768
|
-
#routeVerifiedFecSymbols(blockId, result, ownerPacketId, catalogTrackName, complete, assisted = false) {
|
|
25769
|
-
const entry = ownerPacketId == null ? void 0 : this.#provenanceFor(ownerPacketId, catalogTrackName);
|
|
25770
|
-
const receiver = entry === void 0 || entry === "ambiguous" ? entry : entry.receiver;
|
|
25771
|
-
const userAgent = !assisted && entry !== void 0 && entry !== "ambiguous" && entry.protected === true && entry.userAgent === true;
|
|
25514
|
+
*/
|
|
25515
|
+
#routeVerifiedFecSymbols(blockId, result, ownerPacketId, catalogTrackName, complete) {
|
|
25516
|
+
const receiver = ownerPacketId == null ? void 0 : this.#provenanceFor(ownerPacketId, catalogTrackName);
|
|
25772
25517
|
let recovered = true;
|
|
25773
25518
|
if (receiver) {
|
|
25774
25519
|
const parsed = this.#stream(ownerPacketId, catalogTrackName).duplicates.parsedSymbols;
|
|
@@ -25780,8 +25525,6 @@ var MmtpRouter = class {
|
|
|
25780
25525
|
let admitted = false;
|
|
25781
25526
|
if (receiver === "ambiguous") {
|
|
25782
25527
|
this.#ambiguousRecoveredDrops++;
|
|
25783
|
-
} else if (userAgent) {
|
|
25784
|
-
admitted = true;
|
|
25785
25528
|
} else {
|
|
25786
25529
|
const wire = new Uint8Array(sym.rawSymbol.byteLength + 4);
|
|
25787
25530
|
wire.set(sym.rawSymbol);
|
|
@@ -25809,12 +25552,12 @@ var MmtpRouter = class {
|
|
|
25809
25552
|
catalogTrackName === void 0 || !byName ? byName?.values() : byName.has(catalogTrackName) ? [byName.get(catalogTrackName)] : []
|
|
25810
25553
|
);
|
|
25811
25554
|
if (flows.size > 1) return "ambiguous";
|
|
25812
|
-
for (const bound of flows) return this.#provenance.get(bound) ?? "ambiguous";
|
|
25555
|
+
for (const bound of flows) return this.#provenance.get(bound)?.receiver ?? "ambiguous";
|
|
25813
25556
|
let match;
|
|
25814
|
-
for (const
|
|
25815
|
-
if (
|
|
25557
|
+
for (const { receiver, ingress } of this.#provenance.values()) {
|
|
25558
|
+
if (ingress.tracks.some((t) => t.packetId === packetId && (catalogTrackName === void 0 || t.name === catalogTrackName))) {
|
|
25816
25559
|
if (match) return "ambiguous";
|
|
25817
|
-
match =
|
|
25560
|
+
match = receiver;
|
|
25818
25561
|
}
|
|
25819
25562
|
}
|
|
25820
25563
|
return match;
|
|
@@ -26596,8 +26339,11 @@ function providerErrorOf(error) {
|
|
|
26596
26339
|
return Object.freeze(typeof code === "string" ? { name, message, code } : { name, message });
|
|
26597
26340
|
}
|
|
26598
26341
|
var MAX_TRACKED_DROPPED_PIDS = 16;
|
|
26342
|
+
function getMulticastGlobals() {
|
|
26343
|
+
return globalThis;
|
|
26344
|
+
}
|
|
26599
26345
|
function hasMulticastGateway() {
|
|
26600
|
-
return typeof
|
|
26346
|
+
return typeof getMulticastGlobals().multicast?.subscribe === "function";
|
|
26601
26347
|
}
|
|
26602
26348
|
function senderOf(event) {
|
|
26603
26349
|
const sourceAddress = event.sourceAddress;
|
|
@@ -26689,9 +26435,6 @@ var MulticastTransport = class {
|
|
|
26689
26435
|
// listener closure, so an in-place admission change (setContext) applies
|
|
26690
26436
|
// from the next packet on without rejoining.
|
|
26691
26437
|
#context = void 0;
|
|
26692
|
-
#deliveredContext = void 0;
|
|
26693
|
-
/** The live join's gateway, when it is the user agent's MulticastReceiver provider. */
|
|
26694
|
-
#userAgentGateway = void 0;
|
|
26695
26438
|
// The live join's provider-side filter. An in-place widening must reach it,
|
|
26696
26439
|
// or the provider never delivers the new ID; null without a live join.
|
|
26697
26440
|
#providerFilter = null;
|
|
@@ -26728,7 +26471,7 @@ var MulticastTransport = class {
|
|
|
26728
26471
|
this.#dataPlaneLive = false;
|
|
26729
26472
|
this.#sender = void 0;
|
|
26730
26473
|
if (this.#disposed) return this.#declined(generation, "disposed");
|
|
26731
|
-
const gateway = this.#gateway ??
|
|
26474
|
+
const gateway = this.#gateway ?? getMulticastGlobals().multicast;
|
|
26732
26475
|
if (typeof gateway?.subscribe !== "function") {
|
|
26733
26476
|
console.warn("[MulticastTransport] Multicast API not available");
|
|
26734
26477
|
return this.#declined(generation, "unavailable");
|
|
@@ -26744,16 +26487,15 @@ var MulticastTransport = class {
|
|
|
26744
26487
|
console.log(
|
|
26745
26488
|
`[MulticastTransport] window.multicast.subscribe(${config.sourceAddress ?? config.senderAddress ?? "*"}, ${config.groupAddress}, ${config.port})`
|
|
26746
26489
|
);
|
|
26747
|
-
const userAgent = isUserAgentVerifiedGateway(gateway);
|
|
26748
26490
|
const subscribeConfig = toWindowMulticastSubscribeConfig({
|
|
26749
26491
|
source: config.sourceAddress,
|
|
26750
26492
|
senderAddress: config.senderAddress,
|
|
26751
26493
|
group: config.groupAddress,
|
|
26752
26494
|
port: config.port,
|
|
26753
|
-
mode:
|
|
26754
|
-
amtRelay:
|
|
26755
|
-
amtRelayPort:
|
|
26756
|
-
amtDiscovery:
|
|
26495
|
+
mode: config.mode,
|
|
26496
|
+
amtRelay: config.amtRelay,
|
|
26497
|
+
amtRelayPort: config.amtRelayPort,
|
|
26498
|
+
amtDiscovery: config.amtDiscovery,
|
|
26757
26499
|
packetIds: config.packetIds,
|
|
26758
26500
|
tracks: config.tracks,
|
|
26759
26501
|
atsc3: config.atsc3
|
|
@@ -26799,8 +26541,7 @@ var MulticastTransport = class {
|
|
|
26799
26541
|
acknowledgedUpdate: 0,
|
|
26800
26542
|
inFlight: 0
|
|
26801
26543
|
};
|
|
26802
|
-
this.#
|
|
26803
|
-
this.#setContext(context);
|
|
26544
|
+
this.#context = context;
|
|
26804
26545
|
if (packetIds) {
|
|
26805
26546
|
for (const packetId of packetIds) {
|
|
26806
26547
|
if (!this.#acceptedBytesByPacketId.has(packetId)) {
|
|
@@ -26808,7 +26549,7 @@ var MulticastTransport = class {
|
|
|
26808
26549
|
}
|
|
26809
26550
|
}
|
|
26810
26551
|
}
|
|
26811
|
-
this.#resolvedTransport =
|
|
26552
|
+
this.#resolvedTransport = subscribeConfig.origin === "amt" ? "amt" : "native";
|
|
26812
26553
|
const delivery = subscription.delivery ?? "datagram";
|
|
26813
26554
|
if (delivery !== "datagram" && delivery !== "ordered-relay") {
|
|
26814
26555
|
throw new Error(
|
|
@@ -26889,7 +26630,7 @@ var MulticastTransport = class {
|
|
|
26889
26630
|
}
|
|
26890
26631
|
if (!this.#acceptPacket(packet, sourceAddress)) return false;
|
|
26891
26632
|
const liveness = !this.#isRecordCandidate(packet);
|
|
26892
|
-
this.#onPacket(packet, this.#
|
|
26633
|
+
this.#onPacket(packet, this.#context);
|
|
26893
26634
|
return liveness;
|
|
26894
26635
|
}
|
|
26895
26636
|
#isRecordCandidate(packet) {
|
|
@@ -27100,10 +26841,8 @@ var MulticastTransport = class {
|
|
|
27100
26841
|
return this.#subscription !== null;
|
|
27101
26842
|
}
|
|
27102
26843
|
/**
|
|
27103
|
-
*
|
|
27104
|
-
* the last setContext), or undefined without a live join.
|
|
27105
|
-
* provider each accepted packet is delivered with a marked copy of it
|
|
27106
|
-
* (`userAgentVerifiedIngress`); this getter never carries the mark.
|
|
26844
|
+
* What the live join delivers with every accepted packet (the join's
|
|
26845
|
+
* `context`, or the last setContext), or undefined without a live join.
|
|
27107
26846
|
*/
|
|
27108
26847
|
get context() {
|
|
27109
26848
|
return this.#context;
|
|
@@ -27127,16 +26866,7 @@ var MulticastTransport = class {
|
|
|
27127
26866
|
"[MulticastTransport] setContext needs a live join; join() carries the first context"
|
|
27128
26867
|
);
|
|
27129
26868
|
}
|
|
27130
|
-
this.#setContext(context);
|
|
27131
|
-
}
|
|
27132
|
-
/**
|
|
27133
|
-
* The join's context, and what the subscription delivers with each packet:
|
|
27134
|
-
* marked admitted by the user agent when the gateway is its native
|
|
27135
|
-
* MulticastReceiver provider. feedPacket never marks.
|
|
27136
|
-
*/
|
|
27137
|
-
#setContext(context) {
|
|
27138
26869
|
this.#context = context;
|
|
27139
|
-
this.#deliveredContext = this.#userAgentGateway !== void 0 && typeof context === "object" && context !== null ? userAgentVerifiedIngress(context, this.#userAgentGateway) : context;
|
|
27140
26870
|
}
|
|
27141
26871
|
/**
|
|
27142
26872
|
* Replace the live join's packet-ID allow-list in place, without rejoining.
|
|
@@ -27268,8 +26998,6 @@ var MulticastTransport = class {
|
|
|
27268
26998
|
this.#matchedSource = void 0;
|
|
27269
26999
|
this.#providerFilter = null;
|
|
27270
27000
|
this.#context = void 0;
|
|
27271
|
-
this.#deliveredContext = void 0;
|
|
27272
|
-
this.#userAgentGateway = void 0;
|
|
27273
27001
|
this.#withdrawnPacketIds.clear();
|
|
27274
27002
|
}
|
|
27275
27003
|
/** The ingress filter; an accepted packet is counted here, once. */
|
|
@@ -28622,17 +28350,8 @@ var MulticastManager = class {
|
|
|
28622
28350
|
* repair roles and owners are registered here; in-band owners stay the
|
|
28623
28351
|
* caller's. Joining is best effort: a group that fails to join is logged
|
|
28624
28352
|
* and leaves the media join as it was.
|
|
28625
|
-
|
|
28626
|
-
|
|
28627
|
-
* When it is the browser's own MulticastReceiver provider, the callback's
|
|
28628
|
-
* packets are fed as admitted by the user agent
|
|
28629
|
-
* (`userAgentVerifiedIngress`); nothing the callback passes can mark them.
|
|
28630
|
-
* The mark rests on this statement, not on the data path: every packet the
|
|
28631
|
-
* callback delivers must come from a subscription opened on
|
|
28632
|
-
* `options.gateway`. A callback that also merges another source (a MoQ or
|
|
28633
|
-
* WebSocket repair feed) must not name a native provider here.
|
|
28634
|
-
*/
|
|
28635
|
-
joinRepairEndpoints(endpoints, provider, options) {
|
|
28353
|
+
*/
|
|
28354
|
+
joinRepairEndpoints(endpoints, provider) {
|
|
28636
28355
|
const releases = [];
|
|
28637
28356
|
let released = false;
|
|
28638
28357
|
const safely = (each) => {
|
|
@@ -28654,19 +28373,17 @@ var MulticastManager = class {
|
|
|
28654
28373
|
...endpoint.flow === void 0 ? {} : { flow: endpoint.flow }
|
|
28655
28374
|
};
|
|
28656
28375
|
let sender;
|
|
28657
|
-
const
|
|
28376
|
+
const onPacket = (packet, sourceAddress) => {
|
|
28658
28377
|
if (released) return;
|
|
28659
28378
|
if (sourceAddress !== void 0 && !sameDatagramSource(sourceAddress, this.#transport.sender ?? (sender ??= sourceAddress))) return;
|
|
28660
|
-
this.#router.feedPacket(packet, "multicast",
|
|
28379
|
+
this.#router.feedPacket(packet, "multicast", ingress);
|
|
28661
28380
|
};
|
|
28662
28381
|
const fail = (error) => console.warn(`[MulticastManager] repair group ${endpoint.group}:${endpoint.port} not joined`, error);
|
|
28663
28382
|
if (typeof provider === "function") {
|
|
28664
|
-
const marked = userAgentVerifiedIngress(ingress, options?.gateway);
|
|
28665
|
-
const onPacket = (packet, sourceAddress) => feed(packet, sourceAddress, marked);
|
|
28666
28383
|
Promise.resolve().then(() => provider(endpoint, onPacket)).then((release2) => released ? safely(release2) : releases.push(release2), fail);
|
|
28667
28384
|
continue;
|
|
28668
28385
|
}
|
|
28669
|
-
const transport = new MulticastTransport((packet
|
|
28386
|
+
const transport = new MulticastTransport((packet) => onPacket(packet), {
|
|
28670
28387
|
gateway: provider,
|
|
28671
28388
|
lockedTo: () => this.#transport.sender
|
|
28672
28389
|
});
|
|
@@ -28682,7 +28399,7 @@ var MulticastManager = class {
|
|
|
28682
28399
|
tracks: endpoint.tracks.map(({ name, packetId }) => ({ name, packetId })),
|
|
28683
28400
|
// A protected repair group's records ride in band on its port (7.2.3).
|
|
28684
28401
|
...endpoint.flow === void 0 ? {} : { protected: true }
|
|
28685
|
-
}
|
|
28402
|
+
}).then((joined) => {
|
|
28686
28403
|
if (!joined && !released) fail(transport.lastJoinOutcome);
|
|
28687
28404
|
});
|
|
28688
28405
|
}
|