shaka-player 4.4.3 → 4.6.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 (307) hide show
  1. package/.github/ISSUE_TEMPLATE/bug_report.md +1 -1
  2. package/.github/workflows/release-please.yaml +2 -27
  3. package/AUTHORS +2 -0
  4. package/CHANGELOG.md +156 -39
  5. package/CODE_OF_CONDUCT.md +1 -1
  6. package/CONTRIBUTORS +2 -0
  7. package/README.md +54 -42
  8. package/demo/asset_card.js +18 -21
  9. package/demo/cast_receiver/index.html +0 -1
  10. package/demo/close_button.js +0 -3
  11. package/demo/common/asset.js +33 -22
  12. package/demo/common/assets.js +249 -60
  13. package/demo/config.js +219 -253
  14. package/demo/custom.js +88 -133
  15. package/demo/front.js +7 -10
  16. package/demo/index.html +19 -22
  17. package/demo/input.js +6 -11
  18. package/demo/input_container.js +8 -9
  19. package/demo/main.js +62 -126
  20. package/demo/search.js +29 -34
  21. package/demo/service_worker.js +0 -3
  22. package/demo/tooltip.js +3 -5
  23. package/demo/visualizer.js +3 -5
  24. package/demo/visualizer_button.js +1 -20
  25. package/dist/controls.css +1 -1
  26. package/dist/controls.css.map +1 -1
  27. package/dist/demo.compiled.debug.js +211 -228
  28. package/dist/demo.compiled.debug.map +4 -4
  29. package/dist/demo.compiled.js +211 -228
  30. package/dist/demo.compiled.map +4 -4
  31. package/dist/deps.js +42 -37
  32. package/dist/locales.js +34 -30
  33. package/dist/receiver.compiled.debug.js +64 -77
  34. package/dist/receiver.compiled.debug.map +4 -4
  35. package/dist/receiver.compiled.js +64 -77
  36. package/dist/receiver.compiled.map +4 -4
  37. package/dist/shaka-player.compiled.d.ts +343 -151
  38. package/dist/shaka-player.compiled.debug.d.ts +343 -151
  39. package/dist/shaka-player.compiled.debug.externs.js +356 -117
  40. package/dist/shaka-player.compiled.debug.js +952 -811
  41. package/dist/shaka-player.compiled.debug.map +4 -4
  42. package/dist/shaka-player.compiled.externs.js +356 -117
  43. package/dist/shaka-player.compiled.js +1165 -1078
  44. package/dist/shaka-player.compiled.map +4 -4
  45. package/dist/shaka-player.ui.d.ts +359 -153
  46. package/dist/shaka-player.ui.debug.d.ts +359 -153
  47. package/dist/shaka-player.ui.debug.externs.js +382 -117
  48. package/dist/shaka-player.ui.debug.js +1084 -918
  49. package/dist/shaka-player.ui.debug.map +4 -4
  50. package/dist/shaka-player.ui.externs.js +382 -117
  51. package/dist/shaka-player.ui.js +1377 -1274
  52. package/dist/shaka-player.ui.map +4 -4
  53. package/docs/design/lcevc-integration.md +14 -14
  54. package/docs/tutorials/ad_monetization.md +54 -0
  55. package/docs/tutorials/basic-usage.md +2 -1
  56. package/docs/tutorials/errors.md +2 -1
  57. package/docs/tutorials/faq.md +27 -55
  58. package/docs/tutorials/index.json +2 -1
  59. package/docs/tutorials/lcevc.md +118 -0
  60. package/docs/tutorials/offline.md +10 -8
  61. package/docs/tutorials/plugins.md +5 -0
  62. package/docs/tutorials/ui-customization.md +17 -0
  63. package/docs/tutorials/ui.md +2 -1
  64. package/docs/tutorials/upgrade.md +3 -0
  65. package/externs/css_style_declaration.js +19 -0
  66. package/externs/lcevc.js +37 -13
  67. package/externs/managedmediasource.js +24 -0
  68. package/externs/media_remote.js +68 -0
  69. package/externs/mediatailor.js +193 -0
  70. package/externs/pictureinpicture.js +11 -0
  71. package/externs/shaka/abr_manager.js +6 -2
  72. package/externs/shaka/ads.js +27 -0
  73. package/externs/shaka/cea.js +7 -0
  74. package/externs/shaka/codecs.js +5 -3
  75. package/externs/shaka/manifest.js +31 -10
  76. package/externs/shaka/manifest_parser.js +24 -1
  77. package/externs/shaka/net.js +2 -2
  78. package/externs/shaka/offline.js +13 -2
  79. package/externs/shaka/player.js +65 -8
  80. package/externs/shaka/text.js +3 -1
  81. package/externs/shaka/transmuxer.js +1 -1
  82. package/karma.conf.js +7 -3
  83. package/lib/abr/simple_abr_manager.js +66 -9
  84. package/lib/ads/ad_manager.js +104 -50
  85. package/lib/ads/media_tailor_ad.js +297 -0
  86. package/lib/ads/media_tailor_ad_manager.js +839 -0
  87. package/lib/cast/cast_receiver.js +1 -1
  88. package/lib/cea/cea_decoder.js +28 -0
  89. package/lib/cea/dummy_caption_decoder.js +5 -0
  90. package/lib/cea/ts_cea_parser.js +15 -2
  91. package/lib/config/codec_switching_strategy.js +24 -0
  92. package/lib/dash/content_protection.js +120 -2
  93. package/lib/dash/dash_parser.js +322 -93
  94. package/lib/dash/mpd_utils.js +62 -37
  95. package/lib/dash/segment_base.js +17 -10
  96. package/lib/dash/segment_list.js +18 -9
  97. package/lib/dash/segment_template.js +122 -107
  98. package/lib/dependencies/all.js +8 -7
  99. package/lib/hls/hls_classes.js +28 -43
  100. package/lib/hls/hls_parser.js +798 -658
  101. package/lib/hls/hls_utils.js +64 -5
  102. package/lib/hls/manifest_text_parser.js +7 -11
  103. package/lib/lcevc/lcevc_dec.js +220 -0
  104. package/lib/media/adaptation_set.js +32 -14
  105. package/lib/media/adaptation_set_criteria.js +86 -16
  106. package/lib/media/closed_caption_parser.js +13 -0
  107. package/lib/media/content_workarounds.js +6 -0
  108. package/lib/media/drm_engine.js +8 -13
  109. package/lib/media/manifest_parser.js +8 -113
  110. package/lib/media/media_source_capabilities.js +10 -3
  111. package/lib/media/media_source_engine.js +286 -44
  112. package/lib/media/play_rate_controller.js +8 -38
  113. package/lib/media/presentation_timeline.js +7 -1
  114. package/lib/media/quality_observer.js +1 -1
  115. package/lib/media/segment_index.js +6 -6
  116. package/lib/media/segment_prefetch.js +92 -24
  117. package/lib/media/segment_reference.js +73 -7
  118. package/lib/media/segment_utils.js +343 -0
  119. package/lib/media/stall_detector.js +0 -3
  120. package/lib/media/streaming_engine.js +73 -38
  121. package/lib/mss/mss_parser.js +19 -2
  122. package/lib/net/networking_engine.js +67 -12
  123. package/lib/net/networking_utils.js +95 -0
  124. package/lib/offline/indexeddb/v1_storage_cell.js +3 -0
  125. package/lib/offline/indexeddb/v2_storage_cell.js +3 -0
  126. package/lib/offline/manifest_converter.js +3 -0
  127. package/lib/offline/offline_manifest_parser.js +10 -0
  128. package/lib/offline/storage.js +14 -7
  129. package/lib/player.js +1098 -1537
  130. package/lib/polyfill/mediasource.js +22 -1
  131. package/lib/text/cue.js +15 -0
  132. package/lib/text/mp4_ttml_parser.js +2 -2
  133. package/lib/text/srt_text_parser.js +2 -2
  134. package/lib/text/stub_text_displayer.js +51 -0
  135. package/lib/text/text_engine.js +3 -2
  136. package/lib/text/ttml_text_parser.js +45 -5
  137. package/lib/text/ui_text_displayer.js +8 -0
  138. package/lib/text/vtt_text_parser.js +40 -22
  139. package/lib/transmuxer/aac_transmuxer.js +1 -1
  140. package/lib/transmuxer/ac3_transmuxer.js +1 -1
  141. package/lib/transmuxer/ec3_transmuxer.js +1 -1
  142. package/lib/transmuxer/h264.js +5 -4
  143. package/lib/transmuxer/h265.js +863 -0
  144. package/lib/transmuxer/mp3_transmuxer.js +1 -1
  145. package/lib/transmuxer/mpeg_ts_transmuxer.js +4 -3
  146. package/lib/transmuxer/mss_transmuxer.js +1 -1
  147. package/lib/transmuxer/ts_transmuxer.js +187 -22
  148. package/lib/util/buffer_utils.js +26 -4
  149. package/lib/util/content_steering_manager.js +358 -0
  150. package/lib/util/error.js +30 -15
  151. package/lib/util/exp_golomb.js +30 -2
  152. package/lib/util/fairplay_utils.js +1 -1
  153. package/lib/util/fake_event.js +8 -1
  154. package/lib/util/id3_utils.js +50 -1
  155. package/lib/util/manifest_parser_utils.js +12 -8
  156. package/lib/util/mp4_generator.js +6 -8
  157. package/lib/util/mutex.js +59 -0
  158. package/lib/util/object_utils.js +1 -1
  159. package/lib/util/periods.js +156 -179
  160. package/lib/util/platform.js +44 -3
  161. package/lib/util/player_configuration.js +43 -6
  162. package/lib/util/state_history.js +6 -2
  163. package/lib/util/stream_utils.js +206 -283
  164. package/lib/util/ts_parser.js +335 -98
  165. package/lib/util/uint8array_utils.js +5 -1
  166. package/lib/util/xml_utils.js +5 -0
  167. package/maintained-branches.md +3 -2
  168. package/package.json +4 -6
  169. package/roadmap.md +25 -5
  170. package/shaka-player.uncompiled.js +2 -2
  171. package/support.html +0 -1
  172. package/test/abr/simple_abr_manager_unit.js +33 -33
  173. package/test/ads/ad_manager_unit.js +3 -2
  174. package/test/cast/cast_receiver_integration.js +50 -4
  175. package/test/cast/cast_utils_unit.js +2 -0
  176. package/test/cea/cea_decoder_unit.js +7 -0
  177. package/test/codec_switching/codec_switching_integration.js +182 -0
  178. package/test/dash/dash_parser_content_protection_unit.js +2 -0
  179. package/test/dash/dash_parser_integration.js +74 -0
  180. package/test/dash/dash_parser_live_unit.js +2 -19
  181. package/test/dash/dash_parser_manifest_unit.js +536 -2
  182. package/test/dash/dash_parser_segment_base_unit.js +2 -0
  183. package/test/dash/dash_parser_segment_list_unit.js +2 -0
  184. package/test/dash/dash_parser_segment_template_unit.js +2 -0
  185. package/test/dash/mpd_utils_unit.js +61 -30
  186. package/test/demo/demo_unit.js +0 -16
  187. package/test/hls/hls_live_unit.js +14 -3
  188. package/test/hls/hls_parser_unit.js +385 -33
  189. package/test/hls/manifest_text_parser_unit.js +32 -99
  190. package/test/media/adaptation_set_criteria_unit.js +284 -15
  191. package/test/media/adaptation_set_unit.js +2 -0
  192. package/test/media/content_workarounds_unit.js +1 -1
  193. package/test/media/drm_engine_unit.js +3 -3
  194. package/test/media/media_source_engine_unit.js +166 -2
  195. package/test/media/play_rate_controller_unit.js +0 -48
  196. package/test/media/segment_index_unit.js +5 -4
  197. package/test/media/segment_prefetch_unit.js +1 -1
  198. package/test/media/streaming_engine_unit.js +17 -17
  199. package/test/mss/mss_parser_unit.js +2 -0
  200. package/test/mss/mss_player_integration.js +3 -2
  201. package/test/net/networking_engine_unit.js +3 -3
  202. package/test/{media/manifest_parser_unit.js → net/networking_utils_unit.js} +2 -2
  203. package/test/offline/manifest_convert_unit.js +14 -0
  204. package/test/offline/offline_integration.js +2 -1
  205. package/test/offline/storage_integration.js +22 -13
  206. package/test/player_external.js +4 -2
  207. package/test/player_integration.js +15 -4
  208. package/test/player_load_graph_integration.js +47 -151
  209. package/test/player_src_equals_external.js +1 -0
  210. package/test/player_src_equals_integration.js +71 -4
  211. package/test/player_unit.js +182 -158
  212. package/test/test/assets/dash-aes-128/Fragments(video=0,format=mpd-time-csf) +0 -0
  213. package/test/test/assets/dash-aes-128/Fragments(video=20000000,format=mpd-time-csf) +0 -0
  214. package/test/test/assets/dash-aes-128/Fragments(video=40000000,format=mpd-time-csf) +0 -0
  215. package/test/test/assets/dash-aes-128/Fragments(video=60000000,format=mpd-time-csf) +0 -0
  216. package/test/test/assets/dash-aes-128/Fragments(video=i,format=mpd-time-csf) +0 -0
  217. package/test/test/assets/dash-aes-128/dash.mpd +18 -0
  218. package/test/test/assets/dash-aes-128/license +1 -0
  219. package/test/test/assets/dash-multi-codec/audio_en_2c_128k_aac.mp4 +0 -0
  220. package/test/test/assets/dash-multi-codec/audio_es_2c_64k_opus.webm +0 -0
  221. package/test/test/assets/dash-multi-codec/dash.mpd +32 -0
  222. package/test/test/assets/dash-multi-codec/video_144p_108k_h264.mp4 +0 -0
  223. package/test/test/assets/hls-ts-h265/0.ts +0 -0
  224. package/test/test/assets/hls-ts-h265/hevc.m3u8 +6 -0
  225. package/test/test/assets/hls-ts-h265/index.m3u8 +7 -0
  226. package/test/test/assets/hls-ts-muxed-aac-h265/0.ts +0 -0
  227. package/test/test/assets/hls-ts-muxed-aac-h265/1.ts +0 -0
  228. package/test/test/assets/hls-ts-muxed-aac-h265/media.m3u8 +9 -0
  229. package/test/test/assets/hls-ts-muxed-ac3-h264/media.m3u8 +24 -0
  230. package/test/test/assets/hls-ts-muxed-ac3-h264/s_00000.ts +0 -0
  231. package/test/test/assets/hls-ts-muxed-ac3-h264/s_00001.ts +0 -0
  232. package/test/test/assets/hls-ts-muxed-ac3-h264/s_00002.ts +0 -0
  233. package/test/test/assets/hls-ts-muxed-ac3-h264/s_00003.ts +0 -0
  234. package/test/test/assets/hls-ts-muxed-ac3-h264/s_00004.ts +0 -0
  235. package/test/test/assets/hls-ts-muxed-ac3-h264/s_00005.ts +0 -0
  236. package/test/test/assets/hls-ts-muxed-ec3-h264/ChID_voices_1280x720p_25fps_h264_6ch_256kbps_ddp_sub_0000.ts +0 -0
  237. package/test/test/assets/hls-ts-muxed-ec3-h264/ChID_voices_1280x720p_25fps_h264_6ch_256kbps_ddp_sub_0001.ts +0 -0
  238. package/test/test/assets/hls-ts-muxed-ec3-h264/ChID_voices_1280x720p_25fps_h264_6ch_256kbps_ddp_sub_0002.ts +0 -0
  239. package/test/test/assets/hls-ts-muxed-ec3-h264/ChID_voices_1280x720p_25fps_h264_6ch_256kbps_ddp_sub_0003.ts +0 -0
  240. package/test/test/assets/hls-ts-muxed-ec3-h264/prog_index.m3u8 +14 -0
  241. package/test/test/assets/hls-ts-muxed-mp3-h264/index.m3u8 +23 -0
  242. package/test/test/assets/hls-ts-muxed-mp3-h264/seg-1-v1-a1.ts +0 -0
  243. package/test/test/assets/hls-ts-muxed-mp3-h264/seg-2-v1-a1.ts +0 -0
  244. package/test/test/assets/hls-ts-muxed-mp3-h264/seg-3-v1-a1.ts +0 -0
  245. package/test/test/assets/hls-ts-muxed-mp3-h264/seg-4-v1-a1.ts +0 -0
  246. package/test/test/assets/hls-ts-muxed-mp3-h264/seg-5-v1-a1.ts +0 -0
  247. package/test/test/assets/hls-ts-muxed-mp3-h264/seg-6-v1-a1.ts +0 -0
  248. package/test/test/assets/hls-ts-muxed-mp3-h264/seg-7-v1-a1.ts +0 -0
  249. package/test/test/assets/hls-ts-muxed-mp3-h264/seg-8-v1-a1.ts +0 -0
  250. package/test/test/util/dash_parser_util.js +4 -0
  251. package/test/test/util/fake_ad_manager.js +20 -0
  252. package/test/test/util/fake_media_source_engine.js +13 -2
  253. package/test/test/util/fake_networking_engine.js +9 -1
  254. package/test/test/util/fake_segment_prefetch.js +3 -0
  255. package/test/test/util/manifest_generator.js +11 -3
  256. package/test/test/util/manifest_parser_util.js +14 -2
  257. package/test/test/util/mss_parser_util.js +4 -0
  258. package/test/test/util/offline_utils.js +2 -0
  259. package/test/test/util/simple_fakes.js +2 -2
  260. package/test/test/util/streaming_engine_util.js +10 -4
  261. package/test/test/util/test_scheme.js +6 -0
  262. package/test/test/util/ui_utils.js +4 -3
  263. package/test/text/mp4_ttml_parser_unit.js +4 -4
  264. package/test/text/srt_text_parser_unit.js +1 -1
  265. package/test/text/text_displayer_layout_unit.js +1 -1
  266. package/test/text/text_engine_unit.js +6 -0
  267. package/test/text/ttml_text_parser_unit.js +85 -2
  268. package/test/text/vtt_text_parser_unit.js +103 -31
  269. package/test/transmuxer/transmuxer_engine_integration.js +9 -0
  270. package/test/transmuxer/transmuxer_integration.js +150 -3
  271. package/test/ui/ad_ui_unit.js +2 -2
  272. package/test/ui/ui_customization_unit.js +17 -16
  273. package/test/ui/ui_integration.js +30 -20
  274. package/test/ui/ui_unit.js +28 -29
  275. package/test/util/buffer_utils_unit.js +1 -1
  276. package/test/util/content_steering_manager_unit.js +427 -0
  277. package/test/util/id3_utils_unit.js +52 -0
  278. package/test/util/stream_utils_unit.js +207 -169
  279. package/test/util/ts_parser_unit.js +3 -3
  280. package/third_party/closure-uri/uri.js +13 -1
  281. package/ui/audio_language_selection.js +9 -2
  282. package/ui/controls.js +125 -0
  283. package/ui/externs/ui.js +24 -2
  284. package/ui/language_utils.js +43 -8
  285. package/ui/less/range_elements.less +33 -0
  286. package/ui/locales/en.json +1 -0
  287. package/ui/locales/es-419.json +1 -0
  288. package/ui/locales/es.json +1 -0
  289. package/ui/locales/source.json +4 -0
  290. package/ui/localization.js +10 -0
  291. package/ui/pip_button.js +22 -120
  292. package/ui/remote_button.js +203 -0
  293. package/ui/resolution_selection.js +47 -10
  294. package/ui/seek_bar.js +296 -3
  295. package/ui/text_selection.js +2 -1
  296. package/ui/ui.js +11 -4
  297. package/demo/common/message_ids.js +0 -309
  298. package/demo/locales/en.json +0 -288
  299. package/demo/locales/source.json +0 -1146
  300. package/externs/mux.js +0 -69
  301. package/lib/lcevc/lcevc_dil.js +0 -152
  302. package/lib/routing/node.js +0 -23
  303. package/lib/routing/payload.js +0 -43
  304. package/lib/routing/walker.js +0 -587
  305. package/lib/transmuxer/muxjs_transmuxer.js +0 -242
  306. package/test/routing/walker_unit.js +0 -504
  307. package/test/transmuxer/muxjs_transmuxer_integration.js +0 -177
