@livepeer-frameworks/player-core 0.0.4 → 0.1.1
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/README.md +21 -6
- package/dist/cjs/index.js +792 -146
- package/dist/cjs/index.js.map +1 -1
- package/dist/esm/index.js +792 -146
- package/dist/esm/index.js.map +1 -1
- package/dist/player.css +185 -373
- package/dist/types/core/GatewayClient.d.ts +3 -4
- package/dist/types/core/InteractionController.d.ts +12 -0
- package/dist/types/core/MetaTrackManager.d.ts +1 -1
- package/dist/types/core/PlayerController.d.ts +18 -2
- package/dist/types/core/PlayerInterface.d.ts +10 -0
- package/dist/types/core/SeekingUtils.d.ts +3 -1
- package/dist/types/core/StreamStateClient.d.ts +1 -1
- package/dist/types/players/HlsJsPlayer.d.ts +8 -0
- package/dist/types/players/MewsWsPlayer/index.d.ts +1 -1
- package/dist/types/players/VideoJsPlayer.d.ts +12 -4
- package/dist/types/players/WebCodecsPlayer/SyncController.d.ts +1 -1
- package/dist/types/players/WebCodecsPlayer/index.d.ts +11 -0
- package/dist/types/players/WebCodecsPlayer/types.d.ts +25 -3
- package/dist/types/players/WebCodecsPlayer/worker/types.d.ts +20 -2
- package/dist/types/types.d.ts +32 -1
- package/dist/types/vanilla/FrameWorksPlayer.d.ts +5 -5
- package/dist/types/vanilla/index.d.ts +3 -3
- package/dist/workers/decoder.worker.js +183 -6
- package/dist/workers/decoder.worker.js.map +1 -1
- package/package.json +1 -1
- package/src/core/ABRController.ts +38 -36
- package/src/core/CodecUtils.ts +50 -47
- package/src/core/Disposable.ts +4 -4
- package/src/core/EventEmitter.ts +1 -1
- package/src/core/GatewayClient.ts +48 -48
- package/src/core/InteractionController.ts +89 -82
- package/src/core/LiveDurationProxy.ts +14 -16
- package/src/core/MetaTrackManager.ts +74 -66
- package/src/core/MistReporter.ts +72 -45
- package/src/core/MistSignaling.ts +59 -56
- package/src/core/PlayerController.ts +724 -375
- package/src/core/PlayerInterface.ts +89 -59
- package/src/core/PlayerManager.ts +118 -123
- package/src/core/PlayerRegistry.ts +59 -42
- package/src/core/QualityMonitor.ts +38 -31
- package/src/core/ScreenWakeLockManager.ts +8 -9
- package/src/core/SeekingUtils.ts +31 -22
- package/src/core/StreamStateClient.ts +75 -69
- package/src/core/SubtitleManager.ts +25 -23
- package/src/core/TelemetryReporter.ts +34 -31
- package/src/core/TimeFormat.ts +13 -17
- package/src/core/TimerManager.ts +25 -9
- package/src/core/UrlUtils.ts +20 -17
- package/src/core/detector.ts +44 -44
- package/src/core/index.ts +57 -48
- package/src/core/scorer.ts +137 -138
- package/src/core/selector.ts +2 -6
- package/src/global.d.ts +1 -1
- package/src/index.ts +46 -35
- package/src/players/DashJsPlayer.ts +175 -114
- package/src/players/HlsJsPlayer.ts +154 -76
- package/src/players/MewsWsPlayer/SourceBufferManager.ts +44 -39
- package/src/players/MewsWsPlayer/WebSocketManager.ts +9 -10
- package/src/players/MewsWsPlayer/index.ts +196 -154
- package/src/players/MewsWsPlayer/types.ts +21 -21
- package/src/players/MistPlayer.ts +46 -27
- package/src/players/MistWebRTCPlayer/index.ts +175 -129
- package/src/players/NativePlayer.ts +203 -143
- package/src/players/VideoJsPlayer.ts +200 -146
- package/src/players/WebCodecsPlayer/JitterBuffer.ts +6 -7
- package/src/players/WebCodecsPlayer/LatencyProfiles.ts +43 -43
- package/src/players/WebCodecsPlayer/RawChunkParser.ts +10 -10
- package/src/players/WebCodecsPlayer/SyncController.ts +46 -55
- package/src/players/WebCodecsPlayer/WebSocketController.ts +67 -69
- package/src/players/WebCodecsPlayer/index.ts +280 -220
- package/src/players/WebCodecsPlayer/polyfills/MediaStreamTrackGenerator.ts +12 -17
- package/src/players/WebCodecsPlayer/types.ts +81 -53
- package/src/players/WebCodecsPlayer/worker/decoder.worker.ts +255 -192
- package/src/players/WebCodecsPlayer/worker/types.ts +33 -29
- package/src/players/index.ts +8 -8
- package/src/styles/animations.css +2 -1
- package/src/styles/player.css +182 -356
- package/src/styles/tailwind.css +473 -159
- package/src/types.ts +75 -33
- package/src/vanilla/FrameWorksPlayer.ts +34 -19
- package/src/vanilla/index.ts +7 -7
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Common Player Interface
|
|
3
|
-
*
|
|
3
|
+
*
|
|
4
4
|
* All player implementations must implement this interface to ensure
|
|
5
5
|
* consistent behavior and enable the PlayerManager selection system
|
|
6
6
|
*/
|
|
@@ -14,7 +14,7 @@ export interface StreamSource {
|
|
|
14
14
|
}
|
|
15
15
|
|
|
16
16
|
export interface StreamTrack {
|
|
17
|
-
type:
|
|
17
|
+
type: "video" | "audio" | "meta";
|
|
18
18
|
codec: string;
|
|
19
19
|
codecstring?: string;
|
|
20
20
|
init?: string;
|
|
@@ -35,7 +35,7 @@ export interface StreamInfo {
|
|
|
35
35
|
meta: {
|
|
36
36
|
tracks: StreamTrack[];
|
|
37
37
|
};
|
|
38
|
-
type?:
|
|
38
|
+
type?: "live" | "vod";
|
|
39
39
|
}
|
|
40
40
|
|
|
41
41
|
export interface PlayerOptions {
|
|
@@ -61,6 +61,12 @@ export interface PlayerOptions {
|
|
|
61
61
|
onPlaying?: () => void;
|
|
62
62
|
onCanPlay?: () => void;
|
|
63
63
|
onDurationChange?: (duration: number) => void;
|
|
64
|
+
/** HLS.js configuration override (merged with defaults) */
|
|
65
|
+
hlsConfig?: Record<string, unknown>;
|
|
66
|
+
/** DASH.js configuration override (merged with defaults) */
|
|
67
|
+
dashConfig?: Record<string, unknown>;
|
|
68
|
+
/** Video.js VHS configuration override (merged with defaults) */
|
|
69
|
+
vhsConfig?: Record<string, unknown>;
|
|
64
70
|
}
|
|
65
71
|
|
|
66
72
|
export interface PlayerCapability {
|
|
@@ -93,26 +99,26 @@ export interface PlayerEvents {
|
|
|
93
99
|
export interface IPlayer {
|
|
94
100
|
/** Player metadata */
|
|
95
101
|
readonly capability: PlayerCapability;
|
|
96
|
-
|
|
102
|
+
|
|
97
103
|
/**
|
|
98
104
|
* Check if this player supports the given MIME type
|
|
99
105
|
*/
|
|
100
106
|
isMimeSupported(mimetype: string): boolean;
|
|
101
|
-
|
|
107
|
+
|
|
102
108
|
/**
|
|
103
109
|
* Check if this player can play in the current browser environment
|
|
104
110
|
* @param mimetype - MIME type to test
|
|
105
111
|
* @param source - Source information
|
|
106
112
|
* @param streamInfo - Stream metadata
|
|
107
|
-
* @returns false if not supported, true if supported (no track info),
|
|
113
|
+
* @returns false if not supported, true if supported (no track info),
|
|
108
114
|
* or array of supported track types
|
|
109
115
|
*/
|
|
110
116
|
isBrowserSupported(
|
|
111
|
-
mimetype: string,
|
|
112
|
-
source: StreamSource,
|
|
117
|
+
mimetype: string,
|
|
118
|
+
source: StreamSource,
|
|
113
119
|
streamInfo: StreamInfo
|
|
114
120
|
): boolean | string[];
|
|
115
|
-
|
|
121
|
+
|
|
116
122
|
/**
|
|
117
123
|
* Initialize the player with given source and options
|
|
118
124
|
* @param container - Container element to render in
|
|
@@ -127,33 +133,33 @@ export interface IPlayer {
|
|
|
127
133
|
options: PlayerOptions,
|
|
128
134
|
streamInfo?: StreamInfo
|
|
129
135
|
): Promise<HTMLVideoElement>;
|
|
130
|
-
|
|
136
|
+
|
|
131
137
|
/**
|
|
132
138
|
* Clean up and destroy the player.
|
|
133
139
|
* May be async if cleanup requires network requests (e.g., WHEP session DELETE).
|
|
134
140
|
*/
|
|
135
141
|
destroy(): void | Promise<void>;
|
|
136
|
-
|
|
142
|
+
|
|
137
143
|
/**
|
|
138
144
|
* Get the underlying video element (if available)
|
|
139
145
|
*/
|
|
140
146
|
getVideoElement(): HTMLVideoElement | null;
|
|
141
|
-
|
|
147
|
+
|
|
142
148
|
/**
|
|
143
149
|
* Set video size
|
|
144
150
|
*/
|
|
145
151
|
setSize?(width: number, height: number): void;
|
|
146
|
-
|
|
152
|
+
|
|
147
153
|
/**
|
|
148
154
|
* Add event listener
|
|
149
155
|
*/
|
|
150
156
|
on<K extends keyof PlayerEvents>(event: K, listener: (data: PlayerEvents[K]) => void): void;
|
|
151
|
-
|
|
157
|
+
|
|
152
158
|
/**
|
|
153
159
|
* Remove event listener
|
|
154
160
|
*/
|
|
155
161
|
off<K extends keyof PlayerEvents>(event: K, listener: (data: PlayerEvents[K]) => void): void;
|
|
156
|
-
|
|
162
|
+
|
|
157
163
|
/**
|
|
158
164
|
* Get current playback state
|
|
159
165
|
*/
|
|
@@ -165,7 +171,7 @@ export interface IPlayer {
|
|
|
165
171
|
getSeekableRange?(): { start: number; end: number } | null;
|
|
166
172
|
/** Optional: provide buffered ranges override */
|
|
167
173
|
getBufferedRanges?(): TimeRanges | null;
|
|
168
|
-
|
|
174
|
+
|
|
169
175
|
/**
|
|
170
176
|
* Control playback
|
|
171
177
|
*/
|
|
@@ -181,7 +187,15 @@ export interface IPlayer {
|
|
|
181
187
|
selectTextTrack?(id: string | null): void;
|
|
182
188
|
|
|
183
189
|
// Optional: quality/level selection
|
|
184
|
-
getQualities?(): Array<{
|
|
190
|
+
getQualities?(): Array<{
|
|
191
|
+
id: string;
|
|
192
|
+
label: string;
|
|
193
|
+
bitrate?: number;
|
|
194
|
+
width?: number;
|
|
195
|
+
height?: number;
|
|
196
|
+
isAuto?: boolean;
|
|
197
|
+
active?: boolean;
|
|
198
|
+
}>;
|
|
185
199
|
selectQuality?(id: string): void; // use 'auto' to enable ABR
|
|
186
200
|
getCurrentQuality?(): string | null;
|
|
187
201
|
|
|
@@ -210,37 +224,46 @@ export interface IPlayer {
|
|
|
210
224
|
*/
|
|
211
225
|
export abstract class BasePlayer implements IPlayer {
|
|
212
226
|
abstract readonly capability: PlayerCapability;
|
|
213
|
-
|
|
227
|
+
|
|
214
228
|
protected listeners: Map<string, Set<Function>> = new Map();
|
|
215
229
|
protected videoElement: HTMLVideoElement | null = null;
|
|
216
|
-
|
|
230
|
+
|
|
217
231
|
abstract isMimeSupported(mimetype: string): boolean;
|
|
218
|
-
abstract isBrowserSupported(
|
|
219
|
-
|
|
232
|
+
abstract isBrowserSupported(
|
|
233
|
+
mimetype: string,
|
|
234
|
+
source: StreamSource,
|
|
235
|
+
streamInfo: StreamInfo
|
|
236
|
+
): boolean | string[];
|
|
237
|
+
abstract initialize(
|
|
238
|
+
container: HTMLElement,
|
|
239
|
+
source: StreamSource,
|
|
240
|
+
options: PlayerOptions,
|
|
241
|
+
streamInfo?: StreamInfo
|
|
242
|
+
): Promise<HTMLVideoElement>;
|
|
220
243
|
abstract destroy(): void | Promise<void>;
|
|
221
|
-
|
|
244
|
+
|
|
222
245
|
getVideoElement(): HTMLVideoElement | null {
|
|
223
246
|
return this.videoElement;
|
|
224
247
|
}
|
|
225
|
-
|
|
248
|
+
|
|
226
249
|
on<K extends keyof PlayerEvents>(event: K, listener: (data: PlayerEvents[K]) => void): void {
|
|
227
250
|
if (!this.listeners.has(event)) {
|
|
228
251
|
this.listeners.set(event, new Set());
|
|
229
252
|
}
|
|
230
253
|
this.listeners.get(event)!.add(listener);
|
|
231
254
|
}
|
|
232
|
-
|
|
255
|
+
|
|
233
256
|
off<K extends keyof PlayerEvents>(event: K, listener: (data: PlayerEvents[K]) => void): void {
|
|
234
257
|
const eventListeners = this.listeners.get(event);
|
|
235
258
|
if (eventListeners) {
|
|
236
259
|
eventListeners.delete(listener);
|
|
237
260
|
}
|
|
238
261
|
}
|
|
239
|
-
|
|
262
|
+
|
|
240
263
|
protected emit<K extends keyof PlayerEvents>(event: K, data: PlayerEvents[K]): void {
|
|
241
264
|
const eventListeners = this.listeners.get(event);
|
|
242
265
|
if (eventListeners) {
|
|
243
|
-
eventListeners.forEach(listener => {
|
|
266
|
+
eventListeners.forEach((listener) => {
|
|
244
267
|
try {
|
|
245
268
|
listener(data);
|
|
246
269
|
} catch (e) {
|
|
@@ -249,7 +272,7 @@ export abstract class BasePlayer implements IPlayer {
|
|
|
249
272
|
});
|
|
250
273
|
}
|
|
251
274
|
}
|
|
252
|
-
|
|
275
|
+
|
|
253
276
|
protected setupVideoEventListeners(video: HTMLVideoElement, options: PlayerOptions): void {
|
|
254
277
|
const handleEvent = (eventName: keyof PlayerEvents, handler: () => void) => {
|
|
255
278
|
const listener = () => {
|
|
@@ -260,46 +283,44 @@ export abstract class BasePlayer implements IPlayer {
|
|
|
260
283
|
};
|
|
261
284
|
|
|
262
285
|
// Core playback events
|
|
263
|
-
handleEvent(
|
|
264
|
-
handleEvent(
|
|
265
|
-
handleEvent(
|
|
286
|
+
handleEvent("play", () => options.onPlay?.());
|
|
287
|
+
handleEvent("pause", () => options.onPause?.());
|
|
288
|
+
handleEvent("ended", () => options.onEnded?.());
|
|
266
289
|
|
|
267
290
|
// Buffering/state events (previously duplicated in Player.tsx onReady)
|
|
268
|
-
video.addEventListener(
|
|
269
|
-
video.addEventListener(
|
|
270
|
-
video.addEventListener(
|
|
291
|
+
video.addEventListener("waiting", () => options.onWaiting?.());
|
|
292
|
+
video.addEventListener("playing", () => options.onPlaying?.());
|
|
293
|
+
video.addEventListener("canplay", () => options.onCanPlay?.());
|
|
271
294
|
|
|
272
|
-
video.addEventListener(
|
|
295
|
+
video.addEventListener("durationchange", () => {
|
|
273
296
|
options.onDurationChange?.(video.duration);
|
|
274
297
|
});
|
|
275
298
|
|
|
276
|
-
video.addEventListener(
|
|
299
|
+
video.addEventListener("timeupdate", () => {
|
|
277
300
|
const currentTime = video.currentTime;
|
|
278
301
|
options.onTimeUpdate?.(currentTime);
|
|
279
|
-
this.emit(
|
|
302
|
+
this.emit("timeupdate", currentTime);
|
|
280
303
|
});
|
|
281
304
|
|
|
282
|
-
video.addEventListener(
|
|
283
|
-
const error = video.error ?
|
|
284
|
-
`Video error: ${video.error.message}` :
|
|
285
|
-
'Unknown video error';
|
|
305
|
+
video.addEventListener("error", () => {
|
|
306
|
+
const error = video.error ? `Video error: ${video.error.message}` : "Unknown video error";
|
|
286
307
|
options.onError?.(error);
|
|
287
|
-
this.emit(
|
|
308
|
+
this.emit("error", error);
|
|
288
309
|
});
|
|
289
310
|
|
|
290
311
|
// Call onReady LAST - after all listeners are attached
|
|
291
312
|
// This prevents race conditions where events fire before handlers exist
|
|
292
|
-
this.emit(
|
|
313
|
+
this.emit("ready", video);
|
|
293
314
|
if (options.onReady) {
|
|
294
315
|
options.onReady(video);
|
|
295
316
|
}
|
|
296
317
|
}
|
|
297
|
-
|
|
318
|
+
|
|
298
319
|
// Default implementations for optional methods
|
|
299
320
|
getCurrentTime(): number {
|
|
300
321
|
return this.videoElement?.currentTime || 0;
|
|
301
322
|
}
|
|
302
|
-
|
|
323
|
+
|
|
303
324
|
getDuration(): number {
|
|
304
325
|
return this.videoElement?.duration || 0;
|
|
305
326
|
}
|
|
@@ -311,37 +332,37 @@ export abstract class BasePlayer implements IPlayer {
|
|
|
311
332
|
getBufferedRanges(): TimeRanges | null {
|
|
312
333
|
return this.videoElement?.buffered ?? null;
|
|
313
334
|
}
|
|
314
|
-
|
|
335
|
+
|
|
315
336
|
isPaused(): boolean {
|
|
316
337
|
return this.videoElement?.paused ?? true;
|
|
317
338
|
}
|
|
318
|
-
|
|
339
|
+
|
|
319
340
|
isMuted(): boolean {
|
|
320
341
|
return this.videoElement?.muted ?? false;
|
|
321
342
|
}
|
|
322
|
-
|
|
343
|
+
|
|
323
344
|
async play(): Promise<void> {
|
|
324
345
|
if (this.videoElement) {
|
|
325
346
|
return this.videoElement.play();
|
|
326
347
|
}
|
|
327
348
|
}
|
|
328
|
-
|
|
349
|
+
|
|
329
350
|
pause(): void {
|
|
330
351
|
this.videoElement?.pause();
|
|
331
352
|
}
|
|
332
|
-
|
|
353
|
+
|
|
333
354
|
seek(time: number): void {
|
|
334
355
|
if (this.videoElement) {
|
|
335
356
|
this.videoElement.currentTime = time;
|
|
336
357
|
}
|
|
337
358
|
}
|
|
338
|
-
|
|
359
|
+
|
|
339
360
|
setVolume(volume: number): void {
|
|
340
361
|
if (this.videoElement) {
|
|
341
362
|
this.videoElement.volume = Math.max(0, Math.min(1, volume));
|
|
342
363
|
}
|
|
343
364
|
}
|
|
344
|
-
|
|
365
|
+
|
|
345
366
|
setMuted(muted: boolean): void {
|
|
346
367
|
if (this.videoElement) {
|
|
347
368
|
this.videoElement.muted = muted;
|
|
@@ -352,7 +373,7 @@ export abstract class BasePlayer implements IPlayer {
|
|
|
352
373
|
this.videoElement.playbackRate = rate;
|
|
353
374
|
}
|
|
354
375
|
}
|
|
355
|
-
|
|
376
|
+
|
|
356
377
|
// Default captions/text tracks using native TextTrack API
|
|
357
378
|
getTextTracks(): Array<{ id: string; label: string; lang?: string; active: boolean }> {
|
|
358
379
|
const video = this.videoElement;
|
|
@@ -361,7 +382,12 @@ export abstract class BasePlayer implements IPlayer {
|
|
|
361
382
|
const list = video.textTracks as any as TextTrackList;
|
|
362
383
|
for (let i = 0; i < list.length; i++) {
|
|
363
384
|
const tt = list[i];
|
|
364
|
-
out.push({
|
|
385
|
+
out.push({
|
|
386
|
+
id: String(i),
|
|
387
|
+
label: tt.label || `CC ${i + 1}`,
|
|
388
|
+
lang: (tt as any).language,
|
|
389
|
+
active: tt.mode === "showing",
|
|
390
|
+
});
|
|
365
391
|
}
|
|
366
392
|
return out;
|
|
367
393
|
}
|
|
@@ -373,9 +399,9 @@ export abstract class BasePlayer implements IPlayer {
|
|
|
373
399
|
for (let i = 0; i < list.length; i++) {
|
|
374
400
|
const tt = list[i];
|
|
375
401
|
if (id !== null && String(i) === id) {
|
|
376
|
-
tt.mode =
|
|
402
|
+
tt.mode = "showing";
|
|
377
403
|
} else {
|
|
378
|
-
tt.mode =
|
|
404
|
+
tt.mode = "disabled";
|
|
379
405
|
}
|
|
380
406
|
}
|
|
381
407
|
}
|
|
@@ -392,7 +418,9 @@ export abstract class BasePlayer implements IPlayer {
|
|
|
392
418
|
if (!v) return;
|
|
393
419
|
const seekable = v.seekable;
|
|
394
420
|
if (seekable && seekable.length > 0) {
|
|
395
|
-
try {
|
|
421
|
+
try {
|
|
422
|
+
v.currentTime = seekable.end(seekable.length - 1);
|
|
423
|
+
} catch {}
|
|
396
424
|
}
|
|
397
425
|
}
|
|
398
426
|
|
|
@@ -402,18 +430,20 @@ export abstract class BasePlayer implements IPlayer {
|
|
|
402
430
|
if (!v) return;
|
|
403
431
|
// Exit if already in PiP
|
|
404
432
|
if (document.pictureInPictureElement === v) {
|
|
405
|
-
try {
|
|
433
|
+
try {
|
|
434
|
+
await (document as any).exitPictureInPicture?.();
|
|
435
|
+
} catch {}
|
|
406
436
|
return;
|
|
407
437
|
}
|
|
408
438
|
try {
|
|
409
439
|
if (v.requestPictureInPicture) {
|
|
410
440
|
await v.requestPictureInPicture();
|
|
411
441
|
} else if (v.webkitSetPresentationMode) {
|
|
412
|
-
v.webkitSetPresentationMode(
|
|
442
|
+
v.webkitSetPresentationMode("picture-in-picture");
|
|
413
443
|
}
|
|
414
444
|
} catch {}
|
|
415
445
|
}
|
|
416
|
-
|
|
446
|
+
|
|
417
447
|
setSize(width: number, height: number): void {
|
|
418
448
|
if (this.videoElement) {
|
|
419
449
|
this.videoElement.style.width = `${width}px`;
|