@video-editor/renderer 0.0.1-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,26 @@
1
+ # @video-editor/renderer
2
+
3
+ Reactive renderer that consumes an `IVideoProtocol` and drives a Pixi scene like a lightweight video player.
4
+
5
+ ```ts
6
+ import { createRenderer } from '@video-editor/renderer'
7
+ import { reactive } from '@vue/reactivity'
8
+
9
+ const protocol = reactive({
10
+ version: '1.0.0',
11
+ width: 1920,
12
+ height: 1080,
13
+ fps: 30,
14
+ tracks: [],
15
+ })
16
+
17
+ const renderer = await createRenderer({ protocol, autoPlay: true })
18
+
19
+ // drive the player
20
+ renderer.play()
21
+ renderer.seek(1000)
22
+ renderer.tick(16.7) // or rely on requestAnimationFrame loop
23
+
24
+ // mutate protocol reactively; the renderer will verify and update visuals
25
+ protocol.tracks.push(/* ... */)
26
+ ```
@@ -0,0 +1,39 @@
1
+ import { Application } from 'pixi.js';
2
+ import { ApplicationOptions } from 'pixi.js';
3
+ import { ComputedRef } from '@vue/reactivity';
4
+ import { Container } from 'pixi.js';
5
+ import { IVideoProtocol } from '@video-editor/shared';
6
+ import { Ref } from '@vue/reactivity';
7
+
8
+ /**
9
+ * Create a renderer that reacts to protocol updates and drives playback state.
10
+ * - Pass a reactive `protocol` (Ref/readonly/normal object)
11
+ * - Call `play/pause/seek/tick` to drive the timeline
12
+ * - Rendering updates when `protocol` or `currentTime` changes
13
+ */
14
+ export declare function createRenderer(opts: RendererOptions): Promise<Renderer>;
15
+
16
+ declare type MaybeRef<T> = T | Ref<T>;
17
+
18
+ export declare interface Renderer {
19
+ app: Application;
20
+ layer: Container;
21
+ currentTime: Ref<number>;
22
+ duration: ComputedRef<number>;
23
+ isPlaying: Ref<boolean>;
24
+ play: () => void;
25
+ pause: () => void;
26
+ tick: (deltaMs?: number) => void;
27
+ seek: (time: number) => void;
28
+ destroy: () => void;
29
+ }
30
+
31
+ export declare interface RendererOptions {
32
+ protocol: MaybeRef<IVideoProtocol>;
33
+ app?: Application;
34
+ appOptions?: Partial<ApplicationOptions>;
35
+ resourceDir?: string;
36
+ autoPlay?: boolean;
37
+ }
38
+
39
+ export { }
package/dist/index.js ADDED
@@ -0,0 +1,310 @@
1
+ import { createValidator as se, createResourceManager as ue } from "@video-editor/protocol";
2
+ import { toRaw as fe, isRef as le, shallowRef as b, unref as q, ref as L, computed as de, effectScope as pe, watch as A } from "@vue/reactivity";
3
+ import { MP4Clip as _ } from "@webav/av-cliper";
4
+ import { file as ve } from "opfs-tools";
5
+ import { Application as ye, Sprite as F, Texture as D, Graphics as Y, Container as he } from "pixi.js";
6
+ async function me(t) {
7
+ const n = new ye();
8
+ return await n.init({ resizeTo: window, ...t }), n;
9
+ }
10
+ function we(t) {
11
+ const n = /* @__PURE__ */ new Set();
12
+ for (const a of t.tracks)
13
+ for (const i of a.children)
14
+ i.url && n.add(i.url);
15
+ return n;
16
+ }
17
+ function Te(t, n) {
18
+ const a = [];
19
+ return t.tracks.forEach((i, u) => {
20
+ i.children.forEach((s, p) => {
21
+ s.startTime <= n && n < s.endTime && a.push({ segment: s, trackIndex: u, childIndex: p });
22
+ });
23
+ }), a.sort((i, u) => i.trackIndex === u.trackIndex ? i.childIndex - u.childIndex : i.trackIndex - u.trackIndex);
24
+ }
25
+ function xe(t, n, a, i) {
26
+ const u = Me(n);
27
+ t instanceof F ? (t.anchor.set(0.5), t.width = a, t.height = i, t.x = a / 2, t.y = i / 2, t.texture.source?.addEventListener?.("error", () => {
28
+ t.texture = D.from(Ie(a, i));
29
+ }, { once: !0 })) : t instanceof Y && (t.clear(), t.rect(0, 0, a, i).fill({ color: z("url" in n && typeof n.url == "string" ? n.url : n.segmentType), alpha: B(n) ? u : 0.35 }), t.pivot.set(a / 2, i / 2), t.position.set(a / 2, i / 2)), t.alpha = u;
30
+ const s = Ce(n);
31
+ if (s) {
32
+ const [p, g] = s.position ?? [0, 0], [v, m] = s.scale ?? [1, 1], y = s.rotation?.[2] ?? 0;
33
+ t.position.set(a / 2 + p * a / 2, i / 2 - g * i / 2), t.scale.set(v, m), t.rotation = y / 180 * Math.PI;
34
+ }
35
+ }
36
+ function H(t, n) {
37
+ const a = new Y();
38
+ return a.rect(0, 0, 10, 10).fill({ color: z(n ?? t), alpha: 1 }), a;
39
+ }
40
+ function Ie(t, n, a) {
41
+ const i = `<svg xmlns="http://www.w3.org/2000/svg" width="${t}" height="${n}"><rect width="100%" height="100%" fill="#0f172a" fill-opacity="0.8"/></svg>`;
42
+ return `data:image/svg+xml;base64,${btoa(i)}`;
43
+ }
44
+ function z(t) {
45
+ let n = 0;
46
+ for (let a = 0; a < t.length; a++)
47
+ n = t.charCodeAt(a) + ((n << 5) - n);
48
+ return n & 16777215;
49
+ }
50
+ function ge(t) {
51
+ const n = t.tracks.flatMap((a) => a.children.map((i) => i.endTime));
52
+ return n.length ? Math.max(...n) : 0;
53
+ }
54
+ function W(t, n, a) {
55
+ return Math.min(Math.max(t, n), a);
56
+ }
57
+ function J(t) {
58
+ const n = fe(t);
59
+ return JSON.parse(JSON.stringify(n));
60
+ }
61
+ function B(t) {
62
+ return "opacity" in t;
63
+ }
64
+ function Me(t) {
65
+ return B(t) && typeof t.opacity == "number" ? t.opacity : 1;
66
+ }
67
+ function Se(t) {
68
+ return "transform" in t;
69
+ }
70
+ function Ce(t) {
71
+ if (Se(t))
72
+ return t.transform;
73
+ }
74
+ const De = "/video-editor-res";
75
+ async function ke(t) {
76
+ const n = se(), a = le(t.protocol) ? t.protocol : b(t.protocol), i = b(
77
+ n.verify(J(q(a)))
78
+ ), u = t.app ?? await me(t.appOptions), s = new he();
79
+ u.stage.addChild(s);
80
+ const p = ue({ dir: t.resourceDir }), g = /* @__PURE__ */ new Set(), v = /* @__PURE__ */ new Map(), m = /* @__PURE__ */ new Map(), y = /* @__PURE__ */ new Map(), f = L(0), x = L(!1), w = de(() => ge(i.value));
81
+ let M, C = 0;
82
+ async function G(e) {
83
+ const { protocol: o, at: r, layer: c } = e, l = Te(o, r), I = e.app.renderer.width, d = e.app.renderer.height, h = [];
84
+ for (const { segment: T } of l) {
85
+ const P = await e.getDisplay(T);
86
+ P && (xe(P, T, I, d), ne(T) && await re(T, r), h.push(P));
87
+ }
88
+ c.removeChildren();
89
+ const S = h.filter(Boolean);
90
+ S.length && c.addChild(...S), e.app.render();
91
+ }
92
+ const O = Re(() => G({
93
+ app: u,
94
+ layer: s,
95
+ protocol: i.value,
96
+ at: f.value,
97
+ getDisplay: X
98
+ })), k = pe();
99
+ k.run(() => {
100
+ A(
101
+ () => q(a),
102
+ (e) => {
103
+ try {
104
+ i.value = n.verify(J(e));
105
+ } catch (o) {
106
+ console.error("[renderer] invalid protocol update", o);
107
+ return;
108
+ }
109
+ N(), Q(i.value), j(i.value), R(), O();
110
+ },
111
+ { deep: !0, immediate: !0 }
112
+ ), A(f, () => {
113
+ R(), O();
114
+ }), A(w, () => R());
115
+ });
116
+ function R() {
117
+ const e = w.value;
118
+ e <= 0 ? f.value = 0 : f.value > e ? f.value = e : f.value < 0 && (f.value = 0);
119
+ }
120
+ function Q(e) {
121
+ for (const o of we(e))
122
+ g.has(o) || (g.add(o), p.add(o).catch(() => {
123
+ }));
124
+ }
125
+ function j(e) {
126
+ const o = new Set(e.tracks.flatMap((r) => r.children.map((c) => c.id)));
127
+ for (const [r, c] of v)
128
+ o.has(r) || (c.destroy(), v.delete(r));
129
+ for (const [r, c] of y)
130
+ o.has(r) || (c.clip.destroy(), y.delete(r));
131
+ }
132
+ function N() {
133
+ s.removeChildren();
134
+ for (const e of v.values())
135
+ e.destroy();
136
+ v.clear(), m.clear();
137
+ for (const { clip: e } of y.values())
138
+ e.destroy();
139
+ y.clear();
140
+ }
141
+ function $() {
142
+ x.value || (x.value = !0, C = performance.now(), M = requestAnimationFrame(U));
143
+ }
144
+ function E() {
145
+ x.value = !1, M !== void 0 && cancelAnimationFrame(M), M = void 0;
146
+ }
147
+ function U() {
148
+ V(), x.value && (M = requestAnimationFrame(U));
149
+ }
150
+ function V(e) {
151
+ if (!x.value && e === void 0)
152
+ return;
153
+ const o = performance.now(), r = e ?? (C ? o - C : 0);
154
+ C = o, r !== 0 && (f.value = W(
155
+ f.value + r,
156
+ 0,
157
+ w.value || Number.POSITIVE_INFINITY
158
+ ), w.value > 0 && f.value >= w.value && E());
159
+ }
160
+ function K(e) {
161
+ f.value = W(e, 0, w.value || Number.POSITIVE_INFINITY);
162
+ }
163
+ async function X(e) {
164
+ const o = v.get(e.id);
165
+ if (o)
166
+ return o;
167
+ const r = m.get(e.id);
168
+ if (r)
169
+ return r;
170
+ const c = Z(e);
171
+ m.set(e.id, c);
172
+ const l = await c;
173
+ return l && v.set(e.id, l), m.delete(e.id), l;
174
+ }
175
+ async function Z(e) {
176
+ if (e.segmentType === "frames" || e.segmentType === "image") {
177
+ if (!e.url)
178
+ return H(e.segmentType);
179
+ if ("type" in e && e.type === "video") {
180
+ const r = await te(e);
181
+ if (r)
182
+ return r;
183
+ }
184
+ const o = await ee(e.url);
185
+ return o ? new F(o) : H(e.segmentType, e.url);
186
+ }
187
+ e.segmentType !== "text" && (e.segmentType === "effect" || e.segmentType);
188
+ }
189
+ async function ee(e) {
190
+ const o = e.startsWith("data:"), r = /^https?:\/\//.test(e);
191
+ if (!o && !r)
192
+ try {
193
+ await p.add(e);
194
+ const c = await p.get(e);
195
+ if (c instanceof HTMLImageElement)
196
+ return D.from(c);
197
+ } catch {
198
+ }
199
+ return await ae(e);
200
+ }
201
+ async function te(e) {
202
+ const o = y.get(e.id);
203
+ if (o)
204
+ return o.sprite;
205
+ let r;
206
+ try {
207
+ await p.add(e.url), r = await oe(e.url);
208
+ } catch {
209
+ r = void 0;
210
+ }
211
+ let c;
212
+ try {
213
+ if (r)
214
+ c = new _(r);
215
+ else {
216
+ const T = await fetch(e.url);
217
+ if (!T.body)
218
+ return;
219
+ c = new _(T.body);
220
+ }
221
+ } catch {
222
+ return;
223
+ }
224
+ try {
225
+ await c.ready;
226
+ } catch {
227
+ c.destroy();
228
+ return;
229
+ }
230
+ const { width: l, height: I } = c.meta, d = document.createElement("canvas");
231
+ d.width = l || 1, d.height = I || 1;
232
+ const h = D.from(d), S = new F(h);
233
+ return y.set(e.id, { clip: c, canvas: d, texture: h, sprite: S, meta: { width: l, height: I } }), S;
234
+ }
235
+ async function re(e, o) {
236
+ const r = y.get(e.id);
237
+ if (r)
238
+ try {
239
+ const c = e.fromTime ?? 0, l = Math.max(0, o - e.startTime + c), I = Math.floor(l * 1e3), d = await r.clip.tick(I);
240
+ if (d.video) {
241
+ const h = r.canvas.getContext("2d");
242
+ h && (h.drawImage(d.video, 0, 0, r.canvas.width, r.canvas.height), ie(r.texture)), d.video.close();
243
+ }
244
+ } catch (c) {
245
+ console.warn("[renderer] update video frame failed", c);
246
+ }
247
+ }
248
+ function ne(e) {
249
+ return e.segmentType === "frames" && e.type === "video" && typeof e.url == "string";
250
+ }
251
+ async function oe(e) {
252
+ const o = t.resourceDir ?? De;
253
+ try {
254
+ const r = ve(`${o}/${e}`);
255
+ if (await r.exists())
256
+ return r;
257
+ } catch {
258
+ return;
259
+ }
260
+ }
261
+ function ae(e) {
262
+ return new Promise((o) => {
263
+ const r = new Image();
264
+ r.crossOrigin = "anonymous", r.onload = () => o(D.from(r)), r.onerror = () => {
265
+ console.warn("[renderer] failed to load image", e), o(void 0);
266
+ }, r.src = e;
267
+ });
268
+ }
269
+ function ie(e) {
270
+ const o = e.source;
271
+ if ("update" in o && typeof o.update == "function") {
272
+ o.update();
273
+ return;
274
+ }
275
+ typeof e.update == "function" && e.update();
276
+ }
277
+ function ce() {
278
+ E(), k.stop(), N(), s.destroy({ children: !0 }), v.clear(), m.clear(), g.clear(), t.app || u.destroy();
279
+ }
280
+ return t.autoPlay && $(), {
281
+ app: u,
282
+ layer: s,
283
+ currentTime: f,
284
+ duration: w,
285
+ isPlaying: x,
286
+ play: $,
287
+ pause: E,
288
+ tick: V,
289
+ seek: K,
290
+ destroy: ce
291
+ };
292
+ }
293
+ function Re(t) {
294
+ let n = !1, a = !1;
295
+ return async () => {
296
+ if (a) {
297
+ n = !0;
298
+ return;
299
+ }
300
+ a = !0;
301
+ do
302
+ n = !1, await t();
303
+ while (n);
304
+ a = !1;
305
+ };
306
+ }
307
+ export {
308
+ ke as createRenderer
309
+ };
310
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sources":["../src/2d/index.ts","../src/helpers.ts","../src/index.ts"],"sourcesContent":["/// <reference types=\"vite/client\" />\nimport type { ApplicationOptions } from 'pixi.js'\nimport { Application } from 'pixi.js'\n\ndeclare global {\n\n // eslint-disable-next-line vars-on-top\n var __PIXI_APP__: Application\n}\n\nexport async function createApp(opts?: Partial<ApplicationOptions>) {\n const app = new Application()\n\n await app.init({ resizeTo: window, ...opts })\n\n if (import.meta.env.DEV) {\n globalThis.__PIXI_APP__ = app\n }\n\n return app\n}\n","import type { ITransform, IVideoProtocol, SegmentUnion } from '@video-editor/shared'\nimport type { PixiDisplayObject } from './types'\nimport { toRaw } from '@vue/reactivity'\nimport { Graphics, Sprite, Texture } from 'pixi.js'\n\nexport function collectResourceUrls(protocol: IVideoProtocol) {\n const urls = new Set<string>()\n for (const track of protocol.tracks) {\n for (const segment of track.children) {\n if (segment.url)\n urls.add(segment.url)\n }\n }\n return urls\n}\n\nexport function collectActiveSegments(protocol: IVideoProtocol, at: number) {\n const active: { segment: SegmentUnion, trackIndex: number, childIndex: number }[] = []\n protocol.tracks.forEach((track, trackIndex) => {\n track.children.forEach((segment, childIndex) => {\n if (segment.startTime <= at && at < segment.endTime)\n active.push({ segment, trackIndex, childIndex })\n })\n })\n\n return active.sort((a, b) => {\n if (a.trackIndex === b.trackIndex)\n return a.childIndex - b.childIndex\n return a.trackIndex - b.trackIndex\n })\n}\n\nexport function applyDisplayProps(display: PixiDisplayObject, segment: SegmentUnion, width: number, height: number) {\n const opacity = readOpacity(segment)\n // size\n if (display instanceof Sprite) {\n display.anchor.set(0.5)\n display.width = width\n display.height = height\n display.x = width / 2\n display.y = height / 2\n const src = display.texture.source as { addEventListener?: (type: string, cb: () => void, opts?: AddEventListenerOptions) => void } | undefined\n src?.addEventListener?.('error', () => {\n // fallback to a colored rect if texture failed\n display.texture = Texture.from(placeholderTexture(width, height))\n }, { once: true })\n }\n else if (display instanceof Graphics) {\n display.clear()\n display\n .rect(0, 0, width, height)\n .fill({ color: stringToColor('url' in segment && typeof segment.url === 'string' ? segment.url : segment.segmentType), alpha: hasOpacity(segment) ? opacity : 0.35 })\n display.pivot.set(width / 2, height / 2)\n display.position.set(width / 2, height / 2)\n }\n\n display.alpha = opacity\n\n // simple 2D transform\n const transform = readTransform(segment)\n if (transform) {\n const [px, py] = transform.position ?? [0, 0]\n const [sx, sy] = transform.scale ?? [1, 1]\n const rotation = transform.rotation?.[2] ?? 0\n\n display.position.set(width / 2 + (px * width) / 2, height / 2 - (py * height) / 2)\n display.scale.set(sx, sy)\n display.rotation = (rotation / 180) * Math.PI\n }\n}\n\nexport function placeholder(key: string, url?: string) {\n const g = new Graphics()\n g.rect(0, 0, 10, 10).fill({ color: stringToColor(url ?? key), alpha: 1 })\n return g\n}\n\nexport function placeholderTexture(width: number, height: number, color?: string) {\n const svg = `<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"${width}\" height=\"${height}\"><rect width=\"100%\" height=\"100%\" fill=\"${color ?? '#0f172a'}\" fill-opacity=\"0.8\"/></svg>`\n return `data:image/svg+xml;base64,${btoa(svg)}`\n}\n\nexport function stringToColor(key: string) {\n let hash = 0\n for (let i = 0; i < key.length; i++)\n hash = key.charCodeAt(i) + ((hash << 5) - hash)\n return hash & 0x00FFFFFF\n}\n\nexport function computeDuration(protocol: IVideoProtocol) {\n const endTimes = protocol.tracks.flatMap(track => track.children.map(seg => seg.endTime))\n return endTimes.length ? Math.max(...endTimes) : 0\n}\n\nexport function clamp(num: number, min: number, max: number) {\n return Math.min(Math.max(num, min), max)\n}\n\nexport function cloneProtocol(protocol: IVideoProtocol) {\n const raw = toRaw(protocol) as IVideoProtocol\n // use JSON clone to avoid structuredClone errors on proxies (e.g., Vue reactive)\n return JSON.parse(JSON.stringify(raw)) as IVideoProtocol\n}\n\nfunction hasOpacity(segment: SegmentUnion): segment is SegmentUnion & { opacity?: number } {\n return 'opacity' in segment\n}\n\nfunction readOpacity(segment: SegmentUnion) {\n if (hasOpacity(segment) && typeof segment.opacity === 'number')\n return segment.opacity\n return 1\n}\n\nfunction hasTransform(segment: SegmentUnion): segment is SegmentUnion & { transform?: ITransform } {\n return 'transform' in segment\n}\n\nfunction readTransform(segment: SegmentUnion) {\n if (hasTransform(segment))\n return segment.transform\n return undefined\n}\n","import type { IVideoFramesSegment, IVideoProtocol, SegmentUnion } from '@video-editor/shared'\nimport type { ComputedRef, Ref, ShallowRef } from '@vue/reactivity'\nimport type { Application, ApplicationOptions } from 'pixi.js'\nimport type { MaybeRef, PixiDisplayObject } from './types'\nimport { createResourceManager, createValidator } from '@video-editor/protocol'\nimport {\n computed,\n effectScope,\n isRef,\n ref,\n shallowRef,\n unref,\n watch,\n} from '@vue/reactivity'\nimport { MP4Clip } from '@webav/av-cliper'\nimport { file as opfsFile } from 'opfs-tools'\nimport { Container, Sprite, Texture } from 'pixi.js'\nimport { createApp as create2dApp } from './2d'\nimport {\n applyDisplayProps,\n clamp,\n cloneProtocol,\n collectActiveSegments,\n collectResourceUrls,\n computeDuration,\n placeholder,\n} from './helpers'\n\nconst DEFAULT_RES_DIR = '/video-editor-res'\n\nexport interface RendererOptions {\n protocol: MaybeRef<IVideoProtocol>\n app?: Application\n appOptions?: Partial<ApplicationOptions>\n resourceDir?: string\n autoPlay?: boolean\n}\n\nexport interface Renderer {\n app: Application\n layer: Container\n currentTime: Ref<number>\n duration: ComputedRef<number>\n isPlaying: Ref<boolean>\n play: () => void\n pause: () => void\n tick: (deltaMs?: number) => void\n seek: (time: number) => void\n destroy: () => void\n}\n\n/**\n * Create a renderer that reacts to protocol updates and drives playback state.\n * - Pass a reactive `protocol` (Ref/readonly/normal object)\n * - Call `play/pause/seek/tick` to drive the timeline\n * - Rendering updates when `protocol` or `currentTime` changes\n */\nexport async function createRenderer(opts: RendererOptions): Promise<Renderer> {\n const validator = createValidator()\n const protocolInput: Ref<IVideoProtocol> | ShallowRef<IVideoProtocol>\n = isRef(opts.protocol) ? opts.protocol : shallowRef(opts.protocol)\n const validatedProtocol: ShallowRef<IVideoProtocol> = shallowRef(\n validator.verify(cloneProtocol(unref(protocolInput))),\n )\n\n const app = opts.app ?? await create2dApp(opts.appOptions)\n const layer = new Container()\n app.stage.addChild(layer)\n\n const resourceManager = createResourceManager({ dir: opts.resourceDir })\n const resourceWarmUp = new Set<string>()\n const displayCache = new Map<string, PixiDisplayObject>()\n const displayLoading = new Map<string, Promise<PixiDisplayObject | undefined>>()\n const videoEntries = new Map<string, {\n clip: MP4Clip\n canvas: HTMLCanvasElement\n texture: Texture\n sprite: Sprite\n meta?: { width: number, height: number }\n }>()\n\n const currentTime = ref(0)\n const isPlaying = ref(false)\n const duration = computed(() => computeDuration(validatedProtocol.value))\n\n let rafId: number | undefined\n let lastTickAt = 0\n\n interface RenderTask {\n app: Application\n layer: Container\n protocol: IVideoProtocol\n at: number\n getDisplay: (segment: SegmentUnion) => Promise<PixiDisplayObject | undefined>\n }\n\n async function renderScene(task: RenderTask) {\n const { protocol, at, layer } = task\n const active = collectActiveSegments(protocol, at)\n const stageWidth = task.app.renderer.width\n const stageHeight = task.app.renderer.height\n\n const renders: (PixiDisplayObject | undefined)[] = []\n for (const { segment } of active) {\n const display = await task.getDisplay(segment)\n if (!display)\n continue\n applyDisplayProps(display, segment, stageWidth, stageHeight)\n if (isVideoSegment(segment))\n await updateVideoFrame(segment, at)\n renders.push(display)\n }\n\n layer.removeChildren()\n const cleaned = renders.filter(Boolean) as PixiDisplayObject[]\n if (cleaned.length)\n layer.addChild(...cleaned)\n task.app.render()\n }\n\n const queueRender = createRenderQueue(() => renderScene({\n app,\n layer,\n protocol: validatedProtocol.value,\n at: currentTime.value,\n getDisplay: getDisplayForSegment,\n }))\n\n const scope = effectScope()\n scope.run(() => {\n // Sync external protocol mutations into a verified snapshot the renderer can rely on.\n watch(\n () => unref(protocolInput),\n (protocol) => {\n try {\n validatedProtocol.value = validator.verify(cloneProtocol(protocol))\n }\n catch (err) {\n console.error('[renderer] invalid protocol update', err)\n return\n }\n clearDisplays()\n warmUpResources(validatedProtocol.value)\n cleanupCache(validatedProtocol.value)\n clampCurrentTime()\n queueRender()\n },\n { deep: true, immediate: true },\n )\n\n // React to time changes.\n watch(currentTime, () => {\n clampCurrentTime()\n queueRender()\n })\n\n // Keep duration/currentTime in sync with protocol updates.\n watch(duration, () => clampCurrentTime())\n })\n\n function clampCurrentTime() {\n const nextDuration = duration.value\n if (nextDuration <= 0)\n currentTime.value = 0\n else if (currentTime.value > nextDuration)\n currentTime.value = nextDuration\n else if (currentTime.value < 0)\n currentTime.value = 0\n }\n\n function warmUpResources(protocol: IVideoProtocol) {\n for (const url of collectResourceUrls(protocol)) {\n if (resourceWarmUp.has(url))\n continue\n\n resourceWarmUp.add(url)\n resourceManager.add(url).catch(() => {\n // noop – render will fall back to Texture.from(url)\n })\n }\n }\n\n function cleanupCache(protocol: IVideoProtocol) {\n const ids = new Set(protocol.tracks.flatMap(track => track.children.map(seg => seg.id)))\n for (const [id, display] of displayCache) {\n if (ids.has(id))\n continue\n display.destroy()\n displayCache.delete(id)\n }\n for (const [id, entry] of videoEntries) {\n if (ids.has(id))\n continue\n entry.clip.destroy()\n videoEntries.delete(id)\n }\n }\n\n function clearDisplays() {\n layer.removeChildren()\n for (const display of displayCache.values()) {\n display.destroy()\n }\n displayCache.clear()\n displayLoading.clear()\n for (const { clip } of videoEntries.values())\n clip.destroy()\n videoEntries.clear()\n }\n\n function play() {\n if (isPlaying.value)\n return\n isPlaying.value = true\n lastTickAt = performance.now()\n rafId = requestAnimationFrame(loop)\n }\n\n function pause() {\n isPlaying.value = false\n if (rafId !== undefined)\n cancelAnimationFrame(rafId)\n rafId = undefined\n }\n\n function loop() {\n tick()\n if (isPlaying.value)\n rafId = requestAnimationFrame(loop)\n }\n\n function tick(deltaMs?: number) {\n if (!isPlaying.value && deltaMs === undefined)\n return\n\n const now = performance.now()\n const delta = deltaMs ?? (lastTickAt ? now - lastTickAt : 0)\n lastTickAt = now\n\n if (delta === 0)\n return\n\n currentTime.value = clamp(\n currentTime.value + delta,\n 0,\n duration.value || Number.POSITIVE_INFINITY,\n )\n\n if (duration.value > 0 && currentTime.value >= duration.value)\n pause()\n\n // render happens via watch on currentTime\n }\n\n function seek(time: number) {\n currentTime.value = clamp(time, 0, duration.value || Number.POSITIVE_INFINITY)\n }\n\n async function getDisplayForSegment(segment: SegmentUnion) {\n const cached = displayCache.get(segment.id)\n if (cached)\n return cached\n\n const loading = displayLoading.get(segment.id)\n if (loading)\n return loading\n\n const promise = loadDisplay(segment)\n displayLoading.set(segment.id, promise)\n\n const display = await promise\n if (display)\n displayCache.set(segment.id, display)\n\n displayLoading.delete(segment.id)\n return display\n }\n\n async function loadDisplay(segment: SegmentUnion): Promise<PixiDisplayObject | undefined> {\n // prioritize static resources via protocol resource manager\n if (segment.segmentType === 'frames' || segment.segmentType === 'image') {\n if (!segment.url)\n return placeholder(segment.segmentType)\n\n if ('type' in segment && segment.type === 'video') {\n const sprite = await loadVideoSprite(segment)\n if (sprite)\n return sprite\n }\n\n const texture = await loadTexture(segment.url)\n if (texture)\n return new Sprite(texture)\n return placeholder(segment.segmentType, segment.url)\n }\n\n if (segment.segmentType === 'text')\n return undefined\n\n if (segment.segmentType === 'effect' || segment.segmentType === 'filter')\n return undefined\n\n // audio segments do not render visuals\n return undefined\n }\n\n async function loadTexture(url: string) {\n const isDataUrl = url.startsWith('data:')\n const isHttp = /^https?:\\/\\//.test(url)\n\n if (!isDataUrl && !isHttp) {\n try {\n await resourceManager.add(url)\n const res = await resourceManager.get(url)\n if (res instanceof HTMLImageElement)\n return Texture.from(res)\n }\n catch {\n // fall through to direct image load\n }\n }\n\n // load image directly to avoid invalid path issues with http/data URLs\n return await loadImageTexture(url)\n }\n\n async function loadVideoSprite(segment: SegmentUnion & { type: 'video', url: string }): Promise<Sprite | undefined> {\n const existing = videoEntries.get(segment.id)\n if (existing)\n return existing.sprite\n\n let file\n try {\n await resourceManager.add(segment.url)\n file = await getOpfsFile(segment.url)\n }\n catch {\n file = undefined\n }\n let clip: MP4Clip\n try {\n if (file) {\n clip = new MP4Clip(file)\n }\n else {\n const res = await fetch(segment.url)\n if (!res.body)\n return undefined\n clip = new MP4Clip(res.body)\n }\n }\n catch {\n return undefined\n }\n\n try {\n await clip.ready\n }\n catch {\n clip.destroy()\n return undefined\n }\n\n const { width, height } = clip.meta\n const canvas = document.createElement('canvas')\n canvas.width = width || 1\n canvas.height = height || 1\n const texture = Texture.from(canvas)\n const sprite = new Sprite(texture)\n\n videoEntries.set(segment.id, { clip, canvas, texture, sprite, meta: { width, height } })\n return sprite\n }\n\n async function updateVideoFrame(segment: IVideoFramesSegment, at: number) {\n const entry = videoEntries.get(segment.id)\n if (!entry)\n return\n\n try {\n const offsetMs = segment.fromTime ?? 0\n const relativeMs = Math.max(0, at - segment.startTime + offsetMs)\n const relativeUs = Math.floor(relativeMs * 1000)\n const res = await entry.clip.tick(relativeUs)\n if (res.video) {\n const ctx = entry.canvas.getContext('2d')\n if (ctx) {\n ctx.drawImage(res.video, 0, 0, entry.canvas.width, entry.canvas.height)\n refreshCanvasTexture(entry.texture)\n }\n res.video.close()\n }\n }\n catch (err) {\n console.warn('[renderer] update video frame failed', err)\n }\n }\n\n function isVideoSegment(segment: SegmentUnion): segment is IVideoFramesSegment {\n return segment.segmentType === 'frames'\n && segment.type === 'video'\n && typeof segment.url === 'string'\n }\n\n async function getOpfsFile(url: string) {\n const dir = opts.resourceDir ?? DEFAULT_RES_DIR\n try {\n const file = opfsFile(`${dir}/${url}`)\n if (await file.exists())\n return file\n }\n catch {\n return undefined\n }\n return undefined\n }\n\n function loadImageTexture(url: string): Promise<Texture | undefined> {\n return new Promise((resolve) => {\n const img = new Image()\n img.crossOrigin = 'anonymous'\n img.onload = () => resolve(Texture.from(img))\n img.onerror = () => {\n console.warn('[renderer] failed to load image', url)\n resolve(undefined)\n }\n img.src = url\n })\n }\n\n function refreshCanvasTexture(texture: Texture) {\n const source = texture.source\n if ('update' in source && typeof source.update === 'function') {\n source.update()\n return\n }\n\n if (typeof texture.update === 'function')\n texture.update()\n }\n\n function destroy() {\n pause()\n scope.stop()\n clearDisplays()\n layer.destroy({ children: true })\n displayCache.clear()\n displayLoading.clear()\n resourceWarmUp.clear()\n if (!opts.app)\n app.destroy()\n }\n\n if (opts.autoPlay)\n play()\n\n return {\n app,\n layer,\n currentTime,\n duration,\n isPlaying,\n play,\n pause,\n tick,\n seek,\n destroy,\n }\n}\n\nfunction createRenderQueue(job: () => Promise<void> | void) {\n let queued = false\n let running = false\n\n const run = async () => {\n if (running) {\n queued = true\n return\n }\n running = true\n do {\n queued = false\n await job()\n } while (queued)\n running = false\n }\n\n return run\n}\n"],"names":["createApp","opts","app","Application","collectResourceUrls","protocol","urls","track","segment","collectActiveSegments","at","active","trackIndex","childIndex","a","b","applyDisplayProps","display","width","height","opacity","readOpacity","Sprite","Texture","placeholderTexture","Graphics","stringToColor","hasOpacity","transform","readTransform","px","py","sx","sy","rotation","placeholder","key","url","g","color","svg","hash","i","computeDuration","endTimes","seg","clamp","num","min","max","cloneProtocol","raw","toRaw","hasTransform","DEFAULT_RES_DIR","createRenderer","validator","createValidator","protocolInput","isRef","shallowRef","validatedProtocol","unref","create2dApp","layer","Container","resourceManager","createResourceManager","resourceWarmUp","displayCache","displayLoading","videoEntries","currentTime","ref","isPlaying","duration","computed","rafId","lastTickAt","renderScene","task","stageWidth","stageHeight","renders","isVideoSegment","updateVideoFrame","cleaned","queueRender","createRenderQueue","getDisplayForSegment","scope","effectScope","watch","err","clearDisplays","warmUpResources","cleanupCache","clampCurrentTime","nextDuration","ids","id","entry","clip","play","loop","pause","tick","deltaMs","now","delta","seek","time","cached","loading","promise","loadDisplay","sprite","loadVideoSprite","texture","loadTexture","isDataUrl","isHttp","res","loadImageTexture","existing","file","getOpfsFile","MP4Clip","canvas","offsetMs","relativeMs","relativeUs","ctx","refreshCanvasTexture","dir","opfsFile","resolve","img","source","destroy","job","queued","running"],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,32 @@
1
+ {
2
+ "name": "@video-editor/renderer",
3
+ "type": "module",
4
+ "version": "0.0.1-beta.1",
5
+ "exports": {
6
+ ".": {
7
+ "import": "./dist/index.js"
8
+ },
9
+ "./*": "./*"
10
+ },
11
+ "main": "dist/index.js",
12
+ "module": "dist/index.js",
13
+ "types": "dist/index.d.ts",
14
+ "files": [
15
+ "dist"
16
+ ],
17
+ "peerDependencies": {
18
+ "@vue/reactivity": "^3.4.19"
19
+ },
20
+ "dependencies": {
21
+ "@webav/av-cliper": "^1.2.7",
22
+ "opfs-tools": "^0.7.4",
23
+ "pixi.js": "^8.14.3",
24
+ "@video-editor/protocol": "0.0.1-beta.1",
25
+ "@video-editor/shared": "0.0.1-beta.1"
26
+ },
27
+ "scripts": {
28
+ "build": "vite build",
29
+ "build:types": "vue-tsc --declaration --emitDeclarationOnly",
30
+ "preview": "vite preview"
31
+ }
32
+ }