fluent-ffmpeg / node-fluent-ffmpeg

A fluent API to FFMPEG (http://www.ffmpeg.org)
MIT License
7.96k stars 881 forks source link

pass text file to complexFilter #1272

Open jrohlandt opened 6 months ago

jrohlandt commented 6 months ago

Version information

Is it possible to pass a file to complexFilter? I'm running into issues on Windows becuase I have to add hundreds of png overlays to a video. Is there something like complexFilterScript() or how can I achieve this?

benoitlahoz commented 4 weeks ago

I finally ended with a solution with drawtext filter, but I believe it should be valid for overlay (explanation below).

My situation:

The solution: build a string of filters with template strings and use enable between.

const files = ['path1', 'path2', etc...];

let txt = ''; // our description of files to be treated.
let filters = ''; // the filters we want to apply.
let currentPosition = 0; // current position in file.
let img = 0; // index of current image.

for (const file of files) {
  // Get the image path.
  /// In this case it's defined by 'image' followed by the index of the image then '.jpg'.
  const filepath = path.join(
    VIDEOS_PATH,
    '.tmp',
    `image${String(img).padStart(4, '0')}.jpg`
  );

  // Omitted for brevity.
  const duration = /* get the duration of this specific file. */

  // Append to the string of files that will be treated.
  txt += `file '${filepath}'\nduration ${duration}\n`;

  // Here the important part: append to the 'filters' string that will be passed to 'complexFilters'.
  // 'enable' is the key.

  filters += `drawtext=enable='between(t,${currentPosition},${currentPosition + duration})':text='${duration}':x=10:y=H-th-10:fontsize=32:fontcolor=white, `;

  // Next position in file.
  currentPosition += duration;
  img++;
}

// Write the text file that describes our inputs.
fs.writeFileSync(path.join(VIDEOS_PATH, '.tmp', `desc.txt`), txt, 'utf8');

ffmpeg()
  // Add inputs with file.
  .addInput(path.join(VIDEOS_PATH, '.tmp', `desc.txt`))
  // Concat.
  .inputOptions(['-safe 0', '-f concat'])

  // Pass the filters!
  .complexFilter(filters)

  .fps(25)
  .output(
    path.join(VIDEOS_PATH, `output.mov`)
  )
  .run();

For overlay, you can look this or this SO answers. I guess you could add overlays inputs the same way. The problem will be to retrieve [YOUR_INDEX:v] in the filter string. Didn't tested it though.