@blockcast/mmt-container 1.0.1 → 2.0.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/README.md +9 -7
- package/dist/browser-runtime.js +6931 -6685
- package/dist/browser-runtime.js.map +4 -4
- package/dist/carp-catalog.d.ts +7 -7
- package/dist/carp-catalog.d.ts.map +1 -1
- package/dist/carp-catalog.js +65 -230
- package/dist/carp-catalog.js.map +3 -3
- package/dist/catalog-fec-params.js.map +1 -1
- package/dist/catalog-network-source.js.map +1 -1
- package/dist/delivery-catalog.js.map +1 -1
- package/dist/delta-fallback-config.js.map +1 -1
- package/dist/fec-manager.d.ts +25 -1
- package/dist/fec-manager.d.ts.map +1 -1
- package/dist/fec-manager.js +208 -15
- package/dist/fec-manager.js.map +2 -2
- package/dist/fec-state.js.map +1 -1
- package/dist/fec-worker-setup.js.map +1 -1
- package/dist/held-layer-admission.d.ts +8 -0
- package/dist/held-layer-admission.d.ts.map +1 -1
- package/dist/held-layer-admission.js +22 -1
- package/dist/held-layer-admission.js.map +2 -2
- package/dist/iso-block-locator.js.map +1 -1
- package/dist/mfu-reassembler.js.map +1 -1
- package/dist/mmtp-container-parser.js.map +1 -1
- package/dist/mmtp-container-parser.no-modules.js +127 -322
- package/dist/mmtp-container-parser.no-modules.js.map +4 -4
- package/dist/mmtp-decode-pipeline.js.map +1 -1
- package/dist/mmtp-fec-session.d.ts.map +1 -1
- package/dist/mmtp-fec-session.js +61 -15
- package/dist/mmtp-fec-session.js.map +3 -3
- package/dist/mmtp-router.d.ts +11 -6
- package/dist/mmtp-router.d.ts.map +1 -1
- package/dist/mmtp-router.js +47 -10
- package/dist/mmtp-router.js.map +3 -3
- package/dist/multicast-auth-catalog.d.ts +8 -8
- package/dist/multicast-auth-catalog.d.ts.map +1 -1
- package/dist/multicast-auth-catalog.js +171 -75
- package/dist/multicast-auth-catalog.js.map +4 -4
- package/dist/multicast-input.d.ts +5 -5
- package/dist/multicast-input.d.ts.map +1 -1
- package/dist/multicast-input.js +126 -49
- package/dist/multicast-input.js.map +3 -3
- package/dist/multicast-manager.d.ts +11 -4
- package/dist/multicast-manager.d.ts.map +1 -1
- package/dist/multicast-manager.js +7922 -7700
- package/dist/multicast-manager.js.map +4 -4
- package/dist/multicast-transport.d.ts +8 -0
- package/dist/multicast-transport.d.ts.map +1 -1
- package/dist/multicast-transport.js +27 -3
- package/dist/multicast-transport.js.map +3 -3
- package/dist/multicast-unwrap.js +2 -2
- package/dist/multicast-unwrap.js.map +2 -2
- package/dist/no-modules.js +81 -278
- package/dist/no-modules.js.map +4 -4
- package/dist/repair-fetch.js.map +1 -1
- package/dist/signaling.js.map +1 -1
- package/dist/timestamp-codec.js.map +1 -1
- package/dist/wasm-mfu-reassembler.js.map +1 -1
- package/dist/wasm-mmtp-container-parser.js.map +1 -1
- package/package.json +5 -5
- package/schemas/catalog.schema.json +15 -7
- package/src/carp-catalog.ts +14 -13
- package/src/fec-manager.ts +89 -14
- package/src/held-layer-admission.ts +38 -1
- package/src/mmtp-fec-session.ts +20 -12
- package/src/mmtp-router.ts +65 -16
- package/src/multicast-auth-catalog.ts +43 -36
- package/src/multicast-input.ts +28 -8
- package/src/multicast-manager.ts +38 -34
- package/src/multicast-transport.ts +31 -3
- package/test-vectors/bc-provenance-v2.json +614 -175
- package/test-vectors/catalog/README.md +24 -8
- package/test-vectors/catalog/negative/multicast-auth-asm-missing-sender-address.json +0 -2
- package/test-vectors/catalog/negative/{multicast-auth-manifest-port-equals-port.json → multicast-auth-draft01-manifest-port.json} +1 -1
- package/test-vectors/catalog/negative/multicast-auth-duplicate-first-epoch.json +0 -1
- package/test-vectors/catalog/negative/multicast-auth-duplicate-kid.json +0 -1
- package/test-vectors/catalog/negative/multicast-auth-endpoint-auth-without-object-auth.json +0 -1
- package/test-vectors/catalog/negative/multicast-auth-endpoint-missing-bandwidth.json +0 -1
- package/test-vectors/catalog/negative/multicast-auth-manifest-timer-out-of-range.json +0 -1
- package/test-vectors/catalog/negative/multicast-auth-sender-address-on-ssm.json +0 -1
- package/test-vectors/catalog/negative/multicast-auth-shared-sg-port.json +73 -0
- package/test-vectors/catalog/negative/multicast-auth-version-1.json +0 -1
- package/test-vectors/catalog/negative/multicast-auth-version-3.json +0 -1
- package/test-vectors/catalog/negative/multicast-auth-version-missing.json +0 -1
- package/test-vectors/catalog/negative/multicast-endpoint-address-family-mismatch.json +55 -0
- package/test-vectors/catalog/negative/multicast-endpoint-address-host-name.json +55 -0
- package/test-vectors/catalog/negative/multicast-endpoint-address-leading-zero.json +55 -0
- package/test-vectors/catalog/negative/multicast-endpoint-address-ls32-leading-zero.json +55 -0
- package/test-vectors/catalog/negative/multicast-endpoint-address-mapped-family-mismatch.json +55 -0
- package/test-vectors/catalog/negative/multicast-endpoint-address-zone-id.json +55 -0
- package/test-vectors/catalog/positive/multicast-auth-asm-sender.json +0 -2
- package/test-vectors/catalog/{negative/multicast-auth-max-auth-delay-above-max.json → positive/multicast-auth-max-auth-delay-above-max-clamped.json} +0 -1
- package/test-vectors/catalog/{negative/multicast-auth-max-auth-delay-below-min.json → positive/multicast-auth-max-auth-delay-below-min-clamped.json} +0 -1
- package/test-vectors/catalog/positive/multicast-auth-max-auth-delay-string.json +55 -0
- package/test-vectors/catalog/positive/multicast-auth-rotation.json +0 -1
- package/test-vectors/catalog/positive/multicast-draft01-7-2-6-example.json +0 -1
- package/test-vectors/catalog/section-codes.json +8 -3
- package/test-vectors/catalog-multicast-auth-v1.json +0 -1
- package/test-vectors/catalog-validation.json +3 -3
- package/test-vectors/generate-bc-provenance-v2.mjs +94 -14
|
@@ -382,14 +382,11 @@ var BlockcastMmt = (() => {
|
|
|
382
382
|
dataOffset: off + REPAIR_FEC_PID_SIZE
|
|
383
383
|
};
|
|
384
384
|
}
|
|
385
|
-
function
|
|
386
|
-
return moqRepairFrameFrom(frame, readRepairFecPayloadIdFields(frame, header));
|
|
387
|
-
}
|
|
388
|
-
function moqRepairFrameFrom(frame, repairId) {
|
|
385
|
+
function moqRepairFrameFrom(frame, repairId, checkRsId = true) {
|
|
389
386
|
if (!repairId)
|
|
390
387
|
return null;
|
|
391
388
|
const symbolData = frame.subarray(repairId.dataOffset);
|
|
392
|
-
if (repairId.
|
|
389
|
+
if (repairId.ssbLength === 0 || checkRsId && repairId.rsId >= repairId.rsbLength || symbolData.length === 0)
|
|
393
390
|
return null;
|
|
394
391
|
return {
|
|
395
392
|
packetId: repairId.packetId,
|
|
@@ -400,6 +397,23 @@ var BlockcastMmt = (() => {
|
|
|
400
397
|
symbolData
|
|
401
398
|
};
|
|
402
399
|
}
|
|
400
|
+
function admitMmtpRepairObject(packet, header, symbolSize, algorithm) {
|
|
401
|
+
if (header.packetType !== PACKET_TYPE_REPAIR || header.fecType !== 2)
|
|
402
|
+
return null;
|
|
403
|
+
if (header.headerLength !== MMTP_HEADER_SIZE || packet.length !== MMTP_HEADER_SIZE + REPAIR_FEC_PID_SIZE + symbolSize) {
|
|
404
|
+
return { reject: "repairLength" };
|
|
405
|
+
}
|
|
406
|
+
const frame = moqRepairFrameFrom(packet, readRepairFecPayloadIdFields(packet, header), false);
|
|
407
|
+
if (!frame)
|
|
408
|
+
return null;
|
|
409
|
+
if (frame.rsId >= frame.rsbLength)
|
|
410
|
+
return { frame, reject: "repairRsId" };
|
|
411
|
+
const blockSymbols = frame.ssbLength + frame.rsbLength;
|
|
412
|
+
if (algorithm === "reed-solomon" ? blockSymbols > 255 : algorithm === "raptorq" && blockSymbols - 1 > 16777215) {
|
|
413
|
+
return { frame, reject: "repairSchemeBound" };
|
|
414
|
+
}
|
|
415
|
+
return { frame };
|
|
416
|
+
}
|
|
403
417
|
|
|
404
418
|
// ../mmt-base/dist/net-constants.js
|
|
405
419
|
var DEFAULT_AMT_RELAY_PORT = 2268;
|
|
@@ -477,254 +491,6 @@ var BlockcastMmt = (() => {
|
|
|
477
491
|
return serial32Precedes(a, b) ? Math.floor(serial32Distance(a, b) / kMax) : 0;
|
|
478
492
|
}
|
|
479
493
|
|
|
480
|
-
// ../mmt-base/dist/multicast-catalog.js
|
|
481
|
-
var ED25519_PUBLIC_KEY_BASE64URL = /^[A-Za-z0-9_-]{42}[AEIMQUYcgkosw048]$/;
|
|
482
|
-
var UINT32_MAX = 4294967295;
|
|
483
|
-
var AUTH_MEMBERS = /* @__PURE__ */ new Set(["scheme", "version", "sig", "keys", "minEpoch"]);
|
|
484
|
-
var KEY_MEMBERS = /* @__PURE__ */ new Set(["kid", "publicKey", "firstEpoch"]);
|
|
485
|
-
var ENDPOINT_AUTH_MEMBERS = /* @__PURE__ */ new Set([
|
|
486
|
-
"manifestPort",
|
|
487
|
-
"streamId",
|
|
488
|
-
"senderAddress",
|
|
489
|
-
"maxAuthDelayMs",
|
|
490
|
-
"manifestTimerMs",
|
|
491
|
-
"manifestTrack"
|
|
492
|
-
]);
|
|
493
|
-
function isObject(value) {
|
|
494
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
495
|
-
}
|
|
496
|
-
function isNonEmptyString(value) {
|
|
497
|
-
return typeof value === "string" && value.trim().length > 0;
|
|
498
|
-
}
|
|
499
|
-
function isIntegerIn(value, min, max) {
|
|
500
|
-
return Number.isSafeInteger(value) && value >= min && value <= max;
|
|
501
|
-
}
|
|
502
|
-
function multicastEndpointSource(endpoint) {
|
|
503
|
-
if (!isObject(endpoint))
|
|
504
|
-
return void 0;
|
|
505
|
-
const sourceAddress = endpoint["sourceAddress"];
|
|
506
|
-
if (sourceAddress !== void 0 && !isNonEmptyString(sourceAddress))
|
|
507
|
-
return void 0;
|
|
508
|
-
const mode = sourceAddress === void 0 ? "asm" : "ssm";
|
|
509
|
-
const protocol = endpoint["protocol"];
|
|
510
|
-
if (protocol !== void 0 && protocol !== mode)
|
|
511
|
-
return void 0;
|
|
512
|
-
const auth = endpoint["auth"];
|
|
513
|
-
const senderAddress = isObject(auth) ? auth["senderAddress"] : void 0;
|
|
514
|
-
if (mode === "ssm")
|
|
515
|
-
return senderAddress === void 0 ? { mode, source: sourceAddress } : void 0;
|
|
516
|
-
if (auth === void 0)
|
|
517
|
-
return { mode };
|
|
518
|
-
return isNonEmptyString(senderAddress) ? { mode, source: senderAddress } : void 0;
|
|
519
|
-
}
|
|
520
|
-
function authIssues(auth) {
|
|
521
|
-
const base = "$.multicast.auth";
|
|
522
|
-
const issue = (path, message) => [
|
|
523
|
-
{ code: "multicast_auth", path: `${base}${path}`, message }
|
|
524
|
-
];
|
|
525
|
-
if (!isObject(auth))
|
|
526
|
-
return issue("", "auth must be an object");
|
|
527
|
-
const unknown = Object.keys(auth).filter((key) => !AUTH_MEMBERS.has(key));
|
|
528
|
-
if (unknown.length > 0) {
|
|
529
|
-
return issue("", `auth has unknown member(s) ${unknown.join(", ")}; draft -01 7.2.6 defines scheme, version, sig, keys and minEpoch`);
|
|
530
|
-
}
|
|
531
|
-
if (auth["scheme"] !== "bc-provenance")
|
|
532
|
-
return issue(".scheme", 'scheme must be "bc-provenance"');
|
|
533
|
-
if (auth["version"] !== 2)
|
|
534
|
-
return issue(".version", "version must be 2 (bc-provenance version 2, draft -01 7.2)");
|
|
535
|
-
if (auth["sig"] !== "ed25519")
|
|
536
|
-
return issue(".sig", 'sig must be "ed25519"');
|
|
537
|
-
const keys = auth["keys"];
|
|
538
|
-
if (!Array.isArray(keys) || keys.length < 1 || keys.length > 4) {
|
|
539
|
-
return issue(".keys", "keys must be an array of one to four {kid, publicKey, firstEpoch} objects");
|
|
540
|
-
}
|
|
541
|
-
const kids = /* @__PURE__ */ new Set();
|
|
542
|
-
const firstEpochs = /* @__PURE__ */ new Set();
|
|
543
|
-
for (let index = 0; index < keys.length; index++) {
|
|
544
|
-
const key = keys[index];
|
|
545
|
-
const at = `.keys[${index}]`;
|
|
546
|
-
if (!isObject(key) || Object.keys(key).some((member) => !KEY_MEMBERS.has(member))) {
|
|
547
|
-
return issue(at, "a key is exactly {kid, publicKey, firstEpoch}");
|
|
548
|
-
}
|
|
549
|
-
if (!isIntegerIn(key["kid"], 0, UINT32_MAX))
|
|
550
|
-
return issue(`${at}.kid`, "kid must be an unsigned 32-bit integer");
|
|
551
|
-
if (typeof key["publicKey"] !== "string" || !ED25519_PUBLIC_KEY_BASE64URL.test(key["publicKey"])) {
|
|
552
|
-
return issue(`${at}.publicKey`, "publicKey must be a 32-octet Ed25519 key in canonical unpadded base64url");
|
|
553
|
-
}
|
|
554
|
-
if (!isIntegerIn(key["firstEpoch"], 0, UINT32_MAX)) {
|
|
555
|
-
return issue(`${at}.firstEpoch`, "firstEpoch must be an unsigned 32-bit integer");
|
|
556
|
-
}
|
|
557
|
-
if (kids.has(key["kid"]))
|
|
558
|
-
return issue(`${at}.kid`, `kid ${key["kid"]} is repeated; the catalog holds no key set`);
|
|
559
|
-
if (firstEpochs.has(key["firstEpoch"])) {
|
|
560
|
-
return issue(`${at}.firstEpoch`, `firstEpoch ${key["firstEpoch"]} is repeated; the catalog holds no key set`);
|
|
561
|
-
}
|
|
562
|
-
kids.add(key["kid"]);
|
|
563
|
-
firstEpochs.add(key["firstEpoch"]);
|
|
564
|
-
}
|
|
565
|
-
if (!isIntegerIn(auth["minEpoch"], 0, UINT32_MAX)) {
|
|
566
|
-
return issue(".minEpoch", "minEpoch must be an unsigned 32-bit integer");
|
|
567
|
-
}
|
|
568
|
-
return [];
|
|
569
|
-
}
|
|
570
|
-
function endpointAuthIssue(endpoint, mode, at) {
|
|
571
|
-
const auth = endpoint["auth"];
|
|
572
|
-
if (!isObject(auth))
|
|
573
|
-
return { path: `${at}.auth`, message: "auth must be an object" };
|
|
574
|
-
const unknown = Object.keys(auth).filter((key) => !ENDPOINT_AUTH_MEMBERS.has(key));
|
|
575
|
-
if (unknown.length > 0)
|
|
576
|
-
return { path: `${at}.auth`, message: `auth has unknown member(s) ${unknown.join(", ")}` };
|
|
577
|
-
if (!isIntegerIn(auth["manifestPort"], 1, 65535)) {
|
|
578
|
-
return { path: `${at}.auth.manifestPort`, message: "manifestPort must be an integer from 1 to 65535" };
|
|
579
|
-
}
|
|
580
|
-
if (!isIntegerIn(auth["streamId"], 0, UINT32_MAX)) {
|
|
581
|
-
return { path: `${at}.auth.streamId`, message: "streamId must be an unsigned 32-bit integer" };
|
|
582
|
-
}
|
|
583
|
-
if (mode === "ssm" && auth["senderAddress"] !== void 0) {
|
|
584
|
-
return { path: `${at}.auth.senderAddress`, message: "senderAddress must not appear on an SSM endpoint; S is sourceAddress" };
|
|
585
|
-
}
|
|
586
|
-
if (mode === "asm" && !isNonEmptyString(auth["senderAddress"])) {
|
|
587
|
-
return { path: `${at}.auth.senderAddress`, message: "a protected ASM endpoint requires senderAddress" };
|
|
588
|
-
}
|
|
589
|
-
if (auth["maxAuthDelayMs"] !== void 0 && !isIntegerIn(auth["maxAuthDelayMs"], 50, 1e3)) {
|
|
590
|
-
return { path: `${at}.auth.maxAuthDelayMs`, message: "maxAuthDelayMs must be an integer from 50 to 1000" };
|
|
591
|
-
}
|
|
592
|
-
if (auth["manifestTimerMs"] !== void 0 && !isIntegerIn(auth["manifestTimerMs"], 1, Number.MAX_SAFE_INTEGER)) {
|
|
593
|
-
return { path: `${at}.auth.manifestTimerMs`, message: "manifestTimerMs must be an integer of at least 1" };
|
|
594
|
-
}
|
|
595
|
-
if (auth["manifestTrack"] !== void 0 && !isNonEmptyString(auth["manifestTrack"])) {
|
|
596
|
-
return { path: `${at}.auth.manifestTrack`, message: "manifestTrack must be a non-empty string" };
|
|
597
|
-
}
|
|
598
|
-
if (!isIntegerIn(endpoint["bandwidth"], 1, Number.MAX_SAFE_INTEGER)) {
|
|
599
|
-
return { path: `${at}.bandwidth`, message: "a protected endpoint requires bandwidth, which bounds the receiver's hold queues" };
|
|
600
|
-
}
|
|
601
|
-
return void 0;
|
|
602
|
-
}
|
|
603
|
-
var CATALOG_NAMESPACE = /* @__PURE__ */ Symbol.for("@blockcast/mmt-base/catalogNamespace");
|
|
604
|
-
function multicastCatalogIssues(catalog, options = {}) {
|
|
605
|
-
if (!isObject(catalog))
|
|
606
|
-
return [];
|
|
607
|
-
const issues = [];
|
|
608
|
-
if ("multicast.auth" in catalog) {
|
|
609
|
-
issues.push({
|
|
610
|
-
code: "multicast_auth",
|
|
611
|
-
path: '$["multicast.auth"]',
|
|
612
|
-
message: 'the root "multicast.auth" key is retired; declare auth as multicast.auth (draft -01 7.2.6)'
|
|
613
|
-
});
|
|
614
|
-
}
|
|
615
|
-
const multicast = catalog["multicast"];
|
|
616
|
-
if (!isObject(multicast))
|
|
617
|
-
return issues;
|
|
618
|
-
const protectedCatalog = multicast["auth"] !== void 0;
|
|
619
|
-
if (protectedCatalog)
|
|
620
|
-
issues.push(...authIssues(multicast["auth"]));
|
|
621
|
-
const endpoints = multicast["endpoints"];
|
|
622
|
-
if (!Array.isArray(endpoints))
|
|
623
|
-
return issues;
|
|
624
|
-
const tracks = Array.isArray(catalog["tracks"]) ? catalog["tracks"] : [];
|
|
625
|
-
const namespaceOf = /* @__PURE__ */ new Map();
|
|
626
|
-
for (const track of tracks) {
|
|
627
|
-
if (isObject(track) && typeof track["name"] === "string")
|
|
628
|
-
namespaceOf.set(track["name"], track["namespace"]);
|
|
629
|
-
}
|
|
630
|
-
const tagged = catalog[CATALOG_NAMESPACE];
|
|
631
|
-
let referenceNamespace = options.catalogNamespace ?? (typeof tagged === "string" ? tagged : void 0);
|
|
632
|
-
const sources = [];
|
|
633
|
-
for (let index = 0; index < endpoints.length; index++) {
|
|
634
|
-
const endpoint = endpoints[index];
|
|
635
|
-
const at = `$.multicast.endpoints[${index}]`;
|
|
636
|
-
const fail = (code, path, message) => {
|
|
637
|
-
issues.push({ code, path, message, endpointIndex: index });
|
|
638
|
-
};
|
|
639
|
-
sources.push(void 0);
|
|
640
|
-
if (!isObject(endpoint)) {
|
|
641
|
-
fail("multicast_endpoint", at, "endpoint must be an object");
|
|
642
|
-
continue;
|
|
643
|
-
}
|
|
644
|
-
const protocol = endpoint["protocol"];
|
|
645
|
-
if (protocol !== void 0 && protocol !== "ssm" && protocol !== "asm") {
|
|
646
|
-
fail("multicast_endpoint", `${at}.protocol`, `protocol must be "ssm" or "asm"; a bearer such as ${JSON.stringify(protocol)} is a networkSource entry (draft -01 4.2)`);
|
|
647
|
-
continue;
|
|
648
|
-
}
|
|
649
|
-
const hasAuth = endpoint["auth"] !== void 0;
|
|
650
|
-
if (protectedCatalog !== hasAuth) {
|
|
651
|
-
fail("multicast_endpoint_auth", `${at}.auth`, protectedCatalog ? "every endpoint requires auth when multicast carries auth; a receiver ignores this endpoint" : "auth names no key without multicast.auth; a receiver ignores this endpoint");
|
|
652
|
-
continue;
|
|
653
|
-
}
|
|
654
|
-
const sourceAddress = endpoint["sourceAddress"];
|
|
655
|
-
if (sourceAddress !== void 0 && !isNonEmptyString(sourceAddress)) {
|
|
656
|
-
fail("multicast_endpoint", `${at}.sourceAddress`, "sourceAddress must be a non-empty string");
|
|
657
|
-
continue;
|
|
658
|
-
}
|
|
659
|
-
const mode = sourceAddress === void 0 ? "asm" : "ssm";
|
|
660
|
-
if (protocol !== void 0 && protocol !== mode) {
|
|
661
|
-
fail("multicast_endpoint", `${at}.protocol`, `protocol ${JSON.stringify(protocol)} disagrees with sourceAddress, which selects ${mode}`);
|
|
662
|
-
continue;
|
|
663
|
-
}
|
|
664
|
-
if (!isIntegerIn(endpoint["port"], 1, 65535)) {
|
|
665
|
-
fail("multicast_endpoint", `${at}.port`, "port must be an integer from 1 to 65535");
|
|
666
|
-
continue;
|
|
667
|
-
}
|
|
668
|
-
if (hasAuth) {
|
|
669
|
-
const authIssue = endpointAuthIssue(endpoint, mode, at);
|
|
670
|
-
if (authIssue) {
|
|
671
|
-
fail("multicast_endpoint_auth", authIssue.path, authIssue.message);
|
|
672
|
-
continue;
|
|
673
|
-
}
|
|
674
|
-
}
|
|
675
|
-
sources[index] = multicastEndpointSource(endpoint);
|
|
676
|
-
const refs = Array.isArray(endpoint["tracks"]) ? endpoint["tracks"] : [];
|
|
677
|
-
for (let refIndex = 0; refIndex < refs.length; refIndex++) {
|
|
678
|
-
const ref = refs[refIndex];
|
|
679
|
-
const name = isObject(ref) ? ref["name"] : ref;
|
|
680
|
-
if (typeof name !== "string")
|
|
681
|
-
continue;
|
|
682
|
-
const namespace = namespaceOf.get(name);
|
|
683
|
-
if (typeof namespace !== "string")
|
|
684
|
-
continue;
|
|
685
|
-
referenceNamespace ?? (referenceNamespace = namespace);
|
|
686
|
-
if (namespace !== referenceNamespace) {
|
|
687
|
-
fail("multicast_track", `${at}.tracks[${refIndex}].name`, `track ${JSON.stringify(name)} declares namespace ${JSON.stringify(namespace)}, not the catalog track's ${JSON.stringify(referenceNamespace)}`);
|
|
688
|
-
}
|
|
689
|
-
}
|
|
690
|
-
}
|
|
691
|
-
const tupleOf = (index) => {
|
|
692
|
-
const endpoint = endpoints[index];
|
|
693
|
-
const auth = endpoint["auth"];
|
|
694
|
-
return {
|
|
695
|
-
sg: JSON.stringify([sources[index].source ?? null, endpoint["groupAddress"]]),
|
|
696
|
-
port: endpoint["port"],
|
|
697
|
-
manifestPort: isObject(auth) ? auth["manifestPort"] : void 0
|
|
698
|
-
};
|
|
699
|
-
};
|
|
700
|
-
for (let index = 0; index < endpoints.length; index++) {
|
|
701
|
-
if (!sources[index])
|
|
702
|
-
continue;
|
|
703
|
-
const own = tupleOf(index);
|
|
704
|
-
const at = `$.multicast.endpoints[${index}]`;
|
|
705
|
-
if (own.manifestPort !== void 0 && own.manifestPort === own.port) {
|
|
706
|
-
issues.push({ code: "multicast_endpoint_auth", path: `${at}.auth.manifestPort`, message: "manifestPort must differ from the endpoint's own port", endpointIndex: index });
|
|
707
|
-
continue;
|
|
708
|
-
}
|
|
709
|
-
for (let other = 0; other < endpoints.length; other++) {
|
|
710
|
-
if (other === index || !sources[other])
|
|
711
|
-
continue;
|
|
712
|
-
const peer = tupleOf(other);
|
|
713
|
-
if (peer.sg !== own.sg)
|
|
714
|
-
continue;
|
|
715
|
-
if (peer.port === own.port) {
|
|
716
|
-
issues.push({ code: "multicast_endpoint", path: `${at}.port`, message: `(S, G, port) is shared with endpoint ${other}; one endpoint object per multicast tuple`, endpointIndex: index });
|
|
717
|
-
break;
|
|
718
|
-
}
|
|
719
|
-
if (own.manifestPort !== void 0 && (own.manifestPort === peer.port || own.manifestPort === peer.manifestPort)) {
|
|
720
|
-
issues.push({ code: "multicast_endpoint_auth", path: `${at}.auth.manifestPort`, message: `manifestPort is a port or manifestPort of endpoint ${other} on the same (S, G)`, endpointIndex: index });
|
|
721
|
-
break;
|
|
722
|
-
}
|
|
723
|
-
}
|
|
724
|
-
}
|
|
725
|
-
return issues;
|
|
726
|
-
}
|
|
727
|
-
|
|
728
494
|
// ../mmt-base/dist/fec-catalog.js
|
|
729
495
|
var DEFAULT_SCHEMES = ["reed-solomon", "raptorq"];
|
|
730
496
|
var DEFAULT_PROFILES = ["moq-object-v1", "iso-ssbg1-v1"];
|
|
@@ -8061,7 +7827,7 @@ var BlockcastMmt = (() => {
|
|
|
8061
7827
|
}
|
|
8062
7828
|
});
|
|
8063
7829
|
}
|
|
8064
|
-
function validateCmsfMmtpCatalog(value,
|
|
7830
|
+
function validateCmsfMmtpCatalog(value, _options = {}) {
|
|
8065
7831
|
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
8066
7832
|
catalogError("catalog_object", "$", "catalog must be an object");
|
|
8067
7833
|
}
|
|
@@ -8252,8 +8018,6 @@ var BlockcastMmt = (() => {
|
|
|
8252
8018
|
}
|
|
8253
8019
|
}
|
|
8254
8020
|
}
|
|
8255
|
-
const [multicastIssue] = multicastCatalogIssues(catalog, options);
|
|
8256
|
-
if (multicastIssue) catalogError(multicastIssue.code, multicastIssue.path, multicastIssue.message);
|
|
8257
8021
|
validateArchive(catalog);
|
|
8258
8022
|
return catalog;
|
|
8259
8023
|
}
|
|
@@ -8356,9 +8120,10 @@ var BlockcastMmt = (() => {
|
|
|
8356
8120
|
function heldLayerAnchorHorizon(retentionBlocks, k) {
|
|
8357
8121
|
return retentionBlocks === void 0 || k <= 0 ? HORIZON_CAP : Math.min(HORIZON_CAP, retentionBlocks * k);
|
|
8358
8122
|
}
|
|
8359
|
-
var _layerNames, _states;
|
|
8123
|
+
var _layerNames, _states, _HeldLayerAdmission_instances, admit_fn;
|
|
8360
8124
|
var HeldLayerAdmission = class {
|
|
8361
8125
|
constructor(layers, heldLayers = [0]) {
|
|
8126
|
+
__privateAdd(this, _HeldLayerAdmission_instances);
|
|
8362
8127
|
__privateAdd(this, _layerNames);
|
|
8363
8128
|
__privateAdd(this, _states);
|
|
8364
8129
|
const held = new Set(heldLayers);
|
|
@@ -8376,56 +8141,78 @@ var BlockcastMmt = (() => {
|
|
|
8376
8141
|
* @returns undefined to admit, `'discard'`, or the fail-closed reason.
|
|
8377
8142
|
*/
|
|
8378
8143
|
admit(repairTrackName, ssStart, rsbLength, horizon, sourceFrontier) {
|
|
8379
|
-
|
|
8380
|
-
|
|
8381
|
-
|
|
8382
|
-
|
|
8383
|
-
|
|
8384
|
-
|
|
8385
|
-
|
|
8386
|
-
|
|
8387
|
-
|
|
8388
|
-
|
|
8389
|
-
|
|
8390
|
-
|
|
8391
|
-
|
|
8392
|
-
|
|
8393
|
-
|
|
8144
|
+
return __privateMethod(this, _HeldLayerAdmission_instances, admit_fn).call(this, repairTrackName, ssStart, rsbLength, horizon, sourceFrontier, true);
|
|
8145
|
+
}
|
|
8146
|
+
/**
|
|
8147
|
+
* The core-00 7.5 carve-out for a repair object about to be discarded
|
|
8148
|
+
* (RS_ID or scheme bound): whether its own RSB_length, below a held layer's
|
|
8149
|
+
* total on that layer's track or on an anchored block, fails the instance
|
|
8150
|
+
* closed under 7.3.2. Leaves the anchored state as it was: a discarded
|
|
8151
|
+
* object anchors nothing.
|
|
8152
|
+
*/
|
|
8153
|
+
failsClosed(repairTrackName, ssStart, rsbLength, horizon, sourceFrontier) {
|
|
8154
|
+
const saved = __privateGet(this, _states).map((state) => ({ ...state }));
|
|
8155
|
+
try {
|
|
8156
|
+
const verdict = __privateMethod(this, _HeldLayerAdmission_instances, admit_fn).call(this, repairTrackName, ssStart, rsbLength, horizon, sourceFrontier, false);
|
|
8157
|
+
return typeof verdict === "object" ? verdict : void 0;
|
|
8158
|
+
} finally {
|
|
8159
|
+
saved.forEach((state, i) => {
|
|
8160
|
+
__privateGet(this, _states)[i] = state;
|
|
8161
|
+
});
|
|
8162
|
+
}
|
|
8163
|
+
}
|
|
8164
|
+
};
|
|
8165
|
+
_layerNames = new WeakMap();
|
|
8166
|
+
_states = new WeakMap();
|
|
8167
|
+
_HeldLayerAdmission_instances = new WeakSet();
|
|
8168
|
+
admit_fn = function(repairTrackName, ssStart, rsbLength, horizon, sourceFrontier, retroactive) {
|
|
8169
|
+
if (repairTrackName !== void 0 && !__privateGet(this, _layerNames).has(repairTrackName)) return "discard";
|
|
8170
|
+
const farAhead = sourceFrontier !== void 0 && serial32Precedes(sourceFrontier, ssStart) && serial32Distance(sourceFrontier, ssStart) > horizon;
|
|
8171
|
+
for (const state of __privateGet(this, _states)) {
|
|
8172
|
+
let code;
|
|
8173
|
+
if (farAhead) {
|
|
8174
|
+
if (rsbLength < state.total) {
|
|
8175
|
+
if (state.layer > 0 && repairTrackName === state.repairTrackName) code = "below_own_track_total";
|
|
8176
|
+
else if (state.anchor !== void 0 || state.settled) code = "below_anchored_total";
|
|
8177
|
+
}
|
|
8178
|
+
} else {
|
|
8179
|
+
if (state.newest === void 0 || serial32Precedes(state.newest, ssStart)) state.newest = ssStart;
|
|
8180
|
+
const stale = (s) => serial32Distance(s, state.newest) > horizon;
|
|
8181
|
+
if (state.latestBelow !== void 0 && stale(state.latestBelow)) {
|
|
8182
|
+
delete state.latestBelow;
|
|
8183
|
+
delete state.latestBelowRsb;
|
|
8184
|
+
}
|
|
8185
|
+
if (state.anchor !== void 0 && stale(state.anchor)) state.settled = true;
|
|
8186
|
+
if (rsbLength < state.total) {
|
|
8187
|
+
if (state.layer > 0 && repairTrackName === state.repairTrackName) code = "below_own_track_total";
|
|
8188
|
+
else if (stale(ssStart)) continue;
|
|
8189
|
+
else if (state.settled || state.anchor !== void 0 && !serial32Precedes(ssStart, state.anchor)) code = "below_anchored_total";
|
|
8190
|
+
else if (state.latestBelow === void 0 || serial32Precedes(state.latestBelow, ssStart)) {
|
|
8191
|
+
state.latestBelow = ssStart;
|
|
8192
|
+
state.latestBelowRsb = rsbLength;
|
|
8394
8193
|
}
|
|
8395
|
-
|
|
8396
|
-
if (
|
|
8397
|
-
|
|
8398
|
-
|
|
8399
|
-
|
|
8400
|
-
|
|
8401
|
-
state.latestBelow = ssStart;
|
|
8402
|
-
state.latestBelowRsb = rsbLength;
|
|
8403
|
-
}
|
|
8404
|
-
} else if (!state.settled && !stale(ssStart)) {
|
|
8405
|
-
if (state.anchor === void 0 || serial32Precedes(ssStart, state.anchor)) state.anchor = ssStart;
|
|
8406
|
-
if (state.latestBelow !== void 0 && !serial32Precedes(state.latestBelow, state.anchor)) {
|
|
8407
|
-
code = "below_anchored_total";
|
|
8408
|
-
ssStart = state.latestBelow;
|
|
8409
|
-
rsbLength = state.latestBelowRsb;
|
|
8410
|
-
}
|
|
8194
|
+
} else if (!state.settled && !stale(ssStart)) {
|
|
8195
|
+
if (state.anchor === void 0 || serial32Precedes(ssStart, state.anchor)) state.anchor = ssStart;
|
|
8196
|
+
if (retroactive && state.latestBelow !== void 0 && !serial32Precedes(state.latestBelow, state.anchor)) {
|
|
8197
|
+
code = "below_anchored_total";
|
|
8198
|
+
ssStart = state.latestBelow;
|
|
8199
|
+
rsbLength = state.latestBelowRsb;
|
|
8411
8200
|
}
|
|
8412
8201
|
}
|
|
8413
|
-
if (code !== void 0) {
|
|
8414
|
-
return {
|
|
8415
|
-
code,
|
|
8416
|
-
layer: state.layer,
|
|
8417
|
-
total: state.total,
|
|
8418
|
-
rsbLength,
|
|
8419
|
-
ssStart,
|
|
8420
|
-
...state.anchor === void 0 ? {} : { anchor: state.anchor }
|
|
8421
|
-
};
|
|
8422
|
-
}
|
|
8423
8202
|
}
|
|
8424
|
-
|
|
8203
|
+
if (code !== void 0) {
|
|
8204
|
+
return {
|
|
8205
|
+
code,
|
|
8206
|
+
layer: state.layer,
|
|
8207
|
+
total: state.total,
|
|
8208
|
+
rsbLength,
|
|
8209
|
+
ssStart,
|
|
8210
|
+
...state.anchor === void 0 ? {} : { anchor: state.anchor }
|
|
8211
|
+
};
|
|
8212
|
+
}
|
|
8425
8213
|
}
|
|
8214
|
+
return void 0;
|
|
8426
8215
|
};
|
|
8427
|
-
_layerNames = new WeakMap();
|
|
8428
|
-
_states = new WeakMap();
|
|
8429
8216
|
|
|
8430
8217
|
// src/mmtp-fec-session.ts
|
|
8431
8218
|
var PATH_BITS = { moq: 1, multicast: 2 };
|
|
@@ -8597,18 +8384,29 @@ var BlockcastMmt = (() => {
|
|
|
8597
8384
|
} catch {
|
|
8598
8385
|
return __privateMethod(this, _MmtpFecSession_instances, drop_fn2).call(this, "repair:malformed");
|
|
8599
8386
|
}
|
|
8600
|
-
const
|
|
8601
|
-
if (!
|
|
8602
|
-
if (
|
|
8603
|
-
|
|
8604
|
-
|
|
8387
|
+
const admission = admitMmtpRepairObject(packet, header, this.symbolSize, this.algorithm);
|
|
8388
|
+
if (!admission) return __privateMethod(this, _MmtpFecSession_instances, drop_fn2).call(this, "repair:malformed");
|
|
8389
|
+
if (!("frame" in admission)) return __privateMethod(this, _MmtpFecSession_instances, drop_fn2).call(this, "repair:length");
|
|
8390
|
+
const frame = admission.frame;
|
|
8391
|
+
if (__privateGet(this, _repairPacketId) !== void 0 && frame.packetId !== __privateGet(this, _repairPacketId)) {
|
|
8605
8392
|
return __privateMethod(this, _MmtpFecSession_instances, drop_fn2).call(this, "repair:foreign-packet-id");
|
|
8606
8393
|
}
|
|
8607
|
-
if (
|
|
8608
|
-
|
|
8609
|
-
|
|
8610
|
-
|
|
8394
|
+
if (admission.reject) {
|
|
8395
|
+
const verdict2 = __privateGet(this, _heldLayers).failsClosed(
|
|
8396
|
+
repairTrackName,
|
|
8397
|
+
frame.ssStart,
|
|
8398
|
+
frame.rsbLength,
|
|
8399
|
+
__privateGet(this, _anchorHorizon),
|
|
8400
|
+
__privateGet(this, _newestSource)
|
|
8401
|
+
);
|
|
8402
|
+
if (verdict2) {
|
|
8403
|
+
__privateGet(this, _stats3).failClosed = verdict2;
|
|
8404
|
+
return __privateMethod(this, _MmtpFecSession_instances, drop_fn2).call(this, "repair:fail-closed");
|
|
8405
|
+
}
|
|
8406
|
+
return __privateMethod(this, _MmtpFecSession_instances, drop_fn2).call(this, admission.reject === "repairRsId" ? "repair:rs-id" : "repair:scheme-bound");
|
|
8611
8407
|
}
|
|
8408
|
+
__privateGet(this, _repairPacketId) ?? __privateSet(this, _repairPacketId, frame.packetId);
|
|
8409
|
+
if (frame.ssbLength > this.sourceSymbols) return __privateMethod(this, _MmtpFecSession_instances, drop_fn2).call(this, "repair:ssb-length");
|
|
8612
8410
|
const verdict = __privateGet(this, _heldLayers).admit(
|
|
8613
8411
|
repairTrackName,
|
|
8614
8412
|
frame.ssStart,
|
|
@@ -8895,8 +8693,8 @@ var BlockcastMmt = (() => {
|
|
|
8895
8693
|
}
|
|
8896
8694
|
var KEYFRAME_FLAGS = 33554432;
|
|
8897
8695
|
var DELTA_FLAGS = 16842752;
|
|
8898
|
-
var
|
|
8899
|
-
var MAX_TRACK_ID =
|
|
8696
|
+
var UINT32_MAX = 4294967295;
|
|
8697
|
+
var MAX_TRACK_ID = UINT32_MAX - 1;
|
|
8900
8698
|
var INT32_MIN = -2147483648;
|
|
8901
8699
|
var INT32_MAX = 2147483647;
|
|
8902
8700
|
function assertTrackId(trackId, label) {
|
|
@@ -8905,8 +8703,8 @@ var BlockcastMmt = (() => {
|
|
|
8905
8703
|
}
|
|
8906
8704
|
}
|
|
8907
8705
|
function assertTimescale(timescale, label) {
|
|
8908
|
-
if (!Number.isInteger(timescale) || timescale <= 0 || timescale >
|
|
8909
|
-
throw new RangeError(`${label} must be an integer in 1..${
|
|
8706
|
+
if (!Number.isInteger(timescale) || timescale <= 0 || timescale > UINT32_MAX) {
|
|
8707
|
+
throw new RangeError(`${label} must be an integer in 1..${UINT32_MAX}, got ${timescale}`);
|
|
8910
8708
|
}
|
|
8911
8709
|
}
|
|
8912
8710
|
function assertCompositionTimeOffsets(samples) {
|
|
@@ -10237,6 +10035,11 @@ var BlockcastMmt = (() => {
|
|
|
10237
10035
|
const [leftText, rightText = ""] = address.split("::");
|
|
10238
10036
|
const left = leftText ? leftText.split(":") : [];
|
|
10239
10037
|
const right = rightText ? rightText.split(":") : [];
|
|
10038
|
+
const tail = compressed ? right : left;
|
|
10039
|
+
if (tail.length > 0 && tail[tail.length - 1].includes(".")) {
|
|
10040
|
+
const v4 = parseIpv4(tail.pop());
|
|
10041
|
+
tail.push((v4[0] << 8 | v4[1]).toString(16), (v4[2] << 8 | v4[3]).toString(16));
|
|
10042
|
+
}
|
|
10240
10043
|
if (left.some((group) => group.includes(".")) || right.some((group) => group.includes("."))) {
|
|
10241
10044
|
throw new Error(`invalid IPv6 address ${address}`);
|
|
10242
10045
|
}
|
|
@@ -10259,7 +10062,7 @@ var BlockcastMmt = (() => {
|
|
|
10259
10062
|
return parseIpv4(address);
|
|
10260
10063
|
}
|
|
10261
10064
|
function endpointBytes(endpoint) {
|
|
10262
|
-
if (!validPort(endpoint.port) || !
|
|
10065
|
+
if (!validPort(endpoint.port) || !validU32(endpoint.streamId)) {
|
|
10263
10066
|
throw new Error("invalid BCPV2 endpoint");
|
|
10264
10067
|
}
|
|
10265
10068
|
const source = parseBcProvenanceV2Address(endpoint.sourceAddress);
|
|
@@ -10319,7 +10122,7 @@ var BlockcastMmt = (() => {
|
|
|
10319
10122
|
offset += source.byteLength;
|
|
10320
10123
|
prefix.set(group, offset);
|
|
10321
10124
|
offset += group.byteLength;
|
|
10322
|
-
prefix.set(u16(endpoint.
|
|
10125
|
+
prefix.set(u16(endpoint.port), offset);
|
|
10323
10126
|
const message = new Uint8Array(prefix.byteLength + record.body.byteLength);
|
|
10324
10127
|
message.set(prefix);
|
|
10325
10128
|
message.set(record.body, prefix.byteLength);
|
|
@@ -11874,10 +11677,12 @@ var BlockcastMmt = (() => {
|
|
|
11874
11677
|
__publicField(this, "holdBytes", { multicast: 0, unicast: 0 });
|
|
11875
11678
|
__publicField(this, "order", 0);
|
|
11876
11679
|
__publicField(this, "buckets");
|
|
11877
|
-
|
|
11878
|
-
this.
|
|
11879
|
-
|
|
11880
|
-
|
|
11680
|
+
const delay = cfg.maxAuthDelayMs;
|
|
11681
|
+
this.maxAuthDelayMs = typeof delay !== "number" || !Number.isFinite(delay) ? 200 : delay < 50 ? 50 : delay > 1e3 ? 1e3 : Number.isInteger(delay) ? delay : 200;
|
|
11682
|
+
const timer = cfg.manifestTimerMs;
|
|
11683
|
+
this.manifestTimerMs = typeof timer !== "number" || !Number.isFinite(timer) ? 20 : timer < 1 || Number.isInteger(timer) ? timer : 20;
|
|
11684
|
+
if (!(this.manifestTimerMs >= 1)) {
|
|
11685
|
+
throw new Error("BCPV2 endpoint ignored: manifestTimerMs below 1");
|
|
11881
11686
|
}
|
|
11882
11687
|
if (!(cfg.bandwidth > 0))
|
|
11883
11688
|
throw new Error("BCPV2 endpoint ignored: protected endpoint without bandwidth");
|
|
@@ -11917,7 +11722,7 @@ var BlockcastMmt = (() => {
|
|
|
11917
11722
|
return 0;
|
|
11918
11723
|
return this.lead ?? BCPV2_LEAD_BOUND_MS;
|
|
11919
11724
|
}
|
|
11920
|
-
/** Step 1: a record datagram on `
|
|
11725
|
+
/** Step 1: a record datagram on `port`, or a record object of the manifest track. */
|
|
11921
11726
|
onRecord(bytes, path) {
|
|
11922
11727
|
var _a;
|
|
11923
11728
|
const now = this.now();
|
|
@@ -12243,7 +12048,7 @@ var BlockcastMmt = (() => {
|
|
|
12243
12048
|
}
|
|
12244
12049
|
/**
|
|
12245
12050
|
* Retire verified entries below their arrival path's replay floor, keeping a mirrored entry
|
|
12246
|
-
* while the freshness check would still pass its copy on `
|
|
12051
|
+
* while the freshness check would still pass its copy on `port`.
|
|
12247
12052
|
* ponytail: linear scan per record; index by (kid, epoch) if retained sets grow past a few thousand.
|
|
12248
12053
|
*/
|
|
12249
12054
|
prune(now) {
|