--- name: ffmpeg-webassembly-workers description: Complete browser-based FFmpeg system. PROACTIVELY activate for: (1) ffmpeg.wasm setup and loading, (2) Browser video transcoding, (3) React/Vue/Next.js integration, (4) SharedArrayBuffer and COOP/COEP headers, (5) Multi-threaded ffmpeg-core-mt, (6) Cloudflare Workers limitations, (7) Custom ffmpeg.wasm builds, (8) Memory management and cleanup, (9) Progress tracking and UI, (10) IndexedDB core caching. Provides: Framework-specific examples, header configuration, common operation recipes, performance optimization, troubleshooting guides. Ensures: Client-side video processing without server dependencies. --- ## CRITICAL GUIDELINES ### Windows File Path Requirements **MANDATORY: Always Use Backslashes on Windows for File Paths** When using Edit or Write tools on Windows, you MUST use backslashes (`\`) in file paths, NOT forward slashes (`/`). --- ## Quick Reference | Package | Size | Threading | Install | |---------|------|-----------|---------| | `@ffmpeg/core` | ~31MB | Single | `npm install @ffmpeg/ffmpeg @ffmpeg/util` | | `@ffmpeg/core-mt` | ~31MB | Multi | Requires COOP/COEP headers | | Header | Value | Purpose | |--------|-------|---------| | Cross-Origin-Embedder-Policy | `require-corp` | SharedArrayBuffer | | Cross-Origin-Opener-Policy | `same-origin` | Multi-threading | | Operation | Command | |-----------|---------| | Convert WebM→MP4 | `await ffmpeg.exec(['-i', 'input.webm', 'output.mp4'])` | | Extract frame | `await ffmpeg.exec(['-i', 'video.mp4', '-ss', '5', '-vframes', '1', 'thumb.jpg'])` | ## When to Use This Skill Use for **browser-based video processing**: - Client-side transcoding without server - React/Vue/Next.js FFmpeg integration - Setting up COOP/COEP headers - Cloudflare Workers FFmpeg limitations - Memory management and cleanup --- # FFmpeg WebAssembly & Cloudflare Workers (2025) Guide to running FFmpeg in browsers and edge environments using WebAssembly. ## ffmpeg.wasm Overview **ffmpeg.wasm** is a pure WebAssembly/JavaScript port of FFmpeg that runs directly in browsers without server-side processing. ### Key Features - **Browser-based**: No server required for video processing - **Cross-platform**: Works on any modern browser - **Single-thread**: @ffmpeg/core (~31MB) - **Multi-thread**: @ffmpeg/core-mt (requires SharedArrayBuffer) - **Customizable**: Build your own core with specific codecs ### Limitations - **Performance**: ~10-100x slower than native FFmpeg - **Memory**: Limited by browser memory constraints - **File size**: Core is ~31MB (can be reduced with custom builds) - **Threading**: Multi-thread requires specific headers (COOP/COEP) - **Codecs**: Not all codecs available (licensing restrictions) ## Installation ### npm ```bash npm install @ffmpeg/ffmpeg @ffmpeg/util ``` ### CDN ```html ``` ## Basic Usage ### Single-Thread (Browser) ```javascript import { FFmpeg } from '@ffmpeg/ffmpeg'; import { fetchFile, toBlobURL } from '@ffmpeg/util'; const ffmpeg = new FFmpeg(); // Load FFmpeg core const baseURL = 'https://cdn.jsdelivr.net/npm/@ffmpeg/core@0.12.10/dist/umd'; await ffmpeg.load({ coreURL: await toBlobURL(`${baseURL}/ffmpeg-core.js`, 'text/javascript'), wasmURL: await toBlobURL(`${baseURL}/ffmpeg-core.wasm`, 'application/wasm'), }); // Transcode video await ffmpeg.writeFile('input.webm', await fetchFile(videoFile)); await ffmpeg.exec(['-i', 'input.webm', 'output.mp4']); const data = await ffmpeg.readFile('output.mp4'); // Create blob URL for playback const videoURL = URL.createObjectURL( new Blob([data.buffer], { type: 'video/mp4' }) ); ``` ### Multi-Thread (Requires COOP/COEP Headers) ```javascript import { FFmpeg } from '@ffmpeg/ffmpeg'; import { fetchFile, toBlobURL } from '@ffmpeg/util'; const ffmpeg = new FFmpeg(); // Load multi-threaded core const baseURL = 'https://cdn.jsdelivr.net/npm/@ffmpeg/core-mt@0.12.10/dist/umd'; await ffmpeg.load({ coreURL: await toBlobURL(`${baseURL}/ffmpeg-core.js`, 'text/javascript'), wasmURL: await toBlobURL(`${baseURL}/ffmpeg-core.wasm`, 'application/wasm'), workerURL: await toBlobURL(`${baseURL}/ffmpeg-core.worker.js`, 'text/javascript'), }); // Use multi-threaded encoding await ffmpeg.exec(['-i', 'input.webm', '-threads', '4', 'output.mp4']); ``` ## Cross-Origin Isolation (SharedArrayBuffer) Multi-threaded ffmpeg.wasm requires SharedArrayBuffer, which needs Cross-Origin Isolation headers. ### Vite Configuration ```javascript // vite.config.js export default { server: { headers: { "Cross-Origin-Embedder-Policy": "require-corp", "Cross-Origin-Opener-Policy": "same-origin", }, }, optimizeDeps: { exclude: ["@ffmpeg/ffmpeg", "@ffmpeg/util"], }, }; ``` ### Next.js Configuration ```javascript // next.config.js module.exports = { async headers() { return [ { source: "/:path*", headers: [ { key: "Cross-Origin-Embedder-Policy", value: "require-corp" }, { key: "Cross-Origin-Opener-Policy", value: "same-origin" }, ], }, ]; }, }; ``` ### Express.js Middleware ```javascript import express from 'express'; const app = express(); app.use((req, res, next) => { res.setHeader("Cross-Origin-Embedder-Policy", "require-corp"); res.setHeader("Cross-Origin-Opener-Policy", "same-origin"); res.setHeader("Cross-Origin-Resource-Policy", "cross-origin"); next(); }); app.use(express.static('public')); app.listen(3000); ``` ### Nginx Configuration ```nginx server { listen 443 ssl; add_header Cross-Origin-Embedder-Policy "require-corp" always; add_header Cross-Origin-Opener-Policy "same-origin" always; location / { root /var/www/html; } } ``` ## React Integration ### React Component ```jsx import { useState, useRef } from 'react'; import { FFmpeg } from '@ffmpeg/ffmpeg'; import { fetchFile, toBlobURL } from '@ffmpeg/util'; function VideoTranscoder() { const [loaded, setLoaded] = useState(false); const [progress, setProgress] = useState(0); const [outputURL, setOutputURL] = useState(null); const ffmpegRef = useRef(new FFmpeg()); const load = async () => { const ffmpeg = ffmpegRef.current; // Progress handler ffmpeg.on('progress', ({ progress }) => { setProgress(Math.round(progress * 100)); }); const baseURL = 'https://cdn.jsdelivr.net/npm/@ffmpeg/core@0.12.10/dist/umd'; await ffmpeg.load({ coreURL: await toBlobURL(`${baseURL}/ffmpeg-core.js`, 'text/javascript'), wasmURL: await toBlobURL(`${baseURL}/ffmpeg-core.wasm`, 'application/wasm'), }); setLoaded(true); }; const transcode = async (file) => { const ffmpeg = ffmpegRef.current; await ffmpeg.writeFile('input.webm', await fetchFile(file)); await ffmpeg.exec([ '-i', 'input.webm', '-c:v', 'libx264', '-preset', 'ultrafast', '-c:a', 'aac', 'output.mp4' ]); const data = await ffmpeg.readFile('output.mp4'); const url = URL.createObjectURL( new Blob([data.buffer], { type: 'video/mp4' }) ); setOutputURL(url); }; return (
Progress: {progress}%
{outputURL && } > )}