rx-player 3.28.0 → 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 (104) hide show
  1. package/.github/workflows/checks.yml +20 -18
  2. package/CHANGELOG.md +11 -0
  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/core/api/public_api.js +2 -2
  7. package/dist/_esm5.processed/core/decrypt/create_session.js +33 -4
  8. package/dist/_esm5.processed/core/decrypt/utils/loaded_sessions_store.d.ts +14 -0
  9. package/dist/_esm5.processed/core/decrypt/utils/loaded_sessions_store.js +25 -0
  10. package/dist/_esm5.processed/core/fetchers/segment/segment_fetcher.d.ts +1 -1
  11. package/dist/_esm5.processed/core/fetchers/segment/segment_fetcher.js +1 -1
  12. package/dist/_esm5.processed/core/init/content_time_boundaries_observer.js +110 -38
  13. package/dist/_esm5.processed/core/stream/representation/check_for_discontinuity.js +21 -9
  14. package/dist/_esm5.processed/core/stream/representation/get_buffer_status.js +21 -29
  15. package/dist/_esm5.processed/errors/encrypted_media_error.d.ts +1 -2
  16. package/dist/_esm5.processed/errors/encrypted_media_error.js +0 -1
  17. package/dist/_esm5.processed/errors/error_codes.d.ts +6 -1
  18. package/dist/_esm5.processed/errors/media_error.d.ts +1 -2
  19. package/dist/_esm5.processed/errors/media_error.js +0 -1
  20. package/dist/_esm5.processed/errors/network_error.d.ts +1 -2
  21. package/dist/_esm5.processed/errors/network_error.js +0 -1
  22. package/dist/_esm5.processed/errors/other_error.d.ts +1 -2
  23. package/dist/_esm5.processed/errors/other_error.js +0 -1
  24. package/dist/_esm5.processed/manifest/manifest.d.ts +1 -1
  25. package/dist/_esm5.processed/manifest/manifest.js +1 -1
  26. package/dist/_esm5.processed/manifest/representation_index/static.d.ts +21 -6
  27. package/dist/_esm5.processed/manifest/representation_index/static.js +26 -8
  28. package/dist/_esm5.processed/manifest/representation_index/types.d.ts +55 -44
  29. package/dist/_esm5.processed/parsers/manifest/dash/common/indexes/base.d.ts +21 -8
  30. package/dist/_esm5.processed/parsers/manifest/dash/common/indexes/base.js +25 -10
  31. package/dist/_esm5.processed/parsers/manifest/dash/common/indexes/list.d.ts +26 -12
  32. package/dist/_esm5.processed/parsers/manifest/dash/common/indexes/list.js +26 -13
  33. package/dist/_esm5.processed/parsers/manifest/dash/common/indexes/template.d.ts +23 -7
  34. package/dist/_esm5.processed/parsers/manifest/dash/common/indexes/template.js +65 -22
  35. package/dist/_esm5.processed/parsers/manifest/dash/common/indexes/timeline/timeline_representation_index.d.ts +20 -3
  36. package/dist/_esm5.processed/parsers/manifest/dash/common/indexes/timeline/timeline_representation_index.js +57 -7
  37. package/dist/_esm5.processed/parsers/manifest/dash/common/indexes/{is_period_fulfilled.d.ts → utils.d.ts} +3 -6
  38. package/dist/_esm5.processed/parsers/manifest/dash/common/indexes/{is_period_fulfilled.js → utils.js} +4 -8
  39. package/dist/_esm5.processed/parsers/manifest/dash/common/parse_periods.js +1 -1
  40. package/dist/_esm5.processed/parsers/manifest/local/parse_local_manifest.js +5 -8
  41. package/dist/_esm5.processed/parsers/manifest/local/representation_index.d.ts +21 -8
  42. package/dist/_esm5.processed/parsers/manifest/local/representation_index.js +49 -14
  43. package/dist/_esm5.processed/parsers/manifest/local/types.d.ts +16 -0
  44. package/dist/_esm5.processed/parsers/manifest/metaplaylist/representation_index.d.ts +20 -6
  45. package/dist/_esm5.processed/parsers/manifest/metaplaylist/representation_index.js +28 -10
  46. package/dist/_esm5.processed/parsers/manifest/smooth/create_parser.js +4 -4
  47. package/dist/_esm5.processed/parsers/manifest/smooth/representation_index.d.ts +21 -12
  48. package/dist/_esm5.processed/parsers/manifest/smooth/representation_index.js +39 -14
  49. package/dist/_esm5.processed/parsers/manifest/utils/get_first_time_from_adaptation.js +1 -1
  50. package/dist/_esm5.processed/parsers/manifest/utils/get_last_time_from_adaptation.js +1 -1
  51. package/dist/_esm5.processed/transports/metaplaylist/pipelines.js +0 -2
  52. package/dist/_esm5.processed/transports/smooth/segment_loader.js +1 -1
  53. package/dist/_esm5.processed/transports/types.d.ts +1 -1
  54. package/dist/_esm5.processed/utils/deep_merge.d.ts +1 -1
  55. package/dist/_esm5.processed/utils/deep_merge.js +6 -5
  56. package/dist/_esm5.processed/utils/task_canceller.d.ts +0 -3
  57. package/dist/_esm5.processed/utils/task_canceller.js +0 -3
  58. package/dist/mpd-parser.wasm +0 -0
  59. package/dist/rx-player.js +1382 -1058
  60. package/dist/rx-player.min.js +1 -1
  61. package/jest.config.js +5 -0
  62. package/package.json +31 -30
  63. package/sonar-project.properties +1 -1
  64. package/src/compat/eme/load_session.ts +5 -6
  65. package/src/core/api/public_api.ts +2 -2
  66. package/src/core/decrypt/create_session.ts +28 -2
  67. package/src/core/decrypt/utils/loaded_sessions_store.ts +29 -0
  68. package/src/core/fetchers/segment/segment_fetcher.ts +1 -1
  69. package/src/core/init/content_time_boundaries_observer.ts +116 -42
  70. package/src/core/stream/representation/check_for_discontinuity.ts +28 -10
  71. package/src/core/stream/representation/get_buffer_status.ts +27 -34
  72. package/src/errors/encrypted_media_error.ts +1 -2
  73. package/src/errors/error_codes.ts +2 -2
  74. package/src/errors/media_error.ts +1 -2
  75. package/src/errors/network_error.ts +1 -2
  76. package/src/errors/other_error.ts +1 -2
  77. package/src/manifest/__tests__/adaptation.test.ts +4 -3
  78. package/src/manifest/__tests__/representation.test.ts +4 -3
  79. package/src/manifest/manifest.ts +1 -1
  80. package/src/manifest/representation_index/__tests__/static.test.ts +5 -4
  81. package/src/manifest/representation_index/static.ts +28 -9
  82. package/src/manifest/representation_index/types.ts +62 -46
  83. package/src/parsers/manifest/dash/common/indexes/base.ts +27 -11
  84. package/src/parsers/manifest/dash/common/indexes/list.ts +32 -15
  85. package/src/parsers/manifest/dash/common/indexes/template.ts +73 -27
  86. package/src/parsers/manifest/dash/common/indexes/timeline/timeline_representation_index.ts +60 -8
  87. package/src/parsers/manifest/dash/common/indexes/{is_period_fulfilled.ts → utils.ts} +4 -13
  88. package/src/parsers/manifest/dash/common/parse_periods.ts +1 -1
  89. package/src/parsers/manifest/local/parse_local_manifest.ts +8 -20
  90. package/src/parsers/manifest/local/representation_index.ts +51 -16
  91. package/src/parsers/manifest/local/types.ts +13 -0
  92. package/src/parsers/manifest/metaplaylist/representation_index.ts +31 -11
  93. package/src/parsers/manifest/smooth/create_parser.ts +4 -4
  94. package/src/parsers/manifest/smooth/representation_index.ts +40 -15
  95. package/src/parsers/manifest/utils/__tests__/get_first_time_from_adaptations.test.ts +4 -3
  96. package/src/parsers/manifest/utils/__tests__/get_last_time_from_adaptation.test.ts +4 -3
  97. package/src/parsers/manifest/utils/get_first_time_from_adaptation.ts +1 -1
  98. package/src/parsers/manifest/utils/get_last_time_from_adaptation.ts +1 -1
  99. package/src/transports/metaplaylist/pipelines.ts +0 -2
  100. package/src/transports/smooth/segment_loader.ts +1 -1
  101. package/src/transports/types.ts +1 -1
  102. package/src/utils/__tests__/initialization_segment_cache.test.ts +7 -0
  103. package/src/utils/deep_merge.ts +7 -4
  104. package/src/utils/task_canceller.ts +0 -3
