webcodecs-node 0.5.1 → 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.
- package/README.md +328 -169
- package/dist/backends/types.d.ts +2 -0
- package/dist/backends/types.d.ts.map +1 -1
- package/dist/backends/types.js.map +1 -1
- package/dist/canvas/canvas-utils.d.ts +115 -0
- package/dist/canvas/canvas-utils.d.ts.map +1 -0
- package/dist/canvas/canvas-utils.js +169 -0
- package/dist/canvas/canvas-utils.js.map +1 -0
- package/dist/canvas/frame-loop.d.ts +113 -0
- package/dist/canvas/frame-loop.d.ts.map +1 -0
- package/dist/canvas/frame-loop.js +291 -0
- package/dist/canvas/frame-loop.js.map +1 -0
- package/dist/canvas/gpu-context.d.ts +61 -0
- package/dist/canvas/gpu-context.d.ts.map +1 -0
- package/dist/canvas/gpu-context.js +134 -0
- package/dist/canvas/gpu-context.js.map +1 -0
- package/dist/canvas/index.d.ts +22 -0
- package/dist/canvas/index.d.ts.map +1 -0
- package/dist/canvas/index.js +25 -0
- package/dist/canvas/index.js.map +1 -0
- package/dist/canvas/types.d.ts +101 -0
- package/dist/canvas/types.d.ts.map +1 -0
- package/dist/canvas/types.js +7 -0
- package/dist/canvas/types.js.map +1 -0
- package/dist/codec-utils/formats.d.ts.map +1 -1
- package/dist/codec-utils/formats.js +31 -0
- package/dist/codec-utils/formats.js.map +1 -1
- package/dist/config/ffmpeg-quality.d.ts +14 -0
- package/dist/config/ffmpeg-quality.d.ts.map +1 -0
- package/dist/config/ffmpeg-quality.js +41 -0
- package/dist/config/ffmpeg-quality.js.map +1 -0
- package/dist/containers/Muxer.d.ts.map +1 -1
- package/dist/containers/Muxer.js +4 -1
- package/dist/containers/Muxer.js.map +1 -1
- package/dist/core/AudioData.d.ts +2 -1
- package/dist/core/AudioData.d.ts.map +1 -1
- package/dist/core/AudioData.js.map +1 -1
- package/dist/core/VideoFrame.d.ts +2 -2
- package/dist/core/VideoFrame.d.ts.map +1 -1
- package/dist/core/VideoFrame.js +49 -47
- package/dist/core/VideoFrame.js.map +1 -1
- package/dist/decoders/AudioDecoder.d.ts +1 -0
- package/dist/decoders/AudioDecoder.d.ts.map +1 -1
- package/dist/decoders/AudioDecoder.js +39 -17
- package/dist/decoders/AudioDecoder.js.map +1 -1
- package/dist/decoders/ImageDecoder.d.ts +1 -0
- package/dist/decoders/ImageDecoder.d.ts.map +1 -1
- package/dist/decoders/ImageDecoder.js +40 -3
- package/dist/decoders/ImageDecoder.js.map +1 -1
- package/dist/decoders/VideoDecoder.d.ts +12 -0
- package/dist/decoders/VideoDecoder.d.ts.map +1 -1
- package/dist/decoders/VideoDecoder.js +50 -8
- package/dist/decoders/VideoDecoder.js.map +1 -1
- package/dist/demos/demo-1080p-transcode.js +8 -2
- package/dist/demos/demo-1080p-transcode.js.map +1 -1
- package/dist/demos/demo-audio-visualizer.d.ts +11 -0
- package/dist/demos/demo-audio-visualizer.d.ts.map +1 -0
- package/dist/demos/demo-audio-visualizer.js +281 -0
- package/dist/demos/demo-audio-visualizer.js.map +1 -0
- package/dist/demos/demo-dvd-logo.d.ts +8 -0
- package/dist/demos/demo-dvd-logo.d.ts.map +1 -0
- package/dist/demos/demo-dvd-logo.js +196 -0
- package/dist/demos/demo-dvd-logo.js.map +1 -0
- package/dist/demos/demo-four-corners.js +9 -0
- package/dist/demos/demo-four-corners.js.map +1 -1
- package/dist/demos/demo-streaming.js +6 -0
- package/dist/demos/demo-streaming.js.map +1 -1
- package/dist/demos/demo-webcodecs.js +1 -0
- package/dist/demos/demo-webcodecs.js.map +1 -1
- package/dist/encoders/AudioEncoder.d.ts +3 -0
- package/dist/encoders/AudioEncoder.d.ts.map +1 -1
- package/dist/encoders/AudioEncoder.js +70 -28
- package/dist/encoders/AudioEncoder.js.map +1 -1
- package/dist/encoders/ImageEncoder.d.ts +80 -0
- package/dist/encoders/ImageEncoder.d.ts.map +1 -0
- package/dist/encoders/ImageEncoder.js +156 -0
- package/dist/encoders/ImageEncoder.js.map +1 -0
- package/dist/encoders/VideoEncoder.d.ts +11 -0
- package/dist/encoders/VideoEncoder.d.ts.map +1 -1
- package/dist/encoders/VideoEncoder.js +46 -4
- package/dist/encoders/VideoEncoder.js.map +1 -1
- package/dist/encoders/index.d.ts +1 -0
- package/dist/encoders/index.d.ts.map +1 -1
- package/dist/encoders/index.js +1 -0
- package/dist/encoders/index.js.map +1 -1
- package/dist/formats/color-space.d.ts +88 -0
- package/dist/formats/color-space.d.ts.map +1 -1
- package/dist/formats/color-space.js +55 -1
- package/dist/formats/color-space.js.map +1 -1
- package/dist/formats/pixel-formats.d.ts +17 -1
- package/dist/formats/pixel-formats.d.ts.map +1 -1
- package/dist/formats/pixel-formats.js +74 -4
- package/dist/formats/pixel-formats.js.map +1 -1
- package/dist/hardware/detection.d.ts.map +1 -1
- package/dist/hardware/detection.js +5 -2
- package/dist/hardware/detection.js.map +1 -1
- package/dist/hardware/encoder-args.d.ts.map +1 -1
- package/dist/hardware/encoder-args.js +6 -6
- package/dist/hardware/encoder-args.js.map +1 -1
- package/dist/index.d.ts +11 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -2
- package/dist/index.js.map +1 -1
- package/dist/node-av/NodeAvAudioDecoder.d.ts.map +1 -1
- package/dist/node-av/NodeAvAudioDecoder.js +3 -0
- package/dist/node-av/NodeAvAudioDecoder.js.map +1 -1
- package/dist/node-av/NodeAvAudioEncoder.d.ts +5 -0
- package/dist/node-av/NodeAvAudioEncoder.d.ts.map +1 -1
- package/dist/node-av/NodeAvAudioEncoder.js +102 -9
- package/dist/node-av/NodeAvAudioEncoder.js.map +1 -1
- package/dist/node-av/NodeAvVideoEncoder.d.ts +2 -0
- package/dist/node-av/NodeAvVideoEncoder.d.ts.map +1 -1
- package/dist/node-av/NodeAvVideoEncoder.js +44 -6
- package/dist/node-av/NodeAvVideoEncoder.js.map +1 -1
- package/dist/node-av/WebPImageDecoder.d.ts +54 -0
- package/dist/node-av/WebPImageDecoder.d.ts.map +1 -0
- package/dist/node-av/WebPImageDecoder.js +176 -0
- package/dist/node-av/WebPImageDecoder.js.map +1 -0
- package/dist/polyfills/OffscreenCanvas.d.ts +141 -7
- package/dist/polyfills/OffscreenCanvas.d.ts.map +1 -1
- package/dist/polyfills/OffscreenCanvas.js +217 -17
- package/dist/polyfills/OffscreenCanvas.js.map +1 -1
- package/dist/types/index.d.ts +1 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js +2 -0
- package/dist/types/index.js.map +1 -1
- package/dist/types/native-frame.d.ts +74 -0
- package/dist/types/native-frame.d.ts.map +1 -0
- package/dist/types/native-frame.js +32 -0
- package/dist/types/native-frame.js.map +1 -0
- package/dist/utils/index.d.ts +1 -1
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +1 -1
- package/dist/utils/index.js.map +1 -1
- package/dist/utils/type-guards.d.ts +29 -1
- package/dist/utils/type-guards.d.ts.map +1 -1
- package/dist/utils/type-guards.js +47 -2
- package/dist/utils/type-guards.js.map +1 -1
- package/docs/api.md +156 -1
- package/docs/configuration.md +30 -14
- package/examples/README.md +28 -1
- package/examples/canvas-encoding.ts +222 -0
- package/examples/offscreen-canvas.ts +230 -0
- package/package.json +10 -4
|
@@ -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,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "webcodecs-node",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "WebCodecs API implementation for Node.js using
|
|
3
|
+
"version": "0.7.0",
|
|
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",
|
|
@@ -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
|
-
"
|
|
81
|
+
"node-webpmux": "^3.2.1",
|
|
82
|
+
"skia-canvas": "^2.0.2"
|
|
77
83
|
},
|
|
78
84
|
"devDependencies": {
|
|
79
85
|
"@types/jest": "^30.0.0",
|