@napi-rs/webcodecs 0.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.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2020 N-API for Rust
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,398 @@
1
+ # @napi-rs/webcodecs
2
+
3
+ [![CI](https://github.com/Brooooooklyn/webcodecs-node/actions/workflows/CI.yml/badge.svg)](https://github.com/Brooooooklyn/webcodecs-node/actions/workflows/CI.yml)
4
+
5
+ WebCodecs API implementation for Node.js using FFmpeg, built with [NAPI-RS](https://napi.rs).
6
+
7
+ ## Features
8
+
9
+ - **W3C WebCodecs API compliant** - Full implementation of the WebCodecs specification with native `DOMException` errors
10
+ - **Video encoding/decoding** - H.264, H.265, VP8, VP9, AV1
11
+ - **Audio encoding/decoding** - AAC, Opus, MP3, FLAC, Vorbis, PCM variants
12
+ - **Image decoding** - JPEG, PNG, WebP, GIF, BMP, AVIF
13
+ - **Hardware acceleration** - Zero-copy GPU encoding with VideoToolbox (macOS), NVENC (NVIDIA), VAAPI (Linux), QSV (Intel)
14
+ - **Cross-platform** - macOS, Windows, Linux (glibc/musl, x64/arm64/armv7)
15
+ - **Structured logging** - FFmpeg logs redirected to Rust `tracing` crate for easy integration
16
+
17
+ ## Installation
18
+
19
+ ```bash
20
+ npm install @napi-rs/webcodecs
21
+ # or
22
+ pnpm add @napi-rs/webcodecs
23
+ # or
24
+ yarn add @napi-rs/webcodecs
25
+ ```
26
+
27
+ ## Quick Start
28
+
29
+ ### Video Encoding
30
+
31
+ ```typescript
32
+ import { VideoEncoder, VideoFrame } from '@napi-rs/webcodecs'
33
+
34
+ const encoder = new VideoEncoder({
35
+ output: (chunk, metadata) => {
36
+ console.log(`Encoded ${chunk.type} chunk: ${chunk.byteLength} bytes`)
37
+ },
38
+ error: (e) => console.error(e),
39
+ })
40
+
41
+ encoder.configure({
42
+ codec: 'avc1.42001E', // H.264 Baseline
43
+ width: 1920,
44
+ height: 1080,
45
+ bitrate: 5_000_000,
46
+ hardwareAcceleration: 'prefer-hardware', // Use GPU when available
47
+ latencyMode: 'realtime', // Optimize for low latency
48
+ })
49
+
50
+ // Create and encode frames
51
+ const frameData = new Uint8Array(1920 * 1080 * 4) // RGBA
52
+ const frame = new VideoFrame(frameData, {
53
+ format: 'RGBA',
54
+ codedWidth: 1920,
55
+ codedHeight: 1080,
56
+ timestamp: 0,
57
+ })
58
+
59
+ encoder.encode(frame)
60
+ frame.close()
61
+
62
+ // Force a keyframe for seeking/streaming
63
+ const frame2 = new VideoFrame(frameData, {
64
+ format: 'RGBA',
65
+ codedWidth: 1920,
66
+ codedHeight: 1080,
67
+ timestamp: 33333, // 30fps
68
+ })
69
+ encoder.encode(frame2, { keyFrame: true }) // Force I-frame
70
+ frame2.close()
71
+
72
+ await encoder.flush()
73
+ encoder.close()
74
+ ```
75
+
76
+ ### Video Decoding
77
+
78
+ ```typescript
79
+ import { VideoDecoder, EncodedVideoChunk } from '@napi-rs/webcodecs'
80
+
81
+ const decoder = new VideoDecoder({
82
+ output: (frame) => {
83
+ console.log(`Decoded frame: ${frame.codedWidth}x${frame.codedHeight}`)
84
+ frame.close()
85
+ },
86
+ error: (e) => console.error(e),
87
+ })
88
+
89
+ decoder.configure({
90
+ codec: 'avc1.42001E',
91
+ codedWidth: 1920,
92
+ codedHeight: 1080,
93
+ })
94
+
95
+ // Decode chunks
96
+ const chunk = new EncodedVideoChunk({
97
+ type: 'key',
98
+ timestamp: 0,
99
+ data: encodedData,
100
+ })
101
+
102
+ decoder.decode(chunk)
103
+ await decoder.flush()
104
+ decoder.close()
105
+ ```
106
+
107
+ ### Audio Encoding
108
+
109
+ ```typescript
110
+ import { AudioEncoder, AudioData } from '@napi-rs/webcodecs'
111
+
112
+ const encoder = new AudioEncoder({
113
+ output: (chunk, metadata) => {
114
+ console.log(`Encoded audio: ${chunk.byteLength} bytes`)
115
+ },
116
+ error: (e) => console.error(e),
117
+ })
118
+
119
+ encoder.configure({
120
+ codec: 'opus',
121
+ sampleRate: 48000,
122
+ numberOfChannels: 2,
123
+ bitrate: 128000,
124
+ })
125
+
126
+ const audioData = new AudioData({
127
+ format: 'f32-planar',
128
+ sampleRate: 48000,
129
+ numberOfFrames: 1024,
130
+ numberOfChannels: 2,
131
+ timestamp: 0,
132
+ data: new Float32Array(1024 * 2),
133
+ })
134
+
135
+ encoder.encode(audioData)
136
+ audioData.close()
137
+
138
+ await encoder.flush()
139
+ encoder.close()
140
+ ```
141
+
142
+ ### Image Decoding
143
+
144
+ ```typescript
145
+ import { ImageDecoder } from '@napi-rs/webcodecs'
146
+ import { readFileSync } from 'fs'
147
+
148
+ const imageData = readFileSync('image.png')
149
+ const decoder = new ImageDecoder({
150
+ data: imageData,
151
+ type: 'image/png',
152
+ })
153
+
154
+ const result = await decoder.decode()
155
+ console.log(`Image: ${result.image.codedWidth}x${result.image.codedHeight}`)
156
+ result.image.close()
157
+ decoder.close()
158
+ ```
159
+
160
+ ## Supported Codecs
161
+
162
+ ### Video
163
+
164
+ | Codec | Codec String | Encoding | Decoding |
165
+ | ----- | ----------------------- | -------- | -------- |
166
+ | H.264 | `avc1.*` | ✅ | ✅ |
167
+ | H.265 | `hev1.*`, `hvc1.*` | ✅ | ✅ |
168
+ | VP8 | `vp8` | ✅ | ✅ |
169
+ | VP9 | `vp09.*`, `vp9` | ✅ | ✅ |
170
+ | AV1 | `av01.*`, `av01`, `av1` | ✅ | ✅ |
171
+
172
+ **Note:** Short form codec strings (`vp9`, `av01`, `av1`) are accepted for compatibility with browser implementations.
173
+
174
+ ### Audio
175
+
176
+ | Codec | Codec String | Encoding | Decoding |
177
+ | ------ | ------------ | -------- | -------- |
178
+ | AAC | `mp4a.40.2` | ✅ | ✅ |
179
+ | Opus | `opus` | ✅ | ✅ |
180
+ | MP3 | `mp3` | ✅ | ✅ |
181
+ | FLAC | `flac` | ✅ | ✅ |
182
+ | Vorbis | `vorbis` | ❌ | ✅ |
183
+ | PCM | `pcm-*` | ❌ | ✅ |
184
+
185
+ ### Image
186
+
187
+ | Format | MIME Type | Decoding |
188
+ | ------ | ------------ | -------- |
189
+ | JPEG | `image/jpeg` | ✅ |
190
+ | PNG | `image/png` | ✅ |
191
+ | WebP | `image/webp` | ✅ |
192
+ | GIF | `image/gif` | ✅ |
193
+ | BMP | `image/bmp` | ✅ |
194
+ | AVIF | `image/avif` | ✅ |
195
+
196
+ ## Platform Support
197
+
198
+ Pre-built binaries are available for:
199
+
200
+ | Platform | Architecture |
201
+ | ------------------------ | ------------ |
202
+ | macOS | x64, arm64 |
203
+ | Windows | x64, arm64 |
204
+ | Linux (glibc) | x64, arm64 |
205
+ | Linux (musl) | x64, arm64 |
206
+ | Linux (glibc, gnueabihf) | armv7 |
207
+
208
+ ## W3C Web Platform Tests Compliance
209
+
210
+ This implementation is validated against the [W3C Web Platform Tests](https://github.com/web-platform-tests/wpt) for WebCodecs.
211
+
212
+ ### Ported Tests Status
213
+
214
+ | Status | Count | Percentage |
215
+ | ----------- | ----- | ---------- |
216
+ | **Passing** | 522 | 99.1% |
217
+ | **Skipped** | 5 | 0.9% |
218
+ | **Failing** | 0 | 0% |
219
+
220
+ **Skipped tests** are due to platform-specific features or edge cases.
221
+
222
+ ### Tests Not Ported (Browser-Only)
223
+
224
+ 19 WPT test files require browser APIs unavailable in Node.js:
225
+
226
+ | Category | Tests | APIs Required |
227
+ | ---------------------- | ----- | -------------------------------------- |
228
+ | Serialization/Transfer | 5 | MessageChannel, structured clone |
229
+ | WebGL/Canvas | 5 | WebGL textures, ImageBitmap, Canvas 2D |
230
+ | Cross-Origin Isolation | 8 | COOP/COEP headers |
231
+ | WebIDL | 1 | IDL interface validation |
232
+
233
+ See [`__test__/wpt/README.md`](./__test__/wpt/README.md) for detailed test status.
234
+
235
+ ## Hardware Acceleration
236
+
237
+ Hardware encoding is fully supported with automatic GPU selection and fallback:
238
+
239
+ | Platform | Encoders | Features |
240
+ | -------- | ------------ | ------------------------------------------------------ |
241
+ | macOS | VideoToolbox | H.264, HEVC; realtime mode, allow_sw control |
242
+ | NVIDIA | NVENC | H.264, HEVC, AV1; presets p1-p7, spatial-aq, lookahead |
243
+ | Linux | VAAPI | H.264, HEVC, VP9, AV1; quality 0-8 |
244
+ | Intel | QSV | H.264, HEVC, VP9, AV1; presets, lookahead |
245
+
246
+ ### Configuration
247
+
248
+ ```typescript
249
+ encoder.configure({
250
+ codec: 'avc1.42001E',
251
+ width: 1920,
252
+ height: 1080,
253
+ // Hardware acceleration preference
254
+ hardwareAcceleration: 'prefer-hardware', // 'no-preference' | 'prefer-hardware' | 'prefer-software'
255
+ // Latency mode affects encoder tuning
256
+ latencyMode: 'realtime', // 'quality' | 'realtime'
257
+ })
258
+ ```
259
+
260
+ - `latencyMode: 'realtime'` - Enables low-latency encoder options (smaller GOP, no B-frames, fast presets)
261
+ - `latencyMode: 'quality'` - Enables quality-focused options (larger GOP, B-frames, lookahead)
262
+
263
+ The encoder automatically applies optimal settings for each hardware encoder based on the latency mode.
264
+
265
+ ## Limitations
266
+
267
+ ### Scalable Video Coding (SVC)
268
+
269
+ All scalability modes (L1Tx, L2Tx, L3Tx, S2Tx, S3Tx, and variants) are accepted and populate `metadata.svc.temporalLayerId` when temporal layers >= 2.
270
+
271
+ The W3C WebCodecs spec only defines `temporalLayerId` in `SvcOutputMetadata` - there is no `spatialLayerId` field in the spec. See [W3C WebCodecs §6.7](https://w3c.github.io/webcodecs/#encoded-video-chunk-metadata).
272
+
273
+ Note: This implementation computes temporal layer IDs algorithmically from frame index per W3C spec. FFmpeg is not configured for actual SVC encoding, so base layer frames are not independently decodable.
274
+
275
+ ### Error Handling
276
+
277
+ Synchronous errors (e.g., calling `encode()` on a closed encoder) throw native `DOMException` instances that pass `instanceof DOMException` checks per W3C spec:
278
+
279
+ ```typescript
280
+ try {
281
+ encoder.encode(frame) // on closed encoder
282
+ } catch (e) {
283
+ console.log(e instanceof DOMException) // true
284
+ console.log(e.name) // "InvalidStateError"
285
+ }
286
+ ```
287
+
288
+ Asynchronous error callbacks receive standard `Error` objects with the DOMException name in the message:
289
+
290
+ ```typescript
291
+ const encoder = new VideoEncoder({
292
+ output: (chunk) => {},
293
+ error: (e) => {
294
+ console.log(e.message) // "EncodingError: ..."
295
+ },
296
+ })
297
+ ```
298
+
299
+ ### ImageDecoder Options
300
+
301
+ ImageDecoder supports all W3C spec options:
302
+
303
+ | Option | Status | Notes |
304
+ | ---------------------- | ------ | --------------------------------------------------------------------------------- |
305
+ | `desiredWidth/Height` | ✅ | Scales decoded frames to specified dimensions |
306
+ | `preferAnimation` | ✅ | When `false`, only decodes first frame for animated formats |
307
+ | `colorSpaceConversion` | ✅ | `"default"` extracts color space metadata, `"none"` ignores it (Chromium-aligned) |
308
+
309
+ **Note:** Per W3C spec, `desiredWidth` and `desiredHeight` must both be specified or both omitted.
310
+
311
+ ### Platform-Specific Notes
312
+
313
+ - **ImageDecoder GIF animation**: FFmpeg may return only the first frame. Use `VideoDecoder` with GIF codec for full animation.
314
+
315
+ ## Logging
316
+
317
+ This library uses Rust's `tracing` crate for structured logging. Enable logging via the `WEBCODECS_LOG` environment variable:
318
+
319
+ ```bash
320
+ # Enable all logs at info level
321
+ WEBCODECS_LOG=info node your-app.js
322
+
323
+ # Enable FFmpeg logs at debug level
324
+ WEBCODECS_LOG=ffmpeg=debug node your-app.js
325
+
326
+ # Enable WebCodecs codec errors at warn, FFmpeg at info
327
+ WEBCODECS_LOG=webcodecs=warn,ffmpeg=info node your-app.js
328
+
329
+ # Enable trace-level logging for everything
330
+ WEBCODECS_LOG=trace node your-app.js
331
+ ```
332
+
333
+ ### Log Targets
334
+
335
+ | Target | Description |
336
+ | ----------- | ---------------------------------------------------------------------- |
337
+ | `ffmpeg` | FFmpeg internal logs (codec initialization, encoding/decoding details) |
338
+ | `webcodecs` | WebCodecs API logs (codec errors, state transitions) |
339
+
340
+ ### FFmpeg Log Level Mapping
341
+
342
+ | FFmpeg Level | Tracing Level |
343
+ | ------------ | ------------- |
344
+ | ERROR/FATAL | `error` |
345
+ | WARNING | `warn` |
346
+ | INFO | `info` |
347
+ | VERBOSE | `debug` |
348
+ | DEBUG/TRACE | `trace` |
349
+
350
+ Without `WEBCODECS_LOG` set, all logs are silently discarded.
351
+
352
+ ## API Reference
353
+
354
+ This package implements the [W3C WebCodecs API](https://w3c.github.io/webcodecs/). Key classes:
355
+
356
+ - `VideoEncoder` / `VideoDecoder` - Video encoding and decoding with EventTarget support
357
+ - `AudioEncoder` / `AudioDecoder` - Audio encoding and decoding with EventTarget support
358
+ - `VideoFrame` - Raw video frame data (RGBA formats default to sRGB colorSpace)
359
+ - `AudioData` - Raw audio sample data
360
+ - `EncodedVideoChunk` / `EncodedAudioChunk` - Encoded media data
361
+ - `ImageDecoder` - Static image decoding
362
+ - `VideoColorSpace` - Color space information
363
+
364
+ All encoders and decoders implement the `EventTarget` interface with `addEventListener()`, `removeEventListener()`, and `dispatchEvent()`.
365
+
366
+ For full API documentation, see the [W3C WebCodecs specification](https://w3c.github.io/webcodecs/).
367
+
368
+ ## Development
369
+
370
+ ### Requirements
371
+
372
+ - Rust (latest stable)
373
+ - Node.js 18+
374
+ - pnpm
375
+
376
+ ### Build
377
+
378
+ ```bash
379
+ pnpm install
380
+ pnpm build
381
+ ```
382
+
383
+ ### Test
384
+
385
+ ```bash
386
+ pnpm test
387
+ ```
388
+
389
+ ### Lint
390
+
391
+ ```bash
392
+ pnpm lint
393
+ cargo clippy
394
+ ```
395
+
396
+ ## License
397
+
398
+ MIT