shaka-player 4.4.3 → 4.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (307) hide show
  1. package/.github/ISSUE_TEMPLATE/bug_report.md +1 -1
  2. package/.github/workflows/release-please.yaml +2 -27
  3. package/AUTHORS +2 -0
  4. package/CHANGELOG.md +156 -39
  5. package/CODE_OF_CONDUCT.md +1 -1
  6. package/CONTRIBUTORS +2 -0
  7. package/README.md +54 -42
  8. package/demo/asset_card.js +18 -21
  9. package/demo/cast_receiver/index.html +0 -1
  10. package/demo/close_button.js +0 -3
  11. package/demo/common/asset.js +33 -22
  12. package/demo/common/assets.js +249 -60
  13. package/demo/config.js +219 -253
  14. package/demo/custom.js +88 -133
  15. package/demo/front.js +7 -10
  16. package/demo/index.html +19 -22
  17. package/demo/input.js +6 -11
  18. package/demo/input_container.js +8 -9
  19. package/demo/main.js +62 -126
  20. package/demo/search.js +29 -34
  21. package/demo/service_worker.js +0 -3
  22. package/demo/tooltip.js +3 -5
  23. package/demo/visualizer.js +3 -5
  24. package/demo/visualizer_button.js +1 -20
  25. package/dist/controls.css +1 -1
  26. package/dist/controls.css.map +1 -1
  27. package/dist/demo.compiled.debug.js +211 -228
  28. package/dist/demo.compiled.debug.map +4 -4
  29. package/dist/demo.compiled.js +211 -228
  30. package/dist/demo.compiled.map +4 -4
  31. package/dist/deps.js +42 -37
  32. package/dist/locales.js +34 -30
  33. package/dist/receiver.compiled.debug.js +64 -77
  34. package/dist/receiver.compiled.debug.map +4 -4
  35. package/dist/receiver.compiled.js +64 -77
  36. package/dist/receiver.compiled.map +4 -4
  37. package/dist/shaka-player.compiled.d.ts +343 -151
  38. package/dist/shaka-player.compiled.debug.d.ts +343 -151
  39. package/dist/shaka-player.compiled.debug.externs.js +356 -117
  40. package/dist/shaka-player.compiled.debug.js +952 -811
  41. package/dist/shaka-player.compiled.debug.map +4 -4
  42. package/dist/shaka-player.compiled.externs.js +356 -117
  43. package/dist/shaka-player.compiled.js +1165 -1078
  44. package/dist/shaka-player.compiled.map +4 -4
  45. package/dist/shaka-player.ui.d.ts +359 -153
  46. package/dist/shaka-player.ui.debug.d.ts +359 -153
  47. package/dist/shaka-player.ui.debug.externs.js +382 -117
  48. package/dist/shaka-player.ui.debug.js +1084 -918
  49. package/dist/shaka-player.ui.debug.map +4 -4
  50. package/dist/shaka-player.ui.externs.js +382 -117
  51. package/dist/shaka-player.ui.js +1377 -1274
  52. package/dist/shaka-player.ui.map +4 -4
  53. package/docs/design/lcevc-integration.md +14 -14
  54. package/docs/tutorials/ad_monetization.md +54 -0
  55. package/docs/tutorials/basic-usage.md +2 -1
  56. package/docs/tutorials/errors.md +2 -1
  57. package/docs/tutorials/faq.md +27 -55
  58. package/docs/tutorials/index.json +2 -1
  59. package/docs/tutorials/lcevc.md +118 -0
  60. package/docs/tutorials/offline.md +10 -8
  61. package/docs/tutorials/plugins.md +5 -0
  62. package/docs/tutorials/ui-customization.md +17 -0
  63. package/docs/tutorials/ui.md +2 -1
  64. package/docs/tutorials/upgrade.md +3 -0
  65. package/externs/css_style_declaration.js +19 -0
  66. package/externs/lcevc.js +37 -13
  67. package/externs/managedmediasource.js +24 -0
  68. package/externs/media_remote.js +68 -0
  69. package/externs/mediatailor.js +193 -0
  70. package/externs/pictureinpicture.js +11 -0
  71. package/externs/shaka/abr_manager.js +6 -2
  72. package/externs/shaka/ads.js +27 -0
  73. package/externs/shaka/cea.js +7 -0
  74. package/externs/shaka/codecs.js +5 -3
  75. package/externs/shaka/manifest.js +31 -10
  76. package/externs/shaka/manifest_parser.js +24 -1
  77. package/externs/shaka/net.js +2 -2
  78. package/externs/shaka/offline.js +13 -2
  79. package/externs/shaka/player.js +65 -8
  80. package/externs/shaka/text.js +3 -1
  81. package/externs/shaka/transmuxer.js +1 -1
  82. package/karma.conf.js +7 -3
  83. package/lib/abr/simple_abr_manager.js +66 -9
  84. package/lib/ads/ad_manager.js +104 -50
  85. package/lib/ads/media_tailor_ad.js +297 -0
  86. package/lib/ads/media_tailor_ad_manager.js +839 -0
  87. package/lib/cast/cast_receiver.js +1 -1
  88. package/lib/cea/cea_decoder.js +28 -0
  89. package/lib/cea/dummy_caption_decoder.js +5 -0
  90. package/lib/cea/ts_cea_parser.js +15 -2
  91. package/lib/config/codec_switching_strategy.js +24 -0
  92. package/lib/dash/content_protection.js +120 -2
  93. package/lib/dash/dash_parser.js +322 -93
  94. package/lib/dash/mpd_utils.js +62 -37
  95. package/lib/dash/segment_base.js +17 -10
  96. package/lib/dash/segment_list.js +18 -9
  97. package/lib/dash/segment_template.js +122 -107
  98. package/lib/dependencies/all.js +8 -7
  99. package/lib/hls/hls_classes.js +28 -43
  100. package/lib/hls/hls_parser.js +798 -658
  101. package/lib/hls/hls_utils.js +64 -5
  102. package/lib/hls/manifest_text_parser.js +7 -11
  103. package/lib/lcevc/lcevc_dec.js +220 -0
  104. package/lib/media/adaptation_set.js +32 -14
  105. package/lib/media/adaptation_set_criteria.js +86 -16
  106. package/lib/media/closed_caption_parser.js +13 -0
  107. package/lib/media/content_workarounds.js +6 -0
  108. package/lib/media/drm_engine.js +8 -13
  109. package/lib/media/manifest_parser.js +8 -113
  110. package/lib/media/media_source_capabilities.js +10 -3
  111. package/lib/media/media_source_engine.js +286 -44
  112. package/lib/media/play_rate_controller.js +8 -38
  113. package/lib/media/presentation_timeline.js +7 -1
  114. package/lib/media/quality_observer.js +1 -1
  115. package/lib/media/segment_index.js +6 -6
  116. package/lib/media/segment_prefetch.js +92 -24
  117. package/lib/media/segment_reference.js +73 -7
  118. package/lib/media/segment_utils.js +343 -0
  119. package/lib/media/stall_detector.js +0 -3
  120. package/lib/media/streaming_engine.js +73 -38
  121. package/lib/mss/mss_parser.js +19 -2
  122. package/lib/net/networking_engine.js +67 -12
  123. package/lib/net/networking_utils.js +95 -0
  124. package/lib/offline/indexeddb/v1_storage_cell.js +3 -0
  125. package/lib/offline/indexeddb/v2_storage_cell.js +3 -0
  126. package/lib/offline/manifest_converter.js +3 -0
  127. package/lib/offline/offline_manifest_parser.js +10 -0
  128. package/lib/offline/storage.js +14 -7
  129. package/lib/player.js +1098 -1537
  130. package/lib/polyfill/mediasource.js +22 -1
  131. package/lib/text/cue.js +15 -0
  132. package/lib/text/mp4_ttml_parser.js +2 -2
  133. package/lib/text/srt_text_parser.js +2 -2
  134. package/lib/text/stub_text_displayer.js +51 -0
  135. package/lib/text/text_engine.js +3 -2
  136. package/lib/text/ttml_text_parser.js +45 -5
  137. package/lib/text/ui_text_displayer.js +8 -0
  138. package/lib/text/vtt_text_parser.js +40 -22
  139. package/lib/transmuxer/aac_transmuxer.js +1 -1
  140. package/lib/transmuxer/ac3_transmuxer.js +1 -1
  141. package/lib/transmuxer/ec3_transmuxer.js +1 -1
  142. package/lib/transmuxer/h264.js +5 -4
  143. package/lib/transmuxer/h265.js +863 -0
  144. package/lib/transmuxer/mp3_transmuxer.js +1 -1
  145. package/lib/transmuxer/mpeg_ts_transmuxer.js +4 -3
  146. package/lib/transmuxer/mss_transmuxer.js +1 -1
  147. package/lib/transmuxer/ts_transmuxer.js +187 -22
  148. package/lib/util/buffer_utils.js +26 -4
  149. package/lib/util/content_steering_manager.js +358 -0
  150. package/lib/util/error.js +30 -15
  151. package/lib/util/exp_golomb.js +30 -2
  152. package/lib/util/fairplay_utils.js +1 -1
  153. package/lib/util/fake_event.js +8 -1
  154. package/lib/util/id3_utils.js +50 -1
  155. package/lib/util/manifest_parser_utils.js +12 -8
  156. package/lib/util/mp4_generator.js +6 -8
  157. package/lib/util/mutex.js +59 -0
  158. package/lib/util/object_utils.js +1 -1
  159. package/lib/util/periods.js +156 -179
  160. package/lib/util/platform.js +44 -3
  161. package/lib/util/player_configuration.js +43 -6
  162. package/lib/util/state_history.js +6 -2
  163. package/lib/util/stream_utils.js +206 -283
  164. package/lib/util/ts_parser.js +335 -98
  165. package/lib/util/uint8array_utils.js +5 -1
  166. package/lib/util/xml_utils.js +5 -0
  167. package/maintained-branches.md +3 -2
  168. package/package.json +4 -6
  169. package/roadmap.md +25 -5
  170. package/shaka-player.uncompiled.js +2 -2
  171. package/support.html +0 -1
  172. package/test/abr/simple_abr_manager_unit.js +33 -33
  173. package/test/ads/ad_manager_unit.js +3 -2
  174. package/test/cast/cast_receiver_integration.js +50 -4
  175. package/test/cast/cast_utils_unit.js +2 -0
  176. package/test/cea/cea_decoder_unit.js +7 -0
  177. package/test/codec_switching/codec_switching_integration.js +182 -0
  178. package/test/dash/dash_parser_content_protection_unit.js +2 -0
  179. package/test/dash/dash_parser_integration.js +74 -0
  180. package/test/dash/dash_parser_live_unit.js +2 -19
  181. package/test/dash/dash_parser_manifest_unit.js +536 -2
  182. package/test/dash/dash_parser_segment_base_unit.js +2 -0
  183. package/test/dash/dash_parser_segment_list_unit.js +2 -0
  184. package/test/dash/dash_parser_segment_template_unit.js +2 -0
  185. package/test/dash/mpd_utils_unit.js +61 -30
  186. package/test/demo/demo_unit.js +0 -16
  187. package/test/hls/hls_live_unit.js +14 -3
  188. package/test/hls/hls_parser_unit.js +385 -33
  189. package/test/hls/manifest_text_parser_unit.js +32 -99
  190. package/test/media/adaptation_set_criteria_unit.js +284 -15
  191. package/test/media/adaptation_set_unit.js +2 -0
  192. package/test/media/content_workarounds_unit.js +1 -1
  193. package/test/media/drm_engine_unit.js +3 -3
  194. package/test/media/media_source_engine_unit.js +166 -2
  195. package/test/media/play_rate_controller_unit.js +0 -48
  196. package/test/media/segment_index_unit.js +5 -4
  197. package/test/media/segment_prefetch_unit.js +1 -1
  198. package/test/media/streaming_engine_unit.js +17 -17
  199. package/test/mss/mss_parser_unit.js +2 -0
  200. package/test/mss/mss_player_integration.js +3 -2
  201. package/test/net/networking_engine_unit.js +3 -3
  202. package/test/{media/manifest_parser_unit.js → net/networking_utils_unit.js} +2 -2
  203. package/test/offline/manifest_convert_unit.js +14 -0
  204. package/test/offline/offline_integration.js +2 -1
  205. package/test/offline/storage_integration.js +22 -13
  206. package/test/player_external.js +4 -2
  207. package/test/player_integration.js +15 -4
  208. package/test/player_load_graph_integration.js +47 -151
  209. package/test/player_src_equals_external.js +1 -0
  210. package/test/player_src_equals_integration.js +71 -4
  211. package/test/player_unit.js +182 -158
  212. package/test/test/assets/dash-aes-128/Fragments(video=0,format=mpd-time-csf) +0 -0
  213. package/test/test/assets/dash-aes-128/Fragments(video=20000000,format=mpd-time-csf) +0 -0
  214. package/test/test/assets/dash-aes-128/Fragments(video=40000000,format=mpd-time-csf) +0 -0
  215. package/test/test/assets/dash-aes-128/Fragments(video=60000000,format=mpd-time-csf) +0 -0
  216. package/test/test/assets/dash-aes-128/Fragments(video=i,format=mpd-time-csf) +0 -0
  217. package/test/test/assets/dash-aes-128/dash.mpd +18 -0
  218. package/test/test/assets/dash-aes-128/license +1 -0
  219. package/test/test/assets/dash-multi-codec/audio_en_2c_128k_aac.mp4 +0 -0
  220. package/test/test/assets/dash-multi-codec/audio_es_2c_64k_opus.webm +0 -0
  221. package/test/test/assets/dash-multi-codec/dash.mpd +32 -0
  222. package/test/test/assets/dash-multi-codec/video_144p_108k_h264.mp4 +0 -0
  223. package/test/test/assets/hls-ts-h265/0.ts +0 -0
  224. package/test/test/assets/hls-ts-h265/hevc.m3u8 +6 -0
  225. package/test/test/assets/hls-ts-h265/index.m3u8 +7 -0
  226. package/test/test/assets/hls-ts-muxed-aac-h265/0.ts +0 -0
  227. package/test/test/assets/hls-ts-muxed-aac-h265/1.ts +0 -0
  228. package/test/test/assets/hls-ts-muxed-aac-h265/media.m3u8 +9 -0
  229. package/test/test/assets/hls-ts-muxed-ac3-h264/media.m3u8 +24 -0
  230. package/test/test/assets/hls-ts-muxed-ac3-h264/s_00000.ts +0 -0
  231. package/test/test/assets/hls-ts-muxed-ac3-h264/s_00001.ts +0 -0
  232. package/test/test/assets/hls-ts-muxed-ac3-h264/s_00002.ts +0 -0
  233. package/test/test/assets/hls-ts-muxed-ac3-h264/s_00003.ts +0 -0
  234. package/test/test/assets/hls-ts-muxed-ac3-h264/s_00004.ts +0 -0
  235. package/test/test/assets/hls-ts-muxed-ac3-h264/s_00005.ts +0 -0
  236. package/test/test/assets/hls-ts-muxed-ec3-h264/ChID_voices_1280x720p_25fps_h264_6ch_256kbps_ddp_sub_0000.ts +0 -0
  237. package/test/test/assets/hls-ts-muxed-ec3-h264/ChID_voices_1280x720p_25fps_h264_6ch_256kbps_ddp_sub_0001.ts +0 -0
  238. package/test/test/assets/hls-ts-muxed-ec3-h264/ChID_voices_1280x720p_25fps_h264_6ch_256kbps_ddp_sub_0002.ts +0 -0
  239. package/test/test/assets/hls-ts-muxed-ec3-h264/ChID_voices_1280x720p_25fps_h264_6ch_256kbps_ddp_sub_0003.ts +0 -0
  240. package/test/test/assets/hls-ts-muxed-ec3-h264/prog_index.m3u8 +14 -0
  241. package/test/test/assets/hls-ts-muxed-mp3-h264/index.m3u8 +23 -0
  242. package/test/test/assets/hls-ts-muxed-mp3-h264/seg-1-v1-a1.ts +0 -0
  243. package/test/test/assets/hls-ts-muxed-mp3-h264/seg-2-v1-a1.ts +0 -0
  244. package/test/test/assets/hls-ts-muxed-mp3-h264/seg-3-v1-a1.ts +0 -0
  245. package/test/test/assets/hls-ts-muxed-mp3-h264/seg-4-v1-a1.ts +0 -0
  246. package/test/test/assets/hls-ts-muxed-mp3-h264/seg-5-v1-a1.ts +0 -0
  247. package/test/test/assets/hls-ts-muxed-mp3-h264/seg-6-v1-a1.ts +0 -0
  248. package/test/test/assets/hls-ts-muxed-mp3-h264/seg-7-v1-a1.ts +0 -0
  249. package/test/test/assets/hls-ts-muxed-mp3-h264/seg-8-v1-a1.ts +0 -0
  250. package/test/test/util/dash_parser_util.js +4 -0
  251. package/test/test/util/fake_ad_manager.js +20 -0
  252. package/test/test/util/fake_media_source_engine.js +13 -2
  253. package/test/test/util/fake_networking_engine.js +9 -1
  254. package/test/test/util/fake_segment_prefetch.js +3 -0
  255. package/test/test/util/manifest_generator.js +11 -3
  256. package/test/test/util/manifest_parser_util.js +14 -2
  257. package/test/test/util/mss_parser_util.js +4 -0
  258. package/test/test/util/offline_utils.js +2 -0
  259. package/test/test/util/simple_fakes.js +2 -2
  260. package/test/test/util/streaming_engine_util.js +10 -4
  261. package/test/test/util/test_scheme.js +6 -0
  262. package/test/test/util/ui_utils.js +4 -3
  263. package/test/text/mp4_ttml_parser_unit.js +4 -4
  264. package/test/text/srt_text_parser_unit.js +1 -1
  265. package/test/text/text_displayer_layout_unit.js +1 -1
  266. package/test/text/text_engine_unit.js +6 -0
  267. package/test/text/ttml_text_parser_unit.js +85 -2
  268. package/test/text/vtt_text_parser_unit.js +103 -31
  269. package/test/transmuxer/transmuxer_engine_integration.js +9 -0
  270. package/test/transmuxer/transmuxer_integration.js +150 -3
  271. package/test/ui/ad_ui_unit.js +2 -2
  272. package/test/ui/ui_customization_unit.js +17 -16
  273. package/test/ui/ui_integration.js +30 -20
  274. package/test/ui/ui_unit.js +28 -29
  275. package/test/util/buffer_utils_unit.js +1 -1
  276. package/test/util/content_steering_manager_unit.js +427 -0
  277. package/test/util/id3_utils_unit.js +52 -0
  278. package/test/util/stream_utils_unit.js +207 -169
  279. package/test/util/ts_parser_unit.js +3 -3
  280. package/third_party/closure-uri/uri.js +13 -1
  281. package/ui/audio_language_selection.js +9 -2
  282. package/ui/controls.js +125 -0
  283. package/ui/externs/ui.js +24 -2
  284. package/ui/language_utils.js +43 -8
  285. package/ui/less/range_elements.less +33 -0
  286. package/ui/locales/en.json +1 -0
  287. package/ui/locales/es-419.json +1 -0
  288. package/ui/locales/es.json +1 -0
  289. package/ui/locales/source.json +4 -0
  290. package/ui/localization.js +10 -0
  291. package/ui/pip_button.js +22 -120
  292. package/ui/remote_button.js +203 -0
  293. package/ui/resolution_selection.js +47 -10
  294. package/ui/seek_bar.js +296 -3
  295. package/ui/text_selection.js +2 -1
  296. package/ui/ui.js +11 -4
  297. package/demo/common/message_ids.js +0 -309
  298. package/demo/locales/en.json +0 -288
  299. package/demo/locales/source.json +0 -1146
  300. package/externs/mux.js +0 -69
  301. package/lib/lcevc/lcevc_dil.js +0 -152
  302. package/lib/routing/node.js +0 -23
  303. package/lib/routing/payload.js +0 -43
  304. package/lib/routing/walker.js +0 -587
  305. package/lib/transmuxer/muxjs_transmuxer.js +0 -242
  306. package/test/routing/walker_unit.js +0 -504
  307. package/test/transmuxer/muxjs_transmuxer_integration.js +0 -177
