Usage
Learn the basics of using ffmpeg.wasm.
note
It is recommended to read Overview first.
Transcode webm to mp4 video
caution
If you are a vite user, use esm
in baseURL instead of umd
:
https://unpkg.com/@ffmpeg/core@0.12.7/dist/umd => https://unpkg.com/@ffmpeg/core@0.12.7/dist/esm
Live Editor
// import { FFmpeg } from '@ffmpeg/ffmpeg';// import { fetchFile, toBlobURL } from '@ffmpeg/util';function() {const [loaded, setLoaded] = useState(false);const ffmpegRef = useRef(new FFmpeg());const videoRef = useRef(null);const messageRef = useRef(null);const load = async () => {const baseURL = 'https://unpkg.com/@ffmpeg/core@0.12.7/dist/umd'const ffmpeg = ffmpegRef.current;ffmpeg.on('log', ({ message }) => {messageRef.current.innerHTML = message;console.log(message);});// toBlobURL is used to bypass CORS issue, urls with the same// domain can be used directly.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 () => {const ffmpeg = ffmpegRef.current;await ffmpeg.writeFile('input.webm', await fetchFile('https://raw.githubusercontent.com/ffmpegwasm/testdata/master/Big_Buck_Bunny_180_10s.webm'));await ffmpeg.exec(['-i', 'input.webm', 'output.mp4']);const data = await ffmpeg.readFile('output.mp4');videoRef.current.src =URL.createObjectURL(new Blob([data.buffer], {type: 'video/mp4'}));}return (loaded? (<><video ref={videoRef} controls></video><br/><button onClick={transcode}>Transcode webm to mp4</button><p ref={messageRef}></p><p>Open Developer Tools (Ctrl+Shift+I) to View Logs</p></>): (<button onClick={load}>Load ffmpeg-core (~31 MB)</button>));}
Result
Loading...
Transcode webm to mp4 video (multi-thread)
caution
As SharedArrayBuffer is required for multithread version, make sure you have have fulfilled Security Requirements.
Live Editor
// import { FFmpeg } from '@ffmpeg/ffmpeg';// import { fetchFile, toBlobURL } from '@ffmpeg/util';function() {const [loaded, setLoaded] = useState(false);const ffmpegRef = useRef(new FFmpeg());const videoRef = useRef(null);const messageRef = useRef(null);const load = async () => {const baseURL = 'https://unpkg.com/@ffmpeg/core-mt@0.12.7/dist/umd'const ffmpeg = ffmpegRef.current;ffmpeg.on('log', ({ message }) => {messageRef.current.innerHTML = message;console.log(message);});// toBlobURL is used to bypass CORS issue, urls with the same// domain can be used directly.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'),});setLoaded(true);}const transcode = async () => {const ffmpeg = ffmpegRef.current;await ffmpeg.writeFile('input.webm', await fetchFile('https://raw.githubusercontent.com/ffmpegwasm/testdata/master/Big_Buck_Bunny_180_10s.webm'));await ffmpeg.exec(['-i', 'input.webm', 'output.mp4']);const data = await ffmpeg.readFile('output.mp4');videoRef.current.src =URL.createObjectURL(new Blob([data.buffer], {type: 'video/mp4'}));}return (loaded? (<><video ref={videoRef} controls></video><br/><button onClick={transcode}>Transcode webm to mp4</button><p ref={messageRef}></p><p>Open Developer Tools (Ctrl+Shift+I) to View Logs</p></>): (<button onClick={load}>Load ffmpeg-core (~31 MB)</button>));}
Result
Loading...
Transcode video with timeout
Live Editor
// import { FFmpeg } from '@ffmpeg/ffmpeg';// import { fetchFile } from '@ffmpeg/util';function() {const [loaded, setLoaded] = useState(false);const ffmpegRef = useRef(new FFmpeg());const videoRef = useRef(null);const messageRef = useRef(null);const load = async () => {const baseURL = 'https://unpkg.com/@ffmpeg/core@0.12.7/dist/umd'const ffmpeg = ffmpegRef.current;ffmpeg.on('log', ({ message }) => {messageRef.current.innerHTML = message;console.log(message);});// toBlobURL is used to bypass CORS issue, urls with the same// domain can be used directly.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 () => {const ffmpeg = ffmpegRef.current;await ffmpeg.writeFile('input.webm', await fetchFile('https://raw.githubusercontent.com/ffmpegwasm/testdata/master/Big_Buck_Bunny_180_10s.webm'));// The exec should stop after 1 second.await ffmpeg.exec(['-i', 'input.webm', 'output.mp4'], 1000);const data = await ffmpeg.readFile('output.mp4');videoRef.current.src =URL.createObjectURL(new Blob([data.buffer], {type: 'video/mp4'}));}return (loaded? (<><video ref={videoRef} controls></video><br/><button onClick={transcode}>Transcode webm to mp4</button><p ref={messageRef}></p><p>Open Developer Tools (Ctrl+Shift+I) to View Logs</p></>): (<button onClick={load}>Load ffmpeg-core (~31 MB)</button>));}
Result
Loading...
Transcode video with progress (experimental)
danger
progress
is an experimental feature and might not work for many cases
(ex. concat video files, convert image files, ...). Please use with caution.
Live Editor
// import { FFmpeg } from '@ffmpeg/ffmpeg';// import { fetchFile } from '@ffmpeg/util';function() {const [loaded, setLoaded] = useState(false);const ffmpegRef = useRef(new FFmpeg());const videoRef = useRef(null);const messageRef = useRef(null);const load = async () => {const baseURL = 'https://unpkg.com/@ffmpeg/core@0.12.7/dist/umd'const ffmpeg = ffmpegRef.current;// Listen to progress event instead of log.ffmpeg.on('progress', ({ progress, time }) => {messageRef.current.innerHTML = `${progress * 100} % (transcoded time: ${time / 1000000} s)`;});// toBlobURL is used to bypass CORS issue, urls with the same// domain can be used directly.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 () => {const ffmpeg = ffmpegRef.current;await ffmpeg.writeFile('input.webm', await fetchFile('https://raw.githubusercontent.com/ffmpegwasm/testdata/master/Big_Buck_Bunny_180_10s.webm'));await ffmpeg.exec(['-i', 'input.webm', 'output.mp4']);const data = await ffmpeg.readFile('output.mp4');videoRef.current.src =URL.createObjectURL(new Blob([data.buffer], {type: 'video/mp4'}));}return (loaded? (<><video ref={videoRef} controls></video><br/><button onClick={transcode}>Transcode webm to mp4</button><p ref={messageRef}></p></>): (<button onClick={load}>Load ffmpeg-core (~31 MB)</button>));}
Result
Loading...
Split video into segments of equal duration
Live Editor
// import { FFmpeg } from '@ffmpeg/ffmpeg';// import { fetchFile } from '@ffmpeg/util';function() {const [loaded, setLoaded] = useState(false);const ffmpegRef = useRef(new FFmpeg());const videoRef = useRef(null);const messageRef = useRef(null);const load = async () => {const baseURL = 'https://unpkg.com/@ffmpeg/core@0.12.7/dist/umd'const ffmpeg = ffmpegRef.current;ffmpeg.on('log', ({ message }) => {messageRef.current.innerHTML = message;console.log(message);});// toBlobURL is used to bypass CORS issue, urls with the same// domain can be used directly.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 () => {const ffmpeg = ffmpegRef.current;await ffmpeg.writeFile('input.webm', await fetchFile('https://raw.githubusercontent.com/ffmpegwasm/testdata/master/Big_Buck_Bunny_180_10s.webm'));await ffmpeg.exec(['-i','input.webm','-f','segment','-segment_time','3','-g','9','-sc_threshold','0','-force_key_frames','expr:gte(t,n_forced*9)','-reset_timestamps','1','-map','0','output_%d.mp4']);const data = await ffmpeg.readFile('output_1.mp4');videoRef.current.src =URL.createObjectURL(new Blob([data.buffer], {type: 'video/mp4'}));}return (loaded? (<><video ref={videoRef} controls></video><br/><button onClick={transcode}>Split video to segments of 3 sec. and plays 2nd segment</button><p ref={messageRef}></p><p>Open Developer Tools (Ctrl+Shift+I) to View Logs</p></>): (<button onClick={load}>Load ffmpeg-core (~31 MB)</button>));}
Result
Loading...
Display Text on the video
Live Editor
// import { FFmpeg } from '@ffmpeg/ffmpeg';// import { fetchFile } from '@ffmpeg/util';function() {const [loaded, setLoaded] = useState(false);const ffmpegRef = useRef(new FFmpeg());const videoRef = useRef(null);const messageRef = useRef(null);const load = async () => {const baseURL = 'https://unpkg.com/@ffmpeg/core@0.12.7/dist/umd'const ffmpeg = ffmpegRef.current;ffmpeg.on('log', ({ message }) => {messageRef.current.innerHTML = message;console.log(message);});// toBlobURL is used to bypass CORS issue, urls with the same// domain can be used directly.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 () => {const ffmpeg = ffmpegRef.current;await ffmpeg.writeFile('input.webm', await fetchFile('https://raw.githubusercontent.com/ffmpegwasm/testdata/master/Big_Buck_Bunny_180_10s.webm'));await ffmpeg.writeFile('arial.ttf', await fetchFile('https://raw.githubusercontent.com/ffmpegwasm/testdata/master/arial.ttf'));await ffmpeg.exec(['-i','input.webm','-vf','drawtext=fontfile=/arial.ttf:text=\'ffmpeg.wasm\':x=10:y=10:fontsize=24:fontcolor=white','output.mp4',]);const data = await ffmpeg.readFile('output.mp4');videoRef.current.src =URL.createObjectURL(new Blob([data.buffer], {type: 'video/mp4'}));}return (loaded? (<><video ref={videoRef} controls></video><br/><button onClick={transcode}>Transcode webm to mp4 with text</button><p ref={messageRef}></p><p>Open Developer Tools (Ctrl+Shift+I) to View Logs</p></>): (<button onClick={load}>Load ffmpeg-core (~31 MB)</button>));}
Result
Loading...
Interlace 2 Videos
Live Editor
// import { FFmpeg } from '@ffmpeg/ffmpeg';// import { fetchFile } from '@ffmpeg/util';function() {const [loaded, setLoaded] = useState(false);const ffmpegRef = useRef(new FFmpeg());const videoRef = useRef(null);const messageRef = useRef(null);const load = async () => {const baseURL = 'https://unpkg.com/@ffmpeg/core@0.12.7/dist/umd'const ffmpeg = ffmpegRef.current;ffmpeg.on('log', ({ message }) => {messageRef.current.innerHTML = message;console.log(message);});// toBlobURL is used to bypass CORS issue, urls with the same// domain can be used directly.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 () => {const ffmpeg = ffmpegRef.current;await ffmpeg.writeFile('input.webm', await fetchFile('https://raw.githubusercontent.com/ffmpegwasm/testdata/master/Big_Buck_Bunny_180_10s.webm'));await ffmpeg.writeFile('reversed.webm', await fetchFile('https://raw.githubusercontent.com/ffmpegwasm/testdata/master/Big_Buck_Bunny_180_10s_reversed.webm'));await ffmpeg.exec(['-i','input.webm','-i','reversed.webm','-filter_complex','[0:v][1:v]blend=all_expr=\'A*(if(eq(0,N/2),1,T))+B*(if(eq(0,N/2),T,1))\'','output.mp4',]);const data = await ffmpeg.readFile('output.mp4');videoRef.current.src =URL.createObjectURL(new Blob([data.buffer], {type: 'video/mp4'}));}return (loaded? (<><video ref={videoRef} controls></video><br/><button onClick={transcode}>Interlace two webm video to mp4</button><p ref={messageRef}></p><p>Open Developer Tools (Ctrl+Shift+I) to View Logs</p></>): (<button onClick={load}>Load ffmpeg-core (~31 MB)</button>));}
Result
Loading...
Use WORKERFS
note
Required:
- @ffmpeg/ffmpeg@0.12.7+
- @ffmpeg/core@0.12.4+ :::
Please Check this PR: Add WORKERFS support
Abort exec() with signal
Required:
- @ffmpeg/ffmpeg@0.12.7+
- @ffmpeg/core@0.12.4+ :::
Please check this PR: abort signal