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
@@ -6,6 +6,7 @@
6
6
 
7
7
  goog.provide('shaka.util.Platform');
8
8
 
9
+ goog.require('shaka.log');
9
10
  goog.require('shaka.util.Timer');
10
11
 
11
12
 
@@ -149,10 +150,9 @@ shaka.util.Platform = class {
149
150
  * @return {boolean}
150
151
  */
151
152
  static isWebOS3() {
152
- // See: http://webostv.developer.lge.com/discover/specifications/web-engine/
153
- return shaka.util.Platform.userAgentContains_('Web0S') &&
154
- shaka.util.Platform.userAgentContains_(
155
- 'Chrome/38.0.2125.122 Safari/537.36');
153
+ // See: https://webostv.developer.lge.com/develop/specifications/web-api-and-web-engine#useragent-string
154
+ return shaka.util.Platform.isWebOS() &&
155
+ shaka.util.Platform.chromeVersion() === 38;
156
156
  }
157
157
 
158
158
  /**
@@ -161,18 +161,20 @@ shaka.util.Platform = class {
161
161
  * @return {boolean}
162
162
  */
163
163
  static isWebOS4() {
164
- // See: http://webostv.developer.lge.com/discover/specifications/web-engine/
165
- return !!navigator.userAgent.match(/webOS\/4/i);
164
+ // See: https://webostv.developer.lge.com/develop/specifications/web-api-and-web-engine#useragent-string
165
+ return shaka.util.Platform.isWebOS() &&
166
+ shaka.util.Platform.chromeVersion() === 53;
166
167
  }
167
168
 
168
169
  /**
169
- * Check if the current platform is a WebOS 4.
170
+ * Check if the current platform is a WebOS 5.
170
171
  *
171
172
  * @return {boolean}
172
173
  */
173
174
  static isWebOS5() {
174
- // See: http://webostv.developer.lge.com/discover/specifications/web-engine/
175
- return !!navigator.userAgent.match(/webOS\/5/i);
175
+ // See: https://webostv.developer.lge.com/develop/specifications/web-api-and-web-engine#useragent-string
176
+ return shaka.util.Platform.isWebOS() &&
177
+ shaka.util.Platform.chromeVersion() === 68;
176
178
  }
177
179
 
178
180
  /**
@@ -440,6 +442,25 @@ shaka.util.Platform = class {
440
442
  return shaka.util.Platform.userAgentContains_('Android');
441
443
  }
442
444
 
445
+ /**
446
+ * Return true if the platform is controlled by a remote control.
447
+ *
448
+ * @return {boolean}
449
+ */
450
+ static isSmartTV() {
451
+ const Platform = shaka.util.Platform;
452
+ if (Platform.isTizen() || Platform.isWebOS() ||
453
+ Platform.isXboxOne() || Platform.isPS4() ||
454
+ Platform.isPS5() || Platform.isAmazonFireTV() ||
455
+ Platform.isEOS() || Platform.isAPL() ||
456
+ Platform.isVirginMedia() || Platform.isOrange() ||
457
+ Platform.isWPE() || Platform.isChromecast() ||
458
+ Platform.isHisense()) {
459
+ return true;
460
+ }
461
+ return false;
462
+ }
463
+
443
464
  /**
444
465
  * Check if the user agent contains a key. This is the best way we know of
445
466
  * right now to detect platforms. If there is a better way, please send a
@@ -554,6 +575,137 @@ shaka.util.Platform = class {
554
575
 
555
576
  return false;
556
577
  }
578
+
579
+ /**
580
+ * Detect the maximum resolution that the platform's hardware can handle.
581
+ *
582
+ * @return {!Promise.<{width: number, height: number}>}
583
+ */
584
+ static async detectMaxHardwareResolution() {
585
+ const Platform = shaka.util.Platform;
586
+ const maxResolution = {
587
+ width: Infinity,
588
+ height: Infinity,
589
+ };
590
+
591
+ if (Platform.isChromecast()) {
592
+ // In our tests, the original Chromecast seems to have trouble decoding
593
+ // above 1080p. It would be a waste to select a higher res anyway, given
594
+ // that the device only outputs 1080p to begin with.
595
+ // Chromecast has an extension to query the device/display's resolution.
596
+ if (window.cast && cast.__platform__ &&
597
+ cast.__platform__.canDisplayType && cast.__platform__.canDisplayType(
598
+ 'video/mp4; codecs="avc1.640028"; width=3840; height=2160')) {
599
+ // The device and display can both do 4k. Assume a 4k limit.
600
+ maxResolution.width = 3840;
601
+ maxResolution.height = 2160;
602
+ } else {
603
+ // Chromecast has always been able to do 1080p. Assume a 1080p limit.
604
+ maxResolution.width = 1920;
605
+ maxResolution.height = 1080;
606
+ }
607
+ } else if (Platform.isTizen()) {
608
+ maxResolution.width = 1920;
609
+ maxResolution.height = 1080;
610
+ try {
611
+ if (webapis.systeminfo && webapis.systeminfo.getMaxVideoResolution) {
612
+ const maxVideoResolution =
613
+ webapis.systeminfo.getMaxVideoResolution();
614
+ maxResolution.width = maxVideoResolution.width;
615
+ maxResolution.height = maxVideoResolution.height;
616
+ } else {
617
+ if (webapis.productinfo.is8KPanelSupported &&
618
+ webapis.productinfo.is8KPanelSupported()) {
619
+ maxResolution.width = 7680;
620
+ maxResolution.height = 4320;
621
+ } else if (webapis.productinfo.isUdPanelSupported &&
622
+ webapis.productinfo.isUdPanelSupported()) {
623
+ maxResolution.width = 3840;
624
+ maxResolution.height = 2160;
625
+ }
626
+ }
627
+ } catch (e) {
628
+ shaka.log.alwaysWarn('Tizen: Error detecting screen size, default ' +
629
+ 'screen size 1920x1080.');
630
+ }
631
+ } else if (Platform.isXboxOne()) {
632
+ const protectionCapabilities =
633
+ new Windows.Media.Protection.ProtectionCapabilities();
634
+ const protectionResult =
635
+ Windows.Media.Protection.ProtectionCapabilityResult;
636
+ // isTypeSupported may return "maybe", which means the operation is not
637
+ // completed. This means we need to retry
638
+ // https://learn.microsoft.com/en-us/uwp/api/windows.media.protection.protectioncapabilityresult?view=winrt-22621
639
+ let result = null;
640
+ try {
641
+ const type =
642
+ 'video/mp4;codecs="hvc1,mp4a";features="decode-res-x=3840,' +
643
+ 'decode-res-y=2160,decode-bitrate=20000,decode-fps=30,' +
644
+ 'decode-bpc=10,display-res-x=3840,display-res-y=2160,' +
645
+ 'display-bpc=8"';
646
+ const keySystem = 'com.microsoft.playready.recommendation';
647
+ do {
648
+ result = protectionCapabilities.isTypeSupported(type, keySystem);
649
+ } while (result === protectionResult.maybe);
650
+ } catch (e) {
651
+ result = protectionResult.notSupported;
652
+ }
653
+ if (result === protectionResult.probably) {
654
+ maxResolution.width = 3840;
655
+ maxResolution.height = 2160;
656
+ } else {
657
+ maxResolution.width = 1920;
658
+ maxResolution.height = 1080;
659
+ }
660
+ } else if (Platform.isWebOS()) {
661
+ try {
662
+ const deviceInfo =
663
+ /** @type {{screenWidth: number, screenHeight: number}} */(
664
+ JSON.parse(window.PalmSystem.deviceInfo));
665
+ // WebOS has always been able to do 1080p. Assume a 1080p limit.
666
+ maxResolution.width = Math.max(1920, deviceInfo.screenWidth);
667
+ maxResolution.height = Math.max(1080, deviceInfo.screenHeight);
668
+ } catch (e) {
669
+ shaka.log.alwaysWarn('WebOS: Error detecting screen size, default ' +
670
+ 'screen size 1920x1080.');
671
+ maxResolution.width = 1920;
672
+ maxResolution.height = 1080;
673
+ }
674
+ } else if (Platform.isHisense()) {
675
+ // eslint-disable-next-line new-cap
676
+ if (window.Hisense_Get4KSupportState &&
677
+ // eslint-disable-next-line new-cap
678
+ window.Hisense_Get4KSupportState()) {
679
+ maxResolution.width = 3840;
680
+ maxResolution.height = 2160;
681
+ } else {
682
+ maxResolution.width = 1920;
683
+ maxResolution.height = 1080;
684
+ }
685
+ } else if (Platform.isPS4() || Platform.isPS5()) {
686
+ let supports4K = false;
687
+ try {
688
+ const result = await window.msdk.device.getDisplayInfo();
689
+ supports4K = result.resolution === '4K';
690
+ } catch (e) {
691
+ try {
692
+ const result = await window.msdk.device.getDisplayInfoImmediate();
693
+ supports4K = result.resolution === '4K';
694
+ } catch (e) {
695
+ shaka.log.alwaysWarn(
696
+ 'PlayStation: Failed to get the display info:', e);
697
+ }
698
+ }
699
+ if (supports4K) {
700
+ maxResolution.width = 3840;
701
+ maxResolution.height = 2160;
702
+ } else {
703
+ maxResolution.width = 1920;
704
+ maxResolution.height = 1080;
705
+ }
706
+ }
707
+ return maxResolution;
708
+ }
557
709
  };
