webcodecs-node 0.5.2 → 0.7.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.
Files changed (144) hide show
  1. package/README.md +279 -0
  2. package/dist/backends/types.d.ts +2 -0
  3. package/dist/backends/types.d.ts.map +1 -1
  4. package/dist/backends/types.js.map +1 -1
  5. package/dist/canvas/canvas-utils.d.ts +115 -0
  6. package/dist/canvas/canvas-utils.d.ts.map +1 -0
  7. package/dist/canvas/canvas-utils.js +169 -0
  8. package/dist/canvas/canvas-utils.js.map +1 -0
  9. package/dist/canvas/frame-loop.d.ts +113 -0
  10. package/dist/canvas/frame-loop.d.ts.map +1 -0
  11. package/dist/canvas/frame-loop.js +291 -0
  12. package/dist/canvas/frame-loop.js.map +1 -0
  13. package/dist/canvas/gpu-context.d.ts +61 -0
  14. package/dist/canvas/gpu-context.d.ts.map +1 -0
  15. package/dist/canvas/gpu-context.js +134 -0
  16. package/dist/canvas/gpu-context.js.map +1 -0
  17. package/dist/canvas/index.d.ts +22 -0
  18. package/dist/canvas/index.d.ts.map +1 -0
  19. package/dist/canvas/index.js +25 -0
  20. package/dist/canvas/index.js.map +1 -0
  21. package/dist/canvas/types.d.ts +101 -0
  22. package/dist/canvas/types.d.ts.map +1 -0
  23. package/dist/canvas/types.js +7 -0
  24. package/dist/canvas/types.js.map +1 -0
  25. package/dist/codec-utils/formats.d.ts.map +1 -1
  26. package/dist/codec-utils/formats.js +31 -0
  27. package/dist/codec-utils/formats.js.map +1 -1
  28. package/dist/config/ffmpeg-quality.d.ts +14 -0
  29. package/dist/config/ffmpeg-quality.d.ts.map +1 -0
  30. package/dist/config/ffmpeg-quality.js +41 -0
  31. package/dist/config/ffmpeg-quality.js.map +1 -0
  32. package/dist/containers/Muxer.d.ts.map +1 -1
  33. package/dist/containers/Muxer.js +4 -1
  34. package/dist/containers/Muxer.js.map +1 -1
  35. package/dist/core/AudioData.d.ts +2 -1
  36. package/dist/core/AudioData.d.ts.map +1 -1
  37. package/dist/core/AudioData.js.map +1 -1
  38. package/dist/core/VideoFrame.d.ts +2 -2
  39. package/dist/core/VideoFrame.d.ts.map +1 -1
  40. package/dist/core/VideoFrame.js +49 -47
  41. package/dist/core/VideoFrame.js.map +1 -1
  42. package/dist/decoders/AudioDecoder.d.ts +1 -0
  43. package/dist/decoders/AudioDecoder.d.ts.map +1 -1
  44. package/dist/decoders/AudioDecoder.js +39 -17
  45. package/dist/decoders/AudioDecoder.js.map +1 -1
  46. package/dist/decoders/ImageDecoder.d.ts +1 -0
  47. package/dist/decoders/ImageDecoder.d.ts.map +1 -1
  48. package/dist/decoders/ImageDecoder.js +40 -3
  49. package/dist/decoders/ImageDecoder.js.map +1 -1
  50. package/dist/decoders/VideoDecoder.d.ts +12 -0
  51. package/dist/decoders/VideoDecoder.d.ts.map +1 -1
  52. package/dist/decoders/VideoDecoder.js +50 -8
  53. package/dist/decoders/VideoDecoder.js.map +1 -1
  54. package/dist/demos/demo-1080p-transcode.js +8 -2
  55. package/dist/demos/demo-1080p-transcode.js.map +1 -1
  56. package/dist/demos/demo-audio-visualizer.d.ts +11 -0
  57. package/dist/demos/demo-audio-visualizer.d.ts.map +1 -0
  58. package/dist/demos/demo-audio-visualizer.js +281 -0
  59. package/dist/demos/demo-audio-visualizer.js.map +1 -0
  60. package/dist/demos/demo-dvd-logo.d.ts +8 -0
  61. package/dist/demos/demo-dvd-logo.d.ts.map +1 -0
  62. package/dist/demos/demo-dvd-logo.js +196 -0
  63. package/dist/demos/demo-dvd-logo.js.map +1 -0
  64. package/dist/demos/demo-four-corners.js +9 -0
  65. package/dist/demos/demo-four-corners.js.map +1 -1
  66. package/dist/demos/demo-streaming.js +6 -0
  67. package/dist/demos/demo-streaming.js.map +1 -1
  68. package/dist/demos/demo-webcodecs.js +1 -0
  69. package/dist/demos/demo-webcodecs.js.map +1 -1
  70. package/dist/encoders/AudioEncoder.d.ts +3 -0
  71. package/dist/encoders/AudioEncoder.d.ts.map +1 -1
  72. package/dist/encoders/AudioEncoder.js +70 -28
  73. package/dist/encoders/AudioEncoder.js.map +1 -1
  74. package/dist/encoders/ImageEncoder.d.ts +80 -0
  75. package/dist/encoders/ImageEncoder.d.ts.map +1 -0
  76. package/dist/encoders/ImageEncoder.js +156 -0
  77. package/dist/encoders/ImageEncoder.js.map +1 -0
  78. package/dist/encoders/VideoEncoder.d.ts +11 -0
  79. package/dist/encoders/VideoEncoder.d.ts.map +1 -1
  80. package/dist/encoders/VideoEncoder.js +46 -4
  81. package/dist/encoders/VideoEncoder.js.map +1 -1
  82. package/dist/encoders/index.d.ts +1 -0
  83. package/dist/encoders/index.d.ts.map +1 -1
  84. package/dist/encoders/index.js +1 -0
  85. package/dist/encoders/index.js.map +1 -1
  86. package/dist/formats/color-space.d.ts +88 -0
  87. package/dist/formats/color-space.d.ts.map +1 -1
  88. package/dist/formats/color-space.js +55 -1
  89. package/dist/formats/color-space.js.map +1 -1
  90. package/dist/formats/pixel-formats.d.ts +17 -1
  91. package/dist/formats/pixel-formats.d.ts.map +1 -1
  92. package/dist/formats/pixel-formats.js +74 -4
  93. package/dist/formats/pixel-formats.js.map +1 -1
  94. package/dist/hardware/detection.d.ts.map +1 -1
  95. package/dist/hardware/detection.js +5 -2
  96. package/dist/hardware/detection.js.map +1 -1
  97. package/dist/hardware/encoder-args.d.ts.map +1 -1
  98. package/dist/hardware/encoder-args.js +6 -6
  99. package/dist/hardware/encoder-args.js.map +1 -1
  100. package/dist/index.d.ts +11 -3
  101. package/dist/index.d.ts.map +1 -1
  102. package/dist/index.js +8 -2
  103. package/dist/index.js.map +1 -1
  104. package/dist/node-av/NodeAvAudioDecoder.d.ts.map +1 -1
  105. package/dist/node-av/NodeAvAudioDecoder.js +3 -0
  106. package/dist/node-av/NodeAvAudioDecoder.js.map +1 -1
  107. package/dist/node-av/NodeAvAudioEncoder.d.ts +5 -0
  108. package/dist/node-av/NodeAvAudioEncoder.d.ts.map +1 -1
  109. package/dist/node-av/NodeAvAudioEncoder.js +102 -9
  110. package/dist/node-av/NodeAvAudioEncoder.js.map +1 -1
  111. package/dist/node-av/NodeAvVideoEncoder.d.ts +2 -0
  112. package/dist/node-av/NodeAvVideoEncoder.d.ts.map +1 -1
  113. package/dist/node-av/NodeAvVideoEncoder.js +44 -6
  114. package/dist/node-av/NodeAvVideoEncoder.js.map +1 -1
  115. package/dist/node-av/WebPImageDecoder.d.ts +54 -0
  116. package/dist/node-av/WebPImageDecoder.d.ts.map +1 -0
  117. package/dist/node-av/WebPImageDecoder.js +176 -0
  118. package/dist/node-av/WebPImageDecoder.js.map +1 -0
  119. package/dist/polyfills/OffscreenCanvas.d.ts +141 -7
  120. package/dist/polyfills/OffscreenCanvas.d.ts.map +1 -1
  121. package/dist/polyfills/OffscreenCanvas.js +217 -17
  122. package/dist/polyfills/OffscreenCanvas.js.map +1 -1
  123. package/dist/types/index.d.ts +1 -0
  124. package/dist/types/index.d.ts.map +1 -1
  125. package/dist/types/index.js +2 -0
  126. package/dist/types/index.js.map +1 -1
  127. package/dist/types/native-frame.d.ts +74 -0
  128. package/dist/types/native-frame.d.ts.map +1 -0
  129. package/dist/types/native-frame.js +32 -0
  130. package/dist/types/native-frame.js.map +1 -0
  131. package/dist/utils/index.d.ts +1 -1
  132. package/dist/utils/index.d.ts.map +1 -1
  133. package/dist/utils/index.js +1 -1
  134. package/dist/utils/index.js.map +1 -1
  135. package/dist/utils/type-guards.d.ts +29 -1
  136. package/dist/utils/type-guards.d.ts.map +1 -1
  137. package/dist/utils/type-guards.js +47 -2
  138. package/dist/utils/type-guards.js.map +1 -1
  139. package/docs/api.md +155 -0
  140. package/docs/configuration.md +27 -0
  141. package/examples/README.md +28 -1
  142. package/examples/canvas-encoding.ts +222 -0
  143. package/examples/offscreen-canvas.ts +230 -0
  144. package/package.json +9 -3
