@editframe/elements 0.18.22-beta.0 → 0.18.26-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.
- package/dist/elements/EFMedia/AssetMediaEngine.d.ts +2 -1
- package/dist/elements/EFMedia/AssetMediaEngine.js +3 -0
- package/dist/elements/EFMedia/BaseMediaEngine.d.ts +9 -0
- package/dist/elements/EFMedia/BaseMediaEngine.js +31 -0
- package/dist/elements/EFMedia/JitMediaEngine.d.ts +1 -0
- package/dist/elements/EFMedia/JitMediaEngine.js +12 -0
- package/dist/elements/EFMedia/audioTasks/makeAudioBufferTask.js +11 -5
- package/dist/elements/EFMedia/shared/BufferUtils.d.ts +19 -18
- package/dist/elements/EFMedia/shared/BufferUtils.js +24 -44
- package/dist/elements/EFMedia/tasks/makeMediaEngineTask.browsertest.d.ts +8 -0
- package/dist/elements/EFMedia/tasks/makeMediaEngineTask.js +5 -5
- package/dist/elements/EFMedia/videoTasks/ScrubInputCache.d.ts +25 -0
- package/dist/elements/EFMedia/videoTasks/ScrubInputCache.js +42 -0
- package/dist/elements/EFMedia/videoTasks/makeScrubVideoBufferTask.d.ts +8 -0
- package/dist/elements/EFMedia/videoTasks/makeScrubVideoBufferTask.js +70 -0
- package/dist/elements/EFMedia/videoTasks/{makeVideoInitSegmentFetchTask.d.ts → makeScrubVideoInitSegmentFetchTask.d.ts} +1 -1
- package/dist/elements/EFMedia/videoTasks/makeScrubVideoInitSegmentFetchTask.js +21 -0
- package/dist/elements/EFMedia/videoTasks/{makeVideoInputTask.d.ts → makeScrubVideoInputTask.d.ts} +1 -1
- package/dist/elements/EFMedia/videoTasks/makeScrubVideoInputTask.js +27 -0
- package/dist/elements/EFMedia/videoTasks/makeScrubVideoSeekTask.d.ts +6 -0
- package/dist/elements/EFMedia/videoTasks/makeScrubVideoSeekTask.js +52 -0
- package/dist/elements/EFMedia/videoTasks/makeScrubVideoSegmentFetchTask.d.ts +4 -0
- package/dist/elements/EFMedia/videoTasks/makeScrubVideoSegmentFetchTask.js +23 -0
- package/dist/elements/EFMedia/videoTasks/makeScrubVideoSegmentIdTask.d.ts +4 -0
- package/dist/elements/EFMedia/videoTasks/{makeVideoSegmentIdTask.js → makeScrubVideoSegmentIdTask.js} +9 -4
- package/dist/elements/EFMedia/videoTasks/makeUnifiedVideoSeekTask.d.ts +6 -0
- package/dist/elements/EFMedia/videoTasks/makeUnifiedVideoSeekTask.js +112 -0
- package/dist/elements/EFMedia/videoTasks/makeVideoBufferTask.js +11 -5
- package/dist/elements/EFMedia.d.ts +0 -10
- package/dist/elements/EFMedia.js +1 -17
- package/dist/elements/EFVideo.d.ts +11 -9
- package/dist/elements/EFVideo.js +31 -23
- package/dist/gui/EFConfiguration.d.ts +1 -0
- package/dist/gui/EFConfiguration.js +5 -0
- package/dist/gui/EFFilmstrip.d.ts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/transcoding/types/index.d.ts +11 -0
- package/package.json +2 -2
- package/src/elements/EFCaptions.ts +1 -1
- package/src/elements/EFImage.ts +1 -1
- package/src/elements/EFMedia/AssetMediaEngine.ts +6 -0
- package/src/elements/EFMedia/BaseMediaEngine.ts +60 -0
- package/src/elements/EFMedia/JitMediaEngine.ts +18 -0
- package/src/elements/EFMedia/audioTasks/makeAudioBufferTask.browsertest.ts +185 -59
- package/src/elements/EFMedia/audioTasks/makeAudioBufferTask.ts +19 -6
- package/src/elements/EFMedia/shared/BufferUtils.ts +71 -85
- package/src/elements/EFMedia/tasks/makeMediaEngineTask.browsertest.ts +151 -112
- package/src/elements/EFMedia/tasks/makeMediaEngineTask.ts +12 -5
- package/src/elements/EFMedia/videoTasks/ScrubInputCache.ts +61 -0
- package/src/elements/EFMedia/videoTasks/makeScrubVideoBufferTask.ts +113 -0
- package/src/elements/EFMedia/videoTasks/{makeVideoInitSegmentFetchTask.ts → makeScrubVideoInitSegmentFetchTask.ts} +15 -3
- package/src/elements/EFMedia/videoTasks/{makeVideoInputTask.ts → makeScrubVideoInputTask.ts} +11 -10
- package/src/elements/EFMedia/videoTasks/makeScrubVideoSeekTask.ts +118 -0
- package/src/elements/EFMedia/videoTasks/makeScrubVideoSegmentFetchTask.ts +44 -0
- package/src/elements/EFMedia/videoTasks/{makeVideoSegmentIdTask.ts → makeScrubVideoSegmentIdTask.ts} +14 -6
- package/src/elements/EFMedia/videoTasks/makeUnifiedVideoSeekTask.ts +258 -0
- package/src/elements/EFMedia/videoTasks/makeVideoBufferTask.ts +19 -5
- package/src/elements/EFMedia.browsertest.ts +74 -11
- package/src/elements/EFMedia.ts +1 -23
- package/src/elements/EFVideo.browsertest.ts +204 -80
- package/src/elements/EFVideo.ts +38 -26
- package/src/elements/TargetController.browsertest.ts +1 -1
- package/src/gui/EFConfiguration.ts +4 -1
- package/src/gui/EFFilmstrip.ts +4 -4
- package/src/gui/EFFocusOverlay.ts +1 -1
- package/src/gui/EFPreview.ts +3 -4
- package/src/gui/EFScrubber.ts +1 -1
- package/src/gui/EFTimeDisplay.ts +1 -1
- package/src/gui/EFToggleLoop.ts +1 -1
- package/src/gui/EFTogglePlay.ts +1 -1
- package/src/gui/EFWorkbench.ts +1 -1
- package/src/transcoding/types/index.ts +16 -0
- package/test/__cache__/GET__api_v1_transcode_scrub_1_m4s_url_http_3A_2F_2Fweb_3A3000_2Fhead_moov_480p_mp4__6ff5127ebeda578a679474347fbd6137/data.bin +0 -0
- package/test/__cache__/GET__api_v1_transcode_scrub_1_m4s_url_http_3A_2F_2Fweb_3A3000_2Fhead_moov_480p_mp4__6ff5127ebeda578a679474347fbd6137/metadata.json +16 -0
- package/test/__cache__/GET__api_v1_transcode_scrub_init_m4s_url_http_3A_2F_2Fweb_3A3000_2Fhead_moov_480p_mp4__f6d4793fc9ff854ee9a738917fb64a53/data.bin +0 -0
- package/test/__cache__/GET__api_v1_transcode_scrub_init_m4s_url_http_3A_2F_2Fweb_3A3000_2Fhead_moov_480p_mp4__f6d4793fc9ff854ee9a738917fb64a53/metadata.json +16 -0
- package/test/cache-integration-verification.browsertest.ts +84 -0
- package/types.json +1 -1
- package/dist/elements/EFMedia/tasks/makeMediaEngineTask.test.d.ts +0 -1
- package/dist/elements/EFMedia/videoTasks/makeVideoBufferTask.browsertest.d.ts +0 -9
- package/dist/elements/EFMedia/videoTasks/makeVideoInitSegmentFetchTask.browsertest.d.ts +0 -9
- package/dist/elements/EFMedia/videoTasks/makeVideoInitSegmentFetchTask.js +0 -16
- package/dist/elements/EFMedia/videoTasks/makeVideoInputTask.browsertest.d.ts +0 -9
- package/dist/elements/EFMedia/videoTasks/makeVideoInputTask.js +0 -27
- package/dist/elements/EFMedia/videoTasks/makeVideoSeekTask.d.ts +0 -7
- package/dist/elements/EFMedia/videoTasks/makeVideoSeekTask.js +0 -34
- package/dist/elements/EFMedia/videoTasks/makeVideoSegmentFetchTask.browsertest.d.ts +0 -9
- package/dist/elements/EFMedia/videoTasks/makeVideoSegmentFetchTask.d.ts +0 -4
- package/dist/elements/EFMedia/videoTasks/makeVideoSegmentFetchTask.js +0 -28
- package/dist/elements/EFMedia/videoTasks/makeVideoSegmentIdTask.browsertest.d.ts +0 -9
- package/dist/elements/EFMedia/videoTasks/makeVideoSegmentIdTask.d.ts +0 -4
- package/src/elements/EFMedia/tasks/makeMediaEngineTask.test.ts +0 -233
- package/src/elements/EFMedia/videoTasks/makeVideoBufferTask.browsertest.ts +0 -555
- package/src/elements/EFMedia/videoTasks/makeVideoInitSegmentFetchTask.browsertest.ts +0 -59
- package/src/elements/EFMedia/videoTasks/makeVideoInputTask.browsertest.ts +0 -55
- package/src/elements/EFMedia/videoTasks/makeVideoSeekTask.ts +0 -65
- package/src/elements/EFMedia/videoTasks/makeVideoSegmentFetchTask.browsertest.ts +0 -57
- package/src/elements/EFMedia/videoTasks/makeVideoSegmentFetchTask.ts +0 -43
- package/src/elements/EFMedia/videoTasks/makeVideoSegmentIdTask.browsertest.ts +0 -56
|
@@ -28,13 +28,13 @@ type VideoBufferTask = Task<readonly [number], VideoBufferState>;
|
|
|
28
28
|
export const makeVideoBufferTask = (host: EFVideo): VideoBufferTask => {
|
|
29
29
|
let currentState: VideoBufferState = {
|
|
30
30
|
currentSeekTimeMs: 0,
|
|
31
|
+
requestedSegments: new Set(),
|
|
31
32
|
activeRequests: new Set(),
|
|
32
|
-
cachedSegments: new Set(),
|
|
33
33
|
requestQueue: [],
|
|
34
34
|
};
|
|
35
35
|
|
|
36
36
|
return new Task(host, {
|
|
37
|
-
autoRun: EF_INTERACTIVE, // Start buffering
|
|
37
|
+
autoRun: EF_INTERACTIVE && !EF_RENDERING(), // Start buffering only in interactive mode, not rendering
|
|
38
38
|
args: () => [host.desiredSeekTimeMs] as const,
|
|
39
39
|
onError: (error) => {
|
|
40
40
|
console.error("videoBufferTask error", error);
|
|
@@ -43,11 +43,16 @@ export const makeVideoBufferTask = (host: EFVideo): VideoBufferTask => {
|
|
|
43
43
|
currentState = value;
|
|
44
44
|
},
|
|
45
45
|
task: async ([seekTimeMs], { signal }) => {
|
|
46
|
+
// Skip buffering entirely in rendering mode
|
|
47
|
+
if (EF_RENDERING()) {
|
|
48
|
+
return currentState; // Return existing state without any buffering activity
|
|
49
|
+
}
|
|
50
|
+
|
|
46
51
|
// Use EFMedia properties directly - no hardcoded duplication!
|
|
47
52
|
const currentConfig: VideoBufferConfig = {
|
|
48
53
|
bufferDurationMs: host.videoBufferDurationMs,
|
|
49
54
|
maxParallelFetches: host.maxVideoBufferFetches,
|
|
50
|
-
enableBuffering: host.enableVideoBuffering
|
|
55
|
+
enableBuffering: host.enableVideoBuffering,
|
|
51
56
|
};
|
|
52
57
|
|
|
53
58
|
return manageMediaBuffer<VideoRendition>(
|
|
@@ -62,9 +67,18 @@ export const makeVideoBufferTask = (host: EFVideo): VideoBufferTask => {
|
|
|
62
67
|
const mediaEngine = await getLatestMediaEngine(host, signal);
|
|
63
68
|
return mediaEngine.computeSegmentId(timeMs, rendition);
|
|
64
69
|
},
|
|
65
|
-
|
|
70
|
+
prefetchSegment: async (segmentId, rendition) => {
|
|
71
|
+
// Trigger prefetch through BaseMediaEngine - let it handle caching
|
|
66
72
|
const mediaEngine = await getLatestMediaEngine(host, signal);
|
|
67
|
-
|
|
73
|
+
await mediaEngine.fetchMediaSegment(segmentId, rendition);
|
|
74
|
+
// Don't return data - just ensure it's cached in BaseMediaEngine
|
|
75
|
+
},
|
|
76
|
+
isSegmentCached: (segmentId, rendition) => {
|
|
77
|
+
// Check if segment is already cached in BaseMediaEngine
|
|
78
|
+
const mediaEngine = host.mediaEngineTask.value;
|
|
79
|
+
if (!mediaEngine) return false;
|
|
80
|
+
|
|
81
|
+
return mediaEngine.isSegmentCached(segmentId, rendition);
|
|
68
82
|
},
|
|
69
83
|
getRendition: async () => {
|
|
70
84
|
// Get real video rendition from media engine
|
|
@@ -13,6 +13,7 @@ import "./EFTimegroup.js";
|
|
|
13
13
|
import type { EFTimegroup } from "./EFTimegroup.js";
|
|
14
14
|
import "./EFVideo.js";
|
|
15
15
|
import { UrlGenerator } from "../transcoding/utils/UrlGenerator.js";
|
|
16
|
+
import { AssetMediaEngine } from "./EFMedia/AssetMediaEngine.js";
|
|
16
17
|
import type { EFVideo } from "./EFVideo.js";
|
|
17
18
|
|
|
18
19
|
@customElement("test-media")
|
|
@@ -100,7 +101,7 @@ describe("JIT Media Engine", () => {
|
|
|
100
101
|
await timegroup.waitForMediaDurations();
|
|
101
102
|
timegroup.currentTimeMs = 2200;
|
|
102
103
|
await timegroup.seekTask.taskComplete;
|
|
103
|
-
const sample = await jitVideo.
|
|
104
|
+
const sample = await jitVideo.unifiedVideoSeekTask.taskComplete;
|
|
104
105
|
expect(sample?.timestamp).toBeCloseTo(2.2, 1);
|
|
105
106
|
});
|
|
106
107
|
});
|
|
@@ -124,10 +125,10 @@ describe("JIT Media Engine", () => {
|
|
|
124
125
|
await timegroup.seekTask.taskComplete;
|
|
125
126
|
|
|
126
127
|
// Check what segment actually got loaded
|
|
127
|
-
const actualSegmentId = (jitVideo as any).
|
|
128
|
+
const actualSegmentId = (jitVideo as any).unifiedVideoSeekTask.value;
|
|
128
129
|
console.log(`videoSegmentIdTask.value = ${actualSegmentId}`);
|
|
129
130
|
|
|
130
|
-
const frame = await (jitVideo as any).
|
|
131
|
+
const frame = await (jitVideo as any).unifiedVideoSeekTask.taskComplete;
|
|
131
132
|
console.log(`Frame timestamp when seeking to 0ms: ${frame?.timestamp}`);
|
|
132
133
|
|
|
133
134
|
expect(frame).toBeDefined();
|
|
@@ -142,7 +143,7 @@ describe("JIT Media Engine", () => {
|
|
|
142
143
|
await timegroup.waitForMediaDurations();
|
|
143
144
|
timegroup.currentTimeMs = 3_000;
|
|
144
145
|
await timegroup.seekTask.taskComplete;
|
|
145
|
-
const frame = await jitVideo.
|
|
146
|
+
const frame = await jitVideo.unifiedVideoSeekTask.taskComplete;
|
|
146
147
|
expect(frame?.timestamp).toBeCloseTo(3, 1);
|
|
147
148
|
});
|
|
148
149
|
|
|
@@ -154,25 +155,30 @@ describe("JIT Media Engine", () => {
|
|
|
154
155
|
await timegroup.waitForMediaDurations();
|
|
155
156
|
timegroup.currentTimeMs = 5_000;
|
|
156
157
|
await timegroup.seekTask.taskComplete;
|
|
157
|
-
const frame = await jitVideo.
|
|
158
|
+
const frame = await jitVideo.unifiedVideoSeekTask.taskComplete;
|
|
158
159
|
expect(frame?.timestamp).toBeCloseTo(5, 1);
|
|
159
160
|
});
|
|
160
161
|
|
|
161
|
-
test("seeks ahead in
|
|
162
|
+
test("seeks ahead in increments", async ({
|
|
162
163
|
timegroup,
|
|
163
164
|
jitVideo,
|
|
164
165
|
expect,
|
|
165
166
|
}) => {
|
|
166
167
|
await timegroup.waitForMediaDurations();
|
|
167
168
|
timegroup.currentTimeMs = 0;
|
|
169
|
+
|
|
170
|
+
// Test seeking in larger increments to avoid CI timeouts
|
|
171
|
+
// while still validating incremental seeking works
|
|
172
|
+
const testPoints = [0, 500, 1000, 1500, 2000, 2500, 3000];
|
|
168
173
|
let frame: VideoSample | undefined;
|
|
169
|
-
|
|
170
|
-
|
|
174
|
+
|
|
175
|
+
for (const timeMs of testPoints) {
|
|
176
|
+
timegroup.currentTimeMs = timeMs;
|
|
171
177
|
await timegroup.seekTask.taskComplete;
|
|
172
|
-
frame = await jitVideo.
|
|
178
|
+
frame = await jitVideo.unifiedVideoSeekTask.taskComplete;
|
|
173
179
|
expect(frame).toBeDefined();
|
|
180
|
+
expect(frame?.timestamp).toBeCloseTo(timeMs / 1000, 1);
|
|
174
181
|
}
|
|
175
|
-
expect(frame?.timestamp).toBeCloseTo(3, 1);
|
|
176
182
|
});
|
|
177
183
|
});
|
|
178
184
|
|
|
@@ -189,7 +195,7 @@ describe("JIT Media Engine", () => {
|
|
|
189
195
|
|
|
190
196
|
timegroup.currentTimeMs = 2026.6666666666663;
|
|
191
197
|
await timegroup.frameTask.taskComplete;
|
|
192
|
-
const sample = await jitVideo.
|
|
198
|
+
const sample = await jitVideo.unifiedVideoSeekTask.taskComplete;
|
|
193
199
|
expect(sample?.timestamp).toBeCloseTo(2, 1);
|
|
194
200
|
});
|
|
195
201
|
|
|
@@ -224,6 +230,63 @@ describe("JIT Media Engine", () => {
|
|
|
224
230
|
});
|
|
225
231
|
});
|
|
226
232
|
|
|
233
|
+
describe("Media Engine Selection", () => {
|
|
234
|
+
const remoteSrc = "http://web:3000/head-moov-480p.mp4";
|
|
235
|
+
const localSrc = "10s-bars.mp4";
|
|
236
|
+
|
|
237
|
+
test("defaults to JitMediaEngine for remote URLs without a configuration element", async ({
|
|
238
|
+
expect,
|
|
239
|
+
}) => {
|
|
240
|
+
const video = document.createElement("ef-video");
|
|
241
|
+
video.src = remoteSrc;
|
|
242
|
+
document.body.appendChild(video);
|
|
243
|
+
await video.mediaEngineTask.run();
|
|
244
|
+
expect(video.mediaEngineTask.value).toBeInstanceOf(JitMediaEngine);
|
|
245
|
+
video.remove();
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
test("uses JitMediaEngine for remote URLs when wrapped in a default configuration", async ({
|
|
249
|
+
configuration,
|
|
250
|
+
expect,
|
|
251
|
+
}) => {
|
|
252
|
+
const video = document.createElement("ef-video");
|
|
253
|
+
video.src = remoteSrc;
|
|
254
|
+
configuration.appendChild(video); // Fixture `configuration` is already on the page.
|
|
255
|
+
await video.mediaEngineTask.run();
|
|
256
|
+
expect(video.mediaEngineTask.value).toBeInstanceOf(JitMediaEngine);
|
|
257
|
+
video.remove();
|
|
258
|
+
});
|
|
259
|
+
|
|
260
|
+
test("uses JitMediaEngine for remote URLs when configured with media-engine='cloud'", async ({
|
|
261
|
+
configuration,
|
|
262
|
+
expect,
|
|
263
|
+
}) => {
|
|
264
|
+
configuration.setAttribute("media-engine", "cloud");
|
|
265
|
+
const video = document.createElement("ef-video");
|
|
266
|
+
video.src = remoteSrc;
|
|
267
|
+
configuration.appendChild(video);
|
|
268
|
+
await video.mediaEngineTask.run();
|
|
269
|
+
expect(video.mediaEngineTask.value).toBeInstanceOf(JitMediaEngine);
|
|
270
|
+
video.remove();
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
// Note: media-engine='local' with remote URLs is not supported
|
|
274
|
+
// AssetMediaEngine is designed for local files and track fragment indexes only
|
|
275
|
+
|
|
276
|
+
test("always uses AssetMediaEngine for local src paths", async ({
|
|
277
|
+
configuration,
|
|
278
|
+
expect,
|
|
279
|
+
}) => {
|
|
280
|
+
configuration.setAttribute("media-engine", "cloud"); // Explicitly set to cloud
|
|
281
|
+
const video = document.createElement("ef-video");
|
|
282
|
+
video.src = localSrc;
|
|
283
|
+
configuration.appendChild(video);
|
|
284
|
+
await video.mediaEngineTask.run();
|
|
285
|
+
expect(video.mediaEngineTask.value).toBeInstanceOf(AssetMediaEngine);
|
|
286
|
+
video.remove();
|
|
287
|
+
});
|
|
288
|
+
});
|
|
289
|
+
|
|
227
290
|
describe("EFMedia", () => {
|
|
228
291
|
beforeEach(() => {
|
|
229
292
|
// Clean up DOM
|
package/src/elements/EFMedia.ts
CHANGED
|
@@ -86,7 +86,7 @@ export class EFMedia extends EFTargetable(
|
|
|
86
86
|
* @domAttribute "audio-buffer-duration"
|
|
87
87
|
*/
|
|
88
88
|
@property({ type: Number, attribute: "audio-buffer-duration" })
|
|
89
|
-
audioBufferDurationMs =
|
|
89
|
+
audioBufferDurationMs = 10000; // 10 seconds - reasonable for JIT encoding
|
|
90
90
|
|
|
91
91
|
/**
|
|
92
92
|
* Maximum number of concurrent audio segment fetches for buffering
|
|
@@ -258,26 +258,4 @@ export class EFMedia extends EFTargetable(
|
|
|
258
258
|
): Promise<AudioSpan> {
|
|
259
259
|
return fetchAudioSpanningTime(this, fromMs, toMs, signal);
|
|
260
260
|
}
|
|
261
|
-
|
|
262
|
-
/**
|
|
263
|
-
* Check if an audio segment is cached in the unified buffer system
|
|
264
|
-
* Now uses the same caching approach as video for consistency
|
|
265
|
-
*/
|
|
266
|
-
getCachedAudioSegment(segmentId: number): boolean {
|
|
267
|
-
return this.audioBufferTask.value?.cachedSegments.has(segmentId) ?? false;
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
/**
|
|
271
|
-
* Get cached audio segments from the unified buffer system
|
|
272
|
-
* Now uses the same caching approach as video for consistency
|
|
273
|
-
*/
|
|
274
|
-
getCachedAudioSegments(segmentIds: number[]): Set<number> {
|
|
275
|
-
const bufferState = this.audioBufferTask.value;
|
|
276
|
-
if (!bufferState) {
|
|
277
|
-
return new Set();
|
|
278
|
-
}
|
|
279
|
-
return new Set(
|
|
280
|
-
segmentIds.filter((id) => bufferState.cachedSegments.has(id)),
|
|
281
|
-
);
|
|
282
|
-
}
|
|
283
261
|
}
|