@editframe/elements 0.17.6-beta.0 → 0.18.3-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 (218) hide show
  1. package/dist/EF_FRAMEGEN.js +1 -1
  2. package/dist/ScrubTrackManager.d.ts +2 -2
  3. package/dist/elements/EFAudio.d.ts +21 -2
  4. package/dist/elements/EFAudio.js +41 -11
  5. package/dist/elements/EFImage.d.ts +1 -0
  6. package/dist/elements/EFImage.js +11 -3
  7. package/dist/elements/EFMedia/AssetIdMediaEngine.d.ts +18 -0
  8. package/dist/elements/EFMedia/AssetIdMediaEngine.js +41 -0
  9. package/dist/elements/EFMedia/AssetMediaEngine.d.ts +47 -0
  10. package/dist/elements/EFMedia/AssetMediaEngine.js +116 -0
  11. package/dist/elements/EFMedia/BaseMediaEngine.d.ts +55 -0
  12. package/dist/elements/EFMedia/BaseMediaEngine.js +96 -0
  13. package/dist/elements/EFMedia/BufferedSeekingInput.d.ts +43 -0
  14. package/dist/elements/EFMedia/BufferedSeekingInput.js +159 -0
  15. package/dist/elements/EFMedia/JitMediaEngine.browsertest.d.ts +0 -0
  16. package/dist/elements/EFMedia/JitMediaEngine.d.ts +31 -0
  17. package/dist/elements/EFMedia/JitMediaEngine.js +62 -0
  18. package/dist/elements/EFMedia/audioTasks/makeAudioBufferTask.browsertest.d.ts +9 -0
  19. package/dist/elements/EFMedia/audioTasks/makeAudioBufferTask.d.ts +16 -0
  20. package/dist/elements/EFMedia/audioTasks/makeAudioBufferTask.js +48 -0
  21. package/dist/elements/EFMedia/audioTasks/makeAudioFrequencyAnalysisTask.d.ts +3 -0
  22. package/dist/elements/EFMedia/audioTasks/makeAudioFrequencyAnalysisTask.js +138 -0
  23. package/dist/elements/EFMedia/audioTasks/makeAudioInitSegmentFetchTask.browsertest.d.ts +9 -0
  24. package/dist/elements/EFMedia/audioTasks/makeAudioInitSegmentFetchTask.d.ts +4 -0
  25. package/dist/elements/EFMedia/audioTasks/makeAudioInitSegmentFetchTask.js +16 -0
  26. package/dist/elements/EFMedia/audioTasks/makeAudioInputTask.browsertest.d.ts +9 -0
  27. package/dist/elements/EFMedia/audioTasks/makeAudioInputTask.d.ts +3 -0
  28. package/dist/elements/EFMedia/audioTasks/makeAudioInputTask.js +22 -0
  29. package/dist/elements/EFMedia/audioTasks/makeAudioSeekTask.d.ts +7 -0
  30. package/dist/elements/EFMedia/audioTasks/makeAudioSeekTask.js +24 -0
  31. package/dist/elements/EFMedia/audioTasks/makeAudioSegmentFetchTask.d.ts +4 -0
  32. package/dist/elements/EFMedia/audioTasks/makeAudioSegmentFetchTask.js +18 -0
  33. package/dist/elements/EFMedia/audioTasks/makeAudioSegmentIdTask.d.ts +4 -0
  34. package/dist/elements/EFMedia/audioTasks/makeAudioSegmentIdTask.js +16 -0
  35. package/dist/elements/EFMedia/audioTasks/makeAudioTimeDomainAnalysisTask.d.ts +3 -0
  36. package/dist/elements/EFMedia/audioTasks/makeAudioTimeDomainAnalysisTask.js +104 -0
  37. package/dist/elements/EFMedia/services/AudioElementFactory.d.ts +22 -0
  38. package/dist/elements/EFMedia/services/AudioElementFactory.js +72 -0
  39. package/dist/elements/EFMedia/services/MediaSourceService.browsertest.d.ts +1 -0
  40. package/dist/elements/EFMedia/services/MediaSourceService.d.ts +47 -0
  41. package/dist/elements/EFMedia/services/MediaSourceService.js +73 -0
  42. package/dist/elements/EFMedia/shared/AudioSpanUtils.d.ts +7 -0
  43. package/dist/elements/EFMedia/shared/AudioSpanUtils.js +54 -0
  44. package/dist/elements/EFMedia/shared/BufferUtils.d.ts +70 -0
  45. package/dist/elements/EFMedia/shared/BufferUtils.js +89 -0
  46. package/dist/elements/EFMedia/shared/MediaTaskUtils.d.ts +23 -0
  47. package/dist/elements/EFMedia/shared/RenditionHelpers.browsertest.d.ts +1 -0
  48. package/dist/elements/EFMedia/shared/RenditionHelpers.d.ts +19 -0
  49. package/dist/elements/EFMedia/tasks/makeMediaEngineTask.browsertest.d.ts +1 -0
  50. package/dist/elements/EFMedia/tasks/makeMediaEngineTask.d.ts +18 -0
  51. package/dist/elements/EFMedia/tasks/makeMediaEngineTask.js +60 -0
  52. package/dist/elements/EFMedia/tasks/makeMediaEngineTask.test.d.ts +1 -0
  53. package/dist/elements/EFMedia/videoTasks/makeVideoBufferTask.browsertest.d.ts +9 -0
  54. package/dist/elements/EFMedia/videoTasks/makeVideoBufferTask.d.ts +16 -0
  55. package/dist/elements/EFMedia/videoTasks/makeVideoBufferTask.js +46 -0
  56. package/dist/elements/EFMedia/videoTasks/makeVideoInitSegmentFetchTask.browsertest.d.ts +9 -0
  57. package/dist/elements/EFMedia/videoTasks/makeVideoInitSegmentFetchTask.d.ts +4 -0
  58. package/dist/elements/EFMedia/videoTasks/makeVideoInitSegmentFetchTask.js +16 -0
  59. package/dist/elements/EFMedia/videoTasks/makeVideoInputTask.browsertest.d.ts +9 -0
  60. package/dist/elements/EFMedia/videoTasks/makeVideoInputTask.d.ts +3 -0
  61. package/dist/elements/EFMedia/videoTasks/makeVideoInputTask.js +27 -0
  62. package/dist/elements/EFMedia/videoTasks/makeVideoSeekTask.d.ts +7 -0
  63. package/dist/elements/EFMedia/videoTasks/makeVideoSeekTask.js +25 -0
  64. package/dist/elements/EFMedia/videoTasks/makeVideoSegmentFetchTask.browsertest.d.ts +9 -0
  65. package/dist/elements/EFMedia/videoTasks/makeVideoSegmentFetchTask.d.ts +4 -0
  66. package/dist/elements/EFMedia/videoTasks/makeVideoSegmentFetchTask.js +18 -0
  67. package/dist/elements/EFMedia/videoTasks/makeVideoSegmentIdTask.browsertest.d.ts +9 -0
  68. package/dist/elements/EFMedia/videoTasks/makeVideoSegmentIdTask.d.ts +4 -0
  69. package/dist/elements/EFMedia/videoTasks/makeVideoSegmentIdTask.js +16 -0
  70. package/dist/elements/EFMedia.browsertest.d.ts +1 -0
  71. package/dist/elements/EFMedia.d.ts +75 -111
  72. package/dist/elements/EFMedia.js +141 -1111
  73. package/dist/elements/EFTemporal.d.ts +1 -1
  74. package/dist/elements/EFTemporal.js +1 -1
  75. package/dist/elements/EFTimegroup.d.ts +11 -0
  76. package/dist/elements/EFTimegroup.js +88 -13
  77. package/dist/elements/EFVideo.d.ts +60 -29
  78. package/dist/elements/EFVideo.js +103 -203
  79. package/dist/elements/EFWaveform.js +2 -2
  80. package/dist/elements/SampleBuffer.d.ts +14 -0
  81. package/dist/elements/SampleBuffer.js +52 -0
  82. package/dist/getRenderInfo.d.ts +2 -2
  83. package/dist/getRenderInfo.js +2 -1
  84. package/dist/gui/ContextMixin.js +17 -70
  85. package/dist/gui/EFFilmstrip.d.ts +3 -3
  86. package/dist/gui/EFFilmstrip.js +1 -1
  87. package/dist/gui/EFFitScale.d.ts +2 -2
  88. package/dist/gui/TWMixin.js +1 -1
  89. package/dist/gui/services/ElementConnectionManager.browsertest.d.ts +1 -0
  90. package/dist/gui/services/ElementConnectionManager.d.ts +59 -0
  91. package/dist/gui/services/ElementConnectionManager.js +128 -0
  92. package/dist/gui/services/PlaybackController.browsertest.d.ts +1 -0
  93. package/dist/gui/services/PlaybackController.d.ts +103 -0
  94. package/dist/gui/services/PlaybackController.js +290 -0
  95. package/dist/services/MediaSourceManager.d.ts +62 -0
  96. package/dist/services/MediaSourceManager.js +211 -0
  97. package/dist/style.css +1 -1
  98. package/dist/transcoding/cache/CacheManager.d.ts +73 -0
  99. package/dist/transcoding/cache/RequestDeduplicator.d.ts +29 -0
  100. package/dist/transcoding/cache/RequestDeduplicator.js +53 -0
  101. package/dist/transcoding/cache/RequestDeduplicator.test.d.ts +1 -0
  102. package/dist/transcoding/types/index.d.ts +242 -0
  103. package/dist/transcoding/utils/MediaUtils.d.ts +9 -0
  104. package/dist/transcoding/utils/UrlGenerator.d.ts +26 -0
  105. package/dist/transcoding/utils/UrlGenerator.js +45 -0
  106. package/dist/transcoding/utils/constants.d.ts +27 -0
  107. package/dist/utils/LRUCache.d.ts +34 -0
  108. package/dist/utils/LRUCache.js +115 -0
  109. package/package.json +3 -2
  110. package/src/elements/EFAudio.browsertest.ts +183 -43
  111. package/src/elements/EFAudio.ts +59 -13
  112. package/src/elements/EFImage.browsertest.ts +42 -0
  113. package/src/elements/EFImage.ts +23 -3
  114. package/src/elements/EFMedia/AssetIdMediaEngine.test.ts +222 -0
  115. package/src/elements/EFMedia/AssetIdMediaEngine.ts +70 -0
  116. package/src/elements/EFMedia/AssetMediaEngine.ts +210 -0
  117. package/src/elements/EFMedia/BaseMediaEngine.test.ts +164 -0
  118. package/src/elements/EFMedia/BaseMediaEngine.ts +170 -0
  119. package/src/elements/EFMedia/BufferedSeekingInput.browsertest.ts +400 -0
  120. package/src/elements/EFMedia/BufferedSeekingInput.ts +267 -0
  121. package/src/elements/EFMedia/JitMediaEngine.browsertest.ts +165 -0
  122. package/src/elements/EFMedia/JitMediaEngine.ts +110 -0
  123. package/src/elements/EFMedia/audioTasks/makeAudioBufferTask.browsertest.ts +554 -0
  124. package/src/elements/EFMedia/audioTasks/makeAudioBufferTask.ts +81 -0
  125. package/src/elements/EFMedia/audioTasks/makeAudioFrequencyAnalysisTask.ts +241 -0
  126. package/src/elements/EFMedia/audioTasks/makeAudioInitSegmentFetchTask.browsertest.ts +59 -0
  127. package/src/elements/EFMedia/audioTasks/makeAudioInitSegmentFetchTask.ts +23 -0
  128. package/src/elements/EFMedia/audioTasks/makeAudioInputTask.browsertest.ts +55 -0
  129. package/src/elements/EFMedia/audioTasks/makeAudioInputTask.ts +35 -0
  130. package/src/elements/EFMedia/audioTasks/makeAudioSeekTask.ts +42 -0
  131. package/src/elements/EFMedia/audioTasks/makeAudioSegmentFetchTask.ts +34 -0
  132. package/src/elements/EFMedia/audioTasks/makeAudioSegmentIdTask.ts +23 -0
  133. package/src/elements/EFMedia/audioTasks/makeAudioTimeDomainAnalysisTask.ts +174 -0
  134. package/src/elements/EFMedia/services/AudioElementFactory.browsertest.ts +325 -0
  135. package/src/elements/EFMedia/services/AudioElementFactory.ts +119 -0
  136. package/src/elements/EFMedia/services/MediaSourceService.browsertest.ts +257 -0
  137. package/src/elements/EFMedia/services/MediaSourceService.ts +102 -0
  138. package/src/elements/EFMedia/shared/AudioSpanUtils.ts +128 -0
  139. package/src/elements/EFMedia/shared/BufferUtils.ts +310 -0
  140. package/src/elements/EFMedia/shared/MediaTaskUtils.ts +44 -0
  141. package/src/elements/EFMedia/shared/RenditionHelpers.browsertest.ts +247 -0
  142. package/src/elements/EFMedia/shared/RenditionHelpers.ts +79 -0
  143. package/src/elements/EFMedia/tasks/makeMediaEngineTask.browsertest.ts +128 -0
  144. package/src/elements/EFMedia/tasks/makeMediaEngineTask.test.ts +233 -0
  145. package/src/elements/EFMedia/tasks/makeMediaEngineTask.ts +89 -0
  146. package/src/elements/EFMedia/videoTasks/makeVideoBufferTask.browsertest.ts +555 -0
  147. package/src/elements/EFMedia/videoTasks/makeVideoBufferTask.ts +79 -0
  148. package/src/elements/EFMedia/videoTasks/makeVideoInitSegmentFetchTask.browsertest.ts +59 -0
  149. package/src/elements/EFMedia/videoTasks/makeVideoInitSegmentFetchTask.ts +23 -0
  150. package/src/elements/EFMedia/videoTasks/makeVideoInputTask.browsertest.ts +55 -0
  151. package/src/elements/EFMedia/videoTasks/makeVideoInputTask.ts +45 -0
  152. package/src/elements/EFMedia/videoTasks/makeVideoSeekTask.ts +44 -0
  153. package/src/elements/EFMedia/videoTasks/makeVideoSegmentFetchTask.browsertest.ts +57 -0
  154. package/src/elements/EFMedia/videoTasks/makeVideoSegmentFetchTask.ts +32 -0
  155. package/src/elements/EFMedia/videoTasks/makeVideoSegmentIdTask.browsertest.ts +56 -0
  156. package/src/elements/EFMedia/videoTasks/makeVideoSegmentIdTask.ts +23 -0
  157. package/src/elements/EFMedia.browsertest.ts +658 -265
  158. package/src/elements/EFMedia.ts +173 -1763
  159. package/src/elements/EFTemporal.ts +3 -4
  160. package/src/elements/EFTimegroup.browsertest.ts +6 -3
  161. package/src/elements/EFTimegroup.ts +152 -21
  162. package/src/elements/EFVideo.browsertest.ts +115 -37
  163. package/src/elements/EFVideo.ts +123 -452
  164. package/src/elements/EFWaveform.ts +1 -1
  165. package/src/elements/MediaController.ts +2 -12
  166. package/src/elements/SampleBuffer.ts +97 -0
  167. package/src/gui/ContextMixin.ts +23 -104
  168. package/src/gui/services/ElementConnectionManager.browsertest.ts +263 -0
  169. package/src/gui/services/ElementConnectionManager.ts +224 -0
  170. package/src/gui/services/PlaybackController.browsertest.ts +437 -0
  171. package/src/gui/services/PlaybackController.ts +521 -0
  172. package/src/services/MediaSourceManager.ts +333 -0
  173. package/src/transcoding/cache/CacheManager.ts +208 -0
  174. package/src/transcoding/cache/RequestDeduplicator.test.ts +170 -0
  175. package/src/transcoding/cache/RequestDeduplicator.ts +65 -0
  176. package/src/transcoding/types/index.ts +265 -0
  177. package/src/transcoding/utils/MediaUtils.ts +63 -0
  178. package/src/transcoding/utils/UrlGenerator.ts +68 -0
  179. package/src/transcoding/utils/constants.ts +36 -0
  180. package/src/utils/LRUCache.ts +153 -0
  181. package/test/EFVideo.framegen.browsertest.ts +38 -29
  182. package/test/__cache__/GET__api_v1_transcode_audio_1_m4s_url_http_3A_2F_2Fweb_3A3000_2Fhead_moov_480p_mp4__32da3954ba60c96ad732020c65a08ebc/data.bin +0 -0
  183. package/test/__cache__/GET__api_v1_transcode_audio_1_m4s_url_http_3A_2F_2Fweb_3A3000_2Fhead_moov_480p_mp4__32da3954ba60c96ad732020c65a08ebc/metadata.json +21 -0
  184. package/test/__cache__/GET__api_v1_transcode_audio_2_m4s_url_http_3A_2F_2Fweb_3A3000_2Fhead_moov_480p_mp4__b0b2b07efcf607de8ee0f650328c32f7/data.bin +0 -0
  185. package/test/__cache__/GET__api_v1_transcode_audio_2_m4s_url_http_3A_2F_2Fweb_3A3000_2Fhead_moov_480p_mp4__b0b2b07efcf607de8ee0f650328c32f7/metadata.json +21 -0
  186. package/test/__cache__/GET__api_v1_transcode_audio_3_m4s_url_http_3A_2F_2Fweb_3A3000_2Fhead_moov_480p_mp4__a75c2252b542e0c152c780e9a8d7b154/data.bin +0 -0
  187. package/test/__cache__/GET__api_v1_transcode_audio_3_m4s_url_http_3A_2F_2Fweb_3A3000_2Fhead_moov_480p_mp4__a75c2252b542e0c152c780e9a8d7b154/metadata.json +21 -0
  188. package/test/__cache__/GET__api_v1_transcode_audio_4_m4s_url_http_3A_2F_2Fweb_3A3000_2Fhead_moov_480p_mp4__a64ff1cfb1b52cae14df4b5dfa1e222b/data.bin +0 -0
  189. package/test/__cache__/GET__api_v1_transcode_audio_4_m4s_url_http_3A_2F_2Fweb_3A3000_2Fhead_moov_480p_mp4__a64ff1cfb1b52cae14df4b5dfa1e222b/metadata.json +21 -0
  190. package/test/__cache__/GET__api_v1_transcode_audio_5_m4s_url_http_3A_2F_2Fweb_3A3000_2Fhead_moov_480p_mp4__91e8a522f950809b9f09f4173113b4b0/data.bin +0 -0
  191. package/test/__cache__/GET__api_v1_transcode_audio_5_m4s_url_http_3A_2F_2Fweb_3A3000_2Fhead_moov_480p_mp4__91e8a522f950809b9f09f4173113b4b0/metadata.json +21 -0
  192. package/test/__cache__/GET__api_v1_transcode_audio_init_m4s_url_http_3A_2F_2Fweb_3A3000_2Fhead_moov_480p_mp4__e66d2c831d951e74ad0aeaa6489795d0/data.bin +0 -0
  193. package/test/__cache__/GET__api_v1_transcode_audio_init_m4s_url_http_3A_2F_2Fweb_3A3000_2Fhead_moov_480p_mp4__e66d2c831d951e74ad0aeaa6489795d0/metadata.json +21 -0
  194. package/test/__cache__/GET__api_v1_transcode_high_1_m4s_url_http_3A_2F_2Fweb_3A3000_2Fhead_moov_480p_mp4__26197f6f7c46cacb0a71134131c3f775/data.bin +0 -0
  195. package/test/__cache__/GET__api_v1_transcode_high_1_m4s_url_http_3A_2F_2Fweb_3A3000_2Fhead_moov_480p_mp4__26197f6f7c46cacb0a71134131c3f775/metadata.json +21 -0
  196. package/test/__cache__/GET__api_v1_transcode_high_2_m4s_url_http_3A_2F_2Fweb_3A3000_2Fhead_moov_480p_mp4__4cb6774cd3650ccf59c8f8dc6678c0b9/data.bin +0 -0
  197. package/test/__cache__/GET__api_v1_transcode_high_2_m4s_url_http_3A_2F_2Fweb_3A3000_2Fhead_moov_480p_mp4__4cb6774cd3650ccf59c8f8dc6678c0b9/metadata.json +21 -0
  198. package/test/__cache__/GET__api_v1_transcode_high_3_m4s_url_http_3A_2F_2Fweb_3A3000_2Fhead_moov_480p_mp4__0b3b2b1c8933f7fcf8a9ecaa88d58b41/data.bin +0 -0
  199. package/test/__cache__/GET__api_v1_transcode_high_3_m4s_url_http_3A_2F_2Fweb_3A3000_2Fhead_moov_480p_mp4__0b3b2b1c8933f7fcf8a9ecaa88d58b41/metadata.json +21 -0
  200. package/test/__cache__/GET__api_v1_transcode_high_init_m4s_url_http_3A_2F_2Fweb_3A3000_2Fhead_moov_480p_mp4__0798c479b44aaeef850609a430f6e613/data.bin +0 -0
  201. package/test/__cache__/GET__api_v1_transcode_high_init_m4s_url_http_3A_2F_2Fweb_3A3000_2Fhead_moov_480p_mp4__0798c479b44aaeef850609a430f6e613/metadata.json +21 -0
  202. package/test/__cache__/GET__api_v1_transcode_manifest_json_url_http_3A_2F_2Fweb_3A3000_2Fhead_moov_480p_mp4__3be92a0437de726b431ed5af2369158a/data.bin +1 -0
  203. package/test/__cache__/GET__api_v1_transcode_manifest_json_url_http_3A_2F_2Fweb_3A3000_2Fhead_moov_480p_mp4__3be92a0437de726b431ed5af2369158a/metadata.json +19 -0
  204. package/test/createJitTestClips.ts +320 -188
  205. package/test/recordReplayProxyPlugin.js +302 -0
  206. package/test/useAssetMSW.ts +1 -1
  207. package/test/useMSW.ts +35 -22
  208. package/types.json +1 -1
  209. package/dist/JitTranscodingClient.d.ts +0 -167
  210. package/dist/JitTranscodingClient.js +0 -373
  211. package/dist/ScrubTrackManager.js +0 -216
  212. package/dist/elements/printTaskStatus.js +0 -11
  213. package/src/elements/__screenshots__/EFMedia.browsertest.ts/EFMedia-JIT-audio-playback-audioBufferTask-should-work-in-JIT-mode-without-URL-errors-1.png +0 -0
  214. package/test/EFVideo.frame-tasks.browsertest.ts +0 -524
  215. /package/dist/{JitTranscodingClient.browsertest.d.ts → elements/EFMedia/AssetIdMediaEngine.test.d.ts} +0 -0
  216. /package/dist/{JitTranscodingClient.test.d.ts → elements/EFMedia/BaseMediaEngine.test.d.ts} +0 -0
  217. /package/dist/{ScrubTrackIntegration.test.d.ts → elements/EFMedia/BufferedSeekingInput.browsertest.d.ts} +0 -0
  218. /package/dist/{SegmentSwitchLoading.test.d.ts → elements/EFMedia/services/AudioElementFactory.browsertest.d.ts} +0 -0
