@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.
- package/dist/elements/EFTimegroup.js +7 -2
- package/dist/elements/EFTimegroup.js.map +1 -1
- package/package.json +2 -2
- package/scripts/build-css.js +3 -3
- package/tsdown.config.ts +1 -1
- package/types.json +1 -1
- package/src/elements/ContextProxiesController.ts +0 -124
- package/src/elements/CrossUpdateController.ts +0 -22
- package/src/elements/EFAudio.browsertest.ts +0 -706
- package/src/elements/EFAudio.ts +0 -56
- package/src/elements/EFCaptions.browsertest.ts +0 -1960
- package/src/elements/EFCaptions.ts +0 -823
- package/src/elements/EFImage.browsertest.ts +0 -120
- package/src/elements/EFImage.ts +0 -113
- package/src/elements/EFMedia/AssetIdMediaEngine.test.ts +0 -224
- package/src/elements/EFMedia/AssetIdMediaEngine.ts +0 -110
- package/src/elements/EFMedia/AssetMediaEngine.browsertest.ts +0 -140
- package/src/elements/EFMedia/AssetMediaEngine.ts +0 -385
- package/src/elements/EFMedia/BaseMediaEngine.browsertest.ts +0 -400
- package/src/elements/EFMedia/BaseMediaEngine.ts +0 -505
- package/src/elements/EFMedia/BufferedSeekingInput.browsertest.ts +0 -386
- package/src/elements/EFMedia/BufferedSeekingInput.ts +0 -430
- package/src/elements/EFMedia/JitMediaEngine.browsertest.ts +0 -226
- package/src/elements/EFMedia/JitMediaEngine.ts +0 -256
- package/src/elements/EFMedia/audioTasks/makeAudioBufferTask.browsertest.ts +0 -679
- package/src/elements/EFMedia/audioTasks/makeAudioBufferTask.ts +0 -117
- package/src/elements/EFMedia/audioTasks/makeAudioFrequencyAnalysisTask.ts +0 -246
- package/src/elements/EFMedia/audioTasks/makeAudioInitSegmentFetchTask.browsertest.ts +0 -59
- package/src/elements/EFMedia/audioTasks/makeAudioInitSegmentFetchTask.ts +0 -27
- package/src/elements/EFMedia/audioTasks/makeAudioInputTask.browsertest.ts +0 -55
- package/src/elements/EFMedia/audioTasks/makeAudioInputTask.ts +0 -53
- package/src/elements/EFMedia/audioTasks/makeAudioSeekTask.chunkboundary.regression.browsertest.ts +0 -207
- package/src/elements/EFMedia/audioTasks/makeAudioSeekTask.ts +0 -72
- package/src/elements/EFMedia/audioTasks/makeAudioSegmentFetchTask.ts +0 -32
- package/src/elements/EFMedia/audioTasks/makeAudioSegmentIdTask.ts +0 -29
- package/src/elements/EFMedia/audioTasks/makeAudioTasksVideoOnly.browsertest.ts +0 -95
- package/src/elements/EFMedia/audioTasks/makeAudioTimeDomainAnalysisTask.ts +0 -184
- package/src/elements/EFMedia/shared/AudioSpanUtils.ts +0 -129
- package/src/elements/EFMedia/shared/BufferUtils.ts +0 -342
- package/src/elements/EFMedia/shared/GlobalInputCache.ts +0 -77
- package/src/elements/EFMedia/shared/MediaTaskUtils.ts +0 -44
- package/src/elements/EFMedia/shared/PrecisionUtils.ts +0 -46
- package/src/elements/EFMedia/shared/RenditionHelpers.browsertest.ts +0 -246
- package/src/elements/EFMedia/shared/RenditionHelpers.ts +0 -56
- package/src/elements/EFMedia/shared/ThumbnailExtractor.ts +0 -227
- package/src/elements/EFMedia/tasks/makeMediaEngineTask.browsertest.ts +0 -167
- package/src/elements/EFMedia/tasks/makeMediaEngineTask.ts +0 -88
- package/src/elements/EFMedia/videoTasks/MainVideoInputCache.ts +0 -76
- package/src/elements/EFMedia/videoTasks/ScrubInputCache.ts +0 -61
- package/src/elements/EFMedia/videoTasks/makeScrubVideoBufferTask.ts +0 -114
- package/src/elements/EFMedia/videoTasks/makeScrubVideoInitSegmentFetchTask.ts +0 -35
- package/src/elements/EFMedia/videoTasks/makeScrubVideoInputTask.ts +0 -52
- package/src/elements/EFMedia/videoTasks/makeScrubVideoSeekTask.ts +0 -124
- package/src/elements/EFMedia/videoTasks/makeScrubVideoSegmentFetchTask.ts +0 -44
- package/src/elements/EFMedia/videoTasks/makeScrubVideoSegmentIdTask.ts +0 -32
- package/src/elements/EFMedia/videoTasks/makeUnifiedVideoSeekTask.ts +0 -370
- package/src/elements/EFMedia/videoTasks/makeVideoBufferTask.ts +0 -109
- package/src/elements/EFMedia.browsertest.ts +0 -872
- package/src/elements/EFMedia.ts +0 -341
- package/src/elements/EFSourceMixin.ts +0 -60
- package/src/elements/EFSurface.browsertest.ts +0 -151
- package/src/elements/EFSurface.ts +0 -142
- package/src/elements/EFTemporal.browsertest.ts +0 -215
- package/src/elements/EFTemporal.ts +0 -800
- package/src/elements/EFThumbnailStrip.browsertest.ts +0 -585
- package/src/elements/EFThumbnailStrip.media-engine.browsertest.ts +0 -714
- package/src/elements/EFThumbnailStrip.ts +0 -906
- package/src/elements/EFTimegroup.browsertest.ts +0 -870
- package/src/elements/EFTimegroup.ts +0 -878
- package/src/elements/EFVideo.browsertest.ts +0 -1482
- package/src/elements/EFVideo.ts +0 -564
- package/src/elements/EFWaveform.ts +0 -547
- package/src/elements/FetchContext.browsertest.ts +0 -401
- package/src/elements/FetchMixin.ts +0 -38
- package/src/elements/SampleBuffer.ts +0 -94
- package/src/elements/TargetController.browsertest.ts +0 -230
- package/src/elements/TargetController.ts +0 -224
- package/src/elements/TimegroupController.ts +0 -26
- package/src/elements/durationConverter.ts +0 -35
- package/src/elements/parseTimeToMs.ts +0 -9
- package/src/elements/printTaskStatus.ts +0 -16
- package/src/elements/renderTemporalAudio.ts +0 -108
- package/src/elements/updateAnimations.browsertest.ts +0 -1884
- package/src/elements/updateAnimations.ts +0 -217
- package/src/elements/util.ts +0 -24
- package/src/gui/ContextMixin.browsertest.ts +0 -860
- package/src/gui/ContextMixin.ts +0 -562
- package/src/gui/Controllable.browsertest.ts +0 -258
- package/src/gui/Controllable.ts +0 -41
- package/src/gui/EFConfiguration.ts +0 -40
- package/src/gui/EFControls.browsertest.ts +0 -389
- package/src/gui/EFControls.ts +0 -195
- package/src/gui/EFDial.browsertest.ts +0 -84
- package/src/gui/EFDial.ts +0 -172
- package/src/gui/EFFilmstrip.browsertest.ts +0 -712
- package/src/gui/EFFilmstrip.ts +0 -1349
- package/src/gui/EFFitScale.ts +0 -152
- package/src/gui/EFFocusOverlay.ts +0 -79
- package/src/gui/EFPause.browsertest.ts +0 -202
- package/src/gui/EFPause.ts +0 -73
- package/src/gui/EFPlay.browsertest.ts +0 -202
- package/src/gui/EFPlay.ts +0 -73
- package/src/gui/EFPreview.ts +0 -74
- package/src/gui/EFResizableBox.browsertest.ts +0 -79
- package/src/gui/EFResizableBox.ts +0 -898
- package/src/gui/EFScrubber.ts +0 -151
- package/src/gui/EFTimeDisplay.browsertest.ts +0 -237
- package/src/gui/EFTimeDisplay.ts +0 -55
- package/src/gui/EFToggleLoop.ts +0 -35
- package/src/gui/EFTogglePlay.ts +0 -70
- package/src/gui/EFWorkbench.ts +0 -115
- package/src/gui/PlaybackController.ts +0 -527
- package/src/gui/TWMixin.css +0 -6
- package/src/gui/TWMixin.ts +0 -61
- package/src/gui/TargetOrContextMixin.ts +0 -185
- package/src/gui/currentTimeContext.ts +0 -5
- package/src/gui/durationContext.ts +0 -3
- package/src/gui/efContext.ts +0 -6
- package/src/gui/fetchContext.ts +0 -5
- package/src/gui/focusContext.ts +0 -7
- package/src/gui/focusedElementContext.ts +0 -5
- package/src/gui/playingContext.ts +0 -5
- package/src/otel/BridgeSpanExporter.ts +0 -150
- package/src/otel/setupBrowserTracing.ts +0 -73
- package/src/otel/tracingHelpers.ts +0 -251
- package/src/transcoding/cache/RequestDeduplicator.test.ts +0 -170
- package/src/transcoding/cache/RequestDeduplicator.ts +0 -65
- package/src/transcoding/cache/URLTokenDeduplicator.test.ts +0 -182
- package/src/transcoding/cache/URLTokenDeduplicator.ts +0 -101
- package/src/transcoding/types/index.ts +0 -312
- package/src/transcoding/utils/MediaUtils.ts +0 -63
- package/src/transcoding/utils/UrlGenerator.ts +0 -68
- package/src/transcoding/utils/constants.ts +0 -36
- package/src/utils/LRUCache.test.ts +0 -274
- package/src/utils/LRUCache.ts +0 -696
|
@@ -1,370 +0,0 @@
|
|
|
1
|
-
import { Task } from "@lit/task";
|
|
2
|
-
import type { VideoSample } from "mediabunny";
|
|
3
|
-
import { withSpan } from "../../../otel/tracingHelpers.js";
|
|
4
|
-
import type { VideoRendition } from "../../../transcoding/types";
|
|
5
|
-
import { EFMedia } from "../../EFMedia.js";
|
|
6
|
-
import type { EFVideo } from "../../EFVideo";
|
|
7
|
-
import { BufferedSeekingInput } from "../BufferedSeekingInput.js";
|
|
8
|
-
import { getLatestMediaEngine } from "../tasks/makeMediaEngineTask";
|
|
9
|
-
import { MainVideoInputCache } from "./MainVideoInputCache";
|
|
10
|
-
import { ScrubInputCache } from "./ScrubInputCache";
|
|
11
|
-
|
|
12
|
-
type UnifiedVideoSeekTask = Task<readonly [number], VideoSample | undefined>;
|
|
13
|
-
|
|
14
|
-
// Shared cache for scrub inputs
|
|
15
|
-
const scrubInputCache = new ScrubInputCache();
|
|
16
|
-
|
|
17
|
-
// Shared cache for main video inputs
|
|
18
|
-
const mainVideoInputCache = new MainVideoInputCache();
|
|
19
|
-
|
|
20
|
-
export const makeUnifiedVideoSeekTask = (
|
|
21
|
-
host: EFVideo,
|
|
22
|
-
): UnifiedVideoSeekTask => {
|
|
23
|
-
return new Task(host, {
|
|
24
|
-
autoRun: false,
|
|
25
|
-
args: () => [host.desiredSeekTimeMs] as const,
|
|
26
|
-
onError: (error) => {
|
|
27
|
-
console.error("unifiedVideoSeekTask error", error);
|
|
28
|
-
},
|
|
29
|
-
onComplete: (_value) => {},
|
|
30
|
-
task: async ([desiredSeekTimeMs], { signal }) => {
|
|
31
|
-
const mediaEngine = await getLatestMediaEngine(host, signal);
|
|
32
|
-
if (!mediaEngine || signal.aborted) return undefined;
|
|
33
|
-
|
|
34
|
-
// FIRST: Check if main quality content is already cached
|
|
35
|
-
const mainRendition = mediaEngine.videoRendition;
|
|
36
|
-
if (mainRendition) {
|
|
37
|
-
const mainSegmentId = mediaEngine.computeSegmentId(
|
|
38
|
-
desiredSeekTimeMs,
|
|
39
|
-
mainRendition,
|
|
40
|
-
);
|
|
41
|
-
if (
|
|
42
|
-
mainSegmentId !== undefined &&
|
|
43
|
-
mediaEngine.isSegmentCached(mainSegmentId, mainRendition)
|
|
44
|
-
) {
|
|
45
|
-
const result = await getMainVideoSample(
|
|
46
|
-
host,
|
|
47
|
-
mediaEngine,
|
|
48
|
-
desiredSeekTimeMs,
|
|
49
|
-
signal,
|
|
50
|
-
);
|
|
51
|
-
|
|
52
|
-
if (signal.aborted) {
|
|
53
|
-
return undefined;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
return result;
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
// SECOND: Main not cached, try scrub path (instant if cached)
|
|
61
|
-
const scrubSample = await tryGetScrubSample(
|
|
62
|
-
mediaEngine,
|
|
63
|
-
desiredSeekTimeMs,
|
|
64
|
-
signal,
|
|
65
|
-
);
|
|
66
|
-
|
|
67
|
-
if (scrubSample || signal.aborted) {
|
|
68
|
-
if (signal.aborted) {
|
|
69
|
-
return undefined;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
// If scrub succeeded, start background main quality upgrade (non-blocking)
|
|
73
|
-
if (scrubSample) {
|
|
74
|
-
startMainQualityUpgrade(
|
|
75
|
-
host,
|
|
76
|
-
mediaEngine,
|
|
77
|
-
desiredSeekTimeMs,
|
|
78
|
-
signal,
|
|
79
|
-
).catch(() => {
|
|
80
|
-
// Main upgrade failed - scrub already succeeded, that's fine
|
|
81
|
-
});
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
return scrubSample;
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
// THIRD: Neither are cached, fetch main video path as final fallback
|
|
88
|
-
const result = await getMainVideoSample(
|
|
89
|
-
host,
|
|
90
|
-
mediaEngine,
|
|
91
|
-
desiredSeekTimeMs,
|
|
92
|
-
signal,
|
|
93
|
-
);
|
|
94
|
-
|
|
95
|
-
if (signal.aborted) {
|
|
96
|
-
return undefined;
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
return result;
|
|
100
|
-
},
|
|
101
|
-
});
|
|
102
|
-
};
|
|
103
|
-
|
|
104
|
-
/**
|
|
105
|
-
* Try to get scrub sample from cache (instant if available)
|
|
106
|
-
*/
|
|
107
|
-
async function tryGetScrubSample(
|
|
108
|
-
mediaEngine: any,
|
|
109
|
-
desiredSeekTimeMs: number,
|
|
110
|
-
signal: AbortSignal,
|
|
111
|
-
): Promise<VideoSample | undefined> {
|
|
112
|
-
return withSpan(
|
|
113
|
-
"video.tryGetScrubSample",
|
|
114
|
-
{
|
|
115
|
-
desiredSeekTimeMs,
|
|
116
|
-
src: mediaEngine.src || "unknown",
|
|
117
|
-
},
|
|
118
|
-
undefined,
|
|
119
|
-
async (span) => {
|
|
120
|
-
try {
|
|
121
|
-
// Get scrub rendition
|
|
122
|
-
let scrubRendition: VideoRendition | undefined;
|
|
123
|
-
|
|
124
|
-
// Check if media engine has a getScrubVideoRendition method (AssetMediaEngine, etc.)
|
|
125
|
-
if (typeof mediaEngine.getScrubVideoRendition === "function") {
|
|
126
|
-
scrubRendition = mediaEngine.getScrubVideoRendition();
|
|
127
|
-
} else if ("data" in mediaEngine && mediaEngine.data?.videoRenditions) {
|
|
128
|
-
// Fallback to data structure for other engines
|
|
129
|
-
scrubRendition = mediaEngine.data.videoRenditions.find(
|
|
130
|
-
(r: any) => r.id === "scrub",
|
|
131
|
-
);
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
if (!scrubRendition) {
|
|
135
|
-
span.setAttribute("result", "no-scrub-rendition");
|
|
136
|
-
return undefined;
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
const scrubRenditionWithSrc = {
|
|
140
|
-
...scrubRendition,
|
|
141
|
-
src: mediaEngine.src,
|
|
142
|
-
};
|
|
143
|
-
|
|
144
|
-
// Check if scrub segment is cached
|
|
145
|
-
const segmentId = mediaEngine.computeSegmentId(
|
|
146
|
-
desiredSeekTimeMs,
|
|
147
|
-
scrubRenditionWithSrc,
|
|
148
|
-
);
|
|
149
|
-
if (segmentId === undefined) {
|
|
150
|
-
span.setAttribute("result", "no-segment-id");
|
|
151
|
-
return undefined;
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
const isCached = mediaEngine.isSegmentCached(
|
|
155
|
-
segmentId,
|
|
156
|
-
scrubRenditionWithSrc,
|
|
157
|
-
);
|
|
158
|
-
span.setAttribute("isCached", isCached);
|
|
159
|
-
if (!isCached) {
|
|
160
|
-
span.setAttribute("result", "not-cached");
|
|
161
|
-
return undefined; // Not cached - let main video handle it
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
// Get cached scrub input and seek within it
|
|
165
|
-
const scrubInput = await scrubInputCache.getOrCreateInput(
|
|
166
|
-
segmentId,
|
|
167
|
-
async () => {
|
|
168
|
-
const [initSegment, mediaSegment] = await Promise.all([
|
|
169
|
-
mediaEngine.fetchInitSegment(scrubRenditionWithSrc, signal),
|
|
170
|
-
mediaEngine.fetchMediaSegment(segmentId, scrubRenditionWithSrc),
|
|
171
|
-
]);
|
|
172
|
-
|
|
173
|
-
if (!initSegment || !mediaSegment || signal.aborted)
|
|
174
|
-
return undefined;
|
|
175
|
-
|
|
176
|
-
const { BufferedSeekingInput } = await import(
|
|
177
|
-
"../BufferedSeekingInput.js"
|
|
178
|
-
);
|
|
179
|
-
const { EFMedia } = await import("../../EFMedia.js");
|
|
180
|
-
|
|
181
|
-
return new BufferedSeekingInput(
|
|
182
|
-
await new Blob([initSegment, mediaSegment]).arrayBuffer(),
|
|
183
|
-
{
|
|
184
|
-
videoBufferSize: EFMedia.VIDEO_SAMPLE_BUFFER_SIZE,
|
|
185
|
-
audioBufferSize: EFMedia.AUDIO_SAMPLE_BUFFER_SIZE,
|
|
186
|
-
startTimeOffsetMs: scrubRendition.startTimeOffsetMs,
|
|
187
|
-
},
|
|
188
|
-
);
|
|
189
|
-
},
|
|
190
|
-
);
|
|
191
|
-
|
|
192
|
-
if (!scrubInput) {
|
|
193
|
-
span.setAttribute("result", "no-scrub-input");
|
|
194
|
-
return undefined;
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
if (signal.aborted) {
|
|
198
|
-
span.setAttribute("result", "aborted-after-scrub-input");
|
|
199
|
-
return undefined;
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
const videoTrack = await scrubInput.getFirstVideoTrack();
|
|
203
|
-
if (!videoTrack) {
|
|
204
|
-
span.setAttribute("result", "no-video-track");
|
|
205
|
-
return undefined;
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
if (signal.aborted) {
|
|
209
|
-
span.setAttribute("result", "aborted-after-scrub-track");
|
|
210
|
-
return undefined;
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
const sample = (await scrubInput.seek(
|
|
214
|
-
videoTrack.id,
|
|
215
|
-
desiredSeekTimeMs,
|
|
216
|
-
)) as unknown as VideoSample | undefined;
|
|
217
|
-
|
|
218
|
-
span.setAttribute("result", sample ? "success" : "no-sample");
|
|
219
|
-
return sample;
|
|
220
|
-
} catch (_error) {
|
|
221
|
-
if (signal.aborted) {
|
|
222
|
-
span.setAttribute("result", "aborted");
|
|
223
|
-
return undefined;
|
|
224
|
-
}
|
|
225
|
-
span.setAttribute("result", "error");
|
|
226
|
-
return undefined; // Scrub failed - let main video handle it
|
|
227
|
-
}
|
|
228
|
-
},
|
|
229
|
-
);
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
/**
|
|
233
|
-
* Get main video sample (slower path with fetching)
|
|
234
|
-
*/
|
|
235
|
-
async function getMainVideoSample(
|
|
236
|
-
_host: EFVideo,
|
|
237
|
-
mediaEngine: any,
|
|
238
|
-
desiredSeekTimeMs: number,
|
|
239
|
-
signal: AbortSignal,
|
|
240
|
-
): Promise<VideoSample | undefined> {
|
|
241
|
-
return withSpan(
|
|
242
|
-
"video.getMainVideoSample",
|
|
243
|
-
{
|
|
244
|
-
desiredSeekTimeMs,
|
|
245
|
-
src: mediaEngine.src || "unknown",
|
|
246
|
-
},
|
|
247
|
-
undefined,
|
|
248
|
-
async (span) => {
|
|
249
|
-
try {
|
|
250
|
-
// Use existing main video task chain
|
|
251
|
-
const videoRendition = mediaEngine.getVideoRendition();
|
|
252
|
-
if (!videoRendition) {
|
|
253
|
-
throw new Error(
|
|
254
|
-
"Video rendition unavailable after checking videoRendition exists",
|
|
255
|
-
);
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
const segmentId = mediaEngine.computeSegmentId(
|
|
259
|
-
desiredSeekTimeMs,
|
|
260
|
-
videoRendition,
|
|
261
|
-
);
|
|
262
|
-
if (segmentId === undefined) {
|
|
263
|
-
span.setAttribute("result", "no-segment-id");
|
|
264
|
-
return undefined;
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
span.setAttribute("segmentId", segmentId);
|
|
268
|
-
|
|
269
|
-
// Get cached main video input or create new one
|
|
270
|
-
const mainInput = await mainVideoInputCache.getOrCreateInput(
|
|
271
|
-
mediaEngine.src,
|
|
272
|
-
segmentId,
|
|
273
|
-
videoRendition.id,
|
|
274
|
-
async () => {
|
|
275
|
-
// Fetch main video segment (will be cached at mediaEngine level)
|
|
276
|
-
const [initSegment, mediaSegment] = await Promise.all([
|
|
277
|
-
mediaEngine.fetchInitSegment(videoRendition, signal),
|
|
278
|
-
mediaEngine.fetchMediaSegment(segmentId, videoRendition, signal),
|
|
279
|
-
]);
|
|
280
|
-
|
|
281
|
-
if (!initSegment || !mediaSegment) {
|
|
282
|
-
return undefined;
|
|
283
|
-
}
|
|
284
|
-
signal.throwIfAborted();
|
|
285
|
-
|
|
286
|
-
const startTimeOffsetMs = videoRendition?.startTimeOffsetMs;
|
|
287
|
-
|
|
288
|
-
return new BufferedSeekingInput(
|
|
289
|
-
await new Blob([initSegment, mediaSegment]).arrayBuffer(),
|
|
290
|
-
{
|
|
291
|
-
videoBufferSize: EFMedia.VIDEO_SAMPLE_BUFFER_SIZE,
|
|
292
|
-
audioBufferSize: EFMedia.AUDIO_SAMPLE_BUFFER_SIZE,
|
|
293
|
-
startTimeOffsetMs,
|
|
294
|
-
},
|
|
295
|
-
);
|
|
296
|
-
},
|
|
297
|
-
);
|
|
298
|
-
|
|
299
|
-
if (!mainInput) {
|
|
300
|
-
span.setAttribute("result", "no-segments");
|
|
301
|
-
return undefined;
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
if (signal.aborted) {
|
|
305
|
-
span.setAttribute("result", "aborted-after-input");
|
|
306
|
-
return undefined;
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
const videoTrack = await mainInput.getFirstVideoTrack();
|
|
310
|
-
if (!videoTrack) {
|
|
311
|
-
span.setAttribute("result", "no-video-track");
|
|
312
|
-
return undefined;
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
if (signal.aborted) {
|
|
316
|
-
span.setAttribute("result", "aborted-after-track");
|
|
317
|
-
return undefined;
|
|
318
|
-
}
|
|
319
|
-
|
|
320
|
-
const sample = (await mainInput.seek(
|
|
321
|
-
videoTrack.id,
|
|
322
|
-
desiredSeekTimeMs,
|
|
323
|
-
)) as unknown as VideoSample | undefined;
|
|
324
|
-
|
|
325
|
-
span.setAttribute("result", sample ? "success" : "no-sample");
|
|
326
|
-
return sample;
|
|
327
|
-
} catch (error) {
|
|
328
|
-
if (signal.aborted) {
|
|
329
|
-
span.setAttribute("result", "aborted");
|
|
330
|
-
return undefined;
|
|
331
|
-
}
|
|
332
|
-
throw error;
|
|
333
|
-
}
|
|
334
|
-
},
|
|
335
|
-
);
|
|
336
|
-
}
|
|
337
|
-
|
|
338
|
-
/**
|
|
339
|
-
* Start background upgrade to main quality (non-blocking)
|
|
340
|
-
*/
|
|
341
|
-
async function startMainQualityUpgrade(
|
|
342
|
-
host: EFVideo,
|
|
343
|
-
mediaEngine: any,
|
|
344
|
-
targetSeekTimeMs: number,
|
|
345
|
-
signal: AbortSignal,
|
|
346
|
-
): Promise<void> {
|
|
347
|
-
// Small delay to let scrub content display first
|
|
348
|
-
await new Promise((resolve) => setTimeout(resolve, 50));
|
|
349
|
-
if (signal.aborted || host.desiredSeekTimeMs !== targetSeekTimeMs) return;
|
|
350
|
-
|
|
351
|
-
// Get main quality sample and upgrade display
|
|
352
|
-
const mainSample = await getMainVideoSample(
|
|
353
|
-
host,
|
|
354
|
-
mediaEngine,
|
|
355
|
-
targetSeekTimeMs,
|
|
356
|
-
signal,
|
|
357
|
-
);
|
|
358
|
-
if (
|
|
359
|
-
mainSample &&
|
|
360
|
-
!signal.aborted &&
|
|
361
|
-
host.desiredSeekTimeMs === targetSeekTimeMs
|
|
362
|
-
) {
|
|
363
|
-
const videoFrame = mainSample.toVideoFrame();
|
|
364
|
-
try {
|
|
365
|
-
host.displayFrame(videoFrame, targetSeekTimeMs);
|
|
366
|
-
} finally {
|
|
367
|
-
videoFrame.close();
|
|
368
|
-
}
|
|
369
|
-
}
|
|
370
|
-
}
|
|
@@ -1,109 +0,0 @@
|
|
|
1
|
-
import { Task } from "@lit/task";
|
|
2
|
-
|
|
3
|
-
import { EF_INTERACTIVE } from "../../../EF_INTERACTIVE";
|
|
4
|
-
import { EF_RENDERING } from "../../../EF_RENDERING";
|
|
5
|
-
import type { VideoRendition } from "../../../transcoding/types";
|
|
6
|
-
import type { EFVideo } from "../../EFVideo";
|
|
7
|
-
import {
|
|
8
|
-
type MediaBufferConfig,
|
|
9
|
-
type MediaBufferState,
|
|
10
|
-
manageMediaBuffer,
|
|
11
|
-
} from "../shared/BufferUtils";
|
|
12
|
-
import { getLatestMediaEngine } from "../tasks/makeMediaEngineTask";
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* Configuration for video buffering - extends the generic interface
|
|
16
|
-
*/
|
|
17
|
-
export interface VideoBufferConfig extends MediaBufferConfig {}
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* State of the video buffer - uses the generic interface
|
|
21
|
-
*/
|
|
22
|
-
export interface VideoBufferState extends MediaBufferState {}
|
|
23
|
-
|
|
24
|
-
type VideoBufferTask = Task<readonly [number], VideoBufferState>;
|
|
25
|
-
export const makeVideoBufferTask = (host: EFVideo): VideoBufferTask => {
|
|
26
|
-
let currentState: VideoBufferState = {
|
|
27
|
-
currentSeekTimeMs: 0,
|
|
28
|
-
requestedSegments: new Set(),
|
|
29
|
-
activeRequests: new Set(),
|
|
30
|
-
requestQueue: [],
|
|
31
|
-
};
|
|
32
|
-
|
|
33
|
-
return new Task(host, {
|
|
34
|
-
autoRun: EF_INTERACTIVE, // Make lazy - only run when element becomes timeline-active
|
|
35
|
-
args: () => [host.desiredSeekTimeMs] as const,
|
|
36
|
-
onError: (error) => {
|
|
37
|
-
console.error("videoBufferTask error", error);
|
|
38
|
-
},
|
|
39
|
-
onComplete: (value) => {
|
|
40
|
-
currentState = value;
|
|
41
|
-
},
|
|
42
|
-
task: async ([seekTimeMs], { signal }) => {
|
|
43
|
-
// Skip buffering entirely in rendering mode
|
|
44
|
-
if (EF_RENDERING()) {
|
|
45
|
-
return currentState; // Return existing state without any buffering activity
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
// Get media engine to potentially override buffer configuration
|
|
49
|
-
const mediaEngine = await getLatestMediaEngine(host, signal);
|
|
50
|
-
|
|
51
|
-
// Use media engine's buffer config, falling back to host properties
|
|
52
|
-
const engineConfig = mediaEngine.getBufferConfig();
|
|
53
|
-
const bufferDurationMs = engineConfig.videoBufferDurationMs;
|
|
54
|
-
const maxParallelFetches = engineConfig.maxVideoBufferFetches;
|
|
55
|
-
|
|
56
|
-
const currentConfig: VideoBufferConfig = {
|
|
57
|
-
bufferDurationMs,
|
|
58
|
-
maxParallelFetches,
|
|
59
|
-
enableBuffering: host.enableVideoBuffering,
|
|
60
|
-
bufferThresholdMs: engineConfig.bufferThresholdMs,
|
|
61
|
-
};
|
|
62
|
-
|
|
63
|
-
// Timeline context for priority-based buffering
|
|
64
|
-
const timelineContext =
|
|
65
|
-
host.rootTimegroup?.currentTimeMs !== undefined
|
|
66
|
-
? {
|
|
67
|
-
elementStartMs: host.startTimeMs,
|
|
68
|
-
elementEndMs: host.endTimeMs,
|
|
69
|
-
playheadMs: host.rootTimegroup.currentTimeMs,
|
|
70
|
-
}
|
|
71
|
-
: undefined;
|
|
72
|
-
|
|
73
|
-
return manageMediaBuffer<VideoRendition>(
|
|
74
|
-
seekTimeMs,
|
|
75
|
-
currentConfig,
|
|
76
|
-
currentState,
|
|
77
|
-
(host as any).intrinsicDurationMs || 10000,
|
|
78
|
-
signal,
|
|
79
|
-
{
|
|
80
|
-
computeSegmentId: async (timeMs, rendition) => {
|
|
81
|
-
// Use media engine's computeSegmentId
|
|
82
|
-
const mediaEngine = await getLatestMediaEngine(host, signal);
|
|
83
|
-
return mediaEngine.computeSegmentId(timeMs, rendition);
|
|
84
|
-
},
|
|
85
|
-
prefetchSegment: async (segmentId, rendition) => {
|
|
86
|
-
// Trigger prefetch through BaseMediaEngine - let it handle caching
|
|
87
|
-
const mediaEngine = await getLatestMediaEngine(host, signal);
|
|
88
|
-
await mediaEngine.fetchMediaSegment(segmentId, rendition);
|
|
89
|
-
// Don't return data - just ensure it's cached in BaseMediaEngine
|
|
90
|
-
},
|
|
91
|
-
isSegmentCached: (segmentId, rendition) => {
|
|
92
|
-
// Check if segment is already cached in BaseMediaEngine
|
|
93
|
-
const mediaEngine = host.mediaEngineTask.value;
|
|
94
|
-
if (!mediaEngine) return false;
|
|
95
|
-
|
|
96
|
-
return mediaEngine.isSegmentCached(segmentId, rendition);
|
|
97
|
-
},
|
|
98
|
-
getRendition: async () => {
|
|
99
|
-
// Get real video rendition from media engine
|
|
100
|
-
const mediaEngine = await getLatestMediaEngine(host, signal);
|
|
101
|
-
return mediaEngine.getVideoRendition();
|
|
102
|
-
},
|
|
103
|
-
logError: console.error,
|
|
104
|
-
},
|
|
105
|
-
timelineContext,
|
|
106
|
-
);
|
|
107
|
-
},
|
|
108
|
-
});
|
|
109
|
-
};
|