node-av 1.1.0 → 1.2.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 +51 -38
- package/dist/api/bitstream-filter.d.ts +180 -123
- package/dist/api/bitstream-filter.js +180 -125
- package/dist/api/bitstream-filter.js.map +1 -1
- package/dist/api/decoder.d.ts +279 -132
- package/dist/api/decoder.js +285 -142
- package/dist/api/decoder.js.map +1 -1
- package/dist/api/encoder.d.ts +246 -162
- package/dist/api/encoder.js +272 -208
- package/dist/api/encoder.js.map +1 -1
- package/dist/api/filter-presets.d.ts +690 -94
- package/dist/api/filter-presets.js +686 -102
- package/dist/api/filter-presets.js.map +1 -1
- package/dist/api/filter.d.ts +249 -213
- package/dist/api/filter.js +252 -242
- package/dist/api/filter.js.map +1 -1
- package/dist/api/hardware.d.ts +224 -117
- package/dist/api/hardware.js +380 -214
- package/dist/api/hardware.js.map +1 -1
- package/dist/api/index.d.ts +3 -3
- package/dist/api/index.js +1 -1
- package/dist/api/index.js.map +1 -1
- package/dist/api/io-stream.d.ts +65 -61
- package/dist/api/io-stream.js +43 -46
- package/dist/api/io-stream.js.map +1 -1
- package/dist/api/media-input.d.ts +242 -140
- package/dist/api/media-input.js +205 -103
- package/dist/api/media-input.js.map +1 -1
- package/dist/api/media-output.d.ts +206 -128
- package/dist/api/media-output.js +210 -128
- package/dist/api/media-output.js.map +1 -1
- package/dist/api/pipeline.d.ts +168 -38
- package/dist/api/pipeline.js +238 -14
- package/dist/api/pipeline.js.map +1 -1
- package/dist/api/types.d.ts +21 -187
- package/dist/api/utils.d.ts +1 -2
- package/dist/api/utils.js +9 -0
- package/dist/api/utils.js.map +1 -1
- package/dist/lib/audio-fifo.d.ts +127 -170
- package/dist/lib/audio-fifo.js +130 -173
- package/dist/lib/audio-fifo.js.map +1 -1
- package/dist/lib/binding.js +5 -0
- package/dist/lib/binding.js.map +1 -1
- package/dist/lib/bitstream-filter-context.d.ts +139 -184
- package/dist/lib/bitstream-filter-context.js +139 -188
- package/dist/lib/bitstream-filter-context.js.map +1 -1
- package/dist/lib/bitstream-filter.d.ts +68 -54
- package/dist/lib/bitstream-filter.js +68 -54
- package/dist/lib/bitstream-filter.js.map +1 -1
- package/dist/lib/codec-context.d.ts +316 -380
- package/dist/lib/codec-context.js +316 -381
- package/dist/lib/codec-context.js.map +1 -1
- package/dist/lib/codec-parameters.d.ts +160 -170
- package/dist/lib/codec-parameters.js +162 -172
- package/dist/lib/codec-parameters.js.map +1 -1
- package/dist/lib/codec-parser.d.ts +91 -104
- package/dist/lib/codec-parser.js +92 -103
- package/dist/lib/codec-parser.js.map +1 -1
- package/dist/lib/codec.d.ts +264 -281
- package/dist/lib/codec.js +268 -285
- package/dist/lib/codec.js.map +1 -1
- package/dist/lib/dictionary.d.ts +149 -203
- package/dist/lib/dictionary.js +158 -212
- package/dist/lib/dictionary.js.map +1 -1
- package/dist/lib/error.d.ts +96 -130
- package/dist/lib/error.js +98 -128
- package/dist/lib/error.js.map +1 -1
- package/dist/lib/filter-context.d.ts +284 -218
- package/dist/lib/filter-context.js +290 -227
- package/dist/lib/filter-context.js.map +1 -1
- package/dist/lib/filter-graph.d.ts +251 -292
- package/dist/lib/filter-graph.js +253 -294
- package/dist/lib/filter-graph.js.map +1 -1
- package/dist/lib/filter-inout.d.ts +87 -95
- package/dist/lib/filter-inout.js +87 -95
- package/dist/lib/filter-inout.js.map +1 -1
- package/dist/lib/filter.d.ts +93 -111
- package/dist/lib/filter.js +93 -111
- package/dist/lib/filter.js.map +1 -1
- package/dist/lib/format-context.d.ts +320 -428
- package/dist/lib/format-context.js +313 -385
- package/dist/lib/format-context.js.map +1 -1
- package/dist/lib/frame.d.ts +262 -405
- package/dist/lib/frame.js +263 -408
- package/dist/lib/frame.js.map +1 -1
- package/dist/lib/hardware-device-context.d.ts +149 -203
- package/dist/lib/hardware-device-context.js +149 -203
- package/dist/lib/hardware-device-context.js.map +1 -1
- package/dist/lib/hardware-frames-context.d.ts +170 -180
- package/dist/lib/hardware-frames-context.js +171 -181
- package/dist/lib/hardware-frames-context.js.map +1 -1
- package/dist/lib/index.d.ts +2 -1
- package/dist/lib/index.js +2 -2
- package/dist/lib/index.js.map +1 -1
- package/dist/lib/input-format.d.ts +89 -117
- package/dist/lib/input-format.js +89 -117
- package/dist/lib/input-format.js.map +1 -1
- package/dist/lib/io-context.d.ts +209 -241
- package/dist/lib/io-context.js +220 -252
- package/dist/lib/io-context.js.map +1 -1
- package/dist/lib/log.d.ts +85 -119
- package/dist/lib/log.js +85 -122
- package/dist/lib/log.js.map +1 -1
- package/dist/lib/native-types.d.ts +117 -106
- package/dist/lib/native-types.js +0 -7
- package/dist/lib/native-types.js.map +1 -1
- package/dist/lib/option.d.ts +284 -241
- package/dist/lib/option.js +309 -249
- package/dist/lib/option.js.map +1 -1
- package/dist/lib/output-format.d.ts +77 -101
- package/dist/lib/output-format.js +77 -101
- package/dist/lib/output-format.js.map +1 -1
- package/dist/lib/packet.d.ts +172 -240
- package/dist/lib/packet.js +172 -241
- package/dist/lib/packet.js.map +1 -1
- package/dist/lib/rational.d.ts +0 -2
- package/dist/lib/rational.js +0 -2
- package/dist/lib/rational.js.map +1 -1
- package/dist/lib/software-resample-context.d.ts +241 -325
- package/dist/lib/software-resample-context.js +242 -326
- package/dist/lib/software-resample-context.js.map +1 -1
- package/dist/lib/software-scale-context.d.ts +129 -173
- package/dist/lib/software-scale-context.js +131 -175
- package/dist/lib/software-scale-context.js.map +1 -1
- package/dist/lib/stream.d.ts +87 -197
- package/dist/lib/stream.js +87 -197
- package/dist/lib/stream.js.map +1 -1
- package/dist/lib/utilities.d.ts +372 -181
- package/dist/lib/utilities.js +373 -182
- package/dist/lib/utilities.js.map +1 -1
- package/install/check.js +0 -1
- package/package.json +21 -12
- package/release_notes.md +43 -59
- package/CHANGELOG.md +0 -8
package/dist/api/filter.js
CHANGED
|
@@ -1,67 +1,42 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Filter - High-level wrapper for media filtering
|
|
3
|
-
*
|
|
4
|
-
* Implements FFmpeg CLI's filter graph behavior with proper hardware context handling.
|
|
5
|
-
* Uses lazy initialization for hardware inputs: graph is built when first frame arrives
|
|
6
|
-
* with hw_frames_ctx. For software inputs, initializes immediately.
|
|
7
|
-
*
|
|
8
|
-
* Handles filter graph creation, frame processing, and format conversion.
|
|
9
|
-
* Supports complex filter chains and hardware-accelerated filters.
|
|
10
|
-
*
|
|
11
|
-
* @module api/filter
|
|
12
|
-
*/
|
|
13
1
|
import { AVERROR_EOF, AVFILTER_FLAG_HWDEVICE, AVMEDIA_TYPE_AUDIO, AVMEDIA_TYPE_VIDEO } from '../constants/constants.js';
|
|
14
2
|
import { AVERROR_EAGAIN, avGetSampleFmtName, avIsHardwarePixelFormat, FFmpegError, Filter, FilterGraph, FilterInOut, Frame } from '../lib/index.js';
|
|
15
3
|
/**
|
|
16
|
-
* High-level filter API for
|
|
4
|
+
* High-level filter API for audio and video processing.
|
|
17
5
|
*
|
|
18
|
-
* Provides
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
* The filter graph uses lazy initialization for hardware inputs - it's built when
|
|
23
|
-
* the first frame arrives with hw_frames_ctx. This matches FFmpeg CLI behavior
|
|
24
|
-
* for proper hardware context propagation.
|
|
6
|
+
* Provides simplified interface for applying FFmpeg filters to frames.
|
|
7
|
+
* Handles filter graph construction, frame buffering, and command control.
|
|
8
|
+
* Supports both software and hardware-accelerated filtering operations.
|
|
9
|
+
* Essential component for effects, transformations, and format conversions.
|
|
25
10
|
*
|
|
26
11
|
* @example
|
|
27
12
|
* ```typescript
|
|
28
|
-
* import { FilterAPI
|
|
29
|
-
*
|
|
30
|
-
* // Simple video filter from a stream
|
|
31
|
-
* const videoStream = media.video();
|
|
32
|
-
* const filter = await FilterAPI.create('scale=1280:720,format=yuv420p', videoStream);
|
|
13
|
+
* import { FilterAPI } from 'node-av/api';
|
|
33
14
|
*
|
|
34
|
-
* //
|
|
35
|
-
* const
|
|
36
|
-
* ```
|
|
15
|
+
* // Create video filter
|
|
16
|
+
* const filter = await FilterAPI.create('scale=1280:720', videoInfo);
|
|
37
17
|
*
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
-
*
|
|
43
|
-
*
|
|
44
|
-
* hardware: hw
|
|
45
|
-
* });
|
|
18
|
+
* // Process frame
|
|
19
|
+
* const output = await filter.process(inputFrame);
|
|
20
|
+
* if (output) {
|
|
21
|
+
* console.log(`Filtered frame: ${output.width}x${output.height}`);
|
|
22
|
+
* output.free();
|
|
23
|
+
* }
|
|
46
24
|
* ```
|
|
47
25
|
*
|
|
48
26
|
* @example
|
|
49
27
|
* ```typescript
|
|
50
|
-
* //
|
|
51
|
-
* const
|
|
52
|
-
* const
|
|
53
|
-
*
|
|
54
|
-
*
|
|
55
|
-
* }
|
|
28
|
+
* // Hardware-accelerated filtering
|
|
29
|
+
* const hw = HardwareContext.auto();
|
|
30
|
+
* const filter = await FilterAPI.create(
|
|
31
|
+
* 'hwupload,scale_cuda=1920:1080,hwdownload',
|
|
32
|
+
* videoInfo,
|
|
33
|
+
* { hardware: hw }
|
|
34
|
+
* );
|
|
56
35
|
* ```
|
|
57
36
|
*
|
|
58
|
-
* @
|
|
59
|
-
*
|
|
60
|
-
*
|
|
61
|
-
* const hw = await HardwareContext.auto();
|
|
62
|
-
* const decoder = await Decoder.create(stream, { hardware: hw });
|
|
63
|
-
* const filter = await FilterAPI.create('hwdownload,format=yuv420p', decoder.getOutputStreamInfo());
|
|
64
|
-
* ```
|
|
37
|
+
* @see {@link FilterGraph} For low-level filter graph API
|
|
38
|
+
* @see {@link HardwareContext} For hardware acceleration
|
|
39
|
+
* @see {@link Frame} For frame operations
|
|
65
40
|
*/
|
|
66
41
|
export class FilterAPI {
|
|
67
42
|
graph = null;
|
|
@@ -74,11 +49,9 @@ export class FilterAPI {
|
|
|
74
49
|
description;
|
|
75
50
|
options;
|
|
76
51
|
/**
|
|
77
|
-
*
|
|
78
|
-
*
|
|
79
|
-
* @param
|
|
80
|
-
* @param description - Filter graph description
|
|
81
|
-
* @param options - Filter options including hardware context
|
|
52
|
+
* @param config - Stream configuration
|
|
53
|
+
* @param description - Filter description string
|
|
54
|
+
* @param options - Filter options
|
|
82
55
|
* @internal
|
|
83
56
|
*/
|
|
84
57
|
constructor(config, description, options) {
|
|
@@ -89,52 +62,48 @@ export class FilterAPI {
|
|
|
89
62
|
this.mediaType = config.type === 'video' ? AVMEDIA_TYPE_VIDEO : AVMEDIA_TYPE_AUDIO;
|
|
90
63
|
}
|
|
91
64
|
/**
|
|
92
|
-
* Create a filter
|
|
65
|
+
* Create a filter with specified description and configuration.
|
|
93
66
|
*
|
|
94
|
-
*
|
|
95
|
-
*
|
|
67
|
+
* Constructs filter graph from description string.
|
|
68
|
+
* Configures input/output buffers and threading.
|
|
69
|
+
* For video filters, uses lazy initialization to detect hardware frames.
|
|
96
70
|
*
|
|
97
|
-
*
|
|
98
|
-
* with hw_frames_ctx before configuring the filter graph.
|
|
99
|
-
* For software formats: Initializes immediately.
|
|
71
|
+
* Direct mapping to avfilter_graph_parse_ptr() and avfilter_graph_config().
|
|
100
72
|
*
|
|
101
|
-
*
|
|
102
|
-
*
|
|
103
|
-
*
|
|
104
|
-
*
|
|
105
|
-
*
|
|
106
|
-
* @param description - Filter graph description (e.g., "scale=1280:720" or complex chains)
|
|
107
|
-
* @param input - Stream or StreamInfo describing the input
|
|
108
|
-
* @param options - Optional filter options including hardware context
|
|
109
|
-
*
|
|
110
|
-
* @returns Promise resolving to configured Filter instance
|
|
73
|
+
* @param description - Filter graph description
|
|
74
|
+
* @param input - Input stream configuration
|
|
75
|
+
* @param options - Filter options
|
|
76
|
+
* @returns Configured filter instance
|
|
111
77
|
*
|
|
112
|
-
* @throws {
|
|
113
|
-
* @throws {
|
|
78
|
+
* @throws {Error} If filter creation or configuration fails
|
|
79
|
+
* @throws {FFmpegError} If graph parsing or config fails
|
|
114
80
|
*
|
|
115
81
|
* @example
|
|
116
82
|
* ```typescript
|
|
117
|
-
* // Simple filter
|
|
118
|
-
* const filter = await FilterAPI.create('scale=640:480',
|
|
83
|
+
* // Simple video filter
|
|
84
|
+
* const filter = await FilterAPI.create('scale=640:480', videoInfo);
|
|
85
|
+
* ```
|
|
119
86
|
*
|
|
120
|
-
*
|
|
121
|
-
*
|
|
122
|
-
*
|
|
87
|
+
* @example
|
|
88
|
+
* ```typescript
|
|
89
|
+
* // Complex filter chain
|
|
123
90
|
* const filter = await FilterAPI.create(
|
|
124
|
-
* '
|
|
125
|
-
*
|
|
126
|
-
* { hardware: hw }
|
|
91
|
+
* 'crop=640:480:0:0,rotate=PI/4',
|
|
92
|
+
* videoInfo
|
|
127
93
|
* );
|
|
94
|
+
* ```
|
|
128
95
|
*
|
|
129
|
-
*
|
|
130
|
-
*
|
|
131
|
-
*
|
|
132
|
-
*
|
|
133
|
-
*
|
|
134
|
-
*
|
|
135
|
-
*
|
|
136
|
-
* });
|
|
96
|
+
* @example
|
|
97
|
+
* ```typescript
|
|
98
|
+
* // Audio filter
|
|
99
|
+
* const filter = await FilterAPI.create(
|
|
100
|
+
* 'volume=0.5,aecho=0.8:0.9:1000:0.3',
|
|
101
|
+
* audioInfo
|
|
102
|
+
* );
|
|
137
103
|
* ```
|
|
104
|
+
*
|
|
105
|
+
* @see {@link process} For frame processing
|
|
106
|
+
* @see {@link FilterOptions} For configuration options
|
|
138
107
|
*/
|
|
139
108
|
static async create(description, input, options = {}) {
|
|
140
109
|
let config;
|
|
@@ -172,29 +141,44 @@ export class FilterAPI {
|
|
|
172
141
|
return filter;
|
|
173
142
|
}
|
|
174
143
|
/**
|
|
175
|
-
* Process a
|
|
176
|
-
*
|
|
177
|
-
* Sends a frame through the filter graph and returns the filtered result.
|
|
178
|
-
* May return null if the filter needs more input frames.
|
|
144
|
+
* Process a frame through the filter.
|
|
179
145
|
*
|
|
180
|
-
*
|
|
181
|
-
*
|
|
182
|
-
*
|
|
146
|
+
* Applies filter operations to input frame.
|
|
147
|
+
* May buffer frames internally before producing output.
|
|
148
|
+
* For video, performs lazy initialization on first frame.
|
|
183
149
|
*
|
|
184
|
-
*
|
|
150
|
+
* Direct mapping to av_buffersrc_add_frame() and av_buffersink_get_frame().
|
|
185
151
|
*
|
|
186
|
-
* @
|
|
152
|
+
* @param frame - Input frame to process
|
|
153
|
+
* @returns Filtered frame or null if buffered
|
|
187
154
|
*
|
|
155
|
+
* @throws {Error} If filter not ready
|
|
188
156
|
* @throws {FFmpegError} If processing fails
|
|
189
|
-
* @throws {Error} If filter not initialized or hardware frame required but not provided
|
|
190
157
|
*
|
|
191
158
|
* @example
|
|
192
159
|
* ```typescript
|
|
193
|
-
* const
|
|
194
|
-
* if (
|
|
195
|
-
*
|
|
160
|
+
* const output = await filter.process(inputFrame);
|
|
161
|
+
* if (output) {
|
|
162
|
+
* console.log(`Got filtered frame: pts=${output.pts}`);
|
|
163
|
+
* output.free();
|
|
164
|
+
* }
|
|
165
|
+
* ```
|
|
166
|
+
*
|
|
167
|
+
* @example
|
|
168
|
+
* ```typescript
|
|
169
|
+
* // Process and drain
|
|
170
|
+
* const output = await filter.process(frame);
|
|
171
|
+
* if (output) yield output;
|
|
172
|
+
*
|
|
173
|
+
* // Drain buffered frames
|
|
174
|
+
* let buffered;
|
|
175
|
+
* while ((buffered = await filter.receive()) !== null) {
|
|
176
|
+
* yield buffered;
|
|
196
177
|
* }
|
|
197
178
|
* ```
|
|
179
|
+
*
|
|
180
|
+
* @see {@link receive} For draining buffered frames
|
|
181
|
+
* @see {@link frames} For stream processing
|
|
198
182
|
*/
|
|
199
183
|
async process(frame) {
|
|
200
184
|
// Lazy initialization for video filters (detect hardware from first frame)
|
|
@@ -214,7 +198,7 @@ export class FilterAPI {
|
|
|
214
198
|
if (getRet >= 0) {
|
|
215
199
|
return outputFrame;
|
|
216
200
|
}
|
|
217
|
-
else if (
|
|
201
|
+
else if (getRet === AVERROR_EAGAIN) {
|
|
218
202
|
// Need more input
|
|
219
203
|
outputFrame.free();
|
|
220
204
|
return null;
|
|
@@ -226,21 +210,27 @@ export class FilterAPI {
|
|
|
226
210
|
}
|
|
227
211
|
}
|
|
228
212
|
/**
|
|
229
|
-
* Process multiple frames
|
|
213
|
+
* Process multiple frames at once.
|
|
230
214
|
*
|
|
231
|
-
*
|
|
232
|
-
*
|
|
215
|
+
* Processes batch of frames and drains all output.
|
|
216
|
+
* Useful for filters that buffer multiple frames.
|
|
233
217
|
*
|
|
234
218
|
* @param frames - Array of input frames
|
|
219
|
+
* @returns Array of all output frames
|
|
235
220
|
*
|
|
236
|
-
* @
|
|
237
|
-
*
|
|
221
|
+
* @throws {Error} If filter not ready
|
|
238
222
|
* @throws {FFmpegError} If processing fails
|
|
239
223
|
*
|
|
240
224
|
* @example
|
|
241
225
|
* ```typescript
|
|
242
|
-
* const
|
|
226
|
+
* const outputs = await filter.processMultiple([frame1, frame2, frame3]);
|
|
227
|
+
* for (const output of outputs) {
|
|
228
|
+
* console.log(`Output frame: pts=${output.pts}`);
|
|
229
|
+
* output.free();
|
|
230
|
+
* }
|
|
243
231
|
* ```
|
|
232
|
+
*
|
|
233
|
+
* @see {@link process} For single frame processing
|
|
244
234
|
*/
|
|
245
235
|
async processMultiple(frames) {
|
|
246
236
|
const outputFrames = [];
|
|
@@ -260,24 +250,30 @@ export class FilterAPI {
|
|
|
260
250
|
return outputFrames;
|
|
261
251
|
}
|
|
262
252
|
/**
|
|
263
|
-
* Receive
|
|
253
|
+
* Receive buffered frame from filter.
|
|
254
|
+
*
|
|
255
|
+
* Drains frames buffered by the filter.
|
|
256
|
+
* Call repeatedly until null to get all buffered frames.
|
|
264
257
|
*
|
|
265
|
-
*
|
|
266
|
-
* Returns null when no more frames are available.
|
|
258
|
+
* Direct mapping to av_buffersink_get_frame().
|
|
267
259
|
*
|
|
268
|
-
* @returns
|
|
260
|
+
* @returns Buffered frame or null if none available
|
|
269
261
|
*
|
|
270
|
-
* @throws {
|
|
262
|
+
* @throws {Error} If filter not ready
|
|
263
|
+
* @throws {FFmpegError} If receive fails
|
|
271
264
|
*
|
|
272
265
|
* @example
|
|
273
266
|
* ```typescript
|
|
274
|
-
* // Drain
|
|
275
|
-
*
|
|
276
|
-
*
|
|
277
|
-
*
|
|
278
|
-
*
|
|
267
|
+
* // Drain buffered frames
|
|
268
|
+
* let frame;
|
|
269
|
+
* while ((frame = await filter.receive()) !== null) {
|
|
270
|
+
* console.log(`Buffered frame: pts=${frame.pts}`);
|
|
271
|
+
* frame.free();
|
|
279
272
|
* }
|
|
280
273
|
* ```
|
|
274
|
+
*
|
|
275
|
+
* @see {@link process} For input processing
|
|
276
|
+
* @see {@link flush} For end-of-stream
|
|
281
277
|
*/
|
|
282
278
|
async receive() {
|
|
283
279
|
if (!this.initialized || !this.buffersinkCtx) {
|
|
@@ -291,7 +287,7 @@ export class FilterAPI {
|
|
|
291
287
|
}
|
|
292
288
|
else {
|
|
293
289
|
frame.free();
|
|
294
|
-
if (
|
|
290
|
+
if (ret === AVERROR_EAGAIN || ret === AVERROR_EOF) {
|
|
295
291
|
return null;
|
|
296
292
|
}
|
|
297
293
|
FFmpegError.throwIfError(ret, 'Failed to receive frame from filter');
|
|
@@ -299,53 +295,58 @@ export class FilterAPI {
|
|
|
299
295
|
}
|
|
300
296
|
}
|
|
301
297
|
/**
|
|
302
|
-
* Flush
|
|
298
|
+
* Flush filter and signal end-of-stream.
|
|
303
299
|
*
|
|
304
|
-
*
|
|
305
|
-
*
|
|
300
|
+
* Sends null frame to flush buffered data.
|
|
301
|
+
* Must call receive() to get flushed frames.
|
|
306
302
|
*
|
|
307
|
-
*
|
|
303
|
+
* Direct mapping to av_buffersrc_add_frame(NULL).
|
|
308
304
|
*
|
|
305
|
+
* @throws {Error} If filter not ready
|
|
309
306
|
* @throws {FFmpegError} If flush fails
|
|
310
307
|
*
|
|
311
308
|
* @example
|
|
312
309
|
* ```typescript
|
|
313
310
|
* await filter.flush();
|
|
314
311
|
* // Get remaining frames
|
|
315
|
-
*
|
|
316
|
-
*
|
|
317
|
-
*
|
|
318
|
-
* // Process final frames
|
|
312
|
+
* let frame;
|
|
313
|
+
* while ((frame = await filter.receive()) !== null) {
|
|
314
|
+
* frame.free();
|
|
319
315
|
* }
|
|
320
316
|
* ```
|
|
317
|
+
*
|
|
318
|
+
* @see {@link flushFrames} For async iteration
|
|
319
|
+
* @see {@link receive} For draining frames
|
|
321
320
|
*/
|
|
322
321
|
async flush() {
|
|
323
322
|
if (!this.initialized || !this.buffersrcCtx) {
|
|
324
323
|
throw new Error('Filter not initialized');
|
|
325
324
|
}
|
|
326
325
|
const ret = await this.buffersrcCtx.buffersrcAddFrame(null);
|
|
327
|
-
if (ret < 0 &&
|
|
326
|
+
if (ret < 0 && ret !== AVERROR_EOF) {
|
|
328
327
|
FFmpegError.throwIfError(ret, 'Failed to flush filter');
|
|
329
328
|
}
|
|
330
329
|
}
|
|
331
330
|
/**
|
|
332
|
-
* Flush filter and yield
|
|
331
|
+
* Flush filter and yield remaining frames.
|
|
333
332
|
*
|
|
334
|
-
*
|
|
335
|
-
*
|
|
333
|
+
* Convenient async generator for flushing.
|
|
334
|
+
* Combines flush and receive operations.
|
|
336
335
|
*
|
|
337
|
-
* @
|
|
338
|
-
*
|
|
339
|
-
* @throws {
|
|
336
|
+
* @yields Remaining frames from filter
|
|
337
|
+
* @throws {Error} If filter not ready
|
|
338
|
+
* @throws {FFmpegError} If flush fails
|
|
340
339
|
*
|
|
341
340
|
* @example
|
|
342
341
|
* ```typescript
|
|
343
|
-
* // Process all remaining frames with generator
|
|
344
342
|
* for await (const frame of filter.flushFrames()) {
|
|
345
|
-
*
|
|
346
|
-
*
|
|
343
|
+
* console.log(`Flushed frame: pts=${frame.pts}`);
|
|
344
|
+
* frame.free();
|
|
347
345
|
* }
|
|
348
346
|
* ```
|
|
347
|
+
*
|
|
348
|
+
* @see {@link flush} For manual flush
|
|
349
|
+
* @see {@link frames} For complete pipeline
|
|
349
350
|
*/
|
|
350
351
|
async *flushFrames() {
|
|
351
352
|
if (!this.initialized || !this.buffersrcCtx) {
|
|
@@ -360,26 +361,40 @@ export class FilterAPI {
|
|
|
360
361
|
}
|
|
361
362
|
}
|
|
362
363
|
/**
|
|
363
|
-
* Process
|
|
364
|
+
* Process frame stream through filter.
|
|
364
365
|
*
|
|
365
|
-
*
|
|
366
|
-
* Automatically handles buffering and
|
|
367
|
-
*
|
|
366
|
+
* High-level async generator for filtering frame streams.
|
|
367
|
+
* Automatically handles buffering and flushing.
|
|
368
|
+
* Frees input frames after processing.
|
|
368
369
|
*
|
|
369
|
-
*
|
|
370
|
-
*
|
|
371
|
-
*
|
|
372
|
-
* @
|
|
370
|
+
* @param frames - Async generator of input frames
|
|
371
|
+
* @yields Filtered frames
|
|
372
|
+
* @throws {Error} If filter not ready
|
|
373
|
+
* @throws {FFmpegError} If processing fails
|
|
373
374
|
*
|
|
374
|
-
* @
|
|
375
|
+
* @example
|
|
376
|
+
* ```typescript
|
|
377
|
+
* // Filter decoded frames
|
|
378
|
+
* for await (const frame of filter.frames(decoder.frames(packets))) {
|
|
379
|
+
* await encoder.encode(frame);
|
|
380
|
+
* frame.free();
|
|
381
|
+
* }
|
|
382
|
+
* ```
|
|
375
383
|
*
|
|
376
384
|
* @example
|
|
377
385
|
* ```typescript
|
|
378
|
-
*
|
|
379
|
-
*
|
|
380
|
-
*
|
|
386
|
+
* // Chain filters
|
|
387
|
+
* const filter1 = await FilterAPI.create('scale=640:480', info);
|
|
388
|
+
* const filter2 = await FilterAPI.create('rotate=PI/4', info);
|
|
389
|
+
*
|
|
390
|
+
* for await (const frame of filter2.frames(filter1.frames(input))) {
|
|
391
|
+
* // Process filtered frames
|
|
392
|
+
* frame.free();
|
|
381
393
|
* }
|
|
382
394
|
* ```
|
|
395
|
+
*
|
|
396
|
+
* @see {@link process} For single frame processing
|
|
397
|
+
* @see {@link flush} For end-of-stream handling
|
|
383
398
|
*/
|
|
384
399
|
async *frames(frames) {
|
|
385
400
|
for await (const frame of frames) {
|
|
@@ -412,32 +427,30 @@ export class FilterAPI {
|
|
|
412
427
|
}
|
|
413
428
|
}
|
|
414
429
|
/**
|
|
415
|
-
* Send
|
|
430
|
+
* Send command to filter.
|
|
431
|
+
*
|
|
432
|
+
* Sends runtime command to specific filter in graph.
|
|
433
|
+
* Allows dynamic parameter adjustment.
|
|
416
434
|
*
|
|
417
|
-
*
|
|
418
|
-
* Not all filters support commands - check filter documentation.
|
|
435
|
+
* Direct mapping to avfilter_graph_send_command().
|
|
419
436
|
*
|
|
420
|
-
* @param target -
|
|
421
|
-
* @param cmd - Command name
|
|
422
|
-
* @param arg - Command argument
|
|
423
|
-
* @param flags -
|
|
437
|
+
* @param target - Target filter name
|
|
438
|
+
* @param cmd - Command name
|
|
439
|
+
* @param arg - Command argument
|
|
440
|
+
* @param flags - Command flags
|
|
441
|
+
* @returns Response string from filter
|
|
424
442
|
*
|
|
425
|
-
* @
|
|
443
|
+
* @throws {Error} If filter not ready
|
|
444
|
+
* @throws {FFmpegError} If command fails
|
|
426
445
|
*
|
|
427
446
|
* @example
|
|
428
447
|
* ```typescript
|
|
429
|
-
* // Change volume
|
|
448
|
+
* // Change volume at runtime
|
|
430
449
|
* const response = filter.sendCommand('volume', 'volume', '0.5');
|
|
431
|
-
*
|
|
432
|
-
* console.log('Volume changed successfully');
|
|
433
|
-
* }
|
|
450
|
+
* console.log(`Volume changed: ${response}`);
|
|
434
451
|
* ```
|
|
435
452
|
*
|
|
436
|
-
* @
|
|
437
|
-
* ```typescript
|
|
438
|
-
* // Enable/disable all filters at runtime
|
|
439
|
-
* filter.sendCommand('all', 'enable', 'expr=gte(t,10)');
|
|
440
|
-
* ```
|
|
453
|
+
* @see {@link queueCommand} For delayed commands
|
|
441
454
|
*/
|
|
442
455
|
sendCommand(target, cmd, arg, flags) {
|
|
443
456
|
if (!this.initialized || !this.graph) {
|
|
@@ -450,24 +463,28 @@ export class FilterAPI {
|
|
|
450
463
|
return result.response;
|
|
451
464
|
}
|
|
452
465
|
/**
|
|
453
|
-
* Queue
|
|
466
|
+
* Queue command for later execution.
|
|
467
|
+
*
|
|
468
|
+
* Schedules command to execute at specific timestamp.
|
|
469
|
+
* Useful for synchronized parameter changes.
|
|
454
470
|
*
|
|
455
|
-
*
|
|
456
|
-
* Useful for scripted filter changes synchronized with media playback.
|
|
471
|
+
* Direct mapping to avfilter_graph_queue_command().
|
|
457
472
|
*
|
|
458
|
-
* @param target -
|
|
459
|
-
* @param cmd - Command name
|
|
460
|
-
* @param arg - Command argument
|
|
461
|
-
* @param ts - Timestamp
|
|
462
|
-
* @param flags -
|
|
473
|
+
* @param target - Target filter name
|
|
474
|
+
* @param cmd - Command name
|
|
475
|
+
* @param arg - Command argument
|
|
476
|
+
* @param ts - Timestamp for execution
|
|
477
|
+
* @param flags - Command flags
|
|
478
|
+
* @throws {Error} If filter not ready
|
|
479
|
+
* @throws {FFmpegError} If queue fails
|
|
463
480
|
*
|
|
464
481
|
* @example
|
|
465
482
|
* ```typescript
|
|
466
|
-
* //
|
|
467
|
-
* filter.queueCommand('volume', 'volume', '0.
|
|
468
|
-
* filter.queueCommand('volume', 'volume', '0.8', 10.0); // At 10 seconds
|
|
469
|
-
* filter.queueCommand('volume', 'volume', '0.2', 15.0); // At 15 seconds
|
|
483
|
+
* // Queue volume change at 10 seconds
|
|
484
|
+
* filter.queueCommand('volume', 'volume', '0.8', 10.0);
|
|
470
485
|
* ```
|
|
486
|
+
*
|
|
487
|
+
* @see {@link sendCommand} For immediate commands
|
|
471
488
|
*/
|
|
472
489
|
queueCommand(target, cmd, arg, ts, flags) {
|
|
473
490
|
if (!this.initialized || !this.graph) {
|
|
@@ -477,17 +494,19 @@ export class FilterAPI {
|
|
|
477
494
|
FFmpegError.throwIfError(ret, 'Failed to queue filter command');
|
|
478
495
|
}
|
|
479
496
|
/**
|
|
480
|
-
* Get
|
|
497
|
+
* Get filter graph description.
|
|
481
498
|
*
|
|
482
|
-
* Returns
|
|
483
|
-
* Useful for debugging
|
|
499
|
+
* Returns human-readable graph structure.
|
|
500
|
+
* Useful for debugging filter chains.
|
|
501
|
+
*
|
|
502
|
+
* Direct mapping to avfilter_graph_dump().
|
|
484
503
|
*
|
|
485
504
|
* @returns Graph description or null if not initialized
|
|
486
505
|
*
|
|
487
506
|
* @example
|
|
488
507
|
* ```typescript
|
|
489
508
|
* const description = filter.getGraphDescription();
|
|
490
|
-
* console.log(description);
|
|
509
|
+
* console.log('Filter graph:', description);
|
|
491
510
|
* ```
|
|
492
511
|
*/
|
|
493
512
|
getGraphDescription() {
|
|
@@ -497,32 +516,47 @@ export class FilterAPI {
|
|
|
497
516
|
return this.graph.dump();
|
|
498
517
|
}
|
|
499
518
|
/**
|
|
500
|
-
* Check if
|
|
519
|
+
* Check if filter is ready for processing.
|
|
520
|
+
*
|
|
521
|
+
* @returns true if initialized and ready
|
|
501
522
|
*
|
|
502
|
-
* @
|
|
523
|
+
* @example
|
|
524
|
+
* ```typescript
|
|
525
|
+
* if (filter.isReady()) {
|
|
526
|
+
* const output = await filter.process(frame);
|
|
527
|
+
* }
|
|
528
|
+
* ```
|
|
503
529
|
*/
|
|
504
530
|
isReady() {
|
|
505
531
|
return this.initialized && this.buffersrcCtx !== null && this.buffersinkCtx !== null;
|
|
506
532
|
}
|
|
507
533
|
/**
|
|
508
|
-
* Get
|
|
534
|
+
* Get media type of filter.
|
|
535
|
+
*
|
|
536
|
+
* @returns AVMEDIA_TYPE_VIDEO or AVMEDIA_TYPE_AUDIO
|
|
509
537
|
*
|
|
510
|
-
* @
|
|
538
|
+
* @example
|
|
539
|
+
* ```typescript
|
|
540
|
+
* if (filter.getMediaType() === AVMEDIA_TYPE_VIDEO) {
|
|
541
|
+
* console.log('Video filter');
|
|
542
|
+
* }
|
|
543
|
+
* ```
|
|
511
544
|
*/
|
|
512
545
|
getMediaType() {
|
|
513
546
|
return this.mediaType;
|
|
514
547
|
}
|
|
515
548
|
/**
|
|
516
|
-
* Free
|
|
549
|
+
* Free filter resources.
|
|
517
550
|
*
|
|
518
|
-
* Releases
|
|
519
|
-
*
|
|
551
|
+
* Releases filter graph and contexts.
|
|
552
|
+
* Safe to call multiple times.
|
|
520
553
|
*
|
|
521
554
|
* @example
|
|
522
555
|
* ```typescript
|
|
523
556
|
* filter.free();
|
|
524
|
-
* // filter is now invalid
|
|
525
557
|
* ```
|
|
558
|
+
*
|
|
559
|
+
* @see {@link Symbol.dispose} For automatic cleanup
|
|
526
560
|
*/
|
|
527
561
|
free() {
|
|
528
562
|
if (this.graph) {
|
|
@@ -534,15 +568,14 @@ export class FilterAPI {
|
|
|
534
568
|
this.initialized = false;
|
|
535
569
|
}
|
|
536
570
|
/**
|
|
537
|
-
* Initialize
|
|
571
|
+
* Initialize filter graph.
|
|
538
572
|
*
|
|
539
|
-
*
|
|
540
|
-
*
|
|
573
|
+
* Creates and configures filter graph components.
|
|
574
|
+
* For video, may use hardware frames context from first frame.
|
|
541
575
|
*
|
|
542
|
-
*
|
|
543
|
-
*
|
|
544
|
-
*
|
|
545
|
-
* @internal
|
|
576
|
+
* @param firstFrame - First frame for hardware detection (video only)
|
|
577
|
+
* @throws {Error} If initialization fails
|
|
578
|
+
* @throws {FFmpegError} If configuration fails
|
|
546
579
|
*/
|
|
547
580
|
async initialize(firstFrame) {
|
|
548
581
|
// Create graph
|
|
@@ -587,7 +620,9 @@ export class FilterAPI {
|
|
|
587
620
|
/**
|
|
588
621
|
* Create buffer source with hardware frames context.
|
|
589
622
|
*
|
|
590
|
-
* @
|
|
623
|
+
* @param frame - Frame with hw_frames_ctx
|
|
624
|
+
* @throws {Error} If creation fails
|
|
625
|
+
* @throws {FFmpegError} If configuration fails
|
|
591
626
|
*/
|
|
592
627
|
createBufferSourceWithHwFrames(frame) {
|
|
593
628
|
const filterName = 'buffer';
|
|
@@ -617,9 +652,9 @@ export class FilterAPI {
|
|
|
617
652
|
FFmpegError.throwIfError(initRet, 'Failed to initialize buffer source');
|
|
618
653
|
}
|
|
619
654
|
/**
|
|
620
|
-
* Create
|
|
655
|
+
* Create standard buffer source.
|
|
621
656
|
*
|
|
622
|
-
* @
|
|
657
|
+
* @throws {Error} If creation fails
|
|
623
658
|
*/
|
|
624
659
|
createBufferSource() {
|
|
625
660
|
const filterName = this.config.type === 'video' ? 'buffer' : 'abuffer';
|
|
@@ -651,9 +686,9 @@ export class FilterAPI {
|
|
|
651
686
|
}
|
|
652
687
|
}
|
|
653
688
|
/**
|
|
654
|
-
* Create
|
|
689
|
+
* Create buffer sink.
|
|
655
690
|
*
|
|
656
|
-
* @
|
|
691
|
+
* @throws {Error} If creation fails
|
|
657
692
|
*/
|
|
658
693
|
createBufferSink() {
|
|
659
694
|
if (!this.graph) {
|
|
@@ -670,9 +705,11 @@ export class FilterAPI {
|
|
|
670
705
|
}
|
|
671
706
|
}
|
|
672
707
|
/**
|
|
673
|
-
* Parse
|
|
708
|
+
* Parse filter description and build graph.
|
|
674
709
|
*
|
|
675
|
-
* @
|
|
710
|
+
* @param description - Filter description string
|
|
711
|
+
* @throws {Error} If parsing fails
|
|
712
|
+
* @throws {FFmpegError} If graph construction fails
|
|
676
713
|
*/
|
|
677
714
|
parseFilterDescription(description) {
|
|
678
715
|
if (!this.graph) {
|
|
@@ -707,14 +744,11 @@ export class FilterAPI {
|
|
|
707
744
|
outputs.free();
|
|
708
745
|
}
|
|
709
746
|
/**
|
|
710
|
-
* Check
|
|
711
|
-
*
|
|
712
|
-
* Validates that hardware context is provided when needed:
|
|
713
|
-
* - hwupload: Always requires hardware context
|
|
714
|
-
* - Hardware filters (AVFILTER_FLAG_HWDEVICE): Recommend hardware context
|
|
715
|
-
* - hwdownload: Warns if input is not hardware format
|
|
747
|
+
* Check hardware requirements for filters.
|
|
716
748
|
*
|
|
717
|
-
* @
|
|
749
|
+
* @param description - Filter description
|
|
750
|
+
* @param options - Filter options
|
|
751
|
+
* @throws {Error} If hardware requirements not met
|
|
718
752
|
*/
|
|
719
753
|
checkHardwareRequirements(description, options) {
|
|
720
754
|
if (this.config.type !== 'video') {
|
|
@@ -743,47 +777,23 @@ export class FilterAPI {
|
|
|
743
777
|
throw new Error(`Pixel Format '${this.config.pixelFormat}' is not hardware compatible`);
|
|
744
778
|
}
|
|
745
779
|
}
|
|
746
|
-
// // Check if this is hwupload - always needs hardware context
|
|
747
|
-
// if (filterName === 'hwupload' || filterName === 'hwupload_cuda') {
|
|
748
|
-
// if (!options.hardware) {
|
|
749
|
-
// throw new Error(`Filter '${filterName}' requires a hardware context`);
|
|
750
|
-
// }
|
|
751
|
-
// } else if (filterName === 'hwdownload') {
|
|
752
|
-
// // Check if this is hwdownload - warn if input is not hardware format
|
|
753
|
-
// if (this.config.type === 'video' && !avIsHardwarePixelFormat(this.config.pixelFormat)) {
|
|
754
|
-
// // prettier-ignore
|
|
755
|
-
// console.warn(
|
|
756
|
-
// `Warning: 'hwdownload' filter used with software input format (${this.config.pixelFormat}). ` +
|
|
757
|
-
// 'This will likely fail at runtime. hwdownload expects hardware frames as input. ' +
|
|
758
|
-
// 'Consider removing hwdownload from your filter chain or ensuring hardware input.',
|
|
759
|
-
// );
|
|
760
|
-
// }
|
|
761
|
-
// } else if ((lowLevelFilter.flags & AVFILTER_FLAG_HWDEVICE) !== 0) {
|
|
762
|
-
// // Check if this is a hardware filter
|
|
763
|
-
// if (!options.hardware) {
|
|
764
|
-
// // prettier-ignore
|
|
765
|
-
// console.warn(
|
|
766
|
-
// `Warning: Hardware filter '${filterName}' used without hardware context. ` +
|
|
767
|
-
// "This may work if hw_frames_ctx is propagated from input, but it's recommended " +
|
|
768
|
-
// 'to pass { hardware: HardwareContext } in filter options.',
|
|
769
|
-
// );
|
|
770
|
-
// }
|
|
771
|
-
// }
|
|
772
780
|
}
|
|
773
781
|
}
|
|
774
782
|
/**
|
|
775
|
-
* Dispose of
|
|
783
|
+
* Dispose of filter.
|
|
776
784
|
*
|
|
777
|
-
* Implements
|
|
785
|
+
* Implements Disposable interface for automatic cleanup.
|
|
778
786
|
* Equivalent to calling free().
|
|
779
787
|
*
|
|
780
788
|
* @example
|
|
781
789
|
* ```typescript
|
|
782
790
|
* {
|
|
783
|
-
* using filter = await
|
|
784
|
-
* //
|
|
785
|
-
* } // Automatically freed
|
|
791
|
+
* using filter = await FilterAPI.create('scale=640:480', info);
|
|
792
|
+
* // Use filter...
|
|
793
|
+
* } // Automatically freed
|
|
786
794
|
* ```
|
|
795
|
+
*
|
|
796
|
+
* @see {@link free} For manual cleanup
|
|
787
797
|
*/
|
|
788
798
|
[Symbol.dispose]() {
|
|
789
799
|
this.free();
|