node-av 1.3.0 → 2.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/README.md +47 -40
- package/binding.gyp +12 -0
- package/dist/api/bitstream-filter.d.ts +134 -2
- package/dist/api/bitstream-filter.js +200 -2
- package/dist/api/bitstream-filter.js.map +1 -1
- package/dist/api/decoder.d.ts +261 -105
- package/dist/api/decoder.js +384 -171
- package/dist/api/decoder.js.map +1 -1
- package/dist/api/encoder.d.ts +338 -74
- package/dist/api/encoder.js +546 -188
- package/dist/api/encoder.js.map +1 -1
- package/dist/api/filter-presets.d.ts +479 -1513
- package/dist/api/filter-presets.js +1044 -2005
- package/dist/api/filter-presets.js.map +1 -1
- package/dist/api/filter.d.ts +370 -150
- package/dist/api/filter.js +647 -364
- package/dist/api/filter.js.map +1 -1
- package/dist/api/hardware.d.ts +25 -31
- package/dist/api/hardware.js +36 -70
- package/dist/api/hardware.js.map +1 -1
- package/dist/api/index.d.ts +1 -1
- package/dist/api/index.js +1 -1
- package/dist/api/index.js.map +1 -1
- package/dist/api/io-stream.d.ts +6 -0
- package/dist/api/io-stream.js +2 -0
- package/dist/api/io-stream.js.map +1 -1
- package/dist/api/media-input.d.ts +208 -2
- package/dist/api/media-input.js +356 -8
- package/dist/api/media-input.js.map +1 -1
- package/dist/api/media-output.d.ts +142 -104
- package/dist/api/media-output.js +446 -179
- package/dist/api/media-output.js.map +1 -1
- package/dist/api/pipeline.d.ts +82 -17
- package/dist/api/pipeline.js +80 -42
- package/dist/api/pipeline.js.map +1 -1
- package/dist/api/types.d.ts +24 -57
- package/dist/api/utils.js +2 -0
- package/dist/api/utils.js.map +1 -1
- package/dist/lib/audio-fifo.d.ts +103 -0
- package/dist/lib/audio-fifo.js +109 -0
- package/dist/lib/audio-fifo.js.map +1 -1
- package/dist/lib/binding.d.ts +1 -0
- package/dist/lib/binding.js.map +1 -1
- package/dist/lib/bitstream-filter-context.d.ts +79 -0
- package/dist/lib/bitstream-filter-context.js +83 -0
- package/dist/lib/bitstream-filter-context.js.map +1 -1
- package/dist/lib/bitstream-filter.d.ts +2 -0
- package/dist/lib/bitstream-filter.js +2 -0
- package/dist/lib/bitstream-filter.js.map +1 -1
- package/dist/lib/codec-context.d.ts +168 -0
- package/dist/lib/codec-context.js +178 -0
- package/dist/lib/codec-context.js.map +1 -1
- package/dist/lib/codec-parameters.d.ts +3 -0
- package/dist/lib/codec-parameters.js +3 -0
- package/dist/lib/codec-parameters.js.map +1 -1
- package/dist/lib/codec-parser.d.ts +6 -0
- package/dist/lib/codec-parser.js +6 -0
- package/dist/lib/codec-parser.js.map +1 -1
- package/dist/lib/codec.d.ts +12 -0
- package/dist/lib/codec.js +12 -0
- package/dist/lib/codec.js.map +1 -1
- package/dist/lib/dictionary.d.ts +18 -2
- package/dist/lib/dictionary.js +18 -2
- package/dist/lib/dictionary.js.map +1 -1
- package/dist/lib/error.d.ts +8 -0
- package/dist/lib/error.js +9 -0
- package/dist/lib/error.js.map +1 -1
- package/dist/lib/filter-context.d.ts +119 -2
- package/dist/lib/filter-context.js +119 -0
- package/dist/lib/filter-context.js.map +1 -1
- package/dist/lib/filter-graph.d.ts +80 -0
- package/dist/lib/filter-graph.js +84 -0
- package/dist/lib/filter-graph.js.map +1 -1
- package/dist/lib/filter-inout.d.ts +1 -0
- package/dist/lib/filter-inout.js +1 -0
- package/dist/lib/filter-inout.js.map +1 -1
- package/dist/lib/filter.d.ts +2 -0
- package/dist/lib/filter.js +2 -0
- package/dist/lib/filter.js.map +1 -1
- package/dist/lib/format-context.d.ts +356 -20
- package/dist/lib/format-context.js +375 -23
- package/dist/lib/format-context.js.map +1 -1
- package/dist/lib/frame.d.ts +84 -1
- package/dist/lib/frame.js +96 -0
- package/dist/lib/frame.js.map +1 -1
- package/dist/lib/hardware-device-context.d.ts +8 -0
- package/dist/lib/hardware-device-context.js +8 -0
- package/dist/lib/hardware-device-context.js.map +1 -1
- package/dist/lib/hardware-frames-context.d.ts +55 -0
- package/dist/lib/hardware-frames-context.js +57 -0
- package/dist/lib/hardware-frames-context.js.map +1 -1
- package/dist/lib/input-format.d.ts +43 -3
- package/dist/lib/input-format.js +48 -0
- package/dist/lib/input-format.js.map +1 -1
- package/dist/lib/io-context.d.ts +212 -0
- package/dist/lib/io-context.js +228 -0
- package/dist/lib/io-context.js.map +1 -1
- package/dist/lib/log.d.ts +2 -0
- package/dist/lib/log.js +2 -0
- package/dist/lib/log.js.map +1 -1
- package/dist/lib/native-types.d.ts +39 -1
- package/dist/lib/option.d.ts +90 -0
- package/dist/lib/option.js +97 -0
- package/dist/lib/option.js.map +1 -1
- package/dist/lib/output-format.d.ts +4 -0
- package/dist/lib/output-format.js +4 -0
- package/dist/lib/output-format.js.map +1 -1
- package/dist/lib/packet.d.ts +7 -0
- package/dist/lib/packet.js +7 -0
- package/dist/lib/packet.js.map +1 -1
- package/dist/lib/rational.d.ts +1 -0
- package/dist/lib/rational.js +1 -0
- package/dist/lib/rational.js.map +1 -1
- package/dist/lib/software-resample-context.d.ts +64 -0
- package/dist/lib/software-resample-context.js +66 -0
- package/dist/lib/software-resample-context.js.map +1 -1
- package/dist/lib/software-scale-context.d.ts +98 -0
- package/dist/lib/software-scale-context.js +102 -0
- package/dist/lib/software-scale-context.js.map +1 -1
- package/dist/lib/stream.d.ts +1 -0
- package/dist/lib/stream.js +1 -0
- package/dist/lib/stream.js.map +1 -1
- package/dist/lib/utilities.d.ts +60 -0
- package/dist/lib/utilities.js +60 -0
- package/dist/lib/utilities.js.map +1 -1
- package/package.json +18 -18
- package/release_notes.md +0 -29
|
@@ -1,2578 +1,1616 @@
|
|
|
1
1
|
import { AV_HWDEVICE_TYPE_CUDA, AV_HWDEVICE_TYPE_D3D11VA, AV_HWDEVICE_TYPE_D3D12VA, AV_HWDEVICE_TYPE_DRM, AV_HWDEVICE_TYPE_DXVA2, AV_HWDEVICE_TYPE_MEDIACODEC, AV_HWDEVICE_TYPE_OPENCL, AV_HWDEVICE_TYPE_QSV, AV_HWDEVICE_TYPE_RKMPP, AV_HWDEVICE_TYPE_VAAPI, AV_HWDEVICE_TYPE_VDPAU, AV_HWDEVICE_TYPE_VIDEOTOOLBOX, AV_HWDEVICE_TYPE_VULKAN, AVFILTER_FLAG_HWDEVICE, } from '../constants/constants.js';
|
|
2
2
|
import { Filter } from '../lib/filter.js';
|
|
3
|
-
import { HardwareDeviceContext } from '../lib/hardware-device-context.js';
|
|
4
3
|
import { avGetPixFmtName, avGetSampleFmtName } from '../lib/utilities.js';
|
|
5
4
|
/**
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
* with each method returning a filter string that can be used in a filter graph.
|
|
11
|
-
* Hardware-specific implementations may override these methods to use optimized
|
|
12
|
-
* hardware filters instead of software implementations.
|
|
5
|
+
* Filter preset builder for composing filter chains.
|
|
6
|
+
* Supports both software and hardware-accelerated filters.
|
|
7
|
+
* Automatically selects appropriate filter implementations based on hardware context.
|
|
8
|
+
* Uses fluent interface pattern for chaining multiple filters.
|
|
13
9
|
*
|
|
14
10
|
* @example
|
|
15
11
|
* ```typescript
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
12
|
+
* // Software filter chain
|
|
13
|
+
* const filter = FilterPreset.chain()
|
|
14
|
+
* .scale(1920, 1080)
|
|
15
|
+
* .fps(30)
|
|
16
|
+
* .fade('in', 0, 2)
|
|
17
|
+
* .build();
|
|
18
|
+
*
|
|
19
|
+
* // Hardware-accelerated filter chain
|
|
20
|
+
* const hw = HardwareContext.auto();
|
|
21
|
+
* const hwFilter = FilterPreset.chain(hw)
|
|
22
|
+
* .scale(1920, 1080)
|
|
23
|
+
* .blur('gaussian', 5)
|
|
24
|
+
* .build();
|
|
21
25
|
* ```
|
|
22
26
|
*/
|
|
23
|
-
export class
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
* @returns Filter string or null if not supported
|
|
31
|
-
*
|
|
32
|
-
* @example
|
|
33
|
-
* ```typescript
|
|
34
|
-
* presets.scale(1920, 1080) // Scale to Full HD
|
|
35
|
-
* presets.scale(640, 480, { flags: 'lanczos' }) // With specific algorithm
|
|
36
|
-
* ```
|
|
37
|
-
*
|
|
38
|
-
* @see {@link https://ffmpeg.org/ffmpeg-filters.html#scale | FFmpeg scale filter}
|
|
39
|
-
*/
|
|
40
|
-
scale(width, height, options) {
|
|
41
|
-
const flags = options?.flags;
|
|
42
|
-
const base = `scale=${width}:${height}`;
|
|
43
|
-
return flags ? `${base}:flags=${flags}` : base;
|
|
44
|
-
}
|
|
45
|
-
/**
|
|
46
|
-
* Creates a crop filter string.
|
|
47
|
-
*
|
|
48
|
-
* @param width - Width of the cropped area
|
|
49
|
-
* @param height - Height of the cropped area
|
|
50
|
-
* @param x - X coordinate of top-left corner (default: 0)
|
|
51
|
-
* @param y - Y coordinate of top-left corner (default: 0)
|
|
52
|
-
* @returns Filter string or null if not supported
|
|
53
|
-
*
|
|
54
|
-
* @example
|
|
55
|
-
* ```typescript
|
|
56
|
-
* presets.crop(640, 480, 100, 100) // Crop 640x480 area starting at (100,100)
|
|
57
|
-
* presets.crop(1280, 720) // Crop from top-left corner
|
|
58
|
-
* ```
|
|
59
|
-
*
|
|
60
|
-
* @see {@link https://ffmpeg.org/ffmpeg-filters.html#crop | FFmpeg crop filter}
|
|
61
|
-
*/
|
|
62
|
-
crop(width, height, x = 0, y = 0) {
|
|
63
|
-
return `crop=${width}:${height}:${x}:${y}`;
|
|
64
|
-
}
|
|
65
|
-
/**
|
|
66
|
-
* Creates an FPS filter string to change frame rate.
|
|
67
|
-
*
|
|
68
|
-
* @param fps - Target frames per second
|
|
69
|
-
* @returns Filter string or null if not supported
|
|
70
|
-
*
|
|
71
|
-
* @example
|
|
72
|
-
* ```typescript
|
|
73
|
-
* presets.fps(30) // Convert to 30 FPS
|
|
74
|
-
* presets.fps(23.976) // Film frame rate
|
|
75
|
-
* ```
|
|
76
|
-
*
|
|
77
|
-
* @see {@link https://ffmpeg.org/ffmpeg-filters.html#fps | FFmpeg fps filter}
|
|
78
|
-
*/
|
|
79
|
-
fps(fps) {
|
|
80
|
-
return `fps=${fps}`;
|
|
81
|
-
}
|
|
82
|
-
/**
|
|
83
|
-
* Creates a format filter string to convert pixel format.
|
|
84
|
-
*
|
|
85
|
-
* @param pixelFormat - Target pixel format(s) - can be string, AVPixelFormat enum, or array
|
|
86
|
-
* @returns Filter string or null if not supported
|
|
87
|
-
*
|
|
88
|
-
* @example
|
|
89
|
-
* ```typescript
|
|
90
|
-
* // Single format
|
|
91
|
-
* presets.format('yuv420p');
|
|
92
|
-
* presets.format(AV_PIX_FMT_YUV420P);
|
|
93
|
-
*
|
|
94
|
-
* // Multiple formats (creates a chain)
|
|
95
|
-
* presets.format(['yuv420p', 'rgb24']);
|
|
96
|
-
* ```
|
|
97
|
-
*
|
|
98
|
-
* @see {@link https://ffmpeg.org/ffmpeg-filters.html#format | FFmpeg format filter}
|
|
99
|
-
*/
|
|
100
|
-
format(pixelFormat) {
|
|
101
|
-
if (Array.isArray(pixelFormat)) {
|
|
102
|
-
// Create a chain of format filters
|
|
103
|
-
const formats = pixelFormat.map((fmt) => {
|
|
104
|
-
const formatName = typeof fmt === 'string' ? fmt : (avGetPixFmtName(fmt) ?? 'yuv420p');
|
|
105
|
-
return `format=${formatName}`;
|
|
106
|
-
});
|
|
107
|
-
return formats.join(',');
|
|
108
|
-
}
|
|
109
|
-
const formatName = typeof pixelFormat === 'string' ? pixelFormat : (avGetPixFmtName(pixelFormat) ?? 'yuv420p');
|
|
110
|
-
return `format=${formatName}`;
|
|
111
|
-
}
|
|
112
|
-
/**
|
|
113
|
-
* Creates a rotate filter string.
|
|
114
|
-
*
|
|
115
|
-
* @param angle - Rotation angle in degrees
|
|
116
|
-
* @returns Filter string or null if not supported
|
|
117
|
-
*
|
|
118
|
-
* @example
|
|
119
|
-
* ```typescript
|
|
120
|
-
* presets.rotate(90) // Rotate 90 degrees clockwise
|
|
121
|
-
* presets.rotate(-45) // Rotate 45 degrees counter-clockwise
|
|
122
|
-
* ```
|
|
123
|
-
*
|
|
124
|
-
* @see {@link https://ffmpeg.org/ffmpeg-filters.html#rotate | FFmpeg rotate filter}
|
|
125
|
-
*/
|
|
126
|
-
rotate(angle) {
|
|
127
|
-
return `rotate=${angle}*PI/180`;
|
|
128
|
-
}
|
|
129
|
-
/**
|
|
130
|
-
* Creates a horizontal flip filter string.
|
|
131
|
-
*
|
|
132
|
-
* @returns Filter string or null if not supported
|
|
133
|
-
*
|
|
134
|
-
* @example
|
|
135
|
-
* ```typescript
|
|
136
|
-
* presets.hflip() // Mirror horizontally
|
|
137
|
-
* ```
|
|
138
|
-
*
|
|
139
|
-
* @see {@link https://ffmpeg.org/ffmpeg-filters.html#hflip | FFmpeg hflip filter}
|
|
140
|
-
*/
|
|
141
|
-
hflip() {
|
|
142
|
-
return 'hflip';
|
|
143
|
-
}
|
|
144
|
-
/**
|
|
145
|
-
* Creates a vertical flip filter string.
|
|
146
|
-
*
|
|
147
|
-
* @returns Filter string or null if not supported
|
|
148
|
-
*
|
|
149
|
-
* @example
|
|
150
|
-
* ```typescript
|
|
151
|
-
* presets.vflip() // Flip upside down
|
|
152
|
-
* ```
|
|
153
|
-
*
|
|
154
|
-
* @see {@link https://ffmpeg.org/ffmpeg-filters.html#vflip | FFmpeg vflip filter}
|
|
155
|
-
*/
|
|
156
|
-
vflip() {
|
|
157
|
-
return 'vflip';
|
|
158
|
-
}
|
|
159
|
-
/**
|
|
160
|
-
* Creates a fade filter string for video.
|
|
161
|
-
*
|
|
162
|
-
* @param type - Fade type ('in' or 'out')
|
|
163
|
-
* @param start - Start time in seconds
|
|
164
|
-
* @param duration - Fade duration in seconds
|
|
165
|
-
* @returns Filter string or null if not supported
|
|
166
|
-
*
|
|
167
|
-
* @example
|
|
168
|
-
* ```typescript
|
|
169
|
-
* presets.fade('in', 0, 2) // 2-second fade in from start
|
|
170
|
-
* presets.fade('out', 10, 1) // 1-second fade out at 10 seconds
|
|
171
|
-
* ```
|
|
172
|
-
*
|
|
173
|
-
* @see {@link https://ffmpeg.org/ffmpeg-filters.html#fade | FFmpeg fade filter}
|
|
174
|
-
*/
|
|
175
|
-
fade(type, start, duration) {
|
|
176
|
-
return `fade=t=${type}:st=${start}:d=${duration}`;
|
|
177
|
-
}
|
|
178
|
-
/**
|
|
179
|
-
* Creates an overlay filter string to composite two video streams.
|
|
180
|
-
*
|
|
181
|
-
* @param x - X position for overlay (default: 0)
|
|
182
|
-
* @param y - Y position for overlay (default: 0)
|
|
183
|
-
* @param options - Additional overlay options
|
|
184
|
-
* @returns Filter string or null if not supported
|
|
185
|
-
*
|
|
186
|
-
* @example
|
|
187
|
-
* ```typescript
|
|
188
|
-
* // Basic overlay at position
|
|
189
|
-
* presets.overlay(100, 50);
|
|
190
|
-
*
|
|
191
|
-
* // With additional options
|
|
192
|
-
* presets.overlay(0, 0, { format: 'yuv420' });
|
|
193
|
-
* ```
|
|
194
|
-
*
|
|
195
|
-
* @see {@link https://ffmpeg.org/ffmpeg-filters.html#overlay | FFmpeg overlay filter}
|
|
196
|
-
*/
|
|
197
|
-
overlay(x = 0, y = 0, options) {
|
|
198
|
-
let filter = `overlay=${x}:${y}`;
|
|
199
|
-
if (options) {
|
|
200
|
-
for (const [key, value] of Object.entries(options)) {
|
|
201
|
-
filter += `:${key}=${value}`;
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
return filter;
|
|
205
|
-
}
|
|
206
|
-
/**
|
|
207
|
-
* Creates a volume filter string for audio.
|
|
208
|
-
*
|
|
209
|
-
* @param factor - Volume multiplication factor (1.0 = unchanged, 2.0 = double)
|
|
210
|
-
* @returns Filter string or null if not supported
|
|
211
|
-
*
|
|
212
|
-
* @example
|
|
213
|
-
* ```typescript
|
|
214
|
-
* presets.volume(0.5) // Reduce volume by 50%
|
|
215
|
-
* presets.volume(1.5) // Increase volume by 50%
|
|
216
|
-
* ```
|
|
217
|
-
*
|
|
218
|
-
* @see {@link https://ffmpeg.org/ffmpeg-filters.html#volume | FFmpeg volume filter}
|
|
219
|
-
*/
|
|
220
|
-
volume(factor) {
|
|
221
|
-
return `volume=${factor}`;
|
|
222
|
-
}
|
|
223
|
-
/**
|
|
224
|
-
* Creates an audio format filter string.
|
|
225
|
-
*
|
|
226
|
-
* @param sampleFormat - Target sample format (e.g., 's16', 'fltp')
|
|
227
|
-
* @param sampleRate - Target sample rate in Hz (optional)
|
|
228
|
-
* @param channelLayout - Target channel layout (optional)
|
|
229
|
-
* @returns Filter string or null if not supported
|
|
230
|
-
*
|
|
231
|
-
* @example
|
|
232
|
-
* ```typescript
|
|
233
|
-
* // Change sample format only
|
|
234
|
-
* presets.aformat('s16');
|
|
235
|
-
*
|
|
236
|
-
* // Change format and sample rate
|
|
237
|
-
* presets.aformat('fltp', 48000);
|
|
238
|
-
*
|
|
239
|
-
* // Full conversion
|
|
240
|
-
* presets.aformat('s16', 44100, 'stereo');
|
|
241
|
-
* ```
|
|
242
|
-
*
|
|
243
|
-
* @see {@link https://ffmpeg.org/ffmpeg-filters.html#aformat | FFmpeg aformat filter}
|
|
244
|
-
*/
|
|
245
|
-
aformat(sampleFormat, sampleRate, channelLayout) {
|
|
246
|
-
const formatName = typeof sampleFormat === 'string' ? sampleFormat : (avGetSampleFmtName(sampleFormat) ?? 's16');
|
|
247
|
-
let filter = `aformat=sample_fmts=${formatName}`;
|
|
248
|
-
if (sampleRate)
|
|
249
|
-
filter += `:sample_rates=${sampleRate}`;
|
|
250
|
-
if (channelLayout)
|
|
251
|
-
filter += `:channel_layouts=${channelLayout}`;
|
|
252
|
-
return filter;
|
|
253
|
-
}
|
|
254
|
-
/**
|
|
255
|
-
* Creates an asetnsamples filter string to set the number of samples per frame.
|
|
256
|
-
* This is crucial for encoders like Opus that require specific frame sizes.
|
|
257
|
-
*
|
|
258
|
-
* @param samples - Number of samples per frame
|
|
259
|
-
* @param padding - Whether to pad or drop samples (default: true)
|
|
260
|
-
* @returns Filter string or null if not supported
|
|
261
|
-
*
|
|
262
|
-
* @example
|
|
263
|
-
* ```typescript
|
|
264
|
-
* // For Opus encoder (requires 960 samples)
|
|
265
|
-
* presets.asetnsamples(960);
|
|
266
|
-
*
|
|
267
|
-
* // Drop samples instead of padding
|
|
268
|
-
* presets.asetnsamples(1024, false);
|
|
269
|
-
* ```
|
|
270
|
-
*
|
|
271
|
-
* @see {@link https://ffmpeg.org/ffmpeg-filters.html#asetnsamples | FFmpeg asetnsamples filter}
|
|
272
|
-
*/
|
|
273
|
-
asetnsamples(samples, padding = true) {
|
|
274
|
-
const p = padding ? 1 : 0;
|
|
275
|
-
return `asetnsamples=n=${samples}:p=${p}`;
|
|
276
|
-
}
|
|
277
|
-
/**
|
|
278
|
-
* Creates an atempo filter string to change audio playback speed.
|
|
279
|
-
* Factor must be between 0.5 and 2.0. For larger changes, chain multiple atempo filters.
|
|
280
|
-
*
|
|
281
|
-
* @param factor - Tempo factor (0.5 = half speed, 2.0 = double speed)
|
|
282
|
-
* @returns Filter string or null if not supported
|
|
283
|
-
*
|
|
284
|
-
* @example
|
|
285
|
-
* ```typescript
|
|
286
|
-
* presets.atempo(1.5) // 1.5x speed
|
|
287
|
-
* presets.atempo(0.8) // Slow down to 80% speed
|
|
288
|
-
* ```
|
|
289
|
-
*
|
|
290
|
-
* @see {@link https://ffmpeg.org/ffmpeg-filters.html#atempo | FFmpeg atempo filter}
|
|
291
|
-
*/
|
|
292
|
-
atempo(factor) {
|
|
293
|
-
return `atempo=${factor}`;
|
|
294
|
-
}
|
|
295
|
-
/**
|
|
296
|
-
* Creates an audio fade filter string.
|
|
297
|
-
*
|
|
298
|
-
* @param type - Fade type ('in' or 'out')
|
|
299
|
-
* @param start - Start time in seconds
|
|
300
|
-
* @param duration - Fade duration in seconds
|
|
301
|
-
* @returns Filter string or null if not supported
|
|
302
|
-
*
|
|
303
|
-
* @example
|
|
304
|
-
* ```typescript
|
|
305
|
-
* presets.afade('in', 0, 3) // 3-second audio fade in
|
|
306
|
-
* presets.afade('out', 20, 2) // 2-second fade out at 20s
|
|
307
|
-
* ```
|
|
308
|
-
*
|
|
309
|
-
* @see {@link https://ffmpeg.org/ffmpeg-filters.html#afade | FFmpeg afade filter}
|
|
310
|
-
*/
|
|
311
|
-
afade(type, start, duration) {
|
|
312
|
-
return `afade=t=${type}:st=${start}:d=${duration}`;
|
|
27
|
+
export class FilterPreset {
|
|
28
|
+
hardware;
|
|
29
|
+
filters = [];
|
|
30
|
+
support;
|
|
31
|
+
constructor(hardware) {
|
|
32
|
+
this.hardware = hardware;
|
|
33
|
+
this.support = this.getSupport();
|
|
313
34
|
}
|
|
314
35
|
/**
|
|
315
|
-
*
|
|
316
|
-
*
|
|
317
|
-
* @param inputs - Number of input streams to mix (default: 2)
|
|
318
|
-
* @param duration - How to determine output duration (default: 'longest')
|
|
319
|
-
* @returns Filter string or null if not supported
|
|
320
|
-
*
|
|
321
|
-
* @example
|
|
322
|
-
* ```typescript
|
|
323
|
-
* presets.amix(3, 'longest') // Mix 3 audio streams
|
|
324
|
-
* presets.amix(2, 'first') // Mix 2 streams, use first's duration
|
|
325
|
-
* ```
|
|
36
|
+
* Checks if a filter is hardware-accelerated.
|
|
326
37
|
*
|
|
327
|
-
* @
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
return `amix=inputs=${inputs}:duration=${duration}`;
|
|
331
|
-
}
|
|
332
|
-
/**
|
|
333
|
-
* Creates a pad filter string to add padding to video.
|
|
334
|
-
* Essential for aspect ratio adjustments and letterboxing.
|
|
335
|
-
*
|
|
336
|
-
* @param width - Output width (can use expressions like 'iw+100')
|
|
337
|
-
* @param height - Output height (can use expressions like 'ih+100')
|
|
338
|
-
* @param x - X position of input video (default: '(ow-iw)/2' for center)
|
|
339
|
-
* @param y - Y position of input video (default: '(oh-ih)/2' for center)
|
|
340
|
-
* @param color - Padding color (default: 'black')
|
|
341
|
-
* @returns Filter string or null if not supported
|
|
342
|
-
*
|
|
343
|
-
* @example
|
|
344
|
-
* ```typescript
|
|
345
|
-
* // Add black bars for 16:9 aspect ratio
|
|
346
|
-
* presets.pad('iw', 'iw*9/16');
|
|
347
|
-
*
|
|
348
|
-
* // Add 50px padding on all sides
|
|
349
|
-
* presets.pad('iw+100', 'ih+100');
|
|
350
|
-
* ```
|
|
351
|
-
*
|
|
352
|
-
* @see {@link https://ffmpeg.org/ffmpeg-filters.html#pad | FFmpeg pad filter}
|
|
353
|
-
*/
|
|
354
|
-
pad(width, height, x, y, color = 'black') {
|
|
355
|
-
let filter = `pad=${width}:${height}`;
|
|
356
|
-
if (x !== undefined)
|
|
357
|
-
filter += `:${x}`;
|
|
358
|
-
if (y !== undefined)
|
|
359
|
-
filter += `:${y}`;
|
|
360
|
-
filter += `:${color}`;
|
|
361
|
-
return filter;
|
|
362
|
-
}
|
|
363
|
-
/**
|
|
364
|
-
* Creates a trim filter string to cut a portion of the stream.
|
|
365
|
-
* Crucial for cutting segments from media.
|
|
366
|
-
*
|
|
367
|
-
* @param start - Start time in seconds
|
|
368
|
-
* @param end - End time in seconds (optional)
|
|
369
|
-
* @param duration - Duration in seconds (optional, alternative to end)
|
|
370
|
-
* @returns Filter string or null if not supported
|
|
371
|
-
*
|
|
372
|
-
* @example
|
|
373
|
-
* ```typescript
|
|
374
|
-
* presets.trim(10, 30) // Extract from 10s to 30s
|
|
375
|
-
* presets.trim(5, undefined, 10) // Extract 10s starting at 5s
|
|
376
|
-
* ```
|
|
377
|
-
*
|
|
378
|
-
* @see {@link https://ffmpeg.org/ffmpeg-filters.html#trim | FFmpeg trim filter}
|
|
379
|
-
*/
|
|
380
|
-
trim(start, end, duration) {
|
|
381
|
-
let filter = `trim=start=${start}`;
|
|
382
|
-
if (end !== undefined)
|
|
383
|
-
filter += `:end=${end}`;
|
|
384
|
-
if (duration !== undefined)
|
|
385
|
-
filter += `:duration=${duration}`;
|
|
386
|
-
return filter;
|
|
387
|
-
}
|
|
388
|
-
/**
|
|
389
|
-
* Creates a setpts filter string to change presentation timestamps.
|
|
390
|
-
* Essential for speed changes and timestamp manipulation.
|
|
391
|
-
*
|
|
392
|
-
* @param expression - PTS expression (e.g., 'PTS*2' for half speed, 'PTS/2' for double speed)
|
|
393
|
-
* @returns Filter string or null if not supported
|
|
394
|
-
*
|
|
395
|
-
* @example
|
|
396
|
-
* ```typescript
|
|
397
|
-
* // Double speed
|
|
398
|
-
* presets.setpts('PTS/2');
|
|
399
|
-
*
|
|
400
|
-
* // Half speed
|
|
401
|
-
* presets.setpts('PTS*2');
|
|
402
|
-
*
|
|
403
|
-
* // Reset timestamps
|
|
404
|
-
* presets.setpts('PTS-STARTPTS');
|
|
405
|
-
* ```
|
|
406
|
-
*
|
|
407
|
-
* @see {@link https://ffmpeg.org/ffmpeg-filters.html#setpts | FFmpeg setpts filter}
|
|
408
|
-
*/
|
|
409
|
-
setpts(expression) {
|
|
410
|
-
return `setpts=${expression}`;
|
|
411
|
-
}
|
|
412
|
-
/**
|
|
413
|
-
* Creates an asetpts filter string for audio timestamp manipulation.
|
|
414
|
-
*
|
|
415
|
-
* @param expression - PTS expression
|
|
416
|
-
* @returns Filter string or null if not supported
|
|
417
|
-
*
|
|
418
|
-
* @example
|
|
419
|
-
* ```typescript
|
|
420
|
-
* presets.asetpts('PTS-STARTPTS') // Reset timestamps to start from 0
|
|
421
|
-
* ```
|
|
422
|
-
*
|
|
423
|
-
* @see {@link https://ffmpeg.org/ffmpeg-filters.html#asetpts | FFmpeg asetpts filter}
|
|
424
|
-
*/
|
|
425
|
-
asetpts(expression) {
|
|
426
|
-
return `asetpts=${expression}`;
|
|
427
|
-
}
|
|
428
|
-
/**
|
|
429
|
-
* Creates a transpose filter string for rotation/flipping.
|
|
430
|
-
* More efficient than rotate for 90-degree rotations.
|
|
431
|
-
*
|
|
432
|
-
* @param mode - Transpose mode (0-3, or named constants)
|
|
433
|
-
* @returns Filter string or null if not supported
|
|
434
|
-
*
|
|
435
|
-
* @example
|
|
436
|
-
* ```typescript
|
|
437
|
-
* presets.transpose(1) // Rotate 90 degrees clockwise
|
|
438
|
-
* presets.transpose('cclock') // Rotate 90 degrees counter-clockwise
|
|
439
|
-
* ```
|
|
440
|
-
*
|
|
441
|
-
* @see {@link https://ffmpeg.org/ffmpeg-filters.html#transpose | FFmpeg transpose filter}
|
|
442
|
-
*/
|
|
443
|
-
transpose(mode) {
|
|
444
|
-
return `transpose=${mode}`;
|
|
445
|
-
}
|
|
446
|
-
/**
|
|
447
|
-
* Creates a setsar filter string to set sample aspect ratio.
|
|
448
|
-
* Important for correcting aspect ratio issues.
|
|
449
|
-
*
|
|
450
|
-
* @param ratio - Aspect ratio (e.g., '1:1', '16:9', or number)
|
|
451
|
-
* @returns Filter string or null if not supported
|
|
452
|
-
*
|
|
453
|
-
* @example
|
|
454
|
-
* ```typescript
|
|
455
|
-
* presets.setsar('1:1') // Square pixels
|
|
456
|
-
* presets.setsar(1.333) // 4:3 aspect ratio
|
|
457
|
-
* ```
|
|
458
|
-
*
|
|
459
|
-
* @see {@link https://ffmpeg.org/ffmpeg-filters.html#setsar | FFmpeg setsar/setdar filter}
|
|
460
|
-
*/
|
|
461
|
-
setsar(ratio) {
|
|
462
|
-
return `setsar=${ratio}`;
|
|
463
|
-
}
|
|
464
|
-
/**
|
|
465
|
-
* Creates a setdar filter string to set display aspect ratio.
|
|
466
|
-
*
|
|
467
|
-
* @param ratio - Aspect ratio (e.g., '16:9', '4:3')
|
|
468
|
-
* @returns Filter string or null if not supported
|
|
469
|
-
*
|
|
470
|
-
* @example
|
|
471
|
-
* ```typescript
|
|
472
|
-
* presets.setdar('16:9') // Widescreen
|
|
473
|
-
* presets.setdar('4:3') // Traditional TV aspect
|
|
474
|
-
* ```
|
|
475
|
-
*
|
|
476
|
-
* @see {@link https://ffmpeg.org/ffmpeg-filters.html#setsar | FFmpeg setsar/setdar filter}
|
|
477
|
-
*/
|
|
478
|
-
setdar(ratio) {
|
|
479
|
-
return `setdar=${ratio}`;
|
|
480
|
-
}
|
|
481
|
-
/**
|
|
482
|
-
* Creates an apad filter string to add audio padding.
|
|
483
|
-
* Useful for ensuring minimum audio duration.
|
|
484
|
-
*
|
|
485
|
-
* @param wholeDuration - Minimum duration in seconds (optional)
|
|
486
|
-
* @param padDuration - Amount of padding to add in seconds (optional)
|
|
487
|
-
* @returns Filter string or null if not supported
|
|
488
|
-
*
|
|
489
|
-
* @example
|
|
490
|
-
* ```typescript
|
|
491
|
-
* presets.apad(30) // Ensure at least 30 seconds total
|
|
492
|
-
* presets.apad(undefined, 5) // Add 5 seconds of padding
|
|
493
|
-
* ```
|
|
494
|
-
*
|
|
495
|
-
* @see {@link https://ffmpeg.org/ffmpeg-filters.html#apad | FFmpeg apad filter}
|
|
496
|
-
*/
|
|
497
|
-
apad(wholeDuration, padDuration) {
|
|
498
|
-
if (!wholeDuration && !padDuration)
|
|
499
|
-
return 'apad';
|
|
500
|
-
let filter = 'apad';
|
|
501
|
-
if (wholeDuration)
|
|
502
|
-
filter += `=whole_dur=${wholeDuration}`;
|
|
503
|
-
if (padDuration)
|
|
504
|
-
filter += wholeDuration ? `:pad_dur=${padDuration}` : `=pad_dur=${padDuration}`;
|
|
505
|
-
return filter;
|
|
506
|
-
}
|
|
507
|
-
/**
|
|
508
|
-
* Creates a deinterlace filter string.
|
|
509
|
-
* Essential for processing interlaced content.
|
|
510
|
-
*
|
|
511
|
-
* @param mode - Deinterlace mode (default: 'yadif')
|
|
512
|
-
* @returns Filter string or null if not supported
|
|
513
|
-
*
|
|
514
|
-
* @example
|
|
515
|
-
* ```typescript
|
|
516
|
-
* presets.deinterlace('yadif') // Standard deinterlacing
|
|
517
|
-
* presets.deinterlace('bwdif') // Bob Weaver deinterlacing
|
|
518
|
-
* ```
|
|
519
|
-
*
|
|
520
|
-
* @see {@link https://ffmpeg.org/ffmpeg-filters.html#yadif | FFmpeg yadif filter}
|
|
521
|
-
*/
|
|
522
|
-
deinterlace(mode = 'yadif') {
|
|
523
|
-
return mode;
|
|
524
|
-
}
|
|
525
|
-
/**
|
|
526
|
-
* Creates a select filter string to select specific frames.
|
|
527
|
-
* Powerful for extracting keyframes, specific frame types, etc.
|
|
528
|
-
*
|
|
529
|
-
* @param expression - Selection expression
|
|
530
|
-
* @returns Filter string or null if not supported
|
|
531
|
-
*
|
|
532
|
-
* @example
|
|
533
|
-
* ```typescript
|
|
534
|
-
* presets.select('eq(pict_type,I)') // Select only keyframes
|
|
535
|
-
* presets.select('not(mod(n,10))') // Select every 10th frame
|
|
536
|
-
* ```
|
|
537
|
-
*
|
|
538
|
-
* @see {@link https://ffmpeg.org/ffmpeg-filters.html#select | FFmpeg select filter}
|
|
539
|
-
*/
|
|
540
|
-
select(expression) {
|
|
541
|
-
return `select='${expression}'`;
|
|
542
|
-
}
|
|
543
|
-
/**
|
|
544
|
-
* Creates an aselect filter string for audio selection.
|
|
545
|
-
*
|
|
546
|
-
* @param expression - Selection expression
|
|
547
|
-
* @returns Filter string or null if not supported
|
|
548
|
-
*
|
|
549
|
-
* @example
|
|
550
|
-
* ```typescript
|
|
551
|
-
* presets.aselect('between(t,10,20)') // Select audio between 10-20 seconds
|
|
552
|
-
* ```
|
|
553
|
-
*
|
|
554
|
-
* @see {@link https://ffmpeg.org/ffmpeg-filters.html#aselect | FFmpeg aselect filter}
|
|
555
|
-
*/
|
|
556
|
-
aselect(expression) {
|
|
557
|
-
return `aselect='${expression}'`;
|
|
558
|
-
}
|
|
559
|
-
/**
|
|
560
|
-
* Creates a concat filter string to concatenate multiple inputs.
|
|
561
|
-
* Essential for joining multiple video/audio segments.
|
|
562
|
-
*
|
|
563
|
-
* @param n - Number of input segments
|
|
564
|
-
* @param v - Number of output video streams (0 or 1)
|
|
565
|
-
* @param a - Number of output audio streams (0 or 1)
|
|
566
|
-
* @returns Filter string or null if not supported
|
|
567
|
-
*
|
|
568
|
-
* @example
|
|
569
|
-
* ```typescript
|
|
570
|
-
* presets.concat(3, 1, 1) // Join 3 segments with video and audio
|
|
571
|
-
* presets.concat(2, 1, 0) // Join 2 video-only segments
|
|
572
|
-
* ```
|
|
573
|
-
*
|
|
574
|
-
* @see {@link https://ffmpeg.org/ffmpeg-filters.html#concat | FFmpeg concat filter}
|
|
575
|
-
*/
|
|
576
|
-
concat(n, v = 1, a = 1) {
|
|
577
|
-
return `concat=n=${n}:v=${v}:a=${a}`;
|
|
578
|
-
}
|
|
579
|
-
/**
|
|
580
|
-
* Creates an amerge filter string to merge multiple audio streams into one.
|
|
581
|
-
* Different from amix - this creates multi-channel output.
|
|
582
|
-
*
|
|
583
|
-
* @param inputs - Number of input streams
|
|
584
|
-
* @returns Filter string or null if not supported
|
|
585
|
-
*
|
|
586
|
-
* @example
|
|
587
|
-
* ```typescript
|
|
588
|
-
* presets.amerge(2) // Merge 2 mono streams to stereo
|
|
589
|
-
* presets.amerge(6) // Merge 6 channels for 5.1 surround
|
|
590
|
-
* ```
|
|
591
|
-
*
|
|
592
|
-
* @see {@link https://ffmpeg.org/ffmpeg-filters.html#amerge | FFmpeg amerge filter}
|
|
593
|
-
*/
|
|
594
|
-
amerge(inputs = 2) {
|
|
595
|
-
return `amerge=inputs=${inputs}`;
|
|
596
|
-
}
|
|
597
|
-
/**
|
|
598
|
-
* Creates a channelmap filter string to remap audio channels.
|
|
599
|
-
* Critical for audio channel manipulation.
|
|
600
|
-
*
|
|
601
|
-
* @param map - Channel mapping (e.g., '0-0|1-1' or 'FL-FR|FR-FL' to swap stereo)
|
|
602
|
-
* @returns Filter string or null if not supported
|
|
603
|
-
*
|
|
604
|
-
* @example
|
|
605
|
-
* ```typescript
|
|
606
|
-
* presets.channelmap('FL-FR|FR-FL') // Swap left and right channels
|
|
607
|
-
* presets.channelmap('0-0|0-1') // Duplicate mono to stereo
|
|
608
|
-
* ```
|
|
609
|
-
*
|
|
610
|
-
* @see {@link https://ffmpeg.org/ffmpeg-filters.html#channelmap | FFmpeg channelmap filter}
|
|
611
|
-
*/
|
|
612
|
-
channelmap(map) {
|
|
613
|
-
return `channelmap=${map}`;
|
|
614
|
-
}
|
|
615
|
-
/**
|
|
616
|
-
* Creates a channelsplit filter string to split audio channels.
|
|
617
|
-
*
|
|
618
|
-
* @param channelLayout - Channel layout to split (optional)
|
|
619
|
-
* @returns Filter string or null if not supported
|
|
620
|
-
*
|
|
621
|
-
* @example
|
|
622
|
-
* ```typescript
|
|
623
|
-
* presets.channelsplit('stereo') // Split stereo to 2 mono
|
|
624
|
-
* presets.channelsplit('5.1') // Split 5.1 to individual channels
|
|
625
|
-
* ```
|
|
626
|
-
*
|
|
627
|
-
* @see {@link https://ffmpeg.org/ffmpeg-filters.html#channelsplit | FFmpeg channelsplit filter}
|
|
628
|
-
*/
|
|
629
|
-
channelsplit(channelLayout) {
|
|
630
|
-
return channelLayout ? `channelsplit=channel_layout=${channelLayout}` : 'channelsplit';
|
|
631
|
-
}
|
|
632
|
-
/**
|
|
633
|
-
* Creates a loudnorm filter string for loudness normalization.
|
|
634
|
-
* Essential for broadcast compliance and consistent audio levels.
|
|
635
|
-
*
|
|
636
|
-
* @param I - Integrated loudness target (default: -24 LUFS)
|
|
637
|
-
* @param TP - True peak (default: -2 dBTP)
|
|
638
|
-
* @param LRA - Loudness range (default: 7 LU)
|
|
639
|
-
* @returns Filter string or null if not supported
|
|
640
|
-
*
|
|
641
|
-
* @example
|
|
642
|
-
* ```typescript
|
|
643
|
-
* presets.loudnorm(-23, -1, 7) // EBU R128 broadcast standard
|
|
644
|
-
* presets.loudnorm(-16, -1.5, 11) // Streaming platforms standard
|
|
645
|
-
* ```
|
|
646
|
-
*
|
|
647
|
-
* @see {@link https://ffmpeg.org/ffmpeg-filters.html#loudnorm | FFmpeg loudnorm filter}
|
|
648
|
-
*/
|
|
649
|
-
loudnorm(I = -24, TP = -2, LRA = 7) {
|
|
650
|
-
return `loudnorm=I=${I}:TP=${TP}:LRA=${LRA}`;
|
|
651
|
-
}
|
|
652
|
-
/**
|
|
653
|
-
* Creates a compand filter string for audio compression/expansion.
|
|
654
|
-
* Important for dynamic range control.
|
|
655
|
-
*
|
|
656
|
-
* @param attacks - Attack times
|
|
657
|
-
* @param decays - Decay times
|
|
658
|
-
* @param points - Transfer function points
|
|
659
|
-
* @param gain - Output gain
|
|
660
|
-
* @returns Filter string or null if not supported
|
|
661
|
-
*
|
|
662
|
-
* @example
|
|
663
|
-
* ```typescript
|
|
664
|
-
* presets.compand('0.3|0.3', '1|1', '-90/-60|-60/-40|-40/-30|-20/-20', 6)
|
|
665
|
-
* ```
|
|
666
|
-
*
|
|
667
|
-
* @see {@link https://ffmpeg.org/ffmpeg-filters.html#compand | FFmpeg compand filter}
|
|
668
|
-
*/
|
|
669
|
-
compand(attacks, decays, points, gain) {
|
|
670
|
-
let filter = `compand=attacks=${attacks}:decays=${decays}:points=${points}`;
|
|
671
|
-
if (gain !== undefined)
|
|
672
|
-
filter += `:gain=${gain}`;
|
|
673
|
-
return filter;
|
|
674
|
-
}
|
|
675
|
-
}
|
|
676
|
-
/**
|
|
677
|
-
* Filter chain builder for composing multiple filters.
|
|
678
|
-
* Allows fluent API for building complex filter graphs by chaining filter operations.
|
|
679
|
-
*
|
|
680
|
-
* @example
|
|
681
|
-
* ```typescript
|
|
682
|
-
* const chain = new FilterChain()
|
|
683
|
-
* .add('scale=1920:1080')
|
|
684
|
-
* .add('fps=30')
|
|
685
|
-
* .custom('rotate=45*PI/180')
|
|
686
|
-
* .build();
|
|
687
|
-
* // Result: "scale=1920:1080,fps=30,rotate=45*PI/180"
|
|
688
|
-
* ```
|
|
689
|
-
*/
|
|
690
|
-
export class FilterChain {
|
|
691
|
-
filters = [];
|
|
692
|
-
/**
|
|
693
|
-
* Adds a filter to the chain.
|
|
694
|
-
*
|
|
695
|
-
* @param filter - Filter string to add (ignored if null/undefined)
|
|
696
|
-
* @returns This instance for chaining
|
|
697
|
-
*
|
|
698
|
-
* @example
|
|
699
|
-
* ```typescript
|
|
700
|
-
* chain.add('scale=1920:1080')
|
|
701
|
-
* ```
|
|
702
|
-
*/
|
|
703
|
-
add(filter) {
|
|
704
|
-
if (filter) {
|
|
705
|
-
this.filters.push(filter);
|
|
706
|
-
}
|
|
707
|
-
return this;
|
|
708
|
-
}
|
|
709
|
-
/**
|
|
710
|
-
* Adds a custom filter string to the chain.
|
|
711
|
-
*
|
|
712
|
-
* @param filter - Custom filter string
|
|
713
|
-
* @returns This instance for chaining
|
|
714
|
-
*
|
|
715
|
-
* @example
|
|
716
|
-
* ```typescript
|
|
717
|
-
* chain.custom('myfilter=param1:param2')
|
|
718
|
-
* ```
|
|
719
|
-
*/
|
|
720
|
-
custom(filter) {
|
|
721
|
-
return this.add(filter);
|
|
722
|
-
}
|
|
723
|
-
/**
|
|
724
|
-
* Builds the final filter string.
|
|
725
|
-
*
|
|
726
|
-
* @param separator - Separator between filters (default: ',')
|
|
727
|
-
* @returns Combined filter string
|
|
728
|
-
*
|
|
729
|
-
* @example
|
|
730
|
-
* ```typescript
|
|
731
|
-
* const filterString = chain.build() // "scale=1920:1080,fps=30"
|
|
732
|
-
* ```
|
|
733
|
-
*/
|
|
734
|
-
build(separator = ',') {
|
|
735
|
-
return this.filters.join(separator);
|
|
736
|
-
}
|
|
737
|
-
/**
|
|
738
|
-
* Returns the filters as an array.
|
|
739
|
-
*
|
|
740
|
-
* @returns Array of filter strings
|
|
741
|
-
*
|
|
742
|
-
* @example
|
|
743
|
-
* ```typescript
|
|
744
|
-
* const filters = chain.toArray() // ["scale=1920:1080", "fps=30"]
|
|
745
|
-
* ```
|
|
746
|
-
*/
|
|
747
|
-
toArray() {
|
|
748
|
-
return [...this.filters];
|
|
749
|
-
}
|
|
750
|
-
}
|
|
751
|
-
/**
|
|
752
|
-
* Base chain builder with common filter methods.
|
|
753
|
-
* Provides a fluent API for building filter chains using preset methods.
|
|
754
|
-
*
|
|
755
|
-
* @template T The preset type this builder uses
|
|
756
|
-
*
|
|
757
|
-
* @example
|
|
758
|
-
* ```typescript
|
|
759
|
-
* const chain = new ChainBuilderBase(presets)
|
|
760
|
-
* .scale(1920, 1080)
|
|
761
|
-
* .fps(30)
|
|
762
|
-
* .fade('in', 0, 2)
|
|
763
|
-
* .build();
|
|
764
|
-
* ```
|
|
765
|
-
*/
|
|
766
|
-
export class ChainBuilderBase extends FilterChain {
|
|
767
|
-
presets;
|
|
768
|
-
/**
|
|
769
|
-
* @param presets - The filter presets to use
|
|
770
|
-
* @internal
|
|
771
|
-
*/
|
|
772
|
-
constructor(presets) {
|
|
773
|
-
super();
|
|
774
|
-
this.presets = presets;
|
|
775
|
-
}
|
|
776
|
-
/**
|
|
777
|
-
* Adds a scale filter to the chain.
|
|
778
|
-
*
|
|
779
|
-
* @param width - Target width
|
|
780
|
-
* @param height - Target height
|
|
781
|
-
* @param options - Additional scaling options
|
|
782
|
-
*
|
|
783
|
-
* @returns This instance for chaining
|
|
784
|
-
*
|
|
785
|
-
* @example
|
|
786
|
-
* ```typescript
|
|
787
|
-
* const chain = FilterPresets.chain()
|
|
788
|
-
* .scale(1920, 1080)
|
|
789
|
-
* .build();
|
|
790
|
-
* ```
|
|
791
|
-
*
|
|
792
|
-
* @example
|
|
793
|
-
* ```typescript
|
|
794
|
-
* const chain = FilterPresets.chain()
|
|
795
|
-
* .scale(1280, 720, { flags: 'lanczos' })
|
|
796
|
-
* .build();
|
|
797
|
-
* ```
|
|
798
|
-
*
|
|
799
|
-
* @see {@link FilterPresetBase.scale}
|
|
800
|
-
*/
|
|
801
|
-
scale(width, height, options) {
|
|
802
|
-
return this.add(this.presets.scale(width, height, options));
|
|
803
|
-
}
|
|
804
|
-
/**
|
|
805
|
-
* Adds a crop filter to the chain.
|
|
806
|
-
*
|
|
807
|
-
* @param width - Crop width
|
|
808
|
-
* @param height - Crop height
|
|
809
|
-
* @param x - X position (default: 0)
|
|
810
|
-
* @param y - Y position (default: 0)
|
|
811
|
-
*
|
|
812
|
-
* @returns This instance for chaining
|
|
813
|
-
*
|
|
814
|
-
* @example
|
|
815
|
-
* ```typescript
|
|
816
|
-
* const chain = FilterPresets.chain()
|
|
817
|
-
* .crop(640, 480)
|
|
818
|
-
* .build();
|
|
819
|
-
* ```
|
|
820
|
-
*
|
|
821
|
-
* @example
|
|
822
|
-
* ```typescript
|
|
823
|
-
* const chain = FilterPresets.chain()
|
|
824
|
-
* .crop(1920, 1080, 100, 50)
|
|
825
|
-
* .build();
|
|
826
|
-
* ```
|
|
827
|
-
*
|
|
828
|
-
* @see {@link FilterPresetBase.crop}
|
|
829
|
-
*/
|
|
830
|
-
crop(width, height, x = 0, y = 0) {
|
|
831
|
-
return this.add(this.presets.crop(width, height, x, y));
|
|
832
|
-
}
|
|
833
|
-
/**
|
|
834
|
-
* Adds an FPS filter to the chain.
|
|
835
|
-
*
|
|
836
|
-
* @param fps - Target frame rate
|
|
837
|
-
*
|
|
838
|
-
* @returns This instance for chaining
|
|
839
|
-
*
|
|
840
|
-
* @example
|
|
841
|
-
* ```typescript
|
|
842
|
-
* const chain = FilterPresets.chain()
|
|
843
|
-
* .fps(30)
|
|
844
|
-
* .build();
|
|
845
|
-
* ```
|
|
846
|
-
*
|
|
847
|
-
* @example
|
|
848
|
-
* ```typescript
|
|
849
|
-
* const chain = FilterPresets.chain()
|
|
850
|
-
* .fps(23.976)
|
|
851
|
-
* .build();
|
|
852
|
-
* ```
|
|
853
|
-
*
|
|
854
|
-
* @see {@link FilterPresetBase.fps}
|
|
855
|
-
*/
|
|
856
|
-
fps(fps) {
|
|
857
|
-
return this.add(this.presets.fps(fps));
|
|
858
|
-
}
|
|
859
|
-
/**
|
|
860
|
-
* Adds a format filter to the chain.
|
|
861
|
-
*
|
|
862
|
-
* @param pixelFormat - Target pixel format(s)
|
|
863
|
-
*
|
|
864
|
-
* @returns This instance for chaining
|
|
865
|
-
*
|
|
866
|
-
* @example
|
|
867
|
-
* ```typescript
|
|
868
|
-
* const chain = FilterPresets.chain()
|
|
869
|
-
* .format('yuv420p')
|
|
870
|
-
* .build();
|
|
871
|
-
* ```
|
|
872
|
-
*
|
|
873
|
-
* @example
|
|
874
|
-
* ```typescript
|
|
875
|
-
* const chain = FilterPresets.chain()
|
|
876
|
-
* .format(AV_PIX_FMT_RGB24)
|
|
877
|
-
* .build();
|
|
878
|
-
* ```
|
|
879
|
-
*
|
|
880
|
-
* @see {@link FilterPresetBase.format}
|
|
881
|
-
*/
|
|
882
|
-
format(pixelFormat) {
|
|
883
|
-
return this.add(this.presets.format(pixelFormat));
|
|
884
|
-
}
|
|
885
|
-
/**
|
|
886
|
-
* Adds a rotate filter to the chain.
|
|
887
|
-
*
|
|
888
|
-
* @param angle - Rotation angle in degrees
|
|
889
|
-
*
|
|
890
|
-
* @returns This instance for chaining
|
|
891
|
-
*
|
|
892
|
-
* @example
|
|
893
|
-
* ```typescript
|
|
894
|
-
* const chain = FilterPresets.chain()
|
|
895
|
-
* .rotate(45)
|
|
896
|
-
* .build();
|
|
897
|
-
* ```
|
|
898
|
-
*
|
|
899
|
-
* @example
|
|
900
|
-
* ```typescript
|
|
901
|
-
* const chain = FilterPresets.chain()
|
|
902
|
-
* .rotate(-90)
|
|
903
|
-
* .build();
|
|
904
|
-
* ```
|
|
905
|
-
*
|
|
906
|
-
* @see {@link FilterPresetBase.rotate}
|
|
907
|
-
*/
|
|
908
|
-
rotate(angle) {
|
|
909
|
-
return this.add(this.presets.rotate(angle));
|
|
910
|
-
}
|
|
911
|
-
/**
|
|
912
|
-
* Adds a horizontal flip filter to the chain.
|
|
913
|
-
*
|
|
914
|
-
* @returns This instance for chaining
|
|
915
|
-
*
|
|
916
|
-
* @example
|
|
917
|
-
* ```typescript
|
|
918
|
-
* const chain = FilterPresets.chain()
|
|
919
|
-
* .hflip()
|
|
920
|
-
* .build();
|
|
921
|
-
* ```
|
|
922
|
-
*
|
|
923
|
-
* @see {@link FilterPresetBase.hflip}
|
|
924
|
-
*/
|
|
925
|
-
hflip() {
|
|
926
|
-
return this.add(this.presets.hflip());
|
|
927
|
-
}
|
|
928
|
-
/**
|
|
929
|
-
* Adds a vertical flip filter to the chain.
|
|
930
|
-
*
|
|
931
|
-
* @returns This instance for chaining
|
|
932
|
-
*
|
|
933
|
-
* @example
|
|
934
|
-
* ```typescript
|
|
935
|
-
* const chain = FilterPresets.chain()
|
|
936
|
-
* .vflip()
|
|
937
|
-
* .build();
|
|
938
|
-
* ```
|
|
939
|
-
*
|
|
940
|
-
* @see {@link FilterPresetBase.vflip}
|
|
941
|
-
*/
|
|
942
|
-
vflip() {
|
|
943
|
-
return this.add(this.presets.vflip());
|
|
944
|
-
}
|
|
945
|
-
/**
|
|
946
|
-
* Adds a fade filter to the chain.
|
|
947
|
-
*
|
|
948
|
-
* @param type - Fade type ('in' or 'out')
|
|
949
|
-
* @param start - Start time in seconds
|
|
950
|
-
* @param duration - Fade duration in seconds
|
|
951
|
-
*
|
|
952
|
-
* @returns This instance for chaining
|
|
953
|
-
*
|
|
954
|
-
* @example
|
|
955
|
-
* ```typescript
|
|
956
|
-
* const chain = FilterPresets.chain()
|
|
957
|
-
* .fade('in', 0, 2)
|
|
958
|
-
* .build();
|
|
959
|
-
* ```
|
|
960
|
-
*
|
|
961
|
-
* @example
|
|
962
|
-
* ```typescript
|
|
963
|
-
* const chain = FilterPresets.chain()
|
|
964
|
-
* .fade('out', 10, 1.5)
|
|
965
|
-
* .build();
|
|
966
|
-
* ```
|
|
967
|
-
*
|
|
968
|
-
* @see {@link FilterPresetBase.fade}
|
|
969
|
-
*/
|
|
970
|
-
fade(type, start, duration) {
|
|
971
|
-
return this.add(this.presets.fade(type, start, duration));
|
|
972
|
-
}
|
|
973
|
-
/**
|
|
974
|
-
* Adds an overlay filter to the chain.
|
|
975
|
-
*
|
|
976
|
-
* @param x - X position (default: 0)
|
|
977
|
-
* @param y - Y position (default: 0)
|
|
978
|
-
* @param options - Additional overlay options
|
|
979
|
-
*
|
|
980
|
-
* @returns This instance for chaining
|
|
981
|
-
*
|
|
982
|
-
* @example
|
|
983
|
-
* ```typescript
|
|
984
|
-
* const chain = FilterPresets.chain()
|
|
985
|
-
* .overlay(100, 50)
|
|
986
|
-
* .build();
|
|
987
|
-
* ```
|
|
988
|
-
*
|
|
989
|
-
* @example
|
|
990
|
-
* ```typescript
|
|
991
|
-
* const chain = FilterPresets.chain()
|
|
992
|
-
* .overlay(10, 10, { enable: 'between(t,5,10)' })
|
|
993
|
-
* .build();
|
|
994
|
-
* ```
|
|
995
|
-
*
|
|
996
|
-
* @see {@link FilterPresetBase.overlay}
|
|
997
|
-
*/
|
|
998
|
-
overlay(x = 0, y = 0, options) {
|
|
999
|
-
return this.add(this.presets.overlay(x, y, options));
|
|
1000
|
-
}
|
|
1001
|
-
/**
|
|
1002
|
-
* Adds a volume filter to the chain.
|
|
1003
|
-
*
|
|
1004
|
-
* @param factor - Volume factor
|
|
1005
|
-
*
|
|
1006
|
-
* @returns This instance for chaining
|
|
1007
|
-
*
|
|
1008
|
-
* @example
|
|
1009
|
-
* ```typescript
|
|
1010
|
-
* const chain = FilterPresets.chain()
|
|
1011
|
-
* .volume(0.5)
|
|
1012
|
-
* .build();
|
|
1013
|
-
* ```
|
|
1014
|
-
*
|
|
1015
|
-
* @example
|
|
1016
|
-
* ```typescript
|
|
1017
|
-
* const chain = FilterPresets.chain()
|
|
1018
|
-
* .volume(2.0)
|
|
1019
|
-
* .build();
|
|
1020
|
-
* ```
|
|
1021
|
-
*
|
|
1022
|
-
* @see {@link FilterPresetBase.volume}
|
|
1023
|
-
*/
|
|
1024
|
-
volume(factor) {
|
|
1025
|
-
return this.add(this.presets.volume(factor));
|
|
1026
|
-
}
|
|
1027
|
-
/**
|
|
1028
|
-
* Adds an audio format filter to the chain.
|
|
1029
|
-
*
|
|
1030
|
-
* @param sampleFormat - Target sample format
|
|
1031
|
-
* @param sampleRate - Target sample rate (optional)
|
|
1032
|
-
* @param channelLayout - Target channel layout (optional)
|
|
1033
|
-
*
|
|
1034
|
-
* @returns This instance for chaining
|
|
1035
|
-
*
|
|
1036
|
-
* @example
|
|
1037
|
-
* ```typescript
|
|
1038
|
-
* const chain = FilterPresets.chain()
|
|
1039
|
-
* .aformat(AV_SAMPLE_FMT_FLT, 48000, 'stereo')
|
|
1040
|
-
* .build();
|
|
1041
|
-
* ```
|
|
1042
|
-
*
|
|
1043
|
-
* @example
|
|
1044
|
-
* ```typescript
|
|
1045
|
-
* const chain = FilterPresets.chain()
|
|
1046
|
-
* .aformat('s16', 44100)
|
|
1047
|
-
* .build();
|
|
1048
|
-
* ```
|
|
1049
|
-
*
|
|
1050
|
-
* @see {@link FilterPresetBase.aformat}
|
|
1051
|
-
*/
|
|
1052
|
-
aformat(sampleFormat, sampleRate, channelLayout) {
|
|
1053
|
-
return this.add(this.presets.aformat(sampleFormat, sampleRate, channelLayout));
|
|
1054
|
-
}
|
|
1055
|
-
/**
|
|
1056
|
-
* Adds an asetnsamples filter to the chain.
|
|
1057
|
-
*
|
|
1058
|
-
* @param samples - Number of samples per frame
|
|
1059
|
-
* @param padding - Whether to pad or drop samples (default: true)
|
|
1060
|
-
*
|
|
1061
|
-
* @returns This instance for chaining
|
|
1062
|
-
*
|
|
1063
|
-
* @example
|
|
1064
|
-
* ```typescript
|
|
1065
|
-
* const chain = FilterPresets.chain()
|
|
1066
|
-
* .asetnsamples(960)
|
|
1067
|
-
* .build();
|
|
1068
|
-
* ```
|
|
1069
|
-
*
|
|
1070
|
-
* @example
|
|
1071
|
-
* ```typescript
|
|
1072
|
-
* const chain = FilterPresets.chain()
|
|
1073
|
-
* .asetnsamples(1024, false)
|
|
1074
|
-
* .build();
|
|
1075
|
-
* ```
|
|
1076
|
-
*
|
|
1077
|
-
* @see {@link FilterPresetBase.asetnsamples}
|
|
1078
|
-
*/
|
|
1079
|
-
asetnsamples(samples, padding = true) {
|
|
1080
|
-
return this.add(this.presets.asetnsamples(samples, padding));
|
|
1081
|
-
}
|
|
1082
|
-
/**
|
|
1083
|
-
* Adds an atempo filter to the chain.
|
|
1084
|
-
*
|
|
1085
|
-
* @param factor - Tempo factor (0.5 to 2.0)
|
|
1086
|
-
*
|
|
1087
|
-
* @returns This instance for chaining
|
|
1088
|
-
*
|
|
1089
|
-
* @example
|
|
1090
|
-
* ```typescript
|
|
1091
|
-
* const chain = FilterPresets.chain()
|
|
1092
|
-
* .atempo(1.5)
|
|
1093
|
-
* .build();
|
|
1094
|
-
* ```
|
|
1095
|
-
*
|
|
1096
|
-
* @example
|
|
1097
|
-
* ```typescript
|
|
1098
|
-
* const chain = FilterPresets.chain()
|
|
1099
|
-
* .atempo(0.8)
|
|
1100
|
-
* .build();
|
|
1101
|
-
* ```
|
|
1102
|
-
*
|
|
1103
|
-
* @see {@link FilterPresetBase.atempo}
|
|
1104
|
-
*/
|
|
1105
|
-
atempo(factor) {
|
|
1106
|
-
return this.add(this.presets.atempo(factor));
|
|
1107
|
-
}
|
|
1108
|
-
/**
|
|
1109
|
-
* Adds an audio fade filter to the chain.
|
|
1110
|
-
*
|
|
1111
|
-
* @param type - Fade type ('in' or 'out')
|
|
1112
|
-
* @param start - Start time in seconds
|
|
1113
|
-
* @param duration - Fade duration in seconds
|
|
1114
|
-
*
|
|
1115
|
-
* @returns This instance for chaining
|
|
1116
|
-
*
|
|
1117
|
-
* @example
|
|
1118
|
-
* ```typescript
|
|
1119
|
-
* const chain = FilterPresets.chain()
|
|
1120
|
-
* .afade('in', 0, 3)
|
|
1121
|
-
* .build();
|
|
1122
|
-
* ```
|
|
38
|
+
* @param filterName - Name of the filter to check
|
|
39
|
+
*
|
|
40
|
+
* @returns True if the filter uses hardware acceleration
|
|
1123
41
|
*
|
|
1124
42
|
* @example
|
|
1125
43
|
* ```typescript
|
|
1126
|
-
*
|
|
1127
|
-
* .
|
|
1128
|
-
*
|
|
44
|
+
* if (FilterPreset.isHardwareFilter('scale_cuda')) {
|
|
45
|
+
* console.log('Hardware accelerated scaling');
|
|
46
|
+
* }
|
|
1129
47
|
* ```
|
|
1130
|
-
*
|
|
1131
|
-
* @see {@link FilterPresetBase.afade}
|
|
1132
48
|
*/
|
|
1133
|
-
|
|
1134
|
-
|
|
49
|
+
static isHardwareFilter(filterName) {
|
|
50
|
+
const filter = Filter.getByName(filterName);
|
|
51
|
+
if (!filter) {
|
|
52
|
+
return false;
|
|
53
|
+
}
|
|
54
|
+
// Check if filter has hardware device flag
|
|
55
|
+
return (filter.flags & AVFILTER_FLAG_HWDEVICE) !== 0;
|
|
1135
56
|
}
|
|
1136
57
|
/**
|
|
1137
|
-
*
|
|
58
|
+
* Creates a new filter chain builder.
|
|
1138
59
|
*
|
|
1139
|
-
* @param
|
|
1140
|
-
* @param duration - Duration mode (default: 'longest')
|
|
60
|
+
* @param hardware - Optional hardware context for hardware-accelerated filters
|
|
1141
61
|
*
|
|
1142
|
-
* @returns
|
|
62
|
+
* @returns A new FilterPreset instance for chaining
|
|
1143
63
|
*
|
|
1144
64
|
* @example
|
|
1145
65
|
* ```typescript
|
|
1146
|
-
*
|
|
1147
|
-
*
|
|
66
|
+
* // Software filter chain
|
|
67
|
+
* const filter = FilterPreset.chain()
|
|
68
|
+
* .scale(1280, 720)
|
|
69
|
+
* .fps(30)
|
|
1148
70
|
* .build();
|
|
1149
71
|
* ```
|
|
1150
72
|
*
|
|
1151
73
|
* @example
|
|
1152
74
|
* ```typescript
|
|
1153
|
-
*
|
|
1154
|
-
*
|
|
75
|
+
* // Hardware filter chain
|
|
76
|
+
* const hw = HardwareContext.auto();
|
|
77
|
+
* const filter = FilterPreset.chain(hw)
|
|
78
|
+
* .scale(1280, 720)
|
|
79
|
+
* .deinterlace()
|
|
1155
80
|
* .build();
|
|
1156
81
|
* ```
|
|
1157
|
-
*
|
|
1158
|
-
* @see {@link FilterPresetBase.amix}
|
|
1159
82
|
*/
|
|
1160
|
-
|
|
1161
|
-
|
|
83
|
+
static chain(hardware) {
|
|
84
|
+
const preset = new FilterPreset(hardware);
|
|
85
|
+
return preset;
|
|
1162
86
|
}
|
|
1163
|
-
// ========== New Critical Filter Chain Methods ==========
|
|
1164
87
|
/**
|
|
1165
|
-
* Adds a
|
|
88
|
+
* Adds a custom filter string to the chain.
|
|
1166
89
|
*
|
|
1167
|
-
* @param
|
|
1168
|
-
* @param height - Target height
|
|
1169
|
-
* @param x - X position of input
|
|
1170
|
-
* @param y - Y position of input
|
|
1171
|
-
* @param color - Padding color
|
|
90
|
+
* @param filter - Custom filter string
|
|
1172
91
|
*
|
|
1173
92
|
* @returns This instance for chaining
|
|
1174
93
|
*
|
|
1175
94
|
* @example
|
|
1176
95
|
* ```typescript
|
|
1177
|
-
* chain.
|
|
96
|
+
* chain.custom('myfilter=param1:param2')
|
|
1178
97
|
* ```
|
|
1179
|
-
*
|
|
1180
|
-
* @see {@link FilterPresetBase.pad}
|
|
1181
98
|
*/
|
|
1182
|
-
|
|
1183
|
-
return this.add(
|
|
99
|
+
custom(filter) {
|
|
100
|
+
return this.add(filter);
|
|
1184
101
|
}
|
|
1185
102
|
/**
|
|
1186
|
-
*
|
|
103
|
+
* Builds the final filter string.
|
|
1187
104
|
*
|
|
1188
|
-
* @param
|
|
1189
|
-
* @param end - End time in seconds
|
|
1190
|
-
* @param duration - Duration in seconds
|
|
105
|
+
* @param separator - Separator between filters (default: ',')
|
|
1191
106
|
*
|
|
1192
|
-
* @returns
|
|
107
|
+
* @returns Combined filter string
|
|
1193
108
|
*
|
|
1194
109
|
* @example
|
|
1195
110
|
* ```typescript
|
|
1196
|
-
* chain.
|
|
111
|
+
* const filterString = chain.build() // "scale=1920:1080,fps=30"
|
|
1197
112
|
* ```
|
|
1198
|
-
*
|
|
1199
|
-
* @see {@link FilterPresetBase.trim}
|
|
1200
113
|
*/
|
|
1201
|
-
|
|
1202
|
-
return this.
|
|
114
|
+
build(separator = ',') {
|
|
115
|
+
return this.filters.join(separator);
|
|
1203
116
|
}
|
|
1204
117
|
/**
|
|
1205
|
-
*
|
|
1206
|
-
*
|
|
1207
|
-
* @param expression - PTS expression
|
|
118
|
+
* Returns the filters as an array.
|
|
1208
119
|
*
|
|
1209
|
-
* @returns
|
|
120
|
+
* @returns Array of filter strings
|
|
1210
121
|
*
|
|
1211
122
|
* @example
|
|
1212
123
|
* ```typescript
|
|
1213
|
-
* chain.
|
|
124
|
+
* const filters = chain.toArray() // ["scale=1920:1080", "fps=30"]
|
|
1214
125
|
* ```
|
|
1215
|
-
*
|
|
1216
|
-
* @see {@link FilterPresetBase.setpts}
|
|
1217
126
|
*/
|
|
1218
|
-
|
|
1219
|
-
return this.
|
|
127
|
+
toArray() {
|
|
128
|
+
return [...this.filters];
|
|
1220
129
|
}
|
|
1221
130
|
/**
|
|
1222
|
-
* Adds
|
|
131
|
+
* Adds a scale filter to the chain.
|
|
132
|
+
* Automatically selects hardware-specific scaler if hardware context is set.
|
|
1223
133
|
*
|
|
1224
|
-
* @param
|
|
134
|
+
* @param width - Target width in pixels
|
|
135
|
+
*
|
|
136
|
+
* @param height - Target height in pixels
|
|
137
|
+
*
|
|
138
|
+
* @param options - Additional scaling options (e.g., flags for algorithm, npp for CUDA)
|
|
1225
139
|
*
|
|
1226
140
|
* @returns This instance for chaining
|
|
1227
141
|
*
|
|
1228
142
|
* @example
|
|
1229
143
|
* ```typescript
|
|
1230
|
-
* chain.
|
|
144
|
+
* chain.scale(1920, 1080) // Scale to Full HD
|
|
145
|
+
* chain.scale(640, 480, { flags: 'lanczos' }) // With specific algorithm
|
|
146
|
+
* chain.scale(1920, 1080, { npp: true }) // Use NPP for CUDA
|
|
1231
147
|
* ```
|
|
1232
148
|
*
|
|
1233
|
-
* @see {@link
|
|
149
|
+
* @see {@link https://ffmpeg.org/ffmpeg-filters.html#scale | FFmpeg scale filter}
|
|
1234
150
|
*/
|
|
1235
|
-
|
|
1236
|
-
|
|
151
|
+
scale(width, height, options) {
|
|
152
|
+
if (!this.support.scale) {
|
|
153
|
+
return this;
|
|
154
|
+
}
|
|
155
|
+
if (this.hardware) {
|
|
156
|
+
// Special handling for different hardware scalers
|
|
157
|
+
let filterName;
|
|
158
|
+
if (this.hardware.deviceType === AV_HWDEVICE_TYPE_CUDA && options?.npp) {
|
|
159
|
+
filterName = 'scale_npp';
|
|
160
|
+
}
|
|
161
|
+
else if (this.hardware.deviceType === AV_HWDEVICE_TYPE_RKMPP) {
|
|
162
|
+
filterName = 'scale_rkrga'; // RKMPP uses RGA for scaling
|
|
163
|
+
}
|
|
164
|
+
else if (this.hardware.deviceType === AV_HWDEVICE_TYPE_VIDEOTOOLBOX) {
|
|
165
|
+
filterName = 'scale_vt'; // VideoToolbox uses scale_vt
|
|
166
|
+
}
|
|
167
|
+
else {
|
|
168
|
+
filterName = `scale_${this.hardware.deviceTypeName}`;
|
|
169
|
+
}
|
|
170
|
+
let filter = `${filterName}=${width}:${height}`;
|
|
171
|
+
if (options) {
|
|
172
|
+
for (const [key, value] of Object.entries(options)) {
|
|
173
|
+
if (key !== 'npp') {
|
|
174
|
+
// Skip our special npp flag
|
|
175
|
+
filter += `:${key}=${value}`;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
this.add(filter);
|
|
180
|
+
}
|
|
181
|
+
else {
|
|
182
|
+
const flags = options?.flags;
|
|
183
|
+
const base = `scale=${width}:${height}`;
|
|
184
|
+
const result = flags ? `${base}:flags=${flags}` : base;
|
|
185
|
+
this.add(result);
|
|
186
|
+
}
|
|
187
|
+
return this;
|
|
1237
188
|
}
|
|
1238
189
|
/**
|
|
1239
|
-
* Adds a
|
|
1240
|
-
*
|
|
1241
|
-
* @param ratio - Sample aspect ratio
|
|
190
|
+
* Adds a crop filter to the chain.
|
|
1242
191
|
*
|
|
1243
|
-
* @
|
|
192
|
+
* @param width - Width of the cropped area
|
|
1244
193
|
*
|
|
1245
|
-
* @
|
|
1246
|
-
* ```typescript
|
|
1247
|
-
* chain.setsar('1:1') // Square pixels
|
|
1248
|
-
* ```
|
|
194
|
+
* @param height - Height of the cropped area
|
|
1249
195
|
*
|
|
1250
|
-
* @
|
|
1251
|
-
*/
|
|
1252
|
-
setsar(ratio) {
|
|
1253
|
-
return this.add(this.presets.setsar(ratio));
|
|
1254
|
-
}
|
|
1255
|
-
/**
|
|
1256
|
-
* Adds a setdar filter to the chain.
|
|
196
|
+
* @param x - X coordinate of top-left corner (default: 0)
|
|
1257
197
|
*
|
|
1258
|
-
* @param
|
|
198
|
+
* @param y - Y coordinate of top-left corner (default: 0)
|
|
1259
199
|
*
|
|
1260
200
|
* @returns This instance for chaining
|
|
1261
201
|
*
|
|
1262
202
|
* @example
|
|
1263
203
|
* ```typescript
|
|
1264
|
-
* chain.
|
|
204
|
+
* chain.crop(640, 480, 100, 100) // Crop 640x480 area starting at (100,100)
|
|
205
|
+
* chain.crop(1280, 720) // Crop from top-left corner
|
|
1265
206
|
* ```
|
|
1266
207
|
*
|
|
1267
|
-
* @see {@link
|
|
208
|
+
* @see {@link https://ffmpeg.org/ffmpeg-filters.html#crop | FFmpeg crop filter}
|
|
1268
209
|
*/
|
|
1269
|
-
|
|
1270
|
-
|
|
210
|
+
crop(width, height, x = 0, y = 0) {
|
|
211
|
+
this.add(`crop=${width}:${height}:${x}:${y}`);
|
|
212
|
+
return this;
|
|
1271
213
|
}
|
|
1272
214
|
/**
|
|
1273
|
-
* Adds
|
|
215
|
+
* Adds a blur filter to the chain (hardware-specific).
|
|
216
|
+
* Only available for hardware presets that support blur
|
|
217
|
+
*
|
|
218
|
+
* @param type - Blur type (default: 'avg')
|
|
1274
219
|
*
|
|
1275
|
-
* @param
|
|
1276
|
-
* @param padDuration - Padding duration to add
|
|
220
|
+
* @param radius - Blur radius (optional)
|
|
1277
221
|
*
|
|
1278
222
|
* @returns This instance for chaining
|
|
1279
223
|
*
|
|
1280
224
|
* @example
|
|
1281
225
|
* ```typescript
|
|
1282
|
-
* chain.
|
|
226
|
+
* const chain = FilterPresets.chain()
|
|
227
|
+
* .blur('gaussian', 5)
|
|
228
|
+
* .build();
|
|
1283
229
|
* ```
|
|
1284
230
|
*
|
|
1285
|
-
* @
|
|
231
|
+
* @example
|
|
232
|
+
* ```typescript
|
|
233
|
+
* const chain = FilterPresets.chain()
|
|
234
|
+
* .blur('box')
|
|
235
|
+
* .build();
|
|
236
|
+
* ```
|
|
1286
237
|
*/
|
|
1287
|
-
|
|
1288
|
-
|
|
238
|
+
blur(type = 'avg', radius) {
|
|
239
|
+
if (!this.support.blur) {
|
|
240
|
+
return this;
|
|
241
|
+
}
|
|
242
|
+
if (this.hardware) {
|
|
243
|
+
let filter = null;
|
|
244
|
+
switch (this.hardware.deviceType) {
|
|
245
|
+
case AV_HWDEVICE_TYPE_CUDA:
|
|
246
|
+
filter = radius ? `bilateral_cuda=sigmaS=${radius}` : 'bilateral_cuda';
|
|
247
|
+
break;
|
|
248
|
+
case AV_HWDEVICE_TYPE_VULKAN:
|
|
249
|
+
filter = type === 'gaussian' ? (radius ? `gblur_vulkan=sigma=${radius}` : 'gblur_vulkan') : radius ? `avgblur_vulkan=sizeX=${radius}` : 'avgblur_vulkan';
|
|
250
|
+
break;
|
|
251
|
+
case AV_HWDEVICE_TYPE_OPENCL:
|
|
252
|
+
filter = type === 'box' ? (radius ? `boxblur_opencl=luma_radius=${radius}` : 'boxblur_opencl') : radius ? `avgblur_opencl=sizeX=${radius}` : 'avgblur_opencl';
|
|
253
|
+
break;
|
|
254
|
+
default:
|
|
255
|
+
filter = null;
|
|
256
|
+
}
|
|
257
|
+
if (filter) {
|
|
258
|
+
this.add(filter);
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
else {
|
|
262
|
+
const filter = type === 'gaussian' ? (radius ? `gblur=sigma=${radius}` : 'gblur') : radius ? `avgblur=sizeX=${radius}` : 'avgblur';
|
|
263
|
+
this.add(filter);
|
|
264
|
+
}
|
|
265
|
+
return this;
|
|
1289
266
|
}
|
|
1290
267
|
/**
|
|
1291
|
-
* Adds a
|
|
268
|
+
* Adds a sharpen filter to the chain (hardware-specific).
|
|
269
|
+
* Only available for hardware presets that support sharpening
|
|
1292
270
|
*
|
|
1293
|
-
* @param
|
|
271
|
+
* @param amount - Sharpen amount (optional)
|
|
1294
272
|
*
|
|
1295
273
|
* @returns This instance for chaining
|
|
1296
274
|
*
|
|
1297
275
|
* @example
|
|
1298
276
|
* ```typescript
|
|
1299
|
-
* chain.
|
|
277
|
+
* const chain = FilterPresets.chain()
|
|
278
|
+
* .sharpen(1.5)
|
|
279
|
+
* .build();
|
|
1300
280
|
* ```
|
|
1301
281
|
*
|
|
1302
|
-
* @
|
|
282
|
+
* @example
|
|
283
|
+
* ```typescript
|
|
284
|
+
* const chain = FilterPresets.chain()
|
|
285
|
+
* .sharpen()
|
|
286
|
+
* .build();
|
|
287
|
+
* ```
|
|
1303
288
|
*/
|
|
1304
|
-
|
|
1305
|
-
|
|
289
|
+
sharpen(amount) {
|
|
290
|
+
if (!this.support.sharpen) {
|
|
291
|
+
return this;
|
|
292
|
+
}
|
|
293
|
+
if (this.hardware) {
|
|
294
|
+
let filter = null;
|
|
295
|
+
switch (this.hardware.deviceType) {
|
|
296
|
+
case AV_HWDEVICE_TYPE_VAAPI:
|
|
297
|
+
filter = amount ? `sharpness_vaapi=sharpness=${amount}` : 'sharpness_vaapi';
|
|
298
|
+
break;
|
|
299
|
+
case AV_HWDEVICE_TYPE_OPENCL:
|
|
300
|
+
filter = amount ? `unsharp_opencl=amount=${amount}` : 'unsharp_opencl';
|
|
301
|
+
break;
|
|
302
|
+
case AV_HWDEVICE_TYPE_CUDA:
|
|
303
|
+
// CUDA uses NPP for sharpening
|
|
304
|
+
filter = 'sharpen_npp';
|
|
305
|
+
break;
|
|
306
|
+
default:
|
|
307
|
+
filter = null;
|
|
308
|
+
}
|
|
309
|
+
if (filter) {
|
|
310
|
+
this.add(filter);
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
else {
|
|
314
|
+
const filter = amount ? `unsharp=amount=${amount}` : 'unsharp';
|
|
315
|
+
this.add(filter);
|
|
316
|
+
}
|
|
317
|
+
return this;
|
|
1306
318
|
}
|
|
1307
319
|
/**
|
|
1308
|
-
* Adds an
|
|
320
|
+
* Adds an FPS filter to change frame rate.
|
|
1309
321
|
*
|
|
1310
|
-
* @param
|
|
322
|
+
* @param fps - Target frames per second
|
|
1311
323
|
*
|
|
1312
324
|
* @returns This instance for chaining
|
|
1313
325
|
*
|
|
1314
326
|
* @example
|
|
1315
327
|
* ```typescript
|
|
1316
|
-
* chain.
|
|
328
|
+
* chain.fps(30) // Convert to 30 FPS
|
|
329
|
+
* chain.fps(23.976) // Film frame rate
|
|
1317
330
|
* ```
|
|
1318
331
|
*
|
|
1319
|
-
* @see {@link
|
|
332
|
+
* @see {@link https://ffmpeg.org/ffmpeg-filters.html#fps | FFmpeg fps filter}
|
|
1320
333
|
*/
|
|
1321
|
-
|
|
1322
|
-
|
|
334
|
+
fps(fps) {
|
|
335
|
+
this.add(`fps=${fps}`);
|
|
336
|
+
return this;
|
|
1323
337
|
}
|
|
1324
338
|
/**
|
|
1325
|
-
* Adds a
|
|
339
|
+
* Adds a format filter to convert pixel format.
|
|
1326
340
|
*
|
|
1327
|
-
* @param
|
|
1328
|
-
* @param v - Number of video streams
|
|
1329
|
-
* @param a - Number of audio streams
|
|
341
|
+
* @param pixelFormat - Target pixel format(s) - AVPixelFormat enum, or array
|
|
1330
342
|
*
|
|
1331
343
|
* @returns This instance for chaining
|
|
1332
344
|
*
|
|
1333
345
|
* @example
|
|
1334
346
|
* ```typescript
|
|
1335
|
-
*
|
|
347
|
+
* // Single format
|
|
348
|
+
* chain.format(AV_PIX_FMT_YUV420P);
|
|
349
|
+
*
|
|
350
|
+
* // Multiple formats (tries formats in order)
|
|
351
|
+
* chain.format([AV_PIX_FMT_YUV420P, AV_PIX_FMT_RGB24]);
|
|
1336
352
|
* ```
|
|
1337
353
|
*
|
|
1338
|
-
* @see {@link
|
|
354
|
+
* @see {@link https://ffmpeg.org/ffmpeg-filters.html#format | FFmpeg format filter}
|
|
1339
355
|
*/
|
|
1340
|
-
|
|
1341
|
-
|
|
356
|
+
format(pixelFormat) {
|
|
357
|
+
if (Array.isArray(pixelFormat)) {
|
|
358
|
+
// Create a chain of format filters
|
|
359
|
+
const formats = pixelFormat.map((fmt) => {
|
|
360
|
+
const formatName = typeof fmt === 'string' ? fmt : (avGetPixFmtName(fmt) ?? 'yuv420p');
|
|
361
|
+
return `format=${formatName}`;
|
|
362
|
+
});
|
|
363
|
+
this.add(formats.join(','));
|
|
364
|
+
}
|
|
365
|
+
else {
|
|
366
|
+
const formatName = typeof pixelFormat === 'string' ? pixelFormat : (avGetPixFmtName(pixelFormat) ?? 'yuv420p');
|
|
367
|
+
this.add(`format=${formatName}`);
|
|
368
|
+
}
|
|
369
|
+
return this;
|
|
1342
370
|
}
|
|
1343
371
|
/**
|
|
1344
|
-
* Adds
|
|
372
|
+
* Adds a rotate filter to the chain.
|
|
1345
373
|
*
|
|
1346
|
-
* @param
|
|
374
|
+
* @param angle - Rotation angle in degrees
|
|
1347
375
|
*
|
|
1348
376
|
* @returns This instance for chaining
|
|
1349
377
|
*
|
|
1350
378
|
* @example
|
|
1351
379
|
* ```typescript
|
|
1352
|
-
* chain.
|
|
380
|
+
* chain.rotate(90) // Rotate 90 degrees clockwise
|
|
381
|
+
* chain.rotate(-45) // Rotate 45 degrees counter-clockwise
|
|
1353
382
|
* ```
|
|
1354
383
|
*
|
|
1355
|
-
* @see {@link
|
|
384
|
+
* @see {@link https://ffmpeg.org/ffmpeg-filters.html#rotate | FFmpeg rotate filter}
|
|
1356
385
|
*/
|
|
1357
|
-
|
|
1358
|
-
|
|
386
|
+
rotate(angle) {
|
|
387
|
+
this.add(`rotate=${angle}*PI/180`);
|
|
388
|
+
return this;
|
|
1359
389
|
}
|
|
1360
390
|
/**
|
|
1361
|
-
* Adds a
|
|
391
|
+
* Adds a flip filter to the chain (hardware-specific).
|
|
392
|
+
* Falls back to hflip/vflip if hardware flip not available
|
|
1362
393
|
*
|
|
1363
|
-
* @param
|
|
394
|
+
* @param direction - Flip direction ('h' or 'v')
|
|
1364
395
|
*
|
|
1365
396
|
* @returns This instance for chaining
|
|
1366
397
|
*
|
|
1367
398
|
* @example
|
|
1368
399
|
* ```typescript
|
|
1369
|
-
* chain.
|
|
400
|
+
* const chain = FilterPresets.chain()
|
|
401
|
+
* .flip('h')
|
|
402
|
+
* .build();
|
|
1370
403
|
* ```
|
|
1371
404
|
*
|
|
1372
|
-
* @
|
|
405
|
+
* @example
|
|
406
|
+
* ```typescript
|
|
407
|
+
* const chain = FilterPresets.chain()
|
|
408
|
+
* .flip('v')
|
|
409
|
+
* .build();
|
|
410
|
+
* ```
|
|
1373
411
|
*/
|
|
1374
|
-
|
|
1375
|
-
|
|
412
|
+
flip(direction) {
|
|
413
|
+
if (!this.support.flip) {
|
|
414
|
+
return this;
|
|
415
|
+
}
|
|
416
|
+
if (this.hardware) {
|
|
417
|
+
if (this.hardware.deviceType === AV_HWDEVICE_TYPE_VULKAN) {
|
|
418
|
+
if (direction === 'v') {
|
|
419
|
+
this.add('vflip_vulkan');
|
|
420
|
+
}
|
|
421
|
+
else {
|
|
422
|
+
this.add('hflip_vulkan');
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
else {
|
|
427
|
+
if (direction === 'v') {
|
|
428
|
+
this.add('vflip');
|
|
429
|
+
}
|
|
430
|
+
else {
|
|
431
|
+
this.add('hflip');
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
return this;
|
|
1376
435
|
}
|
|
1377
436
|
/**
|
|
1378
|
-
* Adds a
|
|
437
|
+
* Adds a stack filter to the chain (hardware-specific).
|
|
438
|
+
* Only available for hardware presets that support stacking
|
|
1379
439
|
*
|
|
1380
|
-
* @param
|
|
440
|
+
* @param type - Stack type ('h' for horizontal, 'v' for vertical, 'x' for grid)
|
|
441
|
+
*
|
|
442
|
+
* @param inputs - Number of inputs (default: 2)
|
|
1381
443
|
*
|
|
1382
444
|
* @returns This instance for chaining
|
|
1383
445
|
*
|
|
1384
446
|
* @example
|
|
1385
447
|
* ```typescript
|
|
1386
|
-
* chain.
|
|
448
|
+
* const chain = FilterPresets.chain()
|
|
449
|
+
* .stack('h', 2)
|
|
450
|
+
* .build();
|
|
1387
451
|
* ```
|
|
1388
452
|
*
|
|
1389
|
-
* @
|
|
453
|
+
* @example
|
|
454
|
+
* ```typescript
|
|
455
|
+
* const chain = FilterPresets.chain()
|
|
456
|
+
* .stack('x', 4)
|
|
457
|
+
* .build();
|
|
458
|
+
* ```
|
|
1390
459
|
*/
|
|
1391
|
-
|
|
1392
|
-
|
|
460
|
+
stack(type, inputs = 2) {
|
|
461
|
+
if (!this.support.stack) {
|
|
462
|
+
return this;
|
|
463
|
+
}
|
|
464
|
+
if (this.hardware) {
|
|
465
|
+
if (this.hardware.deviceType === AV_HWDEVICE_TYPE_VAAPI || this.hardware.deviceType === AV_HWDEVICE_TYPE_QSV) {
|
|
466
|
+
const filter = `${type}stack_${this.hardware.deviceTypeName}=inputs=${inputs}`;
|
|
467
|
+
this.add(filter);
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
else {
|
|
471
|
+
const filter = type === 'h' ? `hstack=inputs=${inputs}` : type === 'v' ? `vstack=inputs=${inputs}` : `xstack=inputs=${inputs}`;
|
|
472
|
+
this.add(filter);
|
|
473
|
+
}
|
|
474
|
+
return this;
|
|
1393
475
|
}
|
|
1394
476
|
/**
|
|
1395
|
-
*
|
|
477
|
+
* Creates a tonemap filter.
|
|
478
|
+
* Used for HDR to SDR conversion with hardware acceleration.
|
|
1396
479
|
*
|
|
1397
|
-
* @param
|
|
1398
|
-
* @param TP - True peak (dBTP)
|
|
1399
|
-
* @param LRA - Loudness range (LU)
|
|
480
|
+
* @param alg - Tonemapping algorithm (e.g., 'hable', 'reinhard', 'mobius', etc.)
|
|
1400
481
|
*
|
|
1401
|
-
* @
|
|
482
|
+
* @param options - Tonemapping options
|
|
483
|
+
*
|
|
484
|
+
* @returns Hardware tonemap filter string or null if not supported
|
|
1402
485
|
*
|
|
1403
486
|
* @example
|
|
1404
487
|
* ```typescript
|
|
1405
|
-
*
|
|
488
|
+
* const filter = hwPresets.tonemap();
|
|
1406
489
|
* ```
|
|
1407
490
|
*
|
|
1408
|
-
* @
|
|
491
|
+
* @example
|
|
492
|
+
* ```typescript
|
|
493
|
+
* const filter = hwPresets.tonemap({ tonemap: 'hable', desat: '0' });
|
|
494
|
+
* ```
|
|
1409
495
|
*/
|
|
1410
|
-
|
|
1411
|
-
|
|
496
|
+
tonemap(alg, options) {
|
|
497
|
+
if (!this.support.tonemap) {
|
|
498
|
+
return this;
|
|
499
|
+
}
|
|
500
|
+
if (this.hardware) {
|
|
501
|
+
// VideoToolbox uses different filter name
|
|
502
|
+
const filterName = this.hardware.deviceType === AV_HWDEVICE_TYPE_VIDEOTOOLBOX ? 'tonemap_videotoolbox' : `tonemap_${this.hardware.deviceTypeName}`;
|
|
503
|
+
let filter = `${filterName}=${alg}`;
|
|
504
|
+
if (options) {
|
|
505
|
+
const opts = Object.entries(options)
|
|
506
|
+
.map(([k, v]) => `${k}=${v}`)
|
|
507
|
+
.join(':');
|
|
508
|
+
filter += `=${opts}`;
|
|
509
|
+
}
|
|
510
|
+
this.add(filter);
|
|
511
|
+
}
|
|
512
|
+
else {
|
|
513
|
+
let filter = `tonemap=${alg}`;
|
|
514
|
+
if (options) {
|
|
515
|
+
const opts = Object.entries(options)
|
|
516
|
+
.map(([k, v]) => `${k}=${v}`)
|
|
517
|
+
.join(':');
|
|
518
|
+
filter += `=${opts}`;
|
|
519
|
+
}
|
|
520
|
+
this.add(filter);
|
|
521
|
+
}
|
|
522
|
+
return this;
|
|
1412
523
|
}
|
|
1413
524
|
/**
|
|
1414
|
-
*
|
|
525
|
+
* Creates a fade filter string for video.
|
|
1415
526
|
*
|
|
1416
|
-
* @param
|
|
1417
|
-
* @param decays - Decay times
|
|
1418
|
-
* @param points - Transfer function points
|
|
1419
|
-
* @param gain - Output gain
|
|
527
|
+
* @param type - Fade type ('in' or 'out')
|
|
1420
528
|
*
|
|
1421
|
-
* @
|
|
529
|
+
* @param start - Start time in seconds
|
|
530
|
+
*
|
|
531
|
+
* @param duration - Fade duration in seconds
|
|
532
|
+
*
|
|
533
|
+
* @returns Filter string or null if not supported
|
|
1422
534
|
*
|
|
1423
535
|
* @example
|
|
1424
536
|
* ```typescript
|
|
1425
|
-
*
|
|
537
|
+
* presets.fade('in', 0, 2) // 2-second fade in from start
|
|
538
|
+
* presets.fade('out', 10, 1) // 1-second fade out at 10 seconds
|
|
1426
539
|
* ```
|
|
1427
540
|
*
|
|
1428
|
-
* @see {@link
|
|
541
|
+
* @see {@link https://ffmpeg.org/ffmpeg-filters.html#fade | FFmpeg fade filter}
|
|
1429
542
|
*/
|
|
1430
|
-
|
|
1431
|
-
|
|
543
|
+
fade(type, start, duration) {
|
|
544
|
+
this.add(`fade=t=${type}:st=${start}:d=${duration}`);
|
|
545
|
+
return this;
|
|
1432
546
|
}
|
|
1433
547
|
/**
|
|
1434
|
-
*
|
|
1435
|
-
* Only available for hardware presets that support transpose
|
|
548
|
+
* Creates an overlay filter string to composite two video streams.
|
|
1436
549
|
*
|
|
1437
|
-
* @param
|
|
550
|
+
* @param x - X position for overlay (default: 0)
|
|
1438
551
|
*
|
|
1439
|
-
* @
|
|
552
|
+
* @param y - Y position for overlay (default: 0)
|
|
1440
553
|
*
|
|
1441
|
-
* @
|
|
1442
|
-
*
|
|
1443
|
-
*
|
|
1444
|
-
* .transpose('clock')
|
|
1445
|
-
* .build();
|
|
1446
|
-
* ```
|
|
554
|
+
* @param options - Additional overlay options
|
|
555
|
+
*
|
|
556
|
+
* @returns Filter string or null if not supported
|
|
1447
557
|
*
|
|
1448
558
|
* @example
|
|
1449
559
|
* ```typescript
|
|
1450
|
-
*
|
|
1451
|
-
*
|
|
1452
|
-
*
|
|
560
|
+
* // Basic overlay at position
|
|
561
|
+
* presets.overlay(100, 50);
|
|
562
|
+
*
|
|
563
|
+
* // With additional options
|
|
564
|
+
* presets.overlay(0, 0, { format: 'yuv420' });
|
|
1453
565
|
* ```
|
|
566
|
+
*
|
|
567
|
+
* @see {@link https://ffmpeg.org/ffmpeg-filters.html#overlay | FFmpeg overlay filter}
|
|
1454
568
|
*/
|
|
1455
|
-
|
|
1456
|
-
if (
|
|
1457
|
-
return this
|
|
569
|
+
overlay(x = 0, y = 0, options) {
|
|
570
|
+
if (!this.support.overlay) {
|
|
571
|
+
return this;
|
|
572
|
+
}
|
|
573
|
+
if (this.hardware) {
|
|
574
|
+
// Special handling for RKMPP which uses RGA
|
|
575
|
+
const filterName = this.hardware.deviceType === AV_HWDEVICE_TYPE_RKMPP ? 'overlay_rkrga' : `overlay_${this.hardware.deviceTypeName}`;
|
|
576
|
+
let filter = `${filterName}=${x}:${y}`;
|
|
577
|
+
if (options) {
|
|
578
|
+
for (const [key, value] of Object.entries(options)) {
|
|
579
|
+
filter += `:${key}=${value}`;
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
this.add(filter);
|
|
583
|
+
}
|
|
584
|
+
else {
|
|
585
|
+
let filter = `overlay=${x}:${y}`;
|
|
586
|
+
if (options) {
|
|
587
|
+
for (const [key, value] of Object.entries(options)) {
|
|
588
|
+
filter += `:${key}=${value}`;
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
this.add(filter);
|
|
1458
592
|
}
|
|
1459
|
-
return this
|
|
593
|
+
return this;
|
|
1460
594
|
}
|
|
1461
595
|
/**
|
|
1462
|
-
*
|
|
1463
|
-
* Only available for hardware presets that support tonemapping
|
|
596
|
+
* Creates a volume filter string for audio.
|
|
1464
597
|
*
|
|
1465
|
-
* @param
|
|
598
|
+
* @param factor - Volume multiplication factor (1.0 = unchanged, 2.0 = double)
|
|
1466
599
|
*
|
|
1467
|
-
* @returns
|
|
600
|
+
* @returns Filter string or null if not supported
|
|
1468
601
|
*
|
|
1469
602
|
* @example
|
|
1470
603
|
* ```typescript
|
|
1471
|
-
*
|
|
1472
|
-
*
|
|
1473
|
-
* .build();
|
|
604
|
+
* presets.volume(0.5) // Reduce volume by 50%
|
|
605
|
+
* presets.volume(1.5) // Increase volume by 50%
|
|
1474
606
|
* ```
|
|
1475
607
|
*
|
|
1476
|
-
* @
|
|
1477
|
-
* ```typescript
|
|
1478
|
-
* const chain = FilterPresets.chain()
|
|
1479
|
-
* .tonemap({ tonemap: 'hable', desat: '0' })
|
|
1480
|
-
* .build();
|
|
1481
|
-
* ```
|
|
608
|
+
* @see {@link https://ffmpeg.org/ffmpeg-filters.html#volume | FFmpeg volume filter}
|
|
1482
609
|
*/
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
}
|
|
1487
|
-
return this.add(null);
|
|
610
|
+
volume(factor) {
|
|
611
|
+
this.add(`volume=${factor}`);
|
|
612
|
+
return this;
|
|
1488
613
|
}
|
|
1489
614
|
/**
|
|
1490
|
-
*
|
|
1491
|
-
* Only available for hardware presets that support deinterlacing
|
|
615
|
+
* Creates an audio format filter string.
|
|
1492
616
|
*
|
|
1493
|
-
* @param
|
|
617
|
+
* @param sampleFormat - Target sample format (e.g., 's16', 'fltp')
|
|
1494
618
|
*
|
|
1495
|
-
* @
|
|
619
|
+
* @param sampleRate - Target sample rate in Hz (optional)
|
|
1496
620
|
*
|
|
1497
|
-
* @
|
|
1498
|
-
*
|
|
1499
|
-
*
|
|
1500
|
-
* .deinterlace()
|
|
1501
|
-
* .build();
|
|
1502
|
-
* ```
|
|
621
|
+
* @param channelLayout - Target channel layout (optional)
|
|
622
|
+
*
|
|
623
|
+
* @returns Filter string or null if not supported
|
|
1503
624
|
*
|
|
1504
625
|
* @example
|
|
1505
626
|
* ```typescript
|
|
1506
|
-
*
|
|
1507
|
-
*
|
|
1508
|
-
*
|
|
627
|
+
* // Change sample format only
|
|
628
|
+
* presets.aformat('s16');
|
|
629
|
+
*
|
|
630
|
+
* // Change format and sample rate
|
|
631
|
+
* presets.aformat('fltp', 48000);
|
|
632
|
+
*
|
|
633
|
+
* // Full conversion
|
|
634
|
+
* presets.aformat('s16', 44100, 'stereo');
|
|
1509
635
|
* ```
|
|
636
|
+
*
|
|
637
|
+
* @see {@link https://ffmpeg.org/ffmpeg-filters.html#aformat | FFmpeg aformat filter}
|
|
1510
638
|
*/
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
639
|
+
aformat(sampleFormat, sampleRate, channelLayout) {
|
|
640
|
+
let sampleFormats = '';
|
|
641
|
+
if (!Array.isArray(sampleFormat)) {
|
|
642
|
+
sampleFormat = [sampleFormat];
|
|
1514
643
|
}
|
|
1515
|
-
|
|
644
|
+
sampleFormats = sampleFormat.map((fmt) => (typeof fmt === 'string' ? fmt : (avGetSampleFmtName(fmt) ?? 's16'))).join('|');
|
|
645
|
+
let filter = `aformat=sample_fmts=${sampleFormats}`;
|
|
646
|
+
if (sampleRate)
|
|
647
|
+
filter += `:sample_rates=${sampleRate}`;
|
|
648
|
+
if (channelLayout)
|
|
649
|
+
filter += `:channel_layouts=${channelLayout}`;
|
|
650
|
+
this.add(filter);
|
|
651
|
+
return this;
|
|
1516
652
|
}
|
|
1517
653
|
/**
|
|
1518
|
-
* Adds
|
|
1519
|
-
*
|
|
654
|
+
* Adds an asetnsamples filter to set the number of samples per frame.
|
|
655
|
+
* This is crucial for encoders like Opus that require specific frame sizes.
|
|
1520
656
|
*
|
|
1521
|
-
* @param
|
|
657
|
+
* @param samples - Number of samples per frame
|
|
658
|
+
*
|
|
659
|
+
* @param padding - Whether to pad or drop samples (default: true)
|
|
1522
660
|
*
|
|
1523
661
|
* @returns This instance for chaining
|
|
1524
662
|
*
|
|
1525
663
|
* @example
|
|
1526
664
|
* ```typescript
|
|
1527
|
-
*
|
|
1528
|
-
*
|
|
1529
|
-
* .build();
|
|
1530
|
-
* ```
|
|
665
|
+
* // For Opus encoder (requires 960 samples)
|
|
666
|
+
* chain.asetnsamples(960);
|
|
1531
667
|
*
|
|
1532
|
-
*
|
|
1533
|
-
*
|
|
1534
|
-
* const chain = FilterPresets.chain()
|
|
1535
|
-
* .flip('v')
|
|
1536
|
-
* .build();
|
|
668
|
+
* // Drop samples instead of padding
|
|
669
|
+
* chain.asetnsamples(1024, false);
|
|
1537
670
|
* ```
|
|
671
|
+
*
|
|
672
|
+
* @see {@link https://ffmpeg.org/ffmpeg-filters.html#asetnsamples | FFmpeg asetnsamples filter}
|
|
1538
673
|
*/
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
// Fallback to hflip/vflip
|
|
1544
|
-
return direction === 'h' ? this.hflip() : this.vflip();
|
|
674
|
+
asetnsamples(samples, padding = true) {
|
|
675
|
+
const p = padding ? 1 : 0;
|
|
676
|
+
this.add(`asetnsamples=n=${samples}:p=${p}`);
|
|
677
|
+
return this;
|
|
1545
678
|
}
|
|
1546
679
|
/**
|
|
1547
|
-
* Adds
|
|
1548
|
-
* Only available for hardware presets that support blur
|
|
680
|
+
* Adds an aresample filter to change audio sample rate.
|
|
1549
681
|
*
|
|
1550
|
-
* @param
|
|
1551
|
-
* @param radius - Blur radius (optional)
|
|
682
|
+
* @param rate - Target sample rate in Hz
|
|
1552
683
|
*
|
|
1553
684
|
* @returns This instance for chaining
|
|
1554
685
|
*
|
|
1555
686
|
* @example
|
|
1556
687
|
* ```typescript
|
|
1557
|
-
*
|
|
1558
|
-
*
|
|
1559
|
-
* .build();
|
|
688
|
+
* chain.aresample(44100) // Convert to 44.1 kHz
|
|
689
|
+
* chain.aresample(48000) // Convert to 48 kHz
|
|
1560
690
|
* ```
|
|
1561
691
|
*
|
|
1562
|
-
* @
|
|
1563
|
-
* ```typescript
|
|
1564
|
-
* const chain = FilterPresets.chain()
|
|
1565
|
-
* .blur('box')
|
|
1566
|
-
* .build();
|
|
1567
|
-
* ```
|
|
692
|
+
* @see {@link https://ffmpeg.org/ffmpeg-filters.html#aresample | FFmpeg aresample filter}
|
|
1568
693
|
*/
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
}
|
|
1573
|
-
return this.add(null);
|
|
694
|
+
aresample(rate) {
|
|
695
|
+
this.add(`aresample=${rate}`);
|
|
696
|
+
return this;
|
|
1574
697
|
}
|
|
1575
698
|
/**
|
|
1576
|
-
* Adds
|
|
1577
|
-
*
|
|
699
|
+
* Adds an atempo filter to change audio playback speed.
|
|
700
|
+
* Factor must be between 0.5 and 2.0. For larger changes, chain multiple atempo filters.
|
|
1578
701
|
*
|
|
1579
|
-
* @param
|
|
702
|
+
* @param factor - Tempo factor (0.5 = half speed, 2.0 = double speed)
|
|
1580
703
|
*
|
|
1581
704
|
* @returns This instance for chaining
|
|
1582
705
|
*
|
|
1583
706
|
* @example
|
|
1584
707
|
* ```typescript
|
|
1585
|
-
*
|
|
1586
|
-
*
|
|
1587
|
-
* .build();
|
|
708
|
+
* chain.atempo(1.5) // 1.5x speed
|
|
709
|
+
* chain.atempo(0.8) // Slow down to 80% speed
|
|
1588
710
|
* ```
|
|
1589
711
|
*
|
|
1590
|
-
* @
|
|
1591
|
-
* ```typescript
|
|
1592
|
-
* const chain = FilterPresets.chain()
|
|
1593
|
-
* .sharpen()
|
|
1594
|
-
* .build();
|
|
1595
|
-
* ```
|
|
712
|
+
* @see {@link https://ffmpeg.org/ffmpeg-filters.html#atempo | FFmpeg atempo filter}
|
|
1596
713
|
*/
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
}
|
|
1601
|
-
return this.add(null);
|
|
714
|
+
atempo(factor) {
|
|
715
|
+
this.add(`atempo=${factor}`);
|
|
716
|
+
return this;
|
|
1602
717
|
}
|
|
1603
718
|
/**
|
|
1604
|
-
* Adds
|
|
1605
|
-
* Only available for hardware presets that support stacking
|
|
719
|
+
* Adds an audio fade filter.
|
|
1606
720
|
*
|
|
1607
|
-
* @param type -
|
|
1608
|
-
*
|
|
721
|
+
* @param type - Fade type ('in' or 'out')
|
|
722
|
+
*
|
|
723
|
+
* @param start - Start time in seconds
|
|
724
|
+
*
|
|
725
|
+
* @param duration - Fade duration in seconds
|
|
1609
726
|
*
|
|
1610
727
|
* @returns This instance for chaining
|
|
1611
728
|
*
|
|
1612
729
|
* @example
|
|
1613
730
|
* ```typescript
|
|
1614
|
-
*
|
|
1615
|
-
*
|
|
1616
|
-
* .build();
|
|
731
|
+
* chain.afade('in', 0, 3) // 3-second audio fade in
|
|
732
|
+
* chain.afade('out', 20, 2) // 2-second fade out at 20s
|
|
1617
733
|
* ```
|
|
1618
734
|
*
|
|
1619
|
-
* @
|
|
1620
|
-
* ```typescript
|
|
1621
|
-
* const chain = FilterPresets.chain()
|
|
1622
|
-
* .stack('x', 4)
|
|
1623
|
-
* .build();
|
|
1624
|
-
* ```
|
|
735
|
+
* @see {@link https://ffmpeg.org/ffmpeg-filters.html#afade | FFmpeg afade filter}
|
|
1625
736
|
*/
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
}
|
|
1630
|
-
return this.add(null);
|
|
737
|
+
afade(type, start, duration) {
|
|
738
|
+
this.add(`afade=t=${type}:st=${start}:d=${duration}`);
|
|
739
|
+
return this;
|
|
1631
740
|
}
|
|
1632
741
|
/**
|
|
1633
|
-
* Adds
|
|
742
|
+
* Adds an amix filter to mix multiple audio streams.
|
|
1634
743
|
*
|
|
1635
|
-
* @
|
|
744
|
+
* @param inputs - Number of input streams to mix (default: 2)
|
|
1636
745
|
*
|
|
1637
|
-
* @
|
|
1638
|
-
* ```typescript
|
|
1639
|
-
* const chain = FilterPresets.chain()
|
|
1640
|
-
* .hwupload()
|
|
1641
|
-
* .scale(1920, 1080)
|
|
1642
|
-
* .build();
|
|
1643
|
-
* ```
|
|
1644
|
-
*/
|
|
1645
|
-
hwupload() {
|
|
1646
|
-
if ('hwupload' in this.presets) {
|
|
1647
|
-
return this.add(this.presets.hwupload());
|
|
1648
|
-
}
|
|
1649
|
-
return this.add('hwupload');
|
|
1650
|
-
}
|
|
1651
|
-
/**
|
|
1652
|
-
* Adds a hwdownload filter to download frames from hardware.
|
|
746
|
+
* @param duration - How to determine output duration (default: 'longest')
|
|
1653
747
|
*
|
|
1654
748
|
* @returns This instance for chaining
|
|
1655
749
|
*
|
|
1656
750
|
* @example
|
|
1657
751
|
* ```typescript
|
|
1658
|
-
*
|
|
1659
|
-
*
|
|
1660
|
-
* .hwdownload()
|
|
1661
|
-
* .build();
|
|
752
|
+
* chain.amix(3, 'longest') // Mix 3 audio streams
|
|
753
|
+
* chain.amix(2, 'first') // Mix 2 streams, use first's duration
|
|
1662
754
|
* ```
|
|
755
|
+
*
|
|
756
|
+
* @see {@link https://ffmpeg.org/ffmpeg-filters.html#amix | FFmpeg amix filter}
|
|
1663
757
|
*/
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
}
|
|
1668
|
-
return this.add('hwdownload');
|
|
758
|
+
amix(inputs = 2, duration = 'longest') {
|
|
759
|
+
this.add(`amix=inputs=${inputs}:duration=${duration}`);
|
|
760
|
+
return this;
|
|
1669
761
|
}
|
|
1670
762
|
/**
|
|
1671
|
-
* Adds a
|
|
763
|
+
* Adds a pad filter to add padding to video.
|
|
764
|
+
* Essential for aspect ratio adjustments and letterboxing.
|
|
1672
765
|
*
|
|
1673
|
-
* @param
|
|
766
|
+
* @param width - Output width (can use expressions like 'iw+100')
|
|
767
|
+
*
|
|
768
|
+
* @param height - Output height (can use expressions like 'ih+100')
|
|
769
|
+
*
|
|
770
|
+
* @param x - X position of input video (default: '(ow-iw)/2' for center)
|
|
771
|
+
*
|
|
772
|
+
* @param y - Y position of input video (default: '(oh-ih)/2' for center)
|
|
773
|
+
*
|
|
774
|
+
* @param color - Padding color (default: 'black')
|
|
1674
775
|
*
|
|
1675
776
|
* @returns This instance for chaining
|
|
1676
777
|
*
|
|
1677
778
|
* @example
|
|
1678
779
|
* ```typescript
|
|
1679
|
-
*
|
|
1680
|
-
*
|
|
1681
|
-
* .build();
|
|
1682
|
-
* ```
|
|
780
|
+
* // Add black bars for 16:9 aspect ratio
|
|
781
|
+
* chain.pad('iw', 'iw*9/16');
|
|
1683
782
|
*
|
|
1684
|
-
*
|
|
1685
|
-
*
|
|
1686
|
-
* const chain = FilterPresets.chain()
|
|
1687
|
-
* .hwmap()
|
|
1688
|
-
* .build();
|
|
783
|
+
* // Add 50px padding on all sides
|
|
784
|
+
* chain.pad('iw+100', 'ih+100');
|
|
1689
785
|
* ```
|
|
786
|
+
*
|
|
787
|
+
* @see {@link https://ffmpeg.org/ffmpeg-filters.html#pad | FFmpeg pad filter}
|
|
1690
788
|
*/
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
789
|
+
pad(width, height, x, y, color = 'black') {
|
|
790
|
+
let filter = `pad=${width}:${height}`;
|
|
791
|
+
if (x !== undefined)
|
|
792
|
+
filter += `:${x}`;
|
|
793
|
+
if (y !== undefined)
|
|
794
|
+
filter += `:${y}`;
|
|
795
|
+
filter += `:${color}`;
|
|
796
|
+
this.add(filter);
|
|
797
|
+
return this;
|
|
1696
798
|
}
|
|
1697
|
-
}
|
|
1698
|
-
/**
|
|
1699
|
-
* Fluent filter chain builder with preset methods.
|
|
1700
|
-
* Provides a convenient API for building filter chains using standard presets.
|
|
1701
|
-
*
|
|
1702
|
-
* @example
|
|
1703
|
-
* ```typescript
|
|
1704
|
-
* const filter = FilterPresets.chain()
|
|
1705
|
-
* .scale(1920, 1080)
|
|
1706
|
-
* .fps(30)
|
|
1707
|
-
* .fade('in', 0, 2)
|
|
1708
|
-
* .format('yuv420p')
|
|
1709
|
-
* .build();
|
|
1710
|
-
* ```
|
|
1711
|
-
*/
|
|
1712
|
-
export class FilterChainBuilder extends ChainBuilderBase {
|
|
1713
|
-
}
|
|
1714
|
-
/**
|
|
1715
|
-
* Standard filter presets for software filtering.
|
|
1716
|
-
* Provides static methods for creating common filter strings and
|
|
1717
|
-
* a chain builder for composing complex filter graphs.
|
|
1718
|
-
*
|
|
1719
|
-
* @example
|
|
1720
|
-
* ```typescript
|
|
1721
|
-
* // Static methods for individual filters
|
|
1722
|
-
* const scaleFilter = FilterPresets.scale(1920, 1080);
|
|
1723
|
-
* const fpsFilter = FilterPresets.fps(30);
|
|
1724
|
-
*
|
|
1725
|
-
* // Chain builder for complex graphs
|
|
1726
|
-
* const chain = FilterPresets.chain()
|
|
1727
|
-
* .scale(1920, 1080)
|
|
1728
|
-
* .fps(30)
|
|
1729
|
-
* .fade('in', 0, 2)
|
|
1730
|
-
* .build();
|
|
1731
|
-
* ```
|
|
1732
|
-
*/
|
|
1733
|
-
export class FilterPresets extends FilterPresetBase {
|
|
1734
|
-
static instance = new FilterPresets();
|
|
1735
799
|
/**
|
|
1736
|
-
*
|
|
800
|
+
* Adds a trim filter to cut a portion of the stream.
|
|
801
|
+
* Crucial for cutting segments from media.
|
|
802
|
+
*
|
|
803
|
+
* @param start - Start time in seconds
|
|
804
|
+
*
|
|
805
|
+
* @param end - End time in seconds (optional)
|
|
806
|
+
*
|
|
807
|
+
* @param duration - Duration in seconds (optional, alternative to end)
|
|
1737
808
|
*
|
|
1738
|
-
* @returns
|
|
809
|
+
* @returns This instance for chaining
|
|
1739
810
|
*
|
|
1740
811
|
* @example
|
|
1741
812
|
* ```typescript
|
|
1742
|
-
*
|
|
1743
|
-
*
|
|
1744
|
-
* .fps(30)
|
|
1745
|
-
* .build();
|
|
813
|
+
* chain.trim(10, 30) // Extract from 10s to 30s
|
|
814
|
+
* chain.trim(5, undefined, 10) // Extract 10s starting at 5s
|
|
1746
815
|
* ```
|
|
816
|
+
*
|
|
817
|
+
* @see {@link https://ffmpeg.org/ffmpeg-filters.html#trim | FFmpeg trim filter}
|
|
1747
818
|
*/
|
|
1748
|
-
|
|
1749
|
-
|
|
819
|
+
trim(start, end, duration) {
|
|
820
|
+
let filter = `trim=start=${start}`;
|
|
821
|
+
if (end !== undefined)
|
|
822
|
+
filter += `:end=${end}`;
|
|
823
|
+
if (duration !== undefined)
|
|
824
|
+
filter += `:duration=${duration}`;
|
|
825
|
+
this.add(filter);
|
|
826
|
+
return this;
|
|
1750
827
|
}
|
|
1751
828
|
/**
|
|
1752
|
-
* Creates a
|
|
829
|
+
* Creates a setpts filter string to change presentation timestamps.
|
|
830
|
+
* Essential for speed changes and timestamp manipulation.
|
|
1753
831
|
*
|
|
1754
|
-
* @param
|
|
1755
|
-
* @param height - Target height
|
|
1756
|
-
* @param flags - Scaling algorithm flags (optional)
|
|
832
|
+
* @param expression - PTS expression (e.g., 'PTS*2' for half speed, 'PTS/2' for double speed)
|
|
1757
833
|
*
|
|
1758
|
-
* @returns
|
|
834
|
+
* @returns Filter string or null if not supported
|
|
1759
835
|
*
|
|
1760
836
|
* @example
|
|
1761
837
|
* ```typescript
|
|
1762
|
-
*
|
|
1763
|
-
*
|
|
838
|
+
* // Double speed
|
|
839
|
+
* presets.setpts('PTS/2');
|
|
1764
840
|
*
|
|
1765
|
-
*
|
|
1766
|
-
*
|
|
1767
|
-
*
|
|
841
|
+
* // Half speed
|
|
842
|
+
* presets.setpts('PTS*2');
|
|
843
|
+
*
|
|
844
|
+
* // Reset timestamps
|
|
845
|
+
* presets.setpts('PTS-STARTPTS');
|
|
1768
846
|
* ```
|
|
847
|
+
*
|
|
848
|
+
* @see {@link https://ffmpeg.org/ffmpeg-filters.html#setpts | FFmpeg setpts filter}
|
|
1769
849
|
*/
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
return
|
|
850
|
+
setpts(expression) {
|
|
851
|
+
this.add(`setpts=${expression}`);
|
|
852
|
+
return this;
|
|
1773
853
|
}
|
|
1774
854
|
/**
|
|
1775
|
-
* Creates
|
|
855
|
+
* Creates an asetpts filter string for audio timestamp manipulation.
|
|
1776
856
|
*
|
|
1777
|
-
* @param
|
|
1778
|
-
* @param height - Crop height
|
|
1779
|
-
* @param x - X position (default: 0)
|
|
1780
|
-
* @param y - Y position (default: 0)
|
|
857
|
+
* @param expression - PTS expression
|
|
1781
858
|
*
|
|
1782
|
-
* @returns
|
|
859
|
+
* @returns Filter string or null if not supported
|
|
1783
860
|
*
|
|
1784
861
|
* @example
|
|
1785
862
|
* ```typescript
|
|
1786
|
-
*
|
|
863
|
+
* presets.asetpts('PTS-STARTPTS') // Reset timestamps to start from 0
|
|
1787
864
|
* ```
|
|
1788
865
|
*
|
|
1789
|
-
* @
|
|
1790
|
-
* ```typescript
|
|
1791
|
-
* const filter = FilterPresets.crop(1920, 1080, 100, 50);
|
|
1792
|
-
* ```
|
|
866
|
+
* @see {@link https://ffmpeg.org/ffmpeg-filters.html#asetpts | FFmpeg asetpts filter}
|
|
1793
867
|
*/
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
return
|
|
868
|
+
asetpts(expression) {
|
|
869
|
+
this.add(`asetpts=${expression}`);
|
|
870
|
+
return this;
|
|
1797
871
|
}
|
|
1798
872
|
/**
|
|
1799
|
-
* Creates
|
|
873
|
+
* Creates a transpose filter string for rotation/flipping.
|
|
874
|
+
* More efficient than rotate for 90-degree rotations.
|
|
1800
875
|
*
|
|
1801
|
-
* @param
|
|
876
|
+
* @param mode - Transpose mode (0-3, or named constants)
|
|
1802
877
|
*
|
|
1803
|
-
* @returns
|
|
878
|
+
* @returns Filter string or null if not supported
|
|
1804
879
|
*
|
|
1805
880
|
* @example
|
|
1806
881
|
* ```typescript
|
|
1807
|
-
*
|
|
882
|
+
* presets.transpose(1) // Rotate 90 degrees clockwise
|
|
883
|
+
* presets.transpose('cclock') // Rotate 90 degrees counter-clockwise
|
|
1808
884
|
* ```
|
|
1809
885
|
*
|
|
1810
|
-
* @
|
|
1811
|
-
* ```typescript
|
|
1812
|
-
* const filter = FilterPresets.fps(23.976);
|
|
1813
|
-
* ```
|
|
886
|
+
* @see {@link https://ffmpeg.org/ffmpeg-filters.html#transpose | FFmpeg transpose filter}
|
|
1814
887
|
*/
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
888
|
+
transpose(mode) {
|
|
889
|
+
if (!this.support.transpose) {
|
|
890
|
+
return this;
|
|
891
|
+
}
|
|
892
|
+
if (this.hardware) {
|
|
893
|
+
// Convert string modes to numbers
|
|
894
|
+
let dir;
|
|
895
|
+
if (typeof mode === 'string') {
|
|
896
|
+
switch (mode) {
|
|
897
|
+
case 'clock':
|
|
898
|
+
dir = 1;
|
|
899
|
+
break;
|
|
900
|
+
case 'cclock':
|
|
901
|
+
dir = 2;
|
|
902
|
+
break;
|
|
903
|
+
case 'clock_flip':
|
|
904
|
+
dir = 3;
|
|
905
|
+
break;
|
|
906
|
+
case 'cclock_flip':
|
|
907
|
+
dir = 0;
|
|
908
|
+
break;
|
|
909
|
+
default:
|
|
910
|
+
dir = 0;
|
|
911
|
+
}
|
|
912
|
+
}
|
|
913
|
+
else {
|
|
914
|
+
dir = mode;
|
|
915
|
+
}
|
|
916
|
+
// Special handling for different hardware transpose implementations
|
|
917
|
+
let filterName;
|
|
918
|
+
if (this.hardware.deviceType === AV_HWDEVICE_TYPE_CUDA) {
|
|
919
|
+
filterName = 'transpose_cuda'; // Uses transpose_cuda from patch, not NPP
|
|
920
|
+
}
|
|
921
|
+
else if (this.hardware.deviceType === AV_HWDEVICE_TYPE_VIDEOTOOLBOX) {
|
|
922
|
+
filterName = 'transpose_vt'; // CoreImage-based transpose
|
|
923
|
+
}
|
|
924
|
+
else {
|
|
925
|
+
filterName = `transpose_${this.hardware.deviceTypeName}`;
|
|
926
|
+
}
|
|
927
|
+
this.add(`${filterName}=dir=${dir}`);
|
|
928
|
+
}
|
|
929
|
+
else {
|
|
930
|
+
this.add(`transpose=${mode}`);
|
|
931
|
+
}
|
|
932
|
+
return this;
|
|
1818
933
|
}
|
|
1819
934
|
/**
|
|
1820
|
-
* Creates a
|
|
935
|
+
* Creates a setsar filter string to set sample aspect ratio.
|
|
936
|
+
* Important for correcting aspect ratio issues.
|
|
1821
937
|
*
|
|
1822
|
-
* @param
|
|
938
|
+
* @param ratio - Aspect ratio (e.g., '1:1', '16:9', or number)
|
|
1823
939
|
*
|
|
1824
|
-
* @returns
|
|
940
|
+
* @returns Filter string or null if not supported
|
|
1825
941
|
*
|
|
1826
942
|
* @example
|
|
1827
943
|
* ```typescript
|
|
1828
|
-
*
|
|
944
|
+
* presets.setsar('1:1') // Square pixels
|
|
945
|
+
* presets.setsar(1.333) // 4:3 aspect ratio
|
|
1829
946
|
* ```
|
|
1830
947
|
*
|
|
1831
|
-
* @
|
|
1832
|
-
* ```typescript
|
|
1833
|
-
* const filter = FilterPresets.format(AV_PIX_FMT_RGB24);
|
|
1834
|
-
* ```
|
|
948
|
+
* @see {@link https://ffmpeg.org/ffmpeg-filters.html#setsar | FFmpeg setsar/setdar filter}
|
|
1835
949
|
*/
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
return
|
|
950
|
+
setsar(ratio) {
|
|
951
|
+
this.add(`setsar=${ratio}`);
|
|
952
|
+
return this;
|
|
1839
953
|
}
|
|
1840
954
|
/**
|
|
1841
|
-
* Creates a
|
|
955
|
+
* Creates a setdar filter string to set display aspect ratio.
|
|
1842
956
|
*
|
|
1843
|
-
* @param
|
|
957
|
+
* @param ratio - Aspect ratio (e.g., '16:9', '4:3')
|
|
1844
958
|
*
|
|
1845
|
-
* @returns
|
|
959
|
+
* @returns Filter string or null if not supported
|
|
1846
960
|
*
|
|
1847
961
|
* @example
|
|
1848
962
|
* ```typescript
|
|
1849
|
-
*
|
|
963
|
+
* presets.setdar('16:9') // Widescreen
|
|
964
|
+
* presets.setdar('4:3') // Traditional TV aspect
|
|
1850
965
|
* ```
|
|
1851
966
|
*
|
|
1852
|
-
* @
|
|
1853
|
-
* ```typescript
|
|
1854
|
-
* const filter = FilterPresets.rotate(-90);
|
|
1855
|
-
* ```
|
|
967
|
+
* @see {@link https://ffmpeg.org/ffmpeg-filters.html#setsar | FFmpeg setsar/setdar filter}
|
|
1856
968
|
*/
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
return
|
|
969
|
+
setdar(ratio) {
|
|
970
|
+
this.add(`setdar=${ratio}`);
|
|
971
|
+
return this;
|
|
1860
972
|
}
|
|
1861
973
|
/**
|
|
1862
|
-
*
|
|
974
|
+
* Adds an apad filter to add audio padding.
|
|
975
|
+
* Useful for ensuring minimum audio duration.
|
|
976
|
+
*
|
|
977
|
+
* @param wholeDuration - Minimum duration in seconds (optional)
|
|
978
|
+
*
|
|
979
|
+
* @param padDuration - Amount of padding to add in seconds (optional)
|
|
1863
980
|
*
|
|
1864
|
-
* @returns
|
|
981
|
+
* @returns This instance for chaining
|
|
1865
982
|
*
|
|
1866
983
|
* @example
|
|
1867
984
|
* ```typescript
|
|
1868
|
-
*
|
|
985
|
+
* chain.apad(30) // Ensure at least 30 seconds total
|
|
986
|
+
* chain.apad(undefined, 5) // Add 5 seconds of padding
|
|
1869
987
|
* ```
|
|
988
|
+
*
|
|
989
|
+
* @see {@link https://ffmpeg.org/ffmpeg-filters.html#apad | FFmpeg apad filter}
|
|
1870
990
|
*/
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
991
|
+
apad(wholeDuration, padDuration) {
|
|
992
|
+
if (!wholeDuration && !padDuration)
|
|
993
|
+
return this;
|
|
994
|
+
let filter = 'apad';
|
|
995
|
+
if (wholeDuration)
|
|
996
|
+
filter += `=whole_dur=${wholeDuration}`;
|
|
997
|
+
if (padDuration)
|
|
998
|
+
filter += wholeDuration ? `:pad_dur=${padDuration}` : `=pad_dur=${padDuration}`;
|
|
999
|
+
this.add(filter);
|
|
1000
|
+
return this;
|
|
1874
1001
|
}
|
|
1875
1002
|
/**
|
|
1876
|
-
* Creates a
|
|
1003
|
+
* Creates a deinterlace filter string.
|
|
1004
|
+
* Essential for processing interlaced content.
|
|
1005
|
+
*
|
|
1006
|
+
* @param mode - Deinterlace mode (default: 'yadif')
|
|
1877
1007
|
*
|
|
1878
|
-
* @
|
|
1008
|
+
* @param options - Additional options for the filter
|
|
1009
|
+
*
|
|
1010
|
+
* @returns Filter string or null if not supported
|
|
1879
1011
|
*
|
|
1880
1012
|
* @example
|
|
1881
1013
|
* ```typescript
|
|
1882
|
-
*
|
|
1014
|
+
* presets.deinterlace('yadif') // Standard deinterlacing
|
|
1015
|
+
* presets.deinterlace('bwdif') // Bob Weaver deinterlacing
|
|
1883
1016
|
* ```
|
|
1017
|
+
*
|
|
1018
|
+
* @see {@link https://ffmpeg.org/ffmpeg-filters.html#yadif | FFmpeg yadif filter}
|
|
1884
1019
|
*/
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
1020
|
+
deinterlace(mode = 'yadif', options) {
|
|
1021
|
+
if (!this.support.deinterlace) {
|
|
1022
|
+
return this;
|
|
1023
|
+
}
|
|
1024
|
+
if (this.hardware) {
|
|
1025
|
+
let filter = null;
|
|
1026
|
+
switch (this.hardware.deviceType) {
|
|
1027
|
+
case AV_HWDEVICE_TYPE_CUDA:
|
|
1028
|
+
filter = mode ? `yadif_cuda=mode=${mode}` : 'yadif_cuda';
|
|
1029
|
+
break;
|
|
1030
|
+
case AV_HWDEVICE_TYPE_VAAPI:
|
|
1031
|
+
filter = mode ? `deinterlace_vaapi=mode=${mode}` : 'deinterlace_vaapi';
|
|
1032
|
+
break;
|
|
1033
|
+
case AV_HWDEVICE_TYPE_QSV:
|
|
1034
|
+
filter = mode ? `deinterlace_qsv=mode=${mode}` : 'deinterlace_qsv';
|
|
1035
|
+
break;
|
|
1036
|
+
case AV_HWDEVICE_TYPE_VULKAN:
|
|
1037
|
+
filter = mode ? `bwdif_vulkan=mode=${mode}` : 'bwdif_vulkan';
|
|
1038
|
+
break;
|
|
1039
|
+
case AV_HWDEVICE_TYPE_VIDEOTOOLBOX:
|
|
1040
|
+
filter = mode ? `yadif_videotoolbox=mode=${mode}` : 'yadif_videotoolbox';
|
|
1041
|
+
break;
|
|
1042
|
+
default:
|
|
1043
|
+
filter = null;
|
|
1044
|
+
}
|
|
1045
|
+
if (filter) {
|
|
1046
|
+
if (options) {
|
|
1047
|
+
const params = [];
|
|
1048
|
+
for (const [key, value] of Object.entries(options)) {
|
|
1049
|
+
params.push(`${key}=${value}`);
|
|
1050
|
+
}
|
|
1051
|
+
filter += '=' + params.join(':');
|
|
1052
|
+
}
|
|
1053
|
+
this.add(filter);
|
|
1054
|
+
}
|
|
1055
|
+
}
|
|
1056
|
+
else {
|
|
1057
|
+
let filter = mode;
|
|
1058
|
+
if (options) {
|
|
1059
|
+
const params = [];
|
|
1060
|
+
for (const [key, value] of Object.entries(options)) {
|
|
1061
|
+
params.push(`${key}=${value}`);
|
|
1062
|
+
}
|
|
1063
|
+
filter += '=' + params.join(':');
|
|
1064
|
+
}
|
|
1065
|
+
this.add(filter);
|
|
1066
|
+
}
|
|
1067
|
+
return this;
|
|
1888
1068
|
}
|
|
1889
1069
|
/**
|
|
1890
|
-
* Creates a
|
|
1070
|
+
* Creates a select filter string to select specific frames.
|
|
1071
|
+
* Powerful for extracting keyframes, specific frame types, etc.
|
|
1891
1072
|
*
|
|
1892
|
-
* @param
|
|
1893
|
-
* @param start - Start time in seconds
|
|
1894
|
-
* @param duration - Fade duration in seconds
|
|
1073
|
+
* @param expression - Selection expression
|
|
1895
1074
|
*
|
|
1896
|
-
* @returns
|
|
1075
|
+
* @returns Filter string or null if not supported
|
|
1897
1076
|
*
|
|
1898
1077
|
* @example
|
|
1899
1078
|
* ```typescript
|
|
1900
|
-
*
|
|
1079
|
+
* presets.select('eq(pict_type,I)') // Select only keyframes
|
|
1080
|
+
* presets.select('not(mod(n,10))') // Select every 10th frame
|
|
1901
1081
|
* ```
|
|
1902
1082
|
*
|
|
1903
|
-
* @
|
|
1904
|
-
* ```typescript
|
|
1905
|
-
* const filter = FilterPresets.fade('out', 10, 1.5);
|
|
1906
|
-
* ```
|
|
1083
|
+
* @see {@link https://ffmpeg.org/ffmpeg-filters.html#select | FFmpeg select filter}
|
|
1907
1084
|
*/
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
return
|
|
1085
|
+
select(expression) {
|
|
1086
|
+
this.add(`select='${expression}'`);
|
|
1087
|
+
return this;
|
|
1911
1088
|
}
|
|
1912
1089
|
/**
|
|
1913
|
-
* Creates an
|
|
1090
|
+
* Creates an aselect filter string for audio selection.
|
|
1914
1091
|
*
|
|
1915
|
-
* @param
|
|
1916
|
-
* @param y - Y position (default: 0)
|
|
1092
|
+
* @param expression - Selection expression
|
|
1917
1093
|
*
|
|
1918
|
-
* @returns
|
|
1094
|
+
* @returns Filter string or null if not supported
|
|
1919
1095
|
*
|
|
1920
1096
|
* @example
|
|
1921
1097
|
* ```typescript
|
|
1922
|
-
*
|
|
1098
|
+
* presets.aselect('between(t,10,20)') // Select audio between 10-20 seconds
|
|
1923
1099
|
* ```
|
|
1924
1100
|
*
|
|
1925
|
-
* @
|
|
1926
|
-
* ```typescript
|
|
1927
|
-
* const filter = FilterPresets.overlay();
|
|
1928
|
-
* ```
|
|
1101
|
+
* @see {@link https://ffmpeg.org/ffmpeg-filters.html#aselect | FFmpeg aselect filter}
|
|
1929
1102
|
*/
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
return
|
|
1103
|
+
aselect(expression) {
|
|
1104
|
+
this.add(`aselect='${expression}'`);
|
|
1105
|
+
return this;
|
|
1933
1106
|
}
|
|
1934
1107
|
/**
|
|
1935
|
-
* Creates a
|
|
1108
|
+
* Creates a concat filter string to concatenate multiple inputs.
|
|
1109
|
+
* Essential for joining multiple video/audio segments.
|
|
1110
|
+
*
|
|
1111
|
+
* @param n - Number of input segments
|
|
1936
1112
|
*
|
|
1937
|
-
* @param
|
|
1113
|
+
* @param v - Number of output video streams (0 or 1)
|
|
1938
1114
|
*
|
|
1939
|
-
* @
|
|
1115
|
+
* @param a - Number of output audio streams (0 or 1)
|
|
1940
1116
|
*
|
|
1941
|
-
* @
|
|
1942
|
-
* ```typescript
|
|
1943
|
-
* const filter = FilterPresets.volume(0.5);
|
|
1944
|
-
* ```
|
|
1117
|
+
* @returns Filter string or null if not supported
|
|
1945
1118
|
*
|
|
1946
1119
|
* @example
|
|
1947
1120
|
* ```typescript
|
|
1948
|
-
*
|
|
1121
|
+
* presets.concat(3, 1, 1) // Join 3 segments with video and audio
|
|
1122
|
+
* presets.concat(2, 1, 0) // Join 2 video-only segments
|
|
1949
1123
|
* ```
|
|
1124
|
+
*
|
|
1125
|
+
* @see {@link https://ffmpeg.org/ffmpeg-filters.html#concat | FFmpeg concat filter}
|
|
1950
1126
|
*/
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
return
|
|
1127
|
+
concat(n, v = 1, a = 1) {
|
|
1128
|
+
this.add(`concat=n=${n}:v=${v}:a=${a}`);
|
|
1129
|
+
return this;
|
|
1954
1130
|
}
|
|
1955
1131
|
/**
|
|
1956
|
-
* Creates an audio
|
|
1132
|
+
* Creates an amerge filter string to merge multiple audio streams into one.
|
|
1133
|
+
* Different from amix - this creates multi-channel output.
|
|
1957
1134
|
*
|
|
1958
|
-
* @param
|
|
1959
|
-
* @param sampleRate - Target sample rate (optional)
|
|
1960
|
-
* @param channelLayout - Target channel layout (optional)
|
|
1135
|
+
* @param inputs - Number of input streams
|
|
1961
1136
|
*
|
|
1962
|
-
* @returns
|
|
1137
|
+
* @returns Filter string or null if not supported
|
|
1963
1138
|
*
|
|
1964
1139
|
* @example
|
|
1965
1140
|
* ```typescript
|
|
1966
|
-
*
|
|
1141
|
+
* presets.amerge(2) // Merge 2 mono streams to stereo
|
|
1142
|
+
* presets.amerge(6) // Merge 6 channels for 5.1 surround
|
|
1967
1143
|
* ```
|
|
1968
1144
|
*
|
|
1969
|
-
* @
|
|
1970
|
-
* ```typescript
|
|
1971
|
-
* const filter = FilterPresets.aformat('s16', 44100);
|
|
1972
|
-
* ```
|
|
1145
|
+
* @see {@link https://ffmpeg.org/ffmpeg-filters.html#amerge | FFmpeg amerge filter}
|
|
1973
1146
|
*/
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
return
|
|
1147
|
+
amerge(inputs = 2) {
|
|
1148
|
+
this.add(`amerge=inputs=${inputs}`);
|
|
1149
|
+
return this;
|
|
1977
1150
|
}
|
|
1978
1151
|
/**
|
|
1979
|
-
* Creates
|
|
1152
|
+
* Creates a channelmap filter string to remap audio channels.
|
|
1153
|
+
* Critical for audio channel manipulation.
|
|
1980
1154
|
*
|
|
1981
|
-
* @param
|
|
1982
|
-
* @param padding - Whether to pad or drop samples (default: true)
|
|
1155
|
+
* @param map - Channel mapping (e.g., '0-0|1-1' or 'FL-FR|FR-FL' to swap stereo)
|
|
1983
1156
|
*
|
|
1984
|
-
* @returns
|
|
1157
|
+
* @returns Filter string or null if not supported
|
|
1985
1158
|
*
|
|
1986
1159
|
* @example
|
|
1987
1160
|
* ```typescript
|
|
1988
|
-
*
|
|
1161
|
+
* presets.channelmap('FL-FR|FR-FL') // Swap left and right channels
|
|
1162
|
+
* presets.channelmap('0-0|0-1') // Duplicate mono to stereo
|
|
1989
1163
|
* ```
|
|
1990
1164
|
*
|
|
1165
|
+
* @see {@link https://ffmpeg.org/ffmpeg-filters.html#channelmap | FFmpeg channelmap filter}
|
|
1166
|
+
*/
|
|
1167
|
+
channelmap(map) {
|
|
1168
|
+
this.add(`channelmap=${map}`);
|
|
1169
|
+
return this;
|
|
1170
|
+
}
|
|
1171
|
+
/**
|
|
1172
|
+
* Creates a channelsplit filter string to split audio channels.
|
|
1173
|
+
*
|
|
1174
|
+
* @param channelLayout - Channel layout to split (optional)
|
|
1175
|
+
*
|
|
1176
|
+
* @returns Filter string or null if not supported
|
|
1177
|
+
*
|
|
1991
1178
|
* @example
|
|
1992
1179
|
* ```typescript
|
|
1993
|
-
*
|
|
1180
|
+
* presets.channelsplit('stereo') // Split stereo to 2 mono
|
|
1181
|
+
* presets.channelsplit('5.1') // Split 5.1 to individual channels
|
|
1994
1182
|
* ```
|
|
1183
|
+
*
|
|
1184
|
+
* @see {@link https://ffmpeg.org/ffmpeg-filters.html#channelsplit | FFmpeg channelsplit filter}
|
|
1995
1185
|
*/
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
return
|
|
1186
|
+
channelsplit(channelLayout) {
|
|
1187
|
+
this.add(channelLayout ? `channelsplit=channel_layout=${channelLayout}` : 'channelsplit');
|
|
1188
|
+
return this;
|
|
1999
1189
|
}
|
|
2000
1190
|
/**
|
|
2001
|
-
* Creates
|
|
1191
|
+
* Creates a loudnorm filter string for loudness normalization.
|
|
1192
|
+
* Essential for broadcast compliance and consistent audio levels.
|
|
1193
|
+
*
|
|
1194
|
+
* @param I - Integrated loudness target (default: -24 LUFS)
|
|
2002
1195
|
*
|
|
2003
|
-
* @param
|
|
1196
|
+
* @param TP - True peak (default: -2 dBTP)
|
|
1197
|
+
*
|
|
1198
|
+
* @param LRA - Loudness range (default: 7 LU)
|
|
2004
1199
|
*
|
|
2005
|
-
* @returns
|
|
1200
|
+
* @returns Filter string or null if not supported
|
|
2006
1201
|
*
|
|
2007
1202
|
* @example
|
|
2008
1203
|
* ```typescript
|
|
2009
|
-
*
|
|
1204
|
+
* presets.loudnorm(-23, -1, 7) // EBU R128 broadcast standard
|
|
1205
|
+
* presets.loudnorm(-16, -1.5, 11) // Streaming platforms standard
|
|
2010
1206
|
* ```
|
|
2011
1207
|
*
|
|
1208
|
+
* @see {@link https://ffmpeg.org/ffmpeg-filters.html#loudnorm | FFmpeg loudnorm filter}
|
|
1209
|
+
*/
|
|
1210
|
+
loudnorm(I = -24, TP = -2, LRA = 7) {
|
|
1211
|
+
this.add(`loudnorm=I=${I}:TP=${TP}:LRA=${LRA}`);
|
|
1212
|
+
return this;
|
|
1213
|
+
}
|
|
1214
|
+
/**
|
|
1215
|
+
* Creates a compand filter string for audio compression/expansion.
|
|
1216
|
+
* Important for dynamic range control.
|
|
1217
|
+
*
|
|
1218
|
+
* @param attacks - Attack times
|
|
1219
|
+
*
|
|
1220
|
+
* @param decays - Decay times
|
|
1221
|
+
*
|
|
1222
|
+
* @param points - Transfer function points
|
|
1223
|
+
*
|
|
1224
|
+
* @param gain - Output gain
|
|
1225
|
+
*
|
|
1226
|
+
* @returns Filter string or null if not supported
|
|
1227
|
+
*
|
|
2012
1228
|
* @example
|
|
2013
1229
|
* ```typescript
|
|
2014
|
-
*
|
|
1230
|
+
* presets.compand('0.3|0.3', '1|1', '-90/-60|-60/-40|-40/-30|-20/-20', 6)
|
|
2015
1231
|
* ```
|
|
1232
|
+
*
|
|
1233
|
+
* @see {@link https://ffmpeg.org/ffmpeg-filters.html#compand | FFmpeg compand filter}
|
|
2016
1234
|
*/
|
|
2017
|
-
|
|
2018
|
-
|
|
2019
|
-
|
|
1235
|
+
compand(attacks, decays, points, gain) {
|
|
1236
|
+
let filter = `compand=attacks=${attacks}:decays=${decays}:points=${points}`;
|
|
1237
|
+
if (gain !== undefined)
|
|
1238
|
+
filter += `:gain=${gain}`;
|
|
1239
|
+
this.add(filter);
|
|
1240
|
+
return this;
|
|
2020
1241
|
}
|
|
2021
1242
|
/**
|
|
2022
|
-
*
|
|
1243
|
+
* Adds a drawtext filter to overlay text on video.
|
|
2023
1244
|
*
|
|
2024
|
-
* @param
|
|
2025
|
-
* @param start - Start time in seconds
|
|
2026
|
-
* @param duration - Fade duration in seconds
|
|
1245
|
+
* @param text - Text to display
|
|
2027
1246
|
*
|
|
2028
|
-
* @
|
|
1247
|
+
* @param options - Text rendering options
|
|
2029
1248
|
*
|
|
2030
|
-
* @
|
|
2031
|
-
* ```typescript
|
|
2032
|
-
* const filter = FilterPresets.afade('in', 0, 3);
|
|
2033
|
-
* ```
|
|
1249
|
+
* @returns This instance for chaining
|
|
2034
1250
|
*
|
|
2035
1251
|
* @example
|
|
2036
1252
|
* ```typescript
|
|
2037
|
-
*
|
|
1253
|
+
* chain.drawtext('Hello World', { x: 10, y: 10, fontsize: 24 })
|
|
1254
|
+
* chain.drawtext('Timestamp', {
|
|
1255
|
+
* x: 10,
|
|
1256
|
+
* y: 10,
|
|
1257
|
+
* fontsize: 24,
|
|
1258
|
+
* fontcolor: 'white',
|
|
1259
|
+
* fontfile: '/path/to/font.ttf'
|
|
1260
|
+
* })
|
|
2038
1261
|
* ```
|
|
1262
|
+
*
|
|
1263
|
+
* @see {@link https://ffmpeg.org/ffmpeg-filters.html#drawtext | FFmpeg drawtext filter}
|
|
2039
1264
|
*/
|
|
2040
|
-
|
|
2041
|
-
|
|
2042
|
-
|
|
1265
|
+
drawtext(text, options) {
|
|
1266
|
+
let filter = `drawtext=text='${text.replace(/'/g, "\\'").replace(/"/g, '\\"')}'`;
|
|
1267
|
+
for (const [key, value] of Object.entries(options)) {
|
|
1268
|
+
if (key === 'fontfile' && typeof value === 'string') {
|
|
1269
|
+
filter += `:${key}='${value}'`;
|
|
1270
|
+
}
|
|
1271
|
+
else {
|
|
1272
|
+
filter += `:${key}=${value}`;
|
|
1273
|
+
}
|
|
1274
|
+
}
|
|
1275
|
+
this.add(filter);
|
|
1276
|
+
return this;
|
|
2043
1277
|
}
|
|
2044
1278
|
/**
|
|
2045
|
-
*
|
|
1279
|
+
* Adds a split filter to duplicate a video stream.
|
|
2046
1280
|
*
|
|
2047
|
-
* @param
|
|
2048
|
-
* @param duration - Duration mode (default: 'longest')
|
|
1281
|
+
* @param outputs - Number of output streams (default: 2)
|
|
2049
1282
|
*
|
|
2050
|
-
* @returns
|
|
1283
|
+
* @returns This instance for chaining
|
|
2051
1284
|
*
|
|
2052
1285
|
* @example
|
|
2053
1286
|
* ```typescript
|
|
2054
|
-
*
|
|
1287
|
+
* chain.split() // Split into 2 outputs
|
|
1288
|
+
* chain.split(3) // Split into 3 outputs
|
|
2055
1289
|
* ```
|
|
2056
1290
|
*
|
|
2057
|
-
* @
|
|
2058
|
-
* ```typescript
|
|
2059
|
-
* const filter = FilterPresets.amix(2, 'first');
|
|
2060
|
-
* ```
|
|
2061
|
-
*/
|
|
2062
|
-
static amix(inputs = 2, duration = 'longest') {
|
|
2063
|
-
const result = FilterPresets.instance.amix(inputs, duration);
|
|
2064
|
-
return result ?? '';
|
|
2065
|
-
}
|
|
2066
|
-
}
|
|
2067
|
-
/**
|
|
2068
|
-
* Hardware-accelerated filter presets.
|
|
2069
|
-
* Provides optimized filter implementations for specific hardware types,
|
|
2070
|
-
* with automatic fallback when operations aren't supported.
|
|
2071
|
-
*
|
|
2072
|
-
* @example
|
|
2073
|
-
* ```typescript
|
|
2074
|
-
* // Create hardware presets for CUDA
|
|
2075
|
-
* const hw = new HardwareFilterPresets(AV_HWDEVICE_TYPE_CUDA);
|
|
2076
|
-
*
|
|
2077
|
-
* // Check capabilities
|
|
2078
|
-
* if (hw.support.scale) {
|
|
2079
|
-
* const scaleFilter = hw.scale(1920, 1080);
|
|
2080
|
-
* }
|
|
2081
|
-
*
|
|
2082
|
-
* // Use chain builder
|
|
2083
|
-
* const chain = hw.chain()
|
|
2084
|
-
* .hwupload()
|
|
2085
|
-
* .scale(1920, 1080)
|
|
2086
|
-
* .tonemap()
|
|
2087
|
-
* .hwdownload()
|
|
2088
|
-
* .build();
|
|
2089
|
-
* ```
|
|
2090
|
-
*/
|
|
2091
|
-
export class HardwareFilterPresets extends FilterPresetBase {
|
|
2092
|
-
deviceType;
|
|
2093
|
-
deviceTypeName;
|
|
2094
|
-
support;
|
|
2095
|
-
/**
|
|
2096
|
-
* @param deviceType - Hardware device type enum
|
|
2097
|
-
* @param deviceTypeName - Optional hardware device type name (e.g., 'cuda', 'vaapi')
|
|
1291
|
+
* @see {@link https://ffmpeg.org/ffmpeg-filters.html#split | FFmpeg split filter}
|
|
2098
1292
|
*/
|
|
2099
|
-
|
|
2100
|
-
|
|
2101
|
-
this
|
|
2102
|
-
this.deviceTypeName = deviceTypeName ?? HardwareDeviceContext.getTypeName(deviceType);
|
|
2103
|
-
this.support = this.getSupport();
|
|
1293
|
+
split(outputs = 2) {
|
|
1294
|
+
this.add(`split=${outputs}`);
|
|
1295
|
+
return this;
|
|
2104
1296
|
}
|
|
2105
1297
|
/**
|
|
2106
|
-
*
|
|
1298
|
+
* Adds an asplit filter to duplicate an audio stream.
|
|
2107
1299
|
*
|
|
2108
|
-
* @param
|
|
2109
|
-
*
|
|
1300
|
+
* @param outputs - Number of output streams (default: 2)
|
|
1301
|
+
*
|
|
1302
|
+
* @returns This instance for chaining
|
|
2110
1303
|
*
|
|
2111
1304
|
* @example
|
|
2112
1305
|
* ```typescript
|
|
2113
|
-
*
|
|
2114
|
-
*
|
|
2115
|
-
* }
|
|
1306
|
+
* chain.asplit() // Split into 2 outputs
|
|
1307
|
+
* chain.asplit(3) // Split into 3 outputs
|
|
2116
1308
|
* ```
|
|
1309
|
+
*
|
|
1310
|
+
* @see {@link https://ffmpeg.org/ffmpeg-filters.html#asplit | FFmpeg asplit filter}
|
|
2117
1311
|
*/
|
|
2118
|
-
|
|
2119
|
-
|
|
2120
|
-
|
|
2121
|
-
return false;
|
|
2122
|
-
}
|
|
2123
|
-
// Check if filter has hardware device flag
|
|
2124
|
-
return (filter.flags & AVFILTER_FLAG_HWDEVICE) !== 0;
|
|
1312
|
+
asplit(outputs = 2) {
|
|
1313
|
+
this.add(`asplit=${outputs}`);
|
|
1314
|
+
return this;
|
|
2125
1315
|
}
|
|
2126
1316
|
/**
|
|
2127
|
-
*
|
|
1317
|
+
* Adds an adelay filter to delay audio by specified milliseconds.
|
|
2128
1318
|
*
|
|
2129
|
-
* @
|
|
1319
|
+
* @param delays - Delay in milliseconds (single value or array for multiple channels)
|
|
1320
|
+
*
|
|
1321
|
+
* @returns This instance for chaining
|
|
2130
1322
|
*
|
|
2131
1323
|
* @example
|
|
2132
1324
|
* ```typescript
|
|
2133
|
-
*
|
|
2134
|
-
*
|
|
2135
|
-
* .scale(1920, 1080)
|
|
2136
|
-
* .hwdownload()
|
|
2137
|
-
* .build();
|
|
1325
|
+
* chain.adelay(100) // Delay all channels by 100ms
|
|
1326
|
+
* chain.adelay([100, 200]) // Delay first channel by 100ms, second by 200ms
|
|
2138
1327
|
* ```
|
|
1328
|
+
*
|
|
1329
|
+
* @see {@link https://ffmpeg.org/ffmpeg-filters.html#adelay | FFmpeg adelay filter}
|
|
2139
1330
|
*/
|
|
2140
|
-
|
|
2141
|
-
|
|
1331
|
+
adelay(delays) {
|
|
1332
|
+
const delayStr = Array.isArray(delays) ? delays.join('|') : delays.toString();
|
|
1333
|
+
this.add(`adelay=${delayStr}`);
|
|
1334
|
+
return this;
|
|
2142
1335
|
}
|
|
2143
1336
|
/**
|
|
2144
|
-
*
|
|
1337
|
+
* Adds an aecho filter for audio echo effect.
|
|
2145
1338
|
*
|
|
2146
|
-
*
|
|
2147
|
-
* - CUDA: scale_cuda or scale_npp (with npp option)
|
|
2148
|
-
* - VAAPI: scale_vaapi
|
|
2149
|
-
* - QSV: scale_qsv
|
|
2150
|
-
* - VideoToolbox: scale_vt
|
|
2151
|
-
* - RKMPP: scale_rkrga
|
|
1339
|
+
* @param in_gain - Input gain (0-1)
|
|
2152
1340
|
*
|
|
2153
|
-
* @param
|
|
2154
|
-
* @param height - Target height
|
|
2155
|
-
* @param options - Hardware-specific scaling options
|
|
1341
|
+
* @param out_gain - Output gain (0-1)
|
|
2156
1342
|
*
|
|
2157
|
-
* @
|
|
1343
|
+
* @param delays - Delay in milliseconds
|
|
2158
1344
|
*
|
|
2159
|
-
* @
|
|
2160
|
-
*
|
|
2161
|
-
*
|
|
2162
|
-
* ```
|
|
1345
|
+
* @param decays - Decay factor (0-1)
|
|
1346
|
+
*
|
|
1347
|
+
* @returns This instance for chaining
|
|
2163
1348
|
*
|
|
2164
1349
|
* @example
|
|
2165
1350
|
* ```typescript
|
|
2166
|
-
*
|
|
1351
|
+
* chain.aecho(0.8, 0.9, 1000, 0.3) // Echo with 1 second delay
|
|
2167
1352
|
* ```
|
|
2168
1353
|
*
|
|
2169
|
-
* @see {@link https://ffmpeg.org/ffmpeg-filters.html#
|
|
1354
|
+
* @see {@link https://ffmpeg.org/ffmpeg-filters.html#aecho | FFmpeg aecho filter}
|
|
2170
1355
|
*/
|
|
2171
|
-
|
|
2172
|
-
|
|
2173
|
-
|
|
2174
|
-
}
|
|
2175
|
-
// Special handling for different hardware scalers
|
|
2176
|
-
let filterName;
|
|
2177
|
-
if (this.deviceType === AV_HWDEVICE_TYPE_CUDA && options?.npp) {
|
|
2178
|
-
filterName = 'scale_npp';
|
|
2179
|
-
}
|
|
2180
|
-
else if (this.deviceType === AV_HWDEVICE_TYPE_RKMPP) {
|
|
2181
|
-
filterName = 'scale_rkrga'; // RKMPP uses RGA for scaling
|
|
2182
|
-
}
|
|
2183
|
-
else if (this.deviceType === AV_HWDEVICE_TYPE_VIDEOTOOLBOX) {
|
|
2184
|
-
filterName = 'scale_vt'; // VideoToolbox uses scale_vt
|
|
2185
|
-
}
|
|
2186
|
-
else {
|
|
2187
|
-
filterName = `scale_${this.deviceTypeName}`;
|
|
2188
|
-
}
|
|
2189
|
-
let filter = `${filterName}=${width}:${height}`;
|
|
2190
|
-
if (options) {
|
|
2191
|
-
for (const [key, value] of Object.entries(options)) {
|
|
2192
|
-
if (key !== 'npp') {
|
|
2193
|
-
// Skip our special npp flag
|
|
2194
|
-
filter += `:${key}=${value}`;
|
|
2195
|
-
}
|
|
2196
|
-
}
|
|
2197
|
-
}
|
|
2198
|
-
return filter;
|
|
1356
|
+
aecho(in_gain, out_gain, delays, decays) {
|
|
1357
|
+
this.add(`aecho=${in_gain}:${out_gain}:${delays}:${decays}`);
|
|
1358
|
+
return this;
|
|
2199
1359
|
}
|
|
2200
1360
|
/**
|
|
2201
|
-
*
|
|
1361
|
+
* Adds a highpass filter to remove low frequencies.
|
|
2202
1362
|
*
|
|
2203
|
-
* @param
|
|
2204
|
-
* @param y - Y position (default: 0)
|
|
2205
|
-
* @param options - Hardware-specific overlay options
|
|
1363
|
+
* @param frequency - Cutoff frequency in Hz
|
|
2206
1364
|
*
|
|
2207
|
-
* @
|
|
1365
|
+
* @param options - Additional filter options
|
|
2208
1366
|
*
|
|
2209
|
-
* @
|
|
2210
|
-
* ```typescript
|
|
2211
|
-
* const filter = hwPresets.overlay(100, 50);
|
|
2212
|
-
* ```
|
|
1367
|
+
* @returns This instance for chaining
|
|
2213
1368
|
*
|
|
2214
1369
|
* @example
|
|
2215
1370
|
* ```typescript
|
|
2216
|
-
*
|
|
1371
|
+
* chain.highpass(200) // Remove frequencies below 200Hz
|
|
1372
|
+
* chain.highpass(200, { width_type: 'q', width: 1 })
|
|
2217
1373
|
* ```
|
|
2218
1374
|
*
|
|
2219
|
-
* @see {@link https://ffmpeg.org/ffmpeg-filters.html#
|
|
1375
|
+
* @see {@link https://ffmpeg.org/ffmpeg-filters.html#highpass | FFmpeg highpass filter}
|
|
2220
1376
|
*/
|
|
2221
|
-
|
|
2222
|
-
|
|
2223
|
-
return null;
|
|
2224
|
-
}
|
|
2225
|
-
// Special handling for RKMPP which uses RGA
|
|
2226
|
-
const filterName = this.deviceType === AV_HWDEVICE_TYPE_RKMPP ? 'overlay_rkrga' : `overlay_${this.deviceTypeName}`;
|
|
2227
|
-
let filter = `${filterName}=${x}:${y}`;
|
|
1377
|
+
highpass(frequency, options) {
|
|
1378
|
+
let filter = `highpass=f=${frequency}`;
|
|
2228
1379
|
if (options) {
|
|
2229
1380
|
for (const [key, value] of Object.entries(options)) {
|
|
2230
1381
|
filter += `:${key}=${value}`;
|
|
2231
1382
|
}
|
|
2232
1383
|
}
|
|
2233
|
-
|
|
1384
|
+
this.add(filter);
|
|
1385
|
+
return this;
|
|
2234
1386
|
}
|
|
2235
1387
|
/**
|
|
2236
|
-
*
|
|
1388
|
+
* Adds a lowpass filter to remove high frequencies.
|
|
2237
1389
|
*
|
|
2238
|
-
*
|
|
2239
|
-
* - 0: 90 degrees counter-clockwise and vertical flip
|
|
2240
|
-
* - 1 / 'clock': 90 degrees clockwise
|
|
2241
|
-
* - 2 / 'cclock': 90 degrees counter-clockwise
|
|
2242
|
-
* - 3 / 'clock_flip': 90 degrees clockwise and vertical flip
|
|
1390
|
+
* @param frequency - Cutoff frequency in Hz
|
|
2243
1391
|
*
|
|
2244
|
-
* @param
|
|
1392
|
+
* @param options - Additional filter options
|
|
2245
1393
|
*
|
|
2246
|
-
* @returns
|
|
1394
|
+
* @returns This instance for chaining
|
|
2247
1395
|
*
|
|
2248
1396
|
* @example
|
|
2249
1397
|
* ```typescript
|
|
2250
|
-
*
|
|
1398
|
+
* chain.lowpass(5000) // Remove frequencies above 5000Hz
|
|
2251
1399
|
* ```
|
|
2252
1400
|
*
|
|
2253
|
-
* @
|
|
2254
|
-
* ```typescript
|
|
2255
|
-
* const filter = hwPresets.transpose(2);
|
|
2256
|
-
* ```
|
|
1401
|
+
* @see {@link https://ffmpeg.org/ffmpeg-filters.html#lowpass | FFmpeg lowpass filter}
|
|
2257
1402
|
*/
|
|
2258
|
-
|
|
2259
|
-
|
|
2260
|
-
|
|
2261
|
-
|
|
2262
|
-
|
|
2263
|
-
let dir;
|
|
2264
|
-
if (typeof mode === 'string') {
|
|
2265
|
-
switch (mode) {
|
|
2266
|
-
case 'clock':
|
|
2267
|
-
dir = 1;
|
|
2268
|
-
break;
|
|
2269
|
-
case 'cclock':
|
|
2270
|
-
dir = 2;
|
|
2271
|
-
break;
|
|
2272
|
-
case 'clock_flip':
|
|
2273
|
-
dir = 3;
|
|
2274
|
-
break;
|
|
2275
|
-
case 'cclock_flip':
|
|
2276
|
-
dir = 0;
|
|
2277
|
-
break;
|
|
2278
|
-
default:
|
|
2279
|
-
dir = 0;
|
|
1403
|
+
lowpass(frequency, options) {
|
|
1404
|
+
let filter = `lowpass=f=${frequency}`;
|
|
1405
|
+
if (options) {
|
|
1406
|
+
for (const [key, value] of Object.entries(options)) {
|
|
1407
|
+
filter += `:${key}=${value}`;
|
|
2280
1408
|
}
|
|
2281
1409
|
}
|
|
2282
|
-
|
|
2283
|
-
|
|
2284
|
-
}
|
|
2285
|
-
// Special handling for different hardware transpose implementations
|
|
2286
|
-
let filterName;
|
|
2287
|
-
if (this.deviceType === AV_HWDEVICE_TYPE_CUDA) {
|
|
2288
|
-
filterName = 'transpose_cuda'; // Uses transpose_cuda from patch, not NPP
|
|
2289
|
-
}
|
|
2290
|
-
else if (this.deviceType === AV_HWDEVICE_TYPE_VIDEOTOOLBOX) {
|
|
2291
|
-
filterName = 'transpose_vt'; // CoreImage-based transpose
|
|
2292
|
-
}
|
|
2293
|
-
else {
|
|
2294
|
-
filterName = `transpose_${this.deviceTypeName}`;
|
|
2295
|
-
}
|
|
2296
|
-
return `${filterName}=dir=${dir}`;
|
|
1410
|
+
this.add(filter);
|
|
1411
|
+
return this;
|
|
2297
1412
|
}
|
|
2298
1413
|
/**
|
|
2299
|
-
*
|
|
2300
|
-
* Used for HDR to SDR conversion with hardware acceleration.
|
|
1414
|
+
* Adds a bandpass filter to keep only a frequency band.
|
|
2301
1415
|
*
|
|
2302
|
-
* @param
|
|
1416
|
+
* @param frequency - Center frequency in Hz
|
|
2303
1417
|
*
|
|
2304
|
-
* @
|
|
1418
|
+
* @param options - Additional filter options
|
|
2305
1419
|
*
|
|
2306
|
-
* @
|
|
2307
|
-
* ```typescript
|
|
2308
|
-
* const filter = hwPresets.tonemap();
|
|
2309
|
-
* ```
|
|
1420
|
+
* @returns This instance for chaining
|
|
2310
1421
|
*
|
|
2311
1422
|
* @example
|
|
2312
1423
|
* ```typescript
|
|
2313
|
-
*
|
|
1424
|
+
* chain.bandpass(1000) // Keep frequencies around 1000Hz
|
|
2314
1425
|
* ```
|
|
1426
|
+
*
|
|
1427
|
+
* @see {@link https://ffmpeg.org/ffmpeg-filters.html#bandpass | FFmpeg bandpass filter}
|
|
2315
1428
|
*/
|
|
2316
|
-
|
|
2317
|
-
|
|
2318
|
-
return null;
|
|
2319
|
-
}
|
|
2320
|
-
// VideoToolbox uses different filter name
|
|
2321
|
-
const filterName = this.deviceType === AV_HWDEVICE_TYPE_VIDEOTOOLBOX ? 'tonemap_videotoolbox' : `tonemap_${this.deviceTypeName}`;
|
|
2322
|
-
let filter = filterName;
|
|
1429
|
+
bandpass(frequency, options) {
|
|
1430
|
+
let filter = `bandpass=f=${frequency}`;
|
|
2323
1431
|
if (options) {
|
|
2324
|
-
const
|
|
2325
|
-
|
|
2326
|
-
|
|
2327
|
-
filter += `=${opts}`;
|
|
1432
|
+
for (const [key, value] of Object.entries(options)) {
|
|
1433
|
+
filter += `:${key}=${value}`;
|
|
1434
|
+
}
|
|
2328
1435
|
}
|
|
2329
|
-
|
|
1436
|
+
this.add(filter);
|
|
1437
|
+
return this;
|
|
2330
1438
|
}
|
|
2331
1439
|
/**
|
|
2332
|
-
*
|
|
2333
|
-
*
|
|
2334
|
-
* Different hardware types use different deinterlacers:
|
|
2335
|
-
* - CUDA: yadif_cuda
|
|
2336
|
-
* - VAAPI: deinterlace_vaapi
|
|
2337
|
-
* - QSV: deinterlace_qsv
|
|
2338
|
-
* - Vulkan: bwdif_vulkan
|
|
2339
|
-
* - VideoToolbox: yadif_videotoolbox
|
|
1440
|
+
* Adds an equalizer filter for frequency band adjustment.
|
|
2340
1441
|
*
|
|
2341
|
-
* @param
|
|
1442
|
+
* @param frequency - Center frequency in Hz
|
|
2342
1443
|
*
|
|
2343
|
-
* @
|
|
2344
|
-
*
|
|
2345
|
-
* @example
|
|
2346
|
-
* ```typescript
|
|
2347
|
-
* const filter = hwPresets.deinterlace();
|
|
2348
|
-
* ```
|
|
1444
|
+
* @param width - Band width
|
|
2349
1445
|
*
|
|
2350
|
-
* @
|
|
2351
|
-
* ```typescript
|
|
2352
|
-
* const filter = hwPresets.deinterlace('send_field');
|
|
2353
|
-
* ```
|
|
2354
|
-
*/
|
|
2355
|
-
deinterlace(mode) {
|
|
2356
|
-
if (!this.support.deinterlace) {
|
|
2357
|
-
return null;
|
|
2358
|
-
}
|
|
2359
|
-
switch (this.deviceType) {
|
|
2360
|
-
case AV_HWDEVICE_TYPE_CUDA:
|
|
2361
|
-
return mode ? `yadif_cuda=mode=${mode}` : 'yadif_cuda';
|
|
2362
|
-
case AV_HWDEVICE_TYPE_VAAPI:
|
|
2363
|
-
return mode ? `deinterlace_vaapi=mode=${mode}` : 'deinterlace_vaapi';
|
|
2364
|
-
case AV_HWDEVICE_TYPE_QSV:
|
|
2365
|
-
return mode ? `deinterlace_qsv=mode=${mode}` : 'deinterlace_qsv';
|
|
2366
|
-
case AV_HWDEVICE_TYPE_VULKAN:
|
|
2367
|
-
return mode ? `bwdif_vulkan=mode=${mode}` : 'bwdif_vulkan';
|
|
2368
|
-
case AV_HWDEVICE_TYPE_VIDEOTOOLBOX:
|
|
2369
|
-
return mode ? `yadif_videotoolbox=mode=${mode}` : 'yadif_videotoolbox';
|
|
2370
|
-
default:
|
|
2371
|
-
return null;
|
|
2372
|
-
}
|
|
2373
|
-
}
|
|
2374
|
-
/**
|
|
2375
|
-
* Creates a hardware-accelerated flip filter.
|
|
2376
|
-
* Currently only Vulkan supports hardware flip filters.
|
|
1446
|
+
* @param gain - Gain in dB
|
|
2377
1447
|
*
|
|
2378
|
-
* @param
|
|
1448
|
+
* @param width_type - Width type (optional)
|
|
2379
1449
|
*
|
|
2380
|
-
* @returns
|
|
1450
|
+
* @returns This instance for chaining
|
|
2381
1451
|
*
|
|
2382
1452
|
* @example
|
|
2383
1453
|
* ```typescript
|
|
2384
|
-
*
|
|
1454
|
+
* chain.equalizer(1000, 2, 5) // Boost 1000Hz by 5dB
|
|
1455
|
+
* chain.equalizer(1000, 2, 5, 'q') // Use Q factor for width
|
|
2385
1456
|
* ```
|
|
2386
1457
|
*
|
|
2387
|
-
* @
|
|
2388
|
-
* ```typescript
|
|
2389
|
-
* const filter = hwPresets.flip('v');
|
|
2390
|
-
* ```
|
|
1458
|
+
* @see {@link https://ffmpeg.org/ffmpeg-filters.html#equalizer | FFmpeg equalizer filter}
|
|
2391
1459
|
*/
|
|
2392
|
-
|
|
2393
|
-
|
|
2394
|
-
|
|
2395
|
-
|
|
2396
|
-
if (this.deviceType === AV_HWDEVICE_TYPE_VULKAN) {
|
|
2397
|
-
return direction === 'h' ? 'hflip_vulkan' : 'vflip_vulkan';
|
|
1460
|
+
equalizer(frequency, width, gain, width_type) {
|
|
1461
|
+
let filter = `equalizer=f=${frequency}`;
|
|
1462
|
+
if (width_type) {
|
|
1463
|
+
filter += `:width_type=${width_type}`;
|
|
2398
1464
|
}
|
|
2399
|
-
|
|
1465
|
+
filter += `:width=${width}:gain=${gain}`;
|
|
1466
|
+
this.add(filter);
|
|
1467
|
+
return this;
|
|
2400
1468
|
}
|
|
2401
1469
|
/**
|
|
2402
|
-
*
|
|
2403
|
-
*
|
|
2404
|
-
* Different hardware types support different blur filters:
|
|
2405
|
-
* - CUDA: bilateral_cuda
|
|
2406
|
-
* - Vulkan: avgblur_vulkan, gblur_vulkan
|
|
2407
|
-
* - OpenCL: avgblur_opencl, boxblur_opencl
|
|
1470
|
+
* Adds a compressor filter for dynamic range compression.
|
|
2408
1471
|
*
|
|
2409
|
-
* @param
|
|
2410
|
-
* @param radius - Blur radius (optional)
|
|
1472
|
+
* @param options - Compressor parameters
|
|
2411
1473
|
*
|
|
2412
|
-
* @returns
|
|
1474
|
+
* @returns This instance for chaining
|
|
2413
1475
|
*
|
|
2414
1476
|
* @example
|
|
2415
1477
|
* ```typescript
|
|
2416
|
-
*
|
|
1478
|
+
* chain.compressor() // Default compression
|
|
1479
|
+
* chain.compressor({
|
|
1480
|
+
* threshold: 0.5,
|
|
1481
|
+
* ratio: 4,
|
|
1482
|
+
* attack: 5,
|
|
1483
|
+
* release: 50
|
|
1484
|
+
* })
|
|
2417
1485
|
* ```
|
|
2418
1486
|
*
|
|
2419
|
-
* @
|
|
2420
|
-
* ```typescript
|
|
2421
|
-
* const filter = hwPresets.blur('avg');
|
|
2422
|
-
* ```
|
|
1487
|
+
* @see {@link https://ffmpeg.org/ffmpeg-filters.html#acompressor | FFmpeg acompressor filter}
|
|
2423
1488
|
*/
|
|
2424
|
-
|
|
2425
|
-
if (!
|
|
2426
|
-
|
|
1489
|
+
compressor(options) {
|
|
1490
|
+
if (!options || Object.keys(options).length === 0) {
|
|
1491
|
+
this.add('acompressor');
|
|
2427
1492
|
}
|
|
2428
|
-
|
|
2429
|
-
|
|
2430
|
-
|
|
2431
|
-
|
|
2432
|
-
|
|
2433
|
-
|
|
2434
|
-
|
|
2435
|
-
|
|
2436
|
-
return null;
|
|
1493
|
+
else {
|
|
1494
|
+
let filter = 'acompressor';
|
|
1495
|
+
const params = [];
|
|
1496
|
+
for (const [key, value] of Object.entries(options)) {
|
|
1497
|
+
params.push(`${key}=${value}`);
|
|
1498
|
+
}
|
|
1499
|
+
filter += '=' + params.join(':');
|
|
1500
|
+
this.add(filter);
|
|
2437
1501
|
}
|
|
1502
|
+
return this;
|
|
2438
1503
|
}
|
|
2439
1504
|
/**
|
|
2440
|
-
*
|
|
2441
|
-
*
|
|
2442
|
-
* Hardware sharpening support:
|
|
2443
|
-
* - VAAPI: sharpness_vaapi
|
|
2444
|
-
* - OpenCL: unsharp_opencl
|
|
2445
|
-
* - CUDA: sharpen_npp (NPP-based)
|
|
2446
|
-
*
|
|
2447
|
-
* @param amount - Sharpening amount (optional)
|
|
2448
|
-
*
|
|
2449
|
-
* @returns Hardware sharpen filter string or null if not supported
|
|
1505
|
+
* Adds an atrim filter to trim audio.
|
|
2450
1506
|
*
|
|
2451
|
-
* @
|
|
2452
|
-
* ```typescript
|
|
2453
|
-
* const filter = hwPresets.sharpen(1.5);
|
|
2454
|
-
* ```
|
|
1507
|
+
* @param start - Start time in seconds
|
|
2455
1508
|
*
|
|
2456
|
-
* @
|
|
2457
|
-
* ```typescript
|
|
2458
|
-
* const filter = hwPresets.sharpen();
|
|
2459
|
-
* ```
|
|
2460
|
-
*/
|
|
2461
|
-
sharpen(amount) {
|
|
2462
|
-
if (!this.support.sharpen) {
|
|
2463
|
-
return null;
|
|
2464
|
-
}
|
|
2465
|
-
switch (this.deviceType) {
|
|
2466
|
-
case AV_HWDEVICE_TYPE_VAAPI:
|
|
2467
|
-
return amount ? `sharpness_vaapi=sharpness=${amount}` : 'sharpness_vaapi';
|
|
2468
|
-
case AV_HWDEVICE_TYPE_OPENCL:
|
|
2469
|
-
return amount ? `unsharp_opencl=amount=${amount}` : 'unsharp_opencl';
|
|
2470
|
-
case AV_HWDEVICE_TYPE_CUDA:
|
|
2471
|
-
// CUDA uses NPP for sharpening
|
|
2472
|
-
return 'sharpen_npp';
|
|
2473
|
-
default:
|
|
2474
|
-
return null;
|
|
2475
|
-
}
|
|
2476
|
-
}
|
|
2477
|
-
/**
|
|
2478
|
-
* Creates a hardware-accelerated stack filter.
|
|
2479
|
-
* Only VAAPI and QSV support hardware stacking.
|
|
1509
|
+
* @param end - End time in seconds (optional)
|
|
2480
1510
|
*
|
|
2481
|
-
* @param
|
|
2482
|
-
* @param inputs - Number of inputs to stack (default: 2)
|
|
1511
|
+
* @param duration - Duration in seconds (optional)
|
|
2483
1512
|
*
|
|
2484
|
-
* @returns
|
|
1513
|
+
* @returns This instance for chaining
|
|
2485
1514
|
*
|
|
2486
1515
|
* @example
|
|
2487
1516
|
* ```typescript
|
|
2488
|
-
*
|
|
1517
|
+
* chain.atrim(10, 20) // Extract audio from 10s to 20s
|
|
2489
1518
|
* ```
|
|
2490
1519
|
*
|
|
2491
|
-
* @
|
|
2492
|
-
* ```typescript
|
|
2493
|
-
* const filter = hwPresets.stack('x', 4);
|
|
2494
|
-
* ```
|
|
1520
|
+
* @see {@link https://ffmpeg.org/ffmpeg-filters.html#atrim | FFmpeg atrim filter}
|
|
2495
1521
|
*/
|
|
2496
|
-
|
|
2497
|
-
|
|
2498
|
-
|
|
2499
|
-
|
|
2500
|
-
if (
|
|
2501
|
-
|
|
2502
|
-
|
|
2503
|
-
return
|
|
1522
|
+
atrim(start, end, duration) {
|
|
1523
|
+
let filter = `atrim=start=${start}`;
|
|
1524
|
+
if (end !== undefined)
|
|
1525
|
+
filter += `:end=${end}`;
|
|
1526
|
+
if (duration !== undefined)
|
|
1527
|
+
filter += `:duration=${duration}`;
|
|
1528
|
+
this.add(filter);
|
|
1529
|
+
return this;
|
|
2504
1530
|
}
|
|
2505
1531
|
/**
|
|
2506
|
-
*
|
|
2507
|
-
* CUDA uses hwupload_cuda, others use generic hwupload.
|
|
1532
|
+
* Adds a hwupload filter to upload frames to hardware.
|
|
2508
1533
|
*
|
|
2509
|
-
* @returns
|
|
1534
|
+
* @returns This instance for chaining
|
|
2510
1535
|
*
|
|
2511
1536
|
* @example
|
|
2512
1537
|
* ```typescript
|
|
2513
|
-
* const
|
|
1538
|
+
* const chain = FilterPresets.chain()
|
|
1539
|
+
* .hwupload()
|
|
1540
|
+
* .scale(1920, 1080)
|
|
1541
|
+
* .build();
|
|
2514
1542
|
* ```
|
|
2515
|
-
*
|
|
2516
|
-
* @see {@link https://ffmpeg.org/ffmpeg-filters.html#hwupload | FFmpeg hwupload filter}
|
|
2517
1543
|
*/
|
|
2518
1544
|
hwupload() {
|
|
2519
|
-
if (this.deviceType === AV_HWDEVICE_TYPE_CUDA) {
|
|
2520
|
-
|
|
1545
|
+
if (this.hardware?.deviceType === AV_HWDEVICE_TYPE_CUDA) {
|
|
1546
|
+
this.add('hwupload_cuda');
|
|
2521
1547
|
}
|
|
2522
|
-
|
|
1548
|
+
else {
|
|
1549
|
+
this.add('hwupload');
|
|
1550
|
+
}
|
|
1551
|
+
return this;
|
|
2523
1552
|
}
|
|
2524
1553
|
/**
|
|
2525
|
-
*
|
|
1554
|
+
* Adds a hwdownload filter to download frames from hardware.
|
|
2526
1555
|
*
|
|
2527
|
-
* @returns
|
|
1556
|
+
* @returns This instance for chaining
|
|
2528
1557
|
*
|
|
2529
1558
|
* @example
|
|
2530
1559
|
* ```typescript
|
|
2531
|
-
* const
|
|
1560
|
+
* const chain = FilterPresets.chain()
|
|
1561
|
+
* .scale(1920, 1080)
|
|
1562
|
+
* .hwdownload()
|
|
1563
|
+
* .build();
|
|
2532
1564
|
* ```
|
|
2533
|
-
*
|
|
2534
|
-
* @see {@link https://ffmpeg.org/ffmpeg-filters.html#hwdownload | FFmpeg hwdownload filter}
|
|
2535
1565
|
*/
|
|
2536
1566
|
hwdownload() {
|
|
2537
|
-
|
|
1567
|
+
this.add('hwdownload');
|
|
1568
|
+
return this;
|
|
2538
1569
|
}
|
|
2539
1570
|
/**
|
|
2540
|
-
*
|
|
1571
|
+
* Adds a hwmap filter to map frames between hardware devices.
|
|
2541
1572
|
*
|
|
2542
1573
|
* @param derive - Device to derive from (optional)
|
|
2543
1574
|
*
|
|
2544
|
-
* @returns
|
|
1575
|
+
* @returns This instance for chaining
|
|
2545
1576
|
*
|
|
2546
1577
|
* @example
|
|
2547
1578
|
* ```typescript
|
|
2548
|
-
* const
|
|
1579
|
+
* const chain = FilterPresets.chain()
|
|
1580
|
+
* .hwmap('cuda')
|
|
1581
|
+
* .build();
|
|
2549
1582
|
* ```
|
|
2550
1583
|
*
|
|
2551
1584
|
* @example
|
|
2552
1585
|
* ```typescript
|
|
2553
|
-
* const
|
|
1586
|
+
* const chain = FilterPresets.chain()
|
|
1587
|
+
* .hwmap()
|
|
1588
|
+
* .build();
|
|
2554
1589
|
* ```
|
|
2555
|
-
*
|
|
2556
|
-
* @see {@link https://ffmpeg.org/ffmpeg-filters.html#hwmap | FFmpeg hwmap filter}
|
|
2557
1590
|
*/
|
|
2558
1591
|
hwmap(derive) {
|
|
2559
|
-
|
|
1592
|
+
this.add(derive ? `hwmap=derive_device=${derive}` : 'hwmap');
|
|
1593
|
+
return this;
|
|
2560
1594
|
}
|
|
2561
1595
|
/**
|
|
2562
|
-
*
|
|
1596
|
+
* Adds a filter to the chain.
|
|
1597
|
+
*
|
|
1598
|
+
* @param filter - Filter string to add (ignored if null/undefined)
|
|
2563
1599
|
*
|
|
2564
|
-
* @returns
|
|
1600
|
+
* @returns This instance for chaining
|
|
2565
1601
|
*
|
|
2566
1602
|
* @example
|
|
2567
1603
|
* ```typescript
|
|
2568
|
-
*
|
|
2569
|
-
* if (caps.scale && caps.overlay) {
|
|
2570
|
-
* console.log('Hardware supports scaling and overlay');
|
|
2571
|
-
* }
|
|
1604
|
+
* chain.add('scale=1920:1080')
|
|
2572
1605
|
* ```
|
|
1606
|
+
*
|
|
1607
|
+
* @internal
|
|
2573
1608
|
*/
|
|
2574
|
-
|
|
2575
|
-
|
|
1609
|
+
add(filter) {
|
|
1610
|
+
if (filter) {
|
|
1611
|
+
this.filters.push(filter);
|
|
1612
|
+
}
|
|
1613
|
+
return this;
|
|
2576
1614
|
}
|
|
2577
1615
|
/**
|
|
2578
1616
|
* Determines filter support for the hardware type.
|
|
@@ -2582,7 +1620,25 @@ export class HardwareFilterPresets extends FilterPresetBase {
|
|
|
2582
1620
|
* @internal
|
|
2583
1621
|
*/
|
|
2584
1622
|
getSupport() {
|
|
2585
|
-
|
|
1623
|
+
if (!this.hardware) {
|
|
1624
|
+
// Software-only - all filters supported
|
|
1625
|
+
return {
|
|
1626
|
+
scale: true,
|
|
1627
|
+
overlay: true,
|
|
1628
|
+
transpose: true,
|
|
1629
|
+
tonemap: true,
|
|
1630
|
+
deinterlace: true,
|
|
1631
|
+
denoise: true,
|
|
1632
|
+
flip: true,
|
|
1633
|
+
blur: true,
|
|
1634
|
+
sharpen: true,
|
|
1635
|
+
chromakey: true,
|
|
1636
|
+
colorspace: true,
|
|
1637
|
+
pad: true,
|
|
1638
|
+
stack: true,
|
|
1639
|
+
};
|
|
1640
|
+
}
|
|
1641
|
+
switch (this.hardware.deviceType) {
|
|
2586
1642
|
case AV_HWDEVICE_TYPE_CUDA:
|
|
2587
1643
|
return {
|
|
2588
1644
|
scale: true, // scale_cuda
|
|
@@ -2757,21 +1813,4 @@ export class HardwareFilterPresets extends FilterPresetBase {
|
|
|
2757
1813
|
}
|
|
2758
1814
|
}
|
|
2759
1815
|
}
|
|
2760
|
-
/**
|
|
2761
|
-
* Hardware filter chain builder with fluent API.
|
|
2762
|
-
* Automatically skips unsupported filters (returns null) allowing graceful fallback.
|
|
2763
|
-
*
|
|
2764
|
-
* @example
|
|
2765
|
-
* ```typescript
|
|
2766
|
-
* const hw = new HardwareFilterPresets(AV_HWDEVICE_TYPE_CUDA, 'cuda');
|
|
2767
|
-
* const chain = hw.chain()
|
|
2768
|
-
* .hwupload()
|
|
2769
|
-
* .scale(1920, 1080)
|
|
2770
|
-
* .tonemap() // Skipped if not supported
|
|
2771
|
-
* .hwdownload()
|
|
2772
|
-
* .build();
|
|
2773
|
-
* ```
|
|
2774
|
-
*/
|
|
2775
|
-
export class HardwareFilterChainBuilder extends ChainBuilderBase {
|
|
2776
|
-
}
|
|
2777
1816
|
//# sourceMappingURL=filter-presets.js.map
|