@gcorevideo/player 2.4.0 → 2.4.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.
@@ -2,208 +2,228 @@
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
4
 
5
- import { Events, HTML5Video, Log, Playback, /* PlayerError, */ Utils } from '@clappr/core';
6
- import assert from 'assert'; // uses Node.js's assert types
7
- import DASHJS, {
5
+ import {
6
+ Events,
7
+ HTML5Video,
8
+ Log,
9
+ Playback,
10
+ Utils,
11
+ } from '@clappr/core'
12
+ import assert from 'assert' // uses Node.js's assert types
13
+ import DASHJS, {
8
14
  ErrorEvent as DashErrorEvent,
9
15
  PlaybackErrorEvent as DashPlaybackErrorEvent,
10
16
  type BitrateInfo,
11
17
  MetricEvent as DashMetricEvent,
12
- IManifestInfo
13
- } from 'dashjs';
14
- import { trace } from '../../trace/index.js';
18
+ IManifestInfo,
19
+ } from 'dashjs'
20
+ import { trace } from '../../trace/index.js'
15
21
 
16
- import { Duration, TimePosition, TimeValue } from '../../playback.types.js';
22
+ import { Duration, TimePosition, TimeValue } from '../../playback.types.js'
17
23
 
18
- const AUTO = -1;
24
+ const AUTO = -1
19
25
 
20
- const { now } = Utils;
26
+ const { now } = Utils
21
27
 
22
28
  type PlaybackType =
23
29
  | typeof Playback.VOD
24
30
  | typeof Playback.LIVE
25
31
  | typeof Playback.AOD
26
- | typeof Playback.NO_OP;
32
+ | typeof Playback.NO_OP
27
33
 
28
- type PlaylistType = string; // TODO union
34
+ type PlaylistType = string // TODO union
29
35
 
30
36
  type QualityLevel = {
31
- id: number;
32
- level: BitrateInfo;
33
- };
37
+ id: number
38
+ level: BitrateInfo
39
+ }
34
40
 
35
41
  type LocalTimeCorrelation = {
36
- local: number;
37
- remote: number;
42
+ local: number
43
+ remote: number
38
44
  }
39
45
 
40
- const T = "DashPlayback";
46
+ const T = 'DashPlayback'
41
47
 
42
48
  export default class DashPlayback extends HTML5Video {
43
- _levels: QualityLevel[] | null = null;
49
+ _levels: QualityLevel[] | null = null
44
50
 
45
- _currentLevel: number | null = null;
51
+ _currentLevel: number | null = null
46
52
 
47
- _durationExcludesAfterLiveSyncPoint: boolean = false;
53
+ _durationExcludesAfterLiveSyncPoint: boolean = false
48
54
 
49
- _isReadyState: boolean = false;
55
+ _isReadyState: boolean = false
50
56
 
51
- _playableRegionDuration: number = 0;
57
+ _playableRegionDuration: number = 0
52
58
 
53
- _playableRegionStartTime: number = 0;
59
+ _playableRegionStartTime: number = 0
54
60
 
55
- _playbackType: PlaybackType = Playback.VOD;
61
+ _playbackType: PlaybackType = Playback.VOD
56
62
 
57
- _playlistType: PlaylistType | null = null;
63
+ _playlistType: PlaylistType | null = null
58
64
 
59
65
  // #EXT-X-PROGRAM-DATE-TIME
60
- _programDateTime: TimeValue = 0;
66
+ _programDateTime: TimeValue = 0
61
67
 
62
- _dash: DASHJS.MediaPlayerClass | null = null;
68
+ _dash: DASHJS.MediaPlayerClass | null = null
63
69
 
64
- _extrapolatedWindowDuration: number = 0;
70
+ _extrapolatedWindowDuration: number = 0
65
71
 
66
- _extrapolatedWindowNumSegments: number = 0;
72
+ _extrapolatedWindowNumSegments: number = 0
67
73
 
68
- _lastDuration: Duration | null = null;
74
+ _lastDuration: Duration | null = null
69
75
 
70
- _lastTimeUpdate: TimePosition = { current: 0, total: 0 };
76
+ _lastTimeUpdate: TimePosition = { current: 0, total: 0 }
71
77
 
72
- _localStartTimeCorrelation: LocalTimeCorrelation | null = null;
78
+ _localStartTimeCorrelation: LocalTimeCorrelation | null = null
73
79
 
74
- _localEndTimeCorrelation: LocalTimeCorrelation | null = null;
80
+ _localEndTimeCorrelation: LocalTimeCorrelation | null = null
75
81
 
76
- _recoverAttemptsRemaining: number = 0;
82
+ _recoverAttemptsRemaining: number = 0
77
83
 
78
- _recoveredAudioCodecError = false;
84
+ _recoveredAudioCodecError = false
79
85
 
80
- _recoveredDecodingError = false;
86
+ _recoveredDecodingError = false
81
87
 
82
- startChangeQuality = false;
88
+ startChangeQuality = false
83
89
 
84
- manifestInfo: IManifestInfo | null = null;
90
+ manifestInfo: IManifestInfo | null = null
85
91
 
86
92
  // #EXT-X-TARGETDURATION
87
- _segmentTargetDuration: Duration | null = null;
93
+ _segmentTargetDuration: Duration | null = null
88
94
 
89
- _timeUpdateTimer: ReturnType<typeof setInterval> | null = null;
95
+ _timeUpdateTimer: ReturnType<typeof setInterval> | null = null
90
96
 
91
97
  get name() {
92
- return 'dash';
98
+ return 'dash'
93
99
  }
94
100
 
95
101
  get levels(): QualityLevel[] {
96
- return this._levels || [];
102
+ return this._levels || []
97
103
  }
98
104
 
99
105
  get currentLevel(): number {
100
106
  if (this._currentLevel === null) {
101
- return AUTO;
107
+ return AUTO
102
108
  }
103
109
  // 0 is a valid level ID
104
- return this._currentLevel;
110
+ return this._currentLevel
105
111
  }
106
112
 
107
113
  get isReady() {
108
- return this._isReadyState;
114
+ return this._isReadyState
109
115
  }
110
116
 
111
117
  set currentLevel(id) {
112
- this._currentLevel = id;
118
+ this._currentLevel = id
113
119
 
114
- this.trigger(Events.PLAYBACK_LEVEL_SWITCH_START);
120
+ this.trigger(Events.PLAYBACK_LEVEL_SWITCH_START)
115
121
  const cfg = {
116
122
  streaming: {
117
123
  abr: {
118
124
  autoSwitchBitrate: {
119
125
  video: id === -1,
120
126
  },
121
- ABRStrategy: 'abrL2A'
122
- }
127
+ ABRStrategy: 'abrL2A',
128
+ },
123
129
  },
124
- };
130
+ }
125
131
 
