### Install Dependencies Source: https://github.com/udevbe/ffmpeg-h264-wasm/blob/master/README.md Run this command to install project dependencies. ```bash yarn install ``` -------------------------------- ### Build Project Source: https://github.com/udevbe/ffmpeg-h264-wasm/blob/master/README.md Run this command to build the entire project, including the WASM module. ```bash yarn build ``` -------------------------------- ### Build WASM Module Source: https://github.com/udevbe/ffmpeg-h264-wasm/blob/master/README.md Execute this command to build the WebAssembly module for the H.264 decoder. ```bash yarn build-wasm ``` -------------------------------- ### Build WASM binary for H.264 decoding Source: https://context7.com/udevbe/ffmpeg-h264-wasm/llms.txt This bash script outlines the process for building the WASM binary using FFmpeg and Emscripten. It configures FFmpeg with only the H.264 decoder and links it into an ES6 module. Ensure prerequisites like git and Emscripten are available. ```bash # Prerequisites: git, emscripten (installed automatically by the script) yarn install yarn build-wasm # runs build_wasm.sh — clones ffmpeg + emsdk, compiles WASM yarn build # runs tsc — compiles TypeScript wrappers to dist/ # The script produces: src/libav-h264.js (WASM embedded as base64, ES6 module) # Key emcc flags used: # -s ENVIRONMENT='worker' → worker-only, no DOM APIs # -s MODULARIZE=1 → wrapped in a factory function # -s EXPORT_ES6=1 → ES module output # -s SINGLE_FILE=1 → WASM inlined as base64 (no separate .wasm file) # -s ALLOW_MEMORY_GROWTH=1 → heap can grow up to 128MB # -s INITIAL_MEMORY=32MB ``` -------------------------------- ### init() Source: https://context7.com/udevbe/ffmpeg-h264-wasm/llms.txt Initializes the WASM module within a Web Worker and sets up the message event listener. This function must be called once at the beginning of a worker script. It returns a Promise that resolves when the WASM module is ready, and the worker then posts a 'decoderReady' message to the main thread. ```APIDOC ## init() ### Description Bootstraps the WASM module inside a Web Worker and sets up the message event listener. Must be called once at the top of a worker script. Returns a `Promise` that resolves when the WASM module is ready; the worker posts a `decoderReady` message to the main thread upon completion. ### Usage ```js // H264NALDecoder.worker.js (this file IS the worker) import { init } from 'ffmpegh264' init() // When ready, worker posts: { type: 'decoderReady' } ``` ``` -------------------------------- ### new H264Decoder(libavH264Module, onPictureReady) Source: https://context7.com/udevbe/ffmpeg-h264-wasm/llms.txt Creates a new decoder instance, allocating the necessary codec context and Emscripten heap buffers for a video stream. The `onPictureReady` callback is invoked synchronously within `decode()` whenever a complete frame is decoded. Multiple independent decoders can be managed. ```APIDOC ## new H264Decoder(libavH264Module, onPictureReady) ### Description Allocates a codec context and all required Emscripten heap buffers for one video stream. Multiple independent decoders can coexist, keyed by a `renderStateId`. The `onPictureReady` callback fires synchronously inside `decode()` whenever a full frame is available. ### Parameters - **libavH264Module** (object) - The initialized Emscripten module for libavH264. - **onPictureReady** (function) - Callback function invoked when a frame is ready. It receives `(frame, width, height)` as arguments, where `frame` is an object containing decoded picture data and `width`, `height` are the frame dimensions. ### Usage ```ts import { H264Decoder } from 'ffmpegh264/src/H264Decoder' import LibavH264 from 'ffmpegh264/src/libav-h264' LibavH264().then((module) => { const decoder = new H264Decoder(module, (frame, width, height) => { // frame: { ptr, yPlane: Uint8Array, uPlane: Uint8Array, vPlane: Uint8Array, stride } console.log(`Decoded ${width}x${height} frame, stride=${frame.stride}`) // MUST release the frame when done to free WASM heap memory decoder.closeFrame(frame.ptr) }) // Feed a raw H.264 NAL unit const nalUnit = new Uint8Array(/* ... raw bytes ... */) decoder.decode(nalUnit) // When completely done with this stream decoder.release() }) ``` ``` -------------------------------- ### Initialize Decoder Worker (JavaScript) Source: https://context7.com/udevbe/ffmpeg-h264-wasm/llms.txt Bootstraps the WASM module inside a Web Worker and sets up the message event listener. Must be called once at the top of a worker script. The worker posts a 'decoderReady' message to the main thread upon completion. ```javascript // H264NALDecoder.worker.js (this file IS the worker) import { init } from 'ffmpegh264' init() // When ready, worker posts: { type: 'decoderReady' } ``` -------------------------------- ### Create and Use H264Decoder Instance (TypeScript) Source: https://context7.com/udevbe/ffmpeg-h264-wasm/llms.txt Allocates a codec context and heap buffers. The onPictureReady callback fires synchronously when a frame is available. Remember to release frames using closeFrame() to avoid memory leaks. ```typescript import { H264Decoder } from 'ffmpegh264/src/H264Decoder' import LibavH264 from 'ffmpegh264/src/libav-h264' LibavH264().then((module) => { const decoder = new H264Decoder(module, (frame, width, height) => { // frame: { ptr, yPlane: Uint8Array, uPlane: Uint8Array, vPlane: Uint8Array, stride } console.log(`Decoded ${width}x${height} frame, stride=${frame.stride}`) // MUST release the frame when done to free WASM heap memory decoder.closeFrame(frame.ptr) }) // Feed a raw H.264 NAL unit const nalUnit = new Uint8Array(/* ... raw bytes ... */) decoder.decode(nalUnit) // When completely done with this stream decoder.release() }) ``` -------------------------------- ### Main thread worker communication for H.264 decoding Source: https://context7.com/udevbe/ffmpeg-h264-wasm/llms.txt This code demonstrates the message protocol used between the main thread and the WASM worker. It shows how to send decode commands and handle decoder ready and picture ready events. Transferable buffers are used for zero-copy frame data. ```javascript // --- Main thread --- const worker = new Worker('./H264NALDecoder.worker.js') worker.addEventListener('message', (e) => { switch (e.data.type) { case 'decoderReady': console.log('WASM decoder is ready') break case 'pictureReady': { const { width, height, renderStateId, data } = e.data // data: { ptr, yPlane: Uint8Array, uPlane: Uint8Array, vPlane: Uint8Array, stride } renderFrame(data, width, height) // Release the frame in the worker after consuming it worker.postMessage({ type: 'closeFrame', renderStateId, data: data.ptr }) break } } }) // Send a NAL unit for decoding (transferable buffer = zero-copy) const nal = new Uint8Array(nalBuffer) worker.postMessage( { type: 'decode', data: nal.buffer, offset: nal.byteOffset, length: nal.byteLength, renderStateId: 1 }, [nal.buffer], // transfer ownership — do NOT use nal after this ) // Tear down stream worker.postMessage({ type: 'release', renderStateId: 1 }) ``` -------------------------------- ### WebGL YUV rendering pipeline Source: https://context7.com/udevbe/ffmpeg-h264-wasm/llms.txt This code sets up and uses a WebGL pipeline for displaying decoded I420 frames. It utilizes `YUVSurfaceShader` and `Texture` to render Y, U, and V planes, performing YUV to RGB conversion on the GPU. Ensure the canvas and WebGL context are properly initialized. ```javascript import YUVSurfaceShader from './YUVSurfaceShader' import Texture from './Texture' // Setup (once) const canvas = document.createElement('canvas') document.body.append(canvas) const gl = canvas.getContext('webgl') const shader = YUVSurfaceShader.create(gl) const yTex = Texture.create(gl, gl.LUMINANCE) const uTex = Texture.create(gl, gl.LUMINANCE) const vTex = Texture.create(gl, gl.LUMINANCE) // Per-frame render (called inside onPicture) function renderFrame(frame, width, height) { const { yPlane, uPlane, vPlane, stride } = frame const chromaHeight = height >> 1 canvas.width = width canvas.height = height // Upload I420 planes — stride may be larger than width (padding) yTex.image2dBuffer(yPlane, stride, height) uTex.image2dBuffer(uPlane, stride / 2, chromaHeight) vTex.image2dBuffer(vPlane, stride / 2, chromaHeight) // Crop out stride padding via tex-coord clamping const maxXTexCoord = width / stride const maxYTexCoord = height / height // 1.0 shader.setTexture(yTex, uTex, vTex) shader.updateShaderData({ w: width, h: height }, { maxXTexCoord, maxYTexCoord }) shader.draw() } ``` -------------------------------- ### decoder.decode(nal: Uint8Array) Source: https://context7.com/udevbe/ffmpeg-h264-wasm/llms.txt Decodes a single H.264 NAL unit. The provided byte array is copied to the WASM heap, the FFmpeg decoder is invoked, and pointers to the I420 planes are read back. If a complete picture is not yet available, the method returns without invoking the `onPictureReady` callback. Each call processes exactly one access unit. ```APIDOC ## decoder.decode(nal: Uint8Array) ### Description Copies the NAL byte array into the WASM heap, invokes the FFmpeg decoder, and reads back I420 plane pointers. If the frame is not yet complete (no picture output), the method returns early without calling `onPictureReady`. Each call processes exactly one access unit. ### Parameters - **nal** (Uint8Array) - A byte array containing the raw H.264 NAL unit data. ### Usage ```ts // Inside a worker, handling 'decode' messages from the main thread self.addEventListener('message', (e) => { const { type, data, offset, length, renderStateId } = e.data if (type === 'decode') { const nal = new Uint8Array(data, offset, length) decoder.decode(nal) // → triggers onPictureReady(frame, width, height) if picture is ready } }) ``` ``` -------------------------------- ### Handle Decode Messages in Worker (JavaScript) Source: https://context7.com/udevbe/ffmpeg-h264-wasm/llms.txt This code snippet shows how a Web Worker can handle 'decode' messages from the main thread, process NAL units using the decoder, and trigger the onPictureReady callback if a frame is decoded. ```javascript // Inside a worker, handling 'decode' messages from the main thread self.addEventListener('message', (e) => { const { type, data, offset, length, renderStateId } = e.data if (type === 'decode') { const nal = new Uint8Array(data, offset, length) decoder.decode(nal) // → triggers onPictureReady(frame, width, height) if picture is ready } }) ``` -------------------------------- ### decoder.release() Source: https://context7.com/udevbe/ffmpeg-h264-wasm/llms.txt Destroys the decoder instance, freeing the `AVCodecContext` and all associated Emscripten heap allocations. This method should be called when a video stream concludes or a rendering session is terminated to clean up resources. ```APIDOC ## decoder.release() ### Description Frees the `AVCodecContext` and all associated Emscripten heap allocations. Should be called when a video stream ends or a render session is torn down. ### Usage ```ts // Worker message handler for stream teardown case 'release': { const decoder = h264Decoders[renderStateId] if (decoder) { decoder.release() delete h264Decoders[renderStateId] } break } ``` ``` -------------------------------- ### Destroy Decoder Instance (JavaScript) Source: https://context7.com/udevbe/ffmpeg-h264-wasm/llms.txt Frees the AVCodecContext and associated heap allocations. Should be called when a video stream ends or a render session is torn down to clean up resources. ```javascript // Worker message handler for stream teardown case 'release': { const decoder = h264Decoders[renderStateId] if (decoder) { decoder.release() delete h264Decoders[renderStateId] } break } ``` -------------------------------- ### decoder.closeFrame(framePtr: number) Source: https://context7.com/udevbe/ffmpeg-h264-wasm/llms.txt Releases a decoded frame by calling `av_frame_unref` and `av_frame_free` on the underlying `AVFrame` through the WASM binding. This function must be called for every frame received in the `onPictureReady` callback after its pixel data has been consumed to prevent memory leaks in the WASM heap. ```APIDOC ## decoder.closeFrame(framePtr: number) ### Description Calls `av_frame_unref` + `av_frame_free` on the underlying `AVFrame` via the WASM binding. Must be called for every frame received in `onPictureReady` once the pixel data has been consumed (e.g., uploaded to a WebGL texture). Failing to call this leaks WASM heap memory. ### Parameters - **framePtr** (number) - The pointer to the `AVFrame` to be released, obtained from the `frame` object passed to `onPictureReady`. ### Usage ```ts function onPictureReady(frame, width, height) { const { ptr, yPlane, uPlane, vPlane, stride } = frame // Upload planes to WebGL textures yTexture.image2dBuffer(yPlane, stride, height) uTexture.image2dBuffer(uPlane, stride / 2, height >> 1) vTexture.image2dBuffer(vPlane, stride / 2, height >> 1) // Release the WASM-side AVFrame immediately after copying pixel data decoder.closeFrame(ptr) } ``` ``` -------------------------------- ### Release Decoded Frame (TypeScript) Source: https://context7.com/udevbe/ffmpeg-h264-wasm/llms.txt Frees the underlying AVFrame in the WASM heap after its pixel data has been consumed. Essential for preventing memory leaks; must be called for every frame received in onPictureReady. ```typescript function onPictureReady(frame, width, height) { const { ptr, yPlane, uPlane, vPlane, stride } = frame // Upload planes to WebGL textures yTexture.image2dBuffer(yPlane, stride, height) uTexture.image2dBuffer(uPlane, stride / 2, height >> 1) vTexture.image2dBuffer(vPlane, stride / 2, height >> 1) // Release the WASM-side AVFrame immediately after copying pixel data decoder.closeFrame(ptr) } ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.