@editframe/elements 0.24.1-beta.0 → 0.25.0-beta.0

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 (335) hide show
  1. package/dist/DelayedLoadingState.js +31 -0
  2. package/dist/DelayedLoadingState.js.map +1 -0
  3. package/dist/EF_FRAMEGEN.d.ts +50 -46
  4. package/dist/EF_FRAMEGEN.js +5 -1
  5. package/dist/EF_FRAMEGEN.js.map +1 -0
  6. package/dist/EF_INTERACTIVE.js +4 -0
  7. package/dist/EF_INTERACTIVE.js.map +1 -0
  8. package/dist/EF_RENDERING.js +4 -0
  9. package/dist/EF_RENDERING.js.map +1 -0
  10. package/dist/_virtual/_@oxc-project_runtime@0.94.0/helpers/decorate.js +4 -1
  11. package/dist/attachContextRoot.js +6 -1
  12. package/dist/attachContextRoot.js.map +1 -0
  13. package/dist/elements/CrossUpdateController.js +4 -0
  14. package/dist/elements/CrossUpdateController.js.map +1 -0
  15. package/dist/elements/EFAudio.d.ts +24 -16
  16. package/dist/elements/EFAudio.js +10 -1
  17. package/dist/elements/EFAudio.js.map +1 -0
  18. package/dist/elements/EFCaptions.d.ts +118 -109
  19. package/dist/elements/EFCaptions.js +11 -6
  20. package/dist/elements/EFCaptions.js.map +1 -0
  21. package/dist/elements/EFImage.d.ts +31 -20
  22. package/dist/elements/EFImage.js +6 -1
  23. package/dist/elements/EFImage.js.map +1 -0
  24. package/dist/elements/EFMedia/AssetIdMediaEngine.js +5 -0
  25. package/dist/elements/EFMedia/AssetIdMediaEngine.js.map +1 -0
  26. package/dist/elements/EFMedia/AssetMediaEngine.js +12 -0
  27. package/dist/elements/EFMedia/AssetMediaEngine.js.map +1 -0
  28. package/dist/elements/EFMedia/BaseMediaEngine.js +53 -0
  29. package/dist/elements/EFMedia/BaseMediaEngine.js.map +1 -0
  30. package/dist/elements/EFMedia/BufferedSeekingInput.d.ts +47 -46
  31. package/dist/elements/EFMedia/BufferedSeekingInput.js +6 -1
  32. package/dist/elements/EFMedia/BufferedSeekingInput.js.map +1 -0
  33. package/dist/elements/EFMedia/JitMediaEngine.js +12 -0
  34. package/dist/elements/EFMedia/JitMediaEngine.js.map +1 -0
  35. package/dist/elements/EFMedia/audioTasks/makeAudioBufferTask.d.ts +9 -13
  36. package/dist/elements/EFMedia/audioTasks/makeAudioBufferTask.js +5 -0
  37. package/dist/elements/EFMedia/audioTasks/makeAudioBufferTask.js.map +1 -0
  38. package/dist/elements/EFMedia/audioTasks/makeAudioFrequencyAnalysisTask.js +6 -1
  39. package/dist/elements/EFMedia/audioTasks/makeAudioFrequencyAnalysisTask.js.map +1 -0
  40. package/dist/elements/EFMedia/audioTasks/makeAudioInitSegmentFetchTask.js +5 -0
  41. package/dist/elements/EFMedia/audioTasks/makeAudioInitSegmentFetchTask.js.map +1 -0
  42. package/dist/elements/EFMedia/audioTasks/makeAudioInputTask.js +5 -0
  43. package/dist/elements/EFMedia/audioTasks/makeAudioInputTask.js.map +1 -0
  44. package/dist/elements/EFMedia/audioTasks/makeAudioSeekTask.js +5 -0
  45. package/dist/elements/EFMedia/audioTasks/makeAudioSeekTask.js.map +1 -0
  46. package/dist/elements/EFMedia/audioTasks/makeAudioSegmentFetchTask.js +5 -0
  47. package/dist/elements/EFMedia/audioTasks/makeAudioSegmentFetchTask.js.map +1 -0
  48. package/dist/elements/EFMedia/audioTasks/makeAudioSegmentIdTask.js +5 -0
  49. package/dist/elements/EFMedia/audioTasks/makeAudioSegmentIdTask.js.map +1 -0
  50. package/dist/elements/EFMedia/audioTasks/makeAudioTimeDomainAnalysisTask.js +6 -1
  51. package/dist/elements/EFMedia/audioTasks/makeAudioTimeDomainAnalysisTask.js.map +1 -0
  52. package/dist/elements/EFMedia/shared/AudioSpanUtils.js +18 -2
  53. package/dist/elements/EFMedia/shared/AudioSpanUtils.js.map +1 -0
  54. package/dist/elements/EFMedia/shared/BufferUtils.d.ts +9 -67
  55. package/dist/elements/EFMedia/shared/BufferUtils.js +15 -0
  56. package/dist/elements/EFMedia/shared/BufferUtils.js.map +1 -0
  57. package/dist/elements/EFMedia/shared/GlobalInputCache.js +29 -0
  58. package/dist/elements/EFMedia/shared/GlobalInputCache.js.map +1 -0
  59. package/dist/elements/EFMedia/shared/MediaTaskUtils.d.ts +11 -17
  60. package/dist/elements/EFMedia/shared/PrecisionUtils.js +25 -0
  61. package/dist/elements/EFMedia/shared/PrecisionUtils.js.map +1 -0
  62. package/dist/elements/EFMedia/shared/ThumbnailExtractor.js +22 -0
  63. package/dist/elements/EFMedia/shared/ThumbnailExtractor.js.map +1 -0
  64. package/dist/elements/EFMedia/tasks/makeMediaEngineTask.js +13 -0
  65. package/dist/elements/EFMedia/tasks/makeMediaEngineTask.js.map +1 -0
  66. package/dist/elements/EFMedia/videoTasks/MainVideoInputCache.js +21 -0
  67. package/dist/elements/EFMedia/videoTasks/MainVideoInputCache.js.map +1 -0
  68. package/dist/elements/EFMedia/videoTasks/ScrubInputCache.js +18 -0
  69. package/dist/elements/EFMedia/videoTasks/ScrubInputCache.js.map +1 -0
  70. package/dist/elements/EFMedia/videoTasks/makeScrubVideoBufferTask.js +10 -0
  71. package/dist/elements/EFMedia/videoTasks/makeScrubVideoBufferTask.js.map +1 -0
  72. package/dist/elements/EFMedia/videoTasks/makeScrubVideoInitSegmentFetchTask.js +5 -0
  73. package/dist/elements/EFMedia/videoTasks/makeScrubVideoInitSegmentFetchTask.js.map +1 -0
  74. package/dist/elements/EFMedia/videoTasks/makeScrubVideoInputTask.js +5 -0
  75. package/dist/elements/EFMedia/videoTasks/makeScrubVideoInputTask.js.map +1 -0
  76. package/dist/elements/EFMedia/videoTasks/makeScrubVideoSeekTask.js +6 -1
  77. package/dist/elements/EFMedia/videoTasks/makeScrubVideoSeekTask.js.map +1 -0
  78. package/dist/elements/EFMedia/videoTasks/makeScrubVideoSegmentFetchTask.js +5 -0
  79. package/dist/elements/EFMedia/videoTasks/makeScrubVideoSegmentFetchTask.js.map +1 -0
  80. package/dist/elements/EFMedia/videoTasks/makeScrubVideoSegmentIdTask.js +5 -0
  81. package/dist/elements/EFMedia/videoTasks/makeScrubVideoSegmentIdTask.js.map +1 -0
  82. package/dist/elements/EFMedia/videoTasks/makeUnifiedVideoSeekTask.js +16 -2
  83. package/dist/elements/EFMedia/videoTasks/makeUnifiedVideoSeekTask.js.map +1 -0
  84. package/dist/elements/EFMedia/videoTasks/makeVideoBufferTask.d.ts +9 -13
  85. package/dist/elements/EFMedia/videoTasks/makeVideoBufferTask.js +5 -0
  86. package/dist/elements/EFMedia/videoTasks/makeVideoBufferTask.js.map +1 -0
  87. package/dist/elements/EFMedia.d.ts +115 -104
  88. package/dist/elements/EFMedia.js +25 -1
  89. package/dist/elements/EFMedia.js.map +1 -0
  90. package/dist/elements/EFSourceMixin.d.ts +10 -11
  91. package/dist/elements/EFSourceMixin.js +5 -0
  92. package/dist/elements/EFSourceMixin.js.map +1 -0
  93. package/dist/elements/EFSurface.d.ts +35 -27
  94. package/dist/elements/EFSurface.js +6 -1
  95. package/dist/elements/EFSurface.js.map +1 -0
  96. package/dist/elements/EFTemporal.d.ts +200 -213
  97. package/dist/elements/EFTemporal.js +24 -4
  98. package/dist/elements/EFTemporal.js.map +1 -0
  99. package/dist/elements/EFThumbnailStrip.d.ts +91 -83
  100. package/dist/elements/EFThumbnailStrip.js +49 -4
  101. package/dist/elements/EFThumbnailStrip.js.map +1 -0
  102. package/dist/elements/EFTimegroup.d.ts +107 -101
  103. package/dist/elements/EFTimegroup.js +58 -3
  104. package/dist/elements/EFTimegroup.js.map +1 -0
  105. package/dist/elements/EFVideo.d.ts +120 -108
  106. package/dist/elements/EFVideo.js +46 -2
  107. package/dist/elements/EFVideo.js.map +1 -0
  108. package/dist/elements/EFWaveform.d.ts +48 -41
  109. package/dist/elements/EFWaveform.js +6 -1
  110. package/dist/elements/EFWaveform.js.map +1 -0
  111. package/dist/elements/FetchMixin.d.ts +8 -6
  112. package/dist/elements/FetchMixin.js +4 -0
  113. package/dist/elements/FetchMixin.js.map +1 -0
  114. package/dist/elements/SampleBuffer.d.ts +18 -13
  115. package/dist/elements/SampleBuffer.js +5 -0
  116. package/dist/elements/SampleBuffer.js.map +1 -0
  117. package/dist/elements/TargetController.d.ts +23 -24
  118. package/dist/elements/TargetController.js +8 -3
  119. package/dist/elements/TargetController.js.map +1 -0
  120. package/dist/elements/TimegroupController.d.ts +17 -12
  121. package/dist/elements/TimegroupController.js +4 -0
  122. package/dist/elements/TimegroupController.js.map +1 -0
  123. package/dist/elements/durationConverter.js +9 -4
  124. package/dist/elements/durationConverter.js.map +1 -0
  125. package/dist/elements/parseTimeToMs.js +4 -0
  126. package/dist/elements/parseTimeToMs.js.map +1 -0
  127. package/dist/elements/renderTemporalAudio.js +4 -0
  128. package/dist/elements/renderTemporalAudio.js.map +1 -0
  129. package/dist/elements/updateAnimations.js +29 -8
  130. package/dist/elements/updateAnimations.js.map +1 -0
  131. package/dist/elements-ZhsB7B5N.css +9 -0
  132. package/dist/elements-ZhsB7B5N.css.map +1 -0
  133. package/dist/getRenderInfo.d.ts +53 -47
  134. package/dist/getRenderInfo.js +5 -0
  135. package/dist/getRenderInfo.js.map +1 -0
  136. package/dist/gui/ContextMixin.d.ts +19 -20
  137. package/dist/gui/ContextMixin.js +32 -1
  138. package/dist/gui/ContextMixin.js.map +1 -0
  139. package/dist/gui/Controllable.d.ts +13 -14
  140. package/dist/gui/Controllable.js +5 -0
  141. package/dist/gui/Controllable.js.map +1 -0
  142. package/dist/gui/EFConfiguration.d.ts +18 -14
  143. package/dist/gui/EFConfiguration.js +6 -1
  144. package/dist/gui/EFConfiguration.js.map +1 -0
  145. package/dist/gui/EFControls.d.ts +35 -31
  146. package/dist/gui/EFControls.js +8 -3
  147. package/dist/gui/EFControls.js.map +1 -0
  148. package/dist/gui/EFDial.d.ts +23 -16
  149. package/dist/gui/EFDial.js +6 -1
  150. package/dist/gui/EFDial.js.map +1 -0
  151. package/dist/gui/EFFilmstrip.d.ts +183 -177
  152. package/dist/gui/EFFilmstrip.js +30 -25
  153. package/dist/gui/EFFilmstrip.js.map +1 -0
  154. package/dist/gui/EFFitScale.d.ts +30 -24
  155. package/dist/gui/EFFitScale.js +6 -1
  156. package/dist/gui/EFFitScale.js.map +1 -0
  157. package/dist/gui/EFFocusOverlay.d.ts +22 -14
  158. package/dist/gui/EFFocusOverlay.js +6 -1
  159. package/dist/gui/EFFocusOverlay.js.map +1 -0
  160. package/dist/gui/EFPause.d.ts +24 -18
  161. package/dist/gui/EFPause.js +6 -1
  162. package/dist/gui/EFPause.js.map +1 -0
  163. package/dist/gui/EFPlay.d.ts +24 -18
  164. package/dist/gui/EFPlay.js +6 -1
  165. package/dist/gui/EFPlay.js.map +1 -0
  166. package/dist/gui/EFPreview.d.ts +22 -15
  167. package/dist/gui/EFPreview.js +9 -1
  168. package/dist/gui/EFPreview.js.map +1 -0
  169. package/dist/gui/EFResizableBox.d.ts +39 -32
  170. package/dist/gui/EFResizableBox.js +8 -3
  171. package/dist/gui/EFResizableBox.js.map +1 -0
  172. package/dist/gui/EFScrubber.d.ts +31 -25
  173. package/dist/gui/EFScrubber.js +6 -1
  174. package/dist/gui/EFScrubber.js.map +1 -0
  175. package/dist/gui/EFTimeDisplay.d.ts +21 -14
  176. package/dist/gui/EFTimeDisplay.js +6 -1
  177. package/dist/gui/EFTimeDisplay.js.map +1 -0
  178. package/dist/gui/EFToggleLoop.d.ts +19 -13
  179. package/dist/gui/EFToggleLoop.js +6 -1
  180. package/dist/gui/EFToggleLoop.js.map +1 -0
  181. package/dist/gui/EFTogglePlay.d.ts +23 -17
  182. package/dist/gui/EFTogglePlay.js +6 -1
  183. package/dist/gui/EFTogglePlay.js.map +1 -0
  184. package/dist/gui/EFWorkbench.d.ts +24 -16
  185. package/dist/gui/EFWorkbench.js +6 -1
  186. package/dist/gui/EFWorkbench.js.map +1 -0
  187. package/dist/gui/PlaybackController.d.ts +54 -50
  188. package/dist/gui/PlaybackController.js +18 -0
  189. package/dist/gui/PlaybackController.js.map +1 -0
  190. package/dist/gui/TWMixin.js +5 -1
  191. package/dist/gui/TWMixin.js.map +1 -0
  192. package/dist/gui/TWMixin2.js +6 -1
  193. package/dist/gui/TWMixin2.js.map +1 -0
  194. package/dist/gui/TargetOrContextMixin.js +5 -0
  195. package/dist/gui/TargetOrContextMixin.js.map +1 -0
  196. package/dist/gui/currentTimeContext.js +5 -0
  197. package/dist/gui/currentTimeContext.js.map +1 -0
  198. package/dist/gui/durationContext.js +5 -0
  199. package/dist/gui/durationContext.js.map +1 -0
  200. package/dist/gui/efContext.js +5 -0
  201. package/dist/gui/efContext.js.map +1 -0
  202. package/dist/gui/fetchContext.js +5 -0
  203. package/dist/gui/fetchContext.js.map +1 -0
  204. package/dist/gui/focusContext.d.ts +6 -5
  205. package/dist/gui/focusContext.js +5 -0
  206. package/dist/gui/focusContext.js.map +1 -0
  207. package/dist/gui/focusedElementContext.js +5 -0
  208. package/dist/gui/focusedElementContext.js.map +1 -0
  209. package/dist/gui/playingContext.js +5 -0
  210. package/dist/gui/playingContext.js.map +1 -0
  211. package/dist/index.d.ts +27 -26
  212. package/dist/index.js +6 -1
  213. package/dist/index.js.map +1 -0
  214. package/dist/msToTimeCode.js +4 -0
  215. package/dist/msToTimeCode.js.map +1 -0
  216. package/dist/otel/BridgeSpanExporter.js +5 -0
  217. package/dist/otel/BridgeSpanExporter.js.map +1 -0
  218. package/dist/otel/setupBrowserTracing.js +7 -2
  219. package/dist/otel/setupBrowserTracing.js.map +1 -0
  220. package/dist/otel/tracingHelpers.d.ts +7 -34
  221. package/dist/otel/tracingHelpers.js +34 -2
  222. package/dist/otel/tracingHelpers.js.map +1 -0
  223. package/dist/transcoding/cache/RequestDeduplicator.js +25 -0
  224. package/dist/transcoding/cache/RequestDeduplicator.js.map +1 -0
  225. package/dist/transcoding/cache/URLTokenDeduplicator.js +23 -0
  226. package/dist/transcoding/cache/URLTokenDeduplicator.js.map +1 -0
  227. package/dist/transcoding/types/index.d.ts +96 -270
  228. package/dist/transcoding/utils/UrlGenerator.d.ts +30 -25
  229. package/dist/transcoding/utils/UrlGenerator.js +19 -0
  230. package/dist/transcoding/utils/UrlGenerator.js.map +1 -0
  231. package/dist/utils/LRUCache.js +44 -0
  232. package/dist/utils/LRUCache.js.map +1 -0
  233. package/package.json +11 -24
  234. package/tsdown.config.ts +36 -0
  235. package/dist/DelayedLoadingState.d.ts +0 -48
  236. package/dist/DelayedLoadingState.integration.test.d.ts +0 -1
  237. package/dist/DelayedLoadingState.test.d.ts +0 -1
  238. package/dist/EF_INTERACTIVE.d.ts +0 -1
  239. package/dist/EF_RENDERING.d.ts +0 -1
  240. package/dist/LoadingDebounce.test.d.ts +0 -1
  241. package/dist/ManualScrubTest.test.d.ts +0 -1
  242. package/dist/ScrubResolvedFlashing.test.d.ts +0 -1
  243. package/dist/ScrubTrackManager.test.d.ts +0 -1
  244. package/dist/VideoSeekFlashing.browsertest.d.ts +0 -0
  245. package/dist/VideoStuckDiagnostic.test.d.ts +0 -1
  246. package/dist/attachContextRoot.d.ts +0 -1
  247. package/dist/elements/ContextProxiesController.d.ts +0 -39
  248. package/dist/elements/CrossUpdateController.d.ts +0 -8
  249. package/dist/elements/EFAudio.browsertest.d.ts +0 -0
  250. package/dist/elements/EFCaptions.browsertest.d.ts +0 -0
  251. package/dist/elements/EFImage.browsertest.d.ts +0 -0
  252. package/dist/elements/EFMedia/AssetIdMediaEngine.d.ts +0 -19
  253. package/dist/elements/EFMedia/AssetIdMediaEngine.test.d.ts +0 -1
  254. package/dist/elements/EFMedia/AssetMediaEngine.browsertest.d.ts +0 -0
  255. package/dist/elements/EFMedia/AssetMediaEngine.d.ts +0 -56
  256. package/dist/elements/EFMedia/BaseMediaEngine.browsertest.d.ts +0 -1
  257. package/dist/elements/EFMedia/BaseMediaEngine.d.ts +0 -103
  258. package/dist/elements/EFMedia/BufferedSeekingInput.browsertest.d.ts +0 -1
  259. package/dist/elements/EFMedia/JitMediaEngine.browsertest.d.ts +0 -0
  260. package/dist/elements/EFMedia/JitMediaEngine.d.ts +0 -46
  261. package/dist/elements/EFMedia/audioTasks/makeAudioBufferTask.browsertest.d.ts +0 -9
  262. package/dist/elements/EFMedia/audioTasks/makeAudioFrequencyAnalysisTask.d.ts +0 -3
  263. package/dist/elements/EFMedia/audioTasks/makeAudioInitSegmentFetchTask.browsertest.d.ts +0 -9
  264. package/dist/elements/EFMedia/audioTasks/makeAudioInitSegmentFetchTask.d.ts +0 -4
  265. package/dist/elements/EFMedia/audioTasks/makeAudioInputTask.browsertest.d.ts +0 -9
  266. package/dist/elements/EFMedia/audioTasks/makeAudioInputTask.d.ts +0 -3
  267. package/dist/elements/EFMedia/audioTasks/makeAudioSeekTask.chunkboundary.regression.browsertest.d.ts +0 -0
  268. package/dist/elements/EFMedia/audioTasks/makeAudioSeekTask.d.ts +0 -7
  269. package/dist/elements/EFMedia/audioTasks/makeAudioSegmentFetchTask.d.ts +0 -4
  270. package/dist/elements/EFMedia/audioTasks/makeAudioSegmentIdTask.d.ts +0 -4
  271. package/dist/elements/EFMedia/audioTasks/makeAudioTasksVideoOnly.browsertest.d.ts +0 -1
  272. package/dist/elements/EFMedia/audioTasks/makeAudioTimeDomainAnalysisTask.d.ts +0 -3
  273. package/dist/elements/EFMedia/shared/AudioSpanUtils.d.ts +0 -7
  274. package/dist/elements/EFMedia/shared/GlobalInputCache.d.ts +0 -39
  275. package/dist/elements/EFMedia/shared/PrecisionUtils.d.ts +0 -28
  276. package/dist/elements/EFMedia/shared/RenditionHelpers.browsertest.d.ts +0 -1
  277. package/dist/elements/EFMedia/shared/RenditionHelpers.d.ts +0 -11
  278. package/dist/elements/EFMedia/shared/ThumbnailExtractor.d.ts +0 -27
  279. package/dist/elements/EFMedia/tasks/makeMediaEngineTask.browsertest.d.ts +0 -9
  280. package/dist/elements/EFMedia/tasks/makeMediaEngineTask.d.ts +0 -17
  281. package/dist/elements/EFMedia/videoTasks/MainVideoInputCache.d.ts +0 -29
  282. package/dist/elements/EFMedia/videoTasks/ScrubInputCache.d.ts +0 -25
  283. package/dist/elements/EFMedia/videoTasks/makeScrubVideoBufferTask.d.ts +0 -8
  284. package/dist/elements/EFMedia/videoTasks/makeScrubVideoInitSegmentFetchTask.d.ts +0 -4
  285. package/dist/elements/EFMedia/videoTasks/makeScrubVideoInputTask.d.ts +0 -3
  286. package/dist/elements/EFMedia/videoTasks/makeScrubVideoSeekTask.d.ts +0 -6
  287. package/dist/elements/EFMedia/videoTasks/makeScrubVideoSegmentFetchTask.d.ts +0 -4
  288. package/dist/elements/EFMedia/videoTasks/makeScrubVideoSegmentIdTask.d.ts +0 -4
  289. package/dist/elements/EFMedia/videoTasks/makeUnifiedVideoSeekTask.d.ts +0 -6
  290. package/dist/elements/EFMedia.browsertest.d.ts +0 -10
  291. package/dist/elements/EFSurface.browsertest.d.ts +0 -0
  292. package/dist/elements/EFTemporal.browsertest.d.ts +0 -22
  293. package/dist/elements/EFThumbnailStrip.browsertest.d.ts +0 -0
  294. package/dist/elements/EFThumbnailStrip.media-engine.browsertest.d.ts +0 -0
  295. package/dist/elements/EFTimegroup.browsertest.d.ts +0 -41
  296. package/dist/elements/EFVideo.browsertest.d.ts +0 -0
  297. package/dist/elements/FetchContext.browsertest.d.ts +0 -0
  298. package/dist/elements/TargetController.browsertest.d.ts +0 -19
  299. package/dist/elements/durationConverter.d.ts +0 -16
  300. package/dist/elements/parseTimeToMs.d.ts +0 -1
  301. package/dist/elements/printTaskStatus.d.ts +0 -2
  302. package/dist/elements/renderTemporalAudio.d.ts +0 -10
  303. package/dist/elements/updateAnimations.browsertest.d.ts +0 -13
  304. package/dist/elements/updateAnimations.d.ts +0 -24
  305. package/dist/elements/util.d.ts +0 -3
  306. package/dist/gui/ContextMixin.browsertest.d.ts +0 -15
  307. package/dist/gui/Controllable.browsertest.d.ts +0 -0
  308. package/dist/gui/EFControls.browsertest.d.ts +0 -11
  309. package/dist/gui/EFDial.browsertest.d.ts +0 -0
  310. package/dist/gui/EFFilmstrip.browsertest.d.ts +0 -11
  311. package/dist/gui/EFPause.browsertest.d.ts +0 -0
  312. package/dist/gui/EFPlay.browsertest.d.ts +0 -0
  313. package/dist/gui/EFResizableBox.browsertest.d.ts +0 -0
  314. package/dist/gui/EFTimeDisplay.browsertest.d.ts +0 -0
  315. package/dist/gui/TWMixin.d.ts +0 -2
  316. package/dist/gui/TargetOrContextMixin.d.ts +0 -10
  317. package/dist/gui/currentTimeContext.d.ts +0 -3
  318. package/dist/gui/durationContext.d.ts +0 -3
  319. package/dist/gui/efContext.d.ts +0 -4
  320. package/dist/gui/fetchContext.d.ts +0 -3
  321. package/dist/gui/focusedElementContext.d.ts +0 -3
  322. package/dist/gui/playingContext.d.ts +0 -6
  323. package/dist/msToTimeCode.d.ts +0 -1
  324. package/dist/otel/BridgeSpanExporter.d.ts +0 -13
  325. package/dist/otel/setupBrowserTracing.d.ts +0 -12
  326. package/dist/style.css +0 -2
  327. package/dist/transcoding/cache/RequestDeduplicator.d.ts +0 -29
  328. package/dist/transcoding/cache/RequestDeduplicator.test.d.ts +0 -1
  329. package/dist/transcoding/cache/URLTokenDeduplicator.d.ts +0 -38
  330. package/dist/transcoding/cache/URLTokenDeduplicator.test.d.ts +0 -1
  331. package/dist/transcoding/utils/MediaUtils.d.ts +0 -9
  332. package/dist/transcoding/utils/constants.d.ts +0 -27
  333. package/dist/utils/LRUCache.d.ts +0 -80
  334. package/dist/utils/LRUCache.test.d.ts +0 -1
  335. /package/dist/{LoadingIndicator.browsertest.d.ts → elements.js} +0 -0