126
- assert.ok(this._dash, 'An instance of dashjs MediaPlayer is required to switch levels');
127
- const dash = this._dash;
128
- this.options.dash && dash.updateSettings({ ...this.options.dash, ...cfg });
132
+ assert.ok(
133
+ this._dash,
134
+ 'An instance of dashjs MediaPlayer is required to switch levels',
135
+ )
136
+ const dash = this._dash
137
+ this.options.dash && dash.updateSettings({ ...this.options.dash, ...cfg })
129
138
  if (id !== -1) {
130
- this._dash.setQualityFor('video', id);
139
+ this._dash.setQualityFor('video', id)
131
140
  }
132
141
  if (this._playbackType === Playback.VOD) {
133
- const curr_time = this._dash.time();
142
+ const curr_time = this._dash.time()
134
143
 
135
- this.startChangeQuality = true;
136
- dash.seek(0);
144
+ this.startChangeQuality = true
145
+ dash.seek(0)
137
146
  setTimeout(() => {
138
- dash.seek(curr_time);
139
- dash.play();
140
- this.startChangeQuality = false;
141
- }, 100);
147
+ dash.seek(curr_time)
148
+ dash.play()
149
+ this.startChangeQuality = false
150
+ }, 100)
142
151
  }
143
152
  }
144
153
 
