Process stalls when launched in .NET (ffmpeg) - c#

I'm trying to launch ffmpeg as a Process in .NET (C#), but in some cases, depending on the arguments (specifically, if I omit video arguments in order to create an audio file), it's stalling. It launches, outputs some lines, but then just stalls (using 0% CPU). When the parent .NET process is killed, it continues, and if I let it continue, ffmpeg produces the file correctly. I thought it might be due to using Peek() to look at the stream, so I just simplified it to the following, which behaves the same:
_process = new Process
{
StartInfo =
{
UseShellExecute = false,
RedirectStandardOutput = false,
RedirectStandardError = true,
FileName = "c:\\ffmpeg.exe",
Arguments = string.Format(
"-i {0} {1} {2} {3} -y {4}", inputPath, videoArgs, audioArgs, options, outputPath)
}
};
_process.Start();
_process.WaitForExit();
ffmpeg gets to the point where it outputs information about the input video/audio streams before stalling. Executing the command via the command prompt works as expected.
Does anyone know what the problem could be?
Edit:
Just to add, I tried UseShellExecute = true (and RedirectStandardError = false), and this works. I still need to read the output, however, so this doesn't really help me.

Have a read of this
MSDN on RedirectStandardError
Apparently this is a bit fiddly and can deadlock if the output or error stream buffers get filled up. Sit's there waiting for you to read what it's wrote...

According to the comments on this answer, in some cases FFmpeg manipulates the screen memory directly without using the standard output and error streams. One example of this is the [file] already exists. Overwrite? [y/N] prompt.
I'm guessing you've run the program outside if .NET to see what the output is? If it is a prompt like the one above then it might pay to avoid the trouble and attempt to provide an argument that will skip it eg. the -y command that forces FFmpeg to overwrite a file.

Related

c# start a exe until it is completely started and then append the arguments

