shaka-player 3.2.2 → 3.3.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 (156) hide show
  1. package/.github/ISSUE_TEMPLATE/bug_report.md +1 -1
  2. package/.github/ISSUE_TEMPLATE/feature_request.md +1 -1
  3. package/.github/ISSUE_TEMPLATE/question.md +1 -1
  4. package/AUTHORS +6 -5
  5. package/CHANGELOG.md +247 -0
  6. package/CONTRIBUTORS +4 -2
  7. package/build/types/core +3 -0
  8. package/build/types/offline +1 -0
  9. package/build/types/polyfill +1 -0
  10. package/build/types/ui +2 -0
  11. package/demo/common/assets.js +18 -0
  12. package/demo/common/message_ids.js +13 -1
  13. package/demo/config.js +31 -1
  14. package/demo/index.html +1 -1
  15. package/demo/locales/en.json +12 -0
  16. package/demo/locales/source.json +48 -0
  17. package/dist/controls.css +26 -2
  18. package/dist/controls.css.map +1 -1
  19. package/dist/demo.compiled.debug.js +19 -16
  20. package/dist/demo.compiled.debug.map +3 -3
  21. package/dist/demo.compiled.js +19 -16
  22. package/dist/demo.compiled.map +3 -3
  23. package/dist/deps.js +18 -11
  24. package/dist/locales.js +28 -26
  25. package/dist/receiver.compiled.debug.js +11 -9
  26. package/dist/receiver.compiled.debug.map +3 -3
  27. package/dist/receiver.compiled.js +11 -9
  28. package/dist/receiver.compiled.map +3 -3
  29. package/dist/shaka-player.compiled.d.ts +205 -15
  30. package/dist/shaka-player.compiled.debug.d.ts +205 -15
  31. package/dist/shaka-player.compiled.debug.externs.js +256 -6
  32. package/dist/shaka-player.compiled.debug.js +406 -354
  33. package/dist/shaka-player.compiled.debug.map +4 -4
  34. package/dist/shaka-player.compiled.externs.js +256 -6
  35. package/dist/shaka-player.compiled.js +826 -800
  36. package/dist/shaka-player.compiled.map +4 -4
  37. package/dist/shaka-player.ui.d.ts +221 -16
  38. package/dist/shaka-player.ui.debug.d.ts +221 -16
  39. package/dist/shaka-player.ui.debug.externs.js +283 -6
  40. package/dist/shaka-player.ui.debug.js +547 -476
  41. package/dist/shaka-player.ui.debug.map +4 -4
  42. package/dist/shaka-player.ui.externs.js +283 -6
  43. package/dist/shaka-player.ui.js +1019 -980
  44. package/dist/shaka-player.ui.map +4 -4
  45. package/docs/design/bg-fetch-after.gv +29 -0
  46. package/docs/design/bg-fetch-after.gv.png +0 -0
  47. package/docs/design/bg-fetch-before.gv +18 -0
  48. package/docs/design/bg-fetch-before.gv.png +0 -0
  49. package/docs/design/bg-fetch.md +134 -0
  50. package/docs/tutorials/blob-url.md +15 -0
  51. package/docs/tutorials/index.json +1 -0
  52. package/docs/tutorials/ui-customization.md +66 -0
  53. package/docs/tutorials/ui.md +5 -5
  54. package/externs/ima.js +3 -0
  55. package/externs/shaka/ads.js +5 -0
  56. package/externs/shaka/manifest.js +12 -1
  57. package/externs/shaka/manifest_parser.js +10 -1
  58. package/externs/shaka/net.js +20 -4
  59. package/externs/shaka/offline.js +31 -2
  60. package/externs/shaka/player.js +130 -5
  61. package/externs/shaka/text.js +14 -0
  62. package/externs/webcrypto.js +18 -0
  63. package/lib/abr/ewma.js +14 -0
  64. package/lib/abr/ewma_bandwidth_estimator.js +16 -2
  65. package/lib/abr/simple_abr_manager.js +6 -0
  66. package/lib/ads/client_side_ad.js +8 -0
  67. package/lib/ads/client_side_ad_manager.js +8 -4
  68. package/lib/ads/server_side_ad.js +7 -0
  69. package/lib/cast/cast_utils.js +1 -0
  70. package/lib/dash/dash_parser.js +48 -14
  71. package/lib/dash/segment_base.js +25 -1
  72. package/lib/dash/segment_list.js +18 -9
  73. package/lib/dash/segment_template.js +23 -10
  74. package/lib/hls/hls_parser.js +86 -23
  75. package/lib/media/quality_observer.js +291 -0
  76. package/lib/media/segment_index.js +6 -0
  77. package/lib/media/segment_reference.js +18 -3
  78. package/lib/media/streaming_engine.js +42 -4
  79. package/lib/net/http_fetch_plugin.js +31 -11
  80. package/lib/net/http_plugin_utils.js +1 -0
  81. package/lib/net/http_xhr_plugin.js +40 -14
  82. package/lib/net/networking_engine.js +81 -18
  83. package/lib/offline/download_info.js +68 -0
  84. package/lib/offline/download_manager.js +13 -5
  85. package/lib/offline/indexeddb/base_storage_cell.js +35 -0
  86. package/lib/offline/indexeddb/storage_mechanism.js +1 -1
  87. package/lib/offline/indexeddb/v5_storage_cell.js +5 -0
  88. package/lib/offline/storage.js +393 -284
  89. package/lib/offline/stored_content_utils.js +2 -0
  90. package/lib/player.js +206 -7
  91. package/lib/polyfill/random_uuid.js +52 -0
  92. package/lib/text/cue.js +12 -0
  93. package/lib/text/ttml_text_parser.js +25 -2
  94. package/lib/text/ui_text_displayer.js +2 -2
  95. package/lib/util/cmcd_manager.js +826 -0
  96. package/lib/util/error.js +6 -0
  97. package/lib/util/language_utils.js +12 -0
  98. package/lib/util/mutex.js +52 -0
  99. package/lib/util/periods.js +98 -64
  100. package/lib/util/player_configuration.js +18 -0
  101. package/package.json +1 -1
  102. package/shaka-player.uncompiled.js +3 -0
  103. package/test/dash/dash_parser_content_protection_unit.js +2 -0
  104. package/test/dash/dash_parser_live_unit.js +101 -1
  105. package/test/dash/dash_parser_manifest_unit.js +35 -1
  106. package/test/dash/dash_parser_segment_base_unit.js +2 -0
  107. package/test/dash/dash_parser_segment_list_unit.js +2 -1
  108. package/test/dash/dash_parser_segment_template_unit.js +2 -1
  109. package/test/hls/hls_live_unit.js +2 -0
  110. package/test/hls/hls_parser_unit.js +2 -0
  111. package/test/media/quality_observer_unit.js +139 -0
  112. package/test/media/segment_reference_unit.js +16 -1
  113. package/test/media/streaming_engine_integration.js +2 -1
  114. package/test/media/streaming_engine_unit.js +2 -0
  115. package/test/net/http_plugin_unit.js +20 -15
  116. package/test/offline/storage_integration.js +108 -4
  117. package/test/player_integration.js +99 -0
  118. package/test/test/util/dash_parser_util.js +4 -0
  119. package/test/test/util/fake_ad.js +7 -0
  120. package/test/test/util/fake_drm_engine.js +9 -2
  121. package/test/test/util/manifest_generator.js +3 -2
  122. package/test/test/util/test_scheme.js +14 -1
  123. package/test/text/ttml_text_parser_unit.js +47 -0
  124. package/test/ui/ui_unit.js +250 -0
  125. package/test/util/cmcd_manager_unit.js +277 -0
  126. package/ui/airplay_button.js +1 -0
  127. package/ui/audio_language_selection.js +7 -0
  128. package/ui/cast_button.js +1 -0
  129. package/ui/context_menu.js +109 -0
  130. package/ui/controls.js +41 -7
  131. package/ui/controls.less +1 -0
  132. package/ui/enums.js +4 -0
  133. package/ui/externs/ui.js +28 -1
  134. package/ui/fast_forward_button.js +14 -5
  135. package/ui/fullscreen_button.js +1 -0
  136. package/ui/less/buttons.less +0 -4
  137. package/ui/less/containers.less +74 -0
  138. package/ui/less/overflow_menu.less +0 -10
  139. package/ui/less/tooltip.less +97 -0
  140. package/ui/locales/en.json +1 -0
  141. package/ui/locales/source.json +4 -0
  142. package/ui/loop_button.js +5 -0
  143. package/ui/mute_button.js +2 -1
  144. package/ui/overflow_menu.js +5 -25
  145. package/ui/pip_button.js +5 -0
  146. package/ui/play_button.js +2 -2
  147. package/ui/playback_rate_selection.js +8 -9
  148. package/ui/resolution_selection.js +7 -0
  149. package/ui/rewind_button.js +15 -6
  150. package/ui/seek_bar.js +4 -2
  151. package/ui/settings_menu.js +36 -4
  152. package/ui/small_play_button.js +1 -0
  153. package/ui/statistics_button.js +253 -0
  154. package/ui/text_selection.js +11 -4
  155. package/ui/ui.js +30 -0
  156. package/ui/volume_bar.js +1 -1