145
154
  get _startTime() {
146
- if (this._playbackType === Playback.LIVE && this._playlistType !== 'EVENT') {
147
- return this._extrapolatedStartTime;
155
+ if (
156
+ this._playbackType === Playback.LIVE &&
157
+ this._playlistType !== 'EVENT'
158
+ ) {
159
+ return this._extrapolatedStartTime
148
160
  }
149
161
 
150
- return this._playableRegionStartTime;
162
+ return this._playableRegionStartTime
151
163
  }
152
164
 
153
165
  get _now() {
154
- return now();
166
+ return now()
155
167
  }
156
168
 
157
169
  // the time in the video element which should represent the start of the sliding window
158
170
  // extrapolated to increase in real time (instead of jumping as the early segments are removed)
159
171
  get _extrapolatedStartTime() {
160
172
  if (!this._localStartTimeCorrelation) {
161
- return this._playableRegionStartTime;
173
+ return this._playableRegionStartTime
162
174
  }
163
175
 
164
- const corr = this._localStartTimeCorrelation;
165
- const timePassed = this._now - corr.local;
166
- const extrapolatedWindowStartTime = (corr.remote + timePassed) / 1000;
176
+ const corr = this._localStartTimeCorrelation
177
+ const timePassed = this._now - corr.local
178
+ const extrapolatedWindowStartTime = (corr.remote + timePassed) / 1000
167
179
 
168
180
  // cap at the end of the extrapolated window duration
169
- return Math.min(extrapolatedWindowStartTime, this._playableRegionStartTime + this._extrapolatedWindowDuration);
181
+ return Math.min(
182
+ extrapolatedWindowStartTime,
183
+ this._playableRegionStartTime + this._extrapolatedWindowDuration,
184
+ )
170
185
  }
171
186
 
172
187
  // the time in the video element which should represent the end of the content
173
188
  // extrapolated to increase in real time (instead of jumping as segments are added)
174
189
  get _extrapolatedEndTime() {
175
- const actualEndTime = this._playableRegionStartTime + this._playableRegionDuration;
190
+ const actualEndTime =
191
+ this._playableRegionStartTime + this._playableRegionDuration
176
192
 
177
193
  if (!this._localEndTimeCorrelation) {
178
- return actualEndTime;
194
+ return actualEndTime
179
195
  }
180
196
 
181
- const corr = this._localEndTimeCorrelation;
182
- const timePassed = this._now - corr.local;
183
- const extrapolatedEndTime = (corr.remote + timePassed) / 1000;
197
+ const corr = this._localEndTimeCorrelation
198
+ const timePassed = this._now - corr.local
199
+ const extrapolatedEndTime = (corr.remote + timePassed) / 1000
184
200
 
185
- return Math.max(actualEndTime - this._extrapolatedWindowDuration, Math.min(extrapolatedEndTime, actualEndTime));
201
+ return Math.max(
202
+ actualEndTime - this._extrapolatedWindowDuration,
203
+ Math.min(extrapolatedEndTime, actualEndTime),
204
+ )
186
205
  }
187
206
 
188
207
  get _duration() {
189
208
  if (!this._dash) {
190
- return Infinity;
209
+ return Infinity
191
210
  }
192
- return this._dash.duration() ?? Infinity;
211
+ return this._dash.duration() ?? Infinity
193
212
  }
194
213
 
195
214
  constructor(options: any, i18n: string, playerError?: any) {
196
- super(options, i18n, playerError);
215
+ super(options, i18n, playerError)
197
216
  // backwards compatibility (TODO: remove on 0.3.0)
198
217
  // this.options.playback || (this.options.playback = this.options);
199
218
  // The size of the start time extrapolation window measured as a multiple of segments.
200
219
  // Should be 2 or higher, or 0 to disable. Should only need to be increased above 2 if more than one segment is
201
220
  // removed from the start of the playlist at a time. E.g if the playlist is cached for 10 seconds and new chunks are
202
221
  // added/removed every 5.
203
- this._extrapolatedWindowNumSegments = this.options.playback?.extrapolatedWindowNumSegments ?? 2;
222
+ this._extrapolatedWindowNumSegments =
223
+ this.options.playback?.extrapolatedWindowNumSegments ?? 2
204
224
 
205
225
  if (this.options.playbackType) {
206
- this._playbackType = this.options.playbackType;
226
+ this._playbackType = this.options.playbackType
207
227
  }
208
228
  // this._lastTimeUpdate = { current: 0, total: 0 };
209
229
  // this._lastDuration = null;
@@ -237,70 +257,85 @@ export default class DashPlayback extends HTML5Video {
237
257
  // #EXT-X-PLAYLIST-TYPE
238
258
  // this._playlistType = null;
239
259
  if (this.options.hlsRecoverAttempts) {
240
- this._recoverAttemptsRemaining = this.options.hlsRecoverAttempts;
260
+ this._recoverAttemptsRemaining = this.options.hlsRecoverAttempts
241
261
  }
242
262
  }
243
263
 
244
264
  _setup() {
245
- const dash = DASHJS.MediaPlayer().create();
246
- this._dash = dash;
247
- this._dash.initialize();
265
+ const dash = DASHJS.MediaPlayer().create()
266
+ this._dash = dash
267
+ this._dash.initialize()
248
268
 
249
- const cfg = this.options.dash ?? {};
269
+ const cfg = this.options.dash ?? {}
250
270
 
251
- cfg.streaming = cfg.streaming || {};
252
- cfg.streaming.text = cfg.streaming.text || { defaultEnabled: false };
271
+ cfg.streaming = cfg.streaming || {}
272
+ cfg.streaming.text = cfg.streaming.text || { defaultEnabled: false }
253
273
 
254
- this.options.dash && this._dash.updateSettings(cfg);
274
+ this.options.dash && this._dash.updateSettings(cfg)
255
275
 
256
- this._dash.attachView(this.el);
276
+ this._dash.attachView(this.el)
257
277
 
258
- this._dash.setAutoPlay(false);
259
- this._dash.attachSource(this.options.src);
278
+ this._dash.setAutoPlay(false)
279
+ this._dash.attachSource(this.options.src)
260
280
 
261
- this._dash.on(DASHJS.MediaPlayer.events.ERROR, this._onDASHJSSError);
262
- this._dash.on(DASHJS.MediaPlayer.events.PLAYBACK_ERROR, this._onPlaybackError);
281
+ this._dash.on(DASHJS.MediaPlayer.events.ERROR, this._onDASHJSSError)
282
+ this._dash.on(
283
+ DASHJS.MediaPlayer.events.PLAYBACK_ERROR,
284
+ this._onPlaybackError,
285
+ )
263
286
 
264
287
  this._dash.on(DASHJS.MediaPlayer.events.STREAM_INITIALIZED, () => {
265
- const bitrates = dash.getBitrateInfoListFor('video');
288
+ const bitrates = dash.getBitrateInfoListFor('video')
266
289
 
267
- this._updatePlaybackType();
268
- this._fillLevels(bitrates);
290
+ this._updatePlaybackType()
291
+ this._fillLevels(bitrates)
269
292
  dash.on(DASHJS.MediaPlayer.events.QUALITY_CHANGE_REQUESTED, (evt) => {
270
293
  // TODO
271
- assert.ok(this._levels, 'An array of levels is required to change quality');
272
- const newLevel = this._levels.find((level) => level.id === evt.newQuality); // TODO or simply this._levels[evt.newQuality]?
273
- assert.ok(newLevel, 'A valid level is required to change quality');
274
- this.onLevelSwitch(newLevel.level);
275
- });
276
- });
277
-
278
- this._dash.on(DASHJS.MediaPlayer.events.METRIC_ADDED, (e: DashMetricEvent) => {
279
- // Listen for the first manifest request in order to update player UI
280
- if ((e.metric as string) === 'DVRInfo') { // TODO fix typings
281
- assert.ok(this._dash, 'An instance of dashjs MediaPlayer is required to get metrics');
282
- const dvrInfo = this._dash.getDashMetrics().getCurrentDVRInfo('video');
283
- if (dvrInfo) {
284
- // Extract time info
285
- this.manifestInfo = dvrInfo.manifestInfo;
294
+ assert.ok(
295
+ this._levels,
296
+ 'An array of levels is required to change quality',
297
+ )
298
+ const newLevel = this._levels.find(
299
+ (level) => level.id === evt.newQuality,
300
+ ) // TODO or simply this._levels[evt.newQuality]?
301
+ assert.ok(newLevel, 'A valid level is required to change quality')
302
+ this.onLevelSwitch(newLevel.level)
303
+ })
304
+ })
305
+
306
+ this._dash.on(
307
+ DASHJS.MediaPlayer.events.METRIC_ADDED,
308
+ (e: DashMetricEvent) => {
309
+ // Listen for the first manifest request in order to update player UI
310
+ if ((e.metric as string) === 'DVRInfo') {
311
+ // TODO fix typings
312
+ assert.ok(
313
+ this._dash,
314
+ 'An instance of dashjs MediaPlayer is required to get metrics',
315
+ )
316
+ const dvrInfo = this._dash.getDashMetrics().getCurrentDVRInfo('video')
317
+ if (dvrInfo) {
318
+ // Extract time info
319
+ this.manifestInfo = dvrInfo.manifestInfo
320
+ }
286
321
  }
287
- }
288
- });
322
+ },
323
+ )
289
324
 
