@editframe/elements 0.20.1-beta.0 → 0.20.3-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/AssetIdMediaEngine.js +1 -1
- package/dist/elements/EFMedia/AssetMediaEngine.js +7 -4
- package/dist/elements/EFMedia/videoTasks/makeScrubVideoInitSegmentFetchTask.js +2 -0
- package/dist/elements/EFMedia/videoTasks/makeScrubVideoInputTask.js +2 -0
- package/dist/elements/EFMedia/videoTasks/makeScrubVideoSegmentFetchTask.js +2 -0
- package/dist/elements/EFMedia/videoTasks/makeScrubVideoSegmentIdTask.js +2 -0
- package/dist/elements/EFMedia/videoTasks/makeUnifiedVideoSeekTask.js +2 -0
- package/package.json +2 -2
- package/src/elements/EFMedia/AssetIdMediaEngine.ts +3 -1
- package/src/elements/EFMedia/AssetMediaEngine.ts +17 -4
- package/src/elements/EFMedia/videoTasks/makeScrubVideoInitSegmentFetchTask.ts +5 -0
- package/src/elements/EFMedia/videoTasks/makeScrubVideoInputTask.ts +5 -1
- package/src/elements/EFMedia/videoTasks/makeScrubVideoSegmentFetchTask.ts +5 -0
- package/src/elements/EFMedia/videoTasks/makeScrubVideoSegmentIdTask.ts +5 -0
- package/src/elements/EFMedia/videoTasks/makeUnifiedVideoSeekTask.ts +11 -0
- package/src/utils/LRUCache.test.ts +3 -1
|
@@ -41,7 +41,7 @@ var AssetIdMediaEngine = class AssetIdMediaEngine extends AssetMediaEngine {
|
|
|
41
41
|
return `${this.apiHost}/api/v1/isobmff_tracks/${this.assetId}/${trackId}`;
|
|
42
42
|
}
|
|
43
43
|
convertToSegmentRelativeTimestamps(globalTimestamps, segmentId, rendition) {
|
|
44
|
-
if (!rendition.trackId) throw new Error("Track ID is required for asset metadata");
|
|
44
|
+
if (!rendition.trackId) throw new Error("[convertToSegmentRelativeTimestamps] Track ID is required for asset metadata");
|
|
45
45
|
const trackData = this.data[rendition.trackId];
|
|
46
46
|
if (!trackData) throw new Error("Track not found");
|
|
47
47
|
const segment = trackData.segments?.[segmentId];
|
|
@@ -63,7 +63,7 @@ var AssetMediaEngine = class AssetMediaEngine extends BaseMediaEngine {
|
|
|
63
63
|
return `/@ef-track/${this.src}?trackId=${trackId}&segmentId=${segmentId}`;
|
|
64
64
|
}
|
|
65
65
|
async fetchInitSegment(rendition, signal) {
|
|
66
|
-
if (!rendition.trackId) throw new Error("Track ID is required for asset metadata");
|
|
66
|
+
if (!rendition.trackId) throw new Error("[fetchInitSegment] Track ID is required for asset metadata");
|
|
67
67
|
const url = this.buildInitSegmentUrl(rendition.trackId);
|
|
68
68
|
const initSegment = this.data[rendition.trackId]?.initSegment;
|
|
69
69
|
if (!initSegment) throw new Error("Init segment not found");
|
|
@@ -71,7 +71,7 @@ var AssetMediaEngine = class AssetMediaEngine extends BaseMediaEngine {
|
|
|
71
71
|
return this.fetchMediaWithHeaders(url, headers, signal);
|
|
72
72
|
}
|
|
73
73
|
async fetchMediaSegment(segmentId, rendition, signal) {
|
|
74
|
-
if (!rendition.trackId) throw new Error("Track ID is required for asset metadata");
|
|
74
|
+
if (!rendition.trackId) throw new Error("[fetchMediaSegment] Track ID is required for asset metadata");
|
|
75
75
|
if (segmentId === void 0) throw new Error("Segment ID is not available");
|
|
76
76
|
const url = this.buildMediaSegmentUrl(rendition.trackId, segmentId);
|
|
77
77
|
const mediaSegment = this.data[rendition.trackId]?.segments[segmentId];
|
|
@@ -113,7 +113,10 @@ var AssetMediaEngine = class AssetMediaEngine extends BaseMediaEngine {
|
|
|
113
113
|
return segmentRanges;
|
|
114
114
|
}
|
|
115
115
|
computeSegmentId(seekTimeMs, rendition) {
|
|
116
|
-
if (!rendition.trackId)
|
|
116
|
+
if (!rendition.trackId) {
|
|
117
|
+
console.warn(`computeSegmentId: trackId not found for rendition ${JSON.stringify(rendition)}`);
|
|
118
|
+
throw new Error("[computeSegmentId] Track ID is required for asset metadata");
|
|
119
|
+
}
|
|
117
120
|
const track = this.data[rendition.trackId];
|
|
118
121
|
if (!track) throw new Error("Track not found");
|
|
119
122
|
const { timescale, segments } = track;
|
|
@@ -159,7 +162,7 @@ var AssetMediaEngine = class AssetMediaEngine extends BaseMediaEngine {
|
|
|
159
162
|
}
|
|
160
163
|
convertToSegmentRelativeTimestamps(globalTimestamps, segmentId, rendition) {
|
|
161
164
|
{
|
|
162
|
-
if (!rendition.trackId) throw new Error("Track ID is required for asset metadata");
|
|
165
|
+
if (!rendition.trackId) throw new Error("[convertToSegmentRelativeTimestamps] Track ID is required for asset metadata");
|
|
163
166
|
const trackData = this.data[rendition.trackId];
|
|
164
167
|
if (!trackData) throw new Error("Track not found");
|
|
165
168
|
const segment = trackData.segments?.[segmentId];
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { EF_RENDERING } from "../../../EF_RENDERING.js";
|
|
1
2
|
import { getLatestMediaEngine } from "../tasks/makeMediaEngineTask.js";
|
|
2
3
|
import { Task } from "@lit/task";
|
|
3
4
|
const makeScrubVideoInitSegmentFetchTask = (host) => {
|
|
@@ -8,6 +9,7 @@ const makeScrubVideoInitSegmentFetchTask = (host) => {
|
|
|
8
9
|
},
|
|
9
10
|
onComplete: (_value) => {},
|
|
10
11
|
task: async ([_mediaEngine], { signal }) => {
|
|
12
|
+
if (EF_RENDERING()) return /* @__PURE__ */ new ArrayBuffer(0);
|
|
11
13
|
const mediaEngine = await getLatestMediaEngine(host, signal);
|
|
12
14
|
const scrubRendition = mediaEngine.getScrubVideoRendition();
|
|
13
15
|
if (!scrubRendition) throw new Error("No scrub rendition available");
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { EF_RENDERING } from "../../../EF_RENDERING.js";
|
|
1
2
|
import { BufferedSeekingInput } from "../BufferedSeekingInput.js";
|
|
2
3
|
import { EFMedia } from "../../EFMedia.js";
|
|
3
4
|
import { Task } from "@lit/task";
|
|
@@ -9,6 +10,7 @@ const makeScrubVideoInputTask = (host) => {
|
|
|
9
10
|
},
|
|
10
11
|
onComplete: (_value) => {},
|
|
11
12
|
task: async () => {
|
|
13
|
+
if (EF_RENDERING()) console.info("Scrub not available in rendering mode");
|
|
12
14
|
const initSegment = await host.scrubVideoInitSegmentFetchTask.taskComplete;
|
|
13
15
|
const segment = await host.scrubVideoSegmentFetchTask.taskComplete;
|
|
14
16
|
if (!initSegment || !segment) throw new Error("Scrub init segment or segment is not available");
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { EF_RENDERING } from "../../../EF_RENDERING.js";
|
|
1
2
|
import { getLatestMediaEngine } from "../tasks/makeMediaEngineTask.js";
|
|
2
3
|
import { Task } from "@lit/task";
|
|
3
4
|
const makeScrubVideoSegmentFetchTask = (host) => {
|
|
@@ -8,6 +9,7 @@ const makeScrubVideoSegmentFetchTask = (host) => {
|
|
|
8
9
|
},
|
|
9
10
|
onComplete: (_value) => {},
|
|
10
11
|
task: async (_, { signal }) => {
|
|
12
|
+
if (EF_RENDERING()) return /* @__PURE__ */ new ArrayBuffer(0);
|
|
11
13
|
const mediaEngine = await getLatestMediaEngine(host, signal);
|
|
12
14
|
const segmentId = await host.scrubVideoSegmentIdTask.taskComplete;
|
|
13
15
|
if (segmentId === void 0) throw new Error("Scrub segment ID is not available for video");
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { EF_RENDERING } from "../../../EF_RENDERING.js";
|
|
1
2
|
import { getLatestMediaEngine } from "../tasks/makeMediaEngineTask.js";
|
|
2
3
|
import { Task } from "@lit/task";
|
|
3
4
|
const makeScrubVideoSegmentIdTask = (host) => {
|
|
@@ -8,6 +9,7 @@ const makeScrubVideoSegmentIdTask = (host) => {
|
|
|
8
9
|
},
|
|
9
10
|
onComplete: (_value) => {},
|
|
10
11
|
task: async ([, targetSeekTimeMs], { signal }) => {
|
|
12
|
+
if (EF_RENDERING()) return void 0;
|
|
11
13
|
const mediaEngine = await getLatestMediaEngine(host, signal);
|
|
12
14
|
signal.throwIfAborted();
|
|
13
15
|
const scrubRendition = mediaEngine.getScrubVideoRendition();
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { EF_RENDERING } from "../../../EF_RENDERING.js";
|
|
1
2
|
import { getLatestMediaEngine } from "../tasks/makeMediaEngineTask.js";
|
|
2
3
|
import { ScrubInputCache } from "./ScrubInputCache.js";
|
|
3
4
|
import { Task } from "@lit/task";
|
|
@@ -13,6 +14,7 @@ const makeUnifiedVideoSeekTask = (host) => {
|
|
|
13
14
|
task: async ([desiredSeekTimeMs], { signal }) => {
|
|
14
15
|
const mediaEngine = await getLatestMediaEngine(host, signal);
|
|
15
16
|
if (!mediaEngine) return void 0;
|
|
17
|
+
if (EF_RENDERING()) return await getMainVideoSample(host, mediaEngine, desiredSeekTimeMs, signal);
|
|
16
18
|
const mainRendition = mediaEngine.videoRendition;
|
|
17
19
|
if (mainRendition) {
|
|
18
20
|
const mainSegmentId = mediaEngine.computeSegmentId(desiredSeekTimeMs, mainRendition);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@editframe/elements",
|
|
3
|
-
"version": "0.20.
|
|
3
|
+
"version": "0.20.3-beta.0",
|
|
4
4
|
"description": "",
|
|
5
5
|
"exports": {
|
|
6
6
|
".": {
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
"license": "UNLICENSED",
|
|
28
28
|
"dependencies": {
|
|
29
29
|
"@bramus/style-observer": "^1.3.0",
|
|
30
|
-
"@editframe/assets": "0.20.
|
|
30
|
+
"@editframe/assets": "0.20.3-beta.0",
|
|
31
31
|
"@lit/context": "^1.1.2",
|
|
32
32
|
"@lit/task": "^1.0.1",
|
|
33
33
|
"d3": "^7.9.0",
|
|
@@ -87,7 +87,9 @@ export class AssetIdMediaEngine
|
|
|
87
87
|
rendition: VideoRendition,
|
|
88
88
|
): number[] {
|
|
89
89
|
if (!rendition.trackId) {
|
|
90
|
-
throw new Error(
|
|
90
|
+
throw new Error(
|
|
91
|
+
"[convertToSegmentRelativeTimestamps] Track ID is required for asset metadata",
|
|
92
|
+
);
|
|
91
93
|
}
|
|
92
94
|
// For AssetMediaEngine, we need to calculate the actual segment start time
|
|
93
95
|
// using the precise segment boundaries from the track fragment index
|
|
@@ -110,7 +110,9 @@ export class AssetMediaEngine extends BaseMediaEngine implements MediaEngine {
|
|
|
110
110
|
signal: AbortSignal,
|
|
111
111
|
) {
|
|
112
112
|
if (!rendition.trackId) {
|
|
113
|
-
throw new Error(
|
|
113
|
+
throw new Error(
|
|
114
|
+
"[fetchInitSegment] Track ID is required for asset metadata",
|
|
115
|
+
);
|
|
114
116
|
}
|
|
115
117
|
const url = this.buildInitSegmentUrl(rendition.trackId);
|
|
116
118
|
const initSegment = this.data[rendition.trackId]?.initSegment;
|
|
@@ -132,7 +134,9 @@ export class AssetMediaEngine extends BaseMediaEngine implements MediaEngine {
|
|
|
132
134
|
signal?: AbortSignal,
|
|
133
135
|
) {
|
|
134
136
|
if (!rendition.trackId) {
|
|
135
|
-
throw new Error(
|
|
137
|
+
throw new Error(
|
|
138
|
+
"[fetchMediaSegment] Track ID is required for asset metadata",
|
|
139
|
+
);
|
|
136
140
|
}
|
|
137
141
|
if (segmentId === undefined) {
|
|
138
142
|
throw new Error("Segment ID is not available");
|
|
@@ -217,7 +221,14 @@ export class AssetMediaEngine extends BaseMediaEngine implements MediaEngine {
|
|
|
217
221
|
|
|
218
222
|
computeSegmentId(seekTimeMs: number, rendition: MediaRendition) {
|
|
219
223
|
if (!rendition.trackId) {
|
|
220
|
-
|
|
224
|
+
console.warn(
|
|
225
|
+
`computeSegmentId: trackId not found for rendition ${JSON.stringify(
|
|
226
|
+
rendition,
|
|
227
|
+
)}`,
|
|
228
|
+
);
|
|
229
|
+
throw new Error(
|
|
230
|
+
"[computeSegmentId] Track ID is required for asset metadata",
|
|
231
|
+
);
|
|
221
232
|
}
|
|
222
233
|
const track = this.data[rendition.trackId];
|
|
223
234
|
if (!track) {
|
|
@@ -311,7 +322,9 @@ export class AssetMediaEngine extends BaseMediaEngine implements MediaEngine {
|
|
|
311
322
|
// This is because Asset segments are independent timeline fragments
|
|
312
323
|
|
|
313
324
|
if (!rendition.trackId) {
|
|
314
|
-
throw new Error(
|
|
325
|
+
throw new Error(
|
|
326
|
+
"[convertToSegmentRelativeTimestamps] Track ID is required for asset metadata",
|
|
327
|
+
);
|
|
315
328
|
}
|
|
316
329
|
// For AssetMediaEngine, we need to calculate the actual segment start time
|
|
317
330
|
// using the precise segment boundaries from the track fragment index
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Task } from "@lit/task";
|
|
2
|
+
import { EF_RENDERING } from "../../../EF_RENDERING";
|
|
2
3
|
import type { MediaEngine } from "../../../transcoding/types";
|
|
3
4
|
import type { EFVideo } from "../../EFVideo";
|
|
4
5
|
import { getLatestMediaEngine } from "../tasks/makeMediaEngineTask";
|
|
@@ -13,6 +14,10 @@ export const makeScrubVideoInitSegmentFetchTask = (
|
|
|
13
14
|
},
|
|
14
15
|
onComplete: (_value) => {},
|
|
15
16
|
task: async ([_mediaEngine], { signal }) => {
|
|
17
|
+
if (EF_RENDERING()) {
|
|
18
|
+
return new ArrayBuffer(0);
|
|
19
|
+
}
|
|
20
|
+
|
|
16
21
|
const mediaEngine = await getLatestMediaEngine(host, signal);
|
|
17
22
|
|
|
18
23
|
// Get scrub rendition using the proper interface method
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Task } from "@lit/task";
|
|
2
|
-
|
|
2
|
+
import { EF_RENDERING } from "../../../EF_RENDERING";
|
|
3
3
|
import { EFMedia } from "../../EFMedia";
|
|
4
4
|
import type { EFVideo } from "../../EFVideo";
|
|
5
5
|
import { BufferedSeekingInput } from "../BufferedSeekingInput";
|
|
@@ -20,6 +20,10 @@ export const makeScrubVideoInputTask = (host: EFVideo): InputTask => {
|
|
|
20
20
|
},
|
|
21
21
|
onComplete: (_value) => {},
|
|
22
22
|
task: async () => {
|
|
23
|
+
if (EF_RENDERING()) {
|
|
24
|
+
console.info("Scrub not available in rendering mode");
|
|
25
|
+
}
|
|
26
|
+
|
|
23
27
|
const initSegment =
|
|
24
28
|
await host.scrubVideoInitSegmentFetchTask.taskComplete;
|
|
25
29
|
const segment = await host.scrubVideoSegmentFetchTask.taskComplete;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Task } from "@lit/task";
|
|
2
|
+
import { EF_RENDERING } from "../../../EF_RENDERING";
|
|
2
3
|
import type { MediaEngine } from "../../../transcoding/types";
|
|
3
4
|
import type { EFVideo } from "../../EFVideo";
|
|
4
5
|
import { getLatestMediaEngine } from "../tasks/makeMediaEngineTask";
|
|
@@ -17,6 +18,10 @@ export const makeScrubVideoSegmentFetchTask = (
|
|
|
17
18
|
},
|
|
18
19
|
onComplete: (_value) => {},
|
|
19
20
|
task: async (_, { signal }) => {
|
|
21
|
+
if (EF_RENDERING()) {
|
|
22
|
+
return new ArrayBuffer(0);
|
|
23
|
+
}
|
|
24
|
+
|
|
20
25
|
const mediaEngine = await getLatestMediaEngine(host, signal);
|
|
21
26
|
const segmentId = await host.scrubVideoSegmentIdTask.taskComplete;
|
|
22
27
|
if (segmentId === undefined) {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Task } from "@lit/task";
|
|
2
|
+
import { EF_RENDERING } from "../../../EF_RENDERING";
|
|
2
3
|
import type { MediaEngine } from "../../../transcoding/types";
|
|
3
4
|
import type { EFVideo } from "../../EFVideo";
|
|
4
5
|
import { getLatestMediaEngine } from "../tasks/makeMediaEngineTask";
|
|
@@ -13,6 +14,10 @@ export const makeScrubVideoSegmentIdTask = (
|
|
|
13
14
|
},
|
|
14
15
|
onComplete: (_value) => {},
|
|
15
16
|
task: async ([, targetSeekTimeMs], { signal }) => {
|
|
17
|
+
if (EF_RENDERING()) {
|
|
18
|
+
return undefined;
|
|
19
|
+
}
|
|
20
|
+
|
|
16
21
|
const mediaEngine = await getLatestMediaEngine(host, signal);
|
|
17
22
|
signal.throwIfAborted(); // Abort if a new seek started
|
|
18
23
|
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Task } from "@lit/task";
|
|
2
2
|
import type { VideoSample } from "mediabunny";
|
|
3
|
+
import { EF_RENDERING } from "../../../EF_RENDERING";
|
|
3
4
|
import type { VideoRendition } from "../../../transcoding/types";
|
|
4
5
|
import type { EFVideo } from "../../EFVideo";
|
|
5
6
|
import { getLatestMediaEngine } from "../tasks/makeMediaEngineTask";
|
|
@@ -24,6 +25,16 @@ export const makeUnifiedVideoSeekTask = (
|
|
|
24
25
|
const mediaEngine = await getLatestMediaEngine(host, signal);
|
|
25
26
|
if (!mediaEngine) return undefined;
|
|
26
27
|
|
|
28
|
+
// In rendering mode, skip all the scrub logic and go straight to main
|
|
29
|
+
if (EF_RENDERING()) {
|
|
30
|
+
return await getMainVideoSample(
|
|
31
|
+
host,
|
|
32
|
+
mediaEngine,
|
|
33
|
+
desiredSeekTimeMs,
|
|
34
|
+
signal,
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
|
|
27
38
|
// FIRST: Check if main quality content is already cached
|
|
28
39
|
const mainRendition = mediaEngine.videoRendition;
|
|
29
40
|
if (mainRendition) {
|
|
@@ -244,7 +244,9 @@ describe("OrderedLRUCache", () => {
|
|
|
244
244
|
expect(allItems.map((item) => item.key)).toEqual([100, 200, 300]); // 200±500 = [-300,700] contains all
|
|
245
245
|
});
|
|
246
246
|
|
|
247
|
-
|
|
247
|
+
// This test is cute, but we can't perfectly controll wall time
|
|
248
|
+
// in CI, so it's flaky.
|
|
249
|
+
test.skip("performance characteristics with large dataset", () => {
|
|
248
250
|
const cache = new OrderedLRUCache<number, string>(1000);
|
|
249
251
|
|
|
250
252
|
// Insert many items
|