@@ -47,6 +47,7 @@ shaka.offline.StoredContentUtils = class {
47
47
  expiration: Infinity,
48
48
  tracks: tracks,
49
49
  appMetadata: metadata,
50
+ isIncomplete: false,
50
51
  };
51
52
 
52
53
  return content;
@@ -87,6 +88,7 @@ shaka.offline.StoredContentUtils = class {
87
88
  expiration: manifestDB.expiration,
88
89
  tracks: tracks,
89
90
  appMetadata: metadata,
91
+ isIncomplete: (manifestDB.isIncomplete || false),
90
92
  };
91
93
 
92
94
  return content;
package/lib/player.js CHANGED
@@ -22,6 +22,7 @@ goog.require('shaka.media.PlayRateController');
22
22
  goog.require('shaka.media.Playhead');
23
23
  goog.require('shaka.media.PlayheadObserverManager');
24
24
  goog.require('shaka.media.PreferenceBasedCriteria');
25
+ goog.require('shaka.media.QualityObserver');
25
26
  goog.require('shaka.media.RegionObserver');
26
27
  goog.require('shaka.media.RegionTimeline');
27
28
  goog.require('shaka.media.SegmentIndex');
@@ -36,6 +37,7 @@ goog.require('shaka.text.UITextDisplayer');
36
37
  goog.require('shaka.text.WebVttGenerator');