290
325
  this._dash.on(DASHJS.MediaPlayer.events.PLAYBACK_RATE_CHANGED, () => {
291
- this.trigger('dash:playback-rate-changed');
292
- });
326
+ this.trigger('dash:playback-rate-changed')
327
+ })
293
328
  }
294
329
 
295
330
  render() {
296
- this._ready();
331
+ this._ready()
297
332
 
298
- return super.render();
333
+ return super.render()
299
334
  }
300
335
 
301
336
  _ready() {
302
- this._isReadyState = true;
303
- this.trigger(Events.PLAYBACK_READY, this.name);
337
+ this._isReadyState = true
338
+ this.trigger(Events.PLAYBACK_READY, this.name)
304
339
  }
305
340
 
306
341
  // TODO
@@ -333,89 +368,100 @@ export default class DashPlayback extends HTML5Video {
333
368
  }
334
369
 
335
370
  _startTimeUpdateTimer() {
336
- this._stopTimeUpdateTimer();
371
+ this._stopTimeUpdateTimer()
337
372
  this._timeUpdateTimer = setInterval(() => {
338
- this._onDurationChange();
339
- this._onTimeUpdate();
340
- }, 100);
373
+ this._onDurationChange()
374
+ this._onTimeUpdate()
375
+ }, 100)
341
376
  }
342
377
 
343
378
  _stopTimeUpdateTimer() {
344
379
  if (this._timeUpdateTimer) {
345
- clearInterval(this._timeUpdateTimer);
380
+ clearInterval(this._timeUpdateTimer)
346
381
  }
347
382
  }
348
383
 
349
384
  getProgramDateTime() {
350
- return this._programDateTime;
385
+ return this._programDateTime
351
386
  }
352
387
 
353
388
  // the duration on the video element itself should not be used
354
389
  // as this does not necesarily represent the duration of the stream
355
390
  // https://github.com/clappr/clappr/issues/668#issuecomment-157036678
356
391
  getDuration(): Duration {
357
- assert.ok(this._duration !== null, 'A valid duration is required to get the duration');
358
- return this._duration;
392
+ assert.ok(
393
+ this._duration !== null,
394
+ 'A valid duration is required to get the duration',
395
+ )
396
+ return this._duration
359
397
  }
360
398
 
