@meframe/core 0.0.2 → 0.0.3
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.map +1 -1
- package/dist/Meframe.js +2 -1
- package/dist/Meframe.js.map +1 -1
- package/dist/config/defaults.d.ts.map +1 -1
- package/dist/config/defaults.js +2 -1
- package/dist/config/defaults.js.map +1 -1
- package/dist/config/types.d.ts +3 -0
- package/dist/config/types.d.ts.map +1 -1
- package/dist/orchestrator/Orchestrator.d.ts.map +1 -1
- package/dist/orchestrator/Orchestrator.js +2 -1
- package/dist/orchestrator/Orchestrator.js.map +1 -1
- package/dist/orchestrator/types.d.ts +1 -0
- package/dist/orchestrator/types.d.ts.map +1 -1
- package/dist/stages/compose/types.d.ts +2 -1
- package/dist/stages/compose/types.d.ts.map +1 -1
- package/dist/stages/demux/MP4Demuxer.d.ts +0 -1
- package/dist/stages/demux/MP4Demuxer.d.ts.map +1 -1
- package/dist/utils/time-utils.d.ts +3 -2
- package/dist/utils/time-utils.d.ts.map +1 -1
- package/dist/utils/time-utils.js +2 -1
- package/dist/utils/time-utils.js.map +1 -1
- package/dist/vite-plugin.d.ts +5 -3
- package/dist/vite-plugin.d.ts.map +1 -1
- package/dist/vite-plugin.js +109 -52
- package/dist/vite-plugin.js.map +1 -1
- package/dist/worker/WorkerPool.d.ts +7 -0
- package/dist/worker/WorkerPool.d.ts.map +1 -1
- package/dist/worker/WorkerPool.js +29 -5
- package/dist/worker/WorkerPool.js.map +1 -1
- package/dist/{stages/demux → workers}/MP4Demuxer.js +4 -13
- package/dist/workers/MP4Demuxer.js.map +1 -0
- package/dist/workers/WorkerChannel.js +486 -0
- package/dist/workers/WorkerChannel.js.map +1 -0
- package/dist/{assets/video-demux.worker-D019I7GQ.js → workers/mp4box.all.js} +4 -912
- package/dist/workers/mp4box.all.js.map +1 -0
- package/dist/{assets/audio-compose.worker-nGVvHD5Q.js → workers/stages/compose/audio-compose.worker.js} +7 -481
- package/dist/workers/stages/compose/audio-compose.worker.js.map +1 -0
- package/dist/{assets/video-compose.worker-DPzsC21d.js → workers/stages/compose/video-compose.worker.js} +7 -481
- package/dist/workers/stages/compose/video-compose.worker.js.map +1 -0
- package/dist/{assets/decode.worker-DpWHsc7R.js → workers/stages/decode/decode.worker.js} +7 -481
- package/dist/workers/stages/decode/decode.worker.js.map +1 -0
- package/dist/{stages → workers/stages}/demux/audio-demux.worker.js +184 -4
- package/dist/workers/stages/demux/audio-demux.worker.js.map +1 -0
- package/dist/{stages → workers/stages}/demux/video-demux.worker.js +2 -3
- package/dist/workers/stages/demux/video-demux.worker.js.map +1 -0
- package/dist/{stages → workers/stages}/encode/encode.worker.js +238 -4
- package/dist/workers/stages/encode/encode.worker.js.map +1 -0
- package/dist/{stages/mux/MP4Muxer.js → workers/stages/mux/mux.worker.js} +244 -5
- package/dist/workers/stages/mux/mux.worker.js.map +1 -0
- package/package.json +21 -21
- package/dist/assets/audio-compose.worker-nGVvHD5Q.js.map +0 -1
- package/dist/assets/audio-demux.worker-xwWBtbAe.js +0 -8299
- package/dist/assets/audio-demux.worker-xwWBtbAe.js.map +0 -1
- package/dist/assets/decode.worker-DpWHsc7R.js.map +0 -1
- package/dist/assets/encode.worker-nfOb3kw6.js +0 -1026
- package/dist/assets/encode.worker-nfOb3kw6.js.map +0 -1
- package/dist/assets/mux.worker-uEMQY066.js +0 -8019
- package/dist/assets/mux.worker-uEMQY066.js.map +0 -1
- package/dist/assets/video-compose.worker-DPzsC21d.js.map +0 -1
- package/dist/assets/video-demux.worker-D019I7GQ.js.map +0 -1
- package/dist/model/types.js +0 -5
- package/dist/model/types.js.map +0 -1
- package/dist/plugins/BackpressureMonitor.js +0 -62
- package/dist/plugins/BackpressureMonitor.js.map +0 -1
- package/dist/stages/compose/AudioDucker.js +0 -161
- package/dist/stages/compose/AudioDucker.js.map +0 -1
- package/dist/stages/compose/AudioMixer.js +0 -373
- package/dist/stages/compose/AudioMixer.js.map +0 -1
- package/dist/stages/compose/FilterProcessor.js +0 -226
- package/dist/stages/compose/FilterProcessor.js.map +0 -1
- package/dist/stages/compose/LayerRenderer.js +0 -215
- package/dist/stages/compose/LayerRenderer.js.map +0 -1
- package/dist/stages/compose/TransitionProcessor.js +0 -189
- package/dist/stages/compose/TransitionProcessor.js.map +0 -1
- package/dist/stages/compose/VideoComposer.js +0 -186
- package/dist/stages/compose/VideoComposer.js.map +0 -1
- package/dist/stages/compose/audio-compose.worker.d.ts +0 -79
- package/dist/stages/compose/audio-compose.worker.d.ts.map +0 -1
- package/dist/stages/compose/audio-compose.worker.js +0 -540
- package/dist/stages/compose/audio-compose.worker.js.map +0 -1
- package/dist/stages/compose/audio-compose.worker2.js +0 -5
- package/dist/stages/compose/audio-compose.worker2.js.map +0 -1
- package/dist/stages/compose/video-compose.worker.d.ts +0 -60
- package/dist/stages/compose/video-compose.worker.d.ts.map +0 -1
- package/dist/stages/compose/video-compose.worker.js +0 -379
- package/dist/stages/compose/video-compose.worker.js.map +0 -1
- package/dist/stages/compose/video-compose.worker2.js +0 -5
- package/dist/stages/compose/video-compose.worker2.js.map +0 -1
- package/dist/stages/decode/AudioChunkDecoder.js +0 -82
- package/dist/stages/decode/AudioChunkDecoder.js.map +0 -1
- package/dist/stages/decode/BaseDecoder.js +0 -130
- package/dist/stages/decode/BaseDecoder.js.map +0 -1
- package/dist/stages/decode/VideoChunkDecoder.js +0 -199
- package/dist/stages/decode/VideoChunkDecoder.js.map +0 -1
- package/dist/stages/decode/decode.worker.d.ts +0 -70
- package/dist/stages/decode/decode.worker.d.ts.map +0 -1
- package/dist/stages/decode/decode.worker.js +0 -423
- package/dist/stages/decode/decode.worker.js.map +0 -1
- package/dist/stages/decode/decode.worker2.js +0 -5
- package/dist/stages/decode/decode.worker2.js.map +0 -1
- package/dist/stages/demux/MP3FrameParser.js +0 -186
- package/dist/stages/demux/MP3FrameParser.js.map +0 -1
- package/dist/stages/demux/MP4Demuxer.js.map +0 -1
- package/dist/stages/demux/audio-demux.worker.d.ts +0 -51
- package/dist/stages/demux/audio-demux.worker.d.ts.map +0 -1
- package/dist/stages/demux/audio-demux.worker.js.map +0 -1
- package/dist/stages/demux/audio-demux.worker2.js +0 -5
- package/dist/stages/demux/audio-demux.worker2.js.map +0 -1
- package/dist/stages/demux/video-demux.worker.d.ts +0 -51
- package/dist/stages/demux/video-demux.worker.d.ts.map +0 -1
- package/dist/stages/demux/video-demux.worker.js.map +0 -1
- package/dist/stages/demux/video-demux.worker2.js +0 -5
- package/dist/stages/demux/video-demux.worker2.js.map +0 -1
- package/dist/stages/encode/AudioChunkEncoder.js +0 -37
- package/dist/stages/encode/AudioChunkEncoder.js.map +0 -1
- package/dist/stages/encode/BaseEncoder.js +0 -164
- package/dist/stages/encode/BaseEncoder.js.map +0 -1
- package/dist/stages/encode/VideoChunkEncoder.js +0 -50
- package/dist/stages/encode/VideoChunkEncoder.js.map +0 -1
- package/dist/stages/encode/encode.worker.d.ts +0 -3
- package/dist/stages/encode/encode.worker.d.ts.map +0 -1
- package/dist/stages/encode/encode.worker.js.map +0 -1
- package/dist/stages/encode/encode.worker2.js +0 -5
- package/dist/stages/encode/encode.worker2.js.map +0 -1
- package/dist/stages/mux/MP4Muxer.js.map +0 -1
- package/dist/stages/mux/mux.worker.d.ts +0 -65
- package/dist/stages/mux/mux.worker.d.ts.map +0 -1
- package/dist/stages/mux/mux.worker.js +0 -219
- package/dist/stages/mux/mux.worker.js.map +0 -1
- package/dist/stages/mux/mux.worker2.js +0 -5
- package/dist/stages/mux/mux.worker2.js.map +0 -1
- package/dist/stages/mux/utils.js +0 -34
- package/dist/stages/mux/utils.js.map +0 -1
- package/dist/worker/worker-registry.d.ts +0 -12
- package/dist/worker/worker-registry.d.ts.map +0 -1
- package/dist/worker/worker-registry.js +0 -20
- package/dist/worker/worker-registry.js.map +0 -1
|
@@ -1,189 +0,0 @@
|
|
|
1
|
-
class TransitionProcessor {
|
|
2
|
-
width;
|
|
3
|
-
height;
|
|
4
|
-
constructor(width, height) {
|
|
5
|
-
this.width = width;
|
|
6
|
-
this.height = height;
|
|
7
|
-
}
|
|
8
|
-
/**
|
|
9
|
-
* Apply transition effect to the canvas context
|
|
10
|
-
* Returns true if transition was applied, false if not needed
|
|
11
|
-
*/
|
|
12
|
-
applyTransition(ctx, transition) {
|
|
13
|
-
if (!transition || transition.progress <= 0) return false;
|
|
14
|
-
const progress = this.calculateEasedProgress(transition.progress, transition.easing);
|
|
15
|
-
switch (transition.type) {
|
|
16
|
-
case "fade":
|
|
17
|
-
return this.applyFade(ctx, progress);
|
|
18
|
-
case "slide":
|
|
19
|
-
return this.applySlide(ctx, progress, transition.direction);
|
|
20
|
-
case "wipe":
|
|
21
|
-
return this.applyWipe(ctx, progress, transition.direction);
|
|
22
|
-
case "zoom":
|
|
23
|
-
return this.applyZoom(ctx, progress, transition.direction);
|
|
24
|
-
case "rotate":
|
|
25
|
-
return this.applyRotate(ctx, progress);
|
|
26
|
-
case "dissolve":
|
|
27
|
-
return this.applyDissolve(ctx, progress);
|
|
28
|
-
default:
|
|
29
|
-
return false;
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
calculateEasedProgress(progress, easing) {
|
|
33
|
-
switch (easing) {
|
|
34
|
-
case "ease-in":
|
|
35
|
-
return progress * progress;
|
|
36
|
-
case "ease-out":
|
|
37
|
-
return 1 - (1 - progress) * (1 - progress);
|
|
38
|
-
case "ease-in-out":
|
|
39
|
-
return progress < 0.5 ? 2 * progress * progress : 1 - Math.pow(-2 * progress + 2, 2) / 2;
|
|
40
|
-
default:
|
|
41
|
-
return progress;
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
applyFade(ctx, progress) {
|
|
45
|
-
ctx.globalAlpha = progress;
|
|
46
|
-
return true;
|
|
47
|
-
}
|
|
48
|
-
applySlide(ctx, progress, direction) {
|
|
49
|
-
const distance = 1 - progress;
|
|
50
|
-
switch (direction) {
|
|
51
|
-
case "left":
|
|
52
|
-
ctx.translate(-this.width * distance, 0);
|
|
53
|
-
break;
|
|
54
|
-
case "right":
|
|
55
|
-
ctx.translate(this.width * distance, 0);
|
|
56
|
-
break;
|
|
57
|
-
case "up":
|
|
58
|
-
ctx.translate(0, -this.height * distance);
|
|
59
|
-
break;
|
|
60
|
-
case "down":
|
|
61
|
-
ctx.translate(0, this.height * distance);
|
|
62
|
-
break;
|
|
63
|
-
default:
|
|
64
|
-
ctx.translate(-this.width * distance, 0);
|
|
65
|
-
}
|
|
66
|
-
return true;
|
|
67
|
-
}
|
|
68
|
-
applyWipe(ctx, progress, direction) {
|
|
69
|
-
ctx.save();
|
|
70
|
-
ctx.beginPath();
|
|
71
|
-
switch (direction) {
|
|
72
|
-
case "left":
|
|
73
|
-
ctx.rect(0, 0, this.width * progress, this.height);
|
|
74
|
-
break;
|
|
75
|
-
case "right":
|
|
76
|
-
ctx.rect(this.width * (1 - progress), 0, this.width * progress, this.height);
|
|
77
|
-
break;
|
|
78
|
-
case "up":
|
|
79
|
-
ctx.rect(0, 0, this.width, this.height * progress);
|
|
80
|
-
break;
|
|
81
|
-
case "down":
|
|
82
|
-
ctx.rect(0, this.height * (1 - progress), this.width, this.height * progress);
|
|
83
|
-
break;
|
|
84
|
-
default:
|
|
85
|
-
ctx.rect(0, 0, this.width * progress, this.height);
|
|
86
|
-
}
|
|
87
|
-
ctx.clip();
|
|
88
|
-
return true;
|
|
89
|
-
}
|
|
90
|
-
applyZoom(ctx, progress, direction) {
|
|
91
|
-
const scale = direction === "out" ? 1 + (1 - progress) : progress;
|
|
92
|
-
const centerX = this.width / 2;
|
|
93
|
-
const centerY = this.height / 2;
|
|
94
|
-
ctx.translate(centerX, centerY);
|
|
95
|
-
ctx.scale(scale, scale);
|
|
96
|
-
ctx.translate(-centerX, -centerY);
|
|
97
|
-
if (direction === "out") {
|
|
98
|
-
ctx.globalAlpha = progress;
|
|
99
|
-
}
|
|
100
|
-
return true;
|
|
101
|
-
}
|
|
102
|
-
applyRotate(ctx, progress) {
|
|
103
|
-
const rotation = (1 - progress) * Math.PI * 2;
|
|
104
|
-
const centerX = this.width / 2;
|
|
105
|
-
const centerY = this.height / 2;
|
|
106
|
-
ctx.translate(centerX, centerY);
|
|
107
|
-
ctx.rotate(rotation);
|
|
108
|
-
ctx.translate(-centerX, -centerY);
|
|
109
|
-
return true;
|
|
110
|
-
}
|
|
111
|
-
applyDissolve(ctx, progress) {
|
|
112
|
-
ctx.globalAlpha = progress;
|
|
113
|
-
ctx.globalCompositeOperation = "multiply";
|
|
114
|
-
return true;
|
|
115
|
-
}
|
|
116
|
-
/**
|
|
117
|
-
* Create a transition mask for advanced effects
|
|
118
|
-
*/
|
|
119
|
-
createTransitionMask(transition, canvas) {
|
|
120
|
-
const ctx = canvas.getContext("2d");
|
|
121
|
-
if (!ctx) return null;
|
|
122
|
-
const imageData = ctx.createImageData(this.width, this.height);
|
|
123
|
-
const data = imageData.data;
|
|
124
|
-
const progress = this.calculateEasedProgress(transition.progress, transition.easing);
|
|
125
|
-
for (let y = 0; y < this.height; y++) {
|
|
126
|
-
for (let x = 0; x < this.width; x++) {
|
|
127
|
-
const index = (y * this.width + x) * 4;
|
|
128
|
-
let alpha = 255;
|
|
129
|
-
switch (transition.type) {
|
|
130
|
-
case "wipe":
|
|
131
|
-
alpha = this.calculateWipeAlpha(x, y, progress, transition.direction);
|
|
132
|
-
break;
|
|
133
|
-
case "dissolve":
|
|
134
|
-
alpha = Math.random() < progress ? 255 : 0;
|
|
135
|
-
break;
|
|
136
|
-
default:
|
|
137
|
-
alpha = Math.floor(255 * progress);
|
|
138
|
-
}
|
|
139
|
-
data[index] = 255;
|
|
140
|
-
data[index + 1] = 255;
|
|
141
|
-
data[index + 2] = 255;
|
|
142
|
-
data[index + 3] = alpha;
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
return imageData;
|
|
146
|
-
}
|
|
147
|
-
calculateWipeAlpha(x, y, progress, direction) {
|
|
148
|
-
let position = 0;
|
|
149
|
-
switch (direction) {
|
|
150
|
-
case "left":
|
|
151
|
-
position = x / this.width;
|
|
152
|
-
break;
|
|
153
|
-
case "right":
|
|
154
|
-
position = 1 - x / this.width;
|
|
155
|
-
break;
|
|
156
|
-
case "up":
|
|
157
|
-
position = y / this.height;
|
|
158
|
-
break;
|
|
159
|
-
case "down":
|
|
160
|
-
position = 1 - y / this.height;
|
|
161
|
-
break;
|
|
162
|
-
case "in": {
|
|
163
|
-
const cx = x - this.width / 2;
|
|
164
|
-
const cy = y - this.height / 2;
|
|
165
|
-
const maxDist = Math.sqrt(this.width * this.width + this.height * this.height) / 2;
|
|
166
|
-
position = Math.sqrt(cx * cx + cy * cy) / maxDist;
|
|
167
|
-
break;
|
|
168
|
-
}
|
|
169
|
-
case "out": {
|
|
170
|
-
const cx2 = x - this.width / 2;
|
|
171
|
-
const cy2 = y - this.height / 2;
|
|
172
|
-
const maxDist2 = Math.sqrt(this.width * this.width + this.height * this.height) / 2;
|
|
173
|
-
position = 1 - Math.sqrt(cx2 * cx2 + cy2 * cy2) / maxDist2;
|
|
174
|
-
break;
|
|
175
|
-
}
|
|
176
|
-
default:
|
|
177
|
-
position = x / this.width;
|
|
178
|
-
}
|
|
179
|
-
return position < progress ? 255 : 0;
|
|
180
|
-
}
|
|
181
|
-
updateDimensions(width, height) {
|
|
182
|
-
this.width = width;
|
|
183
|
-
this.height = height;
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
export {
|
|
187
|
-
TransitionProcessor
|
|
188
|
-
};
|
|
189
|
-
//# sourceMappingURL=TransitionProcessor.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"TransitionProcessor.js","sources":["../../../src/stages/compose/TransitionProcessor.ts"],"sourcesContent":["import type { TransitionEffect } from './types';\n\n/**\n * TransitionProcessor - Handles transition effects between frames/scenes\n * Single responsibility: Apply transition transformations to canvas context\n */\nexport class TransitionProcessor {\n private width: number;\n private height: number;\n\n constructor(width: number, height: number) {\n this.width = width;\n this.height = height;\n }\n\n /**\n * Apply transition effect to the canvas context\n * Returns true if transition was applied, false if not needed\n */\n applyTransition(ctx: OffscreenCanvasRenderingContext2D, transition: TransitionEffect): boolean {\n if (!transition || transition.progress <= 0) return false;\n\n const progress = this.calculateEasedProgress(transition.progress, transition.easing);\n\n switch (transition.type) {\n case 'fade':\n return this.applyFade(ctx, progress);\n case 'slide':\n return this.applySlide(ctx, progress, transition.direction);\n case 'wipe':\n return this.applyWipe(ctx, progress, transition.direction);\n case 'zoom':\n return this.applyZoom(ctx, progress, transition.direction);\n case 'rotate':\n return this.applyRotate(ctx, progress);\n case 'dissolve':\n return this.applyDissolve(ctx, progress);\n default:\n return false;\n }\n }\n\n private calculateEasedProgress(\n progress: number,\n easing?: 'linear' | 'ease-in' | 'ease-out' | 'ease-in-out'\n ): number {\n switch (easing) {\n case 'ease-in':\n return progress * progress;\n case 'ease-out':\n return 1 - (1 - progress) * (1 - progress);\n case 'ease-in-out':\n return progress < 0.5 ? 2 * progress * progress : 1 - Math.pow(-2 * progress + 2, 2) / 2;\n default:\n return progress;\n }\n }\n\n private applyFade(ctx: OffscreenCanvasRenderingContext2D, progress: number): boolean {\n ctx.globalAlpha = progress;\n return true;\n }\n\n private applySlide(\n ctx: OffscreenCanvasRenderingContext2D,\n progress: number,\n direction?: 'left' | 'right' | 'up' | 'down' | 'in' | 'out'\n ): boolean {\n const distance = 1 - progress;\n\n switch (direction) {\n case 'left':\n ctx.translate(-this.width * distance, 0);\n break;\n case 'right':\n ctx.translate(this.width * distance, 0);\n break;\n case 'up':\n ctx.translate(0, -this.height * distance);\n break;\n case 'down':\n ctx.translate(0, this.height * distance);\n break;\n default:\n ctx.translate(-this.width * distance, 0);\n }\n\n return true;\n }\n\n private applyWipe(\n ctx: OffscreenCanvasRenderingContext2D,\n progress: number,\n direction?: 'left' | 'right' | 'up' | 'down' | 'in' | 'out'\n ): boolean {\n ctx.save();\n ctx.beginPath();\n\n switch (direction) {\n case 'left':\n ctx.rect(0, 0, this.width * progress, this.height);\n break;\n case 'right':\n ctx.rect(this.width * (1 - progress), 0, this.width * progress, this.height);\n break;\n case 'up':\n ctx.rect(0, 0, this.width, this.height * progress);\n break;\n case 'down':\n ctx.rect(0, this.height * (1 - progress), this.width, this.height * progress);\n break;\n default:\n ctx.rect(0, 0, this.width * progress, this.height);\n }\n\n ctx.clip();\n return true;\n }\n\n private applyZoom(\n ctx: OffscreenCanvasRenderingContext2D,\n progress: number,\n direction?: 'left' | 'right' | 'up' | 'down' | 'in' | 'out'\n ): boolean {\n const scale = direction === 'out' ? 1 + (1 - progress) : progress;\n const centerX = this.width / 2;\n const centerY = this.height / 2;\n\n ctx.translate(centerX, centerY);\n ctx.scale(scale, scale);\n ctx.translate(-centerX, -centerY);\n\n if (direction === 'out') {\n ctx.globalAlpha = progress;\n }\n\n return true;\n }\n\n private applyRotate(ctx: OffscreenCanvasRenderingContext2D, progress: number): boolean {\n const rotation = (1 - progress) * Math.PI * 2;\n const centerX = this.width / 2;\n const centerY = this.height / 2;\n\n ctx.translate(centerX, centerY);\n ctx.rotate(rotation);\n ctx.translate(-centerX, -centerY);\n\n return true;\n }\n\n private applyDissolve(ctx: OffscreenCanvasRenderingContext2D, progress: number): boolean {\n // Simple dissolve using alpha\n ctx.globalAlpha = progress;\n ctx.globalCompositeOperation = 'multiply';\n return true;\n }\n\n /**\n * Create a transition mask for advanced effects\n */\n createTransitionMask(transition: TransitionEffect, canvas: OffscreenCanvas): ImageData | null {\n const ctx = canvas.getContext('2d');\n if (!ctx) return null;\n\n const imageData = ctx.createImageData(this.width, this.height);\n const data = imageData.data;\n const progress = this.calculateEasedProgress(transition.progress, transition.easing);\n\n // Generate gradient mask based on transition type\n for (let y = 0; y < this.height; y++) {\n for (let x = 0; x < this.width; x++) {\n const index = (y * this.width + x) * 4;\n let alpha = 255;\n\n switch (transition.type) {\n case 'wipe':\n alpha = this.calculateWipeAlpha(x, y, progress, transition.direction);\n break;\n case 'dissolve':\n // Random noise dissolve\n alpha = Math.random() < progress ? 255 : 0;\n break;\n default:\n alpha = Math.floor(255 * progress);\n }\n\n data[index] = 255; // R\n data[index + 1] = 255; // G\n data[index + 2] = 255; // B\n data[index + 3] = alpha; // A\n }\n }\n\n return imageData;\n }\n\n private calculateWipeAlpha(\n x: number,\n y: number,\n progress: number,\n direction?: 'left' | 'right' | 'up' | 'down' | 'in' | 'out'\n ): number {\n let position = 0;\n\n switch (direction) {\n case 'left':\n position = x / this.width;\n break;\n case 'right':\n position = 1 - x / this.width;\n break;\n case 'up':\n position = y / this.height;\n break;\n case 'down':\n position = 1 - y / this.height;\n break;\n case 'in': {\n // Radial wipe from center\n const cx = x - this.width / 2;\n const cy = y - this.height / 2;\n const maxDist = Math.sqrt(this.width * this.width + this.height * this.height) / 2;\n position = Math.sqrt(cx * cx + cy * cy) / maxDist;\n break;\n }\n case 'out': {\n // Radial wipe to center\n const cx2 = x - this.width / 2;\n const cy2 = y - this.height / 2;\n const maxDist2 = Math.sqrt(this.width * this.width + this.height * this.height) / 2;\n position = 1 - Math.sqrt(cx2 * cx2 + cy2 * cy2) / maxDist2;\n break;\n }\n default:\n position = x / this.width;\n }\n\n return position < progress ? 255 : 0;\n }\n\n updateDimensions(width: number, height: number): void {\n this.width = width;\n this.height = height;\n }\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -1,186 +0,0 @@
|
|
|
1
|
-
import { frameDurationFromFps } from "../../utils/time-utils.js";
|
|
2
|
-
import { LayerRenderer } from "./LayerRenderer.js";
|
|
3
|
-
import { TransitionProcessor } from "./TransitionProcessor.js";
|
|
4
|
-
import { FilterProcessor } from "./FilterProcessor.js";
|
|
5
|
-
class VideoComposer {
|
|
6
|
-
config;
|
|
7
|
-
canvas;
|
|
8
|
-
ctx;
|
|
9
|
-
layerRenderer;
|
|
10
|
-
transitionProcessor;
|
|
11
|
-
filterProcessor;
|
|
12
|
-
timelineContext;
|
|
13
|
-
constructor(config) {
|
|
14
|
-
this.config = this.applyDefaults(config);
|
|
15
|
-
this.canvas = new OffscreenCanvas(this.config.width, this.config.height);
|
|
16
|
-
const ctx = this.canvas.getContext("2d", {
|
|
17
|
-
alpha: true,
|
|
18
|
-
desynchronized: true,
|
|
19
|
-
willReadFrequently: false,
|
|
20
|
-
colorSpace: "srgb"
|
|
21
|
-
});
|
|
22
|
-
if (!ctx) {
|
|
23
|
-
throw new Error("Failed to create 2D rendering context");
|
|
24
|
-
}
|
|
25
|
-
this.ctx = ctx;
|
|
26
|
-
this.ctx.imageSmoothingEnabled = this.config.enableSmoothing;
|
|
27
|
-
this.ctx.imageSmoothingQuality = "high";
|
|
28
|
-
this.layerRenderer = new LayerRenderer(ctx, this.config.width, this.config.height);
|
|
29
|
-
this.transitionProcessor = new TransitionProcessor(this.config.width, this.config.height);
|
|
30
|
-
this.filterProcessor = new FilterProcessor();
|
|
31
|
-
this.timelineContext = this.config.timeline;
|
|
32
|
-
}
|
|
33
|
-
applyDefaults(config) {
|
|
34
|
-
return {
|
|
35
|
-
width: config.width || 720,
|
|
36
|
-
height: config.height || 1280,
|
|
37
|
-
fps: config.fps || 30,
|
|
38
|
-
backgroundColor: config.backgroundColor ?? "#000",
|
|
39
|
-
renderer: config.renderer ?? "canvas2d",
|
|
40
|
-
enableSmoothing: config.enableSmoothing ?? true,
|
|
41
|
-
enableHardwareAcceleration: config.enableHardwareAcceleration ?? true,
|
|
42
|
-
revision: config.revision ?? 0,
|
|
43
|
-
inputHighWaterMark: config.inputHighWaterMark ?? 3,
|
|
44
|
-
outputHighWaterMark: config.outputHighWaterMark ?? 1,
|
|
45
|
-
maxLayers: config.maxLayers ?? 100,
|
|
46
|
-
timeline: config.timeline ?? {
|
|
47
|
-
clipId: "default",
|
|
48
|
-
trackId: "main",
|
|
49
|
-
clipStartUs: 0,
|
|
50
|
-
clipDurationUs: Infinity,
|
|
51
|
-
compositionFps: 30
|
|
52
|
-
}
|
|
53
|
-
};
|
|
54
|
-
}
|
|
55
|
-
createStreams(_instruction) {
|
|
56
|
-
if (_instruction?.baseConfig.timeline) {
|
|
57
|
-
this.timelineContext = _instruction.baseConfig.timeline;
|
|
58
|
-
}
|
|
59
|
-
const stream = new TransformStream(
|
|
60
|
-
{
|
|
61
|
-
transform: async (request, controller) => {
|
|
62
|
-
const result = await this.composeFrame(request);
|
|
63
|
-
controller.enqueue({
|
|
64
|
-
frame: result.frame,
|
|
65
|
-
metadata: result.metadata
|
|
66
|
-
});
|
|
67
|
-
},
|
|
68
|
-
flush: async () => {
|
|
69
|
-
this.filterProcessor.clearCache();
|
|
70
|
-
}
|
|
71
|
-
},
|
|
72
|
-
{
|
|
73
|
-
highWaterMark: this.config.inputHighWaterMark
|
|
74
|
-
},
|
|
75
|
-
{
|
|
76
|
-
highWaterMark: this.config.outputHighWaterMark
|
|
77
|
-
}
|
|
78
|
-
);
|
|
79
|
-
return {
|
|
80
|
-
composeStream: stream.writable,
|
|
81
|
-
cacheStream: stream.readable
|
|
82
|
-
};
|
|
83
|
-
}
|
|
84
|
-
async composeFrame(request) {
|
|
85
|
-
if (request.layers.length > this.config.maxLayers) {
|
|
86
|
-
throw new Error(`Too many layers: ${request.layers.length} > ${this.config.maxLayers}`);
|
|
87
|
-
}
|
|
88
|
-
this.clearCanvas();
|
|
89
|
-
if (request.transition) {
|
|
90
|
-
this.ctx.save();
|
|
91
|
-
this.transitionProcessor.applyTransition(this.ctx, request.transition);
|
|
92
|
-
}
|
|
93
|
-
for (const layer of request.layers) {
|
|
94
|
-
if (!layer.visible || layer.opacity <= 0) {
|
|
95
|
-
if (layer.type === "video") {
|
|
96
|
-
const vf = layer.videoFrame;
|
|
97
|
-
vf?.close?.();
|
|
98
|
-
}
|
|
99
|
-
continue;
|
|
100
|
-
}
|
|
101
|
-
try {
|
|
102
|
-
if (layer.filters && layer.filters.length > 0) {
|
|
103
|
-
this.ctx.save();
|
|
104
|
-
this.filterProcessor.applyFilters(this.ctx, layer.filters);
|
|
105
|
-
}
|
|
106
|
-
await this.layerRenderer.renderLayer(layer);
|
|
107
|
-
if (layer.filters && layer.filters.length > 0) {
|
|
108
|
-
this.ctx.restore();
|
|
109
|
-
}
|
|
110
|
-
} finally {
|
|
111
|
-
if (layer.type === "video") {
|
|
112
|
-
const vf = layer.videoFrame;
|
|
113
|
-
vf?.close?.();
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
if (request.transition) {
|
|
118
|
-
this.ctx.restore();
|
|
119
|
-
}
|
|
120
|
-
const frame = await this.createOutputFrame(request.timeUs);
|
|
121
|
-
const gopSerial = request.gopSerial;
|
|
122
|
-
const isKeyframe = request.isKeyframe;
|
|
123
|
-
return {
|
|
124
|
-
frame,
|
|
125
|
-
timeUs: request.timeUs,
|
|
126
|
-
metadata: {
|
|
127
|
-
layerCount: request.layers.length,
|
|
128
|
-
renderTime: 0,
|
|
129
|
-
gpuAccelerated: this.config.enableHardwareAcceleration && this.config.renderer !== "canvas2d",
|
|
130
|
-
...typeof gopSerial === "number" && { gopSerial },
|
|
131
|
-
...typeof isKeyframe === "boolean" && { isKeyframe }
|
|
132
|
-
}
|
|
133
|
-
};
|
|
134
|
-
}
|
|
135
|
-
async composeTransition(fromRequest, toRequest, transition) {
|
|
136
|
-
await this.composeFrame(fromRequest);
|
|
137
|
-
const toFrameRequest = {
|
|
138
|
-
...toRequest,
|
|
139
|
-
transition
|
|
140
|
-
};
|
|
141
|
-
return this.composeFrame(toFrameRequest);
|
|
142
|
-
}
|
|
143
|
-
clearCanvas() {
|
|
144
|
-
if (this.config.backgroundColor) {
|
|
145
|
-
this.ctx.fillStyle = this.config.backgroundColor;
|
|
146
|
-
this.ctx.fillRect(0, 0, this.config.width, this.config.height);
|
|
147
|
-
} else {
|
|
148
|
-
this.ctx.clearRect(0, 0, this.config.width, this.config.height);
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
// private sortLayers(layers: Layer[]): Layer[] {
|
|
152
|
-
// return [...layers].sort((a, b) => a.zIndex - b.zIndex);
|
|
153
|
-
// }
|
|
154
|
-
async createOutputFrame(timeUs) {
|
|
155
|
-
const duration = frameDurationFromFps(this.timelineContext.compositionFps);
|
|
156
|
-
const frame = new VideoFrame(this.canvas, {
|
|
157
|
-
timestamp: timeUs,
|
|
158
|
-
duration,
|
|
159
|
-
alpha: "discard",
|
|
160
|
-
visibleRect: { x: 0, y: 0, width: this.canvas.width, height: this.canvas.height }
|
|
161
|
-
});
|
|
162
|
-
return frame;
|
|
163
|
-
}
|
|
164
|
-
updateConfig(config) {
|
|
165
|
-
Object.assign(this.config, this.applyDefaults({ ...this.config, ...config }));
|
|
166
|
-
if (config.width || config.height) {
|
|
167
|
-
this.canvas.width = this.config.width;
|
|
168
|
-
this.canvas.height = this.config.height;
|
|
169
|
-
this.layerRenderer.updateDimensions(this.config.width, this.config.height);
|
|
170
|
-
this.transitionProcessor.updateDimensions(this.config.width, this.config.height);
|
|
171
|
-
}
|
|
172
|
-
if (config.enableSmoothing !== void 0) {
|
|
173
|
-
this.ctx.imageSmoothingEnabled = this.config.enableSmoothing;
|
|
174
|
-
}
|
|
175
|
-
if (config.timeline) {
|
|
176
|
-
this.timelineContext = config.timeline;
|
|
177
|
-
}
|
|
178
|
-
}
|
|
179
|
-
dispose() {
|
|
180
|
-
this.filterProcessor.clearCache();
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
export {
|
|
184
|
-
VideoComposer
|
|
185
|
-
};
|
|
186
|
-
//# sourceMappingURL=VideoComposer.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"VideoComposer.js","sources":["../../../src/stages/compose/VideoComposer.ts"],"sourcesContent":["import type {\n VideoComposeConfig,\n ComposeRequest,\n ComposeResult,\n TransitionEffect,\n VideoLayer,\n ComposeTimelineContext,\n} from './types';\nimport { frameDurationFromFps } from '../../utils/time-utils';\nimport { LayerRenderer } from './LayerRenderer';\nimport { TransitionProcessor } from './TransitionProcessor';\nimport { FilterProcessor } from './FilterProcessor';\nimport { ClipInstructionSet } from './instructions';\n\ninterface ComposeStreams {\n composeStream: WritableStream<ComposeRequest>;\n cacheStream: ReadableStream<VideoFrame>;\n}\n\n/**\n * VideoComposer - Main visual composition orchestrator\n */\nexport class VideoComposer {\n readonly config: Required<VideoComposeConfig>;\n readonly canvas: OffscreenCanvas;\n\n private ctx: OffscreenCanvasRenderingContext2D;\n private layerRenderer: LayerRenderer;\n private transitionProcessor: TransitionProcessor;\n private filterProcessor: FilterProcessor;\n private timelineContext: ComposeTimelineContext;\n\n constructor(config: VideoComposeConfig) {\n this.config = this.applyDefaults(config);\n this.canvas = new OffscreenCanvas(this.config.width, this.config.height);\n\n const ctx = this.canvas.getContext('2d', {\n alpha: true,\n desynchronized: true,\n willReadFrequently: false,\n colorSpace: 'srgb',\n });\n\n if (!ctx) {\n throw new Error('Failed to create 2D rendering context');\n }\n\n this.ctx = ctx;\n this.ctx.imageSmoothingEnabled = this.config.enableSmoothing;\n this.ctx.imageSmoothingQuality = 'high';\n\n this.layerRenderer = new LayerRenderer(ctx, this.config.width, this.config.height);\n this.transitionProcessor = new TransitionProcessor(this.config.width, this.config.height);\n this.filterProcessor = new FilterProcessor();\n this.timelineContext = this.config.timeline;\n }\n\n private applyDefaults(config: VideoComposeConfig): Required<VideoComposeConfig> {\n return {\n width: config.width || 720,\n height: config.height || 1280,\n fps: config.fps || 30,\n backgroundColor: config.backgroundColor ?? '#000',\n renderer: config.renderer ?? 'canvas2d',\n enableSmoothing: config.enableSmoothing ?? true,\n enableHardwareAcceleration: config.enableHardwareAcceleration ?? true,\n revision: config.revision ?? 0,\n inputHighWaterMark: config.inputHighWaterMark ?? 3,\n outputHighWaterMark: config.outputHighWaterMark ?? 1,\n maxLayers: config.maxLayers ?? 100,\n timeline: config.timeline ?? {\n clipId: 'default',\n trackId: 'main',\n clipStartUs: 0,\n clipDurationUs: Infinity,\n compositionFps: 30,\n },\n };\n }\n\n createStreams(_instruction?: ClipInstructionSet): ComposeStreams {\n if (_instruction?.baseConfig.timeline) {\n this.timelineContext = _instruction.baseConfig.timeline;\n }\n\n // Always create new streams for each clip\n // ReadableStreams can only be consumed once\n const stream = new TransformStream<ComposeRequest, any>(\n {\n transform: async (request, controller) => {\n // console.log('[VideoComposer] transform', request, controller.desiredSize);\n const result = await this.composeFrame(request);\n controller.enqueue({\n frame: result.frame,\n metadata: result.metadata,\n });\n },\n\n flush: async () => {\n this.filterProcessor.clearCache();\n },\n },\n {\n highWaterMark: this.config.inputHighWaterMark,\n },\n {\n highWaterMark: this.config.outputHighWaterMark,\n }\n );\n // const [encodeStream, cacheStream] = stream.readable.tee();\n return {\n composeStream: stream.writable,\n cacheStream: stream.readable,\n };\n }\n\n async composeFrame(request: ComposeRequest): Promise<ComposeResult> {\n if (request.layers.length > this.config.maxLayers) {\n throw new Error(`Too many layers: ${request.layers.length} > ${this.config.maxLayers}`);\n }\n\n this.clearCanvas();\n // const sortedLayers = this.sortLayers(request.layers);\n\n if (request.transition) {\n this.ctx.save();\n this.transitionProcessor.applyTransition(this.ctx, request.transition);\n }\n\n for (const layer of request.layers) {\n if (!layer.visible || layer.opacity <= 0) {\n // Close video frame for invisible layers\n if ((layer as any).type === 'video') {\n const vf = (layer as VideoLayer).videoFrame;\n vf?.close?.();\n }\n continue;\n }\n\n try {\n if (layer.filters && layer.filters.length > 0) {\n this.ctx.save();\n this.filterProcessor.applyFilters(this.ctx, layer.filters);\n }\n\n await this.layerRenderer.renderLayer(layer);\n\n if (layer.filters && layer.filters.length > 0) {\n this.ctx.restore();\n }\n } finally {\n // Always close video frame after rendering (or on error)\n if ((layer as any).type === 'video') {\n const vf = (layer as VideoLayer).videoFrame;\n vf?.close?.();\n }\n }\n }\n\n if (request.transition) {\n this.ctx.restore();\n }\n\n const frame = await this.createOutputFrame(request.timeUs);\n\n const gopSerial = (request as any).gopSerial;\n const isKeyframe = (request as any).isKeyframe;\n\n return {\n frame,\n timeUs: request.timeUs,\n metadata: {\n layerCount: request.layers.length,\n renderTime: 0,\n gpuAccelerated:\n this.config.enableHardwareAcceleration && this.config.renderer !== 'canvas2d',\n ...(typeof gopSerial === 'number' && { gopSerial }),\n ...(typeof isKeyframe === 'boolean' && { isKeyframe }),\n },\n };\n }\n\n async composeTransition(\n fromRequest: ComposeRequest,\n toRequest: ComposeRequest,\n transition: TransitionEffect\n ): Promise<ComposeResult> {\n await this.composeFrame(fromRequest);\n\n const toFrameRequest = {\n ...toRequest,\n transition,\n };\n\n return this.composeFrame(toFrameRequest);\n }\n\n private clearCanvas(): void {\n if (this.config.backgroundColor) {\n this.ctx.fillStyle = this.config.backgroundColor;\n this.ctx.fillRect(0, 0, this.config.width, this.config.height);\n } else {\n this.ctx.clearRect(0, 0, this.config.width, this.config.height);\n }\n }\n\n // private sortLayers(layers: Layer[]): Layer[] {\n // return [...layers].sort((a, b) => a.zIndex - b.zIndex);\n // }\n\n private async createOutputFrame(timeUs: number): Promise<VideoFrame> {\n const duration = frameDurationFromFps(this.timelineContext.compositionFps);\n const frame = new VideoFrame(this.canvas, {\n timestamp: timeUs,\n duration,\n alpha: 'discard',\n visibleRect: { x: 0, y: 0, width: this.canvas.width, height: this.canvas.height },\n });\n return frame;\n }\n\n updateConfig(config: Partial<VideoComposeConfig>): void {\n Object.assign(this.config, this.applyDefaults({ ...this.config, ...config }));\n\n if (config.width || config.height) {\n this.canvas.width = this.config.width;\n this.canvas.height = this.config.height;\n this.layerRenderer.updateDimensions(this.config.width, this.config.height);\n this.transitionProcessor.updateDimensions(this.config.width, this.config.height);\n }\n\n if (config.enableSmoothing !== undefined) {\n this.ctx.imageSmoothingEnabled = this.config.enableSmoothing;\n }\n\n if (config.timeline) {\n this.timelineContext = config.timeline;\n }\n }\n\n dispose(): void {\n this.filterProcessor.clearCache();\n }\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -1,79 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* AudioComposeWorker - Audio mixing and ducking stage
|
|
3
|
-
* Mixes multiple audio tracks with volume, ducking, and effects
|
|
4
|
-
*
|
|
5
|
-
* Pipeline: DecodeWorker → AudioComposeWorker → EncodeWorker
|
|
6
|
-
*
|
|
7
|
-
* Features:
|
|
8
|
-
* - Multi-track PCM mixing
|
|
9
|
-
* - BGM ducking for voice tracks
|
|
10
|
-
* - Stream-based processing with backpressure
|
|
11
|
-
* - Real-time volume and effect adjustments
|
|
12
|
-
*/
|
|
13
|
-
export declare class AudioComposeWorker {
|
|
14
|
-
private channel;
|
|
15
|
-
private mixer;
|
|
16
|
-
private ducker;
|
|
17
|
-
private mixStream;
|
|
18
|
-
private decoderPort;
|
|
19
|
-
private encoderPort;
|
|
20
|
-
private trackBuffers;
|
|
21
|
-
private trackQueueWaiters;
|
|
22
|
-
private encoderStreamAttached;
|
|
23
|
-
private readonly mixWindowUs;
|
|
24
|
-
private readonly maxQueuedSegments;
|
|
25
|
-
private mixing;
|
|
26
|
-
constructor();
|
|
27
|
-
private setupHandlers;
|
|
28
|
-
/** Unified connect handler mapping for stream pipeline */
|
|
29
|
-
private handleConnect;
|
|
30
|
-
private handleReceiveStream;
|
|
31
|
-
/**
|
|
32
|
-
* Configure audio composer
|
|
33
|
-
* @param payload.config - Audio composition configuration
|
|
34
|
-
* @param payload.initial - If true, initialize worker state; otherwise just update config
|
|
35
|
-
*/
|
|
36
|
-
private handleConfigure;
|
|
37
|
-
/**
|
|
38
|
-
* Connect to decoder worker to receive audio streams
|
|
39
|
-
*/
|
|
40
|
-
/**
|
|
41
|
-
* Add an audio track
|
|
42
|
-
*/
|
|
43
|
-
private handleAddTrack;
|
|
44
|
-
/**
|
|
45
|
-
* Remove an audio track
|
|
46
|
-
*/
|
|
47
|
-
private handleRemoveTrack;
|
|
48
|
-
/**
|
|
49
|
-
* Update track configuration
|
|
50
|
-
*/
|
|
51
|
-
private handleUpdateTrack;
|
|
52
|
-
/**
|
|
53
|
-
* Configure audio ducking
|
|
54
|
-
*/
|
|
55
|
-
private handleConfigureDucking;
|
|
56
|
-
/**
|
|
57
|
-
* Get mixer statistics
|
|
58
|
-
*/
|
|
59
|
-
private handleGetStats;
|
|
60
|
-
/**
|
|
61
|
-
* Dispose worker and cleanup resources
|
|
62
|
-
*/
|
|
63
|
-
private handleDispose;
|
|
64
|
-
private attachEncodeStream;
|
|
65
|
-
private bufferTrackStream;
|
|
66
|
-
private scheduleMix;
|
|
67
|
-
private computeNextWindow;
|
|
68
|
-
private buildMixRequest;
|
|
69
|
-
private consumeSegment;
|
|
70
|
-
private hasBufferedAudio;
|
|
71
|
-
private cloneTrackConfig;
|
|
72
|
-
private applyTrackConfigPatch;
|
|
73
|
-
private waitForQueueSpace;
|
|
74
|
-
private resolveQueueWaiter;
|
|
75
|
-
private disposeTrackBuffer;
|
|
76
|
-
}
|
|
77
|
-
declare const _default: null;
|
|
78
|
-
export default _default;
|
|
79
|
-
//# sourceMappingURL=audio-compose.worker.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"audio-compose.worker.d.ts","sourceRoot":"","sources":["../../../src/stages/compose/audio-compose.worker.ts"],"names":[],"mappings":"AA4BA;;;;;;;;;;;GAWG;AACH,qBAAa,kBAAkB;IAC7B,OAAO,CAAC,OAAO,CAAgB;IAC/B,OAAO,CAAC,KAAK,CAA2B;IACxC,OAAO,CAAC,MAAM,CAA4B;IAC1C,OAAO,CAAC,SAAS,CAAuD;IAGxE,OAAO,CAAC,WAAW,CAA4B;IAC/C,OAAO,CAAC,WAAW,CAA4B;IAE/C,OAAO,CAAC,YAAY,CAAkC;IACtD,OAAO,CAAC,iBAAiB,CAAwC;IACjE,OAAO,CAAC,qBAAqB,CAAS;IACtC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAkB;IAC9C,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAK;IACvC,OAAO,CAAC,MAAM,CAAS;;IAYvB,OAAO,CAAC,aAAa;IAarB,0DAA0D;YAC5C,aAAa;YAsCb,mBAAmB;IAmDjC;;;;OAIG;YACW,eAAe;IA+C7B;;OAEG;IACH;;OAEG;IACH,OAAO,CAAC,cAAc;IAsCtB;;OAEG;IACH,OAAO,CAAC,iBAAiB;IAkBzB;;OAEG;IACH,OAAO,CAAC,iBAAiB;IA6BzB;;OAEG;IACH,OAAO,CAAC,sBAAsB;IAiB9B;;OAEG;YACW,cAAc;IAoB5B;;OAEG;YACW,aAAa;YAiBb,kBAAkB;YAkElB,iBAAiB;YA6CjB,WAAW;IAmCzB,OAAO,CAAC,iBAAiB;IA2BzB,OAAO,CAAC,eAAe;IAsCvB,OAAO,CAAC,cAAc;IAmCtB,OAAO,CAAC,gBAAgB;IASxB,OAAO,CAAC,gBAAgB;IAiBxB,OAAO,CAAC,qBAAqB;YAqCf,iBAAiB;IAgB/B,OAAO,CAAC,kBAAkB;IAa1B,OAAO,CAAC,kBAAkB;CAsB3B;;AAUD,wBAAoB"}
|