@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,583 @@
1
+ import { BasePlayer } from '../core/PlayerInterface.js';
2
+ import { LiveDurationProxy } from '../core/LiveDurationProxy.js';
3
+
4
+ class VideoJsPlayerImpl extends BasePlayer {
5
+ constructor() {
6
+ super(...arguments);
7
+ this.capability = {
8
+ name: "Video.js Player",
9
+ shortname: "videojs",
10
+ priority: 2,
11
+ // VideoJS only has built-in HLS support via VHS (videojs-http-streaming)
12
+ // DASH requires videojs-contrib-dash plugin which wraps dash.js - we use DashJsPlayer directly instead
13
+ mimes: ["html5/application/vnd.apple.mpegurl", "html5/application/vnd.apple.mpegurl;version=7"],
14
+ };
15
+ this.videojsPlayer = null;
16
+ this.container = null;
17
+ this.destroyed = false;
18
+ this.timeCorrection = 0;
19
+ this.proxyElement = null;
20
+ this.currentStreamInfo = null;
21
+ this.liveDurationProxy = null;
22
+ }
23
+ isMimeSupported(mimetype) {
24
+ return this.capability.mimes.includes(mimetype);
25
+ }
26
+ isBrowserSupported(mimetype, source, streamInfo) {
27
+ // Check for HTTP/HTTPS protocol mismatch
28
+ try {
29
+ const sourceProtocol = new URL(source.url).protocol;
30
+ if (typeof location !== "undefined" && location.protocol !== sourceProtocol) {
31
+ console.debug("[VideoJS] HTTP/HTTPS mismatch - skipping");
32
+ return false;
33
+ }
34
+ }
35
+ catch {
36
+ // URL parsing failed, continue with other checks
37
+ }
38
+ // Test codec support properly - don't just assume compatibility
39
+ const playableTracks = [];
40
+ const tracksByType = {};
41
+ // Group tracks by type
42
+ for (const track of streamInfo.meta.tracks) {
43
+ if (track.type === "meta") {
44
+ if (track.codec === "subtitle") {
45
+ playableTracks.push("subtitle");
46
+ }
47
+ continue;
48
+ }
49
+ if (!tracksByType[track.type]) {
50
+ tracksByType[track.type] = [];
51
+ }
52
+ tracksByType[track.type].push(track);
53
+ }
54
+ // HLS-incompatible audio codecs (VideoJS uses VHS for HLS)
55
+ // HLS standard only supports: AAC, MP3, AC-3/E-AC-3
56
+ const HLS_INCOMPATIBLE_AUDIO = ["OPUS", "Opus", "opus", "VORBIS", "Vorbis", "FLAC"];
57
+ // Test codec support for video/audio tracks using canPlayType
58
+ const testVideo = document.createElement("video");
59
+ for (const [trackType, tracks] of Object.entries(tracksByType)) {
60
+ let hasPlayableTrack = false;
61
+ for (const track of tracks) {
62
+ // Explicit HLS codec filtering - OPUS doesn't work in HLS even if canPlayType says yes
63
+ if (trackType === "audio" && HLS_INCOMPATIBLE_AUDIO.includes(track.codec)) {
64
+ console.debug(`[VideoJS] Codec incompatible with HLS: ${track.codec}`);
65
+ continue;
66
+ }
67
+ // Build codec string
68
+ let codecString = track.codec;
69
+ if (track.init) {
70
+ // Use init data for accurate codec string like HLS.js does
71
+ const bin2hex = (idx) => {
72
+ if (!track.init || idx >= track.init.length)
73
+ return "00";
74
+ return ("0" + track.init.charCodeAt(idx).toString(16)).slice(-2);
75
+ };
76
+ switch (track.codec) {
77
+ case "H264":
78
+ codecString = `avc1.${bin2hex(1)}${bin2hex(2)}${bin2hex(3)}`;
79
+ break;
80
+ case "AAC":
81
+ codecString = "mp4a.40.2";
82
+ break;
83
+ case "MP3":
84
+ codecString = "mp4a.40.34";
85
+ break;
86
+ case "HEVC":
87
+ codecString = "hev1.1.6.L93.B0";
88
+ break;
89
+ }
90
+ }
91
+ // Test with video element canPlayType
92
+ const mimeToTest = trackType === "audio"
93
+ ? `audio/mp4;codecs="${codecString}"`
94
+ : `video/mp4;codecs="${codecString}"`;
95
+ if (testVideo.canPlayType(mimeToTest) !== "") {
96
+ hasPlayableTrack = true;
97
+ break;
98
+ }
99
+ else {
100
+ console.debug(`[VideoJS] Codec not supported: ${mimeToTest}`);
101
+ }
102
+ }
103
+ if (hasPlayableTrack) {
104
+ playableTracks.push(trackType);
105
+ }
106
+ }
107
+ // If no tracks to test, assume basic support (fallback behavior)
108
+ if (Object.keys(tracksByType).length === 0) {
109
+ return ["video", "audio"];
110
+ }
111
+ return playableTracks.length > 0 ? playableTracks : false;
112
+ }
113
+ async initialize(container, source, options, streamInfo) {
114
+ this.destroyed = false;
115
+ this.container = container;
116
+ this.currentStreamInfo = streamInfo || null;
117
+ container.classList.add("fw-player-container");
118
+ const video = document.createElement("video");
119
+ video.classList.add("fw-player-video");
120
+ video.setAttribute("playsinline", "");
121
+ video.setAttribute("crossorigin", "anonymous");
122
+ video.className = "video-js vjs-default-skin fw-player-video";
123
+ if (options.autoplay)
124
+ video.autoplay = true;
125
+ if (options.muted)
126
+ video.muted = true;
127
+ video.controls = options.controls === true; // Explicit false to hide native controls
128
+ if (options.loop)
129
+ video.loop = true;
130
+ if (options.poster)
131
+ video.poster = options.poster;
132
+ this.videoElement = video;
133
+ container.appendChild(video);
134
+ this.setupVideoEventListeners(video, options);
135
+ try {
136
+ const mod = await import('video.js');
137
+ const videojs = mod.default || mod;
138
+ // When using custom controls (controls: false), disable ALL VideoJS UI elements
139
+ const useVideoJsControls = options.controls === true;
140
+ // Android < 7 workaround: enable overrideNative for HLS
141
+ const androidMatch = navigator.userAgent.match(/android\s([\d.]*)/i);
142
+ const androidVersion = androidMatch ? parseFloat(androidMatch[1]) : null;
143
+ // Build VideoJS options
144
+ // NOTE: We disable UI components but NOT children array - that breaks playback
145
+ const vjsOptions = {
146
+ autoplay: options.autoplay,
147
+ controls: useVideoJsControls,
148
+ muted: options.muted,
149
+ sources: [{ src: source.url, type: this.getVideoJsType(source.type) }],
150
+ // Disable VideoJS UI components when using custom controls
151
+ loadingSpinner: useVideoJsControls,
152
+ bigPlayButton: useVideoJsControls,
153
+ textTrackDisplay: useVideoJsControls, // We handle subtitles ourselves
154
+ errorDisplay: useVideoJsControls,
155
+ controlBar: useVideoJsControls,
156
+ liveTracker: useVideoJsControls,
157
+ // Don't set children: [] - that can break internal VideoJS components
158
+ // VHS (http-streaming) configuration - AGGRESSIVE for fastest startup
159
+ html5: {
160
+ vhs: {
161
+ // AGGRESSIVE: Start with lower quality for instant playback
162
+ enableLowInitialPlaylist: true,
163
+ // AGGRESSIVE: Assume 5 Mbps initially
164
+ bandwidth: 5000000,
165
+ // Persist bandwidth across sessions for returning users
166
+ useBandwidthFromLocalStorage: true,
167
+ // Enable partial segment processing for lower latency
168
+ handlePartialData: true,
169
+ // AGGRESSIVE: Very tight live range
170
+ liveRangeSafeTimeDelta: 0.3,
171
+ // Allow user overrides via options.vhsConfig
172
+ ...options.vhsConfig,
173
+ },
174
+ // Android < 7 workaround
175
+ ...(androidVersion && androidVersion < 7
176
+ ? {
177
+ hls: { overrideNative: true },
178
+ }
179
+ : {}),
180
+ },
181
+ nativeAudioTracks: androidVersion && androidVersion < 7 ? false : undefined,
182
+ nativeVideoTracks: androidVersion && androidVersion < 7 ? false : undefined,
183
+ };
184
+ console.debug("[VideoJS] Creating player with options:", vjsOptions);
185
+ this.videojsPlayer = videojs(video, vjsOptions);
186
+ console.debug("[VideoJS] Player created");
187
+ // Hide VideoJS UI completely when using custom controls
188
+ if (!useVideoJsControls) {
189
+ // Add class to hide all VideoJS chrome
190
+ const wrapper = this.videojsPlayer.el();
191
+ if (wrapper) {
192
+ wrapper.classList.add("vjs-fw-custom-controls");
193
+ }
194
+ }
195
+ // Error handling with Firefox NS_ERROR detection
196
+ this.videojsPlayer.on("error", () => {
197
+ if (this.destroyed)
198
+ return; // Guard against zombie callbacks
199
+ const err = this.videojsPlayer?.error();
200
+ const errorMsg = err?.message || "";
201
+ // Firefox-specific segment error - trigger reload
202
+ if (errorMsg.includes("NS_ERROR_DOM_MEDIA_OVERFLOW_ERR")) {
203
+ console.warn("[VideoJS] Firefox segment error, requesting reload");
204
+ this.emit("reloadrequested", { reason: "NS_ERROR_DOM_MEDIA_OVERFLOW_ERR" });
205
+ return;
206
+ }
207
+ this.emit("error", errorMsg || "VideoJS playback error");
208
+ });
209
+ // FIX: Explicitly trigger play after VideoJS is ready
210
+ // VideoJS autoplay option alone doesn't always work (browser policies)
211
+ this.videojsPlayer.ready(() => {
212
+ if (this.destroyed)
213
+ return; // Guard against zombie callbacks
214
+ // Debug: Log VideoJS tech info
215
+ const tech = this.videojsPlayer.tech?.({ IWillNotUseThisInPlugins: true });
216
+ console.debug("[VideoJS] ready - tech:", tech?.name || "unknown", "videoWidth:", video.videoWidth, "videoHeight:", video.videoHeight, "readyState:", video.readyState, "networkState:", video.networkState);
217
+ // Create time-corrected proxy for external consumers
218
+ if (this.currentStreamInfo) {
219
+ this.proxyElement = this.createTimeCorrectedProxy(video, this.currentStreamInfo);
220
+ }
221
+ // Check if live stream and set up LiveDurationProxy
222
+ const duration = this.videojsPlayer.duration();
223
+ if (!isFinite(duration) && !this.liveDurationProxy) {
224
+ this.liveDurationProxy = new LiveDurationProxy(video, {
225
+ constrainSeek: true,
226
+ liveOffset: 0,
227
+ });
228
+ console.debug("[VideoJS] LiveDurationProxy initialized for live stream");
229
+ }
230
+ if (options.autoplay) {
231
+ // Ensure muted for autoplay - browsers block unmuted autoplay
232
+ if (!video.muted) {
233
+ video.muted = true;
234
+ }
235
+ this.videojsPlayer.play().catch((e) => {
236
+ console.warn("VideoJS autoplay failed:", e);
237
+ // Emit a warning but don't fail - user can click play
238
+ });
239
+ }
240
+ });
241
+ // Listen for VideoJS loadedmetadata to track loading progress
242
+ this.videojsPlayer.on("loadedmetadata", () => {
243
+ console.debug("[VideoJS] loadedmetadata - duration:", this.videojsPlayer.duration(), "videoWidth:", video.videoWidth, "videoHeight:", video.videoHeight);
244
+ });
245
+ // Debug: Track VHS (video.js http-streaming) state
246
+ this.videojsPlayer.on("loadeddata", () => {
247
+ const tech = this.videojsPlayer.tech?.({ IWillNotUseThisInPlugins: true });
248
+ const vhs = tech?.vhs || tech?.hls;
249
+ if (vhs) {
250
+ console.debug("[VideoJS] VHS state -", "bandwidth:", vhs.bandwidth, "seekable:", vhs.seekable?.()?.length > 0
251
+ ? `${vhs.seekable().start(0)}-${vhs.seekable().end(0)}`
252
+ : "none", "buffered:", video.buffered.length > 0 ? `${video.buffered.end(0)}s` : "none");
253
+ }
254
+ });
255
+ // Listen for canplay from VideoJS to ensure we transition out of buffering
256
+ this.videojsPlayer.on("canplay", () => {
257
+ console.debug("[VideoJS] canplay");
258
+ });
259
+ // Additional debug events
260
+ this.videojsPlayer.on("playing", () => {
261
+ console.debug("[VideoJS] playing - currentTime:", this.videojsPlayer.currentTime());
262
+ });
263
+ this.videojsPlayer.on("waiting", () => {
264
+ console.debug("[VideoJS] waiting/buffering");
265
+ });
266
+ this.videojsPlayer.on("stalled", () => {
267
+ console.debug("[VideoJS] stalled");
268
+ });
269
+ // Log video element state
270
+ video.addEventListener("loadeddata", () => {
271
+ console.debug("[VideoJS] video loadeddata - readyState:", video.readyState, "videoWidth:", video.videoWidth, "videoHeight:", video.videoHeight);
272
+ });
273
+ // Emit seekable range updates for live streams (DVR support)
274
+ this.videojsPlayer.on("progress", () => {
275
+ if (this.destroyed)
276
+ return;
277
+ try {
278
+ const seekable = this.videojsPlayer.seekable();
279
+ if (seekable && seekable.length > 0) {
280
+ const start = seekable.start(0);
281
+ const end = seekable.end(seekable.length - 1);
282
+ const bufferWindow = (end - start) * 1000; // Convert to ms
283
+ this.emit("seekablechange", {
284
+ start: start + this.timeCorrection,
285
+ end: end + this.timeCorrection,
286
+ bufferWindow,
287
+ });
288
+ }
289
+ }
290
+ catch {
291
+ // Seekable not available yet
292
+ }
293
+ });
294
+ return this.proxyElement || video;
295
+ }
296
+ catch (error) {
297
+ this.emit("error", error.message || String(error));
298
+ throw error;
299
+ }
300
+ }
301
+ /**
302
+ * Creates a Proxy wrapper around the video element that corrects
303
+ * currentTime/duration/buffered using the firstms offset from MistServer.
304
+ * This ensures timestamps align with MistServer's track metadata.
305
+ */
306
+ createTimeCorrectedProxy(video, streamInfo) {
307
+ // Calculate correction from minimum firstms across all tracks
308
+ let firstms = Infinity;
309
+ for (const track of streamInfo.meta.tracks) {
310
+ if (track.firstms !== undefined && track.firstms < firstms) {
311
+ firstms = track.firstms;
312
+ }
313
+ }
314
+ this.timeCorrection = firstms !== Infinity ? firstms / 1000 : 0;
315
+ // No correction needed or Proxy not supported
316
+ if (this.timeCorrection === 0 || typeof Proxy === "undefined") {
317
+ return video;
318
+ }
319
+ console.debug(`[VideoJS] Applying timestamp correction: ${this.timeCorrection}s (firstms=${firstms})`);
320
+ const correction = this.timeCorrection;
321
+ const vjsPlayer = this.videojsPlayer;
322
+ return new Proxy(video, {
323
+ get: (target, prop) => {
324
+ if (prop === "currentTime") {
325
+ const time = vjsPlayer ? vjsPlayer.currentTime() : target.currentTime;
326
+ return isNaN(time) ? 0 : time + correction;
327
+ }
328
+ if (prop === "duration") {
329
+ const duration = target.duration;
330
+ return isNaN(duration) ? 0 : duration + correction;
331
+ }
332
+ if (prop === "buffered") {
333
+ const buffered = target.buffered;
334
+ return {
335
+ length: buffered.length,
336
+ start: (i) => buffered.start(i) + correction,
337
+ end: (i) => buffered.end(i) + correction,
338
+ };
339
+ }
340
+ const value = target[prop];
341
+ if (typeof value === "function") {
342
+ return value.bind(target);
343
+ }
344
+ return value;
345
+ },
346
+ set: (target, prop, value) => {
347
+ if (prop === "currentTime") {
348
+ // Use VideoJS API for seeking (fixes backwards seeking in HLS)
349
+ const correctedValue = value - correction;
350
+ if (vjsPlayer) {
351
+ vjsPlayer.currentTime(correctedValue);
352
+ }
353
+ else {
354
+ target.currentTime = correctedValue;
355
+ }
356
+ return true;
357
+ }
358
+ target[prop] = value;
359
+ return true;
360
+ },
361
+ });
362
+ }
363
+ getVideoJsType(mimeType) {
364
+ if (!mimeType)
365
+ return "application/x-mpegURL";
366
+ // Convert our mime types to VideoJS types
367
+ if (mimeType.includes("mpegurl"))
368
+ return "application/x-mpegURL";
369
+ if (mimeType.includes("dash"))
370
+ return "application/dash+xml";
371
+ if (mimeType.includes("mp4"))
372
+ return "video/mp4";
373
+ if (mimeType.includes("webm"))
374
+ return "video/webm";
375
+ return mimeType.replace("html5/", "");
376
+ }
377
+ setPlaybackRate(rate) {
378
+ super.setPlaybackRate(rate);
379
+ try {
380
+ if (this.videojsPlayer)
381
+ this.videojsPlayer.playbackRate(rate);
382
+ }
383
+ catch { }
384
+ }
385
+ getCurrentTime() {
386
+ const v = this.proxyElement || this.videoElement;
387
+ return v?.currentTime ?? 0;
388
+ }
389
+ /**
390
+ * Seek to time using VideoJS API (fixes backwards seeking in HLS).
391
+ * Time should be in the corrected coordinate space (with firstms offset applied).
392
+ */
393
+ seek(time) {
394
+ const correctedTime = time - this.timeCorrection;
395
+ if (this.videojsPlayer) {
396
+ this.videojsPlayer.currentTime(correctedTime);
397
+ }
398
+ else if (this.videoElement) {
399
+ this.videoElement.currentTime = correctedTime;
400
+ }
401
+ }
402
+ /**
403
+ * Get VideoJS-specific stats for playback monitoring
404
+ */
405
+ async getStats() {
406
+ const video = this.videoElement;
407
+ if (!video)
408
+ return undefined;
409
+ // Calculate buffered ahead of current position
410
+ let buffered = 0;
411
+ if (video.buffered.length > 0) {
412
+ for (let i = 0; i < video.buffered.length; i++) {
413
+ if (video.buffered.start(i) <= video.currentTime &&
414
+ video.buffered.end(i) > video.currentTime) {
415
+ buffered = video.buffered.end(i) - video.currentTime;
416
+ break;
417
+ }
418
+ }
419
+ }
420
+ return {
421
+ type: "videojs",
422
+ buffered,
423
+ currentTime: video.currentTime,
424
+ duration: video.duration,
425
+ readyState: video.readyState,
426
+ networkState: video.networkState,
427
+ playbackRate: video.playbackRate,
428
+ };
429
+ }
430
+ // ============================================================================
431
+ // Live Stream Support
432
+ // ============================================================================
433
+ /**
434
+ * Check if the stream is live
435
+ */
436
+ isLiveStream() {
437
+ if (this.liveDurationProxy) {
438
+ return this.liveDurationProxy.isLive();
439
+ }
440
+ const video = this.videoElement;
441
+ if (!video)
442
+ return false;
443
+ return !isFinite(video.duration);
444
+ }
445
+ /**
446
+ * Get the calculated duration for live streams
447
+ */
448
+ getDuration() {
449
+ if (this.liveDurationProxy) {
450
+ return this.liveDurationProxy.getDuration();
451
+ }
452
+ return this.videoElement?.duration ?? 0;
453
+ }
454
+ /**
455
+ * Jump to live edge
456
+ * Uses VideoJS liveTracker when available, otherwise LiveDurationProxy
457
+ */
458
+ jumpToLive() {
459
+ const video = this.videoElement;
460
+ if (!video)
461
+ return;
462
+ // VideoJS has a liveTracker module for live streams
463
+ if (this.videojsPlayer && this.videojsPlayer.liveTracker) {
464
+ const tracker = this.videojsPlayer.liveTracker;
465
+ if (tracker.isLive && tracker.isLive()) {
466
+ const liveCurrentTime = tracker.liveCurrentTime?.();
467
+ if (typeof liveCurrentTime === "number" && liveCurrentTime > 0) {
468
+ console.debug("[VideoJS] jumpToLive using liveTracker:", liveCurrentTime);
469
+ this.videojsPlayer.currentTime(liveCurrentTime);
470
+ return;
471
+ }
472
+ }
473
+ }
474
+ // Fall back to LiveDurationProxy
475
+ if (this.liveDurationProxy && this.liveDurationProxy.isLive()) {
476
+ console.debug("[VideoJS] jumpToLive using LiveDurationProxy");
477
+ this.liveDurationProxy.jumpToLive();
478
+ return;
479
+ }
480
+ // VideoJS seekable fallback
481
+ if (this.videojsPlayer) {
482
+ try {
483
+ const seekable = this.videojsPlayer.seekable();
484
+ if (seekable && seekable.length > 0) {
485
+ const liveEdge = seekable.end(seekable.length - 1);
486
+ if (isFinite(liveEdge) && liveEdge > 0) {
487
+ console.debug("[VideoJS] jumpToLive using seekable.end:", liveEdge);
488
+ this.videojsPlayer.currentTime(liveEdge);
489
+ return;
490
+ }
491
+ }
492
+ }
493
+ catch { }
494
+ }
495
+ // Native video seekable fallback
496
+ if (video.seekable && video.seekable.length > 0) {
497
+ const liveEdge = video.seekable.end(video.seekable.length - 1);
498
+ if (isFinite(liveEdge) && liveEdge > 0) {
499
+ console.debug("[VideoJS] jumpToLive using video.seekable.end:", liveEdge);
500
+ video.currentTime = liveEdge;
501
+ }
502
+ }
503
+ }
504
+ /**
505
+ * Provide a seekable range override for live streams.
506
+ * Uses VideoJS liveTracker seekableEnd as the live edge when available.
507
+ */
508
+ getSeekableRange() {
509
+ const video = this.videoElement;
510
+ if (!video?.seekable || video.seekable.length === 0)
511
+ return null;
512
+ let start = video.seekable.start(0);
513
+ let end = video.seekable.end(video.seekable.length - 1);
514
+ if (this.videojsPlayer?.liveTracker) {
515
+ const tracker = this.videojsPlayer.liveTracker;
516
+ const trackerEnd = tracker.seekableEnd?.();
517
+ const trackerStart = tracker.seekableStart?.();
518
+ if (typeof trackerStart === "number" && Number.isFinite(trackerStart)) {
519
+ start = trackerStart;
520
+ }
521
+ if (typeof trackerEnd === "number" && Number.isFinite(trackerEnd) && trackerEnd > 0) {
522
+ end = Math.min(end, trackerEnd);
523
+ }
524
+ }
525
+ if (!Number.isFinite(start) || !Number.isFinite(end) || end <= start)
526
+ return null;
527
+ return { start, end };
528
+ }
529
+ /**
530
+ * Get latency from live edge (for live streams)
531
+ */
532
+ getLiveLatency() {
533
+ const video = this.videoElement;
534
+ if (!video)
535
+ return 0;
536
+ // VideoJS liveTracker provides seekableEnd
537
+ if (this.videojsPlayer && this.videojsPlayer.liveTracker) {
538
+ const tracker = this.videojsPlayer.liveTracker;
539
+ if (tracker.isLive?.() && typeof tracker.seekableEnd === "function") {
540
+ const liveEdge = tracker.seekableEnd();
541
+ if (typeof liveEdge === "number" && isFinite(liveEdge)) {
542
+ return Math.max(0, (liveEdge - video.currentTime) * 1000);
543
+ }
544
+ }
545
+ }
546
+ // Fall back to proxy
547
+ if (this.liveDurationProxy) {
548
+ return this.liveDurationProxy.getLatency() * 1000;
549
+ }
550
+ return 0;
551
+ }
552
+ async destroy() {
553
+ this.destroyed = true;
554
+ if (this.liveDurationProxy) {
555
+ this.liveDurationProxy.destroy();
556
+ this.liveDurationProxy = null;
557
+ }
558
+ if (this.videojsPlayer) {
559
+ try {
560
+ this.videojsPlayer.dispose();
561
+ }
562
+ catch (e) {
563
+ console.warn("Error disposing VideoJS:", e);
564
+ }
565
+ this.videojsPlayer = null;
566
+ }
567
+ if (this.videoElement && this.container) {
568
+ try {
569
+ this.container.removeChild(this.videoElement);
570
+ }
571
+ catch { }
572
+ }
573
+ this.videoElement = null;
574
+ this.container = null;
575
+ this.proxyElement = null;
576
+ this.currentStreamInfo = null;
577
+ this.timeCorrection = 0;
578
+ this.listeners.clear();
579
+ }
580
+ }
581
+
582
+ export { VideoJsPlayerImpl };
583
+ //# sourceMappingURL=VideoJsPlayer.js.map