node-av 1.0.3 → 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.
Files changed (162) hide show
  1. package/README.md +56 -41
  2. package/dist/api/bitstream-filter.d.ts +180 -123
  3. package/dist/api/bitstream-filter.js +182 -126
  4. package/dist/api/bitstream-filter.js.map +1 -1
  5. package/dist/api/decoder.d.ts +286 -130
  6. package/dist/api/decoder.js +321 -159
  7. package/dist/api/decoder.js.map +1 -1
  8. package/dist/api/encoder.d.ts +254 -158
  9. package/dist/api/encoder.js +326 -298
  10. package/dist/api/encoder.js.map +1 -1
  11. package/dist/api/filter-presets.d.ts +912 -0
  12. package/dist/api/filter-presets.js +1407 -0
  13. package/dist/api/filter-presets.js.map +1 -0
  14. package/dist/api/filter.d.ts +280 -284
  15. package/dist/api/filter.js +435 -509
  16. package/dist/api/filter.js.map +1 -1
  17. package/dist/api/hardware.d.ts +226 -159
  18. package/dist/api/hardware.js +405 -287
  19. package/dist/api/hardware.js.map +1 -1
  20. package/dist/api/index.d.ts +3 -2
  21. package/dist/api/index.js +1 -0
  22. package/dist/api/index.js.map +1 -1
  23. package/dist/api/io-stream.d.ts +65 -61
  24. package/dist/api/io-stream.js +45 -47
  25. package/dist/api/io-stream.js.map +1 -1
  26. package/dist/api/media-input.d.ts +244 -141
  27. package/dist/api/media-input.js +207 -104
  28. package/dist/api/media-input.js.map +1 -1
  29. package/dist/api/media-output.d.ts +206 -128
  30. package/dist/api/media-output.js +212 -129
  31. package/dist/api/media-output.js.map +1 -1
  32. package/dist/api/pipeline.d.ts +168 -38
  33. package/dist/api/pipeline.js +238 -14
  34. package/dist/api/pipeline.js.map +1 -1
  35. package/dist/api/types.d.ts +22 -182
  36. package/dist/api/utilities/audio-sample.d.ts +1 -1
  37. package/dist/api/utilities/image.d.ts +1 -1
  38. package/dist/api/utilities/media-type.d.ts +1 -1
  39. package/dist/api/utilities/pixel-format.d.ts +1 -1
  40. package/dist/api/utilities/sample-format.d.ts +1 -1
  41. package/dist/api/utilities/timestamp.d.ts +1 -1
  42. package/dist/api/utils.d.ts +1 -2
  43. package/dist/api/utils.js +9 -0
  44. package/dist/api/utils.js.map +1 -1
  45. package/dist/{lib → constants}/channel-layouts.d.ts +1 -1
  46. package/dist/constants/channel-layouts.js.map +1 -0
  47. package/dist/{lib → constants}/constants.d.ts +19 -4
  48. package/dist/{lib → constants}/constants.js +15 -1
  49. package/dist/constants/constants.js.map +1 -0
  50. package/dist/constants/decoders.d.ts +609 -0
  51. package/dist/constants/decoders.js +617 -0
  52. package/dist/constants/decoders.js.map +1 -0
  53. package/dist/constants/encoders.d.ts +285 -0
  54. package/dist/constants/encoders.js +298 -0
  55. package/dist/constants/encoders.js.map +1 -0
  56. package/dist/constants/index.d.ts +4 -0
  57. package/dist/constants/index.js +5 -0
  58. package/dist/constants/index.js.map +1 -0
  59. package/dist/index.d.ts +1 -0
  60. package/dist/index.js +2 -0
  61. package/dist/index.js.map +1 -1
  62. package/dist/lib/audio-fifo.d.ts +128 -171
  63. package/dist/lib/audio-fifo.js +130 -173
  64. package/dist/lib/audio-fifo.js.map +1 -1
  65. package/dist/lib/binding.d.ts +7 -5
  66. package/dist/lib/binding.js +5 -0
  67. package/dist/lib/binding.js.map +1 -1
  68. package/dist/lib/bitstream-filter-context.d.ts +139 -184
  69. package/dist/lib/bitstream-filter-context.js +139 -188
  70. package/dist/lib/bitstream-filter-context.js.map +1 -1
  71. package/dist/lib/bitstream-filter.d.ts +69 -55
  72. package/dist/lib/bitstream-filter.js +68 -54
  73. package/dist/lib/bitstream-filter.js.map +1 -1
  74. package/dist/lib/codec-context.d.ts +317 -381
  75. package/dist/lib/codec-context.js +316 -381
  76. package/dist/lib/codec-context.js.map +1 -1
  77. package/dist/lib/codec-parameters.d.ts +161 -171
  78. package/dist/lib/codec-parameters.js +162 -172
  79. package/dist/lib/codec-parameters.js.map +1 -1
  80. package/dist/lib/codec-parser.d.ts +92 -105
  81. package/dist/lib/codec-parser.js +92 -103
  82. package/dist/lib/codec-parser.js.map +1 -1
  83. package/dist/lib/codec.d.ts +328 -217
  84. package/dist/lib/codec.js +392 -218
  85. package/dist/lib/codec.js.map +1 -1
  86. package/dist/lib/dictionary.d.ts +150 -204
  87. package/dist/lib/dictionary.js +159 -213
  88. package/dist/lib/dictionary.js.map +1 -1
  89. package/dist/lib/error.d.ts +97 -131
  90. package/dist/lib/error.js +98 -128
  91. package/dist/lib/error.js.map +1 -1
  92. package/dist/lib/filter-context.d.ts +317 -194
  93. package/dist/lib/filter-context.js +335 -200
  94. package/dist/lib/filter-context.js.map +1 -1
  95. package/dist/lib/filter-graph.d.ts +252 -293
  96. package/dist/lib/filter-graph.js +253 -294
  97. package/dist/lib/filter-graph.js.map +1 -1
  98. package/dist/lib/filter-inout.d.ts +87 -95
  99. package/dist/lib/filter-inout.js +87 -95
  100. package/dist/lib/filter-inout.js.map +1 -1
  101. package/dist/lib/filter.d.ts +93 -111
  102. package/dist/lib/filter.js +94 -112
  103. package/dist/lib/filter.js.map +1 -1
  104. package/dist/lib/format-context.d.ts +321 -429
  105. package/dist/lib/format-context.js +314 -386
  106. package/dist/lib/format-context.js.map +1 -1
  107. package/dist/lib/frame.d.ts +263 -406
  108. package/dist/lib/frame.js +263 -408
  109. package/dist/lib/frame.js.map +1 -1
  110. package/dist/lib/hardware-device-context.d.ts +150 -204
  111. package/dist/lib/hardware-device-context.js +149 -203
  112. package/dist/lib/hardware-device-context.js.map +1 -1
  113. package/dist/lib/hardware-frames-context.d.ts +171 -181
  114. package/dist/lib/hardware-frames-context.js +171 -181
  115. package/dist/lib/hardware-frames-context.js.map +1 -1
  116. package/dist/lib/index.d.ts +2 -3
  117. package/dist/lib/index.js +2 -5
  118. package/dist/lib/index.js.map +1 -1
  119. package/dist/lib/input-format.d.ts +90 -118
  120. package/dist/lib/input-format.js +89 -117
  121. package/dist/lib/input-format.js.map +1 -1
  122. package/dist/lib/io-context.d.ts +210 -242
  123. package/dist/lib/io-context.js +221 -253
  124. package/dist/lib/io-context.js.map +1 -1
  125. package/dist/lib/log.d.ts +86 -120
  126. package/dist/lib/log.js +85 -122
  127. package/dist/lib/log.js.map +1 -1
  128. package/dist/lib/native-types.d.ts +127 -112
  129. package/dist/lib/native-types.js +9 -0
  130. package/dist/lib/native-types.js.map +1 -1
  131. package/dist/lib/option.d.ts +285 -242
  132. package/dist/lib/option.js +310 -250
  133. package/dist/lib/option.js.map +1 -1
  134. package/dist/lib/output-format.d.ts +78 -102
  135. package/dist/lib/output-format.js +77 -101
  136. package/dist/lib/output-format.js.map +1 -1
  137. package/dist/lib/packet.d.ts +173 -241
  138. package/dist/lib/packet.js +172 -241
  139. package/dist/lib/packet.js.map +1 -1
  140. package/dist/lib/rational.d.ts +0 -2
  141. package/dist/lib/rational.js +0 -2
  142. package/dist/lib/rational.js.map +1 -1
  143. package/dist/lib/software-resample-context.d.ts +242 -326
  144. package/dist/lib/software-resample-context.js +242 -326
  145. package/dist/lib/software-resample-context.js.map +1 -1
  146. package/dist/lib/software-scale-context.d.ts +130 -174
  147. package/dist/lib/software-scale-context.js +132 -176
  148. package/dist/lib/software-scale-context.js.map +1 -1
  149. package/dist/lib/stream.d.ts +88 -198
  150. package/dist/lib/stream.js +87 -197
  151. package/dist/lib/stream.js.map +1 -1
  152. package/dist/lib/types.d.ts +1 -1
  153. package/dist/lib/utilities.d.ts +372 -181
  154. package/dist/lib/utilities.js +373 -182
  155. package/dist/lib/utilities.js.map +1 -1
  156. package/install/check.js +0 -1
  157. package/package.json +32 -24
  158. package/release_notes.md +43 -13
  159. package/CHANGELOG.md +0 -8
  160. package/dist/lib/channel-layouts.js.map +0 -1
  161. package/dist/lib/constants.js.map +0 -1
  162. /package/dist/{lib → constants}/channel-layouts.js +0 -0
