shaka-player 4.4.3 → 4.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (307) hide show
  1. package/.github/ISSUE_TEMPLATE/bug_report.md +1 -1
  2. package/.github/workflows/release-please.yaml +2 -27
  3. package/AUTHORS +2 -0
  4. package/CHANGELOG.md +156 -39
  5. package/CODE_OF_CONDUCT.md +1 -1
  6. package/CONTRIBUTORS +2 -0
  7. package/README.md +54 -42
  8. package/demo/asset_card.js +18 -21
  9. package/demo/cast_receiver/index.html +0 -1
  10. package/demo/close_button.js +0 -3
  11. package/demo/common/asset.js +33 -22
  12. package/demo/common/assets.js +249 -60
  13. package/demo/config.js +219 -253
  14. package/demo/custom.js +88 -133
  15. package/demo/front.js +7 -10
  16. package/demo/index.html +19 -22
  17. package/demo/input.js +6 -11
  18. package/demo/input_container.js +8 -9
  19. package/demo/main.js +62 -126
  20. package/demo/search.js +29 -34
  21. package/demo/service_worker.js +0 -3
  22. package/demo/tooltip.js +3 -5
  23. package/demo/visualizer.js +3 -5
  24. package/demo/visualizer_button.js +1 -20
  25. package/dist/controls.css +1 -1
  26. package/dist/controls.css.map +1 -1
  27. package/dist/demo.compiled.debug.js +211 -228
  28. package/dist/demo.compiled.debug.map +4 -4
  29. package/dist/demo.compiled.js +211 -228
  30. package/dist/demo.compiled.map +4 -4
  31. package/dist/deps.js +42 -37
  32. package/dist/locales.js +34 -30
  33. package/dist/receiver.compiled.debug.js +64 -77
  34. package/dist/receiver.compiled.debug.map +4 -4
  35. package/dist/receiver.compiled.js +64 -77
  36. package/dist/receiver.compiled.map +4 -4
  37. package/dist/shaka-player.compiled.d.ts +343 -151
  38. package/dist/shaka-player.compiled.debug.d.ts +343 -151
  39. package/dist/shaka-player.compiled.debug.externs.js +356 -117
  40. package/dist/shaka-player.compiled.debug.js +952 -811
  41. package/dist/shaka-player.compiled.debug.map +4 -4
  42. package/dist/shaka-player.compiled.externs.js +356 -117
  43. package/dist/shaka-player.compiled.js +1165 -1078
  44. package/dist/shaka-player.compiled.map +4 -4
  45. package/dist/shaka-player.ui.d.ts +359 -153
  46. package/dist/shaka-player.ui.debug.d.ts +359 -153
  47. package/dist/shaka-player.ui.debug.externs.js +382 -117
  48. package/dist/shaka-player.ui.debug.js +1084 -918
  49. package/dist/shaka-player.ui.debug.map +4 -4
  50. package/dist/shaka-player.ui.externs.js +382 -117
  51. package/dist/shaka-player.ui.js +1377 -1274
  52. package/dist/shaka-player.ui.map +4 -4
  53. package/docs/design/lcevc-integration.md +14 -14
  54. package/docs/tutorials/ad_monetization.md +54 -0
  55. package/docs/tutorials/basic-usage.md +2 -1
  56. package/docs/tutorials/errors.md +2 -1
  57. package/docs/tutorials/faq.md +27 -55
  58. package/docs/tutorials/index.json +2 -1
  59. package/docs/tutorials/lcevc.md +118 -0
  60. package/docs/tutorials/offline.md +10 -8
  61. package/docs/tutorials/plugins.md +5 -0
  62. package/docs/tutorials/ui-customization.md +17 -0
  63. package/docs/tutorials/ui.md +2 -1
  64. package/docs/tutorials/upgrade.md +3 -0
  65. package/externs/css_style_declaration.js +19 -0
  66. package/externs/lcevc.js +37 -13
  67. package/externs/managedmediasource.js +24 -0
  68. package/externs/media_remote.js +68 -0
  69. package/externs/mediatailor.js +193 -0
  70. package/externs/pictureinpicture.js +11 -0
  71. package/externs/shaka/abr_manager.js +6 -2
  72. package/externs/shaka/ads.js +27 -0
  73. package/externs/shaka/cea.js +7 -0
  74. package/externs/shaka/codecs.js +5 -3
  75. package/externs/shaka/manifest.js +31 -10
  76. package/externs/shaka/manifest_parser.js +24 -1
  77. package/externs/shaka/net.js +2 -2
  78. package/externs/shaka/offline.js +13 -2
  79. package/externs/shaka/player.js +65 -8
  80. package/externs/shaka/text.js +3 -1
  81. package/externs/shaka/transmuxer.js +1 -1
  82. package/karma.conf.js +7 -3
  83. package/lib/abr/simple_abr_manager.js +66 -9
  84. package/lib/ads/ad_manager.js +104 -50
  85. package/lib/ads/media_tailor_ad.js +297 -0
  86. package/lib/ads/media_tailor_ad_manager.js +839 -0
  87. package/lib/cast/cast_receiver.js +1 -1
  88. package/lib/cea/cea_decoder.js +28 -0
  89. package/lib/cea/dummy_caption_decoder.js +5 -0
  90. package/lib/cea/ts_cea_parser.js +15 -2
  91. package/lib/config/codec_switching_strategy.js +24 -0
  92. package/lib/dash/content_protection.js +120 -2
  93. package/lib/dash/dash_parser.js +322 -93
  94. package/lib/dash/mpd_utils.js +62 -37
  95. package/lib/dash/segment_base.js +17 -10
  96. package/lib/dash/segment_list.js +18 -9
  97. package/lib/dash/segment_template.js +122 -107
  98. package/lib/dependencies/all.js +8 -7
  99. package/lib/hls/hls_classes.js +28 -43
  100. package/lib/hls/hls_parser.js +798 -658
  101. package/lib/hls/hls_utils.js +64 -5
  102. package/lib/hls/manifest_text_parser.js +7 -11
  103. package/lib/lcevc/lcevc_dec.js +220 -0
  104. package/lib/media/adaptation_set.js +32 -14
  105. package/lib/media/adaptation_set_criteria.js +86 -16
  106. package/lib/media/closed_caption_parser.js +13 -0
  107. package/lib/media/content_workarounds.js +6 -0
  108. package/lib/media/drm_engine.js +8 -13
  109. package/lib/media/manifest_parser.js +8 -113
  110. package/lib/media/media_source_capabilities.js +10 -3
  111. package/lib/media/media_source_engine.js +286 -44
  112. package/lib/media/play_rate_controller.js +8 -38
  113. package/lib/media/presentation_timeline.js +7 -1
  114. package/lib/media/quality_observer.js +1 -1
  115. package/lib/media/segment_index.js +6 -6
  116. package/lib/media/segment_prefetch.js +92 -24
  117. package/lib/media/segment_reference.js +73 -7
  118. package/lib/media/segment_utils.js +343 -0
  119. package/lib/media/stall_detector.js +0 -3
  120. package/lib/media/streaming_engine.js +73 -38
  121. package/lib/mss/mss_parser.js +19 -2
  122. package/lib/net/networking_engine.js +67 -12
  123. package/lib/net/networking_utils.js +95 -0
  124. package/lib/offline/indexeddb/v1_storage_cell.js +3 -0
  125. package/lib/offline/indexeddb/v2_storage_cell.js +3 -0
  126. package/lib/offline/manifest_converter.js +3 -0
  127. package/lib/offline/offline_manifest_parser.js +10 -0
  128. package/lib/offline/storage.js +14 -7
  129. package/lib/player.js +1098 -1537
  130. package/lib/polyfill/mediasource.js +22 -1
  131. package/lib/text/cue.js +15 -0
  132. package/lib/text/mp4_ttml_parser.js +2 -2
  133. package/lib/text/srt_text_parser.js +2 -2
  134. package/lib/text/stub_text_displayer.js +51 -0
  135. package/lib/text/text_engine.js +3 -2
  136. package/lib/text/ttml_text_parser.js +45 -5
  137. package/lib/text/ui_text_displayer.js +8 -0
  138. package/lib/text/vtt_text_parser.js +40 -22
  139. package/lib/transmuxer/aac_transmuxer.js +1 -1
  140. package/lib/transmuxer/ac3_transmuxer.js +1 -1
  141. package/lib/transmuxer/ec3_transmuxer.js +1 -1
  142. package/lib/transmuxer/h264.js +5 -4
  143. package/lib/transmuxer/h265.js +863 -0
  144. package/lib/transmuxer/mp3_transmuxer.js +1 -1
  145. package/lib/transmuxer/mpeg_ts_transmuxer.js +4 -3
  146. package/lib/transmuxer/mss_transmuxer.js +1 -1
  147. package/lib/transmuxer/ts_transmuxer.js +187 -22
  148. package/lib/util/buffer_utils.js +26 -4
  149. package/lib/util/content_steering_manager.js +358 -0
  150. package/lib/util/error.js +30 -15
  151. package/lib/util/exp_golomb.js +30 -2
  152. package/lib/util/fairplay_utils.js +1 -1
  153. package/lib/util/fake_event.js +8 -1
  154. package/lib/util/id3_utils.js +50 -1
  155. package/lib/util/manifest_parser_utils.js +12 -8
  156. package/lib/util/mp4_generator.js +6 -8
  157. package/lib/util/mutex.js +59 -0
  158. package/lib/util/object_utils.js +1 -1
  159. package/lib/util/periods.js +156 -179
  160. package/lib/util/platform.js +44 -3
  161. package/lib/util/player_configuration.js +43 -6
  162. package/lib/util/state_history.js +6 -2
  163. package/lib/util/stream_utils.js +206 -283
  164. package/lib/util/ts_parser.js +335 -98
  165. package/lib/util/uint8array_utils.js +5 -1
  166. package/lib/util/xml_utils.js +5 -0
  167. package/maintained-branches.md +3 -2
  168. package/package.json +4 -6
  169. package/roadmap.md +25 -5
  170. package/shaka-player.uncompiled.js +2 -2
  171. package/support.html +0 -1
  172. package/test/abr/simple_abr_manager_unit.js +33 -33
  173. package/test/ads/ad_manager_unit.js +3 -2
  174. package/test/cast/cast_receiver_integration.js +50 -4
  175. package/test/cast/cast_utils_unit.js +2 -0
  176. package/test/cea/cea_decoder_unit.js +7 -0
  177. package/test/codec_switching/codec_switching_integration.js +182 -0
  178. package/test/dash/dash_parser_content_protection_unit.js +2 -0
  179. package/test/dash/dash_parser_integration.js +74 -0
  180. package/test/dash/dash_parser_live_unit.js +2 -19
  181. package/test/dash/dash_parser_manifest_unit.js +536 -2
  182. package/test/dash/dash_parser_segment_base_unit.js +2 -0
  183. package/test/dash/dash_parser_segment_list_unit.js +2 -0
  184. package/test/dash/dash_parser_segment_template_unit.js +2 -0
  185. package/test/dash/mpd_utils_unit.js +61 -30
  186. package/test/demo/demo_unit.js +0 -16
  187. package/test/hls/hls_live_unit.js +14 -3
  188. package/test/hls/hls_parser_unit.js +385 -33
  189. package/test/hls/manifest_text_parser_unit.js +32 -99
  190. package/test/media/adaptation_set_criteria_unit.js +284 -15
  191. package/test/media/adaptation_set_unit.js +2 -0
  192. package/test/media/content_workarounds_unit.js +1 -1
  193. package/test/media/drm_engine_unit.js +3 -3
  194. package/test/media/media_source_engine_unit.js +166 -2
  195. package/test/media/play_rate_controller_unit.js +0 -48
  196. package/test/media/segment_index_unit.js +5 -4
  197. package/test/media/segment_prefetch_unit.js +1 -1
  198. package/test/media/streaming_engine_unit.js +17 -17
  199. package/test/mss/mss_parser_unit.js +2 -0
  200. package/test/mss/mss_player_integration.js +3 -2
  201. package/test/net/networking_engine_unit.js +3 -3
  202. package/test/{media/manifest_parser_unit.js → net/networking_utils_unit.js} +2 -2
  203. package/test/offline/manifest_convert_unit.js +14 -0
  204. package/test/offline/offline_integration.js +2 -1
  205. package/test/offline/storage_integration.js +22 -13
  206. package/test/player_external.js +4 -2
  207. package/test/player_integration.js +15 -4
  208. package/test/player_load_graph_integration.js +47 -151
  209. package/test/player_src_equals_external.js +1 -0
  210. package/test/player_src_equals_integration.js +71 -4
  211. package/test/player_unit.js +182 -158
  212. package/test/test/assets/dash-aes-128/Fragments(video=0,format=mpd-time-csf) +0 -0
  213. package/test/test/assets/dash-aes-128/Fragments(video=20000000,format=mpd-time-csf) +0 -0
  214. package/test/test/assets/dash-aes-128/Fragments(video=40000000,format=mpd-time-csf) +0 -0
  215. package/test/test/assets/dash-aes-128/Fragments(video=60000000,format=mpd-time-csf) +0 -0
  216. package/test/test/assets/dash-aes-128/Fragments(video=i,format=mpd-time-csf) +0 -0
  217. package/test/test/assets/dash-aes-128/dash.mpd +18 -0
  218. package/test/test/assets/dash-aes-128/license +1 -0
  219. package/test/test/assets/dash-multi-codec/audio_en_2c_128k_aac.mp4 +0 -0
  220. package/test/test/assets/dash-multi-codec/audio_es_2c_64k_opus.webm +0 -0
  221. package/test/test/assets/dash-multi-codec/dash.mpd +32 -0
  222. package/test/test/assets/dash-multi-codec/video_144p_108k_h264.mp4 +0 -0
  223. package/test/test/assets/hls-ts-h265/0.ts +0 -0
  224. package/test/test/assets/hls-ts-h265/hevc.m3u8 +6 -0
  225. package/test/test/assets/hls-ts-h265/index.m3u8 +7 -0
  226. package/test/test/assets/hls-ts-muxed-aac-h265/0.ts +0 -0
  227. package/test/test/assets/hls-ts-muxed-aac-h265/1.ts +0 -0
  228. package/test/test/assets/hls-ts-muxed-aac-h265/media.m3u8 +9 -0
  229. package/test/test/assets/hls-ts-muxed-ac3-h264/media.m3u8 +24 -0
  230. package/test/test/assets/hls-ts-muxed-ac3-h264/s_00000.ts +0 -0
  231. package/test/test/assets/hls-ts-muxed-ac3-h264/s_00001.ts +0 -0
  232. package/test/test/assets/hls-ts-muxed-ac3-h264/s_00002.ts +0 -0
  233. package/test/test/assets/hls-ts-muxed-ac3-h264/s_00003.ts +0 -0
  234. package/test/test/assets/hls-ts-muxed-ac3-h264/s_00004.ts +0 -0
  235. package/test/test/assets/hls-ts-muxed-ac3-h264/s_00005.ts +0 -0
  236. package/test/test/assets/hls-ts-muxed-ec3-h264/ChID_voices_1280x720p_25fps_h264_6ch_256kbps_ddp_sub_0000.ts +0 -0
  237. package/test/test/assets/hls-ts-muxed-ec3-h264/ChID_voices_1280x720p_25fps_h264_6ch_256kbps_ddp_sub_0001.ts +0 -0
  238. package/test/test/assets/hls-ts-muxed-ec3-h264/ChID_voices_1280x720p_25fps_h264_6ch_256kbps_ddp_sub_0002.ts +0 -0
  239. package/test/test/assets/hls-ts-muxed-ec3-h264/ChID_voices_1280x720p_25fps_h264_6ch_256kbps_ddp_sub_0003.ts +0 -0
  240. package/test/test/assets/hls-ts-muxed-ec3-h264/prog_index.m3u8 +14 -0
  241. package/test/test/assets/hls-ts-muxed-mp3-h264/index.m3u8 +23 -0
  242. package/test/test/assets/hls-ts-muxed-mp3-h264/seg-1-v1-a1.ts +0 -0
  243. package/test/test/assets/hls-ts-muxed-mp3-h264/seg-2-v1-a1.ts +0 -0
  244. package/test/test/assets/hls-ts-muxed-mp3-h264/seg-3-v1-a1.ts +0 -0
  245. package/test/test/assets/hls-ts-muxed-mp3-h264/seg-4-v1-a1.ts +0 -0
  246. package/test/test/assets/hls-ts-muxed-mp3-h264/seg-5-v1-a1.ts +0 -0
  247. package/test/test/assets/hls-ts-muxed-mp3-h264/seg-6-v1-a1.ts +0 -0
  248. package/test/test/assets/hls-ts-muxed-mp3-h264/seg-7-v1-a1.ts +0 -0
  249. package/test/test/assets/hls-ts-muxed-mp3-h264/seg-8-v1-a1.ts +0 -0
  250. package/test/test/util/dash_parser_util.js +4 -0
  251. package/test/test/util/fake_ad_manager.js +20 -0
  252. package/test/test/util/fake_media_source_engine.js +13 -2
  253. package/test/test/util/fake_networking_engine.js +9 -1
  254. package/test/test/util/fake_segment_prefetch.js +3 -0
  255. package/test/test/util/manifest_generator.js +11 -3
  256. package/test/test/util/manifest_parser_util.js +14 -2
  257. package/test/test/util/mss_parser_util.js +4 -0
  258. package/test/test/util/offline_utils.js +2 -0
  259. package/test/test/util/simple_fakes.js +2 -2
  260. package/test/test/util/streaming_engine_util.js +10 -4
  261. package/test/test/util/test_scheme.js +6 -0
  262. package/test/test/util/ui_utils.js +4 -3
  263. package/test/text/mp4_ttml_parser_unit.js +4 -4
  264. package/test/text/srt_text_parser_unit.js +1 -1
  265. package/test/text/text_displayer_layout_unit.js +1 -1
  266. package/test/text/text_engine_unit.js +6 -0
  267. package/test/text/ttml_text_parser_unit.js +85 -2
  268. package/test/text/vtt_text_parser_unit.js +103 -31
  269. package/test/transmuxer/transmuxer_engine_integration.js +9 -0
  270. package/test/transmuxer/transmuxer_integration.js +150 -3
  271. package/test/ui/ad_ui_unit.js +2 -2
  272. package/test/ui/ui_customization_unit.js +17 -16
  273. package/test/ui/ui_integration.js +30 -20
  274. package/test/ui/ui_unit.js +28 -29
  275. package/test/util/buffer_utils_unit.js +1 -1
  276. package/test/util/content_steering_manager_unit.js +427 -0
  277. package/test/util/id3_utils_unit.js +52 -0
  278. package/test/util/stream_utils_unit.js +207 -169
  279. package/test/util/ts_parser_unit.js +3 -3
  280. package/third_party/closure-uri/uri.js +13 -1
  281. package/ui/audio_language_selection.js +9 -2
  282. package/ui/controls.js +125 -0
  283. package/ui/externs/ui.js +24 -2
  284. package/ui/language_utils.js +43 -8
  285. package/ui/less/range_elements.less +33 -0
  286. package/ui/locales/en.json +1 -0
  287. package/ui/locales/es-419.json +1 -0
  288. package/ui/locales/es.json +1 -0
  289. package/ui/locales/source.json +4 -0
  290. package/ui/localization.js +10 -0
  291. package/ui/pip_button.js +22 -120
  292. package/ui/remote_button.js +203 -0
  293. package/ui/resolution_selection.js +47 -10
  294. package/ui/seek_bar.js +296 -3
  295. package/ui/text_selection.js +2 -1
  296. package/ui/ui.js +11 -4
  297. package/demo/common/message_ids.js +0 -309
  298. package/demo/locales/en.json +0 -288
  299. package/demo/locales/source.json +0 -1146
  300. package/externs/mux.js +0 -69
  301. package/lib/lcevc/lcevc_dil.js +0 -152
  302. package/lib/routing/node.js +0 -23
  303. package/lib/routing/payload.js +0 -43
  304. package/lib/routing/walker.js +0 -587
  305. package/lib/transmuxer/muxjs_transmuxer.js +0 -242
  306. package/test/routing/walker_unit.js +0 -504
  307. package/test/transmuxer/muxjs_transmuxer_integration.js +0 -177