@@ -0,0 +1,203 @@
1
+ /*! @license
2
+ * Shaka Player
3
+ * Copyright 2016 Google LLC
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+
7
+
8
+ goog.provide('shaka.ui.RemoteButton');
9
+
10
+ goog.require('shaka.Player');
11
+ goog.require('shaka.ui.Controls');
12
+ goog.require('shaka.ui.Element');
13
+ goog.require('shaka.ui.Enums');
14
+ goog.require('shaka.ui.Locales');
15
+ goog.require('shaka.ui.Localization');
16
+ goog.require('shaka.ui.OverflowMenu');
17
+ goog.require('shaka.ui.Utils');
18
+ goog.require('shaka.util.Dom');
19
+ goog.require('shaka.util.Platform');
20
+ goog.requireType('shaka.ui.Controls');
21
+
22
+
23
+ /**
24
+ * @extends {shaka.ui.Element}
25
+ * @final
26
+ * @export
27
+ */
28
+ shaka.ui.RemoteButton = class extends shaka.ui.Element {
29
+ /**
30
+ * @param {!HTMLElement} parent
31
+ * @param {!shaka.ui.Controls} controls
32
+ */
33
+ constructor(parent, controls) {
34
+ super(parent, controls);
35
+
36
+ /** @private {!HTMLButtonElement} */
37
+ this.remoteButton_ = shaka.util.Dom.createButton();
38
+ this.remoteButton_.classList.add('shaka-remote-button');
39
+ this.remoteButton_.classList.add('shaka-tooltip');
40
+ this.remoteButton_.ariaPressed = 'false';
41
+
42
+ /** @private {!HTMLElement} */
43
+ this.remoteIcon_ = shaka.util.Dom.createHTMLElement('i');
44
+ this.remoteIcon_.classList.add('material-icons-round');
45
+ let icon = shaka.ui.Enums.MaterialDesignIcons.CAST;
46
+ const safariVersion = shaka.util.Platform.safariVersion();
47
+ if (safariVersion && safariVersion >= 13) {
48
+ icon = shaka.ui.Enums.MaterialDesignIcons.AIRPLAY;
49
+ }
50
+ this.remoteIcon_.textContent = icon;
51
+ this.remoteButton_.appendChild(this.remoteIcon_);
52
+
53
+ const label = shaka.util.Dom.createHTMLElement('label');
54
+ label.classList.add('shaka-overflow-button-label');
55
+ label.classList.add('shaka-overflow-menu-only');
56
+ this.remoteNameSpan_ = shaka.util.Dom.createHTMLElement('span');
57
+ label.appendChild(this.remoteNameSpan_);
58
+
59
+ this.remoteCurrentSelectionSpan_ =
60
+ shaka.util.Dom.createHTMLElement('span');
61
+ this.remoteCurrentSelectionSpan_.classList.add(
62
+ 'shaka-current-selection-span');
63
+ label.appendChild(this.remoteCurrentSelectionSpan_);
64
+ this.remoteButton_.appendChild(label);
65
+ this.parent.appendChild(this.remoteButton_);
66
+
67
+ /** @private {number} */
68
+ this.callbackId_ = -1;
69
+
70
+ // Setup strings in the correct language
71
+ this.updateLocalizedStrings_();
72
+
73
+ shaka.ui.Utils.setDisplay(this.remoteButton_, false);
74
+
75
+ if (!this.video.remote || this.video.disableRemotePlayback) {
76
+ this.remoteButton_.classList.add('shaka-hidden');
77
+ } else {
78
+ this.eventManager.listen(
79
+ this.localization, shaka.ui.Localization.LOCALE_UPDATED, () => {
80
+ this.updateLocalizedStrings_();
81
+ });
82
+
83
+ this.eventManager.listen(
84
+ this.localization, shaka.ui.Localization.LOCALE_CHANGED, () => {
85
+ this.updateLocalizedStrings_();
86
+ });
87
+
88
+ this.eventManager.listen(this.controls, 'caststatuschanged', () => {
89
+ this.updateRemoteState_();
90
+ });
91
+
92
+ this.eventManager.listen(this.remoteButton_, 'click', () => {
93
+ this.video.remote.prompt();
94
+ });
95
+
96
+ this.eventManager.listen(this.video.remote, 'connect', () => {
97
+ this.updateRemoteState_();
98
+ });
99
+
100
+ this.eventManager.listen(this.video.remote, 'connecting', () => {
101
+ this.updateRemoteState_();
102
+ });
103
+
104
+ this.eventManager.listen(this.video.remote, 'disconnect', () => {
105
+ this.updateRemoteState_();
106
+ });
107
+
108
+ this.eventManager.listen(this.player, 'loaded', () => {
109
+ this.updateRemoteState_();
110
+ });
111
+
112
+ this.updateRemoteState_();
113
+ }
114
+ }
115
+
116
+ /** @override */
117
+ release() {
118
+ if (this.video.remote && this.callbackId_ != -1) {
119
+ this.video.remote.cancelWatchAvailability(this.callbackId_).catch(() => {
120
+ // Ignore this error.
121
+ });
122
+ }
123
+
124
+ super.release();
125
+ }
126
+
127
+ /**
128
+ * @private
129
+ */
130
+ async updateRemoteState_() {
131
+ if (this.controls.getCastProxy().canCast() &&
132
+ this.controls.isCastAllowed()) {
133
+ shaka.ui.Utils.setDisplay(this.remoteButton_, false);
134
+ if (this.callbackId_ != -1) {
135
+ this.video.remote.cancelWatchAvailability(this.callbackId_);
136
+ this.callbackId_ = -1;
137
+ }
138
+ } else if (this.video.remote.state == 'disconnected') {
139
+ const handleAvailabilityChange = (availability) => {
140
+ if (this.player) {
141
+ const loadMode = this.player.getLoadMode();
142
+ const srcMode = loadMode == shaka.Player.LoadMode.SRC_EQUALS;
143
+ shaka.ui.Utils.setDisplay(
144
+ this.remoteButton_, srcMode && availability);
145
+ } else {
146
+ shaka.ui.Utils.setDisplay(this.remoteButton_, false);
147
+ }
148
+ };
149
+ try {
150
+ if (this.callbackId_ != -1) {
151
+ await this.video.remote.cancelWatchAvailability(this.callbackId_);
152
+ this.callbackId_ = -1;
153
+ }
154
+ } catch (e) {
155
+ // Ignore this error.
156
+ }
157
+ try {
158
+ const id = await this.video.remote.watchAvailability(
159
+ handleAvailabilityChange);
160
+ this.callbackId_ = id;
161
+ } catch (e) {
162
+ handleAvailabilityChange(/* availability= */ true);
163
+ }
164
+ } else if (this.callbackId_ != -1) {
165
+ // If remote device is connecting or connected, we should stop
166
+ // watching remote device availability to save power.
167
+ await this.video.remote.cancelWatchAvailability(this.callbackId_);
168
+ this.callbackId_ = -1;
169
+ }
170
+ }
171
+
172
+ /**
173
+ * @private
174
+ */
175
+ updateLocalizedStrings_() {
176
+ const LocIds = shaka.ui.Locales.Ids;
177
+ let text = this.localization.resolve(LocIds.CAST);
178
+ const safariVersion = shaka.util.Platform.safariVersion();
179
+ if (safariVersion && safariVersion >= 13) {
180
+ text = this.localization.resolve(LocIds.AIRPLAY);
181
+ }
182
+ this.remoteButton_.ariaLabel = text;
183
+ this.remoteNameSpan_.textContent = text;
184
+ }
185
+ };
186
+
187
+
188
+ /**
189
+ * @implements {shaka.extern.IUIElement.Factory}
190
+ * @final
191
+ */
192
+ shaka.ui.RemoteButton.Factory = class {
193
+ /** @override */
194
+ create(rootElement, controls) {
195
+ return new shaka.ui.RemoteButton(rootElement, controls);
196
+ }
197
+ };
198
+
199
+ shaka.ui.OverflowMenu.registerElement(
200
+ 'remote', new shaka.ui.RemoteButton.Factory());
201
+
202
+ shaka.ui.Controls.registerElement(
203
+ 'remote', new shaka.ui.RemoteButton.Factory());
@@ -89,10 +89,18 @@ shaka.ui.ResolutionSelection = class extends shaka.ui.SettingsMenu {
89
89
  // Remove duplicate entries with the same resolution or quality depending
90
90
  // on content type. Pick an arbitrary one.
91
91
  tracks = tracks.filter((track, idx) => {
92
- // Keep the first one with the same height or bandwidth.
93
- const otherIdx = this.player.isAudioOnly() ?
94
- tracks.findIndex((t) => t.bandwidth == track.bandwidth) :
95
- tracks.findIndex((t) => t.height == track.height);
92
+ // Keep the first one with the same height and framrate or bandwidth.
93
+ let otherIdx = -1;
94
+ if (this.player.isAudioOnly()) {
95
+ otherIdx = tracks.findIndex((t) => t.bandwidth == track.bandwidth);
96
+ } else {
97
+ otherIdx = tracks.findIndex((t) => {
98
+ return t.height == track.height &&
99
+ t.frameRate == track.frameRate &&
100
+ t.hdr == track.hdr &&
101
+ t.videoLayout == track.videoLayout;
102
+ });
103
+ }
96
104
  return otherIdx == idx;
97
105
  });
98
106
 
@@ -132,14 +140,12 @@ shaka.ui.ResolutionSelection = class extends shaka.ui.SettingsMenu {
132
140
  () => this.onTrackSelected_(track));
133
141
 
134
142
  const span = shaka.util.Dom.createHTMLElement('span');
135
- if (this.player.isAudioOnly()) {
143
+ if (this.player.isAudioOnly() && track.bandwidth) {
136
144
  span.textContent = Math.round(track.bandwidth / 1000) + ' kbits/s';
145
+ } else if (track.height && track.width) {
146
+ span.textContent = this.getResolutionLabel_(track);
137
147
  } else {
138
- if (track.hdr == 'PQ' || track.hdr == 'HLG') {
139
- span.textContent = track.height + 'p (HDR)';
140
- } else {
141
- span.textContent = track.height + 'p';
142
- }
148
+ span.textContent = 'Unknown';
143
149
  }
144
150
  button.appendChild(span);
145
151
 
@@ -191,6 +197,37 @@ shaka.ui.ResolutionSelection = class extends shaka.ui.SettingsMenu {
191
197
  }
192
198
 
193
199
 
200
+ /**
201
+ * @param {!shaka.extern.Track} track
202
+ * @return {string}
203
+ * @private
204
+ */
205
+ getResolutionLabel_(track) {
206
+ const trackHeight = track.height || 0;
207
+ const trackWidth = track.width || 0;
208
+ let height = trackHeight;
209
+ const aspectRatio = trackWidth / trackHeight;
210
+ if (aspectRatio > (16 / 9)) {
211
+ height = Math.round(trackWidth * 9 / 16);
212
+ }
213
+ let text = height + 'p';
214
+ if (height == 2160) {
215
+ text = '4K';
216
+ }
217
+ const frameRate = track.frameRate;
218
+ if (frameRate && (frameRate >= 50 || frameRate <= 20)) {
219
+ text += Math.round(track.frameRate);
220
+ }
221
+ if (track.hdr == 'PQ' || track.hdr == 'HLG') {
222
+ text += ' (HDR)';
223
+ }
224
+ if (track.videoLayout == 'CH-STEREO') {
225
+ text += ' (3D)';
226
+ }
227
+ return text;
228
+ }
229
+
230
+
194
231
  /**
195
232
  * @param {!shaka.extern.Track} track
196
233
  * @private
package/ui/seek_bar.js CHANGED
@@ -8,12 +8,17 @@
8
8
  goog.provide('shaka.ui.SeekBar');
9
9
 
10
10
  goog.require('shaka.ads.AdManager');
11
+ goog.require('shaka.net.NetworkingEngine');
11
12
  goog.require('shaka.ui.Constants');
12
13
  goog.require('shaka.ui.Locales');
13
14
  goog.require('shaka.ui.Localization');
14
15
  goog.require('shaka.ui.RangeElement');
15
16
  goog.require('shaka.ui.Utils');
16
17
  goog.require('shaka.util.Dom');
18
+ goog.require('shaka.util.Error');
19
+ goog.require('shaka.util.EventManager');
20
+ goog.require('shaka.util.Mp4Parser');
21
+ goog.require('shaka.util.Networking');
17
22
  goog.require('shaka.util.Timer');
18
23
  goog.requireType('shaka.ui.Controls');
19
24
 
@@ -79,6 +84,19 @@ shaka.ui.SeekBar = class extends shaka.ui.RangeElement {
79
84
  this.markAdBreaks_();
80
85
  });
81
86
 
87
+ /**
88
+ * @private {shaka.util.EventManager}
89
+ */
90
+ this.chaptersEventManager_ = new shaka.util.EventManager();
91
+
92
+
93
+ /** @private {!HTMLElement} */
94
+ this.chaptersContainer_ = shaka.util.Dom.createHTMLElement('div');
95
+ this.chaptersContainer_.id = 'shaka-player-ui-chapters-container';
96
+ this.container.appendChild(this.chaptersContainer_);
97
+
98
+ this.setupChapters_();
99
+
82
100
  /**
83
101
  * When user is scrubbing the seek bar - we should pause the video - see
84
102
  * https://github.com/google/shaka-player/pull/2898#issuecomment-705229215
@@ -108,6 +126,16 @@ shaka.ui.SeekBar = class extends shaka.ui.RangeElement {
108
126
  this.thumbnailContainer_.appendChild(this.thumbnailTime_);
109
127
  this.container.appendChild(this.thumbnailContainer_);
110
128
 
129
+ /**
130
+ * @private {?shaka.extern.Thumbnail}
131
+ */
132
+ this.lastThumbnail_ = null;
133
+
134
+ /**
135
+ * @private {?shaka.net.NetworkingEngine.PendingRequest}
136
+ */
137
+ this.lastThumbnailPendingRequest_ = null;
138
+
111
139
  /**
112
140
  * True if the bar is moving due to touchscreen or keyboard events.
113
141
  *
@@ -159,6 +187,12 @@ shaka.ui.SeekBar = class extends shaka.ui.RangeElement {
159
187
  this.player, 'unloading', () => {
160
188
  this.adCuePoints_ = [];
161
189
  this.onAdCuePointsChanged_();
190
+ if (this.lastThumbnailPendingRequest_) {
191
+ this.lastThumbnailPendingRequest_.abort();
192
+ this.lastThumbnailPendingRequest_ = null;
193
+ }
194
+ this.lastThumbnail_ = null;
195
+ this.hideThumbnail_();
162
196
  });
163
197
 
164
198
  this.eventManager.listen(this.bar, 'mousemove', (event) => {
@@ -204,6 +238,8 @@ shaka.ui.SeekBar = class extends shaka.ui.RangeElement {
204
238
  this.adBreaksTimer_ = null;
205
239
  }
206
240
 
241
+ this.chaptersEventManager_.release();
242
+
207
243
  super.release();
208
244
  }
209
245
 
@@ -504,8 +540,52 @@ shaka.ui.SeekBar = class extends shaka.ui.RangeElement {
504
540
  Math.max(0, pixelPosition - (width / 2)));
505
541
  this.thumbnailContainer_.style.left = leftPosition + 'px';
506
542
  this.thumbnailContainer_.style.visibility = 'visible';
507
- const uri = thumbnail.uris[0].split('#xywh=')[0];
508
- if (uri !== this.thumbnailImage_.src) {
543
+ let uri = thumbnail.uris[0].split('#xywh=')[0];
544
+ if (!this.lastThumbnail_ ||
545
+ uri !== this.lastThumbnail_.uris[0].split('#xywh=')[0] ||
546
+ thumbnail.segment.getStartByte() !=
547
+ this.lastThumbnail_.segment.getStartByte() ||
548
+ thumbnail.segment.getEndByte() !=
549
+ this.lastThumbnail_.segment.getEndByte()) {
550
+ this.lastThumbnail_ = thumbnail;
551
+ if (this.lastThumbnailPendingRequest_) {
552
+ this.lastThumbnailPendingRequest_.abort();
553
+ this.lastThumbnailPendingRequest_ = null;
554
+ }
555
+ if (thumbnailTrack.codecs == 'mjpg' || uri.startsWith('offline:')) {
556
+ this.thumbnailImage_.src = shaka.ui.SeekBar.Transparent_Image_;
557
+ try {
558
+ const requestType = shaka.net.NetworkingEngine.RequestType.SEGMENT;
559
+ const type =
560
+ shaka.net.NetworkingEngine.AdvancedRequestType.MEDIA_SEGMENT;
561
+ const request = shaka.util.Networking.createSegmentRequest(
562
+ thumbnail.segment.getUris(),
563
+ thumbnail.segment.getStartByte(),
564
+ thumbnail.segment.getEndByte(),
565
+ this.player.getConfiguration().streaming.retryParameters);
566
+ this.lastThumbnailPendingRequest_ = this.player.getNetworkingEngine()
567
+ .request(requestType, request, {type});
568
+ const response = await this.lastThumbnailPendingRequest_.promise;
569
+ this.lastThumbnailPendingRequest_ = null;
570
+ if (thumbnailTrack.codecs == 'mjpg') {
571
+ const parser = new shaka.util.Mp4Parser()
572
+ .box('mdat', shaka.util.Mp4Parser.allData((data) => {
573
+ const blob = new Blob([data], {type: 'image/jpeg'});
574
+ uri = URL.createObjectURL(blob);
575
+ }));
576
+ parser.parse(response.data, /* partialOkay= */ false);
577
+ } else {
578
+ const mimeType = thumbnailTrack.mimeType || 'image/jpeg';
579
+ const blob = new Blob([response.data], {type: mimeType});
580
+ uri = URL.createObjectURL(blob);
581
+ }
582
+ } catch (error) {
583
+ if (error.code == shaka.util.Error.Code.OPERATION_ABORTED) {
584
+ return;
585
+ }
586
+ throw error;
587
+ }
588
+ }
509
589
  try {
510
590
  this.thumbnailContainer_.removeChild(this.thumbnailImage_);
511
591
  } catch (e) {
@@ -516,6 +596,11 @@ shaka.ui.SeekBar = class extends shaka.ui.RangeElement {
516
596
  this.thumbnailImage_.id = 'shaka-player-ui-thumbnail-image';
517
597
  this.thumbnailImage_.draggable = false;
518
598
  this.thumbnailImage_.src = uri;
599
+ this.thumbnailImage_.onload = () => {
600
+ if (uri.startsWith('blob:')) {
601
+ URL.revokeObjectURL(uri);
602
+ }
603
+ };
519
604
  this.thumbnailContainer_.insertBefore(this.thumbnailImage_,
520
605
  this.thumbnailContainer_.firstChild);
521
606
  }
@@ -571,7 +656,10 @@ shaka.ui.SeekBar = class extends shaka.ui.RangeElement {
571
656
  return bestOptions[0];
572
657
  }
573
658
  }
