@pproenca/node-webcodecs 0.1.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 (103) hide show
  1. package/README.md +78 -206
  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 -124
  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 +17 -3
  15. package/dist/index.js +9 -4
  16. package/dist/is.d.ts +18 -0
  17. package/dist/is.js +14 -0
  18. package/dist/native-types.d.ts +20 -0
  19. package/dist/platform.d.ts +1 -10
  20. package/dist/platform.js +1 -39
  21. package/dist/resource-manager.d.ts +1 -2
  22. package/dist/resource-manager.js +3 -17
  23. package/dist/types.d.ts +46 -0
  24. package/dist/video-decoder.d.ts +21 -0
  25. package/dist/video-decoder.js +74 -2
  26. package/dist/video-encoder.d.ts +22 -0
  27. package/dist/video-encoder.js +83 -8
  28. package/dist/video-frame.d.ts +6 -3
  29. package/dist/video-frame.js +36 -4
  30. package/lib/audio-decoder.ts +1 -2
  31. package/lib/audio-encoder.ts +31 -2
  32. package/lib/binding.ts +45 -104
  33. package/lib/control-message-queue.ts +0 -1
  34. package/lib/demuxer.ts +10 -0
  35. package/lib/encoded-chunks.ts +90 -2
  36. package/lib/image-decoder.ts +5 -0
  37. package/lib/index.ts +9 -3
  38. package/lib/is.ts +32 -0
  39. package/lib/native-types.ts +22 -0
  40. package/lib/platform.ts +1 -41
  41. package/lib/resource-manager.ts +3 -19
  42. package/lib/types.ts +52 -1
  43. package/lib/video-decoder.ts +84 -2
  44. package/lib/video-encoder.ts +90 -8
  45. package/lib/video-frame.ts +52 -7
  46. package/package.json +49 -32
  47. package/src/addon.cc +57 -0
  48. package/src/async_decode_worker.cc +243 -36
  49. package/src/async_decode_worker.h +55 -4
  50. package/src/async_encode_worker.cc +155 -44
  51. package/src/async_encode_worker.h +38 -12
  52. package/src/audio_data.cc +38 -15
  53. package/src/audio_data.h +1 -0
  54. package/src/audio_decoder.cc +24 -3
  55. package/src/audio_encoder.cc +55 -4
  56. package/src/common.cc +125 -17
  57. package/src/common.h +34 -4
  58. package/src/demuxer.cc +16 -2
  59. package/src/encoded_audio_chunk.cc +10 -0
  60. package/src/encoded_audio_chunk.h +2 -0
  61. package/src/encoded_video_chunk.h +1 -0
  62. package/src/error_builder.cc +0 -4
  63. package/src/image_decoder.cc +127 -90
  64. package/src/image_decoder.h +11 -4
  65. package/src/muxer.cc +1 -0
  66. package/src/test_video_generator.cc +3 -2
  67. package/src/video_decoder.cc +169 -19
  68. package/src/video_decoder.h +9 -11
  69. package/src/video_encoder.cc +428 -35
  70. package/src/video_encoder.h +16 -0
  71. package/src/video_filter.cc +22 -11
  72. package/src/video_frame.cc +160 -5
  73. package/src/warnings.cc +0 -4
  74. package/dist/audio-data.js.map +0 -1
  75. package/dist/audio-decoder.js.map +0 -1
  76. package/dist/audio-encoder.js.map +0 -1
  77. package/dist/binding.js.map +0 -1
  78. package/dist/codec-base.js.map +0 -1
  79. package/dist/control-message-queue.js.map +0 -1
  80. package/dist/demuxer.js.map +0 -1
  81. package/dist/encoded-chunks.js.map +0 -1
  82. package/dist/errors.js.map +0 -1
  83. package/dist/ffmpeg.d.ts +0 -21
  84. package/dist/ffmpeg.js +0 -112
  85. package/dist/image-decoder.js.map +0 -1
  86. package/dist/image-track-list.js.map +0 -1
  87. package/dist/image-track.js.map +0 -1
  88. package/dist/index.js.map +0 -1
  89. package/dist/is.js.map +0 -1
  90. package/dist/muxer.js.map +0 -1
  91. package/dist/native-types.js.map +0 -1
  92. package/dist/platform.js.map +0 -1
  93. package/dist/resource-manager.js.map +0 -1
  94. package/dist/test-video-generator.js.map +0 -1
  95. package/dist/transfer.js.map +0 -1
  96. package/dist/types.js.map +0 -1
  97. package/dist/video-decoder.js.map +0 -1
  98. package/dist/video-encoder.js.map +0 -1
  99. package/dist/video-filter.js.map +0 -1
  100. package/dist/video-frame.js.map +0 -1
  101. package/install/build.js +0 -51
  102. package/install/check.js +0 -192
  103. 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,279 +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
