@blockcast/mmt-container 0.1.0-main.149f78a4adae
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 +432 -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 +148 -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 +93 -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/browser-runtime.js +17109 -0
- package/dist/browser-runtime.js.map +7 -0
- package/dist/carp-catalog.d.ts +318 -0
- package/dist/carp-catalog.d.ts.map +1 -0
- package/dist/carp-catalog.js +549 -0
- package/dist/carp-catalog.js.map +7 -0
- package/dist/catalog-fec-params.d.ts +82 -0
- package/dist/catalog-fec-params.d.ts.map +1 -0
- package/dist/catalog-fec-params.js +66 -0
- package/dist/catalog-fec-params.js.map +7 -0
- package/dist/catalog-network-source.d.ts +19 -0
- package/dist/catalog-network-source.d.ts.map +1 -0
- package/dist/catalog-network-source.js +22 -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 +168 -0
- package/dist/cmaf-block-assembler.d.ts.map +1 -0
- package/dist/cmaf-block-assembler.js +1755 -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 +195 -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 +207 -0
- package/dist/cmcd-producer.js.map +7 -0
- package/dist/codec-utils.d.ts +235 -0
- package/dist/codec-utils.d.ts.map +1 -0
- package/dist/codec-utils.js +1225 -0
- package/dist/codec-utils.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 +266 -0
- package/dist/fec-manager.d.ts.map +1 -0
- package/dist/fec-manager.js +1447 -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 +75 -0
- package/dist/fec-starvation-detector.js.map +7 -0
- package/dist/fec-worker-setup.d.ts +76 -0
- package/dist/fec-worker-setup.d.ts.map +1 -0
- package/dist/fec-worker-setup.js +603 -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 +47 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +330 -0
- package/dist/index.js.map +7 -0
- package/dist/iso-bmff-timing.d.ts +47 -0
- package/dist/iso-bmff-timing.d.ts.map +1 -0
- package/dist/iso-bmff-timing.js +526 -0
- package/dist/iso-bmff-timing.js.map +7 -0
- package/dist/loc.d.ts +68 -0
- package/dist/loc.d.ts.map +1 -0
- package/dist/loc.js +143 -0
- package/dist/loc.js.map +7 -0
- 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 +1267 -0
- package/dist/manifest-auth-plan.js.map +7 -0
- package/dist/media-router.d.ts +338 -0
- package/dist/media-router.d.ts.map +1 -0
- package/dist/media-router.js +802 -0
- package/dist/media-router.js.map +7 -0
- package/dist/mfu-reassembler.d.ts +264 -0
- package/dist/mfu-reassembler.d.ts.map +1 -0
- package/dist/mfu-reassembler.js +2668 -0
- package/dist/mfu-reassembler.js.map +7 -0
- 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 +304 -0
- package/dist/mmtp-container-parser.d.ts.map +1 -0
- package/dist/mmtp-container-parser.js +5378 -0
- package/dist/mmtp-container-parser.js.map +7 -0
- package/dist/mmtp-container-parser.no-modules.js +7853 -0
- package/dist/mmtp-container-parser.no-modules.js.map +7 -0
- package/dist/mmtp-decode-pipeline.d.ts +96 -0
- package/dist/mmtp-decode-pipeline.d.ts.map +1 -0
- package/dist/mmtp-decode-pipeline.js +5589 -0
- package/dist/mmtp-decode-pipeline.js.map +7 -0
- package/dist/mmtp-router.d.ts +383 -0
- package/dist/mmtp-router.d.ts.map +1 -0
- package/dist/mmtp-router.js +8384 -0
- package/dist/mmtp-router.js.map +7 -0
- package/dist/mmtp-to-cmaf.d.ts +166 -0
- package/dist/mmtp-to-cmaf.d.ts.map +1 -0
- package/dist/mmtp-to-cmaf.js +781 -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 +107 -0
- package/dist/multicast-input.d.ts.map +1 -0
- package/dist/multicast-input.js +256 -0
- package/dist/multicast-input.js.map +7 -0
- package/dist/multicast-manager.d.ts +330 -0
- package/dist/multicast-manager.d.ts.map +1 -0
- package/dist/multicast-manager.js +11032 -0
- package/dist/multicast-manager.js.map +7 -0
- package/dist/multicast-transport.d.ts +62 -0
- package/dist/multicast-transport.d.ts.map +1 -0
- package/dist/multicast-transport.js +338 -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 +384 -0
- package/dist/multicast-unwrap.js.map +7 -0
- package/dist/no-modules.d.ts +9 -0
- package/dist/no-modules.d.ts.map +1 -0
- package/dist/no-modules.js +7819 -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 +58 -0
- package/dist/repair-fetch.d.ts.map +1 -0
- package/dist/repair-fetch.js +117 -0
- package/dist/repair-fetch.js.map +7 -0
- package/dist/sample-combiner.d.ts +182 -0
- package/dist/sample-combiner.d.ts.map +1 -0
- package/dist/sample-combiner.js +571 -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 +232 -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 +1220 -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 +85 -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 +456 -0
- package/dist/timestamp-codec.js.map +7 -0
- package/dist/track-pipeline-setup.d.ts +72 -0
- package/dist/track-pipeline-setup.d.ts.map +1 -0
- package/dist/track-pipeline-setup.js +306 -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 +229 -0
- package/dist/track-pipeline.js.map +7 -0
- package/dist/track-session.d.ts +269 -0
- package/dist/track-session.d.ts.map +1 -0
- package/dist/track-session.js +597 -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 +88 -0
- package/dist/transport-pool.js.map +7 -0
- package/dist/wasm-mfu-reassembler.d.ts +81 -0
- package/dist/wasm-mfu-reassembler.d.ts.map +1 -0
- package/dist/wasm-mfu-reassembler.js +1915 -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 +5635 -0
- package/dist/wasm-mmtp-container-parser.js.map +7 -0
- package/node_modules/@blockcast/fec-worker/README.md +213 -0
- package/node_modules/@blockcast/fec-worker/dist/.build-stamp +0 -0
- package/node_modules/@blockcast/fec-worker/dist/fec-worker-client.d.ts +80 -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 +270 -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 +252 -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 +565 -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 +349 -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 +179 -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 +47 -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/fec-worker-client.ts +309 -0
- package/node_modules/@blockcast/fec-worker/src/fec-worker-types.ts +268 -0
- package/node_modules/@blockcast/fec-worker/src/fec-worker.ts +1048 -0
- package/node_modules/@blockcast/fec-worker/src/index.ts +32 -0
- package/node_modules/@blockcast/fec-worker/src/shred-fec-worker.ts +558 -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 +194 -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 +120 -0
- package/node_modules/@blockcast/mmt-base/dist/.build-stamp +0 -0
- package/node_modules/@blockcast/mmt-base/dist/index.d.ts +2 -0
- package/node_modules/@blockcast/mmt-base/dist/index.d.ts.map +1 -0
- package/node_modules/@blockcast/mmt-base/dist/index.js +2 -0
- package/node_modules/@blockcast/mmt-base/dist/index.js.map +1 -0
- package/node_modules/@blockcast/mmt-base/dist/mmtp.d.ts +275 -0
- package/node_modules/@blockcast/mmt-base/dist/mmtp.d.ts.map +1 -0
- package/node_modules/@blockcast/mmt-base/dist/mmtp.js +644 -0
- package/node_modules/@blockcast/mmt-base/dist/mmtp.js.map +1 -0
- package/node_modules/@blockcast/mmt-base/package.json +49 -0
- package/node_modules/@blockcast/mmt-base/src/index.ts +1 -0
- package/node_modules/@blockcast/mmt-base/src/mmtp.ts +898 -0
- package/node_modules/@blockcast/mmt-fec/README.md +270 -0
- package/node_modules/@blockcast/mmt-fec/dist/.build-stamp +0 -0
- package/node_modules/@blockcast/mmt-fec/dist/block-manager.d.ts +80 -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 +294 -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 +72 -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 +192 -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 +185 -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 +273 -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 +22 -0
- package/node_modules/@blockcast/mmt-fec/dist/index.d.ts.map +1 -0
- package/node_modules/@blockcast/mmt-fec/dist/index.js +22 -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 +174 -0
- package/node_modules/@blockcast/mmt-fec/dist/oti.d.ts.map +1 -0
- package/node_modules/@blockcast/mmt-fec/dist/oti.js +256 -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/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 +120 -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/index.d.ts +2 -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 +2 -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 +275 -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 +644 -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/package.json +49 -0
- package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/src/index.ts +1 -0
- package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/src/mmtp.ts +898 -0
- package/node_modules/@blockcast/mmt-fec/package.json +79 -0
- package/node_modules/@blockcast/mmt-fec/src/block-manager.ts +369 -0
- package/node_modules/@blockcast/mmt-fec/src/block-processor.ts +277 -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 +332 -0
- package/node_modules/@blockcast/mmt-fec/src/index.ts +77 -0
- package/node_modules/@blockcast/mmt-fec/src/loc-symbols.ts +34 -0
- package/node_modules/@blockcast/mmt-fec/src/oti.ts +319 -0
- package/node_modules/@blockcast/mmt-fec/src/reorder-buffer.ts +101 -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 +90 -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 +462 -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 +636 -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 +203 -0
- package/schemas/catalog.schema.json +283 -0
- package/src/align.ts +235 -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/carp-catalog.ts +776 -0
- package/src/catalog-fec-params.ts +167 -0
- package/src/catalog-network-source.ts +49 -0
- package/src/catalog-validator.ts +34 -0
- package/src/cmaf-block-assembler.ts +1058 -0
- package/src/cmaf-group-batcher.ts +104 -0
- package/src/cmcd-producer.ts +329 -0
- package/src/codec-utils.ts +1539 -0
- package/src/fec-codec.ts +61 -0
- package/src/fec-manager.ts +1939 -0
- package/src/fec-starvation-detector.ts +128 -0
- package/src/fec-worker-setup.ts +177 -0
- package/src/fmp4-completion.ts +47 -0
- package/src/fmp4-reader.ts +112 -0
- package/src/index.ts +443 -0
- package/src/iso-bmff-timing.ts +688 -0
- package/src/loc.ts +181 -0
- package/src/manifest-auth-plan.ts +107 -0
- package/src/media-router.ts +1185 -0
- package/src/mfu-reassembler.ts +2842 -0
- package/src/mfu-reassembly-policy.ts +134 -0
- package/src/mmtp-container-parser.ts +1498 -0
- package/src/mmtp-decode-pipeline.ts +315 -0
- package/src/mmtp-router.ts +3027 -0
- package/src/mmtp-to-cmaf.ts +956 -0
- package/src/multicast-auth-catalog.ts +340 -0
- package/src/multicast-input.ts +439 -0
- package/src/multicast-manager.ts +1503 -0
- package/src/multicast-transport.ts +320 -0
- package/src/multicast-unwrap.ts +132 -0
- package/src/no-modules.ts +20 -0
- package/src/phase14-catalog-override.ts +336 -0
- package/src/repair-fetch.ts +217 -0
- package/src/sample-combiner.ts +846 -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 +189 -0
- package/src/track-pipeline.ts +332 -0
- package/src/track-session.ts +778 -0
- package/src/transport-pool.ts +128 -0
- package/src/wasm-mfu-reassembler.ts +372 -0
- package/src/wasm-mmtp-container-parser.ts +23 -0
- package/test-vectors/align.json +199 -0
- package/test-vectors/catalog/README.md +25 -0
- package/test-vectors/catalog/negative/legacy-selection-params.json +15 -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/network-source-object.json +25 -0
- package/test-vectors/catalog/negative/raptorq-unaligned-symbol.json +26 -0
- package/test-vectors/catalog/negative/repair-track-legacy-shape.json +18 -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/fec-multicast.json +51 -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/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 +148 -0
- package/test-vectors/catalog-validator-public-api.mjs +25 -0
- package/test-vectors/generate-source-auth-fixture.mjs +114 -0
- package/test-vectors/source-auth-v1.json +61 -0
- package/test-vectors/subsample-placement-v1.json +230 -0
|
@@ -0,0 +1,776 @@
|
|
|
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 { resolveMulticastAuthCatalog, type MulticastAuthConfig } from "./multicast-auth-catalog.js";
|
|
12
|
+
|
|
13
|
+
// ── Track Role (Phase 14, D-02..D-05) ─────────────────────────
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Track role derived from the last slash-segment of the track name.
|
|
17
|
+
*
|
|
18
|
+
* Convention: `{content}/base`, `{content}/delta`, `{content}/repair`.
|
|
19
|
+
* Single-track (no slash) legacy streams use `"single"`.
|
|
20
|
+
*
|
|
21
|
+
* D-01: No `trackRole` / `role` / `kind` field in the catalog.
|
|
22
|
+
* D-02: Role is expressed via track name suffix only.
|
|
23
|
+
* D-03: Unknown suffixes throw at parse time (error on bad init).
|
|
24
|
+
* D-04: Legacy single-track streams (no slash suffix) return `"single"`.
|
|
25
|
+
* D-05: Single role per track — no multi-role arrays.
|
|
26
|
+
*/
|
|
27
|
+
export type TrackRole = "base" | "delta" | "repair" | "single";
|
|
28
|
+
|
|
29
|
+
const VALID_ROLE_SUFFIXES = new Set<string>(["base", "delta", "repair"]);
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Derive the role of a track from its name.
|
|
33
|
+
*
|
|
34
|
+
* - Names with no `/` → `"single"` (legacy single-track streams, D-04).
|
|
35
|
+
* - Names ending with `/base`, `/delta`, or `/repair` → that role (D-02).
|
|
36
|
+
* - Empty name → throws (error on bad init).
|
|
37
|
+
* - Trailing slash (empty last segment) → throws.
|
|
38
|
+
* - Non-ASCII or control characters anywhere in the name → throws (T-14-01 fuzz hardening).
|
|
39
|
+
* - Unknown last segment → throws.
|
|
40
|
+
*
|
|
41
|
+
* @throws {Error} On empty name, non-ASCII/control chars, empty suffix, or unknown suffix.
|
|
42
|
+
*/
|
|
43
|
+
export function parseTrackRole(trackName: string): TrackRole {
|
|
44
|
+
if (trackName === "") {
|
|
45
|
+
throw new Error("[carp-catalog] empty track name");
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// T-14-01 fuzz hardening: reject non-ASCII and control characters.
|
|
49
|
+
// This prevents catalog injection attacks where a publisher sends a crafted
|
|
50
|
+
// track name that bypasses role checks by embedding unusual code points.
|
|
51
|
+
for (let i = 0; i < trackName.length; i++) {
|
|
52
|
+
const cp = trackName.charCodeAt(i);
|
|
53
|
+
// Reject control characters (0x00-0x1F, 0x7F) and non-ASCII (>0x7E)
|
|
54
|
+
if (cp <= 0x1f || cp === 0x7f || cp > 0x7e) {
|
|
55
|
+
throw new Error(
|
|
56
|
+
`[carp-catalog] track name contains non-ASCII or control character (code point 0x${cp.toString(16)}) in "${trackName}"`,
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const lastSlash = trackName.lastIndexOf("/");
|
|
62
|
+
if (lastSlash === -1) {
|
|
63
|
+
// No slash — legacy single-track stream (D-04)
|
|
64
|
+
return "single";
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const suffix = trackName.slice(lastSlash + 1);
|
|
68
|
+
if (suffix === "") {
|
|
69
|
+
throw new Error(
|
|
70
|
+
`[carp-catalog] empty role suffix in track name "${trackName}"; track names must not end with "/"`,
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
if (!VALID_ROLE_SUFFIXES.has(suffix)) {
|
|
75
|
+
throw new Error(
|
|
76
|
+
`[carp-catalog] unknown role suffix "${suffix}" in track name "${trackName}"; expected one of: base, delta, repair`,
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
return suffix as TrackRole;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Non-throwing switching-set detector (BLO-8158).
|
|
85
|
+
*
|
|
86
|
+
* Returns `true` iff at least one track name carries a media role suffix
|
|
87
|
+
* (`base` or `delta`) — i.e. the set looks like a base/delta/repair switching
|
|
88
|
+
* set. A `/repair` track alone is ordinary FEC topology and is not sufficient
|
|
89
|
+
* evidence of a switching set. Unlike `parseTrackRole`, this NEVER throws:
|
|
90
|
+
* names without a `/`, and names with an unrecognised suffix (e.g. an ABR
|
|
91
|
+
* resolution ladder like `video/1080p`, `video/720p`, `video/8k`), simply do
|
|
92
|
+
* not count as a role.
|
|
93
|
+
*
|
|
94
|
+
* Use this for DETECTION ("is this a switching set?"). Once a switching set is
|
|
95
|
+
* confirmed, `parseTrackRole` / `validateSwitchingSet` still strictly gate the
|
|
96
|
+
* actual wiring. ABR ladders yield `false` here and fall through to the
|
|
97
|
+
* ABR/single path instead of throwing.
|
|
98
|
+
*/
|
|
99
|
+
export function hasSwitchingSetRole(names: string[]): boolean {
|
|
100
|
+
return names.some((name) => {
|
|
101
|
+
const lastSlash = name.lastIndexOf("/");
|
|
102
|
+
if (lastSlash === -1) return false;
|
|
103
|
+
const suffix = name.slice(lastSlash + 1);
|
|
104
|
+
return suffix === "base" || suffix === "delta";
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Validate that a set of track names forms a complete switching set.
|
|
110
|
+
*
|
|
111
|
+
* - Empty or single-track sets (all parsing to `"single"`) → `{ ok: true }`.
|
|
112
|
+
* - Multi-track sets (any name has a role suffix) MUST include all three
|
|
113
|
+
* roles (`base`, `delta`, `repair`). Missing roles are listed.
|
|
114
|
+
*
|
|
115
|
+
* Propagates throws from `parseTrackRole` (non-ASCII, unknown suffix, etc.).
|
|
116
|
+
*
|
|
117
|
+
* D-04: Legacy single-track streams pass without enforcement.
|
|
118
|
+
* D-04: Once any suffix appears, the set is multi-track and must be complete.
|
|
119
|
+
*/
|
|
120
|
+
export function validateSwitchingSet(
|
|
121
|
+
trackNames: string[],
|
|
122
|
+
): { ok: true } | { ok: false; missing: TrackRole[] } {
|
|
123
|
+
if (trackNames.length === 0) return { ok: true };
|
|
124
|
+
|
|
125
|
+
const roles = trackNames.map(parseTrackRole);
|
|
126
|
+
|
|
127
|
+
// If every role is "single", this is a legacy single-track stream — pass.
|
|
128
|
+
if (roles.every((r) => r === "single")) {
|
|
129
|
+
return { ok: true };
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// Multi-track: at least one name has a role suffix.
|
|
133
|
+
// The set MUST contain all three required roles.
|
|
134
|
+
// Explicit Set<TrackRole> so the TrackRole-typed `required.filter`
|
|
135
|
+
// callback can call .has(r) without a narrow-mismatch complaint.
|
|
136
|
+
const presentRoles: Set<TrackRole> = new Set(roles.filter((r) => r !== "single"));
|
|
137
|
+
const required: TrackRole[] = ["base", "delta", "repair"];
|
|
138
|
+
const missing = required.filter((r) => !presentRoles.has(r));
|
|
139
|
+
|
|
140
|
+
if (missing.length > 0) {
|
|
141
|
+
return { ok: false, missing };
|
|
142
|
+
}
|
|
143
|
+
return { ok: true };
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// ── FEC Catalog Extension (CARP §3.2) ─────────────────────────
|
|
147
|
+
|
|
148
|
+
export type FecAlgorithm = "none" | "raptorq" | "reed-solomon";
|
|
149
|
+
|
|
150
|
+
export interface FecCatalogConfig {
|
|
151
|
+
/** FEC algorithm identifier (REQUIRED per §4.1, values per §3.1). */
|
|
152
|
+
algorithm: FecAlgorithm;
|
|
153
|
+
/** K — source symbols per block (REQUIRED). */
|
|
154
|
+
sourceSymbols: number;
|
|
155
|
+
/** P — repair symbols per block (REQUIRED). */
|
|
156
|
+
repairSymbols: number;
|
|
157
|
+
/** T — bytes per symbol, 8-byte aligned (REQUIRED). */
|
|
158
|
+
symbolSize: number;
|
|
159
|
+
/** FEC block span in MILLISECONDS (OPTIONAL, default 0 = single-group blocks).
|
|
160
|
+
* D is the positive round-half-up value of interleaveDepthMs / GOP_duration_ms.
|
|
161
|
+
* BLO-14526: renamed from the legacy `interleaveDepth` count-era key — no fallback read. */
|
|
162
|
+
interleaveDepthMs?: number;
|
|
163
|
+
/** Declared jitter/reorder allowance in MILLISECONDS.
|
|
164
|
+
* Added to interleaveDepthMs to form a block's deadline; never multiplied
|
|
165
|
+
* by it. Jitter is a property of the network path and the block span is a
|
|
166
|
+
* property of the encoder, so a multiplicative "safety factor" couples two
|
|
167
|
+
* independent quantities and grants different slack per track for the same
|
|
168
|
+
* network. Consumed identically by FECManager, FEC workers and player
|
|
169
|
+
* policies so one signaled value governs end to end. */
|
|
170
|
+
reorderToleranceMs?: number;
|
|
171
|
+
/** Track name for repair symbols (REQUIRED per §6.1). */
|
|
172
|
+
repairTrack: string;
|
|
173
|
+
/** FEC block mode: "interleaved" (default) or "object" (per-frame). */
|
|
174
|
+
// extensions feature — see moqcast-draft docs/fec-extensions-material.md, not in public -00
|
|
175
|
+
mode?: "interleaved" | "object";
|
|
176
|
+
/** UEP (Unequal Error Protection) — extensions feature, see moqcast-draft docs/fec-extensions-material.md, not in public -00. When present, repair count
|
|
177
|
+
* varies per frame type. repairSymbols becomes the base (P-frame) count.
|
|
178
|
+
* MUST be omitted on multi-track base/delta streams (D-08). */
|
|
179
|
+
uep?: {
|
|
180
|
+
/** Keyframe repair ratio: P = ceil(K * keyRatio). Default 0.5. */
|
|
181
|
+
keyRatio: number;
|
|
182
|
+
/** Delta frame repair ratio: P = ceil(K * deltaRatio). Default 0.25. */
|
|
183
|
+
deltaRatio: number;
|
|
184
|
+
};
|
|
185
|
+
/**
|
|
186
|
+
* Base-only fallback window in FEC blocks (D-11, Phase 14).
|
|
187
|
+
*
|
|
188
|
+
* When the delta track's FEC block failure rate exceeds the derived threshold
|
|
189
|
+
* over this many consecutive blocks, MediaRouter falls back to base-only mode.
|
|
190
|
+
* Derivation: framerate * fallback_window_seconds. If absent, construction
|
|
191
|
+
* of the multi-track subscriber MUST throw (error on bad init).
|
|
192
|
+
*
|
|
193
|
+
* Present only on the base track's fec config (not the delta or repair track).
|
|
194
|
+
*/
|
|
195
|
+
deltaFailWindowBlocks?: number;
|
|
196
|
+
/**
|
|
197
|
+
* Delta re-subscribe probe interval in milliseconds (D-13, Phase 14).
|
|
198
|
+
*
|
|
199
|
+
* After a base-only fallback, wait this long before re-subscribing to the
|
|
200
|
+
* delta track to check if conditions have improved.
|
|
201
|
+
* Derivation: computeFecDeliveryWindowMs(...) — the single FEC recovery-window
|
|
202
|
+
* source of truth (draft-ramadan-moq-fec §9). Never a fixed interleave multiple.
|
|
203
|
+
*
|
|
204
|
+
* Present only on the base track's fec config (not the delta or repair track).
|
|
205
|
+
*/
|
|
206
|
+
deltaProbeIntervalMs?: number;
|
|
207
|
+
/**
|
|
208
|
+
* Jitter + one-way-delay + reorder budget in MILLISECONDS (draft §9).
|
|
209
|
+
*
|
|
210
|
+
* The single catalog source for the ADDITIVE margin that the FEC
|
|
211
|
+
* recovery/loss window adds on top of max(interleave span, source+repair horizon,
|
|
212
|
+
* block serialization) — see computeFecDeliveryWindowMs. It is a margin, not
|
|
213
|
+
* a multiplier. OPTIONAL: when absent, the derivation falls back to the
|
|
214
|
+
* un-rounded interleave window as the reorder-budget bound (margin-policy).
|
|
215
|
+
*/
|
|
216
|
+
jitterBufferMs?: number;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
export function isFecCatalogConfig(obj: unknown): obj is FecCatalogConfig {
|
|
220
|
+
if (typeof obj !== "object" || obj === null) return false;
|
|
221
|
+
const o = obj as Record<string, unknown>;
|
|
222
|
+
const algorithm = o["algorithm"];
|
|
223
|
+
const symbolSize = o["symbolSize"];
|
|
224
|
+
return (
|
|
225
|
+
typeof algorithm === "string" &&
|
|
226
|
+
(algorithm === "none" || algorithm === "raptorq" || algorithm === "reed-solomon") &&
|
|
227
|
+
typeof o["sourceSymbols"] === "number" && o["sourceSymbols"] >= 1 &&
|
|
228
|
+
typeof o["repairSymbols"] === "number" && o["repairSymbols"] >= 1 &&
|
|
229
|
+
typeof symbolSize === "number" && symbolSize >= 1 &&
|
|
230
|
+
(algorithm !== "raptorq" || symbolSize % 8 === 0) &&
|
|
231
|
+
typeof o["repairTrack"] === "string" && (o["repairTrack"] as string).length > 0
|
|
232
|
+
);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
// ── Multicast Catalog Extension (CARP §4.1-4.4) ───────────────
|
|
236
|
+
|
|
237
|
+
export type MulticastProtocol = "ssm" | "asm" | "amt" | "atsc3";
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* Per-track endpoint membership, per draft-ramadan-moq-multicast-00 §4.1.
|
|
241
|
+
*
|
|
242
|
+
* `packetId` is the MMTP packet_id used for packet-level demux on the
|
|
243
|
+
* multicast wire. New emitters MUST use this object form; the legacy
|
|
244
|
+
* `string` form is accepted by parsers for one release for backward
|
|
245
|
+
* compatibility (BLO-3323 migration).
|
|
246
|
+
*/
|
|
247
|
+
export interface MulticastTrackObject {
|
|
248
|
+
name: string;
|
|
249
|
+
packetId: number;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
/** Track entry in a multicast endpoint — accepts both legacy and v2 shape. */
|
|
253
|
+
export type MulticastTrack = string | MulticastTrackObject;
|
|
254
|
+
|
|
255
|
+
export interface MulticastEndpoint {
|
|
256
|
+
/** Transport protocol. Defaults to SSM when sourceAddress is present. */
|
|
257
|
+
protocol?: MulticastProtocol;
|
|
258
|
+
/** SSM source IP address (REQUIRED for SSM). */
|
|
259
|
+
sourceAddress?: string;
|
|
260
|
+
/** Multicast group address (REQUIRED). */
|
|
261
|
+
groupAddress: string;
|
|
262
|
+
/** UDP port number (REQUIRED). */
|
|
263
|
+
port: number;
|
|
264
|
+
/**
|
|
265
|
+
* Tracks available on this endpoint (REQUIRED).
|
|
266
|
+
*
|
|
267
|
+
* Accepts both legacy `string` (track name only) and v2 object form
|
|
268
|
+
* (`{ name, packetId }`) for backward compatibility. New emitters
|
|
269
|
+
* MUST use the object form (BLO-3323 / draft §4.1). Consumers can
|
|
270
|
+
* type-narrow with `typeof t === "string"` to handle both.
|
|
271
|
+
*/
|
|
272
|
+
tracks: MulticastTrack[];
|
|
273
|
+
/** AMT relay hostname (REQUIRED for AMT). */
|
|
274
|
+
relayAddress?: string;
|
|
275
|
+
/**
|
|
276
|
+
* Aggregate bandwidth of this endpoint in bits per second.
|
|
277
|
+
* RECOMMENDED per draft §4.1 — enables capacity-aware joins.
|
|
278
|
+
*/
|
|
279
|
+
bandwidth?: number;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
export type AmtDiscovery = "driad" | "manual";
|
|
283
|
+
|
|
284
|
+
export interface MulticastCatalogConfig {
|
|
285
|
+
/** Enable/disable multicast (OPTIONAL, default true). */
|
|
286
|
+
enabled?: boolean;
|
|
287
|
+
/** Multicast endpoints (REQUIRED). */
|
|
288
|
+
endpoints: MulticastEndpoint[];
|
|
289
|
+
/** AMT fallback configuration (OPTIONAL). */
|
|
290
|
+
amtFallback?: {
|
|
291
|
+
discovery: AmtDiscovery;
|
|
292
|
+
relayDns?: string;
|
|
293
|
+
};
|
|
294
|
+
/**
|
|
295
|
+
* Monotonic catalog version. Publishers MUST bump on any
|
|
296
|
+
* `endpoints[]` mutation (BLO-3323 catalog freshness model).
|
|
297
|
+
*/
|
|
298
|
+
version?: number;
|
|
299
|
+
/**
|
|
300
|
+
* Unix-ms timestamp after which subscribers SHOULD refetch the
|
|
301
|
+
* catalog. Refetch-on-multicast-receive-timeout short-circuits this.
|
|
302
|
+
*/
|
|
303
|
+
validUntil?: number;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
export function isMulticastTrackObject(obj: unknown): obj is MulticastTrackObject {
|
|
307
|
+
if (typeof obj !== "object" || obj === null) return false;
|
|
308
|
+
const o = obj as Record<string, unknown>;
|
|
309
|
+
return typeof o["name"] === "string" && typeof o["packetId"] === "number";
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
/**
|
|
313
|
+
* Wire-shape predicate: returns `true` when `obj` matches the
|
|
314
|
+
* MulticastEndpoint JSON shape strictly enough to be safely cast.
|
|
315
|
+
*
|
|
316
|
+
* Intentionally less strict than `multicastFromConfig`'s
|
|
317
|
+
* `validateEndpoint` (in `multicast-input.ts`):
|
|
318
|
+
*
|
|
319
|
+
* - `tracks: []` is accepted here (Array.isArray check only) but
|
|
320
|
+
* rejected by `validateEndpoint` as `empty_tracks`.
|
|
321
|
+
* - SSM-specific group-range and source-required checks are not
|
|
322
|
+
* enforced here.
|
|
323
|
+
* - Bandwidth/AmtFallback/version are not type-checked.
|
|
324
|
+
*
|
|
325
|
+
* That asymmetry is deliberate: this predicate guards JSON parsed off
|
|
326
|
+
* the wire (already-published catalogs that downstream may have
|
|
327
|
+
* trimmed), while `validateEndpoint` guards publisher input *before*
|
|
328
|
+
* a catalog is composed. Subscribers are tolerant; publishers are
|
|
329
|
+
* strict — the catalog format never re-validates upstream invariants.
|
|
330
|
+
*/
|
|
331
|
+
export function isMulticastEndpoint(obj: unknown): obj is MulticastEndpoint {
|
|
332
|
+
if (typeof obj !== "object" || obj === null) return false;
|
|
333
|
+
const o = obj as Record<string, unknown>;
|
|
334
|
+
const protocol = o["protocol"] ??
|
|
335
|
+
(typeof o["sourceAddress"] === "string" && o["sourceAddress"].length > 0 ? "ssm" : undefined);
|
|
336
|
+
return (
|
|
337
|
+
typeof protocol === "string" &&
|
|
338
|
+
(protocol === "ssm" || protocol === "asm" || protocol === "amt" || protocol === "atsc3") &&
|
|
339
|
+
typeof o["groupAddress"] === "string" && (o["groupAddress"] as string).length > 0 &&
|
|
340
|
+
typeof o["port"] === "number" && (o["port"] as number) > 0 && (o["port"] as number) <= 65535 &&
|
|
341
|
+
Array.isArray(o["tracks"]) &&
|
|
342
|
+
(o["tracks"] as unknown[]).every((t: unknown) => typeof t === "string" || isMulticastTrackObject(t))
|
|
343
|
+
);
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
export function isMulticastCatalogConfig(obj: unknown): obj is MulticastCatalogConfig {
|
|
347
|
+
if (typeof obj !== "object" || obj === null) return false;
|
|
348
|
+
const o = obj as Record<string, unknown>;
|
|
349
|
+
return (
|
|
350
|
+
Array.isArray(o["endpoints"]) &&
|
|
351
|
+
(o["endpoints"] as unknown[]).every((ep: unknown) => isMulticastEndpoint(ep))
|
|
352
|
+
);
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
// ── Track Timing (mmt-00 §4.4.4) ──────────────────────────────
|
|
356
|
+
|
|
357
|
+
/**
|
|
358
|
+
* Per-track group-duration signaling.
|
|
359
|
+
*
|
|
360
|
+
* The catalog publishes either `groupDurationMs` (integer ms) or the exact
|
|
361
|
+
* `groupDurationTicks` value used by `groupIdForTicks` in align.ts. The tick
|
|
362
|
+
* form is required for cases where milliseconds cannot represent the cadence
|
|
363
|
+
* exactly:
|
|
364
|
+
*
|
|
365
|
+
* - Per-frame group durations at non-integer-ms cadences
|
|
366
|
+
* (1/30 s = 33.333… ms; 1/60 s = 16.666… ms).
|
|
367
|
+
* - Audio sample-rate timescales not divisible by 1000 Hz
|
|
368
|
+
* (e.g. 44 100 Hz × 333 ms = 14 685.3 ticks).
|
|
369
|
+
*
|
|
370
|
+
* If both fields are present, `groupDurationTicks` wins (matches the
|
|
371
|
+
* spec, mmt-00 §4.4.4).
|
|
372
|
+
*
|
|
373
|
+
* @see draft-ramadan-moq-mmt-00 §4.4.4
|
|
374
|
+
* @see ./align.ts groupIdForTicks
|
|
375
|
+
*/
|
|
376
|
+
export interface TrackTimingConfig {
|
|
377
|
+
/** Group duration in milliseconds (positive integer). Required unless an
|
|
378
|
+
* exact groupDurationTicks value is supplied. */
|
|
379
|
+
groupDurationMs?: number;
|
|
380
|
+
/** Integer-tick override (OPTIONAL, positive integer). Wins over the
|
|
381
|
+
* ms field when both are present. Required when
|
|
382
|
+
* `(groupDurationMs * timescale) % 1000 != 0`. */
|
|
383
|
+
groupDurationTicks?: number;
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
export function isTrackTimingConfig(obj: unknown): obj is TrackTimingConfig {
|
|
387
|
+
if (typeof obj !== "object" || obj === null) return false;
|
|
388
|
+
const o = obj as Record<string, unknown>;
|
|
389
|
+
const ms = o["groupDurationMs"];
|
|
390
|
+
const ticks = o["groupDurationTicks"];
|
|
391
|
+
if (ms === undefined && ticks === undefined) return false;
|
|
392
|
+
if (ms !== undefined && (typeof ms !== "number" || !Number.isInteger(ms) || ms <= 0)) return false;
|
|
393
|
+
if ("groupDurationTicks" in o) {
|
|
394
|
+
if (ticks === undefined) return ms !== undefined;
|
|
395
|
+
if (typeof ticks !== "number" || !Number.isInteger(ticks) || ticks <= 0) return false;
|
|
396
|
+
}
|
|
397
|
+
return true;
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
/** Resolve catalog group timing to an operational wall-clock duration.
|
|
401
|
+
*
|
|
402
|
+
* `groupDurationTicks` wins when both wire fields are present, matching
|
|
403
|
+
* mmt-00 §4.4.4. The result may be fractional because exact frame cadences
|
|
404
|
+
* such as 33_333 ticks at 1 MHz cannot be represented by the wire catalog's
|
|
405
|
+
* integer `groupDurationMs` field.
|
|
406
|
+
*/
|
|
407
|
+
export function resolveGroupDurationMs(
|
|
408
|
+
timing: TrackTimingConfig,
|
|
409
|
+
timescale: number,
|
|
410
|
+
): number {
|
|
411
|
+
if (!Number.isSafeInteger(timescale) || timescale <= 0) {
|
|
412
|
+
throw new Error(
|
|
413
|
+
`[carp-catalog] resolveGroupDurationMs: timescale must be a positive safe integer Hz, got ${timescale}`,
|
|
414
|
+
);
|
|
415
|
+
}
|
|
416
|
+
if (!isTrackTimingConfig(timing)) {
|
|
417
|
+
throw new Error(
|
|
418
|
+
`[carp-catalog] resolveGroupDurationMs: invalid TrackTimingConfig: ${JSON.stringify(timing)}`,
|
|
419
|
+
);
|
|
420
|
+
}
|
|
421
|
+
if (timing.groupDurationTicks === undefined) {
|
|
422
|
+
return timing.groupDurationMs!;
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
const durationMs = (timing.groupDurationTicks / timescale) * 1000;
|
|
426
|
+
if (!Number.isFinite(durationMs) || durationMs <= 0 || durationMs > Number.MAX_SAFE_INTEGER) {
|
|
427
|
+
throw new Error(
|
|
428
|
+
`[carp-catalog] resolveGroupDurationMs: groupDurationTicks=${timing.groupDurationTicks} ` +
|
|
429
|
+
`at timescale=${timescale} cannot be represented as a positive safe millisecond duration`,
|
|
430
|
+
);
|
|
431
|
+
}
|
|
432
|
+
return durationMs;
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
export type CmsfCatalog = {
|
|
436
|
+
version: number;
|
|
437
|
+
tracks: Record<string, unknown>[];
|
|
438
|
+
commonTrackFields?: Record<string, unknown>;
|
|
439
|
+
multicast?: Record<string, unknown>;
|
|
440
|
+
"multicast.auth"?: MulticastAuthConfig;
|
|
441
|
+
[key: string]: unknown;
|
|
442
|
+
};
|
|
443
|
+
|
|
444
|
+
export type CmsfCatalogErrorCode =
|
|
445
|
+
| "catalog_object"
|
|
446
|
+
| "catalog_version"
|
|
447
|
+
| "catalog_tracks"
|
|
448
|
+
| "track_object"
|
|
449
|
+
| "track_name"
|
|
450
|
+
| "track_packaging"
|
|
451
|
+
| "track_is_live"
|
|
452
|
+
| "track_codec"
|
|
453
|
+
| "track_mmtp_mode"
|
|
454
|
+
| "track_timescale"
|
|
455
|
+
| "track_group_duration_ms"
|
|
456
|
+
| "track_group_duration_ticks"
|
|
457
|
+
| "track_group_duration_exact"
|
|
458
|
+
| "track_keyframe_interval"
|
|
459
|
+
| "track_fec"
|
|
460
|
+
| "track_repair_track"
|
|
461
|
+
| "track_switching_set"
|
|
462
|
+
| "multicast_auth"
|
|
463
|
+
| "multicast_track"
|
|
464
|
+
| "multicast_packet_id";
|
|
465
|
+
|
|
466
|
+
export class CmsfCatalogValidationError extends Error {
|
|
467
|
+
constructor(
|
|
468
|
+
public readonly code: CmsfCatalogErrorCode,
|
|
469
|
+
public readonly path: string,
|
|
470
|
+
message: string,
|
|
471
|
+
) {
|
|
472
|
+
super(`[cmsf:${code}] ${path}: ${message}`);
|
|
473
|
+
this.name = "CmsfCatalogValidationError";
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
function catalogError(code: CmsfCatalogErrorCode, path: string, message: string): never {
|
|
478
|
+
throw new CmsfCatalogValidationError(code, path, message);
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
/** Validate the common CMSF shape and the MMTP required-field profile. */
|
|
482
|
+
export function validateCmsfMmtpCatalog(value: unknown): CmsfCatalog {
|
|
483
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
484
|
+
catalogError("catalog_object", "$", "catalog must be an object");
|
|
485
|
+
}
|
|
486
|
+
const catalog = value as Record<string, unknown>;
|
|
487
|
+
// IETF-draft catalogs (cast `ladder_catalog.rs`, moq-server
|
|
488
|
+
// `catalog-synthesizer.ts`, consumed by the hang player) carry a string
|
|
489
|
+
// version tag such as "draft-ietf-moq-catalogformat-01" instead of the CMSF
|
|
490
|
+
// `version: 1` integer. Nothing keys behavior off the catalog version, so
|
|
491
|
+
// normalize the tag to a number rather than rejecting the whole catalog —
|
|
492
|
+
// mirrors the relay (dual-stack-relay `bridge/catalog.rs`
|
|
493
|
+
// `deserialize_optional_u32_lenient`, #825) and moqtail-ts
|
|
494
|
+
// `normalizeCatalogVersion`.
|
|
495
|
+
const rawVersion = catalog["version"];
|
|
496
|
+
const normalizedVersion =
|
|
497
|
+
typeof rawVersion === "string"
|
|
498
|
+
? Number.isFinite(Number(rawVersion))
|
|
499
|
+
? Number(rawVersion)
|
|
500
|
+
: rawVersion.startsWith("draft-ietf-moq-catalogformat")
|
|
501
|
+
? 1
|
|
502
|
+
: NaN
|
|
503
|
+
: rawVersion;
|
|
504
|
+
if (!Number.isInteger(normalizedVersion) || (normalizedVersion as number) <= 0) {
|
|
505
|
+
catalogError("catalog_version", "$.version", "version must be a positive integer or a recognized format tag");
|
|
506
|
+
}
|
|
507
|
+
catalog["version"] = normalizedVersion;
|
|
508
|
+
if (!Array.isArray(catalog["tracks"])) {
|
|
509
|
+
catalogError("catalog_tracks", "$.tracks", "tracks must be an array");
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
const common = typeof catalog["commonTrackFields"] === "object" &&
|
|
513
|
+
catalog["commonTrackFields"] !== null && !Array.isArray(catalog["commonTrackFields"])
|
|
514
|
+
? catalog["commonTrackFields"] as Record<string, unknown>
|
|
515
|
+
: {};
|
|
516
|
+
const tracks = catalog["tracks"] as unknown[];
|
|
517
|
+
const trackRecords: Record<string, unknown>[] = [];
|
|
518
|
+
const effective = (track: Record<string, unknown>, field: string): unknown => track[field] ?? common[field];
|
|
519
|
+
const positiveInteger = (input: unknown): input is number => Number.isSafeInteger(input) && (input as number) > 0;
|
|
520
|
+
const positiveNumber = (input: unknown): input is number =>
|
|
521
|
+
typeof input === "number" && Number.isFinite(input) && input > 0;
|
|
522
|
+
|
|
523
|
+
for (let index = 0; index < tracks.length; index++) {
|
|
524
|
+
const raw = tracks[index];
|
|
525
|
+
const path = `$.tracks[${index}]`;
|
|
526
|
+
if (typeof raw !== "object" || raw === null || Array.isArray(raw)) {
|
|
527
|
+
catalogError("track_object", path, "track must be an object");
|
|
528
|
+
}
|
|
529
|
+
const track = raw as Record<string, unknown>;
|
|
530
|
+
const name = track["name"];
|
|
531
|
+
if (typeof name !== "string" || name.length === 0) {
|
|
532
|
+
catalogError("track_name", `${path}.name`, "name must be a non-empty string");
|
|
533
|
+
}
|
|
534
|
+
trackRecords.push(track);
|
|
535
|
+
|
|
536
|
+
const packaging = effective(track, "packaging");
|
|
537
|
+
if (typeof packaging !== "string" || packaging.length === 0) {
|
|
538
|
+
catalogError("track_packaging", `${path}.packaging`, "packaging must be a non-empty string");
|
|
539
|
+
}
|
|
540
|
+
const trackHasIsLive = Object.prototype.hasOwnProperty.call(track, "isLive");
|
|
541
|
+
const hasIsLive = trackHasIsLive || Object.prototype.hasOwnProperty.call(common, "isLive");
|
|
542
|
+
const isLive = trackHasIsLive ? track["isLive"] : common["isLive"];
|
|
543
|
+
if (hasIsLive && typeof isLive !== "boolean") {
|
|
544
|
+
catalogError("track_is_live", `${path}.isLive`, "isLive must be a boolean");
|
|
545
|
+
}
|
|
546
|
+
track["isLive"] = isLive ?? true;
|
|
547
|
+
const selectionValue = effective(track, "selectionParams");
|
|
548
|
+
const selection = typeof selectionValue === "object" && selectionValue !== null && !Array.isArray(selectionValue)
|
|
549
|
+
? selectionValue as Record<string, unknown>
|
|
550
|
+
: undefined;
|
|
551
|
+
const codec = selection?.["codec"] ?? effective(track, "codec");
|
|
552
|
+
if (packaging !== "timeline" && packaging !== "mediatimeline" && packaging !== "eventtimeline" && packaging !== "fec-repair" &&
|
|
553
|
+
(typeof codec !== "string" || codec.length === 0)) {
|
|
554
|
+
catalogError("track_codec", `${path}.codec`, `codec is required for packaging=${packaging}`);
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
const timescale = effective(track, "timescale");
|
|
558
|
+
const groupDurationMs = effective(track, "groupDurationMs");
|
|
559
|
+
const groupDurationTicks = effective(track, "groupDurationTicks");
|
|
560
|
+
const keyframeIntervalMs = effective(track, "keyframeIntervalMs");
|
|
561
|
+
const keyframeIntervalTicks = effective(track, "keyframeIntervalTicks");
|
|
562
|
+
if (groupDurationMs !== undefined && !positiveInteger(groupDurationMs)) {
|
|
563
|
+
catalogError("track_group_duration_ms", `${path}.groupDurationMs`, "must be a positive integer");
|
|
564
|
+
}
|
|
565
|
+
if (groupDurationTicks !== undefined && !positiveInteger(groupDurationTicks)) {
|
|
566
|
+
catalogError("track_group_duration_ticks", `${path}.groupDurationTicks`, "must be a positive integer");
|
|
567
|
+
}
|
|
568
|
+
if ((keyframeIntervalMs !== undefined && !positiveNumber(keyframeIntervalMs)) ||
|
|
569
|
+
(keyframeIntervalTicks !== undefined && !positiveInteger(keyframeIntervalTicks))) {
|
|
570
|
+
catalogError("track_keyframe_interval", path, "keyframe interval fields must be positive numbers");
|
|
571
|
+
}
|
|
572
|
+
if (packaging === "mmtp") {
|
|
573
|
+
const mmtpMode = effective(track, "mmtpMode");
|
|
574
|
+
if (mmtpMode !== "mpu" && mmtpMode !== "mfu") {
|
|
575
|
+
catalogError("track_mmtp_mode", `${path}.mmtpMode`, "must be mpu or mfu");
|
|
576
|
+
}
|
|
577
|
+
if (!positiveInteger(timescale)) {
|
|
578
|
+
catalogError("track_timescale", `${path}.timescale`, "must be a positive integer");
|
|
579
|
+
}
|
|
580
|
+
if (groupDurationMs === undefined && groupDurationTicks === undefined) {
|
|
581
|
+
catalogError(
|
|
582
|
+
"track_group_duration_ms",
|
|
583
|
+
path,
|
|
584
|
+
"groupDurationMs or groupDurationTicks must be a positive integer",
|
|
585
|
+
);
|
|
586
|
+
}
|
|
587
|
+
if (groupDurationTicks === undefined &&
|
|
588
|
+
(BigInt(groupDurationMs as number) * BigInt(timescale)) % 1000n !== 0n) {
|
|
589
|
+
catalogError("track_group_duration_exact", `${path}.groupDurationMs`, "does not resolve to an exact integer tick count");
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
if (track["fec"] !== undefined) {
|
|
594
|
+
const fec = track["fec"];
|
|
595
|
+
if (typeof fec !== "object" || fec === null || Array.isArray(fec)) {
|
|
596
|
+
catalogError("track_fec", `${path}.fec`, "FEC must be an object");
|
|
597
|
+
}
|
|
598
|
+
const config = fec as Record<string, unknown>;
|
|
599
|
+
const algorithm = config["algorithm"];
|
|
600
|
+
if (algorithm !== "none" && algorithm !== "raptorq" && algorithm !== "reed-solomon") {
|
|
601
|
+
catalogError("track_fec", `${path}.fec.algorithm`, "algorithm is not supported");
|
|
602
|
+
}
|
|
603
|
+
if ("interleaveDepth" in config || "interleaveMs" in config) {
|
|
604
|
+
catalogError("track_fec", `${path}.fec`, "interleaveDepthMs is the only supported interleave field");
|
|
605
|
+
}
|
|
606
|
+
if (config["interleaveDepthMs"] !== undefined && !positiveInteger(config["interleaveDepthMs"])) {
|
|
607
|
+
catalogError("track_fec", `${path}.fec.interleaveDepthMs`, "must be a positive integer");
|
|
608
|
+
}
|
|
609
|
+
if (algorithm !== "none") {
|
|
610
|
+
for (const field of ["sourceSymbols", "repairSymbols", "symbolSize"] as const) {
|
|
611
|
+
if (!positiveInteger(config[field])) {
|
|
612
|
+
catalogError("track_fec", `${path}.fec.${field}`, "must be a positive integer");
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
if (typeof config["repairTrack"] !== "string" || config["repairTrack"].length === 0) {
|
|
616
|
+
catalogError("track_fec", `${path}.fec.repairTrack`, "must be a non-empty string");
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
for (let index = 0; index < trackRecords.length; index++) {
|
|
623
|
+
const track = trackRecords[index]!;
|
|
624
|
+
const fec = track["fec"] as Record<string, unknown> | undefined;
|
|
625
|
+
if (!fec || fec["algorithm"] === "none") continue;
|
|
626
|
+
const repairTrack = fec["repairTrack"] as string;
|
|
627
|
+
const target = trackRecords.find((candidate) => candidate["name"] === repairTrack);
|
|
628
|
+
if (!target || effective(target, "packaging") !== "fec-repair") {
|
|
629
|
+
catalogError("track_repair_track", `$.tracks[${index}].fec.repairTrack`, `unknown FEC repair track ${JSON.stringify(repairTrack)}`);
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
const switchingSets = new Map<number, { name: string; ticks: bigint; timescale: bigint }>();
|
|
634
|
+
for (let index = 0; index < trackRecords.length; index++) {
|
|
635
|
+
const track = trackRecords[index]!;
|
|
636
|
+
if (effective(track, "packaging") !== "mmtp") continue;
|
|
637
|
+
const altGroup = effective(track, "altGroup");
|
|
638
|
+
if (!Number.isSafeInteger(altGroup)) continue;
|
|
639
|
+
const timescale = BigInt(effective(track, "timescale") as number);
|
|
640
|
+
const durationTicks = effective(track, "groupDurationTicks");
|
|
641
|
+
const ticks = durationTicks !== undefined
|
|
642
|
+
? BigInt(durationTicks as number)
|
|
643
|
+
: BigInt(effective(track, "groupDurationMs") as number) * timescale / 1000n;
|
|
644
|
+
const anchor = switchingSets.get(altGroup as number);
|
|
645
|
+
if (anchor && ticks * anchor.timescale !== anchor.ticks * timescale) {
|
|
646
|
+
catalogError("track_switching_set", `$.tracks[${index}]`, `group duration does not match ${JSON.stringify(anchor.name)} in altGroup ${altGroup}`);
|
|
647
|
+
}
|
|
648
|
+
if (!anchor) switchingSets.set(altGroup as number, { name: track["name"] as string, ticks, timescale });
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
const multicast = catalog["multicast"];
|
|
652
|
+
if (typeof multicast === "object" && multicast !== null && !Array.isArray(multicast)) {
|
|
653
|
+
const endpoints = (multicast as Record<string, unknown>)["endpoints"];
|
|
654
|
+
if (Array.isArray(endpoints)) {
|
|
655
|
+
for (let endpointIndex = 0; endpointIndex < endpoints.length; endpointIndex++) {
|
|
656
|
+
const endpoint = endpoints[endpointIndex];
|
|
657
|
+
if (typeof endpoint !== "object" || endpoint === null || Array.isArray(endpoint)) continue;
|
|
658
|
+
const refs = (endpoint as Record<string, unknown>)["tracks"];
|
|
659
|
+
if (!Array.isArray(refs)) continue;
|
|
660
|
+
const packetIds = new Map<number, string>();
|
|
661
|
+
for (let refIndex = 0; refIndex < refs.length; refIndex++) {
|
|
662
|
+
const ref = refs[refIndex];
|
|
663
|
+
if (typeof ref !== "object" || ref === null || Array.isArray(ref)) continue;
|
|
664
|
+
const trackRef = ref as Record<string, unknown>;
|
|
665
|
+
const refPath = `$.multicast.endpoints[${endpointIndex}].tracks[${refIndex}]`;
|
|
666
|
+
const name = trackRef["name"];
|
|
667
|
+
if (typeof name !== "string" || !trackRecords.some((track) => track["name"] === name)) {
|
|
668
|
+
catalogError("multicast_track", `${refPath}.name`, `unknown catalog track ${JSON.stringify(name)}`);
|
|
669
|
+
}
|
|
670
|
+
const packetId = trackRef["packetId"];
|
|
671
|
+
if (!Number.isSafeInteger(packetId) || (packetId as number) < 0 || (packetId as number) > 65535) {
|
|
672
|
+
catalogError("multicast_packet_id", `${refPath}.packetId`, "packetId must be an integer from 0 to 65535");
|
|
673
|
+
}
|
|
674
|
+
const previous = packetIds.get(packetId as number);
|
|
675
|
+
if (previous !== undefined) {
|
|
676
|
+
catalogError("multicast_packet_id", `${refPath}.packetId`, `packetId is already assigned to ${JSON.stringify(previous)}`);
|
|
677
|
+
}
|
|
678
|
+
packetIds.set(packetId as number, name as string);
|
|
679
|
+
}
|
|
680
|
+
}
|
|
681
|
+
}
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
try {
|
|
685
|
+
resolveMulticastAuthCatalog(catalog);
|
|
686
|
+
} catch (error) {
|
|
687
|
+
catalogError(
|
|
688
|
+
"multicast_auth",
|
|
689
|
+
"$.multicast.auth",
|
|
690
|
+
error instanceof Error ? error.message : "invalid multicast.auth v2 block",
|
|
691
|
+
);
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
return catalog as CmsfCatalog;
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
/**
|
|
698
|
+
* Resolve a track's group duration to integer ticks using the supplied
|
|
699
|
+
* timescale (Hz). Implements mmt-00 §4.4.4:
|
|
700
|
+
*
|
|
701
|
+
* - If `groupDurationTicks` is present, return it (override wins).
|
|
702
|
+
* - Else compute `groupDurationMs * timescale / 1000`. The result MUST
|
|
703
|
+
* be an exact integer; throws if `(ms * timescale) % 1000 != 0`.
|
|
704
|
+
*
|
|
705
|
+
* Throws on invalid timing config or non-positive timescale (CLAUDE.md:
|
|
706
|
+
* error on bad init).
|
|
707
|
+
*/
|
|
708
|
+
export function resolveGroupDurationTicks(
|
|
709
|
+
timing: TrackTimingConfig,
|
|
710
|
+
timescale: number,
|
|
711
|
+
): number {
|
|
712
|
+
if (!Number.isFinite(timescale) || !Number.isInteger(timescale) || timescale <= 0) {
|
|
713
|
+
throw new Error(
|
|
714
|
+
`[carp-catalog] resolveGroupDurationTicks: timescale must be a positive integer Hz, got ${timescale}`,
|
|
715
|
+
);
|
|
716
|
+
}
|
|
717
|
+
if (!isTrackTimingConfig(timing)) {
|
|
718
|
+
throw new Error(
|
|
719
|
+
`[carp-catalog] resolveGroupDurationTicks: invalid TrackTimingConfig: ${JSON.stringify(timing)}`,
|
|
720
|
+
);
|
|
721
|
+
}
|
|
722
|
+
if (timing.groupDurationTicks !== undefined) {
|
|
723
|
+
return timing.groupDurationTicks;
|
|
724
|
+
}
|
|
725
|
+
if (timing.groupDurationMs === undefined) {
|
|
726
|
+
throw new Error(
|
|
727
|
+
"[carp-catalog] resolveGroupDurationTicks: groupDurationMs is required when groupDurationTicks is absent",
|
|
728
|
+
);
|
|
729
|
+
}
|
|
730
|
+
const product = timing.groupDurationMs * timescale;
|
|
731
|
+
if (product % 1000 !== 0) {
|
|
732
|
+
throw new Error(
|
|
733
|
+
`[carp-catalog] resolveGroupDurationTicks: groupDurationMs=${timing.groupDurationMs} at timescale=${timescale} ` +
|
|
734
|
+
`produces non-integer ticks (${product / 1000}); publisher must use groupDurationTicks override (mmt-00 §4.4.4)`,
|
|
735
|
+
);
|
|
736
|
+
}
|
|
737
|
+
return product / 1000;
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
/**
|
|
741
|
+
* Validate that every track in a switching set agrees on the same
|
|
742
|
+
* wall-clock group duration (mmt-00 §4.4.4). Per-track timescales may
|
|
743
|
+
* differ, so raw tick counts cannot be compared directly.
|
|
744
|
+
*
|
|
745
|
+
* Returns `{ ok: true }` when all tracks agree (or the set is empty);
|
|
746
|
+
* `{ ok: false, mismatches }` listing each track whose effective ticks
|
|
747
|
+
* differ from the first track's value. Subscribers per spec SHOULD
|
|
748
|
+
* reject the catalog on mismatch.
|
|
749
|
+
*
|
|
750
|
+
* Throws if any track's timing config is invalid (per
|
|
751
|
+
* `resolveGroupDurationTicks`).
|
|
752
|
+
*/
|
|
753
|
+
export function validateSwitchingSetTiming(
|
|
754
|
+
tracks: Array<{ name: string; timing: TrackTimingConfig; timescale: number }>,
|
|
755
|
+
):
|
|
756
|
+
| { ok: true }
|
|
757
|
+
| { ok: false; expectedTicks: number; mismatches: Array<{ name: string; ticks: number }> } {
|
|
758
|
+
if (tracks.length === 0) return { ok: true };
|
|
759
|
+
const ticksByTrack = tracks.map((t) => ({
|
|
760
|
+
name: t.name,
|
|
761
|
+
ticks: resolveGroupDurationTicks(t.timing, t.timescale),
|
|
762
|
+
timescale: t.timescale,
|
|
763
|
+
}));
|
|
764
|
+
const expectedTicks = ticksByTrack[0].ticks;
|
|
765
|
+
const expectedTimescale = ticksByTrack[0].timescale;
|
|
766
|
+
const mismatches = ticksByTrack
|
|
767
|
+
.slice(1)
|
|
768
|
+
.filter(
|
|
769
|
+
(t) =>
|
|
770
|
+
BigInt(t.ticks) * BigInt(expectedTimescale) !==
|
|
771
|
+
BigInt(expectedTicks) * BigInt(t.timescale),
|
|
772
|
+
)
|
|
773
|
+
.map(({ name, ticks }) => ({ name, ticks }));
|
|
774
|
+
if (mismatches.length === 0) return { ok: true };
|
|
775
|
+
return { ok: false, expectedTicks, mismatches };
|
|
776
|
+
}
|