@editframe/elements 0.20.4-beta.0 → 0.23.6-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 (183) hide show
  1. package/dist/DelayedLoadingState.js +0 -27
  2. package/dist/EF_FRAMEGEN.d.ts +5 -3
  3. package/dist/EF_FRAMEGEN.js +49 -11
  4. package/dist/_virtual/_@oxc-project_runtime@0.94.0/helpers/decorate.js +7 -0
  5. package/dist/attachContextRoot.d.ts +1 -0
  6. package/dist/attachContextRoot.js +9 -0
  7. package/dist/elements/ContextProxiesController.d.ts +1 -2
  8. package/dist/elements/EFAudio.js +5 -9
  9. package/dist/elements/EFCaptions.d.ts +1 -3
  10. package/dist/elements/EFCaptions.js +112 -129
  11. package/dist/elements/EFImage.js +6 -7
  12. package/dist/elements/EFMedia/AssetIdMediaEngine.js +2 -5
  13. package/dist/elements/EFMedia/AssetMediaEngine.js +36 -33
  14. package/dist/elements/EFMedia/BaseMediaEngine.js +57 -73
  15. package/dist/elements/EFMedia/BufferedSeekingInput.d.ts +1 -1
  16. package/dist/elements/EFMedia/BufferedSeekingInput.js +134 -78
  17. package/dist/elements/EFMedia/JitMediaEngine.js +9 -19
  18. package/dist/elements/EFMedia/audioTasks/makeAudioBufferTask.js +7 -13
  19. package/dist/elements/EFMedia/audioTasks/makeAudioFrequencyAnalysisTask.js +2 -3
  20. package/dist/elements/EFMedia/audioTasks/makeAudioInitSegmentFetchTask.js +1 -1
  21. package/dist/elements/EFMedia/audioTasks/makeAudioInputTask.js +6 -5
  22. package/dist/elements/EFMedia/audioTasks/makeAudioSeekTask.js +1 -3
  23. package/dist/elements/EFMedia/audioTasks/makeAudioSegmentFetchTask.js +1 -1
  24. package/dist/elements/EFMedia/audioTasks/makeAudioSegmentIdTask.js +1 -1
  25. package/dist/elements/EFMedia/audioTasks/makeAudioTimeDomainAnalysisTask.js +1 -1
  26. package/dist/elements/EFMedia/shared/AudioSpanUtils.js +9 -25
  27. package/dist/elements/EFMedia/shared/BufferUtils.js +2 -17
  28. package/dist/elements/EFMedia/shared/GlobalInputCache.js +0 -24
  29. package/dist/elements/EFMedia/shared/PrecisionUtils.js +0 -21
  30. package/dist/elements/EFMedia/shared/ThumbnailExtractor.js +0 -17
  31. package/dist/elements/EFMedia/tasks/makeMediaEngineTask.js +1 -10
  32. package/dist/elements/EFMedia/videoTasks/MainVideoInputCache.d.ts +29 -0
  33. package/dist/elements/EFMedia/videoTasks/MainVideoInputCache.js +32 -0
  34. package/dist/elements/EFMedia/videoTasks/ScrubInputCache.js +1 -15
  35. package/dist/elements/EFMedia/videoTasks/makeScrubVideoBufferTask.js +1 -7
  36. package/dist/elements/EFMedia/videoTasks/makeScrubVideoInputTask.js +8 -5
  37. package/dist/elements/EFMedia/videoTasks/makeScrubVideoSeekTask.js +12 -13
  38. package/dist/elements/EFMedia/videoTasks/makeScrubVideoSegmentIdTask.js +1 -1
  39. package/dist/elements/EFMedia/videoTasks/makeUnifiedVideoSeekTask.js +134 -70
  40. package/dist/elements/EFMedia/videoTasks/makeVideoBufferTask.js +11 -18
  41. package/dist/elements/EFMedia.d.ts +19 -0
  42. package/dist/elements/EFMedia.js +44 -25
  43. package/dist/elements/EFSourceMixin.js +5 -7
  44. package/dist/elements/EFSurface.js +6 -9
  45. package/dist/elements/EFTemporal.browsertest.d.ts +11 -0
  46. package/dist/elements/EFTemporal.d.ts +10 -0
  47. package/dist/elements/EFTemporal.js +100 -41
  48. package/dist/elements/EFThumbnailStrip.js +23 -73
  49. package/dist/elements/EFTimegroup.browsertest.d.ts +3 -3
  50. package/dist/elements/EFTimegroup.d.ts +35 -14
  51. package/dist/elements/EFTimegroup.js +138 -181
  52. package/dist/elements/EFVideo.d.ts +16 -2
  53. package/dist/elements/EFVideo.js +156 -108
  54. package/dist/elements/EFWaveform.js +23 -40
  55. package/dist/elements/SampleBuffer.js +3 -7
  56. package/dist/elements/TargetController.js +5 -5
  57. package/dist/elements/durationConverter.js +4 -4
  58. package/dist/elements/renderTemporalAudio.d.ts +10 -0
  59. package/dist/elements/renderTemporalAudio.js +35 -0
  60. package/dist/elements/updateAnimations.js +19 -43
  61. package/dist/gui/ContextMixin.d.ts +5 -5
  62. package/dist/gui/ContextMixin.js +167 -162
  63. package/dist/gui/Controllable.browsertest.d.ts +0 -0
  64. package/dist/gui/Controllable.d.ts +15 -0
  65. package/dist/gui/Controllable.js +9 -0
  66. package/dist/gui/EFConfiguration.js +7 -7
  67. package/dist/gui/EFControls.browsertest.d.ts +11 -0
  68. package/dist/gui/EFControls.d.ts +18 -4
  69. package/dist/gui/EFControls.js +70 -28
  70. package/dist/gui/EFDial.browsertest.d.ts +0 -0
  71. package/dist/gui/EFDial.d.ts +18 -0
  72. package/dist/gui/EFDial.js +141 -0
  73. package/dist/gui/EFFilmstrip.browsertest.d.ts +11 -0
  74. package/dist/gui/EFFilmstrip.d.ts +12 -2
  75. package/dist/gui/EFFilmstrip.js +214 -129
  76. package/dist/gui/EFFitScale.js +5 -8
  77. package/dist/gui/EFFocusOverlay.js +4 -4
  78. package/dist/gui/EFPause.browsertest.d.ts +0 -0
  79. package/dist/gui/EFPause.d.ts +23 -0
  80. package/dist/gui/EFPause.js +59 -0
  81. package/dist/gui/EFPlay.browsertest.d.ts +0 -0
  82. package/dist/gui/EFPlay.d.ts +23 -0
  83. package/dist/gui/EFPlay.js +59 -0
  84. package/dist/gui/EFPreview.d.ts +4 -0
  85. package/dist/gui/EFPreview.js +18 -9
  86. package/dist/gui/EFResizableBox.browsertest.d.ts +0 -0
  87. package/dist/gui/EFResizableBox.d.ts +34 -0
  88. package/dist/gui/EFResizableBox.js +547 -0
  89. package/dist/gui/EFScrubber.d.ts +9 -3
  90. package/dist/gui/EFScrubber.js +13 -13
  91. package/dist/gui/EFTimeDisplay.d.ts +7 -1
  92. package/dist/gui/EFTimeDisplay.js +8 -8
  93. package/dist/gui/EFToggleLoop.d.ts +9 -3
  94. package/dist/gui/EFToggleLoop.js +7 -5
  95. package/dist/gui/EFTogglePlay.d.ts +12 -4
  96. package/dist/gui/EFTogglePlay.js +26 -21
  97. package/dist/gui/EFWorkbench.js +5 -5
  98. package/dist/gui/PlaybackController.d.ts +67 -0
  99. package/dist/gui/PlaybackController.js +310 -0
  100. package/dist/gui/TWMixin.js +1 -1
  101. package/dist/gui/TWMixin2.js +1 -1
  102. package/dist/gui/TargetOrContextMixin.d.ts +10 -0
  103. package/dist/gui/TargetOrContextMixin.js +98 -0
  104. package/dist/gui/efContext.d.ts +2 -2
  105. package/dist/index.d.ts +5 -0
  106. package/dist/index.js +5 -1
  107. package/dist/otel/BridgeSpanExporter.d.ts +13 -0
  108. package/dist/otel/BridgeSpanExporter.js +87 -0
  109. package/dist/otel/setupBrowserTracing.d.ts +12 -0
  110. package/dist/otel/setupBrowserTracing.js +32 -0
  111. package/dist/otel/tracingHelpers.d.ts +34 -0
  112. package/dist/otel/tracingHelpers.js +112 -0
  113. package/dist/style.css +1 -1
  114. package/dist/transcoding/cache/RequestDeduplicator.js +0 -21
  115. package/dist/transcoding/cache/URLTokenDeduplicator.js +1 -21
  116. package/dist/transcoding/utils/UrlGenerator.js +2 -19
  117. package/dist/utils/LRUCache.js +6 -53
  118. package/package.json +13 -5
  119. package/src/elements/ContextProxiesController.ts +10 -10
  120. package/src/elements/EFAudio.ts +1 -0
  121. package/src/elements/EFCaptions.browsertest.ts +128 -56
  122. package/src/elements/EFCaptions.ts +60 -34
  123. package/src/elements/EFImage.browsertest.ts +1 -2
  124. package/src/elements/EFMedia/AssetMediaEngine.ts +65 -37
  125. package/src/elements/EFMedia/BaseMediaEngine.ts +110 -52
  126. package/src/elements/EFMedia/BufferedSeekingInput.ts +218 -101
  127. package/src/elements/EFMedia/JitMediaEngine.browsertest.ts +3 -0
  128. package/src/elements/EFMedia/audioTasks/makeAudioInputTask.ts +7 -3
  129. package/src/elements/EFMedia/audioTasks/makeAudioSeekTask.chunkboundary.regression.browsertest.ts +1 -1
  130. package/src/elements/EFMedia/videoTasks/MainVideoInputCache.ts +76 -0
  131. package/src/elements/EFMedia/videoTasks/makeScrubVideoInputTask.ts +16 -10
  132. package/src/elements/EFMedia/videoTasks/makeScrubVideoSeekTask.ts +7 -1
  133. package/src/elements/EFMedia/videoTasks/makeUnifiedVideoSeekTask.ts +222 -116
  134. package/src/elements/EFMedia.browsertest.ts +8 -15
  135. package/src/elements/EFMedia.ts +54 -8
  136. package/src/elements/EFSurface.browsertest.ts +2 -6
  137. package/src/elements/EFSurface.ts +1 -0
  138. package/src/elements/EFTemporal.browsertest.ts +58 -1
  139. package/src/elements/EFTemporal.ts +140 -4
  140. package/src/elements/EFThumbnailStrip.browsertest.ts +2 -8
  141. package/src/elements/EFThumbnailStrip.ts +1 -0
  142. package/src/elements/EFTimegroup.browsertest.ts +16 -15
  143. package/src/elements/EFTimegroup.ts +281 -275
  144. package/src/elements/EFVideo.browsertest.ts +162 -74
  145. package/src/elements/EFVideo.ts +229 -101
  146. package/src/elements/FetchContext.browsertest.ts +7 -2
  147. package/src/elements/TargetController.browsertest.ts +1 -0
  148. package/src/elements/TargetController.ts +1 -0
  149. package/src/elements/renderTemporalAudio.ts +108 -0
  150. package/src/elements/updateAnimations.browsertest.ts +181 -6
  151. package/src/elements/updateAnimations.ts +6 -6
  152. package/src/gui/ContextMixin.browsertest.ts +274 -27
  153. package/src/gui/ContextMixin.ts +230 -175
  154. package/src/gui/Controllable.browsertest.ts +258 -0
  155. package/src/gui/Controllable.ts +41 -0
  156. package/src/gui/EFControls.browsertest.ts +294 -80
  157. package/src/gui/EFControls.ts +139 -28
  158. package/src/gui/EFDial.browsertest.ts +84 -0
  159. package/src/gui/EFDial.ts +172 -0
  160. package/src/gui/EFFilmstrip.browsertest.ts +712 -0
  161. package/src/gui/EFFilmstrip.ts +213 -23
  162. package/src/gui/EFPause.browsertest.ts +202 -0
  163. package/src/gui/EFPause.ts +73 -0
  164. package/src/gui/EFPlay.browsertest.ts +202 -0
  165. package/src/gui/EFPlay.ts +73 -0
  166. package/src/gui/EFPreview.ts +20 -5
  167. package/src/gui/EFResizableBox.browsertest.ts +79 -0
  168. package/src/gui/EFResizableBox.ts +898 -0
  169. package/src/gui/EFScrubber.ts +7 -5
  170. package/src/gui/EFTimeDisplay.browsertest.ts +19 -19
  171. package/src/gui/EFTimeDisplay.ts +3 -1
  172. package/src/gui/EFToggleLoop.ts +6 -5
  173. package/src/gui/EFTogglePlay.ts +30 -23
  174. package/src/gui/PlaybackController.ts +522 -0
  175. package/src/gui/TWMixin.css +3 -0
  176. package/src/gui/TargetOrContextMixin.ts +185 -0
  177. package/src/gui/efContext.ts +2 -2
  178. package/src/otel/BridgeSpanExporter.ts +150 -0
  179. package/src/otel/setupBrowserTracing.ts +73 -0
  180. package/src/otel/tracingHelpers.ts +251 -0
  181. package/test/cache-integration-verification.browsertest.ts +1 -1
  182. package/types.json +1 -1
  183. package/dist/elements/ContextProxiesController.js +0 -69
