@meframe/core 0.0.29 → 0.0.30-beta
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 -13
- package/dist/Meframe.d.ts.map +1 -1
- package/dist/Meframe.js +6 -100
- package/dist/Meframe.js.map +1 -1
- package/dist/cache/CacheManager.d.ts +35 -19
- package/dist/cache/CacheManager.d.ts.map +1 -1
- package/dist/cache/CacheManager.js +223 -134
- package/dist/cache/CacheManager.js.map +1 -1
- package/dist/cache/l1/VideoL1Cache.d.ts +15 -2
- package/dist/cache/l1/VideoL1Cache.d.ts.map +1 -1
- package/dist/cache/l1/VideoL1Cache.js +58 -38
- package/dist/cache/l1/VideoL1Cache.js.map +1 -1
- package/dist/cache/l2/L2Cache.d.ts.map +1 -1
- package/dist/cache/l2/L2Cache.js +5 -5
- package/dist/cache/l2/L2Cache.js.map +1 -1
- package/dist/cache/l2/L2OPFSStore.d.ts +37 -0
- package/dist/cache/l2/L2OPFSStore.d.ts.map +1 -0
- package/dist/cache/l2/L2OPFSStore.js +89 -0
- package/dist/cache/l2/L2OPFSStore.js.map +1 -0
- package/dist/cache/resource/AudioSampleCache.d.ts +52 -0
- package/dist/cache/resource/AudioSampleCache.d.ts.map +1 -0
- package/dist/cache/resource/AudioSampleCache.js +69 -0
- package/dist/cache/resource/AudioSampleCache.js.map +1 -0
- package/dist/cache/resource/ImageBitmapCache.d.ts +65 -0
- package/dist/cache/resource/ImageBitmapCache.d.ts.map +1 -0
- package/dist/cache/resource/ImageBitmapCache.js +101 -0
- package/dist/cache/resource/ImageBitmapCache.js.map +1 -0
- package/dist/cache/resource/MP4IndexCache.d.ts +48 -0
- package/dist/cache/resource/MP4IndexCache.d.ts.map +1 -0
- package/dist/cache/resource/MP4IndexCache.js +104 -0
- package/dist/cache/resource/MP4IndexCache.js.map +1 -0
- package/dist/cache/resource/ResourceCache.d.ts +46 -0
- package/dist/cache/resource/ResourceCache.d.ts.map +1 -0
- package/dist/cache/resource/ResourceCache.js +92 -0
- package/dist/cache/resource/ResourceCache.js.map +1 -0
- package/dist/cache/storage/indexeddb/ChunkRecordStore.d.ts +75 -0
- package/dist/cache/storage/indexeddb/ChunkRecordStore.d.ts.map +1 -0
- package/dist/cache/{l2/IndexedDBStore.js → storage/indexeddb/ChunkRecordStore.js} +3 -3
- package/dist/cache/storage/indexeddb/ChunkRecordStore.js.map +1 -0
- package/dist/cache/storage/opfs/OPFSManager.d.ts +54 -0
- package/dist/cache/storage/opfs/OPFSManager.d.ts.map +1 -0
- package/dist/cache/storage/opfs/OPFSManager.js +133 -0
- package/dist/cache/storage/opfs/OPFSManager.js.map +1 -0
- package/dist/cache/storage/opfs/types.d.ts +16 -0
- package/dist/cache/storage/opfs/types.d.ts.map +1 -0
- package/dist/config/defaults.d.ts.map +1 -1
- package/dist/config/defaults.js +21 -2
- package/dist/config/defaults.js.map +1 -1
- package/dist/config/types.d.ts +28 -0
- package/dist/config/types.d.ts.map +1 -1
- package/dist/controllers/ExportController.d.ts +16 -0
- package/dist/controllers/ExportController.d.ts.map +1 -0
- package/dist/controllers/ExportController.js +44 -0
- package/dist/controllers/ExportController.js.map +1 -0
- package/dist/controllers/PlaybackController.d.ts +28 -4
- package/dist/controllers/PlaybackController.d.ts.map +1 -1
- package/dist/controllers/PlaybackController.js +116 -51
- package/dist/controllers/PlaybackController.js.map +1 -1
- package/dist/controllers/index.d.ts +2 -3
- package/dist/controllers/index.d.ts.map +1 -1
- package/dist/controllers/types.d.ts +0 -28
- package/dist/controllers/types.d.ts.map +1 -1
- package/dist/event/events.d.ts +8 -0
- package/dist/event/events.d.ts.map +1 -1
- package/dist/event/events.js +1 -0
- package/dist/event/events.js.map +1 -1
- package/dist/model/CompositionModel.d.ts.map +1 -1
- package/dist/model/CompositionModel.js +11 -6
- package/dist/model/CompositionModel.js.map +1 -1
- package/dist/model/RcFrame.d.ts +2 -0
- package/dist/model/RcFrame.d.ts.map +1 -1
- package/dist/model/RcFrame.js +3 -0
- package/dist/model/RcFrame.js.map +1 -1
- package/dist/orchestrator/ExportScheduler.d.ts +35 -0
- package/dist/orchestrator/ExportScheduler.d.ts.map +1 -0
- package/dist/orchestrator/ExportScheduler.js +241 -0
- package/dist/orchestrator/ExportScheduler.js.map +1 -0
- package/dist/orchestrator/GlobalAudioSession.d.ts +21 -7
- package/dist/orchestrator/GlobalAudioSession.d.ts.map +1 -1
- package/dist/orchestrator/GlobalAudioSession.js +132 -140
- package/dist/orchestrator/GlobalAudioSession.js.map +1 -1
- package/dist/orchestrator/OnDemandVideoSession.d.ts +73 -0
- package/dist/orchestrator/OnDemandVideoSession.d.ts.map +1 -0
- package/dist/orchestrator/OnDemandVideoSession.js +281 -0
- package/dist/orchestrator/OnDemandVideoSession.js.map +1 -0
- package/dist/orchestrator/Orchestrator.d.ts +22 -17
- package/dist/orchestrator/Orchestrator.d.ts.map +1 -1
- package/dist/orchestrator/Orchestrator.js +231 -297
- package/dist/orchestrator/Orchestrator.js.map +1 -1
- package/dist/orchestrator/VideoClipSession.d.ts.map +1 -1
- package/dist/orchestrator/VideoClipSession.js +3 -15
- package/dist/orchestrator/VideoClipSession.js.map +1 -1
- package/dist/orchestrator/index.d.ts +0 -1
- package/dist/orchestrator/index.d.ts.map +1 -1
- package/dist/orchestrator/types.d.ts +4 -4
- package/dist/orchestrator/types.d.ts.map +1 -1
- package/dist/stages/compose/FilterProcessor.d.ts +1 -1
- package/dist/stages/compose/FilterProcessor.d.ts.map +1 -1
- package/dist/stages/compose/FilterProcessor.js +226 -0
- package/dist/stages/compose/FilterProcessor.js.map +1 -0
- package/dist/stages/compose/LayerRenderer.d.ts +1 -1
- package/dist/stages/compose/LayerRenderer.d.ts.map +1 -1
- package/dist/stages/compose/LayerRenderer.js +270 -0
- package/dist/stages/compose/LayerRenderer.js.map +1 -0
- package/dist/stages/compose/TransitionProcessor.d.ts +1 -1
- package/dist/stages/compose/TransitionProcessor.d.ts.map +1 -1
- package/dist/stages/compose/TransitionProcessor.js +189 -0
- package/dist/stages/compose/TransitionProcessor.js.map +1 -0
- package/dist/stages/compose/VideoComposer.d.ts +4 -2
- package/dist/stages/compose/VideoComposer.d.ts.map +1 -1
- package/dist/stages/compose/VideoComposer.js +229 -0
- package/dist/stages/compose/VideoComposer.js.map +1 -0
- package/dist/stages/compose/text-renderers/animation-utils.js +76 -0
- package/dist/stages/compose/text-renderers/animation-utils.js.map +1 -0
- package/dist/stages/compose/text-renderers/basic-text-renderer.d.ts +2 -2
- package/dist/stages/compose/text-renderers/basic-text-renderer.d.ts.map +1 -1
- package/dist/stages/compose/text-renderers/basic-text-renderer.js +93 -0
- package/dist/stages/compose/text-renderers/basic-text-renderer.js.map +1 -0
- package/dist/stages/compose/text-renderers/character-ktv-renderer.d.ts +1 -1
- package/dist/stages/compose/text-renderers/character-ktv-renderer.d.ts.map +1 -1
- package/dist/stages/compose/text-renderers/character-ktv-renderer.js +132 -0
- package/dist/stages/compose/text-renderers/character-ktv-renderer.js.map +1 -0
- package/dist/stages/compose/text-renderers/word-by-word-renderer.d.ts +1 -1
- package/dist/stages/compose/text-renderers/word-by-word-renderer.d.ts.map +1 -1
- package/dist/stages/compose/text-renderers/word-by-word-renderer.js +128 -0
- package/dist/stages/compose/text-renderers/word-by-word-renderer.js.map +1 -0
- package/dist/stages/compose/text-renderers/word-fancy-renderer.d.ts +1 -1
- package/dist/stages/compose/text-renderers/word-fancy-renderer.d.ts.map +1 -1
- package/dist/stages/compose/text-renderers/word-fancy-renderer.js +135 -0
- package/dist/stages/compose/text-renderers/word-fancy-renderer.js.map +1 -0
- package/dist/stages/compose/text-utils/locale-detector.js +16 -0
- package/dist/stages/compose/text-utils/locale-detector.js.map +1 -0
- package/dist/stages/compose/text-utils/text-metrics.js +21 -0
- package/dist/stages/compose/text-utils/text-metrics.js.map +1 -0
- package/dist/stages/compose/text-utils/text-wrapper.js +225 -0
- package/dist/stages/compose/text-utils/text-wrapper.js.map +1 -0
- package/dist/stages/compose/types.d.ts +2 -1
- package/dist/stages/compose/types.d.ts.map +1 -1
- package/dist/stages/decode/BaseDecoder.js +0 -3
- package/dist/stages/decode/BaseDecoder.js.map +1 -1
- package/dist/stages/demux/MP4Demuxer.d.ts +5 -0
- package/dist/stages/demux/MP4Demuxer.d.ts.map +1 -1
- package/dist/stages/demux/MP4Demuxer.js +281 -0
- package/dist/stages/demux/MP4Demuxer.js.map +1 -0
- package/dist/stages/demux/MP4IndexParser.d.ts +71 -0
- package/dist/stages/demux/MP4IndexParser.d.ts.map +1 -0
- package/dist/stages/demux/MP4IndexParser.js +416 -0
- package/dist/stages/demux/MP4IndexParser.js.map +1 -0
- package/dist/stages/demux/types.d.ts +48 -0
- package/dist/stages/demux/types.d.ts.map +1 -1
- package/dist/stages/load/ResourceLoader.d.ts +44 -2
- package/dist/stages/load/ResourceLoader.d.ts.map +1 -1
- package/dist/stages/load/ResourceLoader.js +281 -37
- package/dist/stages/load/ResourceLoader.js.map +1 -1
- package/dist/stages/load/TaskManager.d.ts +6 -2
- package/dist/stages/load/TaskManager.d.ts.map +1 -1
- package/dist/stages/load/TaskManager.js +27 -4
- package/dist/stages/load/TaskManager.js.map +1 -1
- package/dist/stages/load/types.d.ts +7 -0
- package/dist/stages/load/types.d.ts.map +1 -1
- package/dist/stages/mux/MP4Muxer.d.ts +2 -2
- package/dist/stages/mux/MP4Muxer.d.ts.map +1 -1
- package/dist/stages/mux/MP4Muxer.js +24 -13
- package/dist/stages/mux/MP4Muxer.js.map +1 -1
- package/dist/stages/mux/MuxManager.d.ts +10 -21
- package/dist/stages/mux/MuxManager.d.ts.map +1 -1
- package/dist/stages/mux/MuxManager.js +21 -162
- package/dist/stages/mux/MuxManager.js.map +1 -1
- package/dist/stages/mux/index.d.ts +0 -1
- package/dist/stages/mux/index.d.ts.map +1 -1
- package/dist/utils/binary-search.d.ts +12 -4
- package/dist/utils/binary-search.d.ts.map +1 -1
- package/dist/utils/binary-search.js +52 -6
- package/dist/utils/binary-search.js.map +1 -1
- package/dist/workers/{BaseDecoder.BWYu1W0B.js → BaseDecoder.CTW-vr29.js} +1 -4
- package/dist/workers/BaseDecoder.CTW-vr29.js.map +1 -0
- package/dist/workers/{MP4Demuxer.lMOUMWFh.js → MP4Demuxer.BEa6PLJm.js} +9 -2
- package/dist/workers/{MP4Demuxer.lMOUMWFh.js.map → MP4Demuxer.BEa6PLJm.js.map} +1 -1
- package/dist/workers/stages/compose/{video-compose.worker.CIeEIJO7.js → video-compose.worker.DHQ8B105.js} +59 -31
- package/dist/workers/stages/compose/video-compose.worker.DHQ8B105.js.map +1 -0
- package/dist/workers/stages/decode/{audio-decode.worker.DnS17GD9.js → audio-decode.worker.CP8bXXa4.js} +2 -2
- package/dist/workers/stages/decode/{audio-decode.worker.DnS17GD9.js.map → audio-decode.worker.CP8bXXa4.js.map} +1 -1
- package/dist/workers/stages/decode/{video-decode.worker.BEYsjOXp.js → video-decode.worker.BIspTxgV.js} +2 -2
- package/dist/workers/stages/decode/{video-decode.worker.BEYsjOXp.js.map → video-decode.worker.BIspTxgV.js.map} +1 -1
- package/dist/workers/stages/demux/{audio-demux.worker.DcurGC8i.js → audio-demux.worker._VRQdLdv.js} +2 -2
- package/dist/workers/stages/demux/{audio-demux.worker.DcurGC8i.js.map → audio-demux.worker._VRQdLdv.js.map} +1 -1
- package/dist/workers/stages/demux/{video-demux.worker.B1_wntU4.js → video-demux.worker.CSkxGtmx.js} +3 -19
- package/dist/workers/stages/demux/video-demux.worker.CSkxGtmx.js.map +1 -0
- package/dist/workers/worker-manifest.json +5 -5
- package/package.json +1 -1
- package/dist/cache/l2/IndexedDBStore.js.map +0 -1
- package/dist/cache/l2/OPFSStore.js +0 -131
- package/dist/cache/l2/OPFSStore.js.map +0 -1
- package/dist/controllers/PreRenderService.d.ts +0 -59
- package/dist/controllers/PreRenderService.d.ts.map +0 -1
- package/dist/controllers/PreRenderService.js +0 -185
- package/dist/controllers/PreRenderService.js.map +0 -1
- package/dist/controllers/PreRenderTaskQueue.d.ts +0 -21
- package/dist/controllers/PreRenderTaskQueue.d.ts.map +0 -1
- package/dist/orchestrator/ClipSessionManager.d.ts +0 -70
- package/dist/orchestrator/ClipSessionManager.d.ts.map +0 -1
- package/dist/orchestrator/ClipSessionManager.js +0 -158
- package/dist/orchestrator/ClipSessionManager.js.map +0 -1
- package/dist/stages/decode/AudioChunkDecoder.js +0 -169
- package/dist/stages/decode/AudioChunkDecoder.js.map +0 -1
- package/dist/stages/mux/OPFSWriter.d.ts +0 -46
- package/dist/stages/mux/OPFSWriter.d.ts.map +0 -1
- package/dist/utils/BackpressureAdapter.d.ts +0 -26
- package/dist/utils/BackpressureAdapter.d.ts.map +0 -1
- package/dist/workers/BaseDecoder.BWYu1W0B.js.map +0 -1
- package/dist/workers/stages/compose/video-compose.worker.CIeEIJO7.js.map +0 -1
- package/dist/workers/stages/demux/video-demux.worker.B1_wntU4.js.map +0 -1
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
export interface ImageBitmapRecord {
|
|
2
|
+
resourceId: string;
|
|
3
|
+
bitmap: ImageBitmap;
|
|
4
|
+
width: number;
|
|
5
|
+
height: number;
|
|
6
|
+
sizeBytes: number;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* ImageBitmapCache - Resource-level image bitmap cache
|
|
10
|
+
*
|
|
11
|
+
* Caches decoded ImageBitmap for image resources in main thread
|
|
12
|
+
* Used by OnDemandComposeSession for image clip composition
|
|
13
|
+
*
|
|
14
|
+
* Strategy:
|
|
15
|
+
* - Store ImageBitmap after ResourceLoader downloads image
|
|
16
|
+
* - Keep in main thread memory for direct access
|
|
17
|
+
* - Close and evict when memory pressure is high
|
|
18
|
+
*
|
|
19
|
+
* Memory usage:
|
|
20
|
+
* - Typical image (1080p): width * height * 4 bytes = ~8MB
|
|
21
|
+
* - Cache ~10 images max to stay under ~100MB
|
|
22
|
+
*/
|
|
23
|
+
export declare class ImageBitmapCache {
|
|
24
|
+
private cache;
|
|
25
|
+
private maxCacheSizeMB;
|
|
26
|
+
private currentSizeBytes;
|
|
27
|
+
constructor(maxCacheSizeMB?: number);
|
|
28
|
+
/**
|
|
29
|
+
* Set image bitmap for a resource
|
|
30
|
+
*/
|
|
31
|
+
set(resourceId: string, bitmap: ImageBitmap): void;
|
|
32
|
+
/**
|
|
33
|
+
* Get image bitmap for a resource
|
|
34
|
+
*/
|
|
35
|
+
get(resourceId: string): ImageBitmap | null;
|
|
36
|
+
/**
|
|
37
|
+
* Get full record with metadata
|
|
38
|
+
*/
|
|
39
|
+
getRecord(resourceId: string): ImageBitmapRecord | null;
|
|
40
|
+
/**
|
|
41
|
+
* Check if resource has bitmap cached
|
|
42
|
+
*/
|
|
43
|
+
has(resourceId: string): boolean;
|
|
44
|
+
/**
|
|
45
|
+
* Delete bitmap for a resource
|
|
46
|
+
*/
|
|
47
|
+
delete(resourceId: string): void;
|
|
48
|
+
/**
|
|
49
|
+
* Clear all cached bitmaps
|
|
50
|
+
*/
|
|
51
|
+
clear(): void;
|
|
52
|
+
/**
|
|
53
|
+
* Get total memory usage in bytes
|
|
54
|
+
*/
|
|
55
|
+
getTotalBytes(): number;
|
|
56
|
+
/**
|
|
57
|
+
* Get total memory usage in MB
|
|
58
|
+
*/
|
|
59
|
+
getTotalMB(): number;
|
|
60
|
+
/**
|
|
61
|
+
* Evict oldest entries if over size limit
|
|
62
|
+
*/
|
|
63
|
+
private evictIfNeeded;
|
|
64
|
+
}
|
|
65
|
+
//# sourceMappingURL=ImageBitmapCache.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ImageBitmapCache.d.ts","sourceRoot":"","sources":["../../../src/cache/resource/ImageBitmapCache.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,iBAAiB;IAChC,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,WAAW,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;;;;;;;;;;;;;GAcG;AACH,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,KAAK,CAAwC;IACrD,OAAO,CAAC,cAAc,CAAO;IAC7B,OAAO,CAAC,gBAAgB,CAAK;gBAEjB,cAAc,SAAM;IAIhC;;OAEG;IACH,GAAG,CAAC,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,GAAG,IAAI;IA0BlD;;OAEG;IACH,GAAG,CAAC,UAAU,EAAE,MAAM,GAAG,WAAW,GAAG,IAAI;IAK3C;;OAEG;IACH,SAAS,CAAC,UAAU,EAAE,MAAM,GAAG,iBAAiB,GAAG,IAAI;IAIvD;;OAEG;IACH,GAAG,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO;IAIhC;;OAEG;IACH,MAAM,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI;IAShC;;OAEG;IACH,KAAK,IAAI,IAAI;IAQb;;OAEG;IACH,aAAa,IAAI,MAAM;IAIvB;;OAEG;IACH,UAAU,IAAI,MAAM;IAIpB;;OAEG;IACH,OAAO,CAAC,aAAa;CAetB"}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
class ImageBitmapCache {
|
|
2
|
+
cache = /* @__PURE__ */ new Map();
|
|
3
|
+
maxCacheSizeMB = 100;
|
|
4
|
+
// Default 100MB
|
|
5
|
+
currentSizeBytes = 0;
|
|
6
|
+
constructor(maxCacheSizeMB = 100) {
|
|
7
|
+
this.maxCacheSizeMB = maxCacheSizeMB;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Set image bitmap for a resource
|
|
11
|
+
*/
|
|
12
|
+
set(resourceId, bitmap) {
|
|
13
|
+
const existing = this.cache.get(resourceId);
|
|
14
|
+
if (existing) {
|
|
15
|
+
existing.bitmap.close();
|
|
16
|
+
this.currentSizeBytes -= existing.sizeBytes;
|
|
17
|
+
}
|
|
18
|
+
const sizeBytes = bitmap.width * bitmap.height * 4;
|
|
19
|
+
const record = {
|
|
20
|
+
resourceId,
|
|
21
|
+
bitmap,
|
|
22
|
+
width: bitmap.width,
|
|
23
|
+
height: bitmap.height,
|
|
24
|
+
sizeBytes
|
|
25
|
+
};
|
|
26
|
+
this.cache.set(resourceId, record);
|
|
27
|
+
this.currentSizeBytes += sizeBytes;
|
|
28
|
+
this.evictIfNeeded();
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Get image bitmap for a resource
|
|
32
|
+
*/
|
|
33
|
+
get(resourceId) {
|
|
34
|
+
const record = this.cache.get(resourceId);
|
|
35
|
+
return record ? record.bitmap : null;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Get full record with metadata
|
|
39
|
+
*/
|
|
40
|
+
getRecord(resourceId) {
|
|
41
|
+
return this.cache.get(resourceId) || null;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Check if resource has bitmap cached
|
|
45
|
+
*/
|
|
46
|
+
has(resourceId) {
|
|
47
|
+
return this.cache.has(resourceId);
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Delete bitmap for a resource
|
|
51
|
+
*/
|
|
52
|
+
delete(resourceId) {
|
|
53
|
+
const record = this.cache.get(resourceId);
|
|
54
|
+
if (record) {
|
|
55
|
+
record.bitmap.close();
|
|
56
|
+
this.currentSizeBytes -= record.sizeBytes;
|
|
57
|
+
this.cache.delete(resourceId);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Clear all cached bitmaps
|
|
62
|
+
*/
|
|
63
|
+
clear() {
|
|
64
|
+
for (const record of this.cache.values()) {
|
|
65
|
+
record.bitmap.close();
|
|
66
|
+
}
|
|
67
|
+
this.cache.clear();
|
|
68
|
+
this.currentSizeBytes = 0;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Get total memory usage in bytes
|
|
72
|
+
*/
|
|
73
|
+
getTotalBytes() {
|
|
74
|
+
return this.currentSizeBytes;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Get total memory usage in MB
|
|
78
|
+
*/
|
|
79
|
+
getTotalMB() {
|
|
80
|
+
return this.currentSizeBytes / (1024 * 1024);
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Evict oldest entries if over size limit
|
|
84
|
+
*/
|
|
85
|
+
evictIfNeeded() {
|
|
86
|
+
const maxBytes = this.maxCacheSizeMB * 1024 * 1024;
|
|
87
|
+
if (this.currentSizeBytes > maxBytes) {
|
|
88
|
+
const entries = Array.from(this.cache.entries());
|
|
89
|
+
let evictIndex = 0;
|
|
90
|
+
while (this.currentSizeBytes > maxBytes && evictIndex < entries.length) {
|
|
91
|
+
const [resourceId] = entries[evictIndex];
|
|
92
|
+
this.delete(resourceId);
|
|
93
|
+
evictIndex++;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
export {
|
|
99
|
+
ImageBitmapCache
|
|
100
|
+
};
|
|
101
|
+
//# sourceMappingURL=ImageBitmapCache.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ImageBitmapCache.js","sources":["../../../src/cache/resource/ImageBitmapCache.ts"],"sourcesContent":["export interface ImageBitmapRecord {\n resourceId: string;\n bitmap: ImageBitmap;\n width: number;\n height: number;\n sizeBytes: number; // Estimated memory usage\n}\n\n/**\n * ImageBitmapCache - Resource-level image bitmap cache\n *\n * Caches decoded ImageBitmap for image resources in main thread\n * Used by OnDemandComposeSession for image clip composition\n *\n * Strategy:\n * - Store ImageBitmap after ResourceLoader downloads image\n * - Keep in main thread memory for direct access\n * - Close and evict when memory pressure is high\n *\n * Memory usage:\n * - Typical image (1080p): width * height * 4 bytes = ~8MB\n * - Cache ~10 images max to stay under ~100MB\n */\nexport class ImageBitmapCache {\n private cache = new Map<string, ImageBitmapRecord>();\n private maxCacheSizeMB = 100; // Default 100MB\n private currentSizeBytes = 0;\n\n constructor(maxCacheSizeMB = 100) {\n this.maxCacheSizeMB = maxCacheSizeMB;\n }\n\n /**\n * Set image bitmap for a resource\n */\n set(resourceId: string, bitmap: ImageBitmap): void {\n // Close existing bitmap if present\n const existing = this.cache.get(resourceId);\n if (existing) {\n existing.bitmap.close();\n this.currentSizeBytes -= existing.sizeBytes;\n }\n\n // Estimate memory usage (RGBA format)\n const sizeBytes = bitmap.width * bitmap.height * 4;\n\n const record: ImageBitmapRecord = {\n resourceId,\n bitmap,\n width: bitmap.width,\n height: bitmap.height,\n sizeBytes,\n };\n\n this.cache.set(resourceId, record);\n this.currentSizeBytes += sizeBytes;\n\n // Evict if over limit (simple FIFO for now)\n this.evictIfNeeded();\n }\n\n /**\n * Get image bitmap for a resource\n */\n get(resourceId: string): ImageBitmap | null {\n const record = this.cache.get(resourceId);\n return record ? record.bitmap : null;\n }\n\n /**\n * Get full record with metadata\n */\n getRecord(resourceId: string): ImageBitmapRecord | null {\n return this.cache.get(resourceId) || null;\n }\n\n /**\n * Check if resource has bitmap cached\n */\n has(resourceId: string): boolean {\n return this.cache.has(resourceId);\n }\n\n /**\n * Delete bitmap for a resource\n */\n delete(resourceId: string): void {\n const record = this.cache.get(resourceId);\n if (record) {\n record.bitmap.close();\n this.currentSizeBytes -= record.sizeBytes;\n this.cache.delete(resourceId);\n }\n }\n\n /**\n * Clear all cached bitmaps\n */\n clear(): void {\n for (const record of this.cache.values()) {\n record.bitmap.close();\n }\n this.cache.clear();\n this.currentSizeBytes = 0;\n }\n\n /**\n * Get total memory usage in bytes\n */\n getTotalBytes(): number {\n return this.currentSizeBytes;\n }\n\n /**\n * Get total memory usage in MB\n */\n getTotalMB(): number {\n return this.currentSizeBytes / (1024 * 1024);\n }\n\n /**\n * Evict oldest entries if over size limit\n */\n private evictIfNeeded(): void {\n const maxBytes = this.maxCacheSizeMB * 1024 * 1024;\n\n // Simple FIFO eviction: remove oldest entries until under limit\n if (this.currentSizeBytes > maxBytes) {\n const entries = Array.from(this.cache.entries());\n let evictIndex = 0;\n\n while (this.currentSizeBytes > maxBytes && evictIndex < entries.length) {\n const [resourceId] = entries[evictIndex]!;\n this.delete(resourceId);\n evictIndex++;\n }\n }\n }\n}\n"],"names":[],"mappings":"AAuBO,MAAM,iBAAiB;AAAA,EACpB,4BAAY,IAAA;AAAA,EACZ,iBAAiB;AAAA;AAAA,EACjB,mBAAmB;AAAA,EAE3B,YAAY,iBAAiB,KAAK;AAChC,SAAK,iBAAiB;AAAA,EACxB;AAAA;AAAA;AAAA;AAAA,EAKA,IAAI,YAAoB,QAA2B;AAEjD,UAAM,WAAW,KAAK,MAAM,IAAI,UAAU;AAC1C,QAAI,UAAU;AACZ,eAAS,OAAO,MAAA;AAChB,WAAK,oBAAoB,SAAS;AAAA,IACpC;AAGA,UAAM,YAAY,OAAO,QAAQ,OAAO,SAAS;AAEjD,UAAM,SAA4B;AAAA,MAChC;AAAA,MACA;AAAA,MACA,OAAO,OAAO;AAAA,MACd,QAAQ,OAAO;AAAA,MACf;AAAA,IAAA;AAGF,SAAK,MAAM,IAAI,YAAY,MAAM;AACjC,SAAK,oBAAoB;AAGzB,SAAK,cAAA;AAAA,EACP;AAAA;AAAA;AAAA;AAAA,EAKA,IAAI,YAAwC;AAC1C,UAAM,SAAS,KAAK,MAAM,IAAI,UAAU;AACxC,WAAO,SAAS,OAAO,SAAS;AAAA,EAClC;AAAA;AAAA;AAAA;AAAA,EAKA,UAAU,YAA8C;AACtD,WAAO,KAAK,MAAM,IAAI,UAAU,KAAK;AAAA,EACvC;AAAA;AAAA;AAAA;AAAA,EAKA,IAAI,YAA6B;AAC/B,WAAO,KAAK,MAAM,IAAI,UAAU;AAAA,EAClC;AAAA;AAAA;AAAA;AAAA,EAKA,OAAO,YAA0B;AAC/B,UAAM,SAAS,KAAK,MAAM,IAAI,UAAU;AACxC,QAAI,QAAQ;AACV,aAAO,OAAO,MAAA;AACd,WAAK,oBAAoB,OAAO;AAChC,WAAK,MAAM,OAAO,UAAU;AAAA,IAC9B;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,QAAc;AACZ,eAAW,UAAU,KAAK,MAAM,OAAA,GAAU;AACxC,aAAO,OAAO,MAAA;AAAA,IAChB;AACA,SAAK,MAAM,MAAA;AACX,SAAK,mBAAmB;AAAA,EAC1B;AAAA;AAAA;AAAA;AAAA,EAKA,gBAAwB;AACtB,WAAO,KAAK;AAAA,EACd;AAAA;AAAA;AAAA;AAAA,EAKA,aAAqB;AACnB,WAAO,KAAK,oBAAoB,OAAO;AAAA,EACzC;AAAA;AAAA;AAAA;AAAA,EAKQ,gBAAsB;AAC5B,UAAM,WAAW,KAAK,iBAAiB,OAAO;AAG9C,QAAI,KAAK,mBAAmB,UAAU;AACpC,YAAM,UAAU,MAAM,KAAK,KAAK,MAAM,SAAS;AAC/C,UAAI,aAAa;AAEjB,aAAO,KAAK,mBAAmB,YAAY,aAAa,QAAQ,QAAQ;AACtE,cAAM,CAAC,UAAU,IAAI,QAAQ,UAAU;AACvC,aAAK,OAAO,UAAU;AACtB;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF;"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { MP4Index, Sample, GOPRange } from '../../stages/demux/types';
|
|
2
|
+
import { TimeUs } from '../../model/types';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* MP4IndexCache - In-memory cache for MP4 indexes
|
|
6
|
+
*
|
|
7
|
+
* Provides fast lookup for:
|
|
8
|
+
* - Sample at specific timestamp
|
|
9
|
+
* - GOP range for time range
|
|
10
|
+
* - Nearest keyframe
|
|
11
|
+
*/
|
|
12
|
+
export declare class MP4IndexCache {
|
|
13
|
+
private indexes;
|
|
14
|
+
/**
|
|
15
|
+
* Set index for a resource
|
|
16
|
+
*/
|
|
17
|
+
set(resourceId: string, index: MP4Index): void;
|
|
18
|
+
/**
|
|
19
|
+
* Get index for a resource
|
|
20
|
+
*/
|
|
21
|
+
get(resourceId: string): MP4Index | null;
|
|
22
|
+
/**
|
|
23
|
+
* Check if index exists for a resource
|
|
24
|
+
*/
|
|
25
|
+
has(resourceId: string): boolean;
|
|
26
|
+
/**
|
|
27
|
+
* Get GOP range (byte range) for a specific time
|
|
28
|
+
* Returns the GOP that contains the target time
|
|
29
|
+
*/
|
|
30
|
+
getGOPRangeForTime(resourceId: string, timeUs: TimeUs): GOPRange | null;
|
|
31
|
+
/**
|
|
32
|
+
* Get sample at specific timestamp
|
|
33
|
+
*/
|
|
34
|
+
getSampleAtTime(resourceId: string, timeUs: TimeUs): Sample | null;
|
|
35
|
+
/**
|
|
36
|
+
* Get nearest keyframe before or at the target time
|
|
37
|
+
*/
|
|
38
|
+
getNearestKeyframe(resourceId: string, timeUs: TimeUs): Sample | null;
|
|
39
|
+
/**
|
|
40
|
+
* Clear all cached indexes
|
|
41
|
+
*/
|
|
42
|
+
clear(): void;
|
|
43
|
+
/**
|
|
44
|
+
* Remove index for a specific resource
|
|
45
|
+
*/
|
|
46
|
+
remove(resourceId: string): void;
|
|
47
|
+
}
|
|
48
|
+
//# sourceMappingURL=MP4IndexCache.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MP4IndexCache.d.ts","sourceRoot":"","sources":["../../../src/cache/resource/MP4IndexCache.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,0BAA0B,CAAC;AAC3E,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAGhD;;;;;;;GAOG;AACH,qBAAa,aAAa;IACxB,OAAO,CAAC,OAAO,CAA+B;IAE9C;;OAEG;IACH,GAAG,CAAC,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,QAAQ,GAAG,IAAI;IAI9C;;OAEG;IACH,GAAG,CAAC,UAAU,EAAE,MAAM,GAAG,QAAQ,GAAG,IAAI;IAIxC;;OAEG;IACH,GAAG,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO;IAIhC;;;OAGG;IACH,kBAAkB,CAAC,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,QAAQ,GAAG,IAAI;IA0CvE;;OAEG;IACH,eAAe,CAAC,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;IAclE;;OAEG;IACH,kBAAkB,CAAC,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;IAwBrE;;OAEG;IACH,KAAK,IAAI,IAAI;IAIb;;OAEG;IACH,MAAM,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI;CAGjC"}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { binarySearchRange, binarySearchFirst } from "../../utils/binary-search.js";
|
|
2
|
+
class MP4IndexCache {
|
|
3
|
+
indexes = /* @__PURE__ */ new Map();
|
|
4
|
+
/**
|
|
5
|
+
* Set index for a resource
|
|
6
|
+
*/
|
|
7
|
+
set(resourceId, index) {
|
|
8
|
+
this.indexes.set(resourceId, index);
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Get index for a resource
|
|
12
|
+
*/
|
|
13
|
+
get(resourceId) {
|
|
14
|
+
return this.indexes.get(resourceId) || null;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Check if index exists for a resource
|
|
18
|
+
*/
|
|
19
|
+
has(resourceId) {
|
|
20
|
+
return this.indexes.has(resourceId);
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Get GOP range (byte range) for a specific time
|
|
24
|
+
* Returns the GOP that contains the target time
|
|
25
|
+
*/
|
|
26
|
+
getGOPRangeForTime(resourceId, timeUs) {
|
|
27
|
+
const index = this.indexes.get(resourceId);
|
|
28
|
+
if (!index?.tracks.video) return null;
|
|
29
|
+
const { samples, gopIndex } = index.tracks.video;
|
|
30
|
+
const targetGOP = binarySearchRange(gopIndex, timeUs, (gop, idx) => {
|
|
31
|
+
const nextGOP = gopIndex[idx + 1];
|
|
32
|
+
return {
|
|
33
|
+
start: gop.startTimeUs,
|
|
34
|
+
end: nextGOP ? nextGOP.startTimeUs : Infinity
|
|
35
|
+
};
|
|
36
|
+
});
|
|
37
|
+
if (!targetGOP) {
|
|
38
|
+
console.warn("[MP4IndexCache] No GOP found for timeUs:", timeUs);
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
const startSample = samples[targetGOP.keyframeSampleIndex];
|
|
42
|
+
const endSampleIndex = targetGOP.keyframeSampleIndex + targetGOP.sampleCount - 1;
|
|
43
|
+
const endSample = samples[endSampleIndex];
|
|
44
|
+
if (!startSample || !endSample) {
|
|
45
|
+
console.error("[MP4IndexCache] Missing samples for GOP:", {
|
|
46
|
+
keyframeSampleIndex: targetGOP.keyframeSampleIndex,
|
|
47
|
+
sampleCount: targetGOP.sampleCount,
|
|
48
|
+
totalSamples: samples.length
|
|
49
|
+
});
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
return {
|
|
53
|
+
byteStart: startSample.byteOffset,
|
|
54
|
+
byteEnd: endSample.byteOffset + endSample.byteLength,
|
|
55
|
+
startTimeUs: startSample.timestamp,
|
|
56
|
+
endTimeUs: endSample.timestamp + endSample.duration
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Get sample at specific timestamp
|
|
61
|
+
*/
|
|
62
|
+
getSampleAtTime(resourceId, timeUs) {
|
|
63
|
+
const index = this.indexes.get(resourceId);
|
|
64
|
+
if (!index?.tracks.video) return null;
|
|
65
|
+
const { samples } = index.tracks.video;
|
|
66
|
+
const result = binarySearchRange(samples, timeUs, (sample, _index) => ({
|
|
67
|
+
start: sample.timestamp,
|
|
68
|
+
end: sample.timestamp + sample.duration
|
|
69
|
+
}));
|
|
70
|
+
return result || null;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Get nearest keyframe before or at the target time
|
|
74
|
+
*/
|
|
75
|
+
getNearestKeyframe(resourceId, timeUs) {
|
|
76
|
+
const index = this.indexes.get(resourceId);
|
|
77
|
+
if (!index?.tracks.video) return null;
|
|
78
|
+
const { samples, gopIndex } = index.tracks.video;
|
|
79
|
+
const firstGOPAfterIndex = binarySearchFirst(gopIndex, (gop) => gop.startTimeUs > timeUs);
|
|
80
|
+
const targetGOPIndex = firstGOPAfterIndex - 1;
|
|
81
|
+
if (targetGOPIndex < 0) {
|
|
82
|
+
return null;
|
|
83
|
+
}
|
|
84
|
+
const targetGOP = gopIndex[targetGOPIndex];
|
|
85
|
+
if (!targetGOP) return null;
|
|
86
|
+
return samples[targetGOP.keyframeSampleIndex] ?? null;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Clear all cached indexes
|
|
90
|
+
*/
|
|
91
|
+
clear() {
|
|
92
|
+
this.indexes.clear();
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Remove index for a specific resource
|
|
96
|
+
*/
|
|
97
|
+
remove(resourceId) {
|
|
98
|
+
this.indexes.delete(resourceId);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
export {
|
|
102
|
+
MP4IndexCache
|
|
103
|
+
};
|
|
104
|
+
//# sourceMappingURL=MP4IndexCache.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MP4IndexCache.js","sources":["../../../src/cache/resource/MP4IndexCache.ts"],"sourcesContent":["import type { MP4Index, Sample, GOPRange } from '../../stages/demux/types';\nimport type { TimeUs } from '../../model/types';\nimport { binarySearchRange, binarySearchFirst } from '../../utils/binary-search';\n\n/**\n * MP4IndexCache - In-memory cache for MP4 indexes\n *\n * Provides fast lookup for:\n * - Sample at specific timestamp\n * - GOP range for time range\n * - Nearest keyframe\n */\nexport class MP4IndexCache {\n private indexes = new Map<string, MP4Index>();\n\n /**\n * Set index for a resource\n */\n set(resourceId: string, index: MP4Index): void {\n this.indexes.set(resourceId, index);\n }\n\n /**\n * Get index for a resource\n */\n get(resourceId: string): MP4Index | null {\n return this.indexes.get(resourceId) || null;\n }\n\n /**\n * Check if index exists for a resource\n */\n has(resourceId: string): boolean {\n return this.indexes.has(resourceId);\n }\n\n /**\n * Get GOP range (byte range) for a specific time\n * Returns the GOP that contains the target time\n */\n getGOPRangeForTime(resourceId: string, timeUs: TimeUs): GOPRange | null {\n const index = this.indexes.get(resourceId);\n if (!index?.tracks.video) return null;\n\n const { samples, gopIndex } = index.tracks.video;\n\n // Find GOP that contains this timestamp using binary search\n const targetGOP = binarySearchRange(gopIndex, timeUs, (gop, idx) => {\n const nextGOP = gopIndex[idx + 1];\n return {\n start: gop.startTimeUs,\n end: nextGOP ? nextGOP.startTimeUs : Infinity,\n };\n });\n\n if (!targetGOP) {\n console.warn('[MP4IndexCache] No GOP found for timeUs:', timeUs);\n return null;\n }\n\n // Calculate byte range for this GOP\n const startSample = samples[targetGOP.keyframeSampleIndex];\n const endSampleIndex = targetGOP.keyframeSampleIndex + targetGOP.sampleCount - 1;\n const endSample = samples[endSampleIndex];\n\n if (!startSample || !endSample) {\n console.error('[MP4IndexCache] Missing samples for GOP:', {\n keyframeSampleIndex: targetGOP.keyframeSampleIndex,\n sampleCount: targetGOP.sampleCount,\n totalSamples: samples.length,\n });\n return null;\n }\n\n return {\n byteStart: startSample.byteOffset,\n byteEnd: endSample.byteOffset + endSample.byteLength,\n startTimeUs: startSample.timestamp,\n endTimeUs: endSample.timestamp + endSample.duration,\n };\n }\n\n /**\n * Get sample at specific timestamp\n */\n getSampleAtTime(resourceId: string, timeUs: TimeUs): Sample | null {\n const index = this.indexes.get(resourceId);\n if (!index?.tracks.video) return null;\n\n const { samples } = index.tracks.video;\n\n const result = binarySearchRange(samples, timeUs, (sample, _index) => ({\n start: sample.timestamp,\n end: sample.timestamp + sample.duration,\n }));\n\n return result || null;\n }\n\n /**\n * Get nearest keyframe before or at the target time\n */\n getNearestKeyframe(resourceId: string, timeUs: TimeUs): Sample | null {\n const index = this.indexes.get(resourceId);\n if (!index?.tracks.video) return null;\n\n const { samples, gopIndex } = index.tracks.video;\n\n // Find the first GOP that starts after timeUs using binary search\n const firstGOPAfterIndex = binarySearchFirst(gopIndex, (gop) => gop.startTimeUs > timeUs);\n\n // The nearest keyframe is in the GOP before firstGOPAfterIndex\n const targetGOPIndex = firstGOPAfterIndex - 1;\n\n if (targetGOPIndex < 0) {\n // timeUs is before the first GOP\n return null;\n }\n\n const targetGOP = gopIndex[targetGOPIndex];\n if (!targetGOP) return null;\n\n // Return the keyframe sample of this GOP\n return samples[targetGOP.keyframeSampleIndex] ?? null;\n }\n\n /**\n * Clear all cached indexes\n */\n clear(): void {\n this.indexes.clear();\n }\n\n /**\n * Remove index for a specific resource\n */\n remove(resourceId: string): void {\n this.indexes.delete(resourceId);\n }\n}\n"],"names":[],"mappings":";AAYO,MAAM,cAAc;AAAA,EACjB,8BAAc,IAAA;AAAA;AAAA;AAAA;AAAA,EAKtB,IAAI,YAAoB,OAAuB;AAC7C,SAAK,QAAQ,IAAI,YAAY,KAAK;AAAA,EACpC;AAAA;AAAA;AAAA;AAAA,EAKA,IAAI,YAAqC;AACvC,WAAO,KAAK,QAAQ,IAAI,UAAU,KAAK;AAAA,EACzC;AAAA;AAAA;AAAA;AAAA,EAKA,IAAI,YAA6B;AAC/B,WAAO,KAAK,QAAQ,IAAI,UAAU;AAAA,EACpC;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,mBAAmB,YAAoB,QAAiC;AACtE,UAAM,QAAQ,KAAK,QAAQ,IAAI,UAAU;AACzC,QAAI,CAAC,OAAO,OAAO,MAAO,QAAO;AAEjC,UAAM,EAAE,SAAS,SAAA,IAAa,MAAM,OAAO;AAG3C,UAAM,YAAY,kBAAkB,UAAU,QAAQ,CAAC,KAAK,QAAQ;AAClE,YAAM,UAAU,SAAS,MAAM,CAAC;AAChC,aAAO;AAAA,QACL,OAAO,IAAI;AAAA,QACX,KAAK,UAAU,QAAQ,cAAc;AAAA,MAAA;AAAA,IAEzC,CAAC;AAED,QAAI,CAAC,WAAW;AACd,cAAQ,KAAK,4CAA4C,MAAM;AAC/D,aAAO;AAAA,IACT;AAGA,UAAM,cAAc,QAAQ,UAAU,mBAAmB;AACzD,UAAM,iBAAiB,UAAU,sBAAsB,UAAU,cAAc;AAC/E,UAAM,YAAY,QAAQ,cAAc;AAExC,QAAI,CAAC,eAAe,CAAC,WAAW;AAC9B,cAAQ,MAAM,4CAA4C;AAAA,QACxD,qBAAqB,UAAU;AAAA,QAC/B,aAAa,UAAU;AAAA,QACvB,cAAc,QAAQ;AAAA,MAAA,CACvB;AACD,aAAO;AAAA,IACT;AAEA,WAAO;AAAA,MACL,WAAW,YAAY;AAAA,MACvB,SAAS,UAAU,aAAa,UAAU;AAAA,MAC1C,aAAa,YAAY;AAAA,MACzB,WAAW,UAAU,YAAY,UAAU;AAAA,IAAA;AAAA,EAE/C;AAAA;AAAA;AAAA;AAAA,EAKA,gBAAgB,YAAoB,QAA+B;AACjE,UAAM,QAAQ,KAAK,QAAQ,IAAI,UAAU;AACzC,QAAI,CAAC,OAAO,OAAO,MAAO,QAAO;AAEjC,UAAM,EAAE,QAAA,IAAY,MAAM,OAAO;AAEjC,UAAM,SAAS,kBAAkB,SAAS,QAAQ,CAAC,QAAQ,YAAY;AAAA,MACrE,OAAO,OAAO;AAAA,MACd,KAAK,OAAO,YAAY,OAAO;AAAA,IAAA,EAC/B;AAEF,WAAO,UAAU;AAAA,EACnB;AAAA;AAAA;AAAA;AAAA,EAKA,mBAAmB,YAAoB,QAA+B;AACpE,UAAM,QAAQ,KAAK,QAAQ,IAAI,UAAU;AACzC,QAAI,CAAC,OAAO,OAAO,MAAO,QAAO;AAEjC,UAAM,EAAE,SAAS,SAAA,IAAa,MAAM,OAAO;AAG3C,UAAM,qBAAqB,kBAAkB,UAAU,CAAC,QAAQ,IAAI,cAAc,MAAM;AAGxF,UAAM,iBAAiB,qBAAqB;AAE5C,QAAI,iBAAiB,GAAG;AAEtB,aAAO;AAAA,IACT;AAEA,UAAM,YAAY,SAAS,cAAc;AACzC,QAAI,CAAC,UAAW,QAAO;AAGvB,WAAO,QAAQ,UAAU,mBAAmB,KAAK;AAAA,EACnD;AAAA;AAAA;AAAA;AAAA,EAKA,QAAc;AACZ,SAAK,QAAQ,MAAA;AAAA,EACf;AAAA;AAAA;AAAA;AAAA,EAKA,OAAO,YAA0B;AAC/B,SAAK,QAAQ,OAAO,UAAU;AAAA,EAChC;AACF;"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { OPFSManager } from '../storage/opfs/OPFSManager';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* ResourceCache - Original video resource OPFS cache
|
|
5
|
+
*
|
|
6
|
+
* Stores original MP4/video files for on-demand decoding
|
|
7
|
+
* Supports range reads for GOP-level access
|
|
8
|
+
*/
|
|
9
|
+
export declare class ResourceCache {
|
|
10
|
+
private opfsManager;
|
|
11
|
+
private projectId;
|
|
12
|
+
constructor(opfsManager: OPFSManager, projectId: string);
|
|
13
|
+
init(): Promise<void>;
|
|
14
|
+
/**
|
|
15
|
+
* Write resource to OPFS (streaming)
|
|
16
|
+
*/
|
|
17
|
+
writeResource(resourceId: string, stream: ReadableStream<Uint8Array>): Promise<void>;
|
|
18
|
+
/**
|
|
19
|
+
* Read byte range from resource (for GOP-level access)
|
|
20
|
+
*/
|
|
21
|
+
readRange(resourceId: string, start: number, end: number): Promise<ArrayBuffer>;
|
|
22
|
+
/**
|
|
23
|
+
* Check if resource exists in OPFS
|
|
24
|
+
*/
|
|
25
|
+
hasResource(resourceId: string): Promise<boolean>;
|
|
26
|
+
/**
|
|
27
|
+
* Get resource file size
|
|
28
|
+
*/
|
|
29
|
+
getResourceSize(resourceId: string): Promise<number>;
|
|
30
|
+
/**
|
|
31
|
+
* Delete a resource from OPFS
|
|
32
|
+
*/
|
|
33
|
+
deleteResource(resourceId: string): Promise<void>;
|
|
34
|
+
/**
|
|
35
|
+
* Clear all resources for current project
|
|
36
|
+
*/
|
|
37
|
+
clear(): Promise<void>;
|
|
38
|
+
/**
|
|
39
|
+
* LRU eviction to free up space
|
|
40
|
+
* Note: This is a simplified implementation
|
|
41
|
+
* Full implementation would track access times in IndexedDB
|
|
42
|
+
*/
|
|
43
|
+
evictLRU(_targetBytes: number): Promise<void>;
|
|
44
|
+
private getFileName;
|
|
45
|
+
}
|
|
46
|
+
//# sourceMappingURL=ResourceCache.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ResourceCache.d.ts","sourceRoot":"","sources":["../../../src/cache/resource/ResourceCache.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,6BAA6B,CAAC;AAG1D;;;;;GAKG;AACH,qBAAa,aAAa;IACxB,OAAO,CAAC,WAAW,CAAc;IACjC,OAAO,CAAC,SAAS,CAAS;gBAEd,WAAW,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM;IAKjD,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAI3B;;OAEG;IACG,aAAa,CAAC,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,cAAc,CAAC,UAAU,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAW1F;;OAEG;IACG,SAAS,CAAC,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC;IAWrF;;OAEG;IACG,WAAW,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAWvD;;OAEG;IACG,eAAe,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAW1D;;OAEG;IACG,cAAc,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAWvD;;OAEG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAI5B;;;;OAIG;IACG,QAAQ,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAKnD,OAAO,CAAC,WAAW;CAIpB"}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
class ResourceCache {
|
|
2
|
+
opfsManager;
|
|
3
|
+
projectId;
|
|
4
|
+
constructor(opfsManager, projectId) {
|
|
5
|
+
this.opfsManager = opfsManager;
|
|
6
|
+
this.projectId = projectId;
|
|
7
|
+
}
|
|
8
|
+
async init() {
|
|
9
|
+
await this.opfsManager.init();
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Write resource to OPFS (streaming)
|
|
13
|
+
*/
|
|
14
|
+
async writeResource(resourceId, stream) {
|
|
15
|
+
const fileName = this.getFileName(resourceId);
|
|
16
|
+
const path = {
|
|
17
|
+
projectId: this.projectId,
|
|
18
|
+
prefix: "resource",
|
|
19
|
+
fileName
|
|
20
|
+
};
|
|
21
|
+
await this.opfsManager.writeFile(path, stream);
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Read byte range from resource (for GOP-level access)
|
|
25
|
+
*/
|
|
26
|
+
async readRange(resourceId, start, end) {
|
|
27
|
+
const fileName = this.getFileName(resourceId);
|
|
28
|
+
const path = {
|
|
29
|
+
projectId: this.projectId,
|
|
30
|
+
prefix: "resource",
|
|
31
|
+
fileName
|
|
32
|
+
};
|
|
33
|
+
return await this.opfsManager.readRange(path, start, end);
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Check if resource exists in OPFS
|
|
37
|
+
*/
|
|
38
|
+
async hasResource(resourceId) {
|
|
39
|
+
const fileName = this.getFileName(resourceId);
|
|
40
|
+
const path = {
|
|
41
|
+
projectId: this.projectId,
|
|
42
|
+
prefix: "resource",
|
|
43
|
+
fileName
|
|
44
|
+
};
|
|
45
|
+
return await this.opfsManager.exists(path);
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Get resource file size
|
|
49
|
+
*/
|
|
50
|
+
async getResourceSize(resourceId) {
|
|
51
|
+
const fileName = this.getFileName(resourceId);
|
|
52
|
+
const path = {
|
|
53
|
+
projectId: this.projectId,
|
|
54
|
+
prefix: "resource",
|
|
55
|
+
fileName
|
|
56
|
+
};
|
|
57
|
+
return await this.opfsManager.getFileSize(path);
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Delete a resource from OPFS
|
|
61
|
+
*/
|
|
62
|
+
async deleteResource(resourceId) {
|
|
63
|
+
const fileName = this.getFileName(resourceId);
|
|
64
|
+
const path = {
|
|
65
|
+
projectId: this.projectId,
|
|
66
|
+
prefix: "resource",
|
|
67
|
+
fileName
|
|
68
|
+
};
|
|
69
|
+
await this.opfsManager.deleteFile(path);
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Clear all resources for current project
|
|
73
|
+
*/
|
|
74
|
+
async clear() {
|
|
75
|
+
await this.opfsManager.deleteProjectDirectory(this.projectId, "resource");
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* LRU eviction to free up space
|
|
79
|
+
* Note: This is a simplified implementation
|
|
80
|
+
* Full implementation would track access times in IndexedDB
|
|
81
|
+
*/
|
|
82
|
+
async evictLRU(_targetBytes) {
|
|
83
|
+
console.warn("[ResourceCache] LRU eviction not yet implemented");
|
|
84
|
+
}
|
|
85
|
+
getFileName(resourceId) {
|
|
86
|
+
return `${resourceId}.mp4`;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
export {
|
|
90
|
+
ResourceCache
|
|
91
|
+
};
|
|
92
|
+
//# sourceMappingURL=ResourceCache.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ResourceCache.js","sources":["../../../src/cache/resource/ResourceCache.ts"],"sourcesContent":["import { OPFSManager } from '../storage/opfs/OPFSManager';\nimport type { OPFSPath } from '../storage/opfs/types';\n\n/**\n * ResourceCache - Original video resource OPFS cache\n *\n * Stores original MP4/video files for on-demand decoding\n * Supports range reads for GOP-level access\n */\nexport class ResourceCache {\n private opfsManager: OPFSManager;\n private projectId: string;\n\n constructor(opfsManager: OPFSManager, projectId: string) {\n this.opfsManager = opfsManager;\n this.projectId = projectId;\n }\n\n async init(): Promise<void> {\n await this.opfsManager.init();\n }\n\n /**\n * Write resource to OPFS (streaming)\n */\n async writeResource(resourceId: string, stream: ReadableStream<Uint8Array>): Promise<void> {\n const fileName = this.getFileName(resourceId);\n const path: OPFSPath = {\n projectId: this.projectId,\n prefix: 'resource',\n fileName,\n };\n\n await this.opfsManager.writeFile(path, stream);\n }\n\n /**\n * Read byte range from resource (for GOP-level access)\n */\n async readRange(resourceId: string, start: number, end: number): Promise<ArrayBuffer> {\n const fileName = this.getFileName(resourceId);\n const path: OPFSPath = {\n projectId: this.projectId,\n prefix: 'resource',\n fileName,\n };\n\n return await this.opfsManager.readRange(path, start, end);\n }\n\n /**\n * Check if resource exists in OPFS\n */\n async hasResource(resourceId: string): Promise<boolean> {\n const fileName = this.getFileName(resourceId);\n const path: OPFSPath = {\n projectId: this.projectId,\n prefix: 'resource',\n fileName,\n };\n\n return await this.opfsManager.exists(path);\n }\n\n /**\n * Get resource file size\n */\n async getResourceSize(resourceId: string): Promise<number> {\n const fileName = this.getFileName(resourceId);\n const path: OPFSPath = {\n projectId: this.projectId,\n prefix: 'resource',\n fileName,\n };\n\n return await this.opfsManager.getFileSize(path);\n }\n\n /**\n * Delete a resource from OPFS\n */\n async deleteResource(resourceId: string): Promise<void> {\n const fileName = this.getFileName(resourceId);\n const path: OPFSPath = {\n projectId: this.projectId,\n prefix: 'resource',\n fileName,\n };\n\n await this.opfsManager.deleteFile(path);\n }\n\n /**\n * Clear all resources for current project\n */\n async clear(): Promise<void> {\n await this.opfsManager.deleteProjectDirectory(this.projectId, 'resource');\n }\n\n /**\n * LRU eviction to free up space\n * Note: This is a simplified implementation\n * Full implementation would track access times in IndexedDB\n */\n async evictLRU(_targetBytes: number): Promise<void> {\n // TODO: Implement LRU eviction when resource metadata tracking is added\n console.warn('[ResourceCache] LRU eviction not yet implemented');\n }\n\n private getFileName(resourceId: string): string {\n // Simple filename mapping - could be enhanced with extension detection\n return `${resourceId}.mp4`;\n }\n}\n"],"names":[],"mappings":"AASO,MAAM,cAAc;AAAA,EACjB;AAAA,EACA;AAAA,EAER,YAAY,aAA0B,WAAmB;AACvD,SAAK,cAAc;AACnB,SAAK,YAAY;AAAA,EACnB;AAAA,EAEA,MAAM,OAAsB;AAC1B,UAAM,KAAK,YAAY,KAAA;AAAA,EACzB;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,cAAc,YAAoB,QAAmD;AACzF,UAAM,WAAW,KAAK,YAAY,UAAU;AAC5C,UAAM,OAAiB;AAAA,MACrB,WAAW,KAAK;AAAA,MAChB,QAAQ;AAAA,MACR;AAAA,IAAA;AAGF,UAAM,KAAK,YAAY,UAAU,MAAM,MAAM;AAAA,EAC/C;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,UAAU,YAAoB,OAAe,KAAmC;AACpF,UAAM,WAAW,KAAK,YAAY,UAAU;AAC5C,UAAM,OAAiB;AAAA,MACrB,WAAW,KAAK;AAAA,MAChB,QAAQ;AAAA,MACR;AAAA,IAAA;AAGF,WAAO,MAAM,KAAK,YAAY,UAAU,MAAM,OAAO,GAAG;AAAA,EAC1D;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,YAAY,YAAsC;AACtD,UAAM,WAAW,KAAK,YAAY,UAAU;AAC5C,UAAM,OAAiB;AAAA,MACrB,WAAW,KAAK;AAAA,MAChB,QAAQ;AAAA,MACR;AAAA,IAAA;AAGF,WAAO,MAAM,KAAK,YAAY,OAAO,IAAI;AAAA,EAC3C;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,gBAAgB,YAAqC;AACzD,UAAM,WAAW,KAAK,YAAY,UAAU;AAC5C,UAAM,OAAiB;AAAA,MACrB,WAAW,KAAK;AAAA,MAChB,QAAQ;AAAA,MACR;AAAA,IAAA;AAGF,WAAO,MAAM,KAAK,YAAY,YAAY,IAAI;AAAA,EAChD;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,eAAe,YAAmC;AACtD,UAAM,WAAW,KAAK,YAAY,UAAU;AAC5C,UAAM,OAAiB;AAAA,MACrB,WAAW,KAAK;AAAA,MAChB,QAAQ;AAAA,MACR;AAAA,IAAA;AAGF,UAAM,KAAK,YAAY,WAAW,IAAI;AAAA,EACxC;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,QAAuB;AAC3B,UAAM,KAAK,YAAY,uBAAuB,KAAK,WAAW,UAAU;AAAA,EAC1E;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,SAAS,cAAqC;AAElD,YAAQ,KAAK,kDAAkD;AAAA,EACjE;AAAA,EAEQ,YAAY,YAA4B;AAE9C,WAAO,GAAG,UAAU;AAAA,EACtB;AACF;"}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { ChunkBatch } from '../opfs/types';
|
|
2
|
+
|
|
3
|
+
export type { ChunkBatch };
|
|
4
|
+
export interface ChunkRecord {
|
|
5
|
+
projectId: string;
|
|
6
|
+
clipId: string;
|
|
7
|
+
track: 'video' | 'audio';
|
|
8
|
+
fileName: string;
|
|
9
|
+
batches: ChunkBatch[];
|
|
10
|
+
lastAccess: number;
|
|
11
|
+
totalBytes: number;
|
|
12
|
+
isComplete: boolean;
|
|
13
|
+
expectedDurationUs?: number;
|
|
14
|
+
metadata?: {
|
|
15
|
+
codec?: string;
|
|
16
|
+
description?: Uint8Array;
|
|
17
|
+
codedWidth?: number;
|
|
18
|
+
codedHeight?: number;
|
|
19
|
+
displayAspectWidth?: number;
|
|
20
|
+
displayAspectHeight?: number;
|
|
21
|
+
colorSpace?: VideoColorSpaceInit;
|
|
22
|
+
hardwareAcceleration?: HardwareAcceleration;
|
|
23
|
+
optimizeForLatency?: boolean;
|
|
24
|
+
sampleRate?: number;
|
|
25
|
+
numberOfChannels?: number;
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* IndexedDB storage for L2 cache metadata
|
|
30
|
+
* Handles all database operations for chunk records
|
|
31
|
+
*/
|
|
32
|
+
export declare class ChunkRecordStore {
|
|
33
|
+
private db;
|
|
34
|
+
private initPromise;
|
|
35
|
+
init(): Promise<void>;
|
|
36
|
+
private initStorage;
|
|
37
|
+
/**
|
|
38
|
+
* Get a chunk record
|
|
39
|
+
*/
|
|
40
|
+
getRecord(projectId: string, clipId: string, track: string): Promise<ChunkRecord | null>;
|
|
41
|
+
/**
|
|
42
|
+
* Put/update a chunk record
|
|
43
|
+
*/
|
|
44
|
+
putRecord(record: ChunkRecord): Promise<void>;
|
|
45
|
+
/**
|
|
46
|
+
* Delete a chunk record
|
|
47
|
+
*/
|
|
48
|
+
deleteRecord(projectId: string, clipId: string, track: string): Promise<void>;
|
|
49
|
+
/**
|
|
50
|
+
* Update last access time for a record
|
|
51
|
+
*/
|
|
52
|
+
updateLastAccess(projectId: string, clipId: string, track: string): Promise<void>;
|
|
53
|
+
/**
|
|
54
|
+
* Collect all records for a specific clip
|
|
55
|
+
*/
|
|
56
|
+
collectRecordsByClipId(projectId: string, clipId: string): Promise<ChunkRecord[]>;
|
|
57
|
+
/**
|
|
58
|
+
* Get all records for a specific project
|
|
59
|
+
*/
|
|
60
|
+
getRecordsByProjectId(projectId: string): Promise<ChunkRecord[]>;
|
|
61
|
+
/**
|
|
62
|
+
* Get all records sorted by lastAccess (for quota enforcement)
|
|
63
|
+
*/
|
|
64
|
+
getAllRecordsSortedByAccess(): Promise<ChunkRecord[]>;
|
|
65
|
+
/**
|
|
66
|
+
* Get all records (for project statistics)
|
|
67
|
+
*/
|
|
68
|
+
getAllRecords(): Promise<ChunkRecord[]>;
|
|
69
|
+
/**
|
|
70
|
+
* Clear all data from chunks and meta stores
|
|
71
|
+
*/
|
|
72
|
+
clear(): Promise<void>;
|
|
73
|
+
private promisifyRequest;
|
|
74
|
+
}
|
|
75
|
+
//# sourceMappingURL=ChunkRecordStore.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ChunkRecordStore.d.ts","sourceRoot":"","sources":["../../../../src/cache/storage/indexeddb/ChunkRecordStore.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAEhD,YAAY,EAAE,UAAU,EAAE,CAAC;AAE3B,MAAM,WAAW,WAAW;IAC1B,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC;IACzB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,UAAU,EAAE,CAAC;IACtB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,OAAO,CAAC;IACpB,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,EAAE;QACT,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,WAAW,CAAC,EAAE,UAAU,CAAC;QACzB,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,kBAAkB,CAAC,EAAE,MAAM,CAAC;QAC5B,mBAAmB,CAAC,EAAE,MAAM,CAAC;QAC7B,UAAU,CAAC,EAAE,mBAAmB,CAAC;QACjC,oBAAoB,CAAC,EAAE,oBAAoB,CAAC;QAC5C,kBAAkB,CAAC,EAAE,OAAO,CAAC;QAC7B,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,gBAAgB,CAAC,EAAE,MAAM,CAAC;KAC3B,CAAC;CACH;AAED;;;GAGG;AACH,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,EAAE,CAA4B;IACtC,OAAO,CAAC,WAAW,CAA8B;IAE3C,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;YAOb,WAAW;IAqCzB;;OAEG;IACG,SAAS,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC;IAU9F;;OAEG;IACG,SAAS,CAAC,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IAcnD;;OAEG;IACG,YAAY,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAQnF;;OAEG;IACG,gBAAgB,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAavF;;OAEG;IACG,sBAAsB,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC;IA4BvF;;OAEG;IACG,qBAAqB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC;IAatE;;OAEG;IACG,2BAA2B,IAAI,OAAO,CAAC,WAAW,EAAE,CAAC;IAyB3D;;OAEG;IACG,aAAa,IAAI,OAAO,CAAC,WAAW,EAAE,CAAC;IAwB7C;;OAEG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAQ5B,OAAO,CAAC,gBAAgB;CAMzB"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
class
|
|
1
|
+
class ChunkRecordStore {
|
|
2
2
|
db = null;
|
|
3
3
|
initPromise = null;
|
|
4
4
|
async init() {
|
|
@@ -175,6 +175,6 @@ class IndexedDBStore {
|
|
|
175
175
|
}
|
|
176
176
|
}
|
|
177
177
|
export {
|
|
178
|
-
|
|
178
|
+
ChunkRecordStore
|
|
179
179
|
};
|
|
180
|
-
//# sourceMappingURL=
|
|
180
|
+
//# sourceMappingURL=ChunkRecordStore.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ChunkRecordStore.js","sources":["../../../../src/cache/storage/indexeddb/ChunkRecordStore.ts"],"sourcesContent":["import type { ChunkBatch } from '../opfs/types';\n\nexport type { ChunkBatch };\n\nexport interface ChunkRecord {\n projectId: string;\n clipId: string;\n track: 'video' | 'audio';\n fileName: string;\n batches: ChunkBatch[];\n lastAccess: number;\n totalBytes: number;\n isComplete: boolean;\n expectedDurationUs?: number;\n metadata?: {\n codec?: string;\n description?: Uint8Array;\n codedWidth?: number;\n codedHeight?: number;\n displayAspectWidth?: number;\n displayAspectHeight?: number;\n colorSpace?: VideoColorSpaceInit;\n hardwareAcceleration?: HardwareAcceleration;\n optimizeForLatency?: boolean;\n sampleRate?: number;\n numberOfChannels?: number;\n };\n}\n\n/**\n * IndexedDB storage for L2 cache metadata\n * Handles all database operations for chunk records\n */\nexport class ChunkRecordStore {\n private db: IDBDatabase | null = null;\n private initPromise: Promise<void> | null = null;\n\n async init(): Promise<void> {\n if (this.initPromise) return this.initPromise;\n\n this.initPromise = this.initStorage();\n return this.initPromise;\n }\n\n private async initStorage(): Promise<void> {\n // meframe_cache schema v1.1 (IndexedDB version 2)\n // - Added projectId support for multi-project isolation\n // - Composite key: [projectId, clipId, track]\n const request = indexedDB.open('meframe_cache', 2);\n\n request.onupgradeneeded = (event) => {\n const db = (event.target as IDBOpenDBRequest).result;\n const oldVersion = event.oldVersion;\n\n // Version 1→2: Add projectId support\n if (oldVersion < 2) {\n // Delete old chunks store if exists\n if (db.objectStoreNames.contains('chunks')) {\n db.deleteObjectStore('chunks');\n }\n\n // Create new chunks store with composite key [projectId, clipId, track]\n const store = db.createObjectStore('chunks', {\n keyPath: ['projectId', 'clipId', 'track'],\n });\n store.createIndex('lastAccess', 'lastAccess');\n store.createIndex('projectId', 'projectId'); // New: query by project\n }\n\n // meta store (unchanged)\n if (!db.objectStoreNames.contains('meta')) {\n db.createObjectStore('meta', { keyPath: 'projectId' });\n }\n };\n\n this.db = await new Promise((resolve, reject) => {\n request.onsuccess = () => resolve(request.result);\n request.onerror = () => reject(request.error);\n });\n }\n\n /**\n * Get a chunk record\n */\n async getRecord(projectId: string, clipId: string, track: string): Promise<ChunkRecord | null> {\n if (!this.db) return null;\n\n const tx = this.db.transaction('chunks', 'readonly');\n const store = tx.objectStore('chunks');\n const record = await this.promisifyRequest<ChunkRecord>(store.get([projectId, clipId, track]));\n\n return record || null;\n }\n\n /**\n * Put/update a chunk record\n */\n async putRecord(record: ChunkRecord): Promise<void> {\n if (!this.db) return;\n\n const tx = this.db.transaction('chunks', 'readwrite');\n const store = tx.objectStore('chunks');\n store.put(record);\n\n // Wait for transaction to complete\n await new Promise<void>((resolve, reject) => {\n tx.oncomplete = () => resolve();\n tx.onerror = () => reject(tx.error);\n });\n }\n\n /**\n * Delete a chunk record\n */\n async deleteRecord(projectId: string, clipId: string, track: string): Promise<void> {\n if (!this.db) return;\n\n const tx = this.db.transaction('chunks', 'readwrite');\n const store = tx.objectStore('chunks');\n await this.promisifyRequest(store.delete([projectId, clipId, track]));\n }\n\n /**\n * Update last access time for a record\n */\n async updateLastAccess(projectId: string, clipId: string, track: string): Promise<void> {\n if (!this.db) return;\n\n const tx = this.db.transaction('chunks', 'readwrite');\n const store = tx.objectStore('chunks');\n const record = await this.promisifyRequest<ChunkRecord>(store.get([projectId, clipId, track]));\n\n if (record) {\n record.lastAccess = Date.now();\n await this.promisifyRequest(store.put(record));\n }\n }\n\n /**\n * Collect all records for a specific clip\n */\n async collectRecordsByClipId(projectId: string, clipId: string): Promise<ChunkRecord[]> {\n if (!this.db) return [];\n\n const tx = this.db.transaction('chunks', 'readonly');\n const store = tx.objectStore('chunks');\n const records: ChunkRecord[] = [];\n const cursor = store.openCursor();\n\n await new Promise<void>((resolve) => {\n cursor.onsuccess = (event) => {\n const cursor = (event.target as IDBRequest).result;\n if (!cursor) {\n resolve();\n return;\n }\n\n const record: ChunkRecord = cursor.value;\n if (record.projectId === projectId && record.clipId === clipId) {\n records.push(record);\n }\n\n cursor.continue();\n };\n });\n\n return records;\n }\n\n /**\n * Get all records for a specific project\n */\n async getRecordsByProjectId(projectId: string): Promise<ChunkRecord[]> {\n if (!this.db) return [];\n\n const tx = this.db.transaction('chunks', 'readonly');\n const store = tx.objectStore('chunks');\n const index = store.index('projectId');\n const records = await this.promisifyRequest<ChunkRecord[]>(\n index.getAll(IDBKeyRange.only(projectId))\n );\n\n return records;\n }\n\n /**\n * Get all records sorted by lastAccess (for quota enforcement)\n */\n async getAllRecordsSortedByAccess(): Promise<ChunkRecord[]> {\n if (!this.db) return [];\n\n const tx = this.db.transaction('chunks', 'readonly');\n const store = tx.objectStore('chunks');\n const index = store.index('lastAccess');\n const records: ChunkRecord[] = [];\n\n const cursor = index.openCursor();\n await new Promise<void>((resolve) => {\n cursor.onsuccess = (event) => {\n const cursor = (event.target as IDBRequest).result;\n if (!cursor) {\n resolve();\n return;\n }\n\n records.push(cursor.value);\n cursor.continue();\n };\n });\n\n return records;\n }\n\n /**\n * Get all records (for project statistics)\n */\n async getAllRecords(): Promise<ChunkRecord[]> {\n if (!this.db) return [];\n\n const tx = this.db.transaction('chunks', 'readonly');\n const store = tx.objectStore('chunks');\n const records: ChunkRecord[] = [];\n\n const cursor = store.openCursor();\n await new Promise<void>((resolve) => {\n cursor.onsuccess = (event) => {\n const cursor = (event.target as IDBRequest).result;\n if (!cursor) {\n resolve();\n return;\n }\n\n records.push(cursor.value);\n cursor.continue();\n };\n });\n\n return records;\n }\n\n /**\n * Clear all data from chunks and meta stores\n */\n async clear(): Promise<void> {\n if (!this.db) return;\n\n const tx = this.db.transaction(['chunks', 'meta'], 'readwrite');\n await this.promisifyRequest(tx.objectStore('chunks').clear());\n await this.promisifyRequest(tx.objectStore('meta').clear());\n }\n\n private promisifyRequest<T>(request: IDBRequest): Promise<T> {\n return new Promise((resolve, reject) => {\n request.onsuccess = () => resolve(request.result);\n request.onerror = () => reject(request.error);\n });\n }\n}\n"],"names":["cursor"],"mappings":"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"}
|