shaka-player 4.7.13 → 4.8.1

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 (114) hide show
  1. package/AUTHORS +2 -0
  2. package/CONTRIBUTORS +2 -0
  3. package/LICENSE +54 -0
  4. package/README.md +22 -1
  5. package/dist/controls.css +1 -1
  6. package/dist/controls.css.map +1 -1
  7. package/dist/deps.js +34 -23
  8. package/dist/locales.js +41 -31
  9. package/dist/shaka-player.compiled.d.ts +196 -45
  10. package/dist/shaka-player.compiled.debug.d.ts +196 -45
  11. package/dist/shaka-player.compiled.debug.externs.js +491 -348
  12. package/dist/shaka-player.compiled.debug.js +969 -847
  13. package/dist/shaka-player.compiled.debug.map +4 -4
  14. package/dist/shaka-player.compiled.externs.js +491 -348
  15. package/dist/shaka-player.compiled.js +1238 -1171
  16. package/dist/shaka-player.compiled.map +4 -4
  17. package/dist/shaka-player.ui.d.ts +259 -48
  18. package/dist/shaka-player.ui.debug.d.ts +259 -48
  19. package/dist/shaka-player.ui.debug.externs.js +598 -377
  20. package/dist/shaka-player.ui.debug.js +1185 -980
  21. package/dist/shaka-player.ui.debug.map +4 -4
  22. package/dist/shaka-player.ui.externs.js +598 -377
  23. package/dist/shaka-player.ui.js +1526 -1408
  24. package/dist/shaka-player.ui.map +4 -4
  25. package/externs/device_sensor_event.js +18 -0
  26. package/externs/hisense.js +16 -0
  27. package/externs/playstation.js +31 -0
  28. package/externs/shaka/abr_manager.js +3 -1
  29. package/externs/shaka/ads.js +5 -0
  30. package/externs/shaka/codecs.js +13 -0
  31. package/externs/shaka/manifest.js +58 -6
  32. package/externs/shaka/net.js +13 -1
  33. package/externs/shaka/player.js +184 -13
  34. package/externs/shaka/text.js +19 -0
  35. package/externs/tizen.js +41 -0
  36. package/externs/webos.js +19 -0
  37. package/externs/xbox.js +43 -0
  38. package/lib/abr/simple_abr_manager.js +9 -1
  39. package/lib/ads/ad_manager.js +19 -2
  40. package/lib/ads/client_side_ad_manager.js +5 -3
  41. package/lib/ads/media_tailor_ad_manager.js +9 -0
  42. package/lib/cast/cast_receiver.js +0 -14
  43. package/lib/cast/cast_utils.js +1 -0
  44. package/lib/cea/mp4_cea_parser.js +6 -1
  45. package/lib/dash/content_protection.js +97 -53
  46. package/lib/dash/dash_parser.js +237 -177
  47. package/lib/dash/mpd_utils.js +72 -65
  48. package/lib/dash/segment_base.js +27 -21
  49. package/lib/dash/segment_list.js +18 -16
  50. package/lib/dash/segment_template.js +45 -22
  51. package/lib/hls/hls_parser.js +358 -164
  52. package/lib/media/buffering_observer.js +25 -1
  53. package/lib/media/drm_engine.js +91 -71
  54. package/lib/media/manifest_filterer.js +220 -0
  55. package/lib/media/media_source_engine.js +34 -5
  56. package/lib/media/preload_manager.js +827 -0
  57. package/lib/media/presentation_timeline.js +11 -0
  58. package/lib/media/segment_index.js +93 -47
  59. package/lib/media/segment_prefetch.js +140 -43
  60. package/lib/media/segment_reference.js +43 -8
  61. package/lib/media/segment_utils.js +208 -25
  62. package/lib/media/streaming_engine.js +355 -66
  63. package/lib/mss/content_protection.js +31 -25
  64. package/lib/mss/mss_parser.js +79 -55
  65. package/lib/net/networking_engine.js +49 -4
  66. package/lib/offline/storage.js +3 -3
  67. package/lib/player.js +882 -672
  68. package/lib/polyfill/media_capabilities.js +16 -1
  69. package/lib/text/simple_text_displayer.js +9 -0
  70. package/lib/text/stub_text_displayer.js +7 -0
  71. package/lib/text/text_engine.js +16 -3
  72. package/lib/text/ttml_text_parser.js +116 -114
  73. package/lib/text/ui_text_displayer.js +28 -11
  74. package/lib/text/vtt_text_parser.js +54 -115
  75. package/lib/transmuxer/opus.js +91 -0
  76. package/lib/transmuxer/ts_transmuxer.js +115 -0
  77. package/lib/util/cmcd_manager.js +153 -3
  78. package/lib/util/dom_utils.js +11 -0
  79. package/lib/util/error.js +5 -0
  80. package/lib/util/fake_event.js +2 -0
  81. package/lib/util/manifest_parser_utils.js +97 -3
  82. package/lib/util/mp4_box_parsers.js +346 -17
  83. package/lib/util/mp4_generator.js +23 -0
  84. package/lib/util/periods.js +19 -0
  85. package/lib/util/platform.js +161 -9
  86. package/lib/util/player_configuration.js +79 -13
  87. package/lib/util/stats.js +16 -0
  88. package/lib/util/stream_utils.js +22 -8
  89. package/lib/util/string_utils.js +36 -0
  90. package/lib/util/tXml.js +814 -0
  91. package/lib/util/ts_parser.js +50 -0
  92. package/lib/util/xml_utils.js +10 -338
  93. package/package.json +2 -2
  94. package/shaka-player.uncompiled.js +8 -0
  95. package/ui/controls.js +187 -5
  96. package/ui/enums.js +2 -0
  97. package/ui/externs/ui.js +30 -3
  98. package/ui/gl_matrix/matrix_4x4.js +568 -0
  99. package/ui/gl_matrix/matrix_quaternion.js +45 -0
  100. package/ui/less/containers.less +11 -0
  101. package/ui/locales/en-GB.json +2 -0
  102. package/ui/locales/en.json +2 -0
  103. package/ui/locales/es-419.json +2 -0
  104. package/ui/locales/es.json +2 -0
  105. package/ui/locales/source.json +8 -0
  106. package/ui/recenter_vr.js +126 -0
  107. package/ui/resolution_selection.js +25 -5
  108. package/ui/statistics_button.js +12 -0
  109. package/ui/text_selection.js +1 -1
  110. package/ui/toggle_stereoscopic.js +127 -0
  111. package/ui/ui.js +74 -38
  112. package/ui/vr_manager.js +557 -0
  113. package/ui/vr_utils.js +232 -0
  114. package/ui/vr_webgl.js +697 -0
