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/lib/player.js CHANGED
@@ -590,7 +590,8 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
590
590
  new shaka.media.PreferenceBasedCriteria(
591
591
  this.config_.preferredAudioLanguage,
592
592
  this.config_.preferredVariantRole,
593
- this.config_.preferredAudioChannelCount);
593
+ this.config_.preferredAudioChannelCount,
594
+ this.config_.preferredVideoHdrLevel);
594
595
 
595
596
  /** @private {string} */
596
597
  this.currentTextLanguage_ = this.config_.preferredTextLanguage;
@@ -617,6 +618,10 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
617
618
  dependencyInjector(this);
618
619
  }
619
620
 
621
+
622
+ // Create the CMCD manager so client data can be attached to all requests
623
+ this.cmcdManager_ = this.createCmcd_();
624
+
620
625
  this.networkingEngine_ = this.createNetworkingEngine();
621
626
  this.networkingEngine_.setForceHTTPS(this.config_.streaming.forceHTTPS);
622
627
 
@@ -630,6 +635,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
630
635
  // If the browser comes back online after being offline, then try to play
631
636
  // again.
632
637
  this.globalEventManager_.listen(window, 'online', () => {
638
+ this.restoreDisabledVariants_();
633
639
  this.retryStreaming();
634
640
  });
635
641
 
@@ -799,13 +805,13 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
799
805
  const edge = shaka.util.Platform.isEdge() ||
800
806
  shaka.util.Platform.isLegacyEdge();
801
807
  if (edge) {
802
- if (!config.streaming.forceTransmux) {
808
+ if (!config.mediaSource.forceTransmux) {
803
809
  // If forceTransmux is disabled for Microsoft Edge, LCEVC data
804
810
  // is stripped out in case of a MPEG-2 TS container.
805
811
  // Hence the warning for Microsoft Edge when playing content with
806
812
  // MPEG-2 TS container.
807
813
  shaka.log.alwaysWarn('LCEVC Warning: For MPEG-2 TS decoding '+
808
- 'the config.streaming.forceTransmux must be enabled.');
814
+ 'the config.mediaSource.forceTransmux must be enabled.');
809
815
  }
810
816
  }
811
817
  }
@@ -1310,9 +1316,6 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
1310
1316
  // before we allow calls to |updateStateHistory|.
1311
1317
  this.stats_ = new shaka.util.Stats();
1312
1318
 
1313
- // Create the CMCD manager so client data can be attached to all requests
1314
- this.cmcdManager_ = this.createCmcd_();
1315
-
1316
1319
  // Load's request is a little different, so we can't use our normal
1317
1320
  // listeners-to-promise method. It is the only request where we may skip the
1318
1321
  // request, so we need to set the on skip callback to reject with a specific
@@ -1870,12 +1873,6 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
1870
1873
 
1871
1874
  const playerInterface = {
1872
1875
  networkingEngine: networkingEngine,
1873
- modifyManifestRequest: (request, manifestInfo) => {
1874
- this.cmcdManager_.applyManifestData(request, manifestInfo);
1875
- },
1876
- modifySegmentRequest: (request, segmentInfo) => {
1877
- this.cmcdManager_.applySegmentData(request, segmentInfo);
1878
- },
1879
1876
  filter: (manifest) => this.filterManifest_(manifest),
1880
1877
  makeTextStreamsForClosedCaptions: (manifest) => {
1881
1878
  return this.makeTextStreamsForClosedCaptions_(manifest);
@@ -2113,7 +2110,9 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
2113
2110
  new shaka.media.PreferenceBasedCriteria(
2114
2111
  this.config_.preferredAudioLanguage,
2115
2112
  this.config_.preferredVariantRole,
2116
- this.config_.preferredAudioChannelCount);
2113
+ this.config_.preferredAudioChannelCount,
2114
+ this.config_.preferredVideoHdrLevel,
2115
+ this.config_.preferredAudioLabel);
2117
2116
 
2118
2117
  this.currentTextLanguage_ = this.config_.preferredTextLanguage;
2119
2118
  this.currentTextRole_ = this.config_.preferredTextRole;
@@ -2281,6 +2280,12 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
2281
2280
  'arbitrary language initially');
2282
2281
  }
2283
2282
 
2283
+ if (this.isLive() && (this.config_.streaming.liveSync ||
2284
+ this.manifest_.serviceDescription)) {
2285
+ const onTimeUpdate = () => this.onTimeUpdate_();
2286
+ this.loadEventManager_.listen(mediaElement, 'timeupdate', onTimeUpdate);
2287
+ }
2288
+
2284
2289
  this.fullyLoaded_ = true;