@@ -28,7 +28,7 @@ import errorMessage from "./error_message";
28
28
  */
29
29
  export default class EncryptedMediaError extends Error {
30
30
  public readonly name : "EncryptedMediaError";
31
- public readonly type : string;
31
+ public readonly type : "ENCRYPTED_MEDIA_ERROR";
32
32
  public readonly code : IEncryptedMediaErrorCode;
33
33
  public message : string;
34
34
  public fatal : boolean;
@@ -36,7 +36,6 @@ export default class EncryptedMediaError extends Error {
36
36
  /**
37
37
  * @param {string} code
38
38
  * @param {string} reason
39
- * @Param {Boolean} fatal
40
39
  */
41
40
  constructor(code : IEncryptedMediaErrorCode, reason : string) {
42
41
  super();
@@ -74,12 +74,12 @@ export type INetworkErrorType = "TIMEOUT" |
74
74
  "PARSE_ERROR" |
75
75
  "ERROR_HTTP_CODE";
76
76
 
77
- const ErrorTypes : Record<IErrorType, IErrorType> = {
77
+ const ErrorTypes = {
78
78
  NETWORK_ERROR: "NETWORK_ERROR",
79
79
  MEDIA_ERROR: "MEDIA_ERROR",
80
80
  ENCRYPTED_MEDIA_ERROR: "ENCRYPTED_MEDIA_ERROR",
81
81
  OTHER_ERROR: "OTHER_ERROR",
82
- };
82
+ } as const;
83
83
 
84
84
  const NetworkErrorTypes : Record<INetworkErrorType, INetworkErrorType> = {
85
85
  TIMEOUT: "TIMEOUT",
@@ -28,7 +28,7 @@ import errorMessage from "./error_message";
28
28
  */
29
29
  export default class MediaError extends Error {
30
30
  public readonly name : "MediaError";
31
- public readonly type : string;
31
+ public readonly type : "MEDIA_ERROR";
32
32
  public readonly message : string;
33
33
  public readonly code : IMediaErrorCode;
34
34
  public fatal : boolean;
@@ -36,7 +36,6 @@ export default class MediaError extends Error {
36
36
  /**
37
37
  * @param {string} code
38
38
  * @param {string} reason
39
- * @param {Boolean} fatal
40
39
  */
41
40
  constructor(code : IMediaErrorCode, reason : string) {
42
41
  super();
@@ -31,7 +31,7 @@ import RequestError from "./request_error";
31
31
  */
32
32
  export default class NetworkError extends Error {
33
33
  public readonly name : "NetworkError";
34
- public readonly type : string;
34
+ public readonly type : "NETWORK_ERROR";
35
35
  public readonly message : string;
36
36
  public readonly code : INetworkErrorCode;
37
37
  public readonly xhr : XMLHttpRequest | null;
@@ -43,7 +43,6 @@ export default class NetworkError extends Error {
43
43
  /**
44
44
  * @param {string} code
45
45
  * @param {Error} baseError
46
- * @param {Boolean} fatal
47
46
  */
48
47
  constructor(code : INetworkErrorCode, baseError : RequestError) {
49
48
  super();
@@ -26,7 +26,7 @@ import errorMessage from "./error_message";
26
26
  */
27
27
  export default class OtherError extends Error {
28
28
  public readonly name : "OtherError";
29
- public readonly type : string;
29
+ public readonly type : "OTHER_ERROR";
30
30
  public readonly message : string;
31
31
  public readonly code : IOtherErrorCode;
32
32
  public fatal : boolean;
@@ -34,7 +34,6 @@ export default class OtherError extends Error {
34
34
  /**
35
35
  * @param {string} code
36
36
  * @param {string} reason
37
- * @param {Boolean} fatal
38
37
  */
39
38
  constructor(code : IOtherErrorCode, reason : string) {
40
39
  super();
@@ -28,13 +28,14 @@ const minimalRepresentationIndex = {
28
28
  getInitSegment() { return null; },
29
29
  getSegments() { return []; },
30
30
  shouldRefresh() { return false; },
31
- getFirstPosition() : undefined { return ; },
32
- getLastPosition() : undefined { return ; },
31
+ getFirstAvailablePosition() : undefined { return ; },
32
+ getLastAvailablePosition() : undefined { return ; },
33
+ getEnd() : undefined { return ; },
33
34
  checkDiscontinuity() { return null; },
34
35
  isSegmentStillAvailable() : undefined { return ; },
35
36
  canBeOutOfSyncError() : true { return true; },
36
37
  isFinished() : true { return true; },
37
- areSegmentsChronologicallyGenerated() { return true; },
38
+ awaitSegmentBetween() : undefined { return ; },
38
39
  _replace() { /* noop */ },
39
40
  _update() { /* noop */ },
40
41
  };
@@ -24,10 +24,11 @@
24
24
  const minimalIndex = { getInitSegment() { return null; },
25
25
  getSegments() { return []; },
26
26
  shouldRefresh() { return false; },
27
- getFirstPosition() : undefined { return ; },
28
- getLastPosition() : undefined { return ; },
27
+ getFirstAvailablePosition() : undefined { return ; },
28
+ getLastAvailablePosition() : undefined { return ; },
29
+ getEnd() : undefined { return ; },
30
+ awaitSegmentBetween() : undefined { return ; },
29
31
  checkDiscontinuity() { return null; },
30
- areSegmentsChronologicallyGenerated() { return true; },
31
32
  isSegmentStillAvailable() : undefined { return ; },
32
33
  canBeOutOfSyncError() : true { return true; },
33
34
  isFinished() : true { return true; },
@@ -537,7 +537,7 @@ export default class Manifest extends EventEmitter<IManifestEvents> {
537
537
  * and mark them as being impossible to decrypt.
538
538
  * Then trigger a "decipherabilityUpdate" event to notify everyone of the
539
539
  * changes performed.
540
- * @param {Object} keyUpdates
540
+ * @param {Function} isDecipherableCb
541
541
  */
542
542
  public updateRepresentationsDeciperability(
543
543
  isDecipherableCb : (rep : Representation) => boolean | undefined
@@ -33,17 +33,18 @@ describe("manifest - StaticRepresentationIndex", () => {
33
33
  duration: Number.MAX_VALUE,
34
34
  end: Number.MAX_VALUE,
35
35
  timescale: 1,
36
+ privateInfos: {},
36
37
  mediaURLs: ["foo"] }]);
37
38
  });
38
39
 
39
40
  it("should return no first position", () => {
40
41
  const staticRI = new StaticRepresentationIndex({ media: "foo" });
41
- expect(staticRI.getFirstPosition()).toBe(undefined);
42
+ expect(staticRI.getFirstAvailablePosition()).toBe(undefined);
42
43
  });
43
44
 
44
45
  it("should return no last position", () => {
45
46
  const staticRI = new StaticRepresentationIndex({ media: "foo" });
46
- expect(staticRI.getLastPosition()).toBe(undefined);
47
+ expect(staticRI.getLastAvailablePosition()).toBe(undefined);
47
48
  });
48
49
 
49
50
  it("should never be refreshed", () => {
@@ -56,9 +57,9 @@ describe("manifest - StaticRepresentationIndex", () => {
56
57
  expect(staticRI.checkDiscontinuity()).toBe(null);
57
58
  });
58
59
 
59
- it("should always be chronologically generated", () => {
60
+ it("should never be awaiting segments", () => {
60
61
  const staticRI = new StaticRepresentationIndex({ media: "foo" });
61
- expect(staticRI.areSegmentsChronologicallyGenerated()).toBe(true);
62
+ expect(staticRI.awaitSegmentBetween()).toBe(false);
62
63
  });
63
64
 
64
65
  it("should never replace and warn when trying to do so", () => {
@@ -59,6 +59,7 @@ export default class StaticRepresentationIndex implements IRepresentationIndex {
59
59
  end: Number.MAX_VALUE,
60
60
  duration: Number.MAX_VALUE,
61
61
  complete: true,
62
+ privateInfos: {},
62
63
  timescale: 1 }];
63
64
  }
64
65
 
@@ -66,7 +67,7 @@ export default class StaticRepresentationIndex implements IRepresentationIndex {
66
67
  * Returns first position in index.
67
68
  * @returns {undefined}
68
69
  */
69
- getFirstPosition() : undefined {
70
+ getFirstAvailablePosition() : undefined {
70
71
  return ;
71
72
  }
72
73
 
@@ -74,10 +75,35 @@ export default class StaticRepresentationIndex implements IRepresentationIndex {
74
75
  * Returns last position in index.
75
76
  * @returns {undefined}
76
77
  */
77
- getLastPosition() : undefined {
78
+ getLastAvailablePosition() : undefined {
78
79
  return ;
79
80
  }
80
81
 
82
+ /**
83
+ * Returns the absolute end in seconds this RepresentationIndex can reach once
84
+ * all segments are available.
85
+ * @returns {number|null|undefined}
86
+ */
87
+ getEnd() : undefined {
88
+ return;
89
+ }
90
+
91
+ /**
92
+ * Returns:
93
+ * - `true` if in the given time interval, at least one new segment is
94
+ * expected to be available in the future.
95
+ * - `false` either if all segments in that time interval are already
96
+ * available for download or if none will ever be available for it.
97
+ * - `undefined` when it is not possible to tell.
98
+ *
99
+ * Always `false` in a `StaticRepresentationIndex` because all segments should
100
+ * be directly available.
101
+ * @returns {boolean}
102
+ */
103
+ awaitSegmentBetween(): false {
104
+ return false;
105
+ }
106
+
81
107
  /**
82
108
  * Returns false as a static file never need to be refreshed.
83
109
  * @returns {Boolean}
@@ -93,13 +119,6 @@ export default class StaticRepresentationIndex implements IRepresentationIndex {
93
119
  return null;
94
120
  }
95
121
 
96
- /**
97
- * @returns {boolean}
98
- */
99
- areSegmentsChronologicallyGenerated() : boolean {
100
- return true;
101
- }
102
-
103
122
  /**
104
123
  * Returns true as a static file should never need lose availability.
105
124
  * @returns {Boolean}
@@ -112,11 +112,28 @@ export interface ILocalManifestSegmentPrivateInfos {
112
112
  * exploited by the corresponding transport logic.
113
113
  */
114
114
  export interface IPrivateInfos {
115
+ /** Smooth-specific information allowing to generate an initialization segment. */
115
116
  smoothInitSegment? : ISmoothInitSegmentPrivateInfos | undefined;
117
+
118
+ /** Smooth-specific information linked to all Smooth media segments. */
116
119
  smoothMediaSegment? : ISmoothSegmentPrivateInfos | undefined;
120
+
121
+ /** Information that should be present on all MetaPlaylist segments. */
117
122
  metaplaylistInfos? : IMetaPlaylistPrivateInfos | undefined;
123
+
124
+ /**
125
+ * Local Manifest-specific information allowing to request the
126
+ * initialization segment.
127
+ */
118
128
  localManifestInitSegment? : ILocalManifestInitSegmentPrivateInfos | undefined;
129
+
130
+ /** Local Manifest-specific information allowing to request any media segment. */
119
131
  localManifestSegment? : ILocalManifestSegmentPrivateInfos | undefined;
132
+
133
+ /**
134
+ * Function allowing to know if a given emsg's event name has been
135
+ * explicitely authorized.
136
+ */
120
137
  isEMSGWhitelisted? : ((evt: IEMSG) => boolean) | undefined;
121
138
  }
122
139
 
@@ -142,26 +159,16 @@ export interface ISegment {
142
159
  * segments" in the code.
143
160
  */
144
161
  isInit : boolean;
145
- /** URLs where this segment is available. From the most to least prioritary. */
146
- mediaURLs : string[]|null;
147
- /**
148
- * If set, the corresponding byte-range in the downloaded segment will
149
- * contain an index describing other Segments
150
- * TODO put in privateInfos?
151
- */
152
- indexRange? : [number, number] | undefined;
153
162
  /**
154
- * Optional number of the Segment
155
- * TODO put in privateInfos?
163
+ * URLs where this segment is available. From the most to least prioritary.
164
+ * `null` if no URL exists.
156
165
  */
157
- number? : number | undefined;
166
+ mediaURLs : string[]|null;
158
167
  /**
159
168
  * Allows to store supplementary information on a segment that can be later
160
169
  * exploited by the transport logic.
161
170
  */
162
- privateInfos? : IPrivateInfos | undefined;
163
- /** Optional byte range to retrieve the Segment from its URL(s) */
164
- range? : [number, number] | undefined;
171
+ privateInfos : IPrivateInfos | undefined;
165
172
  /**
166
173
  * Estimated time, in seconds, at which the concerned segment should be
167
174
  * offseted when decoded.
@@ -213,7 +220,6 @@ export interface ISegment {
213
220
  * As both are always in seconds now, this property became unneeded.
214
221
  */
215
222
  timescale : 1;
216
-
217
223
  /**
218
224
  * If `false`, this segment's `duration` property may not be the duration of
219
225
  * the full segment as it could still be in the process of being generated
@@ -224,6 +230,31 @@ export interface ISegment {
224
230
  * generated.
225
231
  */
226
232
  complete : boolean;
233
+ /**
234
+ * Optional byte range to retrieve the Segment from its URL(s).
235
+ * TODO this should probably moved to `privateInfos` as this is
236
+ * transport-specific metadata only useful for performing requests, but it is
237
+ * sadly documented in the API, so moving it is actuall a v3.X.X breaking
238
+ * change.
239
+ */
240
+ range? : [number, number] | undefined;
241
+ /**
242
+ * If set, the corresponding byte-range in the downloaded segment will
243
+ * contain an index describing other Segments
244
+ * TODO this should probably moved to `privateInfos` as this is
245
+ * transport-specific metadata only useful for performing requests, but it is
246
+ * sadly documented in the API, so moving it is actuall a v3.X.X breaking
247
+ * change.
248
+ */
249
+ indexRange? : [number, number] | undefined;
250
+ /**
251
+ * Optional number of the Segment.
252
+ * TODO this should probably moved to `privateInfos` as this is
253
+ * transport-specific metadata only useful for performing requests, but it is
254
+ * sadly documented in the API, so moving it is actuall a v3.X.X breaking
255
+ * change.
256
+ */
257
+ number? : number | undefined;
227
258
  }
228
259
 
229
260
  /** Interface that should be implemented by any Representation's `index` value. */
@@ -265,7 +296,7 @@ export interface IRepresentationIndex {
265
296
  * Returns `undefined` if we cannot know this value.
266
297
  * @returns {Number|null}
267
298
  */
268
- getFirstPosition() : number | null | undefined;
299
+ getFirstAvailablePosition() : number | null | undefined;
269
300
 
270
301
  /**
271
302
  * Returns the ending time, in seconds, of the last playable position
@@ -279,7 +310,19 @@ export interface IRepresentationIndex {
279
310
  * instead.
280
311
  * @returns {Number|null|undefined}
281
312
  */
282
- getLastPosition() : number | null | undefined;
313
+ getLastAvailablePosition() : number | null | undefined;
314
+
315
+ /**
316
+ * Returns the ending time, in seconds, of the Representation once it is
317
+ * "finished" (@see isFinished).
318
+ * Should thus be equivalent to `getLastAvailablePosition` once finished.
319
+ *
320
+ * Returns `null` if nothing is in the index
321
+ * Returns `undefined` if we cannot know this value.
322
+ *
323
+ * @returns {number|undefined}
324
+ */
325
+ getEnd() : number | null | undefined;
283
326
 
284
327
  /**
285
328
  * Returns `true` if a Segment returned by this index is still considered
@@ -322,35 +365,6 @@ export interface IRepresentationIndex {
322
365
  */
323
366
  checkDiscontinuity(time : number) : number | null;
324
367
 
325
- /**
326
- * Most RepresentationIndex are linked to segments which are generated in
327
- * chronological order: from an initial position (obtainable with
328
- * `getFirstPosition`) to the last position of the corresponding Period
329
- * (obtainable with `getLastPosition`).
330
- *
331
- * However, some RepresentationIndex could announce segments in a more random
332
- * order.
333
- * Examples of such RepresentationIndex are ones for contents which are being
334
- * downloaded locally. Here a seek close to the end could schedule the
335
- * download of the last segments immediately, which might thus be announced
336
- * in this index before segments in the middle are.
337
- *
338
- * Knowing this value serves for example to check if a discontinuity
339
- * encountered in the content can be skipped over, or if it's possible that
340
- * this discontinuity is due to a segment not yet being generated.
341
- *
342
- * You should return `true` only if there is a chance that segments are not
343
- * chronologically generated (even if they all have since been generated, this
344
- * function is only to know if it's possible, not if it's the case now).
345
- *
346
- * In other most likely cases, you should return `false`.
347
- *
348
- * TODO find a better way with the "local" RepresentationIndex, like
349
- * explicitely declaring which segments have not been downloaded yet.
350
- * @returns {boolean}
351
- */
352
- areSegmentsChronologicallyGenerated() : boolean;
353
-
354
368
  /**
355
369
  * Returns `true` if the last segments in this index have already been
356
370
  * generated so that we can freely go to the next period.
@@ -382,6 +396,8 @@ export interface IRepresentationIndex {
382
396
  */
383
397
  isInitialized() : boolean;
384
398
 
399
+ awaitSegmentBetween(start : number, end : number) : boolean | undefined;
400
+
385
401
  /**
386
402
  * Replace the index with another one, such as after a Manifest update.
387
403
  * @param {Object} newIndex
@@ -274,7 +274,7 @@ export default class BaseRepresentationIndex implements IRepresentationIndex {
274
274
  * Returns first position in index.
275
275
  * @returns {Number|null}
276
276
  */
277
- getFirstPosition() : number|null {
277
+ getFirstAvailablePosition() : number|null {
278
278
  const index = this._index;
279
279
  if (index.timeline.length === 0) {
280
280
  return null;
@@ -288,7 +288,7 @@ export default class BaseRepresentationIndex implements IRepresentationIndex {
288
288
  * Returns last position in index.
289
289
  * @returns {Number|null}
290
290
  */
291
- getLastPosition() : number|null {
291
+ getLastAvailablePosition() : number|null {
292
292
  const { timeline } = this._index;
293
293
  if (timeline.length === 0) {
294
294
  return null;
@@ -301,6 +301,31 @@ export default class BaseRepresentationIndex implements IRepresentationIndex {
301
301
  return fromIndexTime(lastTime, this._index);
302
302
  }
303
303
 
304
+ /**
305
+ * Returns the absolute end in seconds this RepresentationIndex can reach once
306
+ * all segments are available.
307
+ * @returns {number|null|undefined}
308
+ */
309
+ getEnd(): number | null {
310
+ return this.getLastAvailablePosition();
311
+ }
312
+
313
+ /**
314
+ * Returns:
315
+ * - `true` if in the given time interval, at least one new segment is
316
+ * expected to be available in the future.
317
+ * - `false` either if all segments in that time interval are already
318
+ * available for download or if none will ever be available for it.
319
+ * - `undefined` when it is not possible to tell.
320
+ *
321
+ * Always `false` in a `BaseRepresentationIndex` because all segments should
322
+ * be directly available.
323
+ * @returns {boolean}
324
+ */
325
+ awaitSegmentBetween(): false {
326
+ return false;
327
+ }
328
+
304
329
  /**
305
330
  * Segments in a segmentBase scheme should stay available.
306
331
  * @returns {Boolean|undefined}
@@ -317,15 +342,6 @@ export default class BaseRepresentationIndex implements IRepresentationIndex {
317
342
  return null;
318
343
  }
319
344
 
320
- /**
321
- * `BaseRepresentationIndex` should just already all be generated.
322
- * Return `true` as a default value here.
323
- * @returns {boolean}
324
- */
325
- areSegmentsChronologicallyGenerated() : true {
326
- return true;
327
- }
328
-
329
345
  /**
330
346
  * No segment in a `BaseRepresentationIndex` are known initially.
331
347
  * It is only defined generally in an "index segment" that will thus need to
@@ -60,7 +60,10 @@ export interface IListIndex {
60
60
  } | undefined;
61
61
  /** Information on the list of segments for this index. */
62
62
  list: Array<{
63
- /** URLs of the segment. */
63
+ /**
64
+ * URLs of the segment.
65
+ * `null` if no URL exists.
66
+ */
64
67
  mediaURLs : string[] | null;
65
68
  /** Possible byte-range of the segment. */
66
69
  mediaRange? : [number, number] | undefined;
@@ -188,7 +191,7 @@ export default class ListRepresentationIndex implements IRepresentationIndex {
188
191
 
189
192
  /**
190
193
  * @param {Number} fromTime
191
- * @param {Number} duration
194
+ * @param {Number} dur
192
195
  * @returns {Array.<Object>}
193
196
  */
194
197
  getSegments(fromTime : number, dur : number) : ISegment[] {
@@ -243,7 +246,7 @@ export default class ListRepresentationIndex implements IRepresentationIndex {
243
246
  * Returns first position in this index, in seconds.
244
247
  * @returns {Number}
245
248
  */
246
- getFirstPosition() : number {
249
+ getFirstAvailablePosition() : number {
247
250
  return this._periodStart;
248
251
  }
249
252
 
@@ -251,17 +254,41 @@ export default class ListRepresentationIndex implements IRepresentationIndex {
251
254
  * Returns last position in this index, in seconds.
252
255
  * @returns {Number}
253
256
  */
254
- getLastPosition() : number {
257
+ getLastAvailablePosition() : number {
255
258
  const index = this._index;
256
259
  const { duration, list } = index;
257
260
  return Math.min(((list.length * duration) / index.timescale) + this._periodStart,
258
261
  this._periodEnd ?? Infinity);
259
262
  }
260
263
 
264
+ /**
265
+ * Returns the absolute end in seconds this RepresentationIndex can reach once
266
+ * all segments are available.
267
+ * @returns {number|null|undefined}
268
+ */
269
+ getEnd(): number | null {
270
+ return this.getLastAvailablePosition();
271
+ }
272
+
273
+ /**
274
+ * Returns:
275
+ * - `true` if in the given time interval, at least one new segment is
276
+ * expected to be available in the future.
277
+ * - `false` either if all segments in that time interval are already
278
+ * available for download or if none will ever be available for it.
279
+ * - `undefined` when it is not possible to tell.
280
+ *
281
+ * Always `false` in a `ListRepresentationIndex` because all segments should
282
+ * be directly available.
283
+ * @returns {boolean}
284
+ */
285
+ awaitSegmentBetween(): false {
286
+ return false;
287
+ }
288
+
261
289
  /**
262
290
  * Returns true if a Segment returned by this index is still considered
263
291
  * available.
264
- * @param {Object} segment
265
292
  * @returns {Boolean}
266
293
  */
267
294
  isSegmentStillAvailable() : true {
@@ -277,13 +304,6 @@ export default class ListRepresentationIndex implements IRepresentationIndex {
277
304
  return null;
278
305
  }
279
306
 
280
- /**
281
- * @returns {boolean}
282
- */
283
- areSegmentsChronologicallyGenerated() : boolean {
284
- return true;
285
- }
286
-
287
307
  /**
288
308
  * SegmentList should not be updated.
289
309
  * @returns {Boolean}
@@ -313,9 +333,6 @@ export default class ListRepresentationIndex implements IRepresentationIndex {
313
333
  this._index = newIndex._index;
314
334
  }
315
335
 
316
- /**
317
- * @param {Object} newIndex
318
- */
319
336
  _update() : void {
320
337
  log.error("List RepresentationIndex: Cannot update a SegmentList");
321
338
  }