361
399
  getCurrentTime(): TimeValue {
362
400
  // e.g. can be < 0 if user pauses near the start
363
401
  // eventually they will then be kicked to the end by hlsjs if they run out of buffer
364
402
  // before the official start time
365
- return this._dash ? this._dash.time() : 0;
403
+ return this._dash ? this._dash.time() : 0
366
404
  }
367
405
 
368
406
  // the time that "0" now represents relative to when playback started
369
407
  // for a stream with a sliding window this will increase as content is
370
408
  // removed from the beginning
371
409
  getStartTimeOffset(): TimeValue {
372
- return this._startTime;
410
+ return this._startTime
373
411
  }
374
412
 
375
413
  seekPercentage(percentage: number) {
376
- let seekTo = this._duration;
414
+ let seekTo = this._duration
377
415
 
378
416
  if (percentage > 0) {
379
- assert.ok(this._duration !== null, 'A valid duration is required to seek by percentage');
380
- seekTo = this._duration * (percentage / 100);
417
+ assert.ok(
418
+ this._duration !== null,
419
+ 'A valid duration is required to seek by percentage',
420
+ )
421
+ seekTo = this._duration * (percentage / 100)
381
422
  }
382
423
 
383
- assert.ok(seekTo !== null, 'A valid seek time is required');
384
- this.seek(seekTo);
424
+ assert.ok(seekTo !== null, 'A valid seek time is required')
425
+ this.seek(seekTo)
385
426
  }
386
427
 
387
428
  seek(time: TimeValue) {
388
429
  if (time < 0) {
389
430
  // eslint-disable-next-line max-len
390
- Log.warn('Attempt to seek to a negative time. Resetting to live point. Use seekToLivePoint() to seek to the live point.');
391
- time = this.getDuration();
431
+ Log.warn(
432
+ 'Attempt to seek to a negative time. Resetting to live point. Use seekToLivePoint() to seek to the live point.',
433
+ )
434
+ time = this.getDuration()
392
435
  }
393
- this.dvrEnabled && this._updateDvr(time < this.getDuration() - 10);
394
- assert.ok(this._dash, 'An instance of dashjs MediaPlayer is required to seek');
395
- this._dash.seek(time);
436
+ this.dvrEnabled && this._updateDvr(time < this.getDuration() - 10)
437
+ assert.ok(
438
+ this._dash,
439
+ 'An instance of dashjs MediaPlayer is required to seek',
440
+ )
441
+ this._dash.seek(time)
396
442
  }
397
443
 
398
444
  seekToLivePoint() {
399
- this.seek(this.getDuration());
445
+ this.seek(this.getDuration())
400
446
  }
401
447
 
402
448
  _updateDvr(status: boolean) {
403
- this.trigger(Events.PLAYBACK_DVR, status);
404
- this.trigger(Events.PLAYBACK_STATS_ADD, { 'dvr': status });
449
+ this.trigger(Events.PLAYBACK_DVR, status)
450
+ this.trigger(Events.PLAYBACK_STATS_ADD, { dvr: status })
405
451
  }
406
452
 
407
453
  _updateSettings() {
408
454
  if (this._playbackType === Playback.VOD) {
409
- this.settings.left = ['playpause', 'position', 'duration'];
455
+ this.settings.left = ['playpause', 'position', 'duration']
410
456
  // this.settings.left.push('playstop');
411
457
  } else if (this.dvrEnabled) {
412
- this.settings.left = ['playpause'];
458
+ this.settings.left = ['playpause']
413
459
  } else {
414
- this.settings.left = ['playstop'];
460
+ this.settings.left = ['playstop']
415
461
  }
416
462
 
417
- this.settings.seekEnabled = this.isSeekEnabled();
418
- this.trigger(Events.PLAYBACK_SETTINGSUPDATE);
463
+ this.settings.seekEnabled = this.isSeekEnabled()
464
+ this.trigger(Events.PLAYBACK_SETTINGSUPDATE)
419
465
  }
420
466
 
