shaka-player 3.1.0 → 3.1.4

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 (181) hide show
  1. package/AUTHORS +8 -2
  2. package/CHANGELOG.md +209 -0
  3. package/CONTRIBUTORS +10 -3
  4. package/README.md +3 -0
  5. package/build/apps.py +2 -2
  6. package/build/check.py +2 -2
  7. package/build/conformance.textproto +0 -9
  8. package/build/generateExterns.js +1 -1
  9. package/build/misspellings.txt +1 -0
  10. package/build/shakaBuildHelpers.py +6 -5
  11. package/build/subprocessWindowsPatch.py +103 -0
  12. package/demo/common/asset.js +11 -0
  13. package/demo/common/assets.js +1 -1
  14. package/demo/common/message_ids.js +5 -0
  15. package/demo/config.js +2 -1
  16. package/demo/custom.js +84 -3
  17. package/demo/demo.less +13 -1
  18. package/demo/demo_utils.js +1 -0
  19. package/demo/input.js +31 -12
  20. package/demo/input_container.js +24 -1
  21. package/demo/locales/en.json +6 -1
  22. package/demo/locales/source.json +20 -0
  23. package/demo/main.js +5 -1
  24. package/dist/controls.css +2 -2
  25. package/dist/controls.css.map +1 -1
  26. package/dist/demo.compiled.debug.js +99 -91
  27. package/dist/demo.compiled.debug.map +3 -3
  28. package/dist/demo.compiled.js +99 -91
  29. package/dist/demo.compiled.map +3 -3
  30. package/dist/demo.css +1 -1
  31. package/dist/demo.css.map +1 -1
  32. package/dist/deps.js +9 -9
  33. package/dist/locales.js +36 -34
  34. package/dist/receiver.compiled.debug.js +74 -70
  35. package/dist/receiver.compiled.debug.map +3 -3
  36. package/dist/receiver.compiled.js +74 -70
  37. package/dist/receiver.compiled.map +3 -3
  38. package/dist/shaka-player.compiled.d.ts +165 -9
  39. package/dist/shaka-player.compiled.debug.d.ts +165 -9
  40. package/dist/shaka-player.compiled.debug.externs.js +179 -11
  41. package/dist/shaka-player.compiled.debug.js +367 -339
  42. package/dist/shaka-player.compiled.debug.map +4 -4
  43. package/dist/shaka-player.compiled.externs.js +179 -11
  44. package/dist/shaka-player.compiled.js +818 -807
  45. package/dist/shaka-player.compiled.map +4 -4
  46. package/dist/shaka-player.ui.d.ts +165 -9
  47. package/dist/shaka-player.ui.debug.d.ts +165 -9
  48. package/dist/shaka-player.ui.debug.externs.js +181 -13
  49. package/dist/shaka-player.ui.debug.js +459 -429
  50. package/dist/shaka-player.ui.debug.map +4 -4
  51. package/dist/shaka-player.ui.externs.js +181 -13
  52. package/dist/shaka-player.ui.js +973 -961
  53. package/dist/shaka-player.ui.map +4 -4
  54. package/docs/tutorials/fairplay.md +1 -1
  55. package/docs/tutorials/faq.md +25 -0
  56. package/docs/tutorials/index.json +2 -2
  57. package/docs/tutorials/upgrade-v3.md +38 -0
  58. package/docs/tutorials/upgrade.md +3 -3
  59. package/externs/ima.js +4 -0
  60. package/externs/media_capabilities.js +16 -0
  61. package/externs/shaka/ads.js +2 -0
  62. package/externs/shaka/net.js +2 -2
  63. package/externs/shaka/player.js +6 -0
  64. package/karma.conf.js +1 -0
  65. package/lib/abr/simple_abr_manager.js +1 -1
  66. package/lib/ads/ad_manager.js +29 -11
  67. package/lib/ads/client_side_ad_manager.js +76 -86
  68. package/lib/ads/server_side_ad_manager.js +26 -11
  69. package/lib/cast/cast_proxy.js +3 -2
  70. package/lib/cast/cast_receiver.js +2 -1
  71. package/lib/cast/cast_sender.js +2 -2
  72. package/lib/cea/sei_processor.js +24 -17
  73. package/lib/dash/dash_parser.js +107 -4
  74. package/lib/dash/segment_list.js +1 -1
  75. package/lib/dash/segment_template.js +45 -15
  76. package/lib/dependencies/all.js +15 -15
  77. package/lib/hls/hls_parser.js +46 -18
  78. package/lib/media/closed_caption_parser.js +3 -1
  79. package/lib/media/content_workarounds.js +18 -0
  80. package/lib/media/drm_engine.js +20 -1
  81. package/lib/media/gap_jumping_controller.js +8 -4
  82. package/lib/media/media_source_engine.js +1 -2
  83. package/lib/media/playhead.js +1 -1
  84. package/lib/media/presentation_timeline.js +3 -1
  85. package/lib/media/segment_index.js +38 -12
  86. package/lib/media/segment_reference.js +8 -0
  87. package/lib/media/streaming_engine.js +30 -21
  88. package/lib/net/http_fetch_plugin.js +2 -2
  89. package/lib/net/networking_engine.js +3 -2
  90. package/lib/offline/storage.js +4 -0
  91. package/lib/player.js +113 -56
  92. package/lib/polyfill/encryption_scheme.js +2 -0
  93. package/lib/polyfill/fullscreen.js +2 -0
  94. package/lib/polyfill/mathround.js +2 -0
  95. package/lib/polyfill/media_capabilities.js +62 -23
  96. package/lib/polyfill/mediasource.js +8 -0
  97. package/lib/polyfill/orientation.js +3 -1
  98. package/lib/polyfill/patchedmediakeys_apple.js +6 -4
  99. package/lib/polyfill/patchedmediakeys_ms.js +6 -4
  100. package/lib/polyfill/patchedmediakeys_nop.js +2 -0
  101. package/lib/polyfill/patchedmediakeys_webkit.js +6 -4
  102. package/lib/polyfill/pip_webkit.js +2 -0
  103. package/lib/polyfill/storage_estimate.js +2 -0
  104. package/lib/polyfill/video_play_promise.js +2 -0
  105. package/lib/polyfill/videoplaybackquality.js +2 -0
  106. package/lib/polyfill/vttcue.js +2 -0
  107. package/lib/text/cue.js +11 -0
  108. package/lib/text/mp4_ttml_parser.js +3 -0
  109. package/lib/text/text_engine.js +14 -2
  110. package/lib/text/ttml_text_parser.js +52 -30
  111. package/lib/text/ui_text_displayer.js +255 -82
  112. package/lib/text/vtt_text_parser.js +4 -2
  113. package/lib/util/error.js +1 -4
  114. package/lib/util/fake_event.js +53 -7
  115. package/lib/util/mime_utils.js +33 -0
  116. package/lib/util/mp4_box_parsers.js +1 -1
  117. package/lib/util/mp4_parser.js +14 -0
  118. package/lib/util/networking.js +1 -1
  119. package/lib/util/periods.js +132 -37
  120. package/lib/util/platform.js +13 -1
  121. package/lib/util/stream_utils.js +46 -7
  122. package/package.json +3 -3
  123. package/test/cast/cast_proxy_unit.js +12 -6
  124. package/test/cast/cast_receiver_integration.js +1 -1
  125. package/test/cast/cast_utils_unit.js +1 -1
  126. package/test/cea/mp4_cea_parser_unit.js +26 -0
  127. package/test/dash/dash_parser_live_unit.js +1 -0
  128. package/test/dash/dash_parser_manifest_unit.js +187 -1
  129. package/test/dash/dash_parser_segment_template_unit.js +3 -3
  130. package/test/demo/demo_unit.js +0 -1
  131. package/test/hls/hls_parser_unit.js +45 -1
  132. package/test/media/closed_caption_parser_unit.js +20 -0
  133. package/test/media/drm_engine_unit.js +59 -3
  134. package/test/media/playhead_unit.js +27 -1
  135. package/test/media/presentation_timeline_unit.js +19 -0
  136. package/test/media/segment_index_unit.js +76 -3
  137. package/test/media/streaming_engine_unit.js +3 -3
  138. package/test/player_integration.js +38 -0
  139. package/test/player_unit.js +125 -6
  140. package/test/test/assets/3675/chunk-stream0-00001.m4s +0 -0
  141. package/test/test/assets/3675/chunk-stream0-00002.m4s +0 -0
  142. package/test/test/assets/3675/chunk-stream0-00003.m4s +0 -0
  143. package/test/test/assets/3675/chunk-stream0-00004.m4s +0 -0
  144. package/test/test/assets/3675/chunk-stream0-00005.m4s +0 -0
  145. package/test/test/assets/3675/chunk-stream0-00006.m4s +0 -0
  146. package/test/test/assets/3675/chunk-stream0-00007.m4s +0 -0
  147. package/test/test/assets/3675/dash_0.mpd +30 -0
  148. package/test/test/assets/3675/dash_1.mpd +30 -0
  149. package/test/test/assets/3675/dash_2.mpd +30 -0
  150. package/test/test/assets/3675/dash_3.mpd +30 -0
  151. package/test/test/assets/3675/dash_4.mpd +30 -0
  152. package/test/test/assets/3675/dash_5.mpd +26 -0
  153. package/test/test/assets/3675/init-stream0.m4s +0 -0
  154. package/test/test/assets/empty_caption_video_init.mp4 +0 -0
  155. package/test/test/assets/empty_caption_video_segment.mp4 +0 -0
  156. package/test/test/util/fake_ad_manager.js +4 -1
  157. package/test/test/util/simple_fakes.js +5 -3
  158. package/test/test/util/waiter.js +36 -2
  159. package/test/text/ttml_text_parser_unit.js +55 -0
  160. package/test/text/ui_text_displayer_unit.js +95 -18
  161. package/test/text/vtt_text_parser_unit.js +17 -1
  162. package/test/ui/localization_unit.js +15 -14
  163. package/test/ui/text_displayer_layout_unit.js +52 -1
  164. package/test/ui/ui_unit.js +4 -0
  165. package/test/util/mp4_box_parsers_unit.js +28 -0
  166. package/test/util/periods_unit.js +190 -2
  167. package/test/util/stream_utils_unit.js +56 -5
  168. package/ui/airplay_button.js +5 -0
  169. package/ui/big_play_button.js +10 -0
  170. package/ui/cast_button.js +2 -3
  171. package/ui/controls.js +9 -7
  172. package/ui/less/containers.less +10 -5
  173. package/ui/locales/en.json +1 -0
  174. package/ui/locales/ru.json +6 -6
  175. package/ui/locales/source.json +4 -0
  176. package/ui/localization.js +28 -51
  177. package/ui/pip_button.js +2 -3
  178. package/ui/play_button.js +10 -10
  179. package/ui/small_play_button.js +12 -0
  180. package/ui/ui.js +1 -1
  181. package/docs/tutorials/upgrade-v2-4.md +0 -481
