@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.
Files changed (62) hide show
  1. package/dist/browser-runtime.js +458 -77
  2. package/dist/browser-runtime.js.map +4 -4
  3. package/dist/carp-catalog.js.map +1 -1
  4. package/dist/catalog-fec-params.js.map +1 -1
  5. package/dist/catalog-network-source.js.map +1 -1
  6. package/dist/cmcd-producer.d.ts +5 -0
  7. package/dist/cmcd-producer.d.ts.map +1 -1
  8. package/dist/cmcd-producer.js.map +2 -2
  9. package/dist/delivery-catalog.js.map +1 -1
  10. package/dist/delta-fallback-config.js.map +1 -1
  11. package/dist/fec-manager.js.map +2 -2
  12. package/dist/fec-state.js.map +1 -1
  13. package/dist/fec-worker-setup.js.map +1 -1
  14. package/dist/held-layer-admission.js.map +1 -1
  15. package/dist/index.d.ts +1 -1
  16. package/dist/index.d.ts.map +1 -1
  17. package/dist/index.js.map +2 -2
  18. package/dist/iso-block-locator.js.map +1 -1
  19. package/dist/mfu-reassembler.js +12 -19
  20. package/dist/mfu-reassembler.js.map +2 -2
  21. package/dist/mmtp-container-parser.js +12 -19
  22. package/dist/mmtp-container-parser.js.map +2 -2
  23. package/dist/mmtp-container-parser.no-modules.js +133 -34
  24. package/dist/mmtp-container-parser.no-modules.js.map +3 -3
  25. package/dist/mmtp-decode-pipeline.js +12 -19
  26. package/dist/mmtp-decode-pipeline.js.map +2 -2
  27. package/dist/mmtp-fec-session.d.ts +41 -3
  28. package/dist/mmtp-fec-session.d.ts.map +1 -1
  29. package/dist/mmtp-fec-session.js +118 -13
  30. package/dist/mmtp-fec-session.js.map +3 -3
  31. package/dist/mmtp-router.d.ts +9 -1
  32. package/dist/mmtp-router.d.ts.map +1 -1
  33. package/dist/mmtp-router.js +1468 -33
  34. package/dist/mmtp-router.js.map +4 -4
  35. package/dist/multicast-auth-catalog.js.map +1 -1
  36. package/dist/multicast-input.js.map +1 -1
  37. package/dist/multicast-manager.d.ts +15 -2
  38. package/dist/multicast-manager.d.ts.map +1 -1
  39. package/dist/multicast-manager.js +15246 -14969
  40. package/dist/multicast-manager.js.map +4 -4
  41. package/dist/multicast-transport.d.ts +11 -3
  42. package/dist/multicast-transport.d.ts.map +1 -1
  43. package/dist/multicast-transport.js +255 -19
  44. package/dist/multicast-transport.js.map +4 -4
  45. package/dist/multicast-unwrap.js +12 -19
  46. package/dist/multicast-unwrap.js.map +2 -2
  47. package/dist/no-modules.js +130 -33
  48. package/dist/no-modules.js.map +3 -3
  49. package/dist/repair-fetch.js.map +1 -1
  50. package/dist/signaling.js.map +2 -2
  51. package/dist/timestamp-codec.js +12 -19
  52. package/dist/timestamp-codec.js.map +2 -2
  53. package/dist/wasm-mfu-reassembler.js.map +1 -1
  54. package/dist/wasm-mmtp-container-parser.js +12 -19
  55. package/dist/wasm-mmtp-container-parser.js.map +2 -2
  56. package/package.json +5 -5
  57. package/src/cmcd-producer.ts +5 -0
  58. package/src/index.ts +2 -0
  59. package/src/mmtp-fec-session.ts +115 -15
  60. package/src/mmtp-router.ts +81 -16
  61. package/src/multicast-manager.ts +26 -5
  62. package/src/multicast-transport.ts +44 -20