- ### Prerequisites
24
+ Prebuilt binaries with FFmpeg statically linked are included for:
25
+
26
+ - macOS ARM64 (Apple Silicon)
27
+ - macOS x64 (Intel)
28
+ - Linux x64 (musl, fully static)
29
+
30
+ <details>
31
+ <summary><strong>Building from Source</strong></summary>
32
+
33
+ For other platforms or to force a source build:
30
34
 
31
- FFmpeg development libraries are required:
35
+ ```bash
36
+ npm install @pproenca/node-webcodecs --build-from-source
37
+ ```
38
+
39
+ This requires FFmpeg 5.0+ development libraries:
32
40
 
33
41
  **macOS:**
42
+
34
43
  ```bash
35
44
  brew install ffmpeg pkg-config
36
45
  ```
37
46
 
38
47
  **Ubuntu/Debian:**
48
+
39
49
  ```bash
40
- sudo apt-get install \
41
- libavcodec-dev libavformat-dev libavutil-dev \
42
- libswscale-dev libswresample-dev libavfilter-dev \
43
- pkg-config
50
+ sudo apt-get install libavcodec-dev libavformat-dev libavutil-dev \
51
+ libswscale-dev libswresample-dev libavfilter-dev pkg-config
44
52
  ```
45
53
 
46
54
  **Fedora/RHEL:**
55
+
47
56
  ```bash
48
57
  sudo dnf install ffmpeg-devel pkg-config
49
58
  ```
50
59
 
51
- **Windows:**
52
- ```bash
53
- # Using vcpkg
54
- vcpkg install ffmpeg
55
- ```
60
+ </details>
56
61
 
57
62
  ## Quick Start
58
63
 
59
- ### Encode Video Frames
64
+ ### Encode
60
65
 
61
66
  ```javascript
62
- import { VideoEncoder, VideoFrame } from '@pproenca/node-webcodecs';
63
-
64
- const chunks = [];
67
+ import { VideoEncoder, VideoFrame } from "@pproenca/node-webcodecs";
65
68
 
66
69
  const encoder = new VideoEncoder({
67
- output: (chunk, metadata) => {
68
- chunks.push(chunk);
69
- if (metadata?.decoderConfig?.description) {
70
- // Save codec extradata for container
71
- }
72
- },
73
- error: (e) => console.error('Encode error:', e),
70
+ output: (chunk) => chunks.push(chunk),
71
+ error: console.error,
74
72
  });
75
73
 
76
74
  encoder.configure({
77
- codec: 'avc1.42001e', // H.264 Baseline
75
+ codec: "avc1.42001e",
78
76
  width: 1920,
79
77
  height: 1080,
80
78
  bitrate: 5_000_000,
81
- framerate: 30,
82
79
  });
83
80
 
84
- // Create frames from RGBA buffers
85
- for (let i = 0; i < 60; i++) {
86
- const frame = new VideoFrame(rgbaBuffer, {
87
- format: 'RGBA',
88
- codedWidth: 1920,
89
- codedHeight: 1080,
90
- timestamp: i * (1_000_000 / 30), // microseconds
91
- });
92
-
93
- encoder.encode(frame, { keyFrame: i === 0 });
94
- frame.close();
95
- }
81
+ const frame = new VideoFrame(rgbaBuffer, {
82
+ format: "RGBA",
83
+ codedWidth: 1920,
84
+ codedHeight: 1080,
85
+ timestamp: 0,
86
+ });
96
87
 
88
+ encoder.encode(frame);
89
+ frame.close();
97
90
  await encoder.flush();
98
- encoder.close();
99
91
  ```
100
92
 
101
- ### Decode Video Chunks
93
+ ### Decode
102
94
 