package/lib/player.js CHANGED
@@ -596,7 +596,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
596
596
  enterNode: (node, has, wants) => {
597
597
  this.dispatchEvent(this.makeEvent_(
598
598
  /* name= */ shaka.Player.EventName.OnStateChange,
599
- /* data= */ {'state': node.name}));
599
+ /* data= */ (new Map()).set('state', node.name)));
600
600
 
601
601
  const action = actions.get(node);
602
602
  return action(has, wants);
@@ -627,7 +627,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
627
627
  onIdle: (node) => {
628
628
  this.dispatchEvent(this.makeEvent_(
629
629
  /* name= */ shaka.Player.EventName.OnStateIdle,
630
- /* data= */ {'state': node.name}));
630
+ /* data= */ (new Map()).set('state', node.name)));
631
631
  },
632
632
  };
633
633
 
@@ -647,7 +647,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
647
647
 
648
648
  /**
649
649
  * @param {!shaka.Player.EventName} name
650
- * @param {Object=} data
650
+ * @param {Map.<string, Object>=} data
651
651
  * @return {!shaka.util.FakeEvent}
652
652
  * @private
653
653
  */
@@ -1155,6 +1155,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
1155
1155
  'mpg': 'video/mpeg',
1156
1156
  'mpeg': 'video/mpeg',
