shaka-player 4.5.0 → 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 (214) hide show
  1. package/.github/ISSUE_TEMPLATE/bug_report.md +1 -1
  2. package/AUTHORS +2 -0
  3. package/CHANGELOG.md +98 -0
  4. package/CODE_OF_CONDUCT.md +1 -1
  5. package/CONTRIBUTORS +2 -0
  6. package/README.md +38 -32
  7. package/demo/common/asset.js +11 -17
  8. package/demo/common/assets.js +117 -11
  9. package/demo/config.js +28 -1
  10. package/demo/custom.js +52 -38
  11. package/demo/main.js +11 -3
  12. package/demo/search.js +7 -0
  13. package/dist/controls.css +1 -1
  14. package/dist/controls.css.map +1 -1
  15. package/dist/demo.compiled.debug.js +133 -124
  16. package/dist/demo.compiled.debug.map +3 -3
  17. package/dist/demo.compiled.js +133 -124
  18. package/dist/demo.compiled.map +3 -3
  19. package/dist/deps.js +20 -18
  20. package/dist/locales.js +35 -31
  21. package/dist/receiver.compiled.debug.js +58 -50
  22. package/dist/receiver.compiled.debug.map +3 -3
  23. package/dist/receiver.compiled.js +58 -50
  24. package/dist/receiver.compiled.map +3 -3
  25. package/dist/shaka-player.compiled.d.ts +440 -390
  26. package/dist/shaka-player.compiled.debug.d.ts +440 -390
  27. package/dist/shaka-player.compiled.debug.externs.js +162 -77
  28. package/dist/shaka-player.compiled.debug.js +786 -736
  29. package/dist/shaka-player.compiled.debug.map +4 -4
  30. package/dist/shaka-player.compiled.externs.js +162 -77
  31. package/dist/shaka-player.compiled.js +1154 -1121
  32. package/dist/shaka-player.compiled.map +4 -4
  33. package/dist/shaka-player.ui.d.ts +491 -437
  34. package/dist/shaka-player.ui.debug.d.ts +491 -437
  35. package/dist/shaka-player.ui.debug.externs.js +167 -77
  36. package/dist/shaka-player.ui.debug.js +877 -811
  37. package/dist/shaka-player.ui.debug.map +4 -4
  38. package/dist/shaka-player.ui.externs.js +167 -77
  39. package/dist/shaka-player.ui.js +1362 -1318
  40. package/dist/shaka-player.ui.map +4 -4
  41. package/docs/tutorials/ad_monetization.md +54 -0
  42. package/docs/tutorials/basic-usage.md +2 -1
  43. package/docs/tutorials/errors.md +2 -1
  44. package/docs/tutorials/offline.md +10 -8
  45. package/docs/tutorials/plugins.md +5 -0
  46. package/docs/tutorials/ui-customization.md +13 -0
  47. package/docs/tutorials/ui.md +2 -1
  48. package/docs/tutorials/upgrade.md +3 -0
  49. package/externs/managedmediasource.js +24 -0
  50. package/externs/shaka/abr_manager.js +6 -2
  51. package/externs/shaka/cea.js +7 -0
  52. package/externs/shaka/codecs.js +5 -3
  53. package/externs/shaka/manifest.js +21 -5
  54. package/externs/shaka/manifest_parser.js +17 -0
  55. package/externs/shaka/net.js +2 -2
  56. package/externs/shaka/offline.js +9 -2
  57. package/externs/shaka/player.js +53 -7
  58. package/externs/shaka/text.js +3 -1
  59. package/externs/shaka/transmuxer.js +1 -1
  60. package/lib/abr/simple_abr_manager.js +25 -8
  61. package/lib/ads/media_tailor_ad_manager.js +14 -3
  62. package/lib/cast/cast_receiver.js +1 -1
  63. package/lib/cea/cea_decoder.js +28 -0
  64. package/lib/cea/dummy_caption_decoder.js +5 -0
  65. package/lib/dash/dash_parser.js +249 -83
  66. package/lib/dash/mpd_utils.js +52 -31
  67. package/lib/dash/segment_base.js +4 -5
  68. package/lib/dash/segment_list.js +5 -4
  69. package/lib/dash/segment_template.js +83 -26
  70. package/lib/hls/hls_classes.js +28 -43
  71. package/lib/hls/hls_parser.js +761 -649
  72. package/lib/hls/hls_utils.js +64 -5
  73. package/lib/hls/manifest_text_parser.js +7 -11
  74. package/lib/media/adaptation_set.js +20 -13
  75. package/lib/media/adaptation_set_criteria.js +70 -18
  76. package/lib/media/closed_caption_parser.js +13 -0
  77. package/lib/media/content_workarounds.js +6 -0
  78. package/lib/media/drm_engine.js +8 -13
  79. package/lib/media/manifest_parser.js +8 -113
  80. package/lib/media/media_source_capabilities.js +10 -3
  81. package/lib/media/media_source_engine.js +75 -21
  82. package/lib/media/play_rate_controller.js +8 -38
  83. package/lib/media/presentation_timeline.js +7 -1
  84. package/lib/media/quality_observer.js +1 -1
  85. package/lib/media/segment_index.js +5 -5
  86. package/lib/media/segment_prefetch.js +92 -24
  87. package/lib/media/segment_reference.js +67 -1
  88. package/lib/media/segment_utils.js +343 -0
  89. package/lib/media/stall_detector.js +0 -3
  90. package/lib/media/streaming_engine.js +41 -21
  91. package/lib/mss/mss_parser.js +18 -2
  92. package/lib/net/networking_engine.js +66 -12
  93. package/lib/net/networking_utils.js +95 -0
  94. package/lib/offline/indexeddb/v1_storage_cell.js +2 -0
  95. package/lib/offline/indexeddb/v2_storage_cell.js +2 -0
  96. package/lib/offline/manifest_converter.js +2 -0
  97. package/lib/offline/offline_manifest_parser.js +10 -0
  98. package/lib/offline/storage.js +11 -7
  99. package/lib/player.js +993 -1502
  100. package/lib/polyfill/mediasource.js +22 -1
  101. package/lib/text/mp4_ttml_parser.js +2 -2
  102. package/lib/text/srt_text_parser.js +2 -2
  103. package/lib/text/stub_text_displayer.js +51 -0
  104. package/lib/text/text_engine.js +3 -2
  105. package/lib/text/ttml_text_parser.js +28 -5
  106. package/lib/text/ui_text_displayer.js +4 -0
  107. package/lib/text/vtt_text_parser.js +7 -2
  108. package/lib/transmuxer/aac_transmuxer.js +1 -1
  109. package/lib/transmuxer/ac3_transmuxer.js +1 -1
  110. package/lib/transmuxer/ec3_transmuxer.js +1 -1
  111. package/lib/transmuxer/h264.js +5 -4
  112. package/lib/transmuxer/h265.js +5 -4
  113. package/lib/transmuxer/mp3_transmuxer.js +1 -1
  114. package/lib/transmuxer/mpeg_ts_transmuxer.js +1 -1
  115. package/lib/transmuxer/mss_transmuxer.js +1 -1
  116. package/lib/transmuxer/ts_transmuxer.js +33 -13
  117. package/lib/util/buffer_utils.js +26 -4
  118. package/lib/util/content_steering_manager.js +358 -0
  119. package/lib/util/error.js +2 -6
  120. package/lib/util/fairplay_utils.js +1 -1
  121. package/lib/util/fake_event.js +5 -1
  122. package/lib/util/id3_utils.js +50 -1
  123. package/lib/util/manifest_parser_utils.js +12 -8
  124. package/lib/util/mp4_generator.js +2 -4
  125. package/lib/util/mutex.js +59 -0
  126. package/lib/util/object_utils.js +1 -1
  127. package/lib/util/periods.js +154 -179
  128. package/lib/util/platform.js +4 -3
  129. package/lib/util/player_configuration.js +35 -6
  130. package/lib/util/stream_utils.js +105 -98
  131. package/lib/util/ts_parser.js +124 -73
  132. package/lib/util/uint8array_utils.js +5 -1
  133. package/lib/util/xml_utils.js +5 -0
  134. package/maintained-branches.md +2 -2
  135. package/package.json +1 -1
  136. package/roadmap.md +15 -5
  137. package/shaka-player.uncompiled.js +0 -1
  138. package/test/abr/simple_abr_manager_unit.js +33 -33
  139. package/test/ads/ad_manager_unit.js +3 -2
  140. package/test/cast/cast_receiver_integration.js +3 -4
  141. package/test/cast/cast_utils_unit.js +2 -0
  142. package/test/cea/cea_decoder_unit.js +7 -0
  143. package/test/codec_switching/codec_switching_integration.js +2 -1
  144. package/test/dash/dash_parser_integration.js +2 -1
  145. package/test/dash/dash_parser_live_unit.js +0 -19
  146. package/test/dash/dash_parser_manifest_unit.js +494 -2
  147. package/test/dash/mpd_utils_unit.js +61 -30
  148. package/test/hls/hls_live_unit.js +12 -3
  149. package/test/hls/hls_parser_unit.js +377 -27
  150. package/test/hls/manifest_text_parser_unit.js +32 -99
  151. package/test/media/adaptation_set_criteria_unit.js +172 -21
  152. package/test/media/adaptation_set_unit.js +1 -0
  153. package/test/media/content_workarounds_unit.js +1 -1
  154. package/test/media/drm_engine_unit.js +3 -3
  155. package/test/media/media_source_engine_unit.js +1 -1
  156. package/test/media/play_rate_controller_unit.js +0 -48
  157. package/test/media/segment_index_unit.js +2 -1
  158. package/test/mss/mss_player_integration.js +3 -2
  159. package/test/net/networking_engine_unit.js +3 -3
  160. package/test/{media/manifest_parser_unit.js → net/networking_utils_unit.js} +2 -2
  161. package/test/offline/manifest_convert_unit.js +9 -0
  162. package/test/offline/offline_integration.js +2 -1
  163. package/test/offline/storage_integration.js +21 -9
  164. package/test/player_external.js +4 -2
  165. package/test/player_integration.js +15 -4
  166. package/test/player_load_graph_integration.js +47 -151
  167. package/test/player_src_equals_external.js +1 -0
  168. package/test/player_src_equals_integration.js +71 -4
  169. package/test/player_unit.js +80 -27
  170. package/test/test/util/fake_media_source_engine.js +5 -0
  171. package/test/test/util/fake_networking_engine.js +9 -1
  172. package/test/test/util/fake_segment_prefetch.js +3 -0
  173. package/test/test/util/manifest_generator.js +4 -0
  174. package/test/test/util/manifest_parser_util.js +14 -2
  175. package/test/test/util/offline_utils.js +1 -0
  176. package/test/test/util/simple_fakes.js +2 -2
  177. package/test/test/util/streaming_engine_util.js +3 -0
  178. package/test/test/util/test_scheme.js +6 -0
  179. package/test/test/util/ui_utils.js +4 -3
  180. package/test/text/mp4_ttml_parser_unit.js +4 -4
  181. package/test/text/srt_text_parser_unit.js +1 -1
  182. package/test/text/text_displayer_layout_unit.js +1 -1
  183. package/test/text/text_engine_unit.js +6 -0
  184. package/test/text/ttml_text_parser_unit.js +33 -2
  185. package/test/transmuxer/transmuxer_engine_integration.js +9 -0
  186. package/test/transmuxer/transmuxer_integration.js +33 -3
  187. package/test/ui/ad_ui_unit.js +2 -2
  188. package/test/ui/ui_customization_unit.js +17 -16
  189. package/test/ui/ui_integration.js +30 -20
  190. package/test/ui/ui_unit.js +25 -19
  191. package/test/util/buffer_utils_unit.js +1 -1
  192. package/test/util/content_steering_manager_unit.js +427 -0
  193. package/test/util/id3_utils_unit.js +52 -0
  194. package/test/util/stream_utils_unit.js +17 -113
  195. package/test/util/ts_parser_unit.js +3 -3
  196. package/third_party/closure-uri/uri.js +13 -1
  197. package/ui/audio_language_selection.js +9 -2
  198. package/ui/externs/ui.js +17 -2
  199. package/ui/language_utils.js +43 -8
  200. package/ui/less/range_elements.less +33 -0
  201. package/ui/locales/en.json +1 -0
  202. package/ui/locales/es-419.json +1 -0
  203. package/ui/locales/es.json +1 -0
  204. package/ui/locales/source.json +4 -0
  205. package/ui/localization.js +10 -0
  206. package/ui/remote_button.js +3 -1
  207. package/ui/resolution_selection.js +47 -10
  208. package/ui/seek_bar.js +296 -3
  209. package/ui/text_selection.js +2 -1
  210. package/ui/ui.js +6 -2
  211. package/lib/routing/node.js +0 -23
  212. package/lib/routing/payload.js +0 -47
  213. package/lib/routing/walker.js +0 -587
  214. package/test/routing/walker_unit.js +0 -505
