@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.
- package/dist/cjs/index.js +19493 -0
- package/dist/cjs/index.js.map +1 -0
- package/dist/esm/index.js +19398 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/player.css +2140 -0
- package/dist/types/core/ABRController.d.ts +164 -0
- package/dist/types/core/CodecUtils.d.ts +54 -0
- package/dist/types/core/Disposable.d.ts +61 -0
- package/dist/types/core/EventEmitter.d.ts +73 -0
- package/dist/types/core/GatewayClient.d.ts +144 -0
- package/dist/types/core/InteractionController.d.ts +121 -0
- package/dist/types/core/LiveDurationProxy.d.ts +102 -0
- package/dist/types/core/MetaTrackManager.d.ts +220 -0
- package/dist/types/core/MistReporter.d.ts +163 -0
- package/dist/types/core/MistSignaling.d.ts +148 -0
- package/dist/types/core/PlayerController.d.ts +665 -0
- package/dist/types/core/PlayerInterface.d.ts +230 -0
- package/dist/types/core/PlayerManager.d.ts +182 -0
- package/dist/types/core/PlayerRegistry.d.ts +27 -0
- package/dist/types/core/QualityMonitor.d.ts +184 -0
- package/dist/types/core/ScreenWakeLockManager.d.ts +70 -0
- package/dist/types/core/SeekingUtils.d.ts +142 -0
- package/dist/types/core/StreamStateClient.d.ts +108 -0
- package/dist/types/core/SubtitleManager.d.ts +111 -0
- package/dist/types/core/TelemetryReporter.d.ts +79 -0
- package/dist/types/core/TimeFormat.d.ts +97 -0
- package/dist/types/core/TimerManager.d.ts +83 -0
- package/dist/types/core/UrlUtils.d.ts +81 -0
- package/dist/types/core/detector.d.ts +149 -0
- package/dist/types/core/index.d.ts +49 -0
- package/dist/types/core/scorer.d.ts +167 -0
- package/dist/types/core/selector.d.ts +9 -0
- package/dist/types/index.d.ts +45 -0
- package/dist/types/lib/utils.d.ts +2 -0
- package/dist/types/players/DashJsPlayer.d.ts +102 -0
- package/dist/types/players/HlsJsPlayer.d.ts +70 -0
- package/dist/types/players/MewsWsPlayer/SourceBufferManager.d.ts +119 -0
- package/dist/types/players/MewsWsPlayer/WebSocketManager.d.ts +60 -0
- package/dist/types/players/MewsWsPlayer/index.d.ts +220 -0
- package/dist/types/players/MewsWsPlayer/types.d.ts +89 -0
- package/dist/types/players/MistPlayer.d.ts +25 -0
- package/dist/types/players/MistWebRTCPlayer/index.d.ts +133 -0
- package/dist/types/players/NativePlayer.d.ts +143 -0
- package/dist/types/players/VideoJsPlayer.d.ts +59 -0
- package/dist/types/players/WebCodecsPlayer/JitterBuffer.d.ts +118 -0
- package/dist/types/players/WebCodecsPlayer/LatencyProfiles.d.ts +64 -0
- package/dist/types/players/WebCodecsPlayer/RawChunkParser.d.ts +63 -0
- package/dist/types/players/WebCodecsPlayer/SyncController.d.ts +174 -0
- package/dist/types/players/WebCodecsPlayer/WebSocketController.d.ts +164 -0
- package/dist/types/players/WebCodecsPlayer/index.d.ts +149 -0
- package/dist/types/players/WebCodecsPlayer/polyfills/MediaStreamTrackGenerator.d.ts +105 -0
- package/dist/types/players/WebCodecsPlayer/types.d.ts +395 -0
- package/dist/types/players/WebCodecsPlayer/worker/decoder.worker.d.ts +13 -0
- package/dist/types/players/WebCodecsPlayer/worker/types.d.ts +197 -0
- package/dist/types/players/index.d.ts +14 -0
- package/dist/types/styles/index.d.ts +11 -0
- package/dist/types/types.d.ts +363 -0
- package/dist/types/vanilla/FrameWorksPlayer.d.ts +143 -0
- package/dist/types/vanilla/index.d.ts +19 -0
- package/dist/workers/decoder.worker.js +989 -0
- package/dist/workers/decoder.worker.js.map +1 -0
- package/package.json +80 -0
- package/src/core/ABRController.ts +550 -0
- package/src/core/CodecUtils.ts +257 -0
- package/src/core/Disposable.ts +120 -0
- package/src/core/EventEmitter.ts +113 -0
- package/src/core/GatewayClient.ts +439 -0
- package/src/core/InteractionController.ts +712 -0
- package/src/core/LiveDurationProxy.ts +270 -0
- package/src/core/MetaTrackManager.ts +753 -0
- package/src/core/MistReporter.ts +543 -0
- package/src/core/MistSignaling.ts +346 -0
- package/src/core/PlayerController.ts +2829 -0
- package/src/core/PlayerInterface.ts +432 -0
- package/src/core/PlayerManager.ts +900 -0
- package/src/core/PlayerRegistry.ts +149 -0
- package/src/core/QualityMonitor.ts +597 -0
- package/src/core/ScreenWakeLockManager.ts +163 -0
- package/src/core/SeekingUtils.ts +364 -0
- package/src/core/StreamStateClient.ts +457 -0
- package/src/core/SubtitleManager.ts +297 -0
- package/src/core/TelemetryReporter.ts +308 -0
- package/src/core/TimeFormat.ts +205 -0
- package/src/core/TimerManager.ts +209 -0
- package/src/core/UrlUtils.ts +179 -0
- package/src/core/detector.ts +382 -0
- package/src/core/index.ts +140 -0
- package/src/core/scorer.ts +553 -0
- package/src/core/selector.ts +16 -0
- package/src/global.d.ts +11 -0
- package/src/index.ts +75 -0
- package/src/lib/utils.ts +6 -0
- package/src/players/DashJsPlayer.ts +642 -0
- package/src/players/HlsJsPlayer.ts +483 -0
- package/src/players/MewsWsPlayer/SourceBufferManager.ts +572 -0
- package/src/players/MewsWsPlayer/WebSocketManager.ts +241 -0
- package/src/players/MewsWsPlayer/index.ts +1065 -0
- package/src/players/MewsWsPlayer/types.ts +106 -0
- package/src/players/MistPlayer.ts +188 -0
- package/src/players/MistWebRTCPlayer/index.ts +703 -0
- package/src/players/NativePlayer.ts +820 -0
- package/src/players/VideoJsPlayer.ts +643 -0
- package/src/players/WebCodecsPlayer/JitterBuffer.ts +299 -0
- package/src/players/WebCodecsPlayer/LatencyProfiles.ts +151 -0
- package/src/players/WebCodecsPlayer/RawChunkParser.ts +151 -0
- package/src/players/WebCodecsPlayer/SyncController.ts +456 -0
- package/src/players/WebCodecsPlayer/WebSocketController.ts +564 -0
- package/src/players/WebCodecsPlayer/index.ts +1650 -0
- package/src/players/WebCodecsPlayer/polyfills/MediaStreamTrackGenerator.ts +379 -0
- package/src/players/WebCodecsPlayer/types.ts +542 -0
- package/src/players/WebCodecsPlayer/worker/decoder.worker.ts +1360 -0
- package/src/players/WebCodecsPlayer/worker/types.ts +276 -0
- package/src/players/index.ts +22 -0
- package/src/styles/animations.css +21 -0
- package/src/styles/index.ts +52 -0
- package/src/styles/player.css +2126 -0
- package/src/styles/tailwind.css +1015 -0
- package/src/types.ts +421 -0
- package/src/vanilla/FrameWorksPlayer.ts +367 -0
- 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;
|
package/src/global.d.ts
ADDED
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';
|