shaka-player 4.3.9 → 4.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (321) hide show
  1. package/.github/workflows/build-and-test.yaml +11 -18
  2. package/.github/workflows/compute-incremental-coverage.py +3 -0
  3. package/.github/workflows/release-please.yaml +7 -0
  4. package/.github/workflows/selenium-lab-tests.yaml +45 -10
  5. package/AUTHORS +9 -0
  6. package/CHANGELOG.md +295 -190
  7. package/CONTRIBUTORS +8 -0
  8. package/LICENSE +32 -0
  9. package/README.md +63 -10
  10. package/demo/autoTemplate.txt +40 -5
  11. package/demo/close_button.js +11 -0
  12. package/demo/common/asset.js +13 -1
  13. package/demo/common/assets.js +112 -28
  14. package/demo/common/message_ids.js +25 -1
  15. package/demo/config.js +69 -10
  16. package/demo/customWarning.txt +2 -2
  17. package/demo/index.html +3 -1
  18. package/demo/locales/en.json +26 -2
  19. package/demo/locales/source.json +105 -9
  20. package/demo/main.js +57 -9
  21. package/demo/search.js +3 -1
  22. package/demo/service_worker.js +3 -0
  23. package/dist/controls.css +1 -1
  24. package/dist/controls.css.map +1 -1
  25. package/dist/demo.compiled.debug.js +132 -118
  26. package/dist/demo.compiled.debug.map +3 -3
  27. package/dist/demo.compiled.js +132 -118
  28. package/dist/demo.compiled.map +3 -3
  29. package/dist/deps.js +46 -28
  30. package/dist/locales.js +31 -31
  31. package/dist/receiver.compiled.debug.js +60 -52
  32. package/dist/receiver.compiled.debug.map +3 -3
  33. package/dist/receiver.compiled.js +60 -52
  34. package/dist/receiver.compiled.map +3 -3
  35. package/dist/shaka-player.compiled.d.ts +676 -391
  36. package/dist/shaka-player.compiled.debug.d.ts +676 -391
  37. package/dist/shaka-player.compiled.debug.externs.js +1188 -616
  38. package/dist/shaka-player.compiled.debug.js +1046 -700
  39. package/dist/shaka-player.compiled.debug.map +4 -4
  40. package/dist/shaka-player.compiled.externs.js +1188 -616
  41. package/dist/shaka-player.compiled.js +1094 -933
  42. package/dist/shaka-player.compiled.map +4 -4
  43. package/dist/shaka-player.ui.d.ts +677 -392
  44. package/dist/shaka-player.ui.debug.d.ts +677 -392
  45. package/dist/shaka-player.ui.debug.externs.js +1023 -451
  46. package/dist/shaka-player.ui.debug.js +1101 -738
  47. package/dist/shaka-player.ui.debug.map +4 -4
  48. package/dist/shaka-player.ui.externs.js +1023 -451
  49. package/dist/shaka-player.ui.js +1244 -1071
  50. package/dist/shaka-player.ui.map +4 -4
  51. package/docs/tutorials/ad_monetization.md +15 -1
  52. package/docs/tutorials/application-level-redirects.md +1 -1
  53. package/docs/tutorials/config.md +9 -5
  54. package/docs/tutorials/drm-config.md +61 -0
  55. package/docs/tutorials/fairplay.md +19 -4
  56. package/docs/tutorials/license-server-auth.md +4 -4
  57. package/docs/tutorials/license-wrapping.md +2 -2
  58. package/docs/tutorials/ui-customization.md +7 -3
  59. package/docs/tutorials/ui.md +7 -7
  60. package/docs/tutorials/upgrade.md +3 -0
  61. package/externs/ima.js +123 -2
  62. package/externs/isoboxer.js +217 -0
  63. package/externs/mediakeys.js +18 -0
  64. package/externs/mux.js +1 -105
  65. package/externs/pictureinpicture.js +48 -0
  66. package/externs/shaka/ads.js +61 -1
  67. package/externs/shaka/cea.js +110 -0
  68. package/externs/shaka/codecs.js +32 -0
  69. package/externs/shaka/manifest.js +101 -1
  70. package/externs/shaka/manifest_parser.js +0 -10
  71. package/externs/shaka/net.js +36 -7
  72. package/externs/shaka/offline.js +8 -1
  73. package/externs/shaka/player.js +220 -21
  74. package/externs/shaka/text.js +1 -382
  75. package/externs/shaka/transmuxer.js +59 -0
  76. package/karma.conf.js +15 -0
  77. package/lib/abr/simple_abr_manager.js +6 -3
  78. package/lib/ads/ad_manager.js +60 -2
  79. package/lib/ads/client_side_ad.js +86 -0
  80. package/lib/ads/client_side_ad_manager.js +93 -5
  81. package/lib/ads/server_side_ad.js +86 -0
  82. package/lib/ads/server_side_ad_manager.js +13 -0
  83. package/lib/cast/cast_utils.js +1 -0
  84. package/lib/cea/cea608_data_channel.js +24 -14
  85. package/lib/cea/cea608_memory.js +5 -2
  86. package/lib/cea/cea708_service.js +8 -9
  87. package/lib/cea/cea708_window.js +1 -2
  88. package/lib/cea/cea_decoder.js +23 -6
  89. package/lib/cea/cea_utils.js +25 -2
  90. package/lib/cea/dummy_caption_decoder.js +21 -0
  91. package/lib/cea/dummy_cea_parser.js +1 -3
  92. package/lib/cea/mp4_cea_parser.js +15 -10
  93. package/lib/cea/ts_cea_parser.js +69 -0
  94. package/lib/dash/content_protection.js +32 -2
  95. package/lib/dash/dash_parser.js +93 -31
  96. package/lib/dash/mpd_utils.js +6 -25
  97. package/lib/dash/segment_base.js +12 -14
  98. package/lib/dash/segment_list.js +10 -6
  99. package/lib/dash/segment_template.js +349 -32
  100. package/lib/dependencies/all.js +8 -0
  101. package/lib/hls/hls_classes.js +33 -8
  102. package/lib/hls/hls_parser.js +805 -191
  103. package/lib/hls/manifest_text_parser.js +4 -14
  104. package/lib/media/adaptation_set_criteria.js +43 -3
  105. package/lib/media/closed_caption_parser.js +74 -12
  106. package/lib/media/drm_engine.js +189 -49
  107. package/lib/media/gap_jumping_controller.js +1 -1
  108. package/lib/media/manifest_parser.js +34 -0
  109. package/lib/media/media_source_capabilities.js +10 -0
  110. package/lib/media/media_source_engine.js +475 -231
  111. package/lib/media/playhead.js +1 -1
  112. package/lib/media/presentation_timeline.js +62 -0
  113. package/lib/media/segment_index.js +50 -14
  114. package/lib/media/segment_prefetch.js +277 -0
  115. package/lib/media/segment_reference.js +70 -4
  116. package/lib/media/streaming_engine.js +258 -81
  117. package/lib/media/video_wrapper.js +15 -7
  118. package/lib/mss/content_protection.js +353 -0
  119. package/lib/mss/mss_parser.js +1081 -0
  120. package/lib/net/networking_engine.js +60 -10
  121. package/lib/offline/indexeddb/v1_storage_cell.js +1 -0
  122. package/lib/offline/indexeddb/v2_storage_cell.js +1 -0
  123. package/lib/offline/manifest_converter.js +6 -0
  124. package/lib/offline/storage.js +2 -4
  125. package/lib/player.js +309 -105
  126. package/lib/polyfill/media_capabilities.js +43 -3
  127. package/lib/polyfill/patchedmediakeys_apple.js +5 -0
  128. package/lib/polyfill/patchedmediakeys_nop.js +5 -0
  129. package/lib/polyfill/patchedmediakeys_webkit.js +5 -0
  130. package/lib/text/cue.js +208 -130
  131. package/lib/text/lrc_text_parser.js +2 -2
  132. package/lib/text/mp4_vtt_parser.js +2 -2
  133. package/lib/text/sbv_text_parser.js +1 -1
  134. package/lib/text/simple_text_displayer.js +2 -1
  135. package/lib/text/srt_text_parser.js +9 -1
  136. package/lib/text/ssa_text_parser.js +2 -2
  137. package/lib/text/text_engine.js +3 -21
  138. package/lib/text/text_utils.js +71 -20
  139. package/lib/text/ui_text_displayer.js +14 -14
  140. package/lib/text/vtt_text_parser.js +22 -14
  141. package/lib/transmuxer/aac_transmuxer.js +224 -0
  142. package/lib/transmuxer/ac3.js +142 -0
  143. package/lib/transmuxer/ac3_transmuxer.js +212 -0
  144. package/lib/transmuxer/adts.js +232 -0
  145. package/lib/transmuxer/ec3.js +103 -0
  146. package/lib/transmuxer/ec3_transmuxer.js +212 -0
  147. package/lib/transmuxer/h264.js +324 -0
  148. package/lib/transmuxer/mp3_transmuxer.js +204 -0
  149. package/lib/transmuxer/mpeg_audio.js +196 -0
  150. package/lib/transmuxer/mpeg_ts_transmuxer.js +182 -0
  151. package/lib/transmuxer/mss_transmuxer.js +389 -0
  152. package/lib/{media/transmuxer.js → transmuxer/muxjs_transmuxer.js} +34 -80
  153. package/lib/transmuxer/transmuxer_engine.js +161 -0
  154. package/lib/transmuxer/ts_transmuxer.js +738 -0
  155. package/lib/util/cmcd_manager.js +131 -71
  156. package/lib/util/error.js +62 -26
  157. package/lib/util/exp_golomb.js +220 -0
  158. package/lib/util/fairplay_utils.js +64 -13
  159. package/lib/util/id3_utils.js +56 -3
  160. package/lib/util/language_utils.js +11 -8
  161. package/lib/util/manifest_parser_utils.js +3 -0
  162. package/lib/util/mime_utils.js +44 -7
  163. package/lib/util/mp4_box_parsers.js +233 -5
  164. package/lib/util/mp4_generator.js +1381 -0
  165. package/lib/util/periods.js +40 -34
  166. package/lib/util/platform.js +25 -3
  167. package/lib/util/player_configuration.js +38 -1
  168. package/lib/util/stream_utils.js +138 -27
  169. package/lib/util/text_parser.js +1 -1
  170. package/lib/util/ts_parser.js +470 -47
  171. package/lib/util/xml_utils.js +16 -3
  172. package/maintained-branches.md +2 -2
  173. package/package.json +4 -2
  174. package/roadmap.md +21 -6
  175. package/shaka-player.uncompiled.js +18 -0
  176. package/test/abr/simple_abr_manager_unit.js +32 -4
  177. package/test/ads/ad_manager_unit.js +18 -2
  178. package/test/cast/cast_utils_unit.js +7 -2
  179. package/test/cea/cea608_memory_unit.js +24 -0
  180. package/test/cea/cea_decoder_unit.js +33 -0
  181. package/test/dash/dash_parser_content_protection_unit.js +93 -3
  182. package/test/dash/dash_parser_live_unit.js +56 -13
  183. package/test/dash/dash_parser_manifest_unit.js +70 -2
  184. package/test/dash/dash_parser_segment_base_unit.js +14 -9
  185. package/test/dash/dash_parser_segment_template_unit.js +286 -6
  186. package/test/demo/demo_unit.js +5 -1
  187. package/test/hls/hls_live_unit.js +188 -28
  188. package/test/hls/hls_parser_unit.js +250 -109
  189. package/test/hls/manifest_text_parser_unit.js +18 -13
  190. package/test/media/adaptation_set_criteria_unit.js +46 -14
  191. package/test/media/adaptation_set_unit.js +2 -0
  192. package/test/media/drm_engine_integration.js +11 -8
  193. package/test/media/drm_engine_unit.js +220 -7
  194. package/test/media/media_source_engine_integration.js +122 -38
  195. package/test/media/media_source_engine_unit.js +133 -153
  196. package/test/media/playhead_unit.js +3 -0
  197. package/test/media/presentation_timeline_unit.js +51 -0
  198. package/test/media/segment_index_unit.js +52 -0
  199. package/test/media/segment_prefetch_unit.js +243 -0
  200. package/test/media/streaming_engine_integration.js +9 -3
  201. package/test/media/streaming_engine_unit.js +440 -59
  202. package/test/mss/mss_parser_content_protection_unit.js +49 -0
  203. package/test/mss/mss_parser_unit.js +472 -0
  204. package/test/mss/mss_player_integration.js +123 -0
  205. package/test/offline/manifest_convert_unit.js +9 -0
  206. package/test/offline/storage_integration.js +6 -1
  207. package/test/player_integration.js +4 -1
  208. package/test/player_unit.js +386 -133
  209. package/test/polyfill/media_capabilities_unit.js +18 -0
  210. package/test/test/assets/hls-raw-aac/fileSequence0.aac +0 -0
  211. package/test/test/assets/hls-raw-aac/fileSequence1.aac +0 -0
  212. package/test/test/assets/hls-raw-aac/fileSequence2.aac +0 -0
  213. package/test/test/assets/hls-raw-aac/fileSequence3.aac +0 -0
  214. package/test/test/assets/hls-raw-aac/manifest.m3u8 +4 -0
  215. package/test/test/assets/hls-raw-aac/stream.m4a.m3u8 +14 -0
  216. package/test/test/assets/hls-raw-ac3/fileSequence0.ac3 +0 -0
  217. package/test/test/assets/hls-raw-ac3/fileSequence1.ac3 +0 -0
  218. package/test/test/assets/hls-raw-ac3/fileSequence2.ac3 +0 -0
  219. package/test/test/assets/hls-raw-ac3/fileSequence3.ac3 +0 -0
  220. package/test/test/assets/hls-raw-ac3/fileSequence4.ac3 +0 -0
  221. package/test/test/assets/hls-raw-ac3/fileSequence5.ac3 +0 -0
  222. package/test/test/assets/hls-raw-ac3/fileSequence6.ac3 +0 -0
  223. package/test/test/assets/hls-raw-ac3/fileSequence7.ac3 +0 -0
  224. package/test/test/assets/hls-raw-ac3/prog_index.m3u8 +31 -0
  225. package/test/test/assets/hls-raw-ec3/fileSequence0.ec3 +0 -0
  226. package/test/test/assets/hls-raw-ec3/fileSequence1.ec3 +0 -0
  227. package/test/test/assets/hls-raw-ec3/fileSequence2.ec3 +0 -0
  228. package/test/test/assets/hls-raw-ec3/fileSequence3.ec3 +0 -0
  229. package/test/test/assets/hls-raw-ec3/fileSequence4.ec3 +0 -0
  230. package/test/test/assets/hls-raw-ec3/fileSequence5.ec3 +0 -0
  231. package/test/test/assets/hls-raw-ec3/fileSequence6.ec3 +0 -0
  232. package/test/test/assets/hls-raw-ec3/fileSequence7.ec3 +0 -0
  233. package/test/test/assets/hls-raw-ec3/prog_index.m3u8 +31 -0
  234. package/test/test/assets/hls-raw-mp3/chunklist_w905519204.m3u8 +13 -0
  235. package/test/test/assets/hls-raw-mp3/media_w905519204_0.mp3 +0 -0
  236. package/test/test/assets/hls-raw-mp3/media_w905519204_1.mp3 +0 -0
  237. package/test/test/assets/hls-raw-mp3/media_w905519204_2.mp3 +0 -0
  238. package/test/test/assets/hls-raw-mp3/media_w905519204_3.mp3 +0 -0
  239. package/test/test/assets/hls-raw-mp3/playlist.m3u8 +4 -0
  240. package/test/test/assets/hls-ts-aac/playlist.m3u8 +23 -0
  241. package/test/test/assets/hls-ts-aac/segment_0.ts +0 -0
  242. package/test/test/assets/hls-ts-aac/segment_1.ts +0 -0
  243. package/test/test/assets/hls-ts-aac/segment_2.ts +0 -0
  244. package/test/test/assets/hls-ts-aac/segment_3.ts +0 -0
  245. package/test/test/assets/hls-ts-aac/segment_4.ts +0 -0
  246. package/test/test/assets/hls-ts-aac/segment_5.ts +0 -0
  247. package/test/test/assets/hls-ts-aac/segment_6.ts +0 -0
  248. package/test/test/assets/hls-ts-aac/segment_7.ts +0 -0
  249. package/test/test/assets/hls-ts-ac3/ChID_voices_6ch_640kbps_dd_sub_0000.ts +0 -0
  250. package/test/test/assets/hls-ts-ac3/ChID_voices_6ch_640kbps_dd_sub_0001.ts +0 -0
  251. package/test/test/assets/hls-ts-ac3/ChID_voices_6ch_640kbps_dd_sub_0002.ts +0 -0
  252. package/test/test/assets/hls-ts-ac3/ChID_voices_6ch_640kbps_dd_sub_0003.ts +0 -0
  253. package/test/test/assets/hls-ts-ac3/ChID_voices_6ch_640kbps_dd_sub_0004.ts +0 -0
  254. package/test/test/assets/hls-ts-ac3/ChID_voices_6ch_640kbps_dd_sub_0005.ts +0 -0
  255. package/test/test/assets/hls-ts-ac3/prog_index.m3u8 +19 -0
  256. package/test/test/assets/hls-ts-ec3/ChID_voices_6ch_256kbps_ddp_sub_0000.ts +0 -0
  257. package/test/test/assets/hls-ts-ec3/ChID_voices_6ch_256kbps_ddp_sub_0001.ts +0 -0
  258. package/test/test/assets/hls-ts-ec3/ChID_voices_6ch_256kbps_ddp_sub_0002.ts +0 -0
  259. package/test/test/assets/hls-ts-ec3/ChID_voices_6ch_256kbps_ddp_sub_0003.ts +0 -0
  260. package/test/test/assets/hls-ts-ec3/ChID_voices_6ch_256kbps_ddp_sub_0004.ts +0 -0
  261. package/test/test/assets/hls-ts-ec3/ChID_voices_6ch_256kbps_ddp_sub_0005.ts +0 -0
  262. package/test/test/assets/hls-ts-ec3/prog_index.m3u8 +19 -0
  263. package/test/test/assets/hls-ts-h264/fileSequence0.ts +0 -0
  264. package/test/test/assets/hls-ts-h264/fileSequence1.ts +0 -0
  265. package/test/test/assets/hls-ts-h264/fileSequence2.ts +0 -0
  266. package/test/test/assets/hls-ts-h264/fileSequence3.ts +0 -0
  267. package/test/test/assets/hls-ts-h264/fileSequence4.ts +0 -0
  268. package/test/test/assets/hls-ts-h264/fileSequence5.ts +0 -0
  269. package/test/test/assets/hls-ts-h264/prog_index.m3u8 +25 -0
  270. package/test/test/assets/hls-ts-mp3/manifest.m3u8 +13 -0
  271. package/test/test/assets/hls-ts-mp3/media_w239891843_0.ts +0 -0
  272. package/test/test/assets/hls-ts-mp3/media_w239891843_1.ts +0 -0
  273. package/test/test/assets/hls-ts-mp3/media_w239891843_2.ts +0 -0
  274. package/test/test/assets/hls-ts-mp3/media_w239891843_3.ts +0 -0
  275. package/test/test/assets/hls-ts-muxed-aac-h264/chunk.m3u8 +17 -0
  276. package/test/test/assets/hls-ts-muxed-aac-h264/n_0_0_0.ts +0 -0
  277. package/test/test/assets/hls-ts-muxed-aac-h264/n_1_0_0.ts +0 -0
  278. package/test/test/assets/hls-ts-muxed-aac-h264/n_2_0_0.ts +0 -0
  279. package/test/test/assets/hls-ts-muxed-aac-h264/n_3_0_0.ts +0 -0
  280. package/test/test/assets/hls-ts-muxed-aac-h264/n_4_0_0.ts +0 -0
  281. package/test/test/assets/hls-ts-muxed-aac-h264/n_5_0_0.ts +0 -0
  282. package/test/test/assets/hls-ts-muxed-aac-h264/playlist.m3u8 +4 -0
  283. package/test/test/boot.js +7 -2
  284. package/test/test/util/fake_ad.js +99 -0
  285. package/test/test/util/fake_ad_manager.js +7 -1
  286. package/test/test/util/fake_networking_engine.js +41 -14
  287. package/test/test/util/fake_segment_prefetch.js +81 -0
  288. package/test/test/util/manifest_generator.js +19 -5
  289. package/test/test/util/mss_parser_util.js +115 -0
  290. package/test/test/util/offline_utils.js +1 -0
  291. package/test/test/util/simple_fakes.js +12 -3
  292. package/test/test/util/streaming_engine_util.js +14 -4
  293. package/test/test/util/ttml_utils.js +2 -2
  294. package/test/test/util/ui_utils.js +5 -1
  295. package/test/text/cue_integration.js +1 -1
  296. package/test/text/srt_text_parser_unit.js +78 -0
  297. package/test/text/text_engine_unit.js +17 -8
  298. package/test/text/ui_text_displayer_unit.js +4 -4
  299. package/test/text/vtt_text_parser_unit.js +59 -40
  300. package/test/text/web_vtt_generator_unit.js +9 -3
  301. package/test/{media/transmuxer_integration.js → transmuxer/muxjs_transmuxer_integration.js} +30 -33
  302. package/test/transmuxer/transmuxer_engine_integration.js +60 -0
  303. package/test/transmuxer/transmuxer_integration.js +296 -0
  304. package/test/util/cmcd_manager_unit.js +162 -13
  305. package/test/util/language_utils_unit.js +9 -2
  306. package/test/util/mp4_box_parsers_unit.js +39 -4
  307. package/test/util/periods_unit.js +34 -26
  308. package/test/util/stream_utils_unit.js +60 -107
  309. package/ui/controls.js +19 -1
  310. package/ui/controls.less +1 -0
  311. package/ui/externs/ui.js +13 -3
  312. package/ui/language_utils.js +15 -0
  313. package/ui/less/thumbnails.less +26 -0
  314. package/ui/locales/oc.json +42 -0
  315. package/ui/pip_button.js +83 -2
  316. package/ui/range_element.js +15 -0
  317. package/ui/resolution_selection.js +9 -2
  318. package/ui/seek_bar.js +230 -1
  319. package/ui/ui.js +5 -0
  320. package/lib/cea/i_caption_decoder.js +0 -51
  321. package/lib/cea/i_cea_parser.js +0 -68
