shaka-player 4.3.9 → 4.4.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 (321) hide show
  1. package/.github/workflows/build-and-test.yaml +11 -18
  2. package/.github/workflows/compute-incremental-coverage.py +3 -0
  3. package/.github/workflows/release-please.yaml +7 -0
  4. package/.github/workflows/selenium-lab-tests.yaml +45 -10
  5. package/AUTHORS +9 -0
  6. package/CHANGELOG.md +295 -190
  7. package/CONTRIBUTORS +8 -0
  8. package/LICENSE +32 -0
  9. package/README.md +63 -10
  10. package/demo/autoTemplate.txt +40 -5
  11. package/demo/close_button.js +11 -0
  12. package/demo/common/asset.js +13 -1
  13. package/demo/common/assets.js +112 -28
  14. package/demo/common/message_ids.js +25 -1
  15. package/demo/config.js +69 -10
  16. package/demo/customWarning.txt +2 -2
  17. package/demo/index.html +3 -1
  18. package/demo/locales/en.json +26 -2
  19. package/demo/locales/source.json +105 -9
  20. package/demo/main.js +57 -9
  21. package/demo/search.js +3 -1
  22. package/demo/service_worker.js +3 -0
  23. package/dist/controls.css +1 -1
  24. package/dist/controls.css.map +1 -1
  25. package/dist/demo.compiled.debug.js +132 -118
  26. package/dist/demo.compiled.debug.map +3 -3
  27. package/dist/demo.compiled.js +132 -118
  28. package/dist/demo.compiled.map +3 -3
  29. package/dist/deps.js +46 -28
  30. package/dist/locales.js +31 -31
  31. package/dist/receiver.compiled.debug.js +60 -52
  32. package/dist/receiver.compiled.debug.map +3 -3
  33. package/dist/receiver.compiled.js +60 -52
  34. package/dist/receiver.compiled.map +3 -3
  35. package/dist/shaka-player.compiled.d.ts +676 -391
  36. package/dist/shaka-player.compiled.debug.d.ts +676 -391
  37. package/dist/shaka-player.compiled.debug.externs.js +1188 -616
  38. package/dist/shaka-player.compiled.debug.js +1046 -700
  39. package/dist/shaka-player.compiled.debug.map +4 -4
  40. package/dist/shaka-player.compiled.externs.js +1188 -616
  41. package/dist/shaka-player.compiled.js +1094 -933
  42. package/dist/shaka-player.compiled.map +4 -4
  43. package/dist/shaka-player.ui.d.ts +677 -392
  44. package/dist/shaka-player.ui.debug.d.ts +677 -392
  45. package/dist/shaka-player.ui.debug.externs.js +1023 -451
  46. package/dist/shaka-player.ui.debug.js +1101 -738
  47. package/dist/shaka-player.ui.debug.map +4 -4
  48. package/dist/shaka-player.ui.externs.js +1023 -451
  49. package/dist/shaka-player.ui.js +1244 -1071
  50. package/dist/shaka-player.ui.map +4 -4
  51. package/docs/tutorials/ad_monetization.md +15 -1
  52. package/docs/tutorials/application-level-redirects.md +1 -1
  53. package/docs/tutorials/config.md +9 -5
  54. package/docs/tutorials/drm-config.md +61 -0
  55. package/docs/tutorials/fairplay.md +19 -4
  56. package/docs/tutorials/license-server-auth.md +4 -4
  57. package/docs/tutorials/license-wrapping.md +2 -2
  58. package/docs/tutorials/ui-customization.md +7 -3
  59. package/docs/tutorials/ui.md +7 -7
  60. package/docs/tutorials/upgrade.md +3 -0
  61. package/externs/ima.js +123 -2
  62. package/externs/isoboxer.js +217 -0
  63. package/externs/mediakeys.js +18 -0
  64. package/externs/mux.js +1 -105
  65. package/externs/pictureinpicture.js +48 -0
  66. package/externs/shaka/ads.js +61 -1
  67. package/externs/shaka/cea.js +110 -0
  68. package/externs/shaka/codecs.js +32 -0
  69. package/externs/shaka/manifest.js +101 -1
  70. package/externs/shaka/manifest_parser.js +0 -10
  71. package/externs/shaka/net.js +36 -7
  72. package/externs/shaka/offline.js +8 -1
  73. package/externs/shaka/player.js +220 -21
  74. package/externs/shaka/text.js +1 -382
  75. package/externs/shaka/transmuxer.js +59 -0
  76. package/karma.conf.js +15 -0
  77. package/lib/abr/simple_abr_manager.js +6 -3
  78. package/lib/ads/ad_manager.js +60 -2
  79. package/lib/ads/client_side_ad.js +86 -0
  80. package/lib/ads/client_side_ad_manager.js +93 -5
  81. package/lib/ads/server_side_ad.js +86 -0
  82. package/lib/ads/server_side_ad_manager.js +13 -0
  83. package/lib/cast/cast_utils.js +1 -0
  84. package/lib/cea/cea608_data_channel.js +24 -14
  85. package/lib/cea/cea608_memory.js +5 -2
  86. package/lib/cea/cea708_service.js +8 -9
  87. package/lib/cea/cea708_window.js +1 -2
  88. package/lib/cea/cea_decoder.js +23 -6
  89. package/lib/cea/cea_utils.js +25 -2
  90. package/lib/cea/dummy_caption_decoder.js +21 -0
  91. package/lib/cea/dummy_cea_parser.js +1 -3
  92. package/lib/cea/mp4_cea_parser.js +15 -10
  93. package/lib/cea/ts_cea_parser.js +69 -0
  94. package/lib/dash/content_protection.js +32 -2
  95. package/lib/dash/dash_parser.js +93 -31
  96. package/lib/dash/mpd_utils.js +6 -25
  97. package/lib/dash/segment_base.js +12 -14
  98. package/lib/dash/segment_list.js +10 -6
  99. package/lib/dash/segment_template.js +349 -32
  100. package/lib/dependencies/all.js +8 -0
  101. package/lib/hls/hls_classes.js +33 -8
  102. package/lib/hls/hls_parser.js +805 -191
  103. package/lib/hls/manifest_text_parser.js +4 -14
  104. package/lib/media/adaptation_set_criteria.js +43 -3
  105. package/lib/media/closed_caption_parser.js +74 -12
  106. package/lib/media/drm_engine.js +189 -49
  107. package/lib/media/gap_jumping_controller.js +1 -1
  108. package/lib/media/manifest_parser.js +34 -0
  109. package/lib/media/media_source_capabilities.js +10 -0
  110. package/lib/media/media_source_engine.js +475 -231
  111. package/lib/media/playhead.js +1 -1
  112. package/lib/media/presentation_timeline.js +62 -0
  113. package/lib/media/segment_index.js +50 -14
  114. package/lib/media/segment_prefetch.js +277 -0
  115. package/lib/media/segment_reference.js +70 -4
  116. package/lib/media/streaming_engine.js +258 -81
  117. package/lib/media/video_wrapper.js +15 -7
  118. package/lib/mss/content_protection.js +353 -0
  119. package/lib/mss/mss_parser.js +1081 -0
  120. package/lib/net/networking_engine.js +60 -10
  121. package/lib/offline/indexeddb/v1_storage_cell.js +1 -0
  122. package/lib/offline/indexeddb/v2_storage_cell.js +1 -0
  123. package/lib/offline/manifest_converter.js +6 -0
  124. package/lib/offline/storage.js +2 -4
  125. package/lib/player.js +309 -105
  126. package/lib/polyfill/media_capabilities.js +43 -3
  127. package/lib/polyfill/patchedmediakeys_apple.js +5 -0
  128. package/lib/polyfill/patchedmediakeys_nop.js +5 -0
  129. package/lib/polyfill/patchedmediakeys_webkit.js +5 -0
  130. package/lib/text/cue.js +208 -130
  131. package/lib/text/lrc_text_parser.js +2 -2
  132. package/lib/text/mp4_vtt_parser.js +2 -2
  133. package/lib/text/sbv_text_parser.js +1 -1
  134. package/lib/text/simple_text_displayer.js +2 -1
  135. package/lib/text/srt_text_parser.js +9 -1
  136. package/lib/text/ssa_text_parser.js +2 -2
  137. package/lib/text/text_engine.js +3 -21
  138. package/lib/text/text_utils.js +71 -20
  139. package/lib/text/ui_text_displayer.js +14 -14
  140. package/lib/text/vtt_text_parser.js +22 -14
  141. package/lib/transmuxer/aac_transmuxer.js +224 -0
  142. package/lib/transmuxer/ac3.js +142 -0
  143. package/lib/transmuxer/ac3_transmuxer.js +212 -0
  144. package/lib/transmuxer/adts.js +232 -0
  145. package/lib/transmuxer/ec3.js +103 -0
  146. package/lib/transmuxer/ec3_transmuxer.js +212 -0
  147. package/lib/transmuxer/h264.js +324 -0
  148. package/lib/transmuxer/mp3_transmuxer.js +204 -0
  149. package/lib/transmuxer/mpeg_audio.js +196 -0
  150. package/lib/transmuxer/mpeg_ts_transmuxer.js +182 -0
  151. package/lib/transmuxer/mss_transmuxer.js +389 -0
  152. package/lib/{media/transmuxer.js → transmuxer/muxjs_transmuxer.js} +34 -80
  153. package/lib/transmuxer/transmuxer_engine.js +161 -0
  154. package/lib/transmuxer/ts_transmuxer.js +738 -0
  155. package/lib/util/cmcd_manager.js +131 -71
  156. package/lib/util/error.js +62 -26
  157. package/lib/util/exp_golomb.js +220 -0
  158. package/lib/util/fairplay_utils.js +64 -13
  159. package/lib/util/id3_utils.js +56 -3
  160. package/lib/util/language_utils.js +11 -8
  161. package/lib/util/manifest_parser_utils.js +3 -0
  162. package/lib/util/mime_utils.js +44 -7
  163. package/lib/util/mp4_box_parsers.js +233 -5
  164. package/lib/util/mp4_generator.js +1381 -0
  165. package/lib/util/periods.js +40 -34
  166. package/lib/util/platform.js +25 -3
  167. package/lib/util/player_configuration.js +38 -1
  168. package/lib/util/stream_utils.js +138 -27
  169. package/lib/util/text_parser.js +1 -1
  170. package/lib/util/ts_parser.js +470 -47
  171. package/lib/util/xml_utils.js +16 -3
  172. package/maintained-branches.md +2 -2
  173. package/package.json +4 -2
  174. package/roadmap.md +21 -6
  175. package/shaka-player.uncompiled.js +18 -0
  176. package/test/abr/simple_abr_manager_unit.js +32 -4
  177. package/test/ads/ad_manager_unit.js +18 -2
  178. package/test/cast/cast_utils_unit.js +7 -2
  179. package/test/cea/cea608_memory_unit.js +24 -0
  180. package/test/cea/cea_decoder_unit.js +33 -0
  181. package/test/dash/dash_parser_content_protection_unit.js +93 -3
  182. package/test/dash/dash_parser_live_unit.js +56 -13
  183. package/test/dash/dash_parser_manifest_unit.js +70 -2
  184. package/test/dash/dash_parser_segment_base_unit.js +14 -9
  185. package/test/dash/dash_parser_segment_template_unit.js +286 -6
  186. package/test/demo/demo_unit.js +5 -1
  187. package/test/hls/hls_live_unit.js +188 -28
  188. package/test/hls/hls_parser_unit.js +250 -109
  189. package/test/hls/manifest_text_parser_unit.js +18 -13
  190. package/test/media/adaptation_set_criteria_unit.js +46 -14
  191. package/test/media/adaptation_set_unit.js +2 -0
  192. package/test/media/drm_engine_integration.js +11 -8
  193. package/test/media/drm_engine_unit.js +220 -7
  194. package/test/media/media_source_engine_integration.js +122 -38
  195. package/test/media/media_source_engine_unit.js +133 -153
  196. package/test/media/playhead_unit.js +3 -0
  197. package/test/media/presentation_timeline_unit.js +51 -0
  198. package/test/media/segment_index_unit.js +52 -0
  199. package/test/media/segment_prefetch_unit.js +243 -0
  200. package/test/media/streaming_engine_integration.js +9 -3
  201. package/test/media/streaming_engine_unit.js +440 -59
  202. package/test/mss/mss_parser_content_protection_unit.js +49 -0
  203. package/test/mss/mss_parser_unit.js +472 -0
  204. package/test/mss/mss_player_integration.js +123 -0
  205. package/test/offline/manifest_convert_unit.js +9 -0
  206. package/test/offline/storage_integration.js +6 -1
  207. package/test/player_integration.js +4 -1
  208. package/test/player_unit.js +386 -133
  209. package/test/polyfill/media_capabilities_unit.js +18 -0
  210. package/test/test/assets/hls-raw-aac/fileSequence0.aac +0 -0
  211. package/test/test/assets/hls-raw-aac/fileSequence1.aac +0 -0
  212. package/test/test/assets/hls-raw-aac/fileSequence2.aac +0 -0
  213. package/test/test/assets/hls-raw-aac/fileSequence3.aac +0 -0
  214. package/test/test/assets/hls-raw-aac/manifest.m3u8 +4 -0
  215. package/test/test/assets/hls-raw-aac/stream.m4a.m3u8 +14 -0
  216. package/test/test/assets/hls-raw-ac3/fileSequence0.ac3 +0 -0
  217. package/test/test/assets/hls-raw-ac3/fileSequence1.ac3 +0 -0
  218. package/test/test/assets/hls-raw-ac3/fileSequence2.ac3 +0 -0
  219. package/test/test/assets/hls-raw-ac3/fileSequence3.ac3 +0 -0
  220. package/test/test/assets/hls-raw-ac3/fileSequence4.ac3 +0 -0
  221. package/test/test/assets/hls-raw-ac3/fileSequence5.ac3 +0 -0
  222. package/test/test/assets/hls-raw-ac3/fileSequence6.ac3 +0 -0
  223. package/test/test/assets/hls-raw-ac3/fileSequence7.ac3 +0 -0
  224. package/test/test/assets/hls-raw-ac3/prog_index.m3u8 +31 -0
  225. package/test/test/assets/hls-raw-ec3/fileSequence0.ec3 +0 -0
  226. package/test/test/assets/hls-raw-ec3/fileSequence1.ec3 +0 -0
  227. package/test/test/assets/hls-raw-ec3/fileSequence2.ec3 +0 -0
  228. package/test/test/assets/hls-raw-ec3/fileSequence3.ec3 +0 -0
  229. package/test/test/assets/hls-raw-ec3/fileSequence4.ec3 +0 -0
  230. package/test/test/assets/hls-raw-ec3/fileSequence5.ec3 +0 -0
  231. package/test/test/assets/hls-raw-ec3/fileSequence6.ec3 +0 -0
  232. package/test/test/assets/hls-raw-ec3/fileSequence7.ec3 +0 -0
  233. package/test/test/assets/hls-raw-ec3/prog_index.m3u8 +31 -0
  234. package/test/test/assets/hls-raw-mp3/chunklist_w905519204.m3u8 +13 -0
  235. package/test/test/assets/hls-raw-mp3/media_w905519204_0.mp3 +0 -0
  236. package/test/test/assets/hls-raw-mp3/media_w905519204_1.mp3 +0 -0
  237. package/test/test/assets/hls-raw-mp3/media_w905519204_2.mp3 +0 -0
  238. package/test/test/assets/hls-raw-mp3/media_w905519204_3.mp3 +0 -0
  239. package/test/test/assets/hls-raw-mp3/playlist.m3u8 +4 -0
  240. package/test/test/assets/hls-ts-aac/playlist.m3u8 +23 -0
  241. package/test/test/assets/hls-ts-aac/segment_0.ts +0 -0
  242. package/test/test/assets/hls-ts-aac/segment_1.ts +0 -0
  243. package/test/test/assets/hls-ts-aac/segment_2.ts +0 -0
  244. package/test/test/assets/hls-ts-aac/segment_3.ts +0 -0
  245. package/test/test/assets/hls-ts-aac/segment_4.ts +0 -0
  246. package/test/test/assets/hls-ts-aac/segment_5.ts +0 -0
  247. package/test/test/assets/hls-ts-aac/segment_6.ts +0 -0
  248. package/test/test/assets/hls-ts-aac/segment_7.ts +0 -0
  249. package/test/test/assets/hls-ts-ac3/ChID_voices_6ch_640kbps_dd_sub_0000.ts +0 -0
  250. package/test/test/assets/hls-ts-ac3/ChID_voices_6ch_640kbps_dd_sub_0001.ts +0 -0
  251. package/test/test/assets/hls-ts-ac3/ChID_voices_6ch_640kbps_dd_sub_0002.ts +0 -0
  252. package/test/test/assets/hls-ts-ac3/ChID_voices_6ch_640kbps_dd_sub_0003.ts +0 -0
  253. package/test/test/assets/hls-ts-ac3/ChID_voices_6ch_640kbps_dd_sub_0004.ts +0 -0
  254. package/test/test/assets/hls-ts-ac3/ChID_voices_6ch_640kbps_dd_sub_0005.ts +0 -0
  255. package/test/test/assets/hls-ts-ac3/prog_index.m3u8 +19 -0
  256. package/test/test/assets/hls-ts-ec3/ChID_voices_6ch_256kbps_ddp_sub_0000.ts +0 -0
  257. package/test/test/assets/hls-ts-ec3/ChID_voices_6ch_256kbps_ddp_sub_0001.ts +0 -0
  258. package/test/test/assets/hls-ts-ec3/ChID_voices_6ch_256kbps_ddp_sub_0002.ts +0 -0
  259. package/test/test/assets/hls-ts-ec3/ChID_voices_6ch_256kbps_ddp_sub_0003.ts +0 -0
  260. package/test/test/assets/hls-ts-ec3/ChID_voices_6ch_256kbps_ddp_sub_0004.ts +0 -0
  261. package/test/test/assets/hls-ts-ec3/ChID_voices_6ch_256kbps_ddp_sub_0005.ts +0 -0
  262. package/test/test/assets/hls-ts-ec3/prog_index.m3u8 +19 -0
  263. package/test/test/assets/hls-ts-h264/fileSequence0.ts +0 -0
  264. package/test/test/assets/hls-ts-h264/fileSequence1.ts +0 -0
  265. package/test/test/assets/hls-ts-h264/fileSequence2.ts +0 -0
  266. package/test/test/assets/hls-ts-h264/fileSequence3.ts +0 -0
  267. package/test/test/assets/hls-ts-h264/fileSequence4.ts +0 -0
  268. package/test/test/assets/hls-ts-h264/fileSequence5.ts +0 -0
  269. package/test/test/assets/hls-ts-h264/prog_index.m3u8 +25 -0
  270. package/test/test/assets/hls-ts-mp3/manifest.m3u8 +13 -0
  271. package/test/test/assets/hls-ts-mp3/media_w239891843_0.ts +0 -0
  272. package/test/test/assets/hls-ts-mp3/media_w239891843_1.ts +0 -0
  273. package/test/test/assets/hls-ts-mp3/media_w239891843_2.ts +0 -0
  274. package/test/test/assets/hls-ts-mp3/media_w239891843_3.ts +0 -0
  275. package/test/test/assets/hls-ts-muxed-aac-h264/chunk.m3u8 +17 -0
  276. package/test/test/assets/hls-ts-muxed-aac-h264/n_0_0_0.ts +0 -0
  277. package/test/test/assets/hls-ts-muxed-aac-h264/n_1_0_0.ts +0 -0
  278. package/test/test/assets/hls-ts-muxed-aac-h264/n_2_0_0.ts +0 -0
  279. package/test/test/assets/hls-ts-muxed-aac-h264/n_3_0_0.ts +0 -0
  280. package/test/test/assets/hls-ts-muxed-aac-h264/n_4_0_0.ts +0 -0
  281. package/test/test/assets/hls-ts-muxed-aac-h264/n_5_0_0.ts +0 -0
  282. package/test/test/assets/hls-ts-muxed-aac-h264/playlist.m3u8 +4 -0
  283. package/test/test/boot.js +7 -2
  284. package/test/test/util/fake_ad.js +99 -0
  285. package/test/test/util/fake_ad_manager.js +7 -1
  286. package/test/test/util/fake_networking_engine.js +41 -14
  287. package/test/test/util/fake_segment_prefetch.js +81 -0
  288. package/test/test/util/manifest_generator.js +19 -5
  289. package/test/test/util/mss_parser_util.js +115 -0
  290. package/test/test/util/offline_utils.js +1 -0
  291. package/test/test/util/simple_fakes.js +12 -3
  292. package/test/test/util/streaming_engine_util.js +14 -4
  293. package/test/test/util/ttml_utils.js +2 -2
  294. package/test/test/util/ui_utils.js +5 -1
  295. package/test/text/cue_integration.js +1 -1
  296. package/test/text/srt_text_parser_unit.js +78 -0
  297. package/test/text/text_engine_unit.js +17 -8
  298. package/test/text/ui_text_displayer_unit.js +4 -4
  299. package/test/text/vtt_text_parser_unit.js +59 -40
  300. package/test/text/web_vtt_generator_unit.js +9 -3
  301. package/test/{media/transmuxer_integration.js → transmuxer/muxjs_transmuxer_integration.js} +30 -33
  302. package/test/transmuxer/transmuxer_engine_integration.js +60 -0
  303. package/test/transmuxer/transmuxer_integration.js +296 -0
  304. package/test/util/cmcd_manager_unit.js +162 -13
  305. package/test/util/language_utils_unit.js +9 -2
  306. package/test/util/mp4_box_parsers_unit.js +39 -4
  307. package/test/util/periods_unit.js +34 -26
  308. package/test/util/stream_utils_unit.js +60 -107
  309. package/ui/controls.js +19 -1
  310. package/ui/controls.less +1 -0
  311. package/ui/externs/ui.js +13 -3
  312. package/ui/language_utils.js +15 -0
  313. package/ui/less/thumbnails.less +26 -0
  314. package/ui/locales/oc.json +42 -0
  315. package/ui/pip_button.js +83 -2
  316. package/ui/range_element.js +15 -0
  317. package/ui/resolution_selection.js +9 -2
  318. package/ui/seek_bar.js +230 -1
  319. package/ui/ui.js +5 -0
  320. package/lib/cea/i_caption_decoder.js +0 -51
  321. package/lib/cea/i_cea_parser.js +0 -68
