Search code examples
node.jsffmpegvideo-processingfluent-ffmpeg

FFMPEG: How to combine video and image (video template)


Goal

I have a video and an image (a template for the video) that I'm trying to combine into one output video (1080w x 1920h - 9:16 aspect ratio).

  • Input video - 1920x1080
  • Input image - 1080x1920
  • Output video - 1080x1920

This image shows what I'm trying to accomplish. The two orange sections are the input image - it's a single .png with a transparent section in the middle for the video.

As mentioned in the title, I'm trying to accomplish this using FFMPEG. More specifically, I'm using the fluent-ffmpeg npm package.

Current Status

I can read in both inputs just fine but I have issues getting the two to play nicely with one another.

If I get the overlay working then my output video is 1920x1080 instead of 1080x1920.

If I get the output video dimensions right, then the video is either stretched or I get errors adding my overlay.

Code

Here's what I have at the moment. I'm happy to answer any questions. Thank you in advance for taking a look :)

var ffmpeg = require('fluent-ffmpeg');
var command = ffmpeg();
var timemark = null;

command
  .on('end', onEnd )
  .on('progress', onProgress)
  .on('error', onError)
  .input('./input-video.mp4')
  .input('./template.png')
  .complexFilter([
    {
      filter: 'scale',
      options: { width: 1080, height: 1920 }
    },
    // {
    //   filter: 'overlay',
    //   options: { x: 100, y: 100 }
    // },
  ])
  .outputFps(30)
  .output('./output-video.mp4')
  .run();

/* Misc */

function onProgress(progress){
  if (progress.timemark != timemark) {
    timemark = progress.timemark;
    console.log('Time mark: ' + timemark + "...");
  }
}

function onError(err, stdout, stderr) {
  console.log('Cannot process video: ' + err.message);
}

function onEnd() {
  console.log('Finished processing');
}

Solution

  • Thanks to user Voldrix_Suroku on Reddit I was able to figure this out.

    To avoid being a DenverCoder9 the CLI command to get this to work is:

    ffmpeg -i input-video.mp4 -i template.png -filter_complex "[0:V]scale=1080:-1,pad=0:1920:0:(oh-ih)/2[vid];[vid][1:v]overlay" output-video.mp4

    And if you're using fluent-ffmpeg like I am, the full code is below:

    var ffmpeg = require('fluent-ffmpeg');
    var command = ffmpeg();
    var timemark = null;
    
    command
      .on('end', onEnd )
      .on('progress', onProgress)
      .on('error', onError)
      .input('./input-video.mp4')
      .input('./template.png')
      .complexFilter([
        "[0:V]scale=1080:-1,pad=0:1920:0:(oh-ih)/2[vid];[vid][1:v]overlay"
      ])
      .outputFps(30)
      .output('./output-video.mp4')
      .run();
    
    /* Misc */
    
    function onProgress(progress){
      if (progress.timemark != timemark) {
        timemark = progress.timemark;
        console.log('Time mark: ' + timemark + "...");
      }
    }
    
    function onError(err, stdout, stderr) {
      console.log('Cannot process video: ' + err.message);
    }
    
    function onEnd() {
      console.log('Finished processing');
    }