@editframe/elements 0.26.3-beta.0 → 0.26.4-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 (132) hide show
  1. package/package.json +2 -2
  2. package/scripts/build-css.js +3 -3
  3. package/tsdown.config.ts +1 -1
  4. package/src/elements/ContextProxiesController.ts +0 -124
  5. package/src/elements/CrossUpdateController.ts +0 -22
  6. package/src/elements/EFAudio.browsertest.ts +0 -706
  7. package/src/elements/EFAudio.ts +0 -56
  8. package/src/elements/EFCaptions.browsertest.ts +0 -1960
  9. package/src/elements/EFCaptions.ts +0 -823
  10. package/src/elements/EFImage.browsertest.ts +0 -120
  11. package/src/elements/EFImage.ts +0 -113
  12. package/src/elements/EFMedia/AssetIdMediaEngine.test.ts +0 -224
  13. package/src/elements/EFMedia/AssetIdMediaEngine.ts +0 -110
  14. package/src/elements/EFMedia/AssetMediaEngine.browsertest.ts +0 -140
  15. package/src/elements/EFMedia/AssetMediaEngine.ts +0 -385
  16. package/src/elements/EFMedia/BaseMediaEngine.browsertest.ts +0 -400
  17. package/src/elements/EFMedia/BaseMediaEngine.ts +0 -505
  18. package/src/elements/EFMedia/BufferedSeekingInput.browsertest.ts +0 -386
  19. package/src/elements/EFMedia/BufferedSeekingInput.ts +0 -430
  20. package/src/elements/EFMedia/JitMediaEngine.browsertest.ts +0 -226
  21. package/src/elements/EFMedia/JitMediaEngine.ts +0 -256
  22. package/src/elements/EFMedia/audioTasks/makeAudioBufferTask.browsertest.ts +0 -679
  23. package/src/elements/EFMedia/audioTasks/makeAudioBufferTask.ts +0 -117
  24. package/src/elements/EFMedia/audioTasks/makeAudioFrequencyAnalysisTask.ts +0 -246
  25. package/src/elements/EFMedia/audioTasks/makeAudioInitSegmentFetchTask.browsertest.ts +0 -59
  26. package/src/elements/EFMedia/audioTasks/makeAudioInitSegmentFetchTask.ts +0 -27
  27. package/src/elements/EFMedia/audioTasks/makeAudioInputTask.browsertest.ts +0 -55
  28. package/src/elements/EFMedia/audioTasks/makeAudioInputTask.ts +0 -53
  29. package/src/elements/EFMedia/audioTasks/makeAudioSeekTask.chunkboundary.regression.browsertest.ts +0 -207
  30. package/src/elements/EFMedia/audioTasks/makeAudioSeekTask.ts +0 -72
  31. package/src/elements/EFMedia/audioTasks/makeAudioSegmentFetchTask.ts +0 -32
  32. package/src/elements/EFMedia/audioTasks/makeAudioSegmentIdTask.ts +0 -29
  33. package/src/elements/EFMedia/audioTasks/makeAudioTasksVideoOnly.browsertest.ts +0 -95
  34. package/src/elements/EFMedia/audioTasks/makeAudioTimeDomainAnalysisTask.ts +0 -184
  35. package/src/elements/EFMedia/shared/AudioSpanUtils.ts +0 -129
  36. package/src/elements/EFMedia/shared/BufferUtils.ts +0 -342
  37. package/src/elements/EFMedia/shared/GlobalInputCache.ts +0 -77
  38. package/src/elements/EFMedia/shared/MediaTaskUtils.ts +0 -44
  39. package/src/elements/EFMedia/shared/PrecisionUtils.ts +0 -46
  40. package/src/elements/EFMedia/shared/RenditionHelpers.browsertest.ts +0 -246
  41. package/src/elements/EFMedia/shared/RenditionHelpers.ts +0 -56
  42. package/src/elements/EFMedia/shared/ThumbnailExtractor.ts +0 -227
  43. package/src/elements/EFMedia/tasks/makeMediaEngineTask.browsertest.ts +0 -167
  44. package/src/elements/EFMedia/tasks/makeMediaEngineTask.ts +0 -88
  45. package/src/elements/EFMedia/videoTasks/MainVideoInputCache.ts +0 -76
  46. package/src/elements/EFMedia/videoTasks/ScrubInputCache.ts +0 -61
  47. package/src/elements/EFMedia/videoTasks/makeScrubVideoBufferTask.ts +0 -114
  48. package/src/elements/EFMedia/videoTasks/makeScrubVideoInitSegmentFetchTask.ts +0 -35
  49. package/src/elements/EFMedia/videoTasks/makeScrubVideoInputTask.ts +0 -52
  50. package/src/elements/EFMedia/videoTasks/makeScrubVideoSeekTask.ts +0 -124
  51. package/src/elements/EFMedia/videoTasks/makeScrubVideoSegmentFetchTask.ts +0 -44
  52. package/src/elements/EFMedia/videoTasks/makeScrubVideoSegmentIdTask.ts +0 -32
  53. package/src/elements/EFMedia/videoTasks/makeUnifiedVideoSeekTask.ts +0 -370
  54. package/src/elements/EFMedia/videoTasks/makeVideoBufferTask.ts +0 -109
  55. package/src/elements/EFMedia.browsertest.ts +0 -872
  56. package/src/elements/EFMedia.ts +0 -341
  57. package/src/elements/EFSourceMixin.ts +0 -60
  58. package/src/elements/EFSurface.browsertest.ts +0 -151
  59. package/src/elements/EFSurface.ts +0 -142
  60. package/src/elements/EFTemporal.browsertest.ts +0 -215
  61. package/src/elements/EFTemporal.ts +0 -800
  62. package/src/elements/EFThumbnailStrip.browsertest.ts +0 -585
  63. package/src/elements/EFThumbnailStrip.media-engine.browsertest.ts +0 -714
  64. package/src/elements/EFThumbnailStrip.ts +0 -906
  65. package/src/elements/EFTimegroup.browsertest.ts +0 -934
  66. package/src/elements/EFTimegroup.ts +0 -882
  67. package/src/elements/EFVideo.browsertest.ts +0 -1482
  68. package/src/elements/EFVideo.ts +0 -564
  69. package/src/elements/EFWaveform.ts +0 -547
  70. package/src/elements/FetchContext.browsertest.ts +0 -401
  71. package/src/elements/FetchMixin.ts +0 -38
  72. package/src/elements/SampleBuffer.ts +0 -94
  73. package/src/elements/TargetController.browsertest.ts +0 -230
  74. package/src/elements/TargetController.ts +0 -224
  75. package/src/elements/TimegroupController.ts +0 -26
  76. package/src/elements/durationConverter.ts +0 -35
  77. package/src/elements/parseTimeToMs.ts +0 -9
  78. package/src/elements/printTaskStatus.ts +0 -16
  79. package/src/elements/renderTemporalAudio.ts +0 -108
  80. package/src/elements/updateAnimations.browsertest.ts +0 -1884
  81. package/src/elements/updateAnimations.ts +0 -217
  82. package/src/elements/util.ts +0 -24
  83. package/src/gui/ContextMixin.browsertest.ts +0 -860
  84. package/src/gui/ContextMixin.ts +0 -562
  85. package/src/gui/Controllable.browsertest.ts +0 -258
  86. package/src/gui/Controllable.ts +0 -41
  87. package/src/gui/EFConfiguration.ts +0 -40
  88. package/src/gui/EFControls.browsertest.ts +0 -389
  89. package/src/gui/EFControls.ts +0 -195
  90. package/src/gui/EFDial.browsertest.ts +0 -84
  91. package/src/gui/EFDial.ts +0 -172
  92. package/src/gui/EFFilmstrip.browsertest.ts +0 -712
  93. package/src/gui/EFFilmstrip.ts +0 -1349
  94. package/src/gui/EFFitScale.ts +0 -152
  95. package/src/gui/EFFocusOverlay.ts +0 -79
  96. package/src/gui/EFPause.browsertest.ts +0 -202
  97. package/src/gui/EFPause.ts +0 -73
  98. package/src/gui/EFPlay.browsertest.ts +0 -202
  99. package/src/gui/EFPlay.ts +0 -73
  100. package/src/gui/EFPreview.ts +0 -74
  101. package/src/gui/EFResizableBox.browsertest.ts +0 -79
  102. package/src/gui/EFResizableBox.ts +0 -898
  103. package/src/gui/EFScrubber.ts +0 -151
  104. package/src/gui/EFTimeDisplay.browsertest.ts +0 -237
  105. package/src/gui/EFTimeDisplay.ts +0 -55
  106. package/src/gui/EFToggleLoop.ts +0 -35
  107. package/src/gui/EFTogglePlay.ts +0 -70
  108. package/src/gui/EFWorkbench.ts +0 -115
  109. package/src/gui/PlaybackController.ts +0 -527
  110. package/src/gui/TWMixin.css +0 -6
  111. package/src/gui/TWMixin.ts +0 -61
  112. package/src/gui/TargetOrContextMixin.ts +0 -185
  113. package/src/gui/currentTimeContext.ts +0 -5
  114. package/src/gui/durationContext.ts +0 -3
  115. package/src/gui/efContext.ts +0 -6
  116. package/src/gui/fetchContext.ts +0 -5
  117. package/src/gui/focusContext.ts +0 -7
  118. package/src/gui/focusedElementContext.ts +0 -5
  119. package/src/gui/playingContext.ts +0 -5
  120. package/src/otel/BridgeSpanExporter.ts +0 -150
  121. package/src/otel/setupBrowserTracing.ts +0 -73
  122. package/src/otel/tracingHelpers.ts +0 -251
  123. package/src/transcoding/cache/RequestDeduplicator.test.ts +0 -170
  124. package/src/transcoding/cache/RequestDeduplicator.ts +0 -65
  125. package/src/transcoding/cache/URLTokenDeduplicator.test.ts +0 -182
  126. package/src/transcoding/cache/URLTokenDeduplicator.ts +0 -101
  127. package/src/transcoding/types/index.ts +0 -312
  128. package/src/transcoding/utils/MediaUtils.ts +0 -63
  129. package/src/transcoding/utils/UrlGenerator.ts +0 -68
  130. package/src/transcoding/utils/constants.ts +0 -36
  131. package/src/utils/LRUCache.test.ts +0 -274
  132. package/src/utils/LRUCache.ts +0 -696
