@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,454 @@
1
+ import { TimerManager } from './TimerManager.js';
2
+
3
+ /**
4
+ * Default ABR options
5
+ */
6
+ const DEFAULT_OPTIONS = {
7
+ mode: "auto",
8
+ maxResolution: { width: 1920, height: 1080 },
9
+ maxBitrate: 8000000, // 8 Mbps
10
+ minBufferForUpgrade: 10,
11
+ downgradeThreshold: 60,
12
+ };
13
+ /**
14
+ * ABRController - Adaptive Bitrate Controller
15
+ *
16
+ * Manages automatic quality selection based on:
17
+ * - ABR_resize: Matches video resolution to viewport size
18
+ * - ABR_bitrate: Switches quality based on playback performance
19
+ * - auto: Combines both modes
20
+ * - manual: No automatic switching
21
+ *
22
+ * @example
23
+ * ```ts
24
+ * const abr = new ABRController({
25
+ * options: { mode: 'auto' },
26
+ * getQualities: () => player.getQualities(),
27
+ * selectQuality: (id) => player.selectQuality(id),
28
+ * });
29
+ *
30
+ * abr.start(videoElement);
31
+ * abr.onQualityChange((quality) => console.log('Quality:', quality.score));
32
+ * ```
33
+ */
34
+ class ABRController {
35
+ constructor(config) {
36
+ this.videoElement = null;
37
+ this.currentQualityId = "auto";
38
+ this.lastDecision = "none";
39
+ this.lastDecisionTime = 0;
40
+ this.resizeObserver = null;
41
+ this.qualityChangeCallbacks = [];
42
+ // Active monitoring
43
+ this.timers = new TimerManager();
44
+ this.bandwidthHistory = [];
45
+ this.lastUpgradeTime = 0;
46
+ this.lastDowngradeTime = 0;
47
+ this.currentQualityBitrate = 0;
48
+ this.options = { ...DEFAULT_OPTIONS, ...config.options };
49
+ this.config = config;
50
+ this.debug = config.debug ?? false;
51
+ }
52
+ /**
53
+ * Start ABR control
54
+ */
55
+ start(videoElement) {
56
+ this.stop();
57
+ this.videoElement = videoElement;
58
+ if (this.options.mode === "manual") {
59
+ this.log("Manual mode - no automatic ABR");
60
+ return;
61
+ }
62
+ // Setup resize observer for ABR_resize mode
63
+ if (this.options.mode === "resize" || this.options.mode === "auto") {
64
+ this.setupResizeObserver();
65
+ }
66
+ // Start active bandwidth monitoring for bitrate mode
67
+ if (this.options.mode === "bitrate" || this.options.mode === "auto") {
68
+ this.startActiveMonitoring();
69
+ }
70
+ }
71
+ /**
72
+ * Stop ABR control
73
+ */
74
+ stop() {
75
+ if (this.resizeObserver) {
76
+ this.resizeObserver.disconnect();
77
+ this.resizeObserver = null;
78
+ }
79
+ this.timers.destroy();
80
+ this.videoElement = null;
81
+ this.bandwidthHistory = [];
82
+ }
83
+ /**
84
+ * Start active bandwidth monitoring loop
85
+ * Continuously monitors bandwidth and proactively switches quality
86
+ */
87
+ startActiveMonitoring() {
88
+ this.timers.startInterval(() => this.checkBandwidthAndSwitch(), ABRController.MONITORING_INTERVAL_MS, "monitoring");
89
+ // Initial check
90
+ this.checkBandwidthAndSwitch();
91
+ }
92
+ /**
93
+ * Check current bandwidth and switch quality if needed
94
+ *
95
+ * Uses hysteresis (D2) and separate cooldowns (D3) to prevent oscillation:
96
+ * - Downgrade: immediate response (0ms cooldown), triggers at 0.8x
97
+ * - Upgrade: 5s cooldown, requires 1.5x headroom, holds until 1.2x
98
+ */
99
+ async checkBandwidthAndSwitch() {
100
+ if (!this.videoElement)
101
+ return;
102
+ const now = Date.now();
103
+ // Get bandwidth estimate from player stats
104
+ const bandwidth = await this.getBandwidthEstimate();
105
+ if (bandwidth <= 0)
106
+ return;
107
+ // Add to history
108
+ this.bandwidthHistory.push(bandwidth);
109
+ if (this.bandwidthHistory.length > ABRController.BANDWIDTH_HISTORY_SIZE) {
110
+ this.bandwidthHistory.shift();
111
+ }
112
+ // Use smoothed bandwidth (average of history)
113
+ const smoothedBandwidth = this.getSmoothedBandwidth();
114
+ if (smoothedBandwidth <= 0)
115
+ return;
116
+ const qualities = this.config.getQualities();
117
+ const currentQuality = this.config.getCurrentQuality?.();
118
+ if (!currentQuality || qualities.length === 0)
119
+ return;
120
+ const currentBitrate = currentQuality.bitrate || 0;
121
+ // Track current quality bitrate for hysteresis
122
+ if (this.currentQualityBitrate !== currentBitrate) {
123
+ this.currentQualityBitrate = currentBitrate;
124
+ }
125
+ // D3: Check for downgrade (immediate, no cooldown)
126
+ if (now - this.lastDowngradeTime >= ABRController.DOWNGRADE_COOLDOWN_MS) {
127
+ if (smoothedBandwidth < currentBitrate * ABRController.DOWNGRADE_THRESHOLD) {
128
+ const lowerQuality = this.findLowerQuality(qualities, currentQuality);
129
+ if (lowerQuality) {
130
+ this.log(`ABR: bandwidth ${Math.round(smoothedBandwidth / 1000)}kbps < ${Math.round((currentBitrate * ABRController.DOWNGRADE_THRESHOLD) / 1000)}kbps threshold -> downgrading to ${lowerQuality.label}`);
131
+ this.lastDecision = "downgrade";
132
+ this.lastDecisionTime = now;
133
+ this.lastDowngradeTime = now;
134
+ this.selectQuality(lowerQuality.id);
135
+ return;
136
+ }
137
+ }
138
+ }
139
+ // D3: Check for upgrade (5s cooldown required)
140
+ if (now - this.lastUpgradeTime >= ABRController.UPGRADE_COOLDOWN_MS) {
141
+ const higherQuality = this.findHigherQuality(qualities, currentQuality);
142
+ if (higherQuality && this.isWithinConstraints(higherQuality)) {
143
+ const targetBitrate = higherQuality.bitrate || 0;
144
+ // D2: Hysteresis - require 1.5x headroom to upgrade
145
+ // Once at a quality level, stay until bandwidth drops below 1.2x (not 1.0x)
146
+ const shouldUpgrade = smoothedBandwidth >= targetBitrate * ABRController.UPGRADE_HEADROOM;
147
+ smoothedBandwidth >= targetBitrate * ABRController.UPGRADE_HOLD_THRESHOLD;
148
+ if (shouldUpgrade) {
149
+ this.log(`ABR: bandwidth ${Math.round(smoothedBandwidth / 1000)}kbps >= ${Math.round((targetBitrate * ABRController.UPGRADE_HEADROOM) / 1000)}kbps headroom -> upgrading to ${higherQuality.label}`);
150
+ this.lastDecision = "upgrade";
151
+ this.lastDecisionTime = now;
152
+ this.lastUpgradeTime = now;
153
+ this.selectQuality(higherQuality.id);
154
+ return;
155
+ }
156
+ }
157
+ }
158
+ }
159
+ /**
160
+ * Get bandwidth estimate from player stats
161
+ */
162
+ async getBandwidthEstimate() {
163
+ // Try to get bandwidth from player stats
164
+ if (this.config.getBandwidthEstimate) {
165
+ const estimate = await this.config.getBandwidthEstimate();
166
+ if (estimate > 0)
167
+ return estimate;
168
+ }
169
+ // Fallback: estimate from buffer growth (rough)
170
+ const video = this.videoElement;
171
+ if (!video || video.buffered.length === 0)
172
+ return 0;
173
+ // This is a rough fallback - prefer player-specific estimates
174
+ return 0;
175
+ }
176
+ /**
177
+ * Get smoothed bandwidth from history
178
+ */
179
+ getSmoothedBandwidth() {
180
+ if (this.bandwidthHistory.length === 0)
181
+ return 0;
182
+ if (this.bandwidthHistory.length < 3) {
183
+ // Need at least 3 samples for reliable estimate
184
+ return 0;
185
+ }
186
+ const sum = this.bandwidthHistory.reduce((a, b) => a + b, 0);
187
+ return sum / this.bandwidthHistory.length;
188
+ }
189
+ /**
190
+ * Get current bandwidth estimate (for external use)
191
+ */
192
+ getCurrentBandwidth() {
193
+ return this.getSmoothedBandwidth();
194
+ }
195
+ /**
196
+ * Setup resize observer for viewport-based quality selection
197
+ */
198
+ setupResizeObserver() {
199
+ const video = this.videoElement;
200
+ if (!video)
201
+ return;
202
+ this.resizeObserver = new ResizeObserver((entries) => {
203
+ for (const entry of entries) {
204
+ const { width, height } = entry.contentRect;
205
+ this.handleResize(width, height);
206
+ }
207
+ });
208
+ // Observe the video element's container
209
+ const container = video.parentElement;
210
+ if (container) {
211
+ this.resizeObserver.observe(container);
212
+ }
213
+ // Initial resize handling
214
+ const rect = video.getBoundingClientRect();
215
+ this.handleResize(rect.width, rect.height);
216
+ }
217
+ /**
218
+ * Handle viewport resize (ABR_resize mode)
219
+ */
220
+ handleResize(width, height) {
221
+ if (this.options.mode !== "resize" && this.options.mode !== "auto") {
222
+ return;
223
+ }
224
+ const qualities = this.config.getQualities();
225
+ if (qualities.length === 0)
226
+ return;
227
+ // Find best quality for viewport size
228
+ const targetWidth = Math.min(width * window.devicePixelRatio, this.options.maxResolution.width);
229
+ const targetHeight = Math.min(height * window.devicePixelRatio, this.options.maxResolution.height);
230
+ const bestQuality = this.findBestQualityForResolution(qualities, targetWidth, targetHeight);
231
+ if (bestQuality && bestQuality.id !== this.currentQualityId) {
232
+ this.log(`Resize ABR: ${width}x${height} -> selecting ${bestQuality.label}`);
233
+ this.selectQuality(bestQuality.id);
234
+ }
235
+ }
236
+ /**
237
+ * Handle quality degradation (ABR_bitrate mode)
238
+ *
239
+ * Called by QualityMonitor when playback quality drops
240
+ */
241
+ handleQualityDegraded(quality) {
242
+ if (this.options.mode !== "bitrate" && this.options.mode !== "auto") {
243
+ return;
244
+ }
245
+ // D3: Downgrade should be fast (0ms default), but still respects configured cooldown
246
+ const now = Date.now();
247
+ if (now - this.lastDowngradeTime < ABRController.DOWNGRADE_COOLDOWN_MS) {
248
+ return;
249
+ }
250
+ if (quality.score < this.options.downgradeThreshold) {
251
+ const qualities = this.config.getQualities();
252
+ const currentQuality = this.config.getCurrentQuality?.();
253
+ if (currentQuality) {
254
+ // Find a lower quality level
255
+ const lowerQuality = this.findLowerQuality(qualities, currentQuality);
256
+ if (lowerQuality) {
257
+ this.log(`Bitrate ABR: score ${quality.score} -> downgrading to ${lowerQuality.label}`);
258
+ this.lastDecision = "downgrade";
259
+ this.lastDecisionTime = now;
260
+ this.lastDowngradeTime = now;
261
+ this.selectQuality(lowerQuality.id);
262
+ }
263
+ }
264
+ }
265
+ }
266
+ /**
267
+ * Handle quality improvement opportunity
268
+ *
269
+ * Called when conditions are good enough to try higher quality
270
+ */
271
+ handleQualityImproved(quality) {
272
+ if (this.options.mode !== "bitrate" && this.options.mode !== "auto") {
273
+ return;
274
+ }
275
+ // D3: Upgrade requires sustained stability (5s default) to prevent flapping
276
+ const now = Date.now();
277
+ if (now - this.lastUpgradeTime < ABRController.UPGRADE_COOLDOWN_MS) {
278
+ return;
279
+ }
280
+ // Only upgrade if buffer is healthy and quality is good
281
+ if (quality.score >= 90 && quality.bufferedAhead >= this.options.minBufferForUpgrade) {
282
+ const qualities = this.config.getQualities();
283
+ const currentQuality = this.config.getCurrentQuality?.();
284
+ if (currentQuality) {
285
+ // Find a higher quality level
286
+ const higherQuality = this.findHigherQuality(qualities, currentQuality);
287
+ if (higherQuality && this.isWithinConstraints(higherQuality)) {
288
+ this.log(`Bitrate ABR: score ${quality.score} -> upgrading to ${higherQuality.label}`);
289
+ this.lastDecision = "upgrade";
290
+ this.lastDecisionTime = now;
291
+ this.lastUpgradeTime = now;
292
+ this.selectQuality(higherQuality.id);
293
+ }
294
+ }
295
+ }
296
+ }
297
+ /**
298
+ * Find best quality level for given resolution
299
+ */
300
+ findBestQualityForResolution(qualities, targetWidth, targetHeight) {
301
+ // Filter out qualities that exceed constraints
302
+ const validQualities = qualities.filter((q) => this.isWithinConstraints(q));
303
+ if (validQualities.length === 0)
304
+ return null;
305
+ // Sort by resolution (ascending)
306
+ const sorted = [...validQualities].sort((a, b) => {
307
+ const aPixels = (a.width ?? 0) * (a.height ?? 0);
308
+ const bPixels = (b.width ?? 0) * (b.height ?? 0);
309
+ return aPixels - bPixels;
310
+ });
311
+ // Find smallest quality that is >= target resolution
312
+ for (const q of sorted) {
313
+ const qWidth = q.width ?? 0;
314
+ const qHeight = q.height ?? 0;
315
+ if (qWidth >= targetWidth && qHeight >= targetHeight) {
316
+ return q;
317
+ }
318
+ }
319
+ // If no quality is large enough, return the highest available
320
+ return sorted[sorted.length - 1];
321
+ }
322
+ /**
323
+ * Find a lower quality level
324
+ */
325
+ findLowerQuality(qualities, current) {
326
+ const currentBitrate = current.bitrate ?? 0;
327
+ // Sort by bitrate descending
328
+ const sorted = [...qualities].sort((a, b) => (b.bitrate ?? 0) - (a.bitrate ?? 0));
329
+ // Find next lower bitrate
330
+ for (const q of sorted) {
331
+ if ((q.bitrate ?? 0) < currentBitrate) {
332
+ return q;
333
+ }
334
+ }
335
+ return null;
336
+ }
337
+ /**
338
+ * Find a higher quality level
339
+ */
340
+ findHigherQuality(qualities, current) {
341
+ const currentBitrate = current.bitrate ?? 0;
342
+ // Sort by bitrate ascending
343
+ const sorted = [...qualities].sort((a, b) => (a.bitrate ?? 0) - (b.bitrate ?? 0));
344
+ // Find next higher bitrate
345
+ for (const q of sorted) {
346
+ if ((q.bitrate ?? 0) > currentBitrate) {
347
+ return q;
348
+ }
349
+ }
350
+ return null;
351
+ }
352
+ /**
353
+ * Check if quality is within configured constraints
354
+ */
355
+ isWithinConstraints(quality) {
356
+ const { maxResolution, maxBitrate } = this.options;
357
+ if (quality.width && quality.width > maxResolution.width)
358
+ return false;
359
+ if (quality.height && quality.height > maxResolution.height)
360
+ return false;
361
+ if (quality.bitrate && quality.bitrate > maxBitrate)
362
+ return false;
363
+ return true;
364
+ }
365
+ /**
366
+ * Select a quality level
367
+ */
368
+ selectQuality(id) {
369
+ this.currentQualityId = id;
370
+ this.config.selectQuality(id);
371
+ // Notify callbacks
372
+ const qualities = this.config.getQualities();
373
+ const selected = qualities.find((q) => q.id === id);
374
+ if (selected) {
375
+ this.qualityChangeCallbacks.forEach((cb) => cb(selected));
376
+ }
377
+ }
378
+ /**
379
+ * Register callback for quality changes
380
+ */
381
+ onQualityChange(callback) {
382
+ this.qualityChangeCallbacks.push(callback);
383
+ return () => {
384
+ const idx = this.qualityChangeCallbacks.indexOf(callback);
385
+ if (idx >= 0) {
386
+ this.qualityChangeCallbacks.splice(idx, 1);
387
+ }
388
+ };
389
+ }
390
+ /**
391
+ * Manually set quality (switches to manual mode temporarily)
392
+ */
393
+ setQuality(id) {
394
+ this.selectQuality(id);
395
+ }
396
+ /**
397
+ * Get current ABR mode
398
+ */
399
+ getMode() {
400
+ return this.options.mode;
401
+ }
402
+ /**
403
+ * Set ABR mode at runtime.
404
+ * Restarts monitoring if video element is attached.
405
+ */
406
+ setMode(mode) {
407
+ if (this.options.mode === mode)
408
+ return;
409
+ this.options.mode = mode;
410
+ this.log(`Mode changed to: ${mode}`);
411
+ // Restart with new mode if we have a video element
412
+ if (this.videoElement) {
413
+ const video = this.videoElement;
414
+ this.stop();
415
+ this.start(video);
416
+ }
417
+ }
418
+ /**
419
+ * Update ABR options
420
+ */
421
+ updateOptions(options) {
422
+ this.options = { ...this.options, ...options };
423
+ }
424
+ /**
425
+ * Get last ABR decision
426
+ */
427
+ getLastDecision() {
428
+ return this.lastDecision;
429
+ }
430
+ /**
431
+ * Debug logging
432
+ */
433
+ log(message) {
434
+ if (this.debug) {
435
+ console.debug(`[ABRController] ${message}`);
436
+ }
437
+ }
438
+ }
439
+ ABRController.BANDWIDTH_HISTORY_SIZE = 10;
440
+ ABRController.MONITORING_INTERVAL_MS = 1000;
441
+ // D3: Separate upgrade/downgrade cooldowns
442
+ // Downgrade is immediate (0ms) for quick response to problems
443
+ // Upgrade requires 5s stability to prevent flapping
444
+ ABRController.UPGRADE_COOLDOWN_MS = 5000;
445
+ ABRController.DOWNGRADE_COOLDOWN_MS = 0;
446
+ // D2: Hysteresis bands to prevent oscillation at boundaries
447
+ // Upgrade: must exceed 1.5x to upgrade, stay until drops below 1.2x
448
+ // Downgrade: must drop below 0.8x to downgrade
449
+ ABRController.UPGRADE_HEADROOM = 1.5;
450
+ ABRController.UPGRADE_HOLD_THRESHOLD = 1.2;
451
+ ABRController.DOWNGRADE_THRESHOLD = 0.8;
452
+
453
+ export { ABRController };
454
+ //# sourceMappingURL=ABRController.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ABRController.js","sources":["../../../../src/core/ABRController.ts"],"sourcesContent":["import type { ABRMode, ABROptions, PlaybackQuality, QualityLevel } from \"../types\";\nimport { TimerManager } from \"./TimerManager\";\n\n/**\n * Default ABR options\n */\nconst DEFAULT_OPTIONS: Required<ABROptions> = {\n mode: \"auto\",\n maxResolution: { width: 1920, height: 1080 },\n maxBitrate: 8000000, // 8 Mbps\n minBufferForUpgrade: 10,\n downgradeThreshold: 60,\n};\n\nexport interface ABRControllerConfig {\n /** ABR options */\n options?: Partial<ABROptions>;\n /** Callback to get available qualities */\n getQualities: () => QualityLevel[];\n /** Callback to select a quality */\n selectQuality: (id: string | \"auto\") => void;\n /** Callback to get current quality */\n getCurrentQuality?: () => QualityLevel | null;\n /** Callback to get bandwidth estimate (bits per second) - typically from player stats */\n getBandwidthEstimate?: () => Promise<number>;\n /** Debug logging */\n debug?: boolean;\n}\n\nexport type ABRDecision = \"upgrade\" | \"downgrade\" | \"maintain\" | \"none\";\n\n/**\n * ABRController - Adaptive Bitrate Controller\n *\n * Manages automatic quality selection based on:\n * - ABR_resize: Matches video resolution to viewport size\n * - ABR_bitrate: Switches quality based on playback performance\n * - auto: Combines both modes\n * - manual: No automatic switching\n *\n * @example\n * ```ts\n * const abr = new ABRController({\n * options: { mode: 'auto' },\n * getQualities: () => player.getQualities(),\n * selectQuality: (id) => player.selectQuality(id),\n * });\n *\n * abr.start(videoElement);\n * abr.onQualityChange((quality) => console.log('Quality:', quality.score));\n * ```\n */\nexport class ABRController {\n private options: Required<ABROptions>;\n private config: ABRControllerConfig;\n private videoElement: HTMLVideoElement | null = null;\n private currentQualityId: string | \"auto\" = \"auto\";\n private lastDecision: ABRDecision = \"none\";\n private lastDecisionTime = 0;\n private resizeObserver: ResizeObserver | null = null;\n private qualityChangeCallbacks: Array<(level: QualityLevel) => void> = [];\n private debug: boolean;\n\n // Active monitoring\n private timers = new TimerManager();\n private bandwidthHistory: number[] = [];\n private static readonly BANDWIDTH_HISTORY_SIZE = 10;\n private static readonly MONITORING_INTERVAL_MS = 1000;\n\n // D3: Separate upgrade/downgrade cooldowns\n // Downgrade is immediate (0ms) for quick response to problems\n // Upgrade requires 5s stability to prevent flapping\n private static readonly UPGRADE_COOLDOWN_MS = 5000;\n private static readonly DOWNGRADE_COOLDOWN_MS = 0;\n private lastUpgradeTime = 0;\n private lastDowngradeTime = 0;\n\n // D2: Hysteresis bands to prevent oscillation at boundaries\n // Upgrade: must exceed 1.5x to upgrade, stay until drops below 1.2x\n // Downgrade: must drop below 0.8x to downgrade\n private static readonly UPGRADE_HEADROOM = 1.5;\n private static readonly UPGRADE_HOLD_THRESHOLD = 1.2;\n private static readonly DOWNGRADE_THRESHOLD = 0.8;\n private currentQualityBitrate = 0;\n\n constructor(config: ABRControllerConfig) {\n this.options = { ...DEFAULT_OPTIONS, ...config.options };\n this.config = config;\n this.debug = config.debug ?? false;\n }\n\n /**\n * Start ABR control\n */\n start(videoElement: HTMLVideoElement): void {\n this.stop();\n this.videoElement = videoElement;\n\n if (this.options.mode === \"manual\") {\n this.log(\"Manual mode - no automatic ABR\");\n return;\n }\n\n // Setup resize observer for ABR_resize mode\n if (this.options.mode === \"resize\" || this.options.mode === \"auto\") {\n this.setupResizeObserver();\n }\n\n // Start active bandwidth monitoring for bitrate mode\n if (this.options.mode === \"bitrate\" || this.options.mode === \"auto\") {\n this.startActiveMonitoring();\n }\n }\n\n /**\n * Stop ABR control\n */\n stop(): void {\n if (this.resizeObserver) {\n this.resizeObserver.disconnect();\n this.resizeObserver = null;\n }\n this.timers.destroy();\n this.videoElement = null;\n this.bandwidthHistory = [];\n }\n\n /**\n * Start active bandwidth monitoring loop\n * Continuously monitors bandwidth and proactively switches quality\n */\n private startActiveMonitoring(): void {\n this.timers.startInterval(\n () => this.checkBandwidthAndSwitch(),\n ABRController.MONITORING_INTERVAL_MS,\n \"monitoring\"\n );\n\n // Initial check\n this.checkBandwidthAndSwitch();\n }\n\n /**\n * Check current bandwidth and switch quality if needed\n *\n * Uses hysteresis (D2) and separate cooldowns (D3) to prevent oscillation:\n * - Downgrade: immediate response (0ms cooldown), triggers at 0.8x\n * - Upgrade: 5s cooldown, requires 1.5x headroom, holds until 1.2x\n */\n private async checkBandwidthAndSwitch(): Promise<void> {\n if (!this.videoElement) return;\n\n const now = Date.now();\n\n // Get bandwidth estimate from player stats\n const bandwidth = await this.getBandwidthEstimate();\n if (bandwidth <= 0) return;\n\n // Add to history\n this.bandwidthHistory.push(bandwidth);\n if (this.bandwidthHistory.length > ABRController.BANDWIDTH_HISTORY_SIZE) {\n this.bandwidthHistory.shift();\n }\n\n // Use smoothed bandwidth (average of history)\n const smoothedBandwidth = this.getSmoothedBandwidth();\n if (smoothedBandwidth <= 0) return;\n\n const qualities = this.config.getQualities();\n const currentQuality = this.config.getCurrentQuality?.();\n if (!currentQuality || qualities.length === 0) return;\n\n const currentBitrate = currentQuality.bitrate || 0;\n\n // Track current quality bitrate for hysteresis\n if (this.currentQualityBitrate !== currentBitrate) {\n this.currentQualityBitrate = currentBitrate;\n }\n\n // D3: Check for downgrade (immediate, no cooldown)\n if (now - this.lastDowngradeTime >= ABRController.DOWNGRADE_COOLDOWN_MS) {\n if (smoothedBandwidth < currentBitrate * ABRController.DOWNGRADE_THRESHOLD) {\n const lowerQuality = this.findLowerQuality(qualities, currentQuality);\n if (lowerQuality) {\n this.log(\n `ABR: bandwidth ${Math.round(smoothedBandwidth / 1000)}kbps < ${Math.round((currentBitrate * ABRController.DOWNGRADE_THRESHOLD) / 1000)}kbps threshold -> downgrading to ${lowerQuality.label}`\n );\n this.lastDecision = \"downgrade\";\n this.lastDecisionTime = now;\n this.lastDowngradeTime = now;\n this.selectQuality(lowerQuality.id);\n return;\n }\n }\n }\n\n // D3: Check for upgrade (5s cooldown required)\n if (now - this.lastUpgradeTime >= ABRController.UPGRADE_COOLDOWN_MS) {\n const higherQuality = this.findHigherQuality(qualities, currentQuality);\n if (higherQuality && this.isWithinConstraints(higherQuality)) {\n const targetBitrate = higherQuality.bitrate || 0;\n\n // D2: Hysteresis - require 1.5x headroom to upgrade\n // Once at a quality level, stay until bandwidth drops below 1.2x (not 1.0x)\n const shouldUpgrade = smoothedBandwidth >= targetBitrate * ABRController.UPGRADE_HEADROOM;\n const _canHoldHigher =\n smoothedBandwidth >= targetBitrate * ABRController.UPGRADE_HOLD_THRESHOLD;\n\n if (shouldUpgrade) {\n this.log(\n `ABR: bandwidth ${Math.round(smoothedBandwidth / 1000)}kbps >= ${Math.round((targetBitrate * ABRController.UPGRADE_HEADROOM) / 1000)}kbps headroom -> upgrading to ${higherQuality.label}`\n );\n this.lastDecision = \"upgrade\";\n this.lastDecisionTime = now;\n this.lastUpgradeTime = now;\n this.selectQuality(higherQuality.id);\n return;\n }\n }\n }\n }\n\n /**\n * Get bandwidth estimate from player stats\n */\n private async getBandwidthEstimate(): Promise<number> {\n // Try to get bandwidth from player stats\n if (this.config.getBandwidthEstimate) {\n const estimate = await this.config.getBandwidthEstimate();\n if (estimate > 0) return estimate;\n }\n\n // Fallback: estimate from buffer growth (rough)\n const video = this.videoElement;\n if (!video || video.buffered.length === 0) return 0;\n\n // This is a rough fallback - prefer player-specific estimates\n return 0;\n }\n\n /**\n * Get smoothed bandwidth from history\n */\n private getSmoothedBandwidth(): number {\n if (this.bandwidthHistory.length === 0) return 0;\n if (this.bandwidthHistory.length < 3) {\n // Need at least 3 samples for reliable estimate\n return 0;\n }\n const sum = this.bandwidthHistory.reduce((a, b) => a + b, 0);\n return sum / this.bandwidthHistory.length;\n }\n\n /**\n * Get current bandwidth estimate (for external use)\n */\n getCurrentBandwidth(): number {\n return this.getSmoothedBandwidth();\n }\n\n /**\n * Setup resize observer for viewport-based quality selection\n */\n private setupResizeObserver(): void {\n const video = this.videoElement;\n if (!video) return;\n\n this.resizeObserver = new ResizeObserver((entries) => {\n for (const entry of entries) {\n const { width, height } = entry.contentRect;\n this.handleResize(width, height);\n }\n });\n\n // Observe the video element's container\n const container = video.parentElement;\n if (container) {\n this.resizeObserver.observe(container);\n }\n\n // Initial resize handling\n const rect = video.getBoundingClientRect();\n this.handleResize(rect.width, rect.height);\n }\n\n /**\n * Handle viewport resize (ABR_resize mode)\n */\n private handleResize(width: number, height: number): void {\n if (this.options.mode !== \"resize\" && this.options.mode !== \"auto\") {\n return;\n }\n\n const qualities = this.config.getQualities();\n if (qualities.length === 0) return;\n\n // Find best quality for viewport size\n const targetWidth = Math.min(width * window.devicePixelRatio, this.options.maxResolution.width);\n const targetHeight = Math.min(\n height * window.devicePixelRatio,\n this.options.maxResolution.height\n );\n\n const bestQuality = this.findBestQualityForResolution(qualities, targetWidth, targetHeight);\n\n if (bestQuality && bestQuality.id !== this.currentQualityId) {\n this.log(`Resize ABR: ${width}x${height} -> selecting ${bestQuality.label}`);\n this.selectQuality(bestQuality.id);\n }\n }\n\n /**\n * Handle quality degradation (ABR_bitrate mode)\n *\n * Called by QualityMonitor when playback quality drops\n */\n handleQualityDegraded(quality: PlaybackQuality): void {\n if (this.options.mode !== \"bitrate\" && this.options.mode !== \"auto\") {\n return;\n }\n\n // D3: Downgrade should be fast (0ms default), but still respects configured cooldown\n const now = Date.now();\n if (now - this.lastDowngradeTime < ABRController.DOWNGRADE_COOLDOWN_MS) {\n return;\n }\n\n if (quality.score < this.options.downgradeThreshold) {\n const qualities = this.config.getQualities();\n const currentQuality = this.config.getCurrentQuality?.();\n\n if (currentQuality) {\n // Find a lower quality level\n const lowerQuality = this.findLowerQuality(qualities, currentQuality);\n\n if (lowerQuality) {\n this.log(`Bitrate ABR: score ${quality.score} -> downgrading to ${lowerQuality.label}`);\n this.lastDecision = \"downgrade\";\n this.lastDecisionTime = now;\n this.lastDowngradeTime = now;\n this.selectQuality(lowerQuality.id);\n }\n }\n }\n }\n\n /**\n * Handle quality improvement opportunity\n *\n * Called when conditions are good enough to try higher quality\n */\n handleQualityImproved(quality: PlaybackQuality): void {\n if (this.options.mode !== \"bitrate\" && this.options.mode !== \"auto\") {\n return;\n }\n\n // D3: Upgrade requires sustained stability (5s default) to prevent flapping\n const now = Date.now();\n if (now - this.lastUpgradeTime < ABRController.UPGRADE_COOLDOWN_MS) {\n return;\n }\n\n // Only upgrade if buffer is healthy and quality is good\n if (quality.score >= 90 && quality.bufferedAhead >= this.options.minBufferForUpgrade) {\n const qualities = this.config.getQualities();\n const currentQuality = this.config.getCurrentQuality?.();\n\n if (currentQuality) {\n // Find a higher quality level\n const higherQuality = this.findHigherQuality(qualities, currentQuality);\n\n if (higherQuality && this.isWithinConstraints(higherQuality)) {\n this.log(`Bitrate ABR: score ${quality.score} -> upgrading to ${higherQuality.label}`);\n this.lastDecision = \"upgrade\";\n this.lastDecisionTime = now;\n this.lastUpgradeTime = now;\n this.selectQuality(higherQuality.id);\n }\n }\n }\n }\n\n /**\n * Find best quality level for given resolution\n */\n private findBestQualityForResolution(\n qualities: QualityLevel[],\n targetWidth: number,\n targetHeight: number\n ): QualityLevel | null {\n // Filter out qualities that exceed constraints\n const validQualities = qualities.filter((q) => this.isWithinConstraints(q));\n\n if (validQualities.length === 0) return null;\n\n // Sort by resolution (ascending)\n const sorted = [...validQualities].sort((a, b) => {\n const aPixels = (a.width ?? 0) * (a.height ?? 0);\n const bPixels = (b.width ?? 0) * (b.height ?? 0);\n return aPixels - bPixels;\n });\n\n // Find smallest quality that is >= target resolution\n for (const q of sorted) {\n const qWidth = q.width ?? 0;\n const qHeight = q.height ?? 0;\n\n if (qWidth >= targetWidth && qHeight >= targetHeight) {\n return q;\n }\n }\n\n // If no quality is large enough, return the highest available\n return sorted[sorted.length - 1];\n }\n\n /**\n * Find a lower quality level\n */\n private findLowerQuality(qualities: QualityLevel[], current: QualityLevel): QualityLevel | null {\n const currentBitrate = current.bitrate ?? 0;\n\n // Sort by bitrate descending\n const sorted = [...qualities].sort((a, b) => (b.bitrate ?? 0) - (a.bitrate ?? 0));\n\n // Find next lower bitrate\n for (const q of sorted) {\n if ((q.bitrate ?? 0) < currentBitrate) {\n return q;\n }\n }\n\n return null;\n }\n\n /**\n * Find a higher quality level\n */\n private findHigherQuality(qualities: QualityLevel[], current: QualityLevel): QualityLevel | null {\n const currentBitrate = current.bitrate ?? 0;\n\n // Sort by bitrate ascending\n const sorted = [...qualities].sort((a, b) => (a.bitrate ?? 0) - (b.bitrate ?? 0));\n\n // Find next higher bitrate\n for (const q of sorted) {\n if ((q.bitrate ?? 0) > currentBitrate) {\n return q;\n }\n }\n\n return null;\n }\n\n /**\n * Check if quality is within configured constraints\n */\n private isWithinConstraints(quality: QualityLevel): boolean {\n const { maxResolution, maxBitrate } = this.options;\n\n if (quality.width && quality.width > maxResolution.width) return false;\n if (quality.height && quality.height > maxResolution.height) return false;\n if (quality.bitrate && quality.bitrate > maxBitrate) return false;\n\n return true;\n }\n\n /**\n * Select a quality level\n */\n private selectQuality(id: string | \"auto\"): void {\n this.currentQualityId = id;\n this.config.selectQuality(id);\n\n // Notify callbacks\n const qualities = this.config.getQualities();\n const selected = qualities.find((q) => q.id === id);\n if (selected) {\n this.qualityChangeCallbacks.forEach((cb) => cb(selected));\n }\n }\n\n /**\n * Register callback for quality changes\n */\n onQualityChange(callback: (level: QualityLevel) => void): () => void {\n this.qualityChangeCallbacks.push(callback);\n return () => {\n const idx = this.qualityChangeCallbacks.indexOf(callback);\n if (idx >= 0) {\n this.qualityChangeCallbacks.splice(idx, 1);\n }\n };\n }\n\n /**\n * Manually set quality (switches to manual mode temporarily)\n */\n setQuality(id: string | \"auto\"): void {\n this.selectQuality(id);\n }\n\n /**\n * Get current ABR mode\n */\n getMode(): ABRMode {\n return this.options.mode;\n }\n\n /**\n * Set ABR mode at runtime.\n * Restarts monitoring if video element is attached.\n */\n setMode(mode: ABRMode): void {\n if (this.options.mode === mode) return;\n\n this.options.mode = mode;\n this.log(`Mode changed to: ${mode}`);\n\n // Restart with new mode if we have a video element\n if (this.videoElement) {\n const video = this.videoElement;\n this.stop();\n this.start(video);\n }\n }\n\n /**\n * Update ABR options\n */\n updateOptions(options: Partial<ABROptions>): void {\n this.options = { ...this.options, ...options };\n }\n\n /**\n * Get last ABR decision\n */\n getLastDecision(): ABRDecision {\n return this.lastDecision;\n }\n\n /**\n * Debug logging\n */\n private log(message: string): void {\n if (this.debug) {\n console.debug(`[ABRController] ${message}`);\n }\n }\n}\n\nexport default ABRController;\n"],"names":[],"mappings":"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"}