421
467
  _onPlaybackError = (event: DashPlaybackErrorEvent) => {
@@ -426,34 +472,37 @@ export default class DashPlayback extends HTML5Video {
426
472
  // TODO
427
473
  // only report/handle errors if they are fatal
428
474
  // hlsjs should automatically handle non fatal errors
429
- this._stopTimeUpdateTimer();
475
+ this._stopTimeUpdateTimer()
430
476
  if (event.error === 'capability' && event.event === 'mediasource') {
431
477
  // No support for MSE
432
- const formattedError = this.createError(event.error);
433
-
434
- this.trigger(Events.PLAYBACK_ERROR, formattedError);
435
- Log.error('The media cannot be played because it requires a feature ' +
436
- 'that your browser does not support.');
437
- } else if (event.error === 'manifestError' && (
478
+ const formattedError = this.createError(event.error)
479
+
480
+ this.trigger(Events.PLAYBACK_ERROR, formattedError)
481
+ Log.error(
482
+ 'The media cannot be played because it requires a feature ' +
483
+ 'that your browser does not support.',
484
+ )
485
+ } else if (
486
+ event.error === 'manifestError' &&
438
487
  // Manifest type not supported
439
- (event.event.id === 'createParser') ||
440
- // Codec(s) not supported
441
- (event.event.id === 'codec') ||
442
- // No streams available to stream
443
- (event.event.id === 'nostreams') ||
444
- // Error creating Stream object
445
- (event.event.id === 'nostreamscomposed') ||
446
- // syntax error parsing the manifest
447
- (event.event.id === 'parse') ||
448
- // a stream has multiplexed audio+video
449
- (event.event.id === 'multiplexedrep')
450
- )) {
488
+ (event.event.id === 'createParser' ||
489
+ // Codec(s) not supported
490
+ event.event.id === 'codec' ||
491
+ // No streams available to stream
492
+ event.event.id === 'nostreams' ||
493
+ // Error creating Stream object
494
+ event.event.id === 'nostreamscomposed' ||
495
+ // syntax error parsing the manifest
496
+ event.event.id === 'parse' ||
497
+ // a stream has multiplexed audio+video
498
+ event.event.id === 'multiplexedrep')
499
+ ) {
451
500
  // These errors have useful error messages, so we forward it on
452
- const formattedError = this.createError(event.error);
501
+ const formattedError = this.createError(event.error)
453
502
 
454
- this.trigger(Events.PLAYBACK_ERROR, formattedError);
503
+ this.trigger(Events.PLAYBACK_ERROR, formattedError)
455
504
  if (event.error) {
456
- Log.error(event.event.message);
505
+ Log.error(event.event.message)
457
506
  }
458
507
  } else if (event.error === 'mediasource') {
459
508
  // This error happens when dash.js fails to allocate a SourceBuffer
@@ -462,167 +511,193 @@ export default class DashPlayback extends HTML5Video {
462
511
  // (audio/video/text) failed allocation.
463
512
  // If it's a `MediaError`, dash.js inspects the error object for
464
513
  // additional information to append to the error type.
465
- const formattedError = this.createError(event.error);
466
-
467
- this.trigger(Events.PLAYBACK_ERROR, formattedError);
468
- Log.error(event.event);
469
- } else if (event.error === 'capability' && event.event === 'encryptedmedia') {
514
+ const formattedError = this.createError(event.error)
515
+
516
+ this.trigger(Events.PLAYBACK_ERROR, formattedError)
517
+ Log.error(event.event)
518
+ } else if (
519
+ event.error === 'capability' &&
520
+ event.event === 'encryptedmedia'
521
+ ) {
470
522
  // Browser doesn't support EME
471
523
 
472
- const formattedError = this.createError(event.error);
524
+ const formattedError = this.createError(event.error)
473
525
 
474
- this.trigger(Events.PLAYBACK_ERROR, formattedError);
475
- Log.error('The media cannot be played because it requires encryption ' +
476
- 'that your browser does not support.');
526
+ this.trigger(Events.PLAYBACK_ERROR, formattedError)
527
+ Log.error(
528
+ 'The media cannot be played because it requires encryption ' +
529
+ 'that your browser does not support.',
530
+ )
477
531
  } else if (event.error === 'key_session') {
478
532
  // This block handles pretty much all errors thrown by the
479
533
  // encryption subsystem
480
- const formattedError = this.createError(event.error);
534
+ const formattedError = this.createError(event.error)
481
535
 
482
- this.trigger(Events.PLAYBACK_ERROR, formattedError);
483
- Log.error(event.event);
536
+ this.trigger(Events.PLAYBACK_ERROR, formattedError)
537
+ Log.error(event.event)
484
538
  } else if (event.error === 'download') {
485
- const formattedError = this.createError(event.error);
486
-
487
- this.trigger(Events.PLAYBACK_ERROR, formattedError);
488
- Log.error('The media playback was aborted because too many consecutive ' +
489
- 'download errors occurred.');
490
- // } else if (event.error === 'mssError') {
491
- // const formattedError = this.createError(event.error);
492
-
493
- // this.trigger(Events.PLAYBACK_ERROR, formattedError);
494
- // if (event.error) {
495
- // Log.error(event.error.message);
496
- // }
539
+ const formattedError = this.createError(event.error)
540
+
541
+ this.trigger(Events.PLAYBACK_ERROR, formattedError)
542
+ Log.error(
543
+ 'The media playback was aborted because too many consecutive ' +
544
+ 'download errors occurred.',
545
+ )
546
+ // } else if (event.error === 'mssError') {
547
+ // const formattedError = this.createError(event.error);
548
+
549
+ // this.trigger(Events.PLAYBACK_ERROR, formattedError);
550
+ // if (event.error) {
551
+ // Log.error(event.error.message);
552
+ // }
497
553
  } else {
498
554
  // ignore the error
499
- if (typeof event.error === "object") {
500
- const formattedError = this.createError(event.error);
555
+ if (typeof event.error === 'object') {
556
+ const formattedError = this.createError(event.error)
501
557
 
502
- this.trigger(Events.PLAYBACK_ERROR, formattedError);
503
- Log.error(event.error.message);
558
+ this.trigger(Events.PLAYBACK_ERROR, formattedError)
559
+ Log.error(event.error.message)
504
560
  } else {
505
- Log.error(event.error);
561
+ Log.error(event.error)
506
562
  }
507
- return;
563
+ return
508
564
  }
509
565
 
510
566
  // only reset the dash player in 10ms async, so that the rest of the
511
567
  // calling function finishes
512
568
  setTimeout(() => {
513
- assert.ok(this._dash, 'An instance of dashjs MediaPlayer is required to reset');
514
- this._dash.reset();
515
- }, 10);
569
+ assert.ok(
570
+ this._dash,
571
+ 'An instance of dashjs MediaPlayer is required to reset',
572
+ )
573
+ this._dash.reset()
574
+ }, 10)
516
575
  }
517
576
 
518
577
  _onTimeUpdate() {
519
578
  if (this.startChangeQuality) {
520
- return;
579
+ return
521
580
  }
522
581
  const update = {
523
582
  current: this.getCurrentTime(),
524
583
  total: this.getDuration(),
525
- firstFragDateTime: this.getProgramDateTime()
526
- };
527
- const isSame = this._lastTimeUpdate && (
584
+ firstFragDateTime: this.getProgramDateTime(),
585
+ }
586
+ const isSame =
587
+ this._lastTimeUpdate &&
528
588
  update.current === this._lastTimeUpdate.current &&
529
- update.total === this._lastTimeUpdate.total);
589
+ update.total === this._lastTimeUpdate.total
530
590
 
531
591
  if (isSame) {
532
- return;
592
+ return
533
593
  }
534
- this._lastTimeUpdate = update;
535
- this.trigger(Events.PLAYBACK_TIMEUPDATE, update, this.name);
594
+ this._lastTimeUpdate = update
595
+ this.trigger(Events.PLAYBACK_TIMEUPDATE, update, this.name)
536
596
  }
