googlevideo 1.1.0 → 3.0.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 (60) hide show
  1. package/README.md +13 -0
  2. package/dist/protos/generated/misc/common.d.ts +0 -15
  3. package/dist/protos/generated/misc/common.js +0 -133
  4. package/dist/protos/generated/video_streaming/buffered_range.d.ts +0 -15
  5. package/dist/protos/generated/video_streaming/buffered_range.js +0 -146
  6. package/dist/protos/generated/video_streaming/client_abr_state.d.ts +16 -38
  7. package/dist/protos/generated/video_streaming/client_abr_state.js +94 -318
  8. package/dist/protos/generated/video_streaming/crypto_params.d.ts +0 -17
  9. package/dist/protos/generated/video_streaming/crypto_params.js +0 -78
  10. package/dist/protos/generated/video_streaming/encrypted_player_request.d.ts +2 -17
  11. package/dist/protos/generated/video_streaming/encrypted_player_request.js +14 -91
  12. package/dist/protos/generated/video_streaming/format_initialization_metadata.d.ts +0 -15
  13. package/dist/protos/generated/video_streaming/format_initialization_metadata.js +0 -74
  14. package/dist/protos/generated/video_streaming/live_metadata.d.ts +17 -0
  15. package/dist/protos/generated/video_streaming/live_metadata.js +122 -0
  16. package/dist/protos/generated/video_streaming/media_capabilities.d.ts +0 -15
  17. package/dist/protos/generated/video_streaming/media_capabilities.js +0 -121
  18. package/dist/protos/generated/video_streaming/media_header.d.ts +0 -17
  19. package/dist/protos/generated/video_streaming/media_header.js +0 -127
  20. package/dist/protos/generated/video_streaming/next_request_policy.d.ts +0 -15
  21. package/dist/protos/generated/video_streaming/next_request_policy.js +0 -49
  22. package/dist/protos/generated/video_streaming/onesie_header.d.ts +0 -15
  23. package/dist/protos/generated/video_streaming/onesie_header.js +0 -122
  24. package/dist/protos/generated/video_streaming/onesie_header_type.d.ts +0 -2
  25. package/dist/protos/generated/video_streaming/onesie_header_type.js +0 -145
  26. package/dist/protos/generated/video_streaming/onesie_player_request.d.ts +1 -17
  27. package/dist/protos/generated/video_streaming/onesie_player_request.js +4 -49
  28. package/dist/protos/generated/video_streaming/onesie_player_response.d.ts +18 -16
  29. package/dist/protos/generated/video_streaming/onesie_player_response.js +18 -53
  30. package/dist/protos/generated/video_streaming/onesie_request.d.ts +2 -15
  31. package/dist/protos/generated/video_streaming/onesie_request.js +10 -83
  32. package/dist/protos/generated/video_streaming/playback_cookie.d.ts +0 -15
  33. package/dist/protos/generated/video_streaming/playback_cookie.js +0 -42
  34. package/dist/protos/generated/video_streaming/playback_start_policy.d.ts +0 -15
  35. package/dist/protos/generated/video_streaming/playback_start_policy.js +0 -65
  36. package/dist/protos/generated/video_streaming/proxy_status.d.ts +0 -2
  37. package/dist/protos/generated/video_streaming/proxy_status.js +0 -65
  38. package/dist/protos/generated/video_streaming/request_cancellation_policy.d.ts +0 -15
  39. package/dist/protos/generated/video_streaming/request_cancellation_policy.js +0 -65
  40. package/dist/protos/generated/video_streaming/sabr_error.d.ts +0 -15
  41. package/dist/protos/generated/video_streaming/sabr_error.js +0 -28
  42. package/dist/protos/generated/video_streaming/sabr_redirect.d.ts +0 -15
  43. package/dist/protos/generated/video_streaming/sabr_redirect.js +0 -21
  44. package/dist/protos/generated/video_streaming/stream_protection_status.d.ts +0 -15
  45. package/dist/protos/generated/video_streaming/stream_protection_status.js +0 -28
  46. package/dist/protos/generated/video_streaming/streamer_context.d.ts +0 -17
  47. package/dist/protos/generated/video_streaming/streamer_context.js +0 -350
  48. package/dist/protos/generated/video_streaming/time_range.d.ts +0 -15
  49. package/dist/protos/generated/video_streaming/time_range.js +0 -33
  50. package/dist/protos/generated/video_streaming/video_playback_abr_request.d.ts +1 -15
  51. package/dist/protos/generated/video_streaming/video_playback_abr_request.js +18 -256
  52. package/dist/src/core/ServerAbrStream.d.ts +1 -0
  53. package/dist/src/core/ServerAbrStream.js +34 -47
  54. package/dist/src/core/UMP.d.ts +25 -1
  55. package/dist/src/core/UMP.js +35 -20
  56. package/dist/src/utils/Protos.d.ts +3 -2
  57. package/dist/src/utils/Protos.js +3 -2
  58. package/package.json +25 -24
  59. package/bundle/index.cjs +0 -7134
  60. package/bundle/index.cjs.map +0 -7
