@pproenca/node-webcodecs 0.1.1-alpha.0 → 0.1.1-alpha.5

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 (97) hide show
  1. package/README.md +75 -233
  2. package/binding.gyp +123 -0
  3. package/dist/audio-decoder.js +1 -2
  4. package/dist/audio-encoder.d.ts +4 -0
  5. package/dist/audio-encoder.js +28 -2
  6. package/dist/binding.d.ts +0 -2
  7. package/dist/binding.js +43 -125
  8. package/dist/control-message-queue.js +0 -1
  9. package/dist/demuxer.d.ts +7 -0
  10. package/dist/demuxer.js +9 -0
  11. package/dist/encoded-chunks.d.ts +16 -0
  12. package/dist/encoded-chunks.js +82 -2
  13. package/dist/image-decoder.js +4 -0
  14. package/dist/index.d.ts +11 -0
  15. package/dist/index.js +3 -1
  16. package/dist/native-types.d.ts +20 -0
  17. package/dist/platform.d.ts +1 -10
  18. package/dist/platform.js +1 -39
  19. package/dist/resource-manager.d.ts +1 -2
  20. package/dist/resource-manager.js +3 -17
  21. package/dist/types.d.ts +12 -0
  22. package/dist/video-decoder.d.ts +21 -0
  23. package/dist/video-decoder.js +74 -2
  24. package/dist/video-encoder.d.ts +22 -0
  25. package/dist/video-encoder.js +83 -8
  26. package/lib/audio-decoder.ts +1 -2
  27. package/lib/audio-encoder.ts +31 -2
  28. package/lib/binding.ts +45 -104
  29. package/lib/control-message-queue.ts +0 -1
  30. package/lib/demuxer.ts +10 -0
  31. package/lib/encoded-chunks.ts +90 -2
  32. package/lib/image-decoder.ts +5 -0
  33. package/lib/index.ts +3 -0
  34. package/lib/native-types.ts +22 -0
  35. package/lib/platform.ts +1 -41
  36. package/lib/resource-manager.ts +3 -19
  37. package/lib/types.ts +13 -0
  38. package/lib/video-decoder.ts +84 -2
  39. package/lib/video-encoder.ts +90 -8
  40. package/package.json +49 -32
  41. package/src/addon.cc +57 -0
  42. package/src/async_decode_worker.cc +241 -33
  43. package/src/async_decode_worker.h +55 -3
  44. package/src/async_encode_worker.cc +103 -35
  45. package/src/async_encode_worker.h +23 -4
  46. package/src/audio_data.cc +38 -15
  47. package/src/audio_data.h +1 -0
  48. package/src/audio_decoder.cc +24 -3
  49. package/src/audio_encoder.cc +55 -4
  50. package/src/common.cc +125 -17
  51. package/src/common.h +34 -4
  52. package/src/demuxer.cc +16 -2
  53. package/src/encoded_audio_chunk.cc +10 -0
  54. package/src/encoded_audio_chunk.h +2 -0
  55. package/src/encoded_video_chunk.h +1 -0
  56. package/src/error_builder.cc +0 -4
  57. package/src/image_decoder.cc +127 -90
  58. package/src/image_decoder.h +11 -4
  59. package/src/muxer.cc +1 -0
  60. package/src/test_video_generator.cc +3 -2
  61. package/src/video_decoder.cc +169 -19
  62. package/src/video_decoder.h +9 -11
  63. package/src/video_encoder.cc +389 -32
  64. package/src/video_encoder.h +15 -0
  65. package/src/video_filter.cc +22 -11
  66. package/src/video_frame.cc +160 -5
  67. package/src/warnings.cc +0 -4
  68. package/dist/audio-data.js.map +0 -1
  69. package/dist/audio-decoder.js.map +0 -1
  70. package/dist/audio-encoder.js.map +0 -1
  71. package/dist/binding.js.map +0 -1
  72. package/dist/codec-base.js.map +0 -1
  73. package/dist/control-message-queue.js.map +0 -1
  74. package/dist/demuxer.js.map +0 -1
  75. package/dist/encoded-chunks.js.map +0 -1
  76. package/dist/errors.js.map +0 -1
  77. package/dist/ffmpeg.d.ts +0 -21
  78. package/dist/ffmpeg.js +0 -112
  79. package/dist/image-decoder.js.map +0 -1
  80. package/dist/image-track-list.js.map +0 -1
  81. package/dist/image-track.js.map +0 -1
  82. package/dist/index.js.map +0 -1
  83. package/dist/is.js.map +0 -1
  84. package/dist/muxer.js.map +0 -1
  85. package/dist/native-types.js.map +0 -1
  86. package/dist/platform.js.map +0 -1
  87. package/dist/resource-manager.js.map +0 -1
  88. package/dist/test-video-generator.js.map +0 -1
  89. package/dist/transfer.js.map +0 -1
  90. package/dist/types.js.map +0 -1
  91. package/dist/video-decoder.js.map +0 -1
  92. package/dist/video-encoder.js.map +0 -1
  93. package/dist/video-filter.js.map +0 -1
  94. package/dist/video-frame.js.map +0 -1
  95. package/install/build.js +0 -51
  96. package/install/check.js +0 -192
  97. package/lib/ffmpeg.ts +0 -78