@@ -1,5 +1,7 @@
1
1
  import { getLatestMediaEngine } from "../tasks/makeMediaEngineTask.js";
2
2
  import { Task } from "@lit/task";
3
+
4
+ //#region src/elements/EFMedia/audioTasks/makeAudioSegmentIdTask.ts
3
5
  const makeAudioSegmentIdTask = (host) => {
4
6
  return new Task(host, {
5
7
  args: () => [host.mediaEngineTask.value, host.desiredSeekTimeMs],
@@ -16,4 +18,7 @@ const makeAudioSegmentIdTask = (host) => {
16
18
  }
17
19
  });
18
20
  };
21
+
22
+ //#endregion
19
23
  export { makeAudioSegmentIdTask };
24
+ //# sourceMappingURL=makeAudioSegmentIdTask.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"makeAudioSegmentIdTask.js","names":[],"sources":["../../../../src/elements/EFMedia/audioTasks/makeAudioSegmentIdTask.ts"],"sourcesContent":["import { Task } from \"@lit/task\";\nimport type { MediaEngine } from \"../../../transcoding/types\";\nimport type { EFMedia } from \"../../EFMedia\";\nimport { getLatestMediaEngine } from \"../tasks/makeMediaEngineTask\";\n\nexport const makeAudioSegmentIdTask = (\n host: EFMedia,\n): Task<readonly [MediaEngine | undefined, number], number | undefined> => {\n return new Task(host, {\n args: () => [host.mediaEngineTask.value, host.desiredSeekTimeMs] as const,\n onError: (error) => {\n console.error(\"audioSegmentIdTask error\", error);\n },\n onComplete: (_value) => {},\n task: async ([, targetSeekTimeMs], { signal }) => {\n const mediaEngine = await getLatestMediaEngine(host, signal);\n signal.throwIfAborted();\n\n const audioRendition = mediaEngine.getAudioRendition();\n\n // Return undefined if no audio rendition available (video-only asset)\n if (!audioRendition) {\n return undefined;\n }\n\n return mediaEngine.computeSegmentId(targetSeekTimeMs, audioRendition);\n },\n });\n};\n"],"mappings":";;;;AAKA,MAAa,0BACX,SACyE;AACzE,QAAO,IAAI,KAAK,MAAM;EACpB,YAAY,CAAC,KAAK,gBAAgB,OAAO,KAAK,kBAAkB;EAChE,UAAU,UAAU;AAClB,WAAQ,MAAM,4BAA4B,MAAM;;EAElD,aAAa,WAAW;EACxB,MAAM,OAAO,GAAG,mBAAmB,EAAE,aAAa;GAChD,MAAM,cAAc,MAAM,qBAAqB,MAAM,OAAO;AAC5D,UAAO,gBAAgB;GAEvB,MAAM,iBAAiB,YAAY,mBAAmB;AAGtD,OAAI,CAAC,eACH;AAGF,UAAO,YAAY,iBAAiB,kBAAkB,eAAe;;EAExE,CAAC"}
@@ -2,7 +2,9 @@ import { EF_INTERACTIVE } from "../../../EF_INTERACTIVE.js";
2
2
  import { LRUCache } from "../../../utils/LRUCache.js";
