@meframe/core 0.0.1
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/CHANGELOG.md +42 -0
- package/LICENSE +22 -0
- package/README.md +396 -0
- package/dist/Meframe.d.ts +82 -0
- package/dist/Meframe.d.ts.map +1 -0
- package/dist/Meframe.js +290 -0
- package/dist/Meframe.js.map +1 -0
- package/dist/_virtual/mp4box.all.js +5 -0
- package/dist/_virtual/mp4box.all.js.map +1 -0
- package/dist/cache/BatchWriter.d.ts +25 -0
- package/dist/cache/BatchWriter.d.ts.map +1 -0
- package/dist/cache/CacheManager.d.ts +115 -0
- package/dist/cache/CacheManager.d.ts.map +1 -0
- package/dist/cache/CacheManager.js +388 -0
- package/dist/cache/CacheManager.js.map +1 -0
- package/dist/cache/CacheStatsDecorator.d.ts +27 -0
- package/dist/cache/CacheStatsDecorator.d.ts.map +1 -0
- package/dist/cache/L2Cache.d.ts +39 -0
- package/dist/cache/L2Cache.d.ts.map +1 -0
- package/dist/cache/L2Cache.js +282 -0
- package/dist/cache/L2Cache.js.map +1 -0
- package/dist/cache/index.d.ts +7 -0
- package/dist/cache/index.d.ts.map +1 -0
- package/dist/cache/l1/AudioL1Cache.d.ts +30 -0
- package/dist/cache/l1/AudioL1Cache.d.ts.map +1 -0
- package/dist/cache/l1/AudioL1Cache.js +306 -0
- package/dist/cache/l1/AudioL1Cache.js.map +1 -0
- package/dist/cache/l1/MixedAudioL1Cache.d.ts +13 -0
- package/dist/cache/l1/MixedAudioL1Cache.d.ts.map +1 -0
- package/dist/cache/l1/MixedAudioL1Cache.js +52 -0
- package/dist/cache/l1/MixedAudioL1Cache.js.map +1 -0
- package/dist/cache/l1/VideoL1Cache.d.ts +69 -0
- package/dist/cache/l1/VideoL1Cache.d.ts.map +1 -0
- package/dist/cache/l1/VideoL1Cache.js +318 -0
- package/dist/cache/l1/VideoL1Cache.js.map +1 -0
- package/dist/cache/l1/gop-utils.d.ts +10 -0
- package/dist/cache/l1/gop-utils.d.ts.map +1 -0
- package/dist/cache/l1/gop-utils.js +78 -0
- package/dist/cache/l1/gop-utils.js.map +1 -0
- package/dist/cache/l1/index.d.ts +4 -0
- package/dist/cache/l1/index.d.ts.map +1 -0
- package/dist/cache/l1/types.d.ts +17 -0
- package/dist/cache/l1/types.d.ts.map +1 -0
- package/dist/cache/types.d.ts +93 -0
- package/dist/cache/types.d.ts.map +1 -0
- package/dist/config/ConfigLoader.d.ts +69 -0
- package/dist/config/ConfigLoader.d.ts.map +1 -0
- package/dist/config/ConfigLoader.js +133 -0
- package/dist/config/ConfigLoader.js.map +1 -0
- package/dist/config/defaults.d.ts +125 -0
- package/dist/config/defaults.d.ts.map +1 -0
- package/dist/config/defaults.js +191 -0
- package/dist/config/defaults.js.map +1 -0
- package/dist/config/index.d.ts +6 -0
- package/dist/config/index.d.ts.map +1 -0
- package/dist/config/presets.d.ts +32 -0
- package/dist/config/presets.d.ts.map +1 -0
- package/dist/config/presets.js +11 -0
- package/dist/config/presets.js.map +1 -0
- package/dist/config/types.d.ts +199 -0
- package/dist/config/types.d.ts.map +1 -0
- package/dist/config/validation.d.ts +19 -0
- package/dist/config/validation.d.ts.map +1 -0
- package/dist/config/validation.js +232 -0
- package/dist/config/validation.js.map +1 -0
- package/dist/controllers/PlaybackController.d.ts +55 -0
- package/dist/controllers/PlaybackController.d.ts.map +1 -0
- package/dist/controllers/PlaybackController.js +369 -0
- package/dist/controllers/PlaybackController.js.map +1 -0
- package/dist/controllers/PreRenderService.d.ts +34 -0
- package/dist/controllers/PreRenderService.d.ts.map +1 -0
- package/dist/controllers/PreRenderService.js +83 -0
- package/dist/controllers/PreRenderService.js.map +1 -0
- package/dist/controllers/PreRenderTaskQueue.d.ts +21 -0
- package/dist/controllers/PreRenderTaskQueue.d.ts.map +1 -0
- package/dist/controllers/PreviewHandle.d.ts +23 -0
- package/dist/controllers/PreviewHandle.d.ts.map +1 -0
- package/dist/controllers/PreviewHandle.js +39 -0
- package/dist/controllers/PreviewHandle.js.map +1 -0
- package/dist/controllers/index.d.ts +8 -0
- package/dist/controllers/index.d.ts.map +1 -0
- package/dist/controllers/types.d.ts +102 -0
- package/dist/controllers/types.d.ts.map +1 -0
- package/dist/event/EventBus.d.ts +42 -0
- package/dist/event/EventBus.d.ts.map +1 -0
- package/dist/event/EventBus.js +94 -0
- package/dist/event/EventBus.js.map +1 -0
- package/dist/event/events.d.ts +371 -0
- package/dist/event/events.d.ts.map +1 -0
- package/dist/event/events.js +71 -0
- package/dist/event/events.js.map +1 -0
- package/dist/event/index.d.ts +4 -0
- package/dist/event/index.d.ts.map +1 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +15 -0
- package/dist/index.js.map +1 -0
- package/dist/model/CompositionModel.d.ts +48 -0
- package/dist/model/CompositionModel.d.ts.map +1 -0
- package/dist/model/CompositionModel.js +197 -0
- package/dist/model/CompositionModel.js.map +1 -0
- package/dist/model/RcFrame.d.ts +34 -0
- package/dist/model/RcFrame.d.ts.map +1 -0
- package/dist/model/RcFrame.js +97 -0
- package/dist/model/RcFrame.js.map +1 -0
- package/dist/model/dirty-range.d.ts +5 -0
- package/dist/model/dirty-range.d.ts.map +1 -0
- package/dist/model/dirty-range.js +220 -0
- package/dist/model/dirty-range.js.map +1 -0
- package/dist/model/index.d.ts +7 -0
- package/dist/model/index.d.ts.map +1 -0
- package/dist/model/patch.d.ts +5 -0
- package/dist/model/patch.d.ts.map +1 -0
- package/dist/model/patch.js +250 -0
- package/dist/model/patch.js.map +1 -0
- package/dist/model/types.d.ts +135 -0
- package/dist/model/types.d.ts.map +1 -0
- package/dist/model/types.js +5 -0
- package/dist/model/types.js.map +1 -0
- package/dist/model/validation.d.ts +15 -0
- package/dist/model/validation.d.ts.map +1 -0
- package/dist/model/validation.js +74 -0
- package/dist/model/validation.js.map +1 -0
- package/dist/node_modules/.pnpm/mp4box@0.5.4/node_modules/mp4box/dist/mp4box.all.js +7046 -0
- package/dist/node_modules/.pnpm/mp4box@0.5.4/node_modules/mp4box/dist/mp4box.all.js.map +1 -0
- package/dist/orchestrator/ClipSessionManager.d.ts +75 -0
- package/dist/orchestrator/ClipSessionManager.d.ts.map +1 -0
- package/dist/orchestrator/ClipSessionManager.js +160 -0
- package/dist/orchestrator/ClipSessionManager.js.map +1 -0
- package/dist/orchestrator/CompositionPlanner.d.ts +55 -0
- package/dist/orchestrator/CompositionPlanner.d.ts.map +1 -0
- package/dist/orchestrator/CompositionPlanner.js +411 -0
- package/dist/orchestrator/CompositionPlanner.js.map +1 -0
- package/dist/orchestrator/Orchestrator.d.ts +59 -0
- package/dist/orchestrator/Orchestrator.d.ts.map +1 -0
- package/dist/orchestrator/Orchestrator.js +390 -0
- package/dist/orchestrator/Orchestrator.js.map +1 -0
- package/dist/orchestrator/VideoClipSession.d.ts +64 -0
- package/dist/orchestrator/VideoClipSession.d.ts.map +1 -0
- package/dist/orchestrator/VideoClipSession.js +309 -0
- package/dist/orchestrator/VideoClipSession.js.map +1 -0
- package/dist/orchestrator/index.d.ts +5 -0
- package/dist/orchestrator/index.d.ts.map +1 -0
- package/dist/orchestrator/types.d.ts +64 -0
- package/dist/orchestrator/types.d.ts.map +1 -0
- package/dist/plugins/BackpressureMonitor.d.ts +33 -0
- package/dist/plugins/BackpressureMonitor.d.ts.map +1 -0
- package/dist/plugins/BackpressureMonitor.js +62 -0
- package/dist/plugins/BackpressureMonitor.js.map +1 -0
- package/dist/plugins/PluginManager.d.ts +37 -0
- package/dist/plugins/PluginManager.d.ts.map +1 -0
- package/dist/plugins/PluginManager.js +66 -0
- package/dist/plugins/PluginManager.js.map +1 -0
- package/dist/plugins/types.d.ts +60 -0
- package/dist/plugins/types.d.ts.map +1 -0
- package/dist/stages/compose/AudioDucker.d.ts +59 -0
- package/dist/stages/compose/AudioDucker.d.ts.map +1 -0
- package/dist/stages/compose/AudioDucker.js +161 -0
- package/dist/stages/compose/AudioDucker.js.map +1 -0
- package/dist/stages/compose/AudioMixer.d.ts +29 -0
- package/dist/stages/compose/AudioMixer.d.ts.map +1 -0
- package/dist/stages/compose/AudioMixer.js +373 -0
- package/dist/stages/compose/AudioMixer.js.map +1 -0
- package/dist/stages/compose/FilterProcessor.d.ts +41 -0
- package/dist/stages/compose/FilterProcessor.d.ts.map +1 -0
- package/dist/stages/compose/FilterProcessor.js +226 -0
- package/dist/stages/compose/FilterProcessor.js.map +1 -0
- package/dist/stages/compose/GlobalAudioSession.d.ts +38 -0
- package/dist/stages/compose/GlobalAudioSession.d.ts.map +1 -0
- package/dist/stages/compose/GlobalAudioSession.js +122 -0
- package/dist/stages/compose/GlobalAudioSession.js.map +1 -0
- package/dist/stages/compose/LayerRenderer.d.ts +30 -0
- package/dist/stages/compose/LayerRenderer.d.ts.map +1 -0
- package/dist/stages/compose/LayerRenderer.js +215 -0
- package/dist/stages/compose/LayerRenderer.js.map +1 -0
- package/dist/stages/compose/OfflineAudioMixer.d.ts +14 -0
- package/dist/stages/compose/OfflineAudioMixer.d.ts.map +1 -0
- package/dist/stages/compose/OfflineAudioMixer.js +68 -0
- package/dist/stages/compose/OfflineAudioMixer.js.map +1 -0
- package/dist/stages/compose/TransitionProcessor.d.ts +30 -0
- package/dist/stages/compose/TransitionProcessor.d.ts.map +1 -0
- package/dist/stages/compose/TransitionProcessor.js +189 -0
- package/dist/stages/compose/TransitionProcessor.js.map +1 -0
- package/dist/stages/compose/VideoComposer.d.ts +30 -0
- package/dist/stages/compose/VideoComposer.d.ts.map +1 -0
- package/dist/stages/compose/VideoComposer.js +186 -0
- package/dist/stages/compose/VideoComposer.js.map +1 -0
- package/dist/stages/compose/audio-compose.worker.d.ts +79 -0
- package/dist/stages/compose/audio-compose.worker.d.ts.map +1 -0
- package/dist/stages/compose/audio-compose.worker.js +541 -0
- package/dist/stages/compose/audio-compose.worker.js.map +1 -0
- package/dist/stages/compose/instructions.d.ts +95 -0
- package/dist/stages/compose/instructions.d.ts.map +1 -0
- package/dist/stages/compose/types.d.ts +245 -0
- package/dist/stages/compose/types.d.ts.map +1 -0
- package/dist/stages/compose/video-compose.worker.d.ts +60 -0
- package/dist/stages/compose/video-compose.worker.d.ts.map +1 -0
- package/dist/stages/compose/video-compose.worker.js +369 -0
- package/dist/stages/compose/video-compose.worker.js.map +1 -0
- package/dist/stages/decode/AudioChunkDecoder.d.ts +41 -0
- package/dist/stages/decode/AudioChunkDecoder.d.ts.map +1 -0
- package/dist/stages/decode/AudioChunkDecoder.js +83 -0
- package/dist/stages/decode/AudioChunkDecoder.js.map +1 -0
- package/dist/stages/decode/BaseDecoder.d.ts +35 -0
- package/dist/stages/decode/BaseDecoder.d.ts.map +1 -0
- package/dist/stages/decode/BaseDecoder.js +130 -0
- package/dist/stages/decode/BaseDecoder.js.map +1 -0
- package/dist/stages/decode/VideoChunkDecoder.d.ts +54 -0
- package/dist/stages/decode/VideoChunkDecoder.d.ts.map +1 -0
- package/dist/stages/decode/VideoChunkDecoder.js +209 -0
- package/dist/stages/decode/VideoChunkDecoder.js.map +1 -0
- package/dist/stages/decode/decode.worker.d.ts +70 -0
- package/dist/stages/decode/decode.worker.d.ts.map +1 -0
- package/dist/stages/decode/decode.worker.js +436 -0
- package/dist/stages/decode/decode.worker.js.map +1 -0
- package/dist/stages/decode/index.d.ts +5 -0
- package/dist/stages/decode/index.d.ts.map +1 -0
- package/dist/stages/decode/types.d.ts +108 -0
- package/dist/stages/decode/types.d.ts.map +1 -0
- package/dist/stages/demux/MP3FrameParser.d.ts +33 -0
- package/dist/stages/demux/MP3FrameParser.d.ts.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.d.ts +45 -0
- package/dist/stages/demux/MP4Demuxer.d.ts.map +1 -0
- package/dist/stages/demux/MP4Demuxer.js +227 -0
- package/dist/stages/demux/MP4Demuxer.js.map +1 -0
- package/dist/stages/demux/aac-esds-extractor.d.ts +7 -0
- package/dist/stages/demux/aac-esds-extractor.d.ts.map +1 -0
- package/dist/stages/demux/audio-demux.worker.d.ts +51 -0
- package/dist/stages/demux/audio-demux.worker.d.ts.map +1 -0
- package/dist/stages/demux/audio-demux.worker.js +312 -0
- package/dist/stages/demux/audio-demux.worker.js.map +1 -0
- package/dist/stages/demux/types.d.ts +77 -0
- package/dist/stages/demux/types.d.ts.map +1 -0
- package/dist/stages/demux/video-demux.worker.d.ts +48 -0
- package/dist/stages/demux/video-demux.worker.d.ts.map +1 -0
- package/dist/stages/demux/video-demux.worker.js +173 -0
- package/dist/stages/demux/video-demux.worker.js.map +1 -0
- package/dist/stages/encode/AudioChunkEncoder.d.ts +21 -0
- package/dist/stages/encode/AudioChunkEncoder.d.ts.map +1 -0
- package/dist/stages/encode/AudioChunkEncoder.js +37 -0
- package/dist/stages/encode/AudioChunkEncoder.js.map +1 -0
- package/dist/stages/encode/BaseEncoder.d.ts +44 -0
- package/dist/stages/encode/BaseEncoder.d.ts.map +1 -0
- package/dist/stages/encode/BaseEncoder.js +164 -0
- package/dist/stages/encode/BaseEncoder.js.map +1 -0
- package/dist/stages/encode/EncoderPool.d.ts +28 -0
- package/dist/stages/encode/EncoderPool.d.ts.map +1 -0
- package/dist/stages/encode/VideoChunkEncoder.d.ts +26 -0
- package/dist/stages/encode/VideoChunkEncoder.d.ts.map +1 -0
- package/dist/stages/encode/VideoChunkEncoder.js +50 -0
- package/dist/stages/encode/VideoChunkEncoder.js.map +1 -0
- package/dist/stages/encode/encode.worker.d.ts +3 -0
- package/dist/stages/encode/encode.worker.d.ts.map +1 -0
- package/dist/stages/encode/encode.worker.js +318 -0
- package/dist/stages/encode/encode.worker.js.map +1 -0
- package/dist/stages/encode/index.d.ts +6 -0
- package/dist/stages/encode/index.d.ts.map +1 -0
- package/dist/stages/encode/types.d.ts +127 -0
- package/dist/stages/encode/types.d.ts.map +1 -0
- package/dist/stages/load/EventHandlers.d.ts +35 -0
- package/dist/stages/load/EventHandlers.d.ts.map +1 -0
- package/dist/stages/load/EventHandlers.js +65 -0
- package/dist/stages/load/EventHandlers.js.map +1 -0
- package/dist/stages/load/ResourceLoader.d.ts +36 -0
- package/dist/stages/load/ResourceLoader.d.ts.map +1 -0
- package/dist/stages/load/ResourceLoader.js +184 -0
- package/dist/stages/load/ResourceLoader.js.map +1 -0
- package/dist/stages/load/StreamFactory.d.ts +42 -0
- package/dist/stages/load/StreamFactory.d.ts.map +1 -0
- package/dist/stages/load/StreamFactory.js +201 -0
- package/dist/stages/load/StreamFactory.js.map +1 -0
- package/dist/stages/load/TaskManager.d.ts +50 -0
- package/dist/stages/load/TaskManager.d.ts.map +1 -0
- package/dist/stages/load/TaskManager.js +103 -0
- package/dist/stages/load/TaskManager.js.map +1 -0
- package/dist/stages/load/WindowByteRangeResolver.d.ts +47 -0
- package/dist/stages/load/WindowByteRangeResolver.d.ts.map +1 -0
- package/dist/stages/load/WindowByteRangeResolver.js +270 -0
- package/dist/stages/load/WindowByteRangeResolver.js.map +1 -0
- package/dist/stages/load/index.d.ts +11 -0
- package/dist/stages/load/index.d.ts.map +1 -0
- package/dist/stages/load/types.d.ts +177 -0
- package/dist/stages/load/types.d.ts.map +1 -0
- package/dist/stages/mux/MP4Muxer.d.ts +44 -0
- package/dist/stages/mux/MP4Muxer.d.ts.map +1 -0
- package/dist/stages/mux/MP4Muxer.js +262 -0
- package/dist/stages/mux/MP4Muxer.js.map +1 -0
- package/dist/stages/mux/OPFSWriter.d.ts +46 -0
- package/dist/stages/mux/OPFSWriter.d.ts.map +1 -0
- package/dist/stages/mux/index.d.ts +5 -0
- package/dist/stages/mux/index.d.ts.map +1 -0
- package/dist/stages/mux/mux.worker.d.ts +65 -0
- package/dist/stages/mux/mux.worker.d.ts.map +1 -0
- package/dist/stages/mux/mux.worker.js +219 -0
- package/dist/stages/mux/mux.worker.js.map +1 -0
- package/dist/stages/mux/types.d.ts +95 -0
- package/dist/stages/mux/types.d.ts.map +1 -0
- package/dist/stages/mux/utils.d.ts +32 -0
- package/dist/stages/mux/utils.d.ts.map +1 -0
- package/dist/stages/mux/utils.js +34 -0
- package/dist/stages/mux/utils.js.map +1 -0
- package/dist/types.d.ts +25 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/utils/BackpressureAdapter.d.ts +26 -0
- package/dist/utils/BackpressureAdapter.d.ts.map +1 -0
- package/dist/utils/binary-search.d.ts +33 -0
- package/dist/utils/binary-search.d.ts.map +1 -0
- package/dist/utils/binary-search.js +62 -0
- package/dist/utils/binary-search.js.map +1 -0
- package/dist/utils/canvas-utils.d.ts +96 -0
- package/dist/utils/canvas-utils.d.ts.map +1 -0
- package/dist/utils/canvas-utils.js +58 -0
- package/dist/utils/canvas-utils.js.map +1 -0
- package/dist/utils/object-utils.d.ts +34 -0
- package/dist/utils/object-utils.d.ts.map +1 -0
- package/dist/utils/object-utils.js +22 -0
- package/dist/utils/object-utils.js.map +1 -0
- package/dist/utils/time-utils.d.ts +10 -0
- package/dist/utils/time-utils.d.ts.map +1 -0
- package/dist/utils/time-utils.js +60 -0
- package/dist/utils/time-utils.js.map +1 -0
- package/dist/worker/BaseWorker.d.ts +44 -0
- package/dist/worker/BaseWorker.d.ts.map +1 -0
- package/dist/worker/BaseWorker.js +98 -0
- package/dist/worker/BaseWorker.js.map +1 -0
- package/dist/worker/WorkerChannel.d.ts +105 -0
- package/dist/worker/WorkerChannel.d.ts.map +1 -0
- package/dist/worker/WorkerChannel.js +355 -0
- package/dist/worker/WorkerChannel.js.map +1 -0
- package/dist/worker/WorkerPool.d.ts +52 -0
- package/dist/worker/WorkerPool.d.ts.map +1 -0
- package/dist/worker/WorkerPool.js +124 -0
- package/dist/worker/WorkerPool.js.map +1 -0
- package/dist/worker/index.d.ts +11 -0
- package/dist/worker/index.d.ts.map +1 -0
- package/dist/worker/transferable-helper.d.ts +89 -0
- package/dist/worker/transferable-helper.d.ts.map +1 -0
- package/dist/worker/transferable-helper.js +44 -0
- package/dist/worker/transferable-helper.js.map +1 -0
- package/dist/worker/types.d.ts +179 -0
- package/dist/worker/types.d.ts.map +1 -0
- package/dist/worker/types.js +50 -0
- package/dist/worker/types.js.map +1 -0
- package/dist/worker/worker-event-whitelist.d.ts +23 -0
- package/dist/worker/worker-event-whitelist.d.ts.map +1 -0
- package/dist/worker/worker-retry.d.ts +36 -0
- package/dist/worker/worker-retry.d.ts.map +1 -0
- package/dist/worker/worker-retry.js +55 -0
- package/dist/worker/worker-retry.js.map +1 -0
- package/package.json +105 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AudioL1Cache.js","sources":["../../../src/cache/l1/AudioL1Cache.ts"],"sourcesContent":["import type { AudioMetadata, AudioSlot } from './types';\nimport type { TimeUs } from '../../model/types';\n\ninterface PCMClipEntry {\n clipId: string;\n sampleRate: number;\n numberOfChannels: number;\n planes: Float32Array[];\n startUs: TimeUs;\n durationUs: TimeUs;\n lastAccessedAt: number;\n}\n\nexport class AudioL1Cache {\n private slots: AudioSlot[] = [];\n private clipPCM = new Map<string, PCMClipEntry>();\n private metadata: AudioMetadata = { sampleRate: 48_000, numberOfChannels: 2 };\n private volume = 1;\n private muted = false;\n private maxClips = 20;\n\n attachStream(stream: ReadableStream<AudioData>, metadata: AudioMetadata): void {\n this.metadata = metadata;\n const reader = stream.getReader();\n\n const pump = async (): Promise<void> => {\n const { done, value } = await reader.read();\n if (done) {\n reader.releaseLock();\n return;\n }\n\n this.addAudio(value);\n await pump();\n };\n\n pump().catch((error) => {\n console.error('[AudioL1Cache] stream error', error);\n reader.releaseLock();\n });\n }\n\n putClipAudioData(\n clipId: string,\n audioData: AudioData,\n clipStartUs: TimeUs,\n clipDurationUs: TimeUs\n ): void {\n const numberOfChannels = audioData.numberOfChannels ?? this.metadata.numberOfChannels;\n const numberOfFrames = audioData.numberOfFrames ?? 0;\n const sampleRate = audioData.sampleRate ?? this.metadata.sampleRate;\n\n if (!numberOfChannels || !numberOfFrames) {\n audioData.close();\n return;\n }\n\n const planes = this.extractPlanesFromAudioData(audioData, numberOfChannels, numberOfFrames);\n audioData.close();\n\n let entry = this.clipPCM.get(clipId);\n if (!entry) {\n entry = {\n clipId,\n sampleRate,\n numberOfChannels,\n planes: Array.from({ length: numberOfChannels }, () => new Float32Array(0)),\n startUs: clipStartUs,\n durationUs: clipDurationUs,\n lastAccessedAt: Date.now(),\n };\n this.clipPCM.set(clipId, entry);\n\n if (this.clipPCM.size > this.maxClips) {\n this.evictLRU();\n }\n }\n\n for (let channel = 0; channel < numberOfChannels; channel++) {\n const existingPlane = entry.planes[channel];\n const newPlane = planes[channel];\n if (!existingPlane || !newPlane) continue;\n\n const combined = new Float32Array(existingPlane.length + newPlane.length);\n combined.set(existingPlane, 0);\n combined.set(newPlane, existingPlane.length);\n entry.planes[channel] = combined;\n }\n\n entry.lastAccessedAt = Date.now();\n }\n\n getPCM(clipId: string, startUs: TimeUs, endUs: TimeUs): Float32Array[] | null {\n const entry = this.clipPCM.get(clipId);\n if (!entry) {\n return null;\n }\n\n entry.lastAccessedAt = Date.now();\n\n const offsetUs = Math.max(0, startUs - entry.startUs);\n const durationUs = Math.min(endUs - startUs, entry.durationUs - offsetUs);\n\n if (durationUs <= 0) {\n return null;\n }\n\n const offsetFrames = Math.floor((offsetUs / 1_000_000) * entry.sampleRate);\n const frameCount = Math.ceil((durationUs / 1_000_000) * entry.sampleRate);\n\n const result: Float32Array[] = [];\n for (let channel = 0; channel < entry.numberOfChannels; channel++) {\n const plane = entry.planes[channel];\n if (!plane) {\n result.push(new Float32Array(frameCount));\n continue;\n }\n\n const channelData = new Float32Array(frameCount);\n const copyLength = Math.min(frameCount, plane.length - offsetFrames);\n for (let i = 0; i < copyLength; i++) {\n channelData[i] = plane[offsetFrames + i] ?? 0;\n }\n result.push(channelData);\n }\n\n return result;\n }\n\n hasClipPCM(clipId: string): boolean {\n return this.clipPCM.has(clipId);\n }\n\n clearClipPCM(clipId: string): void {\n this.clipPCM.delete(clipId);\n }\n\n private evictLRU(): void {\n let oldestClipId: string | null = null;\n let oldestTime = Date.now();\n\n for (const [clipId, entry] of this.clipPCM) {\n if (entry.lastAccessedAt < oldestTime) {\n oldestTime = entry.lastAccessedAt;\n oldestClipId = clipId;\n }\n }\n\n if (oldestClipId) {\n this.clipPCM.delete(oldestClipId);\n }\n }\n\n private extractPlanesFromAudioData(\n audioData: AudioData,\n numberOfChannels: number,\n numberOfFrames: number\n ): Float32Array[] {\n const planes: Float32Array[] = Array.from(\n { length: numberOfChannels },\n () => new Float32Array(numberOfFrames)\n );\n\n const toFloat = (value: number): number => value / 32768;\n\n const fillInterleaved = (format: 'f32' | 's16'): boolean => {\n const samples =\n format === 'f32'\n ? new Float32Array(numberOfFrames * numberOfChannels)\n : new Int16Array(numberOfFrames * numberOfChannels);\n\n try {\n audioData.copyTo(samples, { format, planeIndex: 0 });\n } catch {\n return false;\n }\n\n for (let frame = 0; frame < numberOfFrames; frame += 1) {\n const offset = frame * numberOfChannels;\n for (let channel = 0; channel < numberOfChannels; channel += 1) {\n const plane = planes[channel];\n if (!plane) continue;\n if (format === 'f32') {\n plane[frame] = (samples as Float32Array)[offset + channel] ?? 0;\n } else {\n plane[frame] = toFloat((samples as Int16Array)[offset + channel] ?? 0);\n }\n }\n }\n\n return true;\n };\n\n const fillPlanar = (format: 'f32-planar' | 's16-planar'): boolean => {\n try {\n if (format === 'f32-planar') {\n for (let channel = 0; channel < numberOfChannels; channel += 1) {\n const plane = planes[channel];\n if (!plane) continue;\n audioData.copyTo(plane, { planeIndex: channel, format: 'f32-planar' });\n }\n return true;\n }\n\n const tmp = new Int16Array(numberOfFrames);\n for (let channel = 0; channel < numberOfChannels; channel += 1) {\n const plane = planes[channel];\n if (!plane) continue;\n audioData.copyTo(tmp, { planeIndex: channel, format: 's16-planar' as any });\n for (let i = 0; i < numberOfFrames; i += 1) {\n plane[i] = toFloat(tmp[i] ?? 0);\n }\n }\n return true;\n } catch {\n return false;\n }\n };\n\n const fillFallback = (): boolean => {\n try {\n for (let channel = 0; channel < numberOfChannels; channel += 1) {\n const plane = planes[channel];\n if (!plane) continue;\n audioData.copyTo(plane, { planeIndex: channel });\n }\n return true;\n } catch {\n return false;\n }\n };\n\n const reportedFormat = (audioData as any).format as string | undefined;\n const attempts: Array<() => boolean> = [];\n const scheduled = new Set<string>();\n\n const scheduleAttempt = (token: string, attempt: () => boolean): void => {\n if (!scheduled.has(token)) {\n scheduled.add(token);\n attempts.push(attempt);\n }\n };\n\n if (reportedFormat) {\n switch (reportedFormat) {\n case 'f32':\n scheduleAttempt('f32', () => fillInterleaved('f32'));\n break;\n case 's16':\n scheduleAttempt('s16', () => fillInterleaved('s16'));\n break;\n case 'f32-planar':\n scheduleAttempt('f32-planar', () => fillPlanar('f32-planar'));\n break;\n case 's16-planar':\n scheduleAttempt('s16-planar', () => fillPlanar('s16-planar'));\n break;\n default:\n break;\n }\n }\n\n scheduleAttempt('f32', () => fillInterleaved('f32'));\n scheduleAttempt('f32-planar', () => fillPlanar('f32-planar'));\n scheduleAttempt('s16', () => fillInterleaved('s16'));\n scheduleAttempt('s16-planar', () => fillPlanar('s16-planar'));\n\n let filled = false;\n for (const attempt of attempts) {\n if (attempt()) {\n filled = true;\n break;\n }\n }\n\n if (!filled) {\n filled = fillFallback();\n }\n\n if (!filled) {\n throw new Error('AudioL1Cache: unsupported AudioData format');\n }\n\n return planes;\n }\n\n addAudio(audio: AudioData): void {\n const numberOfChannels = audio.numberOfChannels ?? this.metadata.numberOfChannels;\n const numberOfFrames = audio.numberOfFrames ?? 0;\n\n if (!numberOfChannels || !numberOfFrames) {\n audio.close();\n return;\n }\n\n if (audio.sampleRate && audio.sampleRate > 0 && audio.sampleRate !== this.metadata.sampleRate) {\n this.metadata = { ...this.metadata, sampleRate: audio.sampleRate };\n }\n\n if (numberOfChannels !== this.metadata.numberOfChannels) {\n this.metadata = { ...this.metadata, numberOfChannels };\n }\n\n const planes = this.extractPlanesFromAudioData(audio, numberOfChannels, numberOfFrames);\n\n this.slots.push({\n timestampUs: audio.timestamp ?? 0,\n durationUs:\n audio.duration ?? Math.round((numberOfFrames / this.metadata.sampleRate) * 1_000_000),\n planes,\n });\n\n audio.close();\n }\n\n getClosest(timeUs: number): (AudioSlot & { metadata: AudioMetadata }) | null {\n if (this.slots.length === 0) {\n return null;\n }\n\n let closest: AudioSlot | null = null;\n let minDelta = Number.MAX_SAFE_INTEGER;\n\n for (const slot of this.slots) {\n const start = slot.timestampUs;\n const end = start + slot.durationUs;\n if (timeUs >= start && timeUs <= end) {\n closest = slot;\n break;\n }\n\n const delta = Math.min(Math.abs(timeUs - start), Math.abs(timeUs - end));\n if (delta < minDelta) {\n closest = slot;\n minDelta = delta;\n }\n }\n\n if (!closest) {\n return null;\n }\n\n return {\n ...closest,\n planes: this.applyGain(closest.planes),\n metadata: this.metadata,\n };\n }\n\n flush(): void {\n this.slots = [];\n }\n\n reset(): void {\n this.flush();\n this.clipPCM.clear();\n this.metadata = { sampleRate: 48_000, numberOfChannels: 2 };\n this.volume = 1;\n this.muted = false;\n }\n\n setPlaybackRate(_rate: number): void {\n // Reserved for future use\n }\n\n setVolume(volume: number): void {\n this.volume = Math.max(0, Math.min(1, volume));\n }\n\n setMute(muted: boolean): void {\n this.muted = muted;\n }\n\n dispose(): void {\n this.reset();\n }\n\n private applyGain(planes: Float32Array[]): Float32Array[] {\n if (this.muted || this.volume === 0) {\n return planes.map((plane) => new Float32Array(plane.length));\n }\n\n if (this.volume === 1) {\n return planes.map((plane) => plane.slice());\n }\n\n return planes.map((plane) => {\n const scaled = new Float32Array(plane.length);\n for (let i = 0; i < plane.length; i += 1) {\n scaled[i] = (plane[i] ?? 0) * this.volume;\n }\n return scaled;\n });\n }\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { TimeUs } from '../../model/types';
|
|
2
|
+
|
|
3
|
+
export declare class MixedAudioL1Cache {
|
|
4
|
+
private windows;
|
|
5
|
+
private maxWindows;
|
|
6
|
+
putMixed(startUs: TimeUs, endUs: TimeUs, buffer: AudioBuffer): void;
|
|
7
|
+
getMixed(startUs: TimeUs, endUs: TimeUs): AudioBuffer | null;
|
|
8
|
+
hasMixed(startUs: TimeUs, endUs: TimeUs): boolean;
|
|
9
|
+
clear(): void;
|
|
10
|
+
private getWindowKey;
|
|
11
|
+
private evictLRU;
|
|
12
|
+
}
|
|
13
|
+
//# sourceMappingURL=MixedAudioL1Cache.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MixedAudioL1Cache.d.ts","sourceRoot":"","sources":["../../../src/cache/l1/MixedAudioL1Cache.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAShD,qBAAa,iBAAiB;IAC5B,OAAO,CAAC,OAAO,CAAuC;IACtD,OAAO,CAAC,UAAU,CAAK;IAEvB,QAAQ,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,GAAG,IAAI;IAcnE,QAAQ,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,WAAW,GAAG,IAAI;IAW5D,QAAQ,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO;IAKjD,KAAK,IAAI,IAAI;IAIb,OAAO,CAAC,YAAY;IAIpB,OAAO,CAAC,QAAQ;CAejB"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
class MixedAudioL1Cache {
|
|
2
|
+
windows = /* @__PURE__ */ new Map();
|
|
3
|
+
maxWindows = 4;
|
|
4
|
+
putMixed(startUs, endUs, buffer) {
|
|
5
|
+
const key = this.getWindowKey(startUs, endUs);
|
|
6
|
+
this.windows.set(key, {
|
|
7
|
+
startUs,
|
|
8
|
+
endUs,
|
|
9
|
+
buffer,
|
|
10
|
+
lastAccessedAt: Date.now()
|
|
11
|
+
});
|
|
12
|
+
if (this.windows.size > this.maxWindows) {
|
|
13
|
+
this.evictLRU();
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
getMixed(startUs, endUs) {
|
|
17
|
+
const key = this.getWindowKey(startUs, endUs);
|
|
18
|
+
const entry = this.windows.get(key);
|
|
19
|
+
if (!entry) {
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
entry.lastAccessedAt = Date.now();
|
|
23
|
+
return entry.buffer;
|
|
24
|
+
}
|
|
25
|
+
hasMixed(startUs, endUs) {
|
|
26
|
+
const key = this.getWindowKey(startUs, endUs);
|
|
27
|
+
return this.windows.has(key);
|
|
28
|
+
}
|
|
29
|
+
clear() {
|
|
30
|
+
this.windows.clear();
|
|
31
|
+
}
|
|
32
|
+
getWindowKey(startUs, endUs) {
|
|
33
|
+
return `${startUs}-${endUs}`;
|
|
34
|
+
}
|
|
35
|
+
evictLRU() {
|
|
36
|
+
let oldestKey = null;
|
|
37
|
+
let oldestTime = Date.now();
|
|
38
|
+
for (const [key, entry] of this.windows) {
|
|
39
|
+
if (entry.lastAccessedAt < oldestTime) {
|
|
40
|
+
oldestTime = entry.lastAccessedAt;
|
|
41
|
+
oldestKey = key;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
if (oldestKey) {
|
|
45
|
+
this.windows.delete(oldestKey);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
export {
|
|
50
|
+
MixedAudioL1Cache
|
|
51
|
+
};
|
|
52
|
+
//# sourceMappingURL=MixedAudioL1Cache.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MixedAudioL1Cache.js","sources":["../../../src/cache/l1/MixedAudioL1Cache.ts"],"sourcesContent":["import type { TimeUs } from '../../model/types';\n\ninterface MixedWindowEntry {\n startUs: TimeUs;\n endUs: TimeUs;\n buffer: AudioBuffer;\n lastAccessedAt: number;\n}\n\nexport class MixedAudioL1Cache {\n private windows = new Map<string, MixedWindowEntry>();\n private maxWindows = 4;\n\n putMixed(startUs: TimeUs, endUs: TimeUs, buffer: AudioBuffer): void {\n const key = this.getWindowKey(startUs, endUs);\n this.windows.set(key, {\n startUs,\n endUs,\n buffer,\n lastAccessedAt: Date.now(),\n });\n\n if (this.windows.size > this.maxWindows) {\n this.evictLRU();\n }\n }\n\n getMixed(startUs: TimeUs, endUs: TimeUs): AudioBuffer | null {\n const key = this.getWindowKey(startUs, endUs);\n const entry = this.windows.get(key);\n if (!entry) {\n return null;\n }\n\n entry.lastAccessedAt = Date.now();\n return entry.buffer;\n }\n\n hasMixed(startUs: TimeUs, endUs: TimeUs): boolean {\n const key = this.getWindowKey(startUs, endUs);\n return this.windows.has(key);\n }\n\n clear(): void {\n this.windows.clear();\n }\n\n private getWindowKey(startUs: TimeUs, endUs: TimeUs): string {\n return `${startUs}-${endUs}`;\n }\n\n private evictLRU(): void {\n let oldestKey: string | null = null;\n let oldestTime = Date.now();\n\n for (const [key, entry] of this.windows) {\n if (entry.lastAccessedAt < oldestTime) {\n oldestTime = entry.lastAccessedAt;\n oldestKey = key;\n }\n }\n\n if (oldestKey) {\n this.windows.delete(oldestKey);\n }\n }\n}\n"],"names":[],"mappings":"AASO,MAAM,kBAAkB;AAAA,EACrB,8BAAc,IAAA;AAAA,EACd,aAAa;AAAA,EAErB,SAAS,SAAiB,OAAe,QAA2B;AAClE,UAAM,MAAM,KAAK,aAAa,SAAS,KAAK;AAC5C,SAAK,QAAQ,IAAI,KAAK;AAAA,MACpB;AAAA,MACA;AAAA,MACA;AAAA,MACA,gBAAgB,KAAK,IAAA;AAAA,IAAI,CAC1B;AAED,QAAI,KAAK,QAAQ,OAAO,KAAK,YAAY;AACvC,WAAK,SAAA;AAAA,IACP;AAAA,EACF;AAAA,EAEA,SAAS,SAAiB,OAAmC;AAC3D,UAAM,MAAM,KAAK,aAAa,SAAS,KAAK;AAC5C,UAAM,QAAQ,KAAK,QAAQ,IAAI,GAAG;AAClC,QAAI,CAAC,OAAO;AACV,aAAO;AAAA,IACT;AAEA,UAAM,iBAAiB,KAAK,IAAA;AAC5B,WAAO,MAAM;AAAA,EACf;AAAA,EAEA,SAAS,SAAiB,OAAwB;AAChD,UAAM,MAAM,KAAK,aAAa,SAAS,KAAK;AAC5C,WAAO,KAAK,QAAQ,IAAI,GAAG;AAAA,EAC7B;AAAA,EAEA,QAAc;AACZ,SAAK,QAAQ,MAAA;AAAA,EACf;AAAA,EAEQ,aAAa,SAAiB,OAAuB;AAC3D,WAAO,GAAG,OAAO,IAAI,KAAK;AAAA,EAC5B;AAAA,EAEQ,WAAiB;AACvB,QAAI,YAA2B;AAC/B,QAAI,aAAa,KAAK,IAAA;AAEtB,eAAW,CAAC,KAAK,KAAK,KAAK,KAAK,SAAS;AACvC,UAAI,MAAM,iBAAiB,YAAY;AACrC,qBAAa,MAAM;AACnB,oBAAY;AAAA,MACd;AAAA,IACF;AAEA,QAAI,WAAW;AACb,WAAK,QAAQ,OAAO,SAAS;AAAA,IAC/B;AAAA,EACF;AACF;"}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { TimeUs } from '../../model/types';
|
|
2
|
+
import { GOP } from '../types';
|
|
3
|
+
import { RcFrame } from '../../model';
|
|
4
|
+
|
|
5
|
+
interface L1Config {
|
|
6
|
+
maxMemoryMB: number;
|
|
7
|
+
maxGOPs?: number;
|
|
8
|
+
gopIntervalUs?: number;
|
|
9
|
+
}
|
|
10
|
+
export interface L1CacheMetadata {
|
|
11
|
+
size: number;
|
|
12
|
+
maxSize: number;
|
|
13
|
+
entries: number;
|
|
14
|
+
clipCount: number;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Simplified VideoL1Cache for 3-Clip strategy
|
|
18
|
+
*
|
|
19
|
+
* Clip lifecycle is managed by ClipSessionManager:
|
|
20
|
+
* - No LRU eviction (clips evicted explicitly)
|
|
21
|
+
* - No capacity limits (fixed 3 clips)
|
|
22
|
+
* - Simple GOP storage per clip
|
|
23
|
+
*/
|
|
24
|
+
export declare class VideoL1Cache {
|
|
25
|
+
private readonly entriesByClip;
|
|
26
|
+
private maxMemoryBytes;
|
|
27
|
+
private gopIntervalUs;
|
|
28
|
+
private currentBytes;
|
|
29
|
+
constructor(config: L1Config);
|
|
30
|
+
get(timeUs: TimeUs, clipId: string): RcFrame | null;
|
|
31
|
+
putGOP(gop: GOP, clipId: string): void;
|
|
32
|
+
addFrame(frame: VideoFrame, clipId: string, frameDuration: TimeUs, gopSerial?: number, isKeyframe?: boolean): RcFrame | null;
|
|
33
|
+
/**
|
|
34
|
+
* Evict all cache entries for a specific clip
|
|
35
|
+
*/
|
|
36
|
+
evictClip(clipId: string): void;
|
|
37
|
+
/**
|
|
38
|
+
* Check if a clip has any cached entries
|
|
39
|
+
*/
|
|
40
|
+
isClipCached(clipId: string): boolean;
|
|
41
|
+
/**
|
|
42
|
+
* Get total frame count for a clip
|
|
43
|
+
*/
|
|
44
|
+
getClipFrameCount(clipId: string): number;
|
|
45
|
+
invalidateRange(startUs: TimeUs, endUs: TimeUs, clipId?: string): void;
|
|
46
|
+
clear(): void;
|
|
47
|
+
getMetadata(): L1CacheMetadata;
|
|
48
|
+
private registerEntry;
|
|
49
|
+
private createEntry;
|
|
50
|
+
private mergeFrames;
|
|
51
|
+
private insertFrame;
|
|
52
|
+
private removeEntry;
|
|
53
|
+
private closeFrames;
|
|
54
|
+
private composeKey;
|
|
55
|
+
private wrapFrame;
|
|
56
|
+
private wrapFrames;
|
|
57
|
+
private getEntryExact;
|
|
58
|
+
private findEntryByTime;
|
|
59
|
+
private countEntries;
|
|
60
|
+
private iterateEntries;
|
|
61
|
+
private ensureClipEntries;
|
|
62
|
+
private findInsertIndex;
|
|
63
|
+
private findFrameInsertIndex;
|
|
64
|
+
private normalizeFrames;
|
|
65
|
+
private updateEntryStats;
|
|
66
|
+
private deriveFallbackDuration;
|
|
67
|
+
}
|
|
68
|
+
export {};
|
|
69
|
+
//# sourceMappingURL=VideoL1Cache.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"VideoL1Cache.d.ts","sourceRoot":"","sources":["../../../src/cache/l1/VideoL1Cache.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,UAAU,CAAC;AACpC,OAAO,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAMtC,UAAU,QAAQ;IAChB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAYD,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;;;;;;GAOG;AACH,qBAAa,YAAY;IACvB,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAqC;IACnE,OAAO,CAAC,cAAc,CAAS;IAC/B,OAAO,CAAC,aAAa,CAAS;IAC9B,OAAO,CAAC,YAAY,CAAK;gBAEb,MAAM,EAAE,QAAQ;IAK5B,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,GAAG,IAAI;IA6BnD,MAAM,CAAC,GAAG,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI;IAwBtC,QAAQ,CACN,KAAK,EAAE,UAAU,EACjB,MAAM,EAAE,MAAM,EACd,aAAa,EAAE,MAAM,EACrB,SAAS,CAAC,EAAE,MAAM,EAClB,UAAU,CAAC,EAAE,OAAO,GACnB,OAAO,GAAG,IAAI;IA0BjB;;OAEG;IACH,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IAY/B;;OAEG;IACH,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO;IAKrC;;OAEG;IACH,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM;IAMzC,eAAe,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI;IAatE,KAAK,IAAI,IAAI;IAUb,WAAW,IAAI,eAAe;IAS9B,OAAO,CAAC,aAAa;IAMrB,OAAO,CAAC,WAAW;IAyBnB,OAAO,CAAC,WAAW;IAWnB,OAAO,CAAC,WAAW;IAqBnB,OAAO,CAAC,WAAW;IAgBnB,OAAO,CAAC,WAAW;IAMnB,OAAO,CAAC,UAAU;IAIlB,OAAO,CAAC,SAAS;IAiBjB,OAAO,CAAC,UAAU;IAalB,OAAO,CAAC,aAAa;IAMrB,OAAO,CAAC,eAAe;IAqBvB,OAAO,CAAC,YAAY;IAQpB,OAAO,CAAC,cAAc;IAQtB,OAAO,CAAC,iBAAiB;IASzB,OAAO,CAAC,eAAe;IAevB,OAAO,CAAC,oBAAoB;IAe5B,OAAO,CAAC,eAAe;IAgBvB,OAAO,CAAC,gBAAgB;IAQxB,OAAO,CAAC,sBAAsB;CAM/B"}
|
|
@@ -0,0 +1,318 @@
|
|
|
1
|
+
import { RcFrame } from "../../model/RcFrame.js";
|
|
2
|
+
import { findFrameIndex, findGopIndex } from "./gop-utils.js";
|
|
3
|
+
const DEFAULT_GOP_INTERVAL_US = 2e6;
|
|
4
|
+
const BYTES_PER_MB = 1024 * 1024;
|
|
5
|
+
class VideoL1Cache {
|
|
6
|
+
entriesByClip = /* @__PURE__ */ new Map();
|
|
7
|
+
maxMemoryBytes;
|
|
8
|
+
gopIntervalUs;
|
|
9
|
+
currentBytes = 0;
|
|
10
|
+
constructor(config) {
|
|
11
|
+
this.maxMemoryBytes = config.maxMemoryMB * BYTES_PER_MB;
|
|
12
|
+
this.gopIntervalUs = config.gopIntervalUs ?? DEFAULT_GOP_INTERVAL_US;
|
|
13
|
+
}
|
|
14
|
+
get(timeUs, clipId) {
|
|
15
|
+
const clipEntries = this.entriesByClip.get(clipId);
|
|
16
|
+
if (!clipEntries || clipEntries.length === 0) {
|
|
17
|
+
return null;
|
|
18
|
+
}
|
|
19
|
+
const entry = this.findEntryByTime(clipEntries, timeUs);
|
|
20
|
+
if (!entry) {
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
23
|
+
const frameIndex = findFrameIndex(
|
|
24
|
+
{
|
|
25
|
+
index: entry.gopIndex,
|
|
26
|
+
startUs: entry.gopStartUs,
|
|
27
|
+
durationUs: entry.durationUs,
|
|
28
|
+
frames: entry.frames,
|
|
29
|
+
clipId: entry.clipId
|
|
30
|
+
},
|
|
31
|
+
timeUs
|
|
32
|
+
);
|
|
33
|
+
if (frameIndex === -1) {
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
return entry.frames[frameIndex] ?? null;
|
|
37
|
+
}
|
|
38
|
+
putGOP(gop, clipId) {
|
|
39
|
+
const gopIndex = gop.index ?? gop.startUs;
|
|
40
|
+
const existing = this.getEntryExact(clipId, gopIndex);
|
|
41
|
+
const frames = this.wrapFrames(gop.frames, clipId, gop.durationUs);
|
|
42
|
+
if (existing) {
|
|
43
|
+
existing.gopStartUs = gop.startUs;
|
|
44
|
+
this.mergeFrames(existing, frames, gop.durationUs);
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
const entry = this.createEntry(clipId, {
|
|
48
|
+
gopIndex,
|
|
49
|
+
startUs: gop.startUs,
|
|
50
|
+
durationUs: gop.durationUs,
|
|
51
|
+
frames,
|
|
52
|
+
isKeyframe: gop.isKeyframe
|
|
53
|
+
});
|
|
54
|
+
this.registerEntry(entry);
|
|
55
|
+
this.currentBytes += entry.size;
|
|
56
|
+
}
|
|
57
|
+
addFrame(frame, clipId, frameDuration, gopSerial, isKeyframe) {
|
|
58
|
+
const timestamp = frame.timestamp ?? 0;
|
|
59
|
+
const gopIndex = typeof gopSerial === "number" ? gopSerial : findGopIndex(timestamp, this.gopIntervalUs);
|
|
60
|
+
const existing = this.getEntryExact(clipId, gopIndex);
|
|
61
|
+
const rcFrame = this.wrapFrame(frame, clipId, frameDuration, gopSerial, isKeyframe);
|
|
62
|
+
if (existing) {
|
|
63
|
+
this.mergeFrames(existing, [rcFrame], frameDuration);
|
|
64
|
+
return rcFrame;
|
|
65
|
+
}
|
|
66
|
+
const entry = this.createEntry(clipId, {
|
|
67
|
+
gopIndex,
|
|
68
|
+
startUs: timestamp,
|
|
69
|
+
durationUs: frameDuration,
|
|
70
|
+
frames: [rcFrame],
|
|
71
|
+
isKeyframe: isKeyframe ?? true
|
|
72
|
+
});
|
|
73
|
+
this.registerEntry(entry);
|
|
74
|
+
this.currentBytes += entry.size;
|
|
75
|
+
return rcFrame;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Evict all cache entries for a specific clip
|
|
79
|
+
*/
|
|
80
|
+
evictClip(clipId) {
|
|
81
|
+
const entries = this.entriesByClip.get(clipId);
|
|
82
|
+
if (!entries) return;
|
|
83
|
+
for (const entry of entries) {
|
|
84
|
+
this.closeFrames(entry);
|
|
85
|
+
this.currentBytes -= entry.size;
|
|
86
|
+
}
|
|
87
|
+
this.entriesByClip.delete(clipId);
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Check if a clip has any cached entries
|
|
91
|
+
*/
|
|
92
|
+
isClipCached(clipId) {
|
|
93
|
+
const entries = this.entriesByClip.get(clipId);
|
|
94
|
+
return !!entries && entries.length > 0;
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Get total frame count for a clip
|
|
98
|
+
*/
|
|
99
|
+
getClipFrameCount(clipId) {
|
|
100
|
+
const entries = this.entriesByClip.get(clipId);
|
|
101
|
+
if (!entries) return 0;
|
|
102
|
+
return entries.reduce((sum, entry) => sum + entry.frames.length, 0);
|
|
103
|
+
}
|
|
104
|
+
invalidateRange(startUs, endUs, clipId) {
|
|
105
|
+
for (const entry of this.iterateEntries()) {
|
|
106
|
+
if (clipId && entry.clipId !== clipId) {
|
|
107
|
+
continue;
|
|
108
|
+
}
|
|
109
|
+
const gopEnd = entry.gopStartUs + entry.durationUs;
|
|
110
|
+
if (entry.gopStartUs < endUs && gopEnd > startUs) {
|
|
111
|
+
this.removeEntry(entry);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
clear() {
|
|
116
|
+
for (const entries of this.entriesByClip.values()) {
|
|
117
|
+
for (const entry of entries) {
|
|
118
|
+
this.closeFrames(entry);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
this.entriesByClip.clear();
|
|
122
|
+
this.currentBytes = 0;
|
|
123
|
+
}
|
|
124
|
+
getMetadata() {
|
|
125
|
+
return {
|
|
126
|
+
size: this.currentBytes,
|
|
127
|
+
maxSize: this.maxMemoryBytes,
|
|
128
|
+
entries: this.countEntries(),
|
|
129
|
+
clipCount: this.entriesByClip.size
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
registerEntry(entry) {
|
|
133
|
+
const entries = this.ensureClipEntries(entry.clipId);
|
|
134
|
+
const insertIndex = this.findInsertIndex(entries, entry.gopIndex);
|
|
135
|
+
entries.splice(insertIndex, 0, entry);
|
|
136
|
+
}
|
|
137
|
+
createEntry(clipId, gop) {
|
|
138
|
+
const frames = this.normalizeFrames(gop.frames);
|
|
139
|
+
const entry = {
|
|
140
|
+
key: this.composeKey(clipId, gop.gopIndex),
|
|
141
|
+
clipId,
|
|
142
|
+
gopIndex: gop.gopIndex,
|
|
143
|
+
gopStartUs: gop.startUs,
|
|
144
|
+
durationUs: gop.durationUs,
|
|
145
|
+
frames,
|
|
146
|
+
size: 0
|
|
147
|
+
};
|
|
148
|
+
this.updateEntryStats(entry, this.deriveFallbackDuration(gop.durationUs, frames.length));
|
|
149
|
+
return entry;
|
|
150
|
+
}
|
|
151
|
+
mergeFrames(entry, frames, fallbackDuration) {
|
|
152
|
+
const durationFallback = this.deriveFallbackDuration(
|
|
153
|
+
fallbackDuration,
|
|
154
|
+
entry.frames.length + frames.length
|
|
155
|
+
);
|
|
156
|
+
for (const rcFrame of frames) {
|
|
157
|
+
this.insertFrame(entry, rcFrame, durationFallback);
|
|
158
|
+
}
|
|
159
|
+
this.updateEntryStats(entry, durationFallback);
|
|
160
|
+
}
|
|
161
|
+
insertFrame(entry, frame, fallbackDuration) {
|
|
162
|
+
const timestamp = frame.timestampUs ?? entry.gopStartUs;
|
|
163
|
+
const frames = entry.frames;
|
|
164
|
+
const insertIndex = this.findFrameInsertIndex(frames, timestamp);
|
|
165
|
+
if (insertIndex < frames.length && (frames[insertIndex]?.timestampUs ?? entry.gopStartUs) === timestamp) {
|
|
166
|
+
const oldFrame = frames[insertIndex];
|
|
167
|
+
frames[insertIndex] = frame;
|
|
168
|
+
oldFrame?.close?.();
|
|
169
|
+
} else {
|
|
170
|
+
frames.splice(insertIndex, 0, frame);
|
|
171
|
+
}
|
|
172
|
+
entry.size += frame.sizeEstimate;
|
|
173
|
+
const duration = frame.durationUs || fallbackDuration;
|
|
174
|
+
entry.durationUs = Math.max(entry.durationUs, timestamp + duration - entry.gopStartUs);
|
|
175
|
+
}
|
|
176
|
+
removeEntry(entry) {
|
|
177
|
+
const clipEntries = this.entriesByClip.get(entry.clipId);
|
|
178
|
+
if (clipEntries) {
|
|
179
|
+
const index = clipEntries.findIndex((item) => item.gopIndex === entry.gopIndex);
|
|
180
|
+
if (index !== -1) {
|
|
181
|
+
clipEntries.splice(index, 1);
|
|
182
|
+
}
|
|
183
|
+
if (clipEntries.length === 0) {
|
|
184
|
+
this.entriesByClip.delete(entry.clipId);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
this.closeFrames(entry);
|
|
188
|
+
this.currentBytes -= entry.size;
|
|
189
|
+
}
|
|
190
|
+
closeFrames(entry) {
|
|
191
|
+
for (const frame of entry.frames) {
|
|
192
|
+
frame?.close?.();
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
composeKey(clipId, gopIndex) {
|
|
196
|
+
return `${clipId}:${gopIndex}`;
|
|
197
|
+
}
|
|
198
|
+
wrapFrame(frame, clipId, frameDuration, gopSerial, isKeyframe) {
|
|
199
|
+
return RcFrame.wrap(frame, {
|
|
200
|
+
trackId: "main",
|
|
201
|
+
clipId,
|
|
202
|
+
timestampUs: frame.timestamp ?? 0,
|
|
203
|
+
durationUs: frame.duration ?? frameDuration,
|
|
204
|
+
gopSerial,
|
|
205
|
+
isKeyframe
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
wrapFrames(frames, clipId, fallbackDuration) {
|
|
209
|
+
const wrapped = frames.map(
|
|
210
|
+
(frame) => frame instanceof RcFrame ? frame : this.wrapFrame(frame, clipId, fallbackDuration / Math.max(frames.length, 1))
|
|
211
|
+
);
|
|
212
|
+
return this.normalizeFrames(wrapped);
|
|
213
|
+
}
|
|
214
|
+
getEntryExact(clipId, gopIndex) {
|
|
215
|
+
const entries = this.entriesByClip.get(clipId);
|
|
216
|
+
if (!entries) return void 0;
|
|
217
|
+
return entries.find((e) => e.gopIndex === gopIndex);
|
|
218
|
+
}
|
|
219
|
+
findEntryByTime(entries, timeUs) {
|
|
220
|
+
let low = 0;
|
|
221
|
+
let high = entries.length - 1;
|
|
222
|
+
while (low <= high) {
|
|
223
|
+
const mid = Math.floor((low + high) / 2);
|
|
224
|
+
const entry = entries[mid];
|
|
225
|
+
if (!entry) break;
|
|
226
|
+
if (timeUs < entry.gopStartUs) {
|
|
227
|
+
high = mid - 1;
|
|
228
|
+
} else if (timeUs >= entry.gopStartUs + entry.durationUs) {
|
|
229
|
+
low = mid + 1;
|
|
230
|
+
} else {
|
|
231
|
+
return entry;
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
return void 0;
|
|
235
|
+
}
|
|
236
|
+
countEntries() {
|
|
237
|
+
let count = 0;
|
|
238
|
+
for (const clipEntries of this.entriesByClip.values()) {
|
|
239
|
+
count += clipEntries.length;
|
|
240
|
+
}
|
|
241
|
+
return count;
|
|
242
|
+
}
|
|
243
|
+
iterateEntries() {
|
|
244
|
+
const entries = [];
|
|
245
|
+
for (const clipEntries of this.entriesByClip.values()) {
|
|
246
|
+
entries.push(...clipEntries);
|
|
247
|
+
}
|
|
248
|
+
return entries;
|
|
249
|
+
}
|
|
250
|
+
ensureClipEntries(clipId) {
|
|
251
|
+
let entries = this.entriesByClip.get(clipId);
|
|
252
|
+
if (!entries) {
|
|
253
|
+
entries = [];
|
|
254
|
+
this.entriesByClip.set(clipId, entries);
|
|
255
|
+
}
|
|
256
|
+
return entries;
|
|
257
|
+
}
|
|
258
|
+
findInsertIndex(entries, gopIndex) {
|
|
259
|
+
let low = 0;
|
|
260
|
+
let high = entries.length;
|
|
261
|
+
while (low < high) {
|
|
262
|
+
const mid = Math.floor((low + high) / 2);
|
|
263
|
+
const entry = entries[mid];
|
|
264
|
+
if (entry && entry.gopIndex < gopIndex) {
|
|
265
|
+
low = mid + 1;
|
|
266
|
+
} else {
|
|
267
|
+
high = mid;
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
return low;
|
|
271
|
+
}
|
|
272
|
+
findFrameInsertIndex(frames, timestamp) {
|
|
273
|
+
let low = 0;
|
|
274
|
+
let high = frames.length;
|
|
275
|
+
while (low < high) {
|
|
276
|
+
const mid = Math.floor((low + high) / 2);
|
|
277
|
+
const midTs = frames[mid]?.timestampUs ?? 0;
|
|
278
|
+
if (midTs < timestamp) {
|
|
279
|
+
low = mid + 1;
|
|
280
|
+
} else {
|
|
281
|
+
high = mid;
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
return low;
|
|
285
|
+
}
|
|
286
|
+
normalizeFrames(frames) {
|
|
287
|
+
const seen = /* @__PURE__ */ new Set();
|
|
288
|
+
const sorted = [...frames].sort((a, b) => (a.timestampUs ?? 0) - (b.timestampUs ?? 0));
|
|
289
|
+
const result = [];
|
|
290
|
+
for (const frame of sorted) {
|
|
291
|
+
const ts = frame.timestampUs ?? 0;
|
|
292
|
+
if (seen.has(ts)) {
|
|
293
|
+
frame?.close?.();
|
|
294
|
+
} else {
|
|
295
|
+
seen.add(ts);
|
|
296
|
+
result.push(frame);
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
return result;
|
|
300
|
+
}
|
|
301
|
+
updateEntryStats(entry, fallbackDuration) {
|
|
302
|
+
entry.size = entry.frames.reduce((acc, frame) => acc + frame.sizeEstimate, 0);
|
|
303
|
+
entry.durationUs = entry.frames.reduce((acc, frame) => {
|
|
304
|
+
const duration = frame.durationUs || fallbackDuration;
|
|
305
|
+
return Math.max(acc, (frame.timestampUs ?? entry.gopStartUs) + duration - entry.gopStartUs);
|
|
306
|
+
}, entry.durationUs);
|
|
307
|
+
}
|
|
308
|
+
deriveFallbackDuration(durationUs, frameCount) {
|
|
309
|
+
if (frameCount <= 1) {
|
|
310
|
+
return durationUs || this.gopIntervalUs;
|
|
311
|
+
}
|
|
312
|
+
return Math.max(durationUs / frameCount, this.gopIntervalUs / frameCount);
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
export {
|
|
316
|
+
VideoL1Cache
|
|
317
|
+
};
|
|
318
|
+
//# sourceMappingURL=VideoL1Cache.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"VideoL1Cache.js","sources":["../../../src/cache/l1/VideoL1Cache.ts"],"sourcesContent":["import type { TimeUs } from '../../model/types';\nimport type { GOP } from '../types';\nimport { RcFrame } from '../../model';\nimport { findFrameIndex, findGopIndex } from './gop-utils';\n\nconst DEFAULT_GOP_INTERVAL_US = 2_000_000;\nconst BYTES_PER_MB = 1024 * 1024;\n\ninterface L1Config {\n maxMemoryMB: number;\n maxGOPs?: number;\n gopIntervalUs?: number;\n}\n\ninterface L1CacheEntry {\n key: string;\n clipId: string;\n gopIndex: number;\n gopStartUs: TimeUs;\n durationUs: TimeUs;\n frames: RcFrame[];\n size: number;\n}\n\nexport interface L1CacheMetadata {\n size: number;\n maxSize: number;\n entries: number;\n clipCount: number;\n}\n\n/**\n * Simplified VideoL1Cache for 3-Clip strategy\n *\n * Clip lifecycle is managed by ClipSessionManager:\n * - No LRU eviction (clips evicted explicitly)\n * - No capacity limits (fixed 3 clips)\n * - Simple GOP storage per clip\n */\nexport class VideoL1Cache {\n private readonly entriesByClip = new Map<string, L1CacheEntry[]>();\n private maxMemoryBytes: number;\n private gopIntervalUs: number;\n private currentBytes = 0;\n\n constructor(config: L1Config) {\n this.maxMemoryBytes = config.maxMemoryMB * BYTES_PER_MB;\n this.gopIntervalUs = config.gopIntervalUs ?? DEFAULT_GOP_INTERVAL_US;\n }\n\n get(timeUs: TimeUs, clipId: string): RcFrame | null {\n const clipEntries = this.entriesByClip.get(clipId);\n if (!clipEntries || clipEntries.length === 0) {\n return null;\n }\n\n const entry = this.findEntryByTime(clipEntries, timeUs);\n if (!entry) {\n return null;\n }\n\n const frameIndex = findFrameIndex(\n {\n index: entry.gopIndex,\n startUs: entry.gopStartUs,\n durationUs: entry.durationUs,\n frames: entry.frames,\n isKeyframe: true,\n clipId: entry.clipId,\n },\n timeUs\n );\n if (frameIndex === -1) {\n return null;\n }\n\n return entry.frames[frameIndex] ?? null;\n }\n\n putGOP(gop: GOP, clipId: string): void {\n const gopIndex = gop.index ?? gop.startUs;\n const existing = this.getEntryExact(clipId, gopIndex);\n\n const frames = this.wrapFrames(gop.frames, clipId, gop.durationUs);\n\n if (existing) {\n existing.gopStartUs = gop.startUs;\n this.mergeFrames(existing, frames, gop.durationUs);\n return;\n }\n\n const entry = this.createEntry(clipId, {\n gopIndex,\n startUs: gop.startUs,\n durationUs: gop.durationUs,\n frames,\n isKeyframe: gop.isKeyframe,\n });\n\n this.registerEntry(entry);\n this.currentBytes += entry.size;\n }\n\n addFrame(\n frame: VideoFrame,\n clipId: string,\n frameDuration: TimeUs,\n gopSerial?: number,\n isKeyframe?: boolean\n ): RcFrame | null {\n const timestamp = frame.timestamp ?? 0;\n const gopIndex =\n typeof gopSerial === 'number' ? gopSerial : findGopIndex(timestamp, this.gopIntervalUs);\n const existing = this.getEntryExact(clipId, gopIndex);\n\n const rcFrame = this.wrapFrame(frame, clipId, frameDuration, gopSerial, isKeyframe);\n\n if (existing) {\n this.mergeFrames(existing, [rcFrame], frameDuration);\n return rcFrame;\n }\n\n const entry = this.createEntry(clipId, {\n gopIndex,\n startUs: timestamp,\n durationUs: frameDuration,\n frames: [rcFrame],\n isKeyframe: isKeyframe ?? true,\n });\n\n this.registerEntry(entry);\n this.currentBytes += entry.size;\n return rcFrame;\n }\n\n /**\n * Evict all cache entries for a specific clip\n */\n evictClip(clipId: string): void {\n const entries = this.entriesByClip.get(clipId);\n if (!entries) return;\n\n for (const entry of entries) {\n this.closeFrames(entry);\n this.currentBytes -= entry.size;\n }\n\n this.entriesByClip.delete(clipId);\n }\n\n /**\n * Check if a clip has any cached entries\n */\n isClipCached(clipId: string): boolean {\n const entries = this.entriesByClip.get(clipId);\n return !!entries && entries.length > 0;\n }\n\n /**\n * Get total frame count for a clip\n */\n getClipFrameCount(clipId: string): number {\n const entries = this.entriesByClip.get(clipId);\n if (!entries) return 0;\n return entries.reduce((sum, entry) => sum + entry.frames.length, 0);\n }\n\n invalidateRange(startUs: TimeUs, endUs: TimeUs, clipId?: string): void {\n for (const entry of this.iterateEntries()) {\n if (clipId && entry.clipId !== clipId) {\n continue;\n }\n\n const gopEnd = entry.gopStartUs + entry.durationUs;\n if (entry.gopStartUs < endUs && gopEnd > startUs) {\n this.removeEntry(entry);\n }\n }\n }\n\n clear(): void {\n for (const entries of this.entriesByClip.values()) {\n for (const entry of entries) {\n this.closeFrames(entry);\n }\n }\n this.entriesByClip.clear();\n this.currentBytes = 0;\n }\n\n getMetadata(): L1CacheMetadata {\n return {\n size: this.currentBytes,\n maxSize: this.maxMemoryBytes,\n entries: this.countEntries(),\n clipCount: this.entriesByClip.size,\n };\n }\n\n private registerEntry(entry: L1CacheEntry): void {\n const entries = this.ensureClipEntries(entry.clipId);\n const insertIndex = this.findInsertIndex(entries, entry.gopIndex);\n entries.splice(insertIndex, 0, entry);\n }\n\n private createEntry(\n clipId: string,\n gop: {\n gopIndex: number;\n startUs: TimeUs;\n durationUs: TimeUs;\n frames: RcFrame[];\n isKeyframe: boolean;\n }\n ): L1CacheEntry {\n const frames = this.normalizeFrames(gop.frames);\n const entry: L1CacheEntry = {\n key: this.composeKey(clipId, gop.gopIndex),\n clipId,\n gopIndex: gop.gopIndex,\n gopStartUs: gop.startUs,\n durationUs: gop.durationUs,\n frames,\n size: 0,\n };\n\n this.updateEntryStats(entry, this.deriveFallbackDuration(gop.durationUs, frames.length));\n return entry;\n }\n\n private mergeFrames(entry: L1CacheEntry, frames: RcFrame[], fallbackDuration: TimeUs): void {\n const durationFallback = this.deriveFallbackDuration(\n fallbackDuration,\n entry.frames.length + frames.length\n );\n for (const rcFrame of frames) {\n this.insertFrame(entry, rcFrame, durationFallback);\n }\n this.updateEntryStats(entry, durationFallback);\n }\n\n private insertFrame(entry: L1CacheEntry, frame: RcFrame, fallbackDuration: TimeUs): void {\n const timestamp = frame.timestampUs ?? entry.gopStartUs;\n const frames = entry.frames;\n const insertIndex = this.findFrameInsertIndex(frames, timestamp);\n\n if (\n insertIndex < frames.length &&\n (frames[insertIndex]?.timestampUs ?? entry.gopStartUs) === timestamp\n ) {\n const oldFrame = frames[insertIndex];\n frames[insertIndex] = frame;\n oldFrame?.close?.();\n } else {\n frames.splice(insertIndex, 0, frame);\n }\n\n entry.size += frame.sizeEstimate;\n const duration = frame.durationUs || fallbackDuration;\n entry.durationUs = Math.max(entry.durationUs, timestamp + duration - entry.gopStartUs);\n }\n\n private removeEntry(entry: L1CacheEntry): void {\n const clipEntries = this.entriesByClip.get(entry.clipId);\n if (clipEntries) {\n const index = clipEntries.findIndex((item) => item.gopIndex === entry.gopIndex);\n if (index !== -1) {\n clipEntries.splice(index, 1);\n }\n if (clipEntries.length === 0) {\n this.entriesByClip.delete(entry.clipId);\n }\n }\n\n this.closeFrames(entry);\n this.currentBytes -= entry.size;\n }\n\n private closeFrames(entry: L1CacheEntry): void {\n for (const frame of entry.frames) {\n frame?.close?.();\n }\n }\n\n private composeKey(clipId: string, gopIndex: number): string {\n return `${clipId}:${gopIndex}`;\n }\n\n private wrapFrame(\n frame: VideoFrame,\n clipId: string,\n frameDuration: TimeUs,\n gopSerial?: number,\n isKeyframe?: boolean\n ): RcFrame {\n return RcFrame.wrap(frame, {\n trackId: 'main',\n clipId,\n timestampUs: frame.timestamp ?? 0,\n durationUs: frame.duration ?? frameDuration,\n gopSerial,\n isKeyframe,\n });\n }\n\n private wrapFrames(\n frames: (RcFrame | VideoFrame)[],\n clipId: string,\n fallbackDuration: TimeUs\n ): RcFrame[] {\n const wrapped = frames.map((frame) =>\n frame instanceof RcFrame\n ? frame\n : this.wrapFrame(frame as VideoFrame, clipId, fallbackDuration / Math.max(frames.length, 1))\n );\n return this.normalizeFrames(wrapped);\n }\n\n private getEntryExact(clipId: string, gopIndex: number): L1CacheEntry | undefined {\n const entries = this.entriesByClip.get(clipId);\n if (!entries) return undefined;\n return entries.find((e) => e.gopIndex === gopIndex);\n }\n\n private findEntryByTime(entries: L1CacheEntry[], timeUs: TimeUs): L1CacheEntry | undefined {\n let low = 0;\n let high = entries.length - 1;\n\n while (low <= high) {\n const mid = Math.floor((low + high) / 2);\n const entry = entries[mid];\n if (!entry) break;\n\n if (timeUs < entry.gopStartUs) {\n high = mid - 1;\n } else if (timeUs >= entry.gopStartUs + entry.durationUs) {\n low = mid + 1;\n } else {\n return entry;\n }\n }\n\n return undefined;\n }\n\n private countEntries(): number {\n let count = 0;\n for (const clipEntries of this.entriesByClip.values()) {\n count += clipEntries.length;\n }\n return count;\n }\n\n private iterateEntries(): Iterable<L1CacheEntry> {\n const entries: L1CacheEntry[] = [];\n for (const clipEntries of this.entriesByClip.values()) {\n entries.push(...clipEntries);\n }\n return entries;\n }\n\n private ensureClipEntries(clipId: string): L1CacheEntry[] {\n let entries = this.entriesByClip.get(clipId);\n if (!entries) {\n entries = [];\n this.entriesByClip.set(clipId, entries);\n }\n return entries;\n }\n\n private findInsertIndex(entries: L1CacheEntry[], gopIndex: number): number {\n let low = 0;\n let high = entries.length;\n while (low < high) {\n const mid = Math.floor((low + high) / 2);\n const entry = entries[mid];\n if (entry && entry.gopIndex < gopIndex) {\n low = mid + 1;\n } else {\n high = mid;\n }\n }\n return low;\n }\n\n private findFrameInsertIndex(frames: RcFrame[], timestamp: TimeUs): number {\n let low = 0;\n let high = frames.length;\n while (low < high) {\n const mid = Math.floor((low + high) / 2);\n const midTs = frames[mid]?.timestampUs ?? 0;\n if (midTs < timestamp) {\n low = mid + 1;\n } else {\n high = mid;\n }\n }\n return low;\n }\n\n private normalizeFrames(frames: RcFrame[]): RcFrame[] {\n const seen = new Set<number>();\n const sorted = [...frames].sort((a, b) => (a.timestampUs ?? 0) - (b.timestampUs ?? 0));\n const result: RcFrame[] = [];\n for (const frame of sorted) {\n const ts = frame.timestampUs ?? 0;\n if (seen.has(ts)) {\n frame?.close?.();\n } else {\n seen.add(ts);\n result.push(frame);\n }\n }\n return result;\n }\n\n private updateEntryStats(entry: L1CacheEntry, fallbackDuration: TimeUs): void {\n entry.size = entry.frames.reduce((acc, frame) => acc + frame.sizeEstimate, 0);\n entry.durationUs = entry.frames.reduce((acc, frame) => {\n const duration = frame.durationUs || fallbackDuration;\n return Math.max(acc, (frame.timestampUs ?? entry.gopStartUs) + duration - entry.gopStartUs);\n }, entry.durationUs);\n }\n\n private deriveFallbackDuration(durationUs: TimeUs, frameCount: number): TimeUs {\n if (frameCount <= 1) {\n return durationUs || this.gopIntervalUs;\n }\n return Math.max(durationUs / frameCount, this.gopIntervalUs / frameCount);\n }\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { TimeUs } from '../../model/types';
|
|
2
|
+
import { GOP } from '../types';
|
|
3
|
+
|
|
4
|
+
export declare function computeGopRange(startUs: TimeUs, endUs: TimeUs, intervalUs: number): {
|
|
5
|
+
startIndex: number;
|
|
6
|
+
endIndex: number;
|
|
7
|
+
};
|
|
8
|
+
export declare function findGopIndex(timeUs: TimeUs, intervalUs: number): number;
|
|
9
|
+
export declare function findFrameIndex(gop: GOP, timeUs: TimeUs, toleranceUs?: TimeUs): number;
|
|
10
|
+
//# sourceMappingURL=gop-utils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gop-utils.d.ts","sourceRoot":"","sources":["../../../src/cache/l1/gop-utils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,UAAU,CAAC;AAMpC,wBAAgB,eAAe,CAC7B,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,MAAM,EACb,UAAU,EAAE,MAAM,GACjB;IAAE,UAAU,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAQ1C;AAED,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,CAGvE;AAED,wBAAgB,cAAc,CAC5B,GAAG,EAAE,GAAG,EACR,MAAM,EAAE,MAAM,EACd,WAAW,GAAE,MAAmC,GAC/C,MAAM,CA2DR"}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { DEFAULT_FRAME_TOLERANCE_US, isTimestampWithinFrame } from "../../utils/time-utils.js";
|
|
2
|
+
const DEFAULT_GOP_INTERVAL_US = 2e6;
|
|
3
|
+
function findGopIndex(timeUs, intervalUs) {
|
|
4
|
+
const interval = intervalUs > 0 ? intervalUs : DEFAULT_GOP_INTERVAL_US;
|
|
5
|
+
return Math.floor(timeUs / interval);
|
|
6
|
+
}
|
|
7
|
+
function findFrameIndex(gop, timeUs, toleranceUs = DEFAULT_FRAME_TOLERANCE_US) {
|
|
8
|
+
if (gop.frames.length === 0) {
|
|
9
|
+
return -1;
|
|
10
|
+
}
|
|
11
|
+
let left = 0;
|
|
12
|
+
let right = gop.frames.length - 1;
|
|
13
|
+
let candidate = -1;
|
|
14
|
+
while (left <= right) {
|
|
15
|
+
const mid = Math.floor((left + right) / 2);
|
|
16
|
+
const frame = gop.frames[mid];
|
|
17
|
+
if (!frame) {
|
|
18
|
+
break;
|
|
19
|
+
}
|
|
20
|
+
const frameTime = getFrameTimestamp(frame, gop, mid);
|
|
21
|
+
if (frameTime === timeUs) {
|
|
22
|
+
return mid;
|
|
23
|
+
}
|
|
24
|
+
if (frameTime < timeUs) {
|
|
25
|
+
candidate = mid;
|
|
26
|
+
left = mid + 1;
|
|
27
|
+
} else {
|
|
28
|
+
right = mid - 1;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
const matches = (index) => {
|
|
32
|
+
if (index < 0 || index >= gop.frames.length) {
|
|
33
|
+
return false;
|
|
34
|
+
}
|
|
35
|
+
const frame = gop.frames[index];
|
|
36
|
+
if (!frame) {
|
|
37
|
+
return false;
|
|
38
|
+
}
|
|
39
|
+
const frameTime = getFrameTimestamp(frame, gop, index);
|
|
40
|
+
const frameDuration = getFrameDuration(frame, gop);
|
|
41
|
+
return isTimestampWithinFrame(timeUs, frameTime, frameDuration, toleranceUs);
|
|
42
|
+
};
|
|
43
|
+
if (candidate !== -1 && matches(candidate)) {
|
|
44
|
+
return candidate;
|
|
45
|
+
}
|
|
46
|
+
const nextIndex = candidate === -1 ? 0 : Math.min(candidate + 1, gop.frames.length - 1);
|
|
47
|
+
if (matches(nextIndex)) {
|
|
48
|
+
return nextIndex;
|
|
49
|
+
}
|
|
50
|
+
if (candidate > 0 && matches(candidate - 1)) {
|
|
51
|
+
return candidate - 1;
|
|
52
|
+
}
|
|
53
|
+
return -1;
|
|
54
|
+
}
|
|
55
|
+
function getFrameTimestamp(frame, gop, index) {
|
|
56
|
+
if (typeof frame.timestampUs === "number") {
|
|
57
|
+
return frame.timestampUs;
|
|
58
|
+
}
|
|
59
|
+
if (gop.frames.length > 1 && gop.durationUs > 0) {
|
|
60
|
+
const approximateInterval = Math.round(gop.durationUs / gop.frames.length);
|
|
61
|
+
return gop.startUs + index * approximateInterval;
|
|
62
|
+
}
|
|
63
|
+
return gop.startUs;
|
|
64
|
+
}
|
|
65
|
+
function getFrameDuration(frame, gop) {
|
|
66
|
+
if (typeof frame.durationUs === "number" && frame.durationUs > 0) {
|
|
67
|
+
return frame.durationUs;
|
|
68
|
+
}
|
|
69
|
+
if (gop.frames.length > 1 && gop.durationUs > 0) {
|
|
70
|
+
return Math.round(gop.durationUs / gop.frames.length);
|
|
71
|
+
}
|
|
72
|
+
return gop.durationUs;
|
|
73
|
+
}
|
|
74
|
+
export {
|
|
75
|
+
findFrameIndex,
|
|
76
|
+
findGopIndex
|
|
77
|
+
};
|
|
78
|
+
//# sourceMappingURL=gop-utils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gop-utils.js","sources":["../../../src/cache/l1/gop-utils.ts"],"sourcesContent":["import type { TimeUs } from '../../model/types';\nimport type { GOP } from '../types';\nimport { RcFrame } from '../../model';\nimport { DEFAULT_FRAME_TOLERANCE_US, isTimestampWithinFrame } from '../../utils/time-utils';\n\nconst DEFAULT_GOP_INTERVAL_US = 2_000_000;\n\nexport function computeGopRange(\n startUs: TimeUs,\n endUs: TimeUs,\n intervalUs: number\n): { startIndex: number; endIndex: number } {\n const startIndex = findGopIndex(startUs, intervalUs);\n const adjustedEnd = endUs > startUs ? endUs - 1 : endUs;\n const endIndex = findGopIndex(adjustedEnd, intervalUs);\n return {\n startIndex,\n endIndex: Math.max(startIndex, endIndex),\n };\n}\n\nexport function findGopIndex(timeUs: TimeUs, intervalUs: number): number {\n const interval = intervalUs > 0 ? intervalUs : DEFAULT_GOP_INTERVAL_US;\n return Math.floor(timeUs / interval);\n}\n\nexport function findFrameIndex(\n gop: GOP,\n timeUs: TimeUs,\n toleranceUs: TimeUs = DEFAULT_FRAME_TOLERANCE_US\n): number {\n if (gop.frames.length === 0) {\n return -1;\n }\n\n let left = 0;\n let right = gop.frames.length - 1;\n let candidate = -1;\n\n while (left <= right) {\n const mid = Math.floor((left + right) / 2);\n const frame = gop.frames[mid];\n if (!frame) {\n break;\n }\n\n const frameTime = getFrameTimestamp(frame, gop, mid);\n\n if (frameTime === timeUs) {\n return mid;\n }\n\n if (frameTime < timeUs) {\n candidate = mid;\n left = mid + 1;\n } else {\n right = mid - 1;\n }\n }\n\n const matches = (index: number): boolean => {\n if (index < 0 || index >= gop.frames.length) {\n return false;\n }\n\n const frame = gop.frames[index];\n if (!frame) {\n return false;\n }\n\n const frameTime = getFrameTimestamp(frame, gop, index);\n const frameDuration = getFrameDuration(frame, gop);\n return isTimestampWithinFrame(timeUs, frameTime, frameDuration, toleranceUs);\n };\n\n if (candidate !== -1 && matches(candidate)) {\n return candidate;\n }\n\n const nextIndex = candidate === -1 ? 0 : Math.min(candidate + 1, gop.frames.length - 1);\n if (matches(nextIndex)) {\n return nextIndex;\n }\n\n if (candidate > 0 && matches(candidate - 1)) {\n return candidate - 1;\n }\n\n return -1;\n}\n\nfunction getFrameTimestamp(frame: RcFrame, gop: GOP, index: number): TimeUs {\n if (typeof frame.timestampUs === 'number') {\n return frame.timestampUs;\n }\n\n if (gop.frames.length > 1 && gop.durationUs > 0) {\n const approximateInterval = Math.round(gop.durationUs / gop.frames.length);\n return gop.startUs + index * approximateInterval;\n }\n\n return gop.startUs;\n}\n\nfunction getFrameDuration(frame: RcFrame, gop: GOP): TimeUs {\n if (typeof frame.durationUs === 'number' && frame.durationUs > 0) {\n return frame.durationUs;\n }\n\n if (gop.frames.length > 1 && gop.durationUs > 0) {\n return Math.round(gop.durationUs / gop.frames.length);\n }\n\n return gop.durationUs;\n}\n"],"names":[],"mappings":";AAKA,MAAM,0BAA0B;AAgBzB,SAAS,aAAa,QAAgB,YAA4B;AACvE,QAAM,WAAW,aAAa,IAAI,aAAa;AAC/C,SAAO,KAAK,MAAM,SAAS,QAAQ;AACrC;AAEO,SAAS,eACd,KACA,QACA,cAAsB,4BACd;AACR,MAAI,IAAI,OAAO,WAAW,GAAG;AAC3B,WAAO;AAAA,EACT;AAEA,MAAI,OAAO;AACX,MAAI,QAAQ,IAAI,OAAO,SAAS;AAChC,MAAI,YAAY;AAEhB,SAAO,QAAQ,OAAO;AACpB,UAAM,MAAM,KAAK,OAAO,OAAO,SAAS,CAAC;AACzC,UAAM,QAAQ,IAAI,OAAO,GAAG;AAC5B,QAAI,CAAC,OAAO;AACV;AAAA,IACF;AAEA,UAAM,YAAY,kBAAkB,OAAO,KAAK,GAAG;AAEnD,QAAI,cAAc,QAAQ;AACxB,aAAO;AAAA,IACT;AAEA,QAAI,YAAY,QAAQ;AACtB,kBAAY;AACZ,aAAO,MAAM;AAAA,IACf,OAAO;AACL,cAAQ,MAAM;AAAA,IAChB;AAAA,EACF;AAEA,QAAM,UAAU,CAAC,UAA2B;AAC1C,QAAI,QAAQ,KAAK,SAAS,IAAI,OAAO,QAAQ;AAC3C,aAAO;AAAA,IACT;AAEA,UAAM,QAAQ,IAAI,OAAO,KAAK;AAC9B,QAAI,CAAC,OAAO;AACV,aAAO;AAAA,IACT;AAEA,UAAM,YAAY,kBAAkB,OAAO,KAAK,KAAK;AACrD,UAAM,gBAAgB,iBAAiB,OAAO,GAAG;AACjD,WAAO,uBAAuB,QAAQ,WAAW,eAAe,WAAW;AAAA,EAC7E;AAEA,MAAI,cAAc,MAAM,QAAQ,SAAS,GAAG;AAC1C,WAAO;AAAA,EACT;AAEA,QAAM,YAAY,cAAc,KAAK,IAAI,KAAK,IAAI,YAAY,GAAG,IAAI,OAAO,SAAS,CAAC;AACtF,MAAI,QAAQ,SAAS,GAAG;AACtB,WAAO;AAAA,EACT;AAEA,MAAI,YAAY,KAAK,QAAQ,YAAY,CAAC,GAAG;AAC3C,WAAO,YAAY;AAAA,EACrB;AAEA,SAAO;AACT;AAEA,SAAS,kBAAkB,OAAgB,KAAU,OAAuB;AAC1E,MAAI,OAAO,MAAM,gBAAgB,UAAU;AACzC,WAAO,MAAM;AAAA,EACf;AAEA,MAAI,IAAI,OAAO,SAAS,KAAK,IAAI,aAAa,GAAG;AAC/C,UAAM,sBAAsB,KAAK,MAAM,IAAI,aAAa,IAAI,OAAO,MAAM;AACzE,WAAO,IAAI,UAAU,QAAQ;AAAA,EAC/B;AAEA,SAAO,IAAI;AACb;AAEA,SAAS,iBAAiB,OAAgB,KAAkB;AAC1D,MAAI,OAAO,MAAM,eAAe,YAAY,MAAM,aAAa,GAAG;AAChE,WAAO,MAAM;AAAA,EACf;AAEA,MAAI,IAAI,OAAO,SAAS,KAAK,IAAI,aAAa,GAAG;AAC/C,WAAO,KAAK,MAAM,IAAI,aAAa,IAAI,OAAO,MAAM;AAAA,EACtD;AAEA,SAAO,IAAI;AACb;"}
|