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
@@ -252,7 +252,7 @@ shaka.media.MediaSourcePlayhead = class {
252
252
  mediaElement,
253
253
  () => this.onSeeking_(),
254
254
  (realStartTime) => this.onStarted_(realStartTime),
255
- this.getStartTime_(startTime));
255
+ () => this.getStartTime_(startTime));
256
256
 
257
257
  /** @type {shaka.util.Timer} */
258
258
  this.checkWindowTimer_ = new shaka.util.Timer(() => {
@@ -219,6 +219,46 @@ shaka.media.PresentationTimeline = class {
219
219
  return this.presentationDelay_;
220
220
  }
221
221
 
222
+ /**
223
+ * Gives PresentationTimeline a Stream's timeline so it can size and position
224
+ * the segment availability window, and account for missing segment
225
+ * information.
226
+ *
227
+ * @param {!Array.<shaka.media.PresentationTimeline.TimeRange>} timeline
228
+ * @param {number} startOffset
229
+ * @export
230
+ */
231
+ notifyTimeRange(timeline, startOffset) {
232
+ if (timeline.length == 0) {
233
+ return;
234
+ }
235
+
236
+ const firstStartTime = timeline[0].start + startOffset;
237
+ const lastEndTime = timeline[timeline.length - 1].end + startOffset;
238
+
239
+ this.notifyMinSegmentStartTime(firstStartTime);
240
+
241
+ this.maxSegmentDuration_ = timeline.reduce(
242
+ (max, r) => { return Math.max(max, r.end - r.start); },
243
+ this.maxSegmentDuration_);
244
+
245
+ this.maxSegmentEndTime_ =
246
+ Math.max(this.maxSegmentEndTime_, lastEndTime);
247
+
248
+ if (this.presentationStartTime_ != null && this.autoCorrectDrift_ &&
249
+ !this.startTimeLocked_) {
250
+ // Since we have explicit segment end times, calculate a presentation
251
+ // start based on them. This start time accounts for drift.
252
+ // Date.now() is in milliseconds, from which we compute "now" in seconds.
253
+ const now = (Date.now() + this.clockOffset_) / 1000.0;
254
+ this.presentationStartTime_ =
255
+ now - this.maxSegmentEndTime_ - this.maxSegmentDuration_;
256
+ }
257
+
258
+ shaka.log.v1('notifySegments:',
259
+ 'maxSegmentDuration=' + this.maxSegmentDuration_);
260
+ }
261
+
222
262
 
223
263
  /**
224
264
  * Gives PresentationTimeline an array of segments so it can size and position
@@ -573,3 +613,25 @@ shaka.media.PresentationTimeline = class {
573
613
  }
574
614
  }
575
615
  };
616
+
617
+
618
+ /**
619
+ * @typedef {{
620
+ * start: number,
621
+ * unscaledStart: number,
622
+ * end: number
623
+ * }}
624
+ *
625
+ * @description
626
+ * Defines a time range of a media segment. Times are in seconds.
627
+ *
628
+ * @property {number} start
629
+ * The start time of the range.
630
+ * @property {number} unscaledStart
631
+ * The start time of the range in representation timescale units.
632
+ * @property {number} end
633
+ * The end time (exclusive) of the range.
634
+ *
635
+ * @export
636
+ */
637
+ shaka.media.PresentationTimeline.TimeRange;
@@ -9,6 +9,7 @@ goog.provide('shaka.media.SegmentIndex');
9
9
  goog.provide('shaka.media.SegmentIterator');
10
10
 
11
11
  goog.require('goog.asserts');
12
+ goog.require('shaka.log');
12
13
  goog.require('shaka.media.SegmentReference');
13
14
  goog.require('shaka.util.IReleasable');
14
15
  goog.require('shaka.util.Timer');
@@ -44,12 +45,29 @@ shaka.media.SegmentIndex = class {
44
45
  *
45
46
  * @protected {number}
46
47
  */
47
- this.numEvicted = 0;
48
+ this.numEvicted_ = 0;
48
49
 
49
50
  /** @private {boolean} */
50
51
  this.immutable_ = false;
51
52
  }
52
53
 
54
+ /**
55
+ * Get immutability
56
+ *
57
+ * @return {boolean}
58
+ */
59
+ getIsImmutable() {
60
+ return this.immutable_;
61
+ }
62
+
63
+ /**
64
+ * Get number of references
65
+ * @protected
66
+ * @return {number}
67
+ */
68
+ getNumReferences() {
69
+ return this.references.length;
70
+ }
53
71
 
54
72
  /**
55
73
  * @override
@@ -140,11 +158,11 @@ shaka.media.SegmentIndex = class {
140
158
  this.references[i + 1].startTime : r.endTime;
141
159
  // Note that a segment ends immediately before the end time.
142
160
  if ((time >= start) && (time < end)) {
143
- return i + this.numEvicted;
161
+ return i + this.numEvicted_;
144
162
  }
145
163
  }
146
164
  if (this.references.length && time < this.references[0].startTime) {
147
- return this.numEvicted;
165
+ return this.numEvicted_;
148
166
  }
149
167
 
150
168
  return null;
@@ -164,7 +182,7 @@ shaka.media.SegmentIndex = class {
164
182
  return null;
165
183
  }
166
184
 
167
- const index = position - this.numEvicted;
185
+ const index = position - this.numEvicted_;
168
186
  if (index < 0 || index >= this.references.length) {
169
187
  return null;
170
188
  }
@@ -290,7 +308,7 @@ shaka.media.SegmentIndex = class {
290
308
  const diff = oldSize - newSize;
291
309
  // Tracking the number of evicted refs will keep their "positions" stable
292
310
  // for the caller.
293
- this.numEvicted += diff;
311
+ this.numEvicted_ += diff;
294
312
  }
295
313
 
296
314
 
@@ -332,7 +350,7 @@ shaka.media.SegmentIndex = class {
332
350
  if (firstReference.endTime <= windowStart) {
333
351
  this.references.shift();
334
352
  if (!isNew) {
335
- this.numEvicted++;
353
+ this.numEvicted_++;
336
354
  }
337
355
  } else {
338
356
  break;
@@ -407,10 +425,17 @@ shaka.media.SegmentIndex = class {
407
425
 
408
426
  /**
409
427
  * Returns a new iterator that initially points to the segment that contains
410
- * the given time. Like the normal iterator, next() must be called first to
411
- * get to the first element. Returns null if we do not find a segment at the
428
+ * the given time, or the nearest independent segment before it.
429
+ *
430
+ * Like the normal iterator, next() must be called first to get to the first
431
+ * element. Returns null if we do not find a segment at the
412
432
  * requested time.
413
433
  *
434
+ * The first segment returned by the iterator _MUST_ be an independent
435
+ * segment. Assumes that only partial references can be dependent, based on
436
+ * RFC 8216 rev 13, section 8.1: "Each (non-Partial) Media Segment in a Media
437
+ * Playlist will contain at least one independent frame."
438
+ *
414
439
  * @param {number} time
415
440
  * @return {?shaka.media.SegmentIterator}
416
441
  * @export
@@ -430,9 +455,18 @@ shaka.media.SegmentIndex = class {
430
455
  if (ref && ref.hasPartialSegments()) {
431
456
  // Look for a partial SegmentReference.
432
457
  for (let i = ref.partialReferences.length - 1; i >= 0; --i) {
433
- const r = ref.partialReferences[i];
458
+ let r = ref.partialReferences[i];
434
459
  // Note that a segment ends immediately before the end time.
435
460
  if ((time >= r.startTime) && (time < r.endTime)) {
461
+ // Find an independent partial segment by moving backwards.
462
+ while (i && !r.isIndependent()) {
463
+ i--;
464
+ r = ref.partialReferences[i];
465
+ }
466
+ if (!r.isIndependent()) {
467
+ shaka.log.alwaysError('No independent partial segment found!');
468
+ return null;
469
+ }
436
470
  // Call to next() should move the partial segment, not the full
437
471
  // segment.
438
472
  index++;
@@ -448,7 +482,7 @@ shaka.media.SegmentIndex = class {
448
482
  * @return {boolean}
449
483
  */
450
484
  isEmpty() {
451
- return this.references.length == 0;
485
+ return this.getNumReferences() == 0;
452
486
  }
453
487
 
454
488
  /**
@@ -629,7 +663,7 @@ shaka.media.MetaSegmentIndex = class extends shaka.media.SegmentIndex {
629
663
  */
630
664
  appendSegmentIndex(segmentIndex) {
631
665
  goog.asserts.assert(
632
- this.indexes_.length == 0 || segmentIndex.numEvicted == 0,
666
+ this.indexes_.length == 0 || segmentIndex.numEvicted_ == 0,
633
667
  'Should not append a new segment index with already-evicted segments');
634
668
  this.indexes_.push(segmentIndex);
635
669
  }
@@ -673,7 +707,8 @@ shaka.media.MetaSegmentIndex = class extends shaka.media.SegmentIndex {
673
707
  return position + numPassedInEarlierIndexes;
674
708
  }
675
709
 
676
- numPassedInEarlierIndexes += index.numEvicted + index.references.length;
710
+ numPassedInEarlierIndexes += index.numEvicted_ +
711
+ index.references.length;
677
712
  }
678
713
 
679
714
  return null;
@@ -689,7 +724,7 @@ shaka.media.MetaSegmentIndex = class extends shaka.media.SegmentIndex {
689
724
 
690
725
  for (const index of this.indexes_) {
691
726
  goog.asserts.assert(
692
- !sawSegments || index.numEvicted == 0,
727
+ !sawSegments || index.numEvicted_ == 0,
693
728
  'Should not see evicted segments after available segments');
694
729
  const reference = index.get(position - numPassedInEarlierIndexes);
695
730
 
@@ -697,7 +732,8 @@ shaka.media.MetaSegmentIndex = class extends shaka.media.SegmentIndex {
697
732
  return reference;
698
733
  }
699
734
 
700
- numPassedInEarlierIndexes += index.numEvicted + index.references.length;
735
+ numPassedInEarlierIndexes += index.numEvicted_ +
736
+ index.references.length;
701
737
  sawSegments = sawSegments || index.references.length != 0;
702
738
  }
703
739
 
@@ -0,0 +1,277 @@
1
+ /*! @license
2
+ * Shaka Player
3
+ * Copyright 2016 Google LLC
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+
7
+ goog.require('goog.asserts');
8
+ goog.require('shaka.net.NetworkingEngine');
9
+ goog.require('shaka.media.InitSegmentReference');
10
+ goog.require('shaka.media.SegmentReference');
11
+ goog.provide('shaka.media.SegmentPrefetch');
12
+ goog.require('shaka.log');
13
+
14
+ /**
15
+ * @summary
16
+ * This class manages segment prefetch operations.
17
+ * Called by StreamingEngine to prefetch next N segments
18
+ * ahead of playhead, to reduce the chances of rebuffering.
19
+ */
20
+ shaka.media.SegmentPrefetch = class {
21
+ /**
22
+ * @param {number} prefetchLimit
23
+ * @param {shaka.extern.Stream} stream
24
+ * @param {shaka.media.SegmentPrefetch.FetchDispatcher} fetchDispatcher
25
+ */
26
+ constructor(prefetchLimit, stream, fetchDispatcher) {
27
+ /** @private {number} */
28
+ this.prefetchLimit_ = prefetchLimit;
29
+
30
+ /** @private {shaka.extern.Stream} */
31
+ this.stream_ = stream;
32
+
33
+ /** @private {number} */
34
+ this.prefetchPosTime_ = 0;
35
+
36
+ /** @private {shaka.media.SegmentPrefetch.FetchDispatcher} */
37
+ this.fetchDispatcher_ = fetchDispatcher;
38
+
39
+ /**
40
+ * @private {!Map.<shaka.media.SegmentReference,
41
+ * !shaka.media.SegmentPrefetchOperation>}
42
+ */
43
+ this.segmentPrefetchMap_ = new Map();
44
+ }
45
+
46
+ /**
47
+ * Fetch next segments ahead of current segment.
48
+ *
49
+ * @param {(!shaka.media.SegmentReference)} startReference
50
+ * @public
51
+ */
52
+ prefetchSegments(startReference) {
53
+ goog.asserts.assert(this.prefetchLimit_ > 0,
54
+ 'SegmentPrefetch can not be used when prefetchLimit <= 0.');
55
+
56
+ const logPrefix = shaka.media.SegmentPrefetch.logPrefix_(this.stream_);
57
+ if (!this.stream_.segmentIndex) {
58
+ shaka.log.info(logPrefix, 'missing segmentIndex');
59
+ return;
60
+ }
61
+ const currTime = startReference.startTime;
62
+ const maxTime = Math.max(currTime, this.prefetchPosTime_);
63
+ const iterator = this.stream_.segmentIndex.getIteratorForTime(maxTime);
64
+ if (!iterator) {
65
+ return;
66
+ }
67
+ let reference = startReference;
68
+ while (this.segmentPrefetchMap_.size < this.prefetchLimit_ &&
69
+ reference != null) {
70
+ // By default doesn't prefech preload partial segments when using
71
+ // byterange
72
+ let prefetchAllowed = true;
73
+ if (reference.isPreload() && reference.endByte != null) {
74
+ prefetchAllowed = false;
75
+ }
76
+ if (reference.getStatus() ==
77
+ shaka.media.SegmentReference.Status.MISSING) {
78
+ prefetchAllowed = false;
79
+ }
80
+ if (prefetchAllowed && !this.segmentPrefetchMap_.has(reference)) {
81
+ const segmentPrefetchOperation =
82
+ new shaka.media.SegmentPrefetchOperation(this.fetchDispatcher_);
83
+ segmentPrefetchOperation.dispatchFetch(reference, this.stream_);
84
+ this.segmentPrefetchMap_.set(reference, segmentPrefetchOperation);
85
+ }
86
+ this.prefetchPosTime_ = reference.startTime;
87
+ reference = iterator.next().value;
88
+ }
89
+ }
90
+
91
+ /**
92
+ * Get the result of prefetched segment if already exists.
93
+ * @param {(!shaka.media.SegmentReference)} reference
94
+ * @param {?function(BufferSource):!Promise=} streamDataCallback
95
+ * @return {?shaka.net.NetworkingEngine.PendingRequest} op
96
+ * @public
97
+ */
98
+ getPrefetchedSegment(reference, streamDataCallback) {
99
+ goog.asserts.assert(this.prefetchLimit_ > 0,
100
+ 'SegmentPrefetch can not be used when prefetchLimit <= 0.');
101
+ goog.asserts.assert(reference instanceof shaka.media.SegmentReference,
102
+ 'getPrefetchedSegment is only used for shaka.media.SegmentReference.');
103
+
104
+ const logPrefix = shaka.media.SegmentPrefetch.logPrefix_(this.stream_);
105
+
106
+ if (this.segmentPrefetchMap_.has(reference)) {
107
+ const segmentPrefetchOperation = this.segmentPrefetchMap_.get(reference);
108
+ if (streamDataCallback) {
109
+ segmentPrefetchOperation.setStreamDataCallback(streamDataCallback);
110
+ }
111
+ this.segmentPrefetchMap_.delete(reference);
112
+ shaka.log.debug(
113
+ logPrefix,
114
+ 'reused prefetched segment at time:', reference.startTime,
115
+ 'mapSize', this.segmentPrefetchMap_.size);
116
+ return segmentPrefetchOperation.getOperation();
117
+ } else {
118
+ shaka.log.debug(
119
+ logPrefix,
120
+ 'missed segment at time:', reference.startTime,
121
+ 'mapSize', this.segmentPrefetchMap_.size);
122
+ return null;
123
+ }
124
+ }
125
+
126
+ /**
127
+ * Clear all segment data.
128
+ * @public
129
+ */
130
+ clearAll() {
131
+ if (this.segmentPrefetchMap_.size === 0) {
132
+ return;
133
+ }
134
+ const logPrefix = shaka.media.SegmentPrefetch.logPrefix_(this.stream_);
135
+ for (const reference of this.segmentPrefetchMap_.keys()) {
136
+ if (reference) {
137
+ this.abortPrefetchedSegment_(reference);
138
+ }
139
+ }
140
+ shaka.log.info(logPrefix, 'cleared all');
141
+ this.prefetchPosTime_ = 0;
142
+ }
143
+
144
+ /**
145
+ * Reset the prefetchLimit and clear all internal states.
146
+ * Called by StreamingEngine when configure() was called.
147
+ * @param {number} newPrefetchLimit
148
+ * @public
149
+ */
150
+ resetLimit(newPrefetchLimit) {
151
+ goog.asserts.assert(newPrefetchLimit >= 0,
152
+ 'The new prefetch limit must be >= 0.');
153
+ this.prefetchLimit_ = newPrefetchLimit;
154
+ const keyArr = Array.from(this.segmentPrefetchMap_.keys());
155
+ while (keyArr.length > newPrefetchLimit) {
156
+ const reference = keyArr.pop();
157
+ if (reference) {
158
+ this.abortPrefetchedSegment_(reference);
159
+ }
160
+ }
161
+ }
162
+
163
+ /**
164
+ * Called by Streaming Engine when switching variant.
165
+ * @param {shaka.extern.Stream} stream
166
+ * @public
167
+ */
168
+ switchStream(stream) {
169
+ if (stream && stream !== this.stream_) {
170
+ this.clearAll();
171
+ this.stream_ = stream;
172
+ }
173
+ }
174
+
175
+ /**
176
+ * Remove a segment from prefetch map and abort it.
177
+ * @param {(!shaka.media.SegmentReference)} reference
178
+ * @private
179
+ */
180
+ abortPrefetchedSegment_(reference) {
181
+ const logPrefix = shaka.media.SegmentPrefetch.logPrefix_(this.stream_);
182
+ const segmentPrefetchOperation = this.segmentPrefetchMap_.get(reference);
183
+ this.segmentPrefetchMap_.delete(reference);
184
+ if (segmentPrefetchOperation) {
185
+ segmentPrefetchOperation.abort();
186
+ shaka.log.info(
187
+ logPrefix,
188
+ 'pop and abort prefetched segment at time:', reference.startTime);
189
+ }
190
+ }
191
+
192
+ /**
193
+ * The prefix of the logs that are created in this class.
194
+ * @return {string}
195
+ * @private
196
+ */
197
+ static logPrefix_(stream) {
198
+ return 'SegmentPrefetch(' + stream.type + ':' + stream.id + ')';
199
+ }
200
+ };
201
+
202
+ /**
203
+ * @summary
204
+ * This class manages a segment prefetch operation.
205
+ */
206
+ shaka.media.SegmentPrefetchOperation = class {
207
+ /**
208
+ * @param {shaka.media.SegmentPrefetch.FetchDispatcher} fetchDispatcher
209
+ */
210
+ constructor(fetchDispatcher) {
211
+ /** @private {shaka.media.SegmentPrefetch.FetchDispatcher} */
212
+ this.fetchDispatcher_ = fetchDispatcher;
213
+
214
+ /** @private {?function(BufferSource):!Promise} */
215
+ this.streamDataCallback_ = null;
216
+
217
+ /** @private {?shaka.net.NetworkingEngine.PendingRequest} */
218
+ this.operation_ = null;
219
+ }
220
+
221
+ /**
222
+ * Fetch a segments
223
+ *
224
+ * @param {!shaka.media.SegmentReference}
225
+ * reference
226
+ * @param {!shaka.extern.Stream} stream
227
+ * @public
228
+ */
229
+ dispatchFetch(reference, stream) {
230
+ this.operation_ = this.fetchDispatcher_(
231
+ reference, stream, async (data) => {
232
+ if (this.streamDataCallback_) {
233
+ await this.streamDataCallback_(data);
234
+ }
235
+ });
236
+ }
237
+
238
+ /**
239
+ * Get the operation of prefetched segment if already exists.
240
+ *
241
+ * @return {?shaka.net.NetworkingEngine.PendingRequest} op
242
+ * @public
243
+ */
244
+ getOperation() {
245
+ return this.operation_;
246
+ }
247
+
248
+ /**
249
+ * @param {?function(BufferSource):!Promise} streamDataCallback
250
+ * @public
251
+ */
252
+ setStreamDataCallback(streamDataCallback) {
253
+ this.streamDataCallback_ = streamDataCallback;
254
+ }
255
+
256
+ /**
257
+ * Abort the current operation if exists.
258
+ */
259
+ abort() {
260
+ if (this.operation_) {
261
+ this.operation_.abort();
262
+ }
263
+ }
264
+ };
265
+
266
+ /**
267
+ * @typedef {function(
268
+ * !(shaka.media.InitSegmentReference|shaka.media.SegmentReference),
269
+ * shaka.extern.Stream,
270
+ * ?function(BufferSource):!Promise=
271
+ * ):!shaka.net.NetworkingEngine.PendingRequest}
272
+ *
273
+ * @description
274
+ * A callback function that fetches a segment.
275
+ * @export
276
+ */
277
+ shaka.media.SegmentPrefetch.FetchDispatcher;
@@ -10,6 +10,7 @@ goog.provide('shaka.media.SegmentReference');
10
10
  goog.require('goog.asserts');
11
11
  goog.require('shaka.log');
12
12
  goog.require('shaka.util.ArrayUtils');
13
+ goog.require('shaka.util.BufferUtils');
13
14
 
14
15
 
15
16
  /**
@@ -29,9 +30,11 @@ shaka.media.InitSegmentReference = class {
29
30
  * segment extends to the end of the resource.
30
31
  * @param {null|shaka.extern.MediaQualityInfo=} mediaQuality Information about
31
32
  * the quality of the media associated with this init segment.
32
- * @param {number=} timescale
33
+ * @param {(null|number)=} timescale
34
+ * @param {(null|BufferSource)=} segmentData
33
35
  */
34
- constructor(uris, startByte, endByte, mediaQuality = null, timescale) {
36
+ constructor(uris, startByte, endByte, mediaQuality = null, timescale = null,
37
+ segmentData = null) {
35
38
  /** @type {function():!Array.<string>} */
36
39
  this.getUris = uris;
37
40
 
@@ -44,8 +47,11 @@ shaka.media.InitSegmentReference = class {
44
47
  /** @const {shaka.extern.MediaQualityInfo|null} */
45
48
  this.mediaQuality = mediaQuality;
46
49
 
47
- /** @type {number|undefined} */
50
+ /** @type {number|null} */
48
51
  this.timescale = timescale;
52
+
53
+ /** @type {BufferSource|null} */
54
+ this.segmentData = segmentData;
49
55
  }
50
56
 
51
57
  /**
@@ -93,6 +99,16 @@ shaka.media.InitSegmentReference = class {
93
99
  return this.mediaQuality;
94
100
  }
95
101
 
102
+ /**
103
+ * Return the segment data.
104
+ *
105
+ * @return {?BufferSource}
106
+ */
107
+ getSegmentData() {
108
+ return this.segmentData;
109
+ }
110
+
111
+
96
112
  /**
97
113
  * Check if two initSegmentReference have all the same values.
98
114
  * @param {?shaka.media.InitSegmentReference} reference1
@@ -101,12 +117,15 @@ shaka.media.InitSegmentReference = class {
101
117
  */
102
118
  static equal(reference1, reference2) {
103
119
  const ArrayUtils = shaka.util.ArrayUtils;
120
+ const BufferUtils = shaka.util.BufferUtils;
104
121
  if (!reference1 || !reference2) {
105
122
  return reference1 == reference2;
106
123
  } else {
107
124
  return reference1.getStartByte() == reference2.getStartByte() &&
108
125
  reference1.getEndByte() == reference2.getEndByte() &&
109
- ArrayUtils.equal(reference1.getUris(), reference2.getUris());
126
+ ArrayUtils.equal(reference1.getUris(), reference2.getUris()) &&
127
+ BufferUtils.equal(reference1.getSegmentData(),
128
+ reference2.getSegmentData());
110
129
  }
111
130
  }
112
131
  };
@@ -246,6 +265,12 @@ shaka.media.SegmentReference = class {
246
265
  /** @type {boolean} */
247
266
  this.preload = false;
248
267
 
268
+ /** @type {boolean} */
269
+ this.independent = true;
270
+
271
+ /** @type {boolean} */
272
+ this.byterangeOptimization = false;
273
+
249
274
  /** @type {?shaka.extern.HlsAes128Key} */
250
275
  this.hlsAes128Key = hlsAes128Key;
251
276
 
@@ -388,6 +413,47 @@ shaka.media.SegmentReference = class {
388
413
  return this.preload;
389
414
  }
390
415
 
416
+ /**
417
+ * Mark the reference as non-independent.
418
+ *
419
+ * @export
420
+ */
421
+ markAsNonIndependent() {
422
+ this.independent = false;
423
+ }
424
+
425
+ /**
426
+ * Returns true if the segment is independent.
427
+ *
428
+ * @return {boolean}
429
+ * @export
430
+ */
431
+ isIndependent() {
432
+ return this.independent;
433
+ }
434
+
435
+ /**
436
+ * Mark the reference as byterange optimization.
437
+ *
438
+ * The "byterange optimization" means that it is playable using MP4 low
439
+ * latency streaming with chunked data.
440
+ *
441
+ * @export
442
+ */
443
+ markAsByterangeOptimization() {
444
+ this.byterangeOptimization = true;
445
+ }
446
+
447
+ /**
448
+ * Returns true if the segment has a byterange optimization.
449
+ *
450
+ * @return {boolean}
451
+ * @export
452
+ */
453
+ hasByterangeOptimization() {
454
+ return this.byterangeOptimization;
455
+ }
456
+
391
457
  /**
392
458
  * Set the segment's thumbnail sprite.
393
459
  *