@livepeer-frameworks/player-core 0.0.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.
Files changed (120) hide show
  1. package/dist/cjs/index.js +19493 -0
  2. package/dist/cjs/index.js.map +1 -0
  3. package/dist/esm/index.js +19398 -0
  4. package/dist/esm/index.js.map +1 -0
  5. package/dist/player.css +2140 -0
  6. package/dist/types/core/ABRController.d.ts +164 -0
  7. package/dist/types/core/CodecUtils.d.ts +54 -0
  8. package/dist/types/core/Disposable.d.ts +61 -0
  9. package/dist/types/core/EventEmitter.d.ts +73 -0
  10. package/dist/types/core/GatewayClient.d.ts +144 -0
  11. package/dist/types/core/InteractionController.d.ts +121 -0
  12. package/dist/types/core/LiveDurationProxy.d.ts +102 -0
  13. package/dist/types/core/MetaTrackManager.d.ts +220 -0
  14. package/dist/types/core/MistReporter.d.ts +163 -0
  15. package/dist/types/core/MistSignaling.d.ts +148 -0
  16. package/dist/types/core/PlayerController.d.ts +665 -0
  17. package/dist/types/core/PlayerInterface.d.ts +230 -0
  18. package/dist/types/core/PlayerManager.d.ts +182 -0
  19. package/dist/types/core/PlayerRegistry.d.ts +27 -0
  20. package/dist/types/core/QualityMonitor.d.ts +184 -0
  21. package/dist/types/core/ScreenWakeLockManager.d.ts +70 -0
  22. package/dist/types/core/SeekingUtils.d.ts +142 -0
  23. package/dist/types/core/StreamStateClient.d.ts +108 -0
  24. package/dist/types/core/SubtitleManager.d.ts +111 -0
  25. package/dist/types/core/TelemetryReporter.d.ts +79 -0
  26. package/dist/types/core/TimeFormat.d.ts +97 -0
  27. package/dist/types/core/TimerManager.d.ts +83 -0
  28. package/dist/types/core/UrlUtils.d.ts +81 -0
  29. package/dist/types/core/detector.d.ts +149 -0
  30. package/dist/types/core/index.d.ts +49 -0
  31. package/dist/types/core/scorer.d.ts +167 -0
  32. package/dist/types/core/selector.d.ts +9 -0
  33. package/dist/types/index.d.ts +45 -0
  34. package/dist/types/lib/utils.d.ts +2 -0
  35. package/dist/types/players/DashJsPlayer.d.ts +102 -0
  36. package/dist/types/players/HlsJsPlayer.d.ts +70 -0
  37. package/dist/types/players/MewsWsPlayer/SourceBufferManager.d.ts +119 -0
  38. package/dist/types/players/MewsWsPlayer/WebSocketManager.d.ts +60 -0
  39. package/dist/types/players/MewsWsPlayer/index.d.ts +220 -0
  40. package/dist/types/players/MewsWsPlayer/types.d.ts +89 -0
  41. package/dist/types/players/MistPlayer.d.ts +25 -0
  42. package/dist/types/players/MistWebRTCPlayer/index.d.ts +133 -0
  43. package/dist/types/players/NativePlayer.d.ts +143 -0
  44. package/dist/types/players/VideoJsPlayer.d.ts +59 -0
  45. package/dist/types/players/WebCodecsPlayer/JitterBuffer.d.ts +118 -0
  46. package/dist/types/players/WebCodecsPlayer/LatencyProfiles.d.ts +64 -0
  47. package/dist/types/players/WebCodecsPlayer/RawChunkParser.d.ts +63 -0
  48. package/dist/types/players/WebCodecsPlayer/SyncController.d.ts +174 -0
  49. package/dist/types/players/WebCodecsPlayer/WebSocketController.d.ts +164 -0
  50. package/dist/types/players/WebCodecsPlayer/index.d.ts +149 -0
  51. package/dist/types/players/WebCodecsPlayer/polyfills/MediaStreamTrackGenerator.d.ts +105 -0
  52. package/dist/types/players/WebCodecsPlayer/types.d.ts +395 -0
  53. package/dist/types/players/WebCodecsPlayer/worker/decoder.worker.d.ts +13 -0
  54. package/dist/types/players/WebCodecsPlayer/worker/types.d.ts +197 -0
  55. package/dist/types/players/index.d.ts +14 -0
  56. package/dist/types/styles/index.d.ts +11 -0
  57. package/dist/types/types.d.ts +363 -0
  58. package/dist/types/vanilla/FrameWorksPlayer.d.ts +143 -0
  59. package/dist/types/vanilla/index.d.ts +19 -0
  60. package/dist/workers/decoder.worker.js +989 -0
  61. package/dist/workers/decoder.worker.js.map +1 -0
  62. package/package.json +80 -0
  63. package/src/core/ABRController.ts +550 -0
  64. package/src/core/CodecUtils.ts +257 -0
  65. package/src/core/Disposable.ts +120 -0
  66. package/src/core/EventEmitter.ts +113 -0
  67. package/src/core/GatewayClient.ts +439 -0
  68. package/src/core/InteractionController.ts +712 -0
  69. package/src/core/LiveDurationProxy.ts +270 -0
  70. package/src/core/MetaTrackManager.ts +753 -0
  71. package/src/core/MistReporter.ts +543 -0
  72. package/src/core/MistSignaling.ts +346 -0
  73. package/src/core/PlayerController.ts +2829 -0
  74. package/src/core/PlayerInterface.ts +432 -0
  75. package/src/core/PlayerManager.ts +900 -0
  76. package/src/core/PlayerRegistry.ts +149 -0
  77. package/src/core/QualityMonitor.ts +597 -0
  78. package/src/core/ScreenWakeLockManager.ts +163 -0
  79. package/src/core/SeekingUtils.ts +364 -0
  80. package/src/core/StreamStateClient.ts +457 -0
  81. package/src/core/SubtitleManager.ts +297 -0
  82. package/src/core/TelemetryReporter.ts +308 -0
  83. package/src/core/TimeFormat.ts +205 -0
  84. package/src/core/TimerManager.ts +209 -0
  85. package/src/core/UrlUtils.ts +179 -0
  86. package/src/core/detector.ts +382 -0
  87. package/src/core/index.ts +140 -0
  88. package/src/core/scorer.ts +553 -0
  89. package/src/core/selector.ts +16 -0
  90. package/src/global.d.ts +11 -0
  91. package/src/index.ts +75 -0
  92. package/src/lib/utils.ts +6 -0
  93. package/src/players/DashJsPlayer.ts +642 -0
  94. package/src/players/HlsJsPlayer.ts +483 -0
  95. package/src/players/MewsWsPlayer/SourceBufferManager.ts +572 -0
  96. package/src/players/MewsWsPlayer/WebSocketManager.ts +241 -0
  97. package/src/players/MewsWsPlayer/index.ts +1065 -0
  98. package/src/players/MewsWsPlayer/types.ts +106 -0
  99. package/src/players/MistPlayer.ts +188 -0
  100. package/src/players/MistWebRTCPlayer/index.ts +703 -0
  101. package/src/players/NativePlayer.ts +820 -0
  102. package/src/players/VideoJsPlayer.ts +643 -0
  103. package/src/players/WebCodecsPlayer/JitterBuffer.ts +299 -0
  104. package/src/players/WebCodecsPlayer/LatencyProfiles.ts +151 -0
  105. package/src/players/WebCodecsPlayer/RawChunkParser.ts +151 -0
  106. package/src/players/WebCodecsPlayer/SyncController.ts +456 -0
  107. package/src/players/WebCodecsPlayer/WebSocketController.ts +564 -0
  108. package/src/players/WebCodecsPlayer/index.ts +1650 -0
  109. package/src/players/WebCodecsPlayer/polyfills/MediaStreamTrackGenerator.ts +379 -0
  110. package/src/players/WebCodecsPlayer/types.ts +542 -0
  111. package/src/players/WebCodecsPlayer/worker/decoder.worker.ts +1360 -0
  112. package/src/players/WebCodecsPlayer/worker/types.ts +276 -0
  113. package/src/players/index.ts +22 -0
  114. package/src/styles/animations.css +21 -0
  115. package/src/styles/index.ts +52 -0
  116. package/src/styles/player.css +2126 -0
  117. package/src/styles/tailwind.css +1015 -0
  118. package/src/types.ts +421 -0
  119. package/src/vanilla/FrameWorksPlayer.ts +367 -0
  120. package/src/vanilla/index.ts +22 -0
