shaka-player 2.5.1 → 2.5.5

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 (166) hide show
  1. package/.csslintrc +13 -0
  2. package/AUTHORS +3 -0
  3. package/CHANGELOG.md +156 -0
  4. package/CONTRIBUTORS +3 -0
  5. package/README.md +3 -0
  6. package/build/README.md +5 -5
  7. package/build/apps.py +3 -1
  8. package/build/build.py +5 -2
  9. package/build/check.py +17 -0
  10. package/build/compiler.py +49 -0
  11. package/build/conformance.textproto +1 -0
  12. package/build/docs.py +0 -6
  13. package/build/generateExterns.js +72 -20
  14. package/build/install-linux-prereqs.sh +4 -4
  15. package/build/shakaBuildHelpers.py +7 -8
  16. package/build/test.py +21 -9
  17. package/build/types/core +1 -0
  18. package/build/types/ui +6 -1
  19. package/demo/asset_card.js +129 -41
  20. package/demo/cast_receiver/receiver_app.css +27 -13
  21. package/demo/close_button.js +10 -15
  22. package/demo/common/asset.js +6 -6
  23. package/demo/common/assets.js +72 -14
  24. package/demo/config.js +28 -25
  25. package/demo/custom.js +20 -16
  26. package/demo/demo.less +107 -16
  27. package/demo/demo_uncompiled.js +33 -0
  28. package/demo/demo_utils.js +4 -3
  29. package/demo/front.js +12 -8
  30. package/demo/index.html +14 -17
  31. package/demo/input.js +30 -23
  32. package/demo/input_container.js +15 -11
  33. package/demo/load.js +5 -7
  34. package/demo/main.js +138 -49
  35. package/demo/search.js +40 -36
  36. package/demo/service_worker.js +1 -1
  37. package/demo/tooltip.js +16 -24
  38. package/dist/.csslintstamp +0 -0
  39. package/dist/controls.css +1 -1
  40. package/dist/controls.css.map +1 -1
  41. package/dist/demo.compiled.debug.js +165 -152
  42. package/dist/demo.compiled.debug.map +4 -4
  43. package/dist/demo.compiled.js +165 -152
  44. package/dist/demo.compiled.map +4 -4
  45. package/dist/demo.css +1 -1
  46. package/dist/demo.css.map +1 -1
  47. package/dist/deps.js +28 -11
  48. package/dist/receiver.compiled.debug.js +11 -11
  49. package/dist/receiver.compiled.debug.map +3 -3
  50. package/dist/receiver.compiled.js +11 -11
  51. package/dist/receiver.compiled.map +3 -3
  52. package/dist/shaka-player.compiled.debug.externs.js +232 -102
  53. package/dist/shaka-player.compiled.debug.js +189 -165
  54. package/dist/shaka-player.compiled.debug.map +4 -4
  55. package/dist/shaka-player.compiled.externs.js +232 -102
  56. package/dist/shaka-player.compiled.js +567 -553
  57. package/dist/shaka-player.compiled.map +4 -4
  58. package/dist/shaka-player.ui.debug.externs.js +338 -113
  59. package/dist/shaka-player.ui.debug.js +296 -276
  60. package/dist/shaka-player.ui.debug.map +4 -4
  61. package/dist/shaka-player.ui.externs.js +338 -113
  62. package/dist/shaka-player.ui.js +685 -671
  63. package/dist/shaka-player.ui.map +4 -4
  64. package/docs/tutorials/drm-config.md +1 -0
  65. package/docs/tutorials/fairplay.md +77 -0
  66. package/docs/tutorials/index.json +1 -0
  67. package/docs/tutorials/manifest-parser.md +1 -1
  68. package/docs/tutorials/ui-customization.md +5 -2
  69. package/docs/tutorials/ui.md +10 -4
  70. package/docs/tutorials/welcome.md +2 -2
  71. package/externs/mediasession.js +16 -1
  72. package/externs/shaka/net.js +14 -4
  73. package/externs/shaka/player.js +136 -90
  74. package/externs/tippy.js +30 -0
  75. package/externs/webkitmediakeys.js +4 -0
  76. package/karma.conf.js +30 -24
  77. package/lib/cast/cast_proxy.js +94 -23
  78. package/lib/cast/cast_receiver.js +8 -1
  79. package/lib/dash/content_protection.js +33 -1
  80. package/lib/dash/dash_parser.js +13 -5
  81. package/lib/hls/hls_parser.js +20 -4
  82. package/lib/media/drm_engine.js +29 -2
  83. package/lib/media/media_source_engine.js +9 -6
  84. package/lib/media/play_rate_controller.js +14 -3
  85. package/lib/media/playhead.js +8 -3
  86. package/lib/media/streaming_engine.js +68 -20
  87. package/lib/media/transmuxer.js +2 -2
  88. package/lib/media/video_wrapper.js +8 -3
  89. package/lib/net/data_uri_plugin.js +5 -2
  90. package/lib/net/http_fetch_plugin.js +3 -3
  91. package/lib/net/http_plugin_utils.js +2 -4
  92. package/lib/net/http_xhr_plugin.js +2 -2
  93. package/lib/net/networking_engine.js +1 -0
  94. package/lib/offline/offline_scheme.js +7 -2
  95. package/lib/player.js +18 -8
  96. package/lib/polyfill/input_event.js +7 -11
  97. package/lib/polyfill/mediasource.js +1 -1
  98. package/lib/polyfill/patchedmediakeys_apple.js +2 -72
  99. package/lib/text/cue.js +1 -1
  100. package/lib/text/ttml_text_parser.js +1 -1
  101. package/lib/util/config_utils.js +1 -0
  102. package/lib/util/error.js +45 -11
  103. package/lib/util/event_manager.js +211 -100
  104. package/lib/util/fairplay_utils.js +122 -0
  105. package/lib/util/i_releasable.js +1 -0
  106. package/lib/util/player_configuration.js +7 -0
  107. package/lib/util/pssh.js +41 -0
  108. package/lib/util/string_utils.js +42 -6
  109. package/lib/util/timer.js +1 -1
  110. package/package.json +33 -33
  111. package/roadmap.md +96 -0
  112. package/shaka-player.uncompiled.js +5 -1
  113. package/test/dash/dash_parser_content_protection_unit.js +35 -1
  114. package/test/dash/dash_parser_manifest_unit.js +60 -0
  115. package/test/hls/hls_live_unit.js +7 -1
  116. package/test/hls/hls_parser_unit.js +50 -0
  117. package/test/media/drm_engine_unit.js +10 -4
  118. package/test/media/media_source_engine_integration.js +12 -0
  119. package/test/media/media_source_engine_unit.js +3 -1
  120. package/test/media/playhead_unit.js +7 -5
  121. package/test/media/streaming_engine_unit.js +46 -1
  122. package/test/media/transmuxer_integration.js +13 -0
  123. package/test/net/data_uri_plugin_unit.js +13 -2
  124. package/test/net/http_plugin_unit.js +17 -12
  125. package/test/net/networking_engine_unit.js +17 -8
  126. package/test/offline/offline_scheme_unit.js +18 -8
  127. package/test/player_external.js +3 -2
  128. package/test/player_integration.js +94 -3
  129. package/test/player_src_equals_integration.js +2 -2
  130. package/test/player_unit.js +21 -8
  131. package/test/test/boot.js +20 -5
  132. package/test/test/externs/jasmine.js +48 -0
  133. package/test/test/util/expect_async_polyfill.js +145 -0
  134. package/test/test/util/fake_networking_engine.js +1 -0
  135. package/test/test/util/manifest_generator.js +2 -1
  136. package/test/test/util/test_scheme.js +34 -2
  137. package/test/test/util/ui_utils.js +41 -0
  138. package/test/test/util/util.js +16 -8
  139. package/test/ui/ui_unit.js +130 -4
  140. package/ui/audio_language_selection.js +17 -96
  141. package/ui/big_play_button.js +61 -0
  142. package/ui/controls.js +226 -335
  143. package/ui/controls.less +3 -5
  144. package/ui/element.js +24 -6
  145. package/ui/externs/ui.js +62 -9
  146. package/ui/fullscreen_button.js +2 -52
  147. package/ui/less/buttons.less +10 -16
  148. package/ui/less/containers.less +75 -20
  149. package/ui/less/general.less +18 -2
  150. package/ui/less/other_elements.less +2 -1
  151. package/ui/less/overflow_menu.less +11 -10
  152. package/ui/less/range_elements.less +12 -10
  153. package/ui/less/spinner.less +2 -0
  154. package/ui/overflow_menu.js +1 -1
  155. package/ui/pip_button.js +1 -1
  156. package/ui/{play_pause_button.js → play_button.js} +17 -47
  157. package/ui/presentation_time.js +17 -12
  158. package/ui/range_element.js +176 -0
  159. package/ui/resolution_selection.js +43 -106
  160. package/ui/seek_bar.js +204 -0
  161. package/ui/settings_menu.js +113 -0
  162. package/ui/small_play_button.js +73 -0
  163. package/ui/text_displayer.js +17 -14
  164. package/ui/text_selection.js +42 -107
  165. package/ui/ui.js +17 -40
  166. package/ui/volume_bar.js +40 -68
