@gcorevideo/player 2.17.0 → 2.18.0
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/dist/index.js +182 -109
- package/dist/player.d.ts +150 -24
- package/docs/api/player.containersize.md +19 -0
- package/docs/api/player.dashsettings.md +2 -0
- package/docs/api/player.errorlevel.md +1 -0
- package/docs/api/player.langtag.md +6 -0
- package/docs/api/player.md +57 -22
- package/docs/api/player.mediatransport.md +1 -0
- package/docs/api/player.playbackerror.code.md +2 -0
- package/docs/api/player.playbackerror.description.md +2 -0
- package/docs/api/player.playbackerror.level.md +2 -0
- package/docs/api/player.playbackerror.md +43 -4
- package/docs/api/player.playbackerror.message.md +2 -0
- package/docs/api/player.playbackerror.origin.md +21 -0
- package/docs/api/player.playbackerror.scope.md +16 -0
- package/docs/api/player.playbackerrorcode.md +4 -3
- package/docs/api/player.playbackmodule.md +1 -0
- package/docs/api/player.player.isdvrinuse.md +24 -0
- package/docs/api/player.player.isplaying.md +1 -1
- package/docs/api/player.player.md +15 -1
- package/docs/api/player.player.off.md +4 -4
- package/docs/api/player.player.on.md +5 -5
- package/docs/api/player.player.resize.md +3 -6
- package/docs/api/player.playercomponenttype.md +16 -0
- package/docs/api/player.playerdebugsettings.md +1 -1
- package/docs/api/player.playerdebugtag.md +1 -0
- package/docs/api/player.playerevent.md +96 -0
- package/docs/api/player.playereventhandler.md +3 -2
- package/docs/api/player.playereventparams.md +20 -0
- package/docs/api/player.playermediasourcedesc.md +1 -1
- package/docs/api/player.playermediasourcedesc.mimetype.md +1 -1
- package/docs/api/player.qualitylevel.bitrate.md +16 -0
- package/docs/api/player.qualitylevel.height.md +16 -0
- package/docs/api/player.qualitylevel.level.md +16 -0
- package/docs/api/player.qualitylevel.md +104 -7
- package/docs/api/player.qualitylevel.width.md +16 -0
- package/docs/api/player.timeposition.current.md +16 -0
- package/docs/api/player.timeposition.md +65 -7
- package/docs/api/player.timeposition.total.md +16 -0
- package/docs/api/player.timevalue.md +1 -1
- package/docs/api/player.translationsettings.md +7 -1
- package/docs/api/player.transportpreference.md +1 -0
- package/lib/Player.d.ts +30 -14
- package/lib/Player.d.ts.map +1 -1
- package/lib/Player.js +72 -65
- package/lib/internal.types.d.ts +3 -5
- package/lib/internal.types.d.ts.map +1 -1
- package/lib/playback/dash-playback/DashPlayback.d.ts +2 -0
- package/lib/playback/dash-playback/DashPlayback.d.ts.map +1 -1
- package/lib/playback/dash-playback/DashPlayback.js +37 -25
- package/lib/playback/hls-playback/HlsPlayback.d.ts +3 -0
- package/lib/playback/hls-playback/HlsPlayback.d.ts.map +1 -1
- package/lib/playback/hls-playback/HlsPlayback.js +33 -18
- package/lib/playback.types.d.ts +65 -6
- package/lib/playback.types.d.ts.map +1 -1
- package/lib/playback.types.js +10 -0
- package/lib/types.d.ts +54 -5
- package/lib/types.d.ts.map +1 -1
- package/lib/types.js +31 -2
- package/package.json +1 -1
- package/src/Player.ts +109 -78
- package/src/internal.types.ts +3 -2
- package/src/playback/dash-playback/DashPlayback.ts +64 -35
- package/src/playback/hls-playback/HlsPlayback.ts +46 -22
- package/src/playback.types.ts +65 -5
- package/src/types.ts +56 -6
- package/temp/player.api.json +611 -87
- package/tsconfig.tsbuildinfo +1 -1
- package/dist/DashPlayback-BRJzl8D8.js +0 -901
package/src/Player.ts
CHANGED
|
@@ -15,18 +15,33 @@ import type {
|
|
|
15
15
|
CoreOptions,
|
|
16
16
|
CorePluginOptions,
|
|
17
17
|
} from './internal.types.js'
|
|
18
|
-
import type { PlayerMediaSourceDesc, PlayerPlugin } from './types.js'
|
|
18
|
+
import type { ContainerSize, PlayerMediaSourceDesc, PlayerPlugin } from './types.js'
|
|
19
19
|
import { PlayerConfig, PlayerEvent } from './types.js'
|
|
20
20
|
import {
|
|
21
21
|
buildMediaSourcesList,
|
|
22
22
|
wrapSource,
|
|
23
23
|
} from './utils/mediaSources.js'
|
|
24
24
|
import { registerPlaybacks } from './playback/index.js'
|
|
25
|
+
import { PlaybackError, TimePosition } from './playback.types.js'
|
|
25
26
|
|
|
26
27
|
/**
|
|
27
28
|
* @beta
|
|
28
29
|
*/
|
|
29
|
-
export type
|
|
30
|
+
export type PlayerEventParams<E extends PlayerEvent> =
|
|
31
|
+
E extends PlayerEvent.Seek ? [number]
|
|
32
|
+
: E extends PlayerEvent.VolumeUpdate ? [number]
|
|
33
|
+
: E extends PlayerEvent.TimeUpdate ? [TimePosition]
|
|
34
|
+
: E extends PlayerEvent.Resize ? [{ width: number, height: number }]
|
|
35
|
+
: E extends PlayerEvent.Fullscreen ? [boolean]
|
|
36
|
+
: E extends PlayerEvent.Error ? [PlaybackError]
|
|
37
|
+
: []
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Type of a listener callback function for a player event.
|
|
41
|
+
* See the description of the event parameters in {@link PlayerEvent}.
|
|
42
|
+
* @beta
|
|
43
|
+
*/
|
|
44
|
+
export type PlayerEventHandler<E extends PlayerEvent> = (...args: PlayerEventParams<E>) => void
|
|
30
45
|
|
|
31
46
|
const T = 'GPlayer'
|
|
32
47
|
|
|
@@ -42,6 +57,7 @@ const DEFAULT_OPTIONS: PlayerConfig = {
|
|
|
42
57
|
}
|
|
43
58
|
|
|
44
59
|
/**
|
|
60
|
+
* Module to perform the playback.
|
|
45
61
|
* @beta
|
|
46
62
|
*/
|
|
47
63
|
export type PlaybackModule = 'dash' | 'hls' | 'html5_video'
|
|
@@ -50,11 +66,11 @@ type PluginOptions = Record<string, unknown>
|
|
|
50
66
|
|
|
51
67
|
/**
|
|
52
68
|
* The main component to use in the application code.
|
|
69
|
+
* @beta
|
|
53
70
|
* @remarks
|
|
54
71
|
* The Player object provides very basic API to control playback.
|
|
55
72
|
* To build a sophisticated UI, use the plugins framework to tap into the Clappr core.
|
|
56
73
|
* {@link https://github.com/clappr/clappr/wiki/Architecture}
|
|
57
|
-
* @beta
|
|
58
74
|
*/
|
|
59
75
|
export class Player {
|
|
60
76
|
private config: PlayerConfig = DEFAULT_OPTIONS
|
|
@@ -80,19 +96,19 @@ export class Player {
|
|
|
80
96
|
|
|
81
97
|
/**
|
|
82
98
|
* Adds a listener to a player event
|
|
83
|
-
* @param event -
|
|
84
|
-
* @param handler -
|
|
99
|
+
* @param event - event type, see {@link PlayerEvent}
|
|
100
|
+
* @param handler - a callback function to handle the event
|
|
85
101
|
*/
|
|
86
|
-
on<
|
|
102
|
+
on<E extends PlayerEvent>(event: E, handler: PlayerEventHandler<E>) {
|
|
87
103
|
this.emitter.on(event, handler)
|
|
88
104
|
}
|
|
89
105
|
|
|
90
106
|
/**
|
|
91
107
|
* Removes a previously added event listener
|
|
92
108
|
* @param event - See {@link PlayerEvent}
|
|
93
|
-
* @param handler -
|
|
109
|
+
* @param handler - a callback attached earlier to that event type
|
|
94
110
|
*/
|
|
95
|
-
off<
|
|
111
|
+
off<E extends PlayerEvent>(event: E, handler: PlayerEventHandler<E>) {
|
|
96
112
|
this.emitter.off(event, handler)
|
|
97
113
|
}
|
|
98
114
|
|
|
@@ -212,7 +228,16 @@ export class Player {
|
|
|
212
228
|
}
|
|
213
229
|
|
|
214
230
|
/**
|
|
215
|
-
* Indicates
|
|
231
|
+
* Indicates whether DVR is in use.
|
|
232
|
+
* @remarks
|
|
233
|
+
* DVR mode, if it is enabled, is triggered we a user seeks behind the live edge.
|
|
234
|
+
*/
|
|
235
|
+
isDvrInUse(): boolean {
|
|
236
|
+
return this.player?.isDvrInUse() ?? false
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* Indicates the playing state.
|
|
216
241
|
*/
|
|
217
242
|
isPlaying(): boolean {
|
|
218
243
|
return this.player?.isPlaying() ?? false
|
|
@@ -250,10 +275,10 @@ export class Player {
|
|
|
250
275
|
* Resizes the player container element and everything within it.
|
|
251
276
|
* @param newSize - new size of the player
|
|
252
277
|
* @remarks
|
|
253
|
-
* Use this method when the player itself does not detect the change in size of its container element.
|
|
278
|
+
* Use this method when the player itself does not detect properly the change in size of its container element.
|
|
254
279
|
* It can be a case for orientation change on some mobile devices.
|
|
255
280
|
*/
|
|
256
|
-
resize(newSize:
|
|
281
|
+
resize(newSize: ContainerSize) {
|
|
257
282
|
this.player?.resize(newSize)
|
|
258
283
|
}
|
|
259
284
|
|
|
@@ -357,50 +382,15 @@ export class Player {
|
|
|
357
382
|
}
|
|
358
383
|
this.tunedIn = true
|
|
359
384
|
const player = this.player
|
|
385
|
+
|
|
360
386
|
this.bindContainerEventListeners(player)
|
|
361
387
|
player.core.on(
|
|
362
388
|
ClapprEvents.CORE_ACTIVE_CONTAINER_CHANGED,
|
|
363
389
|
() => this.bindContainerEventListeners(player),
|
|
364
390
|
null,
|
|
365
391
|
)
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
({ orientation }: { orientation: 'landscape' | 'portrait' }) => {
|
|
369
|
-
trace(`${T} CORE_SCREEN_ORIENTATION_CHANGED`, {
|
|
370
|
-
orientation,
|
|
371
|
-
rootNode: {
|
|
372
|
-
width: this.rootNode?.clientWidth,
|
|
373
|
-
height: this.rootNode?.clientHeight,
|
|
374
|
-
},
|
|
375
|
-
})
|
|
376
|
-
if (Browser.isiOS && this.rootNode) {
|
|
377
|
-
player.core.resize({
|
|
378
|
-
width: this.rootNode.clientWidth,
|
|
379
|
-
height: this.rootNode.clientHeight,
|
|
380
|
-
})
|
|
381
|
-
}
|
|
382
|
-
},
|
|
383
|
-
null,
|
|
384
|
-
)
|
|
385
|
-
player.core.on(
|
|
386
|
-
ClapprEvents.CORE_RESIZE,
|
|
387
|
-
({ width, height }: { width: number; height: number }) => {
|
|
388
|
-
trace(`${T} CORE_RESIZE`, {
|
|
389
|
-
width,
|
|
390
|
-
height,
|
|
391
|
-
})
|
|
392
|
-
},
|
|
393
|
-
null,
|
|
394
|
-
)
|
|
395
|
-
player.core.on(
|
|
396
|
-
ClapprEvents.CORE_FULLSCREEN,
|
|
397
|
-
(isFullscreen: boolean) => {
|
|
398
|
-
trace(`${T} CORE_FULLSCREEN`, {
|
|
399
|
-
isFullscreen,
|
|
400
|
-
})
|
|
401
|
-
},
|
|
402
|
-
null,
|
|
403
|
-
)
|
|
392
|
+
this.bindSizeManagementListeners(player)
|
|
393
|
+
|
|
404
394
|
if (this.config.autoPlay) {
|
|
405
395
|
setTimeout(() => {
|
|
406
396
|
trace(`${T} autoPlay`, {
|
|
@@ -418,6 +408,15 @@ export class Player {
|
|
|
418
408
|
}
|
|
419
409
|
}
|
|
420
410
|
|
|
411
|
+
private safeTriggerEvent<E extends PlayerEvent>(event: E, ...args: PlayerEventParams<E>) {
|
|
412
|
+
try {
|
|
413
|
+
this.emitter.emit(event, ...args)
|
|
414
|
+
} catch (e) {
|
|
415
|
+
reportError(e)
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
// TODO test
|
|
421
420
|
private events: CorePlayerEvents = {
|
|
422
421
|
onReady: () => {
|
|
423
422
|
trace(`${T} onReady`, {
|
|
@@ -434,38 +433,29 @@ export class Player {
|
|
|
434
433
|
// TODO ensure that CORE_ACTIVE_CONTAINER_CHANGED does not get caught before onReady
|
|
435
434
|
setTimeout(() => this.tuneIn(), 0)
|
|
436
435
|
},
|
|
437
|
-
onResize: (newSize: { width: number; height: number }) => {
|
|
438
|
-
trace(`${T} onResize`, {
|
|
439
|
-
newSize,
|
|
440
|
-
})
|
|
441
|
-
},
|
|
442
436
|
onPlay: () => {
|
|
443
|
-
|
|
444
|
-
this.emitter.emit(PlayerEvent.Play)
|
|
445
|
-
} catch (e) {
|
|
446
|
-
reportError(e)
|
|
447
|
-
}
|
|
437
|
+
this.safeTriggerEvent(PlayerEvent.Play)
|
|
448
438
|
},
|
|
449
439
|
onPause: () => {
|
|
450
|
-
|
|
451
|
-
this.emitter.emit(PlayerEvent.Pause)
|
|
452
|
-
} catch (e) {
|
|
453
|
-
reportError(e)
|
|
454
|
-
}
|
|
440
|
+
this.safeTriggerEvent(PlayerEvent.Pause)
|
|
455
441
|
},
|
|
456
442
|
onEnded: () => {
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
}
|
|
443
|
+
this.safeTriggerEvent(PlayerEvent.Ended)
|
|
444
|
+
},
|
|
445
|
+
onSeek: (time: number) => {
|
|
446
|
+
this.safeTriggerEvent(PlayerEvent.Seek, time)
|
|
462
447
|
},
|
|
463
448
|
onStop: () => {
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
449
|
+
this.safeTriggerEvent(PlayerEvent.Stop)
|
|
450
|
+
},
|
|
451
|
+
onVolumeUpdate: (volume: number) => {
|
|
452
|
+
this.safeTriggerEvent(PlayerEvent.VolumeUpdate, volume)
|
|
453
|
+
},
|
|
454
|
+
onTimeUpdate: (time: TimePosition) => {
|
|
455
|
+
this.safeTriggerEvent(PlayerEvent.TimeUpdate, time)
|
|
456
|
+
},
|
|
457
|
+
onError: (error: PlaybackError) => {
|
|
458
|
+
this.safeTriggerEvent(PlayerEvent.Error, error)
|
|
469
459
|
},
|
|
470
460
|
}
|
|
471
461
|
|
|
@@ -496,6 +486,7 @@ export class Player {
|
|
|
496
486
|
mute: this.config.mute,
|
|
497
487
|
crossOrigin: 'anonymous', // TODO
|
|
498
488
|
hlsjsConfig: {
|
|
489
|
+
// TODO
|
|
499
490
|
debug: this.config.debug === 'all' || this.config.debug === 'hls',
|
|
500
491
|
},
|
|
501
492
|
},
|
|
@@ -504,7 +495,7 @@ export class Player {
|
|
|
504
495
|
width: rootNode.clientWidth,
|
|
505
496
|
source: source ? source.source : undefined,
|
|
506
497
|
mimeType: source ? source.mimeType : undefined,
|
|
507
|
-
sources,
|
|
498
|
+
sources,
|
|
508
499
|
strings: this.config.strings,
|
|
509
500
|
}
|
|
510
501
|
return coreOptions
|
|
@@ -523,9 +514,6 @@ export class Player {
|
|
|
523
514
|
}
|
|
524
515
|
|
|
525
516
|
private bindContainerEventListeners(player: PlayerClappr) {
|
|
526
|
-
trace(`${T} bindContainerEventListeners`, {
|
|
527
|
-
activePlayback: player.core.activePlayback?.name,
|
|
528
|
-
})
|
|
529
517
|
if (Browser.isiOS && player.core.activePlayback) {
|
|
530
518
|
player.core.activePlayback.$el.on('webkitendfullscreen', () => {
|
|
531
519
|
try {
|
|
@@ -536,4 +524,47 @@ export class Player {
|
|
|
536
524
|
})
|
|
537
525
|
}
|
|
538
526
|
}
|
|
527
|
+
|
|
528
|
+
private bindSizeManagementListeners(player: PlayerClappr) {
|
|
529
|
+
player.core.on(
|
|
530
|
+
ClapprEvents.CORE_SCREEN_ORIENTATION_CHANGED,
|
|
531
|
+
({ orientation }: { orientation: 'landscape' | 'portrait' }) => {
|
|
532
|
+
trace(`${T} on CORE_SCREEN_ORIENTATION_CHANGED`, {
|
|
533
|
+
orientation,
|
|
534
|
+
rootNode: {
|
|
535
|
+
width: this.rootNode?.clientWidth,
|
|
536
|
+
height: this.rootNode?.clientHeight,
|
|
537
|
+
},
|
|
538
|
+
})
|
|
539
|
+
if (Browser.isiOS && this.rootNode) {
|
|
540
|
+
player.core.resize({
|
|
541
|
+
width: this.rootNode.clientWidth,
|
|
542
|
+
height: this.rootNode.clientHeight,
|
|
543
|
+
})
|
|
544
|
+
}
|
|
545
|
+
},
|
|
546
|
+
null,
|
|
547
|
+
)
|
|
548
|
+
player.core.on(
|
|
549
|
+
ClapprEvents.CORE_RESIZE,
|
|
550
|
+
({ width, height }: { width: number; height: number }) => {
|
|
551
|
+
trace(`${T} on CORE_RESIZE`, {
|
|
552
|
+
width,
|
|
553
|
+
height,
|
|
554
|
+
})
|
|
555
|
+
this.safeTriggerEvent(PlayerEvent.Resize, { width, height })
|
|
556
|
+
},
|
|
557
|
+
null,
|
|
558
|
+
)
|
|
559
|
+
player.core.on(
|
|
560
|
+
ClapprEvents.CORE_FULLSCREEN,
|
|
561
|
+
(isFullscreen: boolean) => {
|
|
562
|
+
trace(`${T} CORE_FULLSCREEN`, {
|
|
563
|
+
isFullscreen,
|
|
564
|
+
})
|
|
565
|
+
this.safeTriggerEvent(PlayerEvent.Fullscreen, isFullscreen)
|
|
566
|
+
},
|
|
567
|
+
null,
|
|
568
|
+
)
|
|
569
|
+
}
|
|
539
570
|
}
|
package/src/internal.types.ts
CHANGED
|
@@ -4,7 +4,7 @@ import type {
|
|
|
4
4
|
Playback as ClapprPlayback,
|
|
5
5
|
} from "@clappr/core";
|
|
6
6
|
|
|
7
|
-
import { PlaybackType, PlayerDebugTag, PlayerMediaSource } from "./types";
|
|
7
|
+
import { ContainerSize, PlaybackType, PlayerDebugTag, PlayerMediaSource } from "./types";
|
|
8
8
|
import { PlaybackError } from "./playback.types";
|
|
9
9
|
|
|
10
10
|
type ExternalTrack = {
|
|
@@ -54,6 +54,7 @@ export interface CorePlaybackConfig {
|
|
|
54
54
|
playInline: boolean;
|
|
55
55
|
preload?: 'metadata' | 'auto' | 'none';
|
|
56
56
|
// preferredAudioLanguage?: string;
|
|
57
|
+
recycleVideo?: boolean;
|
|
57
58
|
shakaConfiguration?: ShakaConfig;
|
|
58
59
|
}
|
|
59
60
|
|
|
@@ -63,7 +64,7 @@ export interface CorePlaybackConfig {
|
|
|
63
64
|
export type CorePlayerEvents = {
|
|
64
65
|
// TODO event arguments types
|
|
65
66
|
onReady?: () => void;
|
|
66
|
-
onResize?: (data:
|
|
67
|
+
onResize?: (data: ContainerSize) => void;
|
|
67
68
|
onPlay?: (metadata: unknown) => void;
|
|
68
69
|
onPause?: (metadata: unknown) => void;
|
|
69
70
|
onStop?: (metadata: unknown) => void;
|
|
@@ -2,7 +2,15 @@
|
|
|
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 {
|
|
5
|
+
import {
|
|
6
|
+
Events,
|
|
7
|
+
HTML5Video,
|
|
8
|
+
Log,
|
|
9
|
+
Playback,
|
|
10
|
+
PlayerError,
|
|
11
|
+
Utils,
|
|
12
|
+
$,
|
|
13
|
+
} from '@clappr/core'
|
|
6
14
|
import { trace } from '@gcorevideo/utils'
|
|
7
15
|
import assert from 'assert'
|
|
8
16
|
import DASHJS, {
|
|
@@ -14,7 +22,15 @@ import DASHJS, {
|
|
|
14
22
|
IManifestInfo,
|
|
15
23
|
} from 'dashjs'
|
|
16
24
|
|
|
17
|
-
import {
|
|
25
|
+
import {
|
|
26
|
+
PlaybackError,
|
|
27
|
+
PlaybackErrorCode,
|
|
28
|
+
PlayerComponentType,
|
|
29
|
+
QualityLevel,
|
|
30
|
+
TimePosition,
|
|
31
|
+
TimeUpdate,
|
|
32
|
+
TimeValue,
|
|
33
|
+
} from '../../playback.types.js'
|
|
18
34
|
import { isDashSource } from '../../utils/mediaSources.js'
|
|
19
35
|
|
|
20
36
|
const AUTO = -1
|
|
@@ -123,13 +139,15 @@ export default class DashPlayback extends HTML5Video {
|
|
|
123
139
|
)
|
|
124
140
|
const dash = this._dash
|
|
125
141
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
142
|
+
const settings = $.extend(true, {}, this.options.dash, {
|
|
143
|
+
streaming: {
|
|
144
|
+
abr: {
|
|
145
|
+
autoSwitchBitrate: {
|
|
146
|
+
video: id === -1,
|
|
147
|
+
},
|
|
148
|
+
},
|
|
149
|
+
},
|
|
150
|
+
})
|
|
133
151
|
|
|
134
152
|
dash.updateSettings(settings)
|
|
135
153
|
if (id !== -1) {
|
|
@@ -222,16 +240,14 @@ export default class DashPlayback extends HTML5Video {
|
|
|
222
240
|
|
|
223
241
|
if (this.options.dash) {
|
|
224
242
|
// TODO use $.extend
|
|
225
|
-
const settings =
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
}
|
|
234
|
-
this._dash.updateSettings(this.options.dash)
|
|
243
|
+
const settings = $.extend({}, this.options.dash, {
|
|
244
|
+
streaming: {
|
|
245
|
+
text: {
|
|
246
|
+
defaultEnabled: false,
|
|
247
|
+
},
|
|
248
|
+
},
|
|
249
|
+
})
|
|
250
|
+
this._dash.updateSettings(settings)
|
|
235
251
|
}
|
|
236
252
|
|
|
237
253
|
this._dash.attachView(this.el)
|
|
@@ -250,20 +266,18 @@ export default class DashPlayback extends HTML5Video {
|
|
|
250
266
|
|
|
251
267
|
this._updatePlaybackType()
|
|
252
268
|
this._fillLevels(bitrates)
|
|
269
|
+
|
|
253
270
|
dash.on(DASHJS.MediaPlayer.events.QUALITY_CHANGE_REQUESTED, (evt) => {
|
|
254
|
-
|
|
255
|
-
assert.ok(
|
|
256
|
-
this._levels,
|
|
257
|
-
'An array of levels is required to change quality',
|
|
258
|
-
)
|
|
259
|
-
const newLevel = this._levels.find(
|
|
260
|
-
(level) => level.level === evt.newQuality,
|
|
261
|
-
) // TODO or simply this._levels[evt.newQuality]?
|
|
262
|
-
assert.ok(newLevel, 'A valid level is required to change quality')
|
|
271
|
+
const newLevel = this.getLevel(evt.newQuality)
|
|
263
272
|
this.onLevelSwitch(newLevel)
|
|
264
273
|
})
|
|
265
274
|
})
|
|
266
275
|
|
|
276
|
+
this._dash.on(DASHJS.MediaPlayer.events.QUALITY_CHANGE_RENDERED, (evt: DASHJS.QualityChangeRenderedEvent) => {
|
|
277
|
+
const currentLevel = this.getLevel(evt.newQuality)
|
|
278
|
+
this.onLevelSwitchEnd(currentLevel)
|
|
279
|
+
})
|
|
280
|
+
|
|
267
281
|
this._dash.on(
|
|
268
282
|
DASHJS.MediaPlayer.events.METRIC_ADDED,
|
|
269
283
|
(e: DashMetricEvent) => {
|
|
@@ -409,7 +423,6 @@ export default class DashPlayback extends HTML5Video {
|
|
|
409
423
|
trace(`${T} _onDASHJSSError`, { event })
|
|
410
424
|
this._stopTimeUpdateTimer()
|
|
411
425
|
|
|
412
|
-
|
|
413
426
|
// Note that the other error types are deprecated
|
|
414
427
|
const e = (event as MediaPlayerErrorEvent).error
|
|
415
428
|
switch (e.code) {
|
|
@@ -419,7 +432,7 @@ export default class DashPlayback extends HTML5Video {
|
|
|
419
432
|
case DASHJS.MediaPlayer.errors.DOWNLOAD_ERROR_ID_MANIFEST_CODE:
|
|
420
433
|
case DASHJS.MediaPlayer.errors.DOWNLOAD_ERROR_ID_CONTENT_CODE:
|
|
421
434
|
case DASHJS.MediaPlayer.errors.DOWNLOAD_ERROR_ID_INITIALIZATION_CODE:
|
|
422
|
-
|
|
435
|
+
// TODO these probably indicate a broken manifest and should be treated by removing the source
|
|
423
436
|
case DASHJS.MediaPlayer.errors.MANIFEST_ERROR_ID_NOSTREAMS_CODE:
|
|
424
437
|
case DASHJS.MediaPlayer.errors.MANIFEST_ERROR_ID_PARSE_CODE:
|
|
425
438
|
case DASHJS.MediaPlayer.errors.MANIFEST_ERROR_ID_MULTIPLEXED_CODE:
|
|
@@ -431,7 +444,7 @@ export default class DashPlayback extends HTML5Video {
|
|
|
431
444
|
description: e.message,
|
|
432
445
|
level: PlayerError.Levels.FATAL,
|
|
433
446
|
})
|
|
434
|
-
break
|
|
447
|
+
break
|
|
435
448
|
// TODO more cases
|
|
436
449
|
default:
|
|
437
450
|
this.triggerError({
|
|
@@ -443,9 +456,15 @@ export default class DashPlayback extends HTML5Video {
|
|
|
443
456
|
}
|
|
444
457
|
}
|
|
445
458
|
|
|
446
|
-
private triggerError(
|
|
459
|
+
private triggerError(
|
|
460
|
+
error: Pick<PlaybackError, 'code' | 'message' | 'description' | 'level'>,
|
|
461
|
+
) {
|
|
447
462
|
trace(`${T} triggerError`, { error })
|
|
448
|
-
this.trigger(Events.PLAYBACK_ERROR,
|
|
463
|
+
this.trigger(Events.PLAYBACK_ERROR, {
|
|
464
|
+
...error,
|
|
465
|
+
origin: this.name,
|
|
466
|
+
scope: DashPlayback.type as PlayerComponentType,
|
|
467
|
+
})
|
|
449
468
|
// only reset the dash player in 10ms async, so that the rest of the
|
|
450
469
|
// calling function finishes
|
|
451
470
|
setTimeout(() => {
|
|
@@ -591,9 +610,13 @@ export default class DashPlayback extends HTML5Video {
|
|
|
591
610
|
private onLevelSwitch(currentLevel: QualityLevel) {
|
|
592
611
|
// TODO check the two below
|
|
593
612
|
this.trigger(Events.PLAYBACK_LEVEL_SWITCH, currentLevel)
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
private onLevelSwitchEnd(currentLevel: QualityLevel) {
|
|
594
616
|
this.trigger(Events.PLAYBACK_LEVEL_SWITCH_END)
|
|
595
|
-
const isHD =
|
|
596
|
-
|
|
617
|
+
const isHD =
|
|
618
|
+
currentLevel.height >= 720 || currentLevel.bitrate / 1000 >= 2000
|
|
619
|
+
this.trigger(Events.PLAYBACK_HIGHDEFINITIONUPDATE, isHD)
|
|
597
620
|
this.trigger(Events.PLAYBACK_BITRATE, currentLevel)
|
|
598
621
|
}
|
|
599
622
|
|
|
@@ -604,6 +627,12 @@ export default class DashPlayback extends HTML5Video {
|
|
|
604
627
|
isSeekEnabled() {
|
|
605
628
|
return this._playbackType === Playback.VOD || this.dvrEnabled
|
|
606
629
|
}
|
|
630
|
+
|
|
631
|
+
private getLevel(quality: number) {
|
|
632
|
+
const ret = this.levels.find((level) => level.level === quality)
|
|
633
|
+
assert.ok(ret, 'Invalid quality level')
|
|
634
|
+
return ret
|
|
635
|
+
}
|
|
607
636
|
}
|
|
608
637
|
|
|
609
638
|
DashPlayback.canPlay = function (resource, mimeType) {
|
|
@@ -28,6 +28,7 @@ import HLSJS, {
|
|
|
28
28
|
import {
|
|
29
29
|
PlaybackError,
|
|
30
30
|
PlaybackErrorCode,
|
|
31
|
+
PlayerComponentType,
|
|
31
32
|
QualityLevel,
|
|
32
33
|
TimePosition,
|
|
33
34
|
TimeUpdate,
|
|
@@ -426,6 +427,11 @@ export default class HlsPlayback extends HTML5Video {
|
|
|
426
427
|
(evt: HlsEvents.LEVEL_SWITCHING, data: LevelSwitchingData) =>
|
|
427
428
|
this._onLevelSwitch(evt, data),
|
|
428
429
|
)
|
|
430
|
+
this._hls.on(
|
|
431
|
+
HLSJS.Events.LEVEL_SWITCHED,
|
|
432
|
+
(evt: HlsEvents.LEVEL_SWITCHED, data: { level: number }) =>
|
|
433
|
+
this._onLevelSwitched(evt, data),
|
|
434
|
+
)
|
|
429
435
|
this._hls.on(
|
|
430
436
|
HLSJS.Events.FRAG_CHANGED,
|
|
431
437
|
(evt: HlsEvents.FRAG_CHANGED, data: FragChangedData) =>
|
|
@@ -511,7 +517,10 @@ export default class HlsPlayback extends HTML5Video {
|
|
|
511
517
|
this._hls.recoverMediaError()
|
|
512
518
|
} else {
|
|
513
519
|
Log.error('hlsjs: failed to recover', { evt, data })
|
|
514
|
-
trace(`${T} _recover failed to recover`, {
|
|
520
|
+
trace(`${T} _recover failed to recover`, {
|
|
521
|
+
type: data.type,
|
|
522
|
+
details: data.details,
|
|
523
|
+
})
|
|
515
524
|
error.level = PlayerError.Levels.FATAL
|
|
516
525
|
|
|
517
526
|
this.triggerError(error)
|
|
@@ -614,12 +623,18 @@ export default class HlsPlayback extends HTML5Video {
|
|
|
614
623
|
}
|
|
615
624
|
|
|
616
625
|
_onHLSJSError(evt: HlsEvents.ERROR, data: HlsErrorData) {
|
|
617
|
-
trace(`${T} _onHLSJSError`, {
|
|
626
|
+
trace(`${T} _onHLSJSError`, {
|
|
627
|
+
fatal: data.fatal,
|
|
628
|
+
type: data.type,
|
|
629
|
+
details: data.details,
|
|
630
|
+
})
|
|
618
631
|
const error: PlaybackError = {
|
|
619
632
|
code: PlaybackErrorCode.Generic,
|
|
620
633
|
description: `${this.name} error: type: ${data.type}, details: ${data.details} fatal: ${data.fatal}`,
|
|
621
634
|
level: data.fatal ? PlayerError.Levels.FATAL : PlayerError.Levels.WARN,
|
|
622
635
|
message: `${this.name} error: type: ${data.type}, details: ${data.details}`,
|
|
636
|
+
origin: this.name,
|
|
637
|
+
scope: HlsPlayback.type as PlayerComponentType,
|
|
623
638
|
}
|
|
624
639
|
|
|
625
640
|
if (data.response) {
|
|
@@ -657,9 +672,12 @@ export default class HlsPlayback extends HTML5Video {
|
|
|
657
672
|
evt,
|
|
658
673
|
data,
|
|
659
674
|
})
|
|
660
|
-
trace(
|
|
661
|
-
|
|
662
|
-
|
|
675
|
+
trace(
|
|
676
|
+
`${T} _onHLSJSError trying to recover from network error`,
|
|
677
|
+
{
|
|
678
|
+
details: data.details,
|
|
679
|
+
},
|
|
680
|
+
)
|
|
663
681
|
error.level = PlayerError.Levels.WARN
|
|
664
682
|
this._hls?.startLoad()
|
|
665
683
|
break
|
|
@@ -705,7 +723,10 @@ export default class HlsPlayback extends HTML5Video {
|
|
|
705
723
|
}
|
|
706
724
|
|
|
707
725
|
Log.warn('hlsjs: non-fatal error occurred', { evt, data })
|
|
708
|
-
trace(`${T} _onHLSJSError non-fatal error occurred`, {
|
|
726
|
+
trace(`${T} _onHLSJSError non-fatal error occurred`, {
|
|
727
|
+
type: data.type,
|
|
728
|
+
details: data.details,
|
|
729
|
+
})
|
|
709
730
|
}
|
|
710
731
|
}
|
|
711
732
|
|
|
@@ -1035,23 +1056,26 @@ export default class HlsPlayback extends HTML5Video {
|
|
|
1035
1056
|
if (!this.levels.length) {
|
|
1036
1057
|
this._fillLevels()
|
|
1037
1058
|
}
|
|
1038
|
-
this.trigger(Events.PLAYBACK_LEVEL_SWITCH_END)
|
|
1039
1059
|
this.trigger(Events.PLAYBACK_LEVEL_SWITCH, data)
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1060
|
+
}
|
|
1061
|
+
|
|
1062
|
+
_onLevelSwitched(evt: HlsEvents.LEVEL_SWITCHED, data: { level: number }) {
|
|
1063
|
+
// @ts-ignore
|
|
1064
|
+
const currentLevel = this._hls.levels[data.level] // TODO or find by .id == level?
|
|
1065
|
+
assert.ok(currentLevel, 'Invalid quality level')
|
|
1066
|
+
this._currentLevel = data.level
|
|
1067
|
+
|
|
1068
|
+
// TODO should highDefinition be private and maybe have a read only accessor if it's used somewhere
|
|
1069
|
+
this.highDefinition =
|
|
1070
|
+
currentLevel.height >= 720 || currentLevel.bitrate / 1000 >= 2000
|
|
1071
|
+
this.trigger(Events.PLAYBACK_HIGHDEFINITIONUPDATE, this.highDefinition)
|
|
1072
|
+
this.trigger(Events.PLAYBACK_BITRATE, {
|
|
1073
|
+
height: currentLevel.height,
|
|
1074
|
+
width: currentLevel.width,
|
|
1075
|
+
bitrate: currentLevel.bitrate,
|
|
1076
|
+
level: data.level,
|
|
1077
|
+
})
|
|
1078
|
+
this.trigger(Events.PLAYBACK_LEVEL_SWITCH_END)
|
|
1055
1079
|
}
|
|
1056
1080
|
|
|
1057
1081
|
get dvrEnabled() {
|