@gcorevideo/player 0.1.0 → 0.2.3
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 +29198 -27398
- package/lib/Player.d.ts +8 -25
- package/lib/Player.d.ts.map +1 -1
- package/lib/Player.js +79 -131
- package/lib/backend.js +2 -2
- package/lib/index.d.ts +1 -0
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +1 -0
- package/lib/internal.types.d.ts +9 -2
- package/lib/internal.types.d.ts.map +1 -1
- package/lib/plugins/dash-playback/DashPlayback.d.ts.map +1 -1
- package/lib/plugins/dash-playback/DashPlayback.js +1 -11
- package/lib/plugins/hls-playback/HlsPlayback.d.ts.map +1 -1
- package/lib/plugins/hls-playback/HlsPlayback.js +5 -1
- package/lib/types.d.ts +16 -3
- package/lib/types.d.ts.map +1 -1
- package/lib/types.js +8 -1
- package/lib/utils/utils.d.ts +0 -4
- package/lib/utils/utils.d.ts.map +1 -1
- package/lib/utils/utils.js +0 -26
- package/lib/version.d.ts +5 -0
- package/lib/version.d.ts.map +1 -0
- package/lib/version.js +8 -0
- package/package.json +2 -1
- package/rollup.config.js +3 -1
- package/src/Player.ts +89 -166
- package/src/backend.ts +2 -2
- package/src/index.ts +1 -0
- package/src/internal.types.ts +11 -3
- package/src/plugins/dash-playback/DashPlayback.ts +1 -11
- package/src/plugins/hls-playback/HlsPlayback.ts +7 -3
- package/src/types.ts +18 -6
- package/src/{xtypings → typings}/@clappr/core/player.d.ts +0 -7
- package/src/typings/@clappr/plugins.d.ts +23 -0
- package/src/utils/utils.ts +0 -26
- package/src/version.ts +9 -0
- package/tsconfig.tsbuildinfo +1 -1
- package/src/utils/scripts-load.ts +0 -26
- /package/src/{xtypings → typings}/@clappr/core/error_mixin.d.ts +0 -0
- /package/src/{xtypings → typings}/@clappr/core/events.d.ts +0 -0
- /package/src/{xtypings → typings}/@clappr/core/html5_video.d.ts +0 -0
- /package/src/{xtypings → typings}/@clappr/core/playback.d.ts +0 -0
- /package/src/{xtypings → typings}/globals.d.ts +0 -0
package/lib/utils/utils.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/utils/utils.ts"],"names":[],"mappings":"AAIA,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,qBAMvC;AAED,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAyBxD
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/utils/utils.ts"],"names":[],"mappings":"AAIA,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,qBAMvC;AAED,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAyBxD"}
|
package/lib/utils/utils.js
CHANGED
|
@@ -1,6 +1,3 @@
|
|
|
1
|
-
// import LogManager from './LogManager';
|
|
2
|
-
import { Browser } from '@clappr/core';
|
|
3
|
-
import assert from 'assert';
|
|
4
1
|
export function getLocation(href) {
|
|
5
2
|
const l = document.createElement('a');
|
|
6
3
|
l.href = href;
|
|
@@ -32,26 +29,3 @@ export function strtimeToMiliseconds(str) {
|
|
|
32
29
|
}
|
|
33
30
|
return (h + m + s);
|
|
34
31
|
}
|
|
35
|
-
// TODO refactor
|
|
36
|
-
export function isFullscreen(el) {
|
|
37
|
-
const video = el.nodeName === "video" ? el : el.querySelector('video');
|
|
38
|
-
assert.ok(video, 'element must be a video or contain a video element');
|
|
39
|
-
if (Browser.isiOS) {
|
|
40
|
-
return FullscreenIOS.isFullscreen(video);
|
|
41
|
-
}
|
|
42
|
-
return !!(document.fullscreenElement);
|
|
43
|
-
}
|
|
44
|
-
export const FullscreenIOS = {
|
|
45
|
-
isFullscreen: function (el) {
|
|
46
|
-
try {
|
|
47
|
-
if (el.webkitDisplayingFullscreen !== undefined) {
|
|
48
|
-
return !!(el.webkitDisplayingFullscreen);
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
catch (e) {
|
|
52
|
-
// LogManager.exception(error);
|
|
53
|
-
reportError(e);
|
|
54
|
-
}
|
|
55
|
-
return false;
|
|
56
|
-
}
|
|
57
|
-
};
|
package/lib/version.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"version.d.ts","sourceRoot":"","sources":["../src/version.ts"],"names":[],"mappings":"AAGA,wBAAgB,OAAO;;;EAKtB"}
|
package/lib/version.js
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import * as pkg from '../package.json' with { "type": "json" };
|
|
2
|
+
import * as lock from '../package-lock.json' with { "type": "json" };
|
|
3
|
+
export function version() {
|
|
4
|
+
return {
|
|
5
|
+
gplayer: pkg.version,
|
|
6
|
+
clappr: lock.packages['node_modules/@clappr/core'].version,
|
|
7
|
+
};
|
|
8
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gcorevideo/player",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.3",
|
|
4
4
|
"description": "Gcore JavaScript video player",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -31,6 +31,7 @@
|
|
|
31
31
|
"homepage": "https://github.com/G-Core/gcore-videoplayer-js#readme",
|
|
32
32
|
"devDependencies": {
|
|
33
33
|
"@rollup/plugin-commonjs": "^28.0.1",
|
|
34
|
+
"@rollup/plugin-json": "^6.1.0",
|
|
34
35
|
"@rollup/plugin-node-resolve": "^15.3.0",
|
|
35
36
|
"@types/node": "^22.10.1",
|
|
36
37
|
"assert": "^2.1.0",
|
package/rollup.config.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// https://github.com/rollup/rollup-starter-lib
|
|
2
|
-
import resolve from '@rollup/plugin-node-resolve';
|
|
3
2
|
import commonjs from '@rollup/plugin-commonjs';
|
|
3
|
+
import json from '@rollup/plugin-json';
|
|
4
|
+
import resolve from '@rollup/plugin-node-resolve';
|
|
4
5
|
import sass from 'rollup-plugin-sass';
|
|
5
6
|
import { string } from 'rollup-plugin-string';
|
|
6
7
|
import polyfillNode from 'rollup-plugin-polyfill-node';
|
|
@@ -15,6 +16,7 @@ export default [
|
|
|
15
16
|
}),
|
|
16
17
|
resolve(),
|
|
17
18
|
commonjs(),
|
|
19
|
+
json(),
|
|
18
20
|
string({
|
|
19
21
|
include: [
|
|
20
22
|
'**/*.ejs',
|
package/src/Player.ts
CHANGED
|
@@ -1,17 +1,15 @@
|
|
|
1
1
|
import {
|
|
2
2
|
Browser,
|
|
3
3
|
Events as ClapprEvents,
|
|
4
|
+
HTML5Video,
|
|
4
5
|
Log,
|
|
5
6
|
Player as PlayerClappr,
|
|
6
7
|
$,
|
|
7
8
|
Loader,
|
|
8
9
|
} from '@clappr/core';
|
|
9
10
|
import assert from 'assert';
|
|
10
|
-
import Hls from 'hls.js';
|
|
11
11
|
import EventLite from "event-lite";
|
|
12
12
|
|
|
13
|
-
import '../assets/style/main.scss'; // TODO check if needed
|
|
14
|
-
|
|
15
13
|
import type {
|
|
16
14
|
CorePlayerEvents,
|
|
17
15
|
CoreOptions,
|
|
@@ -19,33 +17,26 @@ import type {
|
|
|
19
17
|
PlayerMediaSource,
|
|
20
18
|
} from "./internal.types.js";
|
|
21
19
|
import type {
|
|
20
|
+
BitrateInfo,
|
|
21
|
+
PlaybackType,
|
|
22
22
|
PlayerPlugin,
|
|
23
23
|
StreamMediaSource,
|
|
24
|
-
} from "./types";
|
|
25
|
-
|
|
26
|
-
import { reportError, trace } from "./trace/index.js";
|
|
24
|
+
} from "./types.js";
|
|
25
|
+
import { reportError } from "./trace/index.js";
|
|
27
26
|
import {
|
|
28
27
|
PlayerConfig,
|
|
29
|
-
|
|
30
|
-
TransportPreference,
|
|
28
|
+
PlayerEvent,
|
|
31
29
|
} from "./types.js";
|
|
32
30
|
import DashPlayback from './plugins/dash-playback/DashPlayback.js';
|
|
33
31
|
import HlsPlayback from './plugins/hls-playback/HlsPlayback.js';
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
export enum PlayerEvent {
|
|
37
|
-
Ready = 'ready',
|
|
38
|
-
Play = 'play',
|
|
39
|
-
Pause = 'pause',
|
|
40
|
-
Stop = 'stop',
|
|
41
|
-
Ended = 'ended',
|
|
42
|
-
}
|
|
32
|
+
|
|
33
|
+
import '../assets/style/main.scss'; // TODO check if needed
|
|
43
34
|
|
|
44
35
|
// TODO implement transport retry/failover and fallback logic
|
|
45
36
|
|
|
46
37
|
type PlayerEventHandler<T extends PlayerEvent> = () => void;
|
|
47
38
|
|
|
48
|
-
const T = "
|
|
39
|
+
const T = "GPlayer";
|
|
49
40
|
|
|
50
41
|
const DEFAULT_OPTIONS: Partial<PlayerConfig> = {
|
|
51
42
|
autoPlay: false,
|
|
@@ -55,20 +46,6 @@ const DEFAULT_OPTIONS: Partial<PlayerConfig> = {
|
|
|
55
46
|
|
|
56
47
|
export type PlaybackModule = 'dash' | 'hls' | 'native';
|
|
57
48
|
|
|
58
|
-
// TODO drop
|
|
59
|
-
// export type PlaybackInfo = {
|
|
60
|
-
// bitrate: number;
|
|
61
|
-
// hd: boolean;
|
|
62
|
-
// latency: number;
|
|
63
|
-
// }
|
|
64
|
-
|
|
65
|
-
type BitrateInfo = {
|
|
66
|
-
height: number;
|
|
67
|
-
width: number;
|
|
68
|
-
bitrate: number;
|
|
69
|
-
level: number;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
49
|
type PluginOptions = Record<string, unknown>;
|
|
73
50
|
|
|
74
51
|
/**
|
|
@@ -87,13 +64,11 @@ export class Player {
|
|
|
87
64
|
|
|
88
65
|
private tuneInEntered = false;
|
|
89
66
|
|
|
90
|
-
private supportedMediaTransports: MediaTransport[] = [];
|
|
91
|
-
|
|
92
67
|
private config: PlayerConfig;
|
|
93
68
|
|
|
94
69
|
private bitrateInfo: BitrateInfo | null = null;
|
|
95
70
|
|
|
96
|
-
get
|
|
71
|
+
get activePlayback(): PlaybackModule | null {
|
|
97
72
|
if (!this.player?.core.activePlayback) {
|
|
98
73
|
return null;
|
|
99
74
|
}
|
|
@@ -115,6 +90,10 @@ export class Player {
|
|
|
115
90
|
return this.player?.core.activePlayback?.isHighDefinitionInUse || false;
|
|
116
91
|
}
|
|
117
92
|
|
|
93
|
+
get playbackType(): PlaybackType | undefined{
|
|
94
|
+
return this.player?.core.activePlayback?.getPlaybackType();
|
|
95
|
+
}
|
|
96
|
+
|
|
118
97
|
get playing() {
|
|
119
98
|
return this.player ? this.player.isPlaying() : false;
|
|
120
99
|
}
|
|
@@ -137,9 +116,13 @@ export class Player {
|
|
|
137
116
|
this.emitter.off(event, handler);
|
|
138
117
|
}
|
|
139
118
|
|
|
119
|
+
configure(config: Partial<PlayerConfig>) {
|
|
120
|
+
$.extend(true, this.config, config);
|
|
121
|
+
}
|
|
122
|
+
|
|
140
123
|
async init(playerElement: HTMLElement) {
|
|
141
124
|
assert.ok(!this.player, 'Player already initialized');
|
|
142
|
-
assert.ok(playerElement, 'Player element is required');
|
|
125
|
+
assert.ok(playerElement, 'Player container element is required');
|
|
143
126
|
if (
|
|
144
127
|
this.config.debug === 'all' ||
|
|
145
128
|
this.config.debug === 'clappr'
|
|
@@ -147,26 +130,25 @@ export class Player {
|
|
|
147
130
|
Log.setLevel(0);
|
|
148
131
|
}
|
|
149
132
|
|
|
150
|
-
Log.debug('Config', this.config);
|
|
151
|
-
|
|
152
|
-
this.
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
});
|
|
133
|
+
Log.debug(T, 'Config', this.config);
|
|
134
|
+
|
|
135
|
+
this.configurePlaybacks();
|
|
136
|
+
const coreOpts = this.buildCoreOptions(playerElement);
|
|
137
|
+
const {
|
|
138
|
+
core,
|
|
139
|
+
container,
|
|
140
|
+
} = Loader.registeredPlugins;
|
|
141
|
+
coreOpts.plugins = {
|
|
142
|
+
core: Object.values(core),
|
|
143
|
+
container: Object.values(container),
|
|
144
|
+
playback: Loader.registeredPlaybacks,
|
|
145
|
+
} as CorePluginOptions;
|
|
146
|
+
Log.debug(T, 'coreOpts', coreOpts);
|
|
147
|
+
return this.initPlayer(coreOpts);
|
|
166
148
|
}
|
|
167
149
|
|
|
168
150
|
destroy() {
|
|
169
|
-
|
|
151
|
+
Log.debug(T, 'destroy', { player: !!this.player });
|
|
170
152
|
if (this.player) {
|
|
171
153
|
this.player.destroy();
|
|
172
154
|
this.player = null;
|
|
@@ -197,8 +179,8 @@ export class Player {
|
|
|
197
179
|
Loader.registerPlugin(plugin);
|
|
198
180
|
}
|
|
199
181
|
|
|
200
|
-
|
|
201
|
-
|
|
182
|
+
static unregisterPlugin(plugin: PlayerPlugin) {
|
|
183
|
+
Loader.unregisterPlugin(plugin);
|
|
202
184
|
}
|
|
203
185
|
|
|
204
186
|
private initPlayer(coreOptions: CoreOptions) {
|
|
@@ -229,7 +211,7 @@ export class Player {
|
|
|
229
211
|
// TODO sort this out
|
|
230
212
|
private async tuneIn() {
|
|
231
213
|
assert.ok(this.player);
|
|
232
|
-
|
|
214
|
+
Log.debug(T, 'tuneIn enter', {
|
|
233
215
|
ready: this.clapprReady,
|
|
234
216
|
tuneInEntered: this.tuneInEntered,
|
|
235
217
|
});
|
|
@@ -247,7 +229,6 @@ export class Player {
|
|
|
247
229
|
this.bindBitrateChangeHandler();
|
|
248
230
|
}
|
|
249
231
|
player.core.on(ClapprEvents.CORE_ACTIVE_CONTAINER_CHANGED, () => {
|
|
250
|
-
trace(`${T} onActiveContainerChanged`);
|
|
251
232
|
this.bindBitrateChangeHandler();
|
|
252
233
|
}, null);
|
|
253
234
|
if (
|
|
@@ -267,33 +248,9 @@ export class Player {
|
|
|
267
248
|
}
|
|
268
249
|
}
|
|
269
250
|
|
|
270
|
-
private configurePlugins() {
|
|
271
|
-
// TODO remove !isiOS?
|
|
272
|
-
const useDash = !Browser.isiOS && this.config.multisources.some(
|
|
273
|
-
(el) => el.sourceDash && DashPlayback.canPlay(el.sourceDash)
|
|
274
|
-
);
|
|
275
|
-
if (useDash) {
|
|
276
|
-
this.scheduleLoad(async () => {
|
|
277
|
-
const module = await import('./plugins/dash-playback/DashPlayback.js');
|
|
278
|
-
Loader.registerPlayback(module.default);
|
|
279
|
-
})
|
|
280
|
-
}
|
|
281
|
-
// TODO remove !isiOS?
|
|
282
|
-
// if (!Browser.isiOS && this.config.multisources.some((el) => el.hls_mpegts_url)) {
|
|
283
|
-
const useHls = this.config.multisources.some(
|
|
284
|
-
(el) => HlsPlayback.canPlay(el.hlsCmafUrl || el.hlsMpegtsUrl || el.source)
|
|
285
|
-
);
|
|
286
|
-
if (useHls) {
|
|
287
|
-
this.scheduleLoad(async () => {
|
|
288
|
-
const module = await import('./plugins/hls-playback/HlsPlayback.js');
|
|
289
|
-
Loader.registerPlayback(module.default);
|
|
290
|
-
})
|
|
291
|
-
}
|
|
292
|
-
}
|
|
293
|
-
|
|
294
251
|
private events: CorePlayerEvents = {
|
|
295
252
|
onReady: () => {
|
|
296
|
-
|
|
253
|
+
Log.debug(T, 'onReady', { clapprReady: this.clapprReady, player: !!this.player, core: !!this.player?.core, activeContainer: !!this.player?.core.activeContainer });
|
|
297
254
|
if (this.clapprReady) {
|
|
298
255
|
return;
|
|
299
256
|
}
|
|
@@ -303,9 +260,6 @@ export class Player {
|
|
|
303
260
|
clearTimeout(this.timer);
|
|
304
261
|
this.timer = null;
|
|
305
262
|
}
|
|
306
|
-
// trace(`${T} onReady`, {
|
|
307
|
-
// activeContainer: !!this.player?.core.activeContainer,
|
|
308
|
-
// });
|
|
309
263
|
setTimeout(() => this.tuneIn(), 0);
|
|
310
264
|
},
|
|
311
265
|
onPlay: () => {
|
|
@@ -339,20 +293,21 @@ export class Player {
|
|
|
339
293
|
};
|
|
340
294
|
|
|
341
295
|
private buildCoreOptions(playerElement: HTMLElement): CoreOptions {
|
|
342
|
-
this.
|
|
343
|
-
const
|
|
344
|
-
const mediaSources =
|
|
345
|
-
const
|
|
346
|
-
const mainSourceUrl = unwrapSource(mainSource ? this.selectMediaTransport(mainSource, this.config.priorityTransport) : undefined);
|
|
296
|
+
const multisources = this.config.multisources;
|
|
297
|
+
const mainSource = this.config.playbackType === 'live' ? multisources.find(ms => ms.live !== false) : multisources[0];
|
|
298
|
+
const mediaSources = mainSource ? this.buildMediaSourcesList(mainSource): [];
|
|
299
|
+
const mainSourceUrl = mediaSources[0];
|
|
347
300
|
const poster = mainSource?.poster ?? this.config.poster;
|
|
348
301
|
|
|
349
302
|
const coreOptions: CoreOptions & PluginOptions = {
|
|
303
|
+
...this.config.pluginSettings,
|
|
350
304
|
autoPlay: this.config.autoPlay,
|
|
351
305
|
debug: this.config.debug || 'none',
|
|
352
306
|
events: this.events,
|
|
307
|
+
height: playerElement.clientHeight,
|
|
308
|
+
loop: this.config.loop,
|
|
353
309
|
multisources,
|
|
354
310
|
mute: this.config.mute,
|
|
355
|
-
...this.config.pluginSettings,
|
|
356
311
|
playback: {
|
|
357
312
|
controls: false,
|
|
358
313
|
preload: Browser.isiOS ? 'metadata' : 'none',
|
|
@@ -366,94 +321,62 @@ export class Player {
|
|
|
366
321
|
playbackType: this.config.playbackType,
|
|
367
322
|
poster,
|
|
368
323
|
width: playerElement.clientWidth,
|
|
369
|
-
height: playerElement.clientHeight,
|
|
370
|
-
loop: this.config.loop,
|
|
371
|
-
strings: this.config.strings,
|
|
372
324
|
source: mainSourceUrl,
|
|
373
325
|
sources: mediaSources,
|
|
326
|
+
strings: this.config.strings,
|
|
374
327
|
};
|
|
375
|
-
trace(`${T} buildCoreOptions`, coreOptions);
|
|
376
328
|
return coreOptions;
|
|
377
329
|
}
|
|
378
330
|
|
|
379
|
-
private
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
private scheduleLoad(cb: () => Promise<void>) {
|
|
384
|
-
this.pluginLoaders.push(cb);
|
|
385
|
-
}
|
|
386
|
-
|
|
387
|
-
private selectMediaTransport(ms: StreamMediaSource, priorityTransport: TransportPreference = ms.priorityTransport): string {
|
|
388
|
-
const cmafUrl = ms.hlsCmafUrl || ms.source; // source is default url for hls
|
|
389
|
-
const mpegtsUrl = ms.hlsMpegtsUrl; // no-low-latency HLS
|
|
390
|
-
const dashUrl = ms.sourceDash;
|
|
391
|
-
const masterSource = ms.source;
|
|
392
|
-
|
|
393
|
-
const mts = this.getAvailableTransportsPreference(priorityTransport);
|
|
394
|
-
for (const mt of mts) {
|
|
395
|
-
switch (mt) {
|
|
396
|
-
case 'dash':
|
|
397
|
-
if (dashUrl) {
|
|
398
|
-
return dashUrl;
|
|
399
|
-
}
|
|
400
|
-
break;
|
|
401
|
-
case 'hls':
|
|
402
|
-
if (cmafUrl) {
|
|
403
|
-
return cmafUrl;
|
|
404
|
-
}
|
|
405
|
-
break;
|
|
406
|
-
default:
|
|
407
|
-
return mpegtsUrl || masterSource;
|
|
408
|
-
}
|
|
409
|
-
}
|
|
410
|
-
// no supported transport found
|
|
411
|
-
return '';
|
|
412
|
-
}
|
|
413
|
-
|
|
414
|
-
private getAvailableTransportsPreference(priorityTransport: TransportPreference): MediaTransport[] {
|
|
415
|
-
const mtp: MediaTransport[] = [];
|
|
416
|
-
if (priorityTransport !== 'auto' && this.supportedMediaTransports.includes(priorityTransport)) {
|
|
417
|
-
mtp.push(priorityTransport);
|
|
418
|
-
}
|
|
419
|
-
for (const mt of this.supportedMediaTransports) {
|
|
420
|
-
if (mt !== priorityTransport) {
|
|
421
|
-
mtp.push(mt);
|
|
422
|
-
}
|
|
423
|
-
}
|
|
424
|
-
return mtp;
|
|
425
|
-
}
|
|
426
|
-
|
|
427
|
-
private checkMediaTransportsSupport() {
|
|
428
|
-
const isDashSupported = typeof (globalThis.MediaSource || (globalThis as any).WebKitMediaSource) === 'function';
|
|
429
|
-
if (isDashSupported) {
|
|
430
|
-
this.supportedMediaTransports.push('dash');
|
|
431
|
-
}
|
|
432
|
-
if (Hls.isSupported()) {
|
|
433
|
-
this.supportedMediaTransports.push('hls');
|
|
434
|
-
}
|
|
435
|
-
this.supportedMediaTransports.push('mpegts');
|
|
436
|
-
}
|
|
437
|
-
|
|
438
|
-
private processMultisources(transport?: TransportPreference): StreamMediaSource[] {
|
|
439
|
-
return this.config.multisources.map((ms: StreamMediaSource): StreamMediaSource => ({
|
|
440
|
-
...ms,
|
|
441
|
-
source: this.selectMediaTransport(ms, transport),
|
|
442
|
-
})).filter((el): el is StreamMediaSource => !!el.source);
|
|
331
|
+
private configurePlaybacks() {
|
|
332
|
+
Loader.registerPlayback(DashPlayback);
|
|
333
|
+
Loader.registerPlayback(HlsPlayback);
|
|
334
|
+
Loader.registerPlayback(HTML5Video);
|
|
443
335
|
}
|
|
444
336
|
|
|
445
337
|
private bindBitrateChangeHandler() {
|
|
446
|
-
trace(`${T} bindBitrateChangeHandler`, { activePlayback: !!this.player?.core.activePlayback });
|
|
447
338
|
this.player?.core.activeContainer.on(ClapprEvents.CONTAINER_BITRATE, (bitrate: BitrateInfo) => {
|
|
448
|
-
trace(`${T} onPlaybackBitrate`, { bitrate });
|
|
449
339
|
this.bitrateInfo = bitrate;
|
|
450
340
|
});
|
|
451
341
|
}
|
|
452
|
-
}
|
|
453
342
|
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
343
|
+
private buildMediaSourcesList(ms: StreamMediaSource): string[] {
|
|
344
|
+
const msl: string[] = [];
|
|
345
|
+
const sources: Record<'dash' | 'master' | 'hls' | 'mpegts', string | null> = {
|
|
346
|
+
dash: ms.sourceDash,
|
|
347
|
+
master: ms.source,
|
|
348
|
+
hls: ms.hlsCmafUrl,
|
|
349
|
+
mpegts: ms.hlsMpegtsUrl,
|
|
350
|
+
}
|
|
351
|
+
switch (this.config.priorityTransport) {
|
|
352
|
+
case 'dash':
|
|
353
|
+
if (sources.dash) {
|
|
354
|
+
msl.push(sources.dash);
|
|
355
|
+
sources.dash = null;
|
|
356
|
+
}
|
|
357
|
+
break;
|
|
358
|
+
case 'hls':
|
|
359
|
+
if (sources.hls) {
|
|
360
|
+
msl.push(sources.hls);
|
|
361
|
+
sources.hls = null;
|
|
362
|
+
}
|
|
363
|
+
if (sources.master?.endsWith('.m3u8')) {
|
|
364
|
+
msl.push(sources.master);
|
|
365
|
+
sources.master = null;
|
|
366
|
+
}
|
|
367
|
+
break;
|
|
368
|
+
case 'mpegts':
|
|
369
|
+
if (sources.mpegts) {
|
|
370
|
+
msl.push(sources.mpegts);
|
|
371
|
+
sources.mpegts = null
|
|
372
|
+
}
|
|
373
|
+
break;
|
|
374
|
+
}
|
|
375
|
+
Object.values(sources).forEach(s => {
|
|
376
|
+
if (s) {
|
|
377
|
+
msl.push(s);
|
|
378
|
+
}
|
|
379
|
+
});
|
|
380
|
+
return msl;
|
|
457
381
|
}
|
|
458
|
-
return typeof s === "string" ? s : s.source;
|
|
459
382
|
}
|
package/src/backend.ts
CHANGED
|
@@ -3,8 +3,8 @@ import { StreamMediaSource, StreamMediaSourceDto } from "./types";
|
|
|
3
3
|
export function fromStreamMediaSourceDto(s: StreamMediaSourceDto): StreamMediaSource {
|
|
4
4
|
return ({
|
|
5
5
|
...s,
|
|
6
|
-
hlsCmafUrl: s.hls_cmaf_url,
|
|
7
|
-
hlsMpegtsUrl: s.hls_mpegts_url,
|
|
6
|
+
hlsCmafUrl: s.hls_cmaf_url ?? null,
|
|
7
|
+
hlsMpegtsUrl: s.hls_mpegts_url ?? null,
|
|
8
8
|
priorityTransport: s.priority_transport,
|
|
9
9
|
sourceDash: s.source_dash,
|
|
10
10
|
vtt: s.vtt,
|
package/src/index.ts
CHANGED
package/src/internal.types.ts
CHANGED
|
@@ -1,11 +1,17 @@
|
|
|
1
1
|
import type {
|
|
2
2
|
CorePlugin,
|
|
3
3
|
ContainerPlugin,
|
|
4
|
-
Playback as
|
|
5
|
-
ExternalTrack,
|
|
4
|
+
Playback as ClapprPlayback,
|
|
6
5
|
} from "@clappr/core";
|
|
7
6
|
import { PlaybackType, PlayerDebugTag, StreamMediaSource } from "./types";
|
|
8
7
|
|
|
8
|
+
type ExternalTrack = {
|
|
9
|
+
kind?: "subtitles" | "captions";
|
|
10
|
+
src: string;
|
|
11
|
+
label: string;
|
|
12
|
+
lang: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
9
15
|
type MediacontrolStyles = {
|
|
10
16
|
// TODO
|
|
11
17
|
seekbar?: string;
|
|
@@ -76,10 +82,12 @@ export type ClapprVersionSpec = {
|
|
|
76
82
|
// TODO
|
|
77
83
|
}
|
|
78
84
|
|
|
85
|
+
export type PlaybackPluginFactory = typeof ClapprPlayback;
|
|
86
|
+
|
|
79
87
|
export type CorePluginOptions = {
|
|
80
88
|
core?: CorePlugin[];
|
|
81
89
|
container?: ContainerPlugin[];
|
|
82
|
-
playback?:
|
|
90
|
+
playback?: PlaybackPluginFactory[];
|
|
83
91
|
loadExternalPluginsFirst?: boolean;
|
|
84
92
|
loadExternalPlaybacksFirst?: boolean;
|
|
85
93
|
}
|
|
@@ -12,7 +12,6 @@ import DASHJS, {
|
|
|
12
12
|
IManifestInfo
|
|
13
13
|
} from 'dashjs';
|
|
14
14
|
|
|
15
|
-
import { trace } from '../../trace/index.js';
|
|
16
15
|
import { Duration, TimePosition, TimeValue } from '../../playback.types.js';
|
|
17
16
|
|
|
18
17
|
const AUTO = -1;
|
|
@@ -243,7 +242,6 @@ export default class DashPlayback extends HTML5Video {
|
|
|
243
242
|
}
|
|
244
243
|
|
|
245
244
|
_setup() {
|
|
246
|
-
trace(`${T} _setup`, { el: this.el });
|
|
247
245
|
const dash = DASHJS.MediaPlayer().create();
|
|
248
246
|
this._dash = dash;
|
|
249
247
|
this._dash.initialize();
|
|
@@ -265,7 +263,6 @@ export default class DashPlayback extends HTML5Video {
|
|
|
265
263
|
|
|
266
264
|
this._dash.on(DASHJS.MediaPlayer.events.STREAM_INITIALIZED, () => {
|
|
267
265
|
const bitrates = dash.getBitrateInfoListFor('video');
|
|
268
|
-
trace(`${T} STREAM_INITIALIZED`, { bitrates });
|
|
269
266
|
|
|
270
267
|
this._updatePlaybackType();
|
|
271
268
|
this._fillLevels(bitrates);
|
|
@@ -575,7 +572,6 @@ export default class DashPlayback extends HTML5Video {
|
|
|
575
572
|
}
|
|
576
573
|
|
|
577
574
|
play() {
|
|
578
|
-
trace(`${T} play`, { dash: !!this._dash });
|
|
579
575
|
if (!this._dash) {
|
|
580
576
|
this._setup();
|
|
581
577
|
}
|
|
@@ -619,13 +615,9 @@ export default class DashPlayback extends HTML5Video {
|
|
|
619
615
|
_updatePlaybackType() {
|
|
620
616
|
assert.ok(this._dash, 'An instance of dashjs MediaPlayer is required to update the playback type');
|
|
621
617
|
this._playbackType = this._dash.isDynamic() ? Playback.LIVE : Playback.VOD;
|
|
622
|
-
trace(`${T} _updatePlaybackType`, {
|
|
623
|
-
playbackType: this._playbackType,
|
|
624
|
-
});
|
|
625
618
|
}
|
|
626
619
|
|
|
627
620
|
_fillLevels(levels: BitrateInfo[]) {
|
|
628
|
-
// trace(`${T} _fillLevels`, {levels});
|
|
629
621
|
// TOOD check that levels[i].qualityIndex === i
|
|
630
622
|
this._levels = levels.map((level) => {
|
|
631
623
|
return { id: level.qualityIndex, level: level };
|
|
@@ -775,7 +767,6 @@ export default class DashPlayback extends HTML5Video {
|
|
|
775
767
|
// }
|
|
776
768
|
|
|
777
769
|
private onLevelSwitch(currentLevel: BitrateInfo) {
|
|
778
|
-
trace(`${T} onLevelSwitch`, {currentLevel});
|
|
779
770
|
this.trigger(Events.PLAYBACK_BITRATE, {
|
|
780
771
|
height: currentLevel.height,
|
|
781
772
|
width: currentLevel.width,
|
|
@@ -794,12 +785,11 @@ export default class DashPlayback extends HTML5Video {
|
|
|
794
785
|
}
|
|
795
786
|
|
|
796
787
|
DashPlayback.canPlay = function (resource, mimeType) {
|
|
797
|
-
trace(`${T} canPlay resource:%s mimeType:%s`, {resource, mimeType});
|
|
798
788
|
const resourceParts = resource.split('?')[0].match(/.*\.(.*)$/) || [];
|
|
799
789
|
const isDash = ((resourceParts.length > 1 && resourceParts[1].toLowerCase() === 'mpd') ||
|
|
800
790
|
mimeType === 'application/dash+xml' || mimeType === 'video/mp4');
|
|
801
791
|
const ctor = window.MediaSource || ('WebKitMediaSource' in window ? window.WebKitMediaSource : undefined);
|
|
802
792
|
const hasBrowserSupport = typeof ctor === 'function';
|
|
803
|
-
|
|
793
|
+
Log.debug(T, 'canPlay', {hasBrowserSupport, isDash});
|
|
804
794
|
return !!(hasBrowserSupport && isDash);
|
|
805
795
|
};
|
|
@@ -16,8 +16,9 @@ import HLSJS, {
|
|
|
16
16
|
type LevelLoadedData,
|
|
17
17
|
type LevelSwitchingData,
|
|
18
18
|
} from 'hls.js';
|
|
19
|
-
|
|
19
|
+
|
|
20
20
|
import { TimePosition } from '../../playback.types.js';
|
|
21
|
+
import { PlaybackType } from '../../types';
|
|
21
22
|
import { TimerId } from '../../utils/types';
|
|
22
23
|
|
|
23
24
|
const { now, listContainsIgnoreCase } = Utils;
|
|
@@ -904,6 +905,9 @@ export default class HlsPlayback extends HTML5Video {
|
|
|
904
905
|
HlsPlayback.canPlay = function (resource: string, mimeType?: string): boolean {
|
|
905
906
|
const resourceParts = resource.split('?')[0].match(/.*\.(.*)$/) || [];
|
|
906
907
|
const isHls = ((resourceParts.length > 1 && resourceParts[1].toLowerCase() === 'm3u8') || listContainsIgnoreCase(mimeType, ['application/vnd.apple.mpegurl', 'application/x-mpegURL']));
|
|
907
|
-
|
|
908
|
-
|
|
908
|
+
const isSupported = HLSJS.isSupported();
|
|
909
|
+
Log.debug(T, 'canPlay', {
|
|
910
|
+
isSupported, isHls,
|
|
911
|
+
})
|
|
912
|
+
return !!(isSupported && isHls);
|
|
909
913
|
};
|
package/src/types.ts
CHANGED
|
@@ -7,11 +7,9 @@ export type TransportPreference = MediaTransport | 'auto';
|
|
|
7
7
|
|
|
8
8
|
export type PlayerPlugin = {
|
|
9
9
|
new(...args: any[]): unknown;
|
|
10
|
-
type: 'core' | 'container' | 'playback';
|
|
10
|
+
type: string; // 'core' | 'container' | 'playback';
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
-
// export type PluginName = StdPluginName | string;
|
|
14
|
-
|
|
15
13
|
export type PlayerConfig = {
|
|
16
14
|
autoPlay?: boolean;
|
|
17
15
|
debug?: PlayerDebugSettings;
|
|
@@ -20,7 +18,6 @@ export type PlayerConfig = {
|
|
|
20
18
|
multisources: StreamMediaSource[];
|
|
21
19
|
mute?: boolean;
|
|
22
20
|
playbackType: PlaybackType;
|
|
23
|
-
// plugins?: PluginName[];
|
|
24
21
|
pluginSettings?: Record<string, unknown>;
|
|
25
22
|
poster?: string;
|
|
26
23
|
priorityTransport?: TransportPreference;
|
|
@@ -79,8 +76,8 @@ export type StreamMediaSourceDto = {
|
|
|
79
76
|
export type StreamMediaSource = {
|
|
80
77
|
description: string;
|
|
81
78
|
dvr: boolean;
|
|
82
|
-
hlsCmafUrl
|
|
83
|
-
hlsMpegtsUrl
|
|
79
|
+
hlsCmafUrl: string | null;
|
|
80
|
+
hlsMpegtsUrl: string | null;
|
|
84
81
|
id: number;
|
|
85
82
|
live: boolean;
|
|
86
83
|
priorityTransport: TransportPreference;
|
|
@@ -98,3 +95,18 @@ export type SrcProjectionType = 'regular' | '360' | 'vr180' | 'vr360tb';
|
|
|
98
95
|
export type ProjectionType = '360' | '180' | '360_TB';
|
|
99
96
|
|
|
100
97
|
export type TranslationSettings = Partial<Record<LangTag, Record<TranslationKey, string>>>;
|
|
98
|
+
|
|
99
|
+
export type BitrateInfo = {
|
|
100
|
+
height: number;
|
|
101
|
+
width: number;
|
|
102
|
+
bitrate: number;
|
|
103
|
+
level: number;
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
export enum PlayerEvent {
|
|
107
|
+
Ready = 'ready',
|
|
108
|
+
Play = 'play',
|
|
109
|
+
Pause = 'pause',
|
|
110
|
+
Stop = 'stop',
|
|
111
|
+
Ended = 'ended',
|
|
112
|
+
}
|