@@ -1,14 +1,19 @@
1
- import { VideoAsset } from "@editframe/assets/EncodedAsset.js";
2
1
  import { Task } from "@lit/task";
3
2
  import debug from "debug";
4
3
  import { css, html, type PropertyValueMap } from "lit";
5
- import { customElement, state } from "lit/decorators.js";
4
+ import { customElement, property, state } from "lit/decorators.js";
6
5
  import { createRef, ref } from "lit/directives/ref.js";
6
+
7
7
  import { DelayedLoadingState } from "../DelayedLoadingState.js";
8
8
  import { TWMixin } from "../gui/TWMixin.js";
9
- import { type CacheStats, ScrubTrackManager } from "../ScrubTrackManager.js";
9
+ import type { CacheStats, ScrubTrackManager } from "../ScrubTrackManager.js";
10
+ import { makeVideoBufferTask } from "./EFMedia/videoTasks/makeVideoBufferTask.ts";
11
+ import { makeVideoInitSegmentFetchTask } from "./EFMedia/videoTasks/makeVideoInitSegmentFetchTask.ts";
12
+ import { makeVideoInputTask } from "./EFMedia/videoTasks/makeVideoInputTask.ts";
13
+ import { makeVideoSeekTask } from "./EFMedia/videoTasks/makeVideoSeekTask.ts";
14
+ import { makeVideoSegmentFetchTask } from "./EFMedia/videoTasks/makeVideoSegmentFetchTask.ts";
15
+ import { makeVideoSegmentIdTask } from "./EFMedia/videoTasks/makeVideoSegmentIdTask.ts";
10
16
  import { EFMedia } from "./EFMedia.js";
