@gcorevideo/player 2.3.0 → 2.4.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.
@@ -0,0 +1,82 @@
1
+ import DashPlayback from '../plugins/dash-playback/DashPlayback';
2
+ import HlsPlayback from '../plugins/hls-playback/HlsPlayback';
3
+ export function buildSourcesSet(sources) {
4
+ const sv = {
5
+ dash: null,
6
+ master: null,
7
+ hls: null,
8
+ mpegts: null,
9
+ };
10
+ sources.forEach((ps) => {
11
+ const [s, t] = typeof ps === 'string' ? [ps, ''] : [ps.source, ps.mimeType];
12
+ if (DashPlayback.canPlay(s, t)) {
13
+ sv.dash = s;
14
+ }
15
+ else if (HlsPlayback.canPlay(s, t)) {
16
+ sv.hls = s;
17
+ }
18
+ else {
19
+ sv.master = s;
20
+ }
21
+ });
22
+ return sv;
23
+ }
24
+ export function buildSourcesPriorityList(sources, priorityTransport = 'auto') {
25
+ const msl = [];
26
+ switch (priorityTransport) {
27
+ case 'dash':
28
+ addDash();
29
+ break;
30
+ case 'hls':
31
+ addHls();
32
+ break;
33
+ case 'mpegts':
34
+ addMpegts();
35
+ break;
36
+ case 'auto':
37
+ addDash();
38
+ addHls();
39
+ break;
40
+ }
41
+ Object.values(sources).forEach((s) => {
42
+ if (s) {
43
+ msl.push(s);
44
+ }
45
+ });
46
+ return msl;
47
+ function addMpegts() {
48
+ if (sources.mpegts) {
49
+ msl.push(sources.mpegts);
50
+ sources.mpegts = null;
51
+ }
52
+ }
53
+ function addHls() {
54
+ if (sources.hls && HlsPlayback.canPlay(sources.hls)) {
55
+ msl.push(sources.hls);
56
+ sources.hls = null;
57
+ }
58
+ if (sources.master?.endsWith('.m3u8') &&
59
+ HlsPlayback.canPlay(sources.master)) {
60
+ msl.push(sources.master);
61
+ sources.master = null;
62
+ }
63
+ }
64
+ function addDash() {
65
+ if (sources.dash && DashPlayback.canPlay(sources.dash)) {
66
+ msl.push(sources.dash);
67
+ sources.dash = null;
68
+ }
69
+ }
70
+ }
71
+ export function unwrapSource(s) {
72
+ return typeof s === 'string' ? s : s.source;
73
+ }
74
+ export function buildGcoreStreamSourcesList(ms, priorityTransport) {
75
+ const sources = {
76
+ dash: ms.sourceDash,
77
+ master: ms.source,
78
+ hls: ms.hlsCmafUrl,
79
+ mpegts: ms.hlsMpegtsUrl,
80
+ };
81
+ return buildSourcesPriorityList(sources, priorityTransport);
82
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gcorevideo/player",
3
- "version": "2.3.0",
3
+ "version": "2.4.0",
4
4
  "description": "Gcore JavaScript video player",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",
package/src/Player.ts CHANGED
@@ -13,19 +13,20 @@ import type {
13
13
  CorePlayerEvents,
14
14
  CoreOptions,
15
15
  CorePluginOptions,
16
+ PlayerMediaSource,
16
17
  } from './internal.types.js'
17
18
  import type {
18
- BitrateInfo,
19
19
  PlaybackType,
20
20
  PlayerPlugin,
21
+ QualityLevelInfo,
21
22
  StreamMediaSource,
23
+ TransportPreference,
22
24
  } from './types.js'
23
25
  import { reportError, trace } from './trace/index.js'
24
26
  import { PlayerConfig, PlayerEvent } from './types.js'
25
27
  import DashPlayback from './plugins/dash-playback/DashPlayback.js'
26
28
  import HlsPlayback from './plugins/hls-playback/HlsPlayback.js'
27
-
28
- // import '../assets/style/main.scss' // TODO check if needed
29
+ import { buildSourcesPriorityList, buildSourcesSet, unwrapSource } from './utils/mediaSources.js'
29
30
 
30
31
  // TODO implement transport retry/failover and fallback logic
31
32
 
@@ -35,15 +36,16 @@ const T = 'GPlayer'
35
36
 
36
37
  const DEFAULT_OPTIONS: PlayerConfig = {
37
38
  autoPlay: false,
38
- mute: false,
39
+ debug: 'none',
39
40
  loop: false,
41
+ mute: false,
40
42
  multisources: [],
41
43
  playbackType: 'vod',
42
- priorityTransport: 'dash',
43
- debug: 'none',
44
44
  pluginSettings: {},
45
- strings: {},
46
45
  poster: '',
46
+ priorityTransport: 'dash',
47
+ sources: [],
48
+ strings: {},
47
49
  }
48
50
 
49
51
  export type PlaybackModule = 'dash' | 'hls' | 'native'
@@ -54,7 +56,7 @@ type PluginOptions = Record<string, unknown>
54
56
  * @beta
55
57
  */
56
58
  export class Player {
57
- private bitrateInfo: BitrateInfo | null = null
59
+ private qLevel: QualityLevelInfo | null = null
58
60
 
59
61
  private config: PlayerConfig = DEFAULT_OPTIONS
60
62
 
@@ -91,8 +93,8 @@ export class Player {
91
93
  return this.player.core.activePlayback.options.src
92
94
  }
93
95
 
94
- get bitrate(): BitrateInfo | null {
95
- return this.bitrateInfo
96
+ get bitrate(): QualityLevelInfo | null {
97
+ return this.qLevel
96
98
  }
97
99
 
98
100
  get hd() {
@@ -162,7 +164,7 @@ export class Player {
162
164
  clearTimeout(this.tuneInTimerId)
163
165
  this.tuneInTimerId = null
164
166
  }
165
- this.bitrateInfo = null
167
+ this.qLevel = null
166
168
  }
167
169
 
168
170
  pause() {
@@ -236,7 +238,6 @@ export class Player {
236
238
  player.core.on(
237
239
  ClapprEvents.CORE_ACTIVE_CONTAINER_CHANGED,
238
240
  () => {
239
- // this.trigger(Events.ContainerChanged)
240
241
  this.bindBitrateChangeHandler()
241
242
  },
242
243
  null,
@@ -345,16 +346,20 @@ export class Player {
345
346
  }
346
347
 
347
348
  private buildCoreOptions(rootNode: HTMLElement): CoreOptions {
348
- const multisources = this.config.multisources
349
- const mainSource =
350
- this.config.playbackType === 'live'
351
- ? multisources.find((ms) => ms.live !== false)
352
- : multisources[0]
353
- const mediaSources = mainSource
354
- ? this.buildMediaSourcesList(mainSource)
355
- : []
349
+ // TODO extract
350
+ // const multisources = this.config.multisources
351
+ // const mainSource =
352
+ // this.config.playbackType === 'live'
353
+ // ? multisources.find((ms) => ms.live !== false)
354
+ // : multisources[0]
355
+ // const mediaSources = mainSource
356
+ // ? this.buildMediaSourcesList(mainSource)
357
+ // : []
356
358
  // const mainSourceUrl = mediaSources[0];
357
- const poster = mainSource?.poster ?? this.config.poster
359
+ // const poster = mainSource?.poster ?? this.config.poster
360
+ const poster = this.config.poster
361
+
362
+ const source = this.selectMediaSource(); // TODO
358
363
 
359
364
  this.rootNode = rootNode
360
365
 
@@ -366,7 +371,6 @@ export class Player {
366
371
  events: this.events,
367
372
  height: rootNode.clientHeight,
368
373
  loop: this.config.loop,
369
- multisources,
370
374
  mute: this.config.mute,
371
375
  playback: {
372
376
  controls: false,
@@ -382,8 +386,8 @@ export class Player {
382
386
  playbackType: this.config.playbackType,
383
387
  poster,
384
388
  width: rootNode.clientWidth,
385
- // source: mainSourceUrl,
386
- sources: mediaSources,
389
+ source: source ? unwrapSource(source) : undefined,
390
+ // sources: mediaSources,
387
391
  strings: this.config.strings,
388
392
  }
389
393
  return coreOptions
@@ -398,69 +402,14 @@ export class Player {
398
402
  private bindBitrateChangeHandler() {
399
403
  this.player?.core.activeContainer.on(
400
404
  ClapprEvents.CONTAINER_BITRATE,
401
- (bitrate: BitrateInfo) => {
402
- this.bitrateInfo = bitrate
405
+ (bitrate: QualityLevelInfo) => {
406
+ this.qLevel = bitrate
403
407
  },
404
408
  )
405
409
  }
406
410
 
407
- private buildMediaSourcesList(ms: StreamMediaSource): string[] {
408
- const msl: string[] = []
409
- const sources: Record<'dash' | 'master' | 'hls' | 'mpegts', string | null> =
410
- {
411
- dash: ms.sourceDash,
412
- master: ms.source,
413
- hls: ms.hlsCmafUrl,
414
- mpegts: ms.hlsMpegtsUrl,
415
- }
416
- switch (this.config.priorityTransport) {
417
- case 'dash':
418
- addDash()
419
- break
420
- case 'hls':
421
- addHls()
422
- break
423
- case 'mpegts':
424
- addMpegts()
425
- break
426
- case 'auto':
427
- addDash()
428
- addHls()
429
- break
430
- }
431
- Object.values(sources).forEach((s) => {
432
- if (s) {
433
- msl.push(s)
434
- }
435
- })
436
- return msl
437
-
438
- function addMpegts() {
439
- if (sources.mpegts) {
440
- msl.push(sources.mpegts)
441
- sources.mpegts = null
442
- }
443
- }
444
-
445
- function addHls() {
446
- if (sources.hls && HlsPlayback.canPlay(sources.hls)) {
447
- msl.push(sources.hls)
448
- sources.hls = null
449
- }
450
- if (
451
- sources.master?.endsWith('.m3u8') &&
452
- HlsPlayback.canPlay(sources.master)
453
- ) {
454
- msl.push(sources.master)
455
- sources.master = null
456
- }
457
- }
458
-
459
- function addDash() {
460
- if (sources.dash && DashPlayback.canPlay(sources.dash)) {
461
- msl.push(sources.dash)
462
- sources.dash = null
463
- }
464
- }
411
+ // TODO select a single source to play according to the priority transport and the modules support
412
+ private selectMediaSource(): PlayerMediaSource | undefined {
413
+ return buildSourcesPriorityList(buildSourcesSet(this.config.sources), this.config.priorityTransport)[0]
465
414
  }
466
415
  }
@@ -18,7 +18,7 @@ type MediacontrolStyles = {
18
18
  buttons?: string;
19
19
  }
20
20
 
21
- type PlayerMediaSourceDesc = {
21
+ export type PlayerMediaSourceDesc = {
22
22
  mimeType?: string;
23
23
  source: string;
24
24
  }
@@ -116,7 +116,7 @@ export type CoreOptions = {
116
116
  maxBufferLength?: number;
117
117
  mediacontrol?: MediacontrolStyles;
118
118
  mimeType?: string;
119
- multisources: StreamMediaSource[];
119
+ // multisources: StreamMediaSource[];
120
120
  mute?: boolean;
121
121
  persistConfig?: boolean;
122
122
  preload?: "auto" | "metadata" | "none";
@@ -788,6 +788,7 @@ DashPlayback.canPlay = function (resource, mimeType) {
788
788
  const resourceParts = resource.split('?')[0].match(/.*\.(.*)$/) || [];
789
789
  const isDash = ((resourceParts.length > 1 && resourceParts[1].toLowerCase() === 'mpd') ||
790
790
  mimeType === 'application/dash+xml' || mimeType === 'video/mp4');
791
+ // TODO check
791
792
  const ctor = window.MediaSource || ('WebKitMediaSource' in window ? window.WebKitMediaSource : undefined);
792
793
  const hasSupport = typeof ctor === 'function';
793
794
  trace(`${T} canPlay`, {hasSupport, isDash, resource});
package/src/types.ts CHANGED
@@ -1,107 +1,111 @@
1
- export type PlayerDebugTag = 'all' | 'clappr' | 'dash' | 'hls' | 'none';
2
- export type PlayerDebugSettings = PlayerDebugTag | boolean;
1
+ import { PlayerMediaSource } from "./internal.types"
3
2
 
4
- export type PlaybackType = 'live' | 'vod';
5
- export type MediaTransport = 'dash' | 'hls' | 'mpegts';
6
- export type TransportPreference = MediaTransport | 'auto';
3
+ export type PlayerDebugTag = 'all' | 'clappr' | 'dash' | 'hls' | 'none'
4
+ export type PlayerDebugSettings = PlayerDebugTag | boolean
5
+
6
+ export type PlaybackType = 'live' | 'vod'
7
+ export type MediaTransport = 'dash' | 'hls' | 'mpegts'
8
+ export type TransportPreference = MediaTransport | 'auto'
7
9
 
8
10
  export type PlayerPlugin = {
9
- new(...args: any[]): unknown;
10
- type: string; // 'core' | 'container' | 'playback';
11
+ new (...args: any[]): unknown
12
+ type: string // 'core' | 'container' | 'playback';
11
13
  }
12
14
 
13
15
  export type PlayerConfig = {
14
- autoPlay?: boolean;
15
- debug?: PlayerDebugSettings;
16
- language?: string;
17
- loop?: boolean;
18
- multisources: StreamMediaSource[]; // TODO rename sources or better split the sources and the source description settings
19
- mute?: boolean;
20
- playbackType?: PlaybackType;
21
- pluginSettings?: Record<string, unknown>;
22
- poster?: string;
23
- priorityTransport?: TransportPreference;
24
- strings: TranslationSettings;
16
+ autoPlay?: boolean
17
+ debug?: PlayerDebugSettings
18
+ language?: string
19
+ loop?: boolean
20
+ multisources: StreamMediaSource[] // TODO rename sources or better split the sources and the source description settings
21
+ mute?: boolean
22
+ playbackType?: PlaybackType
23
+ pluginSettings?: Record<string, unknown>
24
+ poster?: string
25
+ priorityTransport?: TransportPreference
26
+ sources: PlayerMediaSource[];
27
+ strings: TranslationSettings
25
28
  }
26
29
 
27
30
  export type PlayerOptionsThumbnails = {
28
- sprite?: string | null;
29
- vtt?: string | null;
30
-
31
+ sprite?: string | null
32
+ vtt?: string | null
31
33
  }
32
34
 
33
35
  export type ContextMenuSettings = {
34
- preventShowContextMenu?: boolean;
36
+ preventShowContextMenu?: boolean
35
37
  }
36
38
 
37
- type LangTag = string;
38
- type TranslationKey = string;
39
+ type LangTag = string
40
+ type TranslationKey = string
39
41
 
40
42
  export type BroadcastSettings = {
41
- status?: 'live' | 'noActiveStreams';
43
+ status?: 'live' | 'noActiveStreams'
42
44
  }
43
45
 
44
- export type ClipsPluginOptions = Record<string, unknown>; // TODO
46
+ export type ClipsPluginOptions = Record<string, unknown> // TODO
45
47
 
46
48
  export type PlaybackSettings = {
47
- hlsjsConfig?: Record<string, unknown>;
48
- playInline?: boolean;
49
- preload?: 'auto' | 'metadata' | 'none';
50
- triggerFatalErrorOnResourceDenied?: boolean;
49
+ hlsjsConfig?: Record<string, unknown>
50
+ playInline?: boolean
51
+ preload?: 'auto' | 'metadata' | 'none'
52
+ triggerFatalErrorOnResourceDenied?: boolean
51
53
  }
52
54
 
53
- export type DashSettings = Record<string, unknown>; //
55
+ export type DashSettings = Record<string, unknown> //
54
56
 
55
57
  // TODO consult with the Broadcaster team
56
58
  // TODO turn into camel case convert at user level
57
59
  export type StreamMediaSourceDto = {
58
- description: string;
59
- dvr: boolean;
60
- hls_cmaf_url?: string;
61
- hls_mpegts_url?: string;
62
- id: number;
63
- live: boolean;
64
- priority_transport: TransportPreference;
65
- poster: string | null;
66
- projection: ProjectionType | null;
67
- screenshot: string | null;
60
+ description: string
61
+ dvr: boolean
62
+ hls_cmaf_url?: string
63
+ hls_mpegts_url?: string
64
+ id: number
65
+ live: boolean
66
+ priority_transport: TransportPreference
67
+ poster: string | null
68
+ projection: ProjectionType | null
69
+ screenshot: string | null
68
70
  source: string
69
- source_dash: string | null;
70
- sprite: string | null;
71
- title: string;
72
- vtt: string | null;
71
+ source_dash: string | null
72
+ sprite: string | null
73
+ title: string
74
+ vtt: string | null
73
75
  }
74
76
 
75
77
  // TODO split into sources and source attributes
76
78
  export type StreamMediaSource = {
77
- description: string;
78
- dvr: boolean;
79
- hlsCmafUrl: string | null;
80
- hlsMpegtsUrl: string | null;
81
- id: number;
82
- live: boolean;
83
- priorityTransport: TransportPreference;
84
- poster: string | null;
85
- projection: ProjectionType | null;
86
- screenshot: string | null;
87
- source: string | null;
88
- sourceDash: string | null;
89
- sprite: string | null;
90
- title: string;
91
- vtt: string | null;
79
+ description: string
80
+ dvr: boolean
81
+ hlsCmafUrl: string | null
82
+ hlsMpegtsUrl: string | null
83
+ id: number
84
+ live: boolean
85
+ priorityTransport: TransportPreference
86
+ poster: string | null
87
+ projection: ProjectionType | null
88
+ screenshot: string | null
89
+ source: string | null
90
+ sourceDash: string | null
91
+ sprite: string | null
92
+ title: string
93
+ vtt: string | null
92
94
  }
93
95
 
94
- export type SrcProjectionType = 'regular' | '360' | 'vr180' | 'vr360tb';
95
- export type ProjectionType = '360' | '180' | '360_TB';
96
+ export type SrcProjectionType = 'regular' | '360' | 'vr180' | 'vr360tb'
97
+ export type ProjectionType = '360' | '180' | '360_TB'
96
98
 
97
- export type TranslationSettings = Partial<Record<LangTag, Record<TranslationKey, string>>>;
99
+ export type TranslationSettings = Partial<
100
+ Record<LangTag, Record<TranslationKey, string>>
101
+ >
98
102
 
99
- export type BitrateInfo = {
100
- height: number;
101
- width: number;
102
- bitrate: number;
103
- level: number;
104
- };
103
+ export type QualityLevelInfo = {
104
+ level: number
105
+ width: number
106
+ height: number
107
+ bitrate: number
108
+ }
105
109
 
106
110
  export enum PlayerEvent {
107
111
  Ready = 'ready',
@@ -1,73 +1,73 @@
1
1
  import "@clappr/core";
2
2
 
3
3
  declare module "@clappr/core" {
4
- type MediacontrolStyles = {
5
- // TODO
6
- }
4
+ // type MediacontrolStyles = {
5
+ // // TODO
6
+ // }
7
7
 
8
- type PlayerMediaSourceDesc = {
9
- mimeType?: string;
10
- source: string;
11
- }
8
+ // type PlayerMediaSourceDesc = {
9
+ // mimeType?: string;
10
+ // source: string;
11
+ // }
12
12
 
13
- type PlayerMediaSource = string | PlayerMediaSourceDesc;
13
+ // type PlayerMediaSource = string | PlayerMediaSourceDesc;
14
14
 
15
- type HlsjsConfig = {
16
- debug?: boolean;
17
- startLevel?: number;
18
- } & Record<string, unknown>;
15
+ // type HlsjsConfig = {
16
+ // debug?: boolean;
17
+ // startLevel?: number;
18
+ // } & Record<string, unknown>;
19
19
 
20
- type ShakaConfig = Record<string, unknown>;
20
+ // type ShakaConfig = Record<string, unknown>;
21
21
 
22
- declare type CorePlaybackConfig = {
23
- // audioOnly: boolean;
24
- disableContextMenu?: boolean;
25
- controls?: boolean;
26
- crossOrigin?: 'anonymous' | 'use-credentials';
27
- // enableAutomaticABR?: boolean;
28
- externalTracks?: unknown[]; // TODO
29
- hlsjsConfig?: HlsjsConfig;
30
- // initialBandwidthEstimate?: number;
31
- // maxBufferLength?: number;
32
- // maxBackBufferLength?: number;
33
- // minBufferLength?: number;
34
- minimumDvrSize?: number; // TODO ?
35
- // maxAdaptiveBitrate?: number;
36
- // maxAdaptiveVideoDimensions?: unknown; // TODO
37
- playInline: boolean;
38
- preload?: 'metadata' | 'auto' | 'none';
39
- // preferredTextLanguage?: string;
40
- // preferredAudioLanguage?: string;
41
- shakaConfiguration?: ShakaConfig;
42
- }
22
+ // declare type CorePlaybackConfig = {
23
+ // // audioOnly: boolean;
24
+ // disableContextMenu?: boolean;
25
+ // controls?: boolean;
26
+ // crossOrigin?: 'anonymous' | 'use-credentials';
27
+ // // enableAutomaticABR?: boolean;
28
+ // externalTracks?: unknown[]; // TODO
29
+ // hlsjsConfig?: HlsjsConfig;
30
+ // // initialBandwidthEstimate?: number;
31
+ // // maxBufferLength?: number;
32
+ // // maxBackBufferLength?: number;
33
+ // // minBufferLength?: number;
34
+ // minimumDvrSize?: number; // TODO ?
35
+ // // maxAdaptiveBitrate?: number;
36
+ // // maxAdaptiveVideoDimensions?: unknown; // TODO
37
+ // playInline: boolean;
38
+ // preload?: 'metadata' | 'auto' | 'none';
39
+ // // preferredTextLanguage?: string;
40
+ // // preferredAudioLanguage?: string;
41
+ // shakaConfiguration?: ShakaConfig;
42
+ // }
43
43
 
44
- type ErrorLevel = "FATAL" | "WARN" | "INFO";
44
+ // type ErrorLevel = "FATAL" | "WARN" | "INFO";
45
45
 
46
- declare type EventSpec = string;
47
- declare type EventHandlerSpec = string;
48
- declare type PluginEventsConfig = Record<EventSpec, EventHandlerSpec>;
46
+ // declare type EventSpec = string;
47
+ // declare type EventHandlerSpec = string;
48
+ // declare type PluginEventsConfig = Record<EventSpec, EventHandlerSpec>;
49
49
 
50
- declare type PlaybackError = {
51
- code?: number | string;
52
- description: string;
53
- raw?: MediaError;
54
- level?: ErrorLevel;
55
- }
50
+ // declare type PlaybackError = {
51
+ // code?: number | string;
52
+ // description: string;
53
+ // raw?: MediaError;
54
+ // level?: ErrorLevel;
55
+ // }
56
56
 
57
- declare type CorePlayerEvents = {
58
- // TODO event arguments types
59
- onReady?: () => void;
60
- onResize?: (data: { width: number; height: number }) => void;
61
- onPlay?: (metadata: unknown) => void;
62
- onPause?: (metadata: unknown) => void;
63
- onStop?: (metadata: unknown) => void;
64
- onEnded?: () => void;
65
- onSeek?: (currentTime: number) => void;
66
- onError?: (err: PlaybackError) => void;
67
- onTimeUpdate?: (timeProgress: { current: number; total: number }) => void;
68
- onVolumeUpdate?: (value: number) => void;
69
- onSubtitleAvailable?: () => void;
70
- }
57
+ // declare type CorePlayerEvents = {
58
+ // // TODO event arguments types
59
+ // onReady?: () => void;
60
+ // onResize?: (data: { width: number; height: number }) => void;
61
+ // onPlay?: (metadata: unknown) => void;
62
+ // onPause?: (metadata: unknown) => void;
63
+ // onStop?: (metadata: unknown) => void;
64
+ // onEnded?: () => void;
65
+ // onSeek?: (currentTime: number) => void;
66
+ // onError?: (err: PlaybackError) => void;
67
+ // onTimeUpdate?: (timeProgress: { current: number; total: number }) => void;
68
+ // onVolumeUpdate?: (value: number) => void;
69
+ // onSubtitleAvailable?: () => void;
70
+ // }
71
71
 
72
72
  declare type ClapprVersionSpec = {
73
73
  min: string;