@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,1255 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* FEC-aware CMAF sample assembler for MSE multicast.
|
|
3
|
+
*
|
|
4
|
+
* Emits source frames on arrival and FEC-recovered frames in media order.
|
|
5
|
+
* Nothing is synthesised for a lost video block: the sink holds the last good
|
|
6
|
+
* frame (HoldLastTimeline). A lost audio span is concealed only when libmmt's
|
|
7
|
+
* release gate decided it (`gapBefore` with reason `audio-gap`), by repeating
|
|
8
|
+
* the last good access unit into each missing slot (AudioGapConcealer).
|
|
9
|
+
*
|
|
10
|
+
* One assembler instance per track (video or audio). MulticastManager owns
|
|
11
|
+
* these instances and calls them directly with SBN/ESI from the FEC pipeline.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import {
|
|
15
|
+
encodeDataSegment,
|
|
16
|
+
buildVideoInitSegment,
|
|
17
|
+
buildHevcVideoInitSegment,
|
|
18
|
+
buildAudioInitSegment,
|
|
19
|
+
normalizeAacSampleForCmaf,
|
|
20
|
+
} from './mmtp-to-cmaf.js'
|
|
21
|
+
import { AudioGapConcealer } from './conceal-timeline.js'
|
|
22
|
+
import type { FrameGapDecision } from './block-policy.js'
|
|
23
|
+
import {
|
|
24
|
+
classifyH264Keyframe,
|
|
25
|
+
detectH264Keyframe,
|
|
26
|
+
hevcNalUnitType,
|
|
27
|
+
isAnnexB,
|
|
28
|
+
isCompleteAvccSample,
|
|
29
|
+
prepareHevcCmafSample,
|
|
30
|
+
splitAnnexBNalUnits,
|
|
31
|
+
splitAvccNalUnits,
|
|
32
|
+
splitH264SampleNalUnits,
|
|
33
|
+
videoCodecFamily,
|
|
34
|
+
videoSampleEntryFromCodecString,
|
|
35
|
+
type VideoCodecFamily,
|
|
36
|
+
type VideoSampleEntry,
|
|
37
|
+
} from './codec-utils.js'
|
|
38
|
+
import {
|
|
39
|
+
scaleAuthoredTicks,
|
|
40
|
+
scaleAuthoredTiming,
|
|
41
|
+
type AuthoredTiming,
|
|
42
|
+
} from './authored-timing.js'
|
|
43
|
+
import type { MmtpDeliveryProvenance } from './mfu-reassembler.js'
|
|
44
|
+
|
|
45
|
+
const INT32_MIN = -0x80000000
|
|
46
|
+
const INT32_MAX = 0x7fffffff
|
|
47
|
+
|
|
48
|
+
interface BlockFrame {
|
|
49
|
+
data: Uint8Array
|
|
50
|
+
timestamp: number
|
|
51
|
+
keyframe: boolean
|
|
52
|
+
/** True only for locally generated concealment frames. */
|
|
53
|
+
synthetic?: boolean
|
|
54
|
+
/** Raw MMTP short-NTP header timestamp; never used for CMAF timing. */
|
|
55
|
+
mmtpTimestamp?: bigint
|
|
56
|
+
/** Exact ISO-BMFF timing retained from the parser for FEC/source ordering. */
|
|
57
|
+
authoredTiming?: AuthoredTiming
|
|
58
|
+
/** MMTP packet_sequence_number identifying the delivered access unit. */
|
|
59
|
+
packetSequenceNumber?: number
|
|
60
|
+
/** Every authoritative MMTP source-packet sequence that formed this sample. */
|
|
61
|
+
packetSequenceNumbers?: number[]
|
|
62
|
+
/** Subset of packetSequenceNumbers reconstructed by FEC. */
|
|
63
|
+
repairedPacketSequenceNumbers?: number[]
|
|
64
|
+
/** Native MMTP MPU group coordinate retained across source/FEC ordering. */
|
|
65
|
+
mpuSequenceNumber?: number
|
|
66
|
+
/** Timed-MFU sample identity; absent when the MFU header is untimed. */
|
|
67
|
+
movieFragmentSequenceNumber?: number
|
|
68
|
+
sampleNumber?: number
|
|
69
|
+
catalogTrackName?: string
|
|
70
|
+
/**
|
|
71
|
+
* Whole-frame delivery verdict folded across every contributing fragment.
|
|
72
|
+
* Absent means no caller declared one; it is never an implied 'multicast'.
|
|
73
|
+
*/
|
|
74
|
+
deliveryProvenance?: MmtpDeliveryProvenance
|
|
75
|
+
/** libmmt's stall/conceal decision for the gap before this frame. */
|
|
76
|
+
gapBefore?: FrameGapDecision
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export interface CmafBlockSample {
|
|
80
|
+
data: Uint8Array
|
|
81
|
+
timestamp: number
|
|
82
|
+
duration: number
|
|
83
|
+
keyframe: boolean
|
|
84
|
+
/**
|
|
85
|
+
* True only for locally generated concealment samples. These samples carry
|
|
86
|
+
* codec bytes and a media timestamp, but no authored MMTP identity; object
|
|
87
|
+
* consumers must not validate them as source MFUs.
|
|
88
|
+
*/
|
|
89
|
+
synthetic?: boolean
|
|
90
|
+
/**
|
|
91
|
+
* Raw MMTP short-NTP header timestamp retained for timing telemetry.
|
|
92
|
+
* This is intentionally separate from the authored media timestamp above.
|
|
93
|
+
*/
|
|
94
|
+
mmtpTimestamp?: bigint
|
|
95
|
+
/** Signed PTS-DTS offset in the configured CMAF timescale. */
|
|
96
|
+
compositionTimeOffset?: number
|
|
97
|
+
/** Exact ISO-BMFF timing in the media track's native timescale. */
|
|
98
|
+
authoredTiming?: AuthoredTiming
|
|
99
|
+
/** MMTP packet_sequence_number identifying the delivered access unit. */
|
|
100
|
+
packetSequenceNumber?: number
|
|
101
|
+
/** Every authoritative MMTP source-packet sequence that formed this sample. */
|
|
102
|
+
packetSequenceNumbers?: number[]
|
|
103
|
+
/** Subset of packetSequenceNumbers reconstructed by FEC. */
|
|
104
|
+
repairedPacketSequenceNumbers?: number[]
|
|
105
|
+
/** Native MMTP MPU group coordinate retained for exact replay selection. */
|
|
106
|
+
mpuSequenceNumber?: number
|
|
107
|
+
/**
|
|
108
|
+
* Timed-MFU sample identity `(movie_fragment_sequence_number,
|
|
109
|
+
* sample_number)` from the MFU data-unit header, carried alongside
|
|
110
|
+
* `mpuSequenceNumber` so a consumer can address the sample by its native
|
|
111
|
+
* MMTP coordinates. Absent when the MFU header is untimed.
|
|
112
|
+
*/
|
|
113
|
+
movieFragmentSequenceNumber?: number
|
|
114
|
+
sampleNumber?: number
|
|
115
|
+
/** Immutable catalog identity supplied by the owning ingress subscription. */
|
|
116
|
+
catalogTrackName?: string
|
|
117
|
+
/**
|
|
118
|
+
* Whole-frame delivery verdict, carried so MSE `onSample` consumers can see
|
|
119
|
+
* the same evidence the non-MSE frame path gets. Without it the MSE branch
|
|
120
|
+
* silently loses a computed 'mixed' or 'multicast' finding.
|
|
121
|
+
*/
|
|
122
|
+
deliveryProvenance?: MmtpDeliveryProvenance
|
|
123
|
+
/**
|
|
124
|
+
* libmmt's stall/conceal decision for the gap before this sample
|
|
125
|
+
* (`RoutedFrameMeta.gapBefore`). MSE sinks feed it to HoldLastTimeline.
|
|
126
|
+
*/
|
|
127
|
+
gapBefore?: FrameGapDecision
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
export type CmafSampleOrderPolicy =
|
|
131
|
+
| { mode: 'arrival' }
|
|
132
|
+
| {
|
|
133
|
+
/** Release samples according to media progression, without a wall clock. */
|
|
134
|
+
mode: 'media-window'
|
|
135
|
+
/** Delivery window in the same units as CmafBlockSample.timestamp. */
|
|
136
|
+
deliveryWindow: number
|
|
137
|
+
/** Optional modulo period for wrapped input timestamps, in timestamp units. */
|
|
138
|
+
timestampWrapPeriod?: number
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
export interface CmafSampleOrderStats {
|
|
142
|
+
staleSamples: number
|
|
143
|
+
duplicateSamples: number
|
|
144
|
+
preRapDeadlineDrops: number
|
|
145
|
+
noRapFlushDrops: number
|
|
146
|
+
forcedReleases: number
|
|
147
|
+
/**
|
|
148
|
+
* Samples refused because they carried `authoredTiming` while this track's
|
|
149
|
+
* `admitAuthoredTiming` was false. Non-zero means the contract rollback
|
|
150
|
+
* switch is engaged AND the producer is still sending authored batches —
|
|
151
|
+
* a rollback is meant to be paired with that producer reverting to raw.
|
|
152
|
+
*/
|
|
153
|
+
unadmittedAuthoredSamples: number
|
|
154
|
+
/**
|
|
155
|
+
* Video samples refused because their bitstream could not be read: HEVC
|
|
156
|
+
* whose framing is neither a complete 4-byte length-prefixed sample nor
|
|
157
|
+
* Annex B, or that holds no NAL unit to write. Writing them would put bytes
|
|
158
|
+
* the hvcC's lengthSizeMinusOne = 3 does not describe into the mdat.
|
|
159
|
+
*/
|
|
160
|
+
unreadableVideoSamples: number
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Cumulative counters of what the assembler emitted and which block events
|
|
165
|
+
* it saw. `emits` counts emit calls (one per direct frame, recovered block,
|
|
166
|
+
* recovered drain or block flush); `samples` and `keyframes` count the
|
|
167
|
+
* samples those calls carried.
|
|
168
|
+
*/
|
|
169
|
+
export interface CmafEmitStats {
|
|
170
|
+
emits: number
|
|
171
|
+
samples: number
|
|
172
|
+
keyframes: number
|
|
173
|
+
addedFrames: number
|
|
174
|
+
recoveredBlocks: number
|
|
175
|
+
completedBlocks: number
|
|
176
|
+
failedBlocks: number
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Cumulative audio gap concealment counters since construction. Every audio
|
|
181
|
+
* gap decision the assembler sees lands in exactly one of the gap counters.
|
|
182
|
+
*/
|
|
183
|
+
export interface CmafAudioConcealStats {
|
|
184
|
+
/** Last-good AUs written into the slots of `audio-gap` decisions. */
|
|
185
|
+
repeatedUnits: number
|
|
186
|
+
/**
|
|
187
|
+
* Gap decisions re-anchored at the next AU by design: an
|
|
188
|
+
* `audio-discontinuity`, or no good AU emitted yet.
|
|
189
|
+
*/
|
|
190
|
+
reanchoredGaps: number
|
|
191
|
+
/**
|
|
192
|
+
* `audio-gap` decisions that could not be sized and were re-anchored
|
|
193
|
+
* instead: the AU after the gap, or the last good AU, carried no authored
|
|
194
|
+
* ISO-BMFF timing, or the two were authored in different media timescales.
|
|
195
|
+
* libmmt only decides an `audio-gap` from authored ticks (`decideGap`), so
|
|
196
|
+
* non-zero means a caller handed the assembler a decision without the
|
|
197
|
+
* timing it was computed from.
|
|
198
|
+
*/
|
|
199
|
+
untimedGaps: number
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
export interface BlockAssemblerConfig {
|
|
203
|
+
/** Number of source symbols per FEC block */
|
|
204
|
+
k: number
|
|
205
|
+
/** Timescale for CMAF segments (default 1_000_000 = microseconds) */
|
|
206
|
+
timescale?: number
|
|
207
|
+
/** ISO track ID (default 1) */
|
|
208
|
+
trackId?: number
|
|
209
|
+
/** Track type — determines init segment format and audio concealment */
|
|
210
|
+
trackType: 'video' | 'audio'
|
|
211
|
+
/** Video/audio codec string (e.g. 'avc1.64002a', 'mp4a.40.2'). */
|
|
212
|
+
codec?: string
|
|
213
|
+
// Video config (required when trackType='video')
|
|
214
|
+
/** Raw avcC payload from catalog description — for the init segment */
|
|
215
|
+
avcC?: Uint8Array
|
|
216
|
+
/** Raw hvcC payload for an HEVC init segment (e.g. built from in-band
|
|
217
|
+
* VPS/SPS/PPS by `generateHvcCFromParameterSets`). Requires `codec` to name
|
|
218
|
+
* the `hev1` or `hvc1` sample entry. */
|
|
219
|
+
hvcC?: Uint8Array
|
|
220
|
+
/** Video width in pixels */
|
|
221
|
+
width?: number
|
|
222
|
+
/** Video height in pixels */
|
|
223
|
+
height?: number
|
|
224
|
+
// Audio config (required when trackType='audio')
|
|
225
|
+
/** Audio codec string (e.g. 'mp4a.40.2', 'opus') */
|
|
226
|
+
audioCodec?: string
|
|
227
|
+
/** Audio sample rate (e.g. 48000) */
|
|
228
|
+
sampleRate?: number
|
|
229
|
+
/** Audio channel count (e.g. 1 or 2) */
|
|
230
|
+
channels?: number
|
|
231
|
+
/** Raw AudioSpecificConfig from catalog */
|
|
232
|
+
asc?: Uint8Array
|
|
233
|
+
// Shared
|
|
234
|
+
/** Frames per second — fallback for duration estimation */
|
|
235
|
+
fps?: number
|
|
236
|
+
/** Explicit sample ordering policy. Datagram/FEC callers use media-window. */
|
|
237
|
+
sampleOrder?: CmafSampleOrderPolicy
|
|
238
|
+
/**
|
|
239
|
+
* Bounded sample reorder depth for callback mode.
|
|
240
|
+
*
|
|
241
|
+
* Default 0 preserves immediate emission. When set on video tracks with
|
|
242
|
+
* onSample enabled, samples are sorted by timestamp and the oldest sample is
|
|
243
|
+
* emitted only after this many newer samples are available.
|
|
244
|
+
* @deprecated Use sampleOrder with an explicit policy.
|
|
245
|
+
*/
|
|
246
|
+
sampleReorderWindow?: number
|
|
247
|
+
/**
|
|
248
|
+
* Authored-timing admission for this track (default `true` — admitted).
|
|
249
|
+
*
|
|
250
|
+
* This is the rollback switch named by the authored-audio-CTS contract's
|
|
251
|
+
* removal gate (BLO-30397). Assemblers are keyed on
|
|
252
|
+
* `(packetId, catalogTrackName)` (`mmtp-router.ts` `#mseAssemblerKey`), so
|
|
253
|
+
* this switch is per **track**. A producer feeding several tracks must be
|
|
254
|
+
* de-admitted on each of them: nothing applies it across a track set
|
|
255
|
+
* (BLO-34821).
|
|
256
|
+
*
|
|
257
|
+
* Set `false` and a sample carrying `authoredTiming` is **rejected**, not
|
|
258
|
+
* zeroed: the contract forbids rolling back "by zeroing already-authored
|
|
259
|
+
* offsets", because a zeroed offset is byte-indistinguishable from a feed
|
|
260
|
+
* that authored nothing (BLO-30401 measured both as 0 A/V skew). Raw samples
|
|
261
|
+
* — those with no `authoredTiming` at all — are unaffected and keep emitting
|
|
262
|
+
* a v0 trun with no composition-offset field.
|
|
263
|
+
*/
|
|
264
|
+
admitAuthoredTiming?: boolean
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
interface OrderedSample extends CmafBlockSample {
|
|
268
|
+
advancesHorizon: boolean
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
export class CmafBlockAssembler {
|
|
272
|
+
#config: Required<
|
|
273
|
+
Pick<
|
|
274
|
+
BlockAssemblerConfig,
|
|
275
|
+
'k' | 'timescale' | 'trackId' | 'trackType' | 'admitAuthoredTiming'
|
|
276
|
+
>
|
|
277
|
+
>
|
|
278
|
+
#pendingRecovered = new Map<number, BlockFrame>()
|
|
279
|
+
#sequence = 1
|
|
280
|
+
#baseTs = -1
|
|
281
|
+
/**
|
|
282
|
+
* Last decoder-proven audio AU, repeated into slots a gap decision names.
|
|
283
|
+
* It is noted and planned on the AU's authored clock (`AuthoredTiming`
|
|
284
|
+
* ticks), never on this assembler's CMAF timescale: on the default
|
|
285
|
+
* 1_000_000 an AAC AU is 1024e6 / sampleRate µs, which is not an integer.
|
|
286
|
+
*/
|
|
287
|
+
#audioConcealer = new AudioGapConcealer<Uint8Array>()
|
|
288
|
+
/**
|
|
289
|
+
* Media timescale of the AU the concealer holds; `'untimed'` when the last
|
|
290
|
+
* emitted audio AU carried no authored timing, `null` before any AU.
|
|
291
|
+
*/
|
|
292
|
+
#audioConcealClock: number | 'untimed' | null = null
|
|
293
|
+
#audioConcealStats: CmafAudioConcealStats = {
|
|
294
|
+
repeatedUnits: 0,
|
|
295
|
+
reanchoredGaps: 0,
|
|
296
|
+
untimedGaps: 0,
|
|
297
|
+
}
|
|
298
|
+
#frameDuration: number
|
|
299
|
+
#lastEmittedTs = -1
|
|
300
|
+
#lastSourceTs = -1
|
|
301
|
+
#sampleReorderWindow = 0
|
|
302
|
+
#sampleOrder: CmafSampleOrderPolicy = { mode: 'arrival' }
|
|
303
|
+
#sampleOrderCapacity = 0
|
|
304
|
+
#sampleReorderQueue: OrderedSample[] = []
|
|
305
|
+
#recoveredHorizonTs = -1
|
|
306
|
+
#sampleOrderStats: CmafSampleOrderStats = {
|
|
307
|
+
staleSamples: 0,
|
|
308
|
+
duplicateSamples: 0,
|
|
309
|
+
preRapDeadlineDrops: 0,
|
|
310
|
+
noRapFlushDrops: 0,
|
|
311
|
+
forcedReleases: 0,
|
|
312
|
+
unadmittedAuthoredSamples: 0,
|
|
313
|
+
unreadableVideoSamples: 0,
|
|
314
|
+
}
|
|
315
|
+
/** Codec family whose bitstream keyframe rule overrides the metadata flag
|
|
316
|
+
* (AVC IDR, HEVC IRAP), or null when the bitstream is not validated. */
|
|
317
|
+
#videoKeyframeFamily: VideoCodecFamily | null = null
|
|
318
|
+
/** HEVC sample entry chosen from the catalog codec string. */
|
|
319
|
+
#hevcSampleEntry: 'hev1' | 'hvc1' | null = null
|
|
320
|
+
#keyframeRejectDumpCount = 0
|
|
321
|
+
|
|
322
|
+
/** Callback: emits a complete moof+mdat CMAF segment */
|
|
323
|
+
onSegment: ((segment: Uint8Array) => void) | null = null
|
|
324
|
+
/** Callback: emits a gated/deduped raw sample for callers that already own the MSE timeline. */
|
|
325
|
+
onSample: ((sample: CmafBlockSample) => void) | null = null
|
|
326
|
+
/** Callback: emits an init segment (ftyp+moov) */
|
|
327
|
+
onInitSegment: ((init: Uint8Array) => void) | null = null
|
|
328
|
+
|
|
329
|
+
// Video keyframe gate (BLO-3035 layer 3): Chrome's CHUNK_DEMUXER rejects a
|
|
330
|
+
// moof+mdat whose first sample is non-sync with no prior reference frames
|
|
331
|
+
// ("CHUNK_DEMUXER_ERROR_APPEND_FAILED: Failed to prepare video sample for
|
|
332
|
+
// decode") and detaches the SourceBuffer for the rest of the session.
|
|
333
|
+
// Mid-stream join almost guarantees the first FEC blocks are mid-GOP — all
|
|
334
|
+
// delta frames. Gate emission until the first block containing an IDR;
|
|
335
|
+
// within that block, splice samples to start at the IDR index. Audio passes
|
|
336
|
+
// unconditionally — valid for the codecs supported here (AAC, Opus) where
|
|
337
|
+
// every frame is independently decodable. SBR-only AAC, USAC, or HE-AAC v2
|
|
338
|
+
// with PCE re-entry would need a similar gate.
|
|
339
|
+
#armedVideo = false
|
|
340
|
+
|
|
341
|
+
/** Truncate `frames` to start at the first keyframe for video tracks before
|
|
342
|
+
* the first emission. Returns the (possibly spliced) frames, or null to
|
|
343
|
+
* drop the block entirely. Audio + already-armed video: identity. */
|
|
344
|
+
#applyVideoKeyframeGate<T extends { data: Uint8Array; keyframe: boolean }>(frames: T[]): T[] | null {
|
|
345
|
+
const keyedFrames = this.#normalizeVideoKeyframes(frames)
|
|
346
|
+
if (this.#config.trackType !== 'video' || this.#armedVideo) return keyedFrames
|
|
347
|
+
const firstKfIdx = keyedFrames.findIndex(f => f.keyframe)
|
|
348
|
+
if (firstKfIdx < 0) return null
|
|
349
|
+
return firstKfIdx === 0 ? keyedFrames : keyedFrames.slice(firstKfIdx)
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
#normalizeVideoKeyframes<T extends { data: Uint8Array; keyframe: boolean }>(frames: T[]): T[] {
|
|
353
|
+
if (this.#config.trackType !== 'video' || this.#videoKeyframeFamily === null) return frames
|
|
354
|
+
const out: T[] = []
|
|
355
|
+
for (const frame of frames) {
|
|
356
|
+
const effective = this.#withEffectiveVideoKeyframe(frame)
|
|
357
|
+
if (effective !== null) out.push(effective)
|
|
358
|
+
}
|
|
359
|
+
return out
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
/** The frame with its bitstream keyframe (and, for HEVC, mdat framing);
|
|
363
|
+
* null when the frame is refused and must not be emitted. */
|
|
364
|
+
#withEffectiveVideoKeyframe<T extends { data: Uint8Array; keyframe: boolean }>(frame: T): T | null {
|
|
365
|
+
if (this.#config.trackType !== 'video' || this.#videoKeyframeFamily === null) return frame
|
|
366
|
+
if (this.#videoKeyframeFamily === 'hevc') {
|
|
367
|
+
// HEVC keyframe = IRAP (H.265 Table 7-1). The mdat carries 4-byte
|
|
368
|
+
// length-prefixed NAL units to match the hvcC's lengthSizeMinusOne = 3;
|
|
369
|
+
// normalization is idempotent, so a frame seen twice is unchanged. A
|
|
370
|
+
// frame that cannot be read is refused, not written as-is and not
|
|
371
|
+
// passed off as a delta frame.
|
|
372
|
+
const prepared = prepareHevcCmafSample(frame.data, this.#hevcSampleEntry!)
|
|
373
|
+
if (prepared === null) {
|
|
374
|
+
this.#sampleOrderStats.unreadableVideoSamples++
|
|
375
|
+
return null
|
|
376
|
+
}
|
|
377
|
+
const { keyframe, data } = prepared
|
|
378
|
+
if (!keyframe) this.#dumpRejectedVideoKeyframe(frame.data, frame.keyframe)
|
|
379
|
+
return keyframe === frame.keyframe && data === frame.data ? frame : { ...frame, keyframe, data }
|
|
380
|
+
}
|
|
381
|
+
const keyframe = detectH264Keyframe(frame.data)
|
|
382
|
+
if (!keyframe) this.#dumpRejectedVideoKeyframe(frame.data, frame.keyframe)
|
|
383
|
+
return keyframe === frame.keyframe ? frame : { ...frame, keyframe }
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
#dumpRejectedVideoKeyframe(data: Uint8Array, metadataKeyframe: boolean): void {
|
|
387
|
+
if (this.#keyframeRejectDumpCount >= 12) return
|
|
388
|
+
this.#keyframeRejectDumpCount++
|
|
389
|
+
if (this.#videoKeyframeFamily === 'hevc') {
|
|
390
|
+
const nals = isCompleteAvccSample(data)
|
|
391
|
+
? splitAvccNalUnits(data)
|
|
392
|
+
: isAnnexB(data) ? splitAnnexBNalUnits(data) : []
|
|
393
|
+
console.log(
|
|
394
|
+
`[L4-KF-GATE] reject#${this.#keyframeRejectDumpCount} len=${data.length} ` +
|
|
395
|
+
`metadataKf=${metadataKeyframe} family=hevc ` +
|
|
396
|
+
`nalTypes=[${nals.slice(0, 16).map((nal) => (nal.length >= 2 ? hevcNalUnitType(nal) : -1)).join(',')}]`,
|
|
397
|
+
)
|
|
398
|
+
return
|
|
399
|
+
}
|
|
400
|
+
const sample = splitH264SampleNalUnits(data)
|
|
401
|
+
const nalTypes = describeH264NalTypes(sample.nals)
|
|
402
|
+
const classify = classifyH264Keyframe(data)
|
|
403
|
+
const hex = Array.from(data.subarray(0, Math.min(48, data.length)))
|
|
404
|
+
.map(b => (b as number).toString(16).padStart(2, '0'))
|
|
405
|
+
.join(' ')
|
|
406
|
+
console.log(
|
|
407
|
+
`[L4-KF-GATE] reject#${this.#keyframeRejectDumpCount} len=${data.length} ` +
|
|
408
|
+
`metadataKf=${metadataKeyframe} classify=${classify} format=${sample.format} ` +
|
|
409
|
+
`nalTypes=[${nalTypes.join(',')}] first48=[${hex}]`,
|
|
410
|
+
)
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
/** Mark video as already synchronized by an external decoder path.
|
|
414
|
+
* Used by MoQ→multicast MSE continuity when MoQ has already appended a
|
|
415
|
+
* keyframe to the same SourceBuffer timeline before multicast takeover. */
|
|
416
|
+
markVideoSynced(): void {
|
|
417
|
+
if (this.#config.trackType === 'video') this.#armedVideo = true
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
/** Emit each frame as its own single-sample moof+mdat segment. Matches the
|
|
421
|
+
* case-20 path (`MmtpToCmaf.wrapVideoSample` → `encodeDataSegment`) byte-
|
|
422
|
+
* for-byte per frame. Working hypothesis (pending Chromium-source
|
|
423
|
+
* validation against `media/filters/chunk_demuxer.cc`):
|
|
424
|
+
* `encodeMultiSampleSegment` blobs are accepted by ChunkDemuxer in this
|
|
425
|
+
* pipeline but only the first sample renders for live, possibly because
|
|
426
|
+
* the pipeline expects per-frame moof boundaries. Track in BLO-3035.
|
|
427
|
+
*
|
|
428
|
+
* Stale-tfdt defense-in-depth: drops any frame whose timestamp is older
|
|
429
|
+
* than `#lastEmittedTs`. The SBN watermark already prevents out-of-order
|
|
430
|
+
* FEC-block emissions, but `addBypassFrame` (oversized MFU / no-FEC path)
|
|
431
|
+
* doesn't touch the watermark — a bypass frame at t=T followed by a FEC
|
|
432
|
+
* block whose frames precede T would otherwise inject earlier tfdt and
|
|
433
|
+
* detach the SourceBuffer. */
|
|
434
|
+
#ordersSamples(): boolean {
|
|
435
|
+
return this.#sampleOrder.mode === 'media-window' || this.#sampleReorderWindow > 0
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
#unwrapTimestamp(timestamp: number): number {
|
|
439
|
+
const wrapPeriod = this.#sampleOrder.mode === 'media-window'
|
|
440
|
+
? this.#sampleOrder.timestampWrapPeriod
|
|
441
|
+
: undefined
|
|
442
|
+
if (!wrapPeriod) return timestamp
|
|
443
|
+
const reference = this.#lastSourceTs >= 0
|
|
444
|
+
? this.#lastSourceTs
|
|
445
|
+
: this.#recoveredHorizonTs >= 0
|
|
446
|
+
? this.#recoveredHorizonTs
|
|
447
|
+
: this.#lastEmittedTs
|
|
448
|
+
if (reference < 0) return timestamp
|
|
449
|
+
return timestamp + Math.round((reference - timestamp) / wrapPeriod) * wrapPeriod
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
#emitSample(
|
|
453
|
+
data: Uint8Array,
|
|
454
|
+
timestamp: number,
|
|
455
|
+
duration: number,
|
|
456
|
+
keyframe: boolean,
|
|
457
|
+
advancesHorizon: boolean,
|
|
458
|
+
catalogTrackName: string | undefined,
|
|
459
|
+
mpuSequenceNumber: number | undefined,
|
|
460
|
+
packetSequenceNumber: number | undefined,
|
|
461
|
+
packetSequenceNumbers: number[] | undefined,
|
|
462
|
+
repairedPacketSequenceNumbers: number[] | undefined,
|
|
463
|
+
authoredTiming: AuthoredTiming | undefined,
|
|
464
|
+
// Required, not optional: an omitted optional argument is silently
|
|
465
|
+
// undefined, which is exactly how this field went missing from the
|
|
466
|
+
// recovered path in the first place. Callers with nothing to declare pass
|
|
467
|
+
// `undefined` on purpose.
|
|
468
|
+
deliveryProvenance: MmtpDeliveryProvenance | undefined,
|
|
469
|
+
/** Raw MMTP short-NTP header timestamp for telemetry only. */
|
|
470
|
+
mmtpTimestamp: bigint | undefined,
|
|
471
|
+
// Same rule as deliveryProvenance: required so a caller cannot drop the
|
|
472
|
+
// timed-MFU identity by omission. Untimed MFUs pass `undefined`.
|
|
473
|
+
movieFragmentSequenceNumber: number | undefined,
|
|
474
|
+
sampleNumber: number | undefined,
|
|
475
|
+
/** True only for locally generated concealment samples. */
|
|
476
|
+
synthetic: boolean | undefined,
|
|
477
|
+
/** libmmt's decision for the gap before this sample, carried to sinks. */
|
|
478
|
+
gapBefore: FrameGapDecision | undefined,
|
|
479
|
+
): void {
|
|
480
|
+
// Authored-timing admission — the rollback switch the BLO-30397 removal
|
|
481
|
+
// gate names. Refuse the sample outright rather than stripping its offset:
|
|
482
|
+
// the contract forbids rolling back "by zeroing already-authored offsets",
|
|
483
|
+
// and BLO-30401 measured why — a zeroed offset and a never-authored one are
|
|
484
|
+
// behaviourally identical downstream (same buffered ranges, same duration,
|
|
485
|
+
// no diagnostic), so a silent strip would look exactly like success while
|
|
486
|
+
// audio ran early forever. A de-admitted producer is expected to revert to
|
|
487
|
+
// raw batches, which are untouched by this branch.
|
|
488
|
+
if (authoredTiming !== undefined && !this.#config.admitAuthoredTiming) {
|
|
489
|
+
this.#sampleOrderStats.unadmittedAuthoredSamples++
|
|
490
|
+
return
|
|
491
|
+
}
|
|
492
|
+
const scaledTiming = authoredTiming === undefined
|
|
493
|
+
? null
|
|
494
|
+
: scaleAuthoredTiming(authoredTiming, this.#config.timescale)
|
|
495
|
+
if (authoredTiming !== undefined && scaledTiming === null) return
|
|
496
|
+
const authoredTimestamp = scaledTiming?.timestamp ?? timestamp
|
|
497
|
+
const authoredDuration = scaledTiming?.duration ?? duration
|
|
498
|
+
let compositionTimeOffset: number | undefined
|
|
499
|
+
if (authoredTiming !== undefined) {
|
|
500
|
+
const scaledOffset = scaleAuthoredTicks(
|
|
501
|
+
authoredTiming.presentationTime - authoredTiming.decodeTime,
|
|
502
|
+
authoredTiming.mediaTimescale,
|
|
503
|
+
this.#config.timescale,
|
|
504
|
+
)
|
|
505
|
+
if (scaledOffset === null) return
|
|
506
|
+
// trun stores composition offsets as integer target-timescale ticks.
|
|
507
|
+
// Preserve the nearest representable tick, then enforce the signed
|
|
508
|
+
// int32 serialization range on the rounded value.
|
|
509
|
+
const roundedOffset = Math.round(scaledOffset)
|
|
510
|
+
if (
|
|
511
|
+
!Number.isSafeInteger(roundedOffset) ||
|
|
512
|
+
roundedOffset < INT32_MIN ||
|
|
513
|
+
roundedOffset > INT32_MAX
|
|
514
|
+
) return
|
|
515
|
+
compositionTimeOffset = roundedOffset
|
|
516
|
+
}
|
|
517
|
+
const unwrappedTimestamp = this.#unwrapTimestamp(authoredTimestamp)
|
|
518
|
+
if (unwrappedTimestamp <= this.#lastEmittedTs) {
|
|
519
|
+
this.#sampleOrderStats.staleSamples++
|
|
520
|
+
return
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
const sample = this.#withEffectiveVideoKeyframe({
|
|
524
|
+
data,
|
|
525
|
+
timestamp: unwrappedTimestamp,
|
|
526
|
+
duration: authoredDuration,
|
|
527
|
+
keyframe,
|
|
528
|
+
...(mmtpTimestamp === undefined ? {} : { mmtpTimestamp }),
|
|
529
|
+
...(compositionTimeOffset === undefined ? {} : { compositionTimeOffset }),
|
|
530
|
+
advancesHorizon,
|
|
531
|
+
...(authoredTiming === undefined ? {} : { authoredTiming }),
|
|
532
|
+
...(packetSequenceNumber === undefined ? {} : { packetSequenceNumber }),
|
|
533
|
+
...(packetSequenceNumbers === undefined ? {} : { packetSequenceNumbers }),
|
|
534
|
+
...(repairedPacketSequenceNumbers === undefined
|
|
535
|
+
? {}
|
|
536
|
+
: { repairedPacketSequenceNumbers }),
|
|
537
|
+
...(mpuSequenceNumber === undefined ? {} : { mpuSequenceNumber }),
|
|
538
|
+
...(movieFragmentSequenceNumber === undefined ? {} : { movieFragmentSequenceNumber }),
|
|
539
|
+
...(sampleNumber === undefined ? {} : { sampleNumber }),
|
|
540
|
+
...(catalogTrackName === undefined ? {} : { catalogTrackName }),
|
|
541
|
+
...(deliveryProvenance === undefined ? {} : { deliveryProvenance }),
|
|
542
|
+
...(synthetic === undefined ? {} : { synthetic }),
|
|
543
|
+
...(gapBefore === undefined ? {} : { gapBefore }),
|
|
544
|
+
})
|
|
545
|
+
if (sample === null) return
|
|
546
|
+
if (advancesHorizon && unwrappedTimestamp > this.#lastSourceTs) {
|
|
547
|
+
this.#lastSourceTs = unwrappedTimestamp
|
|
548
|
+
} else if (!advancesHorizon && unwrappedTimestamp > this.#recoveredHorizonTs) {
|
|
549
|
+
this.#recoveredHorizonTs = unwrappedTimestamp
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
if (this.#ordersSamples()) {
|
|
553
|
+
this.#queueSample(sample)
|
|
554
|
+
return
|
|
555
|
+
}
|
|
556
|
+
this.#emitArrivalOrdered(sample)
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
#emitArrivalOrdered(sample: OrderedSample): void {
|
|
560
|
+
if (this.#config.trackType === 'video' && !this.#armedVideo && !sample.keyframe) {
|
|
561
|
+
this.#sampleOrderStats.preRapDeadlineDrops++
|
|
562
|
+
return
|
|
563
|
+
}
|
|
564
|
+
this.#emitSampleNow(sample)
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
#queueSample(sample: OrderedSample): void {
|
|
568
|
+
if (this.#sampleReorderQueue.some(s => s.timestamp === sample.timestamp)) {
|
|
569
|
+
this.#sampleOrderStats.duplicateSamples++
|
|
570
|
+
return
|
|
571
|
+
}
|
|
572
|
+
this.#sampleReorderQueue.push(sample)
|
|
573
|
+
this.#sampleReorderQueue.sort((a, b) => a.timestamp - b.timestamp)
|
|
574
|
+
this.#drainSampleReorder(false)
|
|
575
|
+
this.#enforceSampleOrderCapacity()
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
#releaseHorizon(): number {
|
|
579
|
+
return this.#lastSourceTs >= 0 ? this.#lastSourceTs : this.#recoveredHorizonTs
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
#sampleIsReleasable(sample: OrderedSample, flushAll: boolean): boolean {
|
|
583
|
+
if (flushAll) return true
|
|
584
|
+
if (this.#sampleOrder.mode === 'media-window') {
|
|
585
|
+
const horizon = this.#releaseHorizon()
|
|
586
|
+
return horizon >= 0 && sample.timestamp < horizon - this.#sampleOrder.deliveryWindow
|
|
587
|
+
}
|
|
588
|
+
return this.#sampleReorderQueue.length > this.#sampleReorderWindow
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
#dropPreRapSamples(flushAll: boolean): boolean {
|
|
592
|
+
if (this.#config.trackType !== 'video' || this.#armedVideo) return false
|
|
593
|
+
|
|
594
|
+
const firstRap = this.#sampleReorderQueue.findIndex(
|
|
595
|
+
sample => sample.keyframe && this.#sampleIsReleasable(sample, flushAll),
|
|
596
|
+
)
|
|
597
|
+
if (firstRap >= 0) {
|
|
598
|
+
if (firstRap > 0) {
|
|
599
|
+
this.#sampleOrderStats.preRapDeadlineDrops += firstRap
|
|
600
|
+
this.#sampleReorderQueue.splice(0, firstRap)
|
|
601
|
+
}
|
|
602
|
+
return false
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
let dropped = 0
|
|
606
|
+
while (
|
|
607
|
+
this.#sampleReorderQueue[0] &&
|
|
608
|
+
this.#sampleIsReleasable(this.#sampleReorderQueue[0]!, flushAll)
|
|
609
|
+
) {
|
|
610
|
+
this.#sampleReorderQueue.shift()
|
|
611
|
+
dropped++
|
|
612
|
+
}
|
|
613
|
+
if (flushAll) this.#sampleOrderStats.noRapFlushDrops += dropped
|
|
614
|
+
else this.#sampleOrderStats.preRapDeadlineDrops += dropped
|
|
615
|
+
return dropped > 0
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
#drainSampleReorder(flushAll: boolean): void {
|
|
619
|
+
while (this.#sampleReorderQueue.length > 0) {
|
|
620
|
+
if (this.#dropPreRapSamples(flushAll) && this.#sampleReorderQueue.length === 0) return
|
|
621
|
+
const sample = this.#sampleReorderQueue[0]
|
|
622
|
+
if (!sample || !this.#sampleIsReleasable(sample, flushAll)) return
|
|
623
|
+
this.#sampleReorderQueue.shift()
|
|
624
|
+
this.#emitQueuedSample(sample)
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
#emitQueuedSample(sample: OrderedSample): void {
|
|
629
|
+
const next = this.#sampleReorderQueue.find(s => s.timestamp > sample.timestamp)
|
|
630
|
+
const duration = sample.authoredTiming
|
|
631
|
+
? sample.duration
|
|
632
|
+
: next ? next.timestamp - sample.timestamp : sample.duration
|
|
633
|
+
this.#emitSampleNow({
|
|
634
|
+
...sample,
|
|
635
|
+
duration: duration > 0
|
|
636
|
+
? duration
|
|
637
|
+
: sample.authoredTiming ? sample.duration : this.#frameDuration,
|
|
638
|
+
})
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
#enforceSampleOrderCapacity(): void {
|
|
642
|
+
if (this.#sampleOrder.mode !== 'media-window') return
|
|
643
|
+
while (this.#sampleReorderQueue.length > this.#sampleOrderCapacity) {
|
|
644
|
+
const sample = this.#sampleReorderQueue.shift()
|
|
645
|
+
if (!sample) return
|
|
646
|
+
this.#sampleOrderStats.forcedReleases++
|
|
647
|
+
console.warn(
|
|
648
|
+
`[CmafBlockAssembler] ${this.#config.trackType} media-window queue exceeded ` +
|
|
649
|
+
`${this.#sampleOrderCapacity} samples; force-releasing timestamp=${sample.timestamp}`,
|
|
650
|
+
)
|
|
651
|
+
if (this.#config.trackType === 'video' && !this.#armedVideo && !sample.keyframe) {
|
|
652
|
+
this.#sampleOrderStats.preRapDeadlineDrops++
|
|
653
|
+
continue
|
|
654
|
+
}
|
|
655
|
+
this.#emitQueuedSample(sample)
|
|
656
|
+
}
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
#emitSampleNow(sample: CmafBlockSample): void {
|
|
660
|
+
if (sample.timestamp <= this.#lastEmittedTs) {
|
|
661
|
+
this.#sampleOrderStats.staleSamples++
|
|
662
|
+
return
|
|
663
|
+
}
|
|
664
|
+
this.#concealAudioGapBefore(sample)
|
|
665
|
+
this.#writeSampleNow(sample, true)
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
#writeSampleNow(sample: CmafBlockSample, rememberAudio: boolean): void {
|
|
669
|
+
const { timestamp, duration, keyframe } = sample
|
|
670
|
+
this.#lastEmittedTs = timestamp
|
|
671
|
+
if (this.#config.trackType === 'video' && keyframe) this.#armedVideo = true
|
|
672
|
+
const data = this.#normalizeSampleData(sample.data)
|
|
673
|
+
if (this.#config.trackType === 'audio' && rememberAudio && data.length > 0) {
|
|
674
|
+
// Keep decoder-proven codec data for concealment of a later gap that
|
|
675
|
+
// libmmt's release gate decided, on the AU's own authored clock.
|
|
676
|
+
this.#noteAudioAccessUnit(data, sample.authoredTiming)
|
|
677
|
+
}
|
|
678
|
+
if (this.onSample) {
|
|
679
|
+
this.onSample(data === sample.data ? sample : { ...sample, data })
|
|
680
|
+
} else {
|
|
681
|
+
const baseTime = this.#rebase(timestamp)
|
|
682
|
+
const segment = encodeDataSegment(
|
|
683
|
+
data,
|
|
684
|
+
baseTime,
|
|
685
|
+
duration,
|
|
686
|
+
keyframe,
|
|
687
|
+
this.#sequence++,
|
|
688
|
+
this.#config.trackId,
|
|
689
|
+
sample.compositionTimeOffset,
|
|
690
|
+
)
|
|
691
|
+
this.onSegment?.(segment)
|
|
692
|
+
}
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
/**
|
|
696
|
+
* Record an emitted audio AU for concealment in its authored media ticks —
|
|
697
|
+
* the clock libmmt's `audio-gap` decision was computed in, where an AAC AU
|
|
698
|
+
* is exactly 1024 ticks. An AU with no authored timing has no exact clock,
|
|
699
|
+
* so nothing is held for it and a later `audio-gap` is counted as untimed.
|
|
700
|
+
*/
|
|
701
|
+
#noteAudioAccessUnit(data: Uint8Array, timing: AuthoredTiming | undefined): void {
|
|
702
|
+
if (timing === undefined) {
|
|
703
|
+
this.#audioConcealer.reset()
|
|
704
|
+
this.#audioConcealClock = 'untimed'
|
|
705
|
+
return
|
|
706
|
+
}
|
|
707
|
+
this.#audioConcealer.note({
|
|
708
|
+
payload: data,
|
|
709
|
+
pts: timing.presentationTime,
|
|
710
|
+
duration: timing.durationTicks,
|
|
711
|
+
})
|
|
712
|
+
this.#audioConcealClock = timing.mediaTimescale
|
|
713
|
+
}
|
|
714
|
+
|
|
715
|
+
/**
|
|
716
|
+
* Repeat the last good AU into exactly the slots an `audio-gap` decision
|
|
717
|
+
* names. A synthesised AAC access unit is not portable across every
|
|
718
|
+
* AudioSpecificConfig, so only known-good encoded audio is repeated; with
|
|
719
|
+
* no good AU yet, or on `audio-discontinuity`, the sink re-anchors at the
|
|
720
|
+
* next sample and nothing is written.
|
|
721
|
+
*
|
|
722
|
+
* The gap is sized in authored ticks and each slot is mapped onto this
|
|
723
|
+
* assembler's timescale by `scaleAuthoredTiming`, the conversion every
|
|
724
|
+
* source AU goes through, so a slot lands exactly where the AU it replaces
|
|
725
|
+
* would have. Nothing here can throw into the packet path: a gap that
|
|
726
|
+
* cannot be sized exactly is re-anchored and counted.
|
|
727
|
+
*/
|
|
728
|
+
#concealAudioGapBefore(sample: CmafBlockSample): void {
|
|
729
|
+
const decision = sample.gapBefore
|
|
730
|
+
if (this.#config.trackType !== 'audio' || !decision || decision.trackKind !== 'audio') return
|
|
731
|
+
const timing = sample.authoredTiming
|
|
732
|
+
const clock = this.#audioConcealClock
|
|
733
|
+
if (
|
|
734
|
+
timing === undefined ||
|
|
735
|
+
clock === 'untimed' ||
|
|
736
|
+
(clock !== null && clock !== timing.mediaTimescale)
|
|
737
|
+
) {
|
|
738
|
+
// No single authored clock covers both this AU and the one held, so
|
|
739
|
+
// the gap has no exact size in ticks.
|
|
740
|
+
if (decision.reason === 'audio-gap') this.#audioConcealStats.untimedGaps++
|
|
741
|
+
else this.#audioConcealStats.reanchoredGaps++
|
|
742
|
+
return
|
|
743
|
+
}
|
|
744
|
+
const plan = this.#audioConcealer.plan(decision, { pts: timing.presentationTime })
|
|
745
|
+
if (plan.kind !== 'conceal') {
|
|
746
|
+
this.#audioConcealStats.reanchoredGaps++
|
|
747
|
+
return
|
|
748
|
+
}
|
|
749
|
+
for (const slot of plan.repeat) {
|
|
750
|
+
// Every input here is one this sample's own timing already scaled
|
|
751
|
+
// successfully (same media timescale and duration, an earlier pts), so
|
|
752
|
+
// the conversion cannot fail for a slot.
|
|
753
|
+
const scaled = scaleAuthoredTiming({
|
|
754
|
+
decodeTime: slot.pts,
|
|
755
|
+
presentationTime: slot.pts,
|
|
756
|
+
durationTicks: slot.duration,
|
|
757
|
+
mediaTimescale: timing.mediaTimescale,
|
|
758
|
+
}, this.#config.timescale)
|
|
759
|
+
if (scaled === null) continue
|
|
760
|
+
const slotTimestamp = this.#unwrapTimestamp(scaled.timestamp)
|
|
761
|
+
if (slotTimestamp <= this.#lastEmittedTs || slotTimestamp >= sample.timestamp) continue
|
|
762
|
+
this.#writeSampleNow({
|
|
763
|
+
data: slot.payload.slice(),
|
|
764
|
+
timestamp: slotTimestamp,
|
|
765
|
+
duration: scaled.duration,
|
|
766
|
+
keyframe: true,
|
|
767
|
+
synthetic: true,
|
|
768
|
+
}, false)
|
|
769
|
+
this.#audioConcealStats.repeatedUnits++
|
|
770
|
+
}
|
|
771
|
+
}
|
|
772
|
+
|
|
773
|
+
#emitPerSample(
|
|
774
|
+
frames: BlockFrame[],
|
|
775
|
+
audioDurationOverride?: number,
|
|
776
|
+
advancesHorizon = false,
|
|
777
|
+
): void {
|
|
778
|
+
for (let i = 0; i < frames.length; i++) {
|
|
779
|
+
const f = frames[i]!
|
|
780
|
+
// Per-frame duration from delta to next frame, or fallback.
|
|
781
|
+
let duration: number
|
|
782
|
+
if (this.#config.trackType === 'audio') {
|
|
783
|
+
duration = audioDurationOverride ?? this.#frameDuration
|
|
784
|
+
} else if (i < frames.length - 1) {
|
|
785
|
+
duration = frames[i + 1]!.timestamp - f.timestamp
|
|
786
|
+
} else if (i > 0) {
|
|
787
|
+
duration = f.timestamp - frames[i - 1]!.timestamp
|
|
788
|
+
} else {
|
|
789
|
+
duration = audioDurationOverride ?? this.#frameDuration
|
|
790
|
+
}
|
|
791
|
+
if (duration <= 0) duration = audioDurationOverride ?? this.#frameDuration
|
|
792
|
+
this.#emitSample(
|
|
793
|
+
f.data,
|
|
794
|
+
f.timestamp,
|
|
795
|
+
duration,
|
|
796
|
+
f.keyframe,
|
|
797
|
+
advancesHorizon,
|
|
798
|
+
f.catalogTrackName,
|
|
799
|
+
f.mpuSequenceNumber,
|
|
800
|
+
f.packetSequenceNumber,
|
|
801
|
+
f.packetSequenceNumbers,
|
|
802
|
+
f.repairedPacketSequenceNumbers,
|
|
803
|
+
f.authoredTiming,
|
|
804
|
+
f.deliveryProvenance,
|
|
805
|
+
f.mmtpTimestamp,
|
|
806
|
+
f.movieFragmentSequenceNumber,
|
|
807
|
+
f.sampleNumber,
|
|
808
|
+
f.synthetic,
|
|
809
|
+
f.gapBefore,
|
|
810
|
+
)
|
|
811
|
+
}
|
|
812
|
+
}
|
|
813
|
+
|
|
814
|
+
// Cumulative emit counters (see emitStats). They replace the BLO-3035 L4
|
|
815
|
+
// diagnostics, which hashed and hex-dumped every video keyframe and ran a
|
|
816
|
+
// 1 s setInterval console aggregate on the MSE path (BLO-36599).
|
|
817
|
+
#emitStats: CmafEmitStats = {
|
|
818
|
+
emits: 0,
|
|
819
|
+
samples: 0,
|
|
820
|
+
keyframes: 0,
|
|
821
|
+
addedFrames: 0,
|
|
822
|
+
recoveredBlocks: 0,
|
|
823
|
+
completedBlocks: 0,
|
|
824
|
+
failedBlocks: 0,
|
|
825
|
+
}
|
|
826
|
+
|
|
827
|
+
#countEmit(samples: ReadonlyArray<{ keyframe: boolean }>): void {
|
|
828
|
+
this.#emitStats.emits++
|
|
829
|
+
this.#emitStats.samples += samples.length
|
|
830
|
+
for (const sample of samples) if (sample.keyframe) this.#emitStats.keyframes++
|
|
831
|
+
}
|
|
832
|
+
|
|
833
|
+
constructor(config: BlockAssemblerConfig) {
|
|
834
|
+
this.#config = {
|
|
835
|
+
k: config.k,
|
|
836
|
+
timescale: config.timescale ?? 1_000_000,
|
|
837
|
+
trackId: config.trackId ?? 1,
|
|
838
|
+
trackType: config.trackType,
|
|
839
|
+
admitAuthoredTiming: config.admitAuthoredTiming ?? true,
|
|
840
|
+
}
|
|
841
|
+
|
|
842
|
+
// Compute default frame duration from fps (updated from timestamp deltas at runtime)
|
|
843
|
+
if (config.fps && config.fps > 0) {
|
|
844
|
+
this.#frameDuration = Math.round(this.#config.timescale / config.fps)
|
|
845
|
+
} else if (config.trackType === 'audio' && config.sampleRate) {
|
|
846
|
+
this.#frameDuration = Math.round(this.#config.timescale * 1024 / config.sampleRate)
|
|
847
|
+
} else {
|
|
848
|
+
// No fps info — will be refined from actual timestamp deltas
|
|
849
|
+
this.#frameDuration = 33333
|
|
850
|
+
}
|
|
851
|
+
|
|
852
|
+
const sampleReorderWindow = config.sampleReorderWindow ?? 0
|
|
853
|
+
if (!Number.isFinite(sampleReorderWindow) || sampleReorderWindow < 0) {
|
|
854
|
+
throw new Error('[CmafBlockAssembler] sampleReorderWindow must be finite and non-negative')
|
|
855
|
+
}
|
|
856
|
+
if (config.sampleOrder && sampleReorderWindow > 0) {
|
|
857
|
+
throw new Error('[CmafBlockAssembler] configure sampleOrder or sampleReorderWindow, not both')
|
|
858
|
+
}
|
|
859
|
+
this.#sampleReorderWindow = Math.floor(sampleReorderWindow)
|
|
860
|
+
this.#sampleOrder = config.sampleOrder ?? { mode: 'arrival' }
|
|
861
|
+
if (this.#sampleOrder.mode === 'media-window') {
|
|
862
|
+
const { deliveryWindow, timestampWrapPeriod } = this.#sampleOrder
|
|
863
|
+
if (!Number.isSafeInteger(deliveryWindow) || deliveryWindow <= 0) {
|
|
864
|
+
throw new Error(
|
|
865
|
+
'[CmafBlockAssembler] media-window deliveryWindow must be a positive safe integer',
|
|
866
|
+
)
|
|
867
|
+
}
|
|
868
|
+
if (
|
|
869
|
+
timestampWrapPeriod !== undefined &&
|
|
870
|
+
(!Number.isSafeInteger(timestampWrapPeriod) || timestampWrapPeriod <= deliveryWindow * 2)
|
|
871
|
+
) {
|
|
872
|
+
throw new Error(
|
|
873
|
+
'[CmafBlockAssembler] timestampWrapPeriod must be a safe integer larger than two delivery windows',
|
|
874
|
+
)
|
|
875
|
+
}
|
|
876
|
+
const samplesPerSecond = config.trackType === 'audio' && config.sampleRate
|
|
877
|
+
? config.sampleRate / 1024
|
|
878
|
+
: config.fps
|
|
879
|
+
if (!samplesPerSecond || !Number.isFinite(samplesPerSecond) || samplesPerSecond <= 0) {
|
|
880
|
+
throw new Error(
|
|
881
|
+
'[CmafBlockAssembler] media-window ordering requires catalog-derived media cadence',
|
|
882
|
+
)
|
|
883
|
+
}
|
|
884
|
+
this.#sampleOrderCapacity =
|
|
885
|
+
Math.ceil(deliveryWindow * samplesPerSecond / this.#config.timescale) + 1
|
|
886
|
+
}
|
|
887
|
+
const videoSampleEntry = config.trackType === 'video' ? resolveVideoSampleEntry(config) : null
|
|
888
|
+
this.#videoKeyframeFamily = videoSampleEntry === null
|
|
889
|
+
? null
|
|
890
|
+
: videoSampleEntry === 'unvalidated' ? null : videoCodecFamily(videoSampleEntry)
|
|
891
|
+
this.#hevcSampleEntry = videoSampleEntry === 'hev1' || videoSampleEntry === 'hvc1' ? videoSampleEntry : null
|
|
892
|
+
if (config.trackType === 'video') {
|
|
893
|
+
console.log(
|
|
894
|
+
`[L4-KF-CONFIG] validate=${this.#videoKeyframeFamily ?? false} ` +
|
|
895
|
+
`codec=${config.codec ?? ''} avcC=${config.avcC?.length ?? 0} hvcC=${config.hvcC?.length ?? 0}`,
|
|
896
|
+
)
|
|
897
|
+
}
|
|
898
|
+
|
|
899
|
+
// Store config for init segment generation
|
|
900
|
+
this._videoConfig = config.trackType === 'video' && config.avcC
|
|
901
|
+
? { avcC: config.avcC, width: config.width ?? 0, height: config.height ?? 0 }
|
|
902
|
+
: config.trackType === 'video' && config.hvcC && this.#hevcSampleEntry
|
|
903
|
+
? {
|
|
904
|
+
hvcC: config.hvcC,
|
|
905
|
+
hevcSampleEntry: this.#hevcSampleEntry,
|
|
906
|
+
width: config.width ?? 0,
|
|
907
|
+
height: config.height ?? 0,
|
|
908
|
+
}
|
|
909
|
+
: null
|
|
910
|
+
this._audioConfig = config.trackType === 'audio' && config.sampleRate && config.channels
|
|
911
|
+
? { sampleRate: config.sampleRate, channels: config.channels, asc: config.asc }
|
|
912
|
+
: null
|
|
913
|
+
}
|
|
914
|
+
|
|
915
|
+
private _videoConfig: {
|
|
916
|
+
avcC?: Uint8Array
|
|
917
|
+
hvcC?: Uint8Array
|
|
918
|
+
hevcSampleEntry?: 'hev1' | 'hvc1'
|
|
919
|
+
width: number
|
|
920
|
+
height: number
|
|
921
|
+
} | null
|
|
922
|
+
private _audioConfig: { sampleRate: number; channels: number; asc?: Uint8Array } | null
|
|
923
|
+
|
|
924
|
+
get sampleOrderStats(): Readonly<CmafSampleOrderStats> {
|
|
925
|
+
return { ...this.#sampleOrderStats }
|
|
926
|
+
}
|
|
927
|
+
|
|
928
|
+
/** Cumulative emit and block-event counters since construction. */
|
|
929
|
+
get emitStats(): Readonly<CmafEmitStats> {
|
|
930
|
+
return { ...this.#emitStats }
|
|
931
|
+
}
|
|
932
|
+
|
|
933
|
+
/** Cumulative audio gap concealment counters since construction. */
|
|
934
|
+
get audioConcealStats(): Readonly<CmafAudioConcealStats> {
|
|
935
|
+
return { ...this.#audioConcealStats }
|
|
936
|
+
}
|
|
937
|
+
|
|
938
|
+
/** Emit a single frame immediately as a one-sample moof+mdat segment.
|
|
939
|
+
*
|
|
940
|
+
* BLO-3035 layer 4 (4b): the prior SBN-grouped buffering scheme stranded
|
|
941
|
+
* frames whenever a multi-packet frame spanned FEC block boundaries —
|
|
942
|
+
* intermediate SBNs had packets-but-no-frames and got `completeBlock`
|
|
943
|
+
* events that landed on empty `#blocks` (→ `pendingFlush` leak), while
|
|
944
|
+
* the SBN where the multi-packet frame finally completed received the
|
|
945
|
+
* `addFrame` but its `trackBlock` count never reached K (→ `pendingBlocks`
|
|
946
|
+
* leak). Result: ~50% of received frames never reached MSE.
|
|
947
|
+
*
|
|
948
|
+
* This path now mirrors `addBypassFrame` — emit on arrival, no buffering.
|
|
949
|
+
* FEC recovery (`addRecoveredBlock`) keeps its SBN-watermark dedup against
|
|
950
|
+
* the same SBN being re-emitted from two paths. Out-of-order regression
|
|
951
|
+
* is guarded by the per-frame `lastEmittedTs` check (also enforced in
|
|
952
|
+
* `#emitPerSample`). The `sbn`/`esi` parameters are kept for the call-site
|
|
953
|
+
* signature but no longer affect routing. */
|
|
954
|
+
addFrame(
|
|
955
|
+
sbn: number,
|
|
956
|
+
esi: number,
|
|
957
|
+
data: Uint8Array,
|
|
958
|
+
timestamp: number,
|
|
959
|
+
keyframe: boolean,
|
|
960
|
+
catalogTrackName?: string,
|
|
961
|
+
mpuSequenceNumber?: number,
|
|
962
|
+
packetSequenceNumber?: number,
|
|
963
|
+
packetSequenceNumbers?: number[],
|
|
964
|
+
repairedPacketSequenceNumbers?: number[],
|
|
965
|
+
authoredTiming?: AuthoredTiming,
|
|
966
|
+
deliveryProvenance?: MmtpDeliveryProvenance,
|
|
967
|
+
mmtpTimestamp?: bigint,
|
|
968
|
+
movieFragmentSequenceNumber?: number,
|
|
969
|
+
sampleNumber?: number,
|
|
970
|
+
gapBefore?: FrameGapDecision,
|
|
971
|
+
): void {
|
|
972
|
+
void sbn; void esi
|
|
973
|
+
this.#emitStats.addedFrames++
|
|
974
|
+
|
|
975
|
+
if (!this.#ordersSamples()) this.#drainRecoveredUpTo(timestamp)
|
|
976
|
+
|
|
977
|
+
// Per-frame duration: default to catalog-derived `#frameDuration`. For
|
|
978
|
+
// variable-rate content Chrome refines duration from the next sample's
|
|
979
|
+
// DTS at append time, so a fallback here is safe.
|
|
980
|
+
this.#emitSample(
|
|
981
|
+
data,
|
|
982
|
+
timestamp,
|
|
983
|
+
this.#frameDuration,
|
|
984
|
+
keyframe,
|
|
985
|
+
true,
|
|
986
|
+
catalogTrackName,
|
|
987
|
+
mpuSequenceNumber,
|
|
988
|
+
packetSequenceNumber,
|
|
989
|
+
packetSequenceNumbers,
|
|
990
|
+
repairedPacketSequenceNumbers,
|
|
991
|
+
authoredTiming,
|
|
992
|
+
deliveryProvenance,
|
|
993
|
+
mmtpTimestamp,
|
|
994
|
+
movieFragmentSequenceNumber,
|
|
995
|
+
sampleNumber,
|
|
996
|
+
undefined,
|
|
997
|
+
gapBefore,
|
|
998
|
+
)
|
|
999
|
+
|
|
1000
|
+
this.#countEmit([{ keyframe }])
|
|
1001
|
+
}
|
|
1002
|
+
|
|
1003
|
+
/** Mark a block as complete (all K source symbols received or FEC recovery succeeded).
|
|
1004
|
+
* If all frames are already buffered, flushes immediately. Otherwise sets pending-flush. */
|
|
1005
|
+
completeBlock(sbn: number): void {
|
|
1006
|
+
void sbn
|
|
1007
|
+
this.#emitStats.completedBlocks++
|
|
1008
|
+
// Source samples are emitted directly from addFrame(). A clean FEC block
|
|
1009
|
+
// completion is still useful for stats, but it must not enqueue stale
|
|
1010
|
+
// pending flushes from the retired block-buffered path.
|
|
1011
|
+
}
|
|
1012
|
+
|
|
1013
|
+
/**
|
|
1014
|
+
* Record an unrecoverable block. Nothing is synthesised: the release gate
|
|
1015
|
+
* attaches the stall/conceal decision to the next released frame, and the
|
|
1016
|
+
* sink holds the last good frame (video) or repeats the last good AU into
|
|
1017
|
+
* the decided slots (audio).
|
|
1018
|
+
*/
|
|
1019
|
+
failBlock(sbn: number): void {
|
|
1020
|
+
void sbn
|
|
1021
|
+
this.#emitStats.failedBlocks++
|
|
1022
|
+
}
|
|
1023
|
+
|
|
1024
|
+
/** Add all frames from a FEC-recovered block at once and emit immediately. */
|
|
1025
|
+
addRecoveredBlock(sbn: number, frames: BlockFrame[]): void {
|
|
1026
|
+
void sbn
|
|
1027
|
+
this.#emitStats.recoveredBlocks++
|
|
1028
|
+
|
|
1029
|
+
if (frames.length === 0) return
|
|
1030
|
+
const normalized = this.#normalizeVideoKeyframes(frames)
|
|
1031
|
+
if (normalized.length === 0) return
|
|
1032
|
+
this.#countEmit(normalized)
|
|
1033
|
+
if (this.#ordersSamples()) {
|
|
1034
|
+
this.#emitPerSample(normalized, undefined, false)
|
|
1035
|
+
return
|
|
1036
|
+
}
|
|
1037
|
+
|
|
1038
|
+
// Arrival mode preserves the legacy protection against future recovered
|
|
1039
|
+
// samples advancing past source media. Explicit media-window mode routes
|
|
1040
|
+
// both paths through one queue instead.
|
|
1041
|
+
if (this.#lastSourceTs >= 0) {
|
|
1042
|
+
this.#enqueueRecovered(normalized)
|
|
1043
|
+
this.#drainRecoveredUpTo(this.#lastSourceTs)
|
|
1044
|
+
} else {
|
|
1045
|
+
const gated = this.#applyVideoKeyframeGate(normalized)
|
|
1046
|
+
if (gated) this.#emitPerSample(gated, undefined, false)
|
|
1047
|
+
}
|
|
1048
|
+
}
|
|
1049
|
+
|
|
1050
|
+
/** Emit a single frame immediately as a one-sample segment (for oversized packets bypassing FEC). */
|
|
1051
|
+
addBypassFrame(
|
|
1052
|
+
data: Uint8Array,
|
|
1053
|
+
timestamp: number,
|
|
1054
|
+
keyframe: boolean,
|
|
1055
|
+
catalogTrackName?: string,
|
|
1056
|
+
mpuSequenceNumber?: number,
|
|
1057
|
+
packetSequenceNumber?: number,
|
|
1058
|
+
packetSequenceNumbers?: number[],
|
|
1059
|
+
repairedPacketSequenceNumbers?: number[],
|
|
1060
|
+
authoredTiming?: AuthoredTiming,
|
|
1061
|
+
deliveryProvenance?: MmtpDeliveryProvenance,
|
|
1062
|
+
mmtpTimestamp?: bigint,
|
|
1063
|
+
movieFragmentSequenceNumber?: number,
|
|
1064
|
+
sampleNumber?: number,
|
|
1065
|
+
gapBefore?: FrameGapDecision,
|
|
1066
|
+
): void {
|
|
1067
|
+
if (!this.#ordersSamples()) this.#drainRecoveredUpTo(timestamp)
|
|
1068
|
+
this.#emitSample(
|
|
1069
|
+
data,
|
|
1070
|
+
timestamp,
|
|
1071
|
+
this.#frameDuration,
|
|
1072
|
+
keyframe,
|
|
1073
|
+
true,
|
|
1074
|
+
catalogTrackName,
|
|
1075
|
+
mpuSequenceNumber,
|
|
1076
|
+
packetSequenceNumber,
|
|
1077
|
+
packetSequenceNumbers,
|
|
1078
|
+
repairedPacketSequenceNumbers,
|
|
1079
|
+
authoredTiming,
|
|
1080
|
+
deliveryProvenance,
|
|
1081
|
+
mmtpTimestamp,
|
|
1082
|
+
movieFragmentSequenceNumber,
|
|
1083
|
+
sampleNumber,
|
|
1084
|
+
undefined,
|
|
1085
|
+
gapBefore,
|
|
1086
|
+
)
|
|
1087
|
+
}
|
|
1088
|
+
|
|
1089
|
+
/** Build and emit init segment from catalog config. */
|
|
1090
|
+
emitInit(): void {
|
|
1091
|
+
let init: Uint8Array | null = null
|
|
1092
|
+
if (this._videoConfig?.avcC) {
|
|
1093
|
+
init = buildVideoInitSegment(
|
|
1094
|
+
this._videoConfig.avcC,
|
|
1095
|
+
this._videoConfig.width,
|
|
1096
|
+
this._videoConfig.height,
|
|
1097
|
+
this.#config.timescale,
|
|
1098
|
+
this.#config.trackId,
|
|
1099
|
+
)
|
|
1100
|
+
} else if (this._videoConfig?.hvcC && this._videoConfig.hevcSampleEntry) {
|
|
1101
|
+
init = buildHevcVideoInitSegment(
|
|
1102
|
+
this._videoConfig.hevcSampleEntry,
|
|
1103
|
+
this._videoConfig.hvcC,
|
|
1104
|
+
this._videoConfig.width,
|
|
1105
|
+
this._videoConfig.height,
|
|
1106
|
+
this.#config.timescale,
|
|
1107
|
+
this.#config.trackId,
|
|
1108
|
+
)
|
|
1109
|
+
} else if (this._audioConfig) {
|
|
1110
|
+
init = buildAudioInitSegment(
|
|
1111
|
+
this._audioConfig.sampleRate,
|
|
1112
|
+
this._audioConfig.channels,
|
|
1113
|
+
this._audioConfig.asc,
|
|
1114
|
+
this.#config.timescale,
|
|
1115
|
+
this.#config.trackId,
|
|
1116
|
+
)
|
|
1117
|
+
}
|
|
1118
|
+
if (init) this.onInitSegment?.(init)
|
|
1119
|
+
}
|
|
1120
|
+
|
|
1121
|
+
/** Drain all ordered samples at end-of-stream or before reconfiguration. */
|
|
1122
|
+
flush(): void {
|
|
1123
|
+
if (this.#ordersSamples()) {
|
|
1124
|
+
this.#drainSampleReorder(true)
|
|
1125
|
+
return
|
|
1126
|
+
}
|
|
1127
|
+
if (this.#pendingRecovered.size === 0) return
|
|
1128
|
+
const frames = [...this.#pendingRecovered.values()].sort((a, b) => a.timestamp - b.timestamp)
|
|
1129
|
+
this.#pendingRecovered.clear()
|
|
1130
|
+
const gated = this.#applyVideoKeyframeGate(frames)
|
|
1131
|
+
if (gated) this.#emitPerSample(gated, undefined, false)
|
|
1132
|
+
else if (this.#config.trackType === 'video') {
|
|
1133
|
+
this.#sampleOrderStats.noRapFlushDrops += frames.length
|
|
1134
|
+
}
|
|
1135
|
+
}
|
|
1136
|
+
|
|
1137
|
+
/** Drop all queued state and begin a fresh timestamp epoch. */
|
|
1138
|
+
reset(): void {
|
|
1139
|
+
this.#pendingRecovered.clear()
|
|
1140
|
+
this.#sequence = 1
|
|
1141
|
+
this.#baseTs = -1
|
|
1142
|
+
this.#lastEmittedTs = -1
|
|
1143
|
+
this.#lastSourceTs = -1
|
|
1144
|
+
this.#recoveredHorizonTs = -1
|
|
1145
|
+
this.#audioConcealer.reset()
|
|
1146
|
+
this.#audioConcealClock = null
|
|
1147
|
+
this.#armedVideo = false
|
|
1148
|
+
this.#sampleReorderQueue = []
|
|
1149
|
+
}
|
|
1150
|
+
|
|
1151
|
+
dispose(): void {
|
|
1152
|
+
this.reset()
|
|
1153
|
+
this.onSegment = null
|
|
1154
|
+
this.onSample = null
|
|
1155
|
+
this.onInitSegment = null
|
|
1156
|
+
}
|
|
1157
|
+
|
|
1158
|
+
// ── Private ──
|
|
1159
|
+
|
|
1160
|
+
/** Rebase an absolute timestamp to relative media time (starting near 0). */
|
|
1161
|
+
#rebase(timestamp: number): number {
|
|
1162
|
+
if (this.#baseTs < 0) this.#baseTs = timestamp
|
|
1163
|
+
return timestamp - this.#baseTs
|
|
1164
|
+
}
|
|
1165
|
+
|
|
1166
|
+
#normalizeSampleData(data: Uint8Array): Uint8Array {
|
|
1167
|
+
return this.#config.trackType === 'audio' ? normalizeAacSampleForCmaf(data) : data
|
|
1168
|
+
}
|
|
1169
|
+
|
|
1170
|
+
#enqueueRecovered(frames: BlockFrame[]): void {
|
|
1171
|
+
for (const frame of frames) {
|
|
1172
|
+
if (this.#lastEmittedTs >= 0 && frame.timestamp <= this.#lastEmittedTs) {
|
|
1173
|
+
this.#sampleOrderStats.staleSamples++
|
|
1174
|
+
continue
|
|
1175
|
+
}
|
|
1176
|
+
if (this.#pendingRecovered.has(frame.timestamp)) {
|
|
1177
|
+
this.#sampleOrderStats.duplicateSamples++
|
|
1178
|
+
continue
|
|
1179
|
+
}
|
|
1180
|
+
this.#pendingRecovered.set(frame.timestamp, frame)
|
|
1181
|
+
}
|
|
1182
|
+
}
|
|
1183
|
+
|
|
1184
|
+
#drainRecoveredUpTo(horizonTs: number): void {
|
|
1185
|
+
if (this.#pendingRecovered.size === 0) return
|
|
1186
|
+
const ready = [...this.#pendingRecovered.values()]
|
|
1187
|
+
.filter(frame => frame.timestamp <= horizonTs)
|
|
1188
|
+
.sort((a, b) => a.timestamp - b.timestamp)
|
|
1189
|
+
|
|
1190
|
+
for (const frame of ready) {
|
|
1191
|
+
this.#pendingRecovered.delete(frame.timestamp)
|
|
1192
|
+
this.#emitSample(
|
|
1193
|
+
frame.data,
|
|
1194
|
+
frame.timestamp,
|
|
1195
|
+
this.#frameDuration,
|
|
1196
|
+
frame.keyframe,
|
|
1197
|
+
false,
|
|
1198
|
+
frame.catalogTrackName,
|
|
1199
|
+
frame.mpuSequenceNumber,
|
|
1200
|
+
frame.packetSequenceNumber,
|
|
1201
|
+
frame.packetSequenceNumbers,
|
|
1202
|
+
frame.repairedPacketSequenceNumbers,
|
|
1203
|
+
frame.authoredTiming,
|
|
1204
|
+
frame.deliveryProvenance,
|
|
1205
|
+
frame.mmtpTimestamp,
|
|
1206
|
+
frame.movieFragmentSequenceNumber,
|
|
1207
|
+
frame.sampleNumber,
|
|
1208
|
+
frame.synthetic,
|
|
1209
|
+
frame.gapBefore,
|
|
1210
|
+
)
|
|
1211
|
+
this.#countEmit([frame])
|
|
1212
|
+
}
|
|
1213
|
+
}
|
|
1214
|
+
}
|
|
1215
|
+
|
|
1216
|
+
/**
|
|
1217
|
+
* Video sample entry whose bitstream keyframe rule the assembler enforces,
|
|
1218
|
+
* selected from the catalog codec string (never sniffed):
|
|
1219
|
+
*
|
|
1220
|
+
* - `avc1`/`avc3`/`hev1`/`hvc1` from `config.codec`.
|
|
1221
|
+
* - No codec string: `avc1` (the legacy H.264 contract, which an avcC
|
|
1222
|
+
* confirms). An hvcC without a codec string is refused because the sample
|
|
1223
|
+
* entry (hev1 vs hvc1) cannot be chosen without it.
|
|
1224
|
+
* - Any other video codec (AV1, VP9): 'unvalidated' — its metadata keyframe
|
|
1225
|
+
* flag is trusted, as before.
|
|
1226
|
+
*
|
|
1227
|
+
* A configuration record that contradicts the codec family is refused.
|
|
1228
|
+
*/
|
|
1229
|
+
function resolveVideoSampleEntry(config: BlockAssemblerConfig): VideoSampleEntry | 'unvalidated' {
|
|
1230
|
+
if (!config.codec) {
|
|
1231
|
+
if (config.hvcC) {
|
|
1232
|
+
throw new Error('[CmafBlockAssembler] hvcC requires a catalog codec string naming hev1 or hvc1')
|
|
1233
|
+
}
|
|
1234
|
+
return 'avc1'
|
|
1235
|
+
}
|
|
1236
|
+
const fourCc = config.codec.split('.')[0]
|
|
1237
|
+
if (fourCc !== 'avc1' && fourCc !== 'avc3' && fourCc !== 'hev1' && fourCc !== 'hvc1') {
|
|
1238
|
+
if (config.avcC || config.hvcC) {
|
|
1239
|
+
throw new Error(`[CmafBlockAssembler] codec ${config.codec} contradicts the supplied avcC/hvcC`)
|
|
1240
|
+
}
|
|
1241
|
+
return 'unvalidated'
|
|
1242
|
+
}
|
|
1243
|
+
const sampleEntry = videoSampleEntryFromCodecString(config.codec)
|
|
1244
|
+
const family = videoCodecFamily(sampleEntry)
|
|
1245
|
+
if ((family === 'avc' && config.hvcC) || (family === 'hevc' && config.avcC)) {
|
|
1246
|
+
throw new Error(`[CmafBlockAssembler] codec ${config.codec} contradicts the supplied avcC/hvcC`)
|
|
1247
|
+
}
|
|
1248
|
+
return sampleEntry
|
|
1249
|
+
}
|
|
1250
|
+
|
|
1251
|
+
function describeH264NalTypes(nals: Uint8Array[]): number[] {
|
|
1252
|
+
return nals.slice(0, 16).map((nal) => (
|
|
1253
|
+
nal[0] !== undefined ? nal[0] & 0x1f : -1
|
|
1254
|
+
))
|
|
1255
|
+
}
|