@@ -19,6 +19,7 @@ goog.require('shaka.media.PresentationTimeline');
19
19
  goog.require('shaka.media.SegmentIndex');
20
20
  goog.require('shaka.net.NetworkingEngine');
21
21
  goog.require('shaka.text.TextEngine');
22
+ goog.require('shaka.util.ContentSteeringManager');
22
23
  goog.require('shaka.util.Error');
23
24
  goog.require('shaka.util.Functional');
24
25
  goog.require('shaka.util.LanguageUtils');
@@ -115,6 +116,9 @@ shaka.dash.DashParser = class {
115
116
 
116
117
  /** @private {boolean} */
117
118
  this.lowLatencyMode_ = false;
119
+
120
+ /** @private {?shaka.util.ContentSteeringManager} */
121
+ this.contentSteeringManager_ = null;
118
122
  }
119
123
 
120
124
  /**
@@ -126,6 +130,10 @@ shaka.dash.DashParser = class {
126
130
  'DashManifestConfiguration should not be null!');
127
131
 
128
132
  this.config_ = config;
133
+
134
+ if (this.contentSteeringManager_) {
135
+ this.contentSteeringManager_.configure(this.config_);
136
+ }
129
137
  }
130
138
 
131
139
  /**
@@ -185,6 +193,10 @@ shaka.dash.DashParser = class {
185
193
  this.updateTimer_ = null;
186
194
  }
187
195
 
196
+ if (this.contentSteeringManager_) {
197
+ this.contentSteeringManager_.destroy();
198
+ }
199
+
188
200
  return this.operationManager_.destroy();
189
201
  }
190
202
 
@@ -212,6 +224,43 @@ shaka.dash.DashParser = class {
212
224
  // No-op
213
225
  }
214
226
 
227
+ /**
228
+ * @override
229
+ * @exportInterface
230
+ */
231
+ onInitialVariantChosen(variant) {
232
+ // For live it is necessary that the first time we update the manifest with
233
+ // a shorter time than indicated to take into account that the last segment
234
+ // added could be halfway, for example
235
+ if (this.manifest_ && this.manifest_.presentationTimeline.isLive()) {
236
+ const stream = variant.video || variant.audio;
237
+ if (stream && stream.segmentIndex) {
238
+ const availabilityEnd =
239
+ this.manifest_.presentationTimeline.getSegmentAvailabilityEnd();
240
+ const position = stream.segmentIndex.find(availabilityEnd);
241
+ if (position == null) {
242
+ return;
243
+ }
244
+ const reference = stream.segmentIndex.get(position);
245
+ if (!reference) {
246
+ return;
247
+ }
248
+ this.updatePeriod_ = reference.endTime - availabilityEnd;
249
+ this.setUpdateTimer_(/* offset= */ 0);
250
+ }
251
+ }
252
+ }
253
+
254
+ /**
255
+ * @override
256
+ * @exportInterface
257
+ */
258
+ banLocation(uri) {
259
+ if (this.contentSteeringManager_) {
260
+ this.contentSteeringManager_.banLocation(uri);
261
+ }
262
+ }
263
+
215
264
  /**
216
265
  * Makes a network request for the manifest and parses the resulting data.
217
266
  *
@@ -224,12 +273,10 @@ shaka.dash.DashParser = class {
224
273
  const type = shaka.net.NetworkingEngine.AdvancedRequestType.MPD;
225
274
  const request = shaka.net.NetworkingEngine.makeRequest(
226
275
  this.manifestUris_, this.config_.retryParameters);
227
- const networkingEngine = this.playerInterface_.networkingEngine;
228
276
  const startTime = Date.now();
229
- const operation = networkingEngine.request(requestType, request, {type});
230
- this.operationManager_.manage(operation);
231
277
 
232
- const response = await operation.promise;
278
+ const response = await this.makeNetworkRequest_(
279
+ request, requestType, {type});
233
280
 
234
281
  // Detect calls to stop().
235
282
  if (!this.playerInterface_) {
@@ -322,10 +369,68 @@ shaka.dash.DashParser = class {
322
369
  manifestBaseUris = absoluteLocations;
323
370
  }
324
371
 
372
+ let contentSteeringPromise = Promise.resolve();
373
+
374
+ const contentSteering = XmlUtils.findChild(mpd, 'ContentSteering');
375
+ if (contentSteering && this.playerInterface_) {
376
+ const hasPrevContentSteeringManager = !!this.contentSteeringManager_;
377
+ if (!this.contentSteeringManager_) {
378
+ this.contentSteeringManager_ =
379
+ new shaka.util.ContentSteeringManager(this.playerInterface_);
380
+ }
381
+ this.contentSteeringManager_.configure(this.config_);
382
+ this.contentSteeringManager_.setBaseUris(manifestBaseUris);
383
+ this.contentSteeringManager_.setManifestType(
384
+ shaka.media.ManifestParser.DASH);
385
+ const defaultPathwayId =
386
+ contentSteering.getAttribute('defaultServiceLocation');
387
+ this.contentSteeringManager_.setDefaultPathwayId(defaultPathwayId);
388
+ if (!hasPrevContentSteeringManager) {
389
+ const uri = XmlUtils.getContents(contentSteering);
390
+ if (uri) {
391
+ const queryBeforeStart =
392
+ XmlUtils.parseAttr(contentSteering, 'queryBeforeStart',
393
+ XmlUtils.parseBoolean, /* defaultValue= */ false);
394
+ if (queryBeforeStart) {
395
+ contentSteeringPromise =
396
+ this.contentSteeringManager_.requestInfo(uri);
397
+ } else {
398
+ this.contentSteeringManager_.requestInfo(uri);
399
+ }
400
+ }
401
+ }
402
+ }
403
+
325
404
  const uriObjs = XmlUtils.findChildren(mpd, 'BaseURL');