37
38
  goog.require('shaka.util.AbortableOperation');
38
39
  goog.require('shaka.util.BufferUtils');
40
+ goog.require('shaka.util.CmcdManager');
39
41
  goog.require('shaka.util.ConfigUtils');
40
42
  goog.require('shaka.util.Error');
41
43
  goog.require('shaka.util.EventManager');
@@ -106,6 +108,32 @@ goog.requireType('shaka.routing.Payload');
106
108
  */
107
109
 
108
110
 
111
+ /**
112
+ * @event shaka.Player.DownloadFailed
113
+ * @description Fired when a download has failed, for any reason.
114
+ * 'downloadfailed'
115
+ * @property {!shaka.extern.Request} request
116
+ * @property {?shaka.util.Error} error
117
+ * @param {number} httpResponseCode
118
+ * @param {boolean} aborted
119
+ * @exportDoc
120
+ */
121
+
122
+
123
+ /**
124
+ * @event shaka.Player.DownloadHeadersReceived
125
+ * @description Fired when the networking engine has received the headers for
126
+ * a download, but before the body has been downloaded.
127
+ * If the HTTP plugin being used does not track this information, this event
128
+ * will default to being fired when the body is received, instead.
129
+ * @property {!Object.<string, string>} headers
130
+ * @property {!shaka.extern.Request} request
131
+ * @property {!shaka.net.NetworkingEngine.RequestType} type
132
+ * 'downloadheadersreceived'
133
+ * @exportDoc
134
+ */
135
+
136
+
109
137
  /**
110
138
  * @event shaka.Player.DrmSessionUpdateEvent
111
139
  * @description Fired when the CDM has accepted the license response.
@@ -147,6 +175,21 @@ goog.requireType('shaka.routing.Payload');
147
175
  * @exportDoc
148
176
  */
149
177
 
178
+ /**
179
+ * @event shaka.Player.MediaQualityChangedEvent
180
+ * @description Fired when the media quality changes at the playhead.
181
+ * That may be caused by an adaptation change or a DASH period transition.
182
+ * Separate events are emitted for audio and video contentTypes.
183
+ * This is supported for only DASH streams at this time.
184
+ * @property {string} type
185
+ * 'mediaqualitychanged'
186
+ * @property {shaka.extern.MediaQualityInfo} mediaQuality
187
+ * Information about media quality at the playhead position.
188
+ * @property {number} position
189
+ * The playhead position.
190
+ * @exportDoc
191
+ */
192
+
150
193
 
