@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.
Files changed (82) hide show
  1. package/README.md +21 -6
  2. package/dist/cjs/index.js +792 -146
  3. package/dist/cjs/index.js.map +1 -1
  4. package/dist/esm/index.js +792 -146
  5. package/dist/esm/index.js.map +1 -1
  6. package/dist/player.css +185 -373
  7. package/dist/types/core/GatewayClient.d.ts +3 -4
  8. package/dist/types/core/InteractionController.d.ts +12 -0
  9. package/dist/types/core/MetaTrackManager.d.ts +1 -1
  10. package/dist/types/core/PlayerController.d.ts +18 -2
  11. package/dist/types/core/PlayerInterface.d.ts +10 -0
  12. package/dist/types/core/SeekingUtils.d.ts +3 -1
  13. package/dist/types/core/StreamStateClient.d.ts +1 -1
  14. package/dist/types/players/HlsJsPlayer.d.ts +8 -0
  15. package/dist/types/players/MewsWsPlayer/index.d.ts +1 -1
  16. package/dist/types/players/VideoJsPlayer.d.ts +12 -4
  17. package/dist/types/players/WebCodecsPlayer/SyncController.d.ts +1 -1
  18. package/dist/types/players/WebCodecsPlayer/index.d.ts +11 -0
  19. package/dist/types/players/WebCodecsPlayer/types.d.ts +25 -3
  20. package/dist/types/players/WebCodecsPlayer/worker/types.d.ts +20 -2
  21. package/dist/types/types.d.ts +32 -1
  22. package/dist/types/vanilla/FrameWorksPlayer.d.ts +5 -5
  23. package/dist/types/vanilla/index.d.ts +3 -3
  24. package/dist/workers/decoder.worker.js +183 -6
  25. package/dist/workers/decoder.worker.js.map +1 -1
  26. package/package.json +1 -1
  27. package/src/core/ABRController.ts +38 -36
  28. package/src/core/CodecUtils.ts +50 -47
  29. package/src/core/Disposable.ts +4 -4
  30. package/src/core/EventEmitter.ts +1 -1
  31. package/src/core/GatewayClient.ts +48 -48
  32. package/src/core/InteractionController.ts +89 -82
  33. package/src/core/LiveDurationProxy.ts +14 -16
  34. package/src/core/MetaTrackManager.ts +74 -66
  35. package/src/core/MistReporter.ts +72 -45
  36. package/src/core/MistSignaling.ts +59 -56
  37. package/src/core/PlayerController.ts +724 -375
  38. package/src/core/PlayerInterface.ts +89 -59
  39. package/src/core/PlayerManager.ts +118 -123
  40. package/src/core/PlayerRegistry.ts +59 -42
  41. package/src/core/QualityMonitor.ts +38 -31
  42. package/src/core/ScreenWakeLockManager.ts +8 -9
  43. package/src/core/SeekingUtils.ts +31 -22
  44. package/src/core/StreamStateClient.ts +75 -69
  45. package/src/core/SubtitleManager.ts +25 -23
  46. package/src/core/TelemetryReporter.ts +34 -31
  47. package/src/core/TimeFormat.ts +13 -17
  48. package/src/core/TimerManager.ts +25 -9
  49. package/src/core/UrlUtils.ts +20 -17
  50. package/src/core/detector.ts +44 -44
  51. package/src/core/index.ts +57 -48
  52. package/src/core/scorer.ts +137 -138
  53. package/src/core/selector.ts +2 -6
  54. package/src/global.d.ts +1 -1
  55. package/src/index.ts +46 -35
  56. package/src/players/DashJsPlayer.ts +175 -114
  57. package/src/players/HlsJsPlayer.ts +154 -76
  58. package/src/players/MewsWsPlayer/SourceBufferManager.ts +44 -39
  59. package/src/players/MewsWsPlayer/WebSocketManager.ts +9 -10
  60. package/src/players/MewsWsPlayer/index.ts +196 -154
  61. package/src/players/MewsWsPlayer/types.ts +21 -21
  62. package/src/players/MistPlayer.ts +46 -27
  63. package/src/players/MistWebRTCPlayer/index.ts +175 -129
  64. package/src/players/NativePlayer.ts +203 -143
  65. package/src/players/VideoJsPlayer.ts +200 -146
  66. package/src/players/WebCodecsPlayer/JitterBuffer.ts +6 -7
  67. package/src/players/WebCodecsPlayer/LatencyProfiles.ts +43 -43
  68. package/src/players/WebCodecsPlayer/RawChunkParser.ts +10 -10
  69. package/src/players/WebCodecsPlayer/SyncController.ts +46 -55
  70. package/src/players/WebCodecsPlayer/WebSocketController.ts +67 -69
  71. package/src/players/WebCodecsPlayer/index.ts +280 -220
  72. package/src/players/WebCodecsPlayer/polyfills/MediaStreamTrackGenerator.ts +12 -17
  73. package/src/players/WebCodecsPlayer/types.ts +81 -53
  74. package/src/players/WebCodecsPlayer/worker/decoder.worker.ts +255 -192
  75. package/src/players/WebCodecsPlayer/worker/types.ts +33 -29
  76. package/src/players/index.ts +8 -8
  77. package/src/styles/animations.css +2 -1
  78. package/src/styles/player.css +182 -356
  79. package/src/styles/tailwind.css +473 -159
  80. package/src/types.ts +75 -33
  81. package/src/vanilla/FrameWorksPlayer.ts +34 -19
  82. 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: 'video' | 'audio' | 'meta';
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?: 'live' | 'vod';
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<{ id: string; label: string; bitrate?: number; width?: number; height?: number; isAuto?: boolean; active?: boolean }>;
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(mimetype: string, source: StreamSource, streamInfo: StreamInfo): boolean | string[];
219
- abstract initialize(container: HTMLElement, source: StreamSource, options: PlayerOptions, streamInfo?: StreamInfo): Promise<HTMLVideoElement>;
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('play', () => options.onPlay?.());
264
- handleEvent('pause', () => options.onPause?.());
265
- handleEvent('ended', () => options.onEnded?.());
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('waiting', () => options.onWaiting?.());
269
- video.addEventListener('playing', () => options.onPlaying?.());
270
- video.addEventListener('canplay', () => options.onCanPlay?.());
291
+ video.addEventListener("waiting", () => options.onWaiting?.());
292
+ video.addEventListener("playing", () => options.onPlaying?.());
293
+ video.addEventListener("canplay", () => options.onCanPlay?.());
271
294
 
272
- video.addEventListener('durationchange', () => {
295
+ video.addEventListener("durationchange", () => {
273
296
  options.onDurationChange?.(video.duration);
274
297
  });
275
298
 
276
- video.addEventListener('timeupdate', () => {
299
+ video.addEventListener("timeupdate", () => {
277
300
  const currentTime = video.currentTime;
278
301
  options.onTimeUpdate?.(currentTime);
279
- this.emit('timeupdate', currentTime);
302
+ this.emit("timeupdate", currentTime);
280
303
  });
281
304
 
282
- video.addEventListener('error', () => {
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('error', error);
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('ready', video);
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({ id: String(i), label: tt.label || `CC ${i+1}`, lang: (tt as any).language, active: tt.mode === 'showing' });
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 = 'showing';
402
+ tt.mode = "showing";
377
403
  } else {
378
- tt.mode = 'disabled';
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 { v.currentTime = seekable.end(seekable.length - 1); } catch {}
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 { await (document as any).exitPictureInPicture?.(); } catch {}
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('picture-in-picture');
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`;