https://github.com/sctg-development/libwebm-js
JavaScript/TypeScript bindings for the libwebm library
https://github.com/sctg-development/libwebm-js
Last synced: 3 months ago
JSON representation
JavaScript/TypeScript bindings for the libwebm library
- Host: GitHub
- URL: https://github.com/sctg-development/libwebm-js
- Owner: sctg-development
- License: bsd-3-clause
- Created: 2025-09-07T07:55:38.000Z (4 months ago)
- Default Branch: main
- Last Pushed: 2025-09-13T15:15:16.000Z (4 months ago)
- Last Synced: 2025-09-15T18:51:57.245Z (4 months ago)
- Language: JavaScript
- Homepage: https://sctg-development.github.io/libwebm-js/
- Size: 23.8 MB
- Stars: 11
- Watchers: 0
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- Funding: .github/FUNDING.yml
- License: LICENSE
Awesome Lists containing this project
README
# LibWebM JavaScript Bindings
JavaScript/TypeScript bindings for the libwebm library, providing WebM container format support for web and Node.js applications. This project creates Emscripten-compiled WASM bindings equivalent to the Swift LibWebMSwift package.
## Features
- **WebM Parsing**: Read and analyze WebM files
- **WebM Muxing**: Create WebM containers with video and audio tracks
- **Cross-Platform**: Works in browsers and Node.js
- **TypeScript Support**: Full TypeScript definitions included
- **Codec Support**: VP8, VP9, AV1 video; Opus, Vorbis audio
- **Frame-Level Access**: Read individual video/audio frames
- **Memory Efficient**: Streaming operations with minimal memory usage
## Installation
### Prerequisites
To build from source, you need:
- [Emscripten SDK](https://emscripten.org/docs/getting_started/downloads.html)
- CMake 3.10+
- Git
### Installing Emscripten
```bash
# Clone and install Emscripten
git clone https://github.com/emscripten-core/emsdk.git
cd emsdk
./emsdk install latest
./emsdk activate latest
source ./emsdk_env.sh
```
### Building
```bash
# Clone the repository
git clone https://github.com/sctg-development/libwebm-js libwebm-js
cd libwebm-js
# Build the library
./build.sh
# The built files will be in dist/
```
### Using Pre-built Binaries
If available, you can install via npm:
```bash
npm install @sctg/libwebm-js
```
## Usage
### Basic WebM File Parsing
```javascript
import createLibWebM from '@sctg/libwebm-js';
import fs from 'fs';
async function parseWebMFile() {
// Initialize the library
const libwebm = await createLibWebM();
// Load a WebM file
const buffer = fs.readFileSync('input.webm');
const file = await libwebm.WebMFile.fromBuffer(buffer, libwebm._module);
// Get file information
console.log(`Duration: ${file.getDuration()} seconds`);
console.log(`Track count: ${file.getTrackCount()}`);
// Analyze tracks
for (let i = 0; i < file.getTrackCount(); i++) {
const trackInfo = file.getTrackInfo(i);
console.log(`Track ${i}: ${trackInfo.codecId} (Type: ${trackInfo.trackType})`);
if (trackInfo.trackType === libwebm.WebMTrackType.VIDEO) {
const videoInfo = file.parser.getVideoInfo(trackInfo.trackNumber);
console.log(` Video: ${videoInfo.width}x${videoInfo.height}@${videoInfo.frameRate}fps`);
} else if (trackInfo.trackType === libwebm.WebMTrackType.AUDIO) {
const audioInfo = file.parser.getAudioInfo(trackInfo.trackNumber);
console.log(` Audio: ${audioInfo.channels}ch@${audioInfo.samplingFrequency}Hz`);
}
}
}
parseWebMFile().catch(console.error);
```
### Creating WebM Files - Video Only
```javascript
import createLibWebM from '@sctg/libwebm-js';
import fs from 'fs';
async function createVideoOnlyWebM() {
const libwebm = await createLibWebM();
// Create a new WebM file for writing
const file = libwebm.WebMFile.forWriting(libwebm._module);
// Add a video track (VP8, 1280x720)
const videoTrack = file.addVideoTrack(1280, 720, 'V_VP8');
// Write video frames (30fps = 33.33ms per frame)
const frameDurationNs = libwebm.WebMUtils.msToNs(33.33);
for (let i = 0; i < 150; i++) { // 5 seconds at 30fps
// Create frame data (in real usage, this would be encoded VP8 data)
const frameData = new Uint8Array(1000 + i * 100);
frameData.fill(i % 256); // Sample pattern
const timestampNs = i * frameDurationNs;
const isKeyframe = (i % 30) === 0; // Keyframe every second
file.writeVideoFrame(videoTrack, frameData, timestampNs, isKeyframe);
}
// Finalize and save
const webmData = file.finalize();
fs.writeFileSync('video-only.webm', webmData);
console.log('Video-only WebM created successfully!');
}
createVideoOnlyWebM().catch(console.error);
```
### Creating WebM Files - Audio Only
```javascript
import createLibWebM from '@sctg/libwebm-js';
import fs from 'fs';
async function createAudioOnlyWebM() {
const libwebm = await createLibWebM();
const file = libwebm.WebMFile.forWriting(libwebm._module);
// Add an audio track (Opus, 48kHz stereo)
const audioTrack = file.addAudioTrack(48000, 2, 'A_OPUS');
// Write audio frames (20ms per frame = typical Opus frame size)
const frameDurationNs = libwebm.WebMUtils.msToNs(20);
for (let i = 0; i < 250; i++) { // 5 seconds of audio
// Create audio frame data (in real usage, this would be encoded Opus data)
const frameData = new Uint8Array(100 + i % 50);
frameData.fill(i % 128 + 50); // Sample audio pattern
const timestampNs = i * frameDurationNs;
file.writeAudioFrame(audioTrack, frameData, timestampNs);
}
const webmData = file.finalize();
fs.writeFileSync('audio-only.webm', webmData);
console.log('Audio-only WebM created successfully!');
}
createAudioOnlyWebM().catch(console.error);
```
### Creating WebM Files - Mixed Video and Audio
```javascript
import createLibWebM from '@sctg/libwebm-js';
import fs from 'fs';
async function createMixedWebM() {
const libwebm = await createLibWebM();
const file = libwebm.WebMFile.forWriting(libwebm._module);
// Add video and audio tracks
const videoTrack = file.addVideoTrack(1920, 1080, 'V_VP9');
const audioTrack = file.addAudioTrack(48000, 2, 'A_OPUS');
const videoDurationNs = libwebm.WebMUtils.msToNs(33.33); // 30fps
const audioDurationNs = libwebm.WebMUtils.msToNs(20); // 20ms audio frames
const totalDurationMs = 3000; // 3 seconds
const totalVideoFrames = Math.floor(totalDurationMs / 33.33);
const totalAudioFrames = Math.floor(totalDurationMs / 20);
// Write video frames
for (let i = 0; i < totalVideoFrames; i++) {
const frameData = new Uint8Array(2000 + i * 200);
frameData.fill(i % 256);
const timestampNs = i * videoDurationNs;
const isKeyframe = (i % 30) === 0;
file.writeVideoFrame(videoTrack, frameData, timestampNs, isKeyframe);
}
// Write audio frames
for (let i = 0; i < totalAudioFrames; i++) {
const frameData = new Uint8Array(200 + i % 100);
frameData.fill(i % 128 + 64);
const timestampNs = i * audioDurationNs;
file.writeAudioFrame(audioTrack, frameData, timestampNs);
}
const webmData = file.finalize();
fs.writeFileSync('mixed-content.webm', webmData);
console.log('Mixed video/audio WebM created successfully!');
}
createMixedWebM().catch(console.error);
```
### Frame-by-Frame Extraction
```javascript
import createLibWebM from '@sctg/libwebm-js';
import fs from 'fs';
async function extractFrames() {
const libwebm = await createLibWebM();
const buffer = fs.readFileSync('input.webm');
const file = await libwebm.WebMFile.fromBuffer(buffer, libwebm._module);
// Find video track
let videoTrackNumber = 0;
for (let i = 0; i < file.getTrackCount(); i++) {
const trackInfo = file.getTrackInfo(i);
if (trackInfo.trackType === libwebm.WebMTrackType.VIDEO) {
videoTrackNumber = trackInfo.trackNumber;
break;
}
}
if (videoTrackNumber === 0) {
console.log('No video track found');
return;
}
// Extract video frames
let frameCount = 0;
const maxFrames = 100; // Limit for demo
while (frameCount < maxFrames) {
const frameData = file.parser.readNextVideoFrame(videoTrackNumber);
if (!frameData) break;
console.log(`Frame ${frameCount}: ${frameData.data.length} bytes, ` +
`timestamp: ${libwebm.WebMUtils.nsToMs(frameData.timestampNs)}ms, ` +
`keyframe: ${frameData.isKeyframe}`);
// Save frame data (you could decode this with a VP8/VP9 decoder)
fs.writeFileSync(`frame_${frameCount}.bin`, frameData.data);
frameCount++;
}
console.log(`Extracted ${frameCount} video frames`);
}
extractFrames().catch(console.error);
```
### TypeScript Usage with Full Type Safety
```typescript
import createLibWebM, { WebMFile, WebMUtils, WebMTrackType, WebMErrorCode } from '@sctg/libwebm-js';
import * as fs from 'fs';
async function typescriptExample(): Promise {
const libwebm = await createLibWebM();
// Type-safe file creation
const file: WebMFile = WebMFile.forWriting(libwebm._module);
// Type-safe track creation
const videoTrack: number = file.addVideoTrack(1280, 720, 'V_VP8');
const audioTrack: number = file.addAudioTrack(48000, 2, 'A_OPUS');
// Type-safe utility functions
const frameDurationNs: number = WebMUtils.msToNs(33.33);
// Type-safe codec validation
if (WebMUtils.isVideoCodecSupported('V_VP9')) {
console.log('VP9 is supported');
}
// Error handling with type safety
try {
file.writeVideoFrame(999, new Uint8Array([1, 2, 3]), 0, true); // Invalid track ID
} catch (error: unknown) {
if (error instanceof Error) {
console.error(`Error: ${error.message}`);
}
}
// Write valid frame and finalize
const frameData = new Uint8Array(1000);
file.writeVideoFrame(videoTrack, frameData, 0, true);
const webmData: Uint8Array = file.finalize();
fs.writeFileSync('typescript-output.webm', webmData);
}
typescriptExample().catch(console.error);
```
### Browser Usage
```html
import createLibWebM from './dist/wrapper.js';
async function browserExample() {
const libwebm = await createLibWebM();
// Parse WebM from file input
const fileInput = document.getElementById('webm-input');
fileInput.addEventListener('change', async (event) => {
const file = event.target.files[0];
if (!file) return;
const buffer = new Uint8Array(await file.arrayBuffer());
const webmFile = await libwebm.WebMFile.fromBuffer(buffer, libwebm._module);
console.log(`Duration: ${webmFile.getDuration()}s`);
console.log(`Tracks: ${webmFile.getTrackCount()}`);
// Display file info
const info = document.getElementById('info');
info.innerHTML = `
<p>Duration: ${webmFile.getDuration().toFixed(2)} seconds</p>
<p>Track count: ${webmFile.getTrackCount()}</p>
`;
for (let i = 0; i < webmFile.getTrackCount(); i++) {
const trackInfo = webmFile.getTrackInfo(i);
info.innerHTML += `<p>Track ${i}: ${trackInfo.codecId}</p>`;
}
});
// Create WebM in browser
const createButton = document.getElementById('create-webm');
createButton.addEventListener('click', async () => {
const file = libwebm.WebMFile.forWriting(libwebm._module);
const videoTrack = file.addVideoTrack(640, 480, 'V_VP8');
// Create sample video data
const frameData = new Uint8Array(500);
frameData.fill(128); // Gray frame
file.writeVideoFrame(videoTrack, frameData, 0, true);
const webmData = file.finalize();
// Download the created WebM
const blob = new Blob([webmData], { type: 'video/webm' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = 'created.webm';
a.click();
URL.revokeObjectURL(url);
});
}
browserExample().catch(console.error);
LibWebM Browser Example
Create Sample WebM
```
## Real-World Use Cases
### 1. Media File Analysis Tool
```javascript
// Analyze WebM files and extract metadata
import createLibWebM from '@sctg/libwebm-js';
import fs from 'fs';
async function analyzeMediaFile(filePath) {
const libwebm = await createLibWebM();
const buffer = fs.readFileSync(filePath);
const file = await libwebm.WebMFile.fromBuffer(buffer, libwebm._module);
const analysis = {
filename: filePath,
duration: file.getDuration(),
tracks: [],
hasVideo: false,
hasAudio: false,
fileSize: buffer.length
};
for (let i = 0; i < file.getTrackCount(); i++) {
const trackInfo = file.getTrackInfo(i);
const track = {
number: trackInfo.trackNumber,
type: trackInfo.trackType === libwebm.WebMTrackType.VIDEO ? 'video' : 'audio',
codec: trackInfo.codecId
};
if (track.type === 'video') {
const videoInfo = file.parser.getVideoInfo(trackInfo.trackNumber);
track.width = videoInfo.width;
track.height = videoInfo.height;
track.frameRate = videoInfo.frameRate;
analysis.hasVideo = true;
} else if (track.type === 'audio') {
const audioInfo = file.parser.getAudioInfo(trackInfo.trackNumber);
track.sampleRate = audioInfo.samplingFrequency;
track.channels = audioInfo.channels;
track.bitDepth = audioInfo.bitDepth;
analysis.hasAudio = true;
}
analysis.tracks.push(track);
}
return analysis;
}
```
### 2. WebM Transcoding Pipeline
```javascript
// Convert between different WebM formats
import createLibWebM from '@sctg/libwebm-js';
import fs from 'fs';
async function convertWebM(inputPath, outputPath, options = {}) {
const libwebm = await createLibWebM();
// Parse input file
const inputBuffer = fs.readFileSync(inputPath);
const inputFile = await libwebm.WebMFile.fromBuffer(inputBuffer, libwebm._module);
// Create output file
const outputFile = libwebm.WebMFile.forWriting(libwebm._module);
const trackMapping = {};
// Copy tracks with potential format changes
for (let i = 0; i < inputFile.getTrackCount(); i++) {
const trackInfo = inputFile.getTrackInfo(i);
if (trackInfo.trackType === libwebm.WebMTrackType.VIDEO) {
const videoInfo = inputFile.parser.getVideoInfo(trackInfo.trackNumber);
const newCodec = options.videoCodec || trackInfo.codecId;
const newWidth = options.width || videoInfo.width;
const newHeight = options.height || videoInfo.height;
const newTrackId = outputFile.addVideoTrack(newWidth, newHeight, newCodec);
trackMapping[trackInfo.trackNumber] = { id: newTrackId, type: 'video' };
} else if (trackInfo.trackType === libwebm.WebMTrackType.AUDIO) {
const audioInfo = inputFile.parser.getAudioInfo(trackInfo.trackNumber);
const newCodec = options.audioCodec || trackInfo.codecId;
const newSampleRate = options.sampleRate || audioInfo.samplingFrequency;
const newChannels = options.channels || audioInfo.channels;
const newTrackId = outputFile.addAudioTrack(newSampleRate, newChannels, newCodec);
trackMapping[trackInfo.trackNumber] = { id: newTrackId, type: 'audio' };
}
}
// Copy frame data (in a real implementation, you might re-encode here)
// This is a simplified example that copies raw frame data
for (const [originalId, mapping] of Object.entries(trackMapping)) {
if (mapping.type === 'video') {
let frameData;
while ((frameData = inputFile.parser.readNextVideoFrame(parseInt(originalId)))) {
outputFile.writeVideoFrame(
mapping.id,
frameData.data,
frameData.timestampNs,
frameData.isKeyframe
);
}
} else if (mapping.type === 'audio') {
let frameData;
while ((frameData = inputFile.parser.readNextAudioFrame(parseInt(originalId)))) {
outputFile.writeAudioFrame(
mapping.id,
frameData.data,
frameData.timestampNs
);
}
}
}
const outputData = outputFile.finalize();
fs.writeFileSync(outputPath, outputData);
console.log(`Converted ${inputPath} to ${outputPath}`);
}
```
### 3. Streaming WebM Creator
```javascript
// Create WebM files from streaming data
import createLibWebM from '@sctg/libwebm-js';
import fs from 'fs';
class StreamingWebMWriter {
constructor(options = {}) {
this.options = options;
this.chunks = [];
this.initialized = false;
}
async initialize() {
this.libwebm = await createLibWebM();
this.file = this.libwebm.WebMFile.forWriting(this.libwebm._module);
if (this.options.video) {
this.videoTrack = this.file.addVideoTrack(
this.options.video.width || 1280,
this.options.video.height || 720,
this.options.video.codec || 'V_VP8'
);
}
if (this.options.audio) {
this.audioTrack = this.file.addAudioTrack(
this.options.audio.sampleRate || 48000,
this.options.audio.channels || 2,
this.options.audio.codec || 'A_OPUS'
);
}
this.initialized = true;
}
writeVideoFrame(data, timestampMs, isKeyframe = false) {
if (!this.initialized || !this.videoTrack) {
throw new Error('Writer not initialized or no video track');
}
const timestampNs = this.libwebm.WebMUtils.msToNs(timestampMs);
this.file.writeVideoFrame(this.videoTrack, data, timestampNs, isKeyframe);
}
writeAudioFrame(data, timestampMs) {
if (!this.initialized || !this.audioTrack) {
throw new Error('Writer not initialized or no audio track');
}
const timestampNs = this.libwebm.WebMUtils.msToNs(timestampMs);
this.file.writeAudioFrame(this.audioTrack, data, timestampNs);
}
finalize() {
if (!this.initialized) {
throw new Error('Writer not initialized');
}
return this.file.finalize();
}
}
// Usage example
async function streamingExample() {
const writer = new StreamingWebMWriter({
video: { width: 1920, height: 1080, codec: 'V_VP9' },
audio: { sampleRate: 48000, channels: 2, codec: 'A_OPUS' }
});
await writer.initialize();
// Simulate streaming data (in real usage, this would come from encoders)
for (let i = 0; i < 300; i++) { // 10 seconds at 30fps
// Video frame every 33.33ms
const videoData = new Uint8Array(2000 + Math.random() * 1000);
writer.writeVideoFrame(videoData, i * 33.33, i % 30 === 0);
// Audio frame every 20ms (more frequent than video)
if (i % 20 === 0) {
const audioData = new Uint8Array(200 + Math.random() * 100);
writer.writeAudioFrame(audioData, i * 33.33);
}
}
const webmData = writer.finalize();
fs.writeFileSync('streaming-output.webm', webmData);
console.log('Streaming WebM created successfully!');
}
```
## API Reference
### WebMFile
High-level interface for WebM operations.
#### Static Methods
- `WebMFile.fromBuffer(buffer: Uint8Array, module: LibWebMModule): Promise`
- `WebMFile.forWriting(module: LibWebMModule): WebMFile`
#### Instance Methods
**Reading (Parser mode):**
- `getDuration(): number` - Get duration in seconds
- `getTrackCount(): number` - Get number of tracks
- `getTrackInfo(trackIndex: number): WebMTrackInfo` - Get track information
**Writing (Muxer mode):**
- `addVideoTrack(width: number, height: number, codecId: string): number`
- `addAudioTrack(samplingFrequency: number, channels: number, codecId: string): number`
- `writeVideoFrame(trackId: number, frameData: Uint8Array, timestampNs: number, isKeyframe: boolean): void`
- `writeAudioFrame(trackId: number, frameData: Uint8Array, timestampNs: number): void`
- `finalize(): Uint8Array` - Get final WebM data
### WebMUtils
Utility functions for common operations.
- `isVideoCodecSupported(codecId: string): boolean`
- `isAudioCodecSupported(codecId: string): boolean`
- `nsToMs(ns: number): number` - Convert nanoseconds to milliseconds
- `msToNs(ms: number): number` - Convert milliseconds to nanoseconds
- `getSupportedVideoCodecs(): string[]`
- `getSupportedAudioCodecs(): string[]`
### Supported Codecs
**Video:**
- `V_VP8` - VP8 codec
- `V_VP9` - VP9 codec
- `V_AV01` - AV1 codec
**Audio:**
- `A_OPUS` - Opus codec
- `A_VORBIS` - Vorbis codec
### Error Handling
All methods throw standard JavaScript Error objects with descriptive messages. Common error codes:
- `INVALID_FILE` - File format not recognized
- `CORRUPTED_DATA` - WebM data is corrupted
- `UNSUPPORTED_FORMAT` - Codec or feature not supported
- `IO_ERROR` - Input/output error
- `INVALID_ARGUMENT` - Invalid parameter passed
## Examples
See the `examples/` directory for complete usage examples:
- `basic-usage.js` - Basic file creation and parsing
- `advanced-parser.js` - Advanced parsing with frame extraction
- `streaming-muxer.js` - Streaming WebM creation
- `browser-example.html` - Browser usage example
## Advanced Usage
### Frame-by-Frame Processing
```javascript
const libwebm = await createLibWebM();
const buffer = fs.readFileSync('input.webm');
const file = await libwebm.WebMFile.fromBuffer(buffer, libwebm._module);
// Process all video frames
const trackInfo = file.getTrackInfo(0);
if (trackInfo.trackType === libwebm.WebMTrackType.VIDEO) {
let frame;
while ((frame = file.parser.readNextVideoFrame(trackInfo.trackNumber)) !== null) {
console.log(`Frame: ${frame.data.length} bytes at ${WebMUtils.nsToMs(frame.timestampNs)}ms`);
// Process frame data...
}
}
```
### Streaming WebM Creation
```javascript
const libwebm = await createLibWebM();
const file = libwebm.WebMFile.forWriting(libwebm._module);
const videoTrack = file.addVideoTrack(1920, 1080, 'V_VP9');
// Write frames as they become available
function writeFrame(frameData, timestamp, isKeyframe) {
file.writeVideoFrame(videoTrack, frameData, WebMUtils.msToNs(timestamp), isKeyframe);
// Get current data for streaming (before finalization)
const currentData = file.muxer.getData();
// Send currentData to client/server...
}
// When done
const finalData = file.finalize();
```
## Live Demo
🚀 **Try libwebm-js in your browser!**
A comprehensive interactive demo is available at: **[https://sctg-development.github.io/libwebm-js](https://sctg-development.github.io/libwebm-js)**
### Demo Features
The live demo showcases all major libwebm-js capabilities with a modern, responsive web interface built using:
- **React 19.1.1** - Latest React with modern hooks and concurrent features
- **HeroUI 2.8.0** - Beautiful and accessible component library
- **Tailwind CSS 4.1.12** - Utility-first CSS framework
- **Vite 7.1.1** - Fast build tool and development server
- **TypeScript** - Full type safety and excellent developer experience
### Interactive Examples
#### 📁 WebM File Parser
- **File Upload**: Drag & drop or click to upload WebM files
- **Real-time Parsing**: Instant analysis of file structure and metadata
- **Format Validation**: Automatic detection of supported codecs and formats
- **Error Handling**: Clear error messages for unsupported files
#### 🎵 Track Information Display
- **Detailed Metadata**: Complete track information including codec details
- **Video Parameters**: Resolution, frame rate, and codec information
- **Audio Parameters**: Sample rate, channels, and bit depth
- **Multi-track Support**: Handle files with multiple video/audio tracks
#### 🎬 Frame Extraction
- **Frame-by-Frame Analysis**: Extract and examine individual frames
- **Timing Information**: Precise timestamp data for each frame
- **Keyframe Detection**: Identify keyframes for efficient seeking
- **Performance Metrics**: Real-time extraction speed and memory usage
#### 🎞️ WebM Muxer Demo
- **Track Configuration**: Set up video and audio tracks with custom parameters
- **Codec Selection**: Choose from supported VP8, VP9, AV1, Opus, and Vorbis
- **Frame Writing**: Simulate writing frames with proper timing
- **Output Generation**: Create WebM files with real muxing logic
#### ⚡ Performance Testing
- **Benchmark Suite**: Comprehensive performance tests
- **Memory Monitoring**: Track memory usage during operations
- **Concurrent Operations**: Test multi-threaded performance
- **Detailed Reports**: Performance ratings and optimization suggestions
### Demo Architecture
The demo is built with a modular component architecture:
```
demo/src/
├── components/
│ ├── WebMFileParser.tsx # File upload and parsing interface
│ ├── TrackInfoDisplay.tsx # Track metadata visualization
│ ├── FrameExtractor.tsx # Frame extraction controls
│ ├── MuxerDemo.tsx # WebM creation interface
│ ├── PerformanceTester.tsx # Performance benchmarking
│ └── index.ts # Component exports
├── App.tsx # Main application with tabbed interface
├── main.tsx # Application entry point
├── vite-env.d.ts # TypeScript environment declarations
└── index.css # Tailwind CSS configuration
```
### Running the Demo Locally
```bash
# Clone the repository
git clone https://github.com/sctg-development/libwebm-js.git
cd libwebm-js/demo
# Install dependencies
npm install
# Start the development server
npm run dev
# Open http://localhost:5173 in your browser
```
### Demo vs Production Code
**Note**: The demo currently uses simulated operations for demonstration purposes. The actual libwebm-js library provides:
- Real WebM parsing and muxing capabilities
- Full WASM-powered performance
- Production-ready error handling
- Complete TypeScript type definitions
- Memory-efficient streaming operations
The demo serves as a comprehensive showcase of the API and user experience, while the production library in `dist/` contains the actual compiled WASM bindings.
### Browser Compatibility
The demo works in all modern browsers that support:
- WebAssembly (WASM)
- ES2020 features
- Modern JavaScript APIs
**Supported Browsers:**
- Chrome 57+
- Firefox 52+
- Safari 11+
- Edge 16+
## Building from Source
The build process:
1. Clones Google's libwebm repository
2. Compiles libwebm with Emscripten
3. Compiles the C++ bindings (`src/libwebm-bindings.cpp`)
4. Generates JavaScript/WASM files
5. Creates the wrapper and TypeScript definitions
Build options can be configured in `CMakeLists.txt` and `build.sh`.
## Performance
- WASM compilation provides near-native performance
- Memory usage is optimized for streaming operations
- Large files can be processed with constant memory usage
- Frame-by-frame processing avoids loading entire file into memory
## License
BSD 3-Clause License. See LICENSE file for details.
## Contributing
1. Fork the repository
2. Create a feature branch
3. Make your changes
4. Add tests for new functionality
5. Submit a pull request
## See Also
- [libwebm](https://chromium.googlesource.com/webm/libwebm) - Original C++ library
- [LibWebMSwift](https://github.com/sctg-development/libwebm-swift) - Swift bindings for iOS/macOS
- [WebM Project](https://www.webmproject.org/) - WebM format specification