shaka-player 3.2.2 → 3.3.2

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 (188) hide show
  1. package/.github/ISSUE_TEMPLATE/bug_report.md +1 -1
  2. package/.github/ISSUE_TEMPLATE/feature_request.md +1 -1
  3. package/.github/ISSUE_TEMPLATE/question.md +1 -1
  4. package/.github/workflows/build_and_test.yaml +73 -0
  5. package/.github/workflows/release.yaml +53 -0
  6. package/AUTHORS +6 -5
  7. package/CHANGELOG.md +314 -1
  8. package/CONTRIBUTORS +4 -2
  9. package/build/checkversion.py +1 -1
  10. package/build/conformance.textproto +183 -169
  11. package/build/test.py +6 -0
  12. package/build/types/core +3 -0
  13. package/build/types/offline +1 -0
  14. package/build/types/polyfill +1 -0
  15. package/build/types/ui +2 -0
  16. package/demo/common/assets.js +18 -0
  17. package/demo/common/message_ids.js +13 -1
  18. package/demo/config.js +31 -1
  19. package/demo/index.html +1 -1
  20. package/demo/locales/en.json +12 -0
  21. package/demo/locales/source.json +48 -0
  22. package/dist/controls.css +26 -2
  23. package/dist/controls.css.map +1 -1
  24. package/dist/demo.compiled.debug.js +19 -16
  25. package/dist/demo.compiled.debug.map +3 -3
  26. package/dist/demo.compiled.js +19 -16
  27. package/dist/demo.compiled.map +3 -3
  28. package/dist/deps.js +21 -14
  29. package/dist/locales.js +31 -29
  30. package/dist/receiver.compiled.debug.js +11 -9
  31. package/dist/receiver.compiled.debug.map +3 -3
  32. package/dist/receiver.compiled.js +11 -9
  33. package/dist/receiver.compiled.map +3 -3
  34. package/dist/shaka-player.compiled.d.ts +232 -18
  35. package/dist/shaka-player.compiled.debug.d.ts +232 -18
  36. package/dist/shaka-player.compiled.debug.externs.js +293 -17
  37. package/dist/shaka-player.compiled.debug.js +481 -422
  38. package/dist/shaka-player.compiled.debug.map +4 -4
  39. package/dist/shaka-player.compiled.externs.js +293 -17
  40. package/dist/shaka-player.compiled.js +846 -817
  41. package/dist/shaka-player.compiled.map +4 -4
  42. package/dist/shaka-player.ui.d.ts +250 -30
  43. package/dist/shaka-player.ui.debug.d.ts +250 -30
  44. package/dist/shaka-player.ui.debug.externs.js +322 -28
  45. package/dist/shaka-player.ui.debug.js +630 -553
  46. package/dist/shaka-player.ui.debug.map +4 -4
  47. package/dist/shaka-player.ui.externs.js +322 -28
  48. package/dist/shaka-player.ui.js +1048 -1006
  49. package/dist/shaka-player.ui.map +4 -4
  50. package/docs/design/bg-fetch-after.gv +29 -0
  51. package/docs/design/bg-fetch-after.gv.png +0 -0
  52. package/docs/design/bg-fetch-before.gv +18 -0
  53. package/docs/design/bg-fetch-before.gv.png +0 -0
  54. package/docs/design/bg-fetch.md +134 -0
  55. package/docs/tutorials/blob-url.md +15 -0
  56. package/docs/tutorials/index.json +1 -0
  57. package/docs/tutorials/ui-customization.md +66 -0
  58. package/docs/tutorials/ui.md +5 -5
  59. package/externs/cmcd.js +186 -0
  60. package/externs/ima.js +3 -0
  61. package/externs/shaka/ads.js +5 -0
  62. package/externs/shaka/manifest.js +12 -1
  63. package/externs/shaka/manifest_parser.js +10 -1
  64. package/externs/shaka/net.js +20 -4
  65. package/externs/shaka/offline.js +31 -2
  66. package/externs/shaka/player.js +138 -5
  67. package/externs/shaka/text.js +24 -0
  68. package/externs/webcrypto.js +18 -0
  69. package/karma.conf.js +19 -15
  70. package/lib/abr/ewma.js +14 -0
  71. package/lib/abr/ewma_bandwidth_estimator.js +16 -2
  72. package/lib/abr/simple_abr_manager.js +6 -0
  73. package/lib/ads/ad_manager.js +1 -0
  74. package/lib/ads/client_side_ad.js +8 -0
  75. package/lib/ads/client_side_ad_manager.js +8 -4
  76. package/lib/ads/server_side_ad.js +7 -0
  77. package/lib/cast/cast_proxy.js +3 -0
  78. package/lib/cast/cast_receiver.js +3 -0
  79. package/lib/cast/cast_utils.js +1 -0
  80. package/lib/dash/dash_parser.js +79 -33
  81. package/lib/dash/segment_base.js +25 -1
  82. package/lib/dash/segment_list.js +18 -9
  83. package/lib/dash/segment_template.js +23 -10
  84. package/lib/hls/hls_parser.js +116 -23
  85. package/lib/hls/manifest_text_parser.js +1 -0
  86. package/lib/media/quality_observer.js +274 -0
  87. package/lib/media/region_observer.js +39 -33
  88. package/lib/media/region_timeline.js +17 -19
  89. package/lib/media/segment_index.js +8 -1
  90. package/lib/media/segment_reference.js +37 -4
  91. package/lib/media/streaming_engine.js +47 -6
  92. package/lib/net/http_fetch_plugin.js +31 -11
  93. package/lib/net/http_plugin_utils.js +1 -0
  94. package/lib/net/http_xhr_plugin.js +40 -14
  95. package/lib/net/networking_engine.js +85 -18
  96. package/lib/offline/download_info.js +68 -0
  97. package/lib/offline/download_manager.js +13 -5
  98. package/lib/offline/indexeddb/base_storage_cell.js +35 -0
  99. package/lib/offline/indexeddb/storage_mechanism.js +1 -1
  100. package/lib/offline/indexeddb/v5_storage_cell.js +5 -0
  101. package/lib/offline/storage.js +401 -293
  102. package/lib/offline/stored_content_utils.js +2 -0
  103. package/lib/player.js +254 -27
  104. package/lib/polyfill/media_capabilities.js +30 -15
  105. package/lib/polyfill/random_uuid.js +52 -0
  106. package/lib/text/cue.js +18 -0
  107. package/lib/text/ttml_text_parser.js +109 -60
  108. package/lib/text/ui_text_displayer.js +19 -13
  109. package/lib/util/cmcd_manager.js +672 -0
  110. package/lib/util/error.js +6 -0
  111. package/lib/util/fake_event_target.js +21 -1
  112. package/lib/util/language_utils.js +12 -0
  113. package/lib/util/mutex.js +52 -0
  114. package/lib/util/periods.js +98 -64
  115. package/lib/util/player_configuration.js +18 -0
  116. package/lib/util/stream_utils.js +86 -10
  117. package/lib/util/xml_utils.js +19 -0
  118. package/package.json +3 -6
  119. package/shaka-player.uncompiled.js +3 -0
  120. package/test/dash/dash_parser_content_protection_unit.js +2 -0
  121. package/test/dash/dash_parser_live_unit.js +144 -1
  122. package/test/dash/dash_parser_manifest_unit.js +37 -2
  123. package/test/dash/dash_parser_segment_base_unit.js +2 -0
  124. package/test/dash/dash_parser_segment_list_unit.js +2 -1
  125. package/test/dash/dash_parser_segment_template_unit.js +2 -1
  126. package/test/hls/hls_live_unit.js +2 -0
  127. package/test/hls/hls_parser_unit.js +76 -0
  128. package/test/media/drm_engine_unit.js +3 -3
  129. package/test/media/quality_observer_unit.js +142 -0
  130. package/test/media/region_observer_unit.js +40 -5
  131. package/test/media/region_timeline_unit.js +40 -22
  132. package/test/media/segment_reference_unit.js +16 -1
  133. package/test/media/streaming_engine_integration.js +4 -2
  134. package/test/media/streaming_engine_unit.js +147 -0
  135. package/test/net/http_plugin_unit.js +20 -15
  136. package/test/offline/storage_integration.js +108 -4
  137. package/test/player_integration.js +140 -8
  138. package/test/player_src_equals_integration.js +41 -8
  139. package/test/player_unit.js +19 -9
  140. package/test/test/assets/chapters.vtt +2 -2
  141. package/test/test/assets/chapters2.vtt +10 -0
  142. package/test/test/util/dash_parser_util.js +4 -0
  143. package/test/test/util/fake_ad.js +7 -0
  144. package/test/test/util/fake_drm_engine.js +9 -2
  145. package/test/test/util/manifest_generator.js +3 -2
  146. package/test/test/util/test_scheme.js +14 -1
  147. package/test/test/util/ttml_utils.js +81 -0
  148. package/test/test/util/util.js +10 -7
  149. package/test/text/mp4_ttml_parser_unit.js +11 -18
  150. package/test/text/ttml_text_parser_unit.js +544 -228
  151. package/test/ui/text_displayer_layout_unit.js +8 -7
  152. package/test/ui/ui_unit.js +250 -0
  153. package/test/util/cmcd_manager_unit.js +291 -0
  154. package/test/util/stream_utils_unit.js +36 -30
  155. package/third_party/closure-uri/uri.js +21 -0
  156. package/ui/airplay_button.js +1 -0
  157. package/ui/audio_language_selection.js +7 -0
  158. package/ui/cast_button.js +1 -0
  159. package/ui/context_menu.js +109 -0
  160. package/ui/controls.js +44 -7
  161. package/ui/controls.less +1 -0
  162. package/ui/enums.js +4 -0
  163. package/ui/externs/ui.js +28 -1
  164. package/ui/fast_forward_button.js +14 -5
  165. package/ui/fullscreen_button.js +1 -0
  166. package/ui/less/buttons.less +0 -4
  167. package/ui/less/containers.less +74 -0
  168. package/ui/less/overflow_menu.less +0 -10
  169. package/ui/less/tooltip.less +97 -0
  170. package/ui/locales/en.json +1 -0
  171. package/ui/locales/source.json +4 -0
  172. package/ui/localization.js +12 -36
  173. package/ui/loop_button.js +5 -0
  174. package/ui/mute_button.js +2 -1
  175. package/ui/overflow_menu.js +5 -25
  176. package/ui/pip_button.js +5 -0
  177. package/ui/play_button.js +2 -2
  178. package/ui/playback_rate_selection.js +8 -9
  179. package/ui/resolution_selection.js +7 -0
  180. package/ui/rewind_button.js +15 -6
  181. package/ui/seek_bar.js +4 -2
  182. package/ui/settings_menu.js +36 -4
  183. package/ui/small_play_button.js +1 -0
  184. package/ui/statistics_button.js +253 -0
  185. package/ui/text_selection.js +12 -4
  186. package/ui/ui.js +30 -0
  187. package/ui/volume_bar.js +1 -1
  188. package/externs/media_capabilities.js +0 -16