151
194
  /**
152
195
  * @event shaka.Player.BufferingEvent
@@ -441,6 +484,12 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
441
484
  /** @private {shaka.media.RegionTimeline} */
442
485
  this.regionTimeline_ = null;
443
486
 
487
+ /** @private {shaka.util.CmcdManager} */
488
+ this.cmcdManager_ = null;
489
+
490
+ /** @private {shaka.media.QualityObserver} */
491
+ this.qualityObserver_ = null;
492
+
444
493
  /** @private {shaka.media.StreamingEngine} */
445
494
  this.streamingEngine_ = null;
446
495
 
@@ -507,6 +556,15 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
507
556
  /** @private {!Array.<function():(!Promise|undefined)>} */
508
557
  this.cleanupOnUnload_ = [];
509
558
 
559
+ /**
560
+ * This playback start position will be used when
561
+ * <code>updateStartTime()</code> has been called to provide an updated
562
+ * start position during the media loading process.
563
+ *
564
+ * @private {?number}
565
+ */
566
+ this.updatedStartTime_ = null;
567
+
510
568
  if (dependencyInjector) {
511
569
  dependencyInjector(this);
512
570
  }
@@ -723,6 +781,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
723
781
  this.config_ = null;
724
782
  this.stats_ = null;
725
783
  this.videoContainer_ = null;
784
+ this.cmcdManager_ = null;
726
785
 
727
786
  if (this.networkingEngine_) {
728
787
  await this.networkingEngine_.destroy();
@@ -1020,6 +1079,19 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
1020
1079
  return this.wrapWalkerListenersWithPromise_(events);
1021
1080
  }
1022
1081
 
1082
+ /**
1083
+ * Provides a way to update the stream start position during the media loading
1084
+ * process. Can for example be called from the <code>manifestparsed</code>
1085
+ * event handler to update the start position based on information in the
1086
+ * manifest.
1087
+ *
1088
+ * @param {number} startTime
1089
+ * @export
1090
+ */
1091
+ updateStartTime(startTime) {
1092
+ this.updatedStartTime_ = startTime;
1093
+ }
1094
+
1023
1095
  /**
1024
1096
  * Tell the player to load the content at <code>assetUri</code> and start
1025
1097
  * playback at <code>startTime</code>. Before calling <code>load</code>,
@@ -1040,6 +1112,8 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
1040
1112
  * @export
1041
1113
  */
1042
1114
  load(assetUri, startTime, mimeType) {
1115
+ this.updatedStartTime_ = null;
1116
+
1043
1117
  // Do not allow the player to be used after |destroy| is called.
1044
1118
  if (this.loadMode_ == shaka.Player.LoadMode.DESTROYED) {
1045
1119
  return Promise.reject(this.createAbortLoadError_());
@@ -1093,6 +1167,9 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
1093
1167
  // before we allow calls to |updateStateHistory|.
1094
1168
  this.stats_ = new shaka.util.Stats();
1095
1169
 
1170
+ // Create the CMCD manager so client data can be attached to all requests
1171
+ this.cmcdManager_ = this.createCmcd_();
1172
+
1096
1173
  // Load's request is a little different, so we can't use our normal
1097
1174
  // listeners-to-promise method. It is the only request where we may skip the
1098
1175
  // request, so we need to set the on skip callback to reject with a specific
@@ -1594,6 +1671,9 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
1594
1671
  goog.asserts.assert(
1595
1672
  this.networkingEngine_,
1596
1673
  'Need networking engine to parse manifest.');
1674
+ goog.asserts.assert(
1675
+ this.cmcdManager_,
1676
+ 'Need CMCD manager to populate manifest request data.');
1597
1677
  goog.asserts.assert(
1598
1678
  this.config_,
1599
1679
  'Need player config to parse manifest.');
@@ -1617,9 +1697,23 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
1617
1697
  this.adManager_.onDashTimedMetadata(region);
1618
1698
  }
1619
1699
  });
1700
+ this.qualityObserver_ = null;
1701
+ if (this.config_.streaming.observeQualityChanges) {
1702
+ this.qualityObserver_ = new shaka.media.QualityObserver(
1703
+ () => this.getBufferedInfo());
1704
+ this.qualityObserver_.setListeners((mediaQualityInfo, position) => {
1705
+ this.onMediaQualityChange_(mediaQualityInfo, position);
1706
+ });
1707
+ }
1620
1708
 
