@blockcast/mmt-container 0.1.0-main.cde7600fad6a → 0.1.0-main.ce3c8aeb7f68

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 (68) hide show
  1. package/dist/browser-runtime.js +276 -178
  2. package/dist/browser-runtime.js.map +3 -3
  3. package/dist/carp-catalog.js.map +1 -1
  4. package/dist/catalog-fec-params.js.map +1 -1
  5. package/dist/fec-manager.d.ts.map +1 -1
  6. package/dist/fec-manager.js +35 -2
  7. package/dist/fec-manager.js.map +2 -2
  8. package/dist/fec-worker-setup.js +22 -1
  9. package/dist/fec-worker-setup.js.map +2 -2
  10. package/dist/index.d.ts +1 -1
  11. package/dist/index.d.ts.map +1 -1
  12. package/dist/index.js.map +2 -2
  13. package/dist/mfu-reassembler.js +17 -11
  14. package/dist/mfu-reassembler.js.map +2 -2
  15. package/dist/mmtp-container-parser.d.ts +28 -0
  16. package/dist/mmtp-container-parser.d.ts.map +1 -1
  17. package/dist/mmtp-container-parser.js +75 -49
  18. package/dist/mmtp-container-parser.js.map +3 -3
  19. package/dist/mmtp-container-parser.no-modules.js +85 -59
  20. package/dist/mmtp-container-parser.no-modules.js.map +3 -3
  21. package/dist/mmtp-decode-pipeline.js +75 -49
  22. package/dist/mmtp-decode-pipeline.js.map +3 -3
  23. package/dist/mmtp-router.d.ts.map +1 -1
  24. package/dist/mmtp-router.js +218 -159
  25. package/dist/mmtp-router.js.map +3 -3
  26. package/dist/multicast-input.js.map +1 -1
  27. package/dist/multicast-manager.js +276 -165
  28. package/dist/multicast-manager.js.map +3 -3
  29. package/dist/multicast-transport.js +17 -54
  30. package/dist/multicast-transport.js.map +2 -2
  31. package/dist/multicast-unwrap.js +53 -30
  32. package/dist/multicast-unwrap.js.map +2 -2
  33. package/dist/no-modules.js +75 -49
  34. package/dist/no-modules.js.map +3 -3
  35. package/dist/signaling.js.map +2 -2
  36. package/dist/timestamp-codec.js +17 -11
  37. package/dist/timestamp-codec.js.map +2 -2
  38. package/dist/wasm-mfu-reassembler.js.map +1 -1
  39. package/dist/wasm-mmtp-container-parser.js +75 -49
  40. package/dist/wasm-mmtp-container-parser.js.map +3 -3
  41. package/node_modules/@blockcast/fec-worker/README.md +5 -1
  42. package/node_modules/@blockcast/fec-worker/dist/fec-worker-client.d.ts +6 -1
  43. package/node_modules/@blockcast/fec-worker/dist/fec-worker-client.d.ts.map +1 -1
  44. package/node_modules/@blockcast/fec-worker/dist/fec-worker-client.js +25 -1
  45. package/node_modules/@blockcast/fec-worker/dist/fec-worker-client.js.map +1 -1
  46. package/node_modules/@blockcast/fec-worker/dist/fec-worker-types.d.ts +10 -1
  47. package/node_modules/@blockcast/fec-worker/dist/fec-worker-types.d.ts.map +1 -1
  48. package/node_modules/@blockcast/fec-worker/dist/fec-worker.js +6 -1
  49. package/node_modules/@blockcast/fec-worker/dist/fec-worker.js.map +2 -2
  50. package/node_modules/@blockcast/fec-worker/src/fec-worker-client.ts +29 -1
  51. package/node_modules/@blockcast/fec-worker/src/fec-worker-types.ts +8 -1
  52. package/node_modules/@blockcast/fec-worker/src/fec-worker.ts +7 -1
  53. package/node_modules/@blockcast/mmt-base/dist/mmtp.d.ts +42 -0
  54. package/node_modules/@blockcast/mmt-base/dist/mmtp.d.ts.map +1 -1
  55. package/node_modules/@blockcast/mmt-base/dist/mmtp.js +132 -37
  56. package/node_modules/@blockcast/mmt-base/dist/mmtp.js.map +1 -1
  57. package/node_modules/@blockcast/mmt-base/src/mmtp.ts +146 -37
  58. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/mmtp.d.ts +42 -0
  59. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/mmtp.d.ts.map +1 -1
  60. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/mmtp.js +132 -37
  61. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/mmtp.js.map +1 -1
  62. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/src/mmtp.ts +146 -37
  63. package/package.json +7 -7
  64. package/src/fec-manager.ts +35 -2
  65. package/src/index.ts +1 -0
  66. package/src/mmtp-container-parser.ts +78 -21
  67. package/src/mmtp-router.ts +147 -121
  68. package/src/multicast-transport.ts +7 -7
