shaka-player 4.7.13 → 4.8.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 (114) hide show
  1. package/AUTHORS +2 -0
  2. package/CONTRIBUTORS +2 -0
  3. package/LICENSE +54 -0
  4. package/README.md +22 -1
  5. package/dist/controls.css +1 -1
  6. package/dist/controls.css.map +1 -1
  7. package/dist/deps.js +34 -23
  8. package/dist/locales.js +41 -31
  9. package/dist/shaka-player.compiled.d.ts +196 -45
  10. package/dist/shaka-player.compiled.debug.d.ts +196 -45
  11. package/dist/shaka-player.compiled.debug.externs.js +491 -348
  12. package/dist/shaka-player.compiled.debug.js +969 -847
  13. package/dist/shaka-player.compiled.debug.map +4 -4
  14. package/dist/shaka-player.compiled.externs.js +491 -348
  15. package/dist/shaka-player.compiled.js +1238 -1171
  16. package/dist/shaka-player.compiled.map +4 -4
  17. package/dist/shaka-player.ui.d.ts +259 -48
  18. package/dist/shaka-player.ui.debug.d.ts +259 -48
  19. package/dist/shaka-player.ui.debug.externs.js +598 -377
  20. package/dist/shaka-player.ui.debug.js +1185 -980
  21. package/dist/shaka-player.ui.debug.map +4 -4
  22. package/dist/shaka-player.ui.externs.js +598 -377
  23. package/dist/shaka-player.ui.js +1526 -1408
  24. package/dist/shaka-player.ui.map +4 -4
  25. package/externs/device_sensor_event.js +18 -0
  26. package/externs/hisense.js +16 -0
  27. package/externs/playstation.js +31 -0
  28. package/externs/shaka/abr_manager.js +3 -1
  29. package/externs/shaka/ads.js +5 -0
  30. package/externs/shaka/codecs.js +13 -0
  31. package/externs/shaka/manifest.js +58 -6
  32. package/externs/shaka/net.js +13 -1
  33. package/externs/shaka/player.js +184 -13
  34. package/externs/shaka/text.js +19 -0
  35. package/externs/tizen.js +41 -0
  36. package/externs/webos.js +19 -0
  37. package/externs/xbox.js +43 -0
  38. package/lib/abr/simple_abr_manager.js +9 -1
  39. package/lib/ads/ad_manager.js +19 -2
  40. package/lib/ads/client_side_ad_manager.js +5 -3
  41. package/lib/ads/media_tailor_ad_manager.js +9 -0
  42. package/lib/cast/cast_receiver.js +0 -14
  43. package/lib/cast/cast_utils.js +1 -0
  44. package/lib/cea/mp4_cea_parser.js +6 -1
  45. package/lib/dash/content_protection.js +97 -53
  46. package/lib/dash/dash_parser.js +237 -177
  47. package/lib/dash/mpd_utils.js +72 -65
  48. package/lib/dash/segment_base.js +27 -21
  49. package/lib/dash/segment_list.js +18 -16
  50. package/lib/dash/segment_template.js +45 -22
  51. package/lib/hls/hls_parser.js +358 -164
  52. package/lib/media/buffering_observer.js +25 -1
  53. package/lib/media/drm_engine.js +91 -71
  54. package/lib/media/manifest_filterer.js +220 -0
  55. package/lib/media/media_source_engine.js +34 -5
  56. package/lib/media/preload_manager.js +827 -0
  57. package/lib/media/presentation_timeline.js +11 -0
  58. package/lib/media/segment_index.js +93 -47
  59. package/lib/media/segment_prefetch.js +140 -43
  60. package/lib/media/segment_reference.js +43 -8
  61. package/lib/media/segment_utils.js +208 -25
  62. package/lib/media/streaming_engine.js +355 -66
  63. package/lib/mss/content_protection.js +31 -25
  64. package/lib/mss/mss_parser.js +79 -55
  65. package/lib/net/networking_engine.js +49 -4
  66. package/lib/offline/storage.js +3 -3
  67. package/lib/player.js +882 -672
  68. package/lib/polyfill/media_capabilities.js +16 -1
  69. package/lib/text/simple_text_displayer.js +9 -0
  70. package/lib/text/stub_text_displayer.js +7 -0
  71. package/lib/text/text_engine.js +16 -3
  72. package/lib/text/ttml_text_parser.js +116 -114
  73. package/lib/text/ui_text_displayer.js +28 -11
  74. package/lib/text/vtt_text_parser.js +54 -115
  75. package/lib/transmuxer/opus.js +91 -0
  76. package/lib/transmuxer/ts_transmuxer.js +115 -0
  77. package/lib/util/cmcd_manager.js +153 -3
  78. package/lib/util/dom_utils.js +11 -0
  79. package/lib/util/error.js +5 -0
  80. package/lib/util/fake_event.js +2 -0
  81. package/lib/util/manifest_parser_utils.js +97 -3
  82. package/lib/util/mp4_box_parsers.js +346 -17
  83. package/lib/util/mp4_generator.js +23 -0
  84. package/lib/util/periods.js +19 -0
  85. package/lib/util/platform.js +161 -9
  86. package/lib/util/player_configuration.js +79 -13
  87. package/lib/util/stats.js +16 -0
  88. package/lib/util/stream_utils.js +22 -8
  89. package/lib/util/string_utils.js +36 -0
  90. package/lib/util/tXml.js +814 -0
  91. package/lib/util/ts_parser.js +50 -0
  92. package/lib/util/xml_utils.js +10 -338
  93. package/package.json +2 -2
  94. package/shaka-player.uncompiled.js +8 -0
  95. package/ui/controls.js +187 -5
  96. package/ui/enums.js +2 -0
  97. package/ui/externs/ui.js +30 -3
  98. package/ui/gl_matrix/matrix_4x4.js +568 -0
  99. package/ui/gl_matrix/matrix_quaternion.js +45 -0
  100. package/ui/less/containers.less +11 -0
  101. package/ui/locales/en-GB.json +2 -0
  102. package/ui/locales/en.json +2 -0
  103. package/ui/locales/es-419.json +2 -0
  104. package/ui/locales/es.json +2 -0
  105. package/ui/locales/source.json +8 -0
  106. package/ui/recenter_vr.js +126 -0
  107. package/ui/resolution_selection.js +25 -5
  108. package/ui/statistics_button.js +12 -0
  109. package/ui/text_selection.js +1 -1
  110. package/ui/toggle_stereoscopic.js +127 -0
  111. package/ui/ui.js +74 -38
  112. package/ui/vr_manager.js +557 -0
  113. package/ui/vr_utils.js +232 -0
  114. package/ui/vr_webgl.js +697 -0
