@norskvideo/moq-msf 0.1.3 → 0.1.4
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/catalog.d.ts +4 -2
- package/catalog.d.ts.map +1 -1
- package/catalog.js +4 -2
- package/catalog.js.map +1 -1
- package/package.json +4 -3
- package/catalog.test.d.ts +0 -2
- package/catalog.test.d.ts.map +0 -1
- package/catalog.test.js +0 -147
- package/catalog.test.js.map +0 -1
package/catalog.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type * as Moq from "@norskvideo/moq-net";
|
|
2
|
-
import * as z from "zod/mini";
|
|
2
|
+
import * as z from "@zod/mini";
|
|
3
3
|
/** Zod schema for a track's wire packaging. Accepts known values or any future string. */
|
|
4
4
|
export declare const PackagingSchema: z.ZodMiniUnion<readonly [z.ZodMiniEnum<{
|
|
5
5
|
cmaf: "cmaf";
|
|
@@ -47,6 +47,7 @@ export declare const TrackSchema: z.ZodMiniObject<{
|
|
|
47
47
|
samplerate: z.ZodMiniOptional<z.ZodMiniNumber<number>>;
|
|
48
48
|
channelConfig: z.ZodMiniOptional<z.ZodMiniString<string>>;
|
|
49
49
|
bitrate: z.ZodMiniOptional<z.ZodMiniNumber<number>>;
|
|
50
|
+
stalled: z.ZodMiniOptional<z.ZodMiniBoolean<boolean>>;
|
|
50
51
|
initData: z.ZodMiniOptional<z.ZodMiniString<string>>;
|
|
51
52
|
renderGroup: z.ZodMiniOptional<z.ZodMiniNumber<number>>;
|
|
52
53
|
altGroup: z.ZodMiniOptional<z.ZodMiniNumber<number>>;
|
|
@@ -83,6 +84,7 @@ export declare const CatalogSchema: z.ZodMiniObject<{
|
|
|
83
84
|
samplerate: z.ZodMiniOptional<z.ZodMiniNumber<number>>;
|
|
84
85
|
channelConfig: z.ZodMiniOptional<z.ZodMiniString<string>>;
|
|
85
86
|
bitrate: z.ZodMiniOptional<z.ZodMiniNumber<number>>;
|
|
87
|
+
stalled: z.ZodMiniOptional<z.ZodMiniBoolean<boolean>>;
|
|
86
88
|
initData: z.ZodMiniOptional<z.ZodMiniString<string>>;
|
|
87
89
|
renderGroup: z.ZodMiniOptional<z.ZodMiniNumber<number>>;
|
|
88
90
|
altGroup: z.ZodMiniOptional<z.ZodMiniNumber<number>>;
|
|
@@ -100,5 +102,5 @@ export declare function encode(catalog: Catalog): Uint8Array;
|
|
|
100
102
|
/** Parse and validate a catalog from its JSON wire representation. Throws if invalid. */
|
|
101
103
|
export declare function decode(raw: Uint8Array): Catalog;
|
|
102
104
|
/** Read and decode the next catalog frame from a track, or undefined if the track ended. */
|
|
103
|
-
export declare function fetch(track: Moq.Track): Promise<Catalog | undefined>;
|
|
105
|
+
export declare function fetch(track: Moq.Track.Subscriber): Promise<Catalog | undefined>;
|
|
104
106
|
//# sourceMappingURL=catalog.d.ts.map
|
package/catalog.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"catalog.d.ts","sourceRoot":"","sources":["../src/catalog.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,GAAG,MAAM,UAAU,CAAC;AACrC,OAAO,KAAK,CAAC,MAAM,
|
|
1
|
+
{"version":3,"file":"catalog.d.ts","sourceRoot":"","sources":["../src/catalog.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,GAAG,MAAM,UAAU,CAAC;AACrC,OAAO,KAAK,CAAC,MAAM,WAAW,CAAC;AAE/B,0FAA0F;AAC1F,eAAO,MAAM,eAAe;;;;;;6BAG1B,CAAC;AAEH,4EAA4E;AAC5E,MAAM,MAAM,SAAS,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,eAAe,CAAC,CAAC;AAExD,gFAAgF;AAChF,eAAO,MAAM,UAAU;;;;;;;6BAGrB,CAAC;AAEH,8FAA8F;AAC9F,MAAM,MAAM,IAAI,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,UAAU,CAAC,CAAC;AAiC9C,oEAAoE;AACpE,eAAO,MAAM,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAAuB,CAAC;AAEhD,kFAAkF;AAClF,MAAM,MAAM,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,WAAW,CAAC,CAAC;AAEhD,uFAAuF;AACvF,eAAO,MAAM,aAAa;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAExB,CAAC;AAEH,2DAA2D;AAC3D,MAAM,MAAM,OAAO,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,aAAa,CAAC,CAAC;AAEpD,0EAA0E;AAC1E,eAAO,MAAM,OAAO,aAAa,CAAC;AA0BlC,sEAAsE;AACtE,wBAAgB,MAAM,CAAC,OAAO,EAAE,OAAO,GAAG,UAAU,CAwBnD;AAED,yFAAyF;AACzF,wBAAgB,MAAM,CAAC,GAAG,EAAE,UAAU,GAAG,OAAO,CA2B/C;AAED,4FAA4F;AAC5F,wBAAsB,KAAK,CAAC,KAAK,EAAE,GAAG,CAAC,KAAK,CAAC,UAAU,GAAG,OAAO,CAAC,OAAO,GAAG,SAAS,CAAC,CAIrF"}
|
package/catalog.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/* @ts-self-types="./catalog.d.ts" */
|
|
2
|
-
import * as z from "zod/mini";
|
|
2
|
+
import * as z from "@zod/mini";
|
|
3
3
|
/** Zod schema for a track's wire packaging. Accepts known values or any future string. */
|
|
4
4
|
export const PackagingSchema = z.union([
|
|
5
5
|
z.enum(["loc", "cmaf", "legacy", "mediatimeline", "eventtimeline"]),
|
|
@@ -26,6 +26,8 @@ const trackShape = {
|
|
|
26
26
|
samplerate: z.optional(z.number()),
|
|
27
27
|
channelConfig: z.optional(z.string()),
|
|
28
28
|
bitrate: z.optional(z.number()),
|
|
29
|
+
// Non-standard: whether the publisher recommends temporarily avoiding this track.
|
|
30
|
+
stalled: z.optional(z.boolean()),
|
|
29
31
|
/** Resolved base64 initialization data (draft-01's initRef indirection is resolved away). */
|
|
30
32
|
initData: z.optional(z.string()),
|
|
31
33
|
renderGroup: z.optional(z.number()),
|
|
@@ -120,6 +122,6 @@ export async function fetch(track) {
|
|
|
120
122
|
const frame = await track.readFrame();
|
|
121
123
|
if (!frame)
|
|
122
124
|
return undefined;
|
|
123
|
-
return decode(frame);
|
|
125
|
+
return decode(frame.payload);
|
|
124
126
|
}
|
|
125
127
|
//# sourceMappingURL=catalog.js.map
|
package/catalog.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"catalog.js","sourceRoot":"","sources":["../src/catalog.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,CAAC,MAAM,UAAU,CAAC;AAE9B,0FAA0F;AAC1F,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC;IACtC,CAAC,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,eAAe,EAAE,eAAe,CAAC,CAAC;IACnE,CAAC,CAAC,MAAM,EAAE;CACV,CAAC,CAAC;AAKH,gFAAgF;AAChF,MAAM,CAAC,MAAM,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC;IACjC,CAAC,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,OAAO,EAAE,kBAAkB,EAAE,SAAS,EAAE,UAAU,EAAE,cAAc,CAAC,CAAC;IACrF,CAAC,CAAC,MAAM,EAAE;CACV,CAAC,CAAC;AAKH,iFAAiF;AACjF,kFAAkF;AAClF,MAAM,UAAU,GAAG;IAClB,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE;IAChB,SAAS,EAAE,eAAe;IAC1B,6EAA6E;IAC7E,sEAAsE;IACtE,MAAM,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC;IAC/B,IAAI,EAAE,CAAC,CAAC,QAAQ,CAAC,UAAU,CAAC;IAC5B,KAAK,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;IAC7B,KAAK,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;IAC7B,MAAM,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;IAC9B,SAAS,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;IACjC,UAAU,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;IAClC,aAAa,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;IACrC,OAAO,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;IAC/B,6FAA6F;IAC7F,QAAQ,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;IAChC,WAAW,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;IACnC,QAAQ,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;IAChC,qBAAqB,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;IAC7C,qBAAqB,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;IAE7C,6EAA6E;IAC7E,sEAAsE;IACtE,kDAAkD;IAClD,MAAM,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;CAC9B,CAAC;AAEF,oEAAoE;AACpE,MAAM,CAAC,MAAM,WAAW,GAAG,CAAC,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;AAKhD,uFAAuF;AACvF,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,CAAC,MAAM,CAAC;IACrC,MAAM,EAAE,CAAC,CAAC,KAAK,CAAC,WAAW,CAAC;CAC5B,CAAC,CAAC;AAKH,0EAA0E;AAC1E,MAAM,CAAC,MAAM,OAAO,GAAG,UAAU,CAAC;AAElC,+EAA+E;AAC/E,EAAE;AACF,+EAA+E;AAC/E,4EAA4E;AAC5E,6EAA6E;AAE7E,MAAM,cAAc,GAAG,CAAC,CAAC,MAAM,CAAC;IAC/B,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE;IACd,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE;IAChB,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE;CAChB,CAAC,CAAC;AAEH,MAAM,eAAe,GAAG,CAAC,CAAC,MAAM,CAAC;IAChC,GAAG,UAAU;IACb,OAAO,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;CAC/B,CAAC,CAAC;AAEH,MAAM,iBAAiB,GAAG,CAAC,CAAC,MAAM,CAAC;IAClC,8EAA8E;IAC9E,OAAO,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC;IAC5C,MAAM,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,eAAe,CAAC,CAAC;IAC5C,YAAY,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,cAAc,CAAC,CAAC;CACjD,CAAC,CAAC;AAEH,sEAAsE;AACtE,MAAM,UAAU,MAAM,CAAC,OAAgB;IACtC,0EAA0E;IAC1E,uEAAuE;IACvE,MAAM,YAAY,GAAqC,EAAE,CAAC;IAC1D,MAAM,GAAG,GAAG,IAAI,GAAG,EAAkB,CAAC;IAEtC,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE;QAC3C,MAAM,EAAE,QAAQ,EAAE,GAAG,IAAI,EAAE,GAAG,KAAK,CAAC;QACpC,MAAM,SAAS,GAAG,EAAE,GAAG,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,IAAI,KAAK,EAAE,CAAC;QAC5D,IAAI,QAAQ,KAAK,SAAS;YAAE,OAAO,SAAS,CAAC;QAE7C,IAAI,EAAE,GAAG,GAAG,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QAC3B,IAAI,EAAE,KAAK,SAAS,EAAE,CAAC;YACtB,EAAE,GAAG,OAAO,YAAY,CAAC,MAAM,EAAE,CAAC;YAClC,YAAY,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC,CAAC;YAC1D,GAAG,CAAC,GAAG,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;QACvB,CAAC;QACD,OAAO,EAAE,GAAG,SAAS,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC;IACtC,CAAC,CAAC,CAAC;IAEH,MAAM,IAAI,GAA4B,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC;IACnE,IAAI,YAAY,CAAC,MAAM,GAAG,CAAC;QAAE,IAAI,CAAC,YAAY,GAAG,YAAY,CAAC;IAE9D,OAAO,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC;AACvD,CAAC;AAED,yFAAyF;AACzF,MAAM,UAAU,MAAM,CAAC,GAAe;IACrC,MAAM,GAAG,GAAG,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;IAC1C,IAAI,CAAC;QACJ,MAAM,IAAI,GAAG,iBAAiB,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC;QAEtD,yEAAyE;QACzE,0CAA0C;QAC1C,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,CAAC,IAAI,CAAC,YAAY,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAEhH,MAAM,MAAM,GAAG,CAAC,IAAI,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,EAAE,OAAO,EAAE,GAAG,KAAK,EAAE,EAAE,EAAE;YAChE,KAAK,CAAC,MAAM,KAAK,KAAK,CAAC;YAEvB,qEAAqE;YACrE,iEAAiE;YACjE,IAAI,KAAK,CAAC,QAAQ,KAAK,SAAS,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;gBAC3D,2EAA2E;gBAC3E,4EAA4E;gBAC5E,KAAK,CAAC,QAAQ,GAAG,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;YACtC,CAAC;YACD,OAAO,KAAK,CAAC;QACd,CAAC,CAAC,CAAC;QAEH,OAAO,EAAE,MAAM,EAAE,CAAC;IACnB,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QAChB,OAAO,CAAC,IAAI,CAAC,qBAAqB,EAAE,GAAG,CAAC,CAAC;QACzC,MAAM,KAAK,CAAC;IACb,CAAC;AACF,CAAC;AAED,4FAA4F;AAC5F,MAAM,CAAC,KAAK,UAAU,KAAK,CAAC,KAAgB;IAC3C,MAAM,KAAK,GAAG,MAAM,KAAK,CAAC,SAAS,EAAE,CAAC;IACtC,IAAI,CAAC,KAAK;QAAE,OAAO,SAAS,CAAC;IAC7B,OAAO,MAAM,CAAC,KAAK,CAAC,CAAC;AACtB,CAAC","sourcesContent":["import type * as Moq from \"@moq/net\";\nimport * as z from \"zod/mini\";\n\n/** Zod schema for a track's wire packaging. Accepts known values or any future string. */\nexport const PackagingSchema = z.union([\n\tz.enum([\"loc\", \"cmaf\", \"legacy\", \"mediatimeline\", \"eventtimeline\"]),\n\tz.string(),\n]);\n\n/** How a track's frames are packaged on the wire (e.g. \"loc\" or \"cmaf\"). */\nexport type Packaging = z.infer<typeof PackagingSchema>;\n\n/** Zod schema for a track's role. Accepts known values or any future string. */\nexport const RoleSchema = z.union([\n\tz.enum([\"video\", \"audio\", \"audiodescription\", \"caption\", \"subtitle\", \"signlanguage\"]),\n\tz.string(),\n]);\n\n/** The semantic role a track plays in the presentation (e.g. \"video\", \"audio\", \"caption\"). */\nexport type Role = z.infer<typeof RoleSchema>;\n\n// Shared track fields. This is the version-agnostic shape callers see: init data\n// is exposed inline via `initData`, regardless of how it was carried on the wire.\nconst trackShape = {\n\tname: z.string(),\n\tpackaging: PackagingSchema,\n\t// draft-00 marks isLive required but omits it on mediatimeline/eventtimeline\n\t// tracks, so accept its absence rather than reject the whole catalog.\n\tisLive: z.optional(z.boolean()),\n\trole: z.optional(RoleSchema),\n\tcodec: z.optional(z.string()),\n\twidth: z.optional(z.number()),\n\theight: z.optional(z.number()),\n\tframerate: z.optional(z.number()),\n\tsamplerate: z.optional(z.number()),\n\tchannelConfig: z.optional(z.string()),\n\tbitrate: z.optional(z.number()),\n\t/** Resolved base64 initialization data (draft-01's initRef indirection is resolved away). */\n\tinitData: z.optional(z.string()),\n\trenderGroup: z.optional(z.number()),\n\taltGroup: z.optional(z.number()),\n\tmaxGrpSapStartingType: z.optional(z.number()),\n\tmaxObjSapStartingType: z.optional(z.number()),\n\n\t// Non-standard: maximum delay (ms) between consecutive frames on this track.\n\t// The player's buffer must be at least this large to avoid underruns.\n\t// Mirrors the `jitter` field in the hang catalog.\n\tjitter: z.optional(z.number()),\n};\n\n/** Zod schema describing a single track entry in an MSF catalog. */\nexport const TrackSchema = z.object(trackShape);\n\n/** A single track in an MSF catalog, including its codec and media properties. */\nexport type Track = z.infer<typeof TrackSchema>;\n\n/** Zod schema for the top-level MSF catalog: a version-agnostic snapshot of tracks. */\nexport const CatalogSchema = z.object({\n\ttracks: z.array(TrackSchema),\n});\n\n/** The MSF catalog: a snapshot of the available tracks. */\nexport type Catalog = z.infer<typeof CatalogSchema>;\n\n/** The newest MSF draft version string this package emits on the wire. */\nexport const VERSION = \"draft-01\";\n\n// --- Wire representation (internal) -----------------------------------------\n//\n// The wire format hides two things from callers: the catalog `version` (number\n// in draft-00, \"draft-XX\" string in draft-01) and init data, which draft-01\n// moved out of the track into a root `initDataList` referenced by `initRef`.\n\nconst InitDataSchema = z.object({\n\tid: z.string(),\n\ttype: z.string(),\n\tdata: z.string(),\n});\n\nconst WireTrackSchema = z.object({\n\t...trackShape,\n\tinitRef: z.optional(z.string()),\n});\n\nconst WireCatalogSchema = z.object({\n\t// draft-00 used the number 1; draft-01 uses a \"draft-XX\" string. Accept both.\n\tversion: z.union([z.literal(1), z.string()]),\n\ttracks: z.optional(z.array(WireTrackSchema)),\n\tinitDataList: z.optional(z.array(InitDataSchema)),\n});\n\n/** Serialize a catalog to its JSON wire representation (draft-01). */\nexport function encode(catalog: Catalog): Uint8Array {\n\t// Hoist inline init payloads into a shared, deduplicated initDataList and\n\t// reference each from its track via initRef (the draft-01 wire shape).\n\tconst initDataList: z.infer<typeof InitDataSchema>[] = [];\n\tconst ids = new Map<string, string>();\n\n\tconst tracks = catalog.tracks.map((track) => {\n\t\tconst { initData, ...rest } = track;\n\t\tconst wireTrack = { ...rest, isLive: rest.isLive ?? false };\n\t\tif (initData === undefined) return wireTrack;\n\n\t\tlet id = ids.get(initData);\n\t\tif (id === undefined) {\n\t\t\tid = `init${initDataList.length}`;\n\t\t\tinitDataList.push({ id, type: \"inline\", data: initData });\n\t\t\tids.set(initData, id);\n\t\t}\n\t\treturn { ...wireTrack, initRef: id };\n\t});\n\n\tconst wire: Record<string, unknown> = { version: VERSION, tracks };\n\tif (initDataList.length > 0) wire.initDataList = initDataList;\n\n\treturn new TextEncoder().encode(JSON.stringify(wire));\n}\n\n/** Parse and validate a catalog from its JSON wire representation. Throws if invalid. */\nexport function decode(raw: Uint8Array): Catalog {\n\tconst str = new TextDecoder().decode(raw);\n\ttry {\n\t\tconst wire = WireCatalogSchema.parse(JSON.parse(str));\n\n\t\t// id -> inline payload, built once so resolution is linear in the number\n\t\t// of tracks rather than tracks x entries.\n\t\tconst inline = new Map((wire.initDataList ?? []).filter((e) => e.type === \"inline\").map((e) => [e.id, e.data]));\n\n\t\tconst tracks = (wire.tracks ?? []).map(({ initRef, ...track }) => {\n\t\t\ttrack.isLive ??= false;\n\n\t\t\t// Resolve draft-01 initRef into inline initData so callers never see\n\t\t\t// the indirection. Inline initData (draft-00) is left untouched.\n\t\t\tif (track.initData === undefined && initRef !== undefined) {\n\t\t\t\t// Only inline entries are resolved here. A non-inline (e.g. `url`) initRef\n\t\t\t\t// legitimately stays undefined; the consumer fetches that init out-of-band.\n\t\t\t\ttrack.initData = inline.get(initRef);\n\t\t\t}\n\t\t\treturn track;\n\t\t});\n\n\t\treturn { tracks };\n\t} catch (error) {\n\t\tconsole.warn(\"invalid MSF catalog\", str);\n\t\tthrow error;\n\t}\n}\n\n/** Read and decode the next catalog frame from a track, or undefined if the track ended. */\nexport async function fetch(track: Moq.Track): Promise<Catalog | undefined> {\n\tconst frame = await track.readFrame();\n\tif (!frame) return undefined;\n\treturn decode(frame);\n}\n"]}
|
|
1
|
+
{"version":3,"file":"catalog.js","sourceRoot":"","sources":["../src/catalog.ts"],"names":[],"mappings":"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","sourcesContent":["import type * as Moq from \"@moq/net\";\nimport * as z from \"@zod/mini\";\n\n/** Zod schema for a track's wire packaging. Accepts known values or any future string. */\nexport const PackagingSchema = z.union([\n\tz.enum([\"loc\", \"cmaf\", \"legacy\", \"mediatimeline\", \"eventtimeline\"]),\n\tz.string(),\n]);\n\n/** How a track's frames are packaged on the wire (e.g. \"loc\" or \"cmaf\"). */\nexport type Packaging = z.infer<typeof PackagingSchema>;\n\n/** Zod schema for a track's role. Accepts known values or any future string. */\nexport const RoleSchema = z.union([\n\tz.enum([\"video\", \"audio\", \"audiodescription\", \"caption\", \"subtitle\", \"signlanguage\"]),\n\tz.string(),\n]);\n\n/** The semantic role a track plays in the presentation (e.g. \"video\", \"audio\", \"caption\"). */\nexport type Role = z.infer<typeof RoleSchema>;\n\n// Shared track fields. This is the version-agnostic shape callers see: init data\n// is exposed inline via `initData`, regardless of how it was carried on the wire.\nconst trackShape = {\n\tname: z.string(),\n\tpackaging: PackagingSchema,\n\t// draft-00 marks isLive required but omits it on mediatimeline/eventtimeline\n\t// tracks, so accept its absence rather than reject the whole catalog.\n\tisLive: z.optional(z.boolean()),\n\trole: z.optional(RoleSchema),\n\tcodec: z.optional(z.string()),\n\twidth: z.optional(z.number()),\n\theight: z.optional(z.number()),\n\tframerate: z.optional(z.number()),\n\tsamplerate: z.optional(z.number()),\n\tchannelConfig: z.optional(z.string()),\n\tbitrate: z.optional(z.number()),\n\t// Non-standard: whether the publisher recommends temporarily avoiding this track.\n\tstalled: z.optional(z.boolean()),\n\t/** Resolved base64 initialization data (draft-01's initRef indirection is resolved away). */\n\tinitData: z.optional(z.string()),\n\trenderGroup: z.optional(z.number()),\n\taltGroup: z.optional(z.number()),\n\tmaxGrpSapStartingType: z.optional(z.number()),\n\tmaxObjSapStartingType: z.optional(z.number()),\n\n\t// Non-standard: maximum delay (ms) between consecutive frames on this track.\n\t// The player's buffer must be at least this large to avoid underruns.\n\t// Mirrors the `jitter` field in the hang catalog.\n\tjitter: z.optional(z.number()),\n};\n\n/** Zod schema describing a single track entry in an MSF catalog. */\nexport const TrackSchema = z.object(trackShape);\n\n/** A single track in an MSF catalog, including its codec and media properties. */\nexport type Track = z.infer<typeof TrackSchema>;\n\n/** Zod schema for the top-level MSF catalog: a version-agnostic snapshot of tracks. */\nexport const CatalogSchema = z.object({\n\ttracks: z.array(TrackSchema),\n});\n\n/** The MSF catalog: a snapshot of the available tracks. */\nexport type Catalog = z.infer<typeof CatalogSchema>;\n\n/** The newest MSF draft version string this package emits on the wire. */\nexport const VERSION = \"draft-01\";\n\n// --- Wire representation (internal) -----------------------------------------\n//\n// The wire format hides two things from callers: the catalog `version` (number\n// in draft-00, \"draft-XX\" string in draft-01) and init data, which draft-01\n// moved out of the track into a root `initDataList` referenced by `initRef`.\n\nconst InitDataSchema = z.object({\n\tid: z.string(),\n\ttype: z.string(),\n\tdata: z.string(),\n});\n\nconst WireTrackSchema = z.object({\n\t...trackShape,\n\tinitRef: z.optional(z.string()),\n});\n\nconst WireCatalogSchema = z.object({\n\t// draft-00 used the number 1; draft-01 uses a \"draft-XX\" string. Accept both.\n\tversion: z.union([z.literal(1), z.string()]),\n\ttracks: z.optional(z.array(WireTrackSchema)),\n\tinitDataList: z.optional(z.array(InitDataSchema)),\n});\n\n/** Serialize a catalog to its JSON wire representation (draft-01). */\nexport function encode(catalog: Catalog): Uint8Array {\n\t// Hoist inline init payloads into a shared, deduplicated initDataList and\n\t// reference each from its track via initRef (the draft-01 wire shape).\n\tconst initDataList: z.infer<typeof InitDataSchema>[] = [];\n\tconst ids = new Map<string, string>();\n\n\tconst tracks = catalog.tracks.map((track) => {\n\t\tconst { initData, ...rest } = track;\n\t\tconst wireTrack = { ...rest, isLive: rest.isLive ?? false };\n\t\tif (initData === undefined) return wireTrack;\n\n\t\tlet id = ids.get(initData);\n\t\tif (id === undefined) {\n\t\t\tid = `init${initDataList.length}`;\n\t\t\tinitDataList.push({ id, type: \"inline\", data: initData });\n\t\t\tids.set(initData, id);\n\t\t}\n\t\treturn { ...wireTrack, initRef: id };\n\t});\n\n\tconst wire: Record<string, unknown> = { version: VERSION, tracks };\n\tif (initDataList.length > 0) wire.initDataList = initDataList;\n\n\treturn new TextEncoder().encode(JSON.stringify(wire));\n}\n\n/** Parse and validate a catalog from its JSON wire representation. Throws if invalid. */\nexport function decode(raw: Uint8Array): Catalog {\n\tconst str = new TextDecoder().decode(raw);\n\ttry {\n\t\tconst wire = WireCatalogSchema.parse(JSON.parse(str));\n\n\t\t// id -> inline payload, built once so resolution is linear in the number\n\t\t// of tracks rather than tracks x entries.\n\t\tconst inline = new Map((wire.initDataList ?? []).filter((e) => e.type === \"inline\").map((e) => [e.id, e.data]));\n\n\t\tconst tracks = (wire.tracks ?? []).map(({ initRef, ...track }) => {\n\t\t\ttrack.isLive ??= false;\n\n\t\t\t// Resolve draft-01 initRef into inline initData so callers never see\n\t\t\t// the indirection. Inline initData (draft-00) is left untouched.\n\t\t\tif (track.initData === undefined && initRef !== undefined) {\n\t\t\t\t// Only inline entries are resolved here. A non-inline (e.g. `url`) initRef\n\t\t\t\t// legitimately stays undefined; the consumer fetches that init out-of-band.\n\t\t\t\ttrack.initData = inline.get(initRef);\n\t\t\t}\n\t\t\treturn track;\n\t\t});\n\n\t\treturn { tracks };\n\t} catch (error) {\n\t\tconsole.warn(\"invalid MSF catalog\", str);\n\t\tthrow error;\n\t}\n}\n\n/** Read and decode the next catalog frame from a track, or undefined if the track ended. */\nexport async function fetch(track: Moq.Track.Subscriber): Promise<Catalog | undefined> {\n\tconst frame = await track.readFrame();\n\tif (!frame) return undefined;\n\treturn decode(frame.payload);\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@norskvideo/moq-msf",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.1.
|
|
4
|
+
"version": "0.1.4",
|
|
5
5
|
"description": "MSF (MOQT Streaming Format) catalog types for MoQ",
|
|
6
6
|
"license": "(MIT OR Apache-2.0)",
|
|
7
7
|
"repository": "github:moq-dev/moq",
|
|
@@ -13,7 +13,8 @@
|
|
|
13
13
|
},
|
|
14
14
|
"sideEffects": false,
|
|
15
15
|
"dependencies": {
|
|
16
|
-
"zod": "^4.4
|
|
17
|
-
"
|
|
16
|
+
"@zod/mini": "^4.5.4",
|
|
17
|
+
"zod": "^4.5.4",
|
|
18
|
+
"@norskvideo/moq-net": "^0.1.4"
|
|
18
19
|
}
|
|
19
20
|
}
|
package/catalog.test.d.ts
DELETED
package/catalog.test.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"catalog.test.d.ts","sourceRoot":"","sources":["../src/catalog.test.ts"],"names":[],"mappings":""}
|
package/catalog.test.js
DELETED
|
@@ -1,147 +0,0 @@
|
|
|
1
|
-
/* @ts-self-types="./catalog.test.d.ts" */
|
|
2
|
-
import { expect, test } from "bun:test";
|
|
3
|
-
import { decode, encode } from "./catalog.js";
|
|
4
|
-
function encodeJson(value) {
|
|
5
|
-
return new TextEncoder().encode(JSON.stringify(value));
|
|
6
|
-
}
|
|
7
|
-
function decodeJson(raw) {
|
|
8
|
-
return JSON.parse(new TextDecoder().decode(raw));
|
|
9
|
-
}
|
|
10
|
-
test("decodes a draft-00 catalog with a numeric version", () => {
|
|
11
|
-
// Example 1 from draft-ietf-moq-msf-00, trimmed. Numeric version plus unmodeled
|
|
12
|
-
// fields (namespace, targetLatency, generatedAt) which must be ignored.
|
|
13
|
-
const catalog = decode(encodeJson({
|
|
14
|
-
version: 1,
|
|
15
|
-
generatedAt: 1746104606044,
|
|
16
|
-
tracks: [
|
|
17
|
-
{
|
|
18
|
-
name: "1080p-video",
|
|
19
|
-
namespace: "conference.example.com/conference123/alice",
|
|
20
|
-
packaging: "loc",
|
|
21
|
-
isLive: true,
|
|
22
|
-
targetLatency: 2000,
|
|
23
|
-
role: "video",
|
|
24
|
-
codec: "av01.0.08M.10.0.110.09",
|
|
25
|
-
width: 1920,
|
|
26
|
-
height: 1080,
|
|
27
|
-
framerate: 30,
|
|
28
|
-
bitrate: 1500000,
|
|
29
|
-
},
|
|
30
|
-
],
|
|
31
|
-
}));
|
|
32
|
-
expect(catalog.tracks).toHaveLength(1);
|
|
33
|
-
expect(catalog.tracks[0].codec).toBe("av01.0.08M.10.0.110.09");
|
|
34
|
-
});
|
|
35
|
-
test("decodes a draft-00 catalog whose timeline tracks omit isLive", () => {
|
|
36
|
-
// Example 8 from draft-ietf-moq-msf-00: mediatimeline tracks omit isLive/role/codec.
|
|
37
|
-
const catalog = decode(encodeJson({
|
|
38
|
-
version: 1,
|
|
39
|
-
tracks: [
|
|
40
|
-
{
|
|
41
|
-
name: "history",
|
|
42
|
-
packaging: "mediatimeline",
|
|
43
|
-
mimetype: "application/json",
|
|
44
|
-
depends: ["1080p-video"],
|
|
45
|
-
},
|
|
46
|
-
{
|
|
47
|
-
name: "1080p-video",
|
|
48
|
-
packaging: "loc",
|
|
49
|
-
isLive: true,
|
|
50
|
-
role: "video",
|
|
51
|
-
codec: "av01.0.08M.10.0.110.09",
|
|
52
|
-
},
|
|
53
|
-
],
|
|
54
|
-
}));
|
|
55
|
-
expect(catalog.tracks).toHaveLength(2);
|
|
56
|
-
expect(catalog.tracks[0].isLive).toBe(false);
|
|
57
|
-
expect(catalog.tracks[0].packaging).toBe("mediatimeline");
|
|
58
|
-
});
|
|
59
|
-
test("decodes a draft-01 catalog with a string version", () => {
|
|
60
|
-
const catalog = decode(encodeJson({
|
|
61
|
-
version: "draft-01",
|
|
62
|
-
tracks: [{ name: "audio", packaging: "loc", isLive: true, role: "audio", codec: "opus" }],
|
|
63
|
-
}));
|
|
64
|
-
expect(catalog.tracks[0].role).toBe("audio");
|
|
65
|
-
});
|
|
66
|
-
test("resolves draft-01 initRef into inline initData", () => {
|
|
67
|
-
const catalog = decode(encodeJson({
|
|
68
|
-
version: "draft-01",
|
|
69
|
-
initDataList: [{ id: "v0", type: "inline", data: "AQID" }],
|
|
70
|
-
tracks: [
|
|
71
|
-
{ name: "video0", packaging: "cmaf", isLive: true, role: "video", codec: "avc1.640028", initRef: "v0" },
|
|
72
|
-
],
|
|
73
|
-
}));
|
|
74
|
-
expect(catalog.tracks[0].initData).toBe("AQID");
|
|
75
|
-
});
|
|
76
|
-
test("leaves initData undefined for a dangling or non-inline initRef", () => {
|
|
77
|
-
const catalog = decode(encodeJson({
|
|
78
|
-
version: "draft-01",
|
|
79
|
-
initDataList: [{ id: "v0", type: "url", data: "https://example.com/init" }],
|
|
80
|
-
tracks: [
|
|
81
|
-
{ name: "a", packaging: "cmaf", isLive: true, role: "video", codec: "avc1.640028", initRef: "missing" },
|
|
82
|
-
{ name: "b", packaging: "cmaf", isLive: true, role: "video", codec: "avc1.640028", initRef: "v0" },
|
|
83
|
-
],
|
|
84
|
-
}));
|
|
85
|
-
expect(catalog.tracks[0].initData).toBeUndefined();
|
|
86
|
-
expect(catalog.tracks[1].initData).toBeUndefined();
|
|
87
|
-
});
|
|
88
|
-
test("rejects an unsupported numeric version", () => {
|
|
89
|
-
// Mirrors the Rust side: any number other than 1 is rejected.
|
|
90
|
-
expect(() => decode(encodeJson({ version: 2, tracks: [] }))).toThrow();
|
|
91
|
-
});
|
|
92
|
-
test("encode hoists and dedups init data, then round-trips", () => {
|
|
93
|
-
const catalog = {
|
|
94
|
-
tracks: [
|
|
95
|
-
{ name: "a", packaging: "cmaf", isLive: true, role: "video", codec: "avc1.640028", initData: "AQID" },
|
|
96
|
-
{ name: "b", packaging: "cmaf", isLive: true, role: "video", codec: "avc1.640028", initData: "AQID" },
|
|
97
|
-
],
|
|
98
|
-
};
|
|
99
|
-
const wire = decodeJson(encode(catalog));
|
|
100
|
-
const list = wire.initDataList;
|
|
101
|
-
expect(list).toHaveLength(1);
|
|
102
|
-
expect(list[0].data).toBe("AQID");
|
|
103
|
-
expect(wire.version).toBe("draft-01");
|
|
104
|
-
const wireTracks = wire.tracks;
|
|
105
|
-
for (const t of wireTracks) {
|
|
106
|
-
expect(t.initRef).toBe(list[0].id);
|
|
107
|
-
expect(t.initData).toBeUndefined();
|
|
108
|
-
}
|
|
109
|
-
const parsed = decode(encode(catalog));
|
|
110
|
-
expect(parsed.tracks[0].initData).toBe("AQID");
|
|
111
|
-
expect(parsed.tracks[1].initData).toBe("AQID");
|
|
112
|
-
});
|
|
113
|
-
test("encode emits isLive when omitted", () => {
|
|
114
|
-
const wire = decodeJson(encode({
|
|
115
|
-
tracks: [{ name: "history", packaging: "mediatimeline" }],
|
|
116
|
-
}));
|
|
117
|
-
const wireTracks = wire.tracks;
|
|
118
|
-
expect(wireTracks[0].isLive).toBe(false);
|
|
119
|
-
const parsed = decode(encodeJson(wire));
|
|
120
|
-
expect(parsed.tracks[0].isLive).toBe(false);
|
|
121
|
-
});
|
|
122
|
-
test("preserves SAP fields through decode and encode", () => {
|
|
123
|
-
const catalog = decode(encodeJson({
|
|
124
|
-
version: "draft-01",
|
|
125
|
-
tracks: [
|
|
126
|
-
{
|
|
127
|
-
name: "video0",
|
|
128
|
-
packaging: "cmaf",
|
|
129
|
-
isLive: true,
|
|
130
|
-
role: "video",
|
|
131
|
-
codec: "avc1.640028",
|
|
132
|
-
maxGrpSapStartingType: 1,
|
|
133
|
-
maxObjSapStartingType: 2,
|
|
134
|
-
jitter: 15.0,
|
|
135
|
-
},
|
|
136
|
-
],
|
|
137
|
-
}));
|
|
138
|
-
expect(catalog.tracks[0].maxGrpSapStartingType).toBe(1);
|
|
139
|
-
expect(catalog.tracks[0].maxObjSapStartingType).toBe(2);
|
|
140
|
-
expect(catalog.tracks[0].jitter).toBe(15);
|
|
141
|
-
const wire = decodeJson(encode(catalog));
|
|
142
|
-
const wireTracks = wire.tracks;
|
|
143
|
-
expect(wireTracks[0].maxGrpSapStartingType).toBe(1);
|
|
144
|
-
expect(wireTracks[0].maxObjSapStartingType).toBe(2);
|
|
145
|
-
expect(wireTracks[0].jitter).toBe(15);
|
|
146
|
-
});
|
|
147
|
-
//# sourceMappingURL=catalog.test.js.map
|
package/catalog.test.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"catalog.test.js","sourceRoot":"","sources":["../src/catalog.test.ts"],"names":[],"mappings":"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","sourcesContent":["import { expect, test } from \"bun:test\";\nimport { decode, encode } from \"./catalog.ts\";\n\nfunction encodeJson(value: unknown): Uint8Array {\n\treturn new TextEncoder().encode(JSON.stringify(value));\n}\n\nfunction decodeJson(raw: Uint8Array): Record<string, unknown> {\n\treturn JSON.parse(new TextDecoder().decode(raw));\n}\n\ntest(\"decodes a draft-00 catalog with a numeric version\", () => {\n\t// Example 1 from draft-ietf-moq-msf-00, trimmed. Numeric version plus unmodeled\n\t// fields (namespace, targetLatency, generatedAt) which must be ignored.\n\tconst catalog = decode(\n\t\tencodeJson({\n\t\t\tversion: 1,\n\t\t\tgeneratedAt: 1746104606044,\n\t\t\ttracks: [\n\t\t\t\t{\n\t\t\t\t\tname: \"1080p-video\",\n\t\t\t\t\tnamespace: \"conference.example.com/conference123/alice\",\n\t\t\t\t\tpackaging: \"loc\",\n\t\t\t\t\tisLive: true,\n\t\t\t\t\ttargetLatency: 2000,\n\t\t\t\t\trole: \"video\",\n\t\t\t\t\tcodec: \"av01.0.08M.10.0.110.09\",\n\t\t\t\t\twidth: 1920,\n\t\t\t\t\theight: 1080,\n\t\t\t\t\tframerate: 30,\n\t\t\t\t\tbitrate: 1500000,\n\t\t\t\t},\n\t\t\t],\n\t\t}),\n\t);\n\n\texpect(catalog.tracks).toHaveLength(1);\n\texpect(catalog.tracks[0].codec).toBe(\"av01.0.08M.10.0.110.09\");\n});\n\ntest(\"decodes a draft-00 catalog whose timeline tracks omit isLive\", () => {\n\t// Example 8 from draft-ietf-moq-msf-00: mediatimeline tracks omit isLive/role/codec.\n\tconst catalog = decode(\n\t\tencodeJson({\n\t\t\tversion: 1,\n\t\t\ttracks: [\n\t\t\t\t{\n\t\t\t\t\tname: \"history\",\n\t\t\t\t\tpackaging: \"mediatimeline\",\n\t\t\t\t\tmimetype: \"application/json\",\n\t\t\t\t\tdepends: [\"1080p-video\"],\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tname: \"1080p-video\",\n\t\t\t\t\tpackaging: \"loc\",\n\t\t\t\t\tisLive: true,\n\t\t\t\t\trole: \"video\",\n\t\t\t\t\tcodec: \"av01.0.08M.10.0.110.09\",\n\t\t\t\t},\n\t\t\t],\n\t\t}),\n\t);\n\n\texpect(catalog.tracks).toHaveLength(2);\n\texpect(catalog.tracks[0].isLive).toBe(false);\n\texpect(catalog.tracks[0].packaging).toBe(\"mediatimeline\");\n});\n\ntest(\"decodes a draft-01 catalog with a string version\", () => {\n\tconst catalog = decode(\n\t\tencodeJson({\n\t\t\tversion: \"draft-01\",\n\t\t\ttracks: [{ name: \"audio\", packaging: \"loc\", isLive: true, role: \"audio\", codec: \"opus\" }],\n\t\t}),\n\t);\n\n\texpect(catalog.tracks[0].role).toBe(\"audio\");\n});\n\ntest(\"resolves draft-01 initRef into inline initData\", () => {\n\tconst catalog = decode(\n\t\tencodeJson({\n\t\t\tversion: \"draft-01\",\n\t\t\tinitDataList: [{ id: \"v0\", type: \"inline\", data: \"AQID\" }],\n\t\t\ttracks: [\n\t\t\t\t{ name: \"video0\", packaging: \"cmaf\", isLive: true, role: \"video\", codec: \"avc1.640028\", initRef: \"v0\" },\n\t\t\t],\n\t\t}),\n\t);\n\n\texpect(catalog.tracks[0].initData).toBe(\"AQID\");\n});\n\ntest(\"leaves initData undefined for a dangling or non-inline initRef\", () => {\n\tconst catalog = decode(\n\t\tencodeJson({\n\t\t\tversion: \"draft-01\",\n\t\t\tinitDataList: [{ id: \"v0\", type: \"url\", data: \"https://example.com/init\" }],\n\t\t\ttracks: [\n\t\t\t\t{ name: \"a\", packaging: \"cmaf\", isLive: true, role: \"video\", codec: \"avc1.640028\", initRef: \"missing\" },\n\t\t\t\t{ name: \"b\", packaging: \"cmaf\", isLive: true, role: \"video\", codec: \"avc1.640028\", initRef: \"v0\" },\n\t\t\t],\n\t\t}),\n\t);\n\n\texpect(catalog.tracks[0].initData).toBeUndefined();\n\texpect(catalog.tracks[1].initData).toBeUndefined();\n});\n\ntest(\"rejects an unsupported numeric version\", () => {\n\t// Mirrors the Rust side: any number other than 1 is rejected.\n\texpect(() => decode(encodeJson({ version: 2, tracks: [] }))).toThrow();\n});\n\ntest(\"encode hoists and dedups init data, then round-trips\", () => {\n\tconst catalog = {\n\t\ttracks: [\n\t\t\t{ name: \"a\", packaging: \"cmaf\", isLive: true, role: \"video\", codec: \"avc1.640028\", initData: \"AQID\" },\n\t\t\t{ name: \"b\", packaging: \"cmaf\", isLive: true, role: \"video\", codec: \"avc1.640028\", initData: \"AQID\" },\n\t\t],\n\t};\n\n\tconst wire = decodeJson(encode(catalog));\n\tconst list = wire.initDataList as { id: string; type: string; data: string }[];\n\texpect(list).toHaveLength(1);\n\texpect(list[0].data).toBe(\"AQID\");\n\texpect(wire.version).toBe(\"draft-01\");\n\n\tconst wireTracks = wire.tracks as { initRef?: string; initData?: string }[];\n\tfor (const t of wireTracks) {\n\t\texpect(t.initRef).toBe(list[0].id);\n\t\texpect(t.initData).toBeUndefined();\n\t}\n\n\tconst parsed = decode(encode(catalog));\n\texpect(parsed.tracks[0].initData).toBe(\"AQID\");\n\texpect(parsed.tracks[1].initData).toBe(\"AQID\");\n});\n\ntest(\"encode emits isLive when omitted\", () => {\n\tconst wire = decodeJson(\n\t\tencode({\n\t\t\ttracks: [{ name: \"history\", packaging: \"mediatimeline\" }],\n\t\t}),\n\t);\n\n\tconst wireTracks = wire.tracks as { isLive?: boolean }[];\n\texpect(wireTracks[0].isLive).toBe(false);\n\n\tconst parsed = decode(encodeJson(wire));\n\texpect(parsed.tracks[0].isLive).toBe(false);\n});\n\ntest(\"preserves SAP fields through decode and encode\", () => {\n\tconst catalog = decode(\n\t\tencodeJson({\n\t\t\tversion: \"draft-01\",\n\t\t\ttracks: [\n\t\t\t\t{\n\t\t\t\t\tname: \"video0\",\n\t\t\t\t\tpackaging: \"cmaf\",\n\t\t\t\t\tisLive: true,\n\t\t\t\t\trole: \"video\",\n\t\t\t\t\tcodec: \"avc1.640028\",\n\t\t\t\t\tmaxGrpSapStartingType: 1,\n\t\t\t\t\tmaxObjSapStartingType: 2,\n\t\t\t\t\tjitter: 15.0,\n\t\t\t\t},\n\t\t\t],\n\t\t}),\n\t);\n\n\texpect(catalog.tracks[0].maxGrpSapStartingType).toBe(1);\n\texpect(catalog.tracks[0].maxObjSapStartingType).toBe(2);\n\texpect(catalog.tracks[0].jitter).toBe(15);\n\n\tconst wire = decodeJson(encode(catalog));\n\tconst wireTracks = wire.tracks as {\n\t\tmaxGrpSapStartingType?: number;\n\t\tmaxObjSapStartingType?: number;\n\t\tjitter?: number;\n\t}[];\n\texpect(wireTracks[0].maxGrpSapStartingType).toBe(1);\n\texpect(wireTracks[0].maxObjSapStartingType).toBe(2);\n\texpect(wireTracks[0].jitter).toBe(15);\n});\n"]}
|