@norskvideo/moq-hang 0.1.0

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.
Files changed (130) hide show
  1. package/README.md +65 -0
  2. package/catalog/audio.d.ts +139 -0
  3. package/catalog/audio.d.ts.map +1 -0
  4. package/catalog/audio.js +55 -0
  5. package/catalog/audio.js.map +1 -0
  6. package/catalog/consumer.d.ts +34 -0
  7. package/catalog/consumer.d.ts.map +1 -0
  8. package/catalog/consumer.js +30 -0
  9. package/catalog/consumer.js.map +1 -0
  10. package/catalog/container.d.ts +24 -0
  11. package/catalog/container.d.ts.map +1 -0
  12. package/catalog/container.js +30 -0
  13. package/catalog/container.js.map +1 -0
  14. package/catalog/format.d.ts +13 -0
  15. package/catalog/format.d.ts.map +1 -0
  16. package/catalog/format.js +24 -0
  17. package/catalog/format.js.map +1 -0
  18. package/catalog/index.d.ts +18 -0
  19. package/catalog/index.d.ts.map +1 -0
  20. package/catalog/index.js +19 -0
  21. package/catalog/index.js.map +1 -0
  22. package/catalog/integers.d.ts +23 -0
  23. package/catalog/integers.d.ts.map +1 -0
  24. package/catalog/integers.js +24 -0
  25. package/catalog/integers.js.map +1 -0
  26. package/catalog/priority.d.ts +7 -0
  27. package/catalog/priority.d.ts.map +1 -0
  28. package/catalog/priority.js +9 -0
  29. package/catalog/priority.js.map +1 -0
  30. package/catalog/producer.d.ts +31 -0
  31. package/catalog/producer.d.ts.map +1 -0
  32. package/catalog/producer.js +25 -0
  33. package/catalog/producer.js.map +1 -0
  34. package/catalog/root.d.ts +252 -0
  35. package/catalog/root.d.ts.map +1 -0
  36. package/catalog/root.js +17 -0
  37. package/catalog/root.js.map +1 -0
  38. package/catalog/root.test.d.ts +2 -0
  39. package/catalog/root.test.d.ts.map +1 -0
  40. package/catalog/root.test.js +20 -0
  41. package/catalog/root.test.js.map +1 -0
  42. package/catalog/timeline.d.ts +26 -0
  43. package/catalog/timeline.d.ts.map +1 -0
  44. package/catalog/timeline.js +33 -0
  45. package/catalog/timeline.js.map +1 -0
  46. package/catalog/track.d.ts +8 -0
  47. package/catalog/track.d.ts.map +1 -0
  48. package/catalog/track.js +7 -0
  49. package/catalog/track.js.map +1 -0
  50. package/catalog/video.d.ts +171 -0
  51. package/catalog/video.d.ts.map +1 -0
  52. package/catalog/video.js +87 -0
  53. package/catalog/video.js.map +1 -0
  54. package/container/cmaf/decode.d.ts +68 -0
  55. package/container/cmaf/decode.d.ts.map +1 -0
  56. package/container/cmaf/decode.js +303 -0
  57. package/container/cmaf/decode.js.map +1 -0
  58. package/container/cmaf/encode.d.ts +51 -0
  59. package/container/cmaf/encode.d.ts.map +1 -0
  60. package/container/cmaf/encode.js +834 -0
  61. package/container/cmaf/encode.js.map +1 -0
  62. package/container/cmaf/format.d.ts +12 -0
  63. package/container/cmaf/format.d.ts.map +1 -0
  64. package/container/cmaf/format.js +19 -0
  65. package/container/cmaf/format.js.map +1 -0
  66. package/container/cmaf/index.d.ts +9 -0
  67. package/container/cmaf/index.d.ts.map +1 -0
  68. package/container/cmaf/index.js +10 -0
  69. package/container/cmaf/index.js.map +1 -0
  70. package/container/consumer.d.ts +41 -0
  71. package/container/consumer.d.ts.map +1 -0
  72. package/container/consumer.js +424 -0
  73. package/container/consumer.js.map +1 -0
  74. package/container/consumer.test.d.ts +2 -0
  75. package/container/consumer.test.d.ts.map +1 -0
  76. package/container/consumer.test.js +396 -0
  77. package/container/consumer.test.js.map +1 -0
  78. package/container/format.d.ts +12 -0
  79. package/container/format.d.ts.map +1 -0
  80. package/container/format.js +3 -0
  81. package/container/format.js.map +1 -0
  82. package/container/index.d.ts +14 -0
  83. package/container/index.d.ts.map +1 -0
  84. package/container/index.js +18 -0
  85. package/container/index.js.map +1 -0
  86. package/container/legacy.d.ts +39 -0
  87. package/container/legacy.d.ts.map +1 -0
  88. package/container/legacy.js +53 -0
  89. package/container/legacy.js.map +1 -0
  90. package/container/timeline.d.ts +67 -0
  91. package/container/timeline.d.ts.map +1 -0
  92. package/container/timeline.js +87 -0
  93. package/container/timeline.js.map +1 -0
  94. package/container/types.d.ts +22 -0
  95. package/container/types.d.ts.map +1 -0
  96. package/container/types.js +22 -0
  97. package/container/types.js.map +1 -0
  98. package/index.d.ts +13 -0
  99. package/index.d.ts.map +1 -0
  100. package/index.js +18 -0
  101. package/index.js.map +1 -0
  102. package/package.json +37 -0
  103. package/util/aac.d.ts +8 -0
  104. package/util/aac.d.ts.map +1 -0
  105. package/util/aac.js +57 -0
  106. package/util/aac.js.map +1 -0
  107. package/util/aac.test.d.ts +2 -0
  108. package/util/aac.test.d.ts.map +1 -0
  109. package/util/aac.test.js +34 -0
  110. package/util/aac.test.js.map +1 -0
  111. package/util/hacks.d.ts +5 -0
  112. package/util/hacks.d.ts.map +1 -0
  113. package/util/hacks.js +6 -0
  114. package/util/hacks.js.map +1 -0
  115. package/util/hex.d.ts +5 -0
  116. package/util/hex.d.ts.map +1 -0
  117. package/util/hex.js +18 -0
  118. package/util/hex.js.map +1 -0
  119. package/util/index.d.ts +12 -0
  120. package/util/index.d.ts.map +1 -0
  121. package/util/index.js +18 -0
  122. package/util/index.js.map +1 -0
  123. package/util/latency.d.ts +38 -0
  124. package/util/latency.d.ts.map +1 -0
  125. package/util/latency.js +49 -0
  126. package/util/latency.js.map +1 -0
  127. package/util/libav.d.ts +3 -0
  128. package/util/libav.d.ts.map +1 -0
  129. package/util/libav.js +26 -0
  130. package/util/libav.js.map +1 -0
