@editframe/elements 0.17.6-beta.0 → 0.18.7-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/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.browsertest.d.ts +0 -0
- package/dist/elements/EFMedia/AssetMediaEngine.d.ts +45 -0
- package/dist/elements/EFMedia/AssetMediaEngine.js +135 -0
- package/dist/elements/EFMedia/BaseMediaEngine.d.ts +55 -0
- package/dist/elements/EFMedia/BaseMediaEngine.js +115 -0
- package/dist/elements/EFMedia/BufferedSeekingInput.d.ts +43 -0
- package/dist/elements/EFMedia/BufferedSeekingInput.js +179 -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 +81 -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 +141 -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 +30 -0
- package/dist/elements/EFMedia/audioTasks/makeAudioSeekTask.chunkboundary.regression.browsertest.d.ts +0 -0
- package/dist/elements/EFMedia/audioTasks/makeAudioSeekTask.d.ts +7 -0
- package/dist/elements/EFMedia/audioTasks/makeAudioSeekTask.js +32 -0
- package/dist/elements/EFMedia/audioTasks/makeAudioSegmentFetchTask.d.ts +4 -0
- package/dist/elements/EFMedia/audioTasks/makeAudioSegmentFetchTask.js +28 -0
- package/dist/elements/EFMedia/audioTasks/makeAudioSegmentIdTask.d.ts +4 -0
- package/dist/elements/EFMedia/audioTasks/makeAudioSegmentIdTask.js +17 -0
- package/dist/elements/EFMedia/audioTasks/makeAudioTimeDomainAnalysisTask.d.ts +3 -0
- package/dist/elements/EFMedia/audioTasks/makeAudioTimeDomainAnalysisTask.js +107 -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/PrecisionUtils.d.ts +28 -0
- package/dist/elements/EFMedia/shared/PrecisionUtils.js +29 -0
- package/dist/elements/EFMedia/shared/RenditionHelpers.d.ts +19 -0
- package/dist/elements/EFMedia/tasks/makeMediaEngineTask.d.ts +18 -0
- package/dist/elements/EFMedia/tasks/makeMediaEngineTask.js +60 -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 +34 -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 +28 -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 +17 -0
- package/dist/elements/EFMedia.browsertest.d.ts +1 -0
- package/dist/elements/EFMedia.d.ts +63 -111
- package/dist/elements/EFMedia.js +117 -1113
- 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 +83 -13
- package/dist/elements/EFVideo.d.ts +54 -32
- package/dist/elements/EFVideo.js +100 -207
- 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.js +2 -1
- package/dist/gui/ContextMixin.js +3 -2
- 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/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 -3
- package/src/elements/EFAudio.browsertest.ts +189 -49
- 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.browsertest.ts +100 -0
- package/src/elements/EFMedia/AssetMediaEngine.ts +255 -0
- package/src/elements/EFMedia/BaseMediaEngine.test.ts +164 -0
- package/src/elements/EFMedia/BaseMediaEngine.ts +219 -0
- package/src/elements/EFMedia/BufferedSeekingInput.browsertest.ts +481 -0
- package/src/elements/EFMedia/BufferedSeekingInput.ts +324 -0
- package/src/elements/EFMedia/JitMediaEngine.browsertest.ts +165 -0
- package/src/elements/EFMedia/JitMediaEngine.ts +166 -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 +250 -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 +43 -0
- package/src/elements/EFMedia/audioTasks/makeAudioSeekTask.chunkboundary.regression.browsertest.ts +199 -0
- package/src/elements/EFMedia/audioTasks/makeAudioSeekTask.ts +64 -0
- package/src/elements/EFMedia/audioTasks/makeAudioSegmentFetchTask.ts +45 -0
- package/src/elements/EFMedia/audioTasks/makeAudioSegmentIdTask.ts +24 -0
- package/src/elements/EFMedia/audioTasks/makeAudioTimeDomainAnalysisTask.ts +183 -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/PrecisionUtils.ts +46 -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 +68 -0
- package/src/elements/EFMedia/videoTasks/makeVideoSegmentFetchTask.browsertest.ts +57 -0
- package/src/elements/EFMedia/videoTasks/makeVideoSegmentFetchTask.ts +43 -0
- package/src/elements/EFMedia/videoTasks/makeVideoSegmentIdTask.browsertest.ts +56 -0
- package/src/elements/EFMedia/videoTasks/makeVideoSegmentIdTask.ts +24 -0
- package/src/elements/EFMedia.browsertest.ts +706 -273
- package/src/elements/EFMedia.ts +136 -1769
- package/src/elements/EFTemporal.ts +3 -4
- package/src/elements/EFTimegroup.browsertest.ts +6 -3
- package/src/elements/EFTimegroup.ts +147 -21
- package/src/elements/EFVideo.browsertest.ts +980 -169
- package/src/elements/EFVideo.ts +113 -458
- package/src/elements/EFWaveform.ts +1 -1
- package/src/elements/MediaController.ts +2 -12
- package/src/elements/SampleBuffer.ts +95 -0
- package/src/gui/ContextMixin.ts +3 -6
- 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 +269 -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 +39 -30
- 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_1_mp4_url_http_3A_2F_2Fweb_3A3000_2Fhead_moov_480p_mp4_bytes_0__9ed2d25c675aa6bb6ff5b3ae23887c71/data.bin +0 -0
- package/test/__cache__/GET__api_v1_transcode_audio_1_mp4_url_http_3A_2F_2Fweb_3A3000_2Fhead_moov_480p_mp4_bytes_0__9ed2d25c675aa6bb6ff5b3ae23887c71/metadata.json +22 -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_2_mp4_url_http_3A_2F_2Fweb_3A3000_2Fhead_moov_480p_mp4_bytes_0__d5a3309a2bf756dd6e304807eb402f56/data.bin +0 -0
- package/test/__cache__/GET__api_v1_transcode_audio_2_mp4_url_http_3A_2F_2Fweb_3A3000_2Fhead_moov_480p_mp4_bytes_0__d5a3309a2bf756dd6e304807eb402f56/metadata.json +22 -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_3_mp4_url_http_3A_2F_2Fweb_3A3000_2Fhead_moov_480p_mp4_bytes_0__773254bb671e3466fca8677139fb239e/data.bin +0 -0
- package/test/__cache__/GET__api_v1_transcode_audio_3_mp4_url_http_3A_2F_2Fweb_3A3000_2Fhead_moov_480p_mp4_bytes_0__773254bb671e3466fca8677139fb239e/metadata.json +22 -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_4_m4s_url_http_3A_2F_2Fweb_3A3000_2Fhead_moov_480p_mp4__a6fb05a22b18d850f7f2950bbcdbdeed/data.bin +0 -0
- package/test/__cache__/GET__api_v1_transcode_high_4_m4s_url_http_3A_2F_2Fweb_3A3000_2Fhead_moov_480p_mp4__a6fb05a22b18d850f7f2950bbcdbdeed/metadata.json +21 -0
- package/test/__cache__/GET__api_v1_transcode_high_5_m4s_url_http_3A_2F_2Fweb_3A3000_2Fhead_moov_480p_mp4__a50058c7c3602e90879fe3428ed891f4/data.bin +0 -0
- package/test/__cache__/GET__api_v1_transcode_high_5_m4s_url_http_3A_2F_2Fweb_3A3000_2Fhead_moov_480p_mp4__a50058c7c3602e90879fe3428ed891f4/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 +352 -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.d.ts +0 -96
- 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/{DecoderResetFrequency.test.d.ts → elements/EFMedia/AssetIdMediaEngine.test.d.ts} +0 -0
- /package/dist/{DecoderResetRecovery.test.d.ts → elements/EFMedia/BaseMediaEngine.test.d.ts} +0 -0
- /package/dist/{JitTranscodingClient.browsertest.d.ts → elements/EFMedia/BufferedSeekingInput.browsertest.d.ts} +0 -0
- /package/dist/{JitTranscodingClient.test.d.ts → elements/EFMedia/shared/RenditionHelpers.browsertest.d.ts} +0 -0
- /package/dist/{ScrubTrackIntegration.test.d.ts → elements/EFMedia/tasks/makeMediaEngineTask.browsertest.d.ts} +0 -0
- /package/dist/{SegmentSwitchLoading.test.d.ts → elements/EFMedia/tasks/makeMediaEngineTask.test.d.ts} +0 -0
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { Task } from "@lit/task";
|
|
2
|
+
import type { VideoSample } from "mediabunny";
|
|
3
|
+
import { type EFMedia, IgnorableError } from "../../EFMedia";
|
|
4
|
+
import type { BufferedSeekingInput } from "../BufferedSeekingInput";
|
|
5
|
+
|
|
6
|
+
type AudioSeekTask = Task<
|
|
7
|
+
readonly [number, BufferedSeekingInput | undefined],
|
|
8
|
+
VideoSample | undefined
|
|
9
|
+
>;
|
|
10
|
+
export const makeAudioSeekTask = (host: EFMedia): AudioSeekTask => {
|
|
11
|
+
return new Task(host, {
|
|
12
|
+
args: () => [host.desiredSeekTimeMs, host.audioInputTask.value] as const,
|
|
13
|
+
onError: (error) => {
|
|
14
|
+
if (error instanceof IgnorableError) {
|
|
15
|
+
console.info("audioSeekTask aborted");
|
|
16
|
+
}
|
|
17
|
+
console.error("audioSeekTask error", error);
|
|
18
|
+
},
|
|
19
|
+
onComplete: (_value) => {},
|
|
20
|
+
task: async (
|
|
21
|
+
[targetSeekTimeMs],
|
|
22
|
+
{ signal },
|
|
23
|
+
): Promise<VideoSample | undefined> => {
|
|
24
|
+
// CRITICAL FIX: Use the targetSeekTimeMs from args, not host.desiredSeekTimeMs
|
|
25
|
+
// This ensures we use the same seek time that the segment loading tasks used
|
|
26
|
+
|
|
27
|
+
await host.audioSegmentIdTask.taskComplete;
|
|
28
|
+
signal.throwIfAborted(); // Abort if a new seek started
|
|
29
|
+
await host.audioSegmentFetchTask.taskComplete;
|
|
30
|
+
signal.throwIfAborted(); // Abort if a new seek started
|
|
31
|
+
await host.audioInitSegmentFetchTask.taskComplete;
|
|
32
|
+
signal.throwIfAborted(); // Abort if a new seek started
|
|
33
|
+
|
|
34
|
+
const audioInput = await host.audioInputTask.taskComplete;
|
|
35
|
+
signal.throwIfAborted(); // Abort if a new seek started
|
|
36
|
+
if (!audioInput) {
|
|
37
|
+
throw new Error("Audio input is not available");
|
|
38
|
+
}
|
|
39
|
+
const audioTrack = await audioInput.getFirstAudioTrack();
|
|
40
|
+
if (!audioTrack) {
|
|
41
|
+
throw new Error("Audio track is not available");
|
|
42
|
+
}
|
|
43
|
+
signal.throwIfAborted(); // Abort if a new seek started
|
|
44
|
+
|
|
45
|
+
const sample = (await audioInput.seek(
|
|
46
|
+
audioTrack.id,
|
|
47
|
+
targetSeekTimeMs, // Use the captured value, not host.desiredSeekTimeMs
|
|
48
|
+
)) as unknown as VideoSample | undefined;
|
|
49
|
+
signal.throwIfAborted(); // Abort if a new seek started
|
|
50
|
+
|
|
51
|
+
// If seek returned undefined, it was aborted - don't throw
|
|
52
|
+
if (sample === undefined && signal.aborted) {
|
|
53
|
+
return undefined;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// If we got undefined but weren't aborted, that's an actual error
|
|
57
|
+
if (sample === undefined) {
|
|
58
|
+
throw new Error("Audio seek failed to find sample");
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return sample;
|
|
62
|
+
},
|
|
63
|
+
});
|
|
64
|
+
};
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { Task } from "@lit/task";
|
|
2
|
+
import type { MediaEngine } from "../../../transcoding/types";
|
|
3
|
+
import type { EFMedia } from "../../EFMedia";
|
|
4
|
+
import { getLatestMediaEngine } from "../tasks/makeMediaEngineTask";
|
|
5
|
+
|
|
6
|
+
export const makeAudioSegmentFetchTask = (
|
|
7
|
+
host: EFMedia,
|
|
8
|
+
): Task<
|
|
9
|
+
readonly [MediaEngine | undefined, number | undefined],
|
|
10
|
+
ArrayBuffer
|
|
11
|
+
> => {
|
|
12
|
+
return new Task(host, {
|
|
13
|
+
args: () =>
|
|
14
|
+
[host.mediaEngineTask.value, host.audioSegmentIdTask.value] as const,
|
|
15
|
+
onError: (error) => {
|
|
16
|
+
console.error("audioSegmentFetchTask error", error);
|
|
17
|
+
},
|
|
18
|
+
onComplete: (_value) => {},
|
|
19
|
+
task: async (_, { signal }) => {
|
|
20
|
+
const mediaEngine = await getLatestMediaEngine(host, signal);
|
|
21
|
+
const segmentId = await host.audioSegmentIdTask.taskComplete;
|
|
22
|
+
if (segmentId === undefined) {
|
|
23
|
+
// Provide more context in the error to help with debugging
|
|
24
|
+
const rendition = mediaEngine.audioRendition;
|
|
25
|
+
const debugInfo = {
|
|
26
|
+
hasRendition: !!rendition,
|
|
27
|
+
segmentDurationMs: rendition?.segmentDurationMs,
|
|
28
|
+
segmentDurationsMs: rendition?.segmentDurationsMs?.length || 0,
|
|
29
|
+
desiredSeekTimeMs: host.desiredSeekTimeMs,
|
|
30
|
+
intrinsicDurationMs: host.intrinsicDurationMs,
|
|
31
|
+
};
|
|
32
|
+
throw new Error(
|
|
33
|
+
`Segment ID is not available for audio. Debug info: ${JSON.stringify(debugInfo)}`,
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// SIMPLIFIED: Direct call to mediaEngine - deduplication is built-in
|
|
38
|
+
return mediaEngine.fetchMediaSegment(
|
|
39
|
+
segmentId,
|
|
40
|
+
mediaEngine.getAudioRendition(),
|
|
41
|
+
signal,
|
|
42
|
+
);
|
|
43
|
+
},
|
|
44
|
+
});
|
|
45
|
+
};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { Task } from "@lit/task";
|
|
2
|
+
import type { MediaEngine } from "../../../transcoding/types";
|
|
3
|
+
import type { EFMedia } from "../../EFMedia";
|
|
4
|
+
import { getLatestMediaEngine } from "../tasks/makeMediaEngineTask";
|
|
5
|
+
|
|
6
|
+
export const makeAudioSegmentIdTask = (
|
|
7
|
+
host: EFMedia,
|
|
8
|
+
): Task<readonly [MediaEngine | undefined, number], number | undefined> => {
|
|
9
|
+
return new Task(host, {
|
|
10
|
+
args: () => [host.mediaEngineTask.value, host.desiredSeekTimeMs] as const,
|
|
11
|
+
onError: (error) => {
|
|
12
|
+
console.error("audioSegmentIdTask error", error);
|
|
13
|
+
},
|
|
14
|
+
onComplete: (_value) => {},
|
|
15
|
+
task: async ([, targetSeekTimeMs], { signal }) => {
|
|
16
|
+
const mediaEngine = await getLatestMediaEngine(host, signal);
|
|
17
|
+
signal.throwIfAborted(); // Abort if a new seek started
|
|
18
|
+
return mediaEngine.computeSegmentId(
|
|
19
|
+
targetSeekTimeMs, // Use captured value, not host.desiredSeekTimeMs
|
|
20
|
+
mediaEngine.getAudioRendition(),
|
|
21
|
+
);
|
|
22
|
+
},
|
|
23
|
+
});
|
|
24
|
+
};
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
import { Task } from "@lit/task";
|
|
2
|
+
|
|
3
|
+
import { EF_INTERACTIVE } from "../../../EF_INTERACTIVE.js";
|
|
4
|
+
import { LRUCache } from "../../../utils/LRUCache.js";
|
|
5
|
+
import type { EFMedia } from "../../EFMedia.js";
|
|
6
|
+
|
|
7
|
+
// DECAY_WEIGHT constant - same as original
|
|
8
|
+
const DECAY_WEIGHT = 0.8;
|
|
9
|
+
|
|
10
|
+
export function makeAudioTimeDomainAnalysisTask(element: EFMedia) {
|
|
11
|
+
// Internal cache for this task instance (same as original #byteTimeDomainCache)
|
|
12
|
+
const cache = new LRUCache<string, Uint8Array>(1000);
|
|
13
|
+
|
|
14
|
+
return new Task(element, {
|
|
15
|
+
autoRun: EF_INTERACTIVE,
|
|
16
|
+
onError: (error) => {
|
|
17
|
+
console.error("byteTimeDomainTask error", error);
|
|
18
|
+
},
|
|
19
|
+
args: () =>
|
|
20
|
+
[
|
|
21
|
+
element.audioBufferTask.status,
|
|
22
|
+
element.currentSourceTimeMs,
|
|
23
|
+
element.fftSize,
|
|
24
|
+
element.fftDecay,
|
|
25
|
+
element.fftGain,
|
|
26
|
+
element.shouldInterpolateFrequencies,
|
|
27
|
+
] as const,
|
|
28
|
+
task: async () => {
|
|
29
|
+
await element.audioBufferTask.taskComplete;
|
|
30
|
+
if (!element.audioBufferTask.value) return null;
|
|
31
|
+
if (element.currentSourceTimeMs < 0) return null;
|
|
32
|
+
|
|
33
|
+
const currentTimeMs = element.currentSourceTimeMs;
|
|
34
|
+
|
|
35
|
+
// ONLY CHANGE: Get real audio data for analysis (same technique as playback)
|
|
36
|
+
const analysisWindowMs = 5000; // Get 5 seconds for better analysis
|
|
37
|
+
const fromMs = Math.max(0, currentTimeMs);
|
|
38
|
+
// Clamp toMs to video duration to prevent requesting segments beyond available content
|
|
39
|
+
const maxToMs = fromMs + analysisWindowMs;
|
|
40
|
+
const videoDurationMs = element.intrinsicDurationMs || 0;
|
|
41
|
+
const toMs =
|
|
42
|
+
videoDurationMs > 0 ? Math.min(maxToMs, videoDurationMs) : maxToMs;
|
|
43
|
+
|
|
44
|
+
// If the clamping results in an invalid range (seeking beyond the end), skip analysis silently
|
|
45
|
+
if (fromMs >= toMs) {
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const { fetchAudioSpanningTime: fetchAudioSpan } = await import(
|
|
50
|
+
"../shared/AudioSpanUtils.ts"
|
|
51
|
+
);
|
|
52
|
+
const audioSpan = await fetchAudioSpan(
|
|
53
|
+
element,
|
|
54
|
+
fromMs,
|
|
55
|
+
toMs,
|
|
56
|
+
new AbortController().signal,
|
|
57
|
+
);
|
|
58
|
+
|
|
59
|
+
if (!audioSpan || !audioSpan.blob) {
|
|
60
|
+
console.warn("Time domain analysis skipped: no audio data available");
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Decode the real audio data
|
|
65
|
+
const tempAudioContext = new OfflineAudioContext(2, 48000, 48000);
|
|
66
|
+
const arrayBuffer = await audioSpan.blob.arrayBuffer();
|
|
67
|
+
const audioBuffer = await tempAudioContext.decodeAudioData(arrayBuffer);
|
|
68
|
+
|
|
69
|
+
// Use actual startOffset from audioSpan (relative to requested time)
|
|
70
|
+
const startOffsetMs = audioSpan.startMs;
|
|
71
|
+
|
|
72
|
+
// ORIGINAL ALGORITHM FROM HERE - unchanged customer logic
|
|
73
|
+
const smoothedKey = `${element.shouldInterpolateFrequencies}:${element.fftSize}:${element.fftDecay}:${element.fftGain}:${startOffsetMs}:${currentTimeMs}`;
|
|
74
|
+
const cachedData = cache.get(smoothedKey);
|
|
75
|
+
if (cachedData) {
|
|
76
|
+
return cachedData;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// Process multiple frames with decay, similar to the reference code
|
|
80
|
+
const framesData = await Promise.all(
|
|
81
|
+
Array.from({ length: element.fftDecay }, async (_, frameIndex) => {
|
|
82
|
+
const frameOffset = frameIndex * (1000 / 30);
|
|
83
|
+
const startTime = Math.max(
|
|
84
|
+
0,
|
|
85
|
+
(currentTimeMs - frameOffset - startOffsetMs) / 1000,
|
|
86
|
+
);
|
|
87
|
+
|
|
88
|
+
const cacheKey = `${element.shouldInterpolateFrequencies}:${element.fftSize}:${element.fftGain}:${startOffsetMs}:${startTime}`;
|
|
89
|
+
const cachedFrame = cache.get(cacheKey);
|
|
90
|
+
if (cachedFrame) {
|
|
91
|
+
return cachedFrame;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
let audioContext: OfflineAudioContext;
|
|
95
|
+
try {
|
|
96
|
+
audioContext = new OfflineAudioContext(2, 48000 * (1 / 30), 48000);
|
|
97
|
+
} catch (error) {
|
|
98
|
+
throw new Error(
|
|
99
|
+
`[EFMedia.byteTimeDomainTask] Failed to create OfflineAudioContext(2, ${48000 * (1 / 30)}, 48000) for frame ${frameIndex} at time ${startTime}s: ${error instanceof Error ? error.message : String(error)}. This is for audio time domain analysis.`,
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const source = audioContext.createBufferSource();
|
|
104
|
+
source.buffer = audioBuffer;
|
|
105
|
+
|
|
106
|
+
// Create analyzer for PCM data
|
|
107
|
+
const analyser = audioContext.createAnalyser();
|
|
108
|
+
analyser.fftSize = element.fftSize; // Ensure enough samples
|
|
109
|
+
analyser.minDecibels = -90;
|
|
110
|
+
analyser.maxDecibels = -20;
|
|
111
|
+
|
|
112
|
+
const gainNode = audioContext.createGain();
|
|
113
|
+
gainNode.gain.value = element.fftGain; // Amplify the signal
|
|
114
|
+
|
|
115
|
+
source.connect(gainNode);
|
|
116
|
+
gainNode.connect(analyser);
|
|
117
|
+
analyser.connect(audioContext.destination);
|
|
118
|
+
|
|
119
|
+
source.start(0, startTime, 1 / 30);
|
|
120
|
+
|
|
121
|
+
const dataLength = analyser.fftSize / 2;
|
|
122
|
+
try {
|
|
123
|
+
await audioContext.startRendering();
|
|
124
|
+
const frameData = new Uint8Array(dataLength);
|
|
125
|
+
analyser.getByteTimeDomainData(frameData);
|
|
126
|
+
|
|
127
|
+
// const points = frameData;
|
|
128
|
+
// Calculate RMS and midpoint values
|
|
129
|
+
const points = new Uint8Array(dataLength);
|
|
130
|
+
for (let i = 0; i < dataLength; i++) {
|
|
131
|
+
const pointSamples = frameData.slice(
|
|
132
|
+
i * (frameData.length / dataLength),
|
|
133
|
+
(i + 1) * (frameData.length / dataLength),
|
|
134
|
+
);
|
|
135
|
+
|
|
136
|
+
// Calculate RMS while preserving sign
|
|
137
|
+
const rms = Math.sqrt(
|
|
138
|
+
pointSamples.reduce((sum, sample) => {
|
|
139
|
+
const normalized = (sample - 128) / 128;
|
|
140
|
+
return sum + normalized * normalized;
|
|
141
|
+
}, 0) / pointSamples.length,
|
|
142
|
+
);
|
|
143
|
+
|
|
144
|
+
// Get average sign of the samples to determine direction
|
|
145
|
+
const avgSign = Math.sign(
|
|
146
|
+
pointSamples.reduce((sum, sample) => sum + (sample - 128), 0),
|
|
147
|
+
);
|
|
148
|
+
|
|
149
|
+
// Convert RMS back to byte range, preserving direction
|
|
150
|
+
points[i] = Math.min(255, Math.round(128 + avgSign * rms * 128));
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
cache.set(cacheKey, points);
|
|
154
|
+
return points;
|
|
155
|
+
} finally {
|
|
156
|
+
source.disconnect();
|
|
157
|
+
analyser.disconnect();
|
|
158
|
+
}
|
|
159
|
+
}),
|
|
160
|
+
);
|
|
161
|
+
|
|
162
|
+
// Combine frames with decay weighting
|
|
163
|
+
const frameLength = framesData[0]?.length ?? 0;
|
|
164
|
+
const smoothedData = new Uint8Array(frameLength);
|
|
165
|
+
|
|
166
|
+
for (let i = 0; i < frameLength; i++) {
|
|
167
|
+
let weightedSum = 0;
|
|
168
|
+
let weightSum = 0;
|
|
169
|
+
|
|
170
|
+
framesData.forEach((frame: Uint8Array, frameIndex: number) => {
|
|
171
|
+
const decayWeight = DECAY_WEIGHT ** frameIndex;
|
|
172
|
+
weightedSum += (frame[i] ?? 0) * decayWeight;
|
|
173
|
+
weightSum += decayWeight;
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
smoothedData[i] = Math.min(255, Math.round(weightedSum / weightSum));
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
cache.set(smoothedKey, smoothedData);
|
|
180
|
+
return smoothedData;
|
|
181
|
+
},
|
|
182
|
+
});
|
|
183
|
+
}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
AudioSpan,
|
|
3
|
+
MediaEngine,
|
|
4
|
+
SegmentTimeRange,
|
|
5
|
+
} from "../../../transcoding/types";
|
|
6
|
+
import type { EFMedia } from "../../EFMedia";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Fetch audio segment data using MediaEngine
|
|
10
|
+
* Pure function with explicit dependencies
|
|
11
|
+
*/
|
|
12
|
+
const fetchAudioSegmentData = async (
|
|
13
|
+
segmentIds: number[],
|
|
14
|
+
mediaEngine: MediaEngine,
|
|
15
|
+
signal: AbortSignal,
|
|
16
|
+
): Promise<Map<number, ArrayBuffer>> => {
|
|
17
|
+
const audioRendition = mediaEngine.audioRendition;
|
|
18
|
+
if (!audioRendition) {
|
|
19
|
+
throw new Error("Audio rendition not available");
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const segmentData = new Map<number, ArrayBuffer>();
|
|
23
|
+
|
|
24
|
+
// Fetch all segments - MediaEngine handles deduplication internally
|
|
25
|
+
const fetchPromises = segmentIds.map(async (segmentId) => {
|
|
26
|
+
const arrayBuffer = await mediaEngine.fetchMediaSegment(
|
|
27
|
+
segmentId,
|
|
28
|
+
audioRendition,
|
|
29
|
+
signal,
|
|
30
|
+
);
|
|
31
|
+
return [segmentId, arrayBuffer] as [number, ArrayBuffer];
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
const fetchedSegments = await Promise.all(fetchPromises);
|
|
35
|
+
signal.throwIfAborted();
|
|
36
|
+
|
|
37
|
+
for (const [segmentId, arrayBuffer] of fetchedSegments) {
|
|
38
|
+
segmentData.set(segmentId, arrayBuffer);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return segmentData;
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Create audio span blob from init segment and media segments
|
|
46
|
+
* Pure function for blob creation
|
|
47
|
+
*/
|
|
48
|
+
const createAudioSpanBlob = (
|
|
49
|
+
initSegment: ArrayBuffer,
|
|
50
|
+
mediaSegments: ArrayBuffer[],
|
|
51
|
+
): Blob => {
|
|
52
|
+
const chunks = [initSegment, ...mediaSegments];
|
|
53
|
+
return new Blob(chunks, { type: "audio/mp4" });
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Fetch audio spanning a time range
|
|
58
|
+
* Main function that orchestrates segment calculation, fetching, and blob creation
|
|
59
|
+
*/
|
|
60
|
+
export const fetchAudioSpanningTime = async (
|
|
61
|
+
host: EFMedia,
|
|
62
|
+
fromMs: number,
|
|
63
|
+
toMs: number,
|
|
64
|
+
signal: AbortSignal,
|
|
65
|
+
): Promise<AudioSpan> => {
|
|
66
|
+
// Validate inputs
|
|
67
|
+
if (fromMs >= toMs || fromMs < 0) {
|
|
68
|
+
throw new Error(`Invalid time range: fromMs=${fromMs}, toMs=${toMs}`);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Get dependencies from host
|
|
72
|
+
const mediaEngine = await host.mediaEngineTask.taskComplete;
|
|
73
|
+
const initSegment = await host.audioInitSegmentFetchTask.taskComplete;
|
|
74
|
+
|
|
75
|
+
if (!mediaEngine?.audioRendition) {
|
|
76
|
+
throw new Error("Audio rendition not available");
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
if (!initSegment) {
|
|
80
|
+
throw new Error("Audio init segment is not available");
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Calculate segments needed using the media engine's method
|
|
84
|
+
const segmentRanges = mediaEngine.calculateAudioSegmentRange(
|
|
85
|
+
fromMs,
|
|
86
|
+
toMs,
|
|
87
|
+
mediaEngine.audioRendition,
|
|
88
|
+
host.intrinsicDurationMs || 10000,
|
|
89
|
+
);
|
|
90
|
+
|
|
91
|
+
if (segmentRanges.length === 0) {
|
|
92
|
+
throw new Error(`No segments found for time range ${fromMs}-${toMs}ms`);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// Fetch segment data
|
|
96
|
+
const segmentIds = segmentRanges.map((r: SegmentTimeRange) => r.segmentId);
|
|
97
|
+
const segmentData = await fetchAudioSegmentData(
|
|
98
|
+
segmentIds,
|
|
99
|
+
mediaEngine,
|
|
100
|
+
signal,
|
|
101
|
+
);
|
|
102
|
+
|
|
103
|
+
// Create ordered array of segments
|
|
104
|
+
const orderedSegments = segmentIds.map((id: number) => {
|
|
105
|
+
const segment = segmentData.get(id);
|
|
106
|
+
if (!segment) {
|
|
107
|
+
throw new Error(`Missing segment data for segment ID ${id}`);
|
|
108
|
+
}
|
|
109
|
+
return segment;
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
// Create blob
|
|
113
|
+
const blob = createAudioSpanBlob(initSegment, orderedSegments);
|
|
114
|
+
|
|
115
|
+
// Calculate actual time boundaries
|
|
116
|
+
const actualStartMs = Math.min(
|
|
117
|
+
...segmentRanges.map((r: SegmentTimeRange) => r.startMs),
|
|
118
|
+
);
|
|
119
|
+
const actualEndMs = Math.max(
|
|
120
|
+
...segmentRanges.map((r: SegmentTimeRange) => r.endMs),
|
|
121
|
+
);
|
|
122
|
+
|
|
123
|
+
return {
|
|
124
|
+
startMs: actualStartMs,
|
|
125
|
+
endMs: actualEndMs,
|
|
126
|
+
blob,
|
|
127
|
+
};
|
|
128
|
+
};
|