shaka-player 4.3.10 → 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 (316) hide show
  1. package/.github/workflows/release-please.yaml +7 -0
  2. package/AUTHORS +9 -0
  3. package/CHANGELOG.md +295 -200
  4. package/CONTRIBUTORS +8 -0
  5. package/LICENSE +32 -0
  6. package/README.md +63 -10
  7. package/demo/autoTemplate.txt +40 -5
  8. package/demo/close_button.js +11 -0
  9. package/demo/common/asset.js +13 -1
  10. package/demo/common/assets.js +53 -3
  11. package/demo/common/message_ids.js +25 -1
  12. package/demo/config.js +69 -10
  13. package/demo/customWarning.txt +2 -2
  14. package/demo/index.html +3 -1
  15. package/demo/locales/en.json +26 -2
  16. package/demo/locales/source.json +105 -9
  17. package/demo/main.js +52 -9
  18. package/demo/search.js +3 -1
  19. package/demo/service_worker.js +3 -0
  20. package/dist/controls.css +1 -1
  21. package/dist/controls.css.map +1 -1
  22. package/dist/demo.compiled.debug.js +124 -112
  23. package/dist/demo.compiled.debug.map +3 -3
  24. package/dist/demo.compiled.js +124 -112
  25. package/dist/demo.compiled.map +3 -3
  26. package/dist/deps.js +46 -28
  27. package/dist/locales.js +28 -28
  28. package/dist/receiver.compiled.debug.js +52 -46
  29. package/dist/receiver.compiled.debug.map +3 -3
  30. package/dist/receiver.compiled.js +52 -46
  31. package/dist/receiver.compiled.map +3 -3
  32. package/dist/shaka-player.compiled.d.ts +676 -391
  33. package/dist/shaka-player.compiled.debug.d.ts +676 -391
  34. package/dist/shaka-player.compiled.debug.externs.js +1188 -616
  35. package/dist/shaka-player.compiled.debug.js +1045 -700
  36. package/dist/shaka-player.compiled.debug.map +4 -4
  37. package/dist/shaka-player.compiled.externs.js +1188 -616
  38. package/dist/shaka-player.compiled.js +1094 -933
  39. package/dist/shaka-player.compiled.map +4 -4
  40. package/dist/shaka-player.ui.d.ts +677 -392
  41. package/dist/shaka-player.ui.debug.d.ts +677 -392
  42. package/dist/shaka-player.ui.debug.externs.js +1023 -451
  43. package/dist/shaka-player.ui.debug.js +1100 -739
  44. package/dist/shaka-player.ui.debug.map +4 -4
  45. package/dist/shaka-player.ui.externs.js +1023 -451
  46. package/dist/shaka-player.ui.js +1244 -1071
  47. package/dist/shaka-player.ui.map +4 -4
  48. package/docs/tutorials/ad_monetization.md +15 -1
  49. package/docs/tutorials/application-level-redirects.md +1 -1
  50. package/docs/tutorials/config.md +9 -5
  51. package/docs/tutorials/drm-config.md +61 -0
  52. package/docs/tutorials/fairplay.md +19 -4
  53. package/docs/tutorials/license-server-auth.md +4 -4
  54. package/docs/tutorials/license-wrapping.md +2 -2
  55. package/docs/tutorials/ui-customization.md +7 -3
  56. package/docs/tutorials/ui.md +7 -7
  57. package/docs/tutorials/upgrade.md +3 -0
  58. package/externs/ima.js +123 -2
  59. package/externs/isoboxer.js +217 -0
  60. package/externs/mediakeys.js +18 -0
  61. package/externs/mux.js +1 -105
  62. package/externs/pictureinpicture.js +48 -0
  63. package/externs/shaka/ads.js +61 -1
  64. package/externs/shaka/cea.js +110 -0
  65. package/externs/shaka/codecs.js +32 -0
  66. package/externs/shaka/manifest.js +101 -1
  67. package/externs/shaka/manifest_parser.js +0 -10
  68. package/externs/shaka/net.js +36 -7
  69. package/externs/shaka/offline.js +8 -1
  70. package/externs/shaka/player.js +220 -21
  71. package/externs/shaka/text.js +1 -382
  72. package/externs/shaka/transmuxer.js +59 -0
  73. package/karma.conf.js +13 -0
  74. package/lib/abr/simple_abr_manager.js +6 -3
  75. package/lib/ads/ad_manager.js +60 -2
  76. package/lib/ads/client_side_ad.js +86 -0
  77. package/lib/ads/client_side_ad_manager.js +79 -3
  78. package/lib/ads/server_side_ad.js +86 -0
  79. package/lib/ads/server_side_ad_manager.js +13 -0
  80. package/lib/cast/cast_utils.js +1 -0
  81. package/lib/cea/cea608_data_channel.js +24 -14
  82. package/lib/cea/cea608_memory.js +5 -2
  83. package/lib/cea/cea708_service.js +8 -9
  84. package/lib/cea/cea708_window.js +1 -2
  85. package/lib/cea/cea_decoder.js +23 -6
  86. package/lib/cea/cea_utils.js +25 -2
  87. package/lib/cea/dummy_caption_decoder.js +21 -0
  88. package/lib/cea/dummy_cea_parser.js +1 -3
  89. package/lib/cea/mp4_cea_parser.js +15 -10
  90. package/lib/cea/ts_cea_parser.js +69 -0
  91. package/lib/dash/content_protection.js +32 -2
  92. package/lib/dash/dash_parser.js +93 -31
  93. package/lib/dash/mpd_utils.js +6 -25
  94. package/lib/dash/segment_base.js +12 -14
  95. package/lib/dash/segment_list.js +10 -6
  96. package/lib/dash/segment_template.js +349 -32
  97. package/lib/dependencies/all.js +8 -0
  98. package/lib/hls/hls_classes.js +33 -8
  99. package/lib/hls/hls_parser.js +805 -191
  100. package/lib/hls/manifest_text_parser.js +4 -14
  101. package/lib/media/adaptation_set_criteria.js +43 -3
  102. package/lib/media/closed_caption_parser.js +74 -12
  103. package/lib/media/drm_engine.js +189 -49
  104. package/lib/media/gap_jumping_controller.js +1 -1
  105. package/lib/media/manifest_parser.js +34 -0
  106. package/lib/media/media_source_capabilities.js +10 -0
  107. package/lib/media/media_source_engine.js +475 -231
  108. package/lib/media/playhead.js +1 -1
  109. package/lib/media/presentation_timeline.js +62 -0
  110. package/lib/media/segment_index.js +50 -14
  111. package/lib/media/segment_prefetch.js +277 -0
  112. package/lib/media/segment_reference.js +70 -4
  113. package/lib/media/streaming_engine.js +258 -81
  114. package/lib/media/video_wrapper.js +15 -7
  115. package/lib/mss/content_protection.js +353 -0
  116. package/lib/mss/mss_parser.js +1081 -0
  117. package/lib/net/networking_engine.js +60 -10
  118. package/lib/offline/indexeddb/v1_storage_cell.js +1 -0
  119. package/lib/offline/indexeddb/v2_storage_cell.js +1 -0
  120. package/lib/offline/manifest_converter.js +6 -0
  121. package/lib/offline/storage.js +2 -4
  122. package/lib/player.js +309 -105
  123. package/lib/polyfill/media_capabilities.js +43 -3
  124. package/lib/polyfill/patchedmediakeys_apple.js +5 -0
  125. package/lib/polyfill/patchedmediakeys_nop.js +5 -0
  126. package/lib/polyfill/patchedmediakeys_webkit.js +5 -0
  127. package/lib/text/cue.js +208 -130
  128. package/lib/text/lrc_text_parser.js +2 -2
  129. package/lib/text/mp4_vtt_parser.js +2 -2
  130. package/lib/text/sbv_text_parser.js +1 -1
  131. package/lib/text/simple_text_displayer.js +2 -1
  132. package/lib/text/srt_text_parser.js +9 -1
  133. package/lib/text/ssa_text_parser.js +2 -2
  134. package/lib/text/text_engine.js +3 -21
  135. package/lib/text/text_utils.js +71 -20
  136. package/lib/text/ui_text_displayer.js +14 -14
  137. package/lib/text/vtt_text_parser.js +22 -14
  138. package/lib/transmuxer/aac_transmuxer.js +224 -0
  139. package/lib/transmuxer/ac3.js +142 -0
  140. package/lib/transmuxer/ac3_transmuxer.js +212 -0
  141. package/lib/transmuxer/adts.js +232 -0
  142. package/lib/transmuxer/ec3.js +103 -0
  143. package/lib/transmuxer/ec3_transmuxer.js +212 -0
  144. package/lib/transmuxer/h264.js +324 -0
  145. package/lib/transmuxer/mp3_transmuxer.js +204 -0
  146. package/lib/transmuxer/mpeg_audio.js +196 -0
  147. package/lib/transmuxer/mpeg_ts_transmuxer.js +182 -0
  148. package/lib/transmuxer/mss_transmuxer.js +389 -0
  149. package/lib/{media/transmuxer.js → transmuxer/muxjs_transmuxer.js} +34 -80
  150. package/lib/transmuxer/transmuxer_engine.js +161 -0
  151. package/lib/transmuxer/ts_transmuxer.js +738 -0
  152. package/lib/util/cmcd_manager.js +131 -71
  153. package/lib/util/error.js +62 -26
  154. package/lib/util/exp_golomb.js +220 -0
  155. package/lib/util/fairplay_utils.js +64 -13
  156. package/lib/util/id3_utils.js +56 -3
  157. package/lib/util/language_utils.js +11 -8
  158. package/lib/util/manifest_parser_utils.js +3 -0
  159. package/lib/util/mime_utils.js +44 -7
  160. package/lib/util/mp4_box_parsers.js +233 -5
  161. package/lib/util/mp4_generator.js +1381 -0
  162. package/lib/util/periods.js +40 -34
  163. package/lib/util/platform.js +17 -3
  164. package/lib/util/player_configuration.js +38 -1
  165. package/lib/util/stream_utils.js +138 -27
  166. package/lib/util/text_parser.js +1 -1
  167. package/lib/util/ts_parser.js +470 -47
  168. package/lib/util/xml_utils.js +16 -3
  169. package/maintained-branches.md +2 -2
  170. package/package.json +3 -1
  171. package/roadmap.md +21 -6
  172. package/shaka-player.uncompiled.js +18 -0
  173. package/test/abr/simple_abr_manager_unit.js +32 -4
  174. package/test/ads/ad_manager_unit.js +18 -2
  175. package/test/cast/cast_utils_unit.js +7 -2
  176. package/test/cea/cea608_memory_unit.js +24 -0
  177. package/test/cea/cea_decoder_unit.js +33 -0
  178. package/test/dash/dash_parser_content_protection_unit.js +93 -3
  179. package/test/dash/dash_parser_live_unit.js +56 -13
  180. package/test/dash/dash_parser_manifest_unit.js +70 -2
  181. package/test/dash/dash_parser_segment_base_unit.js +14 -9
  182. package/test/dash/dash_parser_segment_template_unit.js +286 -6
  183. package/test/demo/demo_unit.js +5 -1
  184. package/test/hls/hls_live_unit.js +188 -28
  185. package/test/hls/hls_parser_unit.js +250 -109
  186. package/test/hls/manifest_text_parser_unit.js +18 -13
  187. package/test/media/adaptation_set_criteria_unit.js +46 -14
  188. package/test/media/adaptation_set_unit.js +2 -0
  189. package/test/media/drm_engine_integration.js +11 -8
  190. package/test/media/drm_engine_unit.js +220 -7
  191. package/test/media/media_source_engine_integration.js +122 -38
  192. package/test/media/media_source_engine_unit.js +133 -153
  193. package/test/media/playhead_unit.js +3 -0
  194. package/test/media/presentation_timeline_unit.js +51 -0
  195. package/test/media/segment_index_unit.js +52 -0
  196. package/test/media/segment_prefetch_unit.js +243 -0
  197. package/test/media/streaming_engine_integration.js +9 -3
  198. package/test/media/streaming_engine_unit.js +440 -59
  199. package/test/mss/mss_parser_content_protection_unit.js +49 -0
  200. package/test/mss/mss_parser_unit.js +472 -0
  201. package/test/mss/mss_player_integration.js +123 -0
  202. package/test/offline/manifest_convert_unit.js +9 -0
  203. package/test/offline/storage_integration.js +6 -1
  204. package/test/player_unit.js +386 -133
  205. package/test/polyfill/media_capabilities_unit.js +18 -0
  206. package/test/test/assets/hls-raw-aac/fileSequence0.aac +0 -0
  207. package/test/test/assets/hls-raw-aac/fileSequence1.aac +0 -0
  208. package/test/test/assets/hls-raw-aac/fileSequence2.aac +0 -0
  209. package/test/test/assets/hls-raw-aac/fileSequence3.aac +0 -0
  210. package/test/test/assets/hls-raw-aac/manifest.m3u8 +4 -0
  211. package/test/test/assets/hls-raw-aac/stream.m4a.m3u8 +14 -0
  212. package/test/test/assets/hls-raw-ac3/fileSequence0.ac3 +0 -0
  213. package/test/test/assets/hls-raw-ac3/fileSequence1.ac3 +0 -0
  214. package/test/test/assets/hls-raw-ac3/fileSequence2.ac3 +0 -0
  215. package/test/test/assets/hls-raw-ac3/fileSequence3.ac3 +0 -0
  216. package/test/test/assets/hls-raw-ac3/fileSequence4.ac3 +0 -0
  217. package/test/test/assets/hls-raw-ac3/fileSequence5.ac3 +0 -0
  218. package/test/test/assets/hls-raw-ac3/fileSequence6.ac3 +0 -0
  219. package/test/test/assets/hls-raw-ac3/fileSequence7.ac3 +0 -0
  220. package/test/test/assets/hls-raw-ac3/prog_index.m3u8 +31 -0
  221. package/test/test/assets/hls-raw-ec3/fileSequence0.ec3 +0 -0
  222. package/test/test/assets/hls-raw-ec3/fileSequence1.ec3 +0 -0
  223. package/test/test/assets/hls-raw-ec3/fileSequence2.ec3 +0 -0
  224. package/test/test/assets/hls-raw-ec3/fileSequence3.ec3 +0 -0
  225. package/test/test/assets/hls-raw-ec3/fileSequence4.ec3 +0 -0
  226. package/test/test/assets/hls-raw-ec3/fileSequence5.ec3 +0 -0
  227. package/test/test/assets/hls-raw-ec3/fileSequence6.ec3 +0 -0
  228. package/test/test/assets/hls-raw-ec3/fileSequence7.ec3 +0 -0
  229. package/test/test/assets/hls-raw-ec3/prog_index.m3u8 +31 -0
  230. package/test/test/assets/hls-raw-mp3/chunklist_w905519204.m3u8 +13 -0
  231. package/test/test/assets/hls-raw-mp3/media_w905519204_0.mp3 +0 -0
  232. package/test/test/assets/hls-raw-mp3/media_w905519204_1.mp3 +0 -0
  233. package/test/test/assets/hls-raw-mp3/media_w905519204_2.mp3 +0 -0
  234. package/test/test/assets/hls-raw-mp3/media_w905519204_3.mp3 +0 -0
  235. package/test/test/assets/hls-raw-mp3/playlist.m3u8 +4 -0
  236. package/test/test/assets/hls-ts-aac/playlist.m3u8 +23 -0
  237. package/test/test/assets/hls-ts-aac/segment_0.ts +0 -0
  238. package/test/test/assets/hls-ts-aac/segment_1.ts +0 -0
  239. package/test/test/assets/hls-ts-aac/segment_2.ts +0 -0
  240. package/test/test/assets/hls-ts-aac/segment_3.ts +0 -0
  241. package/test/test/assets/hls-ts-aac/segment_4.ts +0 -0
  242. package/test/test/assets/hls-ts-aac/segment_5.ts +0 -0
  243. package/test/test/assets/hls-ts-aac/segment_6.ts +0 -0
  244. package/test/test/assets/hls-ts-aac/segment_7.ts +0 -0
  245. package/test/test/assets/hls-ts-ac3/ChID_voices_6ch_640kbps_dd_sub_0000.ts +0 -0
  246. package/test/test/assets/hls-ts-ac3/ChID_voices_6ch_640kbps_dd_sub_0001.ts +0 -0
  247. package/test/test/assets/hls-ts-ac3/ChID_voices_6ch_640kbps_dd_sub_0002.ts +0 -0
  248. package/test/test/assets/hls-ts-ac3/ChID_voices_6ch_640kbps_dd_sub_0003.ts +0 -0
  249. package/test/test/assets/hls-ts-ac3/ChID_voices_6ch_640kbps_dd_sub_0004.ts +0 -0
  250. package/test/test/assets/hls-ts-ac3/ChID_voices_6ch_640kbps_dd_sub_0005.ts +0 -0
  251. package/test/test/assets/hls-ts-ac3/prog_index.m3u8 +19 -0
  252. package/test/test/assets/hls-ts-ec3/ChID_voices_6ch_256kbps_ddp_sub_0000.ts +0 -0
  253. package/test/test/assets/hls-ts-ec3/ChID_voices_6ch_256kbps_ddp_sub_0001.ts +0 -0
  254. package/test/test/assets/hls-ts-ec3/ChID_voices_6ch_256kbps_ddp_sub_0002.ts +0 -0
  255. package/test/test/assets/hls-ts-ec3/ChID_voices_6ch_256kbps_ddp_sub_0003.ts +0 -0
  256. package/test/test/assets/hls-ts-ec3/ChID_voices_6ch_256kbps_ddp_sub_0004.ts +0 -0
  257. package/test/test/assets/hls-ts-ec3/ChID_voices_6ch_256kbps_ddp_sub_0005.ts +0 -0
  258. package/test/test/assets/hls-ts-ec3/prog_index.m3u8 +19 -0
  259. package/test/test/assets/hls-ts-h264/fileSequence0.ts +0 -0
  260. package/test/test/assets/hls-ts-h264/fileSequence1.ts +0 -0
  261. package/test/test/assets/hls-ts-h264/fileSequence2.ts +0 -0
  262. package/test/test/assets/hls-ts-h264/fileSequence3.ts +0 -0
  263. package/test/test/assets/hls-ts-h264/fileSequence4.ts +0 -0
  264. package/test/test/assets/hls-ts-h264/fileSequence5.ts +0 -0
  265. package/test/test/assets/hls-ts-h264/prog_index.m3u8 +25 -0
  266. package/test/test/assets/hls-ts-mp3/manifest.m3u8 +13 -0
  267. package/test/test/assets/hls-ts-mp3/media_w239891843_0.ts +0 -0
  268. package/test/test/assets/hls-ts-mp3/media_w239891843_1.ts +0 -0
  269. package/test/test/assets/hls-ts-mp3/media_w239891843_2.ts +0 -0
  270. package/test/test/assets/hls-ts-mp3/media_w239891843_3.ts +0 -0
  271. package/test/test/assets/hls-ts-muxed-aac-h264/chunk.m3u8 +17 -0
  272. package/test/test/assets/hls-ts-muxed-aac-h264/n_0_0_0.ts +0 -0
  273. package/test/test/assets/hls-ts-muxed-aac-h264/n_1_0_0.ts +0 -0
  274. package/test/test/assets/hls-ts-muxed-aac-h264/n_2_0_0.ts +0 -0
  275. package/test/test/assets/hls-ts-muxed-aac-h264/n_3_0_0.ts +0 -0
  276. package/test/test/assets/hls-ts-muxed-aac-h264/n_4_0_0.ts +0 -0
  277. package/test/test/assets/hls-ts-muxed-aac-h264/n_5_0_0.ts +0 -0
  278. package/test/test/assets/hls-ts-muxed-aac-h264/playlist.m3u8 +4 -0
  279. package/test/test/boot.js +7 -2
  280. package/test/test/util/fake_ad.js +99 -0
  281. package/test/test/util/fake_ad_manager.js +7 -1
  282. package/test/test/util/fake_networking_engine.js +41 -14
  283. package/test/test/util/fake_segment_prefetch.js +81 -0
  284. package/test/test/util/manifest_generator.js +19 -5
  285. package/test/test/util/mss_parser_util.js +115 -0
  286. package/test/test/util/offline_utils.js +1 -0
  287. package/test/test/util/simple_fakes.js +12 -3
  288. package/test/test/util/streaming_engine_util.js +14 -4
  289. package/test/test/util/ttml_utils.js +2 -2
  290. package/test/text/cue_integration.js +1 -1
  291. package/test/text/srt_text_parser_unit.js +78 -0
  292. package/test/text/text_engine_unit.js +17 -8
  293. package/test/text/ui_text_displayer_unit.js +4 -4
  294. package/test/text/vtt_text_parser_unit.js +59 -40
  295. package/test/text/web_vtt_generator_unit.js +9 -3
  296. package/test/{media/transmuxer_integration.js → transmuxer/muxjs_transmuxer_integration.js} +30 -33
  297. package/test/transmuxer/transmuxer_engine_integration.js +60 -0
  298. package/test/transmuxer/transmuxer_integration.js +296 -0
  299. package/test/util/cmcd_manager_unit.js +162 -13
  300. package/test/util/language_utils_unit.js +9 -2
  301. package/test/util/mp4_box_parsers_unit.js +39 -4
  302. package/test/util/periods_unit.js +34 -26
  303. package/test/util/stream_utils_unit.js +60 -107
  304. package/ui/controls.js +19 -1
  305. package/ui/controls.less +1 -0
  306. package/ui/externs/ui.js +11 -1
  307. package/ui/language_utils.js +15 -0
  308. package/ui/less/thumbnails.less +26 -0
  309. package/ui/locales/oc.json +42 -0
  310. package/ui/pip_button.js +83 -2
  311. package/ui/range_element.js +15 -0
  312. package/ui/resolution_selection.js +9 -2
  313. package/ui/seek_bar.js +223 -0
  314. package/ui/ui.js +5 -0
  315. package/lib/cea/i_caption_decoder.js +0 -51
  316. 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
