@meframe/core 0.0.31 → 0.0.33
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/Meframe.d.ts +2 -2
- package/dist/Meframe.d.ts.map +1 -1
- package/dist/Meframe.js +3 -2
- package/dist/Meframe.js.map +1 -1
- package/dist/cache/CacheManager.d.ts +12 -17
- package/dist/cache/CacheManager.d.ts.map +1 -1
- package/dist/cache/CacheManager.js +18 -280
- package/dist/cache/CacheManager.js.map +1 -1
- package/dist/cache/l1/AudioL1Cache.d.ts +36 -19
- package/dist/cache/l1/AudioL1Cache.d.ts.map +1 -1
- package/dist/cache/l1/AudioL1Cache.js +182 -282
- package/dist/cache/l1/AudioL1Cache.js.map +1 -1
- package/dist/controllers/PlaybackController.d.ts +5 -3
- package/dist/controllers/PlaybackController.d.ts.map +1 -1
- package/dist/controllers/PlaybackController.js +58 -16
- package/dist/controllers/PlaybackController.js.map +1 -1
- package/dist/event/events.d.ts +1 -1
- package/dist/event/events.d.ts.map +1 -1
- package/dist/event/events.js.map +1 -1
- package/dist/model/CompositionModel.d.ts +8 -0
- package/dist/model/CompositionModel.d.ts.map +1 -1
- package/dist/model/CompositionModel.js +18 -0
- package/dist/model/CompositionModel.js.map +1 -1
- package/dist/model/types.d.ts +0 -4
- package/dist/model/types.d.ts.map +1 -1
- package/dist/model/types.js.map +1 -1
- package/dist/orchestrator/ExportScheduler.d.ts +10 -0
- package/dist/orchestrator/ExportScheduler.d.ts.map +1 -1
- package/dist/orchestrator/ExportScheduler.js +66 -83
- package/dist/orchestrator/ExportScheduler.js.map +1 -1
- package/dist/orchestrator/GlobalAudioSession.d.ts +35 -28
- package/dist/orchestrator/GlobalAudioSession.d.ts.map +1 -1
- package/dist/orchestrator/GlobalAudioSession.js +213 -422
- package/dist/orchestrator/GlobalAudioSession.js.map +1 -1
- package/dist/orchestrator/OnDemandVideoSession.d.ts +3 -3
- package/dist/orchestrator/OnDemandVideoSession.d.ts.map +1 -1
- package/dist/orchestrator/OnDemandVideoSession.js +4 -4
- package/dist/orchestrator/OnDemandVideoSession.js.map +1 -1
- package/dist/orchestrator/Orchestrator.d.ts +11 -4
- package/dist/orchestrator/Orchestrator.d.ts.map +1 -1
- package/dist/orchestrator/Orchestrator.js +75 -68
- package/dist/orchestrator/Orchestrator.js.map +1 -1
- package/dist/orchestrator/VideoClipSession.d.ts +0 -2
- package/dist/orchestrator/VideoClipSession.d.ts.map +1 -1
- package/dist/orchestrator/VideoClipSession.js +0 -49
- package/dist/orchestrator/VideoClipSession.js.map +1 -1
- package/dist/stages/compose/OfflineAudioMixer.d.ts.map +1 -1
- package/dist/stages/compose/OfflineAudioMixer.js +13 -18
- package/dist/stages/compose/OfflineAudioMixer.js.map +1 -1
- package/dist/stages/decode/AudioChunkDecoder.js +169 -0
- package/dist/stages/decode/AudioChunkDecoder.js.map +1 -0
- package/dist/stages/demux/MP3FrameParser.js +186 -0
- package/dist/stages/demux/MP3FrameParser.js.map +1 -0
- package/dist/stages/load/ResourceLoader.d.ts +49 -30
- package/dist/stages/load/ResourceLoader.d.ts.map +1 -1
- package/dist/stages/load/ResourceLoader.js +255 -189
- package/dist/stages/load/ResourceLoader.js.map +1 -1
- package/dist/stages/load/TaskManager.d.ts +4 -0
- package/dist/stages/load/TaskManager.d.ts.map +1 -1
- package/dist/stages/load/TaskManager.js +11 -0
- package/dist/stages/load/TaskManager.js.map +1 -1
- package/dist/stages/load/types.d.ts +1 -0
- package/dist/stages/load/types.d.ts.map +1 -1
- package/dist/utils/audio-data.d.ts +16 -0
- package/dist/utils/audio-data.d.ts.map +1 -0
- package/dist/utils/audio-data.js +111 -0
- package/dist/utils/audio-data.js.map +1 -0
- package/package.json +1 -1
- package/dist/cache/resource/ImageBitmapCache.d.ts +0 -65
- package/dist/cache/resource/ImageBitmapCache.d.ts.map +0 -1
- package/dist/cache/resource/ImageBitmapCache.js +0 -101
- package/dist/cache/resource/ImageBitmapCache.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CompositionModel.js","sources":["../../src/model/CompositionModel.ts"],"sourcesContent":["import {\n CompositionModelData,\n Track,\n Clip,\n Resource,\n TimeUs,\n AnimationEffect,\n hasResourceId,\n} from './types';\nimport { binarySearchRange, binarySearchOverlapping } from '../utils/binary-search';\nimport { validateCompositionStructure } from './validation';\n\nexport class CompositionModel {\n public readonly version = '1.0' as const;\n public readonly fps: 24 | 25 | 30 | 60;\n public durationUs!: TimeUs; // Assigned in buildIndexes()\n public readonly mainTrackId: string;\n public tracks: Track[];\n public readonly resources: Map<string, Resource>;\n\n private readonly trackMap: Map<string, Track>;\n private readonly clipMap: Map<string, Clip>;\n private readonly resourceRefCount: Map<string, number>;\n\n public readonly renderConfig?: {\n width: number;\n height: number;\n backgroundColor?: string;\n };\n\n public readonly ext?: Record<string, unknown>;\n\n constructor(data: CompositionModelData) {\n const errors = validateCompositionStructure(data);\n if (errors.length > 0) {\n throw new Error(\n `Validation failed:\\n${errors.map((e) => `${e.path}: ${e.message}`).join('\\n')}`\n );\n }\n\n this.fps = data.fps;\n this.mainTrackId = data.mainTrackId ?? 'main';\n this.tracks = data.tracks;\n this.resources = new Map(Object.entries(data.resources));\n this.renderConfig = data.renderConfig;\n this.ext = data.ext;\n\n // Build indexes\n this.trackMap = new Map();\n this.clipMap = new Map();\n this.resourceRefCount = new Map();\n\n this.buildIndexes();\n\n console.log(\n 'CompositionModel constructed',\n this.tracks.find((t) => t.id === this.mainTrackId)?.clips[0]\n );\n }\n\n // Track operations\n findTrack(id: string): Track | null {\n return this.trackMap.get(id) || null;\n }\n\n getTracksByKind(kind: 'video' | 'audio' | 'caption' | 'overlay' | 'fx'): Track[] {\n return this.tracks.filter((track) => track.kind === kind);\n }\n\n // Clip operations with binary search optimization\n findClip(id: string): Clip | null {\n return this.clipMap.get(id) || null;\n }\n\n getClipsAtTime(timeUs: TimeUs, trackId?: string): Clip[] {\n const tracks = trackId ? [this.findTrack(trackId)] : this.tracks;\n const clips: Clip[] = [];\n\n for (const track of tracks) {\n if (!track) continue;\n // Use binary search for single point lookup\n const clip = binarySearchRange(track.clips, timeUs, (entry, _index) => ({\n start: entry.startUs,\n end: entry.startUs + entry.durationUs,\n }));\n\n if (clip) {\n clips.push(clip);\n }\n }\n\n return clips;\n }\n\n getActiveClips(startUs: TimeUs, endUs: TimeUs): Clip[] {\n const clips: Clip[] = [];\n\n for (const track of this.tracks) {\n // Use binary search for range overlap\n const overlappingClips = binarySearchOverlapping(\n track.clips,\n startUs,\n endUs,\n (clip, _index) => ({\n start: clip.startUs,\n end: clip.startUs + clip.durationUs,\n })\n );\n\n clips.push(...overlappingClips);\n }\n\n return clips;\n }\n\n getClipIdsByResourceId(resourceId: string): string[] {\n const resource = this.resources.get(resourceId);\n return resource?.clipIds || [];\n }\n\n getClipIdAtTime(trackId: string, timeUs: TimeUs): string | undefined {\n const track = this.findTrack(trackId);\n if (!track) {\n return undefined;\n }\n\n const clip = binarySearchRange(track.clips, timeUs, (entry, _index) => ({\n start: entry.startUs,\n end: entry.startUs + entry.durationUs,\n }));\n\n return clip?.id;\n }\n\n /**\n * Get neighboring clips (Prev/Current/Next) at a specific time for video tracks\n * Returns prev, current, and next clip IDs\n */\n getNeighboringClips(timeUs: TimeUs): { prev?: string; current?: string; next?: string } {\n const videoTracks = this.getTracksByKind('video');\n const result: { prev?: string; current?: string; next?: string } = {};\n\n for (const track of videoTracks) {\n const clips = track.clips;\n\n for (let i = 0; i < clips.length; i++) {\n const clip = clips[i];\n if (!clip) continue;\n\n const clipEndUs = clip.startUs + clip.durationUs;\n\n if (clip.startUs <= timeUs && timeUs < clipEndUs) {\n if (!result.current) {\n result.current = clip.id;\n }\n\n if (i > 0 && !result.prev) {\n const prevClip = clips[i - 1];\n if (prevClip) {\n result.prev = prevClip.id;\n }\n }\n\n if (i < clips.length - 1 && !result.next) {\n const nextClip = clips[i + 1];\n if (nextClip) {\n result.next = nextClip.id;\n }\n }\n }\n }\n }\n\n return result;\n }\n\n /**\n * Get all clip IDs that should be cached using adaptive strategy\n * - Short clips (≤ maxDuration): cache Current + Next (smooth transitions)\n * - Long clips (> maxDuration): cache Current only (memory control)\n * @param timeUs - Current playback time\n * @param maxDuration - Max duration for 2-clip strategy (default 5s)\n */\n getClipsToCacheAtTime(timeUs: TimeUs, maxDuration = 5_000_000): Set<string> {\n const { current, next } = this.getNeighboringClips(timeUs);\n const clipIds = new Set<string>();\n\n if (!current) return clipIds;\n clipIds.add(current);\n\n // Only cache next clip if current clip is short enough\n const currentClip = this.findClip(current);\n if (currentClip && currentClip.durationUs <= maxDuration && next) {\n clipIds.add(next);\n }\n\n return clipIds;\n }\n\n // Resource operations\n getResource(id: string): Resource | null {\n return this.resources.get(id) || null;\n }\n\n updateResourceState(id: string, state: 'pending' | 'loading' | 'ready' | 'error'): void {\n const resource = this.resources.get(id);\n if (resource) {\n resource.state = state;\n }\n }\n\n getUnusedResources(): Resource[] {\n const unused: Resource[] = [];\n\n for (const [id, resource] of this.resources) {\n if (!this.resourceRefCount.has(id) || this.resourceRefCount.get(id) === 0) {\n unused.push(resource);\n }\n }\n\n return unused;\n }\n\n // Time operations\n getDuration(): TimeUs {\n return this.durationUs;\n }\n\n getTrackDuration(trackId: string): TimeUs {\n const track = this.findTrack(trackId);\n if (!track || track.clips.length === 0) return 0;\n\n // Since clips are sorted, last clip determines duration\n const lastClip = track.clips[track.clips.length - 1];\n return (lastClip?.startUs ?? 0) + (lastClip?.durationUs ?? 0);\n }\n\n buildIndexes(options?: {\n incremental?: boolean;\n trackId?: string;\n clipId?: string;\n clip?: Clip;\n operation?: 'add' | 'update' | 'remove';\n }): void {\n const track = options?.trackId ? this.findTrack(options.trackId) : undefined;\n const isAttachmentTrack = track && track.kind !== 'video' && track.kind !== 'audio';\n // Incremental update for video/audio track clip operations\n if (options?.incremental && !isAttachmentTrack && options.clipId && options.operation) {\n const clip = options.clip ?? this.clipMap.get(options.clipId);\n\n if (options.operation === 'add' && clip) {\n this.addClipToIndexes(clip);\n } else if (options.operation === 'remove' && clip) {\n this.removeClipFromIndexes(clip);\n } else if (options.operation === 'update' && clip) {\n // Handle resource change during update\n if (clip.oldResourceId) {\n this.updateClipResourceIndex(\n options.clipId,\n clip.oldResourceId,\n hasResourceId(clip) ? clip.resourceId : undefined\n );\n }\n }\n\n // Recalculate duration only if affected main track\n if (track?.id === this.mainTrackId) {\n this.recalculateDuration();\n }\n return;\n }\n\n // Full rebuild: needed for attachment tracks or initial load\n this.fullRebuildIndexes();\n }\n\n private fullRebuildIndexes(): void {\n // Clear existing indexes\n this.trackMap.clear();\n this.clipMap.clear();\n this.resourceRefCount.clear();\n\n // Step 1: Sink attachment tracks to main track (preserves original tracks)\n this.sinkAttachmentTracks();\n\n let maxEndUs = 0;\n\n // Step 2: Build all indexes in one pass (track, clip, resource)\n for (const track of this.tracks) {\n this.trackMap.set(track.id, track);\n\n for (const clip of track.clips) {\n (clip as Clip).trackId = track.id;\n (clip as Clip).trackKind = track.kind;\n this.clipMap.set(clip.id, clip);\n\n // Main track resource index (only for clips with resourceId)\n if (hasResourceId(clip)) {\n const resource = this.resources.get(clip.resourceId);\n if (resource) {\n resource.clipIds = [...(resource.clipIds || []), clip.id];\n }\n const count = this.resourceRefCount.get(clip.resourceId) || 0;\n this.resourceRefCount.set(clip.resourceId, count + 1);\n }\n\n // Attachment resource indexes (attachments are already sunk)\n const attachments = clip.attachments ?? [];\n for (const attachment of attachments) {\n const attachmentResourceId = attachment.data?.resourceId;\n if (attachmentResourceId && typeof attachmentResourceId === 'string') {\n const attachmentResource = this.resources.get(attachmentResourceId);\n if (attachmentResource) {\n const clipIds = attachmentResource.clipIds || [];\n if (!clipIds.includes(clip.id)) {\n attachmentResource.clipIds = [...clipIds, clip.id];\n }\n }\n const attachmentCount = this.resourceRefCount.get(attachmentResourceId) || 0;\n this.resourceRefCount.set(attachmentResourceId, attachmentCount + 1);\n }\n }\n\n // Calculate max end time\n const clipEndUs = clip.startUs + clip.durationUs;\n if (clipEndUs > maxEndUs) {\n maxEndUs = clipEndUs;\n }\n }\n }\n\n this.durationUs = maxEndUs;\n }\n\n private addClipToIndexes(clip: Clip): void {\n this.clipMap.set(clip.id, clip);\n\n if (hasResourceId(clip)) {\n const resource = this.resources.get(clip.resourceId);\n if (resource) {\n if (!resource.clipIds) {\n resource.clipIds = [];\n }\n if (!resource.clipIds.includes(clip.id)) {\n resource.clipIds.push(clip.id);\n }\n }\n const count = this.resourceRefCount.get(clip.resourceId) || 0;\n this.resourceRefCount.set(clip.resourceId, count + 1);\n }\n }\n\n private removeClipFromIndexes(clip: Clip): void {\n this.clipMap.delete(clip.id);\n\n const resourceId = clip.oldResourceId || (hasResourceId(clip) ? clip.resourceId : undefined);\n if (resourceId) {\n const resource = this.resources.get(resourceId);\n if (resource?.clipIds) {\n resource.clipIds = resource.clipIds.filter((id) => id !== clip.id);\n }\n const count = this.resourceRefCount.get(resourceId) || 0;\n this.resourceRefCount.set(resourceId, Math.max(0, count - 1));\n }\n }\n\n /**\n * Incrementally update resource index when clip's resourceId changes\n */\n updateClipResourceIndex(\n clipId: string,\n oldResourceId: string | undefined,\n newResourceId: string | undefined\n ): void {\n // Remove from old resource\n if (oldResourceId) {\n const oldResource = this.resources.get(oldResourceId);\n if (oldResource?.clipIds) {\n oldResource.clipIds = oldResource.clipIds.filter((id) => id !== clipId);\n }\n const oldCount = this.resourceRefCount.get(oldResourceId) || 0;\n this.resourceRefCount.set(oldResourceId, Math.max(0, oldCount - 1));\n }\n\n // Add to new resource\n if (newResourceId) {\n const newResource = this.resources.get(newResourceId);\n if (newResource) {\n if (!newResource.clipIds) {\n newResource.clipIds = [];\n }\n if (!newResource.clipIds.includes(clipId)) {\n newResource.clipIds.push(clipId);\n }\n }\n const newCount = this.resourceRefCount.get(newResourceId) || 0;\n this.resourceRefCount.set(newResourceId, newCount + 1);\n }\n }\n\n /**\n * Recalculate total duration based on main track\n */\n recalculateDuration(): void {\n const mainTrack = this.findTrack(this.mainTrackId);\n if (!mainTrack || mainTrack.clips.length === 0) {\n this.durationUs = 0;\n return;\n }\n\n // Since clips are sorted, last clip determines duration\n const lastClip = mainTrack.clips[mainTrack.clips.length - 1];\n this.durationUs = (lastClip?.startUs ?? 0) + (lastClip?.durationUs ?? 0);\n }\n\n private sinkAttachmentTracks(): void {\n let mainTrack: Track | undefined;\n const attachmentTracks = [];\n\n // Sort all tracks\n for (const track of this.tracks) {\n track.clips.sort((a, b) => a.startUs - b.startUs);\n\n if (track.id === this.mainTrackId) {\n mainTrack = track;\n continue;\n }\n // Collect attachment tracks for sinking (caption, overlay, fx)\n // Video and audio tracks are not sunk\n if (track.kind === 'caption' || track.kind === 'overlay' || track.kind === 'fx') {\n attachmentTracks.push(track);\n }\n }\n\n if (!mainTrack) {\n throw new Error('Main track not found');\n }\n\n // Clear existing attachments before sinking (in case of rebuild)\n for (const clip of mainTrack.clips) {\n clip.attachments = [];\n }\n\n for (const attachmentTrack of attachmentTracks) {\n for (const attachmentClip of attachmentTrack.clips) {\n // Use track.kind directly as attachment kind\n const attachmentKind = attachmentTrack.kind;\n\n for (const mainClip of mainTrack.clips) {\n const overlap = this.getTimeOverlap(attachmentClip, mainClip);\n if (!overlap) continue;\n\n if (!mainClip.attachments) {\n mainClip.attachments = [];\n }\n\n // Extract animation effect\n const animationEffect = attachmentClip.effects?.find(\n (e) => e.effectType === 'animation'\n ) as AnimationEffect | undefined;\n\n // Extract target dimensions from metadata\n const targetWidth = attachmentClip.metadata?.targetWidth as number | undefined;\n const targetHeight = attachmentClip.metadata?.targetHeight as number | undefined;\n\n const attachmentData: Record<string, unknown> = {\n animation: animationEffect?.params,\n overlayClipStartUs: attachmentClip.startUs,\n mainClipStartUs: mainClip.startUs,\n ...(targetWidth !== undefined && { targetWidth }),\n ...(targetHeight !== undefined && { targetHeight }),\n };\n\n // Add resourceId if exists (trackKind not yet set, check field directly)\n if ('resourceId' in attachmentClip && attachmentClip.resourceId) {\n attachmentData.resourceId = attachmentClip.resourceId;\n }\n\n // Add text if exists (trackKind not yet set, check field directly)\n if ('text' in attachmentClip && attachmentClip.text) {\n attachmentData.text = attachmentClip.text;\n }\n\n mainClip.attachments.push({\n id: `${attachmentKind}-${attachmentClip.id}-${mainClip.id}`,\n kind: attachmentKind,\n startUs: overlap.clipRelativeStart,\n durationUs: overlap.duration,\n data: attachmentData,\n });\n }\n }\n }\n }\n\n private getTimeOverlap(\n attachmentClip: Clip,\n mainClip: Clip\n ): { clipRelativeStart: number; duration: number } | null {\n const attachmentStart = attachmentClip.startUs;\n const attachmentEnd = attachmentClip.startUs + attachmentClip.durationUs;\n const mainStart = mainClip.startUs;\n const mainEnd = mainClip.startUs + mainClip.durationUs;\n\n if (attachmentEnd <= mainStart || attachmentStart >= mainEnd) {\n return null;\n }\n\n const overlapStart = Math.max(attachmentStart, mainStart);\n const overlapEnd = Math.min(attachmentEnd, mainEnd);\n const duration = overlapEnd - overlapStart;\n const clipRelativeStart = overlapStart - mainStart;\n\n return { clipRelativeStart, duration };\n }\n}\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"CompositionModel.js","sources":["../../src/model/CompositionModel.ts"],"sourcesContent":["import {\n CompositionModelData,\n Track,\n Clip,\n Resource,\n TimeUs,\n AnimationEffect,\n hasResourceId,\n} from './types';\nimport { binarySearchRange, binarySearchOverlapping } from '../utils/binary-search';\nimport { validateCompositionStructure } from './validation';\n\nexport class CompositionModel {\n public readonly version = '1.0' as const;\n public readonly fps: 24 | 25 | 30 | 60;\n public durationUs!: TimeUs; // Assigned in buildIndexes()\n public readonly mainTrackId: string;\n public tracks: Track[];\n public readonly resources: Map<string, Resource>;\n\n private readonly trackMap: Map<string, Track>;\n private readonly clipMap: Map<string, Clip>;\n private readonly resourceRefCount: Map<string, number>;\n\n public readonly renderConfig?: {\n width: number;\n height: number;\n backgroundColor?: string;\n };\n\n public readonly ext?: Record<string, unknown>;\n\n constructor(data: CompositionModelData) {\n const errors = validateCompositionStructure(data);\n if (errors.length > 0) {\n throw new Error(\n `Validation failed:\\n${errors.map((e) => `${e.path}: ${e.message}`).join('\\n')}`\n );\n }\n\n this.fps = data.fps;\n this.mainTrackId = data.mainTrackId ?? 'main';\n this.tracks = data.tracks;\n this.resources = new Map(Object.entries(data.resources));\n this.renderConfig = data.renderConfig;\n this.ext = data.ext;\n\n // Build indexes\n this.trackMap = new Map();\n this.clipMap = new Map();\n this.resourceRefCount = new Map();\n\n this.buildIndexes();\n\n console.log(\n 'CompositionModel constructed',\n this.tracks.find((t) => t.id === this.mainTrackId)?.clips[0]\n );\n }\n\n // Track operations\n findTrack(id: string): Track | null {\n return this.trackMap.get(id) || null;\n }\n\n getTracksByKind(kind: 'video' | 'audio' | 'caption' | 'overlay' | 'fx'): Track[] {\n return this.tracks.filter((track) => track.kind === kind);\n }\n\n // Clip operations with binary search optimization\n findClip(id: string): Clip | null {\n return this.clipMap.get(id) || null;\n }\n\n getClipsAtTime(timeUs: TimeUs, trackId?: string): Clip[] {\n const tracks = trackId ? [this.findTrack(trackId)] : this.tracks;\n const clips: Clip[] = [];\n\n for (const track of tracks) {\n if (!track) continue;\n // Use binary search for single point lookup\n const clip = binarySearchRange(track.clips, timeUs, (entry, _index) => ({\n start: entry.startUs,\n end: entry.startUs + entry.durationUs,\n }));\n\n if (clip) {\n clips.push(clip);\n }\n }\n\n return clips;\n }\n\n getActiveClips(startUs: TimeUs, endUs: TimeUs): Clip[] {\n const clips: Clip[] = [];\n\n for (const track of this.tracks) {\n // Use binary search for range overlap\n const overlappingClips = binarySearchOverlapping(\n track.clips,\n startUs,\n endUs,\n (clip, _index) => ({\n start: clip.startUs,\n end: clip.startUs + clip.durationUs,\n })\n );\n\n clips.push(...overlappingClips);\n }\n\n return clips;\n }\n\n /**\n * Get all clips in a specific track that overlap with the given time range\n * Uses binary search for O(log n + k) performance\n * @param startUs - Range start time (inclusive)\n * @param endUs - Range end time (exclusive)\n * @param trackId - Optional track ID to filter (defaults to main track)\n */\n getClipsInRange(startUs: TimeUs, endUs: TimeUs, trackId?: string): Clip[] {\n const targetTrackId = trackId ?? this.mainTrackId;\n const track = this.findTrack(targetTrackId);\n\n if (!track) {\n return [];\n }\n\n return binarySearchOverlapping(track.clips, startUs, endUs, (clip) => ({\n start: clip.startUs,\n end: clip.startUs + clip.durationUs,\n }));\n }\n\n getClipIdsByResourceId(resourceId: string): string[] {\n const resource = this.resources.get(resourceId);\n return resource?.clipIds || [];\n }\n\n getClipIdAtTime(trackId: string, timeUs: TimeUs): string | undefined {\n const track = this.findTrack(trackId);\n if (!track) {\n return undefined;\n }\n\n const clip = binarySearchRange(track.clips, timeUs, (entry, _index) => ({\n start: entry.startUs,\n end: entry.startUs + entry.durationUs,\n }));\n\n return clip?.id;\n }\n\n /**\n * Get neighboring clips (Prev/Current/Next) at a specific time for video tracks\n * Returns prev, current, and next clip IDs\n */\n getNeighboringClips(timeUs: TimeUs): { prev?: string; current?: string; next?: string } {\n const videoTracks = this.getTracksByKind('video');\n const result: { prev?: string; current?: string; next?: string } = {};\n\n for (const track of videoTracks) {\n const clips = track.clips;\n\n for (let i = 0; i < clips.length; i++) {\n const clip = clips[i];\n if (!clip) continue;\n\n const clipEndUs = clip.startUs + clip.durationUs;\n\n if (clip.startUs <= timeUs && timeUs < clipEndUs) {\n if (!result.current) {\n result.current = clip.id;\n }\n\n if (i > 0 && !result.prev) {\n const prevClip = clips[i - 1];\n if (prevClip) {\n result.prev = prevClip.id;\n }\n }\n\n if (i < clips.length - 1 && !result.next) {\n const nextClip = clips[i + 1];\n if (nextClip) {\n result.next = nextClip.id;\n }\n }\n }\n }\n }\n\n return result;\n }\n\n /**\n * Get all clip IDs that should be cached using adaptive strategy\n * - Short clips (≤ maxDuration): cache Current + Next (smooth transitions)\n * - Long clips (> maxDuration): cache Current only (memory control)\n * @param timeUs - Current playback time\n * @param maxDuration - Max duration for 2-clip strategy (default 5s)\n */\n getClipsToCacheAtTime(timeUs: TimeUs, maxDuration = 5_000_000): Set<string> {\n const { current, next } = this.getNeighboringClips(timeUs);\n const clipIds = new Set<string>();\n\n if (!current) return clipIds;\n clipIds.add(current);\n\n // Only cache next clip if current clip is short enough\n const currentClip = this.findClip(current);\n if (currentClip && currentClip.durationUs <= maxDuration && next) {\n clipIds.add(next);\n }\n\n return clipIds;\n }\n\n // Resource operations\n getResource(id: string): Resource | null {\n return this.resources.get(id) || null;\n }\n\n updateResourceState(id: string, state: 'pending' | 'loading' | 'ready' | 'error'): void {\n const resource = this.resources.get(id);\n if (resource) {\n resource.state = state;\n }\n }\n\n getUnusedResources(): Resource[] {\n const unused: Resource[] = [];\n\n for (const [id, resource] of this.resources) {\n if (!this.resourceRefCount.has(id) || this.resourceRefCount.get(id) === 0) {\n unused.push(resource);\n }\n }\n\n return unused;\n }\n\n // Time operations\n getDuration(): TimeUs {\n return this.durationUs;\n }\n\n getTrackDuration(trackId: string): TimeUs {\n const track = this.findTrack(trackId);\n if (!track || track.clips.length === 0) return 0;\n\n // Since clips are sorted, last clip determines duration\n const lastClip = track.clips[track.clips.length - 1];\n return (lastClip?.startUs ?? 0) + (lastClip?.durationUs ?? 0);\n }\n\n buildIndexes(options?: {\n incremental?: boolean;\n trackId?: string;\n clipId?: string;\n clip?: Clip;\n operation?: 'add' | 'update' | 'remove';\n }): void {\n const track = options?.trackId ? this.findTrack(options.trackId) : undefined;\n const isAttachmentTrack = track && track.kind !== 'video' && track.kind !== 'audio';\n // Incremental update for video/audio track clip operations\n if (options?.incremental && !isAttachmentTrack && options.clipId && options.operation) {\n const clip = options.clip ?? this.clipMap.get(options.clipId);\n\n if (options.operation === 'add' && clip) {\n this.addClipToIndexes(clip);\n } else if (options.operation === 'remove' && clip) {\n this.removeClipFromIndexes(clip);\n } else if (options.operation === 'update' && clip) {\n // Handle resource change during update\n if (clip.oldResourceId) {\n this.updateClipResourceIndex(\n options.clipId,\n clip.oldResourceId,\n hasResourceId(clip) ? clip.resourceId : undefined\n );\n }\n }\n\n // Recalculate duration only if affected main track\n if (track?.id === this.mainTrackId) {\n this.recalculateDuration();\n }\n return;\n }\n\n // Full rebuild: needed for attachment tracks or initial load\n this.fullRebuildIndexes();\n }\n\n private fullRebuildIndexes(): void {\n // Clear existing indexes\n this.trackMap.clear();\n this.clipMap.clear();\n this.resourceRefCount.clear();\n\n // Step 1: Sink attachment tracks to main track (preserves original tracks)\n this.sinkAttachmentTracks();\n\n let maxEndUs = 0;\n\n // Step 2: Build all indexes in one pass (track, clip, resource)\n for (const track of this.tracks) {\n this.trackMap.set(track.id, track);\n\n for (const clip of track.clips) {\n (clip as Clip).trackId = track.id;\n (clip as Clip).trackKind = track.kind;\n this.clipMap.set(clip.id, clip);\n\n // Main track resource index (only for clips with resourceId)\n if (hasResourceId(clip)) {\n const resource = this.resources.get(clip.resourceId);\n if (resource) {\n resource.clipIds = [...(resource.clipIds || []), clip.id];\n }\n const count = this.resourceRefCount.get(clip.resourceId) || 0;\n this.resourceRefCount.set(clip.resourceId, count + 1);\n }\n\n // Attachment resource indexes (attachments are already sunk)\n const attachments = clip.attachments ?? [];\n for (const attachment of attachments) {\n const attachmentResourceId = attachment.data?.resourceId;\n if (attachmentResourceId && typeof attachmentResourceId === 'string') {\n const attachmentResource = this.resources.get(attachmentResourceId);\n if (attachmentResource) {\n const clipIds = attachmentResource.clipIds || [];\n if (!clipIds.includes(clip.id)) {\n attachmentResource.clipIds = [...clipIds, clip.id];\n }\n }\n const attachmentCount = this.resourceRefCount.get(attachmentResourceId) || 0;\n this.resourceRefCount.set(attachmentResourceId, attachmentCount + 1);\n }\n }\n\n // Calculate max end time\n const clipEndUs = clip.startUs + clip.durationUs;\n if (clipEndUs > maxEndUs) {\n maxEndUs = clipEndUs;\n }\n }\n }\n\n this.durationUs = maxEndUs;\n }\n\n private addClipToIndexes(clip: Clip): void {\n this.clipMap.set(clip.id, clip);\n\n if (hasResourceId(clip)) {\n const resource = this.resources.get(clip.resourceId);\n if (resource) {\n if (!resource.clipIds) {\n resource.clipIds = [];\n }\n if (!resource.clipIds.includes(clip.id)) {\n resource.clipIds.push(clip.id);\n }\n }\n const count = this.resourceRefCount.get(clip.resourceId) || 0;\n this.resourceRefCount.set(clip.resourceId, count + 1);\n }\n }\n\n private removeClipFromIndexes(clip: Clip): void {\n this.clipMap.delete(clip.id);\n\n const resourceId = clip.oldResourceId || (hasResourceId(clip) ? clip.resourceId : undefined);\n if (resourceId) {\n const resource = this.resources.get(resourceId);\n if (resource?.clipIds) {\n resource.clipIds = resource.clipIds.filter((id) => id !== clip.id);\n }\n const count = this.resourceRefCount.get(resourceId) || 0;\n this.resourceRefCount.set(resourceId, Math.max(0, count - 1));\n }\n }\n\n /**\n * Incrementally update resource index when clip's resourceId changes\n */\n updateClipResourceIndex(\n clipId: string,\n oldResourceId: string | undefined,\n newResourceId: string | undefined\n ): void {\n // Remove from old resource\n if (oldResourceId) {\n const oldResource = this.resources.get(oldResourceId);\n if (oldResource?.clipIds) {\n oldResource.clipIds = oldResource.clipIds.filter((id) => id !== clipId);\n }\n const oldCount = this.resourceRefCount.get(oldResourceId) || 0;\n this.resourceRefCount.set(oldResourceId, Math.max(0, oldCount - 1));\n }\n\n // Add to new resource\n if (newResourceId) {\n const newResource = this.resources.get(newResourceId);\n if (newResource) {\n if (!newResource.clipIds) {\n newResource.clipIds = [];\n }\n if (!newResource.clipIds.includes(clipId)) {\n newResource.clipIds.push(clipId);\n }\n }\n const newCount = this.resourceRefCount.get(newResourceId) || 0;\n this.resourceRefCount.set(newResourceId, newCount + 1);\n }\n }\n\n /**\n * Recalculate total duration based on main track\n */\n recalculateDuration(): void {\n const mainTrack = this.findTrack(this.mainTrackId);\n if (!mainTrack || mainTrack.clips.length === 0) {\n this.durationUs = 0;\n return;\n }\n\n // Since clips are sorted, last clip determines duration\n const lastClip = mainTrack.clips[mainTrack.clips.length - 1];\n this.durationUs = (lastClip?.startUs ?? 0) + (lastClip?.durationUs ?? 0);\n }\n\n private sinkAttachmentTracks(): void {\n let mainTrack: Track | undefined;\n const attachmentTracks = [];\n\n // Sort all tracks\n for (const track of this.tracks) {\n track.clips.sort((a, b) => a.startUs - b.startUs);\n\n if (track.id === this.mainTrackId) {\n mainTrack = track;\n continue;\n }\n // Collect attachment tracks for sinking (caption, overlay, fx)\n // Video and audio tracks are not sunk\n if (track.kind === 'caption' || track.kind === 'overlay' || track.kind === 'fx') {\n attachmentTracks.push(track);\n }\n }\n\n if (!mainTrack) {\n throw new Error('Main track not found');\n }\n\n // Clear existing attachments before sinking (in case of rebuild)\n for (const clip of mainTrack.clips) {\n clip.attachments = [];\n }\n\n for (const attachmentTrack of attachmentTracks) {\n for (const attachmentClip of attachmentTrack.clips) {\n // Use track.kind directly as attachment kind\n const attachmentKind = attachmentTrack.kind;\n\n for (const mainClip of mainTrack.clips) {\n const overlap = this.getTimeOverlap(attachmentClip, mainClip);\n if (!overlap) continue;\n\n if (!mainClip.attachments) {\n mainClip.attachments = [];\n }\n\n // Extract animation effect\n const animationEffect = attachmentClip.effects?.find(\n (e) => e.effectType === 'animation'\n ) as AnimationEffect | undefined;\n\n // Extract target dimensions from metadata\n const targetWidth = attachmentClip.metadata?.targetWidth as number | undefined;\n const targetHeight = attachmentClip.metadata?.targetHeight as number | undefined;\n\n const attachmentData: Record<string, unknown> = {\n animation: animationEffect?.params,\n overlayClipStartUs: attachmentClip.startUs,\n mainClipStartUs: mainClip.startUs,\n ...(targetWidth !== undefined && { targetWidth }),\n ...(targetHeight !== undefined && { targetHeight }),\n };\n\n // Add resourceId if exists (trackKind not yet set, check field directly)\n if ('resourceId' in attachmentClip && attachmentClip.resourceId) {\n attachmentData.resourceId = attachmentClip.resourceId;\n }\n\n // Add text if exists (trackKind not yet set, check field directly)\n if ('text' in attachmentClip && attachmentClip.text) {\n attachmentData.text = attachmentClip.text;\n }\n\n mainClip.attachments.push({\n id: `${attachmentKind}-${attachmentClip.id}-${mainClip.id}`,\n kind: attachmentKind,\n startUs: overlap.clipRelativeStart,\n durationUs: overlap.duration,\n data: attachmentData,\n });\n }\n }\n }\n }\n\n private getTimeOverlap(\n attachmentClip: Clip,\n mainClip: Clip\n ): { clipRelativeStart: number; duration: number } | null {\n const attachmentStart = attachmentClip.startUs;\n const attachmentEnd = attachmentClip.startUs + attachmentClip.durationUs;\n const mainStart = mainClip.startUs;\n const mainEnd = mainClip.startUs + mainClip.durationUs;\n\n if (attachmentEnd <= mainStart || attachmentStart >= mainEnd) {\n return null;\n }\n\n const overlapStart = Math.max(attachmentStart, mainStart);\n const overlapEnd = Math.min(attachmentEnd, mainEnd);\n const duration = overlapEnd - overlapStart;\n const clipRelativeStart = overlapStart - mainStart;\n\n return { clipRelativeStart, duration };\n }\n}\n"],"names":[],"mappings":"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"}
|
package/dist/model/types.d.ts
CHANGED
|
@@ -227,9 +227,5 @@ export declare function isCaptionClip(clip: Clip): clip is CaptionClip;
|
|
|
227
227
|
export declare function isFxClip(clip: Clip): clip is FxClip;
|
|
228
228
|
export declare function hasResourceId(clip: Clip): clip is VideoClip | AudioClip;
|
|
229
229
|
export declare function hasAudioConfig(clip: Clip): clip is VideoClip | AudioClip;
|
|
230
|
-
export interface SetCompositionModelOptions {
|
|
231
|
-
clearL1Cache?: boolean;
|
|
232
|
-
needExport?: boolean;
|
|
233
|
-
}
|
|
234
230
|
export {};
|
|
235
231
|
//# sourceMappingURL=types.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/model/types.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/model/types.ts"],"names":[],"mappings":"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"}
|
package/dist/model/types.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.js","sources":["../../src/model/types.ts"],"sourcesContent":["// All time values in microseconds (µs)\nexport type TimeUs = number; // 1 second = 1_000_000 µs\n\n// Helper constants\nexport const MICROSECONDS_PER_SECOND = 1_000_000;\nexport const MICROSECONDS_PER_MILLISECOND = 1_000;\n\n// ────── Root Object ──────\nexport interface CompositionModelData {\n version: '1.0';\n fps: 24 | 25 | 30 | 60;\n durationUs: TimeUs;\n tracks: Track[];\n resources: Record<string, Resource>;\n\n mainTrackId?: string;\n renderConfig?: RenderConfig;\n\n ext?: Record<string, unknown>;\n}\n\nexport interface RenderConfig {\n width: number;\n height: number;\n backgroundColor?: string;\n}\n\n// ────── Track ──────\nexport interface Track {\n id: string;\n kind: 'video' | 'audio' | 'caption' | 'overlay' | 'fx';\n clips: Clip[];\n\n effects?: Effect[];\n duckingRules?: DuckingRule[];\n}\n\n// ────── Clip ──────\ninterface BaseClip {\n id: string;\n startUs: TimeUs;\n durationUs: TimeUs;\n trackId?: string;\n\n trimStartUs?: TimeUs;\n trimEndUs?: TimeUs;\n\n effects?: Effect[];\n attachments?: Attachment[];\n\n transitionIn?: Transition;\n transitionOut?: Transition;\n\n metadata?: {\n purpose?: 'caption' | 'overlay' | 'mask';\n [key: string]: unknown;\n };\n\n // Internal: temporary field for tracking old resourceId during patch operations\n oldResourceId?: string;\n}\n\nexport interface VideoClip extends BaseClip {\n trackKind: 'video';\n resourceId: string;\n\n // Audio configuration for video's original sound\n audioConfig?: {\n volume?: number; // 0.0-1.0, default 1.0\n muted?: boolean; // default false\n };\n}\n\nexport interface AudioClip extends BaseClip {\n trackKind: 'audio';\n resourceId: string;\n\n // Audio configuration\n audioConfig?: {\n volume?: number; // 0.0-1.0, default 1.0\n muted?: boolean; // default false\n };\n}\n\nexport interface CaptionClip extends BaseClip {\n trackKind: 'caption';\n text: string;\n localeCode?: string;\n fontTemplate?: string;\n fontFamily?: string;\n wordTimings?: Array<{\n text: string;\n startUs: TimeUs;\n endUs: TimeUs;\n }>;\n animation?: {\n type: 'none' | 'fade' | 'wordByWord' | 'characterKTV' | 'wordByWordFancy' | 'wordByWordSlideUp';\n [key: string]: unknown;\n };\n letterCase?: 'upper' | 'lower' | 'none';\n}\n\nexport interface FxClip extends BaseClip {\n trackKind: 'fx';\n}\n\nexport interface OverlayClip extends BaseClip {\n trackKind: 'overlay';\n resourceId: string;\n\n // Optional overlay-specific config\n opacity?: number; // 0.0-1.0, default 1.0\n}\n\nexport type Clip = VideoClip | AudioClip | CaptionClip | OverlayClip | FxClip;\n\n// ────── Resource ──────\nexport interface Resource {\n id: string;\n type: 'video' | 'image' | 'audio' | 'json' | string;\n uri: string;\n metadata?: Record<string, unknown>;\n clipIds?: string[];\n // Runtime state maintained by engine\n state?: 'pending' | 'loading' | 'ready' | 'error';\n}\n\n// ────── Common Structures ──────\nexport interface Effect {\n id: string;\n effectType: 'filter' | 'lut' | 'animation' | string;\n params?: Record<string, unknown>;\n}\n\n// Animation effect (effectType: 'animation')\nexport interface AnimationEffect extends Effect {\n effectType: 'animation';\n params: {\n position: { x: number; y: number };\n keyframes: AnimationKeyframe[];\n };\n}\n\nexport interface AnimationKeyframe {\n time: number; // Relative to clip.startUs (microseconds)\n transform?: Transform2D;\n opacity?: number;\n easing?: 'linear' | 'ease-in' | 'ease-out' | 'ease-in-out';\n}\n\nexport interface Transform2D {\n x?: number; // Relative offset\n y?: number;\n scaleX?: number;\n scaleY?: number;\n rotation?: number; // Degrees\n anchorX?: number; // Rotation center x (0-1)\n anchorY?: number; // Rotation center y (0-1)\n}\n\nexport interface Transition {\n id: string;\n transitionType: 'fade' | 'wipe' | 'slide' | string;\n durationUs: TimeUs;\n curve?: 'linear' | 'ease-in' | 'ease-out' | string;\n params?: Record<string, unknown>;\n}\n\nexport interface Attachment {\n id: string;\n kind: 'caption' | 'overlay' | 'mask' | string;\n startUs: TimeUs;\n durationUs: TimeUs;\n data: Record<string, unknown>;\n}\n\nexport interface CaptionAttachmentData {\n text: string;\n localeCode?: string;\n fontTemplate?: string;\n fontFamily?: string;\n wordTimings?: Array<{\n text: string;\n startUs: TimeUs;\n endUs: TimeUs;\n }>;\n animation?: {\n type: 'none' | 'fade' | 'wordByWord' | 'characterKTV' | 'wordByWordFancy' | 'wordByWordSlideUp';\n [key: string]: unknown;\n };\n letterCase?: 'upper' | 'lower' | 'none';\n overlayClipStartUs?: TimeUs;\n mainClipStartUs?: TimeUs;\n}\n\nexport interface DuckingRule {\n targetTrackKind: 'voice' | 'audio' | string;\n ratio: number;\n attackMs: number;\n releaseMs: number;\n}\n\n// ────── Patch System ──────\nexport interface CompositionPatch {\n operations: PatchOperation[];\n metadata?: {\n timestamp: number;\n source?: string;\n version?: string;\n };\n}\n\nexport type PatchOperation =\n | TrackOperation\n | ClipOperation\n | ResourceOperation\n | AttachmentOperation\n | TransitionOperation\n | EffectOperation\n | RenderConfigOperation;\n\n// Track operations\nexport interface TrackOperation {\n type: 'addTrack' | 'updateTrack' | 'removeTrack';\n trackId?: string;\n track?: Partial<Track>;\n}\n\n// Clip operations\nexport interface ClipOperation {\n type: 'addClip' | 'updateClip' | 'removeClip' | 'moveClip';\n trackId: string;\n clipId?: string;\n clip?: Partial<Clip>;\n targetTrackId?: string;\n targetStartUs?: TimeUs;\n}\n\n// Resource operations\nexport interface ResourceOperation {\n type: 'addResource' | 'updateResource' | 'removeResource';\n resourceId: string;\n resource?: Partial<Resource>;\n}\n\n// Attachment operations\nexport interface AttachmentOperation {\n type: 'addAttachment' | 'updateAttachment' | 'removeAttachment';\n trackId: string;\n clipId: string;\n attachmentId?: string;\n attachment?: Partial<Attachment>;\n}\n\n// Transition operations\nexport interface TransitionOperation {\n type: 'addTransition' | 'updateTransition' | 'removeTransition';\n trackId: string;\n clipId: string;\n position: 'in' | 'out';\n transition?: Partial<Transition>;\n}\n\n// Render config operations\nexport interface RenderConfigOperation {\n type: 'updateRenderConfig';\n renderConfig?: Partial<RenderConfig>;\n}\n\n// Effect operations\nexport interface EffectOperation {\n type: 'addEffect' | 'updateEffect' | 'removeEffect';\n targetType: 'track' | 'clip';\n targetId: string;\n effectId?: string;\n effect?: Partial<Effect>;\n}\n\n// ────── Dirty Range ──────\nexport interface DirtyRange {\n trackId: string;\n startUs: TimeUs;\n endUs: TimeUs;\n reason: string;\n}\n\n// ────── Validation ──────\nexport interface ValidationError {\n path: string;\n message: string;\n value: any;\n}\n\n// ────── Type Guards ──────\nexport function isVideoClip(clip: Clip): clip is VideoClip {\n return clip.trackKind === 'video';\n}\n\nexport function isAudioClip(clip: Clip): clip is AudioClip {\n return clip.trackKind === 'audio';\n}\n\nexport function isCaptionClip(clip: Clip): clip is CaptionClip {\n return clip.trackKind === 'caption';\n}\n\nexport function isFxClip(clip: Clip): clip is FxClip {\n return clip.trackKind === 'fx';\n}\n\nexport function hasResourceId(clip: Clip): clip is VideoClip | AudioClip {\n return isVideoClip(clip) || isAudioClip(clip);\n}\n\nexport function hasAudioConfig(clip: Clip): clip is VideoClip | AudioClip {\n return isVideoClip(clip) || isAudioClip(clip);\n}\n
|
|
1
|
+
{"version":3,"file":"types.js","sources":["../../src/model/types.ts"],"sourcesContent":["// All time values in microseconds (µs)\nexport type TimeUs = number; // 1 second = 1_000_000 µs\n\n// Helper constants\nexport const MICROSECONDS_PER_SECOND = 1_000_000;\nexport const MICROSECONDS_PER_MILLISECOND = 1_000;\n\n// ────── Root Object ──────\nexport interface CompositionModelData {\n version: '1.0';\n fps: 24 | 25 | 30 | 60;\n durationUs: TimeUs;\n tracks: Track[];\n resources: Record<string, Resource>;\n\n mainTrackId?: string;\n renderConfig?: RenderConfig;\n\n ext?: Record<string, unknown>;\n}\n\nexport interface RenderConfig {\n width: number;\n height: number;\n backgroundColor?: string;\n}\n\n// ────── Track ──────\nexport interface Track {\n id: string;\n kind: 'video' | 'audio' | 'caption' | 'overlay' | 'fx';\n clips: Clip[];\n\n effects?: Effect[];\n duckingRules?: DuckingRule[];\n}\n\n// ────── Clip ──────\ninterface BaseClip {\n id: string;\n startUs: TimeUs;\n durationUs: TimeUs;\n trackId?: string;\n\n trimStartUs?: TimeUs;\n trimEndUs?: TimeUs;\n\n effects?: Effect[];\n attachments?: Attachment[];\n\n transitionIn?: Transition;\n transitionOut?: Transition;\n\n metadata?: {\n purpose?: 'caption' | 'overlay' | 'mask';\n [key: string]: unknown;\n };\n\n // Internal: temporary field for tracking old resourceId during patch operations\n oldResourceId?: string;\n}\n\nexport interface VideoClip extends BaseClip {\n trackKind: 'video';\n resourceId: string;\n\n // Audio configuration for video's original sound\n audioConfig?: {\n volume?: number; // 0.0-1.0, default 1.0\n muted?: boolean; // default false\n };\n}\n\nexport interface AudioClip extends BaseClip {\n trackKind: 'audio';\n resourceId: string;\n\n // Audio configuration\n audioConfig?: {\n volume?: number; // 0.0-1.0, default 1.0\n muted?: boolean; // default false\n };\n}\n\nexport interface CaptionClip extends BaseClip {\n trackKind: 'caption';\n text: string;\n localeCode?: string;\n fontTemplate?: string;\n fontFamily?: string;\n wordTimings?: Array<{\n text: string;\n startUs: TimeUs;\n endUs: TimeUs;\n }>;\n animation?: {\n type: 'none' | 'fade' | 'wordByWord' | 'characterKTV' | 'wordByWordFancy' | 'wordByWordSlideUp';\n [key: string]: unknown;\n };\n letterCase?: 'upper' | 'lower' | 'none';\n}\n\nexport interface FxClip extends BaseClip {\n trackKind: 'fx';\n}\n\nexport interface OverlayClip extends BaseClip {\n trackKind: 'overlay';\n resourceId: string;\n\n // Optional overlay-specific config\n opacity?: number; // 0.0-1.0, default 1.0\n}\n\nexport type Clip = VideoClip | AudioClip | CaptionClip | OverlayClip | FxClip;\n\n// ────── Resource ──────\nexport interface Resource {\n id: string;\n type: 'video' | 'image' | 'audio' | 'json' | string;\n uri: string;\n metadata?: Record<string, unknown>;\n clipIds?: string[];\n // Runtime state maintained by engine\n state?: 'pending' | 'loading' | 'ready' | 'error';\n}\n\n// ────── Common Structures ──────\nexport interface Effect {\n id: string;\n effectType: 'filter' | 'lut' | 'animation' | string;\n params?: Record<string, unknown>;\n}\n\n// Animation effect (effectType: 'animation')\nexport interface AnimationEffect extends Effect {\n effectType: 'animation';\n params: {\n position: { x: number; y: number };\n keyframes: AnimationKeyframe[];\n };\n}\n\nexport interface AnimationKeyframe {\n time: number; // Relative to clip.startUs (microseconds)\n transform?: Transform2D;\n opacity?: number;\n easing?: 'linear' | 'ease-in' | 'ease-out' | 'ease-in-out';\n}\n\nexport interface Transform2D {\n x?: number; // Relative offset\n y?: number;\n scaleX?: number;\n scaleY?: number;\n rotation?: number; // Degrees\n anchorX?: number; // Rotation center x (0-1)\n anchorY?: number; // Rotation center y (0-1)\n}\n\nexport interface Transition {\n id: string;\n transitionType: 'fade' | 'wipe' | 'slide' | string;\n durationUs: TimeUs;\n curve?: 'linear' | 'ease-in' | 'ease-out' | string;\n params?: Record<string, unknown>;\n}\n\nexport interface Attachment {\n id: string;\n kind: 'caption' | 'overlay' | 'mask' | string;\n startUs: TimeUs;\n durationUs: TimeUs;\n data: Record<string, unknown>;\n}\n\nexport interface CaptionAttachmentData {\n text: string;\n localeCode?: string;\n fontTemplate?: string;\n fontFamily?: string;\n wordTimings?: Array<{\n text: string;\n startUs: TimeUs;\n endUs: TimeUs;\n }>;\n animation?: {\n type: 'none' | 'fade' | 'wordByWord' | 'characterKTV' | 'wordByWordFancy' | 'wordByWordSlideUp';\n [key: string]: unknown;\n };\n letterCase?: 'upper' | 'lower' | 'none';\n overlayClipStartUs?: TimeUs;\n mainClipStartUs?: TimeUs;\n}\n\nexport interface DuckingRule {\n targetTrackKind: 'voice' | 'audio' | string;\n ratio: number;\n attackMs: number;\n releaseMs: number;\n}\n\n// ────── Patch System ──────\nexport interface CompositionPatch {\n operations: PatchOperation[];\n metadata?: {\n timestamp: number;\n source?: string;\n version?: string;\n };\n}\n\nexport type PatchOperation =\n | TrackOperation\n | ClipOperation\n | ResourceOperation\n | AttachmentOperation\n | TransitionOperation\n | EffectOperation\n | RenderConfigOperation;\n\n// Track operations\nexport interface TrackOperation {\n type: 'addTrack' | 'updateTrack' | 'removeTrack';\n trackId?: string;\n track?: Partial<Track>;\n}\n\n// Clip operations\nexport interface ClipOperation {\n type: 'addClip' | 'updateClip' | 'removeClip' | 'moveClip';\n trackId: string;\n clipId?: string;\n clip?: Partial<Clip>;\n targetTrackId?: string;\n targetStartUs?: TimeUs;\n}\n\n// Resource operations\nexport interface ResourceOperation {\n type: 'addResource' | 'updateResource' | 'removeResource';\n resourceId: string;\n resource?: Partial<Resource>;\n}\n\n// Attachment operations\nexport interface AttachmentOperation {\n type: 'addAttachment' | 'updateAttachment' | 'removeAttachment';\n trackId: string;\n clipId: string;\n attachmentId?: string;\n attachment?: Partial<Attachment>;\n}\n\n// Transition operations\nexport interface TransitionOperation {\n type: 'addTransition' | 'updateTransition' | 'removeTransition';\n trackId: string;\n clipId: string;\n position: 'in' | 'out';\n transition?: Partial<Transition>;\n}\n\n// Render config operations\nexport interface RenderConfigOperation {\n type: 'updateRenderConfig';\n renderConfig?: Partial<RenderConfig>;\n}\n\n// Effect operations\nexport interface EffectOperation {\n type: 'addEffect' | 'updateEffect' | 'removeEffect';\n targetType: 'track' | 'clip';\n targetId: string;\n effectId?: string;\n effect?: Partial<Effect>;\n}\n\n// ────── Dirty Range ──────\nexport interface DirtyRange {\n trackId: string;\n startUs: TimeUs;\n endUs: TimeUs;\n reason: string;\n}\n\n// ────── Validation ──────\nexport interface ValidationError {\n path: string;\n message: string;\n value: any;\n}\n\n// ────── Type Guards ──────\nexport function isVideoClip(clip: Clip): clip is VideoClip {\n return clip.trackKind === 'video';\n}\n\nexport function isAudioClip(clip: Clip): clip is AudioClip {\n return clip.trackKind === 'audio';\n}\n\nexport function isCaptionClip(clip: Clip): clip is CaptionClip {\n return clip.trackKind === 'caption';\n}\n\nexport function isFxClip(clip: Clip): clip is FxClip {\n return clip.trackKind === 'fx';\n}\n\nexport function hasResourceId(clip: Clip): clip is VideoClip | AudioClip {\n return isVideoClip(clip) || isAudioClip(clip);\n}\n\nexport function hasAudioConfig(clip: Clip): clip is VideoClip | AudioClip {\n return isVideoClip(clip) || isAudioClip(clip);\n}\n"],"names":[],"mappings":"AAsSO,SAAS,YAAY,MAA+B;AACzD,SAAO,KAAK,cAAc;AAC5B;AAEO,SAAS,YAAY,MAA+B;AACzD,SAAO,KAAK,cAAc;AAC5B;AAUO,SAAS,cAAc,MAA2C;AACvE,SAAO,YAAY,IAAI,KAAK,YAAY,IAAI;AAC9C;AAEO,SAAS,eAAe,MAA2C;AACxE,SAAO,YAAY,IAAI,KAAK,YAAY,IAAI;AAC9C;"}
|
|
@@ -29,6 +29,16 @@ export declare class ExportScheduler {
|
|
|
29
29
|
private deps;
|
|
30
30
|
constructor(deps: ExportSchedulerDeps);
|
|
31
31
|
execute(model: CompositionModel, options: ExtendedExportOptions): Promise<Blob>;
|
|
32
|
+
/**
|
|
33
|
+
* Preload all resources (0-40% progress)
|
|
34
|
+
*/
|
|
35
|
+
private preloadResources;
|
|
36
|
+
/**
|
|
37
|
+
* Process audio in 60-second windows
|
|
38
|
+
* - Videos ≤60s: Single pass (zero boundaries)
|
|
39
|
+
* - Videos >60s: 60s windows (minimal boundaries, ~23MB per window)
|
|
40
|
+
*/
|
|
41
|
+
private processAudioInWindows;
|
|
32
42
|
private processVideoClipsSequentially;
|
|
33
43
|
}
|
|
34
44
|
export {};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ExportScheduler.d.ts","sourceRoot":"","sources":["../../src/orchestrator/ExportScheduler.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AAC5C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAClD,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAC1D,OAAO,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AACrD,OAAO,EAAE,cAAc,EAAE,MAAM,+BAA+B,CAAC;AAC/D,OAAO,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AACtD,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAE1D,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAE7C,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,iCAAiC,CAAC;AACxE,OAAO,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAC7C,OAAO,EAAgB,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAEhE,UAAU,mBAAmB;IAC3B,UAAU,EAAE,UAAU,CAAC;IACvB,OAAO,EAAE,kBAAkB,CAAC;IAC5B,YAAY,EAAE,YAAY,CAAC;IAC3B,cAAc,EAAE,cAAc,CAAC;IAC/B,UAAU,EAAE,UAAU,CAAC;IACvB,YAAY,EAAE,kBAAkB,CAAC;IACjC,qBAAqB,EAAE,MAAM,MAAM,CAAC,UAAU,EAAE,GAAG,CAAC,CAAC;IACrD,QAAQ,EAAE,QAAQ,CAAC,eAAe,CAAC,CAAC;CACrC;AAED,UAAU,qBAAsB,SAAQ,aAAa;IACnD,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,UAAU,CAAC,EAAE,gBAAgB,CAAC;CAC/B;AAED,qBAAa,eAAe;IAC1B,OAAO,CAAC,IAAI,CAAsB;gBAEtB,IAAI,EAAE,mBAAmB;IAI/B,OAAO,CAAC,KAAK,EAAE,gBAAgB,EAAE,OAAO,EAAE,qBAAqB,GAAG,OAAO,CAAC,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"ExportScheduler.d.ts","sourceRoot":"","sources":["../../src/orchestrator/ExportScheduler.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AAC5C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAClD,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAC1D,OAAO,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AACrD,OAAO,EAAE,cAAc,EAAE,MAAM,+BAA+B,CAAC;AAC/D,OAAO,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AACtD,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAE1D,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAE7C,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,iCAAiC,CAAC;AACxE,OAAO,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAC7C,OAAO,EAAgB,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAEhE,UAAU,mBAAmB;IAC3B,UAAU,EAAE,UAAU,CAAC;IACvB,OAAO,EAAE,kBAAkB,CAAC;IAC5B,YAAY,EAAE,YAAY,CAAC;IAC3B,cAAc,EAAE,cAAc,CAAC;IAC/B,UAAU,EAAE,UAAU,CAAC;IACvB,YAAY,EAAE,kBAAkB,CAAC;IACjC,qBAAqB,EAAE,MAAM,MAAM,CAAC,UAAU,EAAE,GAAG,CAAC,CAAC;IACrD,QAAQ,EAAE,QAAQ,CAAC,eAAe,CAAC,CAAC;CACrC;AAED,UAAU,qBAAsB,SAAQ,aAAa;IACnD,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,UAAU,CAAC,EAAE,gBAAgB,CAAC;CAC/B;AAED,qBAAa,eAAe;IAC1B,OAAO,CAAC,IAAI,CAAsB;gBAEtB,IAAI,EAAE,mBAAmB;IAI/B,OAAO,CAAC,KAAK,EAAE,gBAAgB,EAAE,OAAO,EAAE,qBAAqB,GAAG,OAAO,CAAC,IAAI,CAAC;IAuFrF;;OAEG;YACW,gBAAgB;IAwC9B;;;;OAIG;YACW,qBAAqB;YAsBrB,6BAA6B;CAwH5C"}
|
|
@@ -7,7 +7,7 @@ class ExportScheduler {
|
|
|
7
7
|
this.deps = deps;
|
|
8
8
|
}
|
|
9
9
|
async execute(model, options) {
|
|
10
|
-
const { muxManager, audioSession,
|
|
10
|
+
const { muxManager, audioSession, eventBus, resourceLoader } = this.deps;
|
|
11
11
|
const signal = options.signal;
|
|
12
12
|
const controller = options.controller;
|
|
13
13
|
const checkStatus = async () => {
|
|
@@ -32,33 +32,22 @@ class ExportScheduler {
|
|
|
32
32
|
durationUs: model.durationUs
|
|
33
33
|
});
|
|
34
34
|
try {
|
|
35
|
+
await this.preloadResources(model, resourceLoader, eventBus, checkStatus);
|
|
35
36
|
muxManager.start({
|
|
36
37
|
width,
|
|
37
38
|
height,
|
|
38
39
|
fps
|
|
39
40
|
});
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
priority: "high",
|
|
46
|
-
clipId: clip.id,
|
|
47
|
-
trackId: clip.trackId
|
|
48
|
-
});
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
const videoTrack = model.tracks.find((t) => t.kind === "video");
|
|
54
|
-
if (videoTrack && videoTrack.clips.length > 0) {
|
|
55
|
-
await this.processVideoClipsSequentially(
|
|
56
|
-
videoTrack.clips,
|
|
41
|
+
const mainTrack = model.tracks.find((t) => t.id === model.mainTrackId);
|
|
42
|
+
if (mainTrack && mainTrack.clips.length > 0) {
|
|
43
|
+
const audioPromise = this.processAudioInWindows(
|
|
44
|
+
model.durationUs,
|
|
45
|
+
audioSession,
|
|
57
46
|
muxManager,
|
|
58
|
-
|
|
59
|
-
checkStatus,
|
|
60
|
-
audioSession
|
|
47
|
+
checkStatus
|
|
61
48
|
);
|
|
49
|
+
await this.processVideoClipsSequentially(mainTrack.clips, muxManager, model, checkStatus);
|
|
50
|
+
await audioPromise;
|
|
62
51
|
} else {
|
|
63
52
|
console.warn("[ExportScheduler] No video clips found");
|
|
64
53
|
}
|
|
@@ -81,20 +70,60 @@ class ExportScheduler {
|
|
|
81
70
|
throw error;
|
|
82
71
|
}
|
|
83
72
|
}
|
|
84
|
-
|
|
73
|
+
/**
|
|
74
|
+
* Preload all resources (0-40% progress)
|
|
75
|
+
*/
|
|
76
|
+
async preloadResources(model, resourceLoader, eventBus, checkStatus) {
|
|
77
|
+
eventBus.emit(MeframeEvent.ExportProgress, {
|
|
78
|
+
progress: 0,
|
|
79
|
+
stage: "preparing",
|
|
80
|
+
message: "Loading and parsing resources..."
|
|
81
|
+
});
|
|
82
|
+
const resourcesToLoad = [];
|
|
83
|
+
for (const track of model.tracks) {
|
|
84
|
+
if (!["video", "audio"].includes(track.kind)) continue;
|
|
85
|
+
for (const clip of track.clips) {
|
|
86
|
+
if (hasResourceId(clip)) {
|
|
87
|
+
resourcesToLoad.push(clip.resourceId);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
for (let i = 0; i < resourcesToLoad.length; i++) {
|
|
92
|
+
await checkStatus();
|
|
93
|
+
const resourceId = resourcesToLoad[i];
|
|
94
|
+
await resourceLoader.load(resourceId, { priority: "high" });
|
|
95
|
+
const progress = (i + 1) / resourcesToLoad.length * 0.4;
|
|
96
|
+
eventBus.emit(MeframeEvent.ExportProgress, {
|
|
97
|
+
progress,
|
|
98
|
+
stage: "preparing",
|
|
99
|
+
message: `Loading resources... (${i + 1}/${resourcesToLoad.length})`
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Process audio in 60-second windows
|
|
105
|
+
* - Videos ≤60s: Single pass (zero boundaries)
|
|
106
|
+
* - Videos >60s: 60s windows (minimal boundaries, ~23MB per window)
|
|
107
|
+
*/
|
|
108
|
+
async processAudioInWindows(totalDurationUs, audioSession, muxManager, checkStatus) {
|
|
109
|
+
const WINDOW_DURATION_US = 5 * 60 * 1e6;
|
|
110
|
+
let currentUs = 0;
|
|
111
|
+
while (currentUs < totalDurationUs) {
|
|
112
|
+
await checkStatus();
|
|
113
|
+
const endUs = Math.min(currentUs + WINDOW_DURATION_US, totalDurationUs);
|
|
114
|
+
await audioSession.mixAndEncodeSegment(
|
|
115
|
+
currentUs,
|
|
116
|
+
endUs,
|
|
117
|
+
(chunk, meta) => muxManager.writeAudioChunk(chunk, meta)
|
|
118
|
+
);
|
|
119
|
+
currentUs = endUs;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
async processVideoClipsSequentially(clips, muxManager, model, checkStatus) {
|
|
85
123
|
let accumulatedDurationUs = 0;
|
|
86
124
|
for (let i = 0; i < clips.length; i++) {
|
|
87
125
|
const clip = clips[i];
|
|
88
|
-
const nextClip = clips[i + 1];
|
|
89
126
|
await checkStatus();
|
|
90
|
-
if (nextClip && hasResourceId(nextClip)) {
|
|
91
|
-
this.deps.resourceLoader.fetch(nextClip.resourceId, {
|
|
92
|
-
priority: "normal",
|
|
93
|
-
clipId: nextClip.id,
|
|
94
|
-
trackId: nextClip.trackId,
|
|
95
|
-
isMainTrack: true
|
|
96
|
-
});
|
|
97
|
-
}
|
|
98
127
|
const sessionId = `${clip.id}-export`;
|
|
99
128
|
let streamFinishedResolver;
|
|
100
129
|
let streamFinishedRejecter;
|
|
@@ -102,11 +131,6 @@ class ExportScheduler {
|
|
|
102
131
|
streamFinishedResolver = resolve;
|
|
103
132
|
streamFinishedRejecter = reject;
|
|
104
133
|
});
|
|
105
|
-
let audioFinishedResolver;
|
|
106
|
-
const audioFinishedPromise = new Promise((resolve) => {
|
|
107
|
-
audioFinishedResolver = resolve;
|
|
108
|
-
});
|
|
109
|
-
let audioStreamStarted = false;
|
|
110
134
|
const session = await VideoClipSession.create({
|
|
111
135
|
clipId: clip.id,
|
|
112
136
|
sessionId,
|
|
@@ -139,13 +163,11 @@ class ExportScheduler {
|
|
|
139
163
|
data: buffer
|
|
140
164
|
});
|
|
141
165
|
muxManager.writeVideoChunk(remappedChunk, metadata);
|
|
142
|
-
const
|
|
143
|
-
|
|
144
|
-
(accumulatedDurationUs + originalChunk.timestamp) / model.durationUs
|
|
145
|
-
);
|
|
166
|
+
const encodingProgress = (accumulatedDurationUs + originalChunk.timestamp) / model.durationUs;
|
|
167
|
+
const totalProgress = 0.4 + encodingProgress * 0.6;
|
|
146
168
|
this.deps.eventBus.emit(MeframeEvent.ExportProgress, {
|
|
147
|
-
progress,
|
|
148
|
-
stage: "
|
|
169
|
+
progress: Math.min(1, totalProgress),
|
|
170
|
+
stage: "encoding",
|
|
149
171
|
timeUs: accumulatedDurationUs + originalChunk.timestamp
|
|
150
172
|
});
|
|
151
173
|
}
|
|
@@ -163,35 +185,9 @@ class ExportScheduler {
|
|
|
163
185
|
}
|
|
164
186
|
}
|
|
165
187
|
},
|
|
166
|
-
onAudioStreamReady: (stream, metadata) => {
|
|
167
|
-
audioStreamStarted = true;
|
|
168
|
-
const reader = stream.getReader();
|
|
169
|
-
const pump = async () => {
|
|
170
|
-
try {
|
|
171
|
-
while (true) {
|
|
172
|
-
const { done, value } = await reader.read();
|
|
173
|
-
if (done) break;
|
|
174
|
-
if (value) {
|
|
175
|
-
this.deps.cacheManager.putClipAudioData(
|
|
176
|
-
clip.id,
|
|
177
|
-
// Use clip.id, NOT sessionId (which is clipId-export)
|
|
178
|
-
value,
|
|
179
|
-
metadata.clipDurationUs
|
|
180
|
-
);
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
} catch (e) {
|
|
184
|
-
console.error("[ExportScheduler] Audio stream error", e);
|
|
185
|
-
} finally {
|
|
186
|
-
reader.releaseLock();
|
|
187
|
-
audioFinishedResolver();
|
|
188
|
-
}
|
|
189
|
-
};
|
|
190
|
-
pump();
|
|
191
|
-
},
|
|
192
188
|
onPipelineReady: async (attachmentResourceIds) => {
|
|
193
189
|
if (hasResourceId(clip)) {
|
|
194
|
-
await this.deps.resourceLoader.
|
|
190
|
+
await this.deps.resourceLoader.load(clip.resourceId, {
|
|
195
191
|
priority: "high",
|
|
196
192
|
sessionId,
|
|
197
193
|
clipId: clip.id,
|
|
@@ -201,7 +197,7 @@ class ExportScheduler {
|
|
|
201
197
|
}
|
|
202
198
|
if (attachmentResourceIds) {
|
|
203
199
|
for (const rid of attachmentResourceIds) {
|
|
204
|
-
await this.deps.resourceLoader.
|
|
200
|
+
await this.deps.resourceLoader.load(rid, {
|
|
205
201
|
priority: "normal",
|
|
206
202
|
sessionId,
|
|
207
203
|
clipId: clip.id,
|
|
@@ -215,19 +211,6 @@ class ExportScheduler {
|
|
|
215
211
|
try {
|
|
216
212
|
await session.activate();
|
|
217
213
|
await streamFinishedPromise;
|
|
218
|
-
if (audioStreamStarted) {
|
|
219
|
-
await Promise.race([
|
|
220
|
-
audioFinishedPromise,
|
|
221
|
-
new Promise((resolve) => setTimeout(resolve, 5e3))
|
|
222
|
-
// 5s timeout for audio tail
|
|
223
|
-
]);
|
|
224
|
-
} else {
|
|
225
|
-
}
|
|
226
|
-
await audioSession.mixAndEncodeSegment(
|
|
227
|
-
accumulatedDurationUs,
|
|
228
|
-
accumulatedDurationUs + clip.durationUs,
|
|
229
|
-
(chunk, meta) => muxManager.writeAudioChunk(chunk, meta)
|
|
230
|
-
);
|
|
231
214
|
} finally {
|
|
232
215
|
await session.dispose();
|
|
233
216
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ExportScheduler.js","sources":["../../src/orchestrator/ExportScheduler.ts"],"sourcesContent":["import { CompositionModel } from '../model';\nimport { ExportOptions } from '../types';\nimport { WorkerPool } from '../worker/WorkerPool';\nimport { CompositionPlanner } from './CompositionPlanner';\nimport { CacheManager } from '../cache/CacheManager';\nimport { ResourceLoader } from '../stages/load/ResourceLoader';\nimport { MuxManager } from '../stages/mux/MuxManager';\nimport { GlobalAudioSession } from './GlobalAudioSession';\nimport { VideoClipSession } from './VideoClipSession';\nimport { WorkerType } from '../worker/types';\nimport { hasResourceId } from '../model/types';\nimport type { ExportController } from '../controllers/ExportController';\nimport { EventBus } from '../event/EventBus';\nimport { MeframeEvent, EventPayloadMap } from '../event/events';\n\ninterface ExportSchedulerDeps {\n workerPool: WorkerPool;\n planner: CompositionPlanner;\n cacheManager: CacheManager;\n resourceLoader: ResourceLoader;\n muxManager: MuxManager;\n audioSession: GlobalAudioSession;\n workerConfigsProvider: () => Record<WorkerType, any>;\n eventBus: EventBus<EventPayloadMap>;\n}\n\ninterface ExtendedExportOptions extends ExportOptions {\n signal?: AbortSignal;\n controller?: ExportController;\n}\n\nexport class ExportScheduler {\n private deps: ExportSchedulerDeps;\n\n constructor(deps: ExportSchedulerDeps) {\n this.deps = deps;\n }\n\n async execute(model: CompositionModel, options: ExtendedExportOptions): Promise<Blob> {\n const { muxManager, audioSession, resourceLoader, eventBus } = this.deps;\n const signal = options.signal;\n const controller = options.controller;\n\n const checkStatus = async () => {\n if (signal?.aborted) {\n throw new DOMException('Export aborted', 'AbortError');\n }\n if (controller?.isPaused()) {\n // Wait until resumed\n while (controller.isPaused()) {\n if (signal?.aborted) throw new DOMException('Export aborted', 'AbortError');\n await new Promise((resolve) => setTimeout(resolve, 100));\n }\n }\n };\n\n const width = options.width || model.renderConfig?.width || 720;\n const height = options.height || model.renderConfig?.height || 1280;\n const fps = options.fps || model.fps || 30;\n\n eventBus.emit(MeframeEvent.ExportStart, {\n format: options.format || 'mp4',\n width,\n height,\n fps,\n durationUs: model.durationUs,\n });\n\n try {\n // 1. Start Muxer\n muxManager.start({\n width,\n height,\n fps,\n });\n\n // 2. Preload Audio Tracks (BGM/Speech)\n // Identify pure audio clips and start downloading them immediately\n // This ensures BGM is ready when we need to mix it with video clips\n for (const track of model.tracks) {\n if (track.kind === 'audio') {\n for (const clip of track.clips) {\n if (hasResourceId(clip)) {\n // Fire and forget - we don't await here, let them download in background\n // GlobalAudioSession will wait for them if needed during mixing\n resourceLoader.fetch(clip.resourceId, {\n priority: 'high',\n clipId: clip.id,\n trackId: clip.trackId,\n });\n }\n }\n }\n }\n\n // 3. Process Video Clips (Sequential) and Audio (Interleaved)\n const videoTrack = model.tracks.find((t) => t.kind === 'video');\n if (videoTrack && videoTrack.clips.length > 0) {\n await this.processVideoClipsSequentially(\n videoTrack.clips,\n muxManager,\n model,\n checkStatus,\n audioSession\n );\n } else {\n console.warn('[ExportScheduler] No video clips found');\n }\n\n // Finalize audio session (close encoder)\n await audioSession.finalizeExportAudio();\n\n if (signal?.aborted) {\n throw new DOMException('Export aborted', 'AbortError');\n }\n\n // 4. Finalize\n const blob = await muxManager.finalize();\n\n eventBus.emit(MeframeEvent.ExportComplete, {\n size: blob.size,\n durationMs: model.durationUs / 1000,\n format: options.format || 'mp4',\n });\n\n return blob;\n } catch (error) {\n eventBus.emit(MeframeEvent.ExportError, {\n error: error instanceof Error ? error : new Error(String(error)),\n stage: 'export',\n });\n throw error;\n }\n }\n\n private async processVideoClipsSequentially(\n clips: any[],\n muxManager: MuxManager,\n model: CompositionModel,\n checkStatus: () => Promise<void>,\n audioSession: GlobalAudioSession\n ) {\n let accumulatedDurationUs = 0;\n\n for (let i = 0; i < clips.length; i++) {\n const clip = clips[i];\n const nextClip = clips[i + 1];\n\n await checkStatus(); // Check before starting new clip\n\n // --- Preload Next Clip ---\n // Start downloading next clip's resource while current clip is processing\n if (nextClip && hasResourceId(nextClip)) {\n // Fire and forget - ResourceLoader handles deduping\n this.deps.resourceLoader.fetch(nextClip.resourceId, {\n priority: 'normal',\n clipId: nextClip.id,\n trackId: nextClip.trackId,\n isMainTrack: true,\n });\n }\n // -------------------------\n\n const sessionId = `${clip.id}-export`;\n let streamFinishedResolver: () => void;\n let streamFinishedRejecter: (err: any) => void;\n const streamFinishedPromise = new Promise<void>((resolve, reject) => {\n streamFinishedResolver = resolve;\n streamFinishedRejecter = reject;\n });\n\n let audioFinishedResolver: () => void;\n const audioFinishedPromise = new Promise<void>((resolve) => {\n audioFinishedResolver = resolve;\n });\n let audioStreamStarted = false;\n\n const session = await VideoClipSession.create({\n clipId: clip.id,\n sessionId,\n forL2Only: true,\n planner: this.deps.planner,\n workerPool: this.deps.workerPool,\n cacheManager: this.deps.cacheManager,\n compositionModel: model,\n workerConfigs: this.deps.workerConfigsProvider(),\n callbacks: {\n onComposedStreamReady: async () => {},\n onEncodedStreamReady: async (stream, track) => {\n if (track === 'video') {\n const reader = stream.getReader();\n try {\n while (true) {\n await checkStatus();\n\n const { done, value } = await reader.read();\n if (done) break;\n if (value) {\n const originalChunk = value.chunk;\n const metadata = value.metadata;\n\n const buffer = new ArrayBuffer(originalChunk.byteLength);\n originalChunk.copyTo(buffer);\n\n const remappedChunk = new EncodedVideoChunk({\n type: originalChunk.type,\n timestamp: originalChunk.timestamp + accumulatedDurationUs,\n duration: originalChunk.duration ?? undefined,\n data: buffer,\n });\n\n muxManager.writeVideoChunk(remappedChunk, metadata);\n\n // Emit progress\n const progress = Math.min(\n 1.0,\n (accumulatedDurationUs + originalChunk.timestamp) / model.durationUs\n );\n this.deps.eventBus.emit(MeframeEvent.ExportProgress, {\n progress,\n stage: 'muxing',\n timeUs: accumulatedDurationUs + originalChunk.timestamp,\n });\n }\n }\n streamFinishedResolver();\n } catch (error) {\n if (error instanceof DOMException && error.name === 'AbortError') {\n streamFinishedRejecter(error);\n } else {\n console.error(`[ExportScheduler] Stream error for clip ${clip.id}:`, error);\n streamFinishedRejecter(error);\n }\n } finally {\n reader.releaseLock();\n }\n }\n },\n onAudioStreamReady: (stream, metadata) => {\n audioStreamStarted = true;\n // Handle audio stream from VideoClipSession (AudioDecodeWorker)\n const reader = stream.getReader();\n const pump = async () => {\n try {\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n if (value) {\n // Put into CacheManager immediately so GlobalAudioSession can find it\n // Use clipId as sessionId because GlobalAudioSession expects that\n this.deps.cacheManager.putClipAudioData(\n clip.id, // Use clip.id, NOT sessionId (which is clipId-export)\n value,\n metadata.clipDurationUs\n );\n }\n }\n } catch (e) {\n console.error('[ExportScheduler] Audio stream error', e);\n } finally {\n reader.releaseLock();\n audioFinishedResolver(); // Signal audio stream completion\n }\n };\n pump();\n },\n onPipelineReady: async (attachmentResourceIds) => {\n // Load Main Track Resource\n if (hasResourceId(clip)) {\n await this.deps.resourceLoader.fetch(clip.resourceId, {\n priority: 'high',\n sessionId,\n clipId: clip.id,\n trackId: clip.trackId,\n isMainTrack: true,\n });\n }\n\n // Load Attachment Resources\n if (attachmentResourceIds) {\n for (const rid of attachmentResourceIds) {\n await this.deps.resourceLoader.fetch(rid, {\n priority: 'normal',\n sessionId,\n clipId: clip.id,\n isMainTrack: false,\n });\n }\n }\n },\n },\n });\n\n try {\n await session.activate();\n\n await streamFinishedPromise;\n\n // If audio stream started, we wait for it to finish.\n // This ensures we capture the tail of the audio.\n if (audioStreamStarted) {\n // Use a race with timeout to prevent hanging indefinitely if audio stream is stuck\n await Promise.race([\n audioFinishedPromise,\n new Promise((resolve) => setTimeout(resolve, 5000)), // 5s timeout for audio tail\n ]);\n } else {\n // If audio didn't start, maybe it's a silent clip, or audio init failed.\n // We just proceed.\n }\n\n // Mix and encode audio for this segment\n await audioSession.mixAndEncodeSegment(\n accumulatedDurationUs,\n accumulatedDurationUs + clip.durationUs,\n (chunk, meta) => muxManager.writeAudioChunk(chunk, meta)\n );\n } finally {\n await session.dispose();\n }\n\n accumulatedDurationUs += clip.durationUs;\n }\n }\n}\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ExportScheduler.js","sources":["../../src/orchestrator/ExportScheduler.ts"],"sourcesContent":["import { CompositionModel } from '../model';\nimport { ExportOptions } from '../types';\nimport { WorkerPool } from '../worker/WorkerPool';\nimport { CompositionPlanner } from './CompositionPlanner';\nimport { CacheManager } from '../cache/CacheManager';\nimport { ResourceLoader } from '../stages/load/ResourceLoader';\nimport { MuxManager } from '../stages/mux/MuxManager';\nimport { GlobalAudioSession } from './GlobalAudioSession';\nimport { VideoClipSession } from './VideoClipSession';\nimport { WorkerType } from '../worker/types';\nimport { hasResourceId, type TimeUs } from '../model/types';\nimport type { ExportController } from '../controllers/ExportController';\nimport { EventBus } from '../event/EventBus';\nimport { MeframeEvent, EventPayloadMap } from '../event/events';\n\ninterface ExportSchedulerDeps {\n workerPool: WorkerPool;\n planner: CompositionPlanner;\n cacheManager: CacheManager;\n resourceLoader: ResourceLoader;\n muxManager: MuxManager;\n audioSession: GlobalAudioSession;\n workerConfigsProvider: () => Record<WorkerType, any>;\n eventBus: EventBus<EventPayloadMap>;\n}\n\ninterface ExtendedExportOptions extends ExportOptions {\n signal?: AbortSignal;\n controller?: ExportController;\n}\n\nexport class ExportScheduler {\n private deps: ExportSchedulerDeps;\n\n constructor(deps: ExportSchedulerDeps) {\n this.deps = deps;\n }\n\n async execute(model: CompositionModel, options: ExtendedExportOptions): Promise<Blob> {\n const { muxManager, audioSession, eventBus, resourceLoader } = this.deps;\n const signal = options.signal;\n const controller = options.controller;\n\n const checkStatus = async () => {\n if (signal?.aborted) {\n throw new DOMException('Export aborted', 'AbortError');\n }\n if (controller?.isPaused()) {\n // Wait until resumed\n while (controller.isPaused()) {\n if (signal?.aborted) throw new DOMException('Export aborted', 'AbortError');\n await new Promise((resolve) => setTimeout(resolve, 100));\n }\n }\n };\n\n const width = options.width || model.renderConfig?.width || 720;\n const height = options.height || model.renderConfig?.height || 1280;\n const fps = options.fps || model.fps || 30;\n\n eventBus.emit(MeframeEvent.ExportStart, {\n format: options.format || 'mp4',\n width,\n height,\n fps,\n durationUs: model.durationUs,\n });\n\n try {\n // 1. Preload and parse all resources (0-40%)\n await this.preloadResources(model, resourceLoader, eventBus, checkStatus);\n\n // 2. Start Muxer\n muxManager.start({\n width,\n height,\n fps,\n });\n\n // 3. Process Video and Audio\n const mainTrack = model.tracks.find((t) => t.id === model.mainTrackId);\n if (mainTrack && mainTrack.clips.length > 0) {\n // Process audio with 60s windows (balance quality and memory)\n const audioPromise = this.processAudioInWindows(\n model.durationUs,\n audioSession,\n muxManager,\n checkStatus\n );\n\n // Process video clips sequentially\n await this.processVideoClipsSequentially(mainTrack.clips, muxManager, model, checkStatus);\n\n // Wait for audio encoding to complete\n await audioPromise;\n } else {\n console.warn('[ExportScheduler] No video clips found');\n }\n\n // Finalize audio session (close encoder)\n await audioSession.finalizeExportAudio();\n\n if (signal?.aborted) {\n throw new DOMException('Export aborted', 'AbortError');\n }\n\n // 4. Finalize\n const blob = await muxManager.finalize();\n\n eventBus.emit(MeframeEvent.ExportComplete, {\n size: blob.size,\n durationMs: model.durationUs / 1000,\n format: options.format || 'mp4',\n });\n\n return blob;\n } catch (error) {\n eventBus.emit(MeframeEvent.ExportError, {\n error: error instanceof Error ? error : new Error(String(error)),\n stage: 'export',\n });\n throw error;\n }\n }\n\n /**\n * Preload all resources (0-40% progress)\n */\n private async preloadResources(\n model: CompositionModel,\n resourceLoader: ResourceLoader,\n eventBus: EventBus<EventPayloadMap>,\n checkStatus: () => Promise<void>\n ): Promise<void> {\n eventBus.emit(MeframeEvent.ExportProgress, {\n progress: 0,\n stage: 'preparing',\n message: 'Loading and parsing resources...',\n });\n\n // Collect all resources to load\n const resourcesToLoad: string[] = [];\n for (const track of model.tracks) {\n if (!['video', 'audio'].includes(track.kind)) continue;\n for (const clip of track.clips) {\n if (hasResourceId(clip)) {\n resourcesToLoad.push(clip.resourceId);\n }\n }\n }\n\n // Load resources with progress updates\n for (let i = 0; i < resourcesToLoad.length; i++) {\n await checkStatus();\n\n const resourceId = resourcesToLoad[i];\n await resourceLoader.load(resourceId, { priority: 'high' });\n\n // Update progress: 0-40%\n const progress = ((i + 1) / resourcesToLoad.length) * 0.4;\n eventBus.emit(MeframeEvent.ExportProgress, {\n progress,\n stage: 'preparing',\n message: `Loading resources... (${i + 1}/${resourcesToLoad.length})`,\n });\n }\n }\n\n /**\n * Process audio in 60-second windows\n * - Videos ≤60s: Single pass (zero boundaries)\n * - Videos >60s: 60s windows (minimal boundaries, ~23MB per window)\n */\n private async processAudioInWindows(\n totalDurationUs: TimeUs,\n audioSession: GlobalAudioSession,\n muxManager: MuxManager,\n checkStatus: () => Promise<void>\n ): Promise<void> {\n const WINDOW_DURATION_US = 5 * 60 * 1_000_000; // 5 minutes\n let currentUs = 0;\n\n while (currentUs < totalDurationUs) {\n await checkStatus();\n\n const endUs = Math.min(currentUs + WINDOW_DURATION_US, totalDurationUs);\n\n await audioSession.mixAndEncodeSegment(currentUs, endUs, (chunk, meta) =>\n muxManager.writeAudioChunk(chunk, meta)\n );\n\n currentUs = endUs;\n }\n }\n\n private async processVideoClipsSequentially(\n clips: any[],\n muxManager: MuxManager,\n model: CompositionModel,\n checkStatus: () => Promise<void>\n ) {\n let accumulatedDurationUs = 0;\n\n for (let i = 0; i < clips.length; i++) {\n const clip = clips[i];\n // const nextClip = clips[i + 1];\n\n await checkStatus(); // Check before starting new clip\n\n const sessionId = `${clip.id}-export`;\n let streamFinishedResolver: () => void;\n let streamFinishedRejecter: (err: any) => void;\n const streamFinishedPromise = new Promise<void>((resolve, reject) => {\n streamFinishedResolver = resolve;\n streamFinishedRejecter = reject;\n });\n\n const session = await VideoClipSession.create({\n clipId: clip.id,\n sessionId,\n forL2Only: true,\n planner: this.deps.planner,\n workerPool: this.deps.workerPool,\n cacheManager: this.deps.cacheManager,\n compositionModel: model,\n workerConfigs: this.deps.workerConfigsProvider(),\n callbacks: {\n onComposedStreamReady: async () => {},\n onEncodedStreamReady: async (stream, track) => {\n if (track === 'video') {\n const reader = stream.getReader();\n try {\n while (true) {\n await checkStatus();\n\n const { done, value } = await reader.read();\n if (done) break;\n if (value) {\n const originalChunk = value.chunk;\n const metadata = value.metadata;\n\n const buffer = new ArrayBuffer(originalChunk.byteLength);\n originalChunk.copyTo(buffer);\n\n const remappedChunk = new EncodedVideoChunk({\n type: originalChunk.type,\n timestamp: originalChunk.timestamp + accumulatedDurationUs,\n duration: originalChunk.duration ?? undefined,\n data: buffer,\n });\n\n muxManager.writeVideoChunk(remappedChunk, metadata);\n\n // Emit progress: 40-100%\n const encodingProgress =\n (accumulatedDurationUs + originalChunk.timestamp) / model.durationUs;\n const totalProgress = 0.4 + encodingProgress * 0.6; // 40% + (0-60%)\n\n this.deps.eventBus.emit(MeframeEvent.ExportProgress, {\n progress: Math.min(1.0, totalProgress),\n stage: 'encoding',\n timeUs: accumulatedDurationUs + originalChunk.timestamp,\n });\n }\n }\n streamFinishedResolver();\n } catch (error) {\n if (error instanceof DOMException && error.name === 'AbortError') {\n streamFinishedRejecter(error);\n } else {\n console.error(`[ExportScheduler] Stream error for clip ${clip.id}:`, error);\n streamFinishedRejecter(error);\n }\n } finally {\n reader.releaseLock();\n }\n }\n },\n onPipelineReady: async (attachmentResourceIds) => {\n // Load Main Track Resource\n if (hasResourceId(clip)) {\n await this.deps.resourceLoader.load(clip.resourceId, {\n priority: 'high',\n sessionId,\n clipId: clip.id,\n trackId: clip.trackId,\n isMainTrack: true,\n });\n }\n\n // Load Attachment Resources\n if (attachmentResourceIds) {\n for (const rid of attachmentResourceIds) {\n await this.deps.resourceLoader.load(rid, {\n priority: 'normal',\n sessionId,\n clipId: clip.id,\n isMainTrack: false,\n });\n }\n }\n },\n },\n });\n\n try {\n await session.activate();\n await streamFinishedPromise;\n } finally {\n await session.dispose();\n }\n\n accumulatedDurationUs += clip.durationUs;\n }\n }\n}\n"],"names":[],"mappings":"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"}
|