@meframe/core 0.0.30 → 0.0.32
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/Meframe.d.ts +2 -2
- package/dist/Meframe.d.ts.map +1 -1
- package/dist/Meframe.js +3 -3
- package/dist/Meframe.js.map +1 -1
- package/dist/_virtual/_commonjsHelpers.js +7 -0
- package/dist/_virtual/_commonjsHelpers.js.map +1 -0
- package/dist/cache/CacheManager.d.ts +12 -17
- package/dist/cache/CacheManager.d.ts.map +1 -1
- package/dist/cache/CacheManager.js +18 -281
- package/dist/cache/CacheManager.js.map +1 -1
- package/dist/cache/l1/AudioL1Cache.d.ts +36 -19
- package/dist/cache/l1/AudioL1Cache.d.ts.map +1 -1
- package/dist/cache/l1/AudioL1Cache.js +182 -282
- package/dist/cache/l1/AudioL1Cache.js.map +1 -1
- package/dist/controllers/PlaybackController.d.ts +5 -2
- package/dist/controllers/PlaybackController.d.ts.map +1 -1
- package/dist/controllers/PlaybackController.js +60 -13
- package/dist/controllers/PlaybackController.js.map +1 -1
- package/dist/medeo-fe/node_modules/.pnpm/mp4-muxer@5.2.2/node_modules/mp4-muxer/build/mp4-muxer.js.map +1 -0
- package/dist/{node_modules → medeo-fe/node_modules}/.pnpm/mp4box@0.5.4/node_modules/mp4box/dist/mp4box.all.js +7 -2
- package/dist/medeo-fe/node_modules/.pnpm/mp4box@0.5.4/node_modules/mp4box/dist/mp4box.all.js.map +1 -0
- package/dist/model/types.d.ts +0 -4
- package/dist/model/types.d.ts.map +1 -1
- package/dist/model/types.js.map +1 -1
- package/dist/orchestrator/ExportScheduler.d.ts +6 -0
- package/dist/orchestrator/ExportScheduler.d.ts.map +1 -1
- package/dist/orchestrator/ExportScheduler.js +45 -66
- package/dist/orchestrator/ExportScheduler.js.map +1 -1
- package/dist/orchestrator/GlobalAudioSession.d.ts +35 -28
- package/dist/orchestrator/GlobalAudioSession.d.ts.map +1 -1
- package/dist/orchestrator/GlobalAudioSession.js +212 -421
- package/dist/orchestrator/GlobalAudioSession.js.map +1 -1
- package/dist/orchestrator/OnDemandVideoSession.d.ts +3 -3
- package/dist/orchestrator/OnDemandVideoSession.d.ts.map +1 -1
- package/dist/orchestrator/OnDemandVideoSession.js +4 -4
- package/dist/orchestrator/OnDemandVideoSession.js.map +1 -1
- package/dist/orchestrator/Orchestrator.d.ts +1 -2
- package/dist/orchestrator/Orchestrator.d.ts.map +1 -1
- package/dist/orchestrator/Orchestrator.js +34 -48
- package/dist/orchestrator/Orchestrator.js.map +1 -1
- package/dist/orchestrator/VideoClipSession.d.ts +0 -2
- package/dist/orchestrator/VideoClipSession.d.ts.map +1 -1
- package/dist/orchestrator/VideoClipSession.js +0 -49
- package/dist/orchestrator/VideoClipSession.js.map +1 -1
- package/dist/stages/compose/OfflineAudioMixer.d.ts.map +1 -1
- package/dist/stages/compose/OfflineAudioMixer.js +13 -18
- package/dist/stages/compose/OfflineAudioMixer.js.map +1 -1
- package/dist/stages/compose/VideoComposer.d.ts.map +1 -1
- package/dist/stages/compose/VideoComposer.js +4 -0
- package/dist/stages/compose/VideoComposer.js.map +1 -1
- package/dist/stages/decode/AudioChunkDecoder.js +169 -0
- package/dist/stages/decode/AudioChunkDecoder.js.map +1 -0
- package/dist/stages/demux/MP3FrameParser.js +186 -0
- package/dist/stages/demux/MP3FrameParser.js.map +1 -0
- package/dist/stages/demux/MP4Demuxer.js +6 -7
- package/dist/stages/demux/MP4Demuxer.js.map +1 -1
- package/dist/stages/demux/MP4IndexParser.js +3 -4
- package/dist/stages/demux/MP4IndexParser.js.map +1 -1
- package/dist/stages/load/ResourceLoader.d.ts +20 -10
- package/dist/stages/load/ResourceLoader.d.ts.map +1 -1
- package/dist/stages/load/ResourceLoader.js +92 -139
- package/dist/stages/load/ResourceLoader.js.map +1 -1
- package/dist/stages/load/index.d.ts +0 -1
- package/dist/stages/load/index.d.ts.map +1 -1
- package/dist/stages/load/types.d.ts +1 -0
- package/dist/stages/load/types.d.ts.map +1 -1
- package/dist/stages/mux/MP4Muxer.js +1 -1
- package/dist/utils/audio-data.d.ts +16 -0
- package/dist/utils/audio-data.d.ts.map +1 -0
- package/dist/utils/audio-data.js +111 -0
- package/dist/utils/audio-data.js.map +1 -0
- package/dist/utils/mp4box.d.ts +4 -0
- package/dist/utils/mp4box.d.ts.map +1 -0
- package/dist/utils/mp4box.js +17 -0
- package/dist/utils/mp4box.js.map +1 -0
- package/dist/workers/{MP4Demuxer.BEa6PLJm.js → MP4Demuxer.DxMpB08B.js} +49 -11
- package/dist/workers/MP4Demuxer.DxMpB08B.js.map +1 -0
- package/dist/workers/stages/compose/{video-compose.worker.DHQ8B105.js → video-compose.worker.BhpN-lxf.js} +5 -1
- package/dist/workers/stages/compose/video-compose.worker.BhpN-lxf.js.map +1 -0
- package/dist/workers/stages/demux/{audio-demux.worker._VRQdLdv.js → audio-demux.worker.Fd8sRTYi.js} +2 -2
- package/dist/workers/stages/demux/{audio-demux.worker._VRQdLdv.js.map → audio-demux.worker.Fd8sRTYi.js.map} +1 -1
- package/dist/workers/stages/demux/{video-demux.worker.CSkxGtmx.js → video-demux.worker.DqFOe12v.js} +2 -2
- package/dist/workers/stages/demux/{video-demux.worker.CSkxGtmx.js.map → video-demux.worker.DqFOe12v.js.map} +1 -1
- package/dist/workers/worker-manifest.json +3 -3
- package/package.json +1 -1
- package/dist/cache/resource/ImageBitmapCache.d.ts +0 -65
- package/dist/cache/resource/ImageBitmapCache.d.ts.map +0 -1
- package/dist/cache/resource/ImageBitmapCache.js +0 -101
- package/dist/cache/resource/ImageBitmapCache.js.map +0 -1
- package/dist/node_modules/.pnpm/mp4-muxer@5.2.2/node_modules/mp4-muxer/build/mp4-muxer.js.map +0 -1
- package/dist/node_modules/.pnpm/mp4box@0.5.4/node_modules/mp4box/dist/mp4box.all.js.map +0 -1
- package/dist/stages/load/WindowByteRangeResolver.d.ts +0 -47
- package/dist/stages/load/WindowByteRangeResolver.d.ts.map +0 -1
- package/dist/stages/load/WindowByteRangeResolver.js +0 -270
- package/dist/stages/load/WindowByteRangeResolver.js.map +0 -1
- package/dist/workers/MP4Demuxer.BEa6PLJm.js.map +0 -1
- package/dist/workers/stages/compose/video-compose.worker.DHQ8B105.js.map +0 -1
- /package/dist/{node_modules → medeo-fe/node_modules}/.pnpm/mp4-muxer@5.2.2/node_modules/mp4-muxer/build/mp4-muxer.js +0 -0
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
import { Resource } from '../../model';
|
|
2
|
-
|
|
3
|
-
type TrackSample = {
|
|
4
|
-
timeUs: number;
|
|
5
|
-
byteOffset: number;
|
|
6
|
-
byteSize: number;
|
|
7
|
-
isKey: boolean;
|
|
8
|
-
};
|
|
9
|
-
type TrackIndex = {
|
|
10
|
-
trackId: number;
|
|
11
|
-
timescale: number;
|
|
12
|
-
samples: TrackSample[];
|
|
13
|
-
};
|
|
14
|
-
interface ResolverOptions {
|
|
15
|
-
maxMoovBytes?: number;
|
|
16
|
-
maxInitialFetchBytes?: number;
|
|
17
|
-
}
|
|
18
|
-
export interface ByteRange {
|
|
19
|
-
byteStart: number;
|
|
20
|
-
byteEnd: number;
|
|
21
|
-
alignedToKeyframe: boolean;
|
|
22
|
-
}
|
|
23
|
-
interface ResolverState {
|
|
24
|
-
resource: Resource;
|
|
25
|
-
status: 'pending' | 'ready' | 'failed';
|
|
26
|
-
promise: Promise<void>;
|
|
27
|
-
trackIndex?: TrackIndex;
|
|
28
|
-
error?: Error;
|
|
29
|
-
}
|
|
30
|
-
export declare class WindowByteRangeResolver {
|
|
31
|
-
private maxMoovBytes;
|
|
32
|
-
private initialFetchBytes;
|
|
33
|
-
private states;
|
|
34
|
-
constructor(options?: ResolverOptions);
|
|
35
|
-
ensureReady(resource: Resource): Promise<void>;
|
|
36
|
-
resolveWindow(resourceId: string, startUs: number, endUs: number): ByteRange | null;
|
|
37
|
-
getStatus(resourceId: string): ResolverState['status'] | undefined;
|
|
38
|
-
dispose(): void;
|
|
39
|
-
private buildIndex;
|
|
40
|
-
private createTrackIndex;
|
|
41
|
-
private findStartSampleIndex;
|
|
42
|
-
private findEndSampleIndex;
|
|
43
|
-
private binarySearch;
|
|
44
|
-
private resolveSampleByteOffset;
|
|
45
|
-
}
|
|
46
|
-
export {};
|
|
47
|
-
//# sourceMappingURL=WindowByteRangeResolver.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"WindowByteRangeResolver.d.ts","sourceRoot":"","sources":["../../../src/stages/load/WindowByteRangeResolver.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAE5C,KAAK,WAAW,GAAG;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,OAAO,CAAC;CAChB,CAAC;AAEF,KAAK,UAAU,GAAG;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,WAAW,EAAE,CAAC;CACxB,CAAC;AAEF,UAAU,eAAe;IACvB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,oBAAoB,CAAC,EAAE,MAAM,CAAC;CAC/B;AAED,MAAM,WAAW,SAAS;IACxB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,iBAAiB,EAAE,OAAO,CAAC;CAC5B;AAED,UAAU,aAAa;IACrB,QAAQ,EAAE,QAAQ,CAAC;IACnB,MAAM,EAAE,SAAS,GAAG,OAAO,GAAG,QAAQ,CAAC;IACvC,OAAO,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC;IACvB,UAAU,CAAC,EAAE,UAAU,CAAC;IACxB,KAAK,CAAC,EAAE,KAAK,CAAC;CACf;AAKD,qBAAa,uBAAuB;IAClC,OAAO,CAAC,YAAY,CAAS;IAC7B,OAAO,CAAC,iBAAiB,CAAS;IAClC,OAAO,CAAC,MAAM,CAAoC;gBAEtC,OAAO,CAAC,EAAE,eAAe;IAKrC,WAAW,CAAC,QAAQ,EAAE,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC;IAgC9C,aAAa,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI;IAyCnF,SAAS,CAAC,UAAU,EAAE,MAAM,GAAG,aAAa,CAAC,QAAQ,CAAC,GAAG,SAAS;IAIlE,OAAO,IAAI,IAAI;YAID,UAAU;IAyFxB,OAAO,CAAC,gBAAgB;IAwCxB,OAAO,CAAC,oBAAoB;IAwB5B,OAAO,CAAC,kBAAkB;IAiB1B,OAAO,CAAC,YAAY;IA6BpB,OAAO,CAAC,uBAAuB;CA8ChC"}
|
|
@@ -1,270 +0,0 @@
|
|
|
1
|
-
import "../../node_modules/.pnpm/mp4box@0.5.4/node_modules/mp4box/dist/mp4box.all.js";
|
|
2
|
-
import { __exports as mp4box_all } from "../../_virtual/mp4box.all.js";
|
|
3
|
-
const DEFAULT_MAX_MOOV_BYTES = 4 * 1024 * 1024;
|
|
4
|
-
const DEFAULT_INITIAL_FETCH_BYTES = 1024 * 1024;
|
|
5
|
-
class WindowByteRangeResolver {
|
|
6
|
-
maxMoovBytes;
|
|
7
|
-
initialFetchBytes;
|
|
8
|
-
states = /* @__PURE__ */ new Map();
|
|
9
|
-
constructor(options) {
|
|
10
|
-
this.maxMoovBytes = options?.maxMoovBytes ?? DEFAULT_MAX_MOOV_BYTES;
|
|
11
|
-
this.initialFetchBytes = options?.maxInitialFetchBytes ?? DEFAULT_INITIAL_FETCH_BYTES;
|
|
12
|
-
}
|
|
13
|
-
ensureReady(resource) {
|
|
14
|
-
if (!resource.id) {
|
|
15
|
-
return Promise.reject(new Error("Resource id missing"));
|
|
16
|
-
}
|
|
17
|
-
const existing = this.states.get(resource.id);
|
|
18
|
-
if (existing) {
|
|
19
|
-
if (existing.status === "failed") {
|
|
20
|
-
return Promise.reject(existing.error ?? new Error("Resolver failed"));
|
|
21
|
-
}
|
|
22
|
-
return existing.promise;
|
|
23
|
-
}
|
|
24
|
-
const promise = this.buildIndex(resource).catch((error) => {
|
|
25
|
-
const state = this.states.get(resource.id);
|
|
26
|
-
if (state) {
|
|
27
|
-
state.status = "failed";
|
|
28
|
-
state.error = error instanceof Error ? error : new Error(String(error));
|
|
29
|
-
}
|
|
30
|
-
throw error;
|
|
31
|
-
});
|
|
32
|
-
this.states.set(resource.id, {
|
|
33
|
-
resource,
|
|
34
|
-
status: "pending",
|
|
35
|
-
promise
|
|
36
|
-
});
|
|
37
|
-
return promise;
|
|
38
|
-
}
|
|
39
|
-
resolveWindow(resourceId, startUs, endUs) {
|
|
40
|
-
const state = this.states.get(resourceId);
|
|
41
|
-
if (!state || state.status !== "ready" || !state.trackIndex) {
|
|
42
|
-
return null;
|
|
43
|
-
}
|
|
44
|
-
const { samples } = state.trackIndex;
|
|
45
|
-
if (!samples.length) {
|
|
46
|
-
return null;
|
|
47
|
-
}
|
|
48
|
-
const startSampleIndex = this.findStartSampleIndex(samples, startUs);
|
|
49
|
-
const endSampleIndex = this.findEndSampleIndex(samples, endUs, startSampleIndex);
|
|
50
|
-
if (startSampleIndex < 0 || endSampleIndex < 0) {
|
|
51
|
-
return null;
|
|
52
|
-
}
|
|
53
|
-
const startSample = samples[startSampleIndex];
|
|
54
|
-
const endSample = samples[endSampleIndex];
|
|
55
|
-
if (!startSample || !endSample) {
|
|
56
|
-
return null;
|
|
57
|
-
}
|
|
58
|
-
const alignedToKeyframe = startSample.isKey;
|
|
59
|
-
const byteStart = startSample.byteOffset;
|
|
60
|
-
const byteEnd = endSample.byteOffset + endSample.byteSize - 1;
|
|
61
|
-
if (Number.isNaN(byteStart) || Number.isNaN(byteEnd) || byteEnd < byteStart) {
|
|
62
|
-
return null;
|
|
63
|
-
}
|
|
64
|
-
return {
|
|
65
|
-
byteStart,
|
|
66
|
-
byteEnd,
|
|
67
|
-
alignedToKeyframe
|
|
68
|
-
};
|
|
69
|
-
}
|
|
70
|
-
getStatus(resourceId) {
|
|
71
|
-
return this.states.get(resourceId)?.status;
|
|
72
|
-
}
|
|
73
|
-
dispose() {
|
|
74
|
-
this.states.clear();
|
|
75
|
-
}
|
|
76
|
-
async buildIndex(resource) {
|
|
77
|
-
if (!resource.uri) {
|
|
78
|
-
throw new Error(`Resource ${resource.id} missing uri`);
|
|
79
|
-
}
|
|
80
|
-
const mp4boxFile = mp4box_all.createFile();
|
|
81
|
-
const buffers = [];
|
|
82
|
-
let totalFetched = 0;
|
|
83
|
-
let readyInfo;
|
|
84
|
-
let trackIndex;
|
|
85
|
-
const readyPromise = new Promise((resolve, reject) => {
|
|
86
|
-
mp4boxFile.onReady = (info) => {
|
|
87
|
-
readyInfo = info;
|
|
88
|
-
resolve();
|
|
89
|
-
};
|
|
90
|
-
mp4boxFile.onError = (error) => {
|
|
91
|
-
reject(error instanceof Error ? error : new Error(String(error)));
|
|
92
|
-
};
|
|
93
|
-
});
|
|
94
|
-
while (totalFetched < this.maxMoovBytes) {
|
|
95
|
-
const chunkSize = Math.min(
|
|
96
|
-
this.initialFetchBytes * Math.pow(2, buffers.length),
|
|
97
|
-
this.maxMoovBytes - totalFetched
|
|
98
|
-
);
|
|
99
|
-
const rangeStart = totalFetched;
|
|
100
|
-
const rangeEnd = totalFetched + chunkSize - 1;
|
|
101
|
-
const response = await fetch(resource.uri, {
|
|
102
|
-
headers: {
|
|
103
|
-
Range: `bytes=${rangeStart}-${rangeEnd}`
|
|
104
|
-
}
|
|
105
|
-
});
|
|
106
|
-
if (!response.ok) {
|
|
107
|
-
throw new Error(`Failed to fetch range ${rangeStart}-${rangeEnd}: HTTP ${response.status}`);
|
|
108
|
-
}
|
|
109
|
-
const buffer = await response.arrayBuffer();
|
|
110
|
-
buffer.fileStart = rangeStart;
|
|
111
|
-
totalFetched += buffer.byteLength;
|
|
112
|
-
buffers.push(buffer);
|
|
113
|
-
mp4boxFile.appendBuffer(buffer);
|
|
114
|
-
try {
|
|
115
|
-
await readyPromise;
|
|
116
|
-
break;
|
|
117
|
-
} catch (error) {
|
|
118
|
-
if (totalFetched >= this.maxMoovBytes) {
|
|
119
|
-
throw error;
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
if (readyInfo) {
|
|
124
|
-
for (const track of readyInfo.tracks ?? []) {
|
|
125
|
-
const extractionOptions = {
|
|
126
|
-
rapAlignment: false,
|
|
127
|
-
keepSamples: true
|
|
128
|
-
};
|
|
129
|
-
const totalSamples = track?.samples?.length ?? track?.sample_count;
|
|
130
|
-
if (typeof totalSamples === "number" && Number.isFinite(totalSamples) && totalSamples > 0) {
|
|
131
|
-
extractionOptions.nbSamples = totalSamples;
|
|
132
|
-
}
|
|
133
|
-
mp4boxFile.setExtractionOptions(track.id, null, extractionOptions);
|
|
134
|
-
}
|
|
135
|
-
mp4boxFile.start();
|
|
136
|
-
mp4boxFile.flush();
|
|
137
|
-
trackIndex = this.createTrackIndex(mp4boxFile, readyInfo);
|
|
138
|
-
}
|
|
139
|
-
if (!trackIndex) {
|
|
140
|
-
throw new Error("Failed to build track index");
|
|
141
|
-
}
|
|
142
|
-
const state = this.states.get(resource.id);
|
|
143
|
-
if (state) {
|
|
144
|
-
state.status = "ready";
|
|
145
|
-
state.trackIndex = trackIndex;
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
createTrackIndex(mp4boxFile, info) {
|
|
149
|
-
const videoTrack = info.tracks?.find((track) => track.type === "video");
|
|
150
|
-
if (!videoTrack) {
|
|
151
|
-
throw new Error("No video track found");
|
|
152
|
-
}
|
|
153
|
-
const timescale = videoTrack.timescale || 9e4;
|
|
154
|
-
const trackId = videoTrack.id;
|
|
155
|
-
let samples = mp4boxFile.getTrackSamples?.(trackId) ?? [];
|
|
156
|
-
if ((!samples || samples.length === 0) && typeof mp4boxFile.getTrackById === "function") {
|
|
157
|
-
const fullTrack = mp4boxFile.getTrackById(trackId);
|
|
158
|
-
if (fullTrack?.samples?.length) {
|
|
159
|
-
samples = fullTrack.samples;
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
const indexedSamples = (samples ?? []).map((sample, index) => {
|
|
163
|
-
const sampleByteOffset = this.resolveSampleByteOffset(mp4boxFile, trackId, sample, index);
|
|
164
|
-
const timeUs = Math.round(sample.cts * 1e6 / timescale);
|
|
165
|
-
return {
|
|
166
|
-
timeUs,
|
|
167
|
-
byteOffset: sampleByteOffset,
|
|
168
|
-
byteSize: sample.size,
|
|
169
|
-
isKey: Boolean(sample.is_sync)
|
|
170
|
-
};
|
|
171
|
-
});
|
|
172
|
-
return {
|
|
173
|
-
trackId,
|
|
174
|
-
timescale,
|
|
175
|
-
samples: indexedSamples
|
|
176
|
-
};
|
|
177
|
-
}
|
|
178
|
-
findStartSampleIndex(samples, targetUs) {
|
|
179
|
-
if (!samples.length) {
|
|
180
|
-
return 0;
|
|
181
|
-
}
|
|
182
|
-
let index = this.binarySearch(samples, targetUs, (sample, time) => sample.timeUs - time);
|
|
183
|
-
if (index < 0) {
|
|
184
|
-
index = ~index;
|
|
185
|
-
if (index >= samples.length) {
|
|
186
|
-
index = samples.length - 1;
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
while (index > 0) {
|
|
190
|
-
const current = samples[index];
|
|
191
|
-
if (!current || current.isKey) {
|
|
192
|
-
break;
|
|
193
|
-
}
|
|
194
|
-
index -= 1;
|
|
195
|
-
}
|
|
196
|
-
return index;
|
|
197
|
-
}
|
|
198
|
-
findEndSampleIndex(samples, targetUs, startIndex) {
|
|
199
|
-
if (!samples.length) {
|
|
200
|
-
return 0;
|
|
201
|
-
}
|
|
202
|
-
let index = Math.min(Math.max(startIndex, 0), samples.length - 1);
|
|
203
|
-
while (index + 1 < samples.length) {
|
|
204
|
-
const nextSample = samples[index + 1];
|
|
205
|
-
if (!nextSample || nextSample.timeUs > targetUs) {
|
|
206
|
-
break;
|
|
207
|
-
}
|
|
208
|
-
index += 1;
|
|
209
|
-
}
|
|
210
|
-
return index;
|
|
211
|
-
}
|
|
212
|
-
binarySearch(samples, targetUs, compare) {
|
|
213
|
-
let low = 0;
|
|
214
|
-
let high = samples.length - 1;
|
|
215
|
-
while (low <= high) {
|
|
216
|
-
const mid = Math.floor((low + high) / 2);
|
|
217
|
-
const sample = samples[mid];
|
|
218
|
-
if (!sample) {
|
|
219
|
-
break;
|
|
220
|
-
}
|
|
221
|
-
const cmp = compare(sample, targetUs);
|
|
222
|
-
if (cmp === 0) {
|
|
223
|
-
return mid;
|
|
224
|
-
}
|
|
225
|
-
if (cmp < 0) {
|
|
226
|
-
low = mid + 1;
|
|
227
|
-
} else {
|
|
228
|
-
high = mid - 1;
|
|
229
|
-
}
|
|
230
|
-
}
|
|
231
|
-
return ~low;
|
|
232
|
-
}
|
|
233
|
-
resolveSampleByteOffset(mp4boxFile, trackId, sample, sampleIndex) {
|
|
234
|
-
if (typeof sample.position === "number" && Number.isFinite(sample.position)) {
|
|
235
|
-
return sample.position;
|
|
236
|
-
}
|
|
237
|
-
const trak = mp4boxFile.getTrackById?.(trackId);
|
|
238
|
-
if (!trak) {
|
|
239
|
-
return NaN;
|
|
240
|
-
}
|
|
241
|
-
const chunkRefs = trak?.samples;
|
|
242
|
-
const chunkIndex = sample.chunk_index ?? sample.chunk_index_in_trak ?? sample.chunkIndex;
|
|
243
|
-
const chunkOffset = typeof chunkIndex === "number" ? trak?.stco?.chunk_offsets?.[chunkIndex] : void 0;
|
|
244
|
-
if (typeof chunkOffset === "number" && Number.isFinite(chunkOffset)) {
|
|
245
|
-
const offsetInChunk = typeof sample.offset === "number" ? sample.offset : 0;
|
|
246
|
-
return chunkOffset + offsetInChunk;
|
|
247
|
-
}
|
|
248
|
-
if (Array.isArray(chunkRefs) && chunkIndex >= 0 && chunkIndex < chunkRefs.length) {
|
|
249
|
-
const chunk = chunkRefs[chunkIndex];
|
|
250
|
-
if (chunk && typeof chunk.offset === "number") {
|
|
251
|
-
return chunk.offset;
|
|
252
|
-
}
|
|
253
|
-
}
|
|
254
|
-
if (sample.fileStart && typeof sample.fileStart === "number") {
|
|
255
|
-
return sample.fileStart;
|
|
256
|
-
}
|
|
257
|
-
const positions = mp4boxFile.getTrackSamplePositions?.(trackId);
|
|
258
|
-
if (Array.isArray(positions) && sampleIndex < positions.length) {
|
|
259
|
-
const pos = positions[sampleIndex];
|
|
260
|
-
if (typeof pos === "number") {
|
|
261
|
-
return pos;
|
|
262
|
-
}
|
|
263
|
-
}
|
|
264
|
-
return NaN;
|
|
265
|
-
}
|
|
266
|
-
}
|
|
267
|
-
export {
|
|
268
|
-
WindowByteRangeResolver
|
|
269
|
-
};
|
|
270
|
-
//# sourceMappingURL=WindowByteRangeResolver.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"WindowByteRangeResolver.js","sources":["../../../src/stages/load/WindowByteRangeResolver.ts"],"sourcesContent":["import * as MP4Box from 'mp4box';\nimport type { MP4BoxFile } from 'mp4box';\nimport type { Resource } from '../../model';\n\ntype TrackSample = {\n timeUs: number;\n byteOffset: number;\n byteSize: number;\n isKey: boolean;\n};\n\ntype TrackIndex = {\n trackId: number;\n timescale: number;\n samples: TrackSample[];\n};\n\ninterface ResolverOptions {\n maxMoovBytes?: number;\n maxInitialFetchBytes?: number;\n}\n\nexport interface ByteRange {\n byteStart: number;\n byteEnd: number;\n alignedToKeyframe: boolean;\n}\n\ninterface ResolverState {\n resource: Resource;\n status: 'pending' | 'ready' | 'failed';\n promise: Promise<void>;\n trackIndex?: TrackIndex;\n error?: Error;\n}\n\nconst DEFAULT_MAX_MOOV_BYTES = 4 * 1024 * 1024;\nconst DEFAULT_INITIAL_FETCH_BYTES = 1024 * 1024;\n\nexport class WindowByteRangeResolver {\n private maxMoovBytes: number;\n private initialFetchBytes: number;\n private states = new Map<string, ResolverState>();\n\n constructor(options?: ResolverOptions) {\n this.maxMoovBytes = options?.maxMoovBytes ?? DEFAULT_MAX_MOOV_BYTES;\n this.initialFetchBytes = options?.maxInitialFetchBytes ?? DEFAULT_INITIAL_FETCH_BYTES;\n }\n\n ensureReady(resource: Resource): Promise<void> {\n // console.log('>>>>>>>>>>>>>> ensureReady', resource.id, resource.type, resource.state);\n if (!resource.id) {\n return Promise.reject(new Error('Resource id missing'));\n }\n\n const existing = this.states.get(resource.id);\n if (existing) {\n if (existing.status === 'failed') {\n return Promise.reject(existing.error ?? new Error('Resolver failed'));\n }\n return existing.promise;\n }\n\n const promise = this.buildIndex(resource).catch((error) => {\n const state = this.states.get(resource.id);\n if (state) {\n state.status = 'failed';\n state.error = error instanceof Error ? error : new Error(String(error));\n }\n throw error;\n });\n\n this.states.set(resource.id, {\n resource,\n status: 'pending',\n promise,\n });\n\n return promise;\n }\n\n resolveWindow(resourceId: string, startUs: number, endUs: number): ByteRange | null {\n const state = this.states.get(resourceId);\n if (!state || state.status !== 'ready' || !state.trackIndex) {\n return null;\n }\n\n const { samples } = state.trackIndex;\n // console.log('>>>>>>>>>>>>>> resolveWindow samples length', samples[0], state);\n if (!samples.length) {\n return null;\n }\n\n const startSampleIndex = this.findStartSampleIndex(samples, startUs);\n const endSampleIndex = this.findEndSampleIndex(samples, endUs, startSampleIndex);\n\n if (startSampleIndex < 0 || endSampleIndex < 0) {\n return null;\n }\n\n const startSample = samples[startSampleIndex];\n const endSample = samples[endSampleIndex];\n\n if (!startSample || !endSample) {\n return null;\n }\n\n const alignedToKeyframe = startSample.isKey;\n const byteStart = startSample.byteOffset;\n const byteEnd = endSample.byteOffset + endSample.byteSize - 1;\n\n if (Number.isNaN(byteStart) || Number.isNaN(byteEnd) || byteEnd < byteStart) {\n return null;\n }\n\n return {\n byteStart,\n byteEnd,\n alignedToKeyframe,\n };\n }\n\n getStatus(resourceId: string): ResolverState['status'] | undefined {\n return this.states.get(resourceId)?.status;\n }\n\n dispose(): void {\n this.states.clear();\n }\n\n private async buildIndex(resource: Resource): Promise<void> {\n if (!resource.uri) {\n throw new Error(`Resource ${resource.id} missing uri`);\n }\n\n const mp4boxFile = MP4Box.createFile();\n const buffers: ArrayBuffer[] = [];\n let totalFetched = 0;\n let readyInfo: any | undefined;\n let trackIndex: TrackIndex | undefined;\n\n const readyPromise = new Promise<void>((resolve, reject) => {\n mp4boxFile.onReady = (info: any) => {\n readyInfo = info;\n resolve();\n };\n\n mp4boxFile.onError = (error: any) => {\n reject(error instanceof Error ? error : new Error(String(error)));\n };\n });\n\n while (totalFetched < this.maxMoovBytes) {\n const chunkSize = Math.min(\n this.initialFetchBytes * Math.pow(2, buffers.length),\n this.maxMoovBytes - totalFetched\n );\n const rangeStart = totalFetched;\n const rangeEnd = totalFetched + chunkSize - 1;\n\n const response = await fetch(resource.uri, {\n headers: {\n Range: `bytes=${rangeStart}-${rangeEnd}`,\n },\n });\n\n if (!response.ok) {\n throw new Error(`Failed to fetch range ${rangeStart}-${rangeEnd}: HTTP ${response.status}`);\n }\n\n const buffer = await response.arrayBuffer();\n (buffer as ArrayBuffer & { fileStart?: number }).fileStart = rangeStart;\n totalFetched += buffer.byteLength;\n buffers.push(buffer);\n\n mp4boxFile.appendBuffer(buffer);\n\n try {\n await readyPromise;\n break;\n } catch (error) {\n if (totalFetched >= this.maxMoovBytes) {\n throw error;\n }\n }\n }\n\n if (readyInfo) {\n for (const track of readyInfo.tracks ?? []) {\n const extractionOptions: Record<string, unknown> = {\n rapAlignment: false,\n keepSamples: true,\n };\n\n const totalSamples = (track as any)?.samples?.length ?? (track as any)?.sample_count;\n if (typeof totalSamples === 'number' && Number.isFinite(totalSamples) && totalSamples > 0) {\n extractionOptions.nbSamples = totalSamples;\n }\n\n mp4boxFile.setExtractionOptions(track.id, null, extractionOptions);\n }\n\n mp4boxFile.start();\n mp4boxFile.flush();\n\n trackIndex = this.createTrackIndex(mp4boxFile, readyInfo);\n }\n\n if (!trackIndex) {\n throw new Error('Failed to build track index');\n }\n\n const state = this.states.get(resource.id!);\n if (state) {\n state.status = 'ready';\n state.trackIndex = trackIndex;\n }\n }\n\n private createTrackIndex(mp4boxFile: MP4BoxFile, info: any): TrackIndex {\n const videoTrack = info.tracks?.find((track: any) => track.type === 'video');\n if (!videoTrack) {\n throw new Error('No video track found');\n }\n\n const timescale = videoTrack.timescale || 90000;\n const trackId = videoTrack.id;\n let samples = mp4boxFile.getTrackSamples?.(trackId) ?? [];\n\n if (\n (!samples || samples.length === 0) &&\n typeof (mp4boxFile as any).getTrackById === 'function'\n ) {\n const fullTrack = (mp4boxFile as any).getTrackById(trackId);\n if (fullTrack?.samples?.length) {\n samples = fullTrack.samples;\n }\n }\n\n // console.log('>>>>>>>>>>>>>> createTrackIndex samples length', samples[0]);\n\n const indexedSamples: TrackSample[] = (samples ?? []).map((sample: any, index: number) => {\n const sampleByteOffset = this.resolveSampleByteOffset(mp4boxFile, trackId, sample, index);\n const timeUs = Math.round((sample.cts * 1_000_000) / timescale);\n return {\n timeUs,\n byteOffset: sampleByteOffset,\n byteSize: sample.size,\n isKey: Boolean(sample.is_sync),\n };\n });\n\n return {\n trackId,\n timescale,\n samples: indexedSamples,\n };\n }\n\n private findStartSampleIndex(samples: TrackSample[], targetUs: number): number {\n if (!samples.length) {\n return 0;\n }\n\n let index = this.binarySearch(samples, targetUs, (sample, time) => sample.timeUs - time);\n if (index < 0) {\n index = ~index;\n if (index >= samples.length) {\n index = samples.length - 1;\n }\n }\n\n while (index > 0) {\n const current = samples[index];\n if (!current || current.isKey) {\n break;\n }\n index -= 1;\n }\n\n return index;\n }\n\n private findEndSampleIndex(samples: TrackSample[], targetUs: number, startIndex: number): number {\n if (!samples.length) {\n return 0;\n }\n\n let index = Math.min(Math.max(startIndex, 0), samples.length - 1);\n while (index + 1 < samples.length) {\n const nextSample = samples[index + 1];\n if (!nextSample || nextSample.timeUs > targetUs) {\n break;\n }\n index += 1;\n }\n\n return index;\n }\n\n private binarySearch(\n samples: TrackSample[],\n targetUs: number,\n compare: (sample: TrackSample, targetUs: number) => number\n ): number {\n let low = 0;\n let high = samples.length - 1;\n\n while (low <= high) {\n const mid = Math.floor((low + high) / 2);\n const sample = samples[mid];\n if (!sample) {\n break;\n }\n const cmp = compare(sample, targetUs);\n\n if (cmp === 0) {\n return mid;\n }\n if (cmp < 0) {\n low = mid + 1;\n } else {\n high = mid - 1;\n }\n }\n\n return ~low;\n }\n\n private resolveSampleByteOffset(\n mp4boxFile: MP4BoxFile,\n trackId: number,\n sample: any,\n sampleIndex: number\n ): number {\n if (typeof sample.position === 'number' && Number.isFinite(sample.position)) {\n return sample.position;\n }\n\n const trak = (mp4boxFile as any).getTrackById?.(trackId);\n if (!trak) {\n return NaN;\n }\n\n const chunkRefs = trak?.samples as any[] | undefined;\n const chunkIndex = sample.chunk_index ?? sample.chunk_index_in_trak ?? sample.chunkIndex;\n const chunkOffset =\n typeof chunkIndex === 'number' ? trak?.stco?.chunk_offsets?.[chunkIndex] : undefined;\n\n if (typeof chunkOffset === 'number' && Number.isFinite(chunkOffset)) {\n const offsetInChunk = typeof sample.offset === 'number' ? sample.offset : 0;\n return chunkOffset + offsetInChunk;\n }\n\n if (Array.isArray(chunkRefs) && chunkIndex >= 0 && chunkIndex < chunkRefs.length) {\n const chunk = chunkRefs[chunkIndex];\n if (chunk && typeof chunk.offset === 'number') {\n return chunk.offset;\n }\n }\n\n if (sample.fileStart && typeof sample.fileStart === 'number') {\n return sample.fileStart;\n }\n\n const positions = (mp4boxFile as any).getTrackSamplePositions?.(trackId);\n if (Array.isArray(positions) && sampleIndex < positions.length) {\n const pos = positions[sampleIndex];\n if (typeof pos === 'number') {\n return pos;\n }\n }\n\n return NaN;\n }\n}\n"],"names":["MP4Box.createFile"],"mappings":"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"}
|