@@ -0,0 +1,230 @@
1
+ /**
2
+ * OffscreenCanvas Example
3
+ *
4
+ * Demonstrates using the OffscreenCanvas polyfill for browser-compatible
5
+ * canvas code in Node.js. This makes porting browser code trivial.
6
+ *
7
+ * Features shown:
8
+ * - OffscreenCanvasPolyfill (browser API compatible)
9
+ * - ImageDataPolyfill with Uint8ClampedArray
10
+ * - createPixelBuffer utilities
11
+ * - convertToBlob for image export
12
+ * - VideoFrame integration
13
+ *
14
+ * Run: npx tsx examples/offscreen-canvas.ts
15
+ */
16
+
17
+ import {
18
+ VideoEncoder,
19
+ VideoFrame,
20
+ EncodedVideoChunk,
21
+ } from '../src/index.js';
22
+
23
+ import {
24
+ OffscreenCanvasPolyfill,
25
+ ImageDataPolyfill,
26
+ installOffscreenCanvasPolyfill,
27
+ } from '../src/polyfills/OffscreenCanvas.js';
28
+
29
+ import {
30
+ createPixelBuffer,
31
+ createPixelBufferWithColor,
32
+ ensureEvenDimensions,
33
+ validateEvenDimensions,
34
+ } from '../src/canvas/index.js';
35
+
36
+ import { writeFileSync } from 'fs';
37
+
38
+ async function main() {
39
+ console.log('=== OffscreenCanvas Polyfill Demo ===\n');
40
+
41
+ // ============================================
42
+ // 1. Install Polyfill Globally (Optional)
43
+ // ============================================
44
+ // This makes OffscreenCanvas available globally like in browsers
45
+ installOffscreenCanvasPolyfill();
46
+ console.log('Polyfill installed globally');
47
+ console.log(` globalThis.OffscreenCanvas: ${typeof (globalThis as any).OffscreenCanvas}`);
48
+ console.log(` globalThis.ImageData: ${typeof (globalThis as any).ImageData}`);
49
+ console.log(` globalThis.VideoFrame: ${typeof (globalThis as any).VideoFrame}`);
50
+
51
+ // ============================================
52
+ // 2. Create OffscreenCanvas (Browser-Style)
53
+ // ============================================
54
+ console.log('\n=== Creating OffscreenCanvas ===\n');
55
+
56
+ const { width, height } = ensureEvenDimensions(640, 480);
57
+ const canvas = new OffscreenCanvasPolyfill(width, height);
58
+
59
+ console.log(`Canvas created: ${canvas.width}x${canvas.height}`);
60
+ console.log(`GPU enabled: ${canvas.gpu}`);
61
+
62
+ // Get 2D context (just like in browser)
63
+ const ctx = canvas.getContext('2d');
64
+ if (!ctx) {
65
+ throw new Error('Failed to get 2D context');
66
+ }
67
+
68
+ // ============================================
69
+ // 3. Draw on Canvas
70
+ // ============================================
71
+ console.log('\n=== Drawing ===\n');
72
+
73
+ // Background
74
+ ctx.fillStyle = '#1a1a2e';
75
+ ctx.fillRect(0, 0, width, height);
76
+
77
+ // Gradient rectangle
78
+ const gradient = ctx.createLinearGradient(50, 50, 250, 250);
79
+ gradient.addColorStop(0, '#e94560');
80
+ gradient.addColorStop(1, '#0f3460');
81
+ ctx.fillStyle = gradient;
82
+ ctx.fillRect(50, 50, 200, 200);
83
+
84
+ // Circle
85
+ ctx.beginPath();
86
+ ctx.arc(450, 200, 100, 0, Math.PI * 2);
87
+ ctx.fillStyle = '#16213e';
88
+ ctx.fill();
89
+ ctx.strokeStyle = '#e94560';
90
+ ctx.lineWidth = 4;
91
+ ctx.stroke();
92
+
93
+ // Text
94
+ ctx.fillStyle = 'white';
95
+ ctx.font = 'bold 32px sans-serif';
96
+ ctx.textAlign = 'center';
97
+ ctx.fillText('OffscreenCanvas', width / 2, height - 80);
98
+
99
+ ctx.font = '20px sans-serif';
100
+ ctx.fillStyle = '#888';
101
+ ctx.fillText('Node.js + skia-canvas', width / 2, height - 50);
102
+
103
+ console.log('Drawing complete');
104
+
105
+ // ============================================
106
+ // 4. Export to Blob (PNG, JPEG, WebP)
107
+ // ============================================
108
+ console.log('\n=== Export to Blob ===\n');
109
+
110
+ // PNG export
111
+ const pngBlob = await canvas.convertToBlob({ type: 'image/png' });
112
+ console.log(`PNG: ${pngBlob.size} bytes, type: ${pngBlob.type}`);
113
+
114
+ // JPEG export with quality
115
+ const jpegBlob = await canvas.convertToBlob({ type: 'image/jpeg', quality: 0.8 });
116
+ console.log(`JPEG: ${jpegBlob.size} bytes, type: ${jpegBlob.type}`);
117
+
118
+ // WebP export
119
+ const webpBlob = await canvas.convertToBlob({ type: 'image/webp', quality: 0.85 });
120
+ console.log(`WebP: ${webpBlob.size} bytes, type: ${webpBlob.type}`);
121
+
122
+ // ============================================
123
+ // 5. ImageData Operations
124
+ // ============================================
125
+ console.log('\n=== ImageData Operations ===\n');
126
+
127
+ // Create empty ImageData
128
+ const imageData = new ImageDataPolyfill(100, 100);
129
+ console.log(`Empty ImageData: ${imageData.width}x${imageData.height}`);
130
+ console.log(`Data type: ${imageData.data.constructor.name}`);
131
+ console.log(`Data length: ${imageData.data.length} bytes`);
132
+ console.log(`Color type: ${imageData.colorType} (for skia-canvas)`);
133
+
134
+ // Fill with gradient using direct pixel manipulation
135
+ for (let y = 0; y < imageData.height; y++) {
136
+ for (let x = 0; x < imageData.width; x++) {
137
+ const idx = (y * imageData.width + x) * 4;
138
+ imageData.data[idx] = (x * 255) / imageData.width; // R
139
+ imageData.data[idx + 1] = (y * 255) / imageData.height; // G
140
+ imageData.data[idx + 2] = 128; // B
141
+ imageData.data[idx + 3] = 255; // A
142
+ }
143
+ }
144
+
145
+ // Draw ImageData to canvas
146
+ ctx.putImageData(imageData as any, 270, 280);
147
+ console.log('ImageData drawn to canvas');
148
+
149
+ // ============================================
150
+ // 6. Pixel Buffer Utilities
151
+ // ============================================
152
+ console.log('\n=== Pixel Buffer Utilities ===\n');
153
+
154
+ // Create empty buffer
155
+ const emptyBuffer = createPixelBuffer(64, 64);
156
+ console.log(`createPixelBuffer(64, 64): ${emptyBuffer.length} bytes`);
157
+ console.log(`Type: ${emptyBuffer.constructor.name}`);
158
+
159
+ // Create solid color buffer
160
+ const redBuffer = createPixelBufferWithColor(64, 64, 255, 0, 0);
161
+ console.log(`createPixelBufferWithColor (red): first pixel = [${redBuffer[0]}, ${redBuffer[1]}, ${redBuffer[2]}, ${redBuffer[3]}]`);
162
+
163
+ // Demonstrate Uint8ClampedArray auto-clamping
164
+ const testBuffer = createPixelBuffer(1, 1);
165
+ testBuffer[0] = 300; // Will be clamped to 255
166
+ testBuffer[1] = -50; // Will be clamped to 0
167
+ console.log(`Auto-clamping: 300 -> ${testBuffer[0]}, -50 -> ${testBuffer[1]}`);
168
+
169
+ // ============================================
170
+ // 7. Dimension Validation
171
+ // ============================================
172
+ console.log('\n=== Dimension Validation ===\n');
173
+
174
+ // ensureEvenDimensions rounds up
175
+ const dims = ensureEvenDimensions(1279, 719);
176
+ console.log(`ensureEvenDimensions(1279, 719) = ${dims.width}x${dims.height}`);
177
+
178
+ // validateEvenDimensions throws for odd dimensions
179
+ try {
180
+ validateEvenDimensions(1279, 720);
181
+ } catch (err: any) {
182
+ console.log(`validateEvenDimensions(1279, 720) threw: "${err.message.split('.')[0]}..."`);
183
+ }
184
+
185
+ // ============================================
186
+ // 8. VideoFrame Integration
187
+ // ============================================
188
+ console.log('\n=== VideoFrame Integration ===\n');
189
+
190
+ // Create VideoFrame directly from OffscreenCanvas
191
+ const frame = new VideoFrame(canvas as any, {
192
+ timestamp: 0,
193
+ });
194
+
195
+ console.log(`VideoFrame from OffscreenCanvas:`);
196
+ console.log(` Size: ${frame.displayWidth}x${frame.displayHeight}`);
197
+ console.log(` Format: ${frame.format}`);
198
+ console.log(` Timestamp: ${frame.timestamp}`);
199
+
200
+ // Use with encoder
201
+ const chunks: EncodedVideoChunk[] = [];
202
+ const encoder = new VideoEncoder({
203
+ output: (chunk) => chunks.push(chunk),
204
+ error: console.error,
205
+ });
206
+
207
+ encoder.configure({
208
+ codec: 'avc1.42001f',
209
+ width,
210
+ height,
211
+ bitrate: 2_000_000,
212
+ framerate: 30,
213
+ });
214
+
215
+ encoder.encode(frame, { keyFrame: true });
216
+ frame.close();
217
+
218
+ await encoder.flush();
219
+ encoder.close();
220
+
221
+ console.log(`Encoded to ${chunks.length} chunk(s), ${chunks[0].byteLength} bytes`);
222
+
223
+ // ============================================
224
+ // 9. Save Sample Output
225
+ // ============================================
226
+ console.log('\n=== Done ===\n');
227
+ console.log('OffscreenCanvas polyfill provides full browser API compatibility!');
228
+ }
229
+
230
+ main().catch(console.error);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "webcodecs-node",
3
- "version": "0.5.2",
3
+ "version": "0.7.0",
4
4
  "description": "WebCodecs API implementation for Node.js using node-av",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -38,7 +38,12 @@
