@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,553 @@
1
+ /**
2
+ * Player Scoring System
3
+ * Enhanced from MistMetaPlayer v3.1.0 with reliability and mode-aware scoring
4
+ *
5
+ * Implements the scoring algorithm for player selection with:
6
+ * - Track type scoring
7
+ * - Player priority scoring
8
+ * - Source order scoring
9
+ * - Player reliability scoring (new)
10
+ * - Playback mode bonuses (new)
11
+ * - Protocol-specific routing (new)
12
+ */
13
+
14
+ import type { PlaybackMode } from '../types';
15
+
16
+ export interface TrackScore {
17
+ video: number;
18
+ audio: number;
19
+ subtitle: number;
20
+ }
21
+
22
+ export interface PlayerScore {
23
+ base: number;
24
+ trackTypes: string[];
25
+ total: number;
26
+ // Extended score breakdown for debugging
27
+ breakdown?: {
28
+ trackScore: number;
29
+ priorityScore: number;
30
+ sourceScore: number;
31
+ reliabilityScore: number;
32
+ modeBonus: number;
33
+ routingBonus: number;
34
+ protocolPenalty: number;
35
+ };
36
+ }
37
+
38
+ /**
39
+ * Default track type scores
40
+ */
41
+ export const DEFAULT_TRACK_SCORES: TrackScore = {
42
+ video: 2.0,
43
+ audio: 1.0,
44
+ subtitle: 0.5
45
+ };
46
+
47
+ /**
48
+ * Calculate score for supported track types
49
+ */
50
+ export function calculateTrackScore(
51
+ supportedTracks: string[] | boolean,
52
+ scores: TrackScore = DEFAULT_TRACK_SCORES
53
+ ): number {
54
+ if (supportedTracks === true) {
55
+ // Player supports something but doesn't specify what
56
+ return 1.9; // Slightly less than perfect video score
57
+ }
58
+
59
+ if (supportedTracks === false || supportedTracks.length === 0) {
60
+ return 0;
61
+ }
62
+
63
+ let totalScore = 0;
64
+ for (const trackType of supportedTracks) {
65
+ const score = scores[trackType as keyof TrackScore];
66
+ if (score !== undefined) {
67
+ totalScore += score;
68
+ }
69
+ }
70
+
71
+ return totalScore;
72
+ }
73
+
74
+ /**
75
+ * Calculate maximum possible score for a stream
76
+ */
77
+ export function calculateMaxScore(
78
+ availableTracks: string[],
79
+ scores: TrackScore = DEFAULT_TRACK_SCORES
80
+ ): number {
81
+ return calculateTrackScore(availableTracks, scores);
82
+ }
83
+
84
+ /**
85
+ * Player priority scoring
86
+ */
87
+ export function calculatePriorityScore(priority: number, maxPriority: number): number {
88
+ // Lower priority number = higher score
89
+ // Normalize to 0-1 range, then invert
90
+ return 1 - (priority / Math.max(maxPriority, 1));
91
+ }
92
+
93
+ /**
94
+ * Source preference scoring based on MistServer ordering
95
+ */
96
+ export function calculateSourceScore(
97
+ sourceIndex: number,
98
+ totalSources: number
99
+ ): number {
100
+ // Earlier sources (lower index) get higher scores
101
+ return 1 - (sourceIndex / Math.max(totalSources - 1, 1));
102
+ }
103
+
104
+ /**
105
+ * Bandwidth/quality scoring
106
+ */
107
+ export function calculateQualityScore(
108
+ bandwidth?: number,
109
+ targetBandwidth?: number
110
+ ): number {
111
+ if (!bandwidth || !targetBandwidth) {
112
+ return 1.0; // Neutral score if no bandwidth info
113
+ }
114
+
115
+ // Score based on how close to target bandwidth
116
+ const ratio = Math.min(bandwidth, targetBandwidth) / Math.max(bandwidth, targetBandwidth);
117
+ return ratio;
118
+ }
119
+
120
+ /**
121
+ * Protocol blacklist - completely excluded from selection
122
+ * These protocols are truly broken and should never be considered.
123
+ *
124
+ * NOTE: Protocols that ARE supported by reference implementation but unreliable
125
+ * should be in PROTOCOL_PENALTIES instead (heavily penalized but still selectable).
126
+ */
127
+ export const PROTOCOL_BLACKLIST: Set<string> = new Set([
128
+ // Flash - browsers removed support in 2020
129
+ 'flash/7',
130
+ 'flash/10',
131
+ 'flash/11',
132
+ // Silverlight - dead technology
133
+ 'silverlight',
134
+ // SDP is WebRTC signaling format, not a playback source type
135
+ 'html5/application/sdp',
136
+ 'sdp',
137
+ // MPEG-TS - no browser support, no wrapper implementation in reference
138
+ 'html5/video/mpeg',
139
+ // Smooth Streaming - intentionally disabled in reference (commented out in dashjs.js!)
140
+ 'html5/application/vnd.ms-sstr+xml',
141
+ // Server-side only protocols - browsers can't connect to these directly
142
+ 'srt',
143
+ 'rtsp',
144
+ 'rtmp',
145
+ // MistServer internal protocols - not for browser playback
146
+ 'dtsc',
147
+ // NOTE: ws/video/raw is supported by WebCodecs player
148
+ // Image formats - not video playback
149
+ 'html5/image/jpeg',
150
+ // Script/metadata formats - not playback sources
151
+ 'html5/text/javascript',
152
+ // Subtitle-only formats - not standalone playback sources (used as tracks)
153
+ 'html5/text/vtt',
154
+ 'html5/text/plain',
155
+ ]);
156
+
157
+ /**
158
+ * Check if a protocol is blacklisted
159
+ */
160
+ export function isProtocolBlacklisted(mimeType: string): boolean {
161
+ return PROTOCOL_BLACKLIST.has(mimeType);
162
+ }
163
+
164
+ /**
165
+ * Protocol penalties for problematic protocols (not blacklisted, but deprioritized)
166
+ * These get subtracted from the total score to deprioritize certain protocols.
167
+ *
168
+ * Heavy penalties (0.50+): Reference-supported but consistently unreliable
169
+ * Medium penalties (0.20): Experimental or spotty support
170
+ */
171
+ export const PROTOCOL_PENALTIES: Record<string, number> = {
172
+ // WebCodecs raw - NOW PREFERRED for low-latency (no penalty)
173
+ // 'ws/video/raw': 0,
174
+ // 'wss/video/raw': 0,
175
+ // 'ws/video/h264': 0,
176
+ // 'wss/video/h264': 0,
177
+ // WebM - reference supports but unreliable in practice
178
+ 'html5/video/webm': 0.80, // Heavy penalty - very broken
179
+ 'html5/audio/webm': 0.60,
180
+ 'ws/video/webm': 0.80,
181
+ 'wss/video/webm': 0.80,
182
+ // MEWS - heavy penalty, prefer HLS/WebRTC (reference mews.js has issues)
183
+ 'ws/video/mp4': 0.50,
184
+ 'wss/video/mp4': 0.50,
185
+ // DASH - heavy penalty, broken implementation
186
+ 'dash/video/mp4': 0.90, // Below legacy
187
+ 'dash/video/webm': 0.95,
188
+ // CMAF-style protocols (fMP4 over HLS/DASH) - fragmentation issues
189
+ 'html5/application/vnd.apple.mpegurl;version=7': 0.20, // HLSv7 is CMAF-based
190
+ // LL-HLS specific - experimental, spotty support
191
+ 'll-hls': 0.20,
192
+ 'cmaf': 0.20,
193
+ };
194
+
195
+ /**
196
+ * Calculate protocol penalty based on known problematic protocols
197
+ */
198
+ export function calculateProtocolPenalty(mimeType: string): number {
199
+ // Direct match
200
+ if (PROTOCOL_PENALTIES[mimeType]) {
201
+ return PROTOCOL_PENALTIES[mimeType];
202
+ }
203
+ // Pattern-based penalties for protocols not explicitly listed
204
+ const lowerMime = mimeType.toLowerCase();
205
+ if (lowerMime.includes('webm')) {
206
+ return 0.50; // Heavy penalty for any WebM variant
207
+ }
208
+ if (lowerMime.startsWith('dash/')) {
209
+ return 0.40; // DASH penalty
210
+ }
211
+ if (lowerMime.includes('cmaf') || lowerMime.includes('ll-hls')) {
212
+ return 0.20;
213
+ }
214
+ return 0;
215
+ }
216
+
217
+ /**
218
+ * Player reliability scores
219
+ * Based on library maturity, error recovery, and overall stability
220
+ */
221
+ export const PLAYER_RELIABILITY: Record<string, number> = {
222
+ 'webcodecs': 0.95, // Stable, lowest latency option
223
+ 'videojs': 0.95, // Fast loading, built-in HLS via VHS
224
+ 'hlsjs': 0.90, // Battle-tested but slower to load
225
+ 'native': 0.85, // Native is lightweight but has edge cases
226
+ 'mist-webrtc': 0.85, // Full signaling features
227
+ 'mews': 0.75, // Custom protocol, less tested
228
+ 'mist-legacy': 0.70, // Ultimate fallback, delegates everything
229
+ 'dashjs': 0.50, // Broken, lowest reliability
230
+ };
231
+
232
+ /**
233
+ * Calculate player reliability score
234
+ */
235
+ export function calculateReliabilityScore(playerShortname: string): number {
236
+ return PLAYER_RELIABILITY[playerShortname] ?? 0.5;
237
+ }
238
+
239
+ /**
240
+ * Protocol bonuses for each playback mode
241
+ *
242
+ * IMPORTANT: Track compatibility is enforced by trackScore (50% weight).
243
+ * Mode bonuses (12% weight) only affect ordering among compatible options.
244
+ * A protocol missing required tracks will never be selected regardless of mode bonus.
245
+ *
246
+ * Priority rationale:
247
+ * - Low-latency: WHEP/WebRTC first (<1s), then MP4/WS (2-5s), HLS last (10-30s)
248
+ * - Quality: MP4/WS first (stable + low latency), HLS fallback, WHEP minimal
249
+ * - VOD: MP4/HLS first (seekable), WHEP hard penalty (no seek support)
250
+ * - Auto: MP4/WS balanced choice, WHEP for low latency, HLS last resort
251
+ */
252
+ export const MODE_PROTOCOL_BONUSES: Record<PlaybackMode, Record<string, number>> = {
253
+ 'low-latency': {
254
+ // WebCodecs raw/h264: HIGHEST PRIORITY - ultra-low latency via WebCodecs API
255
+ 'ws/video/raw': 0.55,
256
+ 'wss/video/raw': 0.55,
257
+ 'ws/video/h264': 0.52,
258
+ 'wss/video/h264': 0.52,
259
+ // WHEP/WebRTC: sub-second latency
260
+ 'whep': 0.50,
261
+ 'webrtc': 0.45,
262
+ 'mist/webrtc': 0.45,
263
+ // MP4/WS (MEWS): 2-5s latency, good fallback
264
+ 'ws/video/mp4': 0.30,
265
+ 'wss/video/mp4': 0.30,
266
+ // Progressive MP4: lower latency than HLS (5-10s vs 10-30s)
267
+ 'html5/video/mp4': 0.15,
268
+ // HLS: high latency, minimal bonus
269
+ 'html5/application/vnd.apple.mpegurl': 0.05,
270
+ },
271
+ 'quality': {
272
+ // MP4/WS: stable + lower latency than HLS, preferred when supported
273
+ 'ws/video/mp4': 0.45,
274
+ 'wss/video/mp4': 0.45,
275
+ // WebCodecs raw: below MEWS but above HLS - good quality + low latency
276
+ 'ws/video/raw': 0.40,
277
+ 'wss/video/raw': 0.40,
278
+ 'ws/video/h264': 0.38,
279
+ 'wss/video/h264': 0.38,
280
+ // HLS: ABR support, universal fallback
281
+ 'html5/application/vnd.apple.mpegurl': 0.30,
282
+ 'html5/video/mp4': 0.20,
283
+ // WebRTC: minimal for quality mode
284
+ 'whep': 0.05,
285
+ 'webrtc': 0.05,
286
+ },
287
+ 'vod': {
288
+ // VOD/Clip: Prefer seekable protocols, EXCLUDE WebRTC (no seek support)
289
+ 'html5/video/mp4': 0.50, // Progressive MP4 - best for clips
290
+ 'html5/application/vnd.apple.mpegurl': 0.45, // HLS - ABR support
291
+ 'dash/video/mp4': 0.40, // DASH - ABR support
292
+ 'ws/video/mp4': 0.35, // MEWS - seekable via MSE
293
+ 'wss/video/mp4': 0.35,
294
+ // WHEP/WebRTC: HARD PENALTY - no seek support, inappropriate for VOD
295
+ 'whep': -1.0,
296
+ 'webrtc': -1.0,
297
+ 'mist/webrtc': -1.0,
298
+ },
299
+ 'auto': {
300
+ // WebCodecs raw: highest priority for low-latency live streams
301
+ 'ws/video/raw': 0.50,
302
+ 'wss/video/raw': 0.50,
303
+ 'ws/video/h264': 0.48,
304
+ 'wss/video/h264': 0.48,
305
+ // Direct MP4: simple, reliable, preferred over HLS when available
306
+ 'html5/video/mp4': 0.42,
307
+ // WHEP/WebRTC: good for low latency
308
+ 'whep': 0.38,
309
+ 'webrtc': 0.35,
310
+ 'mist/webrtc': 0.35,
311
+ // MP4/WS (MEWS): lower latency than HLS
312
+ 'ws/video/mp4': 0.30,
313
+ 'wss/video/mp4': 0.30,
314
+ // HLS: high latency, fallback option (but reliable)
315
+ 'html5/application/vnd.apple.mpegurl': 0.20,
316
+ }
317
+ };
318
+
319
+ /**
320
+ * Calculate mode-specific bonus for a protocol
321
+ */
322
+ export function calculateModeBonus(mimeType: string, mode: PlaybackMode): number {
323
+ if (!mode) return 0;
324
+ return MODE_PROTOCOL_BONUSES[mode]?.[mimeType] ?? 0;
325
+ }
326
+
327
+ /**
328
+ * Protocol routing rules - certain players are preferred for certain protocols
329
+ */
330
+ export const PROTOCOL_ROUTING: Record<string, { prefer: string[]; avoid?: string[] }> = {
331
+ 'whep': { prefer: ['native'] },
332
+ 'webrtc': { prefer: ['mist-webrtc', 'native'] },
333
+ 'mist/webrtc': { prefer: ['mist-webrtc'] },
334
+
335
+ // Raw WebSocket (12-byte header + AVCC NAL units) - WebCodecs only
336
+ 'ws/video/raw': { prefer: ['webcodecs'] },
337
+ 'wss/video/raw': { prefer: ['webcodecs'] },
338
+
339
+ // Annex B WebSocket (H.264 NAL units) - WebCodecs
340
+ 'ws/video/h264': { prefer: ['webcodecs'] },
341
+ 'wss/video/h264': { prefer: ['webcodecs'] },
342
+
343
+ // MP4-muxed WebSocket - MEWS (uses MSE for demuxing)
344
+ 'ws/video/mp4': { prefer: ['mews'] },
345
+ 'wss/video/mp4': { prefer: ['mews'] },
346
+ 'ws/video/webm': { prefer: ['mews'] },
347
+ 'wss/video/webm': { prefer: ['mews'] },
348
+
349
+ // HLS
350
+ 'html5/application/vnd.apple.mpegurl': {
351
+ prefer: ['videojs', 'hlsjs'],
352
+ avoid: ['native'],
353
+ },
354
+ 'html5/application/vnd.apple.mpegurl;version=7': {
355
+ prefer: ['videojs', 'hlsjs'],
356
+ avoid: ['native'],
357
+ },
358
+
359
+ // DASH
360
+ 'dash/video/mp4': { prefer: ['dashjs', 'videojs'] },
361
+
362
+ // Progressive download
363
+ 'html5/video/mp4': { prefer: ['native'] },
364
+ 'html5/video/webm': { prefer: ['native'] },
365
+
366
+ // Audio-only formats
367
+ 'html5/audio/aac': { prefer: ['native'] },
368
+ 'html5/audio/mp3': { prefer: ['native'] },
369
+ 'html5/audio/flac': { prefer: ['native'] },
370
+ 'html5/audio/wav': { prefer: ['native'] },
371
+ };
372
+
373
+ /**
374
+ * Calculate routing bonus based on protocol+player pairing
375
+ */
376
+ export function calculateRoutingBonus(mimeType: string, playerShortname: string): number {
377
+ const rules = PROTOCOL_ROUTING[mimeType];
378
+ if (!rules) return 0;
379
+
380
+ // Check if player is avoided
381
+ if (rules.avoid?.includes(playerShortname)) {
382
+ return -0.1; // Penalty for avoided players
383
+ }
384
+
385
+ // Check if player is preferred
386
+ if (rules.prefer?.includes(playerShortname)) {
387
+ const preferIndex = rules.prefer.indexOf(playerShortname);
388
+ // First preferred player gets 0.15, second gets 0.10, etc.
389
+ return 0.15 - (preferIndex * 0.05);
390
+ }
391
+
392
+ return 0;
393
+ }
394
+
395
+ /**
396
+ * Comprehensive player scoring with enhanced factors
397
+ */
398
+ export function scorePlayer(
399
+ supportedTracks: string[] | boolean,
400
+ priority: number,
401
+ sourceIndex: number,
402
+ options: {
403
+ maxPriority?: number;
404
+ totalSources?: number;
405
+ trackScores?: Partial<TrackScore>;
406
+ bandwidth?: number;
407
+ targetBandwidth?: number;
408
+ // New options for enhanced scoring
409
+ playerShortname?: string;
410
+ mimeType?: string;
411
+ playbackMode?: PlaybackMode;
412
+ weights?: {
413
+ tracks: number;
414
+ priority: number;
415
+ source: number;
416
+ quality: number;
417
+ reliability?: number;
418
+ mode?: number;
419
+ routing?: number;
420
+ protocolPenalty?: number;
421
+ };
422
+ } = {}
423
+ ): PlayerScore {
424
+ const {
425
+ maxPriority = 10,
426
+ totalSources = 1,
427
+ trackScores = {},
428
+ bandwidth,
429
+ targetBandwidth,
430
+ playerShortname,
431
+ mimeType,
432
+ playbackMode = 'auto',
433
+ weights = {
434
+ tracks: 0.50, // Reduced from 0.70 to make room for new factors
435
+ priority: 0.10, // Reduced from 0.15
436
+ source: 0.05, // Reduced from 0.10
437
+ quality: 0.05, // Unchanged
438
+ reliability: 0.10, // NEW: Player stability
439
+ mode: 0.10, // Playback mode bonus (reduced slightly)
440
+ routing: 0.08, // Protocol routing preference
441
+ protocolPenalty: 1.0, // Protocol penalty weight (applied as subtraction)
442
+ }
443
+ } = options;
444
+
445
+ const finalTrackScores = { ...DEFAULT_TRACK_SCORES, ...trackScores };
446
+
447
+ // Individual component scores
448
+ const trackScore = calculateTrackScore(supportedTracks, finalTrackScores);
449
+ const priorityScore = calculatePriorityScore(priority, maxPriority);
450
+ const sourceScore = calculateSourceScore(sourceIndex, totalSources);
451
+ const qualityScore = calculateQualityScore(bandwidth, targetBandwidth);
452
+
453
+ // New enhanced scores
454
+ const reliabilityScore = playerShortname ? calculateReliabilityScore(playerShortname) : 0.5;
455
+ const modeBonus = mimeType ? calculateModeBonus(mimeType, playbackMode) : 0;
456
+ const routingBonus = mimeType && playerShortname ? calculateRoutingBonus(mimeType, playerShortname) : 0;
457
+ const protocolPenalty = mimeType ? calculateProtocolPenalty(mimeType) : 0;
458
+
459
+ // Weighted total score (penalty is subtracted)
460
+ const total =
461
+ trackScore * weights.tracks +
462
+ priorityScore * weights.priority +
463
+ sourceScore * weights.source +
464
+ qualityScore * weights.quality +
465
+ reliabilityScore * (weights.reliability ?? 0) +
466
+ modeBonus * (weights.mode ?? 0) +
467
+ routingBonus * (weights.routing ?? 0) -
468
+ protocolPenalty * (weights.protocolPenalty ?? 1.0);
469
+
470
+ return {
471
+ base: trackScore,
472
+ trackTypes: Array.isArray(supportedTracks) ? supportedTracks : [],
473
+ total,
474
+ breakdown: {
475
+ trackScore,
476
+ priorityScore,
477
+ sourceScore,
478
+ reliabilityScore,
479
+ modeBonus,
480
+ routingBonus,
481
+ protocolPenalty,
482
+ }
483
+ };
484
+ }
485
+
486
+ /**
487
+ * Compare two player scores
488
+ */
489
+ export function compareScores(a: PlayerScore, b: PlayerScore): number {
490
+ return b.total - a.total; // Higher scores first
491
+ }
492
+
493
+ /**
494
+ * Batch score multiple players and return sorted by best score
495
+ */
496
+ export function scoreAndRankPlayers<T extends { priority: number }>(
497
+ players: Array<{
498
+ player: T;
499
+ supportedTracks: string[] | boolean;
500
+ sourceIndex: number;
501
+ }>,
502
+ options?: Parameters<typeof scorePlayer>[3]
503
+ ): Array<{
504
+ player: T;
505
+ score: PlayerScore;
506
+ }> {
507
+ const scoredPlayers = players.map(({ player, supportedTracks, sourceIndex }) => ({
508
+ player,
509
+ score: scorePlayer(supportedTracks, player.priority, sourceIndex, options)
510
+ }));
511
+
512
+ return scoredPlayers.sort((a, b) => compareScores(a.score, b.score));
513
+ }
514
+
515
+ /**
516
+ * Utility to check if a score meets minimum thresholds
517
+ */
518
+ export function meetsMinimumScore(
519
+ score: PlayerScore,
520
+ requirements: {
521
+ minTotal?: number;
522
+ requireVideo?: boolean;
523
+ requireAudio?: boolean;
524
+ minTrackTypes?: number;
525
+ }
526
+ ): boolean {
527
+ const {
528
+ minTotal = 0,
529
+ requireVideo = false,
530
+ requireAudio = false,
531
+ minTrackTypes = 0
532
+ } = requirements;
533
+
534
+ // Check total score
535
+ if (score.total < minTotal) {
536
+ return false;
537
+ }
538
+
539
+ // Check track type requirements
540
+ if (requireVideo && !score.trackTypes.includes('video')) {
541
+ return false;
542
+ }
543
+
544
+ if (requireAudio && !score.trackTypes.includes('audio')) {
545
+ return false;
546
+ }
547
+
548
+ if (score.trackTypes.length < minTrackTypes) {
549
+ return false;
550
+ }
551
+
552
+ return true;
553
+ }
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Player Selection Types
3
+ *
4
+ * Re-exports from PlayerManager for backwards compatibility.
5
+ * All selection logic is now consolidated in PlayerManager.
6
+ */
7
+
8
+ export type {
9
+ PlayerSelection,
10
+ PlayerCombination,
11
+ PlayerManagerOptions,
12
+ } from './PlayerManager';
13
+
14
+ // Legacy type aliases for external consumers
15
+ import type { PlayerManagerOptions } from './PlayerManager';
16
+ export type SelectionOptions = PlayerManagerOptions;
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Global type declarations for player-core
3
+ */
4
+
5
+ // Web Worker inline imports (rollup-plugin-web-worker-loader)
6
+ declare module 'web-worker:*' {
7
+ const WorkerFactory: {
8
+ new (): Worker;
9
+ };
10
+ export default WorkerFactory;
11
+ }
package/src/index.ts ADDED
@@ -0,0 +1,75 @@
1
+ /**
2
+ * @livepeer-frameworks/player-core
3
+ *
4
+ * Framework-agnostic core player logic for FrameWorks streaming.
5
+ * This package provides:
6
+ * - PlayerManager: Intelligent player/protocol selection
7
+ * - PlayerController: High-level player orchestration
8
+ * - GatewayClient: Gateway endpoint resolution
9
+ * - StreamStateClient: MistServer WebSocket/HTTP polling
10
+ * - Quality monitoring, ABR control, interaction handling
11
+ */
12
+
13
+ // Core classes
14
+ export { PlayerManager } from './core/PlayerManager';
15
+ export type {
16
+ PlayerSelection,
17
+ PlayerCombination,
18
+ PlayerManagerOptions,
19
+ PlayerManagerEvents,
20
+ } from './core/PlayerManager';
21
+ export { PlayerController, buildStreamInfoFromEndpoints } from './core/PlayerController';
22
+ export type { PlayerControllerConfig, PlayerControllerEvents } from './core/PlayerController';
23
+ export {
24
+ ensurePlayersRegistered,
25
+ registerAllPlayers,
26
+ createPlayerManager,
27
+ globalPlayerManager,
28
+ globalPlayerManager as globalRegistry
29
+ } from './core/PlayerRegistry';
30
+ export { GatewayClient } from './core/GatewayClient';
31
+
32
+ // Player implementations (framework-agnostic)
33
+ export * from './players';
34
+ export { StreamStateClient } from './core/StreamStateClient';
35
+ export { QualityMonitor } from './core/QualityMonitor';
36
+ export { ABRController } from './core/ABRController';
37
+ export { InteractionController } from './core/InteractionController';
38
+ export { MistSignaling } from './core/MistSignaling';
39
+ export { MistReporter } from './core/MistReporter';
40
+ export { MetaTrackManager } from './core/MetaTrackManager';
41
+ export type { MetaTrackSubscription } from './core/MetaTrackManager';
42
+ export { SubtitleManager } from './core/SubtitleManager';
43
+ export { LiveDurationProxy } from './core/LiveDurationProxy';
44
+ export { TimerManager } from './core/TimerManager';
45
+ export { TypedEventEmitter, TypedEventEmitter as EventEmitter } from './core/EventEmitter';
46
+ export { TelemetryReporter } from './core/TelemetryReporter';
47
+
48
+ // Player interface and base class
49
+ export type { IPlayer, PlayerCapability, PlayerEvents } from './core/PlayerInterface';
50
+ export { BasePlayer } from './core/PlayerInterface';
51
+ export type { StreamSource, StreamTrack, StreamInfo, PlayerOptions as CorePlayerOptions } from './core/PlayerInterface';
52
+
53
+ // Utilities
54
+ export * from './core/scorer';
55
+ export * from './core/selector';
56
+ export * from './core/detector';
57
+ export * from './core/UrlUtils';
58
+ // Note: CodecUtils has overlapping exports with detector (translateCodec), export specific items if needed
59
+
60
+ // Seeking utilities (centralized from React/Svelte wrappers)
61
+ export * from './core/SeekingUtils';
62
+ export type { LatencyTier, LiveThresholds, SeekableRange, SeekableRangeParams, CanSeekParams } from './core/SeekingUtils';
63
+
64
+ // Time formatting utilities
65
+ export * from './core/TimeFormat';
66
+ export type { TimeDisplayParams } from './core/TimeFormat';
67
+
68
+ // Styles
69
+ export { ensurePlayerStyles, injectPlayerStyles } from './styles';
70
+
71
+ // Utility functions
72
+ export { cn } from './lib/utils';
73
+
74
+ // Types
75
+ export * from './types';
@@ -0,0 +1,6 @@
1
+ import { clsx, type ClassValue } from "clsx";
2
+ import { twMerge } from "tailwind-merge";
3
+
4
+ export function cn(...inputs: ClassValue[]): string {
5
+ return twMerge(clsx(inputs));
6
+ }