node-av 1.1.0 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (134) hide show
  1. package/README.md +51 -38
  2. package/dist/api/bitstream-filter.d.ts +180 -123
  3. package/dist/api/bitstream-filter.js +180 -125
  4. package/dist/api/bitstream-filter.js.map +1 -1
  5. package/dist/api/decoder.d.ts +279 -132
  6. package/dist/api/decoder.js +285 -142
  7. package/dist/api/decoder.js.map +1 -1
  8. package/dist/api/encoder.d.ts +246 -162
  9. package/dist/api/encoder.js +272 -208
  10. package/dist/api/encoder.js.map +1 -1
  11. package/dist/api/filter-presets.d.ts +690 -94
  12. package/dist/api/filter-presets.js +686 -102
  13. package/dist/api/filter-presets.js.map +1 -1
  14. package/dist/api/filter.d.ts +249 -213
  15. package/dist/api/filter.js +252 -242
  16. package/dist/api/filter.js.map +1 -1
  17. package/dist/api/hardware.d.ts +224 -117
  18. package/dist/api/hardware.js +380 -214
  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 -61
  24. package/dist/api/io-stream.js +43 -46
  25. package/dist/api/io-stream.js.map +1 -1
  26. package/dist/api/media-input.d.ts +242 -140
  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 +206 -128
  30. package/dist/api/media-output.js +210 -128
  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 +21 -187
  36. package/dist/api/utils.d.ts +1 -2
  37. package/dist/api/utils.js +9 -0
  38. package/dist/api/utils.js.map +1 -1
  39. package/dist/lib/audio-fifo.d.ts +127 -170
  40. package/dist/lib/audio-fifo.js +130 -173
  41. package/dist/lib/audio-fifo.js.map +1 -1
  42. package/dist/lib/binding.js +5 -0
  43. package/dist/lib/binding.js.map +1 -1
  44. package/dist/lib/bitstream-filter-context.d.ts +139 -184
  45. package/dist/lib/bitstream-filter-context.js +139 -188
  46. package/dist/lib/bitstream-filter-context.js.map +1 -1
  47. package/dist/lib/bitstream-filter.d.ts +68 -54
  48. package/dist/lib/bitstream-filter.js +68 -54
  49. package/dist/lib/bitstream-filter.js.map +1 -1
  50. package/dist/lib/codec-context.d.ts +316 -380
  51. package/dist/lib/codec-context.js +316 -381
  52. package/dist/lib/codec-context.js.map +1 -1
  53. package/dist/lib/codec-parameters.d.ts +160 -170
  54. package/dist/lib/codec-parameters.js +162 -172
  55. package/dist/lib/codec-parameters.js.map +1 -1
  56. package/dist/lib/codec-parser.d.ts +91 -104
  57. package/dist/lib/codec-parser.js +92 -103
  58. package/dist/lib/codec-parser.js.map +1 -1
  59. package/dist/lib/codec.d.ts +264 -281
  60. package/dist/lib/codec.js +268 -285
  61. package/dist/lib/codec.js.map +1 -1
  62. package/dist/lib/dictionary.d.ts +149 -203
  63. package/dist/lib/dictionary.js +158 -212
  64. package/dist/lib/dictionary.js.map +1 -1
  65. package/dist/lib/error.d.ts +96 -130
  66. package/dist/lib/error.js +98 -128
  67. package/dist/lib/error.js.map +1 -1
  68. package/dist/lib/filter-context.d.ts +284 -218
  69. package/dist/lib/filter-context.js +290 -227
  70. package/dist/lib/filter-context.js.map +1 -1
  71. package/dist/lib/filter-graph.d.ts +251 -292
  72. package/dist/lib/filter-graph.js +253 -294
  73. package/dist/lib/filter-graph.js.map +1 -1
  74. package/dist/lib/filter-inout.d.ts +87 -95
  75. package/dist/lib/filter-inout.js +87 -95
  76. package/dist/lib/filter-inout.js.map +1 -1
  77. package/dist/lib/filter.d.ts +93 -111
  78. package/dist/lib/filter.js +93 -111
  79. package/dist/lib/filter.js.map +1 -1
  80. package/dist/lib/format-context.d.ts +320 -428
  81. package/dist/lib/format-context.js +313 -385
  82. package/dist/lib/format-context.js.map +1 -1
  83. package/dist/lib/frame.d.ts +262 -405
  84. package/dist/lib/frame.js +263 -408
  85. package/dist/lib/frame.js.map +1 -1
  86. package/dist/lib/hardware-device-context.d.ts +149 -203
  87. package/dist/lib/hardware-device-context.js +149 -203
  88. package/dist/lib/hardware-device-context.js.map +1 -1
  89. package/dist/lib/hardware-frames-context.d.ts +170 -180
  90. package/dist/lib/hardware-frames-context.js +171 -181
  91. package/dist/lib/hardware-frames-context.js.map +1 -1
  92. package/dist/lib/index.d.ts +2 -1
  93. package/dist/lib/index.js +2 -2
  94. package/dist/lib/index.js.map +1 -1
  95. package/dist/lib/input-format.d.ts +89 -117
  96. package/dist/lib/input-format.js +89 -117
  97. package/dist/lib/input-format.js.map +1 -1
  98. package/dist/lib/io-context.d.ts +209 -241
  99. package/dist/lib/io-context.js +220 -252
  100. package/dist/lib/io-context.js.map +1 -1
  101. package/dist/lib/log.d.ts +85 -119
  102. package/dist/lib/log.js +85 -122
  103. package/dist/lib/log.js.map +1 -1
  104. package/dist/lib/native-types.d.ts +117 -106
  105. package/dist/lib/native-types.js +0 -7
  106. package/dist/lib/native-types.js.map +1 -1
  107. package/dist/lib/option.d.ts +284 -241
  108. package/dist/lib/option.js +309 -249
  109. package/dist/lib/option.js.map +1 -1
  110. package/dist/lib/output-format.d.ts +77 -101
  111. package/dist/lib/output-format.js +77 -101
  112. package/dist/lib/output-format.js.map +1 -1
  113. package/dist/lib/packet.d.ts +172 -240
  114. package/dist/lib/packet.js +172 -241
  115. package/dist/lib/packet.js.map +1 -1
  116. package/dist/lib/rational.d.ts +0 -2
  117. package/dist/lib/rational.js +0 -2
  118. package/dist/lib/rational.js.map +1 -1
  119. package/dist/lib/software-resample-context.d.ts +241 -325
  120. package/dist/lib/software-resample-context.js +242 -326
  121. package/dist/lib/software-resample-context.js.map +1 -1
  122. package/dist/lib/software-scale-context.d.ts +129 -173
  123. package/dist/lib/software-scale-context.js +131 -175
  124. package/dist/lib/software-scale-context.js.map +1 -1
  125. package/dist/lib/stream.d.ts +87 -197
  126. package/dist/lib/stream.js +87 -197
  127. package/dist/lib/stream.js.map +1 -1
  128. package/dist/lib/utilities.d.ts +372 -181
  129. package/dist/lib/utilities.js +373 -182
  130. package/dist/lib/utilities.js.map +1 -1
  131. package/install/check.js +0 -1
  132. package/package.json +21 -12
  133. package/release_notes.md +43 -59
  134. package/CHANGELOG.md +0 -8
