DragonBorn
DragonBorn

Reputation: 1809

Cut multiple parts from a video and merge them together

I am trying to cut specific parts out from a video and then merge all those parts into a single video file using nodejs and ffmpeg.

Here's my code and currently I can cut only one part out of the video from .setStartTime to .setDuration and that part is being saved.

var ffmpeg = require('fluent-ffmpeg');

var command = ffmpeg()
  .input('./videos/placeholder-video.mp4')
  .setStartTime('00:00:03')
  .setDuration('02')
  .output('./videos/test.mp4')

  .on('start', function(commandLine) {
    console.log('Started: ' + commandLine);
  })

  .on('end', function(err) {   
    if(!err)
    {
      console.log('conversion Done');
    }                 
  })

  .on('error', function(err){
    console.log('error: ', +err);
  }).run();

How do I cut out multiple parts out from the video and merge them in a single video file. I know about .mergeToFile method but how do I use it after cutting different parts from my video.

I tried using .setStartTime and .setDuration twice like below but the first one's are being ignored.

.input('./videos/placeholder-video.mp4')
.setStartTime('00:00:03')
.setDuration('02')
.setStartTime('00:00:15')
.setDuration('05')
.output('./videos/test.mp4')

Upvotes: 2

Views: 4036

Answers (2)

webmastersmith
webmastersmith

Reputation: 11

This is how I would solve it:

(async function() {
  const ffmpeg = require('fluent-ffmpeg')
  
  function ffmpegPromise(fn) {
    return new Promise((res, rej) => {
      fn.on('error', (e) => rej(e))
        .on('end', () => res('finished!'))
        .run()
    })
  }

  const file1 = ffmpeg().input('./videos/placeholder-video.mp4')
  .setStartTime('00:00:03')
  .setDuration(2)
  .output('./videos/test1.mp4')
  
  const file2 = ffmpeg().input('./videos/placeholder-video.mp4')
  .setStartTime('00:00:10')
  .setDuration(2)
  .output('./videos/test2.mp4')
  
  const files = await Promise.all([ffmpegPromise(file1), ffmpegPromise(file2)])
  console.log(files) // ['finished!', 'finished!']

  ffmpeg()
    .mergeAdd('./videos/test1.mp4')
    .mergeAdd('./videos/test2.mp4')
    .mergeToFile('./videos/mergedFile.mp4')
    .on('end', () => {
       fs.unlink('./videos/test1.mp4', err => {
         if (err) console.error(err)
       })
       fs.unlink('./videos/test2.mp4', err => {
         if (err) console.error(err)
       })
     })     
})()

File1 instantiates ffmpeg. The code is not run until ffmpegPromise is called with the '.run()' command. It starts at 3 seconds and takes 2 seconds of video. Then saves the 2 second video clip to file './videos/test1.mp4'.

File2 does the same thing except it starts at 10 seconds and takes 2 seconds of video. Then saves the 2 second video clip to file './videos/test2.mp4'.

ffmpegPromise passes the ffmpeg instantiated function to a promise, so it can be awaited.

Promise.all calls both 'file1' and 'file2' at the same time, and returns 'finished!' when files are saved.

ffpeg().mergeAdd()...mergeToFile(...) takes both 2 second video clips and merges them into one file, './videos/mergedFile.mp4'. When completed, the 'end' event is fired and fs.unlink will delete the test1 and test2 videos.

Upvotes: 1

Business Tomcat
Business Tomcat

Reputation: 1051

PLEASE READ UPDATE

Could not test it yet, but try following: Set the input video two times, like this:

.input('./videos/placeholder-video.mp4')
.setStartTime('00:00:03')
.setDuration('02')
.input('./videos/placeholder-video.mp4')
.setStartTime('00:00:15')
.setDuration('05')

Then you can merge the multiple inputs with .mergeToFile.


UPDATE

My answer cannot work due to restriction of .setDuration. It is an output option, so it defines how long transcoding to the output file is done: https://github.com/fluent-ffmpeg/node-fluent-ffmpeg It is not used to define the length/duration of the input.

Another option would be .loop, but apparently it is not supported for this purpose: https://video.stackexchange.com/questions/12905/repeat-loop-input-video-with-ffmpeg/12906#12906

If you really want to use nodejs, you need multiple commands

  • cut the input video to temporary files, one for each cut
  • merge the temporary files to one output file

Something like this:

var command1 = ffmpeg()
  .input('./small.mp4')
  .seekInput('00:00:02')
  .withDuration(1)
  .output('./temp1.mp4')
  .run();

var command2 = ffmpeg()
  .input('./small.mp4')
  .seekInput('00:00:01')
  .withDuration(3)
  .output('./temp2.mp4')
  .run();

var command2 = ffmpeg()
  .input('./temp1.mp4')
  .input('./temp2.mp4')
  .mergeToFile('./merge.mp4', './temp');

Big problem: your nodejs script will not start if temporary files are not present. So it would be much more easier to use a bash or batch script.

Upvotes: 1

Related Questions