@@ -73,6 +73,8 @@ var BlockcastMmt = (() => {
73
73
  var PACKET_TYPE_REPAIR = 3;
74
74
  var MMTP_SIGNALING_PACKET_ID = 0;
75
75
  var MMTP_PACKET_ID_MAX = 65535;
76
+ var FEC_CODE_REED_SOLOMON = 1;
77
+ var FEC_CODE_RAPTORQ = 3;
76
78
  function parseMMTPHeader(buffer) {
77
79
  if (buffer.length < MMTP_HEADER_SIZE) {
78
80
  throw new Error(`MMTP packet too small: ${buffer.length} bytes`);
@@ -213,30 +215,26 @@ var BlockcastMmt = (() => {
213
215
  const authLength = view2.getUint16(2, false);
214
216
  return start + LEGACY_TRAILER_HEADER_SIZE + authLength === end;
215
217
  }
216
- function resolveDeclaredTrailers(payload, declaredEnd, fecType, options, rapFlag = false) {
218
+ function resolveDeclaredTrailers(payload, declaredEnd, fecType, options) {
217
219
  if (declaredEnd > payload.length)
218
220
  return null;
219
221
  if (options?.skipFecId) {
220
- return isEmptyOrExactLegacyTrailer(payload, declaredEnd, payload.length) ? { hasFecId: false, hasOverlayFecId: false } : null;
222
+ return isEmptyOrExactLegacyTrailer(payload, declaredEnd, payload.length) ? { hasFecId: false } : null;
221
223
  }
222
- const overlay = options?.overlayDeclared === true && rapFlag;
223
224
  if (fecType === 1) {
224
- const fecStart = payload.length - sourceFecTrailerSize(overlay);
225
+ const fecStart = payload.length - SOURCE_FEC_PAYLOAD_ID_SIZE;
225
226
  if (fecStart < declaredEnd || !isEmptyOrExactLegacyTrailer(payload, declaredEnd, fecStart)) {
226
227
  return null;
227
228
  }
228
- return { hasFecId: true, hasOverlayFecId: overlay };
229
+ return { hasFecId: true };
229
230
  }
230
- if (fecType !== 0 || overlay)
231
+ if (fecType !== 0)
231
232
  return null;
232
233
  if (isEmptyOrExactLegacyTrailer(payload, declaredEnd, payload.length)) {
233
- return { hasFecId: false, hasOverlayFecId: false };
234
+ return { hasFecId: false };
234
235
  }
235
236
  return null;
236
237
  }
237
- function sourceFecTrailerSize(overlay) {
238
- return overlay ? 2 * SOURCE_FEC_PAYLOAD_ID_SIZE : SOURCE_FEC_PAYLOAD_ID_SIZE;
239
- }
240
238
  function planMfuExtraction(mmtpPacket, options, parsedHeader) {
241
239
  if (mmtpPacket.length < MMTP_HEADER_SIZE + MPU_HEADER_SIZE) {
242
240
  return {
@@ -289,11 +287,11 @@ var BlockcastMmt = (() => {
289
287
  if ("rejection" in layoutResult)
290
288
  return layoutResult;
291
289
  const { layout } = layoutResult;
292
- const trailerLayout = resolveDeclaredTrailers(payload, layout.dataEnd, header.fecType, options, header.rapFlag);
290
+ const trailerLayout = resolveDeclaredTrailers(payload, layout.dataEnd, header.fecType, options);
293
291
  if (!trailerLayout) {
294
292
  if (!options?.skipFecId && header.fecType === 1) {
295
293
  const observed = payload.length - layout.dataEnd;
296
- const expected = sourceFecTrailerSize(options?.overlayDeclared === true && header.rapFlag);
294
+ const expected = SOURCE_FEC_PAYLOAD_ID_SIZE;
297
295
  if (observed !== expected) {
298
296
  return {
299
297
  rejection: {
@@ -325,13 +323,10 @@ var BlockcastMmt = (() => {
325
323
  const mfuDuHeader = mfuDuHeaderPresent && mpu.timedFlag === 1 ? parseTimedMfuDuHeader(payload, MPU_HEADER_SIZE) : null;
326
324
  const itemId = mfuDuHeaderPresent && mpu.timedFlag === 0 ? new DataView(payload.buffer, payload.byteOffset + MPU_HEADER_SIZE, 4).getUint32(0, false) : null;
327
325
  const data = payload.subarray(mfuOffset, dataEnd);
328
- const trailerStart = mmtpPacket.length - sourceFecTrailerSize(trailerLayout.hasOverlayFecId);
329
- const ssIdAt = (offset) => ({
330
- ssId: new DataView(mmtpPacket.buffer, mmtpPacket.byteOffset + offset, 4).getUint32(0, false)
331
- });
332
- const fecPayloadId = trailerLayout.hasFecId ? ssIdAt(trailerStart) : null;
333
- const overlayFecPayloadId = trailerLayout.hasOverlayFecId ? ssIdAt(trailerStart + SOURCE_FEC_PAYLOAD_ID_SIZE) : null;
334
- return { header, mpu, fragmentType, data, mfuDuHeader, itemId, fecPayloadId, overlayFecPayloadId };
326
+ const fecPayloadId = trailerLayout.hasFecId ? {
327
+ ssId: new DataView(mmtpPacket.buffer, mmtpPacket.byteOffset + mmtpPacket.length - SOURCE_FEC_PAYLOAD_ID_SIZE, 4).getUint32(0, false)
328
+ } : null;
329
+ return { header, mpu, fragmentType, data, mfuDuHeader, itemId, fecPayloadId };
335
330
  }
336
331
  function sourceFecPayloadIdOf(mmtpPacket, header) {
337
332
  if (mmtpPacket.length < MMTP_HEADER_SIZE + MPU_HEADER_SIZE)
@@ -423,6 +418,50 @@ var BlockcastMmt = (() => {
423
418
  var NTP_SHORT_MODULUS = 2 ** NTP_SHORT_BITS;
424
419
  var NTP_SHORT_HALF_RANGE = 2 ** (NTP_SHORT_BITS - 1);
425
420
 
421
+ // ../mmt-base/dist/signalling.js
422
+ var FEC_CODING_STRUCTURE_ONE_STAGE = 1;
423
+ var SSBG_MODE_1 = 1;
424
+ var RAPTORQ_MAX_SOURCE_SYMBOLS = 56403;
425
+ var REPAIR_SYMBOL_ID_MAX = 16777215;
426
+ var MIN_REPAIR_SYMBOL_SIZE = 14;
427
+ var REED_SOLOMON_MAX_BLOCK = 255;
428
+ function alFecRaptorQFlowConfig(flow) {
429
+ if (flow.fecCodingStructure !== FEC_CODING_STRUCTURE_ONE_STAGE || flow.repairFlows.length !== 1)
430
+ return null;
431
+ if (flow.ssbgMode !== SSBG_MODE_1 || flow.ffsrptsFlag !== 0 || flow.fecPayloadIdMode !== 0)
432
+ return null;
433
+ if (flow.packetIds.length !== 1)
434
+ return null;
435
+ const repair = flow.repairFlows[0];
436
+ const { fecCodeId, maximumK: k, maximumP: p } = repair;
437
+ const t = flow.repairSymbolSize;
438
+ if (t < MIN_REPAIR_SYMBOL_SIZE || k < 1 || p < 1 || k + p - 1 > REPAIR_SYMBOL_ID_MAX)
439
+ return null;
440
+ if (fecCodeId === FEC_CODE_RAPTORQ) {
441
+ if (t % 8 !== 0 || k > RAPTORQ_MAX_SOURCE_SYMBOLS)
442
+ return null;
443
+ } else if (fecCodeId === FEC_CODE_REED_SOLOMON) {
444
+ if (k + p > REED_SOLOMON_MAX_BLOCK)
445
+ return null;
446
+ } else {
447
+ return null;
448
+ }
449
+ if (repair.repairFlowId === MMTP_SIGNALING_PACKET_ID || repair.repairFlowId === flow.packetIds[0])
450
+ return null;
451
+ return {
452
+ sourcePacketIds: [...flow.packetIds],
453
+ repairFlowId: repair.repairFlowId,
454
+ fecCodeId,
455
+ ssbgMode: flow.ssbgMode,
456
+ fecPayloadIdMode: flow.fecPayloadIdMode,
457
+ repairSymbolSize: t,
458
+ maximumK: k,
459
+ maximumP: p,
460
+ protectionWindowTime: repair.protectionWindowTime,
461
+ protectionWindowSize: repair.protectionWindowSize
462
+ };
463
+ }
464
+
426
465
  // ../mmt-base/dist/serial-number.js
427
466
  var SERIAL32_SPAN = 4294967296;
428
467
  var SERIAL32_HALF = 2147483648;
@@ -8217,7 +8256,7 @@ var BlockcastMmt = (() => {
8217
8256
  // src/mmtp-fec-session.ts
8218
8257
  var PATH_BITS = { moq: 1, multicast: 2 };
8219
8258
  var MIXED_PATHS = PATH_BITS.moq | PATH_BITS.multicast;
8220
- var _decoder, _blocks, _settled, _have, _newest, _prunedAt, _repairPacketId, _newestSource, _heldLayers, _anchorHorizon, _stats3, _MmtpFecSession_instances, drop_fn2, noteNewest_fn, blockStartOf_fn, countHeld_fn, noteMixed_fn, behindHorizon_fn, settle_fn, settleClean_fn, settleDecoded_fn, retireBehindHorizon_fn;
8259
+ var _decoder, _repairSymbols, _message, _blocks, _settled, _have, _newest, _prunedAt, _repairPacketId, _newestSource, _heldLayers, _anchorHorizon, _stats3, _MmtpFecSession_instances, trackFor_fn, drop_fn2, noteNewest_fn, blockStartOf_fn, countHeld_fn, noteMixed_fn, behindHorizon_fn, settle_fn, settleClean_fn, settleDecoded_fn, retireBehindHorizon_fn;
8221
8260
  var MmtpFecSession = class {
8222
8261
  /**
8223
8262
  * @throws when the instance lacks or misstates a field the session needs:
@@ -8227,6 +8266,9 @@ var BlockcastMmt = (() => {
8227
8266
  constructor(config) {
8228
8267
  __privateAdd(this, _MmtpFecSession_instances);
8229
8268
  __privateAdd(this, _decoder);
8269
+ __privateAdd(this, _repairSymbols);
8270
+ /** The message entry a catalog-less session was built from; undefined under a catalog. */
8271
+ __privateAdd(this, _message);
8230
8272
  /** Blocks a repair has defined and that are not settled yet, by SS_Start. */
8231
8273
  __privateAdd(this, _blocks, /* @__PURE__ */ new Map());
8232
8274
  /** SS_Starts of settled blocks, so a later repair for one is refused. */
@@ -8258,6 +8300,24 @@ var BlockcastMmt = (() => {
8258
8300
  mixedPath: { clean: 0, recovered: 0, failed: 0, released: 0 },
8259
8301
  failClosed: null
8260
8302
  });
8303
+ if (config && "alFecFlow" in config) {
8304
+ const message = alFecRaptorQFlowConfig(config.alFecFlow);
8305
+ if (!message) {
8306
+ throw new Error("MmtpFecSession: the AL-FEC entry is outside draft-ramadan-moq-mmt-02 8.3 (iso-ssbg1-v1)");
8307
+ }
8308
+ __privateSet(this, _message, message);
8309
+ this.blockProfile = parseFecBlockProfile("iso-ssbg1-v1", "alFecFlow");
8310
+ this.algorithm = message.fecCodeId === FEC_CODE_RAPTORQ ? "raptorq" : "reed-solomon";
8311
+ this.sourceSymbols = message.maximumK;
8312
+ __privateSet(this, _repairSymbols, message.maximumP);
8313
+ this.symbolSize = message.repairSymbolSize;
8314
+ this.retentionBlocks = requirePositiveInteger(config.retentionBlocks, "retentionBlocks");
8315
+ __privateSet(this, _repairPacketId, message.repairFlowId);
8316
+ __privateSet(this, _heldLayers, layerZero(message.maximumP));
8317
+ __privateSet(this, _anchorHorizon, heldLayerAnchorHorizon(this.retentionBlocks, this.sourceSymbols));
8318
+ __privateSet(this, _decoder, buildDecoder(config.Decoder, this));
8319
+ return;
8320
+ }
8261
8321
  const fec = config?.fec;
8262
8322
  if (!fec || typeof fec !== "object") {
8263
8323
  throw new Error("MmtpFecSession: the catalog fec instance is required");
@@ -8268,7 +8328,7 @@ var BlockcastMmt = (() => {
8268
8328
  }
8269
8329
  this.algorithm = fec.scheme;
8270
8330
  this.sourceSymbols = requirePositiveInteger(fec.sourceSymbols, "fec.sourceSymbols");
8271
- this.repairSymbols = requirePositiveInteger(fec.repairSymbols, "fec.repairSymbols");
8331
+ __privateSet(this, _repairSymbols, requirePositiveInteger(fec.repairSymbols, "fec.repairSymbols"));
8272
8332
  this.symbolSize = requirePositiveInteger(fec.symbolSize, "fec.symbolSize");
8273
8333
  const interleaveDepthMs = requireNumber(fec.interleaveDepthMs, "fec.interleaveDepthMs");
8274
8334
  const reorderToleranceMs = requireNumber(fec.reorderToleranceMs, "fec.reorderToleranceMs");
@@ -8288,17 +8348,34 @@ var BlockcastMmt = (() => {
8288
8348
  config.heldLayers
8289
8349
  ));
8290
8350
  __privateSet(this, _anchorHorizon, heldLayerAnchorHorizon(this.retentionBlocks, this.sourceSymbols));
8291
- if (typeof config.Decoder !== "function") {
8292
- throw new Error("MmtpFecSession: a decoder constructor (wasm_bindgen.MmtFecDecoder) is required");
8293
- }
8294
- const decoder = new config.Decoder(this.retentionBlocks, this.blockProfile, this.algorithm);
8295
- try {
8296
- decoder.configure_params(this.sourceSymbols, this.symbolSize);
8297
- } catch (error) {
8298
- decoder.free();
8299
- throw error;
8300
- }
8301
- __privateSet(this, _decoder, decoder);
8351
+ __privateSet(this, _decoder, buildDecoder(config.Decoder, this));
8352
+ }
8353
+ /** P_0 = B_0, the held total every repair packet is checked against. */
8354
+ get repairSymbols() {
8355
+ return __privateGet(this, _repairSymbols);
8356
+ }
8357
+ /**
8358
+ * A later version of the AL-FEC message, catalog-less sessions only. A
8359
+ * maximum_p_for_repair_flow above the held total raises it (`raised`): the
8360
+ * anchored check restarts at the new total, so a block generated under the
8361
+ * previous one stays valid before the first block carrying the new total
8362
+ * (fec-core-00 7.3.2, mmt-02 8.3). A field at or below it changes nothing
8363
+ * (`unchanged`): no publisher lowers it, and the check catches a wire that
8364
+ * does. An entry with another asset, repair flow, code point, K or T, or
8365
+ * one outside 8.3, is `refused`: that is a new source encoding, which needs
8366
+ * a new session. A latched fail-closed stays latched either way.
8367
+ */
8368
+ updateAlFecFlow(flow) {
8369
+ const held = __privateGet(this, _message);
8370
+ if (!held) throw new Error("MmtpFecSession: updateAlFecFlow() needs a session built from an AL-FEC entry");
8371
+ const next = alFecRaptorQFlowConfig(flow);
8372
+ if (!next || next.sourcePacketIds[0] !== held.sourcePacketIds[0] || next.repairFlowId !== held.repairFlowId || next.fecCodeId !== held.fecCodeId || next.maximumK !== held.maximumK || next.repairSymbolSize !== held.repairSymbolSize) {
8373
+ return "refused";
8374
+ }
8375
+ if (next.maximumP <= __privateGet(this, _repairSymbols)) return "unchanged";
8376
+ __privateSet(this, _repairSymbols, next.maximumP);
8377
+ __privateSet(this, _heldLayers, layerZero(next.maximumP));
8378
+ return "raised";
8302
8379
  }
8303
8380
  /** True once release() ran. */
8304
8381
  get released() {
@@ -8393,7 +8470,7 @@ var BlockcastMmt = (() => {
8393
8470
  }
8394
8471
  if (admission.reject) {
8395
8472
  const verdict2 = __privateGet(this, _heldLayers).failsClosed(
8396
- repairTrackName,
8473
+ __privateMethod(this, _MmtpFecSession_instances, trackFor_fn).call(this, repairTrackName),
8397
8474
  frame.ssStart,
8398
8475
  frame.rsbLength,
8399
8476
  __privateGet(this, _anchorHorizon),
@@ -8408,7 +8485,7 @@ var BlockcastMmt = (() => {
8408
8485
  __privateGet(this, _repairPacketId) ?? __privateSet(this, _repairPacketId, frame.packetId);
8409
8486
  if (frame.ssbLength > this.sourceSymbols) return __privateMethod(this, _MmtpFecSession_instances, drop_fn2).call(this, "repair:ssb-length");
8410
8487
  const verdict = __privateGet(this, _heldLayers).admit(
8411
- repairTrackName,
8488
+ __privateMethod(this, _MmtpFecSession_instances, trackFor_fn).call(this, repairTrackName),
8412
8489
  frame.ssStart,
8413
8490
  frame.rsbLength,
8414
8491
  __privateGet(this, _anchorHorizon),
@@ -8469,6 +8546,8 @@ var BlockcastMmt = (() => {
8469
8546
  }
8470
8547
  };
8471
8548
  _decoder = new WeakMap();
8549
+ _repairSymbols = new WeakMap();
8550
+ _message = new WeakMap();
8472
8551
  _blocks = new WeakMap();
8473
8552
  _settled = new WeakMap();
8474
8553
  _have = new WeakMap();
@@ -8480,6 +8559,10 @@ var BlockcastMmt = (() => {
8480
8559
  _anchorHorizon = new WeakMap();
8481
8560
  _stats3 = new WeakMap();
8482
8561
  _MmtpFecSession_instances = new WeakSet();
8562
+ /** Catalog-less: layer 0 has no own-track clause (mmt-02 8.3), so no track name is judged. */
8563
+ trackFor_fn = function(repairTrackName) {
8564
+ return __privateGet(this, _message) ? void 0 : repairTrackName;
8565
+ };
8483
8566
  drop_fn2 = function(reason) {
8484
8567
  __privateGet(this, _stats3).dropped[reason] = (__privateGet(this, _stats3).dropped[reason] ?? 0) + 1;
8485
8568
  return [];
@@ -8600,6 +8683,22 @@ var BlockcastMmt = (() => {
8600
8683
  if (__privateMethod(this, _MmtpFecSession_instances, behindHorizon_fn).call(this, start)) __privateGet(this, _settled).delete(start);
8601
8684
  }
8602
8685
  };
8686
+ function layerZero(total) {
8687
+ return new HeldLayerAdmission([{ layer: 0, repairTrackName: "", rsIdOffset: 0, repairSymbols: total }]);
8688
+ }
8689
+ function buildDecoder(Decoder, session) {
8690
+ if (typeof Decoder !== "function") {
8691
+ throw new Error("MmtpFecSession: a decoder constructor (wasm_bindgen.MmtFecDecoder) is required");
8692
+ }
8693
+ const decoder = new Decoder(session.retentionBlocks, session.blockProfile, session.algorithm);
8694
+ try {
8695
+ decoder.configure_params(session.sourceSymbols, session.symbolSize);
8696
+ } catch (error) {
8697
+ decoder.free();
8698
+ throw error;
8699
+ }
8700
+ return decoder;
8701
+ }
8603
8702
  function requirePositiveInteger(value, path) {
8604
8703
  if (typeof value !== "number" || !Number.isSafeInteger(value) || value <= 0) {
8605
8704
  throw new Error(`MmtpFecSession: ${path} must be a positive integer (got ${String(value)})`);