Node Mp4 转换器

Flying
2019-09-08 / 0 评论 / 133 阅读 / 正在检测是否收录...

之前我们学习了 FFmpeg 命令行工具,命令参数很多,不太容易记住。能不能用 UI 界面来调用FFmpeg 命令。答案是肯定的。本文将讲述FFmpeg 的 Node.js 实现 node-fluent-ffmpeg

converter-ui.jpg

介绍

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:指定输入文件的流循环次数。

输出选项

输出选项主要包括:

  1. 输出格式:可以选择输出的格式,支持的格式有:MP4、FLV、MOV、AVI、WMV、MKV、WEBM、OGG、MP3、AAC等。
  2. 输出质量:可以选择输出的质量,支持的质量有:低、中、高、最高。
  3. 输出视频编码:可以选择输出的视频编码,支持的编码有:H.264、MPEG-4、VP8、VP9等。
  4. 输出音频编码:可以选择输出的音频编码,支持的编码有:AAC、MP3、Vorbis等。
  5. 输出视频分辨率:可以选择输出的视频分辨率,支持的分辨率有:1080p、720p、480p、360p等。
  6. 输出音频采样率:可以选择输出的音频采样率,支持的采样率有:44.1kHz、48kHz、96kHz等。
  7. 输出文件大小:可以选择输出的文件大小,支持的文件大小有:小、中、大、自定义等。

音频选项

音频选项可以用来控制输出文件的音频属性,包括音频编码器、采样率、声道数、比特率等。

音频编码器:可以使用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等。

视频选项

视频选项包括:

  1. 视频编码:可以使用 -c:v 参数来指定视频编码器,支持的编码器有 libx264、libx265、mpeg4、vp8、vp9 等。
  2. 视频分辨率:可以使用 -s 参数来指定视频的分辨率,支持的分辨率有 1080p、720p、480p 等。
  3. 视频帧率:可以使用 -r 参数来指定视频的帧率,支持的帧率有 24、25、30、60 等。
  4. 视频码率:可以使用 -b:v 参数来指定视频的码率,支持的码率有 1000k、2000k、3000k 等。
  5. 视频关键帧间隔:可以使用 -g 参数来指定视频的关键帧间隔,支持的关键帧间隔有 10、20、30 等。
  6. 视频比特率模式:可以使用 -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 浏览器和移动设备上使用。

  1. 上传界面:
<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>
  1. 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) {
  // ...
}
  1. 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);
  // ...
})
  1. 读取视频元数据

您可以使用模块的 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 命令很可能会失败,除非流是直播流。只有在你知道自己在做什么的情况下才会这么做。
  1. 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 设置视频的输入输出选项、音频视频比特率、编码。这个转换过程是异步的。我们还监听了转换命令的 progressenderror事件。当视频转换完成时,会在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

参考示例

1

评论 (0)

取消