@@ -198,6 +198,17 @@ shaka.media.PresentationTimeline = class {
198
198
  }
199
199
 
200
200
 
201
+ /**
202
+ * Gets the presentation's segment availability duration.
203
+ *
204
+ * @return {number}
205
+ * @export
206
+ */
207
+ getSegmentAvailabilityDuration() {
208
+ return this.segmentAvailabilityDuration_;
209
+ }
210
+
211
+
201
212
  /**
202
213
  * Sets the presentation delay in seconds.
203
214
  *
@@ -18,6 +18,7 @@ goog.require('shaka.util.Timer');
18
18
  /**
19
19
  * SegmentIndex.
20
20
  *
21
+ * @implements {shaka.extern.SegmentIndex}
21
22
  * @implements {shaka.util.IReleasable}
22
23
  * @implements {Iterable.<!shaka.media.SegmentReference>}
23
24
  * @export
@@ -61,14 +62,21 @@ shaka.media.SegmentIndex = class {
61
62
  }
62
63
 
63
64
  /**
64
- * Get number of references
65
- * @protected
66
- * @return {number}
65
+ * @override
66
+ * @export
67
67
  */
68
68
  getNumReferences() {
69
69
  return this.references.length;
70
70
  }
71
71
 
72
+ /**
73
+ * @override
74
+ * @export
75
+ */
76
+ getNumEvicted() {
77
+ return this.numEvicted_;
78
+ }
79
+
72
80
  /**
73
81
  * @override
74
82
  * @export
@@ -132,14 +140,7 @@ shaka.media.SegmentIndex = class {
132
140
 
133
141
 
134
142
  /**
135
- * Finds the position of the segment for the given time, in seconds, relative
136
- * to the start of the presentation. Returns the position of the segment
137
- * with the largest end time if more than one segment is known for the given
138
- * time.
139
- *
140
- * @param {number} time
141
- * @return {?number} The position of the segment, or null if the position of
142
- * the segment could not be determined.
143
+ * @override
143
144
  * @export
144
145
  */
