@blockcast/mmt-container 0.5.0 → 1.0.0-main.63922d1832ee
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 +69 -48
- package/dist/browser-runtime.js.map +2 -2
- package/dist/mmtp-router.d.ts +7 -16
- package/dist/mmtp-router.d.ts.map +1 -1
- package/dist/mmtp-router.js +13 -27
- package/dist/mmtp-router.js.map +2 -2
- package/dist/multicast-manager.d.ts +5 -5
- package/dist/multicast-manager.d.ts.map +1 -1
- package/dist/multicast-manager.js +69 -48
- package/dist/multicast-manager.js.map +2 -2
- package/dist/multicast-transport.d.ts +23 -2
- package/dist/multicast-transport.d.ts.map +1 -1
- package/dist/multicast-transport.js +45 -12
- package/dist/multicast-transport.js.map +2 -2
- package/package.json +5 -5
- package/src/mmtp-router.ts +14 -29
- package/src/multicast-manager.ts +17 -11
- package/src/multicast-transport.ts +65 -12
package/README.md
CHANGED
|
@@ -270,7 +270,7 @@ per-commit `0.1.0-main.<sha>` prerelease from `stamp-prerelease.mjs`), and a
|
|
|
270
270
|
> Superseded by BLO-40665. Every package is now bumped in lockstep by a `v*`
|
|
271
271
|
> tag and the sibling ranges move with it, so the objection above no longer
|
|
272
272
|
> applies -- it was an argument against bumping *one* package. The set is at
|
|
273
|
-
> `0.
|
|
273
|
+
> `1.0.0`; see `RELEASES.md`.
|
|
274
274
|
|
|
275
275
|
#### Track-role convention (Phase 14)
|
|
276
276
|
|
package/dist/browser-runtime.js
CHANGED
|
@@ -19622,8 +19622,6 @@ var MmtpRouter = class {
|
|
|
19622
19622
|
// Callbacks (legacy — used when per-track callbacks are not registered)
|
|
19623
19623
|
#onVideoFrame = null;
|
|
19624
19624
|
#onAudioFrame = null;
|
|
19625
|
-
// Init segment (ftyp+moov) from MMTP MPU metadata — needed for MSE SourceBuffer
|
|
19626
|
-
#onInitSegment = null;
|
|
19627
19625
|
// Per-track init segment callbacks keyed by packetId
|
|
19628
19626
|
#trackInitCallbacks = /* @__PURE__ */ new Map();
|
|
19629
19627
|
// Track whether init has been delivered per physical stream — prevent duplicates
|
|
@@ -19886,11 +19884,10 @@ var MmtpRouter = class {
|
|
|
19886
19884
|
};
|
|
19887
19885
|
}
|
|
19888
19886
|
/**
|
|
19889
|
-
* Init segments
|
|
19890
|
-
*
|
|
19891
|
-
*
|
|
19892
|
-
* 0 here
|
|
19893
|
-
* aggregate callback (MOQT-01, BLO-21181).
|
|
19887
|
+
* Init segments rejected since construction because their packetId had no
|
|
19888
|
+
* `onTrackInit` handler. A rejected init is delivered nowhere; there is no
|
|
19889
|
+
* aggregate fallback (removed in 1.0.0, MOQT-01 / BLO-21181). A supported
|
|
19890
|
+
* deployment reads 0 here.
|
|
19894
19891
|
*/
|
|
19895
19892
|
get unmappedInitDeliveryCount() {
|
|
19896
19893
|
return this.#unmappedInitDeliveries;
|
|
@@ -20067,20 +20064,10 @@ var MmtpRouter = class {
|
|
|
20067
20064
|
onTrackPacket(packetId, cb) {
|
|
20068
20065
|
this.#trackPacketCallbacks.set(packetId, cb);
|
|
20069
20066
|
}
|
|
20070
|
-
/**
|
|
20071
|
-
* Register an init segment callback (ftyp+moov from MMTP MPU metadata).
|
|
20072
|
-
* Receives an init only for a packetId with no `onTrackInit` handler.
|
|
20073
|
-
*
|
|
20074
|
-
* @deprecated Use onTrackInit(packetId, cb). Still delivered as a fallback;
|
|
20075
|
-
* removed in the 1.0.0 major, no earlier than one minor release after this
|
|
20076
|
-
* deprecation reaches the `latest` dist-tag (BLO-21181). Count fallback
|
|
20077
|
-
* deliveries with `unmappedInitDeliveryCount`.
|
|
20078
|
-
*/
|
|
20079
|
-
onInitSegment(cb) {
|
|
20080
|
-
this.#onInitSegment = cb;
|
|
20081
|
-
}
|
|
20082
20067
|
/** Register an init segment callback for a specific MMTP packetId.
|
|
20083
|
-
* Called when FT=0 init arrives via direct source or FEC recovery.
|
|
20068
|
+
* Called when FT=0 init arrives via direct source or FEC recovery. An init
|
|
20069
|
+
* for a packetId with no handler is rejected and counted by
|
|
20070
|
+
* `unmappedInitDeliveryCount`. */
|
|
20084
20071
|
onTrackInit(packetId, cb) {
|
|
20085
20072
|
this.#trackInitCallbacks.set(packetId, cb);
|
|
20086
20073
|
}
|
|
@@ -21480,16 +21467,15 @@ var MmtpRouter = class {
|
|
|
21480
21467
|
if (this.#trackInitDelivered.has(streamKey)) return;
|
|
21481
21468
|
if (data.byteLength <= 8) return;
|
|
21482
21469
|
this.#trackInitDelivered.add(streamKey);
|
|
21470
|
+
const track = `packetId=${packetId}${catalogTrackName === void 0 ? "" : ` track=${catalogTrackName}`}`;
|
|
21483
21471
|
const cb = this.#trackInitCallbacks.get(packetId);
|
|
21484
|
-
if (cb) {
|
|
21485
|
-
cb(data);
|
|
21486
|
-
} else {
|
|
21472
|
+
if (!cb) {
|
|
21487
21473
|
this.#unmappedInitDeliveries++;
|
|
21488
|
-
|
|
21474
|
+
console.log(`[MmtpRouter] Init rejected for ${track}: no onTrackInit handler`);
|
|
21475
|
+
return;
|
|
21489
21476
|
}
|
|
21490
|
-
|
|
21491
|
-
|
|
21492
|
-
);
|
|
21477
|
+
cb(data);
|
|
21478
|
+
console.log(`[MmtpRouter] Init delivered for ${track} (${data.byteLength}B)`);
|
|
21493
21479
|
}
|
|
21494
21480
|
/**
|
|
21495
21481
|
* Core MMTP packet processing — source packets → play + FEC, repair → FEC only.
|
|
@@ -22989,6 +22975,10 @@ function getMulticastGlobals() {
|
|
|
22989
22975
|
function hasMulticastGateway() {
|
|
22990
22976
|
return typeof getMulticastGlobals().multicast?.subscribe === "function";
|
|
22991
22977
|
}
|
|
22978
|
+
function senderOf(event) {
|
|
22979
|
+
const sourceAddress = event.sourceAddress;
|
|
22980
|
+
return typeof sourceAddress === "string" ? sourceAddress : void 0;
|
|
22981
|
+
}
|
|
22992
22982
|
function normalizeBytesEventDetail(detail) {
|
|
22993
22983
|
const value = detail && typeof detail === "object" && "data" in detail ? detail.data : detail;
|
|
22994
22984
|
if (value instanceof Uint8Array) return value;
|
|
@@ -23035,7 +23025,8 @@ var MulticastTransport = class {
|
|
|
23035
23025
|
bytesReceived: 0,
|
|
23036
23026
|
packetsDroppedUnregisteredPid: 0,
|
|
23037
23027
|
packetsDroppedUnparseable: 0,
|
|
23038
|
-
packetsDroppedSignaling: 0
|
|
23028
|
+
packetsDroppedSignaling: 0,
|
|
23029
|
+
packetsDroppedForeignSender: 0
|
|
23039
23030
|
};
|
|
23040
23031
|
// join() calls by how they resolved; see joinStats.
|
|
23041
23032
|
#joinsSucceeded = 0;
|
|
@@ -23060,6 +23051,11 @@ var MulticastTransport = class {
|
|
|
23060
23051
|
// per packet once a count outgrows the small-integer range.
|
|
23061
23052
|
#acceptedBytesByPacketId = /* @__PURE__ */ new Map();
|
|
23062
23053
|
#packetIds = null;
|
|
23054
|
+
// The sender S this join's accepted packets came from, once known; see
|
|
23055
|
+
// packetsDroppedForeignSender. Cleared by every join and by a silence
|
|
23056
|
+
// failback, so a replacement publisher is not locked out.
|
|
23057
|
+
#sender = void 0;
|
|
23058
|
+
#lockedTo;
|
|
23063
23059
|
// What the live join delivers with each accepted packet. A field, not a
|
|
23064
23060
|
// listener closure, so an in-place admission change (setContext) applies
|
|
23065
23061
|
// from the next packet on without rejoining.
|
|
@@ -23081,9 +23077,15 @@ var MulticastTransport = class {
|
|
|
23081
23077
|
#joinGeneration = 0;
|
|
23082
23078
|
// The provider to subscribe through; window.multicast when unset.
|
|
23083
23079
|
#gateway;
|
|
23080
|
+
/**
|
|
23081
|
+
* `lockedTo` reads the sender another transport is locked to (a repair
|
|
23082
|
+
* group follows its media join, whose publisher sends both). While it reads
|
|
23083
|
+
* a sender, this join admits only that one; until then its own lock applies.
|
|
23084
|
+
*/
|
|
23084
23085
|
constructor(onPacket, options = {}) {
|
|
23085
23086
|
this.#onPacket = onPacket;
|
|
23086
23087
|
this.#gateway = options.gateway;
|
|
23088
|
+
this.#lockedTo = options.lockedTo;
|
|
23087
23089
|
}
|
|
23088
23090
|
static isAvailable() {
|
|
23089
23091
|
return hasMulticastGateway();
|
|
@@ -23092,6 +23094,7 @@ var MulticastTransport = class {
|
|
|
23092
23094
|
const generation = ++this.#joinGeneration;
|
|
23093
23095
|
this.#releaseSubscription();
|
|
23094
23096
|
this.#dataPlaneLive = false;
|
|
23097
|
+
this.#sender = void 0;
|
|
23095
23098
|
if (this.#disposed) return this.#declined(generation, "disposed");
|
|
23096
23099
|
const gateway = this.#gateway ?? getMulticastGlobals().multicast;
|
|
23097
23100
|
if (typeof gateway?.subscribe !== "function") {
|
|
@@ -23133,7 +23136,8 @@ var MulticastTransport = class {
|
|
|
23133
23136
|
}
|
|
23134
23137
|
this.#subscription = subscription;
|
|
23135
23138
|
const live = () => !this.#disposed && generation === this.#joinGeneration;
|
|
23136
|
-
const
|
|
23139
|
+
const anySource = !config.sourceAddress && !config.senderAddress && !config.atsc3;
|
|
23140
|
+
const packetListener = !anySource && subscriptionDeliversPacketBatches(subscription) ? {
|
|
23137
23141
|
type: "packets",
|
|
23138
23142
|
listener: (event) => {
|
|
23139
23143
|
if (!live()) return;
|
|
@@ -23143,7 +23147,7 @@ var MulticastTransport = class {
|
|
|
23143
23147
|
type: "bytes",
|
|
23144
23148
|
listener: (event) => {
|
|
23145
23149
|
if (!live()) return;
|
|
23146
|
-
if (this.#deliver(event.detail)) this.#resetWatchdog();
|
|
23150
|
+
if (this.#deliver(event.detail, senderOf(event))) this.#resetWatchdog();
|
|
23147
23151
|
}
|
|
23148
23152
|
};
|
|
23149
23153
|
this.#packetListener = packetListener;
|
|
@@ -23234,13 +23238,13 @@ var MulticastTransport = class {
|
|
|
23234
23238
|
* One packet from the subscription: filter, count, and hand it to the
|
|
23235
23239
|
* callback. Returns whether it was delivered; the caller notes liveness.
|
|
23236
23240
|
*/
|
|
23237
|
-
#deliver(detail) {
|
|
23241
|
+
#deliver(detail, sourceAddress) {
|
|
23238
23242
|
const packet = normalizeBytesEventDetail(detail);
|
|
23239
23243
|
if (!packet) {
|
|
23240
23244
|
this.#stats.packetsDroppedUnparseable++;
|
|
23241
23245
|
return false;
|
|
23242
23246
|
}
|
|
23243
|
-
if (!this.#acceptPacket(packet)) return false;
|
|
23247
|
+
if (!this.#acceptPacket(packet, sourceAddress)) return false;
|
|
23244
23248
|
this.#onPacket(packet, this.#context);
|
|
23245
23249
|
return true;
|
|
23246
23250
|
}
|
|
@@ -23281,11 +23285,15 @@ var MulticastTransport = class {
|
|
|
23281
23285
|
throw failures[0];
|
|
23282
23286
|
}
|
|
23283
23287
|
}
|
|
23284
|
-
/**
|
|
23285
|
-
|
|
23288
|
+
/**
|
|
23289
|
+
* Feed a raw packet from an external source (e.g., AMT tunnel or ASM
|
|
23290
|
+
* subscription). `sourceAddress` is the datagram's sender when the source
|
|
23291
|
+
* knows it; see packetsDroppedForeignSender.
|
|
23292
|
+
*/
|
|
23293
|
+
feedPacket(data, context, sourceAddress) {
|
|
23286
23294
|
if (this.#disposed) return;
|
|
23287
23295
|
const packet = data instanceof Uint8Array ? data : new Uint8Array(data);
|
|
23288
|
-
if (!this.#acceptPacket(packet)) return;
|
|
23296
|
+
if (!this.#acceptPacket(packet, sourceAddress)) return;
|
|
23289
23297
|
this.#onPacket(packet, context);
|
|
23290
23298
|
this.#resetWatchdog();
|
|
23291
23299
|
}
|
|
@@ -23435,6 +23443,10 @@ var MulticastTransport = class {
|
|
|
23435
23443
|
get withdrawnPacketIdDrops() {
|
|
23436
23444
|
return this.#withdrawnPacketIdDrops;
|
|
23437
23445
|
}
|
|
23446
|
+
/** The sender this join is locked to; undefined until one is known. */
|
|
23447
|
+
get sender() {
|
|
23448
|
+
return this.#sender;
|
|
23449
|
+
}
|
|
23438
23450
|
/** Whether a subscription is live: joined, and not yet released or replaced. */
|
|
23439
23451
|
get joined() {
|
|
23440
23452
|
return this.#subscription !== null;
|
|
@@ -23597,11 +23609,16 @@ var MulticastTransport = class {
|
|
|
23597
23609
|
this.#withdrawnPacketIds.clear();
|
|
23598
23610
|
}
|
|
23599
23611
|
/** The ingress filter; an accepted packet is counted here, once. */
|
|
23600
|
-
#acceptPacket(packet) {
|
|
23612
|
+
#acceptPacket(packet, sourceAddress) {
|
|
23613
|
+
const owner = this.#lockedTo?.() ?? this.#sender;
|
|
23614
|
+
if (sourceAddress !== void 0 && owner !== void 0 && sourceAddress !== owner) {
|
|
23615
|
+
this.#stats.packetsDroppedForeignSender++;
|
|
23616
|
+
return false;
|
|
23617
|
+
}
|
|
23601
23618
|
const packetId = readMMTPPacketId(packet);
|
|
23602
23619
|
if (packetId === null) {
|
|
23603
23620
|
if (!this.#packetIds) {
|
|
23604
|
-
this.#countAccepted(packet, null);
|
|
23621
|
+
this.#countAccepted(packet, null, sourceAddress);
|
|
23605
23622
|
return true;
|
|
23606
23623
|
}
|
|
23607
23624
|
this.#stats.packetsDroppedUnparseable++;
|
|
@@ -23612,14 +23629,15 @@ var MulticastTransport = class {
|
|
|
23612
23629
|
return false;
|
|
23613
23630
|
}
|
|
23614
23631
|
if (!this.#packetIds || this.#packetIds.has(packetId)) {
|
|
23615
|
-
this.#countAccepted(packet, packetId);
|
|
23632
|
+
this.#countAccepted(packet, packetId, sourceAddress);
|
|
23616
23633
|
return true;
|
|
23617
23634
|
}
|
|
23618
23635
|
this.#recordUnregisteredPidDrop(packetId);
|
|
23619
23636
|
return false;
|
|
23620
23637
|
}
|
|
23621
23638
|
/** Receive counters for one accepted packet; `packetId` is null when it has none. */
|
|
23622
|
-
#countAccepted(packet, packetId) {
|
|
23639
|
+
#countAccepted(packet, packetId, sourceAddress) {
|
|
23640
|
+
this.#sender ??= sourceAddress;
|
|
23623
23641
|
this.#stats.packetsReceived++;
|
|
23624
23642
|
this.#stats.bytesReceived += packet.byteLength;
|
|
23625
23643
|
if (packetId === null) return;
|
|
@@ -23690,6 +23708,7 @@ var MulticastTransport = class {
|
|
|
23690
23708
|
console.warn(
|
|
23691
23709
|
`[MulticastTransport] No packets for ${this.#failbackWindowMs}ms; failing back to MoQ`
|
|
23692
23710
|
);
|
|
23711
|
+
this.#sender = void 0;
|
|
23693
23712
|
this.#onFailback({ kind: "silence", windowMs: this.#failbackWindowMs });
|
|
23694
23713
|
}, delayMs);
|
|
23695
23714
|
}
|
|
@@ -27704,10 +27723,6 @@ var MulticastManager = class {
|
|
|
27704
27723
|
onTrackPacket(packetId, cb) {
|
|
27705
27724
|
this.#router.onTrackPacket(packetId, cb);
|
|
27706
27725
|
}
|
|
27707
|
-
/** @deprecated Use onTrackInit(packetId, cb); see MmtpRouter.onInitSegment. */
|
|
27708
|
-
onInitSegment(cb) {
|
|
27709
|
-
this.#router.onInitSegment(cb);
|
|
27710
|
-
}
|
|
27711
27726
|
onTrackInit(packetId, cb) {
|
|
27712
27727
|
this.#router.onTrackInit(packetId, cb);
|
|
27713
27728
|
}
|
|
@@ -27931,15 +27946,21 @@ var MulticastManager = class {
|
|
|
27931
27946
|
tracks: Object.freeze(endpoint.tracks.map(({ name, packetId }) => Object.freeze({ name, packetId }))),
|
|
27932
27947
|
...endpoint.flow === void 0 ? {} : { flow: endpoint.flow }
|
|
27933
27948
|
};
|
|
27934
|
-
|
|
27935
|
-
|
|
27949
|
+
let sender;
|
|
27950
|
+
const onPacket = (packet, sourceAddress) => {
|
|
27951
|
+
if (released) return;
|
|
27952
|
+
if (sourceAddress !== void 0 && sourceAddress !== (this.#transport.sender ?? (sender ??= sourceAddress))) return;
|
|
27953
|
+
this.#router.feedPacket(packet, "multicast", ingress);
|
|
27936
27954
|
};
|
|
27937
27955
|
const fail = (error) => console.warn(`[MulticastManager] repair group ${endpoint.group}:${endpoint.port} not joined`, error);
|
|
27938
27956
|
if (typeof provider === "function") {
|
|
27939
27957
|
Promise.resolve().then(() => provider(endpoint, onPacket)).then((release2) => released ? safely(release2) : releases.push(release2), fail);
|
|
27940
27958
|
continue;
|
|
27941
27959
|
}
|
|
27942
|
-
const transport = new MulticastTransport((packet) => onPacket(packet), {
|
|
27960
|
+
const transport = new MulticastTransport((packet) => onPacket(packet), {
|
|
27961
|
+
gateway: provider,
|
|
27962
|
+
lockedTo: () => this.#transport.sender
|
|
27963
|
+
});
|
|
27943
27964
|
releases.push(() => transport.dispose());
|
|
27944
27965
|
void transport.join({
|
|
27945
27966
|
sourceAddress: endpoint.source,
|
|
@@ -28218,8 +28239,8 @@ var MulticastManager = class {
|
|
|
28218
28239
|
* transport attribution in FEC path counters.
|
|
28219
28240
|
* The `router` accessor above is already public.
|
|
28220
28241
|
*/
|
|
28221
|
-
feedPacket(data, ingress) {
|
|
28222
|
-
this.#transport.feedPacket(data, ingress);
|
|
28242
|
+
feedPacket(data, ingress, sourceAddress) {
|
|
28243
|
+
this.#transport.feedPacket(data, ingress, sourceAddress);
|
|
28223
28244
|
}
|
|
28224
28245
|
/**
|
|
28225
28246
|
* Declare how packets given to feedPacket() reach this manager. A consumer
|