@cutcraft/player 0.0.0-stage → 0.1.0
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 +102 -2
- package/dist/backdrop-BqrzCzTB.d.mts +131 -0
- package/dist/engine.d.mts +3 -0
- package/dist/engine.mjs +3 -0
- package/dist/frame-plan-BNwblf1s.mjs +1074 -0
- package/dist/index.d.mts +298 -0
- package/dist/index.mjs +1410 -0
- package/dist/layer-interiors-r4SeMbp2.d.mts +8 -0
- package/dist/nested-Bd4Y7qrF.d.mts +720 -0
- package/dist/nested-DSXA1q8d.mjs +1698 -0
- package/dist/overlays.d.mts +201 -0
- package/dist/overlays.mjs +3 -0
- package/dist/svg-element-BUXbz-U5.mjs +1736 -0
- package/fonts.css +23 -0
- package/package.json +67 -3
|
@@ -0,0 +1,720 @@
|
|
|
1
|
+
import { d as DrawRect, m as Matrix2D, r as ClipRect } from "./backdrop-BqrzCzTB.mjs";
|
|
2
|
+
import { AssetEntry, AssetManifest, EASING_FN, FitMode, Placement, ResolvedState, ResolvedTrack } from "@cutcraft/kit";
|
|
3
|
+
import { Input, VideoSample } from "mediabunny";
|
|
4
|
+
/** Seconds before a clip starts at which its decoder is mounted. */
|
|
5
|
+
declare const MOUNT_AHEAD_S = 2;
|
|
6
|
+
/** Seconds after a clip ends for which its decoder is kept. */
|
|
7
|
+
declare const UNMOUNT_BEHIND_S = 0.5;
|
|
8
|
+
/** Seconds an unreferenced mediabunny Input stays open before it is disposed. */
|
|
9
|
+
declare const INPUT_IDLE_CLOSE_S = 10;
|
|
10
|
+
/** Hard cap on open Inputs; idle ones are evicted LRU first. */
|
|
11
|
+
declare const MAX_OPEN_INPUTS = 6;
|
|
12
|
+
/** Decoded frames a clip decoder keeps queued ahead of the play head. */
|
|
13
|
+
declare const QUEUE_TARGET = 8;
|
|
14
|
+
declare const QUEUE_MAX = 10;
|
|
15
|
+
/** Effective speed (clip speed × rate) above which frames are fetched by seek per tick instead of streamed. */
|
|
16
|
+
declare const SEEK_MODE_SPEED = 4;
|
|
17
|
+
/** Reverse playback pre-decodes this window behind the cursor. */
|
|
18
|
+
declare const REVERSE_WINDOW_S = 1;
|
|
19
|
+
declare const REVERSE_WINDOW_FRAMES = 16;
|
|
20
|
+
/** Longest wait for a frame in `wait` mode (seek, export) before drawing without it. */
|
|
21
|
+
declare const FRAME_WAIT_MS = 250;
|
|
22
|
+
/** Audio scheduler period and horizon. */
|
|
23
|
+
declare const SCHEDULER_MS = 100;
|
|
24
|
+
declare const LOOKAHEAD_S = 0.2;
|
|
25
|
+
/** Source ranges longer than twice this are decoded in chunks of this length. */
|
|
26
|
+
declare const AUDIO_CHUNK_S = 30;
|
|
27
|
+
/** LRU cap, in decoded seconds, on cached PCM. */
|
|
28
|
+
declare const AUDIO_CACHE_S = 600;
|
|
29
|
+
/** Pre-decode window on play, and how long play() waits for it. */
|
|
30
|
+
declare const AUDIO_PRIME_S = 0.5;
|
|
31
|
+
declare const PLAY_PRIME_TIMEOUT_MS = 500;
|
|
32
|
+
/** Ducking defaults. */
|
|
33
|
+
declare const DUCK_ATTACK_S = 0.1;
|
|
34
|
+
declare const DUCK_RELEASE_S = 0.3;
|
|
35
|
+
/** Sampling step for multiplied gain ramps. */
|
|
36
|
+
declare const ENVELOPE_STEP_S = 0.05;
|
|
37
|
+
/** Playback rate clamp. */
|
|
38
|
+
declare const MIN_RATE = 0.25;
|
|
39
|
+
declare const MAX_RATE = 4;
|
|
40
|
+
/** Quiet time after the last setState before a paused preview fetches its exact frame. */
|
|
41
|
+
declare const SETTLE_MS = 120;
|
|
42
|
+
/** Tolerance when rounding a time to a frame index. */
|
|
43
|
+
declare const FRAME_EPS = 0.000001;
|
|
44
|
+
/**
|
|
45
|
+
* Container timestamps are often rounded to the millisecond, so the frame for
|
|
46
|
+
* the exact time n/fps can sit a fraction of a millisecond after it. A frame
|
|
47
|
+
* is picked when its timestamp is at most this far past the wanted time.
|
|
48
|
+
*/
|
|
49
|
+
declare const PICK_EPS = 0.001;
|
|
50
|
+
/** Warn once per key. Tests capture warnings with `captureWarnings`. */
|
|
51
|
+
declare function devWarn(key: string, message: string): void;
|
|
52
|
+
declare function captureWarnings(fn: ((message: string) => void) | null): void;
|
|
53
|
+
interface PlayerErrorEvent {
|
|
54
|
+
readonly id?: string;
|
|
55
|
+
readonly asset?: string;
|
|
56
|
+
readonly error: Error;
|
|
57
|
+
readonly fatal: boolean;
|
|
58
|
+
}
|
|
59
|
+
interface PlayerEvents {
|
|
60
|
+
frameupdate: {
|
|
61
|
+
readonly time: number;
|
|
62
|
+
readonly frame: number;
|
|
63
|
+
};
|
|
64
|
+
play: {
|
|
65
|
+
readonly time: number;
|
|
66
|
+
};
|
|
67
|
+
pause: {
|
|
68
|
+
readonly time: number;
|
|
69
|
+
};
|
|
70
|
+
seeked: {
|
|
71
|
+
readonly time: number;
|
|
72
|
+
};
|
|
73
|
+
ended: {
|
|
74
|
+
readonly time: number;
|
|
75
|
+
};
|
|
76
|
+
ratechange: {
|
|
77
|
+
readonly rate: number;
|
|
78
|
+
};
|
|
79
|
+
volumechange: {
|
|
80
|
+
readonly volume: number;
|
|
81
|
+
readonly muted: boolean;
|
|
82
|
+
};
|
|
83
|
+
error: PlayerErrorEvent;
|
|
84
|
+
}
|
|
85
|
+
type Ctx2D = CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D;
|
|
86
|
+
type AnyCanvas = HTMLCanvasElement | OffscreenCanvas;
|
|
87
|
+
type CreateCanvas = (width: number, height: number) => AnyCanvas;
|
|
88
|
+
/** Where an asset's bytes come from. `path` is a file on disk, for the Node renderer; browsers use `blob` or `url`. */
|
|
89
|
+
type MediaHandle = {
|
|
90
|
+
readonly kind: "blob";
|
|
91
|
+
readonly blob: Blob;
|
|
92
|
+
} | {
|
|
93
|
+
readonly kind: "url";
|
|
94
|
+
readonly url: string;
|
|
95
|
+
} | {
|
|
96
|
+
readonly kind: "path";
|
|
97
|
+
readonly path: string;
|
|
98
|
+
};
|
|
99
|
+
interface MediaResolver {
|
|
100
|
+
resolve(assetId: string): Promise<MediaHandle>;
|
|
101
|
+
}
|
|
102
|
+
interface FrameSourceInfo {
|
|
103
|
+
readonly width: number;
|
|
104
|
+
readonly height: number;
|
|
105
|
+
readonly duration: number;
|
|
106
|
+
readonly fps?: number;
|
|
107
|
+
readonly decodable: boolean;
|
|
108
|
+
}
|
|
109
|
+
/** One decoded picture. Frames handed out by a source belong to the caller, who closes them. */
|
|
110
|
+
interface Frame {
|
|
111
|
+
readonly timestamp: number;
|
|
112
|
+
readonly duration: number;
|
|
113
|
+
readonly width: number;
|
|
114
|
+
readonly height: number;
|
|
115
|
+
draw(ctx: Ctx2D, dx: number, dy: number, dw: number, dh: number, crop?: {
|
|
116
|
+
x: number;
|
|
117
|
+
y: number;
|
|
118
|
+
w: number;
|
|
119
|
+
h: number;
|
|
120
|
+
}): void;
|
|
121
|
+
/**
|
|
122
|
+
* Optional fast path for a picture that covers the whole `width`×`height` canvas, untransformed, opaque and
|
|
123
|
+
* normally blended: replace the canvas content with it and return true, or return false to be drawn with `draw`.
|
|
124
|
+
*/
|
|
125
|
+
paintFull?(ctx: Ctx2D, width: number, height: number): boolean;
|
|
126
|
+
/**
|
|
127
|
+
* Makes the picture drawable, for a frame that defers work until it is drawn (the Node renderer converts decoded
|
|
128
|
+
* pictures to RGBA only then). The compositor awaits it before painting the frame.
|
|
129
|
+
*/
|
|
130
|
+
prepare?(): Promise<void>;
|
|
131
|
+
/** A copy that outlives the source's pools and the original's `close()`. */
|
|
132
|
+
retain(): Frame;
|
|
133
|
+
close(): void;
|
|
134
|
+
}
|
|
135
|
+
interface FrameSource {
|
|
136
|
+
open(): Promise<FrameSourceInfo>;
|
|
137
|
+
/** The last frame at or before `sourceTime`, or null before the first frame. */
|
|
138
|
+
frameAt(sourceTime: number): Promise<Frame | null>;
|
|
139
|
+
/** Frames from `from` (the frame covering it first) up to `to`, in order. */
|
|
140
|
+
stream(from: number, to: number): AsyncGenerator<Frame, void, unknown>;
|
|
141
|
+
close(): void;
|
|
142
|
+
}
|
|
143
|
+
type FrameSourceFactory = (assetId: string, handle: MediaHandle, kind: AssetEntry["kind"]) => FrameSource;
|
|
144
|
+
interface AudioSourceInfo {
|
|
145
|
+
readonly sampleRate: number;
|
|
146
|
+
readonly channels: number;
|
|
147
|
+
readonly duration: number;
|
|
148
|
+
readonly decodable: boolean;
|
|
149
|
+
}
|
|
150
|
+
interface AudioSource {
|
|
151
|
+
open(): Promise<AudioSourceInfo>;
|
|
152
|
+
/** PCM for [from, to) as one buffer whose first sample is `from`. */
|
|
153
|
+
range(from: number, to: number): Promise<AudioBuffer>;
|
|
154
|
+
close(): void;
|
|
155
|
+
}
|
|
156
|
+
type AudioSourceFactory = (assetId: string, handle: MediaHandle, context: BaseAudioContext) => AudioSource;
|
|
157
|
+
interface Clock {
|
|
158
|
+
/** Monotonic seconds. */
|
|
159
|
+
now(): number;
|
|
160
|
+
}
|
|
161
|
+
interface EnvelopePoint {
|
|
162
|
+
/** Audio context seconds. */
|
|
163
|
+
readonly at: number;
|
|
164
|
+
readonly value: number;
|
|
165
|
+
}
|
|
166
|
+
interface ScheduledSource {
|
|
167
|
+
/** `${placementId}#${chunk}#${session}`. */
|
|
168
|
+
readonly key: string;
|
|
169
|
+
readonly buffer: AudioBuffer;
|
|
170
|
+
/** Audio context seconds. */
|
|
171
|
+
readonly when: number;
|
|
172
|
+
readonly offset: number;
|
|
173
|
+
readonly duration: number;
|
|
174
|
+
/** The rate at `when`; `rate`, when set, automates it from there. */
|
|
175
|
+
readonly playbackRate: number;
|
|
176
|
+
readonly rate?: readonly EnvelopePoint[];
|
|
177
|
+
readonly envelope: readonly EnvelopePoint[];
|
|
178
|
+
readonly pan?: readonly EnvelopePoint[];
|
|
179
|
+
}
|
|
180
|
+
interface AudioOutput {
|
|
181
|
+
readonly contextTime: number;
|
|
182
|
+
readonly running: boolean;
|
|
183
|
+
schedule(src: ScheduledSource): void;
|
|
184
|
+
/** Stop and disconnect every source whose key starts with `keyPrefix`. */
|
|
185
|
+
cancel(keyPrefix: string): void;
|
|
186
|
+
cancelAll(): void;
|
|
187
|
+
setMaster(gain: number): void;
|
|
188
|
+
resume(): Promise<void>;
|
|
189
|
+
}
|
|
190
|
+
declare function ease(name: string | undefined, x: number): number;
|
|
191
|
+
/**
|
|
192
|
+
* The keyframed value of `property` at element-relative time `rel`. Holds
|
|
193
|
+
* before the first and after the last key; the segment between two keys uses
|
|
194
|
+
* the easing of the key it leaves. Non-numeric values step.
|
|
195
|
+
*/
|
|
196
|
+
declare function keyframedValue<T>(children: readonly Placement[], property: string, rel: number, fallback: T): T;
|
|
197
|
+
declare function hasKeyframes(children: readonly Placement[], property: string): boolean;
|
|
198
|
+
interface FilterResult {
|
|
199
|
+
readonly filter: string;
|
|
200
|
+
readonly unsupported: readonly string[];
|
|
201
|
+
}
|
|
202
|
+
/** A single canvas/CSS `filter` string for an element's effect and colour-grade children at element-relative `rel`; unsupported types are listed. */
|
|
203
|
+
declare function filterString(children: readonly Placement[], elementId: string, rel: number): FilterResult;
|
|
204
|
+
type PostEffect = {
|
|
205
|
+
readonly type: "vignette";
|
|
206
|
+
readonly amount: number;
|
|
207
|
+
} | {
|
|
208
|
+
readonly type: "grain";
|
|
209
|
+
readonly amount: number;
|
|
210
|
+
readonly seed: number;
|
|
211
|
+
};
|
|
212
|
+
/** Vignette and grain, in order, with `amount` on 0..1; grain is seeded by the frame, so a frame always looks the same. */
|
|
213
|
+
declare function postEffects(children: readonly Placement[], frame: number, rel: number): PostEffect[] | undefined;
|
|
214
|
+
interface MaskSpec {
|
|
215
|
+
readonly shape?: Record<string, unknown> | undefined;
|
|
216
|
+
readonly path?: {
|
|
217
|
+
points: {
|
|
218
|
+
x: number;
|
|
219
|
+
y: number;
|
|
220
|
+
handleIn?: {
|
|
221
|
+
x: number;
|
|
222
|
+
y: number;
|
|
223
|
+
};
|
|
224
|
+
handleOut?: {
|
|
225
|
+
x: number;
|
|
226
|
+
y: number;
|
|
227
|
+
};
|
|
228
|
+
}[];
|
|
229
|
+
closed: boolean;
|
|
230
|
+
} | undefined;
|
|
231
|
+
readonly feather: number;
|
|
232
|
+
readonly inverted: boolean;
|
|
233
|
+
readonly opacity: number;
|
|
234
|
+
}
|
|
235
|
+
interface DrawOp {
|
|
236
|
+
readonly id: string;
|
|
237
|
+
readonly kind: "video" | "image" | "nested" | "error" | "adjustment";
|
|
238
|
+
readonly asset?: string;
|
|
239
|
+
readonly sourceTime?: number;
|
|
240
|
+
readonly nested?: {
|
|
241
|
+
readonly state: ResolvedState;
|
|
242
|
+
readonly time: number;
|
|
243
|
+
};
|
|
244
|
+
readonly rect: DrawRect;
|
|
245
|
+
readonly matrix: Matrix2D;
|
|
246
|
+
readonly crop?: DrawRect;
|
|
247
|
+
/** Dimensions used to express crop pixels; decoded proxies can have another resolution. */
|
|
248
|
+
readonly cropReference?: {
|
|
249
|
+
readonly width: number;
|
|
250
|
+
readonly height: number;
|
|
251
|
+
};
|
|
252
|
+
readonly fit: FitMode;
|
|
253
|
+
readonly opacity: number;
|
|
254
|
+
readonly blend: GlobalCompositeOperation;
|
|
255
|
+
readonly filter: string;
|
|
256
|
+
readonly borderRadius: number;
|
|
257
|
+
readonly mask?: MaskSpec;
|
|
258
|
+
/** The part shown, in composition pixels (a wipe). */
|
|
259
|
+
readonly clip?: readonly ClipRect[];
|
|
260
|
+
/** Vignette and grain, drawn over the element's own pixels. */
|
|
261
|
+
readonly post?: readonly PostEffect[];
|
|
262
|
+
}
|
|
263
|
+
/** A composition background gradient, as the schema writes it. CSS angles: 0 points up, 180 (the default) runs down. */
|
|
264
|
+
interface BackgroundGradient {
|
|
265
|
+
readonly type: "linear" | "radial" | "conic";
|
|
266
|
+
readonly angle?: number;
|
|
267
|
+
readonly center?: {
|
|
268
|
+
readonly x: number;
|
|
269
|
+
readonly y: number;
|
|
270
|
+
};
|
|
271
|
+
readonly stops: readonly {
|
|
272
|
+
readonly offset: number;
|
|
273
|
+
readonly color: string;
|
|
274
|
+
readonly opacity?: number;
|
|
275
|
+
}[];
|
|
276
|
+
}
|
|
277
|
+
interface FramePlan {
|
|
278
|
+
readonly time: number;
|
|
279
|
+
readonly frame: number;
|
|
280
|
+
readonly background: string;
|
|
281
|
+
/** A gradient that fills the frame instead of `background` (which is then its first colour). */
|
|
282
|
+
readonly backgroundGradient?: BackgroundGradient;
|
|
283
|
+
/** The footage goes over a transparent ground: overlays beneath it (see `backdropTracks`) and the background are drawn under the canvas. */
|
|
284
|
+
readonly clear?: true;
|
|
285
|
+
/** `background.mode: "blur"`: this op's picture, blurred to cover the frame, goes under everything. */
|
|
286
|
+
readonly backgroundBlur?: {
|
|
287
|
+
readonly id: string;
|
|
288
|
+
};
|
|
289
|
+
readonly ops: readonly DrawOp[];
|
|
290
|
+
readonly warnings: readonly string[];
|
|
291
|
+
}
|
|
292
|
+
type ResolveNested = (p: Placement) => ResolvedState | null;
|
|
293
|
+
declare function blendOf(value: unknown): GlobalCompositeOperation;
|
|
294
|
+
/**
|
|
295
|
+
* Everything the painter needs to draw composition time `t`, in draw order.
|
|
296
|
+
* Pure: the same state and time give a deep-equal plan. DOM-rendered kinds
|
|
297
|
+
* (title, shape, svg, sticker, layer, captions) are not in it; the DOM layer
|
|
298
|
+
* reads `visiblePlacements` itself.
|
|
299
|
+
*/
|
|
300
|
+
declare function buildFramePlan(state: ResolvedState, t: number, resolveNested?: ResolveNested): FramePlan;
|
|
301
|
+
/** The composition's background as the painter draws it: a colour, or a gradient (whose first colour is `background`). */
|
|
302
|
+
declare function compositionBackground(state: ResolvedState): {
|
|
303
|
+
background: string;
|
|
304
|
+
backgroundGradient?: BackgroundGradient;
|
|
305
|
+
blur: boolean;
|
|
306
|
+
};
|
|
307
|
+
/** A gain breakpoint in composition seconds. */
|
|
308
|
+
type GainPoint = readonly [t: number, value: number];
|
|
309
|
+
interface AudioOccurrence {
|
|
310
|
+
readonly id: string;
|
|
311
|
+
readonly asset: string;
|
|
312
|
+
readonly chunk: number;
|
|
313
|
+
/** Composition range of this chunk, unclipped; the engine clips to "now". */
|
|
314
|
+
readonly compStart: number;
|
|
315
|
+
readonly compEnd: number;
|
|
316
|
+
/** Source seconds of the chunk's buffer; stable across windows so decoded PCM caches. */
|
|
317
|
+
readonly bufferFrom: number;
|
|
318
|
+
readonly bufferTo: number;
|
|
319
|
+
/** Placement speed; the engine multiplies in the transport rate. Ignored when `rate` is set. */
|
|
320
|
+
readonly speed: number;
|
|
321
|
+
/** Speed-key ramp: buffer seconds per composition second, piecewise linear over [compStart, compEnd]. */
|
|
322
|
+
readonly rate?: readonly GainPoint[];
|
|
323
|
+
readonly gain: readonly GainPoint[];
|
|
324
|
+
readonly pan?: readonly GainPoint[];
|
|
325
|
+
/** The buffer plays backwards: the engine reverses the decoded PCM. */
|
|
326
|
+
readonly reverse?: true;
|
|
327
|
+
}
|
|
328
|
+
/** Every audio-bearing chunk that overlaps [from, to). */
|
|
329
|
+
declare function buildAudioPlan(state: ResolvedState, from: number, to: number, resolveNested?: ResolveNested): AudioOccurrence[];
|
|
330
|
+
/**
|
|
331
|
+
* Gain over [winFrom, winTo] in composition seconds: volume × keyframed
|
|
332
|
+
* `audio.volume` × fades × automation × ducking. The factors are multiplied
|
|
333
|
+
* and sampled at their breakpoints plus a fixed step inside ramps, so the
|
|
334
|
+
* product stays close to piecewise linear.
|
|
335
|
+
*/
|
|
336
|
+
declare function buildGainEnvelope(p: Placement, track: ResolvedTrack, state: ResolvedState, winFrom: number, winTo: number): GainPoint[];
|
|
337
|
+
/** Composition intervals where sound plays on the track `under` names by id, else on the tracks of that role; silent assets and muted clips do not duck. */
|
|
338
|
+
declare function duckIntervals(state: ResolvedState, under: string, exceptId: string): [number, number][];
|
|
339
|
+
declare class ManualClock implements Clock {
|
|
340
|
+
private t;
|
|
341
|
+
now(): number;
|
|
342
|
+
advance(dt: number): void;
|
|
343
|
+
set(t: number): void;
|
|
344
|
+
}
|
|
345
|
+
declare class PerformanceClock implements Clock {
|
|
346
|
+
now(): number;
|
|
347
|
+
}
|
|
348
|
+
/** A resolver over a map of asset id → Blob or URL. */
|
|
349
|
+
declare function fromMap(map: Readonly<Record<string, Blob | string>>): MediaResolver;
|
|
350
|
+
declare function toResolver(media: MediaResolver | Readonly<Record<string, Blob | string>>): MediaResolver;
|
|
351
|
+
declare function defaultCreateCanvas(width: number, height: number): AnyCanvas;
|
|
352
|
+
type Crop$1 = {
|
|
353
|
+
x: number;
|
|
354
|
+
y: number;
|
|
355
|
+
w: number;
|
|
356
|
+
h: number;
|
|
357
|
+
};
|
|
358
|
+
/** A frame backed by an image we own (canvas or bitmap); `retain` is free. */
|
|
359
|
+
declare class ImageFrame implements Frame {
|
|
360
|
+
private readonly image;
|
|
361
|
+
readonly timestamp: number;
|
|
362
|
+
readonly duration: number;
|
|
363
|
+
private readonly owned;
|
|
364
|
+
constructor(image: CanvasImageSource & {
|
|
365
|
+
width: number;
|
|
366
|
+
height: number;
|
|
367
|
+
}, timestamp: number, duration: number, owned: boolean);
|
|
368
|
+
get width(): number;
|
|
369
|
+
get height(): number;
|
|
370
|
+
draw(ctx: Ctx2D, dx: number, dy: number, dw: number, dh: number, crop?: Crop$1): void;
|
|
371
|
+
retain(): Frame;
|
|
372
|
+
close(): void;
|
|
373
|
+
}
|
|
374
|
+
/** Copies any frame into a canvas we own. */
|
|
375
|
+
declare function copyFrame(frame: Frame, createCanvas: CreateCanvas): Frame;
|
|
376
|
+
/** Still images: one bitmap, returned for every time. */
|
|
377
|
+
declare class ImageFrameSource implements FrameSource {
|
|
378
|
+
private readonly load;
|
|
379
|
+
private bitmap;
|
|
380
|
+
constructor(load: () => Promise<ImageBitmap | HTMLImageElement>);
|
|
381
|
+
open(): Promise<FrameSourceInfo>;
|
|
382
|
+
frameAt(): Promise<Frame | null>;
|
|
383
|
+
stream(): AsyncGenerator<Frame, void, unknown>;
|
|
384
|
+
close(): void;
|
|
385
|
+
private frame;
|
|
386
|
+
}
|
|
387
|
+
/** Draws a grey box with a label; used for missing or undecodable assets. */
|
|
388
|
+
declare class ErrorFrame implements Frame {
|
|
389
|
+
readonly width: number;
|
|
390
|
+
readonly height: number;
|
|
391
|
+
private readonly label;
|
|
392
|
+
readonly timestamp = 0;
|
|
393
|
+
readonly duration: number;
|
|
394
|
+
constructor(width: number, height: number, label: string);
|
|
395
|
+
draw(ctx: Ctx2D, dx: number, dy: number, dw: number, dh: number): void;
|
|
396
|
+
retain(): Frame;
|
|
397
|
+
close(): void;
|
|
398
|
+
}
|
|
399
|
+
declare class ErrorFrameSource implements FrameSource {
|
|
400
|
+
private readonly frame;
|
|
401
|
+
constructor(label: string, width?: number, height?: number);
|
|
402
|
+
open(): Promise<FrameSourceInfo>;
|
|
403
|
+
frameAt(): Promise<Frame | null>;
|
|
404
|
+
stream(): AsyncGenerator<Frame, void, unknown>;
|
|
405
|
+
close(): void;
|
|
406
|
+
}
|
|
407
|
+
interface AssetSourceDeps {
|
|
408
|
+
readonly media: MediaResolver;
|
|
409
|
+
readonly manifest: AssetManifest;
|
|
410
|
+
readonly frameSourceFactory: FrameSourceFactory;
|
|
411
|
+
readonly clock: Clock;
|
|
412
|
+
readonly onError: (e: PlayerErrorEvent) => void;
|
|
413
|
+
}
|
|
414
|
+
/**
|
|
415
|
+
* One FrameSource per asset, shared by every clip of that asset and by nested
|
|
416
|
+
* compositors. Ref-counted; an unreferenced source stays open for a while
|
|
417
|
+
* (a seek back is cheap) and is closed when idle too long or when too many
|
|
418
|
+
* are open.
|
|
419
|
+
*/
|
|
420
|
+
declare class AssetSources {
|
|
421
|
+
private readonly deps;
|
|
422
|
+
private readonly entries;
|
|
423
|
+
private readonly errored;
|
|
424
|
+
constructor(deps: AssetSourceDeps);
|
|
425
|
+
/** Acquire a reference; the source opens lazily on first use. */
|
|
426
|
+
acquire(assetId: string): {
|
|
427
|
+
source: FrameSource;
|
|
428
|
+
info: Promise<FrameSourceInfo>;
|
|
429
|
+
};
|
|
430
|
+
release(assetId: string): void;
|
|
431
|
+
/** Close sources idle for too long, then the least recently used idle ones beyond the cap. */
|
|
432
|
+
sweep(): void;
|
|
433
|
+
get openCount(): number;
|
|
434
|
+
isOpen(assetId: string): boolean;
|
|
435
|
+
dispose(): void;
|
|
436
|
+
private close;
|
|
437
|
+
private open;
|
|
438
|
+
}
|
|
439
|
+
type DecodeMode = "forward" | "reverse" | "seek";
|
|
440
|
+
/**
|
|
441
|
+
* Decoded frames for one placement. Forward playback streams from the source
|
|
442
|
+
* into a short queue; reverse pre-decodes windows behind the cursor; seek
|
|
443
|
+
* mode (high speeds, scrubbing) fetches single frames. Freezes hold one
|
|
444
|
+
* retained frame. The decoder never blocks a playing tick: when the frame for
|
|
445
|
+
* `t` is not ready it returns the last frame it delivered.
|
|
446
|
+
*/
|
|
447
|
+
declare class ClipDecoder {
|
|
448
|
+
private readonly source;
|
|
449
|
+
private readonly effectiveSpeed;
|
|
450
|
+
readonly id: string;
|
|
451
|
+
readonly sourceKey: string;
|
|
452
|
+
private queue;
|
|
453
|
+
private current;
|
|
454
|
+
private generation;
|
|
455
|
+
private gen;
|
|
456
|
+
private pumping;
|
|
457
|
+
private streamEnd;
|
|
458
|
+
private seekPending;
|
|
459
|
+
private reverseLow;
|
|
460
|
+
private reverseLoading;
|
|
461
|
+
private readonly freezes;
|
|
462
|
+
private disposed;
|
|
463
|
+
private primed;
|
|
464
|
+
private _placement;
|
|
465
|
+
private _children;
|
|
466
|
+
constructor(source: FrameSource, placement: Placement, children: readonly Placement[], effectiveSpeed: () => number);
|
|
467
|
+
/** Asset and direction: everything else (trim, speed, freezes) may change under a live decoder. */
|
|
468
|
+
static sourceKey(p: Placement): string;
|
|
469
|
+
get placement(): Placement;
|
|
470
|
+
get children(): readonly Placement[];
|
|
471
|
+
/**
|
|
472
|
+
* Take a new resolve of the same placement. A trim or speed change moves
|
|
473
|
+
* where source time lands; the queue is kept and the next `frameFor` reseeks
|
|
474
|
+
* only if the new source time falls outside what is queued.
|
|
475
|
+
*/
|
|
476
|
+
update(placement: Placement, children: readonly Placement[]): void;
|
|
477
|
+
get mode(): DecodeMode;
|
|
478
|
+
get queued(): number;
|
|
479
|
+
get ready(): Promise<void>;
|
|
480
|
+
sourceTime(t: number): number;
|
|
481
|
+
/** Jump to composition time `t`: cancel streaming, fetch the frame, restart from there. */
|
|
482
|
+
prime(t: number): Promise<void>;
|
|
483
|
+
/**
|
|
484
|
+
* The frame to draw at composition time `t`. With `wait`, fetch it if it is
|
|
485
|
+
* not queued, for at most `FRAME_WAIT_MS`; with `exact` as well, for as long
|
|
486
|
+
* as it takes, so an export never draws a frame it did not wait for.
|
|
487
|
+
*/
|
|
488
|
+
frameFor(t: number, wait: boolean, exact?: boolean): Promise<Frame | null>;
|
|
489
|
+
/** Close consumed frames; called after a frame was painted. */
|
|
490
|
+
advance(t: number): void;
|
|
491
|
+
dispose(): void;
|
|
492
|
+
private forwardFrame;
|
|
493
|
+
/** The newest queued frame at or before `st` becomes current; older ones are consumed. */
|
|
494
|
+
private forwardPick;
|
|
495
|
+
private primeAtSource;
|
|
496
|
+
private startStream;
|
|
497
|
+
private pump;
|
|
498
|
+
/** Pull frames until the queue is at target; a caller that finds a pump running awaits that one. */
|
|
499
|
+
private pumpOnce;
|
|
500
|
+
private seekFrame;
|
|
501
|
+
private reverseFrame;
|
|
502
|
+
private loadReverseWindow;
|
|
503
|
+
private activeFreeze;
|
|
504
|
+
private freezeFrame;
|
|
505
|
+
private setCurrent;
|
|
506
|
+
private cancelStream;
|
|
507
|
+
}
|
|
508
|
+
/**
|
|
509
|
+
* Keeps a ClipDecoder for every media placement near the play head and drops
|
|
510
|
+
* the rest. Asset sources are shared through `AssetSources`, so nested
|
|
511
|
+
* compositors handed the same registry never open an asset twice.
|
|
512
|
+
*/
|
|
513
|
+
declare class DecoderPool {
|
|
514
|
+
readonly sources: AssetSources;
|
|
515
|
+
private readonly decoders;
|
|
516
|
+
private rate;
|
|
517
|
+
constructor(sources: AssetSources);
|
|
518
|
+
setRate(rate: number): void;
|
|
519
|
+
/** Mount decoders for placements within the window around `t`; unmount the rest. */
|
|
520
|
+
sync(state: ResolvedState, t: number): void;
|
|
521
|
+
decoderFor(id: string): ClipDecoder | undefined;
|
|
522
|
+
get mounted(): string[];
|
|
523
|
+
/** After a seek: every decoder jumps to `t`. */
|
|
524
|
+
seekAll(t: number): Promise<void>;
|
|
525
|
+
/** Resolves when every decoder has its first frame. */
|
|
526
|
+
settle(): Promise<void>;
|
|
527
|
+
dispose(): void;
|
|
528
|
+
private unmount;
|
|
529
|
+
}
|
|
530
|
+
/** One mediabunny `Input` per asset, shared by the video and audio sources of that asset. */
|
|
531
|
+
declare class MediabunnyInputs {
|
|
532
|
+
private readonly entries;
|
|
533
|
+
acquire(assetId: string, handle: MediaHandle): Input;
|
|
534
|
+
release(assetId: string): void;
|
|
535
|
+
get openCount(): number;
|
|
536
|
+
dispose(): void;
|
|
537
|
+
}
|
|
538
|
+
type Crop = {
|
|
539
|
+
x: number;
|
|
540
|
+
y: number;
|
|
541
|
+
w: number;
|
|
542
|
+
h: number;
|
|
543
|
+
};
|
|
544
|
+
/** A decoded `VideoSample`: GPU-backed, drawn straight to the context, closed when consumed. */
|
|
545
|
+
declare class SampleFrame implements Frame {
|
|
546
|
+
private readonly sample;
|
|
547
|
+
private readonly createCanvas;
|
|
548
|
+
readonly timestamp: number;
|
|
549
|
+
readonly duration: number;
|
|
550
|
+
readonly width: number;
|
|
551
|
+
readonly height: number;
|
|
552
|
+
constructor(sample: VideoSample, createCanvas: CreateCanvas);
|
|
553
|
+
draw(ctx: Ctx2D, dx: number, dy: number, dw: number, dh: number, crop?: Crop): void;
|
|
554
|
+
retain(): Frame;
|
|
555
|
+
close(): void;
|
|
556
|
+
}
|
|
557
|
+
/** How a host customises decoding: the Node renderer converts samples itself and decodes in software. */
|
|
558
|
+
interface FrameSourceHooks {
|
|
559
|
+
/** Turns a decoded sample into a frame; may convert eagerly (the sample is closed by the frame). */
|
|
560
|
+
readonly makeFrame?: (sample: VideoSample) => Promise<Frame> | Frame;
|
|
561
|
+
/** Loads a still image asset. */
|
|
562
|
+
readonly loadImage?: (handle: MediaHandle) => Promise<ImageBitmap | HTMLImageElement>;
|
|
563
|
+
readonly hardwareAcceleration?: "no-preference" | "prefer-hardware" | "prefer-software";
|
|
564
|
+
}
|
|
565
|
+
/** Frames of one video asset through a shared `VideoSampleSink`. */
|
|
566
|
+
declare class MediabunnyFrameSource implements FrameSource {
|
|
567
|
+
private readonly assetId;
|
|
568
|
+
private readonly handle;
|
|
569
|
+
private readonly inputs;
|
|
570
|
+
private readonly createCanvas;
|
|
571
|
+
private readonly hooks;
|
|
572
|
+
private input;
|
|
573
|
+
private track;
|
|
574
|
+
private sink;
|
|
575
|
+
private stillSink;
|
|
576
|
+
constructor(assetId: string, handle: MediaHandle, inputs: MediabunnyInputs, createCanvas: CreateCanvas, hooks?: FrameSourceHooks);
|
|
577
|
+
private wrap;
|
|
578
|
+
open(): Promise<FrameSourceInfo>;
|
|
579
|
+
frameAt(sourceTime: number): Promise<Frame | null>;
|
|
580
|
+
stream(from: number, to: number): AsyncGenerator<Frame, void, unknown>;
|
|
581
|
+
/** A canvas copy of the frame at `sourceTime`, for freeze frames; pooled canvases are copied right away. */
|
|
582
|
+
still(sourceTime: number): Promise<Frame | null>;
|
|
583
|
+
close(): void;
|
|
584
|
+
}
|
|
585
|
+
declare function mediabunnyFrameSourceFactory(inputs: MediabunnyInputs, createCanvas: CreateCanvas, hooks?: FrameSourceHooks): FrameSourceFactory;
|
|
586
|
+
/** PCM ranges of one audio track, decoded on demand into AudioBuffers of the track's sample rate. */
|
|
587
|
+
declare class MediabunnyAudioSource implements AudioSource {
|
|
588
|
+
private readonly assetId;
|
|
589
|
+
private readonly handle;
|
|
590
|
+
private readonly inputs;
|
|
591
|
+
private readonly context;
|
|
592
|
+
private readonly trackIndex;
|
|
593
|
+
private input;
|
|
594
|
+
private track;
|
|
595
|
+
private sink;
|
|
596
|
+
private sampleRate;
|
|
597
|
+
private channels;
|
|
598
|
+
constructor(assetId: string, handle: MediaHandle, inputs: MediabunnyInputs, context: BaseAudioContext, trackIndex?: number);
|
|
599
|
+
open(): Promise<AudioSourceInfo>;
|
|
600
|
+
range(from: number, to: number): Promise<AudioBuffer>;
|
|
601
|
+
close(): void;
|
|
602
|
+
}
|
|
603
|
+
declare function mediabunnyAudioSourceFactory(inputs: MediabunnyInputs): AudioSourceFactory;
|
|
604
|
+
/**
|
|
605
|
+
* Trace the mask outline onto the context's current path, in local element
|
|
606
|
+
* pixels. Shape coordinates are fractions of the element box; bezier paths
|
|
607
|
+
* are in pixels with handles. Returns false when the spec has no usable shape.
|
|
608
|
+
*/
|
|
609
|
+
declare function traceMask(ctx: Ctx2D, mask: MaskSpec, rect: DrawRect): boolean;
|
|
610
|
+
declare function roundedRect(ctx: Ctx2D, x: number, y: number, w: number, h: number, r: number): void;
|
|
611
|
+
interface PaintInputs {
|
|
612
|
+
readonly frames: ReadonlyMap<string, Frame | null>;
|
|
613
|
+
readonly nested: ReadonlyMap<string, AnyCanvas>;
|
|
614
|
+
}
|
|
615
|
+
/**
|
|
616
|
+
* Executes a FramePlan on a 2D context of composition size. Each op is
|
|
617
|
+
* drawn under its own transform, alpha, blend and filter; masks clip; an
|
|
618
|
+
* adjustment op re-filters everything drawn so far through a scratch canvas.
|
|
619
|
+
*/
|
|
620
|
+
declare class CanvasPainter {
|
|
621
|
+
readonly width: number;
|
|
622
|
+
readonly height: number;
|
|
623
|
+
private readonly createCanvas;
|
|
624
|
+
private scratch;
|
|
625
|
+
/** An element drawn on its own before compositing: a feathered mask, vignette, grain. */
|
|
626
|
+
private layer;
|
|
627
|
+
private grain;
|
|
628
|
+
constructor(width: number, height: number, createCanvas: CreateCanvas);
|
|
629
|
+
paint(ctx: Ctx2D, plan: FramePlan, inputs: PaintInputs): void;
|
|
630
|
+
private drawOp;
|
|
631
|
+
/** The footage's picture, blurred and scaled to cover the frame, under everything; skipped when the footage covers the frame itself. */
|
|
632
|
+
private blurBackground;
|
|
633
|
+
private blit;
|
|
634
|
+
/** Applies the border radius and mask as a clip in local coordinates. Returns true when a mask clip was set. */
|
|
635
|
+
private clip;
|
|
636
|
+
/**
|
|
637
|
+
* The element alone on the layer canvas, then composited: a feathered mask punches it out with a
|
|
638
|
+
* blurred alpha there, and vignette and grain darken or speckle only its own pixels.
|
|
639
|
+
*/
|
|
640
|
+
private drawLayered;
|
|
641
|
+
/** A tile of black and white specks, the same for the same seed; alpha up to `amount`. */
|
|
642
|
+
private noise;
|
|
643
|
+
private adjust;
|
|
644
|
+
}
|
|
645
|
+
declare function setMatrix(ctx: Ctx2D, m: Matrix2D): void;
|
|
646
|
+
/** Whether a draw op puts its picture over the whole canvas as it is: no transform, crop, clip, fade, filter or blend. */
|
|
647
|
+
declare function coversCanvas(op: DrawOp, width: number, height: number): boolean;
|
|
648
|
+
/** The composition background (a colour or a gradient) over the whole frame. */
|
|
649
|
+
declare function paintBackground(ctx: Ctx2D, plan: Pick<FramePlan, "background" | "backgroundGradient">, w: number, h: number): void;
|
|
650
|
+
/** The same background as CSS, for the preview's ground under backdrop overlays. */
|
|
651
|
+
declare function backgroundCss(plan: Pick<FramePlan, "background" | "backgroundGradient">): string;
|
|
652
|
+
/** A canvas gradient for the frame, laid out the way CSS lays out the same gradient on a box of this size. */
|
|
653
|
+
declare function backgroundFill(ctx: Ctx2D, g: BackgroundGradient, w: number, h: number): CanvasGradient | string;
|
|
654
|
+
interface CompositorDeps {
|
|
655
|
+
readonly sources: AssetSources;
|
|
656
|
+
readonly createCanvas: CreateCanvas;
|
|
657
|
+
readonly resolveNested?: ResolveNested | undefined;
|
|
658
|
+
/** Called after a frame was painted with a new frame index. */
|
|
659
|
+
readonly onFrame?: ((time: number, frame: number) => void) | undefined;
|
|
660
|
+
}
|
|
661
|
+
/**
|
|
662
|
+
* Sees a frame's plan and its pictures before they are painted. Returning true means the host takes this frame as it
|
|
663
|
+
* is (an export handing a decoded picture straight to its encoder): nothing is prepared or painted.
|
|
664
|
+
*/
|
|
665
|
+
type FrameIntercept = (plan: FramePlan, frames: ReadonlyMap<string, Frame | null>) => boolean;
|
|
666
|
+
interface TickOptions {
|
|
667
|
+
/** Fetch missing frames before painting (seek, export). */
|
|
668
|
+
readonly wait: boolean;
|
|
669
|
+
/** With `wait`: no deadline. An export waits for every frame instead of drawing a stale one. */
|
|
670
|
+
readonly exact?: boolean;
|
|
671
|
+
/** This composition's frames only; nested compositions paint as usual. */
|
|
672
|
+
readonly intercept?: FrameIntercept;
|
|
673
|
+
}
|
|
674
|
+
/**
|
|
675
|
+
* One tick: plan the frame, sync decoders, gather frames, paint. A tick that
|
|
676
|
+
* arrives while another is in flight is coalesced into one re-run; a tick on
|
|
677
|
+
* the same frame index as the last paint only keeps decoders prefetching.
|
|
678
|
+
*/
|
|
679
|
+
declare class Compositor {
|
|
680
|
+
private readonly deps;
|
|
681
|
+
readonly pool: DecoderPool;
|
|
682
|
+
private painter;
|
|
683
|
+
private ctx;
|
|
684
|
+
private state;
|
|
685
|
+
private inFlight;
|
|
686
|
+
private pending;
|
|
687
|
+
private lastFrame;
|
|
688
|
+
private lastPlan;
|
|
689
|
+
private readonly nested;
|
|
690
|
+
private disposed;
|
|
691
|
+
constructor(state: ResolvedState, deps: CompositorDeps);
|
|
692
|
+
attach(ctx: Ctx2D | null): void;
|
|
693
|
+
get renderedFrame(): number;
|
|
694
|
+
get attachedContext(): Ctx2D | null;
|
|
695
|
+
get lastPlanBuilt(): FramePlan | null;
|
|
696
|
+
get currentState(): ResolvedState;
|
|
697
|
+
setState(state: ResolvedState): void;
|
|
698
|
+
setRate(rate: number): void;
|
|
699
|
+
plan(t: number): FramePlan;
|
|
700
|
+
/** Jump every decoder (nested ones too) to `t`. */
|
|
701
|
+
seek(t: number): Promise<void>;
|
|
702
|
+
/**
|
|
703
|
+
* The next frame of a sequential render (export): decoders keep streaming
|
|
704
|
+
* from where they are instead of being re-primed, and every frame is waited
|
|
705
|
+
* for. Paint order and output are the same as `seek` then `tick`, at a
|
|
706
|
+
* fraction of the decode work.
|
|
707
|
+
*/
|
|
708
|
+
step(t: number, intercept?: FrameIntercept): Promise<void>;
|
|
709
|
+
tick(t: number, opts: TickOptions): Promise<void>;
|
|
710
|
+
dispose(): void;
|
|
711
|
+
private run;
|
|
712
|
+
private syncNested;
|
|
713
|
+
}
|
|
714
|
+
/**
|
|
715
|
+
* Resolves `<Nested of={Helper}>` through the kit's `resolveScene`, once per
|
|
716
|
+
* helper. File references (`of="file:..."`) have no loader yet and resolve
|
|
717
|
+
* to nothing.
|
|
718
|
+
*/
|
|
719
|
+
declare function makeNestedResolver(parent: () => ResolvedState): ResolveNested;
|
|
720
|
+
export { postEffects as $, ImageFrameSource as A, FRAME_EPS as At, buildGainEnvelope as B, QUEUE_MAX as Bt, ClipDecoder as C, devWarn as Ct, ErrorFrame as D, DUCK_ATTACK_S as Dt, AssetSources as E, AUDIO_PRIME_S as Et, ManualClock as F, MAX_RATE as Ft, MaskSpec as G, SEEK_MODE_SPEED as Gt, BackgroundGradient as H, REVERSE_WINDOW_FRAMES as Ht, PerformanceClock as I, MIN_RATE as It, buildFramePlan as J, ResolveNested as K, SETTLE_MS as Kt, AudioOccurrence as L, MOUNT_AHEAD_S as Lt, defaultCreateCanvas as M, INPUT_IDLE_CLOSE_S as Mt, fromMap as N, LOOKAHEAD_S as Nt, ErrorFrameSource as O, DUCK_RELEASE_S as Ot, toResolver as P, MAX_OPEN_INPUTS as Pt, filterString as Q, GainPoint as R, PICK_EPS as Rt, DecoderPool as S, captureWarnings as St, AssetSourceDeps as T, AUDIO_CHUNK_S as Tt, DrawOp as U, REVERSE_WINDOW_S as Ut, duckIntervals as V, QUEUE_TARGET as Vt, FramePlan as W, SCHEDULER_MS as Wt, FilterResult as X, compositionBackground as Y, PostEffect as Z, FrameSourceHooks as _, MediaHandle as _t, TickOptions as a, AudioOutput as at, mediabunnyFrameSourceFactory as b, PlayerEvents as bt, backgroundCss as c, AudioSourceInfo as ct, paintBackground as d, Ctx2D as dt, EASING_FN as et, setMatrix as f, EnvelopePoint as ft, mediabunnyAudioSourceFactory as g, FrameSourceInfo as gt, MediabunnyAudioSource as h, FrameSourceFactory as ht, FrameIntercept as i, AnyCanvas as it, copyFrame as j, FRAME_WAIT_MS as jt, ImageFrame as k, ENVELOPE_STEP_S as kt, backgroundFill as l, Clock as lt, traceMask as m, FrameSource as mt, Compositor as n, hasKeyframes as nt, CanvasPainter as o, AudioSource as ot, roundedRect as p, Frame as pt, blendOf as q, UNMOUNT_BEHIND_S as qt, CompositorDeps as r, keyframedValue as rt, PaintInputs as s, AudioSourceFactory as st, makeNestedResolver as t, ease as tt, coversCanvas as u, CreateCanvas as ut, MediabunnyFrameSource as v, MediaResolver as vt, DecodeMode as w, AUDIO_CACHE_S as wt, MediabunnyInputs as x, ScheduledSource as xt, SampleFrame as y, PlayerErrorEvent as yt, buildAudioPlan as z, PLAY_PRIME_TIMEOUT_MS as zt };
|