@blockcast/mmt-container 1.0.0 → 1.0.1-main.a3b5ae176fff

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blockcast/mmt-container",
3
- "version": "1.0.0",
3
+ "version": "1.0.1-main.a3b5ae176fff",
4
4
  "description": "MMTP/LOC container parsing for MMT over MoQ and SSM",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -169,10 +169,10 @@
169
169
  "test:watch": "vitest"
170
170
  },
171
171
  "dependencies": {
172
- "@blockcast/fec-worker": "^1.0.0",
173
- "@blockcast/mmt-base": "^1.0.0",
174
- "@blockcast/mmt-fec": "^1.0.0",
175
- "@blockcast/mmt-manifest-verify": "^1.0.0",
172
+ "@blockcast/fec-worker": "1.0.1-main.a3b5ae176fff",
173
+ "@blockcast/mmt-base": "1.0.1-main.a3b5ae176fff",
174
+ "@blockcast/mmt-fec": "1.0.1-main.a3b5ae176fff",
175
+ "@blockcast/mmt-manifest-verify": "1.0.1-main.a3b5ae176fff",
176
176
  "@noble/curves": "^1.9.7",
177
177
  "@noble/hashes": "^1.8.0",
178
178
  "ajv": "^8.17.1",
@@ -29,7 +29,9 @@
29
29
  * below a RAP the receiver already holds complete is not requested at
30
30
  * all (`moot-rap`), and a P0 FETCH still running when such a RAP
31
31
  * arrives is cancelled (`repair-stopped`, `moot`): the receiver resumes
32
- * from that RAP (draft-ramadan-moq-fec §11.4.2).
32
+ * from that RAP (draft-ramadan-moq-fec §11.4.2). Only a repair that
33
+ * could not land by the block's playout deadline (it relies on the
34
+ * keyframe budget's extension) is moot: one that can stalls nothing.
33
35
  * - P1 (audio) dispatches, uncapped, while no P0 is queued or in flight;
34
36
  * P2 (delta) only while no P0/P1 is, earliest absolute fail deadline
35
37
  * first (§11.4.5 item 3).
@@ -575,6 +577,8 @@ type StopReason = RepairStopReason
575
577
 
576
578
  interface FetchRun {
577
579
  readonly controller: AbortController
580
+ /** When this FETCH went out: it lands around `sentAt + rtt`. */
581
+ readonly sentAt: number
578
582
  stopped: StopReason | null
579
583
  }
580
584
 
@@ -596,6 +600,11 @@ interface RepairEntry {
596
600
  * later: a block reclassified from delta can gain the keyframe budget.
597
601
  */
598
602
  deadlineAt: number
603
+ /**
604
+ * `deadlineAt` without the keyframe budget's extension: the block's playout
605
+ * deadline. A FETCH landing by it costs no stall, so it is never moot.
606
+ */
607
+ playoutDeadlineAt: number
599
608
  /** Inputs `deadlineAt` was computed from; they move with it. */
600
609
  anchorAt: number | null
601
610
  delayMs: number | null
@@ -697,9 +706,18 @@ export function wireRepairFetch(mc: RepairFetchHost, opts: WireRepairFetchOption
697
706
  return length === undefined ? undefined : entry.sbn + length - 1
698
707
  }
699
708
 
700
- /** A keyframe request whose whole block lies below a RAP already held complete. */
709
+ /**
710
+ * A keyframe request whose whole block lies below a RAP already held
711
+ * complete, and whose repair can only land on the keyframe budget's
712
+ * extension. One that lands by the playout deadline stalls nothing, and
713
+ * skipping it only throws away the block's frames (BLO-51635).
714
+ */
701
715
  const moot = (entry: RepairEntry): boolean => {
702
716
  if (entry.priority !== 0) return false
717
+ const rttMs = readRtt()
718
+ // A running FETCH lands relative to when it was sent, not to now.
719
+ const sentAt = entry.running?.sentAt ?? performance.now()
720
+ if (rttMs !== null && sentAt + rttMs < entry.playoutDeadlineAt) return false
703
721
  const last = lastSsId(entry)
704
722
  if (last === undefined) return false
705
723
  try {
@@ -832,7 +850,7 @@ export function wireRepairFetch(mc: RepairFetchHost, opts: WireRepairFetchOption
832
850
  bound: entry.bound,
833
851
  ...(entry.range ? { range: true as const } : {}),
834
852
  })
835
- const run: FetchRun = { controller: new AbortController(), stopped: null }
853
+ const run: FetchRun = { controller: new AbortController(), sentAt: now, stopped: null }
836
854
  entry.running = run
837
855
  // Hold the block open to the presentation deadline while this FETCH can
838
856
  // still land: the multicast span elapsing must not abort it.
@@ -1053,6 +1071,10 @@ export function wireRepairFetch(mc: RepairFetchHost, opts: WireRepairFetchOption
1053
1071
  )
1054
1072
  if (promotedDeadline !== null && promotedDeadline.deadlineAt > entry.deadlineAt) {
1055
1073
  entry.deadlineAt = promotedDeadline.deadlineAt
1074
+ entry.playoutDeadlineAt = Math.max(
1075
+ entry.playoutDeadlineAt,
1076
+ promotedDeadline.deadlineAt - promotedDeadline.extensionMs,
1077
+ )
1056
1078
  entry.anchorAt = promotedDeadline.anchorAt
1057
1079
  entry.delayMs = promotedDeadline.delayMs
1058
1080
  entry.bound = promotedDeadline.bound
@@ -1231,6 +1253,7 @@ export function wireRepairFetch(mc: RepairFetchHost, opts: WireRepairFetchOption
1231
1253
  priority,
1232
1254
  subscriberPriority: wire.subscriberPriority,
1233
1255
  deadlineAt: deadline.deadlineAt,
1256
+ playoutDeadlineAt: deadline.deadlineAt - deadline.extensionMs,
1234
1257
  anchorAt: deadline.anchorAt,
1235
1258
  delayMs: deadline.delayMs,
1236
1259
  bound: deadline.bound,
@@ -1313,6 +1336,7 @@ export function wireRepairFetch(mc: RepairFetchHost, opts: WireRepairFetchOption
1313
1336
  priority,
1314
1337
  subscriberPriority: wire.subscriberPriority,
1315
1338
  deadlineAt: deadline.deadlineAt,
1339
+ playoutDeadlineAt: deadline.deadlineAt - deadline.extensionMs,
1316
1340
  anchorAt: deadline.anchorAt,
1317
1341
  delayMs: deadline.delayMs,
1318
1342
  bound: deadline.bound,