node-av 1.3.0 → 2.0.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 (45) hide show
  1. package/README.md +37 -38
  2. package/dist/api/bitstream-filter.d.ts +2 -2
  3. package/dist/api/bitstream-filter.js +2 -2
  4. package/dist/api/decoder.d.ts +131 -120
  5. package/dist/api/decoder.js +191 -203
  6. package/dist/api/decoder.js.map +1 -1
  7. package/dist/api/encoder.d.ts +135 -77
  8. package/dist/api/encoder.js +235 -192
  9. package/dist/api/encoder.js.map +1 -1
  10. package/dist/api/filter-presets.d.ts +408 -1534
  11. package/dist/api/filter-presets.js +1005 -2058
  12. package/dist/api/filter-presets.js.map +1 -1
  13. package/dist/api/filter.d.ts +160 -165
  14. package/dist/api/filter.js +294 -374
  15. package/dist/api/filter.js.map +1 -1
  16. package/dist/api/hardware.d.ts +8 -31
  17. package/dist/api/hardware.js +19 -70
  18. package/dist/api/hardware.js.map +1 -1
  19. package/dist/api/index.d.ts +1 -1
  20. package/dist/api/index.js +1 -1
  21. package/dist/api/index.js.map +1 -1
  22. package/dist/api/media-input.d.ts +1 -1
  23. package/dist/api/media-input.js +3 -8
  24. package/dist/api/media-input.js.map +1 -1
  25. package/dist/api/media-output.d.ts +35 -128
  26. package/dist/api/media-output.js +136 -208
  27. package/dist/api/media-output.js.map +1 -1
  28. package/dist/api/pipeline.d.ts +17 -17
  29. package/dist/api/pipeline.js +19 -42
  30. package/dist/api/pipeline.js.map +1 -1
  31. package/dist/api/types.d.ts +17 -57
  32. package/dist/lib/dictionary.d.ts +2 -2
  33. package/dist/lib/dictionary.js +2 -2
  34. package/dist/lib/dictionary.js.map +1 -1
  35. package/dist/lib/filter-context.d.ts +19 -2
  36. package/dist/lib/filter-context.js +15 -0
  37. package/dist/lib/filter-context.js.map +1 -1
  38. package/dist/lib/format-context.d.ts +18 -18
  39. package/dist/lib/format-context.js +20 -20
  40. package/dist/lib/format-context.js.map +1 -1
  41. package/dist/lib/frame.d.ts +43 -1
  42. package/dist/lib/frame.js +53 -0
  43. package/dist/lib/frame.js.map +1 -1
  44. package/package.json +17 -17
  45. package/release_notes.md +0 -29
package/README.md CHANGED
@@ -34,46 +34,45 @@ await using input = await MediaInput.open('input.mp4');
34
34
  await using output = await MediaOutput.open('output.mp4');
35
35
 
36
36
  // Get video stream
37
- const videoStream = input.video();
37
+ const videoStream = input.video()!;
38
38
 