@@ -1,91 +1,78 @@
1
- /**
2
- * Encoder - High-level wrapper for media encoding
3
- *
4
- * Simplifies FFmpeg's encoding API with automatic codec selection,
5
- * parameter configuration, and packet management.
6
- *
7
- * Handles codec initialization, frame encoding, and packet output.
8
- * Supports hardware acceleration and zero-copy transcoding.
9
- *
10
- * @module api/encoder
11
- */
12
1
  import { AVERROR_EOF, AVMEDIA_TYPE_AUDIO, AVMEDIA_TYPE_VIDEO } from '../constants/constants.js';
13
- import { AVERROR_EAGAIN, Codec, CodecContext, FFmpegError, Packet, Rational } from '../lib/index.js';
2
+ import { AVERROR_EAGAIN, avGetPixFmtName, avGetSampleFmtName, Codec, CodecContext, FFmpegError, Packet, Rational } from '../lib/index.js';
14
3
  import { parseBitrate } from './utils.js';
15
4
  /**
16
- * High-level encoder for media streams.
17
- *
18
- * Handles codec initialization, frame encoding, and packet output.
19
- * Supports various codecs with flexible configuration options.
5
+ * High-level encoder for audio and video streams.
20
6
  *
21
- * Manages codec context lifecycle and provides automatic cleanup.
22
- * Supports hardware acceleration with shared frames context for zero-copy.
7
+ * Provides a simplified interface for encoding media frames to packets.
8
+ * Handles codec initialization, hardware acceleration setup, and packet management.
9
+ * Supports both synchronous frame-by-frame encoding and async iteration over packets.
10
+ * Essential component in media processing pipelines for converting raw frames to compressed data.
23
11
  *
24
12
  * @example
25
13
  * ```typescript
14
+ * import { Encoder } from 'node-av/api';
15
+ * import { AV_CODEC_ID_H264 } from 'node-av/constants';
16
+ *
26
17
  * // Create H.264 encoder
27
18
  * const encoder = await Encoder.create('libx264', {
19
+ * type: 'video',
28
20
  * width: 1920,
29
21
  * height: 1080,
30
- * pixelFormat: 'yuv420p',
22
+ * pixelFormat: AV_PIX_FMT_YUV420P,
23
+ * timeBase: { num: 1, den: 30 },
24
+ * frameRate: { num: 30, den: 1 }
25
+ * }, {
31
26
  * bitrate: '5M',
32
- * gopSize: 60,
33
- * options: {
34
- * preset: 'fast',
35
- * crf: 23
36
- * }
27
+ * gopSize: 60
37
28
  * });
38
29
  *
39
30
  * // Encode frames
40
31
  * const packet = await encoder.encode(frame);
41
32
  * if (packet) {
42
- * // Write packet to output
33
+ * await output.writePacket(packet);
34
+ * packet.free();
43
35
  * }
44
- *
45
- * // Flush encoder
46
- * let packet;
47
- * while ((packet = await encoder.flush()) !== null) {
48
- * // Process final packets
49
- * }
50
- * encoder.close();
51
36
  * ```
52
37
  *
53
38
  * @example
54
39
  * ```typescript
55
- * // With hardware acceleration
56
- * const hw = await HardwareContext.auto();
57
- * const encoder = await Encoder.create('h264_videotoolbox', {
58
- * width: 1920,
59
- * height: 1080,
60
- * pixelFormat: 'nv12',
61
- * bitrate: '5M',
62
- * hardware: hw
40
+ * // Hardware-accelerated encoding
41
+ * import { HardwareContext } from 'node-av/api';
42
+ * import { AV_HWDEVICE_TYPE_CUDA } from 'node-av/constants';
43
+ *
44
+ * const hw = HardwareContext.create(AV_HWDEVICE_TYPE_CUDA);
45
+ * const encoder = await Encoder.create('h264_nvenc', streamInfo, {
46
+ * hardware: hw,
47
+ * bitrate: '10M'
63
48
  * });
64
- * // ... use encoder
65
- * encoder.close(); // Also disposes hardware
66
- * hw?.dispose(); // Safe to call again (no-op)
49
+ *
50
+ * // Frames with hw_frames_ctx will be encoded on GPU
51
+ * for await (const packet of encoder.packets(frames)) {
52
+ * await output.writePacket(packet);
53
+ * packet.free();
54
+ * }
67
55
  * ```
56
+ *
57
+ * @see {@link Decoder} For decoding packets to frames
58
+ * @see {@link MediaOutput} For writing encoded packets
59
+ * @see {@link HardwareContext} For GPU acceleration
68
60
  */
