@napi-rs/webcodecs 1.0.0 → 1.1.1
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 +147 -1
- package/index.d.ts +620 -24
- package/index.js +58 -52
- package/package.json +12 -11
package/README.md
CHANGED
|
@@ -9,6 +9,7 @@ WebCodecs API implementation for Node.js using FFmpeg, built with [NAPI-RS](http
|
|
|
9
9
|
- **W3C WebCodecs API compliant** - Full implementation of the WebCodecs specification with native `DOMException` errors
|
|
10
10
|
- **Video encoding/decoding** - H.264, H.265, VP8, VP9, AV1
|
|
11
11
|
- **Audio encoding/decoding** - AAC, Opus, MP3, FLAC, Vorbis, PCM variants
|
|
12
|
+
- **Container muxing/demuxing** - MP4, WebM, MKV containers with seeking support
|
|
12
13
|
- **Image decoding** - JPEG, PNG, WebP, GIF, BMP, AVIF
|
|
13
14
|
- **Canvas integration** - Create VideoFrames from `@napi-rs/canvas` for graphics and text rendering
|
|
14
15
|
- **Hardware acceleration** - Zero-copy GPU encoding with VideoToolbox (macOS), NVENC (NVIDIA), VAAPI (Linux), QSV (Intel)
|
|
@@ -18,7 +19,7 @@ WebCodecs API implementation for Node.js using FFmpeg, built with [NAPI-RS](http
|
|
|
18
19
|
## Installation
|
|
19
20
|
|
|
20
21
|
```bash
|
|
21
|
-
|
|
22
|
+
bun add @napi-rs/webcodecs
|
|
22
23
|
# or
|
|
23
24
|
pnpm add @napi-rs/webcodecs
|
|
24
25
|
# or
|
|
@@ -166,6 +167,141 @@ result.image.close()
|
|
|
166
167
|
decoder.close()
|
|
167
168
|
```
|
|
168
169
|
|
|
170
|
+
### Container Demuxing
|
|
171
|
+
|
|
172
|
+
Read encoded video/audio from MP4, WebM, or MKV containers:
|
|
173
|
+
|
|
174
|
+
```typescript
|
|
175
|
+
import { Mp4Demuxer, VideoDecoder, AudioDecoder } from '@napi-rs/webcodecs'
|
|
176
|
+
|
|
177
|
+
// Create decoder instances
|
|
178
|
+
const videoDecoder = new VideoDecoder({
|
|
179
|
+
output: (frame) => {
|
|
180
|
+
console.log(`Decoded frame: ${frame.timestamp}`)
|
|
181
|
+
frame.close()
|
|
182
|
+
},
|
|
183
|
+
error: (e) => console.error(e),
|
|
184
|
+
})
|
|
185
|
+
|
|
186
|
+
const audioDecoder = new AudioDecoder({
|
|
187
|
+
output: (data) => {
|
|
188
|
+
console.log(`Decoded audio: ${data.timestamp}`)
|
|
189
|
+
data.close()
|
|
190
|
+
},
|
|
191
|
+
error: (e) => console.error(e),
|
|
192
|
+
})
|
|
193
|
+
|
|
194
|
+
// Create demuxer with callbacks
|
|
195
|
+
const demuxer = new Mp4Demuxer({
|
|
196
|
+
videoOutput: (chunk) => videoDecoder.decode(chunk),
|
|
197
|
+
audioOutput: (chunk) => audioDecoder.decode(chunk),
|
|
198
|
+
error: (e) => console.error(e),
|
|
199
|
+
})
|
|
200
|
+
|
|
201
|
+
// Load from file or buffer
|
|
202
|
+
await demuxer.load('./video.mp4')
|
|
203
|
+
// or: await demuxer.loadBuffer(uint8Array)
|
|
204
|
+
|
|
205
|
+
// Configure decoders with extracted configs
|
|
206
|
+
videoDecoder.configure(demuxer.videoDecoderConfig)
|
|
207
|
+
audioDecoder.configure(demuxer.audioDecoderConfig)
|
|
208
|
+
|
|
209
|
+
// Get track info
|
|
210
|
+
console.log(demuxer.tracks) // Array of track info
|
|
211
|
+
console.log(demuxer.duration) // Duration in microseconds
|
|
212
|
+
|
|
213
|
+
// Demux all packets (calls callbacks)
|
|
214
|
+
demuxer.demux()
|
|
215
|
+
|
|
216
|
+
// Or demux in batches
|
|
217
|
+
demuxer.demux(100) // Demux up to 100 packets
|
|
218
|
+
|
|
219
|
+
// Seek to timestamp (microseconds)
|
|
220
|
+
demuxer.seek(5_000_000) // Seek to 5 seconds
|
|
221
|
+
|
|
222
|
+
demuxer.close()
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
### Container Muxing
|
|
226
|
+
|
|
227
|
+
Write encoded video/audio to MP4, WebM, or MKV containers:
|
|
228
|
+
|
|
229
|
+
```typescript
|
|
230
|
+
import { Mp4Muxer, VideoEncoder, AudioEncoder } from '@napi-rs/webcodecs'
|
|
231
|
+
import { writeFileSync } from 'fs'
|
|
232
|
+
|
|
233
|
+
// Create muxer
|
|
234
|
+
const muxer = new Mp4Muxer({ fastStart: true })
|
|
235
|
+
|
|
236
|
+
// Track description will be set from encoder metadata
|
|
237
|
+
let videoDescription: Uint8Array | undefined
|
|
238
|
+
|
|
239
|
+
// Create encoder that feeds into muxer
|
|
240
|
+
const videoEncoder = new VideoEncoder({
|
|
241
|
+
output: (chunk, metadata) => {
|
|
242
|
+
// Capture codec description from first keyframe
|
|
243
|
+
if (metadata?.decoderConfig?.description) {
|
|
244
|
+
videoDescription = metadata.decoderConfig.description
|
|
245
|
+
}
|
|
246
|
+
muxer.addVideoChunk(chunk, metadata)
|
|
247
|
+
},
|
|
248
|
+
error: (e) => console.error(e),
|
|
249
|
+
})
|
|
250
|
+
|
|
251
|
+
videoEncoder.configure({
|
|
252
|
+
codec: 'avc1.42001E',
|
|
253
|
+
width: 1920,
|
|
254
|
+
height: 1080,
|
|
255
|
+
bitrate: 5_000_000,
|
|
256
|
+
})
|
|
257
|
+
|
|
258
|
+
// Add tracks before adding chunks
|
|
259
|
+
muxer.addVideoTrack({
|
|
260
|
+
codec: 'avc1.42001E',
|
|
261
|
+
width: 1920,
|
|
262
|
+
height: 1080,
|
|
263
|
+
})
|
|
264
|
+
|
|
265
|
+
// Encode frames...
|
|
266
|
+
// videoEncoder.encode(frame)
|
|
267
|
+
|
|
268
|
+
await videoEncoder.flush()
|
|
269
|
+
|
|
270
|
+
// Finalize and write output
|
|
271
|
+
const mp4Data = muxer.finalize()
|
|
272
|
+
writeFileSync('output.mp4', mp4Data)
|
|
273
|
+
muxer.close()
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
#### Streaming Muxer Mode
|
|
277
|
+
|
|
278
|
+
For live streaming or large files, use streaming mode:
|
|
279
|
+
|
|
280
|
+
```typescript
|
|
281
|
+
import { Mp4Muxer } from '@napi-rs/webcodecs'
|
|
282
|
+
|
|
283
|
+
const muxer = new Mp4Muxer({
|
|
284
|
+
fragmented: true, // Required for MP4 streaming
|
|
285
|
+
streaming: { bufferCapacity: 256 * 1024 },
|
|
286
|
+
})
|
|
287
|
+
|
|
288
|
+
muxer.addVideoTrack({ codec: 'avc1.42001E', width: 1920, height: 1080 })
|
|
289
|
+
|
|
290
|
+
// Add chunks as they arrive
|
|
291
|
+
muxer.addVideoChunk(chunk, metadata)
|
|
292
|
+
|
|
293
|
+
// Read available data incrementally
|
|
294
|
+
const data = muxer.read()
|
|
295
|
+
if (data) {
|
|
296
|
+
stream.write(data)
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
// Check when finished
|
|
300
|
+
if (muxer.isFinished) {
|
|
301
|
+
stream.end()
|
|
302
|
+
}
|
|
303
|
+
```
|
|
304
|
+
|
|
169
305
|
### VideoFrame from Canvas
|
|
170
306
|
|
|
171
307
|
Create VideoFrames from `@napi-rs/canvas` for graphics, text rendering, or image compositing:
|
|
@@ -235,6 +371,14 @@ frame.close()
|
|
|
235
371
|
| BMP | `image/bmp` | ✅ |
|
|
236
372
|
| AVIF | `image/avif` | ✅ |
|
|
237
373
|
|
|
374
|
+
### Containers
|
|
375
|
+
|
|
376
|
+
| Container | Video Codecs | Audio Codecs | Demuxer | Muxer |
|
|
377
|
+
| --------- | --------------------------- | ---------------------------- | ------------- | ----------- |
|
|
378
|
+
| MP4 | H.264, H.265, AV1 | AAC, Opus, MP3, FLAC | `Mp4Demuxer` | `Mp4Muxer` |
|
|
379
|
+
| WebM | VP8, VP9, AV1 | Opus, Vorbis | `WebMDemuxer` | `WebMMuxer` |
|
|
380
|
+
| MKV | H.264, H.265, VP8, VP9, AV1 | AAC, Opus, Vorbis, FLAC, MP3 | `MkvDemuxer` | `MkvMuxer` |
|
|
381
|
+
|
|
238
382
|
## Platform Support
|
|
239
383
|
|
|
240
384
|
Pre-built binaries are available for:
|
|
@@ -436,6 +580,8 @@ This package implements the [W3C WebCodecs API](https://w3c.github.io/webcodecs/
|
|
|
436
580
|
- `EncodedVideoChunk` / `EncodedAudioChunk` - Encoded media data
|
|
437
581
|
- `ImageDecoder` - Static image decoding
|
|
438
582
|
- `VideoColorSpace` - Color space information
|
|
583
|
+
- `Mp4Demuxer` / `WebMDemuxer` / `MkvDemuxer` - Container demuxing with seeking
|
|
584
|
+
- `Mp4Muxer` / `WebMMuxer` / `MkvMuxer` - Container muxing with streaming support
|
|
439
585
|
|
|
440
586
|
All encoders and decoders implement the `EventTarget` interface with `addEventListener()`, `removeEventListener()`, and `dispatchEvent()`.
|
|
441
587
|
|