@blockcast/mmt-container 0.5.0 → 1.0.0-main.3cb7359ea316
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 +21 -16
- package/dist/browser-runtime.js +1024 -1078
- package/dist/browser-runtime.js.map +4 -4
- package/dist/carp-catalog.d.ts +33 -87
- package/dist/carp-catalog.d.ts.map +1 -1
- package/dist/carp-catalog.js +12 -434
- package/dist/carp-catalog.js.map +4 -4
- package/dist/catalog-fec-params.d.ts +48 -161
- package/dist/catalog-fec-params.d.ts.map +1 -1
- package/dist/catalog-fec-params.js +567 -592
- package/dist/catalog-fec-params.js.map +4 -4
- package/dist/catalog-network-source.js +6 -2
- package/dist/catalog-network-source.js.map +3 -3
- package/dist/delivery-catalog.js +12 -4
- package/dist/delivery-catalog.js.map +4 -4
- package/dist/delta-fallback-config.d.ts +21 -26
- package/dist/delta-fallback-config.d.ts.map +1 -1
- package/dist/delta-fallback-config.js +582 -24
- package/dist/delta-fallback-config.js.map +4 -4
- package/dist/fec-manager.js +6 -2
- package/dist/fec-manager.js.map +3 -3
- package/dist/fec-state.d.ts +5 -43
- package/dist/fec-state.d.ts.map +1 -1
- package/dist/fec-state.js +529 -34
- package/dist/fec-state.js.map +4 -4
- package/dist/fec-worker-setup.js +6 -2
- package/dist/fec-worker-setup.js.map +3 -3
- package/dist/held-layer-admission.js +6 -2
- package/dist/held-layer-admission.js.map +3 -3
- package/dist/index.d.ts +3 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -39
- package/dist/index.js.map +2 -2
- package/dist/iso-block-locator.js +6 -2
- package/dist/iso-block-locator.js.map +3 -3
- package/dist/mfu-reassembler.js +6 -2
- package/dist/mfu-reassembler.js.map +3 -3
- package/dist/mmtp-container-parser.js +6 -2
- package/dist/mmtp-container-parser.js.map +3 -3
- package/dist/mmtp-container-parser.no-modules.js +551 -452
- package/dist/mmtp-container-parser.no-modules.js.map +4 -4
- package/dist/mmtp-decode-pipeline.js +6 -2
- package/dist/mmtp-decode-pipeline.js.map +3 -3
- package/dist/mmtp-fec-session.d.ts +7 -6
- package/dist/mmtp-fec-session.d.ts.map +1 -1
- package/dist/mmtp-fec-session.js +13 -9
- package/dist/mmtp-fec-session.js.map +3 -3
- package/dist/mmtp-router.d.ts +7 -16
- package/dist/mmtp-router.d.ts.map +1 -1
- package/dist/mmtp-router.js +19 -29
- package/dist/mmtp-router.js.map +3 -3
- package/dist/multicast-auth-catalog.js +7 -3
- package/dist/multicast-auth-catalog.js.map +3 -3
- package/dist/multicast-input.js +7 -3
- package/dist/multicast-input.js.map +3 -3
- package/dist/multicast-manager.d.ts +29 -32
- package/dist/multicast-manager.d.ts.map +1 -1
- package/dist/multicast-manager.js +682 -253
- package/dist/multicast-manager.js.map +4 -4
- package/dist/multicast-transport.d.ts +23 -2
- package/dist/multicast-transport.d.ts.map +1 -1
- package/dist/multicast-transport.js +57 -16
- package/dist/multicast-transport.js.map +4 -4
- package/dist/multicast-unwrap.js +6 -2
- package/dist/multicast-unwrap.js.map +3 -3
- package/dist/no-modules.js +551 -452
- package/dist/no-modules.js.map +4 -4
- package/dist/phase14-catalog-override.d.ts +10 -5
- package/dist/phase14-catalog-override.d.ts.map +1 -1
- package/dist/phase14-catalog-override.js +35 -11
- package/dist/phase14-catalog-override.js.map +2 -2
- package/dist/repair-fetch.d.ts +3 -1
- package/dist/repair-fetch.d.ts.map +1 -1
- package/dist/repair-fetch.js +16 -3
- package/dist/repair-fetch.js.map +3 -3
- package/dist/repair-layers.d.ts +0 -123
- package/dist/repair-layers.d.ts.map +1 -1
- package/dist/repair-layers.js +1 -349
- package/dist/repair-layers.js.map +3 -3
- package/dist/signaling.js +6 -2
- package/dist/signaling.js.map +3 -3
- package/dist/timestamp-codec.js +6 -2
- package/dist/timestamp-codec.js.map +3 -3
- package/dist/wasm-mfu-reassembler.js +6 -2
- package/dist/wasm-mfu-reassembler.js.map +3 -3
- package/dist/wasm-mmtp-container-parser.js +6 -2
- package/dist/wasm-mmtp-container-parser.js.map +3 -3
- package/package.json +9 -5
- package/schemas/catalog.schema.json +53 -58
- package/src/carp-catalog.ts +39 -229
- package/src/catalog-fec-params.ts +130 -493
- package/src/delta-fallback-config.ts +63 -50
- package/src/fec-state.ts +7 -62
- package/src/index.ts +10 -30
- package/src/mmtp-fec-session.ts +11 -10
- package/src/mmtp-router.ts +14 -29
- package/src/multicast-manager.ts +89 -121
- package/src/multicast-transport.ts +65 -12
- package/src/phase14-catalog-override.ts +67 -16
- package/src/repair-fetch.ts +27 -3
- package/src/repair-layers.ts +7 -520
- package/test-vectors/catalog/README.md +39 -67
- package/test-vectors/catalog/fec-01-runtime/README.md +15 -0
- package/test-vectors/catalog/{negative/fec-block-profile-retired.json → fec-01-runtime/fec-iso-ssbg1.json} +1 -1
- package/test-vectors/catalog/fec-01-runtime/fec-layered-repair.json +106 -0
- package/test-vectors/catalog/{negative/fec-block-profile-unknown.json → fec-01-runtime/fec-multicast.json} +2 -1
- package/test-vectors/catalog/fec-01-runtime/multicast-native-ssm-no-network-source.json +44 -0
- package/test-vectors/catalog/negative/fec-block-profile-missing.json +24 -11
- package/test-vectors/catalog/negative/fec-iso-symbol-size-below-floor.json +13 -8
- package/test-vectors/catalog/negative/fec-reed-solomon-iso-symbol-size-13.json +13 -8
- package/test-vectors/catalog/negative/fec-repair-layer-geometry.json +20 -10
- package/test-vectors/catalog/negative/fec-repair-layer-missing-symbols.json +20 -10
- package/test-vectors/catalog/negative/fec-repair-overlay-source-symbols-zero.json +25 -17
- package/test-vectors/catalog/negative/fec-repair-symbols-zero.json +13 -8
- package/test-vectors/catalog/negative/fec-source-symbols-zero.json +13 -8
- package/test-vectors/catalog/negative/fec-symbol-size-zero.json +13 -8
- package/test-vectors/catalog/negative/raptorq-source-symbols-over-max.json +18 -9
- package/test-vectors/catalog/negative/raptorq-unaligned-symbol.json +13 -8
- package/test-vectors/catalog/positive/fec-algorithm-unimplemented.json +14 -9
- package/test-vectors/catalog/positive/fec-iso-ssbg1.json +25 -12
- package/test-vectors/catalog/positive/fec-layered-repair.json +79 -35
- package/test-vectors/catalog/positive/fec-multicast.json +26 -13
- package/test-vectors/catalog/positive/fec-reed-solomon-layered.json +29 -18
- package/test-vectors/catalog/positive/multicast-draft01-4-1-example.json +28 -20
- package/test-vectors/catalog/positive/multicast-draft01-7-2-6-example.json +14 -10
- package/test-vectors/catalog/positive/multicast-native-ssm-no-network-source.json +22 -12
- package/test-vectors/catalog/section-codes.json +13 -22
- package/test-vectors/catalog-timing.json +2 -2
- package/test-vectors/catalog-validation.json +30 -243
- package/test-vectors/catalog/negative/fec-enhancement-repair-removed.json +0 -28
- package/test-vectors/catalog/negative/fec-reed-solomon-k-plus-p-over-255.json +0 -39
- package/test-vectors/catalog/negative/fec-reed-solomon-overlay-over-255.json +0 -40
- package/test-vectors/catalog/negative/fec-repair-multiple-depends.json +0 -32
- package/test-vectors/catalog/negative/fec-repair-overlay-with-layer.json +0 -34
- package/test-vectors/catalog/negative/fec-repair-priority-out-of-band.json +0 -24
- package/test-vectors/catalog/negative/fec-repair-source-symbols-without-scope.json +0 -33
|
@@ -3,70 +3,83 @@
|
|
|
3
3
|
* {@link MediaRouter.configureDelta} (BLO-45475).
|
|
4
4
|
*
|
|
5
5
|
* Generic client policy selects WHICH group to admit (roles, groups, endpoint
|
|
6
|
-
* availability); this module owns the MMTP/FEC half of that decision —
|
|
7
|
-
*
|
|
6
|
+
* availability); this module owns the MMTP/FEC half of that decision — each
|
|
7
|
+
* track's repair track and the delta block geometry — so no player re-states
|
|
8
8
|
* the catalog FEC schema. It validates and never defaults: a group whose
|
|
9
9
|
* required metadata is absent or contradictory throws, and the caller decides
|
|
10
10
|
* that such a group invalidates the catalog rather than being skipped.
|
|
11
|
+
*
|
|
12
|
+
* FEC comes only from `resolveFecBindings` (BLO-50171), the validator's
|
|
13
|
+
* per-instance result: base and delta each bind their own instance and their
|
|
14
|
+
* own repair track (draft-ramadan-moq-fec-catalog-00 5 rule 2 forbids one
|
|
15
|
+
* Track Name in two `repairTrack` members, so a shared repair track is no FEC
|
|
16
|
+
* on either). The Phase-14 extension keys `deltaFailWindowBlocks` and
|
|
17
|
+
* `deltaProbeIntervalMs` are read from the bound instance objects; the
|
|
18
|
+
* validator ignores them as unrecognized instance keys (catalog-00 3.4).
|
|
11
19
|
*/
|
|
12
20
|
|
|
21
|
+
import { resolveFecBindings, type FecSourceBinding } from '@blockcast/mmt-base'
|
|
13
22
|
import { assertDeltaFallbackConfig, type DeltaFallbackConfig } from './media-router.js'
|
|
14
23
|
|
|
15
|
-
/** The catalog fields this mapping reads from one base or delta track. */
|
|
16
|
-
export interface DeltaFallbackTrack {
|
|
17
|
-
name: string
|
|
18
|
-
framerate?: number
|
|
19
|
-
fec?: {
|
|
20
|
-
repairTrack?: string
|
|
21
|
-
sourceSymbols?: number
|
|
22
|
-
repairSymbols?: number
|
|
23
|
-
interleaveDepthMs?: number
|
|
24
|
-
deltaFailWindowBlocks?: number
|
|
25
|
-
deltaProbeIntervalMs?: number
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
|
|
29
24
|
export interface ResolvedDeltaFallback {
|
|
30
|
-
/** The
|
|
31
|
-
|
|
25
|
+
/** The base track's layer-0 repair track. */
|
|
26
|
+
baseRepairTrackName: string
|
|
27
|
+
/** The delta track's layer-0 repair track. */
|
|
28
|
+
deltaRepairTrackName: string
|
|
32
29
|
config: DeltaFallbackConfig
|
|
33
30
|
}
|
|
34
31
|
|
|
32
|
+
type Obj = Record<string, unknown>
|
|
33
|
+
const recordOf = (v: unknown): Obj | undefined =>
|
|
34
|
+
typeof v === 'object' && v !== null && !Array.isArray(v) ? (v as Obj) : undefined
|
|
35
|
+
|
|
36
|
+
/** RFC 6901 lookup; the binding pointers are the validator's own. */
|
|
37
|
+
function at(root: unknown, pointer: string): Obj | undefined {
|
|
38
|
+
let cur: unknown = root
|
|
39
|
+
for (const raw of pointer.split('/').slice(1)) {
|
|
40
|
+
const key = raw.replace(/~1/g, '/').replace(/~0/g, '~')
|
|
41
|
+
cur = Array.isArray(cur) ? cur[Number(key)] : recordOf(cur)?.[key]
|
|
42
|
+
}
|
|
43
|
+
return recordOf(cur)
|
|
44
|
+
}
|
|
45
|
+
|
|
35
46
|
/**
|
|
36
|
-
* Resolve one switching group
|
|
37
|
-
*
|
|
38
|
-
* not positive, or when the
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
-
* runs, so a group this resolves is a group configureDelta accepts — including
|
|
43
|
-
* the requirement that a probe interval or an interleave depth be present.
|
|
47
|
+
* Resolve one switching group of `catalog` (either dialect). Throws when
|
|
48
|
+
* either track has no `ok` FEC binding (the message carries the validator's
|
|
49
|
+
* code and path), when the base framerate is not positive, or when the
|
|
50
|
+
* assembled config fails {@link assertDeltaFallbackConfig}, the same check
|
|
51
|
+
* configureDelta runs. Fallback-window and probe keys are read from the base
|
|
52
|
+
* instance first, then the delta's; the interleave depth likewise.
|
|
44
53
|
*/
|
|
45
|
-
export function resolveDeltaFallback(
|
|
46
|
-
const
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
)
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
54
|
+
export function resolveDeltaFallback(catalog: unknown, baseName: string, deltaName: string): ResolvedDeltaFallback {
|
|
55
|
+
const bindings = resolveFecBindings(catalog)
|
|
56
|
+
const bound = (name: string, role: string) => {
|
|
57
|
+
const b = bindings.get(name)
|
|
58
|
+
if (!b) throw new Error(`[delta-fallback] ${role} track "${name}" declares no fec`)
|
|
59
|
+
if (b.status !== 'ok')
|
|
60
|
+
throw new Error(`[delta-fallback] ${role} track "${name}" has no FEC: ${b.reason.code} at ${b.reason.path}`)
|
|
61
|
+
const track = at(catalog, b.pointer) ?? {}
|
|
62
|
+
const instance = (Array.isArray(track['fec']) ? track['fec'] : [])
|
|
63
|
+
.map(recordOf)
|
|
64
|
+
.find((o) => o && (o['instanceId'] ?? 0) === b.base.instanceId) ?? {}
|
|
65
|
+
return { b, track, instance }
|
|
66
|
+
}
|
|
67
|
+
const base = bound(baseName, 'base')
|
|
68
|
+
const delta = bound(deltaName, 'delta')
|
|
69
|
+
const framerate = base.track['framerate']
|
|
70
|
+
if (!(typeof framerate === 'number' && Number.isFinite(framerate) && framerate > 0))
|
|
71
|
+
throw new Error(`[delta-fallback] base track "${baseName}" missing positive framerate`)
|
|
72
|
+
const key = (k: string) => (base.instance[k] ?? delta.instance[k]) as number | undefined
|
|
58
73
|
const config: DeltaFallbackConfig = {
|
|
59
|
-
framerate
|
|
60
|
-
sourceSymbols:
|
|
61
|
-
repairSymbols:
|
|
62
|
-
deltaFailWindowBlocks:
|
|
63
|
-
deltaProbeIntervalMs:
|
|
64
|
-
interleaveDepthMs: base.
|
|
74
|
+
framerate,
|
|
75
|
+
sourceSymbols: delta.b.base.sourceSymbols,
|
|
76
|
+
repairSymbols: delta.b.base.repairSymbols,
|
|
77
|
+
deltaFailWindowBlocks: key('deltaFailWindowBlocks'),
|
|
78
|
+
deltaProbeIntervalMs: key('deltaProbeIntervalMs'),
|
|
79
|
+
interleaveDepthMs: base.b.base.interleaveDepthMs ?? delta.b.base.interleaveDepthMs,
|
|
65
80
|
}
|
|
66
|
-
assertDeltaFallbackConfig(config, `[delta-fallback] group ${
|
|
67
|
-
return {
|
|
81
|
+
assertDeltaFallbackConfig(config, `[delta-fallback] group ${baseName}/${deltaName}`)
|
|
82
|
+
return { baseRepairTrackName: repairOf(base.b), deltaRepairTrackName: repairOf(delta.b), config }
|
|
68
83
|
}
|
|
69
84
|
|
|
70
|
-
|
|
71
|
-
return typeof value === 'number' && Number.isFinite(value) && value > 0
|
|
72
|
-
}
|
|
85
|
+
const repairOf = (b: Extract<FecSourceBinding, { status: 'ok' }>) => b.layers[0]!.repairTrackName
|
package/src/fec-state.ts
CHANGED
|
@@ -13,15 +13,7 @@
|
|
|
13
13
|
// timers or elapsed time. The rules live here so the three players cannot
|
|
14
14
|
// disagree about precedence.
|
|
15
15
|
|
|
16
|
-
import {
|
|
17
|
-
catalogFecDeclaresUnconfigurableInstance,
|
|
18
|
-
fecAlgorithmUnimplemented,
|
|
19
|
-
isFecBlockProfile,
|
|
20
|
-
isoBlockSpanUsable,
|
|
21
|
-
} from '@blockcast/mmt-base'
|
|
22
|
-
|
|
23
|
-
/** The optional elapsed-span rule lives in mmt-base beside the other catalog FEC rules. */
|
|
24
|
-
export { isoBlockSpanUsable }
|
|
16
|
+
import { resolveFecBindings } from '@blockcast/mmt-base'
|
|
25
17
|
|
|
26
18
|
/** Smallest positive catalog elapsed span; zero/absence means no elapsed deadline. */
|
|
27
19
|
export const MIN_INTERLEAVE_DEPTH_MS = 1
|
|
@@ -100,61 +92,14 @@ function recordOf(value: unknown): Record<string, unknown> | undefined {
|
|
|
100
92
|
: undefined
|
|
101
93
|
}
|
|
102
94
|
|
|
103
|
-
function isPositiveSafeInteger(value: unknown): boolean {
|
|
104
|
-
return typeof value === 'number' && Number.isSafeInteger(value) && value > 0
|
|
105
|
-
}
|
|
106
|
-
|
|
107
95
|
/**
|
|
108
|
-
* Whether
|
|
109
|
-
*
|
|
110
|
-
*
|
|
111
|
-
*
|
|
112
|
-
* (`fecAlgorithmUnimplemented`, draft-ramadan-moq-fec-01 4), `blockProfile` names the implemented profile
|
|
113
|
-
* `iso-ssbg1-v1` (BLO-35597), and the block carries that profile's usable
|
|
114
|
-
* span field (`isoBlockSpanUsable`); zero/absence still permits in-band FEC. Total: it never throws, and answers false for
|
|
115
|
-
* any other value.
|
|
116
|
-
*
|
|
117
|
-
* `resolveCatalogTrackFec` reads this predicate, so the players' parameters and
|
|
118
|
-
* this state never disagree. Of the blocks this rejects, it returns no FEC
|
|
119
|
-
* geometry only for `algorithm: "none"`. One with any other algorithm is a
|
|
120
|
-
* malformed catalog (`FecCatalogConfig` marks both fields REQUIRED; the MSF
|
|
121
|
-
* schema and `validateCmsfMmtpCatalog` enforce them on `tracks[]`, but the
|
|
122
|
-
* hang dialect's `renditions` schema is permissive, so this is where a hang
|
|
123
|
-
* block fails closed), and `resolveCatalogTrackFec` throws a `RepairContractError` for
|
|
124
|
-
* it (`track_fec` unless another repair-contract violation fires first),
|
|
125
|
-
* whether or not the source has repair layers, rather than silently dropping
|
|
126
|
-
* the track's repair subscription.
|
|
127
|
-
*
|
|
128
|
-
* The block profile is part of this predicate because the profile selects the
|
|
129
|
-
* block math and is never defaulted: draft-ramadan-moq-fec §5.1 defines one,
|
|
130
|
-
* `iso-ssbg1-v1`, and a receiver MUST NOT apply FEC under any other token. The
|
|
131
|
-
* retired `k-padded-v1` is simply not a known profile. `resolveCatalogTrackFec`
|
|
132
|
-
* and `MulticastManager.configureMultiTrack` resolve such a track to no FEC
|
|
133
|
-
* with a typed `TrackFecUnavailable` reason (`catalogFecUnavailable`: code
|
|
134
|
-
* `track_fec_block_profile_unimplemented` for an unimplemented token,
|
|
135
|
-
* `track_fec_block_profile` for an absent one), scoped to that track, so their
|
|
136
|
-
* parameters and this state still agree. The CMSF conformance validator
|
|
137
|
-
* (`validateCmsfMmtpCatalog`) rejects the catalog with the same code. An
|
|
138
|
-
* `iso-ssbg1-v1` instance with an invalid span is likewise unavailable for its
|
|
139
|
-
* track alone. Zero or absence supplies no elapsed deadline and uses bounded
|
|
140
|
-
* receiver-local state while decoding in-band FEC.
|
|
141
|
-
*
|
|
142
|
-
* The repair-contract resolvers (`resolveRepairLayers`) gate a `depends` target
|
|
143
|
-
* on `algorithm` alone and never read a source's `symbolSize`. They check its
|
|
144
|
-
* `sourceSymbols` for the RaptorQ K bound, and require a positive integer
|
|
145
|
-
* `sourceSymbols` and `repairSymbols` only when the source has repair layers or
|
|
146
|
-
* an overlay beyond layer 0.
|
|
96
|
+
* Whether the catalog gives the named source track FEC this receiver can run:
|
|
97
|
+
* its base instance is `ok` (`resolveFecBindings`, draft-ramadan-moq-fec-catalog-00).
|
|
98
|
+
* `resolveCatalogTrackFec` reads the same binding, so the players' parameters
|
|
99
|
+
* and this state never disagree. Total: never throws.
|
|
147
100
|
*/
|
|
148
|
-
export function catalogDeclaresFec(
|
|
149
|
-
|
|
150
|
-
if (block === undefined || block['algorithm'] === 'none' || fecAlgorithmUnimplemented(block['algorithm'])) return false
|
|
151
|
-
return (
|
|
152
|
-
isPositiveSafeInteger(block['sourceSymbols']) &&
|
|
153
|
-
isPositiveSafeInteger(block['symbolSize']) &&
|
|
154
|
-
isFecBlockProfile(block['blockProfile']) &&
|
|
155
|
-
isoBlockSpanUsable(block) &&
|
|
156
|
-
!catalogFecDeclaresUnconfigurableInstance(block)
|
|
157
|
-
)
|
|
101
|
+
export function catalogDeclaresFec(catalog: unknown, track: string): boolean {
|
|
102
|
+
return resolveFecBindings(catalog).get(track)?.status === 'ok'
|
|
158
103
|
}
|
|
159
104
|
|
|
160
105
|
function requireBoolean(value: unknown, field: string): boolean {
|
package/src/index.ts
CHANGED
|
@@ -119,10 +119,8 @@ export {
|
|
|
119
119
|
// CARP Catalog Extensions (draft-law-ramadan-moq-carp-reliability §3-4)
|
|
120
120
|
// + Track Timing (draft-ramadan-moq-mmt-01 §4.4.2)
|
|
121
121
|
export {
|
|
122
|
-
type FecAlgorithm,
|
|
123
122
|
type FecCatalogConfig,
|
|
124
|
-
|
|
125
|
-
isFecCatalogGeometry,
|
|
123
|
+
type FecInstanceConfig,
|
|
126
124
|
type MulticastProtocol,
|
|
127
125
|
type MulticastTrack,
|
|
128
126
|
type MulticastTrackObject,
|
|
@@ -429,7 +427,6 @@ export {
|
|
|
429
427
|
} from './media-router.js'
|
|
430
428
|
export {
|
|
431
429
|
resolveDeltaFallback,
|
|
432
|
-
type DeltaFallbackTrack,
|
|
433
430
|
type ResolvedDeltaFallback,
|
|
434
431
|
} from './delta-fallback-config.js'
|
|
435
432
|
|
|
@@ -543,13 +540,7 @@ export {
|
|
|
543
540
|
type KeyframeCadenceCatalog,
|
|
544
541
|
type TrackFecParams,
|
|
545
542
|
type TrackFecUnavailable,
|
|
546
|
-
|
|
547
|
-
catalogFecAvailable,
|
|
548
|
-
catalogFecDeclaresUnconfigurableInstance,
|
|
549
|
-
fecAlgorithmUnimplemented,
|
|
550
|
-
locateCatalogFecSource,
|
|
551
|
-
type CatalogFecRole,
|
|
552
|
-
type CatalogFecSource,
|
|
543
|
+
catalogTrackFecUnavailable,
|
|
553
544
|
catalogGroupDurationMs,
|
|
554
545
|
catalogKeyframeCadenceMs,
|
|
555
546
|
catalogFecMediaUnitsPerSecond,
|
|
@@ -559,38 +550,27 @@ export {
|
|
|
559
550
|
FEC_BLOCK_PROFILES,
|
|
560
551
|
isFecBlockProfile,
|
|
561
552
|
parseFecBlockProfile,
|
|
562
|
-
readFecBlockProfile,
|
|
563
553
|
type FecBlockProfile,
|
|
564
554
|
resolveRepairLayers,
|
|
565
555
|
catalogRepairTarget,
|
|
566
|
-
assembleRepairLayers,
|
|
567
|
-
assertRepairTrackReachable,
|
|
568
|
-
repairReachability,
|
|
569
|
-
type AssembleRepairLayersOptions,
|
|
570
|
-
type RepairReachability,
|
|
571
|
-
dependsTarget,
|
|
572
|
-
describeRepairTrack,
|
|
573
|
-
rejectRepairFieldsOnMediaTrack,
|
|
574
|
-
rejectRepairTrackGeometry,
|
|
575
|
-
REPAIR_TRACK_GEOMETRY_KEYS,
|
|
576
|
-
REPAIR_TRACK_ONLY_KEYS,
|
|
577
|
-
validateRepairTrackFields,
|
|
578
|
-
RepairContractError,
|
|
579
556
|
DEFAULT_REPAIR_PRIORITY,
|
|
580
557
|
MAX_ENCODING_SYMBOL_ID,
|
|
581
558
|
MAX_RAPTORQ_SOURCE_SYMBOLS,
|
|
582
559
|
MAX_REPAIR_SYMBOLS,
|
|
583
560
|
REPAIR_PRIORITY_MAX,
|
|
584
561
|
REPAIR_PRIORITY_MIN,
|
|
585
|
-
type RepairContractCode,
|
|
586
|
-
type RepairContractFail,
|
|
587
562
|
type RepairLayerCatalog,
|
|
588
563
|
type RepairLayerParams,
|
|
589
564
|
type RepairLayersResolution,
|
|
590
565
|
type RepairOverlayParams,
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
type
|
|
566
|
+
// FEC catalog binding, draft-ramadan-moq-fec-catalog-00 array shape (BLO-50171).
|
|
567
|
+
validateFecCatalog,
|
|
568
|
+
type FecCatalogCode,
|
|
569
|
+
type FecCatalogInstance,
|
|
570
|
+
type FecCatalogIssue,
|
|
571
|
+
type FecCatalogOutcome,
|
|
572
|
+
type FecCatalogReceiver,
|
|
573
|
+
type FecCatalogReport,
|
|
594
574
|
} from "./catalog-fec-params.js";
|
|
595
575
|
|
|
596
576
|
// Typed FEC state (active with counters, or inactive with a reason) shared by
|
package/src/mmtp-fec-session.ts
CHANGED
|
@@ -74,10 +74,11 @@ export type MmtpFecSessionDecoderConstructor = new (
|
|
|
74
74
|
algorithm?: string,
|
|
75
75
|
) => MmtpFecSessionDecoder
|
|
76
76
|
|
|
77
|
-
/** The
|
|
77
|
+
/** The fields of one `ok` draft-ramadan-moq-fec-catalog-00 instance object
|
|
78
|
+
* the session reads (the source track's base instance). */
|
|
78
79
|
export interface MmtpFecSessionCatalogFec {
|
|
79
|
-
/** `raptorq`
|
|
80
|
-
|
|
80
|
+
/** `raptorq` or `reed-solomon`; required. */
|
|
81
|
+
scheme?: unknown
|
|
81
82
|
blockProfile?: unknown
|
|
82
83
|
sourceSymbols?: unknown
|
|
83
84
|
/** P_0: layer 0's repair symbols per block, the fec-01 6.3.9 total B_0. */
|
|
@@ -235,20 +236,20 @@ export class MmtpFecSession {
|
|
|
235
236
|
}
|
|
236
237
|
|
|
237
238
|
/**
|
|
238
|
-
* @throws when the
|
|
239
|
-
*
|
|
240
|
-
* `reorderToleranceMs`. None is defaulted.
|
|
239
|
+
* @throws when the instance lacks or misstates a field the session needs:
|
|
240
|
+
* the scheme, the block profile, K, P_0 (`repairSymbols`), T,
|
|
241
|
+
* `interleaveDepthMs` or `reorderToleranceMs`. None is defaulted.
|
|
241
242
|
*/
|
|
242
243
|
constructor(config: MmtpFecSessionConfig) {
|
|
243
244
|
const fec = config?.fec
|
|
244
245
|
if (!fec || typeof fec !== 'object') {
|
|
245
|
-
throw new Error('MmtpFecSession: the catalog fec
|
|
246
|
+
throw new Error('MmtpFecSession: the catalog fec instance is required')
|
|
246
247
|
}
|
|
247
248
|
this.blockProfile = parseFecBlockProfile(fec.blockProfile, 'fec.blockProfile')
|
|
248
|
-
if (fec.
|
|
249
|
-
throw new Error(`MmtpFecSession: fec.
|
|
249
|
+
if (fec.scheme !== 'raptorq' && fec.scheme !== 'reed-solomon') {
|
|
250
|
+
throw new Error(`MmtpFecSession: fec.scheme ${JSON.stringify(fec.scheme)} is not implemented`)
|
|
250
251
|
}
|
|
251
|
-
this.algorithm = fec.
|
|
252
|
+
this.algorithm = fec.scheme
|
|
252
253
|
this.sourceSymbols = requirePositiveInteger(fec.sourceSymbols, 'fec.sourceSymbols')
|
|
253
254
|
this.repairSymbols = requirePositiveInteger(fec.repairSymbols, 'fec.repairSymbols')
|
|
254
255
|
this.symbolSize = requirePositiveInteger(fec.symbolSize, 'fec.symbolSize')
|
package/src/mmtp-router.ts
CHANGED
|
@@ -989,9 +989,6 @@ export class MmtpRouter {
|
|
|
989
989
|
#onVideoFrame: FrameCallback | null = null;
|
|
990
990
|
#onAudioFrame: FrameCallback | null = null;
|
|
991
991
|
|
|
992
|
-
// Init segment (ftyp+moov) from MMTP MPU metadata — needed for MSE SourceBuffer
|
|
993
|
-
#onInitSegment: ((data: Uint8Array) => void) | null = null;
|
|
994
|
-
|
|
995
992
|
// Per-track init segment callbacks keyed by packetId
|
|
996
993
|
#trackInitCallbacks = new Map<number, (data: Uint8Array) => void>();
|
|
997
994
|
// Track whether init has been delivered per physical stream — prevent duplicates
|
|
@@ -1418,11 +1415,10 @@ export class MmtpRouter {
|
|
|
1418
1415
|
}
|
|
1419
1416
|
|
|
1420
1417
|
/**
|
|
1421
|
-
* Init segments
|
|
1422
|
-
*
|
|
1423
|
-
*
|
|
1424
|
-
* 0 here
|
|
1425
|
-
* aggregate callback (MOQT-01, BLO-21181).
|
|
1418
|
+
* Init segments rejected since construction because their packetId had no
|
|
1419
|
+
* `onTrackInit` handler. A rejected init is delivered nowhere; there is no
|
|
1420
|
+
* aggregate fallback (removed in 1.0.0, MOQT-01 / BLO-21181). A supported
|
|
1421
|
+
* deployment reads 0 here.
|
|
1426
1422
|
*/
|
|
1427
1423
|
get unmappedInitDeliveryCount(): number {
|
|
1428
1424
|
return this.#unmappedInitDeliveries;
|
|
@@ -1640,21 +1636,10 @@ export class MmtpRouter {
|
|
|
1640
1636
|
this.#trackPacketCallbacks.set(packetId, cb);
|
|
1641
1637
|
}
|
|
1642
1638
|
|
|
1643
|
-
/**
|
|
1644
|
-
* Register an init segment callback (ftyp+moov from MMTP MPU metadata).
|
|
1645
|
-
* Receives an init only for a packetId with no `onTrackInit` handler.
|
|
1646
|
-
*
|
|
1647
|
-
* @deprecated Use onTrackInit(packetId, cb). Still delivered as a fallback;
|
|
1648
|
-
* removed in the 1.0.0 major, no earlier than one minor release after this
|
|
1649
|
-
* deprecation reaches the `latest` dist-tag (BLO-21181). Count fallback
|
|
1650
|
-
* deliveries with `unmappedInitDeliveryCount`.
|
|
1651
|
-
*/
|
|
1652
|
-
onInitSegment(cb: (data: Uint8Array) => void): void {
|
|
1653
|
-
this.#onInitSegment = cb;
|
|
1654
|
-
}
|
|
1655
|
-
|
|
1656
1639
|
/** Register an init segment callback for a specific MMTP packetId.
|
|
1657
|
-
* Called when FT=0 init arrives via direct source or FEC recovery.
|
|
1640
|
+
* Called when FT=0 init arrives via direct source or FEC recovery. An init
|
|
1641
|
+
* for a packetId with no handler is rejected and counted by
|
|
1642
|
+
* `unmappedInitDeliveryCount`. */
|
|
1658
1643
|
onTrackInit(packetId: number, cb: (data: Uint8Array) => void): void {
|
|
1659
1644
|
this.#trackInitCallbacks.set(packetId, cb);
|
|
1660
1645
|
}
|
|
@@ -3404,16 +3389,16 @@ export class MmtpRouter {
|
|
|
3404
3389
|
if (this.#trackInitDelivered.has(streamKey)) return;
|
|
3405
3390
|
if (data.byteLength <= 8) return;
|
|
3406
3391
|
this.#trackInitDelivered.add(streamKey);
|
|
3392
|
+
const track = `packetId=${packetId}${catalogTrackName === undefined ? "" : ` track=${catalogTrackName}`}`;
|
|
3407
3393
|
const cb = this.#trackInitCallbacks.get(packetId);
|
|
3408
|
-
if (cb) {
|
|
3409
|
-
|
|
3410
|
-
} else {
|
|
3394
|
+
if (!cb) {
|
|
3395
|
+
// No aggregate fallback (MOQT-01): an init nobody mapped is rejected.
|
|
3411
3396
|
this.#unmappedInitDeliveries++;
|
|
3412
|
-
|
|
3397
|
+
console.log(`[MmtpRouter] Init rejected for ${track}: no onTrackInit handler`);
|
|
3398
|
+
return;
|
|
3413
3399
|
}
|
|
3414
|
-
|
|
3415
|
-
|
|
3416
|
-
);
|
|
3400
|
+
cb(data);
|
|
3401
|
+
console.log(`[MmtpRouter] Init delivered for ${track} (${data.byteLength}B)`);
|
|
3417
3402
|
}
|
|
3418
3403
|
|
|
3419
3404
|
/**
|