2285
2290
 
2286
2291
  // Wait for the 'loadedmetadata' event to measure load() latency.
@@ -2379,6 +2384,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
2379
2384
  drmInfos: [], // Filled in by DrmEngine config.
2380
2385
  keyIds: new Set(),
2381
2386
  language: 'und',
2387
+ originalLanguage: null,
2382
2388
  label: null,
2383
2389
  type: ContentType.VIDEO,
2384
2390
  primary: false,
@@ -2390,6 +2396,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
2390
2396
  audioSamplingRate: null,
2391
2397
  spatialAudio: false,
2392
2398
  closedCaptions: null,
2399
+ accessibilityPurpose: null,
2393
2400
  external: false,
2394
2401
  },
2395
2402
  bandwidth: 100,
@@ -2633,6 +2640,11 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
2633
2640
  fullyLoaded.reject(abortedError);
2634
2641
  return Promise.resolve(); // Abort complete.
2635
2642
  }).chain(() => {
2643
+ if (this.isLive() && this.config_.streaming.liveSync) {
2644
+ const onTimeUpdate = () => this.onTimeUpdate_();
2645
+ this.loadEventManager_.listen(mediaElement, 'timeupdate', onTimeUpdate);
2646
+ }
2647
+
2636
2648
  this.fullyLoaded_ = true;
2637
2649
  });
2638
2650
  }
@@ -2894,9 +2906,13 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
2894
2906
  .set('aborted', aborted);
2895
2907
  this.dispatchEvent(this.makeEvent_(name, data));
2896
2908
  };
2909
+ /** @type {shaka.net.NetworkingEngine.OnRequest} */
2910
+ const onRequest_ = (type, request, context) => {
2911
+ this.cmcdManager_.applyData(type, request, context);
2912
+ };
2897
2913
 
2898
2914
  return new shaka.net.NetworkingEngine(
2899
- onProgressUpdated_, onHeadersReceived_, onDownloadFailed_);
2915
+ onProgressUpdated_, onHeadersReceived_, onDownloadFailed_, onRequest_);
2900
2916
  }
2901
2917
 
2902
2918
  /**
@@ -3102,8 +3118,9 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
3102
3118
  this.abrManager_.getBandwidthEstimate() : NaN,
3103
3119
  getBufferedInfo: () => this.getBufferedInfo(),
3104
3120
  getCurrentTime: () => this.video_ ? this.video_.currentTime : 0,
3105
- getVariantTracks: () => this.getVariantTracks(),
3106
3121
  getPlaybackRate: () => this.getPlaybackRate(),
3122
+ getNetworkingEngine: () => this.getNetworkingEngine(),
3123
+ getVariantTracks: () => this.getVariantTracks(),
3107
3124
  isLive: () => this.isLive(),
3108
3125
  };
3109
3126
 
@@ -3118,17 +3135,13 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
3118
3135
  */
3119
3136
  createStreamingEngine() {
3120
3137
  goog.asserts.assert(
3121
- this.abrManager_ && this.mediaSourceEngine_ &&
3122
- this.cmcdManager_ && this.manifest_,
3138
+ this.abrManager_ && this.mediaSourceEngine_ && this.manifest_,
3123
3139
  'Must not be destroyed');
3124
3140
 
3125
3141
  /** @type {shaka.media.StreamingEngine.PlayerInterface} */
3126
3142
  const playerInterface = {
3127
3143
  getPresentationTime: () => this.playhead_ ? this.playhead_.getTime() : 0,
3128
3144
  getBandwidthEstimate: () => this.abrManager_.getBandwidthEstimate(),
3129
- modifySegmentRequest: (request, segmentInfo) => {
3130
- this.cmcdManager_.applySegmentData(request, segmentInfo);
3131
- },
3132
3145
  mediaSourceEngine: this.mediaSourceEngine_,
3133
3146
  netEngine: this.networkingEngine_,
3134
3147
  onError: (error) => this.onError_(error),
@@ -3149,6 +3162,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
3149
3162
  onMetadata: (metadata, offset, endTime) => {
3150
3163
  this.processTimedMetadataMediaSrc_(metadata, offset, endTime);
3151
3164
  },
3165
+ disableStream: (stream, time) => this.disableStream(stream, time),
3152
3166
  };
3153
3167
 
3154
3168
  return new shaka.media.StreamingEngine(this.manifest_, playerInterface);
@@ -3196,15 +3210,26 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
3196
3210
  if (config['streaming'] && 'forceTransmuxTS' in config['streaming']) {
3197
3211
  shaka.Deprecate.deprecateFeature(5,
3198
3212
  'streaming.forceTransmuxTS configuration',
3199
- 'Please Use streaming.forceTransmux instead.');
3200
- config['streaming']['forceTransmux'] =
3213
+ 'Please Use mediaSource.forceTransmux instead.');
3214
+ config['mediaSource']['mediaSource'] =
3201
3215
  config['streaming']['forceTransmuxTS'];
3202
3216
  delete config['streaming']['forceTransmuxTS'];
3203
3217
  }