1157
1157
  'm3u8': 'application/x-mpegurl',
1158
+ 'mpd': 'application/dash+xml',
1158
1159
  'mp3': 'audio/mpeg',
1159
1160
  'aac': 'audio/aac',
1160
1161
  'flac': 'audio/flac',
@@ -1285,6 +1286,11 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
1285
1286
  has.mediaElement = null;
1286
1287
  }
1287
1288
 
1289
+ if (this.adManager_) {
1290
+ // The ad manager is specific to the video, so detach it too.
1291
+ this.adManager_.release();
1292
+ }
1293
+
1288
1294
  // Clear our cached copy of the media element.
1289
1295
  this.video_ = null;
1290
1296
 
@@ -1843,6 +1849,8 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
1843
1849
  this.config_.preferredAudioChannelCount);
1844
1850
 
1845
1851
  this.currentTextLanguage_ = this.config_.preferredTextLanguage;
1852
+ this.currentTextRole_ = this.config_.preferredTextRole;
1853
+ this.currentTextForced_ = this.config_.preferForcedSubs;
1846
1854
 
1847
1855
  shaka.Player.applyPlayRange_(this.manifest_.presentationTimeline,
1848
1856
  this.config_.playRangeStart,
@@ -1882,36 +1890,55 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
1882
1890
  this.dispatchEvent(this.makeEvent_(shaka.Player.EventName.Streaming));
1883
1891
 
1884
1892
  // Pick the initial streams to play.
1885
- const initialVariant = this.chooseVariant_();
1886
- goog.asserts.assert(initialVariant, 'Must choose an initial variant!');
1887
- this.addVariantToSwitchHistory_(
1888
- initialVariant, /* fromAdaptation= */ true);
1889
- this.streamingEngine_.switchVariant(
1890
- initialVariant, /* clearBuffer= */ false, /* safeMargin= */ 0);
1893
+ // however, we would skip switch to initial variant
1894
+ // if user already pick variant track (via selectVariantTrack api)
1895
+ let initialVariant = null;
1896
+ const activeVariantTrack = this.getVariantTracks().find((t) => t.active);
1897
+
1898
+ if (!activeVariantTrack) {
1899
+ initialVariant = this.chooseVariant_();
1900
+ goog.asserts.assert(initialVariant, 'Must choose an initial variant!');
1901
+ this.switchVariant_(initialVariant, /* fromAdaptation= */ true,
1902
+ /* clearBuffer= */ false, /* safeMargin= */ 0);
1903
+
1904
+ // Now that we have initial streams, we may adjust the start time to align
1905
+ // to a segment boundary.
1906
+ if (this.config_.streaming.startAtSegmentBoundary) {
1907
+ const startTime = this.playhead_.getTime();
1908
+ const adjustedTime =
1909
+ await this.adjustStartTime_(initialVariant, startTime);
1910
+
1911
+ this.playhead_.setStartTime(adjustedTime);
1912
+ }
1891
1913
 
1892
- // Decide if text should be shown automatically.
1893
- const initialTextStream = this.chooseTextStream_();
1894
- if (initialTextStream) {
1895
- this.addTextStreamToSwitchHistory_(
1896
- initialTextStream, /* fromAdaptation= */ true);
1914
+ // Since the first streams just became active, send an adaptation event.
1915
+ this.onAdaptation_();
1897
1916
  }
1898
1917
 
1899
- this.setInitialTextState_(initialVariant, initialTextStream);
1900
- // Don't initialize with a text stream unless we should be streaming text.
1901
- if (initialTextStream && this.shouldStreamText_()) {
1902
- this.streamingEngine_.switchTextStream(initialTextStream);
1903
- }
1918
+ // Decide if text should be shown automatically.
1919
+ // similar to video/audio track, we would skip switch initial text track
1920
+ // if user already pick text track (via selectTextTrack api)
1921
+ const activeTextTrack = this.getTextTracks().find((t) => t.active);
1922
+
1923
+ if (!activeTextTrack) {
1924
+ const initialTextStream = this.chooseTextStream_();
1904
1925
 
1905
- // Now that we have initial streams, we may adjust the start time to align
1906
- // to a segment boundary.
1907
- if (this.config_.streaming.startAtSegmentBoundary) {
1908
- const startTime = this.playhead_.getTime();
1909
- const adjustedTime =
1910
- await this.adjustStartTime_(initialVariant, startTime);
1926
+ if (initialTextStream) {
1927
+ this.addTextStreamToSwitchHistory_(
1928
+ initialTextStream, /* fromAdaptation= */ true);
1929
+ }
1911
1930
 
1912
- this.playhead_.setStartTime(adjustedTime);
1931
+ if (initialVariant) {
1932
+ this.setInitialTextState_(initialVariant, initialTextStream);
1933
+ }
1934
+
1935
+ // Don't initialize with a text stream unless we should be streaming text.
1936
+ if (initialTextStream && this.shouldStreamText_()) {
1937
+ this.streamingEngine_.switchTextStream(initialTextStream);
1938
+ }
1913
1939
  }
1914
1940
 
1941
+
1915
1942
  // Start streaming content. This will start the flow of content down to
1916
1943
  // media source.
1917
1944
  await this.streamingEngine_.start();
@@ -1925,8 +1952,6 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
1925
1952
  this.filterManifestByCurrentVariant_();
1926
1953
  // Dispatch a 'trackschanged' event now that all initial filtering is done.
1927
1954
  this.onTracksChanged_();
1928
- // Since the first streams just became active, send an adaptation event.
1929
- this.onAdaptation_();
1930
1955
 
1931
1956
  // Now that we've filtered out variants that aren't compatible with the
1932
1957
  // active one, update abr manager with filtered variants.
@@ -2019,8 +2044,10 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
2019
2044
  originalId: null,
2020
2045
  createSegmentIndex: () => Promise.resolve(),
2021
2046
  segmentIndex: null,
2022
- mimeType: 'video/mp4',
2023
- codecs: '',
2047
+ mimeType: wants.mimeType ?
2048
+ shaka.util.MimeUtils.getBasicType(wants.mimeType) : 'video/mp4',
2049
+ codecs: wants.mimeType ?
2050
+ shaka.util.MimeUtils.getCodecs(wants.mimeType) : '',
2024
2051
  encrypted: true,
2025
2052
  drmInfos: [], // Filled in by DrmEngine config.
2026
2053
  keyIds: new Set(),
@@ -2043,6 +2070,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
2043
2070
  decodingInfos: [],
2044
2071
  };
2045
2072
 
2073
+ this.drmEngine_.setSrcEquals(/* srcEquals= */ true);
2046
2074
  await this.drmEngine_.initForPlayback(
2047
2075
  [variant], /* offlineSessionIds= */ []);
2048
2076
  await this.drmEngine_.attach(has.mediaElement);
@@ -2388,12 +2416,11 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
2388
2416
  goog.asserts.assert(!endTime || startTime <= endTime,
2389
2417
  'Metadata start time should be less or equal to the end time!');
2390
2418
  const eventName = shaka.Player.EventName.Metadata;
2391
- const data = {
2392
- startTime: startTime,
2393
- endTime: endTime,
2394
- metadataType: metadataType,
2395
- payload: payload,
2396
- };
2419
+ const data = new Map()
2420
+ .set('startTime', startTime)
2421
+ .set('endTime', endTime)
2422
+ .set('metadataType', metadataType)
2423
+ .set('payload', payload);
2397
2424
  this.dispatchEvent(this.makeEvent_(eventName, data));
2398
2425
  }
2399
2426
 
@@ -2455,7 +2482,6 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
2455
2482
  this.abrManager_.segmentDownloaded(deltaTimeMs, bytesDownloaded);
2456
2483
  }
