webcodecs-node 0.5.0 → 0.5.2

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 (188) hide show
  1. package/README.md +64 -184
  2. package/docs/api.md +1 -1
  3. package/docs/configuration.md +6 -17
  4. package/package.json +2 -2
  5. package/dist/AudioData.d.ts +0 -6
  6. package/dist/AudioData.d.ts.map +0 -1
  7. package/dist/AudioData.js +0 -6
  8. package/dist/AudioData.js.map +0 -1
  9. package/dist/AudioDecoder.d.ts +0 -6
  10. package/dist/AudioDecoder.d.ts.map +0 -1
  11. package/dist/AudioDecoder.js +0 -6
  12. package/dist/AudioDecoder.js.map +0 -1
  13. package/dist/AudioEncoder.d.ts +0 -6
  14. package/dist/AudioEncoder.d.ts.map +0 -1
  15. package/dist/AudioEncoder.js +0 -6
  16. package/dist/AudioEncoder.js.map +0 -1
  17. package/dist/EncodedAudioChunk.d.ts +0 -6
  18. package/dist/EncodedAudioChunk.d.ts.map +0 -1
  19. package/dist/EncodedAudioChunk.js +0 -6
  20. package/dist/EncodedAudioChunk.js.map +0 -1
  21. package/dist/EncodedVideoChunk.d.ts +0 -6
  22. package/dist/EncodedVideoChunk.d.ts.map +0 -1
  23. package/dist/EncodedVideoChunk.js +0 -6
  24. package/dist/EncodedVideoChunk.js.map +0 -1
  25. package/dist/FFmpegProcess.d.ts +0 -6
  26. package/dist/FFmpegProcess.d.ts.map +0 -1
  27. package/dist/FFmpegProcess.js +0 -6
  28. package/dist/FFmpegProcess.js.map +0 -1
  29. package/dist/HardwareAcceleration.d.ts +0 -6
  30. package/dist/HardwareAcceleration.d.ts.map +0 -1
  31. package/dist/HardwareAcceleration.js +0 -6
  32. package/dist/HardwareAcceleration.js.map +0 -1
  33. package/dist/ImageDecoder.d.ts +0 -6
  34. package/dist/ImageDecoder.d.ts.map +0 -1
  35. package/dist/ImageDecoder.js +0 -6
  36. package/dist/ImageDecoder.js.map +0 -1
  37. package/dist/Logger.d.ts +0 -6
  38. package/dist/Logger.d.ts.map +0 -1
  39. package/dist/Logger.js +0 -6
  40. package/dist/Logger.js.map +0 -1
  41. package/dist/MediaCapabilities.d.ts +0 -9
  42. package/dist/MediaCapabilities.d.ts.map +0 -1
  43. package/dist/MediaCapabilities.js +0 -8
  44. package/dist/MediaCapabilities.js.map +0 -1
  45. package/dist/VideoDecoder.d.ts +0 -6
  46. package/dist/VideoDecoder.d.ts.map +0 -1
  47. package/dist/VideoDecoder.js +0 -6
  48. package/dist/VideoDecoder.js.map +0 -1
  49. package/dist/VideoEncoder.d.ts +0 -6
  50. package/dist/VideoEncoder.d.ts.map +0 -1
  51. package/dist/VideoEncoder.js +0 -6
  52. package/dist/VideoEncoder.js.map +0 -1
  53. package/dist/VideoFrame.d.ts +0 -10
  54. package/dist/VideoFrame.d.ts.map +0 -1
  55. package/dist/VideoFrame.js +0 -9
  56. package/dist/VideoFrame.js.map +0 -1
  57. package/dist/__tests__/AudioData.test.d.ts +0 -5
  58. package/dist/__tests__/AudioData.test.d.ts.map +0 -1
  59. package/dist/__tests__/AudioData.test.js +0 -180
  60. package/dist/__tests__/AudioData.test.js.map +0 -1
  61. package/dist/__tests__/AudioDecoder.test.d.ts +0 -5
  62. package/dist/__tests__/AudioDecoder.test.d.ts.map +0 -1
  63. package/dist/__tests__/AudioDecoder.test.js +0 -733
  64. package/dist/__tests__/AudioDecoder.test.js.map +0 -1
  65. package/dist/__tests__/AudioEncoder.test.d.ts +0 -5
  66. package/dist/__tests__/AudioEncoder.test.d.ts.map +0 -1
  67. package/dist/__tests__/AudioEncoder.test.js +0 -247
  68. package/dist/__tests__/AudioEncoder.test.js.map +0 -1
  69. package/dist/__tests__/EncodedChunks.test.d.ts +0 -5
  70. package/dist/__tests__/EncodedChunks.test.d.ts.map +0 -1
  71. package/dist/__tests__/EncodedChunks.test.js +0 -100
  72. package/dist/__tests__/EncodedChunks.test.js.map +0 -1
  73. package/dist/__tests__/HardwareAcceleration.test.d.ts +0 -5
  74. package/dist/__tests__/HardwareAcceleration.test.d.ts.map +0 -1
  75. package/dist/__tests__/HardwareAcceleration.test.js +0 -85
  76. package/dist/__tests__/HardwareAcceleration.test.js.map +0 -1
  77. package/dist/__tests__/ImageDecoder.test.d.ts +0 -5
  78. package/dist/__tests__/ImageDecoder.test.d.ts.map +0 -1
  79. package/dist/__tests__/ImageDecoder.test.js +0 -558
  80. package/dist/__tests__/ImageDecoder.test.js.map +0 -1
  81. package/dist/__tests__/ImageDecoder.wpt.test.d.ts +0 -8
  82. package/dist/__tests__/ImageDecoder.wpt.test.d.ts.map +0 -1
  83. package/dist/__tests__/ImageDecoder.wpt.test.js +0 -135
  84. package/dist/__tests__/ImageDecoder.wpt.test.js.map +0 -1
  85. package/dist/__tests__/MediaCapabilities.test.d.ts +0 -5
  86. package/dist/__tests__/MediaCapabilities.test.d.ts.map +0 -1
  87. package/dist/__tests__/MediaCapabilities.test.js +0 -346
  88. package/dist/__tests__/MediaCapabilities.test.js.map +0 -1
  89. package/dist/__tests__/NodeAvDecoder.test.d.ts +0 -2
  90. package/dist/__tests__/NodeAvDecoder.test.d.ts.map +0 -1
  91. package/dist/__tests__/NodeAvDecoder.test.js +0 -206
  92. package/dist/__tests__/NodeAvDecoder.test.js.map +0 -1
  93. package/dist/__tests__/NodeAvEncoder.test.d.ts +0 -2
  94. package/dist/__tests__/NodeAvEncoder.test.d.ts.map +0 -1
  95. package/dist/__tests__/NodeAvEncoder.test.js +0 -176
  96. package/dist/__tests__/NodeAvEncoder.test.js.map +0 -1
  97. package/dist/__tests__/VideoDecoder.test.d.ts +0 -5
  98. package/dist/__tests__/VideoDecoder.test.d.ts.map +0 -1
  99. package/dist/__tests__/VideoDecoder.test.js +0 -675
  100. package/dist/__tests__/VideoDecoder.test.js.map +0 -1
  101. package/dist/__tests__/VideoEncoder.test.d.ts +0 -5
  102. package/dist/__tests__/VideoEncoder.test.d.ts.map +0 -1
  103. package/dist/__tests__/VideoEncoder.test.js +0 -468
  104. package/dist/__tests__/VideoEncoder.test.js.map +0 -1
  105. package/dist/__tests__/VideoFrame.test.d.ts +0 -5
  106. package/dist/__tests__/VideoFrame.test.d.ts.map +0 -1
  107. package/dist/__tests__/VideoFrame.test.js +0 -246
  108. package/dist/__tests__/VideoFrame.test.js.map +0 -1
  109. package/dist/__tests__/aac-utils.test.d.ts +0 -2
  110. package/dist/__tests__/aac-utils.test.d.ts.map +0 -1
  111. package/dist/__tests__/aac-utils.test.js +0 -37
  112. package/dist/__tests__/aac-utils.test.js.map +0 -1
  113. package/dist/__tests__/avc-utils.test.d.ts +0 -2
  114. package/dist/__tests__/avc-utils.test.d.ts.map +0 -1
  115. package/dist/__tests__/avc-utils.test.js +0 -63
  116. package/dist/__tests__/avc-utils.test.js.map +0 -1
  117. package/dist/__tests__/hevc-utils.test.d.ts +0 -2
  118. package/dist/__tests__/hevc-utils.test.d.ts.map +0 -1
  119. package/dist/__tests__/hevc-utils.test.js +0 -78
  120. package/dist/__tests__/hevc-utils.test.js.map +0 -1
  121. package/dist/demos/demo-conversion.d.ts +0 -13
  122. package/dist/demos/demo-conversion.d.ts.map +0 -1
  123. package/dist/demos/demo-conversion.js +0 -301
  124. package/dist/demos/demo-conversion.js.map +0 -1
  125. package/dist/demos/demo-hwaccel-conversion.d.ts +0 -8
  126. package/dist/demos/demo-hwaccel-conversion.d.ts.map +0 -1
  127. package/dist/demos/demo-hwaccel-conversion.js +0 -161
  128. package/dist/demos/demo-hwaccel-conversion.js.map +0 -1
  129. package/dist/demos/demo-mediabunny.d.ts +0 -7
  130. package/dist/demos/demo-mediabunny.d.ts.map +0 -1
  131. package/dist/demos/demo-mediabunny.js +0 -78
  132. package/dist/demos/demo-mediabunny.js.map +0 -1
  133. package/dist/ffmpeg/FFmpegProcess.d.ts +0 -111
  134. package/dist/ffmpeg/FFmpegProcess.d.ts.map +0 -1
  135. package/dist/ffmpeg/FFmpegProcess.js +0 -543
  136. package/dist/ffmpeg/FFmpegProcess.js.map +0 -1
  137. package/dist/ffmpeg/audio-codecs.d.ts +0 -59
  138. package/dist/ffmpeg/audio-codecs.d.ts.map +0 -1
  139. package/dist/ffmpeg/audio-codecs.js +0 -99
  140. package/dist/ffmpeg/audio-codecs.js.map +0 -1
  141. package/dist/ffmpeg/formats.d.ts +0 -42
  142. package/dist/ffmpeg/formats.d.ts.map +0 -1
  143. package/dist/ffmpeg/formats.js +0 -147
  144. package/dist/ffmpeg/formats.js.map +0 -1
  145. package/dist/ffmpeg/index.d.ts +0 -10
  146. package/dist/ffmpeg/index.d.ts.map +0 -1
  147. package/dist/ffmpeg/index.js +0 -13
  148. package/dist/ffmpeg/index.js.map +0 -1
  149. package/dist/ffmpeg/parsers/annexb.d.ts +0 -88
  150. package/dist/ffmpeg/parsers/annexb.d.ts.map +0 -1
  151. package/dist/ffmpeg/parsers/annexb.js +0 -201
  152. package/dist/ffmpeg/parsers/annexb.js.map +0 -1
  153. package/dist/ffmpeg/parsers/index.d.ts +0 -6
  154. package/dist/ffmpeg/parsers/index.d.ts.map +0 -1
  155. package/dist/ffmpeg/parsers/index.js +0 -8
  156. package/dist/ffmpeg/parsers/index.js.map +0 -1
  157. package/dist/ffmpeg/parsers/ivf.d.ts +0 -54
  158. package/dist/ffmpeg/parsers/ivf.d.ts.map +0 -1
  159. package/dist/ffmpeg/parsers/ivf.js +0 -109
  160. package/dist/ffmpeg/parsers/ivf.js.map +0 -1
  161. package/dist/ffmpeg/types.d.ts +0 -85
  162. package/dist/ffmpeg/types.d.ts.map +0 -1
  163. package/dist/ffmpeg/types.js +0 -8
  164. package/dist/ffmpeg/types.js.map +0 -1
  165. package/dist/mediabunny/FFmpegAudioDecoder.d.ts +0 -24
  166. package/dist/mediabunny/FFmpegAudioDecoder.d.ts.map +0 -1
  167. package/dist/mediabunny/FFmpegAudioDecoder.js +0 -126
  168. package/dist/mediabunny/FFmpegAudioDecoder.js.map +0 -1
  169. package/dist/mediabunny/FFmpegAudioEncoder.d.ts +0 -44
  170. package/dist/mediabunny/FFmpegAudioEncoder.d.ts.map +0 -1
  171. package/dist/mediabunny/FFmpegAudioEncoder.js +0 -221
  172. package/dist/mediabunny/FFmpegAudioEncoder.js.map +0 -1
  173. package/dist/mediabunny/FFmpegVideoDecoder.d.ts +0 -25
  174. package/dist/mediabunny/FFmpegVideoDecoder.d.ts.map +0 -1
  175. package/dist/mediabunny/FFmpegVideoDecoder.js +0 -122
  176. package/dist/mediabunny/FFmpegVideoDecoder.js.map +0 -1
  177. package/dist/mediabunny/FFmpegVideoEncoder.d.ts +0 -49
  178. package/dist/mediabunny/FFmpegVideoEncoder.d.ts.map +0 -1
  179. package/dist/mediabunny/FFmpegVideoEncoder.js +0 -264
  180. package/dist/mediabunny/FFmpegVideoEncoder.js.map +0 -1
  181. package/dist/mediabunny/index.d.ts +0 -21
  182. package/dist/mediabunny/index.d.ts.map +0 -1
  183. package/dist/mediabunny/index.js +0 -33
  184. package/dist/mediabunny/index.js.map +0 -1
  185. package/dist/types.d.ts +0 -6
  186. package/dist/types.d.ts.map +0 -1
  187. package/dist/types.js +0 -6
  188. package/dist/types.js.map +0 -1
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # webcodecs-node
2
2
 
