@meframe/core 0.0.30-beta → 0.0.31

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.
Files changed (79) hide show
  1. package/dist/Meframe.d.ts +0 -17
  2. package/dist/Meframe.d.ts.map +1 -1
  3. package/dist/Meframe.js +0 -18
  4. package/dist/Meframe.js.map +1 -1
  5. package/dist/_virtual/_commonjsHelpers.js +7 -0
  6. package/dist/_virtual/_commonjsHelpers.js.map +1 -0
  7. package/dist/cache/CacheManager.d.ts +7 -49
  8. package/dist/cache/CacheManager.d.ts.map +1 -1
  9. package/dist/cache/CacheManager.js +3 -57
  10. package/dist/cache/CacheManager.js.map +1 -1
  11. package/dist/cache/resource/ResourceCache.d.ts +2 -2
  12. package/dist/cache/resource/ResourceCache.d.ts.map +1 -1
  13. package/dist/cache/resource/ResourceCache.js.map +1 -1
  14. package/dist/controllers/PlaybackController.d.ts +2 -1
  15. package/dist/controllers/PlaybackController.d.ts.map +1 -1
  16. package/dist/controllers/PlaybackController.js +21 -5
  17. package/dist/controllers/PlaybackController.js.map +1 -1
  18. package/dist/medeo-fe/node_modules/.pnpm/mp4-muxer@5.2.2/node_modules/mp4-muxer/build/mp4-muxer.js.map +1 -0
  19. package/dist/{node_modules → medeo-fe/node_modules}/.pnpm/mp4box@0.5.4/node_modules/mp4box/dist/mp4box.all.js +7 -2
  20. package/dist/medeo-fe/node_modules/.pnpm/mp4box@0.5.4/node_modules/mp4box/dist/mp4box.all.js.map +1 -0
  21. package/dist/model/types.d.ts +0 -1
  22. package/dist/model/types.d.ts.map +1 -1
  23. package/dist/model/types.js.map +1 -1
  24. package/dist/orchestrator/GlobalAudioSession.d.ts +3 -2
  25. package/dist/orchestrator/GlobalAudioSession.d.ts.map +1 -1
  26. package/dist/orchestrator/GlobalAudioSession.js +18 -13
  27. package/dist/orchestrator/GlobalAudioSession.js.map +1 -1
  28. package/dist/orchestrator/Orchestrator.d.ts.map +1 -1
  29. package/dist/orchestrator/Orchestrator.js +15 -17
  30. package/dist/orchestrator/Orchestrator.js.map +1 -1
  31. package/dist/stages/compose/VideoComposer.d.ts.map +1 -1
  32. package/dist/stages/compose/VideoComposer.js +4 -0
  33. package/dist/stages/compose/VideoComposer.js.map +1 -1
  34. package/dist/stages/demux/MP4Demuxer.js +6 -7
  35. package/dist/stages/demux/MP4Demuxer.js.map +1 -1
  36. package/dist/stages/demux/MP4IndexParser.js +3 -4
  37. package/dist/stages/demux/MP4IndexParser.js.map +1 -1
  38. package/dist/stages/load/ResourceLoader.d.ts +6 -14
  39. package/dist/stages/load/ResourceLoader.d.ts.map +1 -1
  40. package/dist/stages/load/ResourceLoader.js +37 -68
  41. package/dist/stages/load/ResourceLoader.js.map +1 -1
  42. package/dist/stages/load/index.d.ts +0 -2
  43. package/dist/stages/load/index.d.ts.map +1 -1
  44. package/dist/stages/load/types.d.ts +3 -10
  45. package/dist/stages/load/types.d.ts.map +1 -1
  46. package/dist/stages/mux/MP4Muxer.js +1 -1
  47. package/dist/utils/mp4box.d.ts +4 -0
  48. package/dist/utils/mp4box.d.ts.map +1 -0
  49. package/dist/utils/mp4box.js +17 -0
  50. package/dist/utils/mp4box.js.map +1 -0
  51. package/dist/workers/{MP4Demuxer.BEa6PLJm.js → MP4Demuxer.DxMpB08B.js} +49 -11
  52. package/dist/workers/MP4Demuxer.DxMpB08B.js.map +1 -0
  53. package/dist/workers/stages/compose/{video-compose.worker.DHQ8B105.js → video-compose.worker.BhpN-lxf.js} +5 -1
  54. package/dist/workers/stages/compose/video-compose.worker.BhpN-lxf.js.map +1 -0
  55. package/dist/workers/stages/demux/{audio-demux.worker._VRQdLdv.js → audio-demux.worker.Fd8sRTYi.js} +2 -2
  56. package/dist/workers/stages/demux/{audio-demux.worker._VRQdLdv.js.map → audio-demux.worker.Fd8sRTYi.js.map} +1 -1
  57. package/dist/workers/stages/demux/{video-demux.worker.CSkxGtmx.js → video-demux.worker.DqFOe12v.js} +2 -2
  58. package/dist/workers/stages/demux/{video-demux.worker.CSkxGtmx.js.map → video-demux.worker.DqFOe12v.js.map} +1 -1
  59. package/dist/workers/worker-manifest.json +3 -3
  60. package/package.json +1 -1
  61. package/dist/cache/l2/L2Cache.js +0 -329
  62. package/dist/cache/l2/L2Cache.js.map +0 -1
  63. package/dist/cache/l2/L2OPFSStore.js +0 -89
  64. package/dist/cache/l2/L2OPFSStore.js.map +0 -1
  65. package/dist/cache/storage/indexeddb/ChunkRecordStore.js +0 -180
  66. package/dist/cache/storage/indexeddb/ChunkRecordStore.js.map +0 -1
  67. package/dist/node_modules/.pnpm/mp4-muxer@5.2.2/node_modules/mp4-muxer/build/mp4-muxer.js.map +0 -1
  68. package/dist/node_modules/.pnpm/mp4box@0.5.4/node_modules/mp4box/dist/mp4box.all.js.map +0 -1
  69. package/dist/stages/load/EventHandlers.d.ts +0 -26
  70. package/dist/stages/load/EventHandlers.d.ts.map +0 -1
  71. package/dist/stages/load/EventHandlers.js +0 -42
  72. package/dist/stages/load/EventHandlers.js.map +0 -1
  73. package/dist/stages/load/WindowByteRangeResolver.d.ts +0 -47
  74. package/dist/stages/load/WindowByteRangeResolver.d.ts.map +0 -1
  75. package/dist/stages/load/WindowByteRangeResolver.js +0 -270
  76. package/dist/stages/load/WindowByteRangeResolver.js.map +0 -1
  77. package/dist/workers/MP4Demuxer.BEa6PLJm.js.map +0 -1
  78. package/dist/workers/stages/compose/video-compose.worker.DHQ8B105.js.map +0 -1
  79. /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,26 +0,0 @@