@@ -1,5 +1,8 @@
1
- import { EFMedia } from "./EFMedia.js";
1
+ import { __decorate } from "../_virtual/_@oxc-project_runtime@0.94.0/helpers/decorate.js";
2
2
  import { TWMixin } from "../gui/TWMixin2.js";
3
+ import { withSpan, withSpanSync } from "../otel/tracingHelpers.js";
4
+ import { EFMedia } from "./EFMedia.js";
5
+ import { updateAnimations } from "./updateAnimations.js";
3
6
  import { DelayedLoadingState } from "../DelayedLoadingState.js";
4
7
  import { makeScrubVideoBufferTask } from "./EFMedia/videoTasks/makeScrubVideoBufferTask.js";
5
8
  import { makeScrubVideoInitSegmentFetchTask } from "./EFMedia/videoTasks/makeScrubVideoInitSegmentFetchTask.js";
@@ -13,10 +16,10 @@ import { Task } from "@lit/task";
13
16
  import debug from "debug";
14
17
  import { css, html } from "lit";
15
18
  import { customElement, property, state } from "lit/decorators.js";
16
- import _decorate from "@oxc-project/runtime/helpers/decorate";
19
+ import { context, trace } from "@opentelemetry/api";
17
20
  import { createRef, ref } from "lit/directives/ref.js";
