@pixerate/editor 0.82.0 → 0.84.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.
@@ -142,8 +142,16 @@ interface VideoTransform {
142
142
  /** How a picture is fitted to the frame before its transform: like CSS `object-fit`. */
143
143
  type VideoFit = 'contain' | 'cover' | 'fill' | 'none';
144
144
  declare const VIDEO_FITS: readonly ["contain", "cover", "fill", "none"];
145
- type VideoTransitionType = 'crossfade' | 'dip' | 'wipe';
146
- declare const VIDEO_TRANSITION_TYPES: readonly ["crossfade", "dip", "wipe"];
145
+ /**
146
+ * - `crossfade`: this clip fades up over the one before.
147
+ * - `dip`: down into a colour and up out of it.
148
+ * - `wipe`: an edge travels across, revealing this clip.
149
+ * - `push`: this clip slides in, pushing the one before out.
150
+ * - `slide`: this clip slides in over the one before, which stays put.
151
+ * - `iris`: a circle opens from the centre, revealing this clip.
152
+ */
153
+ type VideoTransitionType = 'crossfade' | 'dip' | 'wipe' | 'push' | 'slide' | 'iris';
154
+ declare const VIDEO_TRANSITION_TYPES: readonly ["crossfade", "dip", "wipe", "push", "slide", "iris"];
147
155
  type VideoWipeDirection = 'left' | 'right' | 'up' | 'down';
148
156
  declare const VIDEO_WIPE_DIRECTIONS: readonly ["left", "right", "up", "down"];
149
157
  /**
@@ -157,8 +165,54 @@ interface VideoTransition {
157
165
  duration: number;
158
166
  /** For dip: the colour passed through (default black). */
159
167
  color?: string;
160
- /** For wipe: the way the edge travels (default right, revealing from the left). */
168
+ /** For wipe, push and slide: the way the edge or picture travels (default right: in from the left). */
161
169
  direction?: VideoWipeDirection;
170
+ /**
171
+ * Shape of the sound's fades through the transition (see `VideoFadeCurve`). Default
172
+ * `equal-power` for crossfades and wipes, where two sounds overlap and a linear mix dips in
173
+ * loudness halfway; `linear` for dips, where one sound fades out before the other fades in.
174
+ */
175
+ audioCurve?: VideoFadeCurve;
176
+ }
177
+ /** The curve a transition's sound fades follow (its `audioCurve`, else the default for its type). */
178
+ declare const videoTransitionAudioCurve: (t: VideoTransition) => VideoFadeCurve;
179
+ /**
180
+ * How a clip's picture mixes with what is beneath it, as in CSS `mix-blend-mode` (`add` sums the
181
+ * colours, `plus-lighter` in CSS). These are the modes the canvas preview, SVG frames and ffmpeg's
182
+ * `blend` filter all share.
183
+ */
184
+ type VideoBlendMode = 'normal' | 'multiply' | 'screen' | 'overlay' | 'darken' | 'lighten' | 'color-dodge' | 'color-burn' | 'hard-light' | 'soft-light' | 'difference' | 'exclusion' | 'add';
185
+ declare const VIDEO_BLEND_MODES: readonly ["normal", "multiply", "screen", "overlay", "darken", "lighten", "color-dodge", "color-burn", "hard-light", "soft-light", "difference", "exclusion", "add"];
186
+ /**
187
+ * Colour correction for a clip's picture (see `VIDEO_GRADE_CONTROLS` for ranges). Applied in a fixed
188
+ * order: exposure, contrast, saturation, hue, then white balance (temperature and tint).
189
+ */
190
+ interface VideoGrade {
191
+ /** Stops: +1 doubles every channel (default 0). */
192
+ exposure?: number;
193
+ /** -1 (flat grey) to 1 (twice as steep), around mid grey (default 0). */
194
+ contrast?: number;
195
+ /** -1 (black and white) to 1 (twice as saturated); default 0. */
196
+ saturation?: number;
197
+ /** Hue rotation in degrees (default 0). */
198
+ hue?: number;
199
+ /** -1 (cool) to 1 (warm); default 0. */
200
+ temperature?: number;
201
+ /** -1 (green) to 1 (magenta); default 0. */
202
+ tint?: number;
203
+ }
204
+ /**
205
+ * How a clip enters or leaves: `fade`; `rise` (up into place) or `drop` (down into place);
206
+ * `slide-left` / `slide-right` (in moving that way); `zoom` (grows into place); `pop` (grows past its
207
+ * size and settles). Each also fades. An exit plays the same movement backwards.
208
+ */
209
+ type VideoClipAnimationEffect = 'fade' | 'rise' | 'drop' | 'slide-left' | 'slide-right' | 'zoom' | 'pop';
210
+ declare const VIDEO_CLIP_ANIMATION_EFFECTS: readonly ["fade", "rise", "drop", "slide-left", "slide-right", "zoom", "pop"];
211
+ /** An entrance or exit animation (see `VideoClipAnimationEffect`). */
212
+ interface VideoClipAnimation {
213
+ effect: VideoClipAnimationEffect;
214
+ /** Length (ms, default 500). */
215
+ duration?: number;
162
216
  }