@@ -0,0 +1,432 @@
1
+ /**
2
+ * Common Player Interface
3
+ *
4
+ * All player implementations must implement this interface to ensure
5
+ * consistent behavior and enable the PlayerManager selection system
6
+ */
7
+
8
+ export interface StreamSource {
9
+ url: string;
10
+ type: string;
11
+ index?: number;
12
+ streamName?: string;
13
+ mistPlayerUrl?: string;
14
+ }
15
+
16
+ export interface StreamTrack {
17
+ type: 'video' | 'audio' | 'meta';
18
+ codec: string;
19
+ codecstring?: string;
20
+ init?: string;
21
+ /** Track index from MistServer (used for binary chunk routing) */
22
+ idx?: number;
23
+ // Video-specific
24
+ width?: number;
25
+ height?: number;
26
+ fpks?: number; // frames per kilosecond
27
+ // Audio-specific
28
+ channels?: number;
29
+ rate?: number; // sample rate
30
+ size?: number; // bits per sample
31
+ }
32
+
33
+ export interface StreamInfo {
34
+ source: StreamSource[];
35
+ meta: {
36
+ tracks: StreamTrack[];
37
+ };
38
+ type?: 'live' | 'vod';
39
+ }
40
+
41
+ export interface PlayerOptions {
42
+ autoplay?: boolean;
43
+ muted?: boolean;
44
+ controls?: boolean;
45
+ loop?: boolean;
46
+ poster?: string;
47
+ width?: number;
48
+ height?: number;
49
+ /** Enable dev mode - for Legacy player, uses MistServer's dev skin with source selection */
50
+ devMode?: boolean;
51
+ /** Enable debug logging in player implementations */
52
+ debug?: boolean;
53
+ onReady?: (element: HTMLVideoElement) => void;
54
+ onError?: (error: string | Error) => void;
55
+ onPlay?: () => void;
56
+ onPause?: () => void;
57
+ onEnded?: () => void;
58
+ onTimeUpdate?: (currentTime: number) => void;
59
+ // New callbacks for buffering/state management
60
+ onWaiting?: () => void;
61
+ onPlaying?: () => void;
62
+ onCanPlay?: () => void;
63
+ onDurationChange?: (duration: number) => void;
64
+ }
65
+
66
+ export interface PlayerCapability {
67
+ /** Player name for display */
68
+ name: string;
69
+ /** Unique identifier */
70
+ shortname: string;
71
+ /** Priority (lower number = higher priority) */
72
+ priority: number;
73
+ /** MIME types this player can handle */
74
+ mimes: string[];
75
+ }
76
+
77
+ export interface PlayerEvents {
78
+ ready: HTMLVideoElement;
79
+ error: string | Error;
80
+ play: void;
81
+ pause: void;
82
+ ended: void;
83
+ timeupdate: number;
84
+ /** Request to reload the player (e.g., Firefox segment error recovery) */
85
+ reloadrequested: { reason: string };
86
+ /** Seekable range changed */
87
+ seekablechange: { start: number; end: number; bufferWindow: number };
88
+ }
89
+
90
+ /**
91
+ * Base interface all players must implement
92
+ */
93
+ export interface IPlayer {
94
+ /** Player metadata */
95
+ readonly capability: PlayerCapability;
96
+
97
+ /**
98
+ * Check if this player supports the given MIME type
99
+ */
100
+ isMimeSupported(mimetype: string): boolean;
101
+
102
+ /**
103
+ * Check if this player can play in the current browser environment
104
+ * @param mimetype - MIME type to test
105
+ * @param source - Source information
106
+ * @param streamInfo - Stream metadata
107
+ * @returns false if not supported, true if supported (no track info),
108
+ * or array of supported track types
109
+ */
110
+ isBrowserSupported(
111
+ mimetype: string,
112
+ source: StreamSource,
113
+ streamInfo: StreamInfo
114
+ ): boolean | string[];
115
+
116
+ /**
117
+ * Initialize the player with given source and options
118
+ * @param container - Container element to render in
119
+ * @param source - Source to play
120
+ * @param options - Player options
121
+ * @param streamInfo - Full stream metadata (optional, for players that need track details)
122
+ * @returns Promise resolving to video element
123
+ */
124
+ initialize(
125
+ container: HTMLElement,
126
+ source: StreamSource,
127
+ options: PlayerOptions,
128
+ streamInfo?: StreamInfo
129
+ ): Promise<HTMLVideoElement>;
130
+
131
+ /**
132
+ * Clean up and destroy the player.
133
+ * May be async if cleanup requires network requests (e.g., WHEP session DELETE).
134
+ */
135
+ destroy(): void | Promise<void>;
136
+
137
+ /**
138
+ * Get the underlying video element (if available)
139
+ */
140
+ getVideoElement(): HTMLVideoElement | null;
141
+
142
+ /**
143
+ * Set video size
144
+ */
145
+ setSize?(width: number, height: number): void;
146
+
147
+ /**
148
+ * Add event listener
149
+ */
150
+ on<K extends keyof PlayerEvents>(event: K, listener: (data: PlayerEvents[K]) => void): void;
151
+
152
+ /**
153
+ * Remove event listener
154
+ */
155
+ off<K extends keyof PlayerEvents>(event: K, listener: (data: PlayerEvents[K]) => void): void;
156
+
157
+ /**
158
+ * Get current playback state
159
+ */
160
+ getCurrentTime?(): number;
161
+ getDuration?(): number;
162
+ isPaused?(): boolean;
163
+ isMuted?(): boolean;
164
+ /** Optional: provide an override seekable range (seconds) */
165
+ getSeekableRange?(): { start: number; end: number } | null;
166
+ /** Optional: provide buffered ranges override */
167
+ getBufferedRanges?(): TimeRanges | null;
168
+
169
+ /**
170
+ * Control playback
171
+ */
172
+ play?(): Promise<void>;
173
+ pause?(): void;
174
+ seek?(time: number): void;
175
+ setVolume?(volume: number): void;
176
+ setMuted?(muted: boolean): void;
177
+ setPlaybackRate?(rate: number): void;
178
+
179
+ // Optional: captions/text tracks
180
+ getTextTracks?(): Array<{ id: string; label: string; lang?: string; active: boolean }>;
181
+ selectTextTrack?(id: string | null): void;
182
+
183
+ // Optional: quality/level selection
184
+ getQualities?(): Array<{ id: string; label: string; bitrate?: number; width?: number; height?: number; isAuto?: boolean; active?: boolean }>;
185
+ selectQuality?(id: string): void; // use 'auto' to enable ABR
186
+ getCurrentQuality?(): string | null;
187
+
188
+ // Optional: live edge helpers
189
+ isLive?(): boolean;
190
+ jumpToLive?(): void;
191
+ /** Optional: frame step (direction -1/1, optional step seconds) */
192
+ frameStep?(direction: -1 | 1, seconds?: number): void;
193
+
194
+ // Optional: PiP
195
+ requestPiP?(): Promise<void>;
196
+
197
+ /**
198
+ * Optional: Retrieve player-specific stats (e.g., WebRTC inbound-rtp)
199
+ */
200
+ getStats?(): Promise<any>;
201
+
202
+ /**
203
+ * Optional: Retrieve approximate playback latency stats
204
+ */
205
+ getLatency?(): Promise<any>;
206
+ }
207
+
208
+ /**
209
+ * Base class providing common functionality
210
+ */
211
+ export abstract class BasePlayer implements IPlayer {
212
+ abstract readonly capability: PlayerCapability;
213
+
214
+ protected listeners: Map<string, Set<Function>> = new Map();
215
+ protected videoElement: HTMLVideoElement | null = null;
216
+
217
+ 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>;
220
+ abstract destroy(): void | Promise<void>;
221
+
222
+ getVideoElement(): HTMLVideoElement | null {
223
+ return this.videoElement;
224
+ }
225
+
226
+ on<K extends keyof PlayerEvents>(event: K, listener: (data: PlayerEvents[K]) => void): void {
227
+ if (!this.listeners.has(event)) {
228
+ this.listeners.set(event, new Set());
229
+ }
230
+ this.listeners.get(event)!.add(listener);
231
+ }
232
+
233
+ off<K extends keyof PlayerEvents>(event: K, listener: (data: PlayerEvents[K]) => void): void {
234
+ const eventListeners = this.listeners.get(event);
235
+ if (eventListeners) {
236
+ eventListeners.delete(listener);
237
+ }
238
+ }
239
+
240
+ protected emit<K extends keyof PlayerEvents>(event: K, data: PlayerEvents[K]): void {
241
+ const eventListeners = this.listeners.get(event);
242
+ if (eventListeners) {
243
+ eventListeners.forEach(listener => {
244
+ try {
245
+ listener(data);
246
+ } catch (e) {
247
+ console.error(`Error in ${event} listener:`, e);
248
+ }
249
+ });
250
+ }
251
+ }
252
+
253
+ protected setupVideoEventListeners(video: HTMLVideoElement, options: PlayerOptions): void {
254
+ const handleEvent = (eventName: keyof PlayerEvents, handler: () => void) => {
255
+ const listener = () => {
256
+ handler();
257
+ this.emit(eventName as any, undefined as any);
258
+ };
259
+ video.addEventListener(eventName, listener);
260
+ };
261
+
262
+ // Core playback events
263
+ handleEvent('play', () => options.onPlay?.());
264
+ handleEvent('pause', () => options.onPause?.());
265
+ handleEvent('ended', () => options.onEnded?.());
266
+
267
+ // 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?.());
271
+
272
+ video.addEventListener('durationchange', () => {
273
+ options.onDurationChange?.(video.duration);
274
+ });
275
+
276
+ video.addEventListener('timeupdate', () => {
277
+ const currentTime = video.currentTime;
278
+ options.onTimeUpdate?.(currentTime);
279
+ this.emit('timeupdate', currentTime);
280
+ });
281
+
282
+ video.addEventListener('error', () => {
283
+ const error = video.error ?
284
+ `Video error: ${video.error.message}` :
285
+ 'Unknown video error';
286
+ options.onError?.(error);
287
+ this.emit('error', error);
288
+ });
289
+
290
+ // Call onReady LAST - after all listeners are attached
291
+ // This prevents race conditions where events fire before handlers exist
292
+ this.emit('ready', video);
293
+ if (options.onReady) {
294
+ options.onReady(video);
295
+ }
296
+ }
297
+
298
+ // Default implementations for optional methods
299
+ getCurrentTime(): number {
300
+ return this.videoElement?.currentTime || 0;
301
+ }
302
+
303
+ getDuration(): number {
304
+ return this.videoElement?.duration || 0;
305
+ }
306
+
307
+ getSeekableRange(): { start: number; end: number } | null {
308
+ return null;
309
+ }
310
+
311
+ getBufferedRanges(): TimeRanges | null {
312
+ return this.videoElement?.buffered ?? null;
313
+ }
314
+
315
+ isPaused(): boolean {
316
+ return this.videoElement?.paused ?? true;
317
+ }
318
+
319
+ isMuted(): boolean {
320
+ return this.videoElement?.muted ?? false;
321
+ }
322
+
323
+ async play(): Promise<void> {
324
+ if (this.videoElement) {
325
+ return this.videoElement.play();
326
+ }
327
+ }
328
+
329
+ pause(): void {
330
+ this.videoElement?.pause();
331
+ }
332
+
333
+ seek(time: number): void {
334
+ if (this.videoElement) {
335
+ this.videoElement.currentTime = time;
336
+ }
337
+ }
338
+
339
+ setVolume(volume: number): void {
340
+ if (this.videoElement) {
341
+ this.videoElement.volume = Math.max(0, Math.min(1, volume));
342
+ }
343
+ }
344
+
345
+ setMuted(muted: boolean): void {
346
+ if (this.videoElement) {
347
+ this.videoElement.muted = muted;
348
+ }
349
+ }
350
+ setPlaybackRate(rate: number): void {
351
+ if (this.videoElement) {
352
+ this.videoElement.playbackRate = rate;
353
+ }
354
+ }
355
+
356
+ // Default captions/text tracks using native TextTrack API
357
+ getTextTracks(): Array<{ id: string; label: string; lang?: string; active: boolean }> {
358
+ const video = this.videoElement;
359
+ if (!video || !video.textTracks) return [];
360
+ const out: Array<{ id: string; label: string; lang?: string; active: boolean }> = [];
361
+ const list = video.textTracks as any as TextTrackList;
362
+ for (let i = 0; i < list.length; i++) {
363
+ 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' });
365
+ }
366
+ return out;
367
+ }
368
+
369
+ selectTextTrack(id: string | null): void {
370
+ const video = this.videoElement;
371
+ if (!video || !video.textTracks) return;
372
+ const list = video.textTracks as any as TextTrackList;
373
+ for (let i = 0; i < list.length; i++) {
374
+ const tt = list[i];
375
+ if (id !== null && String(i) === id) {
376
+ tt.mode = 'showing';
377
+ } else {
378
+ tt.mode = 'disabled';
379
+ }
380
+ }
381
+ }
382
+
383
+ // Default live helpers
384
+ isLive(): boolean {
385
+ const v = this.videoElement;
386
+ if (!v) return false;
387
+ return !isFinite(v.duration) || v.duration === Infinity;
388
+ }
389
+
390
+ jumpToLive(): void {
391
+ const v = this.videoElement;
392
+ if (!v) return;
393
+ const seekable = v.seekable;
394
+ if (seekable && seekable.length > 0) {
395
+ try { v.currentTime = seekable.end(seekable.length - 1); } catch {}
396
+ }
397
+ }
398
+
399
+ // Default PiP helper
400
+ async requestPiP(): Promise<void> {
401
+ const v: any = this.videoElement as any;
402
+ if (!v) return;
403
+ // Exit if already in PiP
404
+ if (document.pictureInPictureElement === v) {
405
+ try { await (document as any).exitPictureInPicture?.(); } catch {}
406
+ return;
407
+ }
408
+ try {
409
+ if (v.requestPictureInPicture) {
410
+ await v.requestPictureInPicture();
411
+ } else if (v.webkitSetPresentationMode) {
412
+ v.webkitSetPresentationMode('picture-in-picture');
413
+ }
414
+ } catch {}
415
+ }
416
+
417
+ setSize(width: number, height: number): void {
418
+ if (this.videoElement) {
419
+ this.videoElement.style.width = `${width}px`;
420
+ this.videoElement.style.height = `${height}px`;
421
+ }
422
+ }
423
+
424
+ // Default optional stats methods
425
+ async getStats(): Promise<any> {
426
+ return undefined;
427
+ }
428
+
429
+ async getLatency(): Promise<any> {
430
+ return undefined;
431
+ }
432
+ }