@@ -1,564 +0,0 @@
1
- import { Task } from "@lit/task";
2
- import { context, trace } from "@opentelemetry/api";
3
- import debug from "debug";
4
- import { css, html, type PropertyValueMap } from "lit";
5
- import { customElement, property, state } from "lit/decorators.js";
6
- import { createRef, ref } from "lit/directives/ref.js";
7
- import { DelayedLoadingState } from "../DelayedLoadingState.js";
8
- import { TWMixin } from "../gui/TWMixin.js";
9
- import { withSpan, withSpanSync } from "../otel/tracingHelpers.js";
10
- import { makeScrubVideoBufferTask } from "./EFMedia/videoTasks/makeScrubVideoBufferTask.ts";
11
- import { makeScrubVideoInitSegmentFetchTask } from "./EFMedia/videoTasks/makeScrubVideoInitSegmentFetchTask.ts";
12
- import { makeScrubVideoInputTask } from "./EFMedia/videoTasks/makeScrubVideoInputTask.ts";
13
- import { makeScrubVideoSeekTask } from "./EFMedia/videoTasks/makeScrubVideoSeekTask.ts";
14
- import { makeScrubVideoSegmentFetchTask } from "./EFMedia/videoTasks/makeScrubVideoSegmentFetchTask.ts";
15
- import { makeScrubVideoSegmentIdTask } from "./EFMedia/videoTasks/makeScrubVideoSegmentIdTask.ts";
16
- import { makeUnifiedVideoSeekTask } from "./EFMedia/videoTasks/makeUnifiedVideoSeekTask.ts";
17
- import { makeVideoBufferTask } from "./EFMedia/videoTasks/makeVideoBufferTask.ts";
18
- import { EFMedia } from "./EFMedia.js";
19
- import { updateAnimations } from "./updateAnimations.js";
20
-
21
- // EF_FRAMEGEN is a global instance created in EF_FRAMEGEN.ts
22
- declare global {
23
- var EF_FRAMEGEN: import("../EF_FRAMEGEN.js").EFFramegen;
24
- }
25
-
26
- const log = debug("ef:elements:EFVideo");
27
-
28
- interface LoadingState {
29
- isLoading: boolean;
30
- operation: "scrub-segment" | "video-segment" | "seeking" | "decoding" | null;
31
- message: string;
32
- }
33
-
34
- @customElement("ef-video")
35
- export class EFVideo extends TWMixin(EFMedia) {
36
- static styles = [
37
- css`
38
- :host {
39
- display: block;
40
- position: relative;
41
- }
42
- canvas {
43
- overflow: hidden;
44
- position: static;
45
- width: 100%;
46
- height: 100%;
47
- margin: 0;
48
- padding: 0;
49
- overflow: hidden;
50
- border: none;
51
- outline: none;
52
- box-shadow: none;
53
- }
54
- .loading-overlay {
55
- position: absolute;
56
- top: 0;
57
- left: 0;
58
- right: 0;
59
- bottom: 0;
60
- background: rgba(0, 0, 0, 0.6);
61
- display: flex;
62
- align-items: center;
63
- justify-content: center;
64
- z-index: 10;
65
- backdrop-filter: blur(2px);
66
- }
67
- .loading-content {
68
- background: rgba(0, 0, 0, 0.8);
69
- border-radius: 8px;
70
- padding: 16px 24px;
71
- display: flex;
72
- align-items: center;
73
- gap: 12px;
74
- color: white;
75
- font-size: 14px;
76
- font-weight: 500;
77
- }
78
- .loading-spinner {
79
- width: 20px;
80
- height: 20px;
81
- border: 2px solid rgba(255, 255, 255, 0.2);
82
- border-left: 2px solid #fff;
83
- border-radius: 50%;
84
- animation: spin 1s linear infinite;
85
- }
86
- @keyframes spin {
87
- 0% { transform: rotate(0deg); }
88
- 100% { transform: rotate(360deg); }
89
- }
90
- .loading-message {
91
- font-size: 12px;
92
- opacity: 0.8;
93
- }
94
- `,
95
- ];
96
- canvasRef = createRef<HTMLCanvasElement>();
97
-
98
- /**
99
- * Duration in milliseconds for video buffering ahead of current time
100
- * @domAttribute "video-buffer-duration"
101
- */
102
- @property({ type: Number, attribute: "video-buffer-duration" })
103
- videoBufferDurationMs = 10000; // 10 seconds - reasonable for JIT encoding
104
-
105
- /**
106
- * Maximum number of concurrent video segment fetches for buffering
107
- * @domAttribute "max-video-buffer-fetches"
108
- */
109
- @property({ type: Number, attribute: "max-video-buffer-fetches" })
110
- maxVideoBufferFetches = 2;
111
-
112
- /**
113
- * Enable/disable video buffering system
114
- * @domAttribute "enable-video-buffering"
115
- */
116
- @property({ type: Boolean, attribute: "enable-video-buffering" })
117
- enableVideoBuffering = true;
118
-
119
- // Unified video system - single smart seek task that routes to scrub or main
120
- unifiedVideoSeekTask = makeUnifiedVideoSeekTask(this);
121
- videoBufferTask = makeVideoBufferTask(this); // Keep for main video buffering
122
-
123
- // Scrub video preloading system
124
- scrubVideoBufferTask = makeScrubVideoBufferTask(this);
125
- scrubVideoInputTask = makeScrubVideoInputTask(this);
126
- scrubVideoSeekTask = makeScrubVideoSeekTask(this);
127
- scrubVideoSegmentIdTask = makeScrubVideoSegmentIdTask(this);
128
- scrubVideoSegmentFetchTask = makeScrubVideoSegmentFetchTask(this);
129
- scrubVideoInitSegmentFetchTask = makeScrubVideoInitSegmentFetchTask(this);
130
-
131
- /**
132
- * Delayed loading state manager for user feedback
133
- */
134
- private delayedLoadingState: DelayedLoadingState;
135
-
136
- /**
137
- * Loading state for user feedback
138
- */
139
- @state()
140
- loadingState = {
141
- isLoading: false,
142
- operation: null as LoadingState["operation"],
143
- message: "",
144
- };
145
-
146
- constructor() {
147
- super();
148
-
149
- // Initialize delayed loading state with callback to update UI
150
- this.delayedLoadingState = new DelayedLoadingState(
151
- 250,
152
- (isLoading, message) => {
153
- this.setLoadingState(isLoading, null, message);
154
- },
155
- );
156
- }
157
-
158
- protected updated(
159
- changedProperties: PropertyValueMap<any> | Map<PropertyKey, unknown>,
160
- ): void {
161
- super.updated(changedProperties);
162
-
163
- // No need to clear canvas - displayFrame() overwrites it completely
164
- // and clearing creates blank frame gaps during transitions
165
- }
166
-
167
- render() {
168
- return html`
169
- <canvas ${ref(this.canvasRef)}></canvas>
170
- ${
171
- this.loadingState.isLoading
172
- ? html`
173
- <div class="loading-overlay">
174
- <div class="loading-content">
175
- <div class="loading-spinner"></div>
176
- <div>
177
- <div>Loading Video...</div>
178
- <div class="loading-message">${this.loadingState.message}</div>
179
- </div>
180
- </div>
181
- </div>
182
- `
183
- : ""
184
- }
185
- `;
186
- }
187
-
188
- get canvasElement() {
189
- const referencedCanvas = this.canvasRef.value;
190
- if (referencedCanvas) {
191
- return referencedCanvas;
192
- }
193
- const shadowCanvas = this.shadowRoot?.querySelector("canvas");
194
- if (shadowCanvas) {
195
- return shadowCanvas;
196
- }
197
- return undefined;
198
- }
199
-
200
- frameTask = new Task(this, {
201
- autoRun: false,
202
- args: () => [this.desiredSeekTimeMs] as const,
203
- onError: (error) => {
204
- console.error("frameTask error", error);
205
- },
206
- onComplete: () => {},
207
- task: async ([_desiredSeekTimeMs], { signal }) => {
208
- const t0 = performance.now();
209
-
210
- await withSpan(
211
- "video.frameTask",
212
- {
213
- elementId: this.id || "unknown",
214
- desiredSeekTimeMs: _desiredSeekTimeMs,
215
- src: this.src || "none",
216
- },
217
- undefined,
218
- async (span) => {
219
- const t1 = performance.now();
220
- span.setAttribute("preworkMs", t1 - t0);
221
-
222
- this.unifiedVideoSeekTask.run();
223
- const t2 = performance.now();
224
- span.setAttribute("seekRunMs", t2 - t1);
225
-
226
- await this.unifiedVideoSeekTask.taskComplete;
227
- const t3 = performance.now();
228
- span.setAttribute("seekAwaitMs", t3 - t2);
229
- if (signal.aborted) {
230
- span.setAttribute("aborted", true);
231
- return;
232
- }
233
-
234
- this.paint(this.desiredSeekTimeMs, span);
235
-
236
- if (!this.parentTimegroup) {
237
- updateAnimations(this);
238
- }
239
-
240
- const t4 = performance.now();
241
- this.paint(_desiredSeekTimeMs, span);
242
- const t5 = performance.now();
243
- span.setAttribute("paintMs", t5 - t4);
244
- span.setAttribute("totalFrameMs", t5 - t0);
245
- },
246
- );
247
- },
248
- });
249
-
250
- /**
251
- * Start a delayed loading operation for testing
252
- */
253
- startDelayedLoading(
254
- operationId: string,
255
- message: string,
256
- options: { background?: boolean } = {},
257
- ): void {
258
- this.delayedLoadingState.startLoading(operationId, message, options);
259
- }
260
-
261
- /**
262
- * Clear a delayed loading operation for testing
263
- */
264
- clearDelayedLoading(operationId: string): void {
265
- this.delayedLoadingState.clearLoading(operationId);
266
- }
267
-
268
- /**
269
- * Set loading state for user feedback
270
- */
271
- private setLoadingState(
272
- isLoading: boolean,
273
- operation: LoadingState["operation"] = null,
274
- message = "",
275
- ): void {
276
- this.loadingState = {
277
- isLoading,
278
- operation,
279
- message,
280
- };
281
- }
282
-
283
- /**
284
- * Paint the current video frame to canvas
285
- * Called by frameTask after seek is complete
286
- */
287
- paint(seekToMs: number, parentSpan?: any): void {
288
- const parentContext = parentSpan
289
- ? trace.setSpan(context.active(), parentSpan)
290
- : undefined;
291
-
292
- withSpanSync(
293
- "video.paint",
294
- {
295
- elementId: this.id || "unknown",
296
- seekToMs,
297
- src: this.src || "none",
298
- },
299
- parentContext,
300
- (span) => {
301
- const t0 = performance.now();
302
-
303
- // Check if we're in production rendering mode vs preview mode
304
- const isProductionRendering = this.isInProductionRenderingMode();
305
- const t1 = performance.now();
306
- span.setAttribute("isProductionRendering", isProductionRendering);
307
- span.setAttribute("modeCheckMs", t1 - t0);
308
-
309
- // Unified video system: smart routing to scrub or main, with background upgrades
310
- // Note: frameTask guarantees unifiedVideoSeekTask is complete before calling paint
311
- try {
312
- const t2 = performance.now();
313
- const videoSample = this.unifiedVideoSeekTask.value;
314
- span.setAttribute("hasVideoSample", !!videoSample);
315
- span.setAttribute("valueAccessMs", t2 - t1);
316
-
317
- if (videoSample) {
318
- const t3 = performance.now();
319
- const videoFrame = videoSample.toVideoFrame();
320
- const t4 = performance.now();
321
- span.setAttribute("toVideoFrameMs", t4 - t3);
322
-
323
- try {
324
- const t5 = performance.now();
325
- this.displayFrame(videoFrame, seekToMs, span);
326
- const t6 = performance.now();
327
- span.setAttribute("displayFrameMs", t6 - t5);
328
- } finally {
329
- videoFrame.close();
330
- }
331
- }
332
- } catch (error) {
333
- console.warn("Unified video pipeline error:", error);
334
- }
335
-
336
- // EF_FRAMEGEN-aware rendering mode detection
337
- if (!isProductionRendering) {
338
- // Preview mode: skip rendering during initialization to prevent artifacts
339
- if (
340
- !this.rootTimegroup ||
341
- (this.rootTimegroup.currentTimeMs === 0 &&
342
- this.desiredSeekTimeMs === 0)
343
- ) {
344
- span.setAttribute("skipped", "preview-initialization");
345
- return; // Skip initialization frame in preview mode
346
- }
347
- // Preview mode: proceed with rendering
348
- } else {
349
- // Production rendering mode: only render when EF_FRAMEGEN has explicitly started frame rendering
350
- // This prevents initialization frames before the actual render sequence begins
351
- if (!this.rootTimegroup) {
352
- span.setAttribute("skipped", "no-root-timegroup");
353
- return;
354
- }
355
-
356
- if (!this.isFrameRenderingActive()) {
357
- span.setAttribute("skipped", "frame-rendering-not-active");
358
- return; // Wait for EF_FRAMEGEN to start frame sequence
359
- }
360
-
361
- // Production mode: EF_FRAMEGEN has started frame sequence, proceed with rendering
362
- }
363
-
364
- const tEnd = performance.now();
365
- span.setAttribute("totalPaintMs", tEnd - t0);
366
- },
367
- );
368
- }
369
-
370
- /**
371
- * Clear the canvas when element becomes inactive
372
- */
373
- clearCanvas(): void {
374
- if (!this.canvasElement) return;
375
-
376
- const ctx = this.canvasElement.getContext("2d");
377
- if (ctx) {
378
- ctx.clearRect(0, 0, this.canvasElement.width, this.canvasElement.height);
379
- }
380
- }
381
-
382
- /**
383
- * Display a video frame on the canvas
384
- */
385
- displayFrame(frame: VideoFrame, seekToMs: number, parentSpan?: any): void {
386
- const parentContext = parentSpan
387
- ? trace.setSpan(context.active(), parentSpan)
388
- : undefined;
389
-
390
- withSpanSync(
391
- "video.displayFrame",
392
- {
393
- elementId: this.id || "unknown",
394
- seekToMs,
395
- format: frame.format || "unknown",
396
- width: frame.codedWidth,
397
- height: frame.codedHeight,
398
- },
399
- parentContext,
400
- (span) => {
401
- const t0 = performance.now();
402
-
403
- log("trace: displayFrame start", {
404
- seekToMs,
405
- frameFormat: frame.format,
406
- });
407
-
408
- if (!this.canvasElement) {
409
- log("trace: displayFrame aborted - no canvas element");
410
- throw new Error(
411
- `Frame display failed: Canvas element is not available at time ${seekToMs}ms. The video component may not be properly initialized.`,
412
- );
413
- }
414
- const t1 = performance.now();
415
- span.setAttribute("getCanvasMs", Math.round((t1 - t0) * 100) / 100);
416
-
417
- const ctx = this.canvasElement.getContext("2d");
418
- const t2 = performance.now();
419
- span.setAttribute("getCtxMs", Math.round((t2 - t1) * 100) / 100);
420
-
421
- if (!ctx) {
422
- log("trace: displayFrame aborted - no canvas context");
423
- throw new Error(
424
- `Frame display failed: Unable to get 2D canvas context at time ${seekToMs}ms. This may indicate a browser compatibility issue or canvas corruption.`,
425
- );
426
- }
427
-
428
- let resized = false;
429
- if (frame?.codedWidth && frame?.codedHeight) {
430
- if (
431
- this.canvasElement.width !== frame.codedWidth ||
432
- this.canvasElement.height !== frame.codedHeight
433
- ) {
434
- log("trace: updating canvas dimensions", {
435
- width: frame.codedWidth,
436
- height: frame.codedHeight,
437
- });
438
- this.canvasElement.width = frame.codedWidth;
439
- this.canvasElement.height = frame.codedHeight;
440
- resized = true;
441
- const t3 = performance.now();
442
- span.setAttribute("resizeMs", Math.round((t3 - t2) * 100) / 100);
443
- }
444
- }
445
- span.setAttribute("canvasResized", resized);
446
-
447
- if (frame.format === null) {
448
- log("trace: displayFrame aborted - null frame format");
449
- throw new Error(
450
- `Frame display failed: Video frame has null format at time ${seekToMs}ms. This indicates corrupted or incompatible video data.`,
451
- );
452
- }
453
-
454
- const tDrawStart = performance.now();
455
- ctx.drawImage(
456
- frame,
457
- 0,
458
- 0,
459
- this.canvasElement.width,
460
- this.canvasElement.height,
461
- );
462
- const tDrawEnd = performance.now();
463
- span.setAttribute(
464
- "drawImageMs",
465
- Math.round((tDrawEnd - tDrawStart) * 100) / 100,
466
- );
467
- span.setAttribute(
468
- "totalDisplayMs",
469
- Math.round((tDrawEnd - t0) * 100) / 100,
470
- );
471
- span.setAttribute("canvasWidth", this.canvasElement.width);
472
- span.setAttribute("canvasHeight", this.canvasElement.height);
473
-
474
- log("trace: frame drawn to canvas", { seekToMs });
475
- },
476
- );
477
- }
478
-
479
- /**
480
- * Check if we're in production rendering mode (EF_FRAMEGEN active) vs preview mode
481
- */
482
- private isInProductionRenderingMode(): boolean {
483
- // Check if EF_RENDERING function exists and returns true (production rendering)
484
- if (typeof window.EF_RENDERING === "function") {
485
- return window.EF_RENDERING();
486
- }
487
-
488
- // Check if workbench is in rendering mode
489
- const workbench = document.querySelector("ef-workbench") as any;
490
- if (workbench?.rendering) {
491
- return true;
492
- }
493
-
494
- // Check if EF_FRAMEGEN exists and has render options (indicates active rendering)
495
- if (window.EF_FRAMEGEN?.renderOptions) {
496
- return true;
497
- }
498
-
499
- // Default to preview mode
500
- return false;
501
- }
502
-
503
- /**
504
- * Check if EF_FRAMEGEN has explicitly started frame rendering (not just initialization)
505
- */
506
- private isFrameRenderingActive(): boolean {
507
- if (!window.EF_FRAMEGEN?.renderOptions) {
508
- return false;
509
- }
510
-
511
- // In production mode, only render when EF_FRAMEGEN has actually begun frame sequence
512
- // Check if we're past the initialization phase by looking for explicit frame control
513
- const renderOptions = window.EF_FRAMEGEN.renderOptions;
514
- const renderStartTime = renderOptions.encoderOptions.fromMs;
515
- const currentTime = this.rootTimegroup?.currentTimeMs || 0;
516
-
517
- // We're in active frame rendering if:
518
- // 1. currentTime >= renderStartTime (includes the starting frame)
519
- return currentTime >= renderStartTime;
520
- }
521
-
522
- /**
523
- * Legacy getter for fragment index task
524
- * Still used by EFCaptions - maps to unified video seek task
525
- */
526
- get fragmentIndexTask() {
527
- return this.unifiedVideoSeekTask;
528
- }
529
-
530
- /**
531
- * Helper method for tests: wait for the current frame to be ready
532
- * This encapsulates the complexity of ensuring the video has updated
533
- * and its frameTask has completed.
534
- *
535
- * @returns Promise that resolves when the frame is ready
536
- */
537
- async waitForFrameReady(): Promise<void> {
538
- await this.updateComplete;
539
- await this.frameTask.run();
540
- }
541
-
542
- /**
543
- * Clean up resources when component is disconnected
544
- */
545
- disconnectedCallback(): void {
546
- super.disconnectedCallback();
547
-
548
- // Clean up delayed loading state
549
- this.delayedLoadingState.clearAllLoading();
550
- }
551
-
552
- didBecomeRoot() {
553
- super.didBecomeRoot();
554
- }
555
- didBecomeChild() {
556
- super.didBecomeChild();
557
- }
558
- }
559
-
560
- declare global {
561
- interface HTMLElementTagNameMap {
562
- "ef-video": EFVideo;
563
- }
564
- }