@napi-rs/webcodecs 0.0.0 → 1.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/README.md CHANGED
@@ -10,6 +10,7 @@ WebCodecs API implementation for Node.js using FFmpeg, built with [NAPI-RS](http
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
12
  - **Image decoding** - JPEG, PNG, WebP, GIF, BMP, AVIF
13
+ - **Canvas integration** - Create VideoFrames from `@napi-rs/canvas` for graphics and text rendering
13
14
  - **Hardware acceleration** - Zero-copy GPU encoding with VideoToolbox (macOS), NVENC (NVIDIA), VAAPI (Linux), QSV (Intel)
14
15
  - **Cross-platform** - macOS, Windows, Linux (glibc/musl, x64/arm64/armv7)
15
16
  - **Structured logging** - FFmpeg logs redirected to Rust `tracing` crate for easy integration
@@ -24,6 +25,14 @@ pnpm add @napi-rs/webcodecs
24
25
  yarn add @napi-rs/webcodecs
25
26
  ```
26
27
 
28
+ ### Optional: Canvas Integration
29
+
30
+ For creating VideoFrames from canvas content, install `@napi-rs/canvas`:
31
+
32
+ ```bash
33
+ npm install @napi-rs/canvas
34
+ ```
35
+
27
36
  ## Quick Start
28
37
 
29
38
  ### Video Encoding
@@ -157,6 +166,39 @@ result.image.close()
157
166
  decoder.close()
158
167
  ```
159
168
 
169
+ ### VideoFrame from Canvas
170
+
171
+ Create VideoFrames from `@napi-rs/canvas` for graphics, text rendering, or image compositing:
172
+
173
+ ```typescript
174
+ import { VideoFrame } from '@napi-rs/webcodecs'
175
+ import { createCanvas } from '@napi-rs/canvas'
176
+
177
+ const canvas = createCanvas(1920, 1080)
178
+ const ctx = canvas.getContext('2d')
179
+
180
+ // Draw graphics
181
+ ctx.fillStyle = '#FF0000'
182
+ ctx.fillRect(0, 0, 1920, 1080)
183
+ ctx.fillStyle = '#FFFFFF'
184
+ ctx.font = '48px sans-serif'
185
+ ctx.fillText('Hello WebCodecs!', 100, 100)
186
+
187
+ // Create VideoFrame from canvas (timestamp required per W3C spec)
188
+ const frame = new VideoFrame(canvas, {
189
+ timestamp: 0,
190
+ duration: 33333, // optional: frame duration in microseconds
191
+ })
192
+
193
+ console.log(frame.format) // 'RGBA'
194
+ console.log(frame.codedWidth, frame.codedHeight) // 1920, 1080
195
+
196
+ // Use with VideoEncoder (see Video Encoding section)
197
+ frame.close()
198
+ ```
199
+
200
+ **Note:** Canvas pixel data is copied as RGBA format with sRGB color space.
201
+
160
202
  ## Supported Codecs
161
203
 
162
204
  ### Video
@@ -213,7 +255,7 @@ This implementation is validated against the [W3C Web Platform Tests](https://gi
213
255
 
214
256
  | Status | Count | Percentage |
215
257
  | ----------- | ----- | ---------- |
216
- | **Passing** | 522 | 99.1% |
258
+ | **Passing** | 573 | 99.1% |
217
259
  | **Skipped** | 5 | 0.9% |
218
260
  | **Failing** | 0 | 0% |
219
261
 
@@ -296,6 +338,40 @@ const encoder = new VideoEncoder({
296
338
  })
297
339
  ```
298
340
 
341
+ ### VideoFrame Format Conversion
342
+
343
+ `VideoFrame.copyTo()` and `VideoFrame.allocationSize()` support format conversion per W3C WebCodecs spec:
344
+
345
+ ```typescript
346
+ const frame = new VideoFrame(i420Data, {
347
+ format: 'I420',
348
+ codedWidth: 1920,
349
+ codedHeight: 1080,
350
+ timestamp: 0,
351
+ })
352
+
353
+ // Get allocation size for RGBA output
354
+ const rgbaSize = frame.allocationSize({ format: 'RGBA' })
355
+
356
+ // Copy with format conversion (I420 → RGBA)
357
+ const rgbaBuffer = new Uint8Array(rgbaSize)
358
+ const layout = await frame.copyTo(rgbaBuffer, { format: 'RGBA' })
359
+
360
+ frame.close()
361
+ ```
362
+
363
+ **Supported conversions:**
364
+
365
+ | Source Format | Target Format | Status |
366
+ | ---------------------------- | ---------------------- | -------------------- |
367
+ | I420, I422, I444, NV12, NV21 | RGBA, RGBX, BGRA, BGRX | ✅ |
368
+ | RGBA, RGBX, BGRA, BGRX | RGBA, RGBX, BGRA, BGRX | ✅ |
369
+ | RGBA, RGBX, BGRA, BGRX | I420, I422, I444, NV12 | ❌ NotSupportedError |
370
+
371
+ Per WPT `videoFrame-copyTo-rgb.any.js`, RGB-to-YUV conversion throws `NotSupportedError`.
372
+
373
+ Custom layouts with overflow-inducing values (e.g., `offset: 2³²-2`) throw `TypeError` via checked arithmetic. Rect alignment is validated against the source format during conversion.
374
+
299
375
  ### ImageDecoder Options
300
376
 
301
377
  ImageDecoder supports all W3C spec options:
@@ -355,7 +431,7 @@ This package implements the [W3C WebCodecs API](https://w3c.github.io/webcodecs/
355
431
 
356
432
  - `VideoEncoder` / `VideoDecoder` - Video encoding and decoding with EventTarget support
357
433
  - `AudioEncoder` / `AudioDecoder` - Audio encoding and decoding with EventTarget support
358
- - `VideoFrame` - Raw video frame data (RGBA formats default to sRGB colorSpace)
434
+ - `VideoFrame` - Raw video frame data (supports buffer data, existing VideoFrame, or `@napi-rs/canvas` Canvas)
359
435
  - `AudioData` - Raw audio sample data
360
436
  - `EncodedVideoChunk` / `EncodedAudioChunk` - Encoded media data
361
437
  - `ImageDecoder` - Static image decoding
package/index.d.ts CHANGED
@@ -33,6 +33,33 @@ export {
33
33
  AllowSharedBufferSource,
34
34
  } from './standard'
35
35
 
36
+ export type TypedArray =
37
+ | Int8Array
38
+ | Uint8Array
39
+ | Uint8ClampedArray
40
+ | Int16Array
41
+ | Uint16Array
42
+ | Int32Array
43
+ | Uint32Array
44
+ | Float32Array
45
+ | Float64Array
46
+ | BigInt64Array
47
+ | BigUint64Array
48
+
49
+ /**
50
+ * Interface for Canvas-like objects compatible with VideoFrame constructor.
51
+ * Compatible with @napi-rs/canvas Canvas class.
52
+ *
53
+ * @napi-rs/canvas is an optional peer dependency. If installed, Canvas objects
54
+ * can be used as VideoFrame sources. The Canvas pixel data is copied (RGBA format).
55
+ */
56
+ export interface CanvasLike {
57
+ readonly width: number
58
+ readonly height: number
59
+ /** Returns raw RGBA pixel data as a Buffer */
60
+ data(): Uint8Array
61
+ }
62
+
36
63
  export type TypedArray =
37
64
  | Int8Array
38
65
  | Uint8Array
@@ -668,13 +695,14 @@ export declare class VideoEncoder {
668
695
  */
669
696
  export declare class VideoFrame {
670
697
  /**
671
- * Create a new VideoFrame from buffer data or another VideoFrame (W3C WebCodecs spec)
698
+ * Create a new VideoFrame from buffer data, another VideoFrame, or a Canvas (W3C WebCodecs spec)
672
699
  *
673
- * Two constructor forms per W3C spec:
700
+ * Constructor forms per W3C spec:
674
701
  * 1. `new VideoFrame(data, init)` - from BufferSource with VideoFrameBufferInit
675
702
  * 2. `new VideoFrame(source, init?)` - from another VideoFrame with optional VideoFrameInit
703
+ * 3. `new VideoFrame(canvas, init)` - from @napi-rs/canvas Canvas (requires timestamp in init)
676
704
  */
677
- constructor(source: VideoFrame | Uint8Array, init?: VideoFrameBufferInit | VideoFrameInit)
705
+ constructor(source: VideoFrame | Uint8Array | CanvasLike, init?: VideoFrameBufferInit | VideoFrameInit)
678
706
  /** Get the pixel format */
679
707
  get format(): VideoPixelFormat | null
680
708
  /** Get the coded width in pixels (returns 0 when closed per W3C spec) */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@napi-rs/webcodecs",
3
- "version": "0.0.0",
3
+ "version": "1.0.0",
4
4
  "description": "WebCodecs API implementation for Node.js using FFmpeg",
5
5
  "keywords": [
6
6
  "N-API",
@@ -14,6 +14,10 @@
14
14
  "type": "git",
15
15
  "url": "git+ssh://git@github.com/Brooooooklyn/webcodecs-node.git"
16
16
  },
17
+ "funding": {
18
+ "type": "github",
19
+ "url": "https://github.com/sponsors/Brooooooklyn"
20
+ },
17
21
  "license": "MIT",
18
22
  "main": "index.js",
19
23
  "exports": {
@@ -68,6 +72,7 @@
68
72
  "devDependencies": {
69
73
  "@emnapi/core": "^1.7.1",
70
74
  "@emnapi/runtime": "^1.7.1",
75
+ "@napi-rs/canvas": "^0.1.84",
71
76
  "@napi-rs/cli": "^3.5.0",
72
77
  "@napi-rs/wasm-runtime": "^1.1.0",
73
78
  "@oxc-node/core": "^0.0.35",
@@ -88,6 +93,14 @@
88
93
  "tinybench": "^6.0.0",
89
94
  "typescript": "^5.9.3"
90
95
  },
96
+ "peerDependencies": {
97
+ "@napi-rs/canvas": ">=0.1.0"
98
+ },
99
+ "peerDependenciesMeta": {
100
+ "@napi-rs/canvas": {
101
+ "optional": true
102
+ }
103
+ },
91
104
  "napi": {
92
105
  "binaryName": "webcodecs",
93
106
  "constEnum": false,
@@ -137,14 +150,14 @@
137
150
  "registry": "https://registry.npmjs.org/"
138
151
  },
139
152
  "optionalDependencies": {
140
- "@napi-rs/webcodecs-darwin-x64": "0.0.0",
141
- "@napi-rs/webcodecs-darwin-arm64": "0.0.0",
142
- "@napi-rs/webcodecs-linux-x64-gnu": "0.0.0",
143
- "@napi-rs/webcodecs-linux-x64-musl": "0.0.0",
144
- "@napi-rs/webcodecs-win32-x64-msvc": "0.0.0",
145
- "@napi-rs/webcodecs-linux-arm64-gnu": "0.0.0",
146
- "@napi-rs/webcodecs-linux-arm64-musl": "0.0.0",
147
- "@napi-rs/webcodecs-win32-arm64-msvc": "0.0.0",
148
- "@napi-rs/webcodecs-linux-arm-gnueabihf": "0.0.0"
153
+ "@napi-rs/webcodecs-darwin-x64": "1.0.0",
154
+ "@napi-rs/webcodecs-darwin-arm64": "1.0.0",
155
+ "@napi-rs/webcodecs-linux-x64-gnu": "1.0.0",
156
+ "@napi-rs/webcodecs-linux-x64-musl": "1.0.0",
157
+ "@napi-rs/webcodecs-win32-x64-msvc": "1.0.0",
158
+ "@napi-rs/webcodecs-linux-arm64-gnu": "1.0.0",
159
+ "@napi-rs/webcodecs-linux-arm64-musl": "1.0.0",
160
+ "@napi-rs/webcodecs-win32-arm64-msvc": "1.0.0",
161
+ "@napi-rs/webcodecs-linux-arm-gnueabihf": "1.0.0"
149
162
  }
150
163
  }
package/standard.d.ts CHANGED
@@ -296,6 +296,28 @@ export type VideoPixelFormat =
296
296
  | 'BGRA'
297
297
  | 'BGRX'
298
298
 
299
+ /**
300
+ * Layout information for a single plane
301
+ * @see https://w3c.github.io/webcodecs/#dictdef-planelayout
302
+ */
303
+ export interface PlaneLayout {
304
+ /** Byte offset from the start of the buffer to the start of the plane */
305
+ offset: number
306
+ /** Number of bytes per row (stride) */
307
+ stride: number
308
+ }
309
+
310
+ /**
311
+ * DOMRectInit for specifying regions
312
+ * @see https://drafts.fxtf.org/geometry/#dictdef-domrectinit
313
+ */
314
+ export interface DOMRectInit {
315
+ x?: number
316
+ y?: number
317
+ width?: number
318
+ height?: number
319
+ }
320
+
299
321
  /**
300
322
  * VideoFrame buffer init
301
323
  * @see https://w3c.github.io/webcodecs/#dictdef-videoframebufferinit
@@ -311,6 +333,10 @@ export interface VideoFrameBufferInit {
311
333
  timestamp: number
312
334
  /** Duration in microseconds */
313
335
  duration?: number
336
+ /** Layout for input planes (offset and stride per plane) */
337
+ layout?: PlaneLayout[]
338
+ /** Visible rect within the coded frame */
339
+ visibleRect?: DOMRectInit
314
340
  /** Display width */
315
341
  displayWidth?: number
316
342
  /** Display height */