1621
1709
  const playerInterface = {
1622
1710
  networkingEngine: networkingEngine,
1711
+ modifyManifestRequest: (request, manifestInfo) => {
1712
+ this.cmcdManager_.applyManifestData(request, manifestInfo);
1713
+ },
1714
+ modifySegmentRequest: (request, segmentInfo) => {
1715
+ this.cmcdManager_.applySegmentData(request, segmentInfo);
1716
+ },
1623
1717
  filter: (manifest) => this.filterManifest_(manifest),
1624
1718
  makeTextStreamsForClosedCaptions: (manifest) => {
1625
1719
  return this.makeTextStreamsForClosedCaptions_(manifest);
@@ -1780,6 +1874,13 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
1780
1874
  // something we are now depending on.
1781
1875
  has.startTime = wants.startTime;
1782
1876
 
1877
+ // If updateStartTime() has been called since load() was invoked use the
1878
+ // requested startTime
1879
+ if (this.updatedStartTime_ != null) {
1880
+ has.startTime = this.updatedStartTime_;
1881
+ this.updatedStartTime_ = null;
1882
+ }
1883
+
1783
1884
  // Store a reference to values in |has| after asserting so that closure will
1784
1885
  // know that they will still be non-null between calls to await.
1785
1886
  const mediaElement = has.mediaElement;
@@ -2177,9 +2278,13 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
2177
2278
  this.video_.textTracks, 'change', () => this.onTracksChanged_());
2178
2279
  }
2179
2280
 
2281
+ const extension = shaka.media.ManifestParser.getExtension(has.uri);
2282
+ const mimeType =
2283
+ shaka.Player.SRC_EQUAL_EXTENSIONS_TO_MIME_TYPES_[extension];
2284
+
2180
2285
  // By setting |src| we are done "loading" with src=. We don't need to set
2181
2286
  // the current time because |playhead| will do that for us.
2182
- has.mediaElement.src = has.uri;
2287
+ has.mediaElement.src = this.cmcdManager_.appendSrcData(has.uri, mimeType);
2183
2288
 
2184
2289
  // Tizen 3 / WebOS won't load anything unless you call load() explicitly,
2185
2290
  // no matter the value of the preload attribute. This is harmful on some
@@ -2509,8 +2614,30 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
2509
2614
  this.abrManager_.segmentDownloaded(deltaTimeMs, bytesDownloaded);
2510
2615
  }
2511
2616
  };
2617
+ /** @type {shaka.net.NetworkingEngine.OnHeadersReceived} */
2618
+ const onHeadersReceived_ = (headers, request, requestType) => {
2619
+ // Release a 'downloadheadersreceived' event.
2620
+ const name = shaka.Player.EventName.DownloadHeadersReceived;
2621
+ const data = new Map()
2622
+ .set('headers', headers)
2623
+ .set('request', request)
2624
+ .set('requestType', requestType);
2625
+ this.dispatchEvent(this.makeEvent_(name, data));
2626
+ };
2627
+ /** @type {shaka.net.NetworkingEngine.OnDownloadFailed} */
2628
+ const onDownloadFailed_ = (request, error, httpResponseCode, aborted) => {
2629
+ // Release a 'downloadfailed' event.
2630
+ const name = shaka.Player.EventName.DownloadFailed;
2631
+ const data = new Map()
2632
+ .set('request', request)
2633
+ .set('error', error)
2634
+ .set('httpResponseCode', httpResponseCode)
2635
+ .set('aborted', aborted);
2636
+ this.dispatchEvent(this.makeEvent_(name, data));
2637
+ };
2512
2638
 
2513
- return new shaka.net.NetworkingEngine(onProgressUpdated_);
2639
+ return new shaka.net.NetworkingEngine(
2640
+ onProgressUpdated_, onHeadersReceived_, onDownloadFailed_);
2514
2641
  }
2515
2642
 
