rx-player 3.28.0-dev.2022063000 → 3.28.1-dev.2022083000

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 (108) hide show
  1. package/.github/workflows/checks.yml +20 -18
  2. package/CHANGELOG.md +14 -2
  3. package/VERSION +1 -1
  4. package/dist/_esm5.processed/compat/eme/load_session.d.ts +5 -6
  5. package/dist/_esm5.processed/compat/eme/load_session.js +5 -6
  6. package/dist/_esm5.processed/compat/event_listeners.js +5 -0
  7. package/dist/_esm5.processed/core/api/media_element_track_choice_manager.js +1 -1
  8. package/dist/_esm5.processed/core/api/public_api.js +2 -2
  9. package/dist/_esm5.processed/core/decrypt/create_session.js +33 -4
  10. package/dist/_esm5.processed/core/decrypt/utils/loaded_sessions_store.d.ts +14 -0
  11. package/dist/_esm5.processed/core/decrypt/utils/loaded_sessions_store.js +25 -0
  12. package/dist/_esm5.processed/core/fetchers/segment/segment_fetcher.d.ts +1 -1
  13. package/dist/_esm5.processed/core/fetchers/segment/segment_fetcher.js +1 -1
  14. package/dist/_esm5.processed/core/init/content_time_boundaries_observer.js +110 -38
  15. package/dist/_esm5.processed/core/stream/representation/check_for_discontinuity.js +21 -9
  16. package/dist/_esm5.processed/core/stream/representation/get_buffer_status.js +21 -29
  17. package/dist/_esm5.processed/errors/encrypted_media_error.d.ts +1 -2
  18. package/dist/_esm5.processed/errors/encrypted_media_error.js +0 -1
  19. package/dist/_esm5.processed/errors/error_codes.d.ts +6 -1
  20. package/dist/_esm5.processed/errors/media_error.d.ts +1 -2
  21. package/dist/_esm5.processed/errors/media_error.js +0 -1
  22. package/dist/_esm5.processed/errors/network_error.d.ts +1 -2
  23. package/dist/_esm5.processed/errors/network_error.js +0 -1
  24. package/dist/_esm5.processed/errors/other_error.d.ts +1 -2
  25. package/dist/_esm5.processed/errors/other_error.js +0 -1
  26. package/dist/_esm5.processed/manifest/manifest.d.ts +1 -1
  27. package/dist/_esm5.processed/manifest/manifest.js +1 -1
  28. package/dist/_esm5.processed/manifest/representation_index/static.d.ts +21 -6
  29. package/dist/_esm5.processed/manifest/representation_index/static.js +26 -8
  30. package/dist/_esm5.processed/manifest/representation_index/types.d.ts +55 -44
  31. package/dist/_esm5.processed/parsers/manifest/dash/common/indexes/base.d.ts +21 -8
  32. package/dist/_esm5.processed/parsers/manifest/dash/common/indexes/base.js +25 -10
  33. package/dist/_esm5.processed/parsers/manifest/dash/common/indexes/list.d.ts +26 -12
  34. package/dist/_esm5.processed/parsers/manifest/dash/common/indexes/list.js +26 -13
  35. package/dist/_esm5.processed/parsers/manifest/dash/common/indexes/template.d.ts +23 -7
  36. package/dist/_esm5.processed/parsers/manifest/dash/common/indexes/template.js +65 -22
  37. package/dist/_esm5.processed/parsers/manifest/dash/common/indexes/timeline/timeline_representation_index.d.ts +20 -3
  38. package/dist/_esm5.processed/parsers/manifest/dash/common/indexes/timeline/timeline_representation_index.js +57 -7
  39. package/dist/_esm5.processed/parsers/manifest/dash/common/indexes/{is_period_fulfilled.d.ts → utils.d.ts} +3 -6
  40. package/dist/_esm5.processed/parsers/manifest/dash/common/indexes/{is_period_fulfilled.js → utils.js} +4 -8
  41. package/dist/_esm5.processed/parsers/manifest/dash/common/parse_periods.js +1 -1
  42. package/dist/_esm5.processed/parsers/manifest/local/parse_local_manifest.js +5 -8
  43. package/dist/_esm5.processed/parsers/manifest/local/representation_index.d.ts +21 -8
  44. package/dist/_esm5.processed/parsers/manifest/local/representation_index.js +49 -14
  45. package/dist/_esm5.processed/parsers/manifest/local/types.d.ts +16 -0
  46. package/dist/_esm5.processed/parsers/manifest/metaplaylist/representation_index.d.ts +20 -6
  47. package/dist/_esm5.processed/parsers/manifest/metaplaylist/representation_index.js +28 -10
  48. package/dist/_esm5.processed/parsers/manifest/smooth/create_parser.js +4 -4
  49. package/dist/_esm5.processed/parsers/manifest/smooth/representation_index.d.ts +21 -12
  50. package/dist/_esm5.processed/parsers/manifest/smooth/representation_index.js +39 -14
  51. package/dist/_esm5.processed/parsers/manifest/utils/get_first_time_from_adaptation.js +1 -1
  52. package/dist/_esm5.processed/parsers/manifest/utils/get_last_time_from_adaptation.js +1 -1
  53. package/dist/_esm5.processed/transports/metaplaylist/pipelines.js +0 -2
  54. package/dist/_esm5.processed/transports/smooth/segment_loader.js +1 -1
  55. package/dist/_esm5.processed/transports/types.d.ts +1 -1
  56. package/dist/_esm5.processed/utils/deep_merge.d.ts +1 -1
  57. package/dist/_esm5.processed/utils/deep_merge.js +6 -5
  58. package/dist/_esm5.processed/utils/task_canceller.d.ts +0 -3
  59. package/dist/_esm5.processed/utils/task_canceller.js +0 -3
  60. package/dist/mpd-parser.wasm +0 -0
  61. package/dist/rx-player.js +1390 -1059
  62. package/dist/rx-player.min.js +1 -1
  63. package/jest.config.js +5 -0
  64. package/package.json +31 -30
  65. package/sonar-project.properties +1 -1
  66. package/src/compat/eme/load_session.ts +5 -6
  67. package/src/compat/event_listeners.ts +5 -0
  68. package/src/core/api/media_element_track_choice_manager.ts +1 -1
  69. package/src/core/api/public_api.ts +2 -2
  70. package/src/core/decrypt/create_session.ts +28 -2
  71. package/src/core/decrypt/utils/loaded_sessions_store.ts +29 -0
  72. package/src/core/fetchers/segment/segment_fetcher.ts +1 -1
  73. package/src/core/init/content_time_boundaries_observer.ts +116 -42
  74. package/src/core/stream/representation/check_for_discontinuity.ts +28 -10
  75. package/src/core/stream/representation/get_buffer_status.ts +27 -34
  76. package/src/errors/encrypted_media_error.ts +1 -2
  77. package/src/errors/error_codes.ts +2 -2
  78. package/src/errors/media_error.ts +1 -2
  79. package/src/errors/network_error.ts +1 -2
  80. package/src/errors/other_error.ts +1 -2
  81. package/src/manifest/__tests__/adaptation.test.ts +4 -3
  82. package/src/manifest/__tests__/representation.test.ts +4 -3
  83. package/src/manifest/manifest.ts +1 -1
  84. package/src/manifest/representation_index/__tests__/static.test.ts +5 -4
  85. package/src/manifest/representation_index/static.ts +28 -9
  86. package/src/manifest/representation_index/types.ts +62 -46
  87. package/src/parsers/manifest/dash/common/indexes/base.ts +27 -11
  88. package/src/parsers/manifest/dash/common/indexes/list.ts +32 -15
  89. package/src/parsers/manifest/dash/common/indexes/template.ts +73 -27
  90. package/src/parsers/manifest/dash/common/indexes/timeline/timeline_representation_index.ts +60 -8
  91. package/src/parsers/manifest/dash/common/indexes/{is_period_fulfilled.ts → utils.ts} +4 -13
  92. package/src/parsers/manifest/dash/common/parse_periods.ts +1 -1
  93. package/src/parsers/manifest/local/parse_local_manifest.ts +8 -20
  94. package/src/parsers/manifest/local/representation_index.ts +51 -16
  95. package/src/parsers/manifest/local/types.ts +13 -0
  96. package/src/parsers/manifest/metaplaylist/representation_index.ts +31 -11
  97. package/src/parsers/manifest/smooth/create_parser.ts +4 -4
  98. package/src/parsers/manifest/smooth/representation_index.ts +40 -15
  99. package/src/parsers/manifest/utils/__tests__/get_first_time_from_adaptations.test.ts +4 -3
  100. package/src/parsers/manifest/utils/__tests__/get_last_time_from_adaptation.test.ts +4 -3
  101. package/src/parsers/manifest/utils/get_first_time_from_adaptation.ts +1 -1
  102. package/src/parsers/manifest/utils/get_last_time_from_adaptation.ts +1 -1
  103. package/src/transports/metaplaylist/pipelines.ts +0 -2
  104. package/src/transports/smooth/segment_loader.ts +1 -1
  105. package/src/transports/types.ts +1 -1
  106. package/src/utils/__tests__/initialization_segment_cache.test.ts +7 -0
  107. package/src/utils/deep_merge.ts +7 -4
  108. package/src/utils/task_canceller.ts +0 -3