3204
3218
 
3219
+ // Deprecate 'streaming.forceTransmux' configuration.
3220
+ if (config['streaming'] && 'forceTransmux' in config['streaming']) {
3221
+ shaka.Deprecate.deprecateFeature(5,
3222
+ 'streaming.forceTransmux configuration',
3223
+ 'Please Use mediaSource.forceTransmux instead.');
3224
+ config['mediaSource']['mediaSource'] =
3225
+ config['streaming']['forceTransmux'];
3226
+ delete config['streaming']['forceTransmux'];
3227
+ }
3228
+
3205
3229
  // If lowLatencyMode is enabled, and inaccurateManifestTolerance and
3206
- // rebufferingGoal are not specified, set inaccurateManifestTolerance to 0
3207
- // and rebufferingGoal to 0.01 by default for low latency streaming.
3230
+ // rebufferingGoal and segmentPrefetchLimit are not specified, set
3231
+ // inaccurateManifestTolerance to 0 and rebufferingGoal to 0.01 and
3232
+ // segmentPrefetchLimit to 2 by default for low latency streaming.
3208
3233
  if (config['streaming'] && config['streaming']['lowLatencyMode']) {
3209
3234
  if (config['streaming']['inaccurateManifestTolerance'] == undefined) {
3210
3235
  config['streaming']['inaccurateManifestTolerance'] = 0;
@@ -3212,6 +3237,9 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
3212
3237
  if (config['streaming']['rebufferingGoal'] == undefined) {
3213
3238
  config['streaming']['rebufferingGoal'] = 0.01;
3214
3239
  }
3240
+ if (config['streaming']['segmentPrefetchLimit'] == undefined) {
3241
+ config['streaming']['segmentPrefetchLimit'] = 2;
3242
+ }
3215
3243
  }
3216
3244
  const ret = shaka.util.PlayerConfiguration.mergeConfigObjects(
3217
3245
  this.config_, config, this.defaultConfig_());
@@ -3311,6 +3339,12 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
3311
3339
  this.config_.playRangeStart,
3312
3340
  this.config_.playRangeEnd);
3313
3341
  }
3342
+ if (this.adManager_) {
3343
+ this.adManager_.configure(this.config_.ads);
3344
+ }
3345
+ if (this.cmcdManager_) {
3346
+ this.cmcdManager_.configure(this.config_.cmcd);
3347
+ }
3314
3348
  }
3315
3349
 
3316
3350
  /**
@@ -3397,6 +3431,19 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
3397
3431
  return this.loadMode_;
3398
3432
  }
3399
3433
 
3434
+ /**
3435
+ * Get the current manifest type.
3436
+ *
3437
+ * @return {?string}
3438
+ * @export
3439
+ */
3440
+ getManifestType() {
3441
+ if (!this.manifest_) {
3442
+ return null;
3443
+ }
3444
+ return this.manifest_.type;
3445
+ }
3446
+
3400
3447
  /**
3401
3448
  * Get the media element that the player is currently using to play loaded
3402
3449
  * content. If the player has not loaded content, this will return
@@ -3647,6 +3694,16 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
3647
3694
  return this.drmEngine_ ? this.drmEngine_.getExpiration() : Infinity;
3648
3695
  }
3649
3696
 
3697
+ /**
3698
+ * Returns the active sessions metadata
3699
+ *
3700
+ * @return {!Array.<shaka.extern.DrmSessionMetadata>}
3701
+ * @export
3702
+ */
3703
+ getActiveSessionsMetadata() {
3704
+ return this.drmEngine_ ? this.drmEngine_.getActiveSessionsMetadata() : [];
3705
+ }
3706
+
3650
3707
  /**
3651
3708
  * Gets a map of EME key ID to the current key status.
3652
3709
  *
@@ -4138,15 +4195,16 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
4138
4195
  * @param {string} language
4139
4196
  * @param {string=} role
4140
4197
  * @param {number=} channelsCount
4198
+ * @param {number=} safeMargin
4141
4199
  * @export
4142
4200
  */