2516
2643
  /**
@@ -2566,7 +2693,9 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
2566
2693
  // Now that we have all our observers, create a manager for them.
2567
2694
  const manager = new shaka.media.PlayheadObserverManager(this.video_);
2568
2695
  manager.manage(regionObserver);
2569
-
2696
+ if (this.qualityObserver_) {
2697
+ manager.manage(this.qualityObserver_);
2698
+ }
2570
2699
  return manager;
2571
2700
  }
2572
2701
 
@@ -2682,6 +2811,27 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
2682
2811
  mediaElement, closedCaptionsParser, textDisplayer, onMetadata);
2683
2812
  }
2684
2813
 
2814
+ /**
2815
+ * Create a new CMCD manager.
2816
+ *
2817
+ * @private
2818
+ */
2819
+ createCmcd_() {
2820
+ const abr = this.abrManager_;
2821
+
2822
+ /** @type {shaka.util.CmcdManager.PlayerInterface} */
2823
+ const playerInterface = {
2824
+ getBandwidthEstimate: () => abr ? abr.getBandwidthEstimate() : NaN,
2825
+ getBufferedInfo: () => this.getBufferedInfo(),
2826
+ getCurrentTime: () => this.video_ ? this.video_.currentTime : 0,
2827
+ getManifest: () => this.manifest_,
2828
+ getPlaybackRate: () => this.getPlaybackRate(),
2829
+ isLive: () => this.isLive(),
2830
+ };
2831
+
2832
+ return new shaka.util.CmcdManager(playerInterface, this.config_.cmcd);
2833
+ }
2834
+
2685
2835
  /**
2686
2836
  * Creates a new instance of StreamingEngine. This can be replaced by tests
2687
2837
  * to create fake instances instead.
@@ -2691,19 +2841,28 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
2691
2841
  createStreamingEngine() {
2692
2842
  goog.asserts.assert(
2693
2843
  this.playhead_ && this.abrManager_ && this.mediaSourceEngine_ &&
2694
- this.manifest_,
2844
+ this.cmcdManager_ && this.manifest_,
2695
2845
  'Must not be destroyed');
2696
2846
 
2697
2847
  /** @type {shaka.media.StreamingEngine.PlayerInterface} */
2698
2848
  const playerInterface = {
2699
2849
  getPresentationTime: () => this.playhead_.getTime(),
2700
2850
  getBandwidthEstimate: () => this.abrManager_.getBandwidthEstimate(),
2851
+ modifySegmentRequest: (request, segmentInfo) => {
2852
+ this.cmcdManager_.applySegmentData(request, segmentInfo);
2853
+ },
2701
2854
  mediaSourceEngine: this.mediaSourceEngine_,
2702
2855
  netEngine: this.networkingEngine_,
2703
2856
  onError: (error) => this.onError_(error),
2704
2857
  onEvent: (event) => this.dispatchEvent(event),
2705
2858
  onManifestUpdate: () => this.onManifestUpdate_(),
2706
2859
  onSegmentAppended: () => this.onSegmentAppended_(),
2860
+ onInitSegmentAppended: (position, initSegment) => {
2861
+ const mediaQuality = initSegment.getMediaQuality();
2862
+ if (mediaQuality && this.qualityObserver_) {
2863
+ this.qualityObserver_.addMediaQualityChange(mediaQuality, position);
2864
+ }
2865
+ },
2707
2866
  };
2708
2867
 
2709
2868
  return new shaka.media.StreamingEngine(this.manifest_, playerInterface);
@@ -4267,7 +4426,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
4267
4426
  }
4268
4427
  const trackElement =
4269
4428
  /** @type {!HTMLTrackElement} */(document.createElement('track'));
4270
- trackElement.src = uri;
4429
+ trackElement.src = this.cmcdManager_.appendTextTrackData(uri);
4271
4430
  trackElement.label = label || '';
4272
4431
  trackElement.kind = kind;
4273
4432
  trackElement.srclang = language;
@@ -4605,7 +4764,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
4605
4764
  }
4606
4765
  const trackElement =
4607
4766
  /** @type {!HTMLTrackElement} */(document.createElement('track'));
4608
- trackElement.src = uri;
4767
+ trackElement.src = this.cmcdManager_.appendTextTrackData(uri);
4609
4768
  trackElement.label = label;
4610
4769
  trackElement.kind = kind;
4611
4770
  trackElement.srclang = language;
@@ -4634,6 +4793,8 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
4634
4793
  const request = shaka.net.NetworkingEngine.makeRequest([uri], retryParams);
4635
4794
  request.method = 'GET';
4636
4795
 
4796
+ this.cmcdManager_.applyTextData(request);
4797
+
4637
4798
  const response = await netEngine.request(type, request).promise;
4638
4799
 
4639
4800
  return response.data;
@@ -5031,6 +5192,9 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
5031
5192
 
