rx-player 3.27.0 → 3.27.1-dev.2022041500
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.
- package/CHANGELOG.md +12 -0
- package/VERSION +1 -1
- package/dist/_esm5.processed/core/abr/abr_manager.d.ts +7 -3
- package/dist/_esm5.processed/core/abr/abr_manager.js +4 -3
- package/dist/_esm5.processed/core/abr/representation_estimator.d.ts +13 -8
- package/dist/_esm5.processed/core/abr/representation_estimator.js +4 -4
- package/dist/_esm5.processed/core/api/public_api.js +13 -14
- package/dist/_esm5.processed/core/init/content_time_boundaries_observer.d.ts +52 -0
- package/dist/_esm5.processed/core/init/content_time_boundaries_observer.js +223 -0
- package/dist/_esm5.processed/core/init/create_stream_playback_observer.js +17 -6
- package/dist/_esm5.processed/core/init/get_initial_time.d.ts +30 -5
- package/dist/_esm5.processed/core/init/get_initial_time.js +20 -13
- package/dist/_esm5.processed/core/init/load_on_media_source.js +16 -3
- package/dist/_esm5.processed/core/init/media_duration_updater.d.ts +56 -0
- package/dist/_esm5.processed/core/init/media_duration_updater.js +188 -0
- package/dist/_esm5.processed/core/stream/adaptation/adaptation_stream.d.ts +2 -5
- package/dist/_esm5.processed/core/stream/adaptation/create_representation_estimator.d.ts +3 -2
- package/dist/_esm5.processed/core/stream/adaptation/create_representation_estimator.js +3 -3
- package/dist/_esm5.processed/core/stream/orchestrator/stream_orchestrator.js +2 -22
- package/dist/_esm5.processed/core/stream/period/period_stream.d.ts +2 -5
- package/dist/_esm5.processed/core/stream/period/period_stream.js +6 -4
- package/dist/_esm5.processed/core/stream/representation/get_buffer_status.d.ts +4 -2
- package/dist/_esm5.processed/core/stream/representation/get_buffer_status.js +85 -42
- package/dist/_esm5.processed/core/stream/representation/representation_stream.js +3 -3
- package/dist/_esm5.processed/manifest/manifest.d.ts +14 -5
- package/dist/_esm5.processed/manifest/manifest.js +31 -12
- package/dist/_esm5.processed/manifest/period.d.ts +6 -0
- package/dist/_esm5.processed/manifest/period.js +9 -0
- package/dist/_esm5.processed/manifest/representation_index/types.d.ts +7 -2
- package/dist/_esm5.processed/parsers/manifest/dash/common/get_minimum_and_maximum_positions.d.ts +5 -1
- package/dist/_esm5.processed/parsers/manifest/dash/common/get_minimum_and_maximum_positions.js +6 -4
- package/dist/_esm5.processed/parsers/manifest/dash/common/indexes/base.js +2 -1
- package/dist/_esm5.processed/parsers/manifest/dash/common/indexes/list.d.ts +4 -0
- package/dist/_esm5.processed/parsers/manifest/dash/common/indexes/list.js +4 -2
- package/dist/_esm5.processed/parsers/manifest/dash/common/indexes/template.js +4 -2
- package/dist/_esm5.processed/parsers/manifest/dash/common/indexes/timeline/timeline_representation_index.js +3 -3
- package/dist/_esm5.processed/parsers/manifest/dash/common/parse_mpd.js +28 -20
- package/dist/_esm5.processed/parsers/manifest/local/parse_local_manifest.js +3 -2
- package/dist/_esm5.processed/parsers/manifest/metaplaylist/metaplaylist_parser.js +9 -3
- package/dist/_esm5.processed/parsers/manifest/smooth/create_parser.js +26 -17
- package/dist/_esm5.processed/parsers/manifest/types.d.ts +45 -17
- package/dist/_esm5.processed/parsers/manifest/utils/{get_maximum_position.d.ts → get_maximum_positions.d.ts} +4 -1
- package/dist/_esm5.processed/parsers/manifest/utils/{get_maximum_position.js → get_maximum_positions.js} +10 -6
- package/dist/rx-player.js +854 -351
- package/dist/rx-player.min.js +1 -1
- package/package.json +1 -1
- package/sonar-project.properties +1 -1
- package/src/core/abr/abr_manager.ts +11 -3
- package/src/core/abr/representation_estimator.ts +17 -10
- package/src/core/api/public_api.ts +13 -15
- package/src/core/init/content_time_boundaries_observer.ts +312 -0
- package/src/core/init/create_stream_playback_observer.ts +18 -7
- package/src/core/init/get_initial_time.ts +52 -19
- package/src/core/init/load_on_media_source.ts +22 -5
- package/src/core/init/media_duration_updater.ts +268 -0
- package/src/core/stream/adaptation/adaptation_stream.ts +2 -5
- package/src/core/stream/adaptation/create_representation_estimator.ts +6 -3
- package/src/core/stream/orchestrator/stream_orchestrator.ts +2 -29
- package/src/core/stream/period/period_stream.ts +12 -9
- package/src/core/stream/representation/get_buffer_status.ts +102 -41
- package/src/core/stream/representation/representation_stream.ts +3 -3
- package/src/manifest/__tests__/manifest.test.ts +94 -68
- package/src/manifest/manifest.ts +73 -33
- package/src/manifest/period.ts +10 -0
- package/src/manifest/representation_index/types.ts +7 -2
- package/src/parsers/manifest/dash/common/get_minimum_and_maximum_positions.ts +10 -5
- package/src/parsers/manifest/dash/common/indexes/base.ts +4 -3
- package/src/parsers/manifest/dash/common/indexes/list.ts +8 -1
- package/src/parsers/manifest/dash/common/indexes/template.ts +8 -3
- package/src/parsers/manifest/dash/common/indexes/timeline/timeline_representation_index.ts +7 -7
- package/src/parsers/manifest/dash/common/parse_mpd.ts +38 -20
- package/src/parsers/manifest/local/parse_local_manifest.ts +3 -2
- package/src/parsers/manifest/metaplaylist/metaplaylist_parser.ts +9 -4
- package/src/parsers/manifest/smooth/create_parser.ts +36 -18
- package/src/parsers/manifest/types.ts +45 -17
- package/src/parsers/manifest/utils/{get_maximum_position.ts → get_maximum_positions.ts} +13 -7
- package/dist/_esm5.processed/core/init/duration_updater.d.ts +0 -27
- package/dist/_esm5.processed/core/init/duration_updater.js +0 -136
- package/src/core/init/duration_updater.ts +0 -206
package/src/manifest/manifest.ts
CHANGED
|
@@ -248,10 +248,13 @@ export default class Manifest extends EventEmitter<IManifestEvents> {
|
|
|
248
248
|
*/
|
|
249
249
|
private _timeBounds : {
|
|
250
250
|
/**
|
|
251
|
-
*
|
|
252
|
-
*
|
|
251
|
+
* This is the theoretical minimum playable position on the content
|
|
252
|
+
* regardless of the current Adaptation chosen, as estimated at parsing
|
|
253
|
+
* time.
|
|
254
|
+
* `undefined` if unknown.
|
|
253
255
|
*
|
|
254
|
-
* `
|
|
256
|
+
* More technically, the `minimumSafePosition` is the maximum between all
|
|
257
|
+
* the minimum positions reachable in any of the audio and video Adaptation.
|
|
255
258
|
*
|
|
256
259
|
* Together with `timeshiftDepth` and the `maximumTimeData` object, this
|
|
257
260
|
* value allows to compute at any time the minimum seekable time:
|
|
@@ -259,17 +262,17 @@ export default class Manifest extends EventEmitter<IManifestEvents> {
|
|
|
259
262
|
* - if `timeshiftDepth` is not set, the minimum seekable time is a
|
|
260
263
|
* constant that corresponds to this value.
|
|
261
264
|
*
|
|
262
|
-
* - if `timeshiftDepth` is set, `
|
|
265
|
+
* - if `timeshiftDepth` is set, `minimumSafePosition` will act as the
|
|
263
266
|
* absolute minimum seekable time we can never seek below, even when
|
|
264
267
|
* `timeshiftDepth` indicates a possible lower position.
|
|
265
268
|
* This becomes useful for example when playing live contents which -
|
|
266
269
|
* despite having a large window depth - just begun and as such only
|
|
267
270
|
* have a few segment available for now.
|
|
268
|
-
* Here, `
|
|
271
|
+
* Here, `minimumSafePosition` would be the start time of the initial
|
|
269
272
|
* segment, and `timeshiftDepth` would be the whole depth that will
|
|
270
273
|
* become available once enough segments have been generated.
|
|
271
274
|
*/
|
|
272
|
-
|
|
275
|
+
minimumSafePosition? : number | undefined;
|
|
273
276
|
/**
|
|
274
277
|
* Some dynamic contents have the concept of a "window depth" (or "buffer
|
|
275
278
|
* depth") which allows to set a minimum position for all reachable
|
|
@@ -287,29 +290,46 @@ export default class Manifest extends EventEmitter<IManifestEvents> {
|
|
|
287
290
|
* If set to `null`, this content has no concept of a "window depth".
|
|
288
291
|
*/
|
|
289
292
|
timeshiftDepth : number | null;
|
|
290
|
-
/** Data allowing to calculate the maximum position at any given time. */
|
|
293
|
+
/** Data allowing to calculate the maximum playable position at any given time. */
|
|
291
294
|
maximumTimeData : {
|
|
292
|
-
/** Maximum seekable time in milliseconds calculated at `time`. */
|
|
293
|
-
value : number;
|
|
294
295
|
/**
|
|
295
|
-
*
|
|
296
|
-
*
|
|
297
|
-
*
|
|
296
|
+
* Current position representing live content.
|
|
297
|
+
* Only makes sense for un-ended live contents.
|
|
298
|
+
*
|
|
299
|
+
* `undefined` if unknown or if it doesn't make sense in the current context.
|
|
298
300
|
*/
|
|
299
|
-
|
|
301
|
+
livePosition : number | undefined;
|
|
300
302
|
/**
|
|
301
|
-
* Whether the maximum
|
|
302
|
-
*
|
|
303
|
-
* If set to `false`, `value` indicates the constant maximum position.
|
|
303
|
+
* Whether the maximum positions should evolve linearly over time.
|
|
304
304
|
*
|
|
305
|
-
* If set to `true`, the maximum seekable
|
|
305
|
+
* If set to `true`, the maximum seekable position continuously increase at
|
|
306
306
|
* the same rate than the time since `time` does.
|
|
307
|
-
* For example, a `value` of 50000 (50 seconds) will indicate a maximum time
|
|
308
|
-
* of 51 seconds after 1 second have passed, of 56 seconds after 6 seconds
|
|
309
|
-
* have passed (we know how many seconds have passed since the initial
|
|
310
|
-
* calculation of value by checking the `time` property) etc.
|
|
311
307
|
*/
|
|
312
308
|
isLinear: boolean;
|
|
309
|
+
/**
|
|
310
|
+
* This is the theoretical maximum playable position on the content,
|
|
311
|
+
* regardless of the current Adaptation chosen, as estimated at parsing
|
|
312
|
+
* time.
|
|
313
|
+
*
|
|
314
|
+
* More technically, the `maximumSafePosition` is the minimum between all
|
|
315
|
+
* attributes indicating the duration of the content in the Manifest.
|
|
316
|
+
*
|
|
317
|
+
* That is the minimum between:
|
|
318
|
+
* - The Manifest original attributes relative to its duration
|
|
319
|
+
* - The minimum between all known maximum audio positions
|
|
320
|
+
* - The minimum between all known maximum video positions
|
|
321
|
+
*
|
|
322
|
+
* This can for example be understood as the safe maximum playable
|
|
323
|
+
* position through all possible tacks.
|
|
324
|
+
*/
|
|
325
|
+
maximumSafePosition : number;
|
|
326
|
+
/**
|
|
327
|
+
* `Performance.now()` output at the time both `maximumSafePosition` and
|
|
328
|
+
* `livePosition` were calculated.
|
|
329
|
+
* This can be used to retrieve a new maximum position from them when they
|
|
330
|
+
* linearly evolves over time (see `isLinear` property).
|
|
331
|
+
*/
|
|
332
|
+
time : number;
|
|
313
333
|
};
|
|
314
334
|
};
|
|
315
335
|
|
|
@@ -459,38 +479,58 @@ export default class Manifest extends EventEmitter<IManifestEvents> {
|
|
|
459
479
|
}
|
|
460
480
|
|
|
461
481
|
/**
|
|
462
|
-
*
|
|
482
|
+
* Returns the theoretical minimum playable position on the content
|
|
483
|
+
* regardless of the current Adaptation chosen, as estimated at parsing
|
|
484
|
+
* time.
|
|
463
485
|
* @returns {number}
|
|
464
486
|
*/
|
|
465
|
-
public
|
|
487
|
+
public getMinimumSafePosition() : number {
|
|
466
488
|
const windowData = this._timeBounds;
|
|
467
489
|
if (windowData.timeshiftDepth === null) {
|
|
468
|
-
return windowData.
|
|
490
|
+
return windowData.minimumSafePosition ?? 0;
|
|
469
491
|
}
|
|
470
492
|
|
|
471
493
|
const { maximumTimeData } = windowData;
|
|
472
494
|
let maximumTime : number;
|
|
473
495
|
if (!windowData.maximumTimeData.isLinear) {
|
|
474
|
-
maximumTime = maximumTimeData.
|
|
496
|
+
maximumTime = maximumTimeData.maximumSafePosition;
|
|
475
497
|
} else {
|
|
476
498
|
const timeDiff = performance.now() - maximumTimeData.time;
|
|
477
|
-
maximumTime = maximumTimeData.
|
|
499
|
+
maximumTime = maximumTimeData.maximumSafePosition + timeDiff / 1000;
|
|
478
500
|
}
|
|
479
501
|
const theoricalMinimum = maximumTime - windowData.timeshiftDepth;
|
|
480
|
-
return Math.max(windowData.
|
|
502
|
+
return Math.max(windowData.minimumSafePosition ?? 0, theoricalMinimum);
|
|
481
503
|
}
|
|
482
504
|
|
|
483
505
|
/**
|
|
484
|
-
* Get the
|
|
485
|
-
*
|
|
506
|
+
* Get the position of the live edge - that is, the position of what is
|
|
507
|
+
* currently being broadcasted, in seconds.
|
|
508
|
+
* @returns {number|undefined}
|
|
509
|
+
*/
|
|
510
|
+
public getLivePosition() : number | undefined {
|
|
511
|
+
const { maximumTimeData } = this._timeBounds;
|
|
512
|
+
if (!this.isLive || maximumTimeData.livePosition === undefined) {
|
|
513
|
+
return undefined;
|
|
514
|
+
}
|
|
515
|
+
if (!maximumTimeData.isLinear) {
|
|
516
|
+
return maximumTimeData.livePosition;
|
|
517
|
+
}
|
|
518
|
+
const timeDiff = performance.now() - maximumTimeData.time;
|
|
519
|
+
return maximumTimeData.livePosition + timeDiff / 1000;
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
/**
|
|
523
|
+
* Returns the theoretical maximum playable position on the content
|
|
524
|
+
* regardless of the current Adaptation chosen, as estimated at parsing
|
|
525
|
+
* time.
|
|
486
526
|
*/
|
|
487
|
-
public
|
|
527
|
+
public getMaximumSafePosition() : number {
|
|
488
528
|
const { maximumTimeData } = this._timeBounds;
|
|
489
529
|
if (!maximumTimeData.isLinear) {
|
|
490
|
-
return maximumTimeData.
|
|
530
|
+
return maximumTimeData.maximumSafePosition;
|
|
491
531
|
}
|
|
492
532
|
const timeDiff = performance.now() - maximumTimeData.time;
|
|
493
|
-
return maximumTimeData.
|
|
533
|
+
return maximumTimeData.maximumSafePosition + timeDiff / 1000;
|
|
494
534
|
}
|
|
495
535
|
|
|
496
536
|
/**
|
|
@@ -690,7 +730,7 @@ export default class Manifest extends EventEmitter<IManifestEvents> {
|
|
|
690
730
|
// Partial updates do not remove old Periods.
|
|
691
731
|
// This can become a memory problem when playing a content long enough.
|
|
692
732
|
// Let's clean manually Periods behind the minimum possible position.
|
|
693
|
-
const min = this.
|
|
733
|
+
const min = this.getMinimumSafePosition();
|
|
694
734
|
while (this.periods.length > 0) {
|
|
695
735
|
const period = this.periods[0];
|
|
696
736
|
if (period.end === undefined || period.end > min) {
|
package/src/manifest/period.ts
CHANGED
|
@@ -185,4 +185,14 @@ export default class Period {
|
|
|
185
185
|
return ada.isSupported;
|
|
186
186
|
});
|
|
187
187
|
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Returns true if the give time is in the time boundaries of this `Period`.
|
|
191
|
+
* @param {number} time
|
|
192
|
+
* @returns {boolean}
|
|
193
|
+
*/
|
|
194
|
+
containsTime(time : number) : boolean {
|
|
195
|
+
return time >= this.start && (this.end === undefined ||
|
|
196
|
+
time < this.end);
|
|
197
|
+
}
|
|
188
198
|
}
|
|
@@ -268,10 +268,15 @@ export interface IRepresentationIndex {
|
|
|
268
268
|
getFirstPosition() : number | null | undefined;
|
|
269
269
|
|
|
270
270
|
/**
|
|
271
|
-
* Returns the ending time, in seconds, of the last
|
|
272
|
-
* available in this index.
|
|
271
|
+
* Returns the ending time, in seconds, of the last playable position
|
|
272
|
+
* currently available in this index.
|
|
273
273
|
* Returns `null` if nothing is in the index
|
|
274
274
|
* Returns `undefined` if we cannot know this value.
|
|
275
|
+
*
|
|
276
|
+
* The last playable position is generally equivalent to the end of the last
|
|
277
|
+
* segment associated to this index, excepted when it goes over the end limit
|
|
278
|
+
* of the corresponding `Period`, in which case it will be equal to this end
|
|
279
|
+
* instead.
|
|
275
280
|
* @returns {Number|null|undefined}
|
|
276
281
|
*/
|
|
277
282
|
getLastPosition() : number | null | undefined;
|
|
@@ -15,20 +15,25 @@
|
|
|
15
15
|
*/
|
|
16
16
|
|
|
17
17
|
import { IParsedPeriod } from "../../types";
|
|
18
|
-
import
|
|
18
|
+
import getMaximumPositions from "../../utils/get_maximum_positions";
|
|
19
19
|
import getMinimumPosition from "../../utils/get_minimum_position";
|
|
20
20
|
|
|
21
21
|
/**
|
|
22
22
|
* @param {Object} periods
|
|
23
23
|
* @returns {Array.<number>}
|
|
24
24
|
*/
|
|
25
|
-
export default function
|
|
25
|
+
export default function getMinimumAndMaximumPositions(
|
|
26
26
|
periods: IParsedPeriod[]
|
|
27
|
-
) :
|
|
27
|
+
) : { minimumSafePosition : number | undefined;
|
|
28
|
+
maximumSafePosition : number | undefined;
|
|
29
|
+
maximumUnsafePosition : number | undefined; } {
|
|
28
30
|
if (periods.length === 0) {
|
|
29
31
|
throw new Error("DASH Parser: no period available for a dynamic content");
|
|
30
32
|
}
|
|
31
33
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
+
const minimumSafePosition = getMinimumPosition(periods);
|
|
35
|
+
const maxPositions = getMaximumPositions(periods);
|
|
36
|
+
return { minimumSafePosition,
|
|
37
|
+
maximumSafePosition: maxPositions.safe,
|
|
38
|
+
maximumUnsafePosition: maxPositions.unsafe };
|
|
34
39
|
}
|
|
@@ -288,9 +288,10 @@ export default class BaseRepresentationIndex implements IRepresentationIndex {
|
|
|
288
288
|
return null;
|
|
289
289
|
}
|
|
290
290
|
const lastTimelineElement = timeline[timeline.length - 1];
|
|
291
|
-
const lastTime = getIndexSegmentEnd(lastTimelineElement,
|
|
292
|
-
|
|
293
|
-
|
|
291
|
+
const lastTime = Math.min(getIndexSegmentEnd(lastTimelineElement,
|
|
292
|
+
null,
|
|
293
|
+
this._scaledPeriodEnd),
|
|
294
|
+
this._scaledPeriodEnd ?? Infinity);
|
|
294
295
|
return fromIndexTime(lastTime, this._index);
|
|
295
296
|
}
|
|
296
297
|
|
|
@@ -106,6 +106,8 @@ export interface IListIndexIndexArgument {
|
|
|
106
106
|
export interface IListIndexContextArgument {
|
|
107
107
|
/** Start of the period concerned by this RepresentationIndex, in seconds. */
|
|
108
108
|
periodStart : number;
|
|
109
|
+
/** End of the period concerned by this RepresentationIndex, in seconds. */
|
|
110
|
+
periodEnd : number | undefined;
|
|
109
111
|
/** Base URL for the Representation concerned. */
|
|
110
112
|
representationBaseURLs : IResolvedBaseUrl[];
|
|
111
113
|
/** ID of the Representation concerned. */
|
|
@@ -121,6 +123,8 @@ export default class ListRepresentationIndex implements IRepresentationIndex {
|
|
|
121
123
|
private _index : IListIndex;
|
|
122
124
|
/** Start of the period concerned by this RepresentationIndex, in seconds. */
|
|
123
125
|
protected _periodStart : number;
|
|
126
|
+
/** End of the period concerned by this RepresentationIndex, in seconds. */
|
|
127
|
+
protected _periodEnd : number | undefined;
|
|
124
128
|
/* Function that tells if an EMSG is whitelisted by the manifest */
|
|
125
129
|
private _isEMSGWhitelisted: (inbandEvent: IEMSG) => boolean;
|
|
126
130
|
|
|
@@ -134,12 +138,14 @@ export default class ListRepresentationIndex implements IRepresentationIndex {
|
|
|
134
138
|
}
|
|
135
139
|
|
|
136
140
|
const { periodStart,
|
|
141
|
+
periodEnd,
|
|
137
142
|
representationBaseURLs,
|
|
138
143
|
representationId,
|
|
139
144
|
representationBitrate,
|
|
140
145
|
isEMSGWhitelisted } = context;
|
|
141
146
|
this._isEMSGWhitelisted = isEMSGWhitelisted;
|
|
142
147
|
this._periodStart = periodStart;
|
|
148
|
+
this._periodEnd = periodEnd;
|
|
143
149
|
const presentationTimeOffset =
|
|
144
150
|
index.presentationTimeOffset != null ? index.presentationTimeOffset :
|
|
145
151
|
0;
|
|
@@ -248,7 +254,8 @@ export default class ListRepresentationIndex implements IRepresentationIndex {
|
|
|
248
254
|
getLastPosition() : number {
|
|
249
255
|
const index = this._index;
|
|
250
256
|
const { duration, list } = index;
|
|
251
|
-
return ((list.length * duration) / index.timescale) + this._periodStart
|
|
257
|
+
return Math.min(((list.length * duration) / index.timescale) + this._periodStart,
|
|
258
|
+
this._periodEnd ?? Infinity);
|
|
252
259
|
}
|
|
253
260
|
|
|
254
261
|
/**
|
|
@@ -218,8 +218,9 @@ export default class TemplateRepresentationIndex implements IRepresentationIndex
|
|
|
218
218
|
startNumber: index.startNumber };
|
|
219
219
|
this._isDynamic = isDynamic;
|
|
220
220
|
this._periodStart = periodStart;
|
|
221
|
-
this._scaledPeriodEnd = periodEnd
|
|
222
|
-
|
|
221
|
+
this._scaledPeriodEnd = periodEnd === undefined ?
|
|
222
|
+
undefined :
|
|
223
|
+
(periodEnd - periodStart) * timescale;
|
|
223
224
|
this._isEMSGWhitelisted = isEMSGWhitelisted;
|
|
224
225
|
}
|
|
225
226
|
|
|
@@ -334,7 +335,11 @@ export default class TemplateRepresentationIndex implements IRepresentationIndex
|
|
|
334
335
|
// the two functions. So, we return the result of the latter.
|
|
335
336
|
return lastSegmentStart;
|
|
336
337
|
}
|
|
337
|
-
|
|
338
|
+
|
|
339
|
+
const lastSegmentEnd = Math.min(
|
|
340
|
+
lastSegmentStart + this._index.duration,
|
|
341
|
+
this._scaledPeriodEnd ?? Infinity
|
|
342
|
+
);
|
|
338
343
|
return (lastSegmentEnd / this._index.timescale) + this._periodStart;
|
|
339
344
|
}
|
|
340
345
|
|
|
@@ -47,7 +47,6 @@ import constructTimelineFromElements from "./construct_timeline_from_elements";
|
|
|
47
47
|
// eslint-disable-next-line max-len
|
|
48
48
|
import constructTimelineFromPreviousTimeline from "./construct_timeline_from_previous_timeline";
|
|
49
49
|
|
|
50
|
-
|
|
51
50
|
/**
|
|
52
51
|
* Index property defined for a SegmentTimeline RepresentationIndex
|
|
53
52
|
* This object contains every property needed to generate an ISegment for a
|
|
@@ -307,8 +306,8 @@ export default class TimelineRepresentationIndex implements IRepresentationIndex
|
|
|
307
306
|
timeline: index.timeline ?? null,
|
|
308
307
|
timescale };
|
|
309
308
|
this._scaledPeriodStart = toIndexTime(periodStart, this._index);
|
|
310
|
-
this._scaledPeriodEnd = periodEnd
|
|
311
|
-
|
|
309
|
+
this._scaledPeriodEnd = periodEnd === undefined ? undefined :
|
|
310
|
+
toIndexTime(periodEnd, this._index);
|
|
312
311
|
}
|
|
313
312
|
|
|
314
313
|
/**
|
|
@@ -389,7 +388,7 @@ export default class TimelineRepresentationIndex implements IRepresentationIndex
|
|
|
389
388
|
this._index.timeline = this._getTimeline();
|
|
390
389
|
}
|
|
391
390
|
const lastTime = TimelineRepresentationIndex.getIndexEnd(this._index.timeline,
|
|
392
|
-
this.
|
|
391
|
+
this._scaledPeriodEnd);
|
|
393
392
|
return lastTime === null ? null :
|
|
394
393
|
fromIndexTime(lastTime, this._index);
|
|
395
394
|
}
|
|
@@ -566,9 +565,10 @@ export default class TimelineRepresentationIndex implements IRepresentationIndex
|
|
|
566
565
|
if (timeline.length <= 0) {
|
|
567
566
|
return null;
|
|
568
567
|
}
|
|
569
|
-
return getIndexSegmentEnd(timeline[timeline.length - 1],
|
|
570
|
-
|
|
571
|
-
|
|
568
|
+
return Math.min(getIndexSegmentEnd(timeline[timeline.length - 1],
|
|
569
|
+
null,
|
|
570
|
+
scaledPeriodEnd),
|
|
571
|
+
scaledPeriodEnd ?? Infinity);
|
|
572
572
|
}
|
|
573
573
|
|
|
574
574
|
/**
|
|
@@ -28,7 +28,7 @@ import { IResponseData } from "../parsers_types";
|
|
|
28
28
|
// eslint-disable-next-line max-len
|
|
29
29
|
import getClockOffset from "./get_clock_offset";
|
|
30
30
|
import getHTTPUTCTimingURL from "./get_http_utc-timing_url";
|
|
31
|
-
import
|
|
31
|
+
import getMinimumAndMaximumPositions from "./get_minimum_and_maximum_positions";
|
|
32
32
|
import parseAvailabilityStartTime from "./parse_availability_start_time";
|
|
33
33
|
import parsePeriods, {
|
|
34
34
|
IXLinkInfos,
|
|
@@ -270,7 +270,10 @@ function parseCompleteIntermediateRepresentation(
|
|
|
270
270
|
let lifetime : number | undefined;
|
|
271
271
|
let minimumTime : number | undefined;
|
|
272
272
|
let timeshiftDepth : number | null = null;
|
|
273
|
-
let maximumTimeData : { isLinear : boolean;
|
|
273
|
+
let maximumTimeData : { isLinear : boolean;
|
|
274
|
+
maximumSafePosition : number;
|
|
275
|
+
livePosition : number | undefined;
|
|
276
|
+
time : number; };
|
|
274
277
|
|
|
275
278
|
if (rootAttributes.minimumUpdatePeriod !== undefined &&
|
|
276
279
|
rootAttributes.minimumUpdatePeriod >= 0)
|
|
@@ -280,58 +283,73 @@ function parseCompleteIntermediateRepresentation(
|
|
|
280
283
|
rootAttributes.minimumUpdatePeriod;
|
|
281
284
|
}
|
|
282
285
|
|
|
283
|
-
const
|
|
286
|
+
const { minimumSafePosition,
|
|
287
|
+
maximumSafePosition,
|
|
288
|
+
maximumUnsafePosition } = getMinimumAndMaximumPositions(parsedPeriods);
|
|
284
289
|
const now = performance.now();
|
|
285
290
|
|
|
286
291
|
if (!isDynamic) {
|
|
287
|
-
minimumTime =
|
|
292
|
+
minimumTime = minimumSafePosition !== undefined ? minimumSafePosition :
|
|
288
293
|
parsedPeriods[0]?.start !== undefined ? parsedPeriods[0].start :
|
|
289
294
|
0;
|
|
290
|
-
let
|
|
295
|
+
let finalMaximumSafePosition = mediaPresentationDuration ?? Infinity;
|
|
291
296
|
if (parsedPeriods[parsedPeriods.length - 1] !== undefined) {
|
|
292
297
|
const lastPeriod = parsedPeriods[parsedPeriods.length - 1];
|
|
293
298
|
const lastPeriodEnd = lastPeriod.end ??
|
|
294
299
|
(lastPeriod.duration !== undefined ?
|
|
295
300
|
lastPeriod.start + lastPeriod.duration :
|
|
296
301
|
undefined);
|
|
297
|
-
if (lastPeriodEnd !== undefined && lastPeriodEnd <
|
|
298
|
-
|
|
302
|
+
if (lastPeriodEnd !== undefined && lastPeriodEnd < finalMaximumSafePosition) {
|
|
303
|
+
finalMaximumSafePosition = lastPeriodEnd;
|
|
299
304
|
}
|
|
300
305
|
}
|
|
301
|
-
if (
|
|
302
|
-
|
|
306
|
+
if (maximumSafePosition !== undefined &&
|
|
307
|
+
maximumSafePosition < finalMaximumSafePosition)
|
|
308
|
+
{
|
|
309
|
+
finalMaximumSafePosition = maximumSafePosition;
|
|
303
310
|
}
|
|
304
311
|
|
|
305
312
|
maximumTimeData = { isLinear: false,
|
|
306
|
-
|
|
313
|
+
maximumSafePosition: finalMaximumSafePosition,
|
|
314
|
+
livePosition: undefined,
|
|
307
315
|
time: now };
|
|
308
316
|
} else {
|
|
309
|
-
minimumTime =
|
|
317
|
+
minimumTime = minimumSafePosition;
|
|
310
318
|
timeshiftDepth = timeShiftBufferDepth ?? null;
|
|
311
|
-
let
|
|
312
|
-
|
|
313
|
-
|
|
319
|
+
let finalMaximumSafePosition : number;
|
|
320
|
+
let livePosition;
|
|
321
|
+
|
|
322
|
+
if (maximumUnsafePosition !== undefined) {
|
|
323
|
+
livePosition = maximumUnsafePosition;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
if (maximumSafePosition !== undefined) {
|
|
327
|
+
finalMaximumSafePosition = maximumSafePosition;
|
|
314
328
|
} else {
|
|
315
329
|
const ast = availabilityStartTime ?? 0;
|
|
316
330
|
const { externalClockOffset } = args;
|
|
317
331
|
if (externalClockOffset === undefined) {
|
|
318
332
|
log.warn("DASH Parser: use system clock to define maximum position");
|
|
319
|
-
|
|
333
|
+
finalMaximumSafePosition = (Date.now() / 1000) - ast;
|
|
320
334
|
} else {
|
|
321
335
|
const serverTime = performance.now() + externalClockOffset;
|
|
322
|
-
|
|
336
|
+
finalMaximumSafePosition = (serverTime / 1000) - ast;
|
|
323
337
|
}
|
|
324
338
|
}
|
|
339
|
+
if (livePosition === undefined) {
|
|
340
|
+
livePosition = finalMaximumSafePosition;
|
|
341
|
+
}
|
|
325
342
|
maximumTimeData = { isLinear: true,
|
|
326
|
-
|
|
343
|
+
maximumSafePosition: finalMaximumSafePosition,
|
|
344
|
+
livePosition,
|
|
327
345
|
time: now };
|
|
328
346
|
|
|
329
347
|
// if the minimum calculated time is even below the buffer depth, perhaps we
|
|
330
348
|
// can go even lower in terms of depth
|
|
331
349
|
if (timeshiftDepth !== null && minimumTime !== undefined &&
|
|
332
|
-
|
|
350
|
+
finalMaximumSafePosition - minimumTime > timeshiftDepth)
|
|
333
351
|
{
|
|
334
|
-
timeshiftDepth =
|
|
352
|
+
timeshiftDepth = finalMaximumSafePosition - minimumTime;
|
|
335
353
|
}
|
|
336
354
|
}
|
|
337
355
|
|
|
@@ -358,7 +376,7 @@ function parseCompleteIntermediateRepresentation(
|
|
|
358
376
|
publishTime: rootAttributes.publishTime,
|
|
359
377
|
suggestedPresentationDelay: rootAttributes.suggestedPresentationDelay,
|
|
360
378
|
transportType: "dash",
|
|
361
|
-
timeBounds: {
|
|
379
|
+
timeBounds: { minimumSafePosition: minimumTime,
|
|
362
380
|
timeshiftDepth,
|
|
363
381
|
maximumTimeData },
|
|
364
382
|
lifetime,
|
|
@@ -61,10 +61,11 @@ export default function parseLocalManifest(
|
|
|
61
61
|
isLastPeriodKnown: isFinished,
|
|
62
62
|
isLive: false,
|
|
63
63
|
uris: [],
|
|
64
|
-
timeBounds: {
|
|
64
|
+
timeBounds: { minimumSafePosition: minimumPosition ?? 0,
|
|
65
65
|
timeshiftDepth: null,
|
|
66
66
|
maximumTimeData: { isLinear: false,
|
|
67
|
-
|
|
67
|
+
maximumSafePosition: maximumPosition,
|
|
68
|
+
livePosition: undefined,
|
|
68
69
|
time: performance.now() } },
|
|
69
70
|
periods: parsedPeriods };
|
|
70
71
|
}
|
|
@@ -308,11 +308,16 @@ function createManifest(
|
|
|
308
308
|
isLastPeriodKnown,
|
|
309
309
|
uris: url == null ? [] :
|
|
310
310
|
[url],
|
|
311
|
-
|
|
311
|
+
|
|
312
|
+
// TODO more precize time bounds?
|
|
313
|
+
timeBounds: { minimumSafePosition: minimumTime,
|
|
312
314
|
timeshiftDepth: null,
|
|
313
|
-
maximumTimeData: {
|
|
314
|
-
|
|
315
|
-
|
|
315
|
+
maximumTimeData: {
|
|
316
|
+
isLinear: false,
|
|
317
|
+
maximumSafePosition: maximumTime,
|
|
318
|
+
livePosition: undefined,
|
|
319
|
+
time,
|
|
320
|
+
} },
|
|
316
321
|
lifetime: mplData.pollInterval };
|
|
317
322
|
|
|
318
323
|
return manifest;
|
|
@@ -533,7 +533,10 @@ function createSmoothStreamingParser(
|
|
|
533
533
|
let availabilityStartTime : number|undefined;
|
|
534
534
|
let minimumTime : number | undefined;
|
|
535
535
|
let timeshiftDepth : number | null = null;
|
|
536
|
-
let maximumTimeData : { isLinear : boolean;
|
|
536
|
+
let maximumTimeData : { isLinear : boolean;
|
|
537
|
+
maximumSafePosition: number;
|
|
538
|
+
livePosition : number | undefined;
|
|
539
|
+
time : number; };
|
|
537
540
|
|
|
538
541
|
const firstVideoAdaptation = adaptations.video !== undefined ?
|
|
539
542
|
adaptations.video[0] :
|
|
@@ -541,8 +544,15 @@ function createSmoothStreamingParser(
|
|
|
541
544
|
const firstAudioAdaptation = adaptations.audio !== undefined ?
|
|
542
545
|
adaptations.audio[0] :
|
|
543
546
|
undefined;
|
|
544
|
-
|
|
545
|
-
|
|
547
|
+
|
|
548
|
+
/** Minimum time that can be reached regardless of the StreamIndex chosen. */
|
|
549
|
+
let safeMinimumTime : number|undefined;
|
|
550
|
+
|
|
551
|
+
/** Maximum time that can be reached regardless of the StreamIndex chosen. */
|
|
552
|
+
let safeMaximumTime : number|undefined;
|
|
553
|
+
|
|
554
|
+
/** Maximum time that can be reached in absolute on the content. */
|
|
555
|
+
let unsafeMaximumTime : number|undefined;
|
|
546
556
|
|
|
547
557
|
if (firstVideoAdaptation !== undefined || firstAudioAdaptation !== undefined) {
|
|
548
558
|
const firstTimeReferences : number[] = [];
|
|
@@ -585,44 +595,52 @@ function createSmoothStreamingParser(
|
|
|
585
595
|
}
|
|
586
596
|
|
|
587
597
|
if (firstTimeReferences.length > 0) {
|
|
588
|
-
|
|
598
|
+
safeMinimumTime = Math.max(...firstTimeReferences);
|
|
589
599
|
}
|
|
590
600
|
|
|
591
601
|
if (lastTimeReferences.length > 0) {
|
|
592
|
-
|
|
602
|
+
safeMaximumTime = Math.min(...lastTimeReferences);
|
|
603
|
+
unsafeMaximumTime = Math.max(...lastTimeReferences);
|
|
593
604
|
}
|
|
594
605
|
}
|
|
595
606
|
|
|
596
607
|
const manifestDuration = root.getAttribute("Duration");
|
|
597
|
-
const duration = (manifestDuration
|
|
608
|
+
const duration = (manifestDuration !== null && +manifestDuration !== 0) ?
|
|
598
609
|
(+manifestDuration / timescale) : undefined;
|
|
599
610
|
|
|
600
611
|
if (isLive) {
|
|
601
612
|
suggestedPresentationDelay = parserOptions.suggestedPresentationDelay;
|
|
602
613
|
availabilityStartTime = referenceDateTime;
|
|
603
614
|
|
|
604
|
-
minimumTime =
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
(Date.now() / 1000 - availabilityStartTime);
|
|
615
|
+
minimumTime = safeMinimumTime ?? availabilityStartTime;
|
|
616
|
+
let livePosition = unsafeMaximumTime;
|
|
617
|
+
if (livePosition === undefined) {
|
|
618
|
+
livePosition = (Date.now() / 1000 - availabilityStartTime);
|
|
619
|
+
}
|
|
620
|
+
let maximumSafePosition = safeMaximumTime;
|
|
621
|
+
if (maximumSafePosition === undefined) {
|
|
622
|
+
maximumSafePosition = livePosition;
|
|
623
|
+
}
|
|
608
624
|
maximumTimeData = { isLinear: true,
|
|
609
|
-
|
|
625
|
+
maximumSafePosition,
|
|
626
|
+
livePosition,
|
|
610
627
|
time: performance.now() };
|
|
611
628
|
timeshiftDepth = timeShiftBufferDepth ?? null;
|
|
612
629
|
} else {
|
|
613
|
-
minimumTime =
|
|
614
|
-
const maximumTime =
|
|
615
|
-
duration !== undefined
|
|
616
|
-
|
|
630
|
+
minimumTime = safeMinimumTime ?? 0;
|
|
631
|
+
const maximumTime = safeMaximumTime !== undefined ? safeMaximumTime :
|
|
632
|
+
duration !== undefined ? minimumTime + duration :
|
|
633
|
+
Infinity;
|
|
617
634
|
maximumTimeData = { isLinear: false,
|
|
618
|
-
|
|
635
|
+
maximumSafePosition: maximumTime,
|
|
636
|
+
livePosition: undefined,
|
|
619
637
|
time: performance.now() };
|
|
620
638
|
}
|
|
621
639
|
|
|
622
640
|
const periodStart = isLive ? 0 :
|
|
623
641
|
minimumTime;
|
|
624
642
|
const periodEnd = isLive ? undefined :
|
|
625
|
-
maximumTimeData.
|
|
643
|
+
maximumTimeData.maximumSafePosition;
|
|
626
644
|
const manifest = {
|
|
627
645
|
availabilityStartTime: availabilityStartTime === undefined ?
|
|
628
646
|
0 :
|
|
@@ -631,7 +649,7 @@ function createSmoothStreamingParser(
|
|
|
631
649
|
isLive,
|
|
632
650
|
isDynamic: isLive,
|
|
633
651
|
isLastPeriodKnown: true,
|
|
634
|
-
timeBounds: {
|
|
652
|
+
timeBounds: { minimumSafePosition: minimumTime,
|
|
635
653
|
timeshiftDepth,
|
|
636
654
|
maximumTimeData },
|
|
637
655
|
periods: [{ adaptations,
|