shaka-player 4.3.10 → 4.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (316) hide show
  1. package/.github/workflows/release-please.yaml +7 -0
  2. package/AUTHORS +9 -0
  3. package/CHANGELOG.md +295 -200
  4. package/CONTRIBUTORS +8 -0
  5. package/LICENSE +32 -0
  6. package/README.md +63 -10
  7. package/demo/autoTemplate.txt +40 -5
  8. package/demo/close_button.js +11 -0
  9. package/demo/common/asset.js +13 -1
  10. package/demo/common/assets.js +53 -3
  11. package/demo/common/message_ids.js +25 -1
  12. package/demo/config.js +69 -10
  13. package/demo/customWarning.txt +2 -2
  14. package/demo/index.html +3 -1
  15. package/demo/locales/en.json +26 -2
  16. package/demo/locales/source.json +105 -9
  17. package/demo/main.js +52 -9
  18. package/demo/search.js +3 -1
  19. package/demo/service_worker.js +3 -0
  20. package/dist/controls.css +1 -1
  21. package/dist/controls.css.map +1 -1
  22. package/dist/demo.compiled.debug.js +124 -112
  23. package/dist/demo.compiled.debug.map +3 -3
  24. package/dist/demo.compiled.js +124 -112
  25. package/dist/demo.compiled.map +3 -3
  26. package/dist/deps.js +46 -28
  27. package/dist/locales.js +28 -28
  28. package/dist/receiver.compiled.debug.js +52 -46
  29. package/dist/receiver.compiled.debug.map +3 -3
  30. package/dist/receiver.compiled.js +52 -46
  31. package/dist/receiver.compiled.map +3 -3
  32. package/dist/shaka-player.compiled.d.ts +676 -391
  33. package/dist/shaka-player.compiled.debug.d.ts +676 -391
  34. package/dist/shaka-player.compiled.debug.externs.js +1188 -616
  35. package/dist/shaka-player.compiled.debug.js +1045 -700
  36. package/dist/shaka-player.compiled.debug.map +4 -4
  37. package/dist/shaka-player.compiled.externs.js +1188 -616
  38. package/dist/shaka-player.compiled.js +1094 -933
  39. package/dist/shaka-player.compiled.map +4 -4
  40. package/dist/shaka-player.ui.d.ts +677 -392
  41. package/dist/shaka-player.ui.debug.d.ts +677 -392
  42. package/dist/shaka-player.ui.debug.externs.js +1023 -451
  43. package/dist/shaka-player.ui.debug.js +1100 -739
  44. package/dist/shaka-player.ui.debug.map +4 -4
  45. package/dist/shaka-player.ui.externs.js +1023 -451
  46. package/dist/shaka-player.ui.js +1244 -1071
  47. package/dist/shaka-player.ui.map +4 -4
  48. package/docs/tutorials/ad_monetization.md +15 -1
  49. package/docs/tutorials/application-level-redirects.md +1 -1
  50. package/docs/tutorials/config.md +9 -5
  51. package/docs/tutorials/drm-config.md +61 -0
  52. package/docs/tutorials/fairplay.md +19 -4
  53. package/docs/tutorials/license-server-auth.md +4 -4
  54. package/docs/tutorials/license-wrapping.md +2 -2
  55. package/docs/tutorials/ui-customization.md +7 -3
  56. package/docs/tutorials/ui.md +7 -7
  57. package/docs/tutorials/upgrade.md +3 -0
  58. package/externs/ima.js +123 -2
  59. package/externs/isoboxer.js +217 -0
  60. package/externs/mediakeys.js +18 -0
  61. package/externs/mux.js +1 -105
  62. package/externs/pictureinpicture.js +48 -0
  63. package/externs/shaka/ads.js +61 -1
  64. package/externs/shaka/cea.js +110 -0
  65. package/externs/shaka/codecs.js +32 -0
  66. package/externs/shaka/manifest.js +101 -1
  67. package/externs/shaka/manifest_parser.js +0 -10
  68. package/externs/shaka/net.js +36 -7
  69. package/externs/shaka/offline.js +8 -1
  70. package/externs/shaka/player.js +220 -21
  71. package/externs/shaka/text.js +1 -382
  72. package/externs/shaka/transmuxer.js +59 -0
  73. package/karma.conf.js +13 -0
  74. package/lib/abr/simple_abr_manager.js +6 -3
  75. package/lib/ads/ad_manager.js +60 -2
  76. package/lib/ads/client_side_ad.js +86 -0
  77. package/lib/ads/client_side_ad_manager.js +79 -3
  78. package/lib/ads/server_side_ad.js +86 -0
  79. package/lib/ads/server_side_ad_manager.js +13 -0
  80. package/lib/cast/cast_utils.js +1 -0
  81. package/lib/cea/cea608_data_channel.js +24 -14
  82. package/lib/cea/cea608_memory.js +5 -2
  83. package/lib/cea/cea708_service.js +8 -9
  84. package/lib/cea/cea708_window.js +1 -2
  85. package/lib/cea/cea_decoder.js +23 -6
  86. package/lib/cea/cea_utils.js +25 -2
  87. package/lib/cea/dummy_caption_decoder.js +21 -0
  88. package/lib/cea/dummy_cea_parser.js +1 -3
  89. package/lib/cea/mp4_cea_parser.js +15 -10
  90. package/lib/cea/ts_cea_parser.js +69 -0
  91. package/lib/dash/content_protection.js +32 -2
  92. package/lib/dash/dash_parser.js +93 -31
  93. package/lib/dash/mpd_utils.js +6 -25
  94. package/lib/dash/segment_base.js +12 -14
  95. package/lib/dash/segment_list.js +10 -6
  96. package/lib/dash/segment_template.js +349 -32
  97. package/lib/dependencies/all.js +8 -0
  98. package/lib/hls/hls_classes.js +33 -8
  99. package/lib/hls/hls_parser.js +805 -191
  100. package/lib/hls/manifest_text_parser.js +4 -14
  101. package/lib/media/adaptation_set_criteria.js +43 -3
  102. package/lib/media/closed_caption_parser.js +74 -12
  103. package/lib/media/drm_engine.js +189 -49
  104. package/lib/media/gap_jumping_controller.js +1 -1
  105. package/lib/media/manifest_parser.js +34 -0
  106. package/lib/media/media_source_capabilities.js +10 -0
  107. package/lib/media/media_source_engine.js +475 -231
  108. package/lib/media/playhead.js +1 -1
  109. package/lib/media/presentation_timeline.js +62 -0
  110. package/lib/media/segment_index.js +50 -14
  111. package/lib/media/segment_prefetch.js +277 -0
  112. package/lib/media/segment_reference.js +70 -4
  113. package/lib/media/streaming_engine.js +258 -81
  114. package/lib/media/video_wrapper.js +15 -7
  115. package/lib/mss/content_protection.js +353 -0
  116. package/lib/mss/mss_parser.js +1081 -0
  117. package/lib/net/networking_engine.js +60 -10
  118. package/lib/offline/indexeddb/v1_storage_cell.js +1 -0
  119. package/lib/offline/indexeddb/v2_storage_cell.js +1 -0
  120. package/lib/offline/manifest_converter.js +6 -0
  121. package/lib/offline/storage.js +2 -4
  122. package/lib/player.js +309 -105
  123. package/lib/polyfill/media_capabilities.js +43 -3
  124. package/lib/polyfill/patchedmediakeys_apple.js +5 -0
  125. package/lib/polyfill/patchedmediakeys_nop.js +5 -0
  126. package/lib/polyfill/patchedmediakeys_webkit.js +5 -0
  127. package/lib/text/cue.js +208 -130
  128. package/lib/text/lrc_text_parser.js +2 -2
  129. package/lib/text/mp4_vtt_parser.js +2 -2
  130. package/lib/text/sbv_text_parser.js +1 -1
  131. package/lib/text/simple_text_displayer.js +2 -1
  132. package/lib/text/srt_text_parser.js +9 -1
  133. package/lib/text/ssa_text_parser.js +2 -2
  134. package/lib/text/text_engine.js +3 -21
  135. package/lib/text/text_utils.js +71 -20
  136. package/lib/text/ui_text_displayer.js +14 -14
  137. package/lib/text/vtt_text_parser.js +22 -14
  138. package/lib/transmuxer/aac_transmuxer.js +224 -0
  139. package/lib/transmuxer/ac3.js +142 -0
  140. package/lib/transmuxer/ac3_transmuxer.js +212 -0
  141. package/lib/transmuxer/adts.js +232 -0
  142. package/lib/transmuxer/ec3.js +103 -0
  143. package/lib/transmuxer/ec3_transmuxer.js +212 -0
  144. package/lib/transmuxer/h264.js +324 -0
  145. package/lib/transmuxer/mp3_transmuxer.js +204 -0
  146. package/lib/transmuxer/mpeg_audio.js +196 -0
  147. package/lib/transmuxer/mpeg_ts_transmuxer.js +182 -0
  148. package/lib/transmuxer/mss_transmuxer.js +389 -0
  149. package/lib/{media/transmuxer.js → transmuxer/muxjs_transmuxer.js} +34 -80
  150. package/lib/transmuxer/transmuxer_engine.js +161 -0
  151. package/lib/transmuxer/ts_transmuxer.js +738 -0
  152. package/lib/util/cmcd_manager.js +131 -71
  153. package/lib/util/error.js +62 -26
  154. package/lib/util/exp_golomb.js +220 -0
  155. package/lib/util/fairplay_utils.js +64 -13
  156. package/lib/util/id3_utils.js +56 -3
  157. package/lib/util/language_utils.js +11 -8
  158. package/lib/util/manifest_parser_utils.js +3 -0
  159. package/lib/util/mime_utils.js +44 -7
  160. package/lib/util/mp4_box_parsers.js +233 -5
  161. package/lib/util/mp4_generator.js +1381 -0
  162. package/lib/util/periods.js +40 -34
  163. package/lib/util/platform.js +17 -3
  164. package/lib/util/player_configuration.js +38 -1
  165. package/lib/util/stream_utils.js +138 -27
  166. package/lib/util/text_parser.js +1 -1
  167. package/lib/util/ts_parser.js +470 -47
  168. package/lib/util/xml_utils.js +16 -3
  169. package/maintained-branches.md +2 -2
  170. package/package.json +3 -1
  171. package/roadmap.md +21 -6
  172. package/shaka-player.uncompiled.js +18 -0
  173. package/test/abr/simple_abr_manager_unit.js +32 -4
  174. package/test/ads/ad_manager_unit.js +18 -2
  175. package/test/cast/cast_utils_unit.js +7 -2
  176. package/test/cea/cea608_memory_unit.js +24 -0
  177. package/test/cea/cea_decoder_unit.js +33 -0
  178. package/test/dash/dash_parser_content_protection_unit.js +93 -3
  179. package/test/dash/dash_parser_live_unit.js +56 -13
  180. package/test/dash/dash_parser_manifest_unit.js +70 -2
  181. package/test/dash/dash_parser_segment_base_unit.js +14 -9
  182. package/test/dash/dash_parser_segment_template_unit.js +286 -6
  183. package/test/demo/demo_unit.js +5 -1
  184. package/test/hls/hls_live_unit.js +188 -28
  185. package/test/hls/hls_parser_unit.js +250 -109
  186. package/test/hls/manifest_text_parser_unit.js +18 -13
  187. package/test/media/adaptation_set_criteria_unit.js +46 -14
  188. package/test/media/adaptation_set_unit.js +2 -0
  189. package/test/media/drm_engine_integration.js +11 -8
  190. package/test/media/drm_engine_unit.js +220 -7
  191. package/test/media/media_source_engine_integration.js +122 -38
  192. package/test/media/media_source_engine_unit.js +133 -153
  193. package/test/media/playhead_unit.js +3 -0
  194. package/test/media/presentation_timeline_unit.js +51 -0
  195. package/test/media/segment_index_unit.js +52 -0
  196. package/test/media/segment_prefetch_unit.js +243 -0
  197. package/test/media/streaming_engine_integration.js +9 -3
  198. package/test/media/streaming_engine_unit.js +440 -59
  199. package/test/mss/mss_parser_content_protection_unit.js +49 -0
  200. package/test/mss/mss_parser_unit.js +472 -0
  201. package/test/mss/mss_player_integration.js +123 -0
  202. package/test/offline/manifest_convert_unit.js +9 -0
  203. package/test/offline/storage_integration.js +6 -1
  204. package/test/player_unit.js +386 -133
  205. package/test/polyfill/media_capabilities_unit.js +18 -0
  206. package/test/test/assets/hls-raw-aac/fileSequence0.aac +0 -0
  207. package/test/test/assets/hls-raw-aac/fileSequence1.aac +0 -0
  208. package/test/test/assets/hls-raw-aac/fileSequence2.aac +0 -0
  209. package/test/test/assets/hls-raw-aac/fileSequence3.aac +0 -0
  210. package/test/test/assets/hls-raw-aac/manifest.m3u8 +4 -0
  211. package/test/test/assets/hls-raw-aac/stream.m4a.m3u8 +14 -0
  212. package/test/test/assets/hls-raw-ac3/fileSequence0.ac3 +0 -0
  213. package/test/test/assets/hls-raw-ac3/fileSequence1.ac3 +0 -0
  214. package/test/test/assets/hls-raw-ac3/fileSequence2.ac3 +0 -0
  215. package/test/test/assets/hls-raw-ac3/fileSequence3.ac3 +0 -0
  216. package/test/test/assets/hls-raw-ac3/fileSequence4.ac3 +0 -0
  217. package/test/test/assets/hls-raw-ac3/fileSequence5.ac3 +0 -0
  218. package/test/test/assets/hls-raw-ac3/fileSequence6.ac3 +0 -0
  219. package/test/test/assets/hls-raw-ac3/fileSequence7.ac3 +0 -0
  220. package/test/test/assets/hls-raw-ac3/prog_index.m3u8 +31 -0
  221. package/test/test/assets/hls-raw-ec3/fileSequence0.ec3 +0 -0
  222. package/test/test/assets/hls-raw-ec3/fileSequence1.ec3 +0 -0
  223. package/test/test/assets/hls-raw-ec3/fileSequence2.ec3 +0 -0
  224. package/test/test/assets/hls-raw-ec3/fileSequence3.ec3 +0 -0
  225. package/test/test/assets/hls-raw-ec3/fileSequence4.ec3 +0 -0
  226. package/test/test/assets/hls-raw-ec3/fileSequence5.ec3 +0 -0
  227. package/test/test/assets/hls-raw-ec3/fileSequence6.ec3 +0 -0
  228. package/test/test/assets/hls-raw-ec3/fileSequence7.ec3 +0 -0
  229. package/test/test/assets/hls-raw-ec3/prog_index.m3u8 +31 -0
  230. package/test/test/assets/hls-raw-mp3/chunklist_w905519204.m3u8 +13 -0
  231. package/test/test/assets/hls-raw-mp3/media_w905519204_0.mp3 +0 -0
  232. package/test/test/assets/hls-raw-mp3/media_w905519204_1.mp3 +0 -0
  233. package/test/test/assets/hls-raw-mp3/media_w905519204_2.mp3 +0 -0
  234. package/test/test/assets/hls-raw-mp3/media_w905519204_3.mp3 +0 -0
  235. package/test/test/assets/hls-raw-mp3/playlist.m3u8 +4 -0
  236. package/test/test/assets/hls-ts-aac/playlist.m3u8 +23 -0
  237. package/test/test/assets/hls-ts-aac/segment_0.ts +0 -0
  238. package/test/test/assets/hls-ts-aac/segment_1.ts +0 -0
  239. package/test/test/assets/hls-ts-aac/segment_2.ts +0 -0
  240. package/test/test/assets/hls-ts-aac/segment_3.ts +0 -0
  241. package/test/test/assets/hls-ts-aac/segment_4.ts +0 -0
  242. package/test/test/assets/hls-ts-aac/segment_5.ts +0 -0
  243. package/test/test/assets/hls-ts-aac/segment_6.ts +0 -0
  244. package/test/test/assets/hls-ts-aac/segment_7.ts +0 -0
  245. package/test/test/assets/hls-ts-ac3/ChID_voices_6ch_640kbps_dd_sub_0000.ts +0 -0
  246. package/test/test/assets/hls-ts-ac3/ChID_voices_6ch_640kbps_dd_sub_0001.ts +0 -0
  247. package/test/test/assets/hls-ts-ac3/ChID_voices_6ch_640kbps_dd_sub_0002.ts +0 -0
  248. package/test/test/assets/hls-ts-ac3/ChID_voices_6ch_640kbps_dd_sub_0003.ts +0 -0
  249. package/test/test/assets/hls-ts-ac3/ChID_voices_6ch_640kbps_dd_sub_0004.ts +0 -0
  250. package/test/test/assets/hls-ts-ac3/ChID_voices_6ch_640kbps_dd_sub_0005.ts +0 -0
  251. package/test/test/assets/hls-ts-ac3/prog_index.m3u8 +19 -0
  252. package/test/test/assets/hls-ts-ec3/ChID_voices_6ch_256kbps_ddp_sub_0000.ts +0 -0
  253. package/test/test/assets/hls-ts-ec3/ChID_voices_6ch_256kbps_ddp_sub_0001.ts +0 -0
  254. package/test/test/assets/hls-ts-ec3/ChID_voices_6ch_256kbps_ddp_sub_0002.ts +0 -0
  255. package/test/test/assets/hls-ts-ec3/ChID_voices_6ch_256kbps_ddp_sub_0003.ts +0 -0
  256. package/test/test/assets/hls-ts-ec3/ChID_voices_6ch_256kbps_ddp_sub_0004.ts +0 -0
  257. package/test/test/assets/hls-ts-ec3/ChID_voices_6ch_256kbps_ddp_sub_0005.ts +0 -0
  258. package/test/test/assets/hls-ts-ec3/prog_index.m3u8 +19 -0
  259. package/test/test/assets/hls-ts-h264/fileSequence0.ts +0 -0
  260. package/test/test/assets/hls-ts-h264/fileSequence1.ts +0 -0
  261. package/test/test/assets/hls-ts-h264/fileSequence2.ts +0 -0
  262. package/test/test/assets/hls-ts-h264/fileSequence3.ts +0 -0
  263. package/test/test/assets/hls-ts-h264/fileSequence4.ts +0 -0
  264. package/test/test/assets/hls-ts-h264/fileSequence5.ts +0 -0
  265. package/test/test/assets/hls-ts-h264/prog_index.m3u8 +25 -0
  266. package/test/test/assets/hls-ts-mp3/manifest.m3u8 +13 -0
  267. package/test/test/assets/hls-ts-mp3/media_w239891843_0.ts +0 -0
  268. package/test/test/assets/hls-ts-mp3/media_w239891843_1.ts +0 -0
  269. package/test/test/assets/hls-ts-mp3/media_w239891843_2.ts +0 -0
  270. package/test/test/assets/hls-ts-mp3/media_w239891843_3.ts +0 -0
  271. package/test/test/assets/hls-ts-muxed-aac-h264/chunk.m3u8 +17 -0
  272. package/test/test/assets/hls-ts-muxed-aac-h264/n_0_0_0.ts +0 -0
  273. package/test/test/assets/hls-ts-muxed-aac-h264/n_1_0_0.ts +0 -0
  274. package/test/test/assets/hls-ts-muxed-aac-h264/n_2_0_0.ts +0 -0
  275. package/test/test/assets/hls-ts-muxed-aac-h264/n_3_0_0.ts +0 -0
  276. package/test/test/assets/hls-ts-muxed-aac-h264/n_4_0_0.ts +0 -0
  277. package/test/test/assets/hls-ts-muxed-aac-h264/n_5_0_0.ts +0 -0
  278. package/test/test/assets/hls-ts-muxed-aac-h264/playlist.m3u8 +4 -0
  279. package/test/test/boot.js +7 -2
  280. package/test/test/util/fake_ad.js +99 -0
  281. package/test/test/util/fake_ad_manager.js +7 -1
  282. package/test/test/util/fake_networking_engine.js +41 -14
  283. package/test/test/util/fake_segment_prefetch.js +81 -0
  284. package/test/test/util/manifest_generator.js +19 -5
  285. package/test/test/util/mss_parser_util.js +115 -0
  286. package/test/test/util/offline_utils.js +1 -0
  287. package/test/test/util/simple_fakes.js +12 -3
  288. package/test/test/util/streaming_engine_util.js +14 -4
  289. package/test/test/util/ttml_utils.js +2 -2
  290. package/test/text/cue_integration.js +1 -1
  291. package/test/text/srt_text_parser_unit.js +78 -0
  292. package/test/text/text_engine_unit.js +17 -8
  293. package/test/text/ui_text_displayer_unit.js +4 -4
  294. package/test/text/vtt_text_parser_unit.js +59 -40
  295. package/test/text/web_vtt_generator_unit.js +9 -3
  296. package/test/{media/transmuxer_integration.js → transmuxer/muxjs_transmuxer_integration.js} +30 -33
  297. package/test/transmuxer/transmuxer_engine_integration.js +60 -0
  298. package/test/transmuxer/transmuxer_integration.js +296 -0
  299. package/test/util/cmcd_manager_unit.js +162 -13
  300. package/test/util/language_utils_unit.js +9 -2
  301. package/test/util/mp4_box_parsers_unit.js +39 -4
  302. package/test/util/periods_unit.js +34 -26
  303. package/test/util/stream_utils_unit.js +60 -107
  304. package/ui/controls.js +19 -1
  305. package/ui/controls.less +1 -0
  306. package/ui/externs/ui.js +11 -1
  307. package/ui/language_utils.js +15 -0
  308. package/ui/less/thumbnails.less +26 -0
  309. package/ui/locales/oc.json +42 -0
  310. package/ui/pip_button.js +83 -2
  311. package/ui/range_element.js +15 -0
  312. package/ui/resolution_selection.js +9 -2
  313. package/ui/seek_bar.js +223 -0
  314. package/ui/ui.js +5 -0
  315. package/lib/cea/i_caption_decoder.js +0 -51
  316. package/lib/cea/i_cea_parser.js +0 -68
