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,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
- 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';
1
+ import { AVERROR_EAGAIN, AVERROR_EOF, AVMEDIA_TYPE_AUDIO, AVMEDIA_TYPE_VIDEO } from '../constants/constants.js';
2
+ import { 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, FF_ENCODER_LIBX264 } from 'node-av/constants';
16
+ *
26
17
  * // Create H.264 encoder
27
- * const encoder = await Encoder.create('libx264', {
18
+ * const encoder = await Encoder.create(FF_ENCODER_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,61 @@ 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
94
+ * @throws {Error} If encoder not found or unsupported format
108
95
  *
109
- * @throws {Error} If codec not found or configuration fails
96
+ * @throws {FFmpegError} If codec initialization fails
110
97
  *
111
98
  * @example
112
99
  * ```typescript
113
- * // Video encoder from stream
114
- * const videoStream = media.video();
115
- * const videoEncoder = await Encoder.create('libx264', videoStream, {
100
+ * // From decoder stream info
101
+ * const streamInfo = decoder.getOutputStreamInfo();
102
+ * const encoder = await Encoder.create(FF_ENCODER_LIBX264, streamInfo, {
116
103
  * bitrate: '5M',
117
- * gopSize: 60
104
+ * gopSize: 60,
105
+ * options: {
106
+ * preset: 'fast',
107
+ * crf: '23'
108
+ * }
118
109
  * });
110
+ * ```
119
111
  *
120
- * // Audio encoder from stream
121
- * const audioStream = media.audio();
122
- * const audioEncoder = await Encoder.create('aac', audioStream, {
112
+ * @example
113
+ * ```typescript
114
+ * // With custom stream info
115
+ * const encoder = await Encoder.create(FF_ENCODER_AAC, {
116
+ * type: 'audio',
117
+ * sampleRate: 48000,
118
+ * sampleFormat: AV_SAMPLE_FMT_FLTP,
119
+ * channelLayout: AV_CH_LAYOUT_STEREO,
120
+ * timeBase: { num: 1, den: 48000 }
121
+ * }, {
123
122
  * bitrate: '192k'
124
123
  * });
124
+ * ```
125
125
  *
126
+ * @example
127
+ * ```typescript
128
+ * // Hardware encoder
129
+ * const hw = HardwareContext.auto();
130
+ * const encoder = await Encoder.create('hevc_videotoolbox', streamInfo, {
131
+ * hardware: hw,
132
+ * bitrate: '8M'
133
+ * });
126
134
  * ```
135
+ *
136
+ * @see {@link Decoder.getOutputStreamInfo} For stream info source
137
+ * @see {@link EncoderOptions} For configuration options
127
138
  */
128
139
  static async create(encoderCodec, input, options = {}) {
129
140
  let codec = null;
@@ -149,9 +160,16 @@ export class Encoder {
149
160
  // It's StreamInfo - apply manually
150
161
  if (input.type === 'video' && codec.type === AVMEDIA_TYPE_VIDEO) {
151
162
  const videoInfo = input;
163
+ const codecPixelformats = codec.pixelFormats;
164
+ if (codecPixelformats && !codecPixelformats.includes(videoInfo.pixelFormat)) {
165
+ codecContext.freeContext();
166
+ const pixelFormatName = avGetPixFmtName(videoInfo.pixelFormat) ?? 'unknown';
167
+ const codecPixFmtNames = codecPixelformats.map(avGetPixFmtName).filter(Boolean).join(', ');
168
+ throw new Error(`Unsupported pixel format for '${codecName}' encoder: ${pixelFormatName}! Supported formats: ${codecPixFmtNames}`);
169
+ }
152
170
  codecContext.width = videoInfo.width;
153
171
  codecContext.height = videoInfo.height;
154
- codecContext.pixelFormat = videoInfo.pixelFormat; // Will be overwritten if it's a hardware encoder
172
+ codecContext.pixelFormat = videoInfo.pixelFormat;
155
173
  // Set pkt_timebase and timeBase to input timebase
156
174
  codecContext.pktTimebase = new Rational(videoInfo.timeBase.num, videoInfo.timeBase.den);
157
175
  codecContext.timeBase = new Rational(videoInfo.timeBase.num, videoInfo.timeBase.den);
@@ -164,6 +182,13 @@ export class Encoder {
164
182
  }
165
183
  else if (input.type === 'audio' && codec.type === AVMEDIA_TYPE_AUDIO) {
166
184
  const audioInfo = input;
185
+ const codecSampleFormats = codec.sampleFormats;
186
+ if (codecSampleFormats && !codecSampleFormats.includes(audioInfo.sampleFormat)) {
187
+ codecContext.freeContext();
188
+ const sampleFormatName = avGetSampleFmtName(audioInfo.sampleFormat) ?? 'unknown';
189
+ const supportedFormats = codecSampleFormats.map(avGetSampleFmtName).filter(Boolean).join(', ');
190
+ throw new Error(`Unsupported sample format for '${codecName}' encoder: ${sampleFormatName}! Supported formats: ${supportedFormats}`);
191
+ }
167
192
  codecContext.sampleRate = audioInfo.sampleRate;
168
193
  codecContext.sampleFormat = audioInfo.sampleFormat;
169
194
  codecContext.channelLayout = audioInfo.channelLayout;
@@ -205,6 +230,7 @@ export class Encoder {
205
230
  }
206
231
  const isHWEncoder = codec.isHardwareAcceleratedEncoder();
207
232
  if (isHWEncoder && !options.hardware) {
233
+ codecContext.freeContext();
208
234
  throw new Error(`Hardware encoder '${codecName}' requires a hardware context`);
209
235
  }
210
236
  // Open codec
@@ -213,80 +239,80 @@ export class Encoder {
213
239
  codecContext.freeContext();
214
240
  FFmpegError.throwIfError(openRet, 'Failed to open encoder');
215
241
  }
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
- }
242
+ const encoder = new Encoder(codecContext, codec, isHWEncoder ? options.hardware : undefined);
222
243
  return encoder;
223
244
  }
224
245
  /**
225
246
  * Check if encoder is open.
247
+ *
248
+ * @example
249
+ * ```typescript
250
+ * if (encoder.isEncoderOpen) {
251
+ * const packet = await encoder.encode(frame);
252
+ * }
253
+ * ```
226
254
  */
227
255
  get isEncoderOpen() {
228
256
  return this.isOpen;
229
257
  }
230
258
  /**
231
- * Get output stream information.
259
+ * Check if encoder uses hardware acceleration.
232
260
  *
233
- * Returns the encoder output format configuration.
234
- * Useful for setting up subsequent processing stages.
261
+ * @returns true if hardware-accelerated
235
262
  *
236
- * For hardware encoders, returns the hardware pixel format even before
237
- * the first frame is encoded.
263
+ * @example
264
+ * ```typescript
265
+ * if (encoder.isHardware()) {
266
+ * console.log('Using GPU acceleration');
267
+ * }
268
+ * ```
238
269
  *
239
- * @returns StreamInfo with encoder output properties
270
+ * @see {@link HardwareContext} For hardware setup
240
271
  */
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
- }
272
+ isHardware() {
273
+ return !!this.hardware;
266
274
  }
267
275
  /**
268
- * Encode a frame and return a packet if available.
276
+ * Encode a frame to a packet.
269
277
  *
270
- * Sends frame to encoder and attempts to receive a packet.
271
- * May return null if encoder needs more data.
278
+ * Sends a frame to the encoder and attempts to receive an encoded packet.
279
+ * Handles internal buffering - may return null if more frames needed.
280
+ * Automatically manages encoder state and hardware context binding.
272
281
  *
273
- * Uses avcodec_send_frame() and avcodec_receive_packet() internally.
274
- * The encoder may buffer frames before producing packets.
282
+ * Direct mapping to avcodec_send_frame() and avcodec_receive_packet().
275
283
  *
276
- * @param frame - Frame to encode (or null to flush)
284
+ * @param frame - Raw frame to encode (or null to flush)
285
+ * @returns Encoded packet or null if more data needed
277
286
  *
278
- * @returns Promise resolving to Packet or null
287
+ * @throws {Error} If encoder is closed
279
288
  *
280
- * @throws {Error} If encoder is closed or encode fails
289
+ * @throws {FFmpegError} If encoding fails
281
290
  *
282
291
  * @example
283
292
  * ```typescript
284
293
  * const packet = await encoder.encode(frame);
285
294
  * if (packet) {
286
- * // Write packet to output
295
+ * console.log(`Encoded packet with PTS: ${packet.pts}`);
287
296
  * await output.writePacket(packet);
297
+ * packet.free();
298
+ * }
299
+ * ```
300
+ *
301
+ * @example
302
+ * ```typescript
303
+ * // Encode loop
304
+ * for await (const frame of decoder.frames(input.packets())) {
305
+ * const packet = await encoder.encode(frame);
306
+ * if (packet) {
307
+ * await output.writePacket(packet);
308
+ * packet.free();
309
+ * }
310
+ * frame.free();
288
311
  * }
289
312
  * ```
313
+ *
314
+ * @see {@link packets} For automatic frame iteration
315
+ * @see {@link flush} For end-of-stream handling
290
316
  */
291
317
  async encode(frame) {
292
318
  if (!this.isOpen) {
@@ -297,7 +323,7 @@ export class Encoder {
297
323
  if (this.hardware && frame?.hwFramesCtx && !this.codecContext.hwFramesCtx) {
298
324
  // Use the hw_frames_ctx from the frame
299
325
  this.codecContext.hwFramesCtx = frame.hwFramesCtx;
300
- this.codecContext.pixelFormat = this.hardware.getHardwarePixelFormat();
326
+ this.codecContext.pixelFormat = this.hardware.devicePixelFormat;
301
327
  }
302
328
  // Send frame to encoder
303
329
  const sendRet = await this.codecContext.sendFrame(frame);
@@ -312,33 +338,66 @@ export class Encoder {
312
338
  }
313
339
  }
314
340
  // Try to receive packet
315
- return this.receivePacket();
341
+ return await this.receivePacket();
316
342
  }
317
343
  /**
318
- * Async iterator that encodes frames and yields packets.
319
- *
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
+ * Encode frame stream to packet stream.
323
345
  *
324
- * Processes frames in sequence, encoding each and yielding packets.
325
- * After all frames are processed, flushes the encoder for remaining packets.
346
+ * High-level async generator for complete encoding pipeline.
347
+ * Automatically manages frame memory, encoder state,
348
+ * and flushes buffered packets at end.
349
+ * Primary interface for stream-based encoding.
326
350
  *
327
- * IMPORTANT: The yielded packets MUST be freed by the caller!
328
- * Input frames are automatically freed after processing.
351
+ * @param frames - Async iterable of frames (freed automatically)
352
+ * @yields Encoded packets (caller must free)
353
+ * @throws {Error} If encoder is closed
329
354
  *
330
- * @param frames - Async iterable of frames to encode (will be freed automatically)
355
+ * @throws {FFmpegError} If encoding fails
331
356
  *
332
- * @yields Encoded packets (ownership transferred to caller)
357
+ * @example
358
+ * ```typescript
359
+ * // Basic encoding pipeline
360
+ * for await (const packet of encoder.packets(decoder.frames(input.packets()))) {
361
+ * await output.writePacket(packet);
362
+ * packet.free(); // Must free output packets
363
+ * }
364
+ * ```
333
365
  *
334
366
  * @example
335
367
  * ```typescript
336
- * // Transcode video
337
- * for await (const packet of encoder.packets(decoder.frames(media.packets()))) {
368
+ * // With frame filtering
369
+ * async function* filteredFrames() {
370
+ * for await (const frame of decoder.frames(input.packets())) {
371
+ * await filter.filterFrame(frame);
372
+ * const filtered = await filter.getFrame();
373
+ * if (filtered) {
374
+ * yield filtered;
375
+ * }
376
+ * }
377
+ * }
378
+ *
379
+ * for await (const packet of encoder.packets(filteredFrames())) {
338
380
  * await output.writePacket(packet);
339
- * packet.free(); // Must free output packet
381
+ * packet.free();
340
382
  * }
341
383
  * ```
384
+ *
385
+ * @example
386
+ * ```typescript
387
+ * // Pipeline integration
388
+ * import { pipeline } from 'node-av/api';
389
+ *
390
+ * const control = pipeline(
391
+ * input,
392
+ * decoder,
393
+ * encoder,
394
+ * output
395
+ * );
396
+ * await control.completion;
397
+ * ```
398
+ *
399
+ * @see {@link encode} For single frame encoding
400
+ * @see {@link Decoder.frames} For frame source
342
401
  */
343
402
  async *packets(frames) {
344
403
  if (!this.isOpen) {
@@ -364,27 +423,31 @@ export class Encoder {
364
423
  }
365
424
  }
366
425
  /**
367
- * Flush encoder and get remaining packets.
426
+ * Flush encoder and get buffered packet.
368
427
  *
369
- * Sends null frame to trigger flush mode.
370
- * Call repeatedly until it returns null.
428
+ * Signals end-of-stream and retrieves remaining packets.
429
+ * Call repeatedly until null to get all buffered packets.
430
+ * Essential for ensuring all frames are encoded.
371
431
  *
372
- * Uses avcodec_send_frame(NULL) to signal end of stream.
373
- * Retrieves buffered packets from the encoder.
432
+ * Direct mapping to avcodec_send_frame(NULL).
374
433
  *
375
- * @returns Promise resolving to Packet or null
434
+ * @returns Buffered packet or null if none remaining
376
435
  *
377
436
  * @throws {Error} If encoder is closed
378
437
  *
379
438
  * @example
380
439
  * ```typescript
381
- * // Flush all remaining packets
440
+ * // Flush remaining packets
382
441
  * let packet;
383
442
  * while ((packet = await encoder.flush()) !== null) {
384
- * // Write final packets
443
+ * console.log('Got buffered packet');
385
444
  * await output.writePacket(packet);
445
+ * packet.free();
386
446
  * }
387
447
  * ```
448
+ *
449
+ * @see {@link flushPackets} For async iteration
450
+ * @see {@link packets} For complete encoding pipeline
388
451
  */
389
452
  async flush() {
390
453
  if (!this.isOpen) {
@@ -393,26 +456,30 @@ export class Encoder {
393
456
  // Send flush frame (null)
394
457
  await this.codecContext.sendFrame(null);
395
458
  // Receive packet
396
- return this.receivePacket();
459
+ return await this.receivePacket();
397
460
  }
398
461
  /**
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.
462
+ * Flush all buffered packets as async generator.
403
463
  *
404
- * @returns Async generator of remaining packets
464
+ * Convenient async iteration over remaining packets.
465
+ * Automatically handles repeated flush calls.
466
+ * Useful for end-of-stream processing.
405
467
  *
468
+ * @yields Buffered packets
406
469
  * @throws {Error} If encoder is closed
407
470
  *
408
471
  * @example
409
472
  * ```typescript
410
- * // Process all remaining packets with generator
473
+ * // Flush at end of encoding
411
474
  * for await (const packet of encoder.flushPackets()) {
412
- * await output.writePacket(packet, streamIdx);
413
- * using _ = packet; // Auto cleanup
475
+ * console.log('Processing buffered packet');
476
+ * await output.writePacket(packet);
477
+ * packet.free();
414
478
  * }
415
479
  * ```
480
+ *
481
+ * @see {@link flush} For single packet flush
482
+ * @see {@link packets} For complete pipeline
416
483
  */
417
484
  async *flushPackets() {
418
485
  if (!this.isOpen) {
@@ -426,84 +493,76 @@ export class Encoder {
426
493
  /**
427
494
  * Close encoder and free resources.
428
495
  *
429
- * After closing, the encoder cannot be used again.
496
+ * Releases codec context and internal packet buffer.
497
+ * Safe to call multiple times.
498
+ * Does NOT dispose hardware context - caller is responsible.
499
+ * Automatically called by Symbol.dispose.
430
500
  *
431
- * Frees the packet buffer and codec context.
432
- * Note: Does NOT dispose the HardwareContext - caller is responsible for that.
501
+ * @example
502
+ * ```typescript
503
+ * const encoder = await Encoder.create(FF_ENCODER_LIBX264, streamInfo);
504
+ * try {
505
+ * // Use encoder
506
+ * } finally {
507
+ * encoder.close();
508
+ * }
509
+ * ```
510
+ *
511
+ * @see {@link Symbol.dispose} For automatic cleanup
433
512
  */
434
513
  close() {
435
514
  if (!this.isOpen)
436
515
  return;
437
516
  this.packet.free();
438
517
  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
518
  this.isOpen = false;
443
519
  }
444
520
  /**
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.
521
+ * Get encoder codec.
454
522
  *
455
- * Provides access to the underlying AVCodecContext for advanced operations.
523
+ * Returns the codec used by this encoder.
524
+ * Useful for checking codec capabilities and properties.
456
525
  *
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
526
+ * @returns Codec instance
470
527
  *
471
528
  * @example
472
529
  * ```typescript
473
- * const format = encoder.getPreferredPixelFormat();
474
- * if (format) {
475
- * console.log(`Encoder prefers format: ${format}`);
476
- * }
530
+ * const codec = encoder.getCodec();
531
+ * console.log(`Using codec: ${codec.name}`);
532
+ * console.log(`Capabilities: ${codec.capabilities}`);
477
533
  * ```
534
+ *
535
+ * @see {@link Codec} For codec properties
478
536
  */
479
- getPreferredPixelFormat() {
480
- return this.preferredFormat ?? null;
537
+ getCodec() {
538
+ return this.codec;
481
539
  }
482
540
  /**
483
- * Get all supported pixel formats for this encoder.
541
+ * Get underlying codec context.
484
542
  *
485
- * Returns a list of pixel formats that this encoder can accept.
543
+ * Returns the internal codec context for advanced operations.
544
+ * Returns null if encoder is closed.
486
545
  *
487
- * @returns Array of supported pixel formats
546
+ * @returns Codec context or null
488
547
  *
489
- * @example
490
- * ```typescript
491
- * const formats = encoder.getSupportedPixelFormats();
492
- * console.log(`Encoder supports: ${formats.join(', ')}`);
493
- * ```
548
+ * @internal
494
549
  */
495
- getSupportedPixelFormats() {
496
- return this.supportedFormats;
550
+ getCodecContext() {
551
+ return this.isOpen ? this.codecContext : null;
497
552
  }
498
553
  /**
499
- * Receive a packet from the encoder.
554
+ * Receive packet from encoder.
555
+ *
556
+ * Internal method to get encoded packets from codec.
557
+ * Handles packet cloning and error checking.
558
+ *
559
+ * Direct mapping to avcodec_receive_packet().
500
560
  *
501
- * Internal method to receive encoded packets.
561
+ * @returns Cloned packet or null
502
562
  *
503
- * Uses avcodec_receive_packet() to get encoded packets from the codec.
504
- * Clones the packet for the user to prevent internal buffer corruption.
563
+ * @throws {FFmpegError} If receive fails with error other than AVERROR_EAGAIN or AVERROR_EOF
505
564
  *
506
- * @returns Packet or null if no packet available
565
+ * @internal
507
566
  */
508
567
  async receivePacket() {
509
568
  // Clear previous packet data
@@ -524,10 +583,20 @@ export class Encoder {
524
583
  }
525
584
  }
526
585
  /**
527
- * Symbol.dispose for automatic cleanup.
586
+ * Dispose of encoder.
587
+ *
588
+ * Implements Disposable interface for automatic cleanup.
589
+ * Equivalent to calling close().
590
+ *
591
+ * @example
592
+ * ```typescript
593
+ * {
594
+ * using encoder = await Encoder.create(FF_ENCODER_LIBX264, streamInfo);
595
+ * // Encode frames...
596
+ * } // Automatically closed
597
+ * ```
528
598
  *
529
- * Implements the Disposable interface for automatic resource management.
530
- * Calls close() to free all resources.
599
+ * @see {@link close} For manual cleanup
531
600
  */
532
601
  [Symbol.dispose]() {
533
602
  this.close();