@blockcast/mmt-container 2.1.0 → 2.2.0-main.51d1358fdf4c

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.
@@ -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`);
@@ -360,6 +362,50 @@ var NTP_SHORT_BITS = 32;
360
362
  var NTP_SHORT_MODULUS = 2 ** NTP_SHORT_BITS;
361
363
  var NTP_SHORT_HALF_RANGE = 2 ** (NTP_SHORT_BITS - 1);
362
364
 
365
+ // ../mmt-base/dist/signalling.js
366
+ var FEC_CODING_STRUCTURE_ONE_STAGE = 1;
367
+ var SSBG_MODE_1 = 1;
368
+ var RAPTORQ_MAX_SOURCE_SYMBOLS = 56403;
369
+ var REPAIR_SYMBOL_ID_MAX = 16777215;
370
+ var MIN_REPAIR_SYMBOL_SIZE = 14;
371
+ var REED_SOLOMON_MAX_BLOCK = 255;
372
+ function alFecRaptorQFlowConfig(flow) {
373
+ if (flow.fecCodingStructure !== FEC_CODING_STRUCTURE_ONE_STAGE || flow.repairFlows.length !== 1)
374
+ return null;
375
+ if (flow.ssbgMode !== SSBG_MODE_1 || flow.ffsrptsFlag !== 0 || flow.fecPayloadIdMode !== 0)
376
+ return null;
377
+ if (flow.packetIds.length !== 1)
378
+ return null;
379
+ const repair = flow.repairFlows[0];
380
+ const { fecCodeId, maximumK: k, maximumP: p } = repair;
381
+ const t = flow.repairSymbolSize;
382
+ if (t < MIN_REPAIR_SYMBOL_SIZE || k < 1 || p < 1 || k + p - 1 > REPAIR_SYMBOL_ID_MAX)
383
+ return null;
384
+ if (fecCodeId === FEC_CODE_RAPTORQ) {
385
+ if (t % 8 !== 0 || k > RAPTORQ_MAX_SOURCE_SYMBOLS)
386
+ return null;
387
+ } else if (fecCodeId === FEC_CODE_REED_SOLOMON) {
388
+ if (k + p > REED_SOLOMON_MAX_BLOCK)
389
+ return null;
390
+ } else {
391
+ return null;
392
+ }
393
+ if (repair.repairFlowId === MMTP_SIGNALING_PACKET_ID || repair.repairFlowId === flow.packetIds[0])
394
+ return null;
395
+ return {
396
+ sourcePacketIds: [...flow.packetIds],
397
+ repairFlowId: repair.repairFlowId,
398
+ fecCodeId,
399
+ ssbgMode: flow.ssbgMode,
400
+ fecPayloadIdMode: flow.fecPayloadIdMode,
401
+ repairSymbolSize: t,
402
+ maximumK: k,
403
+ maximumP: p,
404
+ protectionWindowTime: repair.protectionWindowTime,
405
+ protectionWindowSize: repair.protectionWindowSize
406
+ };
407
+ }
408
+
363
409
  // ../mmt-base/dist/serial-number.js
364
410
  var SERIAL32_SPAN = 4294967296;
365
411
  var SERIAL32_HALF = 2147483648;
@@ -8071,7 +8117,14 @@ var HeldLayerAdmission = class {
8071
8117
  var PATH_BITS = { moq: 1, multicast: 2 };
8072
8118
  var MIXED_PATHS = PATH_BITS.moq | PATH_BITS.multicast;
8073
8119
  var MmtpFecSession = class {
8120
+ /** P_0 = B_0, the held total every repair packet is checked against. */
8121
+ get repairSymbols() {
8122
+ return this.#repairSymbols;
8123
+ }
8074
8124
  #decoder;
8125
+ #repairSymbols;
8126
+ /** The message entry a catalog-less session was built from; undefined under a catalog. */
8127
+ #message;
8075
8128
  /** Blocks a repair has defined and that are not settled yet, by SS_Start. */
8076
8129
  #blocks = /* @__PURE__ */ new Map();
8077
8130
  /** SS_Starts of settled blocks, so a later repair for one is refused. */
@@ -8109,6 +8162,24 @@ var MmtpFecSession = class {
8109
8162
  * `interleaveDepthMs` or `reorderToleranceMs`. None is defaulted.
8110
8163
  */
8111
8164
  constructor(config) {
8165
+ if (config && "alFecFlow" in config) {
8166
+ const message = alFecRaptorQFlowConfig(config.alFecFlow);
8167
+ if (!message) {
8168
+ throw new Error("MmtpFecSession: the AL-FEC entry is outside draft-ramadan-moq-mmt-02 8.3 (iso-ssbg1-v1)");
8169
+ }
8170
+ this.#message = message;
8171
+ this.blockProfile = parseFecBlockProfile("iso-ssbg1-v1", "alFecFlow");
8172
+ this.algorithm = message.fecCodeId === FEC_CODE_RAPTORQ ? "raptorq" : "reed-solomon";
8173
+ this.sourceSymbols = message.maximumK;
8174
+ this.#repairSymbols = message.maximumP;
8175
+ this.symbolSize = message.repairSymbolSize;
8176
+ this.retentionBlocks = requirePositiveInteger(config.retentionBlocks, "retentionBlocks");
8177
+ this.#repairPacketId = message.repairFlowId;
8178
+ this.#heldLayers = layerZero(message.maximumP);
8179
+ this.#anchorHorizon = heldLayerAnchorHorizon(this.retentionBlocks, this.sourceSymbols);
8180
+ this.#decoder = buildDecoder(config.Decoder, this);
8181
+ return;
8182
+ }
8112
8183
  const fec = config?.fec;
8113
8184
  if (!fec || typeof fec !== "object") {
8114
8185
  throw new Error("MmtpFecSession: the catalog fec instance is required");
@@ -8119,7 +8190,7 @@ var MmtpFecSession = class {
8119
8190
  }
8120
8191
  this.algorithm = fec.scheme;
8121
8192
  this.sourceSymbols = requirePositiveInteger(fec.sourceSymbols, "fec.sourceSymbols");
8122
- this.repairSymbols = requirePositiveInteger(fec.repairSymbols, "fec.repairSymbols");
8193
+ this.#repairSymbols = requirePositiveInteger(fec.repairSymbols, "fec.repairSymbols");
8123
8194
  this.symbolSize = requirePositiveInteger(fec.symbolSize, "fec.symbolSize");
8124
8195
  const interleaveDepthMs = requireNumber(fec.interleaveDepthMs, "fec.interleaveDepthMs");
8125
8196
  const reorderToleranceMs = requireNumber(fec.reorderToleranceMs, "fec.reorderToleranceMs");
@@ -8139,17 +8210,30 @@ var MmtpFecSession = class {
8139
8210
  config.heldLayers
8140
8211
  );
8141
8212
  this.#anchorHorizon = heldLayerAnchorHorizon(this.retentionBlocks, this.sourceSymbols);
8142
- if (typeof config.Decoder !== "function") {
8143
- throw new Error("MmtpFecSession: a decoder constructor (wasm_bindgen.MmtFecDecoder) is required");
8144
- }
8145
- const decoder = new config.Decoder(this.retentionBlocks, this.blockProfile, this.algorithm);
8146
- try {
8147
- decoder.configure_params(this.sourceSymbols, this.symbolSize);
8148
- } catch (error) {
8149
- decoder.free();
8150
- throw error;
8151
- }
8152
- this.#decoder = decoder;
8213
+ this.#decoder = buildDecoder(config.Decoder, this);
8214
+ }
8215
+ /**
8216
+ * A later version of the AL-FEC message, catalog-less sessions only. A
8217
+ * maximum_p_for_repair_flow above the held total raises it (`raised`): the
8218
+ * anchored check restarts at the new total, so a block generated under the
8219
+ * previous one stays valid before the first block carrying the new total
8220
+ * (fec-core-00 7.3.2, mmt-02 8.3). A field at or below it changes nothing
8221
+ * (`unchanged`): no publisher lowers it, and the check catches a wire that
8222
+ * does. An entry with another asset, repair flow, code point, K or T, or
8223
+ * one outside 8.3, is `refused`: that is a new source encoding, which needs
8224
+ * a new session. A latched fail-closed stays latched either way.
8225
+ */
8226
+ updateAlFecFlow(flow) {
8227
+ const held = this.#message;
8228
+ if (!held) throw new Error("MmtpFecSession: updateAlFecFlow() needs a session built from an AL-FEC entry");
8229
+ const next = alFecRaptorQFlowConfig(flow);
8230
+ if (!next || next.sourcePacketIds[0] !== held.sourcePacketIds[0] || next.repairFlowId !== held.repairFlowId || next.fecCodeId !== held.fecCodeId || next.maximumK !== held.maximumK || next.repairSymbolSize !== held.repairSymbolSize) {
8231
+ return "refused";
8232
+ }
8233
+ if (next.maximumP <= this.#repairSymbols) return "unchanged";
8234
+ this.#repairSymbols = next.maximumP;
8235
+ this.#heldLayers = layerZero(next.maximumP);
8236
+ return "raised";
8153
8237
  }
8154
8238
  /** True once release() ran. */
8155
8239
  get released() {
@@ -8244,7 +8328,7 @@ var MmtpFecSession = class {
8244
8328
  }
8245
8329
  if (admission.reject) {
8246
8330
  const verdict2 = this.#heldLayers.failsClosed(
8247
- repairTrackName,
8331
+ this.#trackFor(repairTrackName),
8248
8332
  frame.ssStart,
8249
8333
  frame.rsbLength,
8250
8334
  this.#anchorHorizon,
@@ -8259,7 +8343,7 @@ var MmtpFecSession = class {
8259
8343
  this.#repairPacketId ??= frame.packetId;
8260
8344
  if (frame.ssbLength > this.sourceSymbols) return this.#drop("repair:ssb-length");
8261
8345
  const verdict = this.#heldLayers.admit(
8262
- repairTrackName,
8346
+ this.#trackFor(repairTrackName),
8263
8347
  frame.ssStart,
8264
8348
  frame.rsbLength,
8265
8349
  this.#anchorHorizon,
@@ -8318,6 +8402,10 @@ var MmtpFecSession = class {
8318
8402
  decoder.free();
8319
8403
  return true;
8320
8404
  }
8405
+ /** Catalog-less: layer 0 has no own-track clause (mmt-02 8.3), so no track name is judged. */
8406
+ #trackFor(repairTrackName) {
8407
+ return this.#message ? void 0 : repairTrackName;
8408
+ }
8321
8409
  #drop(reason) {
8322
8410
  this.#stats.dropped[reason] = (this.#stats.dropped[reason] ?? 0) + 1;
8323
8411
  return [];
@@ -8439,6 +8527,22 @@ var MmtpFecSession = class {
8439
8527
  }
8440
8528
  }
8441
8529
  };
8530
+ function layerZero(total) {
8531
+ return new HeldLayerAdmission([{ layer: 0, repairTrackName: "", rsIdOffset: 0, repairSymbols: total }]);
8532
+ }
8533
+ function buildDecoder(Decoder, session) {
8534
+ if (typeof Decoder !== "function") {
8535
+ throw new Error("MmtpFecSession: a decoder constructor (wasm_bindgen.MmtFecDecoder) is required");
8536
+ }
8537
+ const decoder = new Decoder(session.retentionBlocks, session.blockProfile, session.algorithm);
8538
+ try {
8539
+ decoder.configure_params(session.sourceSymbols, session.symbolSize);
8540
+ } catch (error) {
8541
+ decoder.free();
8542
+ throw error;
8543
+ }
8544
+ return decoder;
8545
+ }
8442
8546
  function requirePositiveInteger(value, path) {
8443
8547
  if (typeof value !== "number" || !Number.isSafeInteger(value) || value <= 0) {
8444
8548
  throw new Error(`MmtpFecSession: ${path} must be a positive integer (got ${String(value)})`);