@@ -19,7 +19,12 @@ describe('AdaptationSetCriteria', () => {
19
19
  });
20
20
  });
21
21
 
22
- const builder = new shaka.media.PreferenceBasedCriteria('en', '', 0, '');
22
+ const builder = new shaka.media.PreferenceBasedCriteria(
23
+ /* language= */ 'en',
24
+ /* role= */ '',
25
+ /* channelCount= */ 0,
26
+ /* hdrLevel= */ '',
27
+ /* videoLayout= */ '');
23
28
  const set = builder.create(manifest.variants);
24
29
 
25
30
  checkSet(set, [
@@ -40,7 +45,12 @@ describe('AdaptationSetCriteria', () => {
40
45
  });
41
46
  });
42
47
 
43
- const builder = new shaka.media.PreferenceBasedCriteria('en', '', 0, '');
48
+ const builder = new shaka.media.PreferenceBasedCriteria(
49
+ /* language= */ 'en',
50
+ /* role= */ '',
51
+ /* channelCount= */ 0,
52
+ /* hdrLevel= */ '',
53
+ /* videoLayout= */ '');
44
54
  const set = builder.create(manifest.variants);
45
55
 
46
56
  checkSet(set, [
@@ -49,6 +59,102 @@ describe('AdaptationSetCriteria', () => {
49
59
  ]);
50
60
  });
