shaka-player 4.5.0 → 4.6.1

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 +109 -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 +34 -30
  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 +789 -738
  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 +1147 -1113
  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 +857 -791
  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 +1359 -1314
  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 +251 -84
  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 +92 -29
  70. package/lib/hls/hls_classes.js +28 -43
  71. package/lib/hls/hls_parser.js +770 -652
  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 +72 -44
  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 +998 -1503
  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 +417 -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,16 +131,18 @@ 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);
143
- tsi.appendTemplateInfo(info, periodStart, periodEnd, shouldFit);
144
+ tsi.appendTemplateInfo(
145
+ info, periodStart, periodEnd, shouldFit, initSegmentReference);
144
146
 
145
147
  const availabilityStart =
146
148
  context.presentationTimeline.getSegmentAvailabilityStart();
@@ -166,7 +168,7 @@ shaka.dash.SegmentTemplate = class {
166
168
  if (segmentIndex instanceof shaka.dash.TimelineSegmentIndex &&
167
169
  segmentIndex.isEmpty()) {
168
170
  segmentIndex.appendTemplateInfo(info, periodStart,
169
- periodEnd, shouldFit);
171
+ periodEnd, shouldFit, initSegmentReference);
170
172
  }
171
173
  return Promise.resolve(segmentIndex);
172
174
  },
@@ -284,10 +286,10 @@ shaka.dash.SegmentTemplate = class {
284
286
  goog.asserts.assert(info.indexTemplate, 'must be using index template');
285
287
  const filledTemplate = MpdUtils.fillUriTemplate(
286
288
  info.indexTemplate, context.representation.id,
287
- null, context.bandwidth || null, null);
289
+ null, null, context.bandwidth || null, null);
288
290
 
289
291
  const resolvedUris = ManifestParserUtils.resolveUris(
290
- context.representation.baseUris, [filledTemplate]);
292
+ context.representation.getBaseUris(), [filledTemplate]);
291
293
 
292
294
  return shaka.dash.SegmentBase.generateSegmentIndexFromUris(
293
295
  context, requestSegment, init, resolvedUris, 0, null,
@@ -345,7 +347,7 @@ shaka.dash.SegmentTemplate = class {
345
347
  const template = info.mediaTemplate;
346
348
  const bandwidth = context.bandwidth || null;
347
349
  const id = context.representation.id;
348
- const baseUris = context.representation.baseUris;
350
+ const getBaseUris = context.representation.getBaseUris;
349
351
 
350
352
  const timestampOffset = periodStart - info.scaledPresentationTimeOffset;
351
353
 
@@ -429,8 +431,8 @@ shaka.dash.SegmentTemplate = class {
429
431
  time = BigInt(segmentMediaTime) * BigInt(timescale);
430
432
  }
431
433
  const mediaUri = MpdUtils.fillUriTemplate(
432
- template, id, position, bandwidth, time);
433
- return ManifestParserUtils.resolveUris(baseUris, [mediaUri]);
434
+ template, id, position, /* subNumber= */ null, bandwidth, time);
435
+ return ManifestParserUtils.resolveUris(getBaseUris(), [mediaUri]);
434
436
  };
435
437
 
436
438
  // Relative to the presentation.
@@ -540,13 +542,13 @@ shaka.dash.SegmentTemplate = class {
540
542
 
541
543
  const repId = context.representation.id;
542
544
  const bandwidth = context.bandwidth || null;
543
- const baseUris = context.representation.baseUris;
545
+ const getBaseUris = context.representation.getBaseUris;
544
546
  const getUris = () => {
545
547
  goog.asserts.assert(initialization, 'Should have returned earler');
546
548
  const filledTemplate = MpdUtils.fillUriTemplate(
547
- initialization, repId, null, bandwidth, null);
549
+ initialization, repId, null, null, bandwidth, null);
548
550
  const resolvedUris = ManifestParserUtils.resolveUris(
549
- baseUris, [filledTemplate]);
551
+ getBaseUris(), [filledTemplate]);
550
552
  return resolvedUris;
551
553
  };
552
554
  const qualityInfo = shaka.dash.SegmentBase.createQualityInfo(context);
