ScanSkill
Sign up for daily dose of tech articles at your inbox.
Loading

Video Manipulation With Fluent FFmpeg and Node JS

Video Manipulation With Fluent FFmpeg and Node JS
Video Manipulation With Fluent FFmpeg and Node JS

Introduction

Manipulating a video with Node.js itself would be extremely hard, so instead, we are going to use the most popular video manipulation tool: FFmpeg. In the documentation, we read:

FFmpeg is the leading multimedia framework, able to decode, encode, transcode, mux, demux, stream, filter and play pretty much anything that humans and machines have created. It supports the most obscure ancient formats up to the cutting edge. No matter if they were designed by some standards committee, the community, or a corporation. It is also highly portable: FFmpeg compiles, runs, and passes our testing infrastructure FATE across Linux, Mac OS X, Microsoft Windows, the BSDs, Solaris, etc. under a wide variety of build environments, machine architectures, and configurations.

Prerequisites

To complete this tutorial, you will need:

  • A local development environment for Node.js.
  • FFmpeg >=0.9
  • A video to test your implementation.

Installation

Via npm:

$ npm install fluent-ffmpeg

Setting binary path

For the binary path, you can use the FFmpeg installer which is a platform-independent binary installer of FFmpeg for node projects. it installs a binary of ffmpeg for the current platform and provides a path and version. Supports Linux, Windows, and Mac OS/X.

Installation

Via npm:

$ npm install @ffmpeg-installer/ffmpeg

Usage examples

const ffmpegPath = require('@ffmpeg-installer/ffmpeg').path;
const ffmpeg = require('fluent-ffmpeg');
ffmpeg.setFfmpegPath(ffmpegPath);

Sample Example

ffmpeg('/path/to/input.mp4')
.size('640x480')
.output('/path/to/output.mp4')
.on('error', function(err) {
console.log(error);
})
.on('end', function() {
console.log('Processing finished !');
})
.run();

The above example converts the input file to the size of 640×480 dimension and stores the result in the path provided in the output parameter. In the above example the size argument

may have one of the following formats:

  • 640x480: set a fixed output frame size. Unless autopad() is called, this may result in the video being stretched or squeezed to fit the requested size.
  • 640x?: set a fixed width and compute height automatically. If aspect() is also called, it is used to compute video height; otherwise, it is computed so that the input aspect ratio is preserved.
  • ?x480: set a fixed height and compute width automatically. If aspect() is also called, it is used to compute video width; otherwise, it is computed so that the input aspect ratio is preserved.
  • 50%: rescale both width and height to the given percentage. Aspect ratio is always preserved.

Generating Screenshots

takeScreenshots() method is used to extract one or more thumbnails and save them as PNG files.

ffmpeg('/path/to/input')
  .takeScreenshots(
    {
      count: 2,
	    timemarks: ["00:00:02.000", "00:00:05.000" ],
      filename: `%b`,
      size: "480x?",
    },
    "/path/to/output"
  )
  .on('error', function(err) {
    console.log(error);
  })
  .on('end', function() {
    console.log('Processing finished !');
  });

The options argument is an object with the following keys:

  • filename: output filename pattern (see below). Defaults to “tn.png”.
  • count: specifies how many thumbnails to generate.
  • timemarks: specifies an array of timestamps in the video where thumbnails should be taken. Each timestamp may be a number (in seconds).
  • size: specifies a target size for thumbnails (with the same format as the .size() method). Note: you should not use the .size() method when generating thumbnails.

The filename option specifies a filename pattern for generated files. It may contain the following format tokens:

  • ‘%s’: offset in seconds
  • ‘%w’: screenshot width
  • ‘%h’: screenshot height
  • ‘%r’: screenshot resolution (same as ‘%wx%h’)
  • ‘%f’: input filename
  • ‘%b’: input basename (filename w/o extension)
  • ‘%i’: index of a screenshot in time mark array (can be zero-padded by using it like %000i)

If multiple time marks are passed and no variable format token (‘%s’ or ‘%i’) is specified in the filename pattern, _%iwill be added automatically.

Adding watermark in the video

The complexFilter()method enables setting a complex filtergraph for a command. It expects a filter specification (or a filter specification array) and an optional output mapping parameter as arguments.

ffmpeg('/path/to/input')
  .input('/path/to/watermark')
  .videoCodec("libx264")
  .outputOptions(["-map 0:a"])
  .complexFilter(
    [
      "scale=-2:720[overlayed]",
      {
        filter: "overlay",
        options: {
          x: "(main_w-overlay_w)/2",
          y: "(main_h-overlay_h)/2",
        },
        inputs: "overlayed",
        outputs: "filterOutput",
      },
      "[filterOutput]scale=480:-2[output]",
    ],
    "output"
  )
  .on("error", (err: any) => {
    console.error(err);
  })
  .on("end", () => {
    console.log("Finished processing");
  })
  .output('/path/to/output')
  .run();
});

Filter specifications may be either plain FFmpeg filter strings (eg. split=3[a][b][c]) or objects with the following keys:

  • filter: filter name (eg. overlay, crop,)
  • options: optional; either an option string for the filter (eg. in:0:30), an options array for unnamed options (eg. ['in', 0, 30]) or an object mapping option names to values (eg. { t: 'in', s: 0, n: 30 }). When options is not specified, the filter will be added without any options.
  • inputs: optional; input stream specifier(s) for the filter. The value may be either a single stream specifier string or an array of stream specifiers. Each specifier can be optionally enclosed in square brackets. When input streams are not specified, FFmpeg will use the first unused streams of the correct type.
  • outputs: optional; output stream specifier(s) for the filter. The value may be either a single stream specifier string or an array of stream specifiers. Each specifier can be optionally enclosed in square brackets.

The output mapping parameter specifies which stream(s) to include in the output from the filter graph. It may be either a single stream specifier string or an array of stream specifiers. Each specifier can be optionally enclosed in square brackets. When this parameter is not present, FFmpeg will default to saving all unused outputs to the output file.

Conclusion

This article shows the basic installation step of fluent-ffmpeg and some video manipulation operations like taking screenshots, modifying video size, and adding a watermark to the video.

Sign up for daily dose of tech articles at your inbox.
Loading