@blockcast/mmt-container 2.1.0 → 2.2.0-main.6c178bf47550
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 +458 -77
- package/dist/browser-runtime.js.map +4 -4
- package/dist/carp-catalog.js.map +1 -1
- package/dist/catalog-fec-params.js.map +1 -1
- package/dist/catalog-network-source.js.map +1 -1
- package/dist/cmcd-producer.d.ts +5 -0
- package/dist/cmcd-producer.d.ts.map +1 -1
- package/dist/cmcd-producer.js.map +2 -2
- package/dist/delivery-catalog.js.map +1 -1
- package/dist/delta-fallback-config.js.map +1 -1
- 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.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +2 -2
- package/dist/iso-block-locator.js.map +1 -1
- package/dist/mfu-reassembler.js +12 -19
- package/dist/mfu-reassembler.js.map +2 -2
- package/dist/mmtp-container-parser.js +12 -19
- package/dist/mmtp-container-parser.js.map +2 -2
- package/dist/mmtp-container-parser.no-modules.js +133 -34
- package/dist/mmtp-container-parser.no-modules.js.map +3 -3
- package/dist/mmtp-decode-pipeline.js +12 -19
- package/dist/mmtp-decode-pipeline.js.map +2 -2
- package/dist/mmtp-fec-session.d.ts +41 -3
- package/dist/mmtp-fec-session.d.ts.map +1 -1
- package/dist/mmtp-fec-session.js +118 -13
- package/dist/mmtp-fec-session.js.map +3 -3
- package/dist/mmtp-router.d.ts +9 -1
- package/dist/mmtp-router.d.ts.map +1 -1
- package/dist/mmtp-router.js +1468 -33
- package/dist/mmtp-router.js.map +4 -4
- package/dist/multicast-auth-catalog.js.map +1 -1
- package/dist/multicast-input.js.map +1 -1
- package/dist/multicast-manager.d.ts +15 -2
- package/dist/multicast-manager.d.ts.map +1 -1
- package/dist/multicast-manager.js +15246 -14969
- package/dist/multicast-manager.js.map +4 -4
- package/dist/multicast-transport.d.ts +11 -3
- package/dist/multicast-transport.d.ts.map +1 -1
- package/dist/multicast-transport.js +255 -19
- package/dist/multicast-transport.js.map +4 -4
- package/dist/multicast-unwrap.js +12 -19
- package/dist/multicast-unwrap.js.map +2 -2
- package/dist/no-modules.js +130 -33
- package/dist/no-modules.js.map +3 -3
- package/dist/repair-fetch.js.map +1 -1
- package/dist/signaling.js.map +2 -2
- package/dist/timestamp-codec.js +12 -19
- package/dist/timestamp-codec.js.map +2 -2
- package/dist/wasm-mfu-reassembler.js.map +1 -1
- package/dist/wasm-mmtp-container-parser.js +12 -19
- package/dist/wasm-mmtp-container-parser.js.map +2 -2
- package/package.json +5 -5
- package/src/cmcd-producer.ts +5 -0
- package/src/index.ts +2 -0
- package/src/mmtp-fec-session.ts +115 -15
- package/src/mmtp-router.ts +81 -16
- package/src/multicast-manager.ts +26 -5
- package/src/multicast-transport.ts +44 -20
package/dist/no-modules.js
CHANGED
|
@@ -10,6 +10,8 @@ var PACKET_TYPE_MPU = 0;
|
|
|
10
10
|
var PACKET_TYPE_REPAIR = 3;
|
|
11
11
|
var MMTP_SIGNALING_PACKET_ID = 0;
|
|
12
12
|
var MMTP_PACKET_ID_MAX = 65535;
|
|
13
|
+
var FEC_CODE_REED_SOLOMON = 1;
|
|
14
|
+
var FEC_CODE_RAPTORQ = 3;
|
|
13
15
|
function parseMMTPHeader(buffer) {
|
|
14
16
|
if (buffer.length < MMTP_HEADER_SIZE) {
|
|
15
17
|
throw new Error(`MMTP packet too small: ${buffer.length} bytes`);
|
|
@@ -150,30 +152,26 @@ function isEmptyOrExactLegacyTrailer(payload, start, end) {
|
|
|
150
152
|
const authLength = view2.getUint16(2, false);
|
|
151
153
|
return start + LEGACY_TRAILER_HEADER_SIZE + authLength === end;
|
|
152
154
|
}
|
|
153
|
-
function resolveDeclaredTrailers(payload, declaredEnd, fecType, options
|
|
155
|
+
function resolveDeclaredTrailers(payload, declaredEnd, fecType, options) {
|
|
154
156
|
if (declaredEnd > payload.length)
|
|
155
157
|
return null;
|
|
156
158
|
if (options?.skipFecId) {
|
|
157
|
-
return isEmptyOrExactLegacyTrailer(payload, declaredEnd, payload.length) ? { hasFecId: false
|
|
159
|
+
return isEmptyOrExactLegacyTrailer(payload, declaredEnd, payload.length) ? { hasFecId: false } : null;
|
|
158
160
|
}
|
|
159
|
-
const overlay = options?.overlayDeclared === true && rapFlag;
|
|
160
161
|
if (fecType === 1) {
|
|
161
|
-
const fecStart = payload.length -
|
|
162
|
+
const fecStart = payload.length - SOURCE_FEC_PAYLOAD_ID_SIZE;
|
|
162
163
|
if (fecStart < declaredEnd || !isEmptyOrExactLegacyTrailer(payload, declaredEnd, fecStart)) {
|
|
163
164
|
return null;
|
|
164
165
|
}
|
|
165
|
-
return { hasFecId: true
|
|
166
|
+
return { hasFecId: true };
|
|
166
167
|
}
|
|
167
|
-
if (fecType !== 0
|
|
168
|
+
if (fecType !== 0)
|
|
168
169
|
return null;
|
|
169
170
|
if (isEmptyOrExactLegacyTrailer(payload, declaredEnd, payload.length)) {
|
|
170
|
-
return { hasFecId: false
|
|
171
|
+
return { hasFecId: false };
|
|
171
172
|
}
|
|
172
173
|
return null;
|
|
173
174
|
}
|
|
174
|
-
function sourceFecTrailerSize(overlay) {
|
|
175
|
-
return overlay ? 2 * SOURCE_FEC_PAYLOAD_ID_SIZE : SOURCE_FEC_PAYLOAD_ID_SIZE;
|
|
176
|
-
}
|
|
177
175
|
function planMfuExtraction(mmtpPacket, options, parsedHeader) {
|
|
178
176
|
if (mmtpPacket.length < MMTP_HEADER_SIZE + MPU_HEADER_SIZE) {
|
|
179
177
|
return {
|
|
@@ -226,11 +224,11 @@ function planMfuExtraction(mmtpPacket, options, parsedHeader) {
|
|
|
226
224
|
if ("rejection" in layoutResult)
|
|
227
225
|
return layoutResult;
|
|
228
226
|
const { layout } = layoutResult;
|
|
229
|
-
const trailerLayout = resolveDeclaredTrailers(payload, layout.dataEnd, header.fecType, options
|
|
227
|
+
const trailerLayout = resolveDeclaredTrailers(payload, layout.dataEnd, header.fecType, options);
|
|
230
228
|
if (!trailerLayout) {
|
|
231
229
|
if (!options?.skipFecId && header.fecType === 1) {
|
|
232
230
|
const observed = payload.length - layout.dataEnd;
|
|
233
|
-
const expected =
|
|
231
|
+
const expected = SOURCE_FEC_PAYLOAD_ID_SIZE;
|
|
234
232
|
if (observed !== expected) {
|
|
235
233
|
return {
|
|
236
234
|
rejection: {
|
|
@@ -262,13 +260,10 @@ function extractPlannedMfu(mmtpPacket, result) {
|
|
|
262
260
|
const mfuDuHeader = mfuDuHeaderPresent && mpu.timedFlag === 1 ? parseTimedMfuDuHeader(payload, MPU_HEADER_SIZE) : null;
|
|
263
261
|
const itemId = mfuDuHeaderPresent && mpu.timedFlag === 0 ? new DataView(payload.buffer, payload.byteOffset + MPU_HEADER_SIZE, 4).getUint32(0, false) : null;
|
|
264
262
|
const data = payload.subarray(mfuOffset, dataEnd);
|
|
265
|
-
const
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
}
|
|
269
|
-
const fecPayloadId = trailerLayout.hasFecId ? ssIdAt(trailerStart) : null;
|
|
270
|
-
const overlayFecPayloadId = trailerLayout.hasOverlayFecId ? ssIdAt(trailerStart + SOURCE_FEC_PAYLOAD_ID_SIZE) : null;
|
|
271
|
-
return { header, mpu, fragmentType, data, mfuDuHeader, itemId, fecPayloadId, overlayFecPayloadId };
|
|
263
|
+
const fecPayloadId = trailerLayout.hasFecId ? {
|
|
264
|
+
ssId: new DataView(mmtpPacket.buffer, mmtpPacket.byteOffset + mmtpPacket.length - SOURCE_FEC_PAYLOAD_ID_SIZE, 4).getUint32(0, false)
|
|
265
|
+
} : null;
|
|
266
|
+
return { header, mpu, fragmentType, data, mfuDuHeader, itemId, fecPayloadId };
|
|
272
267
|
}
|
|
273
268
|
function sourceFecPayloadIdOf(mmtpPacket, header) {
|
|
274
269
|
if (mmtpPacket.length < MMTP_HEADER_SIZE + MPU_HEADER_SIZE)
|
|
@@ -360,6 +355,50 @@ var NTP_SHORT_BITS = 32;
|
|
|
360
355
|
var NTP_SHORT_MODULUS = 2 ** NTP_SHORT_BITS;
|
|
361
356
|
var NTP_SHORT_HALF_RANGE = 2 ** (NTP_SHORT_BITS - 1);
|
|
362
357
|
|
|
358
|
+
// ../mmt-base/dist/signalling.js
|
|
359
|
+
var FEC_CODING_STRUCTURE_ONE_STAGE = 1;
|
|
360
|
+
var SSBG_MODE_1 = 1;
|
|
361
|
+
var RAPTORQ_MAX_SOURCE_SYMBOLS = 56403;
|
|
362
|
+
var REPAIR_SYMBOL_ID_MAX = 16777215;
|
|
363
|
+
var MIN_REPAIR_SYMBOL_SIZE = 14;
|
|
364
|
+
var REED_SOLOMON_MAX_BLOCK = 255;
|
|
365
|
+
function alFecRaptorQFlowConfig(flow) {
|
|
366
|
+
if (flow.fecCodingStructure !== FEC_CODING_STRUCTURE_ONE_STAGE || flow.repairFlows.length !== 1)
|
|
367
|
+
return null;
|
|
368
|
+
if (flow.ssbgMode !== SSBG_MODE_1 || flow.ffsrptsFlag !== 0 || flow.fecPayloadIdMode !== 0)
|
|
369
|
+
return null;
|
|
370
|
+
if (flow.packetIds.length !== 1)
|
|
371
|
+
return null;
|
|
372
|
+
const repair = flow.repairFlows[0];
|
|
373
|
+
const { fecCodeId, maximumK: k, maximumP: p } = repair;
|
|
374
|
+
const t = flow.repairSymbolSize;
|
|
375
|
+
if (t < MIN_REPAIR_SYMBOL_SIZE || k < 1 || p < 1 || k + p - 1 > REPAIR_SYMBOL_ID_MAX)
|
|
376
|
+
return null;
|
|
377
|
+
if (fecCodeId === FEC_CODE_RAPTORQ) {
|
|
378
|
+
if (t % 8 !== 0 || k > RAPTORQ_MAX_SOURCE_SYMBOLS)
|
|
379
|
+
return null;
|
|
380
|
+
} else if (fecCodeId === FEC_CODE_REED_SOLOMON) {
|
|
381
|
+
if (k + p > REED_SOLOMON_MAX_BLOCK)
|
|
382
|
+
return null;
|
|
383
|
+
} else {
|
|
384
|
+
return null;
|
|
385
|
+
}
|
|
386
|
+
if (repair.repairFlowId === MMTP_SIGNALING_PACKET_ID || repair.repairFlowId === flow.packetIds[0])
|
|
387
|
+
return null;
|
|
388
|
+
return {
|
|
389
|
+
sourcePacketIds: [...flow.packetIds],
|
|
390
|
+
repairFlowId: repair.repairFlowId,
|
|
391
|
+
fecCodeId,
|
|
392
|
+
ssbgMode: flow.ssbgMode,
|
|
393
|
+
fecPayloadIdMode: flow.fecPayloadIdMode,
|
|
394
|
+
repairSymbolSize: t,
|
|
395
|
+
maximumK: k,
|
|
396
|
+
maximumP: p,
|
|
397
|
+
protectionWindowTime: repair.protectionWindowTime,
|
|
398
|
+
protectionWindowSize: repair.protectionWindowSize
|
|
399
|
+
};
|
|
400
|
+
}
|
|
401
|
+
|
|
363
402
|
// ../mmt-base/dist/serial-number.js
|
|
364
403
|
var SERIAL32_SPAN = 4294967296;
|
|
365
404
|
var SERIAL32_HALF = 2147483648;
|
|
@@ -8071,7 +8110,14 @@ var HeldLayerAdmission = class {
|
|
|
8071
8110
|
var PATH_BITS = { moq: 1, multicast: 2 };
|
|
8072
8111
|
var MIXED_PATHS = PATH_BITS.moq | PATH_BITS.multicast;
|
|
8073
8112
|
var MmtpFecSession = class {
|
|
8113
|
+
/** P_0 = B_0, the held total every repair packet is checked against. */
|
|
8114
|
+
get repairSymbols() {
|
|
8115
|
+
return this.#repairSymbols;
|
|
8116
|
+
}
|
|
8074
8117
|
#decoder;
|
|
8118
|
+
#repairSymbols;
|
|
8119
|
+
/** The message entry a catalog-less session was built from; undefined under a catalog. */
|
|
8120
|
+
#message;
|
|
8075
8121
|
/** Blocks a repair has defined and that are not settled yet, by SS_Start. */
|
|
8076
8122
|
#blocks = /* @__PURE__ */ new Map();
|
|
8077
8123
|
/** SS_Starts of settled blocks, so a later repair for one is refused. */
|
|
@@ -8109,6 +8155,24 @@ var MmtpFecSession = class {
|
|
|
8109
8155
|
* `interleaveDepthMs` or `reorderToleranceMs`. None is defaulted.
|
|
8110
8156
|
*/
|
|
8111
8157
|
constructor(config) {
|
|
8158
|
+
if (config && "alFecFlow" in config) {
|
|
8159
|
+
const message = alFecRaptorQFlowConfig(config.alFecFlow);
|
|
8160
|
+
if (!message) {
|
|
8161
|
+
throw new Error("MmtpFecSession: the AL-FEC entry is outside draft-ramadan-moq-mmt-02 8.3 (iso-ssbg1-v1)");
|
|
8162
|
+
}
|
|
8163
|
+
this.#message = message;
|
|
8164
|
+
this.blockProfile = parseFecBlockProfile("iso-ssbg1-v1", "alFecFlow");
|
|
8165
|
+
this.algorithm = message.fecCodeId === FEC_CODE_RAPTORQ ? "raptorq" : "reed-solomon";
|
|
8166
|
+
this.sourceSymbols = message.maximumK;
|
|
8167
|
+
this.#repairSymbols = message.maximumP;
|
|
8168
|
+
this.symbolSize = message.repairSymbolSize;
|
|
8169
|
+
this.retentionBlocks = requirePositiveInteger(config.retentionBlocks, "retentionBlocks");
|
|
8170
|
+
this.#repairPacketId = message.repairFlowId;
|
|
8171
|
+
this.#heldLayers = layerZero(message.maximumP);
|
|
8172
|
+
this.#anchorHorizon = heldLayerAnchorHorizon(this.retentionBlocks, this.sourceSymbols);
|
|
8173
|
+
this.#decoder = buildDecoder(config.Decoder, this);
|
|
8174
|
+
return;
|
|
8175
|
+
}
|
|
8112
8176
|
const fec = config?.fec;
|
|
8113
8177
|
if (!fec || typeof fec !== "object") {
|
|
8114
8178
|
throw new Error("MmtpFecSession: the catalog fec instance is required");
|
|
@@ -8119,7 +8183,7 @@ var MmtpFecSession = class {
|
|
|
8119
8183
|
}
|
|
8120
8184
|
this.algorithm = fec.scheme;
|
|
8121
8185
|
this.sourceSymbols = requirePositiveInteger(fec.sourceSymbols, "fec.sourceSymbols");
|
|
8122
|
-
this
|
|
8186
|
+
this.#repairSymbols = requirePositiveInteger(fec.repairSymbols, "fec.repairSymbols");
|
|
8123
8187
|
this.symbolSize = requirePositiveInteger(fec.symbolSize, "fec.symbolSize");
|
|
8124
8188
|
const interleaveDepthMs = requireNumber(fec.interleaveDepthMs, "fec.interleaveDepthMs");
|
|
8125
8189
|
const reorderToleranceMs = requireNumber(fec.reorderToleranceMs, "fec.reorderToleranceMs");
|
|
@@ -8139,17 +8203,30 @@ var MmtpFecSession = class {
|
|
|
8139
8203
|
config.heldLayers
|
|
8140
8204
|
);
|
|
8141
8205
|
this.#anchorHorizon = heldLayerAnchorHorizon(this.retentionBlocks, this.sourceSymbols);
|
|
8142
|
-
|
|
8143
|
-
|
|
8144
|
-
|
|
8145
|
-
|
|
8146
|
-
|
|
8147
|
-
|
|
8148
|
-
|
|
8149
|
-
|
|
8150
|
-
|
|
8151
|
-
|
|
8152
|
-
|
|
8206
|
+
this.#decoder = buildDecoder(config.Decoder, this);
|
|
8207
|
+
}
|
|
8208
|
+
/**
|
|
8209
|
+
* A later version of the AL-FEC message, catalog-less sessions only. A
|
|
8210
|
+
* maximum_p_for_repair_flow above the held total raises it (`raised`): the
|
|
8211
|
+
* anchored check restarts at the new total, so a block generated under the
|
|
8212
|
+
* previous one stays valid before the first block carrying the new total
|
|
8213
|
+
* (fec-core-00 7.3.2, mmt-02 8.3). A field at or below it changes nothing
|
|
8214
|
+
* (`unchanged`): no publisher lowers it, and the check catches a wire that
|
|
8215
|
+
* does. An entry with another asset, repair flow, code point, K or T, or
|
|
8216
|
+
* one outside 8.3, is `refused`: that is a new source encoding, which needs
|
|
8217
|
+
* a new session. A latched fail-closed stays latched either way.
|
|
8218
|
+
*/
|
|
8219
|
+
updateAlFecFlow(flow) {
|
|
8220
|
+
const held = this.#message;
|
|
8221
|
+
if (!held) throw new Error("MmtpFecSession: updateAlFecFlow() needs a session built from an AL-FEC entry");
|
|
8222
|
+
const next = alFecRaptorQFlowConfig(flow);
|
|
8223
|
+
if (!next || next.sourcePacketIds[0] !== held.sourcePacketIds[0] || next.repairFlowId !== held.repairFlowId || next.fecCodeId !== held.fecCodeId || next.maximumK !== held.maximumK || next.repairSymbolSize !== held.repairSymbolSize) {
|
|
8224
|
+
return "refused";
|
|
8225
|
+
}
|
|
8226
|
+
if (next.maximumP <= this.#repairSymbols) return "unchanged";
|
|
8227
|
+
this.#repairSymbols = next.maximumP;
|
|
8228
|
+
this.#heldLayers = layerZero(next.maximumP);
|
|
8229
|
+
return "raised";
|
|
8153
8230
|
}
|
|
8154
8231
|
/** True once release() ran. */
|
|
8155
8232
|
get released() {
|
|
@@ -8244,7 +8321,7 @@ var MmtpFecSession = class {
|
|
|
8244
8321
|
}
|
|
8245
8322
|
if (admission.reject) {
|
|
8246
8323
|
const verdict2 = this.#heldLayers.failsClosed(
|
|
8247
|
-
repairTrackName,
|
|
8324
|
+
this.#trackFor(repairTrackName),
|
|
8248
8325
|
frame.ssStart,
|
|
8249
8326
|
frame.rsbLength,
|
|
8250
8327
|
this.#anchorHorizon,
|
|
@@ -8259,7 +8336,7 @@ var MmtpFecSession = class {
|
|
|
8259
8336
|
this.#repairPacketId ??= frame.packetId;
|
|
8260
8337
|
if (frame.ssbLength > this.sourceSymbols) return this.#drop("repair:ssb-length");
|
|
8261
8338
|
const verdict = this.#heldLayers.admit(
|
|
8262
|
-
repairTrackName,
|
|
8339
|
+
this.#trackFor(repairTrackName),
|
|
8263
8340
|
frame.ssStart,
|
|
8264
8341
|
frame.rsbLength,
|
|
8265
8342
|
this.#anchorHorizon,
|
|
@@ -8318,6 +8395,10 @@ var MmtpFecSession = class {
|
|
|
8318
8395
|
decoder.free();
|
|
8319
8396
|
return true;
|
|
8320
8397
|
}
|
|
8398
|
+
/** Catalog-less: layer 0 has no own-track clause (mmt-02 8.3), so no track name is judged. */
|
|
8399
|
+
#trackFor(repairTrackName) {
|
|
8400
|
+
return this.#message ? void 0 : repairTrackName;
|
|
8401
|
+
}
|
|
8321
8402
|
#drop(reason) {
|
|
8322
8403
|
this.#stats.dropped[reason] = (this.#stats.dropped[reason] ?? 0) + 1;
|
|
8323
8404
|
return [];
|
|
@@ -8439,6 +8520,22 @@ var MmtpFecSession = class {
|
|
|
8439
8520
|
}
|
|
8440
8521
|
}
|
|
8441
8522
|
};
|
|
8523
|
+
function layerZero(total) {
|
|
8524
|
+
return new HeldLayerAdmission([{ layer: 0, repairTrackName: "", rsIdOffset: 0, repairSymbols: total }]);
|
|
8525
|
+
}
|
|
8526
|
+
function buildDecoder(Decoder, session) {
|
|
8527
|
+
if (typeof Decoder !== "function") {
|
|
8528
|
+
throw new Error("MmtpFecSession: a decoder constructor (wasm_bindgen.MmtFecDecoder) is required");
|
|
8529
|
+
}
|
|
8530
|
+
const decoder = new Decoder(session.retentionBlocks, session.blockProfile, session.algorithm);
|
|
8531
|
+
try {
|
|
8532
|
+
decoder.configure_params(session.sourceSymbols, session.symbolSize);
|
|
8533
|
+
} catch (error) {
|
|
8534
|
+
decoder.free();
|
|
8535
|
+
throw error;
|
|
8536
|
+
}
|
|
8537
|
+
return decoder;
|
|
8538
|
+
}
|
|
8442
8539
|
function requirePositiveInteger(value, path) {
|
|
8443
8540
|
if (typeof value !== "number" || !Number.isSafeInteger(value) || value <= 0) {
|
|
8444
8541
|
throw new Error(`MmtpFecSession: ${path} must be a positive integer (got ${String(value)})`);
|