@blockcast/mmt-container 0.1.0 → 0.3.0-main.ef68de90edb3
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 +524 -0
- package/dist/.build-stamp +0 -0
- package/dist/__tests__/test-utils.d.ts +71 -0
- package/dist/__tests__/test-utils.d.ts.map +1 -0
- package/dist/align.d.ts +152 -0
- package/dist/align.d.ts.map +1 -0
- package/dist/align.js +88 -0
- package/dist/align.js.map +7 -0
- package/dist/audio-init-parser.d.ts +66 -0
- package/dist/audio-init-parser.d.ts.map +1 -0
- package/dist/audio-init-parser.js +118 -0
- package/dist/audio-init-parser.js.map +7 -0
- package/dist/audio-ring-buffer.d.ts +50 -0
- package/dist/audio-ring-buffer.d.ts.map +1 -0
- package/dist/audio-ring-buffer.js +80 -0
- package/dist/audio-ring-buffer.js.map +7 -0
- package/dist/authored-timing.d.ts +32 -0
- package/dist/authored-timing.d.ts.map +1 -0
- package/dist/authored-timing.js +32 -0
- package/dist/authored-timing.js.map +7 -0
- package/dist/block-policy.d.ts +314 -0
- package/dist/block-policy.d.ts.map +1 -0
- package/dist/block-policy.js +466 -0
- package/dist/block-policy.js.map +7 -0
- package/dist/browser-runtime.js +26549 -0
- package/dist/browser-runtime.js.map +7 -0
- package/dist/carp-catalog.d.ts +429 -0
- package/dist/carp-catalog.d.ts.map +1 -0
- package/dist/carp-catalog.js +985 -0
- package/dist/carp-catalog.js.map +7 -0
- package/dist/catalog-fec-params.d.ts +275 -0
- package/dist/catalog-fec-params.d.ts.map +1 -0
- package/dist/catalog-fec-params.js +702 -0
- package/dist/catalog-fec-params.js.map +7 -0
- package/dist/catalog-network-source.d.ts +42 -0
- package/dist/catalog-network-source.d.ts.map +1 -0
- package/dist/catalog-network-source.js +47 -0
- package/dist/catalog-network-source.js.map +7 -0
- package/dist/catalog-validator.d.ts +18 -0
- package/dist/catalog-validator.d.ts.map +1 -0
- package/dist/catalog-validator.js +7747 -0
- package/dist/catalog-validator.js.map +7 -0
- package/dist/cmaf-block-assembler.d.ts +285 -0
- package/dist/cmaf-block-assembler.d.ts.map +1 -0
- package/dist/cmaf-block-assembler.js +1966 -0
- package/dist/cmaf-block-assembler.js.map +7 -0
- package/dist/cmaf-group-batcher.d.ts +38 -0
- package/dist/cmaf-group-batcher.d.ts.map +1 -0
- package/dist/cmaf-group-batcher.js +176 -0
- package/dist/cmaf-group-batcher.js.map +7 -0
- package/dist/cmcd-producer.d.ts +104 -0
- package/dist/cmcd-producer.d.ts.map +1 -0
- package/dist/cmcd-producer.js +183 -0
- package/dist/cmcd-producer.js.map +7 -0
- package/dist/codec-utils.d.ts +384 -0
- package/dist/codec-utils.d.ts.map +1 -0
- package/dist/codec-utils.js +1546 -0
- package/dist/codec-utils.js.map +7 -0
- package/dist/conceal-timeline.d.ts +146 -0
- package/dist/conceal-timeline.d.ts.map +1 -0
- package/dist/conceal-timeline.js +137 -0
- package/dist/conceal-timeline.js.map +7 -0
- package/dist/delivery-gap.d.ts +75 -0
- package/dist/delivery-gap.d.ts.map +1 -0
- package/dist/delivery-gap.js +88 -0
- package/dist/delivery-gap.js.map +7 -0
- package/dist/duplicate-ledger.d.ts +197 -0
- package/dist/duplicate-ledger.d.ts.map +1 -0
- package/dist/duplicate-ledger.js +226 -0
- package/dist/duplicate-ledger.js.map +7 -0
- package/dist/fec-codec.d.ts +32 -0
- package/dist/fec-codec.d.ts.map +1 -0
- package/dist/fec-codec.js +47 -0
- package/dist/fec-codec.js.map +7 -0
- package/dist/fec-manager.d.ts +583 -0
- package/dist/fec-manager.d.ts.map +1 -0
- package/dist/fec-manager.js +3899 -0
- package/dist/fec-manager.js.map +7 -0
- package/dist/fec-starvation-detector.d.ts +59 -0
- package/dist/fec-starvation-detector.d.ts.map +1 -0
- package/dist/fec-starvation-detector.js +60 -0
- package/dist/fec-starvation-detector.js.map +7 -0
- package/dist/fec-state.d.ts +131 -0
- package/dist/fec-state.d.ts.map +1 -0
- package/dist/fec-state.js +87 -0
- package/dist/fec-state.js.map +7 -0
- package/dist/fec-worker-setup.d.ts +94 -0
- package/dist/fec-worker-setup.d.ts.map +1 -0
- package/dist/fec-worker-setup.js +673 -0
- package/dist/fec-worker-setup.js.map +7 -0
- package/dist/fmp4-completion.d.ts +30 -0
- package/dist/fmp4-completion.d.ts.map +1 -0
- package/dist/fmp4-completion.js +21 -0
- package/dist/fmp4-completion.js.map +7 -0
- package/dist/fmp4-reader.d.ts +37 -0
- package/dist/fmp4-reader.d.ts.map +1 -0
- package/dist/fmp4-reader.js +100 -0
- package/dist/fmp4-reader.js.map +7 -0
- package/dist/index.d.ts +44 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +460 -14
- package/dist/index.js.map +7 -1
- package/dist/iso-block-locator.d.ts +118 -0
- package/dist/iso-block-locator.d.ts.map +1 -0
- package/dist/iso-block-locator.js +289 -0
- package/dist/iso-block-locator.js.map +7 -0
- package/dist/iso-bmff-timing.d.ts +78 -0
- package/dist/iso-bmff-timing.d.ts.map +1 -0
- package/dist/iso-bmff-timing.js +593 -0
- package/dist/iso-bmff-timing.js.map +7 -0
- package/dist/loc.d.ts +70 -75
- package/dist/loc.d.ts.map +1 -1
- package/dist/loc.js +106 -192
- package/dist/loc.js.map +7 -1
- package/dist/manifest-auth-plan.d.ts +30 -0
- package/dist/manifest-auth-plan.d.ts.map +1 -0
- package/dist/manifest-auth-plan.js +1266 -0
- package/dist/manifest-auth-plan.js.map +7 -0
- package/dist/media-router.d.ts +262 -0
- package/dist/media-router.d.ts.map +1 -0
- package/dist/media-router.js +584 -0
- package/dist/media-router.js.map +7 -0
- package/dist/mfu-reassembler.d.ts +313 -9
- package/dist/mfu-reassembler.d.ts.map +1 -1
- package/dist/mfu-reassembler.js +3051 -285
- package/dist/mfu-reassembler.js.map +7 -1
- package/dist/mfu-reassembly-policy.d.ts +24 -0
- package/dist/mfu-reassembly-policy.d.ts.map +1 -0
- package/dist/mfu-reassembly-policy.js +99 -0
- package/dist/mfu-reassembly-policy.js.map +7 -0
- package/dist/mmtp-container-parser.d.ts +568 -0
- package/dist/mmtp-container-parser.d.ts.map +1 -0
- package/dist/mmtp-container-parser.js +6580 -0
- package/dist/mmtp-container-parser.js.map +7 -0
- package/dist/mmtp-container-parser.no-modules.js +9616 -0
- package/dist/mmtp-container-parser.no-modules.js.map +7 -0
- package/dist/mmtp-decode-pipeline.d.ts +138 -0
- package/dist/mmtp-decode-pipeline.d.ts.map +1 -0
- package/dist/mmtp-decode-pipeline.js +6814 -0
- package/dist/mmtp-decode-pipeline.js.map +7 -0
- package/dist/mmtp-router.d.ts +679 -0
- package/dist/mmtp-router.d.ts.map +1 -0
- package/dist/mmtp-router.js +11278 -0
- package/dist/mmtp-router.js.map +7 -0
- package/dist/mmtp-to-cmaf.d.ts +187 -0
- package/dist/mmtp-to-cmaf.d.ts.map +1 -0
- package/dist/mmtp-to-cmaf.js +716 -0
- package/dist/mmtp-to-cmaf.js.map +7 -0
- package/dist/multicast-auth-catalog.d.ts +115 -0
- package/dist/multicast-auth-catalog.d.ts.map +1 -0
- package/dist/multicast-auth-catalog.js +202 -0
- package/dist/multicast-auth-catalog.js.map +7 -0
- package/dist/multicast-input.d.ts +141 -0
- package/dist/multicast-input.d.ts.map +1 -0
- package/dist/multicast-input.js +390 -0
- package/dist/multicast-input.js.map +7 -0
- package/dist/multicast-manager.d.ts +669 -0
- package/dist/multicast-manager.d.ts.map +1 -0
- package/dist/multicast-manager.js +19101 -0
- package/dist/multicast-manager.js.map +7 -0
- package/dist/multicast-transport.d.ts +271 -0
- package/dist/multicast-transport.d.ts.map +1 -0
- package/dist/multicast-transport.js +2264 -0
- package/dist/multicast-transport.js.map +7 -0
- package/dist/multicast-unwrap.d.ts +57 -0
- package/dist/multicast-unwrap.d.ts.map +1 -0
- package/dist/multicast-unwrap.js +418 -0
- package/dist/multicast-unwrap.js.map +7 -0
- package/dist/no-modules.d.ts +10 -0
- package/dist/no-modules.d.ts.map +1 -0
- package/dist/no-modules.js +9491 -0
- package/dist/no-modules.js.map +7 -0
- package/dist/phase14-catalog-override.d.ts +126 -0
- package/dist/phase14-catalog-override.d.ts.map +1 -0
- package/dist/phase14-catalog-override.js +155 -0
- package/dist/phase14-catalog-override.js.map +7 -0
- package/dist/repair-fetch.d.ts +270 -0
- package/dist/repair-fetch.d.ts.map +1 -0
- package/dist/repair-fetch.js +381 -0
- package/dist/repair-fetch.js.map +7 -0
- package/dist/repair-layers.d.ts +158 -0
- package/dist/repair-layers.d.ts.map +1 -0
- package/dist/repair-layers.js +344 -0
- package/dist/repair-layers.js.map +7 -0
- package/dist/sample-combiner.d.ts +212 -0
- package/dist/sample-combiner.d.ts.map +1 -0
- package/dist/sample-combiner.js +595 -0
- package/dist/sample-combiner.js.map +7 -0
- package/dist/signaling.d.ts +29 -0
- package/dist/signaling.d.ts.map +1 -0
- package/dist/signaling.js +572 -0
- package/dist/signaling.js.map +7 -0
- package/dist/source-auth.d.ts +2 -0
- package/dist/source-auth.d.ts.map +1 -0
- package/dist/source-auth.js +1209 -0
- package/dist/source-auth.js.map +7 -0
- package/dist/switching-set-alignment.d.ts +61 -0
- package/dist/switching-set-alignment.d.ts.map +1 -0
- package/dist/switching-set-alignment.js +70 -0
- package/dist/switching-set-alignment.js.map +7 -0
- package/dist/timestamp-codec.d.ts +34 -0
- package/dist/timestamp-codec.d.ts.map +1 -0
- package/dist/timestamp-codec.js +473 -0
- package/dist/timestamp-codec.js.map +7 -0
- package/dist/track-pipeline-setup.d.ts +83 -0
- package/dist/track-pipeline-setup.d.ts.map +1 -0
- package/dist/track-pipeline-setup.js +291 -0
- package/dist/track-pipeline-setup.js.map +7 -0
- package/dist/track-pipeline.d.ts +121 -0
- package/dist/track-pipeline.d.ts.map +1 -0
- package/dist/track-pipeline.js +209 -0
- package/dist/track-pipeline.js.map +7 -0
- package/dist/track-session.d.ts +414 -0
- package/dist/track-session.d.ts.map +1 -0
- package/dist/track-session.js +754 -0
- package/dist/track-session.js.map +7 -0
- package/dist/transport-pool.d.ts +69 -0
- package/dist/transport-pool.d.ts.map +1 -0
- package/dist/transport-pool.js +74 -0
- package/dist/transport-pool.js.map +7 -0
- package/dist/wasm-mfu-reassembler.d.ts +97 -0
- package/dist/wasm-mfu-reassembler.d.ts.map +1 -0
- package/dist/wasm-mfu-reassembler.js +2133 -0
- package/dist/wasm-mfu-reassembler.js.map +7 -0
- package/dist/wasm-mmtp-container-parser.d.ts +10 -0
- package/dist/wasm-mmtp-container-parser.d.ts.map +1 -0
- package/dist/wasm-mmtp-container-parser.js +6834 -0
- package/dist/wasm-mmtp-container-parser.js.map +7 -0
- package/node_modules/@blockcast/fec-worker/README.md +223 -0
- package/node_modules/@blockcast/fec-worker/dist/.build-stamp +0 -0
- package/node_modules/@blockcast/fec-worker/dist/block-order.d.ts +38 -0
- package/node_modules/@blockcast/fec-worker/dist/block-order.d.ts.map +1 -0
- package/node_modules/@blockcast/fec-worker/dist/block-order.js +78 -0
- package/node_modules/@blockcast/fec-worker/dist/block-order.js.map +1 -0
- package/node_modules/@blockcast/fec-worker/dist/fec-worker-client.d.ts +93 -0
- package/node_modules/@blockcast/fec-worker/dist/fec-worker-client.d.ts.map +1 -0
- package/node_modules/@blockcast/fec-worker/dist/fec-worker-client.js +346 -0
- package/node_modules/@blockcast/fec-worker/dist/fec-worker-client.js.map +1 -0
- package/node_modules/@blockcast/fec-worker/dist/fec-worker-types.d.ts +380 -0
- package/node_modules/@blockcast/fec-worker/dist/fec-worker-types.d.ts.map +1 -0
- package/node_modules/@blockcast/fec-worker/dist/fec-worker-types.js +11 -0
- package/node_modules/@blockcast/fec-worker/dist/fec-worker-types.js.map +1 -0
- package/node_modules/@blockcast/fec-worker/dist/fec-worker.d.ts +14 -0
- package/node_modules/@blockcast/fec-worker/dist/fec-worker.d.ts.map +1 -0
- package/node_modules/@blockcast/fec-worker/dist/fec-worker.js +859 -0
- package/node_modules/@blockcast/fec-worker/dist/fec-worker.js.map +7 -0
- package/node_modules/@blockcast/fec-worker/dist/index.d.ts +10 -0
- package/node_modules/@blockcast/fec-worker/dist/index.d.ts.map +1 -0
- package/node_modules/@blockcast/fec-worker/dist/index.js +8 -0
- package/node_modules/@blockcast/fec-worker/dist/index.js.map +1 -0
- package/node_modules/@blockcast/fec-worker/dist/shred-fec-worker.d.ts +20 -0
- package/node_modules/@blockcast/fec-worker/dist/shred-fec-worker.d.ts.map +1 -0
- package/node_modules/@blockcast/fec-worker/dist/shred-fec-worker.js +370 -0
- package/node_modules/@blockcast/fec-worker/dist/shred-fec-worker.js.map +7 -0
- package/node_modules/@blockcast/fec-worker/dist/shred-worker-client.d.ts +104 -0
- package/node_modules/@blockcast/fec-worker/dist/shred-worker-client.d.ts.map +1 -0
- package/node_modules/@blockcast/fec-worker/dist/shred-worker-client.js +181 -0
- package/node_modules/@blockcast/fec-worker/dist/shred-worker-client.js.map +1 -0
- package/node_modules/@blockcast/fec-worker/dist/shred-worker-types.d.ts +194 -0
- package/node_modules/@blockcast/fec-worker/dist/shred-worker-types.d.ts.map +1 -0
- package/node_modules/@blockcast/fec-worker/dist/shred-worker-types.js +62 -0
- package/node_modules/@blockcast/fec-worker/dist/shred-worker-types.js.map +1 -0
- package/node_modules/@blockcast/fec-worker/dist/transfer.d.ts +9 -0
- package/node_modules/@blockcast/fec-worker/dist/transfer.d.ts.map +1 -0
- package/node_modules/@blockcast/fec-worker/dist/transfer.js +13 -0
- package/node_modules/@blockcast/fec-worker/dist/transfer.js.map +1 -0
- package/node_modules/@blockcast/fec-worker/package.json +59 -0
- package/node_modules/@blockcast/fec-worker/src/block-order.ts +93 -0
- package/node_modules/@blockcast/fec-worker/src/fec-worker-client.ts +392 -0
- package/node_modules/@blockcast/fec-worker/src/fec-worker-types.ts +399 -0
- package/node_modules/@blockcast/fec-worker/src/fec-worker.ts +1502 -0
- package/node_modules/@blockcast/fec-worker/src/index.ts +35 -0
- package/node_modules/@blockcast/fec-worker/src/shred-fec-worker.ts +589 -0
- package/node_modules/@blockcast/fec-worker/src/shred-worker-client.ts +222 -0
- package/node_modules/@blockcast/fec-worker/src/shred-worker-types.ts +209 -0
- package/node_modules/@blockcast/fec-worker/src/transfer.ts +12 -0
- package/node_modules/@blockcast/mmt-alta-parse/README.md +181 -0
- package/node_modules/@blockcast/mmt-alta-parse/dist/.build-stamp +0 -0
- package/node_modules/@blockcast/mmt-alta-parse/dist/index.d.ts +57 -0
- package/node_modules/@blockcast/mmt-alta-parse/dist/index.d.ts.map +1 -0
- package/node_modules/@blockcast/mmt-alta-parse/dist/index.js +115 -0
- package/node_modules/@blockcast/mmt-alta-parse/dist/index.js.map +1 -0
- package/node_modules/@blockcast/mmt-alta-parse/package.json +50 -0
- package/node_modules/@blockcast/mmt-alta-parse/src/index.ts +136 -0
- package/node_modules/@blockcast/mmt-base/README.md +134 -0
- package/node_modules/@blockcast/mmt-base/dist/.build-stamp +0 -0
- package/node_modules/@blockcast/mmt-base/dist/fec-block-profile.d.ts +55 -0
- package/node_modules/@blockcast/mmt-base/dist/fec-block-profile.d.ts.map +1 -0
- package/node_modules/@blockcast/mmt-base/dist/fec-block-profile.js +128 -0
- package/node_modules/@blockcast/mmt-base/dist/fec-block-profile.js.map +1 -0
- package/node_modules/@blockcast/mmt-base/dist/index.d.ts +7 -0
- package/node_modules/@blockcast/mmt-base/dist/index.d.ts.map +1 -0
- package/node_modules/@blockcast/mmt-base/dist/index.js +7 -0
- package/node_modules/@blockcast/mmt-base/dist/index.js.map +1 -0
- package/node_modules/@blockcast/mmt-base/dist/mmtp.d.ts +292 -0
- package/node_modules/@blockcast/mmt-base/dist/mmtp.d.ts.map +1 -0
- package/node_modules/@blockcast/mmt-base/dist/mmtp.js +612 -0
- package/node_modules/@blockcast/mmt-base/dist/mmtp.js.map +1 -0
- package/node_modules/@blockcast/mmt-base/dist/net-constants.d.ts +18 -0
- package/node_modules/@blockcast/mmt-base/dist/net-constants.d.ts.map +1 -0
- package/node_modules/@blockcast/mmt-base/dist/net-constants.js +18 -0
- package/node_modules/@blockcast/mmt-base/dist/net-constants.js.map +1 -0
- package/node_modules/@blockcast/mmt-base/dist/ntp-short.d.ts +47 -0
- package/node_modules/@blockcast/mmt-base/dist/ntp-short.d.ts.map +1 -0
- package/node_modules/@blockcast/mmt-base/dist/ntp-short.js +54 -0
- package/node_modules/@blockcast/mmt-base/dist/ntp-short.js.map +1 -0
- package/node_modules/@blockcast/mmt-base/dist/serial-number.d.ts +57 -0
- package/node_modules/@blockcast/mmt-base/dist/serial-number.d.ts.map +1 -0
- package/node_modules/@blockcast/mmt-base/dist/serial-number.js +98 -0
- package/node_modules/@blockcast/mmt-base/dist/serial-number.js.map +1 -0
- package/node_modules/@blockcast/mmt-base/dist/signalling.d.ts +312 -0
- package/node_modules/@blockcast/mmt-base/dist/signalling.d.ts.map +1 -0
- package/node_modules/@blockcast/mmt-base/dist/signalling.js +637 -0
- package/node_modules/@blockcast/mmt-base/dist/signalling.js.map +1 -0
- package/node_modules/@blockcast/mmt-base/package.json +49 -0
- package/node_modules/@blockcast/mmt-base/src/fec-block-profile.ts +145 -0
- package/node_modules/@blockcast/mmt-base/src/index.ts +6 -0
- package/node_modules/@blockcast/mmt-base/src/mmtp.ts +892 -0
- package/node_modules/@blockcast/mmt-base/src/net-constants.ts +17 -0
- package/node_modules/@blockcast/mmt-base/src/ntp-short.ts +67 -0
- package/node_modules/@blockcast/mmt-base/src/serial-number.ts +107 -0
- package/node_modules/@blockcast/mmt-base/src/signalling.ts +862 -0
- package/node_modules/@blockcast/mmt-fec/README.md +271 -0
- package/node_modules/@blockcast/mmt-fec/dist/.build-stamp +0 -0
- package/node_modules/@blockcast/mmt-fec/dist/block-manager.d.ts +82 -0
- package/node_modules/@blockcast/mmt-fec/dist/block-manager.d.ts.map +1 -0
- package/node_modules/@blockcast/mmt-fec/dist/block-manager.js +296 -0
- package/node_modules/@blockcast/mmt-fec/dist/block-manager.js.map +1 -0
- package/node_modules/@blockcast/mmt-fec/dist/block-processor.d.ts +106 -0
- package/node_modules/@blockcast/mmt-fec/dist/block-processor.d.ts.map +1 -0
- package/node_modules/@blockcast/mmt-fec/dist/block-processor.js +288 -0
- package/node_modules/@blockcast/mmt-fec/dist/block-processor.js.map +1 -0
- package/node_modules/@blockcast/mmt-fec/dist/cmaf-recovery.d.ts +46 -0
- package/node_modules/@blockcast/mmt-fec/dist/cmaf-recovery.d.ts.map +1 -0
- package/node_modules/@blockcast/mmt-fec/dist/cmaf-recovery.js +96 -0
- package/node_modules/@blockcast/mmt-fec/dist/cmaf-recovery.js.map +1 -0
- package/node_modules/@blockcast/mmt-fec/dist/cmaf-symbols.d.ts +33 -0
- package/node_modules/@blockcast/mmt-fec/dist/cmaf-symbols.d.ts.map +1 -0
- package/node_modules/@blockcast/mmt-fec/dist/cmaf-symbols.js +57 -0
- package/node_modules/@blockcast/mmt-fec/dist/cmaf-symbols.js.map +1 -0
- package/node_modules/@blockcast/mmt-fec/dist/fec-block-mapper.d.ts +205 -0
- package/node_modules/@blockcast/mmt-fec/dist/fec-block-mapper.d.ts.map +1 -0
- package/node_modules/@blockcast/mmt-fec/dist/fec-block-mapper.js +240 -0
- package/node_modules/@blockcast/mmt-fec/dist/fec-block-mapper.js.map +1 -0
- package/node_modules/@blockcast/mmt-fec/dist/index.d.ts +28 -0
- package/node_modules/@blockcast/mmt-fec/dist/index.d.ts.map +1 -0
- package/node_modules/@blockcast/mmt-fec/dist/index.js +28 -0
- package/node_modules/@blockcast/mmt-fec/dist/index.js.map +1 -0
- package/node_modules/@blockcast/mmt-fec/dist/loc-symbols.d.ts +20 -0
- package/node_modules/@blockcast/mmt-fec/dist/loc-symbols.d.ts.map +1 -0
- package/node_modules/@blockcast/mmt-fec/dist/loc-symbols.js +28 -0
- package/node_modules/@blockcast/mmt-fec/dist/loc-symbols.js.map +1 -0
- package/node_modules/@blockcast/mmt-fec/dist/oti.d.ts +258 -0
- package/node_modules/@blockcast/mmt-fec/dist/oti.d.ts.map +1 -0
- package/node_modules/@blockcast/mmt-fec/dist/oti.js +382 -0
- package/node_modules/@blockcast/mmt-fec/dist/oti.js.map +1 -0
- package/node_modules/@blockcast/mmt-fec/dist/reorder-buffer.d.ts +41 -0
- package/node_modules/@blockcast/mmt-fec/dist/reorder-buffer.d.ts.map +1 -0
- package/node_modules/@blockcast/mmt-fec/dist/reorder-buffer.js +81 -0
- package/node_modules/@blockcast/mmt-fec/dist/reorder-buffer.js.map +1 -0
- package/node_modules/@blockcast/mmt-fec/dist/repair-group.d.ts +82 -0
- package/node_modules/@blockcast/mmt-fec/dist/repair-group.d.ts.map +1 -0
- package/node_modules/@blockcast/mmt-fec/dist/repair-group.js +170 -0
- package/node_modules/@blockcast/mmt-fec/dist/repair-group.js.map +1 -0
- package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-alta-parse/README.md +181 -0
- 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 +57 -0
- package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-alta-parse/dist/index.d.ts.map +1 -0
- package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-alta-parse/dist/index.js +115 -0
- package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-alta-parse/dist/index.js.map +1 -0
- package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-alta-parse/package.json +50 -0
- package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-alta-parse/src/index.ts +136 -0
- package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/README.md +134 -0
- 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 +55 -0
- package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/fec-block-profile.d.ts.map +1 -0
- package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/fec-block-profile.js +128 -0
- package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/fec-block-profile.js.map +1 -0
- package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/index.d.ts +7 -0
- package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/index.d.ts.map +1 -0
- package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/index.js +7 -0
- package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/index.js.map +1 -0
- package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/mmtp.d.ts +292 -0
- package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/mmtp.d.ts.map +1 -0
- package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/mmtp.js +612 -0
- package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/mmtp.js.map +1 -0
- package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/net-constants.d.ts +18 -0
- package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/net-constants.d.ts.map +1 -0
- package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/net-constants.js +18 -0
- package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/net-constants.js.map +1 -0
- package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/ntp-short.d.ts +47 -0
- package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/ntp-short.d.ts.map +1 -0
- package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/ntp-short.js +54 -0
- package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/ntp-short.js.map +1 -0
- package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/serial-number.d.ts +57 -0
- package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/serial-number.d.ts.map +1 -0
- package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/serial-number.js +98 -0
- package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/serial-number.js.map +1 -0
- package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/signalling.d.ts +312 -0
- package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/signalling.d.ts.map +1 -0
- package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/signalling.js +637 -0
- package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/signalling.js.map +1 -0
- package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/package.json +49 -0
- package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/src/fec-block-profile.ts +145 -0
- package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/src/index.ts +6 -0
- package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/src/mmtp.ts +892 -0
- package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/src/net-constants.ts +17 -0
- package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/src/ntp-short.ts +67 -0
- package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/src/serial-number.ts +107 -0
- package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/src/signalling.ts +862 -0
- package/node_modules/@blockcast/mmt-fec/package.json +79 -0
- package/node_modules/@blockcast/mmt-fec/src/block-manager.ts +371 -0
- package/node_modules/@blockcast/mmt-fec/src/block-processor.ts +439 -0
- package/node_modules/@blockcast/mmt-fec/src/cmaf-recovery.ts +127 -0
- package/node_modules/@blockcast/mmt-fec/src/cmaf-symbols.ts +68 -0
- package/node_modules/@blockcast/mmt-fec/src/fec-block-mapper.ts +310 -0
- package/node_modules/@blockcast/mmt-fec/src/index.ts +94 -0
- package/node_modules/@blockcast/mmt-fec/src/loc-symbols.ts +34 -0
- package/node_modules/@blockcast/mmt-fec/src/oti.ts +463 -0
- package/node_modules/@blockcast/mmt-fec/src/reorder-buffer.ts +101 -0
- package/node_modules/@blockcast/mmt-fec/src/repair-group.ts +195 -0
- package/node_modules/@blockcast/mmt-manifest-verify/dist/.build-stamp +0 -0
- package/node_modules/@blockcast/mmt-manifest-verify/dist/admission.d.ts +28 -0
- package/node_modules/@blockcast/mmt-manifest-verify/dist/admission.d.ts.map +1 -0
- package/node_modules/@blockcast/mmt-manifest-verify/dist/admission.js +128 -0
- package/node_modules/@blockcast/mmt-manifest-verify/dist/admission.js.map +1 -0
- package/node_modules/@blockcast/mmt-manifest-verify/dist/index.d.ts +4 -0
- package/node_modules/@blockcast/mmt-manifest-verify/dist/index.d.ts.map +1 -0
- package/node_modules/@blockcast/mmt-manifest-verify/dist/index.js +4 -0
- package/node_modules/@blockcast/mmt-manifest-verify/dist/index.js.map +1 -0
- package/node_modules/@blockcast/mmt-manifest-verify/dist/source-auth.d.ts +37 -0
- package/node_modules/@blockcast/mmt-manifest-verify/dist/source-auth.d.ts.map +1 -0
- package/node_modules/@blockcast/mmt-manifest-verify/dist/source-auth.js +179 -0
- package/node_modules/@blockcast/mmt-manifest-verify/dist/source-auth.js.map +1 -0
- package/node_modules/@blockcast/mmt-manifest-verify/dist/verifier.d.ts +99 -0
- package/node_modules/@blockcast/mmt-manifest-verify/dist/verifier.d.ts.map +1 -0
- package/node_modules/@blockcast/mmt-manifest-verify/dist/verifier.js +492 -0
- package/node_modules/@blockcast/mmt-manifest-verify/dist/verifier.js.map +1 -0
- package/node_modules/@blockcast/mmt-manifest-verify/package.json +45 -0
- package/node_modules/@blockcast/mmt-manifest-verify/src/admission.ts +171 -0
- package/node_modules/@blockcast/mmt-manifest-verify/src/index.ts +3 -0
- package/node_modules/@blockcast/mmt-manifest-verify/src/source-auth.ts +230 -0
- package/node_modules/@blockcast/mmt-manifest-verify/src/verifier.ts +669 -0
- package/node_modules/@noble/hashes/LICENSE +21 -0
- package/node_modules/@noble/hashes/README.md +521 -0
- package/node_modules/@noble/hashes/_assert.d.ts +17 -0
- package/node_modules/@noble/hashes/_assert.d.ts.map +1 -0
- package/node_modules/@noble/hashes/_assert.js +18 -0
- package/node_modules/@noble/hashes/_assert.js.map +1 -0
- package/node_modules/@noble/hashes/_blake.d.ts +14 -0
- package/node_modules/@noble/hashes/_blake.d.ts.map +1 -0
- package/node_modules/@noble/hashes/_blake.js +50 -0
- package/node_modules/@noble/hashes/_blake.js.map +1 -0
- package/node_modules/@noble/hashes/_md.d.ts +51 -0
- package/node_modules/@noble/hashes/_md.d.ts.map +1 -0
- package/node_modules/@noble/hashes/_md.js +162 -0
- package/node_modules/@noble/hashes/_md.js.map +1 -0
- package/node_modules/@noble/hashes/_u64.d.ts +55 -0
- package/node_modules/@noble/hashes/_u64.d.ts.map +1 -0
- package/node_modules/@noble/hashes/_u64.js +90 -0
- package/node_modules/@noble/hashes/_u64.js.map +1 -0
- package/node_modules/@noble/hashes/argon2.d.ts +32 -0
- package/node_modules/@noble/hashes/argon2.d.ts.map +1 -0
- package/node_modules/@noble/hashes/argon2.js +401 -0
- package/node_modules/@noble/hashes/argon2.js.map +1 -0
- package/node_modules/@noble/hashes/blake1.d.ts +106 -0
- package/node_modules/@noble/hashes/blake1.d.ts.map +1 -0
- package/node_modules/@noble/hashes/blake1.js +459 -0
- package/node_modules/@noble/hashes/blake1.js.map +1 -0
- package/node_modules/@noble/hashes/blake2.d.ts +116 -0
- package/node_modules/@noble/hashes/blake2.d.ts.map +1 -0
- package/node_modules/@noble/hashes/blake2.js +420 -0
- package/node_modules/@noble/hashes/blake2.js.map +1 -0
- package/node_modules/@noble/hashes/blake2b.d.ts +11 -0
- package/node_modules/@noble/hashes/blake2b.d.ts.map +1 -0
- package/node_modules/@noble/hashes/blake2b.js +14 -0
- package/node_modules/@noble/hashes/blake2b.js.map +1 -0
- package/node_modules/@noble/hashes/blake2s.d.ts +20 -0
- package/node_modules/@noble/hashes/blake2s.d.ts.map +1 -0
- package/node_modules/@noble/hashes/blake2s.js +24 -0
- package/node_modules/@noble/hashes/blake2s.js.map +1 -0
- package/node_modules/@noble/hashes/blake3.d.ts +54 -0
- package/node_modules/@noble/hashes/blake3.d.ts.map +1 -0
- package/node_modules/@noble/hashes/blake3.js +255 -0
- package/node_modules/@noble/hashes/blake3.js.map +1 -0
- package/node_modules/@noble/hashes/crypto.d.ts +2 -0
- package/node_modules/@noble/hashes/crypto.d.ts.map +1 -0
- package/node_modules/@noble/hashes/crypto.js +5 -0
- package/node_modules/@noble/hashes/crypto.js.map +1 -0
- package/node_modules/@noble/hashes/cryptoNode.d.ts +2 -0
- package/node_modules/@noble/hashes/cryptoNode.d.ts.map +1 -0
- package/node_modules/@noble/hashes/cryptoNode.js +18 -0
- package/node_modules/@noble/hashes/cryptoNode.js.map +1 -0
- package/node_modules/@noble/hashes/eskdf.d.ts +47 -0
- package/node_modules/@noble/hashes/eskdf.d.ts.map +1 -0
- package/node_modules/@noble/hashes/eskdf.js +166 -0
- package/node_modules/@noble/hashes/eskdf.js.map +1 -0
- package/node_modules/@noble/hashes/esm/_assert.d.ts +17 -0
- package/node_modules/@noble/hashes/esm/_assert.d.ts.map +1 -0
- package/node_modules/@noble/hashes/esm/_assert.js +15 -0
- package/node_modules/@noble/hashes/esm/_assert.js.map +1 -0
- package/node_modules/@noble/hashes/esm/_blake.d.ts +14 -0
- package/node_modules/@noble/hashes/esm/_blake.d.ts.map +1 -0
- package/node_modules/@noble/hashes/esm/_blake.js +45 -0
- package/node_modules/@noble/hashes/esm/_blake.js.map +1 -0
- package/node_modules/@noble/hashes/esm/_md.d.ts +51 -0
- package/node_modules/@noble/hashes/esm/_md.d.ts.map +1 -0
- package/node_modules/@noble/hashes/esm/_md.js +155 -0
- package/node_modules/@noble/hashes/esm/_md.js.map +1 -0
- package/node_modules/@noble/hashes/esm/_u64.d.ts +55 -0
- package/node_modules/@noble/hashes/esm/_u64.d.ts.map +1 -0
- package/node_modules/@noble/hashes/esm/_u64.js +67 -0
- package/node_modules/@noble/hashes/esm/_u64.js.map +1 -0
- package/node_modules/@noble/hashes/esm/argon2.d.ts +32 -0
- package/node_modules/@noble/hashes/esm/argon2.d.ts.map +1 -0
- package/node_modules/@noble/hashes/esm/argon2.js +392 -0
- package/node_modules/@noble/hashes/esm/argon2.js.map +1 -0
- package/node_modules/@noble/hashes/esm/blake1.d.ts +106 -0
- package/node_modules/@noble/hashes/esm/blake1.d.ts.map +1 -0
- package/node_modules/@noble/hashes/esm/blake1.js +452 -0
- package/node_modules/@noble/hashes/esm/blake1.js.map +1 -0
- package/node_modules/@noble/hashes/esm/blake2.d.ts +116 -0
- package/node_modules/@noble/hashes/esm/blake2.d.ts.map +1 -0
- package/node_modules/@noble/hashes/esm/blake2.js +413 -0
- package/node_modules/@noble/hashes/esm/blake2.js.map +1 -0
- package/node_modules/@noble/hashes/esm/blake2b.d.ts +11 -0
- package/node_modules/@noble/hashes/esm/blake2b.d.ts.map +1 -0
- package/node_modules/@noble/hashes/esm/blake2b.js +11 -0
- package/node_modules/@noble/hashes/esm/blake2b.js.map +1 -0
- package/node_modules/@noble/hashes/esm/blake2s.d.ts +20 -0
- package/node_modules/@noble/hashes/esm/blake2s.d.ts.map +1 -0
- package/node_modules/@noble/hashes/esm/blake2s.js +21 -0
- package/node_modules/@noble/hashes/esm/blake2s.js.map +1 -0
- package/node_modules/@noble/hashes/esm/blake3.d.ts +54 -0
- package/node_modules/@noble/hashes/esm/blake3.d.ts.map +1 -0
- package/node_modules/@noble/hashes/esm/blake3.js +251 -0
- package/node_modules/@noble/hashes/esm/blake3.js.map +1 -0
- package/node_modules/@noble/hashes/esm/crypto.d.ts +2 -0
- package/node_modules/@noble/hashes/esm/crypto.d.ts.map +1 -0
- package/node_modules/@noble/hashes/esm/crypto.js +2 -0
- package/node_modules/@noble/hashes/esm/crypto.js.map +1 -0
- package/node_modules/@noble/hashes/esm/cryptoNode.d.ts +2 -0
- package/node_modules/@noble/hashes/esm/cryptoNode.d.ts.map +1 -0
- package/node_modules/@noble/hashes/esm/cryptoNode.js +15 -0
- package/node_modules/@noble/hashes/esm/cryptoNode.js.map +1 -0
- package/node_modules/@noble/hashes/esm/eskdf.d.ts +47 -0
- package/node_modules/@noble/hashes/esm/eskdf.d.ts.map +1 -0
- package/node_modules/@noble/hashes/esm/eskdf.js +160 -0
- package/node_modules/@noble/hashes/esm/eskdf.js.map +1 -0
- package/node_modules/@noble/hashes/esm/hkdf.d.ts +36 -0
- package/node_modules/@noble/hashes/esm/hkdf.d.ts.map +1 -0
- package/node_modules/@noble/hashes/esm/hkdf.js +82 -0
- package/node_modules/@noble/hashes/esm/hkdf.js.map +1 -0
- package/node_modules/@noble/hashes/esm/hmac.d.ts +35 -0
- package/node_modules/@noble/hashes/esm/hmac.d.ts.map +1 -0
- package/node_modules/@noble/hashes/esm/hmac.js +86 -0
- package/node_modules/@noble/hashes/esm/hmac.js.map +1 -0
- package/node_modules/@noble/hashes/esm/index.d.ts +2 -0
- package/node_modules/@noble/hashes/esm/index.d.ts.map +1 -0
- package/node_modules/@noble/hashes/esm/index.js +33 -0
- package/node_modules/@noble/hashes/esm/index.js.map +1 -0
- package/node_modules/@noble/hashes/esm/legacy.d.ts +71 -0
- package/node_modules/@noble/hashes/esm/legacy.d.ts.map +1 -0
- package/node_modules/@noble/hashes/esm/legacy.js +281 -0
- package/node_modules/@noble/hashes/esm/legacy.js.map +1 -0
- package/node_modules/@noble/hashes/esm/package.json +10 -0
- package/node_modules/@noble/hashes/esm/pbkdf2.d.ts +23 -0
- package/node_modules/@noble/hashes/esm/pbkdf2.d.ts.map +1 -0
- package/node_modules/@noble/hashes/esm/pbkdf2.js +97 -0
- package/node_modules/@noble/hashes/esm/pbkdf2.js.map +1 -0
- package/node_modules/@noble/hashes/esm/ripemd160.d.ts +13 -0
- package/node_modules/@noble/hashes/esm/ripemd160.d.ts.map +1 -0
- package/node_modules/@noble/hashes/esm/ripemd160.js +13 -0
- package/node_modules/@noble/hashes/esm/ripemd160.js.map +1 -0
- package/node_modules/@noble/hashes/esm/scrypt.d.ts +34 -0
- package/node_modules/@noble/hashes/esm/scrypt.d.ts.map +1 -0
- package/node_modules/@noble/hashes/esm/scrypt.js +228 -0
- package/node_modules/@noble/hashes/esm/scrypt.js.map +1 -0
- package/node_modules/@noble/hashes/esm/sha1.d.ts +11 -0
- package/node_modules/@noble/hashes/esm/sha1.d.ts.map +1 -0
- package/node_modules/@noble/hashes/esm/sha1.js +11 -0
- package/node_modules/@noble/hashes/esm/sha1.js.map +1 -0
- package/node_modules/@noble/hashes/esm/sha2.d.ts +159 -0
- package/node_modules/@noble/hashes/esm/sha2.d.ts.map +1 -0
- package/node_modules/@noble/hashes/esm/sha2.js +375 -0
- package/node_modules/@noble/hashes/esm/sha2.js.map +1 -0
- package/node_modules/@noble/hashes/esm/sha256.d.ts +20 -0
- package/node_modules/@noble/hashes/esm/sha256.d.ts.map +1 -0
- package/node_modules/@noble/hashes/esm/sha256.js +20 -0
- package/node_modules/@noble/hashes/esm/sha256.js.map +1 -0
- package/node_modules/@noble/hashes/esm/sha3-addons.d.ts +142 -0
- package/node_modules/@noble/hashes/esm/sha3-addons.d.ts.map +1 -0
- package/node_modules/@noble/hashes/esm/sha3-addons.js +393 -0
- package/node_modules/@noble/hashes/esm/sha3-addons.js.map +1 -0
- package/node_modules/@noble/hashes/esm/sha3.d.ts +53 -0
- package/node_modules/@noble/hashes/esm/sha3.d.ts.map +1 -0
- package/node_modules/@noble/hashes/esm/sha3.js +234 -0
- package/node_modules/@noble/hashes/esm/sha3.js.map +1 -0
- package/node_modules/@noble/hashes/esm/sha512.d.ts +26 -0
- package/node_modules/@noble/hashes/esm/sha512.d.ts.map +1 -0
- package/node_modules/@noble/hashes/esm/sha512.js +26 -0
- package/node_modules/@noble/hashes/esm/sha512.js.map +1 -0
- package/node_modules/@noble/hashes/esm/utils.d.ts +161 -0
- package/node_modules/@noble/hashes/esm/utils.d.ts.map +1 -0
- package/node_modules/@noble/hashes/esm/utils.js +281 -0
- package/node_modules/@noble/hashes/esm/utils.js.map +1 -0
- package/node_modules/@noble/hashes/hkdf.d.ts +36 -0
- package/node_modules/@noble/hashes/hkdf.d.ts.map +1 -0
- package/node_modules/@noble/hashes/hkdf.js +88 -0
- package/node_modules/@noble/hashes/hkdf.js.map +1 -0
- package/node_modules/@noble/hashes/hmac.d.ts +35 -0
- package/node_modules/@noble/hashes/hmac.d.ts.map +1 -0
- package/node_modules/@noble/hashes/hmac.js +91 -0
- package/node_modules/@noble/hashes/hmac.js.map +1 -0
- package/node_modules/@noble/hashes/index.d.ts +1 -0
- package/node_modules/@noble/hashes/index.d.ts.map +1 -0
- package/node_modules/@noble/hashes/index.js +33 -0
- package/node_modules/@noble/hashes/index.js.map +1 -0
- package/node_modules/@noble/hashes/legacy.d.ts +71 -0
- package/node_modules/@noble/hashes/legacy.d.ts.map +1 -0
- package/node_modules/@noble/hashes/legacy.js +287 -0
- package/node_modules/@noble/hashes/legacy.js.map +1 -0
- package/node_modules/@noble/hashes/package.json +266 -0
- package/node_modules/@noble/hashes/pbkdf2.d.ts +23 -0
- package/node_modules/@noble/hashes/pbkdf2.d.ts.map +1 -0
- package/node_modules/@noble/hashes/pbkdf2.js +101 -0
- package/node_modules/@noble/hashes/pbkdf2.js.map +1 -0
- package/node_modules/@noble/hashes/ripemd160.d.ts +13 -0
- package/node_modules/@noble/hashes/ripemd160.d.ts.map +1 -0
- package/node_modules/@noble/hashes/ripemd160.js +16 -0
- package/node_modules/@noble/hashes/ripemd160.js.map +1 -0
- package/node_modules/@noble/hashes/scrypt.d.ts +34 -0
- package/node_modules/@noble/hashes/scrypt.d.ts.map +1 -0
- package/node_modules/@noble/hashes/scrypt.js +232 -0
- package/node_modules/@noble/hashes/scrypt.js.map +1 -0
- package/node_modules/@noble/hashes/sha1.d.ts +11 -0
- package/node_modules/@noble/hashes/sha1.d.ts.map +1 -0
- package/node_modules/@noble/hashes/sha1.js +14 -0
- package/node_modules/@noble/hashes/sha1.js.map +1 -0
- package/node_modules/@noble/hashes/sha2.d.ts +159 -0
- package/node_modules/@noble/hashes/sha2.d.ts.map +1 -0
- package/node_modules/@noble/hashes/sha2.js +384 -0
- package/node_modules/@noble/hashes/sha2.js.map +1 -0
- package/node_modules/@noble/hashes/sha256.d.ts +20 -0
- package/node_modules/@noble/hashes/sha256.d.ts.map +1 -0
- package/node_modules/@noble/hashes/sha256.js +23 -0
- package/node_modules/@noble/hashes/sha256.js.map +1 -0
- package/node_modules/@noble/hashes/sha3-addons.d.ts +142 -0
- package/node_modules/@noble/hashes/sha3-addons.d.ts.map +1 -0
- package/node_modules/@noble/hashes/sha3-addons.js +402 -0
- package/node_modules/@noble/hashes/sha3-addons.js.map +1 -0
- package/node_modules/@noble/hashes/sha3.d.ts +53 -0
- package/node_modules/@noble/hashes/sha3.d.ts.map +1 -0
- package/node_modules/@noble/hashes/sha3.js +239 -0
- package/node_modules/@noble/hashes/sha3.js.map +1 -0
- package/node_modules/@noble/hashes/sha512.d.ts +26 -0
- package/node_modules/@noble/hashes/sha512.d.ts.map +1 -0
- package/node_modules/@noble/hashes/sha512.js +29 -0
- package/node_modules/@noble/hashes/sha512.js.map +1 -0
- package/node_modules/@noble/hashes/src/_assert.ts +22 -0
- package/node_modules/@noble/hashes/src/_blake.ts +50 -0
- package/node_modules/@noble/hashes/src/_md.ts +176 -0
- package/node_modules/@noble/hashes/src/_u64.ts +91 -0
- package/node_modules/@noble/hashes/src/argon2.ts +497 -0
- package/node_modules/@noble/hashes/src/blake1.ts +534 -0
- package/node_modules/@noble/hashes/src/blake2.ts +486 -0
- package/node_modules/@noble/hashes/src/blake2b.ts +10 -0
- package/node_modules/@noble/hashes/src/blake2s.ts +20 -0
- package/node_modules/@noble/hashes/src/blake3.ts +272 -0
- package/node_modules/@noble/hashes/src/crypto.ts +9 -0
- package/node_modules/@noble/hashes/src/cryptoNode.ts +15 -0
- package/node_modules/@noble/hashes/src/eskdf.ts +187 -0
- package/node_modules/@noble/hashes/src/hkdf.ts +88 -0
- package/node_modules/@noble/hashes/src/hmac.ts +94 -0
- package/node_modules/@noble/hashes/src/index.ts +31 -0
- package/node_modules/@noble/hashes/src/legacy.ts +293 -0
- package/node_modules/@noble/hashes/src/pbkdf2.ts +122 -0
- package/node_modules/@noble/hashes/src/ripemd160.ts +12 -0
- package/node_modules/@noble/hashes/src/scrypt.ts +257 -0
- package/node_modules/@noble/hashes/src/sha1.ts +10 -0
- package/node_modules/@noble/hashes/src/sha2.ts +402 -0
- package/node_modules/@noble/hashes/src/sha256.ts +24 -0
- package/node_modules/@noble/hashes/src/sha3-addons.ts +499 -0
- package/node_modules/@noble/hashes/src/sha3.ts +258 -0
- package/node_modules/@noble/hashes/src/sha512.ts +34 -0
- package/node_modules/@noble/hashes/src/utils.ts +395 -0
- package/node_modules/@noble/hashes/utils.d.ts +161 -0
- package/node_modules/@noble/hashes/utils.d.ts.map +1 -0
- package/node_modules/@noble/hashes/utils.js +313 -0
- package/node_modules/@noble/hashes/utils.js.map +1 -0
- package/package.json +179 -8
- package/schemas/catalog.schema.json +357 -0
- package/src/align.ts +239 -0
- package/src/audio-init-parser.ts +189 -0
- package/src/audio-ring-buffer.ts +120 -0
- package/src/authored-timing.ts +63 -0
- package/src/block-policy.ts +903 -0
- package/src/carp-catalog.ts +1065 -0
- package/src/catalog-fec-params.ts +729 -0
- package/src/catalog-network-source.ts +74 -0
- package/src/catalog-validator.ts +34 -0
- package/src/cmaf-block-assembler.ts +1255 -0
- package/src/cmaf-group-batcher.ts +104 -0
- package/src/cmcd-producer.ts +329 -0
- package/src/codec-utils.ts +1984 -0
- package/src/conceal-timeline.ts +293 -0
- package/src/delivery-gap.ts +147 -0
- package/src/duplicate-ledger.ts +372 -0
- package/src/fec-codec.ts +61 -0
- package/src/fec-manager.ts +4298 -0
- package/src/fec-starvation-detector.ts +128 -0
- package/src/fec-state.ts +219 -0
- package/src/fec-worker-setup.ts +283 -0
- package/src/fmp4-completion.ts +47 -0
- package/src/fmp4-reader.ts +112 -0
- package/src/index.ts +590 -21
- package/src/iso-block-locator.ts +329 -0
- package/src/iso-bmff-timing.ts +813 -0
- package/src/loc.ts +128 -154
- package/src/manifest-auth-plan.ts +107 -0
- package/src/media-router.ts +841 -0
- package/src/mfu-reassembler.ts +3480 -155
- package/src/mfu-reassembly-policy.ts +134 -0
- package/src/mmtp-container-parser.ts +2380 -0
- package/src/mmtp-decode-pipeline.ts +406 -0
- package/src/mmtp-router.ts +4860 -0
- package/src/mmtp-to-cmaf.ts +977 -0
- package/src/multicast-auth-catalog.ts +340 -0
- package/src/multicast-input.ts +793 -0
- package/src/multicast-manager.ts +2783 -0
- package/src/multicast-transport.ts +1058 -0
- package/src/multicast-unwrap.ts +132 -0
- package/src/no-modules.ts +21 -0
- package/src/phase14-catalog-override.ts +336 -0
- package/src/repair-fetch.ts +772 -0
- package/src/repair-layers.ts +533 -0
- package/src/sample-combiner.ts +938 -0
- package/src/signaling.ts +78 -0
- package/src/source-auth.ts +1 -0
- package/src/switching-set-alignment.ts +109 -0
- package/src/timestamp-codec.ts +265 -0
- package/src/track-pipeline-setup.ts +205 -0
- package/src/track-pipeline.ts +332 -0
- package/src/track-session.ts +1010 -0
- package/src/transport-pool.ts +128 -0
- package/src/wasm-mfu-reassembler.ts +405 -0
- package/src/wasm-mmtp-container-parser.ts +23 -0
- package/test-vectors/align.json +199 -0
- package/test-vectors/catalog/README.md +106 -0
- package/test-vectors/catalog/negative/archive-origin-with-path.json +33 -0
- package/test-vectors/catalog/negative/archive-unknown-field.json +34 -0
- package/test-vectors/catalog/negative/fec-block-profile-missing.json +51 -0
- package/test-vectors/catalog/negative/fec-block-profile-unknown.json +52 -0
- package/test-vectors/catalog/negative/fec-enhancement-repair-removed.json +28 -0
- package/test-vectors/catalog/negative/fec-repair-layer-geometry.json +33 -0
- package/test-vectors/catalog/negative/fec-repair-layer-missing-symbols.json +31 -0
- package/test-vectors/catalog/negative/fec-repair-multiple-depends.json +32 -0
- package/test-vectors/catalog/negative/fec-repair-overlay-with-layer.json +34 -0
- package/test-vectors/catalog/negative/fec-repair-priority-out-of-band.json +24 -0
- package/test-vectors/catalog/negative/fec-repair-source-symbols-without-scope.json +33 -0
- package/test-vectors/catalog/negative/legacy-selection-params.json +15 -0
- package/test-vectors/catalog/negative/media-track-repair-layer.json +16 -0
- package/test-vectors/catalog/negative/missing-mmtp-fields.json +12 -0
- package/test-vectors/catalog/negative/multicast-auth-padded-key.json +37 -0
- package/test-vectors/catalog/negative/multicast-auth-reused-track-format.json +53 -0
- package/test-vectors/catalog/negative/multicast-auth-trailing-pad-bits.json +37 -0
- package/test-vectors/catalog/negative/multicast-endpoint-missing-protocol-and-source.json +24 -0
- package/test-vectors/catalog/negative/multicast-network-source-empty.json +44 -0
- package/test-vectors/catalog/negative/multicast-packet-id-signaling.json +26 -0
- package/test-vectors/catalog/negative/network-source-object.json +25 -0
- package/test-vectors/catalog/negative/raptorq-source-symbols-over-max.json +24 -0
- package/test-vectors/catalog/negative/raptorq-unaligned-symbol.json +27 -0
- package/test-vectors/catalog/negative/repair-track-legacy-shape.json +19 -0
- package/test-vectors/catalog/negative/track-role-empty.json +16 -0
- package/test-vectors/catalog/negative/track-role-non-string.json +16 -0
- package/test-vectors/catalog/positive/archive-origin.json +34 -0
- package/test-vectors/catalog/positive/fec-iso-ssbg1.json +52 -0
- package/test-vectors/catalog/positive/fec-layered-repair.json +106 -0
- package/test-vectors/catalog/positive/fec-multicast.json +52 -0
- package/test-vectors/catalog/positive/flat-av.json +30 -0
- package/test-vectors/catalog/positive/multicast-auth-rotation.json +55 -0
- package/test-vectors/catalog/positive/multicast-native-ssm-no-network-source.json +43 -0
- package/test-vectors/catalog/positive/ticks-and-mixed-timescales.json +39 -0
- package/test-vectors/catalog/positive/track-role.json +49 -0
- package/test-vectors/catalog/releases/moqcast-native-v16-v1/catalog.json +65 -0
- package/test-vectors/catalog/releases/moqcast-native-v16-v1.archive.sha256 +1 -0
- package/test-vectors/catalog-multicast-auth-v1.json +26 -0
- package/test-vectors/catalog-validation.json +732 -0
- package/test-vectors/catalog-validator-public-api.mjs +25 -0
- package/test-vectors/generate-source-auth-fixture.mjs +114 -0
- package/test-vectors/hevc-x265-repeat-headers-v1.json +40 -0
- package/test-vectors/source-auth-v1.json +61 -0
- package/test-vectors/source-contiguity-v1.json +131 -0
- package/test-vectors/subsample-placement-v1.json +230 -0
- package/dist/mmtp.d.ts +0 -73
- package/dist/mmtp.d.ts.map +0 -1
- package/dist/mmtp.js +0 -210
- package/dist/mmtp.js.map +0 -1
- package/src/mmtp.ts +0 -277
|
@@ -0,0 +1,2264 @@
|
|
|
1
|
+
// node_modules/@blockcast/mmt-transport/dist/types.js
|
|
2
|
+
var MoqFecAlgorithm;
|
|
3
|
+
(function(MoqFecAlgorithm2) {
|
|
4
|
+
MoqFecAlgorithm2[MoqFecAlgorithm2["None"] = 0] = "None";
|
|
5
|
+
MoqFecAlgorithm2[MoqFecAlgorithm2["Xor"] = 1] = "Xor";
|
|
6
|
+
MoqFecAlgorithm2[MoqFecAlgorithm2["RaptorQ"] = 2] = "RaptorQ";
|
|
7
|
+
})(MoqFecAlgorithm || (MoqFecAlgorithm = {}));
|
|
8
|
+
|
|
9
|
+
// node_modules/@blockcast/mmt-transport/node_modules/@blockcast/mmt-alta-parse/dist/index.js
|
|
10
|
+
var MPU_HEADER_SIZE = 8;
|
|
11
|
+
var MPU_LENGTH_FIELD_SIZE = 2;
|
|
12
|
+
var MPU_HEADER_REMAINDER_SIZE = MPU_HEADER_SIZE - MPU_LENGTH_FIELD_SIZE;
|
|
13
|
+
|
|
14
|
+
// node_modules/@blockcast/mmt-transport/node_modules/@blockcast/mmt-base/dist/mmtp.js
|
|
15
|
+
var MPU_HEADER_SIZE2 = 8;
|
|
16
|
+
var MPU_LENGTH_FIELD_SIZE2 = 2;
|
|
17
|
+
var MPU_HEADER_REMAINDER_SIZE2 = MPU_HEADER_SIZE2 - MPU_LENGTH_FIELD_SIZE2;
|
|
18
|
+
var MMTP_SIGNALING_PACKET_ID = 0;
|
|
19
|
+
var MMTP_PACKET_ID_MAX = 65535;
|
|
20
|
+
|
|
21
|
+
// node_modules/@blockcast/mmt-transport/node_modules/@blockcast/mmt-base/dist/net-constants.js
|
|
22
|
+
var DEFAULT_AMT_RELAY_PORT = 2268;
|
|
23
|
+
|
|
24
|
+
// node_modules/@blockcast/mmt-transport/node_modules/@blockcast/mmt-base/dist/ntp-short.js
|
|
25
|
+
var NTP_SHORT_BITS = 32;
|
|
26
|
+
var NTP_SHORT_MODULUS = 2 ** NTP_SHORT_BITS;
|
|
27
|
+
var NTP_SHORT_HALF_RANGE = 2 ** (NTP_SHORT_BITS - 1);
|
|
28
|
+
function isNtpShort(value) {
|
|
29
|
+
return Number.isInteger(value) && value >= 0 && value < NTP_SHORT_MODULUS;
|
|
30
|
+
}
|
|
31
|
+
function compareNtpShort(a, b) {
|
|
32
|
+
if (!isNtpShort(a) || !isNtpShort(b)) {
|
|
33
|
+
throw new RangeError(`NTP-short values must be integers in [0, 2^${NTP_SHORT_BITS}), got ${String(a)} and ${String(b)}`);
|
|
34
|
+
}
|
|
35
|
+
if (a === b)
|
|
36
|
+
return "equal";
|
|
37
|
+
const forward = (a - b + NTP_SHORT_MODULUS) % NTP_SHORT_MODULUS;
|
|
38
|
+
if (forward < NTP_SHORT_HALF_RANGE)
|
|
39
|
+
return "after";
|
|
40
|
+
if (forward > NTP_SHORT_HALF_RANGE)
|
|
41
|
+
return "before";
|
|
42
|
+
return "unordered";
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// node_modules/@blockcast/mmt-transport/node_modules/@blockcast/mmt-base/dist/fec-block-profile.js
|
|
46
|
+
var FEC_BLOCK_PROFILES = Object.freeze([
|
|
47
|
+
"k-padded-v1",
|
|
48
|
+
"iso-ssbg1-v1"
|
|
49
|
+
]);
|
|
50
|
+
|
|
51
|
+
// node_modules/@blockcast/mmt-transport/dist/driad-discovery.js
|
|
52
|
+
var DRIADDiscovery = class {
|
|
53
|
+
cache = /* @__PURE__ */ new Map();
|
|
54
|
+
cacheExpiry = 3e5;
|
|
55
|
+
// 5 minutes
|
|
56
|
+
/**
|
|
57
|
+
* Discover AMT relays for a multicast source address (RFC 8777)
|
|
58
|
+
*
|
|
59
|
+
* @param source - The multicast source IP address (NOT the group!)
|
|
60
|
+
* @returns Array of discovered AMT relays that can tunnel traffic from this source
|
|
61
|
+
*/
|
|
62
|
+
async discoverRelays(source) {
|
|
63
|
+
const cached = this.cache.get(source);
|
|
64
|
+
if (cached) {
|
|
65
|
+
return cached;
|
|
66
|
+
}
|
|
67
|
+
try {
|
|
68
|
+
const reversedSource = source.split(".").reverse().join(".");
|
|
69
|
+
const queryDomain = `${reversedSource}.amt.in-addr.arpa`;
|
|
70
|
+
console.log(`[DRIAD] Discovering relays for source ${source} via ${queryDomain}`);
|
|
71
|
+
const relays = await this.queryDRIADProxy(source);
|
|
72
|
+
if (relays.length > 0) {
|
|
73
|
+
this.cache.set(source, relays);
|
|
74
|
+
setTimeout(() => this.cache.delete(source), this.cacheExpiry);
|
|
75
|
+
}
|
|
76
|
+
return relays;
|
|
77
|
+
} catch (error) {
|
|
78
|
+
console.warn(`[DRIAD] Discovery failed for source ${source}:`, error);
|
|
79
|
+
return [];
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Query DRIAD proxy service (browser-compatible)
|
|
84
|
+
*/
|
|
85
|
+
async queryDRIADProxy(source) {
|
|
86
|
+
const proxyEndpoints = [
|
|
87
|
+
"https://driad.amt.net/api/v1/relays",
|
|
88
|
+
"https://multicast-gateway.cloudflare.com/driad"
|
|
89
|
+
];
|
|
90
|
+
for (const endpoint of proxyEndpoints) {
|
|
91
|
+
try {
|
|
92
|
+
const response = await fetch(`${endpoint}?source=${encodeURIComponent(source)}`, {
|
|
93
|
+
method: "GET",
|
|
94
|
+
headers: { Accept: "application/json" },
|
|
95
|
+
signal: AbortSignal.timeout(5e3)
|
|
96
|
+
});
|
|
97
|
+
if (response.ok) {
|
|
98
|
+
const data = await response.json();
|
|
99
|
+
return data.relays || [];
|
|
100
|
+
}
|
|
101
|
+
} catch {
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
const dohRelay = await this.queryDNSOverHTTPS(source);
|
|
105
|
+
if (dohRelay) {
|
|
106
|
+
return [dohRelay];
|
|
107
|
+
}
|
|
108
|
+
return [];
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Query DNS-over-HTTPS for DRIAD relay based on source address (RFC 8777)
|
|
112
|
+
*/
|
|
113
|
+
async queryDNSOverHTTPS(source) {
|
|
114
|
+
try {
|
|
115
|
+
const reversedSource = source.split(".").reverse().join(".");
|
|
116
|
+
const queryName = `${reversedSource}.amt.in-addr.arpa`;
|
|
117
|
+
const dohUrl = `https://cloudflare-dns.com/dns-query?name=${queryName}&type=A`;
|
|
118
|
+
const response = await fetch(dohUrl, {
|
|
119
|
+
headers: { Accept: "application/dns-json" },
|
|
120
|
+
signal: AbortSignal.timeout(3e3)
|
|
121
|
+
});
|
|
122
|
+
if (!response.ok) {
|
|
123
|
+
return null;
|
|
124
|
+
}
|
|
125
|
+
const result = await response.json();
|
|
126
|
+
if (result.Answer && result.Answer.length > 0) {
|
|
127
|
+
return {
|
|
128
|
+
host: result.Answer[0].data,
|
|
129
|
+
port: DEFAULT_AMT_RELAY_PORT,
|
|
130
|
+
priority: 10,
|
|
131
|
+
weight: 100,
|
|
132
|
+
asn: 0
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
return null;
|
|
136
|
+
} catch (error) {
|
|
137
|
+
console.warn("[DRIAD] DNS-over-HTTPS query failed:", error);
|
|
138
|
+
return null;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Select best relay based on RTT measurements
|
|
143
|
+
*/
|
|
144
|
+
async selectBestRelay(relays) {
|
|
145
|
+
if (relays.length === 0)
|
|
146
|
+
return null;
|
|
147
|
+
const measured = await Promise.all(relays.map(async (relay) => {
|
|
148
|
+
const rtt = await this.measureRTT(relay.host, relay.port);
|
|
149
|
+
return { ...relay, rtt };
|
|
150
|
+
}));
|
|
151
|
+
measured.sort((a, b) => {
|
|
152
|
+
if (a.priority !== b.priority)
|
|
153
|
+
return a.priority - b.priority;
|
|
154
|
+
return (a.rtt || Infinity) - (b.rtt || Infinity);
|
|
155
|
+
});
|
|
156
|
+
console.log(`[DRIAD] Selected relay: ${measured[0].host} (RTT: ${measured[0].rtt}ms)`);
|
|
157
|
+
return measured[0];
|
|
158
|
+
}
|
|
159
|
+
async measureRTT(host, _port) {
|
|
160
|
+
const start = performance.now();
|
|
161
|
+
try {
|
|
162
|
+
await fetch(`https://${host}/health`, {
|
|
163
|
+
method: "HEAD",
|
|
164
|
+
signal: AbortSignal.timeout(2e3),
|
|
165
|
+
mode: "no-cors"
|
|
166
|
+
});
|
|
167
|
+
return performance.now() - start;
|
|
168
|
+
} catch {
|
|
169
|
+
return Infinity;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* Clear the relay cache
|
|
174
|
+
*/
|
|
175
|
+
clearCache() {
|
|
176
|
+
this.cache.clear();
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Get cached relays for a source address
|
|
180
|
+
*/
|
|
181
|
+
getCached(source) {
|
|
182
|
+
return this.cache.get(source);
|
|
183
|
+
}
|
|
184
|
+
};
|
|
185
|
+
var driadDiscovery = new DRIADDiscovery();
|
|
186
|
+
|
|
187
|
+
// node_modules/@blockcast/mmt-transport/dist/multicast-tier.js
|
|
188
|
+
function endpointKey(endpoint) {
|
|
189
|
+
return `${endpoint.sourceAddress ?? ""}|${endpoint.groupAddress}|${endpoint.port}`;
|
|
190
|
+
}
|
|
191
|
+
function carriesMedia(endpoint) {
|
|
192
|
+
return endpoint.carriesMedia !== false;
|
|
193
|
+
}
|
|
194
|
+
function selectMulticastTier(endpoints, estimate) {
|
|
195
|
+
const media = endpoints.filter(carriesMedia);
|
|
196
|
+
if (media.length <= 1)
|
|
197
|
+
return media[0];
|
|
198
|
+
const ranked = media.filter((endpoint) => typeof endpoint.bandwidth === "number" && endpoint.bandwidth > 0).sort((a, b) => a.bandwidth - b.bandwidth);
|
|
199
|
+
if (ranked.length < 2)
|
|
200
|
+
return media[0];
|
|
201
|
+
if (estimate === void 0)
|
|
202
|
+
return ranked[0];
|
|
203
|
+
const budget = estimate * 0.8;
|
|
204
|
+
let selected;
|
|
205
|
+
for (const endpoint of ranked) {
|
|
206
|
+
if (endpoint.bandwidth <= budget)
|
|
207
|
+
selected = endpoint;
|
|
208
|
+
}
|
|
209
|
+
return selected ?? ranked[0];
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// node_modules/@blockcast/mmt-transport/dist/abr-controller.js
|
|
213
|
+
var QUALITY_LEVELS = Object.freeze([
|
|
214
|
+
{ name: "240p", bitrate: 3e5, width: 426, height: 240, fps: 15 },
|
|
215
|
+
{ name: "360p", bitrate: 5e5, width: 640, height: 360, fps: 24 },
|
|
216
|
+
{ name: "480p", bitrate: 1e6, width: 854, height: 480, fps: 30 },
|
|
217
|
+
{ name: "720p", bitrate: 25e5, width: 1280, height: 720, fps: 30 },
|
|
218
|
+
{ name: "1080p", bitrate: 5e6, width: 1920, height: 1080, fps: 30 },
|
|
219
|
+
{ name: "1440p", bitrate: 8e6, width: 2560, height: 1440, fps: 30 },
|
|
220
|
+
{ name: "4K", bitrate: 15e6, width: 3840, height: 2160, fps: 30 }
|
|
221
|
+
].map((level) => Object.freeze(level)));
|
|
222
|
+
function positiveFinite(value) {
|
|
223
|
+
return typeof value === "number" && Number.isFinite(value) && value > 0;
|
|
224
|
+
}
|
|
225
|
+
function validateQualityLadder(ladder) {
|
|
226
|
+
if (!Array.isArray(ladder) || ladder.length === 0) {
|
|
227
|
+
throw new TypeError("[ABR] ladder must list at least one rendition (e.g. the catalog's video tracks)");
|
|
228
|
+
}
|
|
229
|
+
const names = /* @__PURE__ */ new Set();
|
|
230
|
+
for (const level of ladder) {
|
|
231
|
+
if (typeof level?.name !== "string" || level.name.length === 0) {
|
|
232
|
+
throw new TypeError("[ABR] every ladder rendition needs a name");
|
|
233
|
+
}
|
|
234
|
+
if (!positiveFinite(level.bitrate)) {
|
|
235
|
+
throw new RangeError(`[ABR] rendition ${level.name}: bitrate must be a positive finite number, got ${String(level.bitrate)}`);
|
|
236
|
+
}
|
|
237
|
+
for (const field of ["width", "height", "fps"]) {
|
|
238
|
+
if (level[field] !== void 0 && !positiveFinite(level[field])) {
|
|
239
|
+
throw new RangeError(`[ABR] rendition ${level.name}: ${field} must be a positive finite number when stated, got ${String(level[field])}`);
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
if (names.has(level.name))
|
|
243
|
+
throw new RangeError(`[ABR] rendition ${level.name} appears twice in the ladder`);
|
|
244
|
+
names.add(level.name);
|
|
245
|
+
}
|
|
246
|
+
return Object.freeze([...ladder].sort((a, b) => a.bitrate - b.bitrate).map((level) => Object.freeze({ ...level })));
|
|
247
|
+
}
|
|
248
|
+
var AdaptiveBitrateController = class {
|
|
249
|
+
/** The caller's renditions, ascending by bitrate (`validateQualityLadder`). */
|
|
250
|
+
ladder;
|
|
251
|
+
currentQualityIndex;
|
|
252
|
+
history = [];
|
|
253
|
+
historyWindow = 1e4;
|
|
254
|
+
// 10 second window
|
|
255
|
+
upgradeThreshold = 0.8;
|
|
256
|
+
// 80% of target bitrate sustained
|
|
257
|
+
downgradeThreshold = 0.5;
|
|
258
|
+
// 50% of target bitrate triggers downgrade
|
|
259
|
+
stabilityPeriod = 5e3;
|
|
260
|
+
// Wait 5s before upgrading
|
|
261
|
+
lastQualityChange = 0;
|
|
262
|
+
onQualityChange;
|
|
263
|
+
// FEC stats for effective loss rate calculation
|
|
264
|
+
fecStats = {
|
|
265
|
+
rawLossRate: 0,
|
|
266
|
+
effectiveLossRate: 0,
|
|
267
|
+
recoveryRate: 1,
|
|
268
|
+
fecEnabled: false
|
|
269
|
+
};
|
|
270
|
+
constructor(options) {
|
|
271
|
+
if (!options || typeof options !== "object") {
|
|
272
|
+
throw new TypeError("[ABR] AdaptiveBitrateController needs { ladder, initialQuality }");
|
|
273
|
+
}
|
|
274
|
+
this.ladder = validateQualityLadder(options.ladder);
|
|
275
|
+
this.currentQualityIndex = options.initialQuality === void 0 ? 0 : this.ladder.findIndex((q) => q.name === options.initialQuality);
|
|
276
|
+
if (this.currentQualityIndex < 0) {
|
|
277
|
+
throw new RangeError(`[ABR] initial rendition ${String(options.initialQuality)} is not in the ladder`);
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
setCallback(callback) {
|
|
281
|
+
this.onQualityChange = callback;
|
|
282
|
+
}
|
|
283
|
+
getCurrentQuality() {
|
|
284
|
+
return this.ladder[this.currentQualityIndex];
|
|
285
|
+
}
|
|
286
|
+
/** Selects the multicast (S,G) tier without replacing a player's unicast ABR manager. */
|
|
287
|
+
selectMulticastTier(endpoints, estimate = this.getMetrics().measuredBitrate || void 0) {
|
|
288
|
+
return selectMulticastTier(endpoints, estimate);
|
|
289
|
+
}
|
|
290
|
+
/**
|
|
291
|
+
* Record network sample for bitrate adaptation
|
|
292
|
+
*
|
|
293
|
+
* @param bytesReceived - Bytes received in this sample
|
|
294
|
+
* @param packetsLost - Packets lost (raw, before FEC recovery)
|
|
295
|
+
*/
|
|
296
|
+
recordSample(bytesReceived, packetsLost) {
|
|
297
|
+
const now = Date.now();
|
|
298
|
+
this.history.push({ timestamp: now, bytesReceived, packetsLost });
|
|
299
|
+
this.history = this.history.filter((s) => now - s.timestamp < this.historyWindow);
|
|
300
|
+
const { measuredBitrate, lossRate: rawLossRate } = this.calculateMetrics();
|
|
301
|
+
const currentQuality = this.getCurrentQuality();
|
|
302
|
+
const effectiveLossRate = this.fecStats.fecEnabled ? this.fecStats.effectiveLossRate : rawLossRate;
|
|
303
|
+
if (now - this.lastQualityChange < this.stabilityPeriod) {
|
|
304
|
+
return;
|
|
305
|
+
}
|
|
306
|
+
if (effectiveLossRate > 0.05 || measuredBitrate < currentQuality.bitrate * this.downgradeThreshold) {
|
|
307
|
+
this.downgrade();
|
|
308
|
+
return;
|
|
309
|
+
}
|
|
310
|
+
if (measuredBitrate > currentQuality.bitrate * this.upgradeThreshold && effectiveLossRate < 0.01 && this.currentQualityIndex < this.ladder.length - 1) {
|
|
311
|
+
this.upgrade();
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
/**
|
|
315
|
+
* Update FEC statistics for ABR decisions
|
|
316
|
+
*
|
|
317
|
+
* Per draft-ramadan-moq-fec-00, ABR should use the effective loss rate
|
|
318
|
+
* (after FEC recovery) rather than raw transport loss rate.
|
|
319
|
+
*
|
|
320
|
+
* @param stats - FEC statistics from the FEC decoder
|
|
321
|
+
*/
|
|
322
|
+
setFecStats(stats) {
|
|
323
|
+
this.fecStats = stats;
|
|
324
|
+
}
|
|
325
|
+
/**
|
|
326
|
+
* Get current FEC statistics
|
|
327
|
+
*/
|
|
328
|
+
getFecStats() {
|
|
329
|
+
return { ...this.fecStats };
|
|
330
|
+
}
|
|
331
|
+
calculateMetrics() {
|
|
332
|
+
if (this.history.length < 2) {
|
|
333
|
+
return { measuredBitrate: 0, lossRate: 0 };
|
|
334
|
+
}
|
|
335
|
+
const totalBytes = this.history.reduce((sum, s) => sum + s.bytesReceived, 0);
|
|
336
|
+
const totalLost = this.history.reduce((sum, s) => sum + s.packetsLost, 0);
|
|
337
|
+
const duration = (this.history[this.history.length - 1].timestamp - this.history[0].timestamp) / 1e3;
|
|
338
|
+
const measuredBitrate = duration > 0 ? totalBytes * 8 / duration : 0;
|
|
339
|
+
const lossRate = totalLost / (this.history.length + totalLost);
|
|
340
|
+
return { measuredBitrate, lossRate };
|
|
341
|
+
}
|
|
342
|
+
upgrade() {
|
|
343
|
+
if (this.currentQualityIndex >= this.ladder.length - 1)
|
|
344
|
+
return;
|
|
345
|
+
this.currentQualityIndex++;
|
|
346
|
+
this.lastQualityChange = Date.now();
|
|
347
|
+
const newQuality = this.getCurrentQuality();
|
|
348
|
+
console.log(`[ABR] Upgrading to ${newQuality.name} (${newQuality.bitrate / 1e6}Mbps)`);
|
|
349
|
+
this.onQualityChange?.(newQuality);
|
|
350
|
+
}
|
|
351
|
+
downgrade() {
|
|
352
|
+
if (this.currentQualityIndex <= 0)
|
|
353
|
+
return;
|
|
354
|
+
this.currentQualityIndex--;
|
|
355
|
+
this.lastQualityChange = Date.now();
|
|
356
|
+
const newQuality = this.getCurrentQuality();
|
|
357
|
+
console.log(`[ABR] Downgrading to ${newQuality.name} (${newQuality.bitrate / 1e6}Mbps)`);
|
|
358
|
+
this.onQualityChange?.(newQuality);
|
|
359
|
+
}
|
|
360
|
+
/**
|
|
361
|
+
* Select a ladder rendition: the caller switched to it (a manual override,
|
|
362
|
+
* or a switch the caller made). Throws for a name the ladder does not carry.
|
|
363
|
+
*/
|
|
364
|
+
setQuality(name) {
|
|
365
|
+
const index = this.ladder.findIndex((q) => q.name === name);
|
|
366
|
+
if (index < 0)
|
|
367
|
+
throw new RangeError(`[ABR] rendition ${name} is not in the ladder`);
|
|
368
|
+
this.currentQualityIndex = index;
|
|
369
|
+
this.lastQualityChange = Date.now();
|
|
370
|
+
this.onQualityChange?.(this.getCurrentQuality());
|
|
371
|
+
}
|
|
372
|
+
/**
|
|
373
|
+
* Get current metrics including both raw and effective loss rates
|
|
374
|
+
*/
|
|
375
|
+
getMetrics() {
|
|
376
|
+
const { measuredBitrate, lossRate: rawLossRate } = this.calculateMetrics();
|
|
377
|
+
return {
|
|
378
|
+
measuredBitrate,
|
|
379
|
+
rawLossRate,
|
|
380
|
+
effectiveLossRate: this.fecStats.fecEnabled ? this.fecStats.effectiveLossRate : rawLossRate,
|
|
381
|
+
fecEnabled: this.fecStats.fecEnabled,
|
|
382
|
+
sampleCount: this.history.length
|
|
383
|
+
};
|
|
384
|
+
}
|
|
385
|
+
/**
|
|
386
|
+
* Reset history and start fresh
|
|
387
|
+
*/
|
|
388
|
+
reset() {
|
|
389
|
+
this.history = [];
|
|
390
|
+
this.lastQualityChange = 0;
|
|
391
|
+
}
|
|
392
|
+
};
|
|
393
|
+
|
|
394
|
+
// node_modules/@blockcast/mmt-transport/dist/multicast-orchestrator.js
|
|
395
|
+
var DEFAULT_DWELL_MS = 3e3;
|
|
396
|
+
var DEFAULT_JOIN_DEADLINE_MS = 5e3;
|
|
397
|
+
var MulticastOrchestrator = class {
|
|
398
|
+
options;
|
|
399
|
+
#state = "stable";
|
|
400
|
+
#current;
|
|
401
|
+
#target;
|
|
402
|
+
#estimate;
|
|
403
|
+
#favored;
|
|
404
|
+
#joinStartedAt = 0;
|
|
405
|
+
/**
|
|
406
|
+
* Latest active-tier send time (NTP short), or undefined before the first.
|
|
407
|
+
* "Latest" is serial order, not the numerically largest value: after the
|
|
408
|
+
* field wraps, the largest value is the stale one.
|
|
409
|
+
*/
|
|
410
|
+
#highWater;
|
|
411
|
+
#dwellMs;
|
|
412
|
+
#joinDeadlineMs;
|
|
413
|
+
constructor(options) {
|
|
414
|
+
this.options = options;
|
|
415
|
+
this.#dwellMs = options.dwellMs ?? DEFAULT_DWELL_MS;
|
|
416
|
+
this.#joinDeadlineMs = options.joinDeadlineMs ?? DEFAULT_JOIN_DEADLINE_MS;
|
|
417
|
+
if (!Number.isFinite(this.#dwellMs) || !Number.isFinite(this.#joinDeadlineMs) || this.#dwellMs <= 0 || this.#joinDeadlineMs <= 0) {
|
|
418
|
+
throw new Error("Multicast dwell and join deadline must be positive");
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
get state() {
|
|
422
|
+
return this.#state;
|
|
423
|
+
}
|
|
424
|
+
get current() {
|
|
425
|
+
return this.#current;
|
|
426
|
+
}
|
|
427
|
+
get target() {
|
|
428
|
+
return this.#target;
|
|
429
|
+
}
|
|
430
|
+
get estimate() {
|
|
431
|
+
return this.#estimate;
|
|
432
|
+
}
|
|
433
|
+
start() {
|
|
434
|
+
this.#current ??= this.options.initialEndpoint ?? selectMulticastTier(this.options.endpoints, void 0);
|
|
435
|
+
return this.#current;
|
|
436
|
+
}
|
|
437
|
+
onGoodput(sample) {
|
|
438
|
+
const bitrate = sample.bitrate;
|
|
439
|
+
if (!this.#current || this.options.adaptive === false)
|
|
440
|
+
return void 0;
|
|
441
|
+
if (this.#state === "joining")
|
|
442
|
+
return this.poll();
|
|
443
|
+
if (bitrate === void 0 || !Number.isFinite(bitrate))
|
|
444
|
+
return void 0;
|
|
445
|
+
this.#estimate = bitrate;
|
|
446
|
+
const desired = selectMulticastTier(this.options.endpoints, bitrate);
|
|
447
|
+
if (!desired)
|
|
448
|
+
return void 0;
|
|
449
|
+
const desiredKey = endpointKey(desired);
|
|
450
|
+
if (desiredKey === endpointKey(this.#current)) {
|
|
451
|
+
if (this.#favored?.source === "goodput")
|
|
452
|
+
this.#favored = void 0;
|
|
453
|
+
return void 0;
|
|
454
|
+
}
|
|
455
|
+
if (typeof desired.bandwidth !== "number" || typeof this.#current.bandwidth !== "number" || desired.bandwidth >= this.#current.bandwidth) {
|
|
456
|
+
if (this.#favored?.source === "goodput")
|
|
457
|
+
this.#favored = void 0;
|
|
458
|
+
return void 0;
|
|
459
|
+
}
|
|
460
|
+
const now = this.#now();
|
|
461
|
+
if (!this.#favored || this.#favored.key !== desiredKey || this.#favored.source !== "goodput") {
|
|
462
|
+
this.#favored = { key: desiredKey, since: now, source: "goodput" };
|
|
463
|
+
return void 0;
|
|
464
|
+
}
|
|
465
|
+
if (now - this.#favored.since < this.#dwellMs)
|
|
466
|
+
return void 0;
|
|
467
|
+
this.#state = "joining";
|
|
468
|
+
this.#target = desired;
|
|
469
|
+
this.#joinStartedAt = now;
|
|
470
|
+
this.#favored = void 0;
|
|
471
|
+
return { kind: "join-target", current: this.#current, target: desired };
|
|
472
|
+
}
|
|
473
|
+
onHeadroom(sample) {
|
|
474
|
+
if (!this.#current || this.options.adaptive === false || this.#state === "joining")
|
|
475
|
+
return void 0;
|
|
476
|
+
if (!sample.healthy) {
|
|
477
|
+
if (this.#favored?.source === "headroom")
|
|
478
|
+
this.#favored = void 0;
|
|
479
|
+
return void 0;
|
|
480
|
+
}
|
|
481
|
+
if (this.#favored?.source === "goodput")
|
|
482
|
+
return void 0;
|
|
483
|
+
const currentBandwidth = this.#current.bandwidth;
|
|
484
|
+
if (typeof currentBandwidth !== "number")
|
|
485
|
+
return void 0;
|
|
486
|
+
const desired = this.options.endpoints.filter((endpoint) => carriesMedia(endpoint) && typeof endpoint.bandwidth === "number" && endpoint.bandwidth > currentBandwidth).sort((a, b) => a.bandwidth - b.bandwidth)[0];
|
|
487
|
+
if (!desired) {
|
|
488
|
+
this.#favored = void 0;
|
|
489
|
+
return void 0;
|
|
490
|
+
}
|
|
491
|
+
const desiredKey = endpointKey(desired);
|
|
492
|
+
if (desiredKey === endpointKey(this.#current)) {
|
|
493
|
+
this.#favored = void 0;
|
|
494
|
+
return void 0;
|
|
495
|
+
}
|
|
496
|
+
const now = this.#now();
|
|
497
|
+
if (!this.#favored || this.#favored.key !== desiredKey) {
|
|
498
|
+
this.#favored = { key: desiredKey, since: now, source: "headroom" };
|
|
499
|
+
return void 0;
|
|
500
|
+
}
|
|
501
|
+
if (now - this.#favored.since < this.#dwellMs)
|
|
502
|
+
return void 0;
|
|
503
|
+
this.#state = "joining";
|
|
504
|
+
this.#target = desired;
|
|
505
|
+
this.#joinStartedAt = now;
|
|
506
|
+
this.#favored = void 0;
|
|
507
|
+
return { kind: "join-target", current: this.#current, target: desired };
|
|
508
|
+
}
|
|
509
|
+
/**
|
|
510
|
+
* Feed a packet from the active tier (to advance the high-water mark) or
|
|
511
|
+
* from the in-flight target (to find a committable RAP).
|
|
512
|
+
*
|
|
513
|
+
* Serial order is only the true order while the compared instants are less
|
|
514
|
+
* than `NTP_SHORT_HALF_RANGE` (2^15 s, ~9.1 h) apart. Feeding every
|
|
515
|
+
* active-tier packet keeps the mark that close to the live stream. A mark
|
|
516
|
+
* refreshed only by commits can fall further behind than that, and a
|
|
517
|
+
* target RAP may then compare as older, so the join is abandoned.
|
|
518
|
+
*/
|
|
519
|
+
onTierPacket(packet) {
|
|
520
|
+
if (!this.#current)
|
|
521
|
+
return void 0;
|
|
522
|
+
const sentAt = tierPacketSendTime(packet);
|
|
523
|
+
if (packet.endpointKey === endpointKey(this.#current) && sentAt !== void 0 && (this.#highWater === void 0 || compareNtpShort(sentAt, this.#highWater) === "after")) {
|
|
524
|
+
this.#highWater = sentAt;
|
|
525
|
+
}
|
|
526
|
+
if (this.#state !== "joining" || !this.#target)
|
|
527
|
+
return void 0;
|
|
528
|
+
const expired = this.poll();
|
|
529
|
+
if (expired)
|
|
530
|
+
return expired;
|
|
531
|
+
if (packet.endpointKey !== endpointKey(this.#target) || !packet.keyframe || !packet.hasValidCodecConfig || sentAt === void 0 || !this.#reachesHighWater(sentAt))
|
|
532
|
+
return void 0;
|
|
533
|
+
const command = {
|
|
534
|
+
kind: "commit-swap",
|
|
535
|
+
target: this.#target,
|
|
536
|
+
old: this.#current
|
|
537
|
+
};
|
|
538
|
+
this.#highWater = sentAt;
|
|
539
|
+
this.#current = this.#target;
|
|
540
|
+
this.#target = void 0;
|
|
541
|
+
this.#state = "stable";
|
|
542
|
+
return command;
|
|
543
|
+
}
|
|
544
|
+
poll() {
|
|
545
|
+
if (this.#state !== "joining" || !this.#target || !this.#current)
|
|
546
|
+
return void 0;
|
|
547
|
+
if (this.#now() - this.#joinStartedAt < this.#joinDeadlineMs)
|
|
548
|
+
return void 0;
|
|
549
|
+
return this.cancel();
|
|
550
|
+
}
|
|
551
|
+
cancel() {
|
|
552
|
+
if (this.#state !== "joining" || !this.#target || !this.#current)
|
|
553
|
+
return void 0;
|
|
554
|
+
const command = {
|
|
555
|
+
kind: "abandon-join",
|
|
556
|
+
current: this.#current,
|
|
557
|
+
target: this.#target
|
|
558
|
+
};
|
|
559
|
+
this.#target = void 0;
|
|
560
|
+
this.#state = "stable";
|
|
561
|
+
return command;
|
|
562
|
+
}
|
|
563
|
+
/**
|
|
564
|
+
* Whether a target RAP sent at `sentAt` is at or beyond the active tier's
|
|
565
|
+
* high-water mark. A pair exactly half the NTP-short space apart has no
|
|
566
|
+
* serial order, so it cannot show that and does not commit.
|
|
567
|
+
*/
|
|
568
|
+
#reachesHighWater(sentAt) {
|
|
569
|
+
if (this.#highWater === void 0)
|
|
570
|
+
return true;
|
|
571
|
+
const order = compareNtpShort(sentAt, this.#highWater);
|
|
572
|
+
return order === "after" || order === "equal";
|
|
573
|
+
}
|
|
574
|
+
#now() {
|
|
575
|
+
const now = this.options.now();
|
|
576
|
+
if (!Number.isFinite(now))
|
|
577
|
+
throw new Error("Multicast orchestrator clock must be finite");
|
|
578
|
+
return now;
|
|
579
|
+
}
|
|
580
|
+
};
|
|
581
|
+
function tierPacketSendTime(packet) {
|
|
582
|
+
if (!Number.isFinite(packet.timestamp))
|
|
583
|
+
return void 0;
|
|
584
|
+
if (!isNtpShort(packet.timestamp)) {
|
|
585
|
+
throw new RangeError(`Multicast tier packet timestamp must be an MMTP NTP-short send time, got ${packet.timestamp}`);
|
|
586
|
+
}
|
|
587
|
+
return packet.timestamp;
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
// node_modules/@blockcast/mmt-transport/dist/endpoint-config.js
|
|
591
|
+
function toWindowMulticastSubscribeConfig(input) {
|
|
592
|
+
if (input.mode !== void 0 && !["auto", "native", "tunnel"].includes(input.mode)) {
|
|
593
|
+
throw new TypeError(`Unsupported multicast mode: ${String(input.mode)}`);
|
|
594
|
+
}
|
|
595
|
+
if (input.packetIds !== void 0 && !Array.isArray(input.packetIds)) {
|
|
596
|
+
throw new TypeError(`multicast packetIds must be an array, got ${input.packetIds === null ? "null" : typeof input.packetIds}`);
|
|
597
|
+
}
|
|
598
|
+
if (input.packetIds?.some((id) => !isMulticastTrackPacketId(id)) || input.tracks?.some((track) => !isMulticastTrackPacketId(track.packetId))) {
|
|
599
|
+
throw new RangeError(`multicast packetId must be an integer from 1 to ${MMTP_PACKET_ID_MAX}; packetId 0 is the MMTP signaling flow and is never a track's`);
|
|
600
|
+
}
|
|
601
|
+
const base = {
|
|
602
|
+
group: input.group,
|
|
603
|
+
port: input.port,
|
|
604
|
+
...input.source?.trim() ? { source: input.source.trim() } : {},
|
|
605
|
+
consume: "bytes",
|
|
606
|
+
...input.packetIds?.length ? { packetIds: [...input.packetIds] } : {}
|
|
607
|
+
};
|
|
608
|
+
const relay = input.amtRelay?.trim();
|
|
609
|
+
const wantsAmt = input.mode === "tunnel" || input.mode !== "native" && relay !== void 0 && relay !== "";
|
|
610
|
+
if (!wantsAmt) {
|
|
611
|
+
const native = {
|
|
612
|
+
...base,
|
|
613
|
+
origin: "ip-multicast",
|
|
614
|
+
...input.tracks?.length ? { tracks: input.tracks.map(({ name, packetId }) => ({ name, packetId })) } : {}
|
|
615
|
+
};
|
|
616
|
+
return native;
|
|
617
|
+
}
|
|
618
|
+
const relayPort = input.amtRelayPort;
|
|
619
|
+
if (!relay || relayPort === void 0 || !isPositivePort(relayPort)) {
|
|
620
|
+
throw new TypeError("AMT requires a resolved relay and a valid relay port");
|
|
621
|
+
}
|
|
622
|
+
return {
|
|
623
|
+
...base,
|
|
624
|
+
origin: "amt",
|
|
625
|
+
relay,
|
|
626
|
+
relayPort
|
|
627
|
+
};
|
|
628
|
+
}
|
|
629
|
+
function isValidMulticastGroupAddress(address) {
|
|
630
|
+
const value = address?.trim().toLowerCase();
|
|
631
|
+
if (!value)
|
|
632
|
+
return false;
|
|
633
|
+
if (value.startsWith("ff"))
|
|
634
|
+
return isValidIpv6MulticastAddress(value);
|
|
635
|
+
const parts = value.split(".");
|
|
636
|
+
if (parts.length !== 4)
|
|
637
|
+
return false;
|
|
638
|
+
const octets = parts.map((part) => {
|
|
639
|
+
if (!/^\d+$/.test(part))
|
|
640
|
+
return Number.NaN;
|
|
641
|
+
const octet = Number(part);
|
|
642
|
+
return Number.isInteger(octet) && octet >= 0 && octet <= 255 ? octet : Number.NaN;
|
|
643
|
+
});
|
|
644
|
+
return octets.every(Number.isInteger) && octets[0] >= 224 && octets[0] <= 239;
|
|
645
|
+
}
|
|
646
|
+
function normalizeMulticastEndpoint(endpoint) {
|
|
647
|
+
const groupAddress = endpoint.groupAddress.trim().toLowerCase();
|
|
648
|
+
const sourceAddress = endpoint.sourceAddress?.trim().toLowerCase() || void 0;
|
|
649
|
+
const amtRelay = endpoint.amtRelay?.trim().toLowerCase() || void 0;
|
|
650
|
+
if (!isValidMulticastGroupAddress(groupAddress) || !isPositivePort(endpoint.port))
|
|
651
|
+
return void 0;
|
|
652
|
+
if (endpoint.mode && !["auto", "native", "tunnel"].includes(endpoint.mode))
|
|
653
|
+
return void 0;
|
|
654
|
+
if (endpoint.amtRelayPort !== void 0 && !isPositivePort(endpoint.amtRelayPort))
|
|
655
|
+
return void 0;
|
|
656
|
+
return {
|
|
657
|
+
groupAddress,
|
|
658
|
+
...sourceAddress ? { sourceAddress } : {},
|
|
659
|
+
port: endpoint.port,
|
|
660
|
+
...endpoint.mode ? { mode: endpoint.mode } : {},
|
|
661
|
+
...amtRelay ? { amtRelay } : {},
|
|
662
|
+
...endpoint.amtRelayPort ? { amtRelayPort: endpoint.amtRelayPort } : {}
|
|
663
|
+
};
|
|
664
|
+
}
|
|
665
|
+
function multicastEndpointKey(endpoint) {
|
|
666
|
+
const normalized = normalizeMulticastEndpoint(endpoint);
|
|
667
|
+
if (!normalized)
|
|
668
|
+
throw new Error("Invalid multicast endpoint");
|
|
669
|
+
return JSON.stringify([
|
|
670
|
+
normalized.sourceAddress ?? "",
|
|
671
|
+
normalized.groupAddress,
|
|
672
|
+
normalized.port,
|
|
673
|
+
normalized.mode ?? "auto",
|
|
674
|
+
normalized.amtRelay ?? "",
|
|
675
|
+
normalized.amtRelayPort ?? ""
|
|
676
|
+
]);
|
|
677
|
+
}
|
|
678
|
+
function isMulticastTrackPacketId(value) {
|
|
679
|
+
return Number.isInteger(value) && value > MMTP_SIGNALING_PACKET_ID && value <= MMTP_PACKET_ID_MAX;
|
|
680
|
+
}
|
|
681
|
+
function isValidIpv6MulticastAddress(value) {
|
|
682
|
+
if (!/^[0-9a-f:]+$/.test(value))
|
|
683
|
+
return false;
|
|
684
|
+
const compressed = value.includes("::");
|
|
685
|
+
if (value.indexOf("::") !== value.lastIndexOf("::"))
|
|
686
|
+
return false;
|
|
687
|
+
const groups = value.split(":").filter(Boolean);
|
|
688
|
+
if (groups.length === 0 || groups.length > 8)
|
|
689
|
+
return false;
|
|
690
|
+
if (!compressed && groups.length !== 8)
|
|
691
|
+
return false;
|
|
692
|
+
return groups.every((group) => /^[0-9a-f]{1,4}$/.test(group));
|
|
693
|
+
}
|
|
694
|
+
function isPositivePort(port) {
|
|
695
|
+
return Number.isInteger(port) && port > 0 && port <= 65535;
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
// node_modules/@blockcast/mmt-transport/dist/packet-batch.js
|
|
699
|
+
function subscriptionDeliversPacketBatches(subscription) {
|
|
700
|
+
return subscription.eventTypes?.includes("packets") === true;
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
// node_modules/@blockcast/mmt-transport/dist/multicast-subscription-pool.js
|
|
704
|
+
var MulticastSubscriptionPool = class {
|
|
705
|
+
#entries = /* @__PURE__ */ new Map();
|
|
706
|
+
#gatewayIds = /* @__PURE__ */ new WeakMap();
|
|
707
|
+
#nextGatewayId = 1;
|
|
708
|
+
async acquire(gateway, config, consumer) {
|
|
709
|
+
const endpoint = normalizeMulticastEndpoint({
|
|
710
|
+
groupAddress: config.group,
|
|
711
|
+
sourceAddress: config.source,
|
|
712
|
+
port: config.port,
|
|
713
|
+
mode: config.mode,
|
|
714
|
+
amtRelay: config.amtRelay,
|
|
715
|
+
amtRelayPort: config.amtRelayPort
|
|
716
|
+
});
|
|
717
|
+
if (!endpoint)
|
|
718
|
+
throw new Error("Invalid multicast endpoint");
|
|
719
|
+
if (typeof gateway.subscribe !== "function")
|
|
720
|
+
throw new Error("window.multicast.subscribe unavailable");
|
|
721
|
+
const key = multicastEndpointKey(endpoint);
|
|
722
|
+
const internalKey = `${this.#gatewayId(gateway)}|${key}`;
|
|
723
|
+
let record = this.#entries.get(internalKey);
|
|
724
|
+
if (!record) {
|
|
725
|
+
record = { claims: 0, entry: Promise.resolve(null) };
|
|
726
|
+
const createdRecord = record;
|
|
727
|
+
record.entry = this.#createEntry(gateway, endpoint, (entry2) => {
|
|
728
|
+
if (this.#entries.get(internalKey) === createdRecord)
|
|
729
|
+
this.#entries.delete(internalKey);
|
|
730
|
+
this.#detach(entry2);
|
|
731
|
+
}).catch((error) => {
|
|
732
|
+
if (this.#entries.get(internalKey) === createdRecord)
|
|
733
|
+
this.#entries.delete(internalKey);
|
|
734
|
+
throw error;
|
|
735
|
+
});
|
|
736
|
+
this.#entries.set(internalKey, record);
|
|
737
|
+
}
|
|
738
|
+
record.claims++;
|
|
739
|
+
let entry;
|
|
740
|
+
try {
|
|
741
|
+
entry = await record.entry;
|
|
742
|
+
} catch (error) {
|
|
743
|
+
record.claims--;
|
|
744
|
+
throw error;
|
|
745
|
+
}
|
|
746
|
+
if (entry.closed) {
|
|
747
|
+
record.claims--;
|
|
748
|
+
return this.acquire(gateway, config, consumer);
|
|
749
|
+
}
|
|
750
|
+
const token = Symbol(key);
|
|
751
|
+
entry.consumers.set(token, consumer);
|
|
752
|
+
let released = false;
|
|
753
|
+
return {
|
|
754
|
+
key,
|
|
755
|
+
release: async () => {
|
|
756
|
+
if (released)
|
|
757
|
+
return;
|
|
758
|
+
released = true;
|
|
759
|
+
entry.consumers.delete(token);
|
|
760
|
+
record.claims--;
|
|
761
|
+
if (record.claims > 0)
|
|
762
|
+
return;
|
|
763
|
+
if (this.#entries.get(internalKey) === record)
|
|
764
|
+
this.#entries.delete(internalKey);
|
|
765
|
+
this.#detach(entry);
|
|
766
|
+
if (!entry.closed)
|
|
767
|
+
await entry.subscription.unsubscribe();
|
|
768
|
+
}
|
|
769
|
+
};
|
|
770
|
+
}
|
|
771
|
+
get size() {
|
|
772
|
+
return this.#entries.size;
|
|
773
|
+
}
|
|
774
|
+
async #createEntry(gateway, endpoint, onClosed) {
|
|
775
|
+
const subscription = await gateway.subscribe(toWindowMulticastSubscribeConfig({
|
|
776
|
+
group: endpoint.groupAddress,
|
|
777
|
+
...endpoint.sourceAddress ? { source: endpoint.sourceAddress } : {},
|
|
778
|
+
port: endpoint.port,
|
|
779
|
+
...endpoint.mode ? { mode: endpoint.mode } : {},
|
|
780
|
+
...endpoint.amtRelay ? { amtRelay: endpoint.amtRelay } : {},
|
|
781
|
+
...endpoint.amtRelayPort ? { amtRelayPort: endpoint.amtRelayPort } : {}
|
|
782
|
+
}));
|
|
783
|
+
const consumers = /* @__PURE__ */ new Map();
|
|
784
|
+
let entry;
|
|
785
|
+
const packetListener = subscriptionDeliversPacketBatches(subscription) ? {
|
|
786
|
+
type: "packets",
|
|
787
|
+
listener: (event) => {
|
|
788
|
+
for (const data of event.detail) {
|
|
789
|
+
this.#dispatch(consumers, (consumer) => consumer.onBytes(data));
|
|
790
|
+
}
|
|
791
|
+
}
|
|
792
|
+
} : {
|
|
793
|
+
type: "bytes",
|
|
794
|
+
listener: (event) => {
|
|
795
|
+
this.#dispatch(consumers, (consumer) => consumer.onBytes(event.detail));
|
|
796
|
+
}
|
|
797
|
+
};
|
|
798
|
+
const stateListener = (event) => {
|
|
799
|
+
this.#dispatch(consumers, (consumer) => consumer.onState?.(event.detail));
|
|
800
|
+
if (event.detail === "closed" && !entry.closed) {
|
|
801
|
+
entry.closed = true;
|
|
802
|
+
onClosed(entry);
|
|
803
|
+
}
|
|
804
|
+
};
|
|
805
|
+
const errorListener = (event) => {
|
|
806
|
+
const error = event.detail instanceof Error ? event.detail : new Error(String(event.detail));
|
|
807
|
+
this.#dispatch(consumers, (consumer) => consumer.onError?.(error));
|
|
808
|
+
};
|
|
809
|
+
entry = { subscription, consumers, packetListener, stateListener, errorListener, closed: false };
|
|
810
|
+
if (packetListener.type === "packets") {
|
|
811
|
+
subscription.addEventListener("packets", packetListener.listener);
|
|
812
|
+
} else {
|
|
813
|
+
subscription.addEventListener("bytes", packetListener.listener);
|
|
814
|
+
}
|
|
815
|
+
subscription.addEventListener("state", stateListener);
|
|
816
|
+
subscription.addEventListener("error", errorListener);
|
|
817
|
+
return entry;
|
|
818
|
+
}
|
|
819
|
+
#gatewayId(gateway) {
|
|
820
|
+
const object = gateway;
|
|
821
|
+
let id = this.#gatewayIds.get(object);
|
|
822
|
+
if (!id) {
|
|
823
|
+
id = this.#nextGatewayId++;
|
|
824
|
+
this.#gatewayIds.set(object, id);
|
|
825
|
+
}
|
|
826
|
+
return id;
|
|
827
|
+
}
|
|
828
|
+
#detach(entry) {
|
|
829
|
+
const { packetListener } = entry;
|
|
830
|
+
if (packetListener.type === "packets") {
|
|
831
|
+
entry.subscription.removeEventListener("packets", packetListener.listener);
|
|
832
|
+
} else {
|
|
833
|
+
entry.subscription.removeEventListener("bytes", packetListener.listener);
|
|
834
|
+
}
|
|
835
|
+
entry.subscription.removeEventListener("state", entry.stateListener);
|
|
836
|
+
entry.subscription.removeEventListener("error", entry.errorListener);
|
|
837
|
+
}
|
|
838
|
+
#dispatch(consumers, callback) {
|
|
839
|
+
for (const consumer of consumers.values()) {
|
|
840
|
+
try {
|
|
841
|
+
callback(consumer);
|
|
842
|
+
} catch (error) {
|
|
843
|
+
console.error("[Transport] multicast consumer callback failed", error);
|
|
844
|
+
}
|
|
845
|
+
}
|
|
846
|
+
}
|
|
847
|
+
};
|
|
848
|
+
var multicastSubscriptionPool = new MulticastSubscriptionPool();
|
|
849
|
+
|
|
850
|
+
// node_modules/@blockcast/mmt-transport/dist/transport-manager.js
|
|
851
|
+
var TransportManager = class {
|
|
852
|
+
state = {
|
|
853
|
+
mode: "disconnected",
|
|
854
|
+
connected: false,
|
|
855
|
+
packetsReceived: 0,
|
|
856
|
+
bytesReceived: 0,
|
|
857
|
+
lastPacketTime: 0
|
|
858
|
+
};
|
|
859
|
+
capabilities = null;
|
|
860
|
+
subscription = null;
|
|
861
|
+
driad = new DRIADDiscovery();
|
|
862
|
+
/** Built by `connect()` from the caller's ladder; there is no built-in one. */
|
|
863
|
+
abr = null;
|
|
864
|
+
multicastOrchestrator = null;
|
|
865
|
+
onOrchestratorCommand;
|
|
866
|
+
connectionGeneration = 0;
|
|
867
|
+
onPacket;
|
|
868
|
+
onError;
|
|
869
|
+
onStateChange;
|
|
870
|
+
getWindow() {
|
|
871
|
+
return typeof window === "undefined" ? void 0 : window;
|
|
872
|
+
}
|
|
873
|
+
getWindowMulticastGateway() {
|
|
874
|
+
return this.getWindow()?.multicast;
|
|
875
|
+
}
|
|
876
|
+
async getGatewayCapabilities(gateway) {
|
|
877
|
+
if (!gateway?.capabilities)
|
|
878
|
+
return void 0;
|
|
879
|
+
try {
|
|
880
|
+
return typeof gateway.capabilities === "function" ? await gateway.capabilities() : gateway.capabilities;
|
|
881
|
+
} catch (error) {
|
|
882
|
+
console.warn("[Transport] window.multicast capabilities failed:", error);
|
|
883
|
+
return void 0;
|
|
884
|
+
}
|
|
885
|
+
}
|
|
886
|
+
async gatewaySupportsBytes(gateway) {
|
|
887
|
+
if (typeof gateway?.subscribe !== "function")
|
|
888
|
+
return false;
|
|
889
|
+
if (!gateway.capabilities)
|
|
890
|
+
return true;
|
|
891
|
+
const capabilities = await this.getGatewayCapabilities(gateway);
|
|
892
|
+
if (!capabilities)
|
|
893
|
+
return false;
|
|
894
|
+
const consumeModes = capabilities.consumeModes;
|
|
895
|
+
return !consumeModes || consumeModes.includes("bytes");
|
|
896
|
+
}
|
|
897
|
+
/**
|
|
898
|
+
* Detect available transport capabilities
|
|
899
|
+
*/
|
|
900
|
+
async detectCapabilities() {
|
|
901
|
+
if (this.capabilities)
|
|
902
|
+
return this.capabilities;
|
|
903
|
+
const gateway = this.getWindowMulticastGateway();
|
|
904
|
+
const hasGatewayBytes = await this.gatewaySupportsBytes(gateway);
|
|
905
|
+
const win = this.getWindow();
|
|
906
|
+
this.capabilities = {
|
|
907
|
+
nativeSSM: hasGatewayBytes,
|
|
908
|
+
nativeIGMP: hasGatewayBytes,
|
|
909
|
+
directSockets: typeof win?.UDPSocket !== "undefined",
|
|
910
|
+
amt: false,
|
|
911
|
+
driad: true,
|
|
912
|
+
// DRIAD uses HTTP proxy, always available
|
|
913
|
+
fecDecode: false
|
|
914
|
+
};
|
|
915
|
+
console.log("[Transport] Capabilities:", this.capabilities);
|
|
916
|
+
return this.capabilities;
|
|
917
|
+
}
|
|
918
|
+
/**
|
|
919
|
+
* Connect using best available transport
|
|
920
|
+
*
|
|
921
|
+
* Promotion hierarchy:
|
|
922
|
+
* 1. window.multicast gateway bytes
|
|
923
|
+
* 2. AMT proxy (specified or discovered via DRIAD)
|
|
924
|
+
* 3. MOQ WebTransport (final fallback)
|
|
925
|
+
*
|
|
926
|
+
* The ABR ladder (`abrLadder`) and initial rendition (`initialQuality`)
|
|
927
|
+
* are checked first, before anything is torn down. A config that fails is
|
|
928
|
+
* refused with the same shape as an invalid endpoint, the error to the
|
|
929
|
+
* error callback and `false` resolved; unlike one, it is caught before the
|
|
930
|
+
* live subscription closes, so a refused reconnect leaves that subscription,
|
|
931
|
+
* its state and any connect already in flight untouched.
|
|
932
|
+
*/
|
|
933
|
+
async connect(config) {
|
|
934
|
+
let abr;
|
|
935
|
+
try {
|
|
936
|
+
abr = new AdaptiveBitrateController({
|
|
937
|
+
ladder: config.abrLadder,
|
|
938
|
+
initialQuality: config.initialQuality?.name
|
|
939
|
+
});
|
|
940
|
+
} catch (error) {
|
|
941
|
+
const refused = error instanceof Error ? error : new Error(String(error));
|
|
942
|
+
if (this.onError)
|
|
943
|
+
this.onError(refused);
|
|
944
|
+
else
|
|
945
|
+
console.error("[Transport] ABR config refused, not connecting:", refused);
|
|
946
|
+
return false;
|
|
947
|
+
}
|
|
948
|
+
const generation = ++this.connectionGeneration;
|
|
949
|
+
this.subscription?.close();
|
|
950
|
+
this.subscription = null;
|
|
951
|
+
this.abr = null;
|
|
952
|
+
this.state = {
|
|
953
|
+
mode: "disconnected",
|
|
954
|
+
connected: false,
|
|
955
|
+
packetsReceived: 0,
|
|
956
|
+
bytesReceived: 0,
|
|
957
|
+
lastPacketTime: 0
|
|
958
|
+
};
|
|
959
|
+
this.onStateChange?.(this.state);
|
|
960
|
+
const endpoint = normalizeMulticastEndpoint({
|
|
961
|
+
groupAddress: config.group,
|
|
962
|
+
sourceAddress: config.source,
|
|
963
|
+
port: config.port,
|
|
964
|
+
mode: config.mode,
|
|
965
|
+
amtRelay: config.amtRelay,
|
|
966
|
+
amtRelayPort: config.amtRelayPort
|
|
967
|
+
});
|
|
968
|
+
if (!endpoint) {
|
|
969
|
+
const error = new Error("Invalid multicast endpoint configuration");
|
|
970
|
+
this.onError?.(error);
|
|
971
|
+
return false;
|
|
972
|
+
}
|
|
973
|
+
config = {
|
|
974
|
+
...config,
|
|
975
|
+
group: endpoint.groupAddress,
|
|
976
|
+
source: endpoint.sourceAddress ?? "",
|
|
977
|
+
mode: endpoint.mode,
|
|
978
|
+
amtRelay: endpoint.amtRelay,
|
|
979
|
+
amtRelayPort: endpoint.amtRelayPort
|
|
980
|
+
};
|
|
981
|
+
const caps = await this.detectCapabilities();
|
|
982
|
+
if (generation !== this.connectionGeneration)
|
|
983
|
+
return false;
|
|
984
|
+
this.abr = abr;
|
|
985
|
+
abr.setCallback((quality) => {
|
|
986
|
+
this.state.quality = quality;
|
|
987
|
+
this.subscription?.onqualitychange?.(quality);
|
|
988
|
+
this.onStateChange?.(this.state);
|
|
989
|
+
});
|
|
990
|
+
let connected = false;
|
|
991
|
+
if (caps.nativeSSM || caps.nativeIGMP) {
|
|
992
|
+
console.log("[Transport] Attempting window.multicast...");
|
|
993
|
+
connected = await this.connectWindowMulticast(config, generation);
|
|
994
|
+
if (connected) {
|
|
995
|
+
if (generation !== this.connectionGeneration)
|
|
996
|
+
return false;
|
|
997
|
+
this.state.mode = "multicast";
|
|
998
|
+
return true;
|
|
999
|
+
}
|
|
1000
|
+
}
|
|
1001
|
+
if (!connected) {
|
|
1002
|
+
console.log("[Transport] Attempting AMT tunnel...");
|
|
1003
|
+
connected = await this.connectAmt(config, generation);
|
|
1004
|
+
if (connected) {
|
|
1005
|
+
if (generation !== this.connectionGeneration)
|
|
1006
|
+
return false;
|
|
1007
|
+
this.state.mode = "amt";
|
|
1008
|
+
return true;
|
|
1009
|
+
}
|
|
1010
|
+
}
|
|
1011
|
+
console.log("[Transport] All multicast transports failed, falling back to MOQ");
|
|
1012
|
+
return false;
|
|
1013
|
+
}
|
|
1014
|
+
/**
|
|
1015
|
+
* Connect via the lowercase window.multicast bytes gateway.
|
|
1016
|
+
*/
|
|
1017
|
+
async connectWindowMulticast(config, generation) {
|
|
1018
|
+
try {
|
|
1019
|
+
const gateway = this.getWindowMulticastGateway();
|
|
1020
|
+
if (typeof gateway?.subscribe !== "function")
|
|
1021
|
+
return false;
|
|
1022
|
+
const lease = await multicastSubscriptionPool.acquire(gateway, config, {
|
|
1023
|
+
onBytes: (data) => {
|
|
1024
|
+
if (generation === this.connectionGeneration)
|
|
1025
|
+
this.handlePacket(data);
|
|
1026
|
+
},
|
|
1027
|
+
onState: (state) => {
|
|
1028
|
+
if (generation !== this.connectionGeneration)
|
|
1029
|
+
return;
|
|
1030
|
+
this.state.connected = state !== "closed";
|
|
1031
|
+
this.onStateChange?.(this.state);
|
|
1032
|
+
},
|
|
1033
|
+
onError: (error) => {
|
|
1034
|
+
if (generation !== this.connectionGeneration)
|
|
1035
|
+
return;
|
|
1036
|
+
console.error("[Transport] window.multicast error:", error);
|
|
1037
|
+
this.onError?.(error);
|
|
1038
|
+
}
|
|
1039
|
+
});
|
|
1040
|
+
if (generation !== this.connectionGeneration) {
|
|
1041
|
+
await lease.release();
|
|
1042
|
+
return false;
|
|
1043
|
+
}
|
|
1044
|
+
this.subscription = {
|
|
1045
|
+
id: lease.key,
|
|
1046
|
+
config,
|
|
1047
|
+
close: () => {
|
|
1048
|
+
void lease.release();
|
|
1049
|
+
}
|
|
1050
|
+
};
|
|
1051
|
+
this.state = {
|
|
1052
|
+
mode: "multicast",
|
|
1053
|
+
connected: true,
|
|
1054
|
+
group: config.group,
|
|
1055
|
+
source: config.source,
|
|
1056
|
+
quality: this.requireAbr().getCurrentQuality(),
|
|
1057
|
+
packetsReceived: 0,
|
|
1058
|
+
bytesReceived: 0,
|
|
1059
|
+
lastPacketTime: 0
|
|
1060
|
+
};
|
|
1061
|
+
console.log(`[Transport] Connected via window.multicast: (${config.source}, ${config.group})`);
|
|
1062
|
+
this.onStateChange?.(this.state);
|
|
1063
|
+
return true;
|
|
1064
|
+
} catch (error) {
|
|
1065
|
+
console.warn("[Transport] window.multicast failed:", error);
|
|
1066
|
+
return false;
|
|
1067
|
+
}
|
|
1068
|
+
}
|
|
1069
|
+
/**
|
|
1070
|
+
* Connect via AMT proxy (with optional DRIAD discovery)
|
|
1071
|
+
*/
|
|
1072
|
+
async connectAmt(config, generation) {
|
|
1073
|
+
try {
|
|
1074
|
+
let amtRelay = config.amtRelay;
|
|
1075
|
+
if (!amtRelay && config.driadEnabled !== false && config.source) {
|
|
1076
|
+
console.log(`[Transport] Discovering AMT relay via DRIAD for source ${config.source}...`);
|
|
1077
|
+
const relays = await this.driad.discoverRelays(config.source);
|
|
1078
|
+
if (generation !== this.connectionGeneration)
|
|
1079
|
+
return false;
|
|
1080
|
+
const bestRelay = await this.driad.selectBestRelay(relays);
|
|
1081
|
+
if (generation !== this.connectionGeneration)
|
|
1082
|
+
return false;
|
|
1083
|
+
if (bestRelay) {
|
|
1084
|
+
amtRelay = bestRelay.host;
|
|
1085
|
+
this.state.amtRelay = amtRelay;
|
|
1086
|
+
}
|
|
1087
|
+
}
|
|
1088
|
+
if (!amtRelay) {
|
|
1089
|
+
console.warn("[Transport] No AMT relay available (DRIAD requires source address for SSM)");
|
|
1090
|
+
return false;
|
|
1091
|
+
}
|
|
1092
|
+
return await this.connectAmtProxy(config, amtRelay, generation);
|
|
1093
|
+
} catch (error) {
|
|
1094
|
+
console.warn("[Transport] AMT connection failed:", error);
|
|
1095
|
+
return false;
|
|
1096
|
+
}
|
|
1097
|
+
}
|
|
1098
|
+
/**
|
|
1099
|
+
* Connect via WebSocket-based AMT proxy (browser fallback)
|
|
1100
|
+
*/
|
|
1101
|
+
async connectAmtProxy(config, amtRelay, generation) {
|
|
1102
|
+
try {
|
|
1103
|
+
const relayHost = amtRelay.includes(":") && !amtRelay.startsWith("[") ? `[${amtRelay}]` : amtRelay;
|
|
1104
|
+
const wsUrl = new URL(`wss://${relayHost}/amt-proxy`);
|
|
1105
|
+
wsUrl.searchParams.set("group", config.group);
|
|
1106
|
+
wsUrl.searchParams.set("source", config.source || "");
|
|
1107
|
+
wsUrl.searchParams.set("port", String(config.port));
|
|
1108
|
+
const ws = new WebSocket(wsUrl);
|
|
1109
|
+
return new Promise((resolve, reject) => {
|
|
1110
|
+
ws.binaryType = "arraybuffer";
|
|
1111
|
+
const timeout = setTimeout(() => {
|
|
1112
|
+
ws.close();
|
|
1113
|
+
reject(new Error("AMT proxy connection timeout"));
|
|
1114
|
+
}, 1e4);
|
|
1115
|
+
ws.onopen = () => {
|
|
1116
|
+
clearTimeout(timeout);
|
|
1117
|
+
if (generation !== this.connectionGeneration) {
|
|
1118
|
+
ws.close();
|
|
1119
|
+
resolve(false);
|
|
1120
|
+
return;
|
|
1121
|
+
}
|
|
1122
|
+
console.log(`[Transport] Connected to AMT proxy: ${amtRelay}`);
|
|
1123
|
+
this.subscription = {
|
|
1124
|
+
id: `amt-proxy-${Date.now()}`,
|
|
1125
|
+
config,
|
|
1126
|
+
close: () => ws.close()
|
|
1127
|
+
};
|
|
1128
|
+
this.state = {
|
|
1129
|
+
mode: "amt",
|
|
1130
|
+
connected: true,
|
|
1131
|
+
group: config.group,
|
|
1132
|
+
source: config.source,
|
|
1133
|
+
amtRelay,
|
|
1134
|
+
quality: this.requireAbr().getCurrentQuality(),
|
|
1135
|
+
packetsReceived: 0,
|
|
1136
|
+
bytesReceived: 0,
|
|
1137
|
+
lastPacketTime: 0
|
|
1138
|
+
};
|
|
1139
|
+
this.onStateChange?.(this.state);
|
|
1140
|
+
resolve(true);
|
|
1141
|
+
};
|
|
1142
|
+
ws.onmessage = (event) => {
|
|
1143
|
+
if (generation === this.connectionGeneration && event.data instanceof ArrayBuffer) {
|
|
1144
|
+
this.handlePacket(event.data);
|
|
1145
|
+
}
|
|
1146
|
+
};
|
|
1147
|
+
ws.onerror = (error) => {
|
|
1148
|
+
clearTimeout(timeout);
|
|
1149
|
+
if (generation !== this.connectionGeneration) {
|
|
1150
|
+
resolve(false);
|
|
1151
|
+
return;
|
|
1152
|
+
}
|
|
1153
|
+
console.error("[Transport] AMT proxy error:", error);
|
|
1154
|
+
reject(error);
|
|
1155
|
+
};
|
|
1156
|
+
ws.onclose = () => {
|
|
1157
|
+
clearTimeout(timeout);
|
|
1158
|
+
if (generation !== this.connectionGeneration) {
|
|
1159
|
+
resolve(false);
|
|
1160
|
+
return;
|
|
1161
|
+
}
|
|
1162
|
+
this.state.connected = false;
|
|
1163
|
+
this.onStateChange?.(this.state);
|
|
1164
|
+
resolve(false);
|
|
1165
|
+
};
|
|
1166
|
+
});
|
|
1167
|
+
} catch (error) {
|
|
1168
|
+
console.warn("[Transport] AMT proxy failed:", error);
|
|
1169
|
+
return false;
|
|
1170
|
+
}
|
|
1171
|
+
}
|
|
1172
|
+
/**
|
|
1173
|
+
* Handle received packet
|
|
1174
|
+
*/
|
|
1175
|
+
handlePacket(data) {
|
|
1176
|
+
this.state.packetsReceived++;
|
|
1177
|
+
this.state.bytesReceived += data.byteLength;
|
|
1178
|
+
this.state.lastPacketTime = Date.now();
|
|
1179
|
+
this.abr?.recordSample(data.byteLength, 0);
|
|
1180
|
+
this.onPacket?.(data);
|
|
1181
|
+
}
|
|
1182
|
+
/**
|
|
1183
|
+
* Get current transport state
|
|
1184
|
+
*/
|
|
1185
|
+
getState() {
|
|
1186
|
+
return { ...this.state };
|
|
1187
|
+
}
|
|
1188
|
+
/**
|
|
1189
|
+
* The rung of the live connection's `abrLadder` ABR is on; `null` when
|
|
1190
|
+
* there is no ladder (before `connect(config)` succeeds, after
|
|
1191
|
+
* `disconnect()`, or after a reconnect that was torn down and bailed).
|
|
1192
|
+
*/
|
|
1193
|
+
getQuality() {
|
|
1194
|
+
return this.abr?.getCurrentQuality() ?? null;
|
|
1195
|
+
}
|
|
1196
|
+
/**
|
|
1197
|
+
* Force the rung named `name` of the live connection's `abrLadder`.
|
|
1198
|
+
* Throws `Error` when there is no ladder (see `getQuality()`), and
|
|
1199
|
+
* `RangeError` when the ladder has no rung of that name; it never falls
|
|
1200
|
+
* back to another rung.
|
|
1201
|
+
*/
|
|
1202
|
+
setQuality(name) {
|
|
1203
|
+
this.requireAbr().setQuality(name);
|
|
1204
|
+
}
|
|
1205
|
+
/** The controller `connect()` built; before it there is no ladder to adapt over. */
|
|
1206
|
+
requireAbr() {
|
|
1207
|
+
if (!this.abr)
|
|
1208
|
+
throw new Error("[Transport] ABR has no ladder before connect(config)");
|
|
1209
|
+
return this.abr;
|
|
1210
|
+
}
|
|
1211
|
+
/** Configures multicast-only ABR while leaving the caller's MoQ ABR untouched. */
|
|
1212
|
+
configureMulticastTiers(options, onCommand) {
|
|
1213
|
+
this.dispatchOrchestratorCommand(this.multicastOrchestrator?.cancel());
|
|
1214
|
+
this.multicastOrchestrator = new MulticastOrchestrator(options);
|
|
1215
|
+
this.onOrchestratorCommand = onCommand;
|
|
1216
|
+
return this.multicastOrchestrator.start();
|
|
1217
|
+
}
|
|
1218
|
+
recordMulticastGoodput(bitrate) {
|
|
1219
|
+
return this.dispatchOrchestratorCommand(this.multicastOrchestrator?.onGoodput({ bitrate }));
|
|
1220
|
+
}
|
|
1221
|
+
recordMulticastHeadroom(healthy) {
|
|
1222
|
+
return this.dispatchOrchestratorCommand(this.multicastOrchestrator?.onHeadroom({ healthy }));
|
|
1223
|
+
}
|
|
1224
|
+
recordMulticastTierPacket(packet) {
|
|
1225
|
+
return this.dispatchOrchestratorCommand(this.multicastOrchestrator?.onTierPacket(packet));
|
|
1226
|
+
}
|
|
1227
|
+
pollMulticastTierSwitch() {
|
|
1228
|
+
return this.dispatchOrchestratorCommand(this.multicastOrchestrator?.poll());
|
|
1229
|
+
}
|
|
1230
|
+
/**
|
|
1231
|
+
* Set packet callback
|
|
1232
|
+
*/
|
|
1233
|
+
setPacketCallback(callback) {
|
|
1234
|
+
this.onPacket = callback;
|
|
1235
|
+
}
|
|
1236
|
+
/**
|
|
1237
|
+
* Set error callback
|
|
1238
|
+
*/
|
|
1239
|
+
setErrorCallback(callback) {
|
|
1240
|
+
this.onError = callback;
|
|
1241
|
+
}
|
|
1242
|
+
/**
|
|
1243
|
+
* Set state change callback
|
|
1244
|
+
*/
|
|
1245
|
+
setStateCallback(callback) {
|
|
1246
|
+
this.onStateChange = callback;
|
|
1247
|
+
}
|
|
1248
|
+
/**
|
|
1249
|
+
* Disconnect
|
|
1250
|
+
*/
|
|
1251
|
+
disconnect() {
|
|
1252
|
+
this.connectionGeneration++;
|
|
1253
|
+
this.subscription?.close();
|
|
1254
|
+
this.subscription = null;
|
|
1255
|
+
this.abr = null;
|
|
1256
|
+
this.dispatchOrchestratorCommand(this.multicastOrchestrator?.cancel());
|
|
1257
|
+
this.multicastOrchestrator = null;
|
|
1258
|
+
this.onOrchestratorCommand = void 0;
|
|
1259
|
+
this.state = {
|
|
1260
|
+
mode: "disconnected",
|
|
1261
|
+
connected: false,
|
|
1262
|
+
packetsReceived: 0,
|
|
1263
|
+
bytesReceived: 0,
|
|
1264
|
+
lastPacketTime: 0
|
|
1265
|
+
};
|
|
1266
|
+
this.onStateChange?.(this.state);
|
|
1267
|
+
}
|
|
1268
|
+
dispatchOrchestratorCommand(command) {
|
|
1269
|
+
if (command)
|
|
1270
|
+
this.onOrchestratorCommand?.(command);
|
|
1271
|
+
return command;
|
|
1272
|
+
}
|
|
1273
|
+
};
|
|
1274
|
+
var transportManager = new TransportManager();
|
|
1275
|
+
|
|
1276
|
+
// node_modules/@blockcast/mmt-transport/dist/moqt-priority.js
|
|
1277
|
+
var MAX_MOQT_PRIORITY = 255;
|
|
1278
|
+
function moqtPriority(value, field) {
|
|
1279
|
+
if (!Number.isInteger(value) || value < 0 || value > MAX_MOQT_PRIORITY) {
|
|
1280
|
+
throw new RangeError(`${field} must be an integer in [0, ${MAX_MOQT_PRIORITY}], got ${value}`);
|
|
1281
|
+
}
|
|
1282
|
+
return value;
|
|
1283
|
+
}
|
|
1284
|
+
function moqtSubscriberPriority(value) {
|
|
1285
|
+
return moqtPriority(value, "MOQT subscriber priority");
|
|
1286
|
+
}
|
|
1287
|
+
function moqtPublisherPriority(value) {
|
|
1288
|
+
return moqtPriority(value, "MOQT publisher priority");
|
|
1289
|
+
}
|
|
1290
|
+
var MOQT_SUBSCRIBER_PRIORITY = Object.freeze({
|
|
1291
|
+
catalog: moqtSubscriberPriority(0),
|
|
1292
|
+
/**
|
|
1293
|
+
* @deprecated A repair FETCH's SUBSCRIBER_PRIORITY is derived per request
|
|
1294
|
+
* from the catalog source and repair priorities (draft-ramadan-moq-fec
|
|
1295
|
+
* §11.4.5), not a player constant. Use the `subscriberPriority` that
|
|
1296
|
+
* `@blockcast/mmt-container`'s `wireRepairFetch` puts on each
|
|
1297
|
+
* `RepairFetchRequest` (`repairFetchSubscriberPriority`).
|
|
1298
|
+
*/
|
|
1299
|
+
deadlineRepair: moqtSubscriberPriority(0),
|
|
1300
|
+
audio: moqtSubscriberPriority(1),
|
|
1301
|
+
video: moqtSubscriberPriority(2),
|
|
1302
|
+
/**
|
|
1303
|
+
* @deprecated See `deadlineRepair`: use the request's catalog-derived
|
|
1304
|
+
* `RepairFetchRequest.subscriberPriority` (draft-ramadan-moq-fec §11.4.5).
|
|
1305
|
+
*/
|
|
1306
|
+
backgroundRepair: moqtSubscriberPriority(3)
|
|
1307
|
+
});
|
|
1308
|
+
var MOQT_PUBLISHER_PRIORITY = Object.freeze({
|
|
1309
|
+
catalog: moqtPublisherPriority(32),
|
|
1310
|
+
source: moqtPublisherPriority(128),
|
|
1311
|
+
repair: moqtPublisherPriority(240)
|
|
1312
|
+
});
|
|
1313
|
+
|
|
1314
|
+
// node_modules/@blockcast/mmt-transport/dist/amt-gateway.js
|
|
1315
|
+
var AMTGatewayManager = class {
|
|
1316
|
+
subscriptions = /* @__PURE__ */ new Map();
|
|
1317
|
+
messageHandler;
|
|
1318
|
+
pendingPackets = /* @__PURE__ */ new Map();
|
|
1319
|
+
driad = new DRIADDiscovery();
|
|
1320
|
+
constructor() {
|
|
1321
|
+
this.messageHandler = (event) => {
|
|
1322
|
+
const data = event.data;
|
|
1323
|
+
console.log(`[AMT] SW message received:`, data.type);
|
|
1324
|
+
if (data.type === "UDP_PACKET") {
|
|
1325
|
+
this.handlePacket(data);
|
|
1326
|
+
} else if (data.type === "SOCKET_ERROR") {
|
|
1327
|
+
this.handleSocketError(data);
|
|
1328
|
+
} else if (data.type === "DEBUG_LOG") {
|
|
1329
|
+
console.log(`[AMT SW Debug] ${data.message}`);
|
|
1330
|
+
}
|
|
1331
|
+
};
|
|
1332
|
+
if (typeof navigator !== "undefined" && navigator.serviceWorker) {
|
|
1333
|
+
navigator.serviceWorker.addEventListener("message", this.messageHandler);
|
|
1334
|
+
console.log("[AMT] Registered service worker message handler");
|
|
1335
|
+
} else {
|
|
1336
|
+
console.warn("[AMT] Service worker not available");
|
|
1337
|
+
}
|
|
1338
|
+
}
|
|
1339
|
+
/**
|
|
1340
|
+
* Subscribe via AMT tunnel
|
|
1341
|
+
*/
|
|
1342
|
+
async subscribe(config) {
|
|
1343
|
+
const { key } = config;
|
|
1344
|
+
const keyStr = this.getKeyString(key);
|
|
1345
|
+
if (this.subscriptions.has(keyStr)) {
|
|
1346
|
+
console.log(`[AMT] Reusing existing subscription to ${keyStr}`);
|
|
1347
|
+
return keyStr;
|
|
1348
|
+
}
|
|
1349
|
+
try {
|
|
1350
|
+
let relayAddress;
|
|
1351
|
+
let discoveryMethod;
|
|
1352
|
+
const relayPort = config.relayPort || DEFAULT_AMT_RELAY_PORT;
|
|
1353
|
+
if (config.relayAddress) {
|
|
1354
|
+
relayAddress = config.relayAddress;
|
|
1355
|
+
discoveryMethod = "manual";
|
|
1356
|
+
console.log(`[AMT] Using configured relay ${relayAddress}:${relayPort}`);
|
|
1357
|
+
} else if (key.source) {
|
|
1358
|
+
console.log(`[AMT] No relay configured, discovering via DRIAD for source ${key.source}...`);
|
|
1359
|
+
const relays = await this.driad.discoverRelays(key.source);
|
|
1360
|
+
const bestRelay = await this.driad.selectBestRelay(relays);
|
|
1361
|
+
if (!bestRelay) {
|
|
1362
|
+
throw new Error(`AMT relay discovery failed for source ${key.source}. Please specify a relay address in the subscription configuration.`);
|
|
1363
|
+
}
|
|
1364
|
+
relayAddress = bestRelay.host;
|
|
1365
|
+
discoveryMethod = "driad";
|
|
1366
|
+
console.log(`[AMT] Discovered relay via DRIAD: ${relayAddress}:${relayPort}`);
|
|
1367
|
+
} else {
|
|
1368
|
+
throw new Error(`AMT relay discovery requires a source address for SSM (RFC 8777). Please specify either a relay address or source address.`);
|
|
1369
|
+
}
|
|
1370
|
+
const subscriptionId = keyStr;
|
|
1371
|
+
this.pendingPackets.set(subscriptionId, []);
|
|
1372
|
+
const subscription = {
|
|
1373
|
+
key,
|
|
1374
|
+
subscriptionId,
|
|
1375
|
+
config,
|
|
1376
|
+
handshakeComplete: false,
|
|
1377
|
+
relay: {
|
|
1378
|
+
address: relayAddress,
|
|
1379
|
+
port: relayPort,
|
|
1380
|
+
discoveryMethod,
|
|
1381
|
+
discoveredAt: Date.now()
|
|
1382
|
+
}
|
|
1383
|
+
};
|
|
1384
|
+
this.subscriptions.set(keyStr, subscription);
|
|
1385
|
+
console.log(`[AMT] Subscription created for ${keyStr} via ${relayAddress}:${relayPort}`);
|
|
1386
|
+
return keyStr;
|
|
1387
|
+
} catch (error) {
|
|
1388
|
+
const subscription = this.subscriptions.get(keyStr);
|
|
1389
|
+
if (subscription) {
|
|
1390
|
+
this.subscriptions.delete(keyStr);
|
|
1391
|
+
this.pendingPackets.delete(subscription.subscriptionId);
|
|
1392
|
+
}
|
|
1393
|
+
throw new Error(`Failed to subscribe via AMT: ${error instanceof Error ? error.message : String(error)}`);
|
|
1394
|
+
}
|
|
1395
|
+
}
|
|
1396
|
+
/**
|
|
1397
|
+
* Unsubscribe from AMT tunnel
|
|
1398
|
+
*/
|
|
1399
|
+
async unsubscribe(handle) {
|
|
1400
|
+
const subscription = this.subscriptions.get(handle);
|
|
1401
|
+
if (!subscription) {
|
|
1402
|
+
throw new Error(`Subscription ${handle} not found`);
|
|
1403
|
+
}
|
|
1404
|
+
try {
|
|
1405
|
+
if (subscription.keepAliveTimer) {
|
|
1406
|
+
clearInterval(subscription.keepAliveTimer);
|
|
1407
|
+
}
|
|
1408
|
+
console.log(`[AMT] Unsubscribed from ${handle}`);
|
|
1409
|
+
} catch (error) {
|
|
1410
|
+
console.error("[AMT] Error during unsubscribe:", error);
|
|
1411
|
+
} finally {
|
|
1412
|
+
this.subscriptions.delete(handle);
|
|
1413
|
+
this.pendingPackets.delete(subscription.subscriptionId);
|
|
1414
|
+
}
|
|
1415
|
+
}
|
|
1416
|
+
/**
|
|
1417
|
+
* Get list of active subscriptions
|
|
1418
|
+
*/
|
|
1419
|
+
getSubscriptions() {
|
|
1420
|
+
return Array.from(this.subscriptions.keys());
|
|
1421
|
+
}
|
|
1422
|
+
/**
|
|
1423
|
+
* Handle packet from service worker
|
|
1424
|
+
*/
|
|
1425
|
+
handlePacket(message) {
|
|
1426
|
+
const { subscriptionId, data } = message;
|
|
1427
|
+
if (!subscriptionId)
|
|
1428
|
+
return;
|
|
1429
|
+
const subscription = this.subscriptions.get(subscriptionId);
|
|
1430
|
+
if (!subscription) {
|
|
1431
|
+
console.warn(`[AMT] Received packet for unknown subscription: ${subscriptionId}`);
|
|
1432
|
+
return;
|
|
1433
|
+
}
|
|
1434
|
+
if (!subscription.handshakeComplete) {
|
|
1435
|
+
const queue = this.pendingPackets.get(subscriptionId);
|
|
1436
|
+
const packetData2 = data;
|
|
1437
|
+
if (queue) {
|
|
1438
|
+
queue.push({
|
|
1439
|
+
data: packetData2.data,
|
|
1440
|
+
remoteAddress: packetData2.remoteAddress,
|
|
1441
|
+
remotePort: packetData2.remotePort
|
|
1442
|
+
});
|
|
1443
|
+
}
|
|
1444
|
+
return;
|
|
1445
|
+
}
|
|
1446
|
+
const packetData = data;
|
|
1447
|
+
const metadata = {
|
|
1448
|
+
sourceAddress: subscription.relay?.address || "unknown",
|
|
1449
|
+
sourcePort: subscription.relay?.port || 0,
|
|
1450
|
+
transport: "tunnel",
|
|
1451
|
+
timestamp: performance.now()
|
|
1452
|
+
};
|
|
1453
|
+
subscription.config.onPacket(packetData.data, metadata);
|
|
1454
|
+
}
|
|
1455
|
+
/**
|
|
1456
|
+
* Handle socket error from service worker
|
|
1457
|
+
*/
|
|
1458
|
+
handleSocketError(message) {
|
|
1459
|
+
const { subscriptionId, error } = message;
|
|
1460
|
+
if (!subscriptionId)
|
|
1461
|
+
return;
|
|
1462
|
+
const subscription = this.subscriptions.get(subscriptionId);
|
|
1463
|
+
if (!subscription) {
|
|
1464
|
+
console.warn(`[AMT] Received error for unknown subscription: ${subscriptionId}`);
|
|
1465
|
+
return;
|
|
1466
|
+
}
|
|
1467
|
+
subscription.config.onError(new Error(error || "Unknown error"));
|
|
1468
|
+
}
|
|
1469
|
+
/**
|
|
1470
|
+
* Cleanup on destroy
|
|
1471
|
+
*/
|
|
1472
|
+
destroy() {
|
|
1473
|
+
if (this.messageHandler && typeof navigator !== "undefined" && navigator.serviceWorker) {
|
|
1474
|
+
navigator.serviceWorker.removeEventListener("message", this.messageHandler);
|
|
1475
|
+
}
|
|
1476
|
+
const handles = Array.from(this.subscriptions.keys());
|
|
1477
|
+
for (const handle of handles) {
|
|
1478
|
+
this.unsubscribe(handle).catch((err) => {
|
|
1479
|
+
console.error(`[AMT] Error unsubscribing ${handle}:`, err);
|
|
1480
|
+
});
|
|
1481
|
+
}
|
|
1482
|
+
}
|
|
1483
|
+
/**
|
|
1484
|
+
* Get key string for HashMap
|
|
1485
|
+
*/
|
|
1486
|
+
getKeyString(key) {
|
|
1487
|
+
const source = key.source ? `@${key.source}` : "";
|
|
1488
|
+
return `${key.group}:${key.port}${source}`;
|
|
1489
|
+
}
|
|
1490
|
+
};
|
|
1491
|
+
var amtGatewayManager = new AMTGatewayManager();
|
|
1492
|
+
|
|
1493
|
+
// ../mmt-alta-parse/dist/index.js
|
|
1494
|
+
var MPU_HEADER_SIZE3 = 8;
|
|
1495
|
+
var MPU_LENGTH_FIELD_SIZE3 = 2;
|
|
1496
|
+
var MPU_HEADER_REMAINDER_SIZE3 = MPU_HEADER_SIZE3 - MPU_LENGTH_FIELD_SIZE3;
|
|
1497
|
+
|
|
1498
|
+
// ../mmt-base/dist/mmtp.js
|
|
1499
|
+
var MMTP_HEADER_SIZE = 12;
|
|
1500
|
+
var MPU_HEADER_SIZE4 = 8;
|
|
1501
|
+
var MPU_LENGTH_FIELD_SIZE4 = 2;
|
|
1502
|
+
var MPU_HEADER_REMAINDER_SIZE4 = MPU_HEADER_SIZE4 - MPU_LENGTH_FIELD_SIZE4;
|
|
1503
|
+
var MMTP_SIGNALING_PACKET_ID2 = 0;
|
|
1504
|
+
var MMTP_PACKET_ID_MAX2 = 65535;
|
|
1505
|
+
function readMMTPPacketId(buffer) {
|
|
1506
|
+
if (buffer.length < MMTP_HEADER_SIZE)
|
|
1507
|
+
return null;
|
|
1508
|
+
const flags = buffer[0];
|
|
1509
|
+
if ((flags >> 6 & 3) !== 0)
|
|
1510
|
+
return null;
|
|
1511
|
+
let offset = MMTP_HEADER_SIZE;
|
|
1512
|
+
if ((flags & 32) !== 0) {
|
|
1513
|
+
if (offset + 4 > buffer.length)
|
|
1514
|
+
return null;
|
|
1515
|
+
offset += 4;
|
|
1516
|
+
}
|
|
1517
|
+
if ((flags & 2) !== 0) {
|
|
1518
|
+
if (offset + 4 > buffer.length)
|
|
1519
|
+
return null;
|
|
1520
|
+
const length = buffer[offset + 2] << 8 | buffer[offset + 3];
|
|
1521
|
+
if (offset + 4 + length > buffer.length)
|
|
1522
|
+
return null;
|
|
1523
|
+
}
|
|
1524
|
+
return buffer[2] << 8 | buffer[3];
|
|
1525
|
+
}
|
|
1526
|
+
|
|
1527
|
+
// ../mmt-base/dist/ntp-short.js
|
|
1528
|
+
var NTP_SHORT_BITS2 = 32;
|
|
1529
|
+
var NTP_SHORT_MODULUS2 = 2 ** NTP_SHORT_BITS2;
|
|
1530
|
+
var NTP_SHORT_HALF_RANGE2 = 2 ** (NTP_SHORT_BITS2 - 1);
|
|
1531
|
+
|
|
1532
|
+
// ../mmt-base/dist/fec-block-profile.js
|
|
1533
|
+
var FEC_BLOCK_PROFILES2 = Object.freeze([
|
|
1534
|
+
"k-padded-v1",
|
|
1535
|
+
"iso-ssbg1-v1"
|
|
1536
|
+
]);
|
|
1537
|
+
|
|
1538
|
+
// src/multicast-transport.ts
|
|
1539
|
+
var JOIN_SUCCEEDED = Object.freeze({ ok: true });
|
|
1540
|
+
function providerErrorOf(error) {
|
|
1541
|
+
if (typeof error !== "object" || error === null) return void 0;
|
|
1542
|
+
const fields = error;
|
|
1543
|
+
let name;
|
|
1544
|
+
let message;
|
|
1545
|
+
let code;
|
|
1546
|
+
try {
|
|
1547
|
+
name = fields.name;
|
|
1548
|
+
message = fields.message;
|
|
1549
|
+
code = fields.code;
|
|
1550
|
+
} catch {
|
|
1551
|
+
return void 0;
|
|
1552
|
+
}
|
|
1553
|
+
if (typeof name !== "string" || typeof message !== "string") return void 0;
|
|
1554
|
+
return Object.freeze(typeof code === "string" ? { name, message, code } : { name, message });
|
|
1555
|
+
}
|
|
1556
|
+
var MAX_TRACKED_DROPPED_PIDS = 16;
|
|
1557
|
+
function getMulticastGlobals() {
|
|
1558
|
+
return globalThis;
|
|
1559
|
+
}
|
|
1560
|
+
function hasMulticastGateway() {
|
|
1561
|
+
return typeof getMulticastGlobals().multicast?.subscribe === "function";
|
|
1562
|
+
}
|
|
1563
|
+
function normalizeBytesEventDetail(detail) {
|
|
1564
|
+
const value = detail && typeof detail === "object" && "data" in detail ? detail.data : detail;
|
|
1565
|
+
if (value instanceof Uint8Array) return value;
|
|
1566
|
+
if (value instanceof ArrayBuffer) return new Uint8Array(value);
|
|
1567
|
+
if (ArrayBuffer.isView(value)) {
|
|
1568
|
+
return new Uint8Array(value.buffer, value.byteOffset, value.byteLength);
|
|
1569
|
+
}
|
|
1570
|
+
return null;
|
|
1571
|
+
}
|
|
1572
|
+
function packetFilterUpdater(subscription) {
|
|
1573
|
+
const update = subscription.setPacketIds;
|
|
1574
|
+
return typeof update === "function" ? update.bind(subscription) : void 0;
|
|
1575
|
+
}
|
|
1576
|
+
function sameIds(a, b) {
|
|
1577
|
+
if (a.size !== b.size) return false;
|
|
1578
|
+
for (const id of a) if (!b.has(id)) return false;
|
|
1579
|
+
return true;
|
|
1580
|
+
}
|
|
1581
|
+
var MulticastTransport = class _MulticastTransport {
|
|
1582
|
+
#onPacket;
|
|
1583
|
+
#subscription = null;
|
|
1584
|
+
#packetListener = null;
|
|
1585
|
+
#resolvedTransport;
|
|
1586
|
+
#videoConfirmed = false;
|
|
1587
|
+
#audioConfirmed = false;
|
|
1588
|
+
// One armed no-packets timer per silence window (BLO-36599). Packets only
|
|
1589
|
+
// record #lastPacketAtMs; the timer re-arms for the remainder when it wakes
|
|
1590
|
+
// inside the window, instead of a clear+set pair on every packet.
|
|
1591
|
+
#watchdog = null;
|
|
1592
|
+
#lastPacketAtMs = 0;
|
|
1593
|
+
#failbackWindowMs = null;
|
|
1594
|
+
#onTakeover = null;
|
|
1595
|
+
#onFailback = null;
|
|
1596
|
+
// Data-plane-live edge: the first packet this join accepted. Everything
|
|
1597
|
+
// before it is join latency, not silence (see #resetWatchdog).
|
|
1598
|
+
#dataPlaneLive = false;
|
|
1599
|
+
#onDataPlaneLive = null;
|
|
1600
|
+
#errorListener = null;
|
|
1601
|
+
#onDelivery = null;
|
|
1602
|
+
#delivery;
|
|
1603
|
+
#disposed = false;
|
|
1604
|
+
#stats = {
|
|
1605
|
+
packetsReceived: 0,
|
|
1606
|
+
bytesReceived: 0,
|
|
1607
|
+
packetsDroppedUnregisteredPid: 0,
|
|
1608
|
+
packetsDroppedUnparseable: 0,
|
|
1609
|
+
packetsDroppedSignaling: 0
|
|
1610
|
+
};
|
|
1611
|
+
// join() calls by how they resolved; see joinStats.
|
|
1612
|
+
#joinsSucceeded = 0;
|
|
1613
|
+
#joinsDeclined = {
|
|
1614
|
+
disposed: 0,
|
|
1615
|
+
unavailable: 0,
|
|
1616
|
+
superseded: 0,
|
|
1617
|
+
"invalid-request": 0,
|
|
1618
|
+
"provider-rejected": 0,
|
|
1619
|
+
"invalid-subscription": 0,
|
|
1620
|
+
"delivery-hook-failed": 0
|
|
1621
|
+
};
|
|
1622
|
+
// The newest resolved join's outcome (see lastJoinOutcome) and the join
|
|
1623
|
+
// generation it belongs to. Generations are issued in start order, so a
|
|
1624
|
+
// join that resolves after a newer join has recorded leaves it standing.
|
|
1625
|
+
#lastJoinOutcome = void 0;
|
|
1626
|
+
#lastJoinOutcomeGeneration = 0;
|
|
1627
|
+
#droppedPidCounts = /* @__PURE__ */ new Map();
|
|
1628
|
+
// Accepted bytes per MMTP packet_id; see bytesReceivedForPacketIds. Each
|
|
1629
|
+
// value is a mutable cell, so the per-packet update is one lookup and an
|
|
1630
|
+
// in-place add: no insertion once the key exists, and no re-boxed number
|
|
1631
|
+
// per packet once a count outgrows the small-integer range.
|
|
1632
|
+
#acceptedBytesByPacketId = /* @__PURE__ */ new Map();
|
|
1633
|
+
#packetIds = null;
|
|
1634
|
+
// What the live join delivers with each accepted packet. A field, not a
|
|
1635
|
+
// listener closure, so an in-place admission change (setContext) applies
|
|
1636
|
+
// from the next packet on without rejoining.
|
|
1637
|
+
#context = void 0;
|
|
1638
|
+
// The live join's provider-side filter. An in-place widening must reach it,
|
|
1639
|
+
// or the provider never delivers the new ID; null without a live join.
|
|
1640
|
+
#providerFilter = null;
|
|
1641
|
+
// IDs an in-place update withdrew while the provider may still be
|
|
1642
|
+
// delivering them. Their late packets are still counted as filtered, but
|
|
1643
|
+
// they are the expected tail of a deliberate change, not a provisioning
|
|
1644
|
+
// fault, so they are counted apart and do not raise the unadvertised-PID
|
|
1645
|
+
// warning.
|
|
1646
|
+
#withdrawnPacketIds = /* @__PURE__ */ new Set();
|
|
1647
|
+
#withdrawnPacketIdDrops = 0;
|
|
1648
|
+
// Distinct packet IDs dropped as provisioning faults: on the wire but in
|
|
1649
|
+
// no allow-list the join or an in-place update declared. Capped with
|
|
1650
|
+
// droppedPacketIds.
|
|
1651
|
+
#unadvertisedPacketIds = /* @__PURE__ */ new Set();
|
|
1652
|
+
#joinGeneration = 0;
|
|
1653
|
+
constructor(onPacket) {
|
|
1654
|
+
this.#onPacket = onPacket;
|
|
1655
|
+
}
|
|
1656
|
+
static isAvailable() {
|
|
1657
|
+
return hasMulticastGateway();
|
|
1658
|
+
}
|
|
1659
|
+
async join(config, context) {
|
|
1660
|
+
const generation = ++this.#joinGeneration;
|
|
1661
|
+
this.#releaseSubscription();
|
|
1662
|
+
this.#dataPlaneLive = false;
|
|
1663
|
+
if (this.#disposed) return this.#declined(generation, "disposed");
|
|
1664
|
+
if (!_MulticastTransport.isAvailable()) {
|
|
1665
|
+
console.warn("[MulticastTransport] Multicast API not available");
|
|
1666
|
+
return this.#declined(generation, "unavailable");
|
|
1667
|
+
}
|
|
1668
|
+
let failure = "invalid-request";
|
|
1669
|
+
try {
|
|
1670
|
+
const packetIds = config.packetIds?.length ? new Set(config.packetIds) : null;
|
|
1671
|
+
console.log(
|
|
1672
|
+
`[MulticastTransport] window.multicast.subscribe(${config.sourceAddress}, ${config.groupAddress}, ${config.port})`
|
|
1673
|
+
);
|
|
1674
|
+
const subscribeConfig = toWindowMulticastSubscribeConfig({
|
|
1675
|
+
source: config.sourceAddress,
|
|
1676
|
+
group: config.groupAddress,
|
|
1677
|
+
port: config.port,
|
|
1678
|
+
mode: config.mode,
|
|
1679
|
+
amtRelay: config.amtRelay,
|
|
1680
|
+
amtRelayPort: config.amtRelayPort,
|
|
1681
|
+
packetIds: config.packetIds,
|
|
1682
|
+
tracks: config.tracks
|
|
1683
|
+
});
|
|
1684
|
+
failure = "provider-rejected";
|
|
1685
|
+
const subscription = await getMulticastGlobals().multicast.subscribe(subscribeConfig);
|
|
1686
|
+
failure = "invalid-subscription";
|
|
1687
|
+
console.log(`[MulticastTransport] window.multicast.subscribe returned:`, !!subscription);
|
|
1688
|
+
if (this.#disposed || generation !== this.#joinGeneration) {
|
|
1689
|
+
void subscription.unsubscribe();
|
|
1690
|
+
return this.#declined(generation, this.#displacedReason());
|
|
1691
|
+
}
|
|
1692
|
+
if (typeof subscription.addEventListener !== "function") {
|
|
1693
|
+
if (typeof subscription.unsubscribe === "function") {
|
|
1694
|
+
void subscription.unsubscribe();
|
|
1695
|
+
}
|
|
1696
|
+
throw new Error("window.multicast subscription does not support bytes events");
|
|
1697
|
+
}
|
|
1698
|
+
this.#subscription = subscription;
|
|
1699
|
+
const live = () => !this.#disposed && generation === this.#joinGeneration;
|
|
1700
|
+
const packetListener = subscriptionDeliversPacketBatches(subscription) ? {
|
|
1701
|
+
type: "packets",
|
|
1702
|
+
listener: (event) => {
|
|
1703
|
+
if (!live()) return;
|
|
1704
|
+
this.#deliverBatch(event.detail, live);
|
|
1705
|
+
}
|
|
1706
|
+
} : {
|
|
1707
|
+
type: "bytes",
|
|
1708
|
+
listener: (event) => {
|
|
1709
|
+
if (!live()) return;
|
|
1710
|
+
if (this.#deliver(event.detail)) this.#resetWatchdog();
|
|
1711
|
+
}
|
|
1712
|
+
};
|
|
1713
|
+
this.#packetListener = packetListener;
|
|
1714
|
+
this.#packetIds = packetIds;
|
|
1715
|
+
this.#providerFilter = {
|
|
1716
|
+
acknowledged: packetIds,
|
|
1717
|
+
sent: 0,
|
|
1718
|
+
acknowledgedUpdate: 0,
|
|
1719
|
+
inFlight: 0
|
|
1720
|
+
};
|
|
1721
|
+
this.#context = context;
|
|
1722
|
+
if (packetIds) {
|
|
1723
|
+
for (const packetId of packetIds) {
|
|
1724
|
+
if (!this.#acceptedBytesByPacketId.has(packetId)) {
|
|
1725
|
+
this.#acceptedBytesByPacketId.set(packetId, { bytes: 0 });
|
|
1726
|
+
}
|
|
1727
|
+
}
|
|
1728
|
+
}
|
|
1729
|
+
this.#resolvedTransport = subscribeConfig.origin === "amt" ? "amt" : "native";
|
|
1730
|
+
const delivery = subscription.delivery ?? "datagram";
|
|
1731
|
+
if (delivery !== "datagram" && delivery !== "ordered-relay") {
|
|
1732
|
+
throw new Error(
|
|
1733
|
+
`window.multicast subscription declared unknown delivery ${String(delivery)}`
|
|
1734
|
+
);
|
|
1735
|
+
}
|
|
1736
|
+
this.#delivery = delivery;
|
|
1737
|
+
failure = "delivery-hook-failed";
|
|
1738
|
+
this.#onDelivery?.(delivery);
|
|
1739
|
+
failure = "invalid-subscription";
|
|
1740
|
+
const onError = (event) => {
|
|
1741
|
+
if (this.#disposed || generation !== this.#joinGeneration) return;
|
|
1742
|
+
this.#failBackOnSubscriptionError(event.detail);
|
|
1743
|
+
};
|
|
1744
|
+
this.#errorListener = onError;
|
|
1745
|
+
if (packetListener.type === "packets") {
|
|
1746
|
+
subscription.addEventListener("packets", packetListener.listener);
|
|
1747
|
+
} else {
|
|
1748
|
+
subscription.addEventListener("bytes", packetListener.listener);
|
|
1749
|
+
}
|
|
1750
|
+
subscription.addEventListener("error", onError);
|
|
1751
|
+
console.log(
|
|
1752
|
+
`[MulticastTransport] Joined ${config.groupAddress}:${config.port}`
|
|
1753
|
+
);
|
|
1754
|
+
return this.#joined(generation);
|
|
1755
|
+
} catch (err) {
|
|
1756
|
+
if (this.#disposed || generation !== this.#joinGeneration) {
|
|
1757
|
+
return this.#declined(generation, this.#displacedReason());
|
|
1758
|
+
}
|
|
1759
|
+
this.#releaseSubscription();
|
|
1760
|
+
console.error("[MulticastTransport] Failed to join SSM group:", err);
|
|
1761
|
+
return this.#declined(
|
|
1762
|
+
generation,
|
|
1763
|
+
failure,
|
|
1764
|
+
failure === "provider-rejected" ? providerErrorOf(err) : void 0
|
|
1765
|
+
);
|
|
1766
|
+
}
|
|
1767
|
+
}
|
|
1768
|
+
/** Record a join that resolved true; returns its result. */
|
|
1769
|
+
#joined(generation) {
|
|
1770
|
+
this.#joinsSucceeded++;
|
|
1771
|
+
this.#recordJoinOutcome(generation, JOIN_SUCCEEDED);
|
|
1772
|
+
return true;
|
|
1773
|
+
}
|
|
1774
|
+
/** Record a join that resolved false, and why; returns its result. */
|
|
1775
|
+
#declined(generation, reason, providerError) {
|
|
1776
|
+
this.#joinsDeclined[reason]++;
|
|
1777
|
+
this.#recordJoinOutcome(
|
|
1778
|
+
generation,
|
|
1779
|
+
Object.freeze(
|
|
1780
|
+
providerError === void 0 ? { ok: false, reason } : { ok: false, reason, providerError }
|
|
1781
|
+
)
|
|
1782
|
+
);
|
|
1783
|
+
return false;
|
|
1784
|
+
}
|
|
1785
|
+
/**
|
|
1786
|
+
* Why a join that no longer owns the transport resolved false. dispose()
|
|
1787
|
+
* advances the join generation too, so disposal is checked first.
|
|
1788
|
+
*/
|
|
1789
|
+
#displacedReason() {
|
|
1790
|
+
return this.#disposed ? "disposed" : "superseded";
|
|
1791
|
+
}
|
|
1792
|
+
#recordJoinOutcome(generation, outcome) {
|
|
1793
|
+
if (generation <= this.#lastJoinOutcomeGeneration) return;
|
|
1794
|
+
this.#lastJoinOutcomeGeneration = generation;
|
|
1795
|
+
this.#lastJoinOutcome = outcome;
|
|
1796
|
+
}
|
|
1797
|
+
/**
|
|
1798
|
+
* One packet from the subscription: filter, count, and hand it to the
|
|
1799
|
+
* callback. Returns whether it was delivered; the caller notes liveness.
|
|
1800
|
+
*/
|
|
1801
|
+
#deliver(detail) {
|
|
1802
|
+
const packet = normalizeBytesEventDetail(detail);
|
|
1803
|
+
if (!packet) {
|
|
1804
|
+
this.#stats.packetsDroppedUnparseable++;
|
|
1805
|
+
return false;
|
|
1806
|
+
}
|
|
1807
|
+
if (!this.#acceptPacket(packet)) return false;
|
|
1808
|
+
this.#onPacket(packet, this.#context);
|
|
1809
|
+
return true;
|
|
1810
|
+
}
|
|
1811
|
+
/**
|
|
1812
|
+
* One `packets` event. Each packet is delivered as a `bytes` event would
|
|
1813
|
+
* have delivered it, including isolation: a packet whose callback throws
|
|
1814
|
+
* does not cost the rest of its batch, as it would not have cost the next
|
|
1815
|
+
* `bytes` event. The first failure is rethrown once the batch is done, so
|
|
1816
|
+
* it reaches the event-listener error reporting a `bytes` listener's throw
|
|
1817
|
+
* reached; any further one is rethrown from its own microtask, which the
|
|
1818
|
+
* host reports the same way, so no failure is swallowed. A callback that
|
|
1819
|
+
* disposes the transport or starts another join ends the batch there, as
|
|
1820
|
+
* the per-packet guard would have ignored the remaining events.
|
|
1821
|
+
*/
|
|
1822
|
+
#deliverBatch(batch, live) {
|
|
1823
|
+
if (!Array.isArray(batch)) {
|
|
1824
|
+
throw new TypeError("[MulticastTransport] packets event detail must be an array of packets");
|
|
1825
|
+
}
|
|
1826
|
+
let delivered = false;
|
|
1827
|
+
let failures = null;
|
|
1828
|
+
for (let i = 0; i < batch.length; i++) {
|
|
1829
|
+
try {
|
|
1830
|
+
if (this.#deliver(batch[i])) delivered = true;
|
|
1831
|
+
} catch (error) {
|
|
1832
|
+
if (failures === null) failures = [];
|
|
1833
|
+
failures.push(error);
|
|
1834
|
+
}
|
|
1835
|
+
if (!live()) break;
|
|
1836
|
+
}
|
|
1837
|
+
if (delivered && live()) this.#resetWatchdog();
|
|
1838
|
+
if (failures !== null) {
|
|
1839
|
+
for (let i = 1; i < failures.length; i++) {
|
|
1840
|
+
const failure = failures[i];
|
|
1841
|
+
queueMicrotask(() => {
|
|
1842
|
+
throw failure;
|
|
1843
|
+
});
|
|
1844
|
+
}
|
|
1845
|
+
throw failures[0];
|
|
1846
|
+
}
|
|
1847
|
+
}
|
|
1848
|
+
/** Feed a raw packet from an external source (e.g., AMT tunnel or ASM subscription). */
|
|
1849
|
+
feedPacket(data, context) {
|
|
1850
|
+
if (this.#disposed) return;
|
|
1851
|
+
const packet = data instanceof Uint8Array ? data : new Uint8Array(data);
|
|
1852
|
+
if (!this.#acceptPacket(packet)) return;
|
|
1853
|
+
this.#onPacket(packet, context);
|
|
1854
|
+
this.#resetWatchdog();
|
|
1855
|
+
}
|
|
1856
|
+
confirmVideo() {
|
|
1857
|
+
this.#videoConfirmed = true;
|
|
1858
|
+
this.#checkTakeover();
|
|
1859
|
+
}
|
|
1860
|
+
confirmAudio() {
|
|
1861
|
+
this.#audioConfirmed = true;
|
|
1862
|
+
this.#checkTakeover();
|
|
1863
|
+
}
|
|
1864
|
+
onTakeover(cb) {
|
|
1865
|
+
this.#onTakeover = cb;
|
|
1866
|
+
}
|
|
1867
|
+
onFailback(cb) {
|
|
1868
|
+
this.#onFailback = cb;
|
|
1869
|
+
}
|
|
1870
|
+
/**
|
|
1871
|
+
* Called on every successful join, before the joined subscription can
|
|
1872
|
+
* deliver a packet, with how that subscription delivers packets.
|
|
1873
|
+
*/
|
|
1874
|
+
onDelivery(cb) {
|
|
1875
|
+
this.#onDelivery = cb;
|
|
1876
|
+
}
|
|
1877
|
+
/** How the current subscription delivers packets, once joined. */
|
|
1878
|
+
get delivery() {
|
|
1879
|
+
return this.#delivery;
|
|
1880
|
+
}
|
|
1881
|
+
/**
|
|
1882
|
+
* Fires once per join, at the first packet the join accepts (after the PID
|
|
1883
|
+
* filter; a rejected packet is not liveness). This is the data-plane-live
|
|
1884
|
+
* edge, where this transport's silence window starts. A join whose source
|
|
1885
|
+
* never sends never reaches it, so a host may start a media-arrival budget
|
|
1886
|
+
* here only if the provider bounds the join phase with a typed subscription
|
|
1887
|
+
* error (see SessionManager.joinActive); otherwise the host starts it when
|
|
1888
|
+
* the subscribe settles.
|
|
1889
|
+
*/
|
|
1890
|
+
onDataPlaneLive(cb) {
|
|
1891
|
+
this.#onDataPlaneLive = cb;
|
|
1892
|
+
}
|
|
1893
|
+
/** Whether the current join has accepted a packet (see onDataPlaneLive). */
|
|
1894
|
+
get dataPlaneLive() {
|
|
1895
|
+
return this.#dataPlaneLive;
|
|
1896
|
+
}
|
|
1897
|
+
setFailbackWindowMs(windowMs) {
|
|
1898
|
+
if (!Number.isFinite(windowMs) || windowMs <= 0) {
|
|
1899
|
+
throw new Error(
|
|
1900
|
+
`[MulticastTransport] failbackWindowMs=${windowMs}. Derive a positive failback window from the stream catalog.`
|
|
1901
|
+
);
|
|
1902
|
+
}
|
|
1903
|
+
this.#failbackWindowMs = windowMs;
|
|
1904
|
+
if (this.#watchdog !== null) {
|
|
1905
|
+
clearTimeout(this.#watchdog);
|
|
1906
|
+
const silentMs = performance.now() - this.#lastPacketAtMs;
|
|
1907
|
+
this.#armWatchdog(Math.max(0, Math.ceil(windowMs - silentMs)));
|
|
1908
|
+
}
|
|
1909
|
+
}
|
|
1910
|
+
get stats() {
|
|
1911
|
+
return this.#stats;
|
|
1912
|
+
}
|
|
1913
|
+
/**
|
|
1914
|
+
* How the newest join() to resolve did: `{ ok: true }`, or `{ ok: false }`
|
|
1915
|
+
* with the typed reason it resolved false (and, for `provider-rejected`, the
|
|
1916
|
+
* provider's typed error fields). Undefined until a join resolves. Branch on
|
|
1917
|
+
* `reason` and `providerError.code`, never on log text or
|
|
1918
|
+
* `providerError.message`.
|
|
1919
|
+
*
|
|
1920
|
+
* Newest is by start order: a join that resolves after a newer join has
|
|
1921
|
+
* recorded its outcome does not replace it, so once `await join()` resolves
|
|
1922
|
+
* this names that join's outcome unless a newer join has already resolved.
|
|
1923
|
+
* It records how a join resolved, not whether it is still joined (see
|
|
1924
|
+
* `joined`); a join() that rejects instead of resolving records nothing,
|
|
1925
|
+
* the rejection being its report. Frozen: a later outcome replaces the
|
|
1926
|
+
* object, never changes it.
|
|
1927
|
+
*/
|
|
1928
|
+
get lastJoinOutcome() {
|
|
1929
|
+
return this.#lastJoinOutcome;
|
|
1930
|
+
}
|
|
1931
|
+
/**
|
|
1932
|
+
* join() calls by how they resolved (see MulticastJoinStats). A fresh frozen
|
|
1933
|
+
* snapshot on every read, so a later join never changes one already read.
|
|
1934
|
+
* Kept apart from `stats`, which is the live counter object.
|
|
1935
|
+
*/
|
|
1936
|
+
get joinStats() {
|
|
1937
|
+
return Object.freeze({
|
|
1938
|
+
succeeded: this.#joinsSucceeded,
|
|
1939
|
+
declined: Object.freeze({ ...this.#joinsDeclined })
|
|
1940
|
+
});
|
|
1941
|
+
}
|
|
1942
|
+
/**
|
|
1943
|
+
* Accepted bytes summed over `packetIds`: the `bytesReceived` share of those
|
|
1944
|
+
* MMTP packet_ids. Counted by the same rule, on every path that counts
|
|
1945
|
+
* `bytesReceived` (only packets that pass the ingress filter; packet_id 0
|
|
1946
|
+
* signaling never), and cumulative for the life of the transport, across
|
|
1947
|
+
* rejoins, as `bytesReceived` is. An id never accepted contributes 0. An
|
|
1948
|
+
* unparseable packet admitted by a join without an allow-list has no
|
|
1949
|
+
* packet_id, so it counts toward `bytesReceived` only.
|
|
1950
|
+
*
|
|
1951
|
+
* Use it to scope an ABR throughput sample to the active tier's source and
|
|
1952
|
+
* repair packet_ids when the join admits more than the active tier (a
|
|
1953
|
+
* shared group joined across rendition switches); `bytesReceived` is then
|
|
1954
|
+
* the whole allow-list's arrival, not the active tier's. A Set, so an id
|
|
1955
|
+
* shared by two tracks of the tier is counted once.
|
|
1956
|
+
*
|
|
1957
|
+
* Monotonic only for a fixed set: each id has counted since it was first
|
|
1958
|
+
* admitted, so after a tier switch the new set's sum is not a continuation
|
|
1959
|
+
* of the old one. A cumulative consumer takes a fresh baseline for the new
|
|
1960
|
+
* set and never differences across sets.
|
|
1961
|
+
*/
|
|
1962
|
+
bytesReceivedForPacketIds(packetIds) {
|
|
1963
|
+
let bytes = 0;
|
|
1964
|
+
for (const packetId of packetIds) {
|
|
1965
|
+
bytes += this.#acceptedBytesByPacketId.get(packetId)?.bytes ?? 0;
|
|
1966
|
+
}
|
|
1967
|
+
return bytes;
|
|
1968
|
+
}
|
|
1969
|
+
/**
|
|
1970
|
+
* Per-PID drop breakdown, newest-capped at MAX_TRACKED_DROPPED_PIDS distinct
|
|
1971
|
+
* PIDs. This is what names the offender: `packetsDroppedUnregisteredPid`
|
|
1972
|
+
* says the ingress filter fired, this says it fired on 0x8001.
|
|
1973
|
+
*/
|
|
1974
|
+
get droppedPacketIds() {
|
|
1975
|
+
return this.#droppedPidCounts;
|
|
1976
|
+
}
|
|
1977
|
+
/**
|
|
1978
|
+
* Distinct packet IDs dropped as provisioning faults: arriving on the wire
|
|
1979
|
+
* but in no allow-list the join or an in-place update (setPacketIds)
|
|
1980
|
+
* declared, the drops that raise the unadvertised-PID warning. A packet ID
|
|
1981
|
+
* an in-place update withdrew is not one (see withdrawnPacketIdDrops).
|
|
1982
|
+
* Cumulative across rejoins and capped with droppedPacketIds.
|
|
1983
|
+
*/
|
|
1984
|
+
get unadvertisedPacketIds() {
|
|
1985
|
+
return this.#unadvertisedPacketIds;
|
|
1986
|
+
}
|
|
1987
|
+
/**
|
|
1988
|
+
* Packets dropped because an in-place update (setPacketIds) withdrew their
|
|
1989
|
+
* packet ID while the provider may still deliver it: the expected tail of
|
|
1990
|
+
* a deliberate change. A subset of packetsDroppedUnregisteredPid,
|
|
1991
|
+
* cumulative across rejoins.
|
|
1992
|
+
*/
|
|
1993
|
+
get withdrawnPacketIdDrops() {
|
|
1994
|
+
return this.#withdrawnPacketIdDrops;
|
|
1995
|
+
}
|
|
1996
|
+
/** Whether a subscription is live: joined, and not yet released or replaced. */
|
|
1997
|
+
get joined() {
|
|
1998
|
+
return this.#subscription !== null;
|
|
1999
|
+
}
|
|
2000
|
+
/**
|
|
2001
|
+
* What the live join delivers with every accepted packet (the join's
|
|
2002
|
+
* `context`, or the last setContext), or undefined without a live join.
|
|
2003
|
+
*/
|
|
2004
|
+
get context() {
|
|
2005
|
+
return this.#context;
|
|
2006
|
+
}
|
|
2007
|
+
/**
|
|
2008
|
+
* The live join's page-side packet-ID allow-list, or null when it admits
|
|
2009
|
+
* every packet ID (a join that carried none, or no live join).
|
|
2010
|
+
*/
|
|
2011
|
+
get packetIds() {
|
|
2012
|
+
return this.#packetIds;
|
|
2013
|
+
}
|
|
2014
|
+
/**
|
|
2015
|
+
* Replace what the live join delivers with each accepted packet, from the
|
|
2016
|
+
* next packet on. The subscription (socket, group membership, AMT tunnel)
|
|
2017
|
+
* is untouched: this is how an in-place rendition switch moves the router's
|
|
2018
|
+
* ingress identities without leaving and re-joining the group.
|
|
2019
|
+
*/
|
|
2020
|
+
setContext(context) {
|
|
2021
|
+
if (this.#subscription === null) {
|
|
2022
|
+
throw new Error(
|
|
2023
|
+
"[MulticastTransport] setContext needs a live join; join() carries the first context"
|
|
2024
|
+
);
|
|
2025
|
+
}
|
|
2026
|
+
this.#context = context;
|
|
2027
|
+
}
|
|
2028
|
+
/**
|
|
2029
|
+
* Replace the live join's packet-ID allow-list in place, without rejoining.
|
|
2030
|
+
*
|
|
2031
|
+
* The page-side filter changes synchronously, before this returns: the
|
|
2032
|
+
* next packet is admitted or dropped by the new list. The provider's own
|
|
2033
|
+
* filter is updated through the subscription's `setPacketIds` when it has
|
|
2034
|
+
* one; the returned promise settles with the provider's acknowledgement of
|
|
2035
|
+
* this update. It resolves at once, telling the provider nothing, only
|
|
2036
|
+
* when the provider cannot be told, or when no earlier update is still in
|
|
2037
|
+
* flight and the provider already forwards exactly this list.
|
|
2038
|
+
*
|
|
2039
|
+
* While an update is in flight the provider's filter is not known, so every
|
|
2040
|
+
* later update is sent: one skipped because it matched the last
|
|
2041
|
+
* acknowledged filter would let the in-flight update land after it, and
|
|
2042
|
+
* leave the provider forwarding a list nobody asked for last. Updates are
|
|
2043
|
+
* numbered in send order and the newest acknowledged one describes the
|
|
2044
|
+
* provider, so an acknowledgement that overtakes an older one is not undone
|
|
2045
|
+
* by it.
|
|
2046
|
+
*
|
|
2047
|
+
* A provider without `setPacketIds` can still be narrowed: its wider filter
|
|
2048
|
+
* keeps delivering the dropped IDs and the page-side filter discards them.
|
|
2049
|
+
* It cannot be widened, since it would never deliver the added IDs; that is
|
|
2050
|
+
* refused with a throw before anything changes. An ID this update withdraws
|
|
2051
|
+
* may keep arriving until the provider applies the narrower list; those
|
|
2052
|
+
* packets are counted as filtered like any other and in
|
|
2053
|
+
* withdrawnPacketIdDrops, but are not unadvertised packet IDs: their
|
|
2054
|
+
* arrival is the expected tail of this change rather than a provisioning
|
|
2055
|
+
* fault.
|
|
2056
|
+
*/
|
|
2057
|
+
setPacketIds(packetIds) {
|
|
2058
|
+
const subscription = this.#subscription;
|
|
2059
|
+
const filter = this.#providerFilter;
|
|
2060
|
+
if (subscription === null || filter === null) {
|
|
2061
|
+
throw new Error(
|
|
2062
|
+
"[MulticastTransport] setPacketIds needs a live join; join() carries the first allow-list"
|
|
2063
|
+
);
|
|
2064
|
+
}
|
|
2065
|
+
if (!Array.isArray(packetIds) || packetIds.length === 0) {
|
|
2066
|
+
throw new RangeError(
|
|
2067
|
+
"[MulticastTransport] setPacketIds needs a non-empty packet-ID list: an in-place update names the packet IDs it admits"
|
|
2068
|
+
);
|
|
2069
|
+
}
|
|
2070
|
+
if (!packetIds.every(isMulticastTrackPacketId)) {
|
|
2071
|
+
throw new RangeError(
|
|
2072
|
+
`[MulticastTransport] packetId must be an integer from 1 to ${MMTP_PACKET_ID_MAX2}; packetId 0 is the MMTP signaling flow and is never a track's`
|
|
2073
|
+
);
|
|
2074
|
+
}
|
|
2075
|
+
const next = new Set(packetIds);
|
|
2076
|
+
const provider = filter.acknowledged;
|
|
2077
|
+
const update = packetFilterUpdater(subscription);
|
|
2078
|
+
if (update === void 0 && provider !== null) {
|
|
2079
|
+
const missing = [...next].filter((id) => !provider.has(id));
|
|
2080
|
+
if (missing.length > 0) {
|
|
2081
|
+
throw new Error(
|
|
2082
|
+
`[MulticastTransport] this subscription cannot widen its packet filter in place (the provider exposes no setPacketIds); rejoin to admit ${missing.join(", ")}`
|
|
2083
|
+
);
|
|
2084
|
+
}
|
|
2085
|
+
}
|
|
2086
|
+
const previous = this.#packetIds;
|
|
2087
|
+
this.#packetIds = next;
|
|
2088
|
+
for (const id of next) {
|
|
2089
|
+
this.#withdrawnPacketIds.delete(id);
|
|
2090
|
+
if (!this.#acceptedBytesByPacketId.has(id)) {
|
|
2091
|
+
this.#acceptedBytesByPacketId.set(id, { bytes: 0 });
|
|
2092
|
+
}
|
|
2093
|
+
}
|
|
2094
|
+
if (previous !== null) {
|
|
2095
|
+
for (const id of previous) if (!next.has(id)) this.#withdrawnPacketIds.add(id);
|
|
2096
|
+
}
|
|
2097
|
+
if (update === void 0) return Promise.resolve();
|
|
2098
|
+
if (filter.inFlight === 0 && provider !== null && sameIds(provider, next)) {
|
|
2099
|
+
return Promise.resolve();
|
|
2100
|
+
}
|
|
2101
|
+
const number = ++filter.sent;
|
|
2102
|
+
filter.inFlight++;
|
|
2103
|
+
let applied;
|
|
2104
|
+
try {
|
|
2105
|
+
applied = Promise.resolve(update([...next]));
|
|
2106
|
+
} catch (error) {
|
|
2107
|
+
applied = Promise.reject(error);
|
|
2108
|
+
}
|
|
2109
|
+
return applied.then(
|
|
2110
|
+
() => {
|
|
2111
|
+
if (this.#providerFilter !== filter) return;
|
|
2112
|
+
filter.inFlight--;
|
|
2113
|
+
if (number > filter.acknowledgedUpdate) {
|
|
2114
|
+
filter.acknowledgedUpdate = number;
|
|
2115
|
+
filter.acknowledged = next;
|
|
2116
|
+
}
|
|
2117
|
+
},
|
|
2118
|
+
(error) => {
|
|
2119
|
+
if (this.#providerFilter === filter) filter.inFlight--;
|
|
2120
|
+
throw error;
|
|
2121
|
+
}
|
|
2122
|
+
);
|
|
2123
|
+
}
|
|
2124
|
+
get resolvedTransport() {
|
|
2125
|
+
return this.#resolvedTransport;
|
|
2126
|
+
}
|
|
2127
|
+
dispose() {
|
|
2128
|
+
++this.#joinGeneration;
|
|
2129
|
+
this.#disposed = true;
|
|
2130
|
+
this.#releaseSubscription();
|
|
2131
|
+
}
|
|
2132
|
+
#releaseSubscription() {
|
|
2133
|
+
if (this.#watchdog) clearTimeout(this.#watchdog);
|
|
2134
|
+
this.#watchdog = null;
|
|
2135
|
+
if (this.#subscription) {
|
|
2136
|
+
const packetListener = this.#packetListener;
|
|
2137
|
+
if (packetListener?.type === "packets") {
|
|
2138
|
+
this.#subscription.removeEventListener("packets", packetListener.listener);
|
|
2139
|
+
} else if (packetListener) {
|
|
2140
|
+
this.#subscription.removeEventListener("bytes", packetListener.listener);
|
|
2141
|
+
}
|
|
2142
|
+
if (this.#errorListener) {
|
|
2143
|
+
this.#subscription.removeEventListener("error", this.#errorListener);
|
|
2144
|
+
}
|
|
2145
|
+
void this.#subscription.unsubscribe();
|
|
2146
|
+
}
|
|
2147
|
+
this.#subscription = null;
|
|
2148
|
+
this.#packetListener = null;
|
|
2149
|
+
this.#errorListener = null;
|
|
2150
|
+
this.#resolvedTransport = void 0;
|
|
2151
|
+
this.#delivery = void 0;
|
|
2152
|
+
this.#packetIds = null;
|
|
2153
|
+
this.#providerFilter = null;
|
|
2154
|
+
this.#context = void 0;
|
|
2155
|
+
this.#withdrawnPacketIds.clear();
|
|
2156
|
+
}
|
|
2157
|
+
/** The ingress filter; an accepted packet is counted here, once. */
|
|
2158
|
+
#acceptPacket(packet) {
|
|
2159
|
+
const packetId = readMMTPPacketId(packet);
|
|
2160
|
+
if (packetId === null) {
|
|
2161
|
+
if (!this.#packetIds) {
|
|
2162
|
+
this.#countAccepted(packet, null);
|
|
2163
|
+
return true;
|
|
2164
|
+
}
|
|
2165
|
+
this.#stats.packetsDroppedUnparseable++;
|
|
2166
|
+
return false;
|
|
2167
|
+
}
|
|
2168
|
+
if (packetId === MMTP_SIGNALING_PACKET_ID2) {
|
|
2169
|
+
this.#stats.packetsDroppedSignaling++;
|
|
2170
|
+
return false;
|
|
2171
|
+
}
|
|
2172
|
+
if (!this.#packetIds || this.#packetIds.has(packetId)) {
|
|
2173
|
+
this.#countAccepted(packet, packetId);
|
|
2174
|
+
return true;
|
|
2175
|
+
}
|
|
2176
|
+
this.#recordUnregisteredPidDrop(packetId);
|
|
2177
|
+
return false;
|
|
2178
|
+
}
|
|
2179
|
+
/** Receive counters for one accepted packet; `packetId` is null when it has none. */
|
|
2180
|
+
#countAccepted(packet, packetId) {
|
|
2181
|
+
this.#stats.packetsReceived++;
|
|
2182
|
+
this.#stats.bytesReceived += packet.byteLength;
|
|
2183
|
+
if (packetId === null) return;
|
|
2184
|
+
const counter = this.#acceptedBytesByPacketId.get(packetId);
|
|
2185
|
+
if (counter !== void 0) {
|
|
2186
|
+
counter.bytes += packet.byteLength;
|
|
2187
|
+
} else {
|
|
2188
|
+
this.#acceptedBytesByPacketId.set(packetId, { bytes: packet.byteLength });
|
|
2189
|
+
}
|
|
2190
|
+
}
|
|
2191
|
+
#recordUnregisteredPidDrop(packetId) {
|
|
2192
|
+
this.#stats.packetsDroppedUnregisteredPid++;
|
|
2193
|
+
const withdrawn = this.#withdrawnPacketIds.has(packetId);
|
|
2194
|
+
if (withdrawn) this.#withdrawnPacketIdDrops++;
|
|
2195
|
+
const seen = this.#droppedPidCounts.get(packetId);
|
|
2196
|
+
if (seen !== void 0) {
|
|
2197
|
+
this.#droppedPidCounts.set(packetId, seen + 1);
|
|
2198
|
+
return;
|
|
2199
|
+
}
|
|
2200
|
+
if (this.#droppedPidCounts.size >= MAX_TRACKED_DROPPED_PIDS) return;
|
|
2201
|
+
this.#droppedPidCounts.set(packetId, 1);
|
|
2202
|
+
if (withdrawn) return;
|
|
2203
|
+
this.#unadvertisedPacketIds.add(packetId);
|
|
2204
|
+
console.warn(
|
|
2205
|
+
`[MulticastTransport] Dropping packetId=${packetId} (0x${packetId.toString(16)}): not in the advertised set [${[...this.#packetIds ?? []].join(", ")}]. If this is an FEC repair leg, the catalog is not advertising its packetId.`
|
|
2206
|
+
);
|
|
2207
|
+
}
|
|
2208
|
+
#checkTakeover() {
|
|
2209
|
+
if (this.#videoConfirmed && this.#audioConfirmed && this.#onTakeover) {
|
|
2210
|
+
const cb = this.#onTakeover;
|
|
2211
|
+
this.#onTakeover = null;
|
|
2212
|
+
cb();
|
|
2213
|
+
}
|
|
2214
|
+
}
|
|
2215
|
+
/**
|
|
2216
|
+
* Note liveness now; arm the no-packets timer if none is armed.
|
|
2217
|
+
*
|
|
2218
|
+
* Called only for accepted packets, so the first call of a join is the
|
|
2219
|
+
* data-plane-live edge and is what first arms the silence window: the window
|
|
2220
|
+
* measures silence of a flowing stream, which is undefined before the stream
|
|
2221
|
+
* has flowed.
|
|
2222
|
+
*/
|
|
2223
|
+
#resetWatchdog() {
|
|
2224
|
+
this.#lastPacketAtMs = performance.now();
|
|
2225
|
+
if (!this.#dataPlaneLive) {
|
|
2226
|
+
this.#dataPlaneLive = true;
|
|
2227
|
+
this.#onDataPlaneLive?.();
|
|
2228
|
+
}
|
|
2229
|
+
if (this.#watchdog !== null) return;
|
|
2230
|
+
if (!this.#onFailback) return;
|
|
2231
|
+
if (this.#failbackWindowMs === null) {
|
|
2232
|
+
throw new Error(
|
|
2233
|
+
"[MulticastTransport] Failback watchdog requires a catalog-derived window"
|
|
2234
|
+
);
|
|
2235
|
+
}
|
|
2236
|
+
this.#armWatchdog(this.#failbackWindowMs);
|
|
2237
|
+
}
|
|
2238
|
+
#armWatchdog(delayMs) {
|
|
2239
|
+
this.#watchdog = setTimeout(() => {
|
|
2240
|
+
this.#watchdog = null;
|
|
2241
|
+
if (this.#disposed || !this.#onFailback || this.#failbackWindowMs === null) return;
|
|
2242
|
+
const silentMs = performance.now() - this.#lastPacketAtMs;
|
|
2243
|
+
if (silentMs < this.#failbackWindowMs) {
|
|
2244
|
+
this.#armWatchdog(Math.ceil(this.#failbackWindowMs - silentMs));
|
|
2245
|
+
return;
|
|
2246
|
+
}
|
|
2247
|
+
console.warn(
|
|
2248
|
+
`[MulticastTransport] No packets for ${this.#failbackWindowMs}ms; failing back to MoQ`
|
|
2249
|
+
);
|
|
2250
|
+
this.#onFailback({ kind: "silence", windowMs: this.#failbackWindowMs });
|
|
2251
|
+
}, delayMs);
|
|
2252
|
+
}
|
|
2253
|
+
/** Hand control back to MoQ for a provider-reported subscription failure. */
|
|
2254
|
+
#failBackOnSubscriptionError(error) {
|
|
2255
|
+
if (this.#watchdog !== null) clearTimeout(this.#watchdog);
|
|
2256
|
+
this.#watchdog = null;
|
|
2257
|
+
console.warn("[MulticastTransport] Subscription failed; failing back to MoQ:", error);
|
|
2258
|
+
this.#onFailback?.({ kind: "subscription-error", error });
|
|
2259
|
+
}
|
|
2260
|
+
};
|
|
2261
|
+
export {
|
|
2262
|
+
MulticastTransport
|
|
2263
|
+
};
|
|
2264
|
+
//# sourceMappingURL=multicast-transport.js.map
|