@blockcast/mmt-container 0.3.0 → 0.4.0-main.71d61b2a53f2
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 +14743 -14412
- 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 +36 -30
- package/dist/fec-manager.d.ts.map +1 -1
- package/dist/fec-manager.js +303 -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 +6 -11
- package/dist/mmtp-container-parser.d.ts.map +1 -1
- package/dist/mmtp-container-parser.js +56 -97
- package/dist/mmtp-container-parser.js.map +4 -4
- package/dist/mmtp-container-parser.no-modules.js +78 -105
- package/dist/mmtp-container-parser.no-modules.js.map +4 -4
- package/dist/mmtp-decode-pipeline.js +56 -97
- package/dist/mmtp-decode-pipeline.js.map +4 -4
- package/dist/mmtp-router.d.ts +37 -26
- package/dist/mmtp-router.d.ts.map +1 -1
- package/dist/mmtp-router.js +162 -313
- 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 +34 -6
- package/dist/multicast-manager.d.ts.map +1 -1
- package/dist/multicast-manager.js +709 -658
- 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 +77 -104
- package/dist/no-modules.js.map +4 -4
- package/dist/repair-fetch.d.ts +30 -19
- package/dist/repair-fetch.d.ts.map +1 -1
- package/dist/repair-fetch.js +33 -24
- 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 +56 -97
- 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 +414 -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 +27 -18
- package/src/mmtp-router.ts +146 -313
- package/src/multicast-manager.ts +184 -83
- package/src/multicast-transport.ts +16 -9
- package/src/repair-fetch.ts +46 -27
- package/src/repair-layers.ts +28 -8
- package/src/track-session.ts +28 -9
- package/test-vectors/catalog/README.md +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
|
@@ -1,78 +1,17 @@
|
|
|
1
|
-
// ../mmt-
|
|
1
|
+
// ../mmt-base/dist/mmtp.js
|
|
2
2
|
var MMTP_HEADER_SIZE = 12;
|
|
3
3
|
var MPU_HEADER_SIZE = 8;
|
|
4
|
-
var MPU_LENGTH_FIELD_SIZE = 2;
|
|
5
|
-
var MPU_HEADER_REMAINDER_SIZE = MPU_HEADER_SIZE - MPU_LENGTH_FIELD_SIZE;
|
|
6
|
-
var TIMED_MFU_DU_HEADER_SIZE = 14;
|
|
7
|
-
var NONTIMED_MFU_DU_HEADER_SIZE = 4;
|
|
8
|
-
var PACKET_TYPE_MPU = 0;
|
|
9
|
-
var MMTP_EXT_TYPE_ALTA_AUTH = 61601;
|
|
10
|
-
var MMTP_EXT_TRAILER_HEADER_SIZE = 4;
|
|
11
|
-
function extractAltaTrailer(mmtpPacket, options) {
|
|
12
|
-
if (mmtpPacket.length < MMTP_HEADER_SIZE + MPU_HEADER_SIZE)
|
|
13
|
-
return null;
|
|
14
|
-
const packetType = mmtpPacket[1] & 63;
|
|
15
|
-
if (packetType !== PACKET_TYPE_MPU)
|
|
16
|
-
return null;
|
|
17
|
-
const fecType = mmtpPacket[0] >> 3 & 3;
|
|
18
|
-
let headerLength = MMTP_HEADER_SIZE;
|
|
19
|
-
if (fecType < 2 && (mmtpPacket[0] & 32) !== 0)
|
|
20
|
-
headerLength += 4;
|
|
21
|
-
if (fecType < 2 && (mmtpPacket[0] & 2) !== 0) {
|
|
22
|
-
if (headerLength + 4 > mmtpPacket.length)
|
|
23
|
-
return null;
|
|
24
|
-
const extensionLength = new DataView(mmtpPacket.buffer, mmtpPacket.byteOffset + headerLength + 2, 2).getUint16(0, false);
|
|
25
|
-
headerLength += 4 + extensionLength;
|
|
26
|
-
}
|
|
27
|
-
if (headerLength + MPU_HEADER_SIZE > mmtpPacket.length)
|
|
28
|
-
return null;
|
|
29
|
-
const payload = mmtpPacket.subarray(headerLength);
|
|
30
|
-
const payloadLength = payload[0] << 8 | payload[1];
|
|
31
|
-
const flagsByte = payload[2];
|
|
32
|
-
const fragmentType = flagsByte >> 4 & 15;
|
|
33
|
-
const timed = (flagsByte >> 3 & 1) === 1;
|
|
34
|
-
const fragmentationIndicator = flagsByte >> 1 & 3;
|
|
35
|
-
if (fragmentType !== 2)
|
|
36
|
-
return null;
|
|
37
|
-
const dataUnitHeaderSize = fragmentationIndicator <= 1 ? timed ? TIMED_MFU_DU_HEADER_SIZE : NONTIMED_MFU_DU_HEADER_SIZE : 0;
|
|
38
|
-
if (payloadLength <= MPU_HEADER_REMAINDER_SIZE + dataUnitHeaderSize)
|
|
39
|
-
return null;
|
|
40
|
-
if (fecType > 1)
|
|
41
|
-
return null;
|
|
42
|
-
const hasFecId = !options?.skipFecId && fecType === 1;
|
|
43
|
-
const payloadEnd = hasFecId ? payload.length - 4 : payload.length;
|
|
44
|
-
const declaredEnd = MPU_LENGTH_FIELD_SIZE + payloadLength;
|
|
45
|
-
if (declaredEnd > payloadEnd)
|
|
46
|
-
return null;
|
|
47
|
-
if (declaredEnd + MMTP_EXT_TRAILER_HEADER_SIZE > payloadEnd)
|
|
48
|
-
return null;
|
|
49
|
-
const extType = payload[declaredEnd] << 8 | payload[declaredEnd + 1];
|
|
50
|
-
if (extType !== MMTP_EXT_TYPE_ALTA_AUTH)
|
|
51
|
-
return null;
|
|
52
|
-
const extLen = payload[declaredEnd + 2] << 8 | payload[declaredEnd + 3];
|
|
53
|
-
const authStart = declaredEnd + MMTP_EXT_TRAILER_HEADER_SIZE;
|
|
54
|
-
if (authStart + extLen !== payloadEnd)
|
|
55
|
-
return null;
|
|
56
|
-
return {
|
|
57
|
-
auth: payload.subarray(authStart, payloadEnd),
|
|
58
|
-
signedEnd: headerLength + declaredEnd
|
|
59
|
-
};
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
// ../mmt-base/dist/mmtp.js
|
|
63
|
-
var MMTP_HEADER_SIZE2 = 12;
|
|
64
|
-
var MPU_HEADER_SIZE2 = 8;
|
|
65
4
|
var MFU_DU_HEADER_SIZE_TIMED = 14;
|
|
66
5
|
var MFU_DU_HEADER_SIZE_NONTIMED = 4;
|
|
67
|
-
var
|
|
68
|
-
var
|
|
6
|
+
var MPU_LENGTH_FIELD_SIZE = 2;
|
|
7
|
+
var MPU_HEADER_REMAINDER_SIZE = MPU_HEADER_SIZE - MPU_LENGTH_FIELD_SIZE;
|
|
69
8
|
var SOURCE_FEC_PAYLOAD_ID_SIZE = 4;
|
|
70
|
-
var
|
|
9
|
+
var PACKET_TYPE_MPU = 0;
|
|
71
10
|
var PACKET_TYPE_REPAIR = 3;
|
|
72
11
|
var MMTP_SIGNALING_PACKET_ID = 0;
|
|
73
12
|
var MMTP_PACKET_ID_MAX = 65535;
|
|
74
13
|
function parseMMTPHeader(buffer) {
|
|
75
|
-
if (buffer.length <
|
|
14
|
+
if (buffer.length < MMTP_HEADER_SIZE) {
|
|
76
15
|
throw new Error(`MMTP packet too small: ${buffer.length} bytes`);
|
|
77
16
|
}
|
|
78
17
|
const view2 = new DataView(buffer.buffer, buffer.byteOffset, buffer.byteLength);
|
|
@@ -128,12 +67,12 @@ function parseMMTPHeader(buffer) {
|
|
|
128
67
|
};
|
|
129
68
|
}
|
|
130
69
|
function readMMTPPacketId(buffer) {
|
|
131
|
-
if (buffer.length <
|
|
70
|
+
if (buffer.length < MMTP_HEADER_SIZE)
|
|
132
71
|
return null;
|
|
133
72
|
const flags = buffer[0];
|
|
134
73
|
if ((flags >> 6 & 3) !== 0)
|
|
135
74
|
return null;
|
|
136
|
-
let offset =
|
|
75
|
+
let offset = MMTP_HEADER_SIZE;
|
|
137
76
|
if ((flags & 32) !== 0) {
|
|
138
77
|
if (offset + 4 > buffer.length)
|
|
139
78
|
return null;
|
|
@@ -149,7 +88,7 @@ function readMMTPPacketId(buffer) {
|
|
|
149
88
|
return buffer[2] << 8 | buffer[3];
|
|
150
89
|
}
|
|
151
90
|
function parseMPUHeader(payload) {
|
|
152
|
-
if (payload.length <
|
|
91
|
+
if (payload.length < MPU_HEADER_SIZE) {
|
|
153
92
|
throw new Error(`Payload too small for MPU header: ${payload.length} bytes`);
|
|
154
93
|
}
|
|
155
94
|
const view2 = new DataView(payload.buffer, payload.byteOffset, payload.byteLength);
|
|
@@ -182,15 +121,15 @@ function parseTimedMfuDuHeader(payload, offset) {
|
|
|
182
121
|
};
|
|
183
122
|
}
|
|
184
123
|
function resolveMfuPayloadLayout(mpu, fragmentType, payload) {
|
|
185
|
-
if (mpu.payloadLength <
|
|
124
|
+
if (mpu.payloadLength < MPU_HEADER_REMAINDER_SIZE) {
|
|
186
125
|
return {
|
|
187
126
|
rejection: {
|
|
188
127
|
gate: "mpu-payload-length-below-header-remainder",
|
|
189
|
-
detail: `payload_length=${mpu.payloadLength} < ${
|
|
128
|
+
detail: `payload_length=${mpu.payloadLength} < ${MPU_HEADER_REMAINDER_SIZE}`
|
|
190
129
|
}
|
|
191
130
|
};
|
|
192
131
|
}
|
|
193
|
-
const declaredEnd =
|
|
132
|
+
const declaredEnd = MPU_LENGTH_FIELD_SIZE + mpu.payloadLength;
|
|
194
133
|
if (declaredEnd > payload.length) {
|
|
195
134
|
return {
|
|
196
135
|
rejection: {
|
|
@@ -201,7 +140,7 @@ function resolveMfuPayloadLayout(mpu, fragmentType, payload) {
|
|
|
201
140
|
}
|
|
202
141
|
const hasHeader = fragmentType === 2 && mpu.fragmentationIndicator <= 1;
|
|
203
142
|
const mfuHeaderSize = mpu.timedFlag ? MFU_DU_HEADER_SIZE_TIMED : MFU_DU_HEADER_SIZE_NONTIMED;
|
|
204
|
-
const mfuOffset =
|
|
143
|
+
const mfuOffset = MPU_HEADER_SIZE + (hasHeader ? mfuHeaderSize : 0);
|
|
205
144
|
if (mfuOffset >= declaredEnd) {
|
|
206
145
|
return {
|
|
207
146
|
rejection: {
|
|
@@ -218,44 +157,46 @@ function resolveMfuPayloadLayout(mpu, fragmentType, payload) {
|
|
|
218
157
|
}
|
|
219
158
|
};
|
|
220
159
|
}
|
|
221
|
-
|
|
160
|
+
var LEGACY_TRAILER_EXT_TYPE = 61601;
|
|
161
|
+
var LEGACY_TRAILER_HEADER_SIZE = 4;
|
|
162
|
+
function isEmptyOrExactLegacyTrailer(payload, start, end) {
|
|
222
163
|
if (start === end)
|
|
223
164
|
return true;
|
|
224
|
-
if (start < 0 || end > payload.length || start +
|
|
165
|
+
if (start < 0 || end > payload.length || start + LEGACY_TRAILER_HEADER_SIZE > end) {
|
|
225
166
|
return false;
|
|
226
167
|
}
|
|
227
168
|
const view2 = new DataView(payload.buffer, payload.byteOffset + start, end - start);
|
|
228
|
-
if (view2.getUint16(0, false) !==
|
|
169
|
+
if (view2.getUint16(0, false) !== LEGACY_TRAILER_EXT_TYPE)
|
|
229
170
|
return false;
|
|
230
171
|
const authLength = view2.getUint16(2, false);
|
|
231
|
-
return start +
|
|
172
|
+
return start + LEGACY_TRAILER_HEADER_SIZE + authLength === end;
|
|
232
173
|
}
|
|
233
174
|
function resolveDeclaredTrailers(payload, declaredEnd, fecType, options) {
|
|
234
175
|
if (declaredEnd > payload.length)
|
|
235
176
|
return null;
|
|
236
177
|
if (options?.skipFecId) {
|
|
237
|
-
return
|
|
178
|
+
return isEmptyOrExactLegacyTrailer(payload, declaredEnd, payload.length) ? { hasFecId: false } : null;
|
|
238
179
|
}
|
|
239
180
|
if (fecType === 1) {
|
|
240
181
|
const fecStart = payload.length - SOURCE_FEC_PAYLOAD_ID_SIZE;
|
|
241
|
-
if (fecStart < declaredEnd || !
|
|
182
|
+
if (fecStart < declaredEnd || !isEmptyOrExactLegacyTrailer(payload, declaredEnd, fecStart)) {
|
|
242
183
|
return null;
|
|
243
184
|
}
|
|
244
185
|
return { hasFecId: true };
|
|
245
186
|
}
|
|
246
187
|
if (fecType !== 0)
|
|
247
188
|
return null;
|
|
248
|
-
if (
|
|
189
|
+
if (isEmptyOrExactLegacyTrailer(payload, declaredEnd, payload.length)) {
|
|
249
190
|
return { hasFecId: false };
|
|
250
191
|
}
|
|
251
192
|
return null;
|
|
252
193
|
}
|
|
253
194
|
function planMfuExtraction(mmtpPacket, options, parsedHeader) {
|
|
254
|
-
if (mmtpPacket.length <
|
|
195
|
+
if (mmtpPacket.length < MMTP_HEADER_SIZE + MPU_HEADER_SIZE) {
|
|
255
196
|
return {
|
|
256
197
|
rejection: {
|
|
257
198
|
gate: "packet-shorter-than-mmtp-plus-mpu-header",
|
|
258
|
-
detail: `${mmtpPacket.length} bytes < ${
|
|
199
|
+
detail: `${mmtpPacket.length} bytes < ${MMTP_HEADER_SIZE + MPU_HEADER_SIZE}`
|
|
259
200
|
}
|
|
260
201
|
};
|
|
261
202
|
}
|
|
@@ -274,7 +215,7 @@ function planMfuExtraction(mmtpPacket, options, parsedHeader) {
|
|
|
274
215
|
};
|
|
275
216
|
}
|
|
276
217
|
}
|
|
277
|
-
if (header.packetType !==
|
|
218
|
+
if (header.packetType !== PACKET_TYPE_MPU) {
|
|
278
219
|
return {
|
|
279
220
|
rejection: {
|
|
280
221
|
gate: "not-an-mpu-packet",
|
|
@@ -283,11 +224,11 @@ function planMfuExtraction(mmtpPacket, options, parsedHeader) {
|
|
|
283
224
|
};
|
|
284
225
|
}
|
|
285
226
|
const payload = mmtpPacket.subarray(header.headerLength);
|
|
286
|
-
if (payload.length <
|
|
227
|
+
if (payload.length < MPU_HEADER_SIZE) {
|
|
287
228
|
return {
|
|
288
229
|
rejection: {
|
|
289
230
|
gate: "payload-shorter-than-mpu-header",
|
|
290
|
-
detail: `${payload.length} bytes < ${
|
|
231
|
+
detail: `${payload.length} bytes < ${MPU_HEADER_SIZE}`
|
|
291
232
|
}
|
|
292
233
|
};
|
|
293
234
|
}
|
|
@@ -318,7 +259,7 @@ function planMfuExtraction(mmtpPacket, options, parsedHeader) {
|
|
|
318
259
|
return {
|
|
319
260
|
rejection: {
|
|
320
261
|
gate: "declared-trailers-unresolved",
|
|
321
|
-
detail: `trailer bytes [${layout.dataEnd}, ${payload.length}) are neither empty nor an exact
|
|
262
|
+
detail: `trailer bytes [${layout.dataEnd}, ${payload.length}) are neither empty nor an exact legacy authenticator trailer (fecType=${header.fecType})`
|
|
322
263
|
}
|
|
323
264
|
};
|
|
324
265
|
}
|
|
@@ -334,8 +275,8 @@ function extractPlannedMfu(mmtpPacket, result) {
|
|
|
334
275
|
const mfuDuHeaderPresent = layout.hasMfuDuHeader;
|
|
335
276
|
const mfuOffset = layout.mfuOffset;
|
|
336
277
|
const dataEnd = layout.dataEnd;
|
|
337
|
-
const mfuDuHeader = mfuDuHeaderPresent && mpu.timedFlag === 1 ? parseTimedMfuDuHeader(payload,
|
|
338
|
-
const itemId = mfuDuHeaderPresent && mpu.timedFlag === 0 ? new DataView(payload.buffer, payload.byteOffset +
|
|
278
|
+
const mfuDuHeader = mfuDuHeaderPresent && mpu.timedFlag === 1 ? parseTimedMfuDuHeader(payload, MPU_HEADER_SIZE) : null;
|
|
279
|
+
const itemId = mfuDuHeaderPresent && mpu.timedFlag === 0 ? new DataView(payload.buffer, payload.byteOffset + MPU_HEADER_SIZE, 4).getUint32(0, false) : null;
|
|
339
280
|
const data = payload.subarray(mfuOffset, dataEnd);
|
|
340
281
|
const fecPayloadId = trailerLayout.hasFecId ? {
|
|
341
282
|
ssId: new DataView(mmtpPacket.buffer, mmtpPacket.byteOffset + mmtpPacket.length - SOURCE_FEC_PAYLOAD_ID_SIZE, SOURCE_FEC_PAYLOAD_ID_SIZE).getUint32(0, false)
|
|
@@ -343,17 +284,17 @@ function extractPlannedMfu(mmtpPacket, result) {
|
|
|
343
284
|
return { header, mpu, fragmentType, data, mfuDuHeader, itemId, fecPayloadId };
|
|
344
285
|
}
|
|
345
286
|
function sourceFecPayloadIdOf(mmtpPacket, header) {
|
|
346
|
-
if (mmtpPacket.length <
|
|
287
|
+
if (mmtpPacket.length < MMTP_HEADER_SIZE + MPU_HEADER_SIZE)
|
|
347
288
|
return null;
|
|
348
|
-
if (header.packetType !==
|
|
289
|
+
if (header.packetType !== PACKET_TYPE_MPU)
|
|
349
290
|
return null;
|
|
350
291
|
const payload = mmtpPacket.subarray(header.headerLength);
|
|
351
|
-
if (payload.length <
|
|
292
|
+
if (payload.length < MPU_HEADER_SIZE)
|
|
352
293
|
return null;
|
|
353
294
|
const payloadLength = payload[0] << 8 | payload[1];
|
|
354
|
-
if (payloadLength <
|
|
295
|
+
if (payloadLength < MPU_HEADER_REMAINDER_SIZE)
|
|
355
296
|
return null;
|
|
356
|
-
const declaredEnd =
|
|
297
|
+
const declaredEnd = MPU_LENGTH_FIELD_SIZE + payloadLength;
|
|
357
298
|
if (!resolveDeclaredTrailers(payload, declaredEnd, header.fecType)?.hasFecId)
|
|
358
299
|
return null;
|
|
359
300
|
const off = mmtpPacket.length - SOURCE_FEC_PAYLOAD_ID_SIZE;
|
|
@@ -361,7 +302,6 @@ function sourceFecPayloadIdOf(mmtpPacket, header) {
|
|
|
361
302
|
ssId: (mmtpPacket[off] << 24 | mmtpPacket[off + 1] << 16 | mmtpPacket[off + 2] << 8 | mmtpPacket[off + 3]) >>> 0
|
|
362
303
|
};
|
|
363
304
|
}
|
|
364
|
-
var extractAltaTrailer2 = extractAltaTrailer;
|
|
365
305
|
var REPAIR_FEC_PID_SIZE = 13;
|
|
366
306
|
function readRepairFecPayloadIdFields(mmtpPacket, parsedHeader) {
|
|
367
307
|
let header;
|
|
@@ -441,49 +381,21 @@ function serial32Precedes(a, b) {
|
|
|
441
381
|
const distance = serial32Distance(a, b);
|
|
442
382
|
return distance !== 0 && distance < SERIAL32_HALF;
|
|
443
383
|
}
|
|
444
|
-
function kStrideBlockStart(sbn, k) {
|
|
445
|
-
assertUint32(sbn, "sbn");
|
|
446
|
-
if (!Number.isInteger(k) || k < 1 || k >= SERIAL32_HALF) {
|
|
447
|
-
throw new RangeError(`k must be a positive integer below 2^31 (got ${k})`);
|
|
448
|
-
}
|
|
449
|
-
const high = Math.floor(sbn / 65536);
|
|
450
|
-
const low = sbn % 65536;
|
|
451
|
-
return (high * k % SERIAL32_SPAN * 65536 + low * k) % SERIAL32_SPAN;
|
|
452
|
-
}
|
|
453
|
-
function kStrideBlockPrecedes(a, b, k) {
|
|
454
|
-
return serial32Precedes(kStrideBlockStart(a, k), kStrideBlockStart(b, k));
|
|
455
|
-
}
|
|
456
|
-
function kStrideBlocksBehind(a, b, k) {
|
|
457
|
-
const from = kStrideBlockStart(a, k);
|
|
458
|
-
const to = kStrideBlockStart(b, k);
|
|
459
|
-
if (!serial32Precedes(from, to))
|
|
460
|
-
return 0;
|
|
461
|
-
return Math.ceil(serial32Distance(from, to) / k);
|
|
462
|
-
}
|
|
463
|
-
function isKStrideBlock(sbn, k) {
|
|
464
|
-
return Number.isInteger(sbn) && sbn >= 0 && sbn < SERIAL32_SPAN && Number.isInteger(k) && k >= 1 && k < SERIAL32_HALF;
|
|
465
|
-
}
|
|
466
|
-
function kStrideNextBlock(sbn, k) {
|
|
467
|
-
assertUint32(sbn, "sbn");
|
|
468
|
-
kStrideBlockStart(sbn, k);
|
|
469
|
-
return (sbn + 1) * k >= SERIAL32_SPAN ? 0 : sbn + 1;
|
|
470
|
-
}
|
|
471
384
|
|
|
472
385
|
// ../mmt-base/dist/fec-block-profile.js
|
|
473
386
|
var FEC_BLOCK_PROFILES = Object.freeze([
|
|
474
|
-
"k-padded-v1",
|
|
475
387
|
"iso-ssbg1-v1"
|
|
476
388
|
]);
|
|
477
389
|
function isFecBlockProfile(value) {
|
|
478
|
-
return value === "
|
|
390
|
+
return value === "iso-ssbg1-v1";
|
|
479
391
|
}
|
|
480
392
|
function parseFecBlockProfile(value, where) {
|
|
481
393
|
if (isFecBlockProfile(value))
|
|
482
394
|
return value;
|
|
483
395
|
if (value === void 0) {
|
|
484
|
-
throw new Error(`${where} is required: name the FEC block profile ("
|
|
396
|
+
throw new Error(`${where} is required: name the FEC block profile ("iso-ssbg1-v1")`);
|
|
485
397
|
}
|
|
486
|
-
throw new Error(`${where} must be "
|
|
398
|
+
throw new Error(`${where} must be "iso-ssbg1-v1" (got ${JSON.stringify(value)})`);
|
|
487
399
|
}
|
|
488
400
|
var SSBG1_LENGTH_FIELD_BYTES = 2;
|
|
489
401
|
function ssbg1MaxPacketLength(symbolSize) {
|
|
@@ -508,20 +420,22 @@ function isoBlocksBehindLowerBound(a, b, kMax) {
|
|
|
508
420
|
// src/track-session.ts
|
|
509
421
|
import { fecBlockBudgetMs } from "@blockcast/mmt-fec";
|
|
510
422
|
function computeFecDeliveryWindowMs(config) {
|
|
511
|
-
const fecInterleaveMs = config.interleaveDepthMs;
|
|
512
423
|
const fecK = config.sourceSymbols;
|
|
513
|
-
if (!Number.isFinite(
|
|
424
|
+
if (!Number.isFinite(fecK) || fecK <= 0) {
|
|
514
425
|
throw new Error(
|
|
515
|
-
`[TrackSession]
|
|
426
|
+
`[TrackSession] fecK=${fecK}. Derive a positive FEC source-symbol count from the stream catalog.`
|
|
516
427
|
);
|
|
517
428
|
}
|
|
518
|
-
|
|
429
|
+
return fecSpanDeliveryWindowMs(config.interleaveDepthMs, config.jitterBufferMs);
|
|
430
|
+
}
|
|
431
|
+
function fecSpanDeliveryWindowMs(interleaveDepthMs, jitterBufferMs) {
|
|
432
|
+
if (!Number.isFinite(interleaveDepthMs) || interleaveDepthMs <= 0) {
|
|
519
433
|
throw new Error(
|
|
520
|
-
`[TrackSession]
|
|
434
|
+
`[TrackSession] fecInterleaveMs=${interleaveDepthMs}. Derive a positive FEC interleave window from the stream catalog.`
|
|
521
435
|
);
|
|
522
436
|
}
|
|
523
|
-
if (
|
|
524
|
-
return fecBlockBudgetMs(
|
|
437
|
+
if (jitterBufferMs === void 0) return Math.ceil(interleaveDepthMs);
|
|
438
|
+
return fecBlockBudgetMs(interleaveDepthMs, jitterBufferMs);
|
|
525
439
|
}
|
|
526
440
|
|
|
527
441
|
// src/delivery-gap.ts
|
|
@@ -613,7 +527,9 @@ var DeliveryGapEstimator = class {
|
|
|
613
527
|
import {
|
|
614
528
|
deriveFecInterleaveDepth as deriveFecInterleaveDepth2,
|
|
615
529
|
deriveFecRetentionBlocks,
|
|
616
|
-
tryDeriveFecRetentionBlocks
|
|
530
|
+
tryDeriveFecRetentionBlocks,
|
|
531
|
+
LOCAL_FEC_RETENTION_BLOCKS,
|
|
532
|
+
LOCAL_FEC_TIMEOUT_MS
|
|
617
533
|
} from "@blockcast/mmt-fec";
|
|
618
534
|
|
|
619
535
|
// src/iso-block-locator.ts
|
|
@@ -847,7 +763,7 @@ var IsoBlockLocator = class _IsoBlockLocator {
|
|
|
847
763
|
};
|
|
848
764
|
|
|
849
765
|
// src/fec-state.ts
|
|
850
|
-
var MIN_INTERLEAVE_DEPTH_MS =
|
|
766
|
+
var MIN_INTERLEAVE_DEPTH_MS = 1;
|
|
851
767
|
var FEC_INACTIVE_REASONS = Object.freeze([
|
|
852
768
|
"no-catalog-fec",
|
|
853
769
|
"reliable-moq",
|
|
@@ -858,6 +774,15 @@ var INACTIVE = Object.freeze({
|
|
|
858
774
|
"reliable-moq": Object.freeze({ state: "inactive", reason: "reliable-moq" }),
|
|
859
775
|
"no-multicast-bridge": Object.freeze({ state: "inactive", reason: "no-multicast-bridge" })
|
|
860
776
|
});
|
|
777
|
+
function recordOf(value) {
|
|
778
|
+
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
|
|
779
|
+
}
|
|
780
|
+
function isoBlockSpanUsable(fec) {
|
|
781
|
+
const block = recordOf(fec);
|
|
782
|
+
if (block === void 0 || block["blockProfile"] !== "iso-ssbg1-v1") return true;
|
|
783
|
+
const ms = block["interleaveDepthMs"];
|
|
784
|
+
return ms === void 0 || typeof ms === "number" && Number.isSafeInteger(ms) && ms >= 0;
|
|
785
|
+
}
|
|
861
786
|
|
|
862
787
|
// src/block-policy.ts
|
|
863
788
|
import { deriveFecInterleaveDepth } from "@blockcast/mmt-fec";
|
|
@@ -1366,7 +1291,6 @@ var FECManager = class _FECManager {
|
|
|
1366
1291
|
constructor() {
|
|
1367
1292
|
// Per-track WASM RaptorQ decoders, keyed by physical stream identity.
|
|
1368
1293
|
this.#fecDecoders = /* @__PURE__ */ new Map();
|
|
1369
|
-
/** @deprecated Use setFecDecoder(packetId, ...). Backwards-compat shim. */
|
|
1370
1294
|
// Per-track Worker-backed FEC decoders, keyed by physical stream identity.
|
|
1371
1295
|
// Worker-backed tracks use async decode — recovered frames arrive via onFrame callback,
|
|
1372
1296
|
// not synchronous return from feedSourceSymbol/feedRepairSymbol.
|
|
@@ -1381,10 +1305,9 @@ var FECManager = class _FECManager {
|
|
|
1381
1305
|
// source/repair symbols do not split across two RaptorQ states.
|
|
1382
1306
|
this.#syncDecoderBlockOwners = /* @__PURE__ */ new Map();
|
|
1383
1307
|
this.#decoderBlockOwners = /* @__PURE__ */ new WeakMap();
|
|
1384
|
-
this.#fecK = 4;
|
|
1385
1308
|
this.#fecSymbolSize = 0;
|
|
1386
|
-
this.#fecTimeoutMs =
|
|
1387
|
-
// default
|
|
1309
|
+
this.#fecTimeoutMs = LOCAL_FEC_TIMEOUT_MS;
|
|
1310
|
+
// local default; refined by setFramerate() when fps known
|
|
1388
1311
|
this.#trackConfigs = /* @__PURE__ */ new Map();
|
|
1389
1312
|
this.#fecStats = newFecCounters();
|
|
1390
1313
|
this.#fecTrackStats = /* @__PURE__ */ new Map();
|
|
@@ -1424,10 +1347,10 @@ var FECManager = class _FECManager {
|
|
|
1424
1347
|
this.#blockSourceEsis = /* @__PURE__ */ new Map();
|
|
1425
1348
|
// Authored SSB_length per block (ISO/IEC 23008-1 §C.5.3), learned from the
|
|
1426
1349
|
// first repair symbol. The FFmpeg moq_mmt publisher closes a block every D
|
|
1427
|
-
// media units, usually before it holds K symbols
|
|
1428
|
-
//
|
|
1429
|
-
// SSB_length
|
|
1430
|
-
//
|
|
1350
|
+
// media units, usually before it holds K symbols; under iso-ssbg1-v1
|
|
1351
|
+
// (ssbg_mode1) that block is K' = SSB_length symbols, its repair ESIs start
|
|
1352
|
+
// at SSB_length, and K is only the maximum. Such a block is clean at
|
|
1353
|
+
// SSB_length source arrivals and recoverable from SSB_length received
|
|
1431
1354
|
// symbols; waiting for K fails every one of them.
|
|
1432
1355
|
this.#blockAuthoredLengths = /* @__PURE__ */ new Map();
|
|
1433
1356
|
this.#ssbLengthConflictCount = 0;
|
|
@@ -1544,14 +1467,16 @@ var FECManager = class _FECManager {
|
|
|
1544
1467
|
this.#resolvedBlockClasses = /* @__PURE__ */ new Map();
|
|
1545
1468
|
// Blocks already reported through onBlockRepaired (once per block).
|
|
1546
1469
|
this.#repairedBlocks = /* @__PURE__ */ new Set();
|
|
1547
|
-
// Blocks created
|
|
1470
|
+
// Blocks created before any symbol of theirs was tracked: the repair-less
|
|
1471
|
+
// spans placeIsoRepair materializes (onBlockVanished).
|
|
1548
1472
|
this.#materializedBlocks = /* @__PURE__ */ new Set();
|
|
1549
1473
|
// packetIds whose ordered-relay horizon became underivable and fell back to
|
|
1550
1474
|
// the datagram deadline. Bounds the warn in #derivableOrderedHorizon to once
|
|
1551
1475
|
// per stream. Not only throws: the ordinary out-of-range case is detected by
|
|
1552
1476
|
// arithmetic and never throws at all.
|
|
1553
1477
|
this.#orderedHorizonWarnedFor = /* @__PURE__ */ new Set();
|
|
1554
|
-
//
|
|
1478
|
+
// Newest block start (SS_Start) seen per stream and when: the frontier for
|
|
1479
|
+
// source-discontinuity detection and isoSpanResolution.
|
|
1555
1480
|
this.#highestSbn = /* @__PURE__ */ new Map();
|
|
1556
1481
|
// iso-ssbg1-v1 block location per stream (streamKey). A source belongs to a
|
|
1557
1482
|
// block only once a repair symbol stated the block's (SS_Start, SSB_length).
|
|
@@ -1645,7 +1570,10 @@ var FECManager = class _FECManager {
|
|
|
1645
1570
|
#releasedWorkerClients;
|
|
1646
1571
|
#syncDecoderBlockOwners;
|
|
1647
1572
|
#decoderBlockOwners;
|
|
1648
|
-
|
|
1573
|
+
// The stream-global K a configure() call stated, reported as fecStats.k.
|
|
1574
|
+
// It never resolves a packetId: a packetId has only its own track config
|
|
1575
|
+
// (BLO-45953), and K does not feed the delivery-window math.
|
|
1576
|
+
#streamK;
|
|
1649
1577
|
#fecSymbolSize;
|
|
1650
1578
|
#fecTimeoutMs;
|
|
1651
1579
|
#trackConfigs;
|
|
@@ -1807,7 +1735,8 @@ var FECManager = class _FECManager {
|
|
|
1807
1735
|
this.#completedBlocks.add(key);
|
|
1808
1736
|
if (clean) this.#cleanlyCompletedBlocks.add(key);
|
|
1809
1737
|
this.#indexBlockKey(key, sbn, packetId);
|
|
1810
|
-
const
|
|
1738
|
+
const config = this.#trackConfig(packetId);
|
|
1739
|
+
const expiresAtMs = nowMs + config.interleaveDepthMs + 2 * this.#blockLifetimeMs(config);
|
|
1811
1740
|
this.#terminalExpiryMs.set(key, expiresAtMs);
|
|
1812
1741
|
let queue = this.#terminalQueues.get(packetId);
|
|
1813
1742
|
if (!queue) {
|
|
@@ -1843,8 +1772,9 @@ var FECManager = class _FECManager {
|
|
|
1843
1772
|
* retention the Worker and the WASM decoder are configured with, because it
|
|
1844
1773
|
* comes from the same function they do (`deriveFecRetentionBlocks`, over the
|
|
1845
1774
|
* block's delivery window and its span). The decoder retires every block
|
|
1846
|
-
* more than this many
|
|
1847
|
-
*
|
|
1775
|
+
* more than this many blocks behind the newest SS_Start it saw, counted
|
|
1776
|
+
* from below by `isoBlocksBehindLowerBound` (`cleanup_by_ss_start`), so
|
|
1777
|
+
* settling earlier than this would decide a block the decoder would still
|
|
1848
1778
|
* have recovered.
|
|
1849
1779
|
*
|
|
1850
1780
|
* This used to call `deriveFecInterleaveDepth`, which returns D -- MEDIA
|
|
@@ -1857,6 +1787,14 @@ var FECManager = class _FECManager {
|
|
|
1857
1787
|
* derivation; do not re-derive a horizon at either call site.
|
|
1858
1788
|
*/
|
|
1859
1789
|
#orderedHorizonBlocksFor(packetId, config) {
|
|
1790
|
+
if (config.interleaveDepthMs === 0) {
|
|
1791
|
+
if (Number.isFinite(config.timeoutMs) && config.timeoutMs > 0) {
|
|
1792
|
+
return LOCAL_FEC_RETENTION_BLOCKS;
|
|
1793
|
+
}
|
|
1794
|
+
throw new Error(
|
|
1795
|
+
`[FECManager] packetId=${packetId}: local FEC cleanup requires a positive timeout (got ${config.timeoutMs})`
|
|
1796
|
+
);
|
|
1797
|
+
}
|
|
1860
1798
|
if (!_FECManager.#hasOrderedGeometry(config)) {
|
|
1861
1799
|
throw new Error(
|
|
1862
1800
|
`[FECManager] packetId=${packetId}: ordered-relay settlement derives the decoder's interleave horizon from the catalog interleaveDepthMs (>= ${MIN_INTERLEAVE_DEPTH_MS}ms), the media-unit rate and the block delivery window; got interleaveDepthMs=${config.interleaveDepthMs} fps=${String(config.fps)} timeoutMs=${config.timeoutMs}.`
|
|
@@ -1870,7 +1808,7 @@ var FECManager = class _FECManager {
|
|
|
1870
1808
|
* two can never disagree about which streams settle by stream order.
|
|
1871
1809
|
*/
|
|
1872
1810
|
static #hasOrderedGeometry(config) {
|
|
1873
|
-
return config
|
|
1811
|
+
return _FECManager.#hasCatalogTiming(config) && // `fps` is no longer an input to the horizon, but it still gates WHICH
|
|
1874
1812
|
// streams settle by stream order at all: a track with no usable
|
|
1875
1813
|
// media-unit rate fell back to datagram semantics before this and must
|
|
1876
1814
|
// keep doing so. Removing this widens ordered-relay settlement to streams
|
|
@@ -1910,6 +1848,9 @@ var FECManager = class _FECManager {
|
|
|
1910
1848
|
* argument: narrower IS the failing direction.
|
|
1911
1849
|
*/
|
|
1912
1850
|
#derivableOrderedHorizon(config, packetId) {
|
|
1851
|
+
if (config.interleaveDepthMs === 0) {
|
|
1852
|
+
return Number.isFinite(config.timeoutMs) && config.timeoutMs > 0 ? LOCAL_FEC_RETENTION_BLOCKS : void 0;
|
|
1853
|
+
}
|
|
1913
1854
|
if (!_FECManager.#hasOrderedGeometry(config)) return void 0;
|
|
1914
1855
|
try {
|
|
1915
1856
|
const blocks = tryDeriveFecRetentionBlocks(config.timeoutMs, config.interleaveDepthMs);
|
|
@@ -1942,19 +1883,20 @@ var FECManager = class _FECManager {
|
|
|
1942
1883
|
/**
|
|
1943
1884
|
* The stream's interleave horizon, or undefined when its geometry cannot
|
|
1944
1885
|
* define one. Two different streams reach that case, with different causes:
|
|
1945
|
-
* a packetId that was never configured,
|
|
1946
|
-
*
|
|
1886
|
+
* a packetId that was never configured, which has no geometry at all
|
|
1887
|
+
* (explicitly configured tracks are refused an underivable geometry
|
|
1947
1888
|
* at config time — #upsertTrackConfig, setMulticastSymbolDelivery); or a
|
|
1948
1889
|
* configured track whose timeoutMs was later widened past the 255-block
|
|
1949
1890
|
* ceiling without re-validating, which #derivableOrderedHorizon catches
|
|
1950
1891
|
* rather than throwing from the packet-feed path. Either way the stream
|
|
1951
|
-
* keeps datagram semantics instead of throwing.
|
|
1892
|
+
* keeps datagram semantics instead of throwing. A track's configuration can
|
|
1952
1893
|
* also change under a stream that already holds blocks, so a caller cannot
|
|
1953
1894
|
* assume a horizon that was defined when a block was held is still defined
|
|
1954
1895
|
* (see #advanceOrderedFrontier).
|
|
1955
1896
|
*/
|
|
1956
1897
|
#orderedHorizonBlocks(packetId) {
|
|
1957
|
-
|
|
1898
|
+
const config = this.#trackConfigs.get(packetId);
|
|
1899
|
+
return config === void 0 ? void 0 : this.#derivableOrderedHorizon(config, packetId);
|
|
1958
1900
|
}
|
|
1959
1901
|
/** True while a block of an ordered-relay stream can still receive symbols. */
|
|
1960
1902
|
#withinOrderedHorizon(key) {
|
|
@@ -1988,7 +1930,7 @@ var FECManager = class _FECManager {
|
|
|
1988
1930
|
*/
|
|
1989
1931
|
#advanceOrderedFrontier(streamKey, sbn, packetId, catalogTrackName) {
|
|
1990
1932
|
const previous = this.#orderedFrontiers.get(streamKey);
|
|
1991
|
-
const frontier = previous === void 0 || this.#blockPrecedes(previous, sbn
|
|
1933
|
+
const frontier = previous === void 0 || this.#blockPrecedes(previous, sbn) ? sbn : previous;
|
|
1992
1934
|
if (frontier !== previous) this.#orderedFrontiers.set(streamKey, frontier);
|
|
1993
1935
|
const pending = this.#orderedPendingBlocks.get(streamKey);
|
|
1994
1936
|
if (!pending) return;
|
|
@@ -2023,7 +1965,7 @@ var FECManager = class _FECManager {
|
|
|
2023
1965
|
if (this.#completedBlocks.has(key) || this.#pendingWorkerCleanups.has(key) || this.#repairRequestedBlocks.has(key)) return;
|
|
2024
1966
|
const authoredLength = this.#blockAuthoredLengths.get(key);
|
|
2025
1967
|
const received = (this.#blockSourceCounts.get(key) ?? 0) + (this.#blockRepairRsIds.get(key)?.size ?? 0);
|
|
2026
|
-
if (this.onRepairNeeded && (authoredLength === void 0 || received < authoredLength)) {
|
|
1968
|
+
if (this.#hasCatalogTimingForPacket(packetId) && this.onRepairNeeded && (authoredLength === void 0 || received < authoredLength)) {
|
|
2027
1969
|
this.#requestRepair(sbn, packetId, catalogTrackName, true);
|
|
2028
1970
|
return;
|
|
2029
1971
|
}
|
|
@@ -2179,7 +2121,7 @@ var FECManager = class _FECManager {
|
|
|
2179
2121
|
}
|
|
2180
2122
|
#requestRepair(sbn, packetId, catalogTrackName, settledByOrder = false) {
|
|
2181
2123
|
const key = this.#blockKey(sbn, packetId, catalogTrackName);
|
|
2182
|
-
if (this.#completedBlocks.has(key) || !this.onRepairNeeded || this.#repairRequestedBlocks.has(key)) return;
|
|
2124
|
+
if (this.#completedBlocks.has(key) || !this.#hasCatalogTimingForPacket(packetId) || !this.onRepairNeeded || this.#repairRequestedBlocks.has(key)) return;
|
|
2183
2125
|
const deadline = this.#repairDeadlines.get(key);
|
|
2184
2126
|
if (deadline?.handle != null) clearTimeout(deadline.handle);
|
|
2185
2127
|
this.#repairDeadlines.set(key, null);
|
|
@@ -2259,6 +2201,7 @@ var FECManager = class _FECManager {
|
|
|
2259
2201
|
}
|
|
2260
2202
|
/** A block's live class without creating classifier state for the stream. */
|
|
2261
2203
|
#peekClass(sbn, packetId, catalogTrackName) {
|
|
2204
|
+
if (!this.#hasCatalogTimingForPacket(packetId)) return "unknown";
|
|
2262
2205
|
const classifier = this.#classifiers.get(this.#streamKey(packetId, catalogTrackName));
|
|
2263
2206
|
if (classifier) return classifier.classOf(sbn);
|
|
2264
2207
|
return this.#geometryFor(packetId, catalogTrackName).trackKind === "audio" ? "audio" : "unknown";
|
|
@@ -2280,8 +2223,8 @@ var FECManager = class _FECManager {
|
|
|
2280
2223
|
// Deliberately one block tighter than deriveFecRetentionBlocks over the same
|
|
2281
2224
|
// inputs (that function's `+ 1`). Different question: the retention horizon
|
|
2282
2225
|
// asks how long the decoder still ACCEPTS symbols for a block, this asks how
|
|
2283
|
-
//
|
|
2284
|
-
// #noteSbnSeen).
|
|
2226
|
+
// many blocks a stream can live behind (or ahead of) its frontier before a
|
|
2227
|
+
// source symbol there is a discontinuity rather than loss (see #noteSbnSeen).
|
|
2285
2228
|
// The extra block of retention is slack for a block still being fed; a block
|
|
2286
2229
|
// nothing has been heard from does not get it. Not the units defect — but it
|
|
2287
2230
|
// is the next place the two can drift, so change both or neither.
|
|
@@ -2290,27 +2233,28 @@ var FECManager = class _FECManager {
|
|
|
2290
2233
|
return Math.ceil(config.timeoutMs / config.interleaveDepthMs);
|
|
2291
2234
|
}
|
|
2292
2235
|
/**
|
|
2293
|
-
* Record that a symbol for block `sbn`
|
|
2294
|
-
*
|
|
2236
|
+
* Record that a symbol for block `sbn` (the block's SS_Start) arrived and
|
|
2237
|
+
* advance the stream's frontier, the newest block start seen.
|
|
2295
2238
|
*
|
|
2296
|
-
*
|
|
2297
|
-
*
|
|
2298
|
-
*
|
|
2299
|
-
*
|
|
2300
|
-
*
|
|
2301
|
-
* its last symbol, is a source discontinuity, not loss. A repair symbol can
|
|
2302
|
-
* only advance the frontier: a D-28 fetch legitimately answers for an old
|
|
2303
|
-
* block, so it never signals a discontinuity.
|
|
2239
|
+
* A SOURCE symbol whose block goes back further than one live window, or
|
|
2240
|
+
* jumps further than the stream could have produced since its last symbol,
|
|
2241
|
+
* is a source discontinuity, not loss. A repair symbol can only advance the
|
|
2242
|
+
* frontier: a D-28 fetch legitimately answers for an old block, so it never
|
|
2243
|
+
* signals a discontinuity.
|
|
2304
2244
|
*
|
|
2305
2245
|
* The producer closes a block at its Dth media-unit boundary OR when K
|
|
2306
|
-
* source symbols filled it, so one interleave span can start several
|
|
2307
|
-
*
|
|
2308
|
-
*
|
|
2309
|
-
*
|
|
2310
|
-
*
|
|
2311
|
-
*
|
|
2312
|
-
*
|
|
2313
|
-
*
|
|
2246
|
+
* source symbols filled it, so one interleave span can start several blocks
|
|
2247
|
+
* (every large keyframe does). What bounds a forward jump is therefore
|
|
2248
|
+
* packet evidence, not a block rate: SS_ID advances by one per FEC source
|
|
2249
|
+
* packet (ISO/IEC 23008-1 C.5.2) and the MMTP packet_sequence_number by one
|
|
2250
|
+
* per packet of the packet_id, so with both source symbols'
|
|
2251
|
+
* packet_sequence_number a block start can move at most the packets sent
|
|
2252
|
+
* between them plus the anchor's own offset into its block (< K). Without
|
|
2253
|
+
* it, only the D-close rate is known.
|
|
2254
|
+
*
|
|
2255
|
+
* Vanished blocks are not enumerated here: their starts are unknown until a
|
|
2256
|
+
* repair states them. The SS_IDs between two stated blocks become one
|
|
2257
|
+
* repair-less span when the later block's repair arrives (placeIsoRepair).
|
|
2314
2258
|
*/
|
|
2315
2259
|
#noteSbnSeen(sbn, packetId, catalogTrackName, symbol, packetSequenceNumber) {
|
|
2316
2260
|
const streamKey = this.#streamKey(packetId, catalogTrackName);
|
|
@@ -2319,7 +2263,7 @@ var FECManager = class _FECManager {
|
|
|
2319
2263
|
const anchor = this.#sourceAnchors.get(streamKey);
|
|
2320
2264
|
const psn = symbol === "source" && packetSequenceNumber !== void 0 && Number.isSafeInteger(packetSequenceNumber) && packetSequenceNumber >= 0 && packetSequenceNumber <= 4294967295 ? packetSequenceNumber : void 0;
|
|
2321
2265
|
const anchorHere = () => {
|
|
2322
|
-
if (psn !== void 0 && (anchor === void 0 || !this.#blockPrecedes(sbn, anchor.sbn
|
|
2266
|
+
if (psn !== void 0 && (anchor === void 0 || !this.#blockPrecedes(sbn, anchor.sbn))) {
|
|
2323
2267
|
this.#sourceAnchors.set(streamKey, { sbn, psn, atMs: now });
|
|
2324
2268
|
}
|
|
2325
2269
|
};
|
|
@@ -2335,9 +2279,9 @@ var FECManager = class _FECManager {
|
|
|
2335
2279
|
anchorHere();
|
|
2336
2280
|
return;
|
|
2337
2281
|
}
|
|
2338
|
-
const config = this.#
|
|
2282
|
+
const config = this.#trackConfig(packetId);
|
|
2339
2283
|
const window2 = this.#liveWindowBlocks(config);
|
|
2340
|
-
if (!this.#blockPrecedes(previous.sbn, sbn
|
|
2284
|
+
if (!this.#blockPrecedes(previous.sbn, sbn)) {
|
|
2341
2285
|
if (symbol === "source" && window2 !== null && this.#blocksBehind(sbn, previous.sbn, packetId) > window2) {
|
|
2342
2286
|
discontinuity();
|
|
2343
2287
|
return;
|
|
@@ -2348,15 +2292,9 @@ var FECManager = class _FECManager {
|
|
|
2348
2292
|
const missing = this.#blocksBehind(previous.sbn, sbn, packetId) - 1;
|
|
2349
2293
|
let plausible = true;
|
|
2350
2294
|
if (window2 !== null) {
|
|
2351
|
-
if (
|
|
2295
|
+
if (psn !== void 0 && anchor !== void 0 && this.#blockPrecedes(anchor.sbn, sbn) && psn !== anchor.psn) {
|
|
2352
2296
|
const sent = psn - anchor.psn >>> 0;
|
|
2353
2297
|
plausible = sent <= 2147483647 && serial32Distance(anchor.sbn, sbn) <= sent + config.k;
|
|
2354
|
-
} else if (psn !== void 0 && anchor !== void 0 && this.#blockPrecedes(anchor.sbn, sbn, packetId) && psn !== anchor.psn) {
|
|
2355
|
-
const sent = psn - anchor.psn >>> 0;
|
|
2356
|
-
const between = this.#blocksBehind(anchor.sbn, sbn, packetId) - 1;
|
|
2357
|
-
const dCloses = window2 + Math.ceil((now - anchor.atMs) / config.interleaveDepthMs);
|
|
2358
|
-
const kCloses = config.k > 0 ? Math.ceil(sent / config.k) : sent;
|
|
2359
|
-
plausible = sent <= 2147483647 && between <= sent - 1 && between <= dCloses + kCloses;
|
|
2360
2298
|
} else {
|
|
2361
2299
|
plausible = missing <= window2 + Math.ceil((now - previous.atMs) / config.interleaveDepthMs);
|
|
2362
2300
|
}
|
|
@@ -2368,48 +2306,31 @@ var FECManager = class _FECManager {
|
|
|
2368
2306
|
}
|
|
2369
2307
|
this.#highestSbn.set(streamKey, { sbn, atMs: now });
|
|
2370
2308
|
anchorHere();
|
|
2371
|
-
if (this.#isIso(packetId)) return;
|
|
2372
|
-
if (missing <= 0 || window2 === null) return;
|
|
2373
|
-
let vanished = previous.sbn;
|
|
2374
|
-
for (let i = 0; i < missing; i++) {
|
|
2375
|
-
vanished = this.#nextBlock(vanished, packetId);
|
|
2376
|
-
this.#materializeBlock(
|
|
2377
|
-
vanished,
|
|
2378
|
-
packetId,
|
|
2379
|
-
catalogTrackName,
|
|
2380
|
-
this.#blocksBehind(vanished, sbn, packetId) <= window2
|
|
2381
|
-
);
|
|
2382
|
-
}
|
|
2383
2309
|
}
|
|
2384
2310
|
/*
|
|
2385
|
-
* Block order for one stream (BLO-35597 iso:5).
|
|
2386
|
-
* number (ISO/IEC 23008-1 C.5.2), so
|
|
2387
|
-
*
|
|
2388
|
-
*
|
|
2389
|
-
* the order across the wrap changes. A block outside K-stride geometry has
|
|
2390
|
-
* no SS_ID position, so it keeps the SBN order it always had.
|
|
2311
|
+
* Block order for one stream (BLO-35597 iso:5). A block is keyed by its
|
|
2312
|
+
* SS_Start and SS_ID is a 32-bit serial number (ISO/IEC 23008-1 C.5.2), so
|
|
2313
|
+
* blocks are ordered by RFC 1982 comparison of their starts. Only configured
|
|
2314
|
+
* streams hold blocks: every entry point refuses an unconfigured packetId.
|
|
2391
2315
|
*/
|
|
2392
|
-
#blockPrecedes(a, b
|
|
2393
|
-
|
|
2394
|
-
const k = this.kForPacket(packetId);
|
|
2395
|
-
return isKStrideBlock(a, k) && isKStrideBlock(b, k) ? kStrideBlockPrecedes(a, b, k) : a < b;
|
|
2316
|
+
#blockPrecedes(a, b) {
|
|
2317
|
+
return serial32Precedes(a, b);
|
|
2396
2318
|
}
|
|
2397
|
-
/**
|
|
2319
|
+
/**
|
|
2320
|
+
* Blocks `a` lies behind `b` (0 when it does not precede it). Blocks vary
|
|
2321
|
+
* in length (K' = SSB_length <= K), so only a lower bound is known: the one
|
|
2322
|
+
* metric the decoder and the worker retire by too.
|
|
2323
|
+
*/
|
|
2398
2324
|
#blocksBehind(a, b, packetId) {
|
|
2399
|
-
|
|
2400
|
-
const k = this.kForPacket(packetId);
|
|
2401
|
-
return isKStrideBlock(a, k) && isKStrideBlock(b, k) ? kStrideBlocksBehind(a, b, k) : Math.max(0, b - a);
|
|
2402
|
-
}
|
|
2403
|
-
/** The block after `sbn`, stepping to block 0 where SS_ID wraps. */
|
|
2404
|
-
#nextBlock(sbn, packetId) {
|
|
2405
|
-
const k = this.kForPacket(packetId);
|
|
2406
|
-
return isKStrideBlock(sbn, k) ? kStrideNextBlock(sbn, k) : sbn + 1;
|
|
2325
|
+
return isoBlocksBehindLowerBound(a, b, this.#trackConfig(packetId).k);
|
|
2407
2326
|
}
|
|
2408
2327
|
#materializeBlock(sbn, packetId, catalogTrackName, repairable) {
|
|
2409
2328
|
const key = this.#blockKey(sbn, packetId, catalogTrackName);
|
|
2410
2329
|
if (this.#blockResolutionFor(key) !== "untracked") return;
|
|
2411
2330
|
this.#materializedBlocks.add(key);
|
|
2412
|
-
this.#
|
|
2331
|
+
if (this.#hasCatalogTimingForPacket(packetId)) {
|
|
2332
|
+
this.#classifierFor(packetId, catalogTrackName).touch(sbn);
|
|
2333
|
+
}
|
|
2413
2334
|
this.onBlockVanished?.(sbn, packetId, catalogTrackName);
|
|
2414
2335
|
if (repairable) {
|
|
2415
2336
|
this.noteBlockActivity(sbn, packetId, catalogTrackName, true);
|
|
@@ -2448,21 +2369,39 @@ var FECManager = class _FECManager {
|
|
|
2448
2369
|
});
|
|
2449
2370
|
if (this.#lifecycleTrace.length > 64) this.#lifecycleTrace.splice(0, 32);
|
|
2450
2371
|
}
|
|
2451
|
-
|
|
2452
|
-
|
|
2453
|
-
|
|
2454
|
-
|
|
2455
|
-
|
|
2456
|
-
|
|
2457
|
-
|
|
2458
|
-
|
|
2372
|
+
/**
|
|
2373
|
+
* A configured packetId's track config. There is no manager-wide default
|
|
2374
|
+
* (BLO-45953): an unconfigured packetId does no FEC work, so every caller
|
|
2375
|
+
* here is reached only behind a configuration gate and this throw marks a
|
|
2376
|
+
* broken invariant, never a packet-path outcome.
|
|
2377
|
+
*/
|
|
2378
|
+
#trackConfig(packetId) {
|
|
2379
|
+
const config = this.#trackConfigs.get(packetId);
|
|
2380
|
+
if (config === void 0) {
|
|
2381
|
+
throw new Error(
|
|
2382
|
+
`[FECManager] packetId=${packetId} was never configured; configure the track from its catalog (configureTrack/setFecWorker/setFecDecoder) first`
|
|
2383
|
+
);
|
|
2384
|
+
}
|
|
2385
|
+
return config;
|
|
2459
2386
|
}
|
|
2460
|
-
|
|
2461
|
-
|
|
2387
|
+
/**
|
|
2388
|
+
* Whether this track has the catalog's elapsed-time FEC policy. A zero span
|
|
2389
|
+
* is a deliberate no-deadline signal; it still leaves the wire geometry
|
|
2390
|
+
* usable for in-band decode, but it cannot drive classification, repair
|
|
2391
|
+
* fetches or layer adaptation.
|
|
2392
|
+
*/
|
|
2393
|
+
static #hasCatalogTiming(config) {
|
|
2394
|
+
return Number.isSafeInteger(config.interleaveDepthMs) && config.interleaveDepthMs >= MIN_INTERLEAVE_DEPTH_MS;
|
|
2395
|
+
}
|
|
2396
|
+
#hasCatalogTimingForPacket(packetId) {
|
|
2397
|
+
const config = this.#trackConfigs.get(packetId);
|
|
2398
|
+
return config !== void 0 && _FECManager.#hasCatalogTiming(config);
|
|
2462
2399
|
}
|
|
2463
2400
|
#computeTimeout(config) {
|
|
2464
|
-
if (config.timeoutMs && config.timeoutMs > 0)
|
|
2465
|
-
|
|
2401
|
+
if (typeof config.timeoutMs === "number" && Number.isFinite(config.timeoutMs) && config.timeoutMs > 0) {
|
|
2402
|
+
return config.timeoutMs;
|
|
2403
|
+
}
|
|
2404
|
+
if (Number.isSafeInteger(config.interleaveDepthMs) && config.interleaveDepthMs >= MIN_INTERLEAVE_DEPTH_MS) {
|
|
2466
2405
|
return Math.max(
|
|
2467
2406
|
this.#fecTimeoutMs,
|
|
2468
2407
|
computeFecDeliveryWindowMs({
|
|
@@ -2487,14 +2426,17 @@ var FECManager = class _FECManager {
|
|
|
2487
2426
|
*/
|
|
2488
2427
|
#refreshDeliveryGapWindow() {
|
|
2489
2428
|
let windowMs;
|
|
2490
|
-
const consider = (geometry) => {
|
|
2429
|
+
const consider = (geometry, packetId) => {
|
|
2430
|
+
if (!this.#hasCatalogTimingForPacket(packetId)) return;
|
|
2491
2431
|
if (geometry?.trackKind !== "video" || geometry.mode !== "classifying") return;
|
|
2492
2432
|
const { ticks, timescale } = geometry.keyframeInterval;
|
|
2493
2433
|
const ms = ticks / timescale * 1e3;
|
|
2494
2434
|
if (Number.isFinite(ms) && ms > 0 && (windowMs === void 0 || ms > windowMs)) windowMs = ms;
|
|
2495
2435
|
};
|
|
2496
|
-
for (const geometry of this.#streamGeometries
|
|
2497
|
-
|
|
2436
|
+
for (const [streamKey, geometry] of this.#streamGeometries) {
|
|
2437
|
+
consider(geometry, this.#parseStreamKey(streamKey).packetId);
|
|
2438
|
+
}
|
|
2439
|
+
for (const [packetId, config] of this.#trackConfigs) consider(config.geometry, packetId);
|
|
2498
2440
|
this.#deliveryGaps.setWindowMs(windowMs);
|
|
2499
2441
|
}
|
|
2500
2442
|
/**
|
|
@@ -2522,6 +2464,7 @@ var FECManager = class _FECManager {
|
|
|
2522
2464
|
return Math.max(config.timeoutMs, Math.ceil(config.interleaveDepthMs + gapMs));
|
|
2523
2465
|
}
|
|
2524
2466
|
#repairDeadlineMs(config, waitMs = this.#multicastWaitMs(config)) {
|
|
2467
|
+
if (!_FECManager.#hasCatalogTiming(config)) return waitMs;
|
|
2525
2468
|
const halfFailBoundMs = Math.ceil(waitMs / 2);
|
|
2526
2469
|
if (config.k > 0 && config.interleaveDepthMs >= MIN_INTERLEAVE_DEPTH_MS && config.fps !== void 0 && Number.isFinite(config.fps) && config.fps > 0) {
|
|
2527
2470
|
const mediaUnitMs = 1e3 / config.fps;
|
|
@@ -2536,10 +2479,12 @@ var FECManager = class _FECManager {
|
|
|
2536
2479
|
}
|
|
2537
2480
|
/**
|
|
2538
2481
|
* One block lifetime: the inactivity fail bound plus the D-28 repair grace,
|
|
2539
|
-
* at the widest wait the catalog allows (#maxMulticastWaitMs).
|
|
2540
|
-
*
|
|
2541
|
-
*
|
|
2542
|
-
*
|
|
2482
|
+
* at the widest wait the catalog allows (#maxMulticastWaitMs). For a track
|
|
2483
|
+
* without a catalog span this is receiver-local cleanup time only; no repair
|
|
2484
|
+
* grace is added. Everything derived from it (terminal retention, the
|
|
2485
|
+
* release gate's hold ceiling, the duplicate ledgers, the owed-cleanup
|
|
2486
|
+
* bound) is compared across time, so it must not follow the live
|
|
2487
|
+
* measurement.
|
|
2543
2488
|
*/
|
|
2544
2489
|
#blockLifetimeMs(config) {
|
|
2545
2490
|
const waitMs = this.#maxMulticastWaitMs(config);
|
|
@@ -2555,6 +2500,7 @@ var FECManager = class _FECManager {
|
|
|
2555
2500
|
return Math.min(waitMs + this.#repairGraceMs(config, waitMs), this.#blockLifetimeMs(config));
|
|
2556
2501
|
}
|
|
2557
2502
|
#repairGraceMs(config, waitMs) {
|
|
2503
|
+
if (!_FECManager.#hasCatalogTiming(config)) return 0;
|
|
2558
2504
|
return Math.max(1, waitMs - this.#repairDeadlineMs(config, waitMs));
|
|
2559
2505
|
}
|
|
2560
2506
|
#armBlockTimeout(sbn, packetId, catalogTrackName, delayMs) {
|
|
@@ -2641,51 +2587,72 @@ var FECManager = class _FECManager {
|
|
|
2641
2587
|
* stream; every other field stays per packetId.
|
|
2642
2588
|
*/
|
|
2643
2589
|
#upsertTrackConfig(packetId, config, catalogTrackName) {
|
|
2644
|
-
const
|
|
2590
|
+
const previousTrack = this.#trackConfigs.get(packetId);
|
|
2591
|
+
const k = config.k ?? previousTrack?.k;
|
|
2592
|
+
if (k === void 0 || !Number.isSafeInteger(k) || k <= 0) {
|
|
2593
|
+
throw new Error(
|
|
2594
|
+
`[FECManager] packetId=${packetId}: fec.k=${String(k)} must be a positive integer; configure the track from its catalog`
|
|
2595
|
+
);
|
|
2596
|
+
}
|
|
2597
|
+
const previous = previousTrack ?? {};
|
|
2645
2598
|
const streamScopedGeometry = catalogTrackName !== void 0 && config.geometry !== void 0 ? config.geometry : void 0;
|
|
2646
2599
|
if (streamScopedGeometry !== void 0) {
|
|
2647
2600
|
const { geometry: _streamGeometry, ...packetLevel } = config;
|
|
2648
2601
|
config = packetLevel;
|
|
2649
2602
|
}
|
|
2650
2603
|
const timingChanged = config.k !== void 0 || config.interleaveDepthMs !== void 0 || config.fps !== void 0;
|
|
2604
|
+
if (config.timeoutMs !== void 0 && (!Number.isFinite(config.timeoutMs) || config.timeoutMs < 0)) {
|
|
2605
|
+
throw new Error(
|
|
2606
|
+
`[FECManager] packetId=${packetId}: timeoutMs=${config.timeoutMs} must be a finite non-negative cleanup budget`
|
|
2607
|
+
);
|
|
2608
|
+
}
|
|
2651
2609
|
if (config.blockProfile !== void 0) {
|
|
2652
2610
|
parseFecBlockProfile(config.blockProfile, `packetId=${packetId} fec.blockProfile`);
|
|
2653
2611
|
}
|
|
2654
2612
|
const blockProfile = config.blockProfile ?? previous.blockProfile;
|
|
2655
2613
|
if (blockProfile === void 0) {
|
|
2656
2614
|
throw new Error(
|
|
2657
|
-
`[FECManager] packetId=${packetId}: fec.blockProfile is required ("
|
|
2615
|
+
`[FECManager] packetId=${packetId}: fec.blockProfile is required ("iso-ssbg1-v1"); configure the track from its catalog`
|
|
2658
2616
|
);
|
|
2659
2617
|
}
|
|
2660
|
-
|
|
2661
|
-
|
|
2662
|
-
|
|
2663
|
-
|
|
2664
|
-
|
|
2665
|
-
);
|
|
2666
|
-
}
|
|
2618
|
+
const ownInterleaveDepthMs = config.interleaveDepthMs ?? previousTrack?.interleaveDepthMs ?? 0;
|
|
2619
|
+
if (!Number.isSafeInteger(ownInterleaveDepthMs) || ownInterleaveDepthMs !== 0 && ownInterleaveDepthMs < MIN_INTERLEAVE_DEPTH_MS) {
|
|
2620
|
+
throw new Error(
|
|
2621
|
+
`[FECManager] packetId=${packetId}: fec.interleaveDepthMs must be absent, 0, or a finite value >= ${MIN_INTERLEAVE_DEPTH_MS} ms (got ${String(ownInterleaveDepthMs)})`
|
|
2622
|
+
);
|
|
2667
2623
|
}
|
|
2624
|
+
const hasCatalogTiming = _FECManager.#hasCatalogTiming({ interleaveDepthMs: ownInterleaveDepthMs });
|
|
2668
2625
|
const next = {
|
|
2669
2626
|
blockProfile,
|
|
2670
|
-
k
|
|
2671
|
-
symbolSize: config.symbolSize != null && config.symbolSize > 0 ? config.symbolSize :
|
|
2672
|
-
interleaveDepthMs:
|
|
2673
|
-
fps: config.fps ?? previous.fps,
|
|
2674
|
-
timeoutMs: config.timeoutMs ?? (timingChanged ? 0 : previous.timeoutMs),
|
|
2627
|
+
k,
|
|
2628
|
+
symbolSize: config.symbolSize != null && config.symbolSize > 0 ? config.symbolSize : previousTrack?.symbolSize ?? 0,
|
|
2629
|
+
interleaveDepthMs: ownInterleaveDepthMs,
|
|
2630
|
+
fps: config.fps ?? previous.fps ?? this.#fecFps,
|
|
2631
|
+
timeoutMs: config.timeoutMs ?? (timingChanged ? 0 : previous.timeoutMs ?? 0),
|
|
2675
2632
|
...(config.trackKind ?? previous.trackKind) === void 0 ? {} : { trackKind: config.trackKind ?? previous.trackKind },
|
|
2676
2633
|
...(config.codec ?? previous.codec) === void 0 ? {} : { codec: config.codec ?? previous.codec },
|
|
2677
2634
|
...(config.geometry ?? previous.geometry) === void 0 ? {} : { geometry: config.geometry ?? previous.geometry }
|
|
2678
2635
|
};
|
|
2679
|
-
|
|
2636
|
+
if (!hasCatalogTiming) {
|
|
2637
|
+
next.timeoutMs = config.timeoutMs !== void 0 && config.timeoutMs > 0 ? config.timeoutMs : previousTrack !== void 0 && previousTrack.interleaveDepthMs === 0 ? previousTrack.timeoutMs : LOCAL_FEC_TIMEOUT_MS;
|
|
2638
|
+
} else {
|
|
2639
|
+
next.timeoutMs = this.#computeTimeout(next);
|
|
2640
|
+
}
|
|
2680
2641
|
if (this.#multicastSymbolDelivery === "ordered-relay") {
|
|
2681
2642
|
this.#orderedHorizonBlocksFor(packetId, next);
|
|
2682
2643
|
}
|
|
2683
|
-
|
|
2644
|
+
const timingPolicyChanged = _FECManager.#hasCatalogTiming({ interleaveDepthMs: previous.interleaveDepthMs ?? 0 }) !== hasCatalogTiming;
|
|
2645
|
+
if (timingPolicyChanged && !hasCatalogTiming) {
|
|
2646
|
+
for (const key of [...this.#repairDeadlines.keys()]) {
|
|
2647
|
+
if (this.#belongsToPacket(key, packetId)) this.#clearRepairDeadline(key);
|
|
2648
|
+
}
|
|
2649
|
+
}
|
|
2650
|
+
if (previous.blockProfile !== void 0 && previous.k !== next.k) {
|
|
2684
2651
|
this.#dropIsoLocators(packetId);
|
|
2685
2652
|
}
|
|
2686
2653
|
this.#trackConfigs.set(packetId, next);
|
|
2687
2654
|
this.#refreshDeliveryGapWindow();
|
|
2688
|
-
let policyChanged = config.geometry !== void 0 && JSON.stringify(config.geometry) !== JSON.stringify(previous.geometry) || config.trackKind !== void 0 && config.trackKind !== previous.trackKind;
|
|
2655
|
+
let policyChanged = timingPolicyChanged || config.geometry !== void 0 && JSON.stringify(config.geometry) !== JSON.stringify(previous.geometry) || config.trackKind !== void 0 && config.trackKind !== previous.trackKind;
|
|
2689
2656
|
if (streamScopedGeometry !== void 0) {
|
|
2690
2657
|
const streamKey = this.#streamKey(packetId, catalogTrackName);
|
|
2691
2658
|
if (JSON.stringify(streamScopedGeometry) !== JSON.stringify(this.#streamGeometries.get(streamKey))) {
|
|
@@ -2700,20 +2667,14 @@ var FECManager = class _FECManager {
|
|
|
2700
2667
|
if (!this.#belongsToPacket(key, packetId)) continue;
|
|
2701
2668
|
const stream = this.#parseStreamKey(key);
|
|
2702
2669
|
const geometry = this.#geometryFor(stream.packetId, stream.catalogTrackName);
|
|
2703
|
-
if (JSON.stringify(geometry) === JSON.stringify(classifier.geometry)) continue;
|
|
2670
|
+
if (hasCatalogTiming && JSON.stringify(geometry) === JSON.stringify(classifier.geometry)) continue;
|
|
2704
2671
|
this.#classifiers.delete(key);
|
|
2672
|
+
if (!hasCatalogTiming) continue;
|
|
2705
2673
|
this.#applyClassifierChanges(stream.packetId, stream.catalogTrackName, classifier.retire());
|
|
2706
2674
|
}
|
|
2707
2675
|
}
|
|
2708
2676
|
return next;
|
|
2709
2677
|
}
|
|
2710
|
-
#normalizeSymbolData(packetId, data) {
|
|
2711
|
-
const symbolSize = this.symbolSizeForPacket(packetId);
|
|
2712
|
-
if (symbolSize <= 0 || data.byteLength === symbolSize) return data;
|
|
2713
|
-
const normalized = new Uint8Array(symbolSize);
|
|
2714
|
-
normalized.set(data.subarray(0, symbolSize));
|
|
2715
|
-
return normalized;
|
|
2716
|
-
}
|
|
2717
2678
|
#blockKeysFor(sbn, packetId, catalogTrackName) {
|
|
2718
2679
|
if (packetId != null && catalogTrackName !== void 0) {
|
|
2719
2680
|
return [this.#blockKey(sbn, packetId, catalogTrackName)];
|
|
@@ -2774,7 +2735,7 @@ var FECManager = class _FECManager {
|
|
|
2774
2735
|
...stats,
|
|
2775
2736
|
packetId,
|
|
2776
2737
|
blocksComplete: trackTotal,
|
|
2777
|
-
k: this.#
|
|
2738
|
+
k: this.#trackConfigs.get(packetId)?.k,
|
|
2778
2739
|
recoveryRate: trackAffected > 0 ? stats.blocksRecovered / trackAffected * 100 : 100,
|
|
2779
2740
|
packetLossRate: trackTotal > 0 ? trackAffected / trackTotal * 100 : 0
|
|
2780
2741
|
};
|
|
@@ -2786,7 +2747,7 @@ var FECManager = class _FECManager {
|
|
|
2786
2747
|
(block) => block.multicastSymbols > 0 && block.moqSymbols > 0
|
|
2787
2748
|
).length,
|
|
2788
2749
|
recentBlockPathCounts: Array.from(this.#blockPathCounts.values()).slice(-24),
|
|
2789
|
-
k: this.#
|
|
2750
|
+
k: this.#streamK,
|
|
2790
2751
|
// True recovery rate: of blocks that HAD loss, how many were recovered?
|
|
2791
2752
|
recoveryRate: affected > 0 ? s.blocksRecovered / affected * 100 : 100,
|
|
2792
2753
|
packetLossRate: total > 0 ? affected / total * 100 : 0,
|
|
@@ -2799,7 +2760,6 @@ var FECManager = class _FECManager {
|
|
|
2799
2760
|
},
|
|
2800
2761
|
blocks: stats.blocks.map((block) => ({ ...block })),
|
|
2801
2762
|
decoder: stats.decoder ? { ...stats.decoder } : void 0,
|
|
2802
|
-
altaFailedDetails: stats.altaFailedDetails?.map((detail) => ({ ...detail })),
|
|
2803
2763
|
packetId
|
|
2804
2764
|
})),
|
|
2805
2765
|
repairSymbolSizeMismatches: this.#repairSymbolSizeMismatchCount,
|
|
@@ -3081,15 +3041,20 @@ var FECManager = class _FECManager {
|
|
|
3081
3041
|
}
|
|
3082
3042
|
}
|
|
3083
3043
|
}
|
|
3084
|
-
/**
|
|
3044
|
+
/**
|
|
3045
|
+
* Record a transport symbol and reject later duplicates of the same
|
|
3046
|
+
* packetId/SBN/ESI. `sbn` is the block's SS_Start. A packetId with no
|
|
3047
|
+
* configured block profile has no block geometry: its symbol is refused
|
|
3048
|
+
* (false) and no block state is created for it.
|
|
3049
|
+
*/
|
|
3085
3050
|
feedSymbolFromSource(packetId, sbn, esi, _data, source, catalogTrackName) {
|
|
3051
|
+
if (!this.#hasBlockProfile(packetId)) return false;
|
|
3086
3052
|
const blockKey = this.#blockKey(sbn, packetId, catalogTrackName);
|
|
3087
3053
|
const streamKey = this.#streamKey(packetId, catalogTrackName);
|
|
3088
3054
|
if (source === "multicast" && this.#multicastSymbolDelivery === "ordered-relay") {
|
|
3089
3055
|
this.#advanceOrderedFrontier(streamKey, sbn, packetId, catalogTrackName);
|
|
3090
3056
|
}
|
|
3091
|
-
const
|
|
3092
|
-
const sourceSymbols = this.#isIso(packetId) ? this.#isoLocatorFor(packetId, catalogTrackName).blockAt(sbn)?.length ?? k : k;
|
|
3057
|
+
const sourceSymbols = this.#isoLocatorFor(packetId, catalogTrackName).blockAt(sbn)?.length ?? this.#trackConfig(packetId).k;
|
|
3093
3058
|
const isSourceSymbol = esi >= 0 && esi < sourceSymbols;
|
|
3094
3059
|
const isStartupPartial = this.#startupPartialBlocks.has(blockKey);
|
|
3095
3060
|
const isReadyMulticastRepair = this.#startupRequireRepairReady && source === "multicast" && !isSourceSymbol && this.#startupRepairReadyPackets.has(streamKey);
|
|
@@ -3207,14 +3172,14 @@ var FECManager = class _FECManager {
|
|
|
3207
3172
|
for (const [key] of this.#activeBlocks) {
|
|
3208
3173
|
const { packetId, sbn } = this.#parseBlockKey(key);
|
|
3209
3174
|
const count = this.#blockSourceCounts.get(key) ?? 0;
|
|
3210
|
-
const k = this.#blockAuthoredLengths.get(key) ?? this
|
|
3175
|
+
const k = this.#blockAuthoredLengths.get(key) ?? this.#trackConfig(packetId).k;
|
|
3211
3176
|
blocks.push({ sbn, packetId, state: "collecting", sourceCount: count, k, progress: k > 0 ? Math.min(count / k, 1) : 0 });
|
|
3212
3177
|
}
|
|
3213
3178
|
const completed = [...this.#completedBlocks].slice(-16);
|
|
3214
3179
|
for (const key of completed) {
|
|
3215
3180
|
const { packetId, sbn } = this.#parseBlockKey(key);
|
|
3216
3181
|
if (!this.#activeBlocks.has(key)) {
|
|
3217
|
-
const k = this
|
|
3182
|
+
const k = this.#trackConfig(packetId).k;
|
|
3218
3183
|
blocks.push({ sbn, packetId, state: "complete", sourceCount: k, k, progress: 1 });
|
|
3219
3184
|
}
|
|
3220
3185
|
}
|
|
@@ -3225,25 +3190,23 @@ var FECManager = class _FECManager {
|
|
|
3225
3190
|
get wasmDecoder() {
|
|
3226
3191
|
return this.#fecDecoders.values().next().value?.client ?? null;
|
|
3227
3192
|
}
|
|
3228
|
-
|
|
3229
|
-
return this.#fecK;
|
|
3230
|
-
}
|
|
3193
|
+
/** A configured packetId's K; undefined for an unconfigured one (no FEC work). */
|
|
3231
3194
|
kForPacket(packetId) {
|
|
3232
|
-
return this.#
|
|
3195
|
+
return this.#trackConfigs.get(packetId)?.k;
|
|
3233
3196
|
}
|
|
3234
3197
|
/** FEC symbol size in bytes. */
|
|
3235
3198
|
get symbolSize() {
|
|
3236
3199
|
return this.#fecSymbolSize;
|
|
3237
3200
|
}
|
|
3238
3201
|
symbolSizeForPacket(packetId) {
|
|
3239
|
-
return this.#
|
|
3202
|
+
return this.#trackConfigs.get(packetId)?.symbolSize;
|
|
3240
3203
|
}
|
|
3241
3204
|
/** FEC block span in milliseconds (0 = no interleaving). */
|
|
3242
3205
|
get interleaveDepthMs() {
|
|
3243
3206
|
return this.#fecInterleaveDepthMs;
|
|
3244
3207
|
}
|
|
3245
3208
|
interleaveDepthMsForPacket(packetId) {
|
|
3246
|
-
return this.#
|
|
3209
|
+
return this.#trackConfigs.get(packetId)?.interleaveDepthMs;
|
|
3247
3210
|
}
|
|
3248
3211
|
/**
|
|
3249
3212
|
* Record one multicast MMTP packet arrival (every packet of the leg, FEC
|
|
@@ -3261,7 +3224,8 @@ var FECManager = class _FECManager {
|
|
|
3261
3224
|
* plus the larger of the catalog tolerance and the measured delivery gap.
|
|
3262
3225
|
*/
|
|
3263
3226
|
timeoutMsForPacket(packetId) {
|
|
3264
|
-
|
|
3227
|
+
const config = this.#trackConfigs.get(packetId);
|
|
3228
|
+
return config === void 0 ? void 0 : this.#multicastWaitMs(config);
|
|
3265
3229
|
}
|
|
3266
3230
|
/**
|
|
3267
3231
|
* Largest multicast delivery gap measured over the current window (ms), and
|
|
@@ -3317,27 +3281,31 @@ var FECManager = class _FECManager {
|
|
|
3317
3281
|
}
|
|
3318
3282
|
/** Resolved per-track D-28 repair request deadline in milliseconds. */
|
|
3319
3283
|
repairDeadlineMsForPacket(packetId) {
|
|
3320
|
-
|
|
3284
|
+
const config = this.#trackConfigs.get(packetId);
|
|
3285
|
+
return config === void 0 ? void 0 : this.#repairDeadlineMs(config);
|
|
3321
3286
|
}
|
|
3322
3287
|
/**
|
|
3323
3288
|
* One block lifetime: the longest a block stays open after its last symbol
|
|
3324
3289
|
* (inactivity fail bound plus D-28 repair grace).
|
|
3325
3290
|
*/
|
|
3326
3291
|
blockLifetimeMsForPacket(packetId) {
|
|
3327
|
-
|
|
3292
|
+
const config = this.#trackConfigs.get(packetId);
|
|
3293
|
+
return config === void 0 ? void 0 : this.#blockLifetimeMs(config);
|
|
3328
3294
|
}
|
|
3329
3295
|
/**
|
|
3330
3296
|
* How long a terminal (clean, recovered or failed) block stays authoritative,
|
|
3331
3297
|
* for late symbols and for the in-order release gate. It is also the gate's
|
|
3332
3298
|
* hold ceiling (MmtpRouter's holdLimitMs): a retired block reads back as
|
|
3333
3299
|
* untracked, which the gate counts as lost, so no frame may still be waiting
|
|
3334
|
-
* on a block that can retire.
|
|
3335
|
-
*
|
|
3336
|
-
*
|
|
3337
|
-
*
|
|
3300
|
+
* on a block that can retire. With a positive catalog span the window is one
|
|
3301
|
+
* interleave depth for a gap's last block to finish arriving, one block
|
|
3302
|
+
* lifetime to fail it, and one more for an owed Worker cleanup. With an
|
|
3303
|
+
* absent/zero span it is a receiver-local cleanup bound only; it creates no
|
|
3304
|
+
* catalog FEC deadline.
|
|
3338
3305
|
*/
|
|
3339
3306
|
terminalRetentionMsForPacket(packetId) {
|
|
3340
|
-
const config = this.#
|
|
3307
|
+
const config = this.#trackConfigs.get(packetId);
|
|
3308
|
+
if (config === void 0) return void 0;
|
|
3341
3309
|
return config.interleaveDepthMs + 2 * this.#blockLifetimeMs(config);
|
|
3342
3310
|
}
|
|
3343
3311
|
/** Remaining fetch budget for a specific repair-requested block. */
|
|
@@ -3348,7 +3316,8 @@ var FECManager = class _FECManager {
|
|
|
3348
3316
|
if (deadlineMs !== void 0) {
|
|
3349
3317
|
return Math.max(1, Math.ceil(deadlineMs - performance.now()));
|
|
3350
3318
|
}
|
|
3351
|
-
const trackConfig = this.#
|
|
3319
|
+
const trackConfig = this.#trackConfigs.get(packetId);
|
|
3320
|
+
if (trackConfig === void 0) return void 0;
|
|
3352
3321
|
return Math.max(1, trackConfig.timeoutMs - this.#repairDeadlineMs(trackConfig));
|
|
3353
3322
|
}
|
|
3354
3323
|
/** True once configureTrack/setFecDecoder/setFecWorker configured this packetId. */
|
|
@@ -3359,14 +3328,19 @@ var FECManager = class _FECManager {
|
|
|
3359
3328
|
blockProfileForPacket(packetId) {
|
|
3360
3329
|
return this.#trackConfigs.get(packetId)?.blockProfile;
|
|
3361
3330
|
}
|
|
3362
|
-
|
|
3363
|
-
|
|
3331
|
+
/**
|
|
3332
|
+
* Whether a packetId has a configured block geometry. iso-ssbg1-v1 is the
|
|
3333
|
+
* only profile, so every configured track has it; a packetId never
|
|
3334
|
+
* configured (or released) has none and gets no FEC block tracking.
|
|
3335
|
+
*/
|
|
3336
|
+
#hasBlockProfile(packetId) {
|
|
3337
|
+
return this.#trackConfigs.get(packetId)?.blockProfile !== void 0;
|
|
3364
3338
|
}
|
|
3365
3339
|
#isoLocatorFor(packetId, catalogTrackName) {
|
|
3366
3340
|
const streamKey = this.#streamKey(packetId, catalogTrackName);
|
|
3367
3341
|
let locator = this.#isoLocators.get(streamKey);
|
|
3368
3342
|
if (!locator) {
|
|
3369
|
-
locator = new IsoBlockLocator(this
|
|
3343
|
+
locator = new IsoBlockLocator(this.#trackConfig(packetId).k, this.#isoRetentionBlocks(packetId));
|
|
3370
3344
|
this.#isoLocators.set(streamKey, locator);
|
|
3371
3345
|
}
|
|
3372
3346
|
return locator;
|
|
@@ -3397,12 +3371,14 @@ var FECManager = class _FECManager {
|
|
|
3397
3371
|
*/
|
|
3398
3372
|
#isoRetentionBlocks(packetId) {
|
|
3399
3373
|
const config = this.#trackConfigs.get(packetId);
|
|
3400
|
-
if (config?.blockProfile
|
|
3374
|
+
if (config?.blockProfile === void 0) {
|
|
3401
3375
|
throw new Error(
|
|
3402
|
-
`[FECManager] packetId=${packetId} has no iso-ssbg1-v1 block
|
|
3376
|
+
`[FECManager] packetId=${packetId} has no configured iso-ssbg1-v1 block profile`
|
|
3403
3377
|
);
|
|
3404
3378
|
}
|
|
3405
|
-
|
|
3379
|
+
if (!_FECManager.#hasCatalogTiming(config)) return LOCAL_FEC_RETENTION_BLOCKS;
|
|
3380
|
+
const terminalRetentionMs = config.interleaveDepthMs + 2 * this.#blockLifetimeMs(config);
|
|
3381
|
+
const blocks = Math.ceil(terminalRetentionMs / config.interleaveDepthMs) + 1;
|
|
3406
3382
|
if (!Number.isSafeInteger(blocks) || blocks <= 0) {
|
|
3407
3383
|
throw new Error(`[FECManager] packetId=${packetId} iso-ssbg1-v1 retention of ${blocks} blocks is not derivable`);
|
|
3408
3384
|
}
|
|
@@ -3410,7 +3386,8 @@ var FECManager = class _FECManager {
|
|
|
3410
3386
|
}
|
|
3411
3387
|
/** Whether an iso-ssbg1-v1 protected packet fits one ssbg_mode1 symbol (len + 2 <= T). */
|
|
3412
3388
|
isoSourceFits(packetId, packetLength) {
|
|
3413
|
-
|
|
3389
|
+
const symbolSize = this.#trackConfigs.get(packetId)?.symbolSize;
|
|
3390
|
+
return symbolSize !== void 0 && ssbg1FitsSymbol(packetLength, symbolSize);
|
|
3414
3391
|
}
|
|
3415
3392
|
/**
|
|
3416
3393
|
* Locate an iso-ssbg1-v1 source by range membership in a block a repair
|
|
@@ -3471,11 +3448,11 @@ var FECManager = class _FECManager {
|
|
|
3471
3448
|
* that could hold them is pending or not yet stated by a repair, else the
|
|
3472
3449
|
* starts of the blocks that failed or vanished.
|
|
3473
3450
|
*
|
|
3474
|
-
* `untracked` reads
|
|
3475
|
-
*
|
|
3476
|
-
*
|
|
3477
|
-
*
|
|
3478
|
-
* an SS_Start
|
|
3451
|
+
* `untracked` reads as the release gate reads it (MmtpRouter
|
|
3452
|
+
* #createFecReleaseGate): a block holding either released frame's SS_ID
|
|
3453
|
+
* (`fromSsId` or `toSsId`) delivered that frame, so untracked there means it
|
|
3454
|
+
* resolved and retired; a block starting after the stream's frontier
|
|
3455
|
+
* (`highestSbn`, an SS_Start) may still be heard from; a block at or behind the
|
|
3479
3456
|
* frontier that no symbol of this manager ever reached vanished, and its
|
|
3480
3457
|
* media is lost.
|
|
3481
3458
|
*/
|
|
@@ -3543,6 +3520,7 @@ var FECManager = class _FECManager {
|
|
|
3543
3520
|
}
|
|
3544
3521
|
/** Current classifier mode of a stream. */
|
|
3545
3522
|
classifierModeFor(packetId, catalogTrackName) {
|
|
3523
|
+
if (!this.#hasCatalogTimingForPacket(packetId)) return { mode: "all-protected", reason: null };
|
|
3546
3524
|
const classifier = this.#classifierFor(packetId, catalogTrackName);
|
|
3547
3525
|
return { mode: classifier.mode, reason: classifier.reason };
|
|
3548
3526
|
}
|
|
@@ -3552,10 +3530,12 @@ var FECManager = class _FECManager {
|
|
|
3552
3530
|
* it does not. A stream with no classifier yet has shown nothing against it.
|
|
3553
3531
|
*/
|
|
3554
3532
|
mpuBoundsRapsFor(packetId, catalogTrackName) {
|
|
3533
|
+
if (!this.#hasCatalogTimingForPacket(packetId)) return false;
|
|
3555
3534
|
return this.#classifiers.get(this.#streamKey(packetId, catalogTrackName))?.mpuBoundsRaps ?? true;
|
|
3556
3535
|
}
|
|
3557
3536
|
/** A block's class now, or frozen at its resolution. */
|
|
3558
3537
|
blockClassFor(sbn, packetId, catalogTrackName) {
|
|
3538
|
+
if (!this.#hasCatalogTimingForPacket(packetId)) return "unknown";
|
|
3559
3539
|
const frozen = this.#resolvedBlockClasses.get(this.#blockKey(sbn, packetId, catalogTrackName));
|
|
3560
3540
|
if (frozen !== void 0) return frozen;
|
|
3561
3541
|
return this.#peekClass(sbn, packetId, catalogTrackName);
|
|
@@ -3577,7 +3557,7 @@ var FECManager = class _FECManager {
|
|
|
3577
3557
|
* invariant (consecutive MPUs exactly one keyframe interval apart).
|
|
3578
3558
|
*/
|
|
3579
3559
|
noteReleasedRap(packetId, catalogTrackName, mpu, presentationTime, mediaTimescale) {
|
|
3580
|
-
if (!this.#trackConfigs.has(packetId)) return;
|
|
3560
|
+
if (!this.#trackConfigs.has(packetId) || !this.#hasCatalogTimingForPacket(packetId)) return;
|
|
3581
3561
|
this.#applyClassifierChanges(
|
|
3582
3562
|
packetId,
|
|
3583
3563
|
catalogTrackName,
|
|
@@ -3593,27 +3573,34 @@ var FECManager = class _FECManager {
|
|
|
3593
3573
|
// -----------------------------------------------------------------------
|
|
3594
3574
|
/** Configure FEC parameters. */
|
|
3595
3575
|
configure(config) {
|
|
3596
|
-
if (config.
|
|
3576
|
+
if (config.interleaveDepthMs != null && (!Number.isSafeInteger(config.interleaveDepthMs) || config.interleaveDepthMs < 0)) {
|
|
3577
|
+
throw new Error(
|
|
3578
|
+
`[FECManager] interleaveDepthMs=${config.interleaveDepthMs} must be a non-negative safe integer in milliseconds`
|
|
3579
|
+
);
|
|
3580
|
+
}
|
|
3581
|
+
if (config.timeoutMs != null && (!Number.isFinite(config.timeoutMs) || config.timeoutMs < 0)) {
|
|
3582
|
+
throw new Error(
|
|
3583
|
+
`[FECManager] timeoutMs=${config.timeoutMs} must be a finite non-negative cleanup budget`
|
|
3584
|
+
);
|
|
3585
|
+
}
|
|
3586
|
+
if (config.k != null && (!Number.isSafeInteger(config.k) || config.k <= 0)) {
|
|
3587
|
+
throw new Error(`[FECManager] k=${config.k} must be a positive integer`);
|
|
3588
|
+
}
|
|
3589
|
+
if (config.k != null) this.#streamK = config.k;
|
|
3597
3590
|
if (config.symbolSize != null && config.symbolSize > 0) this.#fecSymbolSize = config.symbolSize;
|
|
3598
3591
|
if (config.interleaveDepthMs != null) this.#fecInterleaveDepthMs = config.interleaveDepthMs;
|
|
3599
3592
|
if (config.fps != null && Number.isFinite(config.fps) && config.fps > 0) {
|
|
3600
3593
|
this.#fecFps = config.fps;
|
|
3601
3594
|
}
|
|
3602
|
-
if (config.interleaveDepthMs != null && config.interleaveDepthMs > 0 && config.interleaveDepthMs < MIN_INTERLEAVE_DEPTH_MS) {
|
|
3603
|
-
console.warn(`[FECManager] interleaveDepthMs=${config.interleaveDepthMs} looks like frame count, not milliseconds (\xA75.1 requires ms). Expected range: ${MIN_INTERLEAVE_DEPTH_MS}-10000ms.`);
|
|
3604
|
-
}
|
|
3605
3595
|
if (config.timeoutMs != null && config.timeoutMs > 0) {
|
|
3606
3596
|
this.#fecTimeoutMs = config.timeoutMs;
|
|
3607
|
-
} else if (config.interleaveDepthMs
|
|
3608
|
-
this.#fecTimeoutMs =
|
|
3609
|
-
|
|
3610
|
-
|
|
3611
|
-
symbolSize: this.#fecSymbolSize,
|
|
3612
|
-
mediaUnitsPerSecond: config.fps
|
|
3613
|
-
});
|
|
3597
|
+
} else if (config.interleaveDepthMs === 0) {
|
|
3598
|
+
this.#fecTimeoutMs = LOCAL_FEC_TIMEOUT_MS;
|
|
3599
|
+
} else if (config.interleaveDepthMs != null && Number.isSafeInteger(config.interleaveDepthMs) && config.interleaveDepthMs >= MIN_INTERLEAVE_DEPTH_MS) {
|
|
3600
|
+
this.#fecTimeoutMs = fecSpanDeliveryWindowMs(config.interleaveDepthMs);
|
|
3614
3601
|
}
|
|
3615
3602
|
for (const [packetId, track] of this.#trackConfigs) {
|
|
3616
|
-
if (track.timeoutMs < this.#fecTimeoutMs) {
|
|
3603
|
+
if (_FECManager.#hasCatalogTiming(track) && track.timeoutMs < this.#fecTimeoutMs) {
|
|
3617
3604
|
this.#trackConfigs.set(packetId, { ...track, timeoutMs: this.#fecTimeoutMs });
|
|
3618
3605
|
}
|
|
3619
3606
|
}
|
|
@@ -3672,7 +3659,7 @@ var FECManager = class _FECManager {
|
|
|
3672
3659
|
);
|
|
3673
3660
|
}
|
|
3674
3661
|
if (config) this.#upsertTrackConfig(packetId, config, catalogTrackName);
|
|
3675
|
-
const workerK = this.#
|
|
3662
|
+
const workerK = this.#trackConfig(packetId).k;
|
|
3676
3663
|
this.#workerClients.set(streamKey, client);
|
|
3677
3664
|
this.#releasedWorkerClients.delete(client);
|
|
3678
3665
|
client.onFrame((data, meta) => {
|
|
@@ -3852,7 +3839,7 @@ var FECManager = class _FECManager {
|
|
|
3852
3839
|
return;
|
|
3853
3840
|
}
|
|
3854
3841
|
this.#settleOwedCleanup(key, "cleanup-ack-timeout");
|
|
3855
|
-
}, Math.ceil(this.#liveBlockLifetimeMs(this.#
|
|
3842
|
+
}, Math.ceil(this.#liveBlockLifetimeMs(this.#trackConfig(packetId))))
|
|
3856
3843
|
};
|
|
3857
3844
|
this.#pendingWorkerCleanupBounds.set(key, bound);
|
|
3858
3845
|
}
|
|
@@ -3893,16 +3880,11 @@ var FECManager = class _FECManager {
|
|
|
3893
3880
|
if (this.#fpsDetected || fps <= 0) return;
|
|
3894
3881
|
this.#fpsDetected = true;
|
|
3895
3882
|
this.#fecFps = fps;
|
|
3896
|
-
if (this.#fecInterleaveDepthMs >= MIN_INTERLEAVE_DEPTH_MS) {
|
|
3897
|
-
this.#fecTimeoutMs =
|
|
3898
|
-
interleaveDepthMs: this.#fecInterleaveDepthMs,
|
|
3899
|
-
sourceSymbols: this.#fecK,
|
|
3900
|
-
symbolSize: this.#fecSymbolSize,
|
|
3901
|
-
mediaUnitsPerSecond: fps
|
|
3902
|
-
});
|
|
3883
|
+
if (Number.isSafeInteger(this.#fecInterleaveDepthMs) && this.#fecInterleaveDepthMs >= MIN_INTERLEAVE_DEPTH_MS) {
|
|
3884
|
+
this.#fecTimeoutMs = fecSpanDeliveryWindowMs(this.#fecInterleaveDepthMs);
|
|
3903
3885
|
}
|
|
3904
3886
|
for (const [packetId, config] of this.#trackConfigs) {
|
|
3905
|
-
if (config.fps !== void 0 || config.interleaveDepthMs < MIN_INTERLEAVE_DEPTH_MS) continue;
|
|
3887
|
+
if (config.fps !== void 0 || !Number.isSafeInteger(config.interleaveDepthMs) || config.interleaveDepthMs < MIN_INTERLEAVE_DEPTH_MS) continue;
|
|
3906
3888
|
this.#trackConfigs.set(packetId, {
|
|
3907
3889
|
...config,
|
|
3908
3890
|
fps,
|
|
@@ -3931,51 +3913,33 @@ var FECManager = class _FECManager {
|
|
|
3931
3913
|
* Caller is responsible for routing recovered data and updating
|
|
3932
3914
|
* block completion state via `markBlockCompleted()`.
|
|
3933
3915
|
*/
|
|
3934
|
-
feedSourceSymbol(packetId, ssId, symbolData,
|
|
3916
|
+
feedSourceSymbol(packetId, ssId, symbolData, catalogTrackName) {
|
|
3935
3917
|
this.#fecStats.sourcePackets++;
|
|
3936
3918
|
this.#statsForPacket(packetId).sourcePackets++;
|
|
3937
|
-
|
|
3938
|
-
|
|
3939
|
-
|
|
3940
|
-
|
|
3941
|
-
|
|
3942
|
-
|
|
3943
|
-
|
|
3944
|
-
`[FECManager] iso-ssbg1-v1 source SS_ID=${ssId} (packetId=${packetId}) has no stated block; hold it until a repair places it (placeIsoRepair)`
|
|
3945
|
-
);
|
|
3946
|
-
}
|
|
3947
|
-
locator.observe(ssId);
|
|
3948
|
-
if (!ssbg1FitsSymbol(symbolData.byteLength, this.symbolSizeForPacket(packetId))) {
|
|
3949
|
-
this.#isoSourceOversizeCount++;
|
|
3950
|
-
return null;
|
|
3951
|
-
}
|
|
3952
|
-
sbn = located.block.start;
|
|
3953
|
-
fecData = symbolData;
|
|
3954
|
-
} else {
|
|
3955
|
-
fecData = this.#normalizeSymbolData(packetId, symbolData);
|
|
3956
|
-
sbn = Math.floor(ssId / this.kForPacket(packetId));
|
|
3919
|
+
if (!this.#hasBlockProfile(packetId)) return null;
|
|
3920
|
+
const locator = this.#isoLocatorFor(packetId, catalogTrackName);
|
|
3921
|
+
const located = locator.locate(ssId);
|
|
3922
|
+
if (located === null) {
|
|
3923
|
+
throw new Error(
|
|
3924
|
+
`[FECManager] iso-ssbg1-v1 source SS_ID=${ssId} (packetId=${packetId}) has no stated block; hold it until a repair places it (placeIsoRepair)`
|
|
3925
|
+
);
|
|
3957
3926
|
}
|
|
3927
|
+
locator.observe(ssId);
|
|
3928
|
+
if (!ssbg1FitsSymbol(symbolData.byteLength, this.#trackConfig(packetId).symbolSize)) {
|
|
3929
|
+
this.#isoSourceOversizeCount++;
|
|
3930
|
+
return null;
|
|
3931
|
+
}
|
|
3932
|
+
const sbn = located.block.start;
|
|
3958
3933
|
const blockKey = this.#blockKey(sbn, packetId, catalogTrackName);
|
|
3959
3934
|
const syncBlockOwner = this.#syncDecoderBlockOwners.get(blockKey);
|
|
3960
3935
|
const workerClient = this.#routableWorkerForPacket(packetId, catalogTrackName);
|
|
3961
3936
|
if (workerClient && !syncBlockOwner) {
|
|
3962
3937
|
if (!this.#claimDecoderBlock(workerClient, sbn, catalogTrackName)) return null;
|
|
3963
|
-
const buf =
|
|
3964
|
-
|
|
3965
|
-
|
|
3938
|
+
const buf = symbolData.buffer.slice(
|
|
3939
|
+
symbolData.byteOffset,
|
|
3940
|
+
symbolData.byteOffset + symbolData.byteLength
|
|
3966
3941
|
);
|
|
3967
|
-
|
|
3968
|
-
const authBuf = alta.auth.buffer.slice(
|
|
3969
|
-
alta.auth.byteOffset,
|
|
3970
|
-
alta.auth.byteOffset + alta.auth.byteLength
|
|
3971
|
-
);
|
|
3972
|
-
workerClient.feedSource(ssId, buf, performance.now(), {
|
|
3973
|
-
auth: authBuf,
|
|
3974
|
-
payloadEnd: alta.payloadEnd
|
|
3975
|
-
});
|
|
3976
|
-
} else {
|
|
3977
|
-
workerClient.feedSource(ssId, buf, performance.now());
|
|
3978
|
-
}
|
|
3942
|
+
workerClient.feedSource(ssId, buf, performance.now());
|
|
3979
3943
|
return null;
|
|
3980
3944
|
}
|
|
3981
3945
|
const entry = this.#decoderForPacket(packetId, catalogTrackName);
|
|
@@ -3985,7 +3949,7 @@ var FECManager = class _FECManager {
|
|
|
3985
3949
|
this.#syncDecoderBlockOwners.set(blockKey, client);
|
|
3986
3950
|
let recovered = null;
|
|
3987
3951
|
try {
|
|
3988
|
-
recovered = client.add_source(ssId,
|
|
3952
|
+
recovered = client.add_source(ssId, symbolData, performance.now());
|
|
3989
3953
|
} catch {
|
|
3990
3954
|
}
|
|
3991
3955
|
this.#retireIsoUnplaced(client, packetId, catalogTrackName);
|
|
@@ -4004,7 +3968,8 @@ var FECManager = class _FECManager {
|
|
|
4004
3968
|
feedRepairSymbol(packetId, ssStart, ssbLength, rsId, repairData, catalogTrackName) {
|
|
4005
3969
|
this.#fecStats.repairPackets++;
|
|
4006
3970
|
this.#statsForPacket(packetId).repairPackets++;
|
|
4007
|
-
|
|
3971
|
+
if (!this.#hasBlockProfile(packetId)) return null;
|
|
3972
|
+
const symbolSize = this.#trackConfig(packetId).symbolSize;
|
|
4008
3973
|
if (symbolSize > 0 && repairData.byteLength !== symbolSize) {
|
|
4009
3974
|
this.#repairSymbolSizeMismatchCount++;
|
|
4010
3975
|
if (this.#repairSymbolSizeMismatchCount <= 5 || this.#repairSymbolSizeMismatchCount % 100 === 0) {
|
|
@@ -4015,18 +3980,12 @@ var FECManager = class _FECManager {
|
|
|
4015
3980
|
return null;
|
|
4016
3981
|
}
|
|
4017
3982
|
const fecData = repairData;
|
|
4018
|
-
const k = this
|
|
4019
|
-
|
|
4020
|
-
if (
|
|
4021
|
-
|
|
4022
|
-
if (ssbLength <= 0 || ssbLength > k || block === void 0 || block.span || block.length !== ssbLength) {
|
|
4023
|
-
return null;
|
|
4024
|
-
}
|
|
4025
|
-
sbn = ssStart;
|
|
4026
|
-
} else {
|
|
4027
|
-
if (ssbLength <= 0 || ssbLength > k || ssStart % k !== 0) return null;
|
|
4028
|
-
sbn = Math.floor(ssStart / k);
|
|
3983
|
+
const k = this.#trackConfig(packetId).k;
|
|
3984
|
+
const block = this.#isoLocatorFor(packetId, catalogTrackName).blockAt(ssStart);
|
|
3985
|
+
if (ssbLength <= 0 || ssbLength > k || block === void 0 || block.span || block.length !== ssbLength) {
|
|
3986
|
+
return null;
|
|
4029
3987
|
}
|
|
3988
|
+
const sbn = ssStart;
|
|
4030
3989
|
this.#noteSbnSeen(sbn, packetId, catalogTrackName, "repair");
|
|
4031
3990
|
this.noteBlockActivity(sbn, packetId, catalogTrackName);
|
|
4032
3991
|
const blockKey = this.#blockKey(sbn, packetId, catalogTrackName);
|
|
@@ -4064,8 +4023,9 @@ var FECManager = class _FECManager {
|
|
|
4064
4023
|
* Record a block's authored SSB_length from a repair symbol. Returns false,
|
|
4065
4024
|
* leaving the block unchanged, when the symbol contradicts the block: a
|
|
4066
4025
|
* different SSB_length than an earlier repair symbol declared is dropped,
|
|
4067
|
-
* and an SSB_length that places an already-received source ESI
|
|
4068
|
-
*
|
|
4026
|
+
* and an SSB_length that places an already-received source ESI at or past
|
|
4027
|
+
* the block's end fails the block, since its geometry can no longer be
|
|
4028
|
+
* trusted.
|
|
4069
4029
|
*/
|
|
4070
4030
|
#noteAuthoredLength(blockKey, sbn, packetId, ssbLength, catalogTrackName) {
|
|
4071
4031
|
const known = this.#blockAuthoredLengths.get(blockKey);
|
|
@@ -4132,7 +4092,7 @@ var FECManager = class _FECManager {
|
|
|
4132
4092
|
* a sweep tied to expiry alone would never run there.
|
|
4133
4093
|
*/
|
|
4134
4094
|
#retireIsoUnplaced(decoder, packetId, catalogTrackName) {
|
|
4135
|
-
if (!this.#
|
|
4095
|
+
if (!this.#hasBlockProfile(packetId)) return;
|
|
4136
4096
|
const newest = this.#isoLocators.get(this.#streamKey(packetId, catalogTrackName))?.newest;
|
|
4137
4097
|
if (newest === void 0) return;
|
|
4138
4098
|
try {
|
|
@@ -4177,11 +4137,13 @@ var FECManager = class _FECManager {
|
|
|
4177
4137
|
this.#failBlock(sbn, packetId, catalogTrackName);
|
|
4178
4138
|
return;
|
|
4179
4139
|
}
|
|
4180
|
-
|
|
4181
|
-
|
|
4182
|
-
|
|
4183
|
-
|
|
4184
|
-
|
|
4140
|
+
if (_FECManager.#hasCatalogTiming(trackConfig)) {
|
|
4141
|
+
this.#applyClassifierChanges(
|
|
4142
|
+
packetId,
|
|
4143
|
+
catalogTrackName,
|
|
4144
|
+
this.#classifierFor(packetId, catalogTrackName).observe(sbn, evidence)
|
|
4145
|
+
);
|
|
4146
|
+
}
|
|
4185
4147
|
const count = (this.#blockSourceCounts.get(key) ?? 0) + 1;
|
|
4186
4148
|
this.#blockSourceCounts.set(key, count);
|
|
4187
4149
|
if (count === 1) this.#indexBlockKey(key, sbn, packetId);
|
|
@@ -4225,6 +4187,7 @@ var FECManager = class _FECManager {
|
|
|
4225
4187
|
}
|
|
4226
4188
|
/** Refresh deadlines after accepted block activity. */
|
|
4227
4189
|
noteBlockActivity(sbn, packetId, catalogTrackName, sourceProgress = false) {
|
|
4190
|
+
if (!this.#trackConfigs.has(packetId)) return;
|
|
4228
4191
|
const key = this.#blockKey(sbn, packetId, catalogTrackName);
|
|
4229
4192
|
if (this.#completedBlocks.has(key)) return;
|
|
4230
4193
|
if (this.#pendingWorkerCleanups.has(key)) return;
|
|
@@ -4234,13 +4197,13 @@ var FECManager = class _FECManager {
|
|
|
4234
4197
|
this.#holdForOrderedSettlement(key, sbn, packetId, catalogTrackName);
|
|
4235
4198
|
return;
|
|
4236
4199
|
}
|
|
4237
|
-
const trackConfig = this.#
|
|
4200
|
+
const trackConfig = this.#trackConfig(packetId);
|
|
4238
4201
|
const timeoutMs = this.#multicastWaitMs(trackConfig);
|
|
4239
4202
|
if (!(timeoutMs > 0)) {
|
|
4240
4203
|
throw new Error(`[FECManager] packetId=${packetId} has no positive fail bound (timeoutMs=${timeoutMs})`);
|
|
4241
4204
|
}
|
|
4242
4205
|
const repairDeadline = this.#repairDeadlines.get(key);
|
|
4243
|
-
if (this.onRepairNeeded && !repairRequested && (repairDeadline === void 0 || sourceProgress)) {
|
|
4206
|
+
if (this.#hasCatalogTimingForPacket(packetId) && this.onRepairNeeded && !repairRequested && (repairDeadline === void 0 || sourceProgress)) {
|
|
4244
4207
|
this.#armRepairDeadline(
|
|
4245
4208
|
key,
|
|
4246
4209
|
sbn,
|
|
@@ -4310,6 +4273,7 @@ var FECManager = class _FECManager {
|
|
|
4310
4273
|
}
|
|
4311
4274
|
/** Mark a block as completed externally (e.g., after routing recovered data). */
|
|
4312
4275
|
markBlockCompleted(sbn, packetId, catalogTrackName) {
|
|
4276
|
+
if (packetId != null && !this.#trackConfigs.has(packetId)) return;
|
|
4313
4277
|
for (const key of this.#blockKeysFor(sbn, packetId, catalogTrackName)) {
|
|
4314
4278
|
this.#resolveCompleted(key);
|
|
4315
4279
|
}
|
|
@@ -8526,7 +8490,6 @@ var CmafBlockAssembler = class {
|
|
|
8526
8490
|
failedBlocks: 0
|
|
8527
8491
|
};
|
|
8528
8492
|
this.#config = {
|
|
8529
|
-
k: config.k,
|
|
8530
8493
|
timescale: config.timescale ?? 1e6,
|
|
8531
8494
|
trackId: config.trackId ?? 1,
|
|
8532
8495
|
trackType: config.trackType,
|
|
@@ -10712,6 +10675,21 @@ var FecReleaseSequencer = class {
|
|
|
10712
10675
|
this.#streams.delete(key);
|
|
10713
10676
|
}
|
|
10714
10677
|
}
|
|
10678
|
+
/**
|
|
10679
|
+
* The packet's FEC ended, so no block will resolve a held gap: release its
|
|
10680
|
+
* frames in order, each gap stalled as a discontinuity, and forget its
|
|
10681
|
+
* streams, so FEC configured on it again starts from a fresh one.
|
|
10682
|
+
*/
|
|
10683
|
+
endPacket(packetId) {
|
|
10684
|
+
for (const [key, stream] of this.#streams) {
|
|
10685
|
+
if (stream.packetId !== packetId) continue;
|
|
10686
|
+
while (stream.buf.length > 0 && stream.last !== null) {
|
|
10687
|
+
stream.discontinuity = true;
|
|
10688
|
+
this.#drain(stream);
|
|
10689
|
+
}
|
|
10690
|
+
this.#streams.delete(key);
|
|
10691
|
+
}
|
|
10692
|
+
}
|
|
10715
10693
|
discardPacket(packetId) {
|
|
10716
10694
|
for (const [key, stream] of this.#streams) {
|
|
10717
10695
|
if (stream.packetId !== packetId) continue;
|
|
@@ -11094,6 +11072,13 @@ var MmtpContainerParser = class _MmtpContainerParser {
|
|
|
11094
11072
|
markFecDiscontinuity(packetId, catalogTrackName) {
|
|
11095
11073
|
this.#fecSequencer?.markDiscontinuity(this.#streamKey(packetId, catalogTrackName));
|
|
11096
11074
|
}
|
|
11075
|
+
/**
|
|
11076
|
+
* The FEC of a packet ID ended: release the frames the FEC release gate
|
|
11077
|
+
* holds for it in order, each gap stalled, and forget its gated streams.
|
|
11078
|
+
*/
|
|
11079
|
+
endFecRelease(packetId) {
|
|
11080
|
+
this.#fecSequencer?.endPacket(packetId);
|
|
11081
|
+
}
|
|
11097
11082
|
/**
|
|
11098
11083
|
* MPU and data-unit identity of one MMTP packet, parsed in place (no copy,
|
|
11099
11084
|
* no reassembly). Null for non-MPU packets and packets that do not parse.
|
|
@@ -11274,7 +11259,7 @@ var MmtpContainerParser = class _MmtpContainerParser {
|
|
|
11274
11259
|
const parsed = preParsed !== void 0 && preParsed.skipFecPayloadId === (options?.skipFecPayloadId === true) ? preParsed : this.parsePacket(packet, options);
|
|
11275
11260
|
if (!parsed) return this.#drop("header-parse-failed");
|
|
11276
11261
|
const info = parsed.info;
|
|
11277
|
-
if (info.packetType !==
|
|
11262
|
+
if (info.packetType !== PACKET_TYPE_MPU) {
|
|
11278
11263
|
return {
|
|
11279
11264
|
kind: info.isRepair ? "repair" : "non-media",
|
|
11280
11265
|
packet: info,
|
|
@@ -11283,10 +11268,8 @@ var MmtpContainerParser = class _MmtpContainerParser {
|
|
|
11283
11268
|
}
|
|
11284
11269
|
if (info.fecPayloadId !== null && this.#sourceSymbolCb) {
|
|
11285
11270
|
const rawSymbol = packet.subarray(0, packet.length - 4);
|
|
11286
|
-
const altaTrailer = extractAltaTrailer2(packet);
|
|
11287
|
-
const alta = altaTrailer ? { auth: altaTrailer.auth, payloadEnd: altaTrailer.signedEnd } : void 0;
|
|
11288
11271
|
try {
|
|
11289
|
-
this.#sourceSymbolCb(info.packetId, info.fecPayloadId.ssId, rawSymbol
|
|
11272
|
+
this.#sourceSymbolCb(info.packetId, info.fecPayloadId.ssId, rawSymbol);
|
|
11290
11273
|
} catch {
|
|
11291
11274
|
}
|
|
11292
11275
|
}
|
|
@@ -12426,6 +12409,8 @@ var MmtpRouter = class {
|
|
|
12426
12409
|
*/
|
|
12427
12410
|
#onPublisherRestart;
|
|
12428
12411
|
#publisherRestarts = 0;
|
|
12412
|
+
/** Inits delivered with no onTrackInit handler for their packetId (MOQT-01). */
|
|
12413
|
+
#unmappedInitDeliveries = 0;
|
|
12429
12414
|
// FEC Payload IDs left unread because their (owner) packetId had no
|
|
12430
12415
|
// configured fec.blockProfile (see #noteUnreadableFecPayloadId).
|
|
12431
12416
|
#fecPayloadIdsWithoutProfile = 0;
|
|
@@ -12622,15 +12607,24 @@ var MmtpRouter = class {
|
|
|
12622
12607
|
return NO_FEC_GEOMETRY_HORIZON;
|
|
12623
12608
|
}
|
|
12624
12609
|
const blockSpanMs = fec.interleaveDepthMsForPacket(packetId);
|
|
12610
|
+
const blockLifetimeMs = fec.blockLifetimeMsForPacket(packetId);
|
|
12611
|
+
if (blockSpanMs === void 0 || blockLifetimeMs === void 0) return NO_FEC_GEOMETRY_HORIZON;
|
|
12625
12612
|
if (blockSpanMs === 0) return NO_BLOCK_SPAN_HORIZON;
|
|
12626
12613
|
return {
|
|
12627
12614
|
kind: "fec-horizon",
|
|
12628
|
-
retentionMs: duplicateRetentionMs({
|
|
12629
|
-
blockSpanMs,
|
|
12630
|
-
blockLifetimeMs: fec.blockLifetimeMsForPacket(packetId)
|
|
12631
|
-
})
|
|
12615
|
+
retentionMs: duplicateRetentionMs({ blockSpanMs, blockLifetimeMs })
|
|
12632
12616
|
};
|
|
12633
12617
|
}
|
|
12618
|
+
/**
|
|
12619
|
+
* Init segments delivered for a packetId with no `onTrackInit` handler
|
|
12620
|
+
* since construction, whether the deprecated aggregate `onInitSegment`
|
|
12621
|
+
* callback then received them or nothing did. A supported deployment reads
|
|
12622
|
+
* 0 here; a non-zero value is the evidence that blocks removing the
|
|
12623
|
+
* aggregate callback (MOQT-01, BLO-21181).
|
|
12624
|
+
*/
|
|
12625
|
+
get unmappedInitDeliveryCount() {
|
|
12626
|
+
return this.#unmappedInitDeliveries;
|
|
12627
|
+
}
|
|
12634
12628
|
/** Publisher restarts detected on the multicast leg since construction. */
|
|
12635
12629
|
get publisherRestartCount() {
|
|
12636
12630
|
return this.#publisherRestarts;
|
|
@@ -12718,7 +12712,7 @@ var MmtpRouter = class {
|
|
|
12718
12712
|
/** Update MFU reassembly timeout. Source and FEC-recovery paths share the
|
|
12719
12713
|
* parser's single reassembler (BLO-8151), so one update covers both. */
|
|
12720
12714
|
setMfuTimeoutMs(ms) {
|
|
12721
|
-
if (ms <= 0) throw new Error("[MmtpRouter] mfuTimeoutMs must be > 0");
|
|
12715
|
+
if (ms !== null && ms <= 0) throw new Error("[MmtpRouter] mfuTimeoutMs must be > 0");
|
|
12722
12716
|
this.#parser.setMfuTimeoutMs(ms);
|
|
12723
12717
|
}
|
|
12724
12718
|
/** Update parser frame reordering for unordered multicast delivery.
|
|
@@ -12788,7 +12782,15 @@ var MmtpRouter = class {
|
|
|
12788
12782
|
onTrackPacket(packetId, cb) {
|
|
12789
12783
|
this.#trackPacketCallbacks.set(packetId, cb);
|
|
12790
12784
|
}
|
|
12791
|
-
/**
|
|
12785
|
+
/**
|
|
12786
|
+
* Register an init segment callback (ftyp+moov from MMTP MPU metadata).
|
|
12787
|
+
* Receives an init only for a packetId with no `onTrackInit` handler.
|
|
12788
|
+
*
|
|
12789
|
+
* @deprecated Use onTrackInit(packetId, cb). Still delivered as a fallback;
|
|
12790
|
+
* removed in the 1.0.0 major, no earlier than one minor release after this
|
|
12791
|
+
* deprecation reaches the `latest` dist-tag (BLO-21181). Count fallback
|
|
12792
|
+
* deliveries with `unmappedInitDeliveryCount`.
|
|
12793
|
+
*/
|
|
12792
12794
|
onInitSegment(cb) {
|
|
12793
12795
|
this.#onInitSegment = cb;
|
|
12794
12796
|
}
|
|
@@ -12802,17 +12804,16 @@ var MmtpRouter = class {
|
|
|
12802
12804
|
* When active, source frames are buffered per FEC block and emitted as
|
|
12803
12805
|
* multi-sample CMAF segments instead of per-frame callbacks. */
|
|
12804
12806
|
enableMseAssembler(packetId, config, catalogTrackName) {
|
|
12805
|
-
const k = this.#kForPacket(packetId);
|
|
12806
12807
|
const assemblerKey = this.#mseAssemblerKey(packetId, catalogTrackName);
|
|
12807
12808
|
const previous = this.#mseAssemblers.get(assemblerKey);
|
|
12808
12809
|
if (previous) {
|
|
12809
12810
|
previous.flush();
|
|
12810
12811
|
previous.dispose();
|
|
12811
12812
|
}
|
|
12812
|
-
const assembler = new CmafBlockAssembler(
|
|
12813
|
+
const assembler = new CmafBlockAssembler(config);
|
|
12813
12814
|
this.#mseAssemblers.set(assemblerKey, assembler);
|
|
12814
12815
|
console.log(
|
|
12815
|
-
`[MmtpRouter] MSE assembler enabled for packetId=${packetId}${catalogTrackName === void 0 ? "" : ` track=${catalogTrackName}`} (
|
|
12816
|
+
`[MmtpRouter] MSE assembler enabled for packetId=${packetId}${catalogTrackName === void 0 ? "" : ` track=${catalogTrackName}`} (${config.trackType})`
|
|
12816
12817
|
);
|
|
12817
12818
|
return assembler;
|
|
12818
12819
|
}
|
|
@@ -12921,6 +12922,21 @@ var MmtpRouter = class {
|
|
|
12921
12922
|
}
|
|
12922
12923
|
this.#fecMappersByPacketId.set(packetId, mapper);
|
|
12923
12924
|
}
|
|
12925
|
+
/**
|
|
12926
|
+
* Release the router side of a packetId whose FEC is ending: drop its block
|
|
12927
|
+
* mapper and end its FEC release gating. Frames the gate holds are released
|
|
12928
|
+
* in order, each gap stalled (no block will resolve it), not stranded. Call
|
|
12929
|
+
* before FECManager.releasePacket, while the gate can still read its blocks.
|
|
12930
|
+
*/
|
|
12931
|
+
releaseFecPacket(packetId) {
|
|
12932
|
+
this.#fecMappersByPacketId.delete(packetId);
|
|
12933
|
+
this.#withDeferredFecPokes(() => {
|
|
12934
|
+
this.#parser.endFecRelease(packetId);
|
|
12935
|
+
for (const [key, poke] of this.#pendingFecPokes) {
|
|
12936
|
+
if (poke.packetId === packetId) this.#pendingFecPokes.delete(key);
|
|
12937
|
+
}
|
|
12938
|
+
});
|
|
12939
|
+
}
|
|
12924
12940
|
registerManifestTrack(context) {
|
|
12925
12941
|
if (!this.#manifestVerifier) throw new Error("manifest verifier is not configured");
|
|
12926
12942
|
this.#manifestVerifier.registerTrack(context);
|
|
@@ -13040,87 +13056,21 @@ var MmtpRouter = class {
|
|
|
13040
13056
|
this.#noteUnreadableFecPayloadId(ownerPacketId, "repair");
|
|
13041
13057
|
return;
|
|
13042
13058
|
}
|
|
13043
|
-
|
|
13044
|
-
this.#handleIsoRepair(ownerPacketId, catalogTrackName, parsed, "moq");
|
|
13045
|
-
return;
|
|
13046
|
-
}
|
|
13047
|
-
const repairSbn = this.#deriveRepairSbn(parsed.ssStart, parsed.ssbLength, ownerPacketId);
|
|
13048
|
-
if (repairSbn === null) return;
|
|
13049
|
-
const repairEsi = this.#kForPacket(ownerPacketId) + parsed.rsId;
|
|
13050
|
-
if (!this.#feedFecTransportSymbol(
|
|
13051
|
-
ownerPacketId,
|
|
13052
|
-
repairSbn,
|
|
13053
|
-
repairEsi,
|
|
13054
|
-
parsed.symbolData,
|
|
13055
|
-
"moq",
|
|
13056
|
-
catalogTrackName
|
|
13057
|
-
)) return;
|
|
13058
|
-
const recovered = catalogTrackName === void 0 ? this.#fecManager.feedRepairSymbol(
|
|
13059
|
-
ownerPacketId,
|
|
13060
|
-
parsed.ssStart,
|
|
13061
|
-
parsed.ssbLength,
|
|
13062
|
-
parsed.rsId,
|
|
13063
|
-
parsed.symbolData
|
|
13064
|
-
) : this.#fecManager.feedRepairSymbol(
|
|
13065
|
-
ownerPacketId,
|
|
13066
|
-
parsed.ssStart,
|
|
13067
|
-
parsed.ssbLength,
|
|
13068
|
-
parsed.rsId,
|
|
13069
|
-
parsed.symbolData,
|
|
13070
|
-
catalogTrackName
|
|
13071
|
-
);
|
|
13072
|
-
if (recovered) {
|
|
13073
|
-
const alreadyCompleted = catalogTrackName === void 0 ? this.#fecManager.isBlockCompleted(repairSbn, ownerPacketId) : this.#fecManager.isBlockCompleted(repairSbn, ownerPacketId, catalogTrackName);
|
|
13074
|
-
if (catalogTrackName === void 0) {
|
|
13075
|
-
this.#fecManager.cancelBlockTimeout(repairSbn, ownerPacketId);
|
|
13076
|
-
} else {
|
|
13077
|
-
this.#fecManager.cancelBlockTimeout(repairSbn, ownerPacketId, catalogTrackName);
|
|
13078
|
-
}
|
|
13079
|
-
if (!alreadyCompleted) {
|
|
13080
|
-
this.#fecManager.countRecovery(ownerPacketId, repairSbn, catalogTrackName);
|
|
13081
|
-
if (catalogTrackName === void 0) {
|
|
13082
|
-
this.#fecManager.markBlockCompleted(repairSbn, ownerPacketId);
|
|
13083
|
-
} else {
|
|
13084
|
-
this.#fecManager.markBlockCompleted(repairSbn, ownerPacketId, catalogTrackName);
|
|
13085
|
-
}
|
|
13086
|
-
this.#routeRecoveredBlock(
|
|
13087
|
-
repairSbn,
|
|
13088
|
-
recovered,
|
|
13089
|
-
ownerPacketId,
|
|
13090
|
-
catalogTrackName
|
|
13091
|
-
);
|
|
13092
|
-
}
|
|
13093
|
-
}
|
|
13059
|
+
this.#handleIsoRepair(ownerPacketId, catalogTrackName, parsed, "moq");
|
|
13094
13060
|
}
|
|
13095
13061
|
}
|
|
13096
|
-
// ──
|
|
13097
|
-
#kForPacket(packetId) {
|
|
13098
|
-
if (packetId != null)
|
|
13099
|
-
return this.#fecManager?.kForPacket?.(packetId) ?? this.#fecMappersByPacketId.get(packetId)?.k ?? this.#fecManager?.k ?? 4;
|
|
13100
|
-
return this.#defaultFecMapper?.k ?? this.#fecManager?.k ?? 4;
|
|
13101
|
-
}
|
|
13062
|
+
// ── Packet FEC geometry and block profile ───────────────────────
|
|
13102
13063
|
#symbolSizeForPacket(packetId) {
|
|
13103
|
-
if (packetId != null) return this.#fecManager?.symbolSizeForPacket?.(packetId) ??
|
|
13064
|
+
if (packetId != null) return this.#fecManager?.symbolSizeForPacket?.(packetId) ?? 0;
|
|
13104
13065
|
return this.#fecManager?.symbolSize ?? 0;
|
|
13105
13066
|
}
|
|
13106
|
-
/** Derive SBN/ESI from SS_ID using packet-scoped FEC geometry. */
|
|
13107
|
-
#deriveSbnEsi(ssId, packetId) {
|
|
13108
|
-
const mapper = packetId == null ? this.#defaultFecMapper : this.#fecMappersByPacketId.get(packetId) ?? null;
|
|
13109
|
-
if (mapper) return mapper.fromSsId(ssId);
|
|
13110
|
-
const k = this.#kForPacket(packetId);
|
|
13111
|
-
return { sbn: Math.floor(ssId / k), esi: ssId % k };
|
|
13112
|
-
}
|
|
13113
|
-
#deriveRepairSbn(ssStart, ssbLength, packetId) {
|
|
13114
|
-
const k = this.#kForPacket(packetId);
|
|
13115
|
-
if (ssbLength <= 0 || ssbLength > k || ssStart % k !== 0) return null;
|
|
13116
|
-
return Math.floor(ssStart / k);
|
|
13117
|
-
}
|
|
13118
13067
|
/**
|
|
13119
13068
|
* The configured block profile of a packetId, or undefined while it is
|
|
13120
13069
|
* unconfigured. With an FEC manager its per-packet configuration is the
|
|
13121
13070
|
* only source. A router without one still recovers symbols through its
|
|
13122
13071
|
* registered mapper, built with the catalog's explicit profile: the
|
|
13123
|
-
* packet's own mapper, else the default one (
|
|
13072
|
+
* packet's own mapper, else the default one. A NullBlockMapper (a stream
|
|
13073
|
+
* without an FEC instance) states none.
|
|
13124
13074
|
*/
|
|
13125
13075
|
#configuredBlockProfile(packetId) {
|
|
13126
13076
|
if (this.#fecManager) return this.#fecManager.blockProfileForPacket(packetId);
|
|
@@ -13128,9 +13078,10 @@ var MmtpRouter = class {
|
|
|
13128
13078
|
return mapper?.blockProfile ?? void 0;
|
|
13129
13079
|
}
|
|
13130
13080
|
/**
|
|
13131
|
-
* The block profile an FEC coordinate of `packetId` is read under
|
|
13132
|
-
*
|
|
13133
|
-
*
|
|
13081
|
+
* The block profile an FEC coordinate of `packetId` is read under:
|
|
13082
|
+
* `iso-ssbg1-v1`, the only one (`k-padded-v1` is retired and refused).
|
|
13083
|
+
* Throws while none is configured: such a packet has no FEC geometry, and
|
|
13084
|
+
* nothing is inferred from the wire or defaulted.
|
|
13134
13085
|
* Packet ingress leaves such a packet's FEC Payload ID unread first
|
|
13135
13086
|
* (#noteUnreadableFecPayloadId); a throw here is a wiring fault on a path
|
|
13136
13087
|
* that cannot hold an unconfigured packet.
|
|
@@ -13144,8 +13095,16 @@ var MmtpRouter = class {
|
|
|
13144
13095
|
}
|
|
13145
13096
|
return profile;
|
|
13146
13097
|
}
|
|
13147
|
-
|
|
13148
|
-
|
|
13098
|
+
/**
|
|
13099
|
+
* The block coordinates (SS_Start, ESI) of an FEC source: range membership
|
|
13100
|
+
* in a block a repair already stated (ISO/IEC 23008-1 C.5.3). Null while no
|
|
13101
|
+
* repair has stated its block, while its packetId has no configured block
|
|
13102
|
+
* profile, and without an FEC manager: SS_ID alone locates no block, so
|
|
13103
|
+
* nothing is derived from it.
|
|
13104
|
+
*/
|
|
13105
|
+
#locateFecSource(packetId, catalogTrackName, ssId) {
|
|
13106
|
+
if (this.#configuredBlockProfile(packetId) === void 0) return null;
|
|
13107
|
+
return this.#fecManager?.locateIsoSource?.(packetId, catalogTrackName, ssId) ?? null;
|
|
13149
13108
|
}
|
|
13150
13109
|
/**
|
|
13151
13110
|
* Explicit refusal to read an FEC Payload ID: its (owner) packetId has no
|
|
@@ -13164,21 +13123,20 @@ var MmtpRouter = class {
|
|
|
13164
13123
|
}
|
|
13165
13124
|
}
|
|
13166
13125
|
/**
|
|
13167
|
-
* The release-gate anchor of a recovered symbol: its
|
|
13168
|
-
*
|
|
13169
|
-
*
|
|
13126
|
+
* The release-gate anchor of a recovered symbol: its own SS_ID, SS_Start +
|
|
13127
|
+
* ESI modulo 2^32 (C.5.2), where a frame is gated by the SS_IDs between it
|
|
13128
|
+
* and the last released frame.
|
|
13170
13129
|
*/
|
|
13171
|
-
#fecAnchorForRecovered(
|
|
13172
|
-
return
|
|
13130
|
+
#fecAnchorForRecovered(blockStart, index) {
|
|
13131
|
+
return serial32Add(blockStart, index);
|
|
13173
13132
|
}
|
|
13174
13133
|
/**
|
|
13175
13134
|
* One iso-ssbg1-v1 repair symbol (ISO/IEC 23008-1 C.5.3). It states the
|
|
13176
13135
|
* block `[SS_Start, SS_Start + SSB_length)`; repair ESI = SSB_length +
|
|
13177
13136
|
* RS_ID (K' = SSB_length). The held sources it placed (and those of a
|
|
13178
13137
|
* repair-less span in front of it) are fed as first-arrival sources, then
|
|
13179
|
-
* the repair, the order they had on the wire, so a loss-free block
|
|
13180
|
-
* on its repair
|
|
13181
|
-
* block is dropped.
|
|
13138
|
+
* the repair, the order they had on the wire, so a loss-free block completes
|
|
13139
|
+
* clean on its repair. A repair contradicting a known block is dropped.
|
|
13182
13140
|
*/
|
|
13183
13141
|
#handleIsoRepair(ownerPacketId, catalogTrackName, repair, source) {
|
|
13184
13142
|
const fec = this.#fecManager;
|
|
@@ -13245,7 +13203,7 @@ var MmtpRouter = class {
|
|
|
13245
13203
|
}
|
|
13246
13204
|
if (payload.oversized) continue;
|
|
13247
13205
|
const alreadyCompleted = fec.isBlockCompleted(blockStart, ownerPacketId, catalogTrackName);
|
|
13248
|
-
const recovered = fec.feedSourceSymbol(ownerPacketId, ssId, payload.symbolData,
|
|
13206
|
+
const recovered = fec.feedSourceSymbol(ownerPacketId, ssId, payload.symbolData, catalogTrackName);
|
|
13249
13207
|
if (!recovered || alreadyCompleted) continue;
|
|
13250
13208
|
fec.cancelBlockTimeout(blockStart, ownerPacketId, catalogTrackName);
|
|
13251
13209
|
fec.countRecovery(ownerPacketId, blockStart, catalogTrackName);
|
|
@@ -13270,7 +13228,8 @@ var MmtpRouter = class {
|
|
|
13270
13228
|
return null;
|
|
13271
13229
|
}
|
|
13272
13230
|
/** Route a recovered FEC block through the parser + dedup pipeline.
|
|
13273
|
-
* Sync WASM decoders return K fixed-width
|
|
13231
|
+
* Sync WASM decoders return the block's K' = SSB_length fixed-width
|
|
13232
|
+
* ssbg_mode1 source symbols concatenated. */
|
|
13274
13233
|
routeRecoveredBlock(blockId, data, packetId, catalogTrackName) {
|
|
13275
13234
|
this.#routeRecoveredBlock(blockId, data, packetId, catalogTrackName);
|
|
13276
13235
|
}
|
|
@@ -13738,29 +13697,19 @@ var MmtpRouter = class {
|
|
|
13738
13697
|
resolvedLoss: (packetId, catalogTrackName, fromSbn, toSbn, releasedSbn) => {
|
|
13739
13698
|
const fec = this.#fecManager;
|
|
13740
13699
|
if (!fec?.blockResolution) return [];
|
|
13741
|
-
|
|
13742
|
-
|
|
13743
|
-
return fec.isoSpanResolution?.(packetId, catalogTrackName, releasedSbn, head) ?? null;
|
|
13744
|
-
}
|
|
13745
|
-
const highest = fec.highestSbn?.(packetId, catalogTrackName);
|
|
13746
|
-
const lost = [];
|
|
13747
|
-
for (let sbn = fromSbn; sbn <= toSbn; sbn++) {
|
|
13748
|
-
const resolution = fec.blockResolution(sbn, packetId, catalogTrackName);
|
|
13749
|
-
if (resolution === "pending") return null;
|
|
13750
|
-
if (resolution === "failed") {
|
|
13751
|
-
lost.push(sbn);
|
|
13752
|
-
} else if (resolution === "untracked") {
|
|
13753
|
-
if (sbn === releasedSbn) continue;
|
|
13754
|
-
if (highest === void 0 || sbn > highest) return null;
|
|
13755
|
-
lost.push(sbn);
|
|
13756
|
-
}
|
|
13757
|
-
}
|
|
13758
|
-
return lost;
|
|
13700
|
+
const head = fromSbn === releasedSbn ? toSbn : fromSbn;
|
|
13701
|
+
return fec.isoSpanResolution?.(packetId, catalogTrackName, releasedSbn, head) ?? null;
|
|
13759
13702
|
},
|
|
13760
13703
|
// The retention window itself: a frame is never still held when a block
|
|
13761
13704
|
// it waits on retires. Only consulted while resolvedLoss holds a gap,
|
|
13762
13705
|
// which needs blockResolution, so the check above guarantees the method.
|
|
13763
|
-
|
|
13706
|
+
// A packetId the manager no longer has configured (released) has no
|
|
13707
|
+
// block left to wait on: release its frames now (BLO-45953).
|
|
13708
|
+
holdLimitMs: (packetId) => {
|
|
13709
|
+
const fec = this.#fecManager;
|
|
13710
|
+
if (!fec?.terminalRetentionMsForPacket) return Infinity;
|
|
13711
|
+
return fec.terminalRetentionMsForPacket(packetId) ?? 0;
|
|
13712
|
+
},
|
|
13764
13713
|
decide: (before, after, lostBlocks, discontinuity) => {
|
|
13765
13714
|
const decision = this.#decideGap(before, after, lostBlocks, discontinuity);
|
|
13766
13715
|
this.#onFrameGap?.(decision);
|
|
@@ -13822,9 +13771,10 @@ var MmtpRouter = class {
|
|
|
13822
13771
|
}
|
|
13823
13772
|
/**
|
|
13824
13773
|
* The most media units a gap between two released frames can hold: the
|
|
13825
|
-
* FEC blocks
|
|
13826
|
-
* block touches (ceil(interleaveDepthMs / unit duration), §5.1).
|
|
13827
|
-
* either
|
|
13774
|
+
* FEC blocks between `before`'s anchor and `after`'s, times D, the media
|
|
13775
|
+
* units one block touches (ceil(interleaveDepthMs / unit duration), §5.1).
|
|
13776
|
+
* Null when either anchor, the unit duration or the catalog interleave is
|
|
13777
|
+
* unknown.
|
|
13828
13778
|
*/
|
|
13829
13779
|
#maxMissingUnits(before, after) {
|
|
13830
13780
|
const from = before.fecSbn;
|
|
@@ -13837,7 +13787,7 @@ var MmtpRouter = class {
|
|
|
13837
13787
|
const denominator = BigInt(timing.durationTicks) * 1000n;
|
|
13838
13788
|
const unitsPerBlock = (numerator + denominator - 1n) / denominator;
|
|
13839
13789
|
const blocks = BigInt(
|
|
13840
|
-
(
|
|
13790
|
+
(serial32Precedes(from, to) ? serial32Distance(from, to) : serial32Distance(to, from)) + 1
|
|
13841
13791
|
);
|
|
13842
13792
|
const bound = unitsPerBlock * blocks;
|
|
13843
13793
|
return bound > BigInt(Number.MAX_SAFE_INTEGER) ? null : Number(bound);
|
|
@@ -13887,8 +13837,9 @@ var MmtpRouter = class {
|
|
|
13887
13837
|
const cb = this.#trackInitCallbacks.get(packetId);
|
|
13888
13838
|
if (cb) {
|
|
13889
13839
|
cb(data);
|
|
13890
|
-
} else
|
|
13891
|
-
this.#
|
|
13840
|
+
} else {
|
|
13841
|
+
this.#unmappedInitDeliveries++;
|
|
13842
|
+
this.#onInitSegment?.(data);
|
|
13892
13843
|
}
|
|
13893
13844
|
console.log(
|
|
13894
13845
|
`[MmtpRouter] Init delivered for packetId=${packetId}${catalogTrackName === void 0 ? "" : ` track=${catalogTrackName}`} (${data.byteLength}B)`
|
|
@@ -13930,9 +13881,9 @@ var MmtpRouter = class {
|
|
|
13930
13881
|
if (!info.isRepair && this.#manifestVerifier && !manifestAdmitted) {
|
|
13931
13882
|
let identity = manifestIdentity;
|
|
13932
13883
|
if (!identity && info.fecPayloadId && !fecUnreadable && (this.#resolveManifestIdentityForRoute || this.#resolveManifestIdentity)) {
|
|
13933
|
-
const coordinates = this.#deriveSbnEsi(info.fecPayloadId.ssId, info.packetId);
|
|
13934
13884
|
const catalogTrackName = catalogTrackNameForPacket(ingress, info.packetId);
|
|
13935
|
-
|
|
13885
|
+
const coordinates = this.#locateFecSource(info.packetId, catalogTrackName, info.fecPayloadId.ssId);
|
|
13886
|
+
identity = coordinates === null ? void 0 : (this.#resolveManifestIdentityForRoute ? this.#resolveManifestIdentityForRoute({
|
|
13936
13887
|
packetId: info.packetId,
|
|
13937
13888
|
sbn: coordinates.sbn,
|
|
13938
13889
|
esi: coordinates.esi,
|
|
@@ -13984,79 +13935,7 @@ var MmtpRouter = class {
|
|
|
13984
13935
|
this.#noteUnreadableFecPayloadId(ownerPacketId, "repair");
|
|
13985
13936
|
return;
|
|
13986
13937
|
}
|
|
13987
|
-
|
|
13988
|
-
this.#handleIsoRepair(ownerPacketId, catalogTrackNameForPacket(ingress, ownerPacketId), repair, source);
|
|
13989
|
-
return;
|
|
13990
|
-
}
|
|
13991
|
-
const repairSbn = this.#deriveRepairSbn(repair.ssStart, repair.ssbLength, ownerPacketId);
|
|
13992
|
-
if (repairSbn === null) return;
|
|
13993
|
-
const repairEsi = this.#kForPacket(ownerPacketId) + repair.rsId;
|
|
13994
|
-
if (!this.#feedFecTransportSymbol(
|
|
13995
|
-
ownerPacketId,
|
|
13996
|
-
repairSbn,
|
|
13997
|
-
repairEsi,
|
|
13998
|
-
repair.symbolData,
|
|
13999
|
-
source,
|
|
14000
|
-
catalogTrackNameForPacket(ingress, ownerPacketId)
|
|
14001
|
-
)) return;
|
|
14002
|
-
const repairCatalogTrackName = catalogTrackNameForPacket(ingress, ownerPacketId);
|
|
14003
|
-
if (repairCatalogTrackName === void 0) {
|
|
14004
|
-
this.#fecManager.noteBlockActivity?.(repairSbn, ownerPacketId);
|
|
14005
|
-
} else {
|
|
14006
|
-
this.#fecManager.noteBlockActivity?.(
|
|
14007
|
-
repairSbn,
|
|
14008
|
-
ownerPacketId,
|
|
14009
|
-
repairCatalogTrackName
|
|
14010
|
-
);
|
|
14011
|
-
}
|
|
14012
|
-
const recovered = repairCatalogTrackName === void 0 ? this.#fecManager.feedRepairSymbol(
|
|
14013
|
-
ownerPacketId,
|
|
14014
|
-
repair.ssStart,
|
|
14015
|
-
repair.ssbLength,
|
|
14016
|
-
repair.rsId,
|
|
14017
|
-
repair.symbolData
|
|
14018
|
-
) : this.#fecManager.feedRepairSymbol(
|
|
14019
|
-
ownerPacketId,
|
|
14020
|
-
repair.ssStart,
|
|
14021
|
-
repair.ssbLength,
|
|
14022
|
-
repair.rsId,
|
|
14023
|
-
repair.symbolData,
|
|
14024
|
-
repairCatalogTrackName
|
|
14025
|
-
);
|
|
14026
|
-
if (recovered) {
|
|
14027
|
-
const alreadyCompleted = repairCatalogTrackName === void 0 ? this.#fecManager.isBlockCompleted(repairSbn, ownerPacketId) : this.#fecManager.isBlockCompleted(
|
|
14028
|
-
repairSbn,
|
|
14029
|
-
ownerPacketId,
|
|
14030
|
-
repairCatalogTrackName
|
|
14031
|
-
);
|
|
14032
|
-
if (repairCatalogTrackName === void 0) {
|
|
14033
|
-
this.#fecManager.cancelBlockTimeout(repairSbn, ownerPacketId);
|
|
14034
|
-
} else {
|
|
14035
|
-
this.#fecManager.cancelBlockTimeout(
|
|
14036
|
-
repairSbn,
|
|
14037
|
-
ownerPacketId,
|
|
14038
|
-
repairCatalogTrackName
|
|
14039
|
-
);
|
|
14040
|
-
}
|
|
14041
|
-
if (!alreadyCompleted) {
|
|
14042
|
-
this.#fecManager.countRecovery(ownerPacketId, repairSbn, repairCatalogTrackName);
|
|
14043
|
-
if (repairCatalogTrackName === void 0) {
|
|
14044
|
-
this.#fecManager.markBlockCompleted(repairSbn, ownerPacketId);
|
|
14045
|
-
} else {
|
|
14046
|
-
this.#fecManager.markBlockCompleted(
|
|
14047
|
-
repairSbn,
|
|
14048
|
-
ownerPacketId,
|
|
14049
|
-
repairCatalogTrackName
|
|
14050
|
-
);
|
|
14051
|
-
}
|
|
14052
|
-
this.#routeRecoveredBlock(
|
|
14053
|
-
repairSbn,
|
|
14054
|
-
recovered,
|
|
14055
|
-
ownerPacketId,
|
|
14056
|
-
catalogTrackNameForPacket(ingress, ownerPacketId)
|
|
14057
|
-
);
|
|
14058
|
-
}
|
|
14059
|
-
}
|
|
13938
|
+
this.#handleIsoRepair(ownerPacketId, catalogTrackNameForPacket(ingress, ownerPacketId), repair, source);
|
|
14060
13939
|
}
|
|
14061
13940
|
} else {
|
|
14062
13941
|
if (this.#classifyPacketId(info.packetId) === null) return;
|
|
@@ -14080,8 +13959,8 @@ var MmtpRouter = class {
|
|
|
14080
13959
|
}
|
|
14081
13960
|
const duplicates = this.#stream(info.packetId, catalogTrackName).duplicates;
|
|
14082
13961
|
const fecPayloadId = fecUnreadable ? null : info.fecPayloadId;
|
|
14083
|
-
const
|
|
14084
|
-
const coords =
|
|
13962
|
+
const fecSource = this.#fecManager !== null && fecPayloadId != null;
|
|
13963
|
+
const coords = fecSource && fecPayloadId ? this.#locateFecSource(info.packetId, catalogTrackName, fecPayloadId.ssId) ?? void 0 : void 0;
|
|
14085
13964
|
duplicates.advance(coords?.sbn);
|
|
14086
13965
|
if (source === "multicast" && !overlapCopy && !releasedFromQuarantine && catalogTrackName !== void 0 && duplicates.predatesHorizon(info.sequenceNumber, fecPayloadId?.ssId)) {
|
|
14087
13966
|
this.#publisherRestarts++;
|
|
@@ -14096,9 +13975,8 @@ var MmtpRouter = class {
|
|
|
14096
13975
|
}
|
|
14097
13976
|
if (this.#fecManager && fecPayloadId && coords) {
|
|
14098
13977
|
const fecId = fecPayloadId;
|
|
14099
|
-
const symbolSize = this.#symbolSizeForPacket(info.packetId);
|
|
14100
13978
|
const rawSymbol = packet.subarray(0, packet.length - 4);
|
|
14101
|
-
const oversized =
|
|
13979
|
+
const oversized = !(this.#fecManager.isoSourceFits?.(info.packetId, rawSymbol.length) ?? false);
|
|
14102
13980
|
let overlapBlockCompleted = false;
|
|
14103
13981
|
if (overlapCopy) {
|
|
14104
13982
|
overlapBlockCompleted = catalogTrackName === void 0 ? this.#fecManager.isBlockCompleted(coords.sbn, info.packetId) : this.#fecManager.isBlockCompleted(
|
|
@@ -14133,7 +14011,7 @@ var MmtpRouter = class {
|
|
|
14133
14011
|
}
|
|
14134
14012
|
parseSourceSymbol = (overlapCopy ? !overlapBlockCompleted : sourceSymbolAccepted) && !duplicates.parsedSymbols.has(fecId.ssId);
|
|
14135
14013
|
fecInfo = { sbn: coords.sbn, esi: coords.esi, oversized };
|
|
14136
|
-
} else if (
|
|
14014
|
+
} else if (fecSource && this.#fecManager && fecPayloadId) {
|
|
14137
14015
|
const ssId = fecPayloadId.ssId;
|
|
14138
14016
|
if (overlapCopy) {
|
|
14139
14017
|
parseSourceSymbol = !duplicates.parsedSymbols.has(ssId);
|
|
@@ -14167,12 +14045,13 @@ var MmtpRouter = class {
|
|
|
14167
14045
|
this.#feedViaParser(packet, info, fecInfo, {
|
|
14168
14046
|
source,
|
|
14169
14047
|
catalogTrackName,
|
|
14170
|
-
|
|
14048
|
+
// An FEC source frame is anchored by its own SS_ID (C.5.2).
|
|
14049
|
+
...fecSource && fecPayloadId ? { fecSbn: fecPayloadId.ssId } : {}
|
|
14171
14050
|
}, parsed);
|
|
14172
14051
|
}
|
|
14173
14052
|
if (this.#classifyPacketId(info.packetId) === null) return;
|
|
14174
|
-
if (this.#fecManager && fecPayloadId && sourceSymbolAccepted && sourceSymbolTrackable) {
|
|
14175
|
-
const { sbn, esi } =
|
|
14053
|
+
if (this.#fecManager && fecPayloadId && coords && sourceSymbolAccepted && sourceSymbolTrackable) {
|
|
14054
|
+
const { sbn, esi } = coords;
|
|
14176
14055
|
if (this.#classifyPacketId(info.packetId) !== null && this.#fecManager.isTrackConfigured?.(info.packetId) !== false) {
|
|
14177
14056
|
this.#fecManager.trackBlock(
|
|
14178
14057
|
sbn,
|
|
@@ -14193,13 +14072,11 @@ var MmtpRouter = class {
|
|
|
14193
14072
|
const recovered = catalogTrackName === void 0 ? this.#fecManager.feedSourceSymbol(
|
|
14194
14073
|
info.packetId,
|
|
14195
14074
|
sourceFec.ssId,
|
|
14196
|
-
sourceFec.symbolData
|
|
14197
|
-
void 0
|
|
14075
|
+
sourceFec.symbolData
|
|
14198
14076
|
) : this.#fecManager.feedSourceSymbol(
|
|
14199
14077
|
info.packetId,
|
|
14200
14078
|
sourceFec.ssId,
|
|
14201
14079
|
sourceFec.symbolData,
|
|
14202
|
-
void 0,
|
|
14203
14080
|
catalogTrackName
|
|
14204
14081
|
);
|
|
14205
14082
|
if (recovered) {
|
|
@@ -14606,7 +14483,6 @@ var MmtpRouter = class {
|
|
|
14606
14483
|
// ── Private: FEC recovery routing ───────────────────────────────
|
|
14607
14484
|
/** Route WASM-recovered block through appropriate path based on container kind. */
|
|
14608
14485
|
#routeRecoveredBlock(blockId, recovered, ownerPacketId, catalogTrackName) {
|
|
14609
|
-
const k = this.#kForPacket(ownerPacketId);
|
|
14610
14486
|
const packetSymbolSize = this.#symbolSizeForPacket(ownerPacketId);
|
|
14611
14487
|
if (ownerPacketId === void 0) {
|
|
14612
14488
|
throw new Error("[MmtpRouter] a recovered FEC block needs its owner packetId to read its fec.blockProfile");
|
|
@@ -14619,7 +14495,6 @@ var MmtpRouter = class {
|
|
|
14619
14495
|
}
|
|
14620
14496
|
const symbolSize = packetSymbolSize;
|
|
14621
14497
|
const result = processFecBlock(recovered, {
|
|
14622
|
-
...blockProfile === "k-padded-v1" ? { k } : {},
|
|
14623
14498
|
symbolSize,
|
|
14624
14499
|
ownerPacketId,
|
|
14625
14500
|
blockProfile
|
|
@@ -14699,8 +14574,7 @@ var MmtpRouter = class {
|
|
|
14699
14574
|
const admission = verifier.admitObject(
|
|
14700
14575
|
{
|
|
14701
14576
|
...identity,
|
|
14702
|
-
packetId: ownerPacketId
|
|
14703
|
-
...ownerPacketId !== void 0 && this.#isIsoPacket(ownerPacketId) ? {} : { ssId: blockId * this.#kForPacket(ownerPacketId) + symbol.index }
|
|
14577
|
+
packetId: ownerPacketId
|
|
14704
14578
|
},
|
|
14705
14579
|
symbol.rawSymbol,
|
|
14706
14580
|
() => {
|
|
@@ -14803,7 +14677,7 @@ var MmtpRouter = class {
|
|
|
14803
14677
|
rapFlag: sym.fragment.rapFlag,
|
|
14804
14678
|
recovered: true,
|
|
14805
14679
|
deliveryProvenance,
|
|
14806
|
-
fecSbn: this.#fecAnchorForRecovered(
|
|
14680
|
+
fecSbn: this.#fecAnchorForRecovered(blockId, sym.index),
|
|
14807
14681
|
...catalogTrackName === void 0 ? {} : { catalogTrackName }
|
|
14808
14682
|
});
|
|
14809
14683
|
continue;
|
|
@@ -14915,7 +14789,7 @@ var MmtpRouter = class {
|
|
|
14915
14789
|
rapFlag: assembled.rapFlag,
|
|
14916
14790
|
recovered: true,
|
|
14917
14791
|
...assembled.deliveryProvenance === void 0 ? {} : { deliveryProvenance: assembled.deliveryProvenance },
|
|
14918
|
-
fecSbn: this.#fecAnchorForRecovered(
|
|
14792
|
+
fecSbn: this.#fecAnchorForRecovered(blockId, sym.index),
|
|
14919
14793
|
...recoveredCatalogTrackName === void 0 ? {} : { catalogTrackName: recoveredCatalogTrackName }
|
|
14920
14794
|
});
|
|
14921
14795
|
continue;
|
|
@@ -15052,7 +14926,7 @@ var MmtpRouter = class {
|
|
|
15052
14926
|
rapFlag: sym.fragment.rapFlag,
|
|
15053
14927
|
recovered: true,
|
|
15054
14928
|
deliveryProvenance,
|
|
15055
|
-
fecSbn: this.#fecAnchorForRecovered(
|
|
14929
|
+
fecSbn: this.#fecAnchorForRecovered(blockId, sym.index),
|
|
15056
14930
|
...catalogTrackName === void 0 ? {} : { catalogTrackName }
|
|
15057
14931
|
});
|
|
15058
14932
|
} else {
|
|
@@ -15131,7 +15005,7 @@ var MmtpRouter = class {
|
|
|
15131
15005
|
rapFlag: assembled.rapFlag,
|
|
15132
15006
|
recovered: true,
|
|
15133
15007
|
deliveryProvenance,
|
|
15134
|
-
fecSbn: this.#fecAnchorForRecovered(
|
|
15008
|
+
fecSbn: this.#fecAnchorForRecovered(blockId, sym.index),
|
|
15135
15009
|
...assembled.catalogTrackName === void 0 ? {} : { catalogTrackName: assembled.catalogTrackName }
|
|
15136
15010
|
});
|
|
15137
15011
|
}
|
|
@@ -15166,7 +15040,7 @@ var MmtpRouter = class {
|
|
|
15166
15040
|
...sym.fragment.sampleNumber === void 0 ? {} : { sampleNumber: sym.fragment.sampleNumber },
|
|
15167
15041
|
recovered: true,
|
|
15168
15042
|
deliveryProvenance,
|
|
15169
|
-
fecSbn: this.#fecAnchorForRecovered(
|
|
15043
|
+
fecSbn: this.#fecAnchorForRecovered(blockId, sym.index),
|
|
15170
15044
|
...catalogTrackName === void 0 ? {} : { catalogTrackName }
|
|
15171
15045
|
});
|
|
15172
15046
|
continue;
|
|
@@ -15217,22 +15091,17 @@ var MoqFecAlgorithm;
|
|
|
15217
15091
|
MoqFecAlgorithm2[MoqFecAlgorithm2["RaptorQ"] = 2] = "RaptorQ";
|
|
15218
15092
|
})(MoqFecAlgorithm || (MoqFecAlgorithm = {}));
|
|
15219
15093
|
|
|
15220
|
-
// node_modules/@blockcast/mmt-
|
|
15221
|
-
var
|
|
15222
|
-
var
|
|
15223
|
-
var
|
|
15224
|
-
|
|
15225
|
-
// node_modules/@blockcast/mmt-transport/node_modules/@blockcast/mmt-base/dist/mmtp.js
|
|
15226
|
-
var MPU_HEADER_SIZE4 = 8;
|
|
15227
|
-
var MPU_LENGTH_FIELD_SIZE4 = 2;
|
|
15228
|
-
var MPU_HEADER_REMAINDER_SIZE4 = MPU_HEADER_SIZE4 - MPU_LENGTH_FIELD_SIZE4;
|
|
15094
|
+
// node_modules/@blockcast/mmt-base/dist/mmtp.js
|
|
15095
|
+
var MPU_HEADER_SIZE2 = 8;
|
|
15096
|
+
var MPU_LENGTH_FIELD_SIZE2 = 2;
|
|
15097
|
+
var MPU_HEADER_REMAINDER_SIZE2 = MPU_HEADER_SIZE2 - MPU_LENGTH_FIELD_SIZE2;
|
|
15229
15098
|
var MMTP_SIGNALING_PACKET_ID2 = 0;
|
|
15230
15099
|
var MMTP_PACKET_ID_MAX2 = 65535;
|
|
15231
15100
|
|
|
15232
|
-
// node_modules/@blockcast/mmt-
|
|
15101
|
+
// node_modules/@blockcast/mmt-base/dist/net-constants.js
|
|
15233
15102
|
var DEFAULT_AMT_RELAY_PORT2 = 2268;
|
|
15234
15103
|
|
|
15235
|
-
// node_modules/@blockcast/mmt-
|
|
15104
|
+
// node_modules/@blockcast/mmt-base/dist/ntp-short.js
|
|
15236
15105
|
var NTP_SHORT_BITS2 = 32;
|
|
15237
15106
|
var NTP_SHORT_MODULUS2 = 2 ** NTP_SHORT_BITS2;
|
|
15238
15107
|
var NTP_SHORT_HALF_RANGE2 = 2 ** (NTP_SHORT_BITS2 - 1);
|
|
@@ -15253,9 +15122,8 @@ function compareNtpShort(a, b) {
|
|
|
15253
15122
|
return "unordered";
|
|
15254
15123
|
}
|
|
15255
15124
|
|
|
15256
|
-
// node_modules/@blockcast/mmt-
|
|
15125
|
+
// node_modules/@blockcast/mmt-base/dist/fec-block-profile.js
|
|
15257
15126
|
var FEC_BLOCK_PROFILES2 = Object.freeze([
|
|
15258
|
-
"k-padded-v1",
|
|
15259
15127
|
"iso-ssbg1-v1"
|
|
15260
15128
|
]);
|
|
15261
15129
|
|
|
@@ -15647,10 +15515,12 @@ var MulticastOrchestrator = class {
|
|
|
15647
15515
|
}
|
|
15648
15516
|
onGoodput(sample) {
|
|
15649
15517
|
const bitrate = sample.bitrate;
|
|
15650
|
-
if (!this.#current
|
|
15518
|
+
if (!this.#current)
|
|
15651
15519
|
return void 0;
|
|
15652
15520
|
if (this.#state === "joining")
|
|
15653
15521
|
return this.poll();
|
|
15522
|
+
if (this.options.adaptive === false)
|
|
15523
|
+
return void 0;
|
|
15654
15524
|
if (bitrate === void 0 || !Number.isFinite(bitrate))
|
|
15655
15525
|
return void 0;
|
|
15656
15526
|
this.#estimate = bitrate;
|
|
@@ -15717,6 +15587,27 @@ var MulticastOrchestrator = class {
|
|
|
15717
15587
|
this.#favored = void 0;
|
|
15718
15588
|
return { kind: "join-target", current: this.#current, target: desired };
|
|
15719
15589
|
}
|
|
15590
|
+
/**
|
|
15591
|
+
* Join `target` because the viewer selected it, not because goodput or
|
|
15592
|
+
* headroom ranked it. It runs whether or not the orchestrator adapts, and
|
|
15593
|
+
* takes the path an adaptive join takes: commit on the target's RAP at or
|
|
15594
|
+
* beyond the high-water mark, abandon at the join deadline. While it is
|
|
15595
|
+
* pending nothing re-ranks the ladder; goodput only advances the deadline.
|
|
15596
|
+
*
|
|
15597
|
+
* Returns undefined, changing nothing, while another join is pending (cancel
|
|
15598
|
+
* it first), when `target` is the current tier, or when it carries no media.
|
|
15599
|
+
*/
|
|
15600
|
+
select(target) {
|
|
15601
|
+
if (!this.#current || this.#state === "joining" || !carriesMedia(target))
|
|
15602
|
+
return void 0;
|
|
15603
|
+
if (endpointKey(target) === endpointKey(this.#current))
|
|
15604
|
+
return void 0;
|
|
15605
|
+
this.#state = "joining";
|
|
15606
|
+
this.#target = target;
|
|
15607
|
+
this.#joinStartedAt = this.#now();
|
|
15608
|
+
this.#favored = void 0;
|
|
15609
|
+
return { kind: "join-target", current: this.#current, target };
|
|
15610
|
+
}
|
|
15720
15611
|
/**
|
|
15721
15612
|
* Feed a packet from the active tier (to advance the high-water mark) or
|
|
15722
15613
|
* from the in-flight target (to find a committable RAP).
|
|
@@ -16756,7 +16647,7 @@ var MulticastTransport = class _MulticastTransport {
|
|
|
16756
16647
|
// inside the window, instead of a clear+set pair on every packet.
|
|
16757
16648
|
#watchdog = null;
|
|
16758
16649
|
#lastPacketAtMs = 0;
|
|
16759
|
-
#failbackWindowMs
|
|
16650
|
+
#failbackWindowMs;
|
|
16760
16651
|
#onTakeover = null;
|
|
16761
16652
|
#onFailback = null;
|
|
16762
16653
|
// Data-plane-live edge: the first packet this join accepted. Everything
|
|
@@ -17061,14 +16952,20 @@ var MulticastTransport = class _MulticastTransport {
|
|
|
17061
16952
|
return this.#dataPlaneLive;
|
|
17062
16953
|
}
|
|
17063
16954
|
setFailbackWindowMs(windowMs) {
|
|
17064
|
-
if (!Number.isFinite(windowMs) || windowMs <= 0) {
|
|
16955
|
+
if (windowMs !== null && (!Number.isFinite(windowMs) || windowMs <= 0)) {
|
|
17065
16956
|
throw new Error(
|
|
17066
16957
|
`[MulticastTransport] failbackWindowMs=${windowMs}. Derive a positive failback window from the stream catalog.`
|
|
17067
16958
|
);
|
|
17068
16959
|
}
|
|
17069
16960
|
this.#failbackWindowMs = windowMs;
|
|
17070
|
-
if (
|
|
17071
|
-
clearTimeout(this.#watchdog);
|
|
16961
|
+
if (windowMs === null) {
|
|
16962
|
+
if (this.#watchdog !== null) clearTimeout(this.#watchdog);
|
|
16963
|
+
this.#watchdog = null;
|
|
16964
|
+
return;
|
|
16965
|
+
}
|
|
16966
|
+
if (this.#watchdog !== null) clearTimeout(this.#watchdog);
|
|
16967
|
+
this.#watchdog = null;
|
|
16968
|
+
if (this.#dataPlaneLive && this.#onFailback) {
|
|
17072
16969
|
const silentMs = performance.now() - this.#lastPacketAtMs;
|
|
17073
16970
|
this.#armWatchdog(Math.max(0, Math.ceil(windowMs - silentMs)));
|
|
17074
16971
|
}
|
|
@@ -17394,7 +17291,8 @@ var MulticastTransport = class _MulticastTransport {
|
|
|
17394
17291
|
}
|
|
17395
17292
|
if (this.#watchdog !== null) return;
|
|
17396
17293
|
if (!this.#onFailback) return;
|
|
17397
|
-
if (this.#failbackWindowMs === null)
|
|
17294
|
+
if (this.#failbackWindowMs === null) return;
|
|
17295
|
+
if (this.#failbackWindowMs === void 0) {
|
|
17398
17296
|
throw new Error(
|
|
17399
17297
|
"[MulticastTransport] Failback watchdog requires a catalog-derived window"
|
|
17400
17298
|
);
|
|
@@ -17404,7 +17302,7 @@ var MulticastTransport = class _MulticastTransport {
|
|
|
17404
17302
|
#armWatchdog(delayMs) {
|
|
17405
17303
|
this.#watchdog = setTimeout(() => {
|
|
17406
17304
|
this.#watchdog = null;
|
|
17407
|
-
if (this.#disposed || !this.#onFailback || this.#failbackWindowMs
|
|
17305
|
+
if (this.#disposed || !this.#onFailback || this.#failbackWindowMs == null) return;
|
|
17408
17306
|
const silentMs = performance.now() - this.#lastPacketAtMs;
|
|
17409
17307
|
if (silentMs < this.#failbackWindowMs) {
|
|
17410
17308
|
this.#armWatchdog(Math.ceil(this.#failbackWindowMs - silentMs));
|
|
@@ -17438,6 +17336,35 @@ var BASE64URL_DECODE_TABLE = (() => {
|
|
|
17438
17336
|
return table;
|
|
17439
17337
|
})();
|
|
17440
17338
|
|
|
17339
|
+
// src/repair-layers.ts
|
|
17340
|
+
var RepairContractError = class extends Error {
|
|
17341
|
+
constructor(code, path, message) {
|
|
17342
|
+
super(`[repair-layers:${code}] ${path}: ${message}`);
|
|
17343
|
+
this.code = code;
|
|
17344
|
+
this.path = path;
|
|
17345
|
+
this.name = "RepairContractError";
|
|
17346
|
+
}
|
|
17347
|
+
};
|
|
17348
|
+
var throwRepairContractError = (code, path, message) => {
|
|
17349
|
+
throw new RepairContractError(code, path, message);
|
|
17350
|
+
};
|
|
17351
|
+
function readFecBlockProfile(value, path, declaresFecInstance, fail) {
|
|
17352
|
+
if (value === void 0 && !declaresFecInstance) return void 0;
|
|
17353
|
+
if (isFecBlockProfile(value)) return value;
|
|
17354
|
+
if (typeof value === "string" && value.length > 0) {
|
|
17355
|
+
return fail(
|
|
17356
|
+
"track_fec_block_profile_unimplemented",
|
|
17357
|
+
path,
|
|
17358
|
+
`${JSON.stringify(value)} is not an implemented FEC block profile; only "iso-ssbg1-v1" is implemented`
|
|
17359
|
+
);
|
|
17360
|
+
}
|
|
17361
|
+
try {
|
|
17362
|
+
return parseFecBlockProfile(value, "blockProfile");
|
|
17363
|
+
} catch (error) {
|
|
17364
|
+
return fail("track_fec_block_profile", path, error.message);
|
|
17365
|
+
}
|
|
17366
|
+
}
|
|
17367
|
+
|
|
17441
17368
|
// src/carp-catalog.ts
|
|
17442
17369
|
var VALID_ROLE_SUFFIXES = /* @__PURE__ */ new Set(["base", "delta", "repair"]);
|
|
17443
17370
|
function parseTrackRole(trackName) {
|
|
@@ -17477,12 +17404,49 @@ function hasSwitchingSetRole(names) {
|
|
|
17477
17404
|
return suffix === "base" || suffix === "delta";
|
|
17478
17405
|
});
|
|
17479
17406
|
}
|
|
17480
|
-
function
|
|
17407
|
+
function isFecCatalogGeometry(obj) {
|
|
17481
17408
|
if (typeof obj !== "object" || obj === null) return false;
|
|
17482
17409
|
const o = obj;
|
|
17483
17410
|
const algorithm = o["algorithm"];
|
|
17484
17411
|
const symbolSize = o["symbolSize"];
|
|
17485
|
-
return typeof algorithm === "string" && (algorithm === "none" || algorithm === "raptorq" || algorithm === "reed-solomon") && typeof o["sourceSymbols"] === "number" && o["sourceSymbols"] >= 1 && typeof o["repairSymbols"] === "number" && o["repairSymbols"] >= 1 && typeof symbolSize === "number" && symbolSize >= 1 && (algorithm !== "raptorq" || symbolSize % 8 === 0) && typeof o["repairTrack"] === "string" && o["repairTrack"].length > 0
|
|
17412
|
+
return typeof algorithm === "string" && (algorithm === "none" || algorithm === "raptorq" || algorithm === "reed-solomon") && typeof o["sourceSymbols"] === "number" && o["sourceSymbols"] >= 1 && typeof o["repairSymbols"] === "number" && o["repairSymbols"] >= 1 && typeof symbolSize === "number" && symbolSize >= 1 && (algorithm !== "raptorq" || symbolSize % 8 === 0) && typeof o["repairTrack"] === "string" && o["repairTrack"].length > 0;
|
|
17413
|
+
}
|
|
17414
|
+
function isFecCatalogConfig(obj) {
|
|
17415
|
+
if (!isFecCatalogGeometry(obj)) return false;
|
|
17416
|
+
const o = obj;
|
|
17417
|
+
return isFecBlockProfile(o["blockProfile"]) || o["algorithm"] === "none" && o["blockProfile"] === void 0;
|
|
17418
|
+
}
|
|
17419
|
+
|
|
17420
|
+
// src/catalog-fec-params.ts
|
|
17421
|
+
import { catalogFecInterleaveMs } from "@blockcast/mmt-fec";
|
|
17422
|
+
var MAX_SAFE_TICKS_PER_HZ = Number.MAX_SAFE_INTEGER / 1e3;
|
|
17423
|
+
function recordOf2(value) {
|
|
17424
|
+
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
|
|
17425
|
+
}
|
|
17426
|
+
function blockProfileUnavailable(value, path, declaresFecInstance) {
|
|
17427
|
+
try {
|
|
17428
|
+
readFecBlockProfile(value, path, declaresFecInstance, throwRepairContractError);
|
|
17429
|
+
return void 0;
|
|
17430
|
+
} catch (error) {
|
|
17431
|
+
if (!(error instanceof RepairContractError) || error.code !== "track_fec_block_profile" && error.code !== "track_fec_block_profile_unimplemented") {
|
|
17432
|
+
throw error;
|
|
17433
|
+
}
|
|
17434
|
+
return Object.freeze({ code: error.code, path: error.path, message: error.message });
|
|
17435
|
+
}
|
|
17436
|
+
}
|
|
17437
|
+
function isoSpanUnavailable(fec, path, declaresFecInstance) {
|
|
17438
|
+
if (!declaresFecInstance || isoBlockSpanUsable(fec)) return void 0;
|
|
17439
|
+
return Object.freeze({
|
|
17440
|
+
code: "track_fec",
|
|
17441
|
+
path,
|
|
17442
|
+
message: `[repair-layers:track_fec] ${path}: must be a non-negative safe integer in milliseconds`
|
|
17443
|
+
});
|
|
17444
|
+
}
|
|
17445
|
+
function catalogFecUnavailable(fec, fecPath) {
|
|
17446
|
+
const block = recordOf2(fec);
|
|
17447
|
+
if (block === void 0) return void 0;
|
|
17448
|
+
const declaresFecInstance = block["algorithm"] !== "none";
|
|
17449
|
+
return blockProfileUnavailable(block["blockProfile"], `${fecPath}.blockProfile`, declaresFecInstance) ?? isoSpanUnavailable(block, `${fecPath}.interleaveDepthMs`, declaresFecInstance);
|
|
17486
17450
|
}
|
|
17487
17451
|
|
|
17488
17452
|
// src/multicast-manager.ts
|
|
@@ -17728,7 +17692,10 @@ function projectFecDiagnosticBlock(block) {
|
|
|
17728
17692
|
lastSymbolTs: block.lastSymbolTs,
|
|
17729
17693
|
...block.completedTs === void 0 ? {} : { completedTs: block.completedTs },
|
|
17730
17694
|
deadlineTs: block.deadlineTs,
|
|
17731
|
-
|
|
17695
|
+
// ponytail: the per-packet authenticator is gone, so this is always 0. It
|
|
17696
|
+
// stays only because pim-multicast-gateway's normalizeSubscriptionDiagnostics
|
|
17697
|
+
// still requires a number here; drop it once that validator stops asking.
|
|
17698
|
+
altaVerified: 0,
|
|
17732
17699
|
relayBlockSigValid: block.relayBlockSigValid
|
|
17733
17700
|
};
|
|
17734
17701
|
}
|
|
@@ -17748,7 +17715,6 @@ function projectFecDiagnosticConfig(config) {
|
|
|
17748
17715
|
interleaveMs: config.interleaveMs,
|
|
17749
17716
|
fecMode: config.fecMode,
|
|
17750
17717
|
...config.repairTrack === void 0 ? {} : { repairTrack: config.repairTrack },
|
|
17751
|
-
altaEnabled: config.altaEnabled,
|
|
17752
17718
|
relayBlockSigEnabled: config.relayBlockSigEnabled
|
|
17753
17719
|
};
|
|
17754
17720
|
}
|
|
@@ -17795,9 +17761,8 @@ function repairTrackNamesBySource(tracks) {
|
|
|
17795
17761
|
bySource.set(source, names);
|
|
17796
17762
|
};
|
|
17797
17763
|
for (const track of tracks) {
|
|
17798
|
-
|
|
17799
|
-
|
|
17800
|
-
}
|
|
17764
|
+
const repairTrack = typeof track.fec === "object" && track.fec !== null ? track.fec.repairTrack : void 0;
|
|
17765
|
+
if (typeof repairTrack === "string") add(track.name, repairTrack);
|
|
17801
17766
|
if (track.packaging === "fec-repair" && Array.isArray(track.depends) && track.depends.length === 1) {
|
|
17802
17767
|
add(track.depends[0], track.name);
|
|
17803
17768
|
}
|
|
@@ -17807,6 +17772,7 @@ function repairTrackNamesBySource(tracks) {
|
|
|
17807
17772
|
var MulticastManager = class {
|
|
17808
17773
|
#fec;
|
|
17809
17774
|
#router;
|
|
17775
|
+
#hostMfuTimeoutMs;
|
|
17810
17776
|
#transport;
|
|
17811
17777
|
#reportedFecDiagnosticsFailure = false;
|
|
17812
17778
|
// ── Stream-global FEC timing reduction ──
|
|
@@ -17905,6 +17871,7 @@ var MulticastManager = class {
|
|
|
17905
17871
|
this.#emitRepairLifecycle(decision);
|
|
17906
17872
|
}
|
|
17907
17873
|
});
|
|
17874
|
+
this.#hostMfuTimeoutMs = this.#router.reassembler.timeoutMs;
|
|
17908
17875
|
this.#transport = new MulticastTransport((packet, ingress) => {
|
|
17909
17876
|
this.#router.feedPacket(packet, "multicast", void 0, ingress);
|
|
17910
17877
|
});
|
|
@@ -18121,7 +18088,6 @@ var MulticastManager = class {
|
|
|
18121
18088
|
...this.#streamFecBaseline ? [this.#streamFecBaseline] : [],
|
|
18122
18089
|
...this.#trackFecTiming.values()
|
|
18123
18090
|
];
|
|
18124
|
-
if (contributions.length === 0) return;
|
|
18125
18091
|
let deliveryWindowMs;
|
|
18126
18092
|
let minimumFailbackWindowMs;
|
|
18127
18093
|
let mfuTimeoutMs;
|
|
@@ -18153,15 +18119,9 @@ var MulticastManager = class {
|
|
|
18153
18119
|
}
|
|
18154
18120
|
}
|
|
18155
18121
|
const failbackWindowMs = deliveryWindowMs === void 0 ? minimumFailbackWindowMs : Math.max(deliveryWindowMs, minimumFailbackWindowMs ?? 0);
|
|
18156
|
-
|
|
18157
|
-
|
|
18158
|
-
|
|
18159
|
-
if (mfuTimeoutMs !== void 0) {
|
|
18160
|
-
this.#router.setMfuTimeoutMs(mfuTimeoutMs);
|
|
18161
|
-
}
|
|
18162
|
-
if (reorderWindowFrames !== void 0) {
|
|
18163
|
-
this.#router.setReorderWindow(reorderWindowFrames);
|
|
18164
|
-
}
|
|
18122
|
+
this.#transport.setFailbackWindowMs(failbackWindowMs ?? null);
|
|
18123
|
+
this.#router.setMfuTimeoutMs(mfuTimeoutMs ?? this.#hostMfuTimeoutMs);
|
|
18124
|
+
this.#router.setReorderWindow(reorderWindowFrames ?? 0);
|
|
18165
18125
|
}
|
|
18166
18126
|
/**
|
|
18167
18127
|
* Configure one track's FEC geometry, block mapper, and block-inactivity
|
|
@@ -18188,9 +18148,9 @@ var MulticastManager = class {
|
|
|
18188
18148
|
`[MulticastManager] configureTrackFec(${packetId}) catalogTrackName must be non-empty when given`
|
|
18189
18149
|
);
|
|
18190
18150
|
}
|
|
18191
|
-
if (config.fecK === void 0
|
|
18151
|
+
if (config.fecK === void 0) {
|
|
18192
18152
|
throw new Error(
|
|
18193
|
-
`[MulticastManager] configureTrackFec(${packetId}) requires fecK
|
|
18153
|
+
`[MulticastManager] configureTrackFec(${packetId}) requires fecK. Derive the per-track FEC geometry from the stream catalog.`
|
|
18194
18154
|
);
|
|
18195
18155
|
}
|
|
18196
18156
|
const blockProfile = parseFecBlockProfile(
|
|
@@ -18198,7 +18158,10 @@ var MulticastManager = class {
|
|
|
18198
18158
|
`[MulticastManager] configureTrackFec(${packetId}) fecBlockProfile`
|
|
18199
18159
|
);
|
|
18200
18160
|
assertPositiveGeometry("fecK", config.fecK);
|
|
18201
|
-
|
|
18161
|
+
const interleaveDepthMs = config.fecInterleaveDepthMs ?? 0;
|
|
18162
|
+
if (!Number.isSafeInteger(interleaveDepthMs) || interleaveDepthMs < 0) {
|
|
18163
|
+
throw new Error("[MulticastManager] fecInterleaveDepthMs must be a non-negative safe integer");
|
|
18164
|
+
}
|
|
18202
18165
|
assertPositiveGeometry("fecSymbolSize", config.fecSymbolSize);
|
|
18203
18166
|
assertPositiveGeometry("fps", config.fps);
|
|
18204
18167
|
assertPositiveGeometry("bandwidth", config.bandwidth);
|
|
@@ -18214,8 +18177,8 @@ var MulticastManager = class {
|
|
|
18214
18177
|
"fecReorderToleranceMs",
|
|
18215
18178
|
config.fecReorderToleranceMs
|
|
18216
18179
|
);
|
|
18217
|
-
const deliveryWindowMs =
|
|
18218
|
-
interleaveDepthMs
|
|
18180
|
+
const deliveryWindowMs = interleaveDepthMs >= MIN_INTERLEAVE_DEPTH_MS ? computeFecDeliveryWindowMs({
|
|
18181
|
+
interleaveDepthMs,
|
|
18219
18182
|
sourceSymbols: config.fecK,
|
|
18220
18183
|
repairSymbols: config.fecRepairSymbols,
|
|
18221
18184
|
symbolSize: config.fecSymbolSize,
|
|
@@ -18224,10 +18187,10 @@ var MulticastManager = class {
|
|
|
18224
18187
|
jitterBufferMs: config.fecReorderToleranceMs
|
|
18225
18188
|
}) : void 0;
|
|
18226
18189
|
const trackKind = config.trackKind ?? this.#router.trackKindFor(packetId);
|
|
18227
|
-
const geometry = trackKind === void 0 ? void 0 : deriveBlockClassifierGeometry({
|
|
18190
|
+
const geometry = trackKind === void 0 || interleaveDepthMs === 0 ? void 0 : deriveBlockClassifierGeometry({
|
|
18228
18191
|
trackKind,
|
|
18229
18192
|
framerate: config.fps,
|
|
18230
|
-
interleaveDepthMs
|
|
18193
|
+
interleaveDepthMs,
|
|
18231
18194
|
timescale: config.timescale,
|
|
18232
18195
|
keyframeIntervalTicks: config.keyframeIntervalTicks,
|
|
18233
18196
|
keyframeIntervalMs: config.keyframeIntervalMs
|
|
@@ -18236,7 +18199,7 @@ var MulticastManager = class {
|
|
|
18236
18199
|
blockProfile,
|
|
18237
18200
|
k: config.fecK,
|
|
18238
18201
|
symbolSize: config.fecSymbolSize,
|
|
18239
|
-
interleaveDepthMs
|
|
18202
|
+
interleaveDepthMs,
|
|
18240
18203
|
fps: config.fps,
|
|
18241
18204
|
timeoutMs: deliveryWindowMs,
|
|
18242
18205
|
...trackKind === void 0 ? {} : { trackKind },
|
|
@@ -18247,7 +18210,7 @@ var MulticastManager = class {
|
|
|
18247
18210
|
this.#configureTrackMapper(packetId, fecConfig);
|
|
18248
18211
|
this.#trackFecTiming.set(packetId, {
|
|
18249
18212
|
deliveryWindowMs,
|
|
18250
|
-
interleaveDepthMs
|
|
18213
|
+
interleaveDepthMs,
|
|
18251
18214
|
fps: config.fps,
|
|
18252
18215
|
k: config.fecK,
|
|
18253
18216
|
repairSymbols: config.fecRepairSymbols,
|
|
@@ -18256,6 +18219,10 @@ var MulticastManager = class {
|
|
|
18256
18219
|
this.#applyStreamGlobalFecTiming();
|
|
18257
18220
|
}
|
|
18258
18221
|
configureFec(config) {
|
|
18222
|
+
const interleaveDepthMs = config.fecInterleaveDepthMs ?? 0;
|
|
18223
|
+
if (!Number.isSafeInteger(interleaveDepthMs) || interleaveDepthMs < 0) {
|
|
18224
|
+
throw new Error("[MulticastManager] fecInterleaveDepthMs must be a non-negative safe integer");
|
|
18225
|
+
}
|
|
18259
18226
|
const minimumFailbackWindowMs = config.minimumFailbackWindowMs;
|
|
18260
18227
|
if (minimumFailbackWindowMs !== void 0 && (!Number.isFinite(minimumFailbackWindowMs) || minimumFailbackWindowMs <= 0)) {
|
|
18261
18228
|
throw new Error(
|
|
@@ -18267,7 +18234,7 @@ var MulticastManager = class {
|
|
|
18267
18234
|
config.fecReorderToleranceMs
|
|
18268
18235
|
);
|
|
18269
18236
|
const deliveryWindowMs = config.fecK && config.fecK > 0 && config.fecInterleaveDepthMs && config.fecInterleaveDepthMs >= MIN_INTERLEAVE_DEPTH_MS ? computeFecDeliveryWindowMs({
|
|
18270
|
-
interleaveDepthMs
|
|
18237
|
+
interleaveDepthMs,
|
|
18271
18238
|
sourceSymbols: config.fecK,
|
|
18272
18239
|
repairSymbols: config.fecRepairSymbols,
|
|
18273
18240
|
symbolSize: config.fecSymbolSize,
|
|
@@ -18278,13 +18245,13 @@ var MulticastManager = class {
|
|
|
18278
18245
|
this.#fec.configure({
|
|
18279
18246
|
k: config.fecK,
|
|
18280
18247
|
symbolSize: config.fecSymbolSize,
|
|
18281
|
-
interleaveDepthMs
|
|
18248
|
+
interleaveDepthMs,
|
|
18282
18249
|
fps: config.fps,
|
|
18283
18250
|
timeoutMs: deliveryWindowMs
|
|
18284
18251
|
});
|
|
18285
18252
|
this.#streamFecBaseline = {
|
|
18286
18253
|
deliveryWindowMs,
|
|
18287
|
-
interleaveDepthMs
|
|
18254
|
+
interleaveDepthMs,
|
|
18288
18255
|
fps: config.fps,
|
|
18289
18256
|
k: config.fecK,
|
|
18290
18257
|
repairSymbols: config.fecRepairSymbols,
|
|
@@ -18333,6 +18300,7 @@ var MulticastManager = class {
|
|
|
18333
18300
|
}
|
|
18334
18301
|
setMfuTimeoutMs(ms) {
|
|
18335
18302
|
this.#router.setMfuTimeoutMs(ms);
|
|
18303
|
+
this.#hostMfuTimeoutMs = this.#router.reassembler.timeoutMs;
|
|
18336
18304
|
}
|
|
18337
18305
|
setTrackCodec(packetId, codec) {
|
|
18338
18306
|
this.#router.setTrackCodec(packetId, codec);
|
|
@@ -18355,6 +18323,7 @@ var MulticastManager = class {
|
|
|
18355
18323
|
onTrackPacket(packetId, cb) {
|
|
18356
18324
|
this.#router.onTrackPacket(packetId, cb);
|
|
18357
18325
|
}
|
|
18326
|
+
/** @deprecated Use onTrackInit(packetId, cb); see MmtpRouter.onInitSegment. */
|
|
18358
18327
|
onInitSegment(cb) {
|
|
18359
18328
|
this.#router.onInitSegment(cb);
|
|
18360
18329
|
}
|
|
@@ -18427,7 +18396,7 @@ var MulticastManager = class {
|
|
|
18427
18396
|
)
|
|
18428
18397
|
)
|
|
18429
18398
|
};
|
|
18430
|
-
const amtRelay = config.amtRelay
|
|
18399
|
+
const amtRelay = config.amtRelay;
|
|
18431
18400
|
const transportConfig = {
|
|
18432
18401
|
sourceAddress: config.sourceAddress,
|
|
18433
18402
|
groupAddress: config.groupAddress,
|
|
@@ -18440,9 +18409,8 @@ var MulticastManager = class {
|
|
|
18440
18409
|
// endpoint-config.ts) deliberately has no implicit relay port — it throws
|
|
18441
18410
|
// and `MulticastTransport.join` resolves false — so the default is
|
|
18442
18411
|
// resolved HERE and arrives there explicit, exactly as the two catalog
|
|
18443
|
-
// readers do it. This is the last of the three readers:
|
|
18444
|
-
//
|
|
18445
|
-
// port field), and `resolveNetworkSource` emits the same port-less shape.
|
|
18412
|
+
// readers do it. This is the last of the three readers: `resolveNetworkSource`
|
|
18413
|
+
// emits a port-less relay shape.
|
|
18446
18414
|
// Keyed on the resolved relay, so a DRIAD entry — which pins no relay —
|
|
18447
18415
|
// never has a port manufactured for it. Blank counts as no relay, the
|
|
18448
18416
|
// same test `catalogAmtNetworkSource` applies (it trims, then skips) and
|
|
@@ -18666,7 +18634,10 @@ var MulticastManager = class {
|
|
|
18666
18634
|
console.warn("[MulticastManager] rendition switch onCancel failed", error);
|
|
18667
18635
|
}
|
|
18668
18636
|
}
|
|
18669
|
-
/**
|
|
18637
|
+
/**
|
|
18638
|
+
* Release what the manager and FEC hold for a stream whose packet ID the
|
|
18639
|
+
* router retired, or whose FEC a catalog reconfiguration no longer configures.
|
|
18640
|
+
*/
|
|
18670
18641
|
#releaseRetiredStream(stream) {
|
|
18671
18642
|
for (const observer of this.#trackResetObservers) {
|
|
18672
18643
|
try {
|
|
@@ -18685,7 +18656,10 @@ var MulticastManager = class {
|
|
|
18685
18656
|
for (const [key, stats] of [...this.#blockPolicyStats]) {
|
|
18686
18657
|
if (stats.packetId === stream.packetId) this.#blockPolicyStats.delete(key);
|
|
18687
18658
|
}
|
|
18688
|
-
this.#trackFecTiming.delete(stream.packetId)
|
|
18659
|
+
if (this.#trackFecTiming.delete(stream.packetId)) {
|
|
18660
|
+
this.#applyStreamGlobalFecTiming();
|
|
18661
|
+
}
|
|
18662
|
+
this.#multiTrackFecTracks.delete(stream.packetId);
|
|
18689
18663
|
this.#fec.releasePacket(stream.packetId);
|
|
18690
18664
|
}
|
|
18691
18665
|
/** Feed a raw packet from an external source (AMT tunnel, ASM subscription).
|
|
@@ -18730,12 +18704,53 @@ var MulticastManager = class {
|
|
|
18730
18704
|
* D-17: Builds altGroup → track-name index for getSwitchingSet().
|
|
18731
18705
|
* D-26: Registers packetId → TrackRole on MmtpRouter for block-complete fanout.
|
|
18732
18706
|
*
|
|
18707
|
+
* FEC availability is per track (draft-ramadan-moq-fec §5.1). A media track
|
|
18708
|
+
* whose `fec` names no implemented `blockProfile` (only `iso-ssbg1-v1` is
|
|
18709
|
+
* implemented; the retired `k-padded-v1` is not), or an `iso-ssbg1-v1` span
|
|
18710
|
+
* it cannot use, is consumed without FEC: it gets no decoder, and none of
|
|
18711
|
+
* its repair tracks (layer 0 by `fec.repairTrack`, further layers and the
|
|
18712
|
+
* overlay by `depends`) is given an owner, so the router drops their packets
|
|
18713
|
+
* before any decoder. Those legs keep the `repair` role and their owner's
|
|
18714
|
+
* switching set. Neither the call nor any other track's FEC fails on that
|
|
18715
|
+
* account; `trackFecUnavailable(name)` reports the reason. FEC a previous
|
|
18716
|
+
* call configured on a packetId this call does not is released
|
|
18717
|
+
* (FECManager.releasePacket); frames its release gate held are delivered in
|
|
18718
|
+
* order, each gap stalled. FEC wired by hand (setFecDecoder) is left alone.
|
|
18719
|
+
* Geometry a decoder could not configure (K, P, T, `repairTrack`,
|
|
18720
|
+
* algorithm) still throws.
|
|
18721
|
+
*
|
|
18733
18722
|
* @param catalog - Multi-track catalog (tracks array with name, altGroup, fec, packetId).
|
|
18734
18723
|
* @param wasmDecoderSource - One WASM FEC decoder for a single FEC stream, or
|
|
18735
18724
|
* a factory returning a distinct decoder per stream.
|
|
18736
18725
|
*/
|
|
18737
18726
|
configureMultiTrack(catalog, wasmDecoderSource) {
|
|
18738
18727
|
const repairNamesBySource = repairTrackNamesBySource(catalog.tracks);
|
|
18728
|
+
const declaredRepairNames = /* @__PURE__ */ new Set();
|
|
18729
|
+
for (const names of repairNamesBySource.values()) {
|
|
18730
|
+
for (const name of names) declaredRepairNames.add(name);
|
|
18731
|
+
}
|
|
18732
|
+
const fecUnavailableByTrack = /* @__PURE__ */ new Map();
|
|
18733
|
+
for (let i = 0; i < catalog.tracks.length; i++) {
|
|
18734
|
+
const track = catalog.tracks[i];
|
|
18735
|
+
if (track.fec === void 0 || track.packaging === "fec-repair" || declaredRepairNames.has(track.name)) {
|
|
18736
|
+
continue;
|
|
18737
|
+
}
|
|
18738
|
+
const fecUnavailable = catalogFecUnavailable(track.fec, `$.tracks[${i}].fec`);
|
|
18739
|
+
if (fecUnavailable === void 0) continue;
|
|
18740
|
+
fecUnavailableByTrack.set(track.name, fecUnavailable);
|
|
18741
|
+
const layerZero = track.fec.repairTrack;
|
|
18742
|
+
if (typeof layerZero === "string" && layerZero.length > 0) {
|
|
18743
|
+
const names = repairNamesBySource.get(track.name) ?? /* @__PURE__ */ new Set();
|
|
18744
|
+
names.add(layerZero);
|
|
18745
|
+
repairNamesBySource.set(track.name, names);
|
|
18746
|
+
}
|
|
18747
|
+
}
|
|
18748
|
+
const unownedRepairNames = /* @__PURE__ */ new Set();
|
|
18749
|
+
for (const sourceName of fecUnavailableByTrack.keys()) {
|
|
18750
|
+
for (const repairName of repairNamesBySource.get(sourceName) ?? []) {
|
|
18751
|
+
unownedRepairNames.add(repairName);
|
|
18752
|
+
}
|
|
18753
|
+
}
|
|
18739
18754
|
const repairOwnerGroup = /* @__PURE__ */ new Map();
|
|
18740
18755
|
const repairOwnerAltGroup = /* @__PURE__ */ new Map();
|
|
18741
18756
|
for (const track of catalog.tracks) {
|
|
@@ -18788,11 +18803,13 @@ var MulticastManager = class {
|
|
|
18788
18803
|
for (let i = 0; i < catalog.tracks.length; i++) {
|
|
18789
18804
|
const track = catalog.tracks[i];
|
|
18790
18805
|
if (resolvedRoles[i] === "repair" || track.fec === void 0) continue;
|
|
18791
|
-
|
|
18806
|
+
const unavailable = fecUnavailableByTrack.has(track.name);
|
|
18807
|
+
if (!(unavailable ? isFecCatalogGeometry(track.fec) : isFecCatalogConfig(track.fec))) {
|
|
18792
18808
|
throw new Error(
|
|
18793
18809
|
`[MulticastManager] invalid fec config for track '${track.name}'`
|
|
18794
18810
|
);
|
|
18795
18811
|
}
|
|
18812
|
+
if (unavailable) continue;
|
|
18796
18813
|
if (track.packetId !== void 0) fecTrackIndexes.push(i);
|
|
18797
18814
|
}
|
|
18798
18815
|
const fecDecoders = /* @__PURE__ */ new Map();
|
|
@@ -18821,6 +18838,20 @@ var MulticastManager = class {
|
|
|
18821
18838
|
this.#switchingSets.clear();
|
|
18822
18839
|
this.#repairPacketIds.clear();
|
|
18823
18840
|
this.#router.clearRepairPacketIdOwners();
|
|
18841
|
+
const fecPacketIds = new Set([...fecDecoders.keys()].map((i) => catalog.tracks[i].packetId));
|
|
18842
|
+
for (const [packetId, catalogTrackName] of this.#multiTrackFecTracks) {
|
|
18843
|
+
if (fecPacketIds.has(packetId)) continue;
|
|
18844
|
+
if (!this.#fec.isTrackConfigured(packetId)) {
|
|
18845
|
+
this.#multiTrackFecTracks.delete(packetId);
|
|
18846
|
+
continue;
|
|
18847
|
+
}
|
|
18848
|
+
this.#router.releaseFecPacket(packetId);
|
|
18849
|
+
this.#releaseRetiredStream({ packetId, catalogTrackName });
|
|
18850
|
+
}
|
|
18851
|
+
this.#fecUnavailableByTrack.clear();
|
|
18852
|
+
for (const [name, reason] of fecUnavailableByTrack) {
|
|
18853
|
+
this.#fecUnavailableByTrack.set(name, reason);
|
|
18854
|
+
}
|
|
18824
18855
|
const catalogTracksByName = new Map(
|
|
18825
18856
|
catalog.tracks.map((track) => [track.name, track])
|
|
18826
18857
|
);
|
|
@@ -18842,10 +18873,11 @@ var MulticastManager = class {
|
|
|
18842
18873
|
}
|
|
18843
18874
|
continue;
|
|
18844
18875
|
}
|
|
18845
|
-
if (track.fec !== void 0) {
|
|
18876
|
+
if (track.fec !== void 0 && !fecUnavailableByTrack.has(track.name)) {
|
|
18846
18877
|
const fecCfg = track.fec;
|
|
18847
18878
|
if (track.packetId !== void 0) {
|
|
18848
18879
|
for (const repairName of repairNamesBySource.get(track.name) ?? []) {
|
|
18880
|
+
if (unownedRepairNames.has(repairName)) continue;
|
|
18849
18881
|
const repairTrack = catalogTracksByName.get(repairName);
|
|
18850
18882
|
if (repairTrack?.packetId !== void 0) {
|
|
18851
18883
|
this.#router.setRepairPacketIdOwner(repairTrack.packetId, track.packetId);
|
|
@@ -18866,17 +18898,18 @@ var MulticastManager = class {
|
|
|
18866
18898
|
blockProfile,
|
|
18867
18899
|
k: fecCfg.sourceSymbols,
|
|
18868
18900
|
symbolSize: fecCfg.symbolSize,
|
|
18869
|
-
interleaveDepthMs: fecCfg.interleaveDepthMs,
|
|
18901
|
+
interleaveDepthMs: fecCfg.interleaveDepthMs ?? 0,
|
|
18870
18902
|
fps: track.framerate,
|
|
18871
18903
|
trackKind: "video"
|
|
18872
18904
|
},
|
|
18873
18905
|
track.name
|
|
18874
18906
|
);
|
|
18907
|
+
this.#multiTrackFecTracks.set(track.packetId, track.name);
|
|
18875
18908
|
this.#configureTrackMapper(track.packetId, {
|
|
18876
18909
|
blockProfile,
|
|
18877
18910
|
k: fecCfg.sourceSymbols,
|
|
18878
18911
|
symbolSize: fecCfg.symbolSize,
|
|
18879
|
-
interleaveDepthMs: fecCfg.interleaveDepthMs,
|
|
18912
|
+
interleaveDepthMs: fecCfg.interleaveDepthMs ?? 0,
|
|
18880
18913
|
fps: track.framerate
|
|
18881
18914
|
});
|
|
18882
18915
|
this.#router.setPacketIdType(track.packetId, "video");
|
|
@@ -18884,6 +18917,24 @@ var MulticastManager = class {
|
|
|
18884
18917
|
}
|
|
18885
18918
|
}
|
|
18886
18919
|
}
|
|
18920
|
+
/**
|
|
18921
|
+
* Why `configureMultiTrack` consumes the named catalog track without FEC
|
|
18922
|
+
* although its `fec` block declares one (draft-ramadan-moq-fec §5.1): no
|
|
18923
|
+
* implemented `blockProfile` (`track_fec_block_profile_unimplemented`, e.g.
|
|
18924
|
+
* the retired `k-padded-v1`; `track_fec_block_profile` when absent), or an
|
|
18925
|
+
* invalid `iso-ssbg1-v1` span (`track_fec`). Absent/zero spans keep in-band
|
|
18926
|
+
* decoding enabled. The same reason the hang
|
|
18927
|
+
* resolver reports (`catalogFecUnavailable`). Undefined for every other
|
|
18928
|
+
* track, and for every track before the first call; each call replaces the
|
|
18929
|
+
* previous call's answers.
|
|
18930
|
+
*/
|
|
18931
|
+
trackFecUnavailable(trackName) {
|
|
18932
|
+
return this.#fecUnavailableByTrack.get(trackName);
|
|
18933
|
+
}
|
|
18934
|
+
/** Per-track FEC availability from the last `configureMultiTrack` call. */
|
|
18935
|
+
#fecUnavailableByTrack = /* @__PURE__ */ new Map();
|
|
18936
|
+
/** packetId → catalog track name of the FEC the last `configureMultiTrack` installed. */
|
|
18937
|
+
#multiTrackFecTracks = /* @__PURE__ */ new Map();
|
|
18887
18938
|
/**
|
|
18888
18939
|
* Return the track names belonging to the given altGroup switching set.
|
|
18889
18940
|
* D-17: altGroup groups base+delta+repair into one switching set.
|