@aelionsdk/sdk 0.1.0-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/LICENSE +21 -0
- package/README.md +63 -0
- package/dist/audio-controller.d.ts +25 -0
- package/dist/audio-controller.d.ts.map +1 -0
- package/dist/audio-controller.js +235 -0
- package/dist/audio-mastering.d.ts +27 -0
- package/dist/audio-mastering.d.ts.map +1 -0
- package/dist/audio-mastering.js +160 -0
- package/dist/composition.d.ts +75 -0
- package/dist/composition.d.ts.map +1 -0
- package/dist/composition.js +146 -0
- package/dist/default-schemas.d.ts +7 -0
- package/dist/default-schemas.d.ts.map +1 -0
- package/dist/default-schemas.js +18 -0
- package/dist/export-job.d.ts +22 -0
- package/dist/export-job.d.ts.map +1 -0
- package/dist/export-job.js +126 -0
- package/dist/extension-host.d.ts +90 -0
- package/dist/extension-host.d.ts.map +1 -0
- package/dist/extension-host.js +367 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +19 -0
- package/dist/media-provider.d.ts +49 -0
- package/dist/media-provider.d.ts.map +1 -0
- package/dist/media-provider.js +388 -0
- package/dist/migration-materials.d.ts +10 -0
- package/dist/migration-materials.d.ts.map +1 -0
- package/dist/migration-materials.js +148 -0
- package/dist/migration.d.ts +109 -0
- package/dist/migration.d.ts.map +1 -0
- package/dist/migration.js +1232 -0
- package/dist/persistence.d.ts +73 -0
- package/dist/persistence.d.ts.map +1 -0
- package/dist/persistence.js +245 -0
- package/dist/player.d.ts +22 -0
- package/dist/player.d.ts.map +1 -0
- package/dist/player.js +522 -0
- package/dist/preview-controller.d.ts +62 -0
- package/dist/preview-controller.d.ts.map +1 -0
- package/dist/preview-controller.js +377 -0
- package/dist/preview-quality.d.ts +7 -0
- package/dist/preview-quality.d.ts.map +1 -0
- package/dist/preview-quality.js +8 -0
- package/dist/production-media-provider.d.ts +112 -0
- package/dist/production-media-provider.d.ts.map +1 -0
- package/dist/production-media-provider.js +749 -0
- package/dist/project-builder.d.ts +249 -0
- package/dist/project-builder.d.ts.map +1 -0
- package/dist/project-builder.js +953 -0
- package/dist/runtime-material-registry.d.ts +10 -0
- package/dist/runtime-material-registry.d.ts.map +1 -0
- package/dist/runtime-material-registry.js +23 -0
- package/dist/session.d.ts +29 -0
- package/dist/session.d.ts.map +1 -0
- package/dist/session.js +1114 -0
- package/dist/types.d.ts +392 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +1 -0
- package/package.json +60 -0
|
@@ -0,0 +1,388 @@
|
|
|
1
|
+
import { AelionError } from '@aelionsdk/core';
|
|
2
|
+
import { createSampleIndex, createVideoFrameDecodeSessionFromReader, decodeAudioPcmRange, MemoryRangeReader, } from '@aelionsdk/media';
|
|
3
|
+
function positiveSafeInteger(value, name) {
|
|
4
|
+
if (!Number.isSafeInteger(value) || value <= 0) {
|
|
5
|
+
throw new RangeError(`${name} must be a positive safe integer`);
|
|
6
|
+
}
|
|
7
|
+
return value;
|
|
8
|
+
}
|
|
9
|
+
function nonNegativeSafeInteger(value, name) {
|
|
10
|
+
if (!Number.isSafeInteger(value) || value < 0) {
|
|
11
|
+
throw new RangeError(`${name} must be a non-negative safe integer`);
|
|
12
|
+
}
|
|
13
|
+
return value;
|
|
14
|
+
}
|
|
15
|
+
function errorReason(value, fallback) {
|
|
16
|
+
return value instanceof Error ? value : new Error(fallback, { cause: value });
|
|
17
|
+
}
|
|
18
|
+
function abortReason(signal) {
|
|
19
|
+
return signal.reason instanceof Error
|
|
20
|
+
? signal.reason
|
|
21
|
+
: new DOMException('Operation aborted', 'AbortError');
|
|
22
|
+
}
|
|
23
|
+
function settleShared(operation, result) {
|
|
24
|
+
if (operation.settled)
|
|
25
|
+
return;
|
|
26
|
+
operation.settled = true;
|
|
27
|
+
const subscribers = [...operation.subscribers.values()];
|
|
28
|
+
operation.subscribers.clear();
|
|
29
|
+
for (const subscriber of subscribers) {
|
|
30
|
+
subscriber.signal?.removeEventListener('abort', subscriber.onAbort);
|
|
31
|
+
if (result.ok)
|
|
32
|
+
subscriber.resolve(result.value);
|
|
33
|
+
else
|
|
34
|
+
subscriber.reject(result.error);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
function observeShared(operation, promise, failureMessage, onSettled) {
|
|
38
|
+
void promise.then(value => {
|
|
39
|
+
try {
|
|
40
|
+
settleShared(operation, { ok: true, value });
|
|
41
|
+
}
|
|
42
|
+
finally {
|
|
43
|
+
onSettled();
|
|
44
|
+
}
|
|
45
|
+
}, (error) => {
|
|
46
|
+
try {
|
|
47
|
+
settleShared(operation, {
|
|
48
|
+
ok: false,
|
|
49
|
+
error: errorReason(error, failureMessage),
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
finally {
|
|
53
|
+
onSettled();
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
function awaitShared(operation, signal) {
|
|
58
|
+
if (signal?.aborted === true)
|
|
59
|
+
return Promise.reject(abortReason(signal));
|
|
60
|
+
if (operation.settled)
|
|
61
|
+
return Promise.reject(new Error('Shared operation already settled'));
|
|
62
|
+
return new Promise((resolve, reject) => {
|
|
63
|
+
const token = Symbol('media-subscriber');
|
|
64
|
+
const onAbort = () => {
|
|
65
|
+
if (!operation.subscribers.delete(token))
|
|
66
|
+
return;
|
|
67
|
+
signal?.removeEventListener('abort', onAbort);
|
|
68
|
+
if (signal !== undefined)
|
|
69
|
+
reject(abortReason(signal));
|
|
70
|
+
if (operation.subscribers.size === 0 && !operation.settled) {
|
|
71
|
+
operation.controller.abort(new DOMException('Shared media operation has no active callers', 'AbortError'));
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
operation.subscribers.set(token, {
|
|
75
|
+
resolve,
|
|
76
|
+
reject,
|
|
77
|
+
...(signal === undefined ? {} : { signal }),
|
|
78
|
+
onAbort,
|
|
79
|
+
});
|
|
80
|
+
signal?.addEventListener('abort', onAbort, { once: true });
|
|
81
|
+
// Covers unusual AbortSignal implementations that become aborted while a
|
|
82
|
+
// listener is being installed.
|
|
83
|
+
if (signal?.aborted === true)
|
|
84
|
+
onAbort();
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Convenience provider for small Alpha projects. It keeps a bounded LRU of
|
|
89
|
+
* immutable asset bytes and reuses SampleIndex across exact seeks. Large/CDN
|
|
90
|
+
* integrations should provide their own Range-backed AelionMediaProvider.
|
|
91
|
+
*/
|
|
92
|
+
export class ByteMediaProvider {
|
|
93
|
+
#resolve;
|
|
94
|
+
#maxCachedBytes;
|
|
95
|
+
#maxConcurrentOperations;
|
|
96
|
+
#maxPendingOperations;
|
|
97
|
+
#maxInFlightRequests;
|
|
98
|
+
#cache = new Map();
|
|
99
|
+
#loads = new Map();
|
|
100
|
+
#assetLoadOperations = new Set();
|
|
101
|
+
#indexOperations = new Set();
|
|
102
|
+
#operationWaiters = [];
|
|
103
|
+
#cachedBytes = 0;
|
|
104
|
+
#activeOperations = 0;
|
|
105
|
+
#inFlightRequests = 0;
|
|
106
|
+
#generation = 0;
|
|
107
|
+
constructor(options) {
|
|
108
|
+
this.#resolve = options.resolveAssetBytes;
|
|
109
|
+
this.#maxCachedBytes = positiveSafeInteger(options.maxCachedBytes ?? 64 * 1_024 * 1_024, 'maxCachedBytes');
|
|
110
|
+
if (options.maxConcurrentOperations !== undefined &&
|
|
111
|
+
options.maxConcurrentLoads !== undefined &&
|
|
112
|
+
options.maxConcurrentOperations !== options.maxConcurrentLoads) {
|
|
113
|
+
throw new RangeError('maxConcurrentOperations and maxConcurrentLoads must match');
|
|
114
|
+
}
|
|
115
|
+
this.#maxConcurrentOperations = positiveSafeInteger(options.maxConcurrentOperations ?? options.maxConcurrentLoads ?? 4, 'maxConcurrentOperations');
|
|
116
|
+
this.#maxPendingOperations = nonNegativeSafeInteger(options.maxPendingOperations ?? 64, 'maxPendingOperations');
|
|
117
|
+
this.#maxInFlightRequests = this.#maxConcurrentOperations + this.#maxPendingOperations;
|
|
118
|
+
if (!Number.isSafeInteger(this.#maxInFlightRequests)) {
|
|
119
|
+
throw new RangeError('maxConcurrentOperations + maxPendingOperations must be a safe integer');
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
async frameAt(assetId, streamIndex, sourceTimeUs, signal) {
|
|
123
|
+
this.#admitRequest(signal);
|
|
124
|
+
try {
|
|
125
|
+
const cached = await this.#asset(assetId, signal);
|
|
126
|
+
const index = await this.#sampleIndex(cached, signal);
|
|
127
|
+
cached.videoSessions ??= new Map();
|
|
128
|
+
let session = cached.videoSessions.get(streamIndex);
|
|
129
|
+
if (session === undefined) {
|
|
130
|
+
session = createVideoFrameDecodeSessionFromReader(new MemoryRangeReader(`${assetId}:video:${streamIndex.toString()}`, cached.bytes), index, {
|
|
131
|
+
streamIndex,
|
|
132
|
+
maxCachedFrames: 8,
|
|
133
|
+
maxCachedBytes: 96 * 1_024 * 1_024,
|
|
134
|
+
});
|
|
135
|
+
cached.videoSessions.set(streamIndex, session);
|
|
136
|
+
}
|
|
137
|
+
const decoded = await this.#runBounded(signal, () => session.frameAt(sourceTimeUs, signal));
|
|
138
|
+
try {
|
|
139
|
+
if (signal?.aborted === true)
|
|
140
|
+
throw abortReason(signal);
|
|
141
|
+
return decoded.frame.clone();
|
|
142
|
+
}
|
|
143
|
+
finally {
|
|
144
|
+
decoded.close();
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
finally {
|
|
148
|
+
this.#releaseRequest();
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
async pcmRange(assetId, streamIndex, startUs, durationUs, signal) {
|
|
152
|
+
this.#admitRequest(signal);
|
|
153
|
+
try {
|
|
154
|
+
const cached = await this.#asset(assetId, signal);
|
|
155
|
+
const block = await this.#runBounded(signal, () => decodeAudioPcmRange(cached.bytes, startUs, durationUs, {
|
|
156
|
+
streamIndex,
|
|
157
|
+
...(signal === undefined ? {} : { signal }),
|
|
158
|
+
}));
|
|
159
|
+
if (signal?.aborted === true)
|
|
160
|
+
throw abortReason(signal);
|
|
161
|
+
return block;
|
|
162
|
+
}
|
|
163
|
+
finally {
|
|
164
|
+
this.#releaseRequest();
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
clear() {
|
|
168
|
+
this.#generation += 1;
|
|
169
|
+
for (const cached of this.#cache.values())
|
|
170
|
+
this.#disposeAsset(cached);
|
|
171
|
+
this.#cache.clear();
|
|
172
|
+
this.#cachedBytes = 0;
|
|
173
|
+
}
|
|
174
|
+
snapshot() {
|
|
175
|
+
return {
|
|
176
|
+
assets: this.#cache.size,
|
|
177
|
+
cachedBytes: this.#cachedBytes,
|
|
178
|
+
maxCachedBytes: this.#maxCachedBytes,
|
|
179
|
+
inFlightAssetLoads: this.#assetLoadOperations.size,
|
|
180
|
+
inFlightSampleIndexes: this.#indexOperations.size,
|
|
181
|
+
inFlightRequests: this.#inFlightRequests,
|
|
182
|
+
maxInFlightRequests: this.#maxInFlightRequests,
|
|
183
|
+
sharedOperationSubscribers: [...this.#assetLoadOperations, ...this.#indexOperations].reduce((total, operation) => total + operation.subscribers.size, 0),
|
|
184
|
+
activeOperations: this.#activeOperations,
|
|
185
|
+
pendingOperations: this.#operationWaiters.length,
|
|
186
|
+
maxConcurrentOperations: this.#maxConcurrentOperations,
|
|
187
|
+
maxPendingOperations: this.#maxPendingOperations,
|
|
188
|
+
activeLoads: this.#activeOperations,
|
|
189
|
+
pendingLoads: this.#operationWaiters.length,
|
|
190
|
+
maxConcurrentLoads: this.#maxConcurrentOperations,
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
async #asset(assetId, signal) {
|
|
194
|
+
if (signal?.aborted === true)
|
|
195
|
+
throw abortReason(signal);
|
|
196
|
+
const existing = this.#cache.get(assetId);
|
|
197
|
+
if (existing !== undefined) {
|
|
198
|
+
this.#cache.delete(assetId);
|
|
199
|
+
this.#cache.set(assetId, existing);
|
|
200
|
+
return existing;
|
|
201
|
+
}
|
|
202
|
+
const generation = this.#generation;
|
|
203
|
+
const inFlight = this.#loads.get(assetId);
|
|
204
|
+
if (inFlight?.generation === generation &&
|
|
205
|
+
!inFlight.settled &&
|
|
206
|
+
!inFlight.controller.signal.aborted) {
|
|
207
|
+
return awaitShared(inFlight, signal);
|
|
208
|
+
}
|
|
209
|
+
if (this.#activeOperations >= this.#maxConcurrentOperations &&
|
|
210
|
+
this.#operationWaiters.length >= this.#maxPendingOperations) {
|
|
211
|
+
throw this.#queueFullError('operation');
|
|
212
|
+
}
|
|
213
|
+
const controller = new AbortController();
|
|
214
|
+
const load = {
|
|
215
|
+
generation,
|
|
216
|
+
controller,
|
|
217
|
+
settled: false,
|
|
218
|
+
subscribers: new Map(),
|
|
219
|
+
};
|
|
220
|
+
this.#loads.set(assetId, load);
|
|
221
|
+
this.#assetLoadOperations.add(load);
|
|
222
|
+
observeShared(load, this.#loadAsset(assetId, generation, controller.signal), 'Shared asset load failed', () => {
|
|
223
|
+
this.#assetLoadOperations.delete(load);
|
|
224
|
+
if (this.#loads.get(assetId) === load)
|
|
225
|
+
this.#loads.delete(assetId);
|
|
226
|
+
});
|
|
227
|
+
return awaitShared(load, signal);
|
|
228
|
+
}
|
|
229
|
+
async #loadAsset(assetId, generation, signal) {
|
|
230
|
+
return this.#runBounded(signal, async () => {
|
|
231
|
+
const bytes = await this.#resolve(assetId, signal);
|
|
232
|
+
if (signal.aborted)
|
|
233
|
+
throw abortReason(signal);
|
|
234
|
+
if (!(bytes instanceof Uint8Array)) {
|
|
235
|
+
throw new TypeError(`Asset resolver returned no bytes for ${assetId}`);
|
|
236
|
+
}
|
|
237
|
+
// Only trust the genuine copy's intrinsic length. A Uint8Array subclass
|
|
238
|
+
// can override `byteLength`; using the resolver object's property would
|
|
239
|
+
// let it under-report cache ownership after a large copy was allocated.
|
|
240
|
+
const copy = new Uint8Array(bytes);
|
|
241
|
+
const byteLength = copy.byteLength;
|
|
242
|
+
if (byteLength === 0)
|
|
243
|
+
throw new TypeError(`Asset resolver returned no bytes for ${assetId}`);
|
|
244
|
+
const cached = { bytes: copy, byteLength };
|
|
245
|
+
if (generation !== this.#generation || byteLength > this.#maxCachedBytes)
|
|
246
|
+
return cached;
|
|
247
|
+
while (this.#cachedBytes + byteLength > this.#maxCachedBytes && this.#cache.size > 0) {
|
|
248
|
+
const oldest = this.#cache.entries().next().value;
|
|
249
|
+
if (oldest === undefined)
|
|
250
|
+
break;
|
|
251
|
+
this.#cache.delete(oldest[0]);
|
|
252
|
+
this.#disposeAsset(oldest[1]);
|
|
253
|
+
this.#cachedBytes -= oldest[1].byteLength;
|
|
254
|
+
}
|
|
255
|
+
this.#cache.set(assetId, cached);
|
|
256
|
+
this.#cachedBytes += cached.byteLength;
|
|
257
|
+
return cached;
|
|
258
|
+
});
|
|
259
|
+
}
|
|
260
|
+
async #sampleIndex(cached, signal) {
|
|
261
|
+
if (signal?.aborted === true)
|
|
262
|
+
throw abortReason(signal);
|
|
263
|
+
if (cached.index !== undefined)
|
|
264
|
+
return cached.index;
|
|
265
|
+
let operation = cached.indexOperation;
|
|
266
|
+
if (operation === undefined || operation.settled || operation.controller.signal.aborted) {
|
|
267
|
+
if (this.#activeOperations >= this.#maxConcurrentOperations &&
|
|
268
|
+
this.#operationWaiters.length >= this.#maxPendingOperations) {
|
|
269
|
+
throw this.#queueFullError('operation');
|
|
270
|
+
}
|
|
271
|
+
const controller = new AbortController();
|
|
272
|
+
operation = {
|
|
273
|
+
controller,
|
|
274
|
+
settled: false,
|
|
275
|
+
subscribers: new Map(),
|
|
276
|
+
};
|
|
277
|
+
const current = operation;
|
|
278
|
+
const promise = this.#runBounded(controller.signal, async () => {
|
|
279
|
+
const index = await createSampleIndex(cached.bytes, { signal: controller.signal });
|
|
280
|
+
if (controller.signal.aborted)
|
|
281
|
+
throw abortReason(controller.signal);
|
|
282
|
+
cached.index = index;
|
|
283
|
+
return index;
|
|
284
|
+
});
|
|
285
|
+
cached.indexOperation = current;
|
|
286
|
+
this.#indexOperations.add(current);
|
|
287
|
+
observeShared(current, promise, 'Shared media index operation failed', () => {
|
|
288
|
+
this.#indexOperations.delete(current);
|
|
289
|
+
if (cached.indexOperation === current)
|
|
290
|
+
cached.indexOperation = undefined;
|
|
291
|
+
});
|
|
292
|
+
}
|
|
293
|
+
return awaitShared(operation, signal);
|
|
294
|
+
}
|
|
295
|
+
#disposeAsset(cached) {
|
|
296
|
+
for (const session of cached.videoSessions?.values() ?? [])
|
|
297
|
+
session.dispose();
|
|
298
|
+
cached.videoSessions?.clear();
|
|
299
|
+
}
|
|
300
|
+
async #runBounded(signal, operation) {
|
|
301
|
+
await this.#acquireOperationSlot(signal);
|
|
302
|
+
try {
|
|
303
|
+
if (signal?.aborted === true)
|
|
304
|
+
throw abortReason(signal);
|
|
305
|
+
return await operation();
|
|
306
|
+
}
|
|
307
|
+
finally {
|
|
308
|
+
this.#releaseOperationSlot();
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
async #acquireOperationSlot(signal) {
|
|
312
|
+
if (signal?.aborted === true)
|
|
313
|
+
throw abortReason(signal);
|
|
314
|
+
if (this.#activeOperations < this.#maxConcurrentOperations) {
|
|
315
|
+
this.#activeOperations += 1;
|
|
316
|
+
return;
|
|
317
|
+
}
|
|
318
|
+
if (this.#operationWaiters.length >= this.#maxPendingOperations) {
|
|
319
|
+
throw this.#queueFullError('operation');
|
|
320
|
+
}
|
|
321
|
+
await new Promise((resolve, reject) => {
|
|
322
|
+
let queued = true;
|
|
323
|
+
const waiter = {
|
|
324
|
+
...(signal === undefined ? {} : { signal }),
|
|
325
|
+
grant: () => {
|
|
326
|
+
if (!queued)
|
|
327
|
+
return;
|
|
328
|
+
queued = false;
|
|
329
|
+
signal?.removeEventListener('abort', onAbort);
|
|
330
|
+
resolve();
|
|
331
|
+
},
|
|
332
|
+
reject: error => {
|
|
333
|
+
if (!queued)
|
|
334
|
+
return;
|
|
335
|
+
queued = false;
|
|
336
|
+
signal?.removeEventListener('abort', onAbort);
|
|
337
|
+
reject(error);
|
|
338
|
+
},
|
|
339
|
+
};
|
|
340
|
+
const onAbort = () => {
|
|
341
|
+
const index = this.#operationWaiters.indexOf(waiter);
|
|
342
|
+
if (index >= 0)
|
|
343
|
+
this.#operationWaiters.splice(index, 1);
|
|
344
|
+
if (signal !== undefined)
|
|
345
|
+
waiter.reject(abortReason(signal));
|
|
346
|
+
};
|
|
347
|
+
signal?.addEventListener('abort', onAbort, { once: true });
|
|
348
|
+
this.#operationWaiters.push(waiter);
|
|
349
|
+
});
|
|
350
|
+
}
|
|
351
|
+
#releaseOperationSlot() {
|
|
352
|
+
let next = this.#operationWaiters.shift();
|
|
353
|
+
while (next?.signal?.aborted === true) {
|
|
354
|
+
next.reject(abortReason(next.signal));
|
|
355
|
+
next = this.#operationWaiters.shift();
|
|
356
|
+
}
|
|
357
|
+
if (next === undefined) {
|
|
358
|
+
this.#activeOperations -= 1;
|
|
359
|
+
return;
|
|
360
|
+
}
|
|
361
|
+
next.grant();
|
|
362
|
+
}
|
|
363
|
+
#admitRequest(signal) {
|
|
364
|
+
if (signal?.aborted === true)
|
|
365
|
+
throw abortReason(signal);
|
|
366
|
+
if (this.#inFlightRequests >= this.#maxInFlightRequests) {
|
|
367
|
+
throw this.#queueFullError('request');
|
|
368
|
+
}
|
|
369
|
+
this.#inFlightRequests += 1;
|
|
370
|
+
}
|
|
371
|
+
#releaseRequest() {
|
|
372
|
+
this.#inFlightRequests -= 1;
|
|
373
|
+
}
|
|
374
|
+
#queueFullError(scope) {
|
|
375
|
+
const limit = scope === 'request' ? this.#maxInFlightRequests : this.#maxPendingOperations;
|
|
376
|
+
return new AelionError([
|
|
377
|
+
{
|
|
378
|
+
code: 'MEDIA_PROVIDER_QUEUE_FULL',
|
|
379
|
+
severity: 'error',
|
|
380
|
+
message: scope === 'request'
|
|
381
|
+
? `ByteMediaProvider reached its ${limit.toString()} in-flight request limit`
|
|
382
|
+
: `ByteMediaProvider operation queue reached its ${limit.toString()} pending operation limit`,
|
|
383
|
+
recoverable: true,
|
|
384
|
+
details: { scope, limit },
|
|
385
|
+
},
|
|
386
|
+
]);
|
|
387
|
+
}
|
|
388
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { JsonValue } from '@aelionsdk/core';
|
|
2
|
+
import type { WebGl2MaterialProgram } from '@aelionsdk/material-compiler';
|
|
3
|
+
import type { IrMaterialDefinition } from '@aelionsdk/render-ir';
|
|
4
|
+
export interface MigrationMaterialRegistry {
|
|
5
|
+
register(definition: IrMaterialDefinition, program: WebGl2MaterialProgram | ((parameters: Readonly<Record<string, JsonValue>>) => WebGl2MaterialProgram)): () => void;
|
|
6
|
+
}
|
|
7
|
+
/** Install renderable programs for every Material emitted by the Diffusion adapter. */
|
|
8
|
+
export declare function installMigrationMaterials(registry: MigrationMaterialRegistry): () => void;
|
|
9
|
+
export declare function migrationMaterialProgram(materialId: string): WebGl2MaterialProgram | undefined;
|
|
10
|
+
//# sourceMappingURL=migration-materials.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"migration-materials.d.ts","sourceRoot":"","sources":["../src/migration-materials.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AACjD,OAAO,KAAK,EAA0B,qBAAqB,EAAE,MAAM,8BAA8B,CAAC;AAClG,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAIjE,MAAM,WAAW,yBAAyB;IACxC,QAAQ,CACN,UAAU,EAAE,oBAAoB,EAChC,OAAO,EACH,qBAAqB,GACrB,CAAC,CAAC,UAAU,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC,KAAK,qBAAqB,CAAC,GAC/E,MAAM,IAAI,CAAC;CACf;AAoMD,uFAAuF;AACvF,wBAAgB,yBAAyB,CAAC,QAAQ,EAAE,yBAAyB,GAAG,MAAM,IAAI,CAOzF;AAED,wBAAgB,wBAAwB,CAAC,UAAU,EAAE,MAAM,GAAG,qBAAqB,GAAG,SAAS,CAE9F"}
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import { migrationMaterialPackage } from './migration.js';
|
|
2
|
+
const vertexPrelude = `#version 300 es
|
|
3
|
+
precision highp float;
|
|
4
|
+
`;
|
|
5
|
+
function plan(id, textureSamples) {
|
|
6
|
+
return {
|
|
7
|
+
passes: [
|
|
8
|
+
{
|
|
9
|
+
id,
|
|
10
|
+
kind: 'draw',
|
|
11
|
+
nodes: [id],
|
|
12
|
+
estimatedTextureSamples: textureSamples,
|
|
13
|
+
},
|
|
14
|
+
],
|
|
15
|
+
intermediateTextureCount: 0,
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
function parameter(id) {
|
|
19
|
+
return {
|
|
20
|
+
name: `u_parameter_${id}`,
|
|
21
|
+
type: 'float',
|
|
22
|
+
source: { kind: 'parameter', id },
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
const progressUniform = {
|
|
26
|
+
name: 'u_system_transitionProgress',
|
|
27
|
+
type: 'float',
|
|
28
|
+
source: { kind: 'system', id: 'transitionProgress' },
|
|
29
|
+
};
|
|
30
|
+
function transitionProgram(id, body, textureSamples = 2) {
|
|
31
|
+
return {
|
|
32
|
+
backend: 'webgl2',
|
|
33
|
+
nodeSet: 'aelion.migration/1.0.0',
|
|
34
|
+
graphHash: `aelion-migration-${id}-v1`,
|
|
35
|
+
inputPorts: ['from', 'to'],
|
|
36
|
+
uniforms: [progressUniform],
|
|
37
|
+
executionPlan: plan(id, textureSamples),
|
|
38
|
+
fragmentShader: `${vertexPrelude}
|
|
39
|
+
uniform sampler2D u_input_from;
|
|
40
|
+
uniform sampler2D u_input_to;
|
|
41
|
+
uniform float u_system_transitionProgress;
|
|
42
|
+
in vec2 v_uv;
|
|
43
|
+
out vec4 out_color;
|
|
44
|
+
void main() {
|
|
45
|
+
float progress = clamp(u_system_transitionProgress, 0.0, 1.0);
|
|
46
|
+
${body}
|
|
47
|
+
}`,
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
const transitionPrograms = {
|
|
51
|
+
'diffusion-dissolve': transitionProgram('diffusion-dissolve', 'out_color = mix(texture(u_input_from, v_uv), texture(u_input_to, v_uv), progress);'),
|
|
52
|
+
'diffusion-slide-from-right': transitionProgram('diffusion-slide-from-right', `float offset = (1.0 - progress) * (1.0 - progress);
|
|
53
|
+
vec4 from_color = texture(u_input_from, v_uv);
|
|
54
|
+
vec2 to_uv = vec2(v_uv.x - offset, v_uv.y);
|
|
55
|
+
vec4 to_color = texture(u_input_to, clamp(to_uv, 0.0, 1.0));
|
|
56
|
+
out_color = v_uv.x >= offset ? to_color : from_color;`),
|
|
57
|
+
'diffusion-slide-from-left': transitionProgram('diffusion-slide-from-left', `float offset = (1.0 - progress) * (1.0 - progress);
|
|
58
|
+
vec4 from_color = texture(u_input_from, v_uv);
|
|
59
|
+
vec2 to_uv = vec2(v_uv.x + offset, v_uv.y);
|
|
60
|
+
vec4 to_color = texture(u_input_to, clamp(to_uv, 0.0, 1.0));
|
|
61
|
+
out_color = v_uv.x <= 1.0 - offset ? to_color : from_color;`),
|
|
62
|
+
'diffusion-fade-to-black': transitionProgram('diffusion-fade-to-black', `vec4 base = progress < 0.5 ? texture(u_input_from, v_uv) : texture(u_input_to, v_uv);
|
|
63
|
+
float amount = progress < 0.5 ? progress * 2.0 : (1.0 - progress) * 2.0;
|
|
64
|
+
out_color = mix(base, vec4(0.0, 0.0, 0.0, 1.0), amount);`),
|
|
65
|
+
'diffusion-fade-to-white': transitionProgram('diffusion-fade-to-white', `vec4 base = progress < 0.5 ? texture(u_input_from, v_uv) : texture(u_input_to, v_uv);
|
|
66
|
+
float amount = progress < 0.5 ? progress * 2.0 : (1.0 - progress) * 2.0;
|
|
67
|
+
out_color = mix(base, vec4(1.0), amount);`),
|
|
68
|
+
};
|
|
69
|
+
function effectProgram(id, body, parameters = ['value'], textureSamples = 1) {
|
|
70
|
+
const declarations = parameters.map(value => `uniform float u_parameter_${value};`).join('\n');
|
|
71
|
+
return {
|
|
72
|
+
backend: 'webgl2',
|
|
73
|
+
nodeSet: 'aelion.migration/1.0.0',
|
|
74
|
+
graphHash: `aelion-migration-${id}-v1`,
|
|
75
|
+
inputPorts: ['source'],
|
|
76
|
+
uniforms: parameters.map(parameter),
|
|
77
|
+
executionPlan: plan(id, textureSamples),
|
|
78
|
+
fragmentShader: `${vertexPrelude}
|
|
79
|
+
uniform sampler2D u_input_source;
|
|
80
|
+
${declarations}
|
|
81
|
+
in vec2 v_uv;
|
|
82
|
+
out vec4 out_color;
|
|
83
|
+
void main() {
|
|
84
|
+
vec4 source_color = texture(u_input_source, v_uv);
|
|
85
|
+
float amount = u_parameter_value / 100.0;
|
|
86
|
+
${body}
|
|
87
|
+
}`,
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
const effectPrograms = {
|
|
91
|
+
'diffusion-brightness': effectProgram('diffusion-brightness', 'out_color = vec4(source_color.rgb * amount, source_color.a);'),
|
|
92
|
+
'diffusion-contrast': effectProgram('diffusion-contrast', 'out_color = vec4((source_color.rgb - 0.5) * amount + 0.5, source_color.a);'),
|
|
93
|
+
'diffusion-grayscale': effectProgram('diffusion-grayscale', `float luma = dot(source_color.rgb, vec3(0.2126, 0.7152, 0.0722));
|
|
94
|
+
out_color = vec4(mix(source_color.rgb, vec3(luma), clamp(amount, 0.0, 1.0)), source_color.a);`),
|
|
95
|
+
'diffusion-hue-rotate': effectProgram('diffusion-hue-rotate', `float angle = radians(u_parameter_value);
|
|
96
|
+
float c = cos(angle);
|
|
97
|
+
float s = sin(angle);
|
|
98
|
+
mat3 hue = mat3(
|
|
99
|
+
0.213 + c * 0.787 - s * 0.213,
|
|
100
|
+
0.715 - c * 0.715 - s * 0.715,
|
|
101
|
+
0.072 - c * 0.072 + s * 0.928,
|
|
102
|
+
0.213 - c * 0.213 + s * 0.143,
|
|
103
|
+
0.715 + c * 0.285 + s * 0.140,
|
|
104
|
+
0.072 - c * 0.072 - s * 0.283,
|
|
105
|
+
0.213 - c * 0.213 - s * 0.787,
|
|
106
|
+
0.715 - c * 0.715 + s * 0.715,
|
|
107
|
+
0.072 + c * 0.928 + s * 0.072
|
|
108
|
+
);
|
|
109
|
+
out_color = vec4(clamp(hue * source_color.rgb, 0.0, 1.0), source_color.a);`),
|
|
110
|
+
'diffusion-invert': effectProgram('diffusion-invert', 'out_color = vec4(mix(source_color.rgb, 1.0 - source_color.rgb, clamp(amount, 0.0, 1.0)), source_color.a);'),
|
|
111
|
+
'diffusion-opacity': effectProgram('diffusion-opacity', 'out_color = source_color * clamp(amount, 0.0, 1.0);'),
|
|
112
|
+
'diffusion-saturate': effectProgram('diffusion-saturate', `float luma = dot(source_color.rgb, vec3(0.2126, 0.7152, 0.0722));
|
|
113
|
+
out_color = vec4(mix(vec3(luma), source_color.rgb, max(0.0, amount)), source_color.a);`),
|
|
114
|
+
'diffusion-sepia': effectProgram('diffusion-sepia', `vec3 sepia = vec3(
|
|
115
|
+
dot(source_color.rgb, vec3(0.393, 0.769, 0.189)),
|
|
116
|
+
dot(source_color.rgb, vec3(0.349, 0.686, 0.168)),
|
|
117
|
+
dot(source_color.rgb, vec3(0.272, 0.534, 0.131))
|
|
118
|
+
);
|
|
119
|
+
out_color = vec4(mix(source_color.rgb, min(sepia, 1.0), clamp(amount, 0.0, 1.0)), source_color.a);`),
|
|
120
|
+
'diffusion-blur': effectProgram('diffusion-blur', `vec2 radius = vec2(
|
|
121
|
+
max(0.0, u_parameter_value) / max(1.0, u_parameter_width),
|
|
122
|
+
max(0.0, u_parameter_value) / max(1.0, u_parameter_height)
|
|
123
|
+
);
|
|
124
|
+
vec4 sum = source_color * 0.227027;
|
|
125
|
+
sum += texture(u_input_source, v_uv + vec2(radius.x, 0.0) * 1.384615) * 0.158108;
|
|
126
|
+
sum += texture(u_input_source, v_uv - vec2(radius.x, 0.0) * 1.384615) * 0.158108;
|
|
127
|
+
sum += texture(u_input_source, v_uv + vec2(0.0, radius.y) * 1.384615) * 0.158108;
|
|
128
|
+
sum += texture(u_input_source, v_uv - vec2(0.0, radius.y) * 1.384615) * 0.158108;
|
|
129
|
+
sum += texture(u_input_source, v_uv + radius * 3.230769) * 0.035885;
|
|
130
|
+
sum += texture(u_input_source, v_uv - radius * 3.230769) * 0.035885;
|
|
131
|
+
sum += texture(u_input_source, v_uv + vec2(radius.x, -radius.y) * 3.230769) * 0.035885;
|
|
132
|
+
sum += texture(u_input_source, v_uv + vec2(-radius.x, radius.y) * 3.230769) * 0.035885;
|
|
133
|
+
out_color = sum;`, ['value', 'width', 'height'], 9),
|
|
134
|
+
};
|
|
135
|
+
function definition(materialId) {
|
|
136
|
+
return { ...migrationMaterialPackage, materialId };
|
|
137
|
+
}
|
|
138
|
+
/** Install renderable programs for every Material emitted by the Diffusion adapter. */
|
|
139
|
+
export function installMigrationMaterials(registry) {
|
|
140
|
+
const disposers = [...Object.entries(transitionPrograms), ...Object.entries(effectPrograms)].map(([materialId, program]) => registry.register(definition(materialId), program));
|
|
141
|
+
return () => {
|
|
142
|
+
for (const dispose of disposers.reverse())
|
|
143
|
+
dispose();
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
export function migrationMaterialProgram(materialId) {
|
|
147
|
+
return transitionPrograms[materialId] ?? effectPrograms[materialId];
|
|
148
|
+
}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { type Rational } from '@aelionsdk/core';
|
|
2
|
+
import type { AelionProject } from '@aelionsdk/project-schema';
|
|
3
|
+
import { type AddAssetOptions } from './project-builder.js';
|
|
4
|
+
export declare const migrationMaterialPackage: Readonly<{
|
|
5
|
+
packageId: "aelion.migration.builtin";
|
|
6
|
+
packageVersion: "1.0.0";
|
|
7
|
+
packageIntegrity: `sha256:${string}`;
|
|
8
|
+
}>;
|
|
9
|
+
export type MigrationSeverity = 'info' | 'warning' | 'error';
|
|
10
|
+
export interface MigrationDiagnostic {
|
|
11
|
+
readonly code: string;
|
|
12
|
+
readonly severity: MigrationSeverity;
|
|
13
|
+
readonly path: string;
|
|
14
|
+
readonly message: string;
|
|
15
|
+
}
|
|
16
|
+
export interface MigrationResult {
|
|
17
|
+
readonly project: Readonly<AelionProject>;
|
|
18
|
+
readonly diagnostics: readonly MigrationDiagnostic[];
|
|
19
|
+
/** Maps source object identifiers to generated Aelion entity identifiers. */
|
|
20
|
+
readonly entityMap: Readonly<Record<string, string>>;
|
|
21
|
+
}
|
|
22
|
+
export declare class ProjectMigrationError extends Error {
|
|
23
|
+
readonly diagnostics: readonly MigrationDiagnostic[];
|
|
24
|
+
readonly name = "ProjectMigrationError";
|
|
25
|
+
constructor(diagnostics: readonly MigrationDiagnostic[]);
|
|
26
|
+
}
|
|
27
|
+
export interface MigrationOptions {
|
|
28
|
+
/** Reject every rendering feature that cannot be represented exactly. Defaults to true. */
|
|
29
|
+
readonly strict?: boolean;
|
|
30
|
+
readonly projectId?: string;
|
|
31
|
+
readonly sequenceId?: string;
|
|
32
|
+
readonly title?: string;
|
|
33
|
+
readonly frameRate?: Rational;
|
|
34
|
+
}
|
|
35
|
+
export interface WebAvAssetBinding extends Omit<AddAssetOptions, 'kind'> {
|
|
36
|
+
readonly kind: 'video' | 'audio' | 'image';
|
|
37
|
+
/** Natural decoded size, required for visual rect conversion. */
|
|
38
|
+
readonly width?: number;
|
|
39
|
+
readonly height?: number;
|
|
40
|
+
}
|
|
41
|
+
export interface WebAvAnimationFrame {
|
|
42
|
+
/** Item-local time in WebAV microseconds. */
|
|
43
|
+
readonly timeUs: number;
|
|
44
|
+
readonly x?: number;
|
|
45
|
+
readonly y?: number;
|
|
46
|
+
readonly width?: number;
|
|
47
|
+
readonly height?: number;
|
|
48
|
+
/** WebAV stores rotation in radians. */
|
|
49
|
+
readonly angle?: number;
|
|
50
|
+
readonly opacity?: number;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Serializable projection of WebAV BaseSprite public state.
|
|
54
|
+
*
|
|
55
|
+
* WebAV deliberately keeps the IClip and its bytes private on OffscreenSprite,
|
|
56
|
+
* so callers must bind the source Asset explicitly instead of the adapter
|
|
57
|
+
* pretending it can recover source bytes from a Sprite.
|
|
58
|
+
*/
|
|
59
|
+
export interface WebAvSpriteSnapshot {
|
|
60
|
+
readonly id: string;
|
|
61
|
+
readonly kind: 'video' | 'audio' | 'image';
|
|
62
|
+
readonly assetId: string;
|
|
63
|
+
readonly time: {
|
|
64
|
+
readonly offset: number;
|
|
65
|
+
readonly duration: number;
|
|
66
|
+
readonly playbackRate?: number;
|
|
67
|
+
};
|
|
68
|
+
readonly rect?: {
|
|
69
|
+
readonly x: number;
|
|
70
|
+
readonly y: number;
|
|
71
|
+
readonly w: number;
|
|
72
|
+
readonly h: number;
|
|
73
|
+
readonly angle?: number;
|
|
74
|
+
};
|
|
75
|
+
readonly zIndex?: number;
|
|
76
|
+
readonly opacity?: number;
|
|
77
|
+
readonly flip?: 'horizontal' | 'vertical' | null;
|
|
78
|
+
readonly visible?: boolean;
|
|
79
|
+
readonly sourceStartUs?: number;
|
|
80
|
+
/** Set when the WebAV IClip produces audio as well as video. */
|
|
81
|
+
readonly includeAudio?: boolean;
|
|
82
|
+
/** WebAV's private animation state must be projected explicitly by the caller. */
|
|
83
|
+
readonly animation?: readonly WebAvAnimationFrame[];
|
|
84
|
+
}
|
|
85
|
+
export interface WebAvProjectSnapshot {
|
|
86
|
+
readonly width: number;
|
|
87
|
+
readonly height: number;
|
|
88
|
+
readonly backgroundColor?: string;
|
|
89
|
+
readonly assets: readonly WebAvAssetBinding[];
|
|
90
|
+
readonly sprites: readonly WebAvSpriteSnapshot[];
|
|
91
|
+
}
|
|
92
|
+
export interface DiffusionAssetBinding extends Omit<AddAssetOptions, 'id' | 'kind'> {
|
|
93
|
+
/** Diffusion BaseSource.id used by checkpoint Clip.source. */
|
|
94
|
+
readonly sourceId: string;
|
|
95
|
+
readonly assetId: string;
|
|
96
|
+
readonly kind: AddAssetOptions['kind'];
|
|
97
|
+
readonly width?: number;
|
|
98
|
+
readonly height?: number;
|
|
99
|
+
/** Whether a VIDEO source also exposes an audio track. */
|
|
100
|
+
readonly hasAudio?: boolean;
|
|
101
|
+
/** Required to turn a Diffusion CaptionSource into an inline Aelion Caption. */
|
|
102
|
+
readonly captionText?: string;
|
|
103
|
+
}
|
|
104
|
+
export interface DiffusionMigrationOptions extends MigrationOptions {
|
|
105
|
+
readonly assets: readonly DiffusionAssetBinding[];
|
|
106
|
+
}
|
|
107
|
+
export declare function migrateWebAvProject(snapshot: WebAvProjectSnapshot, options?: MigrationOptions): MigrationResult;
|
|
108
|
+
export declare function migrateDiffusionCheckpoint(value: unknown, options: DiffusionMigrationOptions): MigrationResult;
|
|
109
|
+
//# sourceMappingURL=migration.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"migration.d.ts","sourceRoot":"","sources":["../src/migration.ts"],"names":[],"mappings":"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"}
|