574
- return imageTracks[0];
659
+ const mjpgTrack = imageTracks.find((track) => {
660
+ return track.mimeType == 'application/mp4' && track.codecs == 'mjpg';
661
+ });
662
+ return mjpgTrack || imageTracks[0];
575
663
  }
576
664
 
577
665
 
@@ -605,9 +693,214 @@ shaka.ui.SeekBar = class extends shaka.ui.RangeElement {
605
693
  return minutes + ':' + seconds;
606
694
  }
607
695
  }
696
+
697
+ /**
698
+ * Sets up the chapter element creator and change handling.
699
+ * @private
700
+ */
701
+ setupChapters_() {
702
+ let language = 'und';
703
+ /** @type {!Array<shaka.extern.Chapter>} */
704
+ let chapters = [];
705
+
706
+ /**
707
+ * Does a value compare on chapters.
708
+ * @param {shaka.extern.Chapter} a
709
+ * @param {shaka.extern.Chapter} b
710
+ * @return {boolean}
711
+ */
712
+ const chaptersEqual = (a, b) => {
713
+ return (!a && !b) || (a.id === b.id && a.title === b.title &&
714
+ a.startTime === b.startTime && a.endTime === b.endTime);
715
+ };
716
+
717
+ /** @type {function(): void} */
718
+ const handleChapterTrackChange = () => {
719
+ let nextLanguage = 'und';
720
+ /** @type {!Array<shaka.extern.Chapter>} */
721
+ let nextChapters = [];
722
+
723
+ const currentLocales = this.localization.getCurrentLocales();
724
+ for (const locale of Array.from(currentLocales)) {
725
+ nextLanguage = locale;
726
+ nextChapters = this.player.getChapters(nextLanguage);
727
+ if (nextChapters.length) {
728
+ break;
729
+ }
730
+ }
731
+ if (!nextChapters.length) {
732
+ nextLanguage = 'und';
733
+ nextChapters = this.player.getChapters(nextLanguage);
734
+ }
735
+
736
+ const languageChanged = nextLanguage !== language;
737
+ const chaptersChanged = chapters.length !== nextChapters.length ||
738
+ !chapters.some((c, idx) => {
739
+ const n = nextChapters.at(idx);
740
+ return chaptersEqual(c, n) ||
741
+ nextChapters.some((n) => chaptersEqual(c, n));
742
+ });
743
+
744
+ language = nextLanguage;
745
+ chapters = nextChapters;
746
+ if (!nextChapters.length) {
747
+ this.deletePreviousChapters_();
748
+ } else if (languageChanged || chaptersChanged) {
749
+ this.createChapterElements_(this.container, chapters);
750
+ }
751
+ };
752
+
753
+ handleChapterTrackChange();
754
+
755
+ this.eventManager.listen(
756
+ this.player, 'unloading', () => {
757
+ this.deletePreviousChapters_();
758
+ language = 'und';
759
+ chapters = [];
760
+ });
761
+
762
+ this.eventManager.listen(
763
+ this.player, 'trackschanged', handleChapterTrackChange);
764
+
765
+ this.eventManager.listen(
766
+ this.localization, shaka.ui.Localization.LOCALE_UPDATED, () => {
767
+ handleChapterTrackChange();
768
+ });
769
+
770
+ this.eventManager.listen(
771
+ this.localization, shaka.ui.Localization.LOCALE_CHANGED, () => {
772
+ handleChapterTrackChange();
773
+ });
774
+ }
775
+
776
+ /**
777
+ * Builds and inserts ChaptersElement into dom container.
778
+ * @param {!HTMLElement} container
779
+ * @param {!Array<shaka.extern.Chapter>} chapterTracks
780
+ * @private
781
+ */
782
+ createChapterElements_(container, chapterTracks) {
783
+ this.deletePreviousChapters_();
784
+
785
+ const hiddenClass = 'shaka-hidden';
786
+
787
+ /** @type {{start: number, end: number}} */
788
+ const seekRange = this.player.seekRange();
789
+
790
+ /**
791
+ * @type {!Array<{
792
+ * start: number,
793
+ * end: number,
794
+ * size: number,
795
+ * title: string,
796
+ * id: string
797
+ * }>}
798
+ * */
799
+ const chapters = [];
800
+
801
+ for (const c of chapterTracks) {
802
+ if (c.startTime >= seekRange.end) {
803
+ continue;
804
+ }
805
+ const start = c.startTime > seekRange.start ?
806
+ c.startTime : seekRange.start;
807
+ const end = c.endTime < seekRange.end ?
808
+ c.endTime : seekRange.end;
809
+ const size = (end-start);
810
+ chapters.push({start, end, size, title: c.title, id: c.id});
811
+ }
812
+
813
+ if (chapters.length < 2) {
814
+ return;
815
+ }
816
+
817
+ const totalSize = chapters.reduce((t, c) => {
818
+ t += c.size;
819
+ return t;
820
+ }, 0);
821
+
822
+ /**
823
+ * @type {!Array<{
824
+ * start: number,
825
+ * end: number,
826
+ * el: HTMLElement
827
+ * }>}
828
+ * */
829
+ const chapterElMap = [];
830
+
831
+ for (const c of chapters) {
832
+ /** @type {!HTMLElement} */
833
+ const chapterEl = shaka.util.Dom.createHTMLElement('div');
834
+ chapterEl.classList.add('shaka-chapter');
835
+ chapterEl.style.width = `${c.size * 100 / totalSize}%`;
836
+
837
+ this.chaptersContainer_.appendChild(chapterEl);
838
+
839
+ /** @type {!HTMLElement} */
840
+ const chapterMarker = shaka.util.Dom.createHTMLElement('div');
841
+ chapterMarker.style.borderColor =
842
+ this.config_.seekBarColors.chapterMarks;
843
+ chapterEl.appendChild(chapterMarker);
844
+
845
+ /** @type {!HTMLElement} */
846
+ const chapterLabel = shaka.util.Dom.createHTMLElement('p');
847
+ chapterLabel.classList.add('shaka-chapter-label', hiddenClass);
848
+ chapterLabel.style.color =
849
+ this.config_.seekBarColors.chapterLabels;
850
+ chapterLabel.innerText = c.title;
851
+ chapterEl.appendChild(chapterLabel);
852
+
853
+ chapterElMap.push(
854
+ {start: c.start, end: c.end, el: chapterLabel});
855
+ }
856
+
857
+ // Add chapter event listeners
858
+ this.chaptersEventManager_.listen(this.bar, 'pointermove', (e) => {
859
+ if (!e.target) {
860
+ return;
861
+ }
862
+ const target = /** @type {HTMLElement} */(e.target);
863
+
864
+ const screenXDiff = e.offsetX / target.clientWidth;
865
+ const rangeMax = parseInt(target.getAttribute('max'), 10);
866
+ const hoverVal = screenXDiff * rangeMax;
867
+
868
+ for (const c of chapterElMap) {
869
+ const hidden = c.el.classList.contains(hiddenClass);
870
+ const inChapter = c.start <= hoverVal && hoverVal < c.end;
871
+ if (inChapter === hidden) {
872
+ c.el.classList.toggle(hiddenClass);
873
+ }
874
+ }
875
+ }, {passive: true});
876
+
877
+ this.chaptersEventManager_.listen(this.bar, 'pointerout', () => {
878
+ for (const c of chapterElMap) {
879
+ if (!c.el.classList.contains(hiddenClass)) {
880
+ c.el.classList.add(hiddenClass);
881
+ }
882
+ }
883
+ }, {passive: true});
884
+ }
885
+
886
+ /**
887
+ * @private
888
+ */
889
+ deletePreviousChapters_() {
890
+ this.chaptersEventManager_.removeAll();
891
+ shaka.util.Dom.removeAllChildren(this.chaptersContainer_);
892
+ }
608
893
  };
