@editframe/elements 0.26.2-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 (135) hide show
  1. package/dist/elements/EFTimegroup.js +7 -2
  2. package/dist/elements/EFTimegroup.js.map +1 -1
  3. package/package.json +2 -2
  4. package/scripts/build-css.js +3 -3
  5. package/tsdown.config.ts +1 -1
  6. package/types.json +1 -1
  7. package/src/elements/ContextProxiesController.ts +0 -124
  8. package/src/elements/CrossUpdateController.ts +0 -22
  9. package/src/elements/EFAudio.browsertest.ts +0 -706
  10. package/src/elements/EFAudio.ts +0 -56
  11. package/src/elements/EFCaptions.browsertest.ts +0 -1960
  12. package/src/elements/EFCaptions.ts +0 -823
  13. package/src/elements/EFImage.browsertest.ts +0 -120
  14. package/src/elements/EFImage.ts +0 -113
  15. package/src/elements/EFMedia/AssetIdMediaEngine.test.ts +0 -224
  16. package/src/elements/EFMedia/AssetIdMediaEngine.ts +0 -110
  17. package/src/elements/EFMedia/AssetMediaEngine.browsertest.ts +0 -140
  18. package/src/elements/EFMedia/AssetMediaEngine.ts +0 -385
  19. package/src/elements/EFMedia/BaseMediaEngine.browsertest.ts +0 -400
  20. package/src/elements/EFMedia/BaseMediaEngine.ts +0 -505
  21. package/src/elements/EFMedia/BufferedSeekingInput.browsertest.ts +0 -386
  22. package/src/elements/EFMedia/BufferedSeekingInput.ts +0 -430
  23. package/src/elements/EFMedia/JitMediaEngine.browsertest.ts +0 -226
  24. package/src/elements/EFMedia/JitMediaEngine.ts +0 -256
  25. package/src/elements/EFMedia/audioTasks/makeAudioBufferTask.browsertest.ts +0 -679
  26. package/src/elements/EFMedia/audioTasks/makeAudioBufferTask.ts +0 -117
  27. package/src/elements/EFMedia/audioTasks/makeAudioFrequencyAnalysisTask.ts +0 -246
  28. package/src/elements/EFMedia/audioTasks/makeAudioInitSegmentFetchTask.browsertest.ts +0 -59
  29. package/src/elements/EFMedia/audioTasks/makeAudioInitSegmentFetchTask.ts +0 -27
  30. package/src/elements/EFMedia/audioTasks/makeAudioInputTask.browsertest.ts +0 -55
  31. package/src/elements/EFMedia/audioTasks/makeAudioInputTask.ts +0 -53
  32. package/src/elements/EFMedia/audioTasks/makeAudioSeekTask.chunkboundary.regression.browsertest.ts +0 -207
  33. package/src/elements/EFMedia/audioTasks/makeAudioSeekTask.ts +0 -72
  34. package/src/elements/EFMedia/audioTasks/makeAudioSegmentFetchTask.ts +0 -32
  35. package/src/elements/EFMedia/audioTasks/makeAudioSegmentIdTask.ts +0 -29
  36. package/src/elements/EFMedia/audioTasks/makeAudioTasksVideoOnly.browsertest.ts +0 -95
  37. package/src/elements/EFMedia/audioTasks/makeAudioTimeDomainAnalysisTask.ts +0 -184
  38. package/src/elements/EFMedia/shared/AudioSpanUtils.ts +0 -129
  39. package/src/elements/EFMedia/shared/BufferUtils.ts +0 -342
  40. package/src/elements/EFMedia/shared/GlobalInputCache.ts +0 -77
  41. package/src/elements/EFMedia/shared/MediaTaskUtils.ts +0 -44
  42. package/src/elements/EFMedia/shared/PrecisionUtils.ts +0 -46
  43. package/src/elements/EFMedia/shared/RenditionHelpers.browsertest.ts +0 -246
  44. package/src/elements/EFMedia/shared/RenditionHelpers.ts +0 -56
  45. package/src/elements/EFMedia/shared/ThumbnailExtractor.ts +0 -227
  46. package/src/elements/EFMedia/tasks/makeMediaEngineTask.browsertest.ts +0 -167
  47. package/src/elements/EFMedia/tasks/makeMediaEngineTask.ts +0 -88
  48. package/src/elements/EFMedia/videoTasks/MainVideoInputCache.ts +0 -76
  49. package/src/elements/EFMedia/videoTasks/ScrubInputCache.ts +0 -61
  50. package/src/elements/EFMedia/videoTasks/makeScrubVideoBufferTask.ts +0 -114
  51. package/src/elements/EFMedia/videoTasks/makeScrubVideoInitSegmentFetchTask.ts +0 -35
  52. package/src/elements/EFMedia/videoTasks/makeScrubVideoInputTask.ts +0 -52
  53. package/src/elements/EFMedia/videoTasks/makeScrubVideoSeekTask.ts +0 -124
  54. package/src/elements/EFMedia/videoTasks/makeScrubVideoSegmentFetchTask.ts +0 -44
  55. package/src/elements/EFMedia/videoTasks/makeScrubVideoSegmentIdTask.ts +0 -32
  56. package/src/elements/EFMedia/videoTasks/makeUnifiedVideoSeekTask.ts +0 -370
  57. package/src/elements/EFMedia/videoTasks/makeVideoBufferTask.ts +0 -109
  58. package/src/elements/EFMedia.browsertest.ts +0 -872
  59. package/src/elements/EFMedia.ts +0 -341
  60. package/src/elements/EFSourceMixin.ts +0 -60
  61. package/src/elements/EFSurface.browsertest.ts +0 -151
  62. package/src/elements/EFSurface.ts +0 -142
  63. package/src/elements/EFTemporal.browsertest.ts +0 -215
  64. package/src/elements/EFTemporal.ts +0 -800
  65. package/src/elements/EFThumbnailStrip.browsertest.ts +0 -585
  66. package/src/elements/EFThumbnailStrip.media-engine.browsertest.ts +0 -714
  67. package/src/elements/EFThumbnailStrip.ts +0 -906
  68. package/src/elements/EFTimegroup.browsertest.ts +0 -870
  69. package/src/elements/EFTimegroup.ts +0 -878
  70. package/src/elements/EFVideo.browsertest.ts +0 -1482
  71. package/src/elements/EFVideo.ts +0 -564
  72. package/src/elements/EFWaveform.ts +0 -547
  73. package/src/elements/FetchContext.browsertest.ts +0 -401
  74. package/src/elements/FetchMixin.ts +0 -38
  75. package/src/elements/SampleBuffer.ts +0 -94
  76. package/src/elements/TargetController.browsertest.ts +0 -230
  77. package/src/elements/TargetController.ts +0 -224
  78. package/src/elements/TimegroupController.ts +0 -26
  79. package/src/elements/durationConverter.ts +0 -35
  80. package/src/elements/parseTimeToMs.ts +0 -9
  81. package/src/elements/printTaskStatus.ts +0 -16
  82. package/src/elements/renderTemporalAudio.ts +0 -108
  83. package/src/elements/updateAnimations.browsertest.ts +0 -1884
  84. package/src/elements/updateAnimations.ts +0 -217
  85. package/src/elements/util.ts +0 -24
  86. package/src/gui/ContextMixin.browsertest.ts +0 -860
  87. package/src/gui/ContextMixin.ts +0 -562
  88. package/src/gui/Controllable.browsertest.ts +0 -258
  89. package/src/gui/Controllable.ts +0 -41
  90. package/src/gui/EFConfiguration.ts +0 -40
  91. package/src/gui/EFControls.browsertest.ts +0 -389
  92. package/src/gui/EFControls.ts +0 -195
  93. package/src/gui/EFDial.browsertest.ts +0 -84
  94. package/src/gui/EFDial.ts +0 -172
  95. package/src/gui/EFFilmstrip.browsertest.ts +0 -712
  96. package/src/gui/EFFilmstrip.ts +0 -1349
  97. package/src/gui/EFFitScale.ts +0 -152
  98. package/src/gui/EFFocusOverlay.ts +0 -79
  99. package/src/gui/EFPause.browsertest.ts +0 -202
  100. package/src/gui/EFPause.ts +0 -73
  101. package/src/gui/EFPlay.browsertest.ts +0 -202
  102. package/src/gui/EFPlay.ts +0 -73
  103. package/src/gui/EFPreview.ts +0 -74
  104. package/src/gui/EFResizableBox.browsertest.ts +0 -79
  105. package/src/gui/EFResizableBox.ts +0 -898
  106. package/src/gui/EFScrubber.ts +0 -151
  107. package/src/gui/EFTimeDisplay.browsertest.ts +0 -237
  108. package/src/gui/EFTimeDisplay.ts +0 -55
  109. package/src/gui/EFToggleLoop.ts +0 -35
  110. package/src/gui/EFTogglePlay.ts +0 -70
  111. package/src/gui/EFWorkbench.ts +0 -115
  112. package/src/gui/PlaybackController.ts +0 -527
  113. package/src/gui/TWMixin.css +0 -6
  114. package/src/gui/TWMixin.ts +0 -61
  115. package/src/gui/TargetOrContextMixin.ts +0 -185
  116. package/src/gui/currentTimeContext.ts +0 -5
  117. package/src/gui/durationContext.ts +0 -3
  118. package/src/gui/efContext.ts +0 -6
  119. package/src/gui/fetchContext.ts +0 -5
  120. package/src/gui/focusContext.ts +0 -7
  121. package/src/gui/focusedElementContext.ts +0 -5
  122. package/src/gui/playingContext.ts +0 -5
  123. package/src/otel/BridgeSpanExporter.ts +0 -150
  124. package/src/otel/setupBrowserTracing.ts +0 -73
  125. package/src/otel/tracingHelpers.ts +0 -251
  126. package/src/transcoding/cache/RequestDeduplicator.test.ts +0 -170
  127. package/src/transcoding/cache/RequestDeduplicator.ts +0 -65
  128. package/src/transcoding/cache/URLTokenDeduplicator.test.ts +0 -182
  129. package/src/transcoding/cache/URLTokenDeduplicator.ts +0 -101
  130. package/src/transcoding/types/index.ts +0 -312
  131. package/src/transcoding/utils/MediaUtils.ts +0 -63
  132. package/src/transcoding/utils/UrlGenerator.ts +0 -68
  133. package/src/transcoding/utils/constants.ts +0 -36
  134. package/src/utils/LRUCache.test.ts +0 -274
  135. 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
- }