webcodecs-utils 0.1.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 +201 -0
- package/package.json +52 -0
package/README.md
ADDED
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
# webcodecs-utils
|
|
2
|
+
|
|
3
|
+
Utility functions for working with the WebCodecs API, extracted from [WebCodecs Fundamentals](https://webcodecsfundamentals.org).
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install webcodecs-utils
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Quick Start
|
|
12
|
+
|
|
13
|
+
```typescript
|
|
14
|
+
import { getBitrate, GPUDrawImage, extractChannels, MP4Demuxer } from 'webcodecs-utils';
|
|
15
|
+
|
|
16
|
+
// Calculate optimal bitrate
|
|
17
|
+
const bitrate = getBitrate(1920, 1080, 30, 'good');
|
|
18
|
+
|
|
19
|
+
// Zero-copy video rendering with WebGPU
|
|
20
|
+
const renderer = new GPUDrawImage(canvas);
|
|
21
|
+
await renderer.init();
|
|
22
|
+
renderer.drawImage(videoFrame, 0, 0);
|
|
23
|
+
|
|
24
|
+
// Extract audio channels
|
|
25
|
+
const decoder = new AudioDecoder({
|
|
26
|
+
output: (audioData) => {
|
|
27
|
+
const channels = extractChannels(audioData);
|
|
28
|
+
const leftChannel = channels[0];
|
|
29
|
+
const rightChannel = channels[1];
|
|
30
|
+
},
|
|
31
|
+
error: (e) => console.error(e)
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
// Parse MP4 files
|
|
35
|
+
const demuxer = new MP4Demuxer(file);
|
|
36
|
+
await demuxer.load();
|
|
37
|
+
const videoChunks = await demuxer.extractSegment('video', 0, 10);
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Utilities
|
|
41
|
+
|
|
42
|
+
### Video
|
|
43
|
+
|
|
44
|
+
#### **getBitrate**
|
|
45
|
+
Calculate optimal bitrate for video encoding based on resolution, framerate, and quality.
|
|
46
|
+
|
|
47
|
+
- 📄 [Source](./src/video/get-bitrate.ts)
|
|
48
|
+
- 🎮 [Demo](./demos/bitrate-demo.html)
|
|
49
|
+
|
|
50
|
+
```typescript
|
|
51
|
+
function getBitrate(
|
|
52
|
+
width: number,
|
|
53
|
+
height: number,
|
|
54
|
+
fps: number,
|
|
55
|
+
quality?: 'low' | 'good' | 'high' | 'very-high'
|
|
56
|
+
): number
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
#### **getCodecString**
|
|
60
|
+
Generate proper codec strings (avc1, vp09, etc.) with correct profile/level for VideoEncoder configuration.
|
|
61
|
+
|
|
62
|
+
- 📄 [Source](./src/video/get-codec-string.ts)
|
|
63
|
+
- 🎮 [Demo](./demos/codec-string-demo.html)
|
|
64
|
+
|
|
65
|
+
```typescript
|
|
66
|
+
function getCodecString(
|
|
67
|
+
codec: 'avc' | 'hevc' | 'vp8' | 'vp9' | 'av1',
|
|
68
|
+
width: number,
|
|
69
|
+
height: number,
|
|
70
|
+
bitrate: number
|
|
71
|
+
): string
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
#### **GPUDrawImage**
|
|
75
|
+
Zero-copy video frame rendering using WebGPU importExternalTexture, with fallback to ImageBitmapRenderer.
|
|
76
|
+
|
|
77
|
+
- 📄 [Source](./src/video/gpu-draw-image.ts)
|
|
78
|
+
- 🎮 [Demo](./demos/gpu-renderer-demo.html)
|
|
79
|
+
|
|
80
|
+
```typescript
|
|
81
|
+
class GPUDrawImage {
|
|
82
|
+
constructor(canvas: HTMLCanvasElement | OffscreenCanvas, options?: {
|
|
83
|
+
filterMode?: 'linear' | 'bicubic'
|
|
84
|
+
})
|
|
85
|
+
|
|
86
|
+
async init(): Promise<void>
|
|
87
|
+
drawImage(videoFrame: VideoFrame, dx?: number, dy?: number): void
|
|
88
|
+
getMode(): 'webgpu' | 'bitmap' | null
|
|
89
|
+
destroy(): void
|
|
90
|
+
}
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
### Audio
|
|
94
|
+
|
|
95
|
+
#### **extractChannels**
|
|
96
|
+
Extract and de-interleave audio channels from AudioData into Float32Array[].
|
|
97
|
+
|
|
98
|
+
Handles both planar (f32-planar) and interleaved (f32) audio formats automatically. Returns an array of Float32Array buffers, one per channel (e.g., [left, right] for stereo).
|
|
99
|
+
|
|
100
|
+
- 📄 [Source](./src/audio/extract-channels.ts)
|
|
101
|
+
- 🎮 [Demo](./demos/audio-channels-demo.html)
|
|
102
|
+
|
|
103
|
+
```typescript
|
|
104
|
+
function extractChannels(audioData: AudioData): Float32Array[]
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
**Example:**
|
|
108
|
+
```typescript
|
|
109
|
+
const channels = extractChannels(audioData);
|
|
110
|
+
const leftChannel = channels[0];
|
|
111
|
+
const rightChannel = channels[1]; // if stereo
|
|
112
|
+
|
|
113
|
+
// Process audio samples
|
|
114
|
+
for (let i = 0; i < leftChannel.length; i++) {
|
|
115
|
+
leftChannel[i] *= 0.5; // Reduce volume by 50%
|
|
116
|
+
}
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
#### **MP3Encoder**
|
|
120
|
+
Encode AudioData to MP3 format using LameJS.
|
|
121
|
+
|
|
122
|
+
- 📄 [Source](./src/audio/mp3-encoder.ts)
|
|
123
|
+
- 🎮 [Demo](./demos/mp3-encoder-demo.html)
|
|
124
|
+
|
|
125
|
+
```typescript
|
|
126
|
+
class MP3Encoder {
|
|
127
|
+
constructor(config: {
|
|
128
|
+
sampleRate: number;
|
|
129
|
+
bitRate: number;
|
|
130
|
+
channels: number;
|
|
131
|
+
})
|
|
132
|
+
|
|
133
|
+
processBatch(audioData: AudioData): Uint8Array
|
|
134
|
+
finish(): Blob
|
|
135
|
+
}
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
### Demux
|
|
139
|
+
|
|
140
|
+
#### **MP4Demuxer**
|
|
141
|
+
Parse MP4 files and extract EncodedVideoChunk/EncodedAudioChunk objects using MP4Box.
|
|
142
|
+
|
|
143
|
+
- 📄 [Source](./src/demux/mp4-demuxer.ts)
|
|
144
|
+
- 🎮 [Demo](./demos/mp4-demuxer-demo.html)
|
|
145
|
+
|
|
146
|
+
```typescript
|
|
147
|
+
class MP4Demuxer {
|
|
148
|
+
constructor(file: File)
|
|
149
|
+
|
|
150
|
+
async load(onProgress?: (progress: number) => void): Promise<void>
|
|
151
|
+
getTracks(): TrackData
|
|
152
|
+
getVideoTrack(): VideoTrackData | undefined
|
|
153
|
+
getAudioTrack(): AudioTrackData | undefined
|
|
154
|
+
async extractSegment(
|
|
155
|
+
trackType: 'audio' | 'video',
|
|
156
|
+
startTime: number,
|
|
157
|
+
endTime: number
|
|
158
|
+
): Promise<EncodedVideoChunk[] | EncodedAudioChunk[]>
|
|
159
|
+
}
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
**Example:**
|
|
163
|
+
```typescript
|
|
164
|
+
const demuxer = new MP4Demuxer(file);
|
|
165
|
+
await demuxer.load((progress) => console.log(`Loading: ${progress * 100}%`));
|
|
166
|
+
|
|
167
|
+
const videoTrack = demuxer.getVideoTrack();
|
|
168
|
+
console.log(`Video: ${videoTrack.codec}, ${videoTrack.codedWidth}x${videoTrack.codedHeight}`);
|
|
169
|
+
|
|
170
|
+
const videoChunks = await demuxer.extractSegment('video', 0, 10);
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
## Browser Support
|
|
174
|
+
|
|
175
|
+
These utilities require:
|
|
176
|
+
- **WebCodecs API** - Chrome 94+, Edge 94+, Safari 17.4+ (some features)
|
|
177
|
+
- **WebGPU** (optional) - Chrome 113+, Edge 113+, Safari 18+ (for GPUDrawImage)
|
|
178
|
+
|
|
179
|
+
All utilities include compatibility checks and graceful degradation where applicable.
|
|
180
|
+
|
|
181
|
+
## Development
|
|
182
|
+
|
|
183
|
+
```bash
|
|
184
|
+
# Install dependencies
|
|
185
|
+
npm install
|
|
186
|
+
|
|
187
|
+
# Start demo server (localhost:5173)
|
|
188
|
+
npm run dev
|
|
189
|
+
|
|
190
|
+
# Build library
|
|
191
|
+
npm run build
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
## License
|
|
195
|
+
|
|
196
|
+
MIT
|
|
197
|
+
|
|
198
|
+
## Related
|
|
199
|
+
|
|
200
|
+
- [WebCodecs Fundamentals](https://webcodecsfundamentals.org) - Comprehensive WebCodecs guide
|
|
201
|
+
- [MediaBunny](https://mediabunny.dev/) - Full-featured WebCodecs library
|
package/package.json
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "webcodecs-utils",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Utility functions for working with WebCodecs API",
|
|
5
|
+
"homepage": "https://webcodecsfundamentals.org",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "https://github.com/sb2702/webcodecs-utils.git"
|
|
9
|
+
},
|
|
10
|
+
"type": "module",
|
|
11
|
+
"main": "./dist/index.cjs",
|
|
12
|
+
"module": "./dist/index.js",
|
|
13
|
+
"types": "./dist/index.d.ts",
|
|
14
|
+
"exports": {
|
|
15
|
+
".": {
|
|
16
|
+
"import": "./dist/index.js",
|
|
17
|
+
"require": "./dist/index.cjs",
|
|
18
|
+
"types": "./dist/index.d.ts"
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
"files": [
|
|
22
|
+
"dist"
|
|
23
|
+
],
|
|
24
|
+
"scripts": {
|
|
25
|
+
"dev": "vite",
|
|
26
|
+
"build": "vite build && tsc --emitDeclarationOnly",
|
|
27
|
+
"preview": "vite preview"
|
|
28
|
+
},
|
|
29
|
+
"keywords": [
|
|
30
|
+
"webcodecs",
|
|
31
|
+
"video",
|
|
32
|
+
"audio",
|
|
33
|
+
"encoding",
|
|
34
|
+
"decoding",
|
|
35
|
+
"mp4",
|
|
36
|
+
"mp3"
|
|
37
|
+
],
|
|
38
|
+
"author": "",
|
|
39
|
+
"license": "MIT",
|
|
40
|
+
"devDependencies": {
|
|
41
|
+
"@webgpu/types": "^0.1.44",
|
|
42
|
+
"typescript": "^5.3.3",
|
|
43
|
+
"vite": "^5.0.0"
|
|
44
|
+
},
|
|
45
|
+
"dependencies": {
|
|
46
|
+
"lamejs": "^1.2.1",
|
|
47
|
+
"mediabunny": "^1.27.3",
|
|
48
|
+
"mp4box": "^0.5.2",
|
|
49
|
+
"mpg123-decoder": "^1.0.3",
|
|
50
|
+
"web-demuxer": "^4.0.0"
|
|
51
|
+
}
|
|
52
|
+
}
|