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.
Files changed (166) hide show
  1. package/README.md +51 -59
  2. package/dist/api/bitstream-filter.d.ts +183 -123
  3. package/dist/api/bitstream-filter.js +185 -127
  4. package/dist/api/bitstream-filter.js.map +1 -1
  5. package/dist/api/decoder.d.ts +282 -130
  6. package/dist/api/decoder.js +290 -142
  7. package/dist/api/decoder.js.map +1 -1
  8. package/dist/api/encoder.d.ts +249 -160
  9. package/dist/api/encoder.js +276 -207
  10. package/dist/api/encoder.js.map +1 -1
  11. package/dist/api/filter-presets.d.ts +1944 -96
  12. package/dist/api/filter-presets.js +2059 -105
  13. package/dist/api/filter-presets.js.map +1 -1
  14. package/dist/api/filter.d.ts +264 -200
  15. package/dist/api/filter.js +269 -231
  16. package/dist/api/filter.js.map +1 -1
  17. package/dist/api/hardware.d.ts +246 -117
  18. package/dist/api/hardware.js +440 -217
  19. package/dist/api/hardware.js.map +1 -1
  20. package/dist/api/index.d.ts +3 -3
  21. package/dist/api/index.js +1 -1
  22. package/dist/api/index.js.map +1 -1
  23. package/dist/api/io-stream.d.ts +65 -55
  24. package/dist/api/io-stream.js +43 -40
  25. package/dist/api/io-stream.js.map +1 -1
  26. package/dist/api/media-input.d.ts +242 -139
  27. package/dist/api/media-input.js +205 -103
  28. package/dist/api/media-input.js.map +1 -1
  29. package/dist/api/media-output.d.ts +208 -126
  30. package/dist/api/media-output.js +212 -126
  31. package/dist/api/media-output.js.map +1 -1
  32. package/dist/api/pipeline.d.ts +361 -38
  33. package/dist/api/pipeline.js +255 -14
  34. package/dist/api/pipeline.js.map +1 -1
  35. package/dist/api/types.d.ts +26 -187
  36. package/dist/api/utilities/audio-sample.d.ts +0 -8
  37. package/dist/api/utilities/audio-sample.js +0 -8
  38. package/dist/api/utilities/audio-sample.js.map +1 -1
  39. package/dist/api/utilities/channel-layout.d.ts +0 -8
  40. package/dist/api/utilities/channel-layout.js +0 -8
  41. package/dist/api/utilities/channel-layout.js.map +1 -1
  42. package/dist/api/utilities/image.d.ts +0 -8
  43. package/dist/api/utilities/image.js +0 -8
  44. package/dist/api/utilities/image.js.map +1 -1
  45. package/dist/api/utilities/index.d.ts +3 -3
  46. package/dist/api/utilities/index.js +3 -3
  47. package/dist/api/utilities/index.js.map +1 -1
  48. package/dist/api/utilities/media-type.d.ts +1 -9
  49. package/dist/api/utilities/media-type.js +1 -9
  50. package/dist/api/utilities/media-type.js.map +1 -1
  51. package/dist/api/utilities/pixel-format.d.ts +1 -9
  52. package/dist/api/utilities/pixel-format.js +1 -9
  53. package/dist/api/utilities/pixel-format.js.map +1 -1
  54. package/dist/api/utilities/sample-format.d.ts +1 -9
  55. package/dist/api/utilities/sample-format.js +1 -9
  56. package/dist/api/utilities/sample-format.js.map +1 -1
  57. package/dist/api/utilities/streaming.d.ts +0 -8
  58. package/dist/api/utilities/streaming.js +0 -8
  59. package/dist/api/utilities/streaming.js.map +1 -1
  60. package/dist/api/utilities/timestamp.d.ts +0 -8
  61. package/dist/api/utilities/timestamp.js +0 -8
  62. package/dist/api/utilities/timestamp.js.map +1 -1
  63. package/dist/api/utils.d.ts +1 -2
  64. package/dist/api/utils.js +11 -0
  65. package/dist/api/utils.js.map +1 -1
  66. package/dist/constants/constants.d.ts +1 -1
  67. package/dist/constants/constants.js +2 -0
  68. package/dist/constants/constants.js.map +1 -1
  69. package/dist/lib/audio-fifo.d.ts +127 -170
  70. package/dist/lib/audio-fifo.js +130 -173
  71. package/dist/lib/audio-fifo.js.map +1 -1
  72. package/dist/lib/binding.d.ts +1 -0
  73. package/dist/lib/binding.js +7 -0
  74. package/dist/lib/binding.js.map +1 -1
  75. package/dist/lib/bitstream-filter-context.d.ts +139 -184
  76. package/dist/lib/bitstream-filter-context.js +139 -188
  77. package/dist/lib/bitstream-filter-context.js.map +1 -1
  78. package/dist/lib/bitstream-filter.d.ts +68 -54
  79. package/dist/lib/bitstream-filter.js +68 -54
  80. package/dist/lib/bitstream-filter.js.map +1 -1
  81. package/dist/lib/codec-context.d.ts +316 -380
  82. package/dist/lib/codec-context.js +316 -381
  83. package/dist/lib/codec-context.js.map +1 -1
  84. package/dist/lib/codec-parameters.d.ts +160 -170
  85. package/dist/lib/codec-parameters.js +162 -172
  86. package/dist/lib/codec-parameters.js.map +1 -1
  87. package/dist/lib/codec-parser.d.ts +91 -104
  88. package/dist/lib/codec-parser.js +92 -103
  89. package/dist/lib/codec-parser.js.map +1 -1
  90. package/dist/lib/codec.d.ts +266 -283
  91. package/dist/lib/codec.js +270 -287
  92. package/dist/lib/codec.js.map +1 -1
  93. package/dist/lib/dictionary.d.ts +149 -203
  94. package/dist/lib/dictionary.js +158 -212
  95. package/dist/lib/dictionary.js.map +1 -1
  96. package/dist/lib/error.d.ts +96 -130
  97. package/dist/lib/error.js +98 -128
  98. package/dist/lib/error.js.map +1 -1
  99. package/dist/lib/filter-context.d.ts +284 -218
  100. package/dist/lib/filter-context.js +290 -227
  101. package/dist/lib/filter-context.js.map +1 -1
  102. package/dist/lib/filter-graph.d.ts +251 -292
  103. package/dist/lib/filter-graph.js +253 -294
  104. package/dist/lib/filter-graph.js.map +1 -1
  105. package/dist/lib/filter-inout.d.ts +87 -95
  106. package/dist/lib/filter-inout.js +87 -95
  107. package/dist/lib/filter-inout.js.map +1 -1
  108. package/dist/lib/filter.d.ts +93 -111
  109. package/dist/lib/filter.js +93 -111
  110. package/dist/lib/filter.js.map +1 -1
  111. package/dist/lib/format-context.d.ts +320 -428
  112. package/dist/lib/format-context.js +313 -385
  113. package/dist/lib/format-context.js.map +1 -1
  114. package/dist/lib/frame.d.ts +262 -405
  115. package/dist/lib/frame.js +263 -408
  116. package/dist/lib/frame.js.map +1 -1
  117. package/dist/lib/hardware-device-context.d.ts +149 -203
  118. package/dist/lib/hardware-device-context.js +149 -203
  119. package/dist/lib/hardware-device-context.js.map +1 -1
  120. package/dist/lib/hardware-frames-context.d.ts +170 -180
  121. package/dist/lib/hardware-frames-context.js +171 -181
  122. package/dist/lib/hardware-frames-context.js.map +1 -1
  123. package/dist/lib/index.d.ts +3 -2
  124. package/dist/lib/index.js +3 -3
  125. package/dist/lib/index.js.map +1 -1
  126. package/dist/lib/input-format.d.ts +89 -117
  127. package/dist/lib/input-format.js +89 -117
  128. package/dist/lib/input-format.js.map +1 -1
  129. package/dist/lib/io-context.d.ts +209 -241
  130. package/dist/lib/io-context.js +220 -252
  131. package/dist/lib/io-context.js.map +1 -1
  132. package/dist/lib/log.d.ts +85 -119
  133. package/dist/lib/log.js +85 -122
  134. package/dist/lib/log.js.map +1 -1
  135. package/dist/lib/native-types.d.ts +118 -106
  136. package/dist/lib/native-types.js +0 -7
  137. package/dist/lib/native-types.js.map +1 -1
  138. package/dist/lib/option.d.ts +437 -218
  139. package/dist/lib/option.js +462 -226
  140. package/dist/lib/option.js.map +1 -1
  141. package/dist/lib/output-format.d.ts +77 -101
  142. package/dist/lib/output-format.js +77 -101
  143. package/dist/lib/output-format.js.map +1 -1
  144. package/dist/lib/packet.d.ts +172 -240
  145. package/dist/lib/packet.js +172 -241
  146. package/dist/lib/packet.js.map +1 -1
  147. package/dist/lib/rational.d.ts +0 -2
  148. package/dist/lib/rational.js +0 -2
  149. package/dist/lib/rational.js.map +1 -1
  150. package/dist/lib/software-resample-context.d.ts +241 -325
  151. package/dist/lib/software-resample-context.js +242 -326
  152. package/dist/lib/software-resample-context.js.map +1 -1
  153. package/dist/lib/software-scale-context.d.ts +129 -173
  154. package/dist/lib/software-scale-context.js +131 -175
  155. package/dist/lib/software-scale-context.js.map +1 -1
  156. package/dist/lib/stream.d.ts +87 -197
  157. package/dist/lib/stream.js +87 -197
  158. package/dist/lib/stream.js.map +1 -1
  159. package/dist/lib/utilities.d.ts +435 -181
  160. package/dist/lib/utilities.js +438 -182
  161. package/dist/lib/utilities.js.map +1 -1
  162. package/install/check.js +0 -1
  163. package/install/ffmpeg.js +0 -11
  164. package/package.json +25 -18
  165. package/release_notes.md +24 -59
  166. package/CHANGELOG.md +0 -8