@@ -15,6 +15,7 @@ function createBaseVideoPlaybackAbrRequest() {
15
15
  clientAbrState: undefined,
16
16
  selectedFormatIds: [],
17
17
  bufferedRanges: [],
18
+ playerTimeMs: 0,
18
19
  videoPlaybackUstreamerConfig: new Uint8Array(0),
19
20
  lo: undefined,
20
21
  selectedAudioFormatIds: [],
@@ -37,6 +38,9 @@ export const VideoPlaybackAbrRequest = {
37
38
  for (const v of message.bufferedRanges) {
38
39
  BufferedRange.encode(v, writer.uint32(26).fork()).join();
39
40
  }
41
+ if (message.playerTimeMs !== undefined && message.playerTimeMs !== 0) {
42
+ writer.uint32(32).int64(message.playerTimeMs);
43
+ }
40
44
  if (message.videoPlaybackUstreamerConfig !== undefined && message.videoPlaybackUstreamerConfig.length !== 0) {
41
45
  writer.uint32(42).bytes(message.videoPlaybackUstreamerConfig);
42
46
  }
@@ -91,6 +95,12 @@ export const VideoPlaybackAbrRequest = {
91
95
  }
92
96
  message.bufferedRanges.push(BufferedRange.decode(reader, reader.uint32()));
93
97
  continue;
98
+ case 4:
99
+ if (tag !== 32) {
100
+ break;
101
+ }
102
+ message.playerTimeMs = longToNumber(reader.int64());
103
+ continue;
94
104
  case 5:
95
105
  if (tag !== 42) {
96
106
  break;
@@ -153,97 +163,6 @@ export const VideoPlaybackAbrRequest = {
153
163
  }
154
164
  return message;
155
165
  },
156
- fromJSON(object) {
157
- return {
158
- clientAbrState: isSet(object.clientAbrState) ? ClientAbrState.fromJSON(object.clientAbrState) : undefined,
159
- selectedFormatIds: globalThis.Array.isArray(object?.selectedFormatIds)
160
- ? object.selectedFormatIds.map((e) => FormatId.fromJSON(e))
161
- : [],
162
- bufferedRanges: globalThis.Array.isArray(object?.bufferedRanges)
163
- ? object.bufferedRanges.map((e) => BufferedRange.fromJSON(e))
164
- : [],
165
- videoPlaybackUstreamerConfig: isSet(object.videoPlaybackUstreamerConfig)
166
- ? bytesFromBase64(object.videoPlaybackUstreamerConfig)
167
- : new Uint8Array(0),
168
- lo: isSet(object.lo) ? Lo.fromJSON(object.lo) : undefined,
169
- selectedAudioFormatIds: globalThis.Array.isArray(object?.selectedAudioFormatIds)
170
- ? object.selectedAudioFormatIds.map((e) => FormatId.fromJSON(e))
171
- : [],
172
- selectedVideoFormatIds: globalThis.Array.isArray(object?.selectedVideoFormatIds)
173
- ? object.selectedVideoFormatIds.map((e) => FormatId.fromJSON(e))
174
- : [],
175
- streamerContext: isSet(object.streamerContext) ? StreamerContext.fromJSON(object.streamerContext) : undefined,
176
- field21: isSet(object.field21) ? OQa.fromJSON(object.field21) : undefined,
177
- field22: isSet(object.field22) ? globalThis.Number(object.field22) : 0,
178
- field23: isSet(object.field23) ? globalThis.Number(object.field23) : 0,
179
- field1000: globalThis.Array.isArray(object?.field1000) ? object.field1000.map((e) => Pqa.fromJSON(e)) : [],
180
- };
181
- },
182
- toJSON(message) {
183
- const obj = {};
184
- if (message.clientAbrState !== undefined) {
185
- obj.clientAbrState = ClientAbrState.toJSON(message.clientAbrState);
186
- }
187
- if (message.selectedFormatIds?.length) {
188
- obj.selectedFormatIds = message.selectedFormatIds.map((e) => FormatId.toJSON(e));
189
- }
190
- if (message.bufferedRanges?.length) {
191
- obj.bufferedRanges = message.bufferedRanges.map((e) => BufferedRange.toJSON(e));
192
- }
193
- if (message.videoPlaybackUstreamerConfig !== undefined && message.videoPlaybackUstreamerConfig.length !== 0) {
194
- obj.videoPlaybackUstreamerConfig = base64FromBytes(message.videoPlaybackUstreamerConfig);
195
- }
196
- if (message.lo !== undefined) {
197
- obj.lo = Lo.toJSON(message.lo);
198
- }
199
- if (message.selectedAudioFormatIds?.length) {
200
- obj.selectedAudioFormatIds = message.selectedAudioFormatIds.map((e) => FormatId.toJSON(e));
201
- }
202
- if (message.selectedVideoFormatIds?.length) {
203
- obj.selectedVideoFormatIds = message.selectedVideoFormatIds.map((e) => FormatId.toJSON(e));
204
- }
205
- if (message.streamerContext !== undefined) {
206
- obj.streamerContext = StreamerContext.toJSON(message.streamerContext);
207
- }
208
- if (message.field21 !== undefined) {
209
- obj.field21 = OQa.toJSON(message.field21);
210
- }
211
- if (message.field22 !== undefined && message.field22 !== 0) {
212
- obj.field22 = Math.round(message.field22);
213
- }
214
- if (message.field23 !== undefined && message.field23 !== 0) {
215
- obj.field23 = Math.round(message.field23);
216
- }
217
- if (message.field1000?.length) {
218
- obj.field1000 = message.field1000.map((e) => Pqa.toJSON(e));
219
- }
220
- return obj;
221
- },
222
- create(base) {
223
- return VideoPlaybackAbrRequest.fromPartial(base ?? {});
224
- },
225
- fromPartial(object) {
226
- const message = createBaseVideoPlaybackAbrRequest();
227
- message.clientAbrState = (object.clientAbrState !== undefined && object.clientAbrState !== null)
228
- ? ClientAbrState.fromPartial(object.clientAbrState)
229
- : undefined;
230
- message.selectedFormatIds = object.selectedFormatIds?.map((e) => FormatId.fromPartial(e)) || [];
231
- message.bufferedRanges = object.bufferedRanges?.map((e) => BufferedRange.fromPartial(e)) || [];
232
- message.videoPlaybackUstreamerConfig = object.videoPlaybackUstreamerConfig ?? new Uint8Array(0);
233
- message.lo = (object.lo !== undefined && object.lo !== null) ? Lo.fromPartial(object.lo) : undefined;
234
- message.selectedAudioFormatIds = object.selectedAudioFormatIds?.map((e) => FormatId.fromPartial(e)) || [];
235
- message.selectedVideoFormatIds = object.selectedVideoFormatIds?.map((e) => FormatId.fromPartial(e)) || [];
236
- message.streamerContext = (object.streamerContext !== undefined && object.streamerContext !== null)
237
- ? StreamerContext.fromPartial(object.streamerContext)
238
- : undefined;
239
- message.field21 = (object.field21 !== undefined && object.field21 !== null)
240
- ? OQa.fromPartial(object.field21)
241
- : undefined;
242
- message.field22 = object.field22 ?? 0;
243
- message.field23 = object.field23 ?? 0;
244
- message.field1000 = object.field1000?.map((e) => Pqa.fromPartial(e)) || [];
245
- return message;
246
- },
247
166
  };
248
167
  function createBaseLo() {
249
168
  return { formatId: undefined, Lj: 0, sequenceNumber: 0, field4: undefined, MZ: 0 };
@@ -312,50 +231,6 @@ export const Lo = {
312
231
  }
313
232
  return message;
314
233
  },
315
- fromJSON(object) {
316
- return {
317
- formatId: isSet(object.formatId) ? FormatId.fromJSON(object.formatId) : undefined,
318
- Lj: isSet(object.Lj) ? globalThis.Number(object.Lj) : 0,
319
- sequenceNumber: isSet(object.sequenceNumber) ? globalThis.Number(object.sequenceNumber) : 0,
320
- field4: isSet(object.field4) ? Lo_Field4.fromJSON(object.field4) : undefined,
321
- MZ: isSet(object.MZ) ? globalThis.Number(object.MZ) : 0,
322
- };
323
- },
324
- toJSON(message) {
325
- const obj = {};
326
- if (message.formatId !== undefined) {
327
- obj.formatId = FormatId.toJSON(message.formatId);
328
- }
329
- if (message.Lj !== undefined && message.Lj !== 0) {
330
- obj.Lj = Math.round(message.Lj);
331
- }
332
- if (message.sequenceNumber !== undefined && message.sequenceNumber !== 0) {
333
- obj.sequenceNumber = Math.round(message.sequenceNumber);
334
- }
335
- if (message.field4 !== undefined) {
336
- obj.field4 = Lo_Field4.toJSON(message.field4);
337
- }
338
- if (message.MZ !== undefined && message.MZ !== 0) {
339
- obj.MZ = Math.round(message.MZ);
340
- }
341
- return obj;
342
- },
343
- create(base) {
344
- return Lo.fromPartial(base ?? {});
345
- },
346
- fromPartial(object) {
347
- const message = createBaseLo();
348
- message.formatId = (object.formatId !== undefined && object.formatId !== null)
349
- ? FormatId.fromPartial(object.formatId)
350
- : undefined;
351
- message.Lj = object.Lj ?? 0;
352
- message.sequenceNumber = object.sequenceNumber ?? 0;
353
- message.field4 = (object.field4 !== undefined && object.field4 !== null)
354
- ? Lo_Field4.fromPartial(object.field4)
355
- : undefined;
356
- message.MZ = object.MZ ?? 0;
357
- return message;
358
- },
359
234
  };