2457
2484
  };
2458
-
2459
2485
  return new shaka.net.NetworkingEngine(onProgressUpdated_);
2460
2486
  }
2461
2487
 
@@ -3214,6 +3240,8 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
3214
3240
 
3215
3241
  const tracks = [];
3216
3242
 
3243
+ let activeTracks = 0;
3244
+
3217
3245
  // Convert each variant to a track.
3218
3246
  for (const variant of this.manifest_.variants) {
3219
3247
  if (!shaka.util.StreamUtils.isPlayable(variant)) {
@@ -3222,10 +3250,22 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
3222
3250
 
3223
3251
  const track = shaka.util.StreamUtils.variantToTrack(variant);
3224
3252
  track.active = variant == currentVariant;
3253
+ if (!track.active && activeTracks != 1 && currentVariant != null &&
3254
+ variant.video == currentVariant.video &&
3255
+ variant.audio == currentVariant.audio) {
3256
+ track.active = true;
3257
+ }
3258
+
3259
+ if (track.active) {
3260
+ activeTracks++;
3261
+ }
3225
3262
 
3226
3263
  tracks.push(track);
3227
3264
  }
3228
3265
 
3266
+ goog.asserts.assert(activeTracks <= 1,
3267
+ 'It should only have one active track');
3268
+
3229
3269
  return tracks;
3230
3270
  } else if (this.video_ && this.video_.audioTracks) {
3231
3271
  // Safari's native HLS always shows a single element in videoTracks.
@@ -3330,9 +3370,12 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
3330
3370
  const rows = parseInt(match[2], 10);
3331
3371
  const width = fullImageWidth / columns;
3332
3372
  const height = fullImageHeight / rows;
3373
+ const totalImages = columns * rows;
3374
+ const segmentDuration = reference.trueEndTime - reference.startTime;
3375
+ const thumbnailDuration = segmentDuration / totalImages;
3376
+ let thumbnailTime = reference.startTime;
3333
3377
  let positionX = 0;
3334
3378
  let positionY = 0;
3335
- const totalImages = columns * rows;
3336
3379
  // If the number of images in the segment is greater than 1, we have to
3337
3380
  // find the correct image. For that we will return to the app the
3338
3381
  // coordinates of the position of the correct image.
@@ -3340,21 +3383,23 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
3340
3383
  // Note: The time between images within the segment is always
3341
3384
  // equidistant.
3342
3385
  //
3343
- // Eg: Total images 5, tileLayout 5x1, segmentTime 5, thumbnailTime 2
3386
+ // Eg: Total images 5, tileLayout 5x1, segmentDuration 5, thumbnailTime 2
3344
3387
  // positionX = 0.4 * fullImageWidth
3345
3388
  // positionY = 0
3346
3389
  if (totalImages > 1) {
3347
- const thumbnailTime = time - reference.startTime;
3348
- const segmentTime = reference.endTime - reference.startTime;
3349
3390
  const thumbnailPosition =
3350
- Math.floor(thumbnailTime * totalImages / segmentTime);
3351
- positionX = (thumbnailPosition % columns) / columns * fullImageWidth;
3352
- positionY = (thumbnailPosition % rows) / rows * fullImageHeight;
3391
+ Math.floor((time - reference.startTime) / thumbnailDuration);
3392
+ thumbnailTime = reference.startTime +
3393
+ (thumbnailPosition * thumbnailDuration);
3394
+ positionX = (thumbnailPosition % columns) * width;
3395
+ positionY = Math.floor(thumbnailPosition / columns) * height;
3353
3396
  }
3354
3397
  return {
3355
3398
  height: height,
3356
3399
  positionX: positionX,
3357
3400
  positionY: positionY,
3401
+ startTime: thumbnailTime,
3402
+ duration: thumbnailDuration,
3358
3403
  uris: reference.getUris(),
3359
3404
  width: width,
3360
3405
  };
@@ -4770,7 +4815,8 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
4770
4815
  // Surface the buffering event so that the app knows if/when we are
4771
4816
  // buffering.
4772
4817
  const eventName = shaka.Player.EventName.Buffering;
4773
- this.dispatchEvent(this.makeEvent_(eventName, {'buffering': isBuffering}));
4818
+ const data = (new Map()).set('buffering', isBuffering);
4819
+ this.dispatchEvent(this.makeEvent_(eventName, data));
4774
4820
  }
