@blockcast/mmt-base 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/dist/fec-bindings.d.ts +103 -0
- package/dist/fec-bindings.d.ts.map +1 -0
- package/dist/fec-bindings.js +210 -0
- package/dist/fec-bindings.js.map +1 -0
- package/dist/fec-catalog.d.ts +41 -0
- package/dist/fec-catalog.d.ts.map +1 -0
- package/dist/fec-catalog.js +410 -0
- package/dist/fec-catalog.js.map +1 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/fec-bindings.ts +281 -0
- package/src/fec-catalog.ts +446 -0
- package/src/index.ts +2 -1
- package/dist/catalog-fec-source.d.ts +0 -88
- package/dist/catalog-fec-source.d.ts.map +0 -1
- package/dist/catalog-fec-source.js +0 -154
- package/dist/catalog-fec-source.js.map +0 -1
- package/src/catalog-fec-source.ts +0 -183
|
@@ -1,154 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Catalog FEC source rules shared by every catalog reader (BLO-40851).
|
|
3
|
-
*
|
|
4
|
-
* These are the dialect-level reads the container resolvers
|
|
5
|
-
* (`resolveRepairLayers`, `catalogFecUnavailable`), the transport endpoint
|
|
6
|
-
* selection (`selectEndpointPacketIds`) and a player's repair inventory all
|
|
7
|
-
* need to agree on. They live here, in the leaf package every one of those
|
|
8
|
-
* depends on, so there is one copy of each rule instead of copies pinned
|
|
9
|
-
* against each other by tests.
|
|
10
|
-
*
|
|
11
|
-
* - `locateCatalogFecSource`: which catalog entry declares a source's `fec`
|
|
12
|
-
* block. hang `video`/`audio` rendition blocks are authoritative over an MSF
|
|
13
|
-
* `tracks[]` entry of the same name.
|
|
14
|
-
* - `catalogFecAvailable`: draft-ramadan-moq-fec §5.1, whether a block's FEC
|
|
15
|
-
* is usable at all (implemented `blockProfile`, usable span).
|
|
16
|
-
* - `catalogFecDeclaresUnconfigurableInstance`: a declared FEC instance whose
|
|
17
|
-
* geometry a decoder cannot configure, which is a malformed catalog rather
|
|
18
|
-
* than "no FEC".
|
|
19
|
-
*
|
|
20
|
-
* Every function is total: it never throws, whatever the input.
|
|
21
|
-
*/
|
|
22
|
-
import { isFecBlockProfile } from './fec-block-profile.js';
|
|
23
|
-
const ALL_ROLES = Object.freeze(['video', 'audio']);
|
|
24
|
-
function recordOf(value) {
|
|
25
|
-
return value !== null && typeof value === 'object' && !Array.isArray(value)
|
|
26
|
-
? value
|
|
27
|
-
: undefined;
|
|
28
|
-
}
|
|
29
|
-
/**
|
|
30
|
-
* Locate the catalog entry that declares one media source's `fec` block, in
|
|
31
|
-
* either catalog dialect.
|
|
32
|
-
*
|
|
33
|
-
* A hang `video`/`audio` rendition named `source` wins, in `roles` order, over
|
|
34
|
-
* a `tracks[]` entry of the same name: when a catalog carries rendition blocks
|
|
35
|
-
* beside a `tracks[]` switching-set index (the unified ABR ladder catalog),
|
|
36
|
-
* the rendition blocks are authoritative, exactly as they are for hang's own
|
|
37
|
-
* catalog decode. The precedence is keyed on the rendition EXISTING as an
|
|
38
|
-
* object, not on it carrying `fec`: a rendition without `fec` locates with
|
|
39
|
-
* `fec: undefined` and never falls through to `tracks[]`.
|
|
40
|
-
*
|
|
41
|
-
* Otherwise the first `tracks[]` entry named `source` locates. Returns
|
|
42
|
-
* undefined when neither dialect names it, or when `catalog` is not an object.
|
|
43
|
-
*
|
|
44
|
-
* @param roles the hang roles to search, in order (both by default). A typed
|
|
45
|
-
* track reference passes its own role only.
|
|
46
|
-
*/
|
|
47
|
-
export function locateCatalogFecSource(catalog, source, roles = ALL_ROLES) {
|
|
48
|
-
const root = recordOf(catalog);
|
|
49
|
-
if (root === undefined)
|
|
50
|
-
return undefined;
|
|
51
|
-
for (const role of roles) {
|
|
52
|
-
const renditions = recordOf(recordOf(root[role])?.['renditions']);
|
|
53
|
-
if (renditions === undefined || !Object.hasOwn(renditions, source))
|
|
54
|
-
continue;
|
|
55
|
-
const entry = recordOf(renditions[source]);
|
|
56
|
-
if (entry === undefined)
|
|
57
|
-
continue;
|
|
58
|
-
return Object.freeze({
|
|
59
|
-
dialect: 'hang',
|
|
60
|
-
role,
|
|
61
|
-
entry,
|
|
62
|
-
fec: entry['fec'],
|
|
63
|
-
fecPath: `$.${role}.renditions[${JSON.stringify(source)}].fec`,
|
|
64
|
-
});
|
|
65
|
-
}
|
|
66
|
-
const tracks = root['tracks'];
|
|
67
|
-
if (!Array.isArray(tracks))
|
|
68
|
-
return undefined;
|
|
69
|
-
const index = tracks.findIndex((track) => recordOf(track)?.['name'] === source);
|
|
70
|
-
if (index < 0)
|
|
71
|
-
return undefined;
|
|
72
|
-
const entry = recordOf(tracks[index]) ?? {};
|
|
73
|
-
return Object.freeze({
|
|
74
|
-
dialect: 'msf',
|
|
75
|
-
index,
|
|
76
|
-
entry,
|
|
77
|
-
fec: entry['fec'],
|
|
78
|
-
fecPath: `$.tracks[${index}].fec`,
|
|
79
|
-
});
|
|
80
|
-
}
|
|
81
|
-
/**
|
|
82
|
-
* Whether an `iso-ssbg1-v1` block's optional `interleaveDepthMs` span is
|
|
83
|
-
* usable: absent, or a non-negative safe integer. Zero or absence supplies no
|
|
84
|
-
* elapsed deadline and still permits in-band FEC. True for every block that
|
|
85
|
-
* names another profile (the span is not read) and for a non-object.
|
|
86
|
-
*/
|
|
87
|
-
export function isoBlockSpanUsable(fec) {
|
|
88
|
-
const block = recordOf(fec);
|
|
89
|
-
if (block === undefined || block['blockProfile'] !== 'iso-ssbg1-v1')
|
|
90
|
-
return true;
|
|
91
|
-
const ms = block['interleaveDepthMs'];
|
|
92
|
-
return ms === undefined || (typeof ms === 'number' && Number.isSafeInteger(ms) && ms >= 0);
|
|
93
|
-
}
|
|
94
|
-
/**
|
|
95
|
-
* Whether one catalog `fec` block leaves its FEC available
|
|
96
|
-
* (draft-ramadan-moq-fec §5.1): the decision the container's
|
|
97
|
-
* `catalogFecUnavailable` explains with a typed reason.
|
|
98
|
-
*
|
|
99
|
-
* False when the block's `algorithm` is a token this receiver does not
|
|
100
|
-
* implement (`fecAlgorithmUnimplemented`); when it declares an FEC instance
|
|
101
|
-
* (any `algorithm` but `"none"`) and names no implemented `blockProfile` (the retired `k-padded-v1`
|
|
102
|
-
* included), or carries an `iso-ssbg1-v1` span it cannot use; and when a block
|
|
103
|
-
* declaring no FEC instance carries a profile token that is not implemented.
|
|
104
|
-
* True for a non-object and for every other block. It reads only the profile
|
|
105
|
-
* and the span: malformed geometry (`sourceSymbols`, `symbolSize`) is
|
|
106
|
-
* `catalogFecDeclaresUnconfigurableInstance`'s.
|
|
107
|
-
*
|
|
108
|
-
* A receiver applies no FEC to a source whose block is unavailable and joins
|
|
109
|
-
* none of its repair tracks, without failing the session.
|
|
110
|
-
*/
|
|
111
|
-
export function catalogFecAvailable(fec) {
|
|
112
|
-
const block = recordOf(fec);
|
|
113
|
-
if (block === undefined)
|
|
114
|
-
return true;
|
|
115
|
-
if (fecAlgorithmUnimplemented(block['algorithm']))
|
|
116
|
-
return false;
|
|
117
|
-
const declaresFecInstance = block['algorithm'] !== 'none';
|
|
118
|
-
const profile = block['blockProfile'];
|
|
119
|
-
const profileOk = isFecBlockProfile(profile) || (profile === undefined && !declaresFecInstance);
|
|
120
|
-
return profileOk && (!declaresFecInstance || isoBlockSpanUsable(block));
|
|
121
|
-
}
|
|
122
|
-
/**
|
|
123
|
-
* Whether `algorithm` is a token this receiver does not implement
|
|
124
|
-
* (draft-ramadan-moq-fec-01 4): any string but `none`, `raptorq` and
|
|
125
|
-
* `reed-solomon`. Such a track is consumed without FEC and its repair tracks
|
|
126
|
-
* are not joined; the catalog is not in error. An absent algorithm is not a
|
|
127
|
-
* token (the hang dialect omits it) and stays implemented.
|
|
128
|
-
*/
|
|
129
|
-
export function fecAlgorithmUnimplemented(algorithm) {
|
|
130
|
-
return typeof algorithm === 'string' && algorithm !== 'none' && algorithm !== 'raptorq' && algorithm !== 'reed-solomon';
|
|
131
|
-
}
|
|
132
|
-
function isPositiveSafeInteger(value) {
|
|
133
|
-
return typeof value === 'number' && Number.isSafeInteger(value) && value > 0;
|
|
134
|
-
}
|
|
135
|
-
/** Smallest `symbolSize` under `iso-ssbg1-v1` (draft-ramadan-moq-fec-01 5.1, 6.3.4 rule 13). */
|
|
136
|
-
export const ISO_SSBG1_MIN_SYMBOL_SIZE = 14;
|
|
137
|
-
/**
|
|
138
|
-
* Whether a source's `fec` block declares an FEC instance (any `algorithm`
|
|
139
|
-
* but `"none"`) whose `sourceSymbols` or `symbolSize` is not a positive
|
|
140
|
-
* integer, or whose `iso-ssbg1-v1` `symbolSize` is below
|
|
141
|
-
* `ISO_SSBG1_MIN_SYMBOL_SIZE` (rule 13: such a symbol carries no protected
|
|
142
|
-
* MMTP packet). Geometry a decoder cannot configure is a malformed catalog, not
|
|
143
|
-
* "no FEC": the container's per-track resolver throws `track_fec` for exactly
|
|
144
|
-
* this block, whatever its profile or span.
|
|
145
|
-
*/
|
|
146
|
-
export function catalogFecDeclaresUnconfigurableInstance(fec) {
|
|
147
|
-
const block = recordOf(fec);
|
|
148
|
-
if (block === undefined || block['algorithm'] === 'none' || fecAlgorithmUnimplemented(block['algorithm']))
|
|
149
|
-
return false;
|
|
150
|
-
if (!(isPositiveSafeInteger(block['sourceSymbols']) && isPositiveSafeInteger(block['symbolSize'])))
|
|
151
|
-
return true;
|
|
152
|
-
return block['blockProfile'] === 'iso-ssbg1-v1' && block['symbolSize'] < ISO_SSBG1_MIN_SYMBOL_SIZE;
|
|
153
|
-
}
|
|
154
|
-
//# sourceMappingURL=catalog-fec-source.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"catalog-fec-source.js","sourceRoot":"","sources":["../src/catalog-fec-source.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAA;AA6B1D,MAAM,SAAS,GAA8B,MAAM,CAAC,MAAM,CAAC,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC,CAAA;AAE9E,SAAS,QAAQ,CAAC,KAAc;IAC9B,OAAO,KAAK,KAAK,IAAI,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;QACzE,CAAC,CAAE,KAAiC;QACpC,CAAC,CAAC,SAAS,CAAA;AACf,CAAC;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,UAAU,sBAAsB,CACpC,OAAgB,EAChB,MAAc,EACd,QAAmC,SAAS;IAE5C,MAAM,IAAI,GAAG,QAAQ,CAAC,OAAO,CAAC,CAAA;IAC9B,IAAI,IAAI,KAAK,SAAS;QAAE,OAAO,SAAS,CAAA;IACxC,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,MAAM,UAAU,GAAG,QAAQ,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC,YAAY,CAAC,CAAC,CAAA;QACjE,IAAI,UAAU,KAAK,SAAS,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,UAAU,EAAE,MAAM,CAAC;YAAE,SAAQ;QAC5E,MAAM,KAAK,GAAG,QAAQ,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAA;QAC1C,IAAI,KAAK,KAAK,SAAS;YAAE,SAAQ;QACjC,OAAO,MAAM,CAAC,MAAM,CAAC;YACnB,OAAO,EAAE,MAAe;YACxB,IAAI;YACJ,KAAK;YACL,GAAG,EAAE,KAAK,CAAC,KAAK,CAAC;YACjB,OAAO,EAAE,KAAK,IAAI,eAAe,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,OAAO;SAC/D,CAAC,CAAA;IACJ,CAAC;IACD,MAAM,MAAM,GAAG,IAAI,CAAC,QAAQ,CAAC,CAAA;IAC7B,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC;QAAE,OAAO,SAAS,CAAA;IAC5C,MAAM,KAAK,GAAG,MAAM,CAAC,SAAS,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC,MAAM,CAAC,KAAK,MAAM,CAAC,CAAA;IAC/E,IAAI,KAAK,GAAG,CAAC;QAAE,OAAO,SAAS,CAAA;IAC/B,MAAM,KAAK,GAAG,QAAQ,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,IAAI,EAAE,CAAA;IAC3C,OAAO,MAAM,CAAC,MAAM,CAAC;QACnB,OAAO,EAAE,KAAc;QACvB,KAAK;QACL,KAAK;QACL,GAAG,EAAE,KAAK,CAAC,KAAK,CAAC;QACjB,OAAO,EAAE,YAAY,KAAK,OAAO;KAClC,CAAC,CAAA;AACJ,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,kBAAkB,CAAC,GAAY;IAC7C,MAAM,KAAK,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAA;IAC3B,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,CAAC,cAAc,CAAC,KAAK,cAAc;QAAE,OAAO,IAAI,CAAA;IAChF,MAAM,EAAE,GAAG,KAAK,CAAC,mBAAmB,CAAC,CAAA;IACrC,OAAO,EAAE,KAAK,SAAS,IAAI,CAAC,OAAO,EAAE,KAAK,QAAQ,IAAI,MAAM,CAAC,aAAa,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,CAAA;AAC5F,CAAC;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,UAAU,mBAAmB,CAAC,GAAY;IAC9C,MAAM,KAAK,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAA;IAC3B,IAAI,KAAK,KAAK,SAAS;QAAE,OAAO,IAAI,CAAA;IACpC,IAAI,yBAAyB,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC;QAAE,OAAO,KAAK,CAAA;IAC/D,MAAM,mBAAmB,GAAG,KAAK,CAAC,WAAW,CAAC,KAAK,MAAM,CAAA;IACzD,MAAM,OAAO,GAAG,KAAK,CAAC,cAAc,CAAC,CAAA;IACrC,MAAM,SAAS,GAAG,iBAAiB,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,KAAK,SAAS,IAAI,CAAC,mBAAmB,CAAC,CAAA;IAC/F,OAAO,SAAS,IAAI,CAAC,CAAC,mBAAmB,IAAI,kBAAkB,CAAC,KAAK,CAAC,CAAC,CAAA;AACzE,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,yBAAyB,CAAC,SAAkB;IAC1D,OAAO,OAAO,SAAS,KAAK,QAAQ,IAAI,SAAS,KAAK,MAAM,IAAI,SAAS,KAAK,SAAS,IAAI,SAAS,KAAK,cAAc,CAAA;AACzH,CAAC;AAED,SAAS,qBAAqB,CAAC,KAAc;IAC3C,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,MAAM,CAAC,aAAa,CAAC,KAAK,CAAC,IAAI,KAAK,GAAG,CAAC,CAAA;AAC9E,CAAC;AAED,gGAAgG;AAChG,MAAM,CAAC,MAAM,yBAAyB,GAAG,EAAE,CAAA;AAE3C;;;;;;;;GAQG;AACH,MAAM,UAAU,wCAAwC,CAAC,GAAY;IACnE,MAAM,KAAK,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAA;IAC3B,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,CAAC,WAAW,CAAC,KAAK,MAAM,IAAI,yBAAyB,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC;QAAE,OAAO,KAAK,CAAA;IACvH,IAAI,CAAC,CAAC,qBAAqB,CAAC,KAAK,CAAC,eAAe,CAAC,CAAC,IAAI,qBAAqB,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC,CAAC;QAAE,OAAO,IAAI,CAAA;IAC/G,OAAO,KAAK,CAAC,cAAc,CAAC,KAAK,cAAc,IAAK,KAAK,CAAC,YAAY,CAAY,GAAG,yBAAyB,CAAA;AAChH,CAAC"}
|
|
@@ -1,183 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Catalog FEC source rules shared by every catalog reader (BLO-40851).
|
|
3
|
-
*
|
|
4
|
-
* These are the dialect-level reads the container resolvers
|
|
5
|
-
* (`resolveRepairLayers`, `catalogFecUnavailable`), the transport endpoint
|
|
6
|
-
* selection (`selectEndpointPacketIds`) and a player's repair inventory all
|
|
7
|
-
* need to agree on. They live here, in the leaf package every one of those
|
|
8
|
-
* depends on, so there is one copy of each rule instead of copies pinned
|
|
9
|
-
* against each other by tests.
|
|
10
|
-
*
|
|
11
|
-
* - `locateCatalogFecSource`: which catalog entry declares a source's `fec`
|
|
12
|
-
* block. hang `video`/`audio` rendition blocks are authoritative over an MSF
|
|
13
|
-
* `tracks[]` entry of the same name.
|
|
14
|
-
* - `catalogFecAvailable`: draft-ramadan-moq-fec §5.1, whether a block's FEC
|
|
15
|
-
* is usable at all (implemented `blockProfile`, usable span).
|
|
16
|
-
* - `catalogFecDeclaresUnconfigurableInstance`: a declared FEC instance whose
|
|
17
|
-
* geometry a decoder cannot configure, which is a malformed catalog rather
|
|
18
|
-
* than "no FEC".
|
|
19
|
-
*
|
|
20
|
-
* Every function is total: it never throws, whatever the input.
|
|
21
|
-
*/
|
|
22
|
-
import { isFecBlockProfile } from './fec-block-profile.js'
|
|
23
|
-
|
|
24
|
-
/** The catalog media roles that carry hang renditions. */
|
|
25
|
-
export type CatalogFecRole = 'video' | 'audio'
|
|
26
|
-
|
|
27
|
-
/** One media source's `fec` block, as `locateCatalogFecSource` found it. */
|
|
28
|
-
export type CatalogFecSource =
|
|
29
|
-
| {
|
|
30
|
-
readonly dialect: 'hang'
|
|
31
|
-
readonly role: CatalogFecRole
|
|
32
|
-
/** The rendition object. */
|
|
33
|
-
readonly entry: Record<string, unknown>
|
|
34
|
-
/** The rendition's `fec` value, unvalidated (may be undefined). */
|
|
35
|
-
readonly fec: unknown
|
|
36
|
-
/** JSON path of that `fec` block. */
|
|
37
|
-
readonly fecPath: string
|
|
38
|
-
}
|
|
39
|
-
| {
|
|
40
|
-
readonly dialect: 'msf'
|
|
41
|
-
/** Index of the entry in `tracks[]`. */
|
|
42
|
-
readonly index: number
|
|
43
|
-
/** The `tracks[]` entry (an empty object when the entry is not an object). */
|
|
44
|
-
readonly entry: Record<string, unknown>
|
|
45
|
-
/** The entry's `fec` value, unvalidated (may be undefined). */
|
|
46
|
-
readonly fec: unknown
|
|
47
|
-
/** JSON path of that `fec` block. */
|
|
48
|
-
readonly fecPath: string
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
const ALL_ROLES: readonly CatalogFecRole[] = Object.freeze(['video', 'audio'])
|
|
52
|
-
|
|
53
|
-
function recordOf(value: unknown): Record<string, unknown> | undefined {
|
|
54
|
-
return value !== null && typeof value === 'object' && !Array.isArray(value)
|
|
55
|
-
? (value as Record<string, unknown>)
|
|
56
|
-
: undefined
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
/**
|
|
60
|
-
* Locate the catalog entry that declares one media source's `fec` block, in
|
|
61
|
-
* either catalog dialect.
|
|
62
|
-
*
|
|
63
|
-
* A hang `video`/`audio` rendition named `source` wins, in `roles` order, over
|
|
64
|
-
* a `tracks[]` entry of the same name: when a catalog carries rendition blocks
|
|
65
|
-
* beside a `tracks[]` switching-set index (the unified ABR ladder catalog),
|
|
66
|
-
* the rendition blocks are authoritative, exactly as they are for hang's own
|
|
67
|
-
* catalog decode. The precedence is keyed on the rendition EXISTING as an
|
|
68
|
-
* object, not on it carrying `fec`: a rendition without `fec` locates with
|
|
69
|
-
* `fec: undefined` and never falls through to `tracks[]`.
|
|
70
|
-
*
|
|
71
|
-
* Otherwise the first `tracks[]` entry named `source` locates. Returns
|
|
72
|
-
* undefined when neither dialect names it, or when `catalog` is not an object.
|
|
73
|
-
*
|
|
74
|
-
* @param roles the hang roles to search, in order (both by default). A typed
|
|
75
|
-
* track reference passes its own role only.
|
|
76
|
-
*/
|
|
77
|
-
export function locateCatalogFecSource(
|
|
78
|
-
catalog: unknown,
|
|
79
|
-
source: string,
|
|
80
|
-
roles: readonly CatalogFecRole[] = ALL_ROLES,
|
|
81
|
-
): CatalogFecSource | undefined {
|
|
82
|
-
const root = recordOf(catalog)
|
|
83
|
-
if (root === undefined) return undefined
|
|
84
|
-
for (const role of roles) {
|
|
85
|
-
const renditions = recordOf(recordOf(root[role])?.['renditions'])
|
|
86
|
-
if (renditions === undefined || !Object.hasOwn(renditions, source)) continue
|
|
87
|
-
const entry = recordOf(renditions[source])
|
|
88
|
-
if (entry === undefined) continue
|
|
89
|
-
return Object.freeze({
|
|
90
|
-
dialect: 'hang' as const,
|
|
91
|
-
role,
|
|
92
|
-
entry,
|
|
93
|
-
fec: entry['fec'],
|
|
94
|
-
fecPath: `$.${role}.renditions[${JSON.stringify(source)}].fec`,
|
|
95
|
-
})
|
|
96
|
-
}
|
|
97
|
-
const tracks = root['tracks']
|
|
98
|
-
if (!Array.isArray(tracks)) return undefined
|
|
99
|
-
const index = tracks.findIndex((track) => recordOf(track)?.['name'] === source)
|
|
100
|
-
if (index < 0) return undefined
|
|
101
|
-
const entry = recordOf(tracks[index]) ?? {}
|
|
102
|
-
return Object.freeze({
|
|
103
|
-
dialect: 'msf' as const,
|
|
104
|
-
index,
|
|
105
|
-
entry,
|
|
106
|
-
fec: entry['fec'],
|
|
107
|
-
fecPath: `$.tracks[${index}].fec`,
|
|
108
|
-
})
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
/**
|
|
112
|
-
* Whether an `iso-ssbg1-v1` block's optional `interleaveDepthMs` span is
|
|
113
|
-
* usable: absent, or a non-negative safe integer. Zero or absence supplies no
|
|
114
|
-
* elapsed deadline and still permits in-band FEC. True for every block that
|
|
115
|
-
* names another profile (the span is not read) and for a non-object.
|
|
116
|
-
*/
|
|
117
|
-
export function isoBlockSpanUsable(fec: unknown): boolean {
|
|
118
|
-
const block = recordOf(fec)
|
|
119
|
-
if (block === undefined || block['blockProfile'] !== 'iso-ssbg1-v1') return true
|
|
120
|
-
const ms = block['interleaveDepthMs']
|
|
121
|
-
return ms === undefined || (typeof ms === 'number' && Number.isSafeInteger(ms) && ms >= 0)
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
/**
|
|
125
|
-
* Whether one catalog `fec` block leaves its FEC available
|
|
126
|
-
* (draft-ramadan-moq-fec §5.1): the decision the container's
|
|
127
|
-
* `catalogFecUnavailable` explains with a typed reason.
|
|
128
|
-
*
|
|
129
|
-
* False when the block's `algorithm` is a token this receiver does not
|
|
130
|
-
* implement (`fecAlgorithmUnimplemented`); when it declares an FEC instance
|
|
131
|
-
* (any `algorithm` but `"none"`) and names no implemented `blockProfile` (the retired `k-padded-v1`
|
|
132
|
-
* included), or carries an `iso-ssbg1-v1` span it cannot use; and when a block
|
|
133
|
-
* declaring no FEC instance carries a profile token that is not implemented.
|
|
134
|
-
* True for a non-object and for every other block. It reads only the profile
|
|
135
|
-
* and the span: malformed geometry (`sourceSymbols`, `symbolSize`) is
|
|
136
|
-
* `catalogFecDeclaresUnconfigurableInstance`'s.
|
|
137
|
-
*
|
|
138
|
-
* A receiver applies no FEC to a source whose block is unavailable and joins
|
|
139
|
-
* none of its repair tracks, without failing the session.
|
|
140
|
-
*/
|
|
141
|
-
export function catalogFecAvailable(fec: unknown): boolean {
|
|
142
|
-
const block = recordOf(fec)
|
|
143
|
-
if (block === undefined) return true
|
|
144
|
-
if (fecAlgorithmUnimplemented(block['algorithm'])) return false
|
|
145
|
-
const declaresFecInstance = block['algorithm'] !== 'none'
|
|
146
|
-
const profile = block['blockProfile']
|
|
147
|
-
const profileOk = isFecBlockProfile(profile) || (profile === undefined && !declaresFecInstance)
|
|
148
|
-
return profileOk && (!declaresFecInstance || isoBlockSpanUsable(block))
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
/**
|
|
152
|
-
* Whether `algorithm` is a token this receiver does not implement
|
|
153
|
-
* (draft-ramadan-moq-fec-01 4): any string but `none`, `raptorq` and
|
|
154
|
-
* `reed-solomon`. Such a track is consumed without FEC and its repair tracks
|
|
155
|
-
* are not joined; the catalog is not in error. An absent algorithm is not a
|
|
156
|
-
* token (the hang dialect omits it) and stays implemented.
|
|
157
|
-
*/
|
|
158
|
-
export function fecAlgorithmUnimplemented(algorithm: unknown): boolean {
|
|
159
|
-
return typeof algorithm === 'string' && algorithm !== 'none' && algorithm !== 'raptorq' && algorithm !== 'reed-solomon'
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
function isPositiveSafeInteger(value: unknown): boolean {
|
|
163
|
-
return typeof value === 'number' && Number.isSafeInteger(value) && value > 0
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
/** Smallest `symbolSize` under `iso-ssbg1-v1` (draft-ramadan-moq-fec-01 5.1, 6.3.4 rule 13). */
|
|
167
|
-
export const ISO_SSBG1_MIN_SYMBOL_SIZE = 14
|
|
168
|
-
|
|
169
|
-
/**
|
|
170
|
-
* Whether a source's `fec` block declares an FEC instance (any `algorithm`
|
|
171
|
-
* but `"none"`) whose `sourceSymbols` or `symbolSize` is not a positive
|
|
172
|
-
* integer, or whose `iso-ssbg1-v1` `symbolSize` is below
|
|
173
|
-
* `ISO_SSBG1_MIN_SYMBOL_SIZE` (rule 13: such a symbol carries no protected
|
|
174
|
-
* MMTP packet). Geometry a decoder cannot configure is a malformed catalog, not
|
|
175
|
-
* "no FEC": the container's per-track resolver throws `track_fec` for exactly
|
|
176
|
-
* this block, whatever its profile or span.
|
|
177
|
-
*/
|
|
178
|
-
export function catalogFecDeclaresUnconfigurableInstance(fec: unknown): boolean {
|
|
179
|
-
const block = recordOf(fec)
|
|
180
|
-
if (block === undefined || block['algorithm'] === 'none' || fecAlgorithmUnimplemented(block['algorithm'])) return false
|
|
181
|
-
if (!(isPositiveSafeInteger(block['sourceSymbols']) && isPositiveSafeInteger(block['symbolSize']))) return true
|
|
182
|
-
return block['blockProfile'] === 'iso-ssbg1-v1' && (block['symbolSize'] as number) < ISO_SSBG1_MIN_SYMBOL_SIZE
|
|
183
|
-
}
|