@livepeer-frameworks/player-core 0.1.1 → 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/dist/cjs/core/ABRController.js +456 -0
- package/dist/cjs/core/ABRController.js.map +1 -0
- package/dist/cjs/core/CodecUtils.js +195 -0
- package/dist/cjs/core/CodecUtils.js.map +1 -0
- package/dist/cjs/core/ErrorClassifier.js +410 -0
- package/dist/cjs/core/ErrorClassifier.js.map +1 -0
- package/dist/cjs/core/EventEmitter.js +108 -0
- package/dist/cjs/core/EventEmitter.js.map +1 -0
- package/dist/cjs/core/GatewayClient.js +342 -0
- package/dist/cjs/core/GatewayClient.js.map +1 -0
- package/dist/cjs/core/InteractionController.js +606 -0
- package/dist/cjs/core/InteractionController.js.map +1 -0
- package/dist/cjs/core/LiveDurationProxy.js +186 -0
- package/dist/cjs/core/LiveDurationProxy.js.map +1 -0
- package/dist/cjs/core/MetaTrackManager.js +624 -0
- package/dist/cjs/core/MetaTrackManager.js.map +1 -0
- package/dist/cjs/core/MistReporter.js +449 -0
- package/dist/cjs/core/MistReporter.js.map +1 -0
- package/dist/cjs/core/MistSignaling.js +264 -0
- package/dist/cjs/core/MistSignaling.js.map +1 -0
- package/dist/cjs/core/PlayerController.js +2658 -0
- package/dist/cjs/core/PlayerController.js.map +1 -0
- package/dist/cjs/core/PlayerInterface.js +269 -0
- package/dist/cjs/core/PlayerInterface.js.map +1 -0
- package/dist/cjs/core/PlayerManager.js +806 -0
- package/dist/cjs/core/PlayerManager.js.map +1 -0
- package/dist/cjs/core/PlayerRegistry.js +270 -0
- package/dist/cjs/core/PlayerRegistry.js.map +1 -0
- package/dist/cjs/core/QualityMonitor.js +474 -0
- package/dist/cjs/core/QualityMonitor.js.map +1 -0
- package/dist/cjs/core/SeekingUtils.js +292 -0
- package/dist/cjs/core/SeekingUtils.js.map +1 -0
- package/dist/cjs/core/StreamStateClient.js +381 -0
- package/dist/cjs/core/StreamStateClient.js.map +1 -0
- package/dist/cjs/core/SubtitleManager.js +227 -0
- package/dist/cjs/core/SubtitleManager.js.map +1 -0
- package/dist/cjs/core/TelemetryReporter.js +258 -0
- package/dist/cjs/core/TelemetryReporter.js.map +1 -0
- package/dist/cjs/core/TimeFormat.js +176 -0
- package/dist/cjs/core/TimeFormat.js.map +1 -0
- package/dist/cjs/core/TimerManager.js +176 -0
- package/dist/cjs/core/TimerManager.js.map +1 -0
- package/dist/cjs/core/UrlUtils.js +160 -0
- package/dist/cjs/core/UrlUtils.js.map +1 -0
- package/dist/cjs/core/detector.js +293 -0
- package/dist/cjs/core/detector.js.map +1 -0
- package/dist/cjs/core/scorer.js +443 -0
- package/dist/cjs/core/scorer.js.map +1 -0
- package/dist/cjs/index.js +121 -20134
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/lib/utils.js +11 -0
- package/dist/cjs/lib/utils.js.map +1 -0
- package/dist/cjs/node_modules/.pnpm/clsx@2.1.1/node_modules/clsx/dist/clsx.js +6 -0
- package/dist/cjs/node_modules/.pnpm/clsx@2.1.1/node_modules/clsx/dist/clsx.js.map +1 -0
- package/dist/cjs/node_modules/.pnpm/tailwind-merge@3.4.0/node_modules/tailwind-merge/dist/bundle-mjs.js +3042 -0
- package/dist/cjs/node_modules/.pnpm/tailwind-merge@3.4.0/node_modules/tailwind-merge/dist/bundle-mjs.js.map +1 -0
- package/dist/cjs/players/DashJsPlayer.js +638 -0
- package/dist/cjs/players/DashJsPlayer.js.map +1 -0
- package/dist/cjs/players/HlsJsPlayer.js +482 -0
- package/dist/cjs/players/HlsJsPlayer.js.map +1 -0
- package/dist/cjs/players/MewsWsPlayer/SourceBufferManager.js +522 -0
- package/dist/cjs/players/MewsWsPlayer/SourceBufferManager.js.map +1 -0
- package/dist/cjs/players/MewsWsPlayer/WebSocketManager.js +215 -0
- package/dist/cjs/players/MewsWsPlayer/WebSocketManager.js.map +1 -0
- package/dist/cjs/players/MewsWsPlayer/index.js +987 -0
- package/dist/cjs/players/MewsWsPlayer/index.js.map +1 -0
- package/dist/cjs/players/MistPlayer.js +185 -0
- package/dist/cjs/players/MistPlayer.js.map +1 -0
- package/dist/cjs/players/MistWebRTCPlayer/index.js +635 -0
- package/dist/cjs/players/MistWebRTCPlayer/index.js.map +1 -0
- package/dist/cjs/players/NativePlayer.js +762 -0
- package/dist/cjs/players/NativePlayer.js.map +1 -0
- package/dist/cjs/players/VideoJsPlayer.js +585 -0
- package/dist/cjs/players/VideoJsPlayer.js.map +1 -0
- package/dist/cjs/players/WebCodecsPlayer/JitterBuffer.js +236 -0
- package/dist/cjs/players/WebCodecsPlayer/JitterBuffer.js.map +1 -0
- package/dist/cjs/players/WebCodecsPlayer/LatencyProfiles.js +143 -0
- package/dist/cjs/players/WebCodecsPlayer/LatencyProfiles.js.map +1 -0
- package/dist/cjs/players/WebCodecsPlayer/RawChunkParser.js +96 -0
- package/dist/cjs/players/WebCodecsPlayer/RawChunkParser.js.map +1 -0
- package/dist/cjs/players/WebCodecsPlayer/SyncController.js +359 -0
- package/dist/cjs/players/WebCodecsPlayer/SyncController.js.map +1 -0
- package/dist/cjs/players/WebCodecsPlayer/WebSocketController.js +460 -0
- package/dist/cjs/players/WebCodecsPlayer/WebSocketController.js.map +1 -0
- package/dist/cjs/players/WebCodecsPlayer/index.js +1467 -0
- package/dist/cjs/players/WebCodecsPlayer/index.js.map +1 -0
- package/dist/cjs/players/WebCodecsPlayer/polyfills/MediaStreamTrackGenerator.js +320 -0
- package/dist/cjs/players/WebCodecsPlayer/polyfills/MediaStreamTrackGenerator.js.map +1 -0
- package/dist/cjs/styles/index.js +57 -0
- package/dist/cjs/styles/index.js.map +1 -0
- package/dist/cjs/vanilla/FrameWorksPlayer.js +269 -0
- package/dist/cjs/vanilla/FrameWorksPlayer.js.map +1 -0
- package/dist/cjs/vanilla.js +11 -0
- package/dist/cjs/vanilla.js.map +1 -0
- package/dist/esm/core/ABRController.js +454 -0
- package/dist/esm/core/ABRController.js.map +1 -0
- package/dist/esm/core/CodecUtils.js +193 -0
- package/dist/esm/core/CodecUtils.js.map +1 -0
- package/dist/esm/core/ErrorClassifier.js +408 -0
- package/dist/esm/core/ErrorClassifier.js.map +1 -0
- package/dist/esm/core/EventEmitter.js +106 -0
- package/dist/esm/core/EventEmitter.js.map +1 -0
- package/dist/esm/core/GatewayClient.js +340 -0
- package/dist/esm/core/GatewayClient.js.map +1 -0
- package/dist/esm/core/InteractionController.js +604 -0
- package/dist/esm/core/InteractionController.js.map +1 -0
- package/dist/esm/core/LiveDurationProxy.js +184 -0
- package/dist/esm/core/LiveDurationProxy.js.map +1 -0
- package/dist/esm/core/MetaTrackManager.js +622 -0
- package/dist/esm/core/MetaTrackManager.js.map +1 -0
- package/dist/esm/core/MistReporter.js +447 -0
- package/dist/esm/core/MistReporter.js.map +1 -0
- package/dist/esm/core/MistSignaling.js +262 -0
- package/dist/esm/core/MistSignaling.js.map +1 -0
- package/dist/esm/core/PlayerController.js +2651 -0
- package/dist/esm/core/PlayerController.js.map +1 -0
- package/dist/esm/core/PlayerInterface.js +267 -0
- package/dist/esm/core/PlayerInterface.js.map +1 -0
- package/dist/esm/core/PlayerManager.js +804 -0
- package/dist/esm/core/PlayerManager.js.map +1 -0
- package/dist/esm/core/PlayerRegistry.js +264 -0
- package/dist/esm/core/PlayerRegistry.js.map +1 -0
- package/dist/esm/core/QualityMonitor.js +471 -0
- package/dist/esm/core/QualityMonitor.js.map +1 -0
- package/dist/esm/core/SeekingUtils.js +280 -0
- package/dist/esm/core/SeekingUtils.js.map +1 -0
- package/dist/esm/core/StreamStateClient.js +379 -0
- package/dist/esm/core/StreamStateClient.js.map +1 -0
- package/dist/esm/core/SubtitleManager.js +225 -0
- package/dist/esm/core/SubtitleManager.js.map +1 -0
- package/dist/esm/core/TelemetryReporter.js +256 -0
- package/dist/esm/core/TelemetryReporter.js.map +1 -0
- package/dist/esm/core/TimeFormat.js +169 -0
- package/dist/esm/core/TimeFormat.js.map +1 -0
- package/dist/esm/core/TimerManager.js +174 -0
- package/dist/esm/core/TimerManager.js.map +1 -0
- package/dist/esm/core/UrlUtils.js +151 -0
- package/dist/esm/core/UrlUtils.js.map +1 -0
- package/dist/esm/core/detector.js +279 -0
- package/dist/esm/core/detector.js.map +1 -0
- package/dist/esm/core/scorer.js +422 -0
- package/dist/esm/core/scorer.js.map +1 -0
- package/dist/esm/index.js +26 -20043
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/lib/utils.js +9 -0
- package/dist/esm/lib/utils.js.map +1 -0
- package/dist/esm/node_modules/.pnpm/clsx@2.1.1/node_modules/clsx/dist/clsx.js +4 -0
- package/dist/esm/node_modules/.pnpm/clsx@2.1.1/node_modules/clsx/dist/clsx.js.map +1 -0
- package/dist/esm/node_modules/.pnpm/tailwind-merge@3.4.0/node_modules/tailwind-merge/dist/bundle-mjs.js +3036 -0
- package/dist/esm/node_modules/.pnpm/tailwind-merge@3.4.0/node_modules/tailwind-merge/dist/bundle-mjs.js.map +1 -0
- package/dist/esm/players/DashJsPlayer.js +636 -0
- package/dist/esm/players/DashJsPlayer.js.map +1 -0
- package/dist/esm/players/HlsJsPlayer.js +480 -0
- package/dist/esm/players/HlsJsPlayer.js.map +1 -0
- package/dist/esm/players/MewsWsPlayer/SourceBufferManager.js +520 -0
- package/dist/esm/players/MewsWsPlayer/SourceBufferManager.js.map +1 -0
- package/dist/esm/players/MewsWsPlayer/WebSocketManager.js +213 -0
- package/dist/esm/players/MewsWsPlayer/WebSocketManager.js.map +1 -0
- package/dist/esm/players/MewsWsPlayer/index.js +985 -0
- package/dist/esm/players/MewsWsPlayer/index.js.map +1 -0
- package/dist/esm/players/MistPlayer.js +183 -0
- package/dist/esm/players/MistPlayer.js.map +1 -0
- package/dist/esm/players/MistWebRTCPlayer/index.js +633 -0
- package/dist/esm/players/MistWebRTCPlayer/index.js.map +1 -0
- package/dist/esm/players/NativePlayer.js +759 -0
- package/dist/esm/players/NativePlayer.js.map +1 -0
- package/dist/esm/players/VideoJsPlayer.js +583 -0
- package/dist/esm/players/VideoJsPlayer.js.map +1 -0
- package/dist/esm/players/WebCodecsPlayer/JitterBuffer.js +233 -0
- package/dist/esm/players/WebCodecsPlayer/JitterBuffer.js.map +1 -0
- package/dist/esm/players/WebCodecsPlayer/LatencyProfiles.js +134 -0
- package/dist/esm/players/WebCodecsPlayer/LatencyProfiles.js.map +1 -0
- package/dist/esm/players/WebCodecsPlayer/RawChunkParser.js +91 -0
- package/dist/esm/players/WebCodecsPlayer/RawChunkParser.js.map +1 -0
- package/dist/esm/players/WebCodecsPlayer/SyncController.js +357 -0
- package/dist/esm/players/WebCodecsPlayer/SyncController.js.map +1 -0
- package/dist/esm/players/WebCodecsPlayer/WebSocketController.js +458 -0
- package/dist/esm/players/WebCodecsPlayer/WebSocketController.js.map +1 -0
- package/dist/esm/players/WebCodecsPlayer/index.js +1458 -0
- package/dist/esm/players/WebCodecsPlayer/index.js.map +1 -0
- package/dist/esm/players/WebCodecsPlayer/polyfills/MediaStreamTrackGenerator.js +315 -0
- package/dist/esm/players/WebCodecsPlayer/polyfills/MediaStreamTrackGenerator.js.map +1 -0
- package/dist/esm/styles/index.js +54 -0
- package/dist/esm/styles/index.js.map +1 -0
- package/dist/esm/vanilla/FrameWorksPlayer.js +264 -0
- package/dist/esm/vanilla/FrameWorksPlayer.js.map +1 -0
- package/dist/esm/vanilla.js +2 -0
- package/dist/esm/vanilla.js.map +1 -0
- package/dist/player.css +4 -1
- package/dist/types/core/ABRController.d.ts +4 -4
- package/dist/types/core/CodecUtils.d.ts +1 -1
- package/dist/types/core/ErrorClassifier.d.ts +77 -0
- package/dist/types/core/GatewayClient.d.ts +4 -4
- package/dist/types/core/MetaTrackManager.d.ts +2 -2
- package/dist/types/core/MistReporter.d.ts +3 -3
- package/dist/types/core/MistSignaling.d.ts +12 -12
- package/dist/types/core/PlayerController.d.ts +19 -14
- package/dist/types/core/PlayerInterface.d.ts +100 -2
- package/dist/types/core/PlayerManager.d.ts +36 -9
- package/dist/types/core/PlayerRegistry.d.ts +11 -11
- package/dist/types/core/QualityMonitor.d.ts +2 -2
- package/dist/types/core/SeekingUtils.d.ts +2 -2
- package/dist/types/core/StreamStateClient.d.ts +2 -2
- package/dist/types/core/TelemetryReporter.d.ts +1 -1
- package/dist/types/core/TimerManager.d.ts +1 -1
- package/dist/types/core/detector.d.ts +1 -1
- package/dist/types/core/index.d.ts +44 -44
- package/dist/types/core/scorer.d.ts +1 -1
- package/dist/types/core/selector.d.ts +2 -2
- package/dist/types/index.d.ts +35 -34
- package/dist/types/players/DashJsPlayer.d.ts +3 -3
- package/dist/types/players/HlsJsPlayer.d.ts +3 -3
- package/dist/types/players/MewsWsPlayer/SourceBufferManager.d.ts +1 -1
- package/dist/types/players/MewsWsPlayer/WebSocketManager.d.ts +1 -1
- package/dist/types/players/MewsWsPlayer/index.d.ts +2 -2
- package/dist/types/players/MewsWsPlayer/types.d.ts +15 -15
- package/dist/types/players/MistPlayer.d.ts +2 -2
- package/dist/types/players/MistWebRTCPlayer/index.d.ts +3 -3
- package/dist/types/players/NativePlayer.d.ts +3 -3
- package/dist/types/players/VideoJsPlayer.d.ts +3 -3
- package/dist/types/players/WebCodecsPlayer/JitterBuffer.d.ts +3 -3
- package/dist/types/players/WebCodecsPlayer/LatencyProfiles.d.ts +1 -1
- package/dist/types/players/WebCodecsPlayer/RawChunkParser.d.ts +2 -2
- package/dist/types/players/WebCodecsPlayer/SyncController.d.ts +2 -2
- package/dist/types/players/WebCodecsPlayer/WebSocketController.d.ts +3 -3
- package/dist/types/players/WebCodecsPlayer/index.d.ts +9 -9
- package/dist/types/players/WebCodecsPlayer/polyfills/MediaStreamTrackGenerator.d.ts +1 -1
- package/dist/types/players/WebCodecsPlayer/types.d.ts +49 -49
- package/dist/types/players/WebCodecsPlayer/worker/types.d.ts +31 -31
- package/dist/types/players/index.d.ts +5 -8
- package/dist/types/types.d.ts +15 -15
- package/dist/types/vanilla/FrameWorksPlayer.d.ts +2 -2
- package/dist/types/vanilla/index.d.ts +4 -4
- package/dist/workers/decoder.worker.js +129 -122
- package/dist/workers/decoder.worker.js.map +1 -1
- package/package.json +31 -15
- package/src/core/ErrorClassifier.ts +499 -0
- package/src/core/PlayerController.ts +17 -2
- package/src/core/PlayerInterface.ts +109 -0
- package/src/core/PlayerManager.ts +290 -46
- package/src/core/PlayerRegistry.ts +221 -87
- package/src/core/TelemetryReporter.ts +4 -1
- package/src/index.ts +13 -4
- package/src/players/WebCodecsPlayer/index.ts +2 -2
- package/src/players/index.ts +5 -16
- package/src/styles/player.css +4 -1
- package/src/vanilla/FrameWorksPlayer.ts +2 -5
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* SeekingUtils.ts
|
|
5
|
+
*
|
|
6
|
+
* Centralized seeking and live detection logic for player controls.
|
|
7
|
+
* Used by React, Svelte, and Vanilla wrappers to ensure consistent behavior.
|
|
8
|
+
*
|
|
9
|
+
* Key concepts:
|
|
10
|
+
* - Seekable range: The portion of the stream that can be seeked to
|
|
11
|
+
* - Live edge: The furthest point in time that can be played (live point)
|
|
12
|
+
* - Near live: Whether playback is close enough to live edge to show "LIVE" badge
|
|
13
|
+
* - Latency tier: Protocol-based classification affecting live detection thresholds
|
|
14
|
+
*/
|
|
15
|
+
// ============================================================================
|
|
16
|
+
// Constants
|
|
17
|
+
// ============================================================================
|
|
18
|
+
/**
|
|
19
|
+
* Latency tier thresholds for "near live" detection.
|
|
20
|
+
* Different protocols have vastly different latency expectations.
|
|
21
|
+
*
|
|
22
|
+
* exitLive: How far behind (seconds) before we show "behind live" indicator
|
|
23
|
+
* enterLive: How close to live (seconds) before we show "LIVE" badge again
|
|
24
|
+
*
|
|
25
|
+
* The gap between exitLive and enterLive creates hysteresis to prevent flicker.
|
|
26
|
+
*/
|
|
27
|
+
const LATENCY_TIERS = {
|
|
28
|
+
// WebRTC/WHEP: sub-second latency
|
|
29
|
+
"ultra-low": { exitLive: 2, enterLive: 0.5 },
|
|
30
|
+
// MEWS (WebSocket MP4): 2-5s latency
|
|
31
|
+
low: { exitLive: 5, enterLive: 1.5 },
|
|
32
|
+
// HLS/DASH: 10-30s latency (segment-based)
|
|
33
|
+
medium: { exitLive: 15, enterLive: 5 },
|
|
34
|
+
// Fallback for unknown protocols
|
|
35
|
+
high: { exitLive: 30, enterLive: 10 },
|
|
36
|
+
};
|
|
37
|
+
/**
|
|
38
|
+
* Playback speed presets for UI controls.
|
|
39
|
+
*/
|
|
40
|
+
const SPEED_PRESETS = [0.5, 1, 1.5, 2];
|
|
41
|
+
/**
|
|
42
|
+
* Default fallback buffer window when no other info available (in seconds).
|
|
43
|
+
* Aligned with MistServer reference player's 60-second default.
|
|
44
|
+
*/
|
|
45
|
+
const DEFAULT_BUFFER_WINDOW_SEC = 60;
|
|
46
|
+
// ============================================================================
|
|
47
|
+
// Pure Functions
|
|
48
|
+
// ============================================================================
|
|
49
|
+
/**
|
|
50
|
+
* Detect latency tier from source type string.
|
|
51
|
+
*
|
|
52
|
+
* @param sourceType - MIME type or protocol identifier (e.g., 'whep', 'ws/video/mp4')
|
|
53
|
+
* @returns Latency tier classification
|
|
54
|
+
*/
|
|
55
|
+
function getLatencyTier(sourceType) {
|
|
56
|
+
if (!sourceType)
|
|
57
|
+
return "medium";
|
|
58
|
+
const t = sourceType.toLowerCase();
|
|
59
|
+
// Ultra-low: WebRTC protocols (sub-second latency)
|
|
60
|
+
if (t === "whep" || t === "webrtc" || t.includes("mist/webrtc")) {
|
|
61
|
+
return "ultra-low";
|
|
62
|
+
}
|
|
63
|
+
// Low: WebSocket-based streaming (2-5s latency)
|
|
64
|
+
if (t.startsWith("ws/") || t.startsWith("wss/")) {
|
|
65
|
+
return "low";
|
|
66
|
+
}
|
|
67
|
+
// Medium: HLS/DASH (segment-based, 10-30s latency)
|
|
68
|
+
if (t.includes("mpegurl") || t.includes("dash")) {
|
|
69
|
+
return "medium";
|
|
70
|
+
}
|
|
71
|
+
// Progressive MP4/WebM - use medium defaults
|
|
72
|
+
if (t.includes("video/mp4") || t.includes("video/webm")) {
|
|
73
|
+
return "medium";
|
|
74
|
+
}
|
|
75
|
+
return "medium";
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Check if video element is using WebRTC/MediaStream source.
|
|
79
|
+
* WebRTC streams have special constraints (no seeking, no playback rate changes).
|
|
80
|
+
*
|
|
81
|
+
* @param video - HTML video element
|
|
82
|
+
* @returns true if source is a MediaStream
|
|
83
|
+
*/
|
|
84
|
+
function isMediaStreamSource(video) {
|
|
85
|
+
if (!video)
|
|
86
|
+
return false;
|
|
87
|
+
return video.srcObject instanceof MediaStream;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Check if playback rate adjustment is supported.
|
|
91
|
+
* WebRTC/MediaStream sources don't support playback rate changes.
|
|
92
|
+
*
|
|
93
|
+
* @param video - HTML video element
|
|
94
|
+
* @returns true if playback rate can be changed
|
|
95
|
+
*/
|
|
96
|
+
function supportsPlaybackRate(video) {
|
|
97
|
+
if (!video)
|
|
98
|
+
return true;
|
|
99
|
+
return !isMediaStreamSource(video);
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Calculate seekable range for live or VOD streams.
|
|
103
|
+
*
|
|
104
|
+
* Priority order:
|
|
105
|
+
* 1. Browser's video.seekable ranges (most accurate for MSE-based players)
|
|
106
|
+
* 2. Track firstms/lastms from MistServer metadata
|
|
107
|
+
* 3. buffer_window from MistServer signaling
|
|
108
|
+
* 4. No fallback (treat as live-only when no reliable data)
|
|
109
|
+
*
|
|
110
|
+
* @param params - Calculation parameters
|
|
111
|
+
* @returns Seekable range with start and live edge
|
|
112
|
+
*/
|
|
113
|
+
function calculateSeekableRange(params) {
|
|
114
|
+
const { isLive, video, mistStreamInfo, currentTime, duration, allowMediaStreamDvr = false, } = params;
|
|
115
|
+
// VOD: full duration is seekable
|
|
116
|
+
if (!isLive) {
|
|
117
|
+
return { seekableStart: 0, liveEdge: duration };
|
|
118
|
+
}
|
|
119
|
+
const isMediaStream = isMediaStreamSource(video);
|
|
120
|
+
// PRIORITY 1: Browser's video.seekable (most reliable - reflects actual browser state)
|
|
121
|
+
if (video?.seekable && video.seekable.length > 0) {
|
|
122
|
+
const start = video.seekable.start(0);
|
|
123
|
+
const end = video.seekable.end(video.seekable.length - 1);
|
|
124
|
+
if (Number.isFinite(start) && Number.isFinite(end) && end > start) {
|
|
125
|
+
return { seekableStart: start, liveEdge: end };
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
// PRIORITY 2: Track firstms/lastms from MistServer (accurate when available)
|
|
129
|
+
// Skip for MediaStream unless explicitly allowed (e.g., WebCodecs DVR via server)
|
|
130
|
+
if ((allowMediaStreamDvr || !isMediaStream) && mistStreamInfo?.meta?.tracks) {
|
|
131
|
+
const tracks = Object.values(mistStreamInfo.meta.tracks);
|
|
132
|
+
if (tracks.length > 0) {
|
|
133
|
+
const firstmsValues = tracks
|
|
134
|
+
.map((t) => t.firstms)
|
|
135
|
+
.filter((v) => v !== undefined);
|
|
136
|
+
const lastmsValues = tracks.map((t) => t.lastms).filter((v) => v !== undefined);
|
|
137
|
+
if (firstmsValues.length > 0 && lastmsValues.length > 0) {
|
|
138
|
+
const firstms = Math.max(...firstmsValues);
|
|
139
|
+
const lastms = Math.min(...lastmsValues);
|
|
140
|
+
return { seekableStart: firstms / 1000, liveEdge: lastms / 1000 };
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
// PRIORITY 3: buffer_window from MistServer signaling
|
|
145
|
+
const bufferWindowMs = mistStreamInfo?.meta?.buffer_window;
|
|
146
|
+
if (bufferWindowMs && bufferWindowMs > 0 && currentTime > 0) {
|
|
147
|
+
const bufferWindowSec = bufferWindowMs / 1000;
|
|
148
|
+
return {
|
|
149
|
+
seekableStart: Math.max(0, currentTime - bufferWindowSec),
|
|
150
|
+
liveEdge: currentTime,
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
// No seekable range (live only)
|
|
154
|
+
return { seekableStart: currentTime, liveEdge: currentTime };
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Determine if seeking is supported for the current stream.
|
|
158
|
+
*
|
|
159
|
+
* @param params - Check parameters
|
|
160
|
+
* @returns true if seeking is available
|
|
161
|
+
*/
|
|
162
|
+
function canSeekStream(params) {
|
|
163
|
+
const { video, isLive, duration, bufferWindowMs, playerCanSeek } = params;
|
|
164
|
+
// Player API says no
|
|
165
|
+
if (playerCanSeek && !playerCanSeek()) {
|
|
166
|
+
return false;
|
|
167
|
+
}
|
|
168
|
+
// Player API says yes - trust it for VOD, but require buffer for live
|
|
169
|
+
if (playerCanSeek && playerCanSeek()) {
|
|
170
|
+
if (!isLive)
|
|
171
|
+
return true;
|
|
172
|
+
return bufferWindowMs !== undefined && bufferWindowMs > 0;
|
|
173
|
+
}
|
|
174
|
+
// No video element
|
|
175
|
+
if (!video) {
|
|
176
|
+
return false;
|
|
177
|
+
}
|
|
178
|
+
// WebRTC/MediaStream: only if buffer_window explicitly configured
|
|
179
|
+
if (isMediaStreamSource(video)) {
|
|
180
|
+
return bufferWindowMs !== undefined && bufferWindowMs > 0;
|
|
181
|
+
}
|
|
182
|
+
// Browser reports seekable ranges
|
|
183
|
+
if (video.seekable && video.seekable.length > 0) {
|
|
184
|
+
return true;
|
|
185
|
+
}
|
|
186
|
+
// VOD with valid duration
|
|
187
|
+
if (!isLive && Number.isFinite(duration) && duration > 0) {
|
|
188
|
+
return true;
|
|
189
|
+
}
|
|
190
|
+
// Live with buffer_window configured
|
|
191
|
+
if (isLive && bufferWindowMs !== undefined && bufferWindowMs > 0) {
|
|
192
|
+
return true;
|
|
193
|
+
}
|
|
194
|
+
return false;
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* Calculate live detection thresholds, optionally scaled by buffer_window.
|
|
198
|
+
*
|
|
199
|
+
* For medium/high latency tiers, scales thresholds based on the actual
|
|
200
|
+
* buffer window to provide more appropriate "near live" detection.
|
|
201
|
+
*
|
|
202
|
+
* @param sourceType - Protocol/MIME type for tier detection
|
|
203
|
+
* @param isWebRTC - Whether source is WebRTC (overrides tier to ultra-low)
|
|
204
|
+
* @param bufferWindowMs - Optional buffer window in milliseconds
|
|
205
|
+
* @returns Thresholds for entering/exiting "LIVE" state
|
|
206
|
+
*/
|
|
207
|
+
function calculateLiveThresholds(sourceType, isWebRTC, bufferWindowMs) {
|
|
208
|
+
// Determine tier from source type, or use ultra-low for WebRTC
|
|
209
|
+
const tier = sourceType ? getLatencyTier(sourceType) : isWebRTC ? "ultra-low" : "medium";
|
|
210
|
+
const tierThresholds = LATENCY_TIERS[tier];
|
|
211
|
+
// For medium/high tiers, scale thresholds based on buffer_window
|
|
212
|
+
if ((tier === "medium" || tier === "high") && bufferWindowMs && bufferWindowMs > 0) {
|
|
213
|
+
const bufferWindowSec = bufferWindowMs / 1000;
|
|
214
|
+
// Scale thresholds proportionally to buffer, with reasonable bounds
|
|
215
|
+
return {
|
|
216
|
+
exitLive: Math.max(tierThresholds.exitLive, Math.min(30, bufferWindowSec / 3)),
|
|
217
|
+
enterLive: Math.max(tierThresholds.enterLive, Math.min(10, bufferWindowSec / 10)),
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
return tierThresholds;
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* Calculate whether playback is "near live" using hysteresis.
|
|
224
|
+
*
|
|
225
|
+
* Hysteresis prevents flip-flopping when hovering near the threshold:
|
|
226
|
+
* - To EXIT "LIVE" state: must be > exitLive + margin behind
|
|
227
|
+
* - To ENTER "LIVE" state: must be < enterLive - margin behind
|
|
228
|
+
*
|
|
229
|
+
* @param currentTime - Current playback position in seconds
|
|
230
|
+
* @param liveEdge - Live edge position in seconds
|
|
231
|
+
* @param thresholds - Enter/exit thresholds
|
|
232
|
+
* @param currentState - Current isNearLive state
|
|
233
|
+
* @returns New isNearLive state
|
|
234
|
+
*/
|
|
235
|
+
function calculateIsNearLive(currentTime, liveEdge, thresholds, currentState) {
|
|
236
|
+
// Invalid state - assume live
|
|
237
|
+
if (!Number.isFinite(liveEdge) || liveEdge <= 0) {
|
|
238
|
+
return true;
|
|
239
|
+
}
|
|
240
|
+
const behindSeconds = liveEdge - currentTime;
|
|
241
|
+
// Hysteresis margins for extra stability
|
|
242
|
+
const exitMargin = 0.5;
|
|
243
|
+
const enterMargin = 0.2;
|
|
244
|
+
if (currentState && behindSeconds > thresholds.exitLive + exitMargin) {
|
|
245
|
+
// Currently "LIVE" - switch to "behind" when significantly behind
|
|
246
|
+
return false;
|
|
247
|
+
}
|
|
248
|
+
else if (!currentState && behindSeconds < thresholds.enterLive - enterMargin) {
|
|
249
|
+
// Currently "behind" - switch to "LIVE" when close to live edge
|
|
250
|
+
return true;
|
|
251
|
+
}
|
|
252
|
+
// No change
|
|
253
|
+
return currentState;
|
|
254
|
+
}
|
|
255
|
+
/**
|
|
256
|
+
* Determine if content is live based on available metadata.
|
|
257
|
+
*
|
|
258
|
+
* Priority:
|
|
259
|
+
* 1. Explicit isContentLive flag (highest priority)
|
|
260
|
+
* 2. MistServer stream type
|
|
261
|
+
* 3. Duration check (non-finite = live)
|
|
262
|
+
*
|
|
263
|
+
* @param isContentLive - Explicit live flag from content metadata
|
|
264
|
+
* @param mistStreamInfo - MistServer stream info
|
|
265
|
+
* @param duration - Video duration
|
|
266
|
+
* @returns true if content is live
|
|
267
|
+
*/
|
|
268
|
+
function isLiveContent(isContentLive, mistStreamInfo, duration) {
|
|
269
|
+
// Explicit flag wins
|
|
270
|
+
if (isContentLive !== undefined) {
|
|
271
|
+
return isContentLive;
|
|
272
|
+
}
|
|
273
|
+
// MistServer type
|
|
274
|
+
if (mistStreamInfo?.type) {
|
|
275
|
+
return mistStreamInfo.type === "live";
|
|
276
|
+
}
|
|
277
|
+
// Fallback: non-finite duration indicates live
|
|
278
|
+
return !Number.isFinite(duration);
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
exports.DEFAULT_BUFFER_WINDOW_SEC = DEFAULT_BUFFER_WINDOW_SEC;
|
|
282
|
+
exports.LATENCY_TIERS = LATENCY_TIERS;
|
|
283
|
+
exports.SPEED_PRESETS = SPEED_PRESETS;
|
|
284
|
+
exports.calculateIsNearLive = calculateIsNearLive;
|
|
285
|
+
exports.calculateLiveThresholds = calculateLiveThresholds;
|
|
286
|
+
exports.calculateSeekableRange = calculateSeekableRange;
|
|
287
|
+
exports.canSeekStream = canSeekStream;
|
|
288
|
+
exports.getLatencyTier = getLatencyTier;
|
|
289
|
+
exports.isLiveContent = isLiveContent;
|
|
290
|
+
exports.isMediaStreamSource = isMediaStreamSource;
|
|
291
|
+
exports.supportsPlaybackRate = supportsPlaybackRate;
|
|
292
|
+
//# sourceMappingURL=SeekingUtils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SeekingUtils.js","sources":["../../../../src/core/SeekingUtils.ts"],"sourcesContent":["/**\n * SeekingUtils.ts\n *\n * Centralized seeking and live detection logic for player controls.\n * Used by React, Svelte, and Vanilla wrappers to ensure consistent behavior.\n *\n * Key concepts:\n * - Seekable range: The portion of the stream that can be seeked to\n * - Live edge: The furthest point in time that can be played (live point)\n * - Near live: Whether playback is close enough to live edge to show \"LIVE\" badge\n * - Latency tier: Protocol-based classification affecting live detection thresholds\n */\n\nimport type { MistStreamInfo, MistTrackInfo } from \"../types\";\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport type LatencyTier = \"ultra-low\" | \"low\" | \"medium\" | \"high\";\n\nexport interface LiveThresholds {\n /** Seconds behind live edge to exit \"LIVE\" state (become clickable) */\n exitLive: number;\n /** Seconds behind live edge to enter \"LIVE\" state (become non-clickable) */\n enterLive: number;\n}\n\nexport interface SeekableRange {\n /** Start of seekable range in seconds */\n seekableStart: number;\n /** End of seekable range (live edge) in seconds */\n liveEdge: number;\n}\n\nexport interface SeekableRangeParams {\n isLive: boolean;\n video: HTMLVideoElement | null;\n mistStreamInfo?: MistStreamInfo;\n currentTime: number;\n duration: number;\n /** Allow Mist track metadata for MediaStream sources (e.g., WebCodecs DVR) */\n allowMediaStreamDvr?: boolean;\n}\n\nexport interface CanSeekParams {\n video: HTMLVideoElement | null;\n isLive: boolean;\n duration: number;\n bufferWindowMs?: number;\n playerCanSeek?: () => boolean;\n}\n\n// ============================================================================\n// Constants\n// ============================================================================\n\n/**\n * Latency tier thresholds for \"near live\" detection.\n * Different protocols have vastly different latency expectations.\n *\n * exitLive: How far behind (seconds) before we show \"behind live\" indicator\n * enterLive: How close to live (seconds) before we show \"LIVE\" badge again\n *\n * The gap between exitLive and enterLive creates hysteresis to prevent flicker.\n */\nexport const LATENCY_TIERS: Record<LatencyTier, LiveThresholds> = {\n // WebRTC/WHEP: sub-second latency\n \"ultra-low\": { exitLive: 2, enterLive: 0.5 },\n // MEWS (WebSocket MP4): 2-5s latency\n low: { exitLive: 5, enterLive: 1.5 },\n // HLS/DASH: 10-30s latency (segment-based)\n medium: { exitLive: 15, enterLive: 5 },\n // Fallback for unknown protocols\n high: { exitLive: 30, enterLive: 10 },\n};\n\n/**\n * Playback speed presets for UI controls.\n */\nexport const SPEED_PRESETS = [0.5, 1, 1.5, 2] as const;\n\n/**\n * Default fallback buffer window when no other info available (in seconds).\n * Aligned with MistServer reference player's 60-second default.\n */\nexport const DEFAULT_BUFFER_WINDOW_SEC = 60;\n\n// ============================================================================\n// Pure Functions\n// ============================================================================\n\n/**\n * Detect latency tier from source type string.\n *\n * @param sourceType - MIME type or protocol identifier (e.g., 'whep', 'ws/video/mp4')\n * @returns Latency tier classification\n */\nexport function getLatencyTier(sourceType?: string): LatencyTier {\n if (!sourceType) return \"medium\";\n const t = sourceType.toLowerCase();\n\n // Ultra-low: WebRTC protocols (sub-second latency)\n if (t === \"whep\" || t === \"webrtc\" || t.includes(\"mist/webrtc\")) {\n return \"ultra-low\";\n }\n\n // Low: WebSocket-based streaming (2-5s latency)\n if (t.startsWith(\"ws/\") || t.startsWith(\"wss/\")) {\n return \"low\";\n }\n\n // Medium: HLS/DASH (segment-based, 10-30s latency)\n if (t.includes(\"mpegurl\") || t.includes(\"dash\")) {\n return \"medium\";\n }\n\n // Progressive MP4/WebM - use medium defaults\n if (t.includes(\"video/mp4\") || t.includes(\"video/webm\")) {\n return \"medium\";\n }\n\n return \"medium\";\n}\n\n/**\n * Check if video element is using WebRTC/MediaStream source.\n * WebRTC streams have special constraints (no seeking, no playback rate changes).\n *\n * @param video - HTML video element\n * @returns true if source is a MediaStream\n */\nexport function isMediaStreamSource(video: HTMLVideoElement | null): boolean {\n if (!video) return false;\n return video.srcObject instanceof MediaStream;\n}\n\n/**\n * Check if playback rate adjustment is supported.\n * WebRTC/MediaStream sources don't support playback rate changes.\n *\n * @param video - HTML video element\n * @returns true if playback rate can be changed\n */\nexport function supportsPlaybackRate(video: HTMLVideoElement | null): boolean {\n if (!video) return true;\n return !isMediaStreamSource(video);\n}\n\n/**\n * Calculate seekable range for live or VOD streams.\n *\n * Priority order:\n * 1. Browser's video.seekable ranges (most accurate for MSE-based players)\n * 2. Track firstms/lastms from MistServer metadata\n * 3. buffer_window from MistServer signaling\n * 4. No fallback (treat as live-only when no reliable data)\n *\n * @param params - Calculation parameters\n * @returns Seekable range with start and live edge\n */\nexport function calculateSeekableRange(params: SeekableRangeParams): SeekableRange {\n const {\n isLive,\n video,\n mistStreamInfo,\n currentTime,\n duration,\n allowMediaStreamDvr = false,\n } = params;\n\n // VOD: full duration is seekable\n if (!isLive) {\n return { seekableStart: 0, liveEdge: duration };\n }\n\n const isMediaStream = isMediaStreamSource(video);\n\n // PRIORITY 1: Browser's video.seekable (most reliable - reflects actual browser state)\n if (video?.seekable && video.seekable.length > 0) {\n const start = video.seekable.start(0);\n const end = video.seekable.end(video.seekable.length - 1);\n if (Number.isFinite(start) && Number.isFinite(end) && end > start) {\n return { seekableStart: start, liveEdge: end };\n }\n }\n\n // PRIORITY 2: Track firstms/lastms from MistServer (accurate when available)\n // Skip for MediaStream unless explicitly allowed (e.g., WebCodecs DVR via server)\n if ((allowMediaStreamDvr || !isMediaStream) && mistStreamInfo?.meta?.tracks) {\n const tracks = Object.values(mistStreamInfo.meta.tracks) as MistTrackInfo[];\n if (tracks.length > 0) {\n const firstmsValues = tracks\n .map((t) => t.firstms)\n .filter((v): v is number => v !== undefined);\n const lastmsValues = tracks.map((t) => t.lastms).filter((v): v is number => v !== undefined);\n\n if (firstmsValues.length > 0 && lastmsValues.length > 0) {\n const firstms = Math.max(...firstmsValues);\n const lastms = Math.min(...lastmsValues);\n return { seekableStart: firstms / 1000, liveEdge: lastms / 1000 };\n }\n }\n }\n\n // PRIORITY 3: buffer_window from MistServer signaling\n const bufferWindowMs = mistStreamInfo?.meta?.buffer_window;\n if (bufferWindowMs && bufferWindowMs > 0 && currentTime > 0) {\n const bufferWindowSec = bufferWindowMs / 1000;\n return {\n seekableStart: Math.max(0, currentTime - bufferWindowSec),\n liveEdge: currentTime,\n };\n }\n\n // No seekable range (live only)\n return { seekableStart: currentTime, liveEdge: currentTime };\n}\n\n/**\n * Determine if seeking is supported for the current stream.\n *\n * @param params - Check parameters\n * @returns true if seeking is available\n */\nexport function canSeekStream(params: CanSeekParams): boolean {\n const { video, isLive, duration, bufferWindowMs, playerCanSeek } = params;\n\n // Player API says no\n if (playerCanSeek && !playerCanSeek()) {\n return false;\n }\n\n // Player API says yes - trust it for VOD, but require buffer for live\n if (playerCanSeek && playerCanSeek()) {\n if (!isLive) return true;\n return bufferWindowMs !== undefined && bufferWindowMs > 0;\n }\n\n // No video element\n if (!video) {\n return false;\n }\n\n // WebRTC/MediaStream: only if buffer_window explicitly configured\n if (isMediaStreamSource(video)) {\n return bufferWindowMs !== undefined && bufferWindowMs > 0;\n }\n\n // Browser reports seekable ranges\n if (video.seekable && video.seekable.length > 0) {\n return true;\n }\n\n // VOD with valid duration\n if (!isLive && Number.isFinite(duration) && duration > 0) {\n return true;\n }\n\n // Live with buffer_window configured\n if (isLive && bufferWindowMs !== undefined && bufferWindowMs > 0) {\n return true;\n }\n\n return false;\n}\n\n/**\n * Calculate live detection thresholds, optionally scaled by buffer_window.\n *\n * For medium/high latency tiers, scales thresholds based on the actual\n * buffer window to provide more appropriate \"near live\" detection.\n *\n * @param sourceType - Protocol/MIME type for tier detection\n * @param isWebRTC - Whether source is WebRTC (overrides tier to ultra-low)\n * @param bufferWindowMs - Optional buffer window in milliseconds\n * @returns Thresholds for entering/exiting \"LIVE\" state\n */\nexport function calculateLiveThresholds(\n sourceType?: string,\n isWebRTC?: boolean,\n bufferWindowMs?: number\n): LiveThresholds {\n // Determine tier from source type, or use ultra-low for WebRTC\n const tier = sourceType ? getLatencyTier(sourceType) : isWebRTC ? \"ultra-low\" : \"medium\";\n const tierThresholds = LATENCY_TIERS[tier];\n\n // For medium/high tiers, scale thresholds based on buffer_window\n if ((tier === \"medium\" || tier === \"high\") && bufferWindowMs && bufferWindowMs > 0) {\n const bufferWindowSec = bufferWindowMs / 1000;\n // Scale thresholds proportionally to buffer, with reasonable bounds\n return {\n exitLive: Math.max(tierThresholds.exitLive, Math.min(30, bufferWindowSec / 3)),\n enterLive: Math.max(tierThresholds.enterLive, Math.min(10, bufferWindowSec / 10)),\n };\n }\n\n return tierThresholds;\n}\n\n/**\n * Calculate whether playback is \"near live\" using hysteresis.\n *\n * Hysteresis prevents flip-flopping when hovering near the threshold:\n * - To EXIT \"LIVE\" state: must be > exitLive + margin behind\n * - To ENTER \"LIVE\" state: must be < enterLive - margin behind\n *\n * @param currentTime - Current playback position in seconds\n * @param liveEdge - Live edge position in seconds\n * @param thresholds - Enter/exit thresholds\n * @param currentState - Current isNearLive state\n * @returns New isNearLive state\n */\nexport function calculateIsNearLive(\n currentTime: number,\n liveEdge: number,\n thresholds: LiveThresholds,\n currentState: boolean\n): boolean {\n // Invalid state - assume live\n if (!Number.isFinite(liveEdge) || liveEdge <= 0) {\n return true;\n }\n\n const behindSeconds = liveEdge - currentTime;\n\n // Hysteresis margins for extra stability\n const exitMargin = 0.5;\n const enterMargin = 0.2;\n\n if (currentState && behindSeconds > thresholds.exitLive + exitMargin) {\n // Currently \"LIVE\" - switch to \"behind\" when significantly behind\n return false;\n } else if (!currentState && behindSeconds < thresholds.enterLive - enterMargin) {\n // Currently \"behind\" - switch to \"LIVE\" when close to live edge\n return true;\n }\n\n // No change\n return currentState;\n}\n\n/**\n * Determine if content is live based on available metadata.\n *\n * Priority:\n * 1. Explicit isContentLive flag (highest priority)\n * 2. MistServer stream type\n * 3. Duration check (non-finite = live)\n *\n * @param isContentLive - Explicit live flag from content metadata\n * @param mistStreamInfo - MistServer stream info\n * @param duration - Video duration\n * @returns true if content is live\n */\nexport function isLiveContent(\n isContentLive?: boolean,\n mistStreamInfo?: MistStreamInfo,\n duration?: number\n): boolean {\n // Explicit flag wins\n if (isContentLive !== undefined) {\n return isContentLive;\n }\n\n // MistServer type\n if (mistStreamInfo?.type) {\n return mistStreamInfo.type === \"live\";\n }\n\n // Fallback: non-finite duration indicates live\n return !Number.isFinite(duration);\n}\n"],"names":[],"mappings":"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"}
|