4775
4821
 
4776
4822
  /**
@@ -4955,6 +5001,11 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
4955
5001
  const currentVariant = this.streamingEngine_.getCurrentVariant();
4956
5002
  if (variant == currentVariant) {
4957
5003
  shaka.log.debug('Variant already selected.');
5004
+ // If you want to clear the buffer, we force to reselect the same variant
5005
+ if (clearBuffer) {
5006
+ this.streamingEngine_.switchVariant(variant, clearBuffer, safeMargin,
5007
+ /* force= */ true);
5008
+ }
4958
5009
  return;
4959
5010
  }
4960
5011
 
@@ -4983,6 +5034,12 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
4983
5034
  // This will reset the "enabled" of other tracks to false.
4984
5035
  track.enabled = true;
4985
5036
 
5037
+ // AirPlay does not reset the "enabled" of other tracks to false, so
5038
+ // it must be changed by hand.
5039
+ if (track.id !== currentTrack.id) {
5040
+ currentTrack.enabled = false;
5041
+ }
5042
+
4986
5043
  const oldTrack =
4987
5044
  shaka.util.StreamUtils.html5AudioTrackToTrack(currentTrack);
4988
5045
  const newTrack =
@@ -5141,10 +5198,10 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
5141
5198
  onVariantChanged_(from, to) {
5142
5199
  // Delay the 'variantchanged' event so StreamingEngine has time to absorb
5143
5200
  // the changes before the user tries to query it.
5144
- const event = this.makeEvent_(shaka.Player.EventName.VariantChanged, {
5145
- oldTrack: from,
5146
- newTrack: to,
5147
- });
5201
+ const data = new Map()
5202
+ .set('oldTrack', from)
5203
+ .set('newTrack', to);
5204
+ const event = this.makeEvent_(shaka.Player.EventName.VariantChanged, data);
5148
5205
  this.delayDispatchEvent_(event);
5149
5206
  }
5150
5207
 
@@ -5167,10 +5224,9 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
5167
5224
 
5168
5225
  /** @private */