69
61
  export class Encoder {
70
62
  codecContext;
71
63
  packet;
72
- codecName;
64
+ codec;
73
65
  isOpen = true;
74
- supportedFormats = [];
75
- preferredFormat;
76
- hardware; // Store reference for hardware pixel format
66
+ hardware;
77
67
  /**
78
- * Private constructor - use Encoder.create() instead.
79
- *
80
- * Initializes the encoder with a codec context and allocates a packet buffer.
81
- *
82
- * @param codecContext - Initialized codec context
83
- * @param codecName - Name of the codec
84
- * @param hardware - Optional hardware context for hardware pixel format
68
+ * @param codecContext - Configured codec context
69
+ * @param codec - Encoder codec
70
+ * @param hardware - Optional hardware context
71
+ * @internal
85
72
  */
86
- constructor(codecContext, codecName, hardware) {
73
+ constructor(codecContext, codec, hardware) {
87
74
  this.codecContext = codecContext;
88
- this.codecName = codecName;
75
+ this.codec = codec;
89
76
  this.hardware = hardware;
90
77
  this.packet = new Packet();
91
78
  this.packet.alloc();
@@ -93,37 +80,60 @@ export class Encoder {
93
80
  /**
94
81
  * Create an encoder with specified codec and options.
95
82
  *
96
- * Factory method that handles codec discovery, context setup,
97
- * and initialization.
83
+ * Initializes an encoder with the appropriate codec and configuration.
84
+ * Automatically configures parameters based on input stream info.
85
+ * Handles hardware acceleration setup if provided.
98
86
  *
99
- * Uses avcodec_find_encoder_by_name() to locate the codec,
100
- * configures the context with provided options, and opens it.
101
- * Handles hardware setup including shared frames context for zero-copy.
87
+ * Direct mapping to avcodec_find_encoder_by_name() or avcodec_find_encoder().
102
88
  *
103
- * @param encoderCodec - Codec to use for encoding
104
- * @param input - Stream or StreamInfo to copy parameters from
89
+ * @param encoderCodec - Codec name, ID, or instance to use for encoding
90
+ * @param input - Stream information to configure encoder
105
91
  * @param options - Encoder configuration options
92
+ * @returns Configured encoder instance
106
93
  *
107
- * @returns Promise resolving to configured Encoder
108
- *
109
- * @throws {Error} If codec not found or configuration fails
94
+ * @throws {Error} If encoder not found or unsupported format
95
+ * @throws {FFmpegError} If codec initialization fails
110
96
  *
111
97
  * @example
112
98
  * ```typescript
113
- * // Video encoder from stream
114
- * const videoStream = media.video();
115
- * const videoEncoder = await Encoder.create('libx264', videoStream, {
99
+ * // From decoder stream info
100
+ * const streamInfo = decoder.getOutputStreamInfo();
101
+ * const encoder = await Encoder.create('libx264', streamInfo, {
116
102
  * bitrate: '5M',
117
- * gopSize: 60
103
+ * gopSize: 60,
104
+ * options: {
105
+ * preset: 'fast',
106
+ * crf: '23'
107
+ * }
118
108
  * });
109
+ * ```
119
110
  *
120
- * // Audio encoder from stream
121
- * const audioStream = media.audio();
122
- * const audioEncoder = await Encoder.create('aac', audioStream, {
111
+ * @example
112
+ * ```typescript
113
+ * // With custom stream info
114
+ * const encoder = await Encoder.create('aac', {
115
+ * type: 'audio',
116
+ * sampleRate: 48000,
117
+ * sampleFormat: AV_SAMPLE_FMT_FLTP,
118
+ * channelLayout: AV_CH_LAYOUT_STEREO,
119
+ * timeBase: { num: 1, den: 48000 }
120
+ * }, {
123
121
  * bitrate: '192k'
124
122
  * });
123
+ * ```
125
124
  *
125
+ * @example
126
+ * ```typescript
127
+ * // Hardware encoder
128
+ * const hw = HardwareContext.auto();
129
+ * const encoder = await Encoder.create('hevc_videotoolbox', streamInfo, {
130
+ * hardware: hw,
131
+ * bitrate: '8M'
132
+ * });
126
133
  * ```
134
+ *
135
+ * @see {@link Decoder.getOutputStreamInfo} For stream info source
136
+ * @see {@link EncoderOptions} For configuration options
127
137
  */
128
138
  static async create(encoderCodec, input, options = {}) {
129
139
  let codec = null;
@@ -149,9 +159,16 @@ export class Encoder {
149
159
  // It's StreamInfo - apply manually
150
160
  if (input.type === 'video' && codec.type === AVMEDIA_TYPE_VIDEO) {
151
161
  const videoInfo = input;
162
+ const codecPixelformats = codec.pixelFormats;
163
+ if (codecPixelformats && !codecPixelformats.includes(videoInfo.pixelFormat)) {
164
+ codecContext.freeContext();
165
+ const pixelFormatName = avGetPixFmtName(videoInfo.pixelFormat) ?? 'unknown';
166
+ const codecPixFmtNames = codecPixelformats.map(avGetPixFmtName).filter(Boolean).join(', ');
167
+ throw new Error(`Unsupported pixel format for '${codecName}' encoder: ${pixelFormatName}! Supported formats: ${codecPixFmtNames}`);
168
+ }
152
169
  codecContext.width = videoInfo.width;
153
170
  codecContext.height = videoInfo.height;
154
- codecContext.pixelFormat = videoInfo.pixelFormat; // Will be overwritten if it's a hardware encoder
171
+ codecContext.pixelFormat = videoInfo.pixelFormat;
155
172
  // Set pkt_timebase and timeBase to input timebase
156
173
  codecContext.pktTimebase = new Rational(videoInfo.timeBase.num, videoInfo.timeBase.den);
157
174
  codecContext.timeBase = new Rational(videoInfo.timeBase.num, videoInfo.timeBase.den);
@@ -164,6 +181,13 @@ export class Encoder {
164
181
  }
165
182
  else if (input.type === 'audio' && codec.type === AVMEDIA_TYPE_AUDIO) {
166
183
  const audioInfo = input;
184
+ const codecSampleFormats = codec.sampleFormats;
185
+ if (codecSampleFormats && !codecSampleFormats.includes(audioInfo.sampleFormat)) {
186
+ codecContext.freeContext();
187
+ const sampleFormatName = avGetSampleFmtName(audioInfo.sampleFormat) ?? 'unknown';
188
+ const supportedFormats = codecSampleFormats.map(avGetSampleFmtName).filter(Boolean).join(', ');
189
+ throw new Error(`Unsupported sample format for '${codecName}' encoder: ${sampleFormatName}! Supported formats: ${supportedFormats}`);
190
+ }
167
191
  codecContext.sampleRate = audioInfo.sampleRate;
168
192
  codecContext.sampleFormat = audioInfo.sampleFormat;
169
193
  codecContext.channelLayout = audioInfo.channelLayout;
@@ -205,6 +229,7 @@ export class Encoder {
205
229
  }
206
230
  const isHWEncoder = codec.isHardwareAcceleratedEncoder();
207
231
  if (isHWEncoder && !options.hardware) {
232
+ codecContext.freeContext();
208
233
  throw new Error(`Hardware encoder '${codecName}' requires a hardware context`);
209
234
  }
210
235
  // Open codec
@@ -213,80 +238,79 @@ export class Encoder {
213
238
  codecContext.freeContext();
214
239
  FFmpegError.throwIfError(openRet, 'Failed to open encoder');
215
240
  }
216
- const encoder = new Encoder(codecContext, codecName, isHWEncoder ? options.hardware : undefined);
217
- // Get supported formats from codec (for validation and helpers)
218
- if (codec.pixelFormats) {
219
- encoder.supportedFormats = codec.pixelFormats;
220
- encoder.preferredFormat = encoder.supportedFormats[0];
221
- }
241
+ const encoder = new Encoder(codecContext, codec, isHWEncoder ? options.hardware : undefined);
222
242
  return encoder;
223
243
  }
224
244
  /**
225
245
  * Check if encoder is open.
246
+ *
247
+ * @example
248
+ * ```typescript
249
+ * if (encoder.isEncoderOpen) {
250
+ * const packet = await encoder.encode(frame);
251
+ * }
252
+ * ```
226
253
  */
227
254
  get isEncoderOpen() {
228
255
  return this.isOpen;
229
256
  }
230
257
  /**
231
- * Get output stream information.
258
+ * Check if encoder uses hardware acceleration.
232
259
  *
233
- * Returns the encoder output format configuration.
234
- * Useful for setting up subsequent processing stages.
260
+ * @returns true if hardware-accelerated
235
261
  *
236
- * For hardware encoders, returns the hardware pixel format even before
237
- * the first frame is encoded.
262
+ * @example
263
+ * ```typescript
264
+ * if (encoder.isHardware()) {
265
+ * console.log('Using GPU acceleration');
266
+ * }
267
+ * ```
238
268
  *
239
- * @returns StreamInfo with encoder output properties
269
+ * @see {@link HardwareContext} For hardware setup
240
270
  */
241
- getOutputStreamInfo() {
242
- if (this.codecContext.codecType === AVMEDIA_TYPE_VIDEO) {
243
- // For hardware encoders, we need to return the hardware pixel format
244
- // even if it hasn't been set yet on the codec context
245
- const pixelFormat = this.hardware?.getHardwarePixelFormat() ?? this.codecContext.pixelFormat;
246
- return {
247
- type: 'video',
248
- width: this.codecContext.width,
249
- height: this.codecContext.height,
250
- pixelFormat,
251
- timeBase: this.codecContext.timeBase,
252
- frameRate: this.codecContext.framerate,
253
- sampleAspectRatio: this.codecContext.sampleAspectRatio,
254
- };
255
- }
256
- else {
257
- // For audio
258
- return {
259
- type: 'audio',
260
- sampleRate: this.codecContext.sampleRate,
261
- sampleFormat: this.codecContext.sampleFormat,
262
- channelLayout: this.codecContext.channelLayout,
263
- timeBase: this.codecContext.timeBase,
264
- };
265
- }
271
+ isHardware() {
272
+ return !!this.hardware;
266
273
  }
267
274
  /**
268
- * Encode a frame and return a packet if available.
269
- *
270
- * Sends frame to encoder and attempts to receive a packet.
271
- * May return null if encoder needs more data.
275
+ * Encode a frame to a packet.
272
276
  *
273
- * Uses avcodec_send_frame() and avcodec_receive_packet() internally.
274
- * The encoder may buffer frames before producing packets.
277
+ * Sends a frame to the encoder and attempts to receive an encoded packet.
278
+ * Handles internal buffering - may return null if more frames needed.
279
+ * Automatically manages encoder state and hardware context binding.
275
280
  *
276
- * @param frame - Frame to encode (or null to flush)
281
+ * Direct mapping to avcodec_send_frame() and avcodec_receive_packet().
277
282
  *
278
- * @returns Promise resolving to Packet or null
283
+ * @param frame - Raw frame to encode (or null to flush)
284
+ * @returns Encoded packet or null if more data needed
279
285
  *
280
- * @throws {Error} If encoder is closed or encode fails
286
+ * @throws {Error} If encoder is closed
287
+ * @throws {FFmpegError} If encoding fails
281
288
  *
282
289
  * @example
283
290
  * ```typescript
284
291
  * const packet = await encoder.encode(frame);
285
292
  * if (packet) {
286
- * // Write packet to output
293
+ * console.log(`Encoded packet with PTS: ${packet.pts}`);
287
294
  * await output.writePacket(packet);
295
+ * packet.free();
288
296
  * }
289
297
  * ```
298
+ *
299
+ * @example
300
+ * ```typescript
301
+ * // Encode loop
302
+ * for await (const frame of decoder.frames(input.packets())) {
303
+ * const packet = await encoder.encode(frame);
304
+ * if (packet) {
305
+ * await output.writePacket(packet);
306
+ * packet.free();
307
+ * }
308
+ * frame.free();
309
+ * }
310
+ * ```
311
+ *
312
+ * @see {@link packets} For automatic frame iteration
313
+ * @see {@link flush} For end-of-stream handling
290
314
  */
291
315
  async encode(frame) {
292
316
  if (!this.isOpen) {
@@ -297,7 +321,7 @@ export class Encoder {
297
321
  if (this.hardware && frame?.hwFramesCtx && !this.codecContext.hwFramesCtx) {
298
322
  // Use the hw_frames_ctx from the frame
299
323
  this.codecContext.hwFramesCtx = frame.hwFramesCtx;
300
- this.codecContext.pixelFormat = this.hardware.getHardwarePixelFormat();
324
+ this.codecContext.pixelFormat = this.hardware.devicePixelFormat;
301
325
  }
302
326
  // Send frame to encoder
303
327
  const sendRet = await this.codecContext.sendFrame(frame);
@@ -312,33 +336,65 @@ export class Encoder {
312
336
  }
313
337
  }
314
338
  // Try to receive packet
315
- return this.receivePacket();
339
+ return await this.receivePacket();
316
340
  }
317
341
  /**
318
- * Async iterator that encodes frames and yields packets.
342
+ * Encode frame stream to packet stream.
319
343
  *
320
- * Encodes all provided frames and yields resulting packets.
321
- * Automatically handles encoder flushing at the end.
322
- * Input frames are automatically freed after encoding.
344
+ * High-level async generator for complete encoding pipeline.
345
+ * Automatically manages frame memory, encoder state,
346
+ * and flushes buffered packets at end.
347
+ * Primary interface for stream-based encoding.
323
348
  *
324
- * Processes frames in sequence, encoding each and yielding packets.
325
- * After all frames are processed, flushes the encoder for remaining packets.
326
- *
327
- * IMPORTANT: The yielded packets MUST be freed by the caller!
328
- * Input frames are automatically freed after processing.
329
- *
330
- * @param frames - Async iterable of frames to encode (will be freed automatically)
349
+ * @param frames - Async iterable of frames (freed automatically)
350
+ * @yields Encoded packets (caller must free)
351
+ * @throws {Error} If encoder is closed
352
+ * @throws {FFmpegError} If encoding fails
331
353
  *
332
- * @yields Encoded packets (ownership transferred to caller)
354
+ * @example
355
+ * ```typescript
356
+ * // Basic encoding pipeline
357
+ * for await (const packet of encoder.packets(decoder.frames(input.packets()))) {
358
+ * await output.writePacket(packet);
359
+ * packet.free(); // Must free output packets
360
+ * }
361
+ * ```
333
362
  *
334
363
  * @example
335
364
  * ```typescript
336
- * // Transcode video
337
- * for await (const packet of encoder.packets(decoder.frames(media.packets()))) {
365
+ * // With frame filtering
366
+ * async function* filteredFrames() {
367
+ * for await (const frame of decoder.frames(input.packets())) {
368
+ * await filter.filterFrame(frame);
369
+ * const filtered = await filter.getFrame();
370
+ * if (filtered) {
371
+ * yield filtered;
372
+ * }
373
+ * }
374
+ * }
375
+ *
376
+ * for await (const packet of encoder.packets(filteredFrames())) {
338
377
  * await output.writePacket(packet);
339
- * packet.free(); // Must free output packet
378
+ * packet.free();
340
379
  * }
341
380
  * ```
381
+ *
382
+ * @example
383
+ * ```typescript
384
+ * // Pipeline integration
385
+ * import { pipeline } from 'node-av/api';
386
+ *
387
+ * const control = pipeline(
388
+ * input,
389
+ * decoder,
390
+ * encoder,
391
+ * output
392
+ * );
393
+ * await control.completion;
394
+ * ```
395
+ *
396
+ * @see {@link encode} For single frame encoding
397
+ * @see {@link Decoder.frames} For frame source
342
398
  */
343
399
  async *packets(frames) {
344
400
  if (!this.isOpen) {
@@ -364,27 +420,31 @@ export class Encoder {
364
420
  }
365
421
  }
366
422
  /**
367
- * Flush encoder and get remaining packets.
423
+ * Flush encoder and get buffered packet.
368
424
  *
369
- * Sends null frame to trigger flush mode.
370
- * Call repeatedly until it returns null.
425
+ * Signals end-of-stream and retrieves remaining packets.
426
+ * Call repeatedly until null to get all buffered packets.
427
+ * Essential for ensuring all frames are encoded.
371
428
  *
372
- * Uses avcodec_send_frame(NULL) to signal end of stream.
373
- * Retrieves buffered packets from the encoder.
429
+ * Direct mapping to avcodec_send_frame(NULL).
374
430
  *
375
- * @returns Promise resolving to Packet or null
431
+ * @returns Buffered packet or null if none remaining
376
432
  *
377
433
  * @throws {Error} If encoder is closed
378
434
  *
379
435
  * @example
380
436
  * ```typescript
381
- * // Flush all remaining packets
437
+ * // Flush remaining packets
382
438
  * let packet;
383
439
  * while ((packet = await encoder.flush()) !== null) {
384
- * // Write final packets
440
+ * console.log('Got buffered packet');
385
441
  * await output.writePacket(packet);
442
+ * packet.free();
386
443
  * }
387
444
  * ```
445
+ *
446
+ * @see {@link flushPackets} For async iteration
447
+ * @see {@link packets} For complete encoding pipeline
388
448
  */
389
449
  async flush() {
390
450
  if (!this.isOpen) {
@@ -393,26 +453,30 @@ export class Encoder {
393
453
  // Send flush frame (null)
394
454
  await this.codecContext.sendFrame(null);
395
455
  // Receive packet
396
- return this.receivePacket();
456
+ return await this.receivePacket();
397
457
  }
398
458
  /**
399
- * Flush encoder and yield all remaining packets as a generator.
400
- *
401
- * More convenient than calling flush() in a loop.
402
- * Automatically sends flush signal and yields all buffered packets.
459
+ * Flush all buffered packets as async generator.
403
460
  *
404
- * @returns Async generator of remaining packets
461
+ * Convenient async iteration over remaining packets.
462
+ * Automatically handles repeated flush calls.
463
+ * Useful for end-of-stream processing.
405
464
  *
465
+ * @yields Buffered packets
406
466
  * @throws {Error} If encoder is closed
407
467
  *
408
468
  * @example
409
469
  * ```typescript
410
- * // Process all remaining packets with generator
470
+ * // Flush at end of encoding
411
471
  * for await (const packet of encoder.flushPackets()) {
412
- * await output.writePacket(packet, streamIdx);
413
- * using _ = packet; // Auto cleanup
472
+ * console.log('Processing buffered packet');
473
+ * await output.writePacket(packet);
474
+ * packet.free();
414
475
  * }
415
476
  * ```
477
+ *
478
+ * @see {@link flush} For single packet flush
479
+ * @see {@link packets} For complete pipeline
416
480
  */
417
481
  async *flushPackets() {
418
482
  if (!this.isOpen) {
@@ -426,84 +490,74 @@ export class Encoder {
426
490
  /**
427
491
  * Close encoder and free resources.
428
492
  *
429
- * After closing, the encoder cannot be used again.
493
+ * Releases codec context and internal packet buffer.
494
+ * Safe to call multiple times.
495
+ * Does NOT dispose hardware context - caller is responsible.
496
+ * Automatically called by Symbol.dispose.
430
497
  *
431
- * Frees the packet buffer and codec context.
432
- * Note: Does NOT dispose the HardwareContext - caller is responsible for that.
498
+ * @example
499
+ * ```typescript
500
+ * const encoder = await Encoder.create('libx264', streamInfo);
501
+ * try {
502
+ * // Use encoder
503
+ * } finally {
504
+ * encoder.close();
505
+ * }
506
+ * ```
507
+ *
508
+ * @see {@link Symbol.dispose} For automatic cleanup
433
509
  */
434
510
  close() {
435
511
  if (!this.isOpen)
436
512
  return;
437
513
  this.packet.free();
438
514
  this.codecContext.freeContext();
439
- // NOTE: We do NOT dispose the hardware context here anymore
440
- // The caller who created the HardwareContext is responsible for disposing it
441
- // This allows reusing the same HardwareContext for multiple encoders
442
515
  this.isOpen = false;
443
516
  }
444
517
  /**
445
- * Get the codec name.
446
- */
447
- getCodecName() {
448
- return this.codecName;
449
- }
450
- /**
451
- * Get codec context for advanced configuration.
452
- *
453
- * Use with caution - direct manipulation may cause issues.
518
+ * Get encoder codec.
454
519
  *
455
- * Provides access to the underlying AVCodecContext for advanced operations.
520
+ * Returns the codec used by this encoder.
521
+ * Useful for checking codec capabilities and properties.
456
522
  *
457
- * @returns CodecContext or null if closed
458
- *
459
- * @internal
460
- */
461
- getCodecContext() {
462
- return this.isOpen ? this.codecContext : null;
463
- }
464
- /**
465
- * Get the preferred pixel format for this encoder.
466
- *
467
- * Returns the first supported format, which is usually the most efficient.
468
- *
469
- * @returns Preferred pixel format or null if not available
523
+ * @returns Codec instance
470
524
  *
471
525
  * @example
472
526
  * ```typescript
473
- * const format = encoder.getPreferredPixelFormat();
474
- * if (format) {
475
- * console.log(`Encoder prefers format: ${format}`);
476
- * }
527
+ * const codec = encoder.getCodec();
528
+ * console.log(`Using codec: ${codec.name}`);
529
+ * console.log(`Capabilities: ${codec.capabilities}`);
477
530
  * ```
531
+ *
532
+ * @see {@link Codec} For codec properties
478
533
  */
479
- getPreferredPixelFormat() {
480
- return this.preferredFormat ?? null;
534
+ getCodec() {
535
+ return this.codec;
481
536
  }
482
537
  /**
483
- * Get all supported pixel formats for this encoder.
538
+ * Get underlying codec context.
484
539
  *
485
- * Returns a list of pixel formats that this encoder can accept.
540
+ * Returns the internal codec context for advanced operations.
541
+ * Returns null if encoder is closed.
486
542
  *
487
- * @returns Array of supported pixel formats
543
+ * @returns Codec context or null
488
544
  *
489
- * @example
490
- * ```typescript
491
- * const formats = encoder.getSupportedPixelFormats();
492
- * console.log(`Encoder supports: ${formats.join(', ')}`);
493
- * ```
545
+ * @internal
494
546
  */
495
- getSupportedPixelFormats() {
496
- return this.supportedFormats;
547
+ getCodecContext() {
548
+ return this.isOpen ? this.codecContext : null;
497
549
  }
498
550
  /**
499
- * Receive a packet from the encoder.
551
+ * Receive packet from encoder.
500
552
  *
501
- * Internal method to receive encoded packets.
553
+ * Internal method to get encoded packets from codec.
554
+ * Handles packet cloning and error checking.
502
555
  *
503
- * Uses avcodec_receive_packet() to get encoded packets from the codec.
504
- * Clones the packet for the user to prevent internal buffer corruption.
556
+ * Direct mapping to avcodec_receive_packet().
505
557
  *
506
- * @returns Packet or null if no packet available
558
+ * @returns Cloned packet or null
559
+ *
560
+ * @throws {FFmpegError} If receive fails with error other than AVERROR_EAGAIN or AVERROR_EOF
507
561
  */
508
562
  async receivePacket() {
509
563
  // Clear previous packet data
@@ -524,10 +578,20 @@ export class Encoder {
524
578
  }
525
579
  }
526
580
  /**
527
- * Symbol.dispose for automatic cleanup.
581
+ * Dispose of encoder.
582
+ *
583
+ * Implements Disposable interface for automatic cleanup.
584
+ * Equivalent to calling close().
585
+ *
586
+ * @example
587
+ * ```typescript
588
+ * {
589
+ * using encoder = await Encoder.create('libx264', streamInfo);
590
+ * // Encode frames...
591
+ * } // Automatically closed
592
+ * ```
528
593
  *
529
- * Implements the Disposable interface for automatic resource management.
530
- * Calls close() to free all resources.
594
+ * @see {@link close} For manual cleanup
531
595
  */
532
596
  [Symbol.dispose]() {
533
597
  this.close();