之前我们学习了 FFmpeg 命令行工具,命令参数很多,不太容易记住。能不能用 UI 界面来调用FFmpeg 命令。答案是肯定的。本文将讲述FFmpeg 的 Node.js 实现 node-fluent-ffmpeg 。
介绍
node-fluent-ffmpeg 是一个 Node.js 库,用于在 Node.js 中调用 FFmpeg 命令行工具,以便在 Node.js 中处理视频和音频文件。它提供了一个简单的 API,可以轻松地调用 FFmpeg 命令,以便在 Node.js 中处理视频和音频文件。
本库将 ffmpeg 的复杂命令行用法抽象成一个流畅、易于使用的 node.js 模块。为了能够使用本模块,请确保您的系统上已安装 ffmpeg(包括所有必要的编码库,如 libmp3lame 或 libx264)。
用法
先决条件
ffmpeg 和 ffprobe
要使用 node-fluent-ffmpeg,您需要安装ffmpeg和ffprobe。Windows用户,请按照FFmpeg 简介下载 “Windows Builds”,将 D:\ffmpeg\bin
添加到系统变量 path
中。
安装依赖
要安装node-fluent-ffmpeg,请使用npm:
npm install fluent-ffmpeg
创建 FFmpeg 命令
要使用 node-fluent-ffmpeg,您需要创建一个新的 ffmpeg 实例:
const Ffmpeg = require('fluent-ffmpeg');
const ffmpeg = new Ffmpeg();
您可以使用此实例来执行ffmpeg命令:
ffmpeg.input('input.mp4')
.output('output.mp4')
.run();
您可以将输入文件名或可读流、配置对象或两者都传递给构造函数。
var command = ffmpeg('/path/to/file.avi');
var command = ffmpeg(fs.createReadStream('/path/to/file.avi'));
var command = ffmpeg({ 选项: "值", ... });
var command = ffmpeg('/path/to/file.avi', { 选项: "值", ... });
可用的选项有:
- source:输入文件名或可读流(如果传递给构造函数的是输入文件,则会被忽略)
- timeout:ffmpeg 超时时间(秒,默认无超时)
- preset 或 presets:加载模块预设的目录(默认为 fluent-ffmpeg 树中的 lib/presets 目录)
- niceness 或priority:ffmpeg niceness值,在-20和20之间;在Windows平台上被忽略(默认为0)
- logger:具有debug()、info()、warn()和error()方法的日志记录器对象(默认不记录)
- stdoutLines:要保留在内存中的 ffmpeg stdout/stderr 的最大行数(默认为100,使用0表示无限存储)
您可以向Ffmpeg命令添加任意数量的输入。输入可以是:
文件名(例如/path/to/file.avi);
图像模式(例如/path/to/frame%03d.png);
可读流;每个命令只能使用一个输入流,但您可以同时使用一个输入流和一个或多个文件名。
// Note that all fluent-ffmpeg methods are chainable
ffmpeg('/path/to/input1.avi')
.input('/path/to/input2.avi')
.input(fs.createReadStream('/path/to/input3.avi'));
// Passing an input to the constructor is the same as calling .input()
ffmpeg()
.input('/path/to/input1.avi')
.input('/path/to/input2.avi');
// Most methods have several aliases, here you may use addInput or mergeAdd instead
ffmpeg()
.addInput('/path/to/frame%02d.png')
.addInput('/path/to/soundtrack.mp3');
ffmpeg()
.mergeAdd('/path/to/input1.avi')
.mergeAdd('/path/to/input2.avi');
输入选项
node-fluent-ffmpeg 提供了一系列的输入选项,可以帮助开发者更好地控制 ffmpeg 的输入文件。
其中,最常用的输入选项包括:
-i:指定输入文件的路径;
-ss:指定输入文件的开始时间;
-t:指定输入文件的持续时间;
-r:指定输入文件的帧率;
-s:指定输入文件的分辨率;
-vcodec:指定输入文件的视频编码器;
-acodec:指定输入文件的音频编码器;
-f:指定输入文件的格式;
-vsync:指定输入文件的视频同步方式;
-async:指定输入文件的音频同步方式;
-threads:指定输入文件的处理线程数。
此外,node-fluent-ffmpeg 还提供了一些高级的输入选项,可以帮助开发者更好地控制 ffmpeg 的输入文件,比如:
-vf:指定输入文件的视频滤镜;
-af:指定输入文件的音频滤镜;
-map:指定输入文件的流映射;
-metadata:指定输入文件的元数据;
-seek_timestamp:指定输入文件的搜索时间戳;
-stream_loop:指定输入文件的流循环次数。
输出选项
输出选项主要包括:
- 输出格式:可以选择输出的格式,支持的格式有:MP4、FLV、MOV、AVI、WMV、MKV、WEBM、OGG、MP3、AAC等。
- 输出质量:可以选择输出的质量,支持的质量有:低、中、高、最高。
- 输出视频编码:可以选择输出的视频编码,支持的编码有:H.264、MPEG-4、VP8、VP9等。
- 输出音频编码:可以选择输出的音频编码,支持的编码有:AAC、MP3、Vorbis等。
- 输出视频分辨率:可以选择输出的视频分辨率,支持的分辨率有:1080p、720p、480p、360p等。
- 输出音频采样率:可以选择输出的音频采样率,支持的采样率有:44.1kHz、48kHz、96kHz等。
- 输出文件大小:可以选择输出的文件大小,支持的文件大小有:小、中、大、自定义等。
音频选项
音频选项可以用来控制输出文件的音频属性,包括音频编码器、采样率、声道数、比特率等。
音频编码器:可以使用audioCodec()
方法来指定音频编码器,支持的编码器有:aac、ac3、mp2、mp3、vorbis、wma等。
采样率:可以使用audioFrequency()
方法来指定音频的采样率,支持的采样率有:8000、11025、22050、44100、48000等。
声道数:可以使用audioChannels()
方法来指定音频的声道数,支持的声道数有:1、2、4、6、8等。
比特率:可以使用audioBitrate()
方法来指定音频的比特率,支持的比特率有:32k、64k、128k、192k、256k等。
视频选项
视频选项包括:
- 视频编码:可以使用 -c:v 参数来指定视频编码器,支持的编码器有 libx264、libx265、mpeg4、vp8、vp9 等。
- 视频分辨率:可以使用 -s 参数来指定视频的分辨率,支持的分辨率有 1080p、720p、480p 等。
- 视频帧率:可以使用 -r 参数来指定视频的帧率,支持的帧率有 24、25、30、60 等。
- 视频码率:可以使用 -b:v 参数来指定视频的码率,支持的码率有 1000k、2000k、3000k 等。
- 视频关键帧间隔:可以使用 -g 参数来指定视频的关键帧间隔,支持的关键帧间隔有 10、20、30 等。
- 视频比特率模式:可以使用 -maxrate 和 -minrate 参数来指定视频的比特率模式,支持的比特率模式有 cbr、vbr 等。
其他选项
使用预设
fluent-ffmpeg支持两种预设。第一种是预设模块;使用时,将预设名称作为预设参数传入。预设模块从由presets构造函数选项指定的目录(默认为fluent-ffmpeg的lib/presets子目录)加载。
// 使用<path-to-fluent-ffmpeg>/lib/presets/divx.js
ffmpeg('/path/to/file.avi').preset('divx');
// 使用/my/presets/foo.js
ffmpeg('/path/to/file.avi', { presets: '/my/presets' }).preset('foo');
预设模块必须导出一个接受FfmpegCommand作为参数的load()函数。fluent-ffmpeg预装了以下预设模块:
- divx
- flashvideo
- podcast
以下是包含的divx预设的代码示例:
exports.load = function(ffmpeg) {
ffmpeg
.format('avi')
.videoCodec('mpeg4')
.audioCodec('libmp3lame')
.videoBitrate('800k')
.audioBitrate('128k')
.size('640x?');
};
使用复杂滤镜
complexFilter()
方法用于为命令设置复杂的 filtergraph。它需要一个过滤器规范(或过滤器规范数组)和一个可选的输出映射参数作为参数。
过滤器规范可以是普通的 ffmpeg 过滤器字符串。(例如:Split =3[a][b][c]
)或具有以下键的对象:
filter
:过滤器名称options
:可选的;过滤器的一个选项字符串(例如:in:0:30
)中,一个选项数组,用于存放未命名选项(例如:['in', 0, 30]
)或一个将选项名称映射到值的对象(例如:{t: 'in', s: 0, n: 30}
)。当没有指定 options 时,过滤器将不带任何选项地添加。inputs
:可选的;过滤器的输入流说明符。这个值可以是一个流说明符字符串,也可以是一个流说明符数组。每个说明符都可以选择放在方括号中。当输入流未指定时,ffmpeg 将使用第一个未使用的正确类型的流。outputs
:可选;过滤器的输出流说明符。这个值可以是一个流说明符字符串,也可以是一个流说明符数组。每个说明符都可以选择放在方括号中。
您还可以添加转换,添加水印,添加视频和音频流,添加元数据等。
示例
下述示例可以将上传的视频装欢成 H264 编码的 Mp4文件,可以在主流PC 浏览器和移动设备上使用。
- 上传界面:
<head>
<title>MP4 转换</title>
<style>
.btn {
padding: 6px 20px;
}
input[type="file"] {
font-size: 16px;
}
</style>
</head>
<body>
<h1>MP4 转换</h1>
<form action="/convert" method="post" enctype="multipart/form-data">
<input type="file" name="file" accept="video/*" />
<p>
<button type="submit" class="btn">转换</button>
</p>
</form>
</body>
</html>
- Express 设置转换路由:
const express = require('express');
const app = express();
app.get('/', function (req, res) {
res.sendFile(path.join(__dirname, 'form.html'));
})
app.post('/convert', function (req, res) {
// ...
}
- multer 上传视频文件:
const upload = multer({
storage: multer.diskStorage({
destination: path.join('public', 'uploads'),
filename: function (req, file, cb) {
cb(null, file.originalname);
}
}),
fileFilter: (req, file, cb) => {
const ext = path.parse(file.originalname).ext;
if (!formats.includes(ext)) {
const error = customizeError(415, `不支持上传 ${ext} 文件`);
cb(error, false);
} else {
cb(null, true);
}
},
limits: {
fileSize
}
});
const folder = path.join('public', 'converts');
app.post('/info', upload.single('file'), function (req, res, next) {
const file = req.file;
if (!file) {
return next(customizeError(402, '文件不能为空'));
}
const filename = file.filename;
// set filePath to use in convert request
app.set('filePath', file.path);
// ...
})
- 读取视频元数据
您可以使用模块的 ffprobe
方法从任何有效的 ffmpeg
输入文件读取元数据。
ffmpeg(file.path).ffprobe(function (err, data) {
if (err) {
return next(err);
}
const { format, streams } = data;
const { format_name, duration, size, bit_rate } = format;
const video = streams.find((item) => item.codec_type === 'video');
// ...
})
它将包含有关容器(作为 format
键)和流数组(作为 streams
键)的信息。format 对象和每个 stream 对象也包含元数据标签,具体取决于格式。
警告:ffprobe可能会被输入流调用,但在这种情况下,它将使用流中的数据,而这些数据将不再对 ffmpeg 可用。在同一个输入流上同时使用 ffprobe 和 transcoding 命令很可能会失败,除非流是直播流。只有在你知道自己在做什么的情况下才会这么做。
- fluent-ffmpeg 转换视频:
const ffmpeg = require('fluent-ffmpeg');
ffmpeg(file.path)
.videoBitrate(512)
.videoCodec('libx264')
.audioBitrate('128k')
.audioCodec('libmp3lame')
.audioChannels(2)
.format('mp4')
.on('progress', function (progress) {
console.log('Processing: ' + Math.floor(progress.percent) + '% done');
})
.on('end', function () {
console.log('file has been converted succesfully');
fs.unlink(file.path, (error) => {
if (err) throw err;
})
ffmpeg(output)
.screenshots({
count: 1,
filename: `%b.png`,
folder
})
.on('end', function () {
console.log('screenshots has been generated succesfully');
res.json({ message: '转换成功' });
})
.on('error', function (err) {
console.log('an error happened: ' + err.message);
res.json({ message: err.message });
})
})
.on('error', function (err) {
console.log('an error happened: ' + err.message);
})
.save(output);
})
通过 fluent-ffmpeg 设置视频的输入输出选项、音频视频比特率、编码。这个转换过程是异步的。我们还监听了转换命令的 progress
、end
、error
事件。当视频转换完成时,会在end 处理函数中发起生成缩略图的命令。直到生成完成时,整个转换才结束。
Processing: 21% done
Processing: 44% done
Processing: 62% done
Processing: 85% done
Processing: 99% done
file has been converted succesfully
screenshots has been generated succesfully
参考示例
- fluent-ffmpeg
- 访问 codesandbox 查看完整示例代码。
评论 (0)