1
- import { CompositionModel } from '../../model';
2
- import { Orchestrator } from './types';
3
-
4
- /**
5
- * Handles resource events from Orchestrator
6
- */
7
- export declare class EventHandlers {
8
- private orchestrator;
9
- private onResourceCancel;
10
- private onModelSet;
11
- private eventHandlers;
12
- constructor(orchestrator: Orchestrator, onResourceCancel: (resourceId: string) => void, onModelSet: (model: CompositionModel) => void);
13
- /**
14
- * Setup event handlers
15
- */
16
- private setupHandlers;
17
- /**
18
- * Handle resource remove event
19
- */
20
- private handleResourceRemove;
21
- /**
22
- * Clean up event handlers
23
- */
24
- dispose(): void;
25
- }
26
- //# sourceMappingURL=EventHandlers.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"EventHandlers.d.ts","sourceRoot":"","sources":["../../../src/stages/load/EventHandlers.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AACpD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAE5C;;GAEG;AACH,qBAAa,aAAa;IAItB,OAAO,CAAC,YAAY;IACpB,OAAO,CAAC,gBAAgB;IACxB,OAAO,CAAC,UAAU;IALpB,OAAO,CAAC,aAAa,CAA0C;gBAGrD,YAAY,EAAE,YAAY,EAC1B,gBAAgB,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,IAAI,EAC9C,UAAU,EAAE,CAAC,KAAK,EAAE,gBAAgB,KAAK,IAAI;IAKvD;;OAEG;IACH,OAAO,CAAC,aAAa;IAYrB;;OAEG;IACH,OAAO,CAAC,oBAAoB;IAI5B;;OAEG;IACH,OAAO,IAAI,IAAI;CAMhB"}
@@ -1,42 +0,0 @@
1
- import { MeframeEvent } from "../../event/events.js";
2
- class EventHandlers {
3
- constructor(orchestrator, onResourceCancel, onModelSet) {
4
- this.orchestrator = orchestrator;
5
- this.onResourceCancel = onResourceCancel;
6
- this.onModelSet = onModelSet;
7
- this.setupHandlers();
8
- }
9
- eventHandlers = /* @__PURE__ */ new Map();
10
- /**
11
- * Setup event handlers
12
- */
13
- setupHandlers() {
14
- const handlers = {
15
- [MeframeEvent.ResourceRemove]: (event) => this.handleResourceRemove(event),
16
- [MeframeEvent.ModelSet]: (model) => this.onModelSet(model)
17
- };
18
- for (const [event, handler] of Object.entries(handlers)) {
19
- this.orchestrator.on(event, handler);
20
- this.eventHandlers.set(event, handler);
21
- }
22
- }
23
- /**
24
- * Handle resource remove event
25
- */
26
- handleResourceRemove(event) {
27
- this.onResourceCancel(event.resourceId);
28
- }
29
- /**
30
- * Clean up event handlers
31
- */
32
- dispose() {
33
- for (const [event, handler] of this.eventHandlers) {
34
- this.orchestrator.off(event, handler);
35
- }
36
- this.eventHandlers.clear();
37
- }
38
- }
39
- export {
40
- EventHandlers
41
- };
42
- //# sourceMappingURL=EventHandlers.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"EventHandlers.js","sources":["../../../src/stages/load/EventHandlers.ts"],"sourcesContent":["import { MeframeEvent, ResourceEvent } from '../../event/events';\nimport type { CompositionModel } from '../../model';\nimport type { Orchestrator } from './types';\n\n/**\n * Handles resource events from Orchestrator\n */\nexport class EventHandlers {\n private eventHandlers = new Map<string, (data: any) => void>();\n\n constructor(\n private orchestrator: Orchestrator,\n private onResourceCancel: (resourceId: string) => void,\n private onModelSet: (model: CompositionModel) => void\n ) {\n this.setupHandlers();\n }\n\n /**\n * Setup event handlers\n */\n private setupHandlers(): void {\n const handlers = {\n [MeframeEvent.ResourceRemove]: (event: ResourceEvent) => this.handleResourceRemove(event),\n [MeframeEvent.ModelSet]: (model: CompositionModel) => this.onModelSet(model),\n };\n\n for (const [event, handler] of Object.entries(handlers)) {\n this.orchestrator.on(event, handler);\n this.eventHandlers.set(event, handler);\n }\n }\n\n /**\n * Handle resource remove event\n */\n private handleResourceRemove(event: ResourceEvent): void {\n this.onResourceCancel(event.resourceId);\n }\n\n /**\n * Clean up event handlers\n */\n dispose(): void {\n for (const [event, handler] of this.eventHandlers) {\n this.orchestrator.off(event, handler);\n }\n this.eventHandlers.clear();\n }\n}\n"],"names":[],"mappings":";AAOO,MAAM,cAAc;AAAA,EAGzB,YACU,cACA,kBACA,YACR;AAHQ,SAAA,eAAA;AACA,SAAA,mBAAA;AACA,SAAA,aAAA;AAER,SAAK,cAAA;AAAA,EACP;AAAA,EARQ,oCAAoB,IAAA;AAAA;AAAA;AAAA;AAAA,EAapB,gBAAsB;AAC5B,UAAM,WAAW;AAAA,MACf,CAAC,aAAa,cAAc,GAAG,CAAC,UAAyB,KAAK,qBAAqB,KAAK;AAAA,MACxF,CAAC,aAAa,QAAQ,GAAG,CAAC,UAA4B,KAAK,WAAW,KAAK;AAAA,IAAA;AAG7E,eAAW,CAAC,OAAO,OAAO,KAAK,OAAO,QAAQ,QAAQ,GAAG;AACvD,WAAK,aAAa,GAAG,OAAO,OAAO;AACnC,WAAK,cAAc,IAAI,OAAO,OAAO;AAAA,IACvC;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKQ,qBAAqB,OAA4B;AACvD,SAAK,iBAAiB,MAAM,UAAU;AAAA,EACxC;AAAA;AAAA;AAAA;AAAA,EAKA,UAAgB;AACd,eAAW,CAAC,OAAO,OAAO,KAAK,KAAK,eAAe;AACjD,WAAK,aAAa,IAAI,OAAO,OAAO;AAAA,IACtC;AACA,SAAK,cAAc,MAAA;AAAA,EACrB;AACF;"}
@@ -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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"}