@blockcast/mmt-container 0.1.0 → 0.3.0-main.d9354616fdf2
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,1065 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CARP Catalog Types — FEC and Multicast catalog extensions.
|
|
3
|
+
*
|
|
4
|
+
* Per draft-law-ramadan-moq-carp-reliability §3 (FEC) and §4 (Multicast).
|
|
5
|
+
* Plain TypeScript interfaces + type guards — no Zod dependency.
|
|
6
|
+
*
|
|
7
|
+
* These are the canonical type definitions. Upstream Zod schemas
|
|
8
|
+
* should derive from these via z.infer<> to avoid duplication.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import {
|
|
12
|
+
MMTP_PACKET_ID_MAX,
|
|
13
|
+
MMTP_SIGNALING_PACKET_ID,
|
|
14
|
+
isFecBlockProfile,
|
|
15
|
+
type FecBlockProfile,
|
|
16
|
+
} from "@blockcast/mmt-base";
|
|
17
|
+
import { resolveMulticastAuthCatalog, type MulticastAuthConfig } from "./multicast-auth-catalog.js";
|
|
18
|
+
import {
|
|
19
|
+
assembleRepairLayers,
|
|
20
|
+
assertRepairTrackReachable,
|
|
21
|
+
describeRepairTrack,
|
|
22
|
+
rejectRepairFieldsOnMediaTrack,
|
|
23
|
+
rejectRepairTrackGeometry,
|
|
24
|
+
repairReachability,
|
|
25
|
+
dependsTarget,
|
|
26
|
+
readFecBlockProfile,
|
|
27
|
+
validateRepairTrackFields,
|
|
28
|
+
} from "./repair-layers.js";
|
|
29
|
+
|
|
30
|
+
// ── Track Role (Phase 14, D-02..D-05) ─────────────────────────
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Track role derived from the last slash-segment of the track name.
|
|
34
|
+
*
|
|
35
|
+
* Convention: `{content}/base`, `{content}/delta`, `{content}/repair`.
|
|
36
|
+
* Single-track (no slash) legacy streams use `"single"`.
|
|
37
|
+
*
|
|
38
|
+
* D-01: No `trackRole` / `role` / `kind` field in the catalog.
|
|
39
|
+
* D-02: Role is expressed via track name suffix only.
|
|
40
|
+
* D-03: Unknown suffixes throw at parse time (error on bad init).
|
|
41
|
+
* D-04: Legacy single-track streams (no slash suffix) return `"single"`.
|
|
42
|
+
* D-05: Single role per track — no multi-role arrays.
|
|
43
|
+
*/
|
|
44
|
+
export type TrackRole = "base" | "delta" | "repair" | "single";
|
|
45
|
+
|
|
46
|
+
const VALID_ROLE_SUFFIXES = new Set<string>(["base", "delta", "repair"]);
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Derive the role of a track from its name.
|
|
50
|
+
*
|
|
51
|
+
* - Names with no `/` → `"single"` (legacy single-track streams, D-04).
|
|
52
|
+
* - Names ending with `/base`, `/delta`, or `/repair` → that role (D-02).
|
|
53
|
+
* - Empty name → throws (error on bad init).
|
|
54
|
+
* - Trailing slash (empty last segment) → throws.
|
|
55
|
+
* - Non-ASCII or control characters anywhere in the name → throws (T-14-01 fuzz hardening).
|
|
56
|
+
* - Unknown last segment → throws.
|
|
57
|
+
*
|
|
58
|
+
* @throws {Error} On empty name, non-ASCII/control chars, empty suffix, or unknown suffix.
|
|
59
|
+
*/
|
|
60
|
+
export function parseTrackRole(trackName: string): TrackRole {
|
|
61
|
+
if (trackName === "") {
|
|
62
|
+
throw new Error("[carp-catalog] empty track name");
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// T-14-01 fuzz hardening: reject non-ASCII and control characters.
|
|
66
|
+
// This prevents catalog injection attacks where a publisher sends a crafted
|
|
67
|
+
// track name that bypasses role checks by embedding unusual code points.
|
|
68
|
+
for (let i = 0; i < trackName.length; i++) {
|
|
69
|
+
const cp = trackName.charCodeAt(i);
|
|
70
|
+
// Reject control characters (0x00-0x1F, 0x7F) and non-ASCII (>0x7E)
|
|
71
|
+
if (cp <= 0x1f || cp === 0x7f || cp > 0x7e) {
|
|
72
|
+
throw new Error(
|
|
73
|
+
`[carp-catalog] track name contains non-ASCII or control character (code point 0x${cp.toString(16)}) in "${trackName}"`,
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const lastSlash = trackName.lastIndexOf("/");
|
|
79
|
+
if (lastSlash === -1) {
|
|
80
|
+
// No slash — legacy single-track stream (D-04)
|
|
81
|
+
return "single";
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const suffix = trackName.slice(lastSlash + 1);
|
|
85
|
+
if (suffix === "") {
|
|
86
|
+
throw new Error(
|
|
87
|
+
`[carp-catalog] empty role suffix in track name "${trackName}"; track names must not end with "/"`,
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
if (!VALID_ROLE_SUFFIXES.has(suffix)) {
|
|
92
|
+
throw new Error(
|
|
93
|
+
`[carp-catalog] unknown role suffix "${suffix}" in track name "${trackName}"; expected one of: base, delta, repair`,
|
|
94
|
+
);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
return suffix as TrackRole;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Non-throwing switching-set detector (BLO-8158).
|
|
102
|
+
*
|
|
103
|
+
* Returns `true` iff at least one track name carries a media role suffix
|
|
104
|
+
* (`base` or `delta`) — i.e. the set looks like a base/delta/repair switching
|
|
105
|
+
* set. A `/repair` track alone is ordinary FEC topology and is not sufficient
|
|
106
|
+
* evidence of a switching set. Unlike `parseTrackRole`, this NEVER throws:
|
|
107
|
+
* names without a `/`, and names with an unrecognised suffix (e.g. an ABR
|
|
108
|
+
* resolution ladder like `video/1080p`, `video/720p`, `video/8k`), simply do
|
|
109
|
+
* not count as a role.
|
|
110
|
+
*
|
|
111
|
+
* Use this for DETECTION ("is this a switching set?"). Once a switching set is
|
|
112
|
+
* confirmed, `parseTrackRole` / `validateSwitchingSet` still strictly gate the
|
|
113
|
+
* actual wiring. ABR ladders yield `false` here and fall through to the
|
|
114
|
+
* ABR/single path instead of throwing.
|
|
115
|
+
*/
|
|
116
|
+
export function hasSwitchingSetRole(names: string[]): boolean {
|
|
117
|
+
return names.some((name) => {
|
|
118
|
+
const lastSlash = name.lastIndexOf("/");
|
|
119
|
+
if (lastSlash === -1) return false;
|
|
120
|
+
const suffix = name.slice(lastSlash + 1);
|
|
121
|
+
return suffix === "base" || suffix === "delta";
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Validate that a set of track names forms a complete switching set.
|
|
127
|
+
*
|
|
128
|
+
* - Empty or single-track sets (all parsing to `"single"`) → `{ ok: true }`.
|
|
129
|
+
* - Multi-track sets (any name has a role suffix) MUST include all three
|
|
130
|
+
* roles (`base`, `delta`, `repair`). Missing roles are listed.
|
|
131
|
+
*
|
|
132
|
+
* Propagates throws from `parseTrackRole` (non-ASCII, unknown suffix, etc.).
|
|
133
|
+
*
|
|
134
|
+
* D-04: Legacy single-track streams pass without enforcement.
|
|
135
|
+
* D-04: Once any suffix appears, the set is multi-track and must be complete.
|
|
136
|
+
*/
|
|
137
|
+
export function validateSwitchingSet(
|
|
138
|
+
trackNames: string[],
|
|
139
|
+
): { ok: true } | { ok: false; missing: TrackRole[] } {
|
|
140
|
+
if (trackNames.length === 0) return { ok: true };
|
|
141
|
+
|
|
142
|
+
const roles = trackNames.map(parseTrackRole);
|
|
143
|
+
|
|
144
|
+
// If every role is "single", this is a legacy single-track stream — pass.
|
|
145
|
+
if (roles.every((r) => r === "single")) {
|
|
146
|
+
return { ok: true };
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// Multi-track: at least one name has a role suffix.
|
|
150
|
+
// The set MUST contain all three required roles.
|
|
151
|
+
// Explicit Set<TrackRole> so the TrackRole-typed `required.filter`
|
|
152
|
+
// callback can call .has(r) without a narrow-mismatch complaint.
|
|
153
|
+
const presentRoles: Set<TrackRole> = new Set(roles.filter((r) => r !== "single"));
|
|
154
|
+
const required: TrackRole[] = ["base", "delta", "repair"];
|
|
155
|
+
const missing = required.filter((r) => !presentRoles.has(r));
|
|
156
|
+
|
|
157
|
+
if (missing.length > 0) {
|
|
158
|
+
return { ok: false, missing };
|
|
159
|
+
}
|
|
160
|
+
return { ok: true };
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// ── FEC Catalog Extension (CARP §3.2) ─────────────────────────
|
|
164
|
+
|
|
165
|
+
export type FecAlgorithm = "none" | "raptorq" | "reed-solomon";
|
|
166
|
+
|
|
167
|
+
export interface FecCatalogConfig {
|
|
168
|
+
/** FEC algorithm identifier (REQUIRED per §4.1, values per §3.1). */
|
|
169
|
+
algorithm: FecAlgorithm;
|
|
170
|
+
/** FEC block profile (BLO-35597, ISO/IEC 23008-1 Annex C). REQUIRED when
|
|
171
|
+
* `algorithm` declares an FEC instance (anything but `"none"`); optional
|
|
172
|
+
* on `"none"`, where a present token must still be known. Never defaulted
|
|
173
|
+
* and never inferred from the wire: `k-padded-v1` (zero-padded K-symbol
|
|
174
|
+
* blocks, SS_Start = SBN*K) and `iso-ssbg1-v1` (ssbg_mode1, K' =
|
|
175
|
+
* SSB_length, consecutive SS_IDs) carry identical wire tuples whose repair
|
|
176
|
+
* math differs. */
|
|
177
|
+
blockProfile?: FecBlockProfile;
|
|
178
|
+
/** K — source symbols per block (REQUIRED). */
|
|
179
|
+
sourceSymbols: number;
|
|
180
|
+
/** P — repair symbols per block (REQUIRED). */
|
|
181
|
+
repairSymbols: number;
|
|
182
|
+
/** T — bytes per symbol, 8-byte aligned (REQUIRED). */
|
|
183
|
+
symbolSize: number;
|
|
184
|
+
/** FEC block span in MILLISECONDS (OPTIONAL, default 0 = single-group blocks).
|
|
185
|
+
* D is the positive round-half-up value of interleaveDepthMs / GOP_duration_ms.
|
|
186
|
+
* BLO-14526: renamed from the legacy `interleaveDepth` count-era key — no fallback read. */
|
|
187
|
+
interleaveDepthMs?: number;
|
|
188
|
+
/** Declared jitter/reorder allowance in MILLISECONDS.
|
|
189
|
+
* Added to interleaveDepthMs to form a block's deadline; never multiplied
|
|
190
|
+
* by it. Jitter is a property of the network path and the block span is a
|
|
191
|
+
* property of the encoder, so a multiplicative "safety factor" couples two
|
|
192
|
+
* independent quantities and grants different slack per track for the same
|
|
193
|
+
* network. Consumed identically by FECManager, FEC workers and player
|
|
194
|
+
* policies so one signaled value governs end to end. */
|
|
195
|
+
reorderToleranceMs?: number;
|
|
196
|
+
/** Track name for repair symbols (REQUIRED per §6.1). Base layer 0 when the
|
|
197
|
+
* source carries further repair layers or a keyframe overlay: those are
|
|
198
|
+
* self-describing `fec-repair` tracks (`depends`, `repairLayer`, `scope`)
|
|
199
|
+
* per draft-ramadan-moq-fec §6.3, never listed here. */
|
|
200
|
+
repairTrack: string;
|
|
201
|
+
/** FEC block mode: "interleaved" (default) or "object" (per-frame). */
|
|
202
|
+
// extensions feature — see moqcast-draft docs/fec-extensions-material.md, not in public -00
|
|
203
|
+
mode?: "interleaved" | "object";
|
|
204
|
+
/** UEP (Unequal Error Protection) — extensions feature, see moqcast-draft docs/fec-extensions-material.md, not in public -00. When present, repair count
|
|
205
|
+
* varies per frame type. repairSymbols becomes the base (P-frame) count.
|
|
206
|
+
* MUST be omitted on multi-track base/delta streams (D-08). */
|
|
207
|
+
uep?: {
|
|
208
|
+
/** Keyframe repair ratio: P = ceil(K * keyRatio). Default 0.5. */
|
|
209
|
+
keyRatio: number;
|
|
210
|
+
/** Delta frame repair ratio: P = ceil(K * deltaRatio). Default 0.25. */
|
|
211
|
+
deltaRatio: number;
|
|
212
|
+
};
|
|
213
|
+
/**
|
|
214
|
+
* Base-only fallback window in FEC blocks (D-11, Phase 14).
|
|
215
|
+
*
|
|
216
|
+
* When the delta track's FEC block failure rate exceeds the derived threshold
|
|
217
|
+
* over this many consecutive blocks, MediaRouter falls back to base-only mode.
|
|
218
|
+
* Derivation: framerate * fallback_window_seconds. If absent, construction
|
|
219
|
+
* of the multi-track subscriber MUST throw (error on bad init).
|
|
220
|
+
*
|
|
221
|
+
* Present only on the base track's fec config (not the delta or repair track).
|
|
222
|
+
*/
|
|
223
|
+
deltaFailWindowBlocks?: number;
|
|
224
|
+
/**
|
|
225
|
+
* Delta re-subscribe probe interval in milliseconds (D-13, Phase 14).
|
|
226
|
+
*
|
|
227
|
+
* After a base-only fallback, wait this long before re-subscribing to the
|
|
228
|
+
* delta track to check if conditions have improved.
|
|
229
|
+
* Derivation: computeFecDeliveryWindowMs(...) — the single FEC recovery-window
|
|
230
|
+
* source of truth (draft-ramadan-moq-fec §9). Never a fixed interleave multiple.
|
|
231
|
+
*
|
|
232
|
+
* Present only on the base track's fec config (not the delta or repair track).
|
|
233
|
+
*/
|
|
234
|
+
deltaProbeIntervalMs?: number;
|
|
235
|
+
/**
|
|
236
|
+
* Jitter + one-way-delay + reorder budget in MILLISECONDS (draft §9).
|
|
237
|
+
*
|
|
238
|
+
* An explicit ADDITIVE margin over the complete signaled FEC block span.
|
|
239
|
+
* OPTIONAL: absence does not authorize a local default or a span multiplier.
|
|
240
|
+
*/
|
|
241
|
+
jitterBufferMs?: number;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
export function isFecCatalogConfig(obj: unknown): obj is FecCatalogConfig {
|
|
245
|
+
if (typeof obj !== "object" || obj === null) return false;
|
|
246
|
+
const o = obj as Record<string, unknown>;
|
|
247
|
+
const algorithm = o["algorithm"];
|
|
248
|
+
const symbolSize = o["symbolSize"];
|
|
249
|
+
return (
|
|
250
|
+
typeof algorithm === "string" &&
|
|
251
|
+
(algorithm === "none" || algorithm === "raptorq" || algorithm === "reed-solomon") &&
|
|
252
|
+
typeof o["sourceSymbols"] === "number" && o["sourceSymbols"] >= 1 &&
|
|
253
|
+
typeof o["repairSymbols"] === "number" && o["repairSymbols"] >= 1 &&
|
|
254
|
+
typeof symbolSize === "number" && symbolSize >= 1 &&
|
|
255
|
+
(algorithm !== "raptorq" || symbolSize % 8 === 0) &&
|
|
256
|
+
typeof o["repairTrack"] === "string" && (o["repairTrack"] as string).length > 0 &&
|
|
257
|
+
(isFecBlockProfile(o["blockProfile"]) || (algorithm === "none" && o["blockProfile"] === undefined))
|
|
258
|
+
);
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
// ── Multicast Catalog Extension (CARP §4.1-4.4) ───────────────
|
|
262
|
+
|
|
263
|
+
export type MulticastProtocol = "ssm" | "asm" | "amt" | "atsc3";
|
|
264
|
+
|
|
265
|
+
/**
|
|
266
|
+
* Per-track endpoint membership, per draft-ramadan-moq-multicast-00 §4.1.
|
|
267
|
+
*
|
|
268
|
+
* `packetId` is the MMTP packet_id used for packet-level demux on the
|
|
269
|
+
* multicast wire, in 1..65535: packet_id 0 (`MMTP_SIGNALING_PACKET_ID`) is
|
|
270
|
+
* the MMTP signaling flow and is never a track's. New emitters MUST use this
|
|
271
|
+
* object form; the legacy
|
|
272
|
+
* `string` form is accepted by parsers for one release for backward
|
|
273
|
+
* compatibility (BLO-3323 migration).
|
|
274
|
+
*/
|
|
275
|
+
export interface MulticastTrackObject {
|
|
276
|
+
name: string;
|
|
277
|
+
packetId: number;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
/** Track entry in a multicast endpoint — accepts both legacy and v2 shape. */
|
|
281
|
+
export type MulticastTrack = string | MulticastTrackObject;
|
|
282
|
+
|
|
283
|
+
export interface MulticastEndpoint {
|
|
284
|
+
/** Transport protocol. Defaults to SSM when sourceAddress is present. */
|
|
285
|
+
protocol?: MulticastProtocol;
|
|
286
|
+
/** SSM source IP address (REQUIRED for SSM). */
|
|
287
|
+
sourceAddress?: string;
|
|
288
|
+
/** Multicast group address (REQUIRED). */
|
|
289
|
+
groupAddress: string;
|
|
290
|
+
/** UDP port number (REQUIRED). */
|
|
291
|
+
port: number;
|
|
292
|
+
/**
|
|
293
|
+
* Tracks available on this endpoint (REQUIRED).
|
|
294
|
+
*
|
|
295
|
+
* Accepts both legacy `string` (track name only) and v2 object form
|
|
296
|
+
* (`{ name, packetId }`) for backward compatibility. New emitters
|
|
297
|
+
* MUST use the object form (BLO-3323 / draft §4.1). Consumers can
|
|
298
|
+
* type-narrow with `typeof t === "string"` to handle both.
|
|
299
|
+
*/
|
|
300
|
+
tracks: MulticastTrack[];
|
|
301
|
+
/**
|
|
302
|
+
* AMT relay hostname.
|
|
303
|
+
*
|
|
304
|
+
* @deprecated Superseded by catalog-level `multicast.networkSource`
|
|
305
|
+
* (`{type: "amt", discovery, relay}`). Retained so a catalog published
|
|
306
|
+
* before the migration still parses; emitters MUST NOT write it, and
|
|
307
|
+
* `catalog.schema.json` already rejects it (`additionalProperties: false`
|
|
308
|
+
* on the endpoint), as does moq-rs `MulticastEndpoint`
|
|
309
|
+
* (`deny_unknown_fields`, and it carries no relay field at all).
|
|
310
|
+
*/
|
|
311
|
+
relayAddress?: string;
|
|
312
|
+
/**
|
|
313
|
+
* Aggregate bandwidth of this endpoint in bits per second.
|
|
314
|
+
* RECOMMENDED per draft §4.1 — enables capacity-aware joins.
|
|
315
|
+
*/
|
|
316
|
+
bandwidth?: number;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
export type AmtDiscovery = "driad" | "manual";
|
|
320
|
+
|
|
321
|
+
/**
|
|
322
|
+
* One `multicast.networkSource[]` entry, per draft-ramadan-moq-multicast §4.1.
|
|
323
|
+
*
|
|
324
|
+
* Mirrors moq-rs `NetworkSource` (the normative validator) and the
|
|
325
|
+
* `networkSource` `$def` in `catalog.schema.json` 1:1 — a change here needs the
|
|
326
|
+
* same change in both. `discovery: "manual"` requires `relay`; that relay is
|
|
327
|
+
* the one the player joins. Under `discovery: "driad"` the relay is resolved at
|
|
328
|
+
* runtime, so `relay` is OPTIONAL and, when present, only a discovery hint
|
|
329
|
+
* (e.g. the `amtFallback.relayDns` name the derived path carries over): readers
|
|
330
|
+
* do not join it (`findAmtRelay` in packages/transport
|
|
331
|
+
* multicast-catalog-policy.ts and `catalogAmtNetworkSource` in
|
|
332
|
+
* catalog-network-source.ts both skip it), so a DRIAD entry never pins a relay.
|
|
333
|
+
* `port` is OPTIONAL on the wire: the join boundary itself has no default, but
|
|
334
|
+
* every reader resolves one before that boundary, so a manual relay without a
|
|
335
|
+
* port joins on RFC 7450's 2268 rather than failing (BLO-38176).
|
|
336
|
+
*
|
|
337
|
+
* Not to be confused with `CatalogNetworkSource` in `catalog-network-source.ts`,
|
|
338
|
+
* which is also exported from the package root and also describes
|
|
339
|
+
* `$defs/networkSource`. The split is deliberate and the two must NOT be
|
|
340
|
+
* unified: this type is the strict EMITTER/wire shape, so tightening it is what
|
|
341
|
+
* keeps published catalogs normative. That one is the tolerant READER — every
|
|
342
|
+
* field optional, and it also accepts the bare-object form that hang's zod
|
|
343
|
+
* RootSchema and older hand-authored catalogs emit. Tightening the reader would
|
|
344
|
+
* reject catalogs already in the wild, so when this type changes, widen the
|
|
345
|
+
* reader only if the new shape must be consumed; never narrow it to match.
|
|
346
|
+
*
|
|
347
|
+
* This is the one place a catalog declares an AMT relay. It is catalog-level,
|
|
348
|
+
* not per-endpoint: the normative endpoint model carries no relay field.
|
|
349
|
+
*/
|
|
350
|
+
export type NetworkSource =
|
|
351
|
+
| { type: "amt"; discovery: AmtDiscovery; relay?: string; port?: number }
|
|
352
|
+
| { type: "atsc3"; frequency: number; plpId: number; serviceId: number; slsUri: string };
|
|
353
|
+
|
|
354
|
+
export interface MulticastCatalogConfig {
|
|
355
|
+
/** Enable/disable multicast (OPTIONAL, default true). */
|
|
356
|
+
enabled?: boolean;
|
|
357
|
+
/** Multicast endpoints (REQUIRED). */
|
|
358
|
+
endpoints: MulticastEndpoint[];
|
|
359
|
+
/**
|
|
360
|
+
* Relay/tuning discovery for these endpoints. Required whenever an endpoint
|
|
361
|
+
* declares a bearer protocol (`amt`, `atsc3`), which must name its own entry
|
|
362
|
+
* type here; omitted for native joins (`ssm`, `asm`), whose endpoint tuple is
|
|
363
|
+
* the whole join.
|
|
364
|
+
*/
|
|
365
|
+
networkSource?: NetworkSource[];
|
|
366
|
+
/**
|
|
367
|
+
* AMT fallback configuration.
|
|
368
|
+
*
|
|
369
|
+
* @deprecated Superseded by `networkSource` — `{discovery, relayDns}` maps
|
|
370
|
+
* onto `{type: "amt", discovery, relay}`. `multicastFromConfig` lifts it and
|
|
371
|
+
* no longer emits it; the schema rejects it.
|
|
372
|
+
*/
|
|
373
|
+
amtFallback?: {
|
|
374
|
+
discovery: AmtDiscovery;
|
|
375
|
+
relayDns?: string;
|
|
376
|
+
};
|
|
377
|
+
/**
|
|
378
|
+
* Monotonic catalog version. Publishers MUST bump on any
|
|
379
|
+
* `endpoints[]` mutation (BLO-3323 catalog freshness model).
|
|
380
|
+
*/
|
|
381
|
+
version?: number;
|
|
382
|
+
/**
|
|
383
|
+
* Unix-ms timestamp after which subscribers SHOULD refetch the
|
|
384
|
+
* catalog. Refetch-on-multicast-receive-timeout short-circuits this.
|
|
385
|
+
*/
|
|
386
|
+
validUntil?: number;
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
export function isMulticastTrackObject(obj: unknown): obj is MulticastTrackObject {
|
|
390
|
+
if (typeof obj !== "object" || obj === null) return false;
|
|
391
|
+
const o = obj as Record<string, unknown>;
|
|
392
|
+
return typeof o["name"] === "string" && typeof o["packetId"] === "number";
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
/**
|
|
396
|
+
* Wire-shape predicate: returns `true` when `obj` matches the
|
|
397
|
+
* MulticastEndpoint JSON shape strictly enough to be safely cast.
|
|
398
|
+
*
|
|
399
|
+
* Intentionally less strict than `multicastFromConfig`'s
|
|
400
|
+
* `validateEndpoint` (in `multicast-input.ts`):
|
|
401
|
+
*
|
|
402
|
+
* - `tracks: []` is accepted here (Array.isArray check only) but
|
|
403
|
+
* rejected by `validateEndpoint` as `empty_tracks`.
|
|
404
|
+
* - SSM-specific group-range and source-required checks are not
|
|
405
|
+
* enforced here.
|
|
406
|
+
* - Bandwidth/AmtFallback/version are not type-checked.
|
|
407
|
+
*
|
|
408
|
+
* That asymmetry is deliberate: this predicate guards JSON parsed off
|
|
409
|
+
* the wire (already-published catalogs that downstream may have
|
|
410
|
+
* trimmed), while `validateEndpoint` guards publisher input *before*
|
|
411
|
+
* a catalog is composed. Subscribers are tolerant; publishers are
|
|
412
|
+
* strict — the catalog format never re-validates upstream invariants.
|
|
413
|
+
*/
|
|
414
|
+
export function isMulticastEndpoint(obj: unknown): obj is MulticastEndpoint {
|
|
415
|
+
if (typeof obj !== "object" || obj === null) return false;
|
|
416
|
+
const o = obj as Record<string, unknown>;
|
|
417
|
+
const protocol = o["protocol"] ??
|
|
418
|
+
(typeof o["sourceAddress"] === "string" && o["sourceAddress"].length > 0 ? "ssm" : undefined);
|
|
419
|
+
return (
|
|
420
|
+
typeof protocol === "string" &&
|
|
421
|
+
(protocol === "ssm" || protocol === "asm" || protocol === "amt" || protocol === "atsc3") &&
|
|
422
|
+
typeof o["groupAddress"] === "string" && (o["groupAddress"] as string).length > 0 &&
|
|
423
|
+
typeof o["port"] === "number" && (o["port"] as number) > 0 && (o["port"] as number) <= 65535 &&
|
|
424
|
+
Array.isArray(o["tracks"]) &&
|
|
425
|
+
(o["tracks"] as unknown[]).every((t: unknown) => typeof t === "string" || isMulticastTrackObject(t))
|
|
426
|
+
);
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
export function isMulticastCatalogConfig(obj: unknown): obj is MulticastCatalogConfig {
|
|
430
|
+
if (typeof obj !== "object" || obj === null) return false;
|
|
431
|
+
const o = obj as Record<string, unknown>;
|
|
432
|
+
return (
|
|
433
|
+
Array.isArray(o["endpoints"]) &&
|
|
434
|
+
(o["endpoints"] as unknown[]).every((ep: unknown) => isMulticastEndpoint(ep)) &&
|
|
435
|
+
// Shape only, not contents — entry validation is `multicastFromConfig`'s.
|
|
436
|
+
// `networkSource` now carries the relay, so a non-array here must not
|
|
437
|
+
// narrow clean: every reader of it indexes it.
|
|
438
|
+
//
|
|
439
|
+
// `=== undefined`, not `!("networkSource" in o)`: `multicastFromConfig`
|
|
440
|
+
// tests definedness, and the two must agree on what "set" means. A key
|
|
441
|
+
// present with value `undefined` is a legal `MulticastCatalogConfig`
|
|
442
|
+
// (`strict` without `exactOptionalPropertyTypes`) and is what a spread
|
|
443
|
+
// of an optional field produces — `in` would narrow that valid config
|
|
444
|
+
// to `false` and a consumer gating on this guard would discard a
|
|
445
|
+
// working multicast block.
|
|
446
|
+
(o["networkSource"] === undefined || Array.isArray(o["networkSource"]))
|
|
447
|
+
);
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
// ── Track Timing (mmt-00 §4.4.4) ──────────────────────────────
|
|
451
|
+
|
|
452
|
+
/**
|
|
453
|
+
* Per-track group-duration signaling.
|
|
454
|
+
*
|
|
455
|
+
* The catalog publishes either `groupDurationMs` (integer ms) or the exact
|
|
456
|
+
* `groupDurationTicks` value used by `groupIdForTicks` in align.ts. The tick
|
|
457
|
+
* form is required for cases where milliseconds cannot represent the cadence
|
|
458
|
+
* exactly:
|
|
459
|
+
*
|
|
460
|
+
* - Per-frame group durations at non-integer-ms cadences
|
|
461
|
+
* (1/30 s = 33.333… ms; 1/60 s = 16.666… ms).
|
|
462
|
+
* - Audio sample-rate timescales not divisible by 1000 Hz
|
|
463
|
+
* (e.g. 44 100 Hz × 333 ms = 14 685.3 ticks).
|
|
464
|
+
*
|
|
465
|
+
* If both fields are present, `groupDurationTicks` wins (matches the
|
|
466
|
+
* spec, mmt-00 §4.4.4).
|
|
467
|
+
*
|
|
468
|
+
* @see draft-ramadan-moq-mmt-00 §4.4.4
|
|
469
|
+
* @see ./align.ts groupIdForTicks
|
|
470
|
+
*/
|
|
471
|
+
export interface TrackTimingConfig {
|
|
472
|
+
/** Group duration in milliseconds (positive integer). Required unless an
|
|
473
|
+
* exact groupDurationTicks value is supplied. */
|
|
474
|
+
groupDurationMs?: number;
|
|
475
|
+
/** Integer-tick override (OPTIONAL, positive integer). Wins over the
|
|
476
|
+
* ms field when both are present. Required when
|
|
477
|
+
* `(groupDurationMs * timescale) % 1000 != 0`. */
|
|
478
|
+
groupDurationTicks?: number;
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
export function isTrackTimingConfig(obj: unknown): obj is TrackTimingConfig {
|
|
482
|
+
if (typeof obj !== "object" || obj === null) return false;
|
|
483
|
+
const o = obj as Record<string, unknown>;
|
|
484
|
+
const ms = o["groupDurationMs"];
|
|
485
|
+
const ticks = o["groupDurationTicks"];
|
|
486
|
+
if (ms === undefined && ticks === undefined) return false;
|
|
487
|
+
if (ms !== undefined && (typeof ms !== "number" || !Number.isInteger(ms) || ms <= 0)) return false;
|
|
488
|
+
if ("groupDurationTicks" in o) {
|
|
489
|
+
if (ticks === undefined) return ms !== undefined;
|
|
490
|
+
if (typeof ticks !== "number" || !Number.isInteger(ticks) || ticks <= 0) return false;
|
|
491
|
+
}
|
|
492
|
+
return true;
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
/** Resolve catalog group timing to an operational wall-clock duration.
|
|
496
|
+
*
|
|
497
|
+
* `groupDurationTicks` wins when both wire fields are present, matching
|
|
498
|
+
* mmt-00 §4.4.4. The result may be fractional because exact frame cadences
|
|
499
|
+
* such as 33_333 ticks at 1 MHz cannot be represented by the wire catalog's
|
|
500
|
+
* integer `groupDurationMs` field.
|
|
501
|
+
*/
|
|
502
|
+
export function resolveGroupDurationMs(
|
|
503
|
+
timing: TrackTimingConfig,
|
|
504
|
+
timescale: number,
|
|
505
|
+
): number {
|
|
506
|
+
if (!Number.isSafeInteger(timescale) || timescale <= 0) {
|
|
507
|
+
throw new Error(
|
|
508
|
+
`[carp-catalog] resolveGroupDurationMs: timescale must be a positive safe integer Hz, got ${timescale}`,
|
|
509
|
+
);
|
|
510
|
+
}
|
|
511
|
+
if (!isTrackTimingConfig(timing)) {
|
|
512
|
+
throw new Error(
|
|
513
|
+
`[carp-catalog] resolveGroupDurationMs: invalid TrackTimingConfig: ${JSON.stringify(timing)}`,
|
|
514
|
+
);
|
|
515
|
+
}
|
|
516
|
+
if (timing.groupDurationTicks === undefined) {
|
|
517
|
+
return timing.groupDurationMs!;
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
const durationMs = (timing.groupDurationTicks / timescale) * 1000;
|
|
521
|
+
if (!Number.isFinite(durationMs) || durationMs <= 0 || durationMs > Number.MAX_SAFE_INTEGER) {
|
|
522
|
+
throw new Error(
|
|
523
|
+
`[carp-catalog] resolveGroupDurationMs: groupDurationTicks=${timing.groupDurationTicks} ` +
|
|
524
|
+
`at timescale=${timescale} cannot be represented as a positive safe millisecond duration`,
|
|
525
|
+
);
|
|
526
|
+
}
|
|
527
|
+
return durationMs;
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
/**
|
|
531
|
+
* Optional unicast archive tier (BLO-41290).
|
|
532
|
+
*
|
|
533
|
+
* `origin` is scheme-and-authority only. Object paths are never advertised:
|
|
534
|
+
* a consumer derives them via the v1 ContentId profile —
|
|
535
|
+
* `/v1/{ns}/{track}/{group}[/{object}]` under this origin
|
|
536
|
+
* (`mmt-archive/src/content_id.rs`). Advertising the pair a second time would
|
|
537
|
+
* be a second source of truth that can disagree with `tracks[]`.
|
|
538
|
+
*
|
|
539
|
+
* `{track}` is the track's `name`. `{ns}` is the track's own `namespace` when
|
|
540
|
+
* it declares one, and otherwise the MoQ subscription's Track Namespace tuple,
|
|
541
|
+
* of which `ContentId::from_moq` admits exactly one element. The catalog does
|
|
542
|
+
* not carry that tuple, so for a track omitting `namespace` the derivation
|
|
543
|
+
* needs the subscription context — the catalog alone is not sufficient. That
|
|
544
|
+
* inheritance is the load-bearing half of the contract: `namespace` is
|
|
545
|
+
* optional in the schema, so a catalog can advertise an archive whose object
|
|
546
|
+
* paths only resolve in a subscribing consumer.
|
|
547
|
+
*
|
|
548
|
+
* One origin at the top level, not one per track: the `(namespace, track)` key
|
|
549
|
+
* lives in the derived path rather than in the origin, so a single origin
|
|
550
|
+
* already disambiguates every track however many namespaces the catalog spans.
|
|
551
|
+
* A per-track origin would only buy splitting one catalog across several
|
|
552
|
+
* archives, which nothing asks for.
|
|
553
|
+
*
|
|
554
|
+
* Absence means "no archive advertised". It is never a default URL.
|
|
555
|
+
*/
|
|
556
|
+
export interface ArchiveConfig {
|
|
557
|
+
origin: string;
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
/**
|
|
561
|
+
* The advertised archive origin, or `undefined` when none is advertised.
|
|
562
|
+
*
|
|
563
|
+
* Takes a catalog already through `validateCmsfMmtpCatalog`, so the returned
|
|
564
|
+
* string is canonical and safe to use as a base — no string surgery on a
|
|
565
|
+
* config blob, and no second place that decides what an origin may look like.
|
|
566
|
+
*/
|
|
567
|
+
export function archiveOrigin(catalog: CmsfCatalog): string | undefined {
|
|
568
|
+
return catalog.archive?.origin;
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
/**
|
|
572
|
+
* Pin the archive origin to exactly one spelling per archive.
|
|
573
|
+
*
|
|
574
|
+
* The ContentId codec rejects percent-encoding so every identity has exactly
|
|
575
|
+
* one URL path; an advertised base must not reintroduce aliasing above it.
|
|
576
|
+
* `value === new URL(value).origin` is most of the rule — `URL.origin` drops
|
|
577
|
+
* path, query, fragment and userinfo, lowercases scheme and host, strips the
|
|
578
|
+
* default port and any trailing slash, and normalizes percent-escapes. So a
|
|
579
|
+
* separate percent-encoding check would be redundant, not missing: `%6d` in a
|
|
580
|
+
* host decodes and fails the comparison. `https:` is checked separately
|
|
581
|
+
* because `http://host` is already its own origin and passes the comparison.
|
|
582
|
+
*
|
|
583
|
+
* The comparison alone admits one alias, so the trailing dot is rejected
|
|
584
|
+
* explicitly: `https://host.` is its own `URL.origin` verbatim, while `host.`
|
|
585
|
+
* and `host` are the same DNS name. That aliases the advertised *base*, not
|
|
586
|
+
* the codec input — the origin contributes no path segments, so a trailing
|
|
587
|
+
* dot never reaches `ContentId::parse_path` — but it still gives one archive
|
|
588
|
+
* two cache keys, and some TLS stacks treat `host.` as a distinct SNI/CORS
|
|
589
|
+
* origin.
|
|
590
|
+
*
|
|
591
|
+
* `additionalProperties: false` on `archive` is enforced here as well as in
|
|
592
|
+
* the schema. The runtime validator is the gate `archiveOrigin` documents
|
|
593
|
+
* itself against, so a second spelling of the origin smuggled in under
|
|
594
|
+
* another key must be rejected by both, not just by AJV.
|
|
595
|
+
*/
|
|
596
|
+
function validateArchive(catalog: Record<string, unknown>): void {
|
|
597
|
+
const archive = catalog["archive"];
|
|
598
|
+
if (archive === undefined) return;
|
|
599
|
+
if (typeof archive !== "object" || archive === null || Array.isArray(archive)) {
|
|
600
|
+
catalogError("archive_object", "$.archive", "archive must be an object");
|
|
601
|
+
}
|
|
602
|
+
const unknown = Object.keys(archive as Record<string, unknown>).filter(key => key !== "origin");
|
|
603
|
+
if (unknown.length > 0) {
|
|
604
|
+
catalogError("archive_object", "$.archive", `archive has unknown field(s) ${unknown.map(key => JSON.stringify(key)).join(", ")}; only "origin" is allowed`);
|
|
605
|
+
}
|
|
606
|
+
const origin = (archive as Record<string, unknown>)["origin"];
|
|
607
|
+
// Absent and present-but-wrong-type are different mistakes and the schema
|
|
608
|
+
// already distinguishes them (`required` vs `type`). Collapsing both into
|
|
609
|
+
// "must be a string" tells someone hand-writing a catalog to fix a field
|
|
610
|
+
// they never wrote. Same wording convention as `codec is required`.
|
|
611
|
+
//
|
|
612
|
+
// This is the one archive rule where the two gates report different fields,
|
|
613
|
+
// deliberately: AJV raises `required` at the parent, `/archive` (its
|
|
614
|
+
// placement, not a choice — same shape as `fec-block-profile-missing`),
|
|
615
|
+
// while this raises at `$.archive.origin`. Pointing at the field someone has
|
|
616
|
+
// to add is the more useful of the two, so the "same field" convention
|
|
617
|
+
// documented above bends here rather than the message getting vaguer.
|
|
618
|
+
if (origin === undefined) {
|
|
619
|
+
catalogError("archive_origin", "$.archive.origin", "origin is required when an archive block is present");
|
|
620
|
+
}
|
|
621
|
+
if (typeof origin !== "string") {
|
|
622
|
+
catalogError("archive_origin", "$.archive.origin", "origin must be a string");
|
|
623
|
+
}
|
|
624
|
+
let parsed: URL;
|
|
625
|
+
try {
|
|
626
|
+
parsed = new URL(origin);
|
|
627
|
+
} catch {
|
|
628
|
+
catalogError("archive_origin", "$.archive.origin", `origin must be an absolute URL, got ${JSON.stringify(origin)}`);
|
|
629
|
+
}
|
|
630
|
+
if (parsed.protocol !== "https:") {
|
|
631
|
+
catalogError("archive_origin", "$.archive.origin", `origin must use https, got ${JSON.stringify(parsed.protocol)}`);
|
|
632
|
+
}
|
|
633
|
+
if (origin !== parsed.origin) {
|
|
634
|
+
catalogError(
|
|
635
|
+
"archive_origin",
|
|
636
|
+
"$.archive.origin",
|
|
637
|
+
`origin must be scheme and authority in canonical form with no path, query, fragment, userinfo or trailing slash; expected ${JSON.stringify(parsed.origin)}, got ${JSON.stringify(origin)}`,
|
|
638
|
+
);
|
|
639
|
+
}
|
|
640
|
+
if (parsed.hostname.endsWith(".")) {
|
|
641
|
+
catalogError("archive_origin", "$.archive.origin", `origin host must not end in a trailing dot; ${JSON.stringify(parsed.hostname)} and ${JSON.stringify(parsed.hostname.slice(0, -1))} are the same host`);
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
export type CmsfCatalog = {
|
|
646
|
+
version: number;
|
|
647
|
+
tracks: Record<string, unknown>[];
|
|
648
|
+
multicast?: Record<string, unknown>;
|
|
649
|
+
"multicast.auth"?: MulticastAuthConfig;
|
|
650
|
+
archive?: ArchiveConfig;
|
|
651
|
+
[key: string]: unknown;
|
|
652
|
+
};
|
|
653
|
+
|
|
654
|
+
export type CmsfCatalogErrorCode =
|
|
655
|
+
| "catalog_object"
|
|
656
|
+
| "catalog_version"
|
|
657
|
+
| "catalog_tracks"
|
|
658
|
+
| "catalog_common_track_fields"
|
|
659
|
+
| "track_object"
|
|
660
|
+
| "track_name"
|
|
661
|
+
| "track_packaging"
|
|
662
|
+
| "track_selection_params"
|
|
663
|
+
| "track_is_live"
|
|
664
|
+
| "track_codec"
|
|
665
|
+
| "track_mmtp_mode"
|
|
666
|
+
| "track_timescale"
|
|
667
|
+
| "track_group_duration_ms"
|
|
668
|
+
| "track_group_duration_ticks"
|
|
669
|
+
| "track_group_duration_exact"
|
|
670
|
+
| "track_keyframe_interval"
|
|
671
|
+
| "track_fec"
|
|
672
|
+
| "track_fec_bounds"
|
|
673
|
+
| "track_fec_block_profile"
|
|
674
|
+
| "track_repair_track"
|
|
675
|
+
| "track_repair_depends"
|
|
676
|
+
| "track_repair_layer"
|
|
677
|
+
| "track_repair_priority"
|
|
678
|
+
| "track_repair_overlay"
|
|
679
|
+
| "track_repair_geometry"
|
|
680
|
+
| "track_switching_set"
|
|
681
|
+
| "multicast_auth"
|
|
682
|
+
| "multicast_track"
|
|
683
|
+
| "multicast_packet_id"
|
|
684
|
+
| "archive_object"
|
|
685
|
+
| "archive_origin";
|
|
686
|
+
|
|
687
|
+
export class CmsfCatalogValidationError extends Error {
|
|
688
|
+
constructor(
|
|
689
|
+
public readonly code: CmsfCatalogErrorCode,
|
|
690
|
+
public readonly path: string,
|
|
691
|
+
message: string,
|
|
692
|
+
) {
|
|
693
|
+
super(`[cmsf:${code}] ${path}: ${message}`);
|
|
694
|
+
this.name = "CmsfCatalogValidationError";
|
|
695
|
+
}
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
function catalogError(code: CmsfCatalogErrorCode, path: string, message: string): never {
|
|
699
|
+
throw new CmsfCatalogValidationError(code, path, message);
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
/** Validate the common CMSF shape and the MMTP required-field profile. */
|
|
703
|
+
export function validateCmsfMmtpCatalog(value: unknown): CmsfCatalog {
|
|
704
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
705
|
+
catalogError("catalog_object", "$", "catalog must be an object");
|
|
706
|
+
}
|
|
707
|
+
const catalog = value as Record<string, unknown>;
|
|
708
|
+
// IETF-draft catalogs (cast `ladder_catalog.rs`, moq-server
|
|
709
|
+
// `catalog-synthesizer.ts`, consumed by the hang player) carry a string
|
|
710
|
+
// version tag such as "draft-ietf-moq-catalogformat-01" instead of the CMSF
|
|
711
|
+
// `version: 1` integer. Nothing keys behavior off the catalog version, so
|
|
712
|
+
// normalize the tag to a number rather than rejecting the whole catalog —
|
|
713
|
+
// mirrors the relay (dual-stack-relay `bridge/catalog.rs`
|
|
714
|
+
// `deserialize_optional_u32_lenient`, #825) and moqtail-ts
|
|
715
|
+
// `normalizeCatalogVersion`.
|
|
716
|
+
const rawVersion = catalog["version"];
|
|
717
|
+
const normalizedVersion =
|
|
718
|
+
typeof rawVersion === "string"
|
|
719
|
+
? Number.isFinite(Number(rawVersion))
|
|
720
|
+
? Number(rawVersion)
|
|
721
|
+
: rawVersion.startsWith("draft-ietf-moq-catalogformat")
|
|
722
|
+
? 1
|
|
723
|
+
: NaN
|
|
724
|
+
: rawVersion;
|
|
725
|
+
if (!Number.isInteger(normalizedVersion) || (normalizedVersion as number) <= 0) {
|
|
726
|
+
catalogError("catalog_version", "$.version", "version must be a positive integer or a recognized format tag");
|
|
727
|
+
}
|
|
728
|
+
catalog["version"] = normalizedVersion;
|
|
729
|
+
if (!Array.isArray(catalog["tracks"])) {
|
|
730
|
+
catalogError("catalog_tracks", "$.tracks", "tracks must be an array");
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
if ("commonTrackFields" in catalog) {
|
|
734
|
+
catalogError(
|
|
735
|
+
"catalog_common_track_fields",
|
|
736
|
+
"$.commonTrackFields",
|
|
737
|
+
"commonTrackFields is retired; every track carries its own fields",
|
|
738
|
+
);
|
|
739
|
+
}
|
|
740
|
+
const tracks = catalog["tracks"] as unknown[];
|
|
741
|
+
const trackRecords: Record<string, unknown>[] = [];
|
|
742
|
+
const positiveInteger = (input: unknown): input is number => Number.isSafeInteger(input) && (input as number) > 0;
|
|
743
|
+
const positiveNumber = (input: unknown): input is number =>
|
|
744
|
+
typeof input === "number" && Number.isFinite(input) && input > 0;
|
|
745
|
+
|
|
746
|
+
for (let index = 0; index < tracks.length; index++) {
|
|
747
|
+
const raw = tracks[index];
|
|
748
|
+
const path = `$.tracks[${index}]`;
|
|
749
|
+
if (typeof raw !== "object" || raw === null || Array.isArray(raw)) {
|
|
750
|
+
catalogError("track_object", path, "track must be an object");
|
|
751
|
+
}
|
|
752
|
+
const track = raw as Record<string, unknown>;
|
|
753
|
+
const name = track["name"];
|
|
754
|
+
if (typeof name !== "string" || name.length === 0) {
|
|
755
|
+
catalogError("track_name", `${path}.name`, "name must be a non-empty string");
|
|
756
|
+
}
|
|
757
|
+
trackRecords.push(track);
|
|
758
|
+
|
|
759
|
+
const packaging = track["packaging"];
|
|
760
|
+
if (typeof packaging !== "string" || packaging.length === 0) {
|
|
761
|
+
catalogError("track_packaging", `${path}.packaging`, "packaging must be a non-empty string");
|
|
762
|
+
}
|
|
763
|
+
const hasIsLive = Object.prototype.hasOwnProperty.call(track, "isLive");
|
|
764
|
+
const isLive = track["isLive"];
|
|
765
|
+
if (hasIsLive && typeof isLive !== "boolean") {
|
|
766
|
+
catalogError("track_is_live", `${path}.isLive`, "isLive must be a boolean");
|
|
767
|
+
}
|
|
768
|
+
track["isLive"] = isLive ?? true;
|
|
769
|
+
if ("selectionParams" in track) {
|
|
770
|
+
catalogError(
|
|
771
|
+
"track_selection_params",
|
|
772
|
+
`${path}.selectionParams`,
|
|
773
|
+
"selectionParams is retired; codec and the other selection fields are flat on the track",
|
|
774
|
+
);
|
|
775
|
+
}
|
|
776
|
+
const codec = track["codec"];
|
|
777
|
+
if (packaging !== "timeline" && packaging !== "mediatimeline" && packaging !== "eventtimeline" && packaging !== "fec-repair" &&
|
|
778
|
+
(typeof codec !== "string" || codec.length === 0)) {
|
|
779
|
+
catalogError("track_codec", `${path}.codec`, `codec is required for packaging=${packaging}`);
|
|
780
|
+
}
|
|
781
|
+
|
|
782
|
+
const timescale = track["timescale"];
|
|
783
|
+
const groupDurationMs = track["groupDurationMs"];
|
|
784
|
+
const groupDurationTicks = track["groupDurationTicks"];
|
|
785
|
+
const keyframeIntervalMs = track["keyframeIntervalMs"];
|
|
786
|
+
const keyframeIntervalTicks = track["keyframeIntervalTicks"];
|
|
787
|
+
if (groupDurationMs !== undefined && !positiveInteger(groupDurationMs)) {
|
|
788
|
+
catalogError("track_group_duration_ms", `${path}.groupDurationMs`, "must be a positive integer");
|
|
789
|
+
}
|
|
790
|
+
if (groupDurationTicks !== undefined && !positiveInteger(groupDurationTicks)) {
|
|
791
|
+
catalogError("track_group_duration_ticks", `${path}.groupDurationTicks`, "must be a positive integer");
|
|
792
|
+
}
|
|
793
|
+
if ((keyframeIntervalMs !== undefined && !positiveNumber(keyframeIntervalMs)) ||
|
|
794
|
+
(keyframeIntervalTicks !== undefined && !positiveInteger(keyframeIntervalTicks))) {
|
|
795
|
+
catalogError("track_keyframe_interval", path, "keyframe interval fields must be positive numbers");
|
|
796
|
+
}
|
|
797
|
+
// draft-ramadan-moq-fec §6.3: repair tracks describe themselves and carry
|
|
798
|
+
// no block geometry; media tracks never carry repair-track fields.
|
|
799
|
+
if (packaging === "fec-repair") {
|
|
800
|
+
rejectRepairTrackGeometry(path, track, catalogError);
|
|
801
|
+
validateRepairTrackFields(describeRepairTrack(name, path, track), catalogError);
|
|
802
|
+
} else {
|
|
803
|
+
rejectRepairFieldsOnMediaTrack(path, track, catalogError);
|
|
804
|
+
}
|
|
805
|
+
if (packaging === "mmtp") {
|
|
806
|
+
const mmtpMode = track["mmtpMode"];
|
|
807
|
+
if (mmtpMode !== "mpu" && mmtpMode !== "mfu") {
|
|
808
|
+
catalogError("track_mmtp_mode", `${path}.mmtpMode`, "must be mpu or mfu");
|
|
809
|
+
}
|
|
810
|
+
if (!positiveInteger(timescale)) {
|
|
811
|
+
catalogError("track_timescale", `${path}.timescale`, "must be a positive integer");
|
|
812
|
+
}
|
|
813
|
+
if (groupDurationMs === undefined && groupDurationTicks === undefined) {
|
|
814
|
+
catalogError(
|
|
815
|
+
"track_group_duration_ms",
|
|
816
|
+
path,
|
|
817
|
+
"groupDurationMs or groupDurationTicks must be a positive integer",
|
|
818
|
+
);
|
|
819
|
+
}
|
|
820
|
+
if (groupDurationTicks === undefined &&
|
|
821
|
+
(BigInt(groupDurationMs as number) * BigInt(timescale)) % 1000n !== 0n) {
|
|
822
|
+
catalogError("track_group_duration_exact", `${path}.groupDurationMs`, "does not resolve to an exact integer tick count");
|
|
823
|
+
}
|
|
824
|
+
}
|
|
825
|
+
|
|
826
|
+
if (track["fec"] !== undefined) {
|
|
827
|
+
const fec = track["fec"];
|
|
828
|
+
if (typeof fec !== "object" || fec === null || Array.isArray(fec)) {
|
|
829
|
+
catalogError("track_fec", `${path}.fec`, "FEC must be an object");
|
|
830
|
+
}
|
|
831
|
+
const config = fec as Record<string, unknown>;
|
|
832
|
+
const algorithm = config["algorithm"];
|
|
833
|
+
if (algorithm !== "none" && algorithm !== "raptorq" && algorithm !== "reed-solomon") {
|
|
834
|
+
catalogError("track_fec", `${path}.fec.algorithm`, "algorithm is not supported");
|
|
835
|
+
}
|
|
836
|
+
if ("interleaveDepth" in config || "interleaveMs" in config) {
|
|
837
|
+
catalogError("track_fec", `${path}.fec`, "interleaveDepthMs is the only supported interleave field");
|
|
838
|
+
}
|
|
839
|
+
if ("enhancementRepair" in config) {
|
|
840
|
+
catalogError(
|
|
841
|
+
"track_fec",
|
|
842
|
+
`${path}.fec.enhancementRepair`,
|
|
843
|
+
"repair layers are self-describing fec-repair tracks (depends, repairLayer); fec.enhancementRepair is not supported",
|
|
844
|
+
);
|
|
845
|
+
}
|
|
846
|
+
if (config["interleaveDepthMs"] !== undefined && !positiveInteger(config["interleaveDepthMs"])) {
|
|
847
|
+
catalogError("track_fec", `${path}.fec.interleaveDepthMs`, "must be a positive integer");
|
|
848
|
+
}
|
|
849
|
+
if (algorithm !== "none") {
|
|
850
|
+
for (const field of ["sourceSymbols", "repairSymbols", "symbolSize"] as const) {
|
|
851
|
+
if (!positiveInteger(config[field])) {
|
|
852
|
+
catalogError("track_fec", `${path}.fec.${field}`, "must be a positive integer");
|
|
853
|
+
}
|
|
854
|
+
}
|
|
855
|
+
if (typeof config["repairTrack"] !== "string" || config["repairTrack"].length === 0) {
|
|
856
|
+
catalogError("track_fec", `${path}.fec.repairTrack`, "must be a non-empty string");
|
|
857
|
+
}
|
|
858
|
+
}
|
|
859
|
+
// Required on a declared FEC instance, optional but known on "none".
|
|
860
|
+
readFecBlockProfile(config["blockProfile"], `${path}.fec.blockProfile`, algorithm !== "none", catalogError);
|
|
861
|
+
}
|
|
862
|
+
}
|
|
863
|
+
|
|
864
|
+
// Repair tracks (draft-ramadan-moq-fec §6.3). Every fec-repair track is
|
|
865
|
+
// described once; each FEC source then assembles its base layer chain and
|
|
866
|
+
// overlays through the shared contract, which fails closed with a code and
|
|
867
|
+
// the JSON path of the offending field.
|
|
868
|
+
const repairDescriptors = trackRecords.flatMap((track, index) =>
|
|
869
|
+
track["packaging"] === "fec-repair"
|
|
870
|
+
? [describeRepairTrack(track["name"] as string, `$.tracks[${index}]`, track)]
|
|
871
|
+
: [],
|
|
872
|
+
);
|
|
873
|
+
// Rule 9 (no orphan repair track) reads the same derivation the resolver
|
|
874
|
+
// uses; see `repairReachability` in repair-layers.ts for its scope.
|
|
875
|
+
const reachability = repairReachability(
|
|
876
|
+
trackRecords.filter((track) => track["packaging"] !== "fec-repair").map((track) => track["fec"]),
|
|
877
|
+
);
|
|
878
|
+
for (const descriptor of repairDescriptors) {
|
|
879
|
+
assertRepairTrackReachable(descriptor, reachability, catalogError);
|
|
880
|
+
const target = dependsTarget(descriptor);
|
|
881
|
+
if (target === undefined) continue;
|
|
882
|
+
const source = trackRecords.find((candidate) => candidate["name"] === target);
|
|
883
|
+
const sourceFec = source?.["fec"] as Record<string, unknown> | undefined;
|
|
884
|
+
if (!source || source["packaging"] !== "mmtp" || !sourceFec || sourceFec["algorithm"] === "none") {
|
|
885
|
+
catalogError(
|
|
886
|
+
"track_repair_depends",
|
|
887
|
+
`${descriptor.path}.depends`,
|
|
888
|
+
`${JSON.stringify(target)} is not an mmtp track with FEC`,
|
|
889
|
+
);
|
|
890
|
+
}
|
|
891
|
+
}
|
|
892
|
+
for (let index = 0; index < trackRecords.length; index++) {
|
|
893
|
+
const track = trackRecords[index]!;
|
|
894
|
+
const fec = track["fec"] as Record<string, unknown> | undefined;
|
|
895
|
+
if (!fec || fec["algorithm"] === "none") continue;
|
|
896
|
+
// A dangling fec.repairTrack (missing, or not a fec-repair track) fails
|
|
897
|
+
// inside the shared contract, so the resolver and this validator agree.
|
|
898
|
+
assembleRepairLayers(
|
|
899
|
+
{
|
|
900
|
+
name: track["name"] as string,
|
|
901
|
+
path: `$.tracks[${index}].fec`,
|
|
902
|
+
algorithm: fec["algorithm"],
|
|
903
|
+
sourceSymbols: fec["sourceSymbols"],
|
|
904
|
+
repairSymbols: fec["repairSymbols"],
|
|
905
|
+
repairTrack: fec["repairTrack"],
|
|
906
|
+
},
|
|
907
|
+
repairDescriptors,
|
|
908
|
+
catalogError,
|
|
909
|
+
{ layerZeroMustBeDeclared: true },
|
|
910
|
+
);
|
|
911
|
+
}
|
|
912
|
+
|
|
913
|
+
const switchingSets = new Map<number, { name: string; ticks: bigint; timescale: bigint }>();
|
|
914
|
+
for (let index = 0; index < trackRecords.length; index++) {
|
|
915
|
+
const track = trackRecords[index]!;
|
|
916
|
+
if (track["packaging"] !== "mmtp") continue;
|
|
917
|
+
const altGroup = track["altGroup"];
|
|
918
|
+
if (!Number.isSafeInteger(altGroup)) continue;
|
|
919
|
+
const timescale = BigInt(track["timescale"] as number);
|
|
920
|
+
const durationTicks = track["groupDurationTicks"];
|
|
921
|
+
const ticks = durationTicks !== undefined
|
|
922
|
+
? BigInt(durationTicks as number)
|
|
923
|
+
: BigInt(track["groupDurationMs"] as number) * timescale / 1000n;
|
|
924
|
+
const anchor = switchingSets.get(altGroup as number);
|
|
925
|
+
if (anchor && ticks * anchor.timescale !== anchor.ticks * timescale) {
|
|
926
|
+
catalogError("track_switching_set", `$.tracks[${index}]`, `group duration does not match ${JSON.stringify(anchor.name)} in altGroup ${altGroup}`);
|
|
927
|
+
}
|
|
928
|
+
if (!anchor) switchingSets.set(altGroup as number, { name: track["name"] as string, ticks, timescale });
|
|
929
|
+
}
|
|
930
|
+
|
|
931
|
+
const multicast = catalog["multicast"];
|
|
932
|
+
if (typeof multicast === "object" && multicast !== null && !Array.isArray(multicast)) {
|
|
933
|
+
const endpoints = (multicast as Record<string, unknown>)["endpoints"];
|
|
934
|
+
if (Array.isArray(endpoints)) {
|
|
935
|
+
for (let endpointIndex = 0; endpointIndex < endpoints.length; endpointIndex++) {
|
|
936
|
+
const endpoint = endpoints[endpointIndex];
|
|
937
|
+
if (typeof endpoint !== "object" || endpoint === null || Array.isArray(endpoint)) continue;
|
|
938
|
+
const refs = (endpoint as Record<string, unknown>)["tracks"];
|
|
939
|
+
if (!Array.isArray(refs)) continue;
|
|
940
|
+
const packetIds = new Map<number, string>();
|
|
941
|
+
for (let refIndex = 0; refIndex < refs.length; refIndex++) {
|
|
942
|
+
const ref = refs[refIndex];
|
|
943
|
+
if (typeof ref !== "object" || ref === null || Array.isArray(ref)) continue;
|
|
944
|
+
const trackRef = ref as Record<string, unknown>;
|
|
945
|
+
const refPath = `$.multicast.endpoints[${endpointIndex}].tracks[${refIndex}]`;
|
|
946
|
+
const name = trackRef["name"];
|
|
947
|
+
if (typeof name !== "string" || !trackRecords.some((track) => track["name"] === name)) {
|
|
948
|
+
catalogError("multicast_track", `${refPath}.name`, `unknown catalog track ${JSON.stringify(name)}`);
|
|
949
|
+
}
|
|
950
|
+
const packetId = trackRef["packetId"];
|
|
951
|
+
if (packetId === MMTP_SIGNALING_PACKET_ID) {
|
|
952
|
+
catalogError(
|
|
953
|
+
"multicast_packet_id",
|
|
954
|
+
`${refPath}.packetId`,
|
|
955
|
+
`packetId ${packetId} is the MMTP signaling flow and must not be advertised for a track; packetId must be an integer from 1 to ${MMTP_PACKET_ID_MAX}`,
|
|
956
|
+
);
|
|
957
|
+
}
|
|
958
|
+
if (!Number.isSafeInteger(packetId) || (packetId as number) < 1 || (packetId as number) > MMTP_PACKET_ID_MAX) {
|
|
959
|
+
catalogError("multicast_packet_id", `${refPath}.packetId`, `packetId must be an integer from 1 to ${MMTP_PACKET_ID_MAX}`);
|
|
960
|
+
}
|
|
961
|
+
const previous = packetIds.get(packetId as number);
|
|
962
|
+
if (previous !== undefined) {
|
|
963
|
+
catalogError("multicast_packet_id", `${refPath}.packetId`, `packetId is already assigned to ${JSON.stringify(previous)}`);
|
|
964
|
+
}
|
|
965
|
+
packetIds.set(packetId as number, name as string);
|
|
966
|
+
}
|
|
967
|
+
}
|
|
968
|
+
}
|
|
969
|
+
}
|
|
970
|
+
|
|
971
|
+
try {
|
|
972
|
+
resolveMulticastAuthCatalog(catalog);
|
|
973
|
+
} catch (error) {
|
|
974
|
+
catalogError(
|
|
975
|
+
"multicast_auth",
|
|
976
|
+
"$.multicast.auth",
|
|
977
|
+
error instanceof Error ? error.message : "invalid multicast.auth v2 block",
|
|
978
|
+
);
|
|
979
|
+
}
|
|
980
|
+
|
|
981
|
+
validateArchive(catalog);
|
|
982
|
+
|
|
983
|
+
return catalog as CmsfCatalog;
|
|
984
|
+
}
|
|
985
|
+
|
|
986
|
+
/**
|
|
987
|
+
* Resolve a track's group duration to integer ticks using the supplied
|
|
988
|
+
* timescale (Hz). Implements mmt-00 §4.4.4:
|
|
989
|
+
*
|
|
990
|
+
* - If `groupDurationTicks` is present, return it (override wins).
|
|
991
|
+
* - Else compute `groupDurationMs * timescale / 1000`. The result MUST
|
|
992
|
+
* be an exact integer; throws if `(ms * timescale) % 1000 != 0`.
|
|
993
|
+
*
|
|
994
|
+
* Throws on invalid timing config or non-positive timescale (CLAUDE.md:
|
|
995
|
+
* error on bad init).
|
|
996
|
+
*/
|
|
997
|
+
export function resolveGroupDurationTicks(
|
|
998
|
+
timing: TrackTimingConfig,
|
|
999
|
+
timescale: number,
|
|
1000
|
+
): number {
|
|
1001
|
+
if (!Number.isFinite(timescale) || !Number.isInteger(timescale) || timescale <= 0) {
|
|
1002
|
+
throw new Error(
|
|
1003
|
+
`[carp-catalog] resolveGroupDurationTicks: timescale must be a positive integer Hz, got ${timescale}`,
|
|
1004
|
+
);
|
|
1005
|
+
}
|
|
1006
|
+
if (!isTrackTimingConfig(timing)) {
|
|
1007
|
+
throw new Error(
|
|
1008
|
+
`[carp-catalog] resolveGroupDurationTicks: invalid TrackTimingConfig: ${JSON.stringify(timing)}`,
|
|
1009
|
+
);
|
|
1010
|
+
}
|
|
1011
|
+
if (timing.groupDurationTicks !== undefined) {
|
|
1012
|
+
return timing.groupDurationTicks;
|
|
1013
|
+
}
|
|
1014
|
+
if (timing.groupDurationMs === undefined) {
|
|
1015
|
+
throw new Error(
|
|
1016
|
+
"[carp-catalog] resolveGroupDurationTicks: groupDurationMs is required when groupDurationTicks is absent",
|
|
1017
|
+
);
|
|
1018
|
+
}
|
|
1019
|
+
const product = timing.groupDurationMs * timescale;
|
|
1020
|
+
if (product % 1000 !== 0) {
|
|
1021
|
+
throw new Error(
|
|
1022
|
+
`[carp-catalog] resolveGroupDurationTicks: groupDurationMs=${timing.groupDurationMs} at timescale=${timescale} ` +
|
|
1023
|
+
`produces non-integer ticks (${product / 1000}); publisher must use groupDurationTicks override (mmt-00 §4.4.4)`,
|
|
1024
|
+
);
|
|
1025
|
+
}
|
|
1026
|
+
return product / 1000;
|
|
1027
|
+
}
|
|
1028
|
+
|
|
1029
|
+
/**
|
|
1030
|
+
* Validate that every track in a switching set agrees on the same
|
|
1031
|
+
* wall-clock group duration (mmt-00 §4.4.4). Per-track timescales may
|
|
1032
|
+
* differ, so raw tick counts cannot be compared directly.
|
|
1033
|
+
*
|
|
1034
|
+
* Returns `{ ok: true }` when all tracks agree (or the set is empty);
|
|
1035
|
+
* `{ ok: false, mismatches }` listing each track whose effective ticks
|
|
1036
|
+
* differ from the first track's value. Subscribers per spec SHOULD
|
|
1037
|
+
* reject the catalog on mismatch.
|
|
1038
|
+
*
|
|
1039
|
+
* Throws if any track's timing config is invalid (per
|
|
1040
|
+
* `resolveGroupDurationTicks`).
|
|
1041
|
+
*/
|
|
1042
|
+
export function validateSwitchingSetTiming(
|
|
1043
|
+
tracks: Array<{ name: string; timing: TrackTimingConfig; timescale: number }>,
|
|
1044
|
+
):
|
|
1045
|
+
| { ok: true }
|
|
1046
|
+
| { ok: false; expectedTicks: number; mismatches: Array<{ name: string; ticks: number }> } {
|
|
1047
|
+
if (tracks.length === 0) return { ok: true };
|
|
1048
|
+
const ticksByTrack = tracks.map((t) => ({
|
|
1049
|
+
name: t.name,
|
|
1050
|
+
ticks: resolveGroupDurationTicks(t.timing, t.timescale),
|
|
1051
|
+
timescale: t.timescale,
|
|
1052
|
+
}));
|
|
1053
|
+
const expectedTicks = ticksByTrack[0].ticks;
|
|
1054
|
+
const expectedTimescale = ticksByTrack[0].timescale;
|
|
1055
|
+
const mismatches = ticksByTrack
|
|
1056
|
+
.slice(1)
|
|
1057
|
+
.filter(
|
|
1058
|
+
(t) =>
|
|
1059
|
+
BigInt(t.ticks) * BigInt(expectedTimescale) !==
|
|
1060
|
+
BigInt(expectedTicks) * BigInt(t.timescale),
|
|
1061
|
+
)
|
|
1062
|
+
.map(({ name, ticks }) => ({ name, ticks }));
|
|
1063
|
+
if (mismatches.length === 0) return { ok: true };
|
|
1064
|
+
return { ok: false, expectedTicks, mismatches };
|
|
1065
|
+
}
|