103
95
  ```javascript
104
- import { VideoDecoder, EncodedVideoChunk } from '@pproenca/node-webcodecs';
96
+ import { VideoDecoder, EncodedVideoChunk } from "@pproenca/node-webcodecs";
105
97
 
106
98
  const decoder = new VideoDecoder({
107
99
  output: (frame) => {
108
- console.log(`Frame: ${frame.codedWidth}x${frame.codedHeight}`);
109
- // Process frame data...
100
+ // Process frame...
110
101
  frame.close();
111
102
  },
112
- error: (e) => console.error('Decode error:', e),
103
+ error: console.error,
113
104
  });
114
105
 
115
106
  decoder.configure({
116
- codec: 'avc1.42001e',
107
+ codec: "avc1.42001e",
117
108
  codedWidth: 1920,
118
109
  codedHeight: 1080,
119
- description: codecExtradata, // From container or encoder
120
110
  });
121
111
 
122
- for (const chunk of encodedChunks) {
123
- decoder.decode(new EncodedVideoChunk({
124
- type: chunk.isKeyframe ? 'key' : 'delta',
125
- timestamp: chunk.timestamp,
126
- data: chunk.data,
127
- }));
128
- }
129
-
112
+ decoder.decode(new EncodedVideoChunk({ type: "key", timestamp: 0, data }));
130
113
  await decoder.flush();
131
- decoder.close();
132
114
  ```
133
115
 
134
- ### Mux to MP4 Container
116
+ ### Mux to MP4
135
117
 
136
118
  ```javascript
137
- import { Muxer, VideoEncoder, VideoFrame } from '@pproenca/node-webcodecs';
119
+ import { Muxer } from "@pproenca/node-webcodecs";
138
120
 
139
- const muxer = new Muxer({ filename: 'output.mp4' });
140
- let videoTrackId;
121
+ const muxer = new Muxer({ filename: "output.mp4" });
141
122
 
142
- const encoder = new VideoEncoder({
143
- output: (chunk, metadata) => {
144
- if (metadata?.decoderConfig?.description) {
145
- // Add video track on first keyframe
146
- videoTrackId = muxer.addVideoTrack({
147
- codec: 'avc1.42001e',
148
- width: 1920,
149
- height: 1080,
150
- description: metadata.decoderConfig.description,
151
- });
152
- }
153
- muxer.writeVideoChunk(videoTrackId, chunk);
154
- },
155
- error: console.error,
156
- });
157
-
158
- encoder.configure({
159
- codec: 'avc1.42001e',
123
+ muxer.addVideoTrack({
124
+ codec: "avc1.42001e",
160
125
  width: 1920,
161
126
  height: 1080,
162
- bitrate: 5_000_000,
163
- avc: { format: 'avc' }, // Use AVCC format for MP4
127
+ description: codecDescription,
164
128
  });
165
129
 
166
- // Encode frames...
167
- await encoder.flush();
168
- encoder.close();
130
+ muxer.writeVideoChunk(chunk);
169
131
  muxer.finalize();
170
132
  ```
171
133
 
172
- ### Demux from Video File
134
+ ### Demux
173
135
 
174
136
  ```javascript
175
- import { Demuxer } from '@pproenca/node-webcodecs';
137
+ import { Demuxer } from "@pproenca/node-webcodecs";
176
138
 
177
139
  const demuxer = new Demuxer({
178
- onTrack: (track) => {
179
- console.log(`Track ${track.index}: ${track.type} (${track.codec})`);
180
- if (track.type === 'video') {
181
- // Configure decoder with track.extradata
182
- }
183
- },
184
- onChunk: (chunk, trackIndex) => {
185
- // Feed chunk to decoder
186
- },
140
+ onTrack: (track) => console.log(track.codec),
141
+ onChunk: (chunk, trackIndex) => decoder.decode(chunk),
187
142
  onError: console.error,
188
143
  });
189
144
 
190
- demuxer.open('input.mp4');
191
- demuxer.demux(); // Process all chunks
145
+ demuxer.open("input.mp4");
146
+ demuxer.demux();
192
147
  demuxer.close();
193
148
  ```
194
149
 
195
- ### Decode Images (including animated GIFs)
150
+ See [examples/](examples/) for complete working code.
196
151
 
