shaka-player 4.3.11 → 4.4.1

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 (315) hide show
  1. package/AUTHORS +9 -0
  2. package/CHANGELOG.md +303 -202
  3. package/CONTRIBUTORS +8 -0
  4. package/LICENSE +32 -0
  5. package/README.md +63 -10
  6. package/demo/autoTemplate.txt +40 -5
  7. package/demo/close_button.js +11 -0
  8. package/demo/common/asset.js +13 -1
  9. package/demo/common/assets.js +53 -3
  10. package/demo/common/message_ids.js +25 -1
  11. package/demo/config.js +69 -10
  12. package/demo/customWarning.txt +2 -2
  13. package/demo/index.html +3 -1
  14. package/demo/locales/en.json +26 -2
  15. package/demo/locales/source.json +105 -9
  16. package/demo/main.js +52 -9
  17. package/demo/search.js +3 -1
  18. package/demo/service_worker.js +3 -0
  19. package/dist/controls.css +1 -1
  20. package/dist/controls.css.map +1 -1
  21. package/dist/demo.compiled.debug.js +124 -112
  22. package/dist/demo.compiled.debug.map +3 -3
  23. package/dist/demo.compiled.js +124 -112
  24. package/dist/demo.compiled.map +3 -3
  25. package/dist/deps.js +46 -28
  26. package/dist/locales.js +33 -33
  27. package/dist/receiver.compiled.debug.js +52 -46
  28. package/dist/receiver.compiled.debug.map +3 -3
  29. package/dist/receiver.compiled.js +52 -46
  30. package/dist/receiver.compiled.map +3 -3
  31. package/dist/shaka-player.compiled.d.ts +676 -391
  32. package/dist/shaka-player.compiled.debug.d.ts +676 -391
  33. package/dist/shaka-player.compiled.debug.externs.js +1188 -616
  34. package/dist/shaka-player.compiled.debug.js +1043 -699
  35. package/dist/shaka-player.compiled.debug.map +4 -4
  36. package/dist/shaka-player.compiled.externs.js +1188 -616
  37. package/dist/shaka-player.compiled.js +1091 -929
  38. package/dist/shaka-player.compiled.map +4 -4
  39. package/dist/shaka-player.ui.d.ts +677 -392
  40. package/dist/shaka-player.ui.debug.d.ts +677 -392
  41. package/dist/shaka-player.ui.debug.externs.js +1023 -451
  42. package/dist/shaka-player.ui.debug.js +1098 -737
  43. package/dist/shaka-player.ui.debug.map +4 -4
  44. package/dist/shaka-player.ui.externs.js +1023 -451
  45. package/dist/shaka-player.ui.js +1246 -1071
  46. package/dist/shaka-player.ui.map +4 -4
  47. package/docs/tutorials/ad_monetization.md +15 -1
  48. package/docs/tutorials/application-level-redirects.md +1 -1
  49. package/docs/tutorials/config.md +9 -5
  50. package/docs/tutorials/drm-config.md +61 -0
  51. package/docs/tutorials/fairplay.md +19 -4
  52. package/docs/tutorials/license-server-auth.md +4 -4
  53. package/docs/tutorials/license-wrapping.md +2 -2
  54. package/docs/tutorials/ui-customization.md +7 -3
  55. package/docs/tutorials/ui.md +7 -7
  56. package/docs/tutorials/upgrade.md +3 -0
  57. package/externs/ima.js +123 -2
  58. package/externs/isoboxer.js +217 -0
  59. package/externs/mediakeys.js +18 -0
  60. package/externs/mux.js +1 -105
  61. package/externs/pictureinpicture.js +48 -0
  62. package/externs/shaka/ads.js +61 -1
  63. package/externs/shaka/cea.js +110 -0
  64. package/externs/shaka/codecs.js +32 -0
  65. package/externs/shaka/manifest.js +101 -1
  66. package/externs/shaka/manifest_parser.js +0 -10
  67. package/externs/shaka/net.js +36 -7
  68. package/externs/shaka/offline.js +8 -1
  69. package/externs/shaka/player.js +220 -21
  70. package/externs/shaka/text.js +1 -382
  71. package/externs/shaka/transmuxer.js +60 -0
  72. package/karma.conf.js +13 -0
  73. package/lib/abr/simple_abr_manager.js +6 -3
  74. package/lib/ads/ad_manager.js +60 -2
  75. package/lib/ads/client_side_ad.js +86 -0
  76. package/lib/ads/client_side_ad_manager.js +79 -3
  77. package/lib/ads/server_side_ad.js +86 -0
  78. package/lib/ads/server_side_ad_manager.js +13 -0
  79. package/lib/cast/cast_utils.js +1 -0
  80. package/lib/cea/cea608_data_channel.js +24 -14
  81. package/lib/cea/cea608_memory.js +5 -2
  82. package/lib/cea/cea708_service.js +8 -9
  83. package/lib/cea/cea708_window.js +1 -2
  84. package/lib/cea/cea_decoder.js +23 -6
  85. package/lib/cea/cea_utils.js +25 -2
  86. package/lib/cea/dummy_caption_decoder.js +21 -0
  87. package/lib/cea/dummy_cea_parser.js +1 -3
  88. package/lib/cea/mp4_cea_parser.js +15 -10
  89. package/lib/cea/ts_cea_parser.js +69 -0
  90. package/lib/dash/content_protection.js +32 -2
  91. package/lib/dash/dash_parser.js +93 -31
  92. package/lib/dash/mpd_utils.js +6 -25
  93. package/lib/dash/segment_base.js +12 -14
  94. package/lib/dash/segment_list.js +10 -6
  95. package/lib/dash/segment_template.js +349 -32
  96. package/lib/dependencies/all.js +8 -0
  97. package/lib/hls/hls_classes.js +33 -8
  98. package/lib/hls/hls_parser.js +805 -191
  99. package/lib/hls/manifest_text_parser.js +4 -14
  100. package/lib/media/adaptation_set_criteria.js +43 -3
  101. package/lib/media/closed_caption_parser.js +74 -12
  102. package/lib/media/drm_engine.js +189 -49
  103. package/lib/media/gap_jumping_controller.js +1 -1
  104. package/lib/media/manifest_parser.js +34 -0
  105. package/lib/media/media_source_capabilities.js +10 -0
  106. package/lib/media/media_source_engine.js +495 -231
  107. package/lib/media/playhead.js +1 -1
  108. package/lib/media/presentation_timeline.js +62 -0
  109. package/lib/media/segment_index.js +50 -14
  110. package/lib/media/segment_prefetch.js +277 -0
  111. package/lib/media/segment_reference.js +70 -4
  112. package/lib/media/streaming_engine.js +258 -81
  113. package/lib/media/video_wrapper.js +15 -7
  114. package/lib/mss/content_protection.js +353 -0
  115. package/lib/mss/mss_parser.js +1082 -0
  116. package/lib/net/networking_engine.js +60 -10
  117. package/lib/offline/indexeddb/v1_storage_cell.js +1 -0
  118. package/lib/offline/indexeddb/v2_storage_cell.js +1 -0
  119. package/lib/offline/manifest_converter.js +6 -0
  120. package/lib/offline/storage.js +2 -4
  121. package/lib/player.js +309 -105
  122. package/lib/polyfill/media_capabilities.js +43 -3
  123. package/lib/polyfill/patchedmediakeys_apple.js +5 -0
  124. package/lib/polyfill/patchedmediakeys_nop.js +5 -0
  125. package/lib/polyfill/patchedmediakeys_webkit.js +5 -0
  126. package/lib/text/cue.js +208 -130
  127. package/lib/text/lrc_text_parser.js +2 -2
  128. package/lib/text/mp4_vtt_parser.js +2 -2
  129. package/lib/text/sbv_text_parser.js +1 -1
  130. package/lib/text/simple_text_displayer.js +1 -1
  131. package/lib/text/srt_text_parser.js +9 -1
  132. package/lib/text/ssa_text_parser.js +2 -2
  133. package/lib/text/text_engine.js +3 -21
  134. package/lib/text/text_utils.js +71 -20
  135. package/lib/text/ui_text_displayer.js +14 -14
  136. package/lib/text/vtt_text_parser.js +22 -14
  137. package/lib/transmuxer/aac_transmuxer.js +224 -0
  138. package/lib/transmuxer/ac3.js +142 -0
  139. package/lib/transmuxer/ac3_transmuxer.js +213 -0
  140. package/lib/transmuxer/adts.js +232 -0
  141. package/lib/transmuxer/ec3.js +103 -0
  142. package/lib/transmuxer/ec3_transmuxer.js +213 -0
  143. package/lib/transmuxer/h264.js +324 -0
  144. package/lib/transmuxer/mp3_transmuxer.js +205 -0
  145. package/lib/transmuxer/mpeg_audio.js +196 -0
  146. package/lib/transmuxer/mpeg_ts_transmuxer.js +182 -0
  147. package/lib/transmuxer/mss_transmuxer.js +389 -0
  148. package/lib/{media/transmuxer.js → transmuxer/muxjs_transmuxer.js} +34 -80
  149. package/lib/transmuxer/transmuxer_engine.js +161 -0
  150. package/lib/transmuxer/ts_transmuxer.js +721 -0
  151. package/lib/util/cmcd_manager.js +131 -71
  152. package/lib/util/error.js +62 -26
  153. package/lib/util/exp_golomb.js +220 -0
  154. package/lib/util/fairplay_utils.js +64 -13
  155. package/lib/util/id3_utils.js +56 -3
  156. package/lib/util/language_utils.js +11 -8
  157. package/lib/util/manifest_parser_utils.js +3 -0
  158. package/lib/util/mime_utils.js +44 -7
  159. package/lib/util/mp4_box_parsers.js +233 -5
  160. package/lib/util/mp4_generator.js +1388 -0
  161. package/lib/util/periods.js +40 -34
  162. package/lib/util/platform.js +17 -3
  163. package/lib/util/player_configuration.js +38 -1
  164. package/lib/util/stream_utils.js +138 -27
  165. package/lib/util/text_parser.js +1 -1
  166. package/lib/util/ts_parser.js +460 -74
  167. package/lib/util/xml_utils.js +16 -3
  168. package/maintained-branches.md +2 -2
  169. package/package.json +3 -1
  170. package/roadmap.md +21 -6
  171. package/shaka-player.uncompiled.js +18 -0
  172. package/test/abr/simple_abr_manager_unit.js +32 -4
  173. package/test/ads/ad_manager_unit.js +18 -2
  174. package/test/cast/cast_utils_unit.js +7 -2
  175. package/test/cea/cea608_memory_unit.js +24 -0
  176. package/test/cea/cea_decoder_unit.js +33 -0
  177. package/test/dash/dash_parser_content_protection_unit.js +93 -3
  178. package/test/dash/dash_parser_live_unit.js +56 -13
  179. package/test/dash/dash_parser_manifest_unit.js +70 -2
  180. package/test/dash/dash_parser_segment_base_unit.js +14 -9
  181. package/test/dash/dash_parser_segment_template_unit.js +286 -6
  182. package/test/demo/demo_unit.js +5 -1
  183. package/test/hls/hls_live_unit.js +188 -28
  184. package/test/hls/hls_parser_unit.js +250 -109
  185. package/test/hls/manifest_text_parser_unit.js +18 -13
  186. package/test/media/adaptation_set_criteria_unit.js +46 -14
  187. package/test/media/adaptation_set_unit.js +2 -0
  188. package/test/media/drm_engine_integration.js +11 -8
  189. package/test/media/drm_engine_unit.js +220 -7
  190. package/test/media/media_source_engine_integration.js +124 -40
  191. package/test/media/media_source_engine_unit.js +133 -153
  192. package/test/media/playhead_unit.js +3 -0
  193. package/test/media/presentation_timeline_unit.js +51 -0
  194. package/test/media/segment_index_unit.js +52 -0
  195. package/test/media/segment_prefetch_unit.js +243 -0
  196. package/test/media/streaming_engine_integration.js +9 -3
  197. package/test/media/streaming_engine_unit.js +440 -59
  198. package/test/mss/mss_parser_content_protection_unit.js +49 -0
  199. package/test/mss/mss_parser_unit.js +472 -0
  200. package/test/mss/mss_player_integration.js +123 -0
  201. package/test/offline/manifest_convert_unit.js +9 -0
  202. package/test/offline/storage_integration.js +6 -1
  203. package/test/player_unit.js +386 -133
  204. package/test/polyfill/media_capabilities_unit.js +18 -0
  205. package/test/test/assets/hls-raw-aac/fileSequence0.aac +0 -0
  206. package/test/test/assets/hls-raw-aac/fileSequence1.aac +0 -0
  207. package/test/test/assets/hls-raw-aac/fileSequence2.aac +0 -0
  208. package/test/test/assets/hls-raw-aac/fileSequence3.aac +0 -0
  209. package/test/test/assets/hls-raw-aac/manifest.m3u8 +4 -0
  210. package/test/test/assets/hls-raw-aac/stream.m4a.m3u8 +14 -0
  211. package/test/test/assets/hls-raw-ac3/fileSequence0.ac3 +0 -0
  212. package/test/test/assets/hls-raw-ac3/fileSequence1.ac3 +0 -0
  213. package/test/test/assets/hls-raw-ac3/fileSequence2.ac3 +0 -0
  214. package/test/test/assets/hls-raw-ac3/fileSequence3.ac3 +0 -0
  215. package/test/test/assets/hls-raw-ac3/fileSequence4.ac3 +0 -0
  216. package/test/test/assets/hls-raw-ac3/fileSequence5.ac3 +0 -0
  217. package/test/test/assets/hls-raw-ac3/fileSequence6.ac3 +0 -0
  218. package/test/test/assets/hls-raw-ac3/fileSequence7.ac3 +0 -0
  219. package/test/test/assets/hls-raw-ac3/prog_index.m3u8 +31 -0
  220. package/test/test/assets/hls-raw-ec3/fileSequence0.ec3 +0 -0
  221. package/test/test/assets/hls-raw-ec3/fileSequence1.ec3 +0 -0
  222. package/test/test/assets/hls-raw-ec3/fileSequence2.ec3 +0 -0
  223. package/test/test/assets/hls-raw-ec3/fileSequence3.ec3 +0 -0
  224. package/test/test/assets/hls-raw-ec3/fileSequence4.ec3 +0 -0
  225. package/test/test/assets/hls-raw-ec3/fileSequence5.ec3 +0 -0
  226. package/test/test/assets/hls-raw-ec3/fileSequence6.ec3 +0 -0
  227. package/test/test/assets/hls-raw-ec3/fileSequence7.ec3 +0 -0
  228. package/test/test/assets/hls-raw-ec3/prog_index.m3u8 +31 -0
  229. package/test/test/assets/hls-raw-mp3/chunklist_w905519204.m3u8 +13 -0
  230. package/test/test/assets/hls-raw-mp3/media_w905519204_0.mp3 +0 -0
  231. package/test/test/assets/hls-raw-mp3/media_w905519204_1.mp3 +0 -0
  232. package/test/test/assets/hls-raw-mp3/media_w905519204_2.mp3 +0 -0
  233. package/test/test/assets/hls-raw-mp3/media_w905519204_3.mp3 +0 -0
  234. package/test/test/assets/hls-raw-mp3/playlist.m3u8 +4 -0
  235. package/test/test/assets/hls-ts-aac/playlist.m3u8 +23 -0
  236. package/test/test/assets/hls-ts-aac/segment_0.ts +0 -0
  237. package/test/test/assets/hls-ts-aac/segment_1.ts +0 -0
  238. package/test/test/assets/hls-ts-aac/segment_2.ts +0 -0
  239. package/test/test/assets/hls-ts-aac/segment_3.ts +0 -0
  240. package/test/test/assets/hls-ts-aac/segment_4.ts +0 -0
  241. package/test/test/assets/hls-ts-aac/segment_5.ts +0 -0
  242. package/test/test/assets/hls-ts-aac/segment_6.ts +0 -0
  243. package/test/test/assets/hls-ts-aac/segment_7.ts +0 -0
  244. package/test/test/assets/hls-ts-ac3/ChID_voices_6ch_640kbps_dd_sub_0000.ts +0 -0
  245. package/test/test/assets/hls-ts-ac3/ChID_voices_6ch_640kbps_dd_sub_0001.ts +0 -0
  246. package/test/test/assets/hls-ts-ac3/ChID_voices_6ch_640kbps_dd_sub_0002.ts +0 -0
  247. package/test/test/assets/hls-ts-ac3/ChID_voices_6ch_640kbps_dd_sub_0003.ts +0 -0
  248. package/test/test/assets/hls-ts-ac3/ChID_voices_6ch_640kbps_dd_sub_0004.ts +0 -0
  249. package/test/test/assets/hls-ts-ac3/ChID_voices_6ch_640kbps_dd_sub_0005.ts +0 -0
  250. package/test/test/assets/hls-ts-ac3/prog_index.m3u8 +19 -0
  251. package/test/test/assets/hls-ts-ec3/ChID_voices_6ch_256kbps_ddp_sub_0000.ts +0 -0
  252. package/test/test/assets/hls-ts-ec3/ChID_voices_6ch_256kbps_ddp_sub_0001.ts +0 -0
  253. package/test/test/assets/hls-ts-ec3/ChID_voices_6ch_256kbps_ddp_sub_0002.ts +0 -0
  254. package/test/test/assets/hls-ts-ec3/ChID_voices_6ch_256kbps_ddp_sub_0003.ts +0 -0
  255. package/test/test/assets/hls-ts-ec3/ChID_voices_6ch_256kbps_ddp_sub_0004.ts +0 -0
  256. package/test/test/assets/hls-ts-ec3/ChID_voices_6ch_256kbps_ddp_sub_0005.ts +0 -0
  257. package/test/test/assets/hls-ts-ec3/prog_index.m3u8 +19 -0
  258. package/test/test/assets/hls-ts-h264/fileSequence0.ts +0 -0
  259. package/test/test/assets/hls-ts-h264/fileSequence1.ts +0 -0
  260. package/test/test/assets/hls-ts-h264/fileSequence2.ts +0 -0
  261. package/test/test/assets/hls-ts-h264/fileSequence3.ts +0 -0
  262. package/test/test/assets/hls-ts-h264/fileSequence4.ts +0 -0
  263. package/test/test/assets/hls-ts-h264/fileSequence5.ts +0 -0
  264. package/test/test/assets/hls-ts-h264/prog_index.m3u8 +25 -0
  265. package/test/test/assets/hls-ts-mp3/manifest.m3u8 +13 -0
  266. package/test/test/assets/hls-ts-mp3/media_w239891843_0.ts +0 -0
  267. package/test/test/assets/hls-ts-mp3/media_w239891843_1.ts +0 -0
  268. package/test/test/assets/hls-ts-mp3/media_w239891843_2.ts +0 -0
  269. package/test/test/assets/hls-ts-mp3/media_w239891843_3.ts +0 -0
  270. package/test/test/assets/hls-ts-muxed-aac-h264/chunk.m3u8 +17 -0
  271. package/test/test/assets/hls-ts-muxed-aac-h264/n_0_0_0.ts +0 -0
  272. package/test/test/assets/hls-ts-muxed-aac-h264/n_1_0_0.ts +0 -0
  273. package/test/test/assets/hls-ts-muxed-aac-h264/n_2_0_0.ts +0 -0
  274. package/test/test/assets/hls-ts-muxed-aac-h264/n_3_0_0.ts +0 -0
  275. package/test/test/assets/hls-ts-muxed-aac-h264/n_4_0_0.ts +0 -0
  276. package/test/test/assets/hls-ts-muxed-aac-h264/n_5_0_0.ts +0 -0
  277. package/test/test/assets/hls-ts-muxed-aac-h264/playlist.m3u8 +4 -0
  278. package/test/test/boot.js +7 -2
  279. package/test/test/util/fake_ad.js +99 -0
  280. package/test/test/util/fake_ad_manager.js +7 -1
  281. package/test/test/util/fake_networking_engine.js +41 -14
  282. package/test/test/util/fake_segment_prefetch.js +81 -0
  283. package/test/test/util/manifest_generator.js +19 -5
  284. package/test/test/util/mss_parser_util.js +115 -0
  285. package/test/test/util/offline_utils.js +1 -0
  286. package/test/test/util/simple_fakes.js +12 -3
  287. package/test/test/util/streaming_engine_util.js +14 -4
  288. package/test/test/util/ttml_utils.js +2 -2
  289. package/test/text/cue_integration.js +1 -1
  290. package/test/text/srt_text_parser_unit.js +78 -0
  291. package/test/text/text_engine_unit.js +17 -8
  292. package/test/text/ui_text_displayer_unit.js +4 -4
  293. package/test/text/vtt_text_parser_unit.js +59 -40
  294. package/test/text/web_vtt_generator_unit.js +9 -3
  295. package/test/{media/transmuxer_integration.js → transmuxer/muxjs_transmuxer_integration.js} +30 -33
  296. package/test/transmuxer/transmuxer_engine_integration.js +60 -0
  297. package/test/transmuxer/transmuxer_integration.js +296 -0
  298. package/test/util/cmcd_manager_unit.js +162 -13
  299. package/test/util/language_utils_unit.js +9 -2
  300. package/test/util/mp4_box_parsers_unit.js +39 -4
  301. package/test/util/periods_unit.js +34 -26
  302. package/test/util/stream_utils_unit.js +60 -107
  303. package/ui/controls.js +19 -1
  304. package/ui/controls.less +1 -0
  305. package/ui/externs/ui.js +11 -1
  306. package/ui/language_utils.js +15 -0
  307. package/ui/less/thumbnails.less +26 -0
  308. package/ui/locales/oc.json +42 -0
  309. package/ui/pip_button.js +83 -2
  310. package/ui/range_element.js +15 -0
  311. package/ui/resolution_selection.js +9 -2
  312. package/ui/seek_bar.js +223 -0
  313. package/ui/ui.js +5 -0
  314. package/lib/cea/i_caption_decoder.js +0 -51
  315. package/lib/cea/i_cea_parser.js +0 -68