326
- const uris = uriObjs.map(XmlUtils.getContents);
327
- const baseUris = shaka.util.ManifestParserUtils.resolveUris(
328
- manifestBaseUris, uris);
405
+ let calculatedBaseUris;
406
+ let someLocationValid = false;
407
+ if (this.contentSteeringManager_) {
408
+ this.contentSteeringManager_.clearPreviousLocations();
409
+ for (const uriObj of uriObjs) {
410
+ const serviceLocation = uriObj.getAttribute('serviceLocation');
411
+ const uri = XmlUtils.getContents(uriObj);
412
+ if (serviceLocation && uri) {
413
+ this.contentSteeringManager_.addLocation(
414
+ 'BaseURL', serviceLocation, uri);
415
+ someLocationValid = true;
416
+ }
417
+ }
418
+ }
419
+ if (!someLocationValid || !this.contentSteeringManager_) {
420
+ const uris = uriObjs.map(XmlUtils.getContents);
421
+ calculatedBaseUris = shaka.util.ManifestParserUtils.resolveUris(
422
+ manifestBaseUris, uris);
423
+ }
424
+
425
+ const getBaseUris = () => {
426
+ if (this.contentSteeringManager_ && someLocationValid) {
427
+ return this.contentSteeringManager_.getLocations('BaseURL');
428
+ }
429
+ if (calculatedBaseUris) {
430
+ return calculatedBaseUris;
431
+ }
432
+ return [];
433
+ };
329
434
 
