I run ffmpeg.exe from node (spawn()) and sometimes (not random but depending on the day) it fails on Windows with error 1
I use a very basic syntax:
const outputPath = getTempFolder() + path.sep + 'video.mp4';
const command = spawn(ffmpegPath, [
'-i',
getTempFolder() + path.sep + 'video.webm',
'-y',
outputPath
]);
Any idea why this could happen and eventually how to debug it? Thanks!
Related
I have a .net core application which will unzip a file using 7zip.exe. this application is working fine with windows. once after I deployed it in docker , I am getting an error as "exec format error at system.diagnostics.process".
var processStartInfo = new ProcessStartInfo
{
FileName = sevenZPath,
Arguments = "x \"" + filePath + Path.DirectorySeparatorChar + fileName + "\" -o" + outputPath
};
var process = System.Diagnostics.Process.Start(processStartInfo);
process?.WaitForExit();
Even if I am running the exe directly from the container I am getting the same error.
I'm relatively new to TypeScript/Javascript/Node.js, but I have to execute a binary myapp (or myapp.exe on Windows) after I have sourced an environment script myapp_env (run myapp_env.bat on Windows)
. myapp_env && myapp
or in windows (cmd)
myapp_env.bat && myapp.exe
I am trying to use spawn:
import {exec,spawn} from 'child_process';
import {exists} from 'fs'
let programhome: string = 'C:/SoftwareAG105/Apama';
let envscript: string = programhome + '/bin/apama_env.bat';
let program: string = programhome + '/bin/correlator.exe';
exists(envscript , found =>
console.log( envscript + (found ? " is there" : " is not there")));
exists(program , found =>
console.log( program + (found ? " is there" : " is not there")));
try {
let test = spawn(envscript + ' && ' + program);
test.stdout.on('data', data => console.log(data));
test.stderr.on('data', data => console.log(data));
test.on('error', data => console.log('ERROR ' + data));
test.on('end', data => console.log('END ' + data));
test.on('exit', data => console.log('Exit ' + data));
} catch (error) {
console.log(error);
}
I get an ENOENT error which I presume is due to it trying to see whether the entire string exists as a file(?). If I run them individually then it works fine. In both cases the exists line will print "is there"....
Edit
After samuels answer I changed the following lines lines
...
import {parse,format,join} from 'path'
...
let programhome: string = join( 'C:' , 'SoftwareAG105', 'Apama' );
let envscript: string = join( programhome ,'bin','apama_env.bat');
let program: string = join(programhome , 'bin' , 'correlator.exe');
exists(envscript , found => console.log( envscript + (found ? " is there" : " is not there")));
exists(program , found => console.log( program + (found ? " is there" : " is not there")));
ERROR Error: spawn C:\SoftwareAG105\Apama\bin\apama_env.bat &&
C:\SoftwareAG105\Apama\bin\correlator.exe ENOENT index.js:15
C:\SoftwareAG105\Apama\bin\apama_env.bat is there index.js:9
C:\SoftwareAG105\Apama\bin\correlator.exe is there
TLDR; So my question is can I chain commands in spawn so that I can source the environment and run the program?
Ok so finally I found the information buried deep in various google posts :
I found that exec would work the way I wanted, but spawn would not, it turns out that for exec a shell is started allowing the chaining to occur.
https://www.freecodecamp.org/news/node-js-child-processes-everything-you-need-to-know-e69498fe970a/
By default, the spawn function does not create a shell to execute the
command we pass into it. This makes it slightly more efficient than
the exec function, which does create a shell. The exec function has
one other major difference. It buffers the command’s generated output
and passes the whole output value to a callback function (instead of
using streams, which is what spawn does).
child_process.spawn takes an options object which can have a property called shell
let test = spawn(envscript + ' && ' + program,{shell:true});
This extra configuration allows me to do the chaining I needed and so I can now source the environment and run the program correctly.
That might have something to do with the path delimiter on Windows being a backslash instead of a forward slash.
When building your strings, try to use path.delimiter (imported from the default node path module) (which is either / or \ depending on the OS). Like so :
let programhome: string = ['C:', 'SoftwareAG105', 'Apama'].join(path.delimiter);.
This way your path will always be valid, regardless of the OS.
I want to use fluent-ffmpeg module to call ffmpeg with complex filter from Electron but have no success. The error '[AVFilterGraph # 0xb8.......] No such filter " Error initalizing complex filters . Invalid argument' is the same as in this question Error : Running FFmpeg command in Android ,splitting command in array not working but the context is different.
What is needed?
Run this ffmpeg command using fluent-ffmpeg:
ffmpeg -i safework-background-0.mp4 -i image1.png -i image2.png -i
image3.png -filter_complex "[0:v][1:v]
overlay=1:1:enable='between(t,5,8.5)' [tmp]; [tmp][2:v]
overlay=1:1:enable='between(t,8.5,12)' [tmp]; [tmp][3:v]
overlay=1:1:enable='between(t,12,15)'" test-video-safework3.mp4
It uses a complex filter to overlay three images on a video in sequence and exports a new video.
What doesn't work?
Obviously fluent-ffmpeg chokes with required quotes for complex filter, that is my conclusion (and is the same as for the Android variant question above).
What works without fluent-ffmpeg in Electron?
As you can guess I have to resort to calling ffmpeg directly. To help others, the following command, with input and output video filenames parametrized, translates to Electron as:
var spawn = require('child_process').spawn
var fargs = ['-y', '-i', sourceDir.path() + '/' + inVideoName, '-i', tempDir.path() + '/' + 'image1.png',
'-i', tempDir.path() + '/' + 'image2.png', '-i', tempDir.path() + '/' + 'image3.png',
'-filter_complex', '[0:v][1:v]overlay=1:1:enable=\'between(t,5,8.5)\'[tmp];' +
'[tmp][2:v]overlay=1:1:enable=\'between(t,8.5,12)\'[tmp];[tmp][3:v]' +
'overlay=1:1:enable=\'between(t,12,15)\'', targetDir.path() + '/' + outVideoName]
var ffmpeg = spawn(ffmpegc, fargs, { cwd:jetpack.cwd(app.getPath('home')).path() })
// some code ommitted
ffmpeg.on('close', (code) => {
console.log(`child process exited with code ${code}`)
webContents.send('notify-user-reply', 'Video processing done.')
})
The above command already has removed spaces between various filters (in complex filter) for each image or it would also choke.
I would really love to use fluent-ffmpeg in Electron, not just for the convenience of calling ffmpeg more elegantly but also for some additional features like easy progress reporting.
I am trying to get the stdout of 7zip when it processes files and get the percentage in nodeJs, but it doesn't behave as expected. 7zip doesn't output anything to stdout until the very end of the execution. Which is not very helpful.. especially when I have large files being compressed and no feedback is shown for a very long time.
The code I am using (simplified):
// 7zip test, place the 7z.exe in the same dir, if it's not on %PATH%
var cp = require('child_process');
var inputFile = process.argv[2]; if(inputFile==null) return;
var regProgress = /(\d{1,3})%\s*$/; //get the last percentage of the string, 3 digits
var proc = cp.spawn("7z.exe",["a","-t7z" ,"-y" ,inputFile + ".7z",inputFile]);
proc.stdout.setEncoding("utf8");
proc.stdout.on("data",function(data){
if(regProgress.test(data))
console.log("Progress = " + regProgress.exec(data)[1] + "%");
});
proc.once("exit",function(exit,sig){ console.log("Complete"); });
I have used the same code to get the percentage with WinRar successfully and I am beginning to think that 7zip might be buggy? Or I am doing it wrong? Can I forcefully read the stdout of a process with a timer perhaps?
The same code above, with the exception of the following line replaced, works as expected with WinRar.
var proc = cp.spawn("Rar.exe",["a","-s","-ma5","-o+",inputFile+".rar",inputFile]);
If anyone knows why this happens and if it is fixable, I would be grateful! :-)
p.s. I have tried 7za.exe, the command line version of 7zip, also the stable, beta and alpha versions, they all have the same issue
It is no longer needed to use a terminal emulator like pty.js, you can pass the -bsp1 to 7z to force to output the progress to stdout.
7-zip only outputs progress when stdout is a terminal.
To trick 7-zip, you need to npm install pty.js (requires Visual Studio or VS Express with Windows SDK) and then use code like:
var pty = require('pty');
var inputFile = process.argv[2],
pathTo7zip = 'c:\\Program Files\\7-Zip\\7z.exe';
if (inputFile == null)
return;
var term = pty.spawn(process.env.ComSpec, [], {
name: 'ansi',
cols: 200,
rows: 30,
cwd: process.env.HOME,
env: process.env
});
var rePrg = /(\d{1,3})%\r\n?/g,
reEsc = /\u001b\[\w{2}/g,
reCwd = new RegExp('^' + process.cwd().replace(/\\/g, '\\\\'), 'm');
prompts = 0,
buffer = '';
term.on('data', function(data) {
var m, idx;
buffer += data;
// remove terminal escape sequences
buffer = buffer.replace(reEsc, '');
// check for multiple progress indicators in the current buffer
while (m = rePrg.exec(buffer)) {
idx = m.index + m[0].length;
console.log(m[1] + ' percent done!');
}
// check for the cmd.exe prompt
if (m = reCwd.exec(buffer)) {
if (++prompts === 2) {
// command is done
return term.kill();
} else {
// first prompt is before we started the actual 7-zip process
if (idx === undefined) {
// we didn't see a progress indicator, so make sure to truncate the
// prompt from our buffer so that we don't accidentally detect the same
// prompt twice
buffer = buffer.substring(m.index + m[0].length);
return;
}
}
}
// truncate the part of our buffer that we're done processing
if (idx !== undefined)
buffer = buffer.substring(idx);
});
term.write('"'
+ pathTo7zip
+ '" a -t7z -y "'
+ inputFile
+ '.7z" "'
+ inputFile
+ '"\r');
It should be noted that 7-zip does not always output 100% at finish. If the file compresses quickly, you may just see only a single 57% for example, so you will have to handle that however you want.
Let's say I'm trying to call ffmpeg from the current directory. On linux I'd call exec or spawn with './ffmpeg'. In order for this code to be portable to windows do I need to strip off the './' or is that somehow taken care of for me?
This is just half an answer: welcome to anyone to modify it and expand it.
Here is the result of my digging in the source code:
The exec method does a small check of the platform (taken from the source):
if (process.platform === 'win32') {
file = 'cmd.exe';
args = ['/s', '/c', '"' + command + '"'];
options = util._extend({}, options);
options.windowsVerbatimArguments = true;
} else {
file = '/bin/sh';
args = ['-c', command];
}
I don't know how you have added ffmpeg to your PATH on Windows, so it really depends on your setup. In any case, simply pass to exec the command as if you would be running it from the cmd.exe shell.
I however could not find something similar for spawn.