@@ -0,0 +1,1081 @@
1
+ /*! @license
2
+ * Shaka Player
3
+ * Copyright 2016 Google LLC
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+
7
+ goog.provide('shaka.mss.MssParser');
8
+
9
+ goog.require('goog.asserts');
10
+ goog.require('shaka.abr.Ewma');
11
+ goog.require('shaka.log');
12
+ goog.require('shaka.media.InitSegmentReference');
13
+ goog.require('shaka.media.ManifestParser');
14
+ goog.require('shaka.media.PresentationTimeline');
15
+ goog.require('shaka.media.SegmentIndex');
16
+ goog.require('shaka.media.SegmentReference');
17
+ goog.require('shaka.mss.ContentProtection');
18
+ goog.require('shaka.net.NetworkingEngine');
19
+ goog.require('shaka.util.Error');
20
+ goog.require('shaka.util.LanguageUtils');
21
+ goog.require('shaka.util.ManifestParserUtils');
22
+ goog.require('shaka.util.Mp4Generator');
23
+ goog.require('shaka.util.OperationManager');
24
+ goog.require('shaka.util.Timer');
25
+ goog.require('shaka.util.XmlUtils');
26
+
27
+
28
+ /**
29
+ * Creates a new MSS parser.
30
+ *
31
+ * @implements {shaka.extern.ManifestParser}
32
+ * @export
33
+ */
34
+ shaka.mss.MssParser = class {
35
+ /** Creates a new MSS parser. */
36
+ constructor() {
37
+ /** @private {?shaka.extern.ManifestConfiguration} */
38
+ this.config_ = null;
39
+
40
+ /** @private {?shaka.extern.ManifestParser.PlayerInterface} */
41
+ this.playerInterface_ = null;
42
+
43
+ /** @private {!Array.<string>} */
44
+ this.manifestUris_ = [];
45
+
46
+ /** @private {?shaka.extern.Manifest} */
47
+ this.manifest_ = null;
48
+
49
+ /** @private {number} */
50
+ this.globalId_ = 1;
51
+
52
+ /**
53
+ * The update period in seconds, or 0 for no updates.
54
+ * @private {number}
55
+ */
56
+ this.updatePeriod_ = 0;
57
+
58
+ /** @private {?shaka.media.PresentationTimeline} */
59
+ this.presentationTimeline_ = null;
60
+
61
+ /**
62
+ * An ewma that tracks how long updates take.
63
+ * This is to mitigate issues caused by slow parsing on embedded devices.
64
+ * @private {!shaka.abr.Ewma}
65
+ */
66
+ this.averageUpdateDuration_ = new shaka.abr.Ewma(5);
67
+
68
+ /** @private {shaka.util.Timer} */
69
+ this.updateTimer_ = new shaka.util.Timer(() => {
70
+ this.onUpdate_();
71
+ });
72
+
73
+ /** @private {!shaka.util.OperationManager} */
74
+ this.operationManager_ = new shaka.util.OperationManager();
75
+
76
+ /**
77
+ * @private {!Map.<number, !BufferSource>}
78
+ */
79
+ this.initSegmentDataByStreamId_ = new Map();
80
+ }
81
+
82
+ /**
83
+ * @override
84
+ * @exportInterface
85
+ */
86
+ configure(config) {
87
+ goog.asserts.assert(config.mss != null,
88
+ 'MssManifestConfiguration should not be null!');
89
+
90
+ this.config_ = config;
91
+ }
92
+
93
+ /**
94
+ * @override
95
+ * @exportInterface
96
+ */
97
+ async start(uri, playerInterface) {
98
+ goog.asserts.assert(this.config_, 'Must call configure() before start()!');
99
+ this.manifestUris_ = [uri];
100
+ this.playerInterface_ = playerInterface;
101
+
102
+ await this.requestManifest_();
103
+
104
+ // Make sure that the parser has not been destroyed.
105
+ if (!this.playerInterface_) {
106
+ throw new shaka.util.Error(
107
+ shaka.util.Error.Severity.CRITICAL,
108
+ shaka.util.Error.Category.PLAYER,
109
+ shaka.util.Error.Code.OPERATION_ABORTED);
110
+ }
111
+
112
+ this.setUpdateTimer_();
113
+
114
+ goog.asserts.assert(this.manifest_, 'Manifest should be non-null!');
115
+ return this.manifest_;
116
+ }
117
+
118
+ /**
119
+ * Called when the update timer ticks.
120
+ *
121
+ * @return {!Promise}
122
+ * @private
123
+ */
124
+ async onUpdate_() {
125
+ goog.asserts.assert(this.updatePeriod_ >= 0,
126
+ 'There should be an update period');
127
+
128
+ shaka.log.info('Updating manifest...');
129
+
130
+ try {
131
+ await this.requestManifest_();
132
+ } catch (error) {
133
+ goog.asserts.assert(error instanceof shaka.util.Error,
134
+ 'Should only receive a Shaka error');
135
+
136
+ // Try updating again, but ensure we haven't been destroyed.
137
+ if (this.playerInterface_) {
138
+ // We will retry updating, so override the severity of the error.
139
+ error.severity = shaka.util.Error.Severity.RECOVERABLE;
140
+ this.playerInterface_.onError(error);
141
+ }
142
+ }
143
+
144
+ // Detect a call to stop()
145
+ if (!this.playerInterface_) {
146
+ return;
147
+ }
148
+
149
+ this.setUpdateTimer_();
150
+ }
151
+
152
+ /**
153
+ * Sets the update timer. Does nothing if the manifest is not live.
154
+ *
155
+ * @private
156
+ */
157
+ setUpdateTimer_() {
158
+ if (this.updatePeriod_ <= 0) {
159
+ return;
160
+ }
161
+
162
+ const finalDelay = Math.max(
163
+ shaka.mss.MssParser.MIN_UPDATE_PERIOD_,
164
+ this.updatePeriod_,
165
+ this.averageUpdateDuration_.getEstimate());
166
+
167
+ // We do not run the timer as repeating because part of update is async and
168
+ // we need schedule the update after it finished.
169
+ this.updateTimer_.tickAfter(/* seconds= */ finalDelay);
170
+ }
171
+
172
+ /**
173
+ * @override
174
+ * @exportInterface
175
+ */
176
+ stop() {
177
+ this.playerInterface_ = null;
178
+ this.config_ = null;
179
+ this.manifestUris_ = [];
180
+ this.manifest_ = null;
181
+
182
+ if (this.updateTimer_ != null) {
183
+ this.updateTimer_.stop();
184
+ this.updateTimer_ = null;
185
+ }
186
+
187
+ this.initSegmentDataByStreamId_.clear();
188
+
189
+ return this.operationManager_.destroy();
190
+ }
191
+
192
+ /**
193
+ * @override
194
+ * @exportInterface
195
+ */
196
+ async update() {
197
+ try {
198
+ await this.requestManifest_();
199
+ } catch (error) {
200
+ if (!this.playerInterface_ || !error) {
201
+ return;
202
+ }
203
+ goog.asserts.assert(error instanceof shaka.util.Error, 'Bad error type');
204
+ this.playerInterface_.onError(error);
205
+ }
206
+ }
207
+
208
+ /**
209
+ * @override
210
+ * @exportInterface
211
+ */
212
+ onExpirationUpdated(sessionId, expiration) {
213
+ // No-op
214
+ }
215
+
216
+ /**
217
+ * Makes a network request for the manifest and parses the resulting data.
218
+ *
219
+ * @private
220
+ */
221
+ async requestManifest_() {
222
+ const requestType = shaka.net.NetworkingEngine.RequestType.MANIFEST;
223
+ const type = shaka.net.NetworkingEngine.AdvancedRequestType.MSS;
224
+ const request = shaka.net.NetworkingEngine.makeRequest(
225
+ this.manifestUris_, this.config_.retryParameters);
226
+ const networkingEngine = this.playerInterface_.networkingEngine;
227
+
228
+ const startTime = Date.now();
229
+ const operation = networkingEngine.request(requestType, request, {type});
230
+ this.operationManager_.manage(operation);
231
+
232
+ const response = await operation.promise;
233
+
234
+ // Detect calls to stop().
235
+ if (!this.playerInterface_) {
236
+ return;
237
+ }
238
+
239
+ // For redirections add the response uri to the first entry in the
240
+ // Manifest Uris array.
241
+ if (response.uri && !this.manifestUris_.includes(response.uri)) {
242
+ this.manifestUris_.unshift(response.uri);
243
+ }
244
+
245
+ // This may throw, but it will result in a failed promise.
246
+ this.parseManifest_(response.data, response.uri);
247
+ // Keep track of how long the longest manifest update took.
248
+ const endTime = Date.now();
249
+ const updateDuration = (endTime - startTime) / 1000.0;
250
+ this.averageUpdateDuration_.sample(1, updateDuration);
251
+ }
252
+
253
+ /**
254
+ * Parses the manifest XML. This also handles updates and will update the
255
+ * stored manifest.
256
+ *
257
+ * @param {BufferSource} data
258
+ * @param {string} finalManifestUri The final manifest URI, which may
259
+ * differ from this.manifestUri_ if there has been a redirect.
260
+ * @return {!Promise}
261
+ * @private
262
+ */
263
+ parseManifest_(data, finalManifestUri) {
264
+ const mss = shaka.util.XmlUtils.parseXml(data, 'SmoothStreamingMedia');
265
+ if (!mss) {
266
+ throw new shaka.util.Error(
267
+ shaka.util.Error.Severity.CRITICAL,
268
+ shaka.util.Error.Category.MANIFEST,
269
+ shaka.util.Error.Code.MSS_INVALID_XML,
270
+ finalManifestUri);
271
+ }
272
+ this.processManifest_(mss, finalManifestUri);
273
+ return Promise.resolve();
274
+ }
275
+
276
+
277
+ /**
278
+ * Takes a formatted MSS and converts it into a manifest.
279
+ *
280
+ * @param {!Element} mss
281
+ * @param {string} finalManifestUri The final manifest URI, which may
282
+ * differ from this.manifestUri_ if there has been a redirect.
283
+ * @private
284
+ */
285
+ processManifest_(mss, finalManifestUri) {
286
+ const XmlUtils = shaka.util.XmlUtils;
287
+
288
+ const manifestPreprocessor = this.config_.mss.manifestPreprocessor;
289
+ if (manifestPreprocessor) {
290
+ manifestPreprocessor(mss);
291
+ }
292
+
293
+ if (!this.presentationTimeline_) {
294
+ this.presentationTimeline_ = new shaka.media.PresentationTimeline(
295
+ /* presentationStartTime= */ null, /* delay= */ 0);
296
+ }
297
+
298
+ const isLive = XmlUtils.parseAttr(mss, 'IsLive',
299
+ XmlUtils.parseBoolean, /* defaultValue= */ false);
300
+
301
+ if (isLive) {
302
+ throw new shaka.util.Error(
303
+ shaka.util.Error.Severity.CRITICAL,
304
+ shaka.util.Error.Category.MANIFEST,
305
+ shaka.util.Error.Code.MSS_LIVE_CONTENT_NOT_SUPPORTED);
306
+ }
307
+
308
+ this.presentationTimeline_.setStatic(!isLive);
309
+
310
+ const timescale = XmlUtils.parseAttr(mss, 'TimeScale',
311
+ XmlUtils.parseNonNegativeInt, shaka.mss.MssParser.DEFAULT_TIME_SCALE_);
312
+ goog.asserts.assert(timescale && timescale >= 0,
313
+ 'Timescale must be defined!');
314
+
315
+ let dvrWindowLength = XmlUtils.parseAttr(mss, 'DVRWindowLength',
316
+ XmlUtils.parseNonNegativeInt);
317
+ // If the DVRWindowLength field is omitted for a live presentation or set
318
+ // to 0, the DVR window is effectively infinite
319
+ if (isLive && (dvrWindowLength === 0 || isNaN(dvrWindowLength))) {
320
+ dvrWindowLength = Infinity;
321
+ }
322
+ // Start-over
323
+ const canSeek = XmlUtils.parseAttr(mss, 'CanSeek',
324
+ XmlUtils.parseBoolean, /* defaultValue= */ false);
325
+ if (dvrWindowLength === 0 && canSeek) {
326
+ dvrWindowLength = Infinity;
327
+ }
328
+
329
+ let segmentAvailabilityDuration = null;
330
+ if (dvrWindowLength && dvrWindowLength > 0) {
331
+ segmentAvailabilityDuration = dvrWindowLength / timescale;
332
+ }
333
+
334
+ // If it's live, we check for an override.
335
+ if (isLive && !isNaN(this.config_.availabilityWindowOverride)) {
336
+ segmentAvailabilityDuration = this.config_.availabilityWindowOverride;
337
+ }
338
+
339
+ // If it's null, that means segments are always available. This is always
340
+ // the case for VOD, and sometimes the case for live.
341
+ if (segmentAvailabilityDuration == null) {
342
+ segmentAvailabilityDuration = Infinity;
343
+ }
344
+
345
+ this.presentationTimeline_.setSegmentAvailabilityDuration(
346
+ segmentAvailabilityDuration);
347
+
348
+ // Duration in timescale units.
349
+ const duration = XmlUtils.parseAttr(mss, 'Duration',
350
+ XmlUtils.parseNonNegativeInt, Infinity);
351
+ goog.asserts.assert(duration && duration >= 0,
352
+ 'Duration must be defined!');
353
+
354
+ if (!isLive) {
355
+ this.presentationTimeline_.setDuration(duration / timescale);
356
+ }
357
+
358
+ /** @type {!shaka.mss.MssParser.Context} */
359
+ const context = {
360
+ variants: [],
361
+ textStreams: [],
362
+ timescale: timescale,
363
+ duration: duration / timescale,
364
+ };
365
+
366
+ this.parseStreamIndexes_(mss, context);
367
+
368
+ // These steps are not done on manifest update.
369
+ if (!this.manifest_) {
370
+ this.manifest_ = {
371
+ presentationTimeline: this.presentationTimeline_,
372
+ variants: context.variants,
373
+ textStreams: context.textStreams,
374
+ imageStreams: [],
375
+ offlineSessionIds: [],
376
+ minBufferTime: 0,
377
+ sequenceMode: this.config_.mss.sequenceMode,
378
+ ignoreManifestTimestampsInSegmentsMode: false,
379
+ type: shaka.media.ManifestParser.MSS,
380
+ serviceDescription: null,
381
+ };
382
+
383
+ // This is the first point where we have a meaningful presentation start
384
+ // time, and we need to tell PresentationTimeline that so that it can
385
+ // maintain consistency from here on.
386
+ this.presentationTimeline_.lockStartTime();
387
+ } else {
388
+ // Just update the variants and text streams.
389
+ this.manifest_.variants = context.variants;
390
+ this.manifest_.textStreams = context.textStreams;
391
+
392
+ // Re-filter the manifest. This will check any configured restrictions on
393
+ // new variants, and will pass any new init data to DrmEngine to ensure
394
+ // that key rotation works correctly.
395
+ this.playerInterface_.filter(this.manifest_);
396
+ }
397
+ }
398
+
399
+ /**
400
+ * @param {!Element} mss
401
+ * @param {!shaka.mss.MssParser.Context} context
402
+ * @private
403
+ */
404
+ parseStreamIndexes_(mss, context) {
405
+ const ContentProtection = shaka.mss.ContentProtection;
406
+ const XmlUtils = shaka.util.XmlUtils;
407
+ const ContentType = shaka.util.ManifestParserUtils.ContentType;
408
+
409
+ const protectionElems = XmlUtils.findChildren(mss, 'Protection');
410
+ const drmInfos = ContentProtection.parseFromProtection(
411
+ protectionElems, this.config_.mss.keySystemsBySystemId);
412
+
413
+ const audioStreams = [];
414
+ const videoStreams = [];
415
+ const textStreams = [];
416
+ const streamIndexes = XmlUtils.findChildren(mss, 'StreamIndex');
417
+ for (const streamIndex of streamIndexes) {
418
+ const qualityLevels = XmlUtils.findChildren(streamIndex, 'QualityLevel');
419
+ const timeline = this.createTimeline_(
420
+ streamIndex, context.timescale, context.duration);
421
+ // For each QualityLevel node, create a stream element
422
+ for (const qualityLevel of qualityLevels) {
423
+ const stream = this.createStream_(
424
+ streamIndex, qualityLevel, timeline, drmInfos, context);
425
+ if (!stream) {
426
+ // Skip unsupported stream
427
+ continue;
428
+ }
429
+ if (stream.type == ContentType.AUDIO &&
430
+ !this.config_.disableAudio) {
431
+ audioStreams.push(stream);
432
+ } else if (stream.type == ContentType.VIDEO &&
433
+ !this.config_.disableVideo) {
434
+ videoStreams.push(stream);
435
+ } else if (stream.type == ContentType.TEXT &&
436
+ !this.config_.disableText) {
437
+ textStreams.push(stream);
438
+ }
439
+ }
440
+ }
441
+
442
+ const variants = [];
443
+ for (const audio of (audioStreams.length > 0 ? audioStreams : [null])) {
444
+ for (const video of (videoStreams.length > 0 ? videoStreams : [null])) {
445
+ variants.push(this.createVariant_(audio, video));
446
+ }
447
+ }
448
+ context.variants = variants;
449
+ context.textStreams = textStreams;
450
+ }
451
+
452
+ /**
453
+ * @param {!Element} streamIndex
454
+ * @param {!Element} qualityLevel
455
+ * @param {!Array.<shaka.mss.MssParser.TimeRange>} timeline
456
+ * @param {!Array.<shaka.extern.DrmInfo>} drmInfos
457
+ * @param {!shaka.mss.MssParser.Context} context
458
+ * @return {?shaka.extern.Stream}
459
+ * @private
460
+ */
461
+ createStream_(streamIndex, qualityLevel, timeline, drmInfos, context) {
462
+ const XmlUtils = shaka.util.XmlUtils;
463
+ const ContentType = shaka.util.ManifestParserUtils.ContentType;
464
+ const MssParser = shaka.mss.MssParser;
465
+
466
+ const type = streamIndex.getAttribute('Type');
467
+ const isValidType = type === 'audio' || type === 'video' ||
468
+ type === 'text';
469
+ if (!isValidType) {
470
+ shaka.log.alwaysWarn('Ignoring unrecognized type:', type);
471
+ return null;
472
+ }
473
+
474
+ const lang = streamIndex.getAttribute('Language');
475
+ const id = this.globalId_++;
476
+
477
+ const bandwidth = XmlUtils.parseAttr(
478
+ qualityLevel, 'Bitrate', XmlUtils.parsePositiveInt);
479
+ const width = XmlUtils.parseAttr(
480
+ qualityLevel, 'MaxWidth', XmlUtils.parsePositiveInt);
481
+ const height = XmlUtils.parseAttr(
482
+ qualityLevel, 'MaxHeight', XmlUtils.parsePositiveInt);
483
+ const channelsCount = XmlUtils.parseAttr(
484
+ qualityLevel, 'Channels', XmlUtils.parsePositiveInt);
485
+ const audioSamplingRate = XmlUtils.parseAttr(
486
+ qualityLevel, 'SamplingRate', XmlUtils.parsePositiveInt);
487
+
488
+ let duration = context.duration;
489
+ if (timeline.length) {
490
+ const start = timeline[0].start;
491
+ const end = timeline[timeline.length - 1].end;
492
+ duration = end - start;
493
+ }
494
+
495
+ const presentationDuration = this.presentationTimeline_.getDuration();
496
+ this.presentationTimeline_.setDuration(
497
+ Math.min(duration, presentationDuration));
498
+
499
+ /** @type {!shaka.extern.Stream} */
500
+ const stream = {
501
+ id: id,
502
+ originalId: streamIndex.getAttribute('Name') || String(id),
503
+ createSegmentIndex: () => Promise.resolve(),
504
+ closeSegmentIndex: () => Promise.resolve(),
505
+ segmentIndex: null,
506
+ mimeType: '',
507
+ codecs: '',
508
+ frameRate: undefined,
509
+ pixelAspectRatio: undefined,
510
+ bandwidth: bandwidth || 0,
511
+ width: width || undefined,
512
+ height: height || undefined,
513
+ kind: '',
514
+ encrypted: drmInfos.length > 0,
515
+ drmInfos: drmInfos,
516
+ keyIds: new Set(),
517
+ language: shaka.util.LanguageUtils.normalize(lang || 'und'),
518
+ originalLanguage: lang,
519
+ label: '',
520
+ type: '',
521
+ primary: false,
522
+ trickModeVideo: null,
523
+ emsgSchemeIdUris: [],
524
+ roles: [],
525
+ forced: false,
526
+ channelsCount: channelsCount,
527
+ audioSamplingRate: audioSamplingRate,
528
+ spatialAudio: false,
529
+ closedCaptions: null,
530
+ hdr: undefined,
531
+ tilesLayout: undefined,
532
+ matchedStreams: [],
533
+ mssPrivateData: {
534
+ duration: duration,
535
+ timescale: context.timescale,
536
+ codecPrivateData: null,
537
+ },
538
+ accessibilityPurpose: null,
539
+ external: false,
540
+ };
541
+
542
+ // This is specifically for text tracks.
543
+ const subType = streamIndex.getAttribute('Subtype');
544
+ if (subType) {
545
+ const role = MssParser.ROLE_MAPPING_[subType];
546
+ if (role) {
547
+ stream.roles.push(role);
548
+ }
549
+ if (role === 'main') {
550
+ stream.primary = true;
551
+ }
552
+ }
553
+
554
+ let fourCCValue = qualityLevel.getAttribute('FourCC');
555
+
556
+ // If FourCC not defined at QualityLevel level,
557
+ // then get it from StreamIndex level
558
+ if (fourCCValue === null || fourCCValue === '') {
559
+ fourCCValue = streamIndex.getAttribute('FourCC');
560
+ }
561
+
562
+ // If still not defined (optional for audio stream,
563
+ // see https://msdn.microsoft.com/en-us/library/ff728116%28v=vs.95%29.aspx),
564
+ // then we consider the stream is an audio AAC stream
565
+ if (!fourCCValue) {
566
+ if (type === 'audio') {
567
+ fourCCValue = 'AAC';
568
+ } else if (type === 'video') {
569
+ shaka.log.alwaysWarn('FourCC is not defined whereas it is required ' +
570
+ 'for a QualityLevel element for a StreamIndex of type "video"');
571
+ return null;
572
+ }
573
+ }
574
+
575
+ // Check if codec is supported
576
+ if (!MssParser.SUPPORTED_CODECS_.includes(fourCCValue.toUpperCase())) {
577
+ shaka.log.alwaysWarn('Codec not supported:', fourCCValue);
578
+ return null;
579
+ }
580
+
581
+ const codecPrivateData = this.getCodecPrivateData_(
582
+ qualityLevel, type, fourCCValue, stream);
583
+ stream.mssPrivateData.codecPrivateData = codecPrivateData;
584
+
585
+ switch (type) {
586
+ case 'audio':
587
+ if (!codecPrivateData) {
588
+ shaka.log.alwaysWarn('Quality unsupported without CodecPrivateData',
589
+ type);
590
+ return null;
591
+ }
592
+ stream.type = ContentType.AUDIO;
593
+ // This mimetype is fake to allow the transmuxing.
594
+ stream.mimeType = 'mss/audio/mp4';
595
+ stream.codecs = this.getAACCodec_(
596
+ qualityLevel, fourCCValue, codecPrivateData);
597
+ break;
598
+ case 'video':
599
+ if (!codecPrivateData) {
600
+ shaka.log.alwaysWarn('Quality unsupported without CodecPrivateData',
601
+ type);
602
+ return null;
603
+ }
604
+ stream.type = ContentType.VIDEO;
605
+ // This mimetype is fake to allow the transmuxing.
606
+ stream.mimeType = 'mss/video/mp4';
607
+ stream.codecs = this.getH264Codec_(
608
+ qualityLevel, codecPrivateData);
609
+ break;
610
+ case 'text':
611
+ stream.type = ContentType.TEXT;
612
+ stream.mimeType = 'application/mp4';
613
+ if (fourCCValue === 'TTML' || fourCCValue === 'DFXP') {
614
+ stream.codecs = 'stpp';
615
+ }
616
+ break;
617
+ }
618
+
619
+ // Lazy-Load the segment index to avoid create all init segment at the
620
+ // same time
621
+ stream.createSegmentIndex = () => {
622
+ if (stream.segmentIndex) {
623
+ return Promise.resolve();
624
+ }
625
+ let initSegmentData;
626
+ if (this.initSegmentDataByStreamId_.has(stream.id)) {
627
+ initSegmentData = this.initSegmentDataByStreamId_.get(stream.id);
628
+ } else {
629
+ let videoNalus = [];
630
+ if (stream.type == ContentType.VIDEO) {
631
+ const codecPrivateData = stream.mssPrivateData.codecPrivateData;
632
+ videoNalus = codecPrivateData.split('00000001').slice(1);
633
+ }
634
+ /** @type {shaka.util.Mp4Generator.StreamInfo} */
635
+ const streamInfo = {
636
+ id: stream.id,
637
+ type: stream.type,
638
+ encrypted: stream.encrypted,
639
+ timescale: stream.mssPrivateData.timescale,
640
+ duration: stream.mssPrivateData.duration,
641
+ videoNalus: videoNalus,
642
+ audioConfig: new Uint8Array([]),
643
+ videoConfig: new Uint8Array([]),
644
+ data: null, // Data is not necessary for init segement.
645
+ stream: stream,
646
+ };
647
+ const mp4Generator = new shaka.util.Mp4Generator([streamInfo]);
648
+ initSegmentData = mp4Generator.initSegment();
649
+ this.initSegmentDataByStreamId_.set(stream.id, initSegmentData);
650
+ }
651
+ const initSegmentRef = new shaka.media.InitSegmentReference(
652
+ () => [],
653
+ /* startByte= */ 0,
654
+ /* endByte= */ null,
655
+ /* mediaQuality= */ null,
656
+ /* timescale= */ undefined,
657
+ initSegmentData);
658
+
659
+ const segments = this.createSegments_(initSegmentRef,
660
+ stream, streamIndex, timeline);
661
+
662
+ stream.segmentIndex = new shaka.media.SegmentIndex(segments);
663
+ return Promise.resolve();
664
+ };
665
+ stream.closeSegmentIndex = () => {
666
+ // If we have a segment index, release it.
667
+ if (stream.segmentIndex) {
668
+ stream.segmentIndex.release();
669
+ stream.segmentIndex = null;
670
+ }
671
+ };
672
+
673
+ return stream;
674
+ }
675
+
676
+ /**
677
+ * @param {!Element} qualityLevel
678
+ * @param {string} type
679
+ * @param {string} fourCCValue
680
+ * @param {!shaka.extern.Stream} stream
681
+ * @return {?string}
682
+ * @private
683
+ */
684
+ getCodecPrivateData_(qualityLevel, type, fourCCValue, stream) {
685
+ const codecPrivateData = qualityLevel.getAttribute('CodecPrivateData');
686
+ if (codecPrivateData) {
687
+ return codecPrivateData;
688
+ }
689
+ if (type !== 'audio') {
690
+ return null;
691
+ }
692
+ // For the audio we can reconstruct the CodecPrivateData
693
+ // By default stereo
694
+ const channels = stream.channelsCount || 2;
695
+ // By default 44,1kHz.
696
+ const samplingRate = stream.audioSamplingRate || 44100;
697
+
698
+ const samplingFrequencyIndex = {
699
+ 96000: 0x0,
700
+ 88200: 0x1,
701
+ 64000: 0x2,
702
+ 48000: 0x3,
703
+ 44100: 0x4,
704
+ 32000: 0x5,
705
+ 24000: 0x6,
706
+ 22050: 0x7,
707
+ 16000: 0x8,
708
+ 12000: 0x9,
709
+ 11025: 0xA,
710
+ 8000: 0xB,
711
+ 7350: 0xC,
712
+ };
713
+
714
+ const indexFreq = samplingFrequencyIndex[samplingRate];
715
+ if (fourCCValue === 'AACH') {
716
+ // High Efficiency AAC Profile
717
+ const objectType = 0x05;
718
+ // 4 bytes :
719
+ // XXXXX XXXX XXXX XXXX
720
+ // 'ObjectType' 'Freq Index' 'Channels value' 'Extens Sampl Freq'
721
+ // XXXXX XXX XXXXXXX
722
+ // 'ObjectType' 'GAS' 'alignment = 0'
723
+ const data = new Uint8Array(4);
724
+ // In HE AAC Extension Sampling frequence
725
+ // equals to SamplingRate * 2
726
+ const extensionSamplingFrequencyIndex =
727
+ samplingFrequencyIndex[samplingRate * 2];
728
+ // Freq Index is present for 3 bits in the first byte, last bit is in
729
+ // the second
730
+ data[0] = (objectType << 3) | (indexFreq >> 1);
731
+ data[1] = (indexFreq << 7) | (channels << 3) |
732
+ (extensionSamplingFrequencyIndex >> 1);
733
+ // Origin object type equals to 2 => AAC Main Low Complexity
734
+ data[2] = (extensionSamplingFrequencyIndex << 7) | (0x02 << 2);
735
+ // Slignment bits
736
+ data[3] = 0x0;
737
+ // Put the 4 bytes in an 16 bits array
738
+ const arr16 = new Uint16Array(2);
739
+ arr16[0] = (data[0] << 8) + data[1];
740
+ arr16[1] = (data[2] << 8) + data[3];
741
+ // Convert decimal to hex value
742
+ return arr16[0].toString(16) + arr16[1].toString(16);
743
+ } else {
744
+ // AAC Main Low Complexity
745
+ const objectType = 0x02;
746
+ // 2 bytes:
747
+ // XXXXX XXXX XXXX XXX
748
+ // 'ObjectType' 'Freq Index' 'Channels value' 'GAS = 000'
749
+ const data = new Uint8Array(2);
750
+ // Freq Index is present for 3 bits in the first byte, last bit is in
751
+ // the second
752
+ data[0] = (objectType << 3) | (indexFreq >> 1);
753
+ data[1] = (indexFreq << 7) | (channels << 3);
754
+ // Put the 2 bytes in an 16 bits array
755
+ const arr16 = new Uint16Array(1);
756
+ arr16[0] = (data[0] << 8) + data[1];
757
+ // Convert decimal to hex value
758
+ return arr16[0].toString(16);
759
+ }
760
+ }
761
+
762
+ /**
763
+ * @param {!Element} qualityLevel
764
+ * @param {string} fourCCValue
765
+ * @param {?string} codecPrivateData
766
+ * @return {string}
767
+ * @private
768
+ */
769
+ getAACCodec_(qualityLevel, fourCCValue, codecPrivateData) {
770
+ let objectType = 0;
771
+
772
+ // Chrome problem, in implicit AAC HE definition, so when AACH is detected
773
+ // in FourCC set objectType to 5 => strange, it should be 2
774
+ if (fourCCValue === 'AACH') {
775
+ objectType = 0x05;
776
+ }
777
+ if (!codecPrivateData) {
778
+ // AAC Main Low Complexity => object Type = 2
779
+ objectType = 0x02;
780
+ if (fourCCValue === 'AACH') {
781
+ // High Efficiency AAC Profile = object Type = 5 SBR
782
+ objectType = 0x05;
783
+ }
784
+ } else if (objectType === 0) {
785
+ objectType = (parseInt(codecPrivateData.substr(0, 2), 16) & 0xF8) >> 3;
786
+ }
787
+
788
+ return 'mp4a.40.' + objectType;
789
+ }
790
+
791
+ /**
792
+ * @param {!Element} qualityLevel
793
+ * @param {?string} codecPrivateData
794
+ * @return {string}
795
+ * @private
796
+ */
797
+ getH264Codec_(qualityLevel, codecPrivateData) {
798
+ // Extract from the CodecPrivateData field the hexadecimal representation
799
+ // of the following three bytes in the sequence parameter set NAL unit.
800
+ // => Find the SPS nal header
801
+ const nalHeader = /00000001[0-9]7/.exec(codecPrivateData);
802
+ if (!nalHeader.length) {
803
+ return '';
804
+ }
805
+ if (!codecPrivateData) {
806
+ return '';
807
+ }
808
+ // => Find the 6 characters after the SPS nalHeader (if it exists)
809
+ const avcoti = codecPrivateData.substr(
810
+ codecPrivateData.indexOf(nalHeader[0]) + 10, 6);
811
+
812
+ return 'avc1.' + avcoti;
813
+ }
814
+
815
+ /**
816
+ * @param {!shaka.media.InitSegmentReference} initSegmentRef
817
+ * @param {!shaka.extern.Stream} stream
818
+ * @param {!Element} streamIndex
819
+ * @param {!Array.<shaka.mss.MssParser.TimeRange>} timeline
820
+ * @return {!Array.<!shaka.media.SegmentReference>}
821
+ * @private
822
+ */
823
+ createSegments_(initSegmentRef, stream, streamIndex, timeline) {
824
+ const ManifestParserUtils = shaka.util.ManifestParserUtils;
825
+ const url = streamIndex.getAttribute('Url');
826
+ goog.asserts.assert(url, 'Missing URL for segments');
827
+
828
+ const mediaUrl = url.replace('{bitrate}', String(stream.bandwidth));
829
+
830
+ const segments = [];
831
+ for (const time of timeline) {
832
+ const getUris = () => {
833
+ return ManifestParserUtils.resolveUris(this.manifestUris_,
834
+ [mediaUrl.replace('{start time}', String(time.unscaledStart))]);
835
+ };
836
+ segments.push(new shaka.media.SegmentReference(
837
+ time.start,
838
+ time.end,
839
+ getUris,
840
+ /* startByte= */ 0,
841
+ /* endByte= */ null,
842
+ initSegmentRef,
843
+ /* timestampOffset= */ 0,
844
+ /* appendWindowStart= */ 0,
845
+ /* appendWindowEnd= */ stream.mssPrivateData.duration));
846
+ }
847
+ return segments;
848
+ }
849
+
850
+ /**
851
+ * Expands a streamIndex into an array-based timeline. The results are in
852
+ * seconds.
853
+ *
854
+ * @param {!Element} streamIndex
855
+ * @param {number} timescale
856
+ * @param {number} duration The duration in seconds.
857
+ * @return {!Array.<shaka.mss.MssParser.TimeRange>}
858
+ * @private
859
+ */
860
+ createTimeline_(streamIndex, timescale, duration) {
861
+ goog.asserts.assert(
862
+ timescale > 0 && timescale < Infinity,
863
+ 'timescale must be a positive, finite integer');
864
+ goog.asserts.assert(
865
+ duration > 0, 'duration must be a positive integer');
866
+
867
+ const XmlUtils = shaka.util.XmlUtils;
868
+
869
+ const timePoints = XmlUtils.findChildren(streamIndex, 'c');
870
+
871
+ /** @type {!Array.<shaka.mss.MssParser.TimeRange>} */
872
+ const timeline = [];
873
+ let lastEndTime = 0;
874
+
875
+ for (let i = 0; i < timePoints.length; ++i) {
876
+ const timePoint = timePoints[i];
877
+ const next = timePoints[i + 1];
878
+ const t =
879
+ XmlUtils.parseAttr(timePoint, 't', XmlUtils.parseNonNegativeInt);
880
+ const d =
881
+ XmlUtils.parseAttr(timePoint, 'd', XmlUtils.parseNonNegativeInt);
882
+ const r = XmlUtils.parseAttr(timePoint, 'r', XmlUtils.parseInt);
883
+
884
+ if (!d) {
885
+ shaka.log.warning(
886
+ '"c" element must have a duration:',
887
+ 'ignoring the remaining "c" elements.', timePoint);
888
+ return timeline;
889
+ }
890
+
891
+ let startTime = t != null ? t : lastEndTime;
892
+
893
+ let repeat = r || 0;
894
+ if (repeat < 0) {
895
+ if (next) {
896
+ const nextStartTime =
897
+ XmlUtils.parseAttr(next, 't', XmlUtils.parseNonNegativeInt);
898
+ if (nextStartTime == null) {
899
+ shaka.log.warning(
900
+ 'An "c" element cannot have a negative repeat',
901
+ 'if the next "c" element does not have a valid start time:',
902
+ 'ignoring the remaining "c" elements.', timePoint);
903
+ return timeline;
904
+ } else if (startTime >= nextStartTime) {
905
+ shaka.log.warning(
906
+ 'An "c" element cannot have a negative repeatif its start ',
907
+ 'time exceeds the next "c" element\'s start time:',
908
+ 'ignoring the remaining "c" elements.', timePoint);
909
+ return timeline;
910
+ }
911
+ repeat = Math.ceil((nextStartTime - startTime) / d) - 1;
912
+ } else {
913
+ if (duration == Infinity) {
914
+ // The MSS spec. actually allows the last "c" element to have a
915
+ // negative repeat value even when it has an infinite
916
+ // duration. No one uses this feature and no one ever should,
917
+ // ever.
918
+ shaka.log.warning(
919
+ 'The last "c" element cannot have a negative repeat',
920
+ 'if the Period has an infinite duration:',
921
+ 'ignoring the last "c" element.', timePoint);
922
+ return timeline;
923
+ } else if (startTime / timescale >= duration) {
924
+ shaka.log.warning(
925
+ 'The last "c" element cannot have a negative repeat',
926
+ 'if its start time exceeds the duration:',
927
+ 'igoring the last "c" element.', timePoint);
928
+ return timeline;
929
+ }
930
+ repeat = Math.ceil((duration * timescale - startTime) / d) - 1;
931
+ }
932
+ }
933
+
934
+ for (let j = 0; j <= repeat; ++j) {
935
+ const endTime = startTime + d;
936
+ const item = {
937
+ start: startTime / timescale,
938
+ end: endTime / timescale,
939
+ unscaledStart: startTime,
940
+ };
941
+ timeline.push(item);
942
+
943
+ startTime = endTime;
944
+ lastEndTime = endTime;
945
+ }
946
+ }
947
+
948
+ return timeline;
949
+ }
950
+
951
+ /**
952
+ * @param {?shaka.extern.Stream} audioStream
953
+ * @param {?shaka.extern.Stream} videoStream
954
+ * @return {!shaka.extern.Variant}
955
+ * @private
956
+ */
957
+ createVariant_(audioStream, videoStream) {
958
+ const ContentType = shaka.util.ManifestParserUtils.ContentType;
959
+
960
+ goog.asserts.assert(!audioStream ||
961
+ audioStream.type == ContentType.AUDIO, 'Audio parameter mismatch!');
962
+ goog.asserts.assert(!videoStream ||
963
+ videoStream.type == ContentType.VIDEO, 'Video parameter mismatch!');
964
+
965
+ let bandwidth = 0;
966
+ if (audioStream && audioStream.bandwidth && audioStream.bandwidth > 0) {
967
+ bandwidth += audioStream.bandwidth;
968
+ }
969
+ if (videoStream && videoStream.bandwidth && videoStream.bandwidth > 0) {
970
+ bandwidth += videoStream.bandwidth;
971
+ }
972
+
973
+ return {
974
+ id: this.globalId_++,
975
+ language: audioStream ? audioStream.language : 'und',
976
+ disabledUntilTime: 0,
977
+ primary: (!!audioStream && audioStream.primary) ||
978
+ (!!videoStream && videoStream.primary),
979
+ audio: audioStream,
980
+ video: videoStream,
981
+ bandwidth: bandwidth,
982
+ allowedByApplication: true,
983
+ allowedByKeySystem: true,
984
+ decodingInfos: [],
985
+ };
986
+ }
987
+ };
988
+
989
+
990
+ /**
991
+ * Contains the minimum amount of time, in seconds, between manifest update
992
+ * requests.
993
+ *
994
+ * @private
995
+ * @const {number}
996
+ */
997
+ shaka.mss.MssParser.MIN_UPDATE_PERIOD_ = 3;
998
+
999
+
1000
+ /**
1001
+ * @private
1002
+ * @const {number}
1003
+ */
1004
+ shaka.mss.MssParser.DEFAULT_TIME_SCALE_ = 1e7;
1005
+
1006
+
1007
+ /**
1008
+ * MSS supported codecs.
1009
+ *
1010
+ * @private
1011
+ * @const {!Array.<string>}
1012
+ */
1013
+ shaka.mss.MssParser.SUPPORTED_CODECS_ = [
1014
+ 'AAC',
1015
+ 'AACL',
1016
+ 'AACH',
1017
+ 'AACP',
1018
+ 'AVC1',
1019
+ 'H264',
1020
+ 'TTML',
1021
+ 'DFXP',
1022
+ ];
1023
+
1024
+
1025
+ /**
1026
+ * MPEG-DASH Role and accessibility mapping for text tracks according to
1027
+ * ETSI TS 103 285 v1.1.1 (section 7.1.2)
1028
+ *
1029
+ * @const {!Object.<string, string>}
1030
+ * @private
1031
+ */
1032
+ shaka.mss.MssParser.ROLE_MAPPING_ = {
1033
+ 'CAPT': 'main',
1034
+ 'SUBT': 'alternate',
1035
+ 'DESC': 'main',
1036
+ };
1037
+
1038
+
1039
+ /**
1040
+ * @typedef {{
1041
+ * variants: !Array.<shaka.extern.Variant>,
1042
+ * textStreams: !Array.<shaka.extern.Stream>,
1043
+ * timescale: number,
1044
+ * duration: number
1045
+ * }}
1046
+ *
1047
+ * @property {!Array.<shaka.extern.Variant>} variants
1048
+ * The presentation's Variants.
1049
+ * @property {!Array.<shaka.extern.Stream>} textStreams
1050
+ * The presentation's text streams.
1051
+ * @property {number} timescale
1052
+ * The presentation's timescale.
1053
+ * @property {number} duration
1054
+ * The presentation's duration.
1055
+ */
1056
+ shaka.mss.MssParser.Context;
1057
+
1058
+
1059
+ /**
1060
+ * @typedef {{
1061
+ * start: number,
1062
+ * unscaledStart: number,
1063
+ * end: number
1064
+ * }}
1065
+ *
1066
+ * @description
1067
+ * Defines a time range of a media segment. Times are in seconds.
1068
+ *
1069
+ * @property {number} start
1070
+ * The start time of the range.
1071
+ * @property {number} unscaledStart
1072
+ * The start time of the range in representation timescale units.
1073
+ * @property {number} end
1074
+ * The end time (exclusive) of the range.
1075
+ */
1076
+ shaka.mss.MssParser.TimeRange;
1077
+
1078
+ shaka.media.ManifestParser.registerParserByExtension(
1079
+ 'ism', () => new shaka.mss.MssParser());
1080
+ shaka.media.ManifestParser.registerParserByMime(
1081
+ 'application/vnd.ms-sstr+xml', () => new shaka.mss.MssParser());