ffmpeg background process video conversion
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?
- Christian Scheuer @chrscheuer2021-02-18 10:40:33.719Z
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.
- AAndrew Sherman @Andrew_Sherman
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", });
Christian Scheuer @chrscheuer2021-02-18 14:33:17.718Z
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';
Christian Scheuer @chrscheuer2021-02-18 14:34:34.603Z
You may also want to check the result of the sf.system.exec call:
https://soundflow.org/docs/api/interfaces/systemexecresultIt'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.
Christian Scheuer @chrscheuer2021-02-18 14:35:38.828Z
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 :)- In reply tochrscheuer⬆:AAndrew Sherman @Andrew_Sherman
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';
- AAndrew Sherman @Andrew_Sherman
// 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);
Christian Scheuer @chrscheuer2021-02-18 14:51:45.524Z
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
.Christian Scheuer @chrscheuer2021-02-18 14:52:26.530Z
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.
- AAndrew Sherman @Andrew_Sherman
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).
Christian Scheuer @chrscheuer2021-02-18 15:24:31.008Z
That sounds like you may be referencing them wrong. Please show us the code you're running
Christian Scheuer @chrscheuer2021-02-18 15:25:09.628Z
Note here from my link what the properties are called:
https://soundflow.org/docs/api/interfaces/systemexecresult- AAndrew Sherman @Andrew_Sherman
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);
Christian Scheuer @chrscheuer2021-02-18 19:03:01.734Z
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);
- AAndrew Sherman @Andrew_Sherman
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 foundI 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);
Dustin Harris @Dustin_Harris
As a test, try running the command as
ffmpeg -version
orffmpeg -h
... I have a sneaking suspicion that the backslash characters in the ffmpeg parameters are causing some problems and might need to be escaped...- AAndrew Sherman @Andrew_Sherman
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` });
Christian Scheuer @chrscheuer2021-02-19 10:37:33.658Z
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 runningwhich 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` });
- AAndrew Sherman @Andrew_Sherman
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}"` });
Christian Scheuer @chrscheuer2021-02-19 14:32:18.357Z
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 :)');