11
- import { printTaskStatus } from "./printTaskStatus.ts";
12
17
 
13
18
  // EF_FRAMEGEN is a global instance created in EF_FRAMEGEN.ts
14
19
  declare global {
@@ -25,6 +30,16 @@ interface LoadingState {
25
30
 
26
31
  @customElement("ef-video")
27
32
  export class EFVideo extends TWMixin(EFMedia) {
33
+ static get observedAttributes() {
34
+ const parentAttributes = EFMedia.observedAttributes || [];
35
+ return [
36
+ ...parentAttributes,
37
+ "video-buffer-duration",
38
+ "max-video-buffer-fetches",
39
+ "enable-video-buffering",
40
+ ];
41
+ }
42
+
28
43
  static styles = [
29
44
  /**
30
45
  *
@@ -92,14 +107,38 @@ export class EFVideo extends TWMixin(EFMedia) {
92
107
  canvasRef = createRef<HTMLCanvasElement>();
93
108
 
94
109
  /**
95
- * Scrub track manager for fast timeline navigation
110
+ * Duration in milliseconds for video buffering ahead of current time
111
+ * @domAttribute "video-buffer-duration"
96
112
  */
97
- scrubTrackManager?: ScrubTrackManager;
113
+ @property({ type: Number, attribute: "video-buffer-duration" })
114
+ videoBufferDurationMs = 60000; // 60 seconds
98
115
 
99
116
  /**
100
- * Track last seek time for fast seeking detection
117
+ * Maximum number of concurrent video segment fetches for buffering
118
+ * @domAttribute "max-video-buffer-fetches"
101
119
  */
102
- private lastSeekTimeMs = 0;
120
+ @property({ type: Number, attribute: "max-video-buffer-fetches" })
121
+ maxVideoBufferFetches = 2;
122
+
123
+ /**
124
+ * Enable/disable video buffering system
125
+ * @domAttribute "enable-video-buffering"
126
+ */
127
+ @property({ type: Boolean, attribute: "enable-video-buffering" })
128
+ enableVideoBuffering = true;
129
+
130
+ // Video-specific tasks
131
+ videoSegmentIdTask = makeVideoSegmentIdTask(this);
132
+ videoInitSegmentFetchTask = makeVideoInitSegmentFetchTask(this);
133
+ videoSegmentFetchTask = makeVideoSegmentFetchTask(this);
134
+ videoInputTask = makeVideoInputTask(this);
135
+ videoSeekTask = makeVideoSeekTask(this);
136
+ videoBufferTask = makeVideoBufferTask(this);
137
+
138
+ /**
139
+ * Scrub track manager for fast timeline navigation
140
+ */
141
+ scrubTrackManager?: ScrubTrackManager;
103
142
 
104
143
  /**
105
144
  * Delayed loading state manager for user feedback
@@ -153,35 +192,14 @@ export class EFVideo extends TWMixin(EFMedia) {
153
192
  return this.canvasRef.value;
154
193
  }
155
194
 
156
- // The underlying video decoder MUST NOT be used concurrently.
157
- // If frames are fed in out of order, the decoder may crash.
158
- #decoderLock = false;
159
-
160
- // Track if decoder needs reset due to errors
161
- #decoderNeedsReset = false;
162
-
163
195
  frameTask = new Task(this, {
164
196
  args: () => [this.desiredSeekTimeMs] as const,
165
197
  onError: (error) => {
166
198
  console.error("frameTask error", error);
167
199
  },
200
+ onComplete: () => {},
168
201
  task: async ([_desiredSeekTimeMs], { signal }) => {
169
- await this.seekTask.taskComplete;
170
- if (signal.aborted) {
171
- return;
172
- }
173
- await this.fragmentIndexTask.taskComplete;
174
- if (signal.aborted) {
175
- return;
176
- }
177
- await this.mediaSegmentsTask.taskComplete;
178
- if (signal.aborted) {
179
- return;
180
- }
181
- await this.videoAssetTask.taskComplete;
182
- if (signal.aborted) {
183
- return;
184
- }
202
+ await this.videoSeekTask.taskComplete;
185
203
  await this.paintTask.taskComplete;
186
204
  if (signal.aborted) {
187
205
  return;
@@ -189,70 +207,10 @@ export class EFVideo extends TWMixin(EFMedia) {
189
207
  },
190
208
  });
191
209
 
192
- get frameTaskStatus() {
193
- return {
194
- desiredSeekTimeMs: this.desiredSeekTimeMs,
195
- fragmentIndexTask: printTaskStatus(this.fragmentIndexTask.status),
196
- seekTask: printTaskStatus(this.seekTask.status),
197
- mediaSegmentsTask: printTaskStatus(this.mediaSegmentsTask.status),
198
- assetSegmentLoader: printTaskStatus(this.assetSegmentLoader.status),
199
- assetSegmentKeysTask: printTaskStatus(this.assetSegmentKeysTask.status),
200
- assetInitSegmentsTask: printTaskStatus(this.assetInitSegmentsTask.status),
201
- videoAssetTask: printTaskStatus(this.videoAssetTask.status),
202
- paintTask: printTaskStatus(this.paintTask.status),
203
- frameTask: printTaskStatus(this.frameTask.status),
204
- };
205
- }
206
-
207
- #lastVideoAsset: any = null;
208
-
209
210
  protected updated(
210
211
  changedProperties: PropertyValueMap<any> | Map<PropertyKey, unknown>,
211
212
  ): void {
212
213
  super.updated(changedProperties);
213
-
214
- const currentVideoAsset = this.videoAssetTask.value;
215
- if (currentVideoAsset !== this.#lastVideoAsset) {
216
- // Track video asset changes for reference, but don't reset decoder
217
- // Decoder resets should only happen due to actual decoder errors, not normal asset transitions
218
- this.#lastVideoAsset = currentVideoAsset;
219
- }
220
-
221
- // Initialize scrub track manager for JIT transcode mode
222
- this.initializeScrubTrackManager();
223
- }
224
-
225
- /**
226
- * Initialize scrub track manager if needed
227
- */
228
- private async initializeScrubTrackManager(): Promise<void> {
229
- const mode = this.effectiveMode;
230
-
231
- // Only initialize for JIT transcode mode with valid src
232
- if (mode === "jit-transcode" && this.src && !this.scrubTrackManager) {
233
- const jitClient = this.jitClientTask.value;
234
- if (jitClient) {
235
- try {
236
- this.scrubTrackManager = new ScrubTrackManager(this.src, jitClient, {
237
- onLoadingStateChange: (isLoading: boolean, message?: string) => {
238
- if (isLoading) {
239
- // Only show loading for user-visible operations (non-background)
240
- this.startDelayedLoading(
241
- "scrub-segment",
242
- message || "Loading scrub track...",
243
- );
244
- } else {
245
- this.clearDelayedLoading("scrub-segment");
246
- }
247
- },
248
- });
249
-
250
- await this.scrubTrackManager.initialize();
251
- } catch (error) {
252
- console.warn("Failed to initialize scrub track manager:", error);
253
- }
254
- }
255
- }
256
214
  }
257
215
 
258
216
  /**
@@ -288,107 +246,24 @@ export class EFVideo extends TWMixin(EFMedia) {
288
246
  };
289
247
  }
290
248
 
291
- videoAssetTask = new Task(this, {
292
- autoRun: true,
293
- args: () => [this.effectiveMode, this.mediaSegmentsTask.value] as const,
294
- onError: (error) => {
295
- console.error("videoAsset task error", error);
296
- },
297
- task: async ([mode, _files], { signal: _signal }) => {
298
- await this.mediaSegmentsTask.taskComplete;
299
- if (_signal.aborted) {
300
- return undefined;
301
- }
302
-
303
- await this.fragmentIndexTask.taskComplete;
304
- if (_signal.aborted) {
305
- return undefined;
306
- }
307
-
308
- // Get fresh values
309
- const files = this.mediaSegmentsTask.value;
310
- const fragmentIndex = this.fragmentIndexTask.value;
311
-
312
- if (!files) {
313
- log("trace: videoAsset task aborted - no files");
314
- throw new Error(
315
- `Video asset creation failed: No media segment files available. This indicates a problem with media segment loading for source: "${this.src}"`,
316
- );
317
- }
318
-
319
- const computedVideoTrackId = Object.values(fragmentIndex ?? {}).find(
320
- (track) => track.type === "video",
321
- )?.track;
322
-
323
- if (computedVideoTrackId === undefined) {
324
- log("trace: videoAsset task aborted - no video track");
325
- throw new Error(
326
- `Video asset creation failed: No video track found in media segments. Source may not contain video content: "${this.src}"`,
327
- );
328
- }
329
-
330
- const videoFile = files[computedVideoTrackId];
331
- if (!videoFile) {
332
- log("trace: videoAsset task aborted - no video file");
333
- throw new Error(
334
- `Video asset creation failed: Video file not available for track ${computedVideoTrackId}. Media segment loading may have failed for source: "${this.src}"`,
335
- );
336
- }
337
-
338
- // Cleanup existing asset
339
- const existingAsset = this.videoAssetTask.value;
340
- if (existingAsset) {
341
- for (const frame of existingAsset?.decodedFrames || []) {
342
- frame.close();
343
- }
344
- const decoder = existingAsset?.videoDecoder;
345
- if (decoder && decoder.state !== "closed") {
346
- decoder.close();
347
- }
348
- }
349
-
350
- if (_signal.aborted) {
351
- return undefined;
352
- }
353
-
354
- log("trace: creating video asset", { mode });
355
-
356
- // Get start time offset from fragment index (timing correction for FFmpeg processing)
357
- const videoTrackFragmentIndex = Object.values(fragmentIndex ?? {}).find(
358
- (track) => track.type === "video",
359
- );
360
- const startTimeOffsetMs = Number(
361
- (videoTrackFragmentIndex?.startTimeOffsetMs ?? 0).toFixed(5),
362
- );
363
-
364
- // Single branching point for creation method
365
- if (mode === "jit-transcode") {
366
- const result = await VideoAsset.createFromCompleteMP4(
367
- `jit-segment-${computedVideoTrackId}`,
368
- videoFile,
369
- { startTimeOffsetMs },
370
- );
371
- return result;
372
- }
373
- const result = await VideoAsset.createFromReadableStream(
374
- "video.mp4",
375
- videoFile.stream(),
376
- videoFile,
377
- { startTimeOffsetMs },
378
- );
379
- return result;
380
- },
381
- });
382
-
383
249
  paintTask = new Task(this, {
384
250
  args: () => [this.desiredSeekTimeMs] as const,
385
251
  onError: (error) => {
386
252
  console.error("paintTask error", error);
387
253
  },
254
+ onComplete: () => {},
388
255
  task: async ([_seekToMs], { signal }) => {
256
+ await this.videoSeekTask.taskComplete;
389
257
  // Check if we're in production rendering mode vs preview mode
390
258
  const isProductionRendering = this.isInProductionRenderingMode();
391
259
 
260
+ const sample = this.videoSeekTask.value;
261
+ if (sample) {
262
+ const videoFrame = sample.toVideoFrame();
263
+ this.displayFrame(videoFrame, _seekToMs);
264
+ videoFrame.close();
265
+ }
266
+
392
267
  // EF_FRAMEGEN-aware rendering mode detection
393
268
  if (!isProductionRendering) {
394
269
  // Preview mode: skip rendering during initialization to prevent artifacts
@@ -417,277 +292,9 @@ export class EFVideo extends TWMixin(EFMedia) {
417
292
  if (signal.aborted) {
418
293
  return;
419
294
  }
420
-
421
- // CRITICAL: For segment transitions, ensure we wait for the correct mediaSegmentsTask
422
- // This prevents using stale VideoAssets from previous segments
423
-
424
- await this.mediaSegmentsTask.taskComplete;
425
- if (signal.aborted) {
426
- return;
427
- }
428
-
429
- // CRITICAL: Always await fresh videoAsset just before using it
430
- // This prevents race conditions where old VideoAssets with closed decoders are used
431
- await this.videoAssetTask.taskComplete;
432
- if (signal.aborted) {
433
- return;
434
- }
435
-
436
- // Get fresh values after await - ensures we use current VideoAsset
437
- const videoAsset = this.videoAssetTask.value;
438
- const currentSeekToMs = this.desiredSeekTimeMs; // Use current seek time, not captured
439
-
440
- if (!videoAsset) {
441
- log("trace: paintTask aborted - no video asset");
442
- throw new Error(
443
- `Frame rendering failed: No video asset available. This may indicate a problem with video loading or an invalid source: "${this.src}"`,
444
- );
445
- }
446
-
447
- // Check if decoder needs reset due to previous errors
448
- if (this.#decoderNeedsReset) {
449
- try {
450
- // Reset the video decoder
451
- if (videoAsset?.videoDecoder) {
452
- videoAsset.configureDecoder();
453
- } else {
454
- console.warn("No video decoder available for reset");
455
- }
456
-
457
- // Clear the flag after successful reset
458
- this.#decoderNeedsReset = false;
459
- } catch (resetError) {
460
- console.error("reset error", resetError);
461
- // Keep the flag set if reset fails
462
- throw new Error(
463
- `Frame rendering failed: Unable to reset video decoder after previous error. Decoder state: ${resetError instanceof Error ? resetError.message : "Unknown error"}. Try refreshing the page or reloading the video.`,
464
- );
465
- }
466
- }
467
- if (signal.aborted) {
468
- return;
469
- }
470
-
471
- if (this.#decoderLock) {
472
- return;
473
- }
474
-
475
- try {
476
- this.#decoderLock = true;
477
-
478
- // Validate VideoAsset is still current and decoder is in valid state
479
- const currentVideoAsset = this.videoAssetTask.value;
480
- if (videoAsset !== currentVideoAsset) {
481
- return; // Skip render with stale videoAsset
482
- }
483
-
484
- // Check decoder state before using it
485
- const decoderState = videoAsset?.videoDecoder?.state;
486
- if (decoderState === "closed") {
487
- return; // Skip render with closed decoder
488
- }
489
-
490
- // Try scrub track first for JIT transcode mode
491
- if (this.effectiveMode === "jit-transcode" && this.scrubTrackManager) {
492
- const shouldUseScrub =
493
- this.scrubTrackManager.shouldUseScrubTrack(currentSeekToMs);
494
- const isFastSeeking = this.scrubTrackManager.isFastSeeking(
495
- this.lastSeekTimeMs,
496
- currentSeekToMs,
497
- );
498
-
499
- if (false || shouldUseScrub || isFastSeeking) {
500
- try {
501
- // Use delayed loading instead of immediate loading
502
- this.startDelayedLoading(
503
- "scrub-segment-load",
504
- "Loading scrub segment...",
505
- );
506
-
507
- const scrubFrame =
508
- await this.scrubTrackManager.getScrubFrame(currentSeekToMs);
509
-
510
- if (scrubFrame && this.canvasElement) {
511
- this.scrubTrackManager.recordCacheMiss();
512
- this.lastSeekTimeMs = currentSeekToMs;
513
-
514
- // Clear loading and display scrub frame
515
- this.clearDelayedLoading("scrub-segment-load");
516
- return this.displayFrame(scrubFrame, currentSeekToMs);
517
- }
518
-
519
- // Scrub frame was null/failed - fall back to normal video
520
- console.warn(
521
- "Scrub track returned null frame, falling back to normal video",
522
- );
523
- this.clearDelayedLoading("scrub-segment-load");
524
- this.startDelayedLoading(
525
- "video-segment-fallback",
526
- "Loading high quality video...",
527
- );
528
- } catch (error) {
529
- this.clearDelayedLoading("scrub-segment-load");
530
- console.warn(
531
- "Scrub track failed, falling back to normal video:",
532
- error,
533
- );
534
-
535
- // Show loading for normal video fallback
536
- this.startDelayedLoading(
537
- "video-segment-fallback",
538
- "Loading high quality video...",
539
- );
540
- }
541
- } else {
542
- // Cache hit for normal video - scrub track manager exists, record the hit
543
- this.scrubTrackManager?.recordCacheHit();
544
- }
545
- }
546
-
547
- // Normal video rendering path (for all cases where scrub track isn't used)
548
- // Check if we need to show loading for normal video operations
549
- const shouldShowLoading =
550
- !this.delayedLoadingState.isLoading &&
551
- (this.effectiveMode !== "asset" || !videoAsset);
552
-
553
- if (shouldShowLoading) {
554
- this.startDelayedLoading("video-segment", "Loading video segment...");
555
- }
556
-
557
- // Render normal video - pass fresh VideoAsset reference
558
- this.lastSeekTimeMs = currentSeekToMs;
559
- const result = await this.renderNormalVideo(
560
- videoAsset,
561
- currentSeekToMs,
562
- );
563
-
564
- // Clear loading state after normal video renders
565
- this.clearDelayedLoading("video-segment");
566
- this.clearDelayedLoading("video-segment-fallback");
567
-
568
- return result;
569
- } catch (error) {
570
- // Clear all loading states on error
571
- this.clearDelayedLoading("scrub-segment-load");
572
- this.clearDelayedLoading("video-segment");
573
- this.clearDelayedLoading("video-segment-fallback");
574
-
575
- // Handle errors with proper error propagation
576
- if (error instanceof Error) {
577
- if (
578
- error.name === "DataError" &&
579
- error.message.includes("key frame is required")
580
- ) {
581
- console.warn(
582
- "Decoder reset during VideoAsset due to key frame requirement",
583
- );
584
- this.#decoderNeedsReset = true;
585
-
586
- if (this.effectiveMode === "jit-transcode") {
587
- this.requestUpdate();
588
- }
589
- throw error;
590
- }
591
- if (error.name === "AbortError") {
592
- // AbortError is expected behavior when tasks are cancelled
593
- throw new Error(
594
- "Frame rendering cancelled: Operation was aborted, likely due to a new seek request or component unmounting.",
595
- );
596
- }
597
- if (
598
- error.message.includes("VideoAsset decoder closed") ||
599
- error.message.includes("recreation in progress")
600
- ) {
601
- // This is now expected behavior during VideoAsset transitions - don't treat as error
602
- return; // Gracefully abort instead of throwing
603
- }
604
- if (
605
- error.name === "InvalidStateError" &&
606
- error.message.includes("closed codec")
607
- ) {
608
- // Expected during VideoAsset recreation - gracefully abort
609
- return; // Gracefully abort instead of throwing
610
- }
611
- console.warn("Decoder reset during VideoAsset recreation", error);
612
- this.#decoderNeedsReset = true;
613
- throw error;
614
- }
615
-
616
- // For non-Error objects, still provide descriptive error
617
- throw new Error(
618
- `Frame rendering failed: Unknown error during video rendering at ${currentSeekToMs}ms. Error: ${String(error)}`,
619
- );
620
- } finally {
621
- this.#decoderLock = false;
622
- }
623
295
  },
624
296
  });
625
297
 
626
- /**
627
- * Render normal video using existing logic
628
- */
629
- private async renderNormalVideo(
630
- videoAsset: VideoAsset,
631
- seekToMs: number,
632
- ): Promise<number> {
633
- let targetSeekTimeSeconds = seekToMs / 1000;
634
-
635
- try {
636
- // Validate VideoAsset is still current before seeking
637
- const currentVideoAsset = this.videoAssetTask.value;
638
- if (videoAsset !== currentVideoAsset) {
639
- throw new Error(
640
- "VideoAsset decoder closed during seek - recreation in progress",
641
- );
642
- }
643
-
644
- // Check decoder state immediately before seeking
645
- const decoderState = videoAsset?.videoDecoder?.state;
646
- if (decoderState === "closed") {
647
- throw new Error(
648
- "VideoAsset decoder closed during seek - recreation in progress",
649
- );
650
- }
651
- if (this.effectiveMode === "jit-transcode") {
652
- targetSeekTimeSeconds %= 2;
653
- }
654
-
655
- const frame = await videoAsset.seekToTime(targetSeekTimeSeconds);
656
-
657
- if (frame) {
658
- // Final validation that VideoAsset is still current before displaying
659
- const finalVideoAsset = this.videoAssetTask.value;
660
- if (videoAsset !== finalVideoAsset) {
661
- frame.close(); // Clean up the frame
662
- throw new Error(
663
- "VideoAsset decoder closed during seek - recreation in progress",
664
- );
665
- }
666
-
667
- // Read fresh time right before displaying - final safeguard against stale values
668
- const finalSeekToMs = this.desiredSeekTimeMs;
669
- return this.displayFrame(frame, finalSeekToMs);
670
- }
671
-
672
- log("trace: no frame returned from seekToTime");
673
- throw new Error(
674
- `Frame rendering failed: No frame available at time ${seekToMs}ms (${targetSeekTimeSeconds}s). This may indicate seeking beyond video duration, corrupted video data, or an incompatible video format.`,
675
- );
676
- } catch (error) {
677
- if (
678
- error instanceof Error &&
679
- (error.message.includes("VideoAsset decoder closed") ||
680
- error.message.includes("recreation in progress"))
681
- ) {
682
- // This is the expected narrow timing window race condition during VideoAsset transitions
683
- throw error; // Re-throw to let paintTask handle it gracefully
684
- }
685
-
686
- // Re-throw other unexpected errors
687
- throw error;
688
- }
689
- }
690
-
691
298
  /**
692
299
  * Display a video frame on the canvas
693
300
  */
@@ -729,7 +336,7 @@ export class EFVideo extends TWMixin(EFMedia) {
729
336
  );
730
337
  }
731
338
 
732
- log("trace: drawing frame to canvas");
339
+ log("trace: drawing frame to canvas", frame.timestamp / 1000);
733
340
  ctx.drawImage(
734
341
  frame,
735
342
  0,
@@ -792,6 +399,70 @@ export class EFVideo extends TWMixin(EFMedia) {
792
399
  return this.scrubTrackManager?.getCacheStats() || null;
793
400
  }
794
401
 
402
+ // Getter properties for backward compatibility with tests
403
+ /**
404
+ * Effective mode - always returns "asset" for EFVideo
405
+ */
406
+ get effectiveMode(): string {
407
+ return "asset";
408
+ }
409
+
410
+ /**
411
+ * Legacy getter for asset index loader (maps to mediaEngine task)
412
+ */
413
+ get assetIndexLoader() {
414
+ return this.mediaEngineTask;
415
+ }
416
+
417
+ /**
418
+ * Legacy getter for fragment index task (maps to videoSegmentIdTask)
419
+ */
420
+ get fragmentIndexTask() {
421
+ return this.videoSegmentIdTask;
422
+ }
423
+
424
+ /**
425
+ * Legacy getter for seek task (maps to videoSeekTask)
426
+ */
427
+ get seekTask() {
428
+ return this.videoSeekTask;
429
+ }
430
+
431
+ /**
432
+ * Legacy getter for media segments task (maps to videoSegmentFetchTask)
433
+ */
434
+ get mediaSegmentsTask() {
435
+ return this.videoSegmentFetchTask;
436
+ }
437
+
438
+ /**
439
+ * Legacy getter for asset segment keys task (maps to videoSegmentIdTask)
440
+ */
441
+ get assetSegmentKeysTask() {
442
+ return this.videoSegmentIdTask;
443
+ }
444
+
445
+ /**
446
+ * Legacy getter for asset init segments task (maps to videoInitSegmentFetchTask)
447
+ */
448
+ get assetInitSegmentsTask() {
449
+ return this.videoInitSegmentFetchTask;
450
+ }
451
+
452
+ /**
453
+ * Legacy getter for asset segment loader (maps to videoSegmentFetchTask)
454
+ */
455
+ get assetSegmentLoader() {
456
+ return this.videoSegmentFetchTask;
457
+ }
458
+
459
+ /**
460
+ * Legacy getter for video asset task (maps to videoBufferTask)
461
+ */
462
+ get videoAssetTask() {
463
+ return this.videoBufferTask;
464
+ }
465
+
795
466
  /**
796
467
  * Clean up resources when component is disconnected
797
468
  */
@@ -16,8 +16,8 @@ import { TargetController } from "./TargetController.ts";
16
16
  export class EFWaveform extends EFTemporal(TWMixin(LitElement)) {
17
17
  static styles = css`
18
18
  :host {
19
- display: block;
20
19
  all: inherit;
20
+ display: block;
21
21
  position: relative;
22
22
  }
23
23