Eloquent FFmpeg simplifies interactions with
FFmpeg's command line tools into a simple yet powerful API.
This library is fully typed, so in editors such as VS Code, intellisense should help you get started.
You may also want to view the API documentation
or examples/
.
If something is missing or doesn't feel right, feel free to open an issue or a pull request to change it. This library is still in a very early stage, but there shouldn't be any major breaking changes.
Only NodeJS 10.x or higher is supported
Eloquent FFmpeg requires a recent version of FFmpeg to be installed. Make sure that ffmpeg and
ffprobe executables are in PATH
, or use the options ffmpegPath
and ffprobePath
.
GitHub Actions
To install FFmpeg on a GitHub Actions' runner use FedericoCarboni/setup-ffmpeg.
Since most of Eloquent FFmpeg's methods are asynchronous it is advised to use
async-await
to improve readability.
A simple example could use the following:
// Create a new command
const cmd = ffmpeg({
// Include any options here...
});
// Select input(s)
cmd.input('input.mkv');
// ... and output(s)
cmd.output('output.mp4');
// Spawn ffmpeg as a child process
const proc = await cmd.spawn();
// Wait for the conversion to complete
await proc.complete();
Streams can be used as input sources and output destinations, there is no hard
limit on how many streams can be used. Pass Node.js streams directly to
FFmpegCommand.input()
and FFmpegCommand.output()
.
Example using Node.js' fs
module.
const cmd = ffmpeg();
cmd.input(fs.createReadStream('input.mkv'));
// The same output will be written to two destinations.
cmd.output(fs.createWriteStream('dest1.webm'), 'dest2.webm')
// When using streams the format must be explicitly specified
// because it can't be inferred from the file extension.
.format('webm');
const proc = await cmd.spawn();
await proc.complete();
Note: Some formats require inputs and/or outputs to be seekable which means
that they cannot be used with streams, notable example being MP4. Some other
formats require a special format name to be explicitly set, for example to use
streams for GIF files the input format must be gif_pipe
.
See FFmpegCommand.input() and FFmpegCommand.output()
To concatenate inputs use FFmpegCommand.concat()
, at the moment it is still unstable.
const cmd = ffmpeg();
cmd.concat(['file:input1.mkv', 'file:input2.mkv']);
cmd.output('output.mkv');
const proc = await cmd.spawn();
await proc.complete();
Note: When passing inputs to FFmpegCommand.concat()
the protocol must be explicitly specified:
file:
for example; streams are handled automatically. Sometimes it may be necessary to explicitly
enable certain protocols.
const cmd = ffmpeg();
cmd.concat(['file:input1.mkv', 'https://example.com/input2.mkv'], {
protocols: ['file', 'tcp', 'tls', 'http', 'https'],
});
cmd.output('output.mkv');
const proc = await cmd.spawn();
await proc.complete();
See FFmpegCommand.concat() and ConcatOptions.
Eloquent FFmpeg exposes a few methods which act as a shortcut to set a few options. See FFmpegInput and FFmpegOutput
const cmd = ffmpeg();
cmd.input('input.mp4')
.format('mp4');
cmd.output('output.mkv')
.audioCodec('aac');
To set input and output options their .args()
method can also be used.
const cmd = ffmpeg();
cmd.input('input.mp4')
.args('-format', 'mp4');
cmd.output('output.mkv')
.args('-codec:a', 'aac');
For debbugging, FFmpegCommand.spawn()
's options accept logger
and report
.
The report
option dumps the full command line arguments and logs to the specified file or, when
not specified, FFmpeg will create a file named ffmpeg-YYYYMMDD-HHMMSS.log
in its current
directory. When the log level is not specified FFmpeg defaults to LogLevel.Debug
.
See -loglevel
and -report
in FFmpeg's docs.
const cmd = ffmpeg({
level: LogLevel.Warning,
});
cmd.input('input.mkv');
cmd.output('output.mp4');
const proc = await cmd.spawn({
// Enable logger and report when not in production mode.
logger: process.env.NODE_ENV !== 'production' && {
warning: (message) => {
console.warn('FFmpeg warning:', message);
},
},
report: process.env.NODE_ENV !== 'production' && {
file: 'ffmpeg-123.log',
level: LogLevel.Debug,
},
});
await proc.complete();
Make sure to check the API documentation for FFmpegProcess.
To receive real-time updates on the conversion's progress, use the FFmpegProcess.progress()
method.
It returns an async generator of Progress.
const cmd = ffmpeg();
cmd.input('input.mkv');
cmd.output('output.mp4');
const proc = await cmd.spawn();
for await (const { speed, time } of proc.progress()) {
console.log(`Converting @ ${speed}x – ${time}/${TOTAL_TIME}`);
}
// NOTE: The progress generator will return when ffmpeg writes a
// `progress=end` line, which signals the end of progress updates,
// not the conversion's completion.
// Use proc.complete() to wait for completion.
await proc.complete();
console.log('Hooray! Conversion complete!');
To use Node.js' streams, FFmpegProcess.progress()
can be turned into a Node.js readable stream
using Readable.from()
.
const cmd = ffmpeg();
cmd.input('input.mkv');
cmd.output('output.mp4');
const proc = await cmd.spawn();
const progress = Readable.from(proc.progress());
progress.on('data', ({ speed, time }) => {
console.log(`Converting @ ${speed}x – ${time}/${TOTAL_TIME}`);
});
progress.on('end', () => {
// NOTE: The progress stream will end when ffmpeg writes a
// `progress=end` line, which signals the end of progress
// updates, not the conversion's completion.
console.log('No more progress updates');
});
// Use proc.complete() to wait for completion.
await proc.complete();
console.log('Hooray! Conversion complete!');
Tracking progress as a percentage: To get a percentage from the progress the total duration of the media must be known, this is very easy if the duration is not modified.
Probe the input file and calculate the percentage by dividing the current time
by the duration
and multiplying by 100.
const cmd = ffmpeg();
const input = cmd.input('input.mkv');
const info = await input.probe();
cmd.output('video.mp4');
const proc = await cmd.spawn();
for await (const { speed, time } of proc.progress()) {
console.log(`Converting @ ${speed}x – ${time / info.duration * 100}%`);
}
await proc.complete();
console.log('Hooray! Conversion complete!');
The conversion can be paused and resumed using FFmpegProcess.pause()
and FFmpegProcess.resume()
. Both methods are synchronous, they return true
upon success, false
otherwise.
Note: On Windows this requires the optional dependency ntsuspend to be installed.
const cmd = ffmpeg();
cmd.input('input.mkv');
cmd.output('output.mp4');
const proc = await cmd.spawn();
// Pause the conversion
proc.pause();
// Resume...
proc.resume();
await proc.complete();
The conversion can be terminated early using FFmpegProcess.abort()
, this
gracefully interrupts the conversion allowing FFmpeg to end the file correctly.
The method is asynchronous.
Note: abort()
resolves when FFmpeg exits, but it doesn't guarantee that it
will exit successfully, any possible errors should be handled explicitly.
const cmd = ffmpeg();
cmd.input('input.mkv');
cmd.output('output.mp4');
const proc = await cmd.spawn();
await proc.abort();
FFmpeg exited with a non-zero status code, which means that the conversion failed. This typically occurs because of a corrupt input or a wrong configuration. See Logging and Debugging.
FFmpeg exited without a status code. This typically means that the conversion was forcefully terminated (or that an error occurred in a system call). See Logging and Debugging.
This error is likely caused by a corrupt or missing installation of ntsuspend.
ntsuspend
is required to pause and resume the FFmpeg child process on Windows.
Try to uninstall and reinstall ntsuspend
, and if you experience further
problems open a new issue to get help.