@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
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@blockcast/mmt-container",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "1.0.0-main.3cb7359ea316",
|
|
4
4
|
"description": "MMTP/LOC container parsing for MMT over MoQ and SSM",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -26,6 +26,10 @@
|
|
|
26
26
|
"import": "./dist/codec-utils.js",
|
|
27
27
|
"types": "./dist/codec-utils.d.ts"
|
|
28
28
|
},
|
|
29
|
+
"./fec-catalog": {
|
|
30
|
+
"import": "./dist/fec-catalog.js",
|
|
31
|
+
"types": "./dist/fec-catalog.d.ts"
|
|
32
|
+
},
|
|
29
33
|
"./fec-manager": {
|
|
30
34
|
"import": "./dist/fec-manager.js",
|
|
31
35
|
"types": "./dist/fec-manager.d.ts"
|
|
@@ -165,10 +169,10 @@
|
|
|
165
169
|
"test:watch": "vitest"
|
|
166
170
|
},
|
|
167
171
|
"dependencies": {
|
|
168
|
-
"@blockcast/fec-worker": "
|
|
169
|
-
"@blockcast/mmt-base": "
|
|
170
|
-
"@blockcast/mmt-fec": "
|
|
171
|
-
"@blockcast/mmt-manifest-verify": "
|
|
172
|
+
"@blockcast/fec-worker": "1.0.0-main.3cb7359ea316",
|
|
173
|
+
"@blockcast/mmt-base": "1.0.0-main.3cb7359ea316",
|
|
174
|
+
"@blockcast/mmt-fec": "1.0.0-main.3cb7359ea316",
|
|
175
|
+
"@blockcast/mmt-manifest-verify": "1.0.0-main.3cb7359ea316",
|
|
172
176
|
"@noble/curves": "^1.9.7",
|
|
173
177
|
"@noble/hashes": "^1.8.0",
|
|
174
178
|
"ajv": "^8.17.1",
|
|
@@ -65,7 +65,7 @@
|
|
|
65
65
|
"properties": {
|
|
66
66
|
"namespace": { "type": "string", "minLength": 1 },
|
|
67
67
|
"name": { "type": "string", "minLength": 1 },
|
|
68
|
-
"packaging": { "enum": ["mmtp", "fec-repair"] },
|
|
68
|
+
"packaging": { "enum": ["mmtp", "fec-repair", "fec-source"] },
|
|
69
69
|
"isLive": {
|
|
70
70
|
"type": "boolean",
|
|
71
71
|
"default": true,
|
|
@@ -106,27 +106,28 @@
|
|
|
106
106
|
},
|
|
107
107
|
"initTrack": { "type": "string", "minLength": 1 },
|
|
108
108
|
"priority": { "type": "integer", "minimum": 0, "maximum": 255 },
|
|
109
|
+
"source": {
|
|
110
|
+
"type": "string",
|
|
111
|
+
"minLength": 1,
|
|
112
|
+
"description": "fec-repair and fec-source only. Track Name of the source track whose fec array declares the instance this track serves (draft-ramadan-moq-fec-catalog-00 3.2)."
|
|
113
|
+
},
|
|
114
|
+
"instanceId": {
|
|
115
|
+
"type": "integer",
|
|
116
|
+
"minimum": 0,
|
|
117
|
+
"maximum": 255,
|
|
118
|
+
"description": "fec-repair and fec-source only. Instance ID, in the source track's fec array, this track serves; 0 when absent (draft-ramadan-moq-fec-catalog-00 3.2)."
|
|
119
|
+
},
|
|
109
120
|
"repairLayer": {
|
|
110
121
|
"type": "integer",
|
|
111
122
|
"minimum": 0,
|
|
112
123
|
"maximum": 16777215,
|
|
113
|
-
"description": "fec-repair only.
|
|
124
|
+
"description": "fec-repair only. Repair layer index within the instance; layer 0 is the track named by the instance's repairTrack, layers 1..n are contiguous (draft-ramadan-moq-fec-core-00 10.3 rule 3)."
|
|
114
125
|
},
|
|
115
126
|
"repairSymbols": {
|
|
116
127
|
"type": "integer",
|
|
117
128
|
"minimum": 1,
|
|
118
129
|
"maximum": 16777215,
|
|
119
|
-
"description": "fec-repair only. Repair symbols per block carried by this layer
|
|
120
|
-
},
|
|
121
|
-
"sourceSymbols": {
|
|
122
|
-
"type": "integer",
|
|
123
|
-
"minimum": 1,
|
|
124
|
-
"maximum": 56403,
|
|
125
|
-
"description": "fec-repair overlay only. Source symbols per block of the overlay FEC instance (K_overlay)."
|
|
126
|
-
},
|
|
127
|
-
"scope": {
|
|
128
|
-
"const": "keyframe",
|
|
129
|
-
"description": "fec-repair only. Marks a keyframe overlay: a separate FEC instance over random access point fragments."
|
|
130
|
+
"description": "fec-repair only. Repair symbols per block carried by this layer (P_i)."
|
|
130
131
|
},
|
|
131
132
|
"mmtpMode": { "const": "mfu" },
|
|
132
133
|
"timescale": { "$ref": "#/$defs/positiveInteger" },
|
|
@@ -153,8 +154,8 @@
|
|
|
153
154
|
"anyOf": [
|
|
154
155
|
{ "properties": { "repairLayer": true }, "required": ["repairLayer"] },
|
|
155
156
|
{ "properties": { "repairSymbols": true }, "required": ["repairSymbols"] },
|
|
156
|
-
{ "properties": { "
|
|
157
|
-
{ "properties": { "
|
|
157
|
+
{ "properties": { "source": true }, "required": ["source"] },
|
|
158
|
+
{ "properties": { "instanceId": true }, "required": ["instanceId"] }
|
|
158
159
|
]
|
|
159
160
|
},
|
|
160
161
|
"anyOf": [
|
|
@@ -170,22 +171,9 @@
|
|
|
170
171
|
}
|
|
171
172
|
},
|
|
172
173
|
{
|
|
173
|
-
"
|
|
174
|
+
"$comment": "draft-ramadan-moq-fec-core-00 10.3 rule 8: a repair or symbol track carries no block geometry. Instance resolution, layer contiguity and priority order are validateFecCatalog's (draft-ramadan-moq-fec-catalog-00 5).",
|
|
175
|
+
"if": { "properties": { "packaging": { "enum": ["fec-repair", "fec-source"] } }, "required": ["packaging"] },
|
|
174
176
|
"then": {
|
|
175
|
-
"properties": {
|
|
176
|
-
"priority": {
|
|
177
|
-
"type": "integer",
|
|
178
|
-
"minimum": 192,
|
|
179
|
-
"maximum": 255,
|
|
180
|
-
"description": "MoQT-scale delivery priority, lower first. Optional on base layer 0 (240 when absent); required on layers 1..n and overlays."
|
|
181
|
-
},
|
|
182
|
-
"depends": {
|
|
183
|
-
"type": "array",
|
|
184
|
-
"minItems": 1,
|
|
185
|
-
"maxItems": 1,
|
|
186
|
-
"description": "Exactly one source track: an mmtp track carrying an fec object."
|
|
187
|
-
}
|
|
188
|
-
},
|
|
189
177
|
"not": {
|
|
190
178
|
"anyOf": [
|
|
191
179
|
{ "properties": { "mmtpMode": true }, "required": ["mmtpMode"] },
|
|
@@ -199,26 +187,22 @@
|
|
|
199
187
|
},
|
|
200
188
|
"allOf": [
|
|
201
189
|
{
|
|
202
|
-
"if": { "properties": { "
|
|
190
|
+
"if": { "properties": { "packaging": { "const": "fec-source" } }, "required": ["packaging"] },
|
|
203
191
|
"then": {
|
|
204
|
-
"
|
|
205
|
-
|
|
206
|
-
|
|
192
|
+
"not": {
|
|
193
|
+
"anyOf": [
|
|
194
|
+
{ "properties": { "repairLayer": true }, "required": ["repairLayer"] },
|
|
195
|
+
{ "properties": { "repairSymbols": true }, "required": ["repairSymbols"] }
|
|
196
|
+
]
|
|
197
|
+
}
|
|
207
198
|
}
|
|
208
199
|
},
|
|
209
|
-
{
|
|
210
|
-
"if": { "properties": { "sourceSymbols": true }, "required": ["sourceSymbols"] },
|
|
211
|
-
"then": { "properties": { "scope": true }, "required": ["scope"] }
|
|
212
|
-
},
|
|
213
200
|
{
|
|
214
201
|
"if": {
|
|
215
202
|
"properties": { "repairLayer": { "type": "integer", "minimum": 1 } },
|
|
216
203
|
"required": ["repairLayer"]
|
|
217
204
|
},
|
|
218
|
-
"then": {
|
|
219
|
-
"properties": { "depends": true, "repairSymbols": true, "priority": true },
|
|
220
|
-
"required": ["depends", "repairSymbols", "priority"]
|
|
221
|
-
}
|
|
205
|
+
"then": { "required": ["source", "repairSymbols"] }
|
|
222
206
|
}
|
|
223
207
|
]
|
|
224
208
|
}
|
|
@@ -226,18 +210,25 @@
|
|
|
226
210
|
]
|
|
227
211
|
},
|
|
228
212
|
"fec": {
|
|
213
|
+
"type": "array",
|
|
214
|
+
"description": "FEC instances of this track (draft-ramadan-moq-fec-catalog-00 3.1). An empty or absent array declares no FEC. The fec-01 single-object shape is not accepted.",
|
|
215
|
+
"items": { "$ref": "#/$defs/fecInstance" }
|
|
216
|
+
},
|
|
217
|
+
"fecInstance": {
|
|
229
218
|
"type": "object",
|
|
230
|
-
"
|
|
231
|
-
"required": ["
|
|
219
|
+
"$comment": "No additionalProperties: false: unknown keys are ignored (draft-ramadan-moq-fec-catalog-00 3.4). This states what a publisher emits; a receiver binds through validateFecCatalog, which fails closed per instance.",
|
|
220
|
+
"required": ["scheme", "blockProfile", "sourceSymbols", "repairSymbols", "symbolSize", "repairTrack"],
|
|
232
221
|
"properties": {
|
|
233
|
-
"
|
|
222
|
+
"instanceId": { "type": "integer", "minimum": 0, "maximum": 255, "default": 0 },
|
|
223
|
+
"scheme": {
|
|
234
224
|
"type": "string",
|
|
235
225
|
"minLength": 1,
|
|
236
|
-
"description": "FEC scheme
|
|
226
|
+
"description": "FEC scheme (draft-ramadan-moq-fec-block-codes-00): raptorq or reed-solomon. A receiver that does not implement it forgoes this instance. \"none\" is reserved and never means no FEC (draft-ramadan-moq-fec-core-00 15.2)."
|
|
237
227
|
},
|
|
238
228
|
"blockProfile": {
|
|
239
|
-
"
|
|
240
|
-
"
|
|
229
|
+
"type": "string",
|
|
230
|
+
"minLength": 1,
|
|
231
|
+
"description": "FEC block profile: moq-object-v1, or iso-ssbg1-v1 on an mmtp track. Required, never defaulted (draft-ramadan-moq-fec-catalog-00 3.1)."
|
|
241
232
|
},
|
|
242
233
|
"sourceSymbols": { "$ref": "#/$defs/positiveInteger" },
|
|
243
234
|
"repairSymbols": { "$ref": "#/$defs/positiveInteger" },
|
|
@@ -245,28 +236,32 @@
|
|
|
245
236
|
"interleaveDepthMs": { "type": "integer", "minimum": 0, "maximum": 9007199254740991 },
|
|
246
237
|
"reorderToleranceMs": { "$ref": "#/$defs/nonNegativeNumber" },
|
|
247
238
|
"repairTrack": { "type": "string", "minLength": 1 },
|
|
248
|
-
"
|
|
249
|
-
"
|
|
239
|
+
"symbolTrack": { "type": "string", "minLength": 1 },
|
|
240
|
+
"label": { "type": "string", "description": "Human-readable only; never drives behaviour." },
|
|
241
|
+
"scope": {
|
|
242
|
+
"const": "keyframe",
|
|
243
|
+
"description": "Marks a keyframe overlay instance (draft-ramadan-moq-mmt-02 7.4.1)."
|
|
244
|
+
}
|
|
250
245
|
},
|
|
251
246
|
"allOf": [
|
|
252
247
|
{
|
|
253
|
-
"
|
|
254
|
-
"else": { "properties": { "blockProfile": true }, "required": ["blockProfile"] }
|
|
255
|
-
},
|
|
256
|
-
{
|
|
257
|
-
"$comment": "draft-ramadan-moq-fec-01 6.3.4 rule 13: under iso-ssbg1-v1 a symbol is at least 14 octets (Section 7.3); a shorter symbol carries no protected MMTP packet.",
|
|
248
|
+
"$comment": "draft-ramadan-moq-mmt-02 7.1: under iso-ssbg1-v1 a symbol is at least 14 octets.",
|
|
258
249
|
"if": { "properties": { "blockProfile": { "const": "iso-ssbg1-v1" } }, "required": ["blockProfile"] },
|
|
259
250
|
"then": { "properties": { "symbolSize": { "type": "integer", "minimum": 14 } } }
|
|
260
251
|
},
|
|
261
252
|
{
|
|
262
|
-
"if": { "properties": { "
|
|
253
|
+
"if": { "properties": { "scheme": { "const": "raptorq" } }, "required": ["scheme"] },
|
|
263
254
|
"then": {
|
|
264
255
|
"properties": {
|
|
265
256
|
"symbolSize": { "type": "integer", "minimum": 1, "multipleOf": 8 },
|
|
266
|
-
"sourceSymbols": { "type": "integer", "maximum": 56403 }
|
|
267
|
-
"repairSymbols": { "type": "integer", "maximum": 16777215 }
|
|
257
|
+
"sourceSymbols": { "type": "integer", "maximum": 56403 }
|
|
268
258
|
}
|
|
269
259
|
}
|
|
260
|
+
},
|
|
261
|
+
{
|
|
262
|
+
"$comment": "draft-ramadan-moq-fec-core-00 10.3 rule 11: interleaveDepthMs 0 only under iso-ssbg1-v1.",
|
|
263
|
+
"if": { "properties": { "interleaveDepthMs": { "const": 0 } }, "required": ["interleaveDepthMs"] },
|
|
264
|
+
"then": { "properties": { "blockProfile": { "const": "iso-ssbg1-v1" } }, "required": ["blockProfile"] }
|
|
270
265
|
}
|
|
271
266
|
]
|
|
272
267
|
},
|
package/src/carp-catalog.ts
CHANGED
|
@@ -11,23 +11,10 @@
|
|
|
11
11
|
import {
|
|
12
12
|
MMTP_PACKET_ID_MAX,
|
|
13
13
|
MMTP_SIGNALING_PACKET_ID,
|
|
14
|
-
ISO_SSBG1_MIN_SYMBOL_SIZE,
|
|
15
|
-
isFecBlockProfile,
|
|
16
14
|
type FecBlockProfile,
|
|
17
15
|
} from "@blockcast/mmt-base";
|
|
18
16
|
import { multicastCatalogIssues, multicastEndpointSource } from "@blockcast/mmt-base";
|
|
19
17
|
import type { EndpointAuthConfig, MulticastAuthConfig } from "./multicast-auth-catalog.js";
|
|
20
|
-
import {
|
|
21
|
-
assembleRepairLayers,
|
|
22
|
-
assertRepairTrackReachable,
|
|
23
|
-
describeRepairTrack,
|
|
24
|
-
rejectRepairFieldsOnMediaTrack,
|
|
25
|
-
rejectRepairTrackGeometry,
|
|
26
|
-
repairReachability,
|
|
27
|
-
dependsTarget,
|
|
28
|
-
readFecBlockProfile,
|
|
29
|
-
validateRepairTrackFields,
|
|
30
|
-
} from "./repair-layers.js";
|
|
31
18
|
|
|
32
19
|
// ── Track Role (Phase 14, D-02..D-05) ─────────────────────────
|
|
33
20
|
|
|
@@ -164,125 +151,51 @@ export function validateSwitchingSet(
|
|
|
164
151
|
return { ok: true };
|
|
165
152
|
}
|
|
166
153
|
|
|
167
|
-
// ── FEC Catalog Extension (
|
|
168
|
-
|
|
169
|
-
/** draft-ramadan-moq-fec-01 4: `none` and the two specified schemes, or any
|
|
170
|
-
* other registered token, which a receiver that does not implement it
|
|
171
|
-
* consumes without FEC (`fecAlgorithmUnimplemented`). */
|
|
172
|
-
export type FecAlgorithm = "none" | "raptorq" | "reed-solomon" | (string & {});
|
|
154
|
+
// ── FEC Catalog Extension (draft-ramadan-moq-fec-catalog-00 3) ─
|
|
173
155
|
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
/**
|
|
156
|
+
/**
|
|
157
|
+
* One FEC instance object (draft-ramadan-moq-fec-catalog-00 3.1). A track's
|
|
158
|
+
* `fec` is an array of these ({@link FecCatalogConfig}); an empty array, or no
|
|
159
|
+
* `fec`, declares no FEC. Member names are case sensitive. This is the shape
|
|
160
|
+
* only: whether an instance is usable is `validateFecCatalog`'s answer (fail
|
|
161
|
+
* closed per instance), and runtime readers take it from `resolveFecBindings`,
|
|
162
|
+
* never from a local predicate.
|
|
163
|
+
*/
|
|
164
|
+
export interface FecInstanceConfig {
|
|
165
|
+
/** 0..255, unique per track; absent means 0. */
|
|
166
|
+
instanceId?: number;
|
|
167
|
+
/** `raptorq`, `reed-solomon`, or any other registered token, which a
|
|
168
|
+
* receiver that does not implement it consumes without FEC. */
|
|
169
|
+
scheme: string;
|
|
170
|
+
/** REQUIRED, never defaulted. This library implements `iso-ssbg1-v1`
|
|
171
|
+
* (BLO-35597); any other token is no FEC on that instance. */
|
|
172
|
+
blockProfile: FecBlockProfile | (string & {});
|
|
173
|
+
/** K. */
|
|
186
174
|
sourceSymbols: number;
|
|
187
|
-
/** P
|
|
175
|
+
/** P of layer 0, >= 1. */
|
|
188
176
|
repairSymbols: number;
|
|
189
|
-
/** T
|
|
177
|
+
/** T, bytes per symbol. */
|
|
190
178
|
symbolSize: number;
|
|
191
|
-
/** First-to-last source/repair send span in milliseconds.
|
|
192
|
-
* Zero or absence means no elapsed deadline; in-band FEC remains usable.
|
|
193
|
-
* BLO-14526: renamed from the legacy `interleaveDepth` count-era key — no fallback read. */
|
|
194
179
|
interleaveDepthMs?: number;
|
|
195
|
-
/**
|
|
196
|
-
* Added to interleaveDepthMs to form a block's deadline; never multiplied
|
|
197
|
-
* by it. Jitter is a property of the network path and the block span is a
|
|
198
|
-
* property of the encoder, so a multiplicative "safety factor" couples two
|
|
199
|
-
* independent quantities and grants different slack per track for the same
|
|
200
|
-
* network. Consumed identically by FECManager, FEC workers and player
|
|
201
|
-
* policies so one signaled value governs end to end. */
|
|
180
|
+
/** Additive jitter/reorder allowance in milliseconds (never a multiplier). */
|
|
202
181
|
reorderToleranceMs?: number;
|
|
203
|
-
/**
|
|
204
|
-
*
|
|
205
|
-
* self-describing `fec-repair` tracks (`depends`, `repairLayer`, `scope`)
|
|
206
|
-
* per draft-ramadan-moq-fec §6.3, never listed here. */
|
|
182
|
+
/** Layer-0 repair track. Further layers and an overlay are `fec-repair`
|
|
183
|
+
* tracks naming this track in `source` and this instance's `instanceId`. */
|
|
207
184
|
repairTrack: string;
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
/**
|
|
212
|
-
*
|
|
213
|
-
*
|
|
214
|
-
uep?: {
|
|
215
|
-
/** Keyframe repair ratio: P = ceil(K * keyRatio). Default 0.5. */
|
|
216
|
-
keyRatio: number;
|
|
217
|
-
/** Delta frame repair ratio: P = ceil(K * deltaRatio). Default 0.25. */
|
|
218
|
-
deltaRatio: number;
|
|
219
|
-
};
|
|
220
|
-
/**
|
|
221
|
-
* Base-only fallback window in FEC blocks (D-11, Phase 14).
|
|
222
|
-
*
|
|
223
|
-
* When the delta track's FEC block failure rate exceeds the derived threshold
|
|
224
|
-
* over this many consecutive blocks, MediaRouter falls back to base-only mode.
|
|
225
|
-
* Derivation: framerate * fallback_window_seconds. If absent, construction
|
|
226
|
-
* of the multi-track subscriber MUST throw (error on bad init).
|
|
227
|
-
*
|
|
228
|
-
* Present only on the base track's fec config (not the delta or repair track).
|
|
229
|
-
*/
|
|
185
|
+
symbolTrack?: string;
|
|
186
|
+
/** Display only; never drives behaviour. */
|
|
187
|
+
label?: string;
|
|
188
|
+
/** Phase-14 base-only fallback window in FEC blocks (D-11). Blockcast
|
|
189
|
+
* extension: an unrecognized instance key the validator ignores
|
|
190
|
+
* (catalog-00 3.4); read by `resolveDeltaFallback`. */
|
|
230
191
|
deltaFailWindowBlocks?: number;
|
|
231
|
-
/**
|
|
232
|
-
*
|
|
233
|
-
*
|
|
234
|
-
* After a base-only fallback, wait this long before re-subscribing to the
|
|
235
|
-
* delta track to check if conditions have improved.
|
|
236
|
-
* Derivation: computeFecDeliveryWindowMs(...) — the single FEC recovery-window
|
|
237
|
-
* source of truth (draft-ramadan-moq-fec §9). Never a fixed interleave multiple.
|
|
238
|
-
*
|
|
239
|
-
* Present only on the base track's fec config (not the delta or repair track).
|
|
240
|
-
*/
|
|
192
|
+
/** Phase-14 delta re-subscribe probe interval in ms (D-13). Blockcast
|
|
193
|
+
* extension, as above. */
|
|
241
194
|
deltaProbeIntervalMs?: number;
|
|
242
|
-
/**
|
|
243
|
-
* Jitter + one-way-delay + reorder budget in MILLISECONDS (draft §9).
|
|
244
|
-
*
|
|
245
|
-
* An explicit ADDITIVE margin over the complete signaled FEC block span.
|
|
246
|
-
* OPTIONAL: absence does not authorize a local default or a span multiplier.
|
|
247
|
-
*/
|
|
248
|
-
jitterBufferMs?: number;
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
/**
|
|
252
|
-
* Whether a catalog `fec` block carries the FEC instance geometry a receiver
|
|
253
|
-
* configures, its block profile aside: a non-empty `algorithm` token, positive
|
|
254
|
-
* `sourceSymbols`, `repairSymbols` and `symbolSize` (8-byte aligned for
|
|
255
|
-
* RaptorQ), and a non-empty `repairTrack`. A block that fails this is
|
|
256
|
-
* malformed whatever its profile. Total: never throws.
|
|
257
|
-
*/
|
|
258
|
-
export function isFecCatalogGeometry(obj: unknown): boolean {
|
|
259
|
-
if (typeof obj !== "object" || obj === null) return false;
|
|
260
|
-
const o = obj as Record<string, unknown>;
|
|
261
|
-
const algorithm = o["algorithm"];
|
|
262
|
-
const symbolSize = o["symbolSize"];
|
|
263
|
-
return (
|
|
264
|
-
typeof algorithm === "string" && algorithm.length > 0 &&
|
|
265
|
-
typeof o["sourceSymbols"] === "number" && o["sourceSymbols"] >= 1 &&
|
|
266
|
-
typeof o["repairSymbols"] === "number" && o["repairSymbols"] >= 1 &&
|
|
267
|
-
typeof symbolSize === "number" && symbolSize >= 1 &&
|
|
268
|
-
(algorithm !== "raptorq" || symbolSize % 8 === 0) &&
|
|
269
|
-
typeof o["repairTrack"] === "string" && (o["repairTrack"] as string).length > 0
|
|
270
|
-
);
|
|
271
195
|
}
|
|
272
196
|
|
|
273
|
-
/**
|
|
274
|
-
|
|
275
|
-
* holds (`isFecCatalogGeometry`) and it names the implemented block profile,
|
|
276
|
-
* `iso-ssbg1-v1` (BLO-35597). On `algorithm: "none"` the profile may be
|
|
277
|
-
* omitted. Any other token, the retired `k-padded-v1` included, is not a
|
|
278
|
-
* known profile, so this is false for it; a receiver resolves such a track to
|
|
279
|
-
* no FEC (`TrackFecUnavailable`) rather than failing the session.
|
|
280
|
-
*/
|
|
281
|
-
export function isFecCatalogConfig(obj: unknown): obj is FecCatalogConfig {
|
|
282
|
-
if (!isFecCatalogGeometry(obj)) return false;
|
|
283
|
-
const o = obj as Record<string, unknown>;
|
|
284
|
-
return isFecBlockProfile(o["blockProfile"]) || (o["algorithm"] === "none" && o["blockProfile"] === undefined);
|
|
285
|
-
}
|
|
197
|
+
/** A track's `fec` member (draft-ramadan-moq-fec-catalog-00 3). */
|
|
198
|
+
export type FecCatalogConfig = readonly FecInstanceConfig[];
|
|
286
199
|
|
|
287
200
|
// ── Multicast Catalog Extension (CARP §4.1-4.4) ───────────────
|
|
288
201
|
|
|
@@ -866,7 +779,7 @@ export function validateCmsfMmtpCatalog(
|
|
|
866
779
|
);
|
|
867
780
|
}
|
|
868
781
|
const codec = track["codec"];
|
|
869
|
-
if (packaging !== "timeline" && packaging !== "mediatimeline" && packaging !== "eventtimeline" && packaging !== "fec-repair" &&
|
|
782
|
+
if (packaging !== "timeline" && packaging !== "mediatimeline" && packaging !== "eventtimeline" && packaging !== "fec-repair" && packaging !== "fec-source" &&
|
|
870
783
|
(typeof codec !== "string" || codec.length === 0)) {
|
|
871
784
|
catalogError("track_codec", `${path}.codec`, `codec is required for packaging=${packaging}`);
|
|
872
785
|
}
|
|
@@ -896,14 +809,10 @@ export function validateCmsfMmtpCatalog(
|
|
|
896
809
|
if (initRef !== undefined && (typeof initRef !== "string" || initRef.length === 0)) {
|
|
897
810
|
catalogError("track_init_ref", `${path}.initRef`, "must be a non-empty string");
|
|
898
811
|
}
|
|
899
|
-
// draft-ramadan-moq-fec
|
|
900
|
-
//
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
validateRepairTrackFields(describeRepairTrack(name, path, track), catalogError);
|
|
904
|
-
} else {
|
|
905
|
-
rejectRepairFieldsOnMediaTrack(path, track, catalogError);
|
|
906
|
-
}
|
|
812
|
+
// FEC binding (draft-ramadan-moq-fec-catalog-00) is `validateFecCatalog`'s:
|
|
813
|
+
// it fails closed per instance and never stops another track, so nothing
|
|
814
|
+
// here throws on `fec` or on a repair or symbol track. A -01 single-object
|
|
815
|
+
// `fec` therefore costs only that track's FEC (BLO-50171).
|
|
907
816
|
if (packaging === "mmtp") {
|
|
908
817
|
// mmt-01 12.1: MUST be "mfu"; a subscriber rejects anything else.
|
|
909
818
|
if (track["mmtpMode"] !== "mfu") {
|
|
@@ -924,105 +833,6 @@ export function validateCmsfMmtpCatalog(
|
|
|
924
833
|
catalogError("track_group_duration_exact", `${path}.groupDurationMs`, "does not resolve to an exact integer tick count");
|
|
925
834
|
}
|
|
926
835
|
}
|
|
927
|
-
|
|
928
|
-
if (track["fec"] !== undefined) {
|
|
929
|
-
const fec = track["fec"];
|
|
930
|
-
if (typeof fec !== "object" || fec === null || Array.isArray(fec)) {
|
|
931
|
-
catalogError("track_fec", `${path}.fec`, "FEC must be an object");
|
|
932
|
-
}
|
|
933
|
-
const config = fec as Record<string, unknown>;
|
|
934
|
-
const algorithm = config["algorithm"];
|
|
935
|
-
// Any token is valid (fec-01 4): a receiver that does not implement it
|
|
936
|
-
// plays the track without FEC (`fecAlgorithmUnimplemented`).
|
|
937
|
-
if (typeof algorithm !== "string" || algorithm.length === 0) {
|
|
938
|
-
catalogError("track_fec", `${path}.fec.algorithm`, "must be a non-empty string");
|
|
939
|
-
}
|
|
940
|
-
if ("interleaveDepth" in config || "interleaveMs" in config) {
|
|
941
|
-
catalogError("track_fec", `${path}.fec`, "interleaveDepthMs is the only supported interleave field");
|
|
942
|
-
}
|
|
943
|
-
if ("enhancementRepair" in config) {
|
|
944
|
-
catalogError(
|
|
945
|
-
"track_fec",
|
|
946
|
-
`${path}.fec.enhancementRepair`,
|
|
947
|
-
"repair layers are self-describing fec-repair tracks (depends, repairLayer); fec.enhancementRepair is not supported",
|
|
948
|
-
);
|
|
949
|
-
}
|
|
950
|
-
if (config["interleaveDepthMs"] !== undefined &&
|
|
951
|
-
(!Number.isSafeInteger(config["interleaveDepthMs"]) || (config["interleaveDepthMs"] as number) < 0)) {
|
|
952
|
-
catalogError("track_fec", `${path}.fec.interleaveDepthMs`, "must be a non-negative safe integer");
|
|
953
|
-
}
|
|
954
|
-
if (algorithm !== "none") {
|
|
955
|
-
for (const field of ["sourceSymbols", "repairSymbols", "symbolSize"] as const) {
|
|
956
|
-
if (!positiveInteger(config[field])) {
|
|
957
|
-
catalogError("track_fec", `${path}.fec.${field}`, "must be a positive integer");
|
|
958
|
-
}
|
|
959
|
-
}
|
|
960
|
-
// The receiver refuses unaligned RaptorQ geometry (`isFecCatalogGeometry`);
|
|
961
|
-
// the schema's `multipleOf: 8` says the same, so the validator does too.
|
|
962
|
-
if (algorithm === "raptorq" && (config["symbolSize"] as number) % 8 !== 0) {
|
|
963
|
-
catalogError("track_fec", `${path}.fec.symbolSize`, "RaptorQ symbolSize must be a multiple of 8");
|
|
964
|
-
}
|
|
965
|
-
if (typeof config["repairTrack"] !== "string" || config["repairTrack"].length === 0) {
|
|
966
|
-
catalogError("track_fec", `${path}.fec.repairTrack`, "must be a non-empty string");
|
|
967
|
-
}
|
|
968
|
-
}
|
|
969
|
-
// Required on a declared FEC instance, optional but implemented on "none".
|
|
970
|
-
readFecBlockProfile(config["blockProfile"], `${path}.fec.blockProfile`, algorithm !== "none", catalogError);
|
|
971
|
-
// draft-ramadan-moq-fec-01 6.3.4 rule 13: an iso-ssbg1-v1 symbol shorter
|
|
972
|
-
// than 14 octets carries no protected MMTP packet (7.3).
|
|
973
|
-
if (algorithm !== "none" && (config["symbolSize"] as number) < ISO_SSBG1_MIN_SYMBOL_SIZE) {
|
|
974
|
-
catalogError("track_fec", `${path}.fec.symbolSize`, "iso-ssbg1-v1 symbolSize must be at least 14");
|
|
975
|
-
}
|
|
976
|
-
}
|
|
977
|
-
}
|
|
978
|
-
|
|
979
|
-
// Repair tracks (draft-ramadan-moq-fec §6.3). Every fec-repair track is
|
|
980
|
-
// described once; each FEC source then assembles its base layer chain and
|
|
981
|
-
// overlays through the shared contract, which fails closed with a code and
|
|
982
|
-
// the JSON path of the offending field.
|
|
983
|
-
const repairDescriptors = trackRecords.flatMap((track, index) =>
|
|
984
|
-
track["packaging"] === "fec-repair"
|
|
985
|
-
? [describeRepairTrack(track["name"] as string, `$.tracks[${index}]`, track)]
|
|
986
|
-
: [],
|
|
987
|
-
);
|
|
988
|
-
// Rule 9 (no orphan repair track) reads the same derivation the resolver
|
|
989
|
-
// uses; see `repairReachability` in repair-layers.ts for its scope.
|
|
990
|
-
const reachability = repairReachability(
|
|
991
|
-
trackRecords.filter((track) => track["packaging"] !== "fec-repair").map((track) => track["fec"]),
|
|
992
|
-
);
|
|
993
|
-
for (const descriptor of repairDescriptors) {
|
|
994
|
-
assertRepairTrackReachable(descriptor, reachability, catalogError);
|
|
995
|
-
const target = dependsTarget(descriptor);
|
|
996
|
-
if (target === undefined) continue;
|
|
997
|
-
const source = trackRecords.find((candidate) => candidate["name"] === target);
|
|
998
|
-
const sourceFec = source?.["fec"] as Record<string, unknown> | undefined;
|
|
999
|
-
if (!source || source["packaging"] !== "mmtp" || !sourceFec || sourceFec["algorithm"] === "none") {
|
|
1000
|
-
catalogError(
|
|
1001
|
-
"track_repair_depends",
|
|
1002
|
-
`${descriptor.path}.depends`,
|
|
1003
|
-
`${JSON.stringify(target)} is not an mmtp track with FEC`,
|
|
1004
|
-
);
|
|
1005
|
-
}
|
|
1006
|
-
}
|
|
1007
|
-
for (let index = 0; index < trackRecords.length; index++) {
|
|
1008
|
-
const track = trackRecords[index]!;
|
|
1009
|
-
const fec = track["fec"] as Record<string, unknown> | undefined;
|
|
1010
|
-
if (!fec || fec["algorithm"] === "none") continue;
|
|
1011
|
-
// A dangling fec.repairTrack (missing, or not a fec-repair track) fails
|
|
1012
|
-
// inside the shared contract, so the resolver and this validator agree.
|
|
1013
|
-
assembleRepairLayers(
|
|
1014
|
-
{
|
|
1015
|
-
name: track["name"] as string,
|
|
1016
|
-
path: `$.tracks[${index}].fec`,
|
|
1017
|
-
algorithm: fec["algorithm"],
|
|
1018
|
-
sourceSymbols: fec["sourceSymbols"],
|
|
1019
|
-
repairSymbols: fec["repairSymbols"],
|
|
1020
|
-
repairTrack: fec["repairTrack"],
|
|
1021
|
-
},
|
|
1022
|
-
repairDescriptors,
|
|
1023
|
-
catalogError,
|
|
1024
|
-
{ layerZeroMustBeDeclared: true },
|
|
1025
|
-
);
|
|
1026
836
|
}
|
|
1027
837
|
|
|
1028
838
|
// mmt-01 §4.4: alternates share group boundaries so a subscriber can switch
|