package/ui/pip_button.js CHANGED
@@ -36,6 +36,9 @@ shaka.ui.PipButton = class extends shaka.ui.Element {
36
36
  /** @private {HTMLMediaElement} */
37
37
  this.localVideo_ = this.controls.getLocalVideo();
38
38
 
39
+ /** @private {HTMLElement } */
40
+ this.videoContainer_ = this.controls.getVideoContainer();
41
+
39
42
  const LocIds = shaka.ui.Locales.Ids;
40
43
  /** @private {!HTMLButtonElement} */
41
44
  this.pipButton_ = shaka.util.Dom.createButton();
@@ -111,8 +114,8 @@ shaka.ui.PipButton = class extends shaka.ui.Element {
111
114
  * @private
112
115
  */
113
116
  isPipAllowed_() {
114
- return document.pictureInPictureEnabled &&
115
- !this.video.disablePictureInPicture;
117
+ return ('documentPictureInPicture' in window) ||
118
+ (document.pictureInPictureEnabled && !this.video.disablePictureInPicture);
116
119
  }
117
120
 
118
121
 
@@ -122,6 +125,10 @@ shaka.ui.PipButton = class extends shaka.ui.Element {
122
125
  */
123
126
  async onPipClick_() {
124
127
  try {
128
+ if ('documentPictureInPicture' in window) {
129
+ await this.toggleDocumentPictureInPicture_();
130
+ return;
131
+ }
125
132
  if (!document.pictureInPictureElement) {
126
133
  // If you were fullscreen, leave fullscreen first.
127
134
  if (document.fullscreenElement) {
@@ -137,6 +144,80 @@ shaka.ui.PipButton = class extends shaka.ui.Element {
137
144
  }
138
145
  }
139
146
 
147
+ /**
148
+ * The Document Picture-in-Picture API makes it possible to open an
149
+ * always-on-top window that can be populated with arbitrary HTML content.
150
+ * https://developer.chrome.com/docs/web-platform/document-picture-in-picture
151
+ * @private
152
+ */
153
+ async toggleDocumentPictureInPicture_() {
154
+ // Close Picture-in-Picture window if any.
155
+ if (window.documentPictureInPicture.window) {
156
+ window.documentPictureInPicture.window.close();
157
+ this.onLeavePictureInPicture_();
158
+ return;
159
+ }
160
+
161
+ // Open a Picture-in-Picture window.
162
+ const pipPlayer = this.videoContainer_;
163
+ const rectPipPlayer = pipPlayer.getBoundingClientRect();
164
+ const pipWindow = await window.documentPictureInPicture.requestWindow({
165
+ width: rectPipPlayer.width,
166
+ height: rectPipPlayer.height,
167
+ });
168
+
169
+ // Copy style sheets to the Picture-in-Picture window.
170
+ this.copyStyleSheetsToWindow_(pipWindow);
171
+
172
+ // Add placeholder for the player.
173
+ const parentPlayer = pipPlayer.parentNode || document.body;
174
+ const placeholder = this.videoContainer_.cloneNode(true);
175
+ placeholder.style.visibility = 'hidden';
176
+ placeholder.style.height = getComputedStyle(pipPlayer).height;
177
+ parentPlayer.appendChild(placeholder);
178
+
179
+ // Make sure player fits in the Picture-in-Picture window.
180
+ const styles = document.createElement('style');
181
+ styles.append(`[data-shaka-player-container] {
182
+ width: 100% !important; max-height: 100%}`);
183
+ pipWindow.document.head.append(styles);
184
+
185
+ // Move player to the Picture-in-Picture window.
186
+ pipWindow.document.body.append(pipPlayer);
187
+ this.onEnterPictureInPicture_();
188
+
189
+ // Listen for the PiP closing event to move the player back.
190
+ this.eventManager.listenOnce(pipWindow, 'pagehide', () => {
191
+ placeholder.replaceWith(/** @type {!Node} */(pipPlayer));
192
+ });
193
+ }
194
+
195
+ /** @private */
196
+ copyStyleSheetsToWindow_(win) {
197
+ const styleSheets = /** @type {!Iterable<*>} */(document.styleSheets);
198
+ const allCSS = [...styleSheets]
199
+ .map((sheet) => {
200
+ try {
201
+ return [...sheet.cssRules].map((rule) => rule.cssText).join('');
202
+ } catch (e) {
203
+ const link = /** @type {!HTMLLinkElement} */(
204
+ document.createElement('link'));
205
+
206
+ link.rel = 'stylesheet';
207
+ link.type = sheet.type;
208
+ link.media = sheet.media;
209
+ link.href = sheet.href;
210
+ win.document.head.appendChild(link);
211
+ }
212
+ return '';
213
+ })
214
+ .filter(Boolean)
215
+ .join('\n');
216
+ const style = document.createElement('style');
217
+
218
+ style.textContent = allCSS;
219
+ win.document.head.appendChild(style);
220
+ }
140
221
 
141
222
  /** @private */
142
223
  onEnterPictureInPicture_() {
@@ -104,6 +104,14 @@ shaka.ui.RangeElement = class extends shaka.ui.Element {
104
104
  }
105
105
  });
106
106
 
107
+ this.eventManager.listen(this.bar, 'touchcancel', (e) => {
108
+ if (this.isChanging_) {
109
+ this.isChanging_ = false;
110
+ this.setBarValueForTouch_(e);
111
+ this.onChangeEnd();
112
+ }
113
+ });
114
+
107
115
  this.eventManager.listen(this.bar, 'mouseup', () => {
108
116
  if (this.isChanging_) {
109
117
  this.isChanging_ = false;
@@ -111,6 +119,13 @@ shaka.ui.RangeElement = class extends shaka.ui.Element {
111
119
  }
112
120
  });
113
121
 
122
+ this.eventManager.listen(this.bar, 'blur', () => {
123
+ if (this.isChanging_) {
124
+ this.isChanging_ = false;
125
+ this.onChangeEnd();
126
+ }
127
+ });
128
+
114
129
  this.eventManager.listen(this.bar, 'contextmenu', (e) => {
115
130
  e.preventDefault();
116
131
  e.stopPropagation();
@@ -132,8 +132,15 @@ shaka.ui.ResolutionSelection = class extends shaka.ui.SettingsMenu {
132
132
  () => this.onTrackSelected_(track));
133
133
 
134
134
  const span = shaka.util.Dom.createHTMLElement('span');
135
- span.textContent = this.player.isAudioOnly() ?
136
- Math.round(track.bandwidth / 1000) + ' kbits/s' : track.height + 'p';
135
+ if (this.player.isAudioOnly()) {
136
+ span.textContent = Math.round(track.bandwidth / 1000) + ' kbits/s';
137
+ } 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
+ }
143
+ }
137
144
  button.appendChild(span);
138
145
 
139
146
  if (!abrEnabled && track == selectedTrack) {
package/ui/seek_bar.js CHANGED
@@ -59,7 +59,13 @@ shaka.ui.SeekBar = class extends shaka.ui.RangeElement {
59
59
  * @private {shaka.util.Timer}
60
60
  */
61
61
  this.seekTimer_ = new shaka.util.Timer(() => {
62
- this.video.currentTime = this.getValue();
62
+ let newCurrentTime = this.getValue();
63
+ if (!this.player.isLive()) {
64
+ if (newCurrentTime == this.video.duration) {
65
+ newCurrentTime -= 0.001;
66
+ }
67
+ }
68
+ this.video.currentTime = newCurrentTime;
63
69
  });
64
70
 
65
71
 
@@ -83,6 +89,41 @@ shaka.ui.SeekBar = class extends shaka.ui.RangeElement {
83
89
  */
84
90
  this.wasPlaying_ = false;
85
91
 
92
+
93
+ /** @private {!HTMLElement} */
94
+ this.thumbnailContainer_ = shaka.util.Dom.createHTMLElement('div');
95
+ this.thumbnailContainer_.id = 'shaka-player-ui-thumbnail-container';
96
+
97
+ /** @private {!HTMLImageElement} */
98
+ this.thumbnailImage_ = /** @type {!HTMLImageElement} */ (
99
+ shaka.util.Dom.createHTMLElement('img'));
100
+ this.thumbnailImage_.id = 'shaka-player-ui-thumbnail-image';
101
+ this.thumbnailImage_.draggable = false;
102
+
103
+ /** @private {!HTMLElement} */
104
+ this.thumbnailTime_ = shaka.util.Dom.createHTMLElement('div');
105
+ this.thumbnailTime_.id = 'shaka-player-ui-thumbnail-time';
106
+
107
+ this.thumbnailContainer_.appendChild(this.thumbnailImage_);
108
+ this.thumbnailContainer_.appendChild(this.thumbnailTime_);
109
+ this.container.appendChild(this.thumbnailContainer_);
110
+
111
+ /**
112
+ * True if the bar is moving due to touchscreen or keyboard events.
113
+ *
114
+ * @private {boolean}
115
+ */
116
+ this.isMoving_ = false;
117
+
118
+ /**
119
+ * The timer is activated to hide the thumbnail.
120
+ *
121
+ * @private {shaka.util.Timer}
122
+ */
123
+ this.hideThumbnailTimer_ = new shaka.util.Timer(() => {
124
+ this.hideThumbnail_();
125
+ });
126
+
86
127
  /** @private {!Array.<!shaka.extern.AdCuePoint>} */
87
128
  this.adCuePoints_ = [];
88
129
 
@@ -120,6 +161,29 @@ shaka.ui.SeekBar = class extends shaka.ui.RangeElement {
120
161
  this.onAdCuePointsChanged_();
121
162
  });
122
163
 
164
+ this.eventManager.listen(this.bar, 'mousemove', (event) => {
165
+ if (!this.player.getImageTracks().length) {
166
+ this.hideThumbnail_();
167
+ return;
168
+ }
169
+ const rect = this.bar.getBoundingClientRect();
170
+ const min = parseFloat(this.bar.min);
171
+ const max = parseFloat(this.bar.max);
172
+ // Pixels from the left of the range element
173
+ const mousePosition = event.clientX - rect.left;
174
+ // Pixels per unit value of the range element.
175
+ const scale = (max - min) / rect.width;
176
+ // Mouse position in units, which may be outside the allowed range.
177
+ const value = Math.round(min + scale * mousePosition);
178
+ // Show Thumbnail
179
+ this.showThumbnail_(mousePosition, value);
180
+ });
181
+
182
+ this.eventManager.listen(this.container, 'mouseleave', () => {
183
+ this.hideThumbnailTimer_.stop();
184
+ this.hideThumbnailTimer_.tickAfter(/* seconds= */ 0.25);
185
+ });
186
+
123
187
  // Initialize seek state and label.
124
188
  this.setValue(this.video.currentTime);
125
189
  this.update();
@@ -153,6 +217,8 @@ shaka.ui.SeekBar = class extends shaka.ui.RangeElement {
153
217
  this.wasPlaying_ = !this.video.paused;
154
218
  this.controls.setSeeking(true);
155
219
  this.video.pause();
220
+ this.hideThumbnailTimer_.stop();
221
+ this.isMoving_ = true;
156
222
  }
157
223
 
158
224
  /**
@@ -180,6 +246,18 @@ shaka.ui.SeekBar = class extends shaka.ui.RangeElement {
180
246
  // Calling |start| on an already pending timer will cancel the old request
181
247
  // and start the new one.
182
248
  this.seekTimer_.tickAfter(/* seconds= */ 0.125);
249
+
250
+ if (this.player.getImageTracks().length) {
251
+ const min = parseFloat(this.bar.min);
252
+ const max = parseFloat(this.bar.max);
253
+ const rect = this.bar.getBoundingClientRect();
254
+ const value = Math.round(this.getValue());
255
+ const scale = (max - min) / rect.width;
256
+ const position = (value - min) / scale;
257
+ this.showThumbnail_(position, value);
258
+ } else {
259
+ this.hideThumbnail_();
260
+ }
183
261
  }
184
262
 
185
263
  /**
@@ -197,6 +275,12 @@ shaka.ui.SeekBar = class extends shaka.ui.RangeElement {
197
275
  if (this.wasPlaying_) {
198
276
  this.video.play();
199
277
  }
278
+
279
+ if (this.isMoving_) {
280
+ this.isMoving_ = false;
281
+ this.hideThumbnailTimer_.stop();
282
+ this.hideThumbnailTimer_.tickAfter(/* seconds= */ 0.25);
283
+ }
200
284
  }
201
285
 
202
286
  /**
@@ -376,6 +460,151 @@ shaka.ui.SeekBar = class extends shaka.ui.RangeElement {
376
460
  updateAriaLabel_() {
377
461
  this.bar.ariaLabel = this.localization.resolve(shaka.ui.Locales.Ids.SEEK);
378
462
  }
463
+
464
+
465
+ /**
466
+ * @private
467
+ */
468
+ async showThumbnail_(pixelPosition, value) {
469
+ const thumbnailTrack = this.getThumbnailTrack_();
470
+ if (!thumbnailTrack) {
471
+ this.hideThumbnail_();
472
+ return;
473
+ }
474
+ if (value < 0) {
475
+ value = 0;
476
+ }
477
+ const seekRange = this.player.seekRange();
478
+ const playerValue = Math.max(Math.ceil(seekRange.start),
479
+ Math.min(Math.floor(seekRange.end), value));
480
+ const thumbnail =
481
+ await this.player.getThumbnails(thumbnailTrack.id, playerValue);
482
+ if (!thumbnail || !thumbnail.uris.length) {
483
+ this.hideThumbnail_();
484
+ return;
485
+ }
486
+ if (this.player.isLive()) {
487
+ const totalSeconds = seekRange.end - value;
488
+ if (totalSeconds < 1) {
489
+ this.thumbnailTime_.textContent =
490
+ this.localization.resolve(shaka.ui.Locales.Ids.LIVE);
491
+ } else {
492
+ this.thumbnailTime_.textContent =
493
+ '-' + this.timeFormatter_(totalSeconds);
494
+ }
495
+ } else {
496
+ this.thumbnailTime_.textContent = this.timeFormatter_(value);
497
+ }
498
+ const offsetTop = -10;
499
+ const width = this.thumbnailContainer_.clientWidth;
500
+ let height = Math.floor(width * 9 / 16);
501
+ this.thumbnailContainer_.style.height = height + 'px';
502
+ this.thumbnailContainer_.style.top = -(height - offsetTop) + 'px';
503
+ const leftPosition = Math.min(this.bar.offsetWidth - width,
504
+ Math.max(0, pixelPosition - (width / 2)));
505
+ this.thumbnailContainer_.style.left = leftPosition + 'px';
506
+ this.thumbnailContainer_.style.visibility = 'visible';
507
+ const uri = thumbnail.uris[0].split('#xywh=')[0];
508
+ if (uri !== this.thumbnailImage_.src) {
509
+ try {
510
+ this.thumbnailContainer_.removeChild(this.thumbnailImage_);
511
+ } catch (e) {
512
+ // The image is not a child
513
+ }
514
+ this.thumbnailImage_ = /** @type {!HTMLImageElement} */ (
515
+ shaka.util.Dom.createHTMLElement('img'));
516
+ this.thumbnailImage_.id = 'shaka-player-ui-thumbnail-image';
517
+ this.thumbnailImage_.draggable = false;
518
+ this.thumbnailImage_.src = uri;
519
+ this.thumbnailContainer_.insertBefore(this.thumbnailImage_,
520
+ this.thumbnailContainer_.firstChild);
521
+ }
522
+ const scale = width / thumbnail.width;
523
+ if (thumbnail.imageHeight) {
524
+ this.thumbnailImage_.height = thumbnail.imageHeight;
525
+ } else if (!thumbnail.sprite) {
526
+ this.thumbnailImage_.style.height = '100%';
527
+ this.thumbnailImage_.style.objectFit = 'contain';
528
+ }
529
+ if (thumbnail.imageWidth) {
530
+ this.thumbnailImage_.width = thumbnail.imageWidth;
531
+ } else if (!thumbnail.sprite) {
532
+ this.thumbnailImage_.style.width = '100%';
533
+ this.thumbnailImage_.style.objectFit = 'contain';
534
+ }
535
+ this.thumbnailImage_.style.left = '-' + scale * thumbnail.positionX + 'px';
536
+ this.thumbnailImage_.style.top = '-' + scale * thumbnail.positionY + 'px';
537
+ this.thumbnailImage_.style.transform = 'scale(' + scale + ')';
538
+ this.thumbnailImage_.style.transformOrigin = 'left top';
539
+ // Update container height and top
540
+ height = Math.floor(width * thumbnail.height / thumbnail.width);
541
+ this.thumbnailContainer_.style.height = height + 'px';
542
+ this.thumbnailContainer_.style.top = -(height - offsetTop) + 'px';
543
+ }
544
+
545
+
546
+ /**
547
+ * @return {?shaka.extern.Track} The thumbnail track.
548
+ * @private
549
+ */
550
+ getThumbnailTrack_() {
551
+ const imageTracks = this.player.getImageTracks();
552
+ if (!imageTracks.length) {
553
+ return null;
554
+ }
555
+ const mimeTypesPreference = [
556
+ 'image/avif',
557
+ 'image/webp',
558
+ 'image/jpeg',
559
+ 'image/png',
560
+ 'image/svg+xml',
561
+ ];
562
+ for (const mimeType of mimeTypesPreference) {
563
+ const estimatedBandwidth = this.player.getStats().estimatedBandwidth;
564
+ const bestOptions = imageTracks.filter((track) => {
565
+ return track.mimeType.toLowerCase() === mimeType &&
566
+ track.bandwidth < estimatedBandwidth * 0.01;
567
+ }).sort((a, b) => {
568
+ return b.bandwidth - a.bandwidth;
569
+ });
570
+ if (bestOptions && bestOptions.length) {
571
+ return bestOptions[0];
572
+ }
573
+ }
574
+ return imageTracks[0];
575
+ }
576
+
577
+
578
+ /**
579
+ * @private
580
+ */
581
+ hideThumbnail_() {
582
+ this.thumbnailContainer_.style.visibility = 'hidden';
583
+ this.thumbnailTime_.textContent = '';
584
+ }
585
+
586
+
587
+ /**
588
+ * @param {number} totalSeconds
589
+ * @private
590
+ */
591
+ timeFormatter_(totalSeconds) {
592
+ const secondsNumber = Math.round(totalSeconds);
593
+ const hours = Math.floor(secondsNumber / 3600);
594
+ let minutes = Math.floor((secondsNumber - (hours * 3600)) / 60);
595
+ let seconds = secondsNumber - (hours * 3600) - (minutes * 60);
596
+ if (seconds < 10) {
597
+ seconds = '0' + seconds;
598
+ }
599
+ if (hours > 0) {
600
+ if (minutes < 10) {
601
+ minutes = '0' + minutes;
602
+ }
603
+ return hours + ':' + minutes + ':' + seconds;
604
+ } else {
605
+ return minutes + ':' + seconds;
606
+ }
607
+ }
379
608
  };
380
609
 
381
610
 
package/ui/ui.js CHANGED
@@ -34,6 +34,9 @@ shaka.ui.Overlay = class {
34
34
  /** @private {shaka.Player} */
35
35
  this.player_ = player;
36
36
 
37
+ /** @private {HTMLElement} */
38
+ this.videoContainer_ = videoContainer;
39
+
37
40
  /** @private {!shaka.extern.UIConfiguration} */
38
41
  this.config_ = this.defaultConfig_();
39
42
 
@@ -240,6 +243,8 @@ shaka.ui.Overlay = class {
240
243
  forceLandscapeOnFullscreen: true,
241
244
  enableTooltips: false,
242
245
  keyboardSeekDistance: 5,
246
+ keyboardLargeSeekDistance: 60,
247
+ fullScreenElement: this.videoContainer_,
243
248
  };
244
249
 
245
250
  // Check AirPlay support
@@ -1,51 +0,0 @@
1
- /*! @license
2
- * Shaka Player
3
- * Copyright 2016 Google LLC
4
- * SPDX-License-Identifier: Apache-2.0
5
- */
6
-
7
- goog.provide('shaka.cea.ICaptionDecoder');
8
-
9
- goog.require('shaka.text.Cue');
10
-
11
-
12
- /**
13
- * Interface for decoding inband closed captions from packets.
14
- * @interface
15
- */
16
- shaka.cea.ICaptionDecoder = class {
17
- /**
18
- * Extracts packets and prepares them for decoding. In a given media fragment,
19
- * all the caption packets found in its SEI messages should be extracted by
20
- * successive calls to extract(), followed by a single call to decode().
21
- *
22
- * @param {!Uint8Array} userDataSeiMessage
23
- * This is a User Data registered by Rec.ITU-T T.35 SEI message.
24
- * It is described in sections D.1.6 and D.2.6 of Rec. ITU-T H.264 (06/2019).
25
- * @param {number} pts PTS when this packet was received, in seconds.
26
- */
27
- extract(userDataSeiMessage, pts) {}
28
-
29
- /**
30
- * Decodes all currently extracted packets and then clears them.
31
- * This should be called once for a set of extracts (see comment on extract).
32
- * @return {!Array.<!shaka.cea.ICaptionDecoder.ClosedCaption>}
33
- */
34
- decode() {}
35
-
36
- /**
37
- * Clears the decoder state completely.
38
- * Should be used when an action renders the decoder state invalid,
39
- * e.g. unbuffered seeks.
40
- */
41
- clear() {}
42
- };
43
-
44
- /**
45
- * Parsed Cue.
46
- * @typedef {{
47
- * cue: !shaka.text.Cue,
48
- * stream: string
49
- * }}
50
- */
51
- shaka.cea.ICaptionDecoder.ClosedCaption;
@@ -1,68 +0,0 @@
1
- /*! @license
2
- * Shaka Player
3
- * Copyright 2016 Google LLC
4
- * SPDX-License-Identifier: Apache-2.0
5
- */
6
-
7
- goog.provide('shaka.cea.ICeaParser');
8
-
9
- /**
10
- * Interface for parsing inband closed caption data from MP4 streams.
11
- * @interface
12
- */
13
- shaka.cea.ICeaParser = class {
14
- /**
15
- * Initializes the parser with init segment data.
16
- * @param {!BufferSource} initSegment init segment to parse.
17
- */
18
- init(initSegment) {}
19
-
20
- /**
21
- * Parses the stream and extracts closed captions packets.
22
- * @param {!BufferSource} mediaSegment media segment to parse.
23
- * @return {!Array<!shaka.cea.ICeaParser.CaptionPacket>}
24
- */
25
- parse(mediaSegment) {}
26
- };
27
-
28
- /**
29
- * NALU type for Supplemental Enhancement Information (SEI) for H.264.
30
- * @const {number}
31
- */
32
- shaka.cea.ICeaParser.H264_NALU_TYPE_SEI = 0x06;
33
-
34
- /**
35
- * NALU type for Supplemental Enhancement Information (SEI) for H.265.
36
- * @const {number}
37
- */
38
- shaka.cea.ICeaParser.H265_PREFIX_NALU_TYPE_SEI = 0x27;
39
-
40
- /**
41
- * NALU type for Supplemental Enhancement Information (SEI) for H.265.
42
- * @const {number}
43
- */
44
- shaka.cea.ICeaParser.H265_SUFFIX_NALU_TYPE_SEI = 0x28;
45
-
46
- /**
47
- * Default timescale value for a track.
48
- */
49
- shaka.cea.ICeaParser.DEFAULT_TIMESCALE_VALUE = 90000;
50
-
51
- /**
52
- * @typedef {{
53
- * packet: !Uint8Array,
54
- * pts: number
55
- * }}
56
- *
57
- * @description Parsed Caption Packet.
58
- * @property {!Uint8Array} packet
59
- * Caption packet. More specifically, it contains a "User data
60
- * registered by Recommendation ITU-T T.35 SEI message", from section D.1.6
61
- * and section D.2.6 of Rec. ITU-T H.264 (06/2019).
62
- * @property {number} pts
63
- * The presentation timestamp (pts) at which the ITU-T T.35 data shows up.
64
- * in seconds.
65
- * @exportDoc
66
- */
67
- shaka.cea.ICeaParser.CaptionPacket;
68
-