No internet connection
  1. Home
  2. How to

ffmpeg background process video conversion

By Andrew Sherman @Andrew_Sherman
    2021-02-17 21:19:31.709Z

    I'm trying to run ffmpeg as a background process without needing any interaction (apart from initial trigger). I have found various options in the forum for running a video conversion via terminal or app, but I'm looking to have it completely in the background without the need for a progress report. I have ffmpeg running properly on my machine and have used something like this previously with KM but I'm battling to get it working here.

    Here's what I'm looking for:

    • Trigger
    • Filepath for selected item/s in finder (Usually a ProRes Quicktime mov). Filepath sometimes has spaces. Normally a single item.
    • log "MP4 conversion started"
    • File is passed into ffmpeg x264 conversion
    • Process happens in background
    • Wait until complete
    • Log "MP4 conversion complete"
    • File is revealed in Finder

    Can you give me any pointers?

    • 20 replies

    There are 20 replies. Estimated reading time: 13 minutes

    1. Hi Andrew,

      Please try to share what you've tried - it's much easier to help with a script if we can see the first attempt.

      1. AAndrew Sherman @Andrew_Sherman
          2021-02-18 13:58:45.746Z

          Hi Christian,

          Sure, apologies for not posting the code earlier.

          Here's what I have so far; I know it's not there yet but it's work in progress.

          // Source file
          // Path may have spaces
          let sourceFile = `"${sf.ui.finder.firstSelectedPath}"`
          
          // Destination file
          // Keep same name and path, change extension to MP4
          let destinationFile = `"${sourceFile.split('.').slice(0, -1).join('.') + '.mp4'}"`;
          
          log(`MP4 conversion started`);
          
          // ffmpeg video conversion to run in background (no terminal?)
          sf.system.exec({
                  commandLine: `ffmpeg -i "${sourceFile}" -nostdin -c:v libx264 -preset fast -b:v 15M -vf scale=1920:1080 -c:a aac -ar 48000 -pix_fmt yuv420p "${destinationFile}"`
              });
          
          // Wait?
          
          log(`MP4 conversion complete`);
          
          // Reveal new file in Finder
          sf.file.open({
              path: destinationFile,
              applicationPath: "/System/Library/CoreServices/Finder.app",
          });
          
          1. Hi Andrew,

            This code should work just fine (almost). The sf.system.exec call is blocking, so your code won't continue until the ffmpeg call itself is done.

            The one thing you're doing wrong is that you're putting " quotes around your paths twice - both when you create the variable and when you embed it in the commandLine.

            Your variables should be just:

            let sourceFile = sf.ui.finder.firstSelectedPath;
            
            // Destination file
            // Keep same name and path, change extension to MP4
            let destinationFile = sourceFile.split('.').slice(0, -1).join('.') + '.mp4';
            
            
            1. You may also want to check the result of the sf.system.exec call:
              https://soundflow.org/docs/api/interfaces/systemexecresult

              It's probably a good idea to check if there was some error in the call to ffmpeg by inspecting the "result" property (stdout), the "standardError" (stderr) and the exitCode.

              1. Once you have that working, we can talk about how the blocking call that also blocks calls to any other SF script (because all Javascript is single threaded) - how to make that still accept new SF scripts while the ffmpeg process is being run.
                But you'll have to get the above to work first. I suggest testing it on short/small inputs so you won't have to fair for too long when testing the process :)

                1. In reply tochrscheuer:
                  AAndrew Sherman @Andrew_Sherman
                    2021-02-18 14:47:07.296Z

                    Thanks for that Christian. I used a log to find what's going on with the system.exec item, and it says "TypeError: Cannot read property 'split' of null
                    (ffmpeg line 8) "

                    let destinationFile = sourceFile.split('.').slice(0, -1).join('.') + '.mp4';
                    
                    1. AAndrew Sherman @Andrew_Sherman
                        2021-02-18 14:50:12.556Z
                        // Source file
                        // Path may have spaces
                        let sourceFile = sf.ui.finder.firstSelectedPath;
                        
                        // Destination file
                        // Keep same name and path, change extension to MP4
                        let destinationFile = sourceFile.split('.').slice(0, -1).join('.') + '.mp4';
                        
                        log(`MP4 conversion started`);
                        
                        // ffmpeg video conversion to run in background (no terminal?)
                        sf.system.exec({
                                commandLine: `ffmpeg -i "${sourceFile}" -nostdin -c:v libx264 -preset fast -b:v 15M -vf scale=1920:1080 -c:a aac -ar 48000 -pix_fmt yuv420p "${destinationFile}"`
                            });
                        
                        log(sf.system.exec.stdout);
                        
                        1. This just indicates there's no selected file in Finder when you run the script, since you're getting a null reply from sf.ui.finder.firstSelectedPath.

                          1. You were swallowing that error before, so you were essentially calling ffmpeg trying to have it convert a file called ""null"". Now we're seeing that initial error.

                            1. AAndrew Sherman @Andrew_Sherman
                                2021-02-18 14:58:15.968Z

                                Doh, silly me. Ok, so now it says "undefined" once I make sure to select the file in Finder and trigger the command (undefined for stdout, stderr and exitCode).

                                1. That sounds like you may be referencing them wrong. Please show us the code you're running

                                  1. Note here from my link what the properties are called:
                                    https://soundflow.org/docs/api/interfaces/systemexecresult

                                    1. AAndrew Sherman @Andrew_Sherman
                                        2021-02-18 16:00:29.161Z

                                        I think I'm missing something obvious, but I can't find how to inspect the results of the ffmpeg process. Is it done with log?

                                        
                                        // Source file
                                        // Path may have spaces
                                        let sourceFile = sf.ui.finder.firstSelectedPath;
                                        
                                        // Destination file
                                        // Keep same name and path, change extension to MP4
                                        let destinationFile = sourceFile.split('.').slice(0, -1).join('.') + '.mp4';
                                        
                                        log(`MP4 conversion started`);
                                        
                                        // ffmpeg video conversion to run in background (no terminal?)
                                        sf.system.exec({
                                                commandLine: `ffmpeg -i "${sourceFile}" -nostdin -c:v libx264 -preset fast -b:v 15M -vf scale=1920:1080 -c:a aac -ar 48000 -pix_fmt yuv420p "${destinationFile}"`
                                            });
                                        
                                        log(sf.system.exec.stdout);
                                        
                                        log(sf.system.exec.standardError);
                                        
                                        log(sf.system.exec.result);
                                        
                                        log(sf.system.exec.exitCode);
                                        
                                        log(sourceFile);
                                        
                                        log(destinationFile);
                                        
                                        1. You have to assign the result of the call to the action :)
                                          This is how results from all SoundFlow actions work.

                                          var execResult = sf.system.exec({
                                                  commandLine: `ffmpeg -i "${sourceFile}" -nostdin -c:v libx264 -preset fast -b:v 15M -vf scale=1920:1080 -c:a aac -ar 48000 -pix_fmt yuv420p "${destinationFile}"`
                                              });
                                          
                                          log(execResult.standardError);
                                          
                                          log(execResult.result);
                                          
                                          log(execResult.exitCode);
                                          
                                          1. AAndrew Sherman @Andrew_Sherman
                                              2021-02-18 19:38:40.667Z

                                              Aah, great, thank you Christian. I understand now. Thank you also for your patience; I'm learning principles which will help for other things in future, and I'm sure other people can also benefit from the forum posts.

                                              So after fixing the results inspection and running the command again, I get the following error
                                              127
                                              [LOG] /bin/bash: line 5: ffmpeg: command not found

                                              I confirm that I do have ffmpeg installed and working properly, and if I take the same ffmpeg code from the script and paste that into terminal and execute it there, it runs correctly and generates the MP4 (obviously replacing the path variable with an actual path).

                                              
                                              // Source file
                                              // Path may have spaces
                                              let sourceFile = sf.ui.finder.firstSelectedPath;
                                              
                                              // Destination file
                                              // Keep same name and path, change extension to MP4
                                              let destinationFile = sourceFile.split('.').slice(0, -1).join('.') + '.mp4';
                                              
                                              log(`MP4 conversion started`);
                                              
                                              // ffmpeg video conversion to run in background (no terminal?)
                                              var execResult = sf.system.exec({
                                                      commandLine: `ffmpeg -r 25 -y -i "${sourceFile}" -nostdin -preset fast \
                                                      -c:v libx264 -profile:v high -bf 2 -crf 17 -pix_fmt yuv420p -vf scale=1920:1080 \
                                                      -color_primaries 1 -color_trc 1 -colorspace 1 \
                                                      -c:a aac -b:a 384k -ar 48000 \
                                                      -r 25 "${destinationFile}"`
                                                  });
                                              
                                              log(execResult.standardError);
                                              
                                              log(execResult.result);
                                              
                                              log(execResult.exitCode);
                                              
                                              log(sourceFile);
                                              
                                              log(destinationFile);
                                              
                                              1. Dustin Harris @Dustin_Harris
                                                  2021-02-19 02:33:53.216Z

                                                  As a test, try running the command as ffmpeg -version or ffmpeg -h... I have a sneaking suspicion that the backslash characters in the ffmpeg parameters are causing some problems and might need to be escaped...

                                                  1. AAndrew Sherman @Andrew_Sherman
                                                      2021-02-19 06:35:23.469Z

                                                      Thanks Dustin. I'm still getting the same error when I run that. [LOG] /bin/bash: line 5: ffmpeg: command not found

                                                      // Source file
                                                      // Path may have spaces
                                                      let sourceFile = sf.ui.finder.firstSelectedPath;
                                                      
                                                      // Destination file
                                                      // Keep same name and path, change extension to MP4
                                                      let destinationFile = sourceFile.split('.').slice(0, -1).join('.') + '.mp4';
                                                      
                                                      log(`MP4 conversion started`);
                                                      
                                                      // ffmpeg video conversion to run in background (no terminal?)
                                                      var execResult = sf.system.exec({
                                                              commandLine: `ffmpeg -version`
                                                          });
                                                      
                                                      1. Hi Andrew,

                                                        This could mean you need to provide the full path to the ffmpeg binary instead of just the name.
                                                        You can find that path by running which ffmpeg in Terminal.

                                                        So, if for example ffmpeg is located at /usr/local/bin/ffmpeg you'd need your code to be (for the test case):

                                                        var execResult = sf.system.exec({
                                                                commandLine: `/usr/local/bin/ffmpeg -version`
                                                            });
                                                        
                                                        1. AAndrew Sherman @Andrew_Sherman
                                                            2021-02-19 13:52:57.239Z

                                                            It's working!!! That did the trick. Amazing. Thanks Christian and Dustin for your input.

                                                            Christian, do you have something that I can read or experiment with for getting the next section of the script working?

                                                            • After ffmpeg encoding
                                                            • wait until finished
                                                            • notification: finished
                                                            • reveal file in finder

                                                            Here's the code so far (working)

                                                            
                                                            // Source file
                                                            let sourceFile = sf.ui.finder.firstSelectedPath;
                                                            
                                                            // Destination file
                                                            let destinationFile = sourceFile.split('.').slice(0, -1).join('.') + '.mp4';
                                                            
                                                            // ffmpeg install location
                                                            const ffmpegLocation = '/opt/homebrew/bin/ffmpeg';
                                                            
                                                            log(`MP4 conversion started`);
                                                            
                                                            // ffmpeg video conversion
                                                            var execResult = sf.system.exec({
                                                                    commandLine: `"${ffmpegLocation}" -y -i "${sourceFile}" -preset fast \
                                                                    -c:v libx264 -profile:v high -crf 17 -pix_fmt yuv420p -vf scale=1920:1080 \
                                                                    -color_primaries 1 -color_trc 1 -colorspace 1 \
                                                                    -c:a aac -b:a 320k -ar 48000 \
                                                                    -vsync 2 -r 25 "${destinationFile}"`
                                                                });
                                                                
                                                            
                                                            1. Great!

                                                              Check my response from earlier - the sf.system.exec call is blocking, that is, it doesn't continue the script until the ffmpeg call has completed - so anything you do after that call is when your conversion has completed.

                                                              For example, you could add:

                                                              if (execResult.exitCode !== 0) {
                                                                  throw `ffmpeg conversion failed with exit code: ${execResult.exitCode}`;
                                                              }
                                                              
                                                              log('MP4 conversion completed - yay :)');