@@ -15,6 +15,10 @@
15
15
  * limitations under the License.
16
16
  */
17
17
 
18
+
19
+ goog.provide('shakaDemo.InputContainer');
20
+
21
+
18
22
  /**
19
23
  * Creates elements for containing inputs. It represents a single "section" of
20
24
  * input.
@@ -23,16 +27,16 @@
23
27
  * It also has an optional header, which can contain style-dependent
24
28
  * functionality.
25
29
  */
26
- class ShakaDemoInputContainer {
30
+ shakaDemo.InputContainer = class {
27
31
  /**
28
32
  * @param {!Element} parentDiv
29
33
  * @param {?string} headerText The text to be displayed by the header. If
30
34
  * null, there will be no header.
31
- * @param {!ShakaDemoInputContainer.Style} style
35
+ * @param {!shakaDemo.InputContainer.Style} style
32
36
  * @param {?string} docLink
33
37
  */
34
38
  constructor(parentDiv, headerText, style, docLink) {
35
- /** @private {!ShakaDemoInputContainer.Style} */
39
+ /** @private {!shakaDemo.InputContainer.Style} */
36
40
  this.style_ = style;
37
41
 
38
42
  /** @private {?Element} */
@@ -57,7 +61,7 @@ class ShakaDemoInputContainer {
57
61
  this.createHeader_(parentDiv, headerText);
58
62
  }
59
63
  this.table_.classList.add(style);
60
- if (style == ShakaDemoInputContainer.Style.ACCORDION) {
64
+ if (style == shakaDemo.InputContainer.Style.ACCORDION) {
61
65
  this.table_.classList.add('hidden');
62
66
  }
63
67
  parentDiv.appendChild(this.table_);
@@ -70,7 +74,7 @@ class ShakaDemoInputContainer {
70
74
  * @return {boolean} true if this is an open accordion menu, false otherwise
71
75
  */
72
76
  getIsOpen() {
73
- if (this.style_ == ShakaDemoInputContainer.Style.ACCORDION) {
77
+ if (this.style_ == shakaDemo.InputContainer.Style.ACCORDION) {
74
78
  return this.table_.classList.contains('show');
75
79
  }
76
80
  return false;
@@ -78,7 +82,7 @@ class ShakaDemoInputContainer {
78
82
 
79
83
  /** If this is an accordion menu, open it. */
80
84
  open() {
81
- if (!this.style_ == ShakaDemoInputContainer.Style.ACCORDION) {
85
+ if (!this.style_ == shakaDemo.InputContainer.Style.ACCORDION) {
82
86
  return;
83
87
  }
84
88
  this.table_.classList.remove('hidden');
@@ -90,7 +94,7 @@ class ShakaDemoInputContainer {
90
94
 
91
95
  /** If this is an accordion menu, close it. */
92
96
  close() {
93
- if (this.style_ != ShakaDemoInputContainer.Style.ACCORDION) {
97
+ if (this.style_ != shakaDemo.InputContainer.Style.ACCORDION) {
94
98
  return;
95
99
  }
96
100
  this.table_.classList.remove('show');
@@ -106,7 +110,7 @@ class ShakaDemoInputContainer {
106
110
  * @private
107
111
  */
108
112
  createHeader_(parentDiv, headerText) {
109
- if (this.style_ == ShakaDemoInputContainer.Style.ACCORDION) {
113
+ if (this.style_ == shakaDemo.InputContainer.Style.ACCORDION) {
110
114
  this.header_ = document.createElement('button');
111
115
  this.header_.classList.add('mdl-button--raised');
112
116
  this.header_.classList.add('mdl-button');
@@ -182,16 +186,16 @@ class ShakaDemoInputContainer {
182
186
 
183
187
  this.latestTooltip = tooltipString;
184
188
  if (tooltipString) {
185
- ShakaDemoTooltips.make(this.table_, this.latestRow_, tooltipString);
189
+ shakaDemo.Tooltips.make(this.latestRow_, tooltipString);
186
190
  // Keep the row from being focused.
187
191
  this.latestRow_.setAttribute('tabindex', -1);
188
192
  this.latestRow_.classList.add('borderless');
189
193
  }
190
194
  }
191
- }
195
+ };
192
196
 
193
197
  /** @enum {string} */
194
- ShakaDemoInputContainer.Style = {
198
+ shakaDemo.InputContainer.Style = {
195
199
  VERTICAL: 'input-container-style-vertical',
196
200
  ACCORDION: 'input-container-style-accordion',
197
201
  FLEX: 'input-container-style-flex',
package/demo/load.js CHANGED
@@ -87,40 +87,34 @@ function shakaUncompiledModeSupported() {
87
87
  fragments = fragments ? fragments.split(';') : [];
88
88
  var combined = fields.concat(fragments);
89
89
 
90
- var scripts = window['UNCOMPILED_JS'];
91
90
  var buildType = 'uncompiled';
92
91
  var buildSpecified = false;
93
92
 
94
93
  if (!shakaUncompiledModeSupported()) {
95
94
  // If uncompiled mode is not supported, default to the compiled debug
96
95
  // version, which should still work.
97
- scripts = window['COMPILED_DEBUG_JS'];
98
96
  buildType = 'debug_compiled';
99
97
  }
100
98
 
101
99
  if (!navigator.onLine) {
102
100
  // If we're offline, default to the compiled version, which may have been
103
101
  // cached by the service worker.
104
- scripts = window['COMPILED_JS'];
105
102
  buildType = 'compiled';
106
103
  }
107
104
 
108
105
  // Very old browsers do not have Array.prototype.indexOf, so we loop.
109
106
  for (var i = 0; i < combined.length; ++i) {
110
107
  if (combined[i] == 'build=compiled') {
111
- scripts = window['COMPILED_JS'];
112
108
  buildType = 'compiled';
113
109
  buildSpecified = true;
114
110
  break;
115
111
  }
116
112
  if (combined[i] == 'build=debug_compiled') {
117
- scripts = window['COMPILED_DEBUG_JS'];
118
113
  buildType = 'debug_compiled';
119
114
  buildSpecified = true;
120
115
  break;
121
116
  }
122
117
  if (combined[i] == 'build=uncompiled') {
123
- scripts = window['UNCOMPILED_JS'];
124
118
  buildType = 'uncompiled';
125
119
  buildSpecified = true;
126
120
  break;
@@ -131,7 +125,6 @@ function shakaUncompiledModeSupported() {
131
125
  // The URL says uncompiled, but we know it won't work. This URL was
132
126
  // probably copied from some other browser, but it won't work in this one.
133
127
  // Force the use of the compiled debug build and update the hash.
134
- scripts = window['COMPILED_DEBUG_JS'];
135
128
  buildType = 'debug_compiled';
136
129
 
137
130
  // Replace the build type in either the hash or the URL parameters.
@@ -159,6 +152,11 @@ function shakaUncompiledModeSupported() {
159
152
 
160
153
  // The application must define its list of compiled and uncompiled sources
161
154
  // before including this loader. The URLs should be relative to the page.
155
+ var scripts = {
156
+ 'compiled': window['COMPILED_JS'],
157
+ 'debug_compiled': window['COMPILED_DEBUG_JS'],
158
+ 'uncompiled': window['UNCOMPILED_JS'],
159
+ }[buildType];
162
160
  for (var j = 0; j < scripts.length; ++j) {
163
161
  loadRelativeScript(scripts[j]);
164
162
  }
package/demo/main.js CHANGED
@@ -15,13 +15,17 @@
15
15
  * limitations under the License.
16
16
  */
17
17
 
18
+
19
+ goog.provide('shakaDemo.Main');
20
+
21
+
18
22
  /**
19
23
  * Shaka Player demo, main section.
20
24
  * This controls the header and the footer, and contains all methods that should
21
25
  * be shared by multiple page layouts (loading assets, setting/checking
22
26
  * configuration, etc).
23
27
  */
24
- class ShakaDemoMain {
28
+ shakaDemo.Main = class {
25
29
  constructor() {
26
30
  /** @private {HTMLMediaElement} */
27
31
  this.video_ = null;
@@ -35,6 +39,12 @@ class ShakaDemoMain {
35
39
  /** @type {?ShakaDemoAssetInfo} */
36
40
  this.selectedAsset = null;
37
41
 
42
+ /**
43
+ * The configuration asked for by the user. I.e., not from the asset.
44
+ * @private {shaka.extern.PlayerConfiguration}
45
+ */
46
+ this.desiredConfig_;
47
+
38
48
  /** @private {shaka.extern.PlayerConfiguration} */
39
49
  this.defaultConfig_;
40
50
 
@@ -55,6 +65,9 @@ class ShakaDemoMain {
55
65
 
56
66
  /** @private {string} */
57
67
  this.uiLocale_ = '';
68
+
69
+ /** @private {boolean} */
70
+ this.noInput_ = false;
58
71
  }
59
72
 
60
73
  /**
@@ -64,13 +77,15 @@ class ShakaDemoMain {
64
77
  */
65
78
  initCommon_() {
66
79
  // Display uncaught exceptions. Note that this doesn't seem to work in IE.
67
- // See ShakaDemoMain.initWrapper for a failsafe that works for init-time
80
+ // See shakaDemo.Main.initWrapper for a failsafe that works for init-time
68
81
  // errors on IE.
69
82
  window.addEventListener('error', (event) => {
70
83
  // Exception to the exceptions we catch: ChromeVox (screenreader) always
71
84
  // throws an error as of Chrome 73. Screen these out since they are
72
85
  // unrelated to our application and we can't control them.
73
- if (event.message.includes('cvox.ApiImplementation')) return;
86
+ if (event.message.includes('cvox.Api')) {
87
+ return;
88
+ }
74
89
 
75
90
  this.onError_(/** @type {!shaka.util.Error} */ (event.error));
76
91
  });
@@ -79,8 +94,8 @@ class ShakaDemoMain {
79
94
  document.getElementById('error-display-close-button').addEventListener(
80
95
  'click', (event) => this.closeError_());
81
96
 
82
- // Set up version string.
83
- this.setUpVersionString_();
97
+ // Set up version strings in the appropriate divs.
98
+ this.setUpVersionStrings_();
84
99
  }
85
100
 
86
101
  /**
@@ -127,7 +142,7 @@ class ShakaDemoMain {
127
142
 
128
143
  this.video_ =
129
144
  /** @type {!HTMLVideoElement} */(document.getElementById('video'));
130
- this.video_.poster = ShakaDemoMain.mainPoster_;
145
+ this.video_.poster = shakaDemo.Main.mainPoster_;
131
146
 
132
147
  this.container_ = /** @type {!HTMLElement} */(
133
148
  document.getElementsByClassName('video-container')[0]);
@@ -143,16 +158,40 @@ class ShakaDemoMain {
143
158
  }
144
159
  }
145
160
 
161
+ // Optionally enter noinput mode. This has to happen before setting up the
162
+ // player.
163
+ this.noInput_ = 'noinput' in this.getParams_();
146
164
  this.setupPlayer_();
147
165
  this.readHash_();
148
166
  window.addEventListener('hashchange', () => this.hashChanged_());
149
167
 
150
168
  await this.setupStorage_();
151
169
 
170
+ if (this.noInput_) {
171
+ // Set the page to noInput mode, disabling the header and footer.
172
+ const hideClass = 'should-hide-in-no-input-mode';
173
+ for (const element of document.getElementsByClassName(hideClass)) {
174
+ this.hideNode_(element);
175
+ }
176
+ const showClass = 'should-show-in-no-input-mode';
177
+ for (const element of document.getElementsByClassName(showClass)) {
178
+ this.showNode_(element);
179
+ }
180
+ // Also fullscreen the container.
181
+ this.container_.classList.add('no-input-sized');
182
+ document.getElementById('video-bar').classList.add('no-input-sized');
183
+ }
184
+
152
185
  // The main page is loaded. Dispatch an event, so the various
153
186
  // configurations will load themselves.
154
187
  this.dispatchEventWithName_('shaka-main-loaded');
155
188
 
189
+ // Wait for one interruptor cycle, so that the tabs have time to load.
190
+ // This ensures that, for example, if there is an asset playing at page
191
+ // load time, the video will scroll into view second, and the page won't
192
+ // scroll away from the video.
193
+ await Promise.resolve();
194
+
156
195
  // Update the componentHandler, to account for any new MDL elements added.
157
196
  componentHandler.upgradeDom();
158
197
 
@@ -174,13 +213,19 @@ class ShakaDemoMain {
174
213
  const ui = video['ui'];
175
214
  this.player_ = ui.getControls().getPlayer();
176
215
 
177
- // Register custom controls to the UI.
178
- shaka.ui.Controls.registerElement('close', new CloseButton.Factory());
216
+ if (!this.noInput_) {
217
+ // Don't add the close button if in noInput mode; it doesn't make much
218
+ // sense to stop playing a video if you can't start playing other videos.
179
219
 
180
- // Configure UI.
181
- const uiConfig = ui.getConfiguration();
182
- uiConfig.controlPanelElements.push('close');
183
- ui.configure(uiConfig);
220
+ // Register custom controls to the UI.
221
+ const factory = new shakaDemo.CloseButton.Factory();
222
+ shaka.ui.Controls.registerElement('close', factory);
223
+
224
+ // Configure UI.
225
+ const uiConfig = ui.getConfiguration();
226
+ uiConfig.controlPanelElements.push('close');
227
+ ui.configure(uiConfig);
228
+ }
184
229
 
185
230
  // Add application-level default configs here. These are not the library
186
231
  // defaults, but they are the application defaults. This will affect the
@@ -192,9 +237,10 @@ class ShakaDemoMain {
192
237
 
193
238
  // Get default config.
194
239
  this.defaultConfig_ = this.player_.getConfiguration();
240
+ this.desiredConfig_ = this.player_.getConfiguration();
195
241
  const languages = navigator.languages || ['en-us'];
196
- this.player_.configure('preferredAudioLanguage', languages[0]);
197
- this.player_.configure('preferredTextLanguage', languages[0]);
242
+ this.configure('preferredAudioLanguage', languages[0]);
243
+ this.configure('preferredTextLanguage', languages[0]);
198
244
  this.uiLocale_ = languages[0];
199
245
  // TODO(#1591): Support multiple language preferences
200
246
 
@@ -423,6 +469,11 @@ class ShakaDemoMain {
423
469
  * @return {?string} unsupportedReason Null if asset is supported.
424
470
  */
425
471
  getAssetUnsupportedReason(asset, needOffline) {
472
+ if (needOffline &&
473
+ (!shaka.offline.Storage.support() || !this.initialStoredList_)) {
474
+ return 'Your browser does not support offline storage.';
475
+ }
476
+
426
477
  if (asset.source == shakaAssets.Source.CUSTOM) {
427
478
  // We can't be sure if custom assets are supported or not. Just assume
428
479
  // they are.
@@ -438,11 +489,7 @@ class ShakaDemoMain {
438
489
  return 'This asset cannot be downloaded.';
439
490
  }
440
491
 
441
- if (needOffline && !shaka.offline.Storage.support()) {
442
- return 'Your browser does not support offline storage.';
443
- }
444
-
445
- if (!asset.drm.includes(shakaAssets.KeySystem.CLEAR)) {
492
+ if (!asset.isClear()) {
446
493
  const hasSupportedDRM = asset.drm.some((drm) => {
447
494
  return this.support_.drm[drm];
448
495
  });
@@ -583,7 +630,7 @@ class ShakaDemoMain {
583
630
  /* manifestUri= */ manifest,
584
631
  /* source= */ shakaAssets.Source.UNKNOWN);
585
632
  if ('license' in params) {
586
- let drmSystems = ShakaDemoMain.commonDrmSystems;
633
+ let drmSystems = shakaDemo.Main.commonDrmSystems;
587
634
  if ('drmSystem' in params) {
588
635
  drmSystems = [params['drmSystem']];
589
636
  }
@@ -618,17 +665,17 @@ class ShakaDemoMain {
618
665
  value = parseFloat(value);
619
666
  }
620
667
 
621
- this.player_.configure(configName, value);
668
+ this.configure(configName, value);
622
669
  }
623
670
  };
624
671
  const config = this.player_.getConfiguration();
625
- ShakaDemoUtils.runThroughHashParams(readParam, config);
672
+ shakaDemo.Utils.runThroughHashParams(readParam, config);
626
673
  }
627
674
  if ('lang' in params) {
628
675
  // Load the legacy 'lang' hash value.
629
676
  const lang = params['lang'];
630
- this.player_.configure('preferredAudioLanguage', lang);
631
- this.player_.configure('preferredTextLanguage', lang);
677
+ this.configure('preferredAudioLanguage', lang);
678
+ this.configure('preferredTextLanguage', lang);
632
679
  this.setUILocale(lang);
633
680
  }
634
681
  if ('uilang' in params) {
@@ -636,10 +683,10 @@ class ShakaDemoMain {
636
683
  // TODO(#1591): Support multiple language preferences
637
684
  }
638
685
  if ('noadaptation' in params) {
639
- this.player_.configure('abr.enabled', false);
686
+ this.configure('abr.enabled', false);
640
687
  }
641
688
  if ('jumpLargeGaps' in params) {
642
- this.player_.configure('streaming.jumpLargeGaps', true);
689
+ this.configure('streaming.jumpLargeGaps', true);
643
690
  }
644
691
 
645
692
  // Add compiled/uncompiled links.
@@ -675,7 +722,7 @@ class ShakaDemoMain {
675
722
  this.nativeControlsEnabled_ = 'nativecontrols' in params;
676
723
 
677
724
  // Check if uncompiled mode is supported.
678
- if (!ShakaDemoUtils.browserSupportsUncompiledMode()) {
725
+ if (!shakaDemo.Utils.browserSupportsUncompiledMode()) {
679
726
  const uncompiledLink = document.getElementById('uncompiled-link');
680
727
  uncompiledLink.setAttribute('disabled', '');
681
728
  uncompiledLink.removeAttribute('href');
@@ -751,7 +798,7 @@ class ShakaDemoMain {
751
798
  * @return {*}
752
799
  */
753
800
  getCurrentConfigValue(valueName) {
754
- const config = this.player_.getConfiguration();
801
+ const config = this.desiredConfig_;
755
802
  return this.getValueFromGivenConfig_(valueName, config);
756
803
  }
757
804
 
@@ -767,12 +814,18 @@ class ShakaDemoMain {
767
814
  * @param {*=} value
768
815
  */
769
816
  configure(config, value) {
817
+ if (arguments.length == 2 && typeof(config) == 'string') {
818
+ config = shaka.util.ConfigUtils.convertToConfigObject(config, value);
819
+ }
820
+ const asObj = /** @type {!Object} */ (config);
821
+ shaka.util.PlayerConfiguration.mergeConfigObjects(
822
+ this.desiredConfig_, asObj, this.defaultConfig_);
770
823
  this.player_.configure(config, value);
771
824
  }
772
825
 
773
826
  /** @return {!shaka.extern.PlayerConfiguration} */
774
827
  getConfiguration() {
775
- return this.player_.getConfiguration();
828
+ return this.desiredConfig_;
776
829
  }
777
830
 
778
831
  /**
@@ -793,13 +846,24 @@ class ShakaDemoMain {
793
846
  this.selectedAsset = null;
794
847
  const videoBar = document.getElementById('video-bar');
795
848
  this.hideNode_(videoBar);
796
- this.video_.poster = ShakaDemoMain.mainPoster_;
849
+ this.video_.poster = shakaDemo.Main.mainPoster_;
797
850
 
851
+ if (document.fullscreenElement) {
852
+ document.exitFullscreen();
853
+ }
854
+ if (document.pictureInPictureElement) {
855
+ document.exitPictureInPicture();
856
+ }
798
857
  this.player_.unload();
799
858
 
800
859
  // The currently-selected asset changed, so update asset cards.
801
860
  this.dispatchEventWithName_('shaka-main-selected-asset-changed');
802
861
 
862
+ // Unset media session title, but only if the browser supports that API.
863
+ if (navigator.mediaSession) {
864
+ navigator.mediaSession.metadata = null;
865
+ }
866
+
803
867
  // Remake hash, to change the current asset.
804
868
  this.remakeHash();
805
869
  }
@@ -821,8 +885,11 @@ class ShakaDemoMain {
821
885
  if (storage) {
822
886
  storage.configure(assetConfig);
823
887
  } else {
824
- this.player_.resetConfiguration();
888
+ // Re-apply the desired config, to remove previous asset configuration
889
+ // without removing previous user configuration.
890
+ this.player_.configure(this.desiredConfig_);
825
891
  this.player_.configure(assetConfig);
892
+ // This uses Player.configure so as to not change |this.desiredConfig_|.
826
893
  }
827
894
 
828
895
  const config = storage ?
@@ -868,7 +935,7 @@ class ShakaDemoMain {
868
935
  const videoBar = document.getElementById('video-bar');
869
936
  this.showNode_(videoBar);
870
937
  this.closeError_();
871
- this.video_.poster = ShakaDemoMain.mainPoster_;
938
+ this.video_.poster = shakaDemo.Main.mainPoster_;
872
939
 
873
940
  // Scroll to the top of the page, so that if the page is scrolled down,
874
941
  // the user won't need to manually scroll up to see the video.
@@ -916,7 +983,19 @@ class ShakaDemoMain {
916
983
  null) || asset.manifestUri;
917
984
  await this.player_.load(manifestUri);
918
985
  if (this.player_.isAudioOnly()) {
919
- this.video_.poster = ShakaDemoMain.audioOnlyPoster_;
986
+ this.video_.poster = shakaDemo.Main.audioOnlyPoster_;
987
+ }
988
+
989
+ // Set media session title, but only if the browser supports that API.
990
+ if (navigator.mediaSession) {
991
+ const metadata = {
992
+ title: asset.name,
993
+ artwork: [{src: asset.iconUri}],
994
+ };
995
+ if (asset.source != shakaAssets.Source.UNKNOWN) {
996
+ metadata.artist = asset.source;
997
+ }
998
+ navigator.mediaSession.metadata = new MediaMetadata(metadata);
920
999
  }
921
1000
  } catch (reason) {
922
1001
  const error = /** @type {!shaka.util.Error} */ (reason);
@@ -956,7 +1035,7 @@ class ShakaDemoMain {
956
1035
  }
957
1036
  };
958
1037
  const config = this.player_.getConfiguration();
959
- ShakaDemoUtils.runThroughHashParams(setParam, config);
1038
+ shakaDemo.Utils.runThroughHashParams(setParam, config);
960
1039
  }
961
1040
  if (!this.getCurrentConfigValue('abr.enabled')) {
962
1041
  params.push('noadaptation');
@@ -973,7 +1052,7 @@ class ShakaDemoMain {
973
1052
  const uri = this.selectedAsset.licenseServers.values().next().value;
974
1053
  params.push('license=' + uri);
975
1054
  for (const drmSystem of this.selectedAsset.licenseServers.keys()) {
976
- if (!ShakaDemoMain.commonDrmSystems.includes(drmSystem)) {
1055
+ if (!shakaDemo.Main.commonDrmSystems.includes(drmSystem)) {
977
1056
  params.push('drmSystem=' + drmSystem);
978
1057
  break;
979
1058
  }
@@ -1000,6 +1079,10 @@ class ShakaDemoMain {
1000
1079
  }
1001
1080
  }
1002
1081
 
1082
+ if (this.noInput_) {
1083
+ params.push('noinput');
1084
+ }
1085
+
1003
1086
  if (this.nativeControlsEnabled_) {
1004
1087
  params.push('nativecontrols');
1005
1088
  }
@@ -1136,8 +1219,12 @@ class ShakaDemoMain {
1136
1219
  document.dispatchEvent(event);
1137
1220
  }
1138
1221
 
1139
- /** @private */
1140
- setUpVersionString_() {
1222
+ /**
1223
+ * Sets the "version-string" divs to a version string.
1224
+ * For example, "v2.5.4-master (uncompiled)".
1225
+ * @private
1226
+ */
1227
+ setUpVersionStrings_() {
1141
1228
  const version = shaka.Player.version;
1142
1229
  let split = version.split('-');
1143
1230
  let inParen = [];
@@ -1152,10 +1239,12 @@ class ShakaDemoMain {
1152
1239
  }
1153
1240
 
1154
1241
  // Put the version into the version string div.
1155
- const versionStringDiv = document.getElementById('version-string');
1156
- versionStringDiv.textContent = split.join('-');
1157
- if (inParen.length > 0) {
1158
- versionStringDiv.textContent += ' (' + inParen.join(', ') + ')';
1242
+ const versionStringDivs = document.getElementsByClassName('version-string');
1243
+ for (const div of versionStringDivs) {
1244
+ div.textContent = split.join('-');
1245
+ if (inParen.length > 0) {
1246
+ div.textContent += ' (' + inParen.join(', ') + ')';
1247
+ }
1159
1248
  }
1160
1249
  }
1161
1250
 
@@ -1245,11 +1334,11 @@ class ShakaDemoMain {
1245
1334
  this.showPlayer_();
1246
1335
  }
1247
1336
  }
1248
- }
1337
+ };
1249
1338
 
1250
1339
 
1251
1340
  /** @type {!Array.<string>} */
1252
- ShakaDemoMain.commonDrmSystems = [
1341
+ shakaDemo.Main.commonDrmSystems = [
1253
1342
  'com.widevine.alpha',
1254
1343
  'com.microsoft.playready',
1255
1344
  'com.apple.fps.1_0',
@@ -1258,14 +1347,14 @@ ShakaDemoMain.commonDrmSystems = [
1258
1347
  ];
1259
1348
 
1260
1349
 
1261
- const shakaDemoMain = new ShakaDemoMain();
1350
+ const shakaDemoMain = new shakaDemo.Main();
1262
1351
 
1263
1352
 
1264
1353
  /**
1265
1354
  * @private
1266
1355
  * @const {string}
1267
1356
  */
1268
- ShakaDemoMain.mainPoster_ =
1357
+ shakaDemo.Main.mainPoster_ =
1269
1358
  'https://shaka-player-demo.appspot.com/assets/poster.jpg';
1270
1359
 
1271
1360
 
@@ -1273,7 +1362,7 @@ ShakaDemoMain.mainPoster_ =
1273
1362
  * @private
1274
1363
  * @const {string}
1275
1364
  */
1276
- ShakaDemoMain.audioOnlyPoster_ =
1365
+ shakaDemo.Main.audioOnlyPoster_ =
1277
1366
  'https://shaka-player-demo.appspot.com/assets/audioOnly.gif';
1278
1367
 
1279
1368
 
@@ -1286,7 +1375,7 @@ ShakaDemoMain.audioOnlyPoster_ =
1286
1375
  * @return {!Promise}
1287
1376
  * @suppress {accessControls}
1288
1377
  */
1289
- ShakaDemoMain.initWrapper = async (initFn) => {
1378
+ shakaDemo.Main.initWrapper = async (initFn) => {
1290
1379
  try {
1291
1380
  await initFn();
1292
1381
  } catch (error) {
@@ -1295,8 +1384,8 @@ ShakaDemoMain.initWrapper = async (initFn) => {
1295
1384
  }
1296
1385
  };
1297
1386
  document.addEventListener('shaka-ui-loaded', () => {
1298
- ShakaDemoMain.initWrapper(() => shakaDemoMain.init());
1387
+ shakaDemo.Main.initWrapper(() => shakaDemoMain.init());
1299
1388
  });
1300
1389
  document.addEventListener('shaka-ui-load-failed', (event) => {
1301
- ShakaDemoMain.initWrapper(() => shakaDemoMain.initFailed());
1390
+ shakaDemo.Main.initWrapper(() => shakaDemoMain.initFailed());
1302
1391
  });