5169
5226
  onAbrStatusChanged_() {
5170
- const event = this.makeEvent_(shaka.Player.EventName.AbrStatusChanged, {
5171
- newStatus: this.config_.abr.enabled,
5172
- });
5173
- this.delayDispatchEvent_(event);
5227
+ const data = (new Map()).set('newStatus', this.config_.abr.enabled);
5228
+ this.delayDispatchEvent_(this.makeEvent_(
5229
+ shaka.Player.EventName.AbrStatusChanged, data));
5174
5230
  }
5175
5231
 
5176
5232
  /**
@@ -5187,7 +5243,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
5187
5243
  }
5188
5244
 
5189
5245
  const eventName = shaka.Player.EventName.Error;
5190
- const event = this.makeEvent_(eventName, {'detail': error});
5246
+ const event = this.makeEvent_(eventName, (new Map()).set('detail', error));
5191
5247
  this.dispatchEvent(event);
5192
5248
  if (event.defaultPrevented) {
5193
5249
  error.handled = true;
@@ -5216,7 +5272,8 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
5216
5272
  eventElement: region.eventElement,
5217
5273
  };
5218
5274
 
5219
- this.dispatchEvent(this.makeEvent_(eventName, {detail: clone}));
5275
+ const data = (new Map()).set('detail', clone);
5276
+ this.dispatchEvent(this.makeEvent_(eventName, data));
5220
5277
  }
5221
5278
 
5222
5279
  /**
@@ -6045,7 +6102,7 @@ shaka.Player.TYPICAL_BUFFERING_THRESHOLD_ = 0.5;
6045
6102
  * @define {string} A version number taken from git at compile time.
6046
6103
  * @export
6047
6104
  */
6048
- shaka.Player.version = 'v3.1.0-uncompiled';
6105
+ shaka.Player.version = 'v3.1.4-uncompiled';
6049
6106
 
6050
6107
  // Initialize the deprecation system using the version string we just set
6051
6108
  // on the player.
@@ -13,12 +13,14 @@ goog.require('shaka.polyfill');
13
13
  * @see https://wicg.github.io/encrypted-media-encryption-scheme/
14
14
  * @see https://github.com/w3c/encrypted-media/pull/457
15
15
  * @see https://github.com/google/eme-encryption-scheme-polyfill
16
+ * @export
16
17
  */