@@ -63,6 +63,11 @@ var init_fec_worker_client = __esm({
63
63
  #flushQueued = false;
64
64
  // Batching starts only once the Worker announced it accepts feedSymbols.
65
65
  #batchSymbols = false;
66
+ // The Worker posts blockUpdate events only to a client that subscribed,
67
+ // which this client does once a callback is registered and the Worker
68
+ // announced the command (BLO-36599).
69
+ #blockUpdatesAnnounced = false;
70
+ #blockUpdatesSubscribed = false;
66
71
  /**
67
72
  * Create a FecWorkerClient.
68
73
  * @param workerOrUrl - Either a Worker instance (for testing) or a URL to create one
@@ -225,9 +230,21 @@ var init_fec_worker_client = __esm({
225
230
  onSnapshot(cb) {
226
231
  this.#onSnapshot = cb;
227
232
  }
228
- /** Register callback for individual block state changes. */
233
+ /**
234
+ * Register callback for individual block state changes. The Worker posts
235
+ * them only once asked, so registering subscribes (after the Worker
236
+ * announces `subscribeBlockUpdates`). An older Worker that does not
237
+ * announce it posts them unasked.
238
+ */
229
239
  onBlockUpdate(cb) {
230
240
  this.#onBlockUpdate = cb;
241
+ this.#subscribeBlockUpdates();
242
+ }
243
+ #subscribeBlockUpdates() {
244
+ if (this.#disposed || this.#blockUpdatesSubscribed || !this.#blockUpdatesAnnounced || !this.#onBlockUpdate)
245
+ return;
246
+ this.#blockUpdatesSubscribed = true;
247
+ this.#worker.postMessage({ type: "subscribeBlockUpdates" });
231
248
  }
232
249
  /** Register callback for green-fill frames. */
233
250
  onGreenFill(cb) {
@@ -270,6 +287,10 @@ var init_fec_worker_client = __esm({
270
287
  if (Array.isArray(event.commands) && event.commands.includes("feedSymbols")) {
271
288
  this.#batchSymbols = true;
272
289
  }
290
+ if (Array.isArray(event.commands) && event.commands.includes("subscribeBlockUpdates")) {
291
+ this.#blockUpdatesAnnounced = true;
292
+ this.#subscribeBlockUpdates();
293
+ }
273
294
  return;
274
295
  }
275
296
  if (event.type === "resetComplete") {
@@ -616,6 +637,27 @@ function parseMMTPHeader(buffer) {
616
637
  rapFlag
617
638
  };
618
639
  }
640
+ function readMMTPPacketId(buffer) {
641
+ if (buffer.length < MMTP_HEADER_SIZE2)
642
+ return null;
643
+ const flags = buffer[0];
644
+ if ((flags >> 6 & 3) !== 0)
645
+ return null;
646
+ let offset = MMTP_HEADER_SIZE2;
647
+ if ((flags & 32) !== 0) {
648
+ if (offset + 4 > buffer.length)
649
+ return null;
650
+ offset += 4;
651
+ }
652
+ if ((flags & 2) !== 0) {
653
+ if (offset + 4 > buffer.length)
654
+ return null;
655
+ const length = buffer[offset + 2] << 8 | buffer[offset + 3];
656
+ if (offset + 4 + length > buffer.length)
657
+ return null;
658
+ }
659
+ return buffer[2] << 8 | buffer[3];
660
+ }
619
661
  function unwrapMMTP(buffer) {
620
662
  const header = parseMMTPHeader(buffer);
621
663
  const payload = buffer.subarray(header.headerLength);
@@ -729,7 +771,7 @@ function extractNALUnit(mmtpPacket) {
729
771
  throw new Error("Malformed MMTP MPU payload");
730
772
  return extraction.data;
731
773
  }
732
- function planMfuExtraction(mmtpPacket, options) {
774
+ function planMfuExtraction(mmtpPacket, options, parsedHeader) {
733
775
  if (mmtpPacket.length < MMTP_HEADER_SIZE2 + MPU_HEADER_SIZE2) {
734
776
  return {
735
777
  rejection: {
@@ -739,15 +781,19 @@ function planMfuExtraction(mmtpPacket, options) {
739
781
  };
740
782
  }
741
783
  let header;
742
- try {
743
- header = parseMMTPHeader(mmtpPacket);
744
- } catch (error) {
745
- return {
746
- rejection: {
747
- gate: "mmtp-header-unparseable",
748
- detail: error instanceof Error ? error.message : String(error)
749
- }
750
- };
784
+ if (parsedHeader) {
785
+ header = parsedHeader;
786
+ } else {
787
+ try {
788
+ header = parseMMTPHeader(mmtpPacket);
789
+ } catch (error) {
790
+ return {
791
+ rejection: {
792
+ gate: "mmtp-header-unparseable",
793
+ detail: error instanceof Error ? error.message : String(error)
794
+ }
795
+ };
796
+ }
751
797
  }
752
798
  if (header.packetType !== PACKET_TYPE_MPU2) {
753
799
  return {
@@ -800,7 +846,12 @@ function planMfuExtraction(mmtpPacket, options) {
800
846
  return { plan: { header, payload, mpu, fragmentType, layout, trailerLayout } };
801
847
  }
802
848
  function extractMFUPayload(mmtpPacket, options) {
803
- const result = planMfuExtraction(mmtpPacket, options);
849
+ return extractPlannedMfu(mmtpPacket, planMfuExtraction(mmtpPacket, options));
850
+ }
851
+ function extractMFUPayloadWithHeader(mmtpPacket, header, options) {
852
+ return extractPlannedMfu(mmtpPacket, planMfuExtraction(mmtpPacket, options, header));
853
+ }
854
+ function extractPlannedMfu(mmtpPacket, result) {
804
855
  if ("rejection" in result)
805
856
  return null;
806
857
  const { header, payload, mpu, fragmentType, layout, trailerLayout } = result.plan;
@@ -825,51 +876,59 @@ function isFecRepairPacket(mmtpPacket) {
825
876
  const fecType = mmtpPacket[0] >> 3 & 3;
826
877
  return fecType >= 2;
827
878
  }
828
- function extractFecPayloadId(mmtpPacket) {
829
- if (!hasSourceFecPayloadId(mmtpPacket))
830
- return null;
831
- const off = mmtpPacket.length - SOURCE_FEC_PAYLOAD_ID_SIZE;
832
- const view2 = new DataView(mmtpPacket.buffer, mmtpPacket.byteOffset + off, SOURCE_FEC_PAYLOAD_ID_SIZE);
833
- return { ssId: view2.getUint32(0, false) };
834
- }
835
879
  function hasSourceFecPayloadId(mmtpPacket) {
880
+ const header = parseHeaderForSourceFecId(mmtpPacket);
881
+ return header !== null && sourceFecPayloadIdOf(mmtpPacket, header) !== null;
882
+ }
883
+ function parseHeaderForSourceFecId(mmtpPacket) {
836
884
  if (mmtpPacket.length < MMTP_HEADER_SIZE2 + MPU_HEADER_SIZE2)
837
- return false;
838
- let header;
885
+ return null;
839
886
  try {
840
- header = parseMMTPHeader(mmtpPacket);
887
+ return parseMMTPHeader(mmtpPacket);
841
888
  } catch {
842
- return false;
889
+ return null;
843
890
  }
891
+ }
892
+ function sourceFecPayloadIdOf(mmtpPacket, header) {
893
+ if (mmtpPacket.length < MMTP_HEADER_SIZE2 + MPU_HEADER_SIZE2)
894
+ return null;
844
895
  if (header.packetType !== PACKET_TYPE_MPU2)
845
- return false;
896
+ return null;
846
897
  const payload = mmtpPacket.subarray(header.headerLength);
847
898
  if (payload.length < MPU_HEADER_SIZE2)
848
- return false;
849
- const mpu = parseMPUHeader(payload);
850
- if (mpu.payloadLength < MPU_HEADER_REMAINDER_SIZE2)
851
- return false;
852
- const declaredEnd = MPU_LENGTH_FIELD_SIZE2 + mpu.payloadLength;
853
- return resolveDeclaredTrailers(payload, declaredEnd, header.fecType)?.hasFecId ?? false;
899
+ return null;
900
+ const payloadLength = payload[0] << 8 | payload[1];
901
+ if (payloadLength < MPU_HEADER_REMAINDER_SIZE2)
902
+ return null;
903
+ const declaredEnd = MPU_LENGTH_FIELD_SIZE2 + payloadLength;
904
+ if (!resolveDeclaredTrailers(payload, declaredEnd, header.fecType)?.hasFecId)
905
+ return null;
906
+ const off = mmtpPacket.length - SOURCE_FEC_PAYLOAD_ID_SIZE;
907
+ return {
908
+ ssId: (mmtpPacket[off] << 24 | mmtpPacket[off + 1] << 16 | mmtpPacket[off + 2] << 8 | mmtpPacket[off + 3]) >>> 0
909
+ };
854
910
  }
855
911
  var MMTP_EXT_TYPE_ALTA_AUTH2 = MMTP_EXT_TYPE_ALTA_AUTH;
856
912
  var MMTP_EXT_TRAILER_HEADER_SIZE2 = MMTP_EXT_TRAILER_HEADER_SIZE;
857
913
  var extractAltaTrailer2 = extractAltaTrailer;
858
914
  var REPAIR_FEC_PID_SIZE = 13;
859
- function readRepairFecPayloadIdFields(mmtpPacket) {
915
+ function readRepairFecPayloadIdFields(mmtpPacket, parsedHeader) {
860
916
  let header;
861
- try {
862
- header = parseMMTPHeader(mmtpPacket);
863
- } catch {
864
- return null;
917
+ if (parsedHeader) {
918
+ header = parsedHeader;
919
+ } else {
920
+ try {
921
+ header = parseMMTPHeader(mmtpPacket);
922
+ } catch {
923
+ return null;
924
+ }
865
925
  }
866
926
  if (header.packetType !== PACKET_TYPE_REPAIR || header.fecType !== 2)
867
927
  return null;
868
928
  const off = header.headerLength;
869
929
  if (mmtpPacket.length < off + REPAIR_FEC_PID_SIZE)
870
930
  return null;
871
- const view2 = new DataView(mmtpPacket.buffer, mmtpPacket.byteOffset + off, REPAIR_FEC_PID_SIZE);
872
- const ssStart = view2.getUint32(0, false);
931
+ const ssStart = (mmtpPacket[off] << 24 | mmtpPacket[off + 1] << 16 | mmtpPacket[off + 2] << 8 | mmtpPacket[off + 3]) >>> 0;
873
932
  const rsbLength = mmtpPacket[off + 4] << 16 | mmtpPacket[off + 5] << 8 | mmtpPacket[off + 6];
874
933
  const rsId = mmtpPacket[off + 7] << 16 | mmtpPacket[off + 8] << 8 | mmtpPacket[off + 9];
875
934
  const ssbLength = mmtpPacket[off + 10] << 16 | mmtpPacket[off + 11] << 8 | mmtpPacket[off + 12];
@@ -883,7 +942,12 @@ function readRepairFecPayloadIdFields(mmtpPacket) {
883
942
  };
884
943
  }
885
944
  function parseMoqRepairFrame(frame) {
886
- const repairId = readRepairFecPayloadIdFields(frame);
945
+ return moqRepairFrameFrom(frame, readRepairFecPayloadIdFields(frame));
946
+ }
947
+ function parseMoqRepairFrameWithHeader(frame, header) {
948
+ return moqRepairFrameFrom(frame, readRepairFecPayloadIdFields(frame, header));
949
+ }
950
+ function moqRepairFrameFrom(frame, repairId) {
887
951
  if (!repairId)
888
952
  return null;
889
953
  const symbolData = frame.subarray(repairId.dataOffset);
@@ -8980,17 +9044,6 @@ function fecBlockBudgetMs(interleaveDepthMs, reorderToleranceMs) {
8980
9044
  }
8981
9045
  return Math.ceil(interleaveDepthMs + reorderToleranceMs);
8982
9046
  }
8983
- function parseRepairPacket(packet) {
8984
- const parsed = parseMoqRepairFrame(packet);
8985
- if (!parsed)
8986
- return null;
8987
- return {
8988
- ssStart: parsed.ssStart,
8989
- ssbLength: parsed.ssbLength,
8990
- rsId: parsed.rsId,
8991
- repairData: parsed.symbolData
8992
- };
8993
- }
8994
9047
 
8995
9048
  // ../fec/dist/fec-block-mapper.js
8996
9049
  var FecBlockMapper = class {
@@ -9836,6 +9889,17 @@ var FECManager = class {
9836
9889
  // Stable per-name encodings for stream and block keys, bounded by the
9837
9890
  // catalog's track names; #blockKey is built several times per packet.
9838
9891
  this.#encodedTrackNames = /* @__PURE__ */ new Map();
9892
+ // The last block and stream key built. One packet asks for the same block's
9893
+ // key from every FECManager entry point it passes through (~6 per source
9894
+ // symbol), so each key is built once per packet rather than once per call
9895
+ // (BLO-36599). Both keys are pure functions of their arguments.
9896
+ this.#lastBlockKeySbn = -1;
9897
+ this.#lastBlockKeyPacketId = -1;
9898
+ this.#lastBlockKeyTrackName = void 0;
9899
+ this.#lastBlockKey = "";
9900
+ this.#lastStreamKeyPacketId = -1;
9901
+ this.#lastStreamKeyTrackName = void 0;
9902
+ this.#lastStreamKey = "";
9839
9903
  this.#seenSymbols = /* @__PURE__ */ new Set();
9840
9904
  this.#blockPathCounts = /* @__PURE__ */ new Map();
9841
9905
  // Exact catalog identity observed on accepted symbols for each FEC block.
@@ -9961,6 +10025,13 @@ var FECManager = class {
9961
10025
  #terminalQueues;
9962
10026
  #blockKeysBySbn;
9963
10027
  #encodedTrackNames;
10028
+ #lastBlockKeySbn;
10029
+ #lastBlockKeyPacketId;
10030
+ #lastBlockKeyTrackName;
10031
+ #lastBlockKey;
10032
+ #lastStreamKeyPacketId;
10033
+ #lastStreamKeyTrackName;
10034
+ #lastStreamKey;
9964
10035
  #seenSymbols;
9965
10036
  #blockPathCounts;
9966
10037
  #blockCatalogTrackNames;
@@ -9990,7 +10061,14 @@ var FECManager = class {
9990
10061
  return encoded;
9991
10062
  }
9992
10063
  #streamKey(packetId, catalogTrackName) {
9993
- return catalogTrackName === void 0 ? `${packetId}` : `${packetId}:${this.#encodeTrackName(catalogTrackName)}`;
10064
+ if (packetId === this.#lastStreamKeyPacketId && catalogTrackName === this.#lastStreamKeyTrackName) {
10065
+ return this.#lastStreamKey;
10066
+ }
10067
+ const key = catalogTrackName === void 0 ? `${packetId}` : `${packetId}:${this.#encodeTrackName(catalogTrackName)}`;
10068
+ this.#lastStreamKeyPacketId = packetId;
10069
+ this.#lastStreamKeyTrackName = catalogTrackName;
10070
+ this.#lastStreamKey = key;
10071
+ return key;
9994
10072
  }
9995
10073
  #belongsToPacket(key, packetId) {
9996
10074
  return Number(key.split(":", 1)[0]) === packetId;
@@ -10015,8 +10093,16 @@ var FECManager = class {
10015
10093
  };
10016
10094
  }
10017
10095
  #blockKey(sbn, packetId, catalogTrackName) {
10096
+ if (sbn === this.#lastBlockKeySbn && packetId === this.#lastBlockKeyPacketId && catalogTrackName === this.#lastBlockKeyTrackName) {
10097
+ return this.#lastBlockKey;
10098
+ }
10018
10099
  const base = `${packetId}:${sbn}`;
10019
- return catalogTrackName === void 0 ? base : `${base}:${this.#encodeTrackName(catalogTrackName)}`;
10100
+ const key = catalogTrackName === void 0 ? base : `${base}:${this.#encodeTrackName(catalogTrackName)}`;
10101
+ this.#lastBlockKeySbn = sbn;
10102
+ this.#lastBlockKeyPacketId = packetId;
10103
+ this.#lastBlockKeyTrackName = catalogTrackName;
10104
+ this.#lastBlockKey = key;
10105
+ return key;
10020
10106
  }
10021
10107
  /** Record that `key` (block `sbn` on `packetId`) is held by a lookup set. */
10022
10108
  #indexBlockKey(key, sbn, packetId) {
@@ -12646,20 +12732,35 @@ var MmtpContainerParser = class _MmtpContainerParser {
12646
12732
  * Returns null for malformed packets.
12647
12733
  */
12648
12734
  parseHeader(packet, options) {
12735
+ return this.parsePacket(packet, options)?.info ?? null;
12736
+ }
12737
+ /**
12738
+ * parseHeader() that also keeps the parsed MMTP header, so a caller that
12739
+ * goes on to feedParsed() the same packet parses its header exactly once.
12740
+ * The MMTP header is parsed once here and the Source FEC Payload ID is read
12741
+ * from it; parseHeader() used to parse the header three times for an FEC
12742
+ * source packet, and feed() repeated all of it (BLO-36599).
12743
+ */
12744
+ parsePacket(packet, options) {
12649
12745
  try {
12650
12746
  const header = parseMMTPHeader(packet);
12651
12747
  const isRepair = header.packetType === PACKET_TYPE_REPAIR;
12652
- const fecPayloadId = !options?.skipFecPayloadId && !isRepair && hasSourceFecPayloadId(packet) ? extractFecPayloadId(packet) : null;
12748
+ const skipFecPayloadId = options?.skipFecPayloadId === true;
12749
+ const fecPayloadId = !skipFecPayloadId && !isRepair ? sourceFecPayloadIdOf(packet, header) : null;
12653
12750
  return {
12654
- packetId: header.packetId,
12655
- packetType: header.packetType,
12656
- timestamp: header.timestamp,
12657
- timestampUs: mmtpTimestampToMicros(header.timestamp),
12658
- sequenceNumber: header.sequenceNumber,
12659
- rapFlag: header.rapFlag,
12660
- fecType: header.fecType,
12661
- fecPayloadId,
12662
- isRepair
12751
+ info: {
12752
+ packetId: header.packetId,
12753
+ packetType: header.packetType,
12754
+ timestamp: header.timestamp,
12755
+ timestampUs: mmtpTimestampToMicros(header.timestamp),
12756
+ sequenceNumber: header.sequenceNumber,
12757
+ rapFlag: header.rapFlag,
12758
+ fecType: header.fecType,
12759
+ fecPayloadId,
12760
+ isRepair
12761
+ },
12762
+ header,
12763
+ skipFecPayloadId
12663
12764
  };
12664
12765
  } catch {
12665
12766
  return null;
@@ -12667,10 +12768,22 @@ var MmtpContainerParser = class _MmtpContainerParser {
12667
12768
  }
12668
12769
  /** Feed a complete MMTP packet. For two-phase use, call parseHeader() first. */
12669
12770
  feed(packet, options) {
12771
+ return this.#feedCounted(packet, options, void 0);
12772
+ }
12773
+ /**
12774
+ * feed() for a packet this caller already ran through parsePacket(): the
12775
+ * header parse is reused instead of repeated. `parsed` must come from
12776
+ * parsePacket() of this same, unmodified packet. If it was resolved under a
12777
+ * different skipFecPayloadId option, the packet is parsed again.
12778
+ */
12779
+ feedParsed(packet, parsed, options) {
12780
+ return this.#feedCounted(packet, options, parsed);
12781
+ }
12782
+ #feedCounted(packet, options, parsed) {
12670
12783
  this.#fedObjects += 1;
12671
12784
  let record;
12672
12785
  try {
12673
- record = this.#feed(packet, options);
12786
+ record = this.#feed(packet, options, parsed);
12674
12787
  } catch (error) {
12675
12788
  this.#countOutcome("drop:threw");
12676
12789
  throw error;
@@ -12678,11 +12791,12 @@ var MmtpContainerParser = class _MmtpContainerParser {
12678
12791
  if (record) this.#countOutcome(record.kind);
12679
12792
  return record;
12680
12793
  }
12681
- #feed(packet, options) {
12794
+ #feed(packet, options, preParsed) {
12682
12795
  if (this.#disposed) return this.#drop("disposed");
12683
12796
  this.cleanupTimeouts();
12684
- const info = this.parseHeader(packet, options);
12685
- if (!info) return this.#drop("header-parse-failed");
12797
+ const parsed = preParsed !== void 0 && preParsed.skipFecPayloadId === (options?.skipFecPayloadId === true) ? preParsed : this.parsePacket(packet, options);
12798
+ if (!parsed) return this.#drop("header-parse-failed");
12799
+ const info = parsed.info;
12686
12800
  if (info.packetType !== PACKET_TYPE_MPU2) {
12687
12801
  return {
12688
12802
  kind: info.isRepair ? "repair" : "non-media",
@@ -12699,7 +12813,7 @@ var MmtpContainerParser = class _MmtpContainerParser {
12699
12813
  } catch {
12700
12814
  }
12701
12815
  }
12702
- const extraction = extractMFUPayload(packet, {
12816
+ const extraction = extractMFUPayloadWithHeader(packet, parsed.header, {
12703
12817
  skipFecId: options?.skipFecPayloadId
12704
12818
  });
12705
12819
  if (!extraction) return this.#drop("mfu-extract-failed");
@@ -13393,6 +13507,17 @@ function ingressHasPacketId(ingress, packetId) {
13393
13507
  if (packetId === MMTP_SIGNALING_PACKET_ID) return false;
13394
13508
  return ingress === void 0 || ingressEntry(ingress, packetId) !== void 0;
13395
13509
  }
13510
+ var PACKET_LEDGER_HIGH_WATER = 4e3;
13511
+ var PACKET_LEDGER_RETAINED = 2e3;
13512
+ function rememberInLedger(ledger, value) {
13513
+ ledger.add(value);
13514
+ if (ledger.size <= PACKET_LEDGER_HIGH_WATER) return;
13515
+ let excess = ledger.size - PACKET_LEDGER_RETAINED;
13516
+ for (const oldest of ledger) {
13517
+ if (excess-- <= 0) break;
13518
+ ledger.delete(oldest);
13519
+ }
13520
+ }
13396
13521
  var DIRECT_RECOVERED_VIDEO_QUEUE_LIMIT = 512;
13397
13522
  var DIRECT_RECOVERED_AUDIO_QUEUE_LIMIT = 512;
13398
13523
  var MmtpRouter = class {
@@ -13430,16 +13555,13 @@ var MmtpRouter = class {
13430
13555
  // identity keeps renditions that reuse a packet ID from opening each other's gate.
13431
13556
  #trackSynced = /* @__PURE__ */ new Set();
13432
13557
  #droppedPreSyncVideo = /* @__PURE__ */ new Set();
13433
- // Dedup: packetId-scoped packetSequenceNumbers already received via direct source path.
13434
- // Recovered FEC blocks contain all K source symbols, so skip direct-path
13435
- // duplicates even when the source frame is still buffered by parser reordering.
13436
- #mediaStreamKeys = /* @__PURE__ */ new Map();
13437
- #sourcePktSeqs = /* @__PURE__ */ new Set();
13438
- #routedPacketSeqs = /* @__PURE__ */ new Set();
13439
- // FEC source symbols (packetId/track/SBN/ESI) already parsed for playback.
13440
- // An overlap copy and its multicast twin carry the same symbol, and the copy
13441
- // never feeds FEC, so FEC's first-arrival check cannot dedup the pair.
13442
- #parsedSourceSymbols = /* @__PURE__ */ new Set();
13558
+ // One entry per physical stream (packetId, catalog track name): its stable
13559
+ // JSON key and its per-packet duplicate ledgers. The ledgers used to be
13560
+ // three router-wide Sets of "<streamKey>:<coordinates>" strings, so every
13561
+ // packet built and hashed up to three strings, and resetTrack() scanned
13562
+ // every retained key by prefix (BLO-36599). They now hold the wire numbers
13563
+ // themselves, and a reset clears one stream's Sets.
13564
+ #streams = /* @__PURE__ */ new Map();
13443
13565
  // Live WebCodecs callbacks cannot accept late recovered inter-frame video.
13444
13566
  // MSE recovery routes through CmafBlockAssembler and can reorder by block; the
13445
13567
  // direct callback path has to preserve decode order before frames leave libmmt.
@@ -13970,11 +14092,15 @@ var MmtpRouter = class {
13970
14092
  */
13971
14093
  feedRepairPacket(packet, ownerPacketIdHint, catalogTrackName) {
13972
14094
  if (this.#disposed) return;
13973
- const parsed = parseRepairPacket(packet);
14095
+ let header;
14096
+ try {
14097
+ header = parseMMTPHeader(packet);
14098
+ } catch {
14099
+ return;
14100
+ }
14101
+ const parsed = parseMoqRepairFrameWithHeader(packet, header);
13974
14102
  if (!parsed) return;
13975
- const header = packet.length >= 4 ? parseMMTPHeader(packet) : null;
13976
- const repairPacketId = header?.packetId;
13977
- if (repairPacketId == null) return;
14103
+ const repairPacketId = header.packetId;
13978
14104
  const hintedOwner = ownerPacketIdHint != null && this.#classifyPacketId(ownerPacketIdHint) !== null ? ownerPacketIdHint : null;
13979
14105
  const ownerPacketId = hintedOwner ?? (this.#classifyPacketId(repairPacketId) !== null ? repairPacketId : null);
13980
14106
  if (ownerPacketId === null) {
@@ -13999,7 +14125,7 @@ var MmtpRouter = class {
13999
14125
  ownerPacketId,
14000
14126
  repairSbn,
14001
14127
  repairEsi,
14002
- parsed.repairData,
14128
+ parsed.symbolData,
14003
14129
  "moq",
14004
14130
  catalogTrackName
14005
14131
  )) return;
@@ -14008,13 +14134,13 @@ var MmtpRouter = class {
14008
14134
  parsed.ssStart,
14009
14135
  parsed.ssbLength,
14010
14136
  parsed.rsId,
14011
- parsed.repairData
14137
+ parsed.symbolData
14012
14138
  ) : this.#fecManager.feedRepairSymbol(
14013
14139
  ownerPacketId,
14014
14140
  parsed.ssStart,
14015
14141
  parsed.ssbLength,
14016
14142
  parsed.rsId,
14017
- parsed.repairData,
14143
+ parsed.symbolData,
14018
14144
  catalogTrackName
14019
14145
  );
14020
14146
  if (recovered) {
@@ -14121,16 +14247,10 @@ var MmtpRouter = class {
14121
14247
  this.#lastDirectEmittedAudioCursorByStream.delete(streamKey);
14122
14248
  this.#pendingDirectRecoveredAudioByStream.delete(streamKey);
14123
14249
  this.#manifestRecoveryQueues.delete(streamKey);
14124
- const streamPrefix = `${streamKey}:`;
14125
- for (const packetSeqs of [
14126
- this.#sourcePktSeqs,
14127
- this.#routedPacketSeqs,
14128
- this.#parsedSourceSymbols
14129
- ]) {
14130
- for (const key of packetSeqs) {
14131
- if (key.startsWith(streamPrefix)) packetSeqs.delete(key);
14132
- }
14133
- }
14250
+ const ledgers = this.#stream(packetId, catalogTrackName);
14251
+ ledgers.sourceSeqs.clear();
14252
+ ledgers.routedSeqs.clear();
14253
+ ledgers.parsedSymbols.clear();
14134
14254
  const assemblersToReset = /* @__PURE__ */ new Set();
14135
14255
  const exactAssembler = this.#mseAssemblers.get(streamKey);
14136
14256
  if (exactAssembler) assemblersToReset.add(exactAssembler);
@@ -14146,9 +14266,7 @@ var MmtpRouter = class {
14146
14266
  this.#disposed = true;
14147
14267
  this.cancelKeyframeLostTimer();
14148
14268
  this.#parser.dispose();
14149
- this.#sourcePktSeqs.clear();
14150
- this.#routedPacketSeqs.clear();
14151
- this.#parsedSourceSymbols.clear();
14269
+ this.#streams.clear();
14152
14270
  this.#trackPacketCallbacks.clear();
14153
14271
  this.#latestVideoSeqByStream.clear();
14154
14272
  this.#lastDirectSourceVideoTsByStream.clear();
@@ -14248,8 +14366,9 @@ var MmtpRouter = class {
14248
14366
  * 2. feed() → MFU extraction, reassembly, keyframe detection, emit via callbacks
14249
14367
  */
14250
14368
  #processMMTP(packet, source, manifestIdentity, manifestAdmitted = false, ingress, overlapCopy = false) {
14251
- const info = this.#parser.parseHeader(packet);
14252
- if (!info) return;
14369
+ const parsed = this.#parser.parsePacket(packet);
14370
+ if (!parsed) return;
14371
+ const info = parsed.info;
14253
14372
  if (source === "multicast" && !ingressHasPacketId(ingress, info.packetId)) {
14254
14373
  debugRouter(
14255
14374
  `[MmtpRouter] Dropping multicast packet pid=${info.packetId}: absent from ingress tracks`
@@ -14303,8 +14422,8 @@ var MmtpRouter = class {
14303
14422
  }
14304
14423
  }
14305
14424
  if (info.isRepair) {
14306
- const parsed = parseRepairPacket(packet);
14307
- if (!parsed) return;
14425
+ const repair = parseMoqRepairFrameWithHeader(packet, parsed.header);
14426
+ if (!repair) return;
14308
14427
  const ownerPacketId = this.#resolveRepairOwnerPacketId(info.packetId, ingress);
14309
14428
  if (ownerPacketId != null && this.#fecManager) {
14310
14429
  if (hasAmbiguousCatalogTrackName(ingress, ownerPacketId)) return;
@@ -14312,17 +14431,17 @@ var MmtpRouter = class {
14312
14431
  const count = this.#repairPidCounts[info.packetId];
14313
14432
  if (count <= 2 || count % 500 === 0) {
14314
14433
  console.log(
14315
- `[MC-PKT] pid=${info.packetId} repairOwner=${ownerPacketId} type=${info.packetType} count=${count} ssbLength=${parsed.ssbLength} len=${packet.length}`
14434
+ `[MC-PKT] pid=${info.packetId} repairOwner=${ownerPacketId} type=${info.packetType} count=${count} ssbLength=${repair.ssbLength} len=${packet.length}`
14316
14435
  );
14317
14436
  }
14318
- const repairSbn = this.#deriveRepairSbn(parsed.ssStart, parsed.ssbLength, ownerPacketId);
14437
+ const repairSbn = this.#deriveRepairSbn(repair.ssStart, repair.ssbLength, ownerPacketId);
14319
14438
  if (repairSbn === null) return;
14320
- const repairEsi = this.#kForPacket(ownerPacketId) + parsed.rsId;
14439
+ const repairEsi = this.#kForPacket(ownerPacketId) + repair.rsId;
14321
14440
  if (!this.#feedFecTransportSymbol(
14322
14441
  ownerPacketId,
14323
14442
  repairSbn,
14324
14443
  repairEsi,
14325
- parsed.repairData,
14444
+ repair.symbolData,
14326
14445
  source,
14327
14446
  catalogTrackNameForPacket(ingress, ownerPacketId)
14328
14447
  )) return;
@@ -14338,16 +14457,16 @@ var MmtpRouter = class {
14338
14457
  }
14339
14458
  const recovered = repairCatalogTrackName === void 0 ? this.#fecManager.feedRepairSymbol(
14340
14459
  ownerPacketId,
14341
- parsed.ssStart,
14342
- parsed.ssbLength,
14343
- parsed.rsId,
14344
- parsed.repairData
14460
+ repair.ssStart,
14461
+ repair.ssbLength,
14462
+ repair.rsId,
14463
+ repair.symbolData
14345
14464
  ) : this.#fecManager.feedRepairSymbol(
14346
14465
  ownerPacketId,
14347
- parsed.ssStart,
14348
- parsed.ssbLength,
14349
- parsed.rsId,
14350
- parsed.repairData,
14466
+ repair.ssStart,
14467
+ repair.ssbLength,
14468
+ repair.rsId,
14469
+ repair.symbolData,
14351
14470
  repairCatalogTrackName
14352
14471
  );
14353
14472
  if (recovered) {
@@ -14399,7 +14518,6 @@ var MmtpRouter = class {
14399
14518
  let sourceSymbolAccepted = false;
14400
14519
  let sourceSymbolTrackable = false;
14401
14520
  let parseSourceSymbol = false;
14402
- let symbolKey;
14403
14521
  const catalogTrackName = catalogTrackNameForPacket(ingress, info.packetId);
14404
14522
  if (catalogTrackName !== void 0) {
14405
14523
  const names = this.#catalogTrackNamesByPacketId.get(info.packetId) ?? /* @__PURE__ */ new Set();
@@ -14412,7 +14530,6 @@ var MmtpRouter = class {
14412
14530
  const rawSymbol = packet.subarray(0, packet.length - 4);
14413
14531
  const oversized = symbolSize > 0 && rawSymbol.length > symbolSize;
14414
14532
  const coords = this.#deriveSbnEsi(fecId.ssId, info.packetId);
14415
- symbolKey = `${this.#mediaStreamKey(info.packetId, catalogTrackName)}:${coords.sbn}:${coords.esi}`;
14416
14533
  let overlapBlockCompleted = false;
14417
14534
  if (overlapCopy) {
14418
14535
  overlapBlockCompleted = catalogTrackName === void 0 ? this.#fecManager.isBlockCompleted(coords.sbn, info.packetId) : this.#fecManager.isBlockCompleted(
@@ -14445,11 +14562,16 @@ var MmtpRouter = class {
14445
14562
  sourceFec = { ssId: fecId.ssId, sbn: coords.sbn, symbolData };
14446
14563
  }
14447
14564
  }
14448
- parseSourceSymbol = (overlapCopy ? !overlapBlockCompleted : sourceSymbolAccepted) && !this.#parsedSourceSymbols.has(symbolKey);
14565
+ parseSourceSymbol = (overlapCopy ? !overlapBlockCompleted : sourceSymbolAccepted) && !this.#stream(info.packetId, catalogTrackName).parsedSymbols.has(fecId.ssId);
14449
14566
  fecInfo = { sbn: coords.sbn, esi: coords.esi, oversized };
14450
14567
  }
14451
14568
  if (!this.#fecManager || !info.fecPayloadId || parseSourceSymbol) {
14452
- if (symbolKey !== void 0) this.#noteParsedSourceSymbol(symbolKey);
14569
+ if (this.#fecManager && info.fecPayloadId) {
14570
+ rememberInLedger(
14571
+ this.#stream(info.packetId, catalogTrackName).parsedSymbols,
14572
+ info.fecPayloadId.ssId
14573
+ );
14574
+ }
14453
14575
  this.#noteSourcePacketSeq(
14454
14576
  info.packetId,
14455
14577
  info.sequenceNumber,
@@ -14463,7 +14585,7 @@ var MmtpRouter = class {
14463
14585
  this.#feedViaParser(packet, info, fecInfo, {
14464
14586
  source,
14465
14587
  catalogTrackName
14466
- });
14588
+ }, parsed);
14467
14589
  }
14468
14590
  if (this.#fecManager && info.fecPayloadId && sourceSymbolAccepted && sourceSymbolTrackable) {
14469
14591
  const { sbn, esi } = this.#deriveSbnEsi(
@@ -14539,7 +14661,7 @@ var MmtpRouter = class {
14539
14661
  * Set temp context and delegate to parser.feed() for MFU extraction,
14540
14662
  * reassembly, keyframe detection, and frame emission via callbacks.
14541
14663
  */
14542
- #feedViaParser(packet, info, fecInfo, parserOptions) {
14664
+ #feedViaParser(packet, info, fecInfo, parserOptions, parsed) {
14543
14665
  const diag = this.#diagFor(info.packetId, parserOptions?.catalogTrackName);
14544
14666
  const fragmentsBefore = this.#parser.reassemblerFragmentsFed;
14545
14667
  const reassembledBefore = this.#parser.reassemblerDataUnitsReturned;
@@ -14550,13 +14672,15 @@ var MmtpRouter = class {
14550
14672
  this.#currentFecInfo = fecInfo;
14551
14673
  this.#currentSeqNum = info.sequenceNumber;
14552
14674
  try {
14553
- this.#parser.feed(packet, {
14675
+ const feedOptions = {
14554
14676
  skipFecPayloadId: parserOptions?.skipFecPayloadId,
14555
14677
  recovered: parserOptions?.recovered,
14556
14678
  source: parserOptions?.source,
14557
14679
  deliveryProvenance: parserOptions?.deliveryProvenance,
14558
14680
  catalogTrackName: parserOptions?.catalogTrackName
14559
- });
14681
+ };
14682
+ if (parsed) this.#parser.feedParsed(packet, parsed, feedOptions);
14683
+ else this.#parser.feed(packet, feedOptions);
14560
14684
  } finally {
14561
14685
  this.#currentFecInfo = void 0;
14562
14686
  }
@@ -14629,41 +14753,21 @@ var MmtpRouter = class {
14629
14753
  }
14630
14754
  #noteSourcePacketSeq(packetId, packetSequenceNumber, catalogTrackName) {
14631
14755
  if (packetSequenceNumber === void 0 || !Number.isFinite(packetSequenceNumber)) return;
14632
- this.#sourcePktSeqs.add(
14633
- this.#packetSeqKey(packetId, packetSequenceNumber, catalogTrackName)
14756
+ rememberInLedger(
14757
+ this.#stream(packetId, catalogTrackName).sourceSeqs,
14758
+ packetSequenceNumber
14634
14759
  );
14635
- if (this.#sourcePktSeqs.size > 4e3) {
14636
- const iter = this.#sourcePktSeqs.values();
14637
- for (let i = 0; i < 2e3; i++) iter.next();
14638
- const keep = /* @__PURE__ */ new Set();
14639
- for (const v of iter) keep.add(v);
14640
- this.#sourcePktSeqs = keep;
14641
- }
14642
14760
  }
14643
- #noteParsedSourceSymbol(key) {
14644
- this.#parsedSourceSymbols.add(key);
14645
- if (this.#parsedSourceSymbols.size > 4e3) {
14646
- const iter = this.#parsedSourceSymbols.values();
14647
- for (let i = 0; i < 2e3; i++) iter.next();
14648
- const keep = /* @__PURE__ */ new Set();
14649
- for (const v of iter) keep.add(v);
14650
- this.#parsedSourceSymbols = keep;
14651
- }
14761
+ #sourceSeqReceived(packetId, packetSequenceNumber, catalogTrackName) {
14762
+ return this.#stream(packetId, catalogTrackName).sourceSeqs.has(packetSequenceNumber);
14652
14763
  }
14653
14764
  #routeTrackPacket(packet, packetId, packetSequenceNumber, meta) {
14654
14765
  if (!Number.isFinite(packetSequenceNumber)) return;
14655
14766
  const callback = this.#trackPacketCallbacks.get(packetId);
14656
14767
  if (!callback) return;
14657
- const key = this.#packetSeqKey(packetId, packetSequenceNumber, meta.catalogTrackName);
14658
- if (this.#routedPacketSeqs.has(key)) return;
14659
- this.#routedPacketSeqs.add(key);
14660
- if (this.#routedPacketSeqs.size > 4e3) {
14661
- const iter = this.#routedPacketSeqs.values();
14662
- for (let i = 0; i < 2e3; i++) iter.next();
14663
- const keep = /* @__PURE__ */ new Set();
14664
- for (const value of iter) keep.add(value);
14665
- this.#routedPacketSeqs = keep;
14666
- }
14768
+ const routedSeqs = this.#stream(packetId, meta.catalogTrackName).routedSeqs;
14769
+ if (routedSeqs.has(packetSequenceNumber)) return;
14770
+ rememberInLedger(routedSeqs, packetSequenceNumber);
14667
14771
  callback(packet, {
14668
14772
  packetId,
14669
14773
  packetSequenceNumber,
@@ -14685,17 +14789,26 @@ var MmtpRouter = class {
14685
14789
  * of pairs is bounded by the catalog (BLO-36599).
14686
14790
  */
14687
14791
  #mediaStreamKey(packetId, catalogTrackName) {
14688
- let byName = this.#mediaStreamKeys.get(packetId);
14792
+ return this.#stream(packetId, catalogTrackName).key;
14793
+ }
14794
+ /** The (packetId, catalog name) stream's key and duplicate ledgers. */
14795
+ #stream(packetId, catalogTrackName) {
14796
+ let byName = this.#streams.get(packetId);
14689
14797
  if (!byName) {
14690
14798
  byName = /* @__PURE__ */ new Map();
14691
- this.#mediaStreamKeys.set(packetId, byName);
14692
- }
14693
- let key = byName.get(catalogTrackName);
14694
- if (key === void 0) {
14695
- key = JSON.stringify([packetId, catalogTrackName ?? null]);
14696
- byName.set(catalogTrackName, key);
14799
+ this.#streams.set(packetId, byName);
14800
+ }
14801
+ let stream = byName.get(catalogTrackName);
14802
+ if (stream === void 0) {
14803
+ stream = {
14804
+ key: JSON.stringify([packetId, catalogTrackName ?? null]),
14805
+ sourceSeqs: /* @__PURE__ */ new Set(),
14806
+ routedSeqs: /* @__PURE__ */ new Set(),
14807
+ parsedSymbols: /* @__PURE__ */ new Set()
14808
+ };
14809
+ byName.set(catalogTrackName, stream);
14697
14810
  }
14698
- return key;
14811
+ return stream;
14699
14812
  }
14700
14813
  #packetIdFromStreamKey(key) {
14701
14814
  try {
@@ -14740,9 +14853,6 @@ var MmtpRouter = class {
14740
14853
  if (scaled === null) return null;
14741
14854
  return { authoredTiming, timestamp: scaled.timestamp, mmtpTimestamp };
14742
14855
  }
14743
- #packetSeqKey(packetId, packetSequenceNumber, catalogTrackName) {
14744
- return `${this.#mediaStreamKey(packetId, catalogTrackName)}:${packetSequenceNumber}`;
14745
- }
14746
14856
  #pendingRecoveredKey(meta) {
14747
14857
  return `${meta.timestamp}:${meta.packetSequenceNumber ?? ""}`;
14748
14858
  }
@@ -15060,9 +15170,7 @@ var MmtpRouter = class {
15060
15170
  if (sym.fragmentType === 0 || sym.fragmentType === 1) continue;
15061
15171
  const fi = sym.fragment.fragmentationIndicator;
15062
15172
  if (fi === 0) {
15063
- if (this.#sourcePktSeqs.has(
15064
- this.#packetSeqKey(ownerPacketId, sym.packetSequenceNumber, catalogTrackName)
15065
- )) {
15173
+ if (this.#sourceSeqReceived(ownerPacketId, sym.packetSequenceNumber, catalogTrackName)) {
15066
15174
  dedupedAgainstSource2++;
15067
15175
  continue;
15068
15176
  }
@@ -15112,9 +15220,7 @@ var MmtpRouter = class {
15112
15220
  });
15113
15221
  this.#noteEmittedKeyframe(ownerPacketId, keyframe);
15114
15222
  } else {
15115
- if (this.#sourcePktSeqs.has(
15116
- this.#packetSeqKey(ownerPacketId, sym.packetSequenceNumber, catalogTrackName)
15117
- )) {
15223
+ if (this.#sourceSeqReceived(ownerPacketId, sym.packetSequenceNumber, catalogTrackName)) {
15118
15224
  dedupedAgainstSource2++;
15119
15225
  continue;
15120
15226
  }
@@ -15242,9 +15348,7 @@ var MmtpRouter = class {
15242
15348
  if (isVideo) {
15243
15349
  const fi = sym.fragment.fragmentationIndicator;
15244
15350
  if (fi === 0) {
15245
- if (this.#sourcePktSeqs.has(
15246
- this.#packetSeqKey(ownerPacketId, sym.packetSequenceNumber, catalogTrackName)
15247
- )) {
15351
+ if (this.#sourceSeqReceived(ownerPacketId, sym.packetSequenceNumber, catalogTrackName)) {
15248
15352
  dedupedAgainstSource++;
15249
15353
  continue;
15250
15354
  }
@@ -15289,9 +15393,7 @@ var MmtpRouter = class {
15289
15393
  ...catalogTrackName === void 0 ? {} : { catalogTrackName }
15290
15394
  });
15291
15395
  } else {
15292
- if (this.#sourcePktSeqs.has(
15293
- this.#packetSeqKey(ownerPacketId, sym.packetSequenceNumber, catalogTrackName)
15294
- )) {
15396
+ if (this.#sourceSeqReceived(ownerPacketId, sym.packetSequenceNumber, catalogTrackName)) {
15295
15397
  dedupedAgainstSource++;
15296
15398
  continue;
15297
15399
  }
@@ -15370,9 +15472,7 @@ var MmtpRouter = class {
15370
15472
  }
15371
15473
  }
15372
15474
  } else {
15373
- if (this.#sourcePktSeqs.has(
15374
- this.#packetSeqKey(ownerPacketId, sym.packetSequenceNumber, catalogTrackName)
15375
- )) {
15475
+ if (this.#sourceSeqReceived(ownerPacketId, sym.packetSequenceNumber, catalogTrackName)) {
15376
15476
  dedupedAgainstSource++;
15377
15477
  continue;
15378
15478
  }
@@ -17234,10 +17334,8 @@ var MulticastTransport = class _MulticastTransport {
17234
17334
  this.#packetIds = null;
17235
17335
  }
17236
17336
  #acceptPacket(packet) {
17237
- let packetId;
17238
- try {
17239
- packetId = parseMMTPHeader(packet).packetId;
17240
- } catch {
17337
+ const packetId = readMMTPPacketId(packet);
17338
+ if (packetId === null) {
17241
17339
  if (!this.#packetIds) return true;
17242
17340
  this.#stats.packetsDroppedUnparseable++;
17243
17341
  return false;