330
435
  let availabilityTimeOffset = 0;
331
436
  if (uriObjs && uriObjs.length) {
@@ -441,7 +546,7 @@ shaka.dash.DashParser = class {
441
546
  profiles: profiles.split(','),
442
547
  };
443
548
 
444
- const periodsAndDuration = this.parsePeriods_(context, baseUris, mpd);
549
+ const periodsAndDuration = this.parsePeriods_(context, getBaseUris, mpd);
445
550
  const duration = periodsAndDuration.duration;
446
551
  const periods = periodsAndDuration.periods;
447
552
 
@@ -485,18 +590,14 @@ shaka.dash.DashParser = class {
485
590
 
486
591
  await this.periodCombiner_.combinePeriods(periods, context.dynamic);
487
592
 
593
+ await contentSteeringPromise;
594
+
488
595
  // Set minBufferTime to 0 for low-latency DASH live stream to achieve the
489
596
  // best latency
490
597
  if (this.lowLatencyMode_) {
491
598
  minBufferTime = 0;
492
599
  }
493
600
 
494
- // Set updatePeriod_ to minTotalAvailabilityTimeOffset_ to achieve the best
495
- // latency in LL streams
496
- if (this.lowLatencyMode_ && this.minTotalAvailabilityTimeOffset_) {
497
- this.updatePeriod_ = this.minTotalAvailabilityTimeOffset_;
498
- }
499
-
500
601
  // These steps are not done on manifest update.
501
602
  if (!this.manifest_) {
502
603
  this.manifest_ = {
@@ -517,7 +618,7 @@ shaka.dash.DashParser = class {
517
618
  if (presentationTimeline.usingPresentationStartTime()) {
518
619
  const XmlUtils = shaka.util.XmlUtils;
519
620
  const timingElements = XmlUtils.findChildren(mpd, 'UTCTiming');
520
- const offset = await this.parseUtcTiming_(baseUris, timingElements);
621
+ const offset = await this.parseUtcTiming_(getBaseUris, timingElements);
521
622
  // Detect calls to stop().
522
623
  if (!this.playerInterface_) {
523
624
  return;
@@ -577,8 +678,19 @@ shaka.dash.DashParser = class {
577
678
  const maxPlaybackRate = playbackRateNode ?
578
679
  parseFloat(playbackRateNode.getAttribute('max')) :
579
680
  null;
681
+ const minLatency = latencyNode && latencyNode.getAttribute('min') ?
682
+ parseInt(latencyNode.getAttribute('min'), 10) / 1000 :
683
+ null;
684
+ const minPlaybackRate = playbackRateNode ?
685
+ parseFloat(playbackRateNode.getAttribute('min')) :
686
+ null;
580
687
 
581
- return {maxLatency, maxPlaybackRate};
688
+ return {
689
+ maxLatency,
690
+ maxPlaybackRate,
691
+ minLatency,
692
+ minPlaybackRate,
693
+ };
582
694
  }
583
695
 
584
696
  return null;
@@ -590,7 +702,7 @@ shaka.dash.DashParser = class {
590
702
  * children.
591
703
  *
592
704
  * @param {shaka.dash.DashParser.Context} context
593
- * @param {!Array.<string>} baseUris
705
+ * @param {function():!Array.<string>} getBaseUris
594
706
  * @param {!Element} mpd
595
707
  * @return {{
596
708
  * periods: !Array.<shaka.extern.Period>,
@@ -599,7 +711,7 @@ shaka.dash.DashParser = class {
599
711
  * }}
600
712
  * @private
601
713
  */
602
- parsePeriods_(context, baseUris, mpd) {
714
+ parsePeriods_(context, getBaseUris, mpd) {
603
715
  const XmlUtils = shaka.util.XmlUtils;
604
716
  const presentationDuration = XmlUtils.parseAttr(
605
717
  mpd, 'mediaPresentationDuration', XmlUtils.parseDuration);
@@ -689,7 +801,7 @@ shaka.dash.DashParser = class {
689
801
  node: elem,
690
802
  isLastPeriod: periodDuration == null || !next,
691
803
  };
692
- const period = this.parsePeriod_(context, baseUris, info);
804
+ const period = this.parsePeriod_(context, getBaseUris, info);
693
805
  periods.push(period);
694
806
 
695
807
  if (context.period.id && periodDuration) {
@@ -743,17 +855,17 @@ shaka.dash.DashParser = class {
743
855
  * was done before this was called so start and duration are valid.
744
856
  *
745
857
  * @param {shaka.dash.DashParser.Context} context
746
- * @param {!Array.<string>} baseUris
858
+ * @param {function():!Array.<string>} getBaseUris
747
859
  * @param {shaka.dash.DashParser.PeriodInfo} periodInfo
748
860
  * @return {shaka.extern.Period}
749
861
  * @private
750
862
  */
751
- parsePeriod_(context, baseUris, periodInfo) {
863
+ parsePeriod_(context, getBaseUris, periodInfo) {
752
864
  const Functional = shaka.util.Functional;
753
865
  const XmlUtils = shaka.util.XmlUtils;
754
866
  const ContentType = shaka.util.ManifestParserUtils.ContentType;
755
867
 
756
- context.period = this.createFrame_(periodInfo.node, null, baseUris);
868
+ context.period = this.createFrame_(periodInfo.node, null, getBaseUris);
757
869
  context.periodInfo = periodInfo;
758
870
  context.period.availabilityTimeOffset = context.availabilityTimeOffset;
759
871
 
@@ -824,40 +936,29 @@ shaka.dash.DashParser = class {
824
936
  }
825
937
  }
826
938
 
827
- const audioSets = this.config_.disableAudio ? [] :
828
- this.getSetsOfType_(normalAdaptationSets, ContentType.AUDIO);
829
- const videoSets = this.config_.disableVideo ? [] :
830
- this.getSetsOfType_(normalAdaptationSets, ContentType.VIDEO);
831
- const textSets = this.config_.disableText ? [] :
832
- this.getSetsOfType_(normalAdaptationSets, ContentType.TEXT);
833
- const imageSets = this.config_.disableThumbnails ? [] :
834
- this.getSetsOfType_(normalAdaptationSets, ContentType.IMAGE);
835
-
836
- if (!videoSets.length && !audioSets.length) {
939
+ const audioStreams = this.getStreamsFromSets_(
940
+ this.config_.disableAudio,
941
+ normalAdaptationSets,
942
+ ContentType.AUDIO);
943
+ const videoStreams = this.getStreamsFromSets_(
944
+ this.config_.disableVideo,
945
+ normalAdaptationSets,
946
+ ContentType.VIDEO);
947
+ const textStreams = this.getStreamsFromSets_(
948
+ this.config_.disableText,
949
+ normalAdaptationSets,
950
+ ContentType.TEXT);
951
+ const imageStreams = this.getStreamsFromSets_(
952
+ this.config_.disableThumbnails,
953
+ normalAdaptationSets,
954
+ ContentType.IMAGE);
955
+
956
+ if (videoStreams.length === 0 && audioStreams.length === 0) {
837
957
  throw new shaka.util.Error(
838
958
  shaka.util.Error.Severity.CRITICAL,
839
959
  shaka.util.Error.Category.MANIFEST,
840
- shaka.util.Error.Code.DASH_EMPTY_PERIOD);
841
- }
842
-
843
- const audioStreams = [];
844
- for (const audioSet of audioSets) {
845
- audioStreams.push(...audioSet.streams);
846
- }
847
-
848
- const videoStreams = [];
849
- for (const videoSet of videoSets) {
850
- videoStreams.push(...videoSet.streams);
851
- }
852
-
853
- const textStreams = [];
854
- for (const textSet of textSets) {
855
- textStreams.push(...textSet.streams);
856
- }
857
-
858
- const imageStreams = [];
859
- for (const imageSet of imageSets) {
860
- imageStreams.push(...imageSet.streams);
960
+ shaka.util.Error.Code.DASH_EMPTY_PERIOD,
961
+ );
861
962
  }
862
963
 
863
964
  return {
@@ -870,15 +971,26 @@ shaka.dash.DashParser = class {
870
971
  }
871
972
 
872
973
  /**
974
+ * Gets the streams from the given sets or returns an empty array if disabled
975
+ * or no streams are found.
976
+ * @param {boolean} disabled
873
977
  * @param {!Array.<!shaka.dash.DashParser.AdaptationInfo>} adaptationSets
874
- * @param {string} type
875
- * @return {!Array.<!shaka.dash.DashParser.AdaptationInfo>}
876
- * @private
877
- */
878
- getSetsOfType_(adaptationSets, type) {
879
- return adaptationSets.filter((as) => {
880
- return as.contentType == type;
881
- });
978
+ * @param {string} contentType
979
+ @private
980
+ */
981
+ getStreamsFromSets_(disabled, adaptationSets, contentType) {
982
+ if (disabled || !adaptationSets.length) {
983
+ return [];
984
+ }
985
+
986
+ return adaptationSets.reduce((all, part) => {
987
+ if (part.contentType != contentType) {
988
+ return all;
989
+ }
990
+
991
+ all.push(...part.streams);
992
+ return all;
993
+ }, []);
882
994
  }
883
995
 
884
996
  /**
@@ -966,6 +1078,7 @@ shaka.dash.DashParser = class {
966
1078
  XmlUtils.findChildren(elem, 'EssentialProperty');
967
1079
  // ID of real AdaptationSet if this is a trick mode set:
968
1080
  let trickModeFor = null;
1081
+ let isFastSwitching = false;
969
1082
  let unrecognizedEssentialProperty = false;
970
1083
  for (const prop of essentialProperties) {
971
1084
  const schemeId = prop.getAttribute('schemeIdUri');
@@ -978,6 +1091,8 @@ shaka.dash.DashParser = class {
978
1091
  } else if (schemeId == colourPrimariesScheme ||
979
1092
  schemeId == matrixCoefficientsScheme) {
980
1093
  continue;
1094
+ } else if (schemeId == 'urn:mpeg:dash:ssr:2023') {
1095
+ isFastSwitching = true;
981
1096
  } else {
982
1097
  unrecognizedEssentialProperty = true;
983
1098
  }
@@ -1135,6 +1250,7 @@ shaka.dash.DashParser = class {
1135
1250
  closedCaptions, representation, accessibilityPurpose);
1136
1251
  if (parsedRepresentation) {
1137
1252
  parsedRepresentation.hdr = parsedRepresentation.hdr || videoRange;
1253
+ parsedRepresentation.fastSwitching = isFastSwitching;
1138
1254
  }
1139
1255
  return parsedRepresentation;
1140
1256
  }).filter((s) => !!s);
@@ -1169,6 +1285,9 @@ shaka.dash.DashParser = class {
1169
1285
  }
1170
1286
  }
1171
1287
 
1288
+ const adaptationId = context.adaptationSet.id ||
1289
+ ('__fake__' + this.globalId_++);
1290
+
1172
1291
  for (const stream of streams) {
1173
1292
  // Some DRM license providers require that we have a default
1174
1293
  // key ID from the manifest in the wrapped license request.
@@ -1178,6 +1297,9 @@ shaka.dash.DashParser = class {
1178
1297
  new Set([...drmInfo.keyIds, ...stream.keyIds]) :
1179
1298
  drmInfo.keyIds || stream.keyIds;
1180
1299
  }
1300
+ if (this.config_.dash.enableAudioGroups) {
1301
+ stream.groupId = adaptationId;
1302
+ }
1181
1303
  }
1182
1304
 
1183
1305
  const repIds = representations
@@ -1185,7 +1307,7 @@ shaka.dash.DashParser = class {
1185
1307
  .filter(shaka.util.Functional.isNotNull);
1186
1308
 
1187
1309
  return {
1188
- id: context.adaptationSet.id || ('__fake__' + this.globalId_++),
1310
+ id: adaptationId,
1189
1311
  contentType: context.adaptationSet.contentType,
1190
1312
  language: language,
1191
1313
  main: main,
@@ -1250,29 +1372,69 @@ shaka.dash.DashParser = class {
1250
1372
  const isImage = contentType == ContentType.IMAGE;
1251
1373
 
1252
1374
  try {
1375
+ /** @type {shaka.extern.aes128Key|undefined} */
1376
+ let aes128Key = undefined;
1377
+ if (contentProtection.aes128Info) {
1378
+ const getBaseUris = context.representation.getBaseUris;
1379
+ const uris = shaka.util.ManifestParserUtils.resolveUris(
1380
+ getBaseUris(), [contentProtection.aes128Info.keyUri]);
1381
+ const requestType = shaka.net.NetworkingEngine.RequestType.KEY;
1382
+ const request = shaka.net.NetworkingEngine.makeRequest(
1383
+ uris, this.config_.retryParameters);
1384
+
1385
+ aes128Key = {
1386
+ method: 'AES-128',
1387
+ iv: contentProtection.aes128Info.iv,
1388
+ firstMediaSequenceNumber: 0,
1389
+ };
1390
+
1391
+ // Don't download the key object until the segment is parsed, to
1392
+ // avoid a startup delay for long manifests with lots of keys.
1393
+ aes128Key.fetchKey = async () => {
1394
+ const keyResponse =
1395
+ await this.makeNetworkRequest_(request, requestType);
1396
+
1397
+ // keyResponse.status is undefined when URI is
1398
+ // "data:text/plain;base64,"
1399
+ if (!keyResponse.data || keyResponse.data.byteLength != 16) {
1400
+ throw new shaka.util.Error(
1401
+ shaka.util.Error.Severity.CRITICAL,
1402
+ shaka.util.Error.Category.MANIFEST,
1403
+ shaka.util.Error.Code.AES_128_INVALID_KEY_LENGTH);
1404
+ }
1405
+
1406
+ const algorithm = {
1407
+ name: 'AES-CBC',
1408
+ };
1409
+ aes128Key.cryptoKey = await window.crypto.subtle.importKey(
1410
+ 'raw', keyResponse.data, algorithm, true, ['decrypt']);
1411
+ aes128Key.fetchKey = undefined; // No longer needed.
1412
+ };
1413
+ }
1253
1414
  const requestSegment = (uris, startByte, endByte, isInit) => {
1254
1415
  return this.requestSegment_(uris, startByte, endByte, isInit);
1255
1416
  };
1256
1417
  if (context.representation.segmentBase) {
1257
1418
  streamInfo = shaka.dash.SegmentBase.createStreamInfo(
1258
- context, requestSegment);
1419
+ context, requestSegment, aes128Key);
1259
1420
  } else if (context.representation.segmentList) {
1260
1421
  streamInfo = shaka.dash.SegmentList.createStreamInfo(
1261
- context, this.streamMap_);
1422
+ context, this.streamMap_, aes128Key);
1262
1423
  } else if (context.representation.segmentTemplate) {
1263
1424
  const hasManifest = !!this.manifest_;
1264
1425
 
1265
1426
  streamInfo = shaka.dash.SegmentTemplate.createStreamInfo(
1266
1427
  context, requestSegment, this.streamMap_, hasManifest,
1267
- this.config_.dash.initialSegmentLimit, this.periodDurations_);
1428
+ this.config_.dash.initialSegmentLimit, this.periodDurations_,
1429
+ aes128Key);
1268
1430
  } else {
1269
1431
  goog.asserts.assert(isText,
1270
1432
  'Must have Segment* with non-text streams.');
1271
1433
 
1272
- const baseUris = context.representation.baseUris;
1273
1434
  const duration = context.periodInfo.duration || 0;
1274
1435
  streamInfo = {
1275
1436
  generateSegmentIndex: () => {
1437
+ const baseUris = context.representation.getBaseUris();
1276
1438
  return Promise.resolve(shaka.media.SegmentIndex.forSingleSegment(
1277
1439
  periodStart, duration, baseUris));
1278
1440
  },
@@ -1363,6 +1525,7 @@ shaka.dash.DashParser = class {
1363
1525
  stream = {
1364
1526
  id: this.globalId_++,
1365
1527
  originalId: context.representation.id,
1528
+ groupId: null,
1366
1529
  createSegmentIndex: () => Promise.resolve(),
1367
1530
  closeSegmentIndex: () => {
1368
1531
  if (stream.segmentIndex) {
@@ -1397,10 +1560,12 @@ shaka.dash.DashParser = class {
1397
1560
  spatialAudio,
1398
1561
  closedCaptions,
1399
1562
  hdr,
1563
+ videoLayout: undefined,
1400
1564
  tilesLayout,
1401
1565
  matchedStreams: [],
1402
1566
  accessibilityPurpose,
1403
1567
  external: false,
1568
+ fastSwitching: false,
1404
1569
  };
1405
1570
  }
1406
1571
 
@@ -1456,6 +1621,8 @@ shaka.dash.DashParser = class {
1456
1621
  return;
1457
1622
  }
1458
1623
 
1624
+ this.playerInterface_.onManifestUpdated();
1625
+
1459
1626
  this.setUpdateTimer_(updateDelay);
1460
1627
  }
1461
1628
 
@@ -1471,8 +1638,8 @@ shaka.dash.DashParser = class {
1471
1638
  // NOTE: An updatePeriod_ of -1 means the attribute was missing.
1472
1639
  // An attribute which is present and set to 0 should still result in
1473
1640
  // periodic updates. For more, see:
1474
- // https://github.com/shaka-project/shaka-player/issues/331
1475
- if (this.updatePeriod_ < 0) {
1641
+ // https://github.com/Dash-Industry-Forum/Guidelines-TimingModel/issues/48
1642
+ if (this.updatePeriod_ <= 0) {
1476
1643
  return;
1477
1644
  }
1478
1645
 
@@ -1490,13 +1657,13 @@ shaka.dash.DashParser = class {
1490
1657
  *
1491
1658
  * @param {!Element} elem
1492
1659
  * @param {?shaka.dash.DashParser.InheritanceFrame} parent
1493
- * @param {Array.<string>} baseUris
1660
+ * @param {?function():!Array.<string>} getBaseUris
1494
1661
  * @return {shaka.dash.DashParser.InheritanceFrame}
1495
1662
  * @private
1496
1663
  */
1497
- createFrame_(elem, parent, baseUris) {
1498
- goog.asserts.assert(parent || baseUris,
1499
- 'Must provide either parent or baseUris');
1664
+ createFrame_(elem, parent, getBaseUris) {
1665
+ goog.asserts.assert(parent || getBaseUris,
1666
+ 'Must provide either parent or getBaseUris');
1500
1667
  const ManifestParserUtils = shaka.util.ManifestParserUtils;
1501
1668
  const XmlUtils = shaka.util.XmlUtils;
1502
1669
  parent = parent || /** @type {shaka.dash.DashParser.InheritanceFrame} */ ({
@@ -1509,14 +1676,44 @@ shaka.dash.DashParser = class {
1509
1676
  numChannels: null,
1510
1677
  audioSamplingRate: null,
1511
1678
  availabilityTimeOffset: 0,
1679
+ segmentSequenceCadence: 0,
1512
1680
  });
1513
- baseUris = baseUris || parent.baseUris;
1681
+ getBaseUris = getBaseUris || parent.getBaseUris;
1514
1682
 
1515
1683
  const parseNumber = XmlUtils.parseNonNegativeInt;
1516
1684
  const evalDivision = XmlUtils.evalDivision;
1517
1685
 
1686
+ const id = elem.getAttribute('id');
1518
1687
  const uriObjs = XmlUtils.findChildren(elem, 'BaseURL');
1519
- const uris = uriObjs.map(XmlUtils.getContents);
1688
+ let calculatedBaseUris;
1689
+ let someLocationValid = false;
1690
+ if (this.contentSteeringManager_) {
1691
+ for (const uriObj of uriObjs) {
1692
+ const serviceLocation = uriObj.getAttribute('serviceLocation');
1693
+ const uri = XmlUtils.getContents(uriObj);
1694
+ if (serviceLocation && uri) {
1695
+ this.contentSteeringManager_.addLocation(
1696
+ id, serviceLocation, uri);
1697
+ someLocationValid = true;
1698
+ }
1699
+ }
1700
+ }
1701
+ if (!someLocationValid || !this.contentSteeringManager_) {
1702
+ calculatedBaseUris = uriObjs.map(XmlUtils.getContents);
1703
+ }
1704
+
1705
+ const getFrameUris = () => {
1706
+ if (!uriObjs.length) {
1707
+ return [];
1708
+ }
1709
+ if (this.contentSteeringManager_ && someLocationValid) {
1710
+ return this.contentSteeringManager_.getLocations(id);
1711
+ }
1712
+ if (calculatedBaseUris) {
1713
+ return calculatedBaseUris;
1714
+ }
1715
+ return [];
1716
+ };
1520
1717
 
1521
1718
  let contentType = elem.getAttribute('contentType') || parent.contentType;
1522
1719
  const mimeType = elem.getAttribute('mimeType') || parent.mimeType;
@@ -1559,8 +1756,20 @@ shaka.dash.DashParser = class {
1559
1756
  const availabilityTimeOffset = parent.availabilityTimeOffset + baseUriAto +
1560
1757
  segmentBaseAto + segmentTemplateAto;
1561
1758
 
1759
+ let segmentSequenceCadence = null;
1760
+ const segmentSequenceProperties =
1761
+ XmlUtils.findChild(elem, 'SegmentSequenceProperties');
1762
+ if (segmentSequenceProperties) {
1763
+ const sap = XmlUtils.findChild(segmentSequenceProperties, 'SAP');
1764
+ if (sap) {
1765
+ segmentSequenceCadence = XmlUtils.parseAttr(sap, 'cadence',
1766
+ XmlUtils.parseInt);
1767
+ }
1768
+ }
1769
+
1562
1770
  return {
1563
- baseUris: ManifestParserUtils.resolveUris(baseUris, uris),
1771
+ getBaseUris:
1772
+ () => ManifestParserUtils.resolveUris(getBaseUris(), getFrameUris()),
1564
1773
  segmentBase: segmentBase || parent.segmentBase,
1565
1774
  segmentList:
1566
1775
  XmlUtils.findChild(elem, 'SegmentList') || parent.segmentList,
@@ -1573,11 +1782,13 @@ shaka.dash.DashParser = class {
1573
1782
  frameRate: frameRate,
1574
1783
  pixelAspectRatio: pixelAspectRatio,
1575
1784
  emsgSchemeIdUris: emsgSchemeIdUris,
1576
- id: elem.getAttribute('id'),
1785
+ id: id,
1577
1786
  language: elem.getAttribute('lang'),
1578
1787
  numChannels: numChannels,
1579
1788
  audioSamplingRate: audioSamplingRate,
1580
1789
  availabilityTimeOffset: availabilityTimeOffset,
1790
+ segmentSequenceCadence:
1791
+ segmentSequenceCadence || parent.segmentSequenceCadence,
1581
1792
  };
1582
1793
  }
1583
1794
 
@@ -1747,15 +1958,15 @@ shaka.dash.DashParser = class {
1747
1958
  /**
1748
1959
  * Makes a request to the given URI and calculates the clock offset.
1749
1960
  *
1750
- * @param {!Array.<string>} baseUris
1961
+ * @param {function():!Array.<string>} getBaseUris
1751
1962
  * @param {string} uri
1752
1963
  * @param {string} method
1753
1964
  * @return {!Promise.<number>}
1754
1965
  * @private
1755
1966
  */
1756
- async requestForTiming_(baseUris, uri, method) {
1967
+ async requestForTiming_(getBaseUris, uri, method) {
1757
1968
  const requestUris =
1758
- shaka.util.ManifestParserUtils.resolveUris(baseUris, [uri]);
1969
+ shaka.util.ManifestParserUtils.resolveUris(getBaseUris(), [uri]);
1759
1970
  const request = shaka.net.NetworkingEngine.makeRequest(
1760
1971
  requestUris, this.config_.retryParameters);
1761
1972
  request.method = method;
@@ -1788,12 +1999,12 @@ shaka.dash.DashParser = class {
1788
1999
  /**
1789
2000
  * Parses an array of UTCTiming elements.
1790
2001
  *
1791
- * @param {!Array.<string>} baseUris
2002
+ * @param {function():!Array.<string>} getBaseUris
1792
2003
  * @param {!Array.<!Element>} elems
1793
2004
  * @return {!Promise.<number>}
1794
2005
  * @private
1795
2006
  */
1796
- async parseUtcTiming_(baseUris, elems) {
2007
+ async parseUtcTiming_(getBaseUris, elems) {
1797
2008
  const schemesAndValues = elems.map((elem) => {
1798
2009
  return {
1799
2010
  scheme: elem.getAttribute('schemeIdUri'),
@@ -1822,13 +2033,13 @@ shaka.dash.DashParser = class {
1822
2033
  case 'urn:mpeg:dash:utc:http-head:2014':
1823
2034
  case 'urn:mpeg:dash:utc:http-head:2012':
1824
2035
  // eslint-disable-next-line no-await-in-loop
1825
- return await this.requestForTiming_(baseUris, value, 'HEAD');
2036
+ return await this.requestForTiming_(getBaseUris, value, 'HEAD');
1826
2037
  case 'urn:mpeg:dash:utc:http-xsdate:2014':
1827
2038
  case 'urn:mpeg:dash:utc:http-iso:2014':
1828
2039
  case 'urn:mpeg:dash:utc:http-xsdate:2012':
1829
2040
  case 'urn:mpeg:dash:utc:http-iso:2012':
1830
2041
  // eslint-disable-next-line no-await-in-loop
1831
- return await this.requestForTiming_(baseUris, value, 'GET');
2042
+ return await this.requestForTiming_(getBaseUris, value, 'GET');
1832
2043
  case 'urn:mpeg:dash:utc:direct:2014':
1833
2044
  case 'urn:mpeg:dash:utc:direct:2012': {
1834
2045
  const date = Date.parse(value);
@@ -1929,10 +2140,8 @@ shaka.dash.DashParser = class {
1929
2140
  endByte,
1930
2141
  this.config_.retryParameters);
1931
2142
 
1932
- const networkingEngine = this.playerInterface_.networkingEngine;
1933
- const operation = networkingEngine.request(requestType, request, {type});
1934
- this.operationManager_.manage(operation);
1935
- const response = await operation.promise;
2143
+ const response = await this.makeNetworkRequest_(
2144
+ request, requestType, {type});
1936
2145
  return response.data;
1937
2146
  }
1938
2147
 
@@ -1958,6 +2167,24 @@ shaka.dash.DashParser = class {
1958
2167
  // types well.
1959
2168
  return mimeType.split('/')[0];
1960
2169
  }
2170
+
2171
+
2172
+ /**
2173
+ * Create a networking request. This will manage the request using the
2174
+ * parser's operation manager.
2175
+ *
2176
+ * @param {shaka.extern.Request} request
2177
+ * @param {shaka.net.NetworkingEngine.RequestType} type
2178
+ * @param {shaka.extern.RequestContext=} context
2179
+ * @return {!Promise.<shaka.extern.Response>}
2180
+ * @private
2181
+ */
2182
+ makeNetworkRequest_(request, type, context) {
2183
+ const op = this.playerInterface_.networkingEngine.request(
2184
+ type, request, context);
2185
+ this.operationManager_.manage(op);
2186
+ return op.promise;
2187
+ }
1961
2188
  };
1962
2189
 
1963
2190
 
@@ -1975,7 +2202,7 @@ shaka.dash.DashParser.RequestSegmentCallback;
1975
2202
  * segmentBase: Element,
1976
2203
  * segmentList: Element,
1977
2204
  * segmentTemplate: Element,
1978
- * baseUris: !Array.<string>,
2205
+ * getBaseUris: function():!Array.<string>,
1979
2206
  * width: (number|undefined),
1980
2207
  * height: (number|undefined),
1981
2208
  * contentType: string,
@@ -1988,7 +2215,8 @@ shaka.dash.DashParser.RequestSegmentCallback;
1988
2215
  * language: ?string,
1989
2216
  * numChannels: ?number,
1990
2217
  * audioSamplingRate: ?number,
1991
- * availabilityTimeOffset: number
2218
+ * availabilityTimeOffset: number,
2219
+ * segmentSequenceCadence: number
1992
2220
  * }}
1993
2221
  *
1994
2222
  * @description
@@ -2001,8 +2229,8 @@ shaka.dash.DashParser.RequestSegmentCallback;
2001
2229
  * The XML node for SegmentList.
2002
2230
  * @property {Element} segmentTemplate
2003
2231
  * The XML node for SegmentTemplate.
2004
- * @property {!Array.<string>} baseUris
2005
- * An array of absolute base URIs for the frame.
2232
+ * @property {function():!Array.<string>} getBaseUris
2233
+ * Function than returns an array of absolute base URIs for the frame.
2006
2234
  * @property {(number|undefined)} width
2007
2235
  * The inherited width value.
2008
2236
  * @property {(number|undefined)} height
@@ -2029,6 +2257,9 @@ shaka.dash.DashParser.RequestSegmentCallback;
2029
2257
  * Specifies the maximum sampling rate of the content, or null if unknown.
2030
2258
  * @property {number} availabilityTimeOffset
2031
2259
  * Specifies the total availabilityTimeOffset of the segment, or 0 if unknown.
2260
+ * @property {number} segmentSequenceCadence
2261
+ * Specifies the cadence of independent segments in Segment Sequence
2262
+ * Representation.
2032
2263
  */
2033
2264
  shaka.dash.DashParser.InheritanceFrame;
2034
2265
 
@@ -2162,8 +2393,6 @@ shaka.dash.DashParser.GenerateSegmentIndexFunction;
2162
2393
  shaka.dash.DashParser.StreamInfo;
2163
2394
 
2164
2395
 
2165
- shaka.media.ManifestParser.registerParserByExtension(
2166
- 'mpd', () => new shaka.dash.DashParser());
2167
2396
  shaka.media.ManifestParser.registerParserByMime(
2168
2397
  'application/dash+xml', () => new shaka.dash.DashParser());
2169
2398
  shaka.media.ManifestParser.registerParserByMime(