@@ -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
- * Filter - High-level wrapper for media filtering
4
+ * High-level filter API for audio and video processing.
3
5
  *
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
- 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, Frame } from '@seydx/av/api';
13
+ * import { FilterAPI } from 'node-av/api';
29
14
  *
30
- * // Simple video filter from a stream
31
- * const videoStream = media.video();
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 frames
35
- * const outputFrame = await filter.process(inputFrame);
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 acceleration (decoder -> hw filter -> encoder)
41
- * const hw = await HardwareContext.auto();
42
- * const decoder = await Decoder.create(stream, { hardware: hw });
43
- * const filter = await FilterAPI.create('scale_vt=640:480', decoder.getOutputStreamInfo(), {
44
- * hardware: hw
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
- * @example
49
- * ```typescript
50
- * // Software decode -> hardware encode pipeline with hwupload
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
- * Create a new Filter instance.
78
- *
79
- * @param config - Stream information from input stream
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 from a filter description string.
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
- * For hardware input formats: Uses lazy initialization, waits for first frame
98
- * with hw_frames_ctx before configuring the filter graph.
99
- * For software formats: Initializes immediately.
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
- * Hardware context handling:
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 (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
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
- * @returns Promise resolving to configured Filter instance
78
+ * @throws {Error} If filter creation or configuration fails
111
79
  *
112
- * @throws {FFmpegError} If filter creation or configuration fails
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', videoStream);
84
+ * // Simple video filter
85
+ * const filter = await FilterAPI.create('scale=640:480', videoInfo);
86
+ * ```
119
87
  *
120
- * // Complex filter chain with hardware
121
- * const hw = await HardwareContext.auto();
122
- * const decoder = await Decoder.create(stream, { hardware: hw });
88
+ * @example
89
+ * ```typescript
90
+ * // Complex filter chain
123
91
  * const filter = await FilterAPI.create(
124
- * 'scale_vt=640:480,hwdownload,format=yuv420p',
125
- * decoder.getOutputStreamInfo(),
126
- * { hardware: hw }
92
+ * 'crop=640:480:0:0,rotate=PI/4',
93
+ * videoInfo
127
94
  * );
95
+ * ```
128
96
  *
129
- * // From StreamInfo (for raw data)
130
- * const filter = await FilterAPI.create('scale=640:480', {
131
- * type: 'video',
132
- * width: 1920,
133
- * height: 1080,
134
- * pixelFormat: AV_PIX_FMT_YUV420P,
135
- * timeBase: { num: 1, den: 30 }
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 single frame through the filter.
145
+ * Process a frame through the filter.
176
146
  *
177
- * Sends a frame through the filter graph and returns the filtered result.
178
- * May return null if the filter needs more input frames.
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
- * On first frame with hw_frames_ctx, initializes the filter graph (lazy initialization).
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 filter
153
+ * @param frame - Input frame to process
154
+ * @returns Filtered frame or null if buffered
185
155
  *
186
- * @returns Promise resolving to filtered frame or null if more input needed
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 outputFrame = await filter.process(inputFrame);
194
- * if (outputFrame) {
195
- * // Process the filtered frame
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 (FFmpegError.is(getRet, AVERROR_EAGAIN)) {
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 through the filter.
215
+ * Process multiple frames at once.
230
216
  *
231
- * Batch processing for better performance.
232
- * Returns all available output frames.
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
- * @returns Promise resolving to array of filtered frames
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 outputFrames = await filter.processMultiple(inputFrames);
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 a filtered frame without sending input.
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
- * Used to drain buffered frames from the filter.
266
- * Returns null when no more frames are available.
263
+ * @returns Buffered frame or null if none available
267
264
  *
268
- * @returns Promise resolving to filtered frame or null
265
+ * @throws {Error} If filter not ready
269
266
  *
270
- * @throws {FFmpegError} If receiving fails
267
+ * @throws {FFmpegError} If receive fails
271
268
  *
272
269
  * @example
273
270
  * ```typescript
274
- * // Drain all buffered frames
275
- * while (true) {
276
- * const frame = await filter.receive();
277
- * if (!frame) break;
278
- * // Process frame
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 (FFmpegError.is(ret, AVERROR_EAGAIN) || FFmpegError.is(ret, AVERROR_EOF)) {
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 the filter by sending null frame.
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
- * Signals end of stream to the filter.
305
- * Use receive() to get any remaining frames.
307
+ * Direct mapping to av_buffersrc_add_frame(NULL).
306
308
  *
307
- * @returns Promise resolving when flush is complete
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
- * while (true) {
316
- * const frame = await filter.receive();
317
- * if (!frame) break;
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 && !FFmpegError.is(ret, AVERROR_EOF)) {
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 all remaining frames as a generator.
336
+ * Flush filter and yield remaining frames.
333
337
  *
334
- * More convenient than calling flush() + receive() in a loop.
335
- * Automatically sends flush signal and yields all buffered frames.
338
+ * Convenient async generator for flushing.
339
+ * Combines flush and receive operations.
336
340
  *
337
- * @returns Async generator of remaining frames
341
+ * @yields Remaining frames from filter
342
+ * @throws {Error} If filter not ready
338
343
  *
339
- * @throws {Error} If filter is not initialized
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
- * // Process final frame
346
- * using _ = frame; // Auto cleanup
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 frames as an async generator.
370
+ * Process frame stream through filter.
364
371
  *
365
- * Provides a convenient iterator interface for filtering.
366
- * Automatically handles buffering and draining.
367
- * Input frames are automatically freed after processing.
372
+ * High-level async generator for filtering frame streams.
373
+ * Automatically handles buffering and flushing.
374
+ * Frees input frames after processing.
368
375
  *
369
- * IMPORTANT: The yielded frames MUST be freed by the caller!
370
- * Input frames are automatically freed after processing.
376
+ * @param frames - Async generator of input frames
377
+ * @yields Filtered frames
378
+ * @throws {Error} If filter not ready
371
379
  *
372
- * @param frames - Async generator of input frames (will be freed automatically)
380
+ * @throws {FFmpegError} If processing fails
373
381
  *
374
- * @returns Async generator of filtered frames (ownership transferred to caller)
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
- * for await (const filtered of filter.frames(decoder.frames())) {
379
- * // Process filtered frame
380
- * using _ = filtered; // Auto cleanup with using statement
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 a command to a filter in the graph.
437
+ * Send command to filter.
438
+ *
439
+ * Sends runtime command to specific filter in graph.
440
+ * Allows dynamic parameter adjustment.
416
441
  *
417
- * Allows runtime modification of filter parameters without recreating the graph.
418
- * Not all filters support commands - check filter documentation.
442
+ * Direct mapping to avfilter_graph_send_command().
419
443
  *
420
- * @param target - Filter name or "all" to send to all filters
421
- * @param cmd - Command name (e.g., "volume", "hue", "brightness")
422
- * @param arg - Command argument value
423
- * @param flags - Optional command 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
- * @returns Command response
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 dynamically
456
+ * // Change volume at runtime
430
457
  * const response = filter.sendCommand('volume', 'volume', '0.5');
431
- * if (response) {
432
- * console.log('Volume changed successfully');
433
- * }
458
+ * console.log(`Volume changed: ${response}`);
434
459
  * ```
435
460
  *
436
- * @example
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 a command to be executed at a specific time.
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
- * Commands are executed when processing frames with matching timestamps.
456
- * Useful for scripted filter changes synchronized with media playback.
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
- * @param target - Filter name or "all" to send to all filters
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
- * // Schedule volume changes at specific times
467
- * filter.queueCommand('volume', 'volume', '0.5', 5.0); // At 5 seconds
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 the filter graph description.
506
+ * Get filter graph description.
507
+ *
508
+ * Returns human-readable graph structure.
509
+ * Useful for debugging filter chains.
481
510
  *
482
- * Returns a string representation of the filter graph in DOT format.
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 the filter is initialized and ready.
528
+ * Check if filter is ready for processing.
529
+ *
530
+ * @returns true if initialized and ready
501
531
  *
502
- * @returns true if the filter is ready for processing
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 the media type of this filter.
543
+ * Get media type of filter.
544
+ *
545
+ * @returns AVMEDIA_TYPE_VIDEO or AVMEDIA_TYPE_AUDIO
509
546
  *
510
- * @returns The media type (video or audio)
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 all filter resources.
558
+ * Free filter resources.
517
559
  *
518
- * Releases the filter graph and all associated filters.
519
- * The filter instance cannot be used after calling this.
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 the filter graph.
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
- * Sets up buffer source, buffer sink, and parses the filter description.
540
- * Configures the graph for processing.
587
+ * @throws {Error} If initialization fails
541
588
  *
542
- * For hardware inputs: Uses hw_frames_ctx from first frame
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 and configure the buffer source filter without hw_frames_ctx.
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 and configure the buffer sink filter.
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 and connect the filter description.
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 if hardware context is required for the filter chain.
772
+ * Check hardware requirements for filters.
711
773
  *
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
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 the filter.
811
+ * Dispose of filter.
776
812
  *
777
- * Implements the Disposable interface for automatic cleanup.
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 Filter.create('scale=1280:720', config);
784
- * // ... use filter
785
- * } // Automatically freed when leaving scope
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();