163
217
  interface VideoClipBase {
164
218
  id: string;
@@ -172,6 +226,14 @@ interface VideoClipBase {
172
226
  transform?: VideoTransform;
173
227
  /** Default contain. */
174
228
  fit?: VideoFit;
229
+ /** How the picture mixes with what is beneath it (default normal). */
230
+ blend?: VideoBlendMode;
231
+ /** Colour correction of the picture. */
232
+ grade?: VideoGrade;
233
+ /** How the picture enters, from the clip's start (on top of any fade in). */
234
+ animateIn?: VideoClipAnimation;
235
+ /** How the picture leaves, up to the clip's end. */
236
+ animateOut?: VideoClipAnimation;
175
237
  /** Fade in from nothing over this long (ms), pictures and sound. */
176
238
  fadeIn?: number;
177
239
  /** Fade out over this long (ms). */
@@ -179,6 +241,17 @@ interface VideoClipBase {
179
241
  /** Transition from the clip before (see `VideoTransition`). */
180
242
  transition?: VideoTransition;
181
243
  }
244
+ /**
245
+ * The shape of a sound fade, the same curves ffmpeg's `afade` draws, so previews and exports agree.
246
+ * With `x` running 0→1 through a fade in (1→0 through a fade out), the gain is:
247
+ * - `linear`: x (ffmpeg `tri`). Dips in loudness in the middle of a crossfade.
248
+ * - `equal-power`: sin(x·π/2) (`qsin`). Keeps loudness steady; the usual choice for crossfades.
249
+ * - `s-curve`: (1 − cos(x·π))/2 (`hsin`). Gentle at both ends.
250
+ * - `exponential`: x³ (`cub`). Slow start; a natural-sounding fade in from silence.
251
+ * - `logarithmic`: 1 − (1 − x)² (`ipar`). Fast start, slow finish.
252
+ */
253
+ type VideoFadeCurve = 'linear' | 'equal-power' | 's-curve' | 'exponential' | 'logarithmic';
254
+ declare const VIDEO_FADE_CURVES: readonly ["linear", "equal-power", "s-curve", "exponential", "logarithmic"];
182
255
  /** A stretch of an asset. */
183
256
  interface VideoMediaClip extends VideoClipBase {
184
257
  type: 'media';
@@ -191,6 +264,10 @@ interface VideoMediaClip extends VideoClipBase {
191
264
  volume?: VideoAnimatable;
192
265
  /** Silences this clip. */
193
266
  muted?: boolean;
267
+ /** Shape of the sound's fade in (default linear). The picture always fades linearly. */
268
+ fadeInCurve?: VideoFadeCurve;
269
+ /** Shape of the sound's fade out (default linear). */
270
+ fadeOutCurve?: VideoFadeCurve;
194
271
  }
195
272
  /** A title. */
196
273
  interface VideoTextClip extends VideoClipBase {
@@ -259,6 +336,8 @@ declare function parseVideoAnimatable(v: unknown, path: string, range?: {
259
336
  optional?: boolean;
260
337
  }): VideoAnimatable | undefined;
261
338
  declare function parseVideoTransform(v: unknown, path: string): VideoTransform | undefined;
339
+ declare function parseVideoClipAnimation(v: unknown, path: string): VideoClipAnimation | undefined;
340
+ declare function parseVideoGrade(v: unknown, path: string): VideoGrade | undefined;
262
341
  declare function parseVideoTransition(v: unknown, path: string): VideoTransition | undefined;
263
342
  declare function parseVideoAsset(v: unknown, path?: string): VideoAsset;
264
343
  declare function parseVideoClip(v: unknown, path?: string): VideoClip;
@@ -302,10 +381,28 @@ declare const videoFrameDuration: (fps: number) => number;
302
381
  declare function snapToVideoFrame(time: number, fps: number): number;
303
382
  /** Whole frames in `time` at `fps` (rounded). */
304
383
  declare const videoFrames: (time: number, fps: number) => number;
305
- /** Non-drop SMPTE timecode `HH:MM:SS:FF` for a time (ms). */
306
- declare function formatVideoTimecode(time: number, fps: number): string;
384
+ /**
385
+ * Whether `fps` is an NTSC rate that SMPTE counts in drop-frame timecode: 29.97 (30000/1001) or
386
+ * 59.94 (60000/1001). Other fractional rates (23.976) have no drop-frame form.
387
+ */
388
+ declare function isVideoDropFrameRate(fps: number): boolean;
389
+ /** Frames elapsed in `time` (ms), counted at the exact rate behind a rounded NTSC rate. */
390
+ declare const videoTimecodeFrames: (time: number, fps: number) => number;
391
+ /**
392
+ * SMPTE timecode for a time (ms): `HH:MM:SS:FF`, or `HH:MM:SS;FF` in drop-frame, which skips frame
393
+ * numbers 0 and 1 (0–3 at 59.94) at the start of every minute except each tenth, so the clock keeps
394
+ * up with real time at NTSC rates. `dropFrame` defaults to on at 29.97 and 59.94 and is ignored at
395
+ * rates without a drop-frame form.
396
+ */
397
+ declare function formatVideoTimecode(time: number, fps: number, dropFrame?: boolean): string;
398
+ /** SMPTE timecode for a frame count (see `formatVideoTimecode`). */
399
+ declare function formatVideoTimecodeFrames(frames: number, fps: number, dropFrame?: boolean): string;
400
+ /** Frames up to timecode `01:00:00:00` (`;00` in drop-frame), where record timecode usually starts. */
401
+ declare const videoHourFrames: (fps: number, dropFrame?: boolean) => number;
307
402
  /** Readable time for messages: `2.50 s`. */
308
403
  declare const formatVideoSeconds: (time: number) => string;
404
+ /** Whether an asset carries sound: audio, or video not marked `hasAudio: false`. */
405
+ declare const videoAssetHasSound: (asset: VideoAsset | undefined) => boolean;
309
406
  declare function videoAssetMap(project: VideoProject): Map<string, VideoAsset>;
310
407
  /** A track's clips in timeline order. */
311
408
  declare function sortedVideoClips(track: VideoTrack): VideoClip[];
@@ -377,7 +474,7 @@ declare function videoClipKeyframes(clip: VideoClip): {
377
474
  keys: ReturnType<typeof videoKeysOf>;
378
475
  }[];
379
476
  type VideoIssueSeverity = 'error' | 'warning' | 'note';
380
- declare const VIDEO_ISSUE_CODES: readonly ["missing-asset", "wrong-track", "overlap", "source-overrun", "transition-without-cut", "transition-too-long", "transition-handles", "fades-too-long", "keyframes-outside-clip", "unknown-duration", "unused-asset", "marker-past-end", "empty"];
477
+ declare const VIDEO_ISSUE_CODES: readonly ["missing-asset", "wrong-track", "overlap", "source-overrun", "transition-without-cut", "transition-too-long", "transition-handles", "fades-too-long", "animations-too-long", "animation-keyed", "keyframes-outside-clip", "unknown-duration", "unused-asset", "marker-past-end", "empty"];
381
478
  type VideoIssueCode = (typeof VIDEO_ISSUE_CODES)[number];
382
479
  interface VideoIssue {
383
480
  code: VideoIssueCode;
@@ -620,6 +717,10 @@ interface VideoLayer {
620
717
  direction: VideoWipeDirection;
621
718
  progress: number;
622
719
  };
720
+ /** For an iris: only a circle on the frame's centre shows, its radius `progress` (0–1) of the way to the frame's corners. */
721
+ iris?: {
722
+ progress: number;
723
+ };
623
724
  /** For text: what to draw. */
624
725
  text?: {
625
726
  text: string;
@@ -631,6 +732,10 @@ interface VideoLayer {
631
732
  };
632
733
  /** For colour clips and dips. */
633
734
  color?: string;
735
+ /** How it mixes with the layers beneath (absent = normal). */
736
+ blend?: VideoBlendMode;
737
+ /** Colour correction (absent = none). */
738
+ grade?: VideoGrade;
634
739
  }
635
740
  /** One sound playing at a moment. */
636
741
  interface VideoAudioSource {
@@ -677,6 +782,15 @@ declare function videoTextStyle(clip: VideoTextClip): NonNullable<VideoLayer['te
677
782
  * transforms without them, and moves the image itself).
678
783
  */
679
784
  declare function videoTitleFrame(project: VideoProject, clip: VideoTextClip, clipTime?: number): VideoFrame;
785
+ /** How far a push or slide has moved at `progress` (0–1): eased in and out, (1 − cos πp) / 2. */
786
+ declare const videoTransitionEase: (progress: number) => number;
787
+ /** Gain of a fade shape `x` of the way up (0–1); a fade out uses the time left, so it mirrors the fade in. */
788
+ declare function videoFadeCurveGain(curve: VideoFadeCurve | undefined, x: number): number;
789
+ /**
790
+ * A media clip's own gain at clip time `clipTime` (ms): its volume (or volume keyframes) times its
791
+ * shaped fades. Leaves out track volume and transitions, which depend on the timeline around it.
792
+ */
793
+ declare function videoClipSoundGain(clip: VideoClip, clipTime: number): number;
680
794
  /**
681
795
  * What plays at `time` (ms): the pictures to draw, back to front, with their placement, opacity
682
796
  * and the moment of source each shows, and the sounds with their gain. A pure function of the
@@ -684,6 +798,241 @@ declare function videoTitleFrame(project: VideoProject, clip: VideoTextClip, cli
684
798
  */
685
799
  declare function videoFrameAt(project: VideoProject, time: number): VideoFrame;
686
800
 
801
+ /**
802
+ * Sound levels of a piece of audio, small enough to keep for every asset: a pyramid of min / max
803
+ * sample values. Level 0 holds one min / max pair per `blockSize` samples; each level above merges
804
+ * four blocks of the one below, so the min / max of any stretch costs a handful of reads at any
805
+ * zoom. Channels are folded together (the loudest of them counts). Plain data: hosts can compute
806
+ * it once (`buildVideoAudioPeaks`) and store it.
807
+ */
808
+ interface VideoAudioPeaks {
809
+ /** Samples per second of the decoded audio. */
810
+ sampleRate: number;
811
+ /** Samples in the audio (per channel). */
812
+ length: number;
813
+ /** Samples per level-0 block. */
814
+ blockSize: number;
815
+ /** `levels[k]` is `[min, max, min, max, …]`, one pair per block of `blockSize × 4^k` samples. */
816
+ levels: Float32Array[];
817
+ }
818
+ /** Samples per level-0 block (well under a millisecond at common rates). */
819
+ declare const VIDEO_PEAK_BLOCK = 32;
820
+ /** Builds the peak pyramid for decoded audio (one `Float32Array` of samples per channel). */
821
+ declare function buildVideoAudioPeaks(channels: readonly Float32Array[], sampleRate: number, blockSize?: number): VideoAudioPeaks;
822
+ /** Length of the audio (ms). */
823
+ declare const videoAudioPeaksDuration: (peaks: VideoAudioPeaks) => number;
824
+ /**
825
+ * The lowest and highest sample between two times (ms) in the audio, to the nearest block (so a
826
+ * stretch can read up to a block wider). `{ min: 0, max: 0 }` outside the audio.
827
+ */
828
+ declare function videoAudioPeakRange(peaks: VideoAudioPeaks, from: number, to: number): {
829
+ min: number;
830
+ max: number;
831
+ };
832
+ /** `columns` min / max pairs (`[min, max, …]`) evenly across `from`..`to` ms, e.g. one per pixel. */
833
+ declare function videoAudioPeakSpans(peaks: VideoAudioPeaks, from: number, to: number, columns: number): Float32Array;
834
+ /**
835
+ * What a media clip plays as `columns` min / max pairs across its length: each column reads the
836
+ * stretch of source under it (through the in point and speed) and is scaled by the clip's volume and
837
+ * fades, so the waveform shows what is heard.
838
+ */
839
+ declare function videoClipWaveform(clip: VideoMediaClip, peaks: VideoAudioPeaks, columns: number): Float32Array;
840
+ /**
841
+ * An SVG path (`d`) of a waveform filling `width` × `height`: the top edge along the maxima, back
842
+ * along the minima. Values are clamped to ±1, so anything louder than full scale touches the edge.
843
+ */
844
+ declare function videoWaveformPath(spans: Float32Array, width: number, height: number): string;
845
+ /**
846
+ * The SVG path of a media clip's waveform drawn `width` × `height` pixels (one column per 2 px), as
847
+ * `videoClipWaveform` reads it: what timelines draw inside an audio clip.
848
+ */
849
+ declare function videoClipWaveformPath(clip: VideoMediaClip, peaks: VideoAudioPeaks, width: number, height: number): string;
850
+ /** A sample value as dBFS, floored at -100 (silence). */
851
+ declare const videoPeakDb: (value: number) => number;
852
+ interface VideoAudioLevel {
853
+ /** Start of the bucket (ms, in the clock the caller asked in). */
854
+ start: number;
855
+ end: number;
856
+ /** Peak level in dBFS (0 = full scale, -100 = silence). */
857
+ peakDb: number;
858
+ }
859
+ /** Peak level in `buckets` equal stretches of `from`..`to` ms of the audio. */
860
+ declare function videoAudioLevels(peaks: VideoAudioPeaks, from: number, to: number, buckets: number): VideoAudioLevel[];
861
+ interface VideoSilenceOptions {
862
+ /** Quieter than this peak level counts as silence (dBFS, default -40). */
863
+ thresholdDb?: number;
864
+ /** Shortest silence to report (ms, default 300). */
865
+ minDuration?: number;
866
+ /** Resolution of the scan (ms, default 10). */
867
+ resolution?: number;
868
+ }
869
+ /** Stretches of `from`..`to` ms of the audio that stay below a level, at least `minDuration` long. */
870
+ declare function findVideoAudioSilences(peaks: VideoAudioPeaks, from: number, to: number, options?: VideoSilenceOptions): {
871
+ start: number;
872
+ end: number;
873
+ }[];
874
+ /** Fetches and decodes a media file's sound into peaks, in a browser (Web Audio). */
875
+ declare function decodeVideoAudioPeaks(src: string, options?: {
876
+ signal?: AbortSignal;
877
+ }): Promise<VideoAudioPeaks>;
878
+ /** Loads the peaks of an asset's sound. */
879
+ type VideoAudioPeaksLoader = (asset: VideoAsset) => Promise<VideoAudioPeaks>;
880
+ /**
881
+ * Peaks per asset, loaded once each (by `src`) and shared by the timeline and agent tools.
882
+ * `get` returns what is ready and starts loading the rest; `subscribe` hears when more is ready.
883
+ */
884
+ declare class VideoAudioPeaksCache {
885
+ private readonly loader;
886
+ private readonly ready;
887
+ private readonly pending;
888
+ private readonly failed;
889
+ private readonly listeners;
890
+ constructor(loader?: VideoAudioPeaksLoader);
891
+ /** The peaks if ready, without starting to load them. */
892
+ peek(asset: VideoAsset): VideoAudioPeaks | undefined;
893
+ /** The peaks if ready; otherwise `undefined`, and loading starts (once) unless it failed. */
894
+ get(asset: VideoAsset): VideoAudioPeaks | undefined;
895
+ /** Why loading an asset's peaks failed, if it did. */
896
+ error(asset: VideoAsset): string | undefined;
897
+ /** The peaks, loading them if needed. Rejects with a readable error. */
898
+ load(asset: VideoAsset): Promise<VideoAudioPeaks>;
899
+ /** Uses peaks the host already has (computed earlier or stored). */
900
+ set(src: string, peaks: VideoAudioPeaks): void;
901
+ subscribe(fn: () => void): () => void;
902
+ private emit;
903
+ }
904
+
905
+ /**
906
+ * Colour correction as a fixed series of colour-matrix steps on gamma-encoded sRGB, each clamped
907
+ * to 0–1, in the same order everywhere: exposure, contrast, saturation, hue, then white balance.
908
+ * Every step is one the browser has (CSS `brightness`, `contrast`, `saturate`, `hue-rotate`; white
909
+ * balance as an SVG `feColorMatrix`) and ffmpeg has (`lutrgb`, `colorchannelmixer`), with the same
910
+ * formulas, so the preview, SVG frames and the export agree.
911
+ */
912
+ /** One slider of a grade: its range, default and step, for schemas, inspectors and clamping. */
913
+ interface VideoGradeControl {
914
+ key: keyof VideoGrade;
915
+ label: string;
916
+ min: number;
917
+ max: number;
918
+ step: number;
919
+ /** What the number means, for tool descriptions. */
920
+ description: string;
921
+ }
922
+ declare const VIDEO_GRADE_CONTROLS: readonly VideoGradeControl[];
923
+ /** Named looks. Apply them at a strength with `videoGradePreset`. */
924
+ declare const VIDEO_GRADE_PRESETS: {
925
+ readonly warm: {
926
+ readonly temperature: 0.4;
927
+ readonly saturation: 0.1;
928
+ };
929
+ readonly cool: {
930
+ readonly temperature: -0.4;
931
+ readonly tint: -0.05;
932
+ };
933
+ readonly punchy: {
934
+ readonly contrast: 0.25;
935
+ readonly saturation: 0.3;
936
+ };
937
+ readonly faded: {
938
+ readonly exposure: 0.15;
939
+ readonly contrast: -0.3;
940
+ readonly saturation: -0.3;
941
+ };
942
+ readonly 'black-and-white': {
943
+ readonly saturation: -1;
944
+ readonly contrast: 0.15;
945
+ };
946
+ readonly 'bleach-bypass': {
947
+ readonly saturation: -0.6;
948
+ readonly contrast: 0.4;
949
+ };
950
+ readonly 'day-for-night': {
951
+ readonly exposure: -1.5;
952
+ readonly saturation: -0.4;
953
+ readonly temperature: -0.6;
954
+ };
955
+ };
956
+ type VideoGradePreset = keyof typeof VIDEO_GRADE_PRESETS;
957
+ declare const VIDEO_GRADE_PRESET_NAMES: VideoGradePreset[];
958
+ /** A grade with every value clamped to its range and zero values left out; `undefined` when nothing is left. */
959
+ declare function normalizeVideoGrade(grade: VideoGrade | undefined): VideoGrade | undefined;
960
+ /** A preset at `amount` (0–1, default 1) of its strength. */
961
+ declare function videoGradePreset(name: VideoGradePreset, amount?: number): VideoGrade | undefined;
962
+ /** One step of a grade. */
963
+ type VideoGradeStep = {
964
+ kind: 'brightness';
965
+ amount: number;
966
+ } | {
967
+ kind: 'contrast';
968
+ amount: number;
969
+ } | {
970
+ kind: 'saturate';
971
+ amount: number;
972
+ } | {
973
+ kind: 'hue-rotate';
974
+ degrees: number;
975
+ } | {
976
+ kind: 'gains';
977
+ r: number;
978
+ g: number;
979
+ b: number;
980
+ };
981
+ /** The steps a grade applies, in order, leaving out those that change nothing. */
982
+ declare function videoGradeSteps(grade: VideoGrade | undefined): VideoGradeStep[];
983
+ /**
984
+ * A step as a 3 × 4 matrix (rows r, g, b; columns r, g, b, offset), with the coefficients the CSS
985
+ * Filter Effects spec gives for `saturate` and `hue-rotate`.
986
+ */
987
+ declare function videoGradeStepMatrix(step: VideoGradeStep): number[][];
988
+ /** An RGB colour (0–255 each) through a grade, as the preview and export draw it. */
989
+ declare function applyVideoGrade(grade: VideoGrade | undefined, rgb: readonly [number, number, number]): [number, number, number];
990
+ /** The `values` of an SVG `feColorMatrix type="matrix"` for a step. */
991
+ declare function videoGradeStepSvgValues(step: VideoGradeStep): string;
992
+ /** An SVG `<filter>` applying a grade, steps in order (each clamps, as CSS filters do). */
993
+ declare function videoGradeSvgFilter(id: string, grade: VideoGrade | undefined): string;
994
+ /** Id of the SVG filter for a white-balance step: the same gains always get the same id. */
995
+ declare const videoGradeGainsFilterId: (step: Extract<VideoGradeStep, {
996
+ kind: "gains";
997
+ }>) => string;
998
+ /**
999
+ * A grade as a CSS / canvas `filter`: CSS functions for every step, and `url(#id)` for white balance,
1000
+ * which CSS has no function for (see `videoGradeGainsFilterId`; the host puts that filter in the page).
1001
+ * `''` when the grade changes nothing.
1002
+ */
1003
+ declare function videoGradeCssFilter(grade: VideoGrade | undefined): string;
1004
+ /** The white-balance SVG filters a grade's CSS filter refers to, as `{ id, markup }`. */
1005
+ declare function videoGradeSvgDefs(grade: VideoGrade | undefined): {
1006
+ id: string;
1007
+ markup: string;
1008
+ }[];
1009
+ /** A grade as ffmpeg filters on RGB, in order: `lutrgb` for exposure and contrast, `colorchannelmixer` for the rest. */
1010
+ declare function videoGradeFfmpegFilters(grade: VideoGrade | undefined): string[];
1011
+
1012
+ /** Default length of an entrance or exit animation (ms). */
1013
+ declare const VIDEO_CLIP_ANIMATION_DURATION = 500;
1014
+ declare const PROPS: readonly ["opacity", "x", "y", "scale"];
1015
+ type Prop = (typeof PROPS)[number];
1016
+ /** The properties an animation moves (each also fades: opacity is always one). */
1017
+ declare function videoClipAnimationProperties(animation: VideoClipAnimation): Prop[];
1018
+ /** Entrance and exit lengths (ms), shortened in proportion when together they are longer than the clip. */
1019
+ declare function videoClipAnimationSpans(clip: VideoClip): {
1020
+ in: number;
1021
+ out: number;
1022
+ };
1023
+ /**
1024
+ * The clip with its entrance and exit animations written out as keyframes on opacity, x, y and
1025
+ * scale, so previews, SVG frames and the ffmpeg export all play them the same way. A property the
1026
+ * clip already keys itself is left alone (`checkVideoProject` notes it). Returns the clip unchanged
1027
+ * when it has no animations.
1028
+ */
1029
+ declare function videoClipAnimated(clip: VideoClip, project: Pick<VideoProject, 'width' | 'height'>): VideoClip;
1030
+
1031
+ /** Radius of an iris `progress` (0–1) open: from nothing to the frame's corners. */
1032
+ declare const videoIrisRadius: (frame: {
1033
+ width: number;
1034
+ height: number;
1035
+ }, progress: number) => number;
687
1036
  /** The part of a rect a wipe shows. */
688
1037
  declare function videoWipeRect(rect: VideoRect, wipe: NonNullable<VideoLayer['wipe']>): VideoRect;
689
1038
  interface VideoSvgOptions {
@@ -705,8 +1054,10 @@ declare function videoContactSheetSvg(project: VideoProject, times: readonly num
705
1054
  width?: number;
706
1055
  }): string;
707
1056
  /** The subset of `CanvasRenderingContext2D` `drawVideoFrame` uses (so tests and workers can pass their own). */
708
- type VideoCanvasContext = Pick<CanvasRenderingContext2D, 'save' | 'restore' | 'fillRect' | 'drawImage' | 'translate' | 'rotate' | 'beginPath' | 'rect' | 'clip' | 'fillText' | 'measureText' | 'setTransform'> & {
1057
+ type VideoCanvasContext = Pick<CanvasRenderingContext2D, 'save' | 'restore' | 'fillRect' | 'drawImage' | 'translate' | 'rotate' | 'beginPath' | 'rect' | 'arc' | 'clip' | 'fillText' | 'measureText' | 'setTransform'> & {
709
1058
  globalAlpha: number;
1059
+ globalCompositeOperation: GlobalCompositeOperation;
1060
+ filter?: string;
710
1061
  fillStyle: string | CanvasGradient | CanvasPattern;
711
1062
  font: string;
712
1063
  textAlign: CanvasTextAlign;
@@ -718,21 +1069,27 @@ type VideoCanvasContext = Pick<CanvasRenderingContext2D, 'save' | 'restore' | 'f
718
1069
  /**
719
1070
  * Draws a frame on a 2D canvas scaled by `scale` (canvas pixels per frame pixel). `sourceFor`
720
1071
  * returns what to draw for a media layer (a `<video>` seeked to `layer.sourceTime`, an `<img>`,
721
- * an `ImageBitmap`…) or `null` for a placeholder box.
1072
+ * an `ImageBitmap`…) or `null` for a placeholder box. Grades become the context's `filter`; white
1073
+ * balance there refers to an SVG filter, which `options.defineSvgFilter` is given to put in the page
1074
+ * (without it, white balance is left out of the canvas).
722
1075
  */
723
- declare function drawVideoFrame(ctx: VideoCanvasContext, frame: VideoFrame, sourceFor: (layer: VideoLayer) => CanvasImageSource | null, scale?: number): void;
1076
+ declare function drawVideoFrame(ctx: VideoCanvasContext, frame: VideoFrame, sourceFor: (layer: VideoLayer) => CanvasImageSource | null, scale?: number, options?: {
1077
+ defineSvgFilter?: (id: string, markup: string) => void;
1078
+ }): void;
724
1079
 
725
1080
  interface VideoEdlOptions {
726
1081
  /** Track to export (default: the bottom video track, V1 in most NLEs). */
727
1082
  track?: string;
728
- /** Record timecode of the timeline's start (ms, default 1 hour, the usual 01:00:00:00). */
1083
+ /** Record timecode of the timeline's start (ms; default timecode 01:00:00:00, the usual start). */
729
1084
  recordStart?: number;
730
1085
  /** Title line (default: the project's name). */
731
1086
  title?: string;
1087
+ /** Drop-frame timecode (default on at 29.97 and 59.94 fps, where it is the norm; ignored at other rates). */
1088
+ dropFrame?: boolean;
732
1089
  }
733
1090
  /**
734
1091
  * One track as a CMX 3600 edit decision list, for conforming in another NLE: an event per clip with
735
- * source and record timecode at the project's frame rate, dissolves (`D`) for crossfades, dips and
1092
+ * source and record timecode at the project's frame rate (drop-frame at 29.97 and 59.94), dissolves (`D`) for crossfades, dips and
736
1093
  * wipes (`W001`), `M2` lines for speed changes, and `* FROM CLIP NAME` comments. Titles and colour
737
1094
  * clips become black (`BL`) events.
738
1095
  */
@@ -760,7 +1117,42 @@ interface VideoFfmpegOptions {
760
1117
  width: number;
761
1118
  height: number;
762
1119
  }) => string;
1120
+ /** Normalise the mix's loudness with ffmpeg's `loudnorm` (EBU R128). */
1121
+ loudness?: VideoLoudnessTarget;
763
1122
  }
1123
+ /** A loudness to normalise the mix to (`loudnorm`'s I, TP and LRA). */
1124
+ interface VideoLoudnessTarget {
1125
+ /** Integrated loudness, in LUFS (default -14, as streaming services play; -16 for podcasts, -23 for EBU R128 broadcast). */
1126
+ integrated?: number;
1127
+ /** Highest true peak, in dBTP (default -1). */
1128
+ truePeak?: number;
1129
+ /** Loudness range, in LU (default 11). */
1130
+ range?: number;
1131
+ /**
1132
+ * The mix as a first pass measured it (`toVideoLoudnessMeasure` then
1133
+ * `parseVideoLoudnessMeasurement`). With it the second pass applies one fixed gain where it can
1134
+ * (linear), so the dynamics are kept; without it `loudnorm` adjusts as it goes in one pass.
1135
+ */
1136
+ measured?: VideoLoudnessMeasurement;
1137
+ }
1138
+ /** What a measuring pass found (`loudnorm`'s input_i, input_tp, input_lra, input_thresh and target_offset). */
1139
+ interface VideoLoudnessMeasurement {
1140
+ /** Integrated loudness (LUFS). */
1141
+ integrated: number;
1142
+ /** True peak (dBTP). */
1143
+ truePeak: number;
1144
+ /** Loudness range (LU). */
1145
+ range: number;
1146
+ /** Gating threshold (LUFS). */
1147
+ threshold: number;
1148
+ /** Offset for the second pass (LU). */
1149
+ offset: number;
1150
+ }
1151
+ /**
1152
+ * Reads the loudness a measuring pass printed (the JSON `loudnorm` writes to stderr at the end of
1153
+ * `toVideoLoudnessMeasure`'s command). Throws when there is none, or when the mix is silent.
1154
+ */
1155
+ declare function parseVideoLoudnessMeasurement(output: string): VideoLoudnessMeasurement;
764
1156
  interface VideoFfmpegExport {
765
1157
  /** Arguments after `ffmpeg` (pass to `spawn('ffmpeg', args)`; no shell quoting needed). */
766
1158
  args: string[];
@@ -782,6 +1174,13 @@ declare const ffmpegFilterValue: (s: string) => string;
782
1174
  * mixed and trimmed to the project's length. Render on a server or with ffmpeg.wasm.
783
1175
  */
784
1176
  declare function toVideoFfmpeg(project: VideoProject, options?: VideoFfmpegOptions): VideoFfmpegExport;
1177
+ /**
1178
+ * The first pass of a two-pass loudness normalisation: an ffmpeg command that mixes only the sound,
1179
+ * as `toVideoFfmpeg` would, and prints its loudness as JSON on stderr instead of writing a file.
1180
+ * Read it with `parseVideoLoudnessMeasurement` and pass it as `loudness.measured` to
1181
+ * `toVideoFfmpeg`. Throws when nothing on the timeline makes a sound.
1182
+ */
1183
+ declare function toVideoLoudnessMeasure(project: VideoProject, options?: Pick<VideoFfmpegOptions, 'resolve' | 'loudness'>): VideoFfmpegExport;
785
1184
 
786
1185
  /** What a preview follows: a `VideoEditorController` fits. */
787
1186
  interface VideoPreviewSource {
@@ -826,6 +1225,11 @@ interface VideoPreviewOptions {
826
1225
  context?: VideoCanvasContext | null;
827
1226
  /** Called after each frame is drawn. */
828
1227
  onFrame?: (frame: VideoFrame) => void;
1228
+ /**
1229
+ * Puts an SVG filter (white balance in a grade) where the canvas's `filter: url(#id)` can find
1230
+ * it. Default: a hidden `<svg>` added to `document.body`. `null` leaves white balance out.
1231
+ */
1232
+ defineSvgFilter?: ((id: string, markup: string) => void) | null;
829
1233
  }
830
1234
  /**
831
1235
  * Plays a project in the browser: keeps one `<video>` or `<audio>` element per playing clip in step
@@ -843,6 +1247,8 @@ declare class VideoPreviewEngine {
843
1247
  private readonly unsubscribe;
844
1248
  private scheduled;
845
1249
  private destroyed;
1250
+ private filterHost;
1251
+ private readonly filters;
846
1252
  constructor(source: VideoPreviewSource, canvas: {
847
1253
  width: number;
848
1254
  height: number;
@@ -858,6 +1264,7 @@ declare class VideoPreviewEngine {
858
1264
  private redraw;
859
1265
  private sourceFor;
860
1266
  private draw;
1267
+ private readonly defineSvgFilter;
861
1268
  /** Stops following the editor and releases every media element. */
862
1269
  destroy(): void;
863
1270
  }
@@ -872,6 +1279,11 @@ interface VideoEditorOptions {
872
1279
  readOnly?: boolean;
873
1280
  /** Called after every change to the project (not on playback or selection). */
874
1281
  onChange?: (project: VideoProject) => void;
1282
+ /**
1283
+ * Where sound levels for waveforms and `get_video_audio_levels` come from: a shared cache, or a
1284
+ * loader for one (default: fetch and decode each asset's `src` with Web Audio, in a browser).
1285
+ */
1286
+ audioPeaks?: VideoAudioPeaksCache | VideoAudioPeaksLoader;
875
1287
  }
876
1288
  /** A snapshot of the editor. The same object is returned until something changes. */
877
1289
  interface VideoEditorState {
@@ -909,6 +1321,8 @@ interface VideoEditorState {
909
1321
  declare class VideoEditorController {
910
1322
  readonly history: HistoryManager;
911
1323
  readonly player: AnimationPlayer;
1324
+ /** Sound levels per asset, loaded on demand for waveforms and agents. */
1325
+ readonly audioPeaks: VideoAudioPeaksCache;
912
1326
  private project;
913
1327
  private issues;
914
1328
  private selection;
@@ -985,6 +1399,8 @@ declare class VideoEditorController {
985
1399
  toSvg(time?: number, options?: VideoSvgOptions): string;
986
1400
  /** The project as an ffmpeg command. */
987
1401
  toFfmpeg(options?: VideoFfmpegOptions): VideoFfmpegExport;
1402
+ /** The ffmpeg command that measures the mix's loudness (see `toVideoLoudnessMeasure`). */
1403
+ toLoudnessMeasure(options?: Pick<VideoFfmpegOptions, 'resolve' | 'loudness'>): VideoFfmpegExport;
988
1404
  /** One track as a CMX 3600 EDL. */
989
1405
  toEdl(options?: VideoEdlOptions): string;
990
1406
  }
@@ -1024,6 +1440,8 @@ declare function describeVideoProject(project: VideoProject): {
1024
1440
  speed?: number;
1025
1441
  volume?: VideoAnimatable;
1026
1442
  muted?: boolean;
1443
+ fadeInCurve?: VideoFadeCurve;
1444
+ fadeOutCurve?: VideoFadeCurve;
1027
1445
  id: string;
1028
1446
  name?: string;
1029
1447
  start: number;
@@ -1031,6 +1449,10 @@ declare function describeVideoProject(project: VideoProject): {
1031
1449
  opacity?: VideoAnimatable;
1032
1450
  transform?: VideoTransform;
1033
1451
  fit?: VideoFit;
1452
+ blend?: VideoBlendMode;
1453
+ grade?: VideoGrade;
1454
+ animateIn?: VideoClipAnimation;
1455
+ animateOut?: VideoClipAnimation;
1034
1456
  fadeIn?: number;
1035
1457
  fadeOut?: number;
1036
1458
  transition?: VideoTransition;
@@ -1054,6 +1476,10 @@ declare function describeVideoProject(project: VideoProject): {
1054
1476
  opacity?: VideoAnimatable;
1055
1477
  transform?: VideoTransform;
1056
1478
  fit?: VideoFit;
1479
+ blend?: VideoBlendMode;
1480
+ grade?: VideoGrade;
1481
+ animateIn?: VideoClipAnimation;
1482
+ animateOut?: VideoClipAnimation;
1057
1483
  fadeIn?: number;
1058
1484
  fadeOut?: number;
1059
1485
  transition?: VideoTransition;
@@ -1072,6 +1498,10 @@ declare function describeVideoProject(project: VideoProject): {
1072
1498
  opacity?: VideoAnimatable;
1073
1499
  transform?: VideoTransform;
1074
1500
  fit?: VideoFit;
1501
+ blend?: VideoBlendMode;
1502
+ grade?: VideoGrade;
1503
+ animateIn?: VideoClipAnimation;
1504
+ animateOut?: VideoClipAnimation;
1075
1505
  fadeIn?: number;
1076
1506
  fadeOut?: number;
1077
1507
  transition?: VideoTransition;
@@ -1116,6 +1546,20 @@ interface VideoRulerTick {
1116
1546
  /** Ruler ticks for `[0, duration]` at `pxPerSecond`: a major tick at least every ~80 px, labelled with timecode. */
1117
1547
  declare function videoRulerTicks(duration: number, pxPerSecond: number, fps: number): VideoRulerTick[];
1118
1548
  /** Fill colour a timeline uses for a clip, by what it is. */
1549
+ /** Which clips a timeline draws waveforms in: audio files only (default), anything with sound, or none. */
1550
+ type VideoWaveformMode = 'audio' | 'all' | 'none';
1551
+ /** Whether a timeline in `mode` draws a waveform in this clip. */
1552
+ declare function videoClipShowsWaveform(clip: VideoClip, asset: VideoAsset | undefined, mode?: VideoWaveformMode): asset is VideoAsset;
1553
+ /** Names for the entrance and exit animations, for menus. */
1554
+ declare const VIDEO_CLIP_ANIMATION_LABELS: Readonly<Record<VideoClipAnimationEffect, string>>;
1555
+ /** Names for the transitions, for menus. */
1556
+ declare const VIDEO_TRANSITION_LABELS: Readonly<Record<VideoTransitionType, string>>;
1557
+ /** Whether a transition type takes a `direction`. */
1558
+ declare const videoTransitionHasDirection: (type: VideoTransitionType) => boolean;
1559
+ /** Names for the blend modes, for menus. */
1560
+ declare const VIDEO_BLEND_MODE_LABELS: Readonly<Record<VideoBlendMode, string>>;
1561
+ /** Names for the sound fade curves, for menus. */
1562
+ declare const VIDEO_FADE_CURVE_LABELS: Readonly<Record<VideoFadeCurve, string>>;
1119
1563
  declare function videoClipTint(clip: VideoClip, asset?: VideoAsset): string;
1120
1564
  /** A short label for a clip on the timeline. */
1121
1565
  declare function videoClipLabel(clip: VideoClip, asset?: VideoAsset): string;
@@ -1211,4 +1655,4 @@ declare function videoKeyframeValueEdit(project: VideoProject, clipId: string, p
1211
1655
  */
1212
1656
  declare function videoKeyframeToggleEdit(project: VideoProject, clipId: string, property: VideoKeyframeProperty, time: number): VideoEdit | null;
1213
1657
 
1214
- export { type CreateVideoProjectOptions, type NewVideoClip, VIDEO_CLIP_TYPES, VIDEO_DEFAULT_STILL_DURATION, VIDEO_DEFAULT_TITLE_DURATION, VIDEO_EASE_NAMES, VIDEO_EDIT_OPS, VIDEO_FITS, VIDEO_ISSUE_CODES, VIDEO_KEYFRAME_LIMITS, VIDEO_KEYFRAME_PROPERTIES, VIDEO_PLACEMENT_MODES, VIDEO_TIME_EPSILON, VIDEO_TRANSFORM_PROPERTIES, VIDEO_TRANSITION_TYPES, VIDEO_WIPE_DIRECTIONS, type VideoAnimatable, type VideoAsset, type VideoAssetKind, type VideoAudioSource, type VideoCanvasContext, type VideoClip, type VideoClipDragInput, type VideoClipDragMode, type VideoClipDragPlan, type VideoClipLocation, type VideoClipType, type VideoColorClip, type VideoEase, type VideoEdit, type VideoEditOp, type VideoEditOutcome, type VideoEditResult, VideoEditorController, type VideoEditorOptions, type VideoEditorState, type VideoEditorToolProviderOptions, type VideoEdlOptions, type VideoFfmpegExport, type VideoFfmpegOptions, type VideoFit, type VideoFrame, type VideoGap, type VideoIssue, type VideoIssueCode, type VideoIssueSeverity, type VideoKey, type VideoKeyframeProperty, type VideoKeyframeRow, type VideoKeyframes, type VideoLayer, type VideoMarker, type VideoMediaClip, type VideoMediaProbe, type VideoPlacementMode, VideoPreviewEngine, type VideoPreviewImage, type VideoPreviewMedia, type VideoPreviewOptions, type VideoPreviewSource, type VideoProject, type VideoRect, type VideoRulerTick, type VideoSnapOptions, type VideoSvgOptions, type VideoTextClip, type VideoTrack, type VideoTrackKind, type VideoTransform, type VideoTransition, type VideoTransitionType, type VideoWipeDirection, allVideoClipIds, applyVideoEdits, checkVideoProject, createVideoEditorToolProvider, createVideoProject, describeVideoProject, drawVideoFrame, ffmpegFilterValue, findVideoClip, findVideoGaps, formatVideoSeconds, formatVideoTimecode, isVideoKeyframes, nextFreeVideoSlot, parseVideoAnimatable, parseVideoAsset, parseVideoClip, parseVideoKeyframes, parseVideoMarker, parseVideoProject, parseVideoTrack, parseVideoTransform, parseVideoTransition, placeVideoRect, planVideoClipDrag, probeVideoMedia, removeVideoKey, retimeVideoAnimatable, setVideoKey, snapToVideoFrame, snapVideoTime, sortVideoKeys, sortedVideoClips, splitVideoAnimatable, toVideoEdl, toVideoFfmpeg, videoAssetDropEdit, videoAssetKindOf, videoAssetMap, videoClipAfter, videoClipBefore, videoClipEnd, videoClipHandles, videoClipKeyframes, videoClipLabel, videoClipOverhang, videoClipSourceRange, videoClipSpeed, videoClipTint, videoClipsAt, videoContactSheetSvg, videoDragLabel, videoDropTracks, videoEaseBezier, videoEaseProgress, videoFrameAt, videoFrameDuration, videoFrameToSvg, videoFrames, videoIssueKey, videoKeyframeBreakpoints, videoKeyframeFfmpegExpr, videoKeyframeRows, videoKeyframeToggleEdit, videoKeyframeValueEdit, videoKeysOf, videoOverlap, videoProjectDuration, videoProjectSvg, videoRulerTicks, videoSnapPoints, videoTextStyle, videoTitleFrame, videoTrackSyncLocked, videoTransformAt, videoValueAt, videoWipeRect };
1658
+ export { type CreateVideoProjectOptions, type NewVideoClip, VIDEO_BLEND_MODES, VIDEO_BLEND_MODE_LABELS, VIDEO_CLIP_ANIMATION_DURATION, VIDEO_CLIP_ANIMATION_EFFECTS, VIDEO_CLIP_ANIMATION_LABELS, VIDEO_CLIP_TYPES, VIDEO_DEFAULT_STILL_DURATION, VIDEO_DEFAULT_TITLE_DURATION, VIDEO_EASE_NAMES, VIDEO_EDIT_OPS, VIDEO_FADE_CURVES, VIDEO_FADE_CURVE_LABELS, VIDEO_FITS, VIDEO_GRADE_CONTROLS, VIDEO_GRADE_PRESETS, VIDEO_GRADE_PRESET_NAMES, VIDEO_ISSUE_CODES, VIDEO_KEYFRAME_LIMITS, VIDEO_KEYFRAME_PROPERTIES, VIDEO_PEAK_BLOCK, VIDEO_PLACEMENT_MODES, VIDEO_TIME_EPSILON, VIDEO_TRANSFORM_PROPERTIES, VIDEO_TRANSITION_LABELS, VIDEO_TRANSITION_TYPES, VIDEO_WIPE_DIRECTIONS, type VideoAnimatable, type VideoAsset, type VideoAssetKind, type VideoAudioLevel, type VideoAudioPeaks, VideoAudioPeaksCache, type VideoAudioPeaksLoader, type VideoAudioSource, type VideoBlendMode, type VideoCanvasContext, type VideoClip, type VideoClipAnimation, type VideoClipAnimationEffect, type VideoClipDragInput, type VideoClipDragMode, type VideoClipDragPlan, type VideoClipLocation, type VideoClipType, type VideoColorClip, type VideoEase, type VideoEdit, type VideoEditOp, type VideoEditOutcome, type VideoEditResult, VideoEditorController, type VideoEditorOptions, type VideoEditorState, type VideoEditorToolProviderOptions, type VideoEdlOptions, type VideoFadeCurve, type VideoFfmpegExport, type VideoFfmpegOptions, type VideoFit, type VideoFrame, type VideoGap, type VideoGrade, type VideoGradeControl, type VideoGradePreset, type VideoGradeStep, type VideoIssue, type VideoIssueCode, type VideoIssueSeverity, type VideoKey, type VideoKeyframeProperty, type VideoKeyframeRow, type VideoKeyframes, type VideoLayer, type VideoLoudnessMeasurement, type VideoLoudnessTarget, type VideoMarker, type VideoMediaClip, type VideoMediaProbe, type VideoPlacementMode, VideoPreviewEngine, type VideoPreviewImage, type VideoPreviewMedia, type VideoPreviewOptions, type VideoPreviewSource, type VideoProject, type VideoRect, type VideoRulerTick, type VideoSilenceOptions, type VideoSnapOptions, type VideoSvgOptions, type VideoTextClip, type VideoTrack, type VideoTrackKind, type VideoTransform, type VideoTransition, type VideoTransitionType, type VideoWaveformMode, type VideoWipeDirection, allVideoClipIds, applyVideoEdits, applyVideoGrade, buildVideoAudioPeaks, checkVideoProject, createVideoEditorToolProvider, createVideoProject, decodeVideoAudioPeaks, describeVideoProject, drawVideoFrame, ffmpegFilterValue, findVideoAudioSilences, findVideoClip, findVideoGaps, formatVideoSeconds, formatVideoTimecode, formatVideoTimecodeFrames, isVideoDropFrameRate, isVideoKeyframes, nextFreeVideoSlot, normalizeVideoGrade, parseVideoAnimatable, parseVideoAsset, parseVideoClip, parseVideoClipAnimation, parseVideoGrade, parseVideoKeyframes, parseVideoLoudnessMeasurement, parseVideoMarker, parseVideoProject, parseVideoTrack, parseVideoTransform, parseVideoTransition, placeVideoRect, planVideoClipDrag, probeVideoMedia, removeVideoKey, retimeVideoAnimatable, setVideoKey, snapToVideoFrame, snapVideoTime, sortVideoKeys, sortedVideoClips, splitVideoAnimatable, toVideoEdl, toVideoFfmpeg, toVideoLoudnessMeasure, videoAssetDropEdit, videoAssetHasSound, videoAssetKindOf, videoAssetMap, videoAudioLevels, videoAudioPeakRange, videoAudioPeakSpans, videoAudioPeaksDuration, videoClipAfter, videoClipAnimated, videoClipAnimationProperties, videoClipAnimationSpans, videoClipBefore, videoClipEnd, videoClipHandles, videoClipKeyframes, videoClipLabel, videoClipOverhang, videoClipShowsWaveform, videoClipSoundGain, videoClipSourceRange, videoClipSpeed, videoClipTint, videoClipWaveform, videoClipWaveformPath, videoClipsAt, videoContactSheetSvg, videoDragLabel, videoDropTracks, videoEaseBezier, videoEaseProgress, videoFadeCurveGain, videoFrameAt, videoFrameDuration, videoFrameToSvg, videoFrames, videoGradeCssFilter, videoGradeFfmpegFilters, videoGradeGainsFilterId, videoGradePreset, videoGradeStepMatrix, videoGradeStepSvgValues, videoGradeSteps, videoGradeSvgDefs, videoGradeSvgFilter, videoHourFrames, videoIrisRadius, videoIssueKey, videoKeyframeBreakpoints, videoKeyframeFfmpegExpr, videoKeyframeRows, videoKeyframeToggleEdit, videoKeyframeValueEdit, videoKeysOf, videoOverlap, videoPeakDb, videoProjectDuration, videoProjectSvg, videoRulerTicks, videoSnapPoints, videoTextStyle, videoTimecodeFrames, videoTitleFrame, videoTrackSyncLocked, videoTransformAt, videoTransitionAudioCurve, videoTransitionEase, videoTransitionHasDirection, videoValueAt, videoWaveformPath, videoWipeRect };