@@ -29,22 +29,24 @@ shaka.dash.MpdUtils = class {
29
29
  * @param {string} uriTemplate
30
30
  * @param {?string} representationId
31
31
  * @param {?number} number
32
+ * @param {?number} subNumber
32
33
  * @param {?number} bandwidth
33
34
  * @param {?(number|bigint)} time
34
35
  * @return {string} A URI string.
35
36
  * @see ISO/IEC 23009-1:2014 section 5.3.9.4.4
36
37
  */
37
38
  static fillUriTemplate(
38
- uriTemplate, representationId, number, bandwidth, time) {
39
+ uriTemplate, representationId, number, subNumber, bandwidth, time) {
39
40
  /** @type {!Object.<string, ?number|?string>} */
40
41
  const valueTable = {
41
42
  'RepresentationID': representationId,
42
43
  'Number': number,
44
+ 'SubNumber': subNumber,
43
45
  'Bandwidth': bandwidth,
44
46
  'Time': time,
45
47
  };
46
48
 
47
- const re = /\$(RepresentationID|Number|Bandwidth|Time)?(?:%0([0-9]+)([diouxX]))?\$/g; // eslint-disable-line max-len
49
+ const re = /\$(RepresentationID|Number|SubNumber|Bandwidth|Time)?(?:%0([0-9]+)([diouxX]))?\$/g; // eslint-disable-line max-len
48
50
  const uri = uriTemplate.replace(re, (match, name, widthStr, format) => {
49
51
  if (match == '$$') {
50
52
  return '$';
@@ -126,11 +128,12 @@ shaka.dash.MpdUtils = class {
126
128
  * @param {number} unscaledPresentationTimeOffset
127
129
  * @param {number} periodDuration The Period's duration in seconds.
128
130
  * Infinity indicates that the Period continues indefinitely.
131
+ * @param {number} startNumber
129
132
  * @return {!Array.<shaka.media.PresentationTimeline.TimeRange>}
130
133
  */
131
134
  static createTimeline(
132
135
  segmentTimeline, timescale, unscaledPresentationTimeOffset,
133
- periodDuration) {
136
+ periodDuration, startNumber) {
134
137
  goog.asserts.assert(
135
138
  timescale > 0 && timescale < Infinity,
136
139
  'timescale must be a positive, finite integer');
@@ -154,6 +157,10 @@ shaka.dash.MpdUtils = class {
154
157
  XmlUtils.parseAttr(timePoint, 'd', XmlUtils.parseNonNegativeInt);
155
158
  const r = XmlUtils.parseAttr(timePoint, 'r', XmlUtils.parseInt);
156
159
 
160
+ const k = XmlUtils.parseAttr(timePoint, 'k', XmlUtils.parseInt);
161
+
162
+ const partialSegments = k || 0;
163
+
157
164
  // Adjust the start time to account for the presentation time offset.
158
165
  if (t != null) {
159
166
  t -= unscaledPresentationTimeOffset;
@@ -236,6 +243,8 @@ shaka.dash.MpdUtils = class {
236
243
  start: startTime / timescale,
237
244
  end: endTime / timescale,
238
245
  unscaledStart: startTime,
246
+ partialSegments: partialSegments,
247
+ segmentPosition: timeline.length + startNumber,
239
248
  };
240
249
  timeline.push(item);
241
250
 
@@ -299,7 +308,7 @@ shaka.dash.MpdUtils = class {
299
308
  if (timelineNode) {
300
309
  timeline = MpdUtils.createTimeline(
301
310
  timelineNode, timescale, unscaledPresentationTimeOffset,
302
- context.periodInfo.duration || Infinity);
311
+ context.periodInfo.duration || Infinity, startNumber);
303
312
  }
304
313
 
305
314
  const scaledPresentationTimeOffset =
@@ -315,30 +324,46 @@ shaka.dash.MpdUtils = class {
315
324
  }
316
325
 
317
326
  /**
318
- * Searches the inheritance for a Segment* with the given attribute.
319
- *
327
+ * Parses common attributes for Representation, AdaptationSet, and Period.
320
328
  * @param {shaka.dash.DashParser.Context} context
321
329
  * @param {function(?shaka.dash.DashParser.InheritanceFrame):Element} callback
322
- * Gets the Element that contains the attribute to inherit.
323
- * @param {string} attribute
324
- * @return {?string}
325
- */
326
- static inheritAttribute(context, callback, attribute) {
330
+ * @return {!Array.<!Element>}
331
+ */
332
+ static getNodes(context, callback) {
327
333
  const Functional = shaka.util.Functional;
328
334
  goog.asserts.assert(
329
335
  callback(context.representation),
330
- 'There must be at least one element of the given type');
336
+ 'There must be at least one element of the given type',
337
+ );
331
338
 
332
- /** @type {!Array.<!Element>} */
333
- const nodes = [
339
+ return [
334
340
  callback(context.representation),
335
341
  callback(context.adaptationSet),
336
342
  callback(context.period),
337
343
  ].filter(Functional.isNotNull);
344
+ }
338
345
 
339
- return nodes
340
- .map((s) => { return s.getAttribute(attribute); })
341
- .reduce((all, part) => { return all || part; });
346
+ /**
347
+ * Searches the inheritance for a Segment* with the given attribute.
348
+ *
349
+ * @param {shaka.dash.DashParser.Context} context
350
+ * @param {function(?shaka.dash.DashParser.InheritanceFrame):Element} callback
351
+ * Gets the Element that contains the attribute to inherit.
352
+ * @param {string} attribute
353
+ * @return {?string}
354
+ */
355
+ static inheritAttribute(context, callback, attribute) {
356
+ const MpdUtils = shaka.dash.MpdUtils;
357
+ const nodes = MpdUtils.getNodes(context, callback);
358
+
359
+ let result = null;
360
+ for (const node of nodes) {
361
+ result = node.getAttribute(attribute);
362
+ if (result) {
363
+ break;
364
+ }
365
+ }
366
+ return result;
342
367
  }
343
368
 
344
369
  /**
@@ -351,22 +376,18 @@ shaka.dash.MpdUtils = class {
351
376
  * @return {Element}
352
377
  */
353
378
  static inheritChild(context, callback, child) {
354
- const Functional = shaka.util.Functional;
355
- goog.asserts.assert(
356
- callback(context.representation),
357
- 'There must be at least one element of the given type');
358
-
359
- /** @type {!Array.<!Element>} */
360
- const nodes = [
361
- callback(context.representation),
362
- callback(context.adaptationSet),
363
- callback(context.period),
364
- ].filter(Functional.isNotNull);
379
+ const MpdUtils = shaka.dash.MpdUtils;
380
+ const nodes = MpdUtils.getNodes(context, callback);
365
381
 
366
382
  const XmlUtils = shaka.util.XmlUtils;
367
- return nodes
368
- .map((s) => { return XmlUtils.findChild(s, child); })
369
- .reduce((all, part) => { return all || part; });
383
+ let result = null;
384
+ for (const node of nodes) {
385
+ result = XmlUtils.findChild(node, child);
386
+ if (result) {
387
+ break;
388
+ }
389
+ }
390
+ return result;
370
391
  }
371
392
 
372
393
  /**
@@ -45,11 +45,10 @@ shaka.dash.SegmentBase = class {
45
45
  return null;
46
46
  }
47
47
 
48
- let resolvedUris = context.representation.baseUris;
48
+ let resolvedUris = context.representation.getBaseUris();
49
49
  const uri = initialization.getAttribute('sourceURL');
50
50
  if (uri) {
51
- resolvedUris = ManifestParserUtils.resolveUris(
52
- context.representation.baseUris, [uri]);
51
+ resolvedUris = ManifestParserUtils.resolveUris(resolvedUris, [uri]);
53
52
  }
54
53
 
55
54
  let startByte = 0;
@@ -249,12 +248,12 @@ shaka.dash.SegmentBase = class {
249
248
  const representationIndex = MpdUtils.inheritChild(
250
249
  context, SegmentBase.fromInheritance_, 'RepresentationIndex');
251
250
 
252
- let indexUris = context.representation.baseUris;
251
+ let indexUris = context.representation.getBaseUris();
253
252
  if (representationIndex) {
254
253
  const representationUri = representationIndex.getAttribute('sourceURL');
255
254
  if (representationUri) {
256
255
  indexUris = ManifestParserUtils.resolveUris(
257
- context.representation.baseUris, [representationUri]);
256
+ indexUris, [representationUri]);
258
257
  }
259
258
  }
260
259
 
@@ -59,7 +59,7 @@ shaka.dash.SegmentList = class {
59
59
 
60
60
  const references = SegmentList.createSegmentReferences_(
61
61
  context.periodInfo.start, context.periodInfo.duration,
62
- info.startNumber, context.representation.baseUris, info,
62
+ info.startNumber, context.representation.getBaseUris, info,
63
63
  initSegmentReference, aes128Key);
64
64
 
65
65
  const isNew = !segmentIndex;
@@ -195,7 +195,7 @@ shaka.dash.SegmentList = class {
195
195
  * @param {number} periodStart in seconds.
196
196
  * @param {?number} periodDuration in seconds.
197
197
  * @param {number} startNumber
198
- * @param {!Array.<string>} baseUris
198
+ * @param {function():!Array.<string>} getBaseUris
199
199
  * @param {shaka.dash.SegmentList.SegmentListInfo} info
200
200
  * @param {shaka.media.InitSegmentReference} initSegmentReference
201
201
  * @param {shaka.extern.aes128Key|undefined} aes128Key
@@ -203,7 +203,7 @@ shaka.dash.SegmentList = class {
203
203
  * @private
204
204
  */
205
205
  static createSegmentReferences_(
206
- periodStart, periodDuration, startNumber, baseUris, info,
206
+ periodStart, periodDuration, startNumber, getBaseUris, info,
207
207
  initSegmentReference, aes128Key) {
208
208
  const ManifestParserUtils = shaka.util.ManifestParserUtils;
209
209
 
@@ -246,7 +246,8 @@ shaka.dash.SegmentList = class {
246
246
  let uris = null;
247
247
  const getUris = () => {
248
248
  if (uris == null) {
249
- uris = ManifestParserUtils.resolveUris(baseUris, [segment.mediaUri]);
249
+ uris = ManifestParserUtils.resolveUris(
250
+ getBaseUris(), [segment.mediaUri]);
250
251
  }
251
252
  return uris;
252
253
  };
@@ -131,12 +131,13 @@ shaka.dash.SegmentTemplate = class {
131
131
  info,
132
132
  context.representation.id,
133
133
  context.bandwidth,
134
- context.representation.baseUris,
134
+ context.representation.getBaseUris,
135
135
  periodStart,
136
136
  periodEnd,
137
137
  initSegmentReference,
138
138
  shouldFit,
139
139
  aes128Key,
140
+ context.representation.segmentSequenceCadence,
140
141
  );
141
142
  } else {
142
143
  const tsi = /** @type {!TimelineSegmentIndex} */(segmentIndex);
@@ -284,10 +285,10 @@ shaka.dash.SegmentTemplate = class {
284
285
  goog.asserts.assert(info.indexTemplate, 'must be using index template');
285
286
  const filledTemplate = MpdUtils.fillUriTemplate(
286
287
  info.indexTemplate, context.representation.id,
287
- null, context.bandwidth || null, null);
288
+ null, null, context.bandwidth || null, null);
288
289
 
289
290
  const resolvedUris = ManifestParserUtils.resolveUris(
290
- context.representation.baseUris, [filledTemplate]);
291
+ context.representation.getBaseUris(), [filledTemplate]);
291
292
 
292
293
  return shaka.dash.SegmentBase.generateSegmentIndexFromUris(
293
294
  context, requestSegment, init, resolvedUris, 0, null,
@@ -345,7 +346,7 @@ shaka.dash.SegmentTemplate = class {
345
346
  const template = info.mediaTemplate;
346
347
  const bandwidth = context.bandwidth || null;
347
348
  const id = context.representation.id;
348
- const baseUris = context.representation.baseUris;
349
+ const getBaseUris = context.representation.getBaseUris;
349
350
 
350
351
  const timestampOffset = periodStart - info.scaledPresentationTimeOffset;
351
352
 
@@ -429,8 +430,8 @@ shaka.dash.SegmentTemplate = class {
429
430
  time = BigInt(segmentMediaTime) * BigInt(timescale);
430
431
  }
431
432
  const mediaUri = MpdUtils.fillUriTemplate(
432
- template, id, position, bandwidth, time);
433
- return ManifestParserUtils.resolveUris(baseUris, [mediaUri]);
433
+ template, id, position, /* subNumber= */ null, bandwidth, time);
434
+ return ManifestParserUtils.resolveUris(getBaseUris(), [mediaUri]);
434
435
  };
435
436
 
436
437
  // Relative to the presentation.
@@ -540,13 +541,13 @@ shaka.dash.SegmentTemplate = class {
540
541
 
541
542
  const repId = context.representation.id;
542
543
  const bandwidth = context.bandwidth || null;
543
- const baseUris = context.representation.baseUris;
544
+ const getBaseUris = context.representation.getBaseUris;
544
545
  const getUris = () => {
545
546
  goog.asserts.assert(initialization, 'Should have returned earler');
546
547
  const filledTemplate = MpdUtils.fillUriTemplate(
547
- initialization, repId, null, bandwidth, null);
548
+ initialization, repId, null, null, bandwidth, null);
548
549
  const resolvedUris = ManifestParserUtils.resolveUris(
549
- baseUris, [filledTemplate]);
550
+ getBaseUris(), [filledTemplate]);
550
551
  return resolvedUris;
551
552
  };
552
553
  const qualityInfo = shaka.dash.SegmentBase.createQualityInfo(context);
@@ -579,16 +580,17 @@ shaka.dash.TimelineSegmentIndex = class extends shaka.media.SegmentIndex {
579
580
  * @param {!shaka.dash.SegmentTemplate.SegmentTemplateInfo} templateInfo
580
581
  * @param {?string} representationId
581
582
  * @param {number} bandwidth
582
- * @param {Array.<string>} baseUris
583
+ * @param {function():Array.<string>} getBaseUris
583
584
  * @param {number} periodStart
584
585
  * @param {number} periodEnd
585
586
  * @param {shaka.media.InitSegmentReference} initSegmentReference
586
587
  * @param {boolean} shouldFit
587
588
  * @param {shaka.extern.aes128Key|undefined} aes128Key
589
+ * @param {number} segmentSequenceCadence
588
590
  */
589
- constructor(templateInfo, representationId, bandwidth, baseUris,
591
+ constructor(templateInfo, representationId, bandwidth, getBaseUris,
590
592
  periodStart, periodEnd, initSegmentReference, shouldFit,
591
- aes128Key) {
593
+ aes128Key, segmentSequenceCadence) {
592
594
  super([]);
593
595
 
594
596
  /** @private {?shaka.dash.SegmentTemplate.SegmentTemplateInfo} */
@@ -597,8 +599,8 @@ shaka.dash.TimelineSegmentIndex = class extends shaka.media.SegmentIndex {
597
599
  this.representationId_ = representationId;
598
600
  /** @private {number} */
599
601
  this.bandwidth_ = bandwidth;
600
- /** @private {Array.<string>} */
601
- this.baseUris_ = baseUris;
602
+ /** @private {function():Array.<string>} */
603
+ this.getBaseUris_ = getBaseUris;
602
604
  /** @private {number} */
603
605
  this.periodStart_ = periodStart;
604
606
  /** @private {number} */
@@ -607,6 +609,8 @@ shaka.dash.TimelineSegmentIndex = class extends shaka.media.SegmentIndex {
607
609
  this.initSegmentReference_ = initSegmentReference;
608
610
  /** @private {shaka.extern.aes128Key|undefined} */
609
611
  this.aes128Key_ = aes128Key;
612
+ /** @private {number} */
613
+ this.segmentSequenceCadence_ = segmentSequenceCadence;
610
614
 
611
615
 
612
616
  if (shouldFit) {
@@ -814,29 +818,81 @@ shaka.dash.TimelineSegmentIndex = class extends shaka.media.SegmentIndex {
814
818
  let ref = this.references[correctedPosition];
815
819
 
816
820
  if (!ref) {
821
+ const mediaTemplate = this.templateInfo_.mediaTemplate;
817
822
  const range = this.templateInfo_.timeline[correctedPosition];
818
- const segmentReplacement = position + this.templateInfo_.startNumber;
823
+ const segmentReplacement = range.segmentPosition;
819
824
  const timeReplacement = this.templateInfo_
820
825
  .unscaledPresentationTimeOffset + range.unscaledStart;
826
+ const timestampOffset = this.periodStart_ -
827
+ this.templateInfo_.scaledPresentationTimeOffset;
828
+
829
+ const partialSegmentRefs = [];
830
+
831
+ const partialDuration = (range.end - range.start) / range.partialSegments;
832
+
833
+ for (let i = 0; i < range.partialSegments; i++) {
834
+ const start = range.start + partialDuration * i;
835
+ const end = start + partialDuration;
836
+ const subNumber = i + 1;
837
+ let uris = null;
838
+ const getPartialUris = () => {
839
+ if (!this.templateInfo_) {
840
+ return [];
841
+ }
842
+ if (uris == null) {
843
+ uris = shaka.dash.TimelineSegmentIndex.createUris_(
844
+ this.templateInfo_.mediaTemplate,
845
+ this.representationId_,
846
+ segmentReplacement,
847
+ this.bandwidth_,
848
+ timeReplacement,
849
+ subNumber,
850
+ this.getBaseUris_);
851
+ }
852
+ return uris;
853
+ };
854
+ const partial = new shaka.media.SegmentReference(
855
+ this.periodStart_ + start,
856
+ this.periodStart_ + end,
857
+ getPartialUris,
858
+ /* startByte= */ 0,
859
+ /* endByte= */ null,
860
+ this.initSegmentReference_,
861
+ timestampOffset,
862
+ this.periodStart_,
863
+ this.periodEnd_,
864
+ /* partialReferences= */ [],
865
+ /* tilesLayout= */ '',
866
+ /* tileDuration= */ null,
867
+ /* syncTime= */ null,
868
+ shaka.media.SegmentReference.Status.AVAILABLE,
869
+ this.aes128Key_);
870
+ if (this.segmentSequenceCadence_ == 0) {
871
+ if (i > 0) {
872
+ partial.markAsNonIndependent();
873
+ }
874
+ } else if ((i % this.segmentSequenceCadence_) != 0) {
875
+ partial.markAsNonIndependent();
876
+ }
877
+ partialSegmentRefs.push(partial);
878
+ }
821
879
 
822
880
  const createUrisCb = () => {
823
- if (!this.templateInfo_) {
881
+ if (range.partialSegments > 0) {
824
882
  return [];
825
883
  }
826
884
  return shaka.dash.TimelineSegmentIndex
827
885
  .createUris_(
828
- this.templateInfo_.mediaTemplate,
886
+ mediaTemplate,
829
887
  this.representationId_,
830
888
  segmentReplacement,
831
889
  this.bandwidth_,
832
890
  timeReplacement,
833
- this.baseUris_,
891
+ /* subNumber= */ null,
892
+ this.getBaseUris_,
834
893
  );
835
894
  };
836
895
 
837
- const timestampOffset = this.periodStart_ -
838
- this.templateInfo_.scaledPresentationTimeOffset;
839
-
840
896
  ref = new shaka.media.SegmentReference(
841
897
  this.periodStart_ + range.start,
842
898
  this.periodStart_ + range.end,
@@ -847,12 +903,13 @@ shaka.dash.TimelineSegmentIndex = class extends shaka.media.SegmentIndex {
847
903
  timestampOffset,
848
904
  this.periodStart_,
849
905
  this.periodEnd_,
850
- /* partialReferences= */ [],
906
+ partialSegmentRefs,
851
907
  /* tilesLayout= */ '',
852
908
  /* tileDuration= */ null,
853
909
  /* syncTime= */ null,
854
910
  shaka.media.SegmentReference.Status.AVAILABLE,
855
- this.aes128Key_);
911
+ this.aes128Key_,
912
+ /* allPartialSegments= */ range.partialSegments > 0);
856
913
  this.references[correctedPosition] = ref;
857
914
  }
858
915
 
@@ -866,12 +923,12 @@ shaka.dash.TimelineSegmentIndex = class extends shaka.media.SegmentIndex {
866
923
  * @private
867
924
  */
868
925
  static createUris_(mediaTemplate, repId, segmentReplacement,
869
- bandwidth, timeReplacement, baseUris) {
926
+ bandwidth, timeReplacement, subNumber, getBaseUris) {
870
927
  const mediaUri = shaka.dash.MpdUtils.fillUriTemplate(
871
928
  mediaTemplate, repId,
872
- segmentReplacement, bandwidth || null, timeReplacement);
929
+ segmentReplacement, subNumber, bandwidth || null, timeReplacement);
873
930
  return shaka.util.ManifestParserUtils
874
- .resolveUris(baseUris, [mediaUri])
931
+ .resolveUris(getBaseUris(), [mediaUri])
875
932
  .map((g) => {
876
933
  return g.toString();
877
934
  });
@@ -11,7 +11,6 @@ goog.provide('shaka.hls.Segment');
11
11
  goog.provide('shaka.hls.Tag');
12
12
 
13
13
  goog.require('goog.asserts');
14
- goog.require('shaka.hls.Utils');
15
14
  goog.require('shaka.util.Error');
16
15
 
17
16
 
@@ -20,19 +19,11 @@ goog.require('shaka.util.Error');
20
19
  */
21
20
  shaka.hls.Playlist = class {
22
21
  /**
23
- * @param {string} absoluteUri An absolute, final URI after redirects.
24
22
  * @param {!shaka.hls.PlaylistType} type
25
23
  * @param {!Array.<shaka.hls.Tag>} tags
26
24
  * @param {!Array.<shaka.hls.Segment>=} segments
27
25
  */
28
- constructor(absoluteUri, type, tags, segments) {
29
- /**
30
- * An absolute, final URI after redirects.
31
- *
32
- * @const {string}
33
- */
34
- this.absoluteUri = absoluteUri;
35
-
26
+ constructor(type, tags, segments) {
36
27
  /** @const {shaka.hls.PlaylistType} */
37
28
  this.type = type;
38
29
 
@@ -87,10 +78,11 @@ shaka.hls.Tag = class {
87
78
  * As in some cases (like in tests) the tag never existed in string form, it
88
79
  * is far easier to recreate the tag from the parsed form.
89
80
  *
81
+ * @param {?Set.<string>=} attributesToSkip
90
82
  * @return {string}
91
83
  * @override
92
84
  */
93
- toString() {
85
+ toString(attributesToSkip) {
94
86
  /**
95
87
  * @param {shaka.hls.Attribute} attr
96
88
  * @return {string}
@@ -107,7 +99,12 @@ shaka.hls.Tag = class {
107
99
  // 4) <NAME>:<VALUE>,<ATTRIBUTE_LIST>
108
100
 
109
101
  let tagStr = '#' + this.name;
110
- const appendages = this.attributes ? this.attributes.map(attrToStr) : [];
102
+ const appendages = this.attributes ? this.attributes.filter((attr) => {
103
+ if (!attributesToSkip) {
104
+ return true;
105
+ }
106
+ return !attributesToSkip.has(attr.name);
107
+ }).map(attrToStr) : [];
111
108
 
112
109
  if (this.value) {
113
110
  appendages.unshift(this.value);
@@ -120,6 +117,22 @@ shaka.hls.Tag = class {
120
117
  return tagStr;
121
118
  }
122
119
 
120
+ /**
121
+ * Create the string key of the tag.
122
+ *
123
+ * @return {string}
124
+ */
125
+ getTagKey() {
126
+ const attributesToSkip = new Set()
127
+ .add('AUDIO')
128
+ .add('VIDEO')
129
+ .add('SUBTITLES')
130
+ .add('PATHWAY-ID')
131
+ .add('GROUP-ID')
132
+ .add('URI');
133
+ return this.toString(attributesToSkip);
134
+ }
135
+
123
136
  /**
124
137
  * Adds an attribute to an HLS Tag.
125
138
  *
@@ -203,48 +216,20 @@ shaka.hls.Segment = class {
203
216
  /**
204
217
  * Creates an HLS segment object.
205
218
  *
206
- * @param {string} absoluteMediaPlaylistUri An absolute Media Playlist URI.
207
219
  * @param {string} verbatimSegmentUri verbatim segment URI.
208
220
  * @param {!Array.<shaka.hls.Tag>} tags
209
221
  * @param {!Array.<shaka.hls.Tag>=} partialSegments
210
222
  */
211
- constructor(absoluteMediaPlaylistUri, verbatimSegmentUri, tags,
212
- partialSegments=[]) {
223
+ constructor(verbatimSegmentUri, tags, partialSegments=[]) {
213
224
  /** @const {!Array.<shaka.hls.Tag>} */
214
225
  this.tags = tags;
215
226
 
216
- /** @type {?string} */
217
- this.absoluteUri_ = null;
218
-
219
- /** @type {?string} */
220
- this.absoluteMediaPlaylistUri_ = absoluteMediaPlaylistUri;
221
-
222
- /** @type {?string} */
223
- this.verbatimSegmentUri_ = verbatimSegmentUri;
227
+ /** @const {?string} */
228
+ this.verbatimSegmentUri = verbatimSegmentUri;
224
229
 
225
230
  /** @type {!Array.<shaka.hls.Tag>} */
226
231
  this.partialSegments = partialSegments;
227
232
  }
228
-
229
- /**
230
- * Returns an absolute URI.
231
- *
232
- * @return {string}
233
- */
234
- get absoluteUri() {
235
- if (!this.absoluteUri_ &&
236
- this.absoluteMediaPlaylistUri_ && this.verbatimSegmentUri_) {
237
- goog.asserts.assert(this.absoluteMediaPlaylistUri_,
238
- 'An absolute Media Playlist URI should be defined!');
239
- goog.asserts.assert(this.verbatimSegmentUri_,
240
- 'An verbatim segment URI should be defined!');
241
- this.absoluteUri_ = shaka.hls.Utils.constructAbsoluteUri(
242
- this.absoluteMediaPlaylistUri_, this.verbatimSegmentUri_);
243
- this.absoluteMediaPlaylistUri_ = null;
244
- this.verbatimSegmentUri_ = null;
245
- }
246
- return this.absoluteUri_ || '';
247
- }
248
233
  };
249
234
 
250
235