537
597
 
538
598
  _onDurationChange() {
539
- const duration = this.getDuration();
599
+ const duration = this.getDuration()
540
600
 
541
601
  if (this._lastDuration === duration) {
542
- return;
602
+ return
543
603
  }
544
604
 
545
- this._lastDuration = duration;
546
- super._onDurationChange();
605
+ this._lastDuration = duration
606
+ super._onDurationChange()
547
607
  }
548
608
 
549
609
  get dvrEnabled() {
550
- assert.ok(this._dash, 'An instance of dashjs MediaPlayer is required to get the DVR status');
551
- return this._dash?.getDVRWindowSize() >= this._minDvrSize && this.getPlaybackType() === Playback.LIVE;
610
+ assert.ok(
611
+ this._dash,
612
+ 'An instance of dashjs MediaPlayer is required to get the DVR status',
613
+ )
614
+ return (
615
+ this._dash?.getDVRWindowSize() >= this._minDvrSize &&
616
+ this.getPlaybackType() === Playback.LIVE
617
+ )
552
618
  }
553
619
 
554
620
  _onProgress() {
555
621
  if (!this._dash) {
556
- return;
622
+ return
557
623
  }
558
624
 
559
- let buffer = this._dash.getDashMetrics().getCurrentBufferLevel('video');
625
+ let buffer = this._dash.getDashMetrics().getCurrentBufferLevel('video')
560
626
 
561
627
  if (!buffer) {
562
- buffer = this._dash.getDashMetrics().getCurrentBufferLevel('audio');
628
+ buffer = this._dash.getDashMetrics().getCurrentBufferLevel('audio')
563
629
  }
564
630
  const progress = {
565
631
  start: this.getCurrentTime(),
566
632
  current: this.getCurrentTime() + buffer,
567
- total: this.getDuration()
568
- };
633
+ total: this.getDuration(),
634
+ }
569
635
 
570
- this.trigger(Events.PLAYBACK_PROGRESS, progress, {});
636
+ this.trigger(Events.PLAYBACK_PROGRESS, progress, {})
571
637
  }
572
638
 
573
639
  play() {
574
- trace(`${T} play`, { dash: !!this._dash });
640
+ trace(`${T} play`, { dash: !!this._dash })
575
641
  if (!this._dash) {
576
- this._setup();
642
+ this._setup()
577
643
  }
578
644
 
579
- super.play();
580
- this._startTimeUpdateTimer();
645
+ super.play()
646
+ this._startTimeUpdateTimer()
581
647
  }
582
648
 
583
649
  pause() {
584
650
  if (!this._dash) {
585
- return;
651
+ return
586
652
  }
587
653
 
588
- super.pause();
654
+ super.pause()
589
655
  if (this.dvrEnabled) {
590
- this._updateDvr(true);
656
+ this._updateDvr(true)
591
657
  }
592
658
  }
593
659
 
594
660
  stop() {
595
661
  if (this._dash) {
596
- this._stopTimeUpdateTimer();
597
- this._dash.reset();
598
- super.stop();
599
- this._dash = null;
662
+ this._stopTimeUpdateTimer()
663
+ this._dash.reset()
664
+ super.stop()
665
+ this._dash = null
600
666
  }
601
667
  }
