@blockcast/mmt-container 0.3.0 → 0.4.0-main.d6f2737828fc
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +22 -8
- package/dist/browser-runtime.js +1185 -1049
- package/dist/browser-runtime.js.map +4 -4
- package/dist/carp-catalog.d.ts +24 -8
- package/dist/carp-catalog.d.ts.map +1 -1
- package/dist/carp-catalog.js +21 -8
- package/dist/carp-catalog.js.map +2 -2
- package/dist/catalog-fec-params.d.ts +88 -28
- package/dist/catalog-fec-params.d.ts.map +1 -1
- package/dist/catalog-fec-params.js +59 -17
- package/dist/catalog-fec-params.js.map +3 -3
- package/dist/catalog-network-source.js +0 -1
- package/dist/catalog-network-source.js.map +2 -2
- package/dist/cmaf-block-assembler.d.ts +0 -2
- package/dist/cmaf-block-assembler.d.ts.map +1 -1
- package/dist/cmaf-block-assembler.js +0 -1
- package/dist/cmaf-block-assembler.js.map +2 -2
- package/dist/delta-fallback-config.d.ts +42 -0
- package/dist/delta-fallback-config.d.ts.map +1 -0
- package/dist/delta-fallback-config.js +59 -0
- package/dist/delta-fallback-config.js.map +7 -0
- package/dist/fec-manager.d.ts +35 -26
- package/dist/fec-manager.d.ts.map +1 -1
- package/dist/fec-manager.js +300 -273
- package/dist/fec-manager.js.map +2 -2
- package/dist/fec-state.d.ts +19 -31
- package/dist/fec-state.d.ts.map +1 -1
- package/dist/fec-state.js +3 -4
- package/dist/fec-state.js.map +2 -2
- package/dist/fec-worker-setup.d.ts +16 -6
- package/dist/fec-worker-setup.d.ts.map +1 -1
- package/dist/fec-worker-setup.js +72 -34
- package/dist/fec-worker-setup.js.map +2 -2
- package/dist/index.d.ts +4 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +12 -0
- package/dist/index.js.map +2 -2
- package/dist/iso-block-locator.js +0 -1
- package/dist/iso-block-locator.js.map +2 -2
- package/dist/media-router.d.ts +20 -8
- package/dist/media-router.d.ts.map +1 -1
- package/dist/media-router.js +46 -21
- package/dist/media-router.js.map +2 -2
- package/dist/mfu-reassembler.js +0 -1
- package/dist/mfu-reassembler.js.map +2 -2
- package/dist/mmtp-container-parser.d.ts +5 -0
- package/dist/mmtp-container-parser.d.ts.map +1 -1
- package/dist/mmtp-container-parser.js +22 -1
- package/dist/mmtp-container-parser.js.map +2 -2
- package/dist/mmtp-container-parser.no-modules.js +37 -9
- package/dist/mmtp-container-parser.no-modules.js.map +2 -2
- package/dist/mmtp-decode-pipeline.js +22 -1
- package/dist/mmtp-decode-pipeline.js.map +2 -2
- package/dist/mmtp-router.d.ts +36 -16
- package/dist/mmtp-router.d.ts.map +1 -1
- package/dist/mmtp-router.js +126 -213
- package/dist/mmtp-router.js.map +2 -2
- package/dist/multicast-input.js +0 -1
- package/dist/multicast-input.js.map +2 -2
- package/dist/multicast-manager.d.ts +33 -3
- package/dist/multicast-manager.d.ts.map +1 -1
- package/dist/multicast-manager.js +660 -529
- package/dist/multicast-manager.js.map +4 -4
- package/dist/multicast-transport.d.ts +1 -1
- package/dist/multicast-transport.d.ts.map +1 -1
- package/dist/multicast-transport.js +42 -14
- package/dist/multicast-transport.js.map +3 -3
- package/dist/multicast-unwrap.js +0 -1
- package/dist/multicast-unwrap.js.map +2 -2
- package/dist/no-modules.js +36 -8
- package/dist/no-modules.js.map +2 -2
- package/dist/repair-fetch.d.ts +30 -19
- package/dist/repair-fetch.d.ts.map +1 -1
- package/dist/repair-fetch.js +32 -18
- package/dist/repair-fetch.js.map +3 -3
- package/dist/repair-layers.d.ts +18 -7
- package/dist/repair-layers.d.ts.map +1 -1
- package/dist/repair-layers.js +11 -4
- package/dist/repair-layers.js.map +2 -2
- package/dist/signaling.js +0 -4
- package/dist/signaling.js.map +2 -2
- package/dist/timestamp-codec.js +0 -1
- package/dist/timestamp-codec.js.map +2 -2
- package/dist/track-session.d.ts +7 -0
- package/dist/track-session.d.ts.map +1 -1
- package/dist/track-session.js +15 -8
- package/dist/track-session.js.map +2 -2
- package/dist/wasm-mfu-reassembler.js +0 -1
- package/dist/wasm-mfu-reassembler.js.map +2 -2
- package/dist/wasm-mmtp-container-parser.js +22 -1
- package/dist/wasm-mmtp-container-parser.js.map +2 -2
- package/package.json +8 -14
- package/schemas/catalog.schema.json +3 -3
- package/src/carp-catalog.ts +35 -13
- package/src/catalog-fec-params.ts +173 -64
- package/src/cmaf-block-assembler.ts +1 -4
- package/src/delta-fallback-config.ts +72 -0
- package/src/fec-manager.ts +413 -317
- package/src/fec-state.ts +20 -32
- package/src/fec-worker-setup.ts +105 -55
- package/src/index.ts +10 -0
- package/src/media-router.ts +59 -21
- package/src/mmtp-container-parser.ts +26 -0
- package/src/mmtp-router.ts +145 -303
- package/src/multicast-manager.ts +172 -60
- package/src/multicast-transport.ts +16 -9
- package/src/repair-fetch.ts +46 -27
- package/src/repair-layers.ts +28 -8
- package/src/track-session.ts +28 -9
- package/test-vectors/catalog/README.md +34 -17
- package/test-vectors/catalog/negative/fec-block-profile-retired.json +52 -0
- package/test-vectors/catalog/negative/fec-enhancement-repair-removed.json +1 -1
- package/test-vectors/catalog/negative/fec-repair-layer-geometry.json +1 -1
- package/test-vectors/catalog/negative/fec-repair-layer-missing-symbols.json +1 -1
- package/test-vectors/catalog/negative/fec-repair-multiple-depends.json +1 -1
- package/test-vectors/catalog/negative/fec-repair-overlay-with-layer.json +1 -1
- package/test-vectors/catalog/negative/fec-repair-priority-out-of-band.json +1 -1
- package/test-vectors/catalog/negative/fec-repair-source-symbols-without-scope.json +1 -1
- package/test-vectors/catalog/negative/multicast-network-source-empty.json +1 -1
- package/test-vectors/catalog/negative/raptorq-source-symbols-over-max.json +1 -1
- package/test-vectors/catalog/negative/raptorq-unaligned-symbol.json +1 -1
- package/test-vectors/catalog/positive/fec-layered-repair.json +2 -2
- package/test-vectors/catalog/positive/fec-multicast.json +2 -1
- package/test-vectors/catalog/positive/multicast-native-ssm-no-network-source.json +2 -1
- package/test-vectors/catalog-timing.json +356 -0
- package/test-vectors/catalog-validation.json +40 -5
- package/node_modules/@blockcast/fec-worker/README.md +0 -223
- package/node_modules/@blockcast/fec-worker/dist/.build-stamp +0 -0
- package/node_modules/@blockcast/fec-worker/dist/block-order.d.ts +0 -38
- package/node_modules/@blockcast/fec-worker/dist/block-order.d.ts.map +0 -1
- package/node_modules/@blockcast/fec-worker/dist/block-order.js +0 -78
- package/node_modules/@blockcast/fec-worker/dist/block-order.js.map +0 -1
- package/node_modules/@blockcast/fec-worker/dist/fec-worker-client.d.ts +0 -93
- package/node_modules/@blockcast/fec-worker/dist/fec-worker-client.d.ts.map +0 -1
- package/node_modules/@blockcast/fec-worker/dist/fec-worker-client.js +0 -346
- package/node_modules/@blockcast/fec-worker/dist/fec-worker-client.js.map +0 -1
- package/node_modules/@blockcast/fec-worker/dist/fec-worker-types.d.ts +0 -380
- package/node_modules/@blockcast/fec-worker/dist/fec-worker-types.d.ts.map +0 -1
- package/node_modules/@blockcast/fec-worker/dist/fec-worker-types.js +0 -11
- package/node_modules/@blockcast/fec-worker/dist/fec-worker-types.js.map +0 -1
- package/node_modules/@blockcast/fec-worker/dist/fec-worker.d.ts +0 -14
- package/node_modules/@blockcast/fec-worker/dist/fec-worker.d.ts.map +0 -1
- package/node_modules/@blockcast/fec-worker/dist/fec-worker.js +0 -859
- package/node_modules/@blockcast/fec-worker/dist/fec-worker.js.map +0 -7
- package/node_modules/@blockcast/fec-worker/dist/index.d.ts +0 -10
- package/node_modules/@blockcast/fec-worker/dist/index.d.ts.map +0 -1
- package/node_modules/@blockcast/fec-worker/dist/index.js +0 -8
- package/node_modules/@blockcast/fec-worker/dist/index.js.map +0 -1
- package/node_modules/@blockcast/fec-worker/dist/shred-fec-worker.d.ts +0 -20
- package/node_modules/@blockcast/fec-worker/dist/shred-fec-worker.d.ts.map +0 -1
- package/node_modules/@blockcast/fec-worker/dist/shred-fec-worker.js +0 -370
- package/node_modules/@blockcast/fec-worker/dist/shred-fec-worker.js.map +0 -7
- package/node_modules/@blockcast/fec-worker/dist/shred-worker-client.d.ts +0 -104
- package/node_modules/@blockcast/fec-worker/dist/shred-worker-client.d.ts.map +0 -1
- package/node_modules/@blockcast/fec-worker/dist/shred-worker-client.js +0 -181
- package/node_modules/@blockcast/fec-worker/dist/shred-worker-client.js.map +0 -1
- package/node_modules/@blockcast/fec-worker/dist/shred-worker-types.d.ts +0 -194
- package/node_modules/@blockcast/fec-worker/dist/shred-worker-types.d.ts.map +0 -1
- package/node_modules/@blockcast/fec-worker/dist/shred-worker-types.js +0 -62
- package/node_modules/@blockcast/fec-worker/dist/shred-worker-types.js.map +0 -1
- package/node_modules/@blockcast/fec-worker/dist/transfer.d.ts +0 -9
- package/node_modules/@blockcast/fec-worker/dist/transfer.d.ts.map +0 -1
- package/node_modules/@blockcast/fec-worker/dist/transfer.js +0 -13
- package/node_modules/@blockcast/fec-worker/dist/transfer.js.map +0 -1
- package/node_modules/@blockcast/fec-worker/package.json +0 -59
- package/node_modules/@blockcast/fec-worker/src/block-order.ts +0 -93
- package/node_modules/@blockcast/fec-worker/src/fec-worker-client.ts +0 -392
- package/node_modules/@blockcast/fec-worker/src/fec-worker-types.ts +0 -399
- package/node_modules/@blockcast/fec-worker/src/fec-worker.ts +0 -1502
- package/node_modules/@blockcast/fec-worker/src/index.ts +0 -35
- package/node_modules/@blockcast/fec-worker/src/shred-fec-worker.ts +0 -589
- package/node_modules/@blockcast/fec-worker/src/shred-worker-client.ts +0 -222
- package/node_modules/@blockcast/fec-worker/src/shred-worker-types.ts +0 -209
- package/node_modules/@blockcast/fec-worker/src/transfer.ts +0 -12
- package/node_modules/@blockcast/mmt-alta-parse/README.md +0 -181
- package/node_modules/@blockcast/mmt-alta-parse/dist/.build-stamp +0 -0
- package/node_modules/@blockcast/mmt-alta-parse/dist/index.d.ts +0 -57
- package/node_modules/@blockcast/mmt-alta-parse/dist/index.d.ts.map +0 -1
- package/node_modules/@blockcast/mmt-alta-parse/dist/index.js +0 -115
- package/node_modules/@blockcast/mmt-alta-parse/dist/index.js.map +0 -1
- package/node_modules/@blockcast/mmt-alta-parse/package.json +0 -50
- package/node_modules/@blockcast/mmt-alta-parse/src/index.ts +0 -136
- package/node_modules/@blockcast/mmt-base/README.md +0 -134
- package/node_modules/@blockcast/mmt-base/dist/.build-stamp +0 -0
- package/node_modules/@blockcast/mmt-base/dist/fec-block-profile.d.ts +0 -55
- package/node_modules/@blockcast/mmt-base/dist/fec-block-profile.d.ts.map +0 -1
- package/node_modules/@blockcast/mmt-base/dist/fec-block-profile.js +0 -128
- package/node_modules/@blockcast/mmt-base/dist/fec-block-profile.js.map +0 -1
- package/node_modules/@blockcast/mmt-base/dist/index.d.ts +0 -7
- package/node_modules/@blockcast/mmt-base/dist/index.d.ts.map +0 -1
- package/node_modules/@blockcast/mmt-base/dist/index.js +0 -7
- package/node_modules/@blockcast/mmt-base/dist/index.js.map +0 -1
- package/node_modules/@blockcast/mmt-base/dist/mmtp.d.ts +0 -292
- package/node_modules/@blockcast/mmt-base/dist/mmtp.d.ts.map +0 -1
- package/node_modules/@blockcast/mmt-base/dist/mmtp.js +0 -612
- package/node_modules/@blockcast/mmt-base/dist/mmtp.js.map +0 -1
- package/node_modules/@blockcast/mmt-base/dist/net-constants.d.ts +0 -18
- package/node_modules/@blockcast/mmt-base/dist/net-constants.d.ts.map +0 -1
- package/node_modules/@blockcast/mmt-base/dist/net-constants.js +0 -18
- package/node_modules/@blockcast/mmt-base/dist/net-constants.js.map +0 -1
- package/node_modules/@blockcast/mmt-base/dist/ntp-short.d.ts +0 -47
- package/node_modules/@blockcast/mmt-base/dist/ntp-short.d.ts.map +0 -1
- package/node_modules/@blockcast/mmt-base/dist/ntp-short.js +0 -54
- package/node_modules/@blockcast/mmt-base/dist/ntp-short.js.map +0 -1
- package/node_modules/@blockcast/mmt-base/dist/serial-number.d.ts +0 -57
- package/node_modules/@blockcast/mmt-base/dist/serial-number.d.ts.map +0 -1
- package/node_modules/@blockcast/mmt-base/dist/serial-number.js +0 -98
- package/node_modules/@blockcast/mmt-base/dist/serial-number.js.map +0 -1
- package/node_modules/@blockcast/mmt-base/dist/signalling.d.ts +0 -312
- package/node_modules/@blockcast/mmt-base/dist/signalling.d.ts.map +0 -1
- package/node_modules/@blockcast/mmt-base/dist/signalling.js +0 -637
- package/node_modules/@blockcast/mmt-base/dist/signalling.js.map +0 -1
- package/node_modules/@blockcast/mmt-base/package.json +0 -49
- package/node_modules/@blockcast/mmt-base/src/fec-block-profile.ts +0 -145
- package/node_modules/@blockcast/mmt-base/src/index.ts +0 -6
- package/node_modules/@blockcast/mmt-base/src/mmtp.ts +0 -892
- package/node_modules/@blockcast/mmt-base/src/net-constants.ts +0 -17
- package/node_modules/@blockcast/mmt-base/src/ntp-short.ts +0 -67
- package/node_modules/@blockcast/mmt-base/src/serial-number.ts +0 -107
- package/node_modules/@blockcast/mmt-base/src/signalling.ts +0 -862
- package/node_modules/@blockcast/mmt-fec/README.md +0 -271
- package/node_modules/@blockcast/mmt-fec/dist/.build-stamp +0 -0
- package/node_modules/@blockcast/mmt-fec/dist/block-manager.d.ts +0 -82
- package/node_modules/@blockcast/mmt-fec/dist/block-manager.d.ts.map +0 -1
- package/node_modules/@blockcast/mmt-fec/dist/block-manager.js +0 -296
- package/node_modules/@blockcast/mmt-fec/dist/block-manager.js.map +0 -1
- package/node_modules/@blockcast/mmt-fec/dist/block-processor.d.ts +0 -106
- package/node_modules/@blockcast/mmt-fec/dist/block-processor.d.ts.map +0 -1
- package/node_modules/@blockcast/mmt-fec/dist/block-processor.js +0 -288
- package/node_modules/@blockcast/mmt-fec/dist/block-processor.js.map +0 -1
- package/node_modules/@blockcast/mmt-fec/dist/cmaf-recovery.d.ts +0 -46
- package/node_modules/@blockcast/mmt-fec/dist/cmaf-recovery.d.ts.map +0 -1
- package/node_modules/@blockcast/mmt-fec/dist/cmaf-recovery.js +0 -96
- package/node_modules/@blockcast/mmt-fec/dist/cmaf-recovery.js.map +0 -1
- package/node_modules/@blockcast/mmt-fec/dist/cmaf-symbols.d.ts +0 -33
- package/node_modules/@blockcast/mmt-fec/dist/cmaf-symbols.d.ts.map +0 -1
- package/node_modules/@blockcast/mmt-fec/dist/cmaf-symbols.js +0 -57
- package/node_modules/@blockcast/mmt-fec/dist/cmaf-symbols.js.map +0 -1
- package/node_modules/@blockcast/mmt-fec/dist/fec-block-mapper.d.ts +0 -205
- package/node_modules/@blockcast/mmt-fec/dist/fec-block-mapper.d.ts.map +0 -1
- package/node_modules/@blockcast/mmt-fec/dist/fec-block-mapper.js +0 -240
- package/node_modules/@blockcast/mmt-fec/dist/fec-block-mapper.js.map +0 -1
- package/node_modules/@blockcast/mmt-fec/dist/index.d.ts +0 -28
- package/node_modules/@blockcast/mmt-fec/dist/index.d.ts.map +0 -1
- package/node_modules/@blockcast/mmt-fec/dist/index.js +0 -28
- package/node_modules/@blockcast/mmt-fec/dist/index.js.map +0 -1
- package/node_modules/@blockcast/mmt-fec/dist/loc-symbols.d.ts +0 -20
- package/node_modules/@blockcast/mmt-fec/dist/loc-symbols.d.ts.map +0 -1
- package/node_modules/@blockcast/mmt-fec/dist/loc-symbols.js +0 -28
- package/node_modules/@blockcast/mmt-fec/dist/loc-symbols.js.map +0 -1
- package/node_modules/@blockcast/mmt-fec/dist/oti.d.ts +0 -258
- package/node_modules/@blockcast/mmt-fec/dist/oti.d.ts.map +0 -1
- package/node_modules/@blockcast/mmt-fec/dist/oti.js +0 -382
- package/node_modules/@blockcast/mmt-fec/dist/oti.js.map +0 -1
- package/node_modules/@blockcast/mmt-fec/dist/reorder-buffer.d.ts +0 -41
- package/node_modules/@blockcast/mmt-fec/dist/reorder-buffer.d.ts.map +0 -1
- package/node_modules/@blockcast/mmt-fec/dist/reorder-buffer.js +0 -81
- package/node_modules/@blockcast/mmt-fec/dist/reorder-buffer.js.map +0 -1
- package/node_modules/@blockcast/mmt-fec/dist/repair-group.d.ts +0 -82
- package/node_modules/@blockcast/mmt-fec/dist/repair-group.d.ts.map +0 -1
- package/node_modules/@blockcast/mmt-fec/dist/repair-group.js +0 -170
- package/node_modules/@blockcast/mmt-fec/dist/repair-group.js.map +0 -1
- package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-alta-parse/README.md +0 -181
- package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-alta-parse/dist/.build-stamp +0 -0
- package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-alta-parse/dist/index.d.ts +0 -57
- package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-alta-parse/dist/index.d.ts.map +0 -1
- package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-alta-parse/dist/index.js +0 -115
- package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-alta-parse/dist/index.js.map +0 -1
- package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-alta-parse/package.json +0 -50
- package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-alta-parse/src/index.ts +0 -136
- package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/README.md +0 -134
- package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/.build-stamp +0 -0
- package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/fec-block-profile.d.ts +0 -55
- package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/fec-block-profile.d.ts.map +0 -1
- package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/fec-block-profile.js +0 -128
- package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/fec-block-profile.js.map +0 -1
- package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/index.d.ts +0 -7
- package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/index.d.ts.map +0 -1
- package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/index.js +0 -7
- package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/index.js.map +0 -1
- package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/mmtp.d.ts +0 -292
- package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/mmtp.d.ts.map +0 -1
- package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/mmtp.js +0 -612
- package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/mmtp.js.map +0 -1
- package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/net-constants.d.ts +0 -18
- package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/net-constants.d.ts.map +0 -1
- package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/net-constants.js +0 -18
- package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/net-constants.js.map +0 -1
- package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/ntp-short.d.ts +0 -47
- package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/ntp-short.d.ts.map +0 -1
- package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/ntp-short.js +0 -54
- package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/ntp-short.js.map +0 -1
- package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/serial-number.d.ts +0 -57
- package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/serial-number.d.ts.map +0 -1
- package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/serial-number.js +0 -98
- package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/serial-number.js.map +0 -1
- package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/signalling.d.ts +0 -312
- package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/signalling.d.ts.map +0 -1
- package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/signalling.js +0 -637
- package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/signalling.js.map +0 -1
- package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/package.json +0 -49
- package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/src/fec-block-profile.ts +0 -145
- package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/src/index.ts +0 -6
- package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/src/mmtp.ts +0 -892
- package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/src/net-constants.ts +0 -17
- package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/src/ntp-short.ts +0 -67
- package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/src/serial-number.ts +0 -107
- package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/src/signalling.ts +0 -862
- package/node_modules/@blockcast/mmt-fec/package.json +0 -79
- package/node_modules/@blockcast/mmt-fec/src/block-manager.ts +0 -371
- package/node_modules/@blockcast/mmt-fec/src/block-processor.ts +0 -439
- package/node_modules/@blockcast/mmt-fec/src/cmaf-recovery.ts +0 -127
- package/node_modules/@blockcast/mmt-fec/src/cmaf-symbols.ts +0 -68
- package/node_modules/@blockcast/mmt-fec/src/fec-block-mapper.ts +0 -310
- package/node_modules/@blockcast/mmt-fec/src/index.ts +0 -94
- package/node_modules/@blockcast/mmt-fec/src/loc-symbols.ts +0 -34
- package/node_modules/@blockcast/mmt-fec/src/oti.ts +0 -463
- package/node_modules/@blockcast/mmt-fec/src/reorder-buffer.ts +0 -101
- package/node_modules/@blockcast/mmt-fec/src/repair-group.ts +0 -195
- package/node_modules/@blockcast/mmt-manifest-verify/dist/.build-stamp +0 -0
- package/node_modules/@blockcast/mmt-manifest-verify/dist/admission.d.ts +0 -28
- package/node_modules/@blockcast/mmt-manifest-verify/dist/admission.d.ts.map +0 -1
- package/node_modules/@blockcast/mmt-manifest-verify/dist/admission.js +0 -128
- package/node_modules/@blockcast/mmt-manifest-verify/dist/admission.js.map +0 -1
- package/node_modules/@blockcast/mmt-manifest-verify/dist/index.d.ts +0 -4
- package/node_modules/@blockcast/mmt-manifest-verify/dist/index.d.ts.map +0 -1
- package/node_modules/@blockcast/mmt-manifest-verify/dist/index.js +0 -4
- package/node_modules/@blockcast/mmt-manifest-verify/dist/index.js.map +0 -1
- package/node_modules/@blockcast/mmt-manifest-verify/dist/source-auth.d.ts +0 -37
- package/node_modules/@blockcast/mmt-manifest-verify/dist/source-auth.d.ts.map +0 -1
- package/node_modules/@blockcast/mmt-manifest-verify/dist/source-auth.js +0 -179
- package/node_modules/@blockcast/mmt-manifest-verify/dist/source-auth.js.map +0 -1
- package/node_modules/@blockcast/mmt-manifest-verify/dist/verifier.d.ts +0 -99
- package/node_modules/@blockcast/mmt-manifest-verify/dist/verifier.d.ts.map +0 -1
- package/node_modules/@blockcast/mmt-manifest-verify/dist/verifier.js +0 -492
- package/node_modules/@blockcast/mmt-manifest-verify/dist/verifier.js.map +0 -1
- package/node_modules/@blockcast/mmt-manifest-verify/package.json +0 -45
- package/node_modules/@blockcast/mmt-manifest-verify/src/admission.ts +0 -171
- package/node_modules/@blockcast/mmt-manifest-verify/src/index.ts +0 -3
- package/node_modules/@blockcast/mmt-manifest-verify/src/source-auth.ts +0 -230
- package/node_modules/@blockcast/mmt-manifest-verify/src/verifier.ts +0 -669
- package/node_modules/@noble/hashes/LICENSE +0 -21
- package/node_modules/@noble/hashes/README.md +0 -521
- package/node_modules/@noble/hashes/_assert.d.ts +0 -17
- package/node_modules/@noble/hashes/_assert.d.ts.map +0 -1
- package/node_modules/@noble/hashes/_assert.js +0 -18
- package/node_modules/@noble/hashes/_assert.js.map +0 -1
- package/node_modules/@noble/hashes/_blake.d.ts +0 -14
- package/node_modules/@noble/hashes/_blake.d.ts.map +0 -1
- package/node_modules/@noble/hashes/_blake.js +0 -50
- package/node_modules/@noble/hashes/_blake.js.map +0 -1
- package/node_modules/@noble/hashes/_md.d.ts +0 -51
- package/node_modules/@noble/hashes/_md.d.ts.map +0 -1
- package/node_modules/@noble/hashes/_md.js +0 -162
- package/node_modules/@noble/hashes/_md.js.map +0 -1
- package/node_modules/@noble/hashes/_u64.d.ts +0 -55
- package/node_modules/@noble/hashes/_u64.d.ts.map +0 -1
- package/node_modules/@noble/hashes/_u64.js +0 -90
- package/node_modules/@noble/hashes/_u64.js.map +0 -1
- package/node_modules/@noble/hashes/argon2.d.ts +0 -32
- package/node_modules/@noble/hashes/argon2.d.ts.map +0 -1
- package/node_modules/@noble/hashes/argon2.js +0 -401
- package/node_modules/@noble/hashes/argon2.js.map +0 -1
- package/node_modules/@noble/hashes/blake1.d.ts +0 -106
- package/node_modules/@noble/hashes/blake1.d.ts.map +0 -1
- package/node_modules/@noble/hashes/blake1.js +0 -459
- package/node_modules/@noble/hashes/blake1.js.map +0 -1
- package/node_modules/@noble/hashes/blake2.d.ts +0 -116
- package/node_modules/@noble/hashes/blake2.d.ts.map +0 -1
- package/node_modules/@noble/hashes/blake2.js +0 -420
- package/node_modules/@noble/hashes/blake2.js.map +0 -1
- package/node_modules/@noble/hashes/blake2b.d.ts +0 -11
- package/node_modules/@noble/hashes/blake2b.d.ts.map +0 -1
- package/node_modules/@noble/hashes/blake2b.js +0 -14
- package/node_modules/@noble/hashes/blake2b.js.map +0 -1
- package/node_modules/@noble/hashes/blake2s.d.ts +0 -20
- package/node_modules/@noble/hashes/blake2s.d.ts.map +0 -1
- package/node_modules/@noble/hashes/blake2s.js +0 -24
- package/node_modules/@noble/hashes/blake2s.js.map +0 -1
- package/node_modules/@noble/hashes/blake3.d.ts +0 -54
- package/node_modules/@noble/hashes/blake3.d.ts.map +0 -1
- package/node_modules/@noble/hashes/blake3.js +0 -255
- package/node_modules/@noble/hashes/blake3.js.map +0 -1
- package/node_modules/@noble/hashes/crypto.d.ts +0 -2
- package/node_modules/@noble/hashes/crypto.d.ts.map +0 -1
- package/node_modules/@noble/hashes/crypto.js +0 -5
- package/node_modules/@noble/hashes/crypto.js.map +0 -1
- package/node_modules/@noble/hashes/cryptoNode.d.ts +0 -2
- package/node_modules/@noble/hashes/cryptoNode.d.ts.map +0 -1
- package/node_modules/@noble/hashes/cryptoNode.js +0 -18
- package/node_modules/@noble/hashes/cryptoNode.js.map +0 -1
- package/node_modules/@noble/hashes/eskdf.d.ts +0 -47
- package/node_modules/@noble/hashes/eskdf.d.ts.map +0 -1
- package/node_modules/@noble/hashes/eskdf.js +0 -166
- package/node_modules/@noble/hashes/eskdf.js.map +0 -1
- package/node_modules/@noble/hashes/esm/_assert.d.ts +0 -17
- package/node_modules/@noble/hashes/esm/_assert.d.ts.map +0 -1
- package/node_modules/@noble/hashes/esm/_assert.js +0 -15
- package/node_modules/@noble/hashes/esm/_assert.js.map +0 -1
- package/node_modules/@noble/hashes/esm/_blake.d.ts +0 -14
- package/node_modules/@noble/hashes/esm/_blake.d.ts.map +0 -1
- package/node_modules/@noble/hashes/esm/_blake.js +0 -45
- package/node_modules/@noble/hashes/esm/_blake.js.map +0 -1
- package/node_modules/@noble/hashes/esm/_md.d.ts +0 -51
- package/node_modules/@noble/hashes/esm/_md.d.ts.map +0 -1
- package/node_modules/@noble/hashes/esm/_md.js +0 -155
- package/node_modules/@noble/hashes/esm/_md.js.map +0 -1
- package/node_modules/@noble/hashes/esm/_u64.d.ts +0 -55
- package/node_modules/@noble/hashes/esm/_u64.d.ts.map +0 -1
- package/node_modules/@noble/hashes/esm/_u64.js +0 -67
- package/node_modules/@noble/hashes/esm/_u64.js.map +0 -1
- package/node_modules/@noble/hashes/esm/argon2.d.ts +0 -32
- package/node_modules/@noble/hashes/esm/argon2.d.ts.map +0 -1
- package/node_modules/@noble/hashes/esm/argon2.js +0 -392
- package/node_modules/@noble/hashes/esm/argon2.js.map +0 -1
- package/node_modules/@noble/hashes/esm/blake1.d.ts +0 -106
- package/node_modules/@noble/hashes/esm/blake1.d.ts.map +0 -1
- package/node_modules/@noble/hashes/esm/blake1.js +0 -452
- package/node_modules/@noble/hashes/esm/blake1.js.map +0 -1
- package/node_modules/@noble/hashes/esm/blake2.d.ts +0 -116
- package/node_modules/@noble/hashes/esm/blake2.d.ts.map +0 -1
- package/node_modules/@noble/hashes/esm/blake2.js +0 -413
- package/node_modules/@noble/hashes/esm/blake2.js.map +0 -1
- package/node_modules/@noble/hashes/esm/blake2b.d.ts +0 -11
- package/node_modules/@noble/hashes/esm/blake2b.d.ts.map +0 -1
- package/node_modules/@noble/hashes/esm/blake2b.js +0 -11
- package/node_modules/@noble/hashes/esm/blake2b.js.map +0 -1
- package/node_modules/@noble/hashes/esm/blake2s.d.ts +0 -20
- package/node_modules/@noble/hashes/esm/blake2s.d.ts.map +0 -1
- package/node_modules/@noble/hashes/esm/blake2s.js +0 -21
- package/node_modules/@noble/hashes/esm/blake2s.js.map +0 -1
- package/node_modules/@noble/hashes/esm/blake3.d.ts +0 -54
- package/node_modules/@noble/hashes/esm/blake3.d.ts.map +0 -1
- package/node_modules/@noble/hashes/esm/blake3.js +0 -251
- package/node_modules/@noble/hashes/esm/blake3.js.map +0 -1
- package/node_modules/@noble/hashes/esm/crypto.d.ts +0 -2
- package/node_modules/@noble/hashes/esm/crypto.d.ts.map +0 -1
- package/node_modules/@noble/hashes/esm/crypto.js +0 -2
- package/node_modules/@noble/hashes/esm/crypto.js.map +0 -1
- package/node_modules/@noble/hashes/esm/cryptoNode.d.ts +0 -2
- package/node_modules/@noble/hashes/esm/cryptoNode.d.ts.map +0 -1
- package/node_modules/@noble/hashes/esm/cryptoNode.js +0 -15
- package/node_modules/@noble/hashes/esm/cryptoNode.js.map +0 -1
- package/node_modules/@noble/hashes/esm/eskdf.d.ts +0 -47
- package/node_modules/@noble/hashes/esm/eskdf.d.ts.map +0 -1
- package/node_modules/@noble/hashes/esm/eskdf.js +0 -160
- package/node_modules/@noble/hashes/esm/eskdf.js.map +0 -1
- package/node_modules/@noble/hashes/esm/hkdf.d.ts +0 -36
- package/node_modules/@noble/hashes/esm/hkdf.d.ts.map +0 -1
- package/node_modules/@noble/hashes/esm/hkdf.js +0 -82
- package/node_modules/@noble/hashes/esm/hkdf.js.map +0 -1
- package/node_modules/@noble/hashes/esm/hmac.d.ts +0 -35
- package/node_modules/@noble/hashes/esm/hmac.d.ts.map +0 -1
- package/node_modules/@noble/hashes/esm/hmac.js +0 -86
- package/node_modules/@noble/hashes/esm/hmac.js.map +0 -1
- package/node_modules/@noble/hashes/esm/index.d.ts +0 -2
- package/node_modules/@noble/hashes/esm/index.d.ts.map +0 -1
- package/node_modules/@noble/hashes/esm/index.js +0 -33
- package/node_modules/@noble/hashes/esm/index.js.map +0 -1
- package/node_modules/@noble/hashes/esm/legacy.d.ts +0 -71
- package/node_modules/@noble/hashes/esm/legacy.d.ts.map +0 -1
- package/node_modules/@noble/hashes/esm/legacy.js +0 -281
- package/node_modules/@noble/hashes/esm/legacy.js.map +0 -1
- package/node_modules/@noble/hashes/esm/package.json +0 -10
- package/node_modules/@noble/hashes/esm/pbkdf2.d.ts +0 -23
- package/node_modules/@noble/hashes/esm/pbkdf2.d.ts.map +0 -1
- package/node_modules/@noble/hashes/esm/pbkdf2.js +0 -97
- package/node_modules/@noble/hashes/esm/pbkdf2.js.map +0 -1
- package/node_modules/@noble/hashes/esm/ripemd160.d.ts +0 -13
- package/node_modules/@noble/hashes/esm/ripemd160.d.ts.map +0 -1
- package/node_modules/@noble/hashes/esm/ripemd160.js +0 -13
- package/node_modules/@noble/hashes/esm/ripemd160.js.map +0 -1
- package/node_modules/@noble/hashes/esm/scrypt.d.ts +0 -34
- package/node_modules/@noble/hashes/esm/scrypt.d.ts.map +0 -1
- package/node_modules/@noble/hashes/esm/scrypt.js +0 -228
- package/node_modules/@noble/hashes/esm/scrypt.js.map +0 -1
- package/node_modules/@noble/hashes/esm/sha1.d.ts +0 -11
- package/node_modules/@noble/hashes/esm/sha1.d.ts.map +0 -1
- package/node_modules/@noble/hashes/esm/sha1.js +0 -11
- package/node_modules/@noble/hashes/esm/sha1.js.map +0 -1
- package/node_modules/@noble/hashes/esm/sha2.d.ts +0 -159
- package/node_modules/@noble/hashes/esm/sha2.d.ts.map +0 -1
- package/node_modules/@noble/hashes/esm/sha2.js +0 -375
- package/node_modules/@noble/hashes/esm/sha2.js.map +0 -1
- package/node_modules/@noble/hashes/esm/sha256.d.ts +0 -20
- package/node_modules/@noble/hashes/esm/sha256.d.ts.map +0 -1
- package/node_modules/@noble/hashes/esm/sha256.js +0 -20
- package/node_modules/@noble/hashes/esm/sha256.js.map +0 -1
- package/node_modules/@noble/hashes/esm/sha3-addons.d.ts +0 -142
- package/node_modules/@noble/hashes/esm/sha3-addons.d.ts.map +0 -1
- package/node_modules/@noble/hashes/esm/sha3-addons.js +0 -393
- package/node_modules/@noble/hashes/esm/sha3-addons.js.map +0 -1
- package/node_modules/@noble/hashes/esm/sha3.d.ts +0 -53
- package/node_modules/@noble/hashes/esm/sha3.d.ts.map +0 -1
- package/node_modules/@noble/hashes/esm/sha3.js +0 -234
- package/node_modules/@noble/hashes/esm/sha3.js.map +0 -1
- package/node_modules/@noble/hashes/esm/sha512.d.ts +0 -26
- package/node_modules/@noble/hashes/esm/sha512.d.ts.map +0 -1
- package/node_modules/@noble/hashes/esm/sha512.js +0 -26
- package/node_modules/@noble/hashes/esm/sha512.js.map +0 -1
- package/node_modules/@noble/hashes/esm/utils.d.ts +0 -161
- package/node_modules/@noble/hashes/esm/utils.d.ts.map +0 -1
- package/node_modules/@noble/hashes/esm/utils.js +0 -281
- package/node_modules/@noble/hashes/esm/utils.js.map +0 -1
- package/node_modules/@noble/hashes/hkdf.d.ts +0 -36
- package/node_modules/@noble/hashes/hkdf.d.ts.map +0 -1
- package/node_modules/@noble/hashes/hkdf.js +0 -88
- package/node_modules/@noble/hashes/hkdf.js.map +0 -1
- package/node_modules/@noble/hashes/hmac.d.ts +0 -35
- package/node_modules/@noble/hashes/hmac.d.ts.map +0 -1
- package/node_modules/@noble/hashes/hmac.js +0 -91
- package/node_modules/@noble/hashes/hmac.js.map +0 -1
- package/node_modules/@noble/hashes/index.d.ts +0 -1
- package/node_modules/@noble/hashes/index.d.ts.map +0 -1
- package/node_modules/@noble/hashes/index.js +0 -33
- package/node_modules/@noble/hashes/index.js.map +0 -1
- package/node_modules/@noble/hashes/legacy.d.ts +0 -71
- package/node_modules/@noble/hashes/legacy.d.ts.map +0 -1
- package/node_modules/@noble/hashes/legacy.js +0 -287
- package/node_modules/@noble/hashes/legacy.js.map +0 -1
- package/node_modules/@noble/hashes/package.json +0 -266
- package/node_modules/@noble/hashes/pbkdf2.d.ts +0 -23
- package/node_modules/@noble/hashes/pbkdf2.d.ts.map +0 -1
- package/node_modules/@noble/hashes/pbkdf2.js +0 -101
- package/node_modules/@noble/hashes/pbkdf2.js.map +0 -1
- package/node_modules/@noble/hashes/ripemd160.d.ts +0 -13
- package/node_modules/@noble/hashes/ripemd160.d.ts.map +0 -1
- package/node_modules/@noble/hashes/ripemd160.js +0 -16
- package/node_modules/@noble/hashes/ripemd160.js.map +0 -1
- package/node_modules/@noble/hashes/scrypt.d.ts +0 -34
- package/node_modules/@noble/hashes/scrypt.d.ts.map +0 -1
- package/node_modules/@noble/hashes/scrypt.js +0 -232
- package/node_modules/@noble/hashes/scrypt.js.map +0 -1
- package/node_modules/@noble/hashes/sha1.d.ts +0 -11
- package/node_modules/@noble/hashes/sha1.d.ts.map +0 -1
- package/node_modules/@noble/hashes/sha1.js +0 -14
- package/node_modules/@noble/hashes/sha1.js.map +0 -1
- package/node_modules/@noble/hashes/sha2.d.ts +0 -159
- package/node_modules/@noble/hashes/sha2.d.ts.map +0 -1
- package/node_modules/@noble/hashes/sha2.js +0 -384
- package/node_modules/@noble/hashes/sha2.js.map +0 -1
- package/node_modules/@noble/hashes/sha256.d.ts +0 -20
- package/node_modules/@noble/hashes/sha256.d.ts.map +0 -1
- package/node_modules/@noble/hashes/sha256.js +0 -23
- package/node_modules/@noble/hashes/sha256.js.map +0 -1
- package/node_modules/@noble/hashes/sha3-addons.d.ts +0 -142
- package/node_modules/@noble/hashes/sha3-addons.d.ts.map +0 -1
- package/node_modules/@noble/hashes/sha3-addons.js +0 -402
- package/node_modules/@noble/hashes/sha3-addons.js.map +0 -1
- package/node_modules/@noble/hashes/sha3.d.ts +0 -53
- package/node_modules/@noble/hashes/sha3.d.ts.map +0 -1
- package/node_modules/@noble/hashes/sha3.js +0 -239
- package/node_modules/@noble/hashes/sha3.js.map +0 -1
- package/node_modules/@noble/hashes/sha512.d.ts +0 -26
- package/node_modules/@noble/hashes/sha512.d.ts.map +0 -1
- package/node_modules/@noble/hashes/sha512.js +0 -29
- package/node_modules/@noble/hashes/sha512.js.map +0 -1
- package/node_modules/@noble/hashes/src/_assert.ts +0 -22
- package/node_modules/@noble/hashes/src/_blake.ts +0 -50
- package/node_modules/@noble/hashes/src/_md.ts +0 -176
- package/node_modules/@noble/hashes/src/_u64.ts +0 -91
- package/node_modules/@noble/hashes/src/argon2.ts +0 -497
- package/node_modules/@noble/hashes/src/blake1.ts +0 -534
- package/node_modules/@noble/hashes/src/blake2.ts +0 -486
- package/node_modules/@noble/hashes/src/blake2b.ts +0 -10
- package/node_modules/@noble/hashes/src/blake2s.ts +0 -20
- package/node_modules/@noble/hashes/src/blake3.ts +0 -272
- package/node_modules/@noble/hashes/src/crypto.ts +0 -9
- package/node_modules/@noble/hashes/src/cryptoNode.ts +0 -15
- package/node_modules/@noble/hashes/src/eskdf.ts +0 -187
- package/node_modules/@noble/hashes/src/hkdf.ts +0 -88
- package/node_modules/@noble/hashes/src/hmac.ts +0 -94
- package/node_modules/@noble/hashes/src/index.ts +0 -31
- package/node_modules/@noble/hashes/src/legacy.ts +0 -293
- package/node_modules/@noble/hashes/src/pbkdf2.ts +0 -122
- package/node_modules/@noble/hashes/src/ripemd160.ts +0 -12
- package/node_modules/@noble/hashes/src/scrypt.ts +0 -257
- package/node_modules/@noble/hashes/src/sha1.ts +0 -10
- package/node_modules/@noble/hashes/src/sha2.ts +0 -402
- package/node_modules/@noble/hashes/src/sha256.ts +0 -24
- package/node_modules/@noble/hashes/src/sha3-addons.ts +0 -499
- package/node_modules/@noble/hashes/src/sha3.ts +0 -258
- package/node_modules/@noble/hashes/src/sha512.ts +0 -34
- package/node_modules/@noble/hashes/src/utils.ts +0 -395
- package/node_modules/@noble/hashes/utils.d.ts +0 -161
- package/node_modules/@noble/hashes/utils.d.ts.map +0 -1
- package/node_modules/@noble/hashes/utils.js +0 -313
- package/node_modules/@noble/hashes/utils.js.map +0 -1
|
@@ -441,49 +441,21 @@ function serial32Precedes(a, b) {
|
|
|
441
441
|
const distance = serial32Distance(a, b);
|
|
442
442
|
return distance !== 0 && distance < SERIAL32_HALF;
|
|
443
443
|
}
|
|
444
|
-
function kStrideBlockStart(sbn, k) {
|
|
445
|
-
assertUint32(sbn, "sbn");
|
|
446
|
-
if (!Number.isInteger(k) || k < 1 || k >= SERIAL32_HALF) {
|
|
447
|
-
throw new RangeError(`k must be a positive integer below 2^31 (got ${k})`);
|
|
448
|
-
}
|
|
449
|
-
const high = Math.floor(sbn / 65536);
|
|
450
|
-
const low = sbn % 65536;
|
|
451
|
-
return (high * k % SERIAL32_SPAN * 65536 + low * k) % SERIAL32_SPAN;
|
|
452
|
-
}
|
|
453
|
-
function kStrideBlockPrecedes(a, b, k) {
|
|
454
|
-
return serial32Precedes(kStrideBlockStart(a, k), kStrideBlockStart(b, k));
|
|
455
|
-
}
|
|
456
|
-
function kStrideBlocksBehind(a, b, k) {
|
|
457
|
-
const from = kStrideBlockStart(a, k);
|
|
458
|
-
const to = kStrideBlockStart(b, k);
|
|
459
|
-
if (!serial32Precedes(from, to))
|
|
460
|
-
return 0;
|
|
461
|
-
return Math.ceil(serial32Distance(from, to) / k);
|
|
462
|
-
}
|
|
463
|
-
function isKStrideBlock(sbn, k) {
|
|
464
|
-
return Number.isInteger(sbn) && sbn >= 0 && sbn < SERIAL32_SPAN && Number.isInteger(k) && k >= 1 && k < SERIAL32_HALF;
|
|
465
|
-
}
|
|
466
|
-
function kStrideNextBlock(sbn, k) {
|
|
467
|
-
assertUint32(sbn, "sbn");
|
|
468
|
-
kStrideBlockStart(sbn, k);
|
|
469
|
-
return (sbn + 1) * k >= SERIAL32_SPAN ? 0 : sbn + 1;
|
|
470
|
-
}
|
|
471
444
|
|
|
472
445
|
// ../mmt-base/dist/fec-block-profile.js
|
|
473
446
|
var FEC_BLOCK_PROFILES = Object.freeze([
|
|
474
|
-
"k-padded-v1",
|
|
475
447
|
"iso-ssbg1-v1"
|
|
476
448
|
]);
|
|
477
449
|
function isFecBlockProfile(value) {
|
|
478
|
-
return value === "
|
|
450
|
+
return value === "iso-ssbg1-v1";
|
|
479
451
|
}
|
|
480
452
|
function parseFecBlockProfile(value, where) {
|
|
481
453
|
if (isFecBlockProfile(value))
|
|
482
454
|
return value;
|
|
483
455
|
if (value === void 0) {
|
|
484
|
-
throw new Error(`${where} is required: name the FEC block profile ("
|
|
456
|
+
throw new Error(`${where} is required: name the FEC block profile ("iso-ssbg1-v1")`);
|
|
485
457
|
}
|
|
486
|
-
throw new Error(`${where} must be "
|
|
458
|
+
throw new Error(`${where} must be "iso-ssbg1-v1" (got ${JSON.stringify(value)})`);
|
|
487
459
|
}
|
|
488
460
|
var SSBG1_LENGTH_FIELD_BYTES = 2;
|
|
489
461
|
function ssbg1MaxPacketLength(symbolSize) {
|
|
@@ -508,20 +480,22 @@ function isoBlocksBehindLowerBound(a, b, kMax) {
|
|
|
508
480
|
// src/track-session.ts
|
|
509
481
|
import { fecBlockBudgetMs } from "@blockcast/mmt-fec";
|
|
510
482
|
function computeFecDeliveryWindowMs(config) {
|
|
511
|
-
const fecInterleaveMs = config.interleaveDepthMs;
|
|
512
483
|
const fecK = config.sourceSymbols;
|
|
513
|
-
if (!Number.isFinite(
|
|
484
|
+
if (!Number.isFinite(fecK) || fecK <= 0) {
|
|
514
485
|
throw new Error(
|
|
515
|
-
`[TrackSession]
|
|
486
|
+
`[TrackSession] fecK=${fecK}. Derive a positive FEC source-symbol count from the stream catalog.`
|
|
516
487
|
);
|
|
517
488
|
}
|
|
518
|
-
|
|
489
|
+
return fecSpanDeliveryWindowMs(config.interleaveDepthMs, config.jitterBufferMs);
|
|
490
|
+
}
|
|
491
|
+
function fecSpanDeliveryWindowMs(interleaveDepthMs, jitterBufferMs) {
|
|
492
|
+
if (!Number.isFinite(interleaveDepthMs) || interleaveDepthMs <= 0) {
|
|
519
493
|
throw new Error(
|
|
520
|
-
`[TrackSession]
|
|
494
|
+
`[TrackSession] fecInterleaveMs=${interleaveDepthMs}. Derive a positive FEC interleave window from the stream catalog.`
|
|
521
495
|
);
|
|
522
496
|
}
|
|
523
|
-
if (
|
|
524
|
-
return fecBlockBudgetMs(
|
|
497
|
+
if (jitterBufferMs === void 0) return Math.ceil(interleaveDepthMs);
|
|
498
|
+
return fecBlockBudgetMs(interleaveDepthMs, jitterBufferMs);
|
|
525
499
|
}
|
|
526
500
|
|
|
527
501
|
// src/delivery-gap.ts
|
|
@@ -613,7 +587,9 @@ var DeliveryGapEstimator = class {
|
|
|
613
587
|
import {
|
|
614
588
|
deriveFecInterleaveDepth as deriveFecInterleaveDepth2,
|
|
615
589
|
deriveFecRetentionBlocks,
|
|
616
|
-
tryDeriveFecRetentionBlocks
|
|
590
|
+
tryDeriveFecRetentionBlocks,
|
|
591
|
+
LOCAL_FEC_RETENTION_BLOCKS,
|
|
592
|
+
LOCAL_FEC_TIMEOUT_MS
|
|
617
593
|
} from "@blockcast/mmt-fec";
|
|
618
594
|
|
|
619
595
|
// src/iso-block-locator.ts
|
|
@@ -847,7 +823,7 @@ var IsoBlockLocator = class _IsoBlockLocator {
|
|
|
847
823
|
};
|
|
848
824
|
|
|
849
825
|
// src/fec-state.ts
|
|
850
|
-
var MIN_INTERLEAVE_DEPTH_MS =
|
|
826
|
+
var MIN_INTERLEAVE_DEPTH_MS = 1;
|
|
851
827
|
var FEC_INACTIVE_REASONS = Object.freeze([
|
|
852
828
|
"no-catalog-fec",
|
|
853
829
|
"reliable-moq",
|
|
@@ -858,6 +834,15 @@ var INACTIVE = Object.freeze({
|
|
|
858
834
|
"reliable-moq": Object.freeze({ state: "inactive", reason: "reliable-moq" }),
|
|
859
835
|
"no-multicast-bridge": Object.freeze({ state: "inactive", reason: "no-multicast-bridge" })
|
|
860
836
|
});
|
|
837
|
+
function recordOf(value) {
|
|
838
|
+
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
|
|
839
|
+
}
|
|
840
|
+
function isoBlockSpanUsable(fec) {
|
|
841
|
+
const block = recordOf(fec);
|
|
842
|
+
if (block === void 0 || block["blockProfile"] !== "iso-ssbg1-v1") return true;
|
|
843
|
+
const ms = block["interleaveDepthMs"];
|
|
844
|
+
return ms === void 0 || typeof ms === "number" && Number.isSafeInteger(ms) && ms >= 0;
|
|
845
|
+
}
|
|
861
846
|
|
|
862
847
|
// src/block-policy.ts
|
|
863
848
|
import { deriveFecInterleaveDepth } from "@blockcast/mmt-fec";
|
|
@@ -1366,7 +1351,6 @@ var FECManager = class _FECManager {
|
|
|
1366
1351
|
constructor() {
|
|
1367
1352
|
// Per-track WASM RaptorQ decoders, keyed by physical stream identity.
|
|
1368
1353
|
this.#fecDecoders = /* @__PURE__ */ new Map();
|
|
1369
|
-
/** @deprecated Use setFecDecoder(packetId, ...). Backwards-compat shim. */
|
|
1370
1354
|
// Per-track Worker-backed FEC decoders, keyed by physical stream identity.
|
|
1371
1355
|
// Worker-backed tracks use async decode — recovered frames arrive via onFrame callback,
|
|
1372
1356
|
// not synchronous return from feedSourceSymbol/feedRepairSymbol.
|
|
@@ -1381,10 +1365,9 @@ var FECManager = class _FECManager {
|
|
|
1381
1365
|
// source/repair symbols do not split across two RaptorQ states.
|
|
1382
1366
|
this.#syncDecoderBlockOwners = /* @__PURE__ */ new Map();
|
|
1383
1367
|
this.#decoderBlockOwners = /* @__PURE__ */ new WeakMap();
|
|
1384
|
-
this.#fecK = 4;
|
|
1385
1368
|
this.#fecSymbolSize = 0;
|
|
1386
|
-
this.#fecTimeoutMs =
|
|
1387
|
-
// default
|
|
1369
|
+
this.#fecTimeoutMs = LOCAL_FEC_TIMEOUT_MS;
|
|
1370
|
+
// local default; refined by setFramerate() when fps known
|
|
1388
1371
|
this.#trackConfigs = /* @__PURE__ */ new Map();
|
|
1389
1372
|
this.#fecStats = newFecCounters();
|
|
1390
1373
|
this.#fecTrackStats = /* @__PURE__ */ new Map();
|
|
@@ -1424,10 +1407,10 @@ var FECManager = class _FECManager {
|
|
|
1424
1407
|
this.#blockSourceEsis = /* @__PURE__ */ new Map();
|
|
1425
1408
|
// Authored SSB_length per block (ISO/IEC 23008-1 §C.5.3), learned from the
|
|
1426
1409
|
// first repair symbol. The FFmpeg moq_mmt publisher closes a block every D
|
|
1427
|
-
// media units, usually before it holds K symbols
|
|
1428
|
-
//
|
|
1429
|
-
// SSB_length
|
|
1430
|
-
//
|
|
1410
|
+
// media units, usually before it holds K symbols; under iso-ssbg1-v1
|
|
1411
|
+
// (ssbg_mode1) that block is K' = SSB_length symbols, its repair ESIs start
|
|
1412
|
+
// at SSB_length, and K is only the maximum. Such a block is clean at
|
|
1413
|
+
// SSB_length source arrivals and recoverable from SSB_length received
|
|
1431
1414
|
// symbols; waiting for K fails every one of them.
|
|
1432
1415
|
this.#blockAuthoredLengths = /* @__PURE__ */ new Map();
|
|
1433
1416
|
this.#ssbLengthConflictCount = 0;
|
|
@@ -1544,14 +1527,16 @@ var FECManager = class _FECManager {
|
|
|
1544
1527
|
this.#resolvedBlockClasses = /* @__PURE__ */ new Map();
|
|
1545
1528
|
// Blocks already reported through onBlockRepaired (once per block).
|
|
1546
1529
|
this.#repairedBlocks = /* @__PURE__ */ new Set();
|
|
1547
|
-
// Blocks created
|
|
1530
|
+
// Blocks created before any symbol of theirs was tracked: the repair-less
|
|
1531
|
+
// spans placeIsoRepair materializes (onBlockVanished).
|
|
1548
1532
|
this.#materializedBlocks = /* @__PURE__ */ new Set();
|
|
1549
1533
|
// packetIds whose ordered-relay horizon became underivable and fell back to
|
|
1550
1534
|
// the datagram deadline. Bounds the warn in #derivableOrderedHorizon to once
|
|
1551
1535
|
// per stream. Not only throws: the ordinary out-of-range case is detected by
|
|
1552
1536
|
// arithmetic and never throws at all.
|
|
1553
1537
|
this.#orderedHorizonWarnedFor = /* @__PURE__ */ new Set();
|
|
1554
|
-
//
|
|
1538
|
+
// Newest block start (SS_Start) seen per stream and when: the frontier for
|
|
1539
|
+
// source-discontinuity detection and isoSpanResolution.
|
|
1555
1540
|
this.#highestSbn = /* @__PURE__ */ new Map();
|
|
1556
1541
|
// iso-ssbg1-v1 block location per stream (streamKey). A source belongs to a
|
|
1557
1542
|
// block only once a repair symbol stated the block's (SS_Start, SSB_length).
|
|
@@ -1645,7 +1630,10 @@ var FECManager = class _FECManager {
|
|
|
1645
1630
|
#releasedWorkerClients;
|
|
1646
1631
|
#syncDecoderBlockOwners;
|
|
1647
1632
|
#decoderBlockOwners;
|
|
1648
|
-
|
|
1633
|
+
// The stream-global K a configure() call stated, reported as fecStats.k.
|
|
1634
|
+
// It never resolves a packetId: a packetId has only its own track config
|
|
1635
|
+
// (BLO-45953), and K does not feed the delivery-window math.
|
|
1636
|
+
#streamK;
|
|
1649
1637
|
#fecSymbolSize;
|
|
1650
1638
|
#fecTimeoutMs;
|
|
1651
1639
|
#trackConfigs;
|
|
@@ -1807,7 +1795,8 @@ var FECManager = class _FECManager {
|
|
|
1807
1795
|
this.#completedBlocks.add(key);
|
|
1808
1796
|
if (clean) this.#cleanlyCompletedBlocks.add(key);
|
|
1809
1797
|
this.#indexBlockKey(key, sbn, packetId);
|
|
1810
|
-
const
|
|
1798
|
+
const config = this.#trackConfig(packetId);
|
|
1799
|
+
const expiresAtMs = nowMs + config.interleaveDepthMs + 2 * this.#blockLifetimeMs(config);
|
|
1811
1800
|
this.#terminalExpiryMs.set(key, expiresAtMs);
|
|
1812
1801
|
let queue = this.#terminalQueues.get(packetId);
|
|
1813
1802
|
if (!queue) {
|
|
@@ -1843,8 +1832,9 @@ var FECManager = class _FECManager {
|
|
|
1843
1832
|
* retention the Worker and the WASM decoder are configured with, because it
|
|
1844
1833
|
* comes from the same function they do (`deriveFecRetentionBlocks`, over the
|
|
1845
1834
|
* block's delivery window and its span). The decoder retires every block
|
|
1846
|
-
* more than this many
|
|
1847
|
-
*
|
|
1835
|
+
* more than this many blocks behind the newest SS_Start it saw, counted
|
|
1836
|
+
* from below by `isoBlocksBehindLowerBound` (`cleanup_by_ss_start`), so
|
|
1837
|
+
* settling earlier than this would decide a block the decoder would still
|
|
1848
1838
|
* have recovered.
|
|
1849
1839
|
*
|
|
1850
1840
|
* This used to call `deriveFecInterleaveDepth`, which returns D -- MEDIA
|
|
@@ -1857,6 +1847,14 @@ var FECManager = class _FECManager {
|
|
|
1857
1847
|
* derivation; do not re-derive a horizon at either call site.
|
|
1858
1848
|
*/
|
|
1859
1849
|
#orderedHorizonBlocksFor(packetId, config) {
|
|
1850
|
+
if (config.interleaveDepthMs === 0) {
|
|
1851
|
+
if (Number.isFinite(config.timeoutMs) && config.timeoutMs > 0) {
|
|
1852
|
+
return LOCAL_FEC_RETENTION_BLOCKS;
|
|
1853
|
+
}
|
|
1854
|
+
throw new Error(
|
|
1855
|
+
`[FECManager] packetId=${packetId}: local FEC cleanup requires a positive timeout (got ${config.timeoutMs})`
|
|
1856
|
+
);
|
|
1857
|
+
}
|
|
1860
1858
|
if (!_FECManager.#hasOrderedGeometry(config)) {
|
|
1861
1859
|
throw new Error(
|
|
1862
1860
|
`[FECManager] packetId=${packetId}: ordered-relay settlement derives the decoder's interleave horizon from the catalog interleaveDepthMs (>= ${MIN_INTERLEAVE_DEPTH_MS}ms), the media-unit rate and the block delivery window; got interleaveDepthMs=${config.interleaveDepthMs} fps=${String(config.fps)} timeoutMs=${config.timeoutMs}.`
|
|
@@ -1870,7 +1868,7 @@ var FECManager = class _FECManager {
|
|
|
1870
1868
|
* two can never disagree about which streams settle by stream order.
|
|
1871
1869
|
*/
|
|
1872
1870
|
static #hasOrderedGeometry(config) {
|
|
1873
|
-
return config
|
|
1871
|
+
return _FECManager.#hasCatalogTiming(config) && // `fps` is no longer an input to the horizon, but it still gates WHICH
|
|
1874
1872
|
// streams settle by stream order at all: a track with no usable
|
|
1875
1873
|
// media-unit rate fell back to datagram semantics before this and must
|
|
1876
1874
|
// keep doing so. Removing this widens ordered-relay settlement to streams
|
|
@@ -1910,6 +1908,9 @@ var FECManager = class _FECManager {
|
|
|
1910
1908
|
* argument: narrower IS the failing direction.
|
|
1911
1909
|
*/
|
|
1912
1910
|
#derivableOrderedHorizon(config, packetId) {
|
|
1911
|
+
if (config.interleaveDepthMs === 0) {
|
|
1912
|
+
return Number.isFinite(config.timeoutMs) && config.timeoutMs > 0 ? LOCAL_FEC_RETENTION_BLOCKS : void 0;
|
|
1913
|
+
}
|
|
1913
1914
|
if (!_FECManager.#hasOrderedGeometry(config)) return void 0;
|
|
1914
1915
|
try {
|
|
1915
1916
|
const blocks = tryDeriveFecRetentionBlocks(config.timeoutMs, config.interleaveDepthMs);
|
|
@@ -1942,19 +1943,20 @@ var FECManager = class _FECManager {
|
|
|
1942
1943
|
/**
|
|
1943
1944
|
* The stream's interleave horizon, or undefined when its geometry cannot
|
|
1944
1945
|
* define one. Two different streams reach that case, with different causes:
|
|
1945
|
-
* a packetId that was never configured,
|
|
1946
|
-
*
|
|
1946
|
+
* a packetId that was never configured, which has no geometry at all
|
|
1947
|
+
* (explicitly configured tracks are refused an underivable geometry
|
|
1947
1948
|
* at config time — #upsertTrackConfig, setMulticastSymbolDelivery); or a
|
|
1948
1949
|
* configured track whose timeoutMs was later widened past the 255-block
|
|
1949
1950
|
* ceiling without re-validating, which #derivableOrderedHorizon catches
|
|
1950
1951
|
* rather than throwing from the packet-feed path. Either way the stream
|
|
1951
|
-
* keeps datagram semantics instead of throwing.
|
|
1952
|
+
* keeps datagram semantics instead of throwing. A track's configuration can
|
|
1952
1953
|
* also change under a stream that already holds blocks, so a caller cannot
|
|
1953
1954
|
* assume a horizon that was defined when a block was held is still defined
|
|
1954
1955
|
* (see #advanceOrderedFrontier).
|
|
1955
1956
|
*/
|
|
1956
1957
|
#orderedHorizonBlocks(packetId) {
|
|
1957
|
-
|
|
1958
|
+
const config = this.#trackConfigs.get(packetId);
|
|
1959
|
+
return config === void 0 ? void 0 : this.#derivableOrderedHorizon(config, packetId);
|
|
1958
1960
|
}
|
|
1959
1961
|
/** True while a block of an ordered-relay stream can still receive symbols. */
|
|
1960
1962
|
#withinOrderedHorizon(key) {
|
|
@@ -1988,7 +1990,7 @@ var FECManager = class _FECManager {
|
|
|
1988
1990
|
*/
|
|
1989
1991
|
#advanceOrderedFrontier(streamKey, sbn, packetId, catalogTrackName) {
|
|
1990
1992
|
const previous = this.#orderedFrontiers.get(streamKey);
|
|
1991
|
-
const frontier = previous === void 0 || this.#blockPrecedes(previous, sbn
|
|
1993
|
+
const frontier = previous === void 0 || this.#blockPrecedes(previous, sbn) ? sbn : previous;
|
|
1992
1994
|
if (frontier !== previous) this.#orderedFrontiers.set(streamKey, frontier);
|
|
1993
1995
|
const pending = this.#orderedPendingBlocks.get(streamKey);
|
|
1994
1996
|
if (!pending) return;
|
|
@@ -2023,7 +2025,7 @@ var FECManager = class _FECManager {
|
|
|
2023
2025
|
if (this.#completedBlocks.has(key) || this.#pendingWorkerCleanups.has(key) || this.#repairRequestedBlocks.has(key)) return;
|
|
2024
2026
|
const authoredLength = this.#blockAuthoredLengths.get(key);
|
|
2025
2027
|
const received = (this.#blockSourceCounts.get(key) ?? 0) + (this.#blockRepairRsIds.get(key)?.size ?? 0);
|
|
2026
|
-
if (this.onRepairNeeded && (authoredLength === void 0 || received < authoredLength)) {
|
|
2028
|
+
if (this.#hasCatalogTimingForPacket(packetId) && this.onRepairNeeded && (authoredLength === void 0 || received < authoredLength)) {
|
|
2027
2029
|
this.#requestRepair(sbn, packetId, catalogTrackName, true);
|
|
2028
2030
|
return;
|
|
2029
2031
|
}
|
|
@@ -2179,7 +2181,7 @@ var FECManager = class _FECManager {
|
|
|
2179
2181
|
}
|
|
2180
2182
|
#requestRepair(sbn, packetId, catalogTrackName, settledByOrder = false) {
|
|
2181
2183
|
const key = this.#blockKey(sbn, packetId, catalogTrackName);
|
|
2182
|
-
if (this.#completedBlocks.has(key) || !this.onRepairNeeded || this.#repairRequestedBlocks.has(key)) return;
|
|
2184
|
+
if (this.#completedBlocks.has(key) || !this.#hasCatalogTimingForPacket(packetId) || !this.onRepairNeeded || this.#repairRequestedBlocks.has(key)) return;
|
|
2183
2185
|
const deadline = this.#repairDeadlines.get(key);
|
|
2184
2186
|
if (deadline?.handle != null) clearTimeout(deadline.handle);
|
|
2185
2187
|
this.#repairDeadlines.set(key, null);
|
|
@@ -2259,6 +2261,7 @@ var FECManager = class _FECManager {
|
|
|
2259
2261
|
}
|
|
2260
2262
|
/** A block's live class without creating classifier state for the stream. */
|
|
2261
2263
|
#peekClass(sbn, packetId, catalogTrackName) {
|
|
2264
|
+
if (!this.#hasCatalogTimingForPacket(packetId)) return "unknown";
|
|
2262
2265
|
const classifier = this.#classifiers.get(this.#streamKey(packetId, catalogTrackName));
|
|
2263
2266
|
if (classifier) return classifier.classOf(sbn);
|
|
2264
2267
|
return this.#geometryFor(packetId, catalogTrackName).trackKind === "audio" ? "audio" : "unknown";
|
|
@@ -2280,8 +2283,8 @@ var FECManager = class _FECManager {
|
|
|
2280
2283
|
// Deliberately one block tighter than deriveFecRetentionBlocks over the same
|
|
2281
2284
|
// inputs (that function's `+ 1`). Different question: the retention horizon
|
|
2282
2285
|
// asks how long the decoder still ACCEPTS symbols for a block, this asks how
|
|
2283
|
-
//
|
|
2284
|
-
// #noteSbnSeen).
|
|
2286
|
+
// many blocks a stream can live behind (or ahead of) its frontier before a
|
|
2287
|
+
// source symbol there is a discontinuity rather than loss (see #noteSbnSeen).
|
|
2285
2288
|
// The extra block of retention is slack for a block still being fed; a block
|
|
2286
2289
|
// nothing has been heard from does not get it. Not the units defect — but it
|
|
2287
2290
|
// is the next place the two can drift, so change both or neither.
|
|
@@ -2290,27 +2293,28 @@ var FECManager = class _FECManager {
|
|
|
2290
2293
|
return Math.ceil(config.timeoutMs / config.interleaveDepthMs);
|
|
2291
2294
|
}
|
|
2292
2295
|
/**
|
|
2293
|
-
* Record that a symbol for block `sbn`
|
|
2294
|
-
*
|
|
2296
|
+
* Record that a symbol for block `sbn` (the block's SS_Start) arrived and
|
|
2297
|
+
* advance the stream's frontier, the newest block start seen.
|
|
2295
2298
|
*
|
|
2296
|
-
*
|
|
2297
|
-
*
|
|
2298
|
-
*
|
|
2299
|
-
*
|
|
2300
|
-
*
|
|
2301
|
-
* its last symbol, is a source discontinuity, not loss. A repair symbol can
|
|
2302
|
-
* only advance the frontier: a D-28 fetch legitimately answers for an old
|
|
2303
|
-
* block, so it never signals a discontinuity.
|
|
2299
|
+
* A SOURCE symbol whose block goes back further than one live window, or
|
|
2300
|
+
* jumps further than the stream could have produced since its last symbol,
|
|
2301
|
+
* is a source discontinuity, not loss. A repair symbol can only advance the
|
|
2302
|
+
* frontier: a D-28 fetch legitimately answers for an old block, so it never
|
|
2303
|
+
* signals a discontinuity.
|
|
2304
2304
|
*
|
|
2305
2305
|
* The producer closes a block at its Dth media-unit boundary OR when K
|
|
2306
|
-
* source symbols filled it, so one interleave span can start several
|
|
2307
|
-
*
|
|
2308
|
-
*
|
|
2309
|
-
*
|
|
2310
|
-
*
|
|
2311
|
-
*
|
|
2312
|
-
*
|
|
2313
|
-
*
|
|
2306
|
+
* source symbols filled it, so one interleave span can start several blocks
|
|
2307
|
+
* (every large keyframe does). What bounds a forward jump is therefore
|
|
2308
|
+
* packet evidence, not a block rate: SS_ID advances by one per FEC source
|
|
2309
|
+
* packet (ISO/IEC 23008-1 C.5.2) and the MMTP packet_sequence_number by one
|
|
2310
|
+
* per packet of the packet_id, so with both source symbols'
|
|
2311
|
+
* packet_sequence_number a block start can move at most the packets sent
|
|
2312
|
+
* between them plus the anchor's own offset into its block (< K). Without
|
|
2313
|
+
* it, only the D-close rate is known.
|
|
2314
|
+
*
|
|
2315
|
+
* Vanished blocks are not enumerated here: their starts are unknown until a
|
|
2316
|
+
* repair states them. The SS_IDs between two stated blocks become one
|
|
2317
|
+
* repair-less span when the later block's repair arrives (placeIsoRepair).
|
|
2314
2318
|
*/
|
|
2315
2319
|
#noteSbnSeen(sbn, packetId, catalogTrackName, symbol, packetSequenceNumber) {
|
|
2316
2320
|
const streamKey = this.#streamKey(packetId, catalogTrackName);
|
|
@@ -2319,7 +2323,7 @@ var FECManager = class _FECManager {
|
|
|
2319
2323
|
const anchor = this.#sourceAnchors.get(streamKey);
|
|
2320
2324
|
const psn = symbol === "source" && packetSequenceNumber !== void 0 && Number.isSafeInteger(packetSequenceNumber) && packetSequenceNumber >= 0 && packetSequenceNumber <= 4294967295 ? packetSequenceNumber : void 0;
|
|
2321
2325
|
const anchorHere = () => {
|
|
2322
|
-
if (psn !== void 0 && (anchor === void 0 || !this.#blockPrecedes(sbn, anchor.sbn
|
|
2326
|
+
if (psn !== void 0 && (anchor === void 0 || !this.#blockPrecedes(sbn, anchor.sbn))) {
|
|
2323
2327
|
this.#sourceAnchors.set(streamKey, { sbn, psn, atMs: now });
|
|
2324
2328
|
}
|
|
2325
2329
|
};
|
|
@@ -2335,9 +2339,9 @@ var FECManager = class _FECManager {
|
|
|
2335
2339
|
anchorHere();
|
|
2336
2340
|
return;
|
|
2337
2341
|
}
|
|
2338
|
-
const config = this.#
|
|
2342
|
+
const config = this.#trackConfig(packetId);
|
|
2339
2343
|
const window2 = this.#liveWindowBlocks(config);
|
|
2340
|
-
if (!this.#blockPrecedes(previous.sbn, sbn
|
|
2344
|
+
if (!this.#blockPrecedes(previous.sbn, sbn)) {
|
|
2341
2345
|
if (symbol === "source" && window2 !== null && this.#blocksBehind(sbn, previous.sbn, packetId) > window2) {
|
|
2342
2346
|
discontinuity();
|
|
2343
2347
|
return;
|
|
@@ -2348,15 +2352,9 @@ var FECManager = class _FECManager {
|
|
|
2348
2352
|
const missing = this.#blocksBehind(previous.sbn, sbn, packetId) - 1;
|
|
2349
2353
|
let plausible = true;
|
|
2350
2354
|
if (window2 !== null) {
|
|
2351
|
-
if (
|
|
2355
|
+
if (psn !== void 0 && anchor !== void 0 && this.#blockPrecedes(anchor.sbn, sbn) && psn !== anchor.psn) {
|
|
2352
2356
|
const sent = psn - anchor.psn >>> 0;
|
|
2353
2357
|
plausible = sent <= 2147483647 && serial32Distance(anchor.sbn, sbn) <= sent + config.k;
|
|
2354
|
-
} else if (psn !== void 0 && anchor !== void 0 && this.#blockPrecedes(anchor.sbn, sbn, packetId) && psn !== anchor.psn) {
|
|
2355
|
-
const sent = psn - anchor.psn >>> 0;
|
|
2356
|
-
const between = this.#blocksBehind(anchor.sbn, sbn, packetId) - 1;
|
|
2357
|
-
const dCloses = window2 + Math.ceil((now - anchor.atMs) / config.interleaveDepthMs);
|
|
2358
|
-
const kCloses = config.k > 0 ? Math.ceil(sent / config.k) : sent;
|
|
2359
|
-
plausible = sent <= 2147483647 && between <= sent - 1 && between <= dCloses + kCloses;
|
|
2360
2358
|
} else {
|
|
2361
2359
|
plausible = missing <= window2 + Math.ceil((now - previous.atMs) / config.interleaveDepthMs);
|
|
2362
2360
|
}
|
|
@@ -2368,48 +2366,31 @@ var FECManager = class _FECManager {
|
|
|
2368
2366
|
}
|
|
2369
2367
|
this.#highestSbn.set(streamKey, { sbn, atMs: now });
|
|
2370
2368
|
anchorHere();
|
|
2371
|
-
if (this.#isIso(packetId)) return;
|
|
2372
|
-
if (missing <= 0 || window2 === null) return;
|
|
2373
|
-
let vanished = previous.sbn;
|
|
2374
|
-
for (let i = 0; i < missing; i++) {
|
|
2375
|
-
vanished = this.#nextBlock(vanished, packetId);
|
|
2376
|
-
this.#materializeBlock(
|
|
2377
|
-
vanished,
|
|
2378
|
-
packetId,
|
|
2379
|
-
catalogTrackName,
|
|
2380
|
-
this.#blocksBehind(vanished, sbn, packetId) <= window2
|
|
2381
|
-
);
|
|
2382
|
-
}
|
|
2383
2369
|
}
|
|
2384
2370
|
/*
|
|
2385
|
-
* Block order for one stream (BLO-35597 iso:5).
|
|
2386
|
-
* number (ISO/IEC 23008-1 C.5.2), so
|
|
2387
|
-
*
|
|
2388
|
-
*
|
|
2389
|
-
* the order across the wrap changes. A block outside K-stride geometry has
|
|
2390
|
-
* no SS_ID position, so it keeps the SBN order it always had.
|
|
2371
|
+
* Block order for one stream (BLO-35597 iso:5). A block is keyed by its
|
|
2372
|
+
* SS_Start and SS_ID is a 32-bit serial number (ISO/IEC 23008-1 C.5.2), so
|
|
2373
|
+
* blocks are ordered by RFC 1982 comparison of their starts. Only configured
|
|
2374
|
+
* streams hold blocks: every entry point refuses an unconfigured packetId.
|
|
2391
2375
|
*/
|
|
2392
|
-
#blockPrecedes(a, b
|
|
2393
|
-
|
|
2394
|
-
const k = this.kForPacket(packetId);
|
|
2395
|
-
return isKStrideBlock(a, k) && isKStrideBlock(b, k) ? kStrideBlockPrecedes(a, b, k) : a < b;
|
|
2376
|
+
#blockPrecedes(a, b) {
|
|
2377
|
+
return serial32Precedes(a, b);
|
|
2396
2378
|
}
|
|
2397
|
-
/**
|
|
2379
|
+
/**
|
|
2380
|
+
* Blocks `a` lies behind `b` (0 when it does not precede it). Blocks vary
|
|
2381
|
+
* in length (K' = SSB_length <= K), so only a lower bound is known: the one
|
|
2382
|
+
* metric the decoder and the worker retire by too.
|
|
2383
|
+
*/
|
|
2398
2384
|
#blocksBehind(a, b, packetId) {
|
|
2399
|
-
|
|
2400
|
-
const k = this.kForPacket(packetId);
|
|
2401
|
-
return isKStrideBlock(a, k) && isKStrideBlock(b, k) ? kStrideBlocksBehind(a, b, k) : Math.max(0, b - a);
|
|
2402
|
-
}
|
|
2403
|
-
/** The block after `sbn`, stepping to block 0 where SS_ID wraps. */
|
|
2404
|
-
#nextBlock(sbn, packetId) {
|
|
2405
|
-
const k = this.kForPacket(packetId);
|
|
2406
|
-
return isKStrideBlock(sbn, k) ? kStrideNextBlock(sbn, k) : sbn + 1;
|
|
2385
|
+
return isoBlocksBehindLowerBound(a, b, this.#trackConfig(packetId).k);
|
|
2407
2386
|
}
|
|
2408
2387
|
#materializeBlock(sbn, packetId, catalogTrackName, repairable) {
|
|
2409
2388
|
const key = this.#blockKey(sbn, packetId, catalogTrackName);
|
|
2410
2389
|
if (this.#blockResolutionFor(key) !== "untracked") return;
|
|
2411
2390
|
this.#materializedBlocks.add(key);
|
|
2412
|
-
this.#
|
|
2391
|
+
if (this.#hasCatalogTimingForPacket(packetId)) {
|
|
2392
|
+
this.#classifierFor(packetId, catalogTrackName).touch(sbn);
|
|
2393
|
+
}
|
|
2413
2394
|
this.onBlockVanished?.(sbn, packetId, catalogTrackName);
|
|
2414
2395
|
if (repairable) {
|
|
2415
2396
|
this.noteBlockActivity(sbn, packetId, catalogTrackName, true);
|
|
@@ -2448,21 +2429,39 @@ var FECManager = class _FECManager {
|
|
|
2448
2429
|
});
|
|
2449
2430
|
if (this.#lifecycleTrace.length > 64) this.#lifecycleTrace.splice(0, 32);
|
|
2450
2431
|
}
|
|
2451
|
-
|
|
2452
|
-
|
|
2453
|
-
|
|
2454
|
-
|
|
2455
|
-
|
|
2456
|
-
|
|
2457
|
-
|
|
2458
|
-
|
|
2432
|
+
/**
|
|
2433
|
+
* A configured packetId's track config. There is no manager-wide default
|
|
2434
|
+
* (BLO-45953): an unconfigured packetId does no FEC work, so every caller
|
|
2435
|
+
* here is reached only behind a configuration gate and this throw marks a
|
|
2436
|
+
* broken invariant, never a packet-path outcome.
|
|
2437
|
+
*/
|
|
2438
|
+
#trackConfig(packetId) {
|
|
2439
|
+
const config = this.#trackConfigs.get(packetId);
|
|
2440
|
+
if (config === void 0) {
|
|
2441
|
+
throw new Error(
|
|
2442
|
+
`[FECManager] packetId=${packetId} was never configured; configure the track from its catalog (configureTrack/setFecWorker/setFecDecoder) first`
|
|
2443
|
+
);
|
|
2444
|
+
}
|
|
2445
|
+
return config;
|
|
2446
|
+
}
|
|
2447
|
+
/**
|
|
2448
|
+
* Whether this track has the catalog's elapsed-time FEC policy. A zero span
|
|
2449
|
+
* is a deliberate no-deadline signal; it still leaves the wire geometry
|
|
2450
|
+
* usable for in-band decode, but it cannot drive classification, repair
|
|
2451
|
+
* fetches or layer adaptation.
|
|
2452
|
+
*/
|
|
2453
|
+
static #hasCatalogTiming(config) {
|
|
2454
|
+
return Number.isSafeInteger(config.interleaveDepthMs) && config.interleaveDepthMs >= MIN_INTERLEAVE_DEPTH_MS;
|
|
2459
2455
|
}
|
|
2460
|
-
#
|
|
2461
|
-
|
|
2456
|
+
#hasCatalogTimingForPacket(packetId) {
|
|
2457
|
+
const config = this.#trackConfigs.get(packetId);
|
|
2458
|
+
return config !== void 0 && _FECManager.#hasCatalogTiming(config);
|
|
2462
2459
|
}
|
|
2463
2460
|
#computeTimeout(config) {
|
|
2464
|
-
if (config.timeoutMs && config.timeoutMs > 0)
|
|
2465
|
-
|
|
2461
|
+
if (typeof config.timeoutMs === "number" && Number.isFinite(config.timeoutMs) && config.timeoutMs > 0) {
|
|
2462
|
+
return config.timeoutMs;
|
|
2463
|
+
}
|
|
2464
|
+
if (Number.isSafeInteger(config.interleaveDepthMs) && config.interleaveDepthMs >= MIN_INTERLEAVE_DEPTH_MS) {
|
|
2466
2465
|
return Math.max(
|
|
2467
2466
|
this.#fecTimeoutMs,
|
|
2468
2467
|
computeFecDeliveryWindowMs({
|
|
@@ -2487,14 +2486,17 @@ var FECManager = class _FECManager {
|
|
|
2487
2486
|
*/
|
|
2488
2487
|
#refreshDeliveryGapWindow() {
|
|
2489
2488
|
let windowMs;
|
|
2490
|
-
const consider = (geometry) => {
|
|
2489
|
+
const consider = (geometry, packetId) => {
|
|
2490
|
+
if (!this.#hasCatalogTimingForPacket(packetId)) return;
|
|
2491
2491
|
if (geometry?.trackKind !== "video" || geometry.mode !== "classifying") return;
|
|
2492
2492
|
const { ticks, timescale } = geometry.keyframeInterval;
|
|
2493
2493
|
const ms = ticks / timescale * 1e3;
|
|
2494
2494
|
if (Number.isFinite(ms) && ms > 0 && (windowMs === void 0 || ms > windowMs)) windowMs = ms;
|
|
2495
2495
|
};
|
|
2496
|
-
for (const geometry of this.#streamGeometries
|
|
2497
|
-
|
|
2496
|
+
for (const [streamKey, geometry] of this.#streamGeometries) {
|
|
2497
|
+
consider(geometry, this.#parseStreamKey(streamKey).packetId);
|
|
2498
|
+
}
|
|
2499
|
+
for (const [packetId, config] of this.#trackConfigs) consider(config.geometry, packetId);
|
|
2498
2500
|
this.#deliveryGaps.setWindowMs(windowMs);
|
|
2499
2501
|
}
|
|
2500
2502
|
/**
|
|
@@ -2522,6 +2524,7 @@ var FECManager = class _FECManager {
|
|
|
2522
2524
|
return Math.max(config.timeoutMs, Math.ceil(config.interleaveDepthMs + gapMs));
|
|
2523
2525
|
}
|
|
2524
2526
|
#repairDeadlineMs(config, waitMs = this.#multicastWaitMs(config)) {
|
|
2527
|
+
if (!_FECManager.#hasCatalogTiming(config)) return waitMs;
|
|
2525
2528
|
const halfFailBoundMs = Math.ceil(waitMs / 2);
|
|
2526
2529
|
if (config.k > 0 && config.interleaveDepthMs >= MIN_INTERLEAVE_DEPTH_MS && config.fps !== void 0 && Number.isFinite(config.fps) && config.fps > 0) {
|
|
2527
2530
|
const mediaUnitMs = 1e3 / config.fps;
|
|
@@ -2536,10 +2539,12 @@ var FECManager = class _FECManager {
|
|
|
2536
2539
|
}
|
|
2537
2540
|
/**
|
|
2538
2541
|
* One block lifetime: the inactivity fail bound plus the D-28 repair grace,
|
|
2539
|
-
* at the widest wait the catalog allows (#maxMulticastWaitMs).
|
|
2540
|
-
*
|
|
2541
|
-
*
|
|
2542
|
-
*
|
|
2542
|
+
* at the widest wait the catalog allows (#maxMulticastWaitMs). For a track
|
|
2543
|
+
* without a catalog span this is receiver-local cleanup time only; no repair
|
|
2544
|
+
* grace is added. Everything derived from it (terminal retention, the
|
|
2545
|
+
* release gate's hold ceiling, the duplicate ledgers, the owed-cleanup
|
|
2546
|
+
* bound) is compared across time, so it must not follow the live
|
|
2547
|
+
* measurement.
|
|
2543
2548
|
*/
|
|
2544
2549
|
#blockLifetimeMs(config) {
|
|
2545
2550
|
const waitMs = this.#maxMulticastWaitMs(config);
|
|
@@ -2555,6 +2560,7 @@ var FECManager = class _FECManager {
|
|
|
2555
2560
|
return Math.min(waitMs + this.#repairGraceMs(config, waitMs), this.#blockLifetimeMs(config));
|
|
2556
2561
|
}
|
|
2557
2562
|
#repairGraceMs(config, waitMs) {
|
|
2563
|
+
if (!_FECManager.#hasCatalogTiming(config)) return 0;
|
|
2558
2564
|
return Math.max(1, waitMs - this.#repairDeadlineMs(config, waitMs));
|
|
2559
2565
|
}
|
|
2560
2566
|
#armBlockTimeout(sbn, packetId, catalogTrackName, delayMs) {
|
|
@@ -2641,51 +2647,72 @@ var FECManager = class _FECManager {
|
|
|
2641
2647
|
* stream; every other field stays per packetId.
|
|
2642
2648
|
*/
|
|
2643
2649
|
#upsertTrackConfig(packetId, config, catalogTrackName) {
|
|
2644
|
-
const
|
|
2650
|
+
const previousTrack = this.#trackConfigs.get(packetId);
|
|
2651
|
+
const k = config.k ?? previousTrack?.k;
|
|
2652
|
+
if (k === void 0 || !Number.isSafeInteger(k) || k <= 0) {
|
|
2653
|
+
throw new Error(
|
|
2654
|
+
`[FECManager] packetId=${packetId}: fec.k=${String(k)} must be a positive integer; configure the track from its catalog`
|
|
2655
|
+
);
|
|
2656
|
+
}
|
|
2657
|
+
const previous = previousTrack ?? {};
|
|
2645
2658
|
const streamScopedGeometry = catalogTrackName !== void 0 && config.geometry !== void 0 ? config.geometry : void 0;
|
|
2646
2659
|
if (streamScopedGeometry !== void 0) {
|
|
2647
2660
|
const { geometry: _streamGeometry, ...packetLevel } = config;
|
|
2648
2661
|
config = packetLevel;
|
|
2649
2662
|
}
|
|
2650
2663
|
const timingChanged = config.k !== void 0 || config.interleaveDepthMs !== void 0 || config.fps !== void 0;
|
|
2664
|
+
if (config.timeoutMs !== void 0 && (!Number.isFinite(config.timeoutMs) || config.timeoutMs < 0)) {
|
|
2665
|
+
throw new Error(
|
|
2666
|
+
`[FECManager] packetId=${packetId}: timeoutMs=${config.timeoutMs} must be a finite non-negative cleanup budget`
|
|
2667
|
+
);
|
|
2668
|
+
}
|
|
2651
2669
|
if (config.blockProfile !== void 0) {
|
|
2652
2670
|
parseFecBlockProfile(config.blockProfile, `packetId=${packetId} fec.blockProfile`);
|
|
2653
2671
|
}
|
|
2654
2672
|
const blockProfile = config.blockProfile ?? previous.blockProfile;
|
|
2655
2673
|
if (blockProfile === void 0) {
|
|
2656
2674
|
throw new Error(
|
|
2657
|
-
`[FECManager] packetId=${packetId}: fec.blockProfile is required ("
|
|
2675
|
+
`[FECManager] packetId=${packetId}: fec.blockProfile is required ("iso-ssbg1-v1"); configure the track from its catalog`
|
|
2658
2676
|
);
|
|
2659
2677
|
}
|
|
2660
|
-
|
|
2661
|
-
|
|
2662
|
-
|
|
2663
|
-
|
|
2664
|
-
|
|
2665
|
-
);
|
|
2666
|
-
}
|
|
2678
|
+
const ownInterleaveDepthMs = config.interleaveDepthMs ?? previousTrack?.interleaveDepthMs ?? 0;
|
|
2679
|
+
if (!Number.isSafeInteger(ownInterleaveDepthMs) || ownInterleaveDepthMs !== 0 && ownInterleaveDepthMs < MIN_INTERLEAVE_DEPTH_MS) {
|
|
2680
|
+
throw new Error(
|
|
2681
|
+
`[FECManager] packetId=${packetId}: fec.interleaveDepthMs must be absent, 0, or a finite value >= ${MIN_INTERLEAVE_DEPTH_MS} ms (got ${String(ownInterleaveDepthMs)})`
|
|
2682
|
+
);
|
|
2667
2683
|
}
|
|
2684
|
+
const hasCatalogTiming = _FECManager.#hasCatalogTiming({ interleaveDepthMs: ownInterleaveDepthMs });
|
|
2668
2685
|
const next = {
|
|
2669
2686
|
blockProfile,
|
|
2670
|
-
k
|
|
2671
|
-
symbolSize: config.symbolSize != null && config.symbolSize > 0 ? config.symbolSize :
|
|
2672
|
-
interleaveDepthMs:
|
|
2673
|
-
fps: config.fps ?? previous.fps,
|
|
2674
|
-
timeoutMs: config.timeoutMs ?? (timingChanged ? 0 : previous.timeoutMs),
|
|
2687
|
+
k,
|
|
2688
|
+
symbolSize: config.symbolSize != null && config.symbolSize > 0 ? config.symbolSize : previousTrack?.symbolSize ?? 0,
|
|
2689
|
+
interleaveDepthMs: ownInterleaveDepthMs,
|
|
2690
|
+
fps: config.fps ?? previous.fps ?? this.#fecFps,
|
|
2691
|
+
timeoutMs: config.timeoutMs ?? (timingChanged ? 0 : previous.timeoutMs ?? 0),
|
|
2675
2692
|
...(config.trackKind ?? previous.trackKind) === void 0 ? {} : { trackKind: config.trackKind ?? previous.trackKind },
|
|
2676
2693
|
...(config.codec ?? previous.codec) === void 0 ? {} : { codec: config.codec ?? previous.codec },
|
|
2677
2694
|
...(config.geometry ?? previous.geometry) === void 0 ? {} : { geometry: config.geometry ?? previous.geometry }
|
|
2678
2695
|
};
|
|
2679
|
-
|
|
2696
|
+
if (!hasCatalogTiming) {
|
|
2697
|
+
next.timeoutMs = config.timeoutMs !== void 0 && config.timeoutMs > 0 ? config.timeoutMs : previousTrack !== void 0 && previousTrack.interleaveDepthMs === 0 ? previousTrack.timeoutMs : LOCAL_FEC_TIMEOUT_MS;
|
|
2698
|
+
} else {
|
|
2699
|
+
next.timeoutMs = this.#computeTimeout(next);
|
|
2700
|
+
}
|
|
2680
2701
|
if (this.#multicastSymbolDelivery === "ordered-relay") {
|
|
2681
2702
|
this.#orderedHorizonBlocksFor(packetId, next);
|
|
2682
2703
|
}
|
|
2683
|
-
|
|
2704
|
+
const timingPolicyChanged = _FECManager.#hasCatalogTiming({ interleaveDepthMs: previous.interleaveDepthMs ?? 0 }) !== hasCatalogTiming;
|
|
2705
|
+
if (timingPolicyChanged && !hasCatalogTiming) {
|
|
2706
|
+
for (const key of [...this.#repairDeadlines.keys()]) {
|
|
2707
|
+
if (this.#belongsToPacket(key, packetId)) this.#clearRepairDeadline(key);
|
|
2708
|
+
}
|
|
2709
|
+
}
|
|
2710
|
+
if (previous.blockProfile !== void 0 && previous.k !== next.k) {
|
|
2684
2711
|
this.#dropIsoLocators(packetId);
|
|
2685
2712
|
}
|
|
2686
2713
|
this.#trackConfigs.set(packetId, next);
|
|
2687
2714
|
this.#refreshDeliveryGapWindow();
|
|
2688
|
-
let policyChanged = config.geometry !== void 0 && JSON.stringify(config.geometry) !== JSON.stringify(previous.geometry) || config.trackKind !== void 0 && config.trackKind !== previous.trackKind;
|
|
2715
|
+
let policyChanged = timingPolicyChanged || config.geometry !== void 0 && JSON.stringify(config.geometry) !== JSON.stringify(previous.geometry) || config.trackKind !== void 0 && config.trackKind !== previous.trackKind;
|
|
2689
2716
|
if (streamScopedGeometry !== void 0) {
|
|
2690
2717
|
const streamKey = this.#streamKey(packetId, catalogTrackName);
|
|
2691
2718
|
if (JSON.stringify(streamScopedGeometry) !== JSON.stringify(this.#streamGeometries.get(streamKey))) {
|
|
@@ -2700,20 +2727,14 @@ var FECManager = class _FECManager {
|
|
|
2700
2727
|
if (!this.#belongsToPacket(key, packetId)) continue;
|
|
2701
2728
|
const stream = this.#parseStreamKey(key);
|
|
2702
2729
|
const geometry = this.#geometryFor(stream.packetId, stream.catalogTrackName);
|
|
2703
|
-
if (JSON.stringify(geometry) === JSON.stringify(classifier.geometry)) continue;
|
|
2730
|
+
if (hasCatalogTiming && JSON.stringify(geometry) === JSON.stringify(classifier.geometry)) continue;
|
|
2704
2731
|
this.#classifiers.delete(key);
|
|
2732
|
+
if (!hasCatalogTiming) continue;
|
|
2705
2733
|
this.#applyClassifierChanges(stream.packetId, stream.catalogTrackName, classifier.retire());
|
|
2706
2734
|
}
|
|
2707
2735
|
}
|
|
2708
2736
|
return next;
|
|
2709
2737
|
}
|
|
2710
|
-
#normalizeSymbolData(packetId, data) {
|
|
2711
|
-
const symbolSize = this.symbolSizeForPacket(packetId);
|
|
2712
|
-
if (symbolSize <= 0 || data.byteLength === symbolSize) return data;
|
|
2713
|
-
const normalized = new Uint8Array(symbolSize);
|
|
2714
|
-
normalized.set(data.subarray(0, symbolSize));
|
|
2715
|
-
return normalized;
|
|
2716
|
-
}
|
|
2717
2738
|
#blockKeysFor(sbn, packetId, catalogTrackName) {
|
|
2718
2739
|
if (packetId != null && catalogTrackName !== void 0) {
|
|
2719
2740
|
return [this.#blockKey(sbn, packetId, catalogTrackName)];
|
|
@@ -2774,7 +2795,7 @@ var FECManager = class _FECManager {
|
|
|
2774
2795
|
...stats,
|
|
2775
2796
|
packetId,
|
|
2776
2797
|
blocksComplete: trackTotal,
|
|
2777
|
-
k: this.#
|
|
2798
|
+
k: this.#trackConfigs.get(packetId)?.k,
|
|
2778
2799
|
recoveryRate: trackAffected > 0 ? stats.blocksRecovered / trackAffected * 100 : 100,
|
|
2779
2800
|
packetLossRate: trackTotal > 0 ? trackAffected / trackTotal * 100 : 0
|
|
2780
2801
|
};
|
|
@@ -2786,7 +2807,7 @@ var FECManager = class _FECManager {
|
|
|
2786
2807
|
(block) => block.multicastSymbols > 0 && block.moqSymbols > 0
|
|
2787
2808
|
).length,
|
|
2788
2809
|
recentBlockPathCounts: Array.from(this.#blockPathCounts.values()).slice(-24),
|
|
2789
|
-
k: this.#
|
|
2810
|
+
k: this.#streamK,
|
|
2790
2811
|
// True recovery rate: of blocks that HAD loss, how many were recovered?
|
|
2791
2812
|
recoveryRate: affected > 0 ? s.blocksRecovered / affected * 100 : 100,
|
|
2792
2813
|
packetLossRate: total > 0 ? affected / total * 100 : 0,
|
|
@@ -3081,15 +3102,20 @@ var FECManager = class _FECManager {
|
|
|
3081
3102
|
}
|
|
3082
3103
|
}
|
|
3083
3104
|
}
|
|
3084
|
-
/**
|
|
3105
|
+
/**
|
|
3106
|
+
* Record a transport symbol and reject later duplicates of the same
|
|
3107
|
+
* packetId/SBN/ESI. `sbn` is the block's SS_Start. A packetId with no
|
|
3108
|
+
* configured block profile has no block geometry: its symbol is refused
|
|
3109
|
+
* (false) and no block state is created for it.
|
|
3110
|
+
*/
|
|
3085
3111
|
feedSymbolFromSource(packetId, sbn, esi, _data, source, catalogTrackName) {
|
|
3112
|
+
if (!this.#hasBlockProfile(packetId)) return false;
|
|
3086
3113
|
const blockKey = this.#blockKey(sbn, packetId, catalogTrackName);
|
|
3087
3114
|
const streamKey = this.#streamKey(packetId, catalogTrackName);
|
|
3088
3115
|
if (source === "multicast" && this.#multicastSymbolDelivery === "ordered-relay") {
|
|
3089
3116
|
this.#advanceOrderedFrontier(streamKey, sbn, packetId, catalogTrackName);
|
|
3090
3117
|
}
|
|
3091
|
-
const
|
|
3092
|
-
const sourceSymbols = this.#isIso(packetId) ? this.#isoLocatorFor(packetId, catalogTrackName).blockAt(sbn)?.length ?? k : k;
|
|
3118
|
+
const sourceSymbols = this.#isoLocatorFor(packetId, catalogTrackName).blockAt(sbn)?.length ?? this.#trackConfig(packetId).k;
|
|
3093
3119
|
const isSourceSymbol = esi >= 0 && esi < sourceSymbols;
|
|
3094
3120
|
const isStartupPartial = this.#startupPartialBlocks.has(blockKey);
|
|
3095
3121
|
const isReadyMulticastRepair = this.#startupRequireRepairReady && source === "multicast" && !isSourceSymbol && this.#startupRepairReadyPackets.has(streamKey);
|
|
@@ -3207,14 +3233,14 @@ var FECManager = class _FECManager {
|
|
|
3207
3233
|
for (const [key] of this.#activeBlocks) {
|
|
3208
3234
|
const { packetId, sbn } = this.#parseBlockKey(key);
|
|
3209
3235
|
const count = this.#blockSourceCounts.get(key) ?? 0;
|
|
3210
|
-
const k = this.#blockAuthoredLengths.get(key) ?? this
|
|
3236
|
+
const k = this.#blockAuthoredLengths.get(key) ?? this.#trackConfig(packetId).k;
|
|
3211
3237
|
blocks.push({ sbn, packetId, state: "collecting", sourceCount: count, k, progress: k > 0 ? Math.min(count / k, 1) : 0 });
|
|
3212
3238
|
}
|
|
3213
3239
|
const completed = [...this.#completedBlocks].slice(-16);
|
|
3214
3240
|
for (const key of completed) {
|
|
3215
3241
|
const { packetId, sbn } = this.#parseBlockKey(key);
|
|
3216
3242
|
if (!this.#activeBlocks.has(key)) {
|
|
3217
|
-
const k = this
|
|
3243
|
+
const k = this.#trackConfig(packetId).k;
|
|
3218
3244
|
blocks.push({ sbn, packetId, state: "complete", sourceCount: k, k, progress: 1 });
|
|
3219
3245
|
}
|
|
3220
3246
|
}
|
|
@@ -3225,25 +3251,23 @@ var FECManager = class _FECManager {
|
|
|
3225
3251
|
get wasmDecoder() {
|
|
3226
3252
|
return this.#fecDecoders.values().next().value?.client ?? null;
|
|
3227
3253
|
}
|
|
3228
|
-
|
|
3229
|
-
return this.#fecK;
|
|
3230
|
-
}
|
|
3254
|
+
/** A configured packetId's K; undefined for an unconfigured one (no FEC work). */
|
|
3231
3255
|
kForPacket(packetId) {
|
|
3232
|
-
return this.#
|
|
3256
|
+
return this.#trackConfigs.get(packetId)?.k;
|
|
3233
3257
|
}
|
|
3234
3258
|
/** FEC symbol size in bytes. */
|
|
3235
3259
|
get symbolSize() {
|
|
3236
3260
|
return this.#fecSymbolSize;
|
|
3237
3261
|
}
|
|
3238
3262
|
symbolSizeForPacket(packetId) {
|
|
3239
|
-
return this.#
|
|
3263
|
+
return this.#trackConfigs.get(packetId)?.symbolSize;
|
|
3240
3264
|
}
|
|
3241
3265
|
/** FEC block span in milliseconds (0 = no interleaving). */
|
|
3242
3266
|
get interleaveDepthMs() {
|
|
3243
3267
|
return this.#fecInterleaveDepthMs;
|
|
3244
3268
|
}
|
|
3245
3269
|
interleaveDepthMsForPacket(packetId) {
|
|
3246
|
-
return this.#
|
|
3270
|
+
return this.#trackConfigs.get(packetId)?.interleaveDepthMs;
|
|
3247
3271
|
}
|
|
3248
3272
|
/**
|
|
3249
3273
|
* Record one multicast MMTP packet arrival (every packet of the leg, FEC
|
|
@@ -3261,7 +3285,8 @@ var FECManager = class _FECManager {
|
|
|
3261
3285
|
* plus the larger of the catalog tolerance and the measured delivery gap.
|
|
3262
3286
|
*/
|
|
3263
3287
|
timeoutMsForPacket(packetId) {
|
|
3264
|
-
|
|
3288
|
+
const config = this.#trackConfigs.get(packetId);
|
|
3289
|
+
return config === void 0 ? void 0 : this.#multicastWaitMs(config);
|
|
3265
3290
|
}
|
|
3266
3291
|
/**
|
|
3267
3292
|
* Largest multicast delivery gap measured over the current window (ms), and
|
|
@@ -3317,27 +3342,31 @@ var FECManager = class _FECManager {
|
|
|
3317
3342
|
}
|
|
3318
3343
|
/** Resolved per-track D-28 repair request deadline in milliseconds. */
|
|
3319
3344
|
repairDeadlineMsForPacket(packetId) {
|
|
3320
|
-
|
|
3345
|
+
const config = this.#trackConfigs.get(packetId);
|
|
3346
|
+
return config === void 0 ? void 0 : this.#repairDeadlineMs(config);
|
|
3321
3347
|
}
|
|
3322
3348
|
/**
|
|
3323
3349
|
* One block lifetime: the longest a block stays open after its last symbol
|
|
3324
3350
|
* (inactivity fail bound plus D-28 repair grace).
|
|
3325
3351
|
*/
|
|
3326
3352
|
blockLifetimeMsForPacket(packetId) {
|
|
3327
|
-
|
|
3353
|
+
const config = this.#trackConfigs.get(packetId);
|
|
3354
|
+
return config === void 0 ? void 0 : this.#blockLifetimeMs(config);
|
|
3328
3355
|
}
|
|
3329
3356
|
/**
|
|
3330
3357
|
* How long a terminal (clean, recovered or failed) block stays authoritative,
|
|
3331
3358
|
* for late symbols and for the in-order release gate. It is also the gate's
|
|
3332
3359
|
* hold ceiling (MmtpRouter's holdLimitMs): a retired block reads back as
|
|
3333
3360
|
* untracked, which the gate counts as lost, so no frame may still be waiting
|
|
3334
|
-
* on a block that can retire.
|
|
3335
|
-
*
|
|
3336
|
-
*
|
|
3337
|
-
*
|
|
3361
|
+
* on a block that can retire. With a positive catalog span the window is one
|
|
3362
|
+
* interleave depth for a gap's last block to finish arriving, one block
|
|
3363
|
+
* lifetime to fail it, and one more for an owed Worker cleanup. With an
|
|
3364
|
+
* absent/zero span it is a receiver-local cleanup bound only; it creates no
|
|
3365
|
+
* catalog FEC deadline.
|
|
3338
3366
|
*/
|
|
3339
3367
|
terminalRetentionMsForPacket(packetId) {
|
|
3340
|
-
const config = this.#
|
|
3368
|
+
const config = this.#trackConfigs.get(packetId);
|
|
3369
|
+
if (config === void 0) return void 0;
|
|
3341
3370
|
return config.interleaveDepthMs + 2 * this.#blockLifetimeMs(config);
|
|
3342
3371
|
}
|
|
3343
3372
|
/** Remaining fetch budget for a specific repair-requested block. */
|
|
@@ -3348,7 +3377,8 @@ var FECManager = class _FECManager {
|
|
|
3348
3377
|
if (deadlineMs !== void 0) {
|
|
3349
3378
|
return Math.max(1, Math.ceil(deadlineMs - performance.now()));
|
|
3350
3379
|
}
|
|
3351
|
-
const trackConfig = this.#
|
|
3380
|
+
const trackConfig = this.#trackConfigs.get(packetId);
|
|
3381
|
+
if (trackConfig === void 0) return void 0;
|
|
3352
3382
|
return Math.max(1, trackConfig.timeoutMs - this.#repairDeadlineMs(trackConfig));
|
|
3353
3383
|
}
|
|
3354
3384
|
/** True once configureTrack/setFecDecoder/setFecWorker configured this packetId. */
|
|
@@ -3359,14 +3389,19 @@ var FECManager = class _FECManager {
|
|
|
3359
3389
|
blockProfileForPacket(packetId) {
|
|
3360
3390
|
return this.#trackConfigs.get(packetId)?.blockProfile;
|
|
3361
3391
|
}
|
|
3362
|
-
|
|
3363
|
-
|
|
3392
|
+
/**
|
|
3393
|
+
* Whether a packetId has a configured block geometry. iso-ssbg1-v1 is the
|
|
3394
|
+
* only profile, so every configured track has it; a packetId never
|
|
3395
|
+
* configured (or released) has none and gets no FEC block tracking.
|
|
3396
|
+
*/
|
|
3397
|
+
#hasBlockProfile(packetId) {
|
|
3398
|
+
return this.#trackConfigs.get(packetId)?.blockProfile !== void 0;
|
|
3364
3399
|
}
|
|
3365
3400
|
#isoLocatorFor(packetId, catalogTrackName) {
|
|
3366
3401
|
const streamKey = this.#streamKey(packetId, catalogTrackName);
|
|
3367
3402
|
let locator = this.#isoLocators.get(streamKey);
|
|
3368
3403
|
if (!locator) {
|
|
3369
|
-
locator = new IsoBlockLocator(this
|
|
3404
|
+
locator = new IsoBlockLocator(this.#trackConfig(packetId).k, this.#isoRetentionBlocks(packetId));
|
|
3370
3405
|
this.#isoLocators.set(streamKey, locator);
|
|
3371
3406
|
}
|
|
3372
3407
|
return locator;
|
|
@@ -3397,12 +3432,14 @@ var FECManager = class _FECManager {
|
|
|
3397
3432
|
*/
|
|
3398
3433
|
#isoRetentionBlocks(packetId) {
|
|
3399
3434
|
const config = this.#trackConfigs.get(packetId);
|
|
3400
|
-
if (config?.blockProfile
|
|
3435
|
+
if (config?.blockProfile === void 0) {
|
|
3401
3436
|
throw new Error(
|
|
3402
|
-
`[FECManager] packetId=${packetId} has no iso-ssbg1-v1 block
|
|
3437
|
+
`[FECManager] packetId=${packetId} has no configured iso-ssbg1-v1 block profile`
|
|
3403
3438
|
);
|
|
3404
3439
|
}
|
|
3405
|
-
|
|
3440
|
+
if (!_FECManager.#hasCatalogTiming(config)) return LOCAL_FEC_RETENTION_BLOCKS;
|
|
3441
|
+
const terminalRetentionMs = config.interleaveDepthMs + 2 * this.#blockLifetimeMs(config);
|
|
3442
|
+
const blocks = Math.ceil(terminalRetentionMs / config.interleaveDepthMs) + 1;
|
|
3406
3443
|
if (!Number.isSafeInteger(blocks) || blocks <= 0) {
|
|
3407
3444
|
throw new Error(`[FECManager] packetId=${packetId} iso-ssbg1-v1 retention of ${blocks} blocks is not derivable`);
|
|
3408
3445
|
}
|
|
@@ -3410,7 +3447,8 @@ var FECManager = class _FECManager {
|
|
|
3410
3447
|
}
|
|
3411
3448
|
/** Whether an iso-ssbg1-v1 protected packet fits one ssbg_mode1 symbol (len + 2 <= T). */
|
|
3412
3449
|
isoSourceFits(packetId, packetLength) {
|
|
3413
|
-
|
|
3450
|
+
const symbolSize = this.#trackConfigs.get(packetId)?.symbolSize;
|
|
3451
|
+
return symbolSize !== void 0 && ssbg1FitsSymbol(packetLength, symbolSize);
|
|
3414
3452
|
}
|
|
3415
3453
|
/**
|
|
3416
3454
|
* Locate an iso-ssbg1-v1 source by range membership in a block a repair
|
|
@@ -3471,11 +3509,11 @@ var FECManager = class _FECManager {
|
|
|
3471
3509
|
* that could hold them is pending or not yet stated by a repair, else the
|
|
3472
3510
|
* starts of the blocks that failed or vanished.
|
|
3473
3511
|
*
|
|
3474
|
-
* `untracked` reads
|
|
3475
|
-
*
|
|
3476
|
-
*
|
|
3477
|
-
*
|
|
3478
|
-
* an SS_Start
|
|
3512
|
+
* `untracked` reads as the release gate reads it (MmtpRouter
|
|
3513
|
+
* #createFecReleaseGate): a block holding either released frame's SS_ID
|
|
3514
|
+
* (`fromSsId` or `toSsId`) delivered that frame, so untracked there means it
|
|
3515
|
+
* resolved and retired; a block starting after the stream's frontier
|
|
3516
|
+
* (`highestSbn`, an SS_Start) may still be heard from; a block at or behind the
|
|
3479
3517
|
* frontier that no symbol of this manager ever reached vanished, and its
|
|
3480
3518
|
* media is lost.
|
|
3481
3519
|
*/
|
|
@@ -3543,6 +3581,7 @@ var FECManager = class _FECManager {
|
|
|
3543
3581
|
}
|
|
3544
3582
|
/** Current classifier mode of a stream. */
|
|
3545
3583
|
classifierModeFor(packetId, catalogTrackName) {
|
|
3584
|
+
if (!this.#hasCatalogTimingForPacket(packetId)) return { mode: "all-protected", reason: null };
|
|
3546
3585
|
const classifier = this.#classifierFor(packetId, catalogTrackName);
|
|
3547
3586
|
return { mode: classifier.mode, reason: classifier.reason };
|
|
3548
3587
|
}
|
|
@@ -3552,10 +3591,12 @@ var FECManager = class _FECManager {
|
|
|
3552
3591
|
* it does not. A stream with no classifier yet has shown nothing against it.
|
|
3553
3592
|
*/
|
|
3554
3593
|
mpuBoundsRapsFor(packetId, catalogTrackName) {
|
|
3594
|
+
if (!this.#hasCatalogTimingForPacket(packetId)) return false;
|
|
3555
3595
|
return this.#classifiers.get(this.#streamKey(packetId, catalogTrackName))?.mpuBoundsRaps ?? true;
|
|
3556
3596
|
}
|
|
3557
3597
|
/** A block's class now, or frozen at its resolution. */
|
|
3558
3598
|
blockClassFor(sbn, packetId, catalogTrackName) {
|
|
3599
|
+
if (!this.#hasCatalogTimingForPacket(packetId)) return "unknown";
|
|
3559
3600
|
const frozen = this.#resolvedBlockClasses.get(this.#blockKey(sbn, packetId, catalogTrackName));
|
|
3560
3601
|
if (frozen !== void 0) return frozen;
|
|
3561
3602
|
return this.#peekClass(sbn, packetId, catalogTrackName);
|
|
@@ -3577,7 +3618,7 @@ var FECManager = class _FECManager {
|
|
|
3577
3618
|
* invariant (consecutive MPUs exactly one keyframe interval apart).
|
|
3578
3619
|
*/
|
|
3579
3620
|
noteReleasedRap(packetId, catalogTrackName, mpu, presentationTime, mediaTimescale) {
|
|
3580
|
-
if (!this.#trackConfigs.has(packetId)) return;
|
|
3621
|
+
if (!this.#trackConfigs.has(packetId) || !this.#hasCatalogTimingForPacket(packetId)) return;
|
|
3581
3622
|
this.#applyClassifierChanges(
|
|
3582
3623
|
packetId,
|
|
3583
3624
|
catalogTrackName,
|
|
@@ -3593,27 +3634,34 @@ var FECManager = class _FECManager {
|
|
|
3593
3634
|
// -----------------------------------------------------------------------
|
|
3594
3635
|
/** Configure FEC parameters. */
|
|
3595
3636
|
configure(config) {
|
|
3596
|
-
if (config.
|
|
3637
|
+
if (config.interleaveDepthMs != null && (!Number.isSafeInteger(config.interleaveDepthMs) || config.interleaveDepthMs < 0)) {
|
|
3638
|
+
throw new Error(
|
|
3639
|
+
`[FECManager] interleaveDepthMs=${config.interleaveDepthMs} must be a non-negative safe integer in milliseconds`
|
|
3640
|
+
);
|
|
3641
|
+
}
|
|
3642
|
+
if (config.timeoutMs != null && (!Number.isFinite(config.timeoutMs) || config.timeoutMs < 0)) {
|
|
3643
|
+
throw new Error(
|
|
3644
|
+
`[FECManager] timeoutMs=${config.timeoutMs} must be a finite non-negative cleanup budget`
|
|
3645
|
+
);
|
|
3646
|
+
}
|
|
3647
|
+
if (config.k != null && (!Number.isSafeInteger(config.k) || config.k <= 0)) {
|
|
3648
|
+
throw new Error(`[FECManager] k=${config.k} must be a positive integer`);
|
|
3649
|
+
}
|
|
3650
|
+
if (config.k != null) this.#streamK = config.k;
|
|
3597
3651
|
if (config.symbolSize != null && config.symbolSize > 0) this.#fecSymbolSize = config.symbolSize;
|
|
3598
3652
|
if (config.interleaveDepthMs != null) this.#fecInterleaveDepthMs = config.interleaveDepthMs;
|
|
3599
3653
|
if (config.fps != null && Number.isFinite(config.fps) && config.fps > 0) {
|
|
3600
3654
|
this.#fecFps = config.fps;
|
|
3601
3655
|
}
|
|
3602
|
-
if (config.interleaveDepthMs != null && config.interleaveDepthMs > 0 && config.interleaveDepthMs < MIN_INTERLEAVE_DEPTH_MS) {
|
|
3603
|
-
console.warn(`[FECManager] interleaveDepthMs=${config.interleaveDepthMs} looks like frame count, not milliseconds (\xA75.1 requires ms). Expected range: ${MIN_INTERLEAVE_DEPTH_MS}-10000ms.`);
|
|
3604
|
-
}
|
|
3605
3656
|
if (config.timeoutMs != null && config.timeoutMs > 0) {
|
|
3606
3657
|
this.#fecTimeoutMs = config.timeoutMs;
|
|
3607
|
-
} else if (config.interleaveDepthMs
|
|
3608
|
-
this.#fecTimeoutMs =
|
|
3609
|
-
|
|
3610
|
-
|
|
3611
|
-
symbolSize: this.#fecSymbolSize,
|
|
3612
|
-
mediaUnitsPerSecond: config.fps
|
|
3613
|
-
});
|
|
3658
|
+
} else if (config.interleaveDepthMs === 0) {
|
|
3659
|
+
this.#fecTimeoutMs = LOCAL_FEC_TIMEOUT_MS;
|
|
3660
|
+
} else if (config.interleaveDepthMs != null && Number.isSafeInteger(config.interleaveDepthMs) && config.interleaveDepthMs >= MIN_INTERLEAVE_DEPTH_MS) {
|
|
3661
|
+
this.#fecTimeoutMs = fecSpanDeliveryWindowMs(config.interleaveDepthMs);
|
|
3614
3662
|
}
|
|
3615
3663
|
for (const [packetId, track] of this.#trackConfigs) {
|
|
3616
|
-
if (track.timeoutMs < this.#fecTimeoutMs) {
|
|
3664
|
+
if (_FECManager.#hasCatalogTiming(track) && track.timeoutMs < this.#fecTimeoutMs) {
|
|
3617
3665
|
this.#trackConfigs.set(packetId, { ...track, timeoutMs: this.#fecTimeoutMs });
|
|
3618
3666
|
}
|
|
3619
3667
|
}
|
|
@@ -3672,7 +3720,7 @@ var FECManager = class _FECManager {
|
|
|
3672
3720
|
);
|
|
3673
3721
|
}
|
|
3674
3722
|
if (config) this.#upsertTrackConfig(packetId, config, catalogTrackName);
|
|
3675
|
-
const workerK = this.#
|
|
3723
|
+
const workerK = this.#trackConfig(packetId).k;
|
|
3676
3724
|
this.#workerClients.set(streamKey, client);
|
|
3677
3725
|
this.#releasedWorkerClients.delete(client);
|
|
3678
3726
|
client.onFrame((data, meta) => {
|
|
@@ -3852,7 +3900,7 @@ var FECManager = class _FECManager {
|
|
|
3852
3900
|
return;
|
|
3853
3901
|
}
|
|
3854
3902
|
this.#settleOwedCleanup(key, "cleanup-ack-timeout");
|
|
3855
|
-
}, Math.ceil(this.#liveBlockLifetimeMs(this.#
|
|
3903
|
+
}, Math.ceil(this.#liveBlockLifetimeMs(this.#trackConfig(packetId))))
|
|
3856
3904
|
};
|
|
3857
3905
|
this.#pendingWorkerCleanupBounds.set(key, bound);
|
|
3858
3906
|
}
|
|
@@ -3893,16 +3941,11 @@ var FECManager = class _FECManager {
|
|
|
3893
3941
|
if (this.#fpsDetected || fps <= 0) return;
|
|
3894
3942
|
this.#fpsDetected = true;
|
|
3895
3943
|
this.#fecFps = fps;
|
|
3896
|
-
if (this.#fecInterleaveDepthMs >= MIN_INTERLEAVE_DEPTH_MS) {
|
|
3897
|
-
this.#fecTimeoutMs =
|
|
3898
|
-
interleaveDepthMs: this.#fecInterleaveDepthMs,
|
|
3899
|
-
sourceSymbols: this.#fecK,
|
|
3900
|
-
symbolSize: this.#fecSymbolSize,
|
|
3901
|
-
mediaUnitsPerSecond: fps
|
|
3902
|
-
});
|
|
3944
|
+
if (Number.isSafeInteger(this.#fecInterleaveDepthMs) && this.#fecInterleaveDepthMs >= MIN_INTERLEAVE_DEPTH_MS) {
|
|
3945
|
+
this.#fecTimeoutMs = fecSpanDeliveryWindowMs(this.#fecInterleaveDepthMs);
|
|
3903
3946
|
}
|
|
3904
3947
|
for (const [packetId, config] of this.#trackConfigs) {
|
|
3905
|
-
if (config.fps !== void 0 || config.interleaveDepthMs < MIN_INTERLEAVE_DEPTH_MS) continue;
|
|
3948
|
+
if (config.fps !== void 0 || !Number.isSafeInteger(config.interleaveDepthMs) || config.interleaveDepthMs < MIN_INTERLEAVE_DEPTH_MS) continue;
|
|
3906
3949
|
this.#trackConfigs.set(packetId, {
|
|
3907
3950
|
...config,
|
|
3908
3951
|
fps,
|
|
@@ -3934,35 +3977,28 @@ var FECManager = class _FECManager {
|
|
|
3934
3977
|
feedSourceSymbol(packetId, ssId, symbolData, alta, catalogTrackName) {
|
|
3935
3978
|
this.#fecStats.sourcePackets++;
|
|
3936
3979
|
this.#statsForPacket(packetId).sourcePackets++;
|
|
3937
|
-
|
|
3938
|
-
|
|
3939
|
-
|
|
3940
|
-
|
|
3941
|
-
|
|
3942
|
-
|
|
3943
|
-
|
|
3944
|
-
`[FECManager] iso-ssbg1-v1 source SS_ID=${ssId} (packetId=${packetId}) has no stated block; hold it until a repair places it (placeIsoRepair)`
|
|
3945
|
-
);
|
|
3946
|
-
}
|
|
3947
|
-
locator.observe(ssId);
|
|
3948
|
-
if (!ssbg1FitsSymbol(symbolData.byteLength, this.symbolSizeForPacket(packetId))) {
|
|
3949
|
-
this.#isoSourceOversizeCount++;
|
|
3950
|
-
return null;
|
|
3951
|
-
}
|
|
3952
|
-
sbn = located.block.start;
|
|
3953
|
-
fecData = symbolData;
|
|
3954
|
-
} else {
|
|
3955
|
-
fecData = this.#normalizeSymbolData(packetId, symbolData);
|
|
3956
|
-
sbn = Math.floor(ssId / this.kForPacket(packetId));
|
|
3980
|
+
if (!this.#hasBlockProfile(packetId)) return null;
|
|
3981
|
+
const locator = this.#isoLocatorFor(packetId, catalogTrackName);
|
|
3982
|
+
const located = locator.locate(ssId);
|
|
3983
|
+
if (located === null) {
|
|
3984
|
+
throw new Error(
|
|
3985
|
+
`[FECManager] iso-ssbg1-v1 source SS_ID=${ssId} (packetId=${packetId}) has no stated block; hold it until a repair places it (placeIsoRepair)`
|
|
3986
|
+
);
|
|
3957
3987
|
}
|
|
3988
|
+
locator.observe(ssId);
|
|
3989
|
+
if (!ssbg1FitsSymbol(symbolData.byteLength, this.#trackConfig(packetId).symbolSize)) {
|
|
3990
|
+
this.#isoSourceOversizeCount++;
|
|
3991
|
+
return null;
|
|
3992
|
+
}
|
|
3993
|
+
const sbn = located.block.start;
|
|
3958
3994
|
const blockKey = this.#blockKey(sbn, packetId, catalogTrackName);
|
|
3959
3995
|
const syncBlockOwner = this.#syncDecoderBlockOwners.get(blockKey);
|
|
3960
3996
|
const workerClient = this.#routableWorkerForPacket(packetId, catalogTrackName);
|
|
3961
3997
|
if (workerClient && !syncBlockOwner) {
|
|
3962
3998
|
if (!this.#claimDecoderBlock(workerClient, sbn, catalogTrackName)) return null;
|
|
3963
|
-
const buf =
|
|
3964
|
-
|
|
3965
|
-
|
|
3999
|
+
const buf = symbolData.buffer.slice(
|
|
4000
|
+
symbolData.byteOffset,
|
|
4001
|
+
symbolData.byteOffset + symbolData.byteLength
|
|
3966
4002
|
);
|
|
3967
4003
|
if (alta) {
|
|
3968
4004
|
const authBuf = alta.auth.buffer.slice(
|
|
@@ -3985,7 +4021,7 @@ var FECManager = class _FECManager {
|
|
|
3985
4021
|
this.#syncDecoderBlockOwners.set(blockKey, client);
|
|
3986
4022
|
let recovered = null;
|
|
3987
4023
|
try {
|
|
3988
|
-
recovered = client.add_source(ssId,
|
|
4024
|
+
recovered = client.add_source(ssId, symbolData, performance.now());
|
|
3989
4025
|
} catch {
|
|
3990
4026
|
}
|
|
3991
4027
|
this.#retireIsoUnplaced(client, packetId, catalogTrackName);
|
|
@@ -4004,7 +4040,8 @@ var FECManager = class _FECManager {
|
|
|
4004
4040
|
feedRepairSymbol(packetId, ssStart, ssbLength, rsId, repairData, catalogTrackName) {
|
|
4005
4041
|
this.#fecStats.repairPackets++;
|
|
4006
4042
|
this.#statsForPacket(packetId).repairPackets++;
|
|
4007
|
-
|
|
4043
|
+
if (!this.#hasBlockProfile(packetId)) return null;
|
|
4044
|
+
const symbolSize = this.#trackConfig(packetId).symbolSize;
|
|
4008
4045
|
if (symbolSize > 0 && repairData.byteLength !== symbolSize) {
|
|
4009
4046
|
this.#repairSymbolSizeMismatchCount++;
|
|
4010
4047
|
if (this.#repairSymbolSizeMismatchCount <= 5 || this.#repairSymbolSizeMismatchCount % 100 === 0) {
|
|
@@ -4015,18 +4052,12 @@ var FECManager = class _FECManager {
|
|
|
4015
4052
|
return null;
|
|
4016
4053
|
}
|
|
4017
4054
|
const fecData = repairData;
|
|
4018
|
-
const k = this
|
|
4019
|
-
|
|
4020
|
-
if (
|
|
4021
|
-
|
|
4022
|
-
if (ssbLength <= 0 || ssbLength > k || block === void 0 || block.span || block.length !== ssbLength) {
|
|
4023
|
-
return null;
|
|
4024
|
-
}
|
|
4025
|
-
sbn = ssStart;
|
|
4026
|
-
} else {
|
|
4027
|
-
if (ssbLength <= 0 || ssbLength > k || ssStart % k !== 0) return null;
|
|
4028
|
-
sbn = Math.floor(ssStart / k);
|
|
4055
|
+
const k = this.#trackConfig(packetId).k;
|
|
4056
|
+
const block = this.#isoLocatorFor(packetId, catalogTrackName).blockAt(ssStart);
|
|
4057
|
+
if (ssbLength <= 0 || ssbLength > k || block === void 0 || block.span || block.length !== ssbLength) {
|
|
4058
|
+
return null;
|
|
4029
4059
|
}
|
|
4060
|
+
const sbn = ssStart;
|
|
4030
4061
|
this.#noteSbnSeen(sbn, packetId, catalogTrackName, "repair");
|
|
4031
4062
|
this.noteBlockActivity(sbn, packetId, catalogTrackName);
|
|
4032
4063
|
const blockKey = this.#blockKey(sbn, packetId, catalogTrackName);
|
|
@@ -4064,8 +4095,9 @@ var FECManager = class _FECManager {
|
|
|
4064
4095
|
* Record a block's authored SSB_length from a repair symbol. Returns false,
|
|
4065
4096
|
* leaving the block unchanged, when the symbol contradicts the block: a
|
|
4066
4097
|
* different SSB_length than an earlier repair symbol declared is dropped,
|
|
4067
|
-
* and an SSB_length that places an already-received source ESI
|
|
4068
|
-
*
|
|
4098
|
+
* and an SSB_length that places an already-received source ESI at or past
|
|
4099
|
+
* the block's end fails the block, since its geometry can no longer be
|
|
4100
|
+
* trusted.
|
|
4069
4101
|
*/
|
|
4070
4102
|
#noteAuthoredLength(blockKey, sbn, packetId, ssbLength, catalogTrackName) {
|
|
4071
4103
|
const known = this.#blockAuthoredLengths.get(blockKey);
|
|
@@ -4132,7 +4164,7 @@ var FECManager = class _FECManager {
|
|
|
4132
4164
|
* a sweep tied to expiry alone would never run there.
|
|
4133
4165
|
*/
|
|
4134
4166
|
#retireIsoUnplaced(decoder, packetId, catalogTrackName) {
|
|
4135
|
-
if (!this.#
|
|
4167
|
+
if (!this.#hasBlockProfile(packetId)) return;
|
|
4136
4168
|
const newest = this.#isoLocators.get(this.#streamKey(packetId, catalogTrackName))?.newest;
|
|
4137
4169
|
if (newest === void 0) return;
|
|
4138
4170
|
try {
|
|
@@ -4177,11 +4209,13 @@ var FECManager = class _FECManager {
|
|
|
4177
4209
|
this.#failBlock(sbn, packetId, catalogTrackName);
|
|
4178
4210
|
return;
|
|
4179
4211
|
}
|
|
4180
|
-
|
|
4181
|
-
|
|
4182
|
-
|
|
4183
|
-
|
|
4184
|
-
|
|
4212
|
+
if (_FECManager.#hasCatalogTiming(trackConfig)) {
|
|
4213
|
+
this.#applyClassifierChanges(
|
|
4214
|
+
packetId,
|
|
4215
|
+
catalogTrackName,
|
|
4216
|
+
this.#classifierFor(packetId, catalogTrackName).observe(sbn, evidence)
|
|
4217
|
+
);
|
|
4218
|
+
}
|
|
4185
4219
|
const count = (this.#blockSourceCounts.get(key) ?? 0) + 1;
|
|
4186
4220
|
this.#blockSourceCounts.set(key, count);
|
|
4187
4221
|
if (count === 1) this.#indexBlockKey(key, sbn, packetId);
|
|
@@ -4225,6 +4259,7 @@ var FECManager = class _FECManager {
|
|
|
4225
4259
|
}
|
|
4226
4260
|
/** Refresh deadlines after accepted block activity. */
|
|
4227
4261
|
noteBlockActivity(sbn, packetId, catalogTrackName, sourceProgress = false) {
|
|
4262
|
+
if (!this.#trackConfigs.has(packetId)) return;
|
|
4228
4263
|
const key = this.#blockKey(sbn, packetId, catalogTrackName);
|
|
4229
4264
|
if (this.#completedBlocks.has(key)) return;
|
|
4230
4265
|
if (this.#pendingWorkerCleanups.has(key)) return;
|
|
@@ -4234,13 +4269,13 @@ var FECManager = class _FECManager {
|
|
|
4234
4269
|
this.#holdForOrderedSettlement(key, sbn, packetId, catalogTrackName);
|
|
4235
4270
|
return;
|
|
4236
4271
|
}
|
|
4237
|
-
const trackConfig = this.#
|
|
4272
|
+
const trackConfig = this.#trackConfig(packetId);
|
|
4238
4273
|
const timeoutMs = this.#multicastWaitMs(trackConfig);
|
|
4239
4274
|
if (!(timeoutMs > 0)) {
|
|
4240
4275
|
throw new Error(`[FECManager] packetId=${packetId} has no positive fail bound (timeoutMs=${timeoutMs})`);
|
|
4241
4276
|
}
|
|
4242
4277
|
const repairDeadline = this.#repairDeadlines.get(key);
|
|
4243
|
-
if (this.onRepairNeeded && !repairRequested && (repairDeadline === void 0 || sourceProgress)) {
|
|
4278
|
+
if (this.#hasCatalogTimingForPacket(packetId) && this.onRepairNeeded && !repairRequested && (repairDeadline === void 0 || sourceProgress)) {
|
|
4244
4279
|
this.#armRepairDeadline(
|
|
4245
4280
|
key,
|
|
4246
4281
|
sbn,
|
|
@@ -4310,6 +4345,7 @@ var FECManager = class _FECManager {
|
|
|
4310
4345
|
}
|
|
4311
4346
|
/** Mark a block as completed externally (e.g., after routing recovered data). */
|
|
4312
4347
|
markBlockCompleted(sbn, packetId, catalogTrackName) {
|
|
4348
|
+
if (packetId != null && !this.#trackConfigs.has(packetId)) return;
|
|
4313
4349
|
for (const key of this.#blockKeysFor(sbn, packetId, catalogTrackName)) {
|
|
4314
4350
|
this.#resolveCompleted(key);
|
|
4315
4351
|
}
|
|
@@ -8526,7 +8562,6 @@ var CmafBlockAssembler = class {
|
|
|
8526
8562
|
failedBlocks: 0
|
|
8527
8563
|
};
|
|
8528
8564
|
this.#config = {
|
|
8529
|
-
k: config.k,
|
|
8530
8565
|
timescale: config.timescale ?? 1e6,
|
|
8531
8566
|
trackId: config.trackId ?? 1,
|
|
8532
8567
|
trackType: config.trackType,
|
|
@@ -10712,6 +10747,21 @@ var FecReleaseSequencer = class {
|
|
|
10712
10747
|
this.#streams.delete(key);
|
|
10713
10748
|
}
|
|
10714
10749
|
}
|
|
10750
|
+
/**
|
|
10751
|
+
* The packet's FEC ended, so no block will resolve a held gap: release its
|
|
10752
|
+
* frames in order, each gap stalled as a discontinuity, and forget its
|
|
10753
|
+
* streams, so FEC configured on it again starts from a fresh one.
|
|
10754
|
+
*/
|
|
10755
|
+
endPacket(packetId) {
|
|
10756
|
+
for (const [key, stream] of this.#streams) {
|
|
10757
|
+
if (stream.packetId !== packetId) continue;
|
|
10758
|
+
while (stream.buf.length > 0 && stream.last !== null) {
|
|
10759
|
+
stream.discontinuity = true;
|
|
10760
|
+
this.#drain(stream);
|
|
10761
|
+
}
|
|
10762
|
+
this.#streams.delete(key);
|
|
10763
|
+
}
|
|
10764
|
+
}
|
|
10715
10765
|
discardPacket(packetId) {
|
|
10716
10766
|
for (const [key, stream] of this.#streams) {
|
|
10717
10767
|
if (stream.packetId !== packetId) continue;
|
|
@@ -11094,6 +11144,13 @@ var MmtpContainerParser = class _MmtpContainerParser {
|
|
|
11094
11144
|
markFecDiscontinuity(packetId, catalogTrackName) {
|
|
11095
11145
|
this.#fecSequencer?.markDiscontinuity(this.#streamKey(packetId, catalogTrackName));
|
|
11096
11146
|
}
|
|
11147
|
+
/**
|
|
11148
|
+
* The FEC of a packet ID ended: release the frames the FEC release gate
|
|
11149
|
+
* holds for it in order, each gap stalled, and forget its gated streams.
|
|
11150
|
+
*/
|
|
11151
|
+
endFecRelease(packetId) {
|
|
11152
|
+
this.#fecSequencer?.endPacket(packetId);
|
|
11153
|
+
}
|
|
11097
11154
|
/**
|
|
11098
11155
|
* MPU and data-unit identity of one MMTP packet, parsed in place (no copy,
|
|
11099
11156
|
* no reassembly). Null for non-MPU packets and packets that do not parse.
|
|
@@ -12426,6 +12483,8 @@ var MmtpRouter = class {
|
|
|
12426
12483
|
*/
|
|
12427
12484
|
#onPublisherRestart;
|
|
12428
12485
|
#publisherRestarts = 0;
|
|
12486
|
+
/** Inits delivered with no onTrackInit handler for their packetId (MOQT-01). */
|
|
12487
|
+
#unmappedInitDeliveries = 0;
|
|
12429
12488
|
// FEC Payload IDs left unread because their (owner) packetId had no
|
|
12430
12489
|
// configured fec.blockProfile (see #noteUnreadableFecPayloadId).
|
|
12431
12490
|
#fecPayloadIdsWithoutProfile = 0;
|
|
@@ -12622,15 +12681,24 @@ var MmtpRouter = class {
|
|
|
12622
12681
|
return NO_FEC_GEOMETRY_HORIZON;
|
|
12623
12682
|
}
|
|
12624
12683
|
const blockSpanMs = fec.interleaveDepthMsForPacket(packetId);
|
|
12684
|
+
const blockLifetimeMs = fec.blockLifetimeMsForPacket(packetId);
|
|
12685
|
+
if (blockSpanMs === void 0 || blockLifetimeMs === void 0) return NO_FEC_GEOMETRY_HORIZON;
|
|
12625
12686
|
if (blockSpanMs === 0) return NO_BLOCK_SPAN_HORIZON;
|
|
12626
12687
|
return {
|
|
12627
12688
|
kind: "fec-horizon",
|
|
12628
|
-
retentionMs: duplicateRetentionMs({
|
|
12629
|
-
blockSpanMs,
|
|
12630
|
-
blockLifetimeMs: fec.blockLifetimeMsForPacket(packetId)
|
|
12631
|
-
})
|
|
12689
|
+
retentionMs: duplicateRetentionMs({ blockSpanMs, blockLifetimeMs })
|
|
12632
12690
|
};
|
|
12633
12691
|
}
|
|
12692
|
+
/**
|
|
12693
|
+
* Init segments delivered for a packetId with no `onTrackInit` handler
|
|
12694
|
+
* since construction, whether the deprecated aggregate `onInitSegment`
|
|
12695
|
+
* callback then received them or nothing did. A supported deployment reads
|
|
12696
|
+
* 0 here; a non-zero value is the evidence that blocks removing the
|
|
12697
|
+
* aggregate callback (MOQT-01, BLO-21181).
|
|
12698
|
+
*/
|
|
12699
|
+
get unmappedInitDeliveryCount() {
|
|
12700
|
+
return this.#unmappedInitDeliveries;
|
|
12701
|
+
}
|
|
12634
12702
|
/** Publisher restarts detected on the multicast leg since construction. */
|
|
12635
12703
|
get publisherRestartCount() {
|
|
12636
12704
|
return this.#publisherRestarts;
|
|
@@ -12718,7 +12786,7 @@ var MmtpRouter = class {
|
|
|
12718
12786
|
/** Update MFU reassembly timeout. Source and FEC-recovery paths share the
|
|
12719
12787
|
* parser's single reassembler (BLO-8151), so one update covers both. */
|
|
12720
12788
|
setMfuTimeoutMs(ms) {
|
|
12721
|
-
if (ms <= 0) throw new Error("[MmtpRouter] mfuTimeoutMs must be > 0");
|
|
12789
|
+
if (ms !== null && ms <= 0) throw new Error("[MmtpRouter] mfuTimeoutMs must be > 0");
|
|
12722
12790
|
this.#parser.setMfuTimeoutMs(ms);
|
|
12723
12791
|
}
|
|
12724
12792
|
/** Update parser frame reordering for unordered multicast delivery.
|
|
@@ -12788,7 +12856,15 @@ var MmtpRouter = class {
|
|
|
12788
12856
|
onTrackPacket(packetId, cb) {
|
|
12789
12857
|
this.#trackPacketCallbacks.set(packetId, cb);
|
|
12790
12858
|
}
|
|
12791
|
-
/**
|
|
12859
|
+
/**
|
|
12860
|
+
* Register an init segment callback (ftyp+moov from MMTP MPU metadata).
|
|
12861
|
+
* Receives an init only for a packetId with no `onTrackInit` handler.
|
|
12862
|
+
*
|
|
12863
|
+
* @deprecated Use onTrackInit(packetId, cb). Still delivered as a fallback;
|
|
12864
|
+
* removed in the 1.0.0 major, no earlier than one minor release after this
|
|
12865
|
+
* deprecation reaches the `latest` dist-tag (BLO-21181). Count fallback
|
|
12866
|
+
* deliveries with `unmappedInitDeliveryCount`.
|
|
12867
|
+
*/
|
|
12792
12868
|
onInitSegment(cb) {
|
|
12793
12869
|
this.#onInitSegment = cb;
|
|
12794
12870
|
}
|
|
@@ -12802,17 +12878,16 @@ var MmtpRouter = class {
|
|
|
12802
12878
|
* When active, source frames are buffered per FEC block and emitted as
|
|
12803
12879
|
* multi-sample CMAF segments instead of per-frame callbacks. */
|
|
12804
12880
|
enableMseAssembler(packetId, config, catalogTrackName) {
|
|
12805
|
-
const k = this.#kForPacket(packetId);
|
|
12806
12881
|
const assemblerKey = this.#mseAssemblerKey(packetId, catalogTrackName);
|
|
12807
12882
|
const previous = this.#mseAssemblers.get(assemblerKey);
|
|
12808
12883
|
if (previous) {
|
|
12809
12884
|
previous.flush();
|
|
12810
12885
|
previous.dispose();
|
|
12811
12886
|
}
|
|
12812
|
-
const assembler = new CmafBlockAssembler(
|
|
12887
|
+
const assembler = new CmafBlockAssembler(config);
|
|
12813
12888
|
this.#mseAssemblers.set(assemblerKey, assembler);
|
|
12814
12889
|
console.log(
|
|
12815
|
-
`[MmtpRouter] MSE assembler enabled for packetId=${packetId}${catalogTrackName === void 0 ? "" : ` track=${catalogTrackName}`} (
|
|
12890
|
+
`[MmtpRouter] MSE assembler enabled for packetId=${packetId}${catalogTrackName === void 0 ? "" : ` track=${catalogTrackName}`} (${config.trackType})`
|
|
12816
12891
|
);
|
|
12817
12892
|
return assembler;
|
|
12818
12893
|
}
|
|
@@ -12921,6 +12996,21 @@ var MmtpRouter = class {
|
|
|
12921
12996
|
}
|
|
12922
12997
|
this.#fecMappersByPacketId.set(packetId, mapper);
|
|
12923
12998
|
}
|
|
12999
|
+
/**
|
|
13000
|
+
* Release the router side of a packetId whose FEC is ending: drop its block
|
|
13001
|
+
* mapper and end its FEC release gating. Frames the gate holds are released
|
|
13002
|
+
* in order, each gap stalled (no block will resolve it), not stranded. Call
|
|
13003
|
+
* before FECManager.releasePacket, while the gate can still read its blocks.
|
|
13004
|
+
*/
|
|
13005
|
+
releaseFecPacket(packetId) {
|
|
13006
|
+
this.#fecMappersByPacketId.delete(packetId);
|
|
13007
|
+
this.#withDeferredFecPokes(() => {
|
|
13008
|
+
this.#parser.endFecRelease(packetId);
|
|
13009
|
+
for (const [key, poke] of this.#pendingFecPokes) {
|
|
13010
|
+
if (poke.packetId === packetId) this.#pendingFecPokes.delete(key);
|
|
13011
|
+
}
|
|
13012
|
+
});
|
|
13013
|
+
}
|
|
12924
13014
|
registerManifestTrack(context) {
|
|
12925
13015
|
if (!this.#manifestVerifier) throw new Error("manifest verifier is not configured");
|
|
12926
13016
|
this.#manifestVerifier.registerTrack(context);
|
|
@@ -13040,87 +13130,21 @@ var MmtpRouter = class {
|
|
|
13040
13130
|
this.#noteUnreadableFecPayloadId(ownerPacketId, "repair");
|
|
13041
13131
|
return;
|
|
13042
13132
|
}
|
|
13043
|
-
|
|
13044
|
-
this.#handleIsoRepair(ownerPacketId, catalogTrackName, parsed, "moq");
|
|
13045
|
-
return;
|
|
13046
|
-
}
|
|
13047
|
-
const repairSbn = this.#deriveRepairSbn(parsed.ssStart, parsed.ssbLength, ownerPacketId);
|
|
13048
|
-
if (repairSbn === null) return;
|
|
13049
|
-
const repairEsi = this.#kForPacket(ownerPacketId) + parsed.rsId;
|
|
13050
|
-
if (!this.#feedFecTransportSymbol(
|
|
13051
|
-
ownerPacketId,
|
|
13052
|
-
repairSbn,
|
|
13053
|
-
repairEsi,
|
|
13054
|
-
parsed.symbolData,
|
|
13055
|
-
"moq",
|
|
13056
|
-
catalogTrackName
|
|
13057
|
-
)) return;
|
|
13058
|
-
const recovered = catalogTrackName === void 0 ? this.#fecManager.feedRepairSymbol(
|
|
13059
|
-
ownerPacketId,
|
|
13060
|
-
parsed.ssStart,
|
|
13061
|
-
parsed.ssbLength,
|
|
13062
|
-
parsed.rsId,
|
|
13063
|
-
parsed.symbolData
|
|
13064
|
-
) : this.#fecManager.feedRepairSymbol(
|
|
13065
|
-
ownerPacketId,
|
|
13066
|
-
parsed.ssStart,
|
|
13067
|
-
parsed.ssbLength,
|
|
13068
|
-
parsed.rsId,
|
|
13069
|
-
parsed.symbolData,
|
|
13070
|
-
catalogTrackName
|
|
13071
|
-
);
|
|
13072
|
-
if (recovered) {
|
|
13073
|
-
const alreadyCompleted = catalogTrackName === void 0 ? this.#fecManager.isBlockCompleted(repairSbn, ownerPacketId) : this.#fecManager.isBlockCompleted(repairSbn, ownerPacketId, catalogTrackName);
|
|
13074
|
-
if (catalogTrackName === void 0) {
|
|
13075
|
-
this.#fecManager.cancelBlockTimeout(repairSbn, ownerPacketId);
|
|
13076
|
-
} else {
|
|
13077
|
-
this.#fecManager.cancelBlockTimeout(repairSbn, ownerPacketId, catalogTrackName);
|
|
13078
|
-
}
|
|
13079
|
-
if (!alreadyCompleted) {
|
|
13080
|
-
this.#fecManager.countRecovery(ownerPacketId, repairSbn, catalogTrackName);
|
|
13081
|
-
if (catalogTrackName === void 0) {
|
|
13082
|
-
this.#fecManager.markBlockCompleted(repairSbn, ownerPacketId);
|
|
13083
|
-
} else {
|
|
13084
|
-
this.#fecManager.markBlockCompleted(repairSbn, ownerPacketId, catalogTrackName);
|
|
13085
|
-
}
|
|
13086
|
-
this.#routeRecoveredBlock(
|
|
13087
|
-
repairSbn,
|
|
13088
|
-
recovered,
|
|
13089
|
-
ownerPacketId,
|
|
13090
|
-
catalogTrackName
|
|
13091
|
-
);
|
|
13092
|
-
}
|
|
13093
|
-
}
|
|
13133
|
+
this.#handleIsoRepair(ownerPacketId, catalogTrackName, parsed, "moq");
|
|
13094
13134
|
}
|
|
13095
13135
|
}
|
|
13096
|
-
// ──
|
|
13097
|
-
#kForPacket(packetId) {
|
|
13098
|
-
if (packetId != null)
|
|
13099
|
-
return this.#fecManager?.kForPacket?.(packetId) ?? this.#fecMappersByPacketId.get(packetId)?.k ?? this.#fecManager?.k ?? 4;
|
|
13100
|
-
return this.#defaultFecMapper?.k ?? this.#fecManager?.k ?? 4;
|
|
13101
|
-
}
|
|
13136
|
+
// ── Packet FEC geometry and block profile ───────────────────────
|
|
13102
13137
|
#symbolSizeForPacket(packetId) {
|
|
13103
|
-
if (packetId != null) return this.#fecManager?.symbolSizeForPacket?.(packetId) ??
|
|
13138
|
+
if (packetId != null) return this.#fecManager?.symbolSizeForPacket?.(packetId) ?? 0;
|
|
13104
13139
|
return this.#fecManager?.symbolSize ?? 0;
|
|
13105
13140
|
}
|
|
13106
|
-
/** Derive SBN/ESI from SS_ID using packet-scoped FEC geometry. */
|
|
13107
|
-
#deriveSbnEsi(ssId, packetId) {
|
|
13108
|
-
const mapper = packetId == null ? this.#defaultFecMapper : this.#fecMappersByPacketId.get(packetId) ?? null;
|
|
13109
|
-
if (mapper) return mapper.fromSsId(ssId);
|
|
13110
|
-
const k = this.#kForPacket(packetId);
|
|
13111
|
-
return { sbn: Math.floor(ssId / k), esi: ssId % k };
|
|
13112
|
-
}
|
|
13113
|
-
#deriveRepairSbn(ssStart, ssbLength, packetId) {
|
|
13114
|
-
const k = this.#kForPacket(packetId);
|
|
13115
|
-
if (ssbLength <= 0 || ssbLength > k || ssStart % k !== 0) return null;
|
|
13116
|
-
return Math.floor(ssStart / k);
|
|
13117
|
-
}
|
|
13118
13141
|
/**
|
|
13119
13142
|
* The configured block profile of a packetId, or undefined while it is
|
|
13120
13143
|
* unconfigured. With an FEC manager its per-packet configuration is the
|
|
13121
13144
|
* only source. A router without one still recovers symbols through its
|
|
13122
13145
|
* registered mapper, built with the catalog's explicit profile: the
|
|
13123
|
-
* packet's own mapper, else the default one (
|
|
13146
|
+
* packet's own mapper, else the default one. A NullBlockMapper (a stream
|
|
13147
|
+
* without an FEC instance) states none.
|
|
13124
13148
|
*/
|
|
13125
13149
|
#configuredBlockProfile(packetId) {
|
|
13126
13150
|
if (this.#fecManager) return this.#fecManager.blockProfileForPacket(packetId);
|
|
@@ -13128,9 +13152,10 @@ var MmtpRouter = class {
|
|
|
13128
13152
|
return mapper?.blockProfile ?? void 0;
|
|
13129
13153
|
}
|
|
13130
13154
|
/**
|
|
13131
|
-
* The block profile an FEC coordinate of `packetId` is read under
|
|
13132
|
-
*
|
|
13133
|
-
*
|
|
13155
|
+
* The block profile an FEC coordinate of `packetId` is read under:
|
|
13156
|
+
* `iso-ssbg1-v1`, the only one (`k-padded-v1` is retired and refused).
|
|
13157
|
+
* Throws while none is configured: such a packet has no FEC geometry, and
|
|
13158
|
+
* nothing is inferred from the wire or defaulted.
|
|
13134
13159
|
* Packet ingress leaves such a packet's FEC Payload ID unread first
|
|
13135
13160
|
* (#noteUnreadableFecPayloadId); a throw here is a wiring fault on a path
|
|
13136
13161
|
* that cannot hold an unconfigured packet.
|
|
@@ -13144,8 +13169,16 @@ var MmtpRouter = class {
|
|
|
13144
13169
|
}
|
|
13145
13170
|
return profile;
|
|
13146
13171
|
}
|
|
13147
|
-
|
|
13148
|
-
|
|
13172
|
+
/**
|
|
13173
|
+
* The block coordinates (SS_Start, ESI) of an FEC source: range membership
|
|
13174
|
+
* in a block a repair already stated (ISO/IEC 23008-1 C.5.3). Null while no
|
|
13175
|
+
* repair has stated its block, while its packetId has no configured block
|
|
13176
|
+
* profile, and without an FEC manager: SS_ID alone locates no block, so
|
|
13177
|
+
* nothing is derived from it.
|
|
13178
|
+
*/
|
|
13179
|
+
#locateFecSource(packetId, catalogTrackName, ssId) {
|
|
13180
|
+
if (this.#configuredBlockProfile(packetId) === void 0) return null;
|
|
13181
|
+
return this.#fecManager?.locateIsoSource?.(packetId, catalogTrackName, ssId) ?? null;
|
|
13149
13182
|
}
|
|
13150
13183
|
/**
|
|
13151
13184
|
* Explicit refusal to read an FEC Payload ID: its (owner) packetId has no
|
|
@@ -13164,21 +13197,20 @@ var MmtpRouter = class {
|
|
|
13164
13197
|
}
|
|
13165
13198
|
}
|
|
13166
13199
|
/**
|
|
13167
|
-
* The release-gate anchor of a recovered symbol: its
|
|
13168
|
-
*
|
|
13169
|
-
*
|
|
13200
|
+
* The release-gate anchor of a recovered symbol: its own SS_ID, SS_Start +
|
|
13201
|
+
* ESI modulo 2^32 (C.5.2), where a frame is gated by the SS_IDs between it
|
|
13202
|
+
* and the last released frame.
|
|
13170
13203
|
*/
|
|
13171
|
-
#fecAnchorForRecovered(
|
|
13172
|
-
return
|
|
13204
|
+
#fecAnchorForRecovered(blockStart, index) {
|
|
13205
|
+
return serial32Add(blockStart, index);
|
|
13173
13206
|
}
|
|
13174
13207
|
/**
|
|
13175
13208
|
* One iso-ssbg1-v1 repair symbol (ISO/IEC 23008-1 C.5.3). It states the
|
|
13176
13209
|
* block `[SS_Start, SS_Start + SSB_length)`; repair ESI = SSB_length +
|
|
13177
13210
|
* RS_ID (K' = SSB_length). The held sources it placed (and those of a
|
|
13178
13211
|
* repair-less span in front of it) are fed as first-arrival sources, then
|
|
13179
|
-
* the repair, the order they had on the wire, so a loss-free block
|
|
13180
|
-
* on its repair
|
|
13181
|
-
* block is dropped.
|
|
13212
|
+
* the repair, the order they had on the wire, so a loss-free block completes
|
|
13213
|
+
* clean on its repair. A repair contradicting a known block is dropped.
|
|
13182
13214
|
*/
|
|
13183
13215
|
#handleIsoRepair(ownerPacketId, catalogTrackName, repair, source) {
|
|
13184
13216
|
const fec = this.#fecManager;
|
|
@@ -13270,7 +13302,8 @@ var MmtpRouter = class {
|
|
|
13270
13302
|
return null;
|
|
13271
13303
|
}
|
|
13272
13304
|
/** Route a recovered FEC block through the parser + dedup pipeline.
|
|
13273
|
-
* Sync WASM decoders return K fixed-width
|
|
13305
|
+
* Sync WASM decoders return the block's K' = SSB_length fixed-width
|
|
13306
|
+
* ssbg_mode1 source symbols concatenated. */
|
|
13274
13307
|
routeRecoveredBlock(blockId, data, packetId, catalogTrackName) {
|
|
13275
13308
|
this.#routeRecoveredBlock(blockId, data, packetId, catalogTrackName);
|
|
13276
13309
|
}
|
|
@@ -13738,29 +13771,19 @@ var MmtpRouter = class {
|
|
|
13738
13771
|
resolvedLoss: (packetId, catalogTrackName, fromSbn, toSbn, releasedSbn) => {
|
|
13739
13772
|
const fec = this.#fecManager;
|
|
13740
13773
|
if (!fec?.blockResolution) return [];
|
|
13741
|
-
|
|
13742
|
-
|
|
13743
|
-
return fec.isoSpanResolution?.(packetId, catalogTrackName, releasedSbn, head) ?? null;
|
|
13744
|
-
}
|
|
13745
|
-
const highest = fec.highestSbn?.(packetId, catalogTrackName);
|
|
13746
|
-
const lost = [];
|
|
13747
|
-
for (let sbn = fromSbn; sbn <= toSbn; sbn++) {
|
|
13748
|
-
const resolution = fec.blockResolution(sbn, packetId, catalogTrackName);
|
|
13749
|
-
if (resolution === "pending") return null;
|
|
13750
|
-
if (resolution === "failed") {
|
|
13751
|
-
lost.push(sbn);
|
|
13752
|
-
} else if (resolution === "untracked") {
|
|
13753
|
-
if (sbn === releasedSbn) continue;
|
|
13754
|
-
if (highest === void 0 || sbn > highest) return null;
|
|
13755
|
-
lost.push(sbn);
|
|
13756
|
-
}
|
|
13757
|
-
}
|
|
13758
|
-
return lost;
|
|
13774
|
+
const head = fromSbn === releasedSbn ? toSbn : fromSbn;
|
|
13775
|
+
return fec.isoSpanResolution?.(packetId, catalogTrackName, releasedSbn, head) ?? null;
|
|
13759
13776
|
},
|
|
13760
13777
|
// The retention window itself: a frame is never still held when a block
|
|
13761
13778
|
// it waits on retires. Only consulted while resolvedLoss holds a gap,
|
|
13762
13779
|
// which needs blockResolution, so the check above guarantees the method.
|
|
13763
|
-
|
|
13780
|
+
// A packetId the manager no longer has configured (released) has no
|
|
13781
|
+
// block left to wait on: release its frames now (BLO-45953).
|
|
13782
|
+
holdLimitMs: (packetId) => {
|
|
13783
|
+
const fec = this.#fecManager;
|
|
13784
|
+
if (!fec?.terminalRetentionMsForPacket) return Infinity;
|
|
13785
|
+
return fec.terminalRetentionMsForPacket(packetId) ?? 0;
|
|
13786
|
+
},
|
|
13764
13787
|
decide: (before, after, lostBlocks, discontinuity) => {
|
|
13765
13788
|
const decision = this.#decideGap(before, after, lostBlocks, discontinuity);
|
|
13766
13789
|
this.#onFrameGap?.(decision);
|
|
@@ -13822,9 +13845,10 @@ var MmtpRouter = class {
|
|
|
13822
13845
|
}
|
|
13823
13846
|
/**
|
|
13824
13847
|
* The most media units a gap between two released frames can hold: the
|
|
13825
|
-
* FEC blocks
|
|
13826
|
-
* block touches (ceil(interleaveDepthMs / unit duration), §5.1).
|
|
13827
|
-
* either
|
|
13848
|
+
* FEC blocks between `before`'s anchor and `after`'s, times D, the media
|
|
13849
|
+
* units one block touches (ceil(interleaveDepthMs / unit duration), §5.1).
|
|
13850
|
+
* Null when either anchor, the unit duration or the catalog interleave is
|
|
13851
|
+
* unknown.
|
|
13828
13852
|
*/
|
|
13829
13853
|
#maxMissingUnits(before, after) {
|
|
13830
13854
|
const from = before.fecSbn;
|
|
@@ -13837,7 +13861,7 @@ var MmtpRouter = class {
|
|
|
13837
13861
|
const denominator = BigInt(timing.durationTicks) * 1000n;
|
|
13838
13862
|
const unitsPerBlock = (numerator + denominator - 1n) / denominator;
|
|
13839
13863
|
const blocks = BigInt(
|
|
13840
|
-
(
|
|
13864
|
+
(serial32Precedes(from, to) ? serial32Distance(from, to) : serial32Distance(to, from)) + 1
|
|
13841
13865
|
);
|
|
13842
13866
|
const bound = unitsPerBlock * blocks;
|
|
13843
13867
|
return bound > BigInt(Number.MAX_SAFE_INTEGER) ? null : Number(bound);
|
|
@@ -13887,8 +13911,9 @@ var MmtpRouter = class {
|
|
|
13887
13911
|
const cb = this.#trackInitCallbacks.get(packetId);
|
|
13888
13912
|
if (cb) {
|
|
13889
13913
|
cb(data);
|
|
13890
|
-
} else
|
|
13891
|
-
this.#
|
|
13914
|
+
} else {
|
|
13915
|
+
this.#unmappedInitDeliveries++;
|
|
13916
|
+
this.#onInitSegment?.(data);
|
|
13892
13917
|
}
|
|
13893
13918
|
console.log(
|
|
13894
13919
|
`[MmtpRouter] Init delivered for packetId=${packetId}${catalogTrackName === void 0 ? "" : ` track=${catalogTrackName}`} (${data.byteLength}B)`
|
|
@@ -13930,9 +13955,9 @@ var MmtpRouter = class {
|
|
|
13930
13955
|
if (!info.isRepair && this.#manifestVerifier && !manifestAdmitted) {
|
|
13931
13956
|
let identity = manifestIdentity;
|
|
13932
13957
|
if (!identity && info.fecPayloadId && !fecUnreadable && (this.#resolveManifestIdentityForRoute || this.#resolveManifestIdentity)) {
|
|
13933
|
-
const coordinates = this.#deriveSbnEsi(info.fecPayloadId.ssId, info.packetId);
|
|
13934
13958
|
const catalogTrackName = catalogTrackNameForPacket(ingress, info.packetId);
|
|
13935
|
-
|
|
13959
|
+
const coordinates = this.#locateFecSource(info.packetId, catalogTrackName, info.fecPayloadId.ssId);
|
|
13960
|
+
identity = coordinates === null ? void 0 : (this.#resolveManifestIdentityForRoute ? this.#resolveManifestIdentityForRoute({
|
|
13936
13961
|
packetId: info.packetId,
|
|
13937
13962
|
sbn: coordinates.sbn,
|
|
13938
13963
|
esi: coordinates.esi,
|
|
@@ -13984,79 +14009,7 @@ var MmtpRouter = class {
|
|
|
13984
14009
|
this.#noteUnreadableFecPayloadId(ownerPacketId, "repair");
|
|
13985
14010
|
return;
|
|
13986
14011
|
}
|
|
13987
|
-
|
|
13988
|
-
this.#handleIsoRepair(ownerPacketId, catalogTrackNameForPacket(ingress, ownerPacketId), repair, source);
|
|
13989
|
-
return;
|
|
13990
|
-
}
|
|
13991
|
-
const repairSbn = this.#deriveRepairSbn(repair.ssStart, repair.ssbLength, ownerPacketId);
|
|
13992
|
-
if (repairSbn === null) return;
|
|
13993
|
-
const repairEsi = this.#kForPacket(ownerPacketId) + repair.rsId;
|
|
13994
|
-
if (!this.#feedFecTransportSymbol(
|
|
13995
|
-
ownerPacketId,
|
|
13996
|
-
repairSbn,
|
|
13997
|
-
repairEsi,
|
|
13998
|
-
repair.symbolData,
|
|
13999
|
-
source,
|
|
14000
|
-
catalogTrackNameForPacket(ingress, ownerPacketId)
|
|
14001
|
-
)) return;
|
|
14002
|
-
const repairCatalogTrackName = catalogTrackNameForPacket(ingress, ownerPacketId);
|
|
14003
|
-
if (repairCatalogTrackName === void 0) {
|
|
14004
|
-
this.#fecManager.noteBlockActivity?.(repairSbn, ownerPacketId);
|
|
14005
|
-
} else {
|
|
14006
|
-
this.#fecManager.noteBlockActivity?.(
|
|
14007
|
-
repairSbn,
|
|
14008
|
-
ownerPacketId,
|
|
14009
|
-
repairCatalogTrackName
|
|
14010
|
-
);
|
|
14011
|
-
}
|
|
14012
|
-
const recovered = repairCatalogTrackName === void 0 ? this.#fecManager.feedRepairSymbol(
|
|
14013
|
-
ownerPacketId,
|
|
14014
|
-
repair.ssStart,
|
|
14015
|
-
repair.ssbLength,
|
|
14016
|
-
repair.rsId,
|
|
14017
|
-
repair.symbolData
|
|
14018
|
-
) : this.#fecManager.feedRepairSymbol(
|
|
14019
|
-
ownerPacketId,
|
|
14020
|
-
repair.ssStart,
|
|
14021
|
-
repair.ssbLength,
|
|
14022
|
-
repair.rsId,
|
|
14023
|
-
repair.symbolData,
|
|
14024
|
-
repairCatalogTrackName
|
|
14025
|
-
);
|
|
14026
|
-
if (recovered) {
|
|
14027
|
-
const alreadyCompleted = repairCatalogTrackName === void 0 ? this.#fecManager.isBlockCompleted(repairSbn, ownerPacketId) : this.#fecManager.isBlockCompleted(
|
|
14028
|
-
repairSbn,
|
|
14029
|
-
ownerPacketId,
|
|
14030
|
-
repairCatalogTrackName
|
|
14031
|
-
);
|
|
14032
|
-
if (repairCatalogTrackName === void 0) {
|
|
14033
|
-
this.#fecManager.cancelBlockTimeout(repairSbn, ownerPacketId);
|
|
14034
|
-
} else {
|
|
14035
|
-
this.#fecManager.cancelBlockTimeout(
|
|
14036
|
-
repairSbn,
|
|
14037
|
-
ownerPacketId,
|
|
14038
|
-
repairCatalogTrackName
|
|
14039
|
-
);
|
|
14040
|
-
}
|
|
14041
|
-
if (!alreadyCompleted) {
|
|
14042
|
-
this.#fecManager.countRecovery(ownerPacketId, repairSbn, repairCatalogTrackName);
|
|
14043
|
-
if (repairCatalogTrackName === void 0) {
|
|
14044
|
-
this.#fecManager.markBlockCompleted(repairSbn, ownerPacketId);
|
|
14045
|
-
} else {
|
|
14046
|
-
this.#fecManager.markBlockCompleted(
|
|
14047
|
-
repairSbn,
|
|
14048
|
-
ownerPacketId,
|
|
14049
|
-
repairCatalogTrackName
|
|
14050
|
-
);
|
|
14051
|
-
}
|
|
14052
|
-
this.#routeRecoveredBlock(
|
|
14053
|
-
repairSbn,
|
|
14054
|
-
recovered,
|
|
14055
|
-
ownerPacketId,
|
|
14056
|
-
catalogTrackNameForPacket(ingress, ownerPacketId)
|
|
14057
|
-
);
|
|
14058
|
-
}
|
|
14059
|
-
}
|
|
14012
|
+
this.#handleIsoRepair(ownerPacketId, catalogTrackNameForPacket(ingress, ownerPacketId), repair, source);
|
|
14060
14013
|
}
|
|
14061
14014
|
} else {
|
|
14062
14015
|
if (this.#classifyPacketId(info.packetId) === null) return;
|
|
@@ -14080,8 +14033,8 @@ var MmtpRouter = class {
|
|
|
14080
14033
|
}
|
|
14081
14034
|
const duplicates = this.#stream(info.packetId, catalogTrackName).duplicates;
|
|
14082
14035
|
const fecPayloadId = fecUnreadable ? null : info.fecPayloadId;
|
|
14083
|
-
const
|
|
14084
|
-
const coords =
|
|
14036
|
+
const fecSource = this.#fecManager !== null && fecPayloadId != null;
|
|
14037
|
+
const coords = fecSource && fecPayloadId ? this.#locateFecSource(info.packetId, catalogTrackName, fecPayloadId.ssId) ?? void 0 : void 0;
|
|
14085
14038
|
duplicates.advance(coords?.sbn);
|
|
14086
14039
|
if (source === "multicast" && !overlapCopy && !releasedFromQuarantine && catalogTrackName !== void 0 && duplicates.predatesHorizon(info.sequenceNumber, fecPayloadId?.ssId)) {
|
|
14087
14040
|
this.#publisherRestarts++;
|
|
@@ -14096,9 +14049,8 @@ var MmtpRouter = class {
|
|
|
14096
14049
|
}
|
|
14097
14050
|
if (this.#fecManager && fecPayloadId && coords) {
|
|
14098
14051
|
const fecId = fecPayloadId;
|
|
14099
|
-
const symbolSize = this.#symbolSizeForPacket(info.packetId);
|
|
14100
14052
|
const rawSymbol = packet.subarray(0, packet.length - 4);
|
|
14101
|
-
const oversized =
|
|
14053
|
+
const oversized = !(this.#fecManager.isoSourceFits?.(info.packetId, rawSymbol.length) ?? false);
|
|
14102
14054
|
let overlapBlockCompleted = false;
|
|
14103
14055
|
if (overlapCopy) {
|
|
14104
14056
|
overlapBlockCompleted = catalogTrackName === void 0 ? this.#fecManager.isBlockCompleted(coords.sbn, info.packetId) : this.#fecManager.isBlockCompleted(
|
|
@@ -14133,7 +14085,7 @@ var MmtpRouter = class {
|
|
|
14133
14085
|
}
|
|
14134
14086
|
parseSourceSymbol = (overlapCopy ? !overlapBlockCompleted : sourceSymbolAccepted) && !duplicates.parsedSymbols.has(fecId.ssId);
|
|
14135
14087
|
fecInfo = { sbn: coords.sbn, esi: coords.esi, oversized };
|
|
14136
|
-
} else if (
|
|
14088
|
+
} else if (fecSource && this.#fecManager && fecPayloadId) {
|
|
14137
14089
|
const ssId = fecPayloadId.ssId;
|
|
14138
14090
|
if (overlapCopy) {
|
|
14139
14091
|
parseSourceSymbol = !duplicates.parsedSymbols.has(ssId);
|
|
@@ -14167,12 +14119,13 @@ var MmtpRouter = class {
|
|
|
14167
14119
|
this.#feedViaParser(packet, info, fecInfo, {
|
|
14168
14120
|
source,
|
|
14169
14121
|
catalogTrackName,
|
|
14170
|
-
|
|
14122
|
+
// An FEC source frame is anchored by its own SS_ID (C.5.2).
|
|
14123
|
+
...fecSource && fecPayloadId ? { fecSbn: fecPayloadId.ssId } : {}
|
|
14171
14124
|
}, parsed);
|
|
14172
14125
|
}
|
|
14173
14126
|
if (this.#classifyPacketId(info.packetId) === null) return;
|
|
14174
|
-
if (this.#fecManager && fecPayloadId && sourceSymbolAccepted && sourceSymbolTrackable) {
|
|
14175
|
-
const { sbn, esi } =
|
|
14127
|
+
if (this.#fecManager && fecPayloadId && coords && sourceSymbolAccepted && sourceSymbolTrackable) {
|
|
14128
|
+
const { sbn, esi } = coords;
|
|
14176
14129
|
if (this.#classifyPacketId(info.packetId) !== null && this.#fecManager.isTrackConfigured?.(info.packetId) !== false) {
|
|
14177
14130
|
this.#fecManager.trackBlock(
|
|
14178
14131
|
sbn,
|
|
@@ -14606,7 +14559,6 @@ var MmtpRouter = class {
|
|
|
14606
14559
|
// ── Private: FEC recovery routing ───────────────────────────────
|
|
14607
14560
|
/** Route WASM-recovered block through appropriate path based on container kind. */
|
|
14608
14561
|
#routeRecoveredBlock(blockId, recovered, ownerPacketId, catalogTrackName) {
|
|
14609
|
-
const k = this.#kForPacket(ownerPacketId);
|
|
14610
14562
|
const packetSymbolSize = this.#symbolSizeForPacket(ownerPacketId);
|
|
14611
14563
|
if (ownerPacketId === void 0) {
|
|
14612
14564
|
throw new Error("[MmtpRouter] a recovered FEC block needs its owner packetId to read its fec.blockProfile");
|
|
@@ -14619,7 +14571,6 @@ var MmtpRouter = class {
|
|
|
14619
14571
|
}
|
|
14620
14572
|
const symbolSize = packetSymbolSize;
|
|
14621
14573
|
const result = processFecBlock(recovered, {
|
|
14622
|
-
...blockProfile === "k-padded-v1" ? { k } : {},
|
|
14623
14574
|
symbolSize,
|
|
14624
14575
|
ownerPacketId,
|
|
14625
14576
|
blockProfile
|
|
@@ -14699,8 +14650,7 @@ var MmtpRouter = class {
|
|
|
14699
14650
|
const admission = verifier.admitObject(
|
|
14700
14651
|
{
|
|
14701
14652
|
...identity,
|
|
14702
|
-
packetId: ownerPacketId
|
|
14703
|
-
...ownerPacketId !== void 0 && this.#isIsoPacket(ownerPacketId) ? {} : { ssId: blockId * this.#kForPacket(ownerPacketId) + symbol.index }
|
|
14653
|
+
packetId: ownerPacketId
|
|
14704
14654
|
},
|
|
14705
14655
|
symbol.rawSymbol,
|
|
14706
14656
|
() => {
|
|
@@ -14803,7 +14753,7 @@ var MmtpRouter = class {
|
|
|
14803
14753
|
rapFlag: sym.fragment.rapFlag,
|
|
14804
14754
|
recovered: true,
|
|
14805
14755
|
deliveryProvenance,
|
|
14806
|
-
fecSbn: this.#fecAnchorForRecovered(
|
|
14756
|
+
fecSbn: this.#fecAnchorForRecovered(blockId, sym.index),
|
|
14807
14757
|
...catalogTrackName === void 0 ? {} : { catalogTrackName }
|
|
14808
14758
|
});
|
|
14809
14759
|
continue;
|
|
@@ -14915,7 +14865,7 @@ var MmtpRouter = class {
|
|
|
14915
14865
|
rapFlag: assembled.rapFlag,
|
|
14916
14866
|
recovered: true,
|
|
14917
14867
|
...assembled.deliveryProvenance === void 0 ? {} : { deliveryProvenance: assembled.deliveryProvenance },
|
|
14918
|
-
fecSbn: this.#fecAnchorForRecovered(
|
|
14868
|
+
fecSbn: this.#fecAnchorForRecovered(blockId, sym.index),
|
|
14919
14869
|
...recoveredCatalogTrackName === void 0 ? {} : { catalogTrackName: recoveredCatalogTrackName }
|
|
14920
14870
|
});
|
|
14921
14871
|
continue;
|
|
@@ -15052,7 +15002,7 @@ var MmtpRouter = class {
|
|
|
15052
15002
|
rapFlag: sym.fragment.rapFlag,
|
|
15053
15003
|
recovered: true,
|
|
15054
15004
|
deliveryProvenance,
|
|
15055
|
-
fecSbn: this.#fecAnchorForRecovered(
|
|
15005
|
+
fecSbn: this.#fecAnchorForRecovered(blockId, sym.index),
|
|
15056
15006
|
...catalogTrackName === void 0 ? {} : { catalogTrackName }
|
|
15057
15007
|
});
|
|
15058
15008
|
} else {
|
|
@@ -15131,7 +15081,7 @@ var MmtpRouter = class {
|
|
|
15131
15081
|
rapFlag: assembled.rapFlag,
|
|
15132
15082
|
recovered: true,
|
|
15133
15083
|
deliveryProvenance,
|
|
15134
|
-
fecSbn: this.#fecAnchorForRecovered(
|
|
15084
|
+
fecSbn: this.#fecAnchorForRecovered(blockId, sym.index),
|
|
15135
15085
|
...assembled.catalogTrackName === void 0 ? {} : { catalogTrackName: assembled.catalogTrackName }
|
|
15136
15086
|
});
|
|
15137
15087
|
}
|
|
@@ -15166,7 +15116,7 @@ var MmtpRouter = class {
|
|
|
15166
15116
|
...sym.fragment.sampleNumber === void 0 ? {} : { sampleNumber: sym.fragment.sampleNumber },
|
|
15167
15117
|
recovered: true,
|
|
15168
15118
|
deliveryProvenance,
|
|
15169
|
-
fecSbn: this.#fecAnchorForRecovered(
|
|
15119
|
+
fecSbn: this.#fecAnchorForRecovered(blockId, sym.index),
|
|
15170
15120
|
...catalogTrackName === void 0 ? {} : { catalogTrackName }
|
|
15171
15121
|
});
|
|
15172
15122
|
continue;
|
|
@@ -15217,22 +15167,22 @@ var MoqFecAlgorithm;
|
|
|
15217
15167
|
MoqFecAlgorithm2[MoqFecAlgorithm2["RaptorQ"] = 2] = "RaptorQ";
|
|
15218
15168
|
})(MoqFecAlgorithm || (MoqFecAlgorithm = {}));
|
|
15219
15169
|
|
|
15220
|
-
// node_modules/@blockcast/mmt-
|
|
15170
|
+
// node_modules/@blockcast/mmt-alta-parse/dist/index.js
|
|
15221
15171
|
var MPU_HEADER_SIZE3 = 8;
|
|
15222
15172
|
var MPU_LENGTH_FIELD_SIZE3 = 2;
|
|
15223
15173
|
var MPU_HEADER_REMAINDER_SIZE3 = MPU_HEADER_SIZE3 - MPU_LENGTH_FIELD_SIZE3;
|
|
15224
15174
|
|
|
15225
|
-
// node_modules/@blockcast/mmt-
|
|
15175
|
+
// node_modules/@blockcast/mmt-base/dist/mmtp.js
|
|
15226
15176
|
var MPU_HEADER_SIZE4 = 8;
|
|
15227
15177
|
var MPU_LENGTH_FIELD_SIZE4 = 2;
|
|
15228
15178
|
var MPU_HEADER_REMAINDER_SIZE4 = MPU_HEADER_SIZE4 - MPU_LENGTH_FIELD_SIZE4;
|
|
15229
15179
|
var MMTP_SIGNALING_PACKET_ID2 = 0;
|
|
15230
15180
|
var MMTP_PACKET_ID_MAX2 = 65535;
|
|
15231
15181
|
|
|
15232
|
-
// node_modules/@blockcast/mmt-
|
|
15182
|
+
// node_modules/@blockcast/mmt-base/dist/net-constants.js
|
|
15233
15183
|
var DEFAULT_AMT_RELAY_PORT2 = 2268;
|
|
15234
15184
|
|
|
15235
|
-
// node_modules/@blockcast/mmt-
|
|
15185
|
+
// node_modules/@blockcast/mmt-base/dist/ntp-short.js
|
|
15236
15186
|
var NTP_SHORT_BITS2 = 32;
|
|
15237
15187
|
var NTP_SHORT_MODULUS2 = 2 ** NTP_SHORT_BITS2;
|
|
15238
15188
|
var NTP_SHORT_HALF_RANGE2 = 2 ** (NTP_SHORT_BITS2 - 1);
|
|
@@ -15253,9 +15203,8 @@ function compareNtpShort(a, b) {
|
|
|
15253
15203
|
return "unordered";
|
|
15254
15204
|
}
|
|
15255
15205
|
|
|
15256
|
-
// node_modules/@blockcast/mmt-
|
|
15206
|
+
// node_modules/@blockcast/mmt-base/dist/fec-block-profile.js
|
|
15257
15207
|
var FEC_BLOCK_PROFILES2 = Object.freeze([
|
|
15258
|
-
"k-padded-v1",
|
|
15259
15208
|
"iso-ssbg1-v1"
|
|
15260
15209
|
]);
|
|
15261
15210
|
|
|
@@ -15647,10 +15596,12 @@ var MulticastOrchestrator = class {
|
|
|
15647
15596
|
}
|
|
15648
15597
|
onGoodput(sample) {
|
|
15649
15598
|
const bitrate = sample.bitrate;
|
|
15650
|
-
if (!this.#current
|
|
15599
|
+
if (!this.#current)
|
|
15651
15600
|
return void 0;
|
|
15652
15601
|
if (this.#state === "joining")
|
|
15653
15602
|
return this.poll();
|
|
15603
|
+
if (this.options.adaptive === false)
|
|
15604
|
+
return void 0;
|
|
15654
15605
|
if (bitrate === void 0 || !Number.isFinite(bitrate))
|
|
15655
15606
|
return void 0;
|
|
15656
15607
|
this.#estimate = bitrate;
|
|
@@ -15717,6 +15668,27 @@ var MulticastOrchestrator = class {
|
|
|
15717
15668
|
this.#favored = void 0;
|
|
15718
15669
|
return { kind: "join-target", current: this.#current, target: desired };
|
|
15719
15670
|
}
|
|
15671
|
+
/**
|
|
15672
|
+
* Join `target` because the viewer selected it, not because goodput or
|
|
15673
|
+
* headroom ranked it. It runs whether or not the orchestrator adapts, and
|
|
15674
|
+
* takes the path an adaptive join takes: commit on the target's RAP at or
|
|
15675
|
+
* beyond the high-water mark, abandon at the join deadline. While it is
|
|
15676
|
+
* pending nothing re-ranks the ladder; goodput only advances the deadline.
|
|
15677
|
+
*
|
|
15678
|
+
* Returns undefined, changing nothing, while another join is pending (cancel
|
|
15679
|
+
* it first), when `target` is the current tier, or when it carries no media.
|
|
15680
|
+
*/
|
|
15681
|
+
select(target) {
|
|
15682
|
+
if (!this.#current || this.#state === "joining" || !carriesMedia(target))
|
|
15683
|
+
return void 0;
|
|
15684
|
+
if (endpointKey(target) === endpointKey(this.#current))
|
|
15685
|
+
return void 0;
|
|
15686
|
+
this.#state = "joining";
|
|
15687
|
+
this.#target = target;
|
|
15688
|
+
this.#joinStartedAt = this.#now();
|
|
15689
|
+
this.#favored = void 0;
|
|
15690
|
+
return { kind: "join-target", current: this.#current, target };
|
|
15691
|
+
}
|
|
15720
15692
|
/**
|
|
15721
15693
|
* Feed a packet from the active tier (to advance the high-water mark) or
|
|
15722
15694
|
* from the in-flight target (to find a committable RAP).
|
|
@@ -16756,7 +16728,7 @@ var MulticastTransport = class _MulticastTransport {
|
|
|
16756
16728
|
// inside the window, instead of a clear+set pair on every packet.
|
|
16757
16729
|
#watchdog = null;
|
|
16758
16730
|
#lastPacketAtMs = 0;
|
|
16759
|
-
#failbackWindowMs
|
|
16731
|
+
#failbackWindowMs;
|
|
16760
16732
|
#onTakeover = null;
|
|
16761
16733
|
#onFailback = null;
|
|
16762
16734
|
// Data-plane-live edge: the first packet this join accepted. Everything
|
|
@@ -17061,14 +17033,20 @@ var MulticastTransport = class _MulticastTransport {
|
|
|
17061
17033
|
return this.#dataPlaneLive;
|
|
17062
17034
|
}
|
|
17063
17035
|
setFailbackWindowMs(windowMs) {
|
|
17064
|
-
if (!Number.isFinite(windowMs) || windowMs <= 0) {
|
|
17036
|
+
if (windowMs !== null && (!Number.isFinite(windowMs) || windowMs <= 0)) {
|
|
17065
17037
|
throw new Error(
|
|
17066
17038
|
`[MulticastTransport] failbackWindowMs=${windowMs}. Derive a positive failback window from the stream catalog.`
|
|
17067
17039
|
);
|
|
17068
17040
|
}
|
|
17069
17041
|
this.#failbackWindowMs = windowMs;
|
|
17070
|
-
if (
|
|
17071
|
-
clearTimeout(this.#watchdog);
|
|
17042
|
+
if (windowMs === null) {
|
|
17043
|
+
if (this.#watchdog !== null) clearTimeout(this.#watchdog);
|
|
17044
|
+
this.#watchdog = null;
|
|
17045
|
+
return;
|
|
17046
|
+
}
|
|
17047
|
+
if (this.#watchdog !== null) clearTimeout(this.#watchdog);
|
|
17048
|
+
this.#watchdog = null;
|
|
17049
|
+
if (this.#dataPlaneLive && this.#onFailback) {
|
|
17072
17050
|
const silentMs = performance.now() - this.#lastPacketAtMs;
|
|
17073
17051
|
this.#armWatchdog(Math.max(0, Math.ceil(windowMs - silentMs)));
|
|
17074
17052
|
}
|
|
@@ -17394,7 +17372,8 @@ var MulticastTransport = class _MulticastTransport {
|
|
|
17394
17372
|
}
|
|
17395
17373
|
if (this.#watchdog !== null) return;
|
|
17396
17374
|
if (!this.#onFailback) return;
|
|
17397
|
-
if (this.#failbackWindowMs === null)
|
|
17375
|
+
if (this.#failbackWindowMs === null) return;
|
|
17376
|
+
if (this.#failbackWindowMs === void 0) {
|
|
17398
17377
|
throw new Error(
|
|
17399
17378
|
"[MulticastTransport] Failback watchdog requires a catalog-derived window"
|
|
17400
17379
|
);
|
|
@@ -17404,7 +17383,7 @@ var MulticastTransport = class _MulticastTransport {
|
|
|
17404
17383
|
#armWatchdog(delayMs) {
|
|
17405
17384
|
this.#watchdog = setTimeout(() => {
|
|
17406
17385
|
this.#watchdog = null;
|
|
17407
|
-
if (this.#disposed || !this.#onFailback || this.#failbackWindowMs
|
|
17386
|
+
if (this.#disposed || !this.#onFailback || this.#failbackWindowMs == null) return;
|
|
17408
17387
|
const silentMs = performance.now() - this.#lastPacketAtMs;
|
|
17409
17388
|
if (silentMs < this.#failbackWindowMs) {
|
|
17410
17389
|
this.#armWatchdog(Math.ceil(this.#failbackWindowMs - silentMs));
|
|
@@ -17438,6 +17417,35 @@ var BASE64URL_DECODE_TABLE = (() => {
|
|
|
17438
17417
|
return table;
|
|
17439
17418
|
})();
|
|
17440
17419
|
|
|
17420
|
+
// src/repair-layers.ts
|
|
17421
|
+
var RepairContractError = class extends Error {
|
|
17422
|
+
constructor(code, path, message) {
|
|
17423
|
+
super(`[repair-layers:${code}] ${path}: ${message}`);
|
|
17424
|
+
this.code = code;
|
|
17425
|
+
this.path = path;
|
|
17426
|
+
this.name = "RepairContractError";
|
|
17427
|
+
}
|
|
17428
|
+
};
|
|
17429
|
+
var throwRepairContractError = (code, path, message) => {
|
|
17430
|
+
throw new RepairContractError(code, path, message);
|
|
17431
|
+
};
|
|
17432
|
+
function readFecBlockProfile(value, path, declaresFecInstance, fail) {
|
|
17433
|
+
if (value === void 0 && !declaresFecInstance) return void 0;
|
|
17434
|
+
if (isFecBlockProfile(value)) return value;
|
|
17435
|
+
if (typeof value === "string" && value.length > 0) {
|
|
17436
|
+
return fail(
|
|
17437
|
+
"track_fec_block_profile_unimplemented",
|
|
17438
|
+
path,
|
|
17439
|
+
`${JSON.stringify(value)} is not an implemented FEC block profile; only "iso-ssbg1-v1" is implemented`
|
|
17440
|
+
);
|
|
17441
|
+
}
|
|
17442
|
+
try {
|
|
17443
|
+
return parseFecBlockProfile(value, "blockProfile");
|
|
17444
|
+
} catch (error) {
|
|
17445
|
+
return fail("track_fec_block_profile", path, error.message);
|
|
17446
|
+
}
|
|
17447
|
+
}
|
|
17448
|
+
|
|
17441
17449
|
// src/carp-catalog.ts
|
|
17442
17450
|
var VALID_ROLE_SUFFIXES = /* @__PURE__ */ new Set(["base", "delta", "repair"]);
|
|
17443
17451
|
function parseTrackRole(trackName) {
|
|
@@ -17477,12 +17485,49 @@ function hasSwitchingSetRole(names) {
|
|
|
17477
17485
|
return suffix === "base" || suffix === "delta";
|
|
17478
17486
|
});
|
|
17479
17487
|
}
|
|
17480
|
-
function
|
|
17488
|
+
function isFecCatalogGeometry(obj) {
|
|
17481
17489
|
if (typeof obj !== "object" || obj === null) return false;
|
|
17482
17490
|
const o = obj;
|
|
17483
17491
|
const algorithm = o["algorithm"];
|
|
17484
17492
|
const symbolSize = o["symbolSize"];
|
|
17485
|
-
return typeof algorithm === "string" && (algorithm === "none" || algorithm === "raptorq" || algorithm === "reed-solomon") && typeof o["sourceSymbols"] === "number" && o["sourceSymbols"] >= 1 && typeof o["repairSymbols"] === "number" && o["repairSymbols"] >= 1 && typeof symbolSize === "number" && symbolSize >= 1 && (algorithm !== "raptorq" || symbolSize % 8 === 0) && typeof o["repairTrack"] === "string" && o["repairTrack"].length > 0
|
|
17493
|
+
return typeof algorithm === "string" && (algorithm === "none" || algorithm === "raptorq" || algorithm === "reed-solomon") && typeof o["sourceSymbols"] === "number" && o["sourceSymbols"] >= 1 && typeof o["repairSymbols"] === "number" && o["repairSymbols"] >= 1 && typeof symbolSize === "number" && symbolSize >= 1 && (algorithm !== "raptorq" || symbolSize % 8 === 0) && typeof o["repairTrack"] === "string" && o["repairTrack"].length > 0;
|
|
17494
|
+
}
|
|
17495
|
+
function isFecCatalogConfig(obj) {
|
|
17496
|
+
if (!isFecCatalogGeometry(obj)) return false;
|
|
17497
|
+
const o = obj;
|
|
17498
|
+
return isFecBlockProfile(o["blockProfile"]) || o["algorithm"] === "none" && o["blockProfile"] === void 0;
|
|
17499
|
+
}
|
|
17500
|
+
|
|
17501
|
+
// src/catalog-fec-params.ts
|
|
17502
|
+
import { catalogFecInterleaveMs } from "@blockcast/mmt-fec";
|
|
17503
|
+
var MAX_SAFE_TICKS_PER_HZ = Number.MAX_SAFE_INTEGER / 1e3;
|
|
17504
|
+
function recordOf2(value) {
|
|
17505
|
+
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
|
|
17506
|
+
}
|
|
17507
|
+
function blockProfileUnavailable(value, path, declaresFecInstance) {
|
|
17508
|
+
try {
|
|
17509
|
+
readFecBlockProfile(value, path, declaresFecInstance, throwRepairContractError);
|
|
17510
|
+
return void 0;
|
|
17511
|
+
} catch (error) {
|
|
17512
|
+
if (!(error instanceof RepairContractError) || error.code !== "track_fec_block_profile" && error.code !== "track_fec_block_profile_unimplemented") {
|
|
17513
|
+
throw error;
|
|
17514
|
+
}
|
|
17515
|
+
return Object.freeze({ code: error.code, path: error.path, message: error.message });
|
|
17516
|
+
}
|
|
17517
|
+
}
|
|
17518
|
+
function isoSpanUnavailable(fec, path, declaresFecInstance) {
|
|
17519
|
+
if (!declaresFecInstance || isoBlockSpanUsable(fec)) return void 0;
|
|
17520
|
+
return Object.freeze({
|
|
17521
|
+
code: "track_fec",
|
|
17522
|
+
path,
|
|
17523
|
+
message: `[repair-layers:track_fec] ${path}: must be a non-negative safe integer in milliseconds`
|
|
17524
|
+
});
|
|
17525
|
+
}
|
|
17526
|
+
function catalogFecUnavailable(fec, fecPath) {
|
|
17527
|
+
const block = recordOf2(fec);
|
|
17528
|
+
if (block === void 0) return void 0;
|
|
17529
|
+
const declaresFecInstance = block["algorithm"] !== "none";
|
|
17530
|
+
return blockProfileUnavailable(block["blockProfile"], `${fecPath}.blockProfile`, declaresFecInstance) ?? isoSpanUnavailable(block, `${fecPath}.interleaveDepthMs`, declaresFecInstance);
|
|
17486
17531
|
}
|
|
17487
17532
|
|
|
17488
17533
|
// src/multicast-manager.ts
|
|
@@ -17795,9 +17840,8 @@ function repairTrackNamesBySource(tracks) {
|
|
|
17795
17840
|
bySource.set(source, names);
|
|
17796
17841
|
};
|
|
17797
17842
|
for (const track of tracks) {
|
|
17798
|
-
|
|
17799
|
-
|
|
17800
|
-
}
|
|
17843
|
+
const repairTrack = typeof track.fec === "object" && track.fec !== null ? track.fec.repairTrack : void 0;
|
|
17844
|
+
if (typeof repairTrack === "string") add(track.name, repairTrack);
|
|
17801
17845
|
if (track.packaging === "fec-repair" && Array.isArray(track.depends) && track.depends.length === 1) {
|
|
17802
17846
|
add(track.depends[0], track.name);
|
|
17803
17847
|
}
|
|
@@ -17807,6 +17851,7 @@ function repairTrackNamesBySource(tracks) {
|
|
|
17807
17851
|
var MulticastManager = class {
|
|
17808
17852
|
#fec;
|
|
17809
17853
|
#router;
|
|
17854
|
+
#hostMfuTimeoutMs;
|
|
17810
17855
|
#transport;
|
|
17811
17856
|
#reportedFecDiagnosticsFailure = false;
|
|
17812
17857
|
// ── Stream-global FEC timing reduction ──
|
|
@@ -17905,6 +17950,7 @@ var MulticastManager = class {
|
|
|
17905
17950
|
this.#emitRepairLifecycle(decision);
|
|
17906
17951
|
}
|
|
17907
17952
|
});
|
|
17953
|
+
this.#hostMfuTimeoutMs = this.#router.reassembler.timeoutMs;
|
|
17908
17954
|
this.#transport = new MulticastTransport((packet, ingress) => {
|
|
17909
17955
|
this.#router.feedPacket(packet, "multicast", void 0, ingress);
|
|
17910
17956
|
});
|
|
@@ -18121,7 +18167,6 @@ var MulticastManager = class {
|
|
|
18121
18167
|
...this.#streamFecBaseline ? [this.#streamFecBaseline] : [],
|
|
18122
18168
|
...this.#trackFecTiming.values()
|
|
18123
18169
|
];
|
|
18124
|
-
if (contributions.length === 0) return;
|
|
18125
18170
|
let deliveryWindowMs;
|
|
18126
18171
|
let minimumFailbackWindowMs;
|
|
18127
18172
|
let mfuTimeoutMs;
|
|
@@ -18153,15 +18198,9 @@ var MulticastManager = class {
|
|
|
18153
18198
|
}
|
|
18154
18199
|
}
|
|
18155
18200
|
const failbackWindowMs = deliveryWindowMs === void 0 ? minimumFailbackWindowMs : Math.max(deliveryWindowMs, minimumFailbackWindowMs ?? 0);
|
|
18156
|
-
|
|
18157
|
-
|
|
18158
|
-
|
|
18159
|
-
if (mfuTimeoutMs !== void 0) {
|
|
18160
|
-
this.#router.setMfuTimeoutMs(mfuTimeoutMs);
|
|
18161
|
-
}
|
|
18162
|
-
if (reorderWindowFrames !== void 0) {
|
|
18163
|
-
this.#router.setReorderWindow(reorderWindowFrames);
|
|
18164
|
-
}
|
|
18201
|
+
this.#transport.setFailbackWindowMs(failbackWindowMs ?? null);
|
|
18202
|
+
this.#router.setMfuTimeoutMs(mfuTimeoutMs ?? this.#hostMfuTimeoutMs);
|
|
18203
|
+
this.#router.setReorderWindow(reorderWindowFrames ?? 0);
|
|
18165
18204
|
}
|
|
18166
18205
|
/**
|
|
18167
18206
|
* Configure one track's FEC geometry, block mapper, and block-inactivity
|
|
@@ -18188,9 +18227,9 @@ var MulticastManager = class {
|
|
|
18188
18227
|
`[MulticastManager] configureTrackFec(${packetId}) catalogTrackName must be non-empty when given`
|
|
18189
18228
|
);
|
|
18190
18229
|
}
|
|
18191
|
-
if (config.fecK === void 0
|
|
18230
|
+
if (config.fecK === void 0) {
|
|
18192
18231
|
throw new Error(
|
|
18193
|
-
`[MulticastManager] configureTrackFec(${packetId}) requires fecK
|
|
18232
|
+
`[MulticastManager] configureTrackFec(${packetId}) requires fecK. Derive the per-track FEC geometry from the stream catalog.`
|
|
18194
18233
|
);
|
|
18195
18234
|
}
|
|
18196
18235
|
const blockProfile = parseFecBlockProfile(
|
|
@@ -18198,7 +18237,10 @@ var MulticastManager = class {
|
|
|
18198
18237
|
`[MulticastManager] configureTrackFec(${packetId}) fecBlockProfile`
|
|
18199
18238
|
);
|
|
18200
18239
|
assertPositiveGeometry("fecK", config.fecK);
|
|
18201
|
-
|
|
18240
|
+
const interleaveDepthMs = config.fecInterleaveDepthMs ?? 0;
|
|
18241
|
+
if (!Number.isSafeInteger(interleaveDepthMs) || interleaveDepthMs < 0) {
|
|
18242
|
+
throw new Error("[MulticastManager] fecInterleaveDepthMs must be a non-negative safe integer");
|
|
18243
|
+
}
|
|
18202
18244
|
assertPositiveGeometry("fecSymbolSize", config.fecSymbolSize);
|
|
18203
18245
|
assertPositiveGeometry("fps", config.fps);
|
|
18204
18246
|
assertPositiveGeometry("bandwidth", config.bandwidth);
|
|
@@ -18214,8 +18256,8 @@ var MulticastManager = class {
|
|
|
18214
18256
|
"fecReorderToleranceMs",
|
|
18215
18257
|
config.fecReorderToleranceMs
|
|
18216
18258
|
);
|
|
18217
|
-
const deliveryWindowMs =
|
|
18218
|
-
interleaveDepthMs
|
|
18259
|
+
const deliveryWindowMs = interleaveDepthMs >= MIN_INTERLEAVE_DEPTH_MS ? computeFecDeliveryWindowMs({
|
|
18260
|
+
interleaveDepthMs,
|
|
18219
18261
|
sourceSymbols: config.fecK,
|
|
18220
18262
|
repairSymbols: config.fecRepairSymbols,
|
|
18221
18263
|
symbolSize: config.fecSymbolSize,
|
|
@@ -18224,10 +18266,10 @@ var MulticastManager = class {
|
|
|
18224
18266
|
jitterBufferMs: config.fecReorderToleranceMs
|
|
18225
18267
|
}) : void 0;
|
|
18226
18268
|
const trackKind = config.trackKind ?? this.#router.trackKindFor(packetId);
|
|
18227
|
-
const geometry = trackKind === void 0 ? void 0 : deriveBlockClassifierGeometry({
|
|
18269
|
+
const geometry = trackKind === void 0 || interleaveDepthMs === 0 ? void 0 : deriveBlockClassifierGeometry({
|
|
18228
18270
|
trackKind,
|
|
18229
18271
|
framerate: config.fps,
|
|
18230
|
-
interleaveDepthMs
|
|
18272
|
+
interleaveDepthMs,
|
|
18231
18273
|
timescale: config.timescale,
|
|
18232
18274
|
keyframeIntervalTicks: config.keyframeIntervalTicks,
|
|
18233
18275
|
keyframeIntervalMs: config.keyframeIntervalMs
|
|
@@ -18236,7 +18278,7 @@ var MulticastManager = class {
|
|
|
18236
18278
|
blockProfile,
|
|
18237
18279
|
k: config.fecK,
|
|
18238
18280
|
symbolSize: config.fecSymbolSize,
|
|
18239
|
-
interleaveDepthMs
|
|
18281
|
+
interleaveDepthMs,
|
|
18240
18282
|
fps: config.fps,
|
|
18241
18283
|
timeoutMs: deliveryWindowMs,
|
|
18242
18284
|
...trackKind === void 0 ? {} : { trackKind },
|
|
@@ -18247,7 +18289,7 @@ var MulticastManager = class {
|
|
|
18247
18289
|
this.#configureTrackMapper(packetId, fecConfig);
|
|
18248
18290
|
this.#trackFecTiming.set(packetId, {
|
|
18249
18291
|
deliveryWindowMs,
|
|
18250
|
-
interleaveDepthMs
|
|
18292
|
+
interleaveDepthMs,
|
|
18251
18293
|
fps: config.fps,
|
|
18252
18294
|
k: config.fecK,
|
|
18253
18295
|
repairSymbols: config.fecRepairSymbols,
|
|
@@ -18256,6 +18298,10 @@ var MulticastManager = class {
|
|
|
18256
18298
|
this.#applyStreamGlobalFecTiming();
|
|
18257
18299
|
}
|
|
18258
18300
|
configureFec(config) {
|
|
18301
|
+
const interleaveDepthMs = config.fecInterleaveDepthMs ?? 0;
|
|
18302
|
+
if (!Number.isSafeInteger(interleaveDepthMs) || interleaveDepthMs < 0) {
|
|
18303
|
+
throw new Error("[MulticastManager] fecInterleaveDepthMs must be a non-negative safe integer");
|
|
18304
|
+
}
|
|
18259
18305
|
const minimumFailbackWindowMs = config.minimumFailbackWindowMs;
|
|
18260
18306
|
if (minimumFailbackWindowMs !== void 0 && (!Number.isFinite(minimumFailbackWindowMs) || minimumFailbackWindowMs <= 0)) {
|
|
18261
18307
|
throw new Error(
|
|
@@ -18267,7 +18313,7 @@ var MulticastManager = class {
|
|
|
18267
18313
|
config.fecReorderToleranceMs
|
|
18268
18314
|
);
|
|
18269
18315
|
const deliveryWindowMs = config.fecK && config.fecK > 0 && config.fecInterleaveDepthMs && config.fecInterleaveDepthMs >= MIN_INTERLEAVE_DEPTH_MS ? computeFecDeliveryWindowMs({
|
|
18270
|
-
interleaveDepthMs
|
|
18316
|
+
interleaveDepthMs,
|
|
18271
18317
|
sourceSymbols: config.fecK,
|
|
18272
18318
|
repairSymbols: config.fecRepairSymbols,
|
|
18273
18319
|
symbolSize: config.fecSymbolSize,
|
|
@@ -18278,13 +18324,13 @@ var MulticastManager = class {
|
|
|
18278
18324
|
this.#fec.configure({
|
|
18279
18325
|
k: config.fecK,
|
|
18280
18326
|
symbolSize: config.fecSymbolSize,
|
|
18281
|
-
interleaveDepthMs
|
|
18327
|
+
interleaveDepthMs,
|
|
18282
18328
|
fps: config.fps,
|
|
18283
18329
|
timeoutMs: deliveryWindowMs
|
|
18284
18330
|
});
|
|
18285
18331
|
this.#streamFecBaseline = {
|
|
18286
18332
|
deliveryWindowMs,
|
|
18287
|
-
interleaveDepthMs
|
|
18333
|
+
interleaveDepthMs,
|
|
18288
18334
|
fps: config.fps,
|
|
18289
18335
|
k: config.fecK,
|
|
18290
18336
|
repairSymbols: config.fecRepairSymbols,
|
|
@@ -18333,6 +18379,7 @@ var MulticastManager = class {
|
|
|
18333
18379
|
}
|
|
18334
18380
|
setMfuTimeoutMs(ms) {
|
|
18335
18381
|
this.#router.setMfuTimeoutMs(ms);
|
|
18382
|
+
this.#hostMfuTimeoutMs = this.#router.reassembler.timeoutMs;
|
|
18336
18383
|
}
|
|
18337
18384
|
setTrackCodec(packetId, codec) {
|
|
18338
18385
|
this.#router.setTrackCodec(packetId, codec);
|
|
@@ -18355,6 +18402,7 @@ var MulticastManager = class {
|
|
|
18355
18402
|
onTrackPacket(packetId, cb) {
|
|
18356
18403
|
this.#router.onTrackPacket(packetId, cb);
|
|
18357
18404
|
}
|
|
18405
|
+
/** @deprecated Use onTrackInit(packetId, cb); see MmtpRouter.onInitSegment. */
|
|
18358
18406
|
onInitSegment(cb) {
|
|
18359
18407
|
this.#router.onInitSegment(cb);
|
|
18360
18408
|
}
|
|
@@ -18666,7 +18714,10 @@ var MulticastManager = class {
|
|
|
18666
18714
|
console.warn("[MulticastManager] rendition switch onCancel failed", error);
|
|
18667
18715
|
}
|
|
18668
18716
|
}
|
|
18669
|
-
/**
|
|
18717
|
+
/**
|
|
18718
|
+
* Release what the manager and FEC hold for a stream whose packet ID the
|
|
18719
|
+
* router retired, or whose FEC a catalog reconfiguration no longer configures.
|
|
18720
|
+
*/
|
|
18670
18721
|
#releaseRetiredStream(stream) {
|
|
18671
18722
|
for (const observer of this.#trackResetObservers) {
|
|
18672
18723
|
try {
|
|
@@ -18685,7 +18736,10 @@ var MulticastManager = class {
|
|
|
18685
18736
|
for (const [key, stats] of [...this.#blockPolicyStats]) {
|
|
18686
18737
|
if (stats.packetId === stream.packetId) this.#blockPolicyStats.delete(key);
|
|
18687
18738
|
}
|
|
18688
|
-
this.#trackFecTiming.delete(stream.packetId)
|
|
18739
|
+
if (this.#trackFecTiming.delete(stream.packetId)) {
|
|
18740
|
+
this.#applyStreamGlobalFecTiming();
|
|
18741
|
+
}
|
|
18742
|
+
this.#multiTrackFecTracks.delete(stream.packetId);
|
|
18689
18743
|
this.#fec.releasePacket(stream.packetId);
|
|
18690
18744
|
}
|
|
18691
18745
|
/** Feed a raw packet from an external source (AMT tunnel, ASM subscription).
|
|
@@ -18730,12 +18784,53 @@ var MulticastManager = class {
|
|
|
18730
18784
|
* D-17: Builds altGroup → track-name index for getSwitchingSet().
|
|
18731
18785
|
* D-26: Registers packetId → TrackRole on MmtpRouter for block-complete fanout.
|
|
18732
18786
|
*
|
|
18787
|
+
* FEC availability is per track (draft-ramadan-moq-fec §5.1). A media track
|
|
18788
|
+
* whose `fec` names no implemented `blockProfile` (only `iso-ssbg1-v1` is
|
|
18789
|
+
* implemented; the retired `k-padded-v1` is not), or an `iso-ssbg1-v1` span
|
|
18790
|
+
* it cannot use, is consumed without FEC: it gets no decoder, and none of
|
|
18791
|
+
* its repair tracks (layer 0 by `fec.repairTrack`, further layers and the
|
|
18792
|
+
* overlay by `depends`) is given an owner, so the router drops their packets
|
|
18793
|
+
* before any decoder. Those legs keep the `repair` role and their owner's
|
|
18794
|
+
* switching set. Neither the call nor any other track's FEC fails on that
|
|
18795
|
+
* account; `trackFecUnavailable(name)` reports the reason. FEC a previous
|
|
18796
|
+
* call configured on a packetId this call does not is released
|
|
18797
|
+
* (FECManager.releasePacket); frames its release gate held are delivered in
|
|
18798
|
+
* order, each gap stalled. FEC wired by hand (setFecDecoder) is left alone.
|
|
18799
|
+
* Geometry a decoder could not configure (K, P, T, `repairTrack`,
|
|
18800
|
+
* algorithm) still throws.
|
|
18801
|
+
*
|
|
18733
18802
|
* @param catalog - Multi-track catalog (tracks array with name, altGroup, fec, packetId).
|
|
18734
18803
|
* @param wasmDecoderSource - One WASM FEC decoder for a single FEC stream, or
|
|
18735
18804
|
* a factory returning a distinct decoder per stream.
|
|
18736
18805
|
*/
|
|
18737
18806
|
configureMultiTrack(catalog, wasmDecoderSource) {
|
|
18738
18807
|
const repairNamesBySource = repairTrackNamesBySource(catalog.tracks);
|
|
18808
|
+
const declaredRepairNames = /* @__PURE__ */ new Set();
|
|
18809
|
+
for (const names of repairNamesBySource.values()) {
|
|
18810
|
+
for (const name of names) declaredRepairNames.add(name);
|
|
18811
|
+
}
|
|
18812
|
+
const fecUnavailableByTrack = /* @__PURE__ */ new Map();
|
|
18813
|
+
for (let i = 0; i < catalog.tracks.length; i++) {
|
|
18814
|
+
const track = catalog.tracks[i];
|
|
18815
|
+
if (track.fec === void 0 || track.packaging === "fec-repair" || declaredRepairNames.has(track.name)) {
|
|
18816
|
+
continue;
|
|
18817
|
+
}
|
|
18818
|
+
const fecUnavailable = catalogFecUnavailable(track.fec, `$.tracks[${i}].fec`);
|
|
18819
|
+
if (fecUnavailable === void 0) continue;
|
|
18820
|
+
fecUnavailableByTrack.set(track.name, fecUnavailable);
|
|
18821
|
+
const layerZero = track.fec.repairTrack;
|
|
18822
|
+
if (typeof layerZero === "string" && layerZero.length > 0) {
|
|
18823
|
+
const names = repairNamesBySource.get(track.name) ?? /* @__PURE__ */ new Set();
|
|
18824
|
+
names.add(layerZero);
|
|
18825
|
+
repairNamesBySource.set(track.name, names);
|
|
18826
|
+
}
|
|
18827
|
+
}
|
|
18828
|
+
const unownedRepairNames = /* @__PURE__ */ new Set();
|
|
18829
|
+
for (const sourceName of fecUnavailableByTrack.keys()) {
|
|
18830
|
+
for (const repairName of repairNamesBySource.get(sourceName) ?? []) {
|
|
18831
|
+
unownedRepairNames.add(repairName);
|
|
18832
|
+
}
|
|
18833
|
+
}
|
|
18739
18834
|
const repairOwnerGroup = /* @__PURE__ */ new Map();
|
|
18740
18835
|
const repairOwnerAltGroup = /* @__PURE__ */ new Map();
|
|
18741
18836
|
for (const track of catalog.tracks) {
|
|
@@ -18788,11 +18883,13 @@ var MulticastManager = class {
|
|
|
18788
18883
|
for (let i = 0; i < catalog.tracks.length; i++) {
|
|
18789
18884
|
const track = catalog.tracks[i];
|
|
18790
18885
|
if (resolvedRoles[i] === "repair" || track.fec === void 0) continue;
|
|
18791
|
-
|
|
18886
|
+
const unavailable = fecUnavailableByTrack.has(track.name);
|
|
18887
|
+
if (!(unavailable ? isFecCatalogGeometry(track.fec) : isFecCatalogConfig(track.fec))) {
|
|
18792
18888
|
throw new Error(
|
|
18793
18889
|
`[MulticastManager] invalid fec config for track '${track.name}'`
|
|
18794
18890
|
);
|
|
18795
18891
|
}
|
|
18892
|
+
if (unavailable) continue;
|
|
18796
18893
|
if (track.packetId !== void 0) fecTrackIndexes.push(i);
|
|
18797
18894
|
}
|
|
18798
18895
|
const fecDecoders = /* @__PURE__ */ new Map();
|
|
@@ -18821,6 +18918,20 @@ var MulticastManager = class {
|
|
|
18821
18918
|
this.#switchingSets.clear();
|
|
18822
18919
|
this.#repairPacketIds.clear();
|
|
18823
18920
|
this.#router.clearRepairPacketIdOwners();
|
|
18921
|
+
const fecPacketIds = new Set([...fecDecoders.keys()].map((i) => catalog.tracks[i].packetId));
|
|
18922
|
+
for (const [packetId, catalogTrackName] of this.#multiTrackFecTracks) {
|
|
18923
|
+
if (fecPacketIds.has(packetId)) continue;
|
|
18924
|
+
if (!this.#fec.isTrackConfigured(packetId)) {
|
|
18925
|
+
this.#multiTrackFecTracks.delete(packetId);
|
|
18926
|
+
continue;
|
|
18927
|
+
}
|
|
18928
|
+
this.#router.releaseFecPacket(packetId);
|
|
18929
|
+
this.#releaseRetiredStream({ packetId, catalogTrackName });
|
|
18930
|
+
}
|
|
18931
|
+
this.#fecUnavailableByTrack.clear();
|
|
18932
|
+
for (const [name, reason] of fecUnavailableByTrack) {
|
|
18933
|
+
this.#fecUnavailableByTrack.set(name, reason);
|
|
18934
|
+
}
|
|
18824
18935
|
const catalogTracksByName = new Map(
|
|
18825
18936
|
catalog.tracks.map((track) => [track.name, track])
|
|
18826
18937
|
);
|
|
@@ -18842,10 +18953,11 @@ var MulticastManager = class {
|
|
|
18842
18953
|
}
|
|
18843
18954
|
continue;
|
|
18844
18955
|
}
|
|
18845
|
-
if (track.fec !== void 0) {
|
|
18956
|
+
if (track.fec !== void 0 && !fecUnavailableByTrack.has(track.name)) {
|
|
18846
18957
|
const fecCfg = track.fec;
|
|
18847
18958
|
if (track.packetId !== void 0) {
|
|
18848
18959
|
for (const repairName of repairNamesBySource.get(track.name) ?? []) {
|
|
18960
|
+
if (unownedRepairNames.has(repairName)) continue;
|
|
18849
18961
|
const repairTrack = catalogTracksByName.get(repairName);
|
|
18850
18962
|
if (repairTrack?.packetId !== void 0) {
|
|
18851
18963
|
this.#router.setRepairPacketIdOwner(repairTrack.packetId, track.packetId);
|
|
@@ -18866,17 +18978,18 @@ var MulticastManager = class {
|
|
|
18866
18978
|
blockProfile,
|
|
18867
18979
|
k: fecCfg.sourceSymbols,
|
|
18868
18980
|
symbolSize: fecCfg.symbolSize,
|
|
18869
|
-
interleaveDepthMs: fecCfg.interleaveDepthMs,
|
|
18981
|
+
interleaveDepthMs: fecCfg.interleaveDepthMs ?? 0,
|
|
18870
18982
|
fps: track.framerate,
|
|
18871
18983
|
trackKind: "video"
|
|
18872
18984
|
},
|
|
18873
18985
|
track.name
|
|
18874
18986
|
);
|
|
18987
|
+
this.#multiTrackFecTracks.set(track.packetId, track.name);
|
|
18875
18988
|
this.#configureTrackMapper(track.packetId, {
|
|
18876
18989
|
blockProfile,
|
|
18877
18990
|
k: fecCfg.sourceSymbols,
|
|
18878
18991
|
symbolSize: fecCfg.symbolSize,
|
|
18879
|
-
interleaveDepthMs: fecCfg.interleaveDepthMs,
|
|
18992
|
+
interleaveDepthMs: fecCfg.interleaveDepthMs ?? 0,
|
|
18880
18993
|
fps: track.framerate
|
|
18881
18994
|
});
|
|
18882
18995
|
this.#router.setPacketIdType(track.packetId, "video");
|
|
@@ -18884,6 +18997,24 @@ var MulticastManager = class {
|
|
|
18884
18997
|
}
|
|
18885
18998
|
}
|
|
18886
18999
|
}
|
|
19000
|
+
/**
|
|
19001
|
+
* Why `configureMultiTrack` consumes the named catalog track without FEC
|
|
19002
|
+
* although its `fec` block declares one (draft-ramadan-moq-fec §5.1): no
|
|
19003
|
+
* implemented `blockProfile` (`track_fec_block_profile_unimplemented`, e.g.
|
|
19004
|
+
* the retired `k-padded-v1`; `track_fec_block_profile` when absent), or an
|
|
19005
|
+
* invalid `iso-ssbg1-v1` span (`track_fec`). Absent/zero spans keep in-band
|
|
19006
|
+
* decoding enabled. The same reason the hang
|
|
19007
|
+
* resolver reports (`catalogFecUnavailable`). Undefined for every other
|
|
19008
|
+
* track, and for every track before the first call; each call replaces the
|
|
19009
|
+
* previous call's answers.
|
|
19010
|
+
*/
|
|
19011
|
+
trackFecUnavailable(trackName) {
|
|
19012
|
+
return this.#fecUnavailableByTrack.get(trackName);
|
|
19013
|
+
}
|
|
19014
|
+
/** Per-track FEC availability from the last `configureMultiTrack` call. */
|
|
19015
|
+
#fecUnavailableByTrack = /* @__PURE__ */ new Map();
|
|
19016
|
+
/** packetId → catalog track name of the FEC the last `configureMultiTrack` installed. */
|
|
19017
|
+
#multiTrackFecTracks = /* @__PURE__ */ new Map();
|
|
18887
19018
|
/**
|
|
18888
19019
|
* Return the track names belonging to the given altGroup switching set.
|
|
18889
19020
|
* D-17: altGroup groups base+delta+repair into one switching set.
|