@@ -158,15 +158,7 @@ shaka.hls.ManifestTextParser = class {
158
158
  partialSegmentTags.push(tag);
159
159
  } else if (tag.name == 'EXT-X-PRELOAD-HINT') {
160
160
  if (tag.getAttributeValue('TYPE') == 'PART') {
161
- // Note: BYTERANGE-START without BYTERANGE-LENGTH is being
162
- // ignored.
163
- if (tag.getAttributeValue('BYTERANGE-START') != null) {
164
- if (tag.getAttributeValue('BYTERANGE-LENGTH') != null) {
165
- partialSegmentTags.push(tag);
166
- }
167
- } else {
168
- partialSegmentTags.push(tag);
169
- }
161
+ partialSegmentTags.push(tag);
170
162
  } else if (tag.getAttributeValue('TYPE') == 'MAP') {
171
163
  // Rename the Preload Hint tag to be a Map tag.
172
164
  tag.setName('EXT-X-MAP');
@@ -180,15 +172,13 @@ shaka.hls.ManifestTextParser = class {
180
172
  // Skip comments.
181
173
  } else {
182
174
  const verbatimSegmentUri = line.trim();
183
- const absoluteSegmentUri = shaka.hls.Utils.constructAbsoluteUri(
184
- absoluteMediaPlaylistUri, verbatimSegmentUri);
185
175
  // Attach the last parsed EXT-X-MAP tag to the segment.
186
176
  if (currentMapTag) {
187
177
  segmentTags.push(currentMapTag);
188
178
  }
189
179
  // The URI appears after all of the tags describing the segment.
190
- const segment = new shaka.hls.Segment(absoluteSegmentUri, segmentTags,
191
- partialSegmentTags);
180
+ const segment = new shaka.hls.Segment(absoluteMediaPlaylistUri,
181
+ verbatimSegmentUri, segmentTags, partialSegmentTags);
192
182
  segments.push(segment);
193
183
  segmentTags = [];
194
184
  partialSegmentTags = [];
@@ -203,7 +193,7 @@ shaka.hls.ManifestTextParser = class {
203
193
  if (currentMapTag) {
204
194
  segmentTags.push(currentMapTag);
205
195
  }
206
- const segment = new shaka.hls.Segment('', segmentTags,
196
+ const segment = new shaka.hls.Segment('', '', segmentTags,
207
197
  partialSegmentTags);
208
198
  segments.push(segment);
209
199
  }
@@ -49,13 +49,14 @@ shaka.media.ExampleBasedCriteria = class {
49
49
  // role and label for this.
50
50
  const role = '';
51
51
  const label = '';
52
+ const hdrLevel = '';
52
53
  const channelCount = example.audio && example.audio.channelsCount ?
53
54
  example.audio.channelsCount :
54
55
  0;
55
56
 
56
57
  /** @private {!shaka.media.AdaptationSetCriteria} */
57
58
  this.fallback_ = new shaka.media.PreferenceBasedCriteria(
58
- example.language, role, channelCount, label);
59
+ example.language, role, channelCount, hdrLevel, label);
59
60
  }
60
61
 
61
62
  /** @override */
@@ -87,9 +88,10 @@ shaka.media.PreferenceBasedCriteria = class {
87
88
  * @param {string} language
88
89
  * @param {string} role
89
90
  * @param {number} channelCount
91
+ * @param {string} hdrLevel
90
92
  * @param {string=} label
91
93
  */
92
- constructor(language, role, channelCount, label = '') {
94
+ constructor(language, role, channelCount, hdrLevel, label = '') {
93
95
  /** @private {string} */
94
96
  this.language_ = language;
95
97
  /** @private {string} */
@@ -97,6 +99,8 @@ shaka.media.PreferenceBasedCriteria = class {
97
99
  /** @private {number} */
98
100
  this.channelCount_ = channelCount;
99
101
  /** @private {string} */
102
+ this.hdrLevel_ = hdrLevel;
103
+ /** @private {string} */
100
104
  this.label_ = label;
101
105
  }
102
106
 
@@ -127,6 +131,17 @@ shaka.media.PreferenceBasedCriteria = class {
127
131
  shaka.log.warning('No exact match for variant role could be found.');
128
132
  }
129
133
 
134
+ if (this.hdrLevel_) {
135
+ const byHdrLevel = Class.filterVariantsByHDRLevel_(
136
+ current, this.hdrLevel_);
137
+ if (byHdrLevel.length) {
138
+ current = byHdrLevel;
139
+ } else {
140
+ shaka.log.warning(
141
+ 'No exact match for the hdr level could be found.');
142
+ }
143
+ }
144
+
130
145
  if (this.channelCount_) {
131
146
  const byChannel = StreamUtils.filterVariantsByAudioChannelCount(
132
147
  current, this.channelCount_);
@@ -218,7 +233,7 @@ shaka.media.PreferenceBasedCriteria = class {
218
233
  */
219
234
  static filterVariantsByLabel_(variants, preferredLabel) {
220
235
  return variants.filter((variant) => {
221
- if (!variant.audio) {
236
+ if (!variant.audio || !variant.audio.label) {
222
237
  return false;
223
238
  }
224
239
 
@@ -227,4 +242,29 @@ shaka.media.PreferenceBasedCriteria = class {
227
242
  return label1 == label2;
228
243
  });
229
244
  }
245
+
246
+
247
+ /**
248
+ * Filters variants according to the given hdr level config.
249
+ *
250
+ * @param {!Array.<shaka.extern.Variant>} variants
251
+ * @param {string} hdrLevel
252
+ * @private
253
+ */
254
+ static filterVariantsByHDRLevel_(variants, hdrLevel) {
255
+ if (hdrLevel == 'AUTO') {
256
+ // Auto detect the ideal HDR level.
257
+ if (window.matchMedia('(color-gamut: p3)').matches) {
258
+ hdrLevel = 'PQ';
259
+ } else {
260
+ hdrLevel = 'SDR';
261
+ }
262
+ }
263
+ return variants.filter((variant) => {
264
+ if (variant.video && variant.video.hdr && variant.video.hdr != hdrLevel) {
265
+ return false;
266
+ }
267
+ return true;
268
+ });
269
+ }
230
270
  };
@@ -7,12 +7,9 @@
7
7
  goog.provide('shaka.media.ClosedCaptionParser');
8
8
  goog.provide('shaka.media.IClosedCaptionParser');
9
9
 
10
- goog.require('shaka.cea.CeaDecoder');
10
+ goog.require('shaka.cea.DummyCaptionDecoder');
11
11
  goog.require('shaka.cea.DummyCeaParser');
12
- goog.require('shaka.cea.Mp4CeaParser');
13
12
  goog.require('shaka.util.BufferUtils');
14
- goog.requireType('shaka.cea.ICaptionDecoder');
15
- goog.requireType('shaka.cea.ICeaParser');
16
13
 
17
14
 
18
15
  /**
@@ -21,6 +18,7 @@ goog.requireType('shaka.cea.ICeaParser');
21
18
  * TODO: Remove this interface and move method definitions
22
19
  * directly to ClosedCaptonParser.
23
20
  * @interface
21
+ * @export
24
22
  */
25
23
  shaka.media.IClosedCaptionParser = class {
26
24
  /**
@@ -35,7 +33,7 @@ shaka.media.IClosedCaptionParser = class {
35
33
  * captions.
36
34
  *
37
35
  * @param {BufferSource} mediaFragment
38
- * @return {!Array<!shaka.cea.ICaptionDecoder.ClosedCaption>}
36
+ * @return {!Array<!shaka.extern.ICaptionDecoder.ClosedCaption>}
39
37
  * An array of parsed closed captions.
40
38
  */
41
39
  parseFrom(mediaFragment) {}
@@ -52,23 +50,32 @@ shaka.media.IClosedCaptionParser = class {
52
50
  *
53
51
  * @implements {shaka.media.IClosedCaptionParser}
54
52
  * @final
53
+ * @export
55
54
  */
56
55
  shaka.media.ClosedCaptionParser = class {
57
- /** */
56
+ /**
57
+ * @param {string} mimeType
58
+ */
58
59
  constructor(mimeType) {
59
- /** @private {!shaka.cea.ICeaParser} */
60
+ /** @private {!shaka.extern.ICeaParser} */
60
61
  this.ceaParser_ = new shaka.cea.DummyCeaParser();
61
62
 
62
- if (mimeType.includes('video/mp4')) {
63
- // MP4 Parser to extract closed caption packets from H.264 video.
64
- this.ceaParser_ = new shaka.cea.Mp4CeaParser();
63
+ const parserFactory =
64
+ shaka.media.ClosedCaptionParser.findParser(mimeType.toLowerCase());
65
+ if (parserFactory) {
66
+ this.ceaParser_ = parserFactory();
65
67
  }
66
68
 
67
69
  /**
68
70
  * Decoder for decoding CEA-X08 data from closed caption packets.
69
- * @private {!shaka.cea.ICaptionDecoder}
71
+ * @private {!shaka.extern.ICaptionDecoder}
70
72
  */
71
- this.ceaDecoder_ = new shaka.cea.CeaDecoder();
73
+ this.ceaDecoder_ = new shaka.cea.DummyCaptionDecoder();
74
+
75
+ const decoderFactory = shaka.media.ClosedCaptionParser.findDecoder();
76
+ if (decoderFactory) {
77
+ this.ceaDecoder_ = decoderFactory();
78
+ }
72
79
  }
73
80
 
74
81
  /**
@@ -104,4 +111,59 @@ shaka.media.ClosedCaptionParser = class {
104
111
  reset() {
105
112
  this.ceaDecoder_.clear();
106
113
  }
114
+
115
+ /**
116
+ * @param {string} mimeType
117
+ * @param {!shaka.extern.CeaParserPlugin} plugin
118
+ * @export
119
+ */
120
+ static registerParser(mimeType, plugin) {
121
+ shaka.media.ClosedCaptionParser.parserMap_[mimeType] = plugin;
122
+ }
123
+
124
+ /**
125
+ * @param {string} mimeType
126
+ * @export
127
+ */
128
+ static unregisterParser(mimeType) {
129
+ delete shaka.media.ClosedCaptionParser.parserMap_[mimeType];
130
+ }
131
+
132
+ /**
133
+ * @param {string} mimeType
134
+ * @return {?shaka.extern.CeaParserPlugin}
135
+ * @export
136
+ */
137
+ static findParser(mimeType) {
138
+ return shaka.media.ClosedCaptionParser.parserMap_[mimeType];
139
+ }
140
+
141
+ /**
142
+ * @param {!shaka.extern.CaptionDecoderPlugin} plugin
143
+ * @export
144
+ */
145
+ static registerDecoder(plugin) {
146
+ shaka.media.ClosedCaptionParser.decoderFactory_ = plugin;
147
+ }
148
+
149
+ /**
150
+ * @export
151
+ */
152
+ static unregisterDecoder() {
153
+ shaka.media.ClosedCaptionParser.decoderFactory_ = null;
154
+ }
155
+
156
+ /**
157
+ * @return {?shaka.extern.CaptionDecoderPlugin}
158
+ * @export
159
+ */
160
+ static findDecoder() {
161
+ return shaka.media.ClosedCaptionParser.decoderFactory_;
162
+ }
107
163
  };
164
+
165
+ /** @private {!Object<string, shaka.extern.CeaParserPlugin>} */
166
+ shaka.media.ClosedCaptionParser.parserMap_ = {};
167
+
168
+ /** @private {?shaka.extern.CaptionDecoderPlugin} */
169
+ shaka.media.ClosedCaptionParser.decoderFactory_ = null;
@@ -8,8 +8,8 @@ goog.provide('shaka.media.DrmEngine');
8
8
 
9
9
  goog.require('goog.asserts');
10
10
  goog.require('shaka.log');
11
- goog.require('shaka.media.Transmuxer');
12
11
  goog.require('shaka.net.NetworkingEngine');
12
+ goog.require('shaka.transmuxer.TransmuxerEngine');
13
13
  goog.require('shaka.util.BufferUtils');
14
14
  goog.require('shaka.util.Destroyer');
15
15
  goog.require('shaka.util.Error');
@@ -71,8 +71,11 @@ shaka.media.DrmEngine = class {
71
71
  */
72
72
  this.activeSessions_ = new Map();
73
73
 
74
- /** @private {!Array.<string>} */
75
- this.offlineSessionIds_ = [];
74
+ /**
75
+ * @private {!Map<string,
76
+ * {initData: ?Uint8Array, initDataType: ?string}>}
77
+ */
78
+ this.storedPersistentSessions_ = new Map();
76
79
 
77
80
  /** @private {!shaka.util.PublicPromise} */
78
81
  this.allSessionsLoaded_ = new shaka.util.PublicPromise();
@@ -82,7 +85,10 @@ shaka.media.DrmEngine = class {
82
85
 
83
86
  /** @private {function(!shaka.util.Error)} */
84
87
  this.onError_ = (err) => {
85
- this.allSessionsLoaded_.reject(err);
88
+ if (err.severity == shaka.util.Error.Severity.CRITICAL) {
89
+ this.allSessionsLoaded_.reject(err);
90
+ }
91
+
86
92
  playerInterface.onError(err);
87
93
  };
88
94
 
@@ -187,7 +193,7 @@ shaka.media.DrmEngine = class {
187
193
  this.currentDrmInfo_ = null;
188
194
  this.supportedTypes_.clear();
189
195
  this.mediaKeys_ = null;
190
- this.offlineSessionIds_ = [];
196
+ this.storedPersistentSessions_ = new Map();
191
197
  this.config_ = null;
192
198
  this.onError_ = () => {};
193
199
  this.playerInterface_ = null;
@@ -231,7 +237,7 @@ shaka.media.DrmEngine = class {
231
237
  // 2. We are about to remove the offline sessions for this manifest - in
232
238
  // that case, we don't need to know about them right now either as
233
239
  // we will be told which ones to remove later.
234
- this.offlineSessionIds_ = [];
240
+ this.storedPersistentSessions_ = new Map();
235
241
 
236
242
  // What we really need to know is whether or not they are expecting to use
237
243
  // persistent licenses.
@@ -249,8 +255,20 @@ shaka.media.DrmEngine = class {
249
255
  * @return {!Promise}
250
256
  */
251
257
  initForPlayback(variants, offlineSessionIds) {
252
- this.offlineSessionIds_ = offlineSessionIds;
253
- this.usePersistentLicenses_ = offlineSessionIds.length > 0;
258
+ this.storedPersistentSessions_ = new Map();
259
+
260
+ for (const sessionId of offlineSessionIds) {
261
+ this.storedPersistentSessions_.set(
262
+ sessionId, {initData: null, initDataType: null});
263
+ }
264
+
265
+ for (const metadata of this.config_.persistentSessionsMetadata) {
266
+ this.storedPersistentSessions_.set(
267
+ metadata.sessionId,
268
+ {initData: metadata.initData, initDataType: metadata.initDataType});
269
+ }
270
+
271
+ this.usePersistentLicenses_ = this.storedPersistentSessions_.size > 0;
254
272
 
255
273
  return this.init_(variants);
256
274
  }
@@ -304,7 +322,7 @@ shaka.media.DrmEngine = class {
304
322
  /**
305
323
  * Negotiate for a key system and set up MediaKeys.
306
324
  * This will assume that both |usePersistentLicences_| and
307
- * |offlineSessionIds_| have been properly set.
325
+ * |storedPersistentSessions_| have been properly set.
308
326
  *
309
327
  * @param {!Array.<shaka.extern.Variant>} variants
310
328
  * The variants that we expect to operate with during the drm engine's
@@ -378,7 +396,8 @@ shaka.media.DrmEngine = class {
378
396
  // We should get the decodingInfo results for the variants after we filling
379
397
  // in the drm infos, and before queryMediaKeys_().
380
398
  await shaka.util.StreamUtils.getDecodingInfosForVariants(variants,
381
- this.usePersistentLicenses_, this.srcEquals_);
399
+ this.usePersistentLicenses_, this.srcEquals_,
400
+ this.config_.preferredKeySystems);
382
401
 
383
402
  const hasDrmInfo = hadDrmInfo || Object.keys(this.config_.servers).length;
384
403
  // An unencrypted content is initialized.
@@ -502,18 +521,21 @@ shaka.media.DrmEngine = class {
502
521
  */
503
522
  if (this.manifestInitData_ ||
504
523
  this.currentDrmInfo_.keySystem !== 'com.apple.fps' ||
505
- this.offlineSessionIds_.length) {
524
+ this.storedPersistentSessions_.size) {
506
525
  await this.attachMediaKeys_();
507
526
  }
508
527
 
509
- this.createOrLoad();
528
+ this.createOrLoad().catch(() => {
529
+ // Silence errors
530
+ // createOrLoad will run async, errors are triggered through onError_
531
+ });
510
532
 
511
533
  // Explicit init data for any one stream or an offline session is
512
534
  // sufficient to suppress 'encrypted' events for all streams.
513
535
  // Also suppress 'encrypted' events when parsing in-band ppsh
514
536
  // from media segments because that serves the same purpose as the
515
537
  // 'encrypted' events.
516
- if (!this.manifestInitData_ && !this.offlineSessionIds_.length &&
538
+ if (!this.manifestInitData_ && !this.storedPersistentSessions_.size &&
517
539
  !this.config_.parseInbandPsshEnabled) {
518
540
  this.eventManager_.listen(
519
541
  this.video_, 'encrypted', (e) => this.onEncryptedEvent_(e));
@@ -601,7 +623,8 @@ shaka.media.DrmEngine = class {
601
623
  goog.asserts.assert(this.mediaKeys_,
602
624
  'Must call init() before removeSession');
603
625
 
604
- const session = await this.loadOfflineSession_(sessionId);
626
+ const session = await this.loadOfflineSession_(
627
+ sessionId, {initData: null, initDataType: null});
605
628
 
606
629
  // This will be null on error, such as session not found.
607
630
  if (!session) {
@@ -634,8 +657,30 @@ shaka.media.DrmEngine = class {
634
657
  *
635
658
  * @return {!Promise}
636
659
  */
637
- createOrLoad() {
638
- // Create temp sessions.
660
+ async createOrLoad() {
661
+ if (this.storedPersistentSessions_.size) {
662
+ this.storedPersistentSessions_.forEach((metadata, sessionId) => {
663
+ this.loadOfflineSession_(sessionId, metadata);
664
+ });
665
+
666
+ await this.allSessionsLoaded_;
667
+
668
+ const keyIds = (this.currentDrmInfo_ && this.currentDrmInfo_.keyIds) ||
669
+ new Set([]);
670
+
671
+ // All the needed keys are already loaded, we don't need another license
672
+ // Therefore we prevent starting a new session
673
+ if (keyIds.size > 0 && this.areAllKeysUsable_()) {
674
+ return this.allSessionsLoaded_;
675
+ }
676
+
677
+ // Reset the promise for the next sessions to come if key needs aren't
678
+ // satisfied with persistent sessions
679
+ this.allSessionsLoaded_ = new shaka.util.PublicPromise();
680
+ this.allSessionsLoaded_.catch(() => {});
681
+ }
682
+
683
+ // Create sessions.
639
684
  const initDatas =
640
685
  (this.currentDrmInfo_ ? this.currentDrmInfo_.initData : []) || [];
641
686
  for (const initDataOverride of initDatas) {
@@ -643,14 +688,9 @@ shaka.media.DrmEngine = class {
643
688
  initDataOverride.initDataType, initDataOverride.initData);
644
689
  }
645
690
 
646
- // Load each session.
647
- for (const sessionId of this.offlineSessionIds_) {
648
- this.loadOfflineSession_(sessionId);
649
- }
650
-
651
691
  // If we have no sessions, we need to resolve the promise right now or else
652
692
  // it will never get resolved.
653
- if (!initDatas.length && !this.offlineSessionIds_.length) {
693
+ if (!initDatas.length) {
654
694
  this.allSessionsLoaded_.resolve();
655
695
  }
656
696
 
@@ -778,6 +818,28 @@ shaka.media.DrmEngine = class {
778
818
  return Array.from(ids);
779
819
  }
780
820
 
821
+ /**
822
+ * Returns the active sessions metadata
823
+ *
824
+ * @return {!Array.<shaka.extern.DrmSessionMetadata>}
825
+ */
826
+ getActiveSessionsMetadata() {
827
+ const sessions = this.activeSessions_.keys();
828
+
829
+ const metadata = shaka.util.Iterables.map(sessions, (session) => {
830
+ const metadata = this.activeSessions_.get(session);
831
+
832
+ return {
833
+ sessionId: session.sessionId,
834
+ sessionType: metadata.type,
835
+ initData: metadata.initData,
836
+ initDataType: metadata.initDataType,
837
+ };
838
+ });
839
+
840
+ return Array.from(metadata);
841
+ }
842
+
781
843
  /**
782
844
  * Returns the next expiration time, or Infinity.
783
845
  * @return {number}
@@ -853,10 +915,11 @@ shaka.media.DrmEngine = class {
853
915
  static computeMimeType_(stream, codecOverride) {
854
916
  const realMimeType = shaka.util.MimeUtils.getFullType(stream.mimeType,
855
917
  codecOverride || stream.codecs);
856
- if (shaka.media.Transmuxer.isSupported(realMimeType)) {
918
+ const TransmuxerEngine = shaka.transmuxer.TransmuxerEngine;
919
+ if (TransmuxerEngine.isSupported(realMimeType, stream.type)) {
857
920
  // This will be handled by the Transmuxer, so use the MIME type that the
858
921
  // Transmuxer will produce.
859
- return shaka.media.Transmuxer.convertCodecs(stream.type, realMimeType);
922
+ return TransmuxerEngine.convertCodecs(stream.type, realMimeType);
860
923
  }
861
924
  return realMimeType;
862
925
  }
@@ -934,6 +997,30 @@ shaka.media.DrmEngine = class {
934
997
  this.currentDrmInfo_.keySystem);
935
998
 
936
999
  this.mediaKeys_ = mediaKeys;
1000
+ if (this.config_.minHdcpVersion != '' &&
1001
+ 'getStatusForPolicy' in this.mediaKeys_) {
1002
+ try {
1003
+ const status = await this.mediaKeys_.getStatusForPolicy({
1004
+ minHdcpVersion: this.config_.minHdcpVersion,
1005
+ });
1006
+ if (status != 'usable') {
1007
+ throw new shaka.util.Error(
1008
+ shaka.util.Error.Severity.CRITICAL,
1009
+ shaka.util.Error.Category.DRM,
1010
+ shaka.util.Error.Code.MIN_HDCP_VERSION_NOT_MATCH);
1011
+ }
1012
+ this.destroyer_.ensureNotDestroyed();
1013
+ } catch (e) {
1014
+ if (e instanceof shaka.util.Error) {
1015
+ throw e;
1016
+ }
1017
+ throw new shaka.util.Error(
1018
+ shaka.util.Error.Severity.CRITICAL,
1019
+ shaka.util.Error.Category.DRM,
1020
+ shaka.util.Error.Code.ERROR_CHECKING_HDCP_VERSION,
1021
+ e.message);
1022
+ }
1023
+ }
937
1024
  this.initialized_ = true;
938
1025
 
939
1026
  await this.setServerCertificate();
@@ -1171,12 +1258,41 @@ shaka.media.DrmEngine = class {
1171
1258
  };
1172
1259
  }
1173
1260
 
1261
+ /**
1262
+ * Resolves the allSessionsLoaded_ promise when all the sessions are loaded
1263
+ *
1264
+ * @private
1265
+ */
1266
+ checkSessionsLoaded_() {
1267
+ if (this.areAllSessionsLoaded_()) {
1268
+ this.allSessionsLoaded_.resolve();
1269
+ }
1270
+ }
1271
+
1272
+ /**
1273
+ * In case there are no key statuses, consider this session loaded
1274
+ * after a reasonable timeout. It should definitely not take 5
1275
+ * seconds to process a license.
1276
+ * @param {!shaka.media.DrmEngine.SessionMetaData} metadata
1277
+ * @private
1278
+ */
1279
+ setLoadSessionTimeoutTimer_(metadata) {
1280
+ const timer = new shaka.util.Timer(() => {
1281
+ metadata.loaded = true;
1282
+ this.checkSessionsLoaded_();
1283
+ });
1284
+
1285
+ timer.tickAfter(
1286
+ /* seconds= */ shaka.media.DrmEngine.SESSION_LOAD_TIMEOUT_);
1287
+ }
1288
+
1174
1289
  /**
1175
1290
  * @param {string} sessionId
1291
+ * @param {{initData: ?Uint8Array, initDataType: ?string}} sessionMetadata
1176
1292
  * @return {!Promise.<MediaKeySession>}
1177
1293
  * @private
1178
1294
  */
1179
- async loadOfflineSession_(sessionId) {
1295
+ async loadOfflineSession_(sessionId, sessionMetadata) {
1180
1296
  let session;
1181
1297
 
1182
1298
  const sessionType = 'persistent-license';
@@ -1201,8 +1317,8 @@ shaka.media.DrmEngine = class {
1201
1317
  (event) => this.onKeyStatusesChange_(event));
1202
1318
 
1203
1319
  const metadata = {
1204
- initData: null,
1205
- initDataType: null,
1320
+ initData: sessionMetadata.initData,
1321
+ initDataType: sessionMetadata.initDataType,
1206
1322
  loaded: false,
1207
1323
  oldExpiration: Infinity,
1208
1324
  updatePromise: null,
@@ -1218,32 +1334,42 @@ shaka.media.DrmEngine = class {
1218
1334
  if (!present) {
1219
1335
  this.activeSessions_.delete(session);
1220
1336
 
1337
+ const severity = this.config_.persistentSessionOnlinePlayback ?
1338
+ shaka.util.Error.Severity.RECOVERABLE :
1339
+ shaka.util.Error.Severity.CRITICAL;
1340
+
1221
1341
  this.onError_(new shaka.util.Error(
1222
- shaka.util.Error.Severity.CRITICAL,
1342
+ severity,
1223
1343
  shaka.util.Error.Category.DRM,
1224
1344
  shaka.util.Error.Code.OFFLINE_SESSION_REMOVED));
1225
- return Promise.resolve();
1226
- }
1227
1345
 
1228
- // TODO: We should get a key status change event. Remove once Chrome CDM
1229
- // is fixed.
1230
- metadata.loaded = true;
1231
- if (this.areAllSessionsLoaded_()) {
1232
- this.allSessionsLoaded_.resolve();
1346
+ metadata.loaded = true;
1233
1347
  }
1234
1348
 
1349
+ this.setLoadSessionTimeoutTimer_(metadata);
1350
+ this.checkSessionsLoaded_();
1351
+
1235
1352
  return session;
1236
1353
  } catch (error) {
1237
1354
  this.destroyer_.ensureNotDestroyed(error);
1238
1355
 
1239
1356
  this.activeSessions_.delete(session);
1240
1357
 
1358
+ const severity = this.config_.persistentSessionOnlinePlayback ?
1359
+ shaka.util.Error.Severity.RECOVERABLE :
1360
+ shaka.util.Error.Severity.CRITICAL;
1361
+
1241
1362
  this.onError_(new shaka.util.Error(
1242
- shaka.util.Error.Severity.CRITICAL,
1363
+ severity,
1243
1364
  shaka.util.Error.Category.DRM,
1244
1365
  shaka.util.Error.Code.FAILED_TO_CREATE_SESSION,
1245
1366
  error.message));
1367
+
1368
+ metadata.loaded = true;
1369
+
1370
+ this.checkSessionsLoaded_();
1246
1371
  }
1372
+
1247
1373
  return Promise.resolve();
1248
1374
  }
1249
1375
 
@@ -1469,18 +1595,8 @@ shaka.media.DrmEngine = class {
1469
1595
  if (metadata.updatePromise) {
1470
1596
  metadata.updatePromise.resolve();
1471
1597
  }
1472
- // In case there are no key statuses, consider this session loaded
1473
- // after a reasonable timeout. It should definitely not take 5
1474
- // seconds to process a license.
1475
- const timer = new shaka.util.Timer(() => {
1476
- metadata.loaded = true;
1477
- if (this.areAllSessionsLoaded_()) {
1478
- this.allSessionsLoaded_.resolve();
1479
- }
1480
- });
1481
1598
 
1482
- timer.tickAfter(
1483
- /* seconds= */ shaka.media.DrmEngine.SESSION_LOAD_TIMEOUT_);
1599
+ this.setLoadSessionTimeoutTimer_(metadata);
1484
1600
  }
1485
1601
  }
1486
1602
 
@@ -1828,8 +1944,9 @@ shaka.media.DrmEngine = class {
1828
1944
  * the playback session ends
1829
1945
  */
1830
1946
  if (!this.initializedForStorage_ &&
1831
- !this.offlineSessionIds_.includes(session.sessionId) &&
1832
- metadata.type === 'persistent-license') {
1947
+ !this.storedPersistentSessions_.has(session.sessionId) &&
1948
+ metadata.type === 'persistent-license' &&
1949
+ !this.config_.persistentSessionOnlinePlayback) {
1833
1950
  shaka.log.v1('Removing session', session.sessionId);
1834
1951
 
1835
1952
  await session.remove();
@@ -1895,6 +2012,10 @@ shaka.media.DrmEngine = class {
1895
2012
  return true;
1896
2013
  }
1897
2014
 
2015
+ if (drms1 === drms2) {
2016
+ return true;
2017
+ }
2018
+
1898
2019
  return shaka.media.DrmEngine.getCommonDrmInfos(
1899
2020
  drms1, drms2).length > 0;
1900
2021
  }
@@ -2003,6 +2124,25 @@ shaka.media.DrmEngine = class {
2003
2124
  return shaka.util.Iterables.every(metadatas, (data) => data.loaded);
2004
2125
  }
2005
2126
 
2127
+ /**
2128
+ * @return {boolean}
2129
+ * @private
2130
+ */
2131
+ areAllKeysUsable_() {
2132
+ const keyIds = (this.currentDrmInfo_ && this.currentDrmInfo_.keyIds) ||
2133
+ new Set([]);
2134
+
2135
+ for (const keyId of keyIds) {
2136
+ const status = this.keyStatusByKeyId_.get(keyId);
2137
+
2138
+ if (status !== 'usable') {
2139
+ return false;
2140
+ }
2141
+ }
2142
+
2143
+ return true;
2144
+ }
2145
+
2006
2146
  /**
2007
2147
  * Replace the drm info used in each variant in |variants| to reflect each
2008
2148
  * key service in |keySystems|.
@@ -89,7 +89,7 @@ shaka.media.GapJumpingController = class {
89
89
  */
90
90
  this.gapJumpTimer_ = new shaka.util.Timer(() => {
91
91
  this.onPollGapJump_();
92
- }).tickEvery(/* seconds= */ 0.25);
92
+ }).tickEvery(this.config_.gapJumpTimerTime);
93
93
  }
94
94
 
95
95