@livepeer-frameworks/player-react 0.1.0 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +7 -9
- package/dist/cjs/_virtual/_rollupPluginBabelHelpers.js +359 -0
- package/dist/cjs/_virtual/_rollupPluginBabelHelpers.js.map +1 -0
- package/dist/cjs/assets/logomark.svg.js +8 -0
- package/dist/cjs/assets/logomark.svg.js.map +1 -0
- package/dist/cjs/components/DevModePanel.js +826 -0
- package/dist/cjs/components/DevModePanel.js.map +1 -0
- package/dist/cjs/components/DvdLogo.js +200 -0
- package/dist/cjs/components/DvdLogo.js.map +1 -0
- package/dist/cjs/components/Icons.js +439 -0
- package/dist/cjs/components/Icons.js.map +1 -0
- package/dist/cjs/components/IdleScreen.js +587 -0
- package/dist/cjs/components/IdleScreen.js.map +1 -0
- package/dist/cjs/components/LoadingScreen.js +523 -0
- package/dist/cjs/components/LoadingScreen.js.map +1 -0
- package/dist/cjs/components/Player.js +420 -0
- package/dist/cjs/components/Player.js.map +1 -0
- package/dist/cjs/components/PlayerControls.js +798 -0
- package/dist/cjs/components/PlayerControls.js.map +1 -0
- package/dist/cjs/components/PlayerErrorBoundary.js +80 -0
- package/dist/cjs/components/PlayerErrorBoundary.js.map +1 -0
- package/dist/cjs/components/SeekBar.js +253 -0
- package/dist/cjs/components/SeekBar.js.map +1 -0
- package/dist/cjs/components/SkipIndicator.js +92 -0
- package/dist/cjs/components/SkipIndicator.js.map +1 -0
- package/dist/cjs/components/SpeedIndicator.js +43 -0
- package/dist/cjs/components/SpeedIndicator.js.map +1 -0
- package/dist/cjs/components/StatsPanel.js +202 -0
- package/dist/cjs/components/StatsPanel.js.map +1 -0
- package/dist/cjs/components/StreamStateOverlay.js +229 -0
- package/dist/cjs/components/StreamStateOverlay.js.map +1 -0
- package/dist/cjs/components/ThumbnailOverlay.js +86 -0
- package/dist/cjs/components/ThumbnailOverlay.js.map +1 -0
- package/dist/cjs/components/TitleOverlay.js +32 -0
- package/dist/cjs/components/TitleOverlay.js.map +1 -0
- package/dist/cjs/context/PlayerContext.js +46 -0
- package/dist/cjs/context/PlayerContext.js.map +1 -0
- package/dist/cjs/hooks/useMetaTrack.js +165 -0
- package/dist/cjs/hooks/useMetaTrack.js.map +1 -0
- package/dist/cjs/hooks/usePlaybackQuality.js +131 -0
- package/dist/cjs/hooks/usePlaybackQuality.js.map +1 -0
- package/dist/cjs/hooks/usePlayerController.js +518 -0
- package/dist/cjs/hooks/usePlayerController.js.map +1 -0
- package/dist/cjs/hooks/usePlayerSelection.js +90 -0
- package/dist/cjs/hooks/usePlayerSelection.js.map +1 -0
- package/dist/cjs/hooks/useStreamState.js +360 -0
- package/dist/cjs/hooks/useStreamState.js.map +1 -0
- package/dist/cjs/hooks/useTelemetry.js +120 -0
- package/dist/cjs/hooks/useTelemetry.js.map +1 -0
- package/dist/cjs/hooks/useViewerEndpoints.js +222 -0
- package/dist/cjs/hooks/useViewerEndpoints.js.map +1 -0
- package/dist/cjs/index.js +97 -1
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/ui/badge.js +34 -0
- package/dist/cjs/ui/badge.js.map +1 -0
- package/dist/cjs/ui/button.js +74 -0
- package/dist/cjs/ui/button.js.map +1 -0
- package/dist/cjs/ui/context-menu.js +163 -0
- package/dist/cjs/ui/context-menu.js.map +1 -0
- package/dist/cjs/ui/slider.js +60 -0
- package/dist/cjs/ui/slider.js.map +1 -0
- package/dist/esm/_virtual/_rollupPluginBabelHelpers.js +329 -0
- package/dist/esm/_virtual/_rollupPluginBabelHelpers.js.map +1 -0
- package/dist/esm/assets/logomark.svg.js +4 -0
- package/dist/esm/assets/logomark.svg.js.map +1 -0
- package/dist/esm/components/DevModePanel.js +822 -0
- package/dist/esm/components/DevModePanel.js.map +1 -0
- package/dist/esm/components/DvdLogo.js +196 -0
- package/dist/esm/components/DvdLogo.js.map +1 -0
- package/dist/esm/components/Icons.js +421 -0
- package/dist/esm/components/Icons.js.map +1 -0
- package/dist/esm/components/IdleScreen.js +582 -0
- package/dist/esm/components/IdleScreen.js.map +1 -0
- package/dist/esm/components/LoadingScreen.js +519 -0
- package/dist/esm/components/LoadingScreen.js.map +1 -0
- package/dist/esm/components/Player.js +416 -0
- package/dist/esm/components/Player.js.map +1 -0
- package/dist/esm/components/PlayerControls.js +794 -0
- package/dist/esm/components/PlayerControls.js.map +1 -0
- package/dist/esm/components/PlayerErrorBoundary.js +76 -0
- package/dist/esm/components/PlayerErrorBoundary.js.map +1 -0
- package/dist/esm/components/SeekBar.js +249 -0
- package/dist/esm/components/SeekBar.js.map +1 -0
- package/dist/esm/components/SkipIndicator.js +88 -0
- package/dist/esm/components/SkipIndicator.js.map +1 -0
- package/dist/esm/components/SpeedIndicator.js +39 -0
- package/dist/esm/components/SpeedIndicator.js.map +1 -0
- package/dist/esm/components/StatsPanel.js +198 -0
- package/dist/esm/components/StatsPanel.js.map +1 -0
- package/dist/esm/components/StreamStateOverlay.js +224 -0
- package/dist/esm/components/StreamStateOverlay.js.map +1 -0
- package/dist/esm/components/ThumbnailOverlay.js +82 -0
- package/dist/esm/components/ThumbnailOverlay.js.map +1 -0
- package/dist/esm/components/TitleOverlay.js +28 -0
- package/dist/esm/components/TitleOverlay.js.map +1 -0
- package/dist/esm/context/PlayerContext.js +41 -0
- package/dist/esm/context/PlayerContext.js.map +1 -0
- package/dist/esm/hooks/useMetaTrack.js +163 -0
- package/dist/esm/hooks/useMetaTrack.js.map +1 -0
- package/dist/esm/hooks/usePlaybackQuality.js +129 -0
- package/dist/esm/hooks/usePlaybackQuality.js.map +1 -0
- package/dist/esm/hooks/usePlayerController.js +516 -0
- package/dist/esm/hooks/usePlayerController.js.map +1 -0
- package/dist/esm/hooks/usePlayerSelection.js +88 -0
- package/dist/esm/hooks/usePlayerSelection.js.map +1 -0
- package/dist/esm/hooks/useStreamState.js +358 -0
- package/dist/esm/hooks/useStreamState.js.map +1 -0
- package/dist/esm/hooks/useTelemetry.js +118 -0
- package/dist/esm/hooks/useTelemetry.js.map +1 -0
- package/dist/esm/hooks/useViewerEndpoints.js +220 -0
- package/dist/esm/hooks/useViewerEndpoints.js.map +1 -0
- package/dist/esm/index.js +23 -1
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/ui/badge.js +31 -0
- package/dist/esm/ui/badge.js.map +1 -0
- package/dist/esm/ui/button.js +52 -0
- package/dist/esm/ui/button.js.map +1 -0
- package/dist/esm/ui/context-menu.js +132 -0
- package/dist/esm/ui/context-menu.js.map +1 -0
- package/dist/esm/ui/slider.js +38 -0
- package/dist/esm/ui/slider.js.map +1 -0
- package/dist/types/components/DvdLogo.d.ts +1 -1
- package/dist/types/components/Icons.d.ts +1 -1
- package/dist/types/components/Player.d.ts +1 -1
- package/dist/types/components/PlayerErrorBoundary.d.ts +2 -1
- package/dist/types/components/StreamStateOverlay.d.ts +2 -2
- package/dist/types/components/SubtitleRenderer.d.ts +2 -2
- package/dist/types/context/PlayerContext.d.ts +2 -2
- package/dist/types/context/index.d.ts +2 -2
- package/dist/types/hooks/useMetaTrack.d.ts +3 -3
- package/dist/types/hooks/usePlaybackQuality.d.ts +2 -2
- package/dist/types/hooks/usePlayerController.d.ts +26 -3
- package/dist/types/hooks/usePlayerSelection.d.ts +1 -1
- package/dist/types/hooks/useStreamState.d.ts +1 -1
- package/dist/types/hooks/useTelemetry.d.ts +1 -1
- package/dist/types/hooks/useViewerEndpoints.d.ts +3 -3
- package/dist/types/index.d.ts +28 -28
- package/dist/types/types.d.ts +3 -3
- package/dist/types/ui/select.d.ts +1 -1
- package/package.json +22 -14
- package/src/components/DevModePanel.tsx +244 -143
- package/src/components/DvdLogo.tsx +1 -1
- package/src/components/Icons.tsx +105 -25
- package/src/components/IdleScreen.tsx +262 -128
- package/src/components/LoadingScreen.tsx +169 -151
- package/src/components/LogoOverlay.tsx +3 -6
- package/src/components/Player.tsx +126 -59
- package/src/components/PlayerControls.tsx +384 -272
- package/src/components/PlayerErrorBoundary.tsx +7 -13
- package/src/components/SeekBar.tsx +96 -88
- package/src/components/SkipIndicator.tsx +2 -12
- package/src/components/SpeedIndicator.tsx +2 -11
- package/src/components/StatsPanel.tsx +31 -22
- package/src/components/StreamStateOverlay.tsx +105 -49
- package/src/components/SubtitleRenderer.tsx +29 -29
- package/src/components/ThumbnailOverlay.tsx +5 -6
- package/src/components/TitleOverlay.tsx +2 -8
- package/src/context/PlayerContext.tsx +4 -8
- package/src/context/index.ts +3 -3
- package/src/hooks/useMetaTrack.ts +27 -27
- package/src/hooks/usePlaybackQuality.ts +3 -3
- package/src/hooks/usePlayerController.ts +246 -138
- package/src/hooks/usePlayerSelection.ts +6 -6
- package/src/hooks/useStreamState.ts +51 -56
- package/src/hooks/useTelemetry.ts +18 -3
- package/src/hooks/useViewerEndpoints.ts +34 -23
- package/src/index.tsx +36 -28
- package/src/types.ts +8 -8
- package/src/ui/badge.tsx +6 -5
- package/src/ui/button.tsx +9 -8
- package/src/ui/context-menu.tsx +42 -61
- package/src/ui/select.tsx +13 -7
- package/src/ui/slider.tsx +18 -29
- package/dist/types/components/players/DashJsPlayer.d.ts +0 -18
- package/dist/types/components/players/HlsJsPlayer.d.ts +0 -18
- package/dist/types/components/players/MewsWsPlayer/index.d.ts +0 -18
- package/dist/types/components/players/MistPlayer.d.ts +0 -20
- package/dist/types/components/players/MistWebRTCPlayer/index.d.ts +0 -20
- package/dist/types/components/players/NativePlayer.d.ts +0 -19
- package/dist/types/components/players/VideoJsPlayer.d.ts +0 -18
- package/src/components/players/DashJsPlayer.tsx +0 -56
- package/src/components/players/HlsJsPlayer.tsx +0 -56
- package/src/components/players/MewsWsPlayer/index.tsx +0 -56
- package/src/components/players/MistPlayer.tsx +0 -60
- package/src/components/players/MistWebRTCPlayer/index.tsx +0 -59
- package/src/components/players/NativePlayer.tsx +0 -58
- package/src/components/players/VideoJsPlayer.tsx +0 -56
|
@@ -14,7 +14,7 @@ const SOURCE_TYPE_LABELS: Record<string, string> = {
|
|
|
14
14
|
"dash/video/mp4": "DASH",
|
|
15
15
|
"html5/video/mp4": "MP4",
|
|
16
16
|
"html5/video/webm": "WebM",
|
|
17
|
-
|
|
17
|
+
whep: "WHEP",
|
|
18
18
|
"mist/html": "Mist",
|
|
19
19
|
"mist/legacy": "Auto",
|
|
20
20
|
"ws/video/mp4": "MEWS",
|
|
@@ -66,7 +66,7 @@ export interface DevModePanelProps {
|
|
|
66
66
|
*/
|
|
67
67
|
const DevModePanel: React.FC<DevModePanelProps> = ({
|
|
68
68
|
onSettingsChange,
|
|
69
|
-
playbackMode =
|
|
69
|
+
playbackMode = "auto",
|
|
70
70
|
onModeChange,
|
|
71
71
|
onReload,
|
|
72
72
|
streamInfo,
|
|
@@ -83,13 +83,16 @@ const DevModePanel: React.FC<DevModePanelProps> = ({
|
|
|
83
83
|
// Support both controlled and uncontrolled modes
|
|
84
84
|
const [internalIsOpen, setInternalIsOpen] = useState(false);
|
|
85
85
|
const isOpen = controlledIsOpen !== undefined ? controlledIsOpen : internalIsOpen;
|
|
86
|
-
const setIsOpen = useCallback(
|
|
87
|
-
|
|
88
|
-
onOpenChange
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
86
|
+
const setIsOpen = useCallback(
|
|
87
|
+
(value: boolean) => {
|
|
88
|
+
if (onOpenChange) {
|
|
89
|
+
onOpenChange(value);
|
|
90
|
+
} else {
|
|
91
|
+
setInternalIsOpen(value);
|
|
92
|
+
}
|
|
93
|
+
},
|
|
94
|
+
[onOpenChange]
|
|
95
|
+
);
|
|
93
96
|
const [activeTab, setActiveTab] = useState<"config" | "stats">("config");
|
|
94
97
|
const [, setCurrentComboIndex] = useState(0);
|
|
95
98
|
const [hoveredComboIndex, setHoveredComboIndex] = useState<number | null>(null);
|
|
@@ -180,7 +183,7 @@ const DevModePanel: React.FC<DevModePanelProps> = ({
|
|
|
180
183
|
|
|
181
184
|
// For backward compatibility (Next Option only cycles compatible)
|
|
182
185
|
const combinations = useMemo(() => {
|
|
183
|
-
return allCombinations.filter(c => c.compatible);
|
|
186
|
+
return allCombinations.filter((c) => c.compatible);
|
|
184
187
|
}, [allCombinations]);
|
|
185
188
|
|
|
186
189
|
// Find current active combo index based on current player/source (in allCombinations)
|
|
@@ -220,18 +223,21 @@ const DevModePanel: React.FC<DevModePanelProps> = ({
|
|
|
220
223
|
});
|
|
221
224
|
}, [combinations, activeCompatibleIndex, onSettingsChange]);
|
|
222
225
|
|
|
223
|
-
const handleSelectCombo = useCallback(
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
226
|
+
const handleSelectCombo = useCallback(
|
|
227
|
+
(index: number) => {
|
|
228
|
+
const combo = allCombinations[index];
|
|
229
|
+
if (!combo) return;
|
|
230
|
+
|
|
231
|
+
// Allow selecting even incompatible combos in dev mode (for testing)
|
|
232
|
+
setCurrentComboIndex(index);
|
|
233
|
+
onSettingsChange({
|
|
234
|
+
forcePlayer: combo.player,
|
|
235
|
+
forceType: combo.sourceType,
|
|
236
|
+
forceSource: combo.sourceIndex,
|
|
237
|
+
});
|
|
238
|
+
},
|
|
239
|
+
[allCombinations, onSettingsChange]
|
|
240
|
+
);
|
|
235
241
|
|
|
236
242
|
// Video stats - poll periodically when stats tab is open
|
|
237
243
|
const [stats, setStats] = useState<{
|
|
@@ -324,20 +330,17 @@ const DevModePanel: React.FC<DevModePanelProps> = ({
|
|
|
324
330
|
<div className="fw-dev-section">
|
|
325
331
|
<div className="fw-dev-label">Active</div>
|
|
326
332
|
<div className="fw-dev-value">
|
|
327
|
-
{currentPlayer?.name || "None"}{" "}
|
|
328
|
-
<span className="fw-dev-value-arrow">→</span>{" "}
|
|
333
|
+
{currentPlayer?.name || "None"} <span className="fw-dev-value-arrow">→</span>{" "}
|
|
329
334
|
{SOURCE_TYPE_LABELS[currentSource?.type || ""] || currentSource?.type || "—"}
|
|
330
335
|
</div>
|
|
331
|
-
{nodeId &&
|
|
332
|
-
<div className="fw-dev-value-muted">Node: {nodeId}</div>
|
|
333
|
-
)}
|
|
336
|
+
{nodeId && <div className="fw-dev-value-muted">Node: {nodeId}</div>}
|
|
334
337
|
</div>
|
|
335
338
|
|
|
336
339
|
{/* Playback Mode Selector */}
|
|
337
340
|
<div className="fw-dev-section">
|
|
338
341
|
<div className="fw-dev-label">Playback Mode</div>
|
|
339
342
|
<div className="fw-dev-mode-group">
|
|
340
|
-
{([
|
|
343
|
+
{(["auto", "low-latency", "quality"] as const).map((mode) => (
|
|
341
344
|
<button
|
|
342
345
|
key={mode}
|
|
343
346
|
type="button"
|
|
@@ -347,31 +350,25 @@ const DevModePanel: React.FC<DevModePanelProps> = ({
|
|
|
347
350
|
playbackMode === mode && "fw-dev-mode-btn--active"
|
|
348
351
|
)}
|
|
349
352
|
>
|
|
350
|
-
{mode ===
|
|
353
|
+
{mode === "low-latency"
|
|
354
|
+
? "Low Lat"
|
|
355
|
+
: mode.charAt(0).toUpperCase() + mode.slice(1)}
|
|
351
356
|
</button>
|
|
352
357
|
))}
|
|
353
358
|
</div>
|
|
354
359
|
<div className="fw-dev-mode-desc">
|
|
355
|
-
{playbackMode ===
|
|
356
|
-
{playbackMode ===
|
|
357
|
-
{playbackMode ===
|
|
360
|
+
{playbackMode === "auto" && "Balanced: MP4/WS → WHEP → HLS"}
|
|
361
|
+
{playbackMode === "low-latency" && "WHEP/WebRTC first (<1s delay)"}
|
|
362
|
+
{playbackMode === "quality" && "MP4/WS first, HLS fallback"}
|
|
358
363
|
</div>
|
|
359
364
|
</div>
|
|
360
365
|
|
|
361
366
|
{/* Action buttons */}
|
|
362
367
|
<div className="fw-dev-actions">
|
|
363
|
-
<button
|
|
364
|
-
type="button"
|
|
365
|
-
onClick={handleReload}
|
|
366
|
-
className="fw-dev-action-btn"
|
|
367
|
-
>
|
|
368
|
+
<button type="button" onClick={handleReload} className="fw-dev-action-btn">
|
|
368
369
|
Reload
|
|
369
370
|
</button>
|
|
370
|
-
<button
|
|
371
|
-
type="button"
|
|
372
|
-
onClick={handleNextCombo}
|
|
373
|
-
className="fw-dev-action-btn"
|
|
374
|
-
>
|
|
371
|
+
<button type="button" onClick={handleNextCombo} className="fw-dev-action-btn">
|
|
375
372
|
Next Option
|
|
376
373
|
</button>
|
|
377
374
|
</div>
|
|
@@ -379,9 +376,7 @@ const DevModePanel: React.FC<DevModePanelProps> = ({
|
|
|
379
376
|
{/* Combo list */}
|
|
380
377
|
<div className="fw-dev-section" style={{ padding: 0, borderBottom: 0 }}>
|
|
381
378
|
<div className="fw-dev-list-header">
|
|
382
|
-
<span className="fw-dev-list-title">
|
|
383
|
-
Player Options ({combinations.length})
|
|
384
|
-
</span>
|
|
379
|
+
<span className="fw-dev-list-title">Player Options ({combinations.length})</span>
|
|
385
380
|
{allCombinations.length > combinations.length && (
|
|
386
381
|
<button
|
|
387
382
|
type="button"
|
|
@@ -399,14 +394,13 @@ const DevModePanel: React.FC<DevModePanelProps> = ({
|
|
|
399
394
|
>
|
|
400
395
|
<path d="M6 9l6 6 6-6" />
|
|
401
396
|
</svg>
|
|
402
|
-
{showDisabledPlayers ? "Hide" : "Show"} disabled (
|
|
397
|
+
{showDisabledPlayers ? "Hide" : "Show"} disabled (
|
|
398
|
+
{allCombinations.length - combinations.length})
|
|
403
399
|
</button>
|
|
404
400
|
)}
|
|
405
401
|
</div>
|
|
406
402
|
{allCombinations.length === 0 ? (
|
|
407
|
-
<div className="fw-dev-list-empty">
|
|
408
|
-
No stream info available
|
|
409
|
-
</div>
|
|
403
|
+
<div className="fw-dev-list-empty">No stream info available</div>
|
|
410
404
|
) : (
|
|
411
405
|
<div>
|
|
412
406
|
{allCombinations.map((combo, index) => {
|
|
@@ -415,11 +409,13 @@ const DevModePanel: React.FC<DevModePanelProps> = ({
|
|
|
415
409
|
if (!combo.compatible && !isCodecIncompat && !showDisabledPlayers) return null;
|
|
416
410
|
|
|
417
411
|
const isActive = activeComboIndex === index;
|
|
418
|
-
const typeLabel =
|
|
412
|
+
const typeLabel =
|
|
413
|
+
SOURCE_TYPE_LABELS[combo.sourceType] || combo.sourceType.split("/").pop();
|
|
419
414
|
|
|
420
415
|
// Determine score class
|
|
421
416
|
const getScoreClass = () => {
|
|
422
|
-
if (!combo.compatible && !isCodecIncompat)
|
|
417
|
+
if (!combo.compatible && !isCodecIncompat)
|
|
418
|
+
return "fw-dev-combo-score--disabled";
|
|
423
419
|
if (isCodecIncompat) return "fw-dev-combo-score--low";
|
|
424
420
|
if (combo.score >= 2) return "fw-dev-combo-score--high";
|
|
425
421
|
if (combo.score >= 1.5) return "fw-dev-combo-score--mid";
|
|
@@ -451,7 +447,8 @@ const DevModePanel: React.FC<DevModePanelProps> = ({
|
|
|
451
447
|
const row = e.currentTarget;
|
|
452
448
|
const containerRect = container.getBoundingClientRect();
|
|
453
449
|
const rowRect = row.getBoundingClientRect();
|
|
454
|
-
const relativePosition =
|
|
450
|
+
const relativePosition =
|
|
451
|
+
(rowRect.top - containerRect.top) / containerRect.height;
|
|
455
452
|
setTooltipAbove(relativePosition > 0.6);
|
|
456
453
|
}
|
|
457
454
|
}}
|
|
@@ -474,9 +471,10 @@ const DevModePanel: React.FC<DevModePanelProps> = ({
|
|
|
474
471
|
</span>
|
|
475
472
|
{/* Player + Protocol */}
|
|
476
473
|
<span className="fw-dev-combo-name">
|
|
477
|
-
{combo.playerName}{" "}
|
|
478
|
-
<span className="fw-dev-combo-
|
|
479
|
-
|
|
474
|
+
{combo.playerName} <span className="fw-dev-combo-arrow">→</span>{" "}
|
|
475
|
+
<span className={cn("fw-dev-combo-type", getTypeClass())}>
|
|
476
|
+
{typeLabel}
|
|
477
|
+
</span>
|
|
480
478
|
</span>
|
|
481
479
|
{/* Score */}
|
|
482
480
|
<span className={cn("fw-dev-combo-score", getScoreClass())}>
|
|
@@ -486,50 +484,105 @@ const DevModePanel: React.FC<DevModePanelProps> = ({
|
|
|
486
484
|
|
|
487
485
|
{/* Score breakdown tooltip */}
|
|
488
486
|
{hoveredComboIndex === index && (
|
|
489
|
-
<div
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
487
|
+
<div
|
|
488
|
+
className={cn(
|
|
489
|
+
"fw-dev-tooltip",
|
|
490
|
+
tooltipAbove ? "fw-dev-tooltip--above" : "fw-dev-tooltip--below"
|
|
491
|
+
)}
|
|
492
|
+
>
|
|
493
493
|
{/* Full player/source info */}
|
|
494
494
|
<div className="fw-dev-tooltip-header">
|
|
495
495
|
<div className="fw-dev-tooltip-title">{combo.playerName}</div>
|
|
496
496
|
<div className="fw-dev-tooltip-subtitle">{combo.sourceType}</div>
|
|
497
|
-
{combo.scoreBreakdown?.trackTypes &&
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
497
|
+
{combo.scoreBreakdown?.trackTypes &&
|
|
498
|
+
combo.scoreBreakdown.trackTypes.length > 0 && (
|
|
499
|
+
<div className="fw-dev-tooltip-tracks">
|
|
500
|
+
Tracks:{" "}
|
|
501
|
+
<span className="fw-dev-tooltip-value">
|
|
502
|
+
{combo.scoreBreakdown.trackTypes.join(", ")}
|
|
503
|
+
</span>
|
|
504
|
+
</div>
|
|
505
|
+
)}
|
|
502
506
|
</div>
|
|
503
507
|
{combo.compatible && combo.scoreBreakdown ? (
|
|
504
508
|
<>
|
|
505
|
-
<div className="fw-dev-tooltip-score">
|
|
509
|
+
<div className="fw-dev-tooltip-score">
|
|
510
|
+
Score: {combo.score.toFixed(2)}
|
|
511
|
+
</div>
|
|
506
512
|
<div className="fw-dev-tooltip-row">
|
|
507
|
-
Tracks [{combo.scoreBreakdown.trackTypes.join(
|
|
513
|
+
Tracks [{combo.scoreBreakdown.trackTypes.join(", ")}]:{" "}
|
|
514
|
+
<span className="fw-dev-tooltip-value">
|
|
515
|
+
{combo.scoreBreakdown.trackScore.toFixed(2)}
|
|
516
|
+
</span>{" "}
|
|
517
|
+
<span className="fw-dev-tooltip-weight">
|
|
518
|
+
x{combo.scoreBreakdown.weights.tracks}
|
|
519
|
+
</span>
|
|
508
520
|
</div>
|
|
509
521
|
<div className="fw-dev-tooltip-row">
|
|
510
|
-
Priority:
|
|
522
|
+
Priority:{" "}
|
|
523
|
+
<span className="fw-dev-tooltip-value">
|
|
524
|
+
{combo.scoreBreakdown.priorityScore.toFixed(2)}
|
|
525
|
+
</span>{" "}
|
|
526
|
+
<span className="fw-dev-tooltip-weight">
|
|
527
|
+
x{combo.scoreBreakdown.weights.priority}
|
|
528
|
+
</span>
|
|
511
529
|
</div>
|
|
512
530
|
<div className="fw-dev-tooltip-row">
|
|
513
|
-
Source:
|
|
531
|
+
Source:{" "}
|
|
532
|
+
<span className="fw-dev-tooltip-value">
|
|
533
|
+
{combo.scoreBreakdown.sourceScore.toFixed(2)}
|
|
534
|
+
</span>{" "}
|
|
535
|
+
<span className="fw-dev-tooltip-weight">
|
|
536
|
+
x{combo.scoreBreakdown.weights.source}
|
|
537
|
+
</span>
|
|
514
538
|
</div>
|
|
515
539
|
{combo.scoreBreakdown.reliabilityScore !== undefined && (
|
|
516
540
|
<div className="fw-dev-tooltip-row">
|
|
517
|
-
Reliability:
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
)}
|
|
525
|
-
{combo.scoreBreakdown.routingBonus !== undefined && combo.scoreBreakdown.routingBonus !== 0 && (
|
|
526
|
-
<div className="fw-dev-tooltip-row">
|
|
527
|
-
Routing: <span className={combo.scoreBreakdown.routingBonus > 0 ? "fw-dev-tooltip-bonus" : "fw-dev-tooltip-penalty"}>{combo.scoreBreakdown.routingBonus > 0 ? '+' : ''}{combo.scoreBreakdown.routingBonus.toFixed(2)}</span> <span className="fw-dev-tooltip-weight">x{combo.scoreBreakdown.weights.routing ?? 0}</span>
|
|
541
|
+
Reliability:{" "}
|
|
542
|
+
<span className="fw-dev-tooltip-value">
|
|
543
|
+
{combo.scoreBreakdown.reliabilityScore.toFixed(2)}
|
|
544
|
+
</span>{" "}
|
|
545
|
+
<span className="fw-dev-tooltip-weight">
|
|
546
|
+
x{combo.scoreBreakdown.weights.reliability ?? 0}
|
|
547
|
+
</span>
|
|
528
548
|
</div>
|
|
529
549
|
)}
|
|
550
|
+
{combo.scoreBreakdown.modeBonus !== undefined &&
|
|
551
|
+
combo.scoreBreakdown.modeBonus !== 0 && (
|
|
552
|
+
<div className="fw-dev-tooltip-row">
|
|
553
|
+
Mode ({playbackMode}):{" "}
|
|
554
|
+
<span className="fw-dev-tooltip-bonus">
|
|
555
|
+
+{combo.scoreBreakdown.modeBonus.toFixed(2)}
|
|
556
|
+
</span>{" "}
|
|
557
|
+
<span className="fw-dev-tooltip-weight">
|
|
558
|
+
x{combo.scoreBreakdown.weights.mode ?? 0}
|
|
559
|
+
</span>
|
|
560
|
+
</div>
|
|
561
|
+
)}
|
|
562
|
+
{combo.scoreBreakdown.routingBonus !== undefined &&
|
|
563
|
+
combo.scoreBreakdown.routingBonus !== 0 && (
|
|
564
|
+
<div className="fw-dev-tooltip-row">
|
|
565
|
+
Routing:{" "}
|
|
566
|
+
<span
|
|
567
|
+
className={
|
|
568
|
+
combo.scoreBreakdown.routingBonus > 0
|
|
569
|
+
? "fw-dev-tooltip-bonus"
|
|
570
|
+
: "fw-dev-tooltip-penalty"
|
|
571
|
+
}
|
|
572
|
+
>
|
|
573
|
+
{combo.scoreBreakdown.routingBonus > 0 ? "+" : ""}
|
|
574
|
+
{combo.scoreBreakdown.routingBonus.toFixed(2)}
|
|
575
|
+
</span>{" "}
|
|
576
|
+
<span className="fw-dev-tooltip-weight">
|
|
577
|
+
x{combo.scoreBreakdown.weights.routing ?? 0}
|
|
578
|
+
</span>
|
|
579
|
+
</div>
|
|
580
|
+
)}
|
|
530
581
|
</>
|
|
531
582
|
) : (
|
|
532
|
-
<div className="fw-dev-tooltip-error">
|
|
583
|
+
<div className="fw-dev-tooltip-error">
|
|
584
|
+
{combo.incompatibleReason || "Incompatible"}
|
|
585
|
+
</div>
|
|
533
586
|
)}
|
|
534
587
|
</div>
|
|
535
588
|
)}
|
|
@@ -549,30 +602,46 @@ const DevModePanel: React.FC<DevModePanelProps> = ({
|
|
|
549
602
|
<div className="fw-dev-section">
|
|
550
603
|
<div className="fw-dev-label">Playback Rate</div>
|
|
551
604
|
<div className="fw-dev-rate">
|
|
552
|
-
<div
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
605
|
+
<div
|
|
606
|
+
className={cn(
|
|
607
|
+
"fw-dev-rate-value",
|
|
608
|
+
playbackScore >= 0.95 && playbackScore <= 1.05
|
|
609
|
+
? "fw-dev-stat-value--good"
|
|
610
|
+
: playbackScore > 1.05
|
|
611
|
+
? "fw-dev-stat-value--accent"
|
|
612
|
+
: playbackScore >= 0.75
|
|
613
|
+
? "fw-dev-stat-value--warn"
|
|
614
|
+
: "fw-dev-stat-value--bad"
|
|
615
|
+
)}
|
|
616
|
+
>
|
|
559
617
|
{playbackScore.toFixed(2)}×
|
|
560
618
|
</div>
|
|
561
619
|
<div className="fw-dev-rate-status">
|
|
562
|
-
{playbackScore >= 0.95 && playbackScore <= 1.05
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
620
|
+
{playbackScore >= 0.95 && playbackScore <= 1.05
|
|
621
|
+
? "realtime"
|
|
622
|
+
: playbackScore > 1.05
|
|
623
|
+
? "catching up"
|
|
624
|
+
: playbackScore >= 0.75
|
|
625
|
+
? "slightly slow"
|
|
626
|
+
: "stalling"}
|
|
566
627
|
</div>
|
|
567
628
|
</div>
|
|
568
629
|
<div className="fw-dev-rate-stats">
|
|
569
|
-
<span
|
|
630
|
+
<span
|
|
631
|
+
className={
|
|
632
|
+
qualityScore >= 75 ? "fw-dev-stat-value--good" : "fw-dev-stat-value--bad"
|
|
633
|
+
}
|
|
634
|
+
>
|
|
570
635
|
Quality: {qualityScore}/100
|
|
571
636
|
</span>
|
|
572
|
-
<span
|
|
637
|
+
<span
|
|
638
|
+
className={stallCount === 0 ? "fw-dev-stat-value--good" : "fw-dev-stat-value--warn"}
|
|
639
|
+
>
|
|
573
640
|
Stalls: {stallCount}
|
|
574
641
|
</span>
|
|
575
|
-
<span
|
|
642
|
+
<span
|
|
643
|
+
className={frameDropRate < 1 ? "fw-dev-stat-value--good" : "fw-dev-stat-value--bad"}
|
|
644
|
+
>
|
|
576
645
|
Drops: {frameDropRate.toFixed(1)}%
|
|
577
646
|
</span>
|
|
578
647
|
</div>
|
|
@@ -616,16 +685,14 @@ const DevModePanel: React.FC<DevModePanelProps> = ({
|
|
|
616
685
|
{nodeId && (
|
|
617
686
|
<div className="fw-dev-stat">
|
|
618
687
|
<span className="fw-dev-stat-label">Node ID</span>
|
|
619
|
-
<span className="fw-dev-stat-value truncate" style={{ maxWidth:
|
|
688
|
+
<span className="fw-dev-stat-value truncate" style={{ maxWidth: "150px" }}>
|
|
620
689
|
{nodeId}
|
|
621
690
|
</span>
|
|
622
691
|
</div>
|
|
623
692
|
)}
|
|
624
693
|
</div>
|
|
625
694
|
) : (
|
|
626
|
-
<div className="fw-dev-list-empty">
|
|
627
|
-
No video element available
|
|
628
|
-
</div>
|
|
695
|
+
<div className="fw-dev-list-empty">No video element available</div>
|
|
629
696
|
)}
|
|
630
697
|
|
|
631
698
|
{/* Player-specific Stats (HLS.js / WebRTC) */}
|
|
@@ -633,20 +700,23 @@ const DevModePanel: React.FC<DevModePanelProps> = ({
|
|
|
633
700
|
<div>
|
|
634
701
|
<div className="fw-dev-list-header fw-dev-section-header">
|
|
635
702
|
<span className="fw-dev-list-title">
|
|
636
|
-
{playerStats.type ===
|
|
637
|
-
|
|
703
|
+
{playerStats.type === "hls"
|
|
704
|
+
? "HLS.js Stats"
|
|
705
|
+
: playerStats.type === "webrtc"
|
|
706
|
+
? "WebRTC Stats"
|
|
707
|
+
: "Player Stats"}
|
|
638
708
|
</span>
|
|
639
709
|
</div>
|
|
640
710
|
|
|
641
711
|
{/* HLS-specific stats */}
|
|
642
|
-
{playerStats.type ===
|
|
712
|
+
{playerStats.type === "hls" && (
|
|
643
713
|
<>
|
|
644
714
|
<div className="fw-dev-stat">
|
|
645
715
|
<span className="fw-dev-stat-label">Bitrate</span>
|
|
646
716
|
<span className="fw-dev-stat-value--accent">
|
|
647
717
|
{playerStats.currentBitrate > 0
|
|
648
718
|
? `${Math.round(playerStats.currentBitrate / 1000)} kbps`
|
|
649
|
-
:
|
|
719
|
+
: "N/A"}
|
|
650
720
|
</span>
|
|
651
721
|
</div>
|
|
652
722
|
<div className="fw-dev-stat">
|
|
@@ -654,19 +724,26 @@ const DevModePanel: React.FC<DevModePanelProps> = ({
|
|
|
654
724
|
<span className="fw-dev-stat-value">
|
|
655
725
|
{playerStats.bandwidthEstimate > 0
|
|
656
726
|
? `${Math.round(playerStats.bandwidthEstimate / 1000)} kbps`
|
|
657
|
-
:
|
|
727
|
+
: "N/A"}
|
|
658
728
|
</span>
|
|
659
729
|
</div>
|
|
660
730
|
<div className="fw-dev-stat">
|
|
661
731
|
<span className="fw-dev-stat-label">Level</span>
|
|
662
732
|
<span className="fw-dev-stat-value">
|
|
663
|
-
{playerStats.currentLevel >= 0 ? playerStats.currentLevel :
|
|
733
|
+
{playerStats.currentLevel >= 0 ? playerStats.currentLevel : "Auto"} /{" "}
|
|
734
|
+
{playerStats.levels?.length || 0}
|
|
664
735
|
</span>
|
|
665
736
|
</div>
|
|
666
737
|
{playerStats.latency !== undefined && (
|
|
667
738
|
<div className="fw-dev-stat">
|
|
668
739
|
<span className="fw-dev-stat-label">Latency</span>
|
|
669
|
-
<span
|
|
740
|
+
<span
|
|
741
|
+
className={
|
|
742
|
+
playerStats.latency > 5000
|
|
743
|
+
? "fw-dev-stat-value--warn"
|
|
744
|
+
: "fw-dev-stat-value"
|
|
745
|
+
}
|
|
746
|
+
>
|
|
670
747
|
{Math.round(playerStats.latency)} ms
|
|
671
748
|
</span>
|
|
672
749
|
</div>
|
|
@@ -675,7 +752,7 @@ const DevModePanel: React.FC<DevModePanelProps> = ({
|
|
|
675
752
|
)}
|
|
676
753
|
|
|
677
754
|
{/* WebRTC-specific stats */}
|
|
678
|
-
{playerStats.type ===
|
|
755
|
+
{playerStats.type === "webrtc" && (
|
|
679
756
|
<>
|
|
680
757
|
{playerStats.video && (
|
|
681
758
|
<>
|
|
@@ -684,7 +761,7 @@ const DevModePanel: React.FC<DevModePanelProps> = ({
|
|
|
684
761
|
<span className="fw-dev-stat-value--accent">
|
|
685
762
|
{playerStats.video.bitrate > 0
|
|
686
763
|
? `${Math.round(playerStats.video.bitrate / 1000)} kbps`
|
|
687
|
-
:
|
|
764
|
+
: "N/A"}
|
|
688
765
|
</span>
|
|
689
766
|
</div>
|
|
690
767
|
<div className="fw-dev-stat">
|
|
@@ -696,21 +773,39 @@ const DevModePanel: React.FC<DevModePanelProps> = ({
|
|
|
696
773
|
<div className="fw-dev-stat">
|
|
697
774
|
<span className="fw-dev-stat-label">Frames</span>
|
|
698
775
|
<span className="fw-dev-stat-value">
|
|
699
|
-
{playerStats.video.framesDecoded} decoded,{
|
|
700
|
-
<span
|
|
776
|
+
{playerStats.video.framesDecoded} decoded,{" "}
|
|
777
|
+
<span
|
|
778
|
+
className={
|
|
779
|
+
playerStats.video.frameDropRate > 1
|
|
780
|
+
? "fw-dev-stat-value--bad"
|
|
781
|
+
: "fw-dev-stat-value--good"
|
|
782
|
+
}
|
|
783
|
+
>
|
|
701
784
|
{playerStats.video.framesDropped} dropped
|
|
702
785
|
</span>
|
|
703
786
|
</span>
|
|
704
787
|
</div>
|
|
705
788
|
<div className="fw-dev-stat">
|
|
706
789
|
<span className="fw-dev-stat-label">Packet Loss</span>
|
|
707
|
-
<span
|
|
790
|
+
<span
|
|
791
|
+
className={
|
|
792
|
+
playerStats.video.packetLossRate > 1
|
|
793
|
+
? "fw-dev-stat-value--bad"
|
|
794
|
+
: "fw-dev-stat-value--good"
|
|
795
|
+
}
|
|
796
|
+
>
|
|
708
797
|
{playerStats.video.packetLossRate.toFixed(2)}%
|
|
709
798
|
</span>
|
|
710
799
|
</div>
|
|
711
800
|
<div className="fw-dev-stat">
|
|
712
801
|
<span className="fw-dev-stat-label">Jitter</span>
|
|
713
|
-
<span
|
|
802
|
+
<span
|
|
803
|
+
className={
|
|
804
|
+
playerStats.video.jitter > 30
|
|
805
|
+
? "fw-dev-stat-value--warn"
|
|
806
|
+
: "fw-dev-stat-value"
|
|
807
|
+
}
|
|
808
|
+
>
|
|
714
809
|
{playerStats.video.jitter.toFixed(1)} ms
|
|
715
810
|
</span>
|
|
716
811
|
</div>
|
|
@@ -725,7 +820,13 @@ const DevModePanel: React.FC<DevModePanelProps> = ({
|
|
|
725
820
|
{playerStats.network && (
|
|
726
821
|
<div className="fw-dev-stat">
|
|
727
822
|
<span className="fw-dev-stat-label">RTT</span>
|
|
728
|
-
<span
|
|
823
|
+
<span
|
|
824
|
+
className={
|
|
825
|
+
playerStats.network.rtt > 200
|
|
826
|
+
? "fw-dev-stat-value--warn"
|
|
827
|
+
: "fw-dev-stat-value"
|
|
828
|
+
}
|
|
829
|
+
>
|
|
729
830
|
{Math.round(playerStats.network.rtt)} ms
|
|
730
831
|
</span>
|
|
731
832
|
</div>
|
|
@@ -746,36 +847,32 @@ const DevModePanel: React.FC<DevModePanelProps> = ({
|
|
|
746
847
|
{Object.entries(mistStreamInfo.meta.tracks).map(([id, track]) => (
|
|
747
848
|
<div key={id} className="fw-dev-track">
|
|
748
849
|
<div className="fw-dev-track-header">
|
|
749
|
-
<span
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
850
|
+
<span
|
|
851
|
+
className={cn(
|
|
852
|
+
"fw-dev-track-badge",
|
|
853
|
+
track.type === "video"
|
|
854
|
+
? "fw-dev-track-badge--video"
|
|
855
|
+
: track.type === "audio"
|
|
856
|
+
? "fw-dev-track-badge--audio"
|
|
857
|
+
: "fw-dev-track-badge--other"
|
|
858
|
+
)}
|
|
859
|
+
>
|
|
755
860
|
{track.type}
|
|
756
861
|
</span>
|
|
757
862
|
<span className="fw-dev-track-codec">{track.codec}</span>
|
|
758
863
|
<span className="fw-dev-track-id">#{id}</span>
|
|
759
864
|
</div>
|
|
760
865
|
<div className="fw-dev-track-meta">
|
|
761
|
-
{track.type ===
|
|
762
|
-
<span>
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
<span>{Math.round(track.bps / 1000)} kbps</span>
|
|
766
|
-
)}
|
|
767
|
-
{track.fpks && (
|
|
768
|
-
<span>{Math.round(track.fpks / 1000)} fps</span>
|
|
769
|
-
)}
|
|
770
|
-
{track.type === 'audio' && track.channels && (
|
|
771
|
-
<span>{track.channels}ch</span>
|
|
772
|
-
)}
|
|
773
|
-
{track.type === 'audio' && track.rate && (
|
|
774
|
-
<span>{track.rate} Hz</span>
|
|
775
|
-
)}
|
|
776
|
-
{track.lang && (
|
|
777
|
-
<span>{track.lang}</span>
|
|
866
|
+
{track.type === "video" && track.width && track.height && (
|
|
867
|
+
<span>
|
|
868
|
+
{track.width}×{track.height}
|
|
869
|
+
</span>
|
|
778
870
|
)}
|
|
871
|
+
{track.bps && <span>{Math.round(track.bps / 1000)} kbps</span>}
|
|
872
|
+
{track.fpks && <span>{Math.round(track.fpks / 1000)} fps</span>}
|
|
873
|
+
{track.type === "audio" && track.channels && <span>{track.channels}ch</span>}
|
|
874
|
+
{track.type === "audio" && track.rate && <span>{track.rate} Hz</span>}
|
|
875
|
+
{track.lang && <span>{track.lang}</span>}
|
|
779
876
|
</div>
|
|
780
877
|
</div>
|
|
781
878
|
))}
|
|
@@ -783,14 +880,18 @@ const DevModePanel: React.FC<DevModePanelProps> = ({
|
|
|
783
880
|
)}
|
|
784
881
|
|
|
785
882
|
{/* Debug: Show if mistStreamInfo is missing tracks */}
|
|
786
|
-
{mistStreamInfo &&
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
883
|
+
{mistStreamInfo &&
|
|
884
|
+
(!mistStreamInfo.meta?.tracks ||
|
|
885
|
+
Object.keys(mistStreamInfo.meta.tracks).length === 0) && (
|
|
886
|
+
<div className="fw-dev-no-tracks">
|
|
887
|
+
<span className="fw-dev-no-tracks-text">
|
|
888
|
+
No track data available
|
|
889
|
+
{mistStreamInfo.type && (
|
|
890
|
+
<span className="fw-dev-no-tracks-type">({mistStreamInfo.type})</span>
|
|
891
|
+
)}
|
|
892
|
+
</span>
|
|
893
|
+
</div>
|
|
894
|
+
)}
|
|
794
895
|
</div>
|
|
795
896
|
)}
|
|
796
897
|
</div>
|