I was trying to start a exe with arguments by Process.Start.
My first try is using Process.Start("Path/of/the/exe", "arguments of exe").
Here's my code snippets:
Process.Start(#"D:\Program Files\ITASCA\UDEC700\Exe64\udecConsole2017.exe", #"call 'D:\Work\202205\20220525\tunnel-for-cmd.txt'");
However the initialization of this exe is a bit slow, and the result is, I can only start the exe but the failed passing arguments. The following is the screenshot:
which is exactly the same result that starts without arguments.
By referencing this post C# - Making a Process.Start wait until the process has start-up, I changed my code as follows:
var process = Process.Start(#"D:\Program Files\ITASCA\UDEC700\Exe64\udecConsole2017.exe", #"call 'D:\Work\202205\20220525\tunnel-for-cmd.txt'");
while (string.IsNullOrEmpty(process.MainWindowTitle))
{
System.Threading.Thread.Sleep(100);
process.Refresh();
}
however these changes does not work.
I think my goal is to wait until exe completely started and then run it with arguments, but I dont know how to implement this.
=====================================================
New additions:
if I type in arguments call 'D:\Work\202205\20220525\tunnel-for-cmd.txt' in this started process, I will get my result:
SO I think the input arguments should be OK?
=======================================
new addition 2:
code for checking outputstream end
It appears this is a console application and you are typing in the console after it starts. This typing is not arguments: Arguments are provided only when starting a new process and never change.
What you are doing is providing something to the standard input of the program. Console programs have three streams the OS provides (one input and two output). You need to redirect these to detect when the program has started and to provide the proper input.
Something like this:
// Start with stdio redirected
var psi = new ProcessStartInfo()
{
UseShellExecute = false,
FileName = #"your exe",
RedirectStandardInput = true,
RedirectStandardOutput = true,
};
var p = System.Diagnostics.Process.Start(psi);
// Read until the udec> prompt
while(true)
{
var line = p.StandardOutput.ReadLine();
if(line.StartsWith("udec>"))
break;
}
// Write the command
p.StandardInput.WriteLine(#"call 'D:\Work\202205\20220525\tunnel-for-cmd.txt'");
// Read the result
p.StandardOutput.ReadToEnd();

Process.Start leaves streams empty

I have the code to run a console command/utility, monitor the live output using 'Debug.WriteLine' and write the final output to a log file when needed.
Edit: It does not work for Praatcon.exe an analysis command line utility. It can be downloaded from here . Just invoke praatcon.exe without argument, it should write on 'stdout' about the Usage. The code wont catch it.
The issue is, it works good for certain utilities and I can see the debug output as well as log in the file. But for certain utilities, I see empty commands, even though when I run those commands through a CMD window, I see the output. I am capturing both the streams Output and Error.
Can someone help me with this ?
Full code can be found here
Here is how I am trying to do it
Initialization of ProcessStartInfo
var info = new ProcessStartInfo(command, parameters)
{
WorkingDirectory = workingDirectory,
UseShellExecute = false,
RedirectStandardOutput = true,
RedirectStandardError = true,
CreateNoWindow = true
};
Running the process and initializing the string builders for output and error streams.
var process = Process.Start(info);
var output = new StringBuilder();
var error = new StringBuilder();
Starting the tasks for Reading Streams
var errorTask = process.StandardError.ReadLineAsync();
var lineTask = process.StandardOutput.ReadLineAsync();
Here is my while loop to monitor the progress and write output to the Debug Output window when there is any available.
while (process.HasExited == false)
{
if (lineTask.IsCompleted)
{
output.AppendLine(lineTask.Result);
Debug.WriteLine(lineTask.Result);
lineTask = process.StandardOutput.ReadLineAsync();
}
if (errorTask.IsCompleted)
{
error.AppendLine(errorTask.Result);
Debug.WriteLine(errorTask.Result);
errorTask = process.StandardError.ReadLineAsync();
}
errorTask.Wait(TimeSpan.FromMilliseconds(100.0));
lineTask.Wait(TimeSpan.FromMilliseconds(100.0));
}
After this, I am reading the streams further to see if there is anything left in there.
I get empty strings in output and error for one command. The only thing I get correct is the 'ExitCode'.
Please tell me if there is anything I am doing the wrong way.
As discussed on IRC, there was a possibility that the program you're calling may have been writing to a stream other than standard out or standard error. There are also streams with numbers 3-9 on Windows.
This was not the case with the process you were calling. It was actually using the Win32 call 'WriteConsole', which seems to access the console directly.
It would be possible to move the output back to stderr by preloading a DLL (DLL injection) but this is hackish, so as the source of the program is available, it's perhaps better to 'fix' it or submit a patch to the authors.
Your question is borderline too broad, in that it's missing details, and "anything I am doing the wrong way" is fairly open-ended.
That said, you are doing the reading of the streams the wrong way, in the sense that you should not be polling. I don't see any specific reason that would cause the behavior you've (vaguely) described. But just in case, I offer this correct implementation of the reading:
async Task ConsumeStream(StreamReader reader, StringBuilder builder)
{
string line;
while ((line = await reader.ReadLineAsync()) != null)
{
builder.AppendLine(line);
Debug.WriteLine(line);
}
}
Then call that method like this:
var errorTask = ConsumeStream(process.StandardError, error);
var lineTask = ConsumeStream(process.StandardOutput, output);
// Technically superfluous, since you'll also wait on the tasks,
// but won't hurt.
process.WaitForExit();
Task.WaitAll(errorTask, lineTask);
// error and output StringBuilders will be valid here
If that doesn't help, you'll need to post a better code example, which will allow others to reproduce the actual problem. See https://stackoverflow.com/help/mcve

Issue Seperate Arguments to Exe in one construct

I'm currently trying to pipe multiple parameters to the adb.exe file in the google sdk. an example of my inputs are:
adb shell getprop ro.build.version.release
adb shell getprop ro.product.brand
which are outputting correct from my application. Though, the problem is I want to populate a list view of information, the problem that i'm currently encountering though, is the method to pipe commands to get desired output. I've currently got:
var proc = new Process
{
StartInfo = new ProcessStartInfo
{
FileName = "Resources/adb.exe",
Arguments = "devices",
UseShellExecute = false,
RedirectStandardOutput = true,
CreateNoWindow = true
}
};
proc.Start();
string Output = proc.StandardOutput.ReadToEnd().ToString();
This currently returns as expected, but i'm wishing to get multiple results from piping something like:
Arguments = "devices ro.build.version.release ro.product.brand"
This provides no avail, even when piping directly into command prompt.
adb shell getprop devices ro.build.version.release ro.product.brand
empty, I have come around with a (assumingly) load heavy solution, which is to move the initialization of the executional into it's own function to be called multiple times. See code below:
public string GetInfo(string CommandArg)
{
var proc = new Process
{
StartInfo = new ProcessStartInfo
{
FileName = "Resources/adb.exe",
Arguments = CommandArg,
UseShellExecute = false,
RedirectStandardOutput = true,
CreateNoWindow = true
}
};
proc.Start();
return proc.StandardOutput.ReadToEnd().ToString();
}
public void SetDefineInformation()
{
AndroidVersion = decimal.Parse(GetInfo("ro.build.version.release"));
DeviceModel = GetInfo("ro.product.device");
...
}
To me, this seems like a load heavy task with constantly opening a single executable for it to close then re-open until the task is complete. Is there an overall work around, which might allow one to pipe multiple parameters to an executional and get the desired results?
adb can start a shell so you could create the shell with adb shell then redirect stdin and stdout to write to it directly
No measurable performance gain would come from the "optimization" you are proposing. The adb code is pretty efficient. The overhead it introduces is very minimal. Specially in the client part. Most of the delay comes from waiting for the device's response anyway.
So stop overcomplicating things and just run multiple adb shell sessions. If loading the adb binary bothers you so much - just use sockets to talk to the adb server directly. Take a look at the Mad Bee library code to see how to do it in c#. Or just use the library.
As for optimizing querying multiple system properties - just use adb shell getprop command to pull all properties at once.

Capture standard output of a python program as it happens

I'm trying to write a C# program that captures the standard output in a python program. My problem is that all of the output comes after the program has executed rather than when it actually happens. As an example, for this python program:
print "Hello"
time.sleep(2)
print "Hello"
I would expect to get "Hello", a two second gap, and then another "Hello". The actual result is a two second gap and then "Hello", "Hello".
If I run the above python script from the command line, I get the desired behaviour. If the command prompt can do this, then I should be able to mimic that functionality without having to flush the buffer repeatedly.
I'm using this to run the process from C#:
_proc = new Process
{
StartInfo = new ProcessStartInfo
{
FileName = "C:\\Python27\\python.exe",
Arguments = pyScript,
RedirectStandardError = true,
UseShellExecute = false,
RedirectStandardOutput = true,
CreateNoWindow = true
}
};
_proc.OutputDataReceived += ProcOnOutputDataReceived;
_proc.Start();
_proc.BeginOutputReadLine();
I can run this C# code (and changing the ProcessStartInfo properties above to run C# executable) and it behaves correctly:
Console.WriteLine("Hello");
Thread.Sleep(2000);
Console.WriteLine("Hello");
With this code I get "Hello", a two second gap, and then another "Hello".
Any idea why? How can I get the python interpreter to send the standard output as it happens?
I know this is old but running python with -u option (unbuffered) seems to be what you were after.
Unbuffered will not wait before flushing output.
Running python without this option, output is flushed when:
The buffer is full
You call sys.stdout.flush()
The process ends
You need to flush the output buffer.
import sys
sys.stdout.flush()
See this question: How to flush output of Python print?
Good
(Capture standard output of a python program as it happens)
Em console is NEED:
import sys
sys.stdout.flush()

Getting output from one executable in an other one

I'm currently trying to get the output of an executable console-app into an other one. To be exact, a little overview of what I'm trying to do:
I have one executable which I cannot edit and neither see it's code. It writes some (quite a bunch to be honest) lines into the console when executed.
Now I want to write another executable that starts the one above and reads the things it writes.
Seems simple to me, so I started coding but ended up with an error message saying that StandardOut has not been redirected or the process hasn't started yet.
I tried it using this kinda structure (C#):
Process MyApp = Process.Start(#"C:\some\dirs\foo.exe", "someargs");
MyApp.Start();
StreamReader _Out = MyApp.StandardOutput;
string _Line = "";
while ((_Line = _Out.ReadLine()) != null)
Console.WriteLine("Read: " + _Line);
MyApp.Close();
I can open the executable and it also does open the one inside, but as soon as it comes to reading the returned values, the app crashes.
What am I doing wrong?!
Take a look at the documentation for the Process.StandardOutput property. You will need to set a boolean indicating that you want the stream redirected as well as disabling shell execute.
Note from the documentation:
To use StandardOutput, you must set ProcessStartInfo..::.UseShellExecute to false, and you must set ProcessStartInfo..::.RedirectStandardOutput to true. Otherwise, reading from the StandardOutput stream throws an exception
You would need to change your code a little bit to adjust for the changes:
Process myApp = new Process(#"C:\some\dirs\foo.exe", "someargs");
myApp.StartInfo.UseShellExecute = false;
myApp.StartInfo.RedirectStandardOutput = false;
myApp.Start();
string output = myApp.StandardOutput.ReadToEnd();
p.WaitForExit();
you could try setting processStartInfo.RedirectStandardOutput = true;
As noted above, you can use RedirectStandardOutput as here.
Another, dirtier way is something like
using (Process child = Process.Start
("cmd", #"/c C:\some\dirs\foo.exe someargs > somefilename"))
{
exeProcess.WaitForExit();
}
And then read its output from somefilename

Categories