@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.
Files changed (247) hide show
  1. package/dist/cjs/core/ABRController.js +456 -0
  2. package/dist/cjs/core/ABRController.js.map +1 -0
  3. package/dist/cjs/core/CodecUtils.js +195 -0
  4. package/dist/cjs/core/CodecUtils.js.map +1 -0
  5. package/dist/cjs/core/ErrorClassifier.js +410 -0
  6. package/dist/cjs/core/ErrorClassifier.js.map +1 -0
  7. package/dist/cjs/core/EventEmitter.js +108 -0
  8. package/dist/cjs/core/EventEmitter.js.map +1 -0
  9. package/dist/cjs/core/GatewayClient.js +342 -0
  10. package/dist/cjs/core/GatewayClient.js.map +1 -0
  11. package/dist/cjs/core/InteractionController.js +606 -0
  12. package/dist/cjs/core/InteractionController.js.map +1 -0
  13. package/dist/cjs/core/LiveDurationProxy.js +186 -0
  14. package/dist/cjs/core/LiveDurationProxy.js.map +1 -0
  15. package/dist/cjs/core/MetaTrackManager.js +624 -0
  16. package/dist/cjs/core/MetaTrackManager.js.map +1 -0
  17. package/dist/cjs/core/MistReporter.js +449 -0
  18. package/dist/cjs/core/MistReporter.js.map +1 -0
  19. package/dist/cjs/core/MistSignaling.js +264 -0
  20. package/dist/cjs/core/MistSignaling.js.map +1 -0
  21. package/dist/cjs/core/PlayerController.js +2658 -0
  22. package/dist/cjs/core/PlayerController.js.map +1 -0
  23. package/dist/cjs/core/PlayerInterface.js +269 -0
  24. package/dist/cjs/core/PlayerInterface.js.map +1 -0
  25. package/dist/cjs/core/PlayerManager.js +806 -0
  26. package/dist/cjs/core/PlayerManager.js.map +1 -0
  27. package/dist/cjs/core/PlayerRegistry.js +270 -0
  28. package/dist/cjs/core/PlayerRegistry.js.map +1 -0
  29. package/dist/cjs/core/QualityMonitor.js +474 -0
  30. package/dist/cjs/core/QualityMonitor.js.map +1 -0
  31. package/dist/cjs/core/SeekingUtils.js +292 -0
  32. package/dist/cjs/core/SeekingUtils.js.map +1 -0
  33. package/dist/cjs/core/StreamStateClient.js +381 -0
  34. package/dist/cjs/core/StreamStateClient.js.map +1 -0
  35. package/dist/cjs/core/SubtitleManager.js +227 -0
  36. package/dist/cjs/core/SubtitleManager.js.map +1 -0
  37. package/dist/cjs/core/TelemetryReporter.js +258 -0
  38. package/dist/cjs/core/TelemetryReporter.js.map +1 -0
  39. package/dist/cjs/core/TimeFormat.js +176 -0
  40. package/dist/cjs/core/TimeFormat.js.map +1 -0
  41. package/dist/cjs/core/TimerManager.js +176 -0
  42. package/dist/cjs/core/TimerManager.js.map +1 -0
  43. package/dist/cjs/core/UrlUtils.js +160 -0
  44. package/dist/cjs/core/UrlUtils.js.map +1 -0
  45. package/dist/cjs/core/detector.js +293 -0
  46. package/dist/cjs/core/detector.js.map +1 -0
  47. package/dist/cjs/core/scorer.js +443 -0
  48. package/dist/cjs/core/scorer.js.map +1 -0
  49. package/dist/cjs/index.js +121 -20134
  50. package/dist/cjs/index.js.map +1 -1
  51. package/dist/cjs/lib/utils.js +11 -0
  52. package/dist/cjs/lib/utils.js.map +1 -0
  53. package/dist/cjs/node_modules/.pnpm/clsx@2.1.1/node_modules/clsx/dist/clsx.js +6 -0
  54. package/dist/cjs/node_modules/.pnpm/clsx@2.1.1/node_modules/clsx/dist/clsx.js.map +1 -0
  55. package/dist/cjs/node_modules/.pnpm/tailwind-merge@3.4.0/node_modules/tailwind-merge/dist/bundle-mjs.js +3042 -0
  56. package/dist/cjs/node_modules/.pnpm/tailwind-merge@3.4.0/node_modules/tailwind-merge/dist/bundle-mjs.js.map +1 -0
  57. package/dist/cjs/players/DashJsPlayer.js +638 -0
  58. package/dist/cjs/players/DashJsPlayer.js.map +1 -0
  59. package/dist/cjs/players/HlsJsPlayer.js +482 -0
  60. package/dist/cjs/players/HlsJsPlayer.js.map +1 -0
  61. package/dist/cjs/players/MewsWsPlayer/SourceBufferManager.js +522 -0
  62. package/dist/cjs/players/MewsWsPlayer/SourceBufferManager.js.map +1 -0
  63. package/dist/cjs/players/MewsWsPlayer/WebSocketManager.js +215 -0
  64. package/dist/cjs/players/MewsWsPlayer/WebSocketManager.js.map +1 -0
  65. package/dist/cjs/players/MewsWsPlayer/index.js +987 -0
  66. package/dist/cjs/players/MewsWsPlayer/index.js.map +1 -0
  67. package/dist/cjs/players/MistPlayer.js +185 -0
  68. package/dist/cjs/players/MistPlayer.js.map +1 -0
  69. package/dist/cjs/players/MistWebRTCPlayer/index.js +635 -0
  70. package/dist/cjs/players/MistWebRTCPlayer/index.js.map +1 -0
  71. package/dist/cjs/players/NativePlayer.js +762 -0
  72. package/dist/cjs/players/NativePlayer.js.map +1 -0
  73. package/dist/cjs/players/VideoJsPlayer.js +585 -0
  74. package/dist/cjs/players/VideoJsPlayer.js.map +1 -0
  75. package/dist/cjs/players/WebCodecsPlayer/JitterBuffer.js +236 -0
  76. package/dist/cjs/players/WebCodecsPlayer/JitterBuffer.js.map +1 -0
  77. package/dist/cjs/players/WebCodecsPlayer/LatencyProfiles.js +143 -0
  78. package/dist/cjs/players/WebCodecsPlayer/LatencyProfiles.js.map +1 -0
  79. package/dist/cjs/players/WebCodecsPlayer/RawChunkParser.js +96 -0
  80. package/dist/cjs/players/WebCodecsPlayer/RawChunkParser.js.map +1 -0
  81. package/dist/cjs/players/WebCodecsPlayer/SyncController.js +359 -0
  82. package/dist/cjs/players/WebCodecsPlayer/SyncController.js.map +1 -0
  83. package/dist/cjs/players/WebCodecsPlayer/WebSocketController.js +460 -0
  84. package/dist/cjs/players/WebCodecsPlayer/WebSocketController.js.map +1 -0
  85. package/dist/cjs/players/WebCodecsPlayer/index.js +1467 -0
  86. package/dist/cjs/players/WebCodecsPlayer/index.js.map +1 -0
  87. package/dist/cjs/players/WebCodecsPlayer/polyfills/MediaStreamTrackGenerator.js +320 -0
  88. package/dist/cjs/players/WebCodecsPlayer/polyfills/MediaStreamTrackGenerator.js.map +1 -0
  89. package/dist/cjs/styles/index.js +57 -0
  90. package/dist/cjs/styles/index.js.map +1 -0
  91. package/dist/cjs/vanilla/FrameWorksPlayer.js +269 -0
  92. package/dist/cjs/vanilla/FrameWorksPlayer.js.map +1 -0
  93. package/dist/cjs/vanilla.js +11 -0
  94. package/dist/cjs/vanilla.js.map +1 -0
  95. package/dist/esm/core/ABRController.js +454 -0
  96. package/dist/esm/core/ABRController.js.map +1 -0
  97. package/dist/esm/core/CodecUtils.js +193 -0
  98. package/dist/esm/core/CodecUtils.js.map +1 -0
  99. package/dist/esm/core/ErrorClassifier.js +408 -0
  100. package/dist/esm/core/ErrorClassifier.js.map +1 -0
  101. package/dist/esm/core/EventEmitter.js +106 -0
  102. package/dist/esm/core/EventEmitter.js.map +1 -0
  103. package/dist/esm/core/GatewayClient.js +340 -0
  104. package/dist/esm/core/GatewayClient.js.map +1 -0
  105. package/dist/esm/core/InteractionController.js +604 -0
  106. package/dist/esm/core/InteractionController.js.map +1 -0
  107. package/dist/esm/core/LiveDurationProxy.js +184 -0
  108. package/dist/esm/core/LiveDurationProxy.js.map +1 -0
  109. package/dist/esm/core/MetaTrackManager.js +622 -0
  110. package/dist/esm/core/MetaTrackManager.js.map +1 -0
  111. package/dist/esm/core/MistReporter.js +447 -0
  112. package/dist/esm/core/MistReporter.js.map +1 -0
  113. package/dist/esm/core/MistSignaling.js +262 -0
  114. package/dist/esm/core/MistSignaling.js.map +1 -0
  115. package/dist/esm/core/PlayerController.js +2651 -0
  116. package/dist/esm/core/PlayerController.js.map +1 -0
  117. package/dist/esm/core/PlayerInterface.js +267 -0
  118. package/dist/esm/core/PlayerInterface.js.map +1 -0
  119. package/dist/esm/core/PlayerManager.js +804 -0
  120. package/dist/esm/core/PlayerManager.js.map +1 -0
  121. package/dist/esm/core/PlayerRegistry.js +264 -0
  122. package/dist/esm/core/PlayerRegistry.js.map +1 -0
  123. package/dist/esm/core/QualityMonitor.js +471 -0
  124. package/dist/esm/core/QualityMonitor.js.map +1 -0
  125. package/dist/esm/core/SeekingUtils.js +280 -0
  126. package/dist/esm/core/SeekingUtils.js.map +1 -0
  127. package/dist/esm/core/StreamStateClient.js +379 -0
  128. package/dist/esm/core/StreamStateClient.js.map +1 -0
  129. package/dist/esm/core/SubtitleManager.js +225 -0
  130. package/dist/esm/core/SubtitleManager.js.map +1 -0
  131. package/dist/esm/core/TelemetryReporter.js +256 -0
  132. package/dist/esm/core/TelemetryReporter.js.map +1 -0
  133. package/dist/esm/core/TimeFormat.js +169 -0
  134. package/dist/esm/core/TimeFormat.js.map +1 -0
  135. package/dist/esm/core/TimerManager.js +174 -0
  136. package/dist/esm/core/TimerManager.js.map +1 -0
  137. package/dist/esm/core/UrlUtils.js +151 -0
  138. package/dist/esm/core/UrlUtils.js.map +1 -0
  139. package/dist/esm/core/detector.js +279 -0
  140. package/dist/esm/core/detector.js.map +1 -0
  141. package/dist/esm/core/scorer.js +422 -0
  142. package/dist/esm/core/scorer.js.map +1 -0
  143. package/dist/esm/index.js +26 -20043
  144. package/dist/esm/index.js.map +1 -1
  145. package/dist/esm/lib/utils.js +9 -0
  146. package/dist/esm/lib/utils.js.map +1 -0
  147. package/dist/esm/node_modules/.pnpm/clsx@2.1.1/node_modules/clsx/dist/clsx.js +4 -0
  148. package/dist/esm/node_modules/.pnpm/clsx@2.1.1/node_modules/clsx/dist/clsx.js.map +1 -0
  149. package/dist/esm/node_modules/.pnpm/tailwind-merge@3.4.0/node_modules/tailwind-merge/dist/bundle-mjs.js +3036 -0
  150. package/dist/esm/node_modules/.pnpm/tailwind-merge@3.4.0/node_modules/tailwind-merge/dist/bundle-mjs.js.map +1 -0
  151. package/dist/esm/players/DashJsPlayer.js +636 -0
  152. package/dist/esm/players/DashJsPlayer.js.map +1 -0
  153. package/dist/esm/players/HlsJsPlayer.js +480 -0
  154. package/dist/esm/players/HlsJsPlayer.js.map +1 -0
  155. package/dist/esm/players/MewsWsPlayer/SourceBufferManager.js +520 -0
  156. package/dist/esm/players/MewsWsPlayer/SourceBufferManager.js.map +1 -0
  157. package/dist/esm/players/MewsWsPlayer/WebSocketManager.js +213 -0
  158. package/dist/esm/players/MewsWsPlayer/WebSocketManager.js.map +1 -0
  159. package/dist/esm/players/MewsWsPlayer/index.js +985 -0
  160. package/dist/esm/players/MewsWsPlayer/index.js.map +1 -0
  161. package/dist/esm/players/MistPlayer.js +183 -0
  162. package/dist/esm/players/MistPlayer.js.map +1 -0
  163. package/dist/esm/players/MistWebRTCPlayer/index.js +633 -0
  164. package/dist/esm/players/MistWebRTCPlayer/index.js.map +1 -0
  165. package/dist/esm/players/NativePlayer.js +759 -0
  166. package/dist/esm/players/NativePlayer.js.map +1 -0
  167. package/dist/esm/players/VideoJsPlayer.js +583 -0
  168. package/dist/esm/players/VideoJsPlayer.js.map +1 -0
  169. package/dist/esm/players/WebCodecsPlayer/JitterBuffer.js +233 -0
  170. package/dist/esm/players/WebCodecsPlayer/JitterBuffer.js.map +1 -0
  171. package/dist/esm/players/WebCodecsPlayer/LatencyProfiles.js +134 -0
  172. package/dist/esm/players/WebCodecsPlayer/LatencyProfiles.js.map +1 -0
  173. package/dist/esm/players/WebCodecsPlayer/RawChunkParser.js +91 -0
  174. package/dist/esm/players/WebCodecsPlayer/RawChunkParser.js.map +1 -0
  175. package/dist/esm/players/WebCodecsPlayer/SyncController.js +357 -0
  176. package/dist/esm/players/WebCodecsPlayer/SyncController.js.map +1 -0
  177. package/dist/esm/players/WebCodecsPlayer/WebSocketController.js +458 -0
  178. package/dist/esm/players/WebCodecsPlayer/WebSocketController.js.map +1 -0
  179. package/dist/esm/players/WebCodecsPlayer/index.js +1458 -0
  180. package/dist/esm/players/WebCodecsPlayer/index.js.map +1 -0
  181. package/dist/esm/players/WebCodecsPlayer/polyfills/MediaStreamTrackGenerator.js +315 -0
  182. package/dist/esm/players/WebCodecsPlayer/polyfills/MediaStreamTrackGenerator.js.map +1 -0
  183. package/dist/esm/styles/index.js +54 -0
  184. package/dist/esm/styles/index.js.map +1 -0
  185. package/dist/esm/vanilla/FrameWorksPlayer.js +264 -0
  186. package/dist/esm/vanilla/FrameWorksPlayer.js.map +1 -0
  187. package/dist/esm/vanilla.js +2 -0
  188. package/dist/esm/vanilla.js.map +1 -0
  189. package/dist/player.css +4 -1
  190. package/dist/types/core/ABRController.d.ts +4 -4
  191. package/dist/types/core/CodecUtils.d.ts +1 -1
  192. package/dist/types/core/ErrorClassifier.d.ts +77 -0
  193. package/dist/types/core/GatewayClient.d.ts +4 -4
  194. package/dist/types/core/MetaTrackManager.d.ts +2 -2
  195. package/dist/types/core/MistReporter.d.ts +3 -3
  196. package/dist/types/core/MistSignaling.d.ts +12 -12
  197. package/dist/types/core/PlayerController.d.ts +19 -14
  198. package/dist/types/core/PlayerInterface.d.ts +100 -2
  199. package/dist/types/core/PlayerManager.d.ts +36 -9
  200. package/dist/types/core/PlayerRegistry.d.ts +11 -11
  201. package/dist/types/core/QualityMonitor.d.ts +2 -2
  202. package/dist/types/core/SeekingUtils.d.ts +2 -2
  203. package/dist/types/core/StreamStateClient.d.ts +2 -2
  204. package/dist/types/core/TelemetryReporter.d.ts +1 -1
  205. package/dist/types/core/TimerManager.d.ts +1 -1
  206. package/dist/types/core/detector.d.ts +1 -1
  207. package/dist/types/core/index.d.ts +44 -44
  208. package/dist/types/core/scorer.d.ts +1 -1
  209. package/dist/types/core/selector.d.ts +2 -2
  210. package/dist/types/index.d.ts +35 -34
  211. package/dist/types/players/DashJsPlayer.d.ts +3 -3
  212. package/dist/types/players/HlsJsPlayer.d.ts +3 -3
  213. package/dist/types/players/MewsWsPlayer/SourceBufferManager.d.ts +1 -1
  214. package/dist/types/players/MewsWsPlayer/WebSocketManager.d.ts +1 -1
  215. package/dist/types/players/MewsWsPlayer/index.d.ts +2 -2
  216. package/dist/types/players/MewsWsPlayer/types.d.ts +15 -15
  217. package/dist/types/players/MistPlayer.d.ts +2 -2
  218. package/dist/types/players/MistWebRTCPlayer/index.d.ts +3 -3
  219. package/dist/types/players/NativePlayer.d.ts +3 -3
  220. package/dist/types/players/VideoJsPlayer.d.ts +3 -3
  221. package/dist/types/players/WebCodecsPlayer/JitterBuffer.d.ts +3 -3
  222. package/dist/types/players/WebCodecsPlayer/LatencyProfiles.d.ts +1 -1
  223. package/dist/types/players/WebCodecsPlayer/RawChunkParser.d.ts +2 -2
  224. package/dist/types/players/WebCodecsPlayer/SyncController.d.ts +2 -2
  225. package/dist/types/players/WebCodecsPlayer/WebSocketController.d.ts +3 -3
  226. package/dist/types/players/WebCodecsPlayer/index.d.ts +9 -9
  227. package/dist/types/players/WebCodecsPlayer/polyfills/MediaStreamTrackGenerator.d.ts +1 -1
  228. package/dist/types/players/WebCodecsPlayer/types.d.ts +49 -49
  229. package/dist/types/players/WebCodecsPlayer/worker/types.d.ts +31 -31
  230. package/dist/types/players/index.d.ts +5 -8
  231. package/dist/types/types.d.ts +15 -15
  232. package/dist/types/vanilla/FrameWorksPlayer.d.ts +2 -2
  233. package/dist/types/vanilla/index.d.ts +4 -4
  234. package/dist/workers/decoder.worker.js +129 -122
  235. package/dist/workers/decoder.worker.js.map +1 -1
  236. package/package.json +31 -15
  237. package/src/core/ErrorClassifier.ts +499 -0
  238. package/src/core/PlayerController.ts +17 -2
  239. package/src/core/PlayerInterface.ts +109 -0
  240. package/src/core/PlayerManager.ts +290 -46
  241. package/src/core/PlayerRegistry.ts +221 -87
  242. package/src/core/TelemetryReporter.ts +4 -1
  243. package/src/index.ts +13 -4
  244. package/src/players/WebCodecsPlayer/index.ts +2 -2
  245. package/src/players/index.ts +5 -16
  246. package/src/styles/player.css +4 -1
  247. 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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"}