5

I am writing a program where I need to process a video multiple times using ffmpeg. The ffmpeg codes (below) are inside a 'then' statement of a promise.

ffmpeg(path)
  .size('640x?')
  .aspect('1:1')
  .autopad('#682BAB')
  .saveToFile(`${userDirPath}/11-${userFileName}`)
  .on('end', () => {
    ffmpeg()
      .input('test-11-start.mp4')
      .mergeAdd(`${userDirPath}/11-${userFileName}`)
      .mergeAdd('test-11-end.mp4')
      .mergeToFile(`${userDirPath}/11-final-${userFileName}`, 'temp/')
      .on('end', () => console.log('FFmpeg done!'));
  });

There is another ffmpeg function after this (same, but with a different aspect ratio) and then, a 'then' statement with some other functions.

The problem is that this ffmpeg function runs asynchronously, and the next statements (which use the resulting file of ffmpeg func) are executed before it finishes executing and so I want it to run synchronously. I've tried async await (below) but it still runs asynchronously. What is wrong with code?

async function ffmpegMerge() {
  try {
    await ffmpeg(path)
    .size('640x?')
    .aspect('1:1')
    .autopad('#682BAB')
    .saveToFile(`${userDirPath}/11-${userFileName}`)
    .on('end', () => {
      ffmpeg()
        .input(`test-11-start.mp4`)
        .mergeAdd(`${userDirPath}/11-${userFileName}`)
        .mergeAdd(`test-11-end.mp4`)
        .mergeToFile(`${userDirPath}/11-final-${userFileName}.mp4`, 'temp/')
        .on('end', () => console.log('FFmpeg done!'));
    })
  }
  catch (err) {
    return Promise.reject(new Error(err));
  }
}
sciencaholic
  • 123
  • 1
  • 11
  • Why not put the first `ffmpeg` "code" into an function which returns a Promise and resolves when the render is finished(where you log FFMPEG done!). The second `ffmpegMerge()` function then is called on the resolve of the first? – Mattstir Jan 23 '20 at 10:18
  • I did try that, but it weirdly did not work. I used promise inside a promise to solve this, put the rest of the code inside the second `ffmpeg()`'s `.on('end', () => {})`. I used to think nested promises are a bad practice, that's why I wasn't going for it before, but they're not. – sciencaholic Jan 24 '20 at 06:35

2 Answers2

10

Create a function with promise and use await to wait until the function is resolved.

This is an example of using ffmpeg synchronously:

function ffmpegSync(){
   return new Promise((resolve,reject)=>{
      ffmpeg(path)
         .size('640x?')
         .aspect('1:1')
         .autopad('#682BAB')
         .saveToFile(`${userDirPath}/11-${userFileName}`)
         .on('end', () => {
            ffmpeg()
               .input(`test-11-start.mp4`)
               .mergeAdd(`${userDirPath}/11-${userFileName}`)
               .mergeAdd(`test-11-end.mp4`)
               .mergeToFile(`${userDirPath}/11-final-${userFileName}.mp4`, 'temp/')
               .on('end', () => console.log('FFmpeg done!'));
               resolve()
         })
         on('error',(err)=>{
            return reject(new Error(err))
         })
   })
}

Now just use the function ffmpegSync and await.

0
 createThumbnailForVideo().then(()=>{
      console.log('ok');
 })


 function createThumbnailForVideo(){

    return new Promise((resolve,reject)=>{

       const ffmpegInstaller = require('@ffmpeg-installer/ffmpeg');
       const ffmpeg = require('fluent-ffmpeg');
       ffmpeg.setFfmpegPath(ffmpegInstaller.path);
       var path = require('path'), // Default node module
       pathToFile = path.join(__dirname, 'tempfiles', fileName),
       pathToSnapshot = path.join(__dirname, 'tempfiles');

       var proc = ffmpeg(pathToFile)
        .on('filenames', (filenames)=> {})
        .on('end', (data)=> {
          console.log('screenshots were saved');
      
        })
        .on('error', (err)=> {
          console.log('an error happened: ' + err.message);
          return reject(new Error(err))
        })
        .takeScreenshots({ count: 1,filename:`thumb_${onlyName}.png`, timemarks: [ '00:00:01.000' ], size: '250x?' },pathToSnapshot)
        .on('end', () => {
          console.log('FFmpeg done!')
          resolve()
        })
        .on('error', (err)=> {
          console.log('an error happened: ' + err.message);
          return reject(new Error(err))
        })
      
   })
  • 3
    Please add some clarification of your code so it is easier to understand what is is going on and/or why doesn't OP's code working in the first place – metodribic Oct 10 '21 at 13:44
  • while there isn't a description, this approach of using promises worked for me – alana314 Jun 15 '23 at 04:37