3
- WebCodecs API implementation for Node.js using FFmpeg.
3
+ WebCodecs API implementation for Node.js using node-av.
4
4
 
5
5
  This package provides a Node.js-compatible implementation of the [WebCodecs API](https://developer.mozilla.org/en-US/docs/Web/API/WebCodecs_API), enabling video and audio encoding/decoding in server-side JavaScript applications.
6
6
 
@@ -16,7 +16,7 @@ This package provides a Node.js-compatible implementation of the [WebCodecs API]
16
16
  - **Latency Modes** - Configure for real-time streaming vs maximum compression
17
17
  - **Bitrate Modes** - Constant, variable, and quantizer (CRF) encoding modes
18
18
  - **Alpha Channel** - Preserve transparency with VP9 and AV1 codecs
19
- - **Mediabunny Integration** - Custom encoders/decoders for file conversion
19
+ - **Container Support** - MP4, WebM demuxing/muxing utilities
20
20
 
21
21
  ## Documentation
22
22
 
@@ -28,17 +28,11 @@ This package provides a Node.js-compatible implementation of the [WebCodecs API]
28
28
  ## Requirements
29
29
 
30
30
  - Node.js 18+
31
- - FFmpeg with encoding libraries (libx264, libx265, libvpx, etc.)
31
+ - The `node-av` package (automatically installed as a dependency)
32
32
 
33
33
  ```bash
34
- # Ubuntu/Debian
35
- sudo apt install ffmpeg
36
-
37
- # macOS
38
- brew install ffmpeg
39
-
40
- # Check installation
41
- ffmpeg -version
34
+ # node-av provides native FFmpeg bindings - no separate FFmpeg installation required
35
+ npm install webcodecs-node
42
36
  ```
43
37
 
44
38
  ## Installation
@@ -67,7 +61,7 @@ const encoder = new VideoEncoder({
67
61
  error: (e) => console.error(e),
68
62
  });
69
63
 
70
- await encoder.configure({
64
+ encoder.configure({
71
65
  codec: 'avc1.42001E', // H.264 Baseline
72
66
  width: 1280,
73
67
  height: 720,
@@ -100,12 +94,12 @@ Encodes raw video frames to compressed video.
100
94
  const encoder = new VideoEncoder({
101
95
  output: (chunk, metadata) => {
102
96
  // chunk is EncodedVideoChunk
103
- // metadata contains timing info
97
+ // metadata contains decoder config info
104
98
  },
105
99
  error: (e) => console.error(e),
106
100
  });
107
101
 
108
- await encoder.configure({
102
+ encoder.configure({
109
103
  codec: 'avc1.42001E', // H.264
110
104
  width: 1920,
111
105
  height: 1080,
@@ -114,7 +108,6 @@ await encoder.configure({
114
108
  bitrateMode: 'variable', // Optional: 'constant', 'variable', or 'quantizer'
115
109
  latencyMode: 'realtime', // Optional: 'realtime' for streaming, 'quality' for best compression
116
110
  hardwareAcceleration: 'prefer-hardware', // Optional: use GPU encoding
117
- format: 'mp4', // Optional: 'annexb' (default) or 'mp4'
118
111
  });
119
112
 
120
113
  // Create a frame from raw RGBA data
@@ -153,7 +146,7 @@ const decoder = new VideoDecoder({
153
146
  error: (e) => console.error(e),
154
147
  });
155
148
 
156
- await decoder.configure({
149
+ decoder.configure({
157
150
  codec: 'avc1.42001E',
158
151
  codedWidth: 1920,
159
152
  codedHeight: 1080,
@@ -177,12 +170,11 @@ const encoder = new AudioEncoder({
177
170
  error: (e) => console.error(e),
178
171
  });
179
172
 
180
- await encoder.configure({
173
+ encoder.configure({
181
174
  codec: 'opus',
182
175
  sampleRate: 48000,
183
176
  numberOfChannels: 2,
184
177
  bitrate: 128000,
185
- format: 'aac', // Optional: 'adts' (default for AAC) or 'aac'
186
178
  });
187
179
 
188
180
  // Create audio data from raw samples
@@ -221,8 +213,6 @@ const imageData = readFileSync('animation.gif');
221
213
  const decoder = new ImageDecoder({
222
214
  type: 'image/gif',
223
215
  data: imageData,
224
- // Optional: transfer ownership for zero-copy
225
- // transfer: [imageData.buffer],
226
216
  });
227
217
 
228
218
  // Wait for parsing to complete
@@ -247,21 +237,6 @@ for (let i = 0; i < track.frameCount; i++) {
247
237
  decoder.close();
248
238
  ```
249
239
 
250
- **Constructor options:**
251
- - `type` - MIME type (required)
252
- - `data` - ArrayBuffer, TypedArray, or ReadableStream (required)
253
- - `transfer` - ArrayBuffer[] for zero-copy ownership
254
- - `colorSpaceConversion` - 'none' | 'default'
255
- - `desiredWidth` / `desiredHeight` - Target dimensions
256
- - `preferAnimation` - Prefer animated track if available
257
- - `premultiplyAlpha` - 'none' | 'premultiply' | 'default'
258
-
259
- **Properties:**
260
- - `type` - MIME type string
261
- - `complete` - Boolean, true when data is buffered
262
- - `completed` - Promise that resolves when ready
263
- - `tracks` - ImageTrackList with track information
264
-
265
240
  **Supported formats:**
266
241
  - `image/png`, `image/apng`
267
242
  - `image/jpeg`
@@ -317,35 +292,6 @@ if (encodeInfo.supported && encodeInfo.powerEfficient) {
317
292
  }
318
293
  ```
319
294
 
320
- **Supported containers & codecs:**
321
- | Container | Video Codecs | Audio Codecs |
322
- |-----------|-------------|--------------|
323
- | video/mp4 | H.264, HEVC, AV1 | AAC |
324
- | video/webm | VP8, VP9, AV1 | Opus, Vorbis |
325
- | audio/mp4 | - | AAC |
326
- | audio/webm | - | Opus, Vorbis |
327
- | audio/ogg | - | Opus, Vorbis, FLAC |
328
- | audio/mpeg | - | MP3 |
329
-
330
- **Result properties:**
331
- - `supported` - Whether the configuration can be decoded/encoded
332
- - `smooth` - Whether playback/encoding will be smooth (no dropped frames)
333
- - `powerEfficient` - Whether hardware acceleration is available
334
-
335
- ### MediaCapabilities Profiles
336
-
337
- By default, capability queries use heuristics (resolution, bitrate, detected hardware). You can provide a detailed profile generated from the local FFmpeg installation:
338
-
339
- ```bash
340
- # Generate a JSON profile alongside the repo (builds first)
341
- npm run capabilities:generate -- ./webcodecs-capabilities.json
342
-
343
- # Point WebCodecs at the profile
344
- export WEBCODECS_CAPABILITIES_PROFILE=$(pwd)/webcodecs-capabilities.json
345
- ```
346
-
347
- `decodingInfo` / `encodingInfo` will load that JSON (schema: `{ video: CapabilityProfileEntry[]; audio: CapabilityProfileEntry[] }`) and match codec/profile/level against those entries for precise limits. Without the env var the library falls back to its built-in heuristics.
348
-
349
295
  ### Hardware Acceleration
350
296
 
351
297
  Detect and use hardware encoding/decoding:
@@ -372,7 +318,7 @@ const best = await getBestEncoder('h264', 'prefer-hardware');
372
318
  console.log(`Using: ${best.encoder} (hardware: ${best.isHardware})`);
373
319
 
374
320
  // Use in VideoEncoder config
375
- await encoder.configure({
321
+ encoder.configure({
376
322
  codec: 'avc1.42001E',
377
323
  width: 1920,
378
324
  height: 1080,
@@ -385,23 +331,38 @@ await encoder.configure({
385
331
  - **VAAPI** - Intel/AMD on Linux
386
332
  - **NVENC/NVDEC** - NVIDIA GPUs
387
333
  - **QSV** - Intel Quick Sync Video
388
- - **VideoToolbox** - macOS (planned)
334
+ - **VideoToolbox** - macOS
389
335
 
390
- ### Streaming & Latency Modes
336
+ ### Container Utilities
391
337
 
392
- For real-time streaming applications, use `latencyMode: 'realtime'` to minimize encoding latency:
338
+ Import container demuxing/muxing utilities for working with MP4 and WebM files:
393
339
 
394
340
  ```typescript
395
- // Real-time streaming encoder
396
- const encoder = new VideoEncoder({
397
- output: (chunk) => {
398
- // Send chunk immediately over network
399
- streamToClient(chunk);
400
- },
401
- error: console.error,
341
+ import { Mp4Demuxer, WebmMuxer } from 'webcodecs-node/containers';
342
+
343
+ // Demux an MP4 file
344
+ const demuxer = new Mp4Demuxer(mp4Data);
345
+ await demuxer.initialize();
346
+
347
+ for await (const sample of demuxer.videoSamples()) {
348
+ // sample contains encoded video chunks
349
+ }
350
+
351
+ // Mux encoded chunks to WebM
352
+ const muxer = new WebmMuxer({
353
+ video: { codec: 'vp9', width: 1920, height: 1080 },
402
354
  });
403
355
 
404
- await encoder.configure({
356
+ muxer.addVideoChunk(encodedChunk, metadata);
357
+ const webmData = muxer.finalize();
358
+ ```
359
+
360
+ ### Streaming & Latency Modes
361
+
362
+ For real-time streaming applications, use `latencyMode: 'realtime'` to minimize encoding latency:
363
+
364
+ ```typescript
365
+ encoder.configure({
405
366
  codec: 'avc1.42001E',
406
367
  width: 1280,
407
368
  height: 720,
@@ -409,40 +370,18 @@ await encoder.configure({
409
370
  framerate: 30,
410
371
  latencyMode: 'realtime', // Prioritize low latency
411
372
  });
412
-
413
- // Process frames as they arrive
414
- camera.on('frame', (frameData) => {
415
- const frame = new VideoFrame(frameData, {
416
- format: 'RGBA',
417
- codedWidth: 1280,
418
- codedHeight: 720,
419
- timestamp: Date.now() * 1000,
420
- });
421
-
422
- encoder.encode(frame);
423
- frame.close();
424
- });
425
373
  ```
426
374
 
427
375
  **Latency mode options:**
428
376
  - `'quality'` (default) - Best compression, higher latency (uses B-frames, lookahead)
429
377
  - `'realtime'` - Minimum latency for live streaming (no B-frames, zero-delay)
430
378
 
431
- **Codec-specific optimizations in realtime mode:**
432
- | Codec | Quality Mode | Realtime Mode |
433
- |-------|-------------|---------------|
434
- | H.264 | B-frames, rc-lookahead | zerolatency tune, no B-frames |
435
- | H.265 | B-frames, lookahead | zerolatency tune, no B-frames |
436
- | VP8 | Default settings | deadline=realtime, cpu-used=8 |
437
- | VP9 | row-mt, tile-columns | deadline=realtime, cpu-used=8 |
438
- | AV1 | Default settings | usage=realtime, cpu-used=8 |
439
-
440
379
  ### Bitrate Modes
441
380
 
442
381
  Control how bitrate is managed during encoding:
443
382
 
444
383
  ```typescript
445
- await encoder.configure({
384
+ encoder.configure({
446
385
  codec: 'avc1.42001E',
447
386
  width: 1920,
448
387
  height: 1080,
@@ -451,7 +390,6 @@ await encoder.configure({
451
390
  });
452
391
  ```
453
392
 
454
- **Bitrate mode options:**
455
393
  | Mode | Description | Use Case |
456
394
  |------|-------------|----------|
457
395
  | `'variable'` | VBR - varies bitrate for quality (default) | General purpose |
@@ -463,8 +401,7 @@ await encoder.configure({
463
401
  Preserve transparency when encoding with VP9 or AV1:
464
402
 
465
403
  ```typescript
466
- // Encode video with alpha channel
467
- await encoder.configure({
404
+ encoder.configure({
468
405
  codec: 'vp9',
469
406
  width: 1920,
470
407
  height: 1080,
@@ -482,72 +419,6 @@ const frame = new VideoFrame(rgbaWithAlpha, {
482
419
  encoder.encode(frame);
483
420
  ```
484
421
 
485
- **Alpha options:**
486
- - `'discard'` (default) - Strip alpha channel (works with all codecs)
487
- - `'keep'` - Preserve transparency (VP9 and AV1 only)
488
-
489
- ## Mediabunny Integration
490
-
491
- For file-to-file conversion, use with [Mediabunny](https://mediabunny.dev):
492
-
493
- ```typescript
494
- import { ReadableStream, WritableStream, TransformStream } from 'stream/web';
495
- import { installWebCodecsPolyfill } from 'webcodecs-node';
496
-
497
- // Polyfill Web Streams
498
- if (typeof globalThis.WritableStream === 'undefined') {
499
- globalThis.WritableStream = WritableStream;
500
- }
501
- if (typeof globalThis.ReadableStream === 'undefined') {
502
- globalThis.ReadableStream = ReadableStream;
503
- }
504
- if (typeof globalThis.TransformStream === 'undefined') {
505
- globalThis.TransformStream = TransformStream;
506
- }
507
-
508
- // Install WebCodecs
509
- installWebCodecsPolyfill();
510
-
511
- import {
512
- Input,
513
- Output,
514
- Conversion,
515
- FilePathSource,
516
- FilePathTarget,
517
- Mp4OutputFormat,
518
- ALL_FORMATS,
519
- registerEncoder,
520
- registerDecoder,
521
- } from 'mediabunny';
522
-
523
- import { FFmpegVideoEncoder } from 'webcodecs-node/mediabunny/FFmpegVideoEncoder';
524
- import { FFmpegVideoDecoder } from 'webcodecs-node/mediabunny/FFmpegVideoDecoder';
525
- import { FFmpegAudioEncoder } from 'webcodecs-node/mediabunny/FFmpegAudioEncoder';
526
- import { FFmpegAudioDecoder } from 'webcodecs-node/mediabunny/FFmpegAudioDecoder';
527
-
528
- // Register FFmpeg-backed encoders/decoders
529
- registerEncoder(FFmpegVideoEncoder);
530
- registerEncoder(FFmpegAudioEncoder);
531
- registerDecoder(FFmpegVideoDecoder);
532
- registerDecoder(FFmpegAudioDecoder);
533
-
534
- // Convert video
535
- const input = new Input({
536
- formats: ALL_FORMATS,
537
- source: new FilePathSource('input.mkv'),
538
- });
539
-
540
- const output = new Output({
541
- format: new Mp4OutputFormat(),
542
- target: new FilePathTarget('output.mp4'),
543
- });
544
-
545
- const conversion = await Conversion.init({ input, output });
546
- await conversion.execute();
547
-
548
- console.log('Conversion complete!');
549
- ```
550
-
551
422
  ## Demos
552
423
 
553
424
  Run the included demos to test functionality:
@@ -555,27 +426,32 @@ Run the included demos to test functionality:
555
426
  ```bash
556
427
  npm run build
557
428
 
558
- # Basic WebCodecs demo
429
+ # Basic demo
430
+ npm run demo
431
+
432
+ # WebCodecs API demo
559
433
  npm run demo:webcodecs
560
434
 
561
435
  # Image decoding demo (animated GIF/PNG/WebP with frame timing)
562
436
  npm run demo:image
563
437
 
564
- # Streaming demo (real-time encoding with latency comparison)
565
- npm run demo:streaming
566
-
567
- # File conversion with Mediabunny
568
- npm run demo:conversion
569
-
570
438
  # Hardware acceleration detection
571
439
  npm run demo:hwaccel
572
440
 
573
- # Hardware vs software encoding comparison
574
- npm run demo:hwaccel-conversion
441
+ # Streaming demo (real-time encoding)
442
+ npm run demo:streaming
443
+
444
+ # Sample-based encoding demo
445
+ npm run demo:samples
446
+
447
+ # Container demuxing/muxing demo
448
+ npm run demo:containers
575
449
 
576
- # Video quadrant compositor demo (WebGPU four-up render → MP4)
577
- # Requires Node 20+; set hardwareAcceleration in src/demos/demo-four-corners.ts to 'prefer-hardware' if VAAPI/NVENC/QSV are available
450
+ # Video quadrant compositor demo (four-up render)
578
451
  npm run demo:fourcorners
452
+
453
+ # 1080p transcoding demo
454
+ npm run demo:1080p
579
455
  ```
580
456
 
581
457
  ## API Compatibility
@@ -600,12 +476,16 @@ This implementation follows the [WebCodecs specification](https://www.w3.org/TR/
600
476
  | bitrateMode | ✓ | ✓ |
601
477
  | alpha (transparency) | ✓ | ✓ (VP9, AV1) |
602
478
  | isConfigSupported() | ✓ | ✓ |
603
- | isTypeSupported() | ✓ | ✓ |
604
479
 
605
- **Notes:**
606
- - Hardware acceleration defaults to software encoding for reliability. Use `hardwareAcceleration: 'prefer-hardware'` to enable GPU acceleration.
607
- - ImageDecoder supports animated image frame timing (duration, timestamp) and loop count (repetitionCount).
480
+ ## Architecture
481
+
482
+ This library uses **node-av** as its backend, which provides native bindings to FFmpeg's libav* libraries. This approach offers:
483
+
484
+ - **Native performance** - Direct library calls instead of subprocess spawning
485
+ - **Lower latency** - No IPC overhead between Node.js and FFmpeg
486
+ - **Better resource management** - Native memory handling and cleanup
487
+ - **Simplified deployment** - No need for separate FFmpeg installation
608
488
 
609
489
  ## License
610
490
 
611
- webcodecs-node is distributed under the GNU Affero General Public License v3.0. Files located under `src/mediabunny/` remain available under the MIT License to preserve compatibility with Mediabunny integrations. See `LICENSE` for full terms.
491
+ webcodecs-node is distributed under the GNU Affero General Public License v3.0. See `LICENSE` for full terms.
package/docs/api.md CHANGED
@@ -496,7 +496,7 @@ const info = await mediaCapabilities.encodingInfo({
496
496
 
497
497
  ### Capability Profiles
498
498
 
499
- You can generate a hardware-specific capability profile to make `mediaCapabilities` match your actual FFmpeg install. Use the provided CLI:
499
+ You can generate a hardware-specific capability profile to make `mediaCapabilities` match your actual system capabilities. Use the provided CLI:
500
500
 
501
501
  ```bash
502
502
  npm run capabilities:generate -- ./webcodecs-capabilities.json
@@ -93,17 +93,6 @@ encoder.configure({
93
93
  - Unpredictable file size
94
94
  - Not suitable for streaming
95
95
 
96
- ### Codec-Specific Implementation
97
-
98
- Different codecs use different parameters for each bitrate mode:
99
-
100
- | Codec | CBR | VBR | Quantizer |
101
- |-------|-----|-----|-----------|
102
- | H.264 | `-b:v` + `maxrate`/`bufsize`, CBR mode | `-b:v` only | `-crf 23` |
103
- | H.265 | `-b:v` + `maxrate`/`bufsize`, CBR mode | `-b:v` only | `-crf 28` |
104
- | VP8/VP9 | `-b:v` + `minrate`/`maxrate` | `-b:v` only | `-crf 31` + `-b:v 0` |
105
- | AV1 | `-b:v` + `maxrate`/`bufsize` | `-b:v` only | `-crf 30` |
106
-
107
96
  ---
108
97
 
109
98
  ## Alpha Channel Handling
@@ -131,7 +120,7 @@ The `alpha` option controls how transparent pixels are handled during encoding.
131
120
 
132
121
  ## Output Bitstream Format
133
122
 
134
- By default the encoders emit Annex B (video) and ADTS/OGG (audio) streams directly from FFmpeg. If you need MP4-style payloads (length-prefixed NAL units, raw AAC frames) you can opt-in via the `format` config field.
123
+ By default the encoders emit Annex B (video) and ADTS/OGG (audio) bitstreams. If you need MP4-style payloads (length-prefixed NAL units, raw AAC frames) you can opt-in via the `format` config field.
135
124
 
136
125
  ### VideoEncoder `format`
137
126
 
@@ -266,11 +255,11 @@ encoder.configure({
266
255
 
267
256
  | Codec | Quality Mode | Realtime Mode |
268
257
  |-------|--------------|---------------|
269
- | H.264 | Default (B-frames, lookahead) | `-tune zerolatency`, no B-frames |
270
- | H.265 | Default settings | `-tune zerolatency`, no B-frames |
271
- | VP8 | Default | `-deadline realtime`, `-cpu-used 8` |
272
- | VP9 | Row multithreading, tile columns | `-deadline realtime`, `-cpu-used 8` |
273
- | AV1 | Default | `-usage realtime`, `-cpu-used 8` |
258
+ | H.264 | B-frames, lookahead enabled | Zero-latency tuning, no B-frames |
259
+ | H.265 | Default settings | Zero-latency tuning, no B-frames |
260
+ | VP8 | Default | Realtime deadline, fast encoding |
261
+ | VP9 | Row multithreading, tile columns | Realtime deadline, fast encoding |
262
+ | AV1 | Default | Realtime usage, fast encoding |
274
263
 
275
264
  **Example for live streaming:**
276
265
 
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "webcodecs-node",
3
- "version": "0.5.0",
4
- "description": "WebCodecs API implementation for Node.js using FFmpeg",
3
+ "version": "0.5.2",
4
+ "description": "WebCodecs API implementation for Node.js using node-av",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
7
7
  "type": "module",
@@ -1,6 +0,0 @@
1
- /**
2
- * AudioData - Re-export from new location for backwards compatibility
3
- * @deprecated Import from './core/AudioData.js' instead
4
- */
5
- export * from './core/AudioData.js';
6
- //# sourceMappingURL=AudioData.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"AudioData.d.ts","sourceRoot":"","sources":["../src/AudioData.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,cAAc,qBAAqB,CAAC"}
package/dist/AudioData.js DELETED
@@ -1,6 +0,0 @@
1
- /**
2
- * AudioData - Re-export from new location for backwards compatibility
3
- * @deprecated Import from './core/AudioData.js' instead
4
- */
5
- export * from './core/AudioData.js';
6
- //# sourceMappingURL=AudioData.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"AudioData.js","sourceRoot":"","sources":["../src/AudioData.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,cAAc,qBAAqB,CAAC"}
@@ -1,6 +0,0 @@
1
- /**
2
- * AudioDecoder - Re-export from new location for backwards compatibility
3
- * @deprecated Import from './decoders/AudioDecoder.js' instead
4
- */
5
- export * from './decoders/AudioDecoder.js';
6
- //# sourceMappingURL=AudioDecoder.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"AudioDecoder.d.ts","sourceRoot":"","sources":["../src/AudioDecoder.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,cAAc,4BAA4B,CAAC"}
@@ -1,6 +0,0 @@
1
- /**
2
- * AudioDecoder - Re-export from new location for backwards compatibility
3
- * @deprecated Import from './decoders/AudioDecoder.js' instead
4
- */
5
- export * from './decoders/AudioDecoder.js';
6
- //# sourceMappingURL=AudioDecoder.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"AudioDecoder.js","sourceRoot":"","sources":["../src/AudioDecoder.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,cAAc,4BAA4B,CAAC"}
@@ -1,6 +0,0 @@
1
- /**
2
- * AudioEncoder - Re-export from new location for backwards compatibility
3
- * @deprecated Import from './encoders/AudioEncoder.js' instead
4
- */
5
- export * from './encoders/AudioEncoder.js';
6
- //# sourceMappingURL=AudioEncoder.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"AudioEncoder.d.ts","sourceRoot":"","sources":["../src/AudioEncoder.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,cAAc,4BAA4B,CAAC"}
@@ -1,6 +0,0 @@
1
- /**
2
- * AudioEncoder - Re-export from new location for backwards compatibility
3
- * @deprecated Import from './encoders/AudioEncoder.js' instead
4
- */
5
- export * from './encoders/AudioEncoder.js';
6
- //# sourceMappingURL=AudioEncoder.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"AudioEncoder.js","sourceRoot":"","sources":["../src/AudioEncoder.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,cAAc,4BAA4B,CAAC"}
@@ -1,6 +0,0 @@
1
- /**
2
- * EncodedAudioChunk - Re-export from new location for backwards compatibility
3
- * @deprecated Import from './core/EncodedAudioChunk.js' instead
4
- */
5
- export * from './core/EncodedAudioChunk.js';
6
- //# sourceMappingURL=EncodedAudioChunk.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"EncodedAudioChunk.d.ts","sourceRoot":"","sources":["../src/EncodedAudioChunk.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,cAAc,6BAA6B,CAAC"}
@@ -1,6 +0,0 @@
1
- /**
2
- * EncodedAudioChunk - Re-export from new location for backwards compatibility
3
- * @deprecated Import from './core/EncodedAudioChunk.js' instead
4
- */
5
- export * from './core/EncodedAudioChunk.js';
6
- //# sourceMappingURL=EncodedAudioChunk.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"EncodedAudioChunk.js","sourceRoot":"","sources":["../src/EncodedAudioChunk.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,cAAc,6BAA6B,CAAC"}
@@ -1,6 +0,0 @@
1
- /**
2
- * EncodedVideoChunk - Re-export from new location for backwards compatibility
3
- * @deprecated Import from './core/EncodedVideoChunk.js' instead
4
- */
5
- export * from './core/EncodedVideoChunk.js';
6
- //# sourceMappingURL=EncodedVideoChunk.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"EncodedVideoChunk.d.ts","sourceRoot":"","sources":["../src/EncodedVideoChunk.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,cAAc,6BAA6B,CAAC"}
@@ -1,6 +0,0 @@
1
- /**
2
- * EncodedVideoChunk - Re-export from new location for backwards compatibility
3
- * @deprecated Import from './core/EncodedVideoChunk.js' instead
4
- */
5
- export * from './core/EncodedVideoChunk.js';
6
- //# sourceMappingURL=EncodedVideoChunk.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"EncodedVideoChunk.js","sourceRoot":"","sources":["../src/EncodedVideoChunk.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,cAAc,6BAA6B,CAAC"}
@@ -1,6 +0,0 @@
1
- /**
2
- * FFmpegProcess - Re-export from new location for backwards compatibility
3
- * @deprecated Import from './ffmpeg/FFmpegProcess.js' instead
4
- */
5
- export * from './ffmpeg/FFmpegProcess.js';
6
- //# sourceMappingURL=FFmpegProcess.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"FFmpegProcess.d.ts","sourceRoot":"","sources":["../src/FFmpegProcess.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,cAAc,2BAA2B,CAAC"}
@@ -1,6 +0,0 @@
1
- /**
2
- * FFmpegProcess - Re-export from new location for backwards compatibility
3
- * @deprecated Import from './ffmpeg/FFmpegProcess.js' instead
4
- */
5
- export * from './ffmpeg/FFmpegProcess.js';
6
- //# sourceMappingURL=FFmpegProcess.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"FFmpegProcess.js","sourceRoot":"","sources":["../src/FFmpegProcess.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,cAAc,2BAA2B,CAAC"}
@@ -1,6 +0,0 @@
1
- /**
2
- * HardwareAcceleration - Re-export from new location for backwards compatibility
3
- * @deprecated Import from './hardware/HardwareAcceleration.js' instead
4
- */
5
- export * from './hardware/HardwareAcceleration.js';
6
- //# sourceMappingURL=HardwareAcceleration.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"HardwareAcceleration.d.ts","sourceRoot":"","sources":["../src/HardwareAcceleration.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,cAAc,oCAAoC,CAAC"}
@@ -1,6 +0,0 @@
1
- /**
2
- * HardwareAcceleration - Re-export from new location for backwards compatibility
3
- * @deprecated Import from './hardware/HardwareAcceleration.js' instead
4
- */
5
- export * from './hardware/HardwareAcceleration.js';
6
- //# sourceMappingURL=HardwareAcceleration.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"HardwareAcceleration.js","sourceRoot":"","sources":["../src/HardwareAcceleration.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,cAAc,oCAAoC,CAAC"}
@@ -1,6 +0,0 @@
1
- /**
2
- * ImageDecoder - Re-export from new location for backwards compatibility
3
- * @deprecated Import from './decoders/ImageDecoder.js' instead
4
- */
5
- export * from './decoders/ImageDecoder.js';
6
- //# sourceMappingURL=ImageDecoder.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"ImageDecoder.d.ts","sourceRoot":"","sources":["../src/ImageDecoder.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,cAAc,4BAA4B,CAAC"}
@@ -1,6 +0,0 @@
1
- /**
2
- * ImageDecoder - Re-export from new location for backwards compatibility
3
- * @deprecated Import from './decoders/ImageDecoder.js' instead
4
- */
5
- export * from './decoders/ImageDecoder.js';
6
- //# sourceMappingURL=ImageDecoder.js.map