node-av 3.1.3 → 5.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 (192) hide show
  1. package/README.md +88 -52
  2. package/binding.gyp +23 -11
  3. package/dist/api/audio-frame-buffer.d.ts +201 -0
  4. package/dist/api/audio-frame-buffer.js +275 -0
  5. package/dist/api/audio-frame-buffer.js.map +1 -0
  6. package/dist/api/bitstream-filter.d.ts +320 -78
  7. package/dist/api/bitstream-filter.js +684 -151
  8. package/dist/api/bitstream-filter.js.map +1 -1
  9. package/dist/api/constants.d.ts +44 -0
  10. package/dist/api/constants.js +45 -0
  11. package/dist/api/constants.js.map +1 -0
  12. package/dist/api/data/test_av1.ivf +0 -0
  13. package/dist/api/data/test_mjpeg.mjpeg +0 -0
  14. package/dist/api/data/test_vp8.ivf +0 -0
  15. package/dist/api/data/test_vp9.ivf +0 -0
  16. package/dist/api/decoder.d.ts +454 -77
  17. package/dist/api/decoder.js +1081 -271
  18. package/dist/api/decoder.js.map +1 -1
  19. package/dist/api/{media-input.d.ts → demuxer.d.ts} +295 -45
  20. package/dist/api/demuxer.js +1965 -0
  21. package/dist/api/demuxer.js.map +1 -0
  22. package/dist/api/encoder.d.ts +423 -132
  23. package/dist/api/encoder.js +1089 -240
  24. package/dist/api/encoder.js.map +1 -1
  25. package/dist/api/filter-complex.d.ts +769 -0
  26. package/dist/api/filter-complex.js +1596 -0
  27. package/dist/api/filter-complex.js.map +1 -0
  28. package/dist/api/filter-presets.d.ts +80 -5
  29. package/dist/api/filter-presets.js +117 -7
  30. package/dist/api/filter-presets.js.map +1 -1
  31. package/dist/api/filter.d.ts +561 -125
  32. package/dist/api/filter.js +1083 -274
  33. package/dist/api/filter.js.map +1 -1
  34. package/dist/api/{fmp4.d.ts → fmp4-stream.d.ts} +141 -140
  35. package/dist/api/fmp4-stream.js +539 -0
  36. package/dist/api/fmp4-stream.js.map +1 -0
  37. package/dist/api/hardware.d.ts +58 -6
  38. package/dist/api/hardware.js +127 -11
  39. package/dist/api/hardware.js.map +1 -1
  40. package/dist/api/index.d.ts +8 -4
  41. package/dist/api/index.js +17 -8
  42. package/dist/api/index.js.map +1 -1
  43. package/dist/api/io-stream.d.ts +6 -6
  44. package/dist/api/io-stream.js +5 -4
  45. package/dist/api/io-stream.js.map +1 -1
  46. package/dist/api/{media-output.d.ts → muxer.d.ts} +280 -66
  47. package/dist/api/muxer.js +1934 -0
  48. package/dist/api/muxer.js.map +1 -0
  49. package/dist/api/pipeline.d.ts +77 -29
  50. package/dist/api/pipeline.js +449 -439
  51. package/dist/api/pipeline.js.map +1 -1
  52. package/dist/api/rtp-stream.d.ts +312 -0
  53. package/dist/api/rtp-stream.js +630 -0
  54. package/dist/api/rtp-stream.js.map +1 -0
  55. package/dist/api/types.d.ts +533 -56
  56. package/dist/api/utilities/async-queue.d.ts +91 -0
  57. package/dist/api/utilities/async-queue.js +162 -0
  58. package/dist/api/utilities/async-queue.js.map +1 -0
  59. package/dist/api/utilities/audio-sample.d.ts +11 -1
  60. package/dist/api/utilities/audio-sample.js +10 -0
  61. package/dist/api/utilities/audio-sample.js.map +1 -1
  62. package/dist/api/utilities/channel-layout.d.ts +1 -0
  63. package/dist/api/utilities/channel-layout.js +1 -0
  64. package/dist/api/utilities/channel-layout.js.map +1 -1
  65. package/dist/api/utilities/image.d.ts +39 -1
  66. package/dist/api/utilities/image.js +38 -0
  67. package/dist/api/utilities/image.js.map +1 -1
  68. package/dist/api/utilities/index.d.ts +3 -0
  69. package/dist/api/utilities/index.js +6 -0
  70. package/dist/api/utilities/index.js.map +1 -1
  71. package/dist/api/utilities/media-type.d.ts +2 -1
  72. package/dist/api/utilities/media-type.js +1 -0
  73. package/dist/api/utilities/media-type.js.map +1 -1
  74. package/dist/api/utilities/pixel-format.d.ts +4 -1
  75. package/dist/api/utilities/pixel-format.js +3 -0
  76. package/dist/api/utilities/pixel-format.js.map +1 -1
  77. package/dist/api/utilities/sample-format.d.ts +6 -1
  78. package/dist/api/utilities/sample-format.js +5 -0
  79. package/dist/api/utilities/sample-format.js.map +1 -1
  80. package/dist/api/utilities/scheduler.d.ts +138 -0
  81. package/dist/api/utilities/scheduler.js +98 -0
  82. package/dist/api/utilities/scheduler.js.map +1 -0
  83. package/dist/api/utilities/streaming.d.ts +105 -15
  84. package/dist/api/utilities/streaming.js +201 -12
  85. package/dist/api/utilities/streaming.js.map +1 -1
  86. package/dist/api/utilities/timestamp.d.ts +15 -1
  87. package/dist/api/utilities/timestamp.js +14 -0
  88. package/dist/api/utilities/timestamp.js.map +1 -1
  89. package/dist/api/utilities/whisper-model.d.ts +310 -0
  90. package/dist/api/utilities/whisper-model.js +528 -0
  91. package/dist/api/utilities/whisper-model.js.map +1 -0
  92. package/dist/api/webrtc-stream.d.ts +288 -0
  93. package/dist/api/webrtc-stream.js +440 -0
  94. package/dist/api/webrtc-stream.js.map +1 -0
  95. package/dist/api/whisper.d.ts +324 -0
  96. package/dist/api/whisper.js +362 -0
  97. package/dist/api/whisper.js.map +1 -0
  98. package/dist/constants/constants.d.ts +54 -2
  99. package/dist/constants/constants.js +48 -1
  100. package/dist/constants/constants.js.map +1 -1
  101. package/dist/constants/encoders.d.ts +2 -1
  102. package/dist/constants/encoders.js +4 -3
  103. package/dist/constants/encoders.js.map +1 -1
  104. package/dist/constants/hardware.d.ts +26 -0
  105. package/dist/constants/hardware.js +27 -0
  106. package/dist/constants/hardware.js.map +1 -0
  107. package/dist/constants/index.d.ts +1 -0
  108. package/dist/constants/index.js +1 -0
  109. package/dist/constants/index.js.map +1 -1
  110. package/dist/ffmpeg/index.d.ts +3 -3
  111. package/dist/ffmpeg/index.js +3 -3
  112. package/dist/ffmpeg/utils.d.ts +27 -0
  113. package/dist/ffmpeg/utils.js +28 -16
  114. package/dist/ffmpeg/utils.js.map +1 -1
  115. package/dist/lib/binding.d.ts +22 -11
  116. package/dist/lib/binding.js.map +1 -1
  117. package/dist/lib/codec-context.d.ts +87 -0
  118. package/dist/lib/codec-context.js +125 -4
  119. package/dist/lib/codec-context.js.map +1 -1
  120. package/dist/lib/codec-parameters.d.ts +229 -1
  121. package/dist/lib/codec-parameters.js +264 -0
  122. package/dist/lib/codec-parameters.js.map +1 -1
  123. package/dist/lib/codec-parser.d.ts +23 -0
  124. package/dist/lib/codec-parser.js +25 -0
  125. package/dist/lib/codec-parser.js.map +1 -1
  126. package/dist/lib/codec.d.ts +26 -4
  127. package/dist/lib/codec.js +35 -0
  128. package/dist/lib/codec.js.map +1 -1
  129. package/dist/lib/dictionary.js +1 -0
  130. package/dist/lib/dictionary.js.map +1 -1
  131. package/dist/lib/error.js +1 -1
  132. package/dist/lib/error.js.map +1 -1
  133. package/dist/lib/fifo.d.ts +416 -0
  134. package/dist/lib/fifo.js +453 -0
  135. package/dist/lib/fifo.js.map +1 -0
  136. package/dist/lib/filter-context.d.ts +52 -11
  137. package/dist/lib/filter-context.js +56 -12
  138. package/dist/lib/filter-context.js.map +1 -1
  139. package/dist/lib/filter-graph.d.ts +9 -0
  140. package/dist/lib/filter-graph.js +13 -0
  141. package/dist/lib/filter-graph.js.map +1 -1
  142. package/dist/lib/filter.d.ts +21 -0
  143. package/dist/lib/filter.js +28 -0
  144. package/dist/lib/filter.js.map +1 -1
  145. package/dist/lib/format-context.d.ts +48 -14
  146. package/dist/lib/format-context.js +76 -7
  147. package/dist/lib/format-context.js.map +1 -1
  148. package/dist/lib/frame.d.ts +264 -1
  149. package/dist/lib/frame.js +351 -1
  150. package/dist/lib/frame.js.map +1 -1
  151. package/dist/lib/hardware-device-context.d.ts +3 -2
  152. package/dist/lib/hardware-device-context.js.map +1 -1
  153. package/dist/lib/index.d.ts +2 -0
  154. package/dist/lib/index.js +4 -0
  155. package/dist/lib/index.js.map +1 -1
  156. package/dist/lib/input-format.d.ts +21 -0
  157. package/dist/lib/input-format.js +42 -2
  158. package/dist/lib/input-format.js.map +1 -1
  159. package/dist/lib/native-types.d.ts +76 -27
  160. package/dist/lib/option.d.ts +25 -13
  161. package/dist/lib/option.js +28 -0
  162. package/dist/lib/option.js.map +1 -1
  163. package/dist/lib/output-format.d.ts +22 -1
  164. package/dist/lib/output-format.js +28 -0
  165. package/dist/lib/output-format.js.map +1 -1
  166. package/dist/lib/packet.d.ts +35 -0
  167. package/dist/lib/packet.js +52 -2
  168. package/dist/lib/packet.js.map +1 -1
  169. package/dist/lib/rational.d.ts +18 -0
  170. package/dist/lib/rational.js +19 -0
  171. package/dist/lib/rational.js.map +1 -1
  172. package/dist/lib/stream.d.ts +126 -0
  173. package/dist/lib/stream.js +188 -5
  174. package/dist/lib/stream.js.map +1 -1
  175. package/dist/lib/sync-queue.d.ts +179 -0
  176. package/dist/lib/sync-queue.js +197 -0
  177. package/dist/lib/sync-queue.js.map +1 -0
  178. package/dist/lib/types.d.ts +49 -1
  179. package/dist/lib/utilities.d.ts +281 -53
  180. package/dist/lib/utilities.js +298 -55
  181. package/dist/lib/utilities.js.map +1 -1
  182. package/install/check.js +2 -2
  183. package/package.json +37 -26
  184. package/dist/api/fmp4.js +0 -710
  185. package/dist/api/fmp4.js.map +0 -1
  186. package/dist/api/media-input.js +0 -1075
  187. package/dist/api/media-input.js.map +0 -1
  188. package/dist/api/media-output.js +0 -1040
  189. package/dist/api/media-output.js.map +0 -1
  190. package/dist/api/webrtc.d.ts +0 -664
  191. package/dist/api/webrtc.js +0 -1132
  192. package/dist/api/webrtc.js.map +0 -1