360
235
  function createBaseLo_Field4() {
361
236
  return { field1: 0, field2: 0, field3: 0 };
@@ -406,36 +281,6 @@ export const Lo_Field4 = {
406
281
  }
407
282
  return message;
408
283
  },
409
- fromJSON(object) {
410
- return {
411
- field1: isSet(object.field1) ? globalThis.Number(object.field1) : 0,
412
- field2: isSet(object.field2) ? globalThis.Number(object.field2) : 0,
413
- field3: isSet(object.field3) ? globalThis.Number(object.field3) : 0,
414
- };
415
- },
416
- toJSON(message) {
417
- const obj = {};
418
- if (message.field1 !== undefined && message.field1 !== 0) {
419
- obj.field1 = Math.round(message.field1);
420
- }
421
- if (message.field2 !== undefined && message.field2 !== 0) {
422
- obj.field2 = Math.round(message.field2);
423
- }
424
- if (message.field3 !== undefined && message.field3 !== 0) {
425
- obj.field3 = Math.round(message.field3);
426
- }
427
- return obj;
428
- },
429
- create(base) {
430
- return Lo_Field4.fromPartial(base ?? {});
431
- },
432
- fromPartial(object) {
433
- const message = createBaseLo_Field4();
434
- message.field1 = object.field1 ?? 0;
435
- message.field2 = object.field2 ?? 0;
436
- message.field3 = object.field3 ?? 0;
437
- return message;
438
- },
439
284
  };