18
- const log = debug("ef:elements:EFVideo");
19
- let EFVideo = class EFVideo$1 extends TWMixin(EFMedia) {
21
+ var log = debug("ef:elements:EFVideo");
22
+ var EFVideo = class EFVideo$1 extends TWMixin(EFMedia) {
20
23
  static {
21
24
  this.styles = [css`
22
25
  :host {
@@ -104,43 +107,33 @@ let EFVideo = class EFVideo$1 extends TWMixin(EFMedia) {
104
107
  console.error("frameTask error", error);
105
108
  },
106
109
  onComplete: () => {},
107
- task: async ([_desiredSeekTimeMs]) => {
108
- this.unifiedVideoSeekTask.run();
109
- await this.unifiedVideoSeekTask.taskComplete;
110
- this.paintTask.run();
111
- await this.paintTask.taskComplete;
112
- }
113
- });
114
- this.paintTask = new Task(this, {
115
- autoRun: false,
116
- args: () => [this.desiredSeekTimeMs],
117
- onError: (error) => {
118
- console.error("paintTask error", error);
119
- },
120
- onComplete: () => {},
121
- task: async ([_seekToMs], { signal }) => {
122
- const isProductionRendering = this.isInProductionRenderingMode();
123
- try {
110
+ task: async ([_desiredSeekTimeMs], { signal }) => {
111
+ const t0 = performance.now();
112
+ await withSpan("video.frameTask", {
113
+ elementId: this.id || "unknown",
114
+ desiredSeekTimeMs: _desiredSeekTimeMs,
115
+ src: this.src || "none"
116
+ }, void 0, async (span) => {
117
+ const t1 = performance.now();
118
+ span.setAttribute("preworkMs", t1 - t0);
119
+ this.unifiedVideoSeekTask.run();
120
+ const t2 = performance.now();
121
+ span.setAttribute("seekRunMs", t2 - t1);
124
122
  await this.unifiedVideoSeekTask.taskComplete;
125
- const videoSample = this.unifiedVideoSeekTask.value;
126
- if (videoSample) {
127
- const videoFrame = videoSample.toVideoFrame();
128
- try {
129
- this.displayFrame(videoFrame, _seekToMs);
130
- } finally {
131
- videoFrame.close();
132
- }
123
+ const t3 = performance.now();
124
+ span.setAttribute("seekAwaitMs", t3 - t2);
125
+ if (signal.aborted) {
126
+ span.setAttribute("aborted", true);
127
+ return;
133
128
  }
134
- } catch (error) {
135
- console.warn("Unified video pipeline error:", error);
136
- }
137
- if (!isProductionRendering) {
138
- if (!this.rootTimegroup || this.rootTimegroup.currentTimeMs === 0 && this.desiredSeekTimeMs === 0) return;
139
- } else {
140
- if (!this.rootTimegroup) return;
141
- if (!this.isFrameRenderingActive()) return;
142
- }
143
- if (signal.aborted) return;
129
+ this.paint(this.desiredSeekTimeMs, span);
130
+ if (!this.parentTimegroup) updateAnimations(this);
131
+ const t4 = performance.now();
132
+ this.paint(_desiredSeekTimeMs, span);
133
+ const t5 = performance.now();
134
+ span.setAttribute("paintMs", t5 - t4);
135
+ span.setAttribute("totalFrameMs", t5 - t0);
136
+ });
144
137
  }
145
138
  });
146
139
  this.delayedLoadingState = new DelayedLoadingState(250, (isLoading, message) => {
@@ -171,23 +164,13 @@ let EFVideo = class EFVideo$1 extends TWMixin(EFMedia) {
171
164
  if (referencedCanvas) return referencedCanvas;
172
165
  const shadowCanvas = this.shadowRoot?.querySelector("canvas");
173
166
  if (shadowCanvas) return shadowCanvas;
174
- return void 0;
175
167
  }
176
- /**
177
- * Start a delayed loading operation for testing
178
- */
179
168
  startDelayedLoading(operationId, message, options = {}) {
180
169
  this.delayedLoadingState.startLoading(operationId, message, options);
181
170
  }
182
- /**
183
- * Clear a delayed loading operation for testing
184
- */
185
171
  clearDelayedLoading(operationId) {
186
172
  this.delayedLoadingState.clearLoading(operationId);
187
173
  }
188
- /**
189
- * Set loading state for user feedback
190
- */
191
174
  setLoadingState(isLoading, operation = null, message = "") {
192
175
  this.loadingState = {
193
176
  isLoading,
@@ -195,96 +178,161 @@ let EFVideo = class EFVideo$1 extends TWMixin(EFMedia) {
195
178
  message
196
179
  };
197
180
  }
198
- /**
199
- * Clear the canvas when element becomes inactive
200
- */
181
+ paint(seekToMs, parentSpan) {
182
+ const parentContext = parentSpan ? trace.setSpan(context.active(), parentSpan) : void 0;
183
+ withSpanSync("video.paint", {
184
+ elementId: this.id || "unknown",
185
+ seekToMs,
186
+ src: this.src || "none"
187
+ }, parentContext, (span) => {
188
+ const t0 = performance.now();
189
+ const isProductionRendering = this.isInProductionRenderingMode();
190
+ const t1 = performance.now();
191
+ span.setAttribute("isProductionRendering", isProductionRendering);
192
+ span.setAttribute("modeCheckMs", t1 - t0);
193
+ try {
194
+ const t2 = performance.now();
195
+ const videoSample = this.unifiedVideoSeekTask.value;
196
+ span.setAttribute("hasVideoSample", !!videoSample);
197
+ span.setAttribute("valueAccessMs", t2 - t1);
198
+ if (videoSample) {
199
+ const t3 = performance.now();
200
+ const videoFrame = videoSample.toVideoFrame();
201
+ const t4 = performance.now();
202
+ span.setAttribute("toVideoFrameMs", t4 - t3);
203
+ try {
204
+ const t5 = performance.now();
205
+ this.displayFrame(videoFrame, seekToMs, span);
206
+ const t6 = performance.now();
207
+ span.setAttribute("displayFrameMs", t6 - t5);
208
+ } finally {
209
+ videoFrame.close();
210
+ }
211
+ }
212
+ } catch (error) {
213
+ console.warn("Unified video pipeline error:", error);
214
+ }
215
+ if (!isProductionRendering) {
216
+ if (!this.rootTimegroup || this.rootTimegroup.currentTimeMs === 0 && this.desiredSeekTimeMs === 0) {
217
+ span.setAttribute("skipped", "preview-initialization");
218
+ return;
219
+ }
220
+ } else {
221
+ if (!this.rootTimegroup) {
222
+ span.setAttribute("skipped", "no-root-timegroup");
223
+ return;
224
+ }
225
+ if (!this.isFrameRenderingActive()) {
226
+ span.setAttribute("skipped", "frame-rendering-not-active");
227
+ return;
228
+ }
229
+ }
230
+ const tEnd = performance.now();
231
+ span.setAttribute("totalPaintMs", tEnd - t0);
232
+ });
233
+ }
201
234
  clearCanvas() {
202
235
  if (!this.canvasElement) return;
203
236
  const ctx = this.canvasElement.getContext("2d");
204
237
  if (ctx) ctx.clearRect(0, 0, this.canvasElement.width, this.canvasElement.height);
205
238
  }
206
- /**
207
- * Display a video frame on the canvas
208
- */
209
- displayFrame(frame, seekToMs) {
210
- log("trace: displayFrame start", {
239
+ displayFrame(frame, seekToMs, parentSpan) {
240
+ const parentContext = parentSpan ? trace.setSpan(context.active(), parentSpan) : void 0;
241
+ withSpanSync("video.displayFrame", {
242
+ elementId: this.id || "unknown",
211
243
  seekToMs,
212
- frameFormat: frame.format
213
- });
214
- if (!this.canvasElement) {
215
- log("trace: displayFrame aborted - no canvas element");
216
- throw new Error(`Frame display failed: Canvas element is not available at time ${seekToMs}ms. The video component may not be properly initialized.`);
217
- }
218
- const ctx = this.canvasElement.getContext("2d");
219
- if (!ctx) {
220
- log("trace: displayFrame aborted - no canvas context");
221
- throw new Error(`Frame display failed: Unable to get 2D canvas context at time ${seekToMs}ms. This may indicate a browser compatibility issue or canvas corruption.`);
222
- }
223
- if (frame?.codedWidth && frame?.codedHeight) {
224
- if (this.canvasElement.width !== frame.codedWidth || this.canvasElement.height !== frame.codedHeight) {
225
- log("trace: updating canvas dimensions", {
226
- width: frame.codedWidth,
227
- height: frame.codedHeight
228
- });
229
- this.canvasElement.width = frame.codedWidth;
230
- this.canvasElement.height = frame.codedHeight;
244
+ format: frame.format || "unknown",
245
+ width: frame.codedWidth,
246
+ height: frame.codedHeight
247
+ }, parentContext, (span) => {
248
+ const t0 = performance.now();
249
+ log("trace: displayFrame start", {
250
+ seekToMs,
251
+ frameFormat: frame.format
252
+ });
253
+ if (!this.canvasElement) {
254
+ log("trace: displayFrame aborted - no canvas element");
255
+ throw new Error(`Frame display failed: Canvas element is not available at time ${seekToMs}ms. The video component may not be properly initialized.`);
231
256
  }
232
- }
233
- if (frame.format === null) {
234
- log("trace: displayFrame aborted - null frame format");
235
- throw new Error(`Frame display failed: Video frame has null format at time ${seekToMs}ms. This indicates corrupted or incompatible video data.`);
236
- }
237
- ctx.drawImage(frame, 0, 0, this.canvasElement.width, this.canvasElement.height);
238
- log("trace: frame drawn to canvas", { seekToMs });
239
- return seekToMs;
257
+ const t1 = performance.now();
258
+ span.setAttribute("getCanvasMs", Math.round((t1 - t0) * 100) / 100);
259
+ const ctx = this.canvasElement.getContext("2d");
260
+ const t2 = performance.now();
261
+ span.setAttribute("getCtxMs", Math.round((t2 - t1) * 100) / 100);
262
+ if (!ctx) {
263
+ log("trace: displayFrame aborted - no canvas context");
264
+ throw new Error(`Frame display failed: Unable to get 2D canvas context at time ${seekToMs}ms. This may indicate a browser compatibility issue or canvas corruption.`);
265
+ }
266
+ let resized = false;
267
+ if (frame?.codedWidth && frame?.codedHeight) {
268
+ if (this.canvasElement.width !== frame.codedWidth || this.canvasElement.height !== frame.codedHeight) {
269
+ log("trace: updating canvas dimensions", {
270
+ width: frame.codedWidth,
271
+ height: frame.codedHeight
272
+ });
273
+ this.canvasElement.width = frame.codedWidth;
274
+ this.canvasElement.height = frame.codedHeight;
275
+ resized = true;
276
+ const t3 = performance.now();
277
+ span.setAttribute("resizeMs", Math.round((t3 - t2) * 100) / 100);
278
+ }
279
+ }
280
+ span.setAttribute("canvasResized", resized);
281
+ if (frame.format === null) {
282
+ log("trace: displayFrame aborted - null frame format");
283
+ throw new Error(`Frame display failed: Video frame has null format at time ${seekToMs}ms. This indicates corrupted or incompatible video data.`);
284
+ }
285
+ const tDrawStart = performance.now();
286
+ ctx.drawImage(frame, 0, 0, this.canvasElement.width, this.canvasElement.height);
287
+ const tDrawEnd = performance.now();
288
+ span.setAttribute("drawImageMs", Math.round((tDrawEnd - tDrawStart) * 100) / 100);
289
+ span.setAttribute("totalDisplayMs", Math.round((tDrawEnd - t0) * 100) / 100);
290
+ span.setAttribute("canvasWidth", this.canvasElement.width);
291
+ span.setAttribute("canvasHeight", this.canvasElement.height);
292
+ log("trace: frame drawn to canvas", { seekToMs });
293
+ });
240
294
  }
241
- /**
242
- * Check if we're in production rendering mode (EF_FRAMEGEN active) vs preview mode
243
- */
244
295
  isInProductionRenderingMode() {
245
296
  if (typeof window.EF_RENDERING === "function") return window.EF_RENDERING();
246
- const workbench = document.querySelector("ef-workbench");
247
- if (workbench?.rendering) return true;
297
+ if (document.querySelector("ef-workbench")?.rendering) return true;
248
298
  if (window.EF_FRAMEGEN?.renderOptions) return true;
249
299
  return false;
250
300
  }
251
- /**
252
- * Check if EF_FRAMEGEN has explicitly started frame rendering (not just initialization)
253
- */
254
301
  isFrameRenderingActive() {
255
302
  if (!window.EF_FRAMEGEN?.renderOptions) return false;
256
- const renderOptions = window.EF_FRAMEGEN.renderOptions;
257
- const renderStartTime = renderOptions.encoderOptions.fromMs;
258
- const currentTime = this.rootTimegroup?.currentTimeMs || 0;
259
- return currentTime >= renderStartTime;
303
+ const renderStartTime = window.EF_FRAMEGEN.renderOptions.encoderOptions.fromMs;
304
+ return (this.rootTimegroup?.currentTimeMs || 0) >= renderStartTime;
260
305
  }
261
- /**
262
- * Legacy getter for fragment index task
263
- * Still used by EFCaptions - maps to unified video seek task
264
- */
265
306
  get fragmentIndexTask() {
266
307
  return this.unifiedVideoSeekTask;
267
308
  }
268
- /**
269
- * Clean up resources when component is disconnected
270
- */
309
+ async waitForFrameReady() {
310
+ await this.updateComplete;
311
+ await this.frameTask.run();
312
+ }
271
313
  disconnectedCallback() {
272
314
  super.disconnectedCallback();
273
315
  this.delayedLoadingState.clearAllLoading();
274
316
  }
317
+ didBecomeRoot() {
318
+ super.didBecomeRoot();
319
+ }
320
+ didBecomeChild() {
321
+ super.didBecomeChild();
322
+ }
275
323
  };
276
- _decorate([property({
324
+ __decorate([property({
277
325
  type: Number,
278
326
  attribute: "video-buffer-duration"
279
327
  })], EFVideo.prototype, "videoBufferDurationMs", void 0);
280
- _decorate([property({
328
+ __decorate([property({
281
329
  type: Number,
282
330
  attribute: "max-video-buffer-fetches"
283
331
  })], EFVideo.prototype, "maxVideoBufferFetches", void 0);
284
- _decorate([property({
332
+ __decorate([property({
285
333
  type: Boolean,
286
334
  attribute: "enable-video-buffering"
287
335
  })], EFVideo.prototype, "enableVideoBuffering", void 0);
288
- _decorate([state()], EFVideo.prototype, "loadingState", void 0);
289
- EFVideo = _decorate([customElement("ef-video")], EFVideo);
336
+ __decorate([state()], EFVideo.prototype, "loadingState", void 0);
337
+ EFVideo = __decorate([customElement("ef-video")], EFVideo);
290
338
  export { EFVideo };
@@ -1,15 +1,15 @@
1
1
  import { EF_RENDERING } from "../EF_RENDERING.js";
2
+ import { __decorate } from "../_virtual/_@oxc-project_runtime@0.94.0/helpers/decorate.js";
2
3
  import { EFTemporal } from "./EFTemporal.js";
3
- import { TargetController } from "./TargetController.js";
4
4
  import { TWMixin } from "../gui/TWMixin2.js";
5
+ import { TargetController } from "./TargetController.js";
5
6
  import { CrossUpdateController } from "./CrossUpdateController.js";
6
7
  import { Task } from "@lit/task";
7
8
  import { LitElement, css, html } from "lit";
8
9
  import { customElement, property, state } from "lit/decorators.js";
9
- import _decorate from "@oxc-project/runtime/helpers/decorate";
10
10
  import { createRef, ref } from "lit/directives/ref.js";
11
11
  import { CSSStyleObserver } from "@bramus/style-observer";
12
- let EFWaveform = class EFWaveform$1 extends EFTemporal(TWMixin(LitElement)) {
12
+ var EFWaveform = class EFWaveform$1 extends EFTemporal(TWMixin(LitElement)) {
13
13
  constructor(..._args) {
14
14
  super(..._args);
15
15
  this.canvasRef = createRef();
@@ -38,8 +38,7 @@ let EFWaveform = class EFWaveform$1 extends EFTemporal(TWMixin(LitElement)) {
38
38
  if (!frequencyData || !byteTimeData) return;
39
39
  ctx.save();
40
40
  if (this.color === "currentColor") {
41
- const computedStyle = getComputedStyle(this);
42
- const currentColor = computedStyle.color;
41
+ const currentColor = getComputedStyle(this).color;
43
42
  ctx.strokeStyle = currentColor;
44
43
  ctx.fillStyle = currentColor;
45
44
  } else {
@@ -150,13 +149,11 @@ let EFWaveform = class EFWaveform$1 extends EFTemporal(TWMixin(LitElement)) {
150
149
  const totalBars = frequencyData.length;
151
150
  const paddingInner = this.barSpacing;
152
151
  const paddingOuter = .01;
153
- const availableWidth = waveWidth;
154
- const barWidth = availableWidth / (totalBars + (totalBars - 1) * paddingInner);
152
+ const barWidth = waveWidth / (totalBars + (totalBars - 1) * paddingInner);
155
153
  ctx.clearRect(0, 0, waveWidth, waveHeight);
156
154
  const path = new Path2D();
157
155
  frequencyData.forEach((value, i) => {
158
- const normalizedValue = value / 255;
159
- const barHeight = normalizedValue * waveHeight;
156
+ const barHeight = value / 255 * waveHeight;
160
157
  const y = (waveHeight - barHeight) / 2;
161
158
  const x = waveWidth * paddingOuter + i * (barWidth * (1 + paddingInner));
162
159
  path.rect(x, y, barWidth, barHeight);
@@ -191,13 +188,11 @@ let EFWaveform = class EFWaveform$1 extends EFTemporal(TWMixin(LitElement)) {
191
188
  const totalBars = frequencyData.length;
192
189
  const paddingInner = this.barSpacing;
193
190
  const paddingOuter = .01;
194
- const availableWidth = waveWidth;
195
- const barWidth = availableWidth / (totalBars + (totalBars - 1) * paddingInner);
191
+ const barWidth = waveWidth / (totalBars + (totalBars - 1) * paddingInner);
196
192
  ctx.clearRect(0, 0, waveWidth, waveHeight);
197
193
  const path = new Path2D();
198
194
  frequencyData.forEach((value, i) => {
199
- const normalizedValue = value / 255;
200
- const height = normalizedValue * waveHeight;
195
+ const height = value / 255 * waveHeight;
201
196
  const x = waveWidth * paddingOuter + i * (barWidth * (1 + paddingInner));
202
197
  const y = (waveHeight - height) / 2;
203
198
  path.roundRect(x, y, barWidth, height, barWidth / 2);
@@ -276,14 +271,11 @@ let EFWaveform = class EFWaveform$1 extends EFTemporal(TWMixin(LitElement)) {
276
271
  frequencyData.forEach((value, i) => {
277
272
  const normalizedValue = Math.min(value / 255 * 2, 1);
278
273
  const x = startX + i / (frequencyData.length - 1) * availableWidth;
279
- const barHeight = normalizedValue * waveHeight;
280
- const y = (waveHeight - barHeight) / 2;
274
+ const y = (waveHeight - normalizedValue * waveHeight) / 2;
281
275
  if (i === 0) path.moveTo(x, y);
282
276
  else {
283
277
  const prevX = startX + (i - 1) / (frequencyData.length - 1) * availableWidth;
284
- const prevValue = Math.min((frequencyData[i - 1] ?? 0) / 255 * 2, 1);
285
- const prevBarHeight = prevValue * waveHeight;
286
- const prevY = (waveHeight - prevBarHeight) / 2;
278
+ const prevY = (waveHeight - Math.min((frequencyData[i - 1] ?? 0) / 255 * 2, 1) * waveHeight) / 2;
287
279
  const xc = (prevX + x) / 2;
288
280
  const yc = (prevY + y) / 2;
289
281
  path.quadraticCurveTo(prevX, prevY, xc, yc);
@@ -292,14 +284,11 @@ let EFWaveform = class EFWaveform$1 extends EFTemporal(TWMixin(LitElement)) {
292
284
  for (let i = frequencyData.length - 1; i >= 0; i--) {
293
285
  const normalizedValue = Math.min((frequencyData[i] ?? 0) / 255 * 2, 1);
294
286
  const x = startX + i / (frequencyData.length - 1) * availableWidth;
295
- const barHeight = normalizedValue * waveHeight;
296
- const y = (waveHeight + barHeight) / 2;
287
+ const y = (waveHeight + normalizedValue * waveHeight) / 2;
297
288
  if (i === frequencyData.length - 1) path.lineTo(x, y);
298
289
  else {
299
290
  const nextX = startX + (i + 1) / (frequencyData.length - 1) * availableWidth;
300
- const nextValue = Math.min((frequencyData[i + 1] ?? 0) / 255 * 2, 1);
301
- const nextBarHeight = nextValue * waveHeight;
302
- const nextY = (waveHeight + nextBarHeight) / 2;
291
+ const nextY = (waveHeight + Math.min((frequencyData[i + 1] ?? 0) / 255 * 2, 1) * waveHeight) / 2;
303
292
  const xc = (nextX + x) / 2;
304
293
  const yc = (nextY + y) / 2;
305
294
  path.quadraticCurveTo(nextX, nextY, xc, yc);
@@ -326,14 +315,11 @@ let EFWaveform = class EFWaveform$1 extends EFTemporal(TWMixin(LitElement)) {
326
315
  frequencyData.forEach((value, i) => {
327
316
  const normalizedValue = Math.min(value / 255 * 2, 1);
328
317
  const x = startX + i / (frequencyData.length - 1) * availableWidth;
329
- const barHeight = normalizedValue * (waveHeight / 2);
330
- const y = (waveHeight - barHeight * 2) / 2;
318
+ const y = (waveHeight - normalizedValue * (waveHeight / 2) * 2) / 2;
331
319
  if (i === 0) path.moveTo(x, y);
332
320
  else {
333
321
  const prevX = startX + (i - 1) / (frequencyData.length - 1) * availableWidth;
334
- const prevValue = (frequencyData[i - 1] ?? 0) / 255;
335
- const prevBarHeight = prevValue * (waveHeight / 2);
336
- const prevY = (waveHeight - prevBarHeight * 2) / 2;
322
+ const prevY = (waveHeight - (frequencyData[i - 1] ?? 0) / 255 * (waveHeight / 2) * 2) / 2;
337
323
  const xc = (prevX + x) / 2;
338
324
  const yc = (prevY + y) / 2;
339
325
  path.quadraticCurveTo(prevX, prevY, xc, yc);
@@ -342,14 +328,11 @@ let EFWaveform = class EFWaveform$1 extends EFTemporal(TWMixin(LitElement)) {
342
328
  for (let i = frequencyData.length - 1; i >= 0; i--) {
343
329
  const normalizedValue = Math.min((frequencyData[i] ?? 0) / 255 * 2, 1);
344
330
  const x = startX + i / (frequencyData.length - 1) * availableWidth;
345
- const barHeight = normalizedValue * (waveHeight / 2);
346
- const y = (waveHeight + barHeight * 2) / 2;
331
+ const y = (waveHeight + normalizedValue * (waveHeight / 2) * 2) / 2;
347
332
  if (i === frequencyData.length - 1) path.lineTo(x, y);
348
333
  else {
349
334
  const nextX = startX + (i + 1) / (frequencyData.length - 1) * availableWidth;
350
- const nextValue = (frequencyData[i + 1] ?? 0) / 255;
351
- const nextBarHeight = nextValue * (waveHeight / 2);
352
- const nextY = (waveHeight + nextBarHeight * 2) / 2;
335
+ const nextY = (waveHeight + (frequencyData[i + 1] ?? 0) / 255 * (waveHeight / 2) * 2) / 2;
353
336
  const xc = (nextX + x) / 2;
354
337
  const yc = (nextY + y) / 2;
355
338
  path.quadraticCurveTo(nextX, nextY, xc, yc);
@@ -370,23 +353,23 @@ let EFWaveform = class EFWaveform$1 extends EFTemporal(TWMixin(LitElement)) {
370
353
  if (changedProperties.size > 0) this.frameTask.run();
371
354
  }
372
355
  };
373
- _decorate([property({
356
+ __decorate([property({
374
357
  type: String,
375
358
  attribute: "mode"
376
359
  })], EFWaveform.prototype, "mode", void 0);
377
- _decorate([property({ type: String })], EFWaveform.prototype, "color", void 0);
378
- _decorate([property({
360
+ __decorate([property({ type: String })], EFWaveform.prototype, "color", void 0);
361
+ __decorate([property({
379
362
  type: String,
380
363
  reflect: true
381
364
  })], EFWaveform.prototype, "target", void 0);
382
- _decorate([property({
365
+ __decorate([property({
383
366
  type: Number,
384
367
  attribute: "bar-spacing"
385
368
  })], EFWaveform.prototype, "barSpacing", void 0);
386
- _decorate([state()], EFWaveform.prototype, "targetElement", void 0);
387
- _decorate([property({
369
+ __decorate([state()], EFWaveform.prototype, "targetElement", void 0);
370
+ __decorate([property({
388
371
  type: Number,
389
372
  attribute: "line-width"
390
373
  })], EFWaveform.prototype, "lineWidth", void 0);
391
- EFWaveform = _decorate([customElement("ef-waveform")], EFWaveform);
374
+ EFWaveform = __decorate([customElement("ef-waveform")], EFWaveform);
392
375
  export { EFWaveform };
@@ -23,8 +23,7 @@ var SampleBuffer = class {
23
23
  } catch (_error) {}
24
24
  }
25
25
  peek() {
26
- const currentBuffer = this.buffer;
27
- return currentBuffer[0];
26
+ return this.buffer[0];
28
27
  }
29
28
  find(desiredSeekTimeMs) {
30
29
  const currentBuffer = [...this.buffer];
@@ -32,18 +31,15 @@ var SampleBuffer = class {
32
31
  const targetTimeMs = roundToMilliseconds(desiredSeekTimeMs);
33
32
  for (const sample of currentBuffer) {
34
33
  const sampleStartMs = roundToMilliseconds((sample.timestamp || 0) * 1e3);
35
- const sampleDurationMs = roundToMilliseconds((sample.duration || 0) * 1e3);
36
- const sampleEndMs = roundToMilliseconds(sampleStartMs + sampleDurationMs);
34
+ const sampleEndMs = roundToMilliseconds(sampleStartMs + roundToMilliseconds((sample.duration || 0) * 1e3));
37
35
  if (targetTimeMs >= sampleStartMs && targetTimeMs < sampleEndMs) return sample;
38
36
  }
39
- return void 0;
40
37
  }
41
38
  get length() {
42
39
  return this.buffer.length;
43
40
  }
44
41
  get firstTimestamp() {
45
- const currentBuffer = this.buffer;
46
- return currentBuffer[0]?.timestamp || 0;
42
+ return this.buffer[0]?.timestamp || 0;
47
43
  }
48
44
  getContents() {
49
45
  return [...this.buffer];
@@ -1,5 +1,5 @@
1
1
  import { LitElement } from "lit";
2
- const EF_TARGETABLE = Symbol("EF_TARGETABLE");
2
+ var EF_TARGETABLE = Symbol("EF_TARGETABLE");
3
3
  var TargetRegistry = class {
4
4
  constructor() {
5
5
  this.idMap = /* @__PURE__ */ new Map();
@@ -28,8 +28,8 @@ var TargetRegistry = class {
28
28
  this.callbacks.delete(id);
29
29
  }
30
30
  };
31
- const documentRegistries = /* @__PURE__ */ new WeakMap();
32
- const getRegistry = (root) => {
31
+ var documentRegistries = /* @__PURE__ */ new WeakMap();
32
+ var getRegistry = (root) => {
33
33
  let registry = documentRegistries.get(root);
34
34
  if (!registry) {
35
35
  registry = new TargetRegistry();
@@ -103,6 +103,7 @@ var TargetController = class {
103
103
  this.disconnectFromTarget();
104
104
  this.host.targetElement = newTarget ?? null;
105
105
  this.connectToTarget();
106
+ this.host.requestUpdate("targetElement");
106
107
  }
107
108
  }
108
109
  connectToTarget() {
@@ -118,8 +119,7 @@ var TargetController = class {
118
119
  }
119
120
  }
120
121
  get registry() {
121
- const root = this.host.getRootNode();
122
- return getRegistry(root);
122
+ return getRegistry(this.host.getRootNode());
123
123
  }
124
124
  hostDisconnected() {
125
125
  this.disconnectFromTarget();
@@ -3,7 +3,7 @@ const durationConverter = {
3
3
  fromAttribute: (value) => value === null ? null : parseTimeToMs(value),
4
4
  toAttribute: (value) => value === null ? null : `${value}s`
5
5
  };
6
- const positiveDurationConverter = (error) => {
6
+ var positiveDurationConverter = (error) => {
7
7
  return {
8
8
  fromAttribute: (value) => {
9
9
  if (value === null) return null;
@@ -13,7 +13,7 @@ const positiveDurationConverter = (error) => {
13
13
  toAttribute: (value) => value === null ? null : `${value}s`
14
14
  };
15
15
  };
16
- const trimDurationConverter = positiveDurationConverter("Trimstart & trimend must be a positive value in milliseconds or seconds (1s, 1000ms)");
17
- const imageDurationConverter = positiveDurationConverter("Image duration must be a positive value in milliseconds or seconds (1s, 1000ms)");
18
- const sourceDurationConverter = positiveDurationConverter("Sourcein & sourceout must be a positive value in milliseconds or seconds (1s, 1000ms)");
16
+ positiveDurationConverter("Trimstart & trimend must be a positive value in milliseconds or seconds (1s, 1000ms)");
17
+ positiveDurationConverter("Image duration must be a positive value in milliseconds or seconds (1s, 1000ms)");
18
+ positiveDurationConverter("Sourcein & sourceout must be a positive value in milliseconds or seconds (1s, 1000ms)");
19
19
  export { durationConverter };
@@ -0,0 +1,10 @@
1
+ import { EFMedia } from './EFMedia.js';
2
+ interface TemporalAudioHost {
3
+ startTimeMs: number;
4
+ endTimeMs: number;
5
+ durationMs: number;
6
+ getMediaElements(): EFMedia[];
7
+ waitForMediaDurations?(): Promise<void>;
8
+ }
9
+ export declare function renderTemporalAudio(host: TemporalAudioHost, fromMs: number, toMs: number): Promise<AudioBuffer>;
10
+ export {};
@@ -0,0 +1,35 @@
1
+ async function renderTemporalAudio(host, fromMs, toMs) {
2
+ const aacFrames = 48e3 * ((toMs - fromMs) / 1e3) / 1024;
3
+ const contextSize = Math.round(aacFrames) * 1024;
4
+ if (contextSize <= 0) throw new Error(`Duration must be greater than 0 when rendering audio. ${contextSize}ms`);
5
+ const audioContext = new OfflineAudioContext(2, contextSize, 48e3);
6
+ if (host.waitForMediaDurations) await host.waitForMediaDurations();
7
+ const abortController = new AbortController();
8
+ await Promise.all(host.getMediaElements().map(async (mediaElement) => {
9
+ if (mediaElement.mute) return;
10
+ const mediaStartsBeforeEnd = mediaElement.startTimeMs <= toMs;
11
+ const mediaEndsAfterStart = mediaElement.endTimeMs >= fromMs;
12
+ if (!(mediaStartsBeforeEnd && mediaEndsAfterStart)) return;
13
+ const mediaLocalFromMs = Math.max(0, fromMs - mediaElement.startTimeMs);
14
+ const mediaLocalToMs = Math.min(mediaElement.endTimeMs - mediaElement.startTimeMs, toMs - mediaElement.startTimeMs);
15
+ if (mediaLocalFromMs >= mediaLocalToMs) return;
16
+ const sourceInMs = mediaElement.sourceInMs || mediaElement.trimStartMs || 0;
17
+ const mediaSourceFromMs = mediaLocalFromMs + sourceInMs;
18
+ const mediaSourceToMs = mediaLocalToMs + sourceInMs;
19
+ const audio = await mediaElement.fetchAudioSpanningTime(mediaSourceFromMs, mediaSourceToMs, abortController.signal);
20
+ if (!audio) return;
21
+ const bufferSource = audioContext.createBufferSource();
22
+ bufferSource.buffer = await audioContext.decodeAudioData(await audio.blob.arrayBuffer());
23
+ bufferSource.connect(audioContext.destination);
24
+ const ctxStartMs = Math.max(0, mediaElement.startTimeMs - fromMs);
25
+ const offsetInBufferMs = mediaSourceFromMs - audio.startMs;
26
+ const safeOffsetMs = Math.max(0, offsetInBufferMs);
27
+ const requestedDurationMs = mediaSourceToMs - mediaSourceFromMs;
28
+ const availableAudioMs = audio.endMs - audio.startMs;
29
+ const actualDurationMs = Math.min(requestedDurationMs, availableAudioMs - safeOffsetMs);
30
+ if (actualDurationMs <= 0) return;
31
+ bufferSource.start(ctxStartMs / 1e3, safeOffsetMs / 1e3, actualDurationMs / 1e3);
32
+ }));
33
+ return audioContext.startRendering();
34
+ }
35
+ export { renderTemporalAudio };