39
- // Create decoder/encoder
40
- using decoder = await Decoder.create(videoStream!);
41
- using encoder = await Encoder.create(FF_ENCODER_LIBX264, decoder.getOutputStreamInfo(), {
42
- bitrate: '2M',
43
- gopSize: 60,
39
+ // Create decoder
40
+ using decoder = await Decoder.create(videoStream);
41
+
42
+ // Create encoder
43
+ using encoder = await Encoder.create(FF_ENCODER_LIBX264, {
44
+ timeBase: videoStream.timeBase,
45
+ frameRate: videoStream.avgFrameRate,
44
46
  });
45
47
 
46
48
  // Add stream to output
47
49
  const outputIndex = output.addStream(encoder);
48
- await output.writeHeader();
49
50
 
50
51
  // Process packets
51
- for await (const packet of input.packets()) {
52
- if (packet.streamIndex === videoStream!.index) {
53
- using frame = await decoder.decode(packet);
54
- if (frame) {
55
- using encoded = await encoder.encode(frame);
56
- if (encoded) {
57
- await output.writePacket(encoded, outputIndex);
58
- }
52
+ for await (using packet of input.packets(videoStream.index)) {
53
+ using frame = await decoder.decode(packet);
54
+ if (frame) {
55
+ using encoded = await encoder.encode(frame);
56
+ if (encoded) {
57
+ await output.writePacket(encoded, outputIndex);
59
58
  }
60
59
  }
61
60
  }
62
61
 
63
- // Flush decoder/encoder
64
- for await (const frame of decoder.flushFrames()) {
62
+ // Flush decoder
63
+ for await (using frame of decoder.flushFrames()) {
65
64
  using encoded = await encoder.encode(frame);
66
65
  if (encoded) {
67
66
  await output.writePacket(encoded, outputIndex);
68
67
  }
69
68
  }
70
69
 
71
- for await (const packet of encoder.flushPackets()) {
70
+ // Flush encoder
71
+ for await (using packet of encoder.flushPackets()) {
72
72
  await output.writePacket(packet, outputIndex);
73
73
  }
74
74
 
75
- // End
76
- await output.writeTrailer();
75
+ // Done
77
76
  ```
78
77
 
79
78
  ### Pipeline API
@@ -85,9 +84,9 @@ import { pipeline, MediaInput, MediaOutput, Decoder, Encoder } from 'node-av/api
85
84
  const input = await MediaInput.open('input.mp4');
86
85
  const output = await MediaOutput.open('output.mp4');
87
86
  const decoder = await Decoder.create(input.video());
88
- const encoder = await Encoder.create(FF_ENCODER_LIBX264, decoder.getOutputStreamInfo(), {
89
- bitrate: '2M',
90
- gopSize: 60
87
+ const encoder = await Encoder.create(FF_ENCODER_LIBX264, {
88
+ timeBase: videoStream.timeBase,
89
+ frameRate: videoStream.avgFrameRate,
91
90
  });
92
91
 
93
92
  const control = pipeline(input, decoder, encoder, output);
@@ -102,23 +101,23 @@ The library supports all hardware acceleration methods available in FFmpeg. The
102
101
 
103
102
  ```typescript
104
103
  import { HardwareContext } from 'node-av/api';
105
- import { FF_ENCODER_H264_VIDEOTOOLBOX } from 'node-av/constants';
104
+ import { FF_ENCODER_LIBX264 } from 'node-av/constants';
106
105
 
107
106
  // Automatically detect best available hardware
108
107
  const hw = HardwareContext.auto();
109
- if (hw) {
110
- console.log(`Using hardware: ${hw.deviceTypeName}`);
111
-
112
- // Use with decoder
113
- const decoder = await Decoder.create(stream, {
114
- hardware: hw
115
- });
116
-
117
- // Use with encoder (use hardware-specific codec)
118
- const encoder = await Encoder.create(FF_ENCODER_H264_VIDEOTOOLBOX, decoder.getOutputStreamInfo(), {
119
- hardware: hw
120
- });
121
- }
108
+ console.log(`Using hardware: ${hw.deviceTypeName}`);
109
+
110
+ // Use with decoder
111
+ const decoder = await Decoder.create(stream, {
112
+ hardware: hw
113
+ });
114
+
115
+ // Use with encoder (use hardware-specific codec)
116
+ const encoderCodec = hw?.getEncoderCodec('h264') ?? FF_ENCODER_LIBX264;
117
+ const encoder = await Encoder.create(encoderCodec, {
118
+ timeBase: videoStream.timeBase,
119
+ frameRate: videoStream.avgFrameRate,
120
+ });
122
121
  ```
123
122
 
124
123
  ### Specific Hardware
@@ -288,7 +287,7 @@ This project is licensed under the MIT License. See the LICENSE file for details
288
287
 
289
288
  ## Contributing
290
289
 
291
- Contributions are welcome! Please read [CONTRIBUTION.md](https://github.com/seydx/av/tree/main/CONTRIBUTION.md) for development setup, code standards, and contribution guidelines before submitting pull requests.
290
+ Contributions are welcome! Please read [CONTRIBUTING.md](https://github.com/seydx/av/tree/main/CONTRIBUTING.md) for development setup, code standards, and contribution guidelines before submitting pull requests.
292
291
 
293
292
  ## Support
294
293
 
@@ -164,7 +164,7 @@ export declare class BitStreamFilterAPI implements Disposable {
164
164
  * Yields filtered packets ready for output.
165
165
  *
166
166
  * @param packets - Async iterable of packets
167
- * @yields Filtered packets
167
+ * @yields {Packet} Filtered packets
168
168
  * @throws {Error} If filter is disposed
169
169
  *
170
170
  * @throws {FFmpegError} If filtering fails
@@ -225,7 +225,7 @@ export declare class BitStreamFilterAPI implements Disposable {
225
225
  * Convenient async iteration over remaining packets.
226
226
  * Combines flush and iteration.
227
227
  *
228
- * @yields Remaining packets
228
+ * @yields {Packet} Remaining packets
229
229
  * @throws {Error} If filter is disposed
230
230
  *
231
231
  * @example
@@ -231,7 +231,7 @@ export class BitStreamFilterAPI {
231
231
  * Yields filtered packets ready for output.
232
232
  *
233
233
  * @param packets - Async iterable of packets
234
- * @yields Filtered packets
234
+ * @yields {Packet} Filtered packets
235
235
  * @throws {Error} If filter is disposed
236
236
  *
237
237
  * @throws {FFmpegError} If filtering fails
@@ -324,7 +324,7 @@ export class BitStreamFilterAPI {
324
324
  * Convenient async iteration over remaining packets.
325
325
  * Combines flush and iteration.
326
326
  *
327
- * @yields Remaining packets
327
+ * @yields {Packet} Remaining packets
328
328
  * @throws {Error} If filter is disposed
329
329
  *
330
330
  * @example
@@ -1,6 +1,6 @@
1
- import { CodecContext, Frame } from '../lib/index.js';
1
+ import { Codec, CodecContext, Frame } from '../lib/index.js';
2
2
  import type { Packet, Stream } from '../lib/index.js';
3
- import type { DecoderOptions, StreamInfo } from './types.js';
3
+ import type { DecoderOptions } from './types.js';
4
4
  /**
5
5
  * High-level decoder for audio and video streams.
6
6
  *
@@ -45,13 +45,15 @@ import type { DecoderOptions, StreamInfo } from './types.js';
45
45
  */
46
46
  export declare class Decoder implements Disposable {
47
47
  private codecContext;
48
+ private codec;
48
49
  private frame;
49
- private streamIndex;
50
50
  private stream;
51
- private isOpen;
51
+ private initialized;
52
+ private isClosed;
52
53
  private hardware?;
53
54
  /**
54
55
  * @param codecContext - Configured codec context
56
+ * @param codec - Codec being used
55
57
  * @param stream - Media stream being decoded
56
58
  * @param hardware - Optional hardware context
57
59
  * Use {@link create} factory method
@@ -120,36 +122,21 @@ export declare class Decoder implements Disposable {
120
122
  */
121
123
  get isDecoderOpen(): boolean;
122
124
  /**
123
- * Get output stream information.
125
+ * Check if decoder has been initialized.
124
126
  *
125
- * Returns format information about decoded frames.
126
- * For hardware decoding, returns the hardware pixel format.
127
- * Essential for configuring downstream components like encoders or filters.
127
+ * Returns true if decoder is initialized (true by default for decoders).
128
+ * Decoders are pre-initialized from stream parameters.
128
129
  *
129
- * @returns Stream format information
130
+ * @returns true if decoder has been initialized
130
131
  *
131
132
  * @example
132
133
  * ```typescript
133
- * const info = decoder.getOutputStreamInfo();
134
- * if (info.type === 'video') {
135
- * console.log(`Video: ${info.width}x${info.height} @ ${info.pixelFormat}`);
136
- * console.log(`Frame rate: ${info.frameRate.num}/${info.frameRate.den}`);
134
+ * if (decoder.isDecoderInitialized) {
135
+ * console.log('Decoder is ready to process frames');
137
136
  * }
138
137
  * ```
139
- *
140
- * @example
141
- * ```typescript
142
- * const info = decoder.getOutputStreamInfo();
143
- * if (info.type === 'audio') {
144
- * console.log(`Audio: ${info.sampleRate}Hz ${info.sampleFormat}`);
145
- * console.log(`Channels: ${info.channelLayout}`);
146
- * }
147
- * ```
148
- *
149
- * @see {@link StreamInfo} For format details
150
- * @see {@link Encoder.create} For matching encoder configuration
151
138
  */
152
- getOutputStreamInfo(): StreamInfo;
139
+ get isDecoderInitialized(): boolean;
153
140
  /**
154
141
  * Check if decoder uses hardware acceleration.
155
142
  *
@@ -165,6 +152,19 @@ export declare class Decoder implements Disposable {
165
152
  * @see {@link HardwareContext} For hardware setup
166
153
  */
167
154
  isHardware(): boolean;
155
+ /**
156
+ * Check if decoder is ready for processing.
157
+ *
158
+ * @returns true if initialized and ready
159
+ *
160
+ * @example
161
+ * ```typescript
162
+ * if (decoder.isReady()) {
163
+ * const frame = await decoder.decode(packet);
164
+ * }
165
+ * ```
166
+ */
167
+ isReady(): boolean;
168
168
  /**
169
169
  * Decode a packet to a frame.
170
170
  *
@@ -193,7 +193,7 @@ export declare class Decoder implements Disposable {
193
193
  * @example
194
194
  * ```typescript
195
195
  * for await (const packet of input.packets()) {
196
- * if (packet.streamIndex === decoder.getStreamIndex()) {
196
+ * if (packet.streamIndex === decoder.getStream().index) {
197
197
  * const frame = await decoder.decode(packet);
198
198
  * if (frame) {
199
199
  * await processFrame(frame);
@@ -209,42 +209,94 @@ export declare class Decoder implements Disposable {
209
209
  */
210
210
  decode(packet: Packet): Promise<Frame | null>;
211
211
  /**
212
- * Flush decoder and get buffered frame.
212
+ * Decode packet stream to frame stream.
213
213
  *
214
- * Signals end-of-stream and retrieves remaining frames.
215
- * Call repeatedly until null to get all buffered frames.
216
- * Essential for ensuring all frames are decoded.
214
+ * High-level async generator for complete decoding pipeline.
215
+ * Automatically filters packets for this stream, manages memory,
216
+ * and flushes buffered frames at end.
217
+ * Primary interface for stream-based decoding.
217
218
  *
218
- * Direct mapping to avcodec_send_packet(NULL).
219
+ * @param packets - Async iterable of packets
220
+ * @yields {Frame} Decoded frames
221
+ * @throws {Error} If decoder is closed
219
222
  *
220
- * @returns Buffered frame or null if none remaining
223
+ * @throws {FFmpegError} If decoding fails
221
224
  *
222
- * @throws {Error} If decoder is closed
225
+ * @example
226
+ * ```typescript
227
+ * await using input = await MediaInput.open('video.mp4');
228
+ * using decoder = await Decoder.create(input.video());
229
+ *
230
+ * for await (const frame of decoder.frames(input.packets())) {
231
+ * console.log(`Frame: ${frame.width}x${frame.height}`);
232
+ * frame.free();
233
+ * }
234
+ * ```
223
235
  *
224
236
  * @example
225
237
  * ```typescript
226
- * // After all packets processed
238
+ * for await (const frame of decoder.frames(input.packets())) {
239
+ * // Process frame
240
+ * await filter.process(frame);
241
+ *
242
+ * // Frame automatically freed
243
+ * frame.free();
244
+ * }
245
+ * ```
246
+ *
247
+ * @example
248
+ * ```typescript
249
+ * import { pipeline } from 'node-av/api';
250
+ *
251
+ * const control = pipeline(
252
+ * input,
253
+ * decoder,
254
+ * encoder,
255
+ * output
256
+ * );
257
+ * await control.completion;
258
+ * ```
259
+ *
260
+ * @see {@link decode} For single packet decoding
261
+ * @see {@link MediaInput.packets} For packet source
262
+ */
263
+ frames(packets: AsyncIterable<Packet>): AsyncGenerator<Frame>;
264
+ /**
265
+ * Flush decoder and signal end-of-stream.
266
+ *
267
+ * Sends null packet to decoder to signal end-of-stream.
268
+ * Does nothing if decoder is closed.
269
+ * Must use receive() or flushFrames() to get remaining buffered frames.
270
+ *
271
+ * Direct mapping to avcodec_send_packet(NULL).
272
+ *
273
+ * @throws {FFmpegError} If flush fails
274
+ *
275
+ * @example
276
+ * ```typescript
277
+ * // Signal end of stream
278
+ * await decoder.flush();
279
+ *
280
+ * // Then get remaining frames
227
281
  * let frame;
228
- * while ((frame = await decoder.flush()) !== null) {
282
+ * while ((frame = await decoder.receive()) !== null) {
229
283
  * console.log('Got buffered frame');
230
- * await processFrame(frame);
231
284
  * frame.free();
232
285
  * }
233
286
  * ```
234
287
  *
235
- * @see {@link flushFrames} For async iteration
236
- * @see {@link frames} For complete decoding pipeline
288
+ * @see {@link flushFrames} For convenient async iteration
289
+ * @see {@link receive} For getting buffered frames
237
290
  */
238
- flush(): Promise<Frame | null>;
291
+ flush(): Promise<void>;
239
292
  /**
240
293
  * Flush all buffered frames as async generator.
241
294
  *
242
295
  * Convenient async iteration over remaining frames.
243
- * Automatically handles repeated flush calls.
296
+ * Automatically sends flush signal and retrieves buffered frames.
244
297
  * Useful for end-of-stream processing.
245
298
  *
246
- * @yields Buffered frames
247
- * @throws {Error} If decoder is closed
299
+ * @yields {Frame} Buffered frames
248
300
  *
249
301
  * @example
250
302
  * ```typescript
@@ -256,63 +308,47 @@ export declare class Decoder implements Disposable {
256
308
  * }
257
309
  * ```
258
310
  *
259
- * @see {@link flush} For single frame flush
311
+ * @see {@link flush} For signaling end-of-stream
260
312
  * @see {@link frames} For complete pipeline
261
313
  */
262
314
  flushFrames(): AsyncGenerator<Frame>;
263
315
  /**
264
- * Decode packet stream to frame stream.
316
+ * Receive frame from decoder.
265
317
  *
266
- * High-level async generator for complete decoding pipeline.
267
- * Automatically filters packets for this stream, manages memory,
268
- * and flushes buffered frames at end.
269
- * Primary interface for stream-based decoding.
318
+ * Gets decoded frames from the codec's internal buffer.
319
+ * Handles frame cloning and error checking.
320
+ * Hardware frames include hw_frames_ctx reference.
321
+ * Call repeatedly until null to drain all buffered frames.
270
322
  *
271
- * @param packets - Async iterable of packets
272
- * @yields Decoded frames
273
- * @throws {Error} If decoder is closed
323
+ * Direct mapping to avcodec_receive_frame().
274
324
  *
275
- * @throws {FFmpegError} If decoding fails
325
+ * @returns Cloned frame or null if no frames available
326
+ *
327
+ * @throws {FFmpegError} If receive fails with error other than AVERROR_EAGAIN or AVERROR_EOF
276
328
  *
277
329
  * @example
278
330
  * ```typescript
279
- * await using input = await MediaInput.open('video.mp4');
280
- * using decoder = await Decoder.create(input.video());
281
- *
282
- * for await (const frame of decoder.frames(input.packets())) {
283
- * console.log(`Frame: ${frame.width}x${frame.height}`);
331
+ * const frame = await decoder.receive();
332
+ * if (frame) {
333
+ * console.log('Got decoded frame');
284
334
  * frame.free();
285
335
  * }
286
336
  * ```
287
337
  *
288
338
  * @example
289
339
  * ```typescript
290
- * for await (const frame of decoder.frames(input.packets())) {
291
- * // Process frame
292
- * await filter.filterFrame(frame);
293
- *
294
- * // Frame automatically freed
340
+ * // Drain all buffered frames
341
+ * let frame;
342
+ * while ((frame = await decoder.receive()) !== null) {
343
+ * console.log(`Frame PTS: ${frame.pts}`);
295
344
  * frame.free();
296
345
  * }
297
346
  * ```
298
347
  *
299
- * @example
300
- * ```typescript
301
- * import { pipeline } from 'node-av/api';
302
- *
303
- * const control = pipeline(
304
- * input,
305
- * decoder,
306
- * encoder,
307
- * output
308
- * );
309
- * await control.completion;
310
- * ```
311
- *
312
- * @see {@link decode} For single packet decoding
313
- * @see {@link MediaInput.packets} For packet source
348
+ * @see {@link decode} For sending packets and receiving frames
349
+ * @see {@link flush} For signaling end-of-stream
314
350
  */
315
- frames(packets: AsyncIterable<Packet>): AsyncGenerator<Frame>;
351
+ receive(): Promise<Frame | null>;
316
352
  /**
317
353
  * Close decoder and free resources.
318
354
  *
@@ -333,24 +369,6 @@ export declare class Decoder implements Disposable {
333
369
  * @see {@link Symbol.dispose} For automatic cleanup
334
370
  */
335
371
  close(): void;
336
- /**
337
- * Get stream index.
338
- *
339
- * Returns the index of the stream being decoded.
340
- * Used for packet filtering in multi-stream files.
341
- *
342
- * @returns Stream index
343
- *
344
- * @example
345
- * ```typescript
346
- * if (packet.streamIndex === decoder.getStreamIndex()) {
347
- * const frame = await decoder.decode(packet);
348
- * }
349
- * ```
350
- *
351
- * @see {@link getStream} For full stream object
352
- */
353
- getStreamIndex(): number;
354
372
  /**
355
373
  * Get stream object.
356
374
  *
@@ -359,45 +377,38 @@ export declare class Decoder implements Disposable {
359
377
  *
360
378
  * @returns Stream object
361
379
  *
362
- * @example
363
- * ```typescript
364
- * const stream = decoder.getStream();
365
- * console.log(`Duration: ${stream.duration}`);
366
- * console.log(`Time base: ${stream.timeBase.num}/${stream.timeBase.den}`);
367
- * ```
380
+ * @internal
368
381
  *
369
- * @see {@link Stream} For stream properties
370
- * @see {@link getStreamIndex} For index only
382
+ * @see {@link Stream} For stream details
371
383
  */
372
384
  getStream(): Stream;
373
385
  /**
374
- * Get underlying codec context.
386
+ * Get decoder codec.
375
387
  *
376
- * Returns the internal codec context for advanced operations.
377
- * Returns null if decoder is closed.
388
+ * Returns the codec used by this decoder.
389
+ * Useful for checking codec capabilities and properties.
378
390
  *
379
- * @returns Codec context or null
391
+ * @returns Codec instance
380
392
  *
381
393
  * @internal
394
+ *
395
+ * @see {@link Codec} For codec details
382
396
  */
383
- getCodecContext(): CodecContext | null;
397
+ getCodec(): Codec;
384
398
  /**
385
- * Receive frame from decoder.
386
- *
387
- * Internal method to get decoded frames from codec.
388
- * Handles frame cloning and error checking.
389
- * Hardware frames include hw_frames_ctx reference.
390
- *
391
- * Direct mapping to avcodec_receive_frame().
399
+ * Get underlying codec context.
392
400
  *
393
- * @returns Cloned frame or null
401
+ * Returns the codec context for advanced operations.
402
+ * Useful for accessing low-level codec properties and settings.
403
+ * Returns null if decoder is closed.
394
404
  *
395
- * @throws {FFmpegError} If receive fails with error other than AVERROR_EAGAIN or AVERROR_EOF
405
+ * @returns Codec context or null if closed
396
406
  *
397
407
  * @internal
398
408
  *
409
+ * @see {@link CodecContext} For context details
399
410
  */
400
- private receiveFrameInternal;
411
+ getCodecContext(): CodecContext | null;
401
412
  /**
402
413
  * Dispose of decoder.
403
414
  *