5032
5193
  if (loaded) {
5033
5194
  this.playRateController_.setBuffering(isBuffering);
5195
+ if (this.cmcdManager_) {
5196
+ this.cmcdManager_.setBuffering(isBuffering);
5197
+ }
5034
5198
  this.updateStateHistory_();
5035
5199
  }
5036
5200
 
@@ -5505,6 +5669,38 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
5505
5669
  this.dispatchEvent(this.makeEvent_(eventName, data));
5506
5670
  }
5507
5671
 
5672
+ /**
5673
+ * When notified of a media quality change we need to emit a
5674
+ * MediaQualityChange event to the app.
5675
+ *
5676
+ * @param {shaka.extern.MediaQualityInfo} mediaQuality
5677
+ * @param {number} position
5678
+ *
5679
+ * @private
5680
+ */
5681
+ onMediaQualityChange_(mediaQuality, position) {
5682
+ // Always make a copy to avoid exposing our internal data to the app.
5683
+ const clone = {
5684
+ bandwidth: mediaQuality.bandwidth,
5685
+ audioSamplingRate: mediaQuality.audioSamplingRate,
5686
+ codecs: mediaQuality.codecs,
5687
+ contentType: mediaQuality.contentType,
5688
+ frameRate: mediaQuality.frameRate,
5689
+ height: mediaQuality.height,
5690
+ mimeType: mediaQuality.mimeType,
5691
+ channelsCount: mediaQuality.channelsCount,
5692
+ pixelAspectRatio: mediaQuality.pixelAspectRatio,
5693
+ width: mediaQuality.width,
5694
+ };
5695
+
5696
+ const data = new Map()
5697
+ .set('mediaQuality', clone)
5698
+ .set('position', position);
5699
+
5700
+ this.dispatchEvent(this.makeEvent_(
5701
+ shaka.Player.EventName.MediaQualityChanged, data));
5702
+ }
5703
+
5508
5704
  /**
5509
5705
  * Turn the media element's error object into a Shaka Player error object.
5510
5706
  *
@@ -6268,6 +6464,8 @@ shaka.Player.EventName = {
6268
6464
  AbrStatusChanged: 'abrstatuschanged',
6269
6465
  Adaptation: 'adaptation',
6270
6466
  Buffering: 'buffering',
6467
+ DownloadFailed: 'downloadfailed',
6468
+ DownloadHeadersReceived: 'downloadheadersreceived',
6271
6469
  DrmSessionUpdate: 'drmsessionupdate',
6272
6470
  Emsg: 'emsg',
6273
6471
  Error: 'error',
@@ -6276,6 +6474,7 @@ shaka.Player.EventName = {
6276
6474
  Loaded: 'loaded',
6277
6475
  Loading: 'loading',
6278
6476
  ManifestParsed: 'manifestparsed',
6477
+ MediaQualityChanged: 'mediaqualitychanged',
6279
6478
  Metadata: 'metadata',
6280
6479
  OnStateChange: 'onstatechange',
6281
6480
  OnStateIdle: 'onstateidle',
@@ -6331,7 +6530,7 @@ shaka.Player.TYPICAL_BUFFERING_THRESHOLD_ = 0.5;
6331
6530
  * @define {string} A version number taken from git at compile time.
6332
6531
  * @export
6333
6532
  */
6334
- shaka.Player.version = 'v3.2.2-uncompiled';
6533
+ shaka.Player.version = 'v3.3.0-uncompiled';
6335
6534
 
6336
6535
  // Initialize the deprecation system using the version string we just set
6337
6536
  // on the player.