@@ -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.CmcdManager');
22
23
  goog.require('shaka.util.Error');
23
24
  goog.require('shaka.util.Functional');
24
25
  goog.require('shaka.util.Iterables');
@@ -58,12 +59,12 @@ shaka.dash.DashParser = class {
58
59
  this.globalId_ = 1;
59
60
 
60
61
  /**
61
- * A map of IDs to SegmentIndex objects.
62
+ * A map of IDs to Stream objects.
62
63
  * ID: Period@id,AdaptationSet@id,@Representation@id
63
64
  * e.g.: '1,5,23'
64
- * @private {!Object.<string, !shaka.media.SegmentIndex>}
65
+ * @private {!Object.<string, !shaka.extern.Stream>}
65
66
  */
66
- this.segmentIndexMap_ = {};
67
+ this.streamMap_ = {};
67
68
 
68
69
  /**
69
70
  * A map of period ids to their durations
@@ -164,8 +165,10 @@ shaka.dash.DashParser = class {
164
165
  stop() {
165
166
  // When the parser stops, release all segment indexes, which stops their
166
167
  // timers, as well.
167
- for (const segmentIndex of Object.values(this.segmentIndexMap_)) {
168
- segmentIndex.release();
168
+ for (const stream of Object.values(this.streamMap_)) {
169
+ if (stream.segmentIndex) {
170
+ stream.segmentIndex.release();
171
+ }
169
172
  }
170
173
 
171
174
  if (this.periodCombiner_) {
@@ -176,7 +179,7 @@ shaka.dash.DashParser = class {
176
179
  this.config_ = null;
177
180
  this.manifestUris_ = [];
178
181
  this.manifest_ = null;
179
- this.segmentIndexMap_ = {};
182
+ this.streamMap_ = {};
180
183
  this.periodCombiner_ = null;
181
184
 
182
185
  if (this.updateTimer_ != null) {
@@ -224,6 +227,9 @@ shaka.dash.DashParser = class {
224
227
  this.manifestUris_, this.config_.retryParameters);
225
228
  const networkingEngine = this.playerInterface_.networkingEngine;
226
229
 
230
+ const format = shaka.util.CmcdManager.StreamingFormat.DASH;
231
+ this.playerInterface_.modifyManifestRequest(request, {format: format});
232
+
227
233
  const startTime = Date.now();
228
234
  const operation = networkingEngine.request(requestType, request);
229
235
  this.operationManager_.manage(operation);
@@ -378,8 +384,11 @@ shaka.dash.DashParser = class {
378
384
  // that the complex logic of multi-Period eviction and period-flattening
379
385
  // is correct. See also:
380
386
  // https://github.com/google/shaka-player/issues/3169#issuecomment-823580634
381
- for (const segmentIndex of Object.values(this.segmentIndexMap_)) {
382
- segmentIndex.evict(presentationTimeline.getSegmentAvailabilityStart());
387
+ for (const stream of Object.values(this.streamMap_)) {
388
+ if (stream.segmentIndex) {
389
+ stream.segmentIndex.evict(
390
+ presentationTimeline.getSegmentAvailabilityStart());
391
+ }
383
392
  }
384
393
  } else {
385
394
  // DASH IOP v3.0 suggests using a default delay between minBufferTime
@@ -401,6 +410,24 @@ shaka.dash.DashParser = class {
401
410
  this.config_.dash.autoCorrectDrift);
402
411
  }
403
412
 
413
+ presentationTimeline.setStatic(mpdType == 'static');
414
+
415
+ const isLive = presentationTimeline.isLive();
416
+
417
+ // If it's live, we check for an override.
418
+ if (isLive && !isNaN(this.config_.availabilityWindowOverride)) {
419
+ segmentAvailabilityDuration = this.config_.availabilityWindowOverride;
420
+ }
421
+
422
+ // If it's null, that means segments are always available. This is always
423
+ // the case for VOD, and sometimes the case for live.
424
+ if (segmentAvailabilityDuration == null) {
425
+ segmentAvailabilityDuration = Infinity;
426
+ }
427
+
428
+ presentationTimeline.setSegmentAvailabilityDuration(
429
+ segmentAvailabilityDuration);
430
+
404
431
  const profiles = mpd.getAttribute('profiles') || '';
405
432
 
406
433
  /** @type {shaka.dash.DashParser.Context} */
@@ -423,7 +450,6 @@ shaka.dash.DashParser = class {
423
450
  const duration = periodsAndDuration.duration;
424
451
  const periods = periodsAndDuration.periods;
425
452
 
426
- presentationTimeline.setStatic(mpdType == 'static');
427
453
  if (mpdType == 'static' ||
428
454
  !periodsAndDuration.durationDerivedFromPeriods) {
429
455
  // Ignore duration calculated from Period lengths if this is dynamic.
@@ -442,6 +468,7 @@ shaka.dash.DashParser = class {
442
468
  this.lowLatencyMode_ = this.playerInterface_.isLowLatencyMode();
443
469
  }
444
470
  }
471
+
445
472
  if (this.lowLatencyMode_) {
446
473
  presentationTimeline.setAvailabilityTimeOffset(
447
474
  this.minTotalAvailabilityTimeOffset_);
@@ -455,22 +482,6 @@ shaka.dash.DashParser = class {
455
482
  'https://bit.ly/3clctcj for details.');
456
483
  }
457
484
 
458
- const isLive = presentationTimeline.isLive();
459
-
460
- // If it's live, we check for an override.
461
- if (isLive && !isNaN(this.config_.availabilityWindowOverride)) {
462
- segmentAvailabilityDuration = this.config_.availabilityWindowOverride;
463
- }
464
-
465
- // If it's null, that means segments are always available. This is always
466
- // the case for VOD, and sometimes the case for live.
467
- if (segmentAvailabilityDuration == null) {
468
- segmentAvailabilityDuration = Infinity;
469
- }
470
-
471
- presentationTimeline.setSegmentAvailabilityDuration(
472
- segmentAvailabilityDuration);
473
-
474
485
  // Use @maxSegmentDuration to override smaller, derived values.
475
486
  presentationTimeline.notifyMaxSegmentDuration(maxSegmentDuration || 1);
476
487
  if (goog.DEBUG) {
@@ -705,8 +716,12 @@ shaka.dash.DashParser = class {
705
716
 
706
717
  const eventStreamNodes =
707
718
  XmlUtils.findChildren(periodInfo.node, 'EventStream');
719
+ const availabilityStart =
720
+ context.presentationTimeline.getSegmentAvailabilityStart();
721
+
708
722
  for (const node of eventStreamNodes) {
709
- this.parseEventStream_(periodInfo.start, periodInfo.duration, node);
723
+ this.parseEventStream_(
724
+ periodInfo.start, periodInfo.duration, node, availabilityStart);
710
725
  }
711
726
 
712
727
  const adaptationSetNodes =
@@ -1163,11 +1178,12 @@ shaka.dash.DashParser = class {
1163
1178
  context, requestInitSegment);
1164
1179
  } else if (context.representation.segmentList) {
1165
1180
  streamInfo = shaka.dash.SegmentList.createStreamInfo(
1166
- context, this.segmentIndexMap_);
1181
+ context, this.streamMap_);
1167
1182
  } else if (context.representation.segmentTemplate) {
1168
1183
  const hasManifest = !!this.manifest_;
1184
+
1169
1185
  streamInfo = shaka.dash.SegmentTemplate.createStreamInfo(
1170
- context, requestInitSegment, this.segmentIndexMap_, hasManifest,
1186
+ context, requestInitSegment, this.streamMap_, hasManifest,
1171
1187
  this.config_.dash.initialSegmentLimit, this.periodDurations_);
1172
1188
  } else {
1173
1189
  goog.asserts.assert(isText,
@@ -1220,8 +1236,10 @@ shaka.dash.DashParser = class {
1220
1236
 
1221
1237
  let forced = false;
1222
1238
  if (isText) {
1223
- // See: https://github.com/google/shaka-player/issues/2122
1224
- forced = roles.includes('forced_subtitle');
1239
+ // See: https://github.com/google/shaka-player/issues/2122 and
1240
+ // https://github.com/Dash-Industry-Forum/DASH-IF-IOP/issues/165
1241
+ forced = roles.includes('forced_subtitle') ||
1242
+ roles.includes('forced-subtitle');
1225
1243
  }
1226
1244
 
1227
1245
  let tilesLayout;
@@ -1254,13 +1272,29 @@ shaka.dash.DashParser = class {
1254
1272
  hdr = 'PQ';
1255
1273
  }
1256
1274
 
1275
+ const contextId = context.representation.id ?
1276
+ context.period.id + ',' + context.representation.id : '';
1277
+
1257
1278
  /** @type {shaka.extern.Stream} */
1258
1279
  const stream = {
1259
1280
  id: this.globalId_++,
1260
1281
  originalId: context.representation.id,
1261
1282
  createSegmentIndex: async () => {
1262
- if (!stream.segmentIndex) {
1263
- stream.segmentIndex = await streamInfo.generateSegmentIndex();
1283
+ // If we have a stream with the same context id stored in the map
1284
+ // that has no segmentIndex, we should set the segmentIndex for it.
1285
+ const storedInMap = contextId && context.dynamic &&
1286
+ this.streamMap_[contextId];
1287
+
1288
+ const currentStream = storedInMap ? this.streamMap_[contextId] : stream;
1289
+ if (!currentStream.segmentIndex) {
1290
+ currentStream.segmentIndex = await streamInfo.generateSegmentIndex();
1291
+ }
1292
+ },
1293
+
1294
+ closeSegmentIndex: () => {
1295
+ if (stream.segmentIndex) {
1296
+ stream.segmentIndex.release();
1297
+ stream.segmentIndex = null;
1264
1298
  }
1265
1299
  },
1266
1300
  segmentIndex: null,
@@ -1290,7 +1324,13 @@ shaka.dash.DashParser = class {
1290
1324
  closedCaptions,
1291
1325
  hdr,
1292
1326
  tilesLayout,
1327
+ matchedStreams: [],
1293
1328
  };
1329
+
1330
+ if (contextId && context.dynamic && !this.streamMap_[contextId]) {
1331
+ this.streamMap_[contextId] = stream;
1332
+ }
1333
+
1294
1334
  return stream;
1295
1335
  }
1296
1336
 
@@ -1735,9 +1775,10 @@ shaka.dash.DashParser = class {
1735
1775
  * @param {number} periodStart
1736
1776
  * @param {?number} periodDuration
1737
1777
  * @param {!Element} elem
1778
+ * @param {number} availabilityStart
1738
1779
  * @private
1739
1780
  */
1740
- parseEventStream_(periodStart, periodDuration, elem) {
1781
+ parseEventStream_(periodStart, periodDuration, elem, availabilityStart) {
1741
1782
  const XmlUtils = shaka.util.XmlUtils;
1742
1783
  const parseNumber = XmlUtils.parseNonNegativeInt;
1743
1784
 
@@ -1760,6 +1801,11 @@ shaka.dash.DashParser = class {
1760
1801
  endTime = Math.min(endTime, periodStart + periodDuration);
1761
1802
  }
1762
1803
 
1804
+ // Don't add unavailable regions to the timeline.
1805
+ if (endTime < availabilityStart) {
1806
+ continue;
1807
+ }
1808
+
1763
1809
  /** @type {shaka.extern.TimelineRegionInfo} */
1764
1810
  const region = {
1765
1811
  schemeIdUri: schemeIdUri,
@@ -61,7 +61,9 @@ shaka.dash.SegmentBase = class {
61
61
  }
62
62
 
63
63
  const getUris = () => resolvedUris;
64
- return new shaka.media.InitSegmentReference(getUris, startByte, endByte);
64
+ const qualityInfo = shaka.dash.SegmentBase.createQualityInfo(context);
65
+ return new shaka.media.InitSegmentReference(
66
+ getUris, startByte, endByte, qualityInfo);
65
67
  }
66
68
 
67
69
  /**
@@ -346,4 +348,26 @@ shaka.dash.SegmentBase = class {
346
348
  indexRange.start, indexRange.end,
347
349
  scaledPresentationTimeOffset);
348
350
  }
351
+
352
+ /**
353
+ * Create a MediaQualityInfo object from a Context object.
354
+ *
355
+ * @param {!shaka.dash.DashParser.Context} context
356
+ * @return {!shaka.extern.MediaQualityInfo}
357
+ */
358
+ static createQualityInfo(context) {
359
+ const representation = context.representation;
360
+ return {
361
+ bandwidth: context.bandwidth,
362
+ audioSamplingRate: representation.audioSamplingRate,
363
+ codecs: representation.codecs,
364
+ contentType: representation.contentType,
365
+ frameRate: representation.frameRate || null,
366
+ height: representation.height || null,
367
+ mimeType: representation.mimeType,
368
+ channelsCount: representation.numChannels,
369
+ pixelAspectRatio: representation.pixelAspectRatio || null,
370
+ width: representation.width || null,
371
+ };
372
+ }
349
373
  };
@@ -30,10 +30,10 @@ shaka.dash.SegmentList = class {
30
30
  * Updates the existing SegmentIndex, if any.
31
31
  *
32
32
  * @param {shaka.dash.DashParser.Context} context
33
- * @param {!Object.<string, !shaka.media.SegmentIndex>} segmentIndexMap
33
+ * @param {!Object.<string, !shaka.extern.Stream>} streamMap
34
34
  * @return {shaka.dash.DashParser.StreamInfo}
35
35
  */
36
- static createStreamInfo(context, segmentIndexMap) {
36
+ static createStreamInfo(context, streamMap) {
37
37
  goog.asserts.assert(context.representation.segmentList,
38
38
  'Should only be called with SegmentList');
39
39
  const SegmentList = shaka.dash.SegmentList;
@@ -46,11 +46,14 @@ shaka.dash.SegmentList = class {
46
46
 
47
47
  /** @type {shaka.media.SegmentIndex} */
48
48
  let segmentIndex = null;
49
- let id = null;
49
+ let stream = null;
50
50
  if (context.period.id && context.representation.id) {
51
51
  // Only check/store the index if period and representation IDs are set.
52
- id = context.period.id + ',' + context.representation.id;
53
- segmentIndex = segmentIndexMap[id];
52
+ const id = context.period.id + ',' + context.representation.id;
53
+ stream = streamMap[id];
54
+ if (stream) {
55
+ segmentIndex = stream.segmentIndex;
56
+ }
54
57
  }
55
58
 
56
59
  const references = SegmentList.createSegmentReferences_(
@@ -64,9 +67,6 @@ shaka.dash.SegmentList = class {
64
67
  segmentIndex.mergeAndEvict(references, start);
65
68
  } else {
66
69
  segmentIndex = new shaka.media.SegmentIndex(references);
67
- if (id && context.dynamic) {
68
- segmentIndexMap[id] = segmentIndex;
69
- }
70
70
  }
71
71
  context.presentationTimeline.notifySegments(references);
72
72
 
@@ -77,8 +77,17 @@ shaka.dash.SegmentList = class {
77
77
  segmentIndex.fit(periodStart, periodEnd, isNew);
78
78
  }
79
79
 
80
+ if (stream) {
81
+ stream.segmentIndex = segmentIndex;
82
+ }
83
+
80
84
  return {
81
- generateSegmentIndex: () => Promise.resolve(segmentIndex),
85
+ generateSegmentIndex: () => {
86
+ if (!segmentIndex || segmentIndex.isEmpty()) {
87
+ segmentIndex.merge(references);
88
+ }
89
+ return Promise.resolve(segmentIndex);
90
+ },
82
91
  };
83
92
  }
84
93
 
@@ -31,7 +31,7 @@ shaka.dash.SegmentTemplate = class {
31
31
  * @param {shaka.dash.DashParser.Context} context
32
32
  * @param {shaka.dash.DashParser.RequestInitSegmentCallback}
33
33
  * requestInitSegment
34
- * @param {!Object.<string, !shaka.media.SegmentIndex>} segmentIndexMap
34
+ * @param {!Object.<string, !shaka.extern.Stream>} streamMap
35
35
  * @param {boolean} isUpdate True if the manifest is being updated.
36
36
  * @param {number} segmentLimit The maximum number of segments to generate for
37
37
  * a SegmentTemplate with fixed duration.
@@ -39,8 +39,8 @@ shaka.dash.SegmentTemplate = class {
39
39
  * @return {shaka.dash.DashParser.StreamInfo}
40
40
  */
41
41
  static createStreamInfo(
42
- context, requestInitSegment, segmentIndexMap, isUpdate,
43
- segmentLimit, periodDurationMap) {
42
+ context, requestInitSegment, streamMap, isUpdate, segmentLimit,
43
+ periodDurationMap) {
44
44
  goog.asserts.assert(context.representation.segmentTemplate,
45
45
  'Should only be called with SegmentTemplate');
46
46
  const SegmentTemplate = shaka.dash.SegmentTemplate;
@@ -86,10 +86,14 @@ shaka.dash.SegmentTemplate = class {
86
86
  /** @type {shaka.media.SegmentIndex} */
87
87
  let segmentIndex = null;
88
88
  let id = null;
89
+ let stream = null;
89
90
  if (context.period.id && context.representation.id) {
90
91
  // Only check/store the index if period and representation IDs are set.
91
92
  id = context.period.id + ',' + context.representation.id;
92
- segmentIndex = segmentIndexMap[id];
93
+ stream = streamMap[id];
94
+ if (stream) {
95
+ segmentIndex = stream.segmentIndex;
96
+ }
93
97
  }
94
98
 
95
99
  const references = SegmentTemplate.createFromTimeline_(
@@ -117,9 +121,6 @@ shaka.dash.SegmentTemplate = class {
117
121
  context.presentationTimeline.getSegmentAvailabilityStart());
118
122
  } else {
119
123
  segmentIndex = new shaka.media.SegmentIndex(references);
120
- if (id && context.dynamic) {
121
- segmentIndexMap[id] = segmentIndex;
122
- }
123
124
  }
124
125
  context.presentationTimeline.notifySegments(references);
125
126
 
@@ -127,8 +128,20 @@ shaka.dash.SegmentTemplate = class {
127
128
  segmentIndex.fit(periodStart, periodEnd);
128
129
  }
129
130
 
131
+ if (stream && context.dynamic) {
132
+ stream.segmentIndex = segmentIndex;
133
+ }
134
+
130
135
  return {
131
- generateSegmentIndex: () => Promise.resolve(segmentIndex),
136
+ generateSegmentIndex: () => {
137
+ // If segmentIndex is deleted, or segmentIndex's references are
138
+ // released by closeSegmentIndex(), we should set the value of
139
+ // segmentIndex again.
140
+ if (!segmentIndex || segmentIndex.isEmpty()) {
141
+ segmentIndex.merge(references);
142
+ }
143
+ return Promise.resolve(segmentIndex);
144
+ },
132
145
  };
133
146
  }
134
147
  }
@@ -569,8 +582,8 @@ shaka.dash.SegmentTemplate = class {
569
582
  baseUris, [filledTemplate]);
570
583
  return resolvedUris;
571
584
  };
572
-
573
- return new shaka.media.InitSegmentReference(getUris, 0, null);
585
+ const qualityInfo = shaka.dash.SegmentBase.createQualityInfo(context);
586
+ return new shaka.media.InitSegmentReference(getUris, 0, null, qualityInfo);
574
587
  }
575
588
  };
576
589