17
18
  shaka.polyfill.EncryptionScheme = class {
18
19
  /**
19
20
  * Install the polyfill if needed.
20
21
  *
21
22
  * @suppress {missingRequire}
23
+ * @export
22
24
  */
23
25
  static install() {
24
26
  EncryptionSchemePolyfills.install();
@@ -14,10 +14,12 @@ goog.require('shaka.polyfill');
14
14
  * Many browsers have prefixed fullscreen methods on Element and document.
15
15
  * See {@link https://mzl.la/2K0xcHo Using fullscreen mode} on MDN for more
16
16
  * information.
17
+ * @export
17
18
  */
18
19
  shaka.polyfill.Fullscreen = class {
19
20
  /**
20
21
  * Install the polyfill if needed.
22
+ * @export
21
23
  */
22
24
  static install() {
23
25
  if (!window.Document) {
@@ -11,10 +11,12 @@ goog.require('shaka.polyfill');
11
11
  /**
12
12
  * @summary A polyfill to patch math round bug on some browsers.
13
13
  * @see https://stackoverflow.com/q/12830742
14
+ * @export
14
15
  */
15
16
  shaka.polyfill.MathRound = class {
16
17
  /**
17
18
  * Install the polyfill if needed.
19
+ * @export
18
20
  */
19
21
  static install() {
20
22
  shaka.log.debug('mathRound.install');
@@ -15,10 +15,13 @@ goog.require('shaka.util.Platform');
15
15
  * @summary A polyfill to provide navigator.mediaCapabilities on all browsers.
16
16
  * This is necessary for Tizen 3, Xbox One and possibly others we have yet to
17
17
  * discover.
18
+ * @export
18
19
  */
19
20
  shaka.polyfill.MediaCapabilities = class {
20
21
  /**
21
22
  * Install the polyfill if needed.
23
+ * @suppress {const}
24
+ * @export
22
25
  */
23
26
  static install() {
24
27
  shaka.log.debug('MediaCapabilities: install');
@@ -26,20 +29,32 @@ shaka.polyfill.MediaCapabilities = class {
26
29
  // Since MediaCapabilities is not fully supported on Chromecast yet, we
27
30
  // should always install polyfill for Chromecast.
28
31
  // TODO: re-evaluate MediaCapabilities in the future versions of Chromecast.
29
- if (!shaka.util.Platform.isChromecast() && navigator.mediaCapabilities) {
32
+ // Since MediaCapabilities implementation is buggy in Apple browsers, we
33
+ // should always install polyfill for Apple browsers.
34
+ // See: https://github.com/google/shaka-player/issues/3530
35
+ // TODO: re-evaluate MediaCapabilities in the future versions of Apple
36
+ // Browsers.
37
+ // Since MediaCapabilities implementation is buggy in PS5 browsers, we
38
+ // should always install polyfill for PS5 browsers.
39
+ // See: https://github.com/google/shaka-player/issues/3582
40
+ // TODO: re-evaluate MediaCapabilities in the future versions of PS5
41
+ // Browsers.
42
+ if (!shaka.util.Platform.isChromecast() &&
43
+ !shaka.util.Platform.isApple() &&
44
+ !shaka.util.Platform.isPS5() &&
45
+ navigator.mediaCapabilities) {
30
46
  shaka.log.debug(
31
47
  'MediaCapabilities: Native mediaCapabilities support found.');
32
- return;
33
- } else if (!window.MediaSource) {
34
- shaka.log.debug(
35
- 'MediaSource must be available to install mediaCapabilities ',
36
- 'polyfill.');
37
- return;
48
+ } else {
49
+ if (!navigator.mediaCapabilities) {
50
+ navigator.mediaCapabilities = /** @type {!MediaCapabilities} */ ({});
51
+ }
52
+ navigator.mediaCapabilities.decodingInfo =
53
+ shaka.polyfill.MediaCapabilities.decodingInfo_;
38
54
  }
39
-
40
- navigator.mediaCapabilities = /** @type {!MediaCapabilities} */ ({});
41
- navigator.mediaCapabilities.decodingInfo =
42
- shaka.polyfill.MediaCapabilities.decodingInfo_;
55
+ // Workaround because in Apple browsers there is no way to overwrite
56
+ // MediaCapabilities.
57
+ window.shakaMediaCapabilities = navigator.mediaCapabilities;
43
58
  }
44
59
 
45
60
  /**
@@ -60,21 +75,45 @@ shaka.polyfill.MediaCapabilities = class {
60
75
  return res;
61
76
  }
62
77
 
63
- // Use 'MediaSource.isTypeSupported' to check if the stream is supported.
64
- if (mediaDecodingConfig['video']) {
65
- const contentType = mediaDecodingConfig['video'].contentType;
66
- const isSupported = MediaSource.isTypeSupported(contentType);
67
- if (!isSupported) {
78
+ if (mediaDecodingConfig.type == 'media-source') {
79
+ if (!shaka.util.Platform.supportsMediaSource()) {
68
80
  return res;
69
81
  }
70
- }
82
+ // Use 'MediaSource.isTypeSupported' to check if the stream is supported.
83
+ if (mediaDecodingConfig['video']) {
84
+ const contentType = mediaDecodingConfig['video'].contentType;
85
+ const isSupported = MediaSource.isTypeSupported(contentType);
86
+ if (!isSupported) {
87
+ return res;
88
+ }
89
+ }
71
90
 
72
- if (mediaDecodingConfig['audio']) {
73
- const contentType = mediaDecodingConfig['audio'].contentType;
74
- const isSupported = MediaSource.isTypeSupported(contentType);
75
- if (!isSupported) {
76
- return res;
91
+ if (mediaDecodingConfig['audio']) {
92
+ const contentType = mediaDecodingConfig['audio'].contentType;
93
+ const isSupported = MediaSource.isTypeSupported(contentType);
94
+ if (!isSupported) {
95
+ return res;
96
+ }
97
+ }
98
+ } else if (mediaDecodingConfig.type == 'file') {
99
+ if (mediaDecodingConfig['video']) {
100
+ const contentType = mediaDecodingConfig['video'].contentType;
101
+ const isSupported = shaka.util.Platform.supportsMediaType(contentType);
102
+ if (!isSupported) {
103
+ return res;
104
+ }
105
+ }
106
+
107
+ if (mediaDecodingConfig['audio']) {
108
+ const contentType = mediaDecodingConfig['audio'].contentType;
109
+ const isSupported = shaka.util.Platform.supportsMediaType(contentType);
110
+ if (!isSupported) {
111
+ return res;
112
+ }
77
113
  }
114
+ } else {
115
+ // Otherwise not supported.
116
+ return res;
78
117
  }
79
118
 
80
119
  if (!mediaDecodingConfig.keySystemConfiguration) {
@@ -116,7 +155,7 @@ shaka.polyfill.MediaCapabilities = class {
116
155
  sessionTypes: mediaCapkeySystemConfig.sessionTypes,
117
156
  };
118
157
 
119
- // Only add the audio video capablities if they have valid data.
158
+ // Only add the audio video capabilities if they have valid data.
120
159
  // Otherwise the query will fail.
121
160
  if (audioCapabilities.length) {
122
161
  mediaKeySystemConfig.audioCapabilities = audioCapabilities;
@@ -13,10 +13,12 @@ goog.require('shaka.util.Platform');
13
13
 
14
14
  /**
15
15
  * @summary A polyfill to patch MSE bugs.
16
+ * @export
16
17
  */
17
18
  shaka.polyfill.MediaSource = class {
18
19
  /**
19
20
  * Install the polyfill if needed.
21
+ * @export
20
22
  */
21
23
  static install() {
22
24
  shaka.log.debug('MediaSource.install');
@@ -206,6 +208,12 @@ shaka.polyfill.MediaSource = class {
206
208
  static patchVp09_() {
207
209
  const originalIsTypeSupported = MediaSource.isTypeSupported;
208
210
 
211
+ if (shaka.util.Platform.isWebOS()) {
212
+ // Don't do this on LG webOS as otherwise it is unable
213
+ // to play vp09 at all.
214
+ return;
215
+ }
216
+
209
217
  MediaSource.isTypeSupported = (mimeType) => {
210
218
  // Split the MIME type into its various parameters.
211
219
  const pieces = mimeType.split(/ *; */);
@@ -15,10 +15,12 @@ goog.require('shaka.polyfill');
15
15
  * @summary A polyfill for systems that do not implement screen.orientation.
16
16
  * For now, this only handles systems that implement the deprecated
17
17
  * window.orientation feature... e.g. iPad.
18
+ * @export
18
19
  */
19
20
  shaka.polyfill.Orientation = class {
20
21
  /**
21
22
  * Install the polyfill if needed.
23
+ * @export
22
24
  */
23
25
  static install() {
24
26
  if (screen.orientation) {
@@ -90,7 +92,7 @@ class extends shaka.util.FakeEventTarget {
90
92
 
91
93
  /** Dispatch a 'change' event. */
92
94
  dispatchChangeEvent() {
93
- const event = new shaka.util.FakeEvent('change', {});
95
+ const event = new shaka.util.FakeEvent('change');
94
96
  this.dispatchEvent(event);
95
97
  }
96
98
 
@@ -22,10 +22,12 @@ goog.require('shaka.util.StringUtils');
22
22
  /**
23
23
  * @summary A polyfill to implement modern, standardized EME on top of Apple's
24
24
  * prefixed EME in Safari.
25
+ * @export
25
26
  */
26
27
  shaka.polyfill.PatchedMediaKeysApple = class {
27
28
  /**
28
29
  * Installs the polyfill if needed.
30
+ * @export
29
31
  */
30
32
  static install() {
31
33
  if (!window.HTMLVideoElement || !window.WebKitMediaKeys) {
@@ -565,10 +567,10 @@ class extends shaka.util.FakeEventTarget {
565
567
 
566
568
  const isNew = this.keyStatuses.getStatus() == undefined;
567
569
 
568
- const event2 = new shaka.util.FakeEvent('message', {
569
- messageType: isNew ? 'license-request' : 'license-renewal',
570
- message: shaka.util.BufferUtils.toArrayBuffer(event.message),
571
- });
570
+ const data = new Map()
571
+ .set('messageType', isNew ? 'license-request' : 'license-renewal')
572
+ .set('message', shaka.util.BufferUtils.toArrayBuffer(event.message));
573
+ const event2 = new shaka.util.FakeEvent('message', data);
572
574
 
573
575
  this.dispatchEvent(event2);
574
576
  }
@@ -24,10 +24,12 @@ goog.require('shaka.util.PublicPromise');
24
24
  * {@link https://bit.ly/EmeMar15 EME draft 12 March 2015}
25
25
  * on top of ms-prefixed
26
26
  * {@link https://www.w3.org/TR/2014/WD-encrypted-media-20140218/ EME v20140218}
27
+ * @export
27
28
  */
28
29
  shaka.polyfill.PatchedMediaKeysMs = class {
29
30
  /**
30
31
  * Installs the polyfill if needed.
32
+ * @export
31
33
  */
32
34
  static install() {
33
35
  if (!window.HTMLVideoElement || !window.MSMediaKeys ||
@@ -510,10 +512,10 @@ class extends shaka.util.FakeEventTarget {
510
512
 
511
513
  const isNew = this.keyStatuses.getStatus() == undefined;
512
514
 
513
- const event2 = new shaka.util.FakeEvent('message', {
514
- messageType: isNew ? 'license-request' : 'license-renewal',
515
- message: shaka.util.BufferUtils.toArrayBuffer(event.message),
516
- });
515
+ const data = new Map()
516
+ .set('messageType', isNew ? 'license-request' : 'license-renewal')
517
+ .set('message', shaka.util.BufferUtils.toArrayBuffer(event.message));
518
+ const event2 = new shaka.util.FakeEvent('message', data);
517
519
 
518
520
  this.dispatchEvent(event2);
519
521
  }
@@ -16,10 +16,12 @@ goog.require('shaka.polyfill');
16
16
  * {@link https://bit.ly/EmeMar15 EME draft 12 March 2015} on browsers without
17
17
  * EME.
18
18
  * All methods will fail.
19
+ * @export
19
20
  */
20
21
  shaka.polyfill.PatchedMediaKeysNop = class {
21
22
  /**
22
23
  * Installs the polyfill if needed.
24
+ * @export
23
25
  */
24
26
  static install() {
25
27
  if (!window.HTMLVideoElement ||
@@ -24,10 +24,12 @@ goog.require('shaka.util.Uint8ArrayUtils');
24
24
  * @summary A polyfill to implement
25
25
  * {@link https://bit.ly/EmeMar15 EME draft 12 March 2015} on top of
26
26
  * webkit-prefixed {@link https://bit.ly/Eme01b EME v0.1b}.
27
+ * @export
27
28
  */
28
29
  shaka.polyfill.PatchedMediaKeysWebkit = class {
29
30
  /**
30
31
  * Installs the polyfill if needed.
32
+ * @export
31
33
  */
32
34
  static install() {
33
35
  // Alias.
@@ -449,10 +451,10 @@ shaka.polyfill.PatchedMediaKeysWebkit.MediaKeys = class {
449
451
 
450
452
  const isNew = session.keyStatuses.getStatus() == undefined;
451
453
 
452
- const event2 = new shaka.util.FakeEvent('message', {
453
- messageType: isNew ? 'licenserequest' : 'licenserenewal',
454
- message: event.message,
455
- });
454
+ const data = new Map()
455
+ .set('messageType', isNew ? 'licenserequest' : 'licenserenewal')
456
+ .set('message', event.message);
457
+ const event2 = new shaka.util.FakeEvent('message', data);
456
458
 
457
459
  session.generated();
458
460
  session.dispatchEvent(event2);