@@ -60,7 +60,7 @@ var ListRepresentationIndex = /** @class */ (function () {
60
60
  };
61
61
  /**
62
62
  * @param {Number} fromTime
63
- * @param {Number} duration
63
+ * @param {Number} dur
64
64
  * @returns {Array.<Object>}
65
65
  */
66
66
  ListRepresentationIndex.prototype.getSegments = function (fromTime, dur) {
@@ -104,23 +104,45 @@ var ListRepresentationIndex = /** @class */ (function () {
104
104
  * Returns first position in this index, in seconds.
105
105
  * @returns {Number}
106
106
  */
107
- ListRepresentationIndex.prototype.getFirstPosition = function () {
107
+ ListRepresentationIndex.prototype.getFirstAvailablePosition = function () {
108
108
  return this._periodStart;
109
109
  };
110
110
  /**
111
111
  * Returns last position in this index, in seconds.
112
112
  * @returns {Number}
113
113
  */
114
- ListRepresentationIndex.prototype.getLastPosition = function () {
114
+ ListRepresentationIndex.prototype.getLastAvailablePosition = function () {
115
115
  var _a;
116
116
  var index = this._index;
117
117
  var duration = index.duration, list = index.list;
118
118
  return Math.min(((list.length * duration) / index.timescale) + this._periodStart, (_a = this._periodEnd) !== null && _a !== void 0 ? _a : Infinity);
119
119
  };
120
+ /**
121
+ * Returns the absolute end in seconds this RepresentationIndex can reach once
122
+ * all segments are available.
123
+ * @returns {number|null|undefined}
124
+ */
125
+ ListRepresentationIndex.prototype.getEnd = function () {
126
+ return this.getLastAvailablePosition();
127
+ };
128
+ /**
129
+ * Returns:
130
+ * - `true` if in the given time interval, at least one new segment is
131
+ * expected to be available in the future.
132
+ * - `false` either if all segments in that time interval are already
133
+ * available for download or if none will ever be available for it.
134
+ * - `undefined` when it is not possible to tell.
135
+ *
136
+ * Always `false` in a `ListRepresentationIndex` because all segments should
137
+ * be directly available.
138
+ * @returns {boolean}
139
+ */
140
+ ListRepresentationIndex.prototype.awaitSegmentBetween = function () {
141
+ return false;
142
+ };
120
143
  /**
121
144
  * Returns true if a Segment returned by this index is still considered
122
145
  * available.
123
- * @param {Object} segment
124
146
  * @returns {Boolean}
125
147
  */
126
148
  ListRepresentationIndex.prototype.isSegmentStillAvailable = function () {
@@ -133,12 +155,6 @@ var ListRepresentationIndex = /** @class */ (function () {
133
155
  ListRepresentationIndex.prototype.checkDiscontinuity = function () {
134
156
  return null;
135
157
  };
136
- /**
137
- * @returns {boolean}
138
- */
139
- ListRepresentationIndex.prototype.areSegmentsChronologicallyGenerated = function () {
140
- return true;
141
- };
142
158
  /**
143
159
  * SegmentList should not be updated.
144
160
  * @returns {Boolean}
@@ -164,9 +180,6 @@ var ListRepresentationIndex = /** @class */ (function () {
164
180
  ListRepresentationIndex.prototype._replace = function (newIndex) {
165
181
  this._index = newIndex._index;
166
182
  };
167
- /**
168
- * @param {Object} newIndex
169
- */
170
183
  ListRepresentationIndex.prototype._update = function () {
171
184
  log.error("List RepresentationIndex: Cannot update a SegmentList");
172
185
  };
@@ -46,6 +46,7 @@ export interface ITemplateIndex {
46
46
  /**
47
47
  * URL base to access any segment.
48
48
  * Can contain token to replace to convert it to real URLs.
49
+ * `null` if no URL exists.
49
50
  */
50
51
  mediaURLs: string[] | null;
51
52
  /**
@@ -134,7 +135,7 @@ export default class TemplateRepresentationIndex implements IRepresentationIndex
134
135
  /** Absolute start of the Period, in seconds. */
135
136
  private _periodStart;
136
137
  /** Difference between the end time of the Period and its start time, in timescale. */
137
- private _scaledPeriodEnd;
138
+ private _scaledRelativePeriodEnd;
138
139
  /** Minimum availabilityTimeOffset concerning the segments of this Representation. */
139
140
  private _availabilityTimeOffset;
140
141
  /** Whether the corresponding Manifest can be updated and changed. */
@@ -160,12 +161,31 @@ export default class TemplateRepresentationIndex implements IRepresentationIndex
160
161
  * Returns first possible position in the index, in seconds.
161
162
  * @returns {number|null|undefined}
162
163
  */
163
- getFirstPosition(): number | null | undefined;
164
+ getFirstAvailablePosition(): number | null | undefined;
164
165
  /**
165
166
  * Returns last possible position in the index, in seconds.
166
167
  * @returns {number|null}
167
168
  */
168
- getLastPosition(): number | null | undefined;
169
+ getLastAvailablePosition(): number | null | undefined;
170
+ /**
171
+ * Returns the absolute end in seconds this RepresentationIndex can reach once
172
+ * all segments are available.
173
+ * @returns {number|null|undefined}
174
+ */
175
+ getEnd(): number | null | undefined;
176
+ /**
177
+ * Returns:
178
+ * - `true` if in the given time interval, at least one new segment is
179
+ * expected to be available in the future.
180
+ * - `false` either if all segments in that time interval are already
181
+ * available for download or if none will ever be available for it.
182
+ * - `undefined` when it is not possible to tell.
183
+ *
184
+ * Always `false` in a `BaseRepresentationIndex` because all segments should
185
+ * be directly available.
186
+ * @returns {boolean}
187
+ */
188
+ awaitSegmentBetween(start: number, end: number): boolean | undefined;
169
189
  /**
170
190
  * Returns true if, based on the arguments, the index should be refreshed.
171
191
  * We never have to refresh a SegmentTemplate-based manifest.
@@ -177,10 +197,6 @@ export default class TemplateRepresentationIndex implements IRepresentationIndex
177
197
  * @returns {null}
178
198
  */
179
199
  checkDiscontinuity(): null;
180
- /**
181
- * @returns {Boolean}
182
- */
183
- areSegmentsChronologicallyGenerated(): true;
184
200
  /**
185
201
  * Returns `true` if the given segment should still be available as of now
186
202
  * (not removed since and still request-able).
@@ -14,9 +14,10 @@
14
14
  * limitations under the License.
15
15
  */
16
16
  import config from "../../../../../config";
17
+ import assert from "../../../../../utils/assert";
17
18
  import getInitSegment from "./get_init_segment";
18
- import isPeriodFulfilled from "./is_period_fulfilled";
19
19
  import { createDashUrlDetokenizer, createIndexURLs, } from "./tokens";
20
+ import { getSegmentTimeRoundingError } from "./utils";
20
21
  /**
21
22
  * IRepresentationIndex implementation for DASH' SegmentTemplate without a
22
23
  * SegmentTimeline.
@@ -55,7 +56,7 @@ var TemplateRepresentationIndex = /** @class */ (function () {
55
56
  mediaURLs: createIndexURLs(urlSources, index.media, representationId, representationBitrate), presentationTimeOffset: presentationTimeOffset, startNumber: index.startNumber };
56
57
  this._isDynamic = isDynamic;
57
58
  this._periodStart = periodStart;
58
- this._scaledPeriodEnd = periodEnd === undefined ?
59
+ this._scaledRelativePeriodEnd = periodEnd === undefined ?
59
60
  undefined :
60
61
  (periodEnd - periodStart) * timescale;
61
62
  this._isEMSGWhitelisted = isEMSGWhitelisted;
@@ -76,7 +77,7 @@ var TemplateRepresentationIndex = /** @class */ (function () {
76
77
  var index = this._index;
77
78
  var duration = index.duration, startNumber = index.startNumber, timescale = index.timescale, mediaURLs = index.mediaURLs;
78
79
  var scaledStart = this._periodStart * timescale;
79
- var scaledEnd = this._scaledPeriodEnd;
80
+ var scaledEnd = this._scaledRelativePeriodEnd;
80
81
  // Convert the asked position to the right timescales, and consider them
81
82
  // relatively to the Period's start.
82
83
  var upFromPeriodStart = fromTime * timescale - scaledStart;
@@ -132,7 +133,7 @@ var TemplateRepresentationIndex = /** @class */ (function () {
132
133
  * Returns first possible position in the index, in seconds.
133
134
  * @returns {number|null|undefined}
134
135
  */
135
- TemplateRepresentationIndex.prototype.getFirstPosition = function () {
136
+ TemplateRepresentationIndex.prototype.getFirstAvailablePosition = function () {
136
137
  var firstSegmentStart = this._getFirstSegmentStart();
137
138
  if (firstSegmentStart == null) {
138
139
  return firstSegmentStart; // return undefined or null
@@ -143,18 +144,64 @@ var TemplateRepresentationIndex = /** @class */ (function () {
143
144
  * Returns last possible position in the index, in seconds.
144
145
  * @returns {number|null}
145
146
  */
146
- TemplateRepresentationIndex.prototype.getLastPosition = function () {
147
+ TemplateRepresentationIndex.prototype.getLastAvailablePosition = function () {
147
148
  var _a;
148
149
  var lastSegmentStart = this._getLastSegmentStart();
149
150
  if (lastSegmentStart == null) {
150
- // In that case (null or undefined), getLastPosition should reflect
151
+ // In that case (null or undefined), getLastAvailablePosition should reflect
151
152
  // the result of getLastSegmentStart, as the meaning is the same for
152
153
  // the two functions. So, we return the result of the latter.
153
154
  return lastSegmentStart;
154
155
  }
155
- var lastSegmentEnd = Math.min(lastSegmentStart + this._index.duration, (_a = this._scaledPeriodEnd) !== null && _a !== void 0 ? _a : Infinity);
156
+ var lastSegmentEnd = Math.min(lastSegmentStart + this._index.duration, (_a = this._scaledRelativePeriodEnd) !== null && _a !== void 0 ? _a : Infinity);
156
157
  return (lastSegmentEnd / this._index.timescale) + this._periodStart;
157
158
  };
159
+ /**
160
+ * Returns the absolute end in seconds this RepresentationIndex can reach once
161
+ * all segments are available.
162
+ * @returns {number|null|undefined}
163
+ */
164
+ TemplateRepresentationIndex.prototype.getEnd = function () {
165
+ if (!this._isDynamic) {
166
+ return this.getLastAvailablePosition();
167
+ }
168
+ if (this._scaledRelativePeriodEnd === undefined) {
169
+ return undefined;
170
+ }
171
+ var timescale = this._index.timescale;
172
+ var absoluteScaledPeriodEnd = (this._scaledRelativePeriodEnd +
173
+ this._periodStart * timescale);
174
+ return absoluteScaledPeriodEnd / this._index.timescale;
175
+ };
176
+ /**
177
+ * Returns:
178
+ * - `true` if in the given time interval, at least one new segment is
179
+ * expected to be available in the future.
180
+ * - `false` either if all segments in that time interval are already
181
+ * available for download or if none will ever be available for it.
182
+ * - `undefined` when it is not possible to tell.
183
+ *
184
+ * Always `false` in a `BaseRepresentationIndex` because all segments should
185
+ * be directly available.
186
+ * @returns {boolean}
187
+ */
188
+ TemplateRepresentationIndex.prototype.awaitSegmentBetween = function (start, end) {
189
+ assert(start <= end);
190
+ if (!this._isDynamic) {
191
+ return false;
192
+ }
193
+ var timescale = this._index.timescale;
194
+ var segmentTimeRounding = getSegmentTimeRoundingError(timescale);
195
+ var scaledPeriodStart = this._periodStart * timescale;
196
+ var scaledRelativeEnd = end * timescale - scaledPeriodStart;
197
+ if (this._scaledRelativePeriodEnd === undefined) {
198
+ return (scaledRelativeEnd + segmentTimeRounding) >= 0;
199
+ }
200
+ var scaledRelativePeriodEnd = this._scaledRelativePeriodEnd;
201
+ var scaledRelativeStart = start * timescale - scaledPeriodStart;
202
+ return (scaledRelativeStart - segmentTimeRounding) < scaledRelativePeriodEnd &&
203
+ (scaledRelativeEnd + segmentTimeRounding) >= 0;
204
+ };
158
205
  /**
159
206
  * Returns true if, based on the arguments, the index should be refreshed.
160
207
  * We never have to refresh a SegmentTemplate-based manifest.
@@ -170,12 +217,6 @@ var TemplateRepresentationIndex = /** @class */ (function () {
170
217
  TemplateRepresentationIndex.prototype.checkDiscontinuity = function () {
171
218
  return null;
172
219
  };
173
- /**
174
- * @returns {Boolean}
175
- */
176
- TemplateRepresentationIndex.prototype.areSegmentsChronologicallyGenerated = function () {
177
- return true;
178
- };
179
220
  /**
180
221
  * Returns `true` if the given segment should still be available as of now
181
222
  * (not removed since and still request-able).
@@ -210,7 +251,7 @@ var TemplateRepresentationIndex = /** @class */ (function () {
210
251
  if (!this._isDynamic) {
211
252
  return true;
212
253
  }
213
- if (this._scaledPeriodEnd === undefined) {
254
+ if (this._scaledRelativePeriodEnd === undefined) {
214
255
  return false;
215
256
  }
216
257
  var timescale = this._index.timescale;
@@ -221,7 +262,8 @@ var TemplateRepresentationIndex = /** @class */ (function () {
221
262
  return false;
222
263
  }
223
264
  var lastSegmentEnd = lastSegmentStart + this._index.duration;
224
- return isPeriodFulfilled(timescale, lastSegmentEnd, this._scaledPeriodEnd);
265
+ var segmentTimeRounding = getSegmentTimeRoundingError(timescale);
266
+ return (lastSegmentEnd + segmentTimeRounding) >= this._scaledRelativePeriodEnd;
225
267
  };
226
268
  /**
227
269
  * @returns {Boolean}
@@ -237,7 +279,7 @@ var TemplateRepresentationIndex = /** @class */ (function () {
237
279
  this._aggressiveMode = newIndex._aggressiveMode;
238
280
  this._isDynamic = newIndex._isDynamic;
239
281
  this._periodStart = newIndex._periodStart;
240
- this._scaledPeriodEnd = newIndex._scaledPeriodEnd;
282
+ this._scaledRelativePeriodEnd = newIndex._scaledRelativePeriodEnd;
241
283
  this._manifestBoundsCalculator = newIndex._manifestBoundsCalculator;
242
284
  };
243
285
  /**
@@ -258,7 +300,8 @@ var TemplateRepresentationIndex = /** @class */ (function () {
258
300
  return 0; // it is the start of the Period
259
301
  }
260
302
  // 1 - check that this index is already available
261
- if (this._scaledPeriodEnd === 0 || this._scaledPeriodEnd === undefined) {
303
+ if (this._scaledRelativePeriodEnd === 0 ||
304
+ this._scaledRelativePeriodEnd === undefined) {
262
305
  // /!\ The scaled max position augments continuously and might not
263
306
  // reflect exactly the real server-side value. As segments are
264
307
  // generated discretely.
@@ -296,13 +339,13 @@ var TemplateRepresentationIndex = /** @class */ (function () {
296
339
  }
297
340
  var agressiveModeOffset = this._aggressiveMode ? (duration / timescale) :
298
341
  0;
299
- if (this._scaledPeriodEnd != null &&
300
- this._scaledPeriodEnd <
342
+ if (this._scaledRelativePeriodEnd != null &&
343
+ this._scaledRelativePeriodEnd <
301
344
  (lastPos + agressiveModeOffset - this._periodStart) * this._index.timescale) {
302
- if (this._scaledPeriodEnd < duration) {
345
+ if (this._scaledRelativePeriodEnd < duration) {
303
346
  return null;
304
347
  }
305
- return (Math.floor(this._scaledPeriodEnd / duration) - 1) * duration;
348
+ return (Math.floor(this._scaledRelativePeriodEnd / duration) - 1) * duration;
306
349
  }
307
350
  // /!\ The scaled last position augments continuously and might not
308
351
  // reflect exactly the real server-side value. As segments are
@@ -321,7 +364,7 @@ var TemplateRepresentationIndex = /** @class */ (function () {
321
364
  (numberOfSegmentsAvailable - 1) * duration;
322
365
  }
323
366
  else {
324
- var maximumTime = (_a = this._scaledPeriodEnd) !== null && _a !== void 0 ? _a : 0;
367
+ var maximumTime = (_a = this._scaledRelativePeriodEnd) !== null && _a !== void 0 ? _a : 0;
325
368
  var numberIndexedToZero = Math.ceil(maximumTime / duration) - 1;
326
369
  var regularLastSegmentStart = numberIndexedToZero * duration;
327
370
  // In some SegmentTemplate, we could think that there is one more
@@ -219,14 +219,32 @@ export default class TimelineRepresentationIndex implements IRepresentationIndex
219
219
  * Returns null if nothing is in the index
220
220
  * @returns {Number|null}
221
221
  */
222
- getFirstPosition(): number | null;
222
+ getFirstAvailablePosition(): number | null;
223
223
  /**
224
224
  * Returns the ending time, in seconds, of the last segment currently
225
225
  * available.
226
226
  * Returns null if nothing is in the index
227
227
  * @returns {Number|null}
228
228
  */
229
- getLastPosition(): number | null;
229
+ getLastAvailablePosition(): number | null;
230
+ /**
231
+ * Returns the absolute end in seconds this RepresentationIndex can reach once
232
+ * all segments are available.
233
+ * @returns {number|null|undefined}
234
+ */
235
+ getEnd(): number | undefined | null;
236
+ /**
237
+ * Returns:
238
+ * - `true` if in the given time interval, at least one new segment is
239
+ * expected to be available in the future.
240
+ * - `false` either if all segments in that time interval are already
241
+ * available for download or if none will ever be available for it.
242
+ * - `undefined` when it is not possible to tell.
243
+ * @param {number} start
244
+ * @param {number} end
245
+ * @returns {boolean|undefined}
246
+ */
247
+ awaitSegmentBetween(start: number, end: number): boolean | undefined;
230
248
  /**
231
249
  * Returns true if a Segment returned by this index is still considered
232
250
  * available.
@@ -250,7 +268,6 @@ export default class TimelineRepresentationIndex implements IRepresentationIndex
250
268
  * @returns {Boolean}
251
269
  */
252
270
  canBeOutOfSyncError(error: IPlayerError): boolean;
253
- areSegmentsChronologicallyGenerated(): boolean;
254
271
  /**
255
272
  * Replace this RepresentationIndex with one from a new version of the
256
273
  * Manifest.
@@ -16,14 +16,15 @@
16
16
  import config from "../../../../../../config";
17
17
  import { NetworkError } from "../../../../../../errors";
18
18
  import log from "../../../../../../log";
19
+ import assert from "../../../../../../utils/assert";
19
20
  import clearTimelineFromPosition from "../../../../utils/clear_timeline_from_position";
20
21
  import { checkDiscontinuity, fromIndexTime, getIndexSegmentEnd, toIndexTime, } from "../../../../utils/index_helpers";
21
22
  import isSegmentStillAvailable from "../../../../utils/is_segment_still_available";
22
23
  import updateSegmentTimeline from "../../../../utils/update_segment_timeline";
23
24
  import getInitSegment from "../get_init_segment";
24
25
  import getSegmentsFromTimeline from "../get_segments_from_timeline";
25
- import isPeriodFulfilled from "../is_period_fulfilled";
26
26
  import { createIndexURLs } from "../tokens";
27
+ import { getSegmentTimeRoundingError } from "../utils";
27
28
  import constructTimelineFromElements from "./construct_timeline_from_elements";
28
29
  // eslint-disable-next-line max-len
29
30
  import constructTimelineFromPreviousTimeline from "./construct_timeline_from_previous_timeline";
@@ -116,7 +117,7 @@ var TimelineRepresentationIndex = /** @class */ (function () {
116
117
  * Returns null if nothing is in the index
117
118
  * @returns {Number|null}
118
119
  */
119
- TimelineRepresentationIndex.prototype.getFirstPosition = function () {
120
+ TimelineRepresentationIndex.prototype.getFirstAvailablePosition = function () {
120
121
  this._refreshTimeline();
121
122
  if (this._index.timeline === null) {
122
123
  this._index.timeline = this._getTimeline();
@@ -131,7 +132,7 @@ var TimelineRepresentationIndex = /** @class */ (function () {
131
132
  * Returns null if nothing is in the index
132
133
  * @returns {Number|null}
133
134
  */
134
- TimelineRepresentationIndex.prototype.getLastPosition = function () {
135
+ TimelineRepresentationIndex.prototype.getLastAvailablePosition = function () {
135
136
  this._refreshTimeline();
136
137
  if (this._index.timeline === null) {
137
138
  this._index.timeline = this._getTimeline();
@@ -140,6 +141,57 @@ var TimelineRepresentationIndex = /** @class */ (function () {
140
141
  return lastTime === null ? null :
141
142
  fromIndexTime(lastTime, this._index);
142
143
  };
144
+ /**
145
+ * Returns the absolute end in seconds this RepresentationIndex can reach once
146
+ * all segments are available.
147
+ * @returns {number|null|undefined}
148
+ */
149
+ TimelineRepresentationIndex.prototype.getEnd = function () {
150
+ if (!this._isDynamic || !this._isLastPeriod) { // @see isFinished
151
+ return this.getLastAvailablePosition();
152
+ }
153
+ return undefined;
154
+ };
155
+ /**
156
+ * Returns:
157
+ * - `true` if in the given time interval, at least one new segment is
158
+ * expected to be available in the future.
159
+ * - `false` either if all segments in that time interval are already
160
+ * available for download or if none will ever be available for it.
161
+ * - `undefined` when it is not possible to tell.
162
+ * @param {number} start
163
+ * @param {number} end
164
+ * @returns {boolean|undefined}
165
+ */
166
+ TimelineRepresentationIndex.prototype.awaitSegmentBetween = function (start, end) {
167
+ var _a;
168
+ assert(start <= end);
169
+ if (!this._isDynamic || !this._isLastPeriod) {
170
+ return false;
171
+ }
172
+ this._refreshTimeline();
173
+ if (this._index.timeline === null) {
174
+ this._index.timeline = this._getTimeline();
175
+ }
176
+ var _b = this._index, timeline = _b.timeline, timescale = _b.timescale;
177
+ var segmentTimeRounding = getSegmentTimeRoundingError(timescale);
178
+ var scaledEnd = toIndexTime(end, this._index);
179
+ if (timeline.length > 0) {
180
+ var lastTimelineElement = timeline[timeline.length - 1];
181
+ var lastSegmentEnd = getIndexSegmentEnd(lastTimelineElement, null, this._scaledPeriodEnd);
182
+ var roundedEnd = lastSegmentEnd + segmentTimeRounding;
183
+ if (roundedEnd >= Math.min(scaledEnd, (_a = this._scaledPeriodEnd) !== null && _a !== void 0 ? _a : Infinity)) {
184
+ return false; // already loaded
185
+ }
186
+ }
187
+ if (this._scaledPeriodEnd === undefined) {
188
+ return (scaledEnd + segmentTimeRounding) > this._scaledPeriodStart ? undefined :
189
+ false;
190
+ }
191
+ var scaledStart = toIndexTime(start, this._index);
192
+ return (scaledStart - segmentTimeRounding) < this._scaledPeriodEnd &&
193
+ (scaledEnd + segmentTimeRounding) > this._scaledPeriodStart;
194
+ };
143
195
  /**
144
196
  * Returns true if a Segment returned by this index is still considered
145
197
  * available.
@@ -188,9 +240,6 @@ var TimelineRepresentationIndex = /** @class */ (function () {
188
240
  return error instanceof NetworkError &&
189
241
  error.isHttpError(404);
190
242
  };
191
- TimelineRepresentationIndex.prototype.areSegmentsChronologicallyGenerated = function () {
192
- return true;
193
- };
194
243
  /**
195
244
  * Replace this RepresentationIndex with one from a new version of the
196
245
  * Manifest.
@@ -253,7 +302,8 @@ var TimelineRepresentationIndex = /** @class */ (function () {
253
302
  }
254
303
  var lastTimelineElement = timeline[timeline.length - 1];
255
304
  var lastTime = getIndexSegmentEnd(lastTimelineElement, null, this._scaledPeriodEnd);
256
- return isPeriodFulfilled(this._index.timescale, lastTime, this._scaledPeriodEnd);
305
+ var segmentTimeRounding = getSegmentTimeRoundingError(this._index.timescale);
306
+ return (lastTime + segmentTimeRounding) >= this._scaledPeriodEnd;
257
307
  };
258
308
  /**
259
309
  * @returns {Boolean}
@@ -17,12 +17,9 @@
17
17
  * In Javascript, numbers are encoded in a way that a floating number may be
18
18
  * represented internally with a rounding error.
19
19
  *
20
- * As the period end is the result of a multiplication between a floating or integer
21
- * number (period end * timescale), this function takes into account the potential
22
- * rounding error to tell if the period is fulfilled with content.
20
+ * This function returns a small number allowing to accound for rounding many
21
+ * rounding errors.
23
22
  * @param {number} timescale
24
- * @param {number} lastSegmentEnd
25
- * @param {number} periodEnd
26
23
  * @returns {boolean}
27
24
  */
28
- export default function isPeriodFulfilled(timescale: number, lastSegmentEnd: number, periodEnd: number): boolean;
25
+ export declare function getSegmentTimeRoundingError(timescale: number): number;
@@ -18,15 +18,11 @@ import config from "../../../../../config";
18
18
  * In Javascript, numbers are encoded in a way that a floating number may be
19
19
  * represented internally with a rounding error.
20
20
  *
21
- * As the period end is the result of a multiplication between a floating or integer
22
- * number (period end * timescale), this function takes into account the potential
23
- * rounding error to tell if the period is fulfilled with content.
21
+ * This function returns a small number allowing to accound for rounding many
22
+ * rounding errors.
24
23
  * @param {number} timescale
25
- * @param {number} lastSegmentEnd
26
- * @param {number} periodEnd
27
24
  * @returns {boolean}
28
25
  */
29
- export default function isPeriodFulfilled(timescale, lastSegmentEnd, periodEnd) {
30
- var scaledRoundingError = config.getCurrent().DEFAULT_MAXIMUM_TIME_ROUNDING_ERROR * timescale;
31
- return (lastSegmentEnd + scaledRoundingError) >= periodEnd;
26
+ export function getSegmentTimeRoundingError(timescale) {
27
+ return config.getCurrent().DEFAULT_MAXIMUM_TIME_ROUNDING_ERROR * timescale;
32
28
  }
@@ -186,7 +186,7 @@ function getMaximumLastPosition(adaptationsPerType) {
186
186
  var representations = adaptation.representations;
187
187
  for (var _a = 0, representations_1 = representations; _a < representations_1.length; _a++) {
188
188
  var representation = representations_1[_a];
189
- var position = representation.index.getLastPosition();
189
+ var position = representation.index.getLastAvailablePosition();
190
190
  if (position !== null) {
191
191
  allIndexAreEmpty = false;
192
192
  if (typeof position === "number") {
@@ -30,12 +30,12 @@ export default function parseLocalManifest(localManifest) {
30
30
  var periodIdGenerator = idGenerator();
31
31
  var minimumPosition = localManifest.minimumPosition, maximumPosition = localManifest.maximumPosition, isFinished = localManifest.isFinished;
32
32
  var parsedPeriods = localManifest.periods
33
- .map(function (period) { return parsePeriod(period, { periodIdGenerator: periodIdGenerator, isFinished: isFinished }); });
33
+ .map(function (period) { return parsePeriod(period, { periodIdGenerator: periodIdGenerator }); });
34
34
  return { availabilityStartTime: 0,
35
35
  expired: localManifest.expired,
36
36
  transportType: "local",
37
37
  isDynamic: !isFinished,
38
- isLastPeriodKnown: isFinished,
38
+ isLastPeriodKnown: true,
39
39
  isLive: false,
40
40
  uris: [],
41
41
  timeBounds: { minimumSafePosition: minimumPosition !== null && minimumPosition !== void 0 ? minimumPosition : 0,
@@ -52,7 +52,6 @@ export default function parseLocalManifest(localManifest) {
52
52
  * @returns {Object}
53
53
  */
54
54
  function parsePeriod(period, ctxt) {
55
- var isFinished = ctxt.isFinished;
56
55
  var adaptationIdGenerator = idGenerator();
57
56
  return {
58
57
  id: "period-" + ctxt.periodIdGenerator(),
@@ -67,7 +66,7 @@ function parsePeriod(period, ctxt) {
67
66
  adaps = [];
68
67
  acc[type] = adaps;
69
68
  }
70
- adaps.push(parseAdaptation(ada, { adaptationIdGenerator: adaptationIdGenerator, isFinished: isFinished }));
69
+ adaps.push(parseAdaptation(ada, { adaptationIdGenerator: adaptationIdGenerator }));
71
70
  return acc;
72
71
  }, {}),
73
72
  };
@@ -78,7 +77,6 @@ function parsePeriod(period, ctxt) {
78
77
  * @returns {Object}
79
78
  */
80
79
  function parseAdaptation(adaptation, ctxt) {
81
- var isFinished = ctxt.isFinished;
82
80
  var representationIdGenerator = idGenerator();
83
81
  return {
84
82
  id: "adaptation-" + ctxt.adaptationIdGenerator(),
@@ -87,7 +85,7 @@ function parseAdaptation(adaptation, ctxt) {
87
85
  closedCaption: adaptation.closedCaption,
88
86
  language: adaptation.language,
89
87
  representations: adaptation.representations.map(function (representation) {
90
- return parseRepresentation(representation, { representationIdGenerator: representationIdGenerator, isFinished: isFinished });
88
+ return parseRepresentation(representation, { representationIdGenerator: representationIdGenerator });
91
89
  }),
92
90
  };
93
91
  }
@@ -96,7 +94,6 @@ function parseAdaptation(adaptation, ctxt) {
96
94
  * @returns {Object}
97
95
  */
98
96
  function parseRepresentation(representation, ctxt) {
99
- var isFinished = ctxt.isFinished;
100
97
  var id = "representation-" + ctxt.representationIdGenerator();
101
98
  var contentProtections = representation.contentProtections === undefined ?
102
99
  undefined :
@@ -106,7 +103,7 @@ function parseRepresentation(representation, ctxt) {
106
103
  width: representation.width,
107
104
  codecs: representation.codecs,
108
105
  mimeType: representation.mimeType,
109
- index: new LocalRepresentationIndex(representation.index, id, isFinished), contentProtections: contentProtections };
106
+ index: new LocalRepresentationIndex(representation.index, id), contentProtections: contentProtections };
110
107
  }
111
108
  /**
112
109
  * Translate Local Manifest's `contentProtections` attribute to the one defined
@@ -18,8 +18,7 @@ import { ILocalIndex } from "./types";
18
18
  export default class LocalRepresentationIndex implements IRepresentationIndex {
19
19
  private _index;
20
20
  private _representationId;
21
- private _isFinished;
22
- constructor(index: ILocalIndex, representationId: string, isFinished: boolean);
21
+ constructor(index: ILocalIndex, representationId: string);
23
22
  /**
24
23
  * @returns {Object}
25
24
  */
@@ -33,11 +32,29 @@ export default class LocalRepresentationIndex implements IRepresentationIndex {
33
32
  /**
34
33
  * @returns {Number|undefined}
35
34
  */
36
- getFirstPosition(): number | undefined;
35
+ getFirstAvailablePosition(): number | undefined;
37
36
  /**
38
37
  * @returns {Number|undefined}
39
38
  */
40
- getLastPosition(): number | undefined;
39
+ getLastAvailablePosition(): number | undefined;
40
+ /**
41
+ * Returns the expected ending position of this RepresentationIndex.
42
+ * `undefined` if unknown.
43
+ * @returns {number|undefined}
44
+ */
45
+ getEnd(): number | undefined;
46
+ /**
47
+ * Returns:
48
+ * - `true` if in the given time interval, at least one new segment is
49
+ * expected to be available in the future.
50
+ * - `false` either if all segments in that time interval are already
51
+ * available for download or if none will ever be available for it.
52
+ * - `undefined` when it is not possible to tell.
53
+ * @param {number} start
54
+ * @param {number} end
55
+ * @returns {boolean|undefined}
56
+ */
57
+ awaitSegmentBetween(start: number, end: number): boolean | undefined;
41
58
  /**
42
59
  * @returns {Boolean}
43
60
  */
@@ -55,10 +72,6 @@ export default class LocalRepresentationIndex implements IRepresentationIndex {
55
72
  * @returns {null}
56
73
  */
57
74
  checkDiscontinuity(): null;
58
- /**
59
- * @returns {boolean}
60
- */
61
- areSegmentsChronologicallyGenerated(): boolean;
62
75
  /**
63
76
  * @returns {Boolean}
64
77
  */