@@ -579,16 +581,17 @@ shaka.dash.TimelineSegmentIndex = class extends shaka.media.SegmentIndex {
579
581
  * @param {!shaka.dash.SegmentTemplate.SegmentTemplateInfo} templateInfo
580
582
  * @param {?string} representationId
581
583
  * @param {number} bandwidth
582
- * @param {Array.<string>} baseUris
584
+ * @param {function():Array.<string>} getBaseUris
583
585
  * @param {number} periodStart
584
586
  * @param {number} periodEnd
585
587
  * @param {shaka.media.InitSegmentReference} initSegmentReference
586
588
  * @param {boolean} shouldFit
587
589
  * @param {shaka.extern.aes128Key|undefined} aes128Key
590
+ * @param {number} segmentSequenceCadence
588
591
  */
589
- constructor(templateInfo, representationId, bandwidth, baseUris,
592
+ constructor(templateInfo, representationId, bandwidth, getBaseUris,
590
593
  periodStart, periodEnd, initSegmentReference, shouldFit,
591
- aes128Key) {
594
+ aes128Key, segmentSequenceCadence) {
592
595
  super([]);
593
596
 
594
597
  /** @private {?shaka.dash.SegmentTemplate.SegmentTemplateInfo} */
@@ -597,8 +600,8 @@ shaka.dash.TimelineSegmentIndex = class extends shaka.media.SegmentIndex {
597
600
  this.representationId_ = representationId;
598
601
  /** @private {number} */
599
602
  this.bandwidth_ = bandwidth;
600
- /** @private {Array.<string>} */
601
- this.baseUris_ = baseUris;
603
+ /** @private {function():Array.<string>} */
604
+ this.getBaseUris_ = getBaseUris;
602
605
  /** @private {number} */
603
606
  this.periodStart_ = periodStart;
604
607
  /** @private {number} */
@@ -607,6 +610,8 @@ shaka.dash.TimelineSegmentIndex = class extends shaka.media.SegmentIndex {
607
610
  this.initSegmentReference_ = initSegmentReference;
608
611
  /** @private {shaka.extern.aes128Key|undefined} */
609
612
  this.aes128Key_ = aes128Key;
613
+ /** @private {number} */
614
+ this.segmentSequenceCadence_ = segmentSequenceCadence;
610
615
 
611
616
 
612
617
  if (shouldFit) {
@@ -680,8 +685,11 @@ shaka.dash.TimelineSegmentIndex = class extends shaka.media.SegmentIndex {
680
685
  * @param {number} periodStart
681
686
  * @param {number} periodEnd
682
687
  * @param {boolean} shouldFit
688
+ * @param {shaka.media.InitSegmentReference} initSegmentReference
683
689
  */
684
- appendTemplateInfo(info, periodStart, periodEnd, shouldFit) {
690
+ appendTemplateInfo(info, periodStart, periodEnd, shouldFit,
691
+ initSegmentReference) {
692
+ this.initSegmentReference_ = initSegmentReference;
685
693
  if (!this.templateInfo_) {
686
694
  this.templateInfo_ = info;
687
695
  this.periodStart_ = periodStart;
@@ -689,6 +697,8 @@ shaka.dash.TimelineSegmentIndex = class extends shaka.media.SegmentIndex {
689
697
  } else {
690
698
  const currentTimeline = this.templateInfo_.timeline;
691
699
 
700
+ this.templateInfo_.mediaTemplate = info.mediaTemplate;
701
+
692
702
  // Append timeline
693
703
  const lastCurrentEntry = currentTimeline[currentTimeline.length - 1];
694
704
  const newEntries = info.timeline.filter((entry) => {
@@ -814,29 +824,81 @@ shaka.dash.TimelineSegmentIndex = class extends shaka.media.SegmentIndex {
814
824
  let ref = this.references[correctedPosition];
815
825
 
816
826
  if (!ref) {
827
+ const mediaTemplate = this.templateInfo_.mediaTemplate;
817
828
  const range = this.templateInfo_.timeline[correctedPosition];
818
- const segmentReplacement = position + this.templateInfo_.startNumber;
829
+ const segmentReplacement = range.segmentPosition;
819
830
  const timeReplacement = this.templateInfo_
820
831
  .unscaledPresentationTimeOffset + range.unscaledStart;
832
+ const timestampOffset = this.periodStart_ -
833
+ this.templateInfo_.scaledPresentationTimeOffset;
834
+
835
+ const partialSegmentRefs = [];
836
+
837
+ const partialDuration = (range.end - range.start) / range.partialSegments;
838
+
839
+ for (let i = 0; i < range.partialSegments; i++) {
840
+ const start = range.start + partialDuration * i;
841
+ const end = start + partialDuration;
842
+ const subNumber = i + 1;
843
+ let uris = null;
844
+ const getPartialUris = () => {
845
+ if (!this.templateInfo_) {
846
+ return [];
847
+ }
848
+ if (uris == null) {
849
+ uris = shaka.dash.TimelineSegmentIndex.createUris_(
850
+ this.templateInfo_.mediaTemplate,
851
+ this.representationId_,
852
+ segmentReplacement,
853
+ this.bandwidth_,
854
+ timeReplacement,
855
+ subNumber,
856
+ this.getBaseUris_);
857
+ }
858
+ return uris;
859
+ };
860
+ const partial = new shaka.media.SegmentReference(
861
+ this.periodStart_ + start,
862
+ this.periodStart_ + end,
863
+ getPartialUris,
864
+ /* startByte= */ 0,
865
+ /* endByte= */ null,
866
+ this.initSegmentReference_,
867
+ timestampOffset,
868
+ this.periodStart_,
869
+ this.periodEnd_,
870
+ /* partialReferences= */ [],
871
+ /* tilesLayout= */ '',
872
+ /* tileDuration= */ null,
873
+ /* syncTime= */ null,
874
+ shaka.media.SegmentReference.Status.AVAILABLE,
875
+ this.aes128Key_);
876
+ if (this.segmentSequenceCadence_ == 0) {
877
+ if (i > 0) {
878
+ partial.markAsNonIndependent();
879
+ }
880
+ } else if ((i % this.segmentSequenceCadence_) != 0) {
881
+ partial.markAsNonIndependent();
882
+ }
883
+ partialSegmentRefs.push(partial);
884
+ }
821
885
 
822
886
  const createUrisCb = () => {
823
- if (!this.templateInfo_) {
887
+ if (range.partialSegments > 0) {
824
888
  return [];
825
889
  }
826
890
  return shaka.dash.TimelineSegmentIndex
827
891
  .createUris_(
828
- this.templateInfo_.mediaTemplate,
892
+ mediaTemplate,
829
893
  this.representationId_,
830
894
  segmentReplacement,
831
895
  this.bandwidth_,
832
896
  timeReplacement,
833
- this.baseUris_,
897
+ /* subNumber= */ null,
898
+ this.getBaseUris_,
834
899
  );
835
900
  };
836
901
 
837
- const timestampOffset = this.periodStart_ -
838
- this.templateInfo_.scaledPresentationTimeOffset;
839
-
840
902
  ref = new shaka.media.SegmentReference(
841
903
  this.periodStart_ + range.start,
842
904
  this.periodStart_ + range.end,
@@ -847,12 +909,13 @@ shaka.dash.TimelineSegmentIndex = class extends shaka.media.SegmentIndex {
847
909
  timestampOffset,
848
910
  this.periodStart_,
849
911
  this.periodEnd_,
850
- /* partialReferences= */ [],
912
+ partialSegmentRefs,
851
913
  /* tilesLayout= */ '',
852
914
  /* tileDuration= */ null,
853
915
  /* syncTime= */ null,
854
916
  shaka.media.SegmentReference.Status.AVAILABLE,
855
- this.aes128Key_);
917
+ this.aes128Key_,
918
+ /* allPartialSegments= */ range.partialSegments > 0);
856
919
  this.references[correctedPosition] = ref;
857
920
  }
858
921
 
@@ -866,12 +929,12 @@ shaka.dash.TimelineSegmentIndex = class extends shaka.media.SegmentIndex {
866
929
  * @private
867
930
  */
868
931
  static createUris_(mediaTemplate, repId, segmentReplacement,
869
- bandwidth, timeReplacement, baseUris) {
932
+ bandwidth, timeReplacement, subNumber, getBaseUris) {
870
933
  const mediaUri = shaka.dash.MpdUtils.fillUriTemplate(
871
934
  mediaTemplate, repId,
872
- segmentReplacement, bandwidth || null, timeReplacement);
935
+ segmentReplacement, subNumber, bandwidth || null, timeReplacement);
873
936
  return shaka.util.ManifestParserUtils
874
- .resolveUris(baseUris, [mediaUri])
937
+ .resolveUris(getBaseUris(), [mediaUri])
875
938
  .map((g) => {
876
939
  return g.toString();
877
940
  });
@@ -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