@@ -53,6 +53,17 @@ shaka.util.Dom = class {
53
53
  }
54
54
 
55
55
 
56
+ /**
57
+ * Cast a Node/Element to an HTMLCanvasElement
58
+ *
59
+ * @param {!Node|!Element} original
60
+ * @return {!HTMLCanvasElement}
61
+ */
62
+ static asHTMLCanvasElement(original) {
63
+ return /** @type {!HTMLCanvasElement}*/ (original);
64
+ }
65
+
66
+
56
67
  /**
57
68
  * Cast a Node/Element to an HTMLMediaElement
58
69
  *
package/lib/util/error.js CHANGED
@@ -946,6 +946,11 @@ shaka.util.Error.Code = {
946
946
  */
947
947
  'CONTENT_NOT_LOADED': 7004,
948
948
 
949
+ /**
950
+ * The call to preload failed, due to being called on src= content.
951
+ */
952
+ 'SRC_EQUALS_PRELOAD_NOT_SUPPORTED': 7005,
953
+
949
954
  /**
950
955
  * The Cast API is unavailable. This may be because of one of the following:
951
956
  * 1. The browser may not have Cast support
@@ -174,10 +174,12 @@ shaka.util.FakeEvent.EventName = {
174
174
  MediaQualityChanged: 'mediaqualitychanged',
175
175
  Metadata: 'metadata',
176
176
  Midpoint: 'midpoint',
177
+ NoSpatialVideoInfoEvent: 'nospatialvideoinfo',
177
178
  OnStateChange: 'onstatechange',
178
179
  RateChange: 'ratechange',
179
180
  SegmentAppended: 'segmentappended',
180
181
  SessionDataEvent: 'sessiondata',
182
+ SpatialVideoInfoEvent: 'spatialvideoinfo',
181
183
  StallDetected: 'stalldetected',
182
184
  Started: 'started',
183
185
  StateChanged: 'statechanged',
@@ -7,7 +7,10 @@
7
7
  goog.provide('shaka.util.ManifestParserUtils');
8
8
 
9
9
  goog.require('goog.Uri');
10
+ goog.require('shaka.util.BufferUtils');
10
11
  goog.require('shaka.util.Error');
12
+ goog.require('shaka.util.StringUtils');
13
+ goog.require('shaka.util.Uint8ArrayUtils');
11
14
 
12
15
 
13
16
  /**
@@ -56,12 +59,14 @@ shaka.util.ManifestParserUtils = class {
56
59
  * Creates a DrmInfo object from the given info.
57
60
  *
58
61
  * @param {string} keySystem
62
+ * @param {string} encryptionScheme
59
63
  * @param {Array.<shaka.extern.InitDataOverride>} initData
60
64
  * @return {shaka.extern.DrmInfo}
61
65
  */