@@ -1,59 +1,46 @@
1
+ import { AVERROR_EOF, AVMEDIA_TYPE_VIDEO } from '../constants/constants.js';
2
+ import { AVERROR_EAGAIN, Codec, CodecContext, FFmpegError, Frame } from '../lib/index.js';
1
3
  /**
2
- * Decoder - High-level wrapper for media decoding
4
+ * High-level decoder for audio and video streams.
3
5
  *
4
- * Simplifies FFmpeg's decoding API with automatic codec selection,
5
- * parameter configuration, and frame management.
6
- *
7
- * Handles codec initialization, packet decoding, and frame output.
8
- * Supports hardware acceleration and zero-copy transcoding.
9
- *
10
- * @module api/decoder
11
- */
12
- import { AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX, AVERROR_EAGAIN, AVERROR_EOF, Codec, CodecContext, FFmpegError, Frame } from '../lib/index.js';
13
- /**
14
- * High-level decoder for media streams.
15
- *
16
- * Handles codec initialization, packet decoding, and frame output.
17
- * Designed for simple, efficient decoding workflows.
18
- *
19
- * Manages codec context lifecycle and provides automatic cleanup.
20
- * Supports hardware acceleration with zero-copy frame sharing.
6
+ * Provides a simplified interface for decoding media streams from packets to frames.
7
+ * Handles codec initialization, hardware acceleration setup, and frame management.
8
+ * Supports both synchronous packet-by-packet decoding and async iteration over frames.
9
+ * Essential component in media processing pipelines for converting compressed data to raw frames.
21
10
  *
22
11
  * @example
23
12
  * ```typescript
24
- * // Create decoder for video stream
25
- * const media = await MediaInput.open('video.mp4');
26
- * const stream = media.video(); // Get video stream
27
- * const decoder = await Decoder.create(stream);
13
+ * import { MediaInput, Decoder } from 'node-av/api';
28
14
  *
29
- * // Decode packets
30
- * for await (const packet of media.packets()) {
31
- * if (packet.streamIndex === stream.index) {
32
- * const frame = await decoder.decode(packet);
33
- * if (frame) {
34
- * console.log(`Decoded frame: ${frame.width}x${frame.height}`);
35
- * // Process frame...
36
- * }
37
- * }
38
- * }
15
+ * // Open media and create decoder
16
+ * await using input = await MediaInput.open('video.mp4');
17
+ * using decoder = await Decoder.create(input.video());
39
18
  *
40
- * // Flush decoder
41
- * const lastFrame = await decoder.flush();
42
- * decoder.close();
19
+ * // Decode frames
20
+ * for await (const frame of decoder.frames(input.packets())) {
21
+ * console.log(`Decoded frame: ${frame.width}x${frame.height}`);
22
+ * frame.free();
23
+ * }
43
24
  * ```
44
25
  *
45
26
  * @example
46
27
  * ```typescript
47
- * // With hardware acceleration
48
- * const hw = await HardwareContext.auto();
49
- * const stream = media.video();
50
- * const decoder = await Decoder.create(stream, {
51
- * hardware: hw
52
- * });
53
- * // ... use decoder
54
- * decoder.close();
55
- * hw?.dispose(); // Safe to call again (no-op)
28
+ * import { HardwareContext } from 'node-av/api';
29
+ * import { AV_HWDEVICE_TYPE_CUDA } from 'node-av/constants';
30
+ *
31
+ * // Setup hardware acceleration
32
+ * const hw = HardwareContext.create(AV_HWDEVICE_TYPE_CUDA);
33
+ * using decoder = await Decoder.create(stream, { hardware: hw });
34
+ *
35
+ * // Frames will be decoded on GPU
36
+ * for await (const frame of decoder.frames(packets)) {
37
+ * // frame.hwFramesCtx contains GPU memory reference
38
+ * }
56
39
  * ```
40
+ *
41
+ * @see {@link Encoder} For encoding frames to packets
42
+ * @see {@link MediaInput} For reading media files
43
+ * @see {@link HardwareContext} For GPU acceleration
57
44
  */