51
61
 
62
+ it('should not filter varaints when codec switching startegy is smooth '+
63
+ 'and changeType is supported', () => {
64
+ const manifest = shaka.test.ManifestGenerator.generate((manifest) => {
65
+ manifest.addVariant(1, (variant) => {
66
+ variant.addAudio(10, (stream) => {
67
+ stream.codecs = 'mp4a.69';
68
+ });
69
+ variant.addVideo(11, (stream) => {
70
+ stream.codecs = 'avc1';
71
+ });
72
+ });
73
+ manifest.addVariant(2, (variant) => {
74
+ variant.addAudio(12, (stream) => {
75
+ stream.codecs = 'mp4a.66';
76
+ });
77
+ variant.addVideo(13, (stream) => {
78
+ stream.codecs = 'hvc1';
79
+ });
80
+ });
81
+ manifest.addVariant(3, (variant) => {
82
+ variant.addAudio(14, (stream) => {
83
+ stream.codecs = 'mp4a.a6';
84
+ });
85
+ variant.addVideo(14, (stream) => {
86
+ stream.codecs = 'dvh1';
87
+ });
88
+ });
89
+ });
90
+
91
+ const originalIsChangeTypeSupported = shaka.media.Capabilities
92
+ .isChangeTypeSupported;
93
+
94
+ try {
95
+ shaka.media.Capabilities.isChangeTypeSupported = () => {
96
+ return true;
97
+ };
98
+
99
+ const builder = new shaka.media.PreferenceBasedCriteria(
100
+ /* language= */ 'en',
101
+ /* role= */ '',
102
+ /* channelCount= */ 0,
103
+ /* hdrLevel= */ '',
104
+ /* videoLayout= */ '',
105
+ /* label= */ '',
106
+ shaka.config.CodecSwitchingStrategy.SMOOTH);
107
+ const set = builder.create(manifest.variants);
108
+
109
+ expect(Array.from(set.values()).length).toBe(3);
110
+ } finally {
111
+ shaka.media.Capabilities
112
+ .isChangeTypeSupported = originalIsChangeTypeSupported;
113
+ }
114
+ });
115
+
116
+ it('should filter varaints when codec switching startegy'+
117
+ 'is not SMOOTH', () => {
118
+ const manifest = shaka.test.ManifestGenerator.generate((manifest) => {
119
+ manifest.addVariant(1, (variant) => {
120
+ variant.addAudio(10, (stream) => {
121
+ stream.codecs = 'mp4a.69';
122
+ });
123
+ variant.addVideo(11, (stream) => {
124
+ stream.codecs = 'avc1';
125
+ });
126
+ });
127
+ manifest.addVariant(2, (variant) => {
128
+ variant.addAudio(12, (stream) => {
129
+ stream.codecs = 'mp4a.66';
130
+ });
131
+ variant.addVideo(13, (stream) => {
132
+ stream.codecs = 'hvc1';
133
+ });
134
+ });
135
+ manifest.addVariant(3, (variant) => {
136
+ variant.addAudio(14, (stream) => {
137
+ stream.codecs = 'mp4a.a6';
138
+ });
139
+ variant.addVideo(14, (stream) => {
140
+ stream.codecs = 'dvh1';
141
+ });
142
+ });
143
+ });
144
+
145
+ const builder = new shaka.media.PreferenceBasedCriteria(
146
+ /* language= */ 'en',
147
+ /* role= */ '',
148
+ /* channelCount= */ 0,
149
+ /* hdrLevel= */ '',
150
+ /* videoLayout= */ '',
151
+ /* label= */ '',
152
+ shaka.config.CodecSwitchingStrategy.RELOAD);
153
+ const set = builder.create(manifest.variants);
154
+
155
+ expect(Array.from(set.values()).length).toBe(1);
156
+ });
157
+
52
158
  it('chooses variants in preferred language and role', () => {
53
159
  const manifest = shaka.test.ManifestGenerator.generate((manifest) => {
54
160
  manifest.addVariant(1, (variant) => {
@@ -72,7 +178,11 @@ describe('AdaptationSetCriteria', () => {
72
178
  });
73
179
 
74
180
  const builder = new shaka.media.PreferenceBasedCriteria(
75
- 'en', 'main', 0, '');
181
+ /* language= */ 'en',
182
+ /* role= */ 'main',
183
+ /* channelCount= */ 0,
184
+ /* hdrLevel= */ '',
185
+ /* videoLayout= */ '');
76
186
  const set = builder.create(manifest.variants);
77
187
 
78
188
  checkSet(set, [
@@ -121,7 +231,12 @@ describe('AdaptationSetCriteria', () => {
121
231
  });
122
232
  });
123
233
 
124
- const builder = new shaka.media.PreferenceBasedCriteria('en', '', 0, '');
234
+ const builder = new shaka.media.PreferenceBasedCriteria(
235
+ /* language= */ 'en',
236
+ /* role= */ '',
237
+ /* channelCount= */ 0,
238
+ /* hdrLevel= */ '',
239
+ /* videoLayout= */ '');
125
240
  const set = builder.create(manifest.variants);
126
241
 
127
242
  // Which role is chosen is an implementation detail.
@@ -179,7 +294,12 @@ describe('AdaptationSetCriteria', () => {
179
294
  });
180
295
  });
181
296
 
182
- const builder = new shaka.media.PreferenceBasedCriteria('zh', '', 0, '');
297
+ const builder = new shaka.media.PreferenceBasedCriteria(
298
+ /* language= */ 'zh',
299
+ /* role= */ '',
300
+ /* channelCount= */ 0,
301
+ /* hdrLevel= */ '',
302
+ /* videoLayout= */ '');
183
303
  const set = builder.create(manifest.variants);
184
304
 
185
305
  // Which role is chosen is an implementation detail.
@@ -215,7 +335,12 @@ describe('AdaptationSetCriteria', () => {
215
335
  });
216
336
  });
217
337
 
218
- const builder = new shaka.media.PreferenceBasedCriteria('zh', '', 0, '');
338
+ const builder = new shaka.media.PreferenceBasedCriteria(
339
+ /* language= */ 'zh',
340
+ /* role= */ '',
341
+ /* channelCount= */ 0,
342
+ /* hdrLevel= */ '',
343
+ /* videoLayout= */ '');
219
344
  const set = builder.create(manifest.variants);
220
345
 
221
346
  // Which language is chosen is an implementation detail.
@@ -272,7 +397,11 @@ describe('AdaptationSetCriteria', () => {
272
397
  });
273
398
 
274
399
  const builder = new shaka.media.PreferenceBasedCriteria(
275
- 'zh', '', 0, '');
400
+ /* language= */ 'zh',
401
+ /* role= */ '',
402
+ /* channelCount= */ 0,
403
+ /* hdrLevel= */ '',
404
+ /* videoLayout= */ '');
276
405
  const set = builder.create(manifest.variants);
