{"id":31923513,"url":"https://github.com/sctg-development/libwebm-js","last_synced_at":"2025-10-13T23:54:26.226Z","repository":{"id":313639780,"uuid":"1052011226","full_name":"sctg-development/libwebm-js","owner":"sctg-development","description":"JavaScript/TypeScript bindings for the libwebm library","archived":false,"fork":false,"pushed_at":"2025-09-13T15:15:16.000Z","size":24960,"stargazers_count":11,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2025-09-15T18:51:57.245Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"https://sctg-development.github.io/libwebm-js/","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-3-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/sctg-development.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null},"funding":{"github":["sctg-development"]}},"created_at":"2025-09-07T07:55:38.000Z","updated_at":"2025-09-13T15:14:08.000Z","dependencies_parsed_at":"2025-09-07T15:17:53.942Z","dependency_job_id":null,"html_url":"https://github.com/sctg-development/libwebm-js","commit_stats":null,"previous_names":["sctg-development/libwebm-js"],"tags_count":7,"template":false,"template_full_name":null,"purl":"pkg:github/sctg-development/libwebm-js","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sctg-development%2Flibwebm-js","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sctg-development%2Flibwebm-js/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sctg-development%2Flibwebm-js/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sctg-development%2Flibwebm-js/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/sctg-development","download_url":"https://codeload.github.com/sctg-development/libwebm-js/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sctg-development%2Flibwebm-js/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":279017190,"owners_count":26086017,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-10-13T02:00:06.723Z","response_time":61,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2025-10-13T23:54:23.762Z","updated_at":"2025-10-13T23:54:26.217Z","avatar_url":"https://github.com/sctg-development.png","language":"JavaScript","funding_links":["https://github.com/sponsors/sctg-development"],"categories":[],"sub_categories":[],"readme":"# LibWebM JavaScript Bindings\n\nJavaScript/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.\n\n## Features\n\n- **WebM Parsing**: Read and analyze WebM files\n- **WebM Muxing**: Create WebM containers with video and audio tracks\n- **Cross-Platform**: Works in browsers and Node.js\n- **TypeScript Support**: Full TypeScript definitions included\n- **Codec Support**: VP8, VP9, AV1 video; Opus, Vorbis audio\n- **Frame-Level Access**: Read individual video/audio frames\n- **Memory Efficient**: Streaming operations with minimal memory usage\n\n## Installation\n\n### Prerequisites\n\nTo build from source, you need:\n\n- [Emscripten SDK](https://emscripten.org/docs/getting_started/downloads.html)\n- CMake 3.10+\n- Git\n\n### Installing Emscripten\n\n```bash\n# Clone and install Emscripten\ngit clone https://github.com/emscripten-core/emsdk.git\ncd emsdk\n./emsdk install latest\n./emsdk activate latest\nsource ./emsdk_env.sh\n```\n\n### Building\n\n```bash\n# Clone the repository\ngit clone https://github.com/sctg-development/libwebm-js libwebm-js\ncd libwebm-js\n\n# Build the library\n./build.sh\n\n# The built files will be in dist/\n```\n\n### Using Pre-built Binaries\n\nIf available, you can install via npm:\n\n```bash\nnpm install @sctg/libwebm-js\n```\n\n## Usage\n\n### Basic WebM File Parsing\n\n```javascript\nimport createLibWebM from '@sctg/libwebm-js';\nimport fs from 'fs';\n\nasync function parseWebMFile() {\n    // Initialize the library\n    const libwebm = await createLibWebM();\n    \n    // Load a WebM file\n    const buffer = fs.readFileSync('input.webm');\n    const file = await libwebm.WebMFile.fromBuffer(buffer, libwebm._module);\n    \n    // Get file information\n    console.log(`Duration: ${file.getDuration()} seconds`);\n    console.log(`Track count: ${file.getTrackCount()}`);\n    \n    // Analyze tracks\n    for (let i = 0; i \u003c file.getTrackCount(); i++) {\n        const trackInfo = file.getTrackInfo(i);\n        console.log(`Track ${i}: ${trackInfo.codecId} (Type: ${trackInfo.trackType})`);\n        \n        if (trackInfo.trackType === libwebm.WebMTrackType.VIDEO) {\n            const videoInfo = file.parser.getVideoInfo(trackInfo.trackNumber);\n            console.log(`  Video: ${videoInfo.width}x${videoInfo.height}@${videoInfo.frameRate}fps`);\n        } else if (trackInfo.trackType === libwebm.WebMTrackType.AUDIO) {\n            const audioInfo = file.parser.getAudioInfo(trackInfo.trackNumber);\n            console.log(`  Audio: ${audioInfo.channels}ch@${audioInfo.samplingFrequency}Hz`);\n        }\n    }\n}\n\nparseWebMFile().catch(console.error);\n```\n\n### Creating WebM Files - Video Only\n\n```javascript\nimport createLibWebM from '@sctg/libwebm-js';\nimport fs from 'fs';\n\nasync function createVideoOnlyWebM() {\n    const libwebm = await createLibWebM();\n    \n    // Create a new WebM file for writing\n    const file = libwebm.WebMFile.forWriting(libwebm._module);\n    \n    // Add a video track (VP8, 1280x720)\n    const videoTrack = file.addVideoTrack(1280, 720, 'V_VP8');\n    \n    // Write video frames (30fps = 33.33ms per frame)\n    const frameDurationNs = libwebm.WebMUtils.msToNs(33.33);\n    \n    for (let i = 0; i \u003c 150; i++) { // 5 seconds at 30fps\n        // Create frame data (in real usage, this would be encoded VP8 data)\n        const frameData = new Uint8Array(1000 + i * 100);\n        frameData.fill(i % 256); // Sample pattern\n        \n        const timestampNs = i * frameDurationNs;\n        const isKeyframe = (i % 30) === 0; // Keyframe every second\n        \n        file.writeVideoFrame(videoTrack, frameData, timestampNs, isKeyframe);\n    }\n    \n    // Finalize and save\n    const webmData = file.finalize();\n    fs.writeFileSync('video-only.webm', webmData);\n    \n    console.log('Video-only WebM created successfully!');\n}\n\ncreateVideoOnlyWebM().catch(console.error);\n```\n\n### Creating WebM Files - Audio Only\n\n```javascript\nimport createLibWebM from '@sctg/libwebm-js';\nimport fs from 'fs';\n\nasync function createAudioOnlyWebM() {\n    const libwebm = await createLibWebM();\n    \n    const file = libwebm.WebMFile.forWriting(libwebm._module);\n    \n    // Add an audio track (Opus, 48kHz stereo)\n    const audioTrack = file.addAudioTrack(48000, 2, 'A_OPUS');\n    \n    // Write audio frames (20ms per frame = typical Opus frame size)\n    const frameDurationNs = libwebm.WebMUtils.msToNs(20);\n    \n    for (let i = 0; i \u003c 250; i++) { // 5 seconds of audio\n        // Create audio frame data (in real usage, this would be encoded Opus data)\n        const frameData = new Uint8Array(100 + i % 50);\n        frameData.fill(i % 128 + 50); // Sample audio pattern\n        \n        const timestampNs = i * frameDurationNs;\n        file.writeAudioFrame(audioTrack, frameData, timestampNs);\n    }\n    \n    const webmData = file.finalize();\n    fs.writeFileSync('audio-only.webm', webmData);\n    \n    console.log('Audio-only WebM created successfully!');\n}\n\ncreateAudioOnlyWebM().catch(console.error);\n```\n\n### Creating WebM Files - Mixed Video and Audio\n\n```javascript\nimport createLibWebM from '@sctg/libwebm-js';\nimport fs from 'fs';\n\nasync function createMixedWebM() {\n    const libwebm = await createLibWebM();\n    \n    const file = libwebm.WebMFile.forWriting(libwebm._module);\n    \n    // Add video and audio tracks\n    const videoTrack = file.addVideoTrack(1920, 1080, 'V_VP9');\n    const audioTrack = file.addAudioTrack(48000, 2, 'A_OPUS');\n    \n    const videoDurationNs = libwebm.WebMUtils.msToNs(33.33); // 30fps\n    const audioDurationNs = libwebm.WebMUtils.msToNs(20);    // 20ms audio frames\n    \n    const totalDurationMs = 3000; // 3 seconds\n    const totalVideoFrames = Math.floor(totalDurationMs / 33.33);\n    const totalAudioFrames = Math.floor(totalDurationMs / 20);\n    \n    // Write video frames\n    for (let i = 0; i \u003c totalVideoFrames; i++) {\n        const frameData = new Uint8Array(2000 + i * 200);\n        frameData.fill(i % 256);\n        \n        const timestampNs = i * videoDurationNs;\n        const isKeyframe = (i % 30) === 0;\n        \n        file.writeVideoFrame(videoTrack, frameData, timestampNs, isKeyframe);\n    }\n    \n    // Write audio frames\n    for (let i = 0; i \u003c totalAudioFrames; i++) {\n        const frameData = new Uint8Array(200 + i % 100);\n        frameData.fill(i % 128 + 64);\n        \n        const timestampNs = i * audioDurationNs;\n        file.writeAudioFrame(audioTrack, frameData, timestampNs);\n    }\n    \n    const webmData = file.finalize();\n    fs.writeFileSync('mixed-content.webm', webmData);\n    \n    console.log('Mixed video/audio WebM created successfully!');\n}\n\ncreateMixedWebM().catch(console.error);\n```\n\n### Frame-by-Frame Extraction\n\n```javascript\nimport createLibWebM from '@sctg/libwebm-js';\nimport fs from 'fs';\n\nasync function extractFrames() {\n    const libwebm = await createLibWebM();\n    \n    const buffer = fs.readFileSync('input.webm');\n    const file = await libwebm.WebMFile.fromBuffer(buffer, libwebm._module);\n    \n    // Find video track\n    let videoTrackNumber = 0;\n    for (let i = 0; i \u003c file.getTrackCount(); i++) {\n        const trackInfo = file.getTrackInfo(i);\n        if (trackInfo.trackType === libwebm.WebMTrackType.VIDEO) {\n            videoTrackNumber = trackInfo.trackNumber;\n            break;\n        }\n    }\n    \n    if (videoTrackNumber === 0) {\n        console.log('No video track found');\n        return;\n    }\n    \n    // Extract video frames\n    let frameCount = 0;\n    const maxFrames = 100; // Limit for demo\n    \n    while (frameCount \u003c maxFrames) {\n        const frameData = file.parser.readNextVideoFrame(videoTrackNumber);\n        if (!frameData) break;\n        \n        console.log(`Frame ${frameCount}: ${frameData.data.length} bytes, ` +\n                   `timestamp: ${libwebm.WebMUtils.nsToMs(frameData.timestampNs)}ms, ` +\n                   `keyframe: ${frameData.isKeyframe}`);\n        \n        // Save frame data (you could decode this with a VP8/VP9 decoder)\n        fs.writeFileSync(`frame_${frameCount}.bin`, frameData.data);\n        \n        frameCount++;\n    }\n    \n    console.log(`Extracted ${frameCount} video frames`);\n}\n\nextractFrames().catch(console.error);\n```\n\n### TypeScript Usage with Full Type Safety\n\n```typescript\nimport createLibWebM, { WebMFile, WebMUtils, WebMTrackType, WebMErrorCode } from '@sctg/libwebm-js';\nimport * as fs from 'fs';\n\nasync function typescriptExample(): Promise\u003cvoid\u003e {\n    const libwebm = await createLibWebM();\n    \n    // Type-safe file creation\n    const file: WebMFile = WebMFile.forWriting(libwebm._module);\n    \n    // Type-safe track creation\n    const videoTrack: number = file.addVideoTrack(1280, 720, 'V_VP8');\n    const audioTrack: number = file.addAudioTrack(48000, 2, 'A_OPUS');\n    \n    // Type-safe utility functions\n    const frameDurationNs: number = WebMUtils.msToNs(33.33);\n    \n    // Type-safe codec validation\n    if (WebMUtils.isVideoCodecSupported('V_VP9')) {\n        console.log('VP9 is supported');\n    }\n    \n    // Error handling with type safety\n    try {\n        file.writeVideoFrame(999, new Uint8Array([1, 2, 3]), 0, true); // Invalid track ID\n    } catch (error: unknown) {\n        if (error instanceof Error) {\n            console.error(`Error: ${error.message}`);\n        }\n    }\n    \n    // Write valid frame and finalize\n    const frameData = new Uint8Array(1000);\n    file.writeVideoFrame(videoTrack, frameData, 0, true);\n    \n    const webmData: Uint8Array = file.finalize();\n    fs.writeFileSync('typescript-output.webm', webmData);\n}\n\ntypescriptExample().catch(console.error);\n```\n\n### Browser Usage\n\n```html\n\u003c!DOCTYPE html\u003e\n\u003chtml\u003e\n\u003chead\u003e\n    \u003cscript type=\"module\"\u003e\n        import createLibWebM from './dist/wrapper.js';\n        \n        async function browserExample() {\n            const libwebm = await createLibWebM();\n            \n            // Parse WebM from file input\n            const fileInput = document.getElementById('webm-input');\n            fileInput.addEventListener('change', async (event) =\u003e {\n                const file = event.target.files[0];\n                if (!file) return;\n                \n                const buffer = new Uint8Array(await file.arrayBuffer());\n                const webmFile = await libwebm.WebMFile.fromBuffer(buffer, libwebm._module);\n                \n                console.log(`Duration: ${webmFile.getDuration()}s`);\n                console.log(`Tracks: ${webmFile.getTrackCount()}`);\n                \n                // Display file info\n                const info = document.getElementById('info');\n                info.innerHTML = `\n                    \u003cp\u003eDuration: ${webmFile.getDuration().toFixed(2)} seconds\u003c/p\u003e\n                    \u003cp\u003eTrack count: ${webmFile.getTrackCount()}\u003c/p\u003e\n                `;\n                \n                for (let i = 0; i \u003c webmFile.getTrackCount(); i++) {\n                    const trackInfo = webmFile.getTrackInfo(i);\n                    info.innerHTML += `\u003cp\u003eTrack ${i}: ${trackInfo.codecId}\u003c/p\u003e`;\n                }\n            });\n            \n            // Create WebM in browser\n            const createButton = document.getElementById('create-webm');\n            createButton.addEventListener('click', async () =\u003e {\n                const file = libwebm.WebMFile.forWriting(libwebm._module);\n                const videoTrack = file.addVideoTrack(640, 480, 'V_VP8');\n                \n                // Create sample video data\n                const frameData = new Uint8Array(500);\n                frameData.fill(128); // Gray frame\n                \n                file.writeVideoFrame(videoTrack, frameData, 0, true);\n                \n                const webmData = file.finalize();\n                \n                // Download the created WebM\n                const blob = new Blob([webmData], { type: 'video/webm' });\n                const url = URL.createObjectURL(blob);\n                const a = document.createElement('a');\n                a.href = url;\n                a.download = 'created.webm';\n                a.click();\n                URL.revokeObjectURL(url);\n            });\n        }\n        \n        browserExample().catch(console.error);\n    \u003c/script\u003e\n\u003c/head\u003e\n\u003cbody\u003e\n    \u003ch1\u003eLibWebM Browser Example\u003c/h1\u003e\n    \u003cinput type=\"file\" id=\"webm-input\" accept=\".webm\"\u003e\n    \u003cbutton id=\"create-webm\"\u003eCreate Sample WebM\u003c/button\u003e\n    \u003cdiv id=\"info\"\u003e\u003c/div\u003e\n\u003c/body\u003e\n\u003c/html\u003e\n```\n\n## Real-World Use Cases\n\n### 1. Media File Analysis Tool\n\n```javascript\n// Analyze WebM files and extract metadata\nimport createLibWebM from '@sctg/libwebm-js';\nimport fs from 'fs';\n\nasync function analyzeMediaFile(filePath) {\n    const libwebm = await createLibWebM();\n    const buffer = fs.readFileSync(filePath);\n    const file = await libwebm.WebMFile.fromBuffer(buffer, libwebm._module);\n    \n    const analysis = {\n        filename: filePath,\n        duration: file.getDuration(),\n        tracks: [],\n        hasVideo: false,\n        hasAudio: false,\n        fileSize: buffer.length\n    };\n    \n    for (let i = 0; i \u003c file.getTrackCount(); i++) {\n        const trackInfo = file.getTrackInfo(i);\n        const track = {\n            number: trackInfo.trackNumber,\n            type: trackInfo.trackType === libwebm.WebMTrackType.VIDEO ? 'video' : 'audio',\n            codec: trackInfo.codecId\n        };\n        \n        if (track.type === 'video') {\n            const videoInfo = file.parser.getVideoInfo(trackInfo.trackNumber);\n            track.width = videoInfo.width;\n            track.height = videoInfo.height;\n            track.frameRate = videoInfo.frameRate;\n            analysis.hasVideo = true;\n        } else if (track.type === 'audio') {\n            const audioInfo = file.parser.getAudioInfo(trackInfo.trackNumber);\n            track.sampleRate = audioInfo.samplingFrequency;\n            track.channels = audioInfo.channels;\n            track.bitDepth = audioInfo.bitDepth;\n            analysis.hasAudio = true;\n        }\n        \n        analysis.tracks.push(track);\n    }\n    \n    return analysis;\n}\n```\n\n### 2. WebM Transcoding Pipeline\n\n```javascript\n// Convert between different WebM formats\nimport createLibWebM from '@sctg/libwebm-js';\nimport fs from 'fs';\n\nasync function convertWebM(inputPath, outputPath, options = {}) {\n    const libwebm = await createLibWebM();\n    \n    // Parse input file\n    const inputBuffer = fs.readFileSync(inputPath);\n    const inputFile = await libwebm.WebMFile.fromBuffer(inputBuffer, libwebm._module);\n    \n    // Create output file\n    const outputFile = libwebm.WebMFile.forWriting(libwebm._module);\n    \n    const trackMapping = {};\n    \n    // Copy tracks with potential format changes\n    for (let i = 0; i \u003c inputFile.getTrackCount(); i++) {\n        const trackInfo = inputFile.getTrackInfo(i);\n        \n        if (trackInfo.trackType === libwebm.WebMTrackType.VIDEO) {\n            const videoInfo = inputFile.parser.getVideoInfo(trackInfo.trackNumber);\n            const newCodec = options.videoCodec || trackInfo.codecId;\n            const newWidth = options.width || videoInfo.width;\n            const newHeight = options.height || videoInfo.height;\n            \n            const newTrackId = outputFile.addVideoTrack(newWidth, newHeight, newCodec);\n            trackMapping[trackInfo.trackNumber] = { id: newTrackId, type: 'video' };\n        } else if (trackInfo.trackType === libwebm.WebMTrackType.AUDIO) {\n            const audioInfo = inputFile.parser.getAudioInfo(trackInfo.trackNumber);\n            const newCodec = options.audioCodec || trackInfo.codecId;\n            const newSampleRate = options.sampleRate || audioInfo.samplingFrequency;\n            const newChannels = options.channels || audioInfo.channels;\n            \n            const newTrackId = outputFile.addAudioTrack(newSampleRate, newChannels, newCodec);\n            trackMapping[trackInfo.trackNumber] = { id: newTrackId, type: 'audio' };\n        }\n    }\n    \n    // Copy frame data (in a real implementation, you might re-encode here)\n    // This is a simplified example that copies raw frame data\n    for (const [originalId, mapping] of Object.entries(trackMapping)) {\n        if (mapping.type === 'video') {\n            let frameData;\n            while ((frameData = inputFile.parser.readNextVideoFrame(parseInt(originalId)))) {\n                outputFile.writeVideoFrame(\n                    mapping.id,\n                    frameData.data,\n                    frameData.timestampNs,\n                    frameData.isKeyframe\n                );\n            }\n        } else if (mapping.type === 'audio') {\n            let frameData;\n            while ((frameData = inputFile.parser.readNextAudioFrame(parseInt(originalId)))) {\n                outputFile.writeAudioFrame(\n                    mapping.id,\n                    frameData.data,\n                    frameData.timestampNs\n                );\n            }\n        }\n    }\n    \n    const outputData = outputFile.finalize();\n    fs.writeFileSync(outputPath, outputData);\n    \n    console.log(`Converted ${inputPath} to ${outputPath}`);\n}\n```\n\n### 3. Streaming WebM Creator\n\n```javascript\n// Create WebM files from streaming data\nimport createLibWebM from '@sctg/libwebm-js';\nimport fs from 'fs';\n\nclass StreamingWebMWriter {\n    constructor(options = {}) {\n        this.options = options;\n        this.chunks = [];\n        this.initialized = false;\n    }\n    \n    async initialize() {\n        this.libwebm = await createLibWebM();\n        this.file = this.libwebm.WebMFile.forWriting(this.libwebm._module);\n        \n        if (this.options.video) {\n            this.videoTrack = this.file.addVideoTrack(\n                this.options.video.width || 1280,\n                this.options.video.height || 720,\n                this.options.video.codec || 'V_VP8'\n            );\n        }\n        \n        if (this.options.audio) {\n            this.audioTrack = this.file.addAudioTrack(\n                this.options.audio.sampleRate || 48000,\n                this.options.audio.channels || 2,\n                this.options.audio.codec || 'A_OPUS'\n            );\n        }\n        \n        this.initialized = true;\n    }\n    \n    writeVideoFrame(data, timestampMs, isKeyframe = false) {\n        if (!this.initialized || !this.videoTrack) {\n            throw new Error('Writer not initialized or no video track');\n        }\n        \n        const timestampNs = this.libwebm.WebMUtils.msToNs(timestampMs);\n        this.file.writeVideoFrame(this.videoTrack, data, timestampNs, isKeyframe);\n    }\n    \n    writeAudioFrame(data, timestampMs) {\n        if (!this.initialized || !this.audioTrack) {\n            throw new Error('Writer not initialized or no audio track');\n        }\n        \n        const timestampNs = this.libwebm.WebMUtils.msToNs(timestampMs);\n        this.file.writeAudioFrame(this.audioTrack, data, timestampNs);\n    }\n    \n    finalize() {\n        if (!this.initialized) {\n            throw new Error('Writer not initialized');\n        }\n        \n        return this.file.finalize();\n    }\n}\n\n// Usage example\nasync function streamingExample() {\n    const writer = new StreamingWebMWriter({\n        video: { width: 1920, height: 1080, codec: 'V_VP9' },\n        audio: { sampleRate: 48000, channels: 2, codec: 'A_OPUS' }\n    });\n    \n    await writer.initialize();\n    \n    // Simulate streaming data (in real usage, this would come from encoders)\n    for (let i = 0; i \u003c 300; i++) { // 10 seconds at 30fps\n        // Video frame every 33.33ms\n        const videoData = new Uint8Array(2000 + Math.random() * 1000);\n        writer.writeVideoFrame(videoData, i * 33.33, i % 30 === 0);\n        \n        // Audio frame every 20ms (more frequent than video)\n        if (i % 20 === 0) {\n            const audioData = new Uint8Array(200 + Math.random() * 100);\n            writer.writeAudioFrame(audioData, i * 33.33);\n        }\n    }\n    \n    const webmData = writer.finalize();\n    fs.writeFileSync('streaming-output.webm', webmData);\n    console.log('Streaming WebM created successfully!');\n}\n```\n\n## API Reference\n\n### WebMFile\n\nHigh-level interface for WebM operations.\n\n#### Static Methods\n\n- `WebMFile.fromBuffer(buffer: Uint8Array, module: LibWebMModule): Promise\u003cWebMFile\u003e`\n- `WebMFile.forWriting(module: LibWebMModule): WebMFile`\n\n#### Instance Methods\n\n**Reading (Parser mode):**\n- `getDuration(): number` - Get duration in seconds\n- `getTrackCount(): number` - Get number of tracks\n- `getTrackInfo(trackIndex: number): WebMTrackInfo` - Get track information\n\n**Writing (Muxer mode):**\n- `addVideoTrack(width: number, height: number, codecId: string): number`\n- `addAudioTrack(samplingFrequency: number, channels: number, codecId: string): number`\n- `writeVideoFrame(trackId: number, frameData: Uint8Array, timestampNs: number, isKeyframe: boolean): void`\n- `writeAudioFrame(trackId: number, frameData: Uint8Array, timestampNs: number): void`\n- `finalize(): Uint8Array` - Get final WebM data\n\n### WebMUtils\n\nUtility functions for common operations.\n\n- `isVideoCodecSupported(codecId: string): boolean`\n- `isAudioCodecSupported(codecId: string): boolean`\n- `nsToMs(ns: number): number` - Convert nanoseconds to milliseconds\n- `msToNs(ms: number): number` - Convert milliseconds to nanoseconds\n- `getSupportedVideoCodecs(): string[]`\n- `getSupportedAudioCodecs(): string[]`\n\n### Supported Codecs\n\n**Video:**\n- `V_VP8` - VP8 codec\n- `V_VP9` - VP9 codec  \n- `V_AV01` - AV1 codec\n\n**Audio:**\n- `A_OPUS` - Opus codec\n- `A_VORBIS` - Vorbis codec\n\n### Error Handling\n\nAll methods throw standard JavaScript Error objects with descriptive messages. Common error codes:\n\n- `INVALID_FILE` - File format not recognized\n- `CORRUPTED_DATA` - WebM data is corrupted\n- `UNSUPPORTED_FORMAT` - Codec or feature not supported\n- `IO_ERROR` - Input/output error\n- `INVALID_ARGUMENT` - Invalid parameter passed\n\n## Examples\n\nSee the `examples/` directory for complete usage examples:\n\n- `basic-usage.js` - Basic file creation and parsing\n- `advanced-parser.js` - Advanced parsing with frame extraction\n- `streaming-muxer.js` - Streaming WebM creation\n- `browser-example.html` - Browser usage example\n\n## Advanced Usage\n\n### Frame-by-Frame Processing\n\n```javascript\nconst libwebm = await createLibWebM();\nconst buffer = fs.readFileSync('input.webm');\nconst file = await libwebm.WebMFile.fromBuffer(buffer, libwebm._module);\n\n// Process all video frames\nconst trackInfo = file.getTrackInfo(0);\nif (trackInfo.trackType === libwebm.WebMTrackType.VIDEO) {\n    let frame;\n    while ((frame = file.parser.readNextVideoFrame(trackInfo.trackNumber)) !== null) {\n        console.log(`Frame: ${frame.data.length} bytes at ${WebMUtils.nsToMs(frame.timestampNs)}ms`);\n        // Process frame data...\n    }\n}\n```\n\n### Streaming WebM Creation\n\n```javascript\nconst libwebm = await createLibWebM();\nconst file = libwebm.WebMFile.forWriting(libwebm._module);\n\nconst videoTrack = file.addVideoTrack(1920, 1080, 'V_VP9');\n\n// Write frames as they become available\nfunction writeFrame(frameData, timestamp, isKeyframe) {\n    file.writeVideoFrame(videoTrack, frameData, WebMUtils.msToNs(timestamp), isKeyframe);\n    \n    // Get current data for streaming (before finalization)\n    const currentData = file.muxer.getData();\n    // Send currentData to client/server...\n}\n\n// When done\nconst finalData = file.finalize();\n```\n\n## Live Demo\n\n🚀 **Try libwebm-js in your browser!**\n\nA comprehensive interactive demo is available at: **[https://sctg-development.github.io/libwebm-js](https://sctg-development.github.io/libwebm-js)**\n\n### Demo Features\n\nThe live demo showcases all major libwebm-js capabilities with a modern, responsive web interface built using:\n\n- **React 19.1.1** - Latest React with modern hooks and concurrent features\n- **HeroUI 2.8.0** - Beautiful and accessible component library\n- **Tailwind CSS 4.1.12** - Utility-first CSS framework\n- **Vite 7.1.1** - Fast build tool and development server\n- **TypeScript** - Full type safety and excellent developer experience\n\n### Interactive Examples\n\n#### 📁 WebM File Parser\n- **File Upload**: Drag \u0026 drop or click to upload WebM files\n- **Real-time Parsing**: Instant analysis of file structure and metadata\n- **Format Validation**: Automatic detection of supported codecs and formats\n- **Error Handling**: Clear error messages for unsupported files\n\n#### 🎵 Track Information Display\n- **Detailed Metadata**: Complete track information including codec details\n- **Video Parameters**: Resolution, frame rate, and codec information\n- **Audio Parameters**: Sample rate, channels, and bit depth\n- **Multi-track Support**: Handle files with multiple video/audio tracks\n\n#### 🎬 Frame Extraction\n- **Frame-by-Frame Analysis**: Extract and examine individual frames\n- **Timing Information**: Precise timestamp data for each frame\n- **Keyframe Detection**: Identify keyframes for efficient seeking\n- **Performance Metrics**: Real-time extraction speed and memory usage\n\n#### 🎞️ WebM Muxer Demo\n- **Track Configuration**: Set up video and audio tracks with custom parameters\n- **Codec Selection**: Choose from supported VP8, VP9, AV1, Opus, and Vorbis\n- **Frame Writing**: Simulate writing frames with proper timing\n- **Output Generation**: Create WebM files with real muxing logic\n\n#### ⚡ Performance Testing\n- **Benchmark Suite**: Comprehensive performance tests\n- **Memory Monitoring**: Track memory usage during operations\n- **Concurrent Operations**: Test multi-threaded performance\n- **Detailed Reports**: Performance ratings and optimization suggestions\n\n### Demo Architecture\n\nThe demo is built with a modular component architecture:\n\n```\ndemo/src/\n├── components/\n│   ├── WebMFileParser.tsx     # File upload and parsing interface\n│   ├── TrackInfoDisplay.tsx   # Track metadata visualization\n│   ├── FrameExtractor.tsx     # Frame extraction controls\n│   ├── MuxerDemo.tsx         # WebM creation interface\n│   ├── PerformanceTester.tsx # Performance benchmarking\n│   └── index.ts              # Component exports\n├── App.tsx                   # Main application with tabbed interface\n├── main.tsx                  # Application entry point\n├── vite-env.d.ts            # TypeScript environment declarations\n└── index.css                # Tailwind CSS configuration\n```\n\n### Running the Demo Locally\n\n```bash\n# Clone the repository\ngit clone https://github.com/sctg-development/libwebm-js.git\ncd libwebm-js/demo\n\n# Install dependencies\nnpm install\n\n# Start the development server\nnpm run dev\n\n# Open http://localhost:5173 in your browser\n```\n\n### Demo vs Production Code\n\n**Note**: The demo currently uses simulated operations for demonstration purposes. The actual libwebm-js library provides:\n\n- Real WebM parsing and muxing capabilities\n- Full WASM-powered performance\n- Production-ready error handling\n- Complete TypeScript type definitions\n- Memory-efficient streaming operations\n\nThe demo serves as a comprehensive showcase of the API and user experience, while the production library in `dist/` contains the actual compiled WASM bindings.\n\n### Browser Compatibility\n\nThe demo works in all modern browsers that support:\n- WebAssembly (WASM)\n- ES2020 features\n- Modern JavaScript APIs\n\n**Supported Browsers:**\n- Chrome 57+\n- Firefox 52+\n- Safari 11+\n- Edge 16+\n\n## Building from Source\n\nThe build process:\n\n1. Clones Google's libwebm repository\n2. Compiles libwebm with Emscripten\n3. Compiles the C++ bindings (`src/libwebm-bindings.cpp`)\n4. Generates JavaScript/WASM files\n5. Creates the wrapper and TypeScript definitions\n\nBuild options can be configured in `CMakeLists.txt` and `build.sh`.\n\n## Performance\n\n- WASM compilation provides near-native performance\n- Memory usage is optimized for streaming operations\n- Large files can be processed with constant memory usage\n- Frame-by-frame processing avoids loading entire file into memory\n\n## License\n\nBSD 3-Clause License. See LICENSE file for details.\n\n## Contributing\n\n1. Fork the repository\n2. Create a feature branch\n3. Make your changes\n4. Add tests for new functionality\n5. Submit a pull request\n\n## See Also\n\n- [libwebm](https://chromium.googlesource.com/webm/libwebm) - Original C++ library\n- [LibWebMSwift](https://github.com/sctg-development/libwebm-swift) - Swift bindings for iOS/macOS\n- [WebM Project](https://www.webmproject.org/) - WebM format specification\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsctg-development%2Flibwebm-js","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsctg-development%2Flibwebm-js","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsctg-development%2Flibwebm-js/lists"}