Rasmus Puls
Rasmus Puls

Reputation: 3219

Frozen video clips when concatenating mp4 videos using ffmpeg via nodje.js fluent-ffmpeg

I'm trying to write a simple js application that takes all video files in a directory and the produce one single video consisting of all the clips combined one after another into one timeline.

To test the application I have download for random short stock video clips from pexels: enter image description here Shown in order: clip 1, clip 2, clip 3, clip 4

Each of the clips plays perfectly in vlc on windows (no broken or frozen frames)

I use the following script to concat the clips:

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

  ffmpeg()
    .input(concatFilePath)
    .inputOptions(['-f concat', '-safe 0'])
    .outputOptions(['-c copy', '-c:v libx264', '-b:v 5M', '-r 30', '-preset', 'slow'])
    .on('end', () => {
      console.log('Video clips concatenated successfully.');
    })
    .on('error', (err) => {
      console.error('Error concatenating video clips:', err);

      // Cleanup: Delete the output folder
      console.log('Cleaning up...');
      deleteOutputFolder(outputDir);
    })
    .save(outputPath);

The contect of my concatFilePath (concat.txt) file looks like this:

file src\input\d69de4a3-2b72-462c-be70-f8b8287b45e0\pexels-fred-c-19065853 (Original).mp4
file src\input\d69de4a3-2b72-462c-be70-f8b8287b45e0\pexels-imad-clicks-16270463 (2160p).mp4
file src\input\d69de4a3-2b72-462c-be70-f8b8287b45e0\pexels-peter-fowler-9683061 (2160p).mp4
file src\input\d69de4a3-2b72-462c-be70-f8b8287b45e0\pexels-sascha-5799767 (1080p).mp4

The problem

When I run the program it takes about 15 seconds to render the output video. The first clip looks as the stock clip, but when I get to the 2nd clip it just sits frozen at the first fram of that video, and only runs for 3-4 seconds despite the stock clip is 8 seconds long. It then transitions into the 3rd clip which is renderd correctly but the rest of the video is frozen on the last frame of clip 3 instead of showing clip 4.

I'm assuming that my input options are to blame for the result of the output, I have tried various other input options suggested from github thread and other sources, but none of them have produces a reasonable result.

Upvotes: 0

Views: 200

Answers (1)

Rasmus Puls
Rasmus Puls

Reputation: 3219

I found a working solution. The problem seemed to be that the videos are of different encoding, and doesn't just concat perfectly - some of them did, some of them didn't. So I decided to try to streamline the video format by first converting every clip individually to the same h264 format, and that had a positive effect.

I also decided to ditch fluent-ffmpeg since there's not much documentation or information online compared to the native ffmpeg commands. So instead I interact directly with ffmpeg through exec.

// step 1. convert videos to h264
const tempFolder = path.join(outputDir, 'temp');
if (!fs.existsSync(tempFolder)) {
  fs.mkdirSync(tempFolder, { recursive: true });
}
for (let i = 0; i < videoClipPaths.length; i++) {
  const inputVideo = videoClipPaths[i];
  const outputVideo = `${tempFolder}/video_${i}.mp4`;
  await exec(`ffmpeg -i "${inputVideo}" -c:v libx264 -b:v 5M -r 22 -preset slow -c:a aac -b:a 192k "${outputVideo}"`);
}

// step 2. create concat file
const concatFilePath = path.join(tempFolder, 'concat.txt');
const fileList = videoClipPaths.map((_, i) => `file 'video_${i}.mp4'`).join('\n');
await fs.promises.writeFile(concatFilePath, fileList);

// step 3. compile and render final video
await exec(`ffmpeg -f concat -safe 0 -i ${concatFilePath} -c copy ${outputPath}`);

Upvotes: 1

Related Questions