@revizly/node-av 5.2.2-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (254) hide show
  1. package/BUILD_LINUX.md +61 -0
  2. package/LICENSE.md +22 -0
  3. package/README.md +662 -0
  4. package/build_mac_local.sh +69 -0
  5. package/dist/api/audio-frame-buffer.d.ts +205 -0
  6. package/dist/api/audio-frame-buffer.js +287 -0
  7. package/dist/api/audio-frame-buffer.js.map +1 -0
  8. package/dist/api/bitstream-filter.d.ts +820 -0
  9. package/dist/api/bitstream-filter.js +1242 -0
  10. package/dist/api/bitstream-filter.js.map +1 -0
  11. package/dist/api/constants.d.ts +44 -0
  12. package/dist/api/constants.js +45 -0
  13. package/dist/api/constants.js.map +1 -0
  14. package/dist/api/data/test_av1.ivf +0 -0
  15. package/dist/api/data/test_h264.h264 +0 -0
  16. package/dist/api/data/test_hevc.h265 +0 -0
  17. package/dist/api/data/test_mjpeg.mjpeg +0 -0
  18. package/dist/api/data/test_vp8.ivf +0 -0
  19. package/dist/api/data/test_vp9.ivf +0 -0
  20. package/dist/api/decoder.d.ts +1088 -0
  21. package/dist/api/decoder.js +1775 -0
  22. package/dist/api/decoder.js.map +1 -0
  23. package/dist/api/demuxer.d.ts +1219 -0
  24. package/dist/api/demuxer.js +2081 -0
  25. package/dist/api/demuxer.js.map +1 -0
  26. package/dist/api/device.d.ts +586 -0
  27. package/dist/api/device.js +961 -0
  28. package/dist/api/device.js.map +1 -0
  29. package/dist/api/encoder.d.ts +1132 -0
  30. package/dist/api/encoder.js +1988 -0
  31. package/dist/api/encoder.js.map +1 -0
  32. package/dist/api/filter-complex.d.ts +821 -0
  33. package/dist/api/filter-complex.js +1604 -0
  34. package/dist/api/filter-complex.js.map +1 -0
  35. package/dist/api/filter-presets.d.ts +1286 -0
  36. package/dist/api/filter-presets.js +2152 -0
  37. package/dist/api/filter-presets.js.map +1 -0
  38. package/dist/api/filter.d.ts +1234 -0
  39. package/dist/api/filter.js +1976 -0
  40. package/dist/api/filter.js.map +1 -0
  41. package/dist/api/fmp4-stream.d.ts +426 -0
  42. package/dist/api/fmp4-stream.js +739 -0
  43. package/dist/api/fmp4-stream.js.map +1 -0
  44. package/dist/api/hardware.d.ts +651 -0
  45. package/dist/api/hardware.js +1260 -0
  46. package/dist/api/hardware.js.map +1 -0
  47. package/dist/api/index.d.ts +17 -0
  48. package/dist/api/index.js +32 -0
  49. package/dist/api/index.js.map +1 -0
  50. package/dist/api/io-stream.d.ts +307 -0
  51. package/dist/api/io-stream.js +282 -0
  52. package/dist/api/io-stream.js.map +1 -0
  53. package/dist/api/muxer.d.ts +957 -0
  54. package/dist/api/muxer.js +2002 -0
  55. package/dist/api/muxer.js.map +1 -0
  56. package/dist/api/pipeline.d.ts +607 -0
  57. package/dist/api/pipeline.js +1145 -0
  58. package/dist/api/pipeline.js.map +1 -0
  59. package/dist/api/utilities/async-queue.d.ts +120 -0
  60. package/dist/api/utilities/async-queue.js +211 -0
  61. package/dist/api/utilities/async-queue.js.map +1 -0
  62. package/dist/api/utilities/audio-sample.d.ts +117 -0
  63. package/dist/api/utilities/audio-sample.js +112 -0
  64. package/dist/api/utilities/audio-sample.js.map +1 -0
  65. package/dist/api/utilities/channel-layout.d.ts +76 -0
  66. package/dist/api/utilities/channel-layout.js +80 -0
  67. package/dist/api/utilities/channel-layout.js.map +1 -0
  68. package/dist/api/utilities/electron-shared-texture.d.ts +328 -0
  69. package/dist/api/utilities/electron-shared-texture.js +503 -0
  70. package/dist/api/utilities/electron-shared-texture.js.map +1 -0
  71. package/dist/api/utilities/image.d.ts +207 -0
  72. package/dist/api/utilities/image.js +213 -0
  73. package/dist/api/utilities/image.js.map +1 -0
  74. package/dist/api/utilities/index.d.ts +12 -0
  75. package/dist/api/utilities/index.js +25 -0
  76. package/dist/api/utilities/index.js.map +1 -0
  77. package/dist/api/utilities/media-type.d.ts +49 -0
  78. package/dist/api/utilities/media-type.js +53 -0
  79. package/dist/api/utilities/media-type.js.map +1 -0
  80. package/dist/api/utilities/pixel-format.d.ts +89 -0
  81. package/dist/api/utilities/pixel-format.js +97 -0
  82. package/dist/api/utilities/pixel-format.js.map +1 -0
  83. package/dist/api/utilities/sample-format.d.ts +129 -0
  84. package/dist/api/utilities/sample-format.js +141 -0
  85. package/dist/api/utilities/sample-format.js.map +1 -0
  86. package/dist/api/utilities/scheduler.d.ts +138 -0
  87. package/dist/api/utilities/scheduler.js +98 -0
  88. package/dist/api/utilities/scheduler.js.map +1 -0
  89. package/dist/api/utilities/streaming.d.ts +186 -0
  90. package/dist/api/utilities/streaming.js +309 -0
  91. package/dist/api/utilities/streaming.js.map +1 -0
  92. package/dist/api/utilities/timestamp.d.ts +193 -0
  93. package/dist/api/utilities/timestamp.js +206 -0
  94. package/dist/api/utilities/timestamp.js.map +1 -0
  95. package/dist/api/utilities/whisper-model.d.ts +310 -0
  96. package/dist/api/utilities/whisper-model.js +528 -0
  97. package/dist/api/utilities/whisper-model.js.map +1 -0
  98. package/dist/api/utils.d.ts +19 -0
  99. package/dist/api/utils.js +39 -0
  100. package/dist/api/utils.js.map +1 -0
  101. package/dist/api/whisper.d.ts +324 -0
  102. package/dist/api/whisper.js +362 -0
  103. package/dist/api/whisper.js.map +1 -0
  104. package/dist/constants/channel-layouts.d.ts +53 -0
  105. package/dist/constants/channel-layouts.js +57 -0
  106. package/dist/constants/channel-layouts.js.map +1 -0
  107. package/dist/constants/constants.d.ts +2325 -0
  108. package/dist/constants/constants.js +1887 -0
  109. package/dist/constants/constants.js.map +1 -0
  110. package/dist/constants/decoders.d.ts +633 -0
  111. package/dist/constants/decoders.js +641 -0
  112. package/dist/constants/decoders.js.map +1 -0
  113. package/dist/constants/encoders.d.ts +295 -0
  114. package/dist/constants/encoders.js +308 -0
  115. package/dist/constants/encoders.js.map +1 -0
  116. package/dist/constants/hardware.d.ts +26 -0
  117. package/dist/constants/hardware.js +27 -0
  118. package/dist/constants/hardware.js.map +1 -0
  119. package/dist/constants/index.d.ts +5 -0
  120. package/dist/constants/index.js +6 -0
  121. package/dist/constants/index.js.map +1 -0
  122. package/dist/ffmpeg/index.d.ts +99 -0
  123. package/dist/ffmpeg/index.js +115 -0
  124. package/dist/ffmpeg/index.js.map +1 -0
  125. package/dist/ffmpeg/utils.d.ts +31 -0
  126. package/dist/ffmpeg/utils.js +68 -0
  127. package/dist/ffmpeg/utils.js.map +1 -0
  128. package/dist/ffmpeg/version.d.ts +6 -0
  129. package/dist/ffmpeg/version.js +7 -0
  130. package/dist/ffmpeg/version.js.map +1 -0
  131. package/dist/index.d.ts +4 -0
  132. package/dist/index.js +9 -0
  133. package/dist/index.js.map +1 -0
  134. package/dist/lib/audio-fifo.d.ts +399 -0
  135. package/dist/lib/audio-fifo.js +431 -0
  136. package/dist/lib/audio-fifo.js.map +1 -0
  137. package/dist/lib/binding.d.ts +228 -0
  138. package/dist/lib/binding.js +60 -0
  139. package/dist/lib/binding.js.map +1 -0
  140. package/dist/lib/bitstream-filter-context.d.ts +379 -0
  141. package/dist/lib/bitstream-filter-context.js +441 -0
  142. package/dist/lib/bitstream-filter-context.js.map +1 -0
  143. package/dist/lib/bitstream-filter.d.ts +140 -0
  144. package/dist/lib/bitstream-filter.js +154 -0
  145. package/dist/lib/bitstream-filter.js.map +1 -0
  146. package/dist/lib/codec-context.d.ts +1071 -0
  147. package/dist/lib/codec-context.js +1354 -0
  148. package/dist/lib/codec-context.js.map +1 -0
  149. package/dist/lib/codec-parameters.d.ts +616 -0
  150. package/dist/lib/codec-parameters.js +761 -0
  151. package/dist/lib/codec-parameters.js.map +1 -0
  152. package/dist/lib/codec-parser.d.ts +201 -0
  153. package/dist/lib/codec-parser.js +213 -0
  154. package/dist/lib/codec-parser.js.map +1 -0
  155. package/dist/lib/codec.d.ts +586 -0
  156. package/dist/lib/codec.js +713 -0
  157. package/dist/lib/codec.js.map +1 -0
  158. package/dist/lib/device.d.ts +291 -0
  159. package/dist/lib/device.js +324 -0
  160. package/dist/lib/device.js.map +1 -0
  161. package/dist/lib/dictionary.d.ts +333 -0
  162. package/dist/lib/dictionary.js +372 -0
  163. package/dist/lib/dictionary.js.map +1 -0
  164. package/dist/lib/error.d.ts +242 -0
  165. package/dist/lib/error.js +303 -0
  166. package/dist/lib/error.js.map +1 -0
  167. package/dist/lib/fifo.d.ts +416 -0
  168. package/dist/lib/fifo.js +453 -0
  169. package/dist/lib/fifo.js.map +1 -0
  170. package/dist/lib/filter-context.d.ts +712 -0
  171. package/dist/lib/filter-context.js +789 -0
  172. package/dist/lib/filter-context.js.map +1 -0
  173. package/dist/lib/filter-graph-segment.d.ts +160 -0
  174. package/dist/lib/filter-graph-segment.js +171 -0
  175. package/dist/lib/filter-graph-segment.js.map +1 -0
  176. package/dist/lib/filter-graph.d.ts +641 -0
  177. package/dist/lib/filter-graph.js +704 -0
  178. package/dist/lib/filter-graph.js.map +1 -0
  179. package/dist/lib/filter-inout.d.ts +198 -0
  180. package/dist/lib/filter-inout.js +257 -0
  181. package/dist/lib/filter-inout.js.map +1 -0
  182. package/dist/lib/filter.d.ts +243 -0
  183. package/dist/lib/filter.js +272 -0
  184. package/dist/lib/filter.js.map +1 -0
  185. package/dist/lib/format-context.d.ts +1254 -0
  186. package/dist/lib/format-context.js +1379 -0
  187. package/dist/lib/format-context.js.map +1 -0
  188. package/dist/lib/frame-utils.d.ts +116 -0
  189. package/dist/lib/frame-utils.js +98 -0
  190. package/dist/lib/frame-utils.js.map +1 -0
  191. package/dist/lib/frame.d.ts +1222 -0
  192. package/dist/lib/frame.js +1435 -0
  193. package/dist/lib/frame.js.map +1 -0
  194. package/dist/lib/hardware-device-context.d.ts +362 -0
  195. package/dist/lib/hardware-device-context.js +383 -0
  196. package/dist/lib/hardware-device-context.js.map +1 -0
  197. package/dist/lib/hardware-frames-context.d.ts +419 -0
  198. package/dist/lib/hardware-frames-context.js +477 -0
  199. package/dist/lib/hardware-frames-context.js.map +1 -0
  200. package/dist/lib/index.d.ts +35 -0
  201. package/dist/lib/index.js +60 -0
  202. package/dist/lib/index.js.map +1 -0
  203. package/dist/lib/input-format.d.ts +249 -0
  204. package/dist/lib/input-format.js +306 -0
  205. package/dist/lib/input-format.js.map +1 -0
  206. package/dist/lib/io-context.d.ts +696 -0
  207. package/dist/lib/io-context.js +769 -0
  208. package/dist/lib/io-context.js.map +1 -0
  209. package/dist/lib/log.d.ts +174 -0
  210. package/dist/lib/log.js +184 -0
  211. package/dist/lib/log.js.map +1 -0
  212. package/dist/lib/native-types.d.ts +946 -0
  213. package/dist/lib/native-types.js +2 -0
  214. package/dist/lib/native-types.js.map +1 -0
  215. package/dist/lib/option.d.ts +927 -0
  216. package/dist/lib/option.js +1583 -0
  217. package/dist/lib/option.js.map +1 -0
  218. package/dist/lib/output-format.d.ts +180 -0
  219. package/dist/lib/output-format.js +213 -0
  220. package/dist/lib/output-format.js.map +1 -0
  221. package/dist/lib/packet.d.ts +501 -0
  222. package/dist/lib/packet.js +590 -0
  223. package/dist/lib/packet.js.map +1 -0
  224. package/dist/lib/rational.d.ts +251 -0
  225. package/dist/lib/rational.js +278 -0
  226. package/dist/lib/rational.js.map +1 -0
  227. package/dist/lib/software-resample-context.d.ts +552 -0
  228. package/dist/lib/software-resample-context.js +592 -0
  229. package/dist/lib/software-resample-context.js.map +1 -0
  230. package/dist/lib/software-scale-context.d.ts +344 -0
  231. package/dist/lib/software-scale-context.js +366 -0
  232. package/dist/lib/software-scale-context.js.map +1 -0
  233. package/dist/lib/stream.d.ts +379 -0
  234. package/dist/lib/stream.js +526 -0
  235. package/dist/lib/stream.js.map +1 -0
  236. package/dist/lib/sync-queue.d.ts +179 -0
  237. package/dist/lib/sync-queue.js +197 -0
  238. package/dist/lib/sync-queue.js.map +1 -0
  239. package/dist/lib/types.d.ts +34 -0
  240. package/dist/lib/types.js +2 -0
  241. package/dist/lib/types.js.map +1 -0
  242. package/dist/lib/utilities.d.ts +1127 -0
  243. package/dist/lib/utilities.js +1225 -0
  244. package/dist/lib/utilities.js.map +1 -0
  245. package/dist/utils/electron.d.ts +49 -0
  246. package/dist/utils/electron.js +63 -0
  247. package/dist/utils/electron.js.map +1 -0
  248. package/dist/utils/index.d.ts +4 -0
  249. package/dist/utils/index.js +5 -0
  250. package/dist/utils/index.js.map +1 -0
  251. package/install/check.js +121 -0
  252. package/install/ffmpeg.js +66 -0
  253. package/jellyfin-ffmpeg.patch +181 -0
  254. package/package.json +129 -0