58
45
  export class Decoder {
59
46
  codecContext;
@@ -61,15 +48,14 @@ export class Decoder {
61
48
  streamIndex;
62
49
  stream;
63
50
  isOpen = true;
64
- hardware; // Reference to hardware context for auto-sharing frames context
51
+ hardware;
65
52
  /**
66
- * Private constructor - use Decoder.create() instead.
53
+ * @param codecContext - Configured codec context
54
+ * @param stream - Media stream being decoded
55
+ * @param hardware - Optional hardware context
56
+ * Use {@link create} factory method
67
57
  *
68
- * Initializes the decoder with a codec context and allocates a frame buffer.
69
- *
70
- * @param codecContext - Initialized codec context
71
- * @param stream - The stream this decoder is for
72
- * @param hardware - Optional hardware context for auto-sharing frames context
58
+ * @internal
73
59
  */
74
60
  constructor(codecContext, stream, hardware) {
75
61
  this.codecContext = codecContext;
@@ -80,27 +66,49 @@ export class Decoder {
80
66
  this.frame.alloc();
81
67
  }
82
68
  /**
83
- * Create a decoder for a specific stream.
69
+ * Create a decoder for a media stream.
84
70
  *
85
- * Factory method that handles codec discovery, context setup,
86
- * and initialization.
71
+ * Initializes a decoder with the appropriate codec and configuration.
72
+ * Automatically detects and configures hardware acceleration if provided.
73
+ * Applies custom codec options and threading configuration.
87
74
  *
88
- * Uses avcodec_find_decoder() to locate the appropriate codec,
89
- * then initializes and opens the codec context.
90
- *
91
- * @param stream - Stream to decode
75
+ * @param stream - Media stream to decode
92
76
  * @param options - Decoder configuration options
77
+ * @returns Configured decoder instance
93
78
  *
94
- * @returns Promise resolving to configured Decoder
79
+ * @throws {Error} If decoder not found for codec
80
+ * @throws {FFmpegError} If codec initialization fails
95
81
  *
96
- * @throws {Error} If codec unavailable
82
+ * @example
83
+ * ```typescript
84
+ * import { MediaInput, Decoder } from 'node-av/api';
85
+ *
86
+ * await using input = await MediaInput.open('video.mp4');
87
+ * using decoder = await Decoder.create(input.video());
88
+ * ```
97
89
  *
98
90
  * @example
99
91
  * ```typescript
100
- * const media = await MediaInput.open('video.mp4');
101
- * const stream = media.video();
102
- * const decoder = await Decoder.create(stream);
92
+ * using decoder = await Decoder.create(stream, {
93
+ * threads: 4,
94
+ * options: {
95
+ * 'refcounted_frames': '1',
96
+ * 'skip_frame': 'nonkey' // Only decode keyframes
97
+ * }
98
+ * });
99
+ * ```
100
+ *
101
+ * @example
102
+ * ```typescript
103
+ * const hw = HardwareContext.auto();
104
+ * using decoder = await Decoder.create(stream, {
105
+ * hardware: hw,
106
+ * threads: 0 // Auto-detect thread count
107
+ * });
103
108
  * ```
109
+ *
110
+ * @see {@link HardwareContext} For GPU acceleration setup
111
+ * @see {@link DecoderOptions} For configuration options
104
112
  */
105
113
  static async create(stream, options = {}) {
106
114
  if (!stream) {
@@ -127,24 +135,11 @@ export class Decoder {
127
135
  codecContext.threadCount = options.threads;
128
136
  }
129
137
  // Check if this decoder supports hardware acceleration
130
- let supportsHardware = false;
131
- if (options.hardware) {
132
- // Check decoder's hardware configurations
133
- for (let i = 0;; i++) {
134
- const config = codec.getHwConfig(i);
135
- if (!config)
136
- break;
137
- // Check if decoder supports HW_DEVICE_CTX method with matching device type
138
- if ((config.methods & AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX) !== 0 && config.deviceType === options.hardware.deviceType) {
139
- supportsHardware = true;
140
- break;
141
- }
142
- }
143
- // Only apply hardware acceleration if the decoder supports it
144
- if (supportsHardware) {
145
- codecContext.hwDeviceCtx = options.hardware.deviceContext;
146
- }
147
- // Silently ignore hardware for software decoders
138
+ // Only apply hardware acceleration if the decoder supports it
139
+ // Silently ignore hardware for software decoders
140
+ const isHWDecoder = codec.isHardwareAcceleratedDecoder();
141
+ if (isHWDecoder && options.hardware) {
142
+ codecContext.hwDeviceCtx = options.hardware.deviceContext;
148
143
  }
149
144
  // Apply codec-specific options via AVOptions
150
145
  if (options.options) {
@@ -158,37 +153,133 @@ export class Decoder {
158
153
  codecContext.freeContext();
159
154
  FFmpegError.throwIfError(openRet, 'Failed to open codec');
160
155
  }
161
- const decoder = new Decoder(codecContext, stream, options.hardware);
156
+ const decoder = new Decoder(codecContext, stream, isHWDecoder ? options.hardware : undefined);
162
157
  return decoder;
163
158
  }
164
159
  /**
165
160
  * Check if decoder is open.
161
+ *
162
+ * @returns true if decoder is open and ready
163
+ *
164
+ * @example
165
+ * ```typescript
166
+ * if (decoder.isDecoderOpen) {
167
+ * const frame = await decoder.decode(packet);
168
+ * }
169
+ * ```
166
170
  */
167
171
  get isDecoderOpen() {
168
172
  return this.isOpen;
169
173
  }
170
174
  /**
171
- * Decode a packet and return a frame if available.
175
+ * Get output stream information.
172
176
  *
173
- * Sends packet to decoder and attempts to receive a frame.
174
- * May return null if decoder needs more data.
177
+ * Returns format information about decoded frames.
178
+ * For hardware decoding, returns the hardware pixel format.
179
+ * Essential for configuring downstream components like encoders or filters.
175
180
  *
176
- * Uses avcodec_send_packet() and avcodec_receive_frame() internally.
177
- * The decoder may buffer packets before producing frames.
181
+ * @returns Stream format information
178
182
  *
179
- * @param packet - Packet to decode
183
+ * @example
184
+ * ```typescript
185
+ * const info = decoder.getOutputStreamInfo();
186
+ * if (info.type === 'video') {
187
+ * console.log(`Video: ${info.width}x${info.height} @ ${info.pixelFormat}`);
188
+ * console.log(`Frame rate: ${info.frameRate.num}/${info.frameRate.den}`);
189
+ * }
190
+ * ```
180
191
  *
181
- * @returns Promise resolving to Frame or null
192
+ * @example
193
+ * ```typescript
194
+ * const info = decoder.getOutputStreamInfo();
195
+ * if (info.type === 'audio') {
196
+ * console.log(`Audio: ${info.sampleRate}Hz ${info.sampleFormat}`);
197
+ * console.log(`Channels: ${info.channelLayout}`);
198
+ * }
199
+ * ```
182
200
  *
183
- * @throws {Error} If decoder is closed or decode fails
201
+ * @see {@link StreamInfo} For format details
202
+ * @see {@link Encoder.create} For matching encoder configuration
203
+ */
204
+ getOutputStreamInfo() {
205
+ if (this.stream.codecpar.codecType === AVMEDIA_TYPE_VIDEO) {
206
+ return {
207
+ type: 'video',
208
+ width: this.codecContext.width,
209
+ height: this.codecContext.height,
210
+ pixelFormat: this.hardware?.devicePixelFormat ?? this.codecContext.pixelFormat,
211
+ timeBase: this.stream.timeBase,
212
+ frameRate: this.stream.rFrameRate,
213
+ sampleAspectRatio: this.codecContext.sampleAspectRatio,
214
+ };
215
+ }
216
+ else {
217
+ return {
218
+ type: 'audio',
219
+ sampleRate: this.codecContext.sampleRate,
220
+ sampleFormat: this.codecContext.sampleFormat,
221
+ channelLayout: this.codecContext.channelLayout,
222
+ timeBase: this.stream.timeBase,
223
+ };
224
+ }
225
+ }
226
+ /**
227
+ * Check if decoder uses hardware acceleration.
228
+ *
229
+ * @returns true if hardware-accelerated
230
+ *
231
+ * @example
232
+ * ```typescript
233
+ * if (decoder.isHardware()) {
234
+ * console.log('Using GPU acceleration');
235
+ * }
236
+ * ```
237
+ *
238
+ * @see {@link HardwareContext} For hardware setup
239
+ */
240
+ isHardware() {
241
+ return !!this.hardware;
242
+ }
243
+ /**
244
+ * Decode a packet to a frame.
245
+ *
246
+ * Sends a packet to the decoder and attempts to receive a decoded frame.
247
+ * Handles internal buffering - may return null if more packets needed.
248
+ * Automatically manages decoder state and error recovery.
249
+ *
250
+ * Direct mapping to avcodec_send_packet() and avcodec_receive_frame().
251
+ *
252
+ * @param packet - Compressed packet to decode
253
+ * @returns Decoded frame or null if more data needed
254
+ *
255
+ * @throws {Error} If decoder is closed
256
+ * @throws {FFmpegError} If decoding fails
184
257
  *
185
258
  * @example
186
259
  * ```typescript
187
260
  * const frame = await decoder.decode(packet);
188
261
  * if (frame) {
189
- * // Process frame
262
+ * console.log(`Decoded frame with PTS: ${frame.pts}`);
263
+ * frame.free();
264
+ * }
265
+ * ```
266
+ *
267
+ * @example
268
+ * ```typescript
269
+ * for await (const packet of input.packets()) {
270
+ * if (packet.streamIndex === decoder.getStreamIndex()) {
271
+ * const frame = await decoder.decode(packet);
272
+ * if (frame) {
273
+ * await processFrame(frame);
274
+ * frame.free();
275
+ * }
276
+ * }
277
+ * packet.free();
190
278
  * }
191
279
  * ```
280
+ *
281
+ * @see {@link frames} For automatic packet iteration
282
+ * @see {@link flush} For end-of-stream handling
192
283
  */
193
284
  async decode(packet) {
194
285
  if (!this.isOpen) {
@@ -212,26 +303,31 @@ export class Decoder {
212
303
  return frame;
213
304
  }
214
305
  /**
215
- * Flush decoder and get remaining frames.
306
+ * Flush decoder and get buffered frame.
216
307
  *
217
- * Sends null packet to trigger flush mode.
218
- * Call repeatedly until it returns null.
308
+ * Signals end-of-stream and retrieves remaining frames.
309
+ * Call repeatedly until null to get all buffered frames.
310
+ * Essential for ensuring all frames are decoded.
219
311
  *
220
- * Uses avcodec_send_packet(NULL) to signal end of stream.
221
- * Retrieves buffered frames from the decoder.
312
+ * Direct mapping to avcodec_send_packet(NULL).
222
313
  *
223
- * @returns Promise resolving to Frame or null
314
+ * @returns Buffered frame or null if none remaining
224
315
  *
225
316
  * @throws {Error} If decoder is closed
226
317
  *
227
318
  * @example
228
319
  * ```typescript
229
- * // Flush all remaining frames
320
+ * // After all packets processed
230
321
  * let frame;
231
322
  * while ((frame = await decoder.flush()) !== null) {
232
- * // Process final frames
323
+ * console.log('Got buffered frame');
324
+ * await processFrame(frame);
325
+ * frame.free();
233
326
  * }
234
327
  * ```
328
+ *
329
+ * @see {@link flushFrames} For async iteration
330
+ * @see {@link frames} For complete decoding pipeline
235
331
  */
236
332
  async flush() {
237
333
  if (!this.isOpen) {
@@ -244,26 +340,27 @@ export class Decoder {
244
340
  return frame;
245
341
  }
246
342
  /**
247
- * Flush decoder and yield all remaining frames as a generator.
248
- *
249
- * More convenient than calling flush() in a loop.
250
- * Automatically sends flush signal and yields all buffered frames.
343
+ * Flush all buffered frames as async generator.
251
344
  *
252
- * IMPORTANT: The yielded frames MUST be freed by the caller!
253
- * Use 'using' statement or manually call frame.free() to avoid memory leaks.
254
- *
255
- * @returns Async generator of remaining frames
345
+ * Convenient async iteration over remaining frames.
346
+ * Automatically handles repeated flush calls.
347
+ * Useful for end-of-stream processing.
256
348
  *
349
+ * @yields Buffered frames
257
350
  * @throws {Error} If decoder is closed
258
351
  *
259
352
  * @example
260
353
  * ```typescript
261
- * // Process all remaining frames with generator
354
+ * // Flush at end of decoding
262
355
  * for await (const frame of decoder.flushFrames()) {
263
- * // Process final frame
264
- * using _ = frame; // Auto cleanup
356
+ * console.log('Processing buffered frame');
357
+ * await encoder.encode(frame);
358
+ * frame.free();
265
359
  * }
266
360
  * ```
361
+ *
362
+ * @see {@link flush} For single frame flush
363
+ * @see {@link frames} For complete pipeline
267
364
  */
268
365
  async *flushFrames() {
269
366
  if (!this.isOpen) {
@@ -275,36 +372,55 @@ export class Decoder {
275
372
  }
276
373
  }
277
374
  /**
278
- * Async iterator that decodes packets and yields frames.
375
+ * Decode packet stream to frame stream.
279
376
  *
280
- * Filters packets for this decoder's stream and yields decoded frames.
281
- * Automatically handles packet cleanup and decoder flushing.
377
+ * High-level async generator for complete decoding pipeline.
378
+ * Automatically filters packets for this stream, manages memory,
379
+ * and flushes buffered frames at end.
380
+ * Primary interface for stream-based decoding.
282
381
  *
283
- * Processes packets in sequence, decoding each and yielding frames.
284
- * After all packets are processed, flushes the decoder for remaining frames.
285
- *
286
- * IMPORTANT: The yielded frames MUST be freed by the caller!
287
- * Use 'using' statement or manually call frame.free() to avoid memory leaks.
288
- *
289
- * @param packets - Async iterable of packets (e.g., from MediaInput.packets())
290
- *
291
- * @yields Decoded frames (ownership transferred to caller)
382
+ * @param packets - Async iterable of packets
383
+ * @yields Decoded frames
384
+ * @throws {Error} If decoder is closed
385
+ * @throws {FFmpegError} If decoding fails
292
386
  *
293
387
  * @example
294
388
  * ```typescript
295
- * // RECOMMENDED: Use 'using' for automatic cleanup
296
- * for await (using frame of decoder.frames(media.packets())) {
389
+ * await using input = await MediaInput.open('video.mp4');
390
+ * using decoder = await Decoder.create(input.video());
391
+ *
392
+ * for await (const frame of decoder.frames(input.packets())) {
297
393
  * console.log(`Frame: ${frame.width}x${frame.height}`);
298
- * // Frame is automatically freed at end of iteration
394
+ * frame.free();
299
395
  * }
396
+ * ```
300
397
  *
301
- * // OR: Manual cleanup
302
- * for await (const frame of decoder.frames(media.packets())) {
303
- * console.log(`Frame: ${frame.width}x${frame.height}`);
304
- * // Process frame...
305
- * frame.free(); // MUST call free()!
398
+ * @example
399
+ * ```typescript
400
+ * for await (const frame of decoder.frames(input.packets())) {
401
+ * // Process frame
402
+ * await filter.filterFrame(frame);
403
+ *
404
+ * // Frame automatically freed
405
+ * frame.free();
306
406
  * }
307
407
  * ```
408
+ *
409
+ * @example
410
+ * ```typescript
411
+ * import { pipeline } from 'node-av/api';
412
+ *
413
+ * const control = pipeline(
414
+ * input,
415
+ * decoder,
416
+ * encoder,
417
+ * output
418
+ * );
419
+ * await control.completion;
420
+ * ```
421
+ *
422
+ * @see {@link decode} For single packet decoding
423
+ * @see {@link MediaInput.packets} For packet source
308
424
  */
309
425
  async *frames(packets) {
310
426
  if (!this.isOpen) {
@@ -335,42 +451,78 @@ export class Decoder {
335
451
  /**
336
452
  * Close decoder and free resources.
337
453
  *
338
- * After closing, the decoder cannot be used again.
454
+ * Releases codec context and internal frame buffer.
455
+ * Safe to call multiple times.
456
+ * Automatically called by Symbol.dispose.
339
457
  *
340
- * Frees the frame buffer and codec context.
341
- * Note: Does NOT dispose the HardwareContext - caller is responsible for that.
458
+ * @example
459
+ * ```typescript
460
+ * const decoder = await Decoder.create(stream);
461
+ * try {
462
+ * // Use decoder
463
+ * } finally {
464
+ * decoder.close();
465
+ * }
466
+ * ```
467
+ *
468
+ * @see {@link Symbol.dispose} For automatic cleanup
342
469
  */
343
470
  close() {
344
- if (!this.isOpen)
471
+ if (!this.isOpen) {
345
472
  return;
473
+ }
346
474
  this.frame.free();
347
475
  this.codecContext.freeContext();
348
- // NOTE: We do NOT dispose the hardware context here
349
- // The caller who created the HardwareContext is responsible for disposing it
350
- // This allows reusing the same HardwareContext for multiple decoders
351
476
  this.isOpen = false;
352
477
  }
353
478
  /**
354
- * Get the stream index this decoder is for.
479
+ * Get stream index.
480
+ *
481
+ * Returns the index of the stream being decoded.
482
+ * Used for packet filtering in multi-stream files.
483
+ *
484
+ * @returns Stream index
485
+ *
486
+ * @example
487
+ * ```typescript
488
+ * if (packet.streamIndex === decoder.getStreamIndex()) {
489
+ * const frame = await decoder.decode(packet);
490
+ * }
491
+ * ```
492
+ *
493
+ * @see {@link getStream} For full stream object
355
494
  */
356
495
  getStreamIndex() {
357
496
  return this.streamIndex;
358
497
  }
359
498
  /**
360
- * Get the original stream this decoder was created from.
361
- * Used for stream-copy operations in pipeline.
499
+ * Get stream object.
500
+ *
501
+ * Returns the underlying stream being decoded.
502
+ * Provides access to stream metadata and parameters.
503
+ *
504
+ * @returns Stream object
505
+ *
506
+ * @example
507
+ * ```typescript
508
+ * const stream = decoder.getStream();
509
+ * console.log(`Duration: ${stream.duration}`);
510
+ * console.log(`Time base: ${stream.timeBase.num}/${stream.timeBase.den}`);
511
+ * ```
512
+ *
513
+ * @see {@link Stream} For stream properties
514
+ * @see {@link getStreamIndex} For index only
362
515
  */
363
516
  getStream() {
364
517
  return this.stream;
365
518
  }
366
519
  /**
367
- * Get codec context for advanced configuration.
368
- *
369
- * Use with caution - direct manipulation may cause issues.
520
+ * Get underlying codec context.
370
521
  *
371
- * Provides access to the underlying AVCodecContext for advanced operations.
522
+ * Returns the internal codec context for advanced operations.
523
+ * Returns null if decoder is closed.
372
524
  *
373
- * @returns CodecContext or null if closed
525
+ * @returns Codec context or null
374
526
  *
375
527
  * @internal
376
528
  */
@@ -378,26 +530,26 @@ export class Decoder {
378
530
  return this.isOpen ? this.codecContext : null;
379
531
  }
380
532
  /**
381
- * Receive a frame from the decoder (internal).
533
+ * Receive frame from decoder.
382
534
  *
383
- * Internal method to receive decoded frames without conversion.
535
+ * Internal method to get decoded frames from codec.
536
+ * Handles frame cloning and error checking.
537
+ * Hardware frames include hw_frames_ctx reference.
384
538
  *
385
- * Uses avcodec_receive_frame() to get decoded frames from the codec.
386
- * Clones the frame for the user to prevent internal buffer corruption.
539
+ * Direct mapping to avcodec_receive_frame().
540
+ *
541
+ * @returns Cloned frame or null
542
+ *
543
+ * @throws {FFmpegError} If receive fails with error other than AVERROR_EAGAIN or AVERROR_EOF
387
544
  *
388
- * @returns Frame or null if no frame available
389
- * @internal
390
545
  */
391
546
  async receiveFrameInternal() {
392
547
  // Clear previous frame data
393
548
  this.frame.unref();
394
549
  const ret = await this.codecContext.receiveFrame(this.frame);
395
550
  if (ret === 0) {
396
- // Set hw_frames_ctx from frame for other components to share
397
- // This is THE moment when hw_frames_ctx becomes available
398
- if (this.hardware && this.frame.hwFramesCtx) {
399
- this.hardware.framesContext = this.frame.hwFramesCtx;
400
- }
551
+ // Note: hw_frames_ctx is now available in the frame
552
+ // Other components should get it directly from frames, not from HardwareContext
401
553
  // Got a frame, clone it for the user
402
554
  return this.frame.clone();
403
555
  }
@@ -412,10 +564,20 @@ export class Decoder {
412
564
  }
413
565
  }
414
566
  /**
415
- * Symbol.dispose for automatic cleanup.
567
+ * Dispose of decoder.
568
+ *
569
+ * Implements Disposable interface for automatic cleanup.
570
+ * Equivalent to calling close().
571
+ *
572
+ * @example
573
+ * ```typescript
574
+ * {
575
+ * using decoder = await Decoder.create(stream);
576
+ * // Decode frames...
577
+ * } // Automatically closed
578
+ * ```
416
579
  *
417
- * Implements the Disposable interface for automatic resource management.
418
- * Calls close() to free all resources.
580
+ * @see {@link close} For manual cleanup
419
581
  */
420
582
  [Symbol.dispose]() {
421
583
  this.close();