440
285
  function createBaseOQa() {
441
286
  return { field1: [], field2: new Uint8Array(0), field3: "", field4: 0, field5: 0, field6: "" };
@@ -513,51 +358,6 @@ export const OQa = {
513
358
  }
514
359
  return message;
515
360
  },
516
- fromJSON(object) {
517
- return {
518
- field1: globalThis.Array.isArray(object?.field1) ? object.field1.map((e) => globalThis.String(e)) : [],
519
- field2: isSet(object.field2) ? bytesFromBase64(object.field2) : new Uint8Array(0),
520
- field3: isSet(object.field3) ? globalThis.String(object.field3) : "",
521
- field4: isSet(object.field4) ? globalThis.Number(object.field4) : 0,
522
- field5: isSet(object.field5) ? globalThis.Number(object.field5) : 0,
523
- field6: isSet(object.field6) ? globalThis.String(object.field6) : "",
524
- };
525
- },
526
- toJSON(message) {
527
- const obj = {};
528
- if (message.field1?.length) {
529
- obj.field1 = message.field1;
530
- }
531
- if (message.field2 !== undefined && message.field2.length !== 0) {
532
- obj.field2 = base64FromBytes(message.field2);
533
- }
534
- if (message.field3 !== undefined && message.field3 !== "") {
535
- obj.field3 = message.field3;
536
- }
537
- if (message.field4 !== undefined && message.field4 !== 0) {
538
- obj.field4 = Math.round(message.field4);
539
- }
540
- if (message.field5 !== undefined && message.field5 !== 0) {
541
- obj.field5 = Math.round(message.field5);
542
- }
543
- if (message.field6 !== undefined && message.field6 !== "") {
544
- obj.field6 = message.field6;
545
- }
546
- return obj;
547
- },
548
- create(base) {
549
- return OQa.fromPartial(base ?? {});
550
- },
551
- fromPartial(object) {
552
- const message = createBaseOQa();
553
- message.field1 = object.field1?.map((e) => e) || [];
554
- message.field2 = object.field2 ?? new Uint8Array(0);
555
- message.field3 = object.field3 ?? "";
556
- message.field4 = object.field4 ?? 0;
557
- message.field5 = object.field5 ?? 0;
558
- message.field6 = object.field6 ?? "";
559
- return message;
560
- },
561
361
  };
562
362
  function createBasePqa() {
563
363
  return { formats: [], ud: [], clipId: "" };
@@ -608,52 +408,14 @@ export const Pqa = {
608
408
  }
609
409
  return message;
610
410
  },