62
- static createDrmInfo(keySystem, initData) {
66
+ static createDrmInfo(keySystem, encryptionScheme, initData) {
63
67
  return {
64
- keySystem: keySystem,
68
+ keySystem,
69
+ encryptionScheme,
65
70
  licenseServerUri: '',
66
71
  distinctiveIdentifierRequired: false,
67
72
  persistentStateRequired: false,
@@ -75,6 +80,69 @@ shaka.util.ManifestParserUtils = class {
75
80
  };
76
81
  }
77
82
 
83
+ /**
84
+ * Creates a DrmInfo object from ClearKeys.
85
+ *
86
+ * @param {!Map.<string, string>} clearKeys
87
+ * @param {string=} encryptionScheme
88
+ * @return {shaka.extern.DrmInfo}
89
+ */
90
+ static createDrmInfoFromClearKeys(clearKeys, encryptionScheme = 'cenc') {
91
+ const StringUtils = shaka.util.StringUtils;
92
+ const Uint8ArrayUtils = shaka.util.Uint8ArrayUtils;
93
+ const keys = [];
94
+ const keyIds = [];
95
+ const originalKeyIds = [];
96
+
97
+ clearKeys.forEach((key, keyId) => {
98
+ let kid = keyId;
99
+ if (kid.length != 22) {
100
+ kid = Uint8ArrayUtils.toBase64(
101
+ Uint8ArrayUtils.fromHex(keyId), false);
102
+ }
103
+ let k = key;
104
+ if (k.length != 22) {
105
+ k = Uint8ArrayUtils.toBase64(
106
+ Uint8ArrayUtils.fromHex(key), false);
107
+ }
108
+ const keyObj = {
109
+ kty: 'oct',
110
+ kid: kid,
111
+ k: k,
112
+ };
113
+
114
+ keys.push(keyObj);
115
+ keyIds.push(keyObj.kid);
116
+ originalKeyIds.push(keyId);
117
+ });
118
+
119
+ const jwkSet = {keys: keys};
120
+ const license = JSON.stringify(jwkSet);
121
+
122
+ // Use the keyids init data since is suggested by EME.
123
+ // Suggestion: https://bit.ly/2JYcNTu
124
+ // Format: https://www.w3.org/TR/eme-initdata-keyids/
125
+ const initDataStr = JSON.stringify({'kids': keyIds});
126
+ const initData =
127
+ shaka.util.BufferUtils.toUint8(StringUtils.toUTF8(initDataStr));
128
+ const initDatas = [{initData: initData, initDataType: 'keyids'}];
129
+
130
+ return {
131
+ keySystem: 'org.w3.clearkey',
132
+ encryptionScheme,
133
+ licenseServerUri: 'data:application/json;base64,' + window.btoa(license),
134
+ distinctiveIdentifierRequired: false,
135
+ persistentStateRequired: false,
136
+ audioRobustness: '',
137
+ videoRobustness: '',
138
+ serverCertificate: null,
139
+ serverCertificateUri: '',
140
+ sessionType: '',
141
+ initData: initDatas,
142
+ keyIds: new Set(originalKeyIds),
143
+ };
144
+ }
145
+
78
146
 
79
147
  /**
80
148
  * Attempts to guess which codecs from the codecs list belong to a given
@@ -134,6 +202,30 @@ shaka.util.ManifestParserUtils = class {
134
202
 
135
203
  return null;
136
204
  }
205
+
206
+
207
+ /**
208
+ * Attempts to guess which codecs from the codecs list belong to a given
209
+ * content.
210
+ *
211
+ * @param {string} contentType
212
+ * @param {!Array.<string>} codecs
213
+ * @return {!Array.<string>}
214
+ */
215
+ static guessAllCodecsSafe(contentType, codecs) {
216
+ const allCodecs = [];
217
+ const formats = shaka.util.ManifestParserUtils
218
+ .CODEC_REGEXPS_BY_CONTENT_TYPE_[contentType];
219
+ for (const format of formats) {
220
+ for (const codec of codecs) {
221
+ if (format.test(codec.trim())) {
222
+ allCodecs.push(codec.trim());
223
+ }
224
+ }
225
+ }
226
+
227
+ return allCodecs;
228
+ }
137
229
  };
138
230
 
139
231
 
@@ -183,7 +275,9 @@ shaka.util.ManifestParserUtils.VIDEO_CODEC_REGEXPS_ = [
183
275
  /^vvi/,
184
276
  /^vp0?[89]/,
185
277
  /^av01/,
186
- /^dvh/,
278
+ /^dvh/, // Dolby Vision based in HEVC
279
+ /^dva/, // Dolby Vision based in AVC
280
+ /^dav/, // Dolby Vision based in AV1
187
281
  ];
188
282
 
189
283
 
@@ -8,6 +8,7 @@ goog.provide('shaka.util.Mp4BoxParsers');
8
8
 
9
9
  goog.require('shaka.util.DataViewReader');
10
10
  goog.require('shaka.util.Mp4Parser');
11
+ goog.require('shaka.util.Uint8ArrayUtils');
11
12
 
12
13
  shaka.util.Mp4BoxParsers = class {
13
14
  /**
@@ -329,7 +330,7 @@ shaka.util.Mp4BoxParsers = class {
329
330
  }
330
331
  }
331
332
  if (oti) {
332
- codec += '.' + this.toHex_(oti);
333
+ codec += '.' + shaka.util.Mp4BoxParsers.toHex_(oti);
333
334
  if (tag == 0x05 && reader.hasMoreData()) {
334
335
  const firstData = reader.readUint8();
335
336
  let audioObjectType = (firstData & 0xF8) >> 3;
@@ -344,15 +345,146 @@ shaka.util.Mp4BoxParsers = class {
344
345
  }
345
346
 
346
347
  /**
347
- * Parses a AVC box.
348
+ * Parses a AVCC box.
349
+ * @param {string} codecBase
348
350
  * @param {!shaka.util.DataViewReader} reader
349
351
  * @param {string} boxName
350
- * @return {!shaka.util.ParsedAVCBox}
352
+ * @return {!shaka.util.ParsedAVCCBox}
351
353
  */
352
- static parseAVC(reader, boxName) {
353
- reader.skip(87);
354
- const codec = boxName + '.' + this.toHex_(reader.readUint8()) +
355
- this.toHex_(reader.readUint8()) + this.toHex_(reader.readUint8());
354
+ static parseAVCC(codecBase, reader, boxName) {
355
+ const Mp4BoxParsers = shaka.util.Mp4BoxParsers;
356
+ reader.skip(1); // Skip "configurationVersion"
357
+ const codec = codecBase + '.' +
358
+ Mp4BoxParsers.toHex_(reader.readUint8()) +
359
+ Mp4BoxParsers.toHex_(reader.readUint8()) +
360
+ Mp4BoxParsers.toHex_(reader.readUint8());
361
+ return {codec};
362
+ }
363
+
364
+ /**
365
+ * Parses a HVCC box.
366
+ * @param {string} codecBase
367
+ * @param {!shaka.util.DataViewReader} reader
368
+ * @param {string} boxName
369
+ * @return {!shaka.util.ParsedHVCCBox}
370
+ */
371
+ static parseHVCC(codecBase, reader, boxName) {
372
+ const Mp4BoxParsers = shaka.util.Mp4BoxParsers;
373
+ reader.skip(1); // Skip "configurationVersion"
374
+ const profileByte = reader.readUint8();
375
+ const profileSpace = ['', 'A', 'B', 'C'][profileByte >> 6];
376
+ const generalProfileIdc = profileByte & 0x1f;
377
+ const profileCompat = reader.readUint32();
378
+ const tierFlag = (profileByte & 0x20) >> 5 ? 'H' : 'L';
379
+ const constraintIndicator = [
380
+ reader.readUint8(),
381
+ reader.readUint8(),
382
+ reader.readUint8(),
383
+ reader.readUint8(),
384
+ reader.readUint8(),
385
+ reader.readUint8(),
386
+ ];
387
+ const levelIDC = reader.readUint8();
388
+ let codec = codecBase;
389
+ codec += '.' + profileSpace + generalProfileIdc;
390
+ codec += '.' + Mp4BoxParsers.toHex_(
391
+ Mp4BoxParsers.reverseBits_(profileCompat),
392
+ /* removeInitialZero= */ true);
393
+ codec += '.' + tierFlag + levelIDC;
394
+ let constraintString = '';
395
+ for (let i = constraintIndicator.length; i--; ) {
396
+ const byte = constraintIndicator[i];
397
+ if (byte || constraintString) {
398
+ const encodedByte = byte.toString(16).toUpperCase();
399
+ constraintString = '.' + encodedByte + constraintString;
400
+ }
401
+ }
402
+ codec += constraintString;
403
+
404
+ return {codec};
405
+ }
406
+
407
+ /**
408
+ * Parses a DVCC box.
409
+ * @param {string} codecBase
410
+ * @param {!shaka.util.DataViewReader} reader
411
+ * @param {string} boxName
412
+ * @return {!shaka.util.ParsedDVCCBox}
413
+ */
414
+ static parseDVCC(codecBase, reader, boxName) {
415
+ const Mp4BoxParsers = shaka.util.Mp4BoxParsers;
416
+ reader.skip(2); // Skip "dv_version_major" and "dv_version_minor"
417
+ const thirdByte = reader.readUint8();
418
+ const fourthByte = reader.readUint8();
419
+ const profile = (thirdByte >> 1) & 0x7f;
420
+ const level = ((thirdByte << 5) & 0x20) | ((fourthByte >> 3) & 0x1f);
421
+ const codec = codecBase + '.' +
422
+ Mp4BoxParsers.addLeadingZero_(profile) + '.' +
423
+ Mp4BoxParsers.addLeadingZero_(level);
424
+
425
+ return {codec};
426
+ }
427
+
428
+ /**
429
+ * Parses a VPCC box.
430
+ * @param {string} codecBase
431
+ * @param {!shaka.util.DataViewReader} reader
432
+ * @param {string} boxName
433
+ * @return {!shaka.util.ParsedVPCCBox}
434
+ */
435
+ static parseVPCC(codecBase, reader, boxName) {
436
+ const Mp4BoxParsers = shaka.util.Mp4BoxParsers;
437
+ const profile = reader.readUint8();
438
+ const level = reader.readUint8();
439
+ const bitDepth = (reader.readUint8() >> 4) & 0x0f;
440
+ const codec = codecBase + '.' +
441
+ Mp4BoxParsers.addLeadingZero_(profile) + '.' +
442
+ Mp4BoxParsers.addLeadingZero_(level) + '.' +
443
+ Mp4BoxParsers.addLeadingZero_(bitDepth);
444
+
445
+ return {codec};
446
+ }
447
+
448
+ /**
449
+ * Parses a AV1C box.
450
+ * @param {string} codecBase
451
+ * @param {!shaka.util.DataViewReader} reader
452
+ * @param {string} boxName
453
+ * @return {!shaka.util.ParsedAV1CBox}
454
+ */
455
+ static parseAV1C(codecBase, reader, boxName) {
456
+ const Mp4BoxParsers = shaka.util.Mp4BoxParsers;
457
+ // More info https://aomediacodec.github.io/av1-isobmff/#av1codecconfigurationbox-syntax
458
+ reader.skip(1); // Skip "marker" and "version"
459
+ const secondByte = reader.readUint8();
460
+ const thirdByte = reader.readUint8();
461
+ const profile = secondByte >>> 5;
462
+ const level = secondByte & 0x1f;
463
+ const tierFlag = thirdByte >>> 7 ? 'H' : 'M';
464
+ const highBitDepth = (thirdByte & 0x40) >> 6;
465
+ const twelveBit = (thirdByte & 0x20) >> 5;
466
+ const bitDepth = profile === 2 && highBitDepth ?
467
+ (twelveBit ? 12 : 10) : (highBitDepth ? 10 : 8);
468
+ const monochrome = (thirdByte & 0x10) >> 4;
469
+ const chromaSubsamplingX = (thirdByte & 0x08) >> 3;
470
+ const chromaSubsamplingY = (thirdByte & 0x04) >> 2;
471
+ const chromaSamplePosition = thirdByte & 0x03;
472
+ // TODO: parse color_description_present_flag
473
+ // default it to BT.709/limited range for now
474
+ const colorPrimaries = 1;
475
+ const transferCharacteristics = 1;
476
+ const matrixCoefficients = 1;
477
+ const videoFullRangeFlag = 0;
478
+ const codec = codecBase + '.' + profile +
479
+ '.' + Mp4BoxParsers.addLeadingZero_(level) + tierFlag +
480
+ '.' + Mp4BoxParsers.addLeadingZero_(bitDepth) +
481
+ '.' + monochrome +
482
+ '.' + chromaSubsamplingX + chromaSubsamplingY + chromaSamplePosition +
483
+ '.' + Mp4BoxParsers.addLeadingZero_(colorPrimaries) +
484
+ '.' + Mp4BoxParsers.addLeadingZero_(transferCharacteristics) +
485
+ '.' + Mp4BoxParsers.addLeadingZero_(matrixCoefficients) +
486
+ '.' + videoFullRangeFlag;
487
+
356
488
  return {codec};
357
489
  }
358
490
 
@@ -367,6 +499,21 @@ shaka.util.Mp4BoxParsers = class {
367
499
  return {codec};
368
500
  }
369
501
 
502
+ /**
503
+ * Parses a TENC box.
504
+ * @param {!shaka.util.DataViewReader} reader
505
+ * @return {!shaka.util.ParsedTENCBox}
506
+ */
507
+ static parseTENC(reader) {
508
+ const Uint8ArrayUtils = shaka.util.Uint8ArrayUtils;
509
+ reader.readUint8(); // reserved
510
+ reader.readUint8();
511
+ reader.readUint8(); // default_isProtected
512
+ reader.readUint8(); // default_Per_Sample_IV_Size
513
+ const defaultKID = Uint8ArrayUtils.toHex(reader.readBytes(16));
514
+ return {defaultKID};
515
+ }
516
+
370
517
  /**
371
518
  * Parses a HDLR box.
372
519
  * @param {!shaka.util.DataViewReader} reader
@@ -375,24 +522,112 @@ shaka.util.Mp4BoxParsers = class {
375
522
  static parseHDLR(reader) {
376
523
  reader.skip(8); // Skip "pre_defined"
377
524
 
378
- const data = reader.readBytes(4);
379
- let handlerType = '';
380
- handlerType += String.fromCharCode(data[0]);
381
- handlerType += String.fromCharCode(data[1]);
382
- handlerType += String.fromCharCode(data[2]);
383
- handlerType += String.fromCharCode(data[3]);
384
-
525
+ const handlerType = reader.readTerminatedString();
385
526
  return {handlerType};
386
527
  }
387
528
 
529
+ /**
530
+ * Parses a PRJI box.
531
+ * @param {!shaka.util.DataViewReader} reader
532
+ * @return {!shaka.util.ParsedPRJIBox}
533
+ */
534
+ static parsePRJI(reader) {
535
+ const projection = reader.readTerminatedString();
536
+ return {projection};
537
+ }
538
+
539
+ /**
540
+ * Parses a HFOV box.
541
+ * @param {!shaka.util.DataViewReader} reader
542
+ * @return {!shaka.util.ParsedHFOVBox}
543
+ */
544
+ static parseHFOV(reader) {
545
+ const millidegrees = reader.readUint32();
546
+
547
+ return {
548
+ hfov: millidegrees / 1000,
549
+ };
550
+ }
551
+
552
+ /**
553
+ * Parses a COLR box.
554
+ * @param {!shaka.util.DataViewReader} reader
555
+ * @return {!shaka.util.ParsedCOLRBox}
556
+ */
557
+ static parseCOLR(reader) {
558
+ let videoRange = null;
559
+ const data = reader.readBytes(4);
560
+ let colorType = '';
561
+ colorType += String.fromCharCode(data[0]);
562
+ colorType += String.fromCharCode(data[1]);
563
+ colorType += String.fromCharCode(data[2]);
564
+ colorType += String.fromCharCode(data[3]);
565
+ if (colorType === 'nclx') {
566
+ reader.readUint16(); // colour_primaries
567
+ const transferCharacteristics = reader.readUint16();
568
+ reader.readUint16(); // matrix_coefficients
569
+ switch (transferCharacteristics) {
570
+ case 1:
571
+ case 6:
572
+ case 13:
573
+ case 14:
574
+ case 15:
575
+ videoRange = 'SDR';
576
+ break;
577
+ case 16:
578
+ videoRange = 'PQ';
579
+ break;
580
+ case 18:
581
+ videoRange = 'HLG';
582
+ break;
583
+ }
584
+ }
585
+ return {videoRange};
586
+ }
587
+
388
588
  /**
389
589
  * Convert a number to hex
390
590
  * @param {number} x
591
+ * @param {boolean=} removeInitialZero
592
+ * @return {string}
593
+ * @private
594
+ */
595
+ static toHex_(x, removeInitialZero = false) {
596
+ const value = x.toString(16).toUpperCase();
597
+ if (removeInitialZero) {
598
+ return value;
599
+ }
600
+ return ('0' + value).slice(-2);
601
+ }
602
+
603
+ /**
604
+ * Reverse a number bit to bit
605
+ * @param {number} x
606
+ * @return {number}
607
+ * @private
608
+ */
609
+ static reverseBits_(x) {
610
+ let val = x;
611
+ let reversed = 0;
612
+ for (let i = 0; i < 32; i++) {
613
+ reversed |= val & 1;
614
+ if (i == 31) {
615
+ break;
616
+ }
617
+ reversed <<= 1;
618
+ val >>= 1;
619
+ }
620
+ return reversed;
621
+ }
622
+
623
+ /**
624
+ *
625
+ * @param {number} x
391
626
  * @return {string}
392
627
  * @private
393
628
  */
394
- static toHex_(x) {
395
- return ('0' + x.toString(16).toUpperCase()).slice(-2);
629
+ static addLeadingZero_(x) {
630
+ return (x < 10 ? '0' : '') + x;
396
631
  }
397
632
  };
398
633
 
@@ -544,6 +779,17 @@ shaka.util.ParsedTKHDBox;
544
779
  */
545
780
  shaka.util.ParsedFRMABox;
546
781
 
782
+ /**
783
+ * @typedef {{
784
+ * defaultKID: string
785
+ * }}
786
+ *
787
+ * @property {string} defaultKID
788
+ *
789
+ * @exportDoc
790
+ */
791
+ shaka.util.ParsedTENCBox;
792
+
547
793
  /**
548
794
  * @typedef {{
549
795
  * channelCount: number,
@@ -577,7 +823,51 @@ shaka.util.ParsedESDSBox;
577
823
  *
578
824
  * @exportDoc
579
825
  */
580
- shaka.util.ParsedAVCBox;
826
+ shaka.util.ParsedAVCCBox;
827
+
828
+ /**
829
+ * @typedef {{
830
+ * codec: string
831
+ * }}
832
+ *
833
+ * @property {string} codec
834
+ *
835
+ * @exportDoc
836
+ */
837
+ shaka.util.ParsedHVCCBox;
838
+
839
+ /**
840
+ * @typedef {{
841
+ * codec: string
842
+ * }}
843
+ *
844
+ * @property {string} codec
845
+ *
846
+ * @exportDoc
847
+ */
848
+ shaka.util.ParsedDVCCBox;
849
+
850
+ /**
851
+ * @typedef {{
852
+ * codec: string
853
+ * }}
854
+ *
855
+ * @property {string} codec
856
+ *
857
+ * @exportDoc
858
+ */
859
+ shaka.util.ParsedVPCCBox;
860
+
861
+ /**
862
+ * @typedef {{
863
+ * codec: string
864
+ * }}
865
+ *
866
+ * @property {string} codec
867
+ *
868
+ * @exportDoc
869
+ */
870
+ shaka.util.ParsedAV1CBox;
581
871
 
582
872
  /**
583
873
  * @typedef {{
@@ -592,3 +882,42 @@ shaka.util.ParsedAVCBox;
592
882
  * @exportDoc
593
883
  */
594
884
  shaka.util.ParsedHDLRBox;
885
+
886
+ /**
887
+ * @typedef {{
888
+ * projection: string
889
+ * }}
890
+ *
891
+ * @property {string} projection
892
+ * A four-character code that identifies the type of the projection.
893
+ * Possible values:
894
+ * - Rectangular: ‘rect’
895
+ * - Half equirectangular: ‘hequ’
896
+ * - Equirectanguler: ?
897
+ * - Fisheye: ‘fish’
898
+ *
899
+ * @exportDoc
900
+ */
901
+ shaka.util.ParsedPRJIBox;
902
+
903
+ /**
904
+ * @typedef {{
905
+ * hfov: number
906
+ * }}
907
+ *
908
+ * @property {number} hfov
909
+ *
910
+ * @exportDoc
911
+ */
912
+ shaka.util.ParsedHFOVBox;
913
+
914
+ /**
915
+ * @typedef {{
916
+ * videoRange: ?string
917
+ * }}
918
+ *
919
+ * @property {?string} videoRange
920
+ *
921
+ * @exportDoc
922
+ */
923
+ shaka.util.ParsedCOLRBox;
@@ -314,6 +314,8 @@ shaka.util.Mp4Generator = class {
314
314
  bytes = this.ac3_(streamInfo);
315
315
  } else if (streamInfo.codecs.includes('ec-3')) {
316
316
  bytes = this.ec3_(streamInfo);
317
+ } else if (streamInfo.codecs.includes('opus')) {
318
+ bytes = this.opus_(streamInfo);
317
319
  } else {
318
320
  bytes = this.mp4a_(streamInfo);
319
321
  }
@@ -611,6 +613,27 @@ shaka.util.Mp4Generator = class {
611
613
  this.audioStsd_(streamInfo), dec3Box, sinfBox);
612
614
  }
613
615
 
616
+ /**
617
+ * Generate a Opus box
618
+ *
619
+ * @param {shaka.util.Mp4Generator.StreamInfo} streamInfo
620
+ * @return {!Uint8Array}
621
+ * @private
622
+ */
623
+ opus_(streamInfo) {
624
+ const Mp4Generator = shaka.util.Mp4Generator;
625
+ const dopsBox = Mp4Generator.box('dOps', streamInfo.audioConfig);
626
+
627
+ let boxName = 'Opus';
628
+ let sinfBox = new Uint8Array([]);
629
+ if (streamInfo.encrypted) {
630
+ sinfBox = this.sinf_(streamInfo);
631
+ boxName = 'enca';
632
+ }
633
+ return Mp4Generator.box(boxName,
634
+ this.audioStsd_(streamInfo), dopsBox, sinfBox);
635
+ }
636
+
614
637
  /**
615
638
  * Generate a MP4A box
616
639
  *
@@ -45,6 +45,9 @@ shaka.util.PeriodCombiner = class {
45
45
  /** @private {boolean} */
46
46
  this.multiTypeVariantsAllowed_ = false;
47
47
 
48
+ /** @private {boolean} */
49
+ this.useStreamOnce_ = false;
50
+
48
51
  /**
49
52
  * The IDs of the periods we have already used to generate streams.
50
53
  * This helps us identify the periods which have been added when a live
@@ -1008,6 +1011,12 @@ shaka.util.PeriodCombiner = class {
1008
1011
  }
1009
1012
  }
1010
1013
 
1014
+ // Remove just found stream if configured to, so possible future linear
1015
+ // searches can be faster.
1016
+ if (this.useStreamOnce_ && !shaka.util.PeriodCombiner.isDummy_(best)) {
1017
+ streams.delete(getKey(best));
1018
+ }
1019
+
1011
1020
  return best;
1012
1021
  }
1013
1022
 
@@ -1042,11 +1051,21 @@ shaka.util.PeriodCombiner = class {
1042
1051
  /**
1043
1052
  * @param {boolean} allowed If set to true, multi-mimeType or multi-codec
1044
1053
  * variants will be allowed.
1054
+ * @export
1045
1055
  */
1046
1056
  setAllowMultiTypeVariants(allowed) {
1047
1057
  this.multiTypeVariantsAllowed_ = allowed;
1048
1058
  }
1049
1059
 
1060
+ /**
1061
+ * @param {boolean} useOnce if true, stream will be used only once in period
1062
+ * flattening algoritnm.
1063
+ * @export
1064
+ */
1065
+ setUseStreamOnce(useOnce) {
1066
+ this.useStreamOnce_ = useOnce;
1067
+ }
1068
+
1050
1069
  /**
1051
1070
  * @param {T} outputStream An audio or video output stream
1052
1071
  * @param {T} candidate A candidate stream to be combined with the output