@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.
- package/README.md +78 -206
- package/binding.gyp +123 -0
- package/dist/audio-decoder.js +1 -2
- package/dist/audio-encoder.d.ts +4 -0
- package/dist/audio-encoder.js +28 -2
- package/dist/binding.d.ts +0 -2
- package/dist/binding.js +43 -124
- package/dist/control-message-queue.js +0 -1
- package/dist/demuxer.d.ts +7 -0
- package/dist/demuxer.js +9 -0
- package/dist/encoded-chunks.d.ts +16 -0
- package/dist/encoded-chunks.js +82 -2
- package/dist/image-decoder.js +4 -0
- package/dist/index.d.ts +17 -3
- package/dist/index.js +9 -4
- package/dist/is.d.ts +18 -0
- package/dist/is.js +14 -0
- package/dist/native-types.d.ts +20 -0
- package/dist/platform.d.ts +1 -10
- package/dist/platform.js +1 -39
- package/dist/resource-manager.d.ts +1 -2
- package/dist/resource-manager.js +3 -17
- package/dist/types.d.ts +46 -0
- package/dist/video-decoder.d.ts +21 -0
- package/dist/video-decoder.js +74 -2
- package/dist/video-encoder.d.ts +22 -0
- package/dist/video-encoder.js +83 -8
- package/dist/video-frame.d.ts +6 -3
- package/dist/video-frame.js +36 -4
- package/lib/audio-decoder.ts +1 -2
- package/lib/audio-encoder.ts +31 -2
- package/lib/binding.ts +45 -104
- package/lib/control-message-queue.ts +0 -1
- package/lib/demuxer.ts +10 -0
- package/lib/encoded-chunks.ts +90 -2
- package/lib/image-decoder.ts +5 -0
- package/lib/index.ts +9 -3
- package/lib/is.ts +32 -0
- package/lib/native-types.ts +22 -0
- package/lib/platform.ts +1 -41
- package/lib/resource-manager.ts +3 -19
- package/lib/types.ts +52 -1
- package/lib/video-decoder.ts +84 -2
- package/lib/video-encoder.ts +90 -8
- package/lib/video-frame.ts +52 -7
- package/package.json +49 -32
- package/src/addon.cc +57 -0
- package/src/async_decode_worker.cc +243 -36
- package/src/async_decode_worker.h +55 -4
- package/src/async_encode_worker.cc +155 -44
- package/src/async_encode_worker.h +38 -12
- package/src/audio_data.cc +38 -15
- package/src/audio_data.h +1 -0
- package/src/audio_decoder.cc +24 -3
- package/src/audio_encoder.cc +55 -4
- package/src/common.cc +125 -17
- package/src/common.h +34 -4
- package/src/demuxer.cc +16 -2
- package/src/encoded_audio_chunk.cc +10 -0
- package/src/encoded_audio_chunk.h +2 -0
- package/src/encoded_video_chunk.h +1 -0
- package/src/error_builder.cc +0 -4
- package/src/image_decoder.cc +127 -90
- package/src/image_decoder.h +11 -4
- package/src/muxer.cc +1 -0
- package/src/test_video_generator.cc +3 -2
- package/src/video_decoder.cc +169 -19
- package/src/video_decoder.h +9 -11
- package/src/video_encoder.cc +428 -35
- package/src/video_encoder.h +16 -0
- package/src/video_filter.cc +22 -11
- package/src/video_frame.cc +160 -5
- package/src/warnings.cc +0 -4
- package/dist/audio-data.js.map +0 -1
- package/dist/audio-decoder.js.map +0 -1
- package/dist/audio-encoder.js.map +0 -1
- package/dist/binding.js.map +0 -1
- package/dist/codec-base.js.map +0 -1
- package/dist/control-message-queue.js.map +0 -1
- package/dist/demuxer.js.map +0 -1
- package/dist/encoded-chunks.js.map +0 -1
- package/dist/errors.js.map +0 -1
- package/dist/ffmpeg.d.ts +0 -21
- package/dist/ffmpeg.js +0 -112
- package/dist/image-decoder.js.map +0 -1
- package/dist/image-track-list.js.map +0 -1
- package/dist/image-track.js.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/is.js.map +0 -1
- package/dist/muxer.js.map +0 -1
- package/dist/native-types.js.map +0 -1
- package/dist/platform.js.map +0 -1
- package/dist/resource-manager.js.map +0 -1
- package/dist/test-video-generator.js.map +0 -1
- package/dist/transfer.js.map +0 -1
- package/dist/types.js.map +0 -1
- package/dist/video-decoder.js.map +0 -1
- package/dist/video-encoder.js.map +0 -1
- package/dist/video-filter.js.map +0 -1
- package/dist/video-frame.js.map +0 -1
- package/install/build.js +0 -51
- package/install/check.js +0 -192
- package/lib/ffmpeg.ts +0 -78
package/README.md
CHANGED
|
@@ -1,24 +1,19 @@
|
|
|
1
1
|
# node-webcodecs
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
WebCodecs API for Node.js — encode and decode video/audio with browser-compatible APIs, powered by FFmpeg.
|
|
4
4
|
|
|
5
|
-
[](test/)
|
|
6
5
|
[](LICENSE)
|
|
7
|
-
[](package.json)
|
|
8
7
|
|
|
9
8
|
## Why node-webcodecs?
|
|
10
9
|
|
|
11
|
-
| Feature
|
|
12
|
-
|
|
13
|
-
| **Video Codecs**
|
|
14
|
-
| **Audio Codecs**
|
|
15
|
-
| **Container Muxing** | MP4
|
|
16
|
-
| **Demuxing**
|
|
17
|
-
| **
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
52
|
-
```bash
|
|
53
|
-
# Using vcpkg
|
|
54
|
-
vcpkg install ffmpeg
|
|
55
|
-
```
|
|
60
|
+
</details>
|
|
56
61
|
|
|
57
62
|
## Quick Start
|
|
58
63
|
|
|
59
|
-
### Encode
|
|
64
|
+
### Encode
|
|
60
65
|
|
|
61
66
|
```javascript
|
|
62
|
-
import { VideoEncoder, VideoFrame } from
|
|
63
|
-
|
|
64
|
-
const chunks = [];
|
|
67
|
+
import { VideoEncoder, VideoFrame } from "@pproenca/node-webcodecs";
|
|
65
68
|
|
|
66
69
|
const encoder = new VideoEncoder({
|
|
67
|
-
output: (chunk
|
|
68
|
-
|
|
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:
|
|
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
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
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
|
|
93
|
+
### Decode
|
|
102
94
|
|
|
103
95
|
```javascript
|
|
104
|
-
import { VideoDecoder, EncodedVideoChunk } from
|
|
96
|
+
import { VideoDecoder, EncodedVideoChunk } from "@pproenca/node-webcodecs";
|
|
105
97
|
|
|
106
98
|
const decoder = new VideoDecoder({
|
|
107
99
|
output: (frame) => {
|
|
108
|
-
|
|
109
|
-
// Process frame data...
|
|
100
|
+
// Process frame...
|
|
110
101
|
frame.close();
|
|
111
102
|
},
|
|
112
|
-
error:
|
|
103
|
+
error: console.error,
|
|
113
104
|
});
|
|
114
105
|
|
|
115
106
|
decoder.configure({
|
|
116
|
-
codec:
|
|
107
|
+
codec: "avc1.42001e",
|
|
117
108
|
codedWidth: 1920,
|
|
118
109
|
codedHeight: 1080,
|
|
119
|
-
description: codecExtradata, // From container or encoder
|
|
120
110
|
});
|
|
121
111
|
|
|
122
|
-
|
|
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
|
|
116
|
+
### Mux to MP4
|
|
135
117
|
|
|
136
118
|
```javascript
|
|
137
|
-
import { Muxer
|
|
119
|
+
import { Muxer } from "@pproenca/node-webcodecs";
|
|
138
120
|
|
|
139
|
-
const muxer = new Muxer({ filename:
|
|
140
|
-
let videoTrackId;
|
|
121
|
+
const muxer = new Muxer({ filename: "output.mp4" });
|
|
141
122
|
|
|
142
|
-
|
|
143
|
-
|
|
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
|
-
|
|
163
|
-
avc: { format: 'avc' }, // Use AVCC format for MP4
|
|
127
|
+
description: codecDescription,
|
|
164
128
|
});
|
|
165
129
|
|
|
166
|
-
|
|
167
|
-
await encoder.flush();
|
|
168
|
-
encoder.close();
|
|
130
|
+
muxer.writeVideoChunk(chunk);
|
|
169
131
|
muxer.finalize();
|
|
170
132
|
```
|
|
171
133
|
|
|
172
|
-
### Demux
|
|
134
|
+
### Demux
|
|
173
135
|
|
|
174
136
|
```javascript
|
|
175
|
-
import { Demuxer } from
|
|
137
|
+
import { Demuxer } from "@pproenca/node-webcodecs";
|
|
176
138
|
|
|
177
139
|
const demuxer = new Demuxer({
|
|
178
|
-
onTrack: (track) =>
|
|
179
|
-
|
|
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(
|
|
191
|
-
demuxer.demux();
|
|
145
|
+
demuxer.open("input.mp4");
|
|
146
|
+
demuxer.demux();
|
|
192
147
|
demuxer.close();
|
|
193
148
|
```
|
|
194
149
|
|
|
195
|
-
|
|
150
|
+
See [examples/](examples/) for complete working code.
|
|
196
151
|
|
|
197
|
-
|
|
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
|
-
|
|
154
|
+
This library implements the [W3C WebCodecs specification](https://www.w3.org/TR/webcodecs/).
|
|
253
155
|
|
|
254
|
-
|
|
|
255
|
-
|
|
256
|
-
|
|
|
257
|
-
|
|
|
258
|
-
|
|
|
259
|
-
|
|
|
260
|
-
|
|
|
261
|
-
|
|
|
262
|
-
|
|
|
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
|
-
|
|
166
|
+
**Video codecs:** H.264, H.265, VP8, VP9, AV1
|
|
167
|
+
**Audio codecs:** AAC, Opus, MP3 (decode), FLAC (decode)
|
|
267
168
|
|
|
268
|
-
|
|
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)
|
|
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
|
+
}
|
package/dist/audio-decoder.js
CHANGED
|
@@ -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
|
|
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');
|
package/dist/audio-encoder.d.ts
CHANGED
|
@@ -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>;
|
package/dist/audio-encoder.js
CHANGED
|
@@ -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
|
|
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