197
- ```javascript
198
- import { ImageDecoder } from '@pproenca/node-webcodecs';
199
- import { readFileSync } from 'fs';
200
-
201
- const imageData = readFileSync('animation.gif');
202
-
203
- const decoder = new ImageDecoder({
204
- type: 'image/gif',
205
- data: imageData,
206
- });
207
-
208
- await decoder.completed;
209
-
210
- console.log(`${decoder.tracks.length} track(s)`);
211
- console.log(`${decoder.tracks[0].frameCount} frames`);
212
- console.log(`Animated: ${decoder.tracks[0].animated}`);
213
-
214
- // Decode each frame
215
- for (let i = 0; i < decoder.tracks[0].frameCount; i++) {
216
- const { image, complete } = await decoder.decode({ frameIndex: i });
217
- console.log(`Frame ${i}: ${image.codedWidth}x${image.codedHeight}`);
218
- image.close();
219
- }
220
-
221
- decoder.close();
222
- ```
223
-
224
- ## API Reference
225
-
226
- This library implements the [W3C WebCodecs specification](https://www.w3.org/TR/webcodecs/):
227
-
228
- ### Core Classes
229
-
230
- | Class | Description |
231
- |-------|-------------|
232
- | `VideoEncoder` | Encode VideoFrame to EncodedVideoChunk |
233
- | `VideoDecoder` | Decode EncodedVideoChunk to VideoFrame |
234
- | `AudioEncoder` | Encode AudioData to EncodedAudioChunk |
235
- | `AudioDecoder` | Decode EncodedAudioChunk to AudioData |
236
- | `VideoFrame` | Raw video frame with pixel data |
237
- | `AudioData` | Raw audio samples |
238
- | `EncodedVideoChunk` | Compressed video data |
239
- | `EncodedAudioChunk` | Compressed audio data |
240
- | `ImageDecoder` | Decode images (JPEG, PNG, WebP, GIF) |
241
- | `VideoColorSpace` | Color space metadata |
242
-
243
- ### Beyond W3C Spec
244
-
245
- | Class | Description |
246
- |-------|-------------|
247
- | `Muxer` | Write to MP4 containers |
248
- | `Demuxer` | Read from any FFmpeg-supported format |
249
- | `VideoFilter` | Apply blur filters (content moderation) |
250
- | `TestVideoGenerator` | Generate test patterns |
152
+ ## API
251
153
 
252
- ### Supported Codecs
154
+ This library implements the [W3C WebCodecs specification](https://www.w3.org/TR/webcodecs/).
253
155
 
254
- | Type | Codec | Encode | Decode | Codec String |
255
- |------|-------|--------|--------|--------------|
256
- | Video | H.264/AVC | | | `avc1.*` |
257
- | Video | H.265/HEVC | | | `hvc1.*`, `hev1.*` |
258
- | Video | VP8 | | | `vp8` |
259
- | Video | VP9 | | | `vp09.*` |
260
- | Video | AV1 | | | `av01.*` |
261
- | Audio | AAC | ✅ | ✅ | `mp4a.40.2` |
262
- | Audio | Opus | | | `opus` |
263
- | Audio | MP3 | ❌ | ✅ | `mp3` |
264
- | Audio | FLAC | ❌ | ✅ | `flac` |
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) |
265
165
 
266
- ### Pixel Formats
166
+ **Video codecs:** H.264, H.265, VP8, VP9, AV1
167
+ **Audio codecs:** AAC, Opus, MP3 (decode), FLAC (decode)
267
168
 
268
- 8-bit: `I420`, `I420A`, `I422`, `I422A`, `I444`, `I444A`, `NV12`, `NV21`, `NV12A`, `RGBA`, `RGBX`, `BGRA`, `BGRX`
269
-
270
- 10-bit: `I420P10`, `I422P10`, `I444P10`, `NV12P10`, `I420AP10`, `I422AP10`, `I444AP10`
271
-
272
- 12-bit: `I420P12`, `I422P12`, `I444P12`
273
-
274
- ## Interactive Demos
275
-
276
- Run the interactive demo with web UI:
277
-
278
- ```bash
279
- git clone https://github.com/pproenca/node-webcodecs
280
- cd node-webcodecs
281
- npm install && npm run build
282
- node examples/run-demo.js
283
- ```
284
-
285
- Or use Docker:
286
-
287
- ```bash
288
- docker compose up demo
289
- ```
290
-
291
- ## Test Results
292
-
293
- ```
294
- Test Files: 45 passed / 51 total
295
- Tests: 428 passed / 442 total
296
- Duration: ~2 minutes
297
- ```
169
+ See [docs/codecs.md](docs/codecs.md) for codec strings and pixel formats.
298
170
 
299
171
  ## Contributing
300
172
 
301
- See [CONTRIBUTING.md](.github/CONTRIBUTING.md) for development setup and guidelines.
173
+ See [CONTRIBUTING.md](.github/CONTRIBUTING.md).
302
174
 
303
175
  ## License
304
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
  };