@gcorevideo/player 2.10.0 → 2.12.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +13 -0
- package/coverage/clover.xml +6 -0
- package/coverage/coverage-final.json +1 -0
- package/coverage/lcov-report/base.css +224 -0
- package/coverage/lcov-report/block-navigation.js +87 -0
- package/coverage/lcov-report/favicon.png +0 -0
- package/coverage/lcov-report/index.html +101 -0
- package/coverage/lcov-report/prettify.css +1 -0
- package/coverage/lcov-report/prettify.js +2 -0
- package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
- package/coverage/lcov-report/sorter.js +196 -0
- package/coverage/lcov.info +0 -0
- package/dist/index.js +3115 -1209
- package/lib/Player.d.ts +3 -2
- package/lib/Player.d.ts.map +1 -1
- package/lib/Player.js +32 -24
- package/lib/index.d.ts +5 -5
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +5 -5
- package/lib/internal.types.d.ts +1 -10
- package/lib/internal.types.d.ts.map +1 -1
- package/lib/playback/index.d.ts +4 -0
- package/lib/playback/index.d.ts.map +1 -0
- package/lib/playback/index.js +13 -0
- package/lib/playback.types.d.ts +19 -0
- package/lib/playback.types.d.ts.map +1 -1
- package/lib/playback.types.js +9 -1
- package/lib/plugins/dash-playback/DashPlayback.d.ts +1 -1
- package/lib/plugins/dash-playback/DashPlayback.d.ts.map +1 -1
- package/lib/plugins/dash-playback/DashPlayback.js +39 -100
- package/lib/plugins/dash-playback/types.d.ts +6 -0
- package/lib/plugins/dash-playback/types.d.ts.map +1 -0
- package/lib/plugins/dash-playback/types.js +1 -0
- package/lib/plugins/hls-playback/HlsPlayback.d.ts +6 -7
- package/lib/plugins/hls-playback/HlsPlayback.d.ts.map +1 -1
- package/lib/plugins/hls-playback/HlsPlayback.js +131 -80
- package/lib/types.d.ts +3 -3
- package/lib/types.d.ts.map +1 -1
- package/lib/utils/mediaSources.d.ts +14 -6
- package/lib/utils/mediaSources.d.ts.map +1 -1
- package/lib/utils/mediaSources.js +56 -53
- package/lib/utils/testUtils.d.ts +3 -0
- package/lib/utils/testUtils.d.ts.map +1 -0
- package/lib/utils/testUtils.js +12 -0
- package/package.json +6 -4
- package/src/Player.ts +40 -31
- package/src/__tests__/Player.test.ts +357 -0
- package/src/index.ts +5 -5
- package/src/internal.types.ts +1 -12
- package/src/playback/index.ts +17 -0
- package/src/playback.types.ts +29 -8
- package/src/plugins/dash-playback/DashPlayback.ts +44 -120
- package/src/plugins/hls-playback/HlsPlayback.ts +544 -390
- package/src/types.ts +5 -3
- package/src/typings/@clappr/core/error_mixin.d.ts +0 -2
- package/src/typings/@clappr/core/index.d.ts +5 -0
- package/src/typings/@clappr/index.d.ts +1 -0
- package/src/utils/__tests__/mediaSources.test.ts +230 -0
- package/src/utils/mediaSources.ts +78 -64
- package/src/utils/testUtils.ts +15 -0
- package/tsconfig.json +0 -9
- package/tsconfig.tsbuildinfo +1 -1
- package/vitest.config.ts +8 -0
- package/licenses.json +0 -782
- package/src/utils/queryParams.ts +0 -5
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
// Copyright 2014 Globo.com Player authors. All rights reserved.
|
|
2
2
|
// Use of this source code is governed by a BSD-style
|
|
3
3
|
// license that can be found in the LICENSE file.
|
|
4
|
-
import { Events, HTML5Video, Log, Playback, PlayerError, Utils } from '@clappr/core';
|
|
4
|
+
import { Events, HTML5Video, Log, Playback, PlayerError, Utils, } from '@clappr/core';
|
|
5
|
+
import { trace } from '@gcorevideo/utils';
|
|
5
6
|
import assert from 'assert';
|
|
6
7
|
import HLSJS from 'hls.js';
|
|
7
|
-
import {
|
|
8
|
-
import { CLAPPR_VERSION } from
|
|
8
|
+
import { PlaybackErrorCode } from '../../playback.types.js';
|
|
9
|
+
import { CLAPPR_VERSION } from '../../build.js';
|
|
9
10
|
const { now, listContainsIgnoreCase } = Utils;
|
|
10
11
|
const AUTO = -1;
|
|
11
12
|
const DEFAULT_RECOVER_ATTEMPTS = 16;
|
|
@@ -75,7 +76,8 @@ export default class HlsPlayback extends HTML5Video {
|
|
|
75
76
|
return this._hls.playingDate;
|
|
76
77
|
}
|
|
77
78
|
get _startTime() {
|
|
78
|
-
if (this._playbackType === Playback.LIVE &&
|
|
79
|
+
if (this._playbackType === Playback.LIVE &&
|
|
80
|
+
this._playlistType !== 'EVENT') {
|
|
79
81
|
return this._extrapolatedStartTime;
|
|
80
82
|
}
|
|
81
83
|
return this._playableRegionStartTime;
|
|
@@ -140,7 +142,8 @@ export default class HlsPlayback extends HTML5Video {
|
|
|
140
142
|
return { preload: true };
|
|
141
143
|
}
|
|
142
144
|
get customListeners() {
|
|
143
|
-
return this.options.hlsPlayback && this.options.hlsPlayback.customListeners ||
|
|
145
|
+
return ((this.options.hlsPlayback && this.options.hlsPlayback.customListeners) ||
|
|
146
|
+
[]);
|
|
144
147
|
}
|
|
145
148
|
get sourceMedia() {
|
|
146
149
|
return this.options.src;
|
|
@@ -162,17 +165,27 @@ export default class HlsPlayback extends HTML5Video {
|
|
|
162
165
|
constructor(...args) {
|
|
163
166
|
// @ts-ignore
|
|
164
167
|
super(...args);
|
|
165
|
-
this.options.hlsPlayback = {
|
|
168
|
+
this.options.hlsPlayback = {
|
|
169
|
+
...this.defaultOptions,
|
|
170
|
+
...this.options.hlsPlayback,
|
|
171
|
+
};
|
|
166
172
|
this._setInitialState();
|
|
167
173
|
}
|
|
168
174
|
_setInitialState() {
|
|
169
175
|
// @ts-ignore
|
|
170
|
-
this._minDvrSize =
|
|
176
|
+
this._minDvrSize =
|
|
177
|
+
typeof this.options.hlsMinimumDvrSize === 'undefined'
|
|
178
|
+
? 60
|
|
179
|
+
: this.options.hlsMinimumDvrSize;
|
|
171
180
|
// The size of the start time extrapolation window measured as a multiple of segments.
|
|
172
181
|
// Should be 2 or higher, or 0 to disable. Should only need to be increased above 2 if more than one segment is
|
|
173
182
|
// removed from the start of the playlist at a time. E.g if the playlist is cached for 10 seconds and new chunks are
|
|
174
183
|
// added/removed every 5.
|
|
175
|
-
this._extrapolatedWindowNumSegments =
|
|
184
|
+
this._extrapolatedWindowNumSegments =
|
|
185
|
+
!this.options.playback ||
|
|
186
|
+
typeof this.options.playback.extrapolatedWindowNumSegments === 'undefined'
|
|
187
|
+
? 2
|
|
188
|
+
: this.options.playback.extrapolatedWindowNumSegments;
|
|
176
189
|
this._playbackType = Playback.VOD;
|
|
177
190
|
this._lastTimeUpdate = { current: 0, total: 0 };
|
|
178
191
|
this._lastDuration = null;
|
|
@@ -204,7 +217,8 @@ export default class HlsPlayback extends HTML5Video {
|
|
|
204
217
|
// #EXT-X-PLAYLIST-TYPE
|
|
205
218
|
this._playlistType = null;
|
|
206
219
|
// TODO options.hlsRecoverAttempts
|
|
207
|
-
this._recoverAttemptsRemaining =
|
|
220
|
+
this._recoverAttemptsRemaining =
|
|
221
|
+
this.options.hlsRecoverAttempts || DEFAULT_RECOVER_ATTEMPTS;
|
|
208
222
|
}
|
|
209
223
|
_setup() {
|
|
210
224
|
this._destroyHLSInstance();
|
|
@@ -247,13 +261,15 @@ export default class HlsPlayback extends HTML5Video {
|
|
|
247
261
|
});
|
|
248
262
|
const onPlaying = () => {
|
|
249
263
|
if (this._hls) {
|
|
250
|
-
this._hls.config.maxBufferLength =
|
|
251
|
-
|
|
264
|
+
this._hls.config.maxBufferLength =
|
|
265
|
+
this.options.hlsPlayback.maxBufferLength || 30;
|
|
266
|
+
this._hls.config.maxMaxBufferLength =
|
|
267
|
+
this.options.hlsPlayback.maxMaxBufferLength || 60;
|
|
252
268
|
}
|
|
253
269
|
this.el.removeEventListener('playing', onPlaying);
|
|
254
270
|
};
|
|
255
271
|
this.el.addEventListener('playing', onPlaying);
|
|
256
|
-
this._hls.on(HLSJS.Events.MANIFEST_PARSED, () => this._manifestParsed = true);
|
|
272
|
+
this._hls.on(HLSJS.Events.MANIFEST_PARSED, () => (this._manifestParsed = true));
|
|
257
273
|
this._hls.on(HLSJS.Events.LEVEL_LOADED, (evt, data) => this._updatePlaybackType(evt, data));
|
|
258
274
|
this._hls.on(HLSJS.Events.LEVEL_UPDATED, (evt, data) => this._onLevelUpdated(evt, data));
|
|
259
275
|
this._hls.on(HLSJS.Events.LEVEL_SWITCHING, (evt, data) => this._onLevelSwitch(evt, data));
|
|
@@ -263,7 +279,7 @@ export default class HlsPlayback extends HTML5Video {
|
|
|
263
279
|
this._hls.on(HLSJS.Events.ERROR, (evt, data) => this._onHLSJSError(evt, data));
|
|
264
280
|
// this._hls.on(HLSJS.Events.SUBTITLE_TRACK_LOADED, (evt, data) => this._onSubtitleLoaded(evt, data));
|
|
265
281
|
this._hls.on(HLSJS.Events.SUBTITLE_TRACK_LOADED, () => this._onSubtitleLoaded());
|
|
266
|
-
this._hls.on(HLSJS.Events.SUBTITLE_TRACKS_UPDATED, () => this._ccTracksUpdated = true);
|
|
282
|
+
this._hls.on(HLSJS.Events.SUBTITLE_TRACKS_UPDATED, () => (this._ccTracksUpdated = true));
|
|
267
283
|
this.bindCustomListeners();
|
|
268
284
|
}
|
|
269
285
|
bindCustomListeners() {
|
|
@@ -271,7 +287,8 @@ export default class HlsPlayback extends HTML5Video {
|
|
|
271
287
|
const requestedEventName = item.eventName;
|
|
272
288
|
const typeOfListener = item.once ? 'once' : 'on';
|
|
273
289
|
assert.ok(this._hls, 'Hls.js instance is not available');
|
|
274
|
-
requestedEventName &&
|
|
290
|
+
requestedEventName &&
|
|
291
|
+
this._hls[`${typeOfListener}`](requestedEventName, item.callback);
|
|
275
292
|
});
|
|
276
293
|
}
|
|
277
294
|
unbindCustomListeners() {
|
|
@@ -283,7 +300,10 @@ export default class HlsPlayback extends HTML5Video {
|
|
|
283
300
|
}
|
|
284
301
|
_onFragmentParsingMetadata(evt, data) {
|
|
285
302
|
// @ts-ignore
|
|
286
|
-
this.trigger(Events.Custom.PLAYBACK_FRAGMENT_PARSING_METADATA, {
|
|
303
|
+
this.trigger(Events.Custom.PLAYBACK_FRAGMENT_PARSING_METADATA, {
|
|
304
|
+
evt,
|
|
305
|
+
data,
|
|
306
|
+
});
|
|
287
307
|
}
|
|
288
308
|
render() {
|
|
289
309
|
this._ready();
|
|
@@ -311,12 +331,9 @@ export default class HlsPlayback extends HTML5Video {
|
|
|
311
331
|
else {
|
|
312
332
|
Log.error('hlsjs: failed to recover', { evt, data });
|
|
313
333
|
error.level = PlayerError.Levels.FATAL;
|
|
314
|
-
|
|
315
|
-
this.trigger(Events.PLAYBACK_ERROR, formattedError);
|
|
316
|
-
this.stop();
|
|
334
|
+
this.triggerError(error);
|
|
317
335
|
}
|
|
318
336
|
}
|
|
319
|
-
// override
|
|
320
337
|
// this playback manages the src on the video element itself
|
|
321
338
|
_setupSrc(srcUrl) { } // eslint-disable-line no-unused-vars
|
|
322
339
|
_startTimeUpdateTimer() {
|
|
@@ -336,7 +353,7 @@ export default class HlsPlayback extends HTML5Video {
|
|
|
336
353
|
this._timeUpdateTimer = null;
|
|
337
354
|
}
|
|
338
355
|
getProgramDateTime() {
|
|
339
|
-
return this._programDateTime;
|
|
356
|
+
return this._programDateTime ?? 0;
|
|
340
357
|
}
|
|
341
358
|
// the duration on the video element itself should not be used
|
|
342
359
|
// as this does not necesarily represent the duration of the stream
|
|
@@ -357,9 +374,7 @@ export default class HlsPlayback extends HTML5Video {
|
|
|
357
374
|
return this._startTime;
|
|
358
375
|
}
|
|
359
376
|
seekPercentage(percentage) {
|
|
360
|
-
const seekTo =
|
|
361
|
-
? this._duration * (percentage / 100)
|
|
362
|
-
: this._duration;
|
|
377
|
+
const seekTo = percentage > 0 ? this._duration * (percentage / 100) : this._duration;
|
|
363
378
|
this.seek(seekTo);
|
|
364
379
|
}
|
|
365
380
|
seek(time) {
|
|
@@ -377,7 +392,7 @@ export default class HlsPlayback extends HTML5Video {
|
|
|
377
392
|
}
|
|
378
393
|
_updateDvr(status) {
|
|
379
394
|
this.trigger(Events.PLAYBACK_DVR, status);
|
|
380
|
-
this.trigger(Events.PLAYBACK_STATS_ADD, {
|
|
395
|
+
this.trigger(Events.PLAYBACK_STATS_ADD, { dvr: status });
|
|
381
396
|
}
|
|
382
397
|
_updateSettings() {
|
|
383
398
|
if (this._playbackType === Playback.VOD) {
|
|
@@ -398,11 +413,11 @@ export default class HlsPlayback extends HTML5Video {
|
|
|
398
413
|
}
|
|
399
414
|
_onHLSJSError(evt, data) {
|
|
400
415
|
const error = {
|
|
401
|
-
code:
|
|
402
|
-
description: `${this.name} error: type: ${data.type}, details: ${data.details}`,
|
|
403
|
-
|
|
416
|
+
code: PlaybackErrorCode.Generic,
|
|
417
|
+
description: `${this.name} error: type: ${data.type}, details: ${data.details} fatal: ${data.fatal}`,
|
|
418
|
+
level: data.fatal ? PlayerError.Levels.FATAL : PlayerError.Levels.WARN,
|
|
419
|
+
message: `${this.name} error: type: ${data.type}, details: ${data.details}`,
|
|
404
420
|
};
|
|
405
|
-
let formattedError;
|
|
406
421
|
if (data.response) {
|
|
407
422
|
error.description += `, response: ${JSON.stringify(data.response)}`;
|
|
408
423
|
}
|
|
@@ -422,37 +437,41 @@ export default class HlsPlayback extends HTML5Video {
|
|
|
422
437
|
case HLSJS.ErrorDetails.MANIFEST_PARSING_ERROR:
|
|
423
438
|
case HLSJS.ErrorDetails.LEVEL_LOAD_ERROR:
|
|
424
439
|
case HLSJS.ErrorDetails.LEVEL_LOAD_TIMEOUT:
|
|
425
|
-
Log.error('hlsjs: unrecoverable network fatal error.', {
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
440
|
+
Log.error('hlsjs: unrecoverable network fatal error.', {
|
|
441
|
+
evt,
|
|
442
|
+
data,
|
|
443
|
+
});
|
|
444
|
+
error.code = PlaybackErrorCode.MediaSourceUnavailable;
|
|
445
|
+
this.triggerError(error);
|
|
429
446
|
break;
|
|
430
447
|
default:
|
|
431
|
-
Log.warn('hlsjs: trying to recover from network error.', {
|
|
448
|
+
Log.warn('hlsjs: trying to recover from network error.', {
|
|
449
|
+
evt,
|
|
450
|
+
data,
|
|
451
|
+
});
|
|
432
452
|
error.level = PlayerError.Levels.WARN;
|
|
433
|
-
|
|
434
|
-
this._hls.startLoad();
|
|
453
|
+
this._hls?.startLoad();
|
|
435
454
|
break;
|
|
436
455
|
}
|
|
437
456
|
break;
|
|
438
457
|
case HLSJS.ErrorTypes.MEDIA_ERROR:
|
|
439
|
-
Log.warn('hlsjs: trying to recover from media error.', {
|
|
458
|
+
Log.warn('hlsjs: trying to recover from media error.', {
|
|
459
|
+
evt,
|
|
460
|
+
data,
|
|
461
|
+
});
|
|
440
462
|
error.level = PlayerError.Levels.WARN;
|
|
441
463
|
this._recover(evt, data, error);
|
|
442
464
|
break;
|
|
443
465
|
default:
|
|
444
466
|
Log.error('hlsjs: could not recover from error.', { evt, data });
|
|
445
|
-
|
|
446
|
-
this.trigger(Events.PLAYBACK_ERROR, formattedError);
|
|
447
|
-
this.stop();
|
|
467
|
+
this.triggerError(error);
|
|
448
468
|
break;
|
|
449
469
|
}
|
|
450
470
|
}
|
|
451
471
|
else {
|
|
452
472
|
Log.error('hlsjs: could not recover from error after maximum number of attempts.', { evt, data });
|
|
453
|
-
|
|
454
|
-
this.
|
|
455
|
-
this.stop();
|
|
473
|
+
// TODO
|
|
474
|
+
this.triggerError(error);
|
|
456
475
|
}
|
|
457
476
|
}
|
|
458
477
|
else {
|
|
@@ -460,28 +479,31 @@ export default class HlsPlayback extends HTML5Video {
|
|
|
460
479
|
// playback fatal error if triggerFatalErrorOnResourceDenied playback
|
|
461
480
|
// option is set. HLSJS.ErrorTypes.KEY_SYSTEM_ERROR are fatal errors
|
|
462
481
|
// and therefore already handled.
|
|
463
|
-
if (this.options.playback.triggerFatalErrorOnResourceDenied &&
|
|
482
|
+
if (this.options.playback.triggerFatalErrorOnResourceDenied &&
|
|
483
|
+
this._keyIsDenied(data)) {
|
|
464
484
|
Log.error('hlsjs: could not load decrypt key.', { evt, data });
|
|
465
|
-
|
|
466
|
-
this.trigger(Events.PLAYBACK_ERROR, formattedError);
|
|
467
|
-
this.stop();
|
|
485
|
+
this.triggerError(error);
|
|
468
486
|
return;
|
|
469
487
|
}
|
|
470
|
-
error.level = PlayerError.Levels.WARN;
|
|
471
488
|
Log.warn('hlsjs: non-fatal error occurred', { evt, data });
|
|
472
489
|
}
|
|
473
490
|
}
|
|
474
491
|
_keyIsDenied(data) {
|
|
475
|
-
return data.type === HLSJS.ErrorTypes.NETWORK_ERROR
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
492
|
+
return (data.type === HLSJS.ErrorTypes.NETWORK_ERROR &&
|
|
493
|
+
data.details === HLSJS.ErrorDetails.KEY_LOAD_ERROR &&
|
|
494
|
+
data.response &&
|
|
495
|
+
data.response.code &&
|
|
496
|
+
data.response.code >= 400);
|
|
480
497
|
}
|
|
481
498
|
_onTimeUpdate() {
|
|
482
|
-
const update = {
|
|
483
|
-
|
|
484
|
-
|
|
499
|
+
const update = {
|
|
500
|
+
current: this.getCurrentTime(),
|
|
501
|
+
total: this.getDuration(),
|
|
502
|
+
firstFragDateTime: this.getProgramDateTime(),
|
|
503
|
+
};
|
|
504
|
+
const isSame = this._lastTimeUpdate &&
|
|
505
|
+
update.current === this._lastTimeUpdate.current &&
|
|
506
|
+
update.total === this._lastTimeUpdate.total;
|
|
485
507
|
if (isSame) {
|
|
486
508
|
return;
|
|
487
509
|
}
|
|
@@ -503,19 +525,25 @@ export default class HlsPlayback extends HTML5Video {
|
|
|
503
525
|
let buffered = [];
|
|
504
526
|
let bufferedPos = 0;
|
|
505
527
|
for (let i = 0; i < this.el.buffered.length; i++) {
|
|
506
|
-
buffered = [
|
|
528
|
+
buffered = [
|
|
529
|
+
...buffered,
|
|
530
|
+
{
|
|
507
531
|
// for a stream with sliding window dvr something that is buffered my slide off the start of the timeline
|
|
508
|
-
start: Math.max(0, this.el.buffered.start(i) -
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
532
|
+
start: Math.max(0, this.el.buffered.start(i) -
|
|
533
|
+
this._playableRegionStartTime),
|
|
534
|
+
end: Math.max(0, this.el.buffered.end(i) -
|
|
535
|
+
this._playableRegionStartTime),
|
|
536
|
+
},
|
|
537
|
+
];
|
|
538
|
+
if (this.el.currentTime >= buffered[i].start &&
|
|
539
|
+
this.el.currentTime <= buffered[i].end) {
|
|
512
540
|
bufferedPos = i;
|
|
513
541
|
}
|
|
514
542
|
}
|
|
515
543
|
const progress = {
|
|
516
544
|
start: buffered[bufferedPos].start,
|
|
517
545
|
current: buffered[bufferedPos].end,
|
|
518
|
-
total: this.getDuration()
|
|
546
|
+
total: this.getDuration(),
|
|
519
547
|
};
|
|
520
548
|
this.trigger(Events.PLAYBACK_PROGRESS, progress, buffered);
|
|
521
549
|
}
|
|
@@ -528,7 +556,9 @@ export default class HlsPlayback extends HTML5Video {
|
|
|
528
556
|
trace(`${T} play`, { hls: !!this._hls, ...this.options.hlsPlayback });
|
|
529
557
|
!this._hls && this._setup();
|
|
530
558
|
assert.ok(this._hls, 'Hls.js instance is not available');
|
|
531
|
-
!this._manifestParsed &&
|
|
559
|
+
!this._manifestParsed &&
|
|
560
|
+
!this.options.hlsPlayback.preload &&
|
|
561
|
+
this._hls.loadSource(this.options.src);
|
|
532
562
|
super.play();
|
|
533
563
|
this._startTimeUpdateTimer();
|
|
534
564
|
}
|
|
@@ -536,6 +566,7 @@ export default class HlsPlayback extends HTML5Video {
|
|
|
536
566
|
if (!this._hls) {
|
|
537
567
|
return;
|
|
538
568
|
}
|
|
569
|
+
;
|
|
539
570
|
this.el.pause();
|
|
540
571
|
if (this.dvrEnabled) {
|
|
541
572
|
this._updateDvr(true);
|
|
@@ -558,7 +589,9 @@ export default class HlsPlayback extends HTML5Video {
|
|
|
558
589
|
this._playbackType = (data.details.live ? Playback.LIVE : Playback.VOD);
|
|
559
590
|
this._onLevelUpdated(evt, data);
|
|
560
591
|
// Live stream subtitle tracks detection hack (may not immediately available)
|
|
561
|
-
if (this._ccTracksUpdated &&
|
|
592
|
+
if (this._ccTracksUpdated &&
|
|
593
|
+
this._playbackType === Playback.LIVE &&
|
|
594
|
+
this.hasClosedCaptionsTracks) {
|
|
562
595
|
this._onSubtitleLoaded();
|
|
563
596
|
}
|
|
564
597
|
if (prevPlaybackType !== this._playbackType) {
|
|
@@ -590,7 +623,7 @@ export default class HlsPlayback extends HTML5Video {
|
|
|
590
623
|
}
|
|
591
624
|
// #EXT-X-PROGRAM-DATE-TIME
|
|
592
625
|
if (fragments[0].rawProgramDateTime) {
|
|
593
|
-
this._programDateTime = fragments[0].rawProgramDateTime;
|
|
626
|
+
this._programDateTime = Number(fragments[0].rawProgramDateTime);
|
|
594
627
|
}
|
|
595
628
|
if (this._playableRegionStartTime !== fragments[0].start) {
|
|
596
629
|
startTimeChanged = true;
|
|
@@ -601,7 +634,7 @@ export default class HlsPlayback extends HTML5Video {
|
|
|
601
634
|
// set the correlation to map to middle of the extrapolation window
|
|
602
635
|
this._localStartTimeCorrelation = {
|
|
603
636
|
local: this._now,
|
|
604
|
-
remote: (fragments[0].start +
|
|
637
|
+
remote: (fragments[0].start + this._extrapolatedWindowDuration / 2) * 1000,
|
|
605
638
|
};
|
|
606
639
|
}
|
|
607
640
|
else {
|
|
@@ -616,17 +649,19 @@ export default class HlsPlayback extends HTML5Video {
|
|
|
616
649
|
// reset correlation so that it sits at the beginning of the first available chunk
|
|
617
650
|
this._localStartTimeCorrelation = {
|
|
618
651
|
local: this._now,
|
|
619
|
-
remote: fragments[0].start * 1000
|
|
652
|
+
remote: fragments[0].start * 1000,
|
|
620
653
|
};
|
|
621
654
|
}
|
|
622
|
-
else if (startTime >
|
|
655
|
+
else if (startTime >
|
|
656
|
+
previousPlayableRegionStartTime + this._extrapolatedWindowDuration) {
|
|
623
657
|
// start time was past the end of the old extrapolation window (so would have been capped)
|
|
624
658
|
// see if now that time would be inside the window, and if it would be set the correlation
|
|
625
659
|
// so that it resumes from the time it was at at the end of the old window
|
|
626
660
|
// update the correlation so that the time starts counting again from the value it's on now
|
|
627
661
|
this._localStartTimeCorrelation = {
|
|
628
662
|
local: this._now,
|
|
629
|
-
remote: Math.max(fragments[0].start, previousPlayableRegionStartTime +
|
|
663
|
+
remote: Math.max(fragments[0].start, previousPlayableRegionStartTime +
|
|
664
|
+
this._extrapolatedWindowDuration) * 1000,
|
|
630
665
|
};
|
|
631
666
|
}
|
|
632
667
|
}
|
|
@@ -638,7 +673,8 @@ export default class HlsPlayback extends HTML5Video {
|
|
|
638
673
|
if (this._playbackType === Playback.LIVE) {
|
|
639
674
|
const fragmentTargetDuration = data.details.targetduration;
|
|
640
675
|
const hlsjsConfig = this.options.playback.hlsjsConfig || {};
|
|
641
|
-
const liveSyncDurationCount = hlsjsConfig.liveSyncDurationCount ||
|
|
676
|
+
const liveSyncDurationCount = hlsjsConfig.liveSyncDurationCount ||
|
|
677
|
+
HLSJS.DefaultConfig.liveSyncDurationCount;
|
|
642
678
|
const hiddenAreaDuration = fragmentTargetDuration * liveSyncDurationCount;
|
|
643
679
|
if (hiddenAreaDuration <= newDuration) {
|
|
644
680
|
newDuration -= hiddenAreaDuration;
|
|
@@ -662,7 +698,7 @@ export default class HlsPlayback extends HTML5Video {
|
|
|
662
698
|
// set the correlation to map to the end
|
|
663
699
|
this._localEndTimeCorrelation = {
|
|
664
700
|
local: this._now,
|
|
665
|
-
remote: endTime * 1000
|
|
701
|
+
remote: endTime * 1000,
|
|
666
702
|
};
|
|
667
703
|
}
|
|
668
704
|
else {
|
|
@@ -674,16 +710,17 @@ export default class HlsPlayback extends HTML5Video {
|
|
|
674
710
|
if (extrapolatedEndTime > endTime) {
|
|
675
711
|
this._localEndTimeCorrelation = {
|
|
676
712
|
local: this._now,
|
|
677
|
-
remote: endTime * 1000
|
|
713
|
+
remote: endTime * 1000,
|
|
678
714
|
};
|
|
679
715
|
}
|
|
680
|
-
else if (extrapolatedEndTime <
|
|
716
|
+
else if (extrapolatedEndTime <
|
|
717
|
+
endTime - this._extrapolatedWindowDuration) {
|
|
681
718
|
// our extrapolated end time is now earlier than the extrapolation window from the actual end time
|
|
682
719
|
// (maybe a chunk became available early)
|
|
683
720
|
// reset correlation so that it sits at the beginning of the extrapolation window from the end time
|
|
684
721
|
this._localEndTimeCorrelation = {
|
|
685
722
|
local: this._now,
|
|
686
|
-
remote: (endTime - this._extrapolatedWindowDuration) * 1000
|
|
723
|
+
remote: (endTime - this._extrapolatedWindowDuration) * 1000,
|
|
687
724
|
};
|
|
688
725
|
}
|
|
689
726
|
else if (extrapolatedEndTime > previousEndTime) {
|
|
@@ -691,7 +728,7 @@ export default class HlsPlayback extends HTML5Video {
|
|
|
691
728
|
// set the correlation so that it resumes from the time it was at at the end of the old window
|
|
692
729
|
this._localEndTimeCorrelation = {
|
|
693
730
|
local: this._now,
|
|
694
|
-
remote: previousEndTime * 1000
|
|
731
|
+
remote: previousEndTime * 1000,
|
|
695
732
|
};
|
|
696
733
|
}
|
|
697
734
|
}
|
|
@@ -729,13 +766,14 @@ export default class HlsPlayback extends HTML5Video {
|
|
|
729
766
|
const currentLevel = this._hls.levels[data.level];
|
|
730
767
|
if (currentLevel) {
|
|
731
768
|
// TODO should highDefinition be private and maybe have a read only accessor if it's used somewhere
|
|
732
|
-
this.highDefinition =
|
|
769
|
+
this.highDefinition =
|
|
770
|
+
currentLevel.height >= 720 || currentLevel.bitrate / 1000 >= 2000;
|
|
733
771
|
this.trigger(Events.PLAYBACK_HIGHDEFINITIONUPDATE, this.highDefinition);
|
|
734
772
|
this.trigger(Events.PLAYBACK_BITRATE, {
|
|
735
773
|
height: currentLevel.height,
|
|
736
774
|
width: currentLevel.width,
|
|
737
775
|
bitrate: currentLevel.bitrate,
|
|
738
|
-
level: data.level
|
|
776
|
+
level: data.level,
|
|
739
777
|
});
|
|
740
778
|
}
|
|
741
779
|
}
|
|
@@ -744,21 +782,34 @@ export default class HlsPlayback extends HTML5Video {
|
|
|
744
782
|
// - the duration does not include content after hlsjs's live sync point
|
|
745
783
|
// - the playable region duration is longer than the configured duration to enable dvr after
|
|
746
784
|
// - the playback type is LIVE.
|
|
747
|
-
return (this._durationExcludesAfterLiveSyncPoint &&
|
|
785
|
+
return (this._durationExcludesAfterLiveSyncPoint &&
|
|
786
|
+
this._duration >= this._minDvrSize &&
|
|
787
|
+
this.getPlaybackType() === Playback.LIVE);
|
|
748
788
|
}
|
|
749
789
|
getPlaybackType() {
|
|
750
790
|
return this._playbackType;
|
|
751
791
|
}
|
|
752
792
|
isSeekEnabled() {
|
|
753
|
-
return
|
|
793
|
+
return this._playbackType === Playback.VOD || this.dvrEnabled;
|
|
794
|
+
}
|
|
795
|
+
triggerError(error) {
|
|
796
|
+
trace(`${T} triggerError`, { error });
|
|
797
|
+
this.trigger(Events.PLAYBACK_ERROR, error);
|
|
798
|
+
this.stop();
|
|
754
799
|
}
|
|
755
800
|
}
|
|
756
801
|
HlsPlayback.canPlay = function (resource, mimeType) {
|
|
757
802
|
const resourceParts = resource.split('?')[0].match(/.*\.(.*)$/) || [];
|
|
758
|
-
const isHls = (
|
|
803
|
+
const isHls = (resourceParts.length > 1 && resourceParts[1].toLowerCase() === 'm3u8') ||
|
|
804
|
+
listContainsIgnoreCase(mimeType, [
|
|
805
|
+
'application/vnd.apple.mpegurl',
|
|
806
|
+
'application/x-mpegURL',
|
|
807
|
+
]);
|
|
759
808
|
const hasSupport = HLSJS.isSupported();
|
|
760
809
|
trace(`${T} canPlay`, {
|
|
761
|
-
hasSupport,
|
|
810
|
+
hasSupport,
|
|
811
|
+
isHls,
|
|
812
|
+
resource,
|
|
762
813
|
});
|
|
763
814
|
return !!(hasSupport && isHls);
|
|
764
815
|
};
|
package/lib/types.d.ts
CHANGED
|
@@ -38,7 +38,7 @@ export type PlaybackType = 'live' | 'vod';
|
|
|
38
38
|
/**
|
|
39
39
|
* @beta
|
|
40
40
|
*/
|
|
41
|
-
export type MediaTransport = 'dash' | 'hls'
|
|
41
|
+
export type MediaTransport = 'dash' | 'hls';
|
|
42
42
|
/**
|
|
43
43
|
* @beta
|
|
44
44
|
*/
|
|
@@ -49,7 +49,7 @@ export type TransportPreference = MediaTransport | 'auto';
|
|
|
49
49
|
*/
|
|
50
50
|
export type PlayerPlugin = {
|
|
51
51
|
new (...args: any[]): unknown;
|
|
52
|
-
|
|
52
|
+
name: string;
|
|
53
53
|
};
|
|
54
54
|
/**
|
|
55
55
|
* Configuration options for the player
|
|
@@ -143,7 +143,7 @@ export interface PlayerConfig extends Record<string, unknown> {
|
|
|
143
143
|
/**
|
|
144
144
|
* Localization strings for the player UI.
|
|
145
145
|
*/
|
|
146
|
-
strings
|
|
146
|
+
strings?: TranslationSettings;
|
|
147
147
|
}
|
|
148
148
|
/**
|
|
149
149
|
* @beta
|
package/lib/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAEA;;;;;;;GAOG;AACH,MAAM,WAAW,qBAAqB;IACpC;;OAEG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAA;IAEjB;;OAEG;IACH,MAAM,EAAE,MAAM,CAAA;CACf;AAED;;;GAGG;AACH,MAAM,MAAM,iBAAiB,GAAG,MAAM,GAAG,qBAAqB,CAAA;AAE9D;;GAEG;AACH,MAAM,MAAM,cAAc,GAAG,KAAK,GAAG,QAAQ,GAAG,MAAM,GAAG,KAAK,GAAG,MAAM,CAAA;AAEvE;;;GAGG;AACH,MAAM,MAAM,mBAAmB,GAAG,cAAc,GAAG,OAAO,CAAA;AAE1D;;;GAGG;AACH,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG,KAAK,CAAA;AAEzC;;GAEG;AACH,MAAM,MAAM,cAAc,GAAG,MAAM,GAAG,KAAK,CAAA;AAE3C;;GAEG;AACH,MAAM,MAAM,mBAAmB,GAAG,cAAc,GAAG,MAAM,CAAA;AAEzD;;;GAGG;AACH,MAAM,MAAM,YAAY,GAAG;IACzB,KAAK,GAAG,IAAI,EAAE,GAAG,EAAE,GAAG,OAAO,CAAA;IAC7B,IAAI,EAAE,MAAM,CAAA;CACb,CAAA;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;AACH,MAAM,WAAW,YAAa,SAAQ,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAC3D;;;OAGG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAA;IAElB;;;;OAIG;IACH,IAAI,CAAC,EAAE,YAAY,CAAA;IAEnB;;;OAGG;IACH,KAAK,CAAC,EAAE,mBAAmB,CAAA;IAE3B;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAA;IAEjB;;;;OAIG;IACH,IAAI,CAAC,EAAE,OAAO,CAAA;IAEd;;;OAGG;IACH,IAAI,CAAC,EAAE,OAAO,CAAA;IAEd;;;;OAIG;IACH,YAAY,CAAC,EAAE,YAAY,CAAA;IAE3B;;;OAGG;IACH,iBAAiB,CAAC,EAAE,mBAAmB,CAAA;IAEvC;;OAEG;IACH,OAAO,EAAE,iBAAiB,EAAE,CAAA;IAE5B;;OAEG;IACH,OAAO,CAAC,EAAE,mBAAmB,CAAA;CAC9B;AAED;;GAEG;AACH,MAAM,MAAM,OAAO,GAAG,MAAM,CAAA;AAE5B;;GAEG;AACH,MAAM,MAAM,cAAc,GAAG,MAAM,CAAA;AAEnC;;;GAGG;AACH,MAAM,MAAM,YAAY,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;AAElD;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,MAAM,mBAAmB,GAAG,OAAO,CACvC,MAAM,CAAC,OAAO,EAAE,MAAM,CAAC,cAAc,EAAE,MAAM,CAAC,CAAC,CAChD,CAAA;AAED;;;GAGG;AACH,oBAAY,WAAW;IACrB;;OAEG;IACH,KAAK,UAAU;IACf;;OAEG;IACH,IAAI,SAAS;IACb;;OAEG;IACH,KAAK,UAAU;IACf;;OAEG;IACH,IAAI,SAAS;IACb;;OAEG;IACH,KAAK,UAAU;CAChB"}
|
|
@@ -1,11 +1,19 @@
|
|
|
1
|
-
import type { PlayerMediaSource, TransportPreference } from '../types';
|
|
1
|
+
import type { PlayerMediaSource, PlayerMediaSourceDesc, TransportPreference } from '../types';
|
|
2
2
|
export type SourceVariants = {
|
|
3
|
-
dash:
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
mpegts: string | null;
|
|
3
|
+
dash: PlayerMediaSourceDesc | null;
|
|
4
|
+
hls: PlayerMediaSourceDesc | null;
|
|
5
|
+
mpegts: PlayerMediaSourceDesc | null;
|
|
7
6
|
};
|
|
7
|
+
/**
|
|
8
|
+
*
|
|
9
|
+
* @param sources
|
|
10
|
+
* @deprecated
|
|
11
|
+
* @returns
|
|
12
|
+
*/
|
|
8
13
|
export declare function buildSourcesSet(sources: PlayerMediaSource[]): SourceVariants;
|
|
9
|
-
export declare function
|
|
14
|
+
export declare function buildMediaSourcesList(sources: PlayerMediaSourceDesc[], priorityTransport?: TransportPreference): PlayerMediaSourceDesc[];
|
|
10
15
|
export declare function unwrapSource(s: PlayerMediaSource): string;
|
|
16
|
+
export declare function wrapSource(s: PlayerMediaSource): PlayerMediaSourceDesc;
|
|
17
|
+
export declare function isDashSource(source: string, mimeType?: string): boolean;
|
|
18
|
+
export declare function isHlsSource(source: string, mimeType?: string): boolean;
|
|
11
19
|
//# sourceMappingURL=mediaSources.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mediaSources.d.ts","sourceRoot":"","sources":["../../src/utils/mediaSources.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"mediaSources.d.ts","sourceRoot":"","sources":["../../src/utils/mediaSources.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,iBAAiB,EACjB,qBAAqB,EACrB,mBAAmB,EACpB,MAAM,UAAU,CAAA;AAGjB,MAAM,MAAM,cAAc,GAAG;IAC3B,IAAI,EAAE,qBAAqB,GAAG,IAAI,CAAA;IAClC,GAAG,EAAE,qBAAqB,GAAG,IAAI,CAAA;IACjC,MAAM,EAAE,qBAAqB,GAAG,IAAI,CAAA;CACrC,CAAA;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,iBAAiB,EAAE,GAAG,cAAc,CAiB5E;AAED,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,qBAAqB,EAAE,EAChC,iBAAiB,GAAE,mBAA4B,GAC9C,qBAAqB,EAAE,CA4BzB;AAED,wBAAgB,YAAY,CAAC,CAAC,EAAE,iBAAiB,GAAG,MAAM,CAEzD;AAED,wBAAgB,UAAU,CAAC,CAAC,EAAE,iBAAiB,GAAG,qBAAqB,CAEtE;AAYD,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,WAK7D;AAED,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,WAO5D"}
|