@blockcast/mmt-container 0.3.0 → 0.4.0-main.1cd3cdea89da
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 +33 -16
- package/dist/browser-runtime.js +14814 -14255
- package/dist/browser-runtime.js.map +4 -4
- package/dist/carp-catalog.d.ts +24 -19
- package/dist/carp-catalog.d.ts.map +1 -1
- package/dist/carp-catalog.js +29 -14
- package/dist/carp-catalog.js.map +4 -4
- 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 +60 -23
- package/dist/catalog-fec-params.js.map +4 -4
- package/dist/catalog-network-source.js +1 -7
- package/dist/catalog-network-source.js.map +4 -4
- 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/cmcd-producer.d.ts.map +1 -1
- package/dist/cmcd-producer.js +0 -4
- package/dist/cmcd-producer.js.map +2 -2
- package/dist/delivery-catalog.d.ts +124 -0
- package/dist/delivery-catalog.d.ts.map +1 -0
- package/dist/delivery-catalog.js +1857 -0
- package/dist/delivery-catalog.js.map +7 -0
- 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 +52 -30
- package/dist/fec-manager.d.ts.map +1 -1
- package/dist/fec-manager.js +338 -293
- package/dist/fec-manager.js.map +4 -4
- package/dist/fec-state.d.ts +19 -31
- package/dist/fec-state.d.ts.map +1 -1
- package/dist/fec-state.js +4 -10
- package/dist/fec-state.js.map +4 -4
- package/dist/fec-worker-setup.d.ts +18 -14
- package/dist/fec-worker-setup.d.ts.map +1 -1
- package/dist/fec-worker-setup.js +81 -96
- package/dist/fec-worker-setup.js.map +4 -4
- package/dist/index.d.ts +5 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +16 -0
- package/dist/index.js.map +2 -2
- package/dist/iso-block-locator.js +1 -7
- package/dist/iso-block-locator.js.map +4 -4
- 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 +24 -30
- package/dist/mfu-reassembler.js.map +4 -4
- package/dist/mmtp-container-parser.d.ts +14 -11
- package/dist/mmtp-container-parser.d.ts.map +1 -1
- package/dist/mmtp-container-parser.js +126 -99
- package/dist/mmtp-container-parser.js.map +4 -4
- package/dist/mmtp-container-parser.no-modules.js +151 -108
- package/dist/mmtp-container-parser.no-modules.js.map +4 -4
- package/dist/mmtp-decode-pipeline.js +126 -99
- package/dist/mmtp-decode-pipeline.js.map +4 -4
- package/dist/mmtp-router.d.ts +51 -29
- package/dist/mmtp-router.d.ts.map +1 -1
- package/dist/mmtp-router.js +260 -326
- package/dist/mmtp-router.js.map +4 -4
- package/dist/multicast-input.js +1 -7
- package/dist/multicast-input.js.map +4 -4
- package/dist/multicast-manager.d.ts +72 -6
- package/dist/multicast-manager.d.ts.map +1 -1
- package/dist/multicast-manager.js +882 -673
- 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 +44 -26
- package/dist/multicast-transport.js.map +4 -4
- package/dist/multicast-unwrap.js +29 -35
- package/dist/multicast-unwrap.js.map +4 -4
- package/dist/no-modules.js +147 -106
- package/dist/no-modules.js.map +4 -4
- package/dist/repair-fetch.d.ts +116 -21
- package/dist/repair-fetch.d.ts.map +1 -1
- package/dist/repair-fetch.js +114 -34
- package/dist/repair-fetch.js.map +4 -4
- package/dist/repair-layers.d.ts +18 -7
- package/dist/repair-layers.d.ts.map +1 -1
- package/dist/repair-layers.js +12 -10
- package/dist/repair-layers.js.map +4 -4
- package/dist/signaling.js +2 -11
- package/dist/signaling.js.map +4 -4
- package/dist/timestamp-codec.js +24 -30
- package/dist/timestamp-codec.js.map +4 -4
- 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 +1 -7
- package/dist/wasm-mfu-reassembler.js.map +4 -4
- package/dist/wasm-mmtp-container-parser.js +126 -99
- package/dist/wasm-mmtp-container-parser.js.map +4 -4
- package/package.json +7 -14
- package/schemas/catalog.schema.json +3 -3
- package/src/carp-catalog.ts +46 -24
- package/src/catalog-fec-params.ts +173 -64
- package/src/cmaf-block-assembler.ts +1 -4
- package/src/cmcd-producer.ts +0 -6
- package/src/delivery-catalog.ts +498 -0
- package/src/delta-fallback-config.ts +72 -0
- package/src/fec-manager.ts +457 -334
- package/src/fec-state.ts +20 -32
- package/src/fec-worker-setup.ts +109 -90
- package/src/index.ts +32 -0
- package/src/media-router.ts +59 -21
- package/src/mmtp-container-parser.ts +106 -20
- package/src/mmtp-router.ts +189 -330
- package/src/multicast-manager.ts +265 -86
- package/src/multicast-transport.ts +16 -9
- package/src/repair-fetch.ts +273 -43
- package/src/repair-layers.ts +28 -8
- package/src/track-session.ts +28 -9
- package/test-vectors/catalog/README.md +37 -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-endpoint-relay-address.json +39 -0
- 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-amt-manual-relay.json +38 -0
- 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/test-vectors/delivery-catalog/README.md +11 -0
- package/test-vectors/delivery-catalog/negative/missing-block-profile.json +58 -0
- package/test-vectors/delivery-catalog/negative/packet-id-signaling.json +59 -0
- package/test-vectors/delivery-catalog/negative/retired-k-padded-block-profile.json +59 -0
- package/test-vectors/delivery-catalog/positive/dash.json +56 -0
- package/test-vectors/delivery-catalog/positive/mmtp-source-only.json +43 -0
- 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
package/src/fec-manager.ts
CHANGED
|
@@ -9,20 +9,18 @@
|
|
|
9
9
|
* MMTP packet parsing and frame routing.
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
|
-
import { computeFecDeliveryWindowMs } from './track-session.js'
|
|
12
|
+
import { computeFecDeliveryWindowMs, fecSpanDeliveryWindowMs } from './track-session.js'
|
|
13
13
|
import { DeliveryGapEstimator } from './delivery-gap.js'
|
|
14
14
|
import {
|
|
15
15
|
deriveFecInterleaveDepth,
|
|
16
16
|
deriveFecRetentionBlocks,
|
|
17
17
|
tryDeriveFecRetentionBlocks,
|
|
18
|
+
LOCAL_FEC_RETENTION_BLOCKS,
|
|
19
|
+
LOCAL_FEC_TIMEOUT_MS,
|
|
18
20
|
} from '@blockcast/mmt-fec'
|
|
19
21
|
import {
|
|
20
|
-
isKStrideBlock,
|
|
21
22
|
isoBlockContains,
|
|
22
23
|
isoBlocksBehindLowerBound,
|
|
23
|
-
kStrideBlockPrecedes,
|
|
24
|
-
kStrideBlocksBehind,
|
|
25
|
-
kStrideNextBlock,
|
|
26
24
|
parseFecBlockProfile,
|
|
27
25
|
serial32Distance,
|
|
28
26
|
serial32Precedes,
|
|
@@ -86,7 +84,7 @@ export type WasmFecDecoder = {
|
|
|
86
84
|
configure(oti: Uint8Array): void
|
|
87
85
|
cleanup(current_time: number): Uint8Array
|
|
88
86
|
/** Retire the named blocks from the decoder (last-chance decode + stats). */
|
|
89
|
-
cleanup_blocks_detailed(
|
|
87
|
+
cleanup_blocks_detailed(ss_starts: Uint32Array): unknown
|
|
90
88
|
/**
|
|
91
89
|
* iso-ssbg1-v1 only: retire the source symbols the decoder holds before a
|
|
92
90
|
* repair states their block, once no repair can place them any more, by the
|
|
@@ -112,7 +110,8 @@ export interface FecStats {
|
|
|
112
110
|
blocksComplete: number
|
|
113
111
|
mixedPathBlocks: number
|
|
114
112
|
recentBlockPathCounts: FecBlockPathCount[]
|
|
115
|
-
k
|
|
113
|
+
/** The stream-global K a configure() call stated; undefined when none did. Per-track K is tracks[].k. */
|
|
114
|
+
k: number | undefined
|
|
116
115
|
/** Of blocks with loss, what percentage were recovered (0-100). */
|
|
117
116
|
recoveryRate: number
|
|
118
117
|
/** What percentage of blocks had any loss (0-100). */
|
|
@@ -136,7 +135,8 @@ export interface FecPacketStats {
|
|
|
136
135
|
blocksRecovered: number
|
|
137
136
|
blocksFailed: number
|
|
138
137
|
blocksComplete: number
|
|
139
|
-
|
|
138
|
+
/** The track's configured K; undefined for a packetId never configured (it does no FEC work). */
|
|
139
|
+
k: number | undefined
|
|
140
140
|
recoveryRate: number
|
|
141
141
|
packetLossRate: number
|
|
142
142
|
}
|
|
@@ -217,10 +217,11 @@ type FecLifecycleTrace = {
|
|
|
217
217
|
export interface FECManagerConfig {
|
|
218
218
|
/**
|
|
219
219
|
* The catalog `fec.blockProfile` of the track (BLO-35597). Required for every
|
|
220
|
-
* configured track: there is no default, and nothing infers it.
|
|
221
|
-
* `
|
|
222
|
-
*
|
|
223
|
-
*
|
|
220
|
+
* configured track: there is no default, and nothing infers it. The only
|
|
221
|
+
* profile is `iso-ssbg1-v1`: ISO/IEC 23008-1 ssbg_mode1 with K' = SSB_length
|
|
222
|
+
* blocks keyed by SS_Start and located by `(SS_Start, SSB_length)` range
|
|
223
|
+
* membership, `k` being only their maximum. The retired `k-padded-v1` token
|
|
224
|
+
* is refused.
|
|
224
225
|
*/
|
|
225
226
|
blockProfile?: FecBlockProfile
|
|
226
227
|
k?: number
|
|
@@ -373,7 +374,6 @@ export interface FecDecoderUnavailableEvent {
|
|
|
373
374
|
export class FECManager {
|
|
374
375
|
// Per-track WASM RaptorQ decoders, keyed by physical stream identity.
|
|
375
376
|
#fecDecoders = new Map<string, { client: WasmFecDecoder; k: number }>()
|
|
376
|
-
/** @deprecated Use setFecDecoder(packetId, ...). Backwards-compat shim. */
|
|
377
377
|
|
|
378
378
|
// Per-track Worker-backed FEC decoders, keyed by physical stream identity.
|
|
379
379
|
// Worker-backed tracks use async decode — recovered frames arrive via onFrame callback,
|
|
@@ -393,9 +393,12 @@ export class FECManager {
|
|
|
393
393
|
conflicted: boolean
|
|
394
394
|
}>>()
|
|
395
395
|
|
|
396
|
-
|
|
396
|
+
// The stream-global K a configure() call stated, reported as fecStats.k.
|
|
397
|
+
// It never resolves a packetId: a packetId has only its own track config
|
|
398
|
+
// (BLO-45953), and K does not feed the delivery-window math.
|
|
399
|
+
#streamK: number | undefined
|
|
397
400
|
#fecSymbolSize = 0
|
|
398
|
-
#fecTimeoutMs =
|
|
401
|
+
#fecTimeoutMs = LOCAL_FEC_TIMEOUT_MS // local default; refined by setFramerate() when fps known
|
|
399
402
|
#trackConfigs = new Map<number, ResolvedFECTrackConfig>()
|
|
400
403
|
#fecStats = newFecCounters()
|
|
401
404
|
#fecTrackStats = new Map<number, MutableFecCounters>()
|
|
@@ -436,14 +439,17 @@ export class FECManager {
|
|
|
436
439
|
#blockSourceEsis = new Map<string, Set<number>>()
|
|
437
440
|
// Authored SSB_length per block (ISO/IEC 23008-1 §C.5.3), learned from the
|
|
438
441
|
// first repair symbol. The FFmpeg moq_mmt publisher closes a block every D
|
|
439
|
-
// media units, usually before it holds K symbols
|
|
440
|
-
//
|
|
441
|
-
// SSB_length
|
|
442
|
-
//
|
|
442
|
+
// media units, usually before it holds K symbols; under iso-ssbg1-v1
|
|
443
|
+
// (ssbg_mode1) that block is K' = SSB_length symbols, its repair ESIs start
|
|
444
|
+
// at SSB_length, and K is only the maximum. Such a block is clean at
|
|
445
|
+
// SSB_length source arrivals and recoverable from SSB_length received
|
|
443
446
|
// symbols; waiting for K fails every one of them.
|
|
444
447
|
#blockAuthoredLengths = new Map<string, number>()
|
|
445
448
|
#ssbLengthConflictCount = 0
|
|
446
449
|
#blockRepairRsIds = new Map<string, Set<number>>()
|
|
450
|
+
// RSB_length per block (C.5.3 Repair FEC Payload ID): how many repair
|
|
451
|
+
// symbols its repair group carries. Same lifecycle as #blockRepairRsIds.
|
|
452
|
+
#blockRsbLengths = new Map<string, number>()
|
|
447
453
|
// Blocks already recovered — prevents late source symbols from restarting timeouts.
|
|
448
454
|
// WASM removes blocks after recovery; late symbols create empty blocks returning null,
|
|
449
455
|
// which would falsely start new timeouts without this guard.
|
|
@@ -558,14 +564,16 @@ export class FECManager {
|
|
|
558
564
|
#resolvedBlockClasses = new Map<string, BlockClass>()
|
|
559
565
|
// Blocks already reported through onBlockRepaired (once per block).
|
|
560
566
|
#repairedBlocks = new Set<string>()
|
|
561
|
-
// Blocks created
|
|
567
|
+
// Blocks created before any symbol of theirs was tracked: the repair-less
|
|
568
|
+
// spans placeIsoRepair materializes (onBlockVanished).
|
|
562
569
|
#materializedBlocks = new Set<string>()
|
|
563
570
|
// packetIds whose ordered-relay horizon became underivable and fell back to
|
|
564
571
|
// the datagram deadline. Bounds the warn in #derivableOrderedHorizon to once
|
|
565
572
|
// per stream. Not only throws: the ordinary out-of-range case is detected by
|
|
566
573
|
// arithmetic and never throws at all.
|
|
567
574
|
#orderedHorizonWarnedFor = new Set<number>()
|
|
568
|
-
//
|
|
575
|
+
// Newest block start (SS_Start) seen per stream and when: the frontier for
|
|
576
|
+
// source-discontinuity detection and isoSpanResolution.
|
|
569
577
|
#highestSbn = new Map<string, { sbn: number; atMs: number }>()
|
|
570
578
|
// iso-ssbg1-v1 block location per stream (streamKey). A source belongs to a
|
|
571
579
|
// block only once a repair symbol stated the block's (SS_Start, SSB_length).
|
|
@@ -829,7 +837,8 @@ export class FECManager {
|
|
|
829
837
|
this.#completedBlocks.add(key)
|
|
830
838
|
if (clean) this.#cleanlyCompletedBlocks.add(key)
|
|
831
839
|
this.#indexBlockKey(key, sbn, packetId)
|
|
832
|
-
const
|
|
840
|
+
const config = this.#trackConfig(packetId)
|
|
841
|
+
const expiresAtMs = nowMs + config.interleaveDepthMs + 2 * this.#blockLifetimeMs(config)
|
|
833
842
|
this.#terminalExpiryMs.set(key, expiresAtMs)
|
|
834
843
|
let queue = this.#terminalQueues.get(packetId)
|
|
835
844
|
if (!queue) {
|
|
@@ -877,8 +886,9 @@ export class FECManager {
|
|
|
877
886
|
* retention the Worker and the WASM decoder are configured with, because it
|
|
878
887
|
* comes from the same function they do (`deriveFecRetentionBlocks`, over the
|
|
879
888
|
* block's delivery window and its span). The decoder retires every block
|
|
880
|
-
* more than this many
|
|
881
|
-
*
|
|
889
|
+
* more than this many blocks behind the newest SS_Start it saw, counted
|
|
890
|
+
* from below by `isoBlocksBehindLowerBound` (`cleanup_by_ss_start`), so
|
|
891
|
+
* settling earlier than this would decide a block the decoder would still
|
|
882
892
|
* have recovered.
|
|
883
893
|
*
|
|
884
894
|
* This used to call `deriveFecInterleaveDepth`, which returns D -- MEDIA
|
|
@@ -891,6 +901,18 @@ export class FECManager {
|
|
|
891
901
|
* derivation; do not re-derive a horizon at either call site.
|
|
892
902
|
*/
|
|
893
903
|
#orderedHorizonBlocksFor(packetId: number, config: ResolvedFECTrackConfig): number {
|
|
904
|
+
// An absent/zero catalog span has no elapsed-time horizon. Ordered relay
|
|
905
|
+
// still needs a bounded local cleanup distance, so use the shared local
|
|
906
|
+
// count without treating it as a catalog-derived deadline.
|
|
907
|
+
if (config.interleaveDepthMs === 0) {
|
|
908
|
+
if (Number.isFinite(config.timeoutMs) && config.timeoutMs > 0) {
|
|
909
|
+
return LOCAL_FEC_RETENTION_BLOCKS
|
|
910
|
+
}
|
|
911
|
+
throw new Error(
|
|
912
|
+
`[FECManager] packetId=${packetId}: local FEC cleanup requires a positive timeout ` +
|
|
913
|
+
`(got ${config.timeoutMs})`,
|
|
914
|
+
)
|
|
915
|
+
}
|
|
894
916
|
if (!FECManager.#hasOrderedGeometry(config)) {
|
|
895
917
|
throw new Error(
|
|
896
918
|
`[FECManager] packetId=${packetId}: ordered-relay settlement derives the ` +
|
|
@@ -913,7 +935,7 @@ export class FECManager {
|
|
|
913
935
|
*/
|
|
914
936
|
static #hasOrderedGeometry(config: ResolvedFECTrackConfig): boolean {
|
|
915
937
|
return (
|
|
916
|
-
config
|
|
938
|
+
FECManager.#hasCatalogTiming(config) &&
|
|
917
939
|
// `fps` is no longer an input to the horizon, but it still gates WHICH
|
|
918
940
|
// streams settle by stream order at all: a track with no usable
|
|
919
941
|
// media-unit rate fell back to datagram semantics before this and must
|
|
@@ -960,6 +982,11 @@ export class FECManager {
|
|
|
960
982
|
* argument: narrower IS the failing direction.
|
|
961
983
|
*/
|
|
962
984
|
#derivableOrderedHorizon(config: ResolvedFECTrackConfig, packetId: number): number | undefined {
|
|
985
|
+
if (config.interleaveDepthMs === 0) {
|
|
986
|
+
return Number.isFinite(config.timeoutMs) && config.timeoutMs > 0
|
|
987
|
+
? LOCAL_FEC_RETENTION_BLOCKS
|
|
988
|
+
: undefined
|
|
989
|
+
}
|
|
963
990
|
if (!FECManager.#hasOrderedGeometry(config)) return undefined
|
|
964
991
|
try {
|
|
965
992
|
// `tryDeriveFecRetentionBlocks`, not the throwing variant: this is the
|
|
@@ -1016,19 +1043,20 @@ export class FECManager {
|
|
|
1016
1043
|
/**
|
|
1017
1044
|
* The stream's interleave horizon, or undefined when its geometry cannot
|
|
1018
1045
|
* define one. Two different streams reach that case, with different causes:
|
|
1019
|
-
* a packetId that was never configured,
|
|
1020
|
-
*
|
|
1046
|
+
* a packetId that was never configured, which has no geometry at all
|
|
1047
|
+
* (explicitly configured tracks are refused an underivable geometry
|
|
1021
1048
|
* at config time — #upsertTrackConfig, setMulticastSymbolDelivery); or a
|
|
1022
1049
|
* configured track whose timeoutMs was later widened past the 255-block
|
|
1023
1050
|
* ceiling without re-validating, which #derivableOrderedHorizon catches
|
|
1024
1051
|
* rather than throwing from the packet-feed path. Either way the stream
|
|
1025
|
-
* keeps datagram semantics instead of throwing.
|
|
1052
|
+
* keeps datagram semantics instead of throwing. A track's configuration can
|
|
1026
1053
|
* also change under a stream that already holds blocks, so a caller cannot
|
|
1027
1054
|
* assume a horizon that was defined when a block was held is still defined
|
|
1028
1055
|
* (see #advanceOrderedFrontier).
|
|
1029
1056
|
*/
|
|
1030
1057
|
#orderedHorizonBlocks(packetId: number): number | undefined {
|
|
1031
|
-
|
|
1058
|
+
const config = this.#trackConfigs.get(packetId)
|
|
1059
|
+
return config === undefined ? undefined : this.#derivableOrderedHorizon(config, packetId)
|
|
1032
1060
|
}
|
|
1033
1061
|
|
|
1034
1062
|
/** True while a block of an ordered-relay stream can still receive symbols. */
|
|
@@ -1073,7 +1101,7 @@ export class FECManager {
|
|
|
1073
1101
|
catalogTrackName?: string,
|
|
1074
1102
|
): void {
|
|
1075
1103
|
const previous = this.#orderedFrontiers.get(streamKey)
|
|
1076
|
-
const frontier = previous === undefined || this.#blockPrecedes(previous, sbn
|
|
1104
|
+
const frontier = previous === undefined || this.#blockPrecedes(previous, sbn)
|
|
1077
1105
|
? sbn
|
|
1078
1106
|
: previous
|
|
1079
1107
|
if (frontier !== previous) this.#orderedFrontiers.set(streamKey, frontier)
|
|
@@ -1081,10 +1109,9 @@ export class FECManager {
|
|
|
1081
1109
|
if (!pending) return
|
|
1082
1110
|
const blocks = this.#orderedHorizonBlocks(packetId)
|
|
1083
1111
|
if (blocks === undefined) {
|
|
1084
|
-
//
|
|
1085
|
-
//
|
|
1086
|
-
// one
|
|
1087
|
-
// validation (#derivableOrderedHorizon). Stream
|
|
1112
|
+
// A configured stream whose timeoutMs was widened past the 255-block
|
|
1113
|
+
// ceiling after validation (#derivableOrderedHorizon); an unconfigured
|
|
1114
|
+
// one never gets here (feedSymbolFromSource refuses it). Stream
|
|
1088
1115
|
// order can no longer settle its held blocks, so hand them to the
|
|
1089
1116
|
// datagram deadline -- as setMulticastSymbolDelivery('datagram') does --
|
|
1090
1117
|
// rather than settle them a horizon early or hold them with no timer.
|
|
@@ -1130,6 +1157,7 @@ export class FECManager {
|
|
|
1130
1157
|
(this.#blockSourceCounts.get(key) ?? 0) +
|
|
1131
1158
|
(this.#blockRepairRsIds.get(key)?.size ?? 0)
|
|
1132
1159
|
if (
|
|
1160
|
+
this.#hasCatalogTimingForPacket(packetId) &&
|
|
1133
1161
|
this.onRepairNeeded &&
|
|
1134
1162
|
(authoredLength === undefined || received < authoredLength)
|
|
1135
1163
|
) {
|
|
@@ -1343,6 +1371,7 @@ export class FECManager {
|
|
|
1343
1371
|
const key = this.#blockKey(sbn, packetId, catalogTrackName)
|
|
1344
1372
|
if (
|
|
1345
1373
|
this.#completedBlocks.has(key) ||
|
|
1374
|
+
!this.#hasCatalogTimingForPacket(packetId) ||
|
|
1346
1375
|
!this.onRepairNeeded ||
|
|
1347
1376
|
this.#repairRequestedBlocks.has(key)
|
|
1348
1377
|
) return
|
|
@@ -1450,6 +1479,7 @@ export class FECManager {
|
|
|
1450
1479
|
|
|
1451
1480
|
/** A block's live class without creating classifier state for the stream. */
|
|
1452
1481
|
#peekClass(sbn: number, packetId: number, catalogTrackName?: string): BlockClass {
|
|
1482
|
+
if (!this.#hasCatalogTimingForPacket(packetId)) return 'unknown'
|
|
1453
1483
|
const classifier = this.#classifiers.get(this.#streamKey(packetId, catalogTrackName))
|
|
1454
1484
|
if (classifier) return classifier.classOf(sbn)
|
|
1455
1485
|
return this.#geometryFor(packetId, catalogTrackName).trackKind === 'audio' ? 'audio' : 'unknown'
|
|
@@ -1479,8 +1509,8 @@ export class FECManager {
|
|
|
1479
1509
|
// Deliberately one block tighter than deriveFecRetentionBlocks over the same
|
|
1480
1510
|
// inputs (that function's `+ 1`). Different question: the retention horizon
|
|
1481
1511
|
// asks how long the decoder still ACCEPTS symbols for a block, this asks how
|
|
1482
|
-
//
|
|
1483
|
-
// #noteSbnSeen).
|
|
1512
|
+
// many blocks a stream can live behind (or ahead of) its frontier before a
|
|
1513
|
+
// source symbol there is a discontinuity rather than loss (see #noteSbnSeen).
|
|
1484
1514
|
// The extra block of retention is slack for a block still being fed; a block
|
|
1485
1515
|
// nothing has been heard from does not get it. Not the units defect — but it
|
|
1486
1516
|
// is the next place the two can drift, so change both or neither.
|
|
@@ -1490,27 +1520,28 @@ export class FECManager {
|
|
|
1490
1520
|
}
|
|
1491
1521
|
|
|
1492
1522
|
/**
|
|
1493
|
-
* Record that a symbol for block `sbn`
|
|
1494
|
-
*
|
|
1523
|
+
* Record that a symbol for block `sbn` (the block's SS_Start) arrived and
|
|
1524
|
+
* advance the stream's frontier, the newest block start seen.
|
|
1495
1525
|
*
|
|
1496
|
-
*
|
|
1497
|
-
*
|
|
1498
|
-
*
|
|
1499
|
-
*
|
|
1500
|
-
*
|
|
1501
|
-
* its last symbol, is a source discontinuity, not loss. A repair symbol can
|
|
1502
|
-
* only advance the frontier: a D-28 fetch legitimately answers for an old
|
|
1503
|
-
* block, so it never signals a discontinuity.
|
|
1526
|
+
* A SOURCE symbol whose block goes back further than one live window, or
|
|
1527
|
+
* jumps further than the stream could have produced since its last symbol,
|
|
1528
|
+
* is a source discontinuity, not loss. A repair symbol can only advance the
|
|
1529
|
+
* frontier: a D-28 fetch legitimately answers for an old block, so it never
|
|
1530
|
+
* signals a discontinuity.
|
|
1504
1531
|
*
|
|
1505
1532
|
* The producer closes a block at its Dth media-unit boundary OR when K
|
|
1506
|
-
* source symbols filled it, so one interleave span can start several
|
|
1507
|
-
*
|
|
1508
|
-
*
|
|
1509
|
-
*
|
|
1510
|
-
*
|
|
1511
|
-
*
|
|
1512
|
-
*
|
|
1513
|
-
*
|
|
1533
|
+
* source symbols filled it, so one interleave span can start several blocks
|
|
1534
|
+
* (every large keyframe does). What bounds a forward jump is therefore
|
|
1535
|
+
* packet evidence, not a block rate: SS_ID advances by one per FEC source
|
|
1536
|
+
* packet (ISO/IEC 23008-1 C.5.2) and the MMTP packet_sequence_number by one
|
|
1537
|
+
* per packet of the packet_id, so with both source symbols'
|
|
1538
|
+
* packet_sequence_number a block start can move at most the packets sent
|
|
1539
|
+
* between them plus the anchor's own offset into its block (< K). Without
|
|
1540
|
+
* it, only the D-close rate is known.
|
|
1541
|
+
*
|
|
1542
|
+
* Vanished blocks are not enumerated here: their starts are unknown until a
|
|
1543
|
+
* repair states them. The SS_IDs between two stated blocks become one
|
|
1544
|
+
* repair-less span when the later block's repair arrives (placeIsoRepair).
|
|
1514
1545
|
*/
|
|
1515
1546
|
#noteSbnSeen(
|
|
1516
1547
|
sbn: number,
|
|
@@ -1531,7 +1562,7 @@ export class FECManager {
|
|
|
1531
1562
|
? packetSequenceNumber
|
|
1532
1563
|
: undefined
|
|
1533
1564
|
const anchorHere = (): void => {
|
|
1534
|
-
if (psn !== undefined && (anchor === undefined || !this.#blockPrecedes(sbn, anchor.sbn
|
|
1565
|
+
if (psn !== undefined && (anchor === undefined || !this.#blockPrecedes(sbn, anchor.sbn))) {
|
|
1535
1566
|
this.#sourceAnchors.set(streamKey, { sbn, psn, atMs: now })
|
|
1536
1567
|
}
|
|
1537
1568
|
}
|
|
@@ -1549,9 +1580,9 @@ export class FECManager {
|
|
|
1549
1580
|
anchorHere()
|
|
1550
1581
|
return
|
|
1551
1582
|
}
|
|
1552
|
-
const config = this.#
|
|
1583
|
+
const config = this.#trackConfig(packetId)
|
|
1553
1584
|
const window = this.#liveWindowBlocks(config)
|
|
1554
|
-
if (!this.#blockPrecedes(previous.sbn, sbn
|
|
1585
|
+
if (!this.#blockPrecedes(previous.sbn, sbn)) {
|
|
1555
1586
|
if (symbol === 'source' && window !== null &&
|
|
1556
1587
|
this.#blocksBehind(sbn, previous.sbn, packetId) > window) {
|
|
1557
1588
|
discontinuity()
|
|
@@ -1566,30 +1597,16 @@ export class FECManager {
|
|
|
1566
1597
|
// A packet_sequence_number that did not advance says nothing (a
|
|
1567
1598
|
// producer that does not maintain it); only then is the D-close rate
|
|
1568
1599
|
// the whole bound.
|
|
1569
|
-
if (
|
|
1570
|
-
this.#blockPrecedes(anchor.sbn, sbn
|
|
1571
|
-
//
|
|
1572
|
-
//
|
|
1573
|
-
//
|
|
1574
|
-
//
|
|
1600
|
+
if (psn !== undefined && anchor !== undefined &&
|
|
1601
|
+
this.#blockPrecedes(anchor.sbn, sbn) && psn !== anchor.psn) {
|
|
1602
|
+
// A block start can move at most the packets sent since the anchor,
|
|
1603
|
+
// plus the anchor's own offset into its block (< K). Packets sent
|
|
1604
|
+
// from the anchor packet to this one, serial arithmetic (RFC 1982):
|
|
1605
|
+
// more than half the space means it went backwards.
|
|
1575
1606
|
const sent = (psn - anchor.psn) >>> 0
|
|
1576
1607
|
plausible =
|
|
1577
1608
|
sent <= 0x7fff_ffff &&
|
|
1578
1609
|
serial32Distance(anchor.sbn, sbn) <= sent + config.k
|
|
1579
|
-
} else if (psn !== undefined && anchor !== undefined &&
|
|
1580
|
-
this.#blockPrecedes(anchor.sbn, sbn, packetId) && psn !== anchor.psn) {
|
|
1581
|
-
// Packets sent from the anchor packet to this one, serial arithmetic
|
|
1582
|
-
// (RFC 1982): more than half the space means it went backwards.
|
|
1583
|
-
const sent = (psn - anchor.psn) >>> 0
|
|
1584
|
-
const between = this.#blocksBehind(anchor.sbn, sbn, packetId) - 1
|
|
1585
|
-
const dCloses = window + Math.ceil((now - anchor.atMs) / config.interleaveDepthMs)
|
|
1586
|
-
// Each K-close consumes K of the packets sent; with K unknown,
|
|
1587
|
-
// every packet may have closed one.
|
|
1588
|
-
const kCloses = config.k > 0 ? Math.ceil(sent / config.k) : sent
|
|
1589
|
-
plausible =
|
|
1590
|
-
sent <= 0x7fff_ffff &&
|
|
1591
|
-
between <= sent - 1 &&
|
|
1592
|
-
between <= dCloses + kCloses
|
|
1593
1610
|
} else {
|
|
1594
1611
|
plausible = missing <= window + Math.ceil((now - previous.atMs) / config.interleaveDepthMs)
|
|
1595
1612
|
}
|
|
@@ -1601,53 +1618,25 @@ export class FECManager {
|
|
|
1601
1618
|
}
|
|
1602
1619
|
this.#highestSbn.set(streamKey, { sbn, atMs: now })
|
|
1603
1620
|
anchorHere()
|
|
1604
|
-
// iso-ssbg1-v1 cannot enumerate vanished blocks by key: their starts are
|
|
1605
|
-
// unknown. The SS_IDs between two stated blocks become one repair-less
|
|
1606
|
-
// span when the later block's repair arrives (placeIsoRepair).
|
|
1607
|
-
if (this.#isIso(packetId)) return
|
|
1608
|
-
if (missing <= 0 || window === null) return
|
|
1609
|
-
let vanished = previous.sbn
|
|
1610
|
-
for (let i = 0; i < missing; i++) {
|
|
1611
|
-
vanished = this.#nextBlock(vanished, packetId)
|
|
1612
|
-
this.#materializeBlock(
|
|
1613
|
-
vanished,
|
|
1614
|
-
packetId,
|
|
1615
|
-
catalogTrackName,
|
|
1616
|
-
this.#blocksBehind(vanished, sbn, packetId) <= window,
|
|
1617
|
-
)
|
|
1618
|
-
}
|
|
1619
1621
|
}
|
|
1620
1622
|
|
|
1621
1623
|
/*
|
|
1622
|
-
* Block order for one stream (BLO-35597 iso:5).
|
|
1623
|
-
* number (ISO/IEC 23008-1 C.5.2), so
|
|
1624
|
-
*
|
|
1625
|
-
*
|
|
1626
|
-
* the order across the wrap changes. A block outside K-stride geometry has
|
|
1627
|
-
* no SS_ID position, so it keeps the SBN order it always had.
|
|
1624
|
+
* Block order for one stream (BLO-35597 iso:5). A block is keyed by its
|
|
1625
|
+
* SS_Start and SS_ID is a 32-bit serial number (ISO/IEC 23008-1 C.5.2), so
|
|
1626
|
+
* blocks are ordered by RFC 1982 comparison of their starts. Only configured
|
|
1627
|
+
* streams hold blocks: every entry point refuses an unconfigured packetId.
|
|
1628
1628
|
*/
|
|
1629
|
-
#blockPrecedes(a: number, b: number
|
|
1630
|
-
|
|
1631
|
-
if (this.#isIso(packetId)) return serial32Precedes(a, b)
|
|
1632
|
-
const k = this.kForPacket(packetId)
|
|
1633
|
-
return isKStrideBlock(a, k) && isKStrideBlock(b, k) ? kStrideBlockPrecedes(a, b, k) : a < b
|
|
1629
|
+
#blockPrecedes(a: number, b: number): boolean {
|
|
1630
|
+
return serial32Precedes(a, b)
|
|
1634
1631
|
}
|
|
1635
1632
|
|
|
1636
|
-
/**
|
|
1633
|
+
/**
|
|
1634
|
+
* Blocks `a` lies behind `b` (0 when it does not precede it). Blocks vary
|
|
1635
|
+
* in length (K' = SSB_length <= K), so only a lower bound is known: the one
|
|
1636
|
+
* metric the decoder and the worker retire by too.
|
|
1637
|
+
*/
|
|
1637
1638
|
#blocksBehind(a: number, b: number, packetId: number): number {
|
|
1638
|
-
|
|
1639
|
-
// one metric the decoder and the worker retire by too.
|
|
1640
|
-
if (this.#isIso(packetId)) return isoBlocksBehindLowerBound(a, b, this.kForPacket(packetId))
|
|
1641
|
-
const k = this.kForPacket(packetId)
|
|
1642
|
-
return isKStrideBlock(a, k) && isKStrideBlock(b, k)
|
|
1643
|
-
? kStrideBlocksBehind(a, b, k)
|
|
1644
|
-
: Math.max(0, b - a)
|
|
1645
|
-
}
|
|
1646
|
-
|
|
1647
|
-
/** The block after `sbn`, stepping to block 0 where SS_ID wraps. */
|
|
1648
|
-
#nextBlock(sbn: number, packetId: number): number {
|
|
1649
|
-
const k = this.kForPacket(packetId)
|
|
1650
|
-
return isKStrideBlock(sbn, k) ? kStrideNextBlock(sbn, k) : sbn + 1
|
|
1639
|
+
return isoBlocksBehindLowerBound(a, b, this.#trackConfig(packetId).k)
|
|
1651
1640
|
}
|
|
1652
1641
|
|
|
1653
1642
|
#materializeBlock(
|
|
@@ -1659,7 +1648,9 @@ export class FECManager {
|
|
|
1659
1648
|
const key = this.#blockKey(sbn, packetId, catalogTrackName)
|
|
1660
1649
|
if (this.#blockResolutionFor(key) !== 'untracked') return
|
|
1661
1650
|
this.#materializedBlocks.add(key)
|
|
1662
|
-
this.#
|
|
1651
|
+
if (this.#hasCatalogTimingForPacket(packetId)) {
|
|
1652
|
+
this.#classifierFor(packetId, catalogTrackName).touch(sbn)
|
|
1653
|
+
}
|
|
1663
1654
|
this.onBlockVanished?.(sbn, packetId, catalogTrackName)
|
|
1664
1655
|
if (repairable) {
|
|
1665
1656
|
this.noteBlockActivity(sbn, packetId, catalogTrackName, true)
|
|
@@ -1723,18 +1714,37 @@ export class FECManager {
|
|
|
1723
1714
|
if (this.#lifecycleTrace.length > 64) this.#lifecycleTrace.splice(0, 32)
|
|
1724
1715
|
}
|
|
1725
1716
|
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1717
|
+
/**
|
|
1718
|
+
* A configured packetId's track config. There is no manager-wide default
|
|
1719
|
+
* (BLO-45953): an unconfigured packetId does no FEC work, so every caller
|
|
1720
|
+
* here is reached only behind a configuration gate and this throw marks a
|
|
1721
|
+
* broken invariant, never a packet-path outcome.
|
|
1722
|
+
*/
|
|
1723
|
+
#trackConfig(packetId: number): ResolvedFECTrackConfig {
|
|
1724
|
+
const config = this.#trackConfigs.get(packetId)
|
|
1725
|
+
if (config === undefined) {
|
|
1726
|
+
throw new Error(
|
|
1727
|
+
`[FECManager] packetId=${packetId} was never configured; ` +
|
|
1728
|
+
'configure the track from its catalog (configureTrack/setFecWorker/setFecDecoder) first',
|
|
1729
|
+
)
|
|
1733
1730
|
}
|
|
1731
|
+
return config
|
|
1734
1732
|
}
|
|
1735
1733
|
|
|
1736
|
-
|
|
1737
|
-
|
|
1734
|
+
/**
|
|
1735
|
+
* Whether this track has the catalog's elapsed-time FEC policy. A zero span
|
|
1736
|
+
* is a deliberate no-deadline signal; it still leaves the wire geometry
|
|
1737
|
+
* usable for in-band decode, but it cannot drive classification, repair
|
|
1738
|
+
* fetches or layer adaptation.
|
|
1739
|
+
*/
|
|
1740
|
+
static #hasCatalogTiming(config: Pick<ResolvedFECTrackConfig, 'interleaveDepthMs'>): boolean {
|
|
1741
|
+
return Number.isSafeInteger(config.interleaveDepthMs) &&
|
|
1742
|
+
config.interleaveDepthMs >= MIN_INTERLEAVE_DEPTH_MS
|
|
1743
|
+
}
|
|
1744
|
+
|
|
1745
|
+
#hasCatalogTimingForPacket(packetId: number): boolean {
|
|
1746
|
+
const config = this.#trackConfigs.get(packetId)
|
|
1747
|
+
return config !== undefined && FECManager.#hasCatalogTiming(config)
|
|
1738
1748
|
}
|
|
1739
1749
|
|
|
1740
1750
|
#computeTimeout(config: {
|
|
@@ -1743,11 +1753,15 @@ export class FECManager {
|
|
|
1743
1753
|
fps?: number
|
|
1744
1754
|
timeoutMs?: number
|
|
1745
1755
|
}): number {
|
|
1746
|
-
if (config.timeoutMs && config.timeoutMs > 0)
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
//
|
|
1750
|
-
|
|
1756
|
+
if (typeof config.timeoutMs === 'number' && Number.isFinite(config.timeoutMs) && config.timeoutMs > 0) {
|
|
1757
|
+
return config.timeoutMs
|
|
1758
|
+
}
|
|
1759
|
+
// Only a valid non-negative integer §5.1 ms interleave drives the timeout;
|
|
1760
|
+
// zero has no catalog deadline, so retain the local manager default.
|
|
1761
|
+
if (
|
|
1762
|
+
Number.isSafeInteger(config.interleaveDepthMs) &&
|
|
1763
|
+
config.interleaveDepthMs >= MIN_INTERLEAVE_DEPTH_MS
|
|
1764
|
+
) {
|
|
1751
1765
|
return Math.max(
|
|
1752
1766
|
this.#fecTimeoutMs,
|
|
1753
1767
|
computeFecDeliveryWindowMs({
|
|
@@ -1773,14 +1787,17 @@ export class FECManager {
|
|
|
1773
1787
|
*/
|
|
1774
1788
|
#refreshDeliveryGapWindow(): void {
|
|
1775
1789
|
let windowMs: number | undefined
|
|
1776
|
-
const consider = (geometry: BlockClassifierGeometry | undefined): void => {
|
|
1790
|
+
const consider = (geometry: BlockClassifierGeometry | undefined, packetId: number): void => {
|
|
1791
|
+
if (!this.#hasCatalogTimingForPacket(packetId)) return
|
|
1777
1792
|
if (geometry?.trackKind !== 'video' || geometry.mode !== 'classifying') return
|
|
1778
1793
|
const { ticks, timescale } = geometry.keyframeInterval
|
|
1779
1794
|
const ms = (ticks / timescale) * 1000
|
|
1780
1795
|
if (Number.isFinite(ms) && ms > 0 && (windowMs === undefined || ms > windowMs)) windowMs = ms
|
|
1781
1796
|
}
|
|
1782
|
-
for (const geometry of this.#streamGeometries
|
|
1783
|
-
|
|
1797
|
+
for (const [streamKey, geometry] of this.#streamGeometries) {
|
|
1798
|
+
consider(geometry, this.#parseStreamKey(streamKey).packetId)
|
|
1799
|
+
}
|
|
1800
|
+
for (const [packetId, config] of this.#trackConfigs) consider(config.geometry, packetId)
|
|
1784
1801
|
this.#deliveryGaps.setWindowMs(windowMs)
|
|
1785
1802
|
}
|
|
1786
1803
|
|
|
@@ -1815,6 +1832,10 @@ export class FECManager {
|
|
|
1815
1832
|
config: ResolvedFECTrackConfig,
|
|
1816
1833
|
waitMs = this.#multicastWaitMs(config),
|
|
1817
1834
|
): number {
|
|
1835
|
+
// No catalog span means no D-28 repair deadline. The local timeout still
|
|
1836
|
+
// bounds cleanup, and returning the whole wait makes the derived grace
|
|
1837
|
+
// zero (rather than exposing half of a local timeout as a repair budget).
|
|
1838
|
+
if (!FECManager.#hasCatalogTiming(config)) return waitMs
|
|
1818
1839
|
const halfFailBoundMs = Math.ceil(waitMs / 2)
|
|
1819
1840
|
if (
|
|
1820
1841
|
config.k > 0 &&
|
|
@@ -1836,10 +1857,12 @@ export class FECManager {
|
|
|
1836
1857
|
|
|
1837
1858
|
/**
|
|
1838
1859
|
* One block lifetime: the inactivity fail bound plus the D-28 repair grace,
|
|
1839
|
-
* at the widest wait the catalog allows (#maxMulticastWaitMs).
|
|
1840
|
-
*
|
|
1841
|
-
*
|
|
1842
|
-
*
|
|
1860
|
+
* at the widest wait the catalog allows (#maxMulticastWaitMs). For a track
|
|
1861
|
+
* without a catalog span this is receiver-local cleanup time only; no repair
|
|
1862
|
+
* grace is added. Everything derived from it (terminal retention, the
|
|
1863
|
+
* release gate's hold ceiling, the duplicate ledgers, the owed-cleanup
|
|
1864
|
+
* bound) is compared across time, so it must not follow the live
|
|
1865
|
+
* measurement.
|
|
1843
1866
|
*/
|
|
1844
1867
|
#blockLifetimeMs(config: ResolvedFECTrackConfig): number {
|
|
1845
1868
|
const waitMs = this.#maxMulticastWaitMs(config)
|
|
@@ -1857,6 +1880,7 @@ export class FECManager {
|
|
|
1857
1880
|
}
|
|
1858
1881
|
|
|
1859
1882
|
#repairGraceMs(config: ResolvedFECTrackConfig, waitMs: number): number {
|
|
1883
|
+
if (!FECManager.#hasCatalogTiming(config)) return 0
|
|
1860
1884
|
return Math.max(1, waitMs - this.#repairDeadlineMs(config, waitMs))
|
|
1861
1885
|
}
|
|
1862
1886
|
|
|
@@ -1977,7 +2001,15 @@ export class FECManager {
|
|
|
1977
2001
|
config: FECManagerConfig,
|
|
1978
2002
|
catalogTrackName?: string,
|
|
1979
2003
|
): ResolvedFECTrackConfig {
|
|
1980
|
-
const
|
|
2004
|
+
const previousTrack = this.#trackConfigs.get(packetId)
|
|
2005
|
+
const k = config.k ?? previousTrack?.k
|
|
2006
|
+
if (k === undefined || !Number.isSafeInteger(k) || k <= 0) {
|
|
2007
|
+
throw new Error(
|
|
2008
|
+
`[FECManager] packetId=${packetId}: fec.k=${String(k)} must be a positive integer; ` +
|
|
2009
|
+
'configure the track from its catalog',
|
|
2010
|
+
)
|
|
2011
|
+
}
|
|
2012
|
+
const previous: Partial<ResolvedFECTrackConfig> = previousTrack ?? {}
|
|
1981
2013
|
const streamScopedGeometry =
|
|
1982
2014
|
catalogTrackName !== undefined && config.geometry !== undefined ? config.geometry : undefined
|
|
1983
2015
|
if (streamScopedGeometry !== undefined) {
|
|
@@ -1988,6 +2020,14 @@ export class FECManager {
|
|
|
1988
2020
|
config.k !== undefined ||
|
|
1989
2021
|
config.interleaveDepthMs !== undefined ||
|
|
1990
2022
|
config.fps !== undefined
|
|
2023
|
+
if (
|
|
2024
|
+
config.timeoutMs !== undefined &&
|
|
2025
|
+
(!Number.isFinite(config.timeoutMs) || config.timeoutMs < 0)
|
|
2026
|
+
) {
|
|
2027
|
+
throw new Error(
|
|
2028
|
+
`[FECManager] packetId=${packetId}: timeoutMs=${config.timeoutMs} must be a finite non-negative cleanup budget`,
|
|
2029
|
+
)
|
|
2030
|
+
}
|
|
1991
2031
|
if (config.blockProfile !== undefined) {
|
|
1992
2032
|
parseFecBlockProfile(config.blockProfile, `packetId=${packetId} fec.blockProfile`)
|
|
1993
2033
|
}
|
|
@@ -1995,38 +2035,35 @@ export class FECManager {
|
|
|
1995
2035
|
if (blockProfile === undefined) {
|
|
1996
2036
|
throw new Error(
|
|
1997
2037
|
`[FECManager] packetId=${packetId}: fec.blockProfile is required ` +
|
|
1998
|
-
'("
|
|
2038
|
+
'("iso-ssbg1-v1"); configure the track from its catalog',
|
|
1999
2039
|
)
|
|
2000
2040
|
}
|
|
2001
|
-
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
|
|
2006
|
-
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
|
|
2013
|
-
|
|
2014
|
-
|
|
2015
|
-
`(>= ${MIN_INTERLEAVE_DEPTH_MS} ms; got ${String(ownInterleaveDepthMs)}): it bounds how long ` +
|
|
2016
|
-
'block location keeps stated blocks and held sources',
|
|
2017
|
-
)
|
|
2018
|
-
}
|
|
2041
|
+
// iso-ssbg1-v1 block location keeps what it holds for a horizon derived
|
|
2042
|
+
// from this packet's own catalog span, or from a fixed receiver-local
|
|
2043
|
+
// count when the catalog has no elapsed-time span. The manager-wide span
|
|
2044
|
+
// is never borrowed by a track whose catalog omitted it.
|
|
2045
|
+
const ownInterleaveDepthMs =
|
|
2046
|
+
config.interleaveDepthMs ?? previousTrack?.interleaveDepthMs ?? 0
|
|
2047
|
+
if (
|
|
2048
|
+
!Number.isSafeInteger(ownInterleaveDepthMs) ||
|
|
2049
|
+
(ownInterleaveDepthMs !== 0 && ownInterleaveDepthMs < MIN_INTERLEAVE_DEPTH_MS)
|
|
2050
|
+
) {
|
|
2051
|
+
throw new Error(
|
|
2052
|
+
`[FECManager] packetId=${packetId}: fec.interleaveDepthMs must be absent, 0, or ` +
|
|
2053
|
+
`a finite value >= ${MIN_INTERLEAVE_DEPTH_MS} ms (got ${String(ownInterleaveDepthMs)})`,
|
|
2054
|
+
)
|
|
2019
2055
|
}
|
|
2056
|
+
const hasCatalogTiming = FECManager.#hasCatalogTiming({ interleaveDepthMs: ownInterleaveDepthMs })
|
|
2020
2057
|
const next: ResolvedFECTrackConfig = {
|
|
2021
2058
|
blockProfile,
|
|
2022
|
-
k
|
|
2059
|
+
k,
|
|
2023
2060
|
symbolSize:
|
|
2024
2061
|
config.symbolSize != null && config.symbolSize > 0
|
|
2025
2062
|
? config.symbolSize
|
|
2026
|
-
:
|
|
2027
|
-
interleaveDepthMs:
|
|
2028
|
-
fps: config.fps ?? previous.fps,
|
|
2029
|
-
timeoutMs: config.timeoutMs ?? (timingChanged ? 0 : previous.timeoutMs),
|
|
2063
|
+
: previousTrack?.symbolSize ?? 0,
|
|
2064
|
+
interleaveDepthMs: ownInterleaveDepthMs,
|
|
2065
|
+
fps: config.fps ?? previous.fps ?? this.#fecFps,
|
|
2066
|
+
timeoutMs: config.timeoutMs ?? (timingChanged ? 0 : previous.timeoutMs ?? 0),
|
|
2030
2067
|
...((config.trackKind ?? previous.trackKind) === undefined
|
|
2031
2068
|
? {}
|
|
2032
2069
|
: { trackKind: config.trackKind ?? previous.trackKind }),
|
|
@@ -2037,24 +2074,44 @@ export class FECManager {
|
|
|
2037
2074
|
? {}
|
|
2038
2075
|
: { geometry: config.geometry ?? previous.geometry }),
|
|
2039
2076
|
}
|
|
2040
|
-
|
|
2077
|
+
if (!hasCatalogTiming) {
|
|
2078
|
+
// A missing/zero span has no catalog deadline. Keep an explicitly
|
|
2079
|
+
// supplied local cleanup budget, preserve a prior local budget on a
|
|
2080
|
+
// metadata-only update, and otherwise use the manager's fixed local
|
|
2081
|
+
// default. A positive catalog track's timeout must never leak here.
|
|
2082
|
+
next.timeoutMs = config.timeoutMs !== undefined && config.timeoutMs > 0
|
|
2083
|
+
? config.timeoutMs
|
|
2084
|
+
: previousTrack !== undefined && previousTrack.interleaveDepthMs === 0
|
|
2085
|
+
? previousTrack.timeoutMs
|
|
2086
|
+
: LOCAL_FEC_TIMEOUT_MS
|
|
2087
|
+
} else {
|
|
2088
|
+
next.timeoutMs = this.#computeTimeout(next)
|
|
2089
|
+
}
|
|
2041
2090
|
if (this.#multicastSymbolDelivery === 'ordered-relay') {
|
|
2042
2091
|
// Error on bad init: an ordered-relay stream settles its blocks by the
|
|
2043
2092
|
// decoder's interleave horizon, which this geometry must define.
|
|
2044
2093
|
this.#orderedHorizonBlocksFor(packetId, next)
|
|
2045
2094
|
}
|
|
2046
|
-
|
|
2047
|
-
previous.
|
|
2048
|
-
|
|
2049
|
-
|
|
2050
|
-
//
|
|
2051
|
-
//
|
|
2052
|
-
//
|
|
2095
|
+
const timingPolicyChanged =
|
|
2096
|
+
FECManager.#hasCatalogTiming({ interleaveDepthMs: previous.interleaveDepthMs ?? 0 }) !== hasCatalogTiming
|
|
2097
|
+
if (timingPolicyChanged && !hasCatalogTiming) {
|
|
2098
|
+
// A D-28 timer belongs to the positive catalog timing policy. Once a
|
|
2099
|
+
// track becomes local-only, cancel it instead of merely relying on the
|
|
2100
|
+
// request callback's policy guard; the local cleanup timer is the only
|
|
2101
|
+
// deadline allowed to remain armed for the block.
|
|
2102
|
+
for (const key of [...this.#repairDeadlines.keys()]) {
|
|
2103
|
+
if (this.#belongsToPacket(key, packetId)) this.#clearRepairDeadline(key)
|
|
2104
|
+
}
|
|
2105
|
+
}
|
|
2106
|
+
if (previous.blockProfile !== undefined && previous.k !== next.k) {
|
|
2107
|
+
// A different maximum K is a different block geometry: the stream's
|
|
2108
|
+
// blocks must not be read under the old one's rules.
|
|
2053
2109
|
this.#dropIsoLocators(packetId)
|
|
2054
2110
|
}
|
|
2055
2111
|
this.#trackConfigs.set(packetId, next)
|
|
2056
2112
|
this.#refreshDeliveryGapWindow()
|
|
2057
2113
|
let policyChanged =
|
|
2114
|
+
timingPolicyChanged ||
|
|
2058
2115
|
(config.geometry !== undefined &&
|
|
2059
2116
|
JSON.stringify(config.geometry) !== JSON.stringify(previous.geometry)) ||
|
|
2060
2117
|
(config.trackKind !== undefined && config.trackKind !== previous.trackKind)
|
|
@@ -2077,8 +2134,12 @@ export class FECManager {
|
|
|
2077
2134
|
if (!this.#belongsToPacket(key, packetId)) continue
|
|
2078
2135
|
const stream = this.#parseStreamKey(key)
|
|
2079
2136
|
const geometry = this.#geometryFor(stream.packetId, stream.catalogTrackName)
|
|
2080
|
-
if (
|
|
2137
|
+
if (
|
|
2138
|
+
hasCatalogTiming &&
|
|
2139
|
+
JSON.stringify(geometry) === JSON.stringify(classifier.geometry)
|
|
2140
|
+
) continue
|
|
2081
2141
|
this.#classifiers.delete(key)
|
|
2142
|
+
if (!hasCatalogTiming) continue
|
|
2082
2143
|
// Blocks still unresolved lose the evidence their class rested on:
|
|
2083
2144
|
// under the fresh classifier they read `unknown`. Say so, so a queued
|
|
2084
2145
|
// or running P2 repair for a former delta block is promoted to P0
|
|
@@ -2089,14 +2150,6 @@ export class FECManager {
|
|
|
2089
2150
|
return next
|
|
2090
2151
|
}
|
|
2091
2152
|
|
|
2092
|
-
#normalizeSymbolData(packetId: number, data: Uint8Array): Uint8Array {
|
|
2093
|
-
const symbolSize = this.symbolSizeForPacket(packetId)
|
|
2094
|
-
if (symbolSize <= 0 || data.byteLength === symbolSize) return data
|
|
2095
|
-
const normalized = new Uint8Array(symbolSize)
|
|
2096
|
-
normalized.set(data.subarray(0, symbolSize))
|
|
2097
|
-
return normalized
|
|
2098
|
-
}
|
|
2099
|
-
|
|
2100
2153
|
#blockKeysFor(sbn: number, packetId?: number, catalogTrackName?: string): string[] {
|
|
2101
2154
|
if (packetId != null && catalogTrackName !== undefined) {
|
|
2102
2155
|
return [this.#blockKey(sbn, packetId, catalogTrackName)]
|
|
@@ -2169,7 +2222,7 @@ export class FECManager {
|
|
|
2169
2222
|
...stats,
|
|
2170
2223
|
packetId,
|
|
2171
2224
|
blocksComplete: trackTotal,
|
|
2172
|
-
k: this.#
|
|
2225
|
+
k: this.#trackConfigs.get(packetId)?.k,
|
|
2173
2226
|
recoveryRate: trackAffected > 0
|
|
2174
2227
|
? (stats.blocksRecovered / trackAffected) * 100
|
|
2175
2228
|
: 100,
|
|
@@ -2183,7 +2236,7 @@ export class FECManager {
|
|
|
2183
2236
|
(block) => block.multicastSymbols > 0 && block.moqSymbols > 0,
|
|
2184
2237
|
).length,
|
|
2185
2238
|
recentBlockPathCounts: Array.from(this.#blockPathCounts.values()).slice(-24),
|
|
2186
|
-
k: this.#
|
|
2239
|
+
k: this.#streamK,
|
|
2187
2240
|
// True recovery rate: of blocks that HAD loss, how many were recovered?
|
|
2188
2241
|
recoveryRate: affected > 0 ? (s.blocksRecovered / affected) * 100 : 100,
|
|
2189
2242
|
packetLossRate: total > 0 ? (affected / total) * 100 : 0,
|
|
@@ -2198,7 +2251,6 @@ export class FECManager {
|
|
|
2198
2251
|
},
|
|
2199
2252
|
blocks: stats.blocks.map((block) => ({ ...block })),
|
|
2200
2253
|
decoder: stats.decoder ? { ...stats.decoder } : undefined,
|
|
2201
|
-
altaFailedDetails: stats.altaFailedDetails?.map((detail) => ({ ...detail })),
|
|
2202
2254
|
packetId,
|
|
2203
2255
|
})),
|
|
2204
2256
|
repairSymbolSizeMismatches: this.#repairSymbolSizeMismatchCount,
|
|
@@ -2322,6 +2374,7 @@ export class FECManager {
|
|
|
2322
2374
|
resetMap(this.#blockSourceEsis)
|
|
2323
2375
|
resetMap(this.#blockAuthoredLengths)
|
|
2324
2376
|
resetMap(this.#blockRepairRsIds)
|
|
2377
|
+
resetMap(this.#blockRsbLengths)
|
|
2325
2378
|
resetMap(this.#blockPathCounts)
|
|
2326
2379
|
resetMap(this.#blockAnchorsMs)
|
|
2327
2380
|
resetMap(this.#blockCatalogTrackNames)
|
|
@@ -2447,6 +2500,7 @@ export class FECManager {
|
|
|
2447
2500
|
dropMap(this.#blockSourceEsis)
|
|
2448
2501
|
dropMap(this.#blockAuthoredLengths)
|
|
2449
2502
|
dropMap(this.#blockRepairRsIds)
|
|
2503
|
+
dropMap(this.#blockRsbLengths)
|
|
2450
2504
|
dropMap(this.#blockPathCounts)
|
|
2451
2505
|
dropMap(this.#blockAnchorsMs)
|
|
2452
2506
|
dropMap(this.#blockCatalogTrackNames)
|
|
@@ -2521,7 +2575,12 @@ export class FECManager {
|
|
|
2521
2575
|
}
|
|
2522
2576
|
}
|
|
2523
2577
|
|
|
2524
|
-
/**
|
|
2578
|
+
/**
|
|
2579
|
+
* Record a transport symbol and reject later duplicates of the same
|
|
2580
|
+
* packetId/SBN/ESI. `sbn` is the block's SS_Start. A packetId with no
|
|
2581
|
+
* configured block profile has no block geometry: its symbol is refused
|
|
2582
|
+
* (false) and no block state is created for it.
|
|
2583
|
+
*/
|
|
2525
2584
|
feedSymbolFromSource(
|
|
2526
2585
|
packetId: number,
|
|
2527
2586
|
sbn: number,
|
|
@@ -2530,6 +2589,7 @@ export class FECManager {
|
|
|
2530
2589
|
source: FecSymbolSource,
|
|
2531
2590
|
catalogTrackName?: string,
|
|
2532
2591
|
): boolean {
|
|
2592
|
+
if (!this.#hasBlockProfile(packetId)) return false
|
|
2533
2593
|
const blockKey = this.#blockKey(sbn, packetId, catalogTrackName)
|
|
2534
2594
|
const streamKey = this.#streamKey(packetId, catalogTrackName)
|
|
2535
2595
|
if (source === 'multicast' && this.#multicastSymbolDelivery === 'ordered-relay') {
|
|
@@ -2537,12 +2597,10 @@ export class FECManager {
|
|
|
2537
2597
|
// one refused below as a join partial or a duplicate.
|
|
2538
2598
|
this.#advanceOrderedFrontier(streamKey, sbn, packetId, catalogTrackName)
|
|
2539
2599
|
}
|
|
2540
|
-
|
|
2541
|
-
//
|
|
2542
|
-
|
|
2543
|
-
|
|
2544
|
-
? this.#isoLocatorFor(packetId, catalogTrackName).blockAt(sbn)?.length ?? k
|
|
2545
|
-
: k
|
|
2600
|
+
// Repair ESIs start at the block's own SSB_length (K'), which its locator
|
|
2601
|
+
// states once a repair placed it; before that, K is the only bound.
|
|
2602
|
+
const sourceSymbols =
|
|
2603
|
+
this.#isoLocatorFor(packetId, catalogTrackName).blockAt(sbn)?.length ?? this.#trackConfig(packetId).k
|
|
2546
2604
|
const isSourceSymbol = esi >= 0 && esi < sourceSymbols
|
|
2547
2605
|
const isStartupPartial = this.#startupPartialBlocks.has(blockKey)
|
|
2548
2606
|
const isReadyMulticastRepair =
|
|
@@ -2711,7 +2769,7 @@ export class FECManager {
|
|
|
2711
2769
|
for (const [key] of this.#activeBlocks) {
|
|
2712
2770
|
const { packetId, sbn } = this.#parseBlockKey(key);
|
|
2713
2771
|
const count = this.#blockSourceCounts.get(key) ?? 0;
|
|
2714
|
-
const k = this.#blockAuthoredLengths.get(key) ?? this
|
|
2772
|
+
const k = this.#blockAuthoredLengths.get(key) ?? this.#trackConfig(packetId).k;
|
|
2715
2773
|
blocks.push({ sbn, packetId, state: 'collecting', sourceCount: count, k, progress: k > 0 ? Math.min(count / k, 1) : 0 });
|
|
2716
2774
|
}
|
|
2717
2775
|
// Recently completed blocks (last 16)
|
|
@@ -2719,7 +2777,7 @@ export class FECManager {
|
|
|
2719
2777
|
for (const key of completed) {
|
|
2720
2778
|
const { packetId, sbn } = this.#parseBlockKey(key);
|
|
2721
2779
|
if (!this.#activeBlocks.has(key)) {
|
|
2722
|
-
const k = this
|
|
2780
|
+
const k = this.#trackConfig(packetId).k;
|
|
2723
2781
|
blocks.push({ sbn, packetId, state: 'complete', sourceCount: k, k, progress: 1 });
|
|
2724
2782
|
}
|
|
2725
2783
|
}
|
|
@@ -2732,14 +2790,16 @@ export class FECManager {
|
|
|
2732
2790
|
return this.#fecDecoders.values().next().value?.client ?? null
|
|
2733
2791
|
}
|
|
2734
2792
|
|
|
2735
|
-
|
|
2736
|
-
kForPacket(packetId: number): number { return this.#
|
|
2793
|
+
/** A configured packetId's K; undefined for an unconfigured one (no FEC work). */
|
|
2794
|
+
kForPacket(packetId: number): number | undefined { return this.#trackConfigs.get(packetId)?.k }
|
|
2737
2795
|
/** FEC symbol size in bytes. */
|
|
2738
2796
|
get symbolSize(): number { return this.#fecSymbolSize }
|
|
2739
|
-
symbolSizeForPacket(packetId: number): number { return this.#
|
|
2797
|
+
symbolSizeForPacket(packetId: number): number | undefined { return this.#trackConfigs.get(packetId)?.symbolSize }
|
|
2740
2798
|
/** FEC block span in milliseconds (0 = no interleaving). */
|
|
2741
2799
|
get interleaveDepthMs(): number { return this.#fecInterleaveDepthMs }
|
|
2742
|
-
interleaveDepthMsForPacket(packetId: number): number
|
|
2800
|
+
interleaveDepthMsForPacket(packetId: number): number | undefined {
|
|
2801
|
+
return this.#trackConfigs.get(packetId)?.interleaveDepthMs
|
|
2802
|
+
}
|
|
2743
2803
|
/**
|
|
2744
2804
|
* Record one multicast MMTP packet arrival (every packet of the leg, FEC
|
|
2745
2805
|
* symbol or not) for the delivery-gap measurement. `packetSequenceNumber`
|
|
@@ -2755,8 +2815,9 @@ export class FECManager {
|
|
|
2755
2815
|
* Resolved per-track multicast late-symbol wait in milliseconds: the span
|
|
2756
2816
|
* plus the larger of the catalog tolerance and the measured delivery gap.
|
|
2757
2817
|
*/
|
|
2758
|
-
timeoutMsForPacket(packetId: number): number {
|
|
2759
|
-
|
|
2818
|
+
timeoutMsForPacket(packetId: number): number | undefined {
|
|
2819
|
+
const config = this.#trackConfigs.get(packetId)
|
|
2820
|
+
return config === undefined ? undefined : this.#multicastWaitMs(config)
|
|
2760
2821
|
}
|
|
2761
2822
|
/**
|
|
2762
2823
|
* Largest multicast delivery gap measured over the current window (ms), and
|
|
@@ -2768,6 +2829,40 @@ export class FECManager {
|
|
|
2768
2829
|
windowMs: this.#deliveryGaps.windowMs,
|
|
2769
2830
|
}
|
|
2770
2831
|
}
|
|
2832
|
+
/**
|
|
2833
|
+
* An open block's accepted sources, distinct repair RS_IDs, authored K'
|
|
2834
|
+
* (SSB_length) and RSB_length (each undefined until a repair stated it).
|
|
2835
|
+
* Undefined for a block that is not open.
|
|
2836
|
+
*/
|
|
2837
|
+
blockSymbolCounts(
|
|
2838
|
+
sbn: number,
|
|
2839
|
+
packetId: number,
|
|
2840
|
+
catalogTrackName?: string,
|
|
2841
|
+
): {
|
|
2842
|
+
readonly sources: number
|
|
2843
|
+
readonly repairs: number
|
|
2844
|
+
readonly authoredLength: number | undefined
|
|
2845
|
+
readonly repairGroupLength: number | undefined
|
|
2846
|
+
} | undefined {
|
|
2847
|
+
const key = this.#blockKey(sbn, packetId, catalogTrackName)
|
|
2848
|
+
if (!this.#activeBlocks.has(key)) return undefined
|
|
2849
|
+
return {
|
|
2850
|
+
sources: this.#blockSourceCounts.get(key) ?? 0,
|
|
2851
|
+
repairs: this.#blockRepairRsIds.get(key)?.size ?? 0,
|
|
2852
|
+
authoredLength: this.#blockAuthoredLengths.get(key),
|
|
2853
|
+
repairGroupLength: this.#blockRsbLengths.get(key),
|
|
2854
|
+
}
|
|
2855
|
+
}
|
|
2856
|
+
/**
|
|
2857
|
+
* Record a block's RSB_length from one of its repair symbols (the router
|
|
2858
|
+
* parses it from the Repair FEC Payload ID). The largest stated value wins.
|
|
2859
|
+
*/
|
|
2860
|
+
noteRepairGroupLength(sbn: number, packetId: number, rsbLength: number, catalogTrackName?: string): void {
|
|
2861
|
+
if (!Number.isSafeInteger(rsbLength) || rsbLength <= 0) return
|
|
2862
|
+
const key = this.#blockKey(sbn, packetId, catalogTrackName)
|
|
2863
|
+
if (!this.#activeBlocks.has(key)) return
|
|
2864
|
+
if ((this.#blockRsbLengths.get(key) ?? 0) < rsbLength) this.#blockRsbLengths.set(key, rsbLength)
|
|
2865
|
+
}
|
|
2771
2866
|
/**
|
|
2772
2867
|
* Receiver-local time of a block's first accepted symbol, or undefined for
|
|
2773
2868
|
* a block no symbol reached (one materialized because a later SBN arrived).
|
|
@@ -2817,28 +2912,32 @@ export class FECManager {
|
|
|
2817
2912
|
this.#applyBlockDeadline(key, sbn, packetId, catalogTrackName)
|
|
2818
2913
|
}
|
|
2819
2914
|
/** Resolved per-track D-28 repair request deadline in milliseconds. */
|
|
2820
|
-
repairDeadlineMsForPacket(packetId: number): number {
|
|
2821
|
-
|
|
2915
|
+
repairDeadlineMsForPacket(packetId: number): number | undefined {
|
|
2916
|
+
const config = this.#trackConfigs.get(packetId)
|
|
2917
|
+
return config === undefined ? undefined : this.#repairDeadlineMs(config)
|
|
2822
2918
|
}
|
|
2823
2919
|
/**
|
|
2824
2920
|
* One block lifetime: the longest a block stays open after its last symbol
|
|
2825
2921
|
* (inactivity fail bound plus D-28 repair grace).
|
|
2826
2922
|
*/
|
|
2827
|
-
blockLifetimeMsForPacket(packetId: number): number {
|
|
2828
|
-
|
|
2923
|
+
blockLifetimeMsForPacket(packetId: number): number | undefined {
|
|
2924
|
+
const config = this.#trackConfigs.get(packetId)
|
|
2925
|
+
return config === undefined ? undefined : this.#blockLifetimeMs(config)
|
|
2829
2926
|
}
|
|
2830
2927
|
/**
|
|
2831
2928
|
* How long a terminal (clean, recovered or failed) block stays authoritative,
|
|
2832
2929
|
* for late symbols and for the in-order release gate. It is also the gate's
|
|
2833
2930
|
* hold ceiling (MmtpRouter's holdLimitMs): a retired block reads back as
|
|
2834
2931
|
* untracked, which the gate counts as lost, so no frame may still be waiting
|
|
2835
|
-
* on a block that can retire.
|
|
2836
|
-
*
|
|
2837
|
-
*
|
|
2838
|
-
*
|
|
2932
|
+
* on a block that can retire. With a positive catalog span the window is one
|
|
2933
|
+
* interleave depth for a gap's last block to finish arriving, one block
|
|
2934
|
+
* lifetime to fail it, and one more for an owed Worker cleanup. With an
|
|
2935
|
+
* absent/zero span it is a receiver-local cleanup bound only; it creates no
|
|
2936
|
+
* catalog FEC deadline.
|
|
2839
2937
|
*/
|
|
2840
|
-
terminalRetentionMsForPacket(packetId: number): number {
|
|
2841
|
-
const config = this.#
|
|
2938
|
+
terminalRetentionMsForPacket(packetId: number): number | undefined {
|
|
2939
|
+
const config = this.#trackConfigs.get(packetId)
|
|
2940
|
+
if (config === undefined) return undefined
|
|
2842
2941
|
return config.interleaveDepthMs + 2 * this.#blockLifetimeMs(config)
|
|
2843
2942
|
}
|
|
2844
2943
|
/** Remaining fetch budget for a specific repair-requested block. */
|
|
@@ -2846,14 +2945,15 @@ export class FECManager {
|
|
|
2846
2945
|
sbn: number,
|
|
2847
2946
|
packetId: number,
|
|
2848
2947
|
catalogTrackName?: string,
|
|
2849
|
-
): number {
|
|
2948
|
+
): number | undefined {
|
|
2850
2949
|
const deadlineMs = this.#activeBlockDeadlinesMs.get(
|
|
2851
2950
|
this.#blockKey(sbn, packetId, catalogTrackName),
|
|
2852
2951
|
)
|
|
2853
2952
|
if (deadlineMs !== undefined) {
|
|
2854
2953
|
return Math.max(1, Math.ceil(deadlineMs - performance.now()))
|
|
2855
2954
|
}
|
|
2856
|
-
const trackConfig = this.#
|
|
2955
|
+
const trackConfig = this.#trackConfigs.get(packetId)
|
|
2956
|
+
if (trackConfig === undefined) return undefined
|
|
2857
2957
|
return Math.max(1, trackConfig.timeoutMs - this.#repairDeadlineMs(trackConfig))
|
|
2858
2958
|
}
|
|
2859
2959
|
|
|
@@ -2867,15 +2967,20 @@ export class FECManager {
|
|
|
2867
2967
|
return this.#trackConfigs.get(packetId)?.blockProfile
|
|
2868
2968
|
}
|
|
2869
2969
|
|
|
2870
|
-
|
|
2871
|
-
|
|
2970
|
+
/**
|
|
2971
|
+
* Whether a packetId has a configured block geometry. iso-ssbg1-v1 is the
|
|
2972
|
+
* only profile, so every configured track has it; a packetId never
|
|
2973
|
+
* configured (or released) has none and gets no FEC block tracking.
|
|
2974
|
+
*/
|
|
2975
|
+
#hasBlockProfile(packetId: number): boolean {
|
|
2976
|
+
return this.#trackConfigs.get(packetId)?.blockProfile !== undefined
|
|
2872
2977
|
}
|
|
2873
2978
|
|
|
2874
2979
|
#isoLocatorFor(packetId: number, catalogTrackName?: string): IsoBlockLocator<IsoHeldSource> {
|
|
2875
2980
|
const streamKey = this.#streamKey(packetId, catalogTrackName)
|
|
2876
2981
|
let locator = this.#isoLocators.get(streamKey)
|
|
2877
2982
|
if (!locator) {
|
|
2878
|
-
locator = new IsoBlockLocator<IsoHeldSource>(this
|
|
2983
|
+
locator = new IsoBlockLocator<IsoHeldSource>(this.#trackConfig(packetId).k, this.#isoRetentionBlocks(packetId))
|
|
2879
2984
|
this.#isoLocators.set(streamKey, locator)
|
|
2880
2985
|
}
|
|
2881
2986
|
return locator
|
|
@@ -2909,12 +3014,14 @@ export class FECManager {
|
|
|
2909
3014
|
*/
|
|
2910
3015
|
#isoRetentionBlocks(packetId: number): number {
|
|
2911
3016
|
const config = this.#trackConfigs.get(packetId)
|
|
2912
|
-
if (config?.blockProfile
|
|
3017
|
+
if (config?.blockProfile === undefined) {
|
|
2913
3018
|
throw new Error(
|
|
2914
|
-
`[FECManager] packetId=${packetId} has no iso-ssbg1-v1 block
|
|
3019
|
+
`[FECManager] packetId=${packetId} has no configured iso-ssbg1-v1 block profile`,
|
|
2915
3020
|
)
|
|
2916
3021
|
}
|
|
2917
|
-
|
|
3022
|
+
if (!FECManager.#hasCatalogTiming(config)) return LOCAL_FEC_RETENTION_BLOCKS
|
|
3023
|
+
const terminalRetentionMs = config.interleaveDepthMs + 2 * this.#blockLifetimeMs(config)
|
|
3024
|
+
const blocks = Math.ceil(terminalRetentionMs / config.interleaveDepthMs) + 1
|
|
2918
3025
|
if (!Number.isSafeInteger(blocks) || blocks <= 0) {
|
|
2919
3026
|
throw new Error(`[FECManager] packetId=${packetId} iso-ssbg1-v1 retention of ${blocks} blocks is not derivable`)
|
|
2920
3027
|
}
|
|
@@ -2923,7 +3030,8 @@ export class FECManager {
|
|
|
2923
3030
|
|
|
2924
3031
|
/** Whether an iso-ssbg1-v1 protected packet fits one ssbg_mode1 symbol (len + 2 <= T). */
|
|
2925
3032
|
isoSourceFits(packetId: number, packetLength: number): boolean {
|
|
2926
|
-
|
|
3033
|
+
const symbolSize = this.#trackConfigs.get(packetId)?.symbolSize
|
|
3034
|
+
return symbolSize !== undefined && ssbg1FitsSymbol(packetLength, symbolSize)
|
|
2927
3035
|
}
|
|
2928
3036
|
|
|
2929
3037
|
/**
|
|
@@ -3010,11 +3118,11 @@ export class FECManager {
|
|
|
3010
3118
|
* that could hold them is pending or not yet stated by a repair, else the
|
|
3011
3119
|
* starts of the blocks that failed or vanished.
|
|
3012
3120
|
*
|
|
3013
|
-
* `untracked` reads
|
|
3014
|
-
*
|
|
3015
|
-
*
|
|
3016
|
-
*
|
|
3017
|
-
* an SS_Start
|
|
3121
|
+
* `untracked` reads as the release gate reads it (MmtpRouter
|
|
3122
|
+
* #createFecReleaseGate): a block holding either released frame's SS_ID
|
|
3123
|
+
* (`fromSsId` or `toSsId`) delivered that frame, so untracked there means it
|
|
3124
|
+
* resolved and retired; a block starting after the stream's frontier
|
|
3125
|
+
* (`highestSbn`, an SS_Start) may still be heard from; a block at or behind the
|
|
3018
3126
|
* frontier that no symbol of this manager ever reached vanished, and its
|
|
3019
3127
|
* media is lost.
|
|
3020
3128
|
*/
|
|
@@ -3107,6 +3215,10 @@ export class FECManager {
|
|
|
3107
3215
|
packetId: number,
|
|
3108
3216
|
catalogTrackName?: string,
|
|
3109
3217
|
): { mode: ClassifierMode; reason: ClassifierReason | null } {
|
|
3218
|
+
// A no-span stream has no catalog timing with which to classify blocks.
|
|
3219
|
+
// Keep the existing fail-closed shape for callers that still query this
|
|
3220
|
+
// accessor, while the span sentinel tells them this mode is local-only.
|
|
3221
|
+
if (!this.#hasCatalogTimingForPacket(packetId)) return { mode: 'all-protected', reason: null }
|
|
3110
3222
|
const classifier = this.#classifierFor(packetId, catalogTrackName)
|
|
3111
3223
|
return { mode: classifier.mode, reason: classifier.reason }
|
|
3112
3224
|
}
|
|
@@ -3117,11 +3229,13 @@ export class FECManager {
|
|
|
3117
3229
|
* it does not. A stream with no classifier yet has shown nothing against it.
|
|
3118
3230
|
*/
|
|
3119
3231
|
mpuBoundsRapsFor(packetId: number, catalogTrackName?: string): boolean {
|
|
3232
|
+
if (!this.#hasCatalogTimingForPacket(packetId)) return false
|
|
3120
3233
|
return this.#classifiers.get(this.#streamKey(packetId, catalogTrackName))?.mpuBoundsRaps ?? true
|
|
3121
3234
|
}
|
|
3122
3235
|
|
|
3123
3236
|
/** A block's class now, or frozen at its resolution. */
|
|
3124
3237
|
blockClassFor(sbn: number, packetId: number, catalogTrackName?: string): BlockClass {
|
|
3238
|
+
if (!this.#hasCatalogTimingForPacket(packetId)) return 'unknown'
|
|
3125
3239
|
const frozen = this.#resolvedBlockClasses.get(this.#blockKey(sbn, packetId, catalogTrackName))
|
|
3126
3240
|
if (frozen !== undefined) return frozen
|
|
3127
3241
|
return this.#peekClass(sbn, packetId, catalogTrackName)
|
|
@@ -3152,7 +3266,7 @@ export class FECManager {
|
|
|
3152
3266
|
presentationTime: bigint,
|
|
3153
3267
|
mediaTimescale: number,
|
|
3154
3268
|
): void {
|
|
3155
|
-
if (!this.#trackConfigs.has(packetId)) return
|
|
3269
|
+
if (!this.#trackConfigs.has(packetId) || !this.#hasCatalogTimingForPacket(packetId)) return
|
|
3156
3270
|
this.#applyClassifierChanges(
|
|
3157
3271
|
packetId,
|
|
3158
3272
|
catalogTrackName,
|
|
@@ -3170,36 +3284,56 @@ export class FECManager {
|
|
|
3170
3284
|
|
|
3171
3285
|
/** Configure FEC parameters. */
|
|
3172
3286
|
configure(config: FECManagerConfig): void {
|
|
3173
|
-
|
|
3287
|
+
// Validate timing before changing any manager-global field. A malformed
|
|
3288
|
+
// value must not leave a partially applied configuration (especially an
|
|
3289
|
+
// infinite local cleanup budget, which would never expire).
|
|
3290
|
+
if (
|
|
3291
|
+
config.interleaveDepthMs != null &&
|
|
3292
|
+
(!Number.isSafeInteger(config.interleaveDepthMs) || config.interleaveDepthMs < 0)
|
|
3293
|
+
) {
|
|
3294
|
+
throw new Error(
|
|
3295
|
+
`[FECManager] interleaveDepthMs=${config.interleaveDepthMs} must be a non-negative safe integer in milliseconds`,
|
|
3296
|
+
)
|
|
3297
|
+
}
|
|
3298
|
+
if (
|
|
3299
|
+
config.timeoutMs != null &&
|
|
3300
|
+
(!Number.isFinite(config.timeoutMs) || config.timeoutMs < 0)
|
|
3301
|
+
) {
|
|
3302
|
+
throw new Error(
|
|
3303
|
+
`[FECManager] timeoutMs=${config.timeoutMs} must be a finite non-negative cleanup budget`,
|
|
3304
|
+
)
|
|
3305
|
+
}
|
|
3306
|
+
if (config.k != null && (!Number.isSafeInteger(config.k) || config.k <= 0)) {
|
|
3307
|
+
throw new Error(`[FECManager] k=${config.k} must be a positive integer`)
|
|
3308
|
+
}
|
|
3309
|
+
if (config.k != null) this.#streamK = config.k
|
|
3174
3310
|
if (config.symbolSize != null && config.symbolSize > 0) this.#fecSymbolSize = config.symbolSize
|
|
3175
3311
|
if (config.interleaveDepthMs != null) this.#fecInterleaveDepthMs = config.interleaveDepthMs
|
|
3176
3312
|
if (config.fps != null && Number.isFinite(config.fps) && config.fps > 0) {
|
|
3177
3313
|
this.#fecFps = config.fps
|
|
3178
3314
|
}
|
|
3179
|
-
// interleaveDepthMs is in milliseconds per spec §5.1.
|
|
3180
|
-
// Valid range: 33ms (interactive/gaming) to 10000ms (broadcast ATSC 3.0).
|
|
3181
|
-
// Warn if value looks like frame count instead of ms (common misconfiguration).
|
|
3182
|
-
if (config.interleaveDepthMs != null && config.interleaveDepthMs > 0 && config.interleaveDepthMs < MIN_INTERLEAVE_DEPTH_MS) {
|
|
3183
|
-
console.warn(`[FECManager] interleaveDepthMs=${config.interleaveDepthMs} looks like frame count, not milliseconds (§5.1 requires ms). Expected range: ${MIN_INTERLEAVE_DEPTH_MS}-10000ms.`)
|
|
3184
|
-
}
|
|
3185
3315
|
if (config.timeoutMs != null && config.timeoutMs > 0) {
|
|
3186
3316
|
this.#fecTimeoutMs = config.timeoutMs
|
|
3317
|
+
} else if (config.interleaveDepthMs === 0) {
|
|
3318
|
+
// An explicit zero span removes the catalog deadline. Reset the global
|
|
3319
|
+
// budget to the bounded receiver-local fallback unless this call also
|
|
3320
|
+
// supplied an explicit positive local timeout above.
|
|
3321
|
+
this.#fecTimeoutMs = LOCAL_FEC_TIMEOUT_MS
|
|
3187
3322
|
} else if (
|
|
3188
3323
|
config.interleaveDepthMs != null &&
|
|
3324
|
+
Number.isSafeInteger(config.interleaveDepthMs) &&
|
|
3189
3325
|
config.interleaveDepthMs >= MIN_INTERLEAVE_DEPTH_MS
|
|
3190
3326
|
) {
|
|
3191
|
-
|
|
3192
|
-
|
|
3193
|
-
sourceSymbols: this.#fecK,
|
|
3194
|
-
symbolSize: this.#fecSymbolSize,
|
|
3195
|
-
mediaUnitsPerSecond: config.fps,
|
|
3196
|
-
})
|
|
3327
|
+
// The stream has no K (a K is per track, BLO-45953); none feeds the window.
|
|
3328
|
+
this.#fecTimeoutMs = fecSpanDeliveryWindowMs(config.interleaveDepthMs)
|
|
3197
3329
|
}
|
|
3198
3330
|
// A shared multicast parser receives every packetId. Keep existing track
|
|
3199
3331
|
// deadlines at least as wide as the selected video delivery envelope so a
|
|
3200
3332
|
// short audio interleave cannot expire during browser/AMT scheduling gaps.
|
|
3201
3333
|
for (const [packetId, track] of this.#trackConfigs) {
|
|
3202
|
-
|
|
3334
|
+
// A track without a positive catalog span owns only its receiver-local
|
|
3335
|
+
// cleanup budget; never widen it with another track's catalog timing.
|
|
3336
|
+
if (FECManager.#hasCatalogTiming(track) && track.timeoutMs < this.#fecTimeoutMs) {
|
|
3203
3337
|
this.#trackConfigs.set(packetId, { ...track, timeoutMs: this.#fecTimeoutMs })
|
|
3204
3338
|
}
|
|
3205
3339
|
}
|
|
@@ -3277,7 +3411,7 @@ export class FECManager {
|
|
|
3277
3411
|
)
|
|
3278
3412
|
}
|
|
3279
3413
|
if (config) this.#upsertTrackConfig(packetId, config, catalogTrackName)
|
|
3280
|
-
const workerK = this.#
|
|
3414
|
+
const workerK = this.#trackConfig(packetId).k
|
|
3281
3415
|
this.#workerClients.set(streamKey, client)
|
|
3282
3416
|
this.#releasedWorkerClients.delete(client)
|
|
3283
3417
|
|
|
@@ -3289,7 +3423,8 @@ export class FECManager {
|
|
|
3289
3423
|
// ESI-less event as recovered would report healthy FEC while dropping
|
|
3290
3424
|
// the media. Enforce the Worker contract before any recovery metric.
|
|
3291
3425
|
// The Worker names the recovered block's authored SSB_length; ESIs
|
|
3292
|
-
//
|
|
3426
|
+
// at or past it are not source symbols of the block and are never
|
|
3427
|
+
// emitted.
|
|
3293
3428
|
if (
|
|
3294
3429
|
!Number.isInteger(meta.ssbLength) ||
|
|
3295
3430
|
meta.ssbLength! <= 0 ||
|
|
@@ -3520,7 +3655,7 @@ export class FECManager {
|
|
|
3520
3655
|
return
|
|
3521
3656
|
}
|
|
3522
3657
|
this.#settleOwedCleanup(key, 'cleanup-ack-timeout')
|
|
3523
|
-
}, Math.ceil(this.#liveBlockLifetimeMs(this.#
|
|
3658
|
+
}, Math.ceil(this.#liveBlockLifetimeMs(this.#trackConfig(packetId)))),
|
|
3524
3659
|
}
|
|
3525
3660
|
this.#pendingWorkerCleanupBounds.set(key, bound)
|
|
3526
3661
|
}
|
|
@@ -3568,18 +3703,20 @@ export class FECManager {
|
|
|
3568
3703
|
if (this.#fpsDetected || fps <= 0) return
|
|
3569
3704
|
this.#fpsDetected = true
|
|
3570
3705
|
this.#fecFps = fps
|
|
3571
|
-
// Only a valid §5.1 ms interleave refines the
|
|
3572
|
-
//
|
|
3573
|
-
if (
|
|
3574
|
-
this.#
|
|
3575
|
-
|
|
3576
|
-
|
|
3577
|
-
|
|
3578
|
-
mediaUnitsPerSecond: fps,
|
|
3579
|
-
})
|
|
3706
|
+
// Only a valid non-negative integer §5.1 ms interleave refines the
|
|
3707
|
+
// timeout; zero is the no-deadline sentinel.
|
|
3708
|
+
if (
|
|
3709
|
+
Number.isSafeInteger(this.#fecInterleaveDepthMs) &&
|
|
3710
|
+
this.#fecInterleaveDepthMs >= MIN_INTERLEAVE_DEPTH_MS
|
|
3711
|
+
) {
|
|
3712
|
+
this.#fecTimeoutMs = fecSpanDeliveryWindowMs(this.#fecInterleaveDepthMs)
|
|
3580
3713
|
}
|
|
3581
3714
|
for (const [packetId, config] of this.#trackConfigs) {
|
|
3582
|
-
if (
|
|
3715
|
+
if (
|
|
3716
|
+
config.fps !== undefined ||
|
|
3717
|
+
!Number.isSafeInteger(config.interleaveDepthMs) ||
|
|
3718
|
+
config.interleaveDepthMs < MIN_INTERLEAVE_DEPTH_MS
|
|
3719
|
+
) continue
|
|
3583
3720
|
this.#trackConfigs.set(packetId, {
|
|
3584
3721
|
...config,
|
|
3585
3722
|
fps,
|
|
@@ -3614,36 +3751,31 @@ export class FECManager {
|
|
|
3614
3751
|
packetId: number,
|
|
3615
3752
|
ssId: number,
|
|
3616
3753
|
symbolData: Uint8Array,
|
|
3617
|
-
alta?: { auth: Uint8Array; payloadEnd: number },
|
|
3618
3754
|
catalogTrackName?: string,
|
|
3619
3755
|
): Uint8Array | null {
|
|
3620
3756
|
this.#fecStats.sourcePackets++
|
|
3621
3757
|
this.#statsForPacket(packetId).sourcePackets++
|
|
3622
|
-
|
|
3623
|
-
|
|
3624
|
-
|
|
3625
|
-
|
|
3626
|
-
|
|
3627
|
-
|
|
3628
|
-
|
|
3629
|
-
|
|
3630
|
-
|
|
3631
|
-
|
|
3632
|
-
|
|
3633
|
-
|
|
3634
|
-
|
|
3635
|
-
|
|
3636
|
-
locator.observe(ssId)
|
|
3637
|
-
if (!ssbg1FitsSymbol(symbolData.byteLength, this.symbolSizeForPacket(packetId))) {
|
|
3638
|
-
this.#isoSourceOversizeCount++
|
|
3639
|
-
return null
|
|
3640
|
-
}
|
|
3641
|
-
sbn = located.block.start
|
|
3642
|
-
fecData = symbolData
|
|
3643
|
-
} else {
|
|
3644
|
-
fecData = this.#normalizeSymbolData(packetId, symbolData)
|
|
3645
|
-
sbn = Math.floor(ssId / this.kForPacket(packetId))
|
|
3758
|
+
// A packetId with no configured block profile has no block geometry and
|
|
3759
|
+
// no decoder (setFecDecoder/setFecWorker configure the track): nothing to
|
|
3760
|
+
// decode.
|
|
3761
|
+
if (!this.#hasBlockProfile(packetId)) return null
|
|
3762
|
+
// The decoder builds the ssbg_mode1 symbol from the exact protected
|
|
3763
|
+
// packet, so the packet travels unpadded. Its block must already be
|
|
3764
|
+
// stated (MmtpRouter holds a source until a repair places it).
|
|
3765
|
+
const locator = this.#isoLocatorFor(packetId, catalogTrackName)
|
|
3766
|
+
const located = locator.locate(ssId)
|
|
3767
|
+
if (located === null) {
|
|
3768
|
+
throw new Error(
|
|
3769
|
+
`[FECManager] iso-ssbg1-v1 source SS_ID=${ssId} (packetId=${packetId}) has no stated block; ` +
|
|
3770
|
+
'hold it until a repair places it (placeIsoRepair)',
|
|
3771
|
+
)
|
|
3646
3772
|
}
|
|
3773
|
+
locator.observe(ssId)
|
|
3774
|
+
if (!ssbg1FitsSymbol(symbolData.byteLength, this.#trackConfig(packetId).symbolSize)) {
|
|
3775
|
+
this.#isoSourceOversizeCount++
|
|
3776
|
+
return null
|
|
3777
|
+
}
|
|
3778
|
+
const sbn = located.block.start
|
|
3647
3779
|
const blockKey = this.#blockKey(sbn, packetId, catalogTrackName)
|
|
3648
3780
|
const syncBlockOwner = this.#syncDecoderBlockOwners.get(blockKey)
|
|
3649
3781
|
|
|
@@ -3651,36 +3783,19 @@ export class FECManager {
|
|
|
3651
3783
|
const workerClient = this.#routableWorkerForPacket(packetId, catalogTrackName)
|
|
3652
3784
|
if (workerClient && !syncBlockOwner) {
|
|
3653
3785
|
if (!this.#claimDecoderBlock(workerClient, sbn, catalogTrackName)) return null
|
|
3654
|
-
// Transfer an ArrayBuffer nothing else references.
|
|
3655
|
-
//
|
|
3656
|
-
//
|
|
3657
|
-
|
|
3658
|
-
|
|
3659
|
-
|
|
3660
|
-
|
|
3661
|
-
|
|
3662
|
-
|
|
3663
|
-
) as ArrayBuffer
|
|
3664
|
-
// Pass the flat SS_ID through to the worker. The WASM decoder derives
|
|
3665
|
-
// SBN/ESI internally per ISO 23008-1 §C.5.2; the WASM add_source
|
|
3786
|
+
// Transfer an ArrayBuffer nothing else references. The source travels
|
|
3787
|
+
// unpadded, and a caller's buffer may be a view into a larger packet,
|
|
3788
|
+
// so it is copied first (T-03-06).
|
|
3789
|
+
const buf = symbolData.buffer.slice(
|
|
3790
|
+
symbolData.byteOffset,
|
|
3791
|
+
symbolData.byteOffset + symbolData.byteLength,
|
|
3792
|
+
) as ArrayBuffer
|
|
3793
|
+
// Pass the flat SS_ID through to the worker. The WASM decoder places
|
|
3794
|
+
// the source in its block by SS_ID (ISO 23008-1 §C.5.2); the WASM add_source
|
|
3666
3795
|
// signature is (ss_id, data, timestamp) — pre-computing sbn/esi on this
|
|
3667
3796
|
// side produces wrong WASM argument alignment and silently breaks
|
|
3668
3797
|
// block assembly.
|
|
3669
|
-
|
|
3670
|
-
// Copy auth into a fresh ArrayBuffer to transfer zero-copy. The
|
|
3671
|
-
// underlying buffer of the parsed trailer view belongs to the wire
|
|
3672
|
-
// packet, which may be transferred separately or reused.
|
|
3673
|
-
const authBuf = alta.auth.buffer.slice(
|
|
3674
|
-
alta.auth.byteOffset,
|
|
3675
|
-
alta.auth.byteOffset + alta.auth.byteLength,
|
|
3676
|
-
) as ArrayBuffer
|
|
3677
|
-
workerClient.feedSource(ssId, buf, performance.now(), {
|
|
3678
|
-
auth: authBuf,
|
|
3679
|
-
payloadEnd: alta.payloadEnd,
|
|
3680
|
-
})
|
|
3681
|
-
} else {
|
|
3682
|
-
workerClient.feedSource(ssId, buf, performance.now())
|
|
3683
|
-
}
|
|
3798
|
+
workerClient.feedSource(ssId, buf, performance.now())
|
|
3684
3799
|
return null
|
|
3685
3800
|
}
|
|
3686
3801
|
|
|
@@ -3693,7 +3808,7 @@ export class FECManager {
|
|
|
3693
3808
|
|
|
3694
3809
|
let recovered: Uint8Array | null = null
|
|
3695
3810
|
try {
|
|
3696
|
-
recovered = client.add_source(ssId,
|
|
3811
|
+
recovered = client.add_source(ssId, symbolData, performance.now())
|
|
3697
3812
|
} catch {
|
|
3698
3813
|
// WASM RefCell panic — decoder instance shared with multicast repair path
|
|
3699
3814
|
}
|
|
@@ -3722,7 +3837,10 @@ export class FECManager {
|
|
|
3722
3837
|
): Uint8Array | null {
|
|
3723
3838
|
this.#fecStats.repairPackets++
|
|
3724
3839
|
this.#statsForPacket(packetId).repairPackets++
|
|
3725
|
-
|
|
3840
|
+
// A packetId with no configured block profile has no block geometry: its
|
|
3841
|
+
// repair names no block this manager can track or decode.
|
|
3842
|
+
if (!this.#hasBlockProfile(packetId)) return null
|
|
3843
|
+
const symbolSize = this.#trackConfig(packetId).symbolSize
|
|
3726
3844
|
if (symbolSize > 0 && repairData.byteLength !== symbolSize) {
|
|
3727
3845
|
this.#repairSymbolSizeMismatchCount++
|
|
3728
3846
|
if (this.#repairSymbolSizeMismatchCount <= 5 || this.#repairSymbolSizeMismatchCount % 100 === 0) {
|
|
@@ -3734,25 +3852,17 @@ export class FECManager {
|
|
|
3734
3852
|
}
|
|
3735
3853
|
const fecData = repairData
|
|
3736
3854
|
|
|
3737
|
-
//
|
|
3738
|
-
//
|
|
3739
|
-
//
|
|
3740
|
-
// Keep the wire tuple unchanged when calling the
|
|
3741
|
-
|
|
3742
|
-
|
|
3743
|
-
|
|
3744
|
-
|
|
3745
|
-
|
|
3746
|
-
// placed the block before feeding its repair (placeIsoRepair).
|
|
3747
|
-
const block = this.#isoLocatorFor(packetId, catalogTrackName).blockAt(ssStart)
|
|
3748
|
-
if (ssbLength <= 0 || ssbLength > k || block === undefined || block.span || block.length !== ssbLength) {
|
|
3749
|
-
return null
|
|
3750
|
-
}
|
|
3751
|
-
sbn = ssStart
|
|
3752
|
-
} else {
|
|
3753
|
-
if (ssbLength <= 0 || ssbLength > k || ssStart % k !== 0) return null
|
|
3754
|
-
sbn = Math.floor(ssStart / k)
|
|
3855
|
+
// ISO/IEC 23008-1 C.5.3: the block is [SS_Start, SS_Start + SSB_length),
|
|
3856
|
+
// K' = SSB_length, and the catalog K is only its maximum. The block is
|
|
3857
|
+
// keyed by its SS_Start. MmtpRouter placed the block before feeding its
|
|
3858
|
+
// repair (placeIsoRepair). Keep the wire tuple unchanged when calling the
|
|
3859
|
+
// decoder below.
|
|
3860
|
+
const k = this.#trackConfig(packetId).k
|
|
3861
|
+
const block = this.#isoLocatorFor(packetId, catalogTrackName).blockAt(ssStart)
|
|
3862
|
+
if (ssbLength <= 0 || ssbLength > k || block === undefined || block.span || block.length !== ssbLength) {
|
|
3863
|
+
return null
|
|
3755
3864
|
}
|
|
3865
|
+
const sbn = ssStart
|
|
3756
3866
|
this.#noteSbnSeen(sbn, packetId, catalogTrackName, 'repair')
|
|
3757
3867
|
|
|
3758
3868
|
// Repair symbols are block activity: refresh the inactivity fail bound so
|
|
@@ -3802,8 +3912,9 @@ export class FECManager {
|
|
|
3802
3912
|
* Record a block's authored SSB_length from a repair symbol. Returns false,
|
|
3803
3913
|
* leaving the block unchanged, when the symbol contradicts the block: a
|
|
3804
3914
|
* different SSB_length than an earlier repair symbol declared is dropped,
|
|
3805
|
-
* and an SSB_length that places an already-received source ESI
|
|
3806
|
-
*
|
|
3915
|
+
* and an SSB_length that places an already-received source ESI at or past
|
|
3916
|
+
* the block's end fails the block, since its geometry can no longer be
|
|
3917
|
+
* trusted.
|
|
3807
3918
|
*/
|
|
3808
3919
|
#noteAuthoredLength(
|
|
3809
3920
|
blockKey: string,
|
|
@@ -3910,7 +4021,7 @@ export class FECManager {
|
|
|
3910
4021
|
packetId: number,
|
|
3911
4022
|
catalogTrackName: string | undefined,
|
|
3912
4023
|
): void {
|
|
3913
|
-
if (!this.#
|
|
4024
|
+
if (!this.#hasBlockProfile(packetId)) return
|
|
3914
4025
|
const newest = this.#isoLocators.get(this.#streamKey(packetId, catalogTrackName))?.newest
|
|
3915
4026
|
if (newest === undefined) return
|
|
3916
4027
|
try {
|
|
@@ -3963,19 +4074,21 @@ export class FECManager {
|
|
|
3963
4074
|
|
|
3964
4075
|
this.#noteSbnSeen(sbn, packetId, catalogTrackName, 'source', evidence.packetSequenceNumber)
|
|
3965
4076
|
|
|
3966
|
-
// A source ESI
|
|
3967
|
-
//
|
|
4077
|
+
// A source ESI at or past the block's authored SSB_length contradicts it;
|
|
4078
|
+
// its geometry can no longer be trusted, so fail it closed.
|
|
3968
4079
|
const authoredLength = this.#blockAuthoredLengths.get(key) ?? k
|
|
3969
4080
|
if (Number.isInteger(esi) && esi! >= authoredLength) {
|
|
3970
4081
|
this.#failBlock(sbn, packetId, catalogTrackName)
|
|
3971
4082
|
return
|
|
3972
4083
|
}
|
|
3973
4084
|
|
|
3974
|
-
|
|
3975
|
-
|
|
3976
|
-
|
|
3977
|
-
|
|
3978
|
-
|
|
4085
|
+
if (FECManager.#hasCatalogTiming(trackConfig)) {
|
|
4086
|
+
this.#applyClassifierChanges(
|
|
4087
|
+
packetId,
|
|
4088
|
+
catalogTrackName,
|
|
4089
|
+
this.#classifierFor(packetId, catalogTrackName).observe(sbn, evidence),
|
|
4090
|
+
)
|
|
4091
|
+
}
|
|
3979
4092
|
|
|
3980
4093
|
// Count source symbols per block
|
|
3981
4094
|
const count = (this.#blockSourceCounts.get(key) ?? 0) + 1
|
|
@@ -4024,6 +4137,7 @@ export class FECManager {
|
|
|
4024
4137
|
this.#blockSourceEsis.delete(key)
|
|
4025
4138
|
this.#blockAuthoredLengths.delete(key)
|
|
4026
4139
|
this.#blockRepairRsIds.delete(key)
|
|
4140
|
+
this.#blockRsbLengths.delete(key)
|
|
4027
4141
|
// BLO-3323 L15: track which SBNs reached K source-symbol arrivals so the
|
|
4028
4142
|
// worker recovery suppression can distinguish "block completed by source"
|
|
4029
4143
|
// (legitimately suppress duplicate recovery) from "block touched by any
|
|
@@ -4049,6 +4163,9 @@ export class FECManager {
|
|
|
4049
4163
|
catalogTrackName?: string,
|
|
4050
4164
|
sourceProgress = false,
|
|
4051
4165
|
): void {
|
|
4166
|
+
// An unconfigured packetId does no FEC work: no deadline is armed at an
|
|
4167
|
+
// assumed geometry (BLO-45953).
|
|
4168
|
+
if (!this.#trackConfigs.has(packetId)) return
|
|
4052
4169
|
const key = this.#blockKey(sbn, packetId, catalogTrackName)
|
|
4053
4170
|
|
|
4054
4171
|
// Late symbols must not resurrect a block that already completed or failed.
|
|
@@ -4070,7 +4187,7 @@ export class FECManager {
|
|
|
4070
4187
|
return
|
|
4071
4188
|
}
|
|
4072
4189
|
|
|
4073
|
-
const trackConfig = this.#
|
|
4190
|
+
const trackConfig = this.#trackConfig(packetId)
|
|
4074
4191
|
const timeoutMs = this.#multicastWaitMs(trackConfig)
|
|
4075
4192
|
// #computeTimeout never yields <= 0 (computeFecDeliveryWindowMs throws on
|
|
4076
4193
|
// a non-positive interleave or K). Returning here would leave a block
|
|
@@ -4087,6 +4204,7 @@ export class FECManager {
|
|
|
4087
4204
|
// most K+P accepted symbols can move the wait, so the block stays bounded.
|
|
4088
4205
|
const repairDeadline = this.#repairDeadlines.get(key)
|
|
4089
4206
|
if (
|
|
4207
|
+
this.#hasCatalogTimingForPacket(packetId) &&
|
|
4090
4208
|
this.onRepairNeeded &&
|
|
4091
4209
|
!repairRequested &&
|
|
4092
4210
|
(repairDeadline === undefined || sourceProgress)
|
|
@@ -4120,6 +4238,7 @@ export class FECManager {
|
|
|
4120
4238
|
this.#blockSourceEsis.delete(key)
|
|
4121
4239
|
this.#blockAuthoredLengths.delete(key)
|
|
4122
4240
|
this.#blockRepairRsIds.delete(key)
|
|
4241
|
+
this.#blockRsbLengths.delete(key)
|
|
4123
4242
|
this.#syncDecoderBlockOwners.delete(key)
|
|
4124
4243
|
this.#markTerminal(key, sbn, packetId, false)
|
|
4125
4244
|
this.#failedBlocks.add(key)
|
|
@@ -4158,6 +4277,7 @@ export class FECManager {
|
|
|
4158
4277
|
this.#blockSourceEsis.delete(key)
|
|
4159
4278
|
this.#blockAuthoredLengths.delete(key)
|
|
4160
4279
|
this.#blockRepairRsIds.delete(key)
|
|
4280
|
+
this.#blockRsbLengths.delete(key)
|
|
4161
4281
|
this.#clearBlockTimer(key)
|
|
4162
4282
|
this.onBlockResolved?.(block.sbn, block.packetId, block.catalogTrackName)
|
|
4163
4283
|
}
|
|
@@ -4169,6 +4289,8 @@ export class FECManager {
|
|
|
4169
4289
|
|
|
4170
4290
|
/** Mark a block as completed externally (e.g., after routing recovered data). */
|
|
4171
4291
|
markBlockCompleted(sbn: number, packetId?: number, catalogTrackName?: string): void {
|
|
4292
|
+
// An unconfigured packetId has no FEC blocks to complete (BLO-45953).
|
|
4293
|
+
if (packetId != null && !this.#trackConfigs.has(packetId)) return
|
|
4172
4294
|
for (const key of this.#blockKeysFor(sbn, packetId, catalogTrackName)) {
|
|
4173
4295
|
this.#resolveCompleted(key)
|
|
4174
4296
|
}
|
|
@@ -4263,6 +4385,7 @@ export class FECManager {
|
|
|
4263
4385
|
this.#blockSourceEsis.clear()
|
|
4264
4386
|
this.#blockAuthoredLengths.clear()
|
|
4265
4387
|
this.#blockRepairRsIds.clear()
|
|
4388
|
+
this.#blockRsbLengths.clear()
|
|
4266
4389
|
this.#completedBlocks.clear()
|
|
4267
4390
|
this.#cleanlyCompletedBlocks.clear()
|
|
4268
4391
|
this.#repairRequestedBlocks.clear()
|