@@ -76,6 +76,8 @@ describe('StreamingEngine', () => {
76
76
  let beforeAppendSegment;
77
77
  /** @type {!jasmine.Spy} */
78
78
  let onMetadata;
79
+ /** @type {!jasmine.Spy} */
80
+ let disableStream;
79
81
 
80
82
  /** @type {function(function(), number)} */
81
83
  let realSetTimeout;
@@ -437,6 +439,8 @@ describe('StreamingEngine', () => {
437
439
  onMetadata = jasmine.createSpy('onMetadata');
438
440
  getBandwidthEstimate = jasmine.createSpy('getBandwidthEstimate');
439
441
  getBandwidthEstimate.and.returnValue(1e3);
442
+ disableStream = jasmine.createSpy('disableStream');
443
+ disableStream.and.callFake(() => false);
440
444
 
441
445
  beforeAppendSegment.and.callFake((segment) => {
442
446
  return Promise.resolve();
@@ -447,13 +451,13 @@ describe('StreamingEngine', () => {
447
451
  config.rebufferingGoal = 2;
448
452
  config.bufferingGoal = 5;
449
453
  config.bufferBehind = Infinity;
454
+ config.maxDisabledTime = 0; // Do not disable stream by default
450
455
  }
451
456
 
452
457
  goog.asserts.assert(
453
458
  presentationTimeInSeconds != undefined,
454
459
  'All tests should have defined an initial presentation time by now!');
455
460
  const playerInterface = {
456
- modifySegmentRequest: (request, segmentInfo) => {},
457
461
  getPresentationTime: () => presentationTimeInSeconds,
458
462
  getBandwidthEstimate: Util.spyFunc(getBandwidthEstimate),
459
463
  mediaSourceEngine: mediaSourceEngine,
@@ -465,6 +469,7 @@ describe('StreamingEngine', () => {
465
469
  onInitSegmentAppended: () => {},
466
470
  beforeAppendSegment: Util.spyFunc(beforeAppendSegment),
467
471
  onMetadata: Util.spyFunc(onMetadata),
472
+ disableStream: Util.spyFunc(disableStream),
468
473
  };
469
474
  streamingEngine = new shaka.media.StreamingEngine(
470
475
  /** @type {shaka.extern.Manifest} */(manifest), playerInterface);
@@ -521,7 +526,8 @@ describe('StreamingEngine', () => {
521
526
  expectedMseInit.set(ContentType.TEXT, textStream);
522
527
 
523
528
  expect(mediaSourceEngine.init).toHaveBeenCalledWith(expectedMseInit,
524
- /** forceTransmux= */ false, /** sequenceMode= */ false);
529
+ /** sequenceMode= */ false, /** manifestType= */ 'UNKNOWN',
530
+ /** ignoreManifestTimestampsInSegmentsMode= */ false);
525
531
  expect(mediaSourceEngine.init).toHaveBeenCalledTimes(1);
526
532
 
527
533
  expect(mediaSourceEngine.setDuration).toHaveBeenCalledTimes(1);
@@ -546,40 +552,47 @@ describe('StreamingEngine', () => {
546
552
  netEngine.expectRangeRequest(
547
553
  '0_audio_init',
548
554
  initSegmentRanges[ContentType.AUDIO][0],
549
- initSegmentRanges[ContentType.AUDIO][1]);
555
+ initSegmentRanges[ContentType.AUDIO][1],
556
+ /* isInit= */ true);
550
557
 
551
558
  netEngine.expectRangeRequest(
552
559
  '0_video_init',
553
560
  initSegmentRanges[ContentType.VIDEO][0],
554
- initSegmentRanges[ContentType.VIDEO][1]);
561
+ initSegmentRanges[ContentType.VIDEO][1],
562
+ /* isInit= */ true);
555
563
 
556
564
  netEngine.expectRangeRequest(
557
565
  '1_audio_init',
558
566
  initSegmentRanges[ContentType.AUDIO][0],
559
- initSegmentRanges[ContentType.AUDIO][1]);
567
+ initSegmentRanges[ContentType.AUDIO][1],
568
+ /* isInit= */ true);
560
569
 
561
570
  netEngine.expectRangeRequest(
562
571
  '1_video_init',
563
572
  initSegmentRanges[ContentType.VIDEO][0],
564
- initSegmentRanges[ContentType.VIDEO][1]);
573
+ initSegmentRanges[ContentType.VIDEO][1],
574
+ /* isInit= */ true);
565
575
 
566
576
  const segmentType = shaka.net.NetworkingEngine.RequestType.SEGMENT;
577
+ const segmentContext = {
578
+ type: shaka.net.NetworkingEngine.AdvancedRequestType.MEDIA_SEGMENT,
579
+ };
567
580
 
568
- netEngine.expectRequest('0_audio_0', segmentType);
569
- netEngine.expectRequest('0_video_0', segmentType);
570
- netEngine.expectRequest('0_text_0', segmentType);
581
+ netEngine.expectRequest('0_audio_0', segmentType, segmentContext);
582
+ netEngine.expectRequest('0_video_0', segmentType, segmentContext);
583
+ netEngine.expectRequest('0_text_0', segmentType, segmentContext);
571
584
 
572
- netEngine.expectRequest('0_audio_1', segmentType);
573
- netEngine.expectRequest('0_video_1', segmentType);
574
- netEngine.expectRequest('0_text_1', segmentType);
585
+ netEngine.expectRequest('0_audio_1', segmentType, segmentContext);
586
+ netEngine.expectRequest('0_video_1', segmentType, segmentContext);
587
+ netEngine.expectRequest('0_text_1', segmentType, segmentContext);
575
588
 
576
- netEngine.expectRequest('1_audio_2', segmentType);
577
- netEngine.expectRequest('1_video_2', segmentType);
578
- netEngine.expectRequest('1_text_2', segmentType);
589
+ netEngine.expectRequest('1_audio_2', segmentType, segmentContext);
590
+ netEngine.expectRequest('1_video_2', segmentType, segmentContext);
591
+ netEngine.expectRequest('1_text_2', segmentType, segmentContext);
579
592
 
580
- netEngine.expectRequest('1_audio_3', segmentType);
581
- netEngine.expectRequest('1_video_3', segmentType);
582
- netEngine.expectRequest('1_text_3', segmentType);
593
+ netEngine.expectRequest('1_audio_3', segmentType, segmentContext);
594
+ netEngine.expectRequest('1_video_3', segmentType, segmentContext);
595
+ netEngine.expectRequest('1_text_3', segmentType, segmentContext);
583
596
  });
584
597
 
585
598
  describe('unloadTextStream', () => {
@@ -597,16 +610,19 @@ describe('StreamingEngine', () => {
597
610
  // is sent.
598
611
  await runTest(() => {
599
612
  const segmentType = shaka.net.NetworkingEngine.RequestType.SEGMENT;
613
+ const segmentContext = {
614
+ type: shaka.net.NetworkingEngine.AdvancedRequestType.MEDIA_SEGMENT,
615
+ };
600
616
 
601
617
  if (presentationTimeInSeconds == 1) {
602
- netEngine.expectRequest('0_text_0', segmentType);
618
+ netEngine.expectRequest('0_text_0', segmentType, segmentContext);
603
619
  netEngine.request.calls.reset();
604
620
  streamingEngine.unloadTextStream();
605
621
  } else if (presentationTimeInSeconds == 35) {
606
- netEngine.expectNoRequest('0_text_0', segmentType);
607
- netEngine.expectNoRequest('0_text_1', segmentType);
608
- netEngine.expectNoRequest('1_text_2', segmentType);
609
- netEngine.expectNoRequest('1_text_3', segmentType);
622
+ netEngine.expectNoRequest('0_text_0', segmentType, segmentContext);
623
+ netEngine.expectNoRequest('0_text_1', segmentType, segmentContext);
624
+ netEngine.expectNoRequest('1_text_2', segmentType, segmentContext);
625
+ netEngine.expectNoRequest('1_text_3', segmentType, segmentContext);
610
626
  }
611
627
  });
612
628
  });
@@ -825,8 +841,13 @@ describe('StreamingEngine', () => {
825
841
  const gt40 = {
826
842
  asymmetricMatch: (val) => val > 40 && val <= 40.1,
827
843
  };
844
+ const streamsByType = new Map();
845
+ streamsByType.set(ContentType.AUDIO, audioStream);
846
+ streamsByType.set(ContentType.VIDEO, videoStream);
847
+
828
848
  expect(mediaSourceEngine.setStreamProperties)
829
- .toHaveBeenCalledWith('video', 0, lt20, gt40, false);
849
+ .toHaveBeenCalledWith('video', 0, lt20, gt40, false,
850
+ videoStream, streamsByType);
830
851
  });
831
852
 
832
853
  // Regression test for https://github.com/shaka-project/shaka-player/issues/3717
@@ -858,22 +879,24 @@ describe('StreamingEngine', () => {
858
879
  expect(mediaSourceEngine.endOfStream).toHaveBeenCalled();
859
880
 
860
881
  const segmentType = shaka.net.NetworkingEngine.RequestType.SEGMENT;
882
+ const segmentContext = {
883
+ type: shaka.net.NetworkingEngine.AdvancedRequestType.MEDIA_SEGMENT,
884
+ };
885
+ netEngine.expectRequest('0_audio_0', segmentType, segmentContext);
886
+ netEngine.expectRequest('0_video_0', segmentType, segmentContext);
887
+ netEngine.expectRequest('0_text_0', segmentType, segmentContext);
861
888
 
862
- netEngine.expectRequest('0_audio_0', segmentType);
863
- netEngine.expectRequest('0_video_0', segmentType);
864
- netEngine.expectRequest('0_text_0', segmentType);
865
-
866
- netEngine.expectRequest('0_audio_1', segmentType);
867
- netEngine.expectRequest('0_video_1', segmentType);
868
- netEngine.expectRequest('0_text_1', segmentType);
889
+ netEngine.expectRequest('0_audio_1', segmentType, segmentContext);
890
+ netEngine.expectRequest('0_video_1', segmentType, segmentContext);
891
+ netEngine.expectRequest('0_text_1', segmentType, segmentContext);
869
892
 
870
- netEngine.expectRequest('1_audio_2', segmentType);
871
- netEngine.expectRequest('1_video_2', segmentType);
872
- netEngine.expectRequest('1_text_2', segmentType);
893
+ netEngine.expectRequest('1_audio_2', segmentType, segmentContext);
894
+ netEngine.expectRequest('1_video_2', segmentType, segmentContext);
895
+ netEngine.expectRequest('1_text_2', segmentType, segmentContext);
873
896
 
874
- netEngine.expectNoRequest('1_audio_3', segmentType);
875
- netEngine.expectNoRequest('1_video_3', segmentType);
876
- netEngine.expectNoRequest('1_text_3', segmentType);
897
+ netEngine.expectNoRequest('1_audio_3', segmentType, segmentContext);
898
+ netEngine.expectNoRequest('1_video_3', segmentType, segmentContext);
899
+ netEngine.expectNoRequest('1_text_3', segmentType, segmentContext);
877
900
  });
878
901
 
879
902
  it('does not buffer one media type ahead of another', async () => {
@@ -1148,10 +1171,15 @@ describe('StreamingEngine', () => {
1148
1171
  await Util.fakeEventLoop(5);
1149
1172
 
1150
1173
  const segmentType = shaka.net.NetworkingEngine.RequestType.SEGMENT;
1174
+ const segmentContext = {
1175
+ type: shaka.net.NetworkingEngine.AdvancedRequestType.INIT_SEGMENT,
1176
+ };
1177
+
1151
1178
  // Quickly switching back to text1, and text init segment should be
1152
1179
  // fetched again.
1153
- netEngine.expectRequest('text-20-init', segmentType);
1154
- netEngine.expectNoRequest('text-21-init', segmentType);
1180
+ netEngine.expectRequest('text-20-init', segmentType, segmentContext);
1181
+ netEngine.expectNoRequest('text-21-init', segmentType, segmentContext);
1182
+ // TODO: huh?
1155
1183
  });
1156
1184
  });
1157
1185
 
@@ -2158,6 +2186,195 @@ describe('StreamingEngine', () => {
2158
2186
  // baseDelay == 10000, maybe be longer due to delays in the event loop.
2159
2187
  expect(callbackTime - startTime).toBeGreaterThanOrEqual(10000);
2160
2188
  });
2189
+
2190
+ it('temporarily disables stream if configured to do so', async () => {
2191
+ setupVod();
2192
+
2193
+ const targetUri = '0_video_0';
2194
+
2195
+ failRequestsForTarget(netEngine, targetUri);
2196
+
2197
+ mediaSourceEngine = new shaka.test.FakeMediaSourceEngine(segmentData);
2198
+ const config = shaka.util.PlayerConfiguration.createDefault().streaming;
2199
+ config.maxDisabledTime = 2;
2200
+ createStreamingEngine(config);
2201
+
2202
+ spyOn(streamingEngine, 'makeAbortDecision_').and.callFake(() => {
2203
+ return Promise.resolve();
2204
+ });
2205
+
2206
+ onError.and.callFake((error) => {
2207
+ expect(error.severity).toBe(shaka.util.Error.Severity.RECOVERABLE);
2208
+ expect(error.category).toBe(shaka.util.Error.Category.NETWORK);
2209
+ expect(error.code).toBe(shaka.util.Error.Code.BAD_HTTP_STATUS);
2210
+ });
2211
+
2212
+ disableStream.and.callFake((stream, time) => {
2213
+ expect(stream).toBe(variant.video);
2214
+ expect(time).toBeGreaterThan(0);
2215
+
2216
+ createAlternateSegmentIndex(stream, alternateVideoStream);
2217
+
2218
+ streamingEngine.switchVariant(
2219
+ alternateVariant, /* clearBuffer= */ true,
2220
+ /* safeMargin= */ 0, /* force= */ true);
2221
+ return true;
2222
+ });
2223
+
2224
+ // Here we go!
2225
+ streamingEngine.switchVariant(variant);
2226
+ streamingEngine.switchTextStream(textStream);
2227
+ await streamingEngine.start();
2228
+ playing = true;
2229
+
2230
+ await runTest();
2231
+ expect(disableStream).toHaveBeenCalledTimes(1);
2232
+ });
2233
+
2234
+ it('does not temporarily disables stream if not configured to',
2235
+ async () => {
2236
+ setupVod();
2237
+
2238
+ const targetUri = '0_audio_init';
2239
+ failRequestsForTarget(
2240
+ netEngine, targetUri, shaka.util.Error.Code.HTTP_ERROR);
2241
+
2242
+ mediaSourceEngine = new shaka.test.FakeMediaSourceEngine(segmentData);
2243
+ const config =
2244
+ shaka.util.PlayerConfiguration.createDefault().streaming;
2245
+ config.maxDisabledTime = 0; // Do not disable streams.
2246
+ createStreamingEngine(config);
2247
+
2248
+ onError.and.callFake((error) => {
2249
+ expect(error.code).toBe(shaka.util.Error.Code.HTTP_ERROR);
2250
+ });
2251
+
2252
+ // Here we go!
2253
+ streamingEngine.switchVariant(variant);
2254
+ streamingEngine.switchTextStream(textStream);
2255
+ await streamingEngine.start();
2256
+ playing = true;
2257
+
2258
+ await runTest();
2259
+ expect(disableStream).not.toHaveBeenCalled();
2260
+ });
2261
+
2262
+ it('always tries to recover shaka.util.Error.Code.SEGMENT_MISSING',
2263
+ async () => {
2264
+ setupVod();
2265
+
2266
+ const targetUri = '0_video_0';
2267
+ failRequestsForTarget(
2268
+ netEngine, targetUri, shaka.util.Error.Code.SEGMENT_MISSING);
2269
+
2270
+ mediaSourceEngine = new shaka.test.FakeMediaSourceEngine(segmentData);
2271
+ const config =
2272
+ shaka.util.PlayerConfiguration.createDefault().streaming;
2273
+ config.maxDisabledTime = 0; // Do not disable streams.
2274
+ createStreamingEngine(config);
2275
+
2276
+ spyOn(streamingEngine, 'makeAbortDecision_').and.callFake(() => {
2277
+ return Promise.resolve();
2278
+ });
2279
+
2280
+ onError.and.callFake((error) => {
2281
+ expect(error.severity).toBe(shaka.util.Error.Severity.RECOVERABLE);
2282
+ expect(error.category).toBe(shaka.util.Error.Category.NETWORK);
2283
+ expect(error.code).toBe(shaka.util.Error.Code.SEGMENT_MISSING);
2284
+ });
2285
+
2286
+ disableStream.and.callFake((stream, time) => {
2287
+ expect(stream).toBe(variant.video);
2288
+ expect(time).toBeGreaterThan(0);
2289
+
2290
+ createAlternateSegmentIndex(stream, alternateVideoStream);
2291
+
2292
+ streamingEngine.switchVariant(
2293
+ alternateVariant, /* clearBuffer= */ true,
2294
+ /* safeMargin= */ 0, /* force= */ true);
2295
+ return true;
2296
+ });
2297
+
2298
+ // Here we go!
2299
+ streamingEngine.switchVariant(variant);
2300
+ streamingEngine.switchTextStream(textStream);
2301
+ await streamingEngine.start();
2302
+ playing = true;
2303
+
2304
+ await runTest();
2305
+ expect(disableStream).toHaveBeenCalledTimes(1);
2306
+ });
2307
+
2308
+ it('throws recoverable error if try to disable stream succeeded',
2309
+ async () => {
2310
+ setupVod();
2311
+
2312
+ const targetUri = '0_video_init';
2313
+ failRequestsForTarget(
2314
+ netEngine, targetUri, shaka.util.Error.Code.BAD_HTTP_STATUS);
2315
+
2316
+ mediaSourceEngine = new shaka.test.FakeMediaSourceEngine(segmentData);
2317
+ const config =
2318
+ shaka.util.PlayerConfiguration.createDefault().streaming;
2319
+ config.maxDisabledTime = 2;
2320
+ createStreamingEngine(config);
2321
+
2322
+ disableStream.and.callFake(() => true);
2323
+
2324
+ onError.and.callFake((error) => {
2325
+ expect(error).toEqual(jasmine.objectContaining({
2326
+ code: shaka.util.Error.Code.BAD_HTTP_STATUS,
2327
+ category: shaka.util.Error.Category.NETWORK,
2328
+ severity: shaka.util.Error.Severity.RECOVERABLE,
2329
+ handled: true,
2330
+ }));
2331
+ });
2332
+
2333
+ // Here we go!
2334
+ streamingEngine.switchVariant(variant);
2335
+ streamingEngine.switchTextStream(textStream);
2336
+ await streamingEngine.start();
2337
+ playing = true;
2338
+
2339
+ await runTest();
2340
+ expect(disableStream).toHaveBeenCalled();
2341
+ expect(onError).toHaveBeenCalled();
2342
+ });
2343
+
2344
+ it('throws critical error if try to disable stream failed', async () => {
2345
+ setupVod();
2346
+
2347
+ const targetUri = '0_video_init';
2348
+ failRequestsForTarget(
2349
+ netEngine, targetUri, shaka.util.Error.Code.BAD_HTTP_STATUS);
2350
+
2351
+ mediaSourceEngine = new shaka.test.FakeMediaSourceEngine(segmentData);
2352
+ const config = shaka.util.PlayerConfiguration.createDefault().streaming;
2353
+ config.maxDisabledTime = 2;
2354
+ createStreamingEngine(config);
2355
+
2356
+ disableStream.and.callFake(() => false);
2357
+
2358
+ onError.and.callFake((error) => {
2359
+ expect(error).toEqual(jasmine.objectContaining({
2360
+ code: shaka.util.Error.Code.BAD_HTTP_STATUS,
2361
+ category: shaka.util.Error.Category.NETWORK,
2362
+ severity: shaka.util.Error.Severity.CRITICAL,
2363
+ handled: false,
2364
+ }));
2365
+ });
2366
+
2367
+ // Here we go!
2368
+ streamingEngine.switchVariant(variant);
2369
+ streamingEngine.switchTextStream(textStream);
2370
+ await streamingEngine.start();
2371
+ playing = true;
2372
+
2373
+ await runTest();
2374
+
2375
+ expect(disableStream).toHaveBeenCalled();
2376
+ expect(onError).toHaveBeenCalled();
2377
+ });
2161
2378
  });
2162
2379
 
2163
2380
  describe('retry()', () => {
@@ -3033,22 +3250,6 @@ describe('StreamingEngine', () => {
3033
3250
  expectedStartDate.toUTCString());
3034
3251
  });
3035
3252
 
3036
- it('does not raise event if mediatime exceeds Number.MAX_VALUE',
3037
- async () => {
3038
- const prftSegment = Uint8ArrayUtils.fromHex(
3039
- '00000020707266740100000000000001E683B62E8E63CC58'+
3040
- 'FFFFFFFFFFFFFFFF');
3041
- segmentData[ContentType.VIDEO].segments[0] = prftSegment;
3042
- segmentData[ContentType.VIDEO].initSegments[0] = mdhdSegment;
3043
-
3044
- streamingEngine.switchVariant(variant);
3045
- streamingEngine.switchTextStream(textStream);
3046
- await streamingEngine.start();
3047
- playing = true;
3048
- await runTest();
3049
- expect(onEvent).not.toHaveBeenCalled();
3050
- });
3051
-
3052
3253
  it('raises an event once only', async () => {
3053
3254
  segmentData[ContentType.VIDEO].segments[0] =
3054
3255
  shaka.util.Uint8ArrayUtils.concat(prftSegment, prftSegment);
@@ -3111,7 +3312,7 @@ describe('StreamingEngine', () => {
3111
3312
  // For these tests, we don't care about specific data appended.
3112
3313
  // Just return any old ArrayBuffer for any requested segment.
3113
3314
  netEngine = new shaka.test.FakeNetworkingEngine();
3114
- netEngine.request.and.callFake((requestType, request) => {
3315
+ netEngine.request.and.callFake((requestType, request, context) => {
3115
3316
  const buffer = new ArrayBuffer(0);
3116
3317
  const response = {uri: request.uris[0], data: buffer, headers: {}};
3117
3318
  const bytes = new shaka.net.NetworkingEngine.NumBytesRemainingClass();
@@ -3433,7 +3634,7 @@ describe('StreamingEngine', () => {
3433
3634
 
3434
3635
  // For these tests, we don't care about specific data appended.
3435
3636
  // Just return any old ArrayBuffer for any requested segment.
3436
- netEngine.request.and.callFake((requestType, request) => {
3637
+ netEngine.request.and.callFake((requestType, request, context) => {
3437
3638
  const buffer = new ArrayBuffer(0);
3438
3639
  /** @type {shaka.extern.Response} */
3439
3640
  const response = {
@@ -3678,7 +3879,7 @@ describe('StreamingEngine', () => {
3678
3879
  let isRequested = false;
3679
3880
  let isAborted = false;
3680
3881
 
3681
- netEngine.request.and.callFake((requestType, request) => {
3882
+ netEngine.request.and.callFake((requestType, request, context) => {
3682
3883
  isRequested = true;
3683
3884
 
3684
3885
  const abortOp = () => {
@@ -3732,6 +3933,149 @@ describe('StreamingEngine', () => {
3732
3933
  });
3733
3934
  });
3734
3935
 
3936
+ describe('prefetch segments', () => {
3937
+ const segmentType = shaka.net.NetworkingEngine.RequestType.SEGMENT;
3938
+
3939
+ let OriginalSegmentPrefetch;
3940
+
3941
+ beforeEach(() => {
3942
+ OriginalSegmentPrefetch = shaka.media.SegmentPrefetch;
3943
+ // eslint-disable-next-line no-restricted-syntax
3944
+ shaka.media.SegmentPrefetch = function(config, stream) {
3945
+ const fake = new shaka.test.FakeSegmentPrefetch(stream, segmentData);
3946
+ return /** @type {?} */(fake);
3947
+ };
3948
+
3949
+ setupVod();
3950
+ mediaSourceEngine = new shaka.test.FakeMediaSourceEngine(segmentData);
3951
+ createStreamingEngine();
3952
+ const config = shaka.util.PlayerConfiguration.createDefault().streaming;
3953
+ config.segmentPrefetchLimit = 3;
3954
+ streamingEngine.configure(config);
3955
+ });
3956
+
3957
+ afterEach(() => {
3958
+ shaka.media.SegmentPrefetch = OriginalSegmentPrefetch;
3959
+ });
3960
+
3961
+ it('should use prefetched segment without fetching again', async () => {
3962
+ streamingEngine.switchVariant(variant);
3963
+ await streamingEngine.start();
3964
+ playing = true;
3965
+ expectNoBuffer();
3966
+
3967
+ await runTest();
3968
+
3969
+ expectHasBuffer();
3970
+ expectSegmentRequest(false);
3971
+ });
3972
+
3973
+ it('should re-use prefetch segment when force clear buffer', async () => {
3974
+ streamingEngine.switchVariant(variant);
3975
+ await streamingEngine.start();
3976
+
3977
+ playing = true;
3978
+ expectNoBuffer();
3979
+ await runTest();
3980
+ expectHasBuffer();
3981
+ expectSegmentRequest(false);
3982
+
3983
+ streamingEngine.switchVariant(variant, true, 0, true);
3984
+ presentationTimeInSeconds = 0;
3985
+ await runTest();
3986
+ expectHasBuffer();
3987
+ expectSegmentRequest(false);
3988
+ });
3989
+
3990
+ it('should disable prefetch if reset config in middle', async () => {
3991
+ streamingEngine.switchVariant(variant);
3992
+ await streamingEngine.start();
3993
+
3994
+ playing = true;
3995
+ expectNoBuffer();
3996
+ await runTest();
3997
+ expectHasBuffer();
3998
+ expectSegmentRequest(false);
3999
+
4000
+ const config = shaka.util.PlayerConfiguration.createDefault().streaming;
4001
+ config.segmentPrefetchLimit = 0;
4002
+ streamingEngine.configure(config);
4003
+ streamingEngine.switchVariant(variant, true, 0, true);
4004
+ presentationTimeInSeconds = 0;
4005
+ await runTest();
4006
+ expectHasBuffer();
4007
+ expectSegmentRequest(true);
4008
+ });
4009
+
4010
+ it('should disable prefetch when reset config at begining', async () => {
4011
+ const config = shaka.util.PlayerConfiguration.createDefault().streaming;
4012
+ config.segmentPrefetchLimit = 0;
4013
+ streamingEngine.configure(config);
4014
+ streamingEngine.switchVariant(variant);
4015
+ await streamingEngine.start();
4016
+ playing = true;
4017
+ expectNoBuffer();
4018
+ await runTest();
4019
+ expectHasBuffer();
4020
+ expectSegmentRequest(true);
4021
+ });
4022
+
4023
+ /**
4024
+ * Expect no buffer has been added to MSE.
4025
+ */
4026
+ function expectNoBuffer() {
4027
+ expect(mediaSourceEngine.initSegments).toEqual({
4028
+ audio: [false, false],
4029
+ video: [false, false],
4030
+ text: [],
4031
+ });
4032
+ expect(mediaSourceEngine.segments).toEqual({
4033
+ audio: [false, false, false, false],
4034
+ video: [false, false, false, false],
4035
+ text: [false, false, false, false],
4036
+ });
4037
+ }
4038
+
4039
+ /**
4040
+ * Expect buffers have been added to MSE.
4041
+ */
4042
+ function expectHasBuffer() {
4043
+ expect(mediaSourceEngine.initSegments).toEqual({
4044
+ audio: [false, true],
4045
+ video: [false, true],
4046
+ text: [],
4047
+ });
4048
+ expect(mediaSourceEngine.segments).toEqual({
4049
+ audio: [true, true, true, true],
4050
+ video: [true, true, true, true],
4051
+ text: [false, false, false, false],
4052
+ });
4053
+ }
4054
+
4055
+ /**
4056
+ * @param {?boolean} hasRequest
4057
+ */
4058
+ function expectSegmentRequest(hasRequest) {
4059
+ const requests = [
4060
+ '0_audio_0', '0_video_0', '0_audio_1',
4061
+ '0_video_1', '1_audio_2', '1_video_2',
4062
+ '1_audio_3', '1_video_3',
4063
+ ];
4064
+
4065
+ const context = {
4066
+ type: shaka.net.NetworkingEngine.AdvancedRequestType.MEDIA_SEGMENT,
4067
+ };
4068
+
4069
+ for (const request of requests) {
4070
+ if (hasRequest) {
4071
+ netEngine.expectRequest(request, segmentType, context);
4072
+ } else {
4073
+ netEngine.expectNoRequest(request, segmentType, context);
4074
+ }
4075
+ }
4076
+ }
4077
+ });
4078
+
3735
4079
  /**
3736
4080
  * Slides the segment availability window forward by 1 second.
3737
4081
  */
@@ -3756,7 +4100,8 @@ describe('StreamingEngine', () => {
3756
4100
  if (request.uris[0] == targetUri) {
3757
4101
  const data = [targetUri];
3758
4102
 
3759
- if (errorCode == shaka.util.Error.Code.BAD_HTTP_STATUS) {
4103
+ if (errorCode == shaka.util.Error.Code.BAD_HTTP_STATUS ||
4104
+ errorCode == shaka.util.Error.Code.SEGMENT_MISSING) {
3760
4105
  data.push(404);
3761
4106
  data.push('');
3762
4107
  }
@@ -3827,4 +4172,40 @@ describe('StreamingEngine', () => {
3827
4172
  return segmentReference;
3828
4173
  };
3829
4174
  }
4175
+
4176
+ /**
4177
+ * Create a valid segment index for |alternateStream| based on |baseStream|
4178
+ * segment index.
4179
+ *
4180
+ * @param {shaka.extern.Stream} baseStream
4181
+ * @param {shaka.extern.Stream} alternateStream
4182
+ */
4183
+ function createAlternateSegmentIndex(baseStream, alternateStream) {
4184
+ const createSegmentIndexSpy =
4185
+ Util.funcSpy(alternateStream.createSegmentIndex);
4186
+ const altSegmentIndex = new shaka.test.FakeSegmentIndex();
4187
+
4188
+ altSegmentIndex.find.and.callFake(
4189
+ (time) => baseStream.segmentIndex.find(time));
4190
+
4191
+ altSegmentIndex.get.and.callFake((pos) => {
4192
+ const ref = baseStream.segmentIndex.get(pos);
4193
+
4194
+ if (ref) {
4195
+ const altInitUri = ref.initSegmentReference.getUris()[0] + '_alt';
4196
+ const altSegmentUri = ref.getUris()[0] + '_alt';
4197
+
4198
+ ref.initSegmentReference.getUris = () => [altInitUri];
4199
+ ref.getUris = () => [altSegmentUri];
4200
+ return ref;
4201
+ }
4202
+
4203
+ return null;
4204
+ });
4205
+
4206
+ createSegmentIndexSpy.and.callFake(() => {
4207
+ alternateStream.segmentIndex = altSegmentIndex;
4208
+ return Promise.resolve();
4209
+ });
4210
+ }
3830
4211
  });