package/README.md CHANGED
@@ -1,24 +1,19 @@
1
1
  # node-webcodecs
2
2
 
3
- W3C WebCodecs API implementation for Node.js - encode and decode video/audio with browser-compatible APIs.
3
+ WebCodecs API for Node.js encode and decode video/audio with browser-compatible APIs, powered by FFmpeg.
4
4
 
5
- [![Tests](https://img.shields.io/badge/tests-428%2F442%20passing-brightgreen)](test/)
6
5
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
7
- [![Node.js](https://img.shields.io/badge/node-%3E%3D18-blue)](package.json)
6
+ [![Node.js](https://img.shields.io/badge/node-%3E%3D20-blue)](package.json)
8
7
 
9
8
  ## Why node-webcodecs?
10
9
 
11
- | Feature | node-webcodecs | Browser WebCodecs |
12
- |---------|----------------|-------------------|
13
- | **Video Codecs** | H.264, H.265, VP8, VP9, AV1 | Varies by browser |
14
- | **Audio Codecs** | AAC, Opus, MP3*, FLAC* | Varies by browser |
15
- | **Container Muxing** | MP4 | Not in spec |
16
- | **Demuxing** | Any FFmpeg format | Not in spec |
17
- | **Image Decoding** | JPEG, PNG, WebP, GIF (animated) | Same |
18
- | **Pixel Formats** | 20+ formats (8/10/12-bit) | Limited |
19
- | **Server-side** | ✅ | ❌ Browser only |
20
-
21
- *\* Decode only*
10
+ | Feature | node-webcodecs | Browser WebCodecs |
11
+ | -------------------- | --------------------------- | ----------------- |
12
+ | **Video Codecs** | H.264, H.265, VP8, VP9, AV1 | Varies by browser |
13
+ | **Audio Codecs** | AAC, Opus, MP3, FLAC | Varies by browser |
14
+ | **Container Muxing** | MP4 | Not in spec |
15
+ | **Demuxing** | Any FFmpeg format | Not in spec |
16
+ | **Server-side** | Yes | Browser only |
22
17
 
23
18
  ## Installation
24
19
 
@@ -26,309 +21,156 @@ W3C WebCodecs API implementation for Node.js - encode and decode video/audio wit
26
21
  npm install @pproenca/node-webcodecs
27
22
  ```
28
23
 
29
- **That's it!** Prebuilt binaries with FFmpeg included are available for:
30
-
31
- | Platform | Architecture |
32
- |----------|--------------|
33
- | macOS | Apple Silicon (arm64), Intel (x64) |
34
- | Linux | x64 (glibc), x64 (musl/Alpine) |
35
- | Windows | x64 |
36
-
37
- ### Building from Source
24
+ Prebuilt binaries with FFmpeg statically linked are included for:
38
25
 
39
- For other platforms, or if you prefer to build from source, install FFmpeg development libraries:
26
+ - macOS ARM64 (Apple Silicon)
27
+ - macOS x64 (Intel)
28
+ - Linux x64 (musl, fully static)
40
29
 
41
30
  <details>
42
- <summary><strong>macOS</strong></summary>
43
-
44
- ```bash
45
- brew install ffmpeg pkg-config
46
- npm install @pproenca/node-webcodecs
47
- ```
48
- </details>
31
+ <summary><strong>Building from Source</strong></summary>
49
32
 
50
- <details>
51
- <summary><strong>Ubuntu/Debian</strong></summary>
33
+ For other platforms or to force a source build:
52
34
 
53
35
  ```bash
54
- sudo apt-get install \
55
- libavcodec-dev libavformat-dev libavutil-dev \
56
- libswscale-dev libswresample-dev libavfilter-dev \
57
- pkg-config
58
- npm install @pproenca/node-webcodecs
36
+ npm install @pproenca/node-webcodecs --build-from-source
59
37
  ```
60
- </details>
61
38
 
62
- <details>
63
- <summary><strong>Fedora/RHEL</strong></summary>
39
+ This requires FFmpeg 5.0+ development libraries:
40
+
41
+ **macOS:**
64
42
 
65
43
  ```bash
66
- sudo dnf install ffmpeg-devel pkg-config
67
- npm install @pproenca/node-webcodecs
44
+ brew install ffmpeg pkg-config
68
45
  ```
69
- </details>
70
46
 
71
- <details>
72
- <summary><strong>Windows</strong></summary>
47
+ **Ubuntu/Debian:**
73
48
 
74
49
  ```bash
75
- # Using vcpkg
76
- vcpkg install ffmpeg
77
- npm install @pproenca/node-webcodecs
50
+ sudo apt-get install libavcodec-dev libavformat-dev libavutil-dev \
51
+ libswscale-dev libswresample-dev libavfilter-dev pkg-config
78
52
  ```
79
- </details>
80
53
 
81
- To force building from source even when prebuilts are available:
54
+ **Fedora/RHEL:**
82
55
 
83
56
  ```bash
84
- npm install @pproenca/node-webcodecs --build-from-source
57
+ sudo dnf install ffmpeg-devel pkg-config
85
58
  ```
86
59
 
60
+ </details>
61
+
87
62
  ## Quick Start
88
63
 
89
- ### Encode Video Frames
64
+ ### Encode
90
65
 
91
66
  ```javascript
92
- import { VideoEncoder, VideoFrame } from '@pproenca/node-webcodecs';
93
-
94
- const chunks = [];
67
+ import { VideoEncoder, VideoFrame } from "@pproenca/node-webcodecs";
95
68
 
96
69
  const encoder = new VideoEncoder({
97
- output: (chunk, metadata) => {
98
- chunks.push(chunk);
99
- if (metadata?.decoderConfig?.description) {
100
- // Save codec extradata for container
101
- }
102
- },
103
- error: (e) => console.error('Encode error:', e),
70
+ output: (chunk) => chunks.push(chunk),
71
+ error: console.error,
104
72
  });
105
73
 
106
74
  encoder.configure({
107
- codec: 'avc1.42001e', // H.264 Baseline
75
+ codec: "avc1.42001e",
108
76
  width: 1920,
109
77
  height: 1080,
110
78
  bitrate: 5_000_000,
111
- framerate: 30,
112
79
  });
113
80
 
114
- // Create frames from RGBA buffers
115
- for (let i = 0; i < 60; i++) {
116
- const frame = new VideoFrame(rgbaBuffer, {
117
- format: 'RGBA',
118
- codedWidth: 1920,
119
- codedHeight: 1080,
120
- timestamp: i * (1_000_000 / 30), // microseconds
121
- });
122
-
123
- encoder.encode(frame, { keyFrame: i === 0 });
124
- frame.close();
125
- }
81
+ const frame = new VideoFrame(rgbaBuffer, {
82
+ format: "RGBA",
83
+ codedWidth: 1920,
84
+ codedHeight: 1080,
85
+ timestamp: 0,
86
+ });
126
87
 
88
+ encoder.encode(frame);
89
+ frame.close();
127
90
  await encoder.flush();
128
- encoder.close();
129
91
  ```
130
92
 
131
- ### Decode Video Chunks
93
+ ### Decode
132
94
 
133
95
  ```javascript
134
- import { VideoDecoder, EncodedVideoChunk } from '@pproenca/node-webcodecs';
96
+ import { VideoDecoder, EncodedVideoChunk } from "@pproenca/node-webcodecs";
135
97
 
136
98
  const decoder = new VideoDecoder({
137
99
  output: (frame) => {
138
- console.log(`Frame: ${frame.codedWidth}x${frame.codedHeight}`);
139
- // Process frame data...
100
+ // Process frame...
140
101
  frame.close();
141
102
  },
142
- error: (e) => console.error('Decode error:', e),
103
+ error: console.error,
143
104
  });
144
105
 
145
106
  decoder.configure({
146
- codec: 'avc1.42001e',
107
+ codec: "avc1.42001e",
147
108
  codedWidth: 1920,
148
109
  codedHeight: 1080,
149
- description: codecExtradata, // From container or encoder
150
110
  });
151
111
 
152
- for (const chunk of encodedChunks) {
153
- decoder.decode(new EncodedVideoChunk({
154
- type: chunk.isKeyframe ? 'key' : 'delta',
155
- timestamp: chunk.timestamp,
156
- data: chunk.data,
157
- }));
158
- }
159
-
112
+ decoder.decode(new EncodedVideoChunk({ type: "key", timestamp: 0, data }));
160
113
  await decoder.flush();
161
- decoder.close();
162
114
  ```
163
115
 
164
- ### Mux to MP4 Container
116
+ ### Mux to MP4
165
117
 
166
118
  ```javascript
167
- import { Muxer, VideoEncoder, VideoFrame } from '@pproenca/node-webcodecs';
119
+ import { Muxer } from "@pproenca/node-webcodecs";
168
120
 
169
- const muxer = new Muxer({ filename: 'output.mp4' });
170
- let videoTrackId;
121
+ const muxer = new Muxer({ filename: "output.mp4" });
171
122
 
172
- const encoder = new VideoEncoder({
173
- output: (chunk, metadata) => {
174
- if (metadata?.decoderConfig?.description) {
175
- // Add video track on first keyframe
176
- videoTrackId = muxer.addVideoTrack({
177
- codec: 'avc1.42001e',
178
- width: 1920,
179
- height: 1080,
180
- description: metadata.decoderConfig.description,
181
- });
182
- }
183
- muxer.writeVideoChunk(videoTrackId, chunk);
184
- },
185
- error: console.error,
186
- });
187
-
188
- encoder.configure({
189
- codec: 'avc1.42001e',
123
+ muxer.addVideoTrack({
124
+ codec: "avc1.42001e",
190
125
  width: 1920,
191
126
  height: 1080,
192
- bitrate: 5_000_000,
193
- avc: { format: 'avc' }, // Use AVCC format for MP4
127
+ description: codecDescription,
194
128
  });
195
129
 
196
- // Encode frames...
197
- await encoder.flush();
198
- encoder.close();
130
+ muxer.writeVideoChunk(chunk);
199
131
  muxer.finalize();
200
132
  ```
201
133
 
202
- ### Demux from Video File
134
+ ### Demux
203
135
 
204
136
  ```javascript
205
- import { Demuxer } from '@pproenca/node-webcodecs';
137
+ import { Demuxer } from "@pproenca/node-webcodecs";
206
138
 
207
139
  const demuxer = new Demuxer({
208
- onTrack: (track) => {
209
- console.log(`Track ${track.index}: ${track.type} (${track.codec})`);
210
- if (track.type === 'video') {
211
- // Configure decoder with track.extradata
212
- }
213
- },
214
- onChunk: (chunk, trackIndex) => {
215
- // Feed chunk to decoder
216
- },
140
+ onTrack: (track) => console.log(track.codec),
141
+ onChunk: (chunk, trackIndex) => decoder.decode(chunk),
217
142
  onError: console.error,
218
143
  });
219
144
 
220
- demuxer.open('input.mp4');
221
- demuxer.demux(); // Process all chunks
145
+ demuxer.open("input.mp4");
146
+ demuxer.demux();
222
147
  demuxer.close();
223
148
  ```
224
149
 
225
- ### Decode Images (including animated GIFs)
226
-
227
- ```javascript
228
- import { ImageDecoder } from '@pproenca/node-webcodecs';
229
- import { readFileSync } from 'fs';
230
-
231
- const imageData = readFileSync('animation.gif');
232
-
233
- const decoder = new ImageDecoder({
234
- type: 'image/gif',
235
- data: imageData,
236
- });
237
-
238
- await decoder.completed;
239
-
240
- console.log(`${decoder.tracks.length} track(s)`);
241
- console.log(`${decoder.tracks[0].frameCount} frames`);
242
- console.log(`Animated: ${decoder.tracks[0].animated}`);
243
-
244
- // Decode each frame
245
- for (let i = 0; i < decoder.tracks[0].frameCount; i++) {
246
- const { image, complete } = await decoder.decode({ frameIndex: i });
247
- console.log(`Frame ${i}: ${image.codedWidth}x${image.codedHeight}`);
248
- image.close();
249
- }
250
-
251
- decoder.close();
252
- ```
253
-
254
- ## API Reference
255
-
256
- This library implements the [W3C WebCodecs specification](https://www.w3.org/TR/webcodecs/):
150
+ See [examples/](examples/) for complete working code.
257
151
 
258
- ### Core Classes
152
+ ## API
259
153
 
260
- | Class | Description |
261
- |-------|-------------|
262
- | `VideoEncoder` | Encode VideoFrame to EncodedVideoChunk |
263
- | `VideoDecoder` | Decode EncodedVideoChunk to VideoFrame |
264
- | `AudioEncoder` | Encode AudioData to EncodedAudioChunk |
265
- | `AudioDecoder` | Decode EncodedAudioChunk to AudioData |
266
- | `VideoFrame` | Raw video frame with pixel data |
267
- | `AudioData` | Raw audio samples |
268
- | `EncodedVideoChunk` | Compressed video data |
269
- | `EncodedAudioChunk` | Compressed audio data |
270
- | `ImageDecoder` | Decode images (JPEG, PNG, WebP, GIF) |
271
- | `VideoColorSpace` | Color space metadata |
154
+ This library implements the [W3C WebCodecs specification](https://www.w3.org/TR/webcodecs/).
272
155
 
273
- ### Beyond W3C Spec
156
+ | Class | Description |
157
+ | ----------------------------------------- | ------------------------------- |
158
+ | `VideoEncoder` / `VideoDecoder` | Compress / decompress video |
159
+ | `AudioEncoder` / `AudioDecoder` | Compress / decompress audio |
160
+ | `VideoFrame` / `AudioData` | Raw media containers |
161
+ | `EncodedVideoChunk` / `EncodedAudioChunk` | Compressed media packets |
162
+ | `ImageDecoder` | Decode JPEG, PNG, WebP, GIF |
163
+ | | |
164
+ | `Muxer` / `Demuxer` | Container I/O (beyond W3C spec) |
274
165
 
275
- | Class | Description |
276
- |-------|-------------|
277
- | `Muxer` | Write to MP4 containers |
278
- | `Demuxer` | Read from any FFmpeg-supported format |
279
- | `VideoFilter` | Apply blur filters (content moderation) |
280
- | `TestVideoGenerator` | Generate test patterns |
166
+ **Video codecs:** H.264, H.265, VP8, VP9, AV1
167
+ **Audio codecs:** AAC, Opus, MP3 (decode), FLAC (decode)
281
168
 
282
- ### Supported Codecs
283
-
284
- | Type | Codec | Encode | Decode | Codec String |
285
- |------|-------|--------|--------|--------------|
286
- | Video | H.264/AVC | ✅ | ✅ | `avc1.*` |
287
- | Video | H.265/HEVC | ✅ | ✅ | `hvc1.*`, `hev1.*` |
288
- | Video | VP8 | ✅ | ✅ | `vp8` |
289
- | Video | VP9 | ✅ | ✅ | `vp09.*` |
290
- | Video | AV1 | ✅ | ✅ | `av01.*` |
291
- | Audio | AAC | ✅ | ✅ | `mp4a.40.2` |
292
- | Audio | Opus | ✅ | ✅ | `opus` |
293
- | Audio | MP3 | ❌ | ✅ | `mp3` |
294
- | Audio | FLAC | ❌ | ✅ | `flac` |
295
-
296
- ### Pixel Formats
297
-
298
- 8-bit: `I420`, `I420A`, `I422`, `I422A`, `I444`, `I444A`, `NV12`, `NV21`, `NV12A`, `RGBA`, `RGBX`, `BGRA`, `BGRX`
299
-
300
- 10-bit: `I420P10`, `I422P10`, `I444P10`, `NV12P10`, `I420AP10`, `I422AP10`, `I444AP10`
301
-
302
- 12-bit: `I420P12`, `I422P12`, `I444P12`
303
-
304
- ## Interactive Demos
305
-
306
- Run the interactive demo with web UI:
307
-
308
- ```bash
309
- git clone https://github.com/pproenca/node-webcodecs
310
- cd node-webcodecs
311
- npm install && npm run build
312
- node examples/run-demo.js
313
- ```
314
-
315
- Or use Docker:
316
-
317
- ```bash
318
- docker compose up demo
319
- ```
320
-
321
- ## Test Results
322
-
323
- ```
324
- Test Files: 45 passed / 51 total
325
- Tests: 428 passed / 442 total
326
- Duration: ~2 minutes
327
- ```
169
+ See [docs/codecs.md](docs/codecs.md) for codec strings and pixel formats.
328
170
 
329
171
  ## Contributing
330
172
 
331
- See [CONTRIBUTING.md](.github/CONTRIBUTING.md) for development setup and guidelines.
173
+ See [CONTRIBUTING.md](.github/CONTRIBUTING.md).
332
174
 
333
175
  ## License
334
176
 
package/binding.gyp ADDED
@@ -0,0 +1,123 @@
1
+ {
2
+ "variables": {
3
+ "enable_sanitizers%": 0
4
+ },
5
+ "targets": [
6
+ {
7
+ "target_name": "node_webcodecs",
8
+ "sources": [
9
+ "src/addon.cc",
10
+ "src/common.cc",
11
+ "src/video_encoder.cc",
12
+ "src/video_decoder.cc",
13
+ "src/video_frame.cc",
14
+ "src/audio_encoder.cc",
15
+ "src/audio_decoder.cc",
16
+ "src/audio_data.cc",
17
+ "src/encoded_video_chunk.cc",
18
+ "src/encoded_audio_chunk.cc",
19
+ "src/video_filter.cc",
20
+ "src/demuxer.cc",
21
+ "src/muxer.cc",
22
+ "src/image_decoder.cc",
23
+ "src/test_video_generator.cc",
24
+ "src/async_encode_worker.cc",
25
+ "src/async_decode_worker.cc",
26
+ "src/warnings.cc",
27
+ "src/error_builder.cc",
28
+ "src/descriptors.cc"
29
+ ],
30
+ "include_dirs": [
31
+ "<!@(node -p \"require('node-addon-api').include\")",
32
+ "."
33
+ ],
34
+ "defines": [
35
+ "NAPI_VERSION=8",
36
+ "NAPI_CPP_EXCEPTIONS",
37
+ "NODE_ADDON_API_DISABLE_DEPRECATED"
38
+ ],
39
+ "dependencies": [
40
+ "<!(node -p \"require('node-addon-api').gyp\")"
41
+ ],
42
+ "conditions": [
43
+ ["OS=='mac'", {
44
+ "include_dirs": [
45
+ "<!@(node gyp/ffmpeg-paths.js include 2>/dev/null || pkg-config --cflags-only-I libavcodec libavutil libswscale libswresample libavfilter 2>/dev/null | sed s/-I//g || echo '/opt/homebrew/include /usr/local/include')"
46
+ ],
47
+ "libraries": [
48
+ "<!@(node gyp/ffmpeg-paths.js lib 2>/dev/null || pkg-config --libs --static libavcodec libavformat libavutil libswscale libswresample libavfilter 2>/dev/null || echo '-L/opt/homebrew/lib -L/usr/local/lib -lavcodec -lavformat -lavutil -lswscale -lswresample -lavfilter')",
49
+ "-framework VideoToolbox",
50
+ "-framework AudioToolbox",
51
+ "-framework CoreMedia",
52
+ "-framework CoreVideo",
53
+ "-framework CoreFoundation",
54
+ "-framework CoreServices",
55
+ "-framework Security",
56
+ "-framework Metal",
57
+ "-framework CoreImage",
58
+ "-framework AppKit",
59
+ "-liconv",
60
+ "-lbz2",
61
+ "-lz"
62
+ ],
63
+ "xcode_settings": {
64
+ "CLANG_CXX_LANGUAGE_STANDARD": "c++20",
65
+ "GCC_ENABLE_CPP_EXCEPTIONS": "YES",
66
+ "GCC_ENABLE_CPP_RTTI": "YES",
67
+ "MACOSX_DEPLOYMENT_TARGET": "11.0",
68
+ "OTHER_CPLUSPLUSFLAGS": [
69
+ "-fexceptions",
70
+ "-Wall",
71
+ "-Wextra",
72
+ "-Wno-unused-parameter"
73
+ ],
74
+ "OTHER_LDFLAGS": [
75
+ "-mmacosx-version-min=11.0"
76
+ ]
77
+ }
78
+ }],
79
+ ["OS=='linux'", {
80
+ "include_dirs": [
81
+ "<!@(node gyp/ffmpeg-paths.js include 2>/dev/null || pkg-config --cflags-only-I libavcodec libavutil libswscale libswresample libavfilter | sed s/-I//g)"
82
+ ],
83
+ "libraries": [
84
+ "<!@(node gyp/ffmpeg-paths.js lib 2>/dev/null || pkg-config --libs --static libavcodec libavformat libavutil libswscale libswresample libavfilter)",
85
+ "-lpthread",
86
+ "-lm",
87
+ "-ldl",
88
+ "-lz"
89
+ ],
90
+ "ldflags": [
91
+ "-Wl,-Bsymbolic"
92
+ ],
93
+ "cflags_cc": [
94
+ "-std=c++20",
95
+ "-fexceptions",
96
+ "-Wall",
97
+ "-Wextra",
98
+ "-Wno-unused-parameter",
99
+ "-fPIC"
100
+ ]
101
+ }],
102
+ ["enable_sanitizers==1", {
103
+ "cflags_cc": [
104
+ "-fsanitize=address,undefined",
105
+ "-fno-omit-frame-pointer"
106
+ ],
107
+ "ldflags": [
108
+ "-fsanitize=address,undefined"
109
+ ],
110
+ "xcode_settings": {
111
+ "OTHER_CFLAGS": [
112
+ "-fsanitize=address,undefined",
113
+ "-fno-omit-frame-pointer"
114
+ ],
115
+ "OTHER_LDFLAGS": [
116
+ "-fsanitize=address,undefined"
117
+ ]
118
+ }
119
+ }]
120
+ ]
121
+ }
122
+ ]
123
+ }
@@ -55,7 +55,6 @@ class AudioDecoder extends codec_base_1.CodecBase {
55
55
  this._errorCallback = init.error;
56
56
  this._controlQueue.setErrorHandler(init.error);
57
57
  const outputCallback = (nativeData) => {
58
- // Decrement queue size when output received
59
58
  this._decodeQueueSize = Math.max(0, this._decodeQueueSize - 1);
60
59
  // biome-ignore lint/suspicious/noExplicitAny: Object.create wrapper pattern requires any for property assignment
61
60
  const wrapper = Object.create(audio_data_1.AudioData.prototype);
@@ -92,7 +91,7 @@ class AudioDecoder extends codec_base_1.CodecBase {
92
91
  decode(chunk) {
93
92
  // W3C spec: throw InvalidStateError if not configured
94
93
  if (this.state === 'unconfigured') {
95
- throw new DOMException('Decoder is not configured', 'InvalidStateError');
94
+ throw new DOMException('Decoder is unconfigured', 'InvalidStateError');
96
95
  }
97
96
  if (this.state === 'closed') {
98
97
  throw new DOMException('Decoder is closed', 'InvalidStateError');
@@ -10,10 +10,14 @@ export declare class AudioEncoder extends CodecBase {
10
10
  private _native;
11
11
  private _controlQueue;
12
12
  private _encodeQueueSize;
13
+ private _maxQueueDepth;
13
14
  constructor(init: AudioEncoderInit);
14
15
  get state(): CodecState;
15
16
  get encodeQueueSize(): number;
16
17
  get codecSaturated(): boolean;
18
+ get maxQueueDepth(): number;
19
+ set maxQueueDepth(value: number);
20
+ get ready(): Promise<void>;
17
21
  configure(config: AudioEncoderConfig): void;
18
22
  encode(data: AudioData): void;
19
23
  flush(): Promise<void>;
@@ -46,10 +46,12 @@ const encoded_chunks_1 = require("./encoded-chunks");
46
46
  const is = __importStar(require("./is"));
47
47
  // Load native addon with type assertion
48
48
  const native = binding_1.binding;
49
+ const DEFAULT_MAX_QUEUE_DEPTH = 16;
49
50
  class AudioEncoder extends codec_base_1.CodecBase {
50
51
  constructor(init) {
51
52
  super();
52
53
  this._encodeQueueSize = 0;
54
+ this._maxQueueDepth = DEFAULT_MAX_QUEUE_DEPTH;
53
55
  // W3C spec: output and error callbacks are required
54
56
  is.assertPlainObject(init, 'init');
55
57
  is.assertFunction(init.output, 'init.output');
@@ -57,7 +59,6 @@ class AudioEncoder extends codec_base_1.CodecBase {
57
59
  this._controlQueue = new control_message_queue_1.ControlMessageQueue();
58
60
  this._controlQueue.setErrorHandler(init.error);
59
61
  const outputCallback = (chunk, metadata) => {
60
- // Decrement queue size when output received
61
62
  this._encodeQueueSize = Math.max(0, this._encodeQueueSize - 1);
62
63
  // biome-ignore lint/suspicious/noExplicitAny: Object.create wrapper pattern requires any for property assignment
63
64
  const wrapper = Object.create(encoded_chunks_1.EncodedAudioChunk.prototype);
@@ -80,6 +81,31 @@ class AudioEncoder extends codec_base_1.CodecBase {
80
81
  get codecSaturated() {
81
82
  return this._native.codecSaturated;
82
83
  }
84
+ get maxQueueDepth() {
85
+ return this._maxQueueDepth;
86
+ }
87
+ set maxQueueDepth(value) {
88
+ if (value < 1) {
89
+ throw new RangeError('maxQueueDepth must be at least 1');
90
+ }
91
+ this._maxQueueDepth = value;
92
+ }
93
+ get ready() {
94
+ if (this._encodeQueueSize < this._maxQueueDepth) {
95
+ return Promise.resolve();
96
+ }
97
+ return new Promise((resolve) => {
98
+ const checkCapacity = () => {
99
+ if (this._encodeQueueSize < this._maxQueueDepth) {
100
+ resolve();
101
+ }
102
+ else {
103
+ setTimeout(checkCapacity, 1);
104
+ }
105
+ };
106
+ setTimeout(checkCapacity, 1);
107
+ });
108
+ }
83
109
  configure(config) {
84
110
  // W3C spec: throw if closed
85
111
  if (this.state === 'closed') {
@@ -95,7 +121,7 @@ class AudioEncoder extends codec_base_1.CodecBase {
95
121
  encode(data) {
96
122
  // W3C spec: throw InvalidStateError if not configured
97
123
  if (this.state === 'unconfigured') {
98
- throw new DOMException('Encoder is not configured', 'InvalidStateError');
124
+ throw new DOMException('Encoder is unconfigured', 'InvalidStateError');
99
125
  }
100
126
  if (this.state === 'closed') {
101
127
  throw new DOMException('Encoder is closed', 'InvalidStateError');
package/dist/binding.d.ts CHANGED
@@ -2,8 +2,6 @@ export declare const binding: unknown;
2
2
  export declare const platformInfo: {
3
3
  platform: NodeJS.Platform;
4
4
  arch: NodeJS.Architecture;
5
- runtimePlatform: string;
6
5
  nodeVersion: string;
7
6
  napiVersion: string;
8
- prebuiltAvailable: boolean;
9
7
  };