@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,174 @@
1
+ /**
2
+ * TimerManager - Centralized timer management for memory leak prevention
3
+ *
4
+ * Tracks all setTimeout/setInterval calls and provides bulk cleanup.
5
+ * Based on MistMetaPlayer's MistVideo.timers pattern.
6
+ *
7
+ * Usage:
8
+ * ```ts
9
+ * const timers = new TimerManager();
10
+ *
11
+ * // Start a timeout
12
+ * const id = timers.start(() => console.log('fired'), 1000);
13
+ *
14
+ * // Start an interval
15
+ * const intervalId = timers.startInterval(() => console.log('tick'), 500);
16
+ *
17
+ * // Stop a specific timer
18
+ * timers.stop(id);
19
+ *
20
+ * // Stop all timers (on cleanup/destroy)
21
+ * timers.stopAll();
22
+ * ```
23
+ */
24
+ class TimerManager {
25
+ constructor(options) {
26
+ this.timers = new Map();
27
+ this.nextId = 1;
28
+ this.debug = options?.debug ?? false;
29
+ }
30
+ /**
31
+ * Start a timeout
32
+ * @param callback Function to call after delay
33
+ * @param delay Delay in milliseconds
34
+ * @param label Optional label for debugging
35
+ * @returns Timer ID (internal, not the native timeout ID)
36
+ */
37
+ start(callback, delay, label) {
38
+ const internalId = this.nextId++;
39
+ const endTime = Date.now() + delay;
40
+ const nativeId = setTimeout(() => {
41
+ this.timers.delete(internalId);
42
+ try {
43
+ callback();
44
+ }
45
+ catch (e) {
46
+ console.error("[TimerManager] Callback error:", e);
47
+ }
48
+ }, delay);
49
+ this.timers.set(internalId, {
50
+ id: nativeId,
51
+ endTime,
52
+ isInterval: false,
53
+ label,
54
+ });
55
+ if (this.debug) {
56
+ console.debug(`[TimerManager] Started timeout ${internalId}${label ? ` (${label})` : ""} for ${delay}ms`);
57
+ }
58
+ return internalId;
59
+ }
60
+ /**
61
+ * Start an interval
62
+ * @param callback Function to call repeatedly
63
+ * @param interval Interval in milliseconds
64
+ * @param label Optional label for debugging
65
+ * @returns Timer ID (internal, not the native interval ID)
66
+ */
67
+ startInterval(callback, interval, label) {
68
+ const internalId = this.nextId++;
69
+ const nativeId = setInterval(() => {
70
+ try {
71
+ callback();
72
+ }
73
+ catch (e) {
74
+ console.error("[TimerManager] Interval callback error:", e);
75
+ }
76
+ }, interval);
77
+ this.timers.set(internalId, {
78
+ id: nativeId,
79
+ endTime: Infinity, // Intervals don't have an end time
80
+ isInterval: true,
81
+ label,
82
+ });
83
+ if (this.debug) {
84
+ console.debug(`[TimerManager] Started interval ${internalId}${label ? ` (${label})` : ""} every ${interval}ms`);
85
+ }
86
+ return internalId;
87
+ }
88
+ /**
89
+ * Stop a specific timer
90
+ * @param internalId The timer ID returned by start() or startInterval()
91
+ */
92
+ stop(internalId) {
93
+ const entry = this.timers.get(internalId);
94
+ if (!entry) {
95
+ return false;
96
+ }
97
+ if (entry.isInterval) {
98
+ clearInterval(entry.id);
99
+ }
100
+ else {
101
+ clearTimeout(entry.id);
102
+ }
103
+ this.timers.delete(internalId);
104
+ if (this.debug) {
105
+ console.debug(`[TimerManager] Stopped ${entry.isInterval ? "interval" : "timeout"} ${internalId}${entry.label ? ` (${entry.label})` : ""}`);
106
+ }
107
+ return true;
108
+ }
109
+ /**
110
+ * Stop all active timers
111
+ * Call this on component unmount/destroy to prevent memory leaks
112
+ */
113
+ stopAll() {
114
+ const count = this.timers.size;
115
+ for (const [_internalId, entry] of this.timers) {
116
+ if (entry.isInterval) {
117
+ clearInterval(entry.id);
118
+ }
119
+ else {
120
+ clearTimeout(entry.id);
121
+ }
122
+ }
123
+ this.timers.clear();
124
+ if (this.debug && count > 0) {
125
+ console.debug(`[TimerManager] Stopped all ${count} timers`);
126
+ }
127
+ }
128
+ /**
129
+ * Get count of active timers
130
+ */
131
+ get activeCount() {
132
+ return this.timers.size;
133
+ }
134
+ /**
135
+ * Check if a timer is active
136
+ */
137
+ isActive(internalId) {
138
+ return this.timers.has(internalId);
139
+ }
140
+ /**
141
+ * Get remaining time for a timeout (0 for intervals or expired)
142
+ */
143
+ getRemainingTime(internalId) {
144
+ const entry = this.timers.get(internalId);
145
+ if (!entry || entry.isInterval) {
146
+ return 0;
147
+ }
148
+ return Math.max(0, entry.endTime - Date.now());
149
+ }
150
+ /**
151
+ * Get debug info about all active timers
152
+ */
153
+ getDebugInfo() {
154
+ const info = [];
155
+ for (const [internalId, entry] of this.timers) {
156
+ info.push({
157
+ id: internalId,
158
+ type: entry.isInterval ? "interval" : "timeout",
159
+ label: entry.label,
160
+ remainingMs: entry.isInterval ? undefined : Math.max(0, entry.endTime - Date.now()),
161
+ });
162
+ }
163
+ return info;
164
+ }
165
+ /**
166
+ * Cleanup - alias for stopAll()
167
+ */
168
+ destroy() {
169
+ this.stopAll();
170
+ }
171
+ }
172
+
173
+ export { TimerManager };
174
+ //# sourceMappingURL=TimerManager.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TimerManager.js","sources":["../../../../src/core/TimerManager.ts"],"sourcesContent":["/**\n * TimerManager - Centralized timer management for memory leak prevention\n *\n * Tracks all setTimeout/setInterval calls and provides bulk cleanup.\n * Based on MistMetaPlayer's MistVideo.timers pattern.\n *\n * Usage:\n * ```ts\n * const timers = new TimerManager();\n *\n * // Start a timeout\n * const id = timers.start(() => console.log('fired'), 1000);\n *\n * // Start an interval\n * const intervalId = timers.startInterval(() => console.log('tick'), 500);\n *\n * // Stop a specific timer\n * timers.stop(id);\n *\n * // Stop all timers (on cleanup/destroy)\n * timers.stopAll();\n * ```\n */\n\ninterface TimerEntry {\n /** Timer ID from setTimeout/setInterval */\n id: ReturnType<typeof setTimeout>;\n /** Expected end time (for timeouts) */\n endTime: number;\n /** Whether this is an interval */\n isInterval: boolean;\n /** Optional label for debugging */\n label?: string;\n}\n\nexport class TimerManager {\n private timers: Map<number, TimerEntry> = new Map();\n private nextId = 1;\n private debug: boolean;\n\n constructor(options?: { debug?: boolean }) {\n this.debug = options?.debug ?? false;\n }\n\n /**\n * Start a timeout\n * @param callback Function to call after delay\n * @param delay Delay in milliseconds\n * @param label Optional label for debugging\n * @returns Timer ID (internal, not the native timeout ID)\n */\n start(callback: () => void, delay: number, label?: string): number {\n const internalId = this.nextId++;\n const endTime = Date.now() + delay;\n\n const nativeId = setTimeout(() => {\n this.timers.delete(internalId);\n try {\n callback();\n } catch (e) {\n console.error(\"[TimerManager] Callback error:\", e);\n }\n }, delay);\n\n this.timers.set(internalId, {\n id: nativeId,\n endTime,\n isInterval: false,\n label,\n });\n\n if (this.debug) {\n console.debug(\n `[TimerManager] Started timeout ${internalId}${label ? ` (${label})` : \"\"} for ${delay}ms`\n );\n }\n\n return internalId;\n }\n\n /**\n * Start an interval\n * @param callback Function to call repeatedly\n * @param interval Interval in milliseconds\n * @param label Optional label for debugging\n * @returns Timer ID (internal, not the native interval ID)\n */\n startInterval(callback: () => void, interval: number, label?: string): number {\n const internalId = this.nextId++;\n\n const nativeId = setInterval(() => {\n try {\n callback();\n } catch (e) {\n console.error(\"[TimerManager] Interval callback error:\", e);\n }\n }, interval);\n\n this.timers.set(internalId, {\n id: nativeId,\n endTime: Infinity, // Intervals don't have an end time\n isInterval: true,\n label,\n });\n\n if (this.debug) {\n console.debug(\n `[TimerManager] Started interval ${internalId}${label ? ` (${label})` : \"\"} every ${interval}ms`\n );\n }\n\n return internalId;\n }\n\n /**\n * Stop a specific timer\n * @param internalId The timer ID returned by start() or startInterval()\n */\n stop(internalId: number): boolean {\n const entry = this.timers.get(internalId);\n if (!entry) {\n return false;\n }\n\n if (entry.isInterval) {\n clearInterval(entry.id);\n } else {\n clearTimeout(entry.id);\n }\n\n this.timers.delete(internalId);\n\n if (this.debug) {\n console.debug(\n `[TimerManager] Stopped ${entry.isInterval ? \"interval\" : \"timeout\"} ${internalId}${entry.label ? ` (${entry.label})` : \"\"}`\n );\n }\n\n return true;\n }\n\n /**\n * Stop all active timers\n * Call this on component unmount/destroy to prevent memory leaks\n */\n stopAll(): void {\n const count = this.timers.size;\n\n for (const [_internalId, entry] of this.timers) {\n if (entry.isInterval) {\n clearInterval(entry.id);\n } else {\n clearTimeout(entry.id);\n }\n }\n\n this.timers.clear();\n\n if (this.debug && count > 0) {\n console.debug(`[TimerManager] Stopped all ${count} timers`);\n }\n }\n\n /**\n * Get count of active timers\n */\n get activeCount(): number {\n return this.timers.size;\n }\n\n /**\n * Check if a timer is active\n */\n isActive(internalId: number): boolean {\n return this.timers.has(internalId);\n }\n\n /**\n * Get remaining time for a timeout (0 for intervals or expired)\n */\n getRemainingTime(internalId: number): number {\n const entry = this.timers.get(internalId);\n if (!entry || entry.isInterval) {\n return 0;\n }\n return Math.max(0, entry.endTime - Date.now());\n }\n\n /**\n * Get debug info about all active timers\n */\n getDebugInfo(): Array<{\n id: number;\n type: \"timeout\" | \"interval\";\n label?: string;\n remainingMs?: number;\n }> {\n const info: Array<{\n id: number;\n type: \"timeout\" | \"interval\";\n label?: string;\n remainingMs?: number;\n }> = [];\n\n for (const [internalId, entry] of this.timers) {\n info.push({\n id: internalId,\n type: entry.isInterval ? \"interval\" : \"timeout\",\n label: entry.label,\n remainingMs: entry.isInterval ? undefined : Math.max(0, entry.endTime - Date.now()),\n });\n }\n\n return info;\n }\n\n /**\n * Cleanup - alias for stopAll()\n */\n destroy(): void {\n this.stopAll();\n }\n}\n\nexport default TimerManager;\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,151 @@
1
+ /**
2
+ * UrlUtils - URL manipulation utilities
3
+ *
4
+ * Based on MistMetaPlayer's urlappend functionality.
5
+ * Provides helpers for appending query parameters to URLs.
6
+ */
7
+ /**
8
+ * Append query parameters to a URL
9
+ * Handles URLs that already have query parameters
10
+ *
11
+ * @param url - Base URL
12
+ * @param params - Parameters to append (string or object)
13
+ * @returns URL with appended parameters
14
+ *
15
+ * @example
16
+ * ```ts
17
+ * appendUrlParams('https://example.com/video.m3u8', 'token=abc&session=123')
18
+ * // => 'https://example.com/video.m3u8?token=abc&session=123'
19
+ *
20
+ * appendUrlParams('https://example.com/video.m3u8?existing=param', 'token=abc')
21
+ * // => 'https://example.com/video.m3u8?existing=param&token=abc'
22
+ *
23
+ * appendUrlParams('https://example.com/video.m3u8', { token: 'abc', session: '123' })
24
+ * // => 'https://example.com/video.m3u8?token=abc&session=123'
25
+ * ```
26
+ */
27
+ function appendUrlParams(url, params) {
28
+ if (!params) {
29
+ return url;
30
+ }
31
+ // Convert object to query string
32
+ let queryString;
33
+ if (typeof params === "object") {
34
+ const entries = Object.entries(params)
35
+ .filter(([, value]) => value !== undefined && value !== null)
36
+ .map(([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(String(value))}`);
37
+ if (entries.length === 0) {
38
+ return url;
39
+ }
40
+ queryString = entries.join("&");
41
+ }
42
+ else {
43
+ queryString = params;
44
+ // Strip leading ? or & if present
45
+ if (queryString.startsWith("?") || queryString.startsWith("&")) {
46
+ queryString = queryString.slice(1);
47
+ }
48
+ }
49
+ if (!queryString) {
50
+ return url;
51
+ }
52
+ // Determine separator (? or &)
53
+ const separator = url.includes("?") ? "&" : "?";
54
+ return `${url}${separator}${queryString}`;
55
+ }
56
+ /**
57
+ * Parse query parameters from a URL
58
+ *
59
+ * @param url - URL to parse
60
+ * @returns Object with query parameters
61
+ */
62
+ function parseUrlParams(url) {
63
+ const params = {};
64
+ try {
65
+ const urlObj = new URL(url);
66
+ urlObj.searchParams.forEach((value, key) => {
67
+ params[key] = value;
68
+ });
69
+ }
70
+ catch {
71
+ // If URL parsing fails, try manual parsing
72
+ const queryIndex = url.indexOf("?");
73
+ if (queryIndex === -1) {
74
+ return params;
75
+ }
76
+ const queryString = url.slice(queryIndex + 1);
77
+ const pairs = queryString.split("&");
78
+ for (const pair of pairs) {
79
+ const [key, value] = pair.split("=");
80
+ if (key) {
81
+ params[decodeURIComponent(key)] = value ? decodeURIComponent(value) : "";
82
+ }
83
+ }
84
+ }
85
+ return params;
86
+ }
87
+ /**
88
+ * Remove query parameters from a URL
89
+ *
90
+ * @param url - URL to strip
91
+ * @returns URL without query parameters
92
+ */
93
+ function stripUrlParams(url) {
94
+ const queryIndex = url.indexOf("?");
95
+ return queryIndex === -1 ? url : url.slice(0, queryIndex);
96
+ }
97
+ /**
98
+ * Build a URL with query parameters
99
+ *
100
+ * @param baseUrl - Base URL
101
+ * @param params - Query parameters
102
+ * @returns Complete URL
103
+ */
104
+ function buildUrl(baseUrl, params) {
105
+ return appendUrlParams(stripUrlParams(baseUrl), params);
106
+ }
107
+ /**
108
+ * Check if URL uses secure protocol (https/wss)
109
+ */
110
+ function isSecureUrl(url) {
111
+ return url.startsWith("https://") || url.startsWith("wss://");
112
+ }
113
+ /**
114
+ * Convert HTTP URL to WebSocket URL
115
+ * http:// -> ws://
116
+ * https:// -> wss://
117
+ */
118
+ function httpToWs(url) {
119
+ return url.replace(/^http/, "ws");
120
+ }
121
+ /**
122
+ * Convert WebSocket URL to HTTP URL
123
+ * ws:// -> http://
124
+ * wss:// -> https://
125
+ */
126
+ function wsToHttp(url) {
127
+ return url.replace(/^ws/, "http");
128
+ }
129
+ /**
130
+ * Ensure URL uses the same protocol as the current page
131
+ * Useful for avoiding mixed content issues
132
+ */
133
+ function matchPageProtocol(url) {
134
+ if (typeof window === "undefined") {
135
+ return url;
136
+ }
137
+ const pageIsSecure = window.location.protocol === "https:";
138
+ const urlIsSecure = isSecureUrl(url);
139
+ if (pageIsSecure && !urlIsSecure) {
140
+ // Upgrade to secure
141
+ return url.replace(/^http:/, "https:").replace(/^ws:/, "wss:");
142
+ }
143
+ if (!pageIsSecure && urlIsSecure) {
144
+ // Downgrade to insecure (not recommended, but avoids issues)
145
+ return url.replace(/^https:/, "http:").replace(/^wss:/, "ws:");
146
+ }
147
+ return url;
148
+ }
149
+
150
+ export { appendUrlParams, buildUrl, httpToWs, isSecureUrl, matchPageProtocol, parseUrlParams, stripUrlParams, wsToHttp };
151
+ //# sourceMappingURL=UrlUtils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"UrlUtils.js","sources":["../../../../src/core/UrlUtils.ts"],"sourcesContent":["/**\n * UrlUtils - URL manipulation utilities\n *\n * Based on MistMetaPlayer's urlappend functionality.\n * Provides helpers for appending query parameters to URLs.\n */\n\n/**\n * Append query parameters to a URL\n * Handles URLs that already have query parameters\n *\n * @param url - Base URL\n * @param params - Parameters to append (string or object)\n * @returns URL with appended parameters\n *\n * @example\n * ```ts\n * appendUrlParams('https://example.com/video.m3u8', 'token=abc&session=123')\n * // => 'https://example.com/video.m3u8?token=abc&session=123'\n *\n * appendUrlParams('https://example.com/video.m3u8?existing=param', 'token=abc')\n * // => 'https://example.com/video.m3u8?existing=param&token=abc'\n *\n * appendUrlParams('https://example.com/video.m3u8', { token: 'abc', session: '123' })\n * // => 'https://example.com/video.m3u8?token=abc&session=123'\n * ```\n */\nexport function appendUrlParams(\n url: string,\n params: string | Record<string, string | number | boolean | undefined | null>\n): string {\n if (!params) {\n return url;\n }\n\n // Convert object to query string\n let queryString: string;\n if (typeof params === \"object\") {\n const entries = Object.entries(params)\n .filter(([, value]) => value !== undefined && value !== null)\n .map(([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(String(value))}`);\n\n if (entries.length === 0) {\n return url;\n }\n queryString = entries.join(\"&\");\n } else {\n queryString = params;\n // Strip leading ? or & if present\n if (queryString.startsWith(\"?\") || queryString.startsWith(\"&\")) {\n queryString = queryString.slice(1);\n }\n }\n\n if (!queryString) {\n return url;\n }\n\n // Determine separator (? or &)\n const separator = url.includes(\"?\") ? \"&\" : \"?\";\n return `${url}${separator}${queryString}`;\n}\n\n/**\n * Parse query parameters from a URL\n *\n * @param url - URL to parse\n * @returns Object with query parameters\n */\nexport function parseUrlParams(url: string): Record<string, string> {\n const params: Record<string, string> = {};\n\n try {\n const urlObj = new URL(url);\n urlObj.searchParams.forEach((value, key) => {\n params[key] = value;\n });\n } catch {\n // If URL parsing fails, try manual parsing\n const queryIndex = url.indexOf(\"?\");\n if (queryIndex === -1) {\n return params;\n }\n\n const queryString = url.slice(queryIndex + 1);\n const pairs = queryString.split(\"&\");\n for (const pair of pairs) {\n const [key, value] = pair.split(\"=\");\n if (key) {\n params[decodeURIComponent(key)] = value ? decodeURIComponent(value) : \"\";\n }\n }\n }\n\n return params;\n}\n\n/**\n * Remove query parameters from a URL\n *\n * @param url - URL to strip\n * @returns URL without query parameters\n */\nexport function stripUrlParams(url: string): string {\n const queryIndex = url.indexOf(\"?\");\n return queryIndex === -1 ? url : url.slice(0, queryIndex);\n}\n\n/**\n * Build a URL with query parameters\n *\n * @param baseUrl - Base URL\n * @param params - Query parameters\n * @returns Complete URL\n */\nexport function buildUrl(\n baseUrl: string,\n params: Record<string, string | number | boolean | undefined | null>\n): string {\n return appendUrlParams(stripUrlParams(baseUrl), params);\n}\n\n/**\n * Check if URL uses secure protocol (https/wss)\n */\nexport function isSecureUrl(url: string): boolean {\n return url.startsWith(\"https://\") || url.startsWith(\"wss://\");\n}\n\n/**\n * Convert HTTP URL to WebSocket URL\n * http:// -> ws://\n * https:// -> wss://\n */\nexport function httpToWs(url: string): string {\n return url.replace(/^http/, \"ws\");\n}\n\n/**\n * Convert WebSocket URL to HTTP URL\n * ws:// -> http://\n * wss:// -> https://\n */\nexport function wsToHttp(url: string): string {\n return url.replace(/^ws/, \"http\");\n}\n\n/**\n * Ensure URL uses the same protocol as the current page\n * Useful for avoiding mixed content issues\n */\nexport function matchPageProtocol(url: string): string {\n if (typeof window === \"undefined\") {\n return url;\n }\n\n const pageIsSecure = window.location.protocol === \"https:\";\n const urlIsSecure = isSecureUrl(url);\n\n if (pageIsSecure && !urlIsSecure) {\n // Upgrade to secure\n return url.replace(/^http:/, \"https:\").replace(/^ws:/, \"wss:\");\n }\n\n if (!pageIsSecure && urlIsSecure) {\n // Downgrade to insecure (not recommended, but avoids issues)\n return url.replace(/^https:/, \"http:\").replace(/^wss:/, \"ws:\");\n }\n\n return url;\n}\n\nexport default {\n appendUrlParams,\n parseUrlParams,\n stripUrlParams,\n buildUrl,\n isSecureUrl,\n httpToWs,\n wsToHttp,\n matchPageProtocol,\n};\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,279 @@
1
+ import { translateCodec } from './CodecUtils.js';
2
+
3
+ /**
4
+ * Browser and Codec Detection
5
+ * Ported from MistMetaPlayer v3.1.0
6
+ *
7
+ * Detects browser capabilities and codec support
8
+ * Removes legacy IE/Flash detection code
9
+ */
10
+ /**
11
+ * Detect browser information
12
+ */
13
+ function getBrowserInfo() {
14
+ const ua = navigator.userAgent.toLowerCase();
15
+ return {
16
+ isChrome: /chrome|crios/.test(ua) && !/edge|edg/.test(ua),
17
+ isFirefox: /firefox/.test(ua),
18
+ isSafari: /safari/.test(ua) && !/chrome|crios/.test(ua),
19
+ isEdge: /edge|edg/.test(ua),
20
+ isAndroid: /android/.test(ua),
21
+ isIOS: /iphone|ipad|ipod/.test(ua),
22
+ isMobile: /mobile|android|iphone|ipad|ipod/.test(ua),
23
+ supportsMediaSource: "MediaSource" in window,
24
+ supportsWebRTC: "RTCPeerConnection" in window,
25
+ supportsWebSocket: "WebSocket" in window,
26
+ };
27
+ }
28
+ /**
29
+ * Test codec support using MediaSource API
30
+ */
31
+ function testCodecSupport(mimeType, codec) {
32
+ if (!("MediaSource" in window)) {
33
+ return false;
34
+ }
35
+ if (!MediaSource.isTypeSupported) {
36
+ return true; // Can't test, assume it works
37
+ }
38
+ const fullType = `${mimeType};codecs="${codec}"`;
39
+ return MediaSource.isTypeSupported(fullType);
40
+ }
41
+ /**
42
+ * Get comprehensive codec support info
43
+ */
44
+ function getCodecSupport() {
45
+ return {
46
+ h264: testCodecSupport("video/mp4", "avc1.42E01E"),
47
+ h265: testCodecSupport("video/mp4", "hev1.1.6.L93.90"),
48
+ vp8: testCodecSupport("video/webm", "vp8"),
49
+ vp9: testCodecSupport("video/webm", "vp09.00.10.08"),
50
+ av1: testCodecSupport("video/mp4", "av01.0.04M.08"),
51
+ aac: testCodecSupport("video/mp4", "mp4a.40.2"),
52
+ mp3: testCodecSupport("audio/mpeg", "mp3"),
53
+ opus: testCodecSupport("audio/webm", "opus"),
54
+ };
55
+ }
56
+ /**
57
+ * Check if tracks are playable by testing codecs
58
+ */
59
+ function checkTrackPlayability(tracks, containerType) {
60
+ const playable = [];
61
+ const supported = [];
62
+ const tracksByType = {};
63
+ // Group tracks by type
64
+ for (const track of tracks) {
65
+ if (track.type === "meta")
66
+ continue;
67
+ if (!tracksByType[track.type]) {
68
+ tracksByType[track.type] = [];
69
+ }
70
+ tracksByType[track.type].push(track);
71
+ }
72
+ // Test each track type
73
+ for (const [trackType, typeTracks] of Object.entries(tracksByType)) {
74
+ let hasPlayableTrack = false;
75
+ for (const track of typeTracks) {
76
+ const codecString = translateCodec(track);
77
+ if (testCodecSupport(containerType, codecString)) {
78
+ supported.push(track.codec);
79
+ hasPlayableTrack = true;
80
+ }
81
+ }
82
+ if (hasPlayableTrack) {
83
+ playable.push(trackType);
84
+ }
85
+ }
86
+ return { playable, supported };
87
+ }
88
+ /**
89
+ * Check protocol/scheme mismatch (http/https)
90
+ */
91
+ function checkProtocolMismatch(sourceUrl) {
92
+ const pageProtocol = window.location.protocol;
93
+ const sourceProtocol = new URL(sourceUrl).protocol;
94
+ // Allow file:// to access http://
95
+ if (pageProtocol === "file:" && sourceProtocol === "http:") {
96
+ return false; // No mismatch
97
+ }
98
+ return pageProtocol !== sourceProtocol;
99
+ }
100
+ /**
101
+ * Check if current page is loaded over file://
102
+ */
103
+ function isFileProtocol() {
104
+ return window.location.protocol === "file:";
105
+ }
106
+ /**
107
+ * Get Android version (returns null if not Android)
108
+ */
109
+ function getAndroidVersion() {
110
+ const match = navigator.userAgent.match(/Android (\d+)(?:\.(\d+))?(?:\.(\d+))*/i);
111
+ if (!match)
112
+ return null;
113
+ const major = parseInt(match[1], 10);
114
+ const minor = match[2] ? parseInt(match[2], 10) : 0;
115
+ return major + minor / 10;
116
+ }
117
+ /**
118
+ * Browser-specific compatibility checks
119
+ */
120
+ function getBrowserCompatibility() {
121
+ const browser = getBrowserInfo();
122
+ const android = getAndroidVersion();
123
+ return {
124
+ // Native HLS support
125
+ supportsNativeHLS: browser.isSafari || browser.isIOS || (android && android >= 7),
126
+ // MSE support
127
+ supportsMSE: browser.supportsMediaSource,
128
+ // WebSocket support
129
+ supportsWebSocket: browser.supportsWebSocket,
130
+ // WebRTC support
131
+ supportsWebRTC: browser.supportsWebRTC && "RTCRtpReceiver" in window,
132
+ // Specific player recommendations
133
+ preferVideoJs: android && android < 7, // VideoJS better for older Android
134
+ avoidMEWSOnMac: browser.isSafari, // MEWS breaks often on Safari/macOS
135
+ // File protocol limitations
136
+ fileProtocolLimitations: isFileProtocol(),
137
+ };
138
+ }
139
+ // ============================================================================
140
+ // WebRTC Codec Compatibility
141
+ // ============================================================================
142
+ /**
143
+ * Codecs that are compatible with WebRTC (WHEP/MistServer native)
144
+ * These are the only codecs that can be used in RTP streams
145
+ */
146
+ const WEBRTC_COMPATIBLE_CODECS = {
147
+ video: ["H264", "VP8", "VP9", "AV1"],
148
+ // Note: AAC is NOT natively supported in browser WebRTC - OPUS is standard
149
+ // MistServer may transcode to OPUS for WebRTC output
150
+ audio: ["OPUS", "PCMU", "PCMA", "G711", "G722"],
151
+ };
152
+ /**
153
+ * Codecs that are explicitly incompatible with WebRTC
154
+ */
155
+ const WEBRTC_INCOMPATIBLE_CODECS = {
156
+ video: ["HEVC", "H265", "THEORA", "MPEG2"],
157
+ audio: ["AC3", "EAC3", "EC3", "MP3", "FLAC", "VORBIS", "DTS"],
158
+ };
159
+ /**
160
+ * Check if browser supports a codec via WebRTC
161
+ * Uses RTCRtpReceiver.getCapabilities() for dynamic browser detection (reference: webrtc.js:38-46)
162
+ * Falls back to static list if API unavailable
163
+ */
164
+ function isBrowserWebRTCCodecSupported(type, codec) {
165
+ // Try dynamic browser API first (reference: webrtc.js:39)
166
+ if (typeof RTCRtpReceiver !== "undefined" && RTCRtpReceiver.getCapabilities) {
167
+ try {
168
+ const capabilities = RTCRtpReceiver.getCapabilities(type);
169
+ if (capabilities?.codecs) {
170
+ const targetMime = `${type}/${codec}`.toLowerCase();
171
+ return capabilities.codecs.some((c) => c.mimeType.toLowerCase() === targetMime);
172
+ }
173
+ }
174
+ catch {
175
+ // Fall through to static list
176
+ }
177
+ }
178
+ // Fallback to static list for older browsers/SSR
179
+ const compatibleCodecs = WEBRTC_COMPATIBLE_CODECS[type];
180
+ return compatibleCodecs.includes(codec.toUpperCase());
181
+ }
182
+ /**
183
+ * Check if stream tracks are compatible with WebRTC playback
184
+ *
185
+ * Uses RTCRtpReceiver.getCapabilities() to dynamically query browser support,
186
+ * matching the reference MistServer implementation (webrtc.js:38-46).
187
+ *
188
+ * @param tracks - Array of track metadata from MistServer
189
+ * @returns Compatibility assessment
190
+ */
191
+ function checkWebRTCCodecCompatibility(tracks) {
192
+ const videoTracks = tracks.filter((t) => t.type === "video");
193
+ const audioTracks = tracks.filter((t) => t.type === "audio");
194
+ const compatibleVideoCodecs = [];
195
+ const compatibleAudioCodecs = [];
196
+ const incompatibleCodecs = [];
197
+ // Check video tracks using dynamic browser detection
198
+ for (const track of videoTracks) {
199
+ if (isBrowserWebRTCCodecSupported("video", track.codec)) {
200
+ compatibleVideoCodecs.push(track.codec);
201
+ }
202
+ else {
203
+ incompatibleCodecs.push(`video:${track.codec}`);
204
+ }
205
+ }
206
+ // Check audio tracks using dynamic browser detection
207
+ for (const track of audioTracks) {
208
+ if (isBrowserWebRTCCodecSupported("audio", track.codec)) {
209
+ compatibleAudioCodecs.push(track.codec);
210
+ }
211
+ else {
212
+ incompatibleCodecs.push(`audio:${track.codec}`);
213
+ }
214
+ }
215
+ // Video is compatible if there's at least one compatible video codec
216
+ // (or no video tracks at all - audio-only streams are fine)
217
+ const videoCompatible = videoTracks.length === 0 || compatibleVideoCodecs.length > 0;
218
+ // Audio is compatible if there's at least one compatible audio codec
219
+ // (or no audio tracks at all - video-only streams are fine)
220
+ const audioCompatible = audioTracks.length === 0 || compatibleAudioCodecs.length > 0;
221
+ return {
222
+ videoCompatible,
223
+ audioCompatible,
224
+ compatible: videoCompatible && audioCompatible,
225
+ incompatibleCodecs,
226
+ details: {
227
+ videoTracks: videoTracks.length,
228
+ audioTracks: audioTracks.length,
229
+ compatibleVideoCodecs,
230
+ compatibleAudioCodecs,
231
+ },
232
+ };
233
+ }
234
+ /**
235
+ * Check if stream tracks are compatible with MSE playback in this browser
236
+ *
237
+ * Unlike WebRTC, MSE compatibility varies by browser. HEVC works in Safari
238
+ * but not Firefox. This function actually tests MediaSource.isTypeSupported().
239
+ *
240
+ * @param tracks - Array of track metadata
241
+ * @param containerType - MIME type (e.g., 'video/mp4')
242
+ */
243
+ function checkMSECodecCompatibility(tracks, containerType = "video/mp4") {
244
+ const videoTracks = tracks.filter((t) => t.type === "video");
245
+ const audioTracks = tracks.filter((t) => t.type === "audio");
246
+ const unsupportedCodecs = [];
247
+ let hasCompatibleVideo = videoTracks.length === 0;
248
+ let hasCompatibleAudio = audioTracks.length === 0;
249
+ // Test each video track
250
+ for (const track of videoTracks) {
251
+ const codecString = translateCodec(track);
252
+ if (testCodecSupport(containerType, codecString)) {
253
+ hasCompatibleVideo = true;
254
+ }
255
+ else {
256
+ unsupportedCodecs.push(`video:${track.codec}`);
257
+ }
258
+ }
259
+ // Test each audio track
260
+ for (const track of audioTracks) {
261
+ const codecString = translateCodec(track);
262
+ const audioContainer = track.codec === "MP3" ? "audio/mpeg" : containerType;
263
+ if (testCodecSupport(audioContainer, codecString)) {
264
+ hasCompatibleAudio = true;
265
+ }
266
+ else {
267
+ unsupportedCodecs.push(`audio:${track.codec}`);
268
+ }
269
+ }
270
+ return {
271
+ videoCompatible: hasCompatibleVideo,
272
+ audioCompatible: hasCompatibleAudio,
273
+ compatible: hasCompatibleVideo && hasCompatibleAudio,
274
+ unsupportedCodecs,
275
+ };
276
+ }
277
+
278
+ export { WEBRTC_COMPATIBLE_CODECS, WEBRTC_INCOMPATIBLE_CODECS, checkMSECodecCompatibility, checkProtocolMismatch, checkTrackPlayability, checkWebRTCCodecCompatibility, getAndroidVersion, getBrowserCompatibility, getBrowserInfo, getCodecSupport, isFileProtocol, testCodecSupport, translateCodec };
279
+ //# sourceMappingURL=detector.js.map