@blockcast/mmt-container 0.1.0-main.149f78a4adae
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +432 -0
- package/dist/.build-stamp +0 -0
- package/dist/__tests__/test-utils.d.ts +71 -0
- package/dist/__tests__/test-utils.d.ts.map +1 -0
- package/dist/align.d.ts +148 -0
- package/dist/align.d.ts.map +1 -0
- package/dist/align.js +88 -0
- package/dist/align.js.map +7 -0
- package/dist/audio-init-parser.d.ts +66 -0
- package/dist/audio-init-parser.d.ts.map +1 -0
- package/dist/audio-init-parser.js +118 -0
- package/dist/audio-init-parser.js.map +7 -0
- package/dist/audio-ring-buffer.d.ts +50 -0
- package/dist/audio-ring-buffer.d.ts.map +1 -0
- package/dist/audio-ring-buffer.js +93 -0
- package/dist/audio-ring-buffer.js.map +7 -0
- package/dist/authored-timing.d.ts +32 -0
- package/dist/authored-timing.d.ts.map +1 -0
- package/dist/authored-timing.js +32 -0
- package/dist/authored-timing.js.map +7 -0
- package/dist/browser-runtime.js +17109 -0
- package/dist/browser-runtime.js.map +7 -0
- package/dist/carp-catalog.d.ts +318 -0
- package/dist/carp-catalog.d.ts.map +1 -0
- package/dist/carp-catalog.js +549 -0
- package/dist/carp-catalog.js.map +7 -0
- package/dist/catalog-fec-params.d.ts +82 -0
- package/dist/catalog-fec-params.d.ts.map +1 -0
- package/dist/catalog-fec-params.js +66 -0
- package/dist/catalog-fec-params.js.map +7 -0
- package/dist/catalog-network-source.d.ts +19 -0
- package/dist/catalog-network-source.d.ts.map +1 -0
- package/dist/catalog-network-source.js +22 -0
- package/dist/catalog-network-source.js.map +7 -0
- package/dist/catalog-validator.d.ts +18 -0
- package/dist/catalog-validator.d.ts.map +1 -0
- package/dist/catalog-validator.js +7747 -0
- package/dist/catalog-validator.js.map +7 -0
- package/dist/cmaf-block-assembler.d.ts +168 -0
- package/dist/cmaf-block-assembler.d.ts.map +1 -0
- package/dist/cmaf-block-assembler.js +1755 -0
- package/dist/cmaf-block-assembler.js.map +7 -0
- package/dist/cmaf-group-batcher.d.ts +38 -0
- package/dist/cmaf-group-batcher.d.ts.map +1 -0
- package/dist/cmaf-group-batcher.js +195 -0
- package/dist/cmaf-group-batcher.js.map +7 -0
- package/dist/cmcd-producer.d.ts +104 -0
- package/dist/cmcd-producer.d.ts.map +1 -0
- package/dist/cmcd-producer.js +207 -0
- package/dist/cmcd-producer.js.map +7 -0
- package/dist/codec-utils.d.ts +235 -0
- package/dist/codec-utils.d.ts.map +1 -0
- package/dist/codec-utils.js +1225 -0
- package/dist/codec-utils.js.map +7 -0
- package/dist/fec-codec.d.ts +32 -0
- package/dist/fec-codec.d.ts.map +1 -0
- package/dist/fec-codec.js +47 -0
- package/dist/fec-codec.js.map +7 -0
- package/dist/fec-manager.d.ts +266 -0
- package/dist/fec-manager.d.ts.map +1 -0
- package/dist/fec-manager.js +1447 -0
- package/dist/fec-manager.js.map +7 -0
- package/dist/fec-starvation-detector.d.ts +59 -0
- package/dist/fec-starvation-detector.d.ts.map +1 -0
- package/dist/fec-starvation-detector.js +75 -0
- package/dist/fec-starvation-detector.js.map +7 -0
- package/dist/fec-worker-setup.d.ts +76 -0
- package/dist/fec-worker-setup.d.ts.map +1 -0
- package/dist/fec-worker-setup.js +603 -0
- package/dist/fec-worker-setup.js.map +7 -0
- package/dist/fmp4-completion.d.ts +30 -0
- package/dist/fmp4-completion.d.ts.map +1 -0
- package/dist/fmp4-completion.js +21 -0
- package/dist/fmp4-completion.js.map +7 -0
- package/dist/fmp4-reader.d.ts +37 -0
- package/dist/fmp4-reader.d.ts.map +1 -0
- package/dist/fmp4-reader.js +100 -0
- package/dist/fmp4-reader.js.map +7 -0
- package/dist/index.d.ts +47 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +330 -0
- package/dist/index.js.map +7 -0
- package/dist/iso-bmff-timing.d.ts +47 -0
- package/dist/iso-bmff-timing.d.ts.map +1 -0
- package/dist/iso-bmff-timing.js +526 -0
- package/dist/iso-bmff-timing.js.map +7 -0
- package/dist/loc.d.ts +68 -0
- package/dist/loc.d.ts.map +1 -0
- package/dist/loc.js +143 -0
- package/dist/loc.js.map +7 -0
- package/dist/manifest-auth-plan.d.ts +30 -0
- package/dist/manifest-auth-plan.d.ts.map +1 -0
- package/dist/manifest-auth-plan.js +1267 -0
- package/dist/manifest-auth-plan.js.map +7 -0
- package/dist/media-router.d.ts +338 -0
- package/dist/media-router.d.ts.map +1 -0
- package/dist/media-router.js +802 -0
- package/dist/media-router.js.map +7 -0
- package/dist/mfu-reassembler.d.ts +264 -0
- package/dist/mfu-reassembler.d.ts.map +1 -0
- package/dist/mfu-reassembler.js +2668 -0
- package/dist/mfu-reassembler.js.map +7 -0
- package/dist/mfu-reassembly-policy.d.ts +24 -0
- package/dist/mfu-reassembly-policy.d.ts.map +1 -0
- package/dist/mfu-reassembly-policy.js +99 -0
- package/dist/mfu-reassembly-policy.js.map +7 -0
- package/dist/mmtp-container-parser.d.ts +304 -0
- package/dist/mmtp-container-parser.d.ts.map +1 -0
- package/dist/mmtp-container-parser.js +5378 -0
- package/dist/mmtp-container-parser.js.map +7 -0
- package/dist/mmtp-container-parser.no-modules.js +7853 -0
- package/dist/mmtp-container-parser.no-modules.js.map +7 -0
- package/dist/mmtp-decode-pipeline.d.ts +96 -0
- package/dist/mmtp-decode-pipeline.d.ts.map +1 -0
- package/dist/mmtp-decode-pipeline.js +5589 -0
- package/dist/mmtp-decode-pipeline.js.map +7 -0
- package/dist/mmtp-router.d.ts +383 -0
- package/dist/mmtp-router.d.ts.map +1 -0
- package/dist/mmtp-router.js +8384 -0
- package/dist/mmtp-router.js.map +7 -0
- package/dist/mmtp-to-cmaf.d.ts +166 -0
- package/dist/mmtp-to-cmaf.d.ts.map +1 -0
- package/dist/mmtp-to-cmaf.js +781 -0
- package/dist/mmtp-to-cmaf.js.map +7 -0
- package/dist/multicast-auth-catalog.d.ts +115 -0
- package/dist/multicast-auth-catalog.d.ts.map +1 -0
- package/dist/multicast-auth-catalog.js +202 -0
- package/dist/multicast-auth-catalog.js.map +7 -0
- package/dist/multicast-input.d.ts +107 -0
- package/dist/multicast-input.d.ts.map +1 -0
- package/dist/multicast-input.js +256 -0
- package/dist/multicast-input.js.map +7 -0
- package/dist/multicast-manager.d.ts +330 -0
- package/dist/multicast-manager.d.ts.map +1 -0
- package/dist/multicast-manager.js +11032 -0
- package/dist/multicast-manager.js.map +7 -0
- package/dist/multicast-transport.d.ts +62 -0
- package/dist/multicast-transport.d.ts.map +1 -0
- package/dist/multicast-transport.js +338 -0
- package/dist/multicast-transport.js.map +7 -0
- package/dist/multicast-unwrap.d.ts +57 -0
- package/dist/multicast-unwrap.d.ts.map +1 -0
- package/dist/multicast-unwrap.js +384 -0
- package/dist/multicast-unwrap.js.map +7 -0
- package/dist/no-modules.d.ts +9 -0
- package/dist/no-modules.d.ts.map +1 -0
- package/dist/no-modules.js +7819 -0
- package/dist/no-modules.js.map +7 -0
- package/dist/phase14-catalog-override.d.ts +126 -0
- package/dist/phase14-catalog-override.d.ts.map +1 -0
- package/dist/phase14-catalog-override.js +155 -0
- package/dist/phase14-catalog-override.js.map +7 -0
- package/dist/repair-fetch.d.ts +58 -0
- package/dist/repair-fetch.d.ts.map +1 -0
- package/dist/repair-fetch.js +117 -0
- package/dist/repair-fetch.js.map +7 -0
- package/dist/sample-combiner.d.ts +182 -0
- package/dist/sample-combiner.d.ts.map +1 -0
- package/dist/sample-combiner.js +571 -0
- package/dist/sample-combiner.js.map +7 -0
- package/dist/signaling.d.ts +29 -0
- package/dist/signaling.d.ts.map +1 -0
- package/dist/signaling.js +232 -0
- package/dist/signaling.js.map +7 -0
- package/dist/source-auth.d.ts +2 -0
- package/dist/source-auth.d.ts.map +1 -0
- package/dist/source-auth.js +1220 -0
- package/dist/source-auth.js.map +7 -0
- package/dist/switching-set-alignment.d.ts +61 -0
- package/dist/switching-set-alignment.d.ts.map +1 -0
- package/dist/switching-set-alignment.js +85 -0
- package/dist/switching-set-alignment.js.map +7 -0
- package/dist/timestamp-codec.d.ts +34 -0
- package/dist/timestamp-codec.d.ts.map +1 -0
- package/dist/timestamp-codec.js +456 -0
- package/dist/timestamp-codec.js.map +7 -0
- package/dist/track-pipeline-setup.d.ts +72 -0
- package/dist/track-pipeline-setup.d.ts.map +1 -0
- package/dist/track-pipeline-setup.js +306 -0
- package/dist/track-pipeline-setup.js.map +7 -0
- package/dist/track-pipeline.d.ts +121 -0
- package/dist/track-pipeline.d.ts.map +1 -0
- package/dist/track-pipeline.js +229 -0
- package/dist/track-pipeline.js.map +7 -0
- package/dist/track-session.d.ts +269 -0
- package/dist/track-session.d.ts.map +1 -0
- package/dist/track-session.js +597 -0
- package/dist/track-session.js.map +7 -0
- package/dist/transport-pool.d.ts +69 -0
- package/dist/transport-pool.d.ts.map +1 -0
- package/dist/transport-pool.js +88 -0
- package/dist/transport-pool.js.map +7 -0
- package/dist/wasm-mfu-reassembler.d.ts +81 -0
- package/dist/wasm-mfu-reassembler.d.ts.map +1 -0
- package/dist/wasm-mfu-reassembler.js +1915 -0
- package/dist/wasm-mfu-reassembler.js.map +7 -0
- package/dist/wasm-mmtp-container-parser.d.ts +10 -0
- package/dist/wasm-mmtp-container-parser.d.ts.map +1 -0
- package/dist/wasm-mmtp-container-parser.js +5635 -0
- package/dist/wasm-mmtp-container-parser.js.map +7 -0
- package/node_modules/@blockcast/fec-worker/README.md +213 -0
- package/node_modules/@blockcast/fec-worker/dist/.build-stamp +0 -0
- package/node_modules/@blockcast/fec-worker/dist/fec-worker-client.d.ts +80 -0
- package/node_modules/@blockcast/fec-worker/dist/fec-worker-client.d.ts.map +1 -0
- package/node_modules/@blockcast/fec-worker/dist/fec-worker-client.js +270 -0
- package/node_modules/@blockcast/fec-worker/dist/fec-worker-client.js.map +1 -0
- package/node_modules/@blockcast/fec-worker/dist/fec-worker-types.d.ts +252 -0
- package/node_modules/@blockcast/fec-worker/dist/fec-worker-types.d.ts.map +1 -0
- package/node_modules/@blockcast/fec-worker/dist/fec-worker-types.js +11 -0
- package/node_modules/@blockcast/fec-worker/dist/fec-worker-types.js.map +1 -0
- package/node_modules/@blockcast/fec-worker/dist/fec-worker.d.ts +14 -0
- package/node_modules/@blockcast/fec-worker/dist/fec-worker.d.ts.map +1 -0
- package/node_modules/@blockcast/fec-worker/dist/fec-worker.js +565 -0
- package/node_modules/@blockcast/fec-worker/dist/fec-worker.js.map +7 -0
- package/node_modules/@blockcast/fec-worker/dist/index.d.ts +10 -0
- package/node_modules/@blockcast/fec-worker/dist/index.d.ts.map +1 -0
- package/node_modules/@blockcast/fec-worker/dist/index.js +8 -0
- package/node_modules/@blockcast/fec-worker/dist/index.js.map +1 -0
- package/node_modules/@blockcast/fec-worker/dist/shred-fec-worker.d.ts +20 -0
- package/node_modules/@blockcast/fec-worker/dist/shred-fec-worker.d.ts.map +1 -0
- package/node_modules/@blockcast/fec-worker/dist/shred-fec-worker.js +349 -0
- package/node_modules/@blockcast/fec-worker/dist/shred-fec-worker.js.map +7 -0
- package/node_modules/@blockcast/fec-worker/dist/shred-worker-client.d.ts +104 -0
- package/node_modules/@blockcast/fec-worker/dist/shred-worker-client.d.ts.map +1 -0
- package/node_modules/@blockcast/fec-worker/dist/shred-worker-client.js +181 -0
- package/node_modules/@blockcast/fec-worker/dist/shred-worker-client.js.map +1 -0
- package/node_modules/@blockcast/fec-worker/dist/shred-worker-types.d.ts +179 -0
- package/node_modules/@blockcast/fec-worker/dist/shred-worker-types.d.ts.map +1 -0
- package/node_modules/@blockcast/fec-worker/dist/shred-worker-types.js +47 -0
- package/node_modules/@blockcast/fec-worker/dist/shred-worker-types.js.map +1 -0
- package/node_modules/@blockcast/fec-worker/dist/transfer.d.ts +9 -0
- package/node_modules/@blockcast/fec-worker/dist/transfer.d.ts.map +1 -0
- package/node_modules/@blockcast/fec-worker/dist/transfer.js +13 -0
- package/node_modules/@blockcast/fec-worker/dist/transfer.js.map +1 -0
- package/node_modules/@blockcast/fec-worker/package.json +59 -0
- package/node_modules/@blockcast/fec-worker/src/fec-worker-client.ts +309 -0
- package/node_modules/@blockcast/fec-worker/src/fec-worker-types.ts +268 -0
- package/node_modules/@blockcast/fec-worker/src/fec-worker.ts +1048 -0
- package/node_modules/@blockcast/fec-worker/src/index.ts +32 -0
- package/node_modules/@blockcast/fec-worker/src/shred-fec-worker.ts +558 -0
- package/node_modules/@blockcast/fec-worker/src/shred-worker-client.ts +222 -0
- package/node_modules/@blockcast/fec-worker/src/shred-worker-types.ts +194 -0
- package/node_modules/@blockcast/fec-worker/src/transfer.ts +12 -0
- package/node_modules/@blockcast/mmt-alta-parse/README.md +181 -0
- package/node_modules/@blockcast/mmt-alta-parse/dist/.build-stamp +0 -0
- package/node_modules/@blockcast/mmt-alta-parse/dist/index.d.ts +57 -0
- package/node_modules/@blockcast/mmt-alta-parse/dist/index.d.ts.map +1 -0
- package/node_modules/@blockcast/mmt-alta-parse/dist/index.js +115 -0
- package/node_modules/@blockcast/mmt-alta-parse/dist/index.js.map +1 -0
- package/node_modules/@blockcast/mmt-alta-parse/package.json +50 -0
- package/node_modules/@blockcast/mmt-alta-parse/src/index.ts +136 -0
- package/node_modules/@blockcast/mmt-base/README.md +120 -0
- package/node_modules/@blockcast/mmt-base/dist/.build-stamp +0 -0
- package/node_modules/@blockcast/mmt-base/dist/index.d.ts +2 -0
- package/node_modules/@blockcast/mmt-base/dist/index.d.ts.map +1 -0
- package/node_modules/@blockcast/mmt-base/dist/index.js +2 -0
- package/node_modules/@blockcast/mmt-base/dist/index.js.map +1 -0
- package/node_modules/@blockcast/mmt-base/dist/mmtp.d.ts +275 -0
- package/node_modules/@blockcast/mmt-base/dist/mmtp.d.ts.map +1 -0
- package/node_modules/@blockcast/mmt-base/dist/mmtp.js +644 -0
- package/node_modules/@blockcast/mmt-base/dist/mmtp.js.map +1 -0
- package/node_modules/@blockcast/mmt-base/package.json +49 -0
- package/node_modules/@blockcast/mmt-base/src/index.ts +1 -0
- package/node_modules/@blockcast/mmt-base/src/mmtp.ts +898 -0
- package/node_modules/@blockcast/mmt-fec/README.md +270 -0
- package/node_modules/@blockcast/mmt-fec/dist/.build-stamp +0 -0
- package/node_modules/@blockcast/mmt-fec/dist/block-manager.d.ts +80 -0
- package/node_modules/@blockcast/mmt-fec/dist/block-manager.d.ts.map +1 -0
- package/node_modules/@blockcast/mmt-fec/dist/block-manager.js +294 -0
- package/node_modules/@blockcast/mmt-fec/dist/block-manager.js.map +1 -0
- package/node_modules/@blockcast/mmt-fec/dist/block-processor.d.ts +72 -0
- package/node_modules/@blockcast/mmt-fec/dist/block-processor.d.ts.map +1 -0
- package/node_modules/@blockcast/mmt-fec/dist/block-processor.js +192 -0
- package/node_modules/@blockcast/mmt-fec/dist/block-processor.js.map +1 -0
- package/node_modules/@blockcast/mmt-fec/dist/cmaf-recovery.d.ts +46 -0
- package/node_modules/@blockcast/mmt-fec/dist/cmaf-recovery.d.ts.map +1 -0
- package/node_modules/@blockcast/mmt-fec/dist/cmaf-recovery.js +96 -0
- package/node_modules/@blockcast/mmt-fec/dist/cmaf-recovery.js.map +1 -0
- package/node_modules/@blockcast/mmt-fec/dist/cmaf-symbols.d.ts +33 -0
- package/node_modules/@blockcast/mmt-fec/dist/cmaf-symbols.d.ts.map +1 -0
- package/node_modules/@blockcast/mmt-fec/dist/cmaf-symbols.js +57 -0
- package/node_modules/@blockcast/mmt-fec/dist/cmaf-symbols.js.map +1 -0
- package/node_modules/@blockcast/mmt-fec/dist/fec-block-mapper.d.ts +185 -0
- package/node_modules/@blockcast/mmt-fec/dist/fec-block-mapper.d.ts.map +1 -0
- package/node_modules/@blockcast/mmt-fec/dist/fec-block-mapper.js +273 -0
- package/node_modules/@blockcast/mmt-fec/dist/fec-block-mapper.js.map +1 -0
- package/node_modules/@blockcast/mmt-fec/dist/index.d.ts +22 -0
- package/node_modules/@blockcast/mmt-fec/dist/index.d.ts.map +1 -0
- package/node_modules/@blockcast/mmt-fec/dist/index.js +22 -0
- package/node_modules/@blockcast/mmt-fec/dist/index.js.map +1 -0
- package/node_modules/@blockcast/mmt-fec/dist/loc-symbols.d.ts +20 -0
- package/node_modules/@blockcast/mmt-fec/dist/loc-symbols.d.ts.map +1 -0
- package/node_modules/@blockcast/mmt-fec/dist/loc-symbols.js +28 -0
- package/node_modules/@blockcast/mmt-fec/dist/loc-symbols.js.map +1 -0
- package/node_modules/@blockcast/mmt-fec/dist/oti.d.ts +174 -0
- package/node_modules/@blockcast/mmt-fec/dist/oti.d.ts.map +1 -0
- package/node_modules/@blockcast/mmt-fec/dist/oti.js +256 -0
- package/node_modules/@blockcast/mmt-fec/dist/oti.js.map +1 -0
- package/node_modules/@blockcast/mmt-fec/dist/reorder-buffer.d.ts +41 -0
- package/node_modules/@blockcast/mmt-fec/dist/reorder-buffer.d.ts.map +1 -0
- package/node_modules/@blockcast/mmt-fec/dist/reorder-buffer.js +81 -0
- package/node_modules/@blockcast/mmt-fec/dist/reorder-buffer.js.map +1 -0
- package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-alta-parse/README.md +181 -0
- package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-alta-parse/dist/.build-stamp +0 -0
- package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-alta-parse/dist/index.d.ts +57 -0
- package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-alta-parse/dist/index.d.ts.map +1 -0
- package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-alta-parse/dist/index.js +115 -0
- package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-alta-parse/dist/index.js.map +1 -0
- package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-alta-parse/package.json +50 -0
- package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-alta-parse/src/index.ts +136 -0
- package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/README.md +120 -0
- package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/.build-stamp +0 -0
- package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/index.d.ts +2 -0
- package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/index.d.ts.map +1 -0
- package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/index.js +2 -0
- package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/index.js.map +1 -0
- package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/mmtp.d.ts +275 -0
- package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/mmtp.d.ts.map +1 -0
- package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/mmtp.js +644 -0
- package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/mmtp.js.map +1 -0
- package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/package.json +49 -0
- package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/src/index.ts +1 -0
- package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/src/mmtp.ts +898 -0
- package/node_modules/@blockcast/mmt-fec/package.json +79 -0
- package/node_modules/@blockcast/mmt-fec/src/block-manager.ts +369 -0
- package/node_modules/@blockcast/mmt-fec/src/block-processor.ts +277 -0
- package/node_modules/@blockcast/mmt-fec/src/cmaf-recovery.ts +127 -0
- package/node_modules/@blockcast/mmt-fec/src/cmaf-symbols.ts +68 -0
- package/node_modules/@blockcast/mmt-fec/src/fec-block-mapper.ts +332 -0
- package/node_modules/@blockcast/mmt-fec/src/index.ts +77 -0
- package/node_modules/@blockcast/mmt-fec/src/loc-symbols.ts +34 -0
- package/node_modules/@blockcast/mmt-fec/src/oti.ts +319 -0
- package/node_modules/@blockcast/mmt-fec/src/reorder-buffer.ts +101 -0
- package/node_modules/@blockcast/mmt-manifest-verify/dist/.build-stamp +0 -0
- package/node_modules/@blockcast/mmt-manifest-verify/dist/admission.d.ts +28 -0
- package/node_modules/@blockcast/mmt-manifest-verify/dist/admission.d.ts.map +1 -0
- package/node_modules/@blockcast/mmt-manifest-verify/dist/admission.js +128 -0
- package/node_modules/@blockcast/mmt-manifest-verify/dist/admission.js.map +1 -0
- package/node_modules/@blockcast/mmt-manifest-verify/dist/index.d.ts +4 -0
- package/node_modules/@blockcast/mmt-manifest-verify/dist/index.d.ts.map +1 -0
- package/node_modules/@blockcast/mmt-manifest-verify/dist/index.js +4 -0
- package/node_modules/@blockcast/mmt-manifest-verify/dist/index.js.map +1 -0
- package/node_modules/@blockcast/mmt-manifest-verify/dist/source-auth.d.ts +37 -0
- package/node_modules/@blockcast/mmt-manifest-verify/dist/source-auth.d.ts.map +1 -0
- package/node_modules/@blockcast/mmt-manifest-verify/dist/source-auth.js +179 -0
- package/node_modules/@blockcast/mmt-manifest-verify/dist/source-auth.js.map +1 -0
- package/node_modules/@blockcast/mmt-manifest-verify/dist/verifier.d.ts +90 -0
- package/node_modules/@blockcast/mmt-manifest-verify/dist/verifier.d.ts.map +1 -0
- package/node_modules/@blockcast/mmt-manifest-verify/dist/verifier.js +462 -0
- package/node_modules/@blockcast/mmt-manifest-verify/dist/verifier.js.map +1 -0
- package/node_modules/@blockcast/mmt-manifest-verify/package.json +45 -0
- package/node_modules/@blockcast/mmt-manifest-verify/src/admission.ts +171 -0
- package/node_modules/@blockcast/mmt-manifest-verify/src/index.ts +3 -0
- package/node_modules/@blockcast/mmt-manifest-verify/src/source-auth.ts +230 -0
- package/node_modules/@blockcast/mmt-manifest-verify/src/verifier.ts +636 -0
- package/node_modules/@noble/hashes/LICENSE +21 -0
- package/node_modules/@noble/hashes/README.md +521 -0
- package/node_modules/@noble/hashes/_assert.d.ts +17 -0
- package/node_modules/@noble/hashes/_assert.d.ts.map +1 -0
- package/node_modules/@noble/hashes/_assert.js +18 -0
- package/node_modules/@noble/hashes/_assert.js.map +1 -0
- package/node_modules/@noble/hashes/_blake.d.ts +14 -0
- package/node_modules/@noble/hashes/_blake.d.ts.map +1 -0
- package/node_modules/@noble/hashes/_blake.js +50 -0
- package/node_modules/@noble/hashes/_blake.js.map +1 -0
- package/node_modules/@noble/hashes/_md.d.ts +51 -0
- package/node_modules/@noble/hashes/_md.d.ts.map +1 -0
- package/node_modules/@noble/hashes/_md.js +162 -0
- package/node_modules/@noble/hashes/_md.js.map +1 -0
- package/node_modules/@noble/hashes/_u64.d.ts +55 -0
- package/node_modules/@noble/hashes/_u64.d.ts.map +1 -0
- package/node_modules/@noble/hashes/_u64.js +90 -0
- package/node_modules/@noble/hashes/_u64.js.map +1 -0
- package/node_modules/@noble/hashes/argon2.d.ts +32 -0
- package/node_modules/@noble/hashes/argon2.d.ts.map +1 -0
- package/node_modules/@noble/hashes/argon2.js +401 -0
- package/node_modules/@noble/hashes/argon2.js.map +1 -0
- package/node_modules/@noble/hashes/blake1.d.ts +106 -0
- package/node_modules/@noble/hashes/blake1.d.ts.map +1 -0
- package/node_modules/@noble/hashes/blake1.js +459 -0
- package/node_modules/@noble/hashes/blake1.js.map +1 -0
- package/node_modules/@noble/hashes/blake2.d.ts +116 -0
- package/node_modules/@noble/hashes/blake2.d.ts.map +1 -0
- package/node_modules/@noble/hashes/blake2.js +420 -0
- package/node_modules/@noble/hashes/blake2.js.map +1 -0
- package/node_modules/@noble/hashes/blake2b.d.ts +11 -0
- package/node_modules/@noble/hashes/blake2b.d.ts.map +1 -0
- package/node_modules/@noble/hashes/blake2b.js +14 -0
- package/node_modules/@noble/hashes/blake2b.js.map +1 -0
- package/node_modules/@noble/hashes/blake2s.d.ts +20 -0
- package/node_modules/@noble/hashes/blake2s.d.ts.map +1 -0
- package/node_modules/@noble/hashes/blake2s.js +24 -0
- package/node_modules/@noble/hashes/blake2s.js.map +1 -0
- package/node_modules/@noble/hashes/blake3.d.ts +54 -0
- package/node_modules/@noble/hashes/blake3.d.ts.map +1 -0
- package/node_modules/@noble/hashes/blake3.js +255 -0
- package/node_modules/@noble/hashes/blake3.js.map +1 -0
- package/node_modules/@noble/hashes/crypto.d.ts +2 -0
- package/node_modules/@noble/hashes/crypto.d.ts.map +1 -0
- package/node_modules/@noble/hashes/crypto.js +5 -0
- package/node_modules/@noble/hashes/crypto.js.map +1 -0
- package/node_modules/@noble/hashes/cryptoNode.d.ts +2 -0
- package/node_modules/@noble/hashes/cryptoNode.d.ts.map +1 -0
- package/node_modules/@noble/hashes/cryptoNode.js +18 -0
- package/node_modules/@noble/hashes/cryptoNode.js.map +1 -0
- package/node_modules/@noble/hashes/eskdf.d.ts +47 -0
- package/node_modules/@noble/hashes/eskdf.d.ts.map +1 -0
- package/node_modules/@noble/hashes/eskdf.js +166 -0
- package/node_modules/@noble/hashes/eskdf.js.map +1 -0
- package/node_modules/@noble/hashes/esm/_assert.d.ts +17 -0
- package/node_modules/@noble/hashes/esm/_assert.d.ts.map +1 -0
- package/node_modules/@noble/hashes/esm/_assert.js +15 -0
- package/node_modules/@noble/hashes/esm/_assert.js.map +1 -0
- package/node_modules/@noble/hashes/esm/_blake.d.ts +14 -0
- package/node_modules/@noble/hashes/esm/_blake.d.ts.map +1 -0
- package/node_modules/@noble/hashes/esm/_blake.js +45 -0
- package/node_modules/@noble/hashes/esm/_blake.js.map +1 -0
- package/node_modules/@noble/hashes/esm/_md.d.ts +51 -0
- package/node_modules/@noble/hashes/esm/_md.d.ts.map +1 -0
- package/node_modules/@noble/hashes/esm/_md.js +155 -0
- package/node_modules/@noble/hashes/esm/_md.js.map +1 -0
- package/node_modules/@noble/hashes/esm/_u64.d.ts +55 -0
- package/node_modules/@noble/hashes/esm/_u64.d.ts.map +1 -0
- package/node_modules/@noble/hashes/esm/_u64.js +67 -0
- package/node_modules/@noble/hashes/esm/_u64.js.map +1 -0
- package/node_modules/@noble/hashes/esm/argon2.d.ts +32 -0
- package/node_modules/@noble/hashes/esm/argon2.d.ts.map +1 -0
- package/node_modules/@noble/hashes/esm/argon2.js +392 -0
- package/node_modules/@noble/hashes/esm/argon2.js.map +1 -0
- package/node_modules/@noble/hashes/esm/blake1.d.ts +106 -0
- package/node_modules/@noble/hashes/esm/blake1.d.ts.map +1 -0
- package/node_modules/@noble/hashes/esm/blake1.js +452 -0
- package/node_modules/@noble/hashes/esm/blake1.js.map +1 -0
- package/node_modules/@noble/hashes/esm/blake2.d.ts +116 -0
- package/node_modules/@noble/hashes/esm/blake2.d.ts.map +1 -0
- package/node_modules/@noble/hashes/esm/blake2.js +413 -0
- package/node_modules/@noble/hashes/esm/blake2.js.map +1 -0
- package/node_modules/@noble/hashes/esm/blake2b.d.ts +11 -0
- package/node_modules/@noble/hashes/esm/blake2b.d.ts.map +1 -0
- package/node_modules/@noble/hashes/esm/blake2b.js +11 -0
- package/node_modules/@noble/hashes/esm/blake2b.js.map +1 -0
- package/node_modules/@noble/hashes/esm/blake2s.d.ts +20 -0
- package/node_modules/@noble/hashes/esm/blake2s.d.ts.map +1 -0
- package/node_modules/@noble/hashes/esm/blake2s.js +21 -0
- package/node_modules/@noble/hashes/esm/blake2s.js.map +1 -0
- package/node_modules/@noble/hashes/esm/blake3.d.ts +54 -0
- package/node_modules/@noble/hashes/esm/blake3.d.ts.map +1 -0
- package/node_modules/@noble/hashes/esm/blake3.js +251 -0
- package/node_modules/@noble/hashes/esm/blake3.js.map +1 -0
- package/node_modules/@noble/hashes/esm/crypto.d.ts +2 -0
- package/node_modules/@noble/hashes/esm/crypto.d.ts.map +1 -0
- package/node_modules/@noble/hashes/esm/crypto.js +2 -0
- package/node_modules/@noble/hashes/esm/crypto.js.map +1 -0
- package/node_modules/@noble/hashes/esm/cryptoNode.d.ts +2 -0
- package/node_modules/@noble/hashes/esm/cryptoNode.d.ts.map +1 -0
- package/node_modules/@noble/hashes/esm/cryptoNode.js +15 -0
- package/node_modules/@noble/hashes/esm/cryptoNode.js.map +1 -0
- package/node_modules/@noble/hashes/esm/eskdf.d.ts +47 -0
- package/node_modules/@noble/hashes/esm/eskdf.d.ts.map +1 -0
- package/node_modules/@noble/hashes/esm/eskdf.js +160 -0
- package/node_modules/@noble/hashes/esm/eskdf.js.map +1 -0
- package/node_modules/@noble/hashes/esm/hkdf.d.ts +36 -0
- package/node_modules/@noble/hashes/esm/hkdf.d.ts.map +1 -0
- package/node_modules/@noble/hashes/esm/hkdf.js +82 -0
- package/node_modules/@noble/hashes/esm/hkdf.js.map +1 -0
- package/node_modules/@noble/hashes/esm/hmac.d.ts +35 -0
- package/node_modules/@noble/hashes/esm/hmac.d.ts.map +1 -0
- package/node_modules/@noble/hashes/esm/hmac.js +86 -0
- package/node_modules/@noble/hashes/esm/hmac.js.map +1 -0
- package/node_modules/@noble/hashes/esm/index.d.ts +2 -0
- package/node_modules/@noble/hashes/esm/index.d.ts.map +1 -0
- package/node_modules/@noble/hashes/esm/index.js +33 -0
- package/node_modules/@noble/hashes/esm/index.js.map +1 -0
- package/node_modules/@noble/hashes/esm/legacy.d.ts +71 -0
- package/node_modules/@noble/hashes/esm/legacy.d.ts.map +1 -0
- package/node_modules/@noble/hashes/esm/legacy.js +281 -0
- package/node_modules/@noble/hashes/esm/legacy.js.map +1 -0
- package/node_modules/@noble/hashes/esm/package.json +10 -0
- package/node_modules/@noble/hashes/esm/pbkdf2.d.ts +23 -0
- package/node_modules/@noble/hashes/esm/pbkdf2.d.ts.map +1 -0
- package/node_modules/@noble/hashes/esm/pbkdf2.js +97 -0
- package/node_modules/@noble/hashes/esm/pbkdf2.js.map +1 -0
- package/node_modules/@noble/hashes/esm/ripemd160.d.ts +13 -0
- package/node_modules/@noble/hashes/esm/ripemd160.d.ts.map +1 -0
- package/node_modules/@noble/hashes/esm/ripemd160.js +13 -0
- package/node_modules/@noble/hashes/esm/ripemd160.js.map +1 -0
- package/node_modules/@noble/hashes/esm/scrypt.d.ts +34 -0
- package/node_modules/@noble/hashes/esm/scrypt.d.ts.map +1 -0
- package/node_modules/@noble/hashes/esm/scrypt.js +228 -0
- package/node_modules/@noble/hashes/esm/scrypt.js.map +1 -0
- package/node_modules/@noble/hashes/esm/sha1.d.ts +11 -0
- package/node_modules/@noble/hashes/esm/sha1.d.ts.map +1 -0
- package/node_modules/@noble/hashes/esm/sha1.js +11 -0
- package/node_modules/@noble/hashes/esm/sha1.js.map +1 -0
- package/node_modules/@noble/hashes/esm/sha2.d.ts +159 -0
- package/node_modules/@noble/hashes/esm/sha2.d.ts.map +1 -0
- package/node_modules/@noble/hashes/esm/sha2.js +375 -0
- package/node_modules/@noble/hashes/esm/sha2.js.map +1 -0
- package/node_modules/@noble/hashes/esm/sha256.d.ts +20 -0
- package/node_modules/@noble/hashes/esm/sha256.d.ts.map +1 -0
- package/node_modules/@noble/hashes/esm/sha256.js +20 -0
- package/node_modules/@noble/hashes/esm/sha256.js.map +1 -0
- package/node_modules/@noble/hashes/esm/sha3-addons.d.ts +142 -0
- package/node_modules/@noble/hashes/esm/sha3-addons.d.ts.map +1 -0
- package/node_modules/@noble/hashes/esm/sha3-addons.js +393 -0
- package/node_modules/@noble/hashes/esm/sha3-addons.js.map +1 -0
- package/node_modules/@noble/hashes/esm/sha3.d.ts +53 -0
- package/node_modules/@noble/hashes/esm/sha3.d.ts.map +1 -0
- package/node_modules/@noble/hashes/esm/sha3.js +234 -0
- package/node_modules/@noble/hashes/esm/sha3.js.map +1 -0
- package/node_modules/@noble/hashes/esm/sha512.d.ts +26 -0
- package/node_modules/@noble/hashes/esm/sha512.d.ts.map +1 -0
- package/node_modules/@noble/hashes/esm/sha512.js +26 -0
- package/node_modules/@noble/hashes/esm/sha512.js.map +1 -0
- package/node_modules/@noble/hashes/esm/utils.d.ts +161 -0
- package/node_modules/@noble/hashes/esm/utils.d.ts.map +1 -0
- package/node_modules/@noble/hashes/esm/utils.js +281 -0
- package/node_modules/@noble/hashes/esm/utils.js.map +1 -0
- package/node_modules/@noble/hashes/hkdf.d.ts +36 -0
- package/node_modules/@noble/hashes/hkdf.d.ts.map +1 -0
- package/node_modules/@noble/hashes/hkdf.js +88 -0
- package/node_modules/@noble/hashes/hkdf.js.map +1 -0
- package/node_modules/@noble/hashes/hmac.d.ts +35 -0
- package/node_modules/@noble/hashes/hmac.d.ts.map +1 -0
- package/node_modules/@noble/hashes/hmac.js +91 -0
- package/node_modules/@noble/hashes/hmac.js.map +1 -0
- package/node_modules/@noble/hashes/index.d.ts +1 -0
- package/node_modules/@noble/hashes/index.d.ts.map +1 -0
- package/node_modules/@noble/hashes/index.js +33 -0
- package/node_modules/@noble/hashes/index.js.map +1 -0
- package/node_modules/@noble/hashes/legacy.d.ts +71 -0
- package/node_modules/@noble/hashes/legacy.d.ts.map +1 -0
- package/node_modules/@noble/hashes/legacy.js +287 -0
- package/node_modules/@noble/hashes/legacy.js.map +1 -0
- package/node_modules/@noble/hashes/package.json +266 -0
- package/node_modules/@noble/hashes/pbkdf2.d.ts +23 -0
- package/node_modules/@noble/hashes/pbkdf2.d.ts.map +1 -0
- package/node_modules/@noble/hashes/pbkdf2.js +101 -0
- package/node_modules/@noble/hashes/pbkdf2.js.map +1 -0
- package/node_modules/@noble/hashes/ripemd160.d.ts +13 -0
- package/node_modules/@noble/hashes/ripemd160.d.ts.map +1 -0
- package/node_modules/@noble/hashes/ripemd160.js +16 -0
- package/node_modules/@noble/hashes/ripemd160.js.map +1 -0
- package/node_modules/@noble/hashes/scrypt.d.ts +34 -0
- package/node_modules/@noble/hashes/scrypt.d.ts.map +1 -0
- package/node_modules/@noble/hashes/scrypt.js +232 -0
- package/node_modules/@noble/hashes/scrypt.js.map +1 -0
- package/node_modules/@noble/hashes/sha1.d.ts +11 -0
- package/node_modules/@noble/hashes/sha1.d.ts.map +1 -0
- package/node_modules/@noble/hashes/sha1.js +14 -0
- package/node_modules/@noble/hashes/sha1.js.map +1 -0
- package/node_modules/@noble/hashes/sha2.d.ts +159 -0
- package/node_modules/@noble/hashes/sha2.d.ts.map +1 -0
- package/node_modules/@noble/hashes/sha2.js +384 -0
- package/node_modules/@noble/hashes/sha2.js.map +1 -0
- package/node_modules/@noble/hashes/sha256.d.ts +20 -0
- package/node_modules/@noble/hashes/sha256.d.ts.map +1 -0
- package/node_modules/@noble/hashes/sha256.js +23 -0
- package/node_modules/@noble/hashes/sha256.js.map +1 -0
- package/node_modules/@noble/hashes/sha3-addons.d.ts +142 -0
- package/node_modules/@noble/hashes/sha3-addons.d.ts.map +1 -0
- package/node_modules/@noble/hashes/sha3-addons.js +402 -0
- package/node_modules/@noble/hashes/sha3-addons.js.map +1 -0
- package/node_modules/@noble/hashes/sha3.d.ts +53 -0
- package/node_modules/@noble/hashes/sha3.d.ts.map +1 -0
- package/node_modules/@noble/hashes/sha3.js +239 -0
- package/node_modules/@noble/hashes/sha3.js.map +1 -0
- package/node_modules/@noble/hashes/sha512.d.ts +26 -0
- package/node_modules/@noble/hashes/sha512.d.ts.map +1 -0
- package/node_modules/@noble/hashes/sha512.js +29 -0
- package/node_modules/@noble/hashes/sha512.js.map +1 -0
- package/node_modules/@noble/hashes/src/_assert.ts +22 -0
- package/node_modules/@noble/hashes/src/_blake.ts +50 -0
- package/node_modules/@noble/hashes/src/_md.ts +176 -0
- package/node_modules/@noble/hashes/src/_u64.ts +91 -0
- package/node_modules/@noble/hashes/src/argon2.ts +497 -0
- package/node_modules/@noble/hashes/src/blake1.ts +534 -0
- package/node_modules/@noble/hashes/src/blake2.ts +486 -0
- package/node_modules/@noble/hashes/src/blake2b.ts +10 -0
- package/node_modules/@noble/hashes/src/blake2s.ts +20 -0
- package/node_modules/@noble/hashes/src/blake3.ts +272 -0
- package/node_modules/@noble/hashes/src/crypto.ts +9 -0
- package/node_modules/@noble/hashes/src/cryptoNode.ts +15 -0
- package/node_modules/@noble/hashes/src/eskdf.ts +187 -0
- package/node_modules/@noble/hashes/src/hkdf.ts +88 -0
- package/node_modules/@noble/hashes/src/hmac.ts +94 -0
- package/node_modules/@noble/hashes/src/index.ts +31 -0
- package/node_modules/@noble/hashes/src/legacy.ts +293 -0
- package/node_modules/@noble/hashes/src/pbkdf2.ts +122 -0
- package/node_modules/@noble/hashes/src/ripemd160.ts +12 -0
- package/node_modules/@noble/hashes/src/scrypt.ts +257 -0
- package/node_modules/@noble/hashes/src/sha1.ts +10 -0
- package/node_modules/@noble/hashes/src/sha2.ts +402 -0
- package/node_modules/@noble/hashes/src/sha256.ts +24 -0
- package/node_modules/@noble/hashes/src/sha3-addons.ts +499 -0
- package/node_modules/@noble/hashes/src/sha3.ts +258 -0
- package/node_modules/@noble/hashes/src/sha512.ts +34 -0
- package/node_modules/@noble/hashes/src/utils.ts +395 -0
- package/node_modules/@noble/hashes/utils.d.ts +161 -0
- package/node_modules/@noble/hashes/utils.d.ts.map +1 -0
- package/node_modules/@noble/hashes/utils.js +313 -0
- package/node_modules/@noble/hashes/utils.js.map +1 -0
- package/package.json +203 -0
- package/schemas/catalog.schema.json +283 -0
- package/src/align.ts +235 -0
- package/src/audio-init-parser.ts +189 -0
- package/src/audio-ring-buffer.ts +120 -0
- package/src/authored-timing.ts +63 -0
- package/src/carp-catalog.ts +776 -0
- package/src/catalog-fec-params.ts +167 -0
- package/src/catalog-network-source.ts +49 -0
- package/src/catalog-validator.ts +34 -0
- package/src/cmaf-block-assembler.ts +1058 -0
- package/src/cmaf-group-batcher.ts +104 -0
- package/src/cmcd-producer.ts +329 -0
- package/src/codec-utils.ts +1539 -0
- package/src/fec-codec.ts +61 -0
- package/src/fec-manager.ts +1939 -0
- package/src/fec-starvation-detector.ts +128 -0
- package/src/fec-worker-setup.ts +177 -0
- package/src/fmp4-completion.ts +47 -0
- package/src/fmp4-reader.ts +112 -0
- package/src/index.ts +443 -0
- package/src/iso-bmff-timing.ts +688 -0
- package/src/loc.ts +181 -0
- package/src/manifest-auth-plan.ts +107 -0
- package/src/media-router.ts +1185 -0
- package/src/mfu-reassembler.ts +2842 -0
- package/src/mfu-reassembly-policy.ts +134 -0
- package/src/mmtp-container-parser.ts +1498 -0
- package/src/mmtp-decode-pipeline.ts +315 -0
- package/src/mmtp-router.ts +3027 -0
- package/src/mmtp-to-cmaf.ts +956 -0
- package/src/multicast-auth-catalog.ts +340 -0
- package/src/multicast-input.ts +439 -0
- package/src/multicast-manager.ts +1503 -0
- package/src/multicast-transport.ts +320 -0
- package/src/multicast-unwrap.ts +132 -0
- package/src/no-modules.ts +20 -0
- package/src/phase14-catalog-override.ts +336 -0
- package/src/repair-fetch.ts +217 -0
- package/src/sample-combiner.ts +846 -0
- package/src/signaling.ts +78 -0
- package/src/source-auth.ts +1 -0
- package/src/switching-set-alignment.ts +109 -0
- package/src/timestamp-codec.ts +265 -0
- package/src/track-pipeline-setup.ts +189 -0
- package/src/track-pipeline.ts +332 -0
- package/src/track-session.ts +778 -0
- package/src/transport-pool.ts +128 -0
- package/src/wasm-mfu-reassembler.ts +372 -0
- package/src/wasm-mmtp-container-parser.ts +23 -0
- package/test-vectors/align.json +199 -0
- package/test-vectors/catalog/README.md +25 -0
- package/test-vectors/catalog/negative/legacy-selection-params.json +15 -0
- package/test-vectors/catalog/negative/missing-mmtp-fields.json +12 -0
- package/test-vectors/catalog/negative/multicast-auth-padded-key.json +37 -0
- package/test-vectors/catalog/negative/multicast-auth-reused-track-format.json +53 -0
- package/test-vectors/catalog/negative/multicast-auth-trailing-pad-bits.json +37 -0
- package/test-vectors/catalog/negative/multicast-endpoint-missing-protocol-and-source.json +24 -0
- package/test-vectors/catalog/negative/network-source-object.json +25 -0
- package/test-vectors/catalog/negative/raptorq-unaligned-symbol.json +26 -0
- package/test-vectors/catalog/negative/repair-track-legacy-shape.json +18 -0
- package/test-vectors/catalog/negative/track-role-empty.json +16 -0
- package/test-vectors/catalog/negative/track-role-non-string.json +16 -0
- package/test-vectors/catalog/positive/fec-multicast.json +51 -0
- package/test-vectors/catalog/positive/flat-av.json +30 -0
- package/test-vectors/catalog/positive/multicast-auth-rotation.json +55 -0
- package/test-vectors/catalog/positive/ticks-and-mixed-timescales.json +39 -0
- package/test-vectors/catalog/positive/track-role.json +49 -0
- package/test-vectors/catalog/releases/moqcast-native-v16-v1/catalog.json +65 -0
- package/test-vectors/catalog/releases/moqcast-native-v16-v1.archive.sha256 +1 -0
- package/test-vectors/catalog-multicast-auth-v1.json +26 -0
- package/test-vectors/catalog-validation.json +148 -0
- package/test-vectors/catalog-validator-public-api.mjs +25 -0
- package/test-vectors/generate-source-auth-fixture.mjs +114 -0
- package/test-vectors/source-auth-v1.json +61 -0
- package/test-vectors/subsample-placement-v1.json +230 -0
|
@@ -0,0 +1,2668 @@
|
|
|
1
|
+
var __typeError = (msg) => {
|
|
2
|
+
throw TypeError(msg);
|
|
3
|
+
};
|
|
4
|
+
var __accessCheck = (obj, member, msg) => member.has(obj) || __typeError("Cannot " + msg);
|
|
5
|
+
var __privateGet = (obj, member, getter) => (__accessCheck(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj));
|
|
6
|
+
var __privateAdd = (obj, member, value) => member.has(obj) ? __typeError("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
|
|
7
|
+
var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "write to private field"), setter ? setter.call(obj, value) : member.set(obj, value), value);
|
|
8
|
+
var __privateMethod = (obj, member, method) => (__accessCheck(obj, member, "access private method"), method);
|
|
9
|
+
|
|
10
|
+
// ../mmt-alta-parse/dist/index.js
|
|
11
|
+
var MPU_HEADER_SIZE = 8;
|
|
12
|
+
var MPU_LENGTH_FIELD_SIZE = 2;
|
|
13
|
+
var MPU_HEADER_REMAINDER_SIZE = MPU_HEADER_SIZE - MPU_LENGTH_FIELD_SIZE;
|
|
14
|
+
var MMTP_EXT_TYPE_ALTA_AUTH = 61601;
|
|
15
|
+
var MMTP_EXT_TRAILER_HEADER_SIZE = 4;
|
|
16
|
+
|
|
17
|
+
// ../mmt-base/dist/mmtp.js
|
|
18
|
+
var MMTP_HEADER_SIZE = 12;
|
|
19
|
+
var MPU_HEADER_SIZE2 = 8;
|
|
20
|
+
var MFU_DU_HEADER_SIZE_TIMED = 14;
|
|
21
|
+
var MFU_DU_HEADER_SIZE_NONTIMED = 4;
|
|
22
|
+
var MPU_LENGTH_FIELD_SIZE2 = 2;
|
|
23
|
+
var MPU_HEADER_REMAINDER_SIZE2 = MPU_HEADER_SIZE2 - MPU_LENGTH_FIELD_SIZE2;
|
|
24
|
+
var SOURCE_FEC_PAYLOAD_ID_SIZE = 4;
|
|
25
|
+
var PACKET_TYPE_MPU = 0;
|
|
26
|
+
function parseMMTPHeader(buffer) {
|
|
27
|
+
if (buffer.length < MMTP_HEADER_SIZE) {
|
|
28
|
+
throw new Error(`MMTP packet too small: ${buffer.length} bytes`);
|
|
29
|
+
}
|
|
30
|
+
const view = new DataView(buffer.buffer, buffer.byteOffset, buffer.byteLength);
|
|
31
|
+
const version = buffer[0] >> 6 & 3;
|
|
32
|
+
if (version !== 0) {
|
|
33
|
+
throw new Error(`Unsupported MMTP version: ${version}`);
|
|
34
|
+
}
|
|
35
|
+
const fecType = buffer[0] >> 3 & 3;
|
|
36
|
+
const packetCounterFlag = (buffer[0] & 32) !== 0;
|
|
37
|
+
const extensionFlag = (buffer[0] & 2) !== 0;
|
|
38
|
+
const rapFlag = (buffer[0] & 1) === 1;
|
|
39
|
+
const packetType = buffer[1] & 63;
|
|
40
|
+
let offset = 2;
|
|
41
|
+
const packetId = view.getUint16(offset, false);
|
|
42
|
+
offset += 2;
|
|
43
|
+
const timestamp = BigInt(view.getUint32(offset, false));
|
|
44
|
+
offset += 4;
|
|
45
|
+
const sequenceNumber = view.getUint32(offset, false);
|
|
46
|
+
offset += 4;
|
|
47
|
+
let packetCounter;
|
|
48
|
+
if (packetCounterFlag) {
|
|
49
|
+
if (offset + 4 > buffer.length)
|
|
50
|
+
throw new Error("MMTP packet truncated in packet_counter");
|
|
51
|
+
packetCounter = view.getUint32(offset, false);
|
|
52
|
+
offset += 4;
|
|
53
|
+
}
|
|
54
|
+
let extension;
|
|
55
|
+
if (extensionFlag) {
|
|
56
|
+
if (offset + 4 > buffer.length)
|
|
57
|
+
throw new Error("MMTP packet truncated in header extension");
|
|
58
|
+
const type = view.getUint16(offset, false);
|
|
59
|
+
const length = view.getUint16(offset + 2, false);
|
|
60
|
+
offset += 4;
|
|
61
|
+
if (offset + length > buffer.length)
|
|
62
|
+
throw new Error("MMTP packet truncated in header extension value");
|
|
63
|
+
extension = { type, data: buffer.subarray(offset, offset + length) };
|
|
64
|
+
offset += length;
|
|
65
|
+
}
|
|
66
|
+
return {
|
|
67
|
+
version,
|
|
68
|
+
packetType,
|
|
69
|
+
packetId,
|
|
70
|
+
timestamp,
|
|
71
|
+
sequenceNumber,
|
|
72
|
+
payloadLength: buffer.length - offset,
|
|
73
|
+
fecType,
|
|
74
|
+
packetCounterFlag,
|
|
75
|
+
packetCounter,
|
|
76
|
+
extensionFlag,
|
|
77
|
+
extension,
|
|
78
|
+
headerLength: offset,
|
|
79
|
+
rapFlag
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
function parseMPUHeader(payload) {
|
|
83
|
+
if (payload.length < MPU_HEADER_SIZE2) {
|
|
84
|
+
throw new Error(`Payload too small for MPU header: ${payload.length} bytes`);
|
|
85
|
+
}
|
|
86
|
+
const view = new DataView(payload.buffer, payload.byteOffset, payload.byteLength);
|
|
87
|
+
const payloadLength = view.getUint16(0, false);
|
|
88
|
+
const flagsByte = payload[2];
|
|
89
|
+
const timedFlag = flagsByte >> 3 & 1;
|
|
90
|
+
const fragmentationIndicator = flagsByte >> 1 & 3;
|
|
91
|
+
const aggregationFlag = (flagsByte & 1) !== 0;
|
|
92
|
+
const fragmentCounter = payload[3];
|
|
93
|
+
const mpuSequenceNumber = view.getUint32(4, false);
|
|
94
|
+
return {
|
|
95
|
+
mpuSequenceNumber,
|
|
96
|
+
fragmentationIndicator,
|
|
97
|
+
fragmentCounter,
|
|
98
|
+
timedFlag,
|
|
99
|
+
aggregationFlag,
|
|
100
|
+
payloadLength
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
function parseTimedMfuDuHeader(payload, offset) {
|
|
104
|
+
if (offset < 0 || offset + MFU_DU_HEADER_SIZE_TIMED > payload.length)
|
|
105
|
+
return null;
|
|
106
|
+
const view = new DataView(payload.buffer, payload.byteOffset + offset, MFU_DU_HEADER_SIZE_TIMED);
|
|
107
|
+
return {
|
|
108
|
+
movieFragmentSequenceNumber: view.getUint32(0, false),
|
|
109
|
+
sampleNumber: view.getUint32(4, false),
|
|
110
|
+
offset: view.getUint32(8, false),
|
|
111
|
+
priority: payload[offset + 12],
|
|
112
|
+
dependencyCounter: payload[offset + 13]
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
function resolveMfuPayloadLayout(mpu, fragmentType, payload) {
|
|
116
|
+
if (mpu.payloadLength < MPU_HEADER_REMAINDER_SIZE2) {
|
|
117
|
+
return {
|
|
118
|
+
rejection: {
|
|
119
|
+
gate: "mpu-payload-length-below-header-remainder",
|
|
120
|
+
detail: `payload_length=${mpu.payloadLength} < ${MPU_HEADER_REMAINDER_SIZE2}`
|
|
121
|
+
}
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
const declaredEnd = MPU_LENGTH_FIELD_SIZE2 + mpu.payloadLength;
|
|
125
|
+
if (declaredEnd > payload.length) {
|
|
126
|
+
return {
|
|
127
|
+
rejection: {
|
|
128
|
+
gate: "mpu-declared-end-exceeds-payload",
|
|
129
|
+
detail: `payload_length=${mpu.payloadLength} implies end=${declaredEnd}, payload is ${payload.length} bytes`
|
|
130
|
+
}
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
const hasHeader = fragmentType === 2 && mpu.fragmentationIndicator <= 1;
|
|
134
|
+
const mfuHeaderSize = mpu.timedFlag ? MFU_DU_HEADER_SIZE_TIMED : MFU_DU_HEADER_SIZE_NONTIMED;
|
|
135
|
+
const mfuOffset = MPU_HEADER_SIZE2 + (hasHeader ? mfuHeaderSize : 0);
|
|
136
|
+
if (mfuOffset >= declaredEnd) {
|
|
137
|
+
return {
|
|
138
|
+
rejection: {
|
|
139
|
+
gate: "mfu-offset-at-or-past-declared-end",
|
|
140
|
+
detail: `mfuOffset=${mfuOffset} >= declaredEnd=${declaredEnd}`
|
|
141
|
+
}
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
return {
|
|
145
|
+
layout: {
|
|
146
|
+
hasMfuDuHeader: hasHeader,
|
|
147
|
+
mfuOffset,
|
|
148
|
+
dataEnd: declaredEnd
|
|
149
|
+
}
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
function isEmptyOrExactAltaTrailer(payload, start, end) {
|
|
153
|
+
if (start === end)
|
|
154
|
+
return true;
|
|
155
|
+
if (start < 0 || end > payload.length || start + MMTP_EXT_TRAILER_HEADER_SIZE > end) {
|
|
156
|
+
return false;
|
|
157
|
+
}
|
|
158
|
+
const view = new DataView(payload.buffer, payload.byteOffset + start, end - start);
|
|
159
|
+
if (view.getUint16(0, false) !== MMTP_EXT_TYPE_ALTA_AUTH)
|
|
160
|
+
return false;
|
|
161
|
+
const authLength = view.getUint16(2, false);
|
|
162
|
+
return start + MMTP_EXT_TRAILER_HEADER_SIZE + authLength === end;
|
|
163
|
+
}
|
|
164
|
+
function resolveDeclaredTrailers(payload, declaredEnd, fecType, options) {
|
|
165
|
+
if (declaredEnd > payload.length)
|
|
166
|
+
return null;
|
|
167
|
+
if (options?.skipFecId) {
|
|
168
|
+
return isEmptyOrExactAltaTrailer(payload, declaredEnd, payload.length) ? { hasFecId: false } : null;
|
|
169
|
+
}
|
|
170
|
+
if (fecType === 1) {
|
|
171
|
+
const fecStart = payload.length - SOURCE_FEC_PAYLOAD_ID_SIZE;
|
|
172
|
+
if (fecStart < declaredEnd || !isEmptyOrExactAltaTrailer(payload, declaredEnd, fecStart)) {
|
|
173
|
+
return null;
|
|
174
|
+
}
|
|
175
|
+
return { hasFecId: true };
|
|
176
|
+
}
|
|
177
|
+
if (fecType !== 0)
|
|
178
|
+
return null;
|
|
179
|
+
if (isEmptyOrExactAltaTrailer(payload, declaredEnd, payload.length)) {
|
|
180
|
+
return { hasFecId: false };
|
|
181
|
+
}
|
|
182
|
+
return null;
|
|
183
|
+
}
|
|
184
|
+
function planMfuExtraction(mmtpPacket, options) {
|
|
185
|
+
if (mmtpPacket.length < MMTP_HEADER_SIZE + MPU_HEADER_SIZE2) {
|
|
186
|
+
return {
|
|
187
|
+
rejection: {
|
|
188
|
+
gate: "packet-shorter-than-mmtp-plus-mpu-header",
|
|
189
|
+
detail: `${mmtpPacket.length} bytes < ${MMTP_HEADER_SIZE + MPU_HEADER_SIZE2}`
|
|
190
|
+
}
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
let header;
|
|
194
|
+
try {
|
|
195
|
+
header = parseMMTPHeader(mmtpPacket);
|
|
196
|
+
} catch (error) {
|
|
197
|
+
return {
|
|
198
|
+
rejection: {
|
|
199
|
+
gate: "mmtp-header-unparseable",
|
|
200
|
+
detail: error instanceof Error ? error.message : String(error)
|
|
201
|
+
}
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
if (header.packetType !== PACKET_TYPE_MPU) {
|
|
205
|
+
return {
|
|
206
|
+
rejection: {
|
|
207
|
+
gate: "not-an-mpu-packet",
|
|
208
|
+
detail: `packetType=0x${header.packetType.toString(16).padStart(2, "0")}`
|
|
209
|
+
}
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
const payload = mmtpPacket.subarray(header.headerLength);
|
|
213
|
+
if (payload.length < MPU_HEADER_SIZE2) {
|
|
214
|
+
return {
|
|
215
|
+
rejection: {
|
|
216
|
+
gate: "payload-shorter-than-mpu-header",
|
|
217
|
+
detail: `${payload.length} bytes < ${MPU_HEADER_SIZE2}`
|
|
218
|
+
}
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
const mpu = parseMPUHeader(payload);
|
|
222
|
+
if (mpu.aggregationFlag) {
|
|
223
|
+
return {
|
|
224
|
+
rejection: { gate: "aggregation-flag-set", detail: "strict profile requires A=0" }
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
const fragmentType = payload[2] >> 4 & 15;
|
|
228
|
+
const layoutResult = resolveMfuPayloadLayout(mpu, fragmentType, payload);
|
|
229
|
+
if ("rejection" in layoutResult)
|
|
230
|
+
return layoutResult;
|
|
231
|
+
const { layout } = layoutResult;
|
|
232
|
+
const trailerLayout = resolveDeclaredTrailers(payload, layout.dataEnd, header.fecType, options);
|
|
233
|
+
if (!trailerLayout) {
|
|
234
|
+
if (!options?.skipFecId && header.fecType === 1) {
|
|
235
|
+
const observed = payload.length - layout.dataEnd;
|
|
236
|
+
if (observed !== SOURCE_FEC_PAYLOAD_ID_SIZE) {
|
|
237
|
+
return {
|
|
238
|
+
rejection: {
|
|
239
|
+
gate: "source-fec-payload-id-width-mismatch",
|
|
240
|
+
detail: `producer wrote a ${observed}-byte Source FEC Payload ID, ISO/IEC 23008-1 \xA7C.5.2 requires ${SOURCE_FEC_PAYLOAD_ID_SIZE} (payload=${payload.length}, declaredEnd=${layout.dataEnd})`
|
|
241
|
+
}
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
return {
|
|
246
|
+
rejection: {
|
|
247
|
+
gate: "declared-trailers-unresolved",
|
|
248
|
+
detail: `trailer bytes [${layout.dataEnd}, ${payload.length}) are neither empty nor an exact ALTA trailer (fecType=${header.fecType})`
|
|
249
|
+
}
|
|
250
|
+
};
|
|
251
|
+
}
|
|
252
|
+
return { plan: { header, payload, mpu, fragmentType, layout, trailerLayout } };
|
|
253
|
+
}
|
|
254
|
+
function extractMFUPayload(mmtpPacket, options) {
|
|
255
|
+
const result = planMfuExtraction(mmtpPacket, options);
|
|
256
|
+
if ("rejection" in result)
|
|
257
|
+
return null;
|
|
258
|
+
const { header, payload, mpu, fragmentType, layout, trailerLayout } = result.plan;
|
|
259
|
+
const mfuDuHeaderPresent = layout.hasMfuDuHeader;
|
|
260
|
+
const mfuOffset = layout.mfuOffset;
|
|
261
|
+
const dataEnd = layout.dataEnd;
|
|
262
|
+
const mfuDuHeader = mfuDuHeaderPresent && mpu.timedFlag === 1 ? parseTimedMfuDuHeader(payload, MPU_HEADER_SIZE2) : null;
|
|
263
|
+
const itemId = mfuDuHeaderPresent && mpu.timedFlag === 0 ? new DataView(payload.buffer, payload.byteOffset + MPU_HEADER_SIZE2, 4).getUint32(0, false) : null;
|
|
264
|
+
const data = payload.subarray(mfuOffset, dataEnd);
|
|
265
|
+
const fecPayloadId = trailerLayout.hasFecId ? {
|
|
266
|
+
ssId: new DataView(mmtpPacket.buffer, mmtpPacket.byteOffset + mmtpPacket.length - SOURCE_FEC_PAYLOAD_ID_SIZE, SOURCE_FEC_PAYLOAD_ID_SIZE).getUint32(0, false)
|
|
267
|
+
} : null;
|
|
268
|
+
return { header, mpu, fragmentType, data, mfuDuHeader, itemId, fecPayloadId };
|
|
269
|
+
}
|
|
270
|
+
function isFecRepairPacket(mmtpPacket) {
|
|
271
|
+
if (mmtpPacket.length < 1)
|
|
272
|
+
return false;
|
|
273
|
+
const fecType = mmtpPacket[0] >> 3 & 3;
|
|
274
|
+
return fecType >= 2;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
// src/mfu-reassembler.ts
|
|
278
|
+
function foldDeliveryProvenance(verdict, source) {
|
|
279
|
+
if (verdict === "mixed") return "mixed";
|
|
280
|
+
if (source === "moq") return "mixed";
|
|
281
|
+
if (source === void 0) return "unknown";
|
|
282
|
+
return verdict;
|
|
283
|
+
}
|
|
284
|
+
function fragmentProvenance(fragment) {
|
|
285
|
+
if (fragment.deliveryProvenance !== void 0) return fragment.deliveryProvenance;
|
|
286
|
+
if (fragment.source === "moq") return "mixed";
|
|
287
|
+
if (fragment.source === "multicast") return "multicast";
|
|
288
|
+
return "unknown";
|
|
289
|
+
}
|
|
290
|
+
function deliveryProvenanceOf(fragments) {
|
|
291
|
+
if (fragments.length === 0) return "unknown";
|
|
292
|
+
let verdict = "multicast";
|
|
293
|
+
for (const fragment of fragments) {
|
|
294
|
+
const own = fragmentProvenance(fragment);
|
|
295
|
+
verdict = verdict === "mixed" || own === "mixed" ? "mixed" : verdict === "unknown" || own === "unknown" ? "unknown" : "multicast";
|
|
296
|
+
}
|
|
297
|
+
return verdict;
|
|
298
|
+
}
|
|
299
|
+
var MFU_REASSEMBLY_ERROR_CODES = [
|
|
300
|
+
"invalid-fragment-state",
|
|
301
|
+
"missing-data-unit-identity",
|
|
302
|
+
"missing-packet-sequence",
|
|
303
|
+
"conflicting-data-unit-identity",
|
|
304
|
+
"overlapping-data-unit",
|
|
305
|
+
"fragment-counter-mismatch",
|
|
306
|
+
"data-unit-identity-mismatch",
|
|
307
|
+
"offset-overflow",
|
|
308
|
+
"active-data-unit-limit",
|
|
309
|
+
"incomplete-terminal-fragment"
|
|
310
|
+
];
|
|
311
|
+
var mfuReassemblyErrorCodes = new Set(MFU_REASSEMBLY_ERROR_CODES);
|
|
312
|
+
function isMfuReassemblyErrorCode(value) {
|
|
313
|
+
return typeof value === "string" && mfuReassemblyErrorCodes.has(value);
|
|
314
|
+
}
|
|
315
|
+
var MfuReassemblyError = class extends Error {
|
|
316
|
+
constructor(code, message) {
|
|
317
|
+
super(message);
|
|
318
|
+
this.code = code;
|
|
319
|
+
this.name = "MfuReassemblyError";
|
|
320
|
+
}
|
|
321
|
+
};
|
|
322
|
+
function assembledPacketSequences(fragments) {
|
|
323
|
+
const packetSequenceNumbers = [];
|
|
324
|
+
const repairedPacketSequenceNumbers = [];
|
|
325
|
+
const seen = /* @__PURE__ */ new Set();
|
|
326
|
+
const repaired = /* @__PURE__ */ new Set();
|
|
327
|
+
for (const fragment of fragments) {
|
|
328
|
+
const sequence = fragment.packetSequenceNumber;
|
|
329
|
+
if (typeof sequence !== "number" || !Number.isSafeInteger(sequence) || sequence < 0 || sequence > 4294967295) {
|
|
330
|
+
return {};
|
|
331
|
+
}
|
|
332
|
+
if (!seen.has(sequence)) {
|
|
333
|
+
seen.add(sequence);
|
|
334
|
+
packetSequenceNumbers.push(sequence);
|
|
335
|
+
}
|
|
336
|
+
if (fragment.recovered === true && !repaired.has(sequence)) {
|
|
337
|
+
repaired.add(sequence);
|
|
338
|
+
repairedPacketSequenceNumbers.push(sequence);
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
return { packetSequenceNumbers, repairedPacketSequenceNumbers };
|
|
342
|
+
}
|
|
343
|
+
function streamScope(fragment) {
|
|
344
|
+
if (fragment.catalogTrackName !== void 0) return ["catalog", fragment.catalogTrackName];
|
|
345
|
+
if (fragment.packetId !== void 0) return ["packet", fragment.packetId];
|
|
346
|
+
return ["unspecified", null];
|
|
347
|
+
}
|
|
348
|
+
function isU32(value) {
|
|
349
|
+
return typeof value === "number" && Number.isInteger(value) && value >= 0 && value <= 4294967295;
|
|
350
|
+
}
|
|
351
|
+
function fragmentRunKey(fragment) {
|
|
352
|
+
if (!isU32(fragment.packetSequenceNumber)) {
|
|
353
|
+
throw new Error("[MFUReassembler] fragmented value has no validated packet sequence");
|
|
354
|
+
}
|
|
355
|
+
const terminalPacketSequence = (fragment.packetSequenceNumber + fragment.fragmentCounter) % 4294967296;
|
|
356
|
+
return JSON.stringify([
|
|
357
|
+
...streamScope(fragment),
|
|
358
|
+
fragment.mpuSequenceNumber,
|
|
359
|
+
terminalPacketSequence
|
|
360
|
+
]);
|
|
361
|
+
}
|
|
362
|
+
function hasAnySampleIdentityField(fragment) {
|
|
363
|
+
return fragment.movieFragmentSequenceNumber !== void 0 || fragment.sampleNumber !== void 0 || fragment.sampleOffset !== void 0;
|
|
364
|
+
}
|
|
365
|
+
function hasSampleIdentity(fragment) {
|
|
366
|
+
return isU32(fragment.movieFragmentSequenceNumber) && fragment.movieFragmentSequenceNumber > 0 && isU32(fragment.sampleNumber) && fragment.sampleNumber > 0 && isU32(fragment.sampleOffset);
|
|
367
|
+
}
|
|
368
|
+
function hasValidFragmentHeader(fragment) {
|
|
369
|
+
const { fragmentationIndicator: fi, fragmentCounter: counter } = fragment;
|
|
370
|
+
if (!Number.isInteger(fi) || fi < 0 || fi > 3) return false;
|
|
371
|
+
if (!Number.isInteger(counter) || counter < 0 || counter > 255) return false;
|
|
372
|
+
return fi === 0 || fi === 3 ? counter === 0 : counter > 0;
|
|
373
|
+
}
|
|
374
|
+
function fragmentCounterOrder(fragment) {
|
|
375
|
+
return 255 - fragment.fragmentCounter;
|
|
376
|
+
}
|
|
377
|
+
function objectKey(fragment) {
|
|
378
|
+
const scope = streamScope(fragment);
|
|
379
|
+
if (fragment.itemId !== void 0) {
|
|
380
|
+
return JSON.stringify([
|
|
381
|
+
...scope,
|
|
382
|
+
fragment.mpuSequenceNumber,
|
|
383
|
+
"item",
|
|
384
|
+
fragment.itemId
|
|
385
|
+
]);
|
|
386
|
+
}
|
|
387
|
+
if (hasSampleIdentity(fragment)) {
|
|
388
|
+
return JSON.stringify([
|
|
389
|
+
...scope,
|
|
390
|
+
fragment.mpuSequenceNumber,
|
|
391
|
+
"sample",
|
|
392
|
+
fragment.movieFragmentSequenceNumber,
|
|
393
|
+
fragment.sampleNumber,
|
|
394
|
+
// The DU's authored base offset (moqcast #37): sibling subsample DUs
|
|
395
|
+
// of one sample get distinct keys instead of colliding. Only ever
|
|
396
|
+
// called with the unmutated FI=1 (or FI=0) fragment, whose
|
|
397
|
+
// sampleOffset is that authored base -- never the accumulating
|
|
398
|
+
// per-continuation value written by #associateDataUnit.
|
|
399
|
+
fragment.sampleOffset
|
|
400
|
+
]);
|
|
401
|
+
}
|
|
402
|
+
return JSON.stringify([
|
|
403
|
+
...scope,
|
|
404
|
+
fragment.mpuSequenceNumber,
|
|
405
|
+
"unidentified"
|
|
406
|
+
]);
|
|
407
|
+
}
|
|
408
|
+
function ordersByOffset(fragments) {
|
|
409
|
+
return fragments.every((fragment) => hasSampleIdentity(fragment));
|
|
410
|
+
}
|
|
411
|
+
function checkedSampleEnd(offset, byteLength) {
|
|
412
|
+
const end = offset + byteLength;
|
|
413
|
+
return Number.isSafeInteger(end) && end <= 4294967295 ? end : null;
|
|
414
|
+
}
|
|
415
|
+
var _buffers, _timeouts, _activeDataUnits, _pendingContinuations, _emitted, _emittedOrder, _timeoutMs, _maxBuffered, _MFUReassembler_instances, addAssociatedFragment_fn, parkContinuation_fn, drainPendingContinuations_fn, incompleteRunCount_fn, reject_fn, associateDataUnit_fn, tryReassemble_fn, assemble_fn, setTimeout_fn, clearPendingTimeout_fn, deletePendingRun_fn, onPendingTimeout_fn, clearTimeout_fn, markEmitted_fn, orderValue_fn, removeActiveDataUnitForBuffer_fn, onTimeout_fn, pendingDebugSamples_fn;
|
|
416
|
+
var MFUReassembler = class {
|
|
417
|
+
constructor(options) {
|
|
418
|
+
__privateAdd(this, _MFUReassembler_instances);
|
|
419
|
+
// Fragment buffers use only the authored MFU DU identity. Continuations are
|
|
420
|
+
// admitted through bounded producer-profile runs keyed by physical stream,
|
|
421
|
+
// MPU, and the wrapping terminal packet sequence. This requires contiguous
|
|
422
|
+
// source-packet sequences within a fragmented DU; interleaving stays
|
|
423
|
+
// separated and incomplete. Continuations may wait in a counter-indexed queue
|
|
424
|
+
// until FI=1 supplies intrinsic identity; MMTP packet send time is never an
|
|
425
|
+
// association key.
|
|
426
|
+
__privateAdd(this, _buffers, /* @__PURE__ */ new Map());
|
|
427
|
+
__privateAdd(this, _timeouts, /* @__PURE__ */ new Map());
|
|
428
|
+
__privateAdd(this, _activeDataUnits, /* @__PURE__ */ new Map());
|
|
429
|
+
__privateAdd(this, _pendingContinuations, /* @__PURE__ */ new Map());
|
|
430
|
+
__privateAdd(this, _emitted, /* @__PURE__ */ new Set());
|
|
431
|
+
// same sample keys — reject FEC-recovered duplicates
|
|
432
|
+
__privateAdd(this, _emittedOrder, []);
|
|
433
|
+
__privateAdd(this, _timeoutMs);
|
|
434
|
+
__privateAdd(this, _maxBuffered);
|
|
435
|
+
/** Statistics for debugging */
|
|
436
|
+
this.stats = {
|
|
437
|
+
totalFragments: 0,
|
|
438
|
+
reassembledMFUs: 0,
|
|
439
|
+
rejectedFragments: 0,
|
|
440
|
+
timedOutMFUs: 0,
|
|
441
|
+
evictedMemoryLimit: 0,
|
|
442
|
+
outOfOrderFragments: 0
|
|
443
|
+
};
|
|
444
|
+
if (options.timeoutMs !== null && (!Number.isFinite(options.timeoutMs) || options.timeoutMs <= 0)) {
|
|
445
|
+
throw new Error("[MFUReassembler] timeoutMs must be > 0 (derive from frame duration or FEC config)");
|
|
446
|
+
}
|
|
447
|
+
const maxBuffered = options.maxBuffered ?? 100;
|
|
448
|
+
if (!Number.isInteger(maxBuffered) || maxBuffered <= 0) {
|
|
449
|
+
throw new Error("[MFUReassembler] maxBuffered must be a positive integer");
|
|
450
|
+
}
|
|
451
|
+
__privateSet(this, _timeoutMs, options.timeoutMs);
|
|
452
|
+
__privateSet(this, _maxBuffered, maxBuffered);
|
|
453
|
+
}
|
|
454
|
+
/** Update the expiry policy for all retained and future incomplete MFUs. */
|
|
455
|
+
set timeoutMs(ms) {
|
|
456
|
+
if (ms !== null && (!Number.isFinite(ms) || ms <= 0)) {
|
|
457
|
+
throw new Error("[MFUReassembler] timeoutMs must be > 0 (derive from frame duration or FEC config)");
|
|
458
|
+
}
|
|
459
|
+
__privateSet(this, _timeoutMs, ms);
|
|
460
|
+
const now = Date.now();
|
|
461
|
+
for (const [bufferKey, state] of __privateGet(this, _timeouts)) {
|
|
462
|
+
if (state.timeoutId !== void 0) clearTimeout(state.timeoutId);
|
|
463
|
+
delete state.timeoutId;
|
|
464
|
+
if (ms !== null) {
|
|
465
|
+
state.timeoutId = setTimeout(
|
|
466
|
+
() => __privateMethod(this, _MFUReassembler_instances, onTimeout_fn).call(this, bufferKey),
|
|
467
|
+
Math.max(0, state.startedAt + ms - now)
|
|
468
|
+
);
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
for (const [runKey, pending] of __privateGet(this, _pendingContinuations)) {
|
|
472
|
+
if (pending.timeoutId !== void 0) clearTimeout(pending.timeoutId);
|
|
473
|
+
delete pending.timeoutId;
|
|
474
|
+
if (ms !== null && !__privateGet(this, _activeDataUnits).has(runKey)) {
|
|
475
|
+
pending.timeoutId = setTimeout(
|
|
476
|
+
() => __privateMethod(this, _MFUReassembler_instances, onPendingTimeout_fn).call(this, runKey),
|
|
477
|
+
Math.max(0, pending.startedAt + ms - now)
|
|
478
|
+
);
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
get timeoutMs() {
|
|
483
|
+
return __privateGet(this, _timeoutMs);
|
|
484
|
+
}
|
|
485
|
+
/**
|
|
486
|
+
* Add a fragment and return reassembled MFU if complete
|
|
487
|
+
*/
|
|
488
|
+
addFragment(fragment) {
|
|
489
|
+
this.stats.totalFragments++;
|
|
490
|
+
if (!hasValidFragmentHeader(fragment)) {
|
|
491
|
+
__privateMethod(this, _MFUReassembler_instances, reject_fn).call(this, "invalid-fragment-state", "invalid MFU fragmentation indicator or counter");
|
|
492
|
+
}
|
|
493
|
+
if (fragment.itemId !== void 0 && hasAnySampleIdentityField(fragment)) {
|
|
494
|
+
__privateMethod(this, _MFUReassembler_instances, reject_fn).call(this, "conflicting-data-unit-identity", "MFU carries timed and non-timed DU identities");
|
|
495
|
+
}
|
|
496
|
+
const { mpuSequenceNumber, fragmentationIndicator } = fragment;
|
|
497
|
+
if (fragmentationIndicator === 0) {
|
|
498
|
+
const sampleIdentity = hasSampleIdentity(fragment);
|
|
499
|
+
const hasSampleFields = hasAnySampleIdentityField(fragment);
|
|
500
|
+
const hasItemIdentity = fragment.itemId !== void 0;
|
|
501
|
+
if (!sampleIdentity && !hasItemIdentity) {
|
|
502
|
+
__privateMethod(this, _MFUReassembler_instances, reject_fn).call(this, "missing-data-unit-identity", "complete MFU has no intrinsic DU identity");
|
|
503
|
+
}
|
|
504
|
+
if (hasSampleFields && !sampleIdentity) {
|
|
505
|
+
__privateMethod(this, _MFUReassembler_instances, reject_fn).call(this, "missing-data-unit-identity", "complete MFU has an incomplete sample identity");
|
|
506
|
+
}
|
|
507
|
+
if (hasItemIdentity && !isU32(fragment.itemId)) {
|
|
508
|
+
__privateMethod(this, _MFUReassembler_instances, reject_fn).call(this, "missing-data-unit-identity", "complete MFU has an invalid Item_ID");
|
|
509
|
+
}
|
|
510
|
+
this.stats.reassembledMFUs++;
|
|
511
|
+
return {
|
|
512
|
+
mpuSequenceNumber,
|
|
513
|
+
packetSequenceNumber: fragment.packetSequenceNumber,
|
|
514
|
+
...assembledPacketSequences([fragment]),
|
|
515
|
+
data: fragment.data,
|
|
516
|
+
timestamp: fragment.timestamp,
|
|
517
|
+
fragmentCount: 1,
|
|
518
|
+
rapFlag: fragment.rapFlag,
|
|
519
|
+
priority: fragment.priority,
|
|
520
|
+
sampleNumber: fragment.sampleNumber,
|
|
521
|
+
movieFragmentSequenceNumber: fragment.movieFragmentSequenceNumber,
|
|
522
|
+
sampleOffset: fragment.sampleOffset,
|
|
523
|
+
source: fragment.source,
|
|
524
|
+
deliveryProvenance: deliveryProvenanceOf([fragment]),
|
|
525
|
+
catalogTrackName: fragment.catalogTrackName
|
|
526
|
+
};
|
|
527
|
+
}
|
|
528
|
+
if (!isU32(fragment.packetSequenceNumber)) {
|
|
529
|
+
__privateMethod(this, _MFUReassembler_instances, reject_fn).call(this, "missing-packet-sequence", "fragmented MFU has no MMTP packet sequence number");
|
|
530
|
+
}
|
|
531
|
+
const runKey = fragmentRunKey(fragment);
|
|
532
|
+
if (fragmentationIndicator === 1 && (hasSampleIdentity(fragment) || isU32(fragment.itemId)) && __privateGet(this, _emitted).has(objectKey(fragment))) {
|
|
533
|
+
__privateMethod(this, _MFUReassembler_instances, deletePendingRun_fn).call(this, runKey);
|
|
534
|
+
return null;
|
|
535
|
+
}
|
|
536
|
+
if (fragmentationIndicator !== 1) {
|
|
537
|
+
const active = __privateGet(this, _activeDataUnits).get(runKey);
|
|
538
|
+
if (active !== void 0 && fragment.fragmentCounter > active.nextFragmentCounter) {
|
|
539
|
+
__privateMethod(this, _MFUReassembler_instances, reject_fn).call(this, "fragment-counter-mismatch", "MFU continuation is behind the authored fragment-counter countdown");
|
|
540
|
+
}
|
|
541
|
+
if (active === void 0 || fragment.fragmentCounter < active.nextFragmentCounter) {
|
|
542
|
+
__privateMethod(this, _MFUReassembler_instances, parkContinuation_fn).call(this, runKey, fragment);
|
|
543
|
+
return null;
|
|
544
|
+
}
|
|
545
|
+
} else if (__privateGet(this, _activeDataUnits).has(runKey)) {
|
|
546
|
+
__privateMethod(this, _MFUReassembler_instances, reject_fn).call(this, "overlapping-data-unit", "fragmented MFU overlaps an active data unit");
|
|
547
|
+
} else {
|
|
548
|
+
const pending = __privateGet(this, _pendingContinuations).get(runKey);
|
|
549
|
+
if (pending !== void 0 && [...pending.fragments.keys()].some(
|
|
550
|
+
(counter) => counter >= fragment.fragmentCounter
|
|
551
|
+
)) {
|
|
552
|
+
__privateMethod(this, _MFUReassembler_instances, reject_fn).call(this, "fragment-counter-mismatch", "queued continuation counter is not below the authored first-fragment counter");
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
const pendingStartedAt = fragmentationIndicator === 1 ? __privateGet(this, _pendingContinuations).get(runKey)?.startedAt : void 0;
|
|
556
|
+
const result = __privateMethod(this, _MFUReassembler_instances, addAssociatedFragment_fn).call(this, fragment, runKey, pendingStartedAt);
|
|
557
|
+
if (fragmentationIndicator === 1) __privateMethod(this, _MFUReassembler_instances, clearPendingTimeout_fn).call(this, runKey);
|
|
558
|
+
if (result !== null) return result;
|
|
559
|
+
return __privateMethod(this, _MFUReassembler_instances, drainPendingContinuations_fn).call(this, runKey);
|
|
560
|
+
}
|
|
561
|
+
/**
|
|
562
|
+
* Flush all buffered fragments
|
|
563
|
+
*/
|
|
564
|
+
flush() {
|
|
565
|
+
for (const key of __privateGet(this, _buffers).keys()) {
|
|
566
|
+
__privateMethod(this, _MFUReassembler_instances, clearTimeout_fn).call(this, key);
|
|
567
|
+
}
|
|
568
|
+
__privateGet(this, _buffers).clear();
|
|
569
|
+
__privateGet(this, _timeouts).clear();
|
|
570
|
+
__privateGet(this, _activeDataUnits).clear();
|
|
571
|
+
for (const runKey of __privateGet(this, _pendingContinuations).keys()) {
|
|
572
|
+
__privateMethod(this, _MFUReassembler_instances, clearPendingTimeout_fn).call(this, runKey);
|
|
573
|
+
}
|
|
574
|
+
__privateGet(this, _pendingContinuations).clear();
|
|
575
|
+
__privateGet(this, _emitted).clear();
|
|
576
|
+
__privateGet(this, _emittedOrder).length = 0;
|
|
577
|
+
}
|
|
578
|
+
/** Drop one catalog stream's incomplete and dedup state. */
|
|
579
|
+
resetStream(catalogTrackName) {
|
|
580
|
+
const belongsToStream = (key) => {
|
|
581
|
+
try {
|
|
582
|
+
const decoded = JSON.parse(key);
|
|
583
|
+
return Array.isArray(decoded) && decoded[0] === "catalog" && decoded[1] === catalogTrackName;
|
|
584
|
+
} catch {
|
|
585
|
+
return false;
|
|
586
|
+
}
|
|
587
|
+
};
|
|
588
|
+
const discarded = /* @__PURE__ */ new Set();
|
|
589
|
+
for (const [key, fragments] of __privateGet(this, _buffers)) {
|
|
590
|
+
if (fragments.some((fragment) => fragment.catalogTrackName === catalogTrackName)) {
|
|
591
|
+
discarded.add(key);
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
for (const [runKey, active] of __privateGet(this, _activeDataUnits)) {
|
|
595
|
+
if (active.catalogTrackName === catalogTrackName) {
|
|
596
|
+
discarded.add(active.bufferKey);
|
|
597
|
+
__privateGet(this, _activeDataUnits).delete(runKey);
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
for (const [runKey, pending] of __privateGet(this, _pendingContinuations)) {
|
|
601
|
+
if ([...pending.fragments.values()].some(
|
|
602
|
+
(fragment) => fragment.catalogTrackName === catalogTrackName
|
|
603
|
+
)) {
|
|
604
|
+
__privateMethod(this, _MFUReassembler_instances, deletePendingRun_fn).call(this, runKey);
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
for (const key of __privateGet(this, _emitted)) {
|
|
608
|
+
if (belongsToStream(key)) discarded.add(key);
|
|
609
|
+
}
|
|
610
|
+
for (const key of discarded) {
|
|
611
|
+
__privateMethod(this, _MFUReassembler_instances, clearTimeout_fn).call(this, key);
|
|
612
|
+
__privateGet(this, _buffers).delete(key);
|
|
613
|
+
__privateGet(this, _emitted).delete(key);
|
|
614
|
+
}
|
|
615
|
+
__privateSet(this, _emittedOrder, __privateGet(this, _emittedOrder).filter((key) => !discarded.has(key)));
|
|
616
|
+
}
|
|
617
|
+
/** Reset all state — flush buffers and zero stats */
|
|
618
|
+
reset() {
|
|
619
|
+
this.flush();
|
|
620
|
+
this.stats.totalFragments = 0;
|
|
621
|
+
this.stats.reassembledMFUs = 0;
|
|
622
|
+
this.stats.rejectedFragments = 0;
|
|
623
|
+
this.stats.timedOutMFUs = 0;
|
|
624
|
+
this.stats.evictedMemoryLimit = 0;
|
|
625
|
+
this.stats.outOfOrderFragments = 0;
|
|
626
|
+
}
|
|
627
|
+
/**
|
|
628
|
+
* Get number of pending MFUs
|
|
629
|
+
*/
|
|
630
|
+
get pendingCount() {
|
|
631
|
+
let count = __privateGet(this, _buffers).size;
|
|
632
|
+
for (const runKey of __privateGet(this, _pendingContinuations).keys()) {
|
|
633
|
+
if (!__privateGet(this, _activeDataUnits).has(runKey)) count++;
|
|
634
|
+
}
|
|
635
|
+
return count;
|
|
636
|
+
}
|
|
637
|
+
/**
|
|
638
|
+
* Get current buffer status
|
|
639
|
+
*/
|
|
640
|
+
getStatus() {
|
|
641
|
+
let pendingFragments = 0;
|
|
642
|
+
for (const fragments of __privateGet(this, _buffers).values()) {
|
|
643
|
+
pendingFragments += fragments.length;
|
|
644
|
+
}
|
|
645
|
+
for (const pending of __privateGet(this, _pendingContinuations).values()) {
|
|
646
|
+
pendingFragments += pending.fragments.size;
|
|
647
|
+
}
|
|
648
|
+
return {
|
|
649
|
+
bufferedMFUs: this.pendingCount,
|
|
650
|
+
pendingFragments,
|
|
651
|
+
pendingSamples: __privateMethod(this, _MFUReassembler_instances, pendingDebugSamples_fn).call(this),
|
|
652
|
+
stats: { ...this.stats }
|
|
653
|
+
};
|
|
654
|
+
}
|
|
655
|
+
};
|
|
656
|
+
_buffers = new WeakMap();
|
|
657
|
+
_timeouts = new WeakMap();
|
|
658
|
+
_activeDataUnits = new WeakMap();
|
|
659
|
+
_pendingContinuations = new WeakMap();
|
|
660
|
+
_emitted = new WeakMap();
|
|
661
|
+
_emittedOrder = new WeakMap();
|
|
662
|
+
_timeoutMs = new WeakMap();
|
|
663
|
+
_maxBuffered = new WeakMap();
|
|
664
|
+
_MFUReassembler_instances = new WeakSet();
|
|
665
|
+
addAssociatedFragment_fn = function(fragment, runKey, startedAt) {
|
|
666
|
+
const association = __privateMethod(this, _MFUReassembler_instances, associateDataUnit_fn).call(this, fragment, runKey);
|
|
667
|
+
const { bufferKey, first, terminal } = association;
|
|
668
|
+
let fragments = __privateGet(this, _buffers).get(bufferKey);
|
|
669
|
+
if (first) {
|
|
670
|
+
if (fragments !== void 0) {
|
|
671
|
+
__privateMethod(this, _MFUReassembler_instances, reject_fn).call(this, "overlapping-data-unit", "intrinsic data-unit buffer already exists");
|
|
672
|
+
}
|
|
673
|
+
fragments = [];
|
|
674
|
+
__privateGet(this, _buffers).set(bufferKey, fragments);
|
|
675
|
+
} else if (fragments === void 0) {
|
|
676
|
+
throw new Error("[MFUReassembler] active data unit has no fragment buffer");
|
|
677
|
+
}
|
|
678
|
+
if (first) __privateMethod(this, _MFUReassembler_instances, setTimeout_fn).call(this, bufferKey, startedAt);
|
|
679
|
+
fragments.push(fragment);
|
|
680
|
+
if (!terminal) return null;
|
|
681
|
+
const result = __privateMethod(this, _MFUReassembler_instances, tryReassemble_fn).call(this, bufferKey);
|
|
682
|
+
__privateGet(this, _activeDataUnits).delete(runKey);
|
|
683
|
+
if (result === null) {
|
|
684
|
+
__privateMethod(this, _MFUReassembler_instances, clearTimeout_fn).call(this, bufferKey);
|
|
685
|
+
__privateGet(this, _buffers).delete(bufferKey);
|
|
686
|
+
__privateMethod(this, _MFUReassembler_instances, reject_fn).call(this, "incomplete-terminal-fragment", "validated terminal fragment did not complete its data unit");
|
|
687
|
+
}
|
|
688
|
+
return result;
|
|
689
|
+
};
|
|
690
|
+
parkContinuation_fn = function(runKey, fragment) {
|
|
691
|
+
let pending = __privateGet(this, _pendingContinuations).get(runKey);
|
|
692
|
+
if (pending === void 0) {
|
|
693
|
+
const awaitingFirst = !__privateGet(this, _activeDataUnits).has(runKey);
|
|
694
|
+
if (awaitingFirst && __privateMethod(this, _MFUReassembler_instances, incompleteRunCount_fn).call(this) >= __privateGet(this, _maxBuffered)) {
|
|
695
|
+
__privateMethod(this, _MFUReassembler_instances, reject_fn).call(this, "active-data-unit-limit", "active MFU data-unit limit reached");
|
|
696
|
+
}
|
|
697
|
+
const startedAt = Date.now();
|
|
698
|
+
pending = { fragments: /* @__PURE__ */ new Map(), startedAt };
|
|
699
|
+
if (awaitingFirst && __privateGet(this, _timeoutMs) !== null) {
|
|
700
|
+
pending.timeoutId = setTimeout(() => __privateMethod(this, _MFUReassembler_instances, onPendingTimeout_fn).call(this, runKey), __privateGet(this, _timeoutMs));
|
|
701
|
+
}
|
|
702
|
+
__privateGet(this, _pendingContinuations).set(runKey, pending);
|
|
703
|
+
}
|
|
704
|
+
if (pending.fragments.has(fragment.fragmentCounter)) {
|
|
705
|
+
__privateMethod(this, _MFUReassembler_instances, reject_fn).call(this, "fragment-counter-mismatch", "duplicate queued continuation counter would replace retained media");
|
|
706
|
+
}
|
|
707
|
+
pending.fragments.set(fragment.fragmentCounter, fragment);
|
|
708
|
+
};
|
|
709
|
+
drainPendingContinuations_fn = function(runKey) {
|
|
710
|
+
while (true) {
|
|
711
|
+
const active = __privateGet(this, _activeDataUnits).get(runKey);
|
|
712
|
+
if (active === void 0) {
|
|
713
|
+
__privateMethod(this, _MFUReassembler_instances, deletePendingRun_fn).call(this, runKey);
|
|
714
|
+
return null;
|
|
715
|
+
}
|
|
716
|
+
const pending = __privateGet(this, _pendingContinuations).get(runKey);
|
|
717
|
+
const fragment = pending?.fragments.get(active.nextFragmentCounter);
|
|
718
|
+
if (fragment === void 0) return null;
|
|
719
|
+
pending.fragments.delete(active.nextFragmentCounter);
|
|
720
|
+
if (pending.fragments.size === 0) __privateMethod(this, _MFUReassembler_instances, deletePendingRun_fn).call(this, runKey);
|
|
721
|
+
const result = __privateMethod(this, _MFUReassembler_instances, addAssociatedFragment_fn).call(this, fragment, runKey);
|
|
722
|
+
if (result !== null) {
|
|
723
|
+
__privateMethod(this, _MFUReassembler_instances, deletePendingRun_fn).call(this, runKey);
|
|
724
|
+
return result;
|
|
725
|
+
}
|
|
726
|
+
}
|
|
727
|
+
};
|
|
728
|
+
incompleteRunCount_fn = function() {
|
|
729
|
+
let count = __privateGet(this, _activeDataUnits).size;
|
|
730
|
+
for (const runKey of __privateGet(this, _pendingContinuations).keys()) {
|
|
731
|
+
if (!__privateGet(this, _activeDataUnits).has(runKey)) count++;
|
|
732
|
+
}
|
|
733
|
+
return count;
|
|
734
|
+
};
|
|
735
|
+
reject_fn = function(code, message) {
|
|
736
|
+
this.stats.rejectedFragments++;
|
|
737
|
+
throw new MfuReassemblyError(code, message);
|
|
738
|
+
};
|
|
739
|
+
associateDataUnit_fn = function(fragment, runKey) {
|
|
740
|
+
const fi = fragment.fragmentationIndicator;
|
|
741
|
+
if (fi === 1) {
|
|
742
|
+
if (__privateGet(this, _activeDataUnits).has(runKey)) {
|
|
743
|
+
__privateMethod(this, _MFUReassembler_instances, reject_fn).call(this, "overlapping-data-unit", "fragmented MFU overlaps an active data unit");
|
|
744
|
+
}
|
|
745
|
+
if (!__privateGet(this, _pendingContinuations).has(runKey) && __privateMethod(this, _MFUReassembler_instances, incompleteRunCount_fn).call(this) >= __privateGet(this, _maxBuffered)) {
|
|
746
|
+
__privateMethod(this, _MFUReassembler_instances, reject_fn).call(this, "active-data-unit-limit", "active MFU data-unit limit reached");
|
|
747
|
+
}
|
|
748
|
+
const sampleIdentity = hasSampleIdentity(fragment);
|
|
749
|
+
const hasSampleFields = hasAnySampleIdentityField(fragment);
|
|
750
|
+
const hasItemIdentity = fragment.itemId !== void 0;
|
|
751
|
+
if (sampleIdentity && hasItemIdentity) {
|
|
752
|
+
__privateMethod(this, _MFUReassembler_instances, reject_fn).call(this, "conflicting-data-unit-identity", "fragmented MFU carries timed and non-timed DU identities");
|
|
753
|
+
}
|
|
754
|
+
if (!sampleIdentity && !hasItemIdentity) {
|
|
755
|
+
__privateMethod(this, _MFUReassembler_instances, reject_fn).call(this, "missing-data-unit-identity", "first fragment has no intrinsic DU identity");
|
|
756
|
+
}
|
|
757
|
+
if (hasSampleFields && !sampleIdentity) {
|
|
758
|
+
__privateMethod(this, _MFUReassembler_instances, reject_fn).call(this, "missing-data-unit-identity", "first fragment has an incomplete sample identity");
|
|
759
|
+
}
|
|
760
|
+
if (hasItemIdentity && !isU32(fragment.itemId)) {
|
|
761
|
+
__privateMethod(this, _MFUReassembler_instances, reject_fn).call(this, "missing-data-unit-identity", "first fragment has an invalid Item_ID");
|
|
762
|
+
}
|
|
763
|
+
const bufferKey = objectKey(fragment);
|
|
764
|
+
if (__privateGet(this, _buffers).has(bufferKey)) {
|
|
765
|
+
__privateMethod(this, _MFUReassembler_instances, reject_fn).call(this, "overlapping-data-unit", "intrinsic data-unit buffer already exists");
|
|
766
|
+
}
|
|
767
|
+
let active2;
|
|
768
|
+
if (sampleIdentity) {
|
|
769
|
+
const nextOffset = checkedSampleEnd(fragment.sampleOffset, fragment.data.byteLength);
|
|
770
|
+
if (nextOffset === null) __privateMethod(this, _MFUReassembler_instances, reject_fn).call(this, "offset-overflow", "sample byte offset overflow");
|
|
771
|
+
active2 = {
|
|
772
|
+
kind: "sample",
|
|
773
|
+
bufferKey,
|
|
774
|
+
nextFragmentCounter: fragment.fragmentCounter - 1,
|
|
775
|
+
movieFragmentSequenceNumber: fragment.movieFragmentSequenceNumber,
|
|
776
|
+
sampleNumber: fragment.sampleNumber,
|
|
777
|
+
nextOffset,
|
|
778
|
+
...fragment.catalogTrackName === void 0 ? {} : { catalogTrackName: fragment.catalogTrackName }
|
|
779
|
+
};
|
|
780
|
+
} else {
|
|
781
|
+
active2 = {
|
|
782
|
+
kind: "item",
|
|
783
|
+
bufferKey,
|
|
784
|
+
nextFragmentCounter: fragment.fragmentCounter - 1,
|
|
785
|
+
itemId: fragment.itemId,
|
|
786
|
+
...fragment.catalogTrackName === void 0 ? {} : { catalogTrackName: fragment.catalogTrackName }
|
|
787
|
+
};
|
|
788
|
+
}
|
|
789
|
+
__privateGet(this, _activeDataUnits).set(runKey, active2);
|
|
790
|
+
return { bufferKey, first: true, terminal: false };
|
|
791
|
+
}
|
|
792
|
+
const active = __privateGet(this, _activeDataUnits).get(runKey);
|
|
793
|
+
if (active === void 0) {
|
|
794
|
+
throw new Error("[MFUReassembler] continuation was not parked before association");
|
|
795
|
+
}
|
|
796
|
+
if (fragment.fragmentCounter !== active.nextFragmentCounter) {
|
|
797
|
+
__privateMethod(this, _MFUReassembler_instances, reject_fn).call(this, "fragment-counter-mismatch", "MFU continuation does not follow the authored fragment-counter countdown");
|
|
798
|
+
}
|
|
799
|
+
if (active.kind === "sample") {
|
|
800
|
+
if (fragment.itemId !== void 0) {
|
|
801
|
+
__privateMethod(this, _MFUReassembler_instances, reject_fn).call(this, "data-unit-identity-mismatch", "sample continuation carries an Item_ID");
|
|
802
|
+
}
|
|
803
|
+
if (hasAnySampleIdentityField(fragment) && (!hasSampleIdentity(fragment) || fragment.movieFragmentSequenceNumber !== active.movieFragmentSequenceNumber || fragment.sampleNumber !== active.sampleNumber || fragment.sampleOffset !== active.nextOffset)) {
|
|
804
|
+
__privateMethod(this, _MFUReassembler_instances, reject_fn).call(this, "data-unit-identity-mismatch", "sample continuation identity does not match");
|
|
805
|
+
}
|
|
806
|
+
const nextOffset = checkedSampleEnd(active.nextOffset, fragment.data.byteLength);
|
|
807
|
+
if (nextOffset === null) __privateMethod(this, _MFUReassembler_instances, reject_fn).call(this, "offset-overflow", "sample byte offset overflow");
|
|
808
|
+
fragment.movieFragmentSequenceNumber = active.movieFragmentSequenceNumber;
|
|
809
|
+
fragment.sampleNumber = active.sampleNumber;
|
|
810
|
+
fragment.sampleOffset = active.nextOffset;
|
|
811
|
+
if (fi === 2) {
|
|
812
|
+
active.nextOffset = nextOffset;
|
|
813
|
+
active.nextFragmentCounter = fragment.fragmentCounter - 1;
|
|
814
|
+
}
|
|
815
|
+
} else {
|
|
816
|
+
if (hasAnySampleIdentityField(fragment) || fragment.itemId !== void 0 && fragment.itemId !== active.itemId) {
|
|
817
|
+
__privateMethod(this, _MFUReassembler_instances, reject_fn).call(this, "data-unit-identity-mismatch", "Item_ID continuation identity does not match");
|
|
818
|
+
}
|
|
819
|
+
fragment.itemId = active.itemId;
|
|
820
|
+
if (fi === 2) active.nextFragmentCounter = fragment.fragmentCounter - 1;
|
|
821
|
+
}
|
|
822
|
+
return {
|
|
823
|
+
bufferKey: active.bufferKey,
|
|
824
|
+
first: false,
|
|
825
|
+
terminal: fi === 3
|
|
826
|
+
};
|
|
827
|
+
};
|
|
828
|
+
tryReassemble_fn = function(bufferKey) {
|
|
829
|
+
const fragments = __privateGet(this, _buffers).get(bufferKey);
|
|
830
|
+
if (!fragments || fragments.length === 0) {
|
|
831
|
+
return null;
|
|
832
|
+
}
|
|
833
|
+
const sorted = [...fragments].sort((a, b) => __privateMethod(this, _MFUReassembler_instances, orderValue_fn).call(this, a) - __privateMethod(this, _MFUReassembler_instances, orderValue_fn).call(this, b));
|
|
834
|
+
if (sorted[0].fragmentationIndicator !== 1 || sorted[sorted.length - 1].fragmentationIndicator !== 3) {
|
|
835
|
+
return null;
|
|
836
|
+
}
|
|
837
|
+
if (sorted.slice(1, -1).some((fragment) => fragment.fragmentationIndicator !== 2)) {
|
|
838
|
+
return null;
|
|
839
|
+
}
|
|
840
|
+
for (let i = 1; i < sorted.length; i++) {
|
|
841
|
+
if (__privateMethod(this, _MFUReassembler_instances, orderValue_fn).call(this, sorted[i]) !== __privateMethod(this, _MFUReassembler_instances, orderValue_fn).call(this, sorted[i - 1]) + 1) {
|
|
842
|
+
return null;
|
|
843
|
+
}
|
|
844
|
+
}
|
|
845
|
+
if (ordersByOffset(sorted)) {
|
|
846
|
+
for (let i = 1; i < sorted.length; i++) {
|
|
847
|
+
const previous = sorted[i - 1];
|
|
848
|
+
if (sorted[i].sampleOffset !== previous.sampleOffset + previous.data.byteLength) {
|
|
849
|
+
return null;
|
|
850
|
+
}
|
|
851
|
+
}
|
|
852
|
+
}
|
|
853
|
+
fragments.splice(0, fragments.length, ...sorted);
|
|
854
|
+
return __privateMethod(this, _MFUReassembler_instances, assemble_fn).call(this, bufferKey, fragments);
|
|
855
|
+
};
|
|
856
|
+
assemble_fn = function(bufferKey, fragments) {
|
|
857
|
+
const totalSize = fragments.reduce((sum, f) => sum + f.data.length, 0);
|
|
858
|
+
const data = new Uint8Array(totalSize);
|
|
859
|
+
let offset = 0;
|
|
860
|
+
for (const fragment of fragments) {
|
|
861
|
+
data.set(fragment.data, offset);
|
|
862
|
+
offset += fragment.data.length;
|
|
863
|
+
}
|
|
864
|
+
__privateMethod(this, _MFUReassembler_instances, clearTimeout_fn).call(this, bufferKey);
|
|
865
|
+
__privateGet(this, _buffers).delete(bufferKey);
|
|
866
|
+
__privateMethod(this, _MFUReassembler_instances, markEmitted_fn).call(this, bufferKey);
|
|
867
|
+
this.stats.reassembledMFUs++;
|
|
868
|
+
return {
|
|
869
|
+
mpuSequenceNumber: fragments[0].mpuSequenceNumber,
|
|
870
|
+
packetSequenceNumber: fragments[0].packetSequenceNumber,
|
|
871
|
+
...assembledPacketSequences(fragments),
|
|
872
|
+
data,
|
|
873
|
+
timestamp: fragments[0].timestamp,
|
|
874
|
+
fragmentCount: fragments.length,
|
|
875
|
+
rapFlag: fragments[0].rapFlag,
|
|
876
|
+
priority: fragments.find((f) => f.priority !== void 0)?.priority,
|
|
877
|
+
sampleNumber: fragments[0].sampleNumber,
|
|
878
|
+
movieFragmentSequenceNumber: fragments[0].movieFragmentSequenceNumber,
|
|
879
|
+
sampleOffset: fragments[0].sampleOffset,
|
|
880
|
+
source: fragments[0].source,
|
|
881
|
+
deliveryProvenance: deliveryProvenanceOf(fragments),
|
|
882
|
+
catalogTrackName: fragments[0].catalogTrackName
|
|
883
|
+
};
|
|
884
|
+
};
|
|
885
|
+
setTimeout_fn = function(bufferKey, startedAt = Date.now()) {
|
|
886
|
+
const state = { startedAt };
|
|
887
|
+
if (__privateGet(this, _timeoutMs) !== null) {
|
|
888
|
+
state.timeoutId = setTimeout(
|
|
889
|
+
() => __privateMethod(this, _MFUReassembler_instances, onTimeout_fn).call(this, bufferKey),
|
|
890
|
+
Math.max(0, startedAt + __privateGet(this, _timeoutMs) - Date.now())
|
|
891
|
+
);
|
|
892
|
+
}
|
|
893
|
+
__privateGet(this, _timeouts).set(bufferKey, state);
|
|
894
|
+
};
|
|
895
|
+
clearPendingTimeout_fn = function(runKey) {
|
|
896
|
+
const pending = __privateGet(this, _pendingContinuations).get(runKey);
|
|
897
|
+
if (pending?.timeoutId !== void 0) clearTimeout(pending.timeoutId);
|
|
898
|
+
if (pending !== void 0) {
|
|
899
|
+
delete pending.timeoutId;
|
|
900
|
+
}
|
|
901
|
+
};
|
|
902
|
+
deletePendingRun_fn = function(runKey) {
|
|
903
|
+
__privateMethod(this, _MFUReassembler_instances, clearPendingTimeout_fn).call(this, runKey);
|
|
904
|
+
__privateGet(this, _pendingContinuations).delete(runKey);
|
|
905
|
+
};
|
|
906
|
+
onPendingTimeout_fn = function(runKey) {
|
|
907
|
+
const pending = __privateGet(this, _pendingContinuations).get(runKey);
|
|
908
|
+
if (pending === void 0) return;
|
|
909
|
+
this.stats.timedOutMFUs++;
|
|
910
|
+
console.warn(
|
|
911
|
+
`[MfuReassembler] timeout: run=${runKey} fragments=${pending.fragments.size} (continuations awaiting first fragment, discarded)`
|
|
912
|
+
);
|
|
913
|
+
__privateGet(this, _pendingContinuations).delete(runKey);
|
|
914
|
+
};
|
|
915
|
+
clearTimeout_fn = function(bufferKey) {
|
|
916
|
+
const state = __privateGet(this, _timeouts).get(bufferKey);
|
|
917
|
+
if (state !== void 0) {
|
|
918
|
+
if (state.timeoutId !== void 0) clearTimeout(state.timeoutId);
|
|
919
|
+
__privateGet(this, _timeouts).delete(bufferKey);
|
|
920
|
+
}
|
|
921
|
+
};
|
|
922
|
+
markEmitted_fn = function(bufferKey) {
|
|
923
|
+
if (__privateGet(this, _emitted).has(bufferKey)) return;
|
|
924
|
+
__privateGet(this, _emitted).add(bufferKey);
|
|
925
|
+
__privateGet(this, _emittedOrder).push(bufferKey);
|
|
926
|
+
while (__privateGet(this, _emittedOrder).length > __privateGet(this, _maxBuffered)) {
|
|
927
|
+
const oldest = __privateGet(this, _emittedOrder).shift();
|
|
928
|
+
if (oldest !== void 0) __privateGet(this, _emitted).delete(oldest);
|
|
929
|
+
}
|
|
930
|
+
};
|
|
931
|
+
orderValue_fn = function(fragment) {
|
|
932
|
+
return fragmentCounterOrder(fragment);
|
|
933
|
+
};
|
|
934
|
+
removeActiveDataUnitForBuffer_fn = function(bufferKey) {
|
|
935
|
+
for (const [runKey, active] of __privateGet(this, _activeDataUnits)) {
|
|
936
|
+
if (active.bufferKey === bufferKey) {
|
|
937
|
+
__privateGet(this, _activeDataUnits).delete(runKey);
|
|
938
|
+
__privateMethod(this, _MFUReassembler_instances, deletePendingRun_fn).call(this, runKey);
|
|
939
|
+
}
|
|
940
|
+
}
|
|
941
|
+
};
|
|
942
|
+
onTimeout_fn = function(bufferKey) {
|
|
943
|
+
const fragments = __privateGet(this, _buffers).get(bufferKey);
|
|
944
|
+
if (!fragments || fragments.length === 0) {
|
|
945
|
+
__privateGet(this, _buffers).delete(bufferKey);
|
|
946
|
+
__privateGet(this, _timeouts).delete(bufferKey);
|
|
947
|
+
__privateMethod(this, _MFUReassembler_instances, removeActiveDataUnitForBuffer_fn).call(this, bufferKey);
|
|
948
|
+
this.stats.timedOutMFUs++;
|
|
949
|
+
console.warn(`[MfuReassembler] timeout: key=${bufferKey} fragments=0 (no data buffered)`);
|
|
950
|
+
return;
|
|
951
|
+
}
|
|
952
|
+
this.stats.timedOutMFUs++;
|
|
953
|
+
console.warn(
|
|
954
|
+
`[MfuReassembler] timeout: key=${bufferKey} fragments=${fragments.length} (partial, discarded)`
|
|
955
|
+
);
|
|
956
|
+
__privateGet(this, _buffers).delete(bufferKey);
|
|
957
|
+
__privateGet(this, _timeouts).delete(bufferKey);
|
|
958
|
+
__privateMethod(this, _MFUReassembler_instances, removeActiveDataUnitForBuffer_fn).call(this, bufferKey);
|
|
959
|
+
};
|
|
960
|
+
pendingDebugSamples_fn = function() {
|
|
961
|
+
const out = [];
|
|
962
|
+
for (const [key, fragments] of __privateGet(this, _buffers)) {
|
|
963
|
+
if (fragments.length === 0) continue;
|
|
964
|
+
const sorted = [...fragments].sort((a, b) => __privateMethod(this, _MFUReassembler_instances, orderValue_fn).call(this, a) - __privateMethod(this, _MFUReassembler_instances, orderValue_fn).call(this, b));
|
|
965
|
+
const fiCounts = [0, 0, 0, 0];
|
|
966
|
+
let minCounter = Infinity;
|
|
967
|
+
let maxCounter = -Infinity;
|
|
968
|
+
let minOffset = Infinity;
|
|
969
|
+
let maxOffset = -Infinity;
|
|
970
|
+
for (const fragment of sorted) {
|
|
971
|
+
fiCounts[fragment.fragmentationIndicator] = (fiCounts[fragment.fragmentationIndicator] ?? 0) + 1;
|
|
972
|
+
minCounter = Math.min(minCounter, fragment.fragmentCounter);
|
|
973
|
+
maxCounter = Math.max(maxCounter, fragment.fragmentCounter);
|
|
974
|
+
if (typeof fragment.sampleOffset === "number") {
|
|
975
|
+
minOffset = Math.min(minOffset, fragment.sampleOffset);
|
|
976
|
+
maxOffset = Math.max(maxOffset, fragment.sampleOffset);
|
|
977
|
+
}
|
|
978
|
+
}
|
|
979
|
+
const point = (fragment) => ({
|
|
980
|
+
fi: fragment.fragmentationIndicator,
|
|
981
|
+
counter: fragment.fragmentCounter,
|
|
982
|
+
order: __privateMethod(this, _MFUReassembler_instances, orderValue_fn).call(this, fragment),
|
|
983
|
+
...typeof fragment.packetSequenceNumber === "number" ? { seq: fragment.packetSequenceNumber } : {},
|
|
984
|
+
...typeof fragment.sampleOffset === "number" ? { offset: fragment.sampleOffset } : {}
|
|
985
|
+
});
|
|
986
|
+
const summary = {
|
|
987
|
+
key,
|
|
988
|
+
fragments: sorted.length,
|
|
989
|
+
fi0: fiCounts[0] ?? 0,
|
|
990
|
+
fi1: fiCounts[1] ?? 0,
|
|
991
|
+
fi2: fiCounts[2] ?? 0,
|
|
992
|
+
fi3: fiCounts[3] ?? 0,
|
|
993
|
+
firstFi: sorted[0].fragmentationIndicator,
|
|
994
|
+
lastFi: sorted[sorted.length - 1].fragmentationIndicator,
|
|
995
|
+
firstOrder: __privateMethod(this, _MFUReassembler_instances, orderValue_fn).call(this, sorted[0]),
|
|
996
|
+
lastOrder: __privateMethod(this, _MFUReassembler_instances, orderValue_fn).call(this, sorted[sorted.length - 1]),
|
|
997
|
+
minCounter,
|
|
998
|
+
maxCounter,
|
|
999
|
+
movieFragmentSequenceNumber: sorted.find((f) => f.movieFragmentSequenceNumber !== void 0)?.movieFragmentSequenceNumber,
|
|
1000
|
+
sampleNumber: sorted.find((f) => f.sampleNumber !== void 0)?.sampleNumber,
|
|
1001
|
+
mpuSequenceNumber: sorted[0].mpuSequenceNumber,
|
|
1002
|
+
sortedHead: sorted.slice(0, 6).map(point),
|
|
1003
|
+
sortedTail: sorted.slice(-6).map(point)
|
|
1004
|
+
};
|
|
1005
|
+
if (minOffset !== Infinity) summary.minOffset = minOffset;
|
|
1006
|
+
if (maxOffset !== -Infinity) summary.maxOffset = maxOffset;
|
|
1007
|
+
out.push(summary);
|
|
1008
|
+
if (out.length >= 12) break;
|
|
1009
|
+
}
|
|
1010
|
+
return out;
|
|
1011
|
+
};
|
|
1012
|
+
function createFragment(mmtpPacket) {
|
|
1013
|
+
const extraction = extractMFUPayload(mmtpPacket);
|
|
1014
|
+
if (!extraction) throw new Error("Unable to extract MFU payload from MMTP packet");
|
|
1015
|
+
const { header, mpu, data, mfuDuHeader, itemId } = extraction;
|
|
1016
|
+
return {
|
|
1017
|
+
fragmentationIndicator: mpu.fragmentationIndicator,
|
|
1018
|
+
mpuSequenceNumber: mpu.mpuSequenceNumber,
|
|
1019
|
+
fragmentCounter: mpu.fragmentCounter,
|
|
1020
|
+
data,
|
|
1021
|
+
timestamp: header.timestamp,
|
|
1022
|
+
rapFlag: header.rapFlag,
|
|
1023
|
+
sampleNumber: mfuDuHeader?.sampleNumber,
|
|
1024
|
+
movieFragmentSequenceNumber: mfuDuHeader?.movieFragmentSequenceNumber,
|
|
1025
|
+
sampleOffset: mfuDuHeader?.offset,
|
|
1026
|
+
priority: mfuDuHeader?.priority,
|
|
1027
|
+
itemId: itemId ?? void 0,
|
|
1028
|
+
packetSequenceNumber: header.sequenceNumber,
|
|
1029
|
+
packetId: header.packetId
|
|
1030
|
+
};
|
|
1031
|
+
}
|
|
1032
|
+
function processMMTPWithReassembly(mmtpPacket, reassembler) {
|
|
1033
|
+
if (isFecRepairPacket(mmtpPacket)) {
|
|
1034
|
+
return null;
|
|
1035
|
+
}
|
|
1036
|
+
const fragment = createFragment(mmtpPacket);
|
|
1037
|
+
return reassembler.addFragment(fragment);
|
|
1038
|
+
}
|
|
1039
|
+
if (void 0) {
|
|
1040
|
+
const { describe, test, expect, vi } = void 0;
|
|
1041
|
+
const expectRejection = (call, code) => {
|
|
1042
|
+
let rejection;
|
|
1043
|
+
try {
|
|
1044
|
+
call();
|
|
1045
|
+
} catch (error) {
|
|
1046
|
+
rejection = error;
|
|
1047
|
+
}
|
|
1048
|
+
expect(rejection).toBeInstanceOf(MfuReassemblyError);
|
|
1049
|
+
expect(rejection.code).toBe(code);
|
|
1050
|
+
};
|
|
1051
|
+
const testPacketSequence = (mpu, counter, run = 0) => (mpu * 1e3 + run - counter + 4294967296) % 4294967296;
|
|
1052
|
+
describe("MFUReassembler", () => {
|
|
1053
|
+
test("should return complete MFU for FI=0", () => {
|
|
1054
|
+
const reassembler = new MFUReassembler({ timeoutMs: 1e3 });
|
|
1055
|
+
const result = reassembler.addFragment({
|
|
1056
|
+
fragmentationIndicator: 0,
|
|
1057
|
+
mpuSequenceNumber: 1,
|
|
1058
|
+
fragmentCounter: 0,
|
|
1059
|
+
itemId: 1,
|
|
1060
|
+
data: new Uint8Array([1, 2, 3, 4]),
|
|
1061
|
+
timestamp: 1000n,
|
|
1062
|
+
rapFlag: true
|
|
1063
|
+
});
|
|
1064
|
+
expect(result).not.toBeNull();
|
|
1065
|
+
expect(result?.data).toEqual(new Uint8Array([1, 2, 3, 4]));
|
|
1066
|
+
expect(result?.fragmentCount).toBe(1);
|
|
1067
|
+
expect(result?.rapFlag).toBe(true);
|
|
1068
|
+
});
|
|
1069
|
+
test("rejects a complete MFU without a valid sample identity", () => {
|
|
1070
|
+
const reassembler = new MFUReassembler({ timeoutMs: 1e3 });
|
|
1071
|
+
const complete = (identity) => ({
|
|
1072
|
+
fragmentationIndicator: 0,
|
|
1073
|
+
mpuSequenceNumber: 1,
|
|
1074
|
+
fragmentCounter: 0,
|
|
1075
|
+
data: new Uint8Array([1]),
|
|
1076
|
+
timestamp: 1000n,
|
|
1077
|
+
rapFlag: true,
|
|
1078
|
+
...identity
|
|
1079
|
+
});
|
|
1080
|
+
expectRejection(
|
|
1081
|
+
() => reassembler.addFragment(complete({})),
|
|
1082
|
+
"missing-data-unit-identity"
|
|
1083
|
+
);
|
|
1084
|
+
expectRejection(
|
|
1085
|
+
() => reassembler.addFragment(complete({
|
|
1086
|
+
movieFragmentSequenceNumber: 2,
|
|
1087
|
+
sampleNumber: 1
|
|
1088
|
+
})),
|
|
1089
|
+
"missing-data-unit-identity"
|
|
1090
|
+
);
|
|
1091
|
+
});
|
|
1092
|
+
test("accepts a complete subsample MFU with a nonzero authored offset", () => {
|
|
1093
|
+
const reassembler = new MFUReassembler({ timeoutMs: 1e3 });
|
|
1094
|
+
const result = reassembler.addFragment({
|
|
1095
|
+
fragmentationIndicator: 0,
|
|
1096
|
+
mpuSequenceNumber: 1,
|
|
1097
|
+
fragmentCounter: 0,
|
|
1098
|
+
movieFragmentSequenceNumber: 2,
|
|
1099
|
+
sampleNumber: 1,
|
|
1100
|
+
sampleOffset: 4,
|
|
1101
|
+
data: new Uint8Array([1]),
|
|
1102
|
+
timestamp: 1000n,
|
|
1103
|
+
rapFlag: true
|
|
1104
|
+
});
|
|
1105
|
+
expect(result).not.toBeNull();
|
|
1106
|
+
expect(result?.sampleOffset).toBe(4);
|
|
1107
|
+
});
|
|
1108
|
+
test("accepts a fragmented subsample DU whose first fragment has a nonzero authored offset", () => {
|
|
1109
|
+
const reassembler = new MFUReassembler({ timeoutMs: 1e3 });
|
|
1110
|
+
expect(reassembler.addFragment({
|
|
1111
|
+
fragmentationIndicator: 1,
|
|
1112
|
+
mpuSequenceNumber: 2,
|
|
1113
|
+
fragmentCounter: 1,
|
|
1114
|
+
movieFragmentSequenceNumber: 2,
|
|
1115
|
+
sampleNumber: 1,
|
|
1116
|
+
sampleOffset: 4,
|
|
1117
|
+
packetSequenceNumber: testPacketSequence(2, 1),
|
|
1118
|
+
data: new Uint8Array([161]),
|
|
1119
|
+
timestamp: 1000n,
|
|
1120
|
+
rapFlag: true
|
|
1121
|
+
})).toBeNull();
|
|
1122
|
+
expect(reassembler.pendingCount).toBe(1);
|
|
1123
|
+
expect(reassembler.getStatus().pendingFragments).toBe(1);
|
|
1124
|
+
const out = reassembler.addFragment({
|
|
1125
|
+
fragmentationIndicator: 3,
|
|
1126
|
+
mpuSequenceNumber: 2,
|
|
1127
|
+
fragmentCounter: 0,
|
|
1128
|
+
data: new Uint8Array([162]),
|
|
1129
|
+
timestamp: 1001n,
|
|
1130
|
+
rapFlag: false,
|
|
1131
|
+
packetSequenceNumber: testPacketSequence(2, 0)
|
|
1132
|
+
});
|
|
1133
|
+
expect(out?.data).toEqual(new Uint8Array([161, 162]));
|
|
1134
|
+
expect(out?.sampleOffset).toBe(4);
|
|
1135
|
+
});
|
|
1136
|
+
test("sibling subsample DUs of one sample reassemble independently by offset", () => {
|
|
1137
|
+
const reassembler = new MFUReassembler({ timeoutMs: 1e3 });
|
|
1138
|
+
expect(reassembler.addFragment({
|
|
1139
|
+
mpuSequenceNumber: 3,
|
|
1140
|
+
movieFragmentSequenceNumber: 5,
|
|
1141
|
+
sampleNumber: 9,
|
|
1142
|
+
fragmentationIndicator: 1,
|
|
1143
|
+
fragmentCounter: 1,
|
|
1144
|
+
sampleOffset: 0,
|
|
1145
|
+
data: new Uint8Array([161]),
|
|
1146
|
+
timestamp: 2000n,
|
|
1147
|
+
rapFlag: true,
|
|
1148
|
+
packetSequenceNumber: testPacketSequence(3, 1, 0)
|
|
1149
|
+
})).toBeNull();
|
|
1150
|
+
expect(reassembler.addFragment({
|
|
1151
|
+
mpuSequenceNumber: 3,
|
|
1152
|
+
movieFragmentSequenceNumber: 5,
|
|
1153
|
+
sampleNumber: 9,
|
|
1154
|
+
fragmentationIndicator: 1,
|
|
1155
|
+
fragmentCounter: 1,
|
|
1156
|
+
sampleOffset: 3,
|
|
1157
|
+
data: new Uint8Array([177]),
|
|
1158
|
+
timestamp: 2001n,
|
|
1159
|
+
rapFlag: false,
|
|
1160
|
+
packetSequenceNumber: testPacketSequence(3, 1, 1)
|
|
1161
|
+
})).toBeNull();
|
|
1162
|
+
expect(reassembler.pendingCount).toBe(2);
|
|
1163
|
+
const first = reassembler.addFragment({
|
|
1164
|
+
mpuSequenceNumber: 3,
|
|
1165
|
+
fragmentationIndicator: 3,
|
|
1166
|
+
fragmentCounter: 0,
|
|
1167
|
+
data: new Uint8Array([162]),
|
|
1168
|
+
timestamp: 2000n,
|
|
1169
|
+
rapFlag: false,
|
|
1170
|
+
packetSequenceNumber: testPacketSequence(3, 0, 0)
|
|
1171
|
+
});
|
|
1172
|
+
expect(first?.data).toEqual(new Uint8Array([161, 162]));
|
|
1173
|
+
expect(first?.sampleOffset).toBe(0);
|
|
1174
|
+
const second = reassembler.addFragment({
|
|
1175
|
+
mpuSequenceNumber: 3,
|
|
1176
|
+
fragmentationIndicator: 3,
|
|
1177
|
+
fragmentCounter: 0,
|
|
1178
|
+
data: new Uint8Array([178]),
|
|
1179
|
+
timestamp: 2001n,
|
|
1180
|
+
rapFlag: false,
|
|
1181
|
+
packetSequenceNumber: testPacketSequence(3, 0, 1)
|
|
1182
|
+
});
|
|
1183
|
+
expect(second?.data).toEqual(new Uint8Array([177, 178]));
|
|
1184
|
+
expect(second?.sampleOffset).toBe(3);
|
|
1185
|
+
expect(reassembler.pendingCount).toBe(0);
|
|
1186
|
+
});
|
|
1187
|
+
test("isolates colliding fragment coordinates by catalog track identity", () => {
|
|
1188
|
+
const reassembler = new MFUReassembler({ timeoutMs: 1e3 });
|
|
1189
|
+
const fragment = (catalogTrackName, fragmentationIndicator, fragmentCounter, byte) => ({
|
|
1190
|
+
itemId: 1,
|
|
1191
|
+
fragmentationIndicator,
|
|
1192
|
+
fragmentCounter,
|
|
1193
|
+
mpuSequenceNumber: 1,
|
|
1194
|
+
data: new Uint8Array([byte]),
|
|
1195
|
+
timestamp: 1000n,
|
|
1196
|
+
rapFlag: fragmentationIndicator === 1,
|
|
1197
|
+
catalogTrackName,
|
|
1198
|
+
packetSequenceNumber: testPacketSequence(1, fragmentCounter)
|
|
1199
|
+
});
|
|
1200
|
+
expect(reassembler.addFragment(fragment("video/720p", 1, 1, 113))).toBeNull();
|
|
1201
|
+
expect(reassembler.addFragment(fragment("video/540p", 1, 1, 81))).toBeNull();
|
|
1202
|
+
const high = reassembler.addFragment(fragment("video/720p", 3, 0, 114));
|
|
1203
|
+
const low = reassembler.addFragment(fragment("video/540p", 3, 0, 82));
|
|
1204
|
+
expect(high?.data).toEqual(new Uint8Array([113, 114]));
|
|
1205
|
+
expect(high?.catalogTrackName).toBe("video/720p");
|
|
1206
|
+
expect(low?.data).toEqual(new Uint8Array([81, 82]));
|
|
1207
|
+
expect(low?.catalogTrackName).toBe("video/540p");
|
|
1208
|
+
});
|
|
1209
|
+
test("rejects an overlapping non-timed item without replacing the active run", () => {
|
|
1210
|
+
const reassembler = new MFUReassembler({ timeoutMs: 1e3 });
|
|
1211
|
+
const fragment = (itemId, fragmentationIndicator, fragmentCounter, byte) => ({
|
|
1212
|
+
itemId,
|
|
1213
|
+
fragmentationIndicator,
|
|
1214
|
+
fragmentCounter,
|
|
1215
|
+
mpuSequenceNumber: 1,
|
|
1216
|
+
data: new Uint8Array([byte]),
|
|
1217
|
+
timestamp: 1000n,
|
|
1218
|
+
rapFlag: false,
|
|
1219
|
+
packetSequenceNumber: testPacketSequence(1, fragmentCounter)
|
|
1220
|
+
});
|
|
1221
|
+
expect(reassembler.addFragment(fragment(10, 1, 1, 16))).toBeNull();
|
|
1222
|
+
expectRejection(
|
|
1223
|
+
() => reassembler.addFragment(fragment(20, 1, 1, 32)),
|
|
1224
|
+
"overlapping-data-unit"
|
|
1225
|
+
);
|
|
1226
|
+
expect(reassembler.pendingCount).toBe(1);
|
|
1227
|
+
expect(reassembler.addFragment(fragment(10, 3, 0, 17))?.data).toEqual(new Uint8Array([16, 17]));
|
|
1228
|
+
expect(reassembler.pendingCount).toBe(0);
|
|
1229
|
+
});
|
|
1230
|
+
test("associates continuations by authored state when packet timestamps change", () => {
|
|
1231
|
+
const reassembler = new MFUReassembler({ timeoutMs: 1e3 });
|
|
1232
|
+
const fragment = (itemId, fragmentationIndicator, fragmentCounter, byte, timestamp) => ({
|
|
1233
|
+
...itemId === void 0 ? {} : { itemId },
|
|
1234
|
+
fragmentationIndicator,
|
|
1235
|
+
fragmentCounter,
|
|
1236
|
+
mpuSequenceNumber: 1,
|
|
1237
|
+
data: new Uint8Array([byte]),
|
|
1238
|
+
timestamp,
|
|
1239
|
+
rapFlag: false,
|
|
1240
|
+
packetSequenceNumber: testPacketSequence(1, fragmentCounter)
|
|
1241
|
+
});
|
|
1242
|
+
expect(reassembler.addFragment(fragment(10, 1, 2, 16, 1000n))).toBeNull();
|
|
1243
|
+
expect(reassembler.addFragment(fragment(void 0, 2, 1, 17, 1001n))).toBeNull();
|
|
1244
|
+
const out = reassembler.addFragment(fragment(void 0, 3, 0, 18, 1002n));
|
|
1245
|
+
expect(out?.data).toEqual(new Uint8Array([16, 17, 18]));
|
|
1246
|
+
expect(out?.timestamp).toBe(1000n);
|
|
1247
|
+
});
|
|
1248
|
+
test("should buffer and reassemble 2 fragments", () => {
|
|
1249
|
+
const reassembler = new MFUReassembler({ timeoutMs: 1e3 });
|
|
1250
|
+
const result1 = reassembler.addFragment({
|
|
1251
|
+
fragmentationIndicator: 1,
|
|
1252
|
+
mpuSequenceNumber: 1,
|
|
1253
|
+
fragmentCounter: 1,
|
|
1254
|
+
itemId: 1,
|
|
1255
|
+
data: new Uint8Array([1, 2]),
|
|
1256
|
+
timestamp: 1000n,
|
|
1257
|
+
rapFlag: true,
|
|
1258
|
+
packetSequenceNumber: testPacketSequence(1, 1)
|
|
1259
|
+
});
|
|
1260
|
+
expect(result1).toBeNull();
|
|
1261
|
+
const result2 = reassembler.addFragment({
|
|
1262
|
+
fragmentationIndicator: 3,
|
|
1263
|
+
mpuSequenceNumber: 1,
|
|
1264
|
+
fragmentCounter: 0,
|
|
1265
|
+
data: new Uint8Array([3, 4]),
|
|
1266
|
+
timestamp: 1000n,
|
|
1267
|
+
rapFlag: false,
|
|
1268
|
+
packetSequenceNumber: testPacketSequence(1, 0)
|
|
1269
|
+
});
|
|
1270
|
+
expect(result2).not.toBeNull();
|
|
1271
|
+
expect(result2?.data).toEqual(new Uint8Array([1, 2, 3, 4]));
|
|
1272
|
+
expect(result2?.fragmentCount).toBe(2);
|
|
1273
|
+
expect(result2?.rapFlag).toBe(true);
|
|
1274
|
+
});
|
|
1275
|
+
test("buffers a terminal continuation until its authored first fragment arrives", () => {
|
|
1276
|
+
const reassembler = new MFUReassembler({ timeoutMs: 1e3 });
|
|
1277
|
+
expect(reassembler.addFragment({
|
|
1278
|
+
fragmentationIndicator: 3,
|
|
1279
|
+
mpuSequenceNumber: 1,
|
|
1280
|
+
fragmentCounter: 0,
|
|
1281
|
+
data: new Uint8Array([3, 4]),
|
|
1282
|
+
timestamp: 1000n,
|
|
1283
|
+
rapFlag: false,
|
|
1284
|
+
packetSequenceNumber: testPacketSequence(1, 0)
|
|
1285
|
+
})).toBeNull();
|
|
1286
|
+
expect(reassembler.pendingCount).toBe(1);
|
|
1287
|
+
const completed = reassembler.addFragment({
|
|
1288
|
+
fragmentationIndicator: 1,
|
|
1289
|
+
mpuSequenceNumber: 1,
|
|
1290
|
+
fragmentCounter: 1,
|
|
1291
|
+
itemId: 1,
|
|
1292
|
+
data: new Uint8Array([1, 2]),
|
|
1293
|
+
timestamp: 1000n,
|
|
1294
|
+
rapFlag: true,
|
|
1295
|
+
packetSequenceNumber: testPacketSequence(1, 1)
|
|
1296
|
+
});
|
|
1297
|
+
expect(completed?.data).toEqual(new Uint8Array([1, 2, 3, 4]));
|
|
1298
|
+
expect(completed?.rapFlag).toBe(true);
|
|
1299
|
+
expect(reassembler.pendingCount).toBe(0);
|
|
1300
|
+
});
|
|
1301
|
+
test("clock-free mode keeps an admitted MFU until completion", async () => {
|
|
1302
|
+
const reassembler = new MFUReassembler({ timeoutMs: null });
|
|
1303
|
+
reassembler.addFragment({
|
|
1304
|
+
fragmentationIndicator: 1,
|
|
1305
|
+
mpuSequenceNumber: 1,
|
|
1306
|
+
fragmentCounter: 1,
|
|
1307
|
+
itemId: 1,
|
|
1308
|
+
data: new Uint8Array([1, 2]),
|
|
1309
|
+
timestamp: 1000n,
|
|
1310
|
+
rapFlag: true,
|
|
1311
|
+
packetSequenceNumber: testPacketSequence(1, 1)
|
|
1312
|
+
});
|
|
1313
|
+
await new Promise((resolve) => setTimeout(resolve, 5));
|
|
1314
|
+
const result = reassembler.addFragment({
|
|
1315
|
+
fragmentationIndicator: 3,
|
|
1316
|
+
mpuSequenceNumber: 1,
|
|
1317
|
+
fragmentCounter: 0,
|
|
1318
|
+
data: new Uint8Array([3, 4]),
|
|
1319
|
+
timestamp: 1000n,
|
|
1320
|
+
rapFlag: false,
|
|
1321
|
+
packetSequenceNumber: testPacketSequence(1, 0)
|
|
1322
|
+
});
|
|
1323
|
+
expect(result).not.toBeNull();
|
|
1324
|
+
expect(result?.data).toEqual(new Uint8Array([1, 2, 3, 4]));
|
|
1325
|
+
expect(reassembler.stats.timedOutMFUs).toBe(0);
|
|
1326
|
+
});
|
|
1327
|
+
test("should track pending count", () => {
|
|
1328
|
+
const reassembler = new MFUReassembler({ timeoutMs: 1e3 });
|
|
1329
|
+
expect(reassembler.pendingCount).toBe(0);
|
|
1330
|
+
reassembler.addFragment({
|
|
1331
|
+
fragmentationIndicator: 1,
|
|
1332
|
+
mpuSequenceNumber: 1,
|
|
1333
|
+
fragmentCounter: 1,
|
|
1334
|
+
itemId: 1,
|
|
1335
|
+
data: new Uint8Array([1]),
|
|
1336
|
+
timestamp: 1000n,
|
|
1337
|
+
rapFlag: false,
|
|
1338
|
+
packetSequenceNumber: testPacketSequence(1, 1)
|
|
1339
|
+
});
|
|
1340
|
+
expect(reassembler.pendingCount).toBe(1);
|
|
1341
|
+
reassembler.flush();
|
|
1342
|
+
expect(reassembler.pendingCount).toBe(0);
|
|
1343
|
+
});
|
|
1344
|
+
test("should reassemble 3 fragments (FI=1, FI=2, FI=3)", () => {
|
|
1345
|
+
const reassembler = new MFUReassembler({ timeoutMs: 1e3 });
|
|
1346
|
+
expect(reassembler.addFragment({
|
|
1347
|
+
fragmentationIndicator: 1,
|
|
1348
|
+
mpuSequenceNumber: 10,
|
|
1349
|
+
fragmentCounter: 2,
|
|
1350
|
+
itemId: 1,
|
|
1351
|
+
data: new Uint8Array([170, 187]),
|
|
1352
|
+
timestamp: 5000n,
|
|
1353
|
+
rapFlag: true,
|
|
1354
|
+
packetSequenceNumber: testPacketSequence(10, 2)
|
|
1355
|
+
})).toBeNull();
|
|
1356
|
+
expect(reassembler.addFragment({
|
|
1357
|
+
fragmentationIndicator: 2,
|
|
1358
|
+
mpuSequenceNumber: 10,
|
|
1359
|
+
fragmentCounter: 1,
|
|
1360
|
+
data: new Uint8Array([204, 221]),
|
|
1361
|
+
timestamp: 5000n,
|
|
1362
|
+
rapFlag: false,
|
|
1363
|
+
packetSequenceNumber: testPacketSequence(10, 1)
|
|
1364
|
+
})).toBeNull();
|
|
1365
|
+
const result = reassembler.addFragment({
|
|
1366
|
+
fragmentationIndicator: 3,
|
|
1367
|
+
mpuSequenceNumber: 10,
|
|
1368
|
+
fragmentCounter: 0,
|
|
1369
|
+
data: new Uint8Array([238, 255]),
|
|
1370
|
+
timestamp: 5000n,
|
|
1371
|
+
rapFlag: false,
|
|
1372
|
+
packetSequenceNumber: testPacketSequence(10, 0)
|
|
1373
|
+
});
|
|
1374
|
+
expect(result).not.toBeNull();
|
|
1375
|
+
expect(result.data).toEqual(new Uint8Array([170, 187, 204, 221, 238, 255]));
|
|
1376
|
+
expect(result.fragmentCount).toBe(3);
|
|
1377
|
+
expect(result.rapFlag).toBe(true);
|
|
1378
|
+
expect(result.mpuSequenceNumber).toBe(10);
|
|
1379
|
+
});
|
|
1380
|
+
test("should handle interleaved MPU sequences", () => {
|
|
1381
|
+
const reassembler = new MFUReassembler({ timeoutMs: 1e3 });
|
|
1382
|
+
reassembler.addFragment({
|
|
1383
|
+
fragmentationIndicator: 1,
|
|
1384
|
+
mpuSequenceNumber: 1,
|
|
1385
|
+
fragmentCounter: 1,
|
|
1386
|
+
itemId: 1,
|
|
1387
|
+
data: new Uint8Array([1]),
|
|
1388
|
+
timestamp: 100n,
|
|
1389
|
+
rapFlag: false,
|
|
1390
|
+
packetSequenceNumber: testPacketSequence(1, 1)
|
|
1391
|
+
});
|
|
1392
|
+
const r2 = reassembler.addFragment({
|
|
1393
|
+
fragmentationIndicator: 0,
|
|
1394
|
+
mpuSequenceNumber: 2,
|
|
1395
|
+
fragmentCounter: 0,
|
|
1396
|
+
itemId: 2,
|
|
1397
|
+
data: new Uint8Array([2]),
|
|
1398
|
+
timestamp: 200n,
|
|
1399
|
+
rapFlag: false
|
|
1400
|
+
});
|
|
1401
|
+
expect(r2).not.toBeNull();
|
|
1402
|
+
expect(r2.mpuSequenceNumber).toBe(2);
|
|
1403
|
+
const r1 = reassembler.addFragment({
|
|
1404
|
+
fragmentationIndicator: 3,
|
|
1405
|
+
mpuSequenceNumber: 1,
|
|
1406
|
+
fragmentCounter: 0,
|
|
1407
|
+
data: new Uint8Array([3]),
|
|
1408
|
+
timestamp: 100n,
|
|
1409
|
+
rapFlag: false,
|
|
1410
|
+
packetSequenceNumber: testPacketSequence(1, 0)
|
|
1411
|
+
});
|
|
1412
|
+
expect(r1).not.toBeNull();
|
|
1413
|
+
expect(r1.data).toEqual(new Uint8Array([1, 3]));
|
|
1414
|
+
});
|
|
1415
|
+
test("rejects overlapping fragmented MFUs sharing one stream and MPU", () => {
|
|
1416
|
+
const reassembler = new MFUReassembler({ timeoutMs: 1e3 });
|
|
1417
|
+
const mpuSeq = 5;
|
|
1418
|
+
expect(
|
|
1419
|
+
reassembler.addFragment({
|
|
1420
|
+
fragmentationIndicator: 1,
|
|
1421
|
+
mpuSequenceNumber: mpuSeq,
|
|
1422
|
+
fragmentCounter: 1,
|
|
1423
|
+
itemId: 10,
|
|
1424
|
+
data: new Uint8Array([161]),
|
|
1425
|
+
timestamp: 100n,
|
|
1426
|
+
rapFlag: true,
|
|
1427
|
+
packetSequenceNumber: testPacketSequence(mpuSeq, 1)
|
|
1428
|
+
})
|
|
1429
|
+
).toBeNull();
|
|
1430
|
+
expectRejection(
|
|
1431
|
+
() => reassembler.addFragment({
|
|
1432
|
+
fragmentationIndicator: 1,
|
|
1433
|
+
mpuSequenceNumber: mpuSeq,
|
|
1434
|
+
fragmentCounter: 1,
|
|
1435
|
+
itemId: 20,
|
|
1436
|
+
data: new Uint8Array([177]),
|
|
1437
|
+
timestamp: 200n,
|
|
1438
|
+
rapFlag: false,
|
|
1439
|
+
packetSequenceNumber: testPacketSequence(mpuSeq, 1)
|
|
1440
|
+
}),
|
|
1441
|
+
"overlapping-data-unit"
|
|
1442
|
+
);
|
|
1443
|
+
expect(reassembler.pendingCount).toBe(1);
|
|
1444
|
+
const a = reassembler.addFragment({
|
|
1445
|
+
fragmentationIndicator: 3,
|
|
1446
|
+
mpuSequenceNumber: mpuSeq,
|
|
1447
|
+
fragmentCounter: 0,
|
|
1448
|
+
data: new Uint8Array([162]),
|
|
1449
|
+
timestamp: 101n,
|
|
1450
|
+
rapFlag: false,
|
|
1451
|
+
packetSequenceNumber: testPacketSequence(mpuSeq, 0)
|
|
1452
|
+
});
|
|
1453
|
+
expect(a).not.toBeNull();
|
|
1454
|
+
expect(a.data).toEqual(new Uint8Array([161, 162]));
|
|
1455
|
+
expect(a.rapFlag).toBe(true);
|
|
1456
|
+
expect(reassembler.addFragment({
|
|
1457
|
+
fragmentationIndicator: 1,
|
|
1458
|
+
mpuSequenceNumber: mpuSeq,
|
|
1459
|
+
fragmentCounter: 1,
|
|
1460
|
+
itemId: 20,
|
|
1461
|
+
data: new Uint8Array([177]),
|
|
1462
|
+
timestamp: 200n,
|
|
1463
|
+
rapFlag: false,
|
|
1464
|
+
packetSequenceNumber: testPacketSequence(mpuSeq, 1, 10)
|
|
1465
|
+
})).toBeNull();
|
|
1466
|
+
const b = reassembler.addFragment({
|
|
1467
|
+
fragmentationIndicator: 3,
|
|
1468
|
+
mpuSequenceNumber: mpuSeq,
|
|
1469
|
+
fragmentCounter: 0,
|
|
1470
|
+
data: new Uint8Array([178]),
|
|
1471
|
+
timestamp: 201n,
|
|
1472
|
+
rapFlag: false,
|
|
1473
|
+
packetSequenceNumber: testPacketSequence(mpuSeq, 0, 10)
|
|
1474
|
+
});
|
|
1475
|
+
expect(b).not.toBeNull();
|
|
1476
|
+
expect(b.data).toEqual(new Uint8Array([177, 178]));
|
|
1477
|
+
});
|
|
1478
|
+
test("reassembles FFmpeg timed MFU fragments by sample number and byte offset", () => {
|
|
1479
|
+
const reassembler = new MFUReassembler({ timeoutMs: 1e3 });
|
|
1480
|
+
const common = {
|
|
1481
|
+
mpuSequenceNumber: 0,
|
|
1482
|
+
movieFragmentSequenceNumber: 9,
|
|
1483
|
+
sampleNumber: 17
|
|
1484
|
+
};
|
|
1485
|
+
expect(reassembler.addFragment({
|
|
1486
|
+
...common,
|
|
1487
|
+
fragmentationIndicator: 1,
|
|
1488
|
+
fragmentCounter: 2,
|
|
1489
|
+
sampleOffset: 0,
|
|
1490
|
+
data: new Uint8Array([161]),
|
|
1491
|
+
timestamp: 1000n,
|
|
1492
|
+
rapFlag: true,
|
|
1493
|
+
packetSequenceNumber: testPacketSequence(0, 2)
|
|
1494
|
+
})).toBeNull();
|
|
1495
|
+
expect(reassembler.addFragment({
|
|
1496
|
+
...common,
|
|
1497
|
+
fragmentationIndicator: 2,
|
|
1498
|
+
fragmentCounter: 1,
|
|
1499
|
+
sampleOffset: 1,
|
|
1500
|
+
data: new Uint8Array([178]),
|
|
1501
|
+
timestamp: 1001n,
|
|
1502
|
+
rapFlag: false,
|
|
1503
|
+
packetSequenceNumber: testPacketSequence(0, 1)
|
|
1504
|
+
})).toBeNull();
|
|
1505
|
+
const out = reassembler.addFragment({
|
|
1506
|
+
...common,
|
|
1507
|
+
fragmentationIndicator: 3,
|
|
1508
|
+
fragmentCounter: 0,
|
|
1509
|
+
sampleOffset: 2,
|
|
1510
|
+
data: new Uint8Array([195]),
|
|
1511
|
+
timestamp: 1002n,
|
|
1512
|
+
rapFlag: false,
|
|
1513
|
+
packetSequenceNumber: testPacketSequence(0, 0)
|
|
1514
|
+
});
|
|
1515
|
+
expect(out).not.toBeNull();
|
|
1516
|
+
expect(out.data).toEqual(new Uint8Array([161, 178, 195]));
|
|
1517
|
+
expect(out.timestamp).toBe(1000n);
|
|
1518
|
+
expect(out.sampleNumber).toBe(17);
|
|
1519
|
+
expect(out.movieFragmentSequenceNumber).toBe(9);
|
|
1520
|
+
});
|
|
1521
|
+
test("buffers a counter gap without advancing the active data unit", () => {
|
|
1522
|
+
const reassembler = new MFUReassembler({ timeoutMs: 1e3 });
|
|
1523
|
+
const common = {
|
|
1524
|
+
mpuSequenceNumber: 0,
|
|
1525
|
+
movieFragmentSequenceNumber: 9,
|
|
1526
|
+
sampleNumber: 17,
|
|
1527
|
+
timestamp: 1000n,
|
|
1528
|
+
rapFlag: true
|
|
1529
|
+
};
|
|
1530
|
+
expect(reassembler.addFragment({
|
|
1531
|
+
...common,
|
|
1532
|
+
fragmentationIndicator: 1,
|
|
1533
|
+
fragmentCounter: 2,
|
|
1534
|
+
sampleOffset: 0,
|
|
1535
|
+
data: new Uint8Array([161, 162]),
|
|
1536
|
+
packetSequenceNumber: testPacketSequence(0, 2)
|
|
1537
|
+
})).toBeNull();
|
|
1538
|
+
expect(reassembler.addFragment({
|
|
1539
|
+
...common,
|
|
1540
|
+
fragmentationIndicator: 3,
|
|
1541
|
+
fragmentCounter: 0,
|
|
1542
|
+
sampleOffset: 4,
|
|
1543
|
+
data: new Uint8Array([193, 194]),
|
|
1544
|
+
packetSequenceNumber: testPacketSequence(0, 0)
|
|
1545
|
+
})).toBeNull();
|
|
1546
|
+
expect(reassembler.pendingCount).toBe(1);
|
|
1547
|
+
const out = reassembler.addFragment({
|
|
1548
|
+
...common,
|
|
1549
|
+
fragmentationIndicator: 2,
|
|
1550
|
+
fragmentCounter: 1,
|
|
1551
|
+
sampleOffset: 2,
|
|
1552
|
+
data: new Uint8Array([177, 178]),
|
|
1553
|
+
packetSequenceNumber: testPacketSequence(0, 1)
|
|
1554
|
+
});
|
|
1555
|
+
expect(out?.data).toEqual(
|
|
1556
|
+
new Uint8Array([161, 162, 177, 178, 193, 194])
|
|
1557
|
+
);
|
|
1558
|
+
});
|
|
1559
|
+
test("reassembles first-only DU-header fragments across changing packet timestamps", () => {
|
|
1560
|
+
const reassembler = new MFUReassembler({ timeoutMs: 1e3 });
|
|
1561
|
+
expect(reassembler.addFragment({
|
|
1562
|
+
fragmentationIndicator: 1,
|
|
1563
|
+
mpuSequenceNumber: 44,
|
|
1564
|
+
fragmentCounter: 2,
|
|
1565
|
+
movieFragmentSequenceNumber: 9,
|
|
1566
|
+
sampleNumber: 17,
|
|
1567
|
+
sampleOffset: 0,
|
|
1568
|
+
data: new Uint8Array([161]),
|
|
1569
|
+
timestamp: 12345n,
|
|
1570
|
+
rapFlag: true,
|
|
1571
|
+
packetSequenceNumber: testPacketSequence(44, 2)
|
|
1572
|
+
})).toBeNull();
|
|
1573
|
+
expect(reassembler.addFragment({
|
|
1574
|
+
fragmentationIndicator: 2,
|
|
1575
|
+
mpuSequenceNumber: 44,
|
|
1576
|
+
fragmentCounter: 1,
|
|
1577
|
+
data: new Uint8Array([178]),
|
|
1578
|
+
timestamp: 12346n,
|
|
1579
|
+
rapFlag: false,
|
|
1580
|
+
packetSequenceNumber: testPacketSequence(44, 1)
|
|
1581
|
+
})).toBeNull();
|
|
1582
|
+
const out = reassembler.addFragment({
|
|
1583
|
+
fragmentationIndicator: 3,
|
|
1584
|
+
mpuSequenceNumber: 44,
|
|
1585
|
+
fragmentCounter: 0,
|
|
1586
|
+
data: new Uint8Array([195]),
|
|
1587
|
+
timestamp: 12347n,
|
|
1588
|
+
rapFlag: false,
|
|
1589
|
+
packetSequenceNumber: testPacketSequence(44, 0)
|
|
1590
|
+
});
|
|
1591
|
+
expect(out).not.toBeNull();
|
|
1592
|
+
expect(out.data).toEqual(new Uint8Array([161, 178, 195]));
|
|
1593
|
+
expect(out.timestamp).toBe(12345n);
|
|
1594
|
+
expect(out.sampleNumber).toBe(17);
|
|
1595
|
+
expect(out.movieFragmentSequenceNumber).toBe(9);
|
|
1596
|
+
});
|
|
1597
|
+
test("reassembles first-only DU-header fragments with an ISO countdown", () => {
|
|
1598
|
+
const reassembler = new MFUReassembler({ timeoutMs: 1e3 });
|
|
1599
|
+
const timestamp = 12345n;
|
|
1600
|
+
expect(reassembler.addFragment({
|
|
1601
|
+
fragmentationIndicator: 1,
|
|
1602
|
+
mpuSequenceNumber: 44,
|
|
1603
|
+
fragmentCounter: 3,
|
|
1604
|
+
movieFragmentSequenceNumber: 9,
|
|
1605
|
+
sampleNumber: 17,
|
|
1606
|
+
sampleOffset: 0,
|
|
1607
|
+
data: new Uint8Array([161]),
|
|
1608
|
+
timestamp,
|
|
1609
|
+
rapFlag: true,
|
|
1610
|
+
packetSequenceNumber: testPacketSequence(44, 3)
|
|
1611
|
+
})).toBeNull();
|
|
1612
|
+
expect(reassembler.addFragment({
|
|
1613
|
+
fragmentationIndicator: 2,
|
|
1614
|
+
mpuSequenceNumber: 44,
|
|
1615
|
+
fragmentCounter: 2,
|
|
1616
|
+
data: new Uint8Array([178]),
|
|
1617
|
+
timestamp,
|
|
1618
|
+
rapFlag: false,
|
|
1619
|
+
packetSequenceNumber: testPacketSequence(44, 2)
|
|
1620
|
+
})).toBeNull();
|
|
1621
|
+
expect(reassembler.addFragment({
|
|
1622
|
+
fragmentationIndicator: 2,
|
|
1623
|
+
mpuSequenceNumber: 44,
|
|
1624
|
+
fragmentCounter: 1,
|
|
1625
|
+
data: new Uint8Array([195]),
|
|
1626
|
+
timestamp,
|
|
1627
|
+
rapFlag: false,
|
|
1628
|
+
packetSequenceNumber: testPacketSequence(44, 1)
|
|
1629
|
+
})).toBeNull();
|
|
1630
|
+
const out = reassembler.addFragment({
|
|
1631
|
+
fragmentationIndicator: 3,
|
|
1632
|
+
mpuSequenceNumber: 44,
|
|
1633
|
+
fragmentCounter: 0,
|
|
1634
|
+
data: new Uint8Array([212]),
|
|
1635
|
+
timestamp,
|
|
1636
|
+
rapFlag: false,
|
|
1637
|
+
packetSequenceNumber: testPacketSequence(44, 0)
|
|
1638
|
+
});
|
|
1639
|
+
expect(out).not.toBeNull();
|
|
1640
|
+
expect(out.data).toEqual(new Uint8Array([161, 178, 195, 212]));
|
|
1641
|
+
expect(out.sampleNumber).toBe(17);
|
|
1642
|
+
expect(out.movieFragmentSequenceNumber).toBe(9);
|
|
1643
|
+
});
|
|
1644
|
+
test("rejects fragmented MFUs without an authored DU identity", () => {
|
|
1645
|
+
const reassembler = new MFUReassembler({ timeoutMs: 1e3 });
|
|
1646
|
+
const timestamp = 22222n;
|
|
1647
|
+
expectRejection(() => reassembler.addFragment({
|
|
1648
|
+
fragmentationIndicator: 1,
|
|
1649
|
+
mpuSequenceNumber: 55,
|
|
1650
|
+
fragmentCounter: 2,
|
|
1651
|
+
data: new Uint8Array([161]),
|
|
1652
|
+
timestamp,
|
|
1653
|
+
rapFlag: true,
|
|
1654
|
+
packetSequenceNumber: testPacketSequence(55, 2)
|
|
1655
|
+
}), "missing-data-unit-identity");
|
|
1656
|
+
expect(reassembler.pendingCount).toBe(0);
|
|
1657
|
+
});
|
|
1658
|
+
test("rejects fragmented MFUs without an authored packet sequence", () => {
|
|
1659
|
+
const reassembler = new MFUReassembler({ timeoutMs: 1e3 });
|
|
1660
|
+
expectRejection(() => reassembler.addFragment({
|
|
1661
|
+
fragmentationIndicator: 1,
|
|
1662
|
+
mpuSequenceNumber: 55,
|
|
1663
|
+
fragmentCounter: 1,
|
|
1664
|
+
itemId: 1,
|
|
1665
|
+
data: new Uint8Array([161]),
|
|
1666
|
+
timestamp: 22222n,
|
|
1667
|
+
rapFlag: true
|
|
1668
|
+
}), "missing-packet-sequence");
|
|
1669
|
+
expect(reassembler.pendingCount).toBe(0);
|
|
1670
|
+
});
|
|
1671
|
+
test("rejects fragmented MFUs whose authored packet sequence is not u32", () => {
|
|
1672
|
+
const reassembler = new MFUReassembler({ timeoutMs: 1e3 });
|
|
1673
|
+
for (const packetSequenceNumber of [-1, 4294967296, 1.5, Number.NaN]) {
|
|
1674
|
+
expectRejection(() => reassembler.addFragment({
|
|
1675
|
+
fragmentationIndicator: 1,
|
|
1676
|
+
mpuSequenceNumber: 55,
|
|
1677
|
+
fragmentCounter: 1,
|
|
1678
|
+
itemId: 1,
|
|
1679
|
+
data: new Uint8Array([161]),
|
|
1680
|
+
timestamp: 22222n,
|
|
1681
|
+
rapFlag: true,
|
|
1682
|
+
packetSequenceNumber
|
|
1683
|
+
}), "missing-packet-sequence");
|
|
1684
|
+
}
|
|
1685
|
+
expect(reassembler.pendingCount).toBe(0);
|
|
1686
|
+
});
|
|
1687
|
+
test("fails closed when one fragmented DU has interleaved packet sequence gaps", () => {
|
|
1688
|
+
const reassembler = new MFUReassembler({ timeoutMs: 1e3 });
|
|
1689
|
+
const timestamp = 66666n;
|
|
1690
|
+
expect(reassembler.addFragment({
|
|
1691
|
+
fragmentationIndicator: 1,
|
|
1692
|
+
mpuSequenceNumber: 80,
|
|
1693
|
+
fragmentCounter: 17,
|
|
1694
|
+
movieFragmentSequenceNumber: 241,
|
|
1695
|
+
sampleNumber: 1,
|
|
1696
|
+
sampleOffset: 0,
|
|
1697
|
+
packetSequenceNumber: 1e3,
|
|
1698
|
+
data: new Uint8Array([160]),
|
|
1699
|
+
timestamp,
|
|
1700
|
+
rapFlag: true
|
|
1701
|
+
})).toBeNull();
|
|
1702
|
+
for (let i = 1; i < 17; i++) {
|
|
1703
|
+
expect(reassembler.addFragment({
|
|
1704
|
+
fragmentationIndicator: 2,
|
|
1705
|
+
mpuSequenceNumber: 80,
|
|
1706
|
+
fragmentCounter: 17 - i,
|
|
1707
|
+
packetSequenceNumber: 1e3 + i * 2,
|
|
1708
|
+
data: new Uint8Array([160 + i]),
|
|
1709
|
+
timestamp,
|
|
1710
|
+
rapFlag: false
|
|
1711
|
+
})).toBeNull();
|
|
1712
|
+
}
|
|
1713
|
+
const out = reassembler.addFragment({
|
|
1714
|
+
fragmentationIndicator: 3,
|
|
1715
|
+
mpuSequenceNumber: 80,
|
|
1716
|
+
fragmentCounter: 0,
|
|
1717
|
+
packetSequenceNumber: 1040,
|
|
1718
|
+
data: new Uint8Array([177]),
|
|
1719
|
+
timestamp,
|
|
1720
|
+
rapFlag: false
|
|
1721
|
+
});
|
|
1722
|
+
expect(out).toBeNull();
|
|
1723
|
+
expect(reassembler.pendingCount).toBe(18);
|
|
1724
|
+
expect(reassembler.stats.reassembledMFUs).toBe(0);
|
|
1725
|
+
});
|
|
1726
|
+
test("does not combine adjacent reordered MFUs in one MPU", () => {
|
|
1727
|
+
const reassembler = new MFUReassembler({ timeoutMs: 1e3 });
|
|
1728
|
+
const common = { mpuSequenceNumber: 80, packetId: 7, timestamp: 66666n };
|
|
1729
|
+
expect(reassembler.addFragment({
|
|
1730
|
+
...common,
|
|
1731
|
+
fragmentationIndicator: 3,
|
|
1732
|
+
fragmentCounter: 0,
|
|
1733
|
+
packetSequenceNumber: 103,
|
|
1734
|
+
data: new Uint8Array([178]),
|
|
1735
|
+
rapFlag: false
|
|
1736
|
+
})).toBeNull();
|
|
1737
|
+
expect(reassembler.addFragment({
|
|
1738
|
+
...common,
|
|
1739
|
+
fragmentationIndicator: 1,
|
|
1740
|
+
fragmentCounter: 1,
|
|
1741
|
+
packetSequenceNumber: 100,
|
|
1742
|
+
movieFragmentSequenceNumber: 241,
|
|
1743
|
+
sampleNumber: 1,
|
|
1744
|
+
sampleOffset: 0,
|
|
1745
|
+
data: new Uint8Array([161]),
|
|
1746
|
+
rapFlag: true
|
|
1747
|
+
})).toBeNull();
|
|
1748
|
+
const earlier = reassembler.addFragment({
|
|
1749
|
+
...common,
|
|
1750
|
+
fragmentationIndicator: 3,
|
|
1751
|
+
fragmentCounter: 0,
|
|
1752
|
+
packetSequenceNumber: 101,
|
|
1753
|
+
data: new Uint8Array([162]),
|
|
1754
|
+
rapFlag: false
|
|
1755
|
+
});
|
|
1756
|
+
expect(earlier?.data).toEqual(new Uint8Array([161, 162]));
|
|
1757
|
+
const later = reassembler.addFragment({
|
|
1758
|
+
...common,
|
|
1759
|
+
fragmentationIndicator: 1,
|
|
1760
|
+
fragmentCounter: 1,
|
|
1761
|
+
packetSequenceNumber: 102,
|
|
1762
|
+
movieFragmentSequenceNumber: 241,
|
|
1763
|
+
sampleNumber: 2,
|
|
1764
|
+
sampleOffset: 0,
|
|
1765
|
+
data: new Uint8Array([177]),
|
|
1766
|
+
rapFlag: false
|
|
1767
|
+
});
|
|
1768
|
+
expect(later?.data).toEqual(new Uint8Array([177, 178]));
|
|
1769
|
+
});
|
|
1770
|
+
test("wraps the fragmented run coordinate at u32 packet sequence", () => {
|
|
1771
|
+
const reassembler = new MFUReassembler({ timeoutMs: 1e3 });
|
|
1772
|
+
expect(reassembler.addFragment({
|
|
1773
|
+
fragmentationIndicator: 1,
|
|
1774
|
+
mpuSequenceNumber: 81,
|
|
1775
|
+
fragmentCounter: 1,
|
|
1776
|
+
packetSequenceNumber: 4294967295,
|
|
1777
|
+
itemId: 1,
|
|
1778
|
+
data: new Uint8Array([161]),
|
|
1779
|
+
timestamp: 66666n,
|
|
1780
|
+
rapFlag: true
|
|
1781
|
+
})).toBeNull();
|
|
1782
|
+
expect(reassembler.addFragment({
|
|
1783
|
+
fragmentationIndicator: 3,
|
|
1784
|
+
mpuSequenceNumber: 81,
|
|
1785
|
+
fragmentCounter: 0,
|
|
1786
|
+
packetSequenceNumber: 0,
|
|
1787
|
+
data: new Uint8Array([162]),
|
|
1788
|
+
timestamp: 66667n,
|
|
1789
|
+
rapFlag: false
|
|
1790
|
+
})?.data).toEqual(new Uint8Array([161, 162]));
|
|
1791
|
+
});
|
|
1792
|
+
test("dedupes recovered copies by ISO fragment-counter position", () => {
|
|
1793
|
+
const reassembler = new MFUReassembler({ timeoutMs: 1e3 });
|
|
1794
|
+
const timestamp = 44444n;
|
|
1795
|
+
expect(reassembler.addFragment({
|
|
1796
|
+
fragmentationIndicator: 1,
|
|
1797
|
+
mpuSequenceNumber: 77,
|
|
1798
|
+
fragmentCounter: 2,
|
|
1799
|
+
movieFragmentSequenceNumber: 12,
|
|
1800
|
+
sampleNumber: 1,
|
|
1801
|
+
sampleOffset: 0,
|
|
1802
|
+
packetSequenceNumber: 2e3,
|
|
1803
|
+
data: new Uint8Array([161]),
|
|
1804
|
+
timestamp,
|
|
1805
|
+
rapFlag: true
|
|
1806
|
+
})).toBeNull();
|
|
1807
|
+
expect(reassembler.addFragment({
|
|
1808
|
+
fragmentationIndicator: 2,
|
|
1809
|
+
mpuSequenceNumber: 77,
|
|
1810
|
+
fragmentCounter: 1,
|
|
1811
|
+
packetSequenceNumber: 2001,
|
|
1812
|
+
data: new Uint8Array([178]),
|
|
1813
|
+
timestamp,
|
|
1814
|
+
rapFlag: false
|
|
1815
|
+
})).toBeNull();
|
|
1816
|
+
expect(reassembler.addFragment({
|
|
1817
|
+
fragmentationIndicator: 3,
|
|
1818
|
+
mpuSequenceNumber: 77,
|
|
1819
|
+
fragmentCounter: 0,
|
|
1820
|
+
packetSequenceNumber: 2002,
|
|
1821
|
+
data: new Uint8Array([195]),
|
|
1822
|
+
timestamp,
|
|
1823
|
+
rapFlag: false
|
|
1824
|
+
})).not.toBeNull();
|
|
1825
|
+
reassembler.flush();
|
|
1826
|
+
expect(reassembler.addFragment({
|
|
1827
|
+
fragmentationIndicator: 1,
|
|
1828
|
+
mpuSequenceNumber: 78,
|
|
1829
|
+
fragmentCounter: 2,
|
|
1830
|
+
movieFragmentSequenceNumber: 13,
|
|
1831
|
+
sampleNumber: 1,
|
|
1832
|
+
sampleOffset: 0,
|
|
1833
|
+
packetSequenceNumber: 3e3,
|
|
1834
|
+
data: new Uint8Array([212]),
|
|
1835
|
+
timestamp,
|
|
1836
|
+
rapFlag: true
|
|
1837
|
+
})).toBeNull();
|
|
1838
|
+
expect(reassembler.addFragment({
|
|
1839
|
+
fragmentationIndicator: 2,
|
|
1840
|
+
mpuSequenceNumber: 78,
|
|
1841
|
+
fragmentCounter: 1,
|
|
1842
|
+
packetSequenceNumber: 3001,
|
|
1843
|
+
data: new Uint8Array([229]),
|
|
1844
|
+
timestamp,
|
|
1845
|
+
rapFlag: false
|
|
1846
|
+
})).toBeNull();
|
|
1847
|
+
expectRejection(() => reassembler.addFragment({
|
|
1848
|
+
fragmentationIndicator: 2,
|
|
1849
|
+
mpuSequenceNumber: 78,
|
|
1850
|
+
fragmentCounter: 1,
|
|
1851
|
+
packetSequenceNumber: 3001,
|
|
1852
|
+
data: new Uint8Array([229]),
|
|
1853
|
+
timestamp,
|
|
1854
|
+
rapFlag: false
|
|
1855
|
+
}), "fragment-counter-mismatch");
|
|
1856
|
+
const out = reassembler.addFragment({
|
|
1857
|
+
fragmentationIndicator: 3,
|
|
1858
|
+
mpuSequenceNumber: 78,
|
|
1859
|
+
fragmentCounter: 0,
|
|
1860
|
+
packetSequenceNumber: 3002,
|
|
1861
|
+
data: new Uint8Array([246]),
|
|
1862
|
+
timestamp,
|
|
1863
|
+
rapFlag: false
|
|
1864
|
+
});
|
|
1865
|
+
expect(out).not.toBeNull();
|
|
1866
|
+
expect(out.data).toEqual(new Uint8Array([212, 229, 246]));
|
|
1867
|
+
});
|
|
1868
|
+
test("reassembles middle and terminal continuations that arrive before first", () => {
|
|
1869
|
+
const reassembler = new MFUReassembler({ timeoutMs: 1e3 });
|
|
1870
|
+
const timestamp = 54321n;
|
|
1871
|
+
expect(reassembler.addFragment({
|
|
1872
|
+
fragmentationIndicator: 2,
|
|
1873
|
+
mpuSequenceNumber: 45,
|
|
1874
|
+
fragmentCounter: 1,
|
|
1875
|
+
data: new Uint8Array([178]),
|
|
1876
|
+
timestamp,
|
|
1877
|
+
rapFlag: false,
|
|
1878
|
+
packetSequenceNumber: testPacketSequence(45, 1)
|
|
1879
|
+
})).toBeNull();
|
|
1880
|
+
expect(reassembler.addFragment({
|
|
1881
|
+
fragmentationIndicator: 3,
|
|
1882
|
+
mpuSequenceNumber: 45,
|
|
1883
|
+
fragmentCounter: 0,
|
|
1884
|
+
data: new Uint8Array([195]),
|
|
1885
|
+
timestamp: timestamp + 1n,
|
|
1886
|
+
rapFlag: false,
|
|
1887
|
+
packetSequenceNumber: testPacketSequence(45, 0)
|
|
1888
|
+
})).toBeNull();
|
|
1889
|
+
expect(reassembler.pendingCount).toBe(1);
|
|
1890
|
+
const out = reassembler.addFragment({
|
|
1891
|
+
fragmentationIndicator: 1,
|
|
1892
|
+
mpuSequenceNumber: 45,
|
|
1893
|
+
fragmentCounter: 2,
|
|
1894
|
+
movieFragmentSequenceNumber: 10,
|
|
1895
|
+
sampleNumber: 18,
|
|
1896
|
+
sampleOffset: 0,
|
|
1897
|
+
data: new Uint8Array([161]),
|
|
1898
|
+
timestamp,
|
|
1899
|
+
rapFlag: true,
|
|
1900
|
+
packetSequenceNumber: testPacketSequence(45, 2)
|
|
1901
|
+
});
|
|
1902
|
+
expect(out).not.toBeNull();
|
|
1903
|
+
expect(out.data).toEqual(new Uint8Array([161, 178, 195]));
|
|
1904
|
+
expect(out.sampleNumber).toBe(18);
|
|
1905
|
+
expect(out.movieFragmentSequenceNumber).toBe(10);
|
|
1906
|
+
expect(reassembler.pendingCount).toBe(0);
|
|
1907
|
+
});
|
|
1908
|
+
test("rejects a duplicate continuation before first without replacing retained media", () => {
|
|
1909
|
+
const reassembler = new MFUReassembler({ timeoutMs: 1e3 });
|
|
1910
|
+
const timestamp = 54322n;
|
|
1911
|
+
expect(reassembler.addFragment({
|
|
1912
|
+
fragmentationIndicator: 3,
|
|
1913
|
+
mpuSequenceNumber: 46,
|
|
1914
|
+
fragmentCounter: 0,
|
|
1915
|
+
data: new Uint8Array([195]),
|
|
1916
|
+
timestamp,
|
|
1917
|
+
rapFlag: false,
|
|
1918
|
+
packetSequenceNumber: testPacketSequence(46, 0)
|
|
1919
|
+
})).toBeNull();
|
|
1920
|
+
expectRejection(() => reassembler.addFragment({
|
|
1921
|
+
fragmentationIndicator: 3,
|
|
1922
|
+
mpuSequenceNumber: 46,
|
|
1923
|
+
fragmentCounter: 0,
|
|
1924
|
+
data: new Uint8Array([255]),
|
|
1925
|
+
timestamp: timestamp + 1n,
|
|
1926
|
+
rapFlag: false,
|
|
1927
|
+
packetSequenceNumber: testPacketSequence(46, 0)
|
|
1928
|
+
}), "fragment-counter-mismatch");
|
|
1929
|
+
expect(reassembler.addFragment({
|
|
1930
|
+
fragmentationIndicator: 2,
|
|
1931
|
+
mpuSequenceNumber: 46,
|
|
1932
|
+
fragmentCounter: 1,
|
|
1933
|
+
data: new Uint8Array([178]),
|
|
1934
|
+
timestamp: timestamp + 2n,
|
|
1935
|
+
rapFlag: false,
|
|
1936
|
+
packetSequenceNumber: testPacketSequence(46, 1)
|
|
1937
|
+
})).toBeNull();
|
|
1938
|
+
const out = reassembler.addFragment({
|
|
1939
|
+
fragmentationIndicator: 1,
|
|
1940
|
+
mpuSequenceNumber: 46,
|
|
1941
|
+
fragmentCounter: 2,
|
|
1942
|
+
movieFragmentSequenceNumber: 10,
|
|
1943
|
+
sampleNumber: 19,
|
|
1944
|
+
sampleOffset: 0,
|
|
1945
|
+
data: new Uint8Array([161]),
|
|
1946
|
+
timestamp: timestamp + 3n,
|
|
1947
|
+
rapFlag: true,
|
|
1948
|
+
packetSequenceNumber: testPacketSequence(46, 2)
|
|
1949
|
+
});
|
|
1950
|
+
expect(out).not.toBeNull();
|
|
1951
|
+
expect(out.data).toEqual(new Uint8Array([161, 178, 195]));
|
|
1952
|
+
expect(reassembler.pendingCount).toBe(0);
|
|
1953
|
+
});
|
|
1954
|
+
test("rejects invalid ISO fragmentation-indicator and counter combinations", () => {
|
|
1955
|
+
const reassembler = new MFUReassembler({ timeoutMs: 1e3 });
|
|
1956
|
+
const invalidHeaders = [
|
|
1957
|
+
{ fragmentationIndicator: 0, fragmentCounter: 1 },
|
|
1958
|
+
{ fragmentationIndicator: 3, fragmentCounter: 1 },
|
|
1959
|
+
{ fragmentationIndicator: 1, fragmentCounter: 0 },
|
|
1960
|
+
{ fragmentationIndicator: 2, fragmentCounter: 0 },
|
|
1961
|
+
{ fragmentationIndicator: -1, fragmentCounter: 1 },
|
|
1962
|
+
{ fragmentationIndicator: 4, fragmentCounter: 1 },
|
|
1963
|
+
{ fragmentationIndicator: 1.5, fragmentCounter: 1 },
|
|
1964
|
+
{ fragmentationIndicator: 1, fragmentCounter: -1 },
|
|
1965
|
+
{ fragmentationIndicator: 1, fragmentCounter: 256 },
|
|
1966
|
+
{ fragmentationIndicator: 1, fragmentCounter: 1.5 },
|
|
1967
|
+
{ fragmentationIndicator: 1, fragmentCounter: Number.NaN }
|
|
1968
|
+
];
|
|
1969
|
+
for (const header of invalidHeaders) {
|
|
1970
|
+
expectRejection(() => reassembler.addFragment({
|
|
1971
|
+
...header,
|
|
1972
|
+
mpuSequenceNumber: 48,
|
|
1973
|
+
data: new Uint8Array([161]),
|
|
1974
|
+
timestamp: 54324n,
|
|
1975
|
+
rapFlag: false
|
|
1976
|
+
}), "invalid-fragment-state");
|
|
1977
|
+
}
|
|
1978
|
+
expect(reassembler.pendingCount).toBe(0);
|
|
1979
|
+
expect(reassembler.stats.reassembledMFUs).toBe(0);
|
|
1980
|
+
});
|
|
1981
|
+
test("reassembles the maximum ISO chain of 256 payloads", () => {
|
|
1982
|
+
const reassembler = new MFUReassembler({ timeoutMs: null });
|
|
1983
|
+
const common = {
|
|
1984
|
+
mpuSequenceNumber: 47,
|
|
1985
|
+
timestamp: 54323n,
|
|
1986
|
+
rapFlag: false
|
|
1987
|
+
};
|
|
1988
|
+
expect(reassembler.addFragment({
|
|
1989
|
+
...common,
|
|
1990
|
+
fragmentationIndicator: 1,
|
|
1991
|
+
fragmentCounter: 255,
|
|
1992
|
+
itemId: 1,
|
|
1993
|
+
data: new Uint8Array([0]),
|
|
1994
|
+
rapFlag: true,
|
|
1995
|
+
packetSequenceNumber: testPacketSequence(47, 255)
|
|
1996
|
+
})).toBeNull();
|
|
1997
|
+
for (let counter = 254; counter > 0; counter--) {
|
|
1998
|
+
expect(reassembler.addFragment({
|
|
1999
|
+
...common,
|
|
2000
|
+
fragmentationIndicator: 2,
|
|
2001
|
+
fragmentCounter: counter,
|
|
2002
|
+
data: new Uint8Array([255 - counter]),
|
|
2003
|
+
packetSequenceNumber: testPacketSequence(47, counter)
|
|
2004
|
+
})).toBeNull();
|
|
2005
|
+
}
|
|
2006
|
+
const out = reassembler.addFragment({
|
|
2007
|
+
...common,
|
|
2008
|
+
fragmentationIndicator: 3,
|
|
2009
|
+
fragmentCounter: 0,
|
|
2010
|
+
data: new Uint8Array([255]),
|
|
2011
|
+
packetSequenceNumber: testPacketSequence(47, 0)
|
|
2012
|
+
});
|
|
2013
|
+
expect(out?.fragmentCount).toBe(256);
|
|
2014
|
+
expect(out?.data).toEqual(Uint8Array.from({ length: 256 }, (_, index) => index));
|
|
2015
|
+
});
|
|
2016
|
+
test("should not reassemble without last fragment (FI=3)", () => {
|
|
2017
|
+
const reassembler = new MFUReassembler({ timeoutMs: 1e3 });
|
|
2018
|
+
reassembler.addFragment({
|
|
2019
|
+
fragmentationIndicator: 1,
|
|
2020
|
+
mpuSequenceNumber: 1,
|
|
2021
|
+
fragmentCounter: 2,
|
|
2022
|
+
itemId: 1,
|
|
2023
|
+
data: new Uint8Array([1]),
|
|
2024
|
+
timestamp: 1000n,
|
|
2025
|
+
rapFlag: false,
|
|
2026
|
+
packetSequenceNumber: testPacketSequence(1, 2)
|
|
2027
|
+
});
|
|
2028
|
+
const result = reassembler.addFragment({
|
|
2029
|
+
fragmentationIndicator: 2,
|
|
2030
|
+
mpuSequenceNumber: 1,
|
|
2031
|
+
fragmentCounter: 1,
|
|
2032
|
+
data: new Uint8Array([2]),
|
|
2033
|
+
timestamp: 1000n,
|
|
2034
|
+
rapFlag: false,
|
|
2035
|
+
packetSequenceNumber: testPacketSequence(1, 1)
|
|
2036
|
+
});
|
|
2037
|
+
expect(result).toBeNull();
|
|
2038
|
+
expect(reassembler.pendingCount).toBe(1);
|
|
2039
|
+
});
|
|
2040
|
+
test("bounds continuations that arrive before their first fragments", () => {
|
|
2041
|
+
const reassembler = new MFUReassembler({ timeoutMs: null, maxBuffered: 1 });
|
|
2042
|
+
expect(reassembler.addFragment({
|
|
2043
|
+
fragmentationIndicator: 3,
|
|
2044
|
+
mpuSequenceNumber: 1,
|
|
2045
|
+
fragmentCounter: 0,
|
|
2046
|
+
data: new Uint8Array([1]),
|
|
2047
|
+
timestamp: 1000n,
|
|
2048
|
+
rapFlag: false,
|
|
2049
|
+
packetSequenceNumber: testPacketSequence(1, 0)
|
|
2050
|
+
})).toBeNull();
|
|
2051
|
+
expectRejection(() => reassembler.addFragment({
|
|
2052
|
+
fragmentationIndicator: 3,
|
|
2053
|
+
mpuSequenceNumber: 2,
|
|
2054
|
+
fragmentCounter: 0,
|
|
2055
|
+
data: new Uint8Array([2]),
|
|
2056
|
+
timestamp: 1000n,
|
|
2057
|
+
rapFlag: false,
|
|
2058
|
+
packetSequenceNumber: testPacketSequence(2, 0)
|
|
2059
|
+
}), "active-data-unit-limit");
|
|
2060
|
+
expectRejection(() => reassembler.addFragment({
|
|
2061
|
+
fragmentationIndicator: 1,
|
|
2062
|
+
mpuSequenceNumber: 2,
|
|
2063
|
+
fragmentCounter: 1,
|
|
2064
|
+
itemId: 2,
|
|
2065
|
+
data: new Uint8Array([2]),
|
|
2066
|
+
timestamp: 1000n,
|
|
2067
|
+
rapFlag: true,
|
|
2068
|
+
packetSequenceNumber: testPacketSequence(2, 1)
|
|
2069
|
+
}), "active-data-unit-limit");
|
|
2070
|
+
expect(reassembler.pendingCount).toBe(1);
|
|
2071
|
+
expect(reassembler.getStatus().pendingFragments).toBe(1);
|
|
2072
|
+
const result = reassembler.addFragment({
|
|
2073
|
+
fragmentationIndicator: 1,
|
|
2074
|
+
mpuSequenceNumber: 1,
|
|
2075
|
+
fragmentCounter: 1,
|
|
2076
|
+
itemId: 1,
|
|
2077
|
+
data: new Uint8Array([3]),
|
|
2078
|
+
timestamp: 1000n,
|
|
2079
|
+
rapFlag: true,
|
|
2080
|
+
packetSequenceNumber: testPacketSequence(1, 1)
|
|
2081
|
+
});
|
|
2082
|
+
expect(result?.data).toEqual(new Uint8Array([3, 1]));
|
|
2083
|
+
});
|
|
2084
|
+
test("expires a continuation that never receives its first fragment", () => {
|
|
2085
|
+
vi.useFakeTimers();
|
|
2086
|
+
try {
|
|
2087
|
+
const reassembler = new MFUReassembler({ timeoutMs: 50, maxBuffered: 1 });
|
|
2088
|
+
expect(reassembler.addFragment({
|
|
2089
|
+
fragmentationIndicator: 3,
|
|
2090
|
+
mpuSequenceNumber: 1,
|
|
2091
|
+
fragmentCounter: 0,
|
|
2092
|
+
data: new Uint8Array([1]),
|
|
2093
|
+
timestamp: 1000n,
|
|
2094
|
+
rapFlag: false,
|
|
2095
|
+
packetSequenceNumber: testPacketSequence(1, 0)
|
|
2096
|
+
})).toBeNull();
|
|
2097
|
+
vi.advanceTimersByTime(50);
|
|
2098
|
+
expect(reassembler.pendingCount).toBe(0);
|
|
2099
|
+
expect(reassembler.stats.timedOutMFUs).toBe(1);
|
|
2100
|
+
} finally {
|
|
2101
|
+
vi.useRealTimers();
|
|
2102
|
+
}
|
|
2103
|
+
});
|
|
2104
|
+
test("retimes retained continuations when the timeout policy changes", () => {
|
|
2105
|
+
vi.useFakeTimers();
|
|
2106
|
+
try {
|
|
2107
|
+
const enabled = new MFUReassembler({ timeoutMs: null, maxBuffered: 1 });
|
|
2108
|
+
expect(enabled.addFragment({
|
|
2109
|
+
fragmentationIndicator: 3,
|
|
2110
|
+
mpuSequenceNumber: 1,
|
|
2111
|
+
fragmentCounter: 0,
|
|
2112
|
+
data: new Uint8Array([1]),
|
|
2113
|
+
timestamp: 1000n,
|
|
2114
|
+
rapFlag: false,
|
|
2115
|
+
packetSequenceNumber: testPacketSequence(1, 0)
|
|
2116
|
+
})).toBeNull();
|
|
2117
|
+
enabled.timeoutMs = 10;
|
|
2118
|
+
vi.advanceTimersByTime(10);
|
|
2119
|
+
expect(enabled.pendingCount).toBe(0);
|
|
2120
|
+
const disabled = new MFUReassembler({ timeoutMs: 10, maxBuffered: 1 });
|
|
2121
|
+
expect(disabled.addFragment({
|
|
2122
|
+
fragmentationIndicator: 3,
|
|
2123
|
+
mpuSequenceNumber: 2,
|
|
2124
|
+
fragmentCounter: 0,
|
|
2125
|
+
data: new Uint8Array([2]),
|
|
2126
|
+
timestamp: 1000n,
|
|
2127
|
+
rapFlag: false,
|
|
2128
|
+
packetSequenceNumber: testPacketSequence(2, 0)
|
|
2129
|
+
})).toBeNull();
|
|
2130
|
+
disabled.timeoutMs = null;
|
|
2131
|
+
vi.advanceTimersByTime(10);
|
|
2132
|
+
expect(disabled.pendingCount).toBe(1);
|
|
2133
|
+
disabled.flush();
|
|
2134
|
+
} finally {
|
|
2135
|
+
vi.useRealTimers();
|
|
2136
|
+
}
|
|
2137
|
+
});
|
|
2138
|
+
test("bounds active data units and rejects a new first at maxBuffered", () => {
|
|
2139
|
+
const reassembler = new MFUReassembler({ timeoutMs: 1e3, maxBuffered: 2 });
|
|
2140
|
+
for (let i = 0; i < 2; i++) {
|
|
2141
|
+
reassembler.addFragment({
|
|
2142
|
+
fragmentationIndicator: 1,
|
|
2143
|
+
mpuSequenceNumber: i,
|
|
2144
|
+
fragmentCounter: 1,
|
|
2145
|
+
itemId: i,
|
|
2146
|
+
data: new Uint8Array([i]),
|
|
2147
|
+
timestamp: BigInt(i * 100),
|
|
2148
|
+
rapFlag: false,
|
|
2149
|
+
packetSequenceNumber: testPacketSequence(i, 1)
|
|
2150
|
+
});
|
|
2151
|
+
}
|
|
2152
|
+
expectRejection(() => reassembler.addFragment({
|
|
2153
|
+
fragmentationIndicator: 1,
|
|
2154
|
+
mpuSequenceNumber: 2,
|
|
2155
|
+
fragmentCounter: 1,
|
|
2156
|
+
itemId: 2,
|
|
2157
|
+
data: new Uint8Array([2]),
|
|
2158
|
+
timestamp: 200n,
|
|
2159
|
+
rapFlag: false,
|
|
2160
|
+
packetSequenceNumber: testPacketSequence(2, 1)
|
|
2161
|
+
}), "active-data-unit-limit");
|
|
2162
|
+
expect(reassembler.pendingCount).toBe(2);
|
|
2163
|
+
expect(reassembler.stats.timedOutMFUs).toBe(0);
|
|
2164
|
+
expect(reassembler.stats.evictedMemoryLimit).toBe(0);
|
|
2165
|
+
});
|
|
2166
|
+
test("should report stats correctly", () => {
|
|
2167
|
+
const reassembler = new MFUReassembler({ timeoutMs: 1e3 });
|
|
2168
|
+
reassembler.addFragment({
|
|
2169
|
+
fragmentationIndicator: 0,
|
|
2170
|
+
mpuSequenceNumber: 1,
|
|
2171
|
+
fragmentCounter: 0,
|
|
2172
|
+
itemId: 1,
|
|
2173
|
+
data: new Uint8Array([1]),
|
|
2174
|
+
timestamp: 1000n,
|
|
2175
|
+
rapFlag: false
|
|
2176
|
+
});
|
|
2177
|
+
const status = reassembler.getStatus();
|
|
2178
|
+
expect(status.bufferedMFUs).toBe(0);
|
|
2179
|
+
expect(status.stats.totalFragments).toBe(1);
|
|
2180
|
+
expect(status.stats.reassembledMFUs).toBe(1);
|
|
2181
|
+
});
|
|
2182
|
+
test("rejects an overlapping first without replacing the original fragment", () => {
|
|
2183
|
+
const reassembler = new MFUReassembler({ timeoutMs: 1e3 });
|
|
2184
|
+
reassembler.addFragment({
|
|
2185
|
+
fragmentationIndicator: 1,
|
|
2186
|
+
mpuSequenceNumber: 1,
|
|
2187
|
+
fragmentCounter: 1,
|
|
2188
|
+
itemId: 1,
|
|
2189
|
+
data: new Uint8Array([170]),
|
|
2190
|
+
timestamp: 1000n,
|
|
2191
|
+
rapFlag: true,
|
|
2192
|
+
source: "multicast",
|
|
2193
|
+
packetSequenceNumber: testPacketSequence(1, 1)
|
|
2194
|
+
});
|
|
2195
|
+
expectRejection(() => reassembler.addFragment({
|
|
2196
|
+
fragmentationIndicator: 1,
|
|
2197
|
+
mpuSequenceNumber: 1,
|
|
2198
|
+
fragmentCounter: 1,
|
|
2199
|
+
itemId: 2,
|
|
2200
|
+
data: new Uint8Array([187]),
|
|
2201
|
+
timestamp: 1000n,
|
|
2202
|
+
rapFlag: true,
|
|
2203
|
+
source: "moq",
|
|
2204
|
+
packetSequenceNumber: testPacketSequence(1, 1)
|
|
2205
|
+
}), "overlapping-data-unit");
|
|
2206
|
+
const result = reassembler.addFragment({
|
|
2207
|
+
fragmentationIndicator: 3,
|
|
2208
|
+
mpuSequenceNumber: 1,
|
|
2209
|
+
fragmentCounter: 0,
|
|
2210
|
+
data: new Uint8Array([204]),
|
|
2211
|
+
timestamp: 1000n,
|
|
2212
|
+
rapFlag: false,
|
|
2213
|
+
packetSequenceNumber: testPacketSequence(1, 0)
|
|
2214
|
+
});
|
|
2215
|
+
expect(result).not.toBeNull();
|
|
2216
|
+
expect(result.data).toEqual(new Uint8Array([170, 204]));
|
|
2217
|
+
expect(result.fragmentCount).toBe(2);
|
|
2218
|
+
expect(result.source).toBe("multicast");
|
|
2219
|
+
});
|
|
2220
|
+
});
|
|
2221
|
+
test("should retain an assembly with a gap in fragment counters", () => {
|
|
2222
|
+
const reassembler = new MFUReassembler({ timeoutMs: 1e3 });
|
|
2223
|
+
reassembler.addFragment({
|
|
2224
|
+
fragmentationIndicator: 1,
|
|
2225
|
+
mpuSequenceNumber: 1,
|
|
2226
|
+
fragmentCounter: 3,
|
|
2227
|
+
itemId: 1,
|
|
2228
|
+
data: new Uint8Array([170]),
|
|
2229
|
+
timestamp: 1000n,
|
|
2230
|
+
rapFlag: true,
|
|
2231
|
+
packetSequenceNumber: testPacketSequence(1, 3)
|
|
2232
|
+
});
|
|
2233
|
+
expect(reassembler.addFragment({
|
|
2234
|
+
fragmentationIndicator: 2,
|
|
2235
|
+
mpuSequenceNumber: 1,
|
|
2236
|
+
fragmentCounter: 1,
|
|
2237
|
+
data: new Uint8Array([204]),
|
|
2238
|
+
timestamp: 1000n,
|
|
2239
|
+
rapFlag: false,
|
|
2240
|
+
packetSequenceNumber: testPacketSequence(1, 1)
|
|
2241
|
+
})).toBeNull();
|
|
2242
|
+
expect(reassembler.addFragment({
|
|
2243
|
+
fragmentationIndicator: 3,
|
|
2244
|
+
mpuSequenceNumber: 1,
|
|
2245
|
+
fragmentCounter: 0,
|
|
2246
|
+
data: new Uint8Array([221]),
|
|
2247
|
+
timestamp: 1000n,
|
|
2248
|
+
rapFlag: false,
|
|
2249
|
+
packetSequenceNumber: testPacketSequence(1, 0)
|
|
2250
|
+
})).toBeNull();
|
|
2251
|
+
expect(reassembler.pendingCount).toBe(1);
|
|
2252
|
+
expect(reassembler.getStatus().pendingFragments).toBe(3);
|
|
2253
|
+
});
|
|
2254
|
+
test("accepts a reordered countdown and rejects a stale duplicate", () => {
|
|
2255
|
+
const reassembler = new MFUReassembler({ timeoutMs: 1e3 });
|
|
2256
|
+
reassembler.addFragment({
|
|
2257
|
+
fragmentationIndicator: 1,
|
|
2258
|
+
mpuSequenceNumber: 1,
|
|
2259
|
+
fragmentCounter: 3,
|
|
2260
|
+
itemId: 1,
|
|
2261
|
+
data: new Uint8Array([170]),
|
|
2262
|
+
timestamp: 1000n,
|
|
2263
|
+
rapFlag: true,
|
|
2264
|
+
packetSequenceNumber: testPacketSequence(1, 3)
|
|
2265
|
+
});
|
|
2266
|
+
expect(reassembler.addFragment({
|
|
2267
|
+
fragmentationIndicator: 2,
|
|
2268
|
+
mpuSequenceNumber: 1,
|
|
2269
|
+
fragmentCounter: 1,
|
|
2270
|
+
data: new Uint8Array([204]),
|
|
2271
|
+
timestamp: 1000n,
|
|
2272
|
+
rapFlag: false,
|
|
2273
|
+
packetSequenceNumber: testPacketSequence(1, 1)
|
|
2274
|
+
})).toBeNull();
|
|
2275
|
+
expect(reassembler.addFragment({
|
|
2276
|
+
fragmentationIndicator: 2,
|
|
2277
|
+
mpuSequenceNumber: 1,
|
|
2278
|
+
fragmentCounter: 2,
|
|
2279
|
+
data: new Uint8Array([187]),
|
|
2280
|
+
timestamp: 1000n,
|
|
2281
|
+
rapFlag: false,
|
|
2282
|
+
packetSequenceNumber: testPacketSequence(1, 2)
|
|
2283
|
+
})).toBeNull();
|
|
2284
|
+
expectRejection(() => reassembler.addFragment({
|
|
2285
|
+
fragmentationIndicator: 2,
|
|
2286
|
+
mpuSequenceNumber: 1,
|
|
2287
|
+
fragmentCounter: 1,
|
|
2288
|
+
data: new Uint8Array([204]),
|
|
2289
|
+
timestamp: 1001n,
|
|
2290
|
+
rapFlag: false,
|
|
2291
|
+
packetSequenceNumber: testPacketSequence(1, 1)
|
|
2292
|
+
}), "fragment-counter-mismatch");
|
|
2293
|
+
const result = reassembler.addFragment({
|
|
2294
|
+
fragmentationIndicator: 3,
|
|
2295
|
+
mpuSequenceNumber: 1,
|
|
2296
|
+
fragmentCounter: 0,
|
|
2297
|
+
data: new Uint8Array([221]),
|
|
2298
|
+
timestamp: 1002n,
|
|
2299
|
+
rapFlag: false,
|
|
2300
|
+
packetSequenceNumber: testPacketSequence(1, 0)
|
|
2301
|
+
});
|
|
2302
|
+
expect(result).not.toBeNull();
|
|
2303
|
+
expect(result.data).toEqual(new Uint8Array([170, 187, 204, 221]));
|
|
2304
|
+
expect(result.fragmentCount).toBe(4);
|
|
2305
|
+
});
|
|
2306
|
+
test("rejects an overlapping first without discarding queued continuations", () => {
|
|
2307
|
+
const reassembler = new MFUReassembler({ timeoutMs: 1e3 });
|
|
2308
|
+
const fragment = (fragmentationIndicator, fragmentCounter, byte) => ({
|
|
2309
|
+
fragmentationIndicator,
|
|
2310
|
+
mpuSequenceNumber: 2,
|
|
2311
|
+
fragmentCounter,
|
|
2312
|
+
data: new Uint8Array([byte]),
|
|
2313
|
+
timestamp: 1000n,
|
|
2314
|
+
rapFlag: fragmentationIndicator === 1,
|
|
2315
|
+
packetSequenceNumber: testPacketSequence(2, fragmentCounter),
|
|
2316
|
+
...fragmentationIndicator === 1 ? { itemId: 9 } : {}
|
|
2317
|
+
});
|
|
2318
|
+
expect(reassembler.addFragment(fragment(1, 3, 170))).toBeNull();
|
|
2319
|
+
expect(reassembler.addFragment(fragment(2, 1, 204))).toBeNull();
|
|
2320
|
+
expectRejection(
|
|
2321
|
+
() => reassembler.addFragment(fragment(1, 1, 238)),
|
|
2322
|
+
"overlapping-data-unit"
|
|
2323
|
+
);
|
|
2324
|
+
expect(reassembler.addFragment(fragment(2, 2, 187))).toBeNull();
|
|
2325
|
+
expect(reassembler.addFragment(fragment(3, 0, 221))?.data).toEqual(
|
|
2326
|
+
new Uint8Array([170, 187, 204, 221])
|
|
2327
|
+
);
|
|
2328
|
+
});
|
|
2329
|
+
test("rejects a duplicate emitted intrinsic identity", () => {
|
|
2330
|
+
const reassembler = new MFUReassembler({ timeoutMs: 1e3 });
|
|
2331
|
+
expect(reassembler.addFragment({
|
|
2332
|
+
fragmentationIndicator: 1,
|
|
2333
|
+
mpuSequenceNumber: 42,
|
|
2334
|
+
fragmentCounter: 1,
|
|
2335
|
+
itemId: 1,
|
|
2336
|
+
data: new Uint8Array([170]),
|
|
2337
|
+
timestamp: 1000n,
|
|
2338
|
+
rapFlag: true,
|
|
2339
|
+
packetSequenceNumber: testPacketSequence(42, 1)
|
|
2340
|
+
})).toBeNull();
|
|
2341
|
+
expect(reassembler.addFragment({
|
|
2342
|
+
fragmentationIndicator: 3,
|
|
2343
|
+
mpuSequenceNumber: 42,
|
|
2344
|
+
fragmentCounter: 0,
|
|
2345
|
+
data: new Uint8Array([171]),
|
|
2346
|
+
timestamp: 1001n,
|
|
2347
|
+
rapFlag: false,
|
|
2348
|
+
packetSequenceNumber: testPacketSequence(42, 0)
|
|
2349
|
+
})).not.toBeNull();
|
|
2350
|
+
expect(reassembler.addFragment({
|
|
2351
|
+
fragmentationIndicator: 3,
|
|
2352
|
+
mpuSequenceNumber: 42,
|
|
2353
|
+
fragmentCounter: 0,
|
|
2354
|
+
data: new Uint8Array([188]),
|
|
2355
|
+
timestamp: 1002n,
|
|
2356
|
+
rapFlag: false,
|
|
2357
|
+
packetSequenceNumber: testPacketSequence(42, 0, 10)
|
|
2358
|
+
})).toBeNull();
|
|
2359
|
+
expect(reassembler.pendingCount).toBe(1);
|
|
2360
|
+
expect(reassembler.addFragment({
|
|
2361
|
+
fragmentationIndicator: 1,
|
|
2362
|
+
mpuSequenceNumber: 42,
|
|
2363
|
+
fragmentCounter: 1,
|
|
2364
|
+
itemId: 1,
|
|
2365
|
+
data: new Uint8Array([187]),
|
|
2366
|
+
timestamp: 2000n,
|
|
2367
|
+
rapFlag: true,
|
|
2368
|
+
packetSequenceNumber: testPacketSequence(42, 1, 10)
|
|
2369
|
+
})).toBeNull();
|
|
2370
|
+
expect(reassembler.pendingCount).toBe(0);
|
|
2371
|
+
const result = reassembler.addFragment({
|
|
2372
|
+
fragmentationIndicator: 1,
|
|
2373
|
+
mpuSequenceNumber: 43,
|
|
2374
|
+
fragmentCounter: 1,
|
|
2375
|
+
itemId: 1,
|
|
2376
|
+
data: new Uint8Array([204]),
|
|
2377
|
+
timestamp: 2000n,
|
|
2378
|
+
rapFlag: false,
|
|
2379
|
+
packetSequenceNumber: testPacketSequence(43, 1)
|
|
2380
|
+
});
|
|
2381
|
+
expect(result).toBeNull();
|
|
2382
|
+
expect(reassembler.addFragment({
|
|
2383
|
+
fragmentationIndicator: 3,
|
|
2384
|
+
mpuSequenceNumber: 43,
|
|
2385
|
+
fragmentCounter: 0,
|
|
2386
|
+
data: new Uint8Array([221]),
|
|
2387
|
+
timestamp: 2001n,
|
|
2388
|
+
rapFlag: false,
|
|
2389
|
+
packetSequenceNumber: testPacketSequence(43, 0)
|
|
2390
|
+
})?.data).toEqual(new Uint8Array([204, 221]));
|
|
2391
|
+
});
|
|
2392
|
+
test("does not create active state for an emitted identity duplicate", () => {
|
|
2393
|
+
const reassembler = new MFUReassembler({ timeoutMs: 1e3 });
|
|
2394
|
+
expect(reassembler.addFragment({
|
|
2395
|
+
fragmentationIndicator: 1,
|
|
2396
|
+
mpuSequenceNumber: 50,
|
|
2397
|
+
fragmentCounter: 1,
|
|
2398
|
+
movieFragmentSequenceNumber: 10,
|
|
2399
|
+
sampleNumber: 18,
|
|
2400
|
+
sampleOffset: 0,
|
|
2401
|
+
data: new Uint8Array([161]),
|
|
2402
|
+
timestamp: 1000n,
|
|
2403
|
+
rapFlag: true,
|
|
2404
|
+
packetSequenceNumber: testPacketSequence(50, 1)
|
|
2405
|
+
})).toBeNull();
|
|
2406
|
+
expect(reassembler.addFragment({
|
|
2407
|
+
fragmentationIndicator: 3,
|
|
2408
|
+
mpuSequenceNumber: 50,
|
|
2409
|
+
fragmentCounter: 0,
|
|
2410
|
+
data: new Uint8Array([178]),
|
|
2411
|
+
timestamp: 1000n,
|
|
2412
|
+
rapFlag: false,
|
|
2413
|
+
packetSequenceNumber: testPacketSequence(50, 0)
|
|
2414
|
+
})?.data).toEqual(new Uint8Array([161, 178]));
|
|
2415
|
+
expect(reassembler.pendingCount).toBe(0);
|
|
2416
|
+
expect(reassembler.addFragment({
|
|
2417
|
+
fragmentationIndicator: 1,
|
|
2418
|
+
mpuSequenceNumber: 50,
|
|
2419
|
+
fragmentCounter: 1,
|
|
2420
|
+
movieFragmentSequenceNumber: 10,
|
|
2421
|
+
sampleNumber: 18,
|
|
2422
|
+
sampleOffset: 0,
|
|
2423
|
+
data: new Uint8Array([161]),
|
|
2424
|
+
timestamp: 2000n,
|
|
2425
|
+
rapFlag: true,
|
|
2426
|
+
packetSequenceNumber: testPacketSequence(50, 1, 10)
|
|
2427
|
+
})).toBeNull();
|
|
2428
|
+
expect(reassembler.pendingCount).toBe(0);
|
|
2429
|
+
});
|
|
2430
|
+
test("flush should clear emitted set and allow re-assembly", () => {
|
|
2431
|
+
const reassembler = new MFUReassembler({ timeoutMs: 1e3 });
|
|
2432
|
+
const first = reassembler.addFragment({
|
|
2433
|
+
fragmentationIndicator: 1,
|
|
2434
|
+
mpuSequenceNumber: 42,
|
|
2435
|
+
fragmentCounter: 1,
|
|
2436
|
+
itemId: 1,
|
|
2437
|
+
data: new Uint8Array([170]),
|
|
2438
|
+
timestamp: 1000n,
|
|
2439
|
+
rapFlag: true,
|
|
2440
|
+
packetSequenceNumber: testPacketSequence(42, 1)
|
|
2441
|
+
});
|
|
2442
|
+
expect(first).toBeNull();
|
|
2443
|
+
const assembled = reassembler.addFragment({
|
|
2444
|
+
fragmentationIndicator: 3,
|
|
2445
|
+
mpuSequenceNumber: 42,
|
|
2446
|
+
fragmentCounter: 0,
|
|
2447
|
+
data: new Uint8Array([187]),
|
|
2448
|
+
timestamp: 1000n,
|
|
2449
|
+
rapFlag: true,
|
|
2450
|
+
packetSequenceNumber: testPacketSequence(42, 0)
|
|
2451
|
+
});
|
|
2452
|
+
expect(assembled).not.toBeNull();
|
|
2453
|
+
expect(reassembler.addFragment({
|
|
2454
|
+
fragmentationIndicator: 1,
|
|
2455
|
+
mpuSequenceNumber: 42,
|
|
2456
|
+
fragmentCounter: 1,
|
|
2457
|
+
itemId: 1,
|
|
2458
|
+
data: new Uint8Array([204]),
|
|
2459
|
+
timestamp: 1000n,
|
|
2460
|
+
rapFlag: true,
|
|
2461
|
+
packetSequenceNumber: testPacketSequence(42, 1, 10)
|
|
2462
|
+
})).toBeNull();
|
|
2463
|
+
reassembler.flush();
|
|
2464
|
+
const r2 = reassembler.addFragment({
|
|
2465
|
+
fragmentationIndicator: 1,
|
|
2466
|
+
mpuSequenceNumber: 42,
|
|
2467
|
+
fragmentCounter: 1,
|
|
2468
|
+
itemId: 1,
|
|
2469
|
+
data: new Uint8Array([221]),
|
|
2470
|
+
timestamp: 1000n,
|
|
2471
|
+
rapFlag: true,
|
|
2472
|
+
packetSequenceNumber: testPacketSequence(42, 1)
|
|
2473
|
+
});
|
|
2474
|
+
expect(r2).toBeNull();
|
|
2475
|
+
const r3 = reassembler.addFragment({
|
|
2476
|
+
fragmentationIndicator: 3,
|
|
2477
|
+
mpuSequenceNumber: 42,
|
|
2478
|
+
fragmentCounter: 0,
|
|
2479
|
+
data: new Uint8Array([238]),
|
|
2480
|
+
timestamp: 1000n,
|
|
2481
|
+
rapFlag: true,
|
|
2482
|
+
packetSequenceNumber: testPacketSequence(42, 0)
|
|
2483
|
+
});
|
|
2484
|
+
expect(r3).not.toBeNull();
|
|
2485
|
+
expect(r3.data).toEqual(new Uint8Array([221, 238]));
|
|
2486
|
+
});
|
|
2487
|
+
describe("createFragment", () => {
|
|
2488
|
+
test("should create fragment from complete timed MMTP packet", () => {
|
|
2489
|
+
const packet = new Uint8Array(12 + 8 + 14 + 5);
|
|
2490
|
+
packet[0] = 1;
|
|
2491
|
+
packet[1] = 0;
|
|
2492
|
+
const view = new DataView(packet.buffer);
|
|
2493
|
+
view.setUint16(2, 1, false);
|
|
2494
|
+
view.setUint32(4, 42, false);
|
|
2495
|
+
view.setUint32(8, 100, false);
|
|
2496
|
+
view.setUint16(12, 25, false);
|
|
2497
|
+
packet[14] = 40;
|
|
2498
|
+
packet[15] = 0;
|
|
2499
|
+
view.setUint32(16, 999, false);
|
|
2500
|
+
view.setUint32(20, 7, false);
|
|
2501
|
+
view.setUint32(24, 1, false);
|
|
2502
|
+
packet[34] = 222;
|
|
2503
|
+
packet[35] = 173;
|
|
2504
|
+
packet[36] = 190;
|
|
2505
|
+
packet[37] = 239;
|
|
2506
|
+
packet[38] = 1;
|
|
2507
|
+
const frag = createFragment(packet);
|
|
2508
|
+
expect(frag.mpuSequenceNumber).toBe(999);
|
|
2509
|
+
expect(frag.fragmentationIndicator).toBe(0);
|
|
2510
|
+
expect(frag.rapFlag).toBe(true);
|
|
2511
|
+
expect(frag.movieFragmentSequenceNumber).toBe(7);
|
|
2512
|
+
expect(frag.sampleNumber).toBe(1);
|
|
2513
|
+
expect(frag.sampleOffset).toBe(0);
|
|
2514
|
+
expect(frag.data.length).toBe(5);
|
|
2515
|
+
});
|
|
2516
|
+
test("reads Item_ID from a non-timed MFU DU header", () => {
|
|
2517
|
+
const packet = new Uint8Array(12 + 8 + 4 + 2);
|
|
2518
|
+
const view = new DataView(packet.buffer);
|
|
2519
|
+
view.setUint16(2, 1, false);
|
|
2520
|
+
view.setUint32(4, 42, false);
|
|
2521
|
+
view.setUint32(8, 100, false);
|
|
2522
|
+
view.setUint16(12, 12, false);
|
|
2523
|
+
packet[14] = 32;
|
|
2524
|
+
view.setUint32(16, 999, false);
|
|
2525
|
+
view.setUint32(20, 16909060, false);
|
|
2526
|
+
packet.set([222, 173], 24);
|
|
2527
|
+
const fragment = createFragment(packet);
|
|
2528
|
+
expect(fragment.itemId).toBe(16909060);
|
|
2529
|
+
expect(fragment.data).toEqual(new Uint8Array([222, 173]));
|
|
2530
|
+
});
|
|
2531
|
+
});
|
|
2532
|
+
describe("processMMTPWithReassembly", () => {
|
|
2533
|
+
test("should skip FEC repair packets", () => {
|
|
2534
|
+
const reassembler = new MFUReassembler({ timeoutMs: 1e3 });
|
|
2535
|
+
const packet = new Uint8Array(12 + 8 + 14 + 4);
|
|
2536
|
+
packet[0] = 16;
|
|
2537
|
+
const result = processMMTPWithReassembly(packet, reassembler);
|
|
2538
|
+
expect(result).toBeNull();
|
|
2539
|
+
});
|
|
2540
|
+
test("should return reassembled MFU for complete packet", () => {
|
|
2541
|
+
const reassembler = new MFUReassembler({ timeoutMs: 1e3 });
|
|
2542
|
+
const packet = new Uint8Array(12 + 8 + 14 + 4);
|
|
2543
|
+
packet[0] = 1;
|
|
2544
|
+
packet[1] = 0;
|
|
2545
|
+
const view = new DataView(packet.buffer);
|
|
2546
|
+
view.setUint16(2, 1, false);
|
|
2547
|
+
view.setUint32(4, 100, false);
|
|
2548
|
+
view.setUint32(8, 1, false);
|
|
2549
|
+
view.setUint16(12, 24, false);
|
|
2550
|
+
packet[14] = 40;
|
|
2551
|
+
view.setUint32(16, 42, false);
|
|
2552
|
+
view.setUint32(20, 7, false);
|
|
2553
|
+
view.setUint32(24, 1, false);
|
|
2554
|
+
packet[34] = 0;
|
|
2555
|
+
packet[35] = 0;
|
|
2556
|
+
packet[36] = 0;
|
|
2557
|
+
packet[37] = 1;
|
|
2558
|
+
const result = processMMTPWithReassembly(packet, reassembler);
|
|
2559
|
+
expect(result).not.toBeNull();
|
|
2560
|
+
expect(result.mpuSequenceNumber).toBe(42);
|
|
2561
|
+
expect(result.rapFlag).toBe(true);
|
|
2562
|
+
});
|
|
2563
|
+
});
|
|
2564
|
+
describe("strict delivery provenance", () => {
|
|
2565
|
+
test("folds to multicast only when every fragment is explicitly multicast", () => {
|
|
2566
|
+
expect(deliveryProvenanceOf([{ source: "multicast" }, { source: "multicast" }])).toBe("multicast");
|
|
2567
|
+
});
|
|
2568
|
+
test("a single moq fragment makes the whole frame mixed", () => {
|
|
2569
|
+
expect(deliveryProvenanceOf([{ source: "multicast" }, { source: "moq" }])).toBe("mixed");
|
|
2570
|
+
expect(deliveryProvenanceOf([{ source: "moq" }, { source: "multicast" }])).toBe("mixed");
|
|
2571
|
+
});
|
|
2572
|
+
test("an unattributed fragment degrades multicast to unknown, never to proven", () => {
|
|
2573
|
+
expect(deliveryProvenanceOf([{ source: "multicast" }, {}])).toBe("unknown");
|
|
2574
|
+
expect(deliveryProvenanceOf([{}])).toBe("unknown");
|
|
2575
|
+
});
|
|
2576
|
+
test("mixed absorbs unknown \u2014 a moq byte is worse news than a missing label", () => {
|
|
2577
|
+
expect(deliveryProvenanceOf([{ source: "moq" }, {}])).toBe("mixed");
|
|
2578
|
+
expect(deliveryProvenanceOf([{}, { source: "moq" }])).toBe("mixed");
|
|
2579
|
+
});
|
|
2580
|
+
test("an empty fragment set proves nothing", () => {
|
|
2581
|
+
expect(deliveryProvenanceOf([])).toBe("unknown");
|
|
2582
|
+
});
|
|
2583
|
+
test("the fold never upgrades a verdict", () => {
|
|
2584
|
+
expect(foldDeliveryProvenance("mixed", "multicast")).toBe("mixed");
|
|
2585
|
+
expect(foldDeliveryProvenance("unknown", "multicast")).toBe("unknown");
|
|
2586
|
+
expect(foldDeliveryProvenance("unknown", "moq")).toBe("mixed");
|
|
2587
|
+
});
|
|
2588
|
+
test("a multicast head with a non-multicast tail is NOT multicast-exclusive", () => {
|
|
2589
|
+
const reassembler = new MFUReassembler({ timeoutMs: 1e3 });
|
|
2590
|
+
reassembler.addFragment({
|
|
2591
|
+
fragmentationIndicator: 1,
|
|
2592
|
+
mpuSequenceNumber: 1,
|
|
2593
|
+
fragmentCounter: 1,
|
|
2594
|
+
itemId: 1,
|
|
2595
|
+
data: new Uint8Array([170]),
|
|
2596
|
+
timestamp: 1000n,
|
|
2597
|
+
rapFlag: true,
|
|
2598
|
+
source: "multicast",
|
|
2599
|
+
packetSequenceNumber: testPacketSequence(1, 1)
|
|
2600
|
+
});
|
|
2601
|
+
const result = reassembler.addFragment({
|
|
2602
|
+
fragmentationIndicator: 3,
|
|
2603
|
+
mpuSequenceNumber: 1,
|
|
2604
|
+
fragmentCounter: 0,
|
|
2605
|
+
data: new Uint8Array([204]),
|
|
2606
|
+
timestamp: 1000n,
|
|
2607
|
+
rapFlag: false,
|
|
2608
|
+
source: "moq",
|
|
2609
|
+
packetSequenceNumber: testPacketSequence(1, 0)
|
|
2610
|
+
});
|
|
2611
|
+
expect(result).not.toBeNull();
|
|
2612
|
+
expect(result.fragmentCount).toBe(2);
|
|
2613
|
+
expect(result.source).toBe("multicast");
|
|
2614
|
+
expect(result.deliveryProvenance).toBe("mixed");
|
|
2615
|
+
});
|
|
2616
|
+
test("an all-multicast fragmented frame is multicast-exclusive", () => {
|
|
2617
|
+
const reassembler = new MFUReassembler({ timeoutMs: 1e3 });
|
|
2618
|
+
reassembler.addFragment({
|
|
2619
|
+
fragmentationIndicator: 1,
|
|
2620
|
+
mpuSequenceNumber: 2,
|
|
2621
|
+
fragmentCounter: 1,
|
|
2622
|
+
itemId: 1,
|
|
2623
|
+
data: new Uint8Array([170]),
|
|
2624
|
+
timestamp: 2000n,
|
|
2625
|
+
rapFlag: true,
|
|
2626
|
+
source: "multicast",
|
|
2627
|
+
packetSequenceNumber: testPacketSequence(2, 1)
|
|
2628
|
+
});
|
|
2629
|
+
const result = reassembler.addFragment({
|
|
2630
|
+
fragmentationIndicator: 3,
|
|
2631
|
+
mpuSequenceNumber: 2,
|
|
2632
|
+
fragmentCounter: 0,
|
|
2633
|
+
data: new Uint8Array([204]),
|
|
2634
|
+
timestamp: 2000n,
|
|
2635
|
+
rapFlag: false,
|
|
2636
|
+
source: "multicast",
|
|
2637
|
+
packetSequenceNumber: testPacketSequence(2, 0)
|
|
2638
|
+
});
|
|
2639
|
+
expect(result).not.toBeNull();
|
|
2640
|
+
expect(result.deliveryProvenance).toBe("multicast");
|
|
2641
|
+
});
|
|
2642
|
+
test("an unlabelled complete frame proves nothing", () => {
|
|
2643
|
+
const reassembler = new MFUReassembler({ timeoutMs: 1e3 });
|
|
2644
|
+
const result = reassembler.addFragment({
|
|
2645
|
+
fragmentationIndicator: 0,
|
|
2646
|
+
mpuSequenceNumber: 3,
|
|
2647
|
+
fragmentCounter: 0,
|
|
2648
|
+
itemId: 1,
|
|
2649
|
+
data: new Uint8Array([1]),
|
|
2650
|
+
timestamp: 3000n,
|
|
2651
|
+
rapFlag: true
|
|
2652
|
+
});
|
|
2653
|
+
expect(result).not.toBeNull();
|
|
2654
|
+
expect(result.deliveryProvenance).toBe("unknown");
|
|
2655
|
+
});
|
|
2656
|
+
});
|
|
2657
|
+
}
|
|
2658
|
+
export {
|
|
2659
|
+
MFUReassembler,
|
|
2660
|
+
MFU_REASSEMBLY_ERROR_CODES,
|
|
2661
|
+
MfuReassemblyError,
|
|
2662
|
+
createFragment,
|
|
2663
|
+
deliveryProvenanceOf,
|
|
2664
|
+
foldDeliveryProvenance,
|
|
2665
|
+
isMfuReassemblyErrorCode,
|
|
2666
|
+
processMMTPWithReassembly
|
|
2667
|
+
};
|
|
2668
|
+
//# sourceMappingURL=mfu-reassembler.js.map
|