558
710
 
559
711
  /** @private {shaka.util.Timer} */
@@ -94,6 +94,13 @@ shaka.util.PlayerConfiguration = class {
94
94
  ignoreDuplicateInitData: !shaka.util.Platform.isTizen2(),
95
95
  };
96
96
 
97
+ // The Xbox One and PS4 only support the Playready DRM, so they should
98
+ // prefer that key system by default to improve startup performance.
99
+ if (shaka.util.Platform.isXboxOne() ||
100
+ shaka.util.Platform.isPS4()) {
101
+ drm.preferredKeySystems.push('com.microsoft.playready');
102
+ }
103
+
97
104
  let codecSwitchingStrategy = shaka.config.CodecSwitchingStrategy.RELOAD;
98
105
  let multiTypeVariantsAllowed = false;
99
106
  if (shaka.util.Platform.supportsSmoothCodecSwitching()) {
@@ -133,17 +140,16 @@ shaka.util.PlayerConfiguration = class {
133
140
  'com.microsoft.playready',
134
141
  'urn:uuid:79f0049a-4098-8642-ab92-e65be0885f95':
135
142
  'com.microsoft.playready',
136
- 'urn:uuid:f239e769-efa3-4850-9c16-a903c6932efb':
137
- 'com.adobe.primetime',
138
- },
139
- manifestPreprocessor: (element) => {
140
- return shaka.util.ConfigUtils.referenceParametersAndReturn(
141
- [element],
142
- element);
143
143
  },
144
+ manifestPreprocessor:
145
+ shaka.util.PlayerConfiguration.defaultManifestPreprocessor,
146
+ manifestPreprocessorTXml:
147
+ shaka.util.PlayerConfiguration.defaultManifestPreprocessorTXml,
144
148
  sequenceMode: false,
145
149
  enableAudioGroups: false,
146
150
  multiTypeVariantsAllowed,
151
+ useStreamOnceInPeriodFlattening: false,
152
+ updatePeriod: -1,
147
153
  },