145
146
  find(time) {
@@ -170,11 +171,7 @@ shaka.media.SegmentIndex = class {
170
171
 
171
172
 
172
173
  /**
173
- * Gets the SegmentReference for the segment at the given position.
174
- *
175
- * @param {number} position The position of the segment as returned by find().
176
- * @return {shaka.media.SegmentReference} The SegmentReference, or null if
177
- * no such SegmentReference exists.
174
+ * @override
178
175
  * @export
179
176
  */
180
177
  get(position) {
@@ -378,7 +375,7 @@ shaka.media.SegmentIndex = class {
378
375
  lastReference.tileDuration,
379
376
  lastReference.syncTime,
380
377
  lastReference.status,
381
- lastReference.aes128Key,
378
+ lastReference.aesKey,
382
379
  );
383
380
  newReference.mimeType = lastReference.mimeType;
384
381
  newReference.codecs = lastReference.codecs;
@@ -438,10 +435,12 @@ shaka.media.SegmentIndex = class {
438
435
  * Playlist will contain at least one independent frame."
439
436
  *
440
437
  * @param {number} time
438
+ * @param {boolean=} allowNonIndepedent
439
+ * @param {boolean=} reverse
441
440
  * @return {?shaka.media.SegmentIterator}
442
441
  * @export
443
442
  */
444
- getIteratorForTime(time) {
443
+ getIteratorForTime(time, allowNonIndepedent = false, reverse = false) {
445
444
  let index = this.find(time);
446
445
  if (index == null) {
447
446
  return null;
@@ -459,14 +458,16 @@ shaka.media.SegmentIndex = class {
459
458
  let r = ref.partialReferences[i];
460
459
  // Note that a segment ends immediately before the end time.
461
460
  if ((time >= r.startTime) && (time < r.endTime)) {
462
- // Find an independent partial segment by moving backwards.
463
- while (i && !r.isIndependent()) {
464
- i--;
465
- r = ref.partialReferences[i];
466
- }
467
- if (!r.isIndependent()) {
468
- shaka.log.alwaysError('No independent partial segment found!');
469
- return null;
461
+ if (!allowNonIndepedent) {
462
+ // Find an independent partial segment by moving backwards.
463
+ while (i && (!r.isIndependent())) {
464
+ i--;
465
+ r = ref.partialReferences[i];
466
+ }
467
+ if (!r.isIndependent()) {
468
+ shaka.log.alwaysError('No independent partial segment found!');
469
+ return null;
470
+ }
470
471
  }
471
472
  // Call to next() should move the partial segment, not the full
472
473
  // segment.
@@ -476,7 +477,8 @@ shaka.media.SegmentIndex = class {
476
477
  }
477
478
  }
478
479
  }
479
- return new shaka.media.SegmentIterator(this, index, partialSegmentIndex);
480
+ return new shaka.media.SegmentIterator(
481
+ this, index, partialSegmentIndex, reverse);
480
482
  }
481
483
 
482
484
  /**
@@ -552,8 +554,9 @@ shaka.media.SegmentIterator = class {
552
554
  * @param {shaka.media.SegmentIndex} segmentIndex
553
555
  * @param {number} index
554
556
  * @param {number} partialSegmentIndex
557
+ * @param {boolean} reverse
555
558
  */
556
- constructor(segmentIndex, index, partialSegmentIndex) {
559
+ constructor(segmentIndex, index, partialSegmentIndex, reverse) {
557
560
  /** @private {shaka.media.SegmentIndex} */
558
561
  this.segmentIndex_ = segmentIndex;
559
562
 
@@ -562,6 +565,17 @@ shaka.media.SegmentIterator = class {
562
565
 
563
566
  /** @private {number} */
564
567
  this.currentPartialPosition_ = partialSegmentIndex;
568
+
569
+ /** @private {boolean} */
570
+ this.reverse = reverse;
571
+ }
572
+
573
+ /**
574
+ * @param {boolean} reverse
575
+ * @export
576
+ */
577
+ setReverse(reverse) {
578
+ this.reverse = reverse;
565
579
  }
566
580
 
567
581
  /**
@@ -606,25 +620,47 @@ shaka.media.SegmentIterator = class {
606
620
  next() {
607
621
  const ref = this.segmentIndex_.get(this.currentPosition_);
608
622
 
609
- if (ref && ref.hasPartialSegments()) {
610
- // If the regular segment contains partial segments, move to the next
611
- // partial SegmentReference.
612
- this.currentPartialPosition_++;
613
- // If the current regular segment has been published completely, and
614
- // we've reached the end of its partial segments list, move to the next
615
- // regular segment.
616
- // If the Partial Segments list is still on the fly, do not move to
617
- // the next regular segment.
618
- if (ref.hasAllPartialSegments() &&
619
- this.currentPartialPosition_ == ref.partialReferences.length) {
623
+ if (!this.reverse) {
624
+ if (ref && ref.hasPartialSegments()) {
625
+ // If the regular segment contains partial segments, move to the next
626
+ // partial SegmentReference.
627
+ this.currentPartialPosition_++;
628
+ // If the current regular segment has been published completely, and
629
+ // we've reached the end of its partial segments list, move to the next
630
+ // regular segment.
631
+ // If the Partial Segments list is still on the fly, do not move to
632
+ // the next regular segment.
633
+ if (ref.hasAllPartialSegments() &&
634
+ this.currentPartialPosition_ == ref.partialReferences.length) {
635
+ this.currentPosition_++;
636
+ this.currentPartialPosition_ = 0;
637
+ }
638
+ } else {
639
+ // If the regular segment doesn't contain partial segments, move to the
640
+ // next regular segment.
620
641
  this.currentPosition_++;
621
642
  this.currentPartialPosition_ = 0;
622
643
  }
623
644
  } else {
624
- // If the regular segment doesn't contain partial segments, move to the
625
- // next regular segment.
626
- this.currentPosition_++;
627
- this.currentPartialPosition_ = 0;
645
+ if (ref && ref.hasPartialSegments()) {
646
+ // If the regular segment contains partial segments, move to the
647
+ // previous partial SegmentReference.
648
+ this.currentPartialPosition_--;
649
+ if (this.currentPartialPosition_ < 0) {
650
+ this.currentPosition_--;
651
+ const prevRef = this.segmentIndex_.get(this.currentPosition_);
652
+ if (prevRef && prevRef.hasPartialSegments()) {
653
+ this.currentPartialPosition_ = prevRef.partialReferences.length - 1;
654
+ } else {
655
+ this.currentPartialPosition_ = 0;
656
+ }
657
+ }
658
+ } else {
659
+ // If the regular segment doesn't contain partial segments, move to the
660
+ // previous regular segment.
661
+ this.currentPosition_--;
662
+ this.currentPartialPosition_ = 0;
663
+ }
628
664
  }
629
665
 
630
666
  const res = this.current();
@@ -664,7 +700,7 @@ shaka.media.MetaSegmentIndex = class extends shaka.media.SegmentIndex {
664
700
  */
665
701
  appendSegmentIndex(segmentIndex) {
666
702
  goog.asserts.assert(
667
- this.indexes_.length == 0 || segmentIndex.numEvicted_ == 0,
703
+ this.indexes_.length == 0 || segmentIndex.getNumEvicted() == 0,
668
704
  'Should not append a new segment index with already-evicted segments');
669
705
  this.indexes_.push(segmentIndex);
670
706
  }
@@ -694,6 +730,16 @@ shaka.media.MetaSegmentIndex = class extends shaka.media.SegmentIndex {
694
730
  this.indexes_ = [];
695
731
  }
696
732
 
733
+ /**
734
+ * @override
735
+ * @export
736
+ */
737
+ forEachTopLevelReference(fn) {
738
+ for (const index of this.indexes_) {
739
+ index.forEachTopLevelReference(fn);
740
+ }
741
+ }
742
+
697
743
  /**
698
744
  * @override
699
745
  * @export
@@ -708,7 +754,7 @@ shaka.media.MetaSegmentIndex = class extends shaka.media.SegmentIndex {
708
754
  return position + numPassedInEarlierIndexes;
709
755
  }
710
756
 
711
- numPassedInEarlierIndexes += index.numEvicted_ +
757
+ numPassedInEarlierIndexes += index.getNumEvicted() +
712
758
  index.getNumReferences();
713
759
  }
714
760
 
@@ -725,7 +771,7 @@ shaka.media.MetaSegmentIndex = class extends shaka.media.SegmentIndex {
725
771
 
726
772
  for (const index of this.indexes_) {
727
773
  goog.asserts.assert(
728
- !sawSegments || index.numEvicted_ == 0,
774
+ !sawSegments || index.getNumEvicted() == 0,
729
775
  'Should not see evicted segments after available segments');
730
776
  const reference = index.get(position - numPassedInEarlierIndexes);
731
777
 
@@ -734,7 +780,7 @@ shaka.media.MetaSegmentIndex = class extends shaka.media.SegmentIndex {
734
780
  }
735
781
 
736
782
  const num = index.getNumReferences();
737
- numPassedInEarlierIndexes += index.numEvicted_ + num;
783
+ numPassedInEarlierIndexes += index.getNumEvicted() + num;
738
784
  sawSegments = sawSegments || num != 0;
739
785
  }
740
786
 
@@ -41,42 +41,64 @@ shaka.media.SegmentPrefetch = class {
41
41
 
42
42
  /**
43
43
  * @private {!Map.<
44
- * !(shaka.media.SegmentReference|shaka.media.InitSegmentReference),
44
+ * !(shaka.media.SegmentReference),
45
45
  * !shaka.media.SegmentPrefetchOperation>}
46
46
  */
47
47
  this.segmentPrefetchMap_ = new Map();
48
+
49
+ /**
50
+ * @private {!Map.<
51
+ * !(shaka.media.InitSegmentReference),
52
+ * !shaka.media.SegmentPrefetchOperation>}
53
+ */
54
+ this.initSegmentPrefetchMap_ = new Map();
55
+ }
56
+
57
+ /**
58
+ * @param {shaka.media.SegmentPrefetch.FetchDispatcher} fetchDispatcher
59
+ */
60
+ replaceFetchDispatcher(fetchDispatcher) {
61
+ this.fetchDispatcher_ = fetchDispatcher;
62
+ for (const operation of this.segmentPrefetchMap_.values()) {
63
+ operation.replaceFetchDispatcher(fetchDispatcher);
64
+ }
48
65
  }
49
66
 
50
67
  /**
51
- * Fetch next segments ahead of current segment.
68
+ * @return {number}
69
+ */
70
+ getLastKnownPosition() {
71
+ return this.prefetchPosTime_;
72
+ }
73
+
74
+ /**
75
+ * Fetch next segments ahead of current time.
52
76
  *
53
- * @param {(!shaka.media.SegmentReference)} startReference
77
+ * @param {number} currTime
54
78
  * @param {boolean=} skipFirst
55
79
  * @public
56
80
  */
57
- prefetchSegments(startReference, skipFirst = false) {
81
+ prefetchSegmentsByTime(currTime, skipFirst = false) {
58
82
  goog.asserts.assert(this.prefetchLimit_ > 0,
59
83
  'SegmentPrefetch can not be used when prefetchLimit <= 0.');
60
84
 
61
85
  const logPrefix = shaka.media.SegmentPrefetch.logPrefix_(this.stream_);
62
86
  if (!this.stream_.segmentIndex) {
63
- shaka.log.info(logPrefix, 'missing segmentIndex');
87
+ shaka.log.debug(logPrefix, 'missing segmentIndex');
64
88
  return;
65
89
  }
66
- const currTime = startReference.startTime;
67
90
  const maxTime = Math.max(currTime, this.prefetchPosTime_);
68
- const iterator = this.stream_.segmentIndex.getIteratorForTime(maxTime);
91
+ const iterator = this.stream_.segmentIndex.getIteratorForTime(
92
+ maxTime, /* allowNonIndepedent= */ true);
69
93
  if (!iterator) {
70
94
  return;
71
95
  }
72
- let reference = startReference;
96
+ let reference = iterator.next().value;
73
97
  if (skipFirst) {
74
98
  reference = iterator.next().value;
75
- if (reference &&
76
- reference.startTime == startReference.startTime &&
77
- reference.endTime == startReference.endTime) {
78
- reference = null;
79
- }
99
+ }
100
+ if (!reference) {
101
+ return;
80
102
  }
81
103
  while (this.segmentPrefetchMap_.size < this.prefetchLimit_ &&
82
104
  reference != null) {
@@ -90,6 +112,9 @@ shaka.media.SegmentPrefetch = class {
90
112
  shaka.media.SegmentReference.Status.MISSING) {
91
113
  prefetchAllowed = false;
92
114
  }
115
+ if (prefetchAllowed && reference.initSegmentReference) {
116
+ this.prefetchInitSegment(reference.initSegmentReference);
117
+ }
93
118
  if (prefetchAllowed && !this.segmentPrefetchMap_.has(reference)) {
94
119
  const segmentPrefetchOperation =
95
120
  new shaka.media.SegmentPrefetchOperation(this.fetchDispatcher_);
@@ -103,13 +128,13 @@ shaka.media.SegmentPrefetch = class {
103
128
  }
104
129
  reference = iterator.next().value;
105
130
  }
131
+ this.clearInitSegments_();
106
132
  }
107
133
 
108
134
  /**
109
135
  * Fetch init segment.
110
136
  *
111
137
  * @param {!shaka.media.InitSegmentReference} initSegmentReference
112
- * @public
113
138
  */
114
139
  prefetchInitSegment(initSegmentReference) {
115
140
  goog.asserts.assert(this.prefetchLimit_ > 0,
@@ -117,19 +142,18 @@ shaka.media.SegmentPrefetch = class {
117
142
 
118
143
  const logPrefix = shaka.media.SegmentPrefetch.logPrefix_(this.stream_);
119
144
  if (!this.stream_.segmentIndex) {
120
- shaka.log.info(logPrefix, 'missing segmentIndex');
145
+ shaka.log.debug(logPrefix, 'missing segmentIndex');
121
146
  return;
122
147
  }
123
148
 
124
- if (this.segmentPrefetchMap_.size < this.prefetchLimit_) {
125
- if (!this.segmentPrefetchMap_.has(initSegmentReference)) {
126
- const segmentPrefetchOperation =
127
- new shaka.media.SegmentPrefetchOperation(this.fetchDispatcher_);
128
- segmentPrefetchOperation.dispatchFetch(
129
- initSegmentReference, this.stream_);
130
- this.segmentPrefetchMap_.set(
131
- initSegmentReference, segmentPrefetchOperation);
132
- }
149
+ // init segments are ignored from the prefetch limit
150
+ if (!this.initSegmentPrefetchMap_.has(initSegmentReference)) {
151
+ const segmentPrefetchOperation =
152
+ new shaka.media.SegmentPrefetchOperation(this.fetchDispatcher_);
153
+ segmentPrefetchOperation.dispatchFetch(
154
+ initSegmentReference, this.stream_);
155
+ this.initSegmentPrefetchMap_.set(
156
+ initSegmentReference, segmentPrefetchOperation);
133
157
  }
134
158
  }
135
159
 
@@ -147,22 +171,26 @@ shaka.media.SegmentPrefetch = class {
147
171
 
148
172
  const logPrefix = shaka.media.SegmentPrefetch.logPrefix_(this.stream_);
149
173
 
150
- if (this.segmentPrefetchMap_.has(reference)) {
151
- const segmentPrefetchOperation = this.segmentPrefetchMap_.get(reference);
174
+ let prefetchMap = this.segmentPrefetchMap_;
175
+ if (reference instanceof shaka.media.InitSegmentReference) {
176
+ prefetchMap = this.initSegmentPrefetchMap_;
177
+ }
178
+
179
+ if (prefetchMap.has(reference)) {
180
+ const segmentPrefetchOperation = prefetchMap.get(reference);
152
181
  if (streamDataCallback) {
153
182
  segmentPrefetchOperation.setStreamDataCallback(streamDataCallback);
154
183
  }
155
- this.segmentPrefetchMap_.delete(reference);
156
184
  if (reference instanceof shaka.media.SegmentReference) {
157
185
  shaka.log.debug(
158
186
  logPrefix,
159
187
  'reused prefetched segment at time:', reference.startTime,
160
- 'mapSize', this.segmentPrefetchMap_.size);
188
+ 'mapSize', prefetchMap.size);
161
189
  } else {
162
190
  shaka.log.debug(
163
191
  logPrefix,
164
192
  'reused prefetched init segment at time, mapSize',
165
- this.segmentPrefetchMap_.size);
193
+ prefetchMap.size);
166
194
  }
167
195
  return segmentPrefetchOperation.getOperation();
168
196
  } else {
@@ -170,33 +198,75 @@ shaka.media.SegmentPrefetch = class {
170
198
  shaka.log.debug(
171
199
  logPrefix,
172
200
  'missed segment at time:', reference.startTime,
173
- 'mapSize', this.segmentPrefetchMap_.size);
201
+ 'mapSize', prefetchMap.size);
174
202
  } else {
175
203
  shaka.log.debug(
176
204
  logPrefix,
177
205
  'missed init segment at time, mapSize',
178
- this.segmentPrefetchMap_.size);
206
+ prefetchMap.size);
179
207
  }
180
208
  return null;
181
209
  }
182
210
  }
183
211
 
212
+ /**
213
+ * Clear All Helper
214
+ * @private
215
+ */
216
+ clearMap_(map) {
217
+ for (const reference of map.keys()) {
218
+ if (reference) {
219
+ this.abortPrefetchedSegment_(reference);
220
+ }
221
+ }
222
+ }
223
+
184
224
  /**
185
225
  * Clear all segment data.
186
226
  * @public
187
227
  */
188
228
  clearAll() {
189
- if (this.segmentPrefetchMap_.size === 0) {
190
- return;
191
- }
229
+ this.clearMap_(this.segmentPrefetchMap_);
230
+ this.clearMap_(this.initSegmentPrefetchMap_);
192
231
  const logPrefix = shaka.media.SegmentPrefetch.logPrefix_(this.stream_);
193
- for (const reference of this.segmentPrefetchMap_.keys()) {
194
- if (reference) {
195
- this.abortPrefetchedSegment_(reference);
232
+ shaka.log.debug(logPrefix, 'cleared all');
233
+ this.prefetchPosTime_ = 0;
234
+ }
235
+
236
+ /**
237
+ * @param {number} time
238
+ * @param {boolean=} clearInitSegments
239
+ */
240
+ evict(time, clearInitSegments = false) {
241
+ for (const ref of this.segmentPrefetchMap_.keys()) {
242
+ if (time > ref.endTime) {
243
+ this.abortPrefetchedSegment_(ref);
244
+ }
245
+ }
246
+ if (clearInitSegments) {
247
+ this.clearInitSegments_();
248
+ }
249
+ }
250
+
251
+ /**
252
+ * Remove all init segments that don't have associated segments in
253
+ * the segment prefetch map.
254
+ * By default, with delete on get, the init segments should get removed as
255
+ * they are used. With deleteOnGet set to false, we need to clear them
256
+ * every so often once the segments that are associated with each init segment
257
+ * is no longer prefetched.
258
+ * @private
259
+ */
260
+ clearInitSegments_() {
261
+ const segmentReferences = Array.from(this.segmentPrefetchMap_.keys());
262
+ for (const initSegmentReference of this.initSegmentPrefetchMap_.keys()) {
263
+ // if no segment references this init segment, we should remove it.
264
+ if (!segmentReferences.some(
265
+ (segmentReference) =>
266
+ segmentReference.initSegmentReference === initSegmentReference)) {
267
+ this.abortPrefetchedSegment_(initSegmentReference);
196
268
  }
197
269
  }
198
- shaka.log.info(logPrefix, 'cleared all');
199
- this.prefetchPosTime_ = 0;
200
270
  }
201
271
 
202
272
  /**
@@ -208,6 +278,9 @@ shaka.media.SegmentPrefetch = class {
208
278
  resetLimit(newPrefetchLimit) {
209
279
  goog.asserts.assert(newPrefetchLimit >= 0,
210
280
  'The new prefetch limit must be >= 0.');
281
+
282
+ const logPrefix = shaka.media.SegmentPrefetch.logPrefix_(this.stream_);
283
+ shaka.log.debug(logPrefix, 'resetting prefetch limit to', newPrefetchLimit);
211
284
  this.prefetchLimit_ = newPrefetchLimit;
212
285
  const keyArr = Array.from(this.segmentPrefetchMap_.keys());
213
286
  while (keyArr.length > newPrefetchLimit) {
@@ -216,6 +289,7 @@ shaka.media.SegmentPrefetch = class {
216
289
  this.abortPrefetchedSegment_(reference);
217
290
  }
218
291
  }
292
+ this.clearInitSegments_();
219
293
  }
220
294
 
221
295
  /**
@@ -230,6 +304,15 @@ shaka.media.SegmentPrefetch = class {
230
304
  }
231
305
  }
232
306
 
307
+ /**
308
+ * Get the current stream.
309
+ * @public
310
+ * @return {shaka.extern.Stream}
311
+ */
312
+ getStream() {
313
+ return this.stream_;
314
+ }
315
+
233
316
  /**
234
317
  * Remove a segment from prefetch map and abort it.
235
318
  * @param {!(shaka.media.SegmentReference|shaka.media.InitSegmentReference)}
@@ -238,16 +321,23 @@ shaka.media.SegmentPrefetch = class {
238
321
  */
239
322
  abortPrefetchedSegment_(reference) {
240
323
  const logPrefix = shaka.media.SegmentPrefetch.logPrefix_(this.stream_);
241
- const segmentPrefetchOperation = this.segmentPrefetchMap_.get(reference);
242
- this.segmentPrefetchMap_.delete(reference);
324
+
325
+ let prefetchMap = this.segmentPrefetchMap_;
326
+ if (reference instanceof shaka.media.InitSegmentReference) {
327
+ prefetchMap = this.initSegmentPrefetchMap_;
328
+ }
329
+
330
+ const segmentPrefetchOperation = prefetchMap.get(reference);
331
+ prefetchMap.delete(reference);
332
+
243
333
  if (segmentPrefetchOperation) {
244
334
  segmentPrefetchOperation.abort();
245
335
  if (reference instanceof shaka.media.SegmentReference) {
246
- shaka.log.info(
336
+ shaka.log.debug(
247
337
  logPrefix,
248
338
  'pop and abort prefetched segment at time:', reference.startTime);
249
339
  } else {
250
- shaka.log.info(logPrefix, 'pop and abort prefetched init segment');
340
+ shaka.log.debug(logPrefix, 'pop and abort prefetched init segment');
251
341
  }
252
342
  }
253
343
  }
@@ -281,6 +371,13 @@ shaka.media.SegmentPrefetchOperation = class {
281
371
  this.operation_ = null;
282
372
  }
283
373
 
374
+ /**
375
+ * @param {shaka.media.SegmentPrefetch.FetchDispatcher} fetchDispatcher
376
+ */
377
+ replaceFetchDispatcher(fetchDispatcher) {
378
+ this.fetchDispatcher_ = fetchDispatcher;
379
+ }
380
+
284
381
  /**
285
382
  * Fetch a segments
286
383
  *