@editframe/elements 0.20.4-beta.0 → 0.23.6-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/DelayedLoadingState.js +0 -27
- package/dist/EF_FRAMEGEN.d.ts +5 -3
- package/dist/EF_FRAMEGEN.js +49 -11
- package/dist/_virtual/_@oxc-project_runtime@0.94.0/helpers/decorate.js +7 -0
- package/dist/attachContextRoot.d.ts +1 -0
- package/dist/attachContextRoot.js +9 -0
- package/dist/elements/ContextProxiesController.d.ts +1 -2
- package/dist/elements/EFAudio.js +5 -9
- package/dist/elements/EFCaptions.d.ts +1 -3
- package/dist/elements/EFCaptions.js +112 -129
- package/dist/elements/EFImage.js +6 -7
- package/dist/elements/EFMedia/AssetIdMediaEngine.js +2 -5
- package/dist/elements/EFMedia/AssetMediaEngine.js +36 -33
- package/dist/elements/EFMedia/BaseMediaEngine.js +57 -73
- package/dist/elements/EFMedia/BufferedSeekingInput.d.ts +1 -1
- package/dist/elements/EFMedia/BufferedSeekingInput.js +134 -78
- package/dist/elements/EFMedia/JitMediaEngine.js +9 -19
- package/dist/elements/EFMedia/audioTasks/makeAudioBufferTask.js +7 -13
- package/dist/elements/EFMedia/audioTasks/makeAudioFrequencyAnalysisTask.js +2 -3
- package/dist/elements/EFMedia/audioTasks/makeAudioInitSegmentFetchTask.js +1 -1
- package/dist/elements/EFMedia/audioTasks/makeAudioInputTask.js +6 -5
- package/dist/elements/EFMedia/audioTasks/makeAudioSeekTask.js +1 -3
- package/dist/elements/EFMedia/audioTasks/makeAudioSegmentFetchTask.js +1 -1
- package/dist/elements/EFMedia/audioTasks/makeAudioSegmentIdTask.js +1 -1
- package/dist/elements/EFMedia/audioTasks/makeAudioTimeDomainAnalysisTask.js +1 -1
- package/dist/elements/EFMedia/shared/AudioSpanUtils.js +9 -25
- package/dist/elements/EFMedia/shared/BufferUtils.js +2 -17
- package/dist/elements/EFMedia/shared/GlobalInputCache.js +0 -24
- package/dist/elements/EFMedia/shared/PrecisionUtils.js +0 -21
- package/dist/elements/EFMedia/shared/ThumbnailExtractor.js +0 -17
- package/dist/elements/EFMedia/tasks/makeMediaEngineTask.js +1 -10
- package/dist/elements/EFMedia/videoTasks/MainVideoInputCache.d.ts +29 -0
- package/dist/elements/EFMedia/videoTasks/MainVideoInputCache.js +32 -0
- package/dist/elements/EFMedia/videoTasks/ScrubInputCache.js +1 -15
- package/dist/elements/EFMedia/videoTasks/makeScrubVideoBufferTask.js +1 -7
- package/dist/elements/EFMedia/videoTasks/makeScrubVideoInputTask.js +8 -5
- package/dist/elements/EFMedia/videoTasks/makeScrubVideoSeekTask.js +12 -13
- package/dist/elements/EFMedia/videoTasks/makeScrubVideoSegmentIdTask.js +1 -1
- package/dist/elements/EFMedia/videoTasks/makeUnifiedVideoSeekTask.js +134 -70
- package/dist/elements/EFMedia/videoTasks/makeVideoBufferTask.js +11 -18
- package/dist/elements/EFMedia.d.ts +19 -0
- package/dist/elements/EFMedia.js +44 -25
- package/dist/elements/EFSourceMixin.js +5 -7
- package/dist/elements/EFSurface.js +6 -9
- package/dist/elements/EFTemporal.browsertest.d.ts +11 -0
- package/dist/elements/EFTemporal.d.ts +10 -0
- package/dist/elements/EFTemporal.js +100 -41
- package/dist/elements/EFThumbnailStrip.js +23 -73
- package/dist/elements/EFTimegroup.browsertest.d.ts +3 -3
- package/dist/elements/EFTimegroup.d.ts +35 -14
- package/dist/elements/EFTimegroup.js +138 -181
- package/dist/elements/EFVideo.d.ts +16 -2
- package/dist/elements/EFVideo.js +156 -108
- package/dist/elements/EFWaveform.js +23 -40
- package/dist/elements/SampleBuffer.js +3 -7
- package/dist/elements/TargetController.js +5 -5
- package/dist/elements/durationConverter.js +4 -4
- package/dist/elements/renderTemporalAudio.d.ts +10 -0
- package/dist/elements/renderTemporalAudio.js +35 -0
- package/dist/elements/updateAnimations.js +19 -43
- package/dist/gui/ContextMixin.d.ts +5 -5
- package/dist/gui/ContextMixin.js +167 -162
- package/dist/gui/Controllable.browsertest.d.ts +0 -0
- package/dist/gui/Controllable.d.ts +15 -0
- package/dist/gui/Controllable.js +9 -0
- package/dist/gui/EFConfiguration.js +7 -7
- package/dist/gui/EFControls.browsertest.d.ts +11 -0
- package/dist/gui/EFControls.d.ts +18 -4
- package/dist/gui/EFControls.js +70 -28
- package/dist/gui/EFDial.browsertest.d.ts +0 -0
- package/dist/gui/EFDial.d.ts +18 -0
- package/dist/gui/EFDial.js +141 -0
- package/dist/gui/EFFilmstrip.browsertest.d.ts +11 -0
- package/dist/gui/EFFilmstrip.d.ts +12 -2
- package/dist/gui/EFFilmstrip.js +214 -129
- package/dist/gui/EFFitScale.js +5 -8
- package/dist/gui/EFFocusOverlay.js +4 -4
- package/dist/gui/EFPause.browsertest.d.ts +0 -0
- package/dist/gui/EFPause.d.ts +23 -0
- package/dist/gui/EFPause.js +59 -0
- package/dist/gui/EFPlay.browsertest.d.ts +0 -0
- package/dist/gui/EFPlay.d.ts +23 -0
- package/dist/gui/EFPlay.js +59 -0
- package/dist/gui/EFPreview.d.ts +4 -0
- package/dist/gui/EFPreview.js +18 -9
- package/dist/gui/EFResizableBox.browsertest.d.ts +0 -0
- package/dist/gui/EFResizableBox.d.ts +34 -0
- package/dist/gui/EFResizableBox.js +547 -0
- package/dist/gui/EFScrubber.d.ts +9 -3
- package/dist/gui/EFScrubber.js +13 -13
- package/dist/gui/EFTimeDisplay.d.ts +7 -1
- package/dist/gui/EFTimeDisplay.js +8 -8
- package/dist/gui/EFToggleLoop.d.ts +9 -3
- package/dist/gui/EFToggleLoop.js +7 -5
- package/dist/gui/EFTogglePlay.d.ts +12 -4
- package/dist/gui/EFTogglePlay.js +26 -21
- package/dist/gui/EFWorkbench.js +5 -5
- package/dist/gui/PlaybackController.d.ts +67 -0
- package/dist/gui/PlaybackController.js +310 -0
- package/dist/gui/TWMixin.js +1 -1
- package/dist/gui/TWMixin2.js +1 -1
- package/dist/gui/TargetOrContextMixin.d.ts +10 -0
- package/dist/gui/TargetOrContextMixin.js +98 -0
- package/dist/gui/efContext.d.ts +2 -2
- package/dist/index.d.ts +5 -0
- package/dist/index.js +5 -1
- package/dist/otel/BridgeSpanExporter.d.ts +13 -0
- package/dist/otel/BridgeSpanExporter.js +87 -0
- package/dist/otel/setupBrowserTracing.d.ts +12 -0
- package/dist/otel/setupBrowserTracing.js +32 -0
- package/dist/otel/tracingHelpers.d.ts +34 -0
- package/dist/otel/tracingHelpers.js +112 -0
- package/dist/style.css +1 -1
- package/dist/transcoding/cache/RequestDeduplicator.js +0 -21
- package/dist/transcoding/cache/URLTokenDeduplicator.js +1 -21
- package/dist/transcoding/utils/UrlGenerator.js +2 -19
- package/dist/utils/LRUCache.js +6 -53
- package/package.json +13 -5
- package/src/elements/ContextProxiesController.ts +10 -10
- package/src/elements/EFAudio.ts +1 -0
- package/src/elements/EFCaptions.browsertest.ts +128 -56
- package/src/elements/EFCaptions.ts +60 -34
- package/src/elements/EFImage.browsertest.ts +1 -2
- package/src/elements/EFMedia/AssetMediaEngine.ts +65 -37
- package/src/elements/EFMedia/BaseMediaEngine.ts +110 -52
- package/src/elements/EFMedia/BufferedSeekingInput.ts +218 -101
- package/src/elements/EFMedia/JitMediaEngine.browsertest.ts +3 -0
- package/src/elements/EFMedia/audioTasks/makeAudioInputTask.ts +7 -3
- package/src/elements/EFMedia/audioTasks/makeAudioSeekTask.chunkboundary.regression.browsertest.ts +1 -1
- package/src/elements/EFMedia/videoTasks/MainVideoInputCache.ts +76 -0
- package/src/elements/EFMedia/videoTasks/makeScrubVideoInputTask.ts +16 -10
- package/src/elements/EFMedia/videoTasks/makeScrubVideoSeekTask.ts +7 -1
- package/src/elements/EFMedia/videoTasks/makeUnifiedVideoSeekTask.ts +222 -116
- package/src/elements/EFMedia.browsertest.ts +8 -15
- package/src/elements/EFMedia.ts +54 -8
- package/src/elements/EFSurface.browsertest.ts +2 -6
- package/src/elements/EFSurface.ts +1 -0
- package/src/elements/EFTemporal.browsertest.ts +58 -1
- package/src/elements/EFTemporal.ts +140 -4
- package/src/elements/EFThumbnailStrip.browsertest.ts +2 -8
- package/src/elements/EFThumbnailStrip.ts +1 -0
- package/src/elements/EFTimegroup.browsertest.ts +16 -15
- package/src/elements/EFTimegroup.ts +281 -275
- package/src/elements/EFVideo.browsertest.ts +162 -74
- package/src/elements/EFVideo.ts +229 -101
- package/src/elements/FetchContext.browsertest.ts +7 -2
- package/src/elements/TargetController.browsertest.ts +1 -0
- package/src/elements/TargetController.ts +1 -0
- package/src/elements/renderTemporalAudio.ts +108 -0
- package/src/elements/updateAnimations.browsertest.ts +181 -6
- package/src/elements/updateAnimations.ts +6 -6
- package/src/gui/ContextMixin.browsertest.ts +274 -27
- package/src/gui/ContextMixin.ts +230 -175
- package/src/gui/Controllable.browsertest.ts +258 -0
- package/src/gui/Controllable.ts +41 -0
- package/src/gui/EFControls.browsertest.ts +294 -80
- package/src/gui/EFControls.ts +139 -28
- package/src/gui/EFDial.browsertest.ts +84 -0
- package/src/gui/EFDial.ts +172 -0
- package/src/gui/EFFilmstrip.browsertest.ts +712 -0
- package/src/gui/EFFilmstrip.ts +213 -23
- package/src/gui/EFPause.browsertest.ts +202 -0
- package/src/gui/EFPause.ts +73 -0
- package/src/gui/EFPlay.browsertest.ts +202 -0
- package/src/gui/EFPlay.ts +73 -0
- package/src/gui/EFPreview.ts +20 -5
- package/src/gui/EFResizableBox.browsertest.ts +79 -0
- package/src/gui/EFResizableBox.ts +898 -0
- package/src/gui/EFScrubber.ts +7 -5
- package/src/gui/EFTimeDisplay.browsertest.ts +19 -19
- package/src/gui/EFTimeDisplay.ts +3 -1
- package/src/gui/EFToggleLoop.ts +6 -5
- package/src/gui/EFTogglePlay.ts +30 -23
- package/src/gui/PlaybackController.ts +522 -0
- package/src/gui/TWMixin.css +3 -0
- package/src/gui/TargetOrContextMixin.ts +185 -0
- package/src/gui/efContext.ts +2 -2
- package/src/otel/BridgeSpanExporter.ts +150 -0
- package/src/otel/setupBrowserTracing.ts +73 -0
- package/src/otel/tracingHelpers.ts +251 -0
- package/test/cache-integration-verification.browsertest.ts +1 -1
- package/types.json +1 -1
- package/dist/elements/ContextProxiesController.js +0 -69
|
@@ -3,8 +3,7 @@ import { ThumbnailExtractor } from "./shared/ThumbnailExtractor.js";
|
|
|
3
3
|
var JitMediaEngine = class JitMediaEngine extends BaseMediaEngine {
|
|
4
4
|
static async fetch(host, urlGenerator, url) {
|
|
5
5
|
const engine = new JitMediaEngine(host, urlGenerator);
|
|
6
|
-
|
|
7
|
-
engine.data = data;
|
|
6
|
+
engine.data = await engine.fetchManifest(url);
|
|
8
7
|
return engine;
|
|
9
8
|
}
|
|
10
9
|
constructor(host, urlGenerator) {
|
|
@@ -20,7 +19,7 @@ var JitMediaEngine = class JitMediaEngine extends BaseMediaEngine {
|
|
|
20
19
|
return this.data.sourceUrl;
|
|
21
20
|
}
|
|
22
21
|
get audioRendition() {
|
|
23
|
-
if (!this.data.audioRenditions || this.data.audioRenditions.length === 0) return
|
|
22
|
+
if (!this.data.audioRenditions || this.data.audioRenditions.length === 0) return;
|
|
24
23
|
const rendition = this.data.audioRenditions[0];
|
|
25
24
|
if (!rendition) return void 0;
|
|
26
25
|
return {
|
|
@@ -32,7 +31,7 @@ var JitMediaEngine = class JitMediaEngine extends BaseMediaEngine {
|
|
|
32
31
|
};
|
|
33
32
|
}
|
|
34
33
|
get videoRendition() {
|
|
35
|
-
if (!this.data.videoRenditions || this.data.videoRenditions.length === 0) return
|
|
34
|
+
if (!this.data.videoRenditions || this.data.videoRenditions.length === 0) return;
|
|
36
35
|
const rendition = this.data.videoRenditions[0];
|
|
37
36
|
if (!rendition) return void 0;
|
|
38
37
|
return {
|
|
@@ -57,26 +56,24 @@ var JitMediaEngine = class JitMediaEngine extends BaseMediaEngine {
|
|
|
57
56
|
return this.fetchMedia(url);
|
|
58
57
|
}
|
|
59
58
|
computeSegmentId(desiredSeekTimeMs, rendition) {
|
|
60
|
-
if (desiredSeekTimeMs > this.durationMs) return
|
|
59
|
+
if (desiredSeekTimeMs > this.durationMs) return;
|
|
61
60
|
if (rendition.segmentDurationsMs && rendition.segmentDurationsMs.length > 0) {
|
|
62
61
|
let cumulativeTime = 0;
|
|
63
62
|
for (let i = 0; i < rendition.segmentDurationsMs.length; i++) {
|
|
64
63
|
const segmentDuration = rendition.segmentDurationsMs[i];
|
|
65
64
|
if (segmentDuration === void 0) throw new Error("Segment duration is required for JIT metadata");
|
|
66
|
-
const segmentStartMs
|
|
65
|
+
const segmentStartMs = cumulativeTime;
|
|
67
66
|
const segmentEndMs = cumulativeTime + segmentDuration;
|
|
68
|
-
const
|
|
69
|
-
|
|
70
|
-
if (desiredSeekTimeMs >= segmentStartMs$1 && (desiredSeekTimeMs < segmentEndMs || includesEndTime)) return i + 1;
|
|
67
|
+
const includesEndTime = i === rendition.segmentDurationsMs.length - 1 && desiredSeekTimeMs === this.durationMs;
|
|
68
|
+
if (desiredSeekTimeMs >= segmentStartMs && (desiredSeekTimeMs < segmentEndMs || includesEndTime)) return i + 1;
|
|
71
69
|
cumulativeTime += segmentDuration;
|
|
72
70
|
if (cumulativeTime >= this.durationMs) break;
|
|
73
71
|
}
|
|
74
|
-
return
|
|
72
|
+
return;
|
|
75
73
|
}
|
|
76
74
|
if (!rendition.segmentDurationMs) throw new Error("Segment duration is required for JIT metadata");
|
|
77
75
|
const segmentIndex = Math.floor(desiredSeekTimeMs / rendition.segmentDurationMs);
|
|
78
|
-
|
|
79
|
-
if (segmentStartMs >= this.durationMs) return void 0;
|
|
76
|
+
if (segmentIndex * rendition.segmentDurationMs >= this.durationMs) return;
|
|
80
77
|
return segmentIndex + 1;
|
|
81
78
|
}
|
|
82
79
|
getScrubVideoRendition() {
|
|
@@ -91,10 +88,6 @@ var JitMediaEngine = class JitMediaEngine extends BaseMediaEngine {
|
|
|
91
88
|
segmentDurationsMs: scrubManifestRendition.segmentDurationsMs
|
|
92
89
|
};
|
|
93
90
|
}
|
|
94
|
-
/**
|
|
95
|
-
* Get preferred buffer configuration for JIT transcoding
|
|
96
|
-
* Uses higher buffering since transcoding introduces latency
|
|
97
|
-
*/
|
|
98
91
|
getBufferConfig() {
|
|
99
92
|
return {
|
|
100
93
|
videoBufferDurationMs: 8e3,
|
|
@@ -103,9 +96,6 @@ var JitMediaEngine = class JitMediaEngine extends BaseMediaEngine {
|
|
|
103
96
|
maxAudioBufferFetches: 3
|
|
104
97
|
};
|
|
105
98
|
}
|
|
106
|
-
/**
|
|
107
|
-
* Extract thumbnail canvases using same rendition priority as video playback for frame alignment
|
|
108
|
-
*/
|
|
109
99
|
async extractThumbnails(timestamps) {
|
|
110
100
|
let rendition;
|
|
111
101
|
try {
|
|
@@ -24,21 +24,16 @@ const makeAudioBufferTask = (host) => {
|
|
|
24
24
|
const mediaEngine = await getLatestMediaEngine(host, signal);
|
|
25
25
|
if (!mediaEngine.audioRendition) return currentState;
|
|
26
26
|
const engineConfig = mediaEngine.getBufferConfig();
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
bufferDurationMs,
|
|
31
|
-
maxParallelFetches,
|
|
27
|
+
return manageMediaBuffer(seekTimeMs, {
|
|
28
|
+
bufferDurationMs: engineConfig.audioBufferDurationMs,
|
|
29
|
+
maxParallelFetches: engineConfig.maxAudioBufferFetches,
|
|
32
30
|
enableBuffering: host.enableAudioBuffering
|
|
33
|
-
}
|
|
34
|
-
return manageMediaBuffer(seekTimeMs, currentConfig, currentState, host.intrinsicDurationMs || 1e4, signal, {
|
|
31
|
+
}, currentState, host.intrinsicDurationMs || 1e4, signal, {
|
|
35
32
|
computeSegmentId: async (timeMs, rendition) => {
|
|
36
|
-
|
|
37
|
-
return mediaEngine$1.computeSegmentId(timeMs, rendition);
|
|
33
|
+
return (await getLatestMediaEngine(host, signal)).computeSegmentId(timeMs, rendition);
|
|
38
34
|
},
|
|
39
35
|
prefetchSegment: async (segmentId, rendition) => {
|
|
40
|
-
|
|
41
|
-
await mediaEngine$1.fetchMediaSegment(segmentId, rendition);
|
|
36
|
+
await (await getLatestMediaEngine(host, signal)).fetchMediaSegment(segmentId, rendition);
|
|
42
37
|
},
|
|
43
38
|
isSegmentCached: (segmentId, rendition) => {
|
|
44
39
|
const mediaEngine$1 = host.mediaEngineTask.value;
|
|
@@ -46,8 +41,7 @@ const makeAudioBufferTask = (host) => {
|
|
|
46
41
|
return mediaEngine$1.isSegmentCached(segmentId, rendition);
|
|
47
42
|
},
|
|
48
43
|
getRendition: async () => {
|
|
49
|
-
const
|
|
50
|
-
const audioRendition = mediaEngine$1.audioRendition;
|
|
44
|
+
const audioRendition = (await getLatestMediaEngine(host, signal)).audioRendition;
|
|
51
45
|
if (!audioRendition) throw new Error("Audio rendition not available");
|
|
52
46
|
return audioRendition;
|
|
53
47
|
},
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { EF_INTERACTIVE } from "../../../EF_INTERACTIVE.js";
|
|
2
2
|
import { LRUCache } from "../../../utils/LRUCache.js";
|
|
3
3
|
import { Task } from "@lit/task";
|
|
4
|
-
|
|
4
|
+
var DECAY_WEIGHT = .8;
|
|
5
5
|
function processFFTData(fftData, zeroThresholdPercent = .1) {
|
|
6
6
|
const totalBins = fftData.length;
|
|
7
7
|
const zeroThresholdCount = Math.floor(totalBins * zeroThresholdPercent);
|
|
@@ -13,8 +13,7 @@ function processFFTData(fftData, zeroThresholdPercent = .1) {
|
|
|
13
13
|
break;
|
|
14
14
|
}
|
|
15
15
|
if (cutoffIndex < zeroThresholdCount) return fftData;
|
|
16
|
-
const
|
|
17
|
-
const resampledData = interpolateData(goodData, fftData.length);
|
|
16
|
+
const resampledData = interpolateData(fftData.slice(0, cutoffIndex), fftData.length);
|
|
18
17
|
const attenuationStartIndex = Math.floor(totalBins * .9);
|
|
19
18
|
for (let i = attenuationStartIndex; i < totalBins; i++) {
|
|
20
19
|
const attenuationProgress = (i - attenuationStartIndex) / (totalBins - attenuationStartIndex) + .2;
|
|
@@ -10,7 +10,7 @@ const makeAudioInitSegmentFetchTask = (host) => {
|
|
|
10
10
|
task: async ([_mediaEngine], { signal }) => {
|
|
11
11
|
const mediaEngine = await getLatestMediaEngine(host, signal);
|
|
12
12
|
const audioRendition = mediaEngine.getAudioRendition();
|
|
13
|
-
if (!audioRendition) return
|
|
13
|
+
if (!audioRendition) return;
|
|
14
14
|
return mediaEngine.fetchInitSegment(audioRendition, signal);
|
|
15
15
|
}
|
|
16
16
|
});
|
|
@@ -10,16 +10,17 @@ const makeAudioInputTask = (host) => {
|
|
|
10
10
|
onComplete: (_value) => {},
|
|
11
11
|
task: async (_, { signal }) => {
|
|
12
12
|
const mediaEngine = await host.mediaEngineTask.taskComplete;
|
|
13
|
+
if (signal.aborted) return void 0;
|
|
13
14
|
const audioRendition = mediaEngine?.audioRendition;
|
|
14
|
-
if (!audioRendition) return
|
|
15
|
+
if (!audioRendition) return;
|
|
15
16
|
const initSegment = await host.audioInitSegmentFetchTask.taskComplete;
|
|
16
|
-
signal.
|
|
17
|
+
if (signal.aborted) return void 0;
|
|
17
18
|
const segment = await host.audioSegmentFetchTask.taskComplete;
|
|
18
|
-
signal.
|
|
19
|
-
if (!initSegment || !segment) return
|
|
19
|
+
if (signal.aborted) return void 0;
|
|
20
|
+
if (!initSegment || !segment) return;
|
|
20
21
|
const startTimeOffsetMs = audioRendition.startTimeOffsetMs;
|
|
21
22
|
const arrayBuffer = await new Blob([initSegment, segment]).arrayBuffer();
|
|
22
|
-
signal.
|
|
23
|
+
if (signal.aborted) return void 0;
|
|
23
24
|
return new BufferedSeekingInput(arrayBuffer, {
|
|
24
25
|
videoBufferSize: EFMedia.VIDEO_SAMPLE_BUFFER_SIZE,
|
|
25
26
|
audioBufferSize: EFMedia.AUDIO_SAMPLE_BUFFER_SIZE,
|
|
@@ -11,7 +11,7 @@ const makeAudioSegmentFetchTask = (host) => {
|
|
|
11
11
|
const mediaEngine = await getLatestMediaEngine(host, signal);
|
|
12
12
|
const segmentId = await host.audioSegmentIdTask.taskComplete;
|
|
13
13
|
const audioRendition = mediaEngine.getAudioRendition();
|
|
14
|
-
if (!audioRendition || segmentId === void 0) return
|
|
14
|
+
if (!audioRendition || segmentId === void 0) return;
|
|
15
15
|
return mediaEngine.fetchMediaSegment(segmentId, audioRendition, signal);
|
|
16
16
|
}
|
|
17
17
|
});
|
|
@@ -11,7 +11,7 @@ const makeAudioSegmentIdTask = (host) => {
|
|
|
11
11
|
const mediaEngine = await getLatestMediaEngine(host, signal);
|
|
12
12
|
signal.throwIfAborted();
|
|
13
13
|
const audioRendition = mediaEngine.getAudioRendition();
|
|
14
|
-
if (!audioRendition) return
|
|
14
|
+
if (!audioRendition) return;
|
|
15
15
|
return mediaEngine.computeSegmentId(targetSeekTimeMs, audioRendition);
|
|
16
16
|
}
|
|
17
17
|
});
|
|
@@ -2,7 +2,7 @@ import { EF_INTERACTIVE } from "../../../EF_INTERACTIVE.js";
|
|
|
2
2
|
import { LRUCache } from "../../../utils/LRUCache.js";
|
|
3
3
|
import { IgnorableError } from "../../EFMedia.js";
|
|
4
4
|
import { Task } from "@lit/task";
|
|
5
|
-
|
|
5
|
+
var DECAY_WEIGHT = .8;
|
|
6
6
|
function makeAudioTimeDomainAnalysisTask(element) {
|
|
7
7
|
const cache = new LRUCache(1e3);
|
|
8
8
|
return new Task(element, {
|
|
@@ -1,53 +1,37 @@
|
|
|
1
|
-
|
|
2
|
-
* Fetch audio segment data using MediaEngine
|
|
3
|
-
* Pure function with explicit dependencies
|
|
4
|
-
*/
|
|
5
|
-
const fetchAudioSegmentData = async (segmentIds, mediaEngine, signal) => {
|
|
1
|
+
var fetchAudioSegmentData = async (segmentIds, mediaEngine, signal) => {
|
|
6
2
|
const audioRendition = mediaEngine.audioRendition;
|
|
7
3
|
if (!audioRendition) throw new Error("Audio rendition not available");
|
|
8
4
|
const segmentData = /* @__PURE__ */ new Map();
|
|
9
5
|
const fetchPromises = segmentIds.map(async (segmentId) => {
|
|
10
|
-
|
|
11
|
-
return [segmentId, arrayBuffer];
|
|
6
|
+
return [segmentId, await mediaEngine.fetchMediaSegment(segmentId, audioRendition, signal)];
|
|
12
7
|
});
|
|
13
8
|
const fetchedSegments = await Promise.all(fetchPromises);
|
|
14
9
|
signal.throwIfAborted();
|
|
15
10
|
for (const [segmentId, arrayBuffer] of fetchedSegments) segmentData.set(segmentId, arrayBuffer);
|
|
16
11
|
return segmentData;
|
|
17
12
|
};
|
|
18
|
-
|
|
19
|
-
* Create audio span blob from init segment and media segments
|
|
20
|
-
* Pure function for blob creation
|
|
21
|
-
*/
|
|
22
|
-
const createAudioSpanBlob = (initSegment, mediaSegments) => {
|
|
13
|
+
var createAudioSpanBlob = (initSegment, mediaSegments) => {
|
|
23
14
|
const chunks = [initSegment, ...mediaSegments];
|
|
24
15
|
return new Blob(chunks, { type: "audio/mp4" });
|
|
25
16
|
};
|
|
26
|
-
/**
|
|
27
|
-
* Fetch audio spanning a time range
|
|
28
|
-
* Main function that orchestrates segment calculation, fetching, and blob creation
|
|
29
|
-
*/
|
|
30
17
|
const fetchAudioSpanningTime = async (host, fromMs, toMs, signal) => {
|
|
31
18
|
if (fromMs >= toMs || fromMs < 0) throw new Error(`Invalid time range: fromMs=${fromMs}, toMs=${toMs}`);
|
|
32
19
|
const mediaEngine = await host.mediaEngineTask.taskComplete;
|
|
33
20
|
const initSegment = await host.audioInitSegmentFetchTask.taskComplete;
|
|
34
|
-
if (!mediaEngine?.audioRendition) return
|
|
35
|
-
if (!initSegment) return
|
|
21
|
+
if (!mediaEngine?.audioRendition) return;
|
|
22
|
+
if (!initSegment) return;
|
|
36
23
|
const segmentRanges = mediaEngine.calculateAudioSegmentRange(fromMs, toMs, mediaEngine.audioRendition, host.intrinsicDurationMs || 1e4);
|
|
37
24
|
if (segmentRanges.length === 0) throw new Error(`No segments found for time range ${fromMs}-${toMs}ms`);
|
|
38
25
|
const segmentIds = segmentRanges.map((r) => r.segmentId);
|
|
39
26
|
const segmentData = await fetchAudioSegmentData(segmentIds, mediaEngine, signal);
|
|
40
|
-
const
|
|
27
|
+
const blob = createAudioSpanBlob(initSegment, segmentIds.map((id) => {
|
|
41
28
|
const segment = segmentData.get(id);
|
|
42
29
|
if (!segment) throw new Error(`Missing segment data for segment ID ${id}`);
|
|
43
30
|
return segment;
|
|
44
|
-
});
|
|
45
|
-
const blob = createAudioSpanBlob(initSegment, orderedSegments);
|
|
46
|
-
const actualStartMs = Math.min(...segmentRanges.map((r) => r.startMs));
|
|
47
|
-
const actualEndMs = Math.max(...segmentRanges.map((r) => r.endMs));
|
|
31
|
+
}));
|
|
48
32
|
return {
|
|
49
|
-
startMs:
|
|
50
|
-
endMs:
|
|
33
|
+
startMs: Math.min(...segmentRanges.map((r) => r.startMs)),
|
|
34
|
+
endMs: Math.max(...segmentRanges.map((r) => r.endMs)),
|
|
51
35
|
blob
|
|
52
36
|
};
|
|
53
37
|
};
|
|
@@ -1,6 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Async version of computeSegmentRange for when computeSegmentId is async
|
|
3
|
-
*/
|
|
4
1
|
const computeSegmentRangeAsync = async (startTimeMs, endTimeMs, durationMs, rendition, computeSegmentId) => {
|
|
5
2
|
const segments = [];
|
|
6
3
|
const segmentDurationMs = rendition.segmentDurationMs || 1e3;
|
|
@@ -15,25 +12,14 @@ const computeSegmentRangeAsync = async (startTimeMs, endTimeMs, durationMs, rend
|
|
|
15
12
|
}
|
|
16
13
|
return segments.filter((id, index, arr) => arr.indexOf(id) === index);
|
|
17
14
|
};
|
|
18
|
-
/**
|
|
19
|
-
* Compute buffer queue based on desired segments and what we've already requested
|
|
20
|
-
* Pure function - determines what new segments should be prefetched
|
|
21
|
-
*/
|
|
22
15
|
const computeBufferQueue = (desiredSegments, requestedSegments) => {
|
|
23
16
|
return desiredSegments.filter((segmentId) => !requestedSegments.has(segmentId));
|
|
24
17
|
};
|
|
25
|
-
/**
|
|
26
|
-
* Core media buffering orchestration logic - prefetch only, no data storage
|
|
27
|
-
* Integrates with BaseMediaEngine's existing caching and request deduplication
|
|
28
|
-
*/
|
|
29
18
|
const manageMediaBuffer = async (seekTimeMs, config, currentState, durationMs, signal, deps) => {
|
|
30
19
|
if (!config.enableBuffering) return currentState;
|
|
31
20
|
const rendition = await deps.getRendition();
|
|
32
21
|
if (!rendition) return currentState;
|
|
33
|
-
const
|
|
34
|
-
const desiredSegments = await computeSegmentRangeAsync(seekTimeMs, endTimeMs, durationMs, rendition, deps.computeSegmentId);
|
|
35
|
-
const uncachedSegments = desiredSegments.filter((segmentId) => !deps.isSegmentCached(segmentId, rendition));
|
|
36
|
-
const newQueue = computeBufferQueue(uncachedSegments, currentState.requestedSegments);
|
|
22
|
+
const newQueue = computeBufferQueue((await computeSegmentRangeAsync(seekTimeMs, seekTimeMs + config.bufferDurationMs, durationMs, rendition, deps.computeSegmentId)).filter((segmentId) => !deps.isSegmentCached(segmentId, rendition)), currentState.requestedSegments);
|
|
37
23
|
const newRequestedSegments = new Set(currentState.requestedSegments);
|
|
38
24
|
const newActiveRequests = new Set(currentState.activeRequests);
|
|
39
25
|
const remainingQueue = [...newQueue];
|
|
@@ -60,12 +46,11 @@ const manageMediaBuffer = async (seekTimeMs, config, currentState, durationMs, s
|
|
|
60
46
|
};
|
|
61
47
|
const initialBatchSize = Math.min(config.maxParallelFetches, newQueue.length);
|
|
62
48
|
for (let i = 0; i < initialBatchSize; i++) startNextSegment();
|
|
63
|
-
|
|
49
|
+
return {
|
|
64
50
|
currentSeekTimeMs: seekTimeMs,
|
|
65
51
|
requestedSegments: newRequestedSegments,
|
|
66
52
|
activeRequests: newActiveRequests,
|
|
67
53
|
requestQueue: remainingQueue
|
|
68
54
|
};
|
|
69
|
-
return result;
|
|
70
55
|
};
|
|
71
56
|
export { manageMediaBuffer };
|
|
@@ -1,50 +1,26 @@
|
|
|
1
1
|
import { LRUCache } from "../../../utils/LRUCache.js";
|
|
2
|
-
/**
|
|
3
|
-
* Global cache for MediaBunny Input instances
|
|
4
|
-
* Shared across all MediaEngine instances to prevent duplicate decoding
|
|
5
|
-
* of the same segment data
|
|
6
|
-
*/
|
|
7
2
|
var GlobalInputCache = class {
|
|
8
3
|
constructor() {
|
|
9
4
|
this.cache = new LRUCache(50);
|
|
10
5
|
}
|
|
11
|
-
/**
|
|
12
|
-
* Generate standardized cache key for Input objects
|
|
13
|
-
* Format: "input:{src}:{segmentId}:{renditionId}"
|
|
14
|
-
*/
|
|
15
6
|
generateKey(src, segmentId, renditionId) {
|
|
16
7
|
return `input:${src}:${segmentId}:${renditionId || "default"}`;
|
|
17
8
|
}
|
|
18
|
-
/**
|
|
19
|
-
* Get cached Input object
|
|
20
|
-
*/
|
|
21
9
|
get(src, segmentId, renditionId) {
|
|
22
10
|
const key = this.generateKey(src, segmentId, renditionId);
|
|
23
11
|
return this.cache.get(key);
|
|
24
12
|
}
|
|
25
|
-
/**
|
|
26
|
-
* Cache Input object
|
|
27
|
-
*/
|
|
28
13
|
set(src, segmentId, input, renditionId) {
|
|
29
14
|
const key = this.generateKey(src, segmentId, renditionId);
|
|
30
15
|
this.cache.set(key, input);
|
|
31
16
|
}
|
|
32
|
-
/**
|
|
33
|
-
* Check if Input is cached
|
|
34
|
-
*/
|
|
35
17
|
has(src, segmentId, renditionId) {
|
|
36
18
|
const key = this.generateKey(src, segmentId, renditionId);
|
|
37
19
|
return this.cache.has(key);
|
|
38
20
|
}
|
|
39
|
-
/**
|
|
40
|
-
* Clear all cached Input objects
|
|
41
|
-
*/
|
|
42
21
|
clear() {
|
|
43
22
|
this.cache.clear();
|
|
44
23
|
}
|
|
45
|
-
/**
|
|
46
|
-
* Get cache statistics for debugging
|
|
47
|
-
*/
|
|
48
24
|
getStats() {
|
|
49
25
|
return {
|
|
50
26
|
size: this.cache.size,
|
|
@@ -1,27 +1,6 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Centralized precision utilities for consistent timing calculations across the media pipeline.
|
|
3
|
-
*
|
|
4
|
-
* The key insight is that floating-point precision errors can cause inconsistencies between:
|
|
5
|
-
* 1. Segment selection logic (in AssetMediaEngine.computeSegmentId)
|
|
6
|
-
* 2. Sample finding logic (in SampleBuffer.find)
|
|
7
|
-
* 3. Timeline mapping (in BufferedSeekingInput.seek)
|
|
8
|
-
*
|
|
9
|
-
* All timing calculations must use the same rounding strategy to ensure consistency.
|
|
10
|
-
*/
|
|
11
|
-
/**
|
|
12
|
-
* Round time to millisecond precision to handle floating-point precision issues.
|
|
13
|
-
* Uses Math.round for consistent behavior across the entire pipeline.
|
|
14
|
-
*
|
|
15
|
-
* This function should be used for ALL time-related calculations that need to be
|
|
16
|
-
* compared between different parts of the system.
|
|
17
|
-
*/
|
|
18
1
|
const roundToMilliseconds = (timeMs) => {
|
|
19
2
|
return Math.round(timeMs * 1e3) / 1e3;
|
|
20
3
|
};
|
|
21
|
-
/**
|
|
22
|
-
* Convert media time (in seconds) to scaled time units using consistent rounding.
|
|
23
|
-
* This is used in segment selection to convert from milliseconds to timescale units.
|
|
24
|
-
*/
|
|
25
4
|
const convertToScaledTime = (timeMs, timescale) => {
|
|
26
5
|
const scaledTime = timeMs / 1e3 * timescale;
|
|
27
6
|
return Math.round(scaledTime);
|
|
@@ -1,16 +1,9 @@
|
|
|
1
1
|
import { globalInputCache } from "./GlobalInputCache.js";
|
|
2
2
|
import { ALL_FORMATS, BlobSource, CanvasSink, Input } from "mediabunny";
|
|
3
|
-
/**
|
|
4
|
-
* Shared thumbnail extraction logic for all MediaEngine implementations
|
|
5
|
-
* Eliminates code duplication and provides consistent behavior
|
|
6
|
-
*/
|
|
7
3
|
var ThumbnailExtractor = class {
|
|
8
4
|
constructor(mediaEngine) {
|
|
9
5
|
this.mediaEngine = mediaEngine;
|
|
10
6
|
}
|
|
11
|
-
/**
|
|
12
|
-
* Extract thumbnails at multiple timestamps efficiently using segment batching
|
|
13
|
-
*/
|
|
14
7
|
async extractThumbnails(timestamps, rendition, durationMs) {
|
|
15
8
|
if (timestamps.length === 0) return [];
|
|
16
9
|
const validTimestamps = timestamps.filter((timeMs) => timeMs >= 0 && timeMs <= durationMs);
|
|
@@ -32,9 +25,6 @@ var ThumbnailExtractor = class {
|
|
|
32
25
|
return results.get(t) || null;
|
|
33
26
|
});
|
|
34
27
|
}
|
|
35
|
-
/**
|
|
36
|
-
* Group timestamps by segment ID for efficient batch processing
|
|
37
|
-
*/
|
|
38
28
|
groupTimestampsBySegment(timestamps, rendition) {
|
|
39
29
|
const segmentGroups = /* @__PURE__ */ new Map();
|
|
40
30
|
for (const timeMs of timestamps) try {
|
|
@@ -50,9 +40,6 @@ var ThumbnailExtractor = class {
|
|
|
50
40
|
}
|
|
51
41
|
return segmentGroups;
|
|
52
42
|
}
|
|
53
|
-
/**
|
|
54
|
-
* Extract thumbnails for a specific segment using CanvasSink
|
|
55
|
-
*/
|
|
56
43
|
async extractSegmentThumbnails(segmentId, timestamps, rendition) {
|
|
57
44
|
const results = /* @__PURE__ */ new Map();
|
|
58
45
|
try {
|
|
@@ -95,10 +82,6 @@ var ThumbnailExtractor = class {
|
|
|
95
82
|
}
|
|
96
83
|
return results;
|
|
97
84
|
}
|
|
98
|
-
/**
|
|
99
|
-
* Convert global timestamps to segment-relative timestamps for mediabunny
|
|
100
|
-
* This is where the main difference between JIT and Asset engines lies
|
|
101
|
-
*/
|
|
102
85
|
convertToSegmentRelativeTimestamps(globalTimestamps, segmentId, rendition) {
|
|
103
86
|
return this.mediaEngine.convertToSegmentRelativeTimestamps(globalTimestamps, segmentId, rendition);
|
|
104
87
|
}
|
|
@@ -9,10 +9,6 @@ const getLatestMediaEngine = async (host, signal) => {
|
|
|
9
9
|
if (!mediaEngine) throw new Error("Media engine is not available");
|
|
10
10
|
return mediaEngine;
|
|
11
11
|
};
|
|
12
|
-
/**
|
|
13
|
-
* Core logic for creating a MediaEngine with explicit dependencies.
|
|
14
|
-
* Pure function that requires all dependencies to be provided.
|
|
15
|
-
*/
|
|
16
12
|
const createMediaEngine = (host) => {
|
|
17
13
|
const { src, assetId, urlGenerator, apiHost } = host;
|
|
18
14
|
if (assetId !== null && assetId !== void 0 && assetId.trim() !== "") {
|
|
@@ -25,15 +21,10 @@ const createMediaEngine = (host) => {
|
|
|
25
21
|
}
|
|
26
22
|
const lowerSrc = src.toLowerCase();
|
|
27
23
|
if (!lowerSrc.startsWith("http://") && !lowerSrc.startsWith("https://")) return AssetMediaEngine.fetch(host, urlGenerator, src);
|
|
28
|
-
|
|
29
|
-
if (configuration?.mediaEngine === "local") return AssetMediaEngine.fetch(host, urlGenerator, src);
|
|
24
|
+
if (host.closest("ef-configuration")?.mediaEngine === "local") return AssetMediaEngine.fetch(host, urlGenerator, src);
|
|
30
25
|
const url = urlGenerator.generateManifestUrl(src);
|
|
31
26
|
return JitMediaEngine.fetch(host, urlGenerator, url);
|
|
32
27
|
};
|
|
33
|
-
/**
|
|
34
|
-
* Handle completion of media engine task - triggers necessary updates.
|
|
35
|
-
* Extracted for testability.
|
|
36
|
-
*/
|
|
37
28
|
const handleMediaEngineComplete = (host) => {
|
|
38
29
|
host.requestUpdate("intrinsicDurationMs");
|
|
39
30
|
host.requestUpdate("ownCurrentTimeMs");
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { BufferedSeekingInput } from '../BufferedSeekingInput';
|
|
2
|
+
/**
|
|
3
|
+
* Cache for main video BufferedSeekingInput instances
|
|
4
|
+
* Main video segments are typically 2s long, so we can reuse the same input
|
|
5
|
+
* for multiple frames within that segment (e.g., 60 frames at 30fps)
|
|
6
|
+
*/
|
|
7
|
+
export declare class MainVideoInputCache {
|
|
8
|
+
private cache;
|
|
9
|
+
private maxCacheSize;
|
|
10
|
+
/**
|
|
11
|
+
* Create a cache key that uniquely identifies a segment
|
|
12
|
+
*/
|
|
13
|
+
private getCacheKey;
|
|
14
|
+
/**
|
|
15
|
+
* Get or create BufferedSeekingInput for a main video segment
|
|
16
|
+
*/
|
|
17
|
+
getOrCreateInput(src: string, segmentId: number, renditionId: string | undefined, createInputFn: () => Promise<BufferedSeekingInput | undefined>): Promise<BufferedSeekingInput | undefined>;
|
|
18
|
+
/**
|
|
19
|
+
* Clear the entire cache (called when video changes)
|
|
20
|
+
*/
|
|
21
|
+
clear(): void;
|
|
22
|
+
/**
|
|
23
|
+
* Get cache statistics
|
|
24
|
+
*/
|
|
25
|
+
getStats(): {
|
|
26
|
+
size: number;
|
|
27
|
+
cacheKeys: string[];
|
|
28
|
+
};
|
|
29
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
var MainVideoInputCache = class {
|
|
2
|
+
constructor() {
|
|
3
|
+
this.cache = /* @__PURE__ */ new Map();
|
|
4
|
+
this.maxCacheSize = 10;
|
|
5
|
+
}
|
|
6
|
+
getCacheKey(src, segmentId, renditionId) {
|
|
7
|
+
return `${src}:${renditionId || "default"}:${segmentId}`;
|
|
8
|
+
}
|
|
9
|
+
async getOrCreateInput(src, segmentId, renditionId, createInputFn) {
|
|
10
|
+
const cacheKey = this.getCacheKey(src, segmentId, renditionId);
|
|
11
|
+
const cached = this.cache.get(cacheKey);
|
|
12
|
+
if (cached) return cached;
|
|
13
|
+
const input = await createInputFn();
|
|
14
|
+
if (!input) return;
|
|
15
|
+
this.cache.set(cacheKey, input);
|
|
16
|
+
if (this.cache.size > this.maxCacheSize) {
|
|
17
|
+
const oldestKey = this.cache.keys().next().value;
|
|
18
|
+
if (oldestKey !== void 0) this.cache.delete(oldestKey);
|
|
19
|
+
}
|
|
20
|
+
return input;
|
|
21
|
+
}
|
|
22
|
+
clear() {
|
|
23
|
+
this.cache.clear();
|
|
24
|
+
}
|
|
25
|
+
getStats() {
|
|
26
|
+
return {
|
|
27
|
+
size: this.cache.size,
|
|
28
|
+
cacheKeys: Array.from(this.cache.keys())
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
export { MainVideoInputCache };
|
|
@@ -1,21 +1,13 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Cache for scrub BufferedSeekingInput instances
|
|
3
|
-
* Since scrub segments are 30s long, we can reuse the same input for many seeks
|
|
4
|
-
* within that time range, making scrub seeking very efficient
|
|
5
|
-
*/
|
|
6
1
|
var ScrubInputCache = class {
|
|
7
2
|
constructor() {
|
|
8
3
|
this.cache = /* @__PURE__ */ new Map();
|
|
9
4
|
this.maxCacheSize = 5;
|
|
10
5
|
}
|
|
11
|
-
/**
|
|
12
|
-
* Get or create BufferedSeekingInput for a scrub segment
|
|
13
|
-
*/
|
|
14
6
|
async getOrCreateInput(segmentId, createInputFn) {
|
|
15
7
|
const cached = this.cache.get(segmentId);
|
|
16
8
|
if (cached) return cached;
|
|
17
9
|
const input = await createInputFn();
|
|
18
|
-
if (!input) return
|
|
10
|
+
if (!input) return;
|
|
19
11
|
this.cache.set(segmentId, input);
|
|
20
12
|
if (this.cache.size > this.maxCacheSize) {
|
|
21
13
|
const oldestKey = this.cache.keys().next().value;
|
|
@@ -23,15 +15,9 @@ var ScrubInputCache = class {
|
|
|
23
15
|
}
|
|
24
16
|
return input;
|
|
25
17
|
}
|
|
26
|
-
/**
|
|
27
|
-
* Clear the entire cache (called when video changes)
|
|
28
|
-
*/
|
|
29
18
|
clear() {
|
|
30
19
|
this.cache.clear();
|
|
31
20
|
}
|
|
32
|
-
/**
|
|
33
|
-
* Get cache statistics
|
|
34
|
-
*/
|
|
35
21
|
getStats() {
|
|
36
22
|
return {
|
|
37
23
|
size: this.cache.size,
|
|
@@ -2,11 +2,6 @@ import { EF_INTERACTIVE } from "../../../EF_INTERACTIVE.js";
|
|
|
2
2
|
import { EF_RENDERING } from "../../../EF_RENDERING.js";
|
|
3
3
|
import { manageMediaBuffer } from "../shared/BufferUtils.js";
|
|
4
4
|
import { Task } from "@lit/task";
|
|
5
|
-
/**
|
|
6
|
-
* Scrub video buffer task - aggressively preloads the ENTIRE scrub track
|
|
7
|
-
* Unlike main video buffering, this loads the full duration with higher concurrency
|
|
8
|
-
* for instant visual feedback during seeking
|
|
9
|
-
*/
|
|
10
5
|
const makeScrubVideoBufferTask = (host) => {
|
|
11
6
|
let currentState = {
|
|
12
7
|
currentSeekTimeMs: 0,
|
|
@@ -39,7 +34,7 @@ const makeScrubVideoBufferTask = (host) => {
|
|
|
39
34
|
} catch (error) {
|
|
40
35
|
console.warn("ScrubBuffer: Failed to cache scrub init segment:", error);
|
|
41
36
|
}
|
|
42
|
-
|
|
37
|
+
return await manageMediaBuffer(0, {
|
|
43
38
|
bufferDurationMs: mediaEngine.durationMs,
|
|
44
39
|
maxParallelFetches: 10,
|
|
45
40
|
enableBuffering: true,
|
|
@@ -59,7 +54,6 @@ const makeScrubVideoBufferTask = (host) => {
|
|
|
59
54
|
console.warn(`ScrubBuffer: ${message}`, error);
|
|
60
55
|
}
|
|
61
56
|
});
|
|
62
|
-
return newState;
|
|
63
57
|
} catch (error) {
|
|
64
58
|
if (signal.aborted) return currentState;
|
|
65
59
|
console.warn("ScrubBuffer failed:", error);
|
|
@@ -8,19 +8,22 @@ const makeScrubVideoInputTask = (host) => {
|
|
|
8
8
|
console.error("scrubVideoInputTask error", error);
|
|
9
9
|
},
|
|
10
10
|
onComplete: (_value) => {},
|
|
11
|
-
task: async () => {
|
|
11
|
+
task: async (_, { signal }) => {
|
|
12
12
|
const initSegment = await host.scrubVideoInitSegmentFetchTask.taskComplete;
|
|
13
|
+
if (signal.aborted) return void 0;
|
|
13
14
|
const segment = await host.scrubVideoSegmentFetchTask.taskComplete;
|
|
15
|
+
if (signal.aborted) return void 0;
|
|
14
16
|
if (!initSegment || !segment) throw new Error("Scrub init segment or segment is not available");
|
|
15
17
|
const mediaEngine = await host.mediaEngineTask.taskComplete;
|
|
16
|
-
|
|
17
|
-
const startTimeOffsetMs =
|
|
18
|
-
const
|
|
18
|
+
if (signal.aborted) return void 0;
|
|
19
|
+
const startTimeOffsetMs = mediaEngine.getScrubVideoRendition()?.startTimeOffsetMs;
|
|
20
|
+
const arrayBuffer = await new Blob([initSegment, segment]).arrayBuffer();
|
|
21
|
+
if (signal.aborted) return void 0;
|
|
22
|
+
return new BufferedSeekingInput(arrayBuffer, {
|
|
19
23
|
videoBufferSize: EFMedia.VIDEO_SAMPLE_BUFFER_SIZE,
|
|
20
24
|
audioBufferSize: EFMedia.AUDIO_SAMPLE_BUFFER_SIZE,
|
|
21
25
|
startTimeOffsetMs
|
|
22
26
|
});
|
|
23
|
-
return input;
|
|
24
27
|
}
|
|
25
28
|
});
|
|
26
29
|
};
|