konowebcodecs/apps/website/docs/getting-started/usage.md
2024-12-23 21:21:19 -08:00

445 lines
15 KiB
Markdown

# Usage
Learn the basics of using ffmpeg.wasm.
:::note
It is recommended to read [Overview](/docs/overview) first.
:::
## Transcode webm to mp4 video
:::caution
If you are a [vite](https://vitejs.dev/) 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
:::
```jsx live
// 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>
)
);
}
```
## Transcode webm to mp4 video (multi-thread)
:::caution
As SharedArrayBuffer is required for multithread version, make sure
you have have fulfilled [Security Requirements](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/SharedArrayBuffer#security_requirements).
:::
```jsx live
// 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>
)
);
}
```
## Transcode video with timeout
```jsx live
// 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>
)
);
}
```
## 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.
:::
```jsx live
// 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>
)
);
}
```
## Split video into segments of equal duration
```jsx live
// 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>
)
);
}
```
## Display Text on the video
```jsx live
// 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>
)
);
}
```
## Interlace 2 Videos
```jsx live
// 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>
)
);
}
```
## Use WORKERFS
:::note
Required:
- @ffmpeg/ffmpeg@0.12.7+
- @ffmpeg/core@0.12.4+
:::
Please Check this PR: [Add WORKERFS support](https://github.com/ffmpegwasm/ffmpeg.wasm/pull/581)
## Abort exec() with signal
:::note
Required:
- @ffmpeg/ffmpeg@0.12.7+
- @ffmpeg/core@0.12.4+
:::
Please check this PR: [abort signal](https://github.com/ffmpegwasm/ffmpeg.wasm/pull/573)