node-av 1.1.0 → 1.3.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 -59
- package/dist/api/bitstream-filter.d.ts +183 -123
- package/dist/api/bitstream-filter.js +185 -127
- package/dist/api/bitstream-filter.js.map +1 -1
- package/dist/api/decoder.d.ts +282 -130
- package/dist/api/decoder.js +290 -142
- package/dist/api/decoder.js.map +1 -1
- package/dist/api/encoder.d.ts +249 -160
- package/dist/api/encoder.js +276 -207
- package/dist/api/encoder.js.map +1 -1
- package/dist/api/filter-presets.d.ts +1944 -96
- package/dist/api/filter-presets.js +2059 -105
- package/dist/api/filter-presets.js.map +1 -1
- package/dist/api/filter.d.ts +264 -200
- package/dist/api/filter.js +269 -231
- package/dist/api/filter.js.map +1 -1
- package/dist/api/hardware.d.ts +246 -117
- package/dist/api/hardware.js +440 -217
- 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 -55
- package/dist/api/io-stream.js +43 -40
- package/dist/api/io-stream.js.map +1 -1
- package/dist/api/media-input.d.ts +242 -139
- package/dist/api/media-input.js +205 -103
- package/dist/api/media-input.js.map +1 -1
- package/dist/api/media-output.d.ts +208 -126
- package/dist/api/media-output.js +212 -126
- package/dist/api/media-output.js.map +1 -1
- package/dist/api/pipeline.d.ts +361 -38
- package/dist/api/pipeline.js +255 -14
- package/dist/api/pipeline.js.map +1 -1
- package/dist/api/types.d.ts +26 -187
- package/dist/api/utilities/audio-sample.d.ts +0 -8
- package/dist/api/utilities/audio-sample.js +0 -8
- package/dist/api/utilities/audio-sample.js.map +1 -1
- package/dist/api/utilities/channel-layout.d.ts +0 -8
- package/dist/api/utilities/channel-layout.js +0 -8
- package/dist/api/utilities/channel-layout.js.map +1 -1
- package/dist/api/utilities/image.d.ts +0 -8
- package/dist/api/utilities/image.js +0 -8
- package/dist/api/utilities/image.js.map +1 -1
- package/dist/api/utilities/index.d.ts +3 -3
- package/dist/api/utilities/index.js +3 -3
- package/dist/api/utilities/index.js.map +1 -1
- package/dist/api/utilities/media-type.d.ts +1 -9
- package/dist/api/utilities/media-type.js +1 -9
- package/dist/api/utilities/media-type.js.map +1 -1
- package/dist/api/utilities/pixel-format.d.ts +1 -9
- package/dist/api/utilities/pixel-format.js +1 -9
- package/dist/api/utilities/pixel-format.js.map +1 -1
- package/dist/api/utilities/sample-format.d.ts +1 -9
- package/dist/api/utilities/sample-format.js +1 -9
- package/dist/api/utilities/sample-format.js.map +1 -1
- package/dist/api/utilities/streaming.d.ts +0 -8
- package/dist/api/utilities/streaming.js +0 -8
- package/dist/api/utilities/streaming.js.map +1 -1
- package/dist/api/utilities/timestamp.d.ts +0 -8
- package/dist/api/utilities/timestamp.js +0 -8
- package/dist/api/utilities/timestamp.js.map +1 -1
- package/dist/api/utils.d.ts +1 -2
- package/dist/api/utils.js +11 -0
- package/dist/api/utils.js.map +1 -1
- package/dist/constants/constants.d.ts +1 -1
- package/dist/constants/constants.js +2 -0
- package/dist/constants/constants.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.d.ts +1 -0
- package/dist/lib/binding.js +7 -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 +266 -283
- package/dist/lib/codec.js +270 -287
- 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 +3 -2
- package/dist/lib/index.js +3 -3
- 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 +118 -106
- package/dist/lib/native-types.js +0 -7
- package/dist/lib/native-types.js.map +1 -1
- package/dist/lib/option.d.ts +437 -218
- package/dist/lib/option.js +462 -226
- 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 +435 -181
- package/dist/lib/utilities.js +438 -182
- package/dist/lib/utilities.js.map +1 -1
- package/install/check.js +0 -1
- package/install/ffmpeg.js +0 -11
- package/package.json +25 -18
- package/release_notes.md +24 -59
- package/CHANGELOG.md +0 -8
package/dist/api/filter.js
CHANGED
|
@@ -1,67 +1,42 @@
|
|
|
1
|
+
import { AVERROR_EAGAIN, AVERROR_EOF, AVFILTER_FLAG_HWDEVICE, AVMEDIA_TYPE_AUDIO, AVMEDIA_TYPE_VIDEO } from '../constants/constants.js';
|
|
2
|
+
import { avGetSampleFmtName, avIsHardwarePixelFormat, FFmpegError, Filter, FilterGraph, FilterInOut, Frame } from '../lib/index.js';
|
|
1
3
|
/**
|
|
2
|
-
*
|
|
4
|
+
* High-level filter API for audio and video processing.
|
|
3
5
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
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
|
-
import { AVERROR_EOF, AVFILTER_FLAG_HWDEVICE, AVMEDIA_TYPE_AUDIO, AVMEDIA_TYPE_VIDEO } from '../constants/constants.js';
|
|
14
|
-
import { AVERROR_EAGAIN, avGetSampleFmtName, avIsHardwarePixelFormat, FFmpegError, Filter, FilterGraph, FilterInOut, Frame } from '../lib/index.js';
|
|
15
|
-
/**
|
|
16
|
-
* High-level filter API for media processing.
|
|
17
|
-
*
|
|
18
|
-
* Provides a simplified interface for FFmpeg's filter system.
|
|
19
|
-
* Supports both simple filter chains and complex filter graphs.
|
|
20
|
-
* Handles automatic format negotiation and buffer management.
|
|
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
|
|
13
|
+
* import { FilterAPI } from 'node-av/api';
|
|
29
14
|
*
|
|
30
|
-
* //
|
|
31
|
-
* const
|
|
32
|
-
* const filter = await FilterAPI.create('scale=1280:720,format=yuv420p', videoStream);
|
|
15
|
+
* // Create video filter
|
|
16
|
+
* const filter = await FilterAPI.create('scale=1280:720', videoInfo);
|
|
33
17
|
*
|
|
34
|
-
* // Process
|
|
35
|
-
* const
|
|
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
|
+
* }
|
|
36
24
|
* ```
|
|
37
25
|
*
|
|
38
26
|
* @example
|
|
39
27
|
* ```typescript
|
|
40
|
-
* // Hardware
|
|
41
|
-
* const hw =
|
|
42
|
-
* const
|
|
43
|
-
*
|
|
44
|
-
*
|
|
45
|
-
* }
|
|
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
|
+
* );
|
|
46
35
|
* ```
|
|
47
36
|
*
|
|
48
|
-
* @
|
|
49
|
-
*
|
|
50
|
-
*
|
|
51
|
-
* const decoder = await Decoder.create(stream);
|
|
52
|
-
* const hw = await HardwareContext.auto();
|
|
53
|
-
* const filter = await FilterAPI.create('format=nv12,hwupload', decoder.getOutputStreamInfo(), {
|
|
54
|
-
* hardware: hw // Required for hwupload to create hw_frames_ctx
|
|
55
|
-
* });
|
|
56
|
-
* ```
|
|
57
|
-
*
|
|
58
|
-
* @example
|
|
59
|
-
* ```typescript
|
|
60
|
-
* // Hardware decode -> software encode pipeline with hwdownload
|
|
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,49 @@ export class FilterAPI {
|
|
|
89
62
|
this.mediaType = config.type === 'video' ? AVMEDIA_TYPE_VIDEO : AVMEDIA_TYPE_AUDIO;
|
|
90
63
|
}
|
|
91
64
|
/**
|
|
92
|
-
* Create a filter
|
|
93
|
-
*
|
|
94
|
-
* Accepts either a Stream (from MediaInput/Decoder) or StreamInfo (for raw data).
|
|
95
|
-
* Automatically sets up buffer source and sink filters.
|
|
65
|
+
* Create a filter with specified description and configuration.
|
|
96
66
|
*
|
|
97
|
-
*
|
|
98
|
-
*
|
|
99
|
-
* For
|
|
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.
|
|
100
70
|
*
|
|
101
|
-
*
|
|
102
|
-
* - hwupload: Requires hardware context, creates its own hw_frames_ctx
|
|
103
|
-
* - hwdownload: Uses hw_frames_ctx propagated from previous filters
|
|
104
|
-
* - Other HW filters: Use propagated hw_frames_ctx or hwupload's output
|
|
71
|
+
* Direct mapping to avfilter_graph_parse_ptr() and avfilter_graph_config().
|
|
105
72
|
*
|
|
106
|
-
* @param description - Filter graph description
|
|
107
|
-
* @param input -
|
|
108
|
-
* @param options -
|
|
73
|
+
* @param description - Filter graph description
|
|
74
|
+
* @param input - Input stream configuration
|
|
75
|
+
* @param options - Filter options
|
|
76
|
+
* @returns Configured filter instance
|
|
109
77
|
*
|
|
110
|
-
* @
|
|
78
|
+
* @throws {Error} If filter creation or configuration fails
|
|
111
79
|
*
|
|
112
|
-
* @throws {FFmpegError} If
|
|
113
|
-
* @throws {Error} If hardware filter requires hardware context but none provided
|
|
80
|
+
* @throws {FFmpegError} If graph parsing or config fails
|
|
114
81
|
*
|
|
115
82
|
* @example
|
|
116
83
|
* ```typescript
|
|
117
|
-
* // Simple filter
|
|
118
|
-
* const filter = await FilterAPI.create('scale=640:480',
|
|
84
|
+
* // Simple video filter
|
|
85
|
+
* const filter = await FilterAPI.create('scale=640:480', videoInfo);
|
|
86
|
+
* ```
|
|
119
87
|
*
|
|
120
|
-
*
|
|
121
|
-
*
|
|
122
|
-
*
|
|
88
|
+
* @example
|
|
89
|
+
* ```typescript
|
|
90
|
+
* // Complex filter chain
|
|
123
91
|
* const filter = await FilterAPI.create(
|
|
124
|
-
* '
|
|
125
|
-
*
|
|
126
|
-
* { hardware: hw }
|
|
92
|
+
* 'crop=640:480:0:0,rotate=PI/4',
|
|
93
|
+
* videoInfo
|
|
127
94
|
* );
|
|
95
|
+
* ```
|
|
128
96
|
*
|
|
129
|
-
*
|
|
130
|
-
*
|
|
131
|
-
*
|
|
132
|
-
*
|
|
133
|
-
*
|
|
134
|
-
*
|
|
135
|
-
*
|
|
136
|
-
* });
|
|
97
|
+
* @example
|
|
98
|
+
* ```typescript
|
|
99
|
+
* // Audio filter
|
|
100
|
+
* const filter = await FilterAPI.create(
|
|
101
|
+
* 'volume=0.5,aecho=0.8:0.9:1000:0.3',
|
|
102
|
+
* audioInfo
|
|
103
|
+
* );
|
|
137
104
|
* ```
|
|
105
|
+
*
|
|
106
|
+
* @see {@link process} For frame processing
|
|
107
|
+
* @see {@link FilterOptions} For configuration options
|
|
138
108
|
*/
|
|
139
109
|
static async create(description, input, options = {}) {
|
|
140
110
|
let config;
|
|
@@ -172,29 +142,45 @@ export class FilterAPI {
|
|
|
172
142
|
return filter;
|
|
173
143
|
}
|
|
174
144
|
/**
|
|
175
|
-
* Process a
|
|
145
|
+
* Process a frame through the filter.
|
|
176
146
|
*
|
|
177
|
-
*
|
|
178
|
-
* May
|
|
147
|
+
* Applies filter operations to input frame.
|
|
148
|
+
* May buffer frames internally before producing output.
|
|
149
|
+
* For video, performs lazy initialization on first frame.
|
|
179
150
|
*
|
|
180
|
-
*
|
|
181
|
-
* Subsequent frames are processed normally. FFmpeg automatically propagates
|
|
182
|
-
* hw_frames_ctx through the filter chain.
|
|
151
|
+
* Direct mapping to av_buffersrc_add_frame() and av_buffersink_get_frame().
|
|
183
152
|
*
|
|
184
|
-
* @param frame - Input frame to
|
|
153
|
+
* @param frame - Input frame to process
|
|
154
|
+
* @returns Filtered frame or null if buffered
|
|
185
155
|
*
|
|
186
|
-
* @
|
|
156
|
+
* @throws {Error} If filter not ready
|
|
187
157
|
*
|
|
188
158
|
* @throws {FFmpegError} If processing fails
|
|
189
|
-
* @throws {Error} If filter not initialized or hardware frame required but not provided
|
|
190
159
|
*
|
|
191
160
|
* @example
|
|
192
161
|
* ```typescript
|
|
193
|
-
* const
|
|
194
|
-
* if (
|
|
195
|
-
*
|
|
162
|
+
* const output = await filter.process(inputFrame);
|
|
163
|
+
* if (output) {
|
|
164
|
+
* console.log(`Got filtered frame: pts=${output.pts}`);
|
|
165
|
+
* output.free();
|
|
166
|
+
* }
|
|
167
|
+
* ```
|
|
168
|
+
*
|
|
169
|
+
* @example
|
|
170
|
+
* ```typescript
|
|
171
|
+
* // Process and drain
|
|
172
|
+
* const output = await filter.process(frame);
|
|
173
|
+
* if (output) yield output;
|
|
174
|
+
*
|
|
175
|
+
* // Drain buffered frames
|
|
176
|
+
* let buffered;
|
|
177
|
+
* while ((buffered = await filter.receive()) !== null) {
|
|
178
|
+
* yield buffered;
|
|
196
179
|
* }
|
|
197
180
|
* ```
|
|
181
|
+
*
|
|
182
|
+
* @see {@link receive} For draining buffered frames
|
|
183
|
+
* @see {@link frames} For stream processing
|
|
198
184
|
*/
|
|
199
185
|
async process(frame) {
|
|
200
186
|
// Lazy initialization for video filters (detect hardware from first frame)
|
|
@@ -214,7 +200,7 @@ export class FilterAPI {
|
|
|
214
200
|
if (getRet >= 0) {
|
|
215
201
|
return outputFrame;
|
|
216
202
|
}
|
|
217
|
-
else if (
|
|
203
|
+
else if (getRet === AVERROR_EAGAIN) {
|
|
218
204
|
// Need more input
|
|
219
205
|
outputFrame.free();
|
|
220
206
|
return null;
|
|
@@ -226,21 +212,28 @@ export class FilterAPI {
|
|
|
226
212
|
}
|
|
227
213
|
}
|
|
228
214
|
/**
|
|
229
|
-
* Process multiple frames
|
|
215
|
+
* Process multiple frames at once.
|
|
230
216
|
*
|
|
231
|
-
*
|
|
232
|
-
*
|
|
217
|
+
* Processes batch of frames and drains all output.
|
|
218
|
+
* Useful for filters that buffer multiple frames.
|
|
233
219
|
*
|
|
234
220
|
* @param frames - Array of input frames
|
|
221
|
+
* @returns Array of all output frames
|
|
235
222
|
*
|
|
236
|
-
* @
|
|
223
|
+
* @throws {Error} If filter not ready
|
|
237
224
|
*
|
|
238
225
|
* @throws {FFmpegError} If processing fails
|
|
239
226
|
*
|
|
240
227
|
* @example
|
|
241
228
|
* ```typescript
|
|
242
|
-
* const
|
|
229
|
+
* const outputs = await filter.processMultiple([frame1, frame2, frame3]);
|
|
230
|
+
* for (const output of outputs) {
|
|
231
|
+
* console.log(`Output frame: pts=${output.pts}`);
|
|
232
|
+
* output.free();
|
|
233
|
+
* }
|
|
243
234
|
* ```
|
|
235
|
+
*
|
|
236
|
+
* @see {@link process} For single frame processing
|
|
244
237
|
*/
|
|
245
238
|
async processMultiple(frames) {
|
|
246
239
|
const outputFrames = [];
|
|
@@ -260,24 +253,31 @@ export class FilterAPI {
|
|
|
260
253
|
return outputFrames;
|
|
261
254
|
}
|
|
262
255
|
/**
|
|
263
|
-
* Receive
|
|
256
|
+
* Receive buffered frame from filter.
|
|
257
|
+
*
|
|
258
|
+
* Drains frames buffered by the filter.
|
|
259
|
+
* Call repeatedly until null to get all buffered frames.
|
|
260
|
+
*
|
|
261
|
+
* Direct mapping to av_buffersink_get_frame().
|
|
264
262
|
*
|
|
265
|
-
*
|
|
266
|
-
* Returns null when no more frames are available.
|
|
263
|
+
* @returns Buffered frame or null if none available
|
|
267
264
|
*
|
|
268
|
-
* @
|
|
265
|
+
* @throws {Error} If filter not ready
|
|
269
266
|
*
|
|
270
|
-
* @throws {FFmpegError} If
|
|
267
|
+
* @throws {FFmpegError} If receive fails
|
|
271
268
|
*
|
|
272
269
|
* @example
|
|
273
270
|
* ```typescript
|
|
274
|
-
* // Drain
|
|
275
|
-
*
|
|
276
|
-
*
|
|
277
|
-
*
|
|
278
|
-
*
|
|
271
|
+
* // Drain buffered frames
|
|
272
|
+
* let frame;
|
|
273
|
+
* while ((frame = await filter.receive()) !== null) {
|
|
274
|
+
* console.log(`Buffered frame: pts=${frame.pts}`);
|
|
275
|
+
* frame.free();
|
|
279
276
|
* }
|
|
280
277
|
* ```
|
|
278
|
+
*
|
|
279
|
+
* @see {@link process} For input processing
|
|
280
|
+
* @see {@link flush} For end-of-stream
|
|
281
281
|
*/
|
|
282
282
|
async receive() {
|
|
283
283
|
if (!this.initialized || !this.buffersinkCtx) {
|
|
@@ -291,7 +291,7 @@ export class FilterAPI {
|
|
|
291
291
|
}
|
|
292
292
|
else {
|
|
293
293
|
frame.free();
|
|
294
|
-
if (
|
|
294
|
+
if (ret === AVERROR_EAGAIN || ret === AVERROR_EOF) {
|
|
295
295
|
return null;
|
|
296
296
|
}
|
|
297
297
|
FFmpegError.throwIfError(ret, 'Failed to receive frame from filter');
|
|
@@ -299,12 +299,14 @@ export class FilterAPI {
|
|
|
299
299
|
}
|
|
300
300
|
}
|
|
301
301
|
/**
|
|
302
|
-
* Flush
|
|
302
|
+
* Flush filter and signal end-of-stream.
|
|
303
|
+
*
|
|
304
|
+
* Sends null frame to flush buffered data.
|
|
305
|
+
* Must call receive() to get flushed frames.
|
|
303
306
|
*
|
|
304
|
-
*
|
|
305
|
-
* Use receive() to get any remaining frames.
|
|
307
|
+
* Direct mapping to av_buffersrc_add_frame(NULL).
|
|
306
308
|
*
|
|
307
|
-
* @
|
|
309
|
+
* @throws {Error} If filter not ready
|
|
308
310
|
*
|
|
309
311
|
* @throws {FFmpegError} If flush fails
|
|
310
312
|
*
|
|
@@ -312,40 +314,45 @@ export class FilterAPI {
|
|
|
312
314
|
* ```typescript
|
|
313
315
|
* await filter.flush();
|
|
314
316
|
* // Get remaining frames
|
|
315
|
-
*
|
|
316
|
-
*
|
|
317
|
-
*
|
|
318
|
-
* // Process final frames
|
|
317
|
+
* let frame;
|
|
318
|
+
* while ((frame = await filter.receive()) !== null) {
|
|
319
|
+
* frame.free();
|
|
319
320
|
* }
|
|
320
321
|
* ```
|
|
322
|
+
*
|
|
323
|
+
* @see {@link flushFrames} For async iteration
|
|
324
|
+
* @see {@link receive} For draining frames
|
|
321
325
|
*/
|
|
322
326
|
async flush() {
|
|
323
327
|
if (!this.initialized || !this.buffersrcCtx) {
|
|
324
328
|
throw new Error('Filter not initialized');
|
|
325
329
|
}
|
|
326
330
|
const ret = await this.buffersrcCtx.buffersrcAddFrame(null);
|
|
327
|
-
if (ret < 0 &&
|
|
331
|
+
if (ret < 0 && ret !== AVERROR_EOF) {
|
|
328
332
|
FFmpegError.throwIfError(ret, 'Failed to flush filter');
|
|
329
333
|
}
|
|
330
334
|
}
|
|
331
335
|
/**
|
|
332
|
-
* Flush filter and yield
|
|
336
|
+
* Flush filter and yield remaining frames.
|
|
333
337
|
*
|
|
334
|
-
*
|
|
335
|
-
*
|
|
338
|
+
* Convenient async generator for flushing.
|
|
339
|
+
* Combines flush and receive operations.
|
|
336
340
|
*
|
|
337
|
-
* @
|
|
341
|
+
* @yields Remaining frames from filter
|
|
342
|
+
* @throws {Error} If filter not ready
|
|
338
343
|
*
|
|
339
|
-
* @throws {
|
|
344
|
+
* @throws {FFmpegError} If flush fails
|
|
340
345
|
*
|
|
341
346
|
* @example
|
|
342
347
|
* ```typescript
|
|
343
|
-
* // Process all remaining frames with generator
|
|
344
348
|
* for await (const frame of filter.flushFrames()) {
|
|
345
|
-
*
|
|
346
|
-
*
|
|
349
|
+
* console.log(`Flushed frame: pts=${frame.pts}`);
|
|
350
|
+
* frame.free();
|
|
347
351
|
* }
|
|
348
352
|
* ```
|
|
353
|
+
*
|
|
354
|
+
* @see {@link flush} For manual flush
|
|
355
|
+
* @see {@link frames} For complete pipeline
|
|
349
356
|
*/
|
|
350
357
|
async *flushFrames() {
|
|
351
358
|
if (!this.initialized || !this.buffersrcCtx) {
|
|
@@ -360,26 +367,41 @@ export class FilterAPI {
|
|
|
360
367
|
}
|
|
361
368
|
}
|
|
362
369
|
/**
|
|
363
|
-
* Process
|
|
370
|
+
* Process frame stream through filter.
|
|
364
371
|
*
|
|
365
|
-
*
|
|
366
|
-
* Automatically handles buffering and
|
|
367
|
-
*
|
|
372
|
+
* High-level async generator for filtering frame streams.
|
|
373
|
+
* Automatically handles buffering and flushing.
|
|
374
|
+
* Frees input frames after processing.
|
|
368
375
|
*
|
|
369
|
-
*
|
|
370
|
-
*
|
|
376
|
+
* @param frames - Async generator of input frames
|
|
377
|
+
* @yields Filtered frames
|
|
378
|
+
* @throws {Error} If filter not ready
|
|
371
379
|
*
|
|
372
|
-
* @
|
|
380
|
+
* @throws {FFmpegError} If processing fails
|
|
373
381
|
*
|
|
374
|
-
* @
|
|
382
|
+
* @example
|
|
383
|
+
* ```typescript
|
|
384
|
+
* // Filter decoded frames
|
|
385
|
+
* for await (const frame of filter.frames(decoder.frames(packets))) {
|
|
386
|
+
* await encoder.encode(frame);
|
|
387
|
+
* frame.free();
|
|
388
|
+
* }
|
|
389
|
+
* ```
|
|
375
390
|
*
|
|
376
391
|
* @example
|
|
377
392
|
* ```typescript
|
|
378
|
-
*
|
|
379
|
-
*
|
|
380
|
-
*
|
|
393
|
+
* // Chain filters
|
|
394
|
+
* const filter1 = await FilterAPI.create('scale=640:480', info);
|
|
395
|
+
* const filter2 = await FilterAPI.create('rotate=PI/4', info);
|
|
396
|
+
*
|
|
397
|
+
* for await (const frame of filter2.frames(filter1.frames(input))) {
|
|
398
|
+
* // Process filtered frames
|
|
399
|
+
* frame.free();
|
|
381
400
|
* }
|
|
382
401
|
* ```
|
|
402
|
+
*
|
|
403
|
+
* @see {@link process} For single frame processing
|
|
404
|
+
* @see {@link flush} For end-of-stream handling
|
|
383
405
|
*/
|
|
384
406
|
async *frames(frames) {
|
|
385
407
|
for await (const frame of frames) {
|
|
@@ -412,32 +434,31 @@ export class FilterAPI {
|
|
|
412
434
|
}
|
|
413
435
|
}
|
|
414
436
|
/**
|
|
415
|
-
* Send
|
|
437
|
+
* Send command to filter.
|
|
438
|
+
*
|
|
439
|
+
* Sends runtime command to specific filter in graph.
|
|
440
|
+
* Allows dynamic parameter adjustment.
|
|
416
441
|
*
|
|
417
|
-
*
|
|
418
|
-
* Not all filters support commands - check filter documentation.
|
|
442
|
+
* Direct mapping to avfilter_graph_send_command().
|
|
419
443
|
*
|
|
420
|
-
* @param target -
|
|
421
|
-
* @param cmd - Command name
|
|
422
|
-
* @param arg - Command argument
|
|
423
|
-
* @param flags -
|
|
444
|
+
* @param target - Target filter name
|
|
445
|
+
* @param cmd - Command name
|
|
446
|
+
* @param arg - Command argument
|
|
447
|
+
* @param flags - Command flags
|
|
448
|
+
* @returns Response string from filter
|
|
424
449
|
*
|
|
425
|
-
* @
|
|
450
|
+
* @throws {Error} If filter not ready
|
|
451
|
+
*
|
|
452
|
+
* @throws {FFmpegError} If command fails
|
|
426
453
|
*
|
|
427
454
|
* @example
|
|
428
455
|
* ```typescript
|
|
429
|
-
* // Change volume
|
|
456
|
+
* // Change volume at runtime
|
|
430
457
|
* const response = filter.sendCommand('volume', 'volume', '0.5');
|
|
431
|
-
*
|
|
432
|
-
* console.log('Volume changed successfully');
|
|
433
|
-
* }
|
|
458
|
+
* console.log(`Volume changed: ${response}`);
|
|
434
459
|
* ```
|
|
435
460
|
*
|
|
436
|
-
* @
|
|
437
|
-
* ```typescript
|
|
438
|
-
* // Enable/disable all filters at runtime
|
|
439
|
-
* filter.sendCommand('all', 'enable', 'expr=gte(t,10)');
|
|
440
|
-
* ```
|
|
461
|
+
* @see {@link queueCommand} For delayed commands
|
|
441
462
|
*/
|
|
442
463
|
sendCommand(target, cmd, arg, flags) {
|
|
443
464
|
if (!this.initialized || !this.graph) {
|
|
@@ -450,24 +471,29 @@ export class FilterAPI {
|
|
|
450
471
|
return result.response;
|
|
451
472
|
}
|
|
452
473
|
/**
|
|
453
|
-
* Queue
|
|
474
|
+
* Queue command for later execution.
|
|
475
|
+
*
|
|
476
|
+
* Schedules command to execute at specific timestamp.
|
|
477
|
+
* Useful for synchronized parameter changes.
|
|
478
|
+
*
|
|
479
|
+
* Direct mapping to avfilter_graph_queue_command().
|
|
454
480
|
*
|
|
455
|
-
*
|
|
456
|
-
*
|
|
481
|
+
* @param target - Target filter name
|
|
482
|
+
* @param cmd - Command name
|
|
483
|
+
* @param arg - Command argument
|
|
484
|
+
* @param ts - Timestamp for execution
|
|
485
|
+
* @param flags - Command flags
|
|
486
|
+
* @throws {Error} If filter not ready
|
|
457
487
|
*
|
|
458
|
-
* @
|
|
459
|
-
* @param cmd - Command name (e.g., "volume", "hue", "brightness")
|
|
460
|
-
* @param arg - Command argument value
|
|
461
|
-
* @param ts - Timestamp when command should execute (in seconds)
|
|
462
|
-
* @param flags - Optional command flags
|
|
488
|
+
* @throws {FFmpegError} If queue fails
|
|
463
489
|
*
|
|
464
490
|
* @example
|
|
465
491
|
* ```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
|
|
492
|
+
* // Queue volume change at 10 seconds
|
|
493
|
+
* filter.queueCommand('volume', 'volume', '0.8', 10.0);
|
|
470
494
|
* ```
|
|
495
|
+
*
|
|
496
|
+
* @see {@link sendCommand} For immediate commands
|
|
471
497
|
*/
|
|
472
498
|
queueCommand(target, cmd, arg, ts, flags) {
|
|
473
499
|
if (!this.initialized || !this.graph) {
|
|
@@ -477,17 +503,19 @@ export class FilterAPI {
|
|
|
477
503
|
FFmpegError.throwIfError(ret, 'Failed to queue filter command');
|
|
478
504
|
}
|
|
479
505
|
/**
|
|
480
|
-
* Get
|
|
506
|
+
* Get filter graph description.
|
|
507
|
+
*
|
|
508
|
+
* Returns human-readable graph structure.
|
|
509
|
+
* Useful for debugging filter chains.
|
|
481
510
|
*
|
|
482
|
-
*
|
|
483
|
-
* Useful for debugging and visualization.
|
|
511
|
+
* Direct mapping to avfilter_graph_dump().
|
|
484
512
|
*
|
|
485
513
|
* @returns Graph description or null if not initialized
|
|
486
514
|
*
|
|
487
515
|
* @example
|
|
488
516
|
* ```typescript
|
|
489
517
|
* const description = filter.getGraphDescription();
|
|
490
|
-
* console.log(description);
|
|
518
|
+
* console.log('Filter graph:', description);
|
|
491
519
|
* ```
|
|
492
520
|
*/
|
|
493
521
|
getGraphDescription() {
|
|
@@ -497,32 +525,47 @@ export class FilterAPI {
|
|
|
497
525
|
return this.graph.dump();
|
|
498
526
|
}
|
|
499
527
|
/**
|
|
500
|
-
* Check if
|
|
528
|
+
* Check if filter is ready for processing.
|
|
529
|
+
*
|
|
530
|
+
* @returns true if initialized and ready
|
|
501
531
|
*
|
|
502
|
-
* @
|
|
532
|
+
* @example
|
|
533
|
+
* ```typescript
|
|
534
|
+
* if (filter.isReady()) {
|
|
535
|
+
* const output = await filter.process(frame);
|
|
536
|
+
* }
|
|
537
|
+
* ```
|
|
503
538
|
*/
|
|
504
539
|
isReady() {
|
|
505
540
|
return this.initialized && this.buffersrcCtx !== null && this.buffersinkCtx !== null;
|
|
506
541
|
}
|
|
507
542
|
/**
|
|
508
|
-
* Get
|
|
543
|
+
* Get media type of filter.
|
|
544
|
+
*
|
|
545
|
+
* @returns AVMEDIA_TYPE_VIDEO or AVMEDIA_TYPE_AUDIO
|
|
509
546
|
*
|
|
510
|
-
* @
|
|
547
|
+
* @example
|
|
548
|
+
* ```typescript
|
|
549
|
+
* if (filter.getMediaType() === AVMEDIA_TYPE_VIDEO) {
|
|
550
|
+
* console.log('Video filter');
|
|
551
|
+
* }
|
|
552
|
+
* ```
|
|
511
553
|
*/
|
|
512
554
|
getMediaType() {
|
|
513
555
|
return this.mediaType;
|
|
514
556
|
}
|
|
515
557
|
/**
|
|
516
|
-
* Free
|
|
558
|
+
* Free filter resources.
|
|
517
559
|
*
|
|
518
|
-
* Releases
|
|
519
|
-
*
|
|
560
|
+
* Releases filter graph and contexts.
|
|
561
|
+
* Safe to call multiple times.
|
|
520
562
|
*
|
|
521
563
|
* @example
|
|
522
564
|
* ```typescript
|
|
523
565
|
* filter.free();
|
|
524
|
-
* // filter is now invalid
|
|
525
566
|
* ```
|
|
567
|
+
*
|
|
568
|
+
* @see {@link Symbol.dispose} For automatic cleanup
|
|
526
569
|
*/
|
|
527
570
|
free() {
|
|
528
571
|
if (this.graph) {
|
|
@@ -534,13 +577,16 @@ export class FilterAPI {
|
|
|
534
577
|
this.initialized = false;
|
|
535
578
|
}
|
|
536
579
|
/**
|
|
537
|
-
* Initialize
|
|
580
|
+
* Initialize filter graph.
|
|
581
|
+
*
|
|
582
|
+
* Creates and configures filter graph components.
|
|
583
|
+
* For video, may use hardware frames context from first frame.
|
|
584
|
+
*
|
|
585
|
+
* @param firstFrame - First frame for hardware detection (video only)
|
|
538
586
|
*
|
|
539
|
-
*
|
|
540
|
-
* Configures the graph for processing.
|
|
587
|
+
* @throws {Error} If initialization fails
|
|
541
588
|
*
|
|
542
|
-
*
|
|
543
|
-
* For software inputs: Initializes without hw_frames_ctx
|
|
589
|
+
* @throws {FFmpegError} If configuration fails
|
|
544
590
|
*
|
|
545
591
|
* @internal
|
|
546
592
|
*/
|
|
@@ -587,6 +633,12 @@ export class FilterAPI {
|
|
|
587
633
|
/**
|
|
588
634
|
* Create buffer source with hardware frames context.
|
|
589
635
|
*
|
|
636
|
+
* @param frame - Frame with hw_frames_ctx
|
|
637
|
+
*
|
|
638
|
+
* @throws {Error} If creation fails
|
|
639
|
+
*
|
|
640
|
+
* @throws {FFmpegError} If configuration fails
|
|
641
|
+
*
|
|
590
642
|
* @internal
|
|
591
643
|
*/
|
|
592
644
|
createBufferSourceWithHwFrames(frame) {
|
|
@@ -617,7 +669,9 @@ export class FilterAPI {
|
|
|
617
669
|
FFmpegError.throwIfError(initRet, 'Failed to initialize buffer source');
|
|
618
670
|
}
|
|
619
671
|
/**
|
|
620
|
-
* Create
|
|
672
|
+
* Create standard buffer source.
|
|
673
|
+
*
|
|
674
|
+
* @throws {Error} If creation fails
|
|
621
675
|
*
|
|
622
676
|
* @internal
|
|
623
677
|
*/
|
|
@@ -651,7 +705,9 @@ export class FilterAPI {
|
|
|
651
705
|
}
|
|
652
706
|
}
|
|
653
707
|
/**
|
|
654
|
-
* Create
|
|
708
|
+
* Create buffer sink.
|
|
709
|
+
*
|
|
710
|
+
* @throws {Error} If creation fails
|
|
655
711
|
*
|
|
656
712
|
* @internal
|
|
657
713
|
*/
|
|
@@ -670,7 +726,13 @@ export class FilterAPI {
|
|
|
670
726
|
}
|
|
671
727
|
}
|
|
672
728
|
/**
|
|
673
|
-
* Parse
|
|
729
|
+
* Parse filter description and build graph.
|
|
730
|
+
*
|
|
731
|
+
* @param description - Filter description string
|
|
732
|
+
*
|
|
733
|
+
* @throws {Error} If parsing fails
|
|
734
|
+
*
|
|
735
|
+
* @throws {FFmpegError} If graph construction fails
|
|
674
736
|
*
|
|
675
737
|
* @internal
|
|
676
738
|
*/
|
|
@@ -707,12 +769,12 @@ export class FilterAPI {
|
|
|
707
769
|
outputs.free();
|
|
708
770
|
}
|
|
709
771
|
/**
|
|
710
|
-
* Check
|
|
772
|
+
* Check hardware requirements for filters.
|
|
711
773
|
*
|
|
712
|
-
*
|
|
713
|
-
*
|
|
714
|
-
*
|
|
715
|
-
*
|
|
774
|
+
* @param description - Filter description
|
|
775
|
+
* @param options - Filter options
|
|
776
|
+
*
|
|
777
|
+
* @throws {Error} If hardware requirements not met
|
|
716
778
|
*
|
|
717
779
|
* @internal
|
|
718
780
|
*/
|
|
@@ -743,47 +805,23 @@ export class FilterAPI {
|
|
|
743
805
|
throw new Error(`Pixel Format '${this.config.pixelFormat}' is not hardware compatible`);
|
|
744
806
|
}
|
|
745
807
|
}
|
|
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
808
|
}
|
|
773
809
|
}
|
|
774
810
|
/**
|
|
775
|
-
* Dispose of
|
|
811
|
+
* Dispose of filter.
|
|
776
812
|
*
|
|
777
|
-
* Implements
|
|
813
|
+
* Implements Disposable interface for automatic cleanup.
|
|
778
814
|
* Equivalent to calling free().
|
|
779
815
|
*
|
|
780
816
|
* @example
|
|
781
817
|
* ```typescript
|
|
782
818
|
* {
|
|
783
|
-
* using filter = await
|
|
784
|
-
* //
|
|
785
|
-
* } // Automatically freed
|
|
819
|
+
* using filter = await FilterAPI.create('scale=640:480', info);
|
|
820
|
+
* // Use filter...
|
|
821
|
+
* } // Automatically freed
|
|
786
822
|
* ```
|
|
823
|
+
*
|
|
824
|
+
* @see {@link free} For manual cleanup
|
|
787
825
|
*/
|
|
788
826
|
[Symbol.dispose]() {
|
|
789
827
|
this.free();
|