@editframe/elements 0.17.6-beta.0 → 0.18.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/EF_FRAMEGEN.js +1 -1
- package/dist/ScrubTrackManager.d.ts +2 -2
- package/dist/elements/EFAudio.d.ts +21 -2
- package/dist/elements/EFAudio.js +41 -11
- package/dist/elements/EFImage.d.ts +1 -0
- package/dist/elements/EFImage.js +11 -3
- package/dist/elements/EFMedia/AssetIdMediaEngine.d.ts +18 -0
- package/dist/elements/EFMedia/AssetIdMediaEngine.js +41 -0
- package/dist/elements/EFMedia/AssetMediaEngine.d.ts +47 -0
- package/dist/elements/EFMedia/AssetMediaEngine.js +116 -0
- package/dist/elements/EFMedia/BaseMediaEngine.d.ts +55 -0
- package/dist/elements/EFMedia/BaseMediaEngine.js +96 -0
- package/dist/elements/EFMedia/BufferedSeekingInput.d.ts +43 -0
- package/dist/elements/EFMedia/BufferedSeekingInput.js +159 -0
- package/dist/elements/EFMedia/JitMediaEngine.browsertest.d.ts +0 -0
- package/dist/elements/EFMedia/JitMediaEngine.d.ts +31 -0
- package/dist/elements/EFMedia/JitMediaEngine.js +62 -0
- package/dist/elements/EFMedia/audioTasks/makeAudioBufferTask.browsertest.d.ts +9 -0
- package/dist/elements/EFMedia/audioTasks/makeAudioBufferTask.d.ts +16 -0
- package/dist/elements/EFMedia/audioTasks/makeAudioBufferTask.js +48 -0
- package/dist/elements/EFMedia/audioTasks/makeAudioFrequencyAnalysisTask.d.ts +3 -0
- package/dist/elements/EFMedia/audioTasks/makeAudioFrequencyAnalysisTask.js +138 -0
- package/dist/elements/EFMedia/audioTasks/makeAudioInitSegmentFetchTask.browsertest.d.ts +9 -0
- package/dist/elements/EFMedia/audioTasks/makeAudioInitSegmentFetchTask.d.ts +4 -0
- package/dist/elements/EFMedia/audioTasks/makeAudioInitSegmentFetchTask.js +16 -0
- package/dist/elements/EFMedia/audioTasks/makeAudioInputTask.browsertest.d.ts +9 -0
- package/dist/elements/EFMedia/audioTasks/makeAudioInputTask.d.ts +3 -0
- package/dist/elements/EFMedia/audioTasks/makeAudioInputTask.js +22 -0
- package/dist/elements/EFMedia/audioTasks/makeAudioSeekTask.d.ts +7 -0
- package/dist/elements/EFMedia/audioTasks/makeAudioSeekTask.js +24 -0
- package/dist/elements/EFMedia/audioTasks/makeAudioSegmentFetchTask.d.ts +4 -0
- package/dist/elements/EFMedia/audioTasks/makeAudioSegmentFetchTask.js +18 -0
- package/dist/elements/EFMedia/audioTasks/makeAudioSegmentIdTask.d.ts +4 -0
- package/dist/elements/EFMedia/audioTasks/makeAudioSegmentIdTask.js +16 -0
- package/dist/elements/EFMedia/audioTasks/makeAudioTimeDomainAnalysisTask.d.ts +3 -0
- package/dist/elements/EFMedia/audioTasks/makeAudioTimeDomainAnalysisTask.js +104 -0
- package/dist/elements/EFMedia/services/AudioElementFactory.d.ts +22 -0
- package/dist/elements/EFMedia/services/AudioElementFactory.js +72 -0
- package/dist/elements/EFMedia/services/MediaSourceService.browsertest.d.ts +1 -0
- package/dist/elements/EFMedia/services/MediaSourceService.d.ts +47 -0
- package/dist/elements/EFMedia/services/MediaSourceService.js +73 -0
- package/dist/elements/EFMedia/shared/AudioSpanUtils.d.ts +7 -0
- package/dist/elements/EFMedia/shared/AudioSpanUtils.js +54 -0
- package/dist/elements/EFMedia/shared/BufferUtils.d.ts +70 -0
- package/dist/elements/EFMedia/shared/BufferUtils.js +89 -0
- package/dist/elements/EFMedia/shared/MediaTaskUtils.d.ts +23 -0
- package/dist/elements/EFMedia/shared/RenditionHelpers.browsertest.d.ts +1 -0
- package/dist/elements/EFMedia/shared/RenditionHelpers.d.ts +19 -0
- package/dist/elements/EFMedia/tasks/makeMediaEngineTask.browsertest.d.ts +1 -0
- package/dist/elements/EFMedia/tasks/makeMediaEngineTask.d.ts +18 -0
- package/dist/elements/EFMedia/tasks/makeMediaEngineTask.js +60 -0
- package/dist/elements/EFMedia/tasks/makeMediaEngineTask.test.d.ts +1 -0
- package/dist/elements/EFMedia/videoTasks/makeVideoBufferTask.browsertest.d.ts +9 -0
- package/dist/elements/EFMedia/videoTasks/makeVideoBufferTask.d.ts +16 -0
- package/dist/elements/EFMedia/videoTasks/makeVideoBufferTask.js +46 -0
- package/dist/elements/EFMedia/videoTasks/makeVideoInitSegmentFetchTask.browsertest.d.ts +9 -0
- package/dist/elements/EFMedia/videoTasks/makeVideoInitSegmentFetchTask.d.ts +4 -0
- package/dist/elements/EFMedia/videoTasks/makeVideoInitSegmentFetchTask.js +16 -0
- package/dist/elements/EFMedia/videoTasks/makeVideoInputTask.browsertest.d.ts +9 -0
- package/dist/elements/EFMedia/videoTasks/makeVideoInputTask.d.ts +3 -0
- package/dist/elements/EFMedia/videoTasks/makeVideoInputTask.js +27 -0
- package/dist/elements/EFMedia/videoTasks/makeVideoSeekTask.d.ts +7 -0
- package/dist/elements/EFMedia/videoTasks/makeVideoSeekTask.js +25 -0
- package/dist/elements/EFMedia/videoTasks/makeVideoSegmentFetchTask.browsertest.d.ts +9 -0
- package/dist/elements/EFMedia/videoTasks/makeVideoSegmentFetchTask.d.ts +4 -0
- package/dist/elements/EFMedia/videoTasks/makeVideoSegmentFetchTask.js +18 -0
- package/dist/elements/EFMedia/videoTasks/makeVideoSegmentIdTask.browsertest.d.ts +9 -0
- package/dist/elements/EFMedia/videoTasks/makeVideoSegmentIdTask.d.ts +4 -0
- package/dist/elements/EFMedia/videoTasks/makeVideoSegmentIdTask.js +16 -0
- package/dist/elements/EFMedia.browsertest.d.ts +1 -0
- package/dist/elements/EFMedia.d.ts +75 -111
- package/dist/elements/EFMedia.js +141 -1111
- package/dist/elements/EFTemporal.d.ts +1 -1
- package/dist/elements/EFTemporal.js +1 -1
- package/dist/elements/EFTimegroup.d.ts +11 -0
- package/dist/elements/EFTimegroup.js +88 -13
- package/dist/elements/EFVideo.d.ts +60 -29
- package/dist/elements/EFVideo.js +103 -203
- package/dist/elements/EFWaveform.js +2 -2
- package/dist/elements/SampleBuffer.d.ts +14 -0
- package/dist/elements/SampleBuffer.js +52 -0
- package/dist/getRenderInfo.d.ts +2 -2
- package/dist/getRenderInfo.js +2 -1
- package/dist/gui/ContextMixin.js +17 -70
- package/dist/gui/EFFilmstrip.d.ts +3 -3
- package/dist/gui/EFFilmstrip.js +1 -1
- package/dist/gui/EFFitScale.d.ts +2 -2
- package/dist/gui/TWMixin.js +1 -1
- package/dist/gui/services/ElementConnectionManager.browsertest.d.ts +1 -0
- package/dist/gui/services/ElementConnectionManager.d.ts +59 -0
- package/dist/gui/services/ElementConnectionManager.js +128 -0
- package/dist/gui/services/PlaybackController.browsertest.d.ts +1 -0
- package/dist/gui/services/PlaybackController.d.ts +103 -0
- package/dist/gui/services/PlaybackController.js +290 -0
- package/dist/services/MediaSourceManager.d.ts +62 -0
- package/dist/services/MediaSourceManager.js +211 -0
- package/dist/style.css +1 -1
- package/dist/transcoding/cache/CacheManager.d.ts +73 -0
- package/dist/transcoding/cache/RequestDeduplicator.d.ts +29 -0
- package/dist/transcoding/cache/RequestDeduplicator.js +53 -0
- package/dist/transcoding/cache/RequestDeduplicator.test.d.ts +1 -0
- package/dist/transcoding/types/index.d.ts +242 -0
- package/dist/transcoding/utils/MediaUtils.d.ts +9 -0
- package/dist/transcoding/utils/UrlGenerator.d.ts +26 -0
- package/dist/transcoding/utils/UrlGenerator.js +45 -0
- package/dist/transcoding/utils/constants.d.ts +27 -0
- package/dist/utils/LRUCache.d.ts +34 -0
- package/dist/utils/LRUCache.js +115 -0
- package/package.json +3 -2
- package/src/elements/EFAudio.browsertest.ts +183 -43
- package/src/elements/EFAudio.ts +59 -13
- package/src/elements/EFImage.browsertest.ts +42 -0
- package/src/elements/EFImage.ts +23 -3
- package/src/elements/EFMedia/AssetIdMediaEngine.test.ts +222 -0
- package/src/elements/EFMedia/AssetIdMediaEngine.ts +70 -0
- package/src/elements/EFMedia/AssetMediaEngine.ts +210 -0
- package/src/elements/EFMedia/BaseMediaEngine.test.ts +164 -0
- package/src/elements/EFMedia/BaseMediaEngine.ts +170 -0
- package/src/elements/EFMedia/BufferedSeekingInput.browsertest.ts +400 -0
- package/src/elements/EFMedia/BufferedSeekingInput.ts +267 -0
- package/src/elements/EFMedia/JitMediaEngine.browsertest.ts +165 -0
- package/src/elements/EFMedia/JitMediaEngine.ts +110 -0
- package/src/elements/EFMedia/audioTasks/makeAudioBufferTask.browsertest.ts +554 -0
- package/src/elements/EFMedia/audioTasks/makeAudioBufferTask.ts +81 -0
- package/src/elements/EFMedia/audioTasks/makeAudioFrequencyAnalysisTask.ts +241 -0
- package/src/elements/EFMedia/audioTasks/makeAudioInitSegmentFetchTask.browsertest.ts +59 -0
- package/src/elements/EFMedia/audioTasks/makeAudioInitSegmentFetchTask.ts +23 -0
- package/src/elements/EFMedia/audioTasks/makeAudioInputTask.browsertest.ts +55 -0
- package/src/elements/EFMedia/audioTasks/makeAudioInputTask.ts +35 -0
- package/src/elements/EFMedia/audioTasks/makeAudioSeekTask.ts +42 -0
- package/src/elements/EFMedia/audioTasks/makeAudioSegmentFetchTask.ts +34 -0
- package/src/elements/EFMedia/audioTasks/makeAudioSegmentIdTask.ts +23 -0
- package/src/elements/EFMedia/audioTasks/makeAudioTimeDomainAnalysisTask.ts +174 -0
- package/src/elements/EFMedia/services/AudioElementFactory.browsertest.ts +325 -0
- package/src/elements/EFMedia/services/AudioElementFactory.ts +119 -0
- package/src/elements/EFMedia/services/MediaSourceService.browsertest.ts +257 -0
- package/src/elements/EFMedia/services/MediaSourceService.ts +102 -0
- package/src/elements/EFMedia/shared/AudioSpanUtils.ts +128 -0
- package/src/elements/EFMedia/shared/BufferUtils.ts +310 -0
- package/src/elements/EFMedia/shared/MediaTaskUtils.ts +44 -0
- package/src/elements/EFMedia/shared/RenditionHelpers.browsertest.ts +247 -0
- package/src/elements/EFMedia/shared/RenditionHelpers.ts +79 -0
- package/src/elements/EFMedia/tasks/makeMediaEngineTask.browsertest.ts +128 -0
- package/src/elements/EFMedia/tasks/makeMediaEngineTask.test.ts +233 -0
- package/src/elements/EFMedia/tasks/makeMediaEngineTask.ts +89 -0
- package/src/elements/EFMedia/videoTasks/makeVideoBufferTask.browsertest.ts +555 -0
- package/src/elements/EFMedia/videoTasks/makeVideoBufferTask.ts +79 -0
- package/src/elements/EFMedia/videoTasks/makeVideoInitSegmentFetchTask.browsertest.ts +59 -0
- package/src/elements/EFMedia/videoTasks/makeVideoInitSegmentFetchTask.ts +23 -0
- package/src/elements/EFMedia/videoTasks/makeVideoInputTask.browsertest.ts +55 -0
- package/src/elements/EFMedia/videoTasks/makeVideoInputTask.ts +45 -0
- package/src/elements/EFMedia/videoTasks/makeVideoSeekTask.ts +44 -0
- package/src/elements/EFMedia/videoTasks/makeVideoSegmentFetchTask.browsertest.ts +57 -0
- package/src/elements/EFMedia/videoTasks/makeVideoSegmentFetchTask.ts +32 -0
- package/src/elements/EFMedia/videoTasks/makeVideoSegmentIdTask.browsertest.ts +56 -0
- package/src/elements/EFMedia/videoTasks/makeVideoSegmentIdTask.ts +23 -0
- package/src/elements/EFMedia.browsertest.ts +658 -265
- package/src/elements/EFMedia.ts +173 -1763
- package/src/elements/EFTemporal.ts +3 -4
- package/src/elements/EFTimegroup.browsertest.ts +6 -3
- package/src/elements/EFTimegroup.ts +152 -21
- package/src/elements/EFVideo.browsertest.ts +115 -37
- package/src/elements/EFVideo.ts +123 -452
- package/src/elements/EFWaveform.ts +1 -1
- package/src/elements/MediaController.ts +2 -12
- package/src/elements/SampleBuffer.ts +97 -0
- package/src/gui/ContextMixin.ts +23 -104
- package/src/gui/services/ElementConnectionManager.browsertest.ts +263 -0
- package/src/gui/services/ElementConnectionManager.ts +224 -0
- package/src/gui/services/PlaybackController.browsertest.ts +437 -0
- package/src/gui/services/PlaybackController.ts +521 -0
- package/src/services/MediaSourceManager.ts +333 -0
- package/src/transcoding/cache/CacheManager.ts +208 -0
- package/src/transcoding/cache/RequestDeduplicator.test.ts +170 -0
- package/src/transcoding/cache/RequestDeduplicator.ts +65 -0
- package/src/transcoding/types/index.ts +265 -0
- package/src/transcoding/utils/MediaUtils.ts +63 -0
- package/src/transcoding/utils/UrlGenerator.ts +68 -0
- package/src/transcoding/utils/constants.ts +36 -0
- package/src/utils/LRUCache.ts +153 -0
- package/test/EFVideo.framegen.browsertest.ts +38 -29
- package/test/__cache__/GET__api_v1_transcode_audio_1_m4s_url_http_3A_2F_2Fweb_3A3000_2Fhead_moov_480p_mp4__32da3954ba60c96ad732020c65a08ebc/data.bin +0 -0
- package/test/__cache__/GET__api_v1_transcode_audio_1_m4s_url_http_3A_2F_2Fweb_3A3000_2Fhead_moov_480p_mp4__32da3954ba60c96ad732020c65a08ebc/metadata.json +21 -0
- package/test/__cache__/GET__api_v1_transcode_audio_2_m4s_url_http_3A_2F_2Fweb_3A3000_2Fhead_moov_480p_mp4__b0b2b07efcf607de8ee0f650328c32f7/data.bin +0 -0
- package/test/__cache__/GET__api_v1_transcode_audio_2_m4s_url_http_3A_2F_2Fweb_3A3000_2Fhead_moov_480p_mp4__b0b2b07efcf607de8ee0f650328c32f7/metadata.json +21 -0
- package/test/__cache__/GET__api_v1_transcode_audio_3_m4s_url_http_3A_2F_2Fweb_3A3000_2Fhead_moov_480p_mp4__a75c2252b542e0c152c780e9a8d7b154/data.bin +0 -0
- package/test/__cache__/GET__api_v1_transcode_audio_3_m4s_url_http_3A_2F_2Fweb_3A3000_2Fhead_moov_480p_mp4__a75c2252b542e0c152c780e9a8d7b154/metadata.json +21 -0
- package/test/__cache__/GET__api_v1_transcode_audio_4_m4s_url_http_3A_2F_2Fweb_3A3000_2Fhead_moov_480p_mp4__a64ff1cfb1b52cae14df4b5dfa1e222b/data.bin +0 -0
- package/test/__cache__/GET__api_v1_transcode_audio_4_m4s_url_http_3A_2F_2Fweb_3A3000_2Fhead_moov_480p_mp4__a64ff1cfb1b52cae14df4b5dfa1e222b/metadata.json +21 -0
- package/test/__cache__/GET__api_v1_transcode_audio_5_m4s_url_http_3A_2F_2Fweb_3A3000_2Fhead_moov_480p_mp4__91e8a522f950809b9f09f4173113b4b0/data.bin +0 -0
- package/test/__cache__/GET__api_v1_transcode_audio_5_m4s_url_http_3A_2F_2Fweb_3A3000_2Fhead_moov_480p_mp4__91e8a522f950809b9f09f4173113b4b0/metadata.json +21 -0
- package/test/__cache__/GET__api_v1_transcode_audio_init_m4s_url_http_3A_2F_2Fweb_3A3000_2Fhead_moov_480p_mp4__e66d2c831d951e74ad0aeaa6489795d0/data.bin +0 -0
- package/test/__cache__/GET__api_v1_transcode_audio_init_m4s_url_http_3A_2F_2Fweb_3A3000_2Fhead_moov_480p_mp4__e66d2c831d951e74ad0aeaa6489795d0/metadata.json +21 -0
- package/test/__cache__/GET__api_v1_transcode_high_1_m4s_url_http_3A_2F_2Fweb_3A3000_2Fhead_moov_480p_mp4__26197f6f7c46cacb0a71134131c3f775/data.bin +0 -0
- package/test/__cache__/GET__api_v1_transcode_high_1_m4s_url_http_3A_2F_2Fweb_3A3000_2Fhead_moov_480p_mp4__26197f6f7c46cacb0a71134131c3f775/metadata.json +21 -0
- package/test/__cache__/GET__api_v1_transcode_high_2_m4s_url_http_3A_2F_2Fweb_3A3000_2Fhead_moov_480p_mp4__4cb6774cd3650ccf59c8f8dc6678c0b9/data.bin +0 -0
- package/test/__cache__/GET__api_v1_transcode_high_2_m4s_url_http_3A_2F_2Fweb_3A3000_2Fhead_moov_480p_mp4__4cb6774cd3650ccf59c8f8dc6678c0b9/metadata.json +21 -0
- package/test/__cache__/GET__api_v1_transcode_high_3_m4s_url_http_3A_2F_2Fweb_3A3000_2Fhead_moov_480p_mp4__0b3b2b1c8933f7fcf8a9ecaa88d58b41/data.bin +0 -0
- package/test/__cache__/GET__api_v1_transcode_high_3_m4s_url_http_3A_2F_2Fweb_3A3000_2Fhead_moov_480p_mp4__0b3b2b1c8933f7fcf8a9ecaa88d58b41/metadata.json +21 -0
- package/test/__cache__/GET__api_v1_transcode_high_init_m4s_url_http_3A_2F_2Fweb_3A3000_2Fhead_moov_480p_mp4__0798c479b44aaeef850609a430f6e613/data.bin +0 -0
- package/test/__cache__/GET__api_v1_transcode_high_init_m4s_url_http_3A_2F_2Fweb_3A3000_2Fhead_moov_480p_mp4__0798c479b44aaeef850609a430f6e613/metadata.json +21 -0
- package/test/__cache__/GET__api_v1_transcode_manifest_json_url_http_3A_2F_2Fweb_3A3000_2Fhead_moov_480p_mp4__3be92a0437de726b431ed5af2369158a/data.bin +1 -0
- package/test/__cache__/GET__api_v1_transcode_manifest_json_url_http_3A_2F_2Fweb_3A3000_2Fhead_moov_480p_mp4__3be92a0437de726b431ed5af2369158a/metadata.json +19 -0
- package/test/createJitTestClips.ts +320 -188
- package/test/recordReplayProxyPlugin.js +302 -0
- package/test/useAssetMSW.ts +1 -1
- package/test/useMSW.ts +35 -22
- package/types.json +1 -1
- package/dist/JitTranscodingClient.d.ts +0 -167
- package/dist/JitTranscodingClient.js +0 -373
- package/dist/ScrubTrackManager.js +0 -216
- package/dist/elements/printTaskStatus.js +0 -11
- package/src/elements/__screenshots__/EFMedia.browsertest.ts/EFMedia-JIT-audio-playback-audioBufferTask-should-work-in-JIT-mode-without-URL-errors-1.png +0 -0
- package/test/EFVideo.frame-tasks.browsertest.ts +0 -524
- /package/dist/{JitTranscodingClient.browsertest.d.ts → elements/EFMedia/AssetIdMediaEngine.test.d.ts} +0 -0
- /package/dist/{JitTranscodingClient.test.d.ts → elements/EFMedia/BaseMediaEngine.test.d.ts} +0 -0
- /package/dist/{ScrubTrackIntegration.test.d.ts → elements/EFMedia/BufferedSeekingInput.browsertest.d.ts} +0 -0
- /package/dist/{SegmentSwitchLoading.test.d.ts → elements/EFMedia/services/AudioElementFactory.browsertest.d.ts} +0 -0
|
@@ -0,0 +1,554 @@
|
|
|
1
|
+
import { TaskStatus } from "@lit/task";
|
|
2
|
+
import { customElement } from "lit/decorators.js";
|
|
3
|
+
import { afterEach, describe, vi } from "vitest";
|
|
4
|
+
import { test as baseTest } from "../../../../test/useMSW.js";
|
|
5
|
+
import type { AudioRendition } from "../../../transcoding/types";
|
|
6
|
+
import { EFMedia } from "../../EFMedia";
|
|
7
|
+
import {
|
|
8
|
+
computeBufferQueue,
|
|
9
|
+
computeSegmentRange,
|
|
10
|
+
computeSegmentRangeAsync,
|
|
11
|
+
getCachedSegment,
|
|
12
|
+
getCachedSegments,
|
|
13
|
+
getMissingSegments,
|
|
14
|
+
handleSeekTimeChange,
|
|
15
|
+
type MediaBufferDependencies,
|
|
16
|
+
type MediaBufferState,
|
|
17
|
+
manageMediaBuffer,
|
|
18
|
+
} from "../shared/BufferUtils";
|
|
19
|
+
import {
|
|
20
|
+
type AudioBufferConfig,
|
|
21
|
+
type AudioBufferState,
|
|
22
|
+
makeAudioBufferTask,
|
|
23
|
+
} from "./makeAudioBufferTask";
|
|
24
|
+
|
|
25
|
+
@customElement("test-media-audio-buffer")
|
|
26
|
+
class TestMediaAudioBuffer extends EFMedia {}
|
|
27
|
+
|
|
28
|
+
declare global {
|
|
29
|
+
interface HTMLElementTagNameMap {
|
|
30
|
+
"test-media-audio-buffer": TestMediaAudioBuffer;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Test that shows the task should use EFMedia properties directly
|
|
35
|
+
describe("makeAudioBufferTask - EFMedia Property Integration", () => {
|
|
36
|
+
test("should allow creating task without hardcoded config parameter", ({
|
|
37
|
+
expect,
|
|
38
|
+
}) => {
|
|
39
|
+
const element = document.createElement("test-media-audio-buffer");
|
|
40
|
+
|
|
41
|
+
// Set custom values on the element
|
|
42
|
+
element.audioBufferDurationMs = 45000;
|
|
43
|
+
element.maxAudioBufferFetches = 5;
|
|
44
|
+
element.enableAudioBuffering = false;
|
|
45
|
+
|
|
46
|
+
// This should work without passing a config parameter
|
|
47
|
+
expect(() => {
|
|
48
|
+
const task = makeAudioBufferTask(element);
|
|
49
|
+
expect(task).toBeDefined();
|
|
50
|
+
}).not.toThrow();
|
|
51
|
+
|
|
52
|
+
element.remove();
|
|
53
|
+
});
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
const test = baseTest.extend<{
|
|
57
|
+
element: TestMediaAudioBuffer;
|
|
58
|
+
mockAudioRendition: {
|
|
59
|
+
segmentDurationMs: number;
|
|
60
|
+
trackId: number;
|
|
61
|
+
src: string;
|
|
62
|
+
};
|
|
63
|
+
mockConfig: AudioBufferConfig;
|
|
64
|
+
mockState: AudioBufferState;
|
|
65
|
+
mockDeps: MediaBufferDependencies<AudioRendition>;
|
|
66
|
+
mockSignal: AbortSignal;
|
|
67
|
+
}>({
|
|
68
|
+
element: async ({}, use) => {
|
|
69
|
+
const element = document.createElement("test-media-audio-buffer");
|
|
70
|
+
await use(element);
|
|
71
|
+
element.remove();
|
|
72
|
+
},
|
|
73
|
+
|
|
74
|
+
mockAudioRendition: async ({}, use) => {
|
|
75
|
+
const rendition = {
|
|
76
|
+
segmentDurationMs: 1000, // 1 second per segment
|
|
77
|
+
trackId: 1,
|
|
78
|
+
src: "test-audio.mp4",
|
|
79
|
+
};
|
|
80
|
+
await use(rendition);
|
|
81
|
+
},
|
|
82
|
+
|
|
83
|
+
mockConfig: async ({}, use) => {
|
|
84
|
+
const config = {
|
|
85
|
+
bufferDurationMs: 5000, // 5 seconds
|
|
86
|
+
maxParallelFetches: 2,
|
|
87
|
+
enableBuffering: true,
|
|
88
|
+
enableContinuousBuffering: false, // Disable for predictable testing
|
|
89
|
+
};
|
|
90
|
+
await use(config);
|
|
91
|
+
},
|
|
92
|
+
|
|
93
|
+
mockState: async ({}, use) => {
|
|
94
|
+
const state = {
|
|
95
|
+
currentSeekTimeMs: 0,
|
|
96
|
+
activeRequests: new Set<number>(),
|
|
97
|
+
cachedSegments: new Set<number>(),
|
|
98
|
+
requestQueue: [],
|
|
99
|
+
};
|
|
100
|
+
await use(state);
|
|
101
|
+
},
|
|
102
|
+
|
|
103
|
+
mockDeps: async ({ mockAudioRendition }, use) => {
|
|
104
|
+
const deps = {
|
|
105
|
+
computeSegmentId: vi.fn(
|
|
106
|
+
async (timeMs: number, rendition: { segmentDurationMs?: number }) => {
|
|
107
|
+
return Math.floor(timeMs / (rendition.segmentDurationMs || 1000));
|
|
108
|
+
},
|
|
109
|
+
),
|
|
110
|
+
fetchSegment: vi.fn().mockResolvedValue(new ArrayBuffer(1024)),
|
|
111
|
+
getRendition: vi.fn().mockResolvedValue(mockAudioRendition),
|
|
112
|
+
logError: vi.fn(),
|
|
113
|
+
};
|
|
114
|
+
await use(deps);
|
|
115
|
+
},
|
|
116
|
+
|
|
117
|
+
mockSignal: async ({}, use) => {
|
|
118
|
+
const mockSignal = new AbortController().signal;
|
|
119
|
+
await use(mockSignal);
|
|
120
|
+
},
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
describe("computeSegmentRange", () => {
|
|
124
|
+
test("computes segment range correctly", ({ mockAudioRendition, expect }) => {
|
|
125
|
+
const computeSegmentId = (
|
|
126
|
+
timeMs: number,
|
|
127
|
+
rendition: { segmentDurationMs: number },
|
|
128
|
+
) => Math.floor(timeMs / (rendition.segmentDurationMs || 1000));
|
|
129
|
+
|
|
130
|
+
const segments = computeSegmentRange(
|
|
131
|
+
2000, // start at 2 seconds
|
|
132
|
+
5000, // end at 5 seconds
|
|
133
|
+
mockAudioRendition,
|
|
134
|
+
computeSegmentId,
|
|
135
|
+
);
|
|
136
|
+
|
|
137
|
+
expect(segments).toEqual([2, 3, 4, 5]); // segments 2, 3, 4, 5
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
test("returns empty array when segment IDs are undefined", ({
|
|
141
|
+
mockAudioRendition,
|
|
142
|
+
expect,
|
|
143
|
+
}) => {
|
|
144
|
+
const computeSegmentId = () => undefined;
|
|
145
|
+
|
|
146
|
+
const segments = computeSegmentRange(
|
|
147
|
+
2000,
|
|
148
|
+
5000,
|
|
149
|
+
mockAudioRendition,
|
|
150
|
+
computeSegmentId,
|
|
151
|
+
);
|
|
152
|
+
|
|
153
|
+
expect(segments).toEqual([]);
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
test("uses default segment duration when missing", ({ expect }) => {
|
|
157
|
+
const rendition = {}; // Missing segmentDurationMs
|
|
158
|
+
const computeSegmentId = () => 1;
|
|
159
|
+
|
|
160
|
+
const segments = computeSegmentRange(
|
|
161
|
+
2000,
|
|
162
|
+
5000,
|
|
163
|
+
rendition as any,
|
|
164
|
+
computeSegmentId,
|
|
165
|
+
);
|
|
166
|
+
|
|
167
|
+
// Should use default 1000ms duration and call computeSegmentId for segments 2, 3, 4, 5
|
|
168
|
+
// Since computeSegmentId always returns 1, and duplicates are filtered out, result is [1]
|
|
169
|
+
expect(segments).toEqual([1]); // duplicates filtered out
|
|
170
|
+
});
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
describe("computeSegmentRangeAsync", () => {
|
|
174
|
+
test("computes segment range with video duration limit", async ({
|
|
175
|
+
mockAudioRendition,
|
|
176
|
+
expect,
|
|
177
|
+
}) => {
|
|
178
|
+
const computeSegmentId = async (timeMs: number) =>
|
|
179
|
+
Math.floor(timeMs / 1000);
|
|
180
|
+
|
|
181
|
+
const segments = await computeSegmentRangeAsync(
|
|
182
|
+
8000, // start at 8 seconds
|
|
183
|
+
12000, // want to end at 12 seconds
|
|
184
|
+
10000, // but video only lasts 10 seconds
|
|
185
|
+
mockAudioRendition,
|
|
186
|
+
computeSegmentId,
|
|
187
|
+
);
|
|
188
|
+
|
|
189
|
+
expect(segments).toEqual([8, 9]); // Limited to video duration, segment 10 would be at 10000ms which is not < 10000
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
test("handles large duration limit", async ({
|
|
193
|
+
mockAudioRendition,
|
|
194
|
+
expect,
|
|
195
|
+
}) => {
|
|
196
|
+
const computeSegmentId = async (timeMs: number) =>
|
|
197
|
+
Math.floor(timeMs / 1000);
|
|
198
|
+
|
|
199
|
+
const segments = await computeSegmentRangeAsync(
|
|
200
|
+
2000,
|
|
201
|
+
5000,
|
|
202
|
+
100000, // Large duration limit - effectively unlimited
|
|
203
|
+
mockAudioRendition,
|
|
204
|
+
computeSegmentId,
|
|
205
|
+
);
|
|
206
|
+
|
|
207
|
+
expect(segments).toEqual([2, 3, 4, 5]);
|
|
208
|
+
});
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
describe("computeBufferQueue", () => {
|
|
212
|
+
test("filters out active and cached segments", ({ expect }) => {
|
|
213
|
+
const desiredSegments = [1, 2, 3, 4, 5];
|
|
214
|
+
const activeRequests = new Set([2, 4]);
|
|
215
|
+
const cachedSegments = new Set([1]);
|
|
216
|
+
|
|
217
|
+
const queue = computeBufferQueue(
|
|
218
|
+
desiredSegments,
|
|
219
|
+
activeRequests,
|
|
220
|
+
cachedSegments,
|
|
221
|
+
);
|
|
222
|
+
|
|
223
|
+
expect(queue).toEqual([3, 5]); // Only segments not active or cached
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
test("returns empty queue when all segments handled", ({ expect }) => {
|
|
227
|
+
const desiredSegments = [1, 2, 3];
|
|
228
|
+
const activeRequests = new Set([2]);
|
|
229
|
+
const cachedSegments = new Set([1, 3]);
|
|
230
|
+
|
|
231
|
+
const queue = computeBufferQueue(
|
|
232
|
+
desiredSegments,
|
|
233
|
+
activeRequests,
|
|
234
|
+
cachedSegments,
|
|
235
|
+
);
|
|
236
|
+
|
|
237
|
+
expect(queue).toEqual([]);
|
|
238
|
+
});
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
describe("handleSeekTimeChange", () => {
|
|
242
|
+
test("computes new queue for seek time change", ({
|
|
243
|
+
mockAudioRendition,
|
|
244
|
+
expect,
|
|
245
|
+
}) => {
|
|
246
|
+
const computeSegmentId = (timeMs: number) => Math.floor(timeMs / 1000);
|
|
247
|
+
const currentState = {
|
|
248
|
+
currentSeekTimeMs: 0,
|
|
249
|
+
activeRequests: new Set([1]),
|
|
250
|
+
cachedSegments: new Set([0]),
|
|
251
|
+
requestQueue: [],
|
|
252
|
+
};
|
|
253
|
+
|
|
254
|
+
const result = handleSeekTimeChange(
|
|
255
|
+
3000, // seek to 3 seconds
|
|
256
|
+
2000, // buffer 2 seconds ahead
|
|
257
|
+
mockAudioRendition,
|
|
258
|
+
currentState,
|
|
259
|
+
computeSegmentId,
|
|
260
|
+
);
|
|
261
|
+
|
|
262
|
+
expect(result.newQueue).toEqual([3, 4, 5]); // segments 3, 4, 5 needed, none in cache/active
|
|
263
|
+
expect(result.overlappingRequests).toEqual([]); // no overlap with active requests
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
test("identifies overlapping requests", ({ mockAudioRendition, expect }) => {
|
|
267
|
+
const computeSegmentId = (timeMs: number) => Math.floor(timeMs / 1000);
|
|
268
|
+
const currentState = {
|
|
269
|
+
currentSeekTimeMs: 0,
|
|
270
|
+
activeRequests: new Set([3, 4]),
|
|
271
|
+
cachedSegments: new Set([]),
|
|
272
|
+
requestQueue: [],
|
|
273
|
+
};
|
|
274
|
+
|
|
275
|
+
const result = handleSeekTimeChange(
|
|
276
|
+
3000, // seek to 3 seconds
|
|
277
|
+
2000, // buffer 2 seconds ahead
|
|
278
|
+
mockAudioRendition,
|
|
279
|
+
currentState,
|
|
280
|
+
computeSegmentId,
|
|
281
|
+
);
|
|
282
|
+
|
|
283
|
+
expect(result.overlappingRequests).toEqual([3, 4]); // both are already being fetched
|
|
284
|
+
expect(result.newQueue).toEqual([5]); // only segment 5 needs fetching
|
|
285
|
+
});
|
|
286
|
+
});
|
|
287
|
+
|
|
288
|
+
describe("manageMediaBuffer (Audio)", () => {
|
|
289
|
+
test("manages buffer state successfully", async ({
|
|
290
|
+
mockConfig,
|
|
291
|
+
mockState,
|
|
292
|
+
mockDeps,
|
|
293
|
+
mockSignal,
|
|
294
|
+
expect,
|
|
295
|
+
}) => {
|
|
296
|
+
const seekTimeMs = 3000;
|
|
297
|
+
|
|
298
|
+
const newState = await manageMediaBuffer(
|
|
299
|
+
seekTimeMs,
|
|
300
|
+
mockConfig,
|
|
301
|
+
mockState,
|
|
302
|
+
10000, // durationMs
|
|
303
|
+
mockSignal,
|
|
304
|
+
mockDeps,
|
|
305
|
+
);
|
|
306
|
+
|
|
307
|
+
expect(newState.currentSeekTimeMs).toBe(seekTimeMs);
|
|
308
|
+
expect(mockDeps.getRendition).toHaveBeenCalled();
|
|
309
|
+
expect(mockDeps.fetchSegment).toHaveBeenCalledTimes(2); // maxParallelFetches = 2
|
|
310
|
+
});
|
|
311
|
+
|
|
312
|
+
test("respects maxParallelFetches limit", async ({
|
|
313
|
+
mockState,
|
|
314
|
+
mockDeps,
|
|
315
|
+
mockSignal,
|
|
316
|
+
expect,
|
|
317
|
+
}) => {
|
|
318
|
+
const config = {
|
|
319
|
+
bufferDurationMs: 10000, // 10 seconds = 10 segments
|
|
320
|
+
maxParallelFetches: 3,
|
|
321
|
+
enableBuffering: true,
|
|
322
|
+
enableContinuousBuffering: false, // Disable for predictable testing
|
|
323
|
+
};
|
|
324
|
+
|
|
325
|
+
await manageMediaBuffer(
|
|
326
|
+
0,
|
|
327
|
+
config,
|
|
328
|
+
mockState,
|
|
329
|
+
10000, // durationMs
|
|
330
|
+
mockSignal,
|
|
331
|
+
mockDeps,
|
|
332
|
+
);
|
|
333
|
+
|
|
334
|
+
expect(mockDeps.fetchSegment).toHaveBeenCalledTimes(3); // Should only fetch 3 despite needing 10
|
|
335
|
+
});
|
|
336
|
+
|
|
337
|
+
test("does nothing when buffering disabled", async ({
|
|
338
|
+
mockState,
|
|
339
|
+
mockDeps,
|
|
340
|
+
mockSignal,
|
|
341
|
+
expect,
|
|
342
|
+
}) => {
|
|
343
|
+
const config = {
|
|
344
|
+
bufferDurationMs: 5000,
|
|
345
|
+
maxParallelFetches: 2,
|
|
346
|
+
enableBuffering: false,
|
|
347
|
+
};
|
|
348
|
+
|
|
349
|
+
const newState = await manageMediaBuffer(
|
|
350
|
+
1000,
|
|
351
|
+
config,
|
|
352
|
+
mockState,
|
|
353
|
+
10000, // durationMs
|
|
354
|
+
mockSignal,
|
|
355
|
+
mockDeps,
|
|
356
|
+
);
|
|
357
|
+
|
|
358
|
+
expect(newState).toBe(mockState); // Should return same state
|
|
359
|
+
expect(mockDeps.fetchSegment).not.toHaveBeenCalled();
|
|
360
|
+
});
|
|
361
|
+
|
|
362
|
+
test("handles fetch errors gracefully", async ({
|
|
363
|
+
mockConfig,
|
|
364
|
+
mockState,
|
|
365
|
+
mockSignal,
|
|
366
|
+
expect,
|
|
367
|
+
}) => {
|
|
368
|
+
const mockDeps = {
|
|
369
|
+
computeSegmentId: vi.fn().mockResolvedValue(1),
|
|
370
|
+
fetchSegment: vi.fn().mockRejectedValue(new Error("Network error")),
|
|
371
|
+
getRendition: vi.fn().mockResolvedValue({ segmentDurationMs: 1000 }),
|
|
372
|
+
logError: vi.fn(),
|
|
373
|
+
};
|
|
374
|
+
|
|
375
|
+
const newState = await manageMediaBuffer(
|
|
376
|
+
1000,
|
|
377
|
+
mockConfig,
|
|
378
|
+
mockState,
|
|
379
|
+
10000, // durationMs
|
|
380
|
+
mockSignal,
|
|
381
|
+
mockDeps,
|
|
382
|
+
);
|
|
383
|
+
|
|
384
|
+
// Wait for async error handling to complete
|
|
385
|
+
await new Promise((resolve) => setTimeout(resolve, 10));
|
|
386
|
+
|
|
387
|
+
expect(newState.currentSeekTimeMs).toBe(1000);
|
|
388
|
+
expect(mockDeps.logError).toHaveBeenCalledWith(
|
|
389
|
+
"Failed to fetch segment 1",
|
|
390
|
+
expect.any(Error),
|
|
391
|
+
);
|
|
392
|
+
});
|
|
393
|
+
});
|
|
394
|
+
|
|
395
|
+
describe("makeAudioBufferTask", () => {
|
|
396
|
+
afterEach(() => {
|
|
397
|
+
const elements = document.querySelectorAll("test-media-audio-buffer");
|
|
398
|
+
for (const element of elements) {
|
|
399
|
+
element.remove();
|
|
400
|
+
}
|
|
401
|
+
vi.restoreAllMocks();
|
|
402
|
+
});
|
|
403
|
+
|
|
404
|
+
test("creates task with correct configuration", ({ element, expect }) => {
|
|
405
|
+
const task = makeAudioBufferTask(element);
|
|
406
|
+
|
|
407
|
+
expect(task).toBeDefined();
|
|
408
|
+
expect(task.status).toBe(TaskStatus.INITIAL);
|
|
409
|
+
expect(task.value).toBeUndefined();
|
|
410
|
+
expect(task.error).toBeUndefined();
|
|
411
|
+
});
|
|
412
|
+
|
|
413
|
+
test("task integrates with element seek time", ({ element, expect }) => {
|
|
414
|
+
element.desiredSeekTimeMs = 5000;
|
|
415
|
+
|
|
416
|
+
const task = makeAudioBufferTask(element);
|
|
417
|
+
expect(task).toBeDefined();
|
|
418
|
+
expect(task.status).toBe(TaskStatus.INITIAL);
|
|
419
|
+
});
|
|
420
|
+
});
|
|
421
|
+
|
|
422
|
+
describe("Cache Access Methods", () => {
|
|
423
|
+
test("getCachedSegment returns true for cached segments", ({ expect }) => {
|
|
424
|
+
const bufferState: MediaBufferState = {
|
|
425
|
+
currentSeekTimeMs: 0,
|
|
426
|
+
activeRequests: new Set(),
|
|
427
|
+
cachedSegments: new Set([1, 2, 3]),
|
|
428
|
+
requestQueue: [],
|
|
429
|
+
};
|
|
430
|
+
|
|
431
|
+
expect(getCachedSegment(1, bufferState)).toBe(true);
|
|
432
|
+
expect(getCachedSegment(2, bufferState)).toBe(true);
|
|
433
|
+
expect(getCachedSegment(4, bufferState)).toBe(false);
|
|
434
|
+
});
|
|
435
|
+
|
|
436
|
+
test("getCachedSegment returns false for undefined buffer state", ({
|
|
437
|
+
expect,
|
|
438
|
+
}) => {
|
|
439
|
+
expect(getCachedSegment(1, undefined)).toBe(false);
|
|
440
|
+
});
|
|
441
|
+
|
|
442
|
+
test("getCachedSegments returns correct cached segment set", ({ expect }) => {
|
|
443
|
+
const bufferState: MediaBufferState = {
|
|
444
|
+
currentSeekTimeMs: 0,
|
|
445
|
+
activeRequests: new Set(),
|
|
446
|
+
cachedSegments: new Set([2, 4, 6]),
|
|
447
|
+
requestQueue: [],
|
|
448
|
+
};
|
|
449
|
+
|
|
450
|
+
const requestedSegments = [1, 2, 3, 4, 5, 6];
|
|
451
|
+
const cachedSegments = getCachedSegments(requestedSegments, bufferState);
|
|
452
|
+
|
|
453
|
+
expect(cachedSegments).toEqual(new Set([2, 4, 6]));
|
|
454
|
+
});
|
|
455
|
+
|
|
456
|
+
test("getCachedSegments returns empty set for undefined buffer state", ({
|
|
457
|
+
expect,
|
|
458
|
+
}) => {
|
|
459
|
+
const requestedSegments = [1, 2, 3];
|
|
460
|
+
const cachedSegments = getCachedSegments(requestedSegments, undefined);
|
|
461
|
+
|
|
462
|
+
expect(cachedSegments).toEqual(new Set());
|
|
463
|
+
});
|
|
464
|
+
|
|
465
|
+
test("getMissingSegments returns correct missing segments", ({ expect }) => {
|
|
466
|
+
const bufferState: MediaBufferState = {
|
|
467
|
+
currentSeekTimeMs: 0,
|
|
468
|
+
activeRequests: new Set(),
|
|
469
|
+
cachedSegments: new Set([2, 4, 6]),
|
|
470
|
+
requestQueue: [],
|
|
471
|
+
};
|
|
472
|
+
|
|
473
|
+
const requestedSegments = [1, 2, 3, 4, 5, 6];
|
|
474
|
+
const missingSegments = getMissingSegments(requestedSegments, bufferState);
|
|
475
|
+
|
|
476
|
+
expect(missingSegments).toEqual([1, 3, 5]);
|
|
477
|
+
});
|
|
478
|
+
|
|
479
|
+
test("getMissingSegments returns all segments for undefined buffer state", ({
|
|
480
|
+
expect,
|
|
481
|
+
}) => {
|
|
482
|
+
const requestedSegments = [1, 2, 3];
|
|
483
|
+
const missingSegments = getMissingSegments(requestedSegments, undefined);
|
|
484
|
+
|
|
485
|
+
expect(missingSegments).toEqual([1, 2, 3]);
|
|
486
|
+
});
|
|
487
|
+
});
|
|
488
|
+
|
|
489
|
+
describe("Continuous Buffering", () => {
|
|
490
|
+
test("enables continuous segment loading when enabled", async ({
|
|
491
|
+
mockState,
|
|
492
|
+
mockDeps,
|
|
493
|
+
mockSignal,
|
|
494
|
+
expect,
|
|
495
|
+
}) => {
|
|
496
|
+
const configWithContinuous = {
|
|
497
|
+
bufferDurationMs: 10000, // 10 seconds = 10 segments
|
|
498
|
+
maxParallelFetches: 2,
|
|
499
|
+
enableBuffering: true,
|
|
500
|
+
enableContinuousBuffering: true, // Enable continuous buffering
|
|
501
|
+
};
|
|
502
|
+
|
|
503
|
+
let fetchCount = 0;
|
|
504
|
+
const mockFetchWithDelay = vi.fn().mockImplementation(() => {
|
|
505
|
+
fetchCount++;
|
|
506
|
+
return Promise.resolve(new ArrayBuffer(1000));
|
|
507
|
+
});
|
|
508
|
+
|
|
509
|
+
const mockDepsWithContinuous = {
|
|
510
|
+
...mockDeps,
|
|
511
|
+
fetchSegment: mockFetchWithDelay,
|
|
512
|
+
};
|
|
513
|
+
|
|
514
|
+
await manageMediaBuffer(
|
|
515
|
+
0,
|
|
516
|
+
configWithContinuous,
|
|
517
|
+
mockState,
|
|
518
|
+
10000, // durationMs
|
|
519
|
+
mockSignal,
|
|
520
|
+
mockDepsWithContinuous,
|
|
521
|
+
);
|
|
522
|
+
|
|
523
|
+
// Should start with initial maxParallelFetches (2) and continue with more requests
|
|
524
|
+
// Continuous buffering should fetch more segments as previous ones complete
|
|
525
|
+
expect(mockFetchWithDelay).toHaveBeenCalledTimes(4); // More than initial batch due to continuous buffering
|
|
526
|
+
expect(fetchCount).toBe(4);
|
|
527
|
+
});
|
|
528
|
+
|
|
529
|
+
test("disabled when flag is false", async ({
|
|
530
|
+
mockState,
|
|
531
|
+
mockDeps,
|
|
532
|
+
mockSignal,
|
|
533
|
+
expect,
|
|
534
|
+
}) => {
|
|
535
|
+
const configWithoutContinuous = {
|
|
536
|
+
bufferDurationMs: 10000, // 10 seconds = 10 segments
|
|
537
|
+
maxParallelFetches: 2,
|
|
538
|
+
enableBuffering: true,
|
|
539
|
+
enableContinuousBuffering: false, // Disable continuous buffering
|
|
540
|
+
};
|
|
541
|
+
|
|
542
|
+
await manageMediaBuffer(
|
|
543
|
+
0,
|
|
544
|
+
configWithoutContinuous,
|
|
545
|
+
mockState,
|
|
546
|
+
10000, // durationMs
|
|
547
|
+
mockSignal,
|
|
548
|
+
mockDeps,
|
|
549
|
+
);
|
|
550
|
+
|
|
551
|
+
// Should only fetch initial maxParallelFetches and stop
|
|
552
|
+
expect(mockDeps.fetchSegment).toHaveBeenCalledTimes(2);
|
|
553
|
+
});
|
|
554
|
+
});
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { Task } from "@lit/task";
|
|
2
|
+
|
|
3
|
+
import { EF_INTERACTIVE } from "../../../EF_INTERACTIVE";
|
|
4
|
+
import { EF_RENDERING } from "../../../EF_RENDERING";
|
|
5
|
+
import type { AudioRendition } from "../../../transcoding/types";
|
|
6
|
+
import type { EFMedia } from "../../EFMedia";
|
|
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 audio buffering - extends the generic interface
|
|
16
|
+
*/
|
|
17
|
+
export interface AudioBufferConfig extends MediaBufferConfig {}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* State of the audio buffer - uses the generic interface
|
|
21
|
+
*/
|
|
22
|
+
export interface AudioBufferState extends MediaBufferState {}
|
|
23
|
+
|
|
24
|
+
type AudioBufferTask = Task<readonly [number], AudioBufferState>;
|
|
25
|
+
export const makeAudioBufferTask = (host: EFMedia): AudioBufferTask => {
|
|
26
|
+
let currentState: AudioBufferState = {
|
|
27
|
+
currentSeekTimeMs: 0,
|
|
28
|
+
activeRequests: new Set(),
|
|
29
|
+
cachedSegments: new Set(),
|
|
30
|
+
requestQueue: [],
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
return new Task(host, {
|
|
34
|
+
autoRun: EF_INTERACTIVE, // Start buffering automatically when media is ready
|
|
35
|
+
args: () => [host.desiredSeekTimeMs] as const,
|
|
36
|
+
onError: (error) => {
|
|
37
|
+
console.error("audioBufferTask error", error);
|
|
38
|
+
},
|
|
39
|
+
onComplete: (value) => {
|
|
40
|
+
currentState = value;
|
|
41
|
+
},
|
|
42
|
+
task: async ([seekTimeMs], { signal }) => {
|
|
43
|
+
// Use EFMedia properties directly - no hardcoded duplication!
|
|
44
|
+
const currentConfig: AudioBufferConfig = {
|
|
45
|
+
bufferDurationMs: host.audioBufferDurationMs,
|
|
46
|
+
maxParallelFetches: host.maxAudioBufferFetches,
|
|
47
|
+
enableBuffering: host.enableAudioBuffering && !EF_RENDERING,
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
return manageMediaBuffer<AudioRendition>(
|
|
51
|
+
seekTimeMs,
|
|
52
|
+
currentConfig,
|
|
53
|
+
currentState,
|
|
54
|
+
(host as any).intrinsicDurationMs || 10000,
|
|
55
|
+
signal,
|
|
56
|
+
{
|
|
57
|
+
computeSegmentId: async (timeMs, rendition) => {
|
|
58
|
+
// Use media engine's computeSegmentId
|
|
59
|
+
const mediaEngine = await getLatestMediaEngine(host, signal);
|
|
60
|
+
return mediaEngine.computeSegmentId(timeMs, rendition);
|
|
61
|
+
},
|
|
62
|
+
fetchSegment: async (segmentId, rendition) => {
|
|
63
|
+
// SIMPLIFIED: Direct call to mediaEngine - deduplication is built-in
|
|
64
|
+
const mediaEngine = await getLatestMediaEngine(host, signal);
|
|
65
|
+
return mediaEngine.fetchMediaSegment(segmentId, rendition);
|
|
66
|
+
},
|
|
67
|
+
getRendition: async () => {
|
|
68
|
+
// Get real audio rendition from media engine
|
|
69
|
+
const mediaEngine = await getLatestMediaEngine(host, signal);
|
|
70
|
+
const audioRendition = mediaEngine.audioRendition;
|
|
71
|
+
if (!audioRendition) {
|
|
72
|
+
throw new Error("Audio rendition not available");
|
|
73
|
+
}
|
|
74
|
+
return audioRendition;
|
|
75
|
+
},
|
|
76
|
+
logError: console.error,
|
|
77
|
+
},
|
|
78
|
+
);
|
|
79
|
+
},
|
|
80
|
+
});
|
|
81
|
+
};
|