38
38
  "demo:samples": "node dist/demos/demo-samples.js",
39
39
  "demo:fourcorners": "node dist/demos/demo-four-corners.js",
40
40
  "demo:containers": "node dist/demos/demo-containers.js",
41
- "demo:1080p": "node dist/demos/demo-1080p-transcode.js"
41
+ "demo:1080p": "node dist/demos/demo-1080p-transcode.js",
42
+ "demo:dvd": "node dist/demos/demo-dvd-logo.js",
43
+ "demo:visualizer": "node dist/demos/demo-audio-visualizer.js",
44
+ "bench": "node scripts/encoding-benchmark.mjs",
45
+ "bench:quick": "node scripts/encoding-benchmark.mjs --frames 30 --resolution 360p",
46
+ "bench:full": "node scripts/encoding-benchmark.mjs --frames 300 --resolution 1080p"
42
47
  },
43
48
  "keywords": [
44
49
  "webcodecs",
@@ -73,7 +78,8 @@
73
78
  "homepage": "",
74
79
  "dependencies": {
75
80
  "node-av": "^5.0.2",
76
- "sharp": "^0.32.6"
81
+ "node-webpmux": "^3.2.1",
82
+ "skia-canvas": "^2.0.2"
77
83
  },
78
84
  "devDependencies": {
79
85
  "@types/jest": "^30.0.0",