@@ -0,0 +1,396 @@
1
+ /* @ts-self-types="./consumer.test.d.ts" */
2
+ import { expect, test } from "bun:test";
3
+ import { Group, Track, Varint } from "@norskvideo/moq-net";
4
+ import { encodeDataSegment } from "./cmaf/encode.js";
5
+ import { Format as CmafFormat } from "./cmaf/format.js";
6
+ import { Consumer } from "./consumer.js";
7
+ import { Format as LegacyFormat } from "./legacy.js";
8
+ const TIMESCALE = 90_000;
9
+ const TEST_INIT = {
10
+ timescale: TIMESCALE,
11
+ trackId: 1,
12
+ defaultSampleDuration: 0,
13
+ defaultSampleSize: 0,
14
+ defaultSampleFlags: 0,
15
+ };
16
+ function encodeLegacyFrame(timestamp, payload) {
17
+ const tsBytes = Varint.encode(timestamp);
18
+ const data = new Uint8Array(tsBytes.byteLength + payload.byteLength);
19
+ data.set(tsBytes, 0);
20
+ data.set(payload, tsBytes.byteLength);
21
+ return data;
22
+ }
23
+ // --- LegacyFormat ---
24
+ test("LegacyFormat decodes a valid frame", () => {
25
+ const format = new LegacyFormat();
26
+ const payload = new Uint8Array([0xde, 0xad]);
27
+ const timestamp = 1000;
28
+ const frame = encodeLegacyFrame(timestamp, payload);
29
+ const result = format.decode(frame);
30
+ expect(result).toHaveLength(1);
31
+ expect(result[0].timestamp).toBe(timestamp);
32
+ expect(result[0].data).toEqual(payload);
33
+ expect(result[0].keyframe).toBe(false);
34
+ });
35
+ test("LegacyFormat always returns keyframe: false", () => {
36
+ const format = new LegacyFormat();
37
+ const frame = encodeLegacyFrame(0, new Uint8Array([0x01]));
38
+ const [decoded] = format.decode(frame);
39
+ expect(decoded.keyframe).toBe(false);
40
+ });
41
+ test("LegacyFormat always returns exactly one frame", () => {
42
+ const format = new LegacyFormat();
43
+ const frame = encodeLegacyFrame(5000, new Uint8Array([0x01, 0x02, 0x03]));
44
+ const result = format.decode(frame);
45
+ expect(result).toHaveLength(1);
46
+ });
47
+ test("LegacyFormat throws on empty input", () => {
48
+ const format = new LegacyFormat();
49
+ expect(() => format.decode(new Uint8Array(0))).toThrow();
50
+ });
51
+ test("LegacyFormat throws on truncated input", () => {
52
+ const format = new LegacyFormat();
53
+ // A varint that indicates more bytes follow but is truncated
54
+ expect(() => format.decode(new Uint8Array([0x80]))).toThrow();
55
+ });
56
+ // --- CmafFormat ---
57
+ test("CmafFormat decodes a valid keyframe segment", () => {
58
+ const format = new CmafFormat(TEST_INIT);
59
+ const segment = encodeDataSegment({
60
+ data: new Uint8Array([0xca, 0xfe]),
61
+ timestamp: 0,
62
+ duration: 3000,
63
+ keyframe: true,
64
+ sequence: 0,
65
+ });
66
+ const result = format.decode(segment);
67
+ expect(result).toHaveLength(1);
68
+ expect(result[0].data).toEqual(new Uint8Array([0xca, 0xfe]));
69
+ expect(result[0].timestamp).toBe(0);
70
+ expect(result[0].keyframe).toBe(true);
71
+ });
72
+ test("CmafFormat decodes a delta frame segment", () => {
73
+ const format = new CmafFormat(TEST_INIT);
74
+ const segment = encodeDataSegment({
75
+ data: new Uint8Array([0xbe, 0xef]),
76
+ timestamp: 3000,
77
+ duration: 3000,
78
+ keyframe: false,
79
+ sequence: 1,
80
+ });
81
+ const result = format.decode(segment);
82
+ expect(result).toHaveLength(1);
83
+ expect(result[0].keyframe).toBe(false);
84
+ });
85
+ test("CmafFormat converts timescale units to microseconds", () => {
86
+ const format = new CmafFormat(TEST_INIT);
87
+ // 90000 timescale units = 1 second = 1_000_000 microseconds
88
+ const segment = encodeDataSegment({
89
+ data: new Uint8Array([0x01]),
90
+ timestamp: TIMESCALE,
91
+ duration: 3000,
92
+ keyframe: true,
93
+ sequence: 0,
94
+ });
95
+ const result = format.decode(segment);
96
+ expect(result[0].timestamp).toBe(1_000_000);
97
+ });
98
+ test("CmafFormat throws on corrupt segment", () => {
99
+ const format = new CmafFormat(TEST_INIT);
100
+ expect(() => format.decode(new Uint8Array([0x00, 0x01, 0x02]))).toThrow();
101
+ });
102
+ // --- Consumer ---
103
+ function encodeLegacy(timestamp) {
104
+ const tsBytes = Varint.encode(timestamp);
105
+ const payload = new Uint8Array([0xde, 0xad]);
106
+ const data = new Uint8Array(tsBytes.byteLength + payload.byteLength);
107
+ data.set(tsBytes, 0);
108
+ data.set(payload, tsBytes.byteLength);
109
+ return data;
110
+ }
111
+ function writeGroupWithLegacyFrames(track, sequence, timestamps) {
112
+ const group = new Group(sequence);
113
+ for (const ts of timestamps) {
114
+ group.writeFrame(encodeLegacy(ts));
115
+ }
116
+ group.close();
117
+ track.writeGroup(group);
118
+ }
119
+ async function drainFrames(consumer, timeout) {
120
+ const frames = [];
121
+ for (;;) {
122
+ const result = await Promise.race([
123
+ consumer.next(),
124
+ new Promise((resolve) => setTimeout(() => resolve(null), timeout)),
125
+ ]);
126
+ if (result === null || result === undefined)
127
+ break;
128
+ if (result.frame) {
129
+ frames.push({ timestamp: result.frame.timestamp, group: result.group, keyframe: result.frame.keyframe });
130
+ }
131
+ }
132
+ return frames;
133
+ }
134
+ test("Consumer delivers frames from a single group", async () => {
135
+ const track = new Track("test");
136
+ const consumer = new Consumer(track, { format: new LegacyFormat(), latency: 500 });
137
+ writeGroupWithLegacyFrames(track, 0, [0, 33_000]);
138
+ track.close();
139
+ const frames = await drainFrames(consumer, 200);
140
+ expect(frames).toHaveLength(2);
141
+ expect(frames[0].timestamp).toBe(0);
142
+ expect(frames[1].timestamp).toBe(33_000);
143
+ consumer.close();
144
+ });
145
+ test("Consumer forces keyframe true at index 0", async () => {
146
+ const track = new Track("test");
147
+ const consumer = new Consumer(track, { format: new LegacyFormat(), latency: 500 });
148
+ writeGroupWithLegacyFrames(track, 0, [0, 33_000]);
149
+ track.close();
150
+ const frames = await drainFrames(consumer, 200);
151
+ expect(frames[0].keyframe).toBe(true);
152
+ expect(frames[1].keyframe).toBe(false);
153
+ consumer.close();
154
+ });
155
+ test("Consumer index spans MoQ frames for keyframe detection", async () => {
156
+ // Custom format that returns 3 samples per MoQ frame, all keyframe: false
157
+ const multiFormat = {
158
+ decode(_frame) {
159
+ return [
160
+ { data: new Uint8Array([1]), timestamp: 0, keyframe: false },
161
+ { data: new Uint8Array([2]), timestamp: 33_000, keyframe: false },
162
+ { data: new Uint8Array([3]), timestamp: 66_000, keyframe: false },
163
+ ];
164
+ },
165
+ };
166
+ const track = new Track("test");
167
+ const consumer = new Consumer(track, { format: multiFormat, latency: 500 });
168
+ const group = new Group(0);
169
+ group.writeFrame(new Uint8Array([0x01])); // first MoQ frame → 3 samples
170
+ group.writeFrame(new Uint8Array([0x02])); // second MoQ frame → 3 samples
171
+ group.close();
172
+ track.writeGroup(group);
173
+ track.close();
174
+ const frames = await drainFrames(consumer, 200);
175
+ expect(frames).toHaveLength(6);
176
+ // Only index 0 is keyframe, rest are false
177
+ expect(frames.map((f) => f.keyframe)).toEqual([true, false, false, false, false, false]);
178
+ consumer.close();
179
+ });
180
+ test("Consumer keeps frames decoded before an error (truncated GoP)", async () => {
181
+ // 0xFF in the first byte signals the format to throw, simulating a stream
182
+ // RESET or corrupt frame mid-group. Encoding the trigger in the frame bytes
183
+ // keeps this deterministic when groups decode in parallel.
184
+ const truncatingFormat = {
185
+ decode(frame) {
186
+ if (frame[0] === 0xff)
187
+ throw new Error("truncated");
188
+ return [{ data: frame, timestamp: frame[0], keyframe: false }];
189
+ },
190
+ };
191
+ const track = new Track("test");
192
+ const consumer = new Consumer(track, { format: truncatingFormat, latency: 500 });
193
+ // Group 0: 2 valid frames then a tail-truncating error.
194
+ const g0 = new Group(0);
195
+ g0.writeFrame(new Uint8Array([0x01]));
196
+ g0.writeFrame(new Uint8Array([0x02]));
197
+ g0.writeFrame(new Uint8Array([0xff]));
198
+ g0.close();
199
+ track.writeGroup(g0);
200
+ // Group 1 decodes cleanly.
201
+ const g1 = new Group(1);
202
+ g1.writeFrame(new Uint8Array([0x04]));
203
+ g1.close();
204
+ track.writeGroup(g1);
205
+ track.close();
206
+ const frames = await drainFrames(consumer, 200);
207
+ // First 2 frames of group 0 survive; group 1 follows.
208
+ expect(frames.map((f) => f.group)).toEqual([0, 0, 1]);
209
+ expect(frames.map((f) => f.timestamp)).toEqual([1, 2, 4]);
210
+ consumer.close();
211
+ });
212
+ test("Consumer close returns undefined from next()", async () => {
213
+ const track = new Track("test");
214
+ const consumer = new Consumer(track, { format: new LegacyFormat(), latency: 500 });
215
+ const promise = consumer.next();
216
+ consumer.close();
217
+ const result = await promise;
218
+ expect(result).toBeUndefined();
219
+ });
220
+ test("Consumer throws on concurrent next() calls", async () => {
221
+ const track = new Track("test");
222
+ const consumer = new Consumer(track, { format: new LegacyFormat(), latency: 500 });
223
+ // First call blocks waiting for data
224
+ void consumer.next();
225
+ // Second call should throw
226
+ expect(() => consumer.next()).toThrow("multiple calls to next not supported");
227
+ consumer.close();
228
+ });
229
+ test("Consumer skips groups via PTS-span when over latency", async () => {
230
+ const track = new Track("test");
231
+ // Zero latency = skip everything that's not the latest
232
+ const consumer = new Consumer(track, { format: new LegacyFormat(), latency: 0 });
233
+ // Write groups with increasing timestamps. With 0 latency, any PTS span > 0 triggers skip.
234
+ writeGroupWithLegacyFrames(track, 0, [0]);
235
+ writeGroupWithLegacyFrames(track, 1, [100_000]);
236
+ writeGroupWithLegacyFrames(track, 2, [200_000]);
237
+ track.close();
238
+ const frames = await drainFrames(consumer, 300);
239
+ // With zero latency, the consumer should skip to the latest group
240
+ const groups = [...new Set(frames.map((f) => f.group))];
241
+ expect(groups.at(-1)).toBe(2);
242
+ consumer.close();
243
+ });
244
+ // --- Ordering ---
245
+ test("Consumer delivers groups in sequence order regardless of arrival order", async () => {
246
+ const track = new Track("test");
247
+ const consumer = new Consumer(track, { format: new LegacyFormat(), latency: 500 });
248
+ writeGroupWithLegacyFrames(track, 2, [60_000]);
249
+ writeGroupWithLegacyFrames(track, 0, [0]);
250
+ writeGroupWithLegacyFrames(track, 1, [30_000]);
251
+ track.close();
252
+ await new Promise((resolve) => setTimeout(resolve, 100));
253
+ const frames = await drainFrames(consumer, 500);
254
+ expect(frames).toHaveLength(3);
255
+ expect(frames[0].group).toBe(0);
256
+ expect(frames[1].group).toBe(1);
257
+ expect(frames[2].group).toBe(2);
258
+ consumer.close();
259
+ });
260
+ test("Consumer rejects stale groups", async () => {
261
+ const track = new Track("test");
262
+ const consumer = new Consumer(track, { format: new LegacyFormat(), latency: 500 });
263
+ // Group 5 arrives first (sets active = 5)
264
+ writeGroupWithLegacyFrames(track, 5, [0]);
265
+ await new Promise((resolve) => setTimeout(resolve, 50));
266
+ // Group 3 is stale
267
+ writeGroupWithLegacyFrames(track, 3, [100_000]);
268
+ // Group 6 is valid
269
+ writeGroupWithLegacyFrames(track, 6, [30_000]);
270
+ track.close();
271
+ await new Promise((resolve) => setTimeout(resolve, 100));
272
+ const frames = await drainFrames(consumer, 500);
273
+ expect(frames).toHaveLength(2);
274
+ expect(frames[0].group).toBe(5);
275
+ expect(frames[1].group).toBe(6);
276
+ consumer.close();
277
+ });
278
+ // --- Group boundary signals ---
279
+ test("Consumer next() returns group-done signals", async () => {
280
+ const track = new Track("test");
281
+ const consumer = new Consumer(track, { format: new LegacyFormat(), latency: 500 });
282
+ writeGroupWithLegacyFrames(track, 0, [0, 33_000]);
283
+ writeGroupWithLegacyFrames(track, 1, [66_000]);
284
+ track.close();
285
+ await new Promise((resolve) => setTimeout(resolve, 50));
286
+ const allResults = [];
287
+ for (;;) {
288
+ const result = await Promise.race([
289
+ consumer.next(),
290
+ new Promise((resolve) => setTimeout(() => resolve(null), 500)),
291
+ ]);
292
+ if (result === null || result === undefined)
293
+ break;
294
+ allResults.push({ frame: result.frame !== undefined, group: result.group });
295
+ }
296
+ const frameResults = allResults.filter((r) => r.frame);
297
+ const boundaries = allResults.filter((r) => !r.frame);
298
+ expect(frameResults).toHaveLength(3);
299
+ expect(boundaries).toHaveLength(2);
300
+ expect(boundaries[0].group).toBe(0);
301
+ expect(boundaries[1].group).toBe(1);
302
+ consumer.close();
303
+ });
304
+ // --- Buffered signal ---
305
+ test("Consumer buffered signal updates as frames arrive", async () => {
306
+ const track = new Track("test");
307
+ const consumer = new Consumer(track, { format: new LegacyFormat(), latency: 500 });
308
+ expect(consumer.buffered.peek()).toEqual([]);
309
+ writeGroupWithLegacyFrames(track, 0, [0, 33_000]);
310
+ writeGroupWithLegacyFrames(track, 1, [66_000, 99_000]);
311
+ await new Promise((resolve) => setTimeout(resolve, 100));
312
+ const ranges = consumer.buffered.peek();
313
+ expect(ranges.length).toBe(1);
314
+ expect(ranges[0].start).toBe(0);
315
+ expect(ranges[0].end >= 66).toBeTruthy();
316
+ track.close();
317
+ consumer.close();
318
+ });
319
+ // --- Gap recovery ---
320
+ test("Consumer recovers from gap in group sequence numbers", async () => {
321
+ const track = new Track("test");
322
+ const consumer = new Consumer(track, { format: new LegacyFormat(), latency: 100 });
323
+ writeGroupWithLegacyFrames(track, 0, [0, 20_000]);
324
+ writeGroupWithLegacyFrames(track, 1, [40_000, 60_000]);
325
+ // Skip group 2
326
+ writeGroupWithLegacyFrames(track, 3, [120_000, 140_000]);
327
+ writeGroupWithLegacyFrames(track, 4, [160_000, 180_000]);
328
+ writeGroupWithLegacyFrames(track, 5, [200_000, 220_000]);
329
+ track.close();
330
+ await new Promise((resolve) => setTimeout(resolve, 100));
331
+ const frames = await drainFrames(consumer, 500);
332
+ expect(frames.length >= 4).toBeTruthy();
333
+ consumer.close();
334
+ });
335
+ // --- Edge cases from design review ---
336
+ test("Consumer handles empty decode result without deadlock", async () => {
337
+ let callCount = 0;
338
+ const emptyThenValid = {
339
+ decode(_frame) {
340
+ callCount++;
341
+ if (callCount === 1)
342
+ return []; // empty result
343
+ return [{ data: new Uint8Array([1]), timestamp: 33_000, keyframe: false }];
344
+ },
345
+ };
346
+ const track = new Track("test");
347
+ const consumer = new Consumer(track, { format: emptyThenValid, latency: 500 });
348
+ const group = new Group(0);
349
+ group.writeFrame(new Uint8Array([0x01])); // empty decode
350
+ group.writeFrame(new Uint8Array([0x02])); // valid decode
351
+ group.close();
352
+ track.writeGroup(group);
353
+ track.close();
354
+ const frames = await drainFrames(consumer, 300);
355
+ // The empty decode produces no frames, but the second MoQ frame does.
356
+ // Since index 0 was never used (empty result), the first actual frame gets index=1 → keyframe false?
357
+ // Actually index increments per sample, and empty decode means 0 samples → index stays at 0.
358
+ // So the next frame's first sample gets index=0 → keyframe=true.
359
+ expect(frames).toHaveLength(1);
360
+ expect(frames[0].keyframe).toBe(true);
361
+ consumer.close();
362
+ });
363
+ // --- CMAF through Consumer ---
364
+ test("Consumer with CmafFormat delivers correct timestamps", async () => {
365
+ const track = new Track("test");
366
+ const consumer = new Consumer(track, {
367
+ format: new CmafFormat(TEST_INIT),
368
+ latency: 500,
369
+ });
370
+ const group = new Group(0);
371
+ group.writeFrame(encodeDataSegment({
372
+ data: new Uint8Array([0xca, 0xfe]),
373
+ timestamp: 0,
374
+ duration: 3000,
375
+ keyframe: true,
376
+ sequence: 0,
377
+ }));
378
+ group.writeFrame(encodeDataSegment({
379
+ data: new Uint8Array([0xbe, 0xef]),
380
+ timestamp: 3000,
381
+ duration: 3000,
382
+ keyframe: false,
383
+ sequence: 0,
384
+ }));
385
+ group.close();
386
+ track.writeGroup(group);
387
+ track.close();
388
+ const frames = await drainFrames(consumer, 200);
389
+ expect(frames).toHaveLength(2);
390
+ expect(frames[0].keyframe).toBe(true); // index 0 override
391
+ expect(frames[1].keyframe).toBe(false); // trusts format
392
+ expect(frames[0].timestamp).toBe(0);
393
+ expect(frames[1].timestamp).toBe(33_333); // 3000/90000 * 1_000_000
394
+ consumer.close();
395
+ });
396
+ //# sourceMappingURL=consumer.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"consumer.test.js","sourceRoot":"","sources":["../../src/container/consumer.test.ts"],"names":[],"mappings":"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","sourcesContent":["import { expect, test } from \"bun:test\";\nimport { Group, type Time, Track, Varint } from \"@moq/net\";\nimport type { InitSegment } from \"./cmaf/decode.ts\";\nimport { encodeDataSegment } from \"./cmaf/encode.ts\";\nimport { Format as CmafFormat } from \"./cmaf/format.ts\";\nimport { Consumer } from \"./consumer.ts\";\nimport type { Format as ContainerFormat } from \"./format.ts\";\nimport { Format as LegacyFormat } from \"./legacy.ts\";\nimport type { Frame } from \"./types.ts\";\n\nconst TIMESCALE = 90_000;\nconst TEST_INIT: InitSegment = {\n\ttimescale: TIMESCALE,\n\ttrackId: 1,\n\tdefaultSampleDuration: 0,\n\tdefaultSampleSize: 0,\n\tdefaultSampleFlags: 0,\n};\n\nfunction encodeLegacyFrame(timestamp: Time.Micro, payload: Uint8Array): Uint8Array {\n\tconst tsBytes = Varint.encode(timestamp);\n\tconst data = new Uint8Array(tsBytes.byteLength + payload.byteLength);\n\tdata.set(tsBytes, 0);\n\tdata.set(payload, tsBytes.byteLength);\n\treturn data;\n}\n\n// --- LegacyFormat ---\n\ntest(\"LegacyFormat decodes a valid frame\", () => {\n\tconst format = new LegacyFormat();\n\tconst payload = new Uint8Array([0xde, 0xad]);\n\tconst timestamp = 1000 as Time.Micro;\n\tconst frame = encodeLegacyFrame(timestamp, payload);\n\n\tconst result = format.decode(frame);\n\n\texpect(result).toHaveLength(1);\n\texpect(result[0].timestamp).toBe(timestamp);\n\texpect(result[0].data).toEqual(payload);\n\texpect(result[0].keyframe).toBe(false);\n});\n\ntest(\"LegacyFormat always returns keyframe: false\", () => {\n\tconst format = new LegacyFormat();\n\tconst frame = encodeLegacyFrame(0 as Time.Micro, new Uint8Array([0x01]));\n\n\tconst [decoded] = format.decode(frame);\n\texpect(decoded.keyframe).toBe(false);\n});\n\ntest(\"LegacyFormat always returns exactly one frame\", () => {\n\tconst format = new LegacyFormat();\n\tconst frame = encodeLegacyFrame(5000 as Time.Micro, new Uint8Array([0x01, 0x02, 0x03]));\n\n\tconst result = format.decode(frame);\n\texpect(result).toHaveLength(1);\n});\n\ntest(\"LegacyFormat throws on empty input\", () => {\n\tconst format = new LegacyFormat();\n\texpect(() => format.decode(new Uint8Array(0))).toThrow();\n});\n\ntest(\"LegacyFormat throws on truncated input\", () => {\n\tconst format = new LegacyFormat();\n\t// A varint that indicates more bytes follow but is truncated\n\texpect(() => format.decode(new Uint8Array([0x80]))).toThrow();\n});\n\n// --- CmafFormat ---\n\ntest(\"CmafFormat decodes a valid keyframe segment\", () => {\n\tconst format = new CmafFormat(TEST_INIT);\n\tconst segment = encodeDataSegment({\n\t\tdata: new Uint8Array([0xca, 0xfe]),\n\t\ttimestamp: 0,\n\t\tduration: 3000,\n\t\tkeyframe: true,\n\t\tsequence: 0,\n\t});\n\n\tconst result = format.decode(segment);\n\n\texpect(result).toHaveLength(1);\n\texpect(result[0].data).toEqual(new Uint8Array([0xca, 0xfe]));\n\texpect(result[0].timestamp).toBe(0 as Time.Micro);\n\texpect(result[0].keyframe).toBe(true);\n});\n\ntest(\"CmafFormat decodes a delta frame segment\", () => {\n\tconst format = new CmafFormat(TEST_INIT);\n\tconst segment = encodeDataSegment({\n\t\tdata: new Uint8Array([0xbe, 0xef]),\n\t\ttimestamp: 3000,\n\t\tduration: 3000,\n\t\tkeyframe: false,\n\t\tsequence: 1,\n\t});\n\n\tconst result = format.decode(segment);\n\n\texpect(result).toHaveLength(1);\n\texpect(result[0].keyframe).toBe(false);\n});\n\ntest(\"CmafFormat converts timescale units to microseconds\", () => {\n\tconst format = new CmafFormat(TEST_INIT);\n\t// 90000 timescale units = 1 second = 1_000_000 microseconds\n\tconst segment = encodeDataSegment({\n\t\tdata: new Uint8Array([0x01]),\n\t\ttimestamp: TIMESCALE,\n\t\tduration: 3000,\n\t\tkeyframe: true,\n\t\tsequence: 0,\n\t});\n\n\tconst result = format.decode(segment);\n\texpect(result[0].timestamp).toBe(1_000_000 as Time.Micro);\n});\n\ntest(\"CmafFormat throws on corrupt segment\", () => {\n\tconst format = new CmafFormat(TEST_INIT);\n\texpect(() => format.decode(new Uint8Array([0x00, 0x01, 0x02]))).toThrow();\n});\n\n// --- Consumer ---\n\nfunction encodeLegacy(timestamp: Time.Micro): Uint8Array {\n\tconst tsBytes = Varint.encode(timestamp);\n\tconst payload = new Uint8Array([0xde, 0xad]);\n\tconst data = new Uint8Array(tsBytes.byteLength + payload.byteLength);\n\tdata.set(tsBytes, 0);\n\tdata.set(payload, tsBytes.byteLength);\n\treturn data;\n}\n\nfunction writeGroupWithLegacyFrames(track: Track, sequence: number, timestamps: Time.Micro[]) {\n\tconst group = new Group(sequence);\n\tfor (const ts of timestamps) {\n\t\tgroup.writeFrame(encodeLegacy(ts));\n\t}\n\tgroup.close();\n\ttrack.writeGroup(group);\n}\n\nasync function drainFrames(\n\tconsumer: Consumer,\n\ttimeout: number,\n): Promise<{ timestamp: Time.Micro; group: number; keyframe: boolean }[]> {\n\tconst frames: { timestamp: Time.Micro; group: number; keyframe: boolean }[] = [];\n\tfor (;;) {\n\t\tconst result = await Promise.race([\n\t\t\tconsumer.next(),\n\t\t\tnew Promise<null>((resolve) => setTimeout(() => resolve(null), timeout)),\n\t\t]);\n\t\tif (result === null || result === undefined) break;\n\t\tif (result.frame) {\n\t\t\tframes.push({ timestamp: result.frame.timestamp, group: result.group, keyframe: result.frame.keyframe });\n\t\t}\n\t}\n\treturn frames;\n}\n\ntest(\"Consumer delivers frames from a single group\", async () => {\n\tconst track = new Track(\"test\");\n\tconst consumer = new Consumer(track, { format: new LegacyFormat(), latency: 500 as Time.Milli });\n\n\twriteGroupWithLegacyFrames(track, 0, [0 as Time.Micro, 33_000 as Time.Micro]);\n\ttrack.close();\n\n\tconst frames = await drainFrames(consumer, 200);\n\texpect(frames).toHaveLength(2);\n\texpect(frames[0].timestamp).toBe(0 as Time.Micro);\n\texpect(frames[1].timestamp).toBe(33_000 as Time.Micro);\n\tconsumer.close();\n});\n\ntest(\"Consumer forces keyframe true at index 0\", async () => {\n\tconst track = new Track(\"test\");\n\tconst consumer = new Consumer(track, { format: new LegacyFormat(), latency: 500 as Time.Milli });\n\n\twriteGroupWithLegacyFrames(track, 0, [0 as Time.Micro, 33_000 as Time.Micro]);\n\ttrack.close();\n\n\tconst frames = await drainFrames(consumer, 200);\n\texpect(frames[0].keyframe).toBe(true);\n\texpect(frames[1].keyframe).toBe(false);\n\tconsumer.close();\n});\n\ntest(\"Consumer index spans MoQ frames for keyframe detection\", async () => {\n\t// Custom format that returns 3 samples per MoQ frame, all keyframe: false\n\tconst multiFormat: ContainerFormat = {\n\t\tdecode(_frame: Uint8Array): Frame[] {\n\t\t\treturn [\n\t\t\t\t{ data: new Uint8Array([1]), timestamp: 0 as Time.Micro, keyframe: false },\n\t\t\t\t{ data: new Uint8Array([2]), timestamp: 33_000 as Time.Micro, keyframe: false },\n\t\t\t\t{ data: new Uint8Array([3]), timestamp: 66_000 as Time.Micro, keyframe: false },\n\t\t\t];\n\t\t},\n\t};\n\n\tconst track = new Track(\"test\");\n\tconst consumer = new Consumer(track, { format: multiFormat, latency: 500 as Time.Milli });\n\n\tconst group = new Group(0);\n\tgroup.writeFrame(new Uint8Array([0x01])); // first MoQ frame → 3 samples\n\tgroup.writeFrame(new Uint8Array([0x02])); // second MoQ frame → 3 samples\n\tgroup.close();\n\ttrack.writeGroup(group);\n\ttrack.close();\n\n\tconst frames = await drainFrames(consumer, 200);\n\texpect(frames).toHaveLength(6);\n\t// Only index 0 is keyframe, rest are false\n\texpect(frames.map((f) => f.keyframe)).toEqual([true, false, false, false, false, false]);\n\tconsumer.close();\n});\n\ntest(\"Consumer keeps frames decoded before an error (truncated GoP)\", async () => {\n\t// 0xFF in the first byte signals the format to throw, simulating a stream\n\t// RESET or corrupt frame mid-group. Encoding the trigger in the frame bytes\n\t// keeps this deterministic when groups decode in parallel.\n\tconst truncatingFormat: ContainerFormat = {\n\t\tdecode(frame: Uint8Array): Frame[] {\n\t\t\tif (frame[0] === 0xff) throw new Error(\"truncated\");\n\t\t\treturn [{ data: frame, timestamp: frame[0] as Time.Micro, keyframe: false }];\n\t\t},\n\t};\n\n\tconst track = new Track(\"test\");\n\tconst consumer = new Consumer(track, { format: truncatingFormat, latency: 500 as Time.Milli });\n\n\t// Group 0: 2 valid frames then a tail-truncating error.\n\tconst g0 = new Group(0);\n\tg0.writeFrame(new Uint8Array([0x01]));\n\tg0.writeFrame(new Uint8Array([0x02]));\n\tg0.writeFrame(new Uint8Array([0xff]));\n\tg0.close();\n\ttrack.writeGroup(g0);\n\n\t// Group 1 decodes cleanly.\n\tconst g1 = new Group(1);\n\tg1.writeFrame(new Uint8Array([0x04]));\n\tg1.close();\n\ttrack.writeGroup(g1);\n\n\ttrack.close();\n\n\tconst frames = await drainFrames(consumer, 200);\n\t// First 2 frames of group 0 survive; group 1 follows.\n\texpect(frames.map((f) => f.group)).toEqual([0, 0, 1]);\n\texpect(frames.map((f) => f.timestamp as number)).toEqual([1, 2, 4]);\n\tconsumer.close();\n});\n\ntest(\"Consumer close returns undefined from next()\", async () => {\n\tconst track = new Track(\"test\");\n\tconst consumer = new Consumer(track, { format: new LegacyFormat(), latency: 500 as Time.Milli });\n\n\tconst promise = consumer.next();\n\tconsumer.close();\n\n\tconst result = await promise;\n\texpect(result).toBeUndefined();\n});\n\ntest(\"Consumer throws on concurrent next() calls\", async () => {\n\tconst track = new Track(\"test\");\n\tconst consumer = new Consumer(track, { format: new LegacyFormat(), latency: 500 as Time.Milli });\n\n\t// First call blocks waiting for data\n\tvoid consumer.next();\n\n\t// Second call should throw\n\texpect(() => consumer.next()).toThrow(\"multiple calls to next not supported\");\n\tconsumer.close();\n});\n\ntest(\"Consumer skips groups via PTS-span when over latency\", async () => {\n\tconst track = new Track(\"test\");\n\t// Zero latency = skip everything that's not the latest\n\tconst consumer = new Consumer(track, { format: new LegacyFormat(), latency: 0 as Time.Milli });\n\n\t// Write groups with increasing timestamps. With 0 latency, any PTS span > 0 triggers skip.\n\twriteGroupWithLegacyFrames(track, 0, [0 as Time.Micro]);\n\twriteGroupWithLegacyFrames(track, 1, [100_000 as Time.Micro]);\n\twriteGroupWithLegacyFrames(track, 2, [200_000 as Time.Micro]);\n\ttrack.close();\n\n\tconst frames = await drainFrames(consumer, 300);\n\t// With zero latency, the consumer should skip to the latest group\n\tconst groups = [...new Set(frames.map((f) => f.group))];\n\texpect(groups.at(-1)).toBe(2);\n\tconsumer.close();\n});\n\n// --- Ordering ---\n\ntest(\"Consumer delivers groups in sequence order regardless of arrival order\", async () => {\n\tconst track = new Track(\"test\");\n\tconst consumer = new Consumer(track, { format: new LegacyFormat(), latency: 500 as Time.Milli });\n\n\twriteGroupWithLegacyFrames(track, 2, [60_000 as Time.Micro]);\n\twriteGroupWithLegacyFrames(track, 0, [0 as Time.Micro]);\n\twriteGroupWithLegacyFrames(track, 1, [30_000 as Time.Micro]);\n\ttrack.close();\n\n\tawait new Promise((resolve) => setTimeout(resolve, 100));\n\n\tconst frames = await drainFrames(consumer, 500);\n\texpect(frames).toHaveLength(3);\n\texpect(frames[0].group).toBe(0);\n\texpect(frames[1].group).toBe(1);\n\texpect(frames[2].group).toBe(2);\n\tconsumer.close();\n});\n\ntest(\"Consumer rejects stale groups\", async () => {\n\tconst track = new Track(\"test\");\n\tconst consumer = new Consumer(track, { format: new LegacyFormat(), latency: 500 as Time.Milli });\n\n\t// Group 5 arrives first (sets active = 5)\n\twriteGroupWithLegacyFrames(track, 5, [0 as Time.Micro]);\n\tawait new Promise((resolve) => setTimeout(resolve, 50));\n\n\t// Group 3 is stale\n\twriteGroupWithLegacyFrames(track, 3, [100_000 as Time.Micro]);\n\t// Group 6 is valid\n\twriteGroupWithLegacyFrames(track, 6, [30_000 as Time.Micro]);\n\ttrack.close();\n\n\tawait new Promise((resolve) => setTimeout(resolve, 100));\n\n\tconst frames = await drainFrames(consumer, 500);\n\texpect(frames).toHaveLength(2);\n\texpect(frames[0].group).toBe(5);\n\texpect(frames[1].group).toBe(6);\n\tconsumer.close();\n});\n\n// --- Group boundary signals ---\n\ntest(\"Consumer next() returns group-done signals\", async () => {\n\tconst track = new Track(\"test\");\n\tconst consumer = new Consumer(track, { format: new LegacyFormat(), latency: 500 as Time.Milli });\n\n\twriteGroupWithLegacyFrames(track, 0, [0 as Time.Micro, 33_000 as Time.Micro]);\n\twriteGroupWithLegacyFrames(track, 1, [66_000 as Time.Micro]);\n\ttrack.close();\n\n\tawait new Promise((resolve) => setTimeout(resolve, 50));\n\n\tconst allResults: { frame: boolean; group: number }[] = [];\n\tfor (;;) {\n\t\tconst result = await Promise.race([\n\t\t\tconsumer.next(),\n\t\t\tnew Promise<null>((resolve) => setTimeout(() => resolve(null), 500)),\n\t\t]);\n\t\tif (result === null || result === undefined) break;\n\t\tallResults.push({ frame: result.frame !== undefined, group: result.group });\n\t}\n\n\tconst frameResults = allResults.filter((r) => r.frame);\n\tconst boundaries = allResults.filter((r) => !r.frame);\n\texpect(frameResults).toHaveLength(3);\n\texpect(boundaries).toHaveLength(2);\n\texpect(boundaries[0].group).toBe(0);\n\texpect(boundaries[1].group).toBe(1);\n\tconsumer.close();\n});\n\n// --- Buffered signal ---\n\ntest(\"Consumer buffered signal updates as frames arrive\", async () => {\n\tconst track = new Track(\"test\");\n\tconst consumer = new Consumer(track, { format: new LegacyFormat(), latency: 500 as Time.Milli });\n\n\texpect(consumer.buffered.peek()).toEqual([]);\n\n\twriteGroupWithLegacyFrames(track, 0, [0 as Time.Micro, 33_000 as Time.Micro]);\n\twriteGroupWithLegacyFrames(track, 1, [66_000 as Time.Micro, 99_000 as Time.Micro]);\n\n\tawait new Promise((resolve) => setTimeout(resolve, 100));\n\n\tconst ranges = consumer.buffered.peek();\n\texpect(ranges.length).toBe(1);\n\texpect(ranges[0].start).toBe(0 as Time.Milli);\n\texpect((ranges[0].end as number) >= 66).toBeTruthy();\n\n\ttrack.close();\n\tconsumer.close();\n});\n\n// --- Gap recovery ---\n\ntest(\"Consumer recovers from gap in group sequence numbers\", async () => {\n\tconst track = new Track(\"test\");\n\tconst consumer = new Consumer(track, { format: new LegacyFormat(), latency: 100 as Time.Milli });\n\n\twriteGroupWithLegacyFrames(track, 0, [0 as Time.Micro, 20_000 as Time.Micro]);\n\twriteGroupWithLegacyFrames(track, 1, [40_000 as Time.Micro, 60_000 as Time.Micro]);\n\t// Skip group 2\n\twriteGroupWithLegacyFrames(track, 3, [120_000 as Time.Micro, 140_000 as Time.Micro]);\n\twriteGroupWithLegacyFrames(track, 4, [160_000 as Time.Micro, 180_000 as Time.Micro]);\n\twriteGroupWithLegacyFrames(track, 5, [200_000 as Time.Micro, 220_000 as Time.Micro]);\n\ttrack.close();\n\n\tawait new Promise((resolve) => setTimeout(resolve, 100));\n\n\tconst frames = await drainFrames(consumer, 500);\n\texpect(frames.length >= 4).toBeTruthy();\n\tconsumer.close();\n});\n\n// --- Edge cases from design review ---\n\ntest(\"Consumer handles empty decode result without deadlock\", async () => {\n\tlet callCount = 0;\n\tconst emptyThenValid: ContainerFormat = {\n\t\tdecode(_frame: Uint8Array): Frame[] {\n\t\t\tcallCount++;\n\t\t\tif (callCount === 1) return []; // empty result\n\t\t\treturn [{ data: new Uint8Array([1]), timestamp: 33_000 as Time.Micro, keyframe: false }];\n\t\t},\n\t};\n\n\tconst track = new Track(\"test\");\n\tconst consumer = new Consumer(track, { format: emptyThenValid, latency: 500 as Time.Milli });\n\n\tconst group = new Group(0);\n\tgroup.writeFrame(new Uint8Array([0x01])); // empty decode\n\tgroup.writeFrame(new Uint8Array([0x02])); // valid decode\n\tgroup.close();\n\ttrack.writeGroup(group);\n\ttrack.close();\n\n\tconst frames = await drainFrames(consumer, 300);\n\t// The empty decode produces no frames, but the second MoQ frame does.\n\t// Since index 0 was never used (empty result), the first actual frame gets index=1 → keyframe false?\n\t// Actually index increments per sample, and empty decode means 0 samples → index stays at 0.\n\t// So the next frame's first sample gets index=0 → keyframe=true.\n\texpect(frames).toHaveLength(1);\n\texpect(frames[0].keyframe).toBe(true);\n\tconsumer.close();\n});\n\n// --- CMAF through Consumer ---\n\ntest(\"Consumer with CmafFormat delivers correct timestamps\", async () => {\n\tconst track = new Track(\"test\");\n\tconst consumer = new Consumer(track, {\n\t\tformat: new CmafFormat(TEST_INIT),\n\t\tlatency: 500 as Time.Milli,\n\t});\n\n\tconst group = new Group(0);\n\tgroup.writeFrame(\n\t\tencodeDataSegment({\n\t\t\tdata: new Uint8Array([0xca, 0xfe]),\n\t\t\ttimestamp: 0,\n\t\t\tduration: 3000,\n\t\t\tkeyframe: true,\n\t\t\tsequence: 0,\n\t\t}),\n\t);\n\tgroup.writeFrame(\n\t\tencodeDataSegment({\n\t\t\tdata: new Uint8Array([0xbe, 0xef]),\n\t\t\ttimestamp: 3000,\n\t\t\tduration: 3000,\n\t\t\tkeyframe: false,\n\t\t\tsequence: 0,\n\t\t}),\n\t);\n\tgroup.close();\n\ttrack.writeGroup(group);\n\ttrack.close();\n\n\tconst frames = await drainFrames(consumer, 200);\n\texpect(frames).toHaveLength(2);\n\texpect(frames[0].keyframe).toBe(true); // index 0 override\n\texpect(frames[1].keyframe).toBe(false); // trusts format\n\texpect(frames[0].timestamp).toBe(0 as Time.Micro);\n\texpect(frames[1].timestamp).toBe(33_333 as Time.Micro); // 3000/90000 * 1_000_000\n\tconsumer.close();\n});\n"]}
@@ -0,0 +1,12 @@
1
+ import type { Frame } from "./types";
2
+ /** A container format that decodes raw MoQ frames into media frames. */
3
+ export interface Format {
4
+ /**
5
+ * Parse one MoQ frame into decoded media frames. `extensions` carries the raw
6
+ * MOQ Object Properties bytes (undefined when the object had none) — LOC reads
7
+ * its per-frame Timestamp from there (draft-ietf-moq-loc-03 §2.3); other
8
+ * formats ignore it.
9
+ */
10
+ decode(frame: Uint8Array, extensions?: Uint8Array): Frame[];
11
+ }
12
+ //# sourceMappingURL=format.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"format.d.ts","sourceRoot":"","sources":["../../src/container/format.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAErC,wEAAwE;AACxE,MAAM,WAAW,MAAM;IACtB;;;;;OAKG;IACH,MAAM,CAAC,KAAK,EAAE,UAAU,EAAE,UAAU,CAAC,EAAE,UAAU,GAAG,KAAK,EAAE,CAAC;CAC5D"}
@@ -0,0 +1,3 @@
1
+ /* @ts-self-types="./format.d.ts" */
2
+ export {};
3
+ //# sourceMappingURL=format.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"format.js","sourceRoot":"","sources":["../../src/container/format.ts"],"names":[],"mappings":"","sourcesContent":["import type { Frame } from \"./types\";\n\n/** A container format that decodes raw MoQ frames into media frames. */\nexport interface Format {\n\t/**\n\t * Parse one MoQ frame into decoded media frames. `extensions` carries the raw\n\t * MOQ Object Properties bytes (undefined when the object had none) — LOC reads\n\t * its per-frame Timestamp from there (draft-ietf-moq-loc-03 §2.3); other\n\t * formats ignore it.\n\t */\n\tdecode(frame: Uint8Array, extensions?: Uint8Array): Frame[];\n}\n"]}
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Per-frame container formats (timestamp plus codec bitstream): the legacy varint
3
+ * container, CMAF (fMP4), and LOC, with consumers and producers to read/write them.
4
+ *
5
+ * @module
6
+ */
7
+ export * as Loc from "@norskvideo/moq-loc";
8
+ export * as Cmaf from "./cmaf";
9
+ export { Consumer, type ConsumerProps } from "./consumer";
10
+ export type { Format } from "./format";
11
+ export * as Legacy from "./legacy";
12
+ export * as Timeline from "./timeline";
13
+ export * from "./types";
14
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/container/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,GAAG,MAAM,UAAU,CAAC;AAChC,OAAO,KAAK,IAAI,MAAM,QAAQ,CAAC;AAC/B,OAAO,EAAE,QAAQ,EAAE,KAAK,aAAa,EAAE,MAAM,YAAY,CAAC;AAC1D,YAAY,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AACvC,OAAO,KAAK,MAAM,MAAM,UAAU,CAAC;AACnC,OAAO,KAAK,QAAQ,MAAM,YAAY,CAAC;AACvC,cAAc,SAAS,CAAC"}
@@ -0,0 +1,18 @@
1
+ /* @ts-self-types="./index.d.ts" */
2
+ /**
3
+ * Per-frame container formats (timestamp plus codec bitstream): the legacy varint
4
+ * container, CMAF (fMP4), and LOC, with consumers and producers to read/write them.
5
+ *
6
+ * @module
7
+ */
8
+ import * as Loc_1 from "@norskvideo/moq-loc";
9
+ export { Loc_1 as Loc };
10
+ import * as Cmaf_1 from "./cmaf";
11
+ export { Cmaf_1 as Cmaf };
12
+ export { Consumer } from "./consumer";
13
+ import * as Legacy_1 from "./legacy";
14
+ export { Legacy_1 as Legacy };
15
+ import * as Timeline_1 from "./timeline";
16
+ export { Timeline_1 as Timeline };
17
+ export * from "./types";
18
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/container/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;uBAEkB,UAAU;kBAAnB,GAAG;wBACO,QAAQ;mBAAlB,IAAI;AAChB,OAAO,EAAE,QAAQ,EAAsB,MAAM,YAAY,CAAC;0BAElC,UAAU;qBAAtB,MAAM;4BACQ,YAAY;uBAA1B,QAAQ;AACpB,cAAc,SAAS,CAAC","sourcesContent":["/**\n * Per-frame container formats (timestamp plus codec bitstream): the legacy varint\n * container, CMAF (fMP4), and LOC, with consumers and producers to read/write them.\n *\n * @module\n */\n\nexport * as Loc from \"@moq/loc\";\nexport * as Cmaf from \"./cmaf\";\nexport { Consumer, type ConsumerProps } from \"./consumer\";\nexport type { Format } from \"./format\";\nexport * as Legacy from \"./legacy\";\nexport * as Timeline from \"./timeline\";\nexport * from \"./types\";\n"]}
@@ -0,0 +1,39 @@
1
+ import type { Time } from "@norskvideo/moq-net";
2
+ import * as Moq from "@norskvideo/moq-net";
3
+ export type { BufferedRange, BufferedRanges, Frame } from "./types";
4
+ import type { Format as ContainerFormat } from "./format";
5
+ import type { Producer as TimelineProducer } from "./timeline";
6
+ import type { Frame } from "./types";
7
+ /** The legacy hang container: a microsecond timestamp varint followed by the raw codec payload. */
8
+ export declare class Format implements ContainerFormat {
9
+ /** Decode one legacy frame into a single media frame. */
10
+ decode(frame: Uint8Array): Frame[];
11
+ }
12
+ /** A byte source that can be copied into a buffer, e.g. a WebCodecs EncodedChunk. */
13
+ export interface Source {
14
+ /** Number of bytes the source will copy. */
15
+ byteLength: number;
16
+ /** Copy the source bytes into the given buffer. */
17
+ copyTo(buffer: Uint8Array): void;
18
+ }
19
+ /** Encode a frame as a timestamp varint followed by the payload bytes. */
20
+ export declare function encodeFrame(source: Uint8Array | Source, timestamp: Time.Micro): Uint8Array;
21
+ /** Options for a legacy-container {@link Producer}. */
22
+ export interface ProducerProps {
23
+ /**
24
+ * Record each group open (sequence + start timestamp) into this companion timeline track, so
25
+ * consumers can index the media without downloading it.
26
+ */
27
+ timeline?: TimelineProducer;
28
+ }
29
+ /** Writes legacy-container frames into a MoQ track, starting a new group on each keyframe. */
30
+ export declare class Producer {
31
+ #private;
32
+ /** Wrap a track to publish legacy-container frames into it. */
33
+ constructor(track: Moq.Track, props?: ProducerProps);
34
+ /** Encode and append a frame; a keyframe starts a new group. Throws if the first frame is not a keyframe. */
35
+ encode(data: Uint8Array | Source, timestamp: Time.Micro, keyframe: boolean): void;
36
+ /** Close the track and current group, optionally with an error. */
37
+ close(err?: Error): void;
38
+ }
39
+ //# sourceMappingURL=legacy.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"legacy.d.ts","sourceRoot":"","sources":["../../src/container/legacy.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,UAAU,CAAC;AACrC,OAAO,KAAK,GAAG,MAAM,UAAU,CAAC;AAEhC,YAAY,EAAE,aAAa,EAAE,cAAc,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAEpE,OAAO,KAAK,EAAE,MAAM,IAAI,eAAe,EAAE,MAAM,UAAU,CAAC;AAC1D,OAAO,KAAK,EAAE,QAAQ,IAAI,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAC/D,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAErC,mGAAmG;AACnG,qBAAa,MAAO,YAAW,eAAe;IAC7C,yDAAyD;IACzD,MAAM,CAAC,KAAK,EAAE,UAAU,GAAG,KAAK,EAAE,CAGjC;CACD;AAED,qFAAqF;AACrF,MAAM,WAAW,MAAM;IACtB,4CAA4C;IAC5C,UAAU,EAAE,MAAM,CAAC;IACnB,mDAAmD;IACnD,MAAM,CAAC,MAAM,EAAE,UAAU,GAAG,IAAI,CAAC;CACjC;AAED,0EAA0E;AAC1E,wBAAgB,WAAW,CAAC,MAAM,EAAE,UAAU,GAAG,MAAM,EAAE,SAAS,EAAE,IAAI,CAAC,KAAK,GAAG,UAAU,CAY1F;AAED,uDAAuD;AACvD,MAAM,WAAW,aAAa;IAC7B;;;OAGG;IACH,QAAQ,CAAC,EAAE,gBAAgB,CAAC;CAC5B;AAED,8FAA8F;AAC9F,qBAAa,QAAQ;;IAKpB,+DAA+D;IAC/D,YAAY,KAAK,EAAE,GAAG,CAAC,KAAK,EAAE,KAAK,GAAE,aAAkB,EAGtD;IAED,6GAA6G;IAC7G,MAAM,CAAC,IAAI,EAAE,UAAU,GAAG,MAAM,EAAE,SAAS,EAAE,IAAI,CAAC,KAAK,EAAE,QAAQ,EAAE,OAAO,QAWzE;IAED,mEAAmE;IACnE,KAAK,CAAC,GAAG,CAAC,EAAE,KAAK,QAGhB;CACD"}
@@ -0,0 +1,53 @@
1
+ /* @ts-self-types="./legacy.d.ts" */
2
+ import * as Moq from "@norskvideo/moq-net";
3
+ /** The legacy hang container: a microsecond timestamp varint followed by the raw codec payload. */
4
+ export class Format {
5
+ /** Decode one legacy frame into a single media frame. */
6
+ decode(frame) {
7
+ const [timestamp, data] = Moq.Varint.decode(frame);
8
+ return [{ data, timestamp: timestamp, keyframe: false }];
9
+ }
10
+ }
11
+ /** Encode a frame as a timestamp varint followed by the payload bytes. */
12
+ export function encodeFrame(source, timestamp) {
13
+ const timestampBytes = Moq.Varint.encode(timestamp);
14
+ const data = new Uint8Array(timestampBytes.byteLength + source.byteLength);
15
+ data.set(timestampBytes, 0);
16
+ if (source instanceof Uint8Array) {
17
+ data.set(source, timestampBytes.byteLength);
18
+ }
19
+ else {
20
+ source.copyTo(data.subarray(timestampBytes.byteLength));
21
+ }
22
+ return data;
23
+ }
24
+ /** Writes legacy-container frames into a MoQ track, starting a new group on each keyframe. */
25
+ export class Producer {
26
+ #track;
27
+ #group;
28
+ #timeline;
29
+ /** Wrap a track to publish legacy-container frames into it. */
30
+ constructor(track, props = {}) {
31
+ this.#track = track;
32
+ this.#timeline = props.timeline;
33
+ }
34
+ /** Encode and append a frame; a keyframe starts a new group. Throws if the first frame is not a keyframe. */
35
+ encode(data, timestamp, keyframe) {
36
+ if (keyframe) {
37
+ this.#group?.close();
38
+ this.#group = this.#track.appendGroup();
39
+ // Index the group the moment it opens: its start is this keyframe's timestamp.
40
+ this.#timeline?.record(this.#group.sequence, timestamp);
41
+ }
42
+ else if (!this.#group) {
43
+ throw new Error("must start with a keyframe");
44
+ }
45
+ this.#group?.writeFrame(encodeFrame(data, timestamp));
46
+ }
47
+ /** Close the track and current group, optionally with an error. */
48
+ close(err) {
49
+ this.#track.close(err);
50
+ this.#group?.close();
51
+ }
52
+ }
53
+ //# sourceMappingURL=legacy.js.map