602
668
 
603
669
  destroy() {
604
- this._stopTimeUpdateTimer();
670
+ this._stopTimeUpdateTimer()
605
671
  if (this._dash) {
606
- this._dash.off(DASHJS.MediaPlayer.events.ERROR, this._onDASHJSSError);
607
- this._dash.off(DASHJS.MediaPlayer.events.PLAYBACK_ERROR, this._onPlaybackError);
608
- this._dash.off(DASHJS.MediaPlayer.events.MANIFEST_LOADED, this.getDuration);
609
- this._dash.reset();
672
+ this._dash.off(DASHJS.MediaPlayer.events.ERROR, this._onDASHJSSError)
673
+ this._dash.off(
674
+ DASHJS.MediaPlayer.events.PLAYBACK_ERROR,
675
+ this._onPlaybackError,
676
+ )
677
+ this._dash.off(
678
+ DASHJS.MediaPlayer.events.MANIFEST_LOADED,
679
+ this.getDuration,
680
+ )
681
+ this._dash.reset()
610
682
  }
611
- this._dash = null;
612
- return super.destroy();
683
+ this._dash = null
684
+ return super.destroy()
613
685
  }
614
686
 
615
687
  _updatePlaybackType() {
616
- assert.ok(this._dash, 'An instance of dashjs MediaPlayer is required to update the playback type');
617
- this._playbackType = this._dash.isDynamic() ? Playback.LIVE : Playback.VOD;
688
+ assert.ok(
689
+ this._dash,
690
+ 'An instance of dashjs MediaPlayer is required to update the playback type',
691
+ )
692
+ this._playbackType = this._dash.isDynamic() ? Playback.LIVE : Playback.VOD
618
693
  }
619
694
 
620
695
  _fillLevels(levels: BitrateInfo[]) {
621
696
  // TOOD check that levels[i].qualityIndex === i
622
697
  this._levels = levels.map((level) => {
623
- return { id: level.qualityIndex, level: level };
624
- });
625
- this.trigger(Events.PLAYBACK_LEVELS_AVAILABLE, this._levels);
698
+ return { id: level.qualityIndex, level: level }
699
+ })
700
+ this.trigger(Events.PLAYBACK_LEVELS_AVAILABLE, this._levels)
626
701
  }
627
702
 
628
703
  // _onLevelUpdated(_: any, data) {
@@ -771,26 +846,41 @@ export default class DashPlayback extends HTML5Video {
771
846
  height: currentLevel.height,
772
847
  width: currentLevel.width,
773
848
  bitrate: currentLevel.bitrate,
774
- level: currentLevel.qualityIndex
775
- });
849
+ level: currentLevel.qualityIndex,
850
+ })
776
851
  }
777
852
 
778
853
  getPlaybackType() {
779
- return this._playbackType;
854
+ return this._playbackType
780
855
  }
781
856
 
782
857
  isSeekEnabled() {
783
- return (this._playbackType === Playback.VOD || this.dvrEnabled);
858
+ return this._playbackType === Playback.VOD || this.dvrEnabled
784
859
  }
785
860
  }
786
861
 
787
862
  DashPlayback.canPlay = function (resource, mimeType) {
788
- const resourceParts = resource.split('?')[0].match(/.*\.(.*)$/) || [];
789
- const isDash = ((resourceParts.length > 1 && resourceParts[1].toLowerCase() === 'mpd') ||
790
- mimeType === 'application/dash+xml' || mimeType === 'video/mp4');
863
+ const resourceParts = resource.split('?')[0].match(/.*\.(.*)$/) || []
864
+ const isDash =
865
+ (resourceParts.length > 1 && resourceParts[1].toLowerCase() === 'mpd') ||
866
+ mimeType === 'application/dash+xml' ||
867
+ mimeType === 'video/mp4'
791
868
  // TODO check
792
- const ctor = window.MediaSource || ('WebKitMediaSource' in window ? window.WebKitMediaSource : undefined);
793
- const hasSupport = typeof ctor === 'function';
794
- trace(`${T} canPlay`, {hasSupport, isDash, resource});
795
- return !!(hasSupport && isDash);
796
- };
869
+ const ms = window.MediaSource
870
+ const mms =
871
+ 'ManagedMediaSource' in window ? window.ManagedMediaSource : undefined
872
+ const wms =
873
+ 'WebKitMediaSource' in window ? window.WebKitMediaSource : undefined
874
+ const ctor = ms || mms || wms
875
+
876
+ const hasSupport = typeof ctor === 'function'
877
+ trace(`${T} canPlay`, {
878
+ hasSupport,
879
+ isDash,
880
+ resource,
881
+ ms: typeof ms === 'function',
882
+ mms: typeof mms === 'function',
883
+ wms: typeof wms === 'function',
884
+ })
885
+ return !!(hasSupport && isDash)
886
+ }