@designcombo/video 0.0.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 +63 -0
- package/dist/SharedSystems-BSw9neqH.js +2691 -0
- package/dist/WebGLRenderer-BrabW-VK.js +2639 -0
- package/dist/WebGPURenderer-BKwBKkzk.js +1655 -0
- package/dist/browserAll-C7HVZtqZ.js +1876 -0
- package/dist/clips/audio-clip.d.ts +132 -0
- package/dist/clips/base-clip.d.ts +86 -0
- package/dist/clips/caption-clip.d.ts +257 -0
- package/dist/clips/iclip.d.ts +120 -0
- package/dist/clips/image-clip.d.ts +110 -0
- package/dist/clips/index.d.ts +8 -0
- package/dist/clips/text-clip.d.ts +192 -0
- package/dist/clips/video-clip.d.ts +200 -0
- package/dist/colorToUniform-Du0ROyNd.js +274 -0
- package/dist/compositor.d.ts +111 -0
- package/dist/index-CjzowIhV.js +28270 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.es.js +20 -0
- package/dist/index.umd.js +1295 -0
- package/dist/internal-utils/event-tool.d.ts +50 -0
- package/dist/internal-utils/index.d.ts +14 -0
- package/dist/internal-utils/log.d.ts +34 -0
- package/dist/internal-utils/meta-box.d.ts +1 -0
- package/dist/internal-utils/recodemux.d.ts +65 -0
- package/dist/internal-utils/stream-utils.d.ts +43 -0
- package/dist/internal-utils/worker-timer.d.ts +8 -0
- package/dist/json-serialization.d.ts +142 -0
- package/dist/mp4-utils/index.d.ts +31 -0
- package/dist/mp4-utils/mp4box-utils.d.ts +36 -0
- package/dist/mp4-utils/sample-transform.d.ts +23 -0
- package/dist/sprite/base-sprite.d.ts +147 -0
- package/dist/sprite/pixi-sprite-renderer.d.ts +48 -0
- package/dist/studio.d.ts +142 -0
- package/dist/transfomer/parts/handle.d.ts +17 -0
- package/dist/transfomer/parts/wireframe.d.ts +5 -0
- package/dist/transfomer/transformer.d.ts +21 -0
- package/dist/utils/audio.d.ts +82 -0
- package/dist/utils/chromakey.d.ts +24 -0
- package/dist/utils/color.d.ts +4 -0
- package/dist/utils/common.d.ts +7 -0
- package/dist/utils/dom.d.ts +48 -0
- package/dist/utils/fonts.d.ts +16 -0
- package/dist/utils/index.d.ts +5 -0
- package/dist/utils/srt-parser.d.ts +15 -0
- package/dist/utils/video.d.ts +18 -0
- package/dist/webworkerAll-DsE6HIYE.js +2497 -0
- package/package.json +53 -0
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import { BaseClip } from './base-clip';
|
|
2
|
+
import { IClip, IPlaybackCapable } from './iclip';
|
|
3
|
+
import { AudioClipJSON } from '../json-serialization';
|
|
4
|
+
interface IAudioClipOpts {
|
|
5
|
+
loop?: boolean;
|
|
6
|
+
volume?: number;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Audio clip providing audio data for creating and editing audio/video
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* // Load audio clip asynchronously
|
|
13
|
+
* const audioClip = await AudioClip.fromUrl('path/to/audio.mp3', {
|
|
14
|
+
* loop: true,
|
|
15
|
+
* });
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* // Traditional approach (for advanced use)
|
|
19
|
+
* new AudioClip((await fetch('<mp3 url>')).body, {
|
|
20
|
+
* loop: true,
|
|
21
|
+
* }),
|
|
22
|
+
*/
|
|
23
|
+
export declare class AudioClip extends BaseClip implements IPlaybackCapable {
|
|
24
|
+
static ctx: AudioContext | null;
|
|
25
|
+
ready: IClip['ready'];
|
|
26
|
+
private _meta;
|
|
27
|
+
/**
|
|
28
|
+
* Audio metadata
|
|
29
|
+
*
|
|
30
|
+
* ⚠️ Note, these are converted (normalized) metadata, not original audio metadata
|
|
31
|
+
*/
|
|
32
|
+
get meta(): {
|
|
33
|
+
sampleRate: 48000;
|
|
34
|
+
chanCount: number;
|
|
35
|
+
duration: number;
|
|
36
|
+
width: number;
|
|
37
|
+
height: number;
|
|
38
|
+
};
|
|
39
|
+
private chan0Buf;
|
|
40
|
+
private chan1Buf;
|
|
41
|
+
/**
|
|
42
|
+
* Get complete PCM data from audio clip
|
|
43
|
+
*/
|
|
44
|
+
getPCMData(): Float32Array[];
|
|
45
|
+
private opts;
|
|
46
|
+
/**
|
|
47
|
+
* Whether to loop the audio (hybrid JSON structure)
|
|
48
|
+
*/
|
|
49
|
+
loop: boolean;
|
|
50
|
+
/**
|
|
51
|
+
* Audio volume level (0-1) (hybrid JSON structure)
|
|
52
|
+
*/
|
|
53
|
+
volume: number;
|
|
54
|
+
/**
|
|
55
|
+
* Load an audio clip from a URL
|
|
56
|
+
* @param url Audio URL
|
|
57
|
+
* @param opts Audio configuration (loop, volume)
|
|
58
|
+
* @returns Promise that resolves to an audio clip
|
|
59
|
+
*
|
|
60
|
+
* @example
|
|
61
|
+
* const audioClip = await AudioClip.fromUrl('path/to/audio.mp3', {
|
|
62
|
+
* loop: true,
|
|
63
|
+
* volume: 0.8,
|
|
64
|
+
* });
|
|
65
|
+
*/
|
|
66
|
+
static fromUrl(url: string, opts?: IAudioClipOpts): Promise<AudioClip>;
|
|
67
|
+
/**
|
|
68
|
+
* Create an AudioClip instance from a JSON object (fabric.js pattern)
|
|
69
|
+
* @param json The JSON object representing the clip
|
|
70
|
+
* @returns Promise that resolves to an AudioClip instance
|
|
71
|
+
*/
|
|
72
|
+
static fromObject(json: AudioClipJSON): Promise<AudioClip>;
|
|
73
|
+
/**
|
|
74
|
+
*
|
|
75
|
+
* @param dataSource Audio file stream
|
|
76
|
+
* @param opts Audio configuration, controls volume and whether to loop
|
|
77
|
+
*/
|
|
78
|
+
constructor(dataSource: ReadableStream<Uint8Array> | Float32Array[], opts?: IAudioClipOpts, src?: string);
|
|
79
|
+
private init;
|
|
80
|
+
/**
|
|
81
|
+
* Intercept data returned by {@link AudioClip.tick} method for secondary processing of audio data
|
|
82
|
+
* @param time Time when tick was called
|
|
83
|
+
* @param tickRet Data returned by tick
|
|
84
|
+
*
|
|
85
|
+
* @see [Remove video green screen background](https://webav-tech.github.io/WebAV/demo/3_2-chromakey-video)
|
|
86
|
+
*/
|
|
87
|
+
tickInterceptor: <T extends Awaited<ReturnType<AudioClip['tick']>>>(time: number, tickRet: T) => Promise<T>;
|
|
88
|
+
private timestamp;
|
|
89
|
+
private frameOffset;
|
|
90
|
+
/**
|
|
91
|
+
* Return audio PCM data corresponding to the time difference between last and current moments
|
|
92
|
+
*
|
|
93
|
+
* If the difference exceeds 3s or current time is less than last time, reset state
|
|
94
|
+
* @example
|
|
95
|
+
* tick(0) // => []
|
|
96
|
+
* tick(1e6) // => [leftChanPCM(1s), rightChanPCM(1s)]
|
|
97
|
+
*
|
|
98
|
+
*/
|
|
99
|
+
tick(time: number): Promise<{
|
|
100
|
+
audio: Float32Array[];
|
|
101
|
+
state: 'success' | 'done';
|
|
102
|
+
}>;
|
|
103
|
+
/**
|
|
104
|
+
* Split at specified time, return two audio clips before and after
|
|
105
|
+
* @param time Time in microseconds
|
|
106
|
+
*/
|
|
107
|
+
split(time: number): Promise<[this, this]>;
|
|
108
|
+
clone(): Promise<this>;
|
|
109
|
+
/**
|
|
110
|
+
* Destroy instance and release resources
|
|
111
|
+
*/
|
|
112
|
+
destroy(): void;
|
|
113
|
+
toJSON(main?: boolean): AudioClipJSON;
|
|
114
|
+
static concatAudioClip: typeof concatAudioClip;
|
|
115
|
+
/**
|
|
116
|
+
* Create HTMLAudioElement for playback
|
|
117
|
+
*/
|
|
118
|
+
createPlaybackElement(): Promise<{
|
|
119
|
+
element: HTMLAudioElement;
|
|
120
|
+
objectUrl?: string;
|
|
121
|
+
}>;
|
|
122
|
+
play(element: HTMLVideoElement | HTMLAudioElement, timeSeconds: number): Promise<void>;
|
|
123
|
+
pause(element: HTMLVideoElement | HTMLAudioElement): void;
|
|
124
|
+
seek(element: HTMLVideoElement | HTMLAudioElement, timeSeconds: number): Promise<void>;
|
|
125
|
+
syncPlayback(element: HTMLVideoElement | HTMLAudioElement, isPlaying: boolean, timeSeconds: number): void;
|
|
126
|
+
cleanupPlayback(element: HTMLVideoElement | HTMLAudioElement, objectUrl?: string): void;
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Concatenate multiple AudioClips
|
|
130
|
+
*/
|
|
131
|
+
export declare function concatAudioClip(clips: AudioClip[], opts?: IAudioClipOpts): Promise<AudioClip>;
|
|
132
|
+
export {};
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { BaseSprite } from '../sprite/base-sprite';
|
|
2
|
+
import { IClip, IClipMeta } from './iclip';
|
|
3
|
+
import { ClipJSON } from '../json-serialization';
|
|
4
|
+
/**
|
|
5
|
+
* Base class for all clips that extends BaseSprite
|
|
6
|
+
* Provides common functionality for sprite operations (position, animation, timing)
|
|
7
|
+
* and frame management
|
|
8
|
+
*/
|
|
9
|
+
export declare abstract class BaseClip extends BaseSprite implements IClip {
|
|
10
|
+
private lastVf;
|
|
11
|
+
protected destroyed: boolean;
|
|
12
|
+
/**
|
|
13
|
+
* Source URL or identifier for this clip
|
|
14
|
+
* Used for serialization and reloading from JSON
|
|
15
|
+
*/
|
|
16
|
+
src: string;
|
|
17
|
+
abstract tick(time: number): Promise<{
|
|
18
|
+
video?: VideoFrame | ImageBitmap | null;
|
|
19
|
+
audio?: Float32Array[];
|
|
20
|
+
state: 'done' | 'success';
|
|
21
|
+
}>;
|
|
22
|
+
ready: Promise<IClipMeta>;
|
|
23
|
+
abstract readonly meta: IClipMeta;
|
|
24
|
+
abstract clone(): Promise<this>;
|
|
25
|
+
abstract split?(time: number): Promise<[this, this]>;
|
|
26
|
+
constructor();
|
|
27
|
+
/**
|
|
28
|
+
* Get video frame and audio at specified time without rendering to canvas
|
|
29
|
+
* Useful for Pixi.js rendering where canvas context is not needed
|
|
30
|
+
* @param time Specified time in microseconds
|
|
31
|
+
*/
|
|
32
|
+
getFrame(time: number): Promise<{
|
|
33
|
+
video: ImageBitmap | null;
|
|
34
|
+
audio: Float32Array[];
|
|
35
|
+
done: boolean;
|
|
36
|
+
}>;
|
|
37
|
+
/**
|
|
38
|
+
* Draw image at specified time to canvas context and return corresponding audio data
|
|
39
|
+
* @param time Specified time in microseconds
|
|
40
|
+
*/
|
|
41
|
+
offscreenRender(ctx: CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D, time: number): Promise<{
|
|
42
|
+
audio: Float32Array[];
|
|
43
|
+
done: boolean;
|
|
44
|
+
}>;
|
|
45
|
+
/**
|
|
46
|
+
* Set clip properties (position, size, display timeline)
|
|
47
|
+
* @param props Properties to set
|
|
48
|
+
* @param fps Optional FPS for frame-to-time conversion (default: 30)
|
|
49
|
+
* @returns this for method chaining
|
|
50
|
+
*
|
|
51
|
+
* @example
|
|
52
|
+
* // Using frames (will be converted to microseconds)
|
|
53
|
+
* clip.set({
|
|
54
|
+
* display: {
|
|
55
|
+
* from: 150, // frames
|
|
56
|
+
* to: 450, // frames (10 seconds at 30fps)
|
|
57
|
+
* },
|
|
58
|
+
* }, 30);
|
|
59
|
+
*
|
|
60
|
+
* // Using microseconds directly
|
|
61
|
+
* clip.set({
|
|
62
|
+
* display: {
|
|
63
|
+
* from: 5000000, // microseconds
|
|
64
|
+
* to: 15000000, // microseconds
|
|
65
|
+
* },
|
|
66
|
+
* });
|
|
67
|
+
*/
|
|
68
|
+
set(props: {
|
|
69
|
+
display?: {
|
|
70
|
+
from?: number;
|
|
71
|
+
to?: number;
|
|
72
|
+
};
|
|
73
|
+
x?: number;
|
|
74
|
+
y?: number;
|
|
75
|
+
width?: number;
|
|
76
|
+
height?: number;
|
|
77
|
+
duration?: number;
|
|
78
|
+
}, fps?: number): this;
|
|
79
|
+
/**
|
|
80
|
+
* Base implementation of toJSON that handles common clip properties
|
|
81
|
+
* Subclasses should override to add their specific options
|
|
82
|
+
* @param main Whether this is the main clip (for Compositor)
|
|
83
|
+
*/
|
|
84
|
+
toJSON(main?: boolean): ClipJSON;
|
|
85
|
+
destroy(): void;
|
|
86
|
+
}
|
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
import { BaseClip } from './base-clip';
|
|
2
|
+
import { IClip } from './iclip';
|
|
3
|
+
import { CaptionClipJSON } from '../json-serialization';
|
|
4
|
+
import { Application, Texture } from 'pixi.js';
|
|
5
|
+
export interface ICaptionClipOpts {
|
|
6
|
+
/**
|
|
7
|
+
* Font size in pixels
|
|
8
|
+
* @default 30
|
|
9
|
+
*/
|
|
10
|
+
fontSize?: number;
|
|
11
|
+
/**
|
|
12
|
+
* Font family
|
|
13
|
+
* @default 'Arial'
|
|
14
|
+
*/
|
|
15
|
+
fontFamily?: string;
|
|
16
|
+
fontUrl?: string;
|
|
17
|
+
/**
|
|
18
|
+
* Font weight (e.g., 'normal', 'bold', '400', '700')
|
|
19
|
+
* @default 'normal'
|
|
20
|
+
*/
|
|
21
|
+
fontWeight?: string;
|
|
22
|
+
/**
|
|
23
|
+
* Font style (e.g., 'normal', 'italic')
|
|
24
|
+
* @default 'normal'
|
|
25
|
+
*/
|
|
26
|
+
fontStyle?: string;
|
|
27
|
+
/**
|
|
28
|
+
* Text color (hex string, color name, or gradient object)
|
|
29
|
+
* @default '#ffffff'
|
|
30
|
+
*/
|
|
31
|
+
fill?: string | number | {
|
|
32
|
+
type: 'gradient';
|
|
33
|
+
x0: number;
|
|
34
|
+
y0: number;
|
|
35
|
+
x1: number;
|
|
36
|
+
y1: number;
|
|
37
|
+
colors: Array<{
|
|
38
|
+
ratio: number;
|
|
39
|
+
color: string | number;
|
|
40
|
+
}>;
|
|
41
|
+
};
|
|
42
|
+
/**
|
|
43
|
+
* Caption data (matches caption object in JSON)
|
|
44
|
+
*/
|
|
45
|
+
caption?: {
|
|
46
|
+
words?: Array<{
|
|
47
|
+
text: string;
|
|
48
|
+
from: number;
|
|
49
|
+
to: number;
|
|
50
|
+
isKeyWord: boolean;
|
|
51
|
+
}>;
|
|
52
|
+
colors?: {
|
|
53
|
+
appeared?: string;
|
|
54
|
+
active?: string;
|
|
55
|
+
activeFill?: string;
|
|
56
|
+
background?: string;
|
|
57
|
+
keyword?: string;
|
|
58
|
+
};
|
|
59
|
+
preserveKeywordColor?: boolean;
|
|
60
|
+
positioning?: {
|
|
61
|
+
videoWidth?: number;
|
|
62
|
+
videoHeight?: number;
|
|
63
|
+
bottomOffset?: number;
|
|
64
|
+
};
|
|
65
|
+
};
|
|
66
|
+
/**
|
|
67
|
+
* @deprecated Use caption.words instead
|
|
68
|
+
*/
|
|
69
|
+
words?: Array<{
|
|
70
|
+
text: string;
|
|
71
|
+
from: number;
|
|
72
|
+
to: number;
|
|
73
|
+
isKeyWord: boolean;
|
|
74
|
+
}>;
|
|
75
|
+
/**
|
|
76
|
+
* @deprecated Use caption.colors instead
|
|
77
|
+
*/
|
|
78
|
+
colors?: {
|
|
79
|
+
appeared?: string;
|
|
80
|
+
active?: string;
|
|
81
|
+
activeFill?: string;
|
|
82
|
+
background?: string;
|
|
83
|
+
keyword?: string;
|
|
84
|
+
};
|
|
85
|
+
/**
|
|
86
|
+
* @deprecated Use caption.preserveKeywordColor instead
|
|
87
|
+
*/
|
|
88
|
+
preserveKeywordColor?: boolean;
|
|
89
|
+
/**
|
|
90
|
+
* @deprecated Use caption.positioning.videoWidth instead
|
|
91
|
+
*/
|
|
92
|
+
videoWidth?: number;
|
|
93
|
+
/**
|
|
94
|
+
* @deprecated Use caption.positioning.videoHeight instead
|
|
95
|
+
*/
|
|
96
|
+
videoHeight?: number;
|
|
97
|
+
/**
|
|
98
|
+
* @deprecated Use caption.positioning.bottomOffset instead
|
|
99
|
+
*/
|
|
100
|
+
bottomOffset?: number;
|
|
101
|
+
/**
|
|
102
|
+
* Stroke color (hex string or color name) or stroke object with advanced options
|
|
103
|
+
*/
|
|
104
|
+
stroke?: string | number | {
|
|
105
|
+
color: string | number;
|
|
106
|
+
width: number;
|
|
107
|
+
join?: 'miter' | 'round' | 'bevel';
|
|
108
|
+
};
|
|
109
|
+
/**
|
|
110
|
+
* Stroke width in pixels (used when stroke is a simple color)
|
|
111
|
+
* @default 0
|
|
112
|
+
*/
|
|
113
|
+
strokeWidth?: number;
|
|
114
|
+
/**
|
|
115
|
+
* Text alignment ('left', 'center', 'right')
|
|
116
|
+
* @default 'center'
|
|
117
|
+
*/
|
|
118
|
+
align?: 'left' | 'center' | 'right';
|
|
119
|
+
/**
|
|
120
|
+
* Drop shadow configuration
|
|
121
|
+
*/
|
|
122
|
+
dropShadow?: {
|
|
123
|
+
color?: string | number;
|
|
124
|
+
alpha?: number;
|
|
125
|
+
blur?: number;
|
|
126
|
+
angle?: number;
|
|
127
|
+
distance?: number;
|
|
128
|
+
};
|
|
129
|
+
/**
|
|
130
|
+
* Word wrap width (0 = no wrap)
|
|
131
|
+
* @default 0
|
|
132
|
+
*/
|
|
133
|
+
wordWrapWidth?: number;
|
|
134
|
+
/**
|
|
135
|
+
* Word wrap mode ('break-word' or 'normal')
|
|
136
|
+
* @default 'break-word'
|
|
137
|
+
*/
|
|
138
|
+
wordWrap?: boolean;
|
|
139
|
+
/**
|
|
140
|
+
* Line height (multiplier)
|
|
141
|
+
* @default 1
|
|
142
|
+
*/
|
|
143
|
+
lineHeight?: number;
|
|
144
|
+
/**
|
|
145
|
+
* Letter spacing in pixels
|
|
146
|
+
* @default 0
|
|
147
|
+
*/
|
|
148
|
+
letterSpacing?: number;
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Caption clip using Canvas 2D for rendering
|
|
152
|
+
* Each instance represents a single caption segment
|
|
153
|
+
*
|
|
154
|
+
* @example
|
|
155
|
+
* const captionClip = new CaptionClip('Hello World', {
|
|
156
|
+
* fontSize: 44,
|
|
157
|
+
* fontFamily: 'Arial',
|
|
158
|
+
* fill: '#ffffff',
|
|
159
|
+
* videoWidth: 1280,
|
|
160
|
+
* videoHeight: 720,
|
|
161
|
+
* });
|
|
162
|
+
* captionClip.display.from = 0;
|
|
163
|
+
* captionClip.duration = 3e6; // 3 seconds
|
|
164
|
+
*/
|
|
165
|
+
export declare class CaptionClip extends BaseClip implements IClip {
|
|
166
|
+
ready: IClip['ready'];
|
|
167
|
+
private _meta;
|
|
168
|
+
get meta(): {
|
|
169
|
+
duration: number;
|
|
170
|
+
width: number;
|
|
171
|
+
height: number;
|
|
172
|
+
};
|
|
173
|
+
/**
|
|
174
|
+
* Caption text content (hybrid JSON structure)
|
|
175
|
+
*/
|
|
176
|
+
text: string;
|
|
177
|
+
get style(): {
|
|
178
|
+
fontSize?: undefined;
|
|
179
|
+
fontFamily?: undefined;
|
|
180
|
+
fontWeight?: undefined;
|
|
181
|
+
fontStyle?: undefined;
|
|
182
|
+
color?: undefined;
|
|
183
|
+
align?: undefined;
|
|
184
|
+
stroke?: undefined;
|
|
185
|
+
shadow?: undefined;
|
|
186
|
+
} | {
|
|
187
|
+
fontSize: number | undefined;
|
|
188
|
+
fontFamily: string | undefined;
|
|
189
|
+
fontWeight: string | undefined;
|
|
190
|
+
fontStyle: string | undefined;
|
|
191
|
+
color: string | number | {
|
|
192
|
+
type: "gradient";
|
|
193
|
+
x0: number;
|
|
194
|
+
y0: number;
|
|
195
|
+
x1: number;
|
|
196
|
+
y1: number;
|
|
197
|
+
colors: Array<{
|
|
198
|
+
ratio: number;
|
|
199
|
+
color: string | number;
|
|
200
|
+
}>;
|
|
201
|
+
} | undefined;
|
|
202
|
+
align: "center" | "left" | "right" | undefined;
|
|
203
|
+
stroke: {
|
|
204
|
+
color: string | number;
|
|
205
|
+
width: number;
|
|
206
|
+
} | undefined;
|
|
207
|
+
shadow: {
|
|
208
|
+
color: string | number;
|
|
209
|
+
alpha: number;
|
|
210
|
+
blur: number;
|
|
211
|
+
offsetX: number;
|
|
212
|
+
offsetY: number;
|
|
213
|
+
} | undefined;
|
|
214
|
+
};
|
|
215
|
+
/**
|
|
216
|
+
* Bottom offset from video bottom (hybrid JSON structure)
|
|
217
|
+
*/
|
|
218
|
+
bottomOffset?: number;
|
|
219
|
+
private opts;
|
|
220
|
+
private pixiTextContainer;
|
|
221
|
+
private renderTexture;
|
|
222
|
+
private wordTexts;
|
|
223
|
+
private extraPadding;
|
|
224
|
+
private textStyle;
|
|
225
|
+
private externalRenderer;
|
|
226
|
+
private pixiApp;
|
|
227
|
+
private originalOpts;
|
|
228
|
+
constructor(text: string, opts?: ICaptionClipOpts, renderer?: Application['renderer']);
|
|
229
|
+
private lastLoggedTime;
|
|
230
|
+
updateState(currentTime: number): void;
|
|
231
|
+
/**
|
|
232
|
+
* Get the PixiJS Texture (RenderTexture) for optimized rendering in Studio
|
|
233
|
+
* This avoids ImageBitmap → Canvas → Texture conversion
|
|
234
|
+
*
|
|
235
|
+
* @returns The RenderTexture containing the rendered caption, or null if not ready
|
|
236
|
+
*/
|
|
237
|
+
getTexture(): Promise<Texture | null>;
|
|
238
|
+
/**
|
|
239
|
+
* Set an external renderer (e.g., from Studio) to avoid creating our own Pixi App
|
|
240
|
+
*/
|
|
241
|
+
setRenderer(renderer: Application['renderer']): void;
|
|
242
|
+
private getRenderer;
|
|
243
|
+
tick(time: number): Promise<{
|
|
244
|
+
video: ImageBitmap;
|
|
245
|
+
state: 'success';
|
|
246
|
+
}>;
|
|
247
|
+
split(_time: number): Promise<[this, this]>;
|
|
248
|
+
clone(): Promise<this>;
|
|
249
|
+
destroy(): void;
|
|
250
|
+
toJSON(main?: boolean): CaptionClipJSON;
|
|
251
|
+
/**
|
|
252
|
+
* Create a CaptionClip instance from a JSON object (fabric.js pattern)
|
|
253
|
+
* @param json The JSON object representing the clip
|
|
254
|
+
* @returns Promise that resolves to a CaptionClip instance
|
|
255
|
+
*/
|
|
256
|
+
static fromObject(json: CaptionClipJSON): Promise<CaptionClip>;
|
|
257
|
+
}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import { BaseSprite } from '../sprite/base-sprite';
|
|
2
|
+
export interface IClipMeta {
|
|
3
|
+
width: number;
|
|
4
|
+
height: number;
|
|
5
|
+
duration: number;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Interface that all clips must implement
|
|
9
|
+
*
|
|
10
|
+
* Clips are abstractions of different data types, providing data to other modules
|
|
11
|
+
*
|
|
12
|
+
* WebAV provides built-in {@link VideoClip}, {@link AudioClip}, {@link ImageClip} and other common clips for providing data to {@link Compositor} and {@link AVCanvas}
|
|
13
|
+
*
|
|
14
|
+
* You only need to implement this interface to create custom clips, giving you maximum flexibility to generate video content such as animations and transition effects
|
|
15
|
+
* @see [Custom Clip](https://webav-tech.github.io/WebAV/demo/2_6-custom-clip)
|
|
16
|
+
*
|
|
17
|
+
*/
|
|
18
|
+
export interface IClip extends Omit<BaseSprite, 'destroy' | 'ready'> {
|
|
19
|
+
destroy: () => void;
|
|
20
|
+
readonly ready: Promise<IClipMeta>;
|
|
21
|
+
/**
|
|
22
|
+
* Extract data from clip at specified time
|
|
23
|
+
* @param time Time in microseconds
|
|
24
|
+
*/
|
|
25
|
+
tick: (time: number) => Promise<{
|
|
26
|
+
video?: VideoFrame | ImageBitmap | null;
|
|
27
|
+
audio?: Float32Array[];
|
|
28
|
+
state: 'done' | 'success';
|
|
29
|
+
}>;
|
|
30
|
+
/**
|
|
31
|
+
* Get video frame and audio at specified time
|
|
32
|
+
* This method is provided by BaseClip and used by Compositor
|
|
33
|
+
*/
|
|
34
|
+
getFrame(time: number): Promise<{
|
|
35
|
+
video: ImageBitmap | null;
|
|
36
|
+
audio: Float32Array[];
|
|
37
|
+
done: boolean;
|
|
38
|
+
}>;
|
|
39
|
+
/**
|
|
40
|
+
* Data metadata
|
|
41
|
+
*/
|
|
42
|
+
readonly meta: IClipMeta;
|
|
43
|
+
/**
|
|
44
|
+
* Clone and return a new clip
|
|
45
|
+
*/
|
|
46
|
+
clone: () => Promise<this>;
|
|
47
|
+
/**
|
|
48
|
+
* Split at specified time, return two new clips before and after that moment, commonly used in editing scenarios to split clips by time
|
|
49
|
+
*
|
|
50
|
+
* This method will not corrupt original clip data
|
|
51
|
+
*
|
|
52
|
+
* @param time Time in microseconds
|
|
53
|
+
* @returns
|
|
54
|
+
*/
|
|
55
|
+
split?: (time: number) => Promise<[this, this]>;
|
|
56
|
+
/**
|
|
57
|
+
* Serialize clip to JSON format
|
|
58
|
+
* Returns a plain object with only serializable data (no circular references)
|
|
59
|
+
* @param main Whether this is the main clip (for Compositor)
|
|
60
|
+
*/
|
|
61
|
+
toJSON(main?: boolean): any;
|
|
62
|
+
/**
|
|
63
|
+
* Set an external renderer (e.g., from Studio)
|
|
64
|
+
* This is called by Studio when adding clips to provide a shared renderer
|
|
65
|
+
* @param renderer The PixiJS renderer to use
|
|
66
|
+
*/
|
|
67
|
+
setRenderer?(renderer: any): void;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Optional interface for clips that support HTML media element playback
|
|
71
|
+
* Used by Studio for interactive preview
|
|
72
|
+
*/
|
|
73
|
+
export interface IPlaybackCapable {
|
|
74
|
+
/**
|
|
75
|
+
* Create and initialize HTML media element for playback
|
|
76
|
+
* @returns Promise resolving to the media element and optional object URL for cleanup
|
|
77
|
+
*/
|
|
78
|
+
createPlaybackElement(): Promise<{
|
|
79
|
+
element: HTMLVideoElement | HTMLAudioElement;
|
|
80
|
+
objectUrl?: string;
|
|
81
|
+
}>;
|
|
82
|
+
/**
|
|
83
|
+
* Start playback at a specific time
|
|
84
|
+
* @param element The HTML media element
|
|
85
|
+
* @param timeSeconds Time in seconds (relative to clip start)
|
|
86
|
+
*/
|
|
87
|
+
play(element: HTMLVideoElement | HTMLAudioElement, timeSeconds: number): Promise<void>;
|
|
88
|
+
/**
|
|
89
|
+
* Pause playback
|
|
90
|
+
* @param element The HTML media element
|
|
91
|
+
*/
|
|
92
|
+
pause(element: HTMLVideoElement | HTMLAudioElement): void;
|
|
93
|
+
/**
|
|
94
|
+
* Seek to a specific time
|
|
95
|
+
* @param element The HTML media element
|
|
96
|
+
* @param timeSeconds Time in seconds (relative to clip start)
|
|
97
|
+
*/
|
|
98
|
+
seek(element: HTMLVideoElement | HTMLAudioElement, timeSeconds: number): Promise<void>;
|
|
99
|
+
/**
|
|
100
|
+
* Sync playback state during preview
|
|
101
|
+
* @param element The HTML media element
|
|
102
|
+
* @param isPlaying Whether playback should be active
|
|
103
|
+
* @param timeSeconds Current time in seconds (relative to clip start)
|
|
104
|
+
*/
|
|
105
|
+
syncPlayback(element: HTMLVideoElement | HTMLAudioElement, isPlaying: boolean, timeSeconds: number): void;
|
|
106
|
+
/**
|
|
107
|
+
* Clean up playback element and resources
|
|
108
|
+
* @param element The HTML media element
|
|
109
|
+
* @param objectUrl Optional object URL to revoke
|
|
110
|
+
*/
|
|
111
|
+
cleanupPlayback(element: HTMLVideoElement | HTMLAudioElement, objectUrl?: string): void;
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Default audio settings, ⚠️ do not change its values ⚠️
|
|
115
|
+
*/
|
|
116
|
+
export declare const DEFAULT_AUDIO_CONF: {
|
|
117
|
+
readonly sampleRate: 48000;
|
|
118
|
+
readonly channelCount: 2;
|
|
119
|
+
readonly codec: "mp4a.40.2";
|
|
120
|
+
};
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { Texture } from 'pixi.js';
|
|
2
|
+
import { BaseClip } from './base-clip';
|
|
3
|
+
import { IClip } from './iclip';
|
|
4
|
+
import { ClipJSON, ImageClipJSON } from '../json-serialization';
|
|
5
|
+
type AnimateImgType = 'avif' | 'webp' | 'png' | 'gif';
|
|
6
|
+
/**
|
|
7
|
+
* Image clip supporting animated images
|
|
8
|
+
*
|
|
9
|
+
* Ordinary text can be converted to image clip using {@link renderTxt2ImgBitmap}
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* // Load from URL using PixiJS Assets (optimized for Studio)
|
|
13
|
+
* const imgClip = await ImageClip.fromUrl('path/to/image.png');
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* // Traditional approach (for Compositor/export)
|
|
17
|
+
* new ImageClip((await fetch('<img url>')).body);
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* new ImageClip(
|
|
21
|
+
* await renderTxt2ImgBitmap(
|
|
22
|
+
* 'Watermark',
|
|
23
|
+
* `font-size:40px; color: white; text-shadow: 2px 2px 6px red;`,
|
|
24
|
+
* )
|
|
25
|
+
* )
|
|
26
|
+
*
|
|
27
|
+
* @see [Video composition](https://webav-tech.github.io/WebAV/demo/2_1-concat-video)
|
|
28
|
+
*/
|
|
29
|
+
export declare class ImageClip extends BaseClip {
|
|
30
|
+
ready: IClip['ready'];
|
|
31
|
+
private _meta;
|
|
32
|
+
/**
|
|
33
|
+
* ⚠️ Static images have duration of Infinity
|
|
34
|
+
*
|
|
35
|
+
* When wrapping with Sprite, you need to set its duration to a finite number
|
|
36
|
+
*
|
|
37
|
+
*/
|
|
38
|
+
get meta(): {
|
|
39
|
+
duration: number;
|
|
40
|
+
width: number;
|
|
41
|
+
height: number;
|
|
42
|
+
};
|
|
43
|
+
private img;
|
|
44
|
+
private pixiTexture;
|
|
45
|
+
private frames;
|
|
46
|
+
/**
|
|
47
|
+
* Load an image clip from a URL using PixiJS Assets
|
|
48
|
+
* This is optimized for Studio as it uses Texture directly
|
|
49
|
+
*
|
|
50
|
+
* @param url Image URL
|
|
51
|
+
* @param src Optional source identifier for serialization
|
|
52
|
+
* @returns Promise that resolves to an ImageClip instance
|
|
53
|
+
*
|
|
54
|
+
* @example
|
|
55
|
+
* const imgClip = await ImageClip.fromUrl('path/to/image.png');
|
|
56
|
+
*/
|
|
57
|
+
static fromUrl(url: string, src?: string): Promise<ImageClip>;
|
|
58
|
+
/**
|
|
59
|
+
* Get the PixiJS Texture (if available)
|
|
60
|
+
* This is used for optimized rendering in Studio
|
|
61
|
+
*/
|
|
62
|
+
getTexture(): Texture | null;
|
|
63
|
+
/**
|
|
64
|
+
* Static images can be initialized using stream or ImageBitmap
|
|
65
|
+
*
|
|
66
|
+
* Animated images need to use VideoFrame[] or provide image type
|
|
67
|
+
*/
|
|
68
|
+
constructor(dataSource: ReadableStream | ImageBitmap | VideoFrame[] | {
|
|
69
|
+
type: `image/${AnimateImgType}`;
|
|
70
|
+
stream: ReadableStream;
|
|
71
|
+
}, src?: string);
|
|
72
|
+
private initAnimateImg;
|
|
73
|
+
tickInterceptor: <T extends Awaited<ReturnType<ImageClip['tick']>>>(time: number, tickRet: T) => Promise<T>;
|
|
74
|
+
tick(time: number): Promise<{
|
|
75
|
+
video: ImageBitmap | VideoFrame;
|
|
76
|
+
state: 'success';
|
|
77
|
+
}>;
|
|
78
|
+
split(time: number): Promise<[this, this]>;
|
|
79
|
+
clone(): Promise<this>;
|
|
80
|
+
destroy(): void;
|
|
81
|
+
toJSON(main?: boolean): ImageClipJSON;
|
|
82
|
+
/**
|
|
83
|
+
* Create an ImageClip instance from a JSON object (fabric.js pattern)
|
|
84
|
+
* @param json The JSON object representing the clip
|
|
85
|
+
* @returns Promise that resolves to an ImageClip instance
|
|
86
|
+
*/
|
|
87
|
+
static fromObject(json: ClipJSON): Promise<ImageClip>;
|
|
88
|
+
/**
|
|
89
|
+
* Scale clip to fit within the scene dimensions while maintaining aspect ratio
|
|
90
|
+
* Similar to fabric.js scaleToFit
|
|
91
|
+
* @param sceneWidth Scene width
|
|
92
|
+
* @param sceneHeight Scene height
|
|
93
|
+
*/
|
|
94
|
+
scaleToFit(sceneWidth: number, sceneHeight: number): Promise<void>;
|
|
95
|
+
/**
|
|
96
|
+
* Scale clip to fill the scene dimensions while maintaining aspect ratio
|
|
97
|
+
* May crop parts of the clip. Similar to fabric.js scaleToFill
|
|
98
|
+
* @param sceneWidth Scene width
|
|
99
|
+
* @param sceneHeight Scene height
|
|
100
|
+
*/
|
|
101
|
+
scaleToFill(sceneWidth: number, sceneHeight: number): Promise<void>;
|
|
102
|
+
/**
|
|
103
|
+
* Center the clip within the scene dimensions
|
|
104
|
+
* Similar to fabric.js center
|
|
105
|
+
* @param sceneWidth Scene width
|
|
106
|
+
* @param sceneHeight Scene height
|
|
107
|
+
*/
|
|
108
|
+
centerInScene(sceneWidth: number, sceneHeight: number): void;
|
|
109
|
+
}
|
|
110
|
+
export {};
|