611
- fromJSON(object) {
612
- return {
613
- formats: globalThis.Array.isArray(object?.formats) ? object.formats.map((e) => FormatId.fromJSON(e)) : [],
614
- ud: globalThis.Array.isArray(object?.ud) ? object.ud.map((e) => BufferedRange.fromJSON(e)) : [],
615
- clipId: isSet(object.clipId) ? globalThis.String(object.clipId) : "",
616
- };
617
- },
618
- toJSON(message) {
619
- const obj = {};
620
- if (message.formats?.length) {
621
- obj.formats = message.formats.map((e) => FormatId.toJSON(e));
622
- }
623
- if (message.ud?.length) {
624
- obj.ud = message.ud.map((e) => BufferedRange.toJSON(e));
625
- }
626
- if (message.clipId !== undefined && message.clipId !== "") {
627
- obj.clipId = message.clipId;
628
- }
629
- return obj;
630
- },
631
- create(base) {
632
- return Pqa.fromPartial(base ?? {});
633
- },
634
- fromPartial(object) {
635
- const message = createBasePqa();
636
- message.formats = object.formats?.map((e) => FormatId.fromPartial(e)) || [];
637
- message.ud = object.ud?.map((e) => BufferedRange.fromPartial(e)) || [];
638
- message.clipId = object.clipId ?? "";
639
- return message;
640
- },
641
411
  };