@@ -89,6 +89,41 @@ shaka.ui.SeekBar = class extends shaka.ui.RangeElement {
89
89
  */
90
90
  this.wasPlaying_ = false;
91
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
+
92
127
  /** @private {!Array.<!shaka.extern.AdCuePoint>} */
93
128
  this.adCuePoints_ = [];
94
129
 
@@ -126,6 +161,29 @@ shaka.ui.SeekBar = class extends shaka.ui.RangeElement {
126
161
  this.onAdCuePointsChanged_();
127
162
  });
128
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
+
129
187
  // Initialize seek state and label.
130
188
  this.setValue(this.video.currentTime);
131
189
  this.update();
@@ -159,6 +217,8 @@ shaka.ui.SeekBar = class extends shaka.ui.RangeElement {
159
217
  this.wasPlaying_ = !this.video.paused;
160
218
  this.controls.setSeeking(true);
161
219
  this.video.pause();
220
+ this.hideThumbnailTimer_.stop();
221
+ this.isMoving_ = true;
162
222
  }
163
223
 
164
224
  /**
@@ -186,6 +246,18 @@ shaka.ui.SeekBar = class extends shaka.ui.RangeElement {
186
246
  // Calling |start| on an already pending timer will cancel the old request
187
247
  // and start the new one.
188
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
+ }
189
261
  }
190
262
 
191
263
  /**
@@ -203,6 +275,12 @@ shaka.ui.SeekBar = class extends shaka.ui.RangeElement {
203
275
  if (this.wasPlaying_) {
204
276
  this.video.play();
205
277
  }
278
+
279
+ if (this.isMoving_) {
280
+ this.isMoving_ = false;
281
+ this.hideThumbnailTimer_.stop();
282
+ this.hideThumbnailTimer_.tickAfter(/* seconds= */ 0.25);
283
+ }
206
284
  }
207
285
 
208
286
  /**
@@ -382,6 +460,151 @@ shaka.ui.SeekBar = class extends shaka.ui.RangeElement {
382
460
  updateAriaLabel_() {
383
461
  this.bar.ariaLabel = this.localization.resolve(shaka.ui.Locales.Ids.SEEK);
384
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
+ }
385
608
  };
386
609
 
387
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
-