package/README.md CHANGED
@@ -22,6 +22,7 @@ Native Node.js bindings for FFmpeg with full TypeScript support. Provides direct
22
22
  - [Low-Level API](#low-level-api)
23
23
  - [High-Level API](#high-level-api)
24
24
  - [Pipeline API](#pipeline-api)
25
+ - [Key Features](#key-features)
25
26
  - [Hardware Acceleration](#hardware-acceleration)
26
27
  - [Auto-Detection](#auto-detection)
27
28
  - [Specific Hardware](#specific-hardware)
@@ -90,10 +91,6 @@ decoderCtx.allocContext3(codec);
90
91
  ret = decoderCtx.parametersToContext(videoStream.codecpar);
91
92
  FFmpegError.throwIfError(ret, 'Could not copy codec parameters to decoder context');
92
93
 
93
- // Inform the decoder about the timebase for packet timestamps and the frame rate
94
- decoderCtx.pktTimebase = videoStream.timeBase;
95
- decoderCtx.framerate = videoStream.rFrameRate || videoStream.avgFrameRate || new Rational(25, 1);
96
-
97
94
  // Open decoder context
98
95
  ret = await decoderCtx.open2(codec, null);
99
96
  FFmpegError.throwIfError(ret, 'Could not open codec');
@@ -140,49 +137,45 @@ while (true) {
140
137
  Higher-level abstractions for common tasks like decoding, encoding, filtering, and transcoding. Easier to use while still providing access to low-level details when needed.
141
138
 
142
139
  ```typescript
143
- import { Decoder, Encoder, MediaInput, MediaOutput } from 'node-av/api';
140
+ import { Decoder, Demuxer, Encoder, HardwareContext, Muxer } from 'node-av/api';
144
141
  import { FF_ENCODER_LIBX264 } from 'node-av/constants';
145
142
 
146
- // Open media
147
- await using input = await MediaInput.open('input.mp4');
148
- await using output = await MediaOutput.open('output.mp4');
143
+ // Open Demuxer
144
+ await using input = await Demuxer.open('input.mp4');
149
145
 
150
146
  // Get video stream
151
147
  const videoStream = input.video()!;
152
148
 
149
+ // Optional, setup hardware acceleration
150
+ using hw = HardwareContext.auto();
151
+
153
152
  // Create decoder
154
- using decoder = await Decoder.create(videoStream);
153
+ using decoder = await Decoder.create(videoStream, {
154
+ hardware: hw, // Optional, use hardware acceleration if available
155
+ });
155
156
 
156
157
  // Create encoder
157
158
  using encoder = await Encoder.create(FF_ENCODER_LIBX264, {
158
- timeBase: videoStream.timeBase,
159
- frameRate: videoStream.avgFrameRate,
159
+ decoder, // Optional, copy settings from decoder
160
160
  });
161
161
 
162
- // Add stream to output
163
- const outputIndex = output.addStream(encoder);
162
+ // Open Muxer
163
+ await using output = await Muxer.open('output.mp4', {
164
+ input, // Optional, used to copy global headers and metadata
165
+ });
164
166
 
165
- // Process packets
166
- for await (using packet of input.packets(videoStream.index)) {
167
- using frame = await decoder.decode(packet);
168
- if (frame) {
169
- using encoded = await encoder.encode(frame);
170
- if (encoded) {
171
- await output.writePacket(encoded, outputIndex);
172
- }
173
- }
174
- }
167
+ // Add stream to output
168
+ const outputIndex = output.addStream(encoder, {
169
+ inputStream: videoStream, // Optional, copy settings from input stream
170
+ });
175
171
 
176
- // Flush decoder
177
- for await (using frame of decoder.flushFrames()) {
178
- using encoded = await encoder.encode(frame);
179
- if (encoded) {
180
- await output.writePacket(encoded, outputIndex);
181
- }
182
- }
172
+ // Create processing generators
173
+ const inputGenerator = input.packets(videoStream.index);
174
+ const decoderGenerator = decoder.frames(inputGenerator);
175
+ const encoderGenerator = encoder.packets(decoderGenerator);
183
176
 
184
- // Flush encoder
185
- for await (using packet of encoder.flushPackets()) {
177
+ // Process packets
178
+ for await (using packet of encoderGenerator) {
186
179
  await output.writePacket(packet, outputIndex);
187
180
  }
188
181
 
@@ -194,21 +187,57 @@ for await (using packet of encoder.flushPackets()) {
194
187
  A simple way to chain together multiple processing steps like decoding, filtering, encoding, and muxing.
195
188
 
196
189
  ```typescript
197
- import { pipeline, MediaInput, MediaOutput, Decoder, Encoder } from 'node-av/api';
190
+ import { Decoder, Demuxer, Encoder, HardwareContext, Muxer, pipeline } from 'node-av/api';
191
+ import { FF_ENCODER_LIBX264 } from 'node-av/constants';
198
192
 
199
193
  // Simple transcode pipeline: input → decoder → encoder → output
200
- const input = await MediaInput.open('input.mp4');
201
- const output = await MediaOutput.open('output.mp4');
202
- const decoder = await Decoder.create(input.video());
203
- const encoder = await Encoder.create(FF_ENCODER_LIBX264, {
204
- timeBase: videoStream.timeBase,
205
- frameRate: videoStream.avgFrameRate,
194
+
195
+ // Open Demuxer
196
+ await using input = await Demuxer.open('input.mp4');
197
+
198
+ // Get video stream
199
+ const videoStream = input.video()!;
200
+
201
+ // Optional, setup hardware acceleration
202
+ using hw = HardwareContext.auto();
203
+
204
+ // Create decoder
205
+ using decoder = await Decoder.create(videoStream, {
206
+ hardware: hw, // Optional, use hardware acceleration if available
207
+ });
208
+
209
+ // Create encoder
210
+ using encoder = await Encoder.create(FF_ENCODER_LIBX264, {
211
+ decoder, // Optional, copy settings from decoder
212
+ });
213
+
214
+ // Open Muxer
215
+ await using output = await Muxer.open('output.mp4', {
216
+ input, // Optional, used to copy global headers and metadata
206
217
  });
207
218
 
208
219
  const control = pipeline(input, decoder, encoder, output);
209
220
  await control.completion;
210
221
  ```
211
222
 
223
+ ## Key Features
224
+
225
+ Beyond basic transcoding, NodeAV provides advanced media processing capabilities:
226
+
227
+ **Speech Recognition with Whisper**
228
+ Integrate automatic speech-to-text transcription using OpenAI's Whisper model through the whisper.cpp implementation. The library handles automatic model downloading from HuggingFace, supports multiple model sizes (tiny, base, small, medium, large) for different accuracy/performance tradeoffs, and provides hardware-accelerated inference through Metal (macOS), Vulkan (cross-platform), or OpenCL backends. Transcription results include precise timestamps and can be processed in real-time from any audio source.
229
+
230
+ **Advanced Video Filtering with FilterComplexAPI**
231
+ Build sophisticated video processing pipelines using FFmpeg's complete filter ecosystem. The FilterComplexAPI provides direct access to complex filtergraphs with multiple inputs and outputs, enabling advanced operations like picture-in-picture overlays, multi-stream composition (side-by-side, grid layouts), real-time video effects, and custom processing chains. All filters support hardware acceleration where available, and filter configurations can be dynamically constructed based on runtime requirements.
232
+
233
+ **Browser Streaming**
234
+ Stream any media source directly to web browsers through fragmented MP4 (fMP4) or WebRTC protocols. The library can process inputs from RTSP cameras, local files, network streams, or custom sources and package them for browser consumption with minimal latency. Complete examples demonstrate both Media Source Extensions (MSE) based playback for on-demand content and WebRTC integration for real-time streaming scenarios.
235
+
236
+ **RTSP Backchannel / Talkback**
237
+ Implements bidirectional RTSP communication for IP camera integration. The library provides native support for RTSP backchannel streams, enabling audio transmission to camera devices. Transport is handled automatically with support for both TCP (interleaved mode) and UDP protocols, with proper RTP packet formatting and stream synchronization.
238
+
239
+ See the [Examples](#examples) section for complete implementations.
240
+
212
241
  ## Hardware Acceleration
213
242
 
214
243
  The library supports all hardware acceleration methods available in FFmpeg. The specific hardware types available depend on your FFmpeg build and system configuration.
@@ -231,8 +260,7 @@ const decoder = await Decoder.create(stream, {
231
260
  // Use with encoder (use hardware-specific codec)
232
261
  const encoderCodec = hw?.getEncoderCodec('h264') ?? FF_ENCODER_LIBX264;
233
262
  const encoder = await Encoder.create(encoderCodec, {
234
- timeBase: videoStream.timeBase,
235
- frameRate: videoStream.avgFrameRate,
263
+ decoder,
236
264
  });
237
265
  ```
238
266
 
@@ -252,7 +280,7 @@ The library provides multiple entry points for optimal tree shaking:
252
280
 
253
281
  ```typescript
254
282
  // High-Level API only - Recommended for most use cases
255
- import { MediaInput, MediaOutput, Decoder, Encoder } from 'node-av/api';
283
+ import { Muxer, Muxer, Decoder, Encoder } from 'node-av/api';
256
284
 
257
285
  // Low-Level API only - Direct FFmpeg bindings
258
286
  import { FormatContext, CodecContext, Frame, Packet } from 'node-av/lib';
@@ -272,11 +300,11 @@ import * as ffmpeg from 'node-av';
272
300
  ### From Files or Network
273
301
 
274
302
  ```typescript
275
- const media = await MediaInput.open('input.mp4');
303
+ const media = await Muxer.open('input.mp4');
276
304
 
277
305
  // or
278
306
 
279
- const media = await MediaInput.open('rtsp://example.com/stream');
307
+ const media = await Muxer.open('rtsp://example.com/stream');
280
308
  ```
281
309
 
282
310
  ### From Buffers
@@ -285,7 +313,7 @@ const media = await MediaInput.open('rtsp://example.com/stream');
285
313
  import { readFile } from 'fs/promises';
286
314
 
287
315
  const buffer = await readFile('input.mp4');
288
- const media = await MediaInput.open(buffer);
316
+ const media = await Muxer.open(buffer);
289
317
  ```
290
318
 
291
319
  ### Custom I/O Callbacks
@@ -305,7 +333,7 @@ const inputCallbacks: IOInputCallbacks = {
305
333
  }
306
334
  };
307
335
 
308
- await using input = await MediaInput.open(inputCallbacks, {
336
+ await using input = await Muxer.open(inputCallbacks, {
309
337
  format: 'mp4'
310
338
  });
311
339
  ```
@@ -314,7 +342,7 @@ await using input = await MediaInput.open(inputCallbacks, {
314
342
 
315
343
  ```typescript
316
344
  // Raw video input
317
- const rawVideo = await MediaInput.open({
345
+ const rawVideo = await Muxer.open({
318
346
  type: 'video',
319
347
  input: 'input.yuv',
320
348
  width: 1280,
@@ -324,7 +352,7 @@ const rawVideo = await MediaInput.open({
324
352
  });
325
353
 
326
354
  // Raw audio input
327
- const rawAudio = await MediaInput.open({
355
+ const rawAudio = await Muxer.open({
328
356
  type: 'audio',
329
357
  input: 'input.pcm',
330
358
  sampleRate: 48000,
@@ -342,13 +370,13 @@ The library supports automatic resource cleanup using the Disposable pattern:
342
370
  ```typescript
343
371
  // Automatic cleanup with 'using'
344
372
  {
345
- await using media = await MediaInput.open('input.mp4');
373
+ await using media = await Muxer.open('input.mp4');
346
374
  using decoder = await Decoder.create(media.video());
347
375
  // Resources automatically cleaned up at end of scope
348
376
  }
349
377
 
350
378
  // Manual cleanup
351
- const media = await MediaInput.open('input.mp4');
379
+ const media = await Muxer.open('input.mp4');
352
380
  try {
353
381
  // Process media
354
382
  } finally {
@@ -420,21 +448,29 @@ NodeAV provides direct bindings to FFmpeg's C APIs, which work with raw memory p
420
448
  | `browser-webrtc` | | | [✓](https://github.com/seydx/node-av/tree/main/examples/browser/webrtc) |
421
449
  | `api-dash` | | | [✓](https://github.com/seydx/node-av/tree/main/examples/api-dash.ts) |
422
450
  | `api-encode-decode` | | | [✓](https://github.com/seydx/node-av/tree/main/examples/api-encode-decode.ts) |
451
+ | `api-filter-complex` | | | [✓](https://github.com/seydx/node-av/tree/main/examples/api-filter-complex.ts) |
452
+ | `api-filter-complex-grid` | | | [✓](https://github.com/seydx/node-av/tree/main/examples/api-filter-complex-grid.ts) |
453
+ | `api-fmp4` | | | [✓](https://github.com/seydx/node-av/tree/main/examples/api-fmp4.ts) |
423
454
  | `api-frame-extract` | | | [✓](https://github.com/seydx/node-av/tree/main/examples/api-frame-extract.ts) |
455
+ | `api-hw-codecs` | | | [✓](https://github.com/seydx/node-av/tree/main/examples/api-hw-codecs.ts) |
424
456
  | `api-hw-decode-sw-encode` | | | [✓](https://github.com/seydx/node-av/tree/main/examples/api-hw-decode-sw-encode.ts) |
425
457
  | `api-hw-raw` | | | [✓](https://github.com/seydx/node-av/tree/main/examples/api-hw-raw.ts) |
426
458
  | `api-hw-raw-output` | | | [✓](https://github.com/seydx/node-av/tree/main/examples/api-hw-raw-output.ts) |
427
- | `api-hw-rtsp-custom-io` | | | [✓](https://github.com/seydx/node-av/tree/main/examples/api-hw-rtsp-custom-io.ts) |
428
459
  | `api-hw-rtsp` | | | [✓](https://github.com/seydx/node-av/tree/main/examples/api-hw-rtsp.ts) |
460
+ | `api-hw-stream-custom-io` | | | [✓](https://github.com/seydx/node-av/tree/main/examples/api-hw-stream-custom-io.ts) |
429
461
  | `api-hw-transcode` | | | [✓](https://github.com/seydx/node-av/tree/main/examples/api-hw-transcode.ts) |
430
462
  | `api-hw-filter-sync` | | | [✓](https://github.com/seydx/node-av/tree/main/examples/api-hw-filter-sync.ts) |
431
463
  | `api-muxing` | | | [✓](https://github.com/seydx/node-av/tree/main/examples/api-muxing.ts) |
432
464
  | `api-pipeline-hw-rtsp` | | | [✓](https://github.com/seydx/node-av/tree/main/examples/api-pipeline-hw-rtsp.ts) |
433
465
  | `api-pipeline-raw-muxing` | | | [✓](https://github.com/seydx/node-av/tree/main/examples/api-pipeline-raw-muxing.ts) |
434
- | `api-sdp` | | | [✓](https://github.com/seydx/node-av/tree/main/examples/api-sdp.ts) |
466
+ | `api-rtp` | | | [✓](https://github.com/seydx/node-av/tree/main/examples/api-rtp.ts) |
467
+ | `api-sdp-custom` | | | [✓](https://github.com/seydx/node-av/tree/main/examples/api-sdp-custom.ts) |
468
+ | `api-sdp-input` | | | [✓](https://github.com/seydx/node-av/tree/main/examples/api-sdp-input.ts) |
435
469
  | `api-stream-input` | | | [✓](https://github.com/seydx/node-av/tree/main/examples/api-stream-input.ts) |
436
470
  | `api-sw-decode-hw-encode` | | | [✓](https://github.com/seydx/node-av/tree/main/examples/api-sw-decode-hw-encode.ts) |
437
471
  | `api-sw-transcode` | | | [✓](https://github.com/seydx/node-av/tree/main/examples/api-sw-transcode.ts) |
472
+ | `api-whisper-subtitles` | | | [✓](https://github.com/seydx/node-av/tree/main/examples/api-whisper-subtitles.ts) |
473
+ | `api-whisper-transcribe` | | | [✓](https://github.com/seydx/node-av/tree/main/examples/api-whisper-transcribe.ts) |
438
474
  | `frame-utils` | | [✓](https://github.com/seydx/node-av/tree/main/examples/frame-utils.ts) | |
439
475
  | `avio-read-callback` | [✓](https://github.com/FFmpeg/FFmpeg/tree/master/doc/examples/avio_read_callback.c) | [✓](https://github.com/seydx/node-av/tree/main/examples/avio-read-callback.ts) | |
440
476
  | `decode-audio` | [✓](https://github.com/FFmpeg/FFmpeg/tree/master/doc/examples/decode_audio.c) | [✓](https://github.com/seydx/node-av/tree/main/examples/decode-audio.ts) | |
package/binding.gyp CHANGED
@@ -51,17 +51,23 @@
51
51
  "src/bindings/audio_fifo.cc",
52
52
  "src/bindings/audio_fifo_async.cc",
53
53
  "src/bindings/audio_fifo_sync.cc",
54
+ "src/bindings/fifo.cc",
55
+ "src/bindings/fifo_async.cc",
56
+ "src/bindings/fifo_sync.cc",
54
57
  "src/bindings/frame_utils.cc",
55
58
  "src/bindings/bitstream_filter.cc",
56
59
  "src/bindings/bitstream_filter_context.cc",
57
60
  "src/bindings/bitstream_filter_context_async.cc",
58
61
  "src/bindings/bitstream_filter_context_sync.cc",
59
62
  "src/bindings/option.cc",
63
+ "src/bindings/sync_queue.cc",
64
+ "externals/jellyfin-ffmpeg/fftools/sync_queue.c",
60
65
  ],
61
66
  "include_dirs": [
62
67
  "<!@(node -p \"require('node-addon-api').include\")",
63
68
  "/opt/ffbuild/prefix/include",
64
69
  "<(module_root_dir)/externals/jellyfin-ffmpeg",
70
+ "<(module_root_dir)/externals/jellyfin-ffmpeg/libavcodec",
65
71
  ],
66
72
  "library_dirs": ["/opt/ffbuild/prefix/lib"],
67
73
  "dependencies": ["<!(node -p \"require('node-addon-api').gyp\")"],
@@ -102,17 +108,21 @@
102
108
  "/opt/ffbuild/prefix/lib/libudfread.a",
103
109
  "/opt/ffbuild/prefix/lib/libz.a",
104
110
  "/opt/ffbuild/prefix/lib/liblzma.a",
105
- "<!@(ls /opt/ffbuild/prefix/lib/libNE10.a 2>/dev/null || echo '')",
106
- "<!@(ls /opt/ffbuild/prefix/lib/libwebpdemux.a 2>/dev/null || echo '')",
107
- "<!@(ls /opt/ffbuild/prefix/lib/libchromaprint.a 2>/dev/null || echo '')",
108
- "<!@(ls /opt/ffbuild/prefix/lib/libopenmpt.a 2>/dev/null || echo '')",
109
- "<!@(ls /opt/ffbuild/prefix/lib/libxml2.a 2>/dev/null || echo '')",
110
- "<!@(ls /opt/ffbuild/prefix/lib/libbz2.a 2>/dev/null || echo '')",
111
- "<!@(ls /opt/ffbuild/prefix/lib/libzvbi.a 2>/dev/null || echo '')",
112
- "<!@(ls /opt/ffbuild/prefix/lib/libzimg.a 2>/dev/null || echo '')",
113
- "<!@(ls /opt/ffbuild/prefix/lib/libgmp.a 2>/dev/null || echo '')",
114
- "<!@(ls /opt/ffbuild/prefix/lib/libssl.a 2>/dev/null || echo '')",
115
- "<!@(ls /opt/ffbuild/prefix/lib/libcrypto.a 2>/dev/null || echo '')",
111
+ "/opt/ffbuild/prefix/lib/libfdk-aac.a",
112
+ "/opt/ffbuild/prefix/lib/libNE10.a",
113
+ "/opt/ffbuild/prefix/lib/libchromaprint.a",
114
+ "/opt/ffbuild/prefix/lib/libopenmpt.a",
115
+ "/opt/ffbuild/prefix/lib/libzvbi.a",
116
+ "/opt/ffbuild/prefix/lib/libzimg.a",
117
+ "/opt/ffbuild/prefix/lib/libgmp.a",
118
+ "/opt/ffbuild/prefix/lib/libssl.a",
119
+ "/opt/ffbuild/prefix/lib/libcrypto.a",
120
+ "/opt/ffbuild/prefix/lib/libwhisper.a",
121
+ "/opt/ffbuild/prefix/lib/libggml.a",
122
+ "/opt/ffbuild/prefix/lib/libggml-base.a",
123
+ "/opt/ffbuild/prefix/lib/libggml-cpu.a",
124
+ "/opt/ffbuild/prefix/lib/libggml-blas.a",
125
+ "/opt/ffbuild/prefix/lib/libggml-metal.a",
116
126
  "-liconv",
117
127
  "-lxml2",
118
128
  "-lbz2",
@@ -129,6 +139,8 @@
129
139
  "-framework IOKit",
130
140
  "-framework OpenGL",
131
141
  "-framework Metal",
142
+ "-framework MetalKit",
143
+ "-framework Foundation",
132
144
  "-framework OpenCL",
133
145
  ],
134
146
  "xcode_settings": {
@@ -0,0 +1,201 @@
1
+ import { Frame } from '../lib/frame.js';
2
+ import type { AVSampleFormat } from '../constants/index.js';
3
+ import type { ChannelLayout } from '../lib/types.js';
4
+ /**
5
+ * Audio frame buffering utility for encoders with fixed frame size requirements.
6
+ *
7
+ * Many audio encoders (Opus, AAC, MP3, etc.) require frames with a specific number
8
+ * of samples (frame_size). This class buffers incoming frames and outputs frames
9
+ * with exactly the required size.
10
+ *
11
+ * Uses FFmpeg's AVAudioFifo internally for efficient sample buffering.
12
+ *
13
+ * @example
14
+ * ```typescript
15
+ * import { AudioFrameBuffer } from 'node-av/api';
16
+ *
17
+ * // Create buffer for 480-sample frames (e.g., Opus at 24kHz)
18
+ * using buffer = AudioFrameBuffer.create(480, AV_SAMPLE_FMT_FLT, 48000, 'mono', 1);
19
+ *
20
+ * // Push variable-sized frames from filter
21
+ * for await (const frame of filterOutput) {
22
+ * await buffer.push(frame);
23
+ *
24
+ * // Pull fixed-size frames for encoder
25
+ * let outputFrame;
26
+ * while ((outputFrame = await buffer.pull()) !== null) {
27
+ * await encoder.encode(outputFrame);
28
+ * outputFrame.free();
29
+ * }
30
+ * }
31
+ *
32
+ * // Flush remaining samples
33
+ * let outputFrame;
34
+ * while ((outputFrame = await buffer.pull()) !== null) {
35
+ * await encoder.encode(outputFrame);
36
+ * outputFrame.free();
37
+ * }
38
+ * ```
39
+ */
40
+ export declare class AudioFrameBuffer implements Disposable {
41
+ private fifo;
42
+ private frame;
43
+ private frameSize;
44
+ private nextPts;
45
+ private firstFramePts;
46
+ /**
47
+ * @param fifo - Underlying AudioFifo instance
48
+ *
49
+ * @param frameSize - Number of samples per output frame
50
+ *
51
+ * @param sampleFormat - Audio sample format
52
+ *
53
+ * @param sampleRate - Sample rate in Hz
54
+ *
55
+ * @param channelLayout - Channel layout
56
+ *
57
+ * @internal
58
+ */
59
+ private constructor();
60
+ /**
61
+ * Create an audio frame buffer.
62
+ *
63
+ * @param frameSize - Required frame size in samples
64
+ *
65
+ * @param sampleFormat - Audio sample format
66
+ *
67
+ * @param sampleRate - Sample rate in Hz
68
+ *
69
+ * @param channelLayout - Channel layout (e.g., 'mono', 'stereo')
70
+ *
71
+ * @param channels - Number of audio channels
72
+ *
73
+ * @returns Configured audio frame buffer
74
+ *
75
+ * @example
76
+ * ```typescript
77
+ * // For Opus encoder at 48kHz with 20ms frames
78
+ * const buffer = AudioFrameBuffer.create(960, AV_SAMPLE_FMT_FLT, 48000, 'mono', 1);
79
+ * ```
80
+ */
81
+ static create(frameSize: number, sampleFormat: AVSampleFormat, sampleRate: number, channelLayout: ChannelLayout, channels: number): AudioFrameBuffer;
82
+ /**
83
+ * Get number of samples currently in buffer.
84
+ *
85
+ * @returns Number of buffered samples
86
+ *
87
+ * @example
88
+ * ```typescript
89
+ * console.log(`Buffer contains ${buffer.size} samples`);
90
+ * ```
91
+ */
92
+ get size(): number;
93
+ /**
94
+ * Check if a complete frame is available.
95
+ *
96
+ * Returns true if the FIFO contains at least frameSize samples.
97
+ *
98
+ * @returns True if a full frame can be pulled
99
+ *
100
+ * @example
101
+ * ```typescript
102
+ * while (buffer.hasFrame()) {
103
+ * const frame = buffer.pull();
104
+ * // Process frame...
105
+ * }
106
+ * ```
107
+ */
108
+ hasFrame(): boolean;
109
+ /**
110
+ * Push an audio frame into the buffer asynchronously.
111
+ *
112
+ * The frame's samples are added to the internal FIFO.
113
+ * Call hasFrame() and pull() to retrieve fixed-size output frames.
114
+ *
115
+ * @param frame - Audio frame to buffer
116
+ *
117
+ * @example
118
+ * ```typescript
119
+ * await buffer.push(filterFrame);
120
+ * ```
121
+ *
122
+ * @see {@link pushSync} For synchronous version
123
+ */
124
+ push(frame: Frame): Promise<void>;
125
+ /**
126
+ * Push an audio frame into the buffer synchronously.
127
+ * Synchronous version of push.
128
+ *
129
+ * The frame's samples are added to the internal FIFO.
130
+ * Call hasFrame() and pullSync() to retrieve fixed-size output frames.
131
+ *
132
+ * @param frame - Audio frame to buffer
133
+ *
134
+ * @example
135
+ * ```typescript
136
+ * buffer.pushSync(filterFrame);
137
+ * ```
138
+ *
139
+ * @see {@link push} For async version
140
+ */
141
+ pushSync(frame: Frame): void;
142
+ /**
143
+ * Pull a fixed-size audio frame from the buffer asynchronously.
144
+ *
145
+ * Reads exactly frameSize samples from the FIFO and returns a cloned Frame.
146
+ * Returns null if not enough samples are available.
147
+ * Reuses internal frame buffer for efficiency (like Decoder does).
148
+ *
149
+ * @returns Audio frame with exactly frameSize samples, or null if insufficient samples
150
+ *
151
+ * @example
152
+ * ```typescript
153
+ * using frame = await buffer.pull();
154
+ * if (frame) {
155
+ * await encoder.encode(frame);
156
+ * }
157
+ * ```
158
+ *
159
+ * @see {@link pullSync} For synchronous version
160
+ */
161
+ pull(): Promise<Frame | null>;
162
+ /**
163
+ * Pull a fixed-size audio frame from the buffer synchronously.
164
+ * Synchronous version of pull.
165
+ *
166
+ * Reads exactly frameSize samples from the FIFO and returns a cloned Frame.
167
+ * Returns null if not enough samples are available.
168
+ * Reuses internal frame buffer for efficiency (like Decoder does).
169
+ *
170
+ * @returns Audio frame with exactly frameSize samples, or null if insufficient samples
171
+ *
172
+ * @example
173
+ * ```typescript
174
+ * using frame = buffer.pullSync();
175
+ * if (frame) {
176
+ * encoder.encodeSync(frame);
177
+ * }
178
+ * ```
179
+ *
180
+ * @see {@link pull} For async version
181
+ */
182
+ pullSync(): Frame | null;
183
+ /**
184
+ * Reset the buffer, discarding all buffered samples.
185
+ *
186
+ * @example
187
+ * ```typescript
188
+ * buffer.reset();
189
+ * ```
190
+ */
191
+ reset(): void;
192
+ /**
193
+ * Free the buffer and all resources.
194
+ *
195
+ * @example
196
+ * ```typescript
197
+ * buffer.free();
198
+ * ```
199
+ */
200
+ [Symbol.dispose](): void;
201
+ }