@@ -0,0 +1,2152 @@
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
+ import { Filter } from '../lib/filter.js';
3
+ import { avGetPixFmtName, avGetSampleFmtName } from '../lib/utilities.js';
4
+ /**
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.
9
+ *
10
+ * @example
11
+ * ```typescript
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();
25
+ * ```
26
+ */
27
+ export class FilterPreset {
28
+ hardware;
29
+ filters = [];
30
+ support;
31
+ constructor(hardware) {
32
+ this.hardware = hardware;
33
+ this.support = this.getSupport();
34
+ }
35
+ /**
36
+ * Checks if a filter is hardware-accelerated.
37
+ *
38
+ * @param filterName - Name of the filter to check
39
+ *
40
+ * @returns True if the filter uses hardware acceleration
41
+ *
42
+ * @example
43
+ * ```typescript
44
+ * if (FilterPreset.isHardwareFilter('scale_cuda')) {
45
+ * console.log('Hardware accelerated scaling');
46
+ * }
47
+ * ```
48
+ */
49
+ static isHardwareFilter(filterName) {
50
+ const filter = Filter.getByName(filterName);
51
+ if (!filter) {
52
+ return false;
53
+ }
54
+ return filter.hasFlags(AVFILTER_FLAG_HWDEVICE);
55
+ }
56
+ /**
57
+ * Creates a new filter chain builder.
58
+ *
59
+ * @param hardware - Optional hardware context for hardware-accelerated filters
60
+ *
61
+ * @returns A new FilterPreset instance for chaining
62
+ *
63
+ * @example
64
+ * ```typescript
65
+ * // Software filter chain
66
+ * const filter = FilterPreset.chain()
67
+ * .scale(1280, 720)
68
+ * .fps(30)
69
+ * .build();
70
+ * ```
71
+ *
72
+ * @example
73
+ * ```typescript
74
+ * // Hardware filter chain
75
+ * const hw = HardwareContext.auto();
76
+ * const filter = FilterPreset.chain(hw)
77
+ * .scale(1280, 720)
78
+ * .deinterlace()
79
+ * .build();
80
+ * ```
81
+ */
82
+ static chain(hardware) {
83
+ const preset = new FilterPreset(hardware);
84
+ return preset;
85
+ }
86
+ /**
87
+ * Adds a custom filter string to the chain.
88
+ *
89
+ * @param filter - Custom filter string
90
+ *
91
+ * @returns This instance for chaining
92
+ *
93
+ * @example
94
+ * ```typescript
95
+ * chain.custom('myfilter=param1:param2')
96
+ * ```
97
+ */
98
+ custom(filter) {
99
+ if (!filter) {
100
+ return this;
101
+ }
102
+ return this.add(filter);
103
+ }
104
+ /**
105
+ * Builds the final filter string.
106
+ *
107
+ * @param separator - Separator between filters (default: ',')
108
+ *
109
+ * @returns Combined filter string
110
+ *
111
+ * @example
112
+ * ```typescript
113
+ * const filterString = chain.build() // "scale=1920:1080,fps=30"
114
+ * ```
115
+ */
116
+ build(separator = ',') {
117
+ return this.filters.join(separator);
118
+ }
119
+ /**
120
+ * Returns the filters as an array.
121
+ *
122
+ * @returns Array of filter strings
123
+ *
124
+ * @example
125
+ * ```typescript
126
+ * const filters = chain.toArray() // ["scale=1920:1080", "fps=30"]
127
+ * ```
128
+ */
129
+ toArray() {
130
+ return [...this.filters];
131
+ }
132
+ /**
133
+ * Adds a scale filter to the chain.
134
+ * Automatically selects hardware-specific scaler if hardware context is set.
135
+ *
136
+ * @param width - Target width in pixels
137
+ *
138
+ * @param height - Target height in pixels
139
+ *
140
+ * @param options - Additional scaling options (e.g., flags for algorithm)
141
+ *
142
+ * @returns This instance for chaining
143
+ *
144
+ * @example
145
+ * ```typescript
146
+ * chain.scale(1920, 1080) // Scale to Full HD
147
+ * chain.scale(640, 480, { flags: 'lanczos' }) // With specific algorithm
148
+ * ```
149
+ *
150
+ * @example
151
+ * ```typescript
152
+ * // Hardware crop + scale (VAAPI, CUDA, QSV, VideoToolbox)
153
+ * chain.crop(100, 100, 50, 50) // Sets AVFrame crop fields
154
+ * .scale(1920, 1080) // Hardware scaler reads crop fields
155
+ * ```
156
+ *
157
+ * @see {@link https://ffmpeg.org/ffmpeg-filters.html#scale | FFmpeg scale filter}
158
+ */
159
+ scale(width, height, options) {
160
+ if (!this.support.scale) {
161
+ return this;
162
+ }
163
+ if (this.hardware) {
164
+ // Special handling for different hardware scalers
165
+ let filterName;
166
+ let filter;
167
+ if (this.hardware.deviceType === AV_HWDEVICE_TYPE_RKMPP) {
168
+ filterName = 'scale_rkrga'; // RKMPP uses RGA for scaling
169
+ filter = `${filterName}=${width}:${height}`;
170
+ }
171
+ else if (this.hardware.deviceType === AV_HWDEVICE_TYPE_VIDEOTOOLBOX) {
172
+ filterName = 'scale_vt'; // VideoToolbox uses scale_vt
173
+ filter = `${filterName}=${width}:${height}`;
174
+ }
175
+ else if (this.hardware.deviceType === AV_HWDEVICE_TYPE_QSV) {
176
+ filterName = 'vpp_qsv'; // QSV uses vpp_qsv for scaling with w= and h= parameters
177
+ filter = `${filterName}=w=${width}:h=${height}`;
178
+ }
179
+ else {
180
+ filterName = `scale_${this.hardware.deviceTypeName}`;
181
+ filter = `${filterName}=${width}:${height}`;
182
+ }
183
+ if (options) {
184
+ for (const [key, value] of Object.entries(options)) {
185
+ filter += `:${key}=${value}`;
186
+ }
187
+ }
188
+ this.add(filter);
189
+ }
190
+ else {
191
+ const flags = options?.flags;
192
+ const base = `scale=${width}:${height}`;
193
+ const result = flags ? `${base}:flags=${flags}` : base;
194
+ this.add(result);
195
+ }
196
+ return this;
197
+ }
198
+ /**
199
+ * Adds a GPU format conversion filter without scaling.
200
+ * Only available for hardware contexts. Uses hardware-specific scale filter with format parameter.
201
+ *
202
+ * @param format - Target pixel format (e.g., 'nv12', 'p010')
203
+ *
204
+ * @returns This instance for chaining
205
+ *
206
+ * @example
207
+ * ```typescript
208
+ * // Convert to NV12 on GPU without scaling
209
+ * chain.scaleFormat('nv12')
210
+ * // Generates: scale_vt=format=nv12 (VideoToolbox)
211
+ * // or: scale_cuda=format=nv12 (CUDA)
212
+ * // or: scale_opencl=format=nv12 (OpenCL)
213
+ * ```
214
+ *
215
+ * @example
216
+ * ```typescript
217
+ * // Convert P010 → NV12 on GPU
218
+ * const hw = await HardwareContext.auto();
219
+ * const filter = FilterPreset.chain(hw)
220
+ * .scaleFormat('nv12')
221
+ * .build();
222
+ * ```
223
+ *
224
+ * @example
225
+ * ```typescript
226
+ * // Scale then convert format (two separate GPU operations)
227
+ * chain.scale(1920, 1080)
228
+ * .scaleFormat('p010')
229
+ * // Generates: scale_vt=1920:1080,scale_vt=format=p010
230
+ * ```
231
+ */
232
+ scaleFormat(format) {
233
+ if (!this.hardware) {
234
+ return this;
235
+ }
236
+ // Special handling for different hardware scalers
237
+ let filterName;
238
+ if (this.hardware.deviceType === AV_HWDEVICE_TYPE_RKMPP) {
239
+ filterName = 'scale_rkrga';
240
+ }
241
+ else if (this.hardware.deviceType === AV_HWDEVICE_TYPE_VIDEOTOOLBOX) {
242
+ filterName = 'scale_vt';
243
+ }
244
+ else if (this.hardware.deviceType === AV_HWDEVICE_TYPE_QSV) {
245
+ filterName = 'vpp_qsv';
246
+ }
247
+ else {
248
+ filterName = `scale_${this.hardware.deviceTypeName}`;
249
+ }
250
+ if (typeof format !== 'string') {
251
+ format = avGetPixFmtName(format) ?? 'nv12';
252
+ }
253
+ this.add(`${filterName}=format=${format}`);
254
+ return this;
255
+ }
256
+ /**
257
+ * Adds a crop filter to the chain.
258
+ *
259
+ * @param width - Width of the cropped area
260
+ *
261
+ * @param height - Height of the cropped area
262
+ *
263
+ * @param x - X coordinate of top-left corner (default: 0)
264
+ *
265
+ * @param y - Y coordinate of top-left corner (default: 0)
266
+ *
267
+ * @returns This instance for chaining
268
+ *
269
+ * @example
270
+ * ```typescript
271
+ * chain.crop(640, 480, 100, 100) // Crop 640x480 area starting at (100,100)
272
+ * chain.crop(1280, 720) // Crop from top-left corner
273
+ * ```
274
+ *
275
+ * @example
276
+ * ```typescript
277
+ * // Hardware crop + scale
278
+ * chain.crop(100, 100, 50, 50)
279
+ * .scale(1920, 1080)
280
+ * ```
281
+ *
282
+ * @see {@link https://ffmpeg.org/ffmpeg-filters.html#crop | FFmpeg crop filter}
283
+ */
284
+ crop(width, height, x = 0, y = 0) {
285
+ if (this.hardware?.deviceType === AV_HWDEVICE_TYPE_OPENCL) {
286
+ this.add(`scale_opencl=cw=${width}:ch=${height}:cx=${x}:cy=${y}`);
287
+ }
288
+ else {
289
+ this.add(`crop=${width}:${height}:${x}:${y}`, true);
290
+ }
291
+ return this;
292
+ }
293
+ /**
294
+ * Adds a blur filter to the chain (hardware-specific).
295
+ * Only available for hardware presets that support blur
296
+ *
297
+ * @param type - Blur type (default: 'avg')
298
+ *
299
+ * @param radius - Blur radius (optional)
300
+ *
301
+ * @returns This instance for chaining
302
+ *
303
+ * @example
304
+ * ```typescript
305
+ * const chain = FilterPresets.chain()
306
+ * .blur('gaussian', 5)
307
+ * .build();
308
+ * ```
309
+ *
310
+ * @example
311
+ * ```typescript
312
+ * const chain = FilterPresets.chain()
313
+ * .blur('box')
314
+ * .build();
315
+ * ```
316
+ */
317
+ blur(type = 'avg', radius) {
318
+ if (!this.support.blur) {
319
+ return this;
320
+ }
321
+ if (this.hardware) {
322
+ let filter = null;
323
+ switch (this.hardware.deviceType) {
324
+ case AV_HWDEVICE_TYPE_CUDA:
325
+ filter = radius ? `bilateral_cuda=sigmaS=${radius}` : 'bilateral_cuda';
326
+ break;
327
+ case AV_HWDEVICE_TYPE_VULKAN:
328
+ filter = type === 'gaussian' ? (radius ? `gblur_vulkan=sigma=${radius}` : 'gblur_vulkan') : radius ? `avgblur_vulkan=sizeX=${radius}` : 'avgblur_vulkan';
329
+ break;
330
+ case AV_HWDEVICE_TYPE_OPENCL:
331
+ filter = type === 'box' ? (radius ? `boxblur_opencl=luma_radius=${radius}` : 'boxblur_opencl') : radius ? `avgblur_opencl=sizeX=${radius}` : 'avgblur_opencl';
332
+ break;
333
+ default:
334
+ filter = null;
335
+ }
336
+ if (filter) {
337
+ this.add(filter);
338
+ }
339
+ }
340
+ else {
341
+ const filter = type === 'gaussian' ? (radius ? `gblur=sigma=${radius}` : 'gblur') : radius ? `avgblur=sizeX=${radius}` : 'avgblur';
342
+ this.add(filter);
343
+ }
344
+ return this;
345
+ }
346
+ /**
347
+ * Adds a sharpen filter to the chain (hardware-specific).
348
+ * Only available for hardware presets that support sharpening
349
+ *
350
+ * @param amount - Sharpen amount (optional)
351
+ *
352
+ * @returns This instance for chaining
353
+ *
354
+ * @example
355
+ * ```typescript
356
+ * const chain = FilterPresets.chain()
357
+ * .sharpen(1.5)
358
+ * .build();
359
+ * ```
360
+ *
361
+ * @example
362
+ * ```typescript
363
+ * const chain = FilterPresets.chain()
364
+ * .sharpen()
365
+ * .build();
366
+ * ```
367
+ */
368
+ sharpen(amount) {
369
+ if (!this.support.sharpen) {
370
+ return this;
371
+ }
372
+ if (this.hardware) {
373
+ let filter = null;
374
+ switch (this.hardware.deviceType) {
375
+ case AV_HWDEVICE_TYPE_VAAPI:
376
+ filter = amount ? `sharpness_vaapi=sharpness=${amount}` : 'sharpness_vaapi';
377
+ break;
378
+ case AV_HWDEVICE_TYPE_OPENCL:
379
+ filter = amount ? `unsharp_opencl=amount=${amount}` : 'unsharp_opencl';
380
+ break;
381
+ case AV_HWDEVICE_TYPE_QSV:
382
+ // vpp_qsv detail parameter: enhancement level [0-100]
383
+ const detail = amount ? Math.min(100, Math.max(0, Math.round(amount * 10))) : 50;
384
+ filter = `vpp_qsv=detail=${detail}`;
385
+ break;
386
+ case AV_HWDEVICE_TYPE_VULKAN:
387
+ // libplacebo contrast parameter (>1 increases sharpness, no native sharpen_vulkan available)
388
+ const contrast = amount ? Math.max(1, 1 + amount) : 1.5;
389
+ filter = `libplacebo=contrast=${contrast}`;
390
+ break;
391
+ case AV_HWDEVICE_TYPE_CUDA:
392
+ // CUDA sharpening not available (NPP not included)
393
+ filter = null;
394
+ break;
395
+ default:
396
+ filter = null;
397
+ }
398
+ if (filter) {
399
+ this.add(filter);
400
+ }
401
+ }
402
+ else {
403
+ const filter = amount ? `unsharp=amount=${amount}` : 'unsharp';
404
+ this.add(filter);
405
+ }
406
+ return this;
407
+ }
408
+ /**
409
+ * Adds a Sobel edge detection filter.
410
+ *
411
+ * Applies Sobel operator to detect edges in images.
412
+ * Supports hardware acceleration with OpenCL.
413
+ * Useful for computer vision and artistic effects.
414
+ *
415
+ * @param planes - Planes to process (1-15, default: 15 for all)
416
+ *
417
+ * @param scale - Scale factor for result (default: 1.0)
418
+ *
419
+ * @returns This instance for chaining
420
+ *
421
+ * @example
422
+ * ```typescript
423
+ * const chain = FilterPresets.chain()
424
+ * .sobel()
425
+ * .build();
426
+ * ```
427
+ *
428
+ * @example
429
+ * ```typescript
430
+ * const chain = FilterPresets.chain()
431
+ * .sobel(15, 2.0) // All planes, 2x scale
432
+ * .build();
433
+ * ```
434
+ */
435
+ sobel(planes, scale) {
436
+ if (this.hardware) {
437
+ let filter = null;
438
+ switch (this.hardware.deviceType) {
439
+ case AV_HWDEVICE_TYPE_OPENCL:
440
+ if (planes !== undefined || scale !== undefined) {
441
+ const params = [];
442
+ if (planes !== undefined)
443
+ params.push(`planes=${planes}`);
444
+ if (scale !== undefined)
445
+ params.push(`scale=${scale}`);
446
+ filter = `sobel_opencl=${params.join(':')}`;
447
+ }
448
+ else {
449
+ filter = 'sobel_opencl';
450
+ }
451
+ break;
452
+ }
453
+ if (filter) {
454
+ this.add(filter);
455
+ }
456
+ }
457
+ else {
458
+ // Software Sobel filter
459
+ if (planes !== undefined || scale !== undefined) {
460
+ const params = [];
461
+ if (planes !== undefined)
462
+ params.push(`planes=${planes}`);
463
+ if (scale !== undefined)
464
+ params.push(`scale=${scale}`);
465
+ this.add(`sobel=${params.join(':')}`);
466
+ }
467
+ else {
468
+ this.add('sobel');
469
+ }
470
+ }
471
+ return this;
472
+ }
473
+ /**
474
+ * Adds an FPS filter to change frame rate.
475
+ *
476
+ * @param fps - Target frames per second
477
+ *
478
+ * @returns This instance for chaining
479
+ *
480
+ * @example
481
+ * ```typescript
482
+ * chain.fps(30) // Convert to 30 FPS
483
+ * chain.fps(23.976) // Film frame rate
484
+ * ```
485
+ *
486
+ * @see {@link https://ffmpeg.org/ffmpeg-filters.html#fps | FFmpeg fps filter}
487
+ */
488
+ fps(fps) {
489
+ if (fps <= 0) {
490
+ return this;
491
+ }
492
+ this.add(`fps=fps=${fps}`);
493
+ return this;
494
+ }
495
+ /**
496
+ * Adds a format filter to convert pixel format.
497
+ *
498
+ * @param pixelFormat - Target pixel format(s) - AVPixelFormat enum, or array
499
+ *
500
+ * @returns This instance for chaining
501
+ *
502
+ * @example
503
+ * ```typescript
504
+ * // Single format
505
+ * chain.format(AV_PIX_FMT_YUV420P);
506
+ *
507
+ * // Multiple formats (tries formats in order)
508
+ * chain.format([AV_PIX_FMT_YUV420P, AV_PIX_FMT_RGB24]);
509
+ * ```
510
+ *
511
+ * @see {@link https://ffmpeg.org/ffmpeg-filters.html#format | FFmpeg format filter}
512
+ */
513
+ format(pixelFormat) {
514
+ if (Array.isArray(pixelFormat)) {
515
+ if (pixelFormat.length === 0) {
516
+ return this;
517
+ }
518
+ // Create a chain of format filters
519
+ const formats = pixelFormat.map((fmt) => {
520
+ const formatName = typeof fmt === 'string' ? fmt : (avGetPixFmtName(fmt) ?? 'yuv420p');
521
+ return `format=${formatName}`;
522
+ });
523
+ this.add(formats.join(','));
524
+ }
525
+ else {
526
+ const formatName = typeof pixelFormat === 'string' ? pixelFormat : (avGetPixFmtName(pixelFormat) ?? 'yuv420p');
527
+ this.add(`format=${formatName}`);
528
+ }
529
+ return this;
530
+ }
531
+ /**
532
+ * Adds a rotate filter to the chain.
533
+ *
534
+ * @param angle - Rotation angle in degrees
535
+ *
536
+ * @returns This instance for chaining
537
+ *
538
+ * @example
539
+ * ```typescript
540
+ * chain.rotate(90) // Rotate 90 degrees clockwise
541
+ * chain.rotate(-45) // Rotate 45 degrees counter-clockwise
542
+ * ```
543
+ *
544
+ * @see {@link https://ffmpeg.org/ffmpeg-filters.html#rotate | FFmpeg rotate filter}
545
+ */
546
+ rotate(angle) {
547
+ this.add(`rotate=${angle}*PI/180`);
548
+ return this;
549
+ }
550
+ /**
551
+ * Adds a flip filter to the chain (hardware-specific).
552
+ * Falls back to hflip/vflip if hardware flip not available
553
+ *
554
+ * @param direction - Flip direction ('h' or 'v')
555
+ *
556
+ * @returns This instance for chaining
557
+ *
558
+ * @example
559
+ * ```typescript
560
+ * const chain = FilterPresets.chain()
561
+ * .flip('h')
562
+ * .build();
563
+ * ```
564
+ *
565
+ * @example
566
+ * ```typescript
567
+ * const chain = FilterPresets.chain()
568
+ * .flip('v')
569
+ * .build();
570
+ * ```
571
+ */
572
+ flip(direction) {
573
+ if (!this.support.flip) {
574
+ return this;
575
+ }
576
+ if (this.hardware) {
577
+ if (this.hardware.deviceType === AV_HWDEVICE_TYPE_VULKAN) {
578
+ // Use native Vulkan flip filters
579
+ if (direction === 'v') {
580
+ this.add('vflip_vulkan');
581
+ }
582
+ else {
583
+ this.add('hflip_vulkan');
584
+ }
585
+ }
586
+ else if (this.hardware.deviceType === AV_HWDEVICE_TYPE_QSV) {
587
+ // vpp_qsv uses transpose option for flip
588
+ if (direction === 'v') {
589
+ this.add('vpp_qsv=transpose=vflip');
590
+ }
591
+ else {
592
+ this.add('vpp_qsv=transpose=hflip');
593
+ }
594
+ }
595
+ }
596
+ else {
597
+ if (direction === 'v') {
598
+ this.add('vflip');
599
+ }
600
+ else {
601
+ this.add('hflip');
602
+ }
603
+ }
604
+ return this;
605
+ }
606
+ /**
607
+ * Adds a stack filter to the chain (hardware-specific).
608
+ * Only available for hardware presets that support stacking
609
+ *
610
+ * @param type - Stack type ('h' for horizontal, 'v' for vertical, 'x' for grid)
611
+ *
612
+ * @param inputs - Number of inputs (default: 2)
613
+ *
614
+ * @returns This instance for chaining
615
+ *
616
+ * @example
617
+ * ```typescript
618
+ * const chain = FilterPresets.chain()
619
+ * .stack('h', 2)
620
+ * .build();
621
+ * ```
622
+ *
623
+ * @example
624
+ * ```typescript
625
+ * const chain = FilterPresets.chain()
626
+ * .stack('x', 4)
627
+ * .build();
628
+ * ```
629
+ */
630
+ stack(type, inputs = 2) {
631
+ if (!this.support.stack) {
632
+ return this;
633
+ }
634
+ if (this.hardware) {
635
+ if (this.hardware.deviceType === AV_HWDEVICE_TYPE_VAAPI || this.hardware.deviceType === AV_HWDEVICE_TYPE_QSV) {
636
+ const filter = `${type}stack_${this.hardware.deviceTypeName}=inputs=${inputs}`;
637
+ this.add(filter);
638
+ }
639
+ }
640
+ else {
641
+ const filter = type === 'h' ? `hstack=inputs=${inputs}` : type === 'v' ? `vstack=inputs=${inputs}` : `xstack=inputs=${inputs}`;
642
+ this.add(filter);
643
+ }
644
+ return this;
645
+ }
646
+ /**
647
+ * Creates a tonemap filter.
648
+ * Used for HDR to SDR conversion with hardware acceleration.
649
+ *
650
+ * @param alg - Tonemapping algorithm (e.g., 'hable', 'reinhard', 'mobius', etc.)
651
+ *
652
+ * @param options - Tonemapping options
653
+ *
654
+ * @returns Hardware tonemap filter string or null if not supported
655
+ *
656
+ * @example
657
+ * ```typescript
658
+ * const filter = hwPresets.tonemap();
659
+ * ```
660
+ *
661
+ * @example
662
+ * ```typescript
663
+ * const filter = hwPresets.tonemap({ tonemap: 'hable', desat: '0' });
664
+ * ```
665
+ */
666
+ tonemap(alg, options) {
667
+ if (!this.support.tonemap) {
668
+ return this;
669
+ }
670
+ if (this.hardware) {
671
+ // Vulkan uses libplacebo for comprehensive HDR tone-mapping (no native tonemap_vulkan available)
672
+ if (this.hardware.deviceType === AV_HWDEVICE_TYPE_VULKAN) {
673
+ let filter = `libplacebo=tonemapping=${alg}`;
674
+ if (options) {
675
+ const opts = Object.entries(options)
676
+ .map(([k, v]) => `${k}=${v}`)
677
+ .join(':');
678
+ filter += `:${opts}`;
679
+ }
680
+ this.add(filter);
681
+ }
682
+ else {
683
+ // VideoToolbox uses different filter name
684
+ const filterName = this.hardware.deviceType === AV_HWDEVICE_TYPE_VIDEOTOOLBOX ? 'tonemap_videotoolbox' : `tonemap_${this.hardware.deviceTypeName}`;
685
+ let filter = `${filterName}=${alg}`;
686
+ if (options) {
687
+ const opts = Object.entries(options)
688
+ .map(([k, v]) => `${k}=${v}`)
689
+ .join(':');
690
+ filter += `=${opts}`;
691
+ }
692
+ this.add(filter);
693
+ }
694
+ }
695
+ else {
696
+ let filter = `tonemap=${alg}`;
697
+ if (options) {
698
+ const opts = Object.entries(options)
699
+ .map(([k, v]) => `${k}=${v}`)
700
+ .join(':');
701
+ filter += `=${opts}`;
702
+ }
703
+ this.add(filter);
704
+ }
705
+ return this;
706
+ }
707
+ /**
708
+ * Creates a fade filter string for video.
709
+ *
710
+ * @param type - Fade type ('in' or 'out')
711
+ *
712
+ * @param start - Start time in seconds
713
+ *
714
+ * @param duration - Fade duration in seconds
715
+ *
716
+ * @returns Filter string or null if not supported
717
+ *
718
+ * @example
719
+ * ```typescript
720
+ * presets.fade('in', 0, 2) // 2-second fade in from start
721
+ * presets.fade('out', 10, 1) // 1-second fade out at 10 seconds
722
+ * ```
723
+ *
724
+ * @see {@link https://ffmpeg.org/ffmpeg-filters.html#fade | FFmpeg fade filter}
725
+ */
726
+ fade(type, start, duration) {
727
+ this.add(`fade=t=${type}:st=${start}:d=${duration}`);
728
+ return this;
729
+ }
730
+ /**
731
+ * Creates an overlay filter string to composite two video streams.
732
+ *
733
+ * @param x - X position for overlay (default: 0)
734
+ *
735
+ * @param y - Y position for overlay (default: 0)
736
+ *
737
+ * @param options - Additional overlay options
738
+ *
739
+ * @returns Filter string or null if not supported
740
+ *
741
+ * @example
742
+ * ```typescript
743
+ * // Basic overlay at position
744
+ * presets.overlay(100, 50);
745
+ *
746
+ * // With additional options
747
+ * presets.overlay(0, 0, { format: 'yuv420' });
748
+ * ```
749
+ *
750
+ * @see {@link https://ffmpeg.org/ffmpeg-filters.html#overlay | FFmpeg overlay filter}
751
+ */
752
+ overlay(x = 0, y = 0, options) {
753
+ if (!this.support.overlay) {
754
+ return this;
755
+ }
756
+ if (this.hardware) {
757
+ // Special handling for RKMPP which uses RGA
758
+ const filterName = this.hardware.deviceType === AV_HWDEVICE_TYPE_RKMPP ? 'overlay_rkrga' : `overlay_${this.hardware.deviceTypeName}`;
759
+ let filter = `${filterName}=${x}:${y}`;
760
+ if (options) {
761
+ for (const [key, value] of Object.entries(options)) {
762
+ filter += `:${key}=${value}`;
763
+ }
764
+ }
765
+ this.add(filter);
766
+ }
767
+ else {
768
+ let filter = `overlay=${x}:${y}`;
769
+ if (options) {
770
+ for (const [key, value] of Object.entries(options)) {
771
+ filter += `:${key}=${value}`;
772
+ }
773
+ }
774
+ this.add(filter);
775
+ }
776
+ return this;
777
+ }
778
+ /**
779
+ * Creates a volume filter string for audio.
780
+ *
781
+ * @param factor - Volume multiplication factor (1.0 = unchanged, 2.0 = double)
782
+ *
783
+ * @returns Filter string or null if not supported
784
+ *
785
+ * @example
786
+ * ```typescript
787
+ * presets.volume(0.5) // Reduce volume by 50%
788
+ * presets.volume(1.5) // Increase volume by 50%
789
+ * ```
790
+ *
791
+ * @see {@link https://ffmpeg.org/ffmpeg-filters.html#volume | FFmpeg volume filter}
792
+ */
793
+ volume(factor) {
794
+ this.add(`volume=${factor}`);
795
+ return this;
796
+ }
797
+ /**
798
+ * Creates an audio format filter string.
799
+ *
800
+ * @param sampleFormat - Target sample format (e.g., 's16', 'fltp')
801
+ *
802
+ * @param sampleRate - Target sample rate in Hz (optional)
803
+ *
804
+ * @param channelLayout - Target channel layout (optional)
805
+ *
806
+ * @returns Filter string or null if not supported
807
+ *
808
+ * @example
809
+ * ```typescript
810
+ * // Change sample format only
811
+ * presets.aformat('s16');
812
+ *
813
+ * // Change format and sample rate
814
+ * presets.aformat('fltp', 48000);
815
+ *
816
+ * // Full conversion
817
+ * presets.aformat('s16', 44100, 'stereo');
818
+ * ```
819
+ *
820
+ * @see {@link https://ffmpeg.org/ffmpeg-filters.html#aformat | FFmpeg aformat filter}
821
+ */
822
+ aformat(sampleFormat, sampleRate, channelLayout) {
823
+ let sampleFormats = '';
824
+ if (!Array.isArray(sampleFormat)) {
825
+ sampleFormat = [sampleFormat];
826
+ }
827
+ sampleFormats = sampleFormat.map((fmt) => (typeof fmt === 'string' ? fmt : (avGetSampleFmtName(fmt) ?? 's16'))).join('|');
828
+ let filter = `aformat=sample_fmts=${sampleFormats}`;
829
+ if (sampleRate)
830
+ filter += `:sample_rates=${sampleRate}`;
831
+ if (channelLayout)
832
+ filter += `:channel_layouts=${channelLayout}`;
833
+ this.add(filter);
834
+ return this;
835
+ }
836
+ /**
837
+ * Adds an asetnsamples filter to set the number of samples per frame.
838
+ * This is crucial for encoders like Opus that require specific frame sizes.
839
+ *
840
+ * @param samples - Number of samples per frame
841
+ *
842
+ * @param padding - Whether to pad or drop samples (default: true)
843
+ *
844
+ * @returns This instance for chaining
845
+ *
846
+ * @example
847
+ * ```typescript
848
+ * // For Opus encoder (requires 960 samples)
849
+ * chain.asetnsamples(960);
850
+ *
851
+ * // Drop samples instead of padding
852
+ * chain.asetnsamples(1024, false);
853
+ * ```
854
+ *
855
+ * @see {@link https://ffmpeg.org/ffmpeg-filters.html#asetnsamples | FFmpeg asetnsamples filter}
856
+ */
857
+ asetnsamples(samples, padding = true) {
858
+ const p = padding ? 1 : 0;
859
+ this.add(`asetnsamples=n=${samples}:p=${p}`);
860
+ return this;
861
+ }
862
+ /**
863
+ * Adds an aresample filter to change audio sample rate, format, and channel layout.
864
+ *
865
+ * Uses libswresample for high-quality audio resampling and format conversion.
866
+ * Can also perform timestamp compensation (stretch/squeeze/fill/trim).
867
+ *
868
+ * @param rate - Target sample rate in Hz
869
+ *
870
+ * @param format - Optional target sample format (e.g., 's16', 'flt', 'fltp')
871
+ *
872
+ * @param channelLayout - Optional target channel layout (e.g., 'mono', 'stereo')
873
+ *
874
+ * @returns This instance for chaining
875
+ *
876
+ * @example
877
+ * ```typescript
878
+ * chain.aresample(44100) // Convert to 44.1 kHz only
879
+ * chain.aresample(48000, 's16', 'stereo') // Full conversion
880
+ * ```
881
+ *
882
+ * @see {@link https://ffmpeg.org/ffmpeg-filters.html#aresample | FFmpeg aresample filter}
883
+ */
884
+ aresample(rate, format, channelLayout) {
885
+ const params = [`${rate}`];
886
+ if (format !== undefined) {
887
+ const formatStr = typeof format === 'number' ? `${format}` : format;
888
+ params.push(`osf=${formatStr}`);
889
+ }
890
+ if (channelLayout !== undefined) {
891
+ params.push(`ochl=${channelLayout}`);
892
+ }
893
+ this.add(`aresample=${params.join(':')}`);
894
+ return this;
895
+ }
896
+ /**
897
+ * Adds an atempo filter to change audio playback speed.
898
+ * Factor must be between 0.5 and 2.0. For larger changes, chain multiple atempo filters.
899
+ *
900
+ * @param factor - Tempo factor (0.5 = half speed, 2.0 = double speed)
901
+ *
902
+ * @returns This instance for chaining
903
+ *
904
+ * @example
905
+ * ```typescript
906
+ * chain.atempo(1.5) // 1.5x speed
907
+ * chain.atempo(0.8) // Slow down to 80% speed
908
+ * ```
909
+ *
910
+ * @see {@link https://ffmpeg.org/ffmpeg-filters.html#atempo | FFmpeg atempo filter}
911
+ */
912
+ atempo(factor) {
913
+ this.add(`atempo=${factor}`);
914
+ return this;
915
+ }
916
+ /**
917
+ * Adds an audio fade filter.
918
+ *
919
+ * @param type - Fade type ('in' or 'out')
920
+ *
921
+ * @param start - Start time in seconds
922
+ *
923
+ * @param duration - Fade duration in seconds
924
+ *
925
+ * @returns This instance for chaining
926
+ *
927
+ * @example
928
+ * ```typescript
929
+ * chain.afade('in', 0, 3) // 3-second audio fade in
930
+ * chain.afade('out', 20, 2) // 2-second fade out at 20s
931
+ * ```
932
+ *
933
+ * @see {@link https://ffmpeg.org/ffmpeg-filters.html#afade | FFmpeg afade filter}
934
+ */
935
+ afade(type, start, duration) {
936
+ this.add(`afade=t=${type}:st=${start}:d=${duration}`);
937
+ return this;
938
+ }
939
+ /**
940
+ * Adds an amix filter to mix multiple audio streams.
941
+ *
942
+ * @param inputs - Number of input streams to mix (default: 2)
943
+ *
944
+ * @param duration - How to determine output duration (default: 'longest')
945
+ *
946
+ * @returns This instance for chaining
947
+ *
948
+ * @example
949
+ * ```typescript
950
+ * chain.amix(3, 'longest') // Mix 3 audio streams
951
+ * chain.amix(2, 'first') // Mix 2 streams, use first's duration
952
+ * ```
953
+ *
954
+ * @see {@link https://ffmpeg.org/ffmpeg-filters.html#amix | FFmpeg amix filter}
955
+ */
956
+ amix(inputs = 2, duration = 'longest') {
957
+ this.add(`amix=inputs=${inputs}:duration=${duration}`);
958
+ return this;
959
+ }
960
+ /**
961
+ * Adds a pad filter to add padding to video.
962
+ * Essential for aspect ratio adjustments and letterboxing.
963
+ *
964
+ * @param width - Output width (can use expressions like 'iw+100')
965
+ *
966
+ * @param height - Output height (can use expressions like 'ih+100')
967
+ *
968
+ * @param x - X position of input video (default: '(ow-iw)/2' for center)
969
+ *
970
+ * @param y - Y position of input video (default: '(oh-ih)/2' for center)
971
+ *
972
+ * @param color - Padding color (default: 'black')
973
+ *
974
+ * @returns This instance for chaining
975
+ *
976
+ * @example
977
+ * ```typescript
978
+ * // Add black bars for 16:9 aspect ratio
979
+ * chain.pad('iw', 'iw*9/16');
980
+ *
981
+ * // Add 50px padding on all sides
982
+ * chain.pad('iw+100', 'ih+100');
983
+ * ```
984
+ *
985
+ * @see {@link https://ffmpeg.org/ffmpeg-filters.html#pad | FFmpeg pad filter}
986
+ */
987
+ pad(width, height, x, y, color = 'black') {
988
+ if (this.hardware?.deviceType === AV_HWDEVICE_TYPE_VULKAN) {
989
+ // Vulkan uses libplacebo (no native pad_vulkan available)
990
+ let filter = `libplacebo=w=${width}:h=${height}`;
991
+ if (x !== undefined)
992
+ filter += `:pos_x=${x}`;
993
+ if (y !== undefined)
994
+ filter += `:pos_y=${y}`;
995
+ filter += `:fillcolor=${color}`;
996
+ this.add(filter);
997
+ }
998
+ else {
999
+ let filter = `pad=${width}:${height}`;
1000
+ if (x !== undefined)
1001
+ filter += `:${x}`;
1002
+ if (y !== undefined)
1003
+ filter += `:${y}`;
1004
+ filter += `:${color}`;
1005
+ this.add(filter);
1006
+ }
1007
+ return this;
1008
+ }
1009
+ /**
1010
+ * Adds a trim filter to cut a portion of the stream.
1011
+ * Crucial for cutting segments from media.
1012
+ *
1013
+ * @param start - Start time in seconds
1014
+ *
1015
+ * @param end - End time in seconds (optional)
1016
+ *
1017
+ * @param duration - Duration in seconds (optional, alternative to end)
1018
+ *
1019
+ * @returns This instance for chaining
1020
+ *
1021
+ * @example
1022
+ * ```typescript
1023
+ * chain.trim(10, 30) // Extract from 10s to 30s
1024
+ * chain.trim(5, undefined, 10) // Extract 10s starting at 5s
1025
+ * ```
1026
+ *
1027
+ * @see {@link https://ffmpeg.org/ffmpeg-filters.html#trim | FFmpeg trim filter}
1028
+ */
1029
+ trim(start, end, duration) {
1030
+ let filter = `trim=start=${start}`;
1031
+ if (end !== undefined)
1032
+ filter += `:end=${end}`;
1033
+ if (duration !== undefined)
1034
+ filter += `:duration=${duration}`;
1035
+ this.add(filter);
1036
+ return this;
1037
+ }
1038
+ /**
1039
+ * Creates a setpts filter string to change presentation timestamps.
1040
+ * Essential for speed changes and timestamp manipulation.
1041
+ *
1042
+ * @param expression - PTS expression (e.g., 'PTS*2' for half speed, 'PTS/2' for double speed)
1043
+ *
1044
+ * @returns Filter string or null if not supported
1045
+ *
1046
+ * @example
1047
+ * ```typescript
1048
+ * // Double speed
1049
+ * presets.setpts('PTS/2');
1050
+ *
1051
+ * // Half speed
1052
+ * presets.setpts('PTS*2');
1053
+ *
1054
+ * // Reset timestamps
1055
+ * presets.setpts('PTS-STARTPTS');
1056
+ * ```
1057
+ *
1058
+ * @see {@link https://ffmpeg.org/ffmpeg-filters.html#setpts | FFmpeg setpts filter}
1059
+ */
1060
+ setpts(expression) {
1061
+ this.add(`setpts=${expression}`);
1062
+ return this;
1063
+ }
1064
+ /**
1065
+ * Creates an asetpts filter string for audio timestamp manipulation.
1066
+ *
1067
+ * @param expression - PTS expression
1068
+ *
1069
+ * @returns Filter string or null if not supported
1070
+ *
1071
+ * @example
1072
+ * ```typescript
1073
+ * presets.asetpts('PTS-STARTPTS') // Reset timestamps to start from 0
1074
+ * ```
1075
+ *
1076
+ * @see {@link https://ffmpeg.org/ffmpeg-filters.html#asetpts | FFmpeg asetpts filter}
1077
+ */
1078
+ asetpts(expression) {
1079
+ this.add(`asetpts=${expression}`);
1080
+ return this;
1081
+ }
1082
+ /**
1083
+ * Creates a transpose filter string for rotation/flipping.
1084
+ * More efficient than rotate for 90-degree rotations.
1085
+ *
1086
+ * @param mode - Transpose mode (0-3, or named constants)
1087
+ *
1088
+ * @returns Filter string or null if not supported
1089
+ *
1090
+ * @example
1091
+ * ```typescript
1092
+ * presets.transpose(1) // Rotate 90 degrees clockwise
1093
+ * presets.transpose('cclock') // Rotate 90 degrees counter-clockwise
1094
+ * ```
1095
+ *
1096
+ * @see {@link https://ffmpeg.org/ffmpeg-filters.html#transpose | FFmpeg transpose filter}
1097
+ */
1098
+ transpose(mode) {
1099
+ if (!this.support.transpose) {
1100
+ return this;
1101
+ }
1102
+ if (this.hardware) {
1103
+ // Convert string modes to numbers
1104
+ let dir;
1105
+ if (typeof mode === 'string') {
1106
+ switch (mode) {
1107
+ case 'clock':
1108
+ dir = 1;
1109
+ break;
1110
+ case 'cclock':
1111
+ dir = 2;
1112
+ break;
1113
+ case 'clock_flip':
1114
+ dir = 3;
1115
+ break;
1116
+ case 'cclock_flip':
1117
+ dir = 0;
1118
+ break;
1119
+ default:
1120
+ dir = 0;
1121
+ }
1122
+ }
1123
+ else {
1124
+ dir = mode;
1125
+ }
1126
+ // Special handling for different hardware transpose implementations
1127
+ let filter;
1128
+ if (this.hardware.deviceType === AV_HWDEVICE_TYPE_CUDA) {
1129
+ filter = `transpose_cuda=dir=${dir}`; // Uses custom CUDA kernel (patch 0054)
1130
+ }
1131
+ else if (this.hardware.deviceType === AV_HWDEVICE_TYPE_VIDEOTOOLBOX) {
1132
+ filter = `transpose_vt=dir=${dir}`; // CoreImage-based transpose
1133
+ }
1134
+ else if (this.hardware.deviceType === AV_HWDEVICE_TYPE_QSV) {
1135
+ // vpp_qsv transpose parameter maps: clock=1, cclock=2, cclock_hflip=0, clock_hflip=3, reversal=4, hflip=5, vflip=6
1136
+ filter = `vpp_qsv=transpose=${dir}`;
1137
+ }
1138
+ else {
1139
+ filter = `transpose_${this.hardware.deviceTypeName}=dir=${dir}`;
1140
+ }
1141
+ this.add(filter);
1142
+ }
1143
+ else {
1144
+ this.add(`transpose=${mode}`);
1145
+ }
1146
+ return this;
1147
+ }
1148
+ /**
1149
+ * Creates a setsar filter string to set sample aspect ratio.
1150
+ * Important for correcting aspect ratio issues.
1151
+ *
1152
+ * @param ratio - Aspect ratio (e.g., '1:1', '16:9', or number)
1153
+ *
1154
+ * @returns Filter string or null if not supported
1155
+ *
1156
+ * @example
1157
+ * ```typescript
1158
+ * presets.setsar('1:1') // Square pixels
1159
+ * presets.setsar(1.333) // 4:3 aspect ratio
1160
+ * ```
1161
+ *
1162
+ * @see {@link https://ffmpeg.org/ffmpeg-filters.html#setsar | FFmpeg setsar/setdar filter}
1163
+ */
1164
+ setsar(ratio) {
1165
+ this.add(`setsar=${ratio}`);
1166
+ return this;
1167
+ }
1168
+ /**
1169
+ * Creates a setdar filter string to set display aspect ratio.
1170
+ *
1171
+ * @param ratio - Aspect ratio (e.g., '16:9', '4:3')
1172
+ *
1173
+ * @returns Filter string or null if not supported
1174
+ *
1175
+ * @example
1176
+ * ```typescript
1177
+ * presets.setdar('16:9') // Widescreen
1178
+ * presets.setdar('4:3') // Traditional TV aspect
1179
+ * ```
1180
+ *
1181
+ * @see {@link https://ffmpeg.org/ffmpeg-filters.html#setsar | FFmpeg setsar/setdar filter}
1182
+ */
1183
+ setdar(ratio) {
1184
+ this.add(`setdar=${ratio}`);
1185
+ return this;
1186
+ }
1187
+ /**
1188
+ * Adds an apad filter to add audio padding.
1189
+ * Useful for ensuring minimum audio duration.
1190
+ *
1191
+ * @param wholeDuration - Minimum duration in seconds (optional)
1192
+ *
1193
+ * @param padDuration - Amount of padding to add in seconds (optional)
1194
+ *
1195
+ * @returns This instance for chaining
1196
+ *
1197
+ * @example
1198
+ * ```typescript
1199
+ * chain.apad(30) // Ensure at least 30 seconds total
1200
+ * chain.apad(undefined, 5) // Add 5 seconds of padding
1201
+ * ```
1202
+ *
1203
+ * @see {@link https://ffmpeg.org/ffmpeg-filters.html#apad | FFmpeg apad filter}
1204
+ */
1205
+ apad(wholeDuration, padDuration) {
1206
+ if (!wholeDuration && !padDuration)
1207
+ return this;
1208
+ let filter = 'apad';
1209
+ if (wholeDuration)
1210
+ filter += `=whole_dur=${wholeDuration}`;
1211
+ if (padDuration)
1212
+ filter += wholeDuration ? `:pad_dur=${padDuration}` : `=pad_dur=${padDuration}`;
1213
+ this.add(filter);
1214
+ return this;
1215
+ }
1216
+ /**
1217
+ * Creates a deinterlace filter string.
1218
+ * Essential for processing interlaced content.
1219
+ *
1220
+ * @param mode - Deinterlace mode (default: 'yadif')
1221
+ *
1222
+ * @param options - Additional options for the filter
1223
+ *
1224
+ * @returns Filter string or null if not supported
1225
+ *
1226
+ * @example
1227
+ * ```typescript
1228
+ * presets.deinterlace('yadif') // Standard deinterlacing
1229
+ * presets.deinterlace('bwdif') // Bob Weaver deinterlacing
1230
+ * ```
1231
+ *
1232
+ * @see {@link https://ffmpeg.org/ffmpeg-filters.html#yadif | FFmpeg yadif filter}
1233
+ */
1234
+ deinterlace(mode = 'yadif', options) {
1235
+ if (!this.support.deinterlace) {
1236
+ return this;
1237
+ }
1238
+ if (this.hardware) {
1239
+ let filter = null;
1240
+ switch (this.hardware.deviceType) {
1241
+ case AV_HWDEVICE_TYPE_CUDA:
1242
+ filter = mode ? `yadif_cuda=mode=${mode}` : 'yadif_cuda';
1243
+ break;
1244
+ case AV_HWDEVICE_TYPE_VAAPI:
1245
+ filter = mode ? `deinterlace_vaapi=mode=${mode}` : 'deinterlace_vaapi';
1246
+ break;
1247
+ case AV_HWDEVICE_TYPE_QSV:
1248
+ // vpp_qsv deinterlace: 0=off, 1=bob, 2=advanced
1249
+ // Map yadif/bwdif mode to vpp_qsv: default to bob(1)
1250
+ filter = mode ? 'vpp_qsv=deinterlace=1' : 'vpp_qsv=deinterlace=1';
1251
+ break;
1252
+ case AV_HWDEVICE_TYPE_VULKAN:
1253
+ filter = mode ? `bwdif_vulkan=mode=${mode}` : 'bwdif_vulkan';
1254
+ break;
1255
+ case AV_HWDEVICE_TYPE_VIDEOTOOLBOX:
1256
+ filter = mode ? `yadif_videotoolbox=mode=${mode}` : 'yadif_videotoolbox';
1257
+ break;
1258
+ default:
1259
+ filter = null;
1260
+ }
1261
+ if (filter) {
1262
+ if (options) {
1263
+ const params = [];
1264
+ for (const [key, value] of Object.entries(options)) {
1265
+ params.push(`${key}=${value}`);
1266
+ }
1267
+ filter += '=' + params.join(':');
1268
+ }
1269
+ this.add(filter);
1270
+ }
1271
+ }
1272
+ else {
1273
+ let filter = mode;
1274
+ if (options) {
1275
+ const params = [];
1276
+ for (const [key, value] of Object.entries(options)) {
1277
+ params.push(`${key}=${value}`);
1278
+ }
1279
+ filter += '=' + params.join(':');
1280
+ }
1281
+ this.add(filter);
1282
+ }
1283
+ return this;
1284
+ }
1285
+ /**
1286
+ * Creates a select filter string to select specific frames.
1287
+ * Powerful for extracting keyframes, specific frame types, etc.
1288
+ *
1289
+ * @param expression - Selection expression
1290
+ *
1291
+ * @returns Filter string or null if not supported
1292
+ *
1293
+ * @example
1294
+ * ```typescript
1295
+ * presets.select('eq(pict_type,I)') // Select only keyframes
1296
+ * presets.select('not(mod(n,10))') // Select every 10th frame
1297
+ * ```
1298
+ *
1299
+ * @see {@link https://ffmpeg.org/ffmpeg-filters.html#select | FFmpeg select filter}
1300
+ */
1301
+ select(expression) {
1302
+ this.add(`select='${expression}'`);
1303
+ return this;
1304
+ }
1305
+ /**
1306
+ * Creates an aselect filter string for audio selection.
1307
+ *
1308
+ * @param expression - Selection expression
1309
+ *
1310
+ * @returns Filter string or null if not supported
1311
+ *
1312
+ * @example
1313
+ * ```typescript
1314
+ * presets.aselect('between(t,10,20)') // Select audio between 10-20 seconds
1315
+ * ```
1316
+ *
1317
+ * @see {@link https://ffmpeg.org/ffmpeg-filters.html#aselect | FFmpeg aselect filter}
1318
+ */
1319
+ aselect(expression) {
1320
+ this.add(`aselect='${expression}'`);
1321
+ return this;
1322
+ }
1323
+ /**
1324
+ * Creates a concat filter string to concatenate multiple inputs.
1325
+ * Essential for joining multiple video/audio segments.
1326
+ *
1327
+ * @param n - Number of input segments
1328
+ *
1329
+ * @param v - Number of output video streams (0 or 1)
1330
+ *
1331
+ * @param a - Number of output audio streams (0 or 1)
1332
+ *
1333
+ * @returns Filter string or null if not supported
1334
+ *
1335
+ * @example
1336
+ * ```typescript
1337
+ * presets.concat(3, 1, 1) // Join 3 segments with video and audio
1338
+ * presets.concat(2, 1, 0) // Join 2 video-only segments
1339
+ * ```
1340
+ *
1341
+ * @see {@link https://ffmpeg.org/ffmpeg-filters.html#concat | FFmpeg concat filter}
1342
+ */
1343
+ concat(n, v = 1, a = 1) {
1344
+ this.add(`concat=n=${n}:v=${v}:a=${a}`);
1345
+ return this;
1346
+ }
1347
+ /**
1348
+ * Creates an amerge filter string to merge multiple audio streams into one.
1349
+ * Different from amix - this creates multi-channel output.
1350
+ *
1351
+ * @param inputs - Number of input streams
1352
+ *
1353
+ * @returns Filter string or null if not supported
1354
+ *
1355
+ * @example
1356
+ * ```typescript
1357
+ * presets.amerge(2) // Merge 2 mono streams to stereo
1358
+ * presets.amerge(6) // Merge 6 channels for 5.1 surround
1359
+ * ```
1360
+ *
1361
+ * @see {@link https://ffmpeg.org/ffmpeg-filters.html#amerge | FFmpeg amerge filter}
1362
+ */
1363
+ amerge(inputs = 2) {
1364
+ this.add(`amerge=inputs=${inputs}`);
1365
+ return this;
1366
+ }
1367
+ /**
1368
+ * Creates a channelmap filter string to remap audio channels.
1369
+ * Critical for audio channel manipulation.
1370
+ *
1371
+ * @param map - Channel mapping (e.g., '0-0|1-1' or 'FL-FR|FR-FL' to swap stereo)
1372
+ *
1373
+ * @returns Filter string or null if not supported
1374
+ *
1375
+ * @example
1376
+ * ```typescript
1377
+ * presets.channelmap('FL-FR|FR-FL') // Swap left and right channels
1378
+ * presets.channelmap('0-0|0-1') // Duplicate mono to stereo
1379
+ * ```
1380
+ *
1381
+ * @see {@link https://ffmpeg.org/ffmpeg-filters.html#channelmap | FFmpeg channelmap filter}
1382
+ */
1383
+ channelmap(map) {
1384
+ this.add(`channelmap=${map}`);
1385
+ return this;
1386
+ }
1387
+ /**
1388
+ * Creates a channelsplit filter string to split audio channels.
1389
+ *
1390
+ * @param channelLayout - Channel layout to split (optional)
1391
+ *
1392
+ * @returns Filter string or null if not supported
1393
+ *
1394
+ * @example
1395
+ * ```typescript
1396
+ * presets.channelsplit('stereo') // Split stereo to 2 mono
1397
+ * presets.channelsplit('5.1') // Split 5.1 to individual channels
1398
+ * ```
1399
+ *
1400
+ * @see {@link https://ffmpeg.org/ffmpeg-filters.html#channelsplit | FFmpeg channelsplit filter}
1401
+ */
1402
+ channelsplit(channelLayout) {
1403
+ this.add(channelLayout ? `channelsplit=channel_layout=${channelLayout}` : 'channelsplit');
1404
+ return this;
1405
+ }
1406
+ /**
1407
+ * Creates a loudnorm filter string for loudness normalization.
1408
+ * Essential for broadcast compliance and consistent audio levels.
1409
+ *
1410
+ * @param I - Integrated loudness target (default: -24 LUFS)
1411
+ *
1412
+ * @param TP - True peak (default: -2 dBTP)
1413
+ *
1414
+ * @param LRA - Loudness range (default: 7 LU)
1415
+ *
1416
+ * @returns Filter string or null if not supported
1417
+ *
1418
+ * @example
1419
+ * ```typescript
1420
+ * presets.loudnorm(-23, -1, 7) // EBU R128 broadcast standard
1421
+ * presets.loudnorm(-16, -1.5, 11) // Streaming platforms standard
1422
+ * ```
1423
+ *
1424
+ * @see {@link https://ffmpeg.org/ffmpeg-filters.html#loudnorm | FFmpeg loudnorm filter}
1425
+ */
1426
+ loudnorm(I = -24, TP = -2, LRA = 7) {
1427
+ this.add(`loudnorm=I=${I}:TP=${TP}:LRA=${LRA}`);
1428
+ return this;
1429
+ }
1430
+ /**
1431
+ * Creates a compand filter string for audio compression/expansion.
1432
+ * Important for dynamic range control.
1433
+ *
1434
+ * @param attacks - Attack times
1435
+ *
1436
+ * @param decays - Decay times
1437
+ *
1438
+ * @param points - Transfer function points
1439
+ *
1440
+ * @param gain - Output gain
1441
+ *
1442
+ * @returns Filter string or null if not supported
1443
+ *
1444
+ * @example
1445
+ * ```typescript
1446
+ * presets.compand('0.3|0.3', '1|1', '-90/-60|-60/-40|-40/-30|-20/-20', 6)
1447
+ * ```
1448
+ *
1449
+ * @see {@link https://ffmpeg.org/ffmpeg-filters.html#compand | FFmpeg compand filter}
1450
+ */
1451
+ compand(attacks, decays, points, gain) {
1452
+ let filter = `compand=attacks=${attacks}:decays=${decays}:points=${points}`;
1453
+ if (gain !== undefined)
1454
+ filter += `:gain=${gain}`;
1455
+ this.add(filter);
1456
+ return this;
1457
+ }
1458
+ /**
1459
+ * Adds a drawtext filter to overlay text on video.
1460
+ *
1461
+ * @param text - Text to display
1462
+ *
1463
+ * @param options - Text rendering options
1464
+ *
1465
+ * @returns This instance for chaining
1466
+ *
1467
+ * @example
1468
+ * ```typescript
1469
+ * chain.drawtext('Hello World', { x: 10, y: 10, fontsize: 24 })
1470
+ * chain.drawtext('Timestamp', {
1471
+ * x: 10,
1472
+ * y: 10,
1473
+ * fontsize: 24,
1474
+ * fontcolor: 'white',
1475
+ * fontfile: '/path/to/font.ttf'
1476
+ * })
1477
+ * ```
1478
+ *
1479
+ * @see {@link https://ffmpeg.org/ffmpeg-filters.html#drawtext | FFmpeg drawtext filter}
1480
+ */
1481
+ drawtext(text, options) {
1482
+ let filter = `drawtext=text='${text.replace(/'/g, "\\'").replace(/"/g, '\\"')}'`;
1483
+ for (const [key, value] of Object.entries(options)) {
1484
+ if (key === 'fontfile' && typeof value === 'string') {
1485
+ filter += `:${key}='${value}'`;
1486
+ }
1487
+ else {
1488
+ filter += `:${key}=${value}`;
1489
+ }
1490
+ }
1491
+ this.add(filter);
1492
+ return this;
1493
+ }
1494
+ /**
1495
+ * Adds a split filter to duplicate a video stream.
1496
+ *
1497
+ * @param outputs - Number of output streams (default: 2)
1498
+ *
1499
+ * @returns This instance for chaining
1500
+ *
1501
+ * @example
1502
+ * ```typescript
1503
+ * chain.split() // Split into 2 outputs
1504
+ * chain.split(3) // Split into 3 outputs
1505
+ * ```
1506
+ *
1507
+ * @see {@link https://ffmpeg.org/ffmpeg-filters.html#split | FFmpeg split filter}
1508
+ */
1509
+ split(outputs = 2) {
1510
+ this.add(`split=${outputs}`);
1511
+ return this;
1512
+ }
1513
+ /**
1514
+ * Adds an asplit filter to duplicate an audio stream.
1515
+ *
1516
+ * @param outputs - Number of output streams (default: 2)
1517
+ *
1518
+ * @returns This instance for chaining
1519
+ *
1520
+ * @example
1521
+ * ```typescript
1522
+ * chain.asplit() // Split into 2 outputs
1523
+ * chain.asplit(3) // Split into 3 outputs
1524
+ * ```
1525
+ *
1526
+ * @see {@link https://ffmpeg.org/ffmpeg-filters.html#asplit | FFmpeg asplit filter}
1527
+ */
1528
+ asplit(outputs = 2) {
1529
+ this.add(`asplit=${outputs}`);
1530
+ return this;
1531
+ }
1532
+ /**
1533
+ * Adds an adelay filter to delay audio by specified milliseconds.
1534
+ *
1535
+ * @param delays - Delay in milliseconds (single value or array for multiple channels)
1536
+ *
1537
+ * @returns This instance for chaining
1538
+ *
1539
+ * @example
1540
+ * ```typescript
1541
+ * chain.adelay(100) // Delay all channels by 100ms
1542
+ * chain.adelay([100, 200]) // Delay first channel by 100ms, second by 200ms
1543
+ * ```
1544
+ *
1545
+ * @see {@link https://ffmpeg.org/ffmpeg-filters.html#adelay | FFmpeg adelay filter}
1546
+ */
1547
+ adelay(delays) {
1548
+ const delayStr = Array.isArray(delays) ? delays.join('|') : delays.toString();
1549
+ this.add(`adelay=${delayStr}`);
1550
+ return this;
1551
+ }
1552
+ /**
1553
+ * Adds an aecho filter for audio echo effect.
1554
+ *
1555
+ * @param in_gain - Input gain (0-1)
1556
+ *
1557
+ * @param out_gain - Output gain (0-1)
1558
+ *
1559
+ * @param delays - Delay in milliseconds
1560
+ *
1561
+ * @param decays - Decay factor (0-1)
1562
+ *
1563
+ * @returns This instance for chaining
1564
+ *
1565
+ * @example
1566
+ * ```typescript
1567
+ * chain.aecho(0.8, 0.9, 1000, 0.3) // Echo with 1 second delay
1568
+ * ```
1569
+ *
1570
+ * @see {@link https://ffmpeg.org/ffmpeg-filters.html#aecho | FFmpeg aecho filter}
1571
+ */
1572
+ aecho(in_gain, out_gain, delays, decays) {
1573
+ this.add(`aecho=${in_gain}:${out_gain}:${delays}:${decays}`);
1574
+ return this;
1575
+ }
1576
+ /**
1577
+ * Adds a highpass filter to remove low frequencies.
1578
+ *
1579
+ * @param frequency - Cutoff frequency in Hz
1580
+ *
1581
+ * @param options - Additional filter options
1582
+ *
1583
+ * @returns This instance for chaining
1584
+ *
1585
+ * @example
1586
+ * ```typescript
1587
+ * chain.highpass(200) // Remove frequencies below 200Hz
1588
+ * chain.highpass(200, { width_type: 'q', width: 1 })
1589
+ * ```
1590
+ *
1591
+ * @see {@link https://ffmpeg.org/ffmpeg-filters.html#highpass | FFmpeg highpass filter}
1592
+ */
1593
+ highpass(frequency, options) {
1594
+ let filter = `highpass=f=${frequency}`;
1595
+ if (options) {
1596
+ for (const [key, value] of Object.entries(options)) {
1597
+ filter += `:${key}=${value}`;
1598
+ }
1599
+ }
1600
+ this.add(filter);
1601
+ return this;
1602
+ }
1603
+ /**
1604
+ * Adds a lowpass filter to remove high frequencies.
1605
+ *
1606
+ * @param frequency - Cutoff frequency in Hz
1607
+ *
1608
+ * @param options - Additional filter options
1609
+ *
1610
+ * @returns This instance for chaining
1611
+ *
1612
+ * @example
1613
+ * ```typescript
1614
+ * chain.lowpass(5000) // Remove frequencies above 5000Hz
1615
+ * ```
1616
+ *
1617
+ * @see {@link https://ffmpeg.org/ffmpeg-filters.html#lowpass | FFmpeg lowpass filter}
1618
+ */
1619
+ lowpass(frequency, options) {
1620
+ let filter = `lowpass=f=${frequency}`;
1621
+ if (options) {
1622
+ for (const [key, value] of Object.entries(options)) {
1623
+ filter += `:${key}=${value}`;
1624
+ }
1625
+ }
1626
+ this.add(filter);
1627
+ return this;
1628
+ }
1629
+ /**
1630
+ * Adds a bandpass filter to keep only a frequency band.
1631
+ *
1632
+ * @param frequency - Center frequency in Hz
1633
+ *
1634
+ * @param options - Additional filter options
1635
+ *
1636
+ * @returns This instance for chaining
1637
+ *
1638
+ * @example
1639
+ * ```typescript
1640
+ * chain.bandpass(1000) // Keep frequencies around 1000Hz
1641
+ * ```
1642
+ *
1643
+ * @see {@link https://ffmpeg.org/ffmpeg-filters.html#bandpass | FFmpeg bandpass filter}
1644
+ */
1645
+ bandpass(frequency, options) {
1646
+ let filter = `bandpass=f=${frequency}`;
1647
+ if (options) {
1648
+ for (const [key, value] of Object.entries(options)) {
1649
+ filter += `:${key}=${value}`;
1650
+ }
1651
+ }
1652
+ this.add(filter);
1653
+ return this;
1654
+ }
1655
+ /**
1656
+ * Adds an equalizer filter for frequency band adjustment.
1657
+ *
1658
+ * @param frequency - Center frequency in Hz
1659
+ *
1660
+ * @param width - Band width
1661
+ *
1662
+ * @param gain - Gain in dB
1663
+ *
1664
+ * @param width_type - Width type (optional)
1665
+ *
1666
+ * @returns This instance for chaining
1667
+ *
1668
+ * @example
1669
+ * ```typescript
1670
+ * chain.equalizer(1000, 2, 5) // Boost 1000Hz by 5dB
1671
+ * chain.equalizer(1000, 2, 5, 'q') // Use Q factor for width
1672
+ * ```
1673
+ *
1674
+ * @see {@link https://ffmpeg.org/ffmpeg-filters.html#equalizer | FFmpeg equalizer filter}
1675
+ */
1676
+ equalizer(frequency, width, gain, width_type) {
1677
+ let filter = `equalizer=f=${frequency}`;
1678
+ if (width_type) {
1679
+ filter += `:width_type=${width_type}`;
1680
+ }
1681
+ filter += `:width=${width}:gain=${gain}`;
1682
+ this.add(filter);
1683
+ return this;
1684
+ }
1685
+ /**
1686
+ * Adds a compressor filter for dynamic range compression.
1687
+ *
1688
+ * @param options - Compressor parameters
1689
+ *
1690
+ * @returns This instance for chaining
1691
+ *
1692
+ * @example
1693
+ * ```typescript
1694
+ * chain.compressor() // Default compression
1695
+ * chain.compressor({
1696
+ * threshold: 0.5,
1697
+ * ratio: 4,
1698
+ * attack: 5,
1699
+ * release: 50
1700
+ * })
1701
+ * ```
1702
+ *
1703
+ * @see {@link https://ffmpeg.org/ffmpeg-filters.html#acompressor | FFmpeg acompressor filter}
1704
+ */
1705
+ compressor(options) {
1706
+ if (!options || Object.keys(options).length === 0) {
1707
+ this.add('acompressor');
1708
+ }
1709
+ else {
1710
+ let filter = 'acompressor';
1711
+ const params = [];
1712
+ for (const [key, value] of Object.entries(options)) {
1713
+ params.push(`${key}=${value}`);
1714
+ }
1715
+ filter += '=' + params.join(':');
1716
+ this.add(filter);
1717
+ }
1718
+ return this;
1719
+ }
1720
+ /**
1721
+ * Adds an atrim filter to trim audio.
1722
+ *
1723
+ * @param start - Start time in seconds
1724
+ *
1725
+ * @param end - End time in seconds (optional)
1726
+ *
1727
+ * @param duration - Duration in seconds (optional)
1728
+ *
1729
+ * @returns This instance for chaining
1730
+ *
1731
+ * @example
1732
+ * ```typescript
1733
+ * chain.atrim(10, 20) // Extract audio from 10s to 20s
1734
+ * ```
1735
+ *
1736
+ * @see {@link https://ffmpeg.org/ffmpeg-filters.html#atrim | FFmpeg atrim filter}
1737
+ */
1738
+ atrim(start, end, duration) {
1739
+ let filter = `atrim=start=${start}`;
1740
+ if (end !== undefined)
1741
+ filter += `:end=${end}`;
1742
+ if (duration !== undefined)
1743
+ filter += `:duration=${duration}`;
1744
+ this.add(filter);
1745
+ return this;
1746
+ }
1747
+ /**
1748
+ * Adds a whisper filter for audio transcription using whisper.cpp.
1749
+ * Transcribes audio and adds metadata to frames (lavfi.whisper.text, lavfi.whisper.duration).
1750
+ *
1751
+ * @param options - Whisper transcription options
1752
+ *
1753
+ * @param options.model - Path to whisper.cpp model file
1754
+ *
1755
+ * @param options.language - Language for transcription (default: 'auto')
1756
+ *
1757
+ * @param options.queue - Audio queue size in seconds (default: 3)
1758
+ *
1759
+ * @param options.useGpu - Use GPU for processing (default: true)
1760
+ *
1761
+ * @param options.gpuDevice - GPU device to use (default: 0)
1762
+ *
1763
+ * @param options.destination - Output destination for transcripts
1764
+ *
1765
+ * @param options.format - Output format: text|srt|json (default: 'text')
1766
+ *
1767
+ * @param options.vadModel - Path to VAD model file (optional)
1768
+ *
1769
+ * @param options.vadThreshold - VAD threshold 0.0-1.0 (default: 0.5)
1770
+ *
1771
+ * @param options.vadMinSpeechDuration - Minimum speech duration for VAD in seconds (default: 0.1)
1772
+ *
1773
+ * @param options.vadMinSilenceDuration - Minimum silence duration for VAD in seconds (default: 0.5)
1774
+ *
1775
+ * @returns This instance for chaining
1776
+ *
1777
+ * @example
1778
+ * ```typescript
1779
+ * // Basic transcription
1780
+ * chain.whisper({
1781
+ * model: '/path/to/ggml-base.bin'
1782
+ * });
1783
+ *
1784
+ * // With language and output
1785
+ * chain.whisper({
1786
+ * model: '/path/to/ggml-base.bin',
1787
+ * language: 'en',
1788
+ * destination: 'output.srt',
1789
+ * format: 'srt'
1790
+ * });
1791
+ *
1792
+ * // With VAD model
1793
+ * chain.whisper({
1794
+ * model: '/path/to/ggml-base.bin',
1795
+ * vadModel: '/path/to/ggml-silero-v5.1.2.bin',
1796
+ * vadThreshold: 0.5
1797
+ * });
1798
+ * ```
1799
+ *
1800
+ * @see {@link https://ffmpeg.org/ffmpeg-filters.html#whisper | FFmpeg whisper filter}
1801
+ */
1802
+ whisper(options) {
1803
+ // Normalize path for FFmpeg filter syntax:
1804
+ // 1. Convert backslashes to forward slashes (Windows compatibility)
1805
+ // 2. Escape colons with double backslash (for multi-level parsing: JS → C++ → FFmpeg)
1806
+ const normalizedModel = options.model.replace(/\\/g, '/').replace(/:/g, '\\\\:');
1807
+ const params = [`model=${normalizedModel}`];
1808
+ if (options.language !== undefined) {
1809
+ params.push(`language=${options.language}`);
1810
+ }
1811
+ if (options.queue !== undefined) {
1812
+ params.push(`queue=${options.queue}`);
1813
+ }
1814
+ if (options.useGpu !== undefined) {
1815
+ params.push(`use_gpu=${options.useGpu ? 1 : 0}`);
1816
+ }
1817
+ if (options.gpuDevice !== undefined) {
1818
+ params.push(`gpu_device=${options.gpuDevice}`);
1819
+ }
1820
+ if (options.destination !== undefined) {
1821
+ const normalizedDest = options.destination.replace(/\\/g, '/').replace(/:/g, '\\\\:');
1822
+ params.push(`destination=${normalizedDest}`);
1823
+ }
1824
+ if (options.format !== undefined) {
1825
+ params.push(`format=${options.format}`);
1826
+ }
1827
+ if (options.vadModel !== undefined) {
1828
+ const normalizedVadModel = options.vadModel.replace(/\\/g, '/').replace(/:/g, '\\\\:');
1829
+ params.push(`vad_model=${normalizedVadModel}`);
1830
+ }
1831
+ if (options.vadThreshold !== undefined) {
1832
+ params.push(`vad_threshold=${options.vadThreshold}`);
1833
+ }
1834
+ if (options.vadMinSpeechDuration !== undefined) {
1835
+ params.push(`vad_min_speech_duration=${options.vadMinSpeechDuration}`);
1836
+ }
1837
+ if (options.vadMinSilenceDuration !== undefined) {
1838
+ params.push(`vad_min_silence_duration=${options.vadMinSilenceDuration}`);
1839
+ }
1840
+ this.add(`whisper=${params.join(':')}`);
1841
+ return this;
1842
+ }
1843
+ /**
1844
+ * Adds a hwupload filter to upload frames to hardware.
1845
+ * Only applied if hardware acceleration is configured.
1846
+ *
1847
+ * @returns This instance for chaining
1848
+ *
1849
+ * @example
1850
+ * ```typescript
1851
+ * const chain = FilterPresets.chain()
1852
+ * .hwupload()
1853
+ * .scale(1920, 1080)
1854
+ * .build();
1855
+ * ```
1856
+ */
1857
+ hwupload() {
1858
+ if (!this.hardware) {
1859
+ return this;
1860
+ }
1861
+ if (this.hardware.deviceType === AV_HWDEVICE_TYPE_CUDA) {
1862
+ this.add('hwupload_cuda');
1863
+ }
1864
+ else {
1865
+ this.add('hwupload');
1866
+ }
1867
+ return this;
1868
+ }
1869
+ /**
1870
+ * Adds a hwdownload filter to download frames from hardware.
1871
+ * Only applied if hardware acceleration is configured.
1872
+ *
1873
+ * @returns This instance for chaining
1874
+ *
1875
+ * @example
1876
+ * ```typescript
1877
+ * const chain = FilterPresets.chain()
1878
+ * .scale(1920, 1080)
1879
+ * .hwdownload()
1880
+ * .build();
1881
+ * ```
1882
+ */
1883
+ hwdownload() {
1884
+ if (!this.hardware) {
1885
+ return this;
1886
+ }
1887
+ this.add('hwdownload');
1888
+ return this;
1889
+ }
1890
+ /**
1891
+ * Adds a hwmap filter to map frames between hardware devices.
1892
+ *
1893
+ * @param derive - Device to derive from (optional)
1894
+ *
1895
+ * @returns This instance for chaining
1896
+ *
1897
+ * @example
1898
+ * ```typescript
1899
+ * const chain = FilterPresets.chain()
1900
+ * .hwmap('cuda')
1901
+ * .build();
1902
+ * ```
1903
+ *
1904
+ * @example
1905
+ * ```typescript
1906
+ * const chain = FilterPresets.chain()
1907
+ * .hwmap()
1908
+ * .build();
1909
+ * ```
1910
+ */
1911
+ hwmap(derive) {
1912
+ this.add(derive ? `hwmap=derive_device=${derive}` : 'hwmap');
1913
+ return this;
1914
+ }
1915
+ /**
1916
+ * Adds a filter to the chain.
1917
+ *
1918
+ * @param filter - Filter string to add (ignored if null/undefined)
1919
+ *
1920
+ * @param unshift - If true, adds to the start instead of the end (default: false)
1921
+ *
1922
+ * @returns This instance for chaining
1923
+ *
1924
+ * @example
1925
+ * ```typescript
1926
+ * chain.add('scale=1920:1080')
1927
+ * ```
1928
+ *
1929
+ * @internal
1930
+ */
1931
+ add(filter, unshift) {
1932
+ if (filter) {
1933
+ if (unshift) {
1934
+ this.filters.unshift(filter);
1935
+ }
1936
+ else {
1937
+ this.filters.push(filter);
1938
+ }
1939
+ }
1940
+ return this;
1941
+ }
1942
+ /**
1943
+ * Determines filter support for the hardware type.
1944
+ *
1945
+ * @returns Hardware filter support configuration
1946
+ *
1947
+ * @internal
1948
+ */
1949
+ getSupport() {
1950
+ if (!this.hardware) {
1951
+ // Software-only - all filters supported
1952
+ return {
1953
+ scale: true,
1954
+ overlay: true,
1955
+ transpose: true,
1956
+ tonemap: true,
1957
+ deinterlace: true,
1958
+ denoise: true,
1959
+ flip: true,
1960
+ blur: true,
1961
+ sharpen: true,
1962
+ sobel: true,
1963
+ chromakey: true,
1964
+ colorspace: true,
1965
+ pad: true,
1966
+ stack: true,
1967
+ };
1968
+ }
1969
+ switch (this.hardware.deviceType) {
1970
+ case AV_HWDEVICE_TYPE_CUDA:
1971
+ return {
1972
+ scale: true, // scale_cuda with crop support (patch 1002)
1973
+ overlay: true, // overlay_cuda
1974
+ transpose: true, // transpose_cuda (patch 0054)
1975
+ tonemap: true, // tonemap_cuda (patch 0004)
1976
+ deinterlace: true, // bwdif_cuda, yadif_cuda
1977
+ denoise: false,
1978
+ flip: false,
1979
+ blur: true, // bilateral_cuda
1980
+ sharpen: false, // Not available (NPP not included in build)
1981
+ sobel: false,
1982
+ chromakey: true, // chromakey_cuda
1983
+ colorspace: true, // colorspace_cuda
1984
+ pad: false,
1985
+ stack: false,
1986
+ };
1987
+ case AV_HWDEVICE_TYPE_VAAPI:
1988
+ return {
1989
+ scale: true, // scale_vaapi
1990
+ overlay: true, // overlay_vaapi
1991
+ transpose: true, // transpose_vaapi
1992
+ tonemap: true, // tonemap_vaapi
1993
+ deinterlace: true, // deinterlace_vaapi
1994
+ denoise: true, // denoise_vaapi
1995
+ flip: false,
1996
+ blur: false,
1997
+ sharpen: true, // sharpness_vaapi
1998
+ sobel: false,
1999
+ chromakey: false,
2000
+ colorspace: false,
2001
+ pad: true, // pad_vaapi
2002
+ stack: true, // hstack_vaapi, vstack_vaapi, xstack_vaapi
2003
+ };
2004
+ case AV_HWDEVICE_TYPE_QSV:
2005
+ return {
2006
+ scale: true, // vpp_qsv (w/h params) with crop support (cw/ch/cx/cy)
2007
+ overlay: true, // overlay_qsv
2008
+ transpose: true, // vpp_qsv transpose option
2009
+ tonemap: true, // vpp_qsv tonemap option
2010
+ deinterlace: true, // vpp_qsv deinterlace option (bob, advanced)
2011
+ denoise: true, // vpp_qsv denoise option [0-100]
2012
+ flip: true, // vpp_qsv transpose=hflip/vflip
2013
+ blur: false,
2014
+ sharpen: true, // vpp_qsv detail option [0-100]
2015
+ sobel: false,
2016
+ chromakey: false,
2017
+ colorspace: true, // vpp_qsv out_color_matrix/primaries/transfer
2018
+ pad: false,
2019
+ stack: true, // hstack_qsv, vstack_qsv, xstack_qsv
2020
+ };
2021
+ case AV_HWDEVICE_TYPE_VULKAN:
2022
+ return {
2023
+ scale: true, // scale_vulkan (native Vulkan scaler)
2024
+ overlay: true, // overlay_vulkan (native Vulkan overlay)
2025
+ transpose: true, // transpose_vulkan (native Vulkan transpose)
2026
+ tonemap: true, // libplacebo (comprehensive HDR tone-mapping, no native alternative)
2027
+ deinterlace: true, // bwdif_vulkan (native Vulkan deinterlacer)
2028
+ denoise: true, // nlmeans_vulkan (native Vulkan denoiser)
2029
+ flip: true, // hflip_vulkan/vflip_vulkan (native Vulkan flip)
2030
+ blur: true, // avgblur_vulkan/gblur_vulkan (native Vulkan blur)
2031
+ sharpen: true, // libplacebo (contrast/sharpness adjustments, no native alternative)
2032
+ sobel: false, // Not available
2033
+ chromakey: false, // chromaber_vulkan exists but different purpose (chromatic aberration)
2034
+ colorspace: true, // libplacebo (comprehensive color management, no native alternative)
2035
+ pad: true, // libplacebo (via pos_x/y/w/h and fillcolor, no native alternative)
2036
+ stack: false, // Not available
2037
+ };
2038
+ case AV_HWDEVICE_TYPE_OPENCL:
2039
+ return {
2040
+ scale: true, // scale_opencl with crop support (patches 0006, 1004)
2041
+ overlay: true, // overlay_opencl (+ PGS support patch 0008)
2042
+ transpose: true, // transpose_opencl
2043
+ tonemap: true, // tonemap_opencl (enhanced in patch 0007)
2044
+ deinterlace: false,
2045
+ denoise: false,
2046
+ flip: false,
2047
+ blur: true, // avgblur_opencl, boxblur_opencl
2048
+ sharpen: true, // unsharp_opencl
2049
+ sobel: true, // sobel_opencl
2050
+ chromakey: true, // colorkey_opencl
2051
+ colorspace: false,
2052
+ pad: true, // pad_opencl
2053
+ stack: false,
2054
+ };
2055
+ case AV_HWDEVICE_TYPE_VIDEOTOOLBOX:
2056
+ return {
2057
+ scale: true, // scale_vt with crop support (patches 0047, 1001)
2058
+ overlay: true, // overlay_videotoolbox (patch 0048)
2059
+ transpose: true, // transpose_vt (patch 0049, CoreImage based)
2060
+ tonemap: true, // tonemap_videotoolbox (patch 0050)
2061
+ deinterlace: true, // yadif_videotoolbox
2062
+ denoise: false,
2063
+ flip: false,
2064
+ blur: false,
2065
+ sharpen: false,
2066
+ sobel: false,
2067
+ chromakey: false,
2068
+ colorspace: false,
2069
+ pad: false,
2070
+ stack: false,
2071
+ };
2072
+ case AV_HWDEVICE_TYPE_MEDIACODEC:
2073
+ // MediaCodec is Android's hardware acceleration - mainly for decode/encode
2074
+ return {
2075
+ scale: false,
2076
+ overlay: false,
2077
+ transpose: false,
2078
+ tonemap: false,
2079
+ deinterlace: false,
2080
+ denoise: false,
2081
+ flip: false,
2082
+ blur: false,
2083
+ sharpen: false,
2084
+ sobel: false,
2085
+ chromakey: false,
2086
+ colorspace: false,
2087
+ pad: false,
2088
+ stack: false,
2089
+ };
2090
+ case AV_HWDEVICE_TYPE_RKMPP: // Rockchip - has RGA filters via patch 0046
2091
+ // Note: RKMPP uses separate RKRGA (Rockchip 2D Raster Graphic Acceleration)
2092
+ // for filtering operations, configured with --enable-rkrga
2093
+ return {
2094
+ scale: true, // scale_rkrga (patch 0046)
2095
+ overlay: true, // overlay_rkrga (patch 0046)
2096
+ transpose: false,
2097
+ tonemap: false,
2098
+ deinterlace: false,
2099
+ denoise: false,
2100
+ flip: false,
2101
+ blur: false,
2102
+ sharpen: false,
2103
+ sobel: false,
2104
+ chromakey: false,
2105
+ colorspace: false,
2106
+ pad: false,
2107
+ stack: false,
2108
+ };
2109
+ // These hardware types don't have dedicated filters - they're mainly for decode/encode
2110
+ case AV_HWDEVICE_TYPE_VDPAU: // Decode-only, deprecated in favor of VAAPI
2111
+ case AV_HWDEVICE_TYPE_DXVA2: // Windows decode-only
2112
+ case AV_HWDEVICE_TYPE_D3D11VA: // Windows decode-only
2113
+ case AV_HWDEVICE_TYPE_D3D12VA: // Has HEVC encoder but no filters
2114
+ case AV_HWDEVICE_TYPE_DRM: // Linux DRM buffer sharing, not processing
2115
+ return {
2116
+ scale: false,
2117
+ overlay: false,
2118
+ transpose: false,
2119
+ tonemap: false,
2120
+ deinterlace: false,
2121
+ denoise: false,
2122
+ flip: false,
2123
+ blur: false,
2124
+ sharpen: false,
2125
+ sobel: false,
2126
+ chromakey: false,
2127
+ colorspace: false,
2128
+ pad: false,
2129
+ stack: false,
2130
+ };
2131
+ default:
2132
+ // Unknown hardware - no filter support
2133
+ return {
2134
+ scale: false,
2135
+ overlay: false,
2136
+ transpose: false,
2137
+ tonemap: false,
2138
+ deinterlace: false,
2139
+ denoise: false,
2140
+ flip: false,
2141
+ blur: false,
2142
+ sharpen: false,
2143
+ sobel: false,
2144
+ chromakey: false,
2145
+ colorspace: false,
2146
+ pad: false,
2147
+ stack: false,
2148
+ };
2149
+ }
2150
+ }
2151
+ }
2152
+ //# sourceMappingURL=filter-presets.js.map