609
894
 
610
895
 
896
+ /**
897
+ * @const {string}
898
+ * @private
899
+ */
900
+ shaka.ui.SeekBar.Transparent_Image_ =
901
+ 'data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg"/>';
902
+
903
+
611
904
  /**
612
905
  * @implements {shaka.extern.IUISeekBar.Factory}
613
906
  * @export
@@ -135,7 +135,8 @@ shaka.ui.TextSelection = class extends shaka.ui.SettingsMenu {
135
135
  this.player.isTextTrackVisible(),
136
136
  this.currentSelection,
137
137
  this.localization,
138
- this.controls.getConfig().trackLabelFormat);
138
+ this.controls.getConfig().trackLabelFormat,
139
+ this.controls.getConfig().showAudioChannelCountVariants);
139
140
 
140
141
  // Add the Off button
141
142
  const offButton = shaka.util.Dom.createButton();
package/ui/ui.js CHANGED
@@ -229,6 +229,8 @@ shaka.ui.Overlay = class {
229
229
  buffered: 'rgba(255, 255, 255, 0.54)',
230
230
  played: 'rgb(255, 255, 255)',
231
231
  adBreaks: 'rgb(255, 204, 0)',
232
+ chapterMarks: 'rgb(27, 27, 27)',
233
+ chapterLabels: 'rgb(255, 255, 255)',
232
234
  },
233
235
  volumeBarColors: {
234
236
  base: 'rgba(255, 255, 255, 0.54)',
@@ -245,10 +247,14 @@ shaka.ui.Overlay = class {
245
247
  keyboardSeekDistance: 5,
246
248
  keyboardLargeSeekDistance: 60,
247
249
  fullScreenElement: this.videoContainer_,
250
+ preferDocumentPictureInPicture: true,
251
+ showAudioChannelCountVariants: true,
248
252
  };
249
253
 
250
- // Check AirPlay support
251
- if (window.WebKitPlaybackTargetAvailabilityEvent) {
254
+ // eslint-disable-next-line no-restricted-syntax
255
+ if ('remote' in HTMLMediaElement.prototype) {
256
+ config.overflowMenuButtons.push('remote');
257
+ } else if (window.WebKitPlaybackTargetAvailabilityEvent) {
252
258
  config.overflowMenuButtons.push('airplay');
253
259
  }
254
260
 
@@ -422,8 +428,7 @@ shaka.ui.Overlay = class {
422
428
  */
423
429
  static async setupUIandAutoLoad_(container, video, canvas) {
424
430
  // Create the UI
425
- const player = new shaka.Player(
426
- shaka.util.Dom.asHTMLMediaElement(video));
431
+ const player = new shaka.Player();
427
432
  const ui = new shaka.ui.Overlay(player,
428
433
  shaka.util.Dom.asHTMLElement(container),
429
434
  shaka.util.Dom.asHTMLMediaElement(video));
@@ -486,6 +491,8 @@ shaka.ui.Overlay = class {
486
491
  shaka.log.error('Error auto-loading asset', e);
487
492
  }
488
493
  }
494
+
495
+ await player.attach(shaka.util.Dom.asHTMLMediaElement(video));
489
496
  }
490
497
  };
491
498