642
- function bytesFromBase64(b64) {
643
- const bin = globalThis.atob(b64);
644
- const arr = new Uint8Array(bin.length);
645
- for (let i = 0; i < bin.length; ++i) {
646
- arr[i] = bin.charCodeAt(i);
412
+ function longToNumber(int64) {
413
+ const num = globalThis.Number(int64.toString());
414
+ if (num > globalThis.Number.MAX_SAFE_INTEGER) {
415
+ throw new globalThis.Error("Value is larger than Number.MAX_SAFE_INTEGER");
647
416
  }
648
- return arr;
649
- }
650
- function base64FromBytes(arr) {
651
- const bin = [];
652
- arr.forEach((byte) => {
653
- bin.push(globalThis.String.fromCharCode(byte));
654
- });
655
- return globalThis.btoa(bin.join(""));
656
- }
657
- function isSet(value) {
658
- return value !== null && value !== undefined;
417
+ if (num < globalThis.Number.MIN_SAFE_INTEGER) {
418
+ throw new globalThis.Error("Value is smaller than Number.MIN_SAFE_INTEGER");
419
+ }
420
+ return num;
659
421
  }
@@ -35,4 +35,5 @@ export declare class ServerAbrStream extends EventEmitterLike {
35
35
  private processNextRequestPolicy;
36
36
  private processFormatInitialization;
37
37
  private processSabrRedirect;
38
+ private registerFormat;
38
39
  }
@@ -1,7 +1,6 @@
1
1
  import { UMP } from './UMP.js';
2
2
  import { ChunkedDataBuffer } from './ChunkedDataBuffer.js';
3
3
  import { EventEmitterLike, PART, QUALITY, base64ToU8, getFormatKey } from '../utils/index.js';
4
- import { ClientAbrState_MediaType } from '../../protos/generated/video_streaming/client_abr_state.js';
5
4
  import { VideoPlaybackAbrRequest } from '../../protos/generated/video_streaming/video_playback_abr_request.js';
6
5
  import { MediaHeader } from '../../protos/generated/video_streaming/media_header.js';
7
6
  import { NextRequestPolicy } from '../../protos/generated/video_streaming/next_request_policy.js';
@@ -40,11 +39,11 @@ export class ServerAbrStream extends EventEmitterLike {
40
39
  const clientAbrState = {
41
40
  lastManualDirection: 0,
42
41
  timeSinceLastManualFormatSelectionMs: 0,
43
- quality: videoFormats.length === 1 ? firstVideoFormat?.width : DEFAULT_QUALITY,
44
- selectedQualityHeight: videoFormats.length === 1 ? firstVideoFormat?.width : DEFAULT_QUALITY,
45
- startTimeMs: 0,
42
+ lastManualSelectedResolution: videoFormats.length === 1 ? firstVideoFormat?.height : DEFAULT_QUALITY,
43
+ stickyResolution: videoFormats.length === 1 ? firstVideoFormat?.height : DEFAULT_QUALITY,
44
+ playerTimeMs: 0,
46
45
  visibility: 0,
47
- mediaType: ClientAbrState_MediaType.MEDIA_TYPE_DEFAULT,
46
+ enabledTrackTypesBitfield: 0,
48
47
  ...initialState
49
48
  };
50
49
  const audioFormatIds = audioFormats.map((fmt) => ({
@@ -57,15 +56,15 @@ export class ServerAbrStream extends EventEmitterLike {
57
56
  lastModified: parseInt(fmt.lastModified),
58
57
  xtags: fmt.xtags
59
58
  }));
60
- if (typeof clientAbrState.startTimeMs !== 'number')
59
+ if (typeof clientAbrState.playerTimeMs !== 'number')
61
60
  throw new Error('Invalid media start time');
62
61
  try {
63
- while (clientAbrState.startTimeMs < this.totalDurationMs) {
62
+ while (clientAbrState.playerTimeMs < this.totalDurationMs) {
64
63
  const data = await this.fetchMedia({ clientAbrState, audioFormatIds, videoFormatIds });
65
64
  this.emit('data', data);
66
65
  if (data.sabrError)
67
66
  break;
68
- const mainFormat = clientAbrState.mediaType === ClientAbrState_MediaType.MEDIA_TYPE_DEFAULT
67
+ const mainFormat = clientAbrState.enabledTrackTypesBitfield === 0
69
68
  ? data.initializedFormats.find((fmt) => fmt.mimeType?.includes('video'))
70
69
  : data.initializedFormats[0];
71
70
  for (const fmt of data.initializedFormats) {
@@ -77,11 +76,12 @@ export class ServerAbrStream extends EventEmitterLike {
77
76
  this.emit('end', data);
78
77
  break;
79
78
  }
80
- clientAbrState.startTimeMs += mainFormat.sequenceList.reduce((acc, seq) => acc + (seq.durationMs || 0), 0);
79
+ clientAbrState.playerTimeMs += mainFormat.sequenceList.reduce((acc, seq) => acc + (seq.durationMs || 0), 0);
81
80
  }
82
81
  }
83
82
  catch (error) {
84
83
  this.emit('error', error);
84
+ clientAbrState.playerTimeMs = Infinity;
85
85
  }
86
86
  }
87
87
  async fetchMedia(args) {
@@ -170,27 +170,9 @@ export class ServerAbrStream extends EventEmitterLike {
170
170
  if (!mediaHeader.formatId)
171
171
  return;
172
172
  const formatKey = getFormatKey(mediaHeader.formatId);
173
- let currentFormat = this.formatsByKey.get(formatKey);
174
- if (!currentFormat) {
175
- this.initializedFormats.push({
176
- formatId: mediaHeader.formatId,
177
- formatKey,
178
- durationMs: mediaHeader.durationMs,
179
- mimeType: undefined,
180
- sequenceCount: undefined,
181
- sequenceList: [],
182
- mediaChunks: [],
183
- _state: {
184
- formatId: mediaHeader.formatId,
185
- startTimeMs: 0,
186
- durationMs: 0,
187
- startSegmentIndex: 1,
188
- endSegmentIndex: 0
189
- }
190
- });
191
- this.formatsByKey.set(formatKey, this.initializedFormats[this.initializedFormats.length - 1]);
192
- currentFormat = this.formatsByKey.get(formatKey);
193
- }
173
+ const currentFormat = this.formatsByKey.get(formatKey) || this.registerFormat(mediaHeader);
174
+ if (!currentFormat)
175
+ return;
194
176
  // FIXME: This is a hacky workaround to prevent duplicate sequences from being added. This should be fixed in the future (preferably by figuring out how to make the server not send duplicates).
195
177
  if (mediaHeader.sequenceNumber !== undefined && this.previousSequences.get(formatKey)?.includes(mediaHeader.sequenceNumber))
196
178
  return;
@@ -239,34 +221,39 @@ export class ServerAbrStream extends EventEmitterLike {
239
221
  }
240
222
  processFormatInitialization(data) {
241
223
  const formatInitializationMetadata = FormatInitializationMetadata.decode(data);
242
- if (!formatInitializationMetadata.formatId)
224
+ this.registerFormat(formatInitializationMetadata);
225
+ }
226
+ processSabrRedirect(data) {
227
+ const sabrRedirect = SabrRedirect.decode(data);
228
+ if (!sabrRedirect.url)
229
+ throw new Error('Invalid SABR redirect');
230
+ this.serverAbrStreamingUrl = sabrRedirect.url;
231
+ return sabrRedirect;
232
+ }
233
+ registerFormat(data) {
234
+ if (!data.formatId)
243
235
  return;
244
- const formatKey = getFormatKey(formatInitializationMetadata.formatId);
236
+ const formatKey = getFormatKey(data.formatId);
245
237
  if (!this.formatsByKey.has(formatKey)) {
246
- this.initializedFormats.push({
247
- formatId: formatInitializationMetadata.formatId,
248
- formatKey: getFormatKey(formatInitializationMetadata.formatId),
249
- durationMs: formatInitializationMetadata.durationMs,
250
- mimeType: formatInitializationMetadata.mimeType,
251
- sequenceCount: formatInitializationMetadata.field4,
238
+ const format = {
239
+ formatId: data.formatId,
240
+ formatKey: formatKey,
241
+ durationMs: data.durationMs,
242
+ mimeType: 'mimeType' in data ? data.mimeType : undefined,
243
+ sequenceCount: 'field4' in data ? data.field4 : undefined,
252
244
  sequenceList: [],
253
245
  mediaChunks: [],
254
246
  _state: {
255
- formatId: formatInitializationMetadata.formatId,
247
+ formatId: data.formatId,
256
248
  startTimeMs: 0,
257
249
  durationMs: 0,
258
250
  startSegmentIndex: 1,
259
251
  endSegmentIndex: 0
260
252
  }
261
- });
253
+ };
254
+ this.initializedFormats.push(format);
262
255
  this.formatsByKey.set(formatKey, this.initializedFormats[this.initializedFormats.length - 1]);
256
+ return format;
263
257
  }
264
258
  }
265
- processSabrRedirect(data) {
266
- const sabrRedirect = SabrRedirect.decode(data);
267
- if (!sabrRedirect.url)
268
- throw new Error('Invalid SABR redirect');
269
- this.serverAbrStreamingUrl = sabrRedirect.url;
270
- return sabrRedirect;
271
- }
272
259
  }
@@ -2,9 +2,33 @@ import type { Part } from '../index.js';
2
2
  import type { ChunkedDataBuffer } from './ChunkedDataBuffer.js';
3
3
  export declare class UMP {
4
4
  private chunkedDataBuffer;
5
+ /**
6
+ * Creates a new UMP parser.
7
+ * @param chunkedDataBuffer - Buffer containing UMP format data.
8
+ */
5
9
  constructor(chunkedDataBuffer: ChunkedDataBuffer);
6
- parse(handlePart: (part: Part) => void): void;
10
+ /**
11
+ * Parses parts from the buffer and calls the handler for each complete part.
12
+ * @param handlePart - Function called with each complete part.
13
+ * @returns Partial part if parsing is incomplete, undefined otherwise.
14
+ */
15
+ parse(handlePart: (part: Part) => void): Part | undefined;
16
+ /**
17
+ * Reads a variable-length integer from the buffer.
18
+ * @param offset - Position to start reading from.
19
+ * @returns Tuple of [value, new offset] or [-1, offset] if incomplete.
20
+ */
7
21
  readVarInt(offset: number): [number, number];
22
+ /**
23
+ * Checks if the specified bytes can be read from the current chunk.
24
+ * @param offset - Position to start reading from.
25
+ * @param length - Number of bytes to read.
26
+ * @returns True if bytes can be read from current chunk, false otherwise.
27
+ */
8
28
  canReadFromCurrentChunk(offset: number, length: number): boolean;
29
+ /**
30
+ * Gets a DataView of the current chunk, creating it if necessary.
31
+ * @returns DataView for the current chunk.
32
+ */
9
33
  getCurrentDataView(): DataView;
10
34
  }
@@ -1,7 +1,16 @@
1
1
  export class UMP {
2
+ /**
3
+ * Creates a new UMP parser.
4
+ * @param chunkedDataBuffer - Buffer containing UMP format data.
5
+ */
2
6
  constructor(chunkedDataBuffer) {
3
7
  this.chunkedDataBuffer = chunkedDataBuffer;
4
8
  }
9
+ /**
10
+ * Parses parts from the buffer and calls the handler for each complete part.
11
+ * @param handlePart - Function called with each complete part.
12
+ * @returns Partial part if parsing is incomplete, undefined otherwise.
13
+ */
5
14
  parse(handlePart) {
6
15
  while (true) {
7
16
  let offset = 0;
@@ -11,9 +20,15 @@ export class UMP {
11
20
  offset = finalOffset;
12
21
  if (partType < 0 || partSize < 0)
13
22
  break;
14
- // Note that we don't handle cases like this YET..
15
- if (!this.chunkedDataBuffer.canReadBytes(offset, partSize))
16
- break;
23
+ if (!this.chunkedDataBuffer.canReadBytes(offset, partSize)) {
24
+ if (!this.chunkedDataBuffer.canReadBytes(offset, 1))
25
+ break;
26
+ return {
27
+ type: partType,
28
+ size: partSize,
29
+ data: this.chunkedDataBuffer
30
+ };
31
+ }
17
32
  const splitResult = this.chunkedDataBuffer.split(offset).remainingBuffer.split(partSize);
18
33
  offset = 0;
19
34
  handlePart({
@@ -24,26 +39,17 @@ export class UMP {
24
39
  this.chunkedDataBuffer = splitResult.remainingBuffer;
25
40
  }
26
41
  }
42
+ /**
43
+ * Reads a variable-length integer from the buffer.
44
+ * @param offset - Position to start reading from.
45
+ * @returns Tuple of [value, new offset] or [-1, offset] if incomplete.
46
+ */
27
47
  readVarInt(offset) {
28
48
  let byteLength;
49
+ // Determine the length of the val
29
50
  if (this.chunkedDataBuffer.canReadBytes(offset, 1)) {
30
51
  const firstByte = this.chunkedDataBuffer.getUint8(offset);
31
- // Determine the length of the val
32
- if (firstByte < 128) {
33
- byteLength = 1;
34
- }
35
- else if (firstByte < 192) {
36
- byteLength = 2;
37
- }
38
- else if (firstByte < 224) {
39
- byteLength = 3;
40
- }
41
- else if (firstByte < 240) {
42
- byteLength = 4;
43
- }
44
- else {
45
- byteLength = 5;
46
- }
52
+ byteLength = firstByte < 128 ? 1 : firstByte < 192 ? 2 : firstByte < 224 ? 3 : firstByte < 240 ? 4 : 5;
47
53
  }
48
54
  else {
49
55
  byteLength = 0;
@@ -96,10 +102,19 @@ export class UMP {
96
102
  }
97
103
  return [value, offset];
98
104
  }
105
+ /**
106
+ * Checks if the specified bytes can be read from the current chunk.
107
+ * @param offset - Position to start reading from.
108
+ * @param length - Number of bytes to read.
109
+ * @returns True if bytes can be read from current chunk, false otherwise.
110
+ */
99
111
  canReadFromCurrentChunk(offset, length) {
100
- this.chunkedDataBuffer.isFocused(offset);
101
112
  return offset - this.chunkedDataBuffer.currentChunkOffset + length <= this.chunkedDataBuffer.chunks[this.chunkedDataBuffer.currentChunkIndex].length;
102
113
  }
114
+ /**
115
+ * Gets a DataView of the current chunk, creating it if necessary.
116
+ * @returns DataView for the current chunk.
117
+ */
103
118
  getCurrentDataView() {
104
119
  if (!this.chunkedDataBuffer.currentDataView) {
105
120
  const currentChunk = this.chunkedDataBuffer.chunks[this.chunkedDataBuffer.currentChunkIndex];
@@ -14,10 +14,11 @@ export { EncryptedPlayerRequest } from '../../protos/generated/video_streaming/e
14
14
  export { OnesieHeader } from '../../protos/generated/video_streaming/onesie_header.js';
15
15
  export { OnesieHeaderType } from '../../protos/generated/video_streaming/onesie_header_type.js';
16
16
  export { OnesiePlayerRequest } from '../../protos/generated/video_streaming/onesie_player_request.js';
17
- export { OnesiePlayerResponse } from '../../protos/generated/video_streaming/onesie_player_response.js';
17
+ export { OnesiePlayerResponse, OnesieProxyStatus } from '../../protos/generated/video_streaming/onesie_player_response.js';
18
18
  export { ClientAbrState } from '../../protos/generated/video_streaming/client_abr_state.js';
19
19
  export { StreamerContext } from '../../protos/generated/video_streaming/streamer_context.js';
20
20
  export { ProxyStatus } from '../../protos/generated/video_streaming/proxy_status.js';
21
21
  export { MediaCapabilities } from '../../protos/generated/video_streaming/media_capabilities.js';
22
22
  export { CryptoParams } from '../../protos/generated/video_streaming/crypto_params.js';
23
- export { ClientAbrState_MediaType as MediaType } from '../../protos/generated/video_streaming/client_abr_state.js';
23
+ export { LiveMetadata } from '../../protos/generated/video_streaming/live_metadata.js';
24
+ export { FormatId, KeyValuePair, InitRange, IndexRange, HttpHeader } from '../../protos/generated/misc/common.js';
@@ -14,10 +14,11 @@ export { EncryptedPlayerRequest } from '../../protos/generated/video_streaming/e
14
14
  export { OnesieHeader } from '../../protos/generated/video_streaming/onesie_header.js';
15
15
  export { OnesieHeaderType } from '../../protos/generated/video_streaming/onesie_header_type.js';
16
16
  export { OnesiePlayerRequest } from '../../protos/generated/video_streaming/onesie_player_request.js';
17
- export { OnesiePlayerResponse } from '../../protos/generated/video_streaming/onesie_player_response.js';
17
+ export { OnesiePlayerResponse, OnesieProxyStatus } from '../../protos/generated/video_streaming/onesie_player_response.js';
18
18
  export { ClientAbrState } from '../../protos/generated/video_streaming/client_abr_state.js';
19
19
  export { StreamerContext } from '../../protos/generated/video_streaming/streamer_context.js';
20
20
  export { ProxyStatus } from '../../protos/generated/video_streaming/proxy_status.js';
21
21
  export { MediaCapabilities } from '../../protos/generated/video_streaming/media_capabilities.js';
22
22
  export { CryptoParams } from '../../protos/generated/video_streaming/crypto_params.js';
23
- export { ClientAbrState_MediaType as MediaType } from '../../protos/generated/video_streaming/client_abr_state.js';
23
+ export { LiveMetadata } from '../../protos/generated/video_streaming/live_metadata.js';
24
+ export { FormatId, KeyValuePair, InitRange, IndexRange, HttpHeader } from '../../protos/generated/misc/common.js';