@@ -0,0 +1,52 @@
1
+ /*! @license
2
+ * Shaka Player
3
+ * Copyright 2016 Google LLC
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+ goog.provide('shaka.polyfill.RandomUUID');
7
+
8
+ goog.require('shaka.log');
9
+ goog.require('shaka.polyfill');
10
+
11
+ /**
12
+ * @summary A polyfill to provide window.crypto.randomUUID in all browsers.
13
+ * @export
14
+ */
15
+ shaka.polyfill.RandomUUID = class {
16
+ /**
17
+ * Install the polyfill if needed.
18
+ * @export
19
+ */
20
+ static install() {
21
+ shaka.log.debug('randomUUID.install');
22
+
23
+ if (!window.crypto) {
24
+ // See: https://caniuse.com/cryptography
25
+ shaka.log.debug(
26
+ 'window.crypto must be available to install randomUUID polyfill.');
27
+ return;
28
+ }
29
+
30
+ if ('randomUUID' in window.crypto) {
31
+ shaka.log.debug(
32
+ 'RandomUUID: Native window.crypto.randomUUID() support found.');
33
+ return;
34
+ }
35
+
36
+ window.crypto.randomUUID = shaka.polyfill.RandomUUID.randomUUID_;
37
+ }
38
+
39
+ /**
40
+ * @return {string}
41
+ * @private
42
+ */
43
+ static randomUUID_() {
44
+ const url = URL.createObjectURL(new Blob());
45
+ const uuid = url.toString();
46
+ URL.revokeObjectURL(url);
47
+ return uuid.substr(uuid.lastIndexOf('/') + 1);
48
+ }
49
+ };
50
+
51
+
52
+ shaka.polyfill.register(shaka.polyfill.RandomUUID.install);
package/lib/text/cue.js CHANGED
@@ -144,6 +144,18 @@ shaka.text.Cue = class {
144
144
  */
145
145
  this.border = '';
146
146
 
147
+ /**
148
+ * @override
149
+ * @exportInterface
150
+ */
151
+ this.textStrokeColor = '';
152
+
153
+ /**
154
+ * @override
155
+ * @exportInterface
156
+ */
157
+ this.textStrokeWidth = '';
158
+
147
159
  /**
148
160
  * @override
149
161
  * @exportInterface
@@ -531,7 +531,6 @@ shaka.text.TtmlTextParser = class {
531
531
 
532
532
  const color = TtmlTextParser.getStyleAttribute_(
533
533
  cueElement, region, styles, 'color', shouldInheritRegionStyles);
534
-
535
534
  if (color) {
536
535
  cue.color = color;
537
536
  }
@@ -584,7 +583,6 @@ shaka.text.TtmlTextParser = class {
584
583
 
585
584
  const fontSize = TtmlTextParser.getStyleAttribute_(
586
585
  cueElement, region, styles, 'fontSize', shouldInheritRegionStyles);
587
-
588
586
  if (fontSize) {
589
587
  const isValidFontSizeUnit =
590
588
  fontSize.match(TtmlTextParser.unitValues_) ||
@@ -622,6 +620,31 @@ shaka.text.TtmlTextParser = class {
622
620
  }
623
621
  }
624
622
 
623
+ const textOutline = TtmlTextParser.getStyleAttribute_(
624
+ cueElement, region, styles, 'textOutline', shouldInheritRegionStyles);
625
+ if (textOutline) {
626
+ // tts:textOutline isn't natively supported by browsers, but it can be
627
+ // mostly replicated using the non-standard -webkit-text-stroke-width and
628
+ // -webkit-text-stroke-color properties.
629
+ const split = textOutline.split(' ');
630
+ if (split[0].match(TtmlTextParser.unitValues_)) {
631
+ // There is no defined color, so default to the text color.
632
+ cue.textStrokeColor = cue.color;
633
+ } else {
634
+ cue.textStrokeColor = split[0];
635
+ split.shift();
636
+ }
637
+ if (split[0] && split[0].match(TtmlTextParser.unitValues_)) {
638
+ cue.textStrokeWidth = split[0];
639
+ } else {
640
+ // If there is no width, or the width is not a number, don't draw a
641
+ // border.
642
+ cue.textStrokeColor = '';
643
+ }
644
+ // There is an optional blur radius also, but we have no way of
645
+ // replicating that, so ignore it.
646
+ }
647
+
625
648
  const letterSpacing = TtmlTextParser.getStyleAttribute_(
626
649
  cueElement, region, styles, 'letterSpacing', shouldInheritRegionStyles);
627
650
  if (letterSpacing && letterSpacing.match(TtmlTextParser.unitValues_)) {
@@ -403,8 +403,8 @@ shaka.text.UITextDisplayer = class {
403
403
  return nonBreakingSpace.repeat(match.length);
404
404
  });
405
405
 
406
- style.backgroundColor = cue.backgroundColor;
407
- style.border = cue.border;
406
+ style.webkitTextStrokeColor = cue.textStrokeColor;
407
+ style.webkitTextStrokeWidth = cue.textStrokeWidth;
408
408
  style.color = cue.color;
409
409
  style.direction = cue.direction;
410
410
  style.opacity = cue.opacity;