@editframe/elements 0.20.3-beta.0 → 0.20.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/EF_FRAMEGEN.js +3 -20
- package/dist/elements/EFMedia/AssetMediaEngine.d.ts +4 -4
- package/dist/elements/EFMedia/AssetMediaEngine.js +8 -4
- package/dist/elements/EFMedia/BaseMediaEngine.d.ts +10 -2
- package/dist/elements/EFMedia/BaseMediaEngine.js +8 -2
- package/dist/elements/EFMedia/JitMediaEngine.js +13 -4
- package/dist/elements/EFMedia/audioTasks/makeAudioBufferTask.js +1 -1
- package/dist/elements/EFMedia/audioTasks/makeAudioFrequencyAnalysisTask.js +0 -2
- package/dist/elements/EFMedia/audioTasks/makeAudioInitSegmentFetchTask.js +1 -1
- package/dist/elements/EFMedia/audioTasks/makeAudioInputTask.js +5 -4
- package/dist/elements/EFMedia/audioTasks/makeAudioSegmentFetchTask.js +2 -12
- package/dist/elements/EFMedia/audioTasks/makeAudioSegmentIdTask.js +1 -1
- package/dist/elements/EFMedia/audioTasks/makeAudioTasksVideoOnly.browsertest.d.ts +1 -0
- package/dist/elements/EFMedia/audioTasks/makeAudioTimeDomainAnalysisTask.js +5 -2
- package/dist/elements/EFMedia/shared/AudioSpanUtils.d.ts +1 -1
- package/dist/elements/EFMedia/shared/AudioSpanUtils.js +3 -3
- package/dist/elements/EFMedia/shared/BufferUtils.d.ts +1 -1
- package/dist/elements/EFMedia/shared/BufferUtils.js +3 -1
- package/dist/elements/EFMedia/shared/MediaTaskUtils.d.ts +1 -1
- package/dist/elements/EFMedia/shared/RenditionHelpers.d.ts +1 -9
- package/dist/elements/EFMedia/tasks/makeMediaEngineTask.d.ts +1 -2
- package/dist/elements/EFMedia/tasks/makeMediaEngineTask.js +1 -6
- package/dist/elements/EFMedia/videoTasks/makeScrubVideoBufferTask.js +2 -1
- package/dist/elements/EFMedia/videoTasks/makeScrubVideoInitSegmentFetchTask.js +0 -2
- package/dist/elements/EFMedia/videoTasks/makeScrubVideoInputTask.js +0 -2
- package/dist/elements/EFMedia/videoTasks/makeScrubVideoSegmentFetchTask.js +0 -2
- package/dist/elements/EFMedia/videoTasks/makeScrubVideoSegmentIdTask.js +0 -2
- package/dist/elements/EFMedia/videoTasks/makeUnifiedVideoSeekTask.js +4 -5
- package/dist/elements/EFMedia/videoTasks/makeVideoBufferTask.js +2 -2
- package/dist/elements/EFMedia.d.ts +2 -1
- package/dist/elements/EFMedia.js +1 -0
- package/dist/elements/EFTimegroup.js +1 -1
- package/dist/transcoding/types/index.d.ts +6 -4
- package/package.json +2 -2
- package/src/elements/EFMedia/AssetIdMediaEngine.test.ts +6 -4
- package/src/elements/EFMedia/AssetMediaEngine.browsertest.ts +25 -23
- package/src/elements/EFMedia/AssetMediaEngine.ts +16 -6
- package/src/elements/EFMedia/BaseMediaEngine.browsertest.ts +94 -0
- package/src/elements/EFMedia/BaseMediaEngine.ts +10 -8
- package/src/elements/EFMedia/JitMediaEngine.ts +20 -6
- package/src/elements/EFMedia/audioTasks/makeAudioBufferTask.ts +5 -2
- package/src/elements/EFMedia/audioTasks/makeAudioFrequencyAnalysisTask.ts +0 -5
- package/src/elements/EFMedia/audioTasks/makeAudioInitSegmentFetchTask.ts +2 -1
- package/src/elements/EFMedia/audioTasks/makeAudioInputTask.ts +11 -5
- package/src/elements/EFMedia/audioTasks/makeAudioSegmentFetchTask.ts +4 -16
- package/src/elements/EFMedia/audioTasks/makeAudioSegmentIdTask.ts +4 -2
- package/src/elements/EFMedia/audioTasks/makeAudioTasksVideoOnly.browsertest.ts +95 -0
- package/src/elements/EFMedia/audioTasks/makeAudioTimeDomainAnalysisTask.ts +5 -6
- package/src/elements/EFMedia/shared/AudioSpanUtils.ts +5 -4
- package/src/elements/EFMedia/shared/BufferUtils.ts +7 -3
- package/src/elements/EFMedia/shared/MediaTaskUtils.ts +1 -1
- package/src/elements/EFMedia/shared/RenditionHelpers.browsertest.ts +41 -42
- package/src/elements/EFMedia/shared/RenditionHelpers.ts +0 -23
- package/src/elements/EFMedia/tasks/makeMediaEngineTask.ts +1 -9
- package/src/elements/EFMedia/videoTasks/makeScrubVideoBufferTask.ts +3 -2
- package/src/elements/EFMedia/videoTasks/makeScrubVideoInitSegmentFetchTask.ts +0 -5
- package/src/elements/EFMedia/videoTasks/makeScrubVideoInputTask.ts +1 -5
- package/src/elements/EFMedia/videoTasks/makeScrubVideoSegmentFetchTask.ts +0 -5
- package/src/elements/EFMedia/videoTasks/makeScrubVideoSegmentIdTask.ts +0 -5
- package/src/elements/EFMedia/videoTasks/makeUnifiedVideoSeekTask.ts +10 -19
- package/src/elements/EFMedia/videoTasks/makeVideoBufferTask.ts +2 -5
- package/src/elements/EFMedia.ts +2 -1
- package/src/elements/EFThumbnailStrip.media-engine.browsertest.ts +2 -1
- package/src/elements/EFTimegroup.ts +1 -1
- package/src/transcoding/types/index.ts +6 -4
- package/types.json +1 -1
|
@@ -4,12 +4,7 @@ import type {
|
|
|
4
4
|
MediaEngine,
|
|
5
5
|
VideoRendition,
|
|
6
6
|
} from "../../../transcoding/types";
|
|
7
|
-
import {
|
|
8
|
-
calculateSegmentRange,
|
|
9
|
-
computeSegmentId,
|
|
10
|
-
getAudioRendition,
|
|
11
|
-
getVideoRendition,
|
|
12
|
-
} from "./RenditionHelpers";
|
|
7
|
+
import { calculateSegmentRange, computeSegmentId } from "./RenditionHelpers";
|
|
13
8
|
|
|
14
9
|
const test = baseTest.extend<{
|
|
15
10
|
mockMediaEngine: MediaEngine;
|
|
@@ -36,6 +31,8 @@ const test = baseTest.extend<{
|
|
|
36
31
|
src: "https://example.com/media.mp4",
|
|
37
32
|
videoRendition: mockVideoRendition,
|
|
38
33
|
audioRendition: mockAudioRendition,
|
|
34
|
+
getVideoRendition: () => mockVideoRendition,
|
|
35
|
+
getAudioRendition: () => mockAudioRendition,
|
|
39
36
|
fetchMediaSegment: vi.fn(),
|
|
40
37
|
} as unknown as MediaEngine;
|
|
41
38
|
await use(mockMediaEngine);
|
|
@@ -60,30 +57,38 @@ const test = baseTest.extend<{
|
|
|
60
57
|
},
|
|
61
58
|
|
|
62
59
|
mockMediaEngineWithoutAudio: async ({}, use) => {
|
|
60
|
+
const videoRendition = {
|
|
61
|
+
trackId: 1,
|
|
62
|
+
src: "video-track.mp4",
|
|
63
|
+
segmentDurationMs: 1000,
|
|
64
|
+
} as VideoRendition;
|
|
65
|
+
|
|
63
66
|
const mockMediaEngine = {
|
|
64
67
|
durationMs: 10000,
|
|
65
68
|
src: "https://example.com/media.mp4",
|
|
66
|
-
videoRendition
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
} as VideoRendition,
|
|
71
|
-
audioRendition: null,
|
|
69
|
+
videoRendition,
|
|
70
|
+
audioRendition: undefined,
|
|
71
|
+
getVideoRendition: () => videoRendition,
|
|
72
|
+
getAudioRendition: () => undefined,
|
|
72
73
|
fetchMediaSegment: vi.fn(),
|
|
73
74
|
} as unknown as MediaEngine;
|
|
74
75
|
await use(mockMediaEngine);
|
|
75
76
|
},
|
|
76
77
|
|
|
77
78
|
mockMediaEngineWithoutVideo: async ({}, use) => {
|
|
79
|
+
const audioRendition = {
|
|
80
|
+
trackId: 2,
|
|
81
|
+
src: "audio-track.mp4",
|
|
82
|
+
segmentDurationMs: 1000,
|
|
83
|
+
} as AudioRendition;
|
|
84
|
+
|
|
78
85
|
const mockMediaEngine = {
|
|
79
86
|
durationMs: 10000,
|
|
80
87
|
src: "https://example.com/media.mp4",
|
|
81
|
-
videoRendition:
|
|
82
|
-
audioRendition
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
segmentDurationMs: 1000,
|
|
86
|
-
} as AudioRendition,
|
|
88
|
+
videoRendition: undefined,
|
|
89
|
+
audioRendition,
|
|
90
|
+
getVideoRendition: () => undefined,
|
|
91
|
+
getAudioRendition: () => audioRendition,
|
|
87
92
|
fetchMediaSegment: vi.fn(),
|
|
88
93
|
} as unknown as MediaEngine;
|
|
89
94
|
await use(mockMediaEngine);
|
|
@@ -91,45 +96,39 @@ const test = baseTest.extend<{
|
|
|
91
96
|
});
|
|
92
97
|
|
|
93
98
|
describe("RenditionHelpers", () => {
|
|
94
|
-
describe("
|
|
95
|
-
test("returns
|
|
96
|
-
|
|
99
|
+
describe("MediaEngine Rendition Access", () => {
|
|
100
|
+
test("mediaEngine.getAudioRendition() returns undefined for video-only assets", ({
|
|
101
|
+
mockMediaEngineWithoutAudio,
|
|
97
102
|
expect,
|
|
98
103
|
}) => {
|
|
99
|
-
const result = getAudioRendition(
|
|
100
|
-
expect(result).
|
|
101
|
-
expect(result.trackId).toBe(2);
|
|
102
|
-
expect(result.src).toBe("audio-track.mp4");
|
|
104
|
+
const result = mockMediaEngineWithoutAudio.getAudioRendition();
|
|
105
|
+
expect(result).toBeUndefined();
|
|
103
106
|
});
|
|
104
107
|
|
|
105
|
-
test("
|
|
106
|
-
|
|
108
|
+
test("mediaEngine.getVideoRendition() returns undefined for audio-only assets", ({
|
|
109
|
+
mockMediaEngineWithoutVideo,
|
|
107
110
|
expect,
|
|
108
111
|
}) => {
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
);
|
|
112
|
+
const result = mockMediaEngineWithoutVideo.getVideoRendition();
|
|
113
|
+
expect(result).toBeUndefined();
|
|
112
114
|
});
|
|
113
|
-
});
|
|
114
115
|
|
|
115
|
-
|
|
116
|
-
test("returns video rendition when available", ({
|
|
116
|
+
test("mediaEngine.getAudioRendition() returns rendition when available", ({
|
|
117
117
|
mockMediaEngine,
|
|
118
118
|
expect,
|
|
119
119
|
}) => {
|
|
120
|
-
const result =
|
|
121
|
-
expect(result).
|
|
122
|
-
expect(result
|
|
123
|
-
expect(result.src).toBe("video-track.mp4");
|
|
120
|
+
const result = mockMediaEngine.getAudioRendition();
|
|
121
|
+
expect(result).toBeDefined();
|
|
122
|
+
expect(result?.trackId).toBe(2);
|
|
124
123
|
});
|
|
125
124
|
|
|
126
|
-
test("
|
|
127
|
-
|
|
125
|
+
test("mediaEngine.getVideoRendition() returns rendition when available", ({
|
|
126
|
+
mockMediaEngine,
|
|
128
127
|
expect,
|
|
129
128
|
}) => {
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
);
|
|
129
|
+
const result = mockMediaEngine.getVideoRendition();
|
|
130
|
+
expect(result).toBeDefined();
|
|
131
|
+
expect(result?.trackId).toBe(1);
|
|
133
132
|
});
|
|
134
133
|
});
|
|
135
134
|
|
|
@@ -1,31 +1,8 @@
|
|
|
1
1
|
import type {
|
|
2
2
|
AudioRendition,
|
|
3
|
-
MediaEngine,
|
|
4
3
|
VideoRendition,
|
|
5
4
|
} from "../../../transcoding/types";
|
|
6
5
|
|
|
7
|
-
/**
|
|
8
|
-
* Get audio rendition from media engine, throwing if not available
|
|
9
|
-
*/
|
|
10
|
-
export const getAudioRendition = (mediaEngine: MediaEngine): AudioRendition => {
|
|
11
|
-
const audioRendition = mediaEngine.audioRendition;
|
|
12
|
-
if (!audioRendition) {
|
|
13
|
-
throw new Error("No audio track available in source");
|
|
14
|
-
}
|
|
15
|
-
return audioRendition;
|
|
16
|
-
};
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* Get video rendition from media engine, throwing if not available
|
|
20
|
-
*/
|
|
21
|
-
export const getVideoRendition = (mediaEngine: MediaEngine): VideoRendition => {
|
|
22
|
-
const videoRendition = mediaEngine.videoRendition;
|
|
23
|
-
if (!videoRendition) {
|
|
24
|
-
throw new Error("No video track available in source");
|
|
25
|
-
}
|
|
26
|
-
return videoRendition;
|
|
27
|
-
};
|
|
28
|
-
|
|
29
6
|
/**
|
|
30
7
|
* Calculate which segment contains a given timestamp
|
|
31
8
|
* Returns 1-based segment ID, or undefined if segmentDurationMs is not available
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Task } from "@lit/task";
|
|
2
2
|
import { EF_INTERACTIVE } from "../../../EF_INTERACTIVE";
|
|
3
|
-
import type { MediaEngine
|
|
3
|
+
import type { MediaEngine } from "../../../transcoding/types";
|
|
4
4
|
import type { EFMedia } from "../../EFMedia";
|
|
5
5
|
import { AssetIdMediaEngine } from "../AssetIdMediaEngine";
|
|
6
6
|
import { AssetMediaEngine } from "../AssetMediaEngine";
|
|
@@ -18,14 +18,6 @@ export const getLatestMediaEngine = async (
|
|
|
18
18
|
return mediaEngine;
|
|
19
19
|
};
|
|
20
20
|
|
|
21
|
-
export const getVideoRendition = (mediaEngine: MediaEngine): VideoRendition => {
|
|
22
|
-
const videoRendition = mediaEngine.videoRendition;
|
|
23
|
-
if (!videoRendition) {
|
|
24
|
-
throw new Error("No video track available in source");
|
|
25
|
-
}
|
|
26
|
-
return videoRendition;
|
|
27
|
-
};
|
|
28
|
-
|
|
29
21
|
/**
|
|
30
22
|
* Core logic for creating a MediaEngine with explicit dependencies.
|
|
31
23
|
* Pure function that requires all dependencies to be provided.
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { Task } from "@lit/task";
|
|
2
|
+
|
|
3
|
+
import { EF_INTERACTIVE } from "../../../EF_INTERACTIVE.js";
|
|
2
4
|
import { EF_RENDERING } from "../../../EF_RENDERING.js";
|
|
3
5
|
import type { VideoRendition } from "../../../transcoding/types";
|
|
4
6
|
import type { EFVideo } from "../../EFVideo";
|
|
@@ -19,8 +21,7 @@ export const makeScrubVideoBufferTask = (host: EFVideo) => {
|
|
|
19
21
|
};
|
|
20
22
|
|
|
21
23
|
return new Task(host, {
|
|
22
|
-
|
|
23
|
-
autoRun: false,
|
|
24
|
+
autoRun: EF_INTERACTIVE,
|
|
24
25
|
args: () => [host.mediaEngineTask.value] as const,
|
|
25
26
|
onError: (error) => {
|
|
26
27
|
console.error("scrubVideoBufferTask error", error);
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { Task } from "@lit/task";
|
|
2
|
-
import { EF_RENDERING } from "../../../EF_RENDERING";
|
|
3
2
|
import type { MediaEngine } from "../../../transcoding/types";
|
|
4
3
|
import type { EFVideo } from "../../EFVideo";
|
|
5
4
|
import { getLatestMediaEngine } from "../tasks/makeMediaEngineTask";
|
|
@@ -14,10 +13,6 @@ export const makeScrubVideoInitSegmentFetchTask = (
|
|
|
14
13
|
},
|
|
15
14
|
onComplete: (_value) => {},
|
|
16
15
|
task: async ([_mediaEngine], { signal }) => {
|
|
17
|
-
if (EF_RENDERING()) {
|
|
18
|
-
return new ArrayBuffer(0);
|
|
19
|
-
}
|
|
20
|
-
|
|
21
16
|
const mediaEngine = await getLatestMediaEngine(host, signal);
|
|
22
17
|
|
|
23
18
|
// Get scrub rendition using the proper interface method
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Task } from "@lit/task";
|
|
2
|
-
|
|
2
|
+
|
|
3
3
|
import { EFMedia } from "../../EFMedia";
|
|
4
4
|
import type { EFVideo } from "../../EFVideo";
|
|
5
5
|
import { BufferedSeekingInput } from "../BufferedSeekingInput";
|
|
@@ -20,10 +20,6 @@ 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
|
-
|
|
27
23
|
const initSegment =
|
|
28
24
|
await host.scrubVideoInitSegmentFetchTask.taskComplete;
|
|
29
25
|
const segment = await host.scrubVideoSegmentFetchTask.taskComplete;
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { Task } from "@lit/task";
|
|
2
|
-
import { EF_RENDERING } from "../../../EF_RENDERING";
|
|
3
2
|
import type { MediaEngine } from "../../../transcoding/types";
|
|
4
3
|
import type { EFVideo } from "../../EFVideo";
|
|
5
4
|
import { getLatestMediaEngine } from "../tasks/makeMediaEngineTask";
|
|
@@ -18,10 +17,6 @@ export const makeScrubVideoSegmentFetchTask = (
|
|
|
18
17
|
},
|
|
19
18
|
onComplete: (_value) => {},
|
|
20
19
|
task: async (_, { signal }) => {
|
|
21
|
-
if (EF_RENDERING()) {
|
|
22
|
-
return new ArrayBuffer(0);
|
|
23
|
-
}
|
|
24
|
-
|
|
25
20
|
const mediaEngine = await getLatestMediaEngine(host, signal);
|
|
26
21
|
const segmentId = await host.scrubVideoSegmentIdTask.taskComplete;
|
|
27
22
|
if (segmentId === undefined) {
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { Task } from "@lit/task";
|
|
2
|
-
import { EF_RENDERING } from "../../../EF_RENDERING";
|
|
3
2
|
import type { MediaEngine } from "../../../transcoding/types";
|
|
4
3
|
import type { EFVideo } from "../../EFVideo";
|
|
5
4
|
import { getLatestMediaEngine } from "../tasks/makeMediaEngineTask";
|
|
@@ -14,10 +13,6 @@ export const makeScrubVideoSegmentIdTask = (
|
|
|
14
13
|
},
|
|
15
14
|
onComplete: (_value) => {},
|
|
16
15
|
task: async ([, targetSeekTimeMs], { signal }) => {
|
|
17
|
-
if (EF_RENDERING()) {
|
|
18
|
-
return undefined;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
16
|
const mediaEngine = await getLatestMediaEngine(host, signal);
|
|
22
17
|
signal.throwIfAborted(); // Abort if a new seek started
|
|
23
18
|
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { Task } from "@lit/task";
|
|
2
2
|
import type { VideoSample } from "mediabunny";
|
|
3
|
-
import { EF_RENDERING } from "../../../EF_RENDERING";
|
|
4
3
|
import type { VideoRendition } from "../../../transcoding/types";
|
|
5
4
|
import type { EFVideo } from "../../EFVideo";
|
|
6
5
|
import { getLatestMediaEngine } from "../tasks/makeMediaEngineTask";
|
|
@@ -25,16 +24,6 @@ export const makeUnifiedVideoSeekTask = (
|
|
|
25
24
|
const mediaEngine = await getLatestMediaEngine(host, signal);
|
|
26
25
|
if (!mediaEngine) return undefined;
|
|
27
26
|
|
|
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
|
-
|
|
38
27
|
// FIRST: Check if main quality content is already cached
|
|
39
28
|
const mainRendition = mediaEngine.videoRendition;
|
|
40
29
|
if (mainRendition) {
|
|
@@ -186,20 +175,23 @@ async function getMainVideoSample(
|
|
|
186
175
|
): Promise<VideoSample | undefined> {
|
|
187
176
|
try {
|
|
188
177
|
// Use existing main video task chain
|
|
178
|
+
const videoRendition = mediaEngine.getVideoRendition();
|
|
179
|
+
if (!videoRendition) {
|
|
180
|
+
throw new Error(
|
|
181
|
+
"Video rendition unavailable after checking videoRendition exists",
|
|
182
|
+
);
|
|
183
|
+
}
|
|
184
|
+
|
|
189
185
|
const segmentId = mediaEngine.computeSegmentId(
|
|
190
186
|
desiredSeekTimeMs,
|
|
191
|
-
|
|
187
|
+
videoRendition,
|
|
192
188
|
);
|
|
193
189
|
if (segmentId === undefined) return undefined;
|
|
194
190
|
|
|
195
191
|
// Fetch main video segment
|
|
196
192
|
const [initSegment, mediaSegment] = await Promise.all([
|
|
197
|
-
mediaEngine.fetchInitSegment(
|
|
198
|
-
mediaEngine.fetchMediaSegment(
|
|
199
|
-
segmentId,
|
|
200
|
-
mediaEngine.getVideoRendition(),
|
|
201
|
-
signal,
|
|
202
|
-
),
|
|
193
|
+
mediaEngine.fetchInitSegment(videoRendition, signal),
|
|
194
|
+
mediaEngine.fetchMediaSegment(segmentId, videoRendition, signal),
|
|
203
195
|
]);
|
|
204
196
|
|
|
205
197
|
if (!initSegment || !mediaSegment) return undefined;
|
|
@@ -209,7 +201,6 @@ async function getMainVideoSample(
|
|
|
209
201
|
const { BufferedSeekingInput } = await import("../BufferedSeekingInput.js");
|
|
210
202
|
const { EFMedia } = await import("../../EFMedia.js");
|
|
211
203
|
|
|
212
|
-
const videoRendition = mediaEngine.videoRendition;
|
|
213
204
|
const startTimeOffsetMs = videoRendition?.startTimeOffsetMs;
|
|
214
205
|
|
|
215
206
|
const mainInput = new BufferedSeekingInput(
|
|
@@ -9,10 +9,7 @@ import {
|
|
|
9
9
|
type MediaBufferState,
|
|
10
10
|
manageMediaBuffer,
|
|
11
11
|
} from "../shared/BufferUtils";
|
|
12
|
-
import {
|
|
13
|
-
getLatestMediaEngine,
|
|
14
|
-
getVideoRendition,
|
|
15
|
-
} from "../tasks/makeMediaEngineTask";
|
|
12
|
+
import { getLatestMediaEngine } from "../tasks/makeMediaEngineTask";
|
|
16
13
|
|
|
17
14
|
/**
|
|
18
15
|
* Configuration for video buffering - extends the generic interface
|
|
@@ -90,7 +87,7 @@ export const makeVideoBufferTask = (host: EFVideo): VideoBufferTask => {
|
|
|
90
87
|
getRendition: async () => {
|
|
91
88
|
// Get real video rendition from media engine
|
|
92
89
|
const mediaEngine = await getLatestMediaEngine(host, signal);
|
|
93
|
-
return getVideoRendition(
|
|
90
|
+
return mediaEngine.getVideoRendition();
|
|
94
91
|
},
|
|
95
92
|
logError: console.error,
|
|
96
93
|
},
|
package/src/elements/EFMedia.ts
CHANGED
|
@@ -283,12 +283,13 @@ export class EFMedia extends EFTargetable(
|
|
|
283
283
|
/**
|
|
284
284
|
* Main integration method for EFTimegroup audio playback
|
|
285
285
|
* Now powered by clean, testable utility functions
|
|
286
|
+
* Returns undefined if no audio rendition is available
|
|
286
287
|
*/
|
|
287
288
|
async fetchAudioSpanningTime(
|
|
288
289
|
fromMs: number,
|
|
289
290
|
toMs: number,
|
|
290
291
|
signal: AbortSignal = new AbortController().signal,
|
|
291
|
-
): Promise<AudioSpan> {
|
|
292
|
+
): Promise<AudioSpan | undefined> {
|
|
292
293
|
return fetchAudioSpanningTime(this, fromMs, toMs, signal);
|
|
293
294
|
}
|
|
294
295
|
}
|
|
@@ -132,7 +132,8 @@ describe("MediaEngine Thumbnail Extraction", () => {
|
|
|
132
132
|
// Get segment duration to ensure timestamps are in same segment
|
|
133
133
|
const videoRendition =
|
|
134
134
|
mediaEngine.getScrubVideoRendition() || mediaEngine.getVideoRendition();
|
|
135
|
-
|
|
135
|
+
expect(videoRendition).toBeDefined();
|
|
136
|
+
const segmentDurationMs = videoRendition!.segmentDurationMs || 2000;
|
|
136
137
|
|
|
137
138
|
// Pick timestamps within the first segment - avoid edge cases near boundaries
|
|
138
139
|
const timestamps = [
|
|
@@ -228,14 +228,16 @@ export interface MediaEngine {
|
|
|
228
228
|
) => number | undefined;
|
|
229
229
|
|
|
230
230
|
/**
|
|
231
|
-
* Get the video rendition
|
|
231
|
+
* Get the video rendition if available, otherwise return undefined.
|
|
232
|
+
* Callers should handle undefined appropriately.
|
|
232
233
|
*/
|
|
233
|
-
getVideoRendition: () => VideoRendition;
|
|
234
|
+
getVideoRendition: () => VideoRendition | undefined;
|
|
234
235
|
|
|
235
236
|
/**
|
|
236
|
-
* Get the audio rendition
|
|
237
|
+
* Get the audio rendition if available, otherwise return undefined.
|
|
238
|
+
* Callers should handle undefined appropriately.
|
|
237
239
|
*/
|
|
238
|
-
getAudioRendition: () => AudioRendition;
|
|
240
|
+
getAudioRendition: () => AudioRendition | undefined;
|
|
239
241
|
|
|
240
242
|
/**
|
|
241
243
|
* Check if a segment is cached for a given rendition
|