3
3
  import { IgnorableError } from "../../EFMedia.js";
4
4
  import { Task } from "@lit/task";
5
- var DECAY_WEIGHT = .8;
5
+
6
+ //#region src/elements/EFMedia/audioTasks/makeAudioTimeDomainAnalysisTask.ts
7
+ const DECAY_WEIGHT = .8;
6
8
  function makeAudioTimeDomainAnalysisTask(element) {
7
9
  const cache = new LRUCache(1e3);
8
10
  return new Task(element, {
@@ -107,4 +109,7 @@ function makeAudioTimeDomainAnalysisTask(element) {
107
109
  }
108
110
  });
109
111
  }
112
+
113
+ //#endregion
110
114
  export { makeAudioTimeDomainAnalysisTask };
115
+ //# sourceMappingURL=makeAudioTimeDomainAnalysisTask.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"makeAudioTimeDomainAnalysisTask.js","names":["audioContext: OfflineAudioContext"],"sources":["../../../../src/elements/EFMedia/audioTasks/makeAudioTimeDomainAnalysisTask.ts"],"sourcesContent":["import { Task } from \"@lit/task\";\n\nimport { EF_INTERACTIVE } from \"../../../EF_INTERACTIVE.js\";\nimport { LRUCache } from \"../../../utils/LRUCache.js\";\nimport { type EFMedia, IgnorableError } from \"../../EFMedia.js\";\n\n// DECAY_WEIGHT constant - same as original\nconst DECAY_WEIGHT = 0.8;\n\nexport function makeAudioTimeDomainAnalysisTask(element: EFMedia) {\n // Internal cache for this task instance (same as original #byteTimeDomainCache)\n const cache = new LRUCache<string, Uint8Array>(1000);\n\n return new Task(element, {\n autoRun: EF_INTERACTIVE,\n onError: (error) => {\n if (error instanceof IgnorableError) {\n console.info(\"byteTimeDomainTask skipped: no audio track\");\n return;\n }\n console.error(\"byteTimeDomainTask error\", error);\n },\n args: () =>\n [\n element.currentSourceTimeMs,\n element.fftSize,\n element.fftDecay,\n element.fftGain,\n element.shouldInterpolateFrequencies,\n ] as const,\n task: async (_, { signal }) => {\n if (element.currentSourceTimeMs < 0) return null;\n\n const currentTimeMs = element.currentSourceTimeMs;\n\n // Calculate exact audio window needed based on fftDecay and frame timing\n const frameIntervalMs = 1000 / 30; // 33.33ms per frame\n\n // Need audio from earliest frame to current frame\n const earliestFrameMs =\n currentTimeMs - (element.fftDecay - 1) * frameIntervalMs;\n const fromMs = Math.max(0, earliestFrameMs);\n const maxToMs = currentTimeMs + frameIntervalMs; // Include current frame\n const videoDurationMs = element.intrinsicDurationMs || 0;\n const toMs =\n videoDurationMs > 0 ? Math.min(maxToMs, videoDurationMs) : maxToMs;\n\n // If the clamping results in an invalid range (seeking beyond the end), skip analysis silently\n if (fromMs >= toMs) {\n return null;\n }\n\n // Check cache early - before expensive audio fetching\n // Use a preliminary cache key that doesn't depend on actual startOffsetMs from audio span\n const preliminaryCacheKey = `${element.shouldInterpolateFrequencies}:${element.fftSize}:${element.fftDecay}:${element.fftGain}:${fromMs}:${currentTimeMs}`;\n const cachedData = cache.get(preliminaryCacheKey);\n if (cachedData) {\n return cachedData;\n }\n\n const { fetchAudioSpanningTime: fetchAudioSpan } = await import(\n \"../shared/AudioSpanUtils.ts\"\n );\n const audioSpan = await fetchAudioSpan(element, fromMs, toMs, signal);\n\n if (!audioSpan || !audioSpan.blob) {\n console.warn(\"Time domain analysis skipped: no audio data available\");\n return null;\n }\n\n // Decode the real audio data\n const tempAudioContext = new OfflineAudioContext(2, 48000, 48000);\n const arrayBuffer = await audioSpan.blob.arrayBuffer();\n const audioBuffer = await tempAudioContext.decodeAudioData(arrayBuffer);\n\n // Use actual startOffset from audioSpan (relative to requested time)\n const startOffsetMs = audioSpan.startMs;\n\n // Process multiple frames with decay, similar to the reference code\n const framesData = await Promise.all(\n Array.from({ length: element.fftDecay }, async (_, frameIndex) => {\n const frameOffset = frameIndex * (1000 / 30);\n const startTime = Math.max(\n 0,\n (currentTimeMs - frameOffset - startOffsetMs) / 1000,\n );\n\n const cacheKey = `${element.shouldInterpolateFrequencies}:${element.fftSize}:${element.fftGain}:${startOffsetMs}:${startTime}`;\n const cachedFrame = cache.get(cacheKey);\n if (cachedFrame) {\n return cachedFrame;\n }\n\n let audioContext: OfflineAudioContext;\n try {\n audioContext = new OfflineAudioContext(2, 48000 * (1 / 30), 48000);\n } catch (error) {\n throw new Error(\n `[EFMedia.byteTimeDomainTask] Failed to create OfflineAudioContext(2, ${48000 * (1 / 30)}, 48000) for frame ${frameIndex} at time ${startTime}s: ${error instanceof Error ? error.message : String(error)}. This is for audio time domain analysis.`,\n );\n }\n\n const source = audioContext.createBufferSource();\n source.buffer = audioBuffer;\n\n // Create analyzer for PCM data\n const analyser = audioContext.createAnalyser();\n analyser.fftSize = element.fftSize; // Ensure enough samples\n analyser.minDecibels = -90;\n analyser.maxDecibels = -20;\n\n const gainNode = audioContext.createGain();\n gainNode.gain.value = element.fftGain; // Amplify the signal\n\n source.connect(gainNode);\n gainNode.connect(analyser);\n analyser.connect(audioContext.destination);\n\n source.start(0, startTime, 1 / 30);\n\n const dataLength = analyser.fftSize / 2;\n try {\n await audioContext.startRendering();\n const frameData = new Uint8Array(dataLength);\n analyser.getByteTimeDomainData(frameData);\n\n // const points = frameData;\n // Calculate RMS and midpoint values\n const points = new Uint8Array(dataLength);\n for (let i = 0; i < dataLength; i++) {\n const pointSamples = frameData.slice(\n i * (frameData.length / dataLength),\n (i + 1) * (frameData.length / dataLength),\n );\n\n // Calculate RMS while preserving sign\n const rms = Math.sqrt(\n pointSamples.reduce((sum, sample) => {\n const normalized = (sample - 128) / 128;\n return sum + normalized * normalized;\n }, 0) / pointSamples.length,\n );\n\n // Get average sign of the samples to determine direction\n const avgSign = Math.sign(\n pointSamples.reduce((sum, sample) => sum + (sample - 128), 0),\n );\n\n // Convert RMS back to byte range, preserving direction\n points[i] = Math.min(255, Math.round(128 + avgSign * rms * 128));\n }\n\n cache.set(cacheKey, points);\n return points;\n } finally {\n source.disconnect();\n analyser.disconnect();\n }\n }),\n );\n\n // Combine frames with decay weighting\n const frameLength = framesData[0]?.length ?? 0;\n const smoothedData = new Uint8Array(frameLength);\n\n for (let i = 0; i < frameLength; i++) {\n let weightedSum = 0;\n let weightSum = 0;\n\n framesData.forEach((frame: Uint8Array, frameIndex: number) => {\n const decayWeight = DECAY_WEIGHT ** frameIndex;\n weightedSum += (frame[i] ?? 0) * decayWeight;\n weightSum += decayWeight;\n });\n\n smoothedData[i] = Math.min(255, Math.round(weightedSum / weightSum));\n }\n\n // Cache with the preliminary key so future requests can skip audio fetching\n cache.set(preliminaryCacheKey, smoothedData);\n return smoothedData;\n },\n });\n}\n"],"mappings":"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"}
@@ -1,4 +1,9 @@
1
- var fetchAudioSegmentData = async (segmentIds, mediaEngine, signal) => {
1
+ //#region src/elements/EFMedia/shared/AudioSpanUtils.ts
2
+ /**
3
+ * Fetch audio segment data using MediaEngine
4
+ * Pure function with explicit dependencies
5
+ */
6
+ const fetchAudioSegmentData = async (segmentIds, mediaEngine, signal) => {
2
7
  const audioRendition = mediaEngine.audioRendition;
3
8
  if (!audioRendition) throw new Error("Audio rendition not available");
4
9
  const segmentData = /* @__PURE__ */ new Map();
@@ -10,10 +15,18 @@ var fetchAudioSegmentData = async (segmentIds, mediaEngine, signal) => {
10
15
  for (const [segmentId, arrayBuffer] of fetchedSegments) segmentData.set(segmentId, arrayBuffer);
11
16
  return segmentData;
12
17
  };
13
- var createAudioSpanBlob = (initSegment, mediaSegments) => {
18
+ /**
19
+ * Create audio span blob from init segment and media segments
20
+ * Pure function for blob creation
21
+ */
22
+ const createAudioSpanBlob = (initSegment, mediaSegments) => {
14
23
  const chunks = [initSegment, ...mediaSegments];
15
24
  return new Blob(chunks, { type: "audio/mp4" });
16
25
  };
26
+ /**
27
+ * Fetch audio spanning a time range
28
+ * Main function that orchestrates segment calculation, fetching, and blob creation
29
+ */
17
30
  const fetchAudioSpanningTime = async (host, fromMs, toMs, signal) => {
18
31
  if (fromMs >= toMs || fromMs < 0) throw new Error(`Invalid time range: fromMs=${fromMs}, toMs=${toMs}`);
19
32
  const mediaEngine = await host.mediaEngineTask.taskComplete;
@@ -35,4 +48,7 @@ const fetchAudioSpanningTime = async (host, fromMs, toMs, signal) => {
35
48
  blob
36
49
  };
37
50
  };
51
+
52
+ //#endregion
38
53
  export { fetchAudioSpanningTime };
54
+ //# sourceMappingURL=AudioSpanUtils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AudioSpanUtils.js","names":[],"sources":["../../../../src/elements/EFMedia/shared/AudioSpanUtils.ts"],"sourcesContent":["import type {\n AudioSpan,\n MediaEngine,\n SegmentTimeRange,\n} from \"../../../transcoding/types\";\nimport type { EFMedia } from \"../../EFMedia\";\n\n/**\n * Fetch audio segment data using MediaEngine\n * Pure function with explicit dependencies\n */\nconst fetchAudioSegmentData = async (\n segmentIds: number[],\n mediaEngine: MediaEngine,\n signal: AbortSignal,\n): Promise<Map<number, ArrayBuffer>> => {\n const audioRendition = mediaEngine.audioRendition;\n if (!audioRendition) {\n throw new Error(\"Audio rendition not available\");\n }\n\n const segmentData = new Map<number, ArrayBuffer>();\n\n // Fetch all segments - MediaEngine handles deduplication internally\n const fetchPromises = segmentIds.map(async (segmentId) => {\n const arrayBuffer = await mediaEngine.fetchMediaSegment(\n segmentId,\n audioRendition,\n signal,\n );\n return [segmentId, arrayBuffer] as [number, ArrayBuffer];\n });\n\n const fetchedSegments = await Promise.all(fetchPromises);\n signal.throwIfAborted();\n\n for (const [segmentId, arrayBuffer] of fetchedSegments) {\n segmentData.set(segmentId, arrayBuffer);\n }\n\n return segmentData;\n};\n\n/**\n * Create audio span blob from init segment and media segments\n * Pure function for blob creation\n */\nconst createAudioSpanBlob = (\n initSegment: ArrayBuffer,\n mediaSegments: ArrayBuffer[],\n): Blob => {\n const chunks = [initSegment, ...mediaSegments];\n return new Blob(chunks, { type: \"audio/mp4\" });\n};\n\n/**\n * Fetch audio spanning a time range\n * Main function that orchestrates segment calculation, fetching, and blob creation\n */\nexport const fetchAudioSpanningTime = async (\n host: EFMedia,\n fromMs: number,\n toMs: number,\n signal: AbortSignal,\n): Promise<AudioSpan | undefined> => {\n // Validate inputs\n if (fromMs >= toMs || fromMs < 0) {\n throw new Error(`Invalid time range: fromMs=${fromMs}, toMs=${toMs}`);\n }\n\n // Get dependencies from host\n const mediaEngine = await host.mediaEngineTask.taskComplete;\n const initSegment = await host.audioInitSegmentFetchTask.taskComplete;\n\n // Return undefined if no audio rendition available\n if (!mediaEngine?.audioRendition) {\n return undefined;\n }\n\n if (!initSegment) {\n return undefined;\n }\n\n // Calculate segments needed using the media engine's method\n const segmentRanges = mediaEngine.calculateAudioSegmentRange(\n fromMs,\n toMs,\n mediaEngine.audioRendition,\n host.intrinsicDurationMs || 10000,\n );\n\n if (segmentRanges.length === 0) {\n throw new Error(`No segments found for time range ${fromMs}-${toMs}ms`);\n }\n\n // Fetch segment data\n const segmentIds = segmentRanges.map((r: SegmentTimeRange) => r.segmentId);\n const segmentData = await fetchAudioSegmentData(\n segmentIds,\n mediaEngine,\n signal,\n );\n\n // Create ordered array of segments\n const orderedSegments = segmentIds.map((id: number) => {\n const segment = segmentData.get(id);\n if (!segment) {\n throw new Error(`Missing segment data for segment ID ${id}`);\n }\n return segment;\n });\n\n // Create blob\n const blob = createAudioSpanBlob(initSegment, orderedSegments);\n\n // Calculate actual time boundaries\n const actualStartMs = Math.min(\n ...segmentRanges.map((r: SegmentTimeRange) => r.startMs),\n );\n const actualEndMs = Math.max(\n ...segmentRanges.map((r: SegmentTimeRange) => r.endMs),\n );\n\n return {\n startMs: actualStartMs,\n endMs: actualEndMs,\n blob,\n };\n};\n"],"mappings":";;;;;AAWA,MAAM,wBAAwB,OAC5B,YACA,aACA,WACsC;CACtC,MAAM,iBAAiB,YAAY;AACnC,KAAI,CAAC,eACH,OAAM,IAAI,MAAM,gCAAgC;CAGlD,MAAM,8BAAc,IAAI,KAA0B;CAGlD,MAAM,gBAAgB,WAAW,IAAI,OAAO,cAAc;AAMxD,SAAO,CAAC,WALY,MAAM,YAAY,kBACpC,WACA,gBACA,OACD,CAC8B;GAC/B;CAEF,MAAM,kBAAkB,MAAM,QAAQ,IAAI,cAAc;AACxD,QAAO,gBAAgB;AAEvB,MAAK,MAAM,CAAC,WAAW,gBAAgB,gBACrC,aAAY,IAAI,WAAW,YAAY;AAGzC,QAAO;;;;;;AAOT,MAAM,uBACJ,aACA,kBACS;CACT,MAAM,SAAS,CAAC,aAAa,GAAG,cAAc;AAC9C,QAAO,IAAI,KAAK,QAAQ,EAAE,MAAM,aAAa,CAAC;;;;;;AAOhD,MAAa,yBAAyB,OACpC,MACA,QACA,MACA,WACmC;AAEnC,KAAI,UAAU,QAAQ,SAAS,EAC7B,OAAM,IAAI,MAAM,8BAA8B,OAAO,SAAS,OAAO;CAIvE,MAAM,cAAc,MAAM,KAAK,gBAAgB;CAC/C,MAAM,cAAc,MAAM,KAAK,0BAA0B;AAGzD,KAAI,CAAC,aAAa,eAChB;AAGF,KAAI,CAAC,YACH;CAIF,MAAM,gBAAgB,YAAY,2BAChC,QACA,MACA,YAAY,gBACZ,KAAK,uBAAuB,IAC7B;AAED,KAAI,cAAc,WAAW,EAC3B,OAAM,IAAI,MAAM,oCAAoC,OAAO,GAAG,KAAK,IAAI;CAIzE,MAAM,aAAa,cAAc,KAAK,MAAwB,EAAE,UAAU;CAC1E,MAAM,cAAc,MAAM,sBACxB,YACA,aACA,OACD;CAYD,MAAM,OAAO,oBAAoB,aATT,WAAW,KAAK,OAAe;EACrD,MAAM,UAAU,YAAY,IAAI,GAAG;AACnC,MAAI,CAAC,QACH,OAAM,IAAI,MAAM,uCAAuC,KAAK;AAE9D,SAAO;GACP,CAG4D;AAU9D,QAAO;EACL,SARoB,KAAK,IACzB,GAAG,cAAc,KAAK,MAAwB,EAAE,QAAQ,CACzD;EAOC,OANkB,KAAK,IACvB,GAAG,cAAc,KAAK,MAAwB,EAAE,MAAM,CACvD;EAKC;EACD"}
@@ -1,71 +1,13 @@
1
- import { AudioRendition, VideoRendition } from '../../../transcoding/types';
1
+ //#region src/elements/EFMedia/shared/BufferUtils.d.ts
2
2
  /**
3
3
  * State interface for media buffering - orchestration only, no data storage
4
4
  */
5
- export interface MediaBufferState {
6
- currentSeekTimeMs: number;
7
- requestedSegments: Set<number>;
8
- activeRequests: Set<number>;
9
- requestQueue: number[];
5
+ interface MediaBufferState {
6
+ currentSeekTimeMs: number;
7
+ requestedSegments: Set<number>;
8
+ activeRequests: Set<number>;
9
+ requestQueue: number[];
10
10
  }
11
- /**
12
- * Configuration interface for media buffering - generic for both audio and video
13
- */
14
- export interface MediaBufferConfig {
15
- bufferDurationMs: number;
16
- maxParallelFetches: number;
17
- enableBuffering: boolean;
18
- enableContinuousBuffering?: boolean;
19
- }
20
- /**
21
- * Dependencies interface for media buffering - integrates with BaseMediaEngine
22
- */
23
- export interface MediaBufferDependencies<T extends AudioRendition | VideoRendition> {
24
- computeSegmentId: (timeMs: number, rendition: T) => Promise<number | undefined>;
25
- prefetchSegment: (segmentId: number, rendition: T) => Promise<void>;
26
- isSegmentCached: (segmentId: number, rendition: T) => boolean;
27
- getRendition: () => Promise<T | undefined>;
28
- logError: (message: string, error: any) => void;
29
- }
30
- /**
31
- * Compute segment range for a time window
32
- * Pure function - determines which segments are needed for a time range
33
- */
34
- export declare const computeSegmentRange: <T extends AudioRendition | VideoRendition>(startTimeMs: number, endTimeMs: number, rendition: T, computeSegmentId: (timeMs: number, rendition: T) => number | undefined) => number[];
35
- /**
36
- * Async version of computeSegmentRange for when computeSegmentId is async
37
- */
38
- export declare const computeSegmentRangeAsync: <T extends AudioRendition | VideoRendition>(startTimeMs: number, endTimeMs: number, durationMs: number, rendition: T, computeSegmentId: (timeMs: number, rendition: T) => Promise<number | undefined>) => Promise<number[]>;
39
- /**
40
- * Compute buffer queue based on desired segments and what we've already requested
41
- * Pure function - determines what new segments should be prefetched
42
- */
43
- export declare const computeBufferQueue: (desiredSegments: number[], requestedSegments: Set<number>) => number[];
44
- /**
45
- * Handle seek time change and recompute buffer queue
46
- * Pure function - computes new queue when seek time changes
47
- */
48
- export declare const handleSeekTimeChange: <T extends AudioRendition | VideoRendition>(newSeekTimeMs: number, bufferDurationMs: number, rendition: T, currentState: MediaBufferState, computeSegmentId: (timeMs: number, rendition: T) => number | undefined) => {
49
- newQueue: number[];
50
- overlappingRequests: number[];
51
- };
52
- /**
53
- * Check if a segment has been requested for buffering
54
- * Pure function for checking buffer orchestration state
55
- */
56
- export declare const isSegmentRequested: (segmentId: number, bufferState: MediaBufferState | undefined) => boolean;
57
- /**
58
- * Get requested segments from a list of segment IDs
59
- * Pure function that returns which segments have been requested for buffering
60
- */
61
- export declare const getRequestedSegments: (segmentIds: number[], bufferState: MediaBufferState | undefined) => Set<number>;
62
- /**
63
- * Get unrequested segments from a list of segment IDs
64
- * Pure function that returns which segments haven't been requested yet
65
- */
66
- export declare const getUnrequestedSegments: (segmentIds: number[], bufferState: MediaBufferState | undefined) => number[];
67
- /**
68
- * Core media buffering orchestration logic - prefetch only, no data storage
69
- * Integrates with BaseMediaEngine's existing caching and request deduplication
70
- */
71
- export declare const manageMediaBuffer: <T extends AudioRendition | VideoRendition>(seekTimeMs: number, config: MediaBufferConfig, currentState: MediaBufferState, durationMs: number, signal: AbortSignal, deps: MediaBufferDependencies<T>) => Promise<MediaBufferState>;
11
+ //#endregion
12
+ export { MediaBufferState };
13
+ //# sourceMappingURL=BufferUtils.d.ts.map
@@ -1,3 +1,7 @@
1
+ //#region src/elements/EFMedia/shared/BufferUtils.ts
2
+ /**
3
+ * Async version of computeSegmentRange for when computeSegmentId is async
4
+ */
1
5
  const computeSegmentRangeAsync = async (startTimeMs, endTimeMs, durationMs, rendition, computeSegmentId) => {
2
6
  const segments = [];
3
7
  const segmentDurationMs = rendition.segmentDurationMs || 1e3;
@@ -12,9 +16,17 @@ const computeSegmentRangeAsync = async (startTimeMs, endTimeMs, durationMs, rend
12
16
  }
13
17
  return segments.filter((id, index, arr) => arr.indexOf(id) === index);
14
18
  };
19
+ /**
20
+ * Compute buffer queue based on desired segments and what we've already requested
21
+ * Pure function - determines what new segments should be prefetched
22
+ */
15
23
  const computeBufferQueue = (desiredSegments, requestedSegments) => {
16
24
  return desiredSegments.filter((segmentId) => !requestedSegments.has(segmentId));
17
25
  };
26
+ /**
27
+ * Core media buffering orchestration logic - prefetch only, no data storage
28
+ * Integrates with BaseMediaEngine's existing caching and request deduplication
29
+ */
18
30
  const manageMediaBuffer = async (seekTimeMs, config, currentState, durationMs, signal, deps) => {
19
31
  if (!config.enableBuffering) return currentState;
20
32
  const rendition = await deps.getRendition();
@@ -53,4 +65,7 @@ const manageMediaBuffer = async (seekTimeMs, config, currentState, durationMs, s
53
65
  requestQueue: remainingQueue
54
66
  };
55
67
  };
68
+
69
+ //#endregion
56
70
  export { manageMediaBuffer };
71
+ //# sourceMappingURL=BufferUtils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"BufferUtils.js","names":["segments: number[]"],"sources":["../../../../src/elements/EFMedia/shared/BufferUtils.ts"],"sourcesContent":["import type {\n AudioRendition,\n VideoRendition,\n} from \"../../../transcoding/types\";\n\n/**\n * State interface for media buffering - orchestration only, no data storage\n */\nexport interface MediaBufferState {\n currentSeekTimeMs: number;\n requestedSegments: Set<number>; // Segments we've requested for buffering\n activeRequests: Set<number>; // Segments currently being fetched\n requestQueue: number[]; // Segments queued to be requested\n}\n\n/**\n * Configuration interface for media buffering - generic for both audio and video\n */\nexport interface MediaBufferConfig {\n bufferDurationMs: number;\n maxParallelFetches: number;\n enableBuffering: boolean;\n enableContinuousBuffering?: boolean;\n}\n\n/**\n * Dependencies interface for media buffering - integrates with BaseMediaEngine\n */\nexport interface MediaBufferDependencies<\n T extends AudioRendition | VideoRendition,\n> {\n computeSegmentId: (\n timeMs: number,\n rendition: T,\n ) => Promise<number | undefined>;\n prefetchSegment: (segmentId: number, rendition: T) => Promise<void>; // Just trigger prefetch, don't return data\n isSegmentCached: (segmentId: number, rendition: T) => boolean; // Check BaseMediaEngine cache\n getRendition: () => Promise<T | undefined>;\n logError: (message: string, error: any) => void;\n}\n\n/**\n * Compute segment range for a time window\n * Pure function - determines which segments are needed for a time range\n */\nexport const computeSegmentRange = <T extends AudioRendition | VideoRendition>(\n startTimeMs: number,\n endTimeMs: number,\n rendition: T,\n computeSegmentId: (timeMs: number, rendition: T) => number | undefined,\n): number[] => {\n const segments: number[] = [];\n const segmentDurationMs = (rendition as any).segmentDurationMs || 1000;\n\n // Calculate segment indices that overlap with [startTimeMs, endTimeMs]\n const startSegmentIndex = Math.floor(startTimeMs / segmentDurationMs);\n const endSegmentIndex = Math.floor(endTimeMs / segmentDurationMs);\n\n for (let i = startSegmentIndex; i <= endSegmentIndex; i++) {\n const segmentId = computeSegmentId(i * segmentDurationMs, rendition);\n if (segmentId !== undefined) {\n segments.push(segmentId);\n }\n }\n\n return segments.filter((id, index, arr) => arr.indexOf(id) === index); // Remove duplicates\n};\n\n/**\n * Async version of computeSegmentRange for when computeSegmentId is async\n */\nexport const computeSegmentRangeAsync = async <\n T extends AudioRendition | VideoRendition,\n>(\n startTimeMs: number,\n endTimeMs: number,\n durationMs: number,\n rendition: T,\n computeSegmentId: (\n timeMs: number,\n rendition: T,\n ) => Promise<number | undefined>,\n): Promise<number[]> => {\n const segments: number[] = [];\n const segmentDurationMs = (rendition as any).segmentDurationMs || 1000;\n\n // Calculate segment indices that overlap with [startTimeMs, endTimeMs]\n const startSegmentIndex = Math.floor(startTimeMs / segmentDurationMs);\n const endSegmentIndex = Math.floor(\n Math.min(endTimeMs, durationMs) / segmentDurationMs,\n );\n\n for (let i = startSegmentIndex; i <= endSegmentIndex; i++) {\n const timeMs = i * segmentDurationMs;\n if (timeMs < durationMs) {\n const segmentId = await computeSegmentId(timeMs, rendition);\n if (segmentId !== undefined) {\n segments.push(segmentId);\n }\n }\n }\n\n return segments.filter((id, index, arr) => arr.indexOf(id) === index); // Remove duplicates\n};\n\n/**\n * Compute buffer queue based on desired segments and what we've already requested\n * Pure function - determines what new segments should be prefetched\n */\nexport const computeBufferQueue = (\n desiredSegments: number[],\n requestedSegments: Set<number>,\n): number[] => {\n return desiredSegments.filter(\n (segmentId) => !requestedSegments.has(segmentId),\n );\n};\n\n/**\n * Handle seek time change and recompute buffer queue\n * Pure function - computes new queue when seek time changes\n */\nexport const handleSeekTimeChange = <T extends AudioRendition | VideoRendition>(\n newSeekTimeMs: number,\n bufferDurationMs: number,\n rendition: T,\n currentState: MediaBufferState,\n computeSegmentId: (timeMs: number, rendition: T) => number | undefined,\n): { newQueue: number[]; overlappingRequests: number[] } => {\n const endTimeMs = newSeekTimeMs + bufferDurationMs;\n const desiredSegments = computeSegmentRange(\n newSeekTimeMs,\n endTimeMs,\n rendition,\n computeSegmentId,\n );\n\n // Find segments that are already being requested\n const overlappingRequests = desiredSegments.filter((segmentId) =>\n currentState.requestedSegments.has(segmentId),\n );\n\n const newQueue = computeBufferQueue(\n desiredSegments,\n currentState.requestedSegments,\n );\n\n return { newQueue, overlappingRequests };\n};\n\n/**\n * Check if a segment has been requested for buffering\n * Pure function for checking buffer orchestration state\n */\nexport const isSegmentRequested = (\n segmentId: number,\n bufferState: MediaBufferState | undefined,\n): boolean => {\n return bufferState?.requestedSegments.has(segmentId) ?? false;\n};\n\n/**\n * Get requested segments from a list of segment IDs\n * Pure function that returns which segments have been requested for buffering\n */\nexport const getRequestedSegments = (\n segmentIds: number[],\n bufferState: MediaBufferState | undefined,\n): Set<number> => {\n if (!bufferState) {\n return new Set();\n }\n return new Set(\n segmentIds.filter((id) => bufferState.requestedSegments.has(id)),\n );\n};\n\n/**\n * Get unrequested segments from a list of segment IDs\n * Pure function that returns which segments haven't been requested yet\n */\nexport const getUnrequestedSegments = (\n segmentIds: number[],\n bufferState: MediaBufferState | undefined,\n): number[] => {\n if (!bufferState) {\n return segmentIds;\n }\n return segmentIds.filter((id) => !bufferState.requestedSegments.has(id));\n};\n\n/**\n * Core media buffering orchestration logic - prefetch only, no data storage\n * Integrates with BaseMediaEngine's existing caching and request deduplication\n */\nexport const manageMediaBuffer = async <\n T extends AudioRendition | VideoRendition,\n>(\n seekTimeMs: number,\n config: MediaBufferConfig,\n currentState: MediaBufferState,\n durationMs: number,\n signal: AbortSignal,\n deps: MediaBufferDependencies<T>,\n): Promise<MediaBufferState> => {\n if (!config.enableBuffering) {\n return currentState;\n }\n\n const rendition = await deps.getRendition();\n if (!rendition) {\n // Cannot buffer without a rendition\n return currentState;\n }\n const endTimeMs = seekTimeMs + config.bufferDurationMs;\n\n const desiredSegments = await computeSegmentRangeAsync(\n seekTimeMs,\n endTimeMs,\n durationMs,\n rendition,\n deps.computeSegmentId,\n );\n // Filter out segments already cached by BaseMediaEngine\n const uncachedSegments = desiredSegments.filter(\n (segmentId) => !deps.isSegmentCached(segmentId, rendition),\n );\n\n const newQueue = computeBufferQueue(\n uncachedSegments,\n currentState.requestedSegments,\n );\n\n // Shared state for concurrency control - prevents race conditions\n const newRequestedSegments = new Set(currentState.requestedSegments);\n const newActiveRequests = new Set(currentState.activeRequests);\n const remainingQueue = [...newQueue];\n\n // Thread-safe function to start next segment when slot becomes available\n const startNextSegment = (): void => {\n // Check if we have capacity and segments to fetch\n if (\n newActiveRequests.size >= config.maxParallelFetches ||\n remainingQueue.length === 0 ||\n signal.aborted\n ) {\n return;\n }\n\n const nextSegmentId = remainingQueue.shift();\n if (nextSegmentId === undefined) return;\n\n // Skip if already requested or now cached\n if (\n newRequestedSegments.has(nextSegmentId) ||\n deps.isSegmentCached(nextSegmentId, rendition)\n ) {\n startNextSegment(); // Try next segment immediately\n return;\n }\n\n newRequestedSegments.add(nextSegmentId);\n newActiveRequests.add(nextSegmentId);\n\n // Start the prefetch request\n deps\n .prefetchSegment(nextSegmentId, rendition)\n .then(() => {\n if (signal.aborted) return;\n newActiveRequests.delete(nextSegmentId);\n // Start next segment if continuous buffering is enabled\n if (config.enableContinuousBuffering ?? true) {\n startNextSegment();\n }\n })\n .catch((error) => {\n if (signal.aborted) return;\n newActiveRequests.delete(nextSegmentId);\n deps.logError(`Failed to prefetch segment ${nextSegmentId}`, error);\n // Continue even after error if continuous buffering is enabled\n if (config.enableContinuousBuffering ?? true) {\n startNextSegment();\n }\n });\n };\n\n // Start initial batch of requests up to maxParallelFetches limit\n const initialBatchSize = Math.min(config.maxParallelFetches, newQueue.length);\n for (let i = 0; i < initialBatchSize; i++) {\n startNextSegment();\n }\n\n const result = {\n currentSeekTimeMs: seekTimeMs,\n requestedSegments: newRequestedSegments,\n activeRequests: newActiveRequests,\n requestQueue: remainingQueue, // What's left in the queue\n };\n return result;\n};\n"],"mappings":";;;;AAuEA,MAAa,2BAA2B,OAGtC,aACA,WACA,YACA,WACA,qBAIsB;CACtB,MAAMA,WAAqB,EAAE;CAC7B,MAAM,oBAAqB,UAAkB,qBAAqB;CAGlE,MAAM,oBAAoB,KAAK,MAAM,cAAc,kBAAkB;CACrE,MAAM,kBAAkB,KAAK,MAC3B,KAAK,IAAI,WAAW,WAAW,GAAG,kBACnC;AAED,MAAK,IAAI,IAAI,mBAAmB,KAAK,iBAAiB,KAAK;EACzD,MAAM,SAAS,IAAI;AACnB,MAAI,SAAS,YAAY;GACvB,MAAM,YAAY,MAAM,iBAAiB,QAAQ,UAAU;AAC3D,OAAI,cAAc,OAChB,UAAS,KAAK,UAAU;;;AAK9B,QAAO,SAAS,QAAQ,IAAI,OAAO,QAAQ,IAAI,QAAQ,GAAG,KAAK,MAAM;;;;;;AAOvE,MAAa,sBACX,iBACA,sBACa;AACb,QAAO,gBAAgB,QACpB,cAAc,CAAC,kBAAkB,IAAI,UAAU,CACjD;;;;;;AAgFH,MAAa,oBAAoB,OAG/B,YACA,QACA,cACA,YACA,QACA,SAC8B;AAC9B,KAAI,CAAC,OAAO,gBACV,QAAO;CAGT,MAAM,YAAY,MAAM,KAAK,cAAc;AAC3C,KAAI,CAAC,UAEH,QAAO;CAgBT,MAAM,WAAW,oBAZO,MAAM,yBAC5B,YAHgB,aAAa,OAAO,kBAKpC,YACA,WACA,KAAK,iBACN,EAEwC,QACtC,cAAc,CAAC,KAAK,gBAAgB,WAAW,UAAU,CAC3D,EAIC,aAAa,kBACd;CAGD,MAAM,uBAAuB,IAAI,IAAI,aAAa,kBAAkB;CACpE,MAAM,oBAAoB,IAAI,IAAI,aAAa,eAAe;CAC9D,MAAM,iBAAiB,CAAC,GAAG,SAAS;CAGpC,MAAM,yBAA+B;AAEnC,MACE,kBAAkB,QAAQ,OAAO,sBACjC,eAAe,WAAW,KAC1B,OAAO,QAEP;EAGF,MAAM,gBAAgB,eAAe,OAAO;AAC5C,MAAI,kBAAkB,OAAW;AAGjC,MACE,qBAAqB,IAAI,cAAc,IACvC,KAAK,gBAAgB,eAAe,UAAU,EAC9C;AACA,qBAAkB;AAClB;;AAGF,uBAAqB,IAAI,cAAc;AACvC,oBAAkB,IAAI,cAAc;AAGpC,OACG,gBAAgB,eAAe,UAAU,CACzC,WAAW;AACV,OAAI,OAAO,QAAS;AACpB,qBAAkB,OAAO,cAAc;AAEvC,OAAI,OAAO,6BAA6B,KACtC,mBAAkB;IAEpB,CACD,OAAO,UAAU;AAChB,OAAI,OAAO,QAAS;AACpB,qBAAkB,OAAO,cAAc;AACvC,QAAK,SAAS,8BAA8B,iBAAiB,MAAM;AAEnE,OAAI,OAAO,6BAA6B,KACtC,mBAAkB;IAEpB;;CAIN,MAAM,mBAAmB,KAAK,IAAI,OAAO,oBAAoB,SAAS,OAAO;AAC7E,MAAK,IAAI,IAAI,GAAG,IAAI,kBAAkB,IACpC,mBAAkB;AASpB,QANe;EACb,mBAAmB;EACnB,mBAAmB;EACnB,gBAAgB;EAChB,cAAc;EACf"}
@@ -1,26 +1,52 @@
1
1
  import { LRUCache } from "../../../utils/LRUCache.js";
2
+
3
+ //#region src/elements/EFMedia/shared/GlobalInputCache.ts
4
+ /**
5
+ * Global cache for MediaBunny Input instances
6
+ * Shared across all MediaEngine instances to prevent duplicate decoding
7
+ * of the same segment data
8
+ */
2
9
  var GlobalInputCache = class {
3
10
  constructor() {
4
11
  this.cache = new LRUCache(50);
5
12
  }
13
+ /**
14
+ * Generate standardized cache key for Input objects
15
+ * Format: "input:{src}:{segmentId}:{renditionId}"
16
+ */
6
17
  generateKey(src, segmentId, renditionId) {
7
18
  return `input:${src}:${segmentId}:${renditionId || "default"}`;
8
19
  }
20
+ /**
21
+ * Get cached Input object
22
+ */
9
23
  get(src, segmentId, renditionId) {
10
24
  const key = this.generateKey(src, segmentId, renditionId);
11
25
  return this.cache.get(key);
12
26
  }
27
+ /**
28
+ * Cache Input object
29
+ */
13
30
  set(src, segmentId, input, renditionId) {
14
31
  const key = this.generateKey(src, segmentId, renditionId);
15
32
  this.cache.set(key, input);
16
33
  }
34
+ /**
35
+ * Check if Input is cached
36
+ */
17
37
  has(src, segmentId, renditionId) {
18
38
  const key = this.generateKey(src, segmentId, renditionId);
19
39
  return this.cache.has(key);
20
40
  }
41
+ /**
42
+ * Clear all cached Input objects
43
+ */
21
44
  clear() {
22
45
  this.cache.clear();
23
46
  }
47
+ /**
48
+ * Get cache statistics for debugging
49
+ */
24
50
  getStats() {
25
51
  return {
26
52
  size: this.cache.size,
@@ -30,4 +56,7 @@ var GlobalInputCache = class {
30
56
  };
31
57
  const globalInputCache = new GlobalInputCache();
32
58
  globalThis.debugInputCache = globalInputCache;
59
+
60
+ //#endregion
33
61
  export { globalInputCache };
62
+ //# sourceMappingURL=GlobalInputCache.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"GlobalInputCache.js","names":[],"sources":["../../../../src/elements/EFMedia/shared/GlobalInputCache.ts"],"sourcesContent":["import type { Input } from \"mediabunny\";\nimport { LRUCache } from \"../../../utils/LRUCache.js\";\n\n/**\n * Global cache for MediaBunny Input instances\n * Shared across all MediaEngine instances to prevent duplicate decoding\n * of the same segment data\n */\nclass GlobalInputCache {\n private cache = new LRUCache<string, Input>(50); // 50 Input instances max\n\n /**\n * Generate standardized cache key for Input objects\n * Format: \"input:{src}:{segmentId}:{renditionId}\"\n */\n private generateKey(\n src: string,\n segmentId: number,\n renditionId?: string,\n ): string {\n return `input:${src}:${segmentId}:${renditionId || \"default\"}`;\n }\n\n /**\n * Get cached Input object\n */\n get(src: string, segmentId: number, renditionId?: string): Input | undefined {\n const key = this.generateKey(src, segmentId, renditionId);\n return this.cache.get(key);\n }\n\n /**\n * Cache Input object\n */\n set(\n src: string,\n segmentId: number,\n input: Input,\n renditionId?: string,\n ): void {\n const key = this.generateKey(src, segmentId, renditionId);\n this.cache.set(key, input);\n }\n\n /**\n * Check if Input is cached\n */\n has(src: string, segmentId: number, renditionId?: string): boolean {\n const key = this.generateKey(src, segmentId, renditionId);\n return this.cache.has(key);\n }\n\n /**\n * Clear all cached Input objects\n */\n clear(): void {\n this.cache.clear();\n }\n\n /**\n * Get cache statistics for debugging\n */\n getStats() {\n return {\n size: this.cache.size,\n cachedKeys: Array.from((this.cache as any).cache.keys()),\n };\n }\n}\n\n// Single global instance shared across all MediaEngine instances\nexport const globalInputCache = new GlobalInputCache();\n\n// Export for debugging (works in both browser and server)\n(\n globalThis as typeof globalThis & { debugInputCache: typeof globalInputCache }\n).debugInputCache = globalInputCache;\n"],"mappings":";;;;;;;;AAQA,IAAM,mBAAN,MAAuB;;eACL,IAAI,SAAwB,GAAG;;;;;;CAM/C,AAAQ,YACN,KACA,WACA,aACQ;AACR,SAAO,SAAS,IAAI,GAAG,UAAU,GAAG,eAAe;;;;;CAMrD,IAAI,KAAa,WAAmB,aAAyC;EAC3E,MAAM,MAAM,KAAK,YAAY,KAAK,WAAW,YAAY;AACzD,SAAO,KAAK,MAAM,IAAI,IAAI;;;;;CAM5B,IACE,KACA,WACA,OACA,aACM;EACN,MAAM,MAAM,KAAK,YAAY,KAAK,WAAW,YAAY;AACzD,OAAK,MAAM,IAAI,KAAK,MAAM;;;;;CAM5B,IAAI,KAAa,WAAmB,aAA+B;EACjE,MAAM,MAAM,KAAK,YAAY,KAAK,WAAW,YAAY;AACzD,SAAO,KAAK,MAAM,IAAI,IAAI;;;;;CAM5B,QAAc;AACZ,OAAK,MAAM,OAAO;;;;;CAMpB,WAAW;AACT,SAAO;GACL,MAAM,KAAK,MAAM;GACjB,YAAY,MAAM,KAAM,KAAK,MAAc,MAAM,MAAM,CAAC;GACzD;;;AAKL,MAAa,mBAAmB,IAAI,kBAAkB;AAGtD,AACE,WACA,kBAAkB"}
@@ -1,23 +1,17 @@
1
- import { Task } from '@lit/task';
2
- import { AudioRendition, MediaEngine, VideoRendition } from '../../../transcoding/types';
3
- import { BufferedSeekingInput } from '../BufferedSeekingInput';
1
+ import { BufferedSeekingInput } from "../BufferedSeekingInput.js";
2
+ import { AudioRendition, VideoRendition } from "../../../transcoding/types/index.js";
3
+ import { Task } from "@lit/task";
4
+
5
+ //#region src/elements/EFMedia/shared/MediaTaskUtils.d.ts
6
+
4
7
  /**
5
8
  * Generic rendition type that can be either audio or video
6
9
  */
7
- export type MediaRendition = AudioRendition | VideoRendition;
8
- /**
9
- * Generic task type for init segment fetch
10
- */
11
- export type InitSegmentFetchTask = Task<readonly [MediaEngine | undefined], ArrayBuffer>;
12
- /**
13
- * Generic task type for segment ID calculation
14
- */
15
- export type SegmentIdTask = Task<readonly [MediaEngine | undefined, number], number | undefined>;
16
- /**
17
- * Generic task type for segment fetch
18
- */
19
- export type SegmentFetchTask = Task<readonly [MediaEngine | undefined, number | undefined], ArrayBuffer>;
10
+ type MediaRendition = AudioRendition | VideoRendition;
20
11
  /**
21
12
  * Generic task type for input creation
22
13
  */
23
- export type InputTask = Task<readonly [ArrayBuffer, ArrayBuffer], BufferedSeekingInput | undefined>;
14
+ type InputTask = Task<readonly [ArrayBuffer, ArrayBuffer], BufferedSeekingInput | undefined>;
15
+ //#endregion
16
+ export { InputTask, MediaRendition };
17
+ //# sourceMappingURL=MediaTaskUtils.d.ts.map
@@ -1,8 +1,33 @@
1
+ //#region src/elements/EFMedia/shared/PrecisionUtils.ts
2
+ /**
3
+ * Centralized precision utilities for consistent timing calculations across the media pipeline.
4
+ *
5
+ * The key insight is that floating-point precision errors can cause inconsistencies between:
6
+ * 1. Segment selection logic (in AssetMediaEngine.computeSegmentId)
7
+ * 2. Sample finding logic (in SampleBuffer.find)
8
+ * 3. Timeline mapping (in BufferedSeekingInput.seek)
9
+ *
10
+ * All timing calculations must use the same rounding strategy to ensure consistency.
11
+ */
12
+ /**
13
+ * Round time to millisecond precision to handle floating-point precision issues.
14
+ * Uses Math.round for consistent behavior across the entire pipeline.
15
+ *
16
+ * This function should be used for ALL time-related calculations that need to be
17
+ * compared between different parts of the system.
18
+ */
1
19
  const roundToMilliseconds = (timeMs) => {
2
20
  return Math.round(timeMs * 1e3) / 1e3;
3
21
  };
22
+ /**
23
+ * Convert media time (in seconds) to scaled time units using consistent rounding.
24
+ * This is used in segment selection to convert from milliseconds to timescale units.
25
+ */
4
26
  const convertToScaledTime = (timeMs, timescale) => {
5
27
  const scaledTime = timeMs / 1e3 * timescale;
6
28
  return Math.round(scaledTime);
7
29
  };
30
+
31
+ //#endregion
8
32
  export { convertToScaledTime, roundToMilliseconds };
33
+ //# sourceMappingURL=PrecisionUtils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PrecisionUtils.js","names":[],"sources":["../../../../src/elements/EFMedia/shared/PrecisionUtils.ts"],"sourcesContent":["/**\n * Centralized precision utilities for consistent timing calculations across the media pipeline.\n *\n * The key insight is that floating-point precision errors can cause inconsistencies between:\n * 1. Segment selection logic (in AssetMediaEngine.computeSegmentId)\n * 2. Sample finding logic (in SampleBuffer.find)\n * 3. Timeline mapping (in BufferedSeekingInput.seek)\n *\n * All timing calculations must use the same rounding strategy to ensure consistency.\n */\n\n/**\n * Round time to millisecond precision to handle floating-point precision issues.\n * Uses Math.round for consistent behavior across the entire pipeline.\n *\n * This function should be used for ALL time-related calculations that need to be\n * compared between different parts of the system.\n */\nexport const roundToMilliseconds = (timeMs: number): number => {\n // Round to 3 decimal places (microsecond precision)\n return Math.round(timeMs * 1000) / 1000;\n};\n\n/**\n * Convert media time (in seconds) to scaled time units using consistent rounding.\n * This is used in segment selection to convert from milliseconds to timescale units.\n */\nexport const convertToScaledTime = (\n timeMs: number,\n timescale: number,\n): number => {\n const scaledTime = (timeMs / 1000) * timescale;\n return Math.round(scaledTime);\n};\n\n/**\n * Convert scaled time units back to media time (in milliseconds) using consistent rounding.\n * This is the inverse of convertToScaledTime.\n */\nexport const convertFromScaledTime = (\n scaledTime: number,\n timescale: number,\n): number => {\n const timeMs = (scaledTime / timescale) * 1000;\n return roundToMilliseconds(timeMs);\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;AAkBA,MAAa,uBAAuB,WAA2B;AAE7D,QAAO,KAAK,MAAM,SAAS,IAAK,GAAG;;;;;;AAOrC,MAAa,uBACX,QACA,cACW;CACX,MAAM,aAAc,SAAS,MAAQ;AACrC,QAAO,KAAK,MAAM,WAAW"}
@@ -1,9 +1,18 @@
1
1
  import { globalInputCache } from "./GlobalInputCache.js";
2
2
  import { ALL_FORMATS, BlobSource, CanvasSink, Input } from "mediabunny";
3
+
4
+ //#region src/elements/EFMedia/shared/ThumbnailExtractor.ts
5
+ /**
6
+ * Shared thumbnail extraction logic for all MediaEngine implementations
7
+ * Eliminates code duplication and provides consistent behavior
8
+ */
3
9
  var ThumbnailExtractor = class {
4
10
  constructor(mediaEngine) {
5
11
  this.mediaEngine = mediaEngine;
6
12
  }
13
+ /**
14
+ * Extract thumbnails at multiple timestamps efficiently using segment batching
15
+ */
7
16
  async extractThumbnails(timestamps, rendition, durationMs) {
8
17
  if (timestamps.length === 0) return [];
9
18
  const validTimestamps = timestamps.filter((timeMs) => timeMs >= 0 && timeMs <= durationMs);
@@ -25,6 +34,9 @@ var ThumbnailExtractor = class {
25
34
  return results.get(t) || null;
26
35
  });
27
36
  }
37
+ /**
38
+ * Group timestamps by segment ID for efficient batch processing
39
+ */
28
40
  groupTimestampsBySegment(timestamps, rendition) {
29
41
  const segmentGroups = /* @__PURE__ */ new Map();
30
42
  for (const timeMs of timestamps) try {
@@ -40,6 +52,9 @@ var ThumbnailExtractor = class {
40
52
  }
41
53
  return segmentGroups;
42
54
  }
55
+ /**
56
+ * Extract thumbnails for a specific segment using CanvasSink
57
+ */
43
58
  async extractSegmentThumbnails(segmentId, timestamps, rendition) {
44
59
  const results = /* @__PURE__ */ new Map();
45
60
  try {
@@ -82,8 +97,15 @@ var ThumbnailExtractor = class {
82
97
  }
83
98
  return results;
84
99
  }
100
+ /**
101
+ * Convert global timestamps to segment-relative timestamps for mediabunny
102
+ * This is where the main difference between JIT and Asset engines lies
103
+ */
85
104
  convertToSegmentRelativeTimestamps(globalTimestamps, segmentId, rendition) {
86
105
  return this.mediaEngine.convertToSegmentRelativeTimestamps(globalTimestamps, segmentId, rendition);
87
106
  }
88
107
  };
108
+
109
+ //#endregion
89
110
  export { ThumbnailExtractor };
111
+ //# sourceMappingURL=ThumbnailExtractor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ThumbnailExtractor.js","names":["mediaEngine: BaseMediaEngine"],"sources":["../../../../src/elements/EFMedia/shared/ThumbnailExtractor.ts"],"sourcesContent":["import { ALL_FORMATS, BlobSource, CanvasSink, Input } from \"mediabunny\";\nimport type {\n ThumbnailResult,\n VideoRendition,\n} from \"../../../transcoding/types/index.js\";\nimport type { BaseMediaEngine } from \"../BaseMediaEngine.js\";\nimport { globalInputCache } from \"./GlobalInputCache.js\";\n\n/**\n * Shared thumbnail extraction logic for all MediaEngine implementations\n * Eliminates code duplication and provides consistent behavior\n */\nexport class ThumbnailExtractor {\n constructor(private mediaEngine: BaseMediaEngine) {}\n\n /**\n * Extract thumbnails at multiple timestamps efficiently using segment batching\n */\n async extractThumbnails(\n timestamps: number[],\n rendition: VideoRendition,\n durationMs: number,\n ): Promise<(ThumbnailResult | null)[]> {\n if (timestamps.length === 0) {\n return [];\n }\n\n // Validate and filter timestamps within bounds\n const validTimestamps = timestamps.filter(\n (timeMs) => timeMs >= 0 && timeMs <= durationMs,\n );\n\n if (validTimestamps.length === 0) {\n console.warn(\n `ThumbnailExtractor: All timestamps out of bounds (0-${durationMs}ms)`,\n );\n return timestamps.map(() => null);\n }\n\n // Group timestamps by segment for batch processing\n const segmentGroups = this.groupTimestampsBySegment(\n validTimestamps,\n rendition,\n );\n\n // Extract batched by segment using CanvasSink\n const results = new Map<number, ThumbnailResult | null>();\n\n for (const [segmentId, segmentTimestamps] of segmentGroups) {\n try {\n const segmentResults = await this.extractSegmentThumbnails(\n segmentId,\n segmentTimestamps,\n rendition,\n );\n\n for (const [timestamp, thumbnail] of segmentResults) {\n results.set(timestamp, thumbnail);\n }\n } catch (error) {\n console.warn(\n `ThumbnailExtractor: Failed to extract thumbnails for segment ${segmentId}:`,\n error,\n );\n // Mark all timestamps in this segment as failed\n for (const timestamp of segmentTimestamps) {\n results.set(timestamp, null);\n }\n }\n }\n\n // Return in original order, null for any that failed or were out of bounds\n return timestamps.map((t) => {\n // If timestamp was out of bounds, return null\n if (t < 0 || t > durationMs) {\n return null;\n }\n return results.get(t) || null;\n });\n }\n\n /**\n * Group timestamps by segment ID for efficient batch processing\n */\n private groupTimestampsBySegment(\n timestamps: number[],\n rendition: VideoRendition,\n ): Map<number, number[]> {\n const segmentGroups = new Map<number, number[]>();\n\n for (const timeMs of timestamps) {\n try {\n const segmentId = this.mediaEngine.computeSegmentId(timeMs, rendition);\n if (segmentId !== undefined) {\n if (!segmentGroups.has(segmentId)) {\n segmentGroups.set(segmentId, []);\n }\n const segmentGroup = segmentGroups.get(segmentId) ?? [];\n if (!segmentGroup) {\n segmentGroups.set(segmentId, []);\n }\n segmentGroup.push(timeMs);\n }\n } catch (error) {\n console.warn(\n `ThumbnailExtractor: Could not compute segment for timestamp ${timeMs}:`,\n error,\n );\n }\n }\n\n return segmentGroups;\n }\n\n /**\n * Extract thumbnails for a specific segment using CanvasSink\n */\n private async extractSegmentThumbnails(\n segmentId: number,\n timestamps: number[],\n rendition: VideoRendition,\n ): Promise<Map<number, ThumbnailResult | null>> {\n const results = new Map<number, ThumbnailResult | null>();\n\n try {\n // Get segment data through existing media engine methods (uses caches)\n const abortController = new AbortController();\n const [initSegment, mediaSegment] = await Promise.all([\n this.mediaEngine.fetchInitSegment(rendition, abortController.signal),\n this.mediaEngine.fetchMediaSegment(segmentId, rendition),\n ]);\n\n // Create Input for this segment using global shared cache\n const segmentBlob = new Blob([initSegment, mediaSegment]);\n\n let input = globalInputCache.get(rendition.src, segmentId, rendition.id);\n if (!input) {\n input = new Input({\n formats: ALL_FORMATS,\n source: new BlobSource(segmentBlob),\n });\n globalInputCache.set(rendition.src, segmentId, input, rendition.id);\n }\n\n // Set up CanvasSink for batched extraction\n const videoTrack = await input.getPrimaryVideoTrack();\n if (!videoTrack) {\n // No video track - return nulls for all timestamps\n for (const timestamp of timestamps) {\n results.set(timestamp, null);\n }\n return results;\n }\n\n const sink = new CanvasSink(videoTrack);\n\n // Convert global timestamps to segment-relative (in seconds for mediabunny)\n const relativeTimestamps = this.convertToSegmentRelativeTimestamps(\n timestamps,\n segmentId,\n rendition,\n );\n\n // Batch extract all thumbnails for this segment\n const timestampResults = [];\n for await (const result of sink.canvasesAtTimestamps(\n relativeTimestamps,\n )) {\n timestampResults.push(result);\n }\n\n // Map results back to original timestamps\n for (let i = 0; i < timestamps.length; i++) {\n const globalTimestamp = timestamps[i];\n if (globalTimestamp === undefined) {\n continue;\n }\n\n const result = timestampResults[i];\n\n if (result?.canvas) {\n const canvas = result.canvas;\n if (\n canvas instanceof HTMLCanvasElement ||\n canvas instanceof OffscreenCanvas\n ) {\n results.set(globalTimestamp, {\n timestamp: globalTimestamp,\n thumbnail: canvas,\n });\n } else {\n results.set(globalTimestamp, null);\n }\n } else {\n results.set(globalTimestamp, null);\n }\n }\n } catch (error) {\n console.error(\n `ThumbnailExtractor: Failed to extract thumbnails for segment ${segmentId}:`,\n error,\n );\n // Return nulls for all timestamps on error\n for (const timestamp of timestamps) {\n results.set(timestamp, null);\n }\n }\n\n return results;\n }\n\n /**\n * Convert global timestamps to segment-relative timestamps for mediabunny\n * This is where the main difference between JIT and Asset engines lies\n */\n private convertToSegmentRelativeTimestamps(\n globalTimestamps: number[],\n segmentId: number,\n rendition: VideoRendition,\n ): number[] {\n return this.mediaEngine.convertToSegmentRelativeTimestamps(\n globalTimestamps,\n segmentId,\n rendition,\n );\n }\n}\n"],"mappings":";;;;;;;;AAYA,IAAa,qBAAb,MAAgC;CAC9B,YAAY,AAAQA,aAA8B;EAA9B;;;;;CAKpB,MAAM,kBACJ,YACA,WACA,YACqC;AACrC,MAAI,WAAW,WAAW,EACxB,QAAO,EAAE;EAIX,MAAM,kBAAkB,WAAW,QAChC,WAAW,UAAU,KAAK,UAAU,WACtC;AAED,MAAI,gBAAgB,WAAW,GAAG;AAChC,WAAQ,KACN,uDAAuD,WAAW,KACnE;AACD,UAAO,WAAW,UAAU,KAAK;;EAInC,MAAM,gBAAgB,KAAK,yBACzB,iBACA,UACD;EAGD,MAAM,0BAAU,IAAI,KAAqC;AAEzD,OAAK,MAAM,CAAC,WAAW,sBAAsB,cAC3C,KAAI;GACF,MAAM,iBAAiB,MAAM,KAAK,yBAChC,WACA,mBACA,UACD;AAED,QAAK,MAAM,CAAC,WAAW,cAAc,eACnC,SAAQ,IAAI,WAAW,UAAU;WAE5B,OAAO;AACd,WAAQ,KACN,gEAAgE,UAAU,IAC1E,MACD;AAED,QAAK,MAAM,aAAa,kBACtB,SAAQ,IAAI,WAAW,KAAK;;AAMlC,SAAO,WAAW,KAAK,MAAM;AAE3B,OAAI,IAAI,KAAK,IAAI,WACf,QAAO;AAET,UAAO,QAAQ,IAAI,EAAE,IAAI;IACzB;;;;;CAMJ,AAAQ,yBACN,YACA,WACuB;EACvB,MAAM,gCAAgB,IAAI,KAAuB;AAEjD,OAAK,MAAM,UAAU,WACnB,KAAI;GACF,MAAM,YAAY,KAAK,YAAY,iBAAiB,QAAQ,UAAU;AACtE,OAAI,cAAc,QAAW;AAC3B,QAAI,CAAC,cAAc,IAAI,UAAU,CAC/B,eAAc,IAAI,WAAW,EAAE,CAAC;IAElC,MAAM,eAAe,cAAc,IAAI,UAAU,IAAI,EAAE;AACvD,QAAI,CAAC,aACH,eAAc,IAAI,WAAW,EAAE,CAAC;AAElC,iBAAa,KAAK,OAAO;;WAEpB,OAAO;AACd,WAAQ,KACN,+DAA+D,OAAO,IACtE,MACD;;AAIL,SAAO;;;;;CAMT,MAAc,yBACZ,WACA,YACA,WAC8C;EAC9C,MAAM,0BAAU,IAAI,KAAqC;AAEzD,MAAI;GAEF,MAAM,kBAAkB,IAAI,iBAAiB;GAC7C,MAAM,CAAC,aAAa,gBAAgB,MAAM,QAAQ,IAAI,CACpD,KAAK,YAAY,iBAAiB,WAAW,gBAAgB,OAAO,EACpE,KAAK,YAAY,kBAAkB,WAAW,UAAU,CACzD,CAAC;GAGF,MAAM,cAAc,IAAI,KAAK,CAAC,aAAa,aAAa,CAAC;GAEzD,IAAI,QAAQ,iBAAiB,IAAI,UAAU,KAAK,WAAW,UAAU,GAAG;AACxE,OAAI,CAAC,OAAO;AACV,YAAQ,IAAI,MAAM;KAChB,SAAS;KACT,QAAQ,IAAI,WAAW,YAAY;KACpC,CAAC;AACF,qBAAiB,IAAI,UAAU,KAAK,WAAW,OAAO,UAAU,GAAG;;GAIrE,MAAM,aAAa,MAAM,MAAM,sBAAsB;AACrD,OAAI,CAAC,YAAY;AAEf,SAAK,MAAM,aAAa,WACtB,SAAQ,IAAI,WAAW,KAAK;AAE9B,WAAO;;GAGT,MAAM,OAAO,IAAI,WAAW,WAAW;GAGvC,MAAM,qBAAqB,KAAK,mCAC9B,YACA,WACA,UACD;GAGD,MAAM,mBAAmB,EAAE;AAC3B,cAAW,MAAM,UAAU,KAAK,qBAC9B,mBACD,CACC,kBAAiB,KAAK,OAAO;AAI/B,QAAK,IAAI,IAAI,GAAG,IAAI,WAAW,QAAQ,KAAK;IAC1C,MAAM,kBAAkB,WAAW;AACnC,QAAI,oBAAoB,OACtB;IAGF,MAAM,SAAS,iBAAiB;AAEhC,QAAI,QAAQ,QAAQ;KAClB,MAAM,SAAS,OAAO;AACtB,SACE,kBAAkB,qBAClB,kBAAkB,gBAElB,SAAQ,IAAI,iBAAiB;MAC3B,WAAW;MACX,WAAW;MACZ,CAAC;SAEF,SAAQ,IAAI,iBAAiB,KAAK;UAGpC,SAAQ,IAAI,iBAAiB,KAAK;;WAG/B,OAAO;AACd,WAAQ,MACN,gEAAgE,UAAU,IAC1E,MACD;AAED,QAAK,MAAM,aAAa,WACtB,SAAQ,IAAI,WAAW,KAAK;;AAIhC,SAAO;;;;;;CAOT,AAAQ,mCACN,kBACA,WACA,WACU;AACV,SAAO,KAAK,YAAY,mCACtB,kBACA,WACA,UACD"}
@@ -3,12 +3,18 @@ import { AssetMediaEngine } from "../AssetMediaEngine.js";
3
3
  import { AssetIdMediaEngine } from "../AssetIdMediaEngine.js";
4
4
  import { JitMediaEngine } from "../JitMediaEngine.js";
5
5
  import { Task } from "@lit/task";
6
+
7
+ //#region src/elements/EFMedia/tasks/makeMediaEngineTask.ts
6
8
  const getLatestMediaEngine = async (host, signal) => {
7
9
  const mediaEngine = await host.mediaEngineTask.taskComplete;
8
10
  signal.throwIfAborted();
9
11
  if (!mediaEngine) throw new Error("Media engine is not available");
10
12
  return mediaEngine;
11
13
  };
14
+ /**
15
+ * Core logic for creating a MediaEngine with explicit dependencies.
16
+ * Pure function that requires all dependencies to be provided.
17
+ */
12
18
  const createMediaEngine = (host) => {
13
19
  const { src, assetId, urlGenerator, apiHost } = host;
14
20
  if (assetId !== null && assetId !== void 0 && assetId.trim() !== "") {
@@ -25,6 +31,10 @@ const createMediaEngine = (host) => {
25
31
  const url = urlGenerator.generateManifestUrl(src);
26
32
  return JitMediaEngine.fetch(host, urlGenerator, url);
27
33
  };
34
+ /**
35
+ * Handle completion of media engine task - triggers necessary updates.
36
+ * Extracted for testability.
37
+ */
28
38
  const handleMediaEngineComplete = (host) => {
29
39
  host.requestUpdate("intrinsicDurationMs");
30
40
  host.requestUpdate("ownCurrentTimeMs");
@@ -43,4 +53,7 @@ const makeMediaEngineTask = (host) => {
43
53
  }
44
54
  });
45
55
  };
56
+
57
+ //#endregion
46
58
  export { getLatestMediaEngine, makeMediaEngineTask };
59
+ //# sourceMappingURL=makeMediaEngineTask.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"makeMediaEngineTask.js","names":[],"sources":["../../../../src/elements/EFMedia/tasks/makeMediaEngineTask.ts"],"sourcesContent":["import { Task } from \"@lit/task\";\nimport { EF_INTERACTIVE } from \"../../../EF_INTERACTIVE\";\nimport type { MediaEngine } from \"../../../transcoding/types\";\nimport type { EFMedia } from \"../../EFMedia\";\nimport { AssetIdMediaEngine } from \"../AssetIdMediaEngine\";\nimport { AssetMediaEngine } from \"../AssetMediaEngine\";\nimport { JitMediaEngine } from \"../JitMediaEngine\";\n\nexport const getLatestMediaEngine = async (\n host: EFMedia,\n signal: AbortSignal,\n): Promise<MediaEngine> => {\n const mediaEngine = await host.mediaEngineTask.taskComplete;\n signal.throwIfAborted();\n if (!mediaEngine) {\n throw new Error(\"Media engine is not available\");\n }\n return mediaEngine;\n};\n\n/**\n * Core logic for creating a MediaEngine with explicit dependencies.\n * Pure function that requires all dependencies to be provided.\n */\nexport const createMediaEngine = (host: EFMedia): Promise<MediaEngine> => {\n const { src, assetId, urlGenerator, apiHost } = host;\n\n // Check for AssetID mode first\n if (assetId !== null && assetId !== undefined && assetId.trim() !== \"\") {\n if (!apiHost) {\n return Promise.reject(new Error(\"API host is required for AssetID mode\"));\n }\n return AssetIdMediaEngine.fetchByAssetId(\n host,\n urlGenerator,\n assetId,\n apiHost,\n );\n }\n\n // Check for null/undefined/empty/whitespace src\n if (!src || typeof src !== \"string\" || src.trim() === \"\") {\n console.error(`Unsupported media source: ${src}, assetId: ${assetId}`);\n return Promise.reject(new Error(\"Unsupported media source\"));\n }\n\n const lowerSrc = src.toLowerCase();\n if (!lowerSrc.startsWith(\"http://\") && !lowerSrc.startsWith(\"https://\")) {\n return AssetMediaEngine.fetch(host, urlGenerator, src);\n }\n\n // Remote (http/https) source, now check configuration\n const configuration = host.closest(\"ef-configuration\");\n if (configuration?.mediaEngine === \"local\") {\n // Only use AssetMediaEngine for remote URLs when explicitly configured\n return AssetMediaEngine.fetch(host, urlGenerator, src);\n }\n\n // Default: Use JitMediaEngine for remote URLs (transcoding service)\n const url = urlGenerator.generateManifestUrl(src);\n return JitMediaEngine.fetch(host, urlGenerator, url);\n};\n\n/**\n * Handle completion of media engine task - triggers necessary updates.\n * Extracted for testability.\n */\nexport const handleMediaEngineComplete = (host: EFMedia): void => {\n host.requestUpdate(\"intrinsicDurationMs\");\n host.requestUpdate(\"ownCurrentTimeMs\");\n host.rootTimegroup?.requestUpdate(\"ownCurrentTimeMs\");\n host.rootTimegroup?.requestUpdate(\"durationMs\");\n};\n\ntype MediaEngineTask = Task<readonly [string, string | null], MediaEngine>;\n\nexport const makeMediaEngineTask = (host: EFMedia): MediaEngineTask => {\n return new Task(host, {\n autoRun: EF_INTERACTIVE,\n args: () => [host.src, host.assetId] as const,\n task: async () => {\n return createMediaEngine(host);\n },\n onComplete: (_value) => {\n handleMediaEngineComplete(host);\n },\n });\n};\n"],"mappings":";;;;;;;AAQA,MAAa,uBAAuB,OAClC,MACA,WACyB;CACzB,MAAM,cAAc,MAAM,KAAK,gBAAgB;AAC/C,QAAO,gBAAgB;AACvB,KAAI,CAAC,YACH,OAAM,IAAI,MAAM,gCAAgC;AAElD,QAAO;;;;;;AAOT,MAAa,qBAAqB,SAAwC;CACxE,MAAM,EAAE,KAAK,SAAS,cAAc,YAAY;AAGhD,KAAI,YAAY,QAAQ,YAAY,UAAa,QAAQ,MAAM,KAAK,IAAI;AACtE,MAAI,CAAC,QACH,QAAO,QAAQ,uBAAO,IAAI,MAAM,wCAAwC,CAAC;AAE3E,SAAO,mBAAmB,eACxB,MACA,cACA,SACA,QACD;;AAIH,KAAI,CAAC,OAAO,OAAO,QAAQ,YAAY,IAAI,MAAM,KAAK,IAAI;AACxD,UAAQ,MAAM,6BAA6B,IAAI,aAAa,UAAU;AACtE,SAAO,QAAQ,uBAAO,IAAI,MAAM,2BAA2B,CAAC;;CAG9D,MAAM,WAAW,IAAI,aAAa;AAClC,KAAI,CAAC,SAAS,WAAW,UAAU,IAAI,CAAC,SAAS,WAAW,WAAW,CACrE,QAAO,iBAAiB,MAAM,MAAM,cAAc,IAAI;AAKxD,KADsB,KAAK,QAAQ,mBAAmB,EACnC,gBAAgB,QAEjC,QAAO,iBAAiB,MAAM,MAAM,cAAc,IAAI;CAIxD,MAAM,MAAM,aAAa,oBAAoB,IAAI;AACjD,QAAO,eAAe,MAAM,MAAM,cAAc,IAAI;;;;;;AAOtD,MAAa,6BAA6B,SAAwB;AAChE,MAAK,cAAc,sBAAsB;AACzC,MAAK,cAAc,mBAAmB;AACtC,MAAK,eAAe,cAAc,mBAAmB;AACrD,MAAK,eAAe,cAAc,aAAa;;AAKjD,MAAa,uBAAuB,SAAmC;AACrE,QAAO,IAAI,KAAK,MAAM;EACpB,SAAS;EACT,YAAY,CAAC,KAAK,KAAK,KAAK,QAAQ;EACpC,MAAM,YAAY;AAChB,UAAO,kBAAkB,KAAK;;EAEhC,aAAa,WAAW;AACtB,6BAA0B,KAAK;;EAElC,CAAC"}
@@ -1,11 +1,23 @@
1
+ //#region src/elements/EFMedia/videoTasks/MainVideoInputCache.ts
2
+ /**
3
+ * Cache for main video BufferedSeekingInput instances
4
+ * Main video segments are typically 2s long, so we can reuse the same input
5
+ * for multiple frames within that segment (e.g., 60 frames at 30fps)
6
+ */
1
7
  var MainVideoInputCache = class {
2
8
  constructor() {
3
9
  this.cache = /* @__PURE__ */ new Map();
4
10
  this.maxCacheSize = 10;
5
11
  }
12
+ /**
13
+ * Create a cache key that uniquely identifies a segment
14
+ */
6
15
  getCacheKey(src, segmentId, renditionId) {
7
16
  return `${src}:${renditionId || "default"}:${segmentId}`;
8
17
  }
18
+ /**
19
+ * Get or create BufferedSeekingInput for a main video segment
20
+ */
9
21
  async getOrCreateInput(src, segmentId, renditionId, createInputFn) {
10
22
  const cacheKey = this.getCacheKey(src, segmentId, renditionId);
11
23
  const cached = this.cache.get(cacheKey);
@@ -19,9 +31,15 @@ var MainVideoInputCache = class {
19
31
  }
20
32
  return input;
21
33
  }
34
+ /**
35
+ * Clear the entire cache (called when video changes)
36
+ */
22
37
  clear() {
23
38
  this.cache.clear();
24
39
  }
40
+ /**
41
+ * Get cache statistics
42
+ */
25
43
  getStats() {
26
44
  return {
27
45
  size: this.cache.size,
@@ -29,4 +47,7 @@ var MainVideoInputCache = class {
29
47
  };
30
48
  }
31
49
  };
50
+
51
+ //#endregion
32
52
  export { MainVideoInputCache };
53
+ //# sourceMappingURL=MainVideoInputCache.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MainVideoInputCache.js","names":[],"sources":["../../../../src/elements/EFMedia/videoTasks/MainVideoInputCache.ts"],"sourcesContent":["import type { BufferedSeekingInput } from \"../BufferedSeekingInput\";\n\n/**\n * Cache for main video BufferedSeekingInput instances\n * Main video segments are typically 2s long, so we can reuse the same input\n * for multiple frames within that segment (e.g., 60 frames at 30fps)\n */\nexport class MainVideoInputCache {\n private cache = new Map<string, BufferedSeekingInput>();\n private maxCacheSize = 10; // Keep last 10 main inputs (covers 20 seconds at 2s/segment)\n\n /**\n * Create a cache key that uniquely identifies a segment\n */\n private getCacheKey(\n src: string,\n segmentId: number,\n renditionId: string | undefined,\n ): string {\n return `${src}:${renditionId || \"default\"}:${segmentId}`;\n }\n\n /**\n * Get or create BufferedSeekingInput for a main video segment\n */\n async getOrCreateInput(\n src: string,\n segmentId: number,\n renditionId: string | undefined,\n createInputFn: () => Promise<BufferedSeekingInput | undefined>,\n ): Promise<BufferedSeekingInput | undefined> {\n const cacheKey = this.getCacheKey(src, segmentId, renditionId);\n\n // Check if we already have this segment cached\n const cached = this.cache.get(cacheKey);\n if (cached) {\n return cached;\n }\n\n // Create new input\n const input = await createInputFn();\n if (!input) {\n return undefined;\n }\n\n // Add to cache and maintain size limit\n this.cache.set(cacheKey, input);\n\n // Evict oldest entries if cache is too large (LRU-like behavior)\n if (this.cache.size > this.maxCacheSize) {\n const oldestKey = this.cache.keys().next().value;\n if (oldestKey !== undefined) {\n this.cache.delete(oldestKey);\n }\n }\n\n return input;\n }\n\n /**\n * Clear the entire cache (called when video changes)\n */\n clear() {\n this.cache.clear();\n }\n\n /**\n * Get cache statistics\n */\n getStats() {\n return {\n size: this.cache.size,\n cacheKeys: Array.from(this.cache.keys()),\n };\n }\n}\n"],"mappings":";;;;;;AAOA,IAAa,sBAAb,MAAiC;;+BACf,IAAI,KAAmC;sBAChC;;;;;CAKvB,AAAQ,YACN,KACA,WACA,aACQ;AACR,SAAO,GAAG,IAAI,GAAG,eAAe,UAAU,GAAG;;;;;CAM/C,MAAM,iBACJ,KACA,WACA,aACA,eAC2C;EAC3C,MAAM,WAAW,KAAK,YAAY,KAAK,WAAW,YAAY;EAG9D,MAAM,SAAS,KAAK,MAAM,IAAI,SAAS;AACvC,MAAI,OACF,QAAO;EAIT,MAAM,QAAQ,MAAM,eAAe;AACnC,MAAI,CAAC,MACH;AAIF,OAAK,MAAM,IAAI,UAAU,MAAM;AAG/B,MAAI,KAAK,MAAM,OAAO,KAAK,cAAc;GACvC,MAAM,YAAY,KAAK,MAAM,MAAM,CAAC,MAAM,CAAC;AAC3C,OAAI,cAAc,OAChB,MAAK,MAAM,OAAO,UAAU;;AAIhC,SAAO;;;;;CAMT,QAAQ;AACN,OAAK,MAAM,OAAO;;;;;CAMpB,WAAW;AACT,SAAO;GACL,MAAM,KAAK,MAAM;GACjB,WAAW,MAAM,KAAK,KAAK,MAAM,MAAM,CAAC;GACzC"}
@@ -1,8 +1,17 @@
1
+ //#region src/elements/EFMedia/videoTasks/ScrubInputCache.ts
2
+ /**
3
+ * Cache for scrub BufferedSeekingInput instances
4
+ * Since scrub segments are 30s long, we can reuse the same input for many seeks
5
+ * within that time range, making scrub seeking very efficient
6
+ */
1
7
  var ScrubInputCache = class {
2
8
  constructor() {
3
9
  this.cache = /* @__PURE__ */ new Map();
4
10
  this.maxCacheSize = 5;
5
11
  }
12
+ /**
13
+ * Get or create BufferedSeekingInput for a scrub segment
14
+ */
6
15
  async getOrCreateInput(segmentId, createInputFn) {
7
16
  const cached = this.cache.get(segmentId);
8
17
  if (cached) return cached;
@@ -15,9 +24,15 @@ var ScrubInputCache = class {
15
24
  }
16
25
  return input;
17
26
  }
27
+ /**
28
+ * Clear the entire cache (called when video changes)
29
+ */
18
30
  clear() {
19
31
  this.cache.clear();
20
32
  }
33
+ /**
34
+ * Get cache statistics
35
+ */
21
36
  getStats() {
22
37
  return {
23
38
  size: this.cache.size,
@@ -25,4 +40,7 @@ var ScrubInputCache = class {
25
40
  };
26
41
  }
27
42
  };
43
+
44
+ //#endregion
28
45
  export { ScrubInputCache };
46
+ //# sourceMappingURL=ScrubInputCache.js.map