148
154
  hls: {
149
155
  ignoreTextStreamFailures: false,
@@ -151,6 +157,7 @@ shaka.util.PlayerConfiguration = class {
151
157
  defaultAudioCodec: 'mp4a.40.2',
152
158
  defaultVideoCodec: 'avc1.42E01E',
153
159
  ignoreManifestProgramDateTime: false,
160
+ ignoreManifestProgramDateTimeForTypes: [],
154
161
  mediaPlaylistFullMimeType:
155
162
  'video/mp2t; codecs="avc1.42E01E, mp4a.40.2"',
156
163
  useSafariBehaviorForLive: true,
@@ -161,11 +168,10 @@ shaka.util.PlayerConfiguration = class {
161
168
  allowLowLatencyByteRangeOptimization: true,
162
169
  },
163
170
  mss: {
164
- manifestPreprocessor: (element) => {
165
- return shaka.util.ConfigUtils.referenceParametersAndReturn(
166
- [element],
167
- element);
168
- },
171
+ manifestPreprocessor:
172
+ shaka.util.PlayerConfiguration.defaultManifestPreprocessor,
173
+ manifestPreprocessorTXml:
174
+ shaka.util.PlayerConfiguration.defaultManifestPreprocessorTXml,
169
175
  sequenceMode: false,
170
176
  keySystemsBySystemId: {
171
177
  '9a04f079-9840-4286-ab92-e65be0885f95':
@@ -192,6 +198,7 @@ shaka.util.PlayerConfiguration = class {
192
198
  rebufferingGoal: 2,
193
199
  bufferingGoal: 10,
194
200
  bufferBehind: 30,
201
+ evictionGoal: 1,
195
202
  ignoreTextStreamFailures: false,
196
203
  alwaysStreamText: false,
197
204
  startAtSegmentBoundary: false,
@@ -205,6 +212,7 @@ shaka.util.PlayerConfiguration = class {
205
212
  stallThreshold: 1 /* seconds */,
206
213
  stallSkip: 0.1 /* seconds */,
207
214
  useNativeHlsOnSafari: true,
215
+ useNativeHlsForFairPlay: true,
208
216
  // If we are within 2 seconds of the start of a live segment, fetch the
209
217
  // previous one. This allows for segment drift, but won't download an
210
218
  // extra segment if we aren't close to the start.
@@ -223,13 +231,23 @@ shaka.util.PlayerConfiguration = class {
223
231
  // When low latency streaming is enabled, segmentPrefetchLimit will
224
232
  // default to 2 if not specified.
225
233
  segmentPrefetchLimit: 0,
234
+ prefetchAudioLanguages: [],
235
+ disableAudioPrefetch: false,
236
+ disableTextPrefetch: false,
237
+ disableVideoPrefetch: false,
226
238
  liveSync: false,
227
239
  liveSyncMaxLatency: 1,
228
240
  liveSyncPlaybackRate: 1.1,
229
241
  liveSyncMinLatency: 0,
230
242
  liveSyncMinPlaybackRate: 1,
243
+ liveSyncPanicMode: false,
244
+ liveSyncPanicThreshold: 60,
231
245
  allowMediaSourceRecoveries: true,
232
246
  minTimeBetweenRecoveries: 5,
247
+ vodDynamicPlaybackRate: false,
248
+ vodDynamicPlaybackRateLowBufferRate: 0.95,
249
+ vodDynamicPlaybackRateBufferRatio: 0.5,
250
+ infiniteLiveStreamDuration: false,
233
251
  };
234
252
 
235
253
  // WebOS, Tizen, Chromecast and Hisense have long hardware pipelines
@@ -316,7 +334,9 @@ shaka.util.PlayerConfiguration = class {
316
334
  enabled: false,
317
335
  sessionId: '',
318
336
  contentId: '',
337
+ rtpSafetyFactor: 5,
319
338
  useHeaders: false,
339
+ includeKeys: [],
320
340
  };
321
341
 
322
342
  const cmsd = {
@@ -334,13 +354,38 @@ shaka.util.PlayerConfiguration = class {
334
354
 
335
355
  const mediaSource = {
336
356
  codecSwitchingStrategy: codecSwitchingStrategy,
337
- sourceBufferExtraFeatures: '',
357
+ addExtraFeaturesToSourceBuffer: (mimeType) => {
358
+ return shaka.util.ConfigUtils.referenceParametersAndReturn(
359
+ [mimeType],
360
+ '');
361
+ },
338
362
  forceTransmux: false,
339
363
  insertFakeEncryptionInInit: true,
364
+ modifyCueCallback: (cue, uri) => {
365
+ return shaka.util.ConfigUtils.referenceParametersAndReturn(
366
+ [cue, uri],
367
+ undefined);
368
+ },
340
369
  };
341
370
 
371
+ let skipPlayDetection = false;
372
+ if (shaka.util.Platform.isWebOS() ||
373
+ shaka.util.Platform.isTizen() ||
374
+ shaka.util.Platform.isChromecast() ||
375
+ shaka.util.Platform.isHisense() ||
376
+ shaka.util.Platform.isPS5() ||
377
+ shaka.util.Platform.isPS4() ||
378
+ shaka.util.Platform.isXboxOne()) {
379
+ skipPlayDetection = true;
380
+ }
381
+
342
382
  const ads = {
343
383
  customPlayheadTracker: false,
384
+ skipPlayDetection,
385
+ };
386
+
387
+ const textDisplayer = {
388
+ captionsUpdatePeriod: 0.25,
344
389
  };
345
390
 
346
391
  const AutoShowText = shaka.config.AutoShowText;
@@ -383,6 +428,7 @@ shaka.util.PlayerConfiguration = class {
383
428
  },
384
429
  playRangeStart: 0,
385
430
  playRangeEnd: Infinity,
431
+ textDisplayer: textDisplayer,
386
432
  textDisplayFactory: () => null,
387
433
  cmcd: cmcd,
388
434
  cmsd: cmsd,
@@ -557,4 +603,24 @@ shaka.util.PlayerConfiguration = class {
557
603
 
558
604
  return selectedTracks;
559
605
  }
606
+
607
+ /**
608
+ * @param {!Element} element
609
+ * @return {!Element}
610
+ */
611
+ static defaultManifestPreprocessor(element) {
612
+ return shaka.util.ConfigUtils.referenceParametersAndReturn(
613
+ [element],
614
+ element);
615
+ }
616
+
617
+ /**
618
+ * @param {!shaka.extern.xml.Node} element
619
+ * @return {!shaka.extern.xml.Node}
620
+ */
621
+ static defaultManifestPreprocessorTXml(element) {
622
+ return shaka.util.ConfigUtils.referenceParametersAndReturn(
623
+ [element],
624
+ element);
625
+ }
560
626
  };
package/lib/util/stats.js CHANGED
@@ -62,6 +62,9 @@ shaka.util.Stats = class {
62
62
  /** @private {number} */
63
63
  this.bandwidthEstimate_ = NaN;
64
64
 
65
+ /** @private {number} */
66
+ this.bytesDownloaded_ = NaN;
67
+
65
68
  /** @private {!shaka.util.StateHistory} */
66
69
  this.stateHistory_ = new shaka.util.StateHistory();
67
70
 
@@ -203,6 +206,17 @@ shaka.util.Stats = class {
203
206
  this.bandwidthEstimate_ = bandwidth;
204
207
  }
205
208
 
209
+ /**
210
+ * @param {number} bytesDownloaded
211
+ */
212
+ addBytesDownloaded(bytesDownloaded) {
213
+ if (isNaN(this.bytesDownloaded_)) {
214
+ this.bytesDownloaded_ = bytesDownloaded;
215
+ } else {
216
+ this.bytesDownloaded_ += bytesDownloaded;
217
+ }
218
+ }
219
+
206
220
  /**
207
221
  * @return {!shaka.util.StateHistory}
208
222
  */
@@ -244,6 +258,7 @@ shaka.util.Stats = class {
244
258
  licenseTime: this.licenseTimeSeconds_,
245
259
  liveLatency: this.liveLatencySeconds_,
246
260
  maxSegmentDuration: this.maxSegmentDurationSeconds_,
261
+ bytesDownloaded: this.bytesDownloaded_,
247
262
  stateHistory: this.stateHistory_.getCopy(),
248
263
  switchHistory: this.switchHistory_.getCopy(),
249
264
  };
@@ -276,6 +291,7 @@ shaka.util.Stats = class {
276
291
  licenseTime: NaN,
277
292
  liveLatency: NaN,
278
293
  maxSegmentDuration: NaN,
294
+ bytesDownloaded: NaN,
279
295
  switchHistory: [],
280
296
  stateHistory: [],
281
297
  };
@@ -263,13 +263,20 @@ shaka.util.StreamUtils = class {
263
263
  // |video.width| and |video.height| can be undefined, which breaks
264
264
  // the math, so make sure they are there first.
265
265
  if (video && video.width && video.height) {
266
- if (!inRange(video.width,
266
+ let videoWidth = video.width;
267
+ let videoHeight = video.height;
268
+ if (videoHeight > videoWidth) {
269
+ // Vertical video.
270
+ [videoWidth, videoHeight] = [videoHeight, videoWidth];
271
+ }
272
+
273
+ if (!inRange(videoWidth,
267
274
  restrictions.minWidth,
268
275
  Math.min(restrictions.maxWidth, maxHwRes.width))) {
269
276
  return false;
270
277
  }
271
278
 
272
- if (!inRange(video.height,
279
+ if (!inRange(videoHeight,
273
280
  restrictions.minHeight,
274
281
  Math.min(restrictions.maxHeight, maxHwRes.height))) {
275
282
  return false;
@@ -330,10 +337,12 @@ shaka.util.StreamUtils = class {
330
337
  *
331
338
  * @param {shaka.media.DrmEngine} drmEngine
332
339
  * @param {shaka.extern.Manifest} manifest
340
+ * @param {!Array<string>=} preferredKeySystems
333
341
  */
334
- static async filterManifest(drmEngine, manifest) {
342
+ static async filterManifest(drmEngine, manifest, preferredKeySystems = []) {
335
343
  await shaka.util.StreamUtils.filterManifestByMediaCapabilities(
336
- drmEngine, manifest, manifest.offlineSessionIds.length > 0);
344
+ drmEngine, manifest, manifest.offlineSessionIds.length > 0,
345
+ preferredKeySystems);
337
346
  shaka.util.StreamUtils.filterTextStreams_(manifest);
338
347
  await shaka.util.StreamUtils.filterImageStreams_(manifest);
339
348
  }
@@ -346,15 +355,16 @@ shaka.util.StreamUtils = class {
346
355
  * @param {shaka.media.DrmEngine} drmEngine
347
356
  * @param {shaka.extern.Manifest} manifest
348
357
  * @param {boolean} usePersistentLicenses
358
+ * @param {!Array<string>} preferredKeySystems
349
359
  */
350
360
  static async filterManifestByMediaCapabilities(
351
- drmEngine, manifest, usePersistentLicenses) {
361
+ drmEngine, manifest, usePersistentLicenses, preferredKeySystems) {
352
362
  goog.asserts.assert(navigator.mediaCapabilities,
353
363
  'MediaCapabilities should be valid.');
354
364
 
355
365
  await shaka.util.StreamUtils.getDecodingInfosForVariants(
356
366
  manifest.variants, usePersistentLicenses, /* srcEquals= */ false,
357
- /** preferredKeySystems= */ []);
367
+ preferredKeySystems);
358
368
 
359
369
  let keySystem = null;
360
370
  if (drmEngine) {
@@ -973,8 +983,12 @@ shaka.util.StreamUtils = class {
973
983
  // currently don't support 'fLaC', while 'flac' is supported by most
974
984
  // major browsers.
975
985
  // See https://bugs.chromium.org/p/chromium/issues/detail?id=1422728
976
- if (codecs === 'fLaC' && !shaka.util.Platform.isSafari()) {
977
- return 'flac';
986
+ if (codecs.toLowerCase() == 'flac') {
987
+ if (!shaka.util.Platform.isSafari()) {
988
+ return 'flac';
989
+ } else {
990
+ return 'fLaC';
991
+ }
978
992
  }
979
993
 
980
994
  // The same is true for 'Opus'.
@@ -276,6 +276,42 @@ shaka.util.StringUtils = class {
276
276
  static resetFromCharCode() {
277
277
  shaka.util.StringUtils.fromCharCodeImpl_.reset();
278
278
  }
279
+
280
+ /**
281
+ * This method converts the HTML entities &amp;, &lt;, &gt;, &quot;, &#39;,
282
+ * &nbsp;, &lrm; and &rlm; in string to their corresponding characters.
283
+ *
284
+ * @param {!string} input
285
+ * @return {string}
286
+ */
287
+ static htmlUnescape(input) {
288
+ // Used to map HTML entities to characters.
289
+ const htmlUnescapes = {
290
+ '&amp;': '&',
291
+ '&lt;': '<',
292
+ '&gt;': '>',
293
+ '&quot;': '"',
294
+ '&#39;': '\'',
295
+ '&apos;': '\'',
296
+ '&nbsp;': '\u{a0}',
297
+ '&lrm;': '\u{200e}',
298
+ '&rlm;': '\u{200f}',
299
+ };
300
+
301
+ // Used to match HTML entities and HTML characters.
302
+ const reEscapedHtml = /&(?:amp|lt|gt|quot|apos|#(0+)?39|nbsp|lrm|rlm);/g;
303
+ const reHasEscapedHtml = RegExp(reEscapedHtml.source);
304
+ // This check is an optimization, since replace always makes a copy
305
+ if (input && reHasEscapedHtml.test(input)) {
306
+ return input.replace(reEscapedHtml, (entity) => {
307
+ // The only thing that might not match the dictionary above is the
308
+ // single quote, which can be matched by many strings in the regex, but
309
+ // only has a single entry in the dictionary.
310
+ return htmlUnescapes[entity] || '\'';
311
+ });
312
+ }
313
+ return input || '';
314
+ }
279
315
  };
280
316
 
281
317