4143
- selectAudioLanguage(language, role, channelsCount = 0) {
4201
+ selectAudioLanguage(language, role, channelsCount = 0, safeMargin = 0) {
4144
4202
  const LanguageUtils = shaka.util.LanguageUtils;
4145
4203
 
4146
4204
  if (this.manifest_ && this.playhead_) {
4147
4205
  this.currentAdaptationSetCriteria_ =
4148
4206
  new shaka.media.PreferenceBasedCriteria(language, role || '',
4149
- channelsCount, /* label= */ '');
4207
+ channelsCount, /* hdrLevel= */ '', /* label= */ '');
4150
4208
 
4151
4209
  const diff = (a, b) => {
4152
4210
  if (!a.video && !b.video) {
@@ -4173,7 +4231,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
4173
4231
  }
4174
4232
  if (bestVariant) {
4175
4233
  const track = shaka.util.StreamUtils.variantToTrack(bestVariant);
4176
- this.selectVariantTrack(track, /* clearBuffer= */ true);
4234
+ this.selectVariantTrack(track, /* clearBuffer= */ true, safeMargin);
4177
4235
  return;
4178
4236
  }
4179
4237
 
@@ -4263,9 +4321,15 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
4263
4321
  * are expected to have the same variant.audio.
4264
4322
  *
4265
4323
  * @param {string} label
4324
+ * @param {boolean=} clearBuffer Optional clear buffer or not when
4325
+ * switch to new variant
4326
+ * Defaults to true if not provided
4327
+ * @param {number=} safeMargin Optional amount of buffer (in seconds) to
4328
+ * retain when clearing the buffer.
4329
+ * Defaults to 0 if not provided. Ignored if clearBuffer is false.
4266
4330
  * @export
4267
4331
  */
4268
- selectVariantsByLabel(label) {
4332
+ selectVariantsByLabel(label, clearBuffer = true, safeMargin = 0) {
4269
4333
  if (this.manifest_ && this.playhead_) {
4270
4334
  let firstVariantWithLabel = null;
4271
4335
  for (const variant of this.manifest_.variants) {
@@ -4287,9 +4351,9 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
4287
4351
  // label have the same language.
4288
4352
  this.currentAdaptationSetCriteria_ =
4289
4353
  new shaka.media.PreferenceBasedCriteria(
4290
- firstVariantWithLabel.language, '', 0, label);
4354
+ firstVariantWithLabel.language, '', 0, '', label);
4291
4355
 
4292
- this.chooseVariantAndSwitch_();
4356
+ this.chooseVariantAndSwitch_(clearBuffer, safeMargin);
4293
4357
  } else if (this.video_ && this.video_.audioTracks) {
4294
4358
  const audioTracks = Array.from(this.video_.audioTracks);
4295
4359
 
@@ -4813,6 +4877,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
4813
4877
  drmInfos: [],
4814
4878
  keyIds: new Set(),
4815
4879
  language: language,
4880
+ originalLanguage: language,
4816
4881
  label: label || null,
4817
4882
  type: ContentType.TEXT,
4818
4883
  primary: false,
@@ -4824,6 +4889,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
4824
4889
  audioSamplingRate: null,
4825
4890
  spatialAudio: false,
4826
4891
  closedCaptions: null,
4892
+ accessibilityPurpose: null,
4827
4893
  external: true,
4828
4894
  };
4829
4895
 
@@ -4964,6 +5030,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
4964
5030
  drmInfos: [],
4965
5031
  keyIds: new Set(),
4966
5032
  language: 'und',
5033
+ originalLanguage: null,
4967
5034
  label: null,
4968
5035
  type: ContentType.IMAGE,
4969
5036
  primary: false,
@@ -4976,6 +5043,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
4976
5043
  spatialAudio: false,
4977
5044
  closedCaptions: null,
4978
5045
  tilesLayout: '1x1',
5046
+ accessibilityPurpose: null,
4979
5047
  external: true,
4980
5048
  };
4981
5049
 
@@ -5373,6 +5441,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
5373
5441
  // Add an empty segmentIndex, for the benefit of the period combiner
5374
5442
  // in our builtin DASH parser.
5375
5443
  const segmentIndex = new shaka.media.MetaSegmentIndex();
5444
+ const language = video.closedCaptions.get(id);
5376
5445
  const textStream = {
5377
5446
  id: this.nextExternalStreamId_++, // A globally unique ID.
5378
5447
  originalId: id, // The CC ID string, like 'CC1', 'CC3', etc.
@@ -5384,7 +5453,8 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
5384
5453
  encrypted: false,
5385
5454
  drmInfos: [],
5386
5455
  keyIds: new Set(),
5387
- language: video.closedCaptions.get(id),
5456
+ language,
5457
+ originalLanguage: language,
5388
5458
  label: null,
5389
5459
  type: ContentType.TEXT,
5390
5460
  primary: false,
@@ -5396,6 +5466,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
5396
5466
  audioSamplingRate: null,
5397
5467
  spatialAudio: false,
5398
5468
  closedCaptions: null,
5469
+ accessibilityPurpose: null,
5399
5470
  external: false,
5400
5471
  };
5401
5472
  manifest.textStreams.push(textStream);
@@ -5650,6 +5721,71 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
5650
5721
  }
5651
5722
  }
5652
5723
 
5724
+ /**
5725
+ * Callback for liveSync
5726
+ *
5727
+ * @private
5728
+ */
5729
+ onTimeUpdate_() {
5730
+ // If the live stream has reached its end, do not sync.
5731
+ if (!this.isLive()) {
5732
+ return;
5733
+ }
5734
+ const seekRange = this.seekRange();
5735
+ if (!Number.isFinite(seekRange.end)) {
5736
+ return;
5737
+ }
5738
+ const currentTime = this.video_.currentTime;
5739
+ if (currentTime < seekRange.start) {
5740
+ // Bad stream?
5741
+ return;
5742
+ }
5743
+
5744
+ let liveSyncMaxLatency;
5745
+ let liveSyncPlaybackRate;
5746
+ if (this.config_.streaming.liveSync) {
5747
+ liveSyncMaxLatency = this.config_.streaming.liveSyncMaxLatency;
5748
+ liveSyncPlaybackRate = this.config_.streaming.liveSyncPlaybackRate;
5749
+ } else {
5750
+ // serviceDescription must override if it is defined in the MPD and
5751
+ // liveSync configuration is not set.
5752
+ if (this.manifest_ && this.manifest_.serviceDescription) {
5753
+ liveSyncMaxLatency = this.manifest_.serviceDescription.maxLatency ||
5754
+ this.config_.streaming.liveSyncMaxLatency;
5755
+ liveSyncPlaybackRate =
5756
+ this.manifest_.serviceDescription.maxPlaybackRate ||
5757
+ this.config_.streaming.liveSyncPlaybackRate;
5758
+ }
5759
+ }
5760
+
5761
+ const playbackRate = this.video_.playbackRate;
5762
+ const latency = seekRange.end - this.video_.currentTime;
5763
+ let offset = 0;
5764
+ // In src= mode, the seek range isn't updated frequently enough, so we need
5765
+ // to fudge the latency number with an offset. The playback rate is used
5766
+ // as an offset, since that is the amount we catch up 1 second of
5767
+ // accelerated playback.
5768
+ if (this.loadMode_ == shaka.Player.LoadMode.SRC_EQUALS) {
5769
+ const buffered = this.video_.buffered;
5770
+ if (buffered.length > 0) {
5771
+ const bufferedEnd = buffered.end(buffered.length - 1);
5772
+ offset = Math.max(liveSyncPlaybackRate, bufferedEnd - seekRange.end);
5773
+ }
5774
+ }
5775
+
5776
+ if (liveSyncMaxLatency && liveSyncPlaybackRate &&
5777
+ (latency - offset) > liveSyncMaxLatency) {
5778
+ if (playbackRate != liveSyncPlaybackRate) {
5779
+ shaka.log.debug('Latency (' + latency + 's) ' +
5780
+ 'is greater than liveSyncMaxLatency (' + liveSyncMaxLatency + 's). ' +
5781
+ 'Updating playbackRate to ' + liveSyncPlaybackRate);
5782
+ this.trickPlay(liveSyncPlaybackRate);
5783
+ }
5784
+ } else if (playbackRate !== 1 && playbackRate !== 0) {
5785
+ this.cancelTrickPlay();
5786
+ }
5787
+ }
5788
+
5653
5789
  /**
5654
5790
  * Callback from Playhead.
5655
5791
  *
@@ -5688,9 +5824,8 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
5688
5824
  return false;
5689
5825
  }
5690
5826
 
5691
- const playableVariants = this.manifest_.variants.filter((variant) => {
5692
- return shaka.util.StreamUtils.isPlayable(variant);
5693
- });
5827
+ const playableVariants = shaka.util.StreamUtils.getPlayableVariants(
5828
+ this.manifest_.variants);
5694
5829
 
5695
5830
  // Update the abr manager with newly filtered variants.
5696
5831
  const adaptationSet = this.currentAdaptationSetCriteria_.create(
@@ -5717,17 +5852,55 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
5717
5852
  }
5718
5853
 
5719
5854
  /**
5720
- * Re-apply restrictions to the variants, to re-enable variants that were
5721
- * temporarily disabled due to network errors.
5722
- * If any variants are enabled this way, a new variant might be chosen for
5723
- * playback.
5855
+ * Checks to re-enable variants that were temporarily disabled due to network
5856
+ * errors. If any variants are enabled this way, a new variant may be chosen
5857
+ * for playback.
5724
5858
  * @private
5725
5859
  */
5726
5860
  checkVariants_() {
5727
- const tracksChanged = shaka.util.StreamUtils.applyRestrictions(
5728
- this.manifest_.variants, this.config_.restrictions, this.maxHwRes_);
5729
- if (tracksChanged) {
5730
- this.chooseVariant_();
5861
+ goog.asserts.assert(this.manifest_, 'Should have manifest!');
5862
+
5863
+ const now = Date.now() / 1000;
5864
+ let hasVariantUpdate = false;
5865
+
5866
+ /** @type {function(shaka.extern.Variant):string} */
5867
+ const streamsAsString = (variant) => {
5868
+ let str = '';
5869
+ if (variant.video) {
5870
+ str += 'video:' + variant.video.id;
5871
+ }
5872
+ if (variant.audio) {
5873
+ str += str ? '&' : '';
5874
+ str += 'audio:' + variant.audio.id;
5875
+ }
5876
+ return str;
5877
+ };
5878
+
5879
+ for (const variant of this.manifest_.variants) {
5880
+ if (variant.disabledUntilTime > 0 && variant.disabledUntilTime <= now) {
5881
+ variant.disabledUntilTime = 0;
5882
+ hasVariantUpdate = true;
5883
+
5884
+ shaka.log.v2('Re-enabled variant with ' + streamsAsString(variant));
5885
+ }
5886
+ }
5887
+
5888
+ const shouldStopTimer = this.manifest_.variants.every((variant) => {
5889
+ goog.asserts.assert(
5890
+ variant.disabledUntilTime >= 0,
5891
+ '|variant.disableTimeUntilTime| must always be >= 0');
5892
+ return variant.disabledUntilTime === 0;
5893
+ });
5894
+
5895
+ if (shouldStopTimer) {
5896
+ this.checkVariantsTimer_.stop();
5897
+ }
5898
+
5899
+ if (hasVariantUpdate) {
5900
+ // Reconsider re-enabled variant for ABR switching.
5901
+ this.chooseVariantAndSwitch_(
5902
+ /* clearBuffer= */ true, /* safeMargin= */ undefined,
5903
+ /* force= */ false, /* fromAdaptation= */ false);
5731
5904
  }
5732
5905
  }
5733
5906
 
@@ -5754,9 +5927,16 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
5754
5927
  * Called after a config change, a key status event, or an explicit language
5755
5928
  * change.
5756
5929
  *
5930
+ * @param {boolean=} clearBuffer Optional clear buffer or not when
5931
+ * switch to new variant
5932
+ * Defaults to true if not provided
5933
+ * @param {number=} safeMargin Optional amount of buffer (in seconds) to
5934
+ * retain when clearing the buffer.
5935
+ * Defaults to 0 if not provided. Ignored if clearBuffer is false.
5757
5936
  * @private
5758
5937
  */
5759
- chooseVariantAndSwitch_() {
5938
+ chooseVariantAndSwitch_(clearBuffer = true, safeMargin = 0, force = false,
5939
+ fromAdaptation = true) {
5760
5940
  goog.asserts.assert(this.config_, 'Must not be destroyed');
5761
5941
 
5762
5942
  // Because we're running this after a config change (manual language
@@ -5764,8 +5944,8 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
5764
5944
  // here.
5765
5945
  const chosenVariant = this.chooseVariant_();
5766
5946
  if (chosenVariant) {
5767
- this.switchVariant_(chosenVariant, /* fromAdaptation= */ true,
5768
- /* clearBuffers= */ true, /* safeMargin= */ 0);
5947
+ this.switchVariant_(chosenVariant, fromAdaptation,
5948
+ clearBuffer, safeMargin, force);
5769
5949
  }
5770
5950
  }
5771
5951
 
@@ -5774,9 +5954,11 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
5774
5954
  * @param {boolean} fromAdaptation
5775
5955
  * @param {boolean} clearBuffer
5776
5956
  * @param {number} safeMargin
5957
+ * @param {boolean=} force
5777
5958
  * @private
5778
5959
  */
5779
- switchVariant_(variant, fromAdaptation, clearBuffer, safeMargin) {
5960
+ switchVariant_(variant, fromAdaptation, clearBuffer, safeMargin,
5961
+ force = false) {
5780
5962
  const currentVariant = this.streamingEngine_.getCurrentVariant();
5781
5963
  if (variant == currentVariant) {
5782
5964
  shaka.log.debug('Variant already selected.');
@@ -5793,7 +5975,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
5793
5975
  // Add entries to the history.
5794
5976
  this.addVariantToSwitchHistory_(variant, fromAdaptation);
5795
5977
  this.streamingEngine_.switchVariant(
5796
- variant, clearBuffer, safeMargin, /* force= */ undefined,
5978
+ variant, clearBuffer, safeMargin, force,
5797
5979
  /* adaptation= */ fromAdaptation);
5798
5980
  let oldTrack = null;
5799
5981
  if (currentVariant) {
@@ -6059,85 +6241,108 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
6059
6241
 
6060
6242
  /** @private */
6061
6243
  onAbrStatusChanged_() {
6244
+ // Restore disabled variants if abr get disabled
6245
+ if (!this.config_.abr.enabled) {
6246
+ this.restoreDisabledVariants_();
6247
+ }
6248
+
6062
6249
  const data = (new Map()).set('newStatus', this.config_.abr.enabled);
6063
6250
  this.delayDispatchEvent_(this.makeEvent_(
6064
6251
  shaka.util.FakeEvent.EventName.AbrStatusChanged, data));
6065
6252
  }
6066
6253
 
6067
6254
  /**
6068
- * Tries to recover from NETWORK HTTP_ERROR, temporary disabling the current
6069
- * problematic variant.
6070
- * @param {!shaka.util.Error} error
6071
- * @return {boolean}
6255
+ * @param {boolean} updateAbrManager
6072
6256
  * @private
6073
6257
  */
6074
- tryToRecoverFromError_(error) {
6075
- if ((error.code != shaka.util.Error.Code.HTTP_ERROR &&
6076
- error.code != shaka.util.Error.Code.SEGMENT_MISSING) ||
6077
- error.category != shaka.util.Error.Category.NETWORK) {
6078
- return false;
6079
- }
6258
+ restoreDisabledVariants_(updateAbrManager=true) {
6080
6259
  if (this.loadMode_ != shaka.Player.LoadMode.MEDIA_SOURCE) {
6081
- return false;
6260
+ return;
6082
6261
  }
6083
6262
  goog.asserts.assert(this.manifest_, 'Should have manifest!');
6084
6263
 
6264
+ shaka.log.v2('Restoring all disabled streams...');
6265
+
6266
+ this.checkVariantsTimer_.stop();
6267
+
6268
+ for (const variant of this.manifest_.variants) {
6269
+ variant.disabledUntilTime = 0;
6270
+ }
6271
+
6272
+ if (updateAbrManager) {
6273
+ this.updateAbrManagerVariants_();
6274
+ }
6275
+ }
6276
+
6277
+ /**
6278
+ * Temporarily disable all variants containing |stream|
6279
+ * @param {shaka.extern.Stream} stream
6280
+ * @param {number} disableTime
6281
+ * @return {boolean}
6282
+ */
6283
+ disableStream(stream, disableTime) {
6284
+ if (!this.config_.abr.enabled ||
6285
+ this.loadMode_ === shaka.Player.LoadMode.DESTROYED) {
6286
+ return false;
6287
+ }
6288
+
6085
6289
  if (!navigator.onLine) {
6086
- // Don't restrict variants if we're completely offline, or else we end up
6290
+ // Don't disable variants if we're completely offline, or else we end up
6087
6291
  // rapidly restricting all of them.
6088
6292
  return false;
6089
6293
  }
6090
6294
 
6091
- let maxDisabledTime = this.config_.streaming.maxDisabledTime;
6092
- if (maxDisabledTime == 0) {
6093
- if (error.code == shaka.util.Error.Code.SEGMENT_MISSING) {
6094
- // Spec: https://datatracker.ietf.org/doc/html/draft-pantos-hls-rfc8216bis#section-6.3.3
6095
- // The client SHOULD NOT attempt to load Media Segments that have been
6096
- // marked with an EXT-X-GAP tag, or to load Partial Segments with a
6097
- // GAP=YES attribute. Instead, clients are encouraged to look for
6098
- // another Variant Stream of the same Rendition which does not have the
6099
- // same gap, and play that instead.
6100
- maxDisabledTime = 1;
6101
- } else {
6102
- return false;
6295
+ // It only makes sense to disable a stream if we have an alternative else we
6296
+ // end up disabling all variants.
6297
+ const hasAltStream = this.manifest_.variants.some((variant) => {
6298
+ const altStream = variant[stream.type];
6299
+
6300
+ if (altStream && altStream.id !== stream.id) {
6301
+ if (shaka.util.StreamUtils.isAudio(stream)) {
6302
+ return stream.language === altStream.language;
6303
+ }
6304
+ return true;
6103
6305
  }
6104
- }
6306
+ return false;
6307
+ });
6105
6308
 
6106
- if (error.code == shaka.util.Error.Code.HTTP_ERROR) {
6107
- shaka.log.debug('Recoverable NETWORK HTTP_ERROR, trying to recover...');
6108
- }
6309
+ if (hasAltStream) {
6310
+ let didDisableStream = false;
6109
6311
 
6110
- // Obtain the active variant and disable it from manifest variants
6111
- const activeVariantTrack = this.getVariantTracks().find((t) => t.active);
6112
- goog.asserts.assert(activeVariantTrack, 'Active variant should be found');
6113
- const manifest = this.manifest_;
6114
- for (const variant of manifest.variants) {
6115
- if (variant.id === activeVariantTrack.id) {
6116
- variant.disabledUntilTime = (Date.now() / 1000) + maxDisabledTime;
6312
+ for (const variant of this.manifest_.variants) {
6313
+ const candidate = variant[stream.type];
6314
+
6315
+ if (candidate && candidate.id === stream.id) {
6316
+ variant.disabledUntilTime = (Date.now() / 1000) + disableTime;
6317
+ didDisableStream = true;
6318
+
6319
+ shaka.log.v2(
6320
+ 'Disabled stream ' + stream.type + ':' + stream.id +
6321
+ ' for ' + disableTime + ' seconds...');
6322
+ }
6117
6323
  }
6118
- }
6119
6324
 
6120
- // Apply restrictions in order to disable variants
6121
- shaka.util.StreamUtils.applyRestrictions(
6122
- manifest.variants, this.config_.restrictions, this.maxHwRes_);
6325
+ goog.asserts.assert(didDisableStream, 'Must have disabled stream');
6123
6326
 
6124
- // Select for a new variant
6125
- const chosenVariant = this.chooseVariant_();
6126
- if (!chosenVariant) {
6127
- shaka.log.warning('Not enough variants to recover from error');
6128
- return false;
6129
- }
6327
+ this.checkVariantsTimer_.tickEvery(1);
6130
6328
 
6131
- // Get the safeMargin to ensure a seamless playback
6132
- const {video} = this.getBufferedInfo();
6133
- const safeMargin =
6134
- video.reduce((size, {start, end}) => size + end - start, 0);
6329
+ // Get the safeMargin to ensure a seamless playback
6330
+ const {video} = this.getBufferedInfo();
6331
+ const safeMargin =
6332
+ video.reduce((size, {start, end}) => size + end - start, 0);
6135
6333
 
6136
- this.switchVariant_(chosenVariant, /* fromAdaptation= */ false,
6137
- /* clearBuffers= */ true, /* safeMargin= */ safeMargin);
6334
+ // Update abr manager variants and switch to recover playback
6335
+ this.chooseVariantAndSwitch_(
6336
+ /* clearBuffer= */ true, /* safeMargin= */ safeMargin,
6337
+ /* force= */ true, /* fromAdaptation= */ false);
6338
+ return true;
6339
+ }
6138
6340
 
6139
- this.checkVariantsTimer_.tickAfter(maxDisabledTime);
6140
- return true;
6341
+ shaka.log.warning(
6342
+ 'No alternate stream found for active ' + stream.type + ' stream. ' +
6343
+ 'Will ignore request to disable stream...');
6344
+
6345
+ return false;
6141
6346
  }
6142
6347
 
6143
6348
  /**
@@ -6153,10 +6358,9 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
6153
6358
  return;
6154
6359
  }
6155
6360
 
6156
-
6157
- if (this.tryToRecoverFromError_(error)) {
6158
- error.handled = true;
6159
- return;
6361
+ // Restore disabled variant if the player experienced a critical error.
6362
+ if (error.severity === shaka.util.Error.Severity.CRITICAL) {
6363
+ this.restoreDisabledVariants_(/* updateAbrManager= */ false);
6160
6364
  }
6161
6365
 
6162
6366
  const eventName = shaka.util.FakeEvent.EventName.Error;
@@ -7020,7 +7224,7 @@ shaka.Player.TYPICAL_BUFFERING_THRESHOLD_ = 0.5;
7020
7224
  * @define {string} A version number taken from git at compile time.
7021
7225
  * @export
7022
7226
  */
7023
- shaka.Player.version = 'v4.3.9-uncompiled';
7227
+ shaka.Player.version = 'v4.4.0-uncompiled';
7024
7228
 
7025
7229
  // Initialize the deprecation system using the version string we just set
7026
7230
  // on the player.