@blockcast/mmt-container 0.1.0 → 0.3.0-main.0bde85f7082f

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 (799) hide show
  1. package/README.md +524 -0
  2. package/dist/.build-stamp +0 -0
  3. package/dist/__tests__/test-utils.d.ts +71 -0
  4. package/dist/__tests__/test-utils.d.ts.map +1 -0
  5. package/dist/align.d.ts +152 -0
  6. package/dist/align.d.ts.map +1 -0
  7. package/dist/align.js +88 -0
  8. package/dist/align.js.map +7 -0
  9. package/dist/audio-init-parser.d.ts +66 -0
  10. package/dist/audio-init-parser.d.ts.map +1 -0
  11. package/dist/audio-init-parser.js +118 -0
  12. package/dist/audio-init-parser.js.map +7 -0
  13. package/dist/audio-ring-buffer.d.ts +50 -0
  14. package/dist/audio-ring-buffer.d.ts.map +1 -0
  15. package/dist/audio-ring-buffer.js +80 -0
  16. package/dist/audio-ring-buffer.js.map +7 -0
  17. package/dist/authored-timing.d.ts +32 -0
  18. package/dist/authored-timing.d.ts.map +1 -0
  19. package/dist/authored-timing.js +32 -0
  20. package/dist/authored-timing.js.map +7 -0
  21. package/dist/block-policy.d.ts +314 -0
  22. package/dist/block-policy.d.ts.map +1 -0
  23. package/dist/block-policy.js +466 -0
  24. package/dist/block-policy.js.map +7 -0
  25. package/dist/browser-runtime.js +26549 -0
  26. package/dist/browser-runtime.js.map +7 -0
  27. package/dist/carp-catalog.d.ts +429 -0
  28. package/dist/carp-catalog.d.ts.map +1 -0
  29. package/dist/carp-catalog.js +985 -0
  30. package/dist/carp-catalog.js.map +7 -0
  31. package/dist/catalog-fec-params.d.ts +275 -0
  32. package/dist/catalog-fec-params.d.ts.map +1 -0
  33. package/dist/catalog-fec-params.js +702 -0
  34. package/dist/catalog-fec-params.js.map +7 -0
  35. package/dist/catalog-network-source.d.ts +42 -0
  36. package/dist/catalog-network-source.d.ts.map +1 -0
  37. package/dist/catalog-network-source.js +47 -0
  38. package/dist/catalog-network-source.js.map +7 -0
  39. package/dist/catalog-validator.d.ts +18 -0
  40. package/dist/catalog-validator.d.ts.map +1 -0
  41. package/dist/catalog-validator.js +7747 -0
  42. package/dist/catalog-validator.js.map +7 -0
  43. package/dist/cmaf-block-assembler.d.ts +285 -0
  44. package/dist/cmaf-block-assembler.d.ts.map +1 -0
  45. package/dist/cmaf-block-assembler.js +1966 -0
  46. package/dist/cmaf-block-assembler.js.map +7 -0
  47. package/dist/cmaf-group-batcher.d.ts +38 -0
  48. package/dist/cmaf-group-batcher.d.ts.map +1 -0
  49. package/dist/cmaf-group-batcher.js +176 -0
  50. package/dist/cmaf-group-batcher.js.map +7 -0
  51. package/dist/cmcd-producer.d.ts +104 -0
  52. package/dist/cmcd-producer.d.ts.map +1 -0
  53. package/dist/cmcd-producer.js +183 -0
  54. package/dist/cmcd-producer.js.map +7 -0
  55. package/dist/codec-utils.d.ts +384 -0
  56. package/dist/codec-utils.d.ts.map +1 -0
  57. package/dist/codec-utils.js +1546 -0
  58. package/dist/codec-utils.js.map +7 -0
  59. package/dist/conceal-timeline.d.ts +146 -0
  60. package/dist/conceal-timeline.d.ts.map +1 -0
  61. package/dist/conceal-timeline.js +137 -0
  62. package/dist/conceal-timeline.js.map +7 -0
  63. package/dist/delivery-gap.d.ts +75 -0
  64. package/dist/delivery-gap.d.ts.map +1 -0
  65. package/dist/delivery-gap.js +88 -0
  66. package/dist/delivery-gap.js.map +7 -0
  67. package/dist/duplicate-ledger.d.ts +197 -0
  68. package/dist/duplicate-ledger.d.ts.map +1 -0
  69. package/dist/duplicate-ledger.js +226 -0
  70. package/dist/duplicate-ledger.js.map +7 -0
  71. package/dist/fec-codec.d.ts +32 -0
  72. package/dist/fec-codec.d.ts.map +1 -0
  73. package/dist/fec-codec.js +47 -0
  74. package/dist/fec-codec.js.map +7 -0
  75. package/dist/fec-manager.d.ts +583 -0
  76. package/dist/fec-manager.d.ts.map +1 -0
  77. package/dist/fec-manager.js +3899 -0
  78. package/dist/fec-manager.js.map +7 -0
  79. package/dist/fec-starvation-detector.d.ts +59 -0
  80. package/dist/fec-starvation-detector.d.ts.map +1 -0
  81. package/dist/fec-starvation-detector.js +60 -0
  82. package/dist/fec-starvation-detector.js.map +7 -0
  83. package/dist/fec-state.d.ts +131 -0
  84. package/dist/fec-state.d.ts.map +1 -0
  85. package/dist/fec-state.js +87 -0
  86. package/dist/fec-state.js.map +7 -0
  87. package/dist/fec-worker-setup.d.ts +94 -0
  88. package/dist/fec-worker-setup.d.ts.map +1 -0
  89. package/dist/fec-worker-setup.js +673 -0
  90. package/dist/fec-worker-setup.js.map +7 -0
  91. package/dist/fmp4-completion.d.ts +30 -0
  92. package/dist/fmp4-completion.d.ts.map +1 -0
  93. package/dist/fmp4-completion.js +21 -0
  94. package/dist/fmp4-completion.js.map +7 -0
  95. package/dist/fmp4-reader.d.ts +37 -0
  96. package/dist/fmp4-reader.d.ts.map +1 -0
  97. package/dist/fmp4-reader.js +100 -0
  98. package/dist/fmp4-reader.js.map +7 -0
  99. package/dist/index.d.ts +44 -3
  100. package/dist/index.d.ts.map +1 -1
  101. package/dist/index.js +460 -14
  102. package/dist/index.js.map +7 -1
  103. package/dist/iso-block-locator.d.ts +118 -0
  104. package/dist/iso-block-locator.d.ts.map +1 -0
  105. package/dist/iso-block-locator.js +289 -0
  106. package/dist/iso-block-locator.js.map +7 -0
  107. package/dist/iso-bmff-timing.d.ts +78 -0
  108. package/dist/iso-bmff-timing.d.ts.map +1 -0
  109. package/dist/iso-bmff-timing.js +593 -0
  110. package/dist/iso-bmff-timing.js.map +7 -0
  111. package/dist/loc.d.ts +70 -75
  112. package/dist/loc.d.ts.map +1 -1
  113. package/dist/loc.js +106 -192
  114. package/dist/loc.js.map +7 -1
  115. package/dist/manifest-auth-plan.d.ts +30 -0
  116. package/dist/manifest-auth-plan.d.ts.map +1 -0
  117. package/dist/manifest-auth-plan.js +1266 -0
  118. package/dist/manifest-auth-plan.js.map +7 -0
  119. package/dist/media-router.d.ts +262 -0
  120. package/dist/media-router.d.ts.map +1 -0
  121. package/dist/media-router.js +584 -0
  122. package/dist/media-router.js.map +7 -0
  123. package/dist/mfu-reassembler.d.ts +313 -9
  124. package/dist/mfu-reassembler.d.ts.map +1 -1
  125. package/dist/mfu-reassembler.js +3051 -285
  126. package/dist/mfu-reassembler.js.map +7 -1
  127. package/dist/mfu-reassembly-policy.d.ts +24 -0
  128. package/dist/mfu-reassembly-policy.d.ts.map +1 -0
  129. package/dist/mfu-reassembly-policy.js +99 -0
  130. package/dist/mfu-reassembly-policy.js.map +7 -0
  131. package/dist/mmtp-container-parser.d.ts +568 -0
  132. package/dist/mmtp-container-parser.d.ts.map +1 -0
  133. package/dist/mmtp-container-parser.js +6580 -0
  134. package/dist/mmtp-container-parser.js.map +7 -0
  135. package/dist/mmtp-container-parser.no-modules.js +9616 -0
  136. package/dist/mmtp-container-parser.no-modules.js.map +7 -0
  137. package/dist/mmtp-decode-pipeline.d.ts +138 -0
  138. package/dist/mmtp-decode-pipeline.d.ts.map +1 -0
  139. package/dist/mmtp-decode-pipeline.js +6814 -0
  140. package/dist/mmtp-decode-pipeline.js.map +7 -0
  141. package/dist/mmtp-router.d.ts +679 -0
  142. package/dist/mmtp-router.d.ts.map +1 -0
  143. package/dist/mmtp-router.js +11278 -0
  144. package/dist/mmtp-router.js.map +7 -0
  145. package/dist/mmtp-to-cmaf.d.ts +187 -0
  146. package/dist/mmtp-to-cmaf.d.ts.map +1 -0
  147. package/dist/mmtp-to-cmaf.js +716 -0
  148. package/dist/mmtp-to-cmaf.js.map +7 -0
  149. package/dist/multicast-auth-catalog.d.ts +115 -0
  150. package/dist/multicast-auth-catalog.d.ts.map +1 -0
  151. package/dist/multicast-auth-catalog.js +202 -0
  152. package/dist/multicast-auth-catalog.js.map +7 -0
  153. package/dist/multicast-input.d.ts +141 -0
  154. package/dist/multicast-input.d.ts.map +1 -0
  155. package/dist/multicast-input.js +390 -0
  156. package/dist/multicast-input.js.map +7 -0
  157. package/dist/multicast-manager.d.ts +669 -0
  158. package/dist/multicast-manager.d.ts.map +1 -0
  159. package/dist/multicast-manager.js +19101 -0
  160. package/dist/multicast-manager.js.map +7 -0
  161. package/dist/multicast-transport.d.ts +271 -0
  162. package/dist/multicast-transport.d.ts.map +1 -0
  163. package/dist/multicast-transport.js +2264 -0
  164. package/dist/multicast-transport.js.map +7 -0
  165. package/dist/multicast-unwrap.d.ts +57 -0
  166. package/dist/multicast-unwrap.d.ts.map +1 -0
  167. package/dist/multicast-unwrap.js +418 -0
  168. package/dist/multicast-unwrap.js.map +7 -0
  169. package/dist/no-modules.d.ts +10 -0
  170. package/dist/no-modules.d.ts.map +1 -0
  171. package/dist/no-modules.js +9491 -0
  172. package/dist/no-modules.js.map +7 -0
  173. package/dist/phase14-catalog-override.d.ts +126 -0
  174. package/dist/phase14-catalog-override.d.ts.map +1 -0
  175. package/dist/phase14-catalog-override.js +155 -0
  176. package/dist/phase14-catalog-override.js.map +7 -0
  177. package/dist/repair-fetch.d.ts +270 -0
  178. package/dist/repair-fetch.d.ts.map +1 -0
  179. package/dist/repair-fetch.js +381 -0
  180. package/dist/repair-fetch.js.map +7 -0
  181. package/dist/repair-layers.d.ts +158 -0
  182. package/dist/repair-layers.d.ts.map +1 -0
  183. package/dist/repair-layers.js +344 -0
  184. package/dist/repair-layers.js.map +7 -0
  185. package/dist/sample-combiner.d.ts +212 -0
  186. package/dist/sample-combiner.d.ts.map +1 -0
  187. package/dist/sample-combiner.js +595 -0
  188. package/dist/sample-combiner.js.map +7 -0
  189. package/dist/signaling.d.ts +29 -0
  190. package/dist/signaling.d.ts.map +1 -0
  191. package/dist/signaling.js +572 -0
  192. package/dist/signaling.js.map +7 -0
  193. package/dist/source-auth.d.ts +2 -0
  194. package/dist/source-auth.d.ts.map +1 -0
  195. package/dist/source-auth.js +1209 -0
  196. package/dist/source-auth.js.map +7 -0
  197. package/dist/switching-set-alignment.d.ts +61 -0
  198. package/dist/switching-set-alignment.d.ts.map +1 -0
  199. package/dist/switching-set-alignment.js +70 -0
  200. package/dist/switching-set-alignment.js.map +7 -0
  201. package/dist/timestamp-codec.d.ts +34 -0
  202. package/dist/timestamp-codec.d.ts.map +1 -0
  203. package/dist/timestamp-codec.js +473 -0
  204. package/dist/timestamp-codec.js.map +7 -0
  205. package/dist/track-pipeline-setup.d.ts +83 -0
  206. package/dist/track-pipeline-setup.d.ts.map +1 -0
  207. package/dist/track-pipeline-setup.js +291 -0
  208. package/dist/track-pipeline-setup.js.map +7 -0
  209. package/dist/track-pipeline.d.ts +121 -0
  210. package/dist/track-pipeline.d.ts.map +1 -0
  211. package/dist/track-pipeline.js +209 -0
  212. package/dist/track-pipeline.js.map +7 -0
  213. package/dist/track-session.d.ts +414 -0
  214. package/dist/track-session.d.ts.map +1 -0
  215. package/dist/track-session.js +754 -0
  216. package/dist/track-session.js.map +7 -0
  217. package/dist/transport-pool.d.ts +69 -0
  218. package/dist/transport-pool.d.ts.map +1 -0
  219. package/dist/transport-pool.js +74 -0
  220. package/dist/transport-pool.js.map +7 -0
  221. package/dist/wasm-mfu-reassembler.d.ts +97 -0
  222. package/dist/wasm-mfu-reassembler.d.ts.map +1 -0
  223. package/dist/wasm-mfu-reassembler.js +2133 -0
  224. package/dist/wasm-mfu-reassembler.js.map +7 -0
  225. package/dist/wasm-mmtp-container-parser.d.ts +10 -0
  226. package/dist/wasm-mmtp-container-parser.d.ts.map +1 -0
  227. package/dist/wasm-mmtp-container-parser.js +6834 -0
  228. package/dist/wasm-mmtp-container-parser.js.map +7 -0
  229. package/node_modules/@blockcast/fec-worker/README.md +223 -0
  230. package/node_modules/@blockcast/fec-worker/dist/.build-stamp +0 -0
  231. package/node_modules/@blockcast/fec-worker/dist/block-order.d.ts +38 -0
  232. package/node_modules/@blockcast/fec-worker/dist/block-order.d.ts.map +1 -0
  233. package/node_modules/@blockcast/fec-worker/dist/block-order.js +78 -0
  234. package/node_modules/@blockcast/fec-worker/dist/block-order.js.map +1 -0
  235. package/node_modules/@blockcast/fec-worker/dist/fec-worker-client.d.ts +93 -0
  236. package/node_modules/@blockcast/fec-worker/dist/fec-worker-client.d.ts.map +1 -0
  237. package/node_modules/@blockcast/fec-worker/dist/fec-worker-client.js +346 -0
  238. package/node_modules/@blockcast/fec-worker/dist/fec-worker-client.js.map +1 -0
  239. package/node_modules/@blockcast/fec-worker/dist/fec-worker-types.d.ts +380 -0
  240. package/node_modules/@blockcast/fec-worker/dist/fec-worker-types.d.ts.map +1 -0
  241. package/node_modules/@blockcast/fec-worker/dist/fec-worker-types.js +11 -0
  242. package/node_modules/@blockcast/fec-worker/dist/fec-worker-types.js.map +1 -0
  243. package/node_modules/@blockcast/fec-worker/dist/fec-worker.d.ts +14 -0
  244. package/node_modules/@blockcast/fec-worker/dist/fec-worker.d.ts.map +1 -0
  245. package/node_modules/@blockcast/fec-worker/dist/fec-worker.js +859 -0
  246. package/node_modules/@blockcast/fec-worker/dist/fec-worker.js.map +7 -0
  247. package/node_modules/@blockcast/fec-worker/dist/index.d.ts +10 -0
  248. package/node_modules/@blockcast/fec-worker/dist/index.d.ts.map +1 -0
  249. package/node_modules/@blockcast/fec-worker/dist/index.js +8 -0
  250. package/node_modules/@blockcast/fec-worker/dist/index.js.map +1 -0
  251. package/node_modules/@blockcast/fec-worker/dist/shred-fec-worker.d.ts +20 -0
  252. package/node_modules/@blockcast/fec-worker/dist/shred-fec-worker.d.ts.map +1 -0
  253. package/node_modules/@blockcast/fec-worker/dist/shred-fec-worker.js +370 -0
  254. package/node_modules/@blockcast/fec-worker/dist/shred-fec-worker.js.map +7 -0
  255. package/node_modules/@blockcast/fec-worker/dist/shred-worker-client.d.ts +104 -0
  256. package/node_modules/@blockcast/fec-worker/dist/shred-worker-client.d.ts.map +1 -0
  257. package/node_modules/@blockcast/fec-worker/dist/shred-worker-client.js +181 -0
  258. package/node_modules/@blockcast/fec-worker/dist/shred-worker-client.js.map +1 -0
  259. package/node_modules/@blockcast/fec-worker/dist/shred-worker-types.d.ts +194 -0
  260. package/node_modules/@blockcast/fec-worker/dist/shred-worker-types.d.ts.map +1 -0
  261. package/node_modules/@blockcast/fec-worker/dist/shred-worker-types.js +62 -0
  262. package/node_modules/@blockcast/fec-worker/dist/shred-worker-types.js.map +1 -0
  263. package/node_modules/@blockcast/fec-worker/dist/transfer.d.ts +9 -0
  264. package/node_modules/@blockcast/fec-worker/dist/transfer.d.ts.map +1 -0
  265. package/node_modules/@blockcast/fec-worker/dist/transfer.js +13 -0
  266. package/node_modules/@blockcast/fec-worker/dist/transfer.js.map +1 -0
  267. package/node_modules/@blockcast/fec-worker/package.json +59 -0
  268. package/node_modules/@blockcast/fec-worker/src/block-order.ts +93 -0
  269. package/node_modules/@blockcast/fec-worker/src/fec-worker-client.ts +392 -0
  270. package/node_modules/@blockcast/fec-worker/src/fec-worker-types.ts +399 -0
  271. package/node_modules/@blockcast/fec-worker/src/fec-worker.ts +1502 -0
  272. package/node_modules/@blockcast/fec-worker/src/index.ts +35 -0
  273. package/node_modules/@blockcast/fec-worker/src/shred-fec-worker.ts +589 -0
  274. package/node_modules/@blockcast/fec-worker/src/shred-worker-client.ts +222 -0
  275. package/node_modules/@blockcast/fec-worker/src/shred-worker-types.ts +209 -0
  276. package/node_modules/@blockcast/fec-worker/src/transfer.ts +12 -0
  277. package/node_modules/@blockcast/mmt-alta-parse/README.md +181 -0
  278. package/node_modules/@blockcast/mmt-alta-parse/dist/.build-stamp +0 -0
  279. package/node_modules/@blockcast/mmt-alta-parse/dist/index.d.ts +57 -0
  280. package/node_modules/@blockcast/mmt-alta-parse/dist/index.d.ts.map +1 -0
  281. package/node_modules/@blockcast/mmt-alta-parse/dist/index.js +115 -0
  282. package/node_modules/@blockcast/mmt-alta-parse/dist/index.js.map +1 -0
  283. package/node_modules/@blockcast/mmt-alta-parse/package.json +50 -0
  284. package/node_modules/@blockcast/mmt-alta-parse/src/index.ts +136 -0
  285. package/node_modules/@blockcast/mmt-base/README.md +134 -0
  286. package/node_modules/@blockcast/mmt-base/dist/.build-stamp +0 -0
  287. package/node_modules/@blockcast/mmt-base/dist/fec-block-profile.d.ts +55 -0
  288. package/node_modules/@blockcast/mmt-base/dist/fec-block-profile.d.ts.map +1 -0
  289. package/node_modules/@blockcast/mmt-base/dist/fec-block-profile.js +128 -0
  290. package/node_modules/@blockcast/mmt-base/dist/fec-block-profile.js.map +1 -0
  291. package/node_modules/@blockcast/mmt-base/dist/index.d.ts +7 -0
  292. package/node_modules/@blockcast/mmt-base/dist/index.d.ts.map +1 -0
  293. package/node_modules/@blockcast/mmt-base/dist/index.js +7 -0
  294. package/node_modules/@blockcast/mmt-base/dist/index.js.map +1 -0
  295. package/node_modules/@blockcast/mmt-base/dist/mmtp.d.ts +292 -0
  296. package/node_modules/@blockcast/mmt-base/dist/mmtp.d.ts.map +1 -0
  297. package/node_modules/@blockcast/mmt-base/dist/mmtp.js +612 -0
  298. package/node_modules/@blockcast/mmt-base/dist/mmtp.js.map +1 -0
  299. package/node_modules/@blockcast/mmt-base/dist/net-constants.d.ts +18 -0
  300. package/node_modules/@blockcast/mmt-base/dist/net-constants.d.ts.map +1 -0
  301. package/node_modules/@blockcast/mmt-base/dist/net-constants.js +18 -0
  302. package/node_modules/@blockcast/mmt-base/dist/net-constants.js.map +1 -0
  303. package/node_modules/@blockcast/mmt-base/dist/ntp-short.d.ts +47 -0
  304. package/node_modules/@blockcast/mmt-base/dist/ntp-short.d.ts.map +1 -0
  305. package/node_modules/@blockcast/mmt-base/dist/ntp-short.js +54 -0
  306. package/node_modules/@blockcast/mmt-base/dist/ntp-short.js.map +1 -0
  307. package/node_modules/@blockcast/mmt-base/dist/serial-number.d.ts +57 -0
  308. package/node_modules/@blockcast/mmt-base/dist/serial-number.d.ts.map +1 -0
  309. package/node_modules/@blockcast/mmt-base/dist/serial-number.js +98 -0
  310. package/node_modules/@blockcast/mmt-base/dist/serial-number.js.map +1 -0
  311. package/node_modules/@blockcast/mmt-base/dist/signalling.d.ts +312 -0
  312. package/node_modules/@blockcast/mmt-base/dist/signalling.d.ts.map +1 -0
  313. package/node_modules/@blockcast/mmt-base/dist/signalling.js +637 -0
  314. package/node_modules/@blockcast/mmt-base/dist/signalling.js.map +1 -0
  315. package/node_modules/@blockcast/mmt-base/package.json +49 -0
  316. package/node_modules/@blockcast/mmt-base/src/fec-block-profile.ts +145 -0
  317. package/node_modules/@blockcast/mmt-base/src/index.ts +6 -0
  318. package/node_modules/@blockcast/mmt-base/src/mmtp.ts +892 -0
  319. package/node_modules/@blockcast/mmt-base/src/net-constants.ts +17 -0
  320. package/node_modules/@blockcast/mmt-base/src/ntp-short.ts +67 -0
  321. package/node_modules/@blockcast/mmt-base/src/serial-number.ts +107 -0
  322. package/node_modules/@blockcast/mmt-base/src/signalling.ts +862 -0
  323. package/node_modules/@blockcast/mmt-fec/README.md +271 -0
  324. package/node_modules/@blockcast/mmt-fec/dist/.build-stamp +0 -0
  325. package/node_modules/@blockcast/mmt-fec/dist/block-manager.d.ts +82 -0
  326. package/node_modules/@blockcast/mmt-fec/dist/block-manager.d.ts.map +1 -0
  327. package/node_modules/@blockcast/mmt-fec/dist/block-manager.js +296 -0
  328. package/node_modules/@blockcast/mmt-fec/dist/block-manager.js.map +1 -0
  329. package/node_modules/@blockcast/mmt-fec/dist/block-processor.d.ts +106 -0
  330. package/node_modules/@blockcast/mmt-fec/dist/block-processor.d.ts.map +1 -0
  331. package/node_modules/@blockcast/mmt-fec/dist/block-processor.js +288 -0
  332. package/node_modules/@blockcast/mmt-fec/dist/block-processor.js.map +1 -0
  333. package/node_modules/@blockcast/mmt-fec/dist/cmaf-recovery.d.ts +46 -0
  334. package/node_modules/@blockcast/mmt-fec/dist/cmaf-recovery.d.ts.map +1 -0
  335. package/node_modules/@blockcast/mmt-fec/dist/cmaf-recovery.js +96 -0
  336. package/node_modules/@blockcast/mmt-fec/dist/cmaf-recovery.js.map +1 -0
  337. package/node_modules/@blockcast/mmt-fec/dist/cmaf-symbols.d.ts +33 -0
  338. package/node_modules/@blockcast/mmt-fec/dist/cmaf-symbols.d.ts.map +1 -0
  339. package/node_modules/@blockcast/mmt-fec/dist/cmaf-symbols.js +57 -0
  340. package/node_modules/@blockcast/mmt-fec/dist/cmaf-symbols.js.map +1 -0
  341. package/node_modules/@blockcast/mmt-fec/dist/fec-block-mapper.d.ts +205 -0
  342. package/node_modules/@blockcast/mmt-fec/dist/fec-block-mapper.d.ts.map +1 -0
  343. package/node_modules/@blockcast/mmt-fec/dist/fec-block-mapper.js +240 -0
  344. package/node_modules/@blockcast/mmt-fec/dist/fec-block-mapper.js.map +1 -0
  345. package/node_modules/@blockcast/mmt-fec/dist/index.d.ts +28 -0
  346. package/node_modules/@blockcast/mmt-fec/dist/index.d.ts.map +1 -0
  347. package/node_modules/@blockcast/mmt-fec/dist/index.js +28 -0
  348. package/node_modules/@blockcast/mmt-fec/dist/index.js.map +1 -0
  349. package/node_modules/@blockcast/mmt-fec/dist/loc-symbols.d.ts +20 -0
  350. package/node_modules/@blockcast/mmt-fec/dist/loc-symbols.d.ts.map +1 -0
  351. package/node_modules/@blockcast/mmt-fec/dist/loc-symbols.js +28 -0
  352. package/node_modules/@blockcast/mmt-fec/dist/loc-symbols.js.map +1 -0
  353. package/node_modules/@blockcast/mmt-fec/dist/oti.d.ts +258 -0
  354. package/node_modules/@blockcast/mmt-fec/dist/oti.d.ts.map +1 -0
  355. package/node_modules/@blockcast/mmt-fec/dist/oti.js +382 -0
  356. package/node_modules/@blockcast/mmt-fec/dist/oti.js.map +1 -0
  357. package/node_modules/@blockcast/mmt-fec/dist/reorder-buffer.d.ts +41 -0
  358. package/node_modules/@blockcast/mmt-fec/dist/reorder-buffer.d.ts.map +1 -0
  359. package/node_modules/@blockcast/mmt-fec/dist/reorder-buffer.js +81 -0
  360. package/node_modules/@blockcast/mmt-fec/dist/reorder-buffer.js.map +1 -0
  361. package/node_modules/@blockcast/mmt-fec/dist/repair-group.d.ts +82 -0
  362. package/node_modules/@blockcast/mmt-fec/dist/repair-group.d.ts.map +1 -0
  363. package/node_modules/@blockcast/mmt-fec/dist/repair-group.js +170 -0
  364. package/node_modules/@blockcast/mmt-fec/dist/repair-group.js.map +1 -0
  365. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-alta-parse/README.md +181 -0
  366. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-alta-parse/dist/.build-stamp +0 -0
  367. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-alta-parse/dist/index.d.ts +57 -0
  368. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-alta-parse/dist/index.d.ts.map +1 -0
  369. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-alta-parse/dist/index.js +115 -0
  370. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-alta-parse/dist/index.js.map +1 -0
  371. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-alta-parse/package.json +50 -0
  372. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-alta-parse/src/index.ts +136 -0
  373. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/README.md +134 -0
  374. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/.build-stamp +0 -0
  375. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/fec-block-profile.d.ts +55 -0
  376. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/fec-block-profile.d.ts.map +1 -0
  377. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/fec-block-profile.js +128 -0
  378. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/fec-block-profile.js.map +1 -0
  379. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/index.d.ts +7 -0
  380. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/index.d.ts.map +1 -0
  381. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/index.js +7 -0
  382. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/index.js.map +1 -0
  383. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/mmtp.d.ts +292 -0
  384. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/mmtp.d.ts.map +1 -0
  385. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/mmtp.js +612 -0
  386. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/mmtp.js.map +1 -0
  387. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/net-constants.d.ts +18 -0
  388. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/net-constants.d.ts.map +1 -0
  389. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/net-constants.js +18 -0
  390. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/net-constants.js.map +1 -0
  391. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/ntp-short.d.ts +47 -0
  392. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/ntp-short.d.ts.map +1 -0
  393. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/ntp-short.js +54 -0
  394. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/ntp-short.js.map +1 -0
  395. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/serial-number.d.ts +57 -0
  396. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/serial-number.d.ts.map +1 -0
  397. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/serial-number.js +98 -0
  398. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/serial-number.js.map +1 -0
  399. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/signalling.d.ts +312 -0
  400. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/signalling.d.ts.map +1 -0
  401. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/signalling.js +637 -0
  402. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/signalling.js.map +1 -0
  403. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/package.json +49 -0
  404. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/src/fec-block-profile.ts +145 -0
  405. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/src/index.ts +6 -0
  406. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/src/mmtp.ts +892 -0
  407. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/src/net-constants.ts +17 -0
  408. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/src/ntp-short.ts +67 -0
  409. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/src/serial-number.ts +107 -0
  410. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/src/signalling.ts +862 -0
  411. package/node_modules/@blockcast/mmt-fec/package.json +79 -0
  412. package/node_modules/@blockcast/mmt-fec/src/block-manager.ts +371 -0
  413. package/node_modules/@blockcast/mmt-fec/src/block-processor.ts +439 -0
  414. package/node_modules/@blockcast/mmt-fec/src/cmaf-recovery.ts +127 -0
  415. package/node_modules/@blockcast/mmt-fec/src/cmaf-symbols.ts +68 -0
  416. package/node_modules/@blockcast/mmt-fec/src/fec-block-mapper.ts +310 -0
  417. package/node_modules/@blockcast/mmt-fec/src/index.ts +94 -0
  418. package/node_modules/@blockcast/mmt-fec/src/loc-symbols.ts +34 -0
  419. package/node_modules/@blockcast/mmt-fec/src/oti.ts +463 -0
  420. package/node_modules/@blockcast/mmt-fec/src/reorder-buffer.ts +101 -0
  421. package/node_modules/@blockcast/mmt-fec/src/repair-group.ts +195 -0
  422. package/node_modules/@blockcast/mmt-manifest-verify/dist/.build-stamp +0 -0
  423. package/node_modules/@blockcast/mmt-manifest-verify/dist/admission.d.ts +28 -0
  424. package/node_modules/@blockcast/mmt-manifest-verify/dist/admission.d.ts.map +1 -0
  425. package/node_modules/@blockcast/mmt-manifest-verify/dist/admission.js +128 -0
  426. package/node_modules/@blockcast/mmt-manifest-verify/dist/admission.js.map +1 -0
  427. package/node_modules/@blockcast/mmt-manifest-verify/dist/index.d.ts +4 -0
  428. package/node_modules/@blockcast/mmt-manifest-verify/dist/index.d.ts.map +1 -0
  429. package/node_modules/@blockcast/mmt-manifest-verify/dist/index.js +4 -0
  430. package/node_modules/@blockcast/mmt-manifest-verify/dist/index.js.map +1 -0
  431. package/node_modules/@blockcast/mmt-manifest-verify/dist/source-auth.d.ts +37 -0
  432. package/node_modules/@blockcast/mmt-manifest-verify/dist/source-auth.d.ts.map +1 -0
  433. package/node_modules/@blockcast/mmt-manifest-verify/dist/source-auth.js +179 -0
  434. package/node_modules/@blockcast/mmt-manifest-verify/dist/source-auth.js.map +1 -0
  435. package/node_modules/@blockcast/mmt-manifest-verify/dist/verifier.d.ts +99 -0
  436. package/node_modules/@blockcast/mmt-manifest-verify/dist/verifier.d.ts.map +1 -0
  437. package/node_modules/@blockcast/mmt-manifest-verify/dist/verifier.js +492 -0
  438. package/node_modules/@blockcast/mmt-manifest-verify/dist/verifier.js.map +1 -0
  439. package/node_modules/@blockcast/mmt-manifest-verify/package.json +45 -0
  440. package/node_modules/@blockcast/mmt-manifest-verify/src/admission.ts +171 -0
  441. package/node_modules/@blockcast/mmt-manifest-verify/src/index.ts +3 -0
  442. package/node_modules/@blockcast/mmt-manifest-verify/src/source-auth.ts +230 -0
  443. package/node_modules/@blockcast/mmt-manifest-verify/src/verifier.ts +669 -0
  444. package/node_modules/@noble/hashes/LICENSE +21 -0
  445. package/node_modules/@noble/hashes/README.md +521 -0
  446. package/node_modules/@noble/hashes/_assert.d.ts +17 -0
  447. package/node_modules/@noble/hashes/_assert.d.ts.map +1 -0
  448. package/node_modules/@noble/hashes/_assert.js +18 -0
  449. package/node_modules/@noble/hashes/_assert.js.map +1 -0
  450. package/node_modules/@noble/hashes/_blake.d.ts +14 -0
  451. package/node_modules/@noble/hashes/_blake.d.ts.map +1 -0
  452. package/node_modules/@noble/hashes/_blake.js +50 -0
  453. package/node_modules/@noble/hashes/_blake.js.map +1 -0
  454. package/node_modules/@noble/hashes/_md.d.ts +51 -0
  455. package/node_modules/@noble/hashes/_md.d.ts.map +1 -0
  456. package/node_modules/@noble/hashes/_md.js +162 -0
  457. package/node_modules/@noble/hashes/_md.js.map +1 -0
  458. package/node_modules/@noble/hashes/_u64.d.ts +55 -0
  459. package/node_modules/@noble/hashes/_u64.d.ts.map +1 -0
  460. package/node_modules/@noble/hashes/_u64.js +90 -0
  461. package/node_modules/@noble/hashes/_u64.js.map +1 -0
  462. package/node_modules/@noble/hashes/argon2.d.ts +32 -0
  463. package/node_modules/@noble/hashes/argon2.d.ts.map +1 -0
  464. package/node_modules/@noble/hashes/argon2.js +401 -0
  465. package/node_modules/@noble/hashes/argon2.js.map +1 -0
  466. package/node_modules/@noble/hashes/blake1.d.ts +106 -0
  467. package/node_modules/@noble/hashes/blake1.d.ts.map +1 -0
  468. package/node_modules/@noble/hashes/blake1.js +459 -0
  469. package/node_modules/@noble/hashes/blake1.js.map +1 -0
  470. package/node_modules/@noble/hashes/blake2.d.ts +116 -0
  471. package/node_modules/@noble/hashes/blake2.d.ts.map +1 -0
  472. package/node_modules/@noble/hashes/blake2.js +420 -0
  473. package/node_modules/@noble/hashes/blake2.js.map +1 -0
  474. package/node_modules/@noble/hashes/blake2b.d.ts +11 -0
  475. package/node_modules/@noble/hashes/blake2b.d.ts.map +1 -0
  476. package/node_modules/@noble/hashes/blake2b.js +14 -0
  477. package/node_modules/@noble/hashes/blake2b.js.map +1 -0
  478. package/node_modules/@noble/hashes/blake2s.d.ts +20 -0
  479. package/node_modules/@noble/hashes/blake2s.d.ts.map +1 -0
  480. package/node_modules/@noble/hashes/blake2s.js +24 -0
  481. package/node_modules/@noble/hashes/blake2s.js.map +1 -0
  482. package/node_modules/@noble/hashes/blake3.d.ts +54 -0
  483. package/node_modules/@noble/hashes/blake3.d.ts.map +1 -0
  484. package/node_modules/@noble/hashes/blake3.js +255 -0
  485. package/node_modules/@noble/hashes/blake3.js.map +1 -0
  486. package/node_modules/@noble/hashes/crypto.d.ts +2 -0
  487. package/node_modules/@noble/hashes/crypto.d.ts.map +1 -0
  488. package/node_modules/@noble/hashes/crypto.js +5 -0
  489. package/node_modules/@noble/hashes/crypto.js.map +1 -0
  490. package/node_modules/@noble/hashes/cryptoNode.d.ts +2 -0
  491. package/node_modules/@noble/hashes/cryptoNode.d.ts.map +1 -0
  492. package/node_modules/@noble/hashes/cryptoNode.js +18 -0
  493. package/node_modules/@noble/hashes/cryptoNode.js.map +1 -0
  494. package/node_modules/@noble/hashes/eskdf.d.ts +47 -0
  495. package/node_modules/@noble/hashes/eskdf.d.ts.map +1 -0
  496. package/node_modules/@noble/hashes/eskdf.js +166 -0
  497. package/node_modules/@noble/hashes/eskdf.js.map +1 -0
  498. package/node_modules/@noble/hashes/esm/_assert.d.ts +17 -0
  499. package/node_modules/@noble/hashes/esm/_assert.d.ts.map +1 -0
  500. package/node_modules/@noble/hashes/esm/_assert.js +15 -0
  501. package/node_modules/@noble/hashes/esm/_assert.js.map +1 -0
  502. package/node_modules/@noble/hashes/esm/_blake.d.ts +14 -0
  503. package/node_modules/@noble/hashes/esm/_blake.d.ts.map +1 -0
  504. package/node_modules/@noble/hashes/esm/_blake.js +45 -0
  505. package/node_modules/@noble/hashes/esm/_blake.js.map +1 -0
  506. package/node_modules/@noble/hashes/esm/_md.d.ts +51 -0
  507. package/node_modules/@noble/hashes/esm/_md.d.ts.map +1 -0
  508. package/node_modules/@noble/hashes/esm/_md.js +155 -0
  509. package/node_modules/@noble/hashes/esm/_md.js.map +1 -0
  510. package/node_modules/@noble/hashes/esm/_u64.d.ts +55 -0
  511. package/node_modules/@noble/hashes/esm/_u64.d.ts.map +1 -0
  512. package/node_modules/@noble/hashes/esm/_u64.js +67 -0
  513. package/node_modules/@noble/hashes/esm/_u64.js.map +1 -0
  514. package/node_modules/@noble/hashes/esm/argon2.d.ts +32 -0
  515. package/node_modules/@noble/hashes/esm/argon2.d.ts.map +1 -0
  516. package/node_modules/@noble/hashes/esm/argon2.js +392 -0
  517. package/node_modules/@noble/hashes/esm/argon2.js.map +1 -0
  518. package/node_modules/@noble/hashes/esm/blake1.d.ts +106 -0
  519. package/node_modules/@noble/hashes/esm/blake1.d.ts.map +1 -0
  520. package/node_modules/@noble/hashes/esm/blake1.js +452 -0
  521. package/node_modules/@noble/hashes/esm/blake1.js.map +1 -0
  522. package/node_modules/@noble/hashes/esm/blake2.d.ts +116 -0
  523. package/node_modules/@noble/hashes/esm/blake2.d.ts.map +1 -0
  524. package/node_modules/@noble/hashes/esm/blake2.js +413 -0
  525. package/node_modules/@noble/hashes/esm/blake2.js.map +1 -0
  526. package/node_modules/@noble/hashes/esm/blake2b.d.ts +11 -0
  527. package/node_modules/@noble/hashes/esm/blake2b.d.ts.map +1 -0
  528. package/node_modules/@noble/hashes/esm/blake2b.js +11 -0
  529. package/node_modules/@noble/hashes/esm/blake2b.js.map +1 -0
  530. package/node_modules/@noble/hashes/esm/blake2s.d.ts +20 -0
  531. package/node_modules/@noble/hashes/esm/blake2s.d.ts.map +1 -0
  532. package/node_modules/@noble/hashes/esm/blake2s.js +21 -0
  533. package/node_modules/@noble/hashes/esm/blake2s.js.map +1 -0
  534. package/node_modules/@noble/hashes/esm/blake3.d.ts +54 -0
  535. package/node_modules/@noble/hashes/esm/blake3.d.ts.map +1 -0
  536. package/node_modules/@noble/hashes/esm/blake3.js +251 -0
  537. package/node_modules/@noble/hashes/esm/blake3.js.map +1 -0
  538. package/node_modules/@noble/hashes/esm/crypto.d.ts +2 -0
  539. package/node_modules/@noble/hashes/esm/crypto.d.ts.map +1 -0
  540. package/node_modules/@noble/hashes/esm/crypto.js +2 -0
  541. package/node_modules/@noble/hashes/esm/crypto.js.map +1 -0
  542. package/node_modules/@noble/hashes/esm/cryptoNode.d.ts +2 -0
  543. package/node_modules/@noble/hashes/esm/cryptoNode.d.ts.map +1 -0
  544. package/node_modules/@noble/hashes/esm/cryptoNode.js +15 -0
  545. package/node_modules/@noble/hashes/esm/cryptoNode.js.map +1 -0
  546. package/node_modules/@noble/hashes/esm/eskdf.d.ts +47 -0
  547. package/node_modules/@noble/hashes/esm/eskdf.d.ts.map +1 -0
  548. package/node_modules/@noble/hashes/esm/eskdf.js +160 -0
  549. package/node_modules/@noble/hashes/esm/eskdf.js.map +1 -0
  550. package/node_modules/@noble/hashes/esm/hkdf.d.ts +36 -0
  551. package/node_modules/@noble/hashes/esm/hkdf.d.ts.map +1 -0
  552. package/node_modules/@noble/hashes/esm/hkdf.js +82 -0
  553. package/node_modules/@noble/hashes/esm/hkdf.js.map +1 -0
  554. package/node_modules/@noble/hashes/esm/hmac.d.ts +35 -0
  555. package/node_modules/@noble/hashes/esm/hmac.d.ts.map +1 -0
  556. package/node_modules/@noble/hashes/esm/hmac.js +86 -0
  557. package/node_modules/@noble/hashes/esm/hmac.js.map +1 -0
  558. package/node_modules/@noble/hashes/esm/index.d.ts +2 -0
  559. package/node_modules/@noble/hashes/esm/index.d.ts.map +1 -0
  560. package/node_modules/@noble/hashes/esm/index.js +33 -0
  561. package/node_modules/@noble/hashes/esm/index.js.map +1 -0
  562. package/node_modules/@noble/hashes/esm/legacy.d.ts +71 -0
  563. package/node_modules/@noble/hashes/esm/legacy.d.ts.map +1 -0
  564. package/node_modules/@noble/hashes/esm/legacy.js +281 -0
  565. package/node_modules/@noble/hashes/esm/legacy.js.map +1 -0
  566. package/node_modules/@noble/hashes/esm/package.json +10 -0
  567. package/node_modules/@noble/hashes/esm/pbkdf2.d.ts +23 -0
  568. package/node_modules/@noble/hashes/esm/pbkdf2.d.ts.map +1 -0
  569. package/node_modules/@noble/hashes/esm/pbkdf2.js +97 -0
  570. package/node_modules/@noble/hashes/esm/pbkdf2.js.map +1 -0
  571. package/node_modules/@noble/hashes/esm/ripemd160.d.ts +13 -0
  572. package/node_modules/@noble/hashes/esm/ripemd160.d.ts.map +1 -0
  573. package/node_modules/@noble/hashes/esm/ripemd160.js +13 -0
  574. package/node_modules/@noble/hashes/esm/ripemd160.js.map +1 -0
  575. package/node_modules/@noble/hashes/esm/scrypt.d.ts +34 -0
  576. package/node_modules/@noble/hashes/esm/scrypt.d.ts.map +1 -0
  577. package/node_modules/@noble/hashes/esm/scrypt.js +228 -0
  578. package/node_modules/@noble/hashes/esm/scrypt.js.map +1 -0
  579. package/node_modules/@noble/hashes/esm/sha1.d.ts +11 -0
  580. package/node_modules/@noble/hashes/esm/sha1.d.ts.map +1 -0
  581. package/node_modules/@noble/hashes/esm/sha1.js +11 -0
  582. package/node_modules/@noble/hashes/esm/sha1.js.map +1 -0
  583. package/node_modules/@noble/hashes/esm/sha2.d.ts +159 -0
  584. package/node_modules/@noble/hashes/esm/sha2.d.ts.map +1 -0
  585. package/node_modules/@noble/hashes/esm/sha2.js +375 -0
  586. package/node_modules/@noble/hashes/esm/sha2.js.map +1 -0
  587. package/node_modules/@noble/hashes/esm/sha256.d.ts +20 -0
  588. package/node_modules/@noble/hashes/esm/sha256.d.ts.map +1 -0
  589. package/node_modules/@noble/hashes/esm/sha256.js +20 -0
  590. package/node_modules/@noble/hashes/esm/sha256.js.map +1 -0
  591. package/node_modules/@noble/hashes/esm/sha3-addons.d.ts +142 -0
  592. package/node_modules/@noble/hashes/esm/sha3-addons.d.ts.map +1 -0
  593. package/node_modules/@noble/hashes/esm/sha3-addons.js +393 -0
  594. package/node_modules/@noble/hashes/esm/sha3-addons.js.map +1 -0
  595. package/node_modules/@noble/hashes/esm/sha3.d.ts +53 -0
  596. package/node_modules/@noble/hashes/esm/sha3.d.ts.map +1 -0
  597. package/node_modules/@noble/hashes/esm/sha3.js +234 -0
  598. package/node_modules/@noble/hashes/esm/sha3.js.map +1 -0
  599. package/node_modules/@noble/hashes/esm/sha512.d.ts +26 -0
  600. package/node_modules/@noble/hashes/esm/sha512.d.ts.map +1 -0
  601. package/node_modules/@noble/hashes/esm/sha512.js +26 -0
  602. package/node_modules/@noble/hashes/esm/sha512.js.map +1 -0
  603. package/node_modules/@noble/hashes/esm/utils.d.ts +161 -0
  604. package/node_modules/@noble/hashes/esm/utils.d.ts.map +1 -0
  605. package/node_modules/@noble/hashes/esm/utils.js +281 -0
  606. package/node_modules/@noble/hashes/esm/utils.js.map +1 -0
  607. package/node_modules/@noble/hashes/hkdf.d.ts +36 -0
  608. package/node_modules/@noble/hashes/hkdf.d.ts.map +1 -0
  609. package/node_modules/@noble/hashes/hkdf.js +88 -0
  610. package/node_modules/@noble/hashes/hkdf.js.map +1 -0
  611. package/node_modules/@noble/hashes/hmac.d.ts +35 -0
  612. package/node_modules/@noble/hashes/hmac.d.ts.map +1 -0
  613. package/node_modules/@noble/hashes/hmac.js +91 -0
  614. package/node_modules/@noble/hashes/hmac.js.map +1 -0
  615. package/node_modules/@noble/hashes/index.d.ts +1 -0
  616. package/node_modules/@noble/hashes/index.d.ts.map +1 -0
  617. package/node_modules/@noble/hashes/index.js +33 -0
  618. package/node_modules/@noble/hashes/index.js.map +1 -0
  619. package/node_modules/@noble/hashes/legacy.d.ts +71 -0
  620. package/node_modules/@noble/hashes/legacy.d.ts.map +1 -0
  621. package/node_modules/@noble/hashes/legacy.js +287 -0
  622. package/node_modules/@noble/hashes/legacy.js.map +1 -0
  623. package/node_modules/@noble/hashes/package.json +266 -0
  624. package/node_modules/@noble/hashes/pbkdf2.d.ts +23 -0
  625. package/node_modules/@noble/hashes/pbkdf2.d.ts.map +1 -0
  626. package/node_modules/@noble/hashes/pbkdf2.js +101 -0
  627. package/node_modules/@noble/hashes/pbkdf2.js.map +1 -0
  628. package/node_modules/@noble/hashes/ripemd160.d.ts +13 -0
  629. package/node_modules/@noble/hashes/ripemd160.d.ts.map +1 -0
  630. package/node_modules/@noble/hashes/ripemd160.js +16 -0
  631. package/node_modules/@noble/hashes/ripemd160.js.map +1 -0
  632. package/node_modules/@noble/hashes/scrypt.d.ts +34 -0
  633. package/node_modules/@noble/hashes/scrypt.d.ts.map +1 -0
  634. package/node_modules/@noble/hashes/scrypt.js +232 -0
  635. package/node_modules/@noble/hashes/scrypt.js.map +1 -0
  636. package/node_modules/@noble/hashes/sha1.d.ts +11 -0
  637. package/node_modules/@noble/hashes/sha1.d.ts.map +1 -0
  638. package/node_modules/@noble/hashes/sha1.js +14 -0
  639. package/node_modules/@noble/hashes/sha1.js.map +1 -0
  640. package/node_modules/@noble/hashes/sha2.d.ts +159 -0
  641. package/node_modules/@noble/hashes/sha2.d.ts.map +1 -0
  642. package/node_modules/@noble/hashes/sha2.js +384 -0
  643. package/node_modules/@noble/hashes/sha2.js.map +1 -0
  644. package/node_modules/@noble/hashes/sha256.d.ts +20 -0
  645. package/node_modules/@noble/hashes/sha256.d.ts.map +1 -0
  646. package/node_modules/@noble/hashes/sha256.js +23 -0
  647. package/node_modules/@noble/hashes/sha256.js.map +1 -0
  648. package/node_modules/@noble/hashes/sha3-addons.d.ts +142 -0
  649. package/node_modules/@noble/hashes/sha3-addons.d.ts.map +1 -0
  650. package/node_modules/@noble/hashes/sha3-addons.js +402 -0
  651. package/node_modules/@noble/hashes/sha3-addons.js.map +1 -0
  652. package/node_modules/@noble/hashes/sha3.d.ts +53 -0
  653. package/node_modules/@noble/hashes/sha3.d.ts.map +1 -0
  654. package/node_modules/@noble/hashes/sha3.js +239 -0
  655. package/node_modules/@noble/hashes/sha3.js.map +1 -0
  656. package/node_modules/@noble/hashes/sha512.d.ts +26 -0
  657. package/node_modules/@noble/hashes/sha512.d.ts.map +1 -0
  658. package/node_modules/@noble/hashes/sha512.js +29 -0
  659. package/node_modules/@noble/hashes/sha512.js.map +1 -0
  660. package/node_modules/@noble/hashes/src/_assert.ts +22 -0
  661. package/node_modules/@noble/hashes/src/_blake.ts +50 -0
  662. package/node_modules/@noble/hashes/src/_md.ts +176 -0
  663. package/node_modules/@noble/hashes/src/_u64.ts +91 -0
  664. package/node_modules/@noble/hashes/src/argon2.ts +497 -0
  665. package/node_modules/@noble/hashes/src/blake1.ts +534 -0
  666. package/node_modules/@noble/hashes/src/blake2.ts +486 -0
  667. package/node_modules/@noble/hashes/src/blake2b.ts +10 -0
  668. package/node_modules/@noble/hashes/src/blake2s.ts +20 -0
  669. package/node_modules/@noble/hashes/src/blake3.ts +272 -0
  670. package/node_modules/@noble/hashes/src/crypto.ts +9 -0
  671. package/node_modules/@noble/hashes/src/cryptoNode.ts +15 -0
  672. package/node_modules/@noble/hashes/src/eskdf.ts +187 -0
  673. package/node_modules/@noble/hashes/src/hkdf.ts +88 -0
  674. package/node_modules/@noble/hashes/src/hmac.ts +94 -0
  675. package/node_modules/@noble/hashes/src/index.ts +31 -0
  676. package/node_modules/@noble/hashes/src/legacy.ts +293 -0
  677. package/node_modules/@noble/hashes/src/pbkdf2.ts +122 -0
  678. package/node_modules/@noble/hashes/src/ripemd160.ts +12 -0
  679. package/node_modules/@noble/hashes/src/scrypt.ts +257 -0
  680. package/node_modules/@noble/hashes/src/sha1.ts +10 -0
  681. package/node_modules/@noble/hashes/src/sha2.ts +402 -0
  682. package/node_modules/@noble/hashes/src/sha256.ts +24 -0
  683. package/node_modules/@noble/hashes/src/sha3-addons.ts +499 -0
  684. package/node_modules/@noble/hashes/src/sha3.ts +258 -0
  685. package/node_modules/@noble/hashes/src/sha512.ts +34 -0
  686. package/node_modules/@noble/hashes/src/utils.ts +395 -0
  687. package/node_modules/@noble/hashes/utils.d.ts +161 -0
  688. package/node_modules/@noble/hashes/utils.d.ts.map +1 -0
  689. package/node_modules/@noble/hashes/utils.js +313 -0
  690. package/node_modules/@noble/hashes/utils.js.map +1 -0
  691. package/package.json +179 -8
  692. package/schemas/catalog.schema.json +357 -0
  693. package/src/align.ts +239 -0
  694. package/src/audio-init-parser.ts +189 -0
  695. package/src/audio-ring-buffer.ts +120 -0
  696. package/src/authored-timing.ts +63 -0
  697. package/src/block-policy.ts +903 -0
  698. package/src/carp-catalog.ts +1065 -0
  699. package/src/catalog-fec-params.ts +729 -0
  700. package/src/catalog-network-source.ts +74 -0
  701. package/src/catalog-validator.ts +34 -0
  702. package/src/cmaf-block-assembler.ts +1255 -0
  703. package/src/cmaf-group-batcher.ts +104 -0
  704. package/src/cmcd-producer.ts +329 -0
  705. package/src/codec-utils.ts +1984 -0
  706. package/src/conceal-timeline.ts +293 -0
  707. package/src/delivery-gap.ts +147 -0
  708. package/src/duplicate-ledger.ts +372 -0
  709. package/src/fec-codec.ts +61 -0
  710. package/src/fec-manager.ts +4298 -0
  711. package/src/fec-starvation-detector.ts +128 -0
  712. package/src/fec-state.ts +219 -0
  713. package/src/fec-worker-setup.ts +283 -0
  714. package/src/fmp4-completion.ts +47 -0
  715. package/src/fmp4-reader.ts +112 -0
  716. package/src/index.ts +590 -21
  717. package/src/iso-block-locator.ts +329 -0
  718. package/src/iso-bmff-timing.ts +813 -0
  719. package/src/loc.ts +128 -154
  720. package/src/manifest-auth-plan.ts +107 -0
  721. package/src/media-router.ts +841 -0
  722. package/src/mfu-reassembler.ts +3480 -155
  723. package/src/mfu-reassembly-policy.ts +134 -0
  724. package/src/mmtp-container-parser.ts +2380 -0
  725. package/src/mmtp-decode-pipeline.ts +406 -0
  726. package/src/mmtp-router.ts +4860 -0
  727. package/src/mmtp-to-cmaf.ts +977 -0
  728. package/src/multicast-auth-catalog.ts +340 -0
  729. package/src/multicast-input.ts +793 -0
  730. package/src/multicast-manager.ts +2783 -0
  731. package/src/multicast-transport.ts +1058 -0
  732. package/src/multicast-unwrap.ts +132 -0
  733. package/src/no-modules.ts +21 -0
  734. package/src/phase14-catalog-override.ts +336 -0
  735. package/src/repair-fetch.ts +772 -0
  736. package/src/repair-layers.ts +533 -0
  737. package/src/sample-combiner.ts +938 -0
  738. package/src/signaling.ts +78 -0
  739. package/src/source-auth.ts +1 -0
  740. package/src/switching-set-alignment.ts +109 -0
  741. package/src/timestamp-codec.ts +265 -0
  742. package/src/track-pipeline-setup.ts +205 -0
  743. package/src/track-pipeline.ts +332 -0
  744. package/src/track-session.ts +1010 -0
  745. package/src/transport-pool.ts +128 -0
  746. package/src/wasm-mfu-reassembler.ts +405 -0
  747. package/src/wasm-mmtp-container-parser.ts +23 -0
  748. package/test-vectors/align.json +199 -0
  749. package/test-vectors/catalog/README.md +106 -0
  750. package/test-vectors/catalog/negative/archive-origin-with-path.json +33 -0
  751. package/test-vectors/catalog/negative/archive-unknown-field.json +34 -0
  752. package/test-vectors/catalog/negative/fec-block-profile-missing.json +51 -0
  753. package/test-vectors/catalog/negative/fec-block-profile-unknown.json +52 -0
  754. package/test-vectors/catalog/negative/fec-enhancement-repair-removed.json +28 -0
  755. package/test-vectors/catalog/negative/fec-repair-layer-geometry.json +33 -0
  756. package/test-vectors/catalog/negative/fec-repair-layer-missing-symbols.json +31 -0
  757. package/test-vectors/catalog/negative/fec-repair-multiple-depends.json +32 -0
  758. package/test-vectors/catalog/negative/fec-repair-overlay-with-layer.json +34 -0
  759. package/test-vectors/catalog/negative/fec-repair-priority-out-of-band.json +24 -0
  760. package/test-vectors/catalog/negative/fec-repair-source-symbols-without-scope.json +33 -0
  761. package/test-vectors/catalog/negative/legacy-selection-params.json +15 -0
  762. package/test-vectors/catalog/negative/media-track-repair-layer.json +16 -0
  763. package/test-vectors/catalog/negative/missing-mmtp-fields.json +12 -0
  764. package/test-vectors/catalog/negative/multicast-auth-padded-key.json +37 -0
  765. package/test-vectors/catalog/negative/multicast-auth-reused-track-format.json +53 -0
  766. package/test-vectors/catalog/negative/multicast-auth-trailing-pad-bits.json +37 -0
  767. package/test-vectors/catalog/negative/multicast-endpoint-missing-protocol-and-source.json +24 -0
  768. package/test-vectors/catalog/negative/multicast-network-source-empty.json +44 -0
  769. package/test-vectors/catalog/negative/multicast-packet-id-signaling.json +26 -0
  770. package/test-vectors/catalog/negative/network-source-object.json +25 -0
  771. package/test-vectors/catalog/negative/raptorq-source-symbols-over-max.json +24 -0
  772. package/test-vectors/catalog/negative/raptorq-unaligned-symbol.json +27 -0
  773. package/test-vectors/catalog/negative/repair-track-legacy-shape.json +19 -0
  774. package/test-vectors/catalog/negative/track-role-empty.json +16 -0
  775. package/test-vectors/catalog/negative/track-role-non-string.json +16 -0
  776. package/test-vectors/catalog/positive/archive-origin.json +34 -0
  777. package/test-vectors/catalog/positive/fec-iso-ssbg1.json +52 -0
  778. package/test-vectors/catalog/positive/fec-layered-repair.json +106 -0
  779. package/test-vectors/catalog/positive/fec-multicast.json +52 -0
  780. package/test-vectors/catalog/positive/flat-av.json +30 -0
  781. package/test-vectors/catalog/positive/multicast-auth-rotation.json +55 -0
  782. package/test-vectors/catalog/positive/multicast-native-ssm-no-network-source.json +43 -0
  783. package/test-vectors/catalog/positive/ticks-and-mixed-timescales.json +39 -0
  784. package/test-vectors/catalog/positive/track-role.json +49 -0
  785. package/test-vectors/catalog/releases/moqcast-native-v16-v1/catalog.json +65 -0
  786. package/test-vectors/catalog/releases/moqcast-native-v16-v1.archive.sha256 +1 -0
  787. package/test-vectors/catalog-multicast-auth-v1.json +26 -0
  788. package/test-vectors/catalog-validation.json +732 -0
  789. package/test-vectors/catalog-validator-public-api.mjs +25 -0
  790. package/test-vectors/generate-source-auth-fixture.mjs +114 -0
  791. package/test-vectors/hevc-x265-repeat-headers-v1.json +40 -0
  792. package/test-vectors/source-auth-v1.json +61 -0
  793. package/test-vectors/source-contiguity-v1.json +131 -0
  794. package/test-vectors/subsample-placement-v1.json +230 -0
  795. package/dist/mmtp.d.ts +0 -73
  796. package/dist/mmtp.d.ts.map +0 -1
  797. package/dist/mmtp.js +0 -210
  798. package/dist/mmtp.js.map +0 -1
  799. package/src/mmtp.ts +0 -277
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/block-policy.ts", "../../mmt-alta-parse/src/index.ts", "../../mmt-base/src/mmtp.ts", "../../mmt-base/src/ntp-short.ts", "../../mmt-base/src/fec-block-profile.ts", "../src/repair-layers.ts", "../src/repair-fetch.ts"],
4
+ "sourcesContent": ["/**\n * Keyframe-protected FEC repair policy.\n *\n * Every FEC block still gets in-band FEC decode. What this module decides is\n * (1) which blocks may hold a keyframe (RAP), so a block short of K at its\n * repair deadline can be repaired over QUIC with the right priority, and\n * (2) what a sink must do with the first frame released after a gap: stall\n * until the next RAP, or conceal and keep playing.\n *\n * The two decisions are deliberately independent. Classification only sets\n * repair priority. The stall/conceal decision is made again per released gap\n * from the media that is actually missing, so a wrong classification can only\n * lower a block's repair priority; it can never make playback wrong.\n *\n * Everything here is derived from catalog-authored timing and in-band MMTP\n * evidence. There are no timers, no cadence heuristics, and no defaults: a\n * track whose geometry cannot be derived exactly runs in the defined\n * fail-closed mode `all-protected`, where every block is treated as possibly\n * keyframe-bearing.\n *\n * All three players (moqtail, hang, Shaka) consume this one implementation.\n */\n\nimport { deriveFecInterleaveDepth } from '@blockcast/mmt-fec'\n\n// \u2500\u2500\u2500 Classes and priorities \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/**\n * Repair class of one FEC block.\n *\n * - `keyframe`: the block holds (or held) a RAP / MPU start.\n * - `delta`: the block provably holds only non-RAP video frames.\n * - `unknown`: neither could be proven; treated exactly like `keyframe`.\n * - `audio`: an audio block.\n */\nexport type BlockClass = 'keyframe' | 'delta' | 'unknown' | 'audio'\n\n/** Repair priority. Lower is more urgent. */\nexport type RepairPriority = 0 | 1 | 2\n\nexport type TrackKind = 'video' | 'audio'\n\n/** P0 = keyframe/unknown (stall-preventing), P1 = audio, P2 = delta. */\nexport function repairPriorityForClass(blockClass: BlockClass): RepairPriority {\n switch (blockClass) {\n case 'keyframe':\n case 'unknown':\n return 0\n case 'audio':\n return 1\n case 'delta':\n return 2\n }\n}\n\n// \u2500\u2500\u2500 Geometry \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport type ClassifierMode = 'classifying' | 'all-protected'\n\n/** Why a track runs `all-protected`. */\nexport type ClassifierReason =\n // Geometry (catalog) reasons.\n | 'framerate-absent'\n | 'keyframe-cadence-absent'\n | 'keyframe-interval-not-integral'\n | 'block-spans-gop'\n /**\n * The catalog keyframe interval is not an exact integer number of ticks\n * in the media track's own timescale (ISO-BMFF `mdhd`), so released RAP\n * spacing cannot be verified and hold-last cannot predict the next RAP.\n * Nothing is wrong with the stream; the cadence is simply unprovable.\n */\n | 'keyframe-interval-not-media-integral'\n /**\n * Another stream on this packetId was configured with its own geometry\n * and this one was not, so no geometry provably belongs to it (ABR rungs\n * sharing a packetId).\n */\n | 'stream-geometry-absent'\n // In-band invariant violations (positive evidence only).\n | 'rap-not-mpu-start'\n | 'mpu-longer-than-gop'\n | 'block-not-contiguous'\n | 'rap-spacing-mismatch'\n | 'mpu-multiple-fragments'\n | 'block-wider-than-interleave'\n\n/** Raw catalog timing for one track. Never a resolved cadence. */\nexport interface BlockClassifierGeometryInput {\n readonly trackKind: TrackKind\n /** Catalog `framerate` (frames per second). */\n readonly framerate?: number\n /** Catalog `fec.interleaveDepthMs`: the full FEC block span (\u00A75.1). */\n readonly interleaveDepthMs: number\n /** Catalog track `timescale` for `keyframeIntervalTicks`. */\n readonly timescale?: number\n /** Catalog `keyframeIntervalTicks` (preferred when the timescale is present). */\n readonly keyframeIntervalTicks?: number\n /** Catalog `keyframeIntervalMs`. */\n readonly keyframeIntervalMs?: number\n}\n\n/** Exact keyframe interval as a rational number of seconds: ticks / timescale. */\nexport interface KeyframeInterval {\n readonly ticks: number\n readonly timescale: number\n}\n\nexport type BlockClassifierGeometry =\n | { readonly trackKind: 'audio' }\n | {\n readonly trackKind: 'video'\n readonly mode: 'classifying'\n /** F: one frame's duration in milliseconds (1000 / framerate). */\n readonly frameDurationMs: number\n /** D: media units per FEC block (deriveFecInterleaveDepth). */\n readonly interleaveUnits: number\n /** G: frames per GOP. */\n readonly gopFrames: number\n readonly keyframeInterval: KeyframeInterval\n }\n | {\n readonly trackKind: 'video'\n readonly mode: 'all-protected'\n readonly reason: ClassifierReason\n }\n\n/**\n * Integer value of `numerator / denominator` when it is one, else null.\n *\n * Catalog framerates are JSON numbers, so an NTSC rate arrives as the double\n * nearest 30000/1001. One multiply and one divide each add at most half an\n * ulp of relative error (IEEE-754), so a quotient within two ulps of an\n * integer IS that integer; anything further is genuinely non-integral.\n */\nfunction exactInteger(numerator: number, denominator: number): number | null {\n if (!Number.isFinite(numerator) || !Number.isFinite(denominator) || denominator === 0) {\n return null\n }\n const quotient = numerator / denominator\n const rounded = Math.round(quotient)\n if (rounded < 1) return null\n const representationError = 2 * Number.EPSILON * Math.abs(quotient)\n return Math.abs(quotient - rounded) <= representationError ? rounded : null\n}\n\nfunction isPositiveFinite(value: number | undefined): value is number {\n return value !== undefined && Number.isFinite(value) && value > 0\n}\n\n/**\n * Derive the per-track classifier geometry from raw catalog fields.\n *\n * `all-protected` is the policy's defined fail-closed state, not a default:\n * it is returned with the exact reason. The only throw is for a value the\n * catalog validator already rejects (a non-positive block span).\n */\nexport function deriveBlockClassifierGeometry(\n input: BlockClassifierGeometryInput,\n): BlockClassifierGeometry {\n if (!isPositiveFinite(input.interleaveDepthMs)) {\n throw new Error(\n `[BlockPolicy] interleaveDepthMs=${input.interleaveDepthMs} is not a positive block span; ` +\n 'the catalog validator rejects this',\n )\n }\n if (input.trackKind === 'audio') return { trackKind: 'audio' }\n\n if (!isPositiveFinite(input.framerate)) {\n return { trackKind: 'video', mode: 'all-protected', reason: 'framerate-absent' }\n }\n const framerate = input.framerate\n\n let keyframeInterval: KeyframeInterval\n if (isPositiveFinite(input.keyframeIntervalTicks) && isPositiveFinite(input.timescale)) {\n keyframeInterval = { ticks: input.keyframeIntervalTicks, timescale: input.timescale }\n } else if (isPositiveFinite(input.keyframeIntervalMs)) {\n keyframeInterval = { ticks: input.keyframeIntervalMs, timescale: 1000 }\n } else {\n return { trackKind: 'video', mode: 'all-protected', reason: 'keyframe-cadence-absent' }\n }\n\n const gopFrames = exactInteger(keyframeInterval.ticks * framerate, keyframeInterval.timescale)\n if (gopFrames === null) {\n return {\n trackKind: 'video',\n mode: 'all-protected',\n reason: 'keyframe-interval-not-integral',\n }\n }\n\n const frameDurationMs = 1000 / framerate\n const interleaveUnits = deriveFecInterleaveDepth(input.interleaveDepthMs, frameDurationMs)\n if (interleaveUnits >= gopFrames) {\n return { trackKind: 'video', mode: 'all-protected', reason: 'block-spans-gop' }\n }\n\n return {\n trackKind: 'video',\n mode: 'classifying',\n frameDurationMs,\n interleaveUnits,\n gopFrames,\n keyframeInterval,\n }\n}\n\n/**\n * The keyframe interval expressed in a media track's timescale, or null when\n * the geometry is not `classifying` or the interval is not an exact integer\n * number of media ticks.\n */\nexport function keyframeIntervalMediaTicks(\n geometry: BlockClassifierGeometry,\n mediaTimescale: number,\n): bigint | null {\n if (geometry.trackKind !== 'video' || geometry.mode !== 'classifying') return null\n if (!Number.isSafeInteger(mediaTimescale) || mediaTimescale <= 0) return null\n const { ticks, timescale } = geometry.keyframeInterval\n if (!Number.isSafeInteger(ticks) || !Number.isSafeInteger(timescale)) return null\n const scaled = BigInt(ticks) * BigInt(mediaTimescale)\n const divisor = BigInt(timescale)\n if (scaled % divisor !== 0n) return null\n return scaled / divisor\n}\n\n// \u2500\u2500\u2500 Evidence \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/**\n * In-band evidence from ONE first-arrival source symbol received from the\n * network. Recovered symbols and repair symbols never contribute, and the\n * MFU `priority > 0x80` hint is not evidence.\n */\nexport interface SourceSymbolEvidence {\n /** The MMTP header R (RAP) bit. */\n readonly rap: boolean\n /** MPU_sequence_number, when parsed from this packet. */\n readonly mpu?: number\n /**\n * 0-based sample position within the MPU: the MFU DU-header\n * `sample_number` minus one (ISO/IEC 23008-1 numbers samples from 1, and\n * the parser rejects `sample_number` 0). Present only when this packet\n * carried a DU header.\n */\n readonly idx?: number\n /** movie_fragment_sequence_number from the DU header, when present. */\n readonly movieFragment?: number\n /**\n * DU-header `offset`: the data unit's authored byte position within its\n * sample (0 for a whole-sample data unit, the partition boundary for a\n * subsample data unit). Present only with `idx`; absent reads as 0.\n */\n readonly offset?: number\n /**\n * The MMTP header packet_sequence_number (contiguous per packet_id). Not\n * classification evidence: FECManager uses it to bound how many blocks the\n * producer can have started between two source symbols, so an outage is\n * never mistaken for a source restart.\n */\n readonly packetSequenceNumber?: number\n}\n\nexport type ReclassificationEvidence = 'rap' | 'mpu-span' | 'window' | 'classifier-disabled'\n\nexport type ClassifierChange =\n | {\n readonly kind: 'reclassified'\n readonly sbn: number\n readonly from: BlockClass\n readonly to: BlockClass\n readonly evidence: ReclassificationEvidence\n }\n | {\n readonly kind: 'mode'\n readonly mode: 'all-protected'\n readonly reason: ClassifierReason\n }\n\ninterface BlockEvidence {\n rapSeen: boolean\n mpus: Set<number>\n idxMin?: number\n idxMax?: number\n cls: BlockClass\n}\n\n/** A data-unit start (a packet carrying a DU header) and the block it is in. */\ninterface DataUnitStart {\n readonly movieFragment: number\n readonly idx: number\n readonly offset: number\n readonly sbn: number\n}\n\n/** Authored order of two data-unit starts of one MPU. */\nfunction compareDataUnitStarts(\n a: Omit<DataUnitStart, 'sbn'>,\n b: Omit<DataUnitStart, 'sbn'>,\n): number {\n return a.movieFragment - b.movieFragment || a.idx - b.idx || a.offset - b.offset\n}\n\n/**\n * Per-stream block classifier.\n *\n * Classes only move toward more protection or narrower evidence:\n * `unknown\u2192delta` and `unknown\u2192keyframe` / `delta\u2192keyframe` on evidence, and\n * `delta\u2192unknown` when an invariant disables classification. `keyframe` is\n * terminal.\n *\n * The delta window rule rests on three premises, each guarded by an\n * invariant that fires on positive evidence only:\n *\n * - P1: the source packets of one block are one contiguous run of the\n * stream's authored order, and blocks follow each other in SBN order\n * (`block-not-contiguous`). The producer assigns SS_IDs in authored order\n * and a block is the SS_ID range [SBN*K, SBN*K + K), so this holds for a\n * block that closes at K symbols in the middle of a frame: that frame's\n * fragments then land in two ADJACENT blocks, which is still one run each.\n * A frame split across adjacent blocks is not a violation.\n * - P2: that run touches at most D media units, counting a frame continued\n * from the previous block (`block-wider-than-interleave`). The producer\n * counts a media-unit boundary only once the open block holds a symbol.\n * - P3: an MPU starts with its only RAP and holds at most G frames\n * (`rap-not-mpu-start`, `mpu-longer-than-gop`, `rap-spacing-mismatch`), in\n * one movie fragment (`mpu-multiple-fragments`).\n *\n * P1 and P2 are what make a block's frames contiguous and lie within\n * [idxMax \u2212 D + 1, idxMin + D \u2212 1] of its MPU. What refutes P1 is a pair of\n * source packets whose wire-stated authored order and SBN order disagree:\n * a later MPU in an earlier block, a later data unit of one MPU (by\n * movie_fragment_sequence_number, sample_number, then DU offset) in an\n * earlier block, or one data unit in two blocks. That is the signature of a\n * round-robin interleave, which scatters one run over several concurrent\n * blocks. Non-adjacency alone is not: a sample larger than K packets\n * legitimately spans blocks SBN..SBN+n.\n */\nexport class BlockClassifier {\n #geometry: BlockClassifierGeometry\n #mode: ClassifierMode\n #reason: ClassifierReason | null\n /**\n * False once the stream showed that an MPU boundary does not bound its\n * RAPs (a RAP inside an MPU, or an MPU longer than a GOP). Recorded in\n * every mode: the stall/conceal decision relies on it even when the\n * catalog geometry already runs the classifier `all-protected`.\n */\n #mpuBoundsRaps = true\n #blocks = new Map<number, BlockEvidence>()\n /**\n * P1 ledger, kept for the MPUs within the classification horizon (the\n * highest MPU seen and the one before it): the SBN span of every source\n * packet of each MPU, and each MPU's data-unit starts in authored order.\n * Valid only within one continuous SBN sequence; a source discontinuity\n * clears it (`noteSourceDiscontinuity`).\n */\n #mpuSbnSpans = new Map<number, { min: number; max: number }>()\n #dataUnitStarts = new Map<number, DataUnitStart[]>()\n #highestMpu: number | null = null\n /** MPU \u2192 movie_fragment_sequence_number observed in it. */\n #mpuFragments = new Map<number, number>()\n #lastRap: { mpu: number; presentationTime: bigint; mediaTimescale: number } | null = null\n\n constructor(geometry: BlockClassifierGeometry) {\n this.#geometry = geometry\n this.#mode = 'classifying'\n this.#reason = null\n this.reset(geometry)\n }\n\n get geometry(): BlockClassifierGeometry {\n return this.#geometry\n }\n\n get mode(): ClassifierMode {\n return this.#mode\n }\n\n get reason(): ClassifierReason | null {\n return this.#reason\n }\n\n /**\n * Whether \"same MPU on both sides of a gap\" still proves the gap held no\n * RAP. It stops holding for the rest of the epoch once a RAP was received\n * inside an MPU or an MPU ran longer than a GOP.\n */\n get mpuBoundsRaps(): boolean {\n return this.#mpuBoundsRaps\n }\n\n /** Begin a fresh epoch (catalog change or track reset). */\n reset(geometry: BlockClassifierGeometry = this.#geometry): void {\n this.#geometry = geometry\n this.#blocks.clear()\n this.#clearContiguityLedger()\n this.#mpuFragments.clear()\n this.#lastRap = null\n this.#mpuBoundsRaps = true\n if (geometry.trackKind === 'video' && geometry.mode === 'all-protected') {\n this.#mode = 'all-protected'\n this.#reason = geometry.reason\n } else {\n this.#mode = 'classifying'\n this.#reason = null\n }\n }\n\n /**\n * Retire this classifier (its stream moves to a new catalog epoch). Every\n * block it had classified `delta` loses the evidence behind that class and\n * reads `unknown` from now on; the returned changes say so.\n */\n retire(): ClassifierChange[] {\n const changes: ClassifierChange[] = []\n for (const [sbn, block] of this.#blocks) {\n if (block.cls !== 'delta') continue\n block.cls = 'unknown'\n changes.push({\n kind: 'reclassified',\n sbn,\n from: 'delta',\n to: 'unknown',\n evidence: 'classifier-disabled',\n })\n }\n return changes\n }\n\n /**\n * The stream's SBN sequence restarted (FECManager source discontinuity:\n * a publisher restart, or the SS_ID space wrapped). Authored order and SBN\n * order are comparable only within one continuous sequence, so the P1\n * ledger starts again.\n */\n noteSourceDiscontinuity(): void {\n this.#clearContiguityLedger()\n }\n\n /** Register a block that exists without evidence (a vanished block). */\n touch(sbn: number): void {\n this.#blockFor(sbn)\n }\n\n /** Forget a resolved block. */\n forget(sbn: number): void {\n this.#blocks.delete(sbn)\n }\n\n classOf(sbn: number): BlockClass {\n if (this.#geometry.trackKind === 'audio') return 'audio'\n return this.#blocks.get(sbn)?.cls ?? 'unknown'\n }\n\n /** Record one first-arrival source symbol's evidence for block `sbn`. */\n observe(sbn: number, evidence: SourceSymbolEvidence): ClassifierChange[] {\n if (this.#geometry.trackKind === 'audio') return []\n const changes: ClassifierChange[] = []\n const block = this.#blockFor(sbn)\n\n if (evidence.rap) block.rapSeen = true\n if (evidence.mpu !== undefined) block.mpus.add(evidence.mpu)\n if (evidence.idx !== undefined) {\n block.idxMin = block.idxMin === undefined ? evidence.idx : Math.min(block.idxMin, evidence.idx)\n block.idxMax = block.idxMax === undefined ? evidence.idx : Math.max(block.idxMax, evidence.idx)\n }\n\n // A RAP inside an MPU refutes \"an MPU boundary bounds RAPs\" whatever the\n // classifier mode, because gap decisions rely on it in every mode.\n if (evidence.rap && evidence.idx !== undefined && evidence.idx !== 0) {\n this.#mpuBoundsRaps = false\n }\n\n const violation = this.#checkInvariants(sbn, block, evidence)\n if (violation !== null) changes.push(...this.#disable(violation))\n\n const next = this.#classify(block)\n if (next !== block.cls && this.#allowed(block.cls, next)) {\n changes.push({\n kind: 'reclassified',\n sbn,\n from: block.cls,\n to: next,\n evidence: this.#evidenceFor(block, next),\n })\n block.cls = next\n }\n return changes\n }\n\n /**\n * Record a RAP frame released in order. Released MPU-start RAPs of MPUs m\n * and m+n must be exactly n keyframe intervals apart in the media\n * timescale, compared as integers.\n *\n * Comparing only consecutive MPUs would miss exactly the case the window\n * rule cannot survive: a short MPU whose successor's RAP was lost (that\n * RAP-bearing block is the one a wrong G calls delta), because the next\n * released RAP then belongs to m+2. Every MPU is one interval long, so the\n * span to any later released RAP proves or refutes all MPUs in between.\n */\n noteReleasedRap(\n mpu: number,\n presentationTime: bigint,\n mediaTimescale: number,\n ): ClassifierChange[] {\n if (this.#geometry.trackKind === 'audio' || this.#mode === 'all-protected') {\n return []\n }\n // Verification compares integers in the media timescale. When the\n // catalog interval is not an exact tick count there, spacing is\n // unprovable: fail closed, naming the cause, rather than blaming the\n // encoder with rap-spacing-mismatch on a perfectly spaced RAP.\n const interval = keyframeIntervalMediaTicks(this.#geometry, mediaTimescale)\n if (interval === null) return this.#disable('keyframe-interval-not-media-integral')\n const previous = this.#lastRap\n // A further RAP inside an already-anchored MPU is not an MPU start; the\n // MPU keeps its first RAP as the spacing anchor (rap-not-mpu-start owns\n // mid-MPU RAPs).\n if (previous !== null && previous.mpu === mpu && previous.mediaTimescale === mediaTimescale) {\n return []\n }\n this.#lastRap = { mpu, presentationTime, mediaTimescale }\n if (\n previous === null ||\n mpu < previous.mpu ||\n previous.mediaTimescale !== mediaTimescale\n ) {\n return []\n }\n if (\n presentationTime - previous.presentationTime === interval * BigInt(mpu - previous.mpu)\n ) {\n return []\n }\n return this.#disable('rap-spacing-mismatch')\n }\n\n #blockFor(sbn: number): BlockEvidence {\n let block = this.#blocks.get(sbn)\n if (!block) {\n block = { rapSeen: false, mpus: new Set(), cls: 'unknown' }\n this.#blocks.set(sbn, block)\n }\n return block\n }\n\n #checkInvariants(\n sbn: number,\n block: BlockEvidence,\n evidence: SourceSymbolEvidence,\n ): ClassifierReason | null {\n if (this.#mode === 'all-protected' || this.#geometry.trackKind !== 'video') return null\n const geometry = this.#geometry\n if (geometry.mode !== 'classifying') return null\n if (evidence.rap && evidence.idx !== undefined && evidence.idx !== 0) {\n return 'rap-not-mpu-start'\n }\n if (evidence.idx !== undefined && evidence.idx >= geometry.gopFrames) {\n return 'mpu-longer-than-gop'\n }\n // A block closes at the latest after D media units, so the DU headers it\n // carries from one MPU lie within D consecutive positions. A wider span\n // means the producer closes blocks some other way (at K symbols, or\n // round-robin across concurrent blocks) and the window rule is void.\n if (\n block.mpus.size === 1 &&\n block.idxMin !== undefined &&\n block.idxMax !== undefined &&\n block.idxMax - block.idxMin >= geometry.interleaveUnits\n ) {\n return 'block-wider-than-interleave'\n }\n if (evidence.mpu !== undefined && evidence.movieFragment !== undefined) {\n const fragment = this.#mpuFragments.get(evidence.mpu)\n if (fragment === undefined) {\n this.#mpuFragments.set(evidence.mpu, evidence.movieFragment)\n // One entry per MPU still inside the classification horizon.\n for (const mpu of this.#mpuFragments.keys()) {\n if (mpu + 1 < evidence.mpu) this.#mpuFragments.delete(mpu)\n }\n } else if (fragment !== evidence.movieFragment) {\n // sample_number is then a position within a movie fragment, not\n // within the MPU, and the window rule's geometry does not hold.\n return 'mpu-multiple-fragments'\n }\n }\n if (evidence.mpu !== undefined && this.#contradictsAuthoredOrder(sbn, evidence.mpu, evidence)) {\n return 'block-not-contiguous'\n }\n return null\n }\n\n #clearContiguityLedger(): void {\n this.#mpuSbnSpans.clear()\n this.#dataUnitStarts.clear()\n this.#highestMpu = null\n }\n\n /**\n * P1 check for one source packet of MPU `mpu` in block `sbn`, then record\n * it. True when some recorded packet's authored order and SBN order\n * disagree with this one's (see the class comment). Only positions the\n * wire states are compared: MPU order for every packet, and data-unit\n * order for packets that carry a DU header. A packet of one data unit\n * that continues into the next block carries no DU header and so only\n * takes part through its MPU, which is exactly what makes an adjacent\n * straddle consistent.\n */\n #contradictsAuthoredOrder(sbn: number, mpu: number, evidence: SourceSymbolEvidence): boolean {\n // Every packet of MPU m precedes every packet of MPU m' > m.\n for (const [other, span] of this.#mpuSbnSpans) {\n if (other < mpu && span.max > sbn) return true\n if (other > mpu && span.min < sbn) return true\n }\n const inHorizon = this.#highestMpu === null || mpu + 1 >= this.#highestMpu\n let insertAt = -1\n let starts: DataUnitStart[] | undefined\n let start: DataUnitStart | undefined\n if (evidence.idx !== undefined) {\n start = {\n movieFragment: evidence.movieFragment ?? 0,\n idx: evidence.idx,\n offset: evidence.offset ?? 0,\n sbn,\n }\n starts = this.#dataUnitStarts.get(mpu)\n if (starts !== undefined) {\n let lo = 0\n let hi = starts.length\n while (lo < hi) {\n const mid = (lo + hi) >>> 1\n if (compareDataUnitStarts(starts[mid]!, start) < 0) lo = mid + 1\n else hi = mid\n }\n const at = starts[lo]\n if (at !== undefined && compareDataUnitStarts(at, start) === 0) {\n // One data unit has one first packet: seen in another block, the\n // SBN-to-authored-order map is not a function.\n if (at.sbn !== sbn) return true\n } else {\n const before = starts[lo - 1]\n if (before !== undefined && before.sbn > sbn) return true\n if (at !== undefined && at.sbn < sbn) return true\n insertAt = lo\n }\n }\n }\n if (!inHorizon) return false\n const span = this.#mpuSbnSpans.get(mpu)\n if (span === undefined) this.#mpuSbnSpans.set(mpu, { min: sbn, max: sbn })\n else {\n if (sbn < span.min) span.min = sbn\n if (sbn > span.max) span.max = sbn\n }\n if (start !== undefined) {\n if (starts === undefined) this.#dataUnitStarts.set(mpu, [start])\n else if (insertAt >= 0) starts.splice(insertAt, 0, start)\n }\n if (this.#highestMpu === null || mpu > this.#highestMpu) {\n this.#highestMpu = mpu\n for (const other of [...this.#mpuSbnSpans.keys()]) {\n if (other + 1 < mpu) {\n this.#mpuSbnSpans.delete(other)\n this.#dataUnitStarts.delete(other)\n }\n }\n }\n return false\n }\n\n #disable(reason: ClassifierReason): ClassifierChange[] {\n if (reason === 'rap-not-mpu-start' || reason === 'mpu-longer-than-gop') {\n this.#mpuBoundsRaps = false\n }\n if (this.#mode === 'all-protected') return []\n this.#mode = 'all-protected'\n this.#reason = reason\n const changes: ClassifierChange[] = [{ kind: 'mode', mode: 'all-protected', reason }]\n for (const [sbn, block] of this.#blocks) {\n if (block.cls !== 'delta') continue\n block.cls = 'unknown'\n changes.push({\n kind: 'reclassified',\n sbn,\n from: 'delta',\n to: 'unknown',\n evidence: 'classifier-disabled',\n })\n }\n return changes\n }\n\n #classify(block: BlockEvidence): BlockClass {\n if (block.rapSeen || block.mpus.size >= 2) return 'keyframe'\n if (this.#mode === 'all-protected') return 'unknown'\n const geometry = this.#geometry\n if (geometry.trackKind !== 'video' || geometry.mode !== 'classifying') return 'unknown'\n if (\n block.mpus.size === 1 &&\n block.idxMin !== undefined &&\n block.idxMax !== undefined\n ) {\n // A block closes every D media units, so its frames are contiguous and\n // lie in [idxMax \u2212 D + 1, idxMin + D \u2212 1] of MPU m. Index 0 is the only\n // RAP of an MPU and G bounds its length.\n const span = geometry.interleaveUnits - 1\n if (block.idxMax - span >= 1 && block.idxMin + span <= geometry.gopFrames - 1) {\n return 'delta'\n }\n }\n return 'unknown'\n }\n\n #allowed(from: BlockClass, to: BlockClass): boolean {\n if (from === 'keyframe') return false\n if (from === 'unknown') return to === 'delta' || to === 'keyframe'\n if (from === 'delta') return to === 'keyframe'\n return false\n }\n\n #evidenceFor(block: BlockEvidence, to: BlockClass): ReclassificationEvidence {\n if (to === 'delta') return 'window'\n return block.rapSeen ? 'rap' : 'mpu-span'\n }\n}\n\n// \u2500\u2500\u2500 Stall vs conceal \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport interface GapStreamIdentity {\n readonly packetId: number\n /** Non-empty catalog identity, or null when the producer has no track name. */\n readonly catalogTrackName: string | null\n}\n\n/** What the sink knows about one side of a released gap. */\nexport interface GapFrameEvidence {\n /**\n * Codec-aware RAP check: `MmtpContainerParser.isKeyframe(data, rapFlag,\n * codec)`, bitstream-only for recovered frames. Never the priority hint.\n */\n readonly rap: boolean\n /**\n * MPU_sequence_number of the frame. Omit it once the stream showed that\n * an MPU boundary does not bound its RAPs (`BlockClassifier.mpuBoundsRaps`):\n * same-MPU then no longer proves a gap lost only deltas.\n */\n readonly mpu?: number\n /** 0-based sample position within the MPU, when the geometry verifies it. */\n readonly idx?: number\n /** Authored presentation time in `timescale` ticks. */\n readonly pts?: bigint\n /** Authored sample duration in `timescale` ticks. */\n readonly durationTicks?: number\n /** The media timescale of `pts` and `durationTicks`. */\n readonly timescale?: number\n}\n\nexport type FrameGapAction = 'stall' | 'conceal'\n\nexport type FrameGapReason =\n | 'rap-lost'\n | 'rap-unknown'\n | 'discontinuity'\n | 'delta-lost'\n | 'rap-next'\n | 'audio-gap'\n | 'audio-discontinuity'\n\n/**\n * The decision attached to the first frame released after a gap\n * (`RoutedFrameMeta.gapBefore`). It is the same frozen object the manager\n * emits as its `gap` repair lifecycle event, so a sink that acts on the frame\n * and an observer that counts events see one fact.\n */\nexport interface FrameGapDecision {\n readonly version: 2\n readonly type: 'gap'\n readonly stream: GapStreamIdentity\n readonly trackKind: TrackKind\n readonly action: FrameGapAction\n readonly reason: FrameGapReason\n /** FEC blocks in the gap whose media was lost (failed or vanished). */\n readonly blocks: readonly number[]\n /** Missing video frames or audio access units, when exactly known. */\n readonly missingFrames: number | null\n /** Authored duration of the missing media in milliseconds, when known. */\n readonly missingMs: number | null\n /** Manager epoch of the stream when the decision was made. */\n readonly epoch: number\n}\n\nexport interface DecideGapInput {\n readonly stream: GapStreamIdentity\n readonly trackKind: TrackKind\n /** Last frame released before the gap. */\n readonly before: GapFrameEvidence\n /** First frame released after the gap. */\n readonly after: GapFrameEvidence\n /** The stream restarted (FEC SBN went backwards or the publisher reset). */\n readonly discontinuity: boolean\n readonly blocks: readonly number[]\n /**\n * The most media units the gap can hold: the FEC blocks spanned from the\n * frame before to the frame after, times D (media units per block,\n * ceil(interleaveDepthMs / unit duration), \u00A75.1). A block touches at most D\n * units whether it closes at D or at K symbols. Null when the span or D is\n * unknown. An audio PTS delta that implies more AUs than this is a clock\n * jump, not loss, and re-anchors instead of concealing every slot.\n */\n readonly maxMissingUnits: number | null\n readonly epoch: number\n}\n\nfunction durationMs(units: number, before: GapFrameEvidence): number | null {\n if (\n before.durationTicks === undefined ||\n before.timescale === undefined ||\n before.timescale <= 0\n ) {\n return null\n }\n return (units * before.durationTicks * 1000) / before.timescale\n}\n\n/**\n * Pure stall/conceal decision for the first frame released after a gap.\n *\n * Video stalls only when a RAP may be inside the gap: the frame after it is\n * not a RAP and either sits in another MPU (`rap-lost`) or the MPU cannot be\n * proven on both sides (`rap-unknown`), or the stream restarted\n * (`discontinuity`). A gap inside one MPU lost only deltas (`delta-lost`) and\n * a RAP after the gap resynchronises by itself (`rap-next`). Audio never\n * stalls: an exact gap is concealed per access unit (`audio-gap`), anything\n * else re-anchors at the next frame (`audio-discontinuity`).\n */\nexport function decideGap(input: DecideGapInput): FrameGapDecision {\n const { before, after } = input\n let action: FrameGapAction\n let reason: FrameGapReason\n let missingFrames: number | null = null\n\n if (input.trackKind === 'audio') {\n action = 'conceal'\n reason = 'audio-discontinuity'\n if (\n !input.discontinuity &&\n before.pts !== undefined &&\n after.pts !== undefined &&\n before.durationTicks !== undefined &&\n Number.isSafeInteger(before.durationTicks) &&\n before.durationTicks > 0\n ) {\n const auDuration = BigInt(before.durationTicks)\n const missingTicks = after.pts - before.pts - auDuration\n const bound = input.maxMissingUnits\n if (\n missingTicks > 0n &&\n missingTicks % auDuration === 0n &&\n bound !== null &&\n missingTicks / auDuration <= BigInt(bound)\n ) {\n reason = 'audio-gap'\n missingFrames = Number(missingTicks / auDuration)\n }\n }\n } else if (input.discontinuity) {\n action = 'stall'\n reason = 'discontinuity'\n } else if (after.rap) {\n action = 'conceal'\n reason = 'rap-next'\n } else if (before.mpu === undefined || after.mpu === undefined) {\n action = 'stall'\n reason = 'rap-unknown'\n } else if (before.mpu !== after.mpu) {\n action = 'stall'\n reason = 'rap-lost'\n } else {\n action = 'conceal'\n reason = 'delta-lost'\n if (before.idx !== undefined && after.idx !== undefined) {\n const missing = after.idx - before.idx - 1\n missingFrames = missing > 0 ? missing : null\n }\n }\n\n return Object.freeze({\n version: 2 as const,\n type: 'gap' as const,\n stream: Object.freeze({\n packetId: input.stream.packetId,\n catalogTrackName: input.stream.catalogTrackName,\n }),\n trackKind: input.trackKind,\n action,\n reason,\n blocks: Object.freeze([...input.blocks]),\n missingFrames,\n missingMs: missingFrames === null ? null : durationMs(missingFrames, before),\n epoch: input.epoch,\n })\n}\n", "/**\n * Zero-dependency helper for extracting the detached ALTA authenticator trailer\n * from an MMTP source packet.\n *\n * Lives in its own leaf package so both `@blockcast/mmt-container` (wire-path\n * parsing) and `@blockcast/fec-worker` (FEC-recovered-source parsing) can share\n * one implementation without creating a package cycle between them.\n *\n * Spec: ISO/IEC 23008-1:2023 + draft-ramadan-moq-mmt \u00A712 (detached trailer, Path 2).\n */\n\n// --- Private MMTP/MPU/MFU constants (ISO/IEC 23008-1 \u00A79.2.2\u20139.2.3) ---\n// Duplicated here as immutable spec values so this package stays a leaf with\n// zero `@blockcast/*` dependencies. Re-deriving from `mmt-container` would\n// reintroduce the package cycle this split exists to avoid.\n\nconst MMTP_HEADER_SIZE = 12\nconst MPU_HEADER_SIZE = 8\nconst MPU_LENGTH_FIELD_SIZE = 2\nconst MPU_HEADER_REMAINDER_SIZE = MPU_HEADER_SIZE - MPU_LENGTH_FIELD_SIZE\nconst TIMED_MFU_DU_HEADER_SIZE = 14\nconst NONTIMED_MFU_DU_HEADER_SIZE = 4\nconst PACKET_TYPE_MPU = 0x00\n\n// --- Public ALTA trailer constants ---\n\n/**\n * MMTP header-extension ext_type assigned to the detached ALTA authenticator\n * trailer (Path 2 / 08-07). Carried in an `[ext_type][ext_length][auth]`\n * block appended after the MPU's declared payload bytes, before the 4-byte\n * Source FEC Payload ID. MMTP X-bit stays 0 \u2014 non-ALTA MMTP receivers treat\n * the trailer as bytes past `payload_length` and skip it (per ISO 23008-1\n * \u00A79.2.2 \"may be discarded without impacting correct processing\").\n */\nexport const MMTP_EXT_TYPE_ALTA_AUTH = 0xf0a1\n\n/** Fixed size of the trailer's `[ext_type][ext_length]` prefix (2 + 2 BE). */\nexport const MMTP_EXT_TRAILER_HEADER_SIZE = 4\n\n/** Result of extracting the detached ALTA authenticator from an MMTP packet. */\nexport interface AltaTrailer {\n /** Authenticator bytes (no ext_type/ext_length prefix, no trailing padding). */\n auth: Uint8Array\n /**\n * Wire-packet byte offset where the signed payload ends, i.e. where the\n * trailer begins. Caller uses this to produce\n * `payload = wirePacket.subarray(0, signedEnd)` for verify_detached.\n */\n signedEnd: number\n}\n\n/**\n * Extract the detached ALTA authenticator trailer from an MMTP source packet.\n *\n * Wire layout:\n * `[MMTP hdr][length(2)][remaining MPU hdr(6)][DU?][media][TRAILER][SS_ID?]`\n * `payload_length = 6 + DU bytes + media bytes`\n *\n * Returns `null` when:\n * - the packet is too small / malformed,\n * - `payload_length` cannot contain the remaining six MPU-header bytes,\n * - the declared payload overruns the packet,\n * - no bytes remain between `payload_length` and the SS_ID,\n * - the trailer's `ext_type` isn't {@link MMTP_EXT_TYPE_ALTA_AUTH},\n * - the declared `ext_length` would extend past the SS_ID boundary.\n *\n * Non-MPU packets (repair, signaling) always return `null`.\n *\n * @param options.skipFecId When true, do NOT reserve 4 trailing bytes for the\n * Source FEC Payload ID. Use on FEC-recovered symbols where the PID has\n * already been stripped upstream.\n */\nexport function extractAltaTrailer(\n mmtpPacket: Uint8Array,\n options?: { skipFecId?: boolean },\n): AltaTrailer | null {\n if (mmtpPacket.length < MMTP_HEADER_SIZE + MPU_HEADER_SIZE) return null\n // ISO/IEC 23008-1:2023 Figure 8 places V=0 packet_type in the low six bits.\n const packetType = mmtpPacket[1]! & 0x3f\n if (packetType !== PACKET_TYPE_MPU) return null\n\n const fecType = (mmtpPacket[0]! >> 3) & 0x03\n let headerLength = MMTP_HEADER_SIZE\n if (fecType < 2 && (mmtpPacket[0]! & 0x20) !== 0) headerLength += 4\n if (fecType < 2 && (mmtpPacket[0]! & 0x02) !== 0) {\n if (headerLength + 4 > mmtpPacket.length) return null\n const extensionLength = new DataView(\n mmtpPacket.buffer,\n mmtpPacket.byteOffset + headerLength + 2,\n 2,\n ).getUint16(0, false)\n headerLength += 4 + extensionLength\n }\n if (headerLength + MPU_HEADER_SIZE > mmtpPacket.length) return null\n\n const payload = mmtpPacket.subarray(headerLength)\n\n // Inline a minimal MPU-header read \u2014 only the fields extractAltaTrailer needs.\n // payloadLength: 16 BE at bytes 0-1.\n // Byte 2 packs FT(4) | T(1) | FI(2) | A(1).\n const payloadLength = (payload[0]! << 8) | payload[1]!\n const flagsByte = payload[2]!\n const fragmentType = (flagsByte >> 4) & 0x0f\n const timed = ((flagsByte >> 3) & 0x01) === 1\n const fragmentationIndicator = (flagsByte >> 1) & 0x03\n\n // Only MFU packets (FT=2) carry the detached trailer.\n if (fragmentType !== 2) return null\n\n // FI=0/1 structurally carries a DU header; FI=2/3 does not. Require at\n // least one media byte after that deterministic layout so the bytes at the\n // declared boundary cannot impersonate ALTA for a header-only fragment.\n const dataUnitHeaderSize = fragmentationIndicator <= 1\n ? (timed ? TIMED_MFU_DU_HEADER_SIZE : NONTIMED_MFU_DU_HEADER_SIZE)\n : 0\n if (payloadLength <= MPU_HEADER_REMAINDER_SIZE + dataUnitHeaderSize) return null\n\n if (fecType > 1) return null\n const hasFecId = !options?.skipFecId && fecType === 1\n const payloadEnd = hasFecId ? payload.length - 4 : payload.length\n const declaredEnd = MPU_LENGTH_FIELD_SIZE + payloadLength\n if (declaredEnd > payloadEnd) return null\n\n if (declaredEnd + MMTP_EXT_TRAILER_HEADER_SIZE > payloadEnd) return null\n const extType = (payload[declaredEnd]! << 8) | payload[declaredEnd + 1]!\n if (extType !== MMTP_EXT_TYPE_ALTA_AUTH) return null\n const extLen = (payload[declaredEnd + 2]! << 8) | payload[declaredEnd + 3]!\n\n const authStart = declaredEnd + MMTP_EXT_TRAILER_HEADER_SIZE\n if (authStart + extLen !== payloadEnd) return null\n\n return {\n auth: payload.subarray(authStart, payloadEnd),\n signedEnd: headerLength + declaredEnd,\n }\n}\n", "import {\n MMTP_EXT_TYPE_ALTA_AUTH as _LEAF_MMTP_EXT_TYPE_ALTA_AUTH,\n MMTP_EXT_TRAILER_HEADER_SIZE as _LEAF_MMTP_EXT_TRAILER_HEADER_SIZE,\n extractAltaTrailer as _leafExtractAltaTrailer,\n type AltaTrailer as _LeafAltaTrailer,\n} from '@blockcast/mmt-alta-parse'\n\n/**\n * @mmt/container - MMTP Parser\n *\n * MMTP (MMT Protocol) packet parser based on ISO/IEC 23008-1:2023 Section 9.2\n *\n * MMTP header format (12 bytes):\n * - Byte 0: V(2) | C(1) | FEC_type(2) | r(1) | X(1) | R(1)\n * - Byte 1: reserved(2) | packet_type(6)\n * - Bytes 2-3: packet_id (big-endian)\n * - Bytes 4-7: timestamp (big-endian, 32-bit)\n * - Bytes 8-11: sequence_number (big-endian)\n *\n * FEC_type values:\n * - 0: No FEC / source packet\n * - 1: FEC mode 0 (source with FEC payload ID)\n * - 2: FEC repair packet mode 0\n * - 3: FEC repair packet mode 1\n *\n * @packageDocumentation\n */\n\nexport interface MMTPHeader {\n version: number\n /** Packet type: 0x00=MPU, 0x02=Signaling, 0x03=Repair */\n packetType: number\n packetId: number\n timestamp: bigint\n sequenceNumber: number\n payloadLength: number\n fecType: number\n /** C flag: header carries a 32-bit packet_counter. */\n packetCounterFlag: boolean\n packetCounter?: number\n /** X flag: header carries an extension header and value. */\n extensionFlag: boolean\n extension?: { type: number; data: Uint8Array }\n /** Byte offset at which the MMTP payload begins. */\n headerLength: number\n /** Random Access Point flag - indicates keyframe (ISO 23008-1, byte 0 bit 0) */\n rapFlag: boolean\n}\n\nexport interface MPUHeader {\n mpuSequenceNumber: number\n fragmentationIndicator: number // FI: 0=complete, 1=first, 2=middle, 3=last\n /** 8-bit MPU fragment_counter from byte 3. */\n fragmentCounter: number\n /** T flag: 1=timed media (14-byte MFU DU header), 0=non-timed (4-byte header) */\n timedFlag: number\n /** A flag: aggregated MPU payload. This strict profile requires A=0. */\n aggregationFlag: boolean\n /** MPU payload_length (bytes 0-1): bytes after the length field, including\n * the remaining six MPU-header bytes and all data-unit bytes. */\n payloadLength: number\n}\n\nexport interface MFUDuHeader {\n movieFragmentSequenceNumber: number\n sampleNumber: number\n offset: number\n priority: number\n dependencyCounter: number\n}\n\nexport interface MMTPPacket {\n header: MMTPHeader\n payload: Uint8Array\n}\n\nexport const MMTP_HEADER_SIZE = 12\nexport const MPU_HEADER_SIZE = 8\nexport const MFU_DU_HEADER_SIZE_TIMED = 14 // T=1: timed media (video/audio samples)\nexport const MFU_DU_HEADER_SIZE_NONTIMED = 4 // T=0: non-timed media (item_id only)\n\nconst MPU_LENGTH_FIELD_SIZE = 2\nconst MPU_HEADER_REMAINDER_SIZE = MPU_HEADER_SIZE - MPU_LENGTH_FIELD_SIZE\nconst SOURCE_FEC_PAYLOAD_ID_SIZE = 4\n\n// MMTP Packet Types (ISO/IEC 23008-1:2023 Section 9.2.2)\nexport const PACKET_TYPE_MPU = 0x00\nexport const PACKET_TYPE_SIGNALING = 0x02\nexport const PACKET_TYPE_REPAIR = 0x03\n\n// AL-FEC Signaling Message ID (ISO/IEC 23008-1:2023 Amendment 1:2025)\nexport const ALFEC_MESSAGE_ID = 0x0203\n\n/**\n * MMTP `packet_id` of the signaling flow, which carries PACKET_TYPE_SIGNALING\n * packets such as the AL-FEC message (ALFEC_MESSAGE_ID). The FFmpeg `moq_mmt`\n * muxer sends that message on this packet_id into the same multicast group as\n * the media, and moqcast-draft (PR #44) makes advertising it for a track a\n * MUST NOT. A track's packet_id is therefore 1..MMTP_PACKET_ID_MAX.\n */\nexport const MMTP_SIGNALING_PACKET_ID = 0\n\n/** Largest value of the 16-bit MMTP `packet_id` header field. */\nexport const MMTP_PACKET_ID_MAX = 0xffff\n\n// FEC Code IDs\nexport const FEC_CODE_XOR = 1\nexport const FEC_CODE_RAPTORQ = 2\n\n/**\n * Parse MMTP header from buffer\n */\nexport function parseMMTPHeader(buffer: Uint8Array): MMTPHeader {\n if (buffer.length < MMTP_HEADER_SIZE) {\n throw new Error(`MMTP packet too small: ${buffer.length} bytes`)\n }\n\n const view = new DataView(buffer.buffer, buffer.byteOffset, buffer.byteLength)\n\n const version = (buffer[0]! >> 6) & 0x03\n if (version !== 0) {\n throw new Error(`Unsupported MMTP version: ${version}`)\n }\n\n // Extract FEC type from byte 0, bits 3-4\n const fecType = (buffer[0]! >> 3) & 0x03\n const packetCounterFlag = (buffer[0]! & 0x20) !== 0\n const extensionFlag = (buffer[0]! & 0x02) !== 0\n\n // Extract RAP flag from byte 0, bit 0 (R bit - Random Access Point)\n const rapFlag = (buffer[0]! & 0x01) === 1\n\n // ISO/IEC 23008-1:2023 Figure 8 (V=0) places reserved(2) before\n // packet_type(6), so packet_type occupies the low six bits of byte 1.\n const packetType = buffer[1]! & 0x3F\n\n let offset = 2\n\n // Packet ID (2 bytes, big-endian)\n const packetId = view.getUint16(offset, false)\n offset += 2\n\n // Timestamp (4 bytes, big-endian)\n const timestamp = BigInt(view.getUint32(offset, false))\n offset += 4\n\n // Sequence number (4 bytes, big-endian)\n const sequenceNumber = view.getUint32(offset, false)\n offset += 4\n\n let packetCounter: number | undefined\n if (packetCounterFlag) {\n if (offset + 4 > buffer.length) throw new Error('MMTP packet truncated in packet_counter')\n packetCounter = view.getUint32(offset, false)\n offset += 4\n }\n\n let extension: { type: number; data: Uint8Array } | undefined\n if (extensionFlag) {\n if (offset + 4 > buffer.length) throw new Error('MMTP packet truncated in header extension')\n const type = view.getUint16(offset, false)\n const length = view.getUint16(offset + 2, false)\n offset += 4\n if (offset + length > buffer.length) throw new Error('MMTP packet truncated in header extension value')\n extension = { type, data: buffer.subarray(offset, offset + length) }\n offset += length\n }\n\n return {\n version,\n packetType,\n packetId,\n timestamp,\n sequenceNumber,\n payloadLength: buffer.length - offset,\n fecType,\n packetCounterFlag,\n packetCounter,\n extensionFlag,\n extension,\n headerLength: offset,\n rapFlag,\n }\n}\n\n/**\n * The MMTP `packet_id` of `buffer`, or null exactly when parseMMTPHeader would\n * throw for it (short buffer, V != 0, truncated packet_counter or header\n * extension).\n *\n * This is the per-packet filter a receiver runs before any other work, so it\n * builds no header object, DataView or BigInt timestamp (BLO-36599). Use\n * parseMMTPHeader when any other header field is needed.\n */\nexport function readMMTPPacketId(buffer: Uint8Array): number | null {\n if (buffer.length < MMTP_HEADER_SIZE) return null\n const flags = buffer[0]!\n if (((flags >> 6) & 0x03) !== 0) return null\n let offset = MMTP_HEADER_SIZE\n if ((flags & 0x20) !== 0) {\n if (offset + 4 > buffer.length) return null\n offset += 4\n }\n if ((flags & 0x02) !== 0) {\n if (offset + 4 > buffer.length) return null\n const length = (buffer[offset + 2]! << 8) | buffer[offset + 3]!\n if (offset + 4 + length > buffer.length) return null\n }\n return (buffer[2]! << 8) | buffer[3]!\n}\n\n/**\n * Unwrap MMTP packet to extract payload\n */\nexport function unwrapMMTP(buffer: Uint8Array): MMTPPacket {\n const header = parseMMTPHeader(buffer)\n const payload = buffer.subarray(header.headerLength)\n\n return { header, payload }\n}\n\n/**\n * Parse MPU header from payload\n * ISO/IEC 23008-1:2023 Section 9.2.3.3\n *\n * MPU Header format (8 bytes) as written by FFmpeg/libmmt:\n * - Bytes 0-1: payload_length (16 bits, big-endian)\n * - Byte 2: FT(4) | T(1) | FI(2) | A(1)\n * - Byte 3: fragment_counter (8 bits)\n * - Bytes 4-7: MPU_sequence_number (32 bits, big-endian)\n */\nexport function parseMPUHeader(payload: Uint8Array): MPUHeader {\n if (payload.length < MPU_HEADER_SIZE) {\n throw new Error(`Payload too small for MPU header: ${payload.length} bytes`)\n }\n\n const view = new DataView(payload.buffer, payload.byteOffset, payload.byteLength)\n\n // Bytes 0-1: payload_length (16 bits, big-endian)\n const payloadLength = view.getUint16(0, false)\n\n // Byte 2: FT(4) | T(1) | FI(2) | A(1)\n const flagsByte = payload[2]!\n const timedFlag = (flagsByte >> 3) & 0x01\n const fragmentationIndicator = (flagsByte >> 1) & 0x03\n const aggregationFlag = (flagsByte & 0x01) !== 0\n\n // Byte 3: fragment_counter (8 bits). The high bit is part of the counter in\n // the current libmmt/FFmpeg writer ABI.\n const fragmentCounter = payload[3]!\n\n // Bytes 4-7: MPU_sequence_number (32 bits, big-endian)\n const mpuSequenceNumber = view.getUint32(4, false)\n\n return {\n mpuSequenceNumber,\n fragmentationIndicator,\n fragmentCounter,\n timedFlag,\n aggregationFlag,\n payloadLength,\n }\n}\n\nexport function parseTimedMfuDuHeader(payload: Uint8Array, offset: number): MFUDuHeader | null {\n if (offset < 0 || offset + MFU_DU_HEADER_SIZE_TIMED > payload.length) return null\n const view = new DataView(payload.buffer, payload.byteOffset + offset, MFU_DU_HEADER_SIZE_TIMED)\n return {\n movieFragmentSequenceNumber: view.getUint32(0, false),\n sampleNumber: view.getUint32(4, false),\n offset: view.getUint32(8, false),\n priority: payload[offset + 12]!,\n dependencyCounter: payload[offset + 13]!,\n }\n}\n\n/**\n * Why `extractMFUPayload` refused a packet.\n *\n * These identifiers are part of the public contract: callers surface them in\n * diagnostics, so renaming one is a breaking change.\n */\nexport type MFURejectionGate =\n | 'packet-shorter-than-mmtp-plus-mpu-header'\n | 'mmtp-header-unparseable'\n | 'not-an-mpu-packet'\n | 'payload-shorter-than-mpu-header'\n | 'aggregation-flag-set'\n | 'mpu-payload-length-below-header-remainder'\n | 'mpu-declared-end-exceeds-payload'\n | 'mfu-offset-at-or-past-declared-end'\n | 'source-fec-payload-id-width-mismatch'\n | 'declared-trailers-unresolved'\n\nexport interface MFURejection {\n gate: MFURejectionGate\n /** Human-readable detail, including the byte arithmetic that decided it. */\n detail: string\n}\n\ninterface MfuPayloadLayout {\n hasMfuDuHeader: boolean\n mfuOffset: number\n dataEnd: number\n}\n\nfunction resolveMfuPayloadLayout(\n mpu: MPUHeader,\n fragmentType: number,\n payload: Uint8Array,\n): { layout: MfuPayloadLayout } | { rejection: MFURejection } {\n if (mpu.payloadLength < MPU_HEADER_REMAINDER_SIZE) {\n return {\n rejection: {\n gate: 'mpu-payload-length-below-header-remainder',\n detail: `payload_length=${mpu.payloadLength} < ${MPU_HEADER_REMAINDER_SIZE}`,\n },\n }\n }\n\n // ISO/IEC 23008-1 defines payload_length from immediately after its own\n // two-byte field. It therefore includes the remaining six MPU-header bytes.\n const declaredEnd = MPU_LENGTH_FIELD_SIZE + mpu.payloadLength\n if (declaredEnd > payload.length) {\n return {\n rejection: {\n gate: 'mpu-declared-end-exceeds-payload',\n detail:\n `payload_length=${mpu.payloadLength} implies end=${declaredEnd}, ` +\n `payload is ${payload.length} bytes`,\n },\n }\n }\n\n const hasHeader = fragmentType === 2 && mpu.fragmentationIndicator <= 1\n const mfuHeaderSize = mpu.timedFlag ? MFU_DU_HEADER_SIZE_TIMED : MFU_DU_HEADER_SIZE_NONTIMED\n const mfuOffset = MPU_HEADER_SIZE + (hasHeader ? mfuHeaderSize : 0)\n if (mfuOffset >= declaredEnd) {\n return {\n rejection: {\n gate: 'mfu-offset-at-or-past-declared-end',\n detail: `mfuOffset=${mfuOffset} >= declaredEnd=${declaredEnd}`,\n },\n }\n }\n\n return {\n layout: {\n hasMfuDuHeader: hasHeader,\n mfuOffset,\n dataEnd: declaredEnd,\n },\n }\n}\n\nfunction isEmptyOrExactAltaTrailer(payload: Uint8Array, start: number, end: number): boolean {\n if (start === end) return true\n if (start < 0 || end > payload.length || start + _LEAF_MMTP_EXT_TRAILER_HEADER_SIZE > end) {\n return false\n }\n\n const view = new DataView(\n payload.buffer,\n payload.byteOffset + start,\n end - start,\n )\n if (view.getUint16(0, false) !== _LEAF_MMTP_EXT_TYPE_ALTA_AUTH) return false\n\n const authLength = view.getUint16(2, false)\n return start + _LEAF_MMTP_EXT_TRAILER_HEADER_SIZE + authLength === end\n}\n\ninterface DeclaredTrailerLayout {\n hasFecId: boolean\n}\n\nfunction resolveDeclaredTrailers(\n payload: Uint8Array,\n declaredEnd: number,\n fecType: number,\n options?: { skipFecId?: boolean },\n): DeclaredTrailerLayout | null {\n if (declaredEnd > payload.length) return null\n\n if (options?.skipFecId) {\n return isEmptyOrExactAltaTrailer(payload, declaredEnd, payload.length)\n ? { hasFecId: false }\n : null\n }\n\n if (fecType === 1) {\n const fecStart = payload.length - SOURCE_FEC_PAYLOAD_ID_SIZE\n if (fecStart < declaredEnd || !isEmptyOrExactAltaTrailer(payload, declaredEnd, fecStart)) {\n return null\n }\n return { hasFecId: true }\n }\n if (fecType !== 0) return null\n\n if (isEmptyOrExactAltaTrailer(payload, declaredEnd, payload.length)) {\n return { hasFecId: false }\n }\n\n return null\n}\n\n/**\n * Extract NAL unit payload from MMTP packet\n */\nexport function extractNALUnit(mmtpPacket: Uint8Array): Uint8Array {\n const extraction = extractMFUPayload(mmtpPacket)\n if (!extraction) throw new Error('Malformed MMTP MPU payload')\n return extraction.data\n}\n\n/** Result of extracting MFU payload from an MMTP packet */\nexport interface MFUExtraction {\n /** MMTP header fields */\n header: MMTPHeader\n /** MPU header fields */\n mpu: MPUHeader\n /** Fragment type (FT field, 4 bits): 0=metadata, 2=MFU data */\n fragmentType: number\n /** MFU payload data (video/audio bytes, headers stripped, FEC ID excluded) */\n data: Uint8Array\n /** Parsed timed MFU DU header when present, otherwise null */\n mfuDuHeader: MFUDuHeader | null\n /** Non-timed MFU Item_ID when the 4-byte DU header is present. */\n itemId: number | null\n /** Source FEC Payload ID if present (`FEC_type == 1`), or null */\n fecPayloadId: { ssId: number } | null\n}\n\n/**\n * Extract MFU payload from a raw MMTP packet.\n *\n * Handles all the byte arithmetic for:\n * - MMTP header (12 bytes)\n * - MPU sub-header (8 bytes, payloadLength field)\n * - MFU DU header (14 bytes timed / 4 bytes non-timed, only for FI \u2264 1)\n * - Source FEC Payload ID (4 bytes at end, only when `FEC_type == 1`)\n *\n * `payloadLength` is the ISO length-after-field value: six remaining MPU-header\n * bytes, then the MFU DU header when present, then media. ALTA and Source FEC\n * trailers are outside that declared span and must consume the exact suffix.\n *\n * @param mmtpPacket Raw MMTP packet (from multicast or MoQ)\n * @param options.skipFecId When true, do NOT subtract the 4-byte Source FEC Payload ID\n * from the payload boundary, even if fecType >= 1 in the MMTP header. Use this for\n * FEC-recovered symbols where the FEC PID was already stripped before WASM decoding\n * but the fecType bits in byte 0 still reflect the original packet (FEC Type=1 per\n * draft-ramadan-moq-mmt-00 \u00A73.1).\n * @returns Extracted MFU data + metadata, or null if packet is too small\n */\ninterface MfuPlan {\n header: MMTPHeader\n payload: Uint8Array\n mpu: MPUHeader\n fragmentType: number\n layout: MfuPayloadLayout\n trailerLayout: DeclaredTrailerLayout\n}\n\n/**\n * Single source of truth for whether a packet yields an MFU payload.\n *\n * `extractMFUPayload` and `explainMFURejection` both delegate here, so the\n * reason reported to operators can never drift from the decision the parser\n * actually made.\n */\nfunction planMfuExtraction(\n mmtpPacket: Uint8Array,\n options?: { skipFecId?: boolean },\n parsedHeader?: MMTPHeader,\n): { plan: MfuPlan } | { rejection: MFURejection } {\n if (mmtpPacket.length < MMTP_HEADER_SIZE + MPU_HEADER_SIZE) {\n return {\n rejection: {\n gate: 'packet-shorter-than-mmtp-plus-mpu-header',\n detail: `${mmtpPacket.length} bytes < ${MMTP_HEADER_SIZE + MPU_HEADER_SIZE}`,\n },\n }\n }\n\n let header: MMTPHeader\n if (parsedHeader) {\n header = parsedHeader\n } else {\n try {\n header = parseMMTPHeader(mmtpPacket)\n } catch (error) {\n return {\n rejection: {\n gate: 'mmtp-header-unparseable',\n detail: error instanceof Error ? error.message : String(error),\n },\n }\n }\n }\n if (header.packetType !== PACKET_TYPE_MPU) {\n return {\n rejection: {\n gate: 'not-an-mpu-packet',\n detail: `packetType=0x${header.packetType.toString(16).padStart(2, '0')}`,\n },\n }\n }\n\n const payload = mmtpPacket.subarray(header.headerLength)\n if (payload.length < MPU_HEADER_SIZE) {\n return {\n rejection: {\n gate: 'payload-shorter-than-mpu-header',\n detail: `${payload.length} bytes < ${MPU_HEADER_SIZE}`,\n },\n }\n }\n\n const mpu = parseMPUHeader(payload)\n if (mpu.aggregationFlag) {\n return {\n rejection: { gate: 'aggregation-flag-set', detail: 'strict profile requires A=0' },\n }\n }\n\n // Fragment type from byte 2 upper nibble (not returned by parseMPUHeader)\n const fragmentType = (payload[2]! >> 4) & 0x0f\n\n const layoutResult = resolveMfuPayloadLayout(mpu, fragmentType, payload)\n if ('rejection' in layoutResult) return layoutResult\n const { layout } = layoutResult\n\n const trailerLayout = resolveDeclaredTrailers(\n payload,\n layout.dataEnd,\n header.fecType,\n options,\n )\n if (!trailerLayout) {\n // The most common cause is a producer whose Source FEC Payload ID width\n // disagrees with ISO/IEC 23008-1 \u00A7C.5.2. Name that explicitly rather than\n // leaving operators to infer it from a generic trailer error.\n if (!options?.skipFecId && header.fecType === 1) {\n const observed = payload.length - layout.dataEnd\n if (observed !== SOURCE_FEC_PAYLOAD_ID_SIZE) {\n return {\n rejection: {\n gate: 'source-fec-payload-id-width-mismatch',\n detail:\n `producer wrote a ${observed}-byte Source FEC Payload ID, ` +\n `ISO/IEC 23008-1 \u00A7C.5.2 requires ${SOURCE_FEC_PAYLOAD_ID_SIZE} ` +\n `(payload=${payload.length}, declaredEnd=${layout.dataEnd})`,\n },\n }\n }\n }\n return {\n rejection: {\n gate: 'declared-trailers-unresolved',\n detail:\n `trailer bytes [${layout.dataEnd}, ${payload.length}) are neither empty ` +\n `nor an exact ALTA trailer (fecType=${header.fecType})`,\n },\n }\n }\n\n return { plan: { header, payload, mpu, fragmentType, layout, trailerLayout } }\n}\n\n/**\n * Report why `extractMFUPayload` would refuse this packet, or null if it would\n * accept it.\n *\n * `extractMFUPayload` returns bare `null` on ten distinct conditions, which\n * makes a producer/consumer framing disagreement indistinguishable from a\n * publisher that never started \u2014 both render as silence. Use this wherever\n * that distinction matters: diagnostics, contract tests, and operator-facing\n * logs.\n */\nexport function explainMFURejection(\n mmtpPacket: Uint8Array,\n options?: { skipFecId?: boolean },\n): MFURejection | null {\n const result = planMfuExtraction(mmtpPacket, options)\n return 'rejection' in result ? result.rejection : null\n}\n\nexport function extractMFUPayload(\n mmtpPacket: Uint8Array,\n options?: { skipFecId?: boolean },\n): MFUExtraction | null {\n return extractPlannedMfu(mmtpPacket, planMfuExtraction(mmtpPacket, options))\n}\n\n/**\n * extractMFUPayload for a caller that already holds this packet's parsed MMTP\n * header, so the header is not parsed a second time (BLO-36599).\n *\n * `header` must be `parseMMTPHeader(mmtpPacket)` of this same packet; the\n * result is then identical to `extractMFUPayload(mmtpPacket, options)`.\n */\nexport function extractMFUPayloadWithHeader(\n mmtpPacket: Uint8Array,\n header: MMTPHeader,\n options?: { skipFecId?: boolean },\n): MFUExtraction | null {\n return extractPlannedMfu(mmtpPacket, planMfuExtraction(mmtpPacket, options, header))\n}\n\nfunction extractPlannedMfu(\n mmtpPacket: Uint8Array,\n result: { plan: MfuPlan } | { rejection: MFURejection },\n): MFUExtraction | null {\n if ('rejection' in result) return null\n const { header, payload, mpu, fragmentType, layout, trailerLayout } = result.plan\n\n const mfuDuHeaderPresent = layout.hasMfuDuHeader\n const mfuOffset = layout.mfuOffset\n const dataEnd = layout.dataEnd\n\n const mfuDuHeader =\n mfuDuHeaderPresent && mpu.timedFlag === 1\n ? parseTimedMfuDuHeader(payload, MPU_HEADER_SIZE)\n : null\n const itemId =\n mfuDuHeaderPresent && mpu.timedFlag === 0\n ? new DataView(payload.buffer, payload.byteOffset + MPU_HEADER_SIZE, 4).getUint32(0, false)\n : null\n\n const data = payload.subarray(mfuOffset, dataEnd)\n\n const fecPayloadId = trailerLayout.hasFecId\n ? {\n ssId: new DataView(\n mmtpPacket.buffer,\n mmtpPacket.byteOffset + mmtpPacket.length - SOURCE_FEC_PAYLOAD_ID_SIZE,\n SOURCE_FEC_PAYLOAD_ID_SIZE,\n ).getUint32(0, false),\n }\n : null\n\n return { header, mpu, fragmentType, data, mfuDuHeader, itemId, fecPayloadId }\n}\n\n/**\n * Extract timestamp from MMTP packet\n */\nexport function extractTimestamp(mmtpPacket: Uint8Array): bigint {\n const { header } = unwrapMMTP(mmtpPacket)\n return header.timestamp\n}\n\n/**\n * Check if MMTP packet is an FEC repair packet\n * FEC_type 2 and 3 are repair packets\n */\nexport function isFecRepairPacket(mmtpPacket: Uint8Array): boolean {\n if (mmtpPacket.length < 1) return false\n const fecType = (mmtpPacket[0]! >> 3) & 0x03\n return fecType >= 2\n}\n\n/**\n * Extract Source FEC Payload ID (SS_ID) from the last 4 bytes of an MMTP\n * source packet per ISO/IEC 23008-1:2023 Section C.5.2 Figure C.10.\n *\n * SS_ID is a flat 32-bit monotonic counter (incremented by 1 per packet\n * for ssbg_mode0 and ssbg_mode1). Block membership and ESI use the\n * configured K; repair SS_Start identifies the first SS_ID of the block and\n * SSB_length records the authored source-symbol count:\n * SBN = floor(SS_ID / K), ESI = SS_ID % K\n * SS_Start = SBN * K (first SS_ID of the block)\n *\n * This matches FFmpeg moqenc_mmt.c, the drafts (draft-ramadan-moq-fec \u00A76.2),\n * and ATSC A/331 \u00A78.1.2.5 which defers to ISO 23008-1 Annex C.\n */\nexport function extractFecPayloadId(mmtpPacket: Uint8Array): { ssId: number } | null {\n const header = parseHeaderForSourceFecId(mmtpPacket)\n return header ? sourceFecPayloadIdOf(mmtpPacket, header) : null\n}\n\n/**\n * Return true when FEC_type=1 and the exact suffix after the ISO-declared MPU\n * payload contains a four-byte Source FEC Payload ID.\n */\nexport function hasSourceFecPayloadId(mmtpPacket: Uint8Array): boolean {\n const header = parseHeaderForSourceFecId(mmtpPacket)\n return header !== null && sourceFecPayloadIdOf(mmtpPacket, header) !== null\n}\n\nfunction parseHeaderForSourceFecId(mmtpPacket: Uint8Array): MMTPHeader | null {\n if (mmtpPacket.length < MMTP_HEADER_SIZE + MPU_HEADER_SIZE) return null\n try {\n return parseMMTPHeader(mmtpPacket)\n } catch {\n return null\n }\n}\n\n/**\n * The Source FEC Payload ID of a packet whose MMTP header the caller already\n * parsed, or null when the packet carries none: present only when FEC_type=1\n * and the exact suffix after the ISO-declared MPU payload holds the four-byte\n * ID (ISO/IEC 23008-1 \u00A7C.5.2). Same answer as extractFecPayloadId, without a\n * second header parse (BLO-36599).\n *\n * `header` must be `parseMMTPHeader(mmtpPacket)` of this same packet.\n */\nexport function sourceFecPayloadIdOf(\n mmtpPacket: Uint8Array,\n header: MMTPHeader,\n): { ssId: number } | null {\n if (mmtpPacket.length < MMTP_HEADER_SIZE + MPU_HEADER_SIZE) return null\n if (header.packetType !== PACKET_TYPE_MPU) return null\n\n const payload = mmtpPacket.subarray(header.headerLength)\n if (payload.length < MPU_HEADER_SIZE) return null\n const payloadLength = (payload[0]! << 8) | payload[1]!\n if (payloadLength < MPU_HEADER_REMAINDER_SIZE) return null\n const declaredEnd = MPU_LENGTH_FIELD_SIZE + payloadLength\n if (!resolveDeclaredTrailers(payload, declaredEnd, header.fecType)?.hasFecId) return null\n\n // Flat 4-byte SS_ID: encoder writes block_id * K + symbol_id\n const off = mmtpPacket.length - SOURCE_FEC_PAYLOAD_ID_SIZE\n return {\n ssId:\n ((mmtpPacket[off]! << 24) |\n (mmtpPacket[off + 1]! << 16) |\n (mmtpPacket[off + 2]! << 8) |\n mmtpPacket[off + 3]!) >>>\n 0,\n }\n}\n\n/**\n * MMTP header-extension ext_type assigned to the detached ALTA authenticator\n * trailer (Path 2 / 08-07). Carried in an `[ext_type][ext_length][auth]`\n * block appended after the MPU's declared payload bytes, before the 4-byte\n * Source FEC Payload ID. MMTP X-bit stays 0 \u2014 non-ALTA MMTP receivers treat\n * the trailer as bytes past `payload_length` and skip it (per ISO 23008-1\n * \u00A79.2.2 \"may be discarded without impacting correct processing\").\n */\n// Re-export the ALTA trailer helper from the zero-dep leaf package. The leaf\n// owns the implementation so container and FEC consumers share one parser\n// without creating a package cycle.\nexport const MMTP_EXT_TYPE_ALTA_AUTH = _LEAF_MMTP_EXT_TYPE_ALTA_AUTH\nexport const MMTP_EXT_TRAILER_HEADER_SIZE = _LEAF_MMTP_EXT_TRAILER_HEADER_SIZE\nexport type AltaTrailer = _LeafAltaTrailer\nexport const extractAltaTrailer = _leafExtractAltaTrailer\n\n/** Size of Repair FEC Payload ID per ISO/IEC 23008-1 Section C.5.3 */\nexport const REPAIR_FEC_PID_SIZE = 13\n\n/**\n * Parse the 13-byte Repair FEC Payload ID after the complete dynamic MMTP header.\n * `parsedHeader`, when given, must be `parseMMTPHeader(mmtpPacket)` of this\n * same packet; it spares the second header parse (BLO-36599).\n */\nfunction readRepairFecPayloadIdFields(mmtpPacket: Uint8Array, parsedHeader?: MMTPHeader): {\n packetId: number; ssStart: number; rsbLength: number; rsId: number; ssbLength: number; dataOffset: number\n} | null {\n let header: MMTPHeader\n if (parsedHeader) {\n header = parsedHeader\n } else {\n try {\n header = parseMMTPHeader(mmtpPacket)\n } catch {\n return null\n }\n }\n if (header.packetType !== PACKET_TYPE_REPAIR || header.fecType !== 2) return null\n\n const off = header.headerLength\n if (mmtpPacket.length < off + REPAIR_FEC_PID_SIZE) return null\n const ssStart =\n ((mmtpPacket[off]! << 24) |\n (mmtpPacket[off + 1]! << 16) |\n (mmtpPacket[off + 2]! << 8) |\n mmtpPacket[off + 3]!) >>>\n 0 // 32 bits\n const rsbLength = (mmtpPacket[off + 4]! << 16) | (mmtpPacket[off + 5]! << 8) | mmtpPacket[off + 6]! // 24 bits\n const rsId = (mmtpPacket[off + 7]! << 16) | (mmtpPacket[off + 8]! << 8) | mmtpPacket[off + 9]! // 24 bits\n const ssbLength = (mmtpPacket[off + 10]! << 16) | (mmtpPacket[off + 11]! << 8) | mmtpPacket[off + 12]! // 24 bits\n return {\n packetId: header.packetId,\n ssStart,\n rsbLength,\n rsId,\n ssbLength,\n dataOffset: off + REPAIR_FEC_PID_SIZE,\n }\n}\n\n/**\n * Extract Repair FEC Payload ID from a raw-multicast MMTP repair packet.\n * Per ISO/IEC 23008-1:2023 Section C.5.3 (ssbg_mode0, one-stage, no FFSRP_TS):\n * [MMTP header][SS_Start:4][RSB_length:3][RS_ID:3][SSB_length:3][repair data]\n * OTI is delivered via AL-FEC signaling message (Table C.3), not per-packet.\n *\n * Byte 1 is reserved(2)|packet_type(6) for V=0, so repair type 0x03 is 0x03.\n * Pre-migration high-bits bytes (0x0c) are rejected \u2014 no compatibility decode.\n */\nexport function extractRepairFecPayloadId(mmtpPacket: Uint8Array): {\n ssStart: number; rsbLength: number; rsId: number; ssbLength: number\n} | null {\n const fields = readRepairFecPayloadIdFields(mmtpPacket)\n if (!fields) return null\n return {\n ssStart: fields.ssStart,\n rsbLength: fields.rsbLength,\n rsId: fields.rsId,\n ssbLength: fields.ssbLength,\n }\n}\n\n/** Parsed MoQ repair frame per ISO/IEC 23008-1 Section C.4.3 + C.5.3 */\nexport interface MoqRepairFrame {\n /** MMTP packetId (1=video, 2=audio) \u2014 for routing to correct FEC decoder */\n packetId: number\n /** SS_Start (32 bits): SS_ID of first source symbol in block */\n ssStart: number\n /** RSB_length (24 bits): number of repair symbols (P) */\n rsbLength: number\n /** RS_ID (24 bits): repair symbol index (0-based) */\n rsId: number\n /** SSB_length (24 bits): authored source-symbol count (1..=configured K) */\n ssbLength: number\n /** Repair symbol data after the dynamic MMTP header and 13-byte FEC payload ID. */\n symbolData: Uint8Array\n}\n\n/**\n * Parse a MoQ repair frame (ISO/IEC 23008-1 Annex C format).\n *\n * Wire format per ISO 23008-1 \u00A7C.4.3 + \u00A7C.5.3:\n * MMTP Header: fixed 12 bytes plus C/X optional fields\n * then SS_Start (4 bytes) \u2192 block identifier\n * then RSB_length (3 bytes) \u2192 P\n * then RS_ID (3 bytes) \u2192 ESI = K + RS_ID\n * then SSB_length (3 bytes) \u2192 authored source-symbol count (1..=K)\n * then Repair Symbol Data (T bytes)\n *\n * @param frame - Raw MoQ repair frame bytes\n * @returns Parsed frame or null if invalid\n */\nexport function parseMoqRepairFrame(frame: Uint8Array): MoqRepairFrame | null {\n return moqRepairFrameFrom(frame, readRepairFecPayloadIdFields(frame))\n}\n\n/**\n * parseMoqRepairFrame for a caller that already holds this frame's parsed MMTP\n * header, so the header is not parsed a second time (BLO-36599). The router's\n * multicast repair branch uses it: one repair packet in every K + P otherwise\n * paid two header parses.\n *\n * `header` must be `parseMMTPHeader(frame)` of this same frame; the result is\n * then identical to `parseMoqRepairFrame(frame)`.\n */\nexport function parseMoqRepairFrameWithHeader(\n frame: Uint8Array,\n header: MMTPHeader,\n): MoqRepairFrame | null {\n return moqRepairFrameFrom(frame, readRepairFecPayloadIdFields(frame, header))\n}\n\nfunction moqRepairFrameFrom(\n frame: Uint8Array,\n repairId: ReturnType<typeof readRepairFecPayloadIdFields>,\n): MoqRepairFrame | null {\n if (!repairId) return null\n\n const symbolData = frame.subarray(repairId.dataOffset)\n if (\n repairId.rsbLength === 0 ||\n repairId.ssbLength === 0 ||\n repairId.rsId >= repairId.rsbLength ||\n symbolData.length === 0\n ) return null\n\n return {\n packetId: repairId.packetId,\n ssStart: repairId.ssStart,\n rsbLength: repairId.rsbLength,\n rsId: repairId.rsId,\n ssbLength: repairId.ssbLength,\n symbolData,\n }\n}\n\n// The signalling-message parsers (9.3.4.2 payload header, AL-FEC message,\n// PA/MPT) live in ./signalling.ts.\n", "/**\n * NTP short format (RFC 5905 \u00A76): an unsigned 32-bit, 16.16 fixed-point count\n * of seconds. The MMTP header `timestamp` field (ISO/IEC 23008-1 \u00A79.2.2)\n * carries each packet's UTC sending instant in this format, so the value wraps\n * to 0 every 2^16 s (~18.2 h).\n *\n * Plain numeric order on these values breaks at every wrap: a retained\n * `Math.max` stays at the pre-wrap value and every later instant compares as\n * older. Order send times only with {@link compareNtpShort}, which applies\n * RFC 1982 serial-number arithmetic over the field's width.\n */\n\n/** Width in bits of the MMTP `timestamp` field (NTP short). */\nexport const NTP_SHORT_BITS = 32\n\n/** Number of distinct NTP-short values; the field wraps to 0 here. */\nexport const NTP_SHORT_MODULUS = 2 ** NTP_SHORT_BITS\n\n/**\n * RFC 1982 \u00A73.2's `2^(SERIAL_BITS - 1)`. Two values whose forward distance is\n * below this are ordered; exactly this far apart they are not ordered at all.\n * Two instants must be less than this far apart (2^15 s, ~9.1 h of send time)\n * for the comparison to give their true order.\n */\nexport const NTP_SHORT_HALF_RANGE = 2 ** (NTP_SHORT_BITS - 1)\n\n/**\n * Serial order of one NTP-short value relative to another.\n *\n * `'unordered'` is RFC 1982's undefined case: the values are exactly\n * {@link NTP_SHORT_HALF_RANGE} apart, so neither precedes the other. It is\n * never folded into `'before'` or `'after'`; each caller decides what an\n * unorderable pair means for it.\n */\nexport type NtpShortOrder = 'before' | 'equal' | 'after' | 'unordered'\n\n/** True when `value` is an NTP-short value: an integer in [0, NTP_SHORT_MODULUS). */\nexport function isNtpShort(value: number): boolean {\n return Number.isInteger(value) && value >= 0 && value < NTP_SHORT_MODULUS\n}\n\n/**\n * RFC 1982 serial comparison of NTP-short `a` against `b`: `'after'` when `a`\n * is the later instant, `'before'` when it is the earlier one.\n *\n * The answer is the true order only while the two instants are less than\n * {@link NTP_SHORT_HALF_RANGE} apart. Past that a 32-bit field cannot tell\n * later from earlier, and the result names whichever direction is nearer.\n * This is not a total order (it is not transitive across the whole space), so\n * it must never be used to sort.\n *\n * @throws {RangeError} if either operand is not an NTP-short value\n */\nexport function compareNtpShort(a: number, b: number): NtpShortOrder {\n if (!isNtpShort(a) || !isNtpShort(b)) {\n throw new RangeError(\n `NTP-short values must be integers in [0, 2^${NTP_SHORT_BITS}), got ${String(a)} and ${String(b)}`,\n )\n }\n if (a === b) return 'equal'\n // Forward distance from b to a, reduced into [1, NTP_SHORT_MODULUS). Both\n // operands are below 2^32, so every step is exact in a double.\n const forward = (a - b + NTP_SHORT_MODULUS) % NTP_SHORT_MODULUS\n if (forward < NTP_SHORT_HALF_RANGE) return 'after'\n if (forward > NTP_SHORT_HALF_RANGE) return 'before'\n return 'unordered'\n}\n", "/**\n * FEC block profile (BLO-35597, ISO/IEC 23008-1 Annex C).\n *\n * Every catalog `fec` object that declares an FEC instance names exactly one\n * block profile in `fec.blockProfile`. A receiver applies all of a profile's\n * rules or none of them, so the token is the one explicit per-stream\n * discriminator: it is never defaulted and never inferred from the wire.\n *\n * - `k-padded-v1` (legacy, Blockcast-private): a source symbol is the\n * protected MMTP packet zero-padded to T with no length field; every block\n * is a K-symbol RaptorQ block whose ESIs `SSB_length..K-1` are implicit\n * zero padding; `SS_ID = SBN*K + ESI`, so `SS_Start = SBN*K`; repair\n * ESI = K + RS_ID; the MoQ repair group id is the SBN.\n * - `iso-ssbg1-v1`: ssbg_mode1 (C.3.3): a source symbol is\n * `u16 length || protected packet || zero padding` to T; a block is a\n * K' = SSB_length RaptorQ block (C.5.3) and repair ESI = SSB_length + RS_ID;\n * SS_ID increments by one per FEC source packet and wraps at 2^32 (C.5.2),\n * so a block is located by membership in `[SS_Start, SS_Start + SSB_length)`\n * under RFC 1982 serial arithmetic; the MoQ repair group id is the 62-bit\n * wrap-epoch-extended SS_Start. The AL-FEC coding byte for this profile is\n * 0x14 (one-stage, ssbg_mode1, fec_payload_id_mode 0, Amd 1:2025 Table C.3).\n */\nimport { serial32Add, serial32Distance, serial32Precedes } from './serial-number.js'\n\n/** The two block profiles a catalog `fec.blockProfile` may name. */\nexport type FecBlockProfile = 'k-padded-v1' | 'iso-ssbg1-v1'\n\n/** Every accepted `fec.blockProfile` token, legacy first. */\nexport const FEC_BLOCK_PROFILES: readonly FecBlockProfile[] = Object.freeze([\n 'k-padded-v1',\n 'iso-ssbg1-v1',\n])\n\n/** True for a known block-profile token. */\nexport function isFecBlockProfile(value: unknown): value is FecBlockProfile {\n return value === 'k-padded-v1' || value === 'iso-ssbg1-v1'\n}\n\n/**\n * Read a required `fec.blockProfile`. Throws on an absent or unknown token:\n * there is no default and nothing is inferred, because the two profiles carry\n * identical wire tuples whose repair math differs.\n *\n * @param where the catalog path of the field, for the error message.\n */\nexport function parseFecBlockProfile(value: unknown, where: string): FecBlockProfile {\n if (isFecBlockProfile(value)) return value\n if (value === undefined) {\n throw new Error(\n `${where} is required: name the FEC block profile (\"k-padded-v1\" or \"iso-ssbg1-v1\")`,\n )\n }\n throw new Error(\n `${where} must be \"k-padded-v1\" or \"iso-ssbg1-v1\" (got ${JSON.stringify(value)})`,\n )\n}\n\n/** ISO/IEC 23008-1 C.3.3 ssbg_mode1: the two-octet packet length field. */\nexport const SSBG1_LENGTH_FIELD_BYTES = 2\n\n/** The largest protected packet an ssbg_mode1 symbol of size T can carry. */\nexport function ssbg1MaxPacketLength(symbolSize: number): number {\n return symbolSize - SSBG1_LENGTH_FIELD_BYTES\n}\n\n/** Whether a protected packet of `packetLength` bytes fits one T-byte ssbg_mode1 symbol. */\nexport function ssbg1FitsSymbol(packetLength: number, symbolSize: number): boolean {\n return (\n Number.isSafeInteger(packetLength) &&\n packetLength >= 0 &&\n packetLength <= ssbg1MaxPacketLength(symbolSize) &&\n packetLength <= 0xffff\n )\n}\n\n/**\n * Build one ssbg_mode1 source symbol (C.3.3): the protected packet's length\n * in two octets, network byte order, then the packet, then zero octets up to\n * T. The protected packet is the MMTP packet before its Source FEC Payload ID\n * is appended (C.4.2).\n *\n * @throws when `length + 2 > T`: the packet cannot be protected under this\n * profile, and silently truncating it would corrupt every repair symbol.\n */\nexport function buildSsbg1SourceSymbol(packet: Uint8Array, symbolSize: number): Uint8Array {\n if (!Number.isSafeInteger(symbolSize) || symbolSize <= SSBG1_LENGTH_FIELD_BYTES) {\n throw new Error(`ssbg_mode1 symbol size must exceed ${SSBG1_LENGTH_FIELD_BYTES} (got ${symbolSize})`)\n }\n if (!ssbg1FitsSymbol(packet.byteLength, symbolSize)) {\n throw new Error(\n `ssbg_mode1 source symbol needs ${packet.byteLength} + ${SSBG1_LENGTH_FIELD_BYTES} bytes but T is ${symbolSize}`,\n )\n }\n const symbol = new Uint8Array(symbolSize)\n symbol[0] = (packet.byteLength >>> 8) & 0xff\n symbol[1] = packet.byteLength & 0xff\n symbol.set(packet, SSBG1_LENGTH_FIELD_BYTES)\n return symbol\n}\n\n/**\n * Recover the protected packet from one ssbg_mode1 source symbol: the bytes\n * the length field names. Returns null, never a guess, when the length field\n * points past the symbol or any padding octet is non-zero (C.3.3 padding is\n * all 00h), since either means the recovered symbol is not what was sent.\n */\nexport function trimSsbg1SourceSymbol(symbol: Uint8Array): Uint8Array | null {\n if (symbol.byteLength < SSBG1_LENGTH_FIELD_BYTES) return null\n const length = (symbol[0]! << 8) | symbol[1]!\n const end = SSBG1_LENGTH_FIELD_BYTES + length\n if (end > symbol.byteLength) return null\n for (let i = end; i < symbol.byteLength; i++) {\n if (symbol[i] !== 0) return null\n }\n return symbol.subarray(SSBG1_LENGTH_FIELD_BYTES, end)\n}\n\n/** The SS_ID after the last source symbol of `[ssStart, ssStart + ssbLength)`. */\nexport function isoBlockEnd(ssStart: number, ssbLength: number): number {\n return serial32Add(ssStart, ssbLength)\n}\n\n/**\n * Range membership (C.5.2, C.5.3): whether `ssId` is one of the\n * `ssbLength` consecutive SS_IDs starting at `ssStart`, modulo 2^32.\n */\nexport function isoBlockContains(ssStart: number, ssbLength: number, ssId: number): boolean {\n return serial32Distance(ssStart, ssId) < ssbLength\n}\n\n/**\n * A lower bound on how many blocks the block starting at `a` lies behind the\n * one starting at `b` (0 when `a` does not precede `b`). An iso-ssbg1-v1\n * block holds at most `kMax` source symbols (the catalog `sourceSymbols`, the\n * AL-FEC maximum_k_for_repair_flow), so `n * kMax` SS_IDs always span at least\n * `n` blocks. Every interleave horizon in the receiver (decoder retention,\n * worker retirement, ordered-relay settlement) uses this one bound, so the\n * manager's horizon can never fall inside the decoder's.\n */\nexport function isoBlocksBehindLowerBound(a: number, b: number, kMax: number): number {\n if (!Number.isSafeInteger(kMax) || kMax <= 0) {\n throw new Error(`iso-ssbg1-v1 block horizon needs a positive maximum K (got ${kMax})`)\n }\n return serial32Precedes(a, b) ? Math.floor(serial32Distance(a, b) / kMax) : 0\n}\n", "// Layered and overlay FEC repair contract (draft-ramadan-moq-fec \u00A76.3).\n//\n// One source track may carry several repair tracks. Every base layer is\n// generated from the same source partition (RaptorQ, K, T, interleaveDepthMs,\n// reorderToleranceMs) and differs from its siblings only in its repair symbol\n// count P_i. Layer i owns the Repair Symbol IDs [O_i, O_i + P_i) with\n// O_i = sum(P_j, j < i), and ESI = K + RS_ID, so any subset of layers combines\n// into one decode. A keyframe overlay is a second, independent FEC instance\n// covering only random access point fragments, with its own K and P and its\n// own RS_ID space starting at 0.\n//\n// Repair tracks describe themselves (`depends`, `repairLayer`, `scope`); names\n// carry no meaning. This module holds the one rule set both catalog dialects\n// (MSF `tracks[]` and hang `repair.renditions`) resolve through, so the CMSF\n// validator and the player resolver cannot drift apart.\n//\n// The one dialect difference is where layer 0 lives. In MSF, `fec.repairTrack`\n// must name a `fec-repair` track in `tracks[]`, so a dangling pointer fails\n// closed (`layerZeroMustBeDeclared`). In hang, layer 0 is named only by the\n// rendition's `fec.repairTrack` and needs no `repair.renditions` entry; an\n// entry there is optional and, when present, must agree with the source.\n\nimport { parseFecBlockProfile, type FecBlockProfile } from \"@blockcast/mmt-base\";\n\n/** Lowest MoQT-scale priority a repair track may declare. */\nexport const REPAIR_PRIORITY_MIN = 192;\n/** Highest MoQT-scale priority a repair track may declare. */\nexport const REPAIR_PRIORITY_MAX = 255;\n/** Priority of a base layer 0 track that declares none. */\nexport const DEFAULT_REPAIR_PRIORITY = 240;\n/** RFC 6330 \u00A74.4.1.2: the largest number of source symbols in one RaptorQ source block. */\nexport const MAX_RAPTORQ_SOURCE_SYMBOLS = 56403;\n/** The 24-bit RS_ID field (\u00A77.1) bounds the repair total of one FEC instance. */\nexport const MAX_REPAIR_SYMBOLS = 0xffffff;\n/** The 24-bit ESI bounds K + totalP - 1 for one FEC instance. */\nexport const MAX_ENCODING_SYMBOL_ID = 0xffffff;\n\nexport type RepairContractCode =\n\t| \"track_fec\"\n\t| \"track_repair_track\"\n\t| \"track_repair_depends\"\n\t| \"track_repair_layer\"\n\t| \"track_repair_priority\"\n\t| \"track_repair_overlay\"\n\t| \"track_repair_geometry\"\n\t| \"track_fec_bounds\"\n\t| \"track_fec_block_profile\";\n\n/** Reports one contract violation and never returns. */\nexport type RepairContractFail = (code: RepairContractCode, path: string, message: string) => never;\n\nexport class RepairContractError extends Error {\n\tconstructor(\n\t\tpublic readonly code: RepairContractCode,\n\t\tpublic readonly path: string,\n\t\tmessage: string,\n\t) {\n\t\tsuper(`[repair-layers:${code}] ${path}: ${message}`);\n\t\tthis.name = \"RepairContractError\";\n\t}\n}\n\nexport const throwRepairContractError: RepairContractFail = (code, path, message) => {\n\tthrow new RepairContractError(code, path, message);\n};\n\n/**\n * Read one catalog `fec.blockProfile` (BLO-35597, ISO/IEC 23008-1 Annex C):\n * the one rule both catalog dialects, the CMSF validator and the per-track\n * resolver apply.\n *\n * A `fec` block that declares an FEC instance (any `algorithm` other than\n * `\"none\"`) MUST name its block profile, so an absent or unknown token fails\n * with `track_fec_block_profile` at `path`. A block declaring no FEC instance\n * may omit it (returns undefined), but a token it does carry must be known.\n * The token is never defaulted and never inferred: `k-padded-v1` and\n * `iso-ssbg1-v1` carry identical wire tuples whose repair math differs.\n *\n * @param path JSON path of the `blockProfile` field, for the error.\n */\nexport function readFecBlockProfile(\n\tvalue: unknown,\n\tpath: string,\n\tdeclaresFecInstance: true,\n\tfail: RepairContractFail,\n): FecBlockProfile;\nexport function readFecBlockProfile(\n\tvalue: unknown,\n\tpath: string,\n\tdeclaresFecInstance: boolean,\n\tfail: RepairContractFail,\n): FecBlockProfile | undefined;\nexport function readFecBlockProfile(\n\tvalue: unknown,\n\tpath: string,\n\tdeclaresFecInstance: boolean,\n\tfail: RepairContractFail,\n): FecBlockProfile | undefined {\n\tif (value === undefined && !declaresFecInstance) return undefined;\n\ttry {\n\t\treturn parseFecBlockProfile(value, \"blockProfile\");\n\t} catch (error) {\n\t\treturn fail(\"track_fec_block_profile\", path, (error as Error).message);\n\t}\n}\n\n/** The self-describing fields of one repair track entry, in either dialect. */\nexport type RepairTrackDescriptor = {\n\t/** Track (MSF) or rendition (hang) name. */\n\tname: string;\n\t/** JSON path of the entry, for error reporting. */\n\tpath: string;\n\tdepends?: unknown;\n\trepairLayer?: unknown;\n\trepairSymbols?: unknown;\n\tpriority?: unknown;\n\tscope?: unknown;\n\tsourceSymbols?: unknown;\n};\n\n/** The source track's `fec` object as the layer contract reads it. */\nexport type RepairSourceGeometry = {\n\t/** Source track or rendition name. */\n\tname: string;\n\t/** JSON path of the `fec` object, for error reporting. */\n\tpath: string;\n\talgorithm?: unknown;\n\t/** K. */\n\tsourceSymbols?: unknown;\n\t/** P_0. */\n\trepairSymbols?: unknown;\n\t/** Layer 0 track name. */\n\trepairTrack?: unknown;\n};\n\n/** A resolved base repair layer with its derived RS_ID range. */\nexport type RepairLayerParams = {\n\t/** Layer index; 0 is the track named by the source's `fec.repairTrack`. */\n\tlayer: number;\n\trepairTrackName: string;\n\t/** P_i: repair symbols per block carried by this layer. */\n\trepairSymbols: number;\n\t/** O_i = sum(P_j for j < i): the first RS_ID this layer carries. ESI = K + RS_ID. */\n\trsIdOffset: number;\n\t/** MoQT-scale priority (lower first); 240 when layer 0 declares none. */\n\tpriority: number;\n};\n\n/** A resolved keyframe overlay: a separate FEC instance over RAP fragments only. */\nexport type RepairOverlayParams = {\n\trepairTrackName: string;\n\tscope: \"keyframe\";\n\t/** K_overlay: source symbols of the overlay instance. */\n\tsourceSymbols: number;\n\t/** P_overlay: repair symbols per overlay block; also the overlay's own RSB_length. */\n\trepairSymbols: number;\n\t/** MoQT-scale priority, independent of the base layer chain. */\n\tpriority: number;\n};\n\nexport type RepairLayersResolution = {\n\tsourceTrackName: string;\n\t/** Base layers in layer order, layer 0 first. Never empty. */\n\tbaseLayers: readonly RepairLayerParams[];\n\t/** Keyframe overlay instances, in catalog order. Empty when none is declared. */\n\t/** At most one entry: the wire carries one overlay trailer per RAP packet. */\n\toverlays: readonly RepairOverlayParams[];\n\t/** Sum of P over base layers: the wire RSB_length on every base layer packet. */\n\ttotalBaseRepairSymbols: number;\n};\n\nconst REPAIR_FIELDS = [\"depends\", \"repairLayer\", \"repairSymbols\", \"priority\", \"scope\", \"sourceSymbols\"] as const;\n\n/** Track-level block geometry keys a repair track must not carry (\u00A76.3 rule 8): geometry is the source's. */\nexport const REPAIR_TRACK_GEOMETRY_KEYS = [\n\t\"mmtpMode\",\n\t\"timescale\",\n\t\"groupDurationMs\",\n\t\"groupDurationTicks\",\n\t\"keyframeIntervalMs\",\n\t\"keyframeIntervalTicks\",\n\t\"fec\",\n\t\"symbolSize\",\n\t\"interleaveDepthMs\",\n] as const;\n\n/** Self-describing repair fields that only a repair track or repair rendition may carry. */\nexport const REPAIR_TRACK_ONLY_KEYS = [\"repairLayer\", \"repairSymbols\", \"sourceSymbols\", \"scope\"] as const;\n\n/** Rule 8: a repair track (MSF) or repair rendition (hang) carries no block geometry. */\nexport function rejectRepairTrackGeometry(path: string, fields: Record<string, unknown>, fail: RepairContractFail): void {\n\tfor (const key of REPAIR_TRACK_GEOMETRY_KEYS) {\n\t\tif (fields[key] !== undefined) {\n\t\t\tfail(\n\t\t\t\t\"track_repair_geometry\",\n\t\t\t\t`${path}.${key}`,\n\t\t\t\t\"a repair track carries no block geometry; it is shared with the source track's fec object\",\n\t\t\t);\n\t\t}\n\t}\n}\n\n/** Which repair tracks a catalog can reach, for rule 9 (no orphan repair track). */\nexport type RepairReachability = {\n\t/** True once any track or rendition carries `fec` with an algorithm other than `none`. */\n\tdeclaresFecSource: boolean;\n\t/** Every non-empty `fec.repairTrack` a FEC source names: the layer-0 tracks. */\n\tlayerZeroNames: ReadonlySet<string>;\n};\n\n/**\n * Derive rule 9's inputs from the raw `fec` objects of every media track (MSF,\n * packaging other than `fec-repair`) or media rendition (hang) in a catalog.\n * Callers must not pass repair tracks: rule 8 forbids `fec` on them, and a\n * repair track that carried one would otherwise name itself as its own\n * layer 0 and slip past rule 9. One derivation for the validator and both\n * resolver dialects, so an empty-string pointer or an `algorithm: \"none\"`\n * source is read the same way everywhere.\n */\nexport function repairReachability(fecObjects: Iterable<unknown>): RepairReachability {\n\tlet declaresFecSource = false;\n\tconst layerZeroNames = new Set<string>();\n\tfor (const candidate of fecObjects) {\n\t\tif (typeof candidate !== \"object\" || candidate === null || Array.isArray(candidate)) continue;\n\t\tconst fec = candidate as Record<string, unknown>;\n\t\tif (fec[\"algorithm\"] === \"none\") continue;\n\t\tdeclaresFecSource = true;\n\t\tconst repairTrack = fec[\"repairTrack\"];\n\t\tif (typeof repairTrack === \"string\" && repairTrack.length > 0) layerZeroNames.add(repairTrack);\n\t}\n\treturn { declaresFecSource, layerZeroNames };\n}\n\n/**\n * Rule 9: once a catalog declares a FEC source, a repair track that declares no\n * `depends` must be some source's layer 0. A catalog with no FEC source may\n * still carry a naming-era repair track that nothing joins.\n */\nexport function assertRepairTrackReachable(\n\tdescriptor: RepairTrackDescriptor,\n\treachability: RepairReachability,\n\tfail: RepairContractFail,\n): void {\n\tif (dependsTarget(descriptor) !== undefined) return;\n\tif (reachability.declaresFecSource && !reachability.layerZeroNames.has(descriptor.name)) {\n\t\tfail(\n\t\t\t\"track_repair_depends\",\n\t\t\t`${descriptor.path}.depends`,\n\t\t\t\"no source names this repair track in fec.repairTrack and it declares no depends\",\n\t\t);\n\t}\n}\n\n/** A media track (MSF) or media rendition (hang) carries no repair-track fields. */\nexport function rejectRepairFieldsOnMediaTrack(path: string, fields: Record<string, unknown>, fail: RepairContractFail): void {\n\tfor (const key of REPAIR_TRACK_ONLY_KEYS) {\n\t\tif (fields[key] !== undefined) {\n\t\t\tfail(\"track_repair_layer\", `${path}.${key}`, `${key} is a repair track field`);\n\t\t}\n\t}\n}\n\n/** Pick the self-describing repair fields out of a raw track or rendition record. */\nexport function describeRepairTrack(name: string, path: string, fields: Record<string, unknown>): RepairTrackDescriptor {\n\tconst descriptor: RepairTrackDescriptor = { name, path };\n\tfor (const field of REPAIR_FIELDS) {\n\t\tif (field in fields) descriptor[field] = fields[field];\n\t}\n\treturn descriptor;\n}\n\nfunction isSafeInteger(value: unknown): value is number {\n\treturn typeof value === \"number\" && Number.isSafeInteger(value);\n}\n\nfunction positiveInteger(value: unknown): value is number {\n\treturn isSafeInteger(value) && value > 0;\n}\n\n/** The single source a repair track names in `depends`, once its shape has been validated. */\nexport function dependsTarget(descriptor: RepairTrackDescriptor): string | undefined {\n\tconst depends = descriptor.depends;\n\treturn Array.isArray(depends) && depends.length === 1 && typeof depends[0] === \"string\" && depends[0].length > 0\n\t\t? depends[0]\n\t\t: undefined;\n}\n\n/**\n * Validate the shape of one repair track's self-describing fields, independent\n * of any source. Cross-track rules (layer 0 agreement, contiguity, priority\n * order, bounds) live in `assembleRepairLayers`.\n */\nexport function validateRepairTrackFields(descriptor: RepairTrackDescriptor, fail: RepairContractFail): void {\n\tconst { path } = descriptor;\n\tif (descriptor.depends !== undefined && dependsTarget(descriptor) === undefined) {\n\t\tfail(\"track_repair_depends\", `${path}.depends`, \"must name exactly one source track\");\n\t}\n\tif (descriptor.repairLayer !== undefined && (!isSafeInteger(descriptor.repairLayer) || descriptor.repairLayer < 0)) {\n\t\tfail(\"track_repair_layer\", `${path}.repairLayer`, \"must be a non-negative integer\");\n\t}\n\tif (\n\t\tdescriptor.repairSymbols !== undefined &&\n\t\t(!positiveInteger(descriptor.repairSymbols) || descriptor.repairSymbols > MAX_REPAIR_SYMBOLS)\n\t) {\n\t\tfail(\"track_repair_layer\", `${path}.repairSymbols`, `must be a positive integer at most ${MAX_REPAIR_SYMBOLS}`);\n\t}\n\tif (\n\t\tdescriptor.priority !== undefined &&\n\t\t(!isSafeInteger(descriptor.priority) ||\n\t\t\tdescriptor.priority < REPAIR_PRIORITY_MIN ||\n\t\t\tdescriptor.priority > REPAIR_PRIORITY_MAX)\n\t) {\n\t\tfail(\n\t\t\t\"track_repair_priority\",\n\t\t\t`${path}.priority`,\n\t\t\t`must be an integer from ${REPAIR_PRIORITY_MIN} to ${REPAIR_PRIORITY_MAX} (MoQT scale, lower first)`,\n\t\t);\n\t}\n\tif (descriptor.scope !== undefined) {\n\t\tif (descriptor.scope !== \"keyframe\") {\n\t\t\tfail(\"track_repair_overlay\", `${path}.scope`, 'must be \"keyframe\"');\n\t\t}\n\t\tif (descriptor.repairLayer !== undefined) {\n\t\t\tfail(\"track_repair_overlay\", `${path}.repairLayer`, \"an overlay is a separate FEC instance and carries no repairLayer\");\n\t\t}\n\t\tfor (const field of [\"depends\", \"sourceSymbols\", \"repairSymbols\", \"priority\"] as const) {\n\t\t\tif (descriptor[field] === undefined) {\n\t\t\t\tfail(\"track_repair_overlay\", `${path}.${field}`, `an overlay must declare ${field}`);\n\t\t\t}\n\t\t}\n\t}\n\tif (descriptor.sourceSymbols !== undefined) {\n\t\tif (descriptor.scope === undefined) {\n\t\t\tfail(\"track_repair_overlay\", `${path}.sourceSymbols`, \"only an overlay (scope: keyframe) declares its own sourceSymbols\");\n\t\t}\n\t\tif (!positiveInteger(descriptor.sourceSymbols) || descriptor.sourceSymbols > MAX_RAPTORQ_SOURCE_SYMBOLS) {\n\t\t\tfail(\n\t\t\t\t\"track_fec_bounds\",\n\t\t\t\t`${path}.sourceSymbols`,\n\t\t\t\t`must be a positive integer at most ${MAX_RAPTORQ_SOURCE_SYMBOLS} (RFC 6330 source block limit)`,\n\t\t\t);\n\t\t}\n\t}\n\tif (isSafeInteger(descriptor.repairLayer) && descriptor.repairLayer >= 1) {\n\t\tfor (const field of [\"depends\", \"repairSymbols\", \"priority\"] as const) {\n\t\t\tif (descriptor[field] === undefined) {\n\t\t\t\tfail(\"track_repair_layer\", `${path}.${field}`, `repair layer ${descriptor.repairLayer} must declare ${field}`);\n\t\t\t}\n\t\t}\n\t}\n}\n\n/**\n * Resolve the base layers and overlays of one source track and derive each\n * base layer's RS_ID offset. Fails closed on every \u00A76.3 rule violation through\n * `fail`, which receives the code and the JSON path of the offending field.\n *\n * `descriptors` are every repair track (or repair rendition) in the catalog,\n * already shape-checked by `validateRepairTrackFields`. Returns undefined when\n * the source names no layer 0 and nothing depends on it.\n */\nexport type AssembleRepairLayersOptions = {\n\t/**\n\t * MSF: `fec.repairTrack` must name one of `descriptors` (a `fec-repair`\n\t * track), so a dangling pointer fails closed with `track_repair_track`.\n\t * hang: layer 0 lives outside `repair.renditions`, so leave this unset.\n\t */\n\tlayerZeroMustBeDeclared?: boolean;\n};\n\nexport function assembleRepairLayers(\n\tsource: RepairSourceGeometry,\n\tdescriptors: readonly RepairTrackDescriptor[],\n\tfail: RepairContractFail = throwRepairContractError,\n\toptions: AssembleRepairLayersOptions = {},\n): RepairLayersResolution | undefined {\n\tconst layerZeroName =\n\t\ttypeof source.repairTrack === \"string\" && source.repairTrack.length > 0 ? source.repairTrack : undefined;\n\tconst layerZero = layerZeroName === undefined ? undefined : descriptors.find((d) => d.name === layerZeroName);\n\tif (layerZeroName !== undefined && layerZero === undefined && options.layerZeroMustBeDeclared) {\n\t\tfail(\"track_repair_track\", `${source.path}.repairTrack`, `unknown FEC repair track ${JSON.stringify(layerZeroName)}`);\n\t}\n\tconst dependents = descriptors.filter((d) => d.name !== layerZeroName && dependsTarget(d) === source.name);\n\tconst k = positiveInteger(source.sourceSymbols) ? source.sourceSymbols : 0;\n\t// RFC 6330 bounds every RaptorQ source block, with or without a repair track.\n\tif (source.algorithm === \"raptorq\" && k > MAX_RAPTORQ_SOURCE_SYMBOLS) {\n\t\tfail(\n\t\t\t\"track_fec_bounds\",\n\t\t\t`${source.path}.sourceSymbols`,\n\t\t\t`must be at most ${MAX_RAPTORQ_SOURCE_SYMBOLS} (RFC 6330 source block limit)`,\n\t\t);\n\t}\n\tif (layerZeroName === undefined) {\n\t\tif (dependents.length === 0) return undefined;\n\t\tfail(\n\t\t\t\"track_repair_layer\",\n\t\t\t`${dependents[0]!.path}.depends`,\n\t\t\t`repair layers of ${JSON.stringify(source.name)} require a layer 0 (fec.repairTrack) on the source`,\n\t\t);\n\t}\n\n\tconst layered = dependents.length > 0;\n\tlet layerZeroSymbols = 0;\n\tif (positiveInteger(source.repairSymbols)) {\n\t\tlayerZeroSymbols = source.repairSymbols;\n\t} else if (layered) {\n\t\tfail(\"track_fec\", `${source.path}.repairSymbols`, \"layered or overlay repair needs a positive integer repairSymbols on layer 0\");\n\t}\n\tif (layered && !positiveInteger(source.sourceSymbols)) {\n\t\tfail(\"track_fec\", `${source.path}.sourceSymbols`, \"layered or overlay repair needs a positive integer sourceSymbols on the source\");\n\t}\n\tif (layered && source.algorithm !== \"raptorq\") {\n\t\tfail(\n\t\t\t\"track_fec\",\n\t\t\t`${source.path}.algorithm`,\n\t\t\t\"repair layers and overlays are defined only for raptorq: a block code fixes its codeword length, so symbols of a larger P are not symbols of the code a receiver holding fewer layers decodes\",\n\t\t);\n\t}\n\n\tlet layerZeroPriority = DEFAULT_REPAIR_PRIORITY;\n\tif (layerZero !== undefined) {\n\t\tif (layerZero.depends !== undefined && dependsTarget(layerZero) !== source.name) {\n\t\t\tfail(\n\t\t\t\t\"track_repair_depends\",\n\t\t\t\t`${layerZero.path}.depends`,\n\t\t\t\t`layer 0 of ${JSON.stringify(source.name)} must depend on that source`,\n\t\t\t);\n\t\t}\n\t\tif (layerZero.repairLayer !== undefined && layerZero.repairLayer !== 0) {\n\t\t\tfail(\"track_repair_layer\", `${layerZero.path}.repairLayer`, \"the track named by fec.repairTrack is layer 0\");\n\t\t}\n\t\tif (layerZero.scope !== undefined) {\n\t\t\tfail(\"track_repair_overlay\", `${layerZero.path}.scope`, \"the track named by fec.repairTrack is base layer 0, not an overlay\");\n\t\t}\n\t\tif (layerZero.repairSymbols !== undefined && layerZero.repairSymbols !== layerZeroSymbols) {\n\t\t\tfail(\n\t\t\t\t\"track_repair_layer\",\n\t\t\t\t`${layerZero.path}.repairSymbols`,\n\t\t\t\t`must agree with the source's fec.repairSymbols (${layerZeroSymbols})`,\n\t\t\t);\n\t\t}\n\t\tif (typeof layerZero.priority === \"number\") layerZeroPriority = layerZero.priority;\n\t}\n\n\tconst baseLayers: RepairLayerParams[] = [\n\t\t{ layer: 0, repairTrackName: layerZeroName!, repairSymbols: layerZeroSymbols, rsIdOffset: 0, priority: layerZeroPriority },\n\t];\n\t// Clarification 7 fixes the wire: a RAP packet carries exactly two Source\n\t// FEC Payload IDs, base first and overlay last, and the receiver strips a\n\t// fixed trailer length. A second overlay would need a third trailer that\n\t// the draft does not define, so one overlay per source is the contract.\n\tconst overlays: RepairOverlayParams[] = [];\n\tfor (const d of dependents) {\n\t\tif (d.scope === undefined && d.repairLayer === undefined) {\n\t\t\tfail(\n\t\t\t\t\"track_repair_layer\",\n\t\t\t\t`${d.path}.repairLayer`,\n\t\t\t\t`a repair track that depends on ${JSON.stringify(source.name)} must declare repairLayer (1..n) or scope: \"keyframe\"`,\n\t\t\t);\n\t\t}\n\t}\n\tconst layerTracks = dependents\n\t\t.filter((d) => d.scope === undefined)\n\t\t.sort((a, b) => (a.repairLayer as number) - (b.repairLayer as number));\n\tfor (const d of dependents) {\n\t\tif (d.scope !== \"keyframe\") continue;\n\t\tif (overlays.length === 1) {\n\t\t\tfail(\n\t\t\t\t\"track_repair_overlay\",\n\t\t\t\t`${d.path}.scope`,\n\t\t\t\t`${JSON.stringify(source.name)} already has keyframe overlay ${JSON.stringify(overlays[0]!.repairTrackName)}: a RAP packet carries exactly two Source FEC Payload IDs (base, then overlay), so one overlay per source`,\n\t\t\t);\n\t\t}\n\t\tconst sourceSymbols = d.sourceSymbols as number;\n\t\tconst repairSymbols = d.repairSymbols as number;\n\t\tif (sourceSymbols + repairSymbols - 1 > MAX_ENCODING_SYMBOL_ID) {\n\t\t\tfail(\n\t\t\t\t\"track_fec_bounds\",\n\t\t\t\t`${d.path}.repairSymbols`,\n\t\t\t\t`overlay sourceSymbols + repairSymbols - 1 exceeds the 24-bit ESI space (${MAX_ENCODING_SYMBOL_ID})`,\n\t\t\t);\n\t\t}\n\t\toverlays.push({ repairTrackName: d.name, scope: \"keyframe\", sourceSymbols, repairSymbols, priority: d.priority as number });\n\t}\n\n\tlet offset = layerZeroSymbols;\n\tlet previousPriority = layerZeroPriority;\n\tlayerTracks.forEach((d, i) => {\n\t\tconst expected = i + 1;\n\t\tif (d.repairLayer === 0) {\n\t\t\tfail(\"track_repair_layer\", `${d.path}.repairLayer`, `layer 0 of ${JSON.stringify(source.name)} is ${JSON.stringify(layerZeroName)}, named by fec.repairTrack`);\n\t\t}\n\t\tif (d.repairLayer !== expected) {\n\t\t\tfail(\n\t\t\t\t\"track_repair_layer\",\n\t\t\t\t`${d.path}.repairLayer`,\n\t\t\t\ti > 0 && d.repairLayer === layerTracks[i - 1]!.repairLayer\n\t\t\t\t\t? `repair layer ${d.repairLayer} is declared twice for ${JSON.stringify(source.name)}`\n\t\t\t\t\t: `repair layers must be contiguous from 1: expected ${expected}, found ${d.repairLayer}`,\n\t\t\t);\n\t\t}\n\t\tconst priority = d.priority as number;\n\t\tif (priority < previousPriority) {\n\t\t\tfail(\n\t\t\t\t\"track_repair_priority\",\n\t\t\t\t`${d.path}.priority`,\n\t\t\t\t`must be at least ${previousPriority}, the priority of layer ${expected - 1}: priority is monotonic non-decreasing with layer index`,\n\t\t\t);\n\t\t}\n\t\tpreviousPriority = priority;\n\t\tconst repairSymbols = d.repairSymbols as number;\n\t\tbaseLayers.push({ layer: expected, repairTrackName: d.name, repairSymbols, rsIdOffset: offset, priority });\n\t\toffset += repairSymbols;\n\t});\n\n\tconst totalBaseRepairSymbols = offset;\n\tif (totalBaseRepairSymbols > MAX_REPAIR_SYMBOLS) {\n\t\tfail(\n\t\t\t\"track_fec_bounds\",\n\t\t\t`${layerTracks.length === 0 ? source.path : layerTracks[layerTracks.length - 1]!.path}.repairSymbols`,\n\t\t\t`total repair symbols across base layers exceeds the 24-bit RS_ID space (${MAX_REPAIR_SYMBOLS})`,\n\t\t);\n\t}\n\tif (k + totalBaseRepairSymbols - 1 > MAX_ENCODING_SYMBOL_ID) {\n\t\tfail(\n\t\t\t\"track_fec_bounds\",\n\t\t\t`${source.path}.sourceSymbols`,\n\t\t\t`sourceSymbols + total base repair symbols - 1 exceeds the 24-bit ESI space (${MAX_ENCODING_SYMBOL_ID})`,\n\t\t);\n\t}\n\n\treturn { sourceTrackName: source.name, baseLayers, overlays, totalBaseRepairSymbols };\n}\n", "/**\n * D-28 shared repair-fetch scheduler.\n *\n * Player-agnostic orchestration for per-SBN on-demand MoQ FEC repair\n * backfill. Each player (moqtail client, hang watch, Shaka MSF bridge)\n * supplies only its protocol verb via {@link RepairFetchTransport}:\n *\n * - moqtail: standalone FETCH (FetchType.Standalone) for group [sbn, sbn+1)\n * - moq-lite/hang: Subscribe with startGroup=sbn, endGroup=sbn (the relay\n * propagates startGroup upstream to pull the past group from the origin's\n * producer cache)\n *\n * Everything else \u2014 registration, fire-time repair-track resolution,\n * keyframe-first admission, preemption, feeding symbols, and in-flight slot\n * release \u2014 is identical across players and lives here. The Player owns the\n * scheduler: one per `wireRepairFetch` call, and its disposer aborts\n * everything.\n *\n * Priority (from the block's class, see `repairPriorityForClass`):\n * - P0 (keyframe / unknown) and P1 (audio) dispatch as soon as they are\n * enqueued, with no cap. Each has its own fail deadline, and a cap would\n * turn a recoverable protected block into a stall; their number is\n * already bounded by the blocks one fail window can hold.\n * - P2 (delta) dispatches only while no P0/P1 is queued or in flight,\n * earliest absolute fail deadline first. Enqueuing a P0/P1 aborts every\n * in-flight P2 (`repair-preempted`), which returns to the queue while its\n * block is unresolved. A P2 that never ran before its block resolved is\n * reported as `repair-deferred`.\n * - A block reclassified to a higher class while its P2 fetch is in flight\n * is re-issued at once under the new class's wire priority: the running\n * FETCH is aborted (FETCH_CANCEL) and a new one sent.\n *\n * Wire priority (draft-ramadan-moq-fec \u00A711.4.5): every request carries the\n * MoQ Transport SUBSCRIBER_PRIORITY its FETCH must use, derived from the two\n * catalog priorities `ResolveRepairTarget` returns (see\n * `repairFetchSubscriberPriority`): a keyframe-bearing (or unknown) block's\n * FETCH is sent in the Source Media range at no more than the source\n * track's priority, so it is delivered ahead of every repair track and not\n * shed with repair data; a delta or audio FETCH is sent at no less than the\n * fetched repair track's priority, so it is shed first.\n *\n * Deadline and admission (BLO-35597): a repair FETCH rides the reliable path,\n * so its deadline is the block's PRESENTATION deadline, not the multicast\n * reassembly deadline. The player states how long after a block's first\n * symbol its media can still be presented (`presentationDelayMs`); from the\n * block's receiver-local anchor that gives an absolute deadline, never earlier\n * than the multicast wait and never later than the release gate's hold ceiling\n * (`terminalRetentionMsForPacket`), which bounds how long any successor frame\n * may wait behind the block. A FETCH is sent only when the measured\n * reliable-path RTT (`reliableRttMs`) is less than the time left to that\n * deadline -- a request that cannot come back in time is never sent, rather\n * than sent and cancelled -- and an admitted FETCH holds the block open to\n * that deadline, so the multicast span elapsing never aborts it.\n *\n * The scheduler arms no timers. A fetch ends when its transport completes or\n * throws, when it is preempted, when any later scheduling event finds it past\n * its presentation deadline, when its block resolves (recovered, or failed at\n * that deadline), on track reset, or on dispose.\n */\n\nimport type { FecBlockProfile } from '@blockcast/mmt-base'\nimport { repairPriorityForClass, type BlockClass, type RepairPriority } from './block-policy.js'\nimport type { RepairLifecycleObserver } from './multicast-manager.js'\nimport { REPAIR_PRIORITY_MIN } from './repair-layers.js'\n\n/** The MoQ Transport priority scale: 0..255, a lower value is delivered first. */\nconst MOQT_PRIORITY_MAX = 255\n\n/**\n * The highest value in the Source Media priority range of\n * draft-ramadan-moq-fec \u00A710 (64..191): one below the repair range, which\n * starts at `REPAIR_PRIORITY_MIN` (192).\n */\nexport const SOURCE_MEDIA_PRIORITY_MAX = REPAIR_PRIORITY_MIN - 1\n\n/** What `ResolveRepairTarget` returns for one repair-bearing stream. */\nexport interface RepairTarget {\n /** The repair track to FETCH (base layer 0 of the block's source). */\n readonly repairTrack: string\n /**\n * The catalog `priority` of the SOURCE track the block belongs to, on the\n * MoQ Transport scale (0..255, lower first), or null when the catalog\n * carries none. Never a player constant: a keyframe repair must not be\n * delivered behind the source it repairs, and only the catalog says what\n * that source's priority is.\n */\n readonly sourcePriority: number | null\n /**\n * The catalog priority of `repairTrack` (MoQ Transport scale). Layer 0\n * that declares none has the value draft-ramadan-moq-fec \u00A710 defines\n * (240); `resolveRepairLayers` / `catalogRepairTarget` already apply it.\n * Null only when the resolver cannot read the repair track's entry.\n */\n readonly repairPriority: number | null\n}\n\n/** Resolve a packetId to its repair track and priorities at FIRE TIME.\n *\n * Called on every repair request, never cached by the shell: ABR ladder\n * rungs can share a packetId (e.g. video/720p and video/360p both p:1), so\n * the mapping must reflect the track active when the request fires, not\n * when the shell was wired. Return null when the packetId has no\n * repair-bearing track \u2014 the request is released without a fetch.\n * `catalogRepairTarget` builds the answer from a catalog. */\nexport type ResolveRepairTarget = (\n packetId: number,\n catalogTrackName?: string,\n) => RepairTarget | null\n\n/** One on-demand repair FETCH, as the scheduler hands it to the transport. */\nexport interface RepairFetchRequest {\n /** Repair track whose block `sbn` is fetched. */\n readonly repairTrack: string\n /**\n * The block's key, by `blockProfile` (BLO-35597 D2a):\n * - `k-padded-v1`: the source block number, which IS the repair-track MoQ\n * group id (fetch `[sbn, sbn]`).\n * - `iso-ssbg1-v1`: the block's 32-bit SS_Start. The MoQ group id is the\n * 62-bit `extSsStart(wrapEpoch, sbn)`; a receiver that knows only a lost\n * SS_ID fetches `repairFetchRange(lostSsId, kMax, anchor)`, where `anchor`\n * is `{ previousBlockStart }` (the extended start of the last block\n * delimited before it) or `{ reference }` (an epoch reference). Never use\n * it as a group id directly.\n */\n readonly sbn: number\n /** The block profile `sbn` is keyed by (the catalog `fec.blockProfile`). */\n readonly blockProfile: FecBlockProfile\n /** The source packetId the block belongs to. */\n readonly packetId: number\n /**\n * Aborts when the scheduler no longer wants the group: preemption by\n * keyframe work, reprioritisation, block resolution, track reset, dispose.\n */\n readonly signal: AbortSignal\n /**\n * The SUBSCRIBER_PRIORITY parameter the FETCH MUST carry, on the MoQ\n * Transport scale (0..255, lower delivered first), as\n * draft-ramadan-moq-fec \u00A711.4.5 requires. `undefined` means OMIT the\n * parameter: the catalog carries no priority to derive it from, and the\n * publisher then uses the value draft-ietf-moq-transport-16 \u00A79.2.2.3\n * defines for an omitted parameter (128, inside the Source Media range).\n * A transport never substitutes a value of its own. A transport whose API\n * uses another scale (moq-lite's higher-wins weight, say) maps this one\n * without reordering it against the source and repair subscriptions.\n */\n readonly subscriberPriority: number | undefined\n}\n\n/** Protocol-specific fetch of one FEC block's repair group.\n *\n * Yield each repair object payload (MMTP packet bytes, ISO 23008-1 \u00A7C.5.3\n * Repair FEC Payload ID framing). Return when the group ends.\n *\n * Put `request.subscriberPriority` on the FETCH (see RepairFetchRequest).\n *\n * When `request.signal` aborts, the transport MUST cancel the FETCH ON THE\n * WIRE, not merely stop reading: send FETCH_CANCEL for its Request ID\n * (draft-ietf-moq-transport-16 \u00A79.18; STOP_SENDING on an open data stream\n * as well, \u00A75.2), and do it from the abort handler itself, before it\n * returns. A relay that is never told keeps sending the group, so a\n * preempted delta repair keeps competing with the keyframe repair that\n * preempted it. The scheduler may issue the next request for the same block\n * (reprioritisation) as soon as it aborts one, and \u00A711.4.5 allows at most\n * one repair FETCH outstanding per block and repair layer, so the cancel\n * must already be queued ahead of that request. A FETCH whose Request ID\n * is not known yet is cancelled as soon as FETCH_OK assigns it. */\nexport type RepairFetchTransport = (request: RepairFetchRequest) => AsyncIterable<Uint8Array>\n\n/** Why a request could not carry a catalog-derived priority. */\nexport type RepairPriorityReason =\n | 'source-priority-absent'\n | 'source-priority-invalid'\n | 'repair-priority-absent'\n | 'repair-priority-invalid'\n\n/**\n * The wire priority of one repair request (draft-ramadan-moq-fec \u00A711.4.5).\n *\n * - `carry`: send the FETCH with `subscriberPriority`.\n * - `omit`: send it without SUBSCRIBER_PRIORITY. Only a keyframe-bearing or\n * unknown block's request is ever sent this way: the publisher then uses\n * 128 (draft-ietf-moq-transport-16 \u00A79.2.2.3), which lies in the Source\n * Media range, and without a catalog source priority there is no bound\n * below it to honour.\n * - `decline`: send no FETCH. A delta or audio FETCH MUST carry a priority\n * at least its repair track's, so at least 192, and the omitted value (128)\n * would put best-effort repair ahead of every repair track. Unicast repair\n * is optional for those classes (\u00A711.4.3, \u00A711.4.4); in-band FEC and\n * concealment still apply.\n */\nexport type RepairFetchPriority =\n | { readonly kind: 'carry'; readonly subscriberPriority: number }\n | { readonly kind: 'omit'; readonly reason: RepairPriorityReason }\n | { readonly kind: 'decline'; readonly reason: RepairPriorityReason }\n\nfunction isMoqtPriority(value: number): boolean {\n return Number.isSafeInteger(value) && value >= 0 && value <= MOQT_PRIORITY_MAX\n}\n\n/**\n * Derive the SUBSCRIBER_PRIORITY of a repair FETCH from the block's class and\n * the catalog priorities of its source and repair tracks\n * (draft-ramadan-moq-fec \u00A711.4.5 and the \u00A710 ranges):\n *\n * - keyframe / unknown: in the Source Media range and no greater than the\n * source track's priority, i.e. min(source, 191); omitted when the catalog\n * carries no source priority.\n * - delta / audio: no lower than the fetched repair track's priority and at\n * least 192, i.e. max(repair, 192); declined when it carries none.\n */\nexport function repairFetchSubscriberPriority(\n blockClass: BlockClass,\n target: Pick<RepairTarget, 'sourcePriority' | 'repairPriority'>,\n): RepairFetchPriority {\n if (repairPriorityForClass(blockClass) === 0) {\n const source = target.sourcePriority\n if (source === null) return { kind: 'omit', reason: 'source-priority-absent' }\n if (!isMoqtPriority(source)) return { kind: 'omit', reason: 'source-priority-invalid' }\n return { kind: 'carry', subscriberPriority: Math.min(source, SOURCE_MEDIA_PRIORITY_MAX) }\n }\n const repair = target.repairPriority\n if (repair === null) return { kind: 'decline', reason: 'repair-priority-absent' }\n if (!isMoqtPriority(repair)) return { kind: 'decline', reason: 'repair-priority-invalid' }\n return { kind: 'carry', subscriberPriority: Math.max(repair, REPAIR_PRIORITY_MIN) }\n}\n\nexport interface WireRepairFetchOptions {\n resolveTarget: ResolveRepairTarget\n transport: RepairFetchTransport\n /**\n * Maximum transport attempts per block (required). A retry happens only\n * when the transport rejects with an error `shouldRetry` classifies as\n * retryable, before any symbol was fed, while the block's deadline remains.\n */\n attemptLimit: number\n /** Classify a transport error as a transient publisher/relay retention race. */\n shouldRetry?: (error: unknown) => boolean\n /**\n * The measured round-trip time of the reliable path the transport fetches\n * over, in ms (required). Read at every admission, so it tracks the live\n * session. A value that is not finite and positive, or a throw, means the\n * path offers no measurement: nothing can show the FETCH would come back in\n * time, so none is sent (`rtt-unmeasured`). Never a default.\n */\n reliableRttMs: () => number\n /**\n * How long after a block's first symbol arrived its media can still be\n * presented, in ms (required): the player's own playout budget for the\n * stream. Same fail-closed rule as `reliableRttMs`\n * (`presentation-delay-unavailable`).\n */\n presentationDelayMs: (packetId: number, catalogTrackName?: string) => number\n}\n\n/** Why the D-28 layer asked for a repair: the block's class at its deadline. */\nexport type RepairSubscribeCallback = (\n sbn: number,\n packetId: number,\n catalogTrackName: string | undefined,\n blockClass: BlockClass,\n) => void\n\n/** Why the scheduler sent no FETCH for a block it was asked to repair. */\nexport type RepairSkipReason =\n /** The reliable path offered no RTT measurement; arrival cannot be shown. */\n | 'rtt-unmeasured'\n /** The measured RTT is not less than the time left to the deadline. */\n | 'rtt-exceeds-budget'\n /** The player gave no usable presentation delay for the block's stream. */\n | 'presentation-delay-unavailable'\n\n/** The subset of MulticastManager the scheduler drives. */\nexport interface RepairFetchHost {\n readonly fecManager: {\n blockProfileForPacket(packetId: number): FecBlockProfile | undefined\n repairFetchTimeoutMsForBlock(sbn: number, packetId: number, catalogTrackName?: string): number\n /** Receiver-local time of the block's first accepted symbol, if any. */\n blockAnchorAtMs(sbn: number, packetId: number, catalogTrackName?: string): number | undefined\n /** How long a terminal block stays authoritative: the release-gate hold ceiling. */\n terminalRetentionMsForPacket(packetId: number): number\n /** Keep the block open to `untilAtMs` for an admitted FETCH. */\n holdBlockForRepair(\n sbn: number,\n packetId: number,\n catalogTrackName: string | undefined,\n untilAtMs: number,\n ): boolean\n /** The admitted FETCH ended without delivering: back to the multicast wait. */\n releaseRepairHold(sbn: number, packetId: number, catalogTrackName?: string): void\n }\n onRepairSubscribe(cb: RepairSubscribeCallback): void\n addRepairLifecycleObserver(observer: RepairLifecycleObserver): () => void\n addTrackResetObserver(observer: (packetId: number, catalogTrackName: string) => void): () => void\n clearRepairForBlock(sbn: number, packetId: number, catalogTrackName?: string): void\n processRepairFromMoQ(packet: Uint8Array, ownerPacketId?: number, catalogTrackName?: string): void\n noteRepairScheduling(\n type: 'repair-preempted' | 'repair-deferred',\n sbn: number,\n packetId: number,\n catalogTrackName?: string,\n ): void\n /** A request whose wire priority the catalog could not supply. */\n noteRepairPriority(\n action: 'omitted' | 'declined',\n reason: RepairPriorityReason,\n blockClass: BlockClass,\n sbn: number,\n packetId: number,\n catalogTrackName?: string,\n ): void\n /** A request the scheduler did not send (see RepairSkipReason). */\n noteRepairSkipped(\n reason: RepairSkipReason,\n measurement: { readonly rttMs: number | null; readonly budgetMs: number },\n sbn: number,\n packetId: number,\n catalogTrackName?: string,\n ): void\n}\n\ntype StopReason = 'preempted' | 'reprioritized' | 'expired' | 'resolved' | 'reset' | 'disposed'\n\ninterface FetchRun {\n readonly controller: AbortController\n stopped: StopReason | null\n}\n\ninterface RepairEntry {\n readonly key: string\n readonly sbn: number\n readonly packetId: number\n readonly catalogTrackName?: string\n readonly repairTrack: string\n readonly target: RepairTarget\n /** The block profile `sbn` is keyed by, resolved before the entry is queued. */\n readonly blockProfile: FecBlockProfile\n priority: RepairPriority\n /** SUBSCRIBER_PRIORITY for this entry's class; undefined = omit it. */\n subscriberPriority: number | undefined\n /**\n * Absolute `performance.now()` millisecond by which a FETCH must land: the\n * block's presentation deadline (see the module note).\n */\n readonly deadlineAt: number\n /** Monotonic enqueue order, the EDF tie-break. */\n readonly order: number\n attempts: number\n running: FetchRun | null\n}\n\n/**\n * Wire the D-28 repair-fetch scheduler onto a MulticastManager.\n *\n * Call AFTER the manager has a repair-bearing FEC configuration (players gate\n * this on the first fec-setup that carries a repairTrack) \u2014 registration arms\n * the per-block repair deadlines in the FEC layer.\n *\n * Returns a dispose function that aborts outstanding fetches and stops new\n * ones from being issued.\n */\nexport function wireRepairFetch(mc: RepairFetchHost, opts: WireRepairFetchOptions): () => void {\n if (!Number.isSafeInteger(opts.attemptLimit) || opts.attemptLimit < 1) {\n throw new Error(\n `[RepairFetch] attemptLimit=${String(opts.attemptLimit)} must be a positive integer`,\n )\n }\n if (typeof opts.reliableRttMs !== 'function') {\n throw new Error('[RepairFetch] reliableRttMs is required: repair admission needs the measured reliable-path RTT')\n }\n if (typeof opts.presentationDelayMs !== 'function') {\n throw new Error('[RepairFetch] presentationDelayMs is required: a repair FETCH is due by the block\\'s presentation deadline')\n }\n const entries = new Map<string, RepairEntry>()\n const warnedNoTarget = new Set<string>()\n let disposed = false\n let enqueueOrder = 0\n\n const requestKey = (sbn: number, packetId: number, catalogTrackName?: string): string =>\n JSON.stringify([packetId, catalogTrackName ?? null, sbn])\n const noTargetKey = (packetId: number, catalogTrackName?: string): string =>\n JSON.stringify([packetId, catalogTrackName ?? null])\n\n const clearRepair = (sbn: number, packetId: number, catalogTrackName?: string): void => {\n if (catalogTrackName === undefined) mc.clearRepairForBlock(sbn, packetId)\n else mc.clearRepairForBlock(sbn, packetId, catalogTrackName)\n }\n const note = (type: 'repair-preempted' | 'repair-deferred', entry: RepairEntry): void => {\n mc.noteRepairScheduling(type, entry.sbn, entry.packetId, entry.catalogTrackName)\n }\n\n const releaseHold = (entry: RepairEntry): void => {\n if (entry.catalogTrackName === undefined) mc.fecManager.releaseRepairHold(entry.sbn, entry.packetId)\n else mc.fecManager.releaseRepairHold(entry.sbn, entry.packetId, entry.catalogTrackName)\n }\n\n const stop = (entry: RepairEntry, reason: StopReason): void => {\n const run = entry.running\n if (!run) return\n entry.running = null\n run.stopped = reason\n run.controller.abort()\n // A preempted delta returns to the queue; until it runs again nothing is\n // in flight that could land, so the block goes back to its multicast wait.\n if (reason === 'preempted') releaseHold(entry)\n }\n\n const readRtt = (): number | null => {\n try {\n const rtt = opts.reliableRttMs()\n return Number.isFinite(rtt) && rtt > 0 ? rtt : null\n } catch {\n return null\n }\n }\n\n /**\n * RTT admission: send only a FETCH the measured round trip says can land\n * before the entry's deadline. A refused entry is released, not queued: the\n * deadline only gets closer.\n */\n const admit = (entry: RepairEntry, now: number): boolean => {\n const budgetMs = entry.deadlineAt - now\n const rttMs = readRtt()\n if (rttMs !== null && rttMs < budgetMs) return true\n retire(entry)\n releaseHold(entry)\n clearRepair(entry.sbn, entry.packetId, entry.catalogTrackName)\n mc.noteRepairSkipped(\n rttMs === null ? 'rtt-unmeasured' : 'rtt-exceeds-budget',\n { rttMs, budgetMs },\n entry.sbn,\n entry.packetId,\n entry.catalogTrackName,\n )\n return false\n }\n\n const retire = (entry: RepairEntry): void => {\n if (entries.get(entry.key) === entry) entries.delete(entry.key)\n }\n\n const urgentOutstanding = (): boolean => {\n for (const entry of entries.values()) {\n if (entry.priority < 2) return true\n }\n return false\n }\n\n const preemptDeltas = (): void => {\n for (const entry of entries.values()) {\n if (entry.priority !== 2 || !entry.running) continue\n stop(entry, 'preempted')\n note('repair-preempted', entry)\n }\n }\n\n /**\n * The wire priority for `blockClass`, or null when no FETCH may be sent.\n * A priority the catalog cannot supply is reported once per request.\n */\n const wirePriority = (\n entry: Pick<RepairEntry, 'target' | 'sbn' | 'packetId' | 'catalogTrackName'>,\n blockClass: BlockClass,\n ): { subscriberPriority: number | undefined } | null => {\n const derived = repairFetchSubscriberPriority(blockClass, entry.target)\n if (derived.kind === 'carry') return { subscriberPriority: derived.subscriberPriority }\n mc.noteRepairPriority(\n derived.kind === 'omit' ? 'omitted' : 'declined',\n derived.reason,\n blockClass,\n entry.sbn,\n entry.packetId,\n entry.catalogTrackName,\n )\n return derived.kind === 'omit' ? { subscriberPriority: undefined } : null\n }\n\n /** `counted` is false for a reprioritisation restart, which is no retry. */\n const start = (entry: RepairEntry, counted = true): void => {\n if (!admit(entry, performance.now())) return\n if (counted) entry.attempts++\n const run: FetchRun = { controller: new AbortController(), stopped: null }\n entry.running = run\n // Hold the block open to the presentation deadline while this FETCH can\n // still land: the multicast span elapsing must not abort it.\n mc.fecManager.holdBlockForRepair(\n entry.sbn,\n entry.packetId,\n entry.catalogTrackName,\n entry.deadlineAt,\n )\n void execute(entry, run)\n }\n\n const dispatch = (): void => {\n if (disposed) return\n const now = performance.now()\n const queued: RepairEntry[] = []\n for (const entry of [...entries.values()]) {\n if (entry.running) {\n // A fetch still running at its block's fail bound can no longer land\n // in time. Cancel it here rather than trust the transport or the FEC\n // layer's resolution to end it: a hung P0/P1 left running would keep\n // urgentOutstanding() true and gate every P2 for the session.\n if (entry.deadlineAt <= now) {\n stop(entry, 'expired')\n retire(entry)\n clearRepair(entry.sbn, entry.packetId, entry.catalogTrackName)\n }\n continue\n }\n if (entry.deadlineAt <= now) {\n // The block's fail bound has passed; its resolution is imminent and\n // no fetch can land in time.\n retire(entry)\n if (entry.priority === 2 && entry.attempts === 0) note('repair-deferred', entry)\n clearRepair(entry.sbn, entry.packetId, entry.catalogTrackName)\n continue\n }\n queued.push(entry)\n }\n for (const entry of queued) {\n if (entry.priority < 2) start(entry)\n }\n if (urgentOutstanding()) return\n queued\n .filter((entry) => entry.priority === 2)\n .sort((a, b) => a.deadlineAt - b.deadlineAt || a.order - b.order)\n .forEach((entry) => start(entry))\n }\n\n const finish = (entry: RepairEntry, run: FetchRun, fed: number, error: unknown): void => {\n if (run.stopped !== null || entry.running !== run || entries.get(entry.key) !== entry) {\n return\n }\n entry.running = null\n if (error === undefined) {\n retire(entry)\n // A fetch that ended without feeding anything cannot resolve the block;\n // release the slot and the hold so failure accounting is not wedged. A\n // fetch that fed symbols leaves resolution to the FEC layer.\n if (fed === 0) {\n releaseHold(entry)\n clearRepair(entry.sbn, entry.packetId, entry.catalogTrackName)\n }\n dispatch()\n return\n }\n const retryable =\n fed === 0 &&\n entry.attempts < opts.attemptLimit &&\n entry.deadlineAt > performance.now() &&\n opts.shouldRetry?.(error) === true\n if (retryable) {\n // Back through the same admission rule: a P2 retry waits for P0/P1.\n dispatch()\n return\n }\n retire(entry)\n if (fed === 0) releaseHold(entry)\n clearRepair(entry.sbn, entry.packetId, entry.catalogTrackName)\n console.warn(\n `[RepairFetch] fetch failed pid=${entry.packetId} sbn=${entry.sbn} track=${entry.repairTrack}:`,\n error,\n )\n dispatch()\n }\n\n // The profile the block key is in; a stream reaching repair was configured\n // from its catalog, so an absent profile is a wiring fault, not a default.\n // Resolved when the request arrives (onRepairSubscribe below), never inside\n // execute: there its throw would read as an ordinary fetch failure, warned\n // once per block while the stream silently lost unicast repair.\n const blockProfileOf = (packetId: number): FecBlockProfile => {\n const profile = mc.fecManager.blockProfileForPacket(packetId)\n if (profile === undefined) {\n throw new Error(`[RepairFetch] packetId=${packetId} has no configured fec.blockProfile`)\n }\n return profile\n }\n\n const execute = async (entry: RepairEntry, run: FetchRun): Promise<void> => {\n let fed = 0\n try {\n for await (const packet of opts.transport({\n repairTrack: entry.repairTrack,\n sbn: entry.sbn,\n blockProfile: entry.blockProfile,\n packetId: entry.packetId,\n signal: run.controller.signal,\n subscriberPriority: entry.subscriberPriority,\n })) {\n if (run.stopped !== null || entry.running !== run) break\n fed++\n if (entry.catalogTrackName === undefined) {\n mc.processRepairFromMoQ(packet, entry.packetId)\n } else {\n mc.processRepairFromMoQ(packet, entry.packetId, entry.catalogTrackName)\n }\n }\n finish(entry, run, fed, undefined)\n } catch (error) {\n finish(entry, run, fed, error ?? new Error('repair transport rejected'))\n }\n }\n\n const removeResetObserver = mc.addTrackResetObserver((packetId, catalogTrackName) => {\n for (const entry of [...entries.values()]) {\n if (entry.packetId !== packetId || entry.catalogTrackName !== catalogTrackName) continue\n retire(entry)\n stop(entry, 'reset')\n }\n // A reset is when the packetId -> repair-track mapping can change, so\n // re-arm the once-per-track warn for the new epoch.\n warnedNoTarget.delete(noTargetKey(packetId, catalogTrackName))\n dispatch()\n })\n\n const removeLifecycleObserver = mc.addRepairLifecycleObserver((event) => {\n if (disposed) return\n if (event.type === 'reclassified') {\n const entry = entries.get(\n requestKey(event.block.sbn, event.block.packetId, event.block.catalogTrackName ?? undefined),\n )\n if (!entry || entry.priority !== 2) return\n const promoted = repairPriorityForClass(event.to)\n if (promoted >= entry.priority) return\n const wire = wirePriority(entry, event.to)\n // A block leaves P2 only for P0, whose request is never declined.\n if (wire === null) return\n entry.priority = promoted\n entry.subscriberPriority = wire.subscriberPriority\n if (entry.running) {\n // The running FETCH carries the delta priority. Cancel it and send\n // the block's repair again under its new class (\u00A711.4.5): a keyframe\n // repair must not stay queued behind source media.\n stop(entry, 'reprioritized')\n start(entry, false)\n }\n preemptDeltas()\n dispatch()\n return\n }\n if (event.type !== 'recovered' && event.type !== 'failed') return\n const entry = entries.get(\n requestKey(event.block.sbn, event.block.packetId, event.block.catalogTrackName ?? undefined),\n )\n if (!entry) return\n retire(entry)\n if (entry.running) stop(entry, 'resolved')\n else if (entry.priority === 2 && entry.attempts === 0) note('repair-deferred', entry)\n dispatch()\n })\n\n /**\n * The block's presentation deadline, or null when the player gives none.\n *\n * anchor + presentationDelay, clamped below by the multicast wait (the block\n * is open that long whatever happens, so a FETCH landing inside it still\n * counts) and above by anchor + the release gate's hold ceiling (a successor\n * frame may not wait behind a block longer than that). A block no symbol\n * reached has no anchor; its deadline is the multicast wait.\n */\n const presentationDeadlineAt = (\n sbn: number,\n packetId: number,\n catalogTrackName: string | undefined,\n now: number,\n ): number | null => {\n const waitDeadlineAt =\n now + mc.fecManager.repairFetchTimeoutMsForBlock(sbn, packetId, catalogTrackName)\n const anchor = mc.fecManager.blockAnchorAtMs(sbn, packetId, catalogTrackName)\n if (anchor === undefined) return waitDeadlineAt\n let delayMs: number\n try {\n delayMs = opts.presentationDelayMs(packetId, catalogTrackName)\n } catch {\n return null\n }\n if (!(Number.isFinite(delayMs) && delayMs > 0)) return null\n const ceilingAt = anchor + mc.fecManager.terminalRetentionMsForPacket(packetId)\n return Math.max(waitDeadlineAt, Math.min(anchor + delayMs, ceilingAt))\n }\n\n mc.onRepairSubscribe((sbn, packetId, catalogTrackName, blockClass) => {\n if (disposed) {\n clearRepair(sbn, packetId, catalogTrackName)\n return\n }\n // Throws on a wiring fault, here and synchronously, before anything is\n // queued (see blockProfileOf).\n const blockProfile = blockProfileOf(packetId)\n\n // Fire-time resolution \u2014 see ResolveRepairTarget.\n const target = opts.resolveTarget(packetId, catalogTrackName)\n if (!target) {\n // A starved block with no repair target can only resolve via the fail\n // bound. Say so, once per track: a null target is also the documented\n // answer for a rung that carries no repair, and the typed lifecycle\n // events already enumerate every block.\n const warnKey = noTargetKey(packetId, catalogTrackName)\n if (!warnedNoTarget.has(warnKey)) {\n warnedNoTarget.add(warnKey)\n console.warn(\n `[RepairFetch] no repair target for pid=${packetId} sbn=${sbn}` +\n `${catalogTrackName === undefined ? '' : ` track=${catalogTrackName}`}; releasing without a fetch`,\n )\n }\n clearRepair(sbn, packetId, catalogTrackName)\n return\n }\n\n const key = requestKey(sbn, packetId, catalogTrackName)\n const previous = entries.get(key)\n if (previous) {\n retire(previous)\n stop(previous, 'reset')\n }\n const identity = {\n target,\n sbn,\n packetId,\n ...(catalogTrackName === undefined ? {} : { catalogTrackName }),\n }\n const wire = wirePriority(identity, blockClass)\n if (wire === null) {\n // No conformant FETCH exists for this block (see RepairFetchPriority).\n clearRepair(sbn, packetId, catalogTrackName)\n return\n }\n const now = performance.now()\n const deadlineAt = presentationDeadlineAt(sbn, packetId, catalogTrackName, now)\n if (deadlineAt === null) {\n clearRepair(sbn, packetId, catalogTrackName)\n mc.noteRepairSkipped(\n 'presentation-delay-unavailable',\n { rttMs: readRtt(), budgetMs: 0 },\n sbn,\n packetId,\n catalogTrackName,\n )\n return\n }\n const entry: RepairEntry = {\n key,\n ...identity,\n repairTrack: target.repairTrack,\n blockProfile,\n priority: repairPriorityForClass(blockClass),\n subscriberPriority: wire.subscriberPriority,\n deadlineAt,\n order: enqueueOrder++,\n attempts: 0,\n running: null,\n }\n entries.set(key, entry)\n if (entry.priority < 2) preemptDeltas()\n dispatch()\n })\n\n return () => {\n disposed = true\n removeResetObserver()\n removeLifecycleObserver()\n for (const entry of [...entries.values()]) {\n clearRepair(entry.sbn, entry.packetId, entry.catalogTrackName)\n if (entry.running) releaseHold(entry)\n stop(entry, 'disposed')\n }\n entries.clear()\n }\n}\n"],
5
+ "mappings": "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",
6
+ "names": ["MPU_HEADER_SIZE", "MPU_LENGTH_FIELD_SIZE", "MPU_HEADER_REMAINDER_SIZE", "MPU_HEADER_SIZE", "entry"]
7
+ }
@@ -0,0 +1,158 @@
1
+ import { type FecBlockProfile } from "@blockcast/mmt-base";
2
+ /** Lowest MoQT-scale priority a repair track may declare. */
3
+ export declare const REPAIR_PRIORITY_MIN = 192;
4
+ /** Highest MoQT-scale priority a repair track may declare. */
5
+ export declare const REPAIR_PRIORITY_MAX = 255;
6
+ /** Priority of a base layer 0 track that declares none. */
7
+ export declare const DEFAULT_REPAIR_PRIORITY = 240;
8
+ /** RFC 6330 §4.4.1.2: the largest number of source symbols in one RaptorQ source block. */
9
+ export declare const MAX_RAPTORQ_SOURCE_SYMBOLS = 56403;
10
+ /** The 24-bit RS_ID field (§7.1) bounds the repair total of one FEC instance. */
11
+ export declare const MAX_REPAIR_SYMBOLS = 16777215;
12
+ /** The 24-bit ESI bounds K + totalP - 1 for one FEC instance. */
13
+ export declare const MAX_ENCODING_SYMBOL_ID = 16777215;
14
+ export type RepairContractCode = "track_fec" | "track_repair_track" | "track_repair_depends" | "track_repair_layer" | "track_repair_priority" | "track_repair_overlay" | "track_repair_geometry" | "track_fec_bounds" | "track_fec_block_profile";
15
+ /** Reports one contract violation and never returns. */
16
+ export type RepairContractFail = (code: RepairContractCode, path: string, message: string) => never;
17
+ export declare class RepairContractError extends Error {
18
+ readonly code: RepairContractCode;
19
+ readonly path: string;
20
+ constructor(code: RepairContractCode, path: string, message: string);
21
+ }
22
+ export declare const throwRepairContractError: RepairContractFail;
23
+ /**
24
+ * Read one catalog `fec.blockProfile` (BLO-35597, ISO/IEC 23008-1 Annex C):
25
+ * the one rule both catalog dialects, the CMSF validator and the per-track
26
+ * resolver apply.
27
+ *
28
+ * A `fec` block that declares an FEC instance (any `algorithm` other than
29
+ * `"none"`) MUST name its block profile, so an absent or unknown token fails
30
+ * with `track_fec_block_profile` at `path`. A block declaring no FEC instance
31
+ * may omit it (returns undefined), but a token it does carry must be known.
32
+ * The token is never defaulted and never inferred: `k-padded-v1` and
33
+ * `iso-ssbg1-v1` carry identical wire tuples whose repair math differs.
34
+ *
35
+ * @param path JSON path of the `blockProfile` field, for the error.
36
+ */
37
+ export declare function readFecBlockProfile(value: unknown, path: string, declaresFecInstance: true, fail: RepairContractFail): FecBlockProfile;
38
+ export declare function readFecBlockProfile(value: unknown, path: string, declaresFecInstance: boolean, fail: RepairContractFail): FecBlockProfile | undefined;
39
+ /** The self-describing fields of one repair track entry, in either dialect. */
40
+ export type RepairTrackDescriptor = {
41
+ /** Track (MSF) or rendition (hang) name. */
42
+ name: string;
43
+ /** JSON path of the entry, for error reporting. */
44
+ path: string;
45
+ depends?: unknown;
46
+ repairLayer?: unknown;
47
+ repairSymbols?: unknown;
48
+ priority?: unknown;
49
+ scope?: unknown;
50
+ sourceSymbols?: unknown;
51
+ };
52
+ /** The source track's `fec` object as the layer contract reads it. */
53
+ export type RepairSourceGeometry = {
54
+ /** Source track or rendition name. */
55
+ name: string;
56
+ /** JSON path of the `fec` object, for error reporting. */
57
+ path: string;
58
+ algorithm?: unknown;
59
+ /** K. */
60
+ sourceSymbols?: unknown;
61
+ /** P_0. */
62
+ repairSymbols?: unknown;
63
+ /** Layer 0 track name. */
64
+ repairTrack?: unknown;
65
+ };
66
+ /** A resolved base repair layer with its derived RS_ID range. */
67
+ export type RepairLayerParams = {
68
+ /** Layer index; 0 is the track named by the source's `fec.repairTrack`. */
69
+ layer: number;
70
+ repairTrackName: string;
71
+ /** P_i: repair symbols per block carried by this layer. */
72
+ repairSymbols: number;
73
+ /** O_i = sum(P_j for j < i): the first RS_ID this layer carries. ESI = K + RS_ID. */
74
+ rsIdOffset: number;
75
+ /** MoQT-scale priority (lower first); 240 when layer 0 declares none. */
76
+ priority: number;
77
+ };
78
+ /** A resolved keyframe overlay: a separate FEC instance over RAP fragments only. */
79
+ export type RepairOverlayParams = {
80
+ repairTrackName: string;
81
+ scope: "keyframe";
82
+ /** K_overlay: source symbols of the overlay instance. */
83
+ sourceSymbols: number;
84
+ /** P_overlay: repair symbols per overlay block; also the overlay's own RSB_length. */
85
+ repairSymbols: number;
86
+ /** MoQT-scale priority, independent of the base layer chain. */
87
+ priority: number;
88
+ };
89
+ export type RepairLayersResolution = {
90
+ sourceTrackName: string;
91
+ /** Base layers in layer order, layer 0 first. Never empty. */
92
+ baseLayers: readonly RepairLayerParams[];
93
+ /** Keyframe overlay instances, in catalog order. Empty when none is declared. */
94
+ /** At most one entry: the wire carries one overlay trailer per RAP packet. */
95
+ overlays: readonly RepairOverlayParams[];
96
+ /** Sum of P over base layers: the wire RSB_length on every base layer packet. */
97
+ totalBaseRepairSymbols: number;
98
+ };
99
+ /** Track-level block geometry keys a repair track must not carry (§6.3 rule 8): geometry is the source's. */
100
+ export declare const REPAIR_TRACK_GEOMETRY_KEYS: readonly ["mmtpMode", "timescale", "groupDurationMs", "groupDurationTicks", "keyframeIntervalMs", "keyframeIntervalTicks", "fec", "symbolSize", "interleaveDepthMs"];
101
+ /** Self-describing repair fields that only a repair track or repair rendition may carry. */
102
+ export declare const REPAIR_TRACK_ONLY_KEYS: readonly ["repairLayer", "repairSymbols", "sourceSymbols", "scope"];
103
+ /** Rule 8: a repair track (MSF) or repair rendition (hang) carries no block geometry. */
104
+ export declare function rejectRepairTrackGeometry(path: string, fields: Record<string, unknown>, fail: RepairContractFail): void;
105
+ /** Which repair tracks a catalog can reach, for rule 9 (no orphan repair track). */
106
+ export type RepairReachability = {
107
+ /** True once any track or rendition carries `fec` with an algorithm other than `none`. */
108
+ declaresFecSource: boolean;
109
+ /** Every non-empty `fec.repairTrack` a FEC source names: the layer-0 tracks. */
110
+ layerZeroNames: ReadonlySet<string>;
111
+ };
112
+ /**
113
+ * Derive rule 9's inputs from the raw `fec` objects of every media track (MSF,
114
+ * packaging other than `fec-repair`) or media rendition (hang) in a catalog.
115
+ * Callers must not pass repair tracks: rule 8 forbids `fec` on them, and a
116
+ * repair track that carried one would otherwise name itself as its own
117
+ * layer 0 and slip past rule 9. One derivation for the validator and both
118
+ * resolver dialects, so an empty-string pointer or an `algorithm: "none"`
119
+ * source is read the same way everywhere.
120
+ */
121
+ export declare function repairReachability(fecObjects: Iterable<unknown>): RepairReachability;
122
+ /**
123
+ * Rule 9: once a catalog declares a FEC source, a repair track that declares no
124
+ * `depends` must be some source's layer 0. A catalog with no FEC source may
125
+ * still carry a naming-era repair track that nothing joins.
126
+ */
127
+ export declare function assertRepairTrackReachable(descriptor: RepairTrackDescriptor, reachability: RepairReachability, fail: RepairContractFail): void;
128
+ /** A media track (MSF) or media rendition (hang) carries no repair-track fields. */
129
+ export declare function rejectRepairFieldsOnMediaTrack(path: string, fields: Record<string, unknown>, fail: RepairContractFail): void;
130
+ /** Pick the self-describing repair fields out of a raw track or rendition record. */
131
+ export declare function describeRepairTrack(name: string, path: string, fields: Record<string, unknown>): RepairTrackDescriptor;
132
+ /** The single source a repair track names in `depends`, once its shape has been validated. */
133
+ export declare function dependsTarget(descriptor: RepairTrackDescriptor): string | undefined;
134
+ /**
135
+ * Validate the shape of one repair track's self-describing fields, independent
136
+ * of any source. Cross-track rules (layer 0 agreement, contiguity, priority
137
+ * order, bounds) live in `assembleRepairLayers`.
138
+ */
139
+ export declare function validateRepairTrackFields(descriptor: RepairTrackDescriptor, fail: RepairContractFail): void;
140
+ /**
141
+ * Resolve the base layers and overlays of one source track and derive each
142
+ * base layer's RS_ID offset. Fails closed on every §6.3 rule violation through
143
+ * `fail`, which receives the code and the JSON path of the offending field.
144
+ *
145
+ * `descriptors` are every repair track (or repair rendition) in the catalog,
146
+ * already shape-checked by `validateRepairTrackFields`. Returns undefined when
147
+ * the source names no layer 0 and nothing depends on it.
148
+ */
149
+ export type AssembleRepairLayersOptions = {
150
+ /**
151
+ * MSF: `fec.repairTrack` must name one of `descriptors` (a `fec-repair`
152
+ * track), so a dangling pointer fails closed with `track_repair_track`.
153
+ * hang: layer 0 lives outside `repair.renditions`, so leave this unset.
154
+ */
155
+ layerZeroMustBeDeclared?: boolean;
156
+ };
157
+ export declare function assembleRepairLayers(source: RepairSourceGeometry, descriptors: readonly RepairTrackDescriptor[], fail?: RepairContractFail, options?: AssembleRepairLayersOptions): RepairLayersResolution | undefined;
158
+ //# sourceMappingURL=repair-layers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"repair-layers.d.ts","sourceRoot":"","sources":["../src/repair-layers.ts"],"names":[],"mappings":"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"}