277
406
 
278
407
  // Which role is chosen is an implementation detail. Each role is
@@ -330,7 +459,11 @@ describe('AdaptationSetCriteria', () => {
330
459
  });
331
460
 
332
461
  const builder = new shaka.media.PreferenceBasedCriteria(
333
- 'zh', '', 0, '');
462
+ /* language= */ 'zh',
463
+ /* role= */ '',
464
+ /* channelCount= */ 0,
465
+ /* hdrLevel= */ '',
466
+ /* videoLayout= */ '');
334
467
  const set = builder.create(manifest.variants);
335
468
 
336
469
  checkSet(set, [
@@ -358,7 +491,45 @@ describe('AdaptationSetCriteria', () => {
358
491
  });
359
492
  });
360
493
 
361
- const builder = new shaka.media.PreferenceBasedCriteria('', '', 0, 'PQ');
494
+ const builder = new shaka.media.PreferenceBasedCriteria(
495
+ /* language= */ '',
496
+ /* role= */ '',
497
+ /* channelCount= */ 0,
498
+ /* hdrLevel= */ 'PQ',
499
+ /* videoLayout= */ '');
500
+ const set = builder.create(manifest.variants);
501
+
502
+ checkSet(set, [
503
+ manifest.variants[0],
504
+ manifest.variants[2],
505
+ ]);
506
+ });
507
+
508
+ it('chooses variants with preferred video layout (CH-STEREO)', () => {
509
+ const manifest = shaka.test.ManifestGenerator.generate((manifest) => {
510
+ manifest.addVariant(1, (variant) => {
511
+ variant.addVideo(10, (stream) => {
512
+ stream.videoLayout = 'CH-STEREO';
513
+ });
514
+ });
515
+ manifest.addVariant(2, (variant) => {
516
+ variant.addVideo(20, (stream) => {
517
+ stream.videoLayout = 'CH-MONO';
518
+ });
519
+ });
520
+ manifest.addVariant(3, (variant) => {
521
+ variant.addVideo(30, (stream) => {
522
+ stream.videoLayout = '';
523
+ });
524
+ });
525
+ });
526
+
527
+ const builder = new shaka.media.PreferenceBasedCriteria(
528
+ /* language= */ '',
529
+ /* role= */ '',
530
+ /* channelCount= */ 0,
531
+ /* hdrLevel= */ '',
532
+ /* videoLayout= */ 'CH-STEREO');
362
533
  const set = builder.create(manifest.variants);
363
534
 
364
535
  checkSet(set, [
@@ -367,6 +538,38 @@ describe('AdaptationSetCriteria', () => {
367
538
  ]);
368
539
  });
369
540
 
541
+ it('chooses variants with preferred video layout (CH-MONO)', () => {
542
+ const manifest = shaka.test.ManifestGenerator.generate((manifest) => {
543
+ manifest.addVariant(1, (variant) => {
544
+ variant.addVideo(10, (stream) => {
545
+ stream.videoLayout = 'CH-STEREO';
546
+ });
547
+ });
548
+ manifest.addVariant(2, (variant) => {
549
+ variant.addVideo(20, (stream) => {
550
+ stream.videoLayout = 'CH-MONO';
551
+ });
552
+ });
553
+ manifest.addVariant(3, (variant) => {
554
+ variant.addVideo(30, (stream) => {
555
+ stream.videoLayout = 'CH-STEREO';
556
+ });
557
+ });
558
+ });
559
+
560
+ const builder = new shaka.media.PreferenceBasedCriteria(
561
+ /* language= */ '',
562
+ /* role= */ '',
563
+ /* channelCount= */ 0,
564
+ /* hdrLevel= */ '',
565
+ /* videoLayout= */ 'CH-MONO');
566
+ const set = builder.create(manifest.variants);
567
+
568
+ checkSet(set, [
569
+ manifest.variants[1],
570
+ ]);
571
+ });
572
+
370
573
  it('chooses variants with preferred audio channels count', () => {
371
574
  const manifest = shaka.test.ManifestGenerator.generate((manifest) => {
372
575
  manifest.addVariant(1, (variant) => {
@@ -386,7 +589,12 @@ describe('AdaptationSetCriteria', () => {
386
589
  });
387
590
  });
388
591
 
389
- const builder = new shaka.media.PreferenceBasedCriteria('', '', 2, '');
592
+ const builder = new shaka.media.PreferenceBasedCriteria(
593
+ /* language= */ '',
594
+ /* role= */ '',
595
+ /* channelCount= */ 2,
596
+ /* hdrLevel= */ '',
597
+ /* videoLayout= */ '');
390
598
  const set = builder.create(manifest.variants);
391
599
 
392
600
  checkSet(set, [
@@ -415,8 +623,12 @@ describe('AdaptationSetCriteria', () => {
415
623
  });
416
624
  });
417
625
 
418
- const builder =
419
- new shaka.media.PreferenceBasedCriteria('', '', 6, '');
626
+ const builder = new shaka.media.PreferenceBasedCriteria(
627
+ /* language= */ '',
628
+ /* role= */ '',
629
+ /* channelCount= */ 6,
630
+ /* hdrLevel= */ '',
631
+ /* videoLayout= */ '');
420
632
  const set = builder.create(manifest.variants);
421
633
 
422
634
  checkSet(set, [
@@ -445,7 +657,12 @@ describe('AdaptationSetCriteria', () => {
445
657
  });
446
658
  });
447
659
 
448
- const builder = new shaka.media.PreferenceBasedCriteria('', '', 2, '');
660
+ const builder = new shaka.media.PreferenceBasedCriteria(
661
+ /* language= */ '',
662
+ /* role= */ '',
663
+ /* channelCount= */ 2,
664
+ /* hdrLevel= */ '',
665
+ /* videoLayout= */ '');
449
666
  const set = builder.create(manifest.variants);
450
667
 
451
668
  checkSet(set, [
@@ -474,7 +691,12 @@ describe('AdaptationSetCriteria', () => {
474
691
  });
475
692
 
476
693
  const builder = new shaka.media.PreferenceBasedCriteria(
477
- '', '', 0, '', 'preferredLabel');
694
+ /* language= */ '',
695
+ /* role= */ '',
696
+ /* channelCount= */ 0,
697
+ /* hdrLevel= */ '',
698
+ /* videoLayout= */ '',
699
+ /* label= */ 'preferredLabel');
478
700
  const set = builder.create(manifest.variants);
479
701
 
480
702
  checkSet(set, [
@@ -516,7 +738,54 @@ describe('AdaptationSetCriteria', () => {
516
738
  });
517
739
 
518
740
  const builder = new shaka.media.PreferenceBasedCriteria(
519
- 'zh', '', 0, '', 'preferredLabel');
741
+ /* language= */ 'zh',
742
+ /* role= */ '',
743
+ /* channelCount= */ 0,
744
+ /* hdrLevel= */ '',
745
+ /* videoLayout= */ '',
746
+ /* label= */ 'preferredLabel');
747
+ const set = builder.create(manifest.variants);
748
+
749
+ checkSet(set, [
750
+ manifest.variants[0],
751
+ manifest.variants[2],
752
+ ]);
753
+ });
754
+
755
+ it('filters by audio group if enabled', () => {
756
+ const manifest = shaka.test.ManifestGenerator.generate((manifest) => {
757
+ manifest.addVariant(1, (variant) => {
758
+ variant.language = 'en';
759
+ variant.bandwidth = 300;
760
+ variant.addAudio(10, (stream) => {
761
+ stream.groupId = '1';
762
+ });
763
+ });
764
+ manifest.addVariant(2, (variant) => {
765
+ variant.language = 'en';
766
+ variant.bandwidth = 400;
767
+ variant.addAudio(11, (stream) => {
768
+ stream.groupId = '2';
769
+ });
770
+ });
771
+ manifest.addVariant(3, (variant) => {
772
+ variant.language = 'en';
773
+ variant.bandwidth = 500;
774
+ variant.addAudio(12, (stream) => {
775
+ stream.groupId = '1';
776
+ });
777
+ });
778
+ });
779
+
780
+ const builder = new shaka.media.PreferenceBasedCriteria(
781
+ /* language= */ 'en',
782
+ /* role= */ '',
783
+ /* channelCount= */ 0,
784
+ /* hdrLevel= */ '',
785
+ /* videoLayout= */ '',
786
+ /* label= */ '',
787
+ shaka.config.CodecSwitchingStrategy.RELOAD,
788
+ /* enableAudioGroups= */ true);
520
789
  const set = builder.create(manifest.variants);
521
790
 
522
791
  checkSet(set, [
@@ -221,6 +221,8 @@ describe('AdaptationSet', () => {
221
221
  type: '',
222
222
  accessibilityPurpose: null,
223
223
  external: false,
224
+ groupId: null,
225
+ fastSwitching: false,
224
226
  };
225
227
  }
226
228
  });
@@ -7,7 +7,7 @@
7
7
  describe('ContentWorkarounds', () => {
8
8
  const encryptionBoxes = {
9
9
  'encv': ['hev1', 'hvc1', 'avc1', 'avc3'],
10
- 'enca': ['ac-3', 'ec-3', 'mp4a'],
10
+ 'enca': ['ac-3', 'ec-3', 'ac-4', 'mp4a'],
11
11
  };
12
12
  for (const encryptionBox of Object.keys(encryptionBoxes)) {
13
13
  for (const box of encryptionBoxes[encryptionBox]) {
@@ -2313,7 +2313,7 @@ describe('DrmEngine', () => {
2313
2313
  videoRobustness: 'really_really_ridiculously_good',
2314
2314
  serverCertificate: serverCert,
2315
2315
  serverCertificateUri: '',
2316
- initData: ['blah'],
2316
+ initData: [{keyId: 'a'}],
2317
2317
  keyIds: new Set(['deadbeefdeadbeefdeadbeefdeadbeef']),
2318
2318
  };
2319
2319
  const drmInfoAudio = {
@@ -2324,7 +2324,7 @@ describe('DrmEngine', () => {
2324
2324
  audioRobustness: 'good',
2325
2325
  serverCertificate: undefined,
2326
2326
  serverCertificateUri: '',
2327
- initData: ['init data'],
2327
+ initData: [{keyId: 'b'}],
2328
2328
  keyIds: new Set(['eadbeefdeadbeefdeadbeefdeadbeefd']),
2329
2329
  };
2330
2330
  const drmInfoDesired = {
@@ -2336,7 +2336,7 @@ describe('DrmEngine', () => {
2336
2336
  videoRobustness: 'really_really_ridiculously_good',
2337
2337
  serverCertificate: serverCert,
2338
2338
  serverCertificateUri: '',
2339
- initData: ['blah', 'init data'],
2339
+ initData: [{keyId: 'a'}, {keyId: 'b'}],
2340
2340
  keyIds: new Set([
2341
2341
  'deadbeefdeadbeefdeadbeefdeadbeef',
2342
2342
  'eadbeefdeadbeefdeadbeefdeadbeefd',
@@ -116,7 +116,13 @@ describe('MediaSourceEngine', () => {
116
116
  mockMediaSource = createMockMediaSource();
117
117
  mockMediaSource.addSourceBuffer.and.callFake((mimeType) => {
118
118
  const type = mimeType.split('/')[0];
119
- return type == 'audio' ? audioSourceBuffer : videoSourceBuffer;
119
+ const buffer = type == 'audio' ? audioSourceBuffer : videoSourceBuffer;
120
+ // reset buffer params
121
+ buffer.timestampOffset = 0;
122
+ buffer.appendWindowEnd = Infinity;
123
+ buffer.appendWindowStart = 0;
124
+
125
+ return buffer;
120
126
  });
121
127
  mockTransmuxer = new shaka.test.FakeTransmuxer();
122
128
  shaka.transmuxer.TransmuxerEngine.findTransmuxer =
@@ -167,10 +173,12 @@ describe('MediaSourceEngine', () => {
167
173
  goog.asserts.assert(attr == 'src', 'Unexpected removeAttribute() call');
168
174
  mockVideo.src = '';
169
175
  },
176
+ addEventListener: jasmine.createSpy('addVideoEventListener'),
170
177
  load: /** @this {HTMLVideoElement} */ () => {
171
178
  // This assertion alerts us if the requirements for this mock change.
172
179
  goog.asserts.assert(mockVideo.src == '', 'Unexpected load() call');
173
180
  },
181
+ play: jasmine.createSpy('play'),
174
182
  };
175
183
  video = /** @type {HTMLMediaElement} */(mockVideo);
176
184
  mockClosedCaptionParser = new shaka.test.FakeClosedCaptionParser();
@@ -623,7 +631,7 @@ describe('MediaSourceEngine', () => {
623
631
  ContentType.TEXT, data, reference, fakeStream,
624
632
  /* hasClosedCaptions= */ false);
625
633
  expect(mockTextEngine.appendBuffer).toHaveBeenCalledWith(
626
- data, 0, 10);
634
+ data, 0, 10, 'foo://bar');
627
635
  });
628
636
 
629
637
  it('appends transmuxed data', async () => {
@@ -1136,6 +1144,161 @@ describe('MediaSourceEngine', () => {
1136
1144
  });
1137
1145
  });
1138
1146
 
1147
+ describe('reload codec switching', () => {
1148
+ beforeEach(
1149
+ /** @suppress {visibility, checkTypes} */
1150
+ () => {
1151
+ mediaSourceEngine.eventManager_.listenOnce =
1152
+ jasmine.createSpy('listener');
1153
+ mediaSourceEngine.eventManager_.listen =
1154
+ jasmine.createSpy('eventListener');
1155
+ });
1156
+ const initObject = new Map();
1157
+ initObject.set(ContentType.VIDEO, fakeVideoStream);
1158
+ initObject.set(ContentType.AUDIO, fakeAudioStream);
1159
+
1160
+ it('should re-create a new MediaSource',
1161
+ /** @suppress {visibility} */ async () => {
1162
+ await mediaSourceEngine.init(initObject, false);
1163
+ mediaSourceEngine.reset_(initObject);
1164
+ expect(createMediaSourceSpy).toHaveBeenCalled();
1165
+ });
1166
+
1167
+ it('should re-create the audio & video source buffers',
1168
+ /** @suppress {invalidCasts, visibility, checkTypes} */ async () => {
1169
+ await mediaSourceEngine.init(initObject, false);
1170
+ mediaSourceEngine.reset_(initObject);
1171
+ expect(mockMediaSource.addSourceBuffer).toHaveBeenCalledTimes(2);
1172
+ });
1173
+
1174
+ it('should persist the previous source buffer parameters',
1175
+ /** @suppress {invalidCasts, visibility, checkTypes} */async () => {
1176
+ await mediaSourceEngine.init(initObject, false);
1177
+
1178
+ audioSourceBuffer.timestampOffset = 10;
1179
+ audioSourceBuffer.appendWindowStart = 5;
1180
+ audioSourceBuffer.appendWindowEnd = 20;
1181
+
1182
+ videoSourceBuffer.timestampOffset = 20;
1183
+ videoSourceBuffer.appendWindowStart = 15;
1184
+ videoSourceBuffer.appendWindowEnd = 30;
1185
+
1186
+ mediaSourceEngine.reset_(initObject);
1187
+
1188
+ expect(audioSourceBuffer.timestampOffset).toBe(10);
1189
+ expect(audioSourceBuffer.appendWindowStart).toBe(5);
1190
+ expect(audioSourceBuffer.appendWindowEnd).toBe(20);
1191
+
1192
+ expect(videoSourceBuffer.timestampOffset).toBe(20);
1193
+ expect(videoSourceBuffer.appendWindowStart).toBe(15);
1194
+ expect(videoSourceBuffer.appendWindowEnd).toBe(30);
1195
+ });
1196
+
1197
+ it('should preserve autoplay state',
1198
+ /** @suppress {invalidCasts, visibility, checkTypes} */
1199
+ async () => {
1200
+ const originalInitSourceBuffer = mediaSourceEngine.initSourceBuffer_;
1201
+ try {
1202
+ await mediaSourceEngine.init(initObject, false);
1203
+ video.autoplay = true;
1204
+ video.paused = true;
1205
+ const playSpy = /** @type {jasmine.Spy} */ (video.play);
1206
+ const addListenOnceSpy =
1207
+ /** @type {jasmine.Spy} */
1208
+ (mediaSourceEngine.eventManager_.listenOnce);
1209
+ const addEventListenerSpy =
1210
+ /** @type {jasmine.Spy} */
1211
+ (mediaSourceEngine.eventManager_.listen);
1212
+ mediaSourceEngine.playbackHasBegun_ = true;
1213
+ mediaSourceEngine.initSourceBuffer_ =
1214
+ jasmine.createSpy('initSourceBuffer');
1215
+ const initSourceBufferSpy =
1216
+ /** @type {jasmine.Spy} */
1217
+ (mediaSourceEngine.initSourceBuffer_);
1218
+ addEventListenerSpy.and.callFake((o, e, c) => {
1219
+ c(); // audio
1220
+ c(); // video
1221
+ });
1222
+ await mediaSourceEngine.reset_(initObject);
1223
+ const callback = addListenOnceSpy.calls.argsFor(0)[2];
1224
+ callback();
1225
+ expect(initSourceBufferSpy).toHaveBeenCalled();
1226
+ expect(addListenOnceSpy.calls.argsFor(0)[1]).toBe('canplay');
1227
+ expect(video.autoplay).toBe(true);
1228
+ expect(playSpy).not.toHaveBeenCalled();
1229
+ } finally {
1230
+ mediaSourceEngine.initSourceBuffer_ = originalInitSourceBuffer;
1231
+ }
1232
+ });
1233
+
1234
+ it('should not set autoplay to false if playback has not begun',
1235
+ /** @suppress {invalidCasts, visibility, checkTypes} */
1236
+ async () => {
1237
+ const originalInitSourceBuffer = mediaSourceEngine.initSourceBuffer_;
1238
+ try {
1239
+ await mediaSourceEngine.init(initObject, false);
1240
+ video.autoplay = true;
1241
+ let setCount = 0;
1242
+ const addEventListenerSpy =
1243
+ /** @type {jasmine.Spy} */
1244
+ (mediaSourceEngine.eventManager_.listen);
1245
+ addEventListenerSpy.and.callFake((o, e, c) => {
1246
+ c(); // audio
1247
+ c(); // video
1248
+ });
1249
+ mediaSourceEngine.initSourceBuffer_ =
1250
+ jasmine.createSpy('initSourceBuffer');
1251
+ Object.defineProperty(video, 'autoplay', {
1252
+ get: () => true,
1253
+ set: () => {
1254
+ setCount++;
1255
+ },
1256
+ });
1257
+ await mediaSourceEngine.reset_(initObject);
1258
+ expect(setCount).toBe(0);
1259
+ } finally {
1260
+ mediaSourceEngine.initSourceBuffer_ = originalInitSourceBuffer;
1261
+ }
1262
+ });
1263
+
1264
+ it('should preserve playing state',
1265
+ /** @suppress {invalidCasts, visibility, checkTypes} */
1266
+ async () => {
1267
+ const originalInitSourceBuffer = mediaSourceEngine.initSourceBuffer_;
1268
+ try {
1269
+ await mediaSourceEngine.init(initObject, false);
1270
+ video.autoplay = false;
1271
+ video.paused = false;
1272
+ const playSpy = /** @type {jasmine.Spy} */ (video.play);
1273
+ const addListenOnceSpy =
1274
+ /** @type {jasmine.Spy} */
1275
+ (mediaSourceEngine.eventManager_.listenOnce);
1276
+ const addEventListenerSpy =
1277
+ /** @type {jasmine.Spy} */
1278
+ (mediaSourceEngine.eventManager_.listen);
1279
+ mediaSourceEngine.playbackHasBegun_ = true;
1280
+ mediaSourceEngine.initSourceBuffer_ =
1281
+ jasmine.createSpy('initSourceBuffer');
1282
+ const initSourceBufferSpy =
1283
+ /** @type {jasmine.Spy} */
1284
+ (mediaSourceEngine.initSourceBuffer_);
1285
+ addEventListenerSpy.and.callFake((o, e, c) => {
1286
+ c(); // audio
1287
+ c(); // video
1288
+ });
1289
+ await mediaSourceEngine.reset_(initObject);
1290
+ const callback = addListenOnceSpy.calls.argsFor(0)[2];
1291
+ callback();
1292
+ expect(initSourceBufferSpy).toHaveBeenCalled();
1293
+ expect(addListenOnceSpy.calls.argsFor(0)[1]).toBe('canplay');
1294
+ expect(video.autoplay).toBe(false);
1295
+ expect(playSpy).toHaveBeenCalled();
1296
+ } finally {
1297
+ mediaSourceEngine.initSourceBuffer_ = originalInitSourceBuffer;
1298
+ }
1299
+ });
1300
+ });
1301
+
1139
1302
  describe('destroy', () => {
1140
1303
  beforeEach(async () => {
1141
1304
  captureEvents(audioSourceBuffer, ['updateend', 'error']);
@@ -1259,6 +1422,7 @@ describe('MediaSourceEngine', () => {
1259
1422
  function createMockMediaSource() {
1260
1423
  const mediaSource = {
1261
1424
  readyState: 'open',
1425
+ sourceBuffers: document.createElement('div'),
1262
1426
  addSourceBuffer: jasmine.createSpy('addSourceBuffer'),
1263
1427
  endOfStream: jasmine.createSpy('endOfStream'),
1264
1428
  durationGetter: jasmine.createSpy('duration getter'),