41

I run ffmpeg like this:

System.Diagnostics.Process p = new System.Diagnostics.Process();
p.StartInfo = new System.Diagnostics.ProcessStartInfo(ffmpegPath, myParams);
p.Start();
p.WaitForExit();

... but the problem is that the console with ffmpeg pops up and disappears right away, so I can't get any feedback. I don't even know if the process ran correctly.

So how can I either:

  • Tell the console to stay opened

  • Retrieve in the C# what the console displayed

Lucas Jones
  • 19,767
  • 8
  • 75
  • 88
marcgg
  • 65,020
  • 52
  • 178
  • 231

4 Answers4

58

What you need to do is capture the Standard Output stream:

p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.UseShellExecute = false;
// instead of p.WaitForExit(), do
string q = "";
while ( ! p.HasExited ) {
    q += p.StandardOutput.ReadToEnd();
}

You may also need to do something similar with StandardError. You can then do what you wish with q.

It is a bit finicky, as I discovered in one of my questions

As Jon Skeet has pointed out, it is not smart performance-wise to use string concatenation like this; you should instead use a StringBuilder:

p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.UseShellExecute = false;
// instead of p.WaitForExit(), do
StringBuilder q = new StringBuilder();
while ( ! p.HasExited ) {
    q.Append(p.StandardOutput.ReadToEnd());
}
string r = q.ToString();
Community
  • 1
  • 1
Lucas Jones
  • 19,767
  • 8
  • 75
  • 88
  • 6
    Right basic stuff, but I wouldn't recommend building up the string like that. Use a StringBuilder :) – Jon Skeet Sep 07 '09 at 18:51
  • 1
    actually there's an error: StandardOut has not been redirected or the process hasn't started yet. I'm trying to figure out what's going on – marcgg Sep 07 '09 at 18:54
  • Hmmm... I'm not sure about that. – Lucas Jones Sep 07 '09 at 18:56
  • If you still have a problem post your code to http://pastebin.com and I'll have a look. – Lucas Jones Sep 07 '09 at 19:05
  • I tried changing some stuff but it's still not working. Here's the full code: http://pastebin.com/m50f26695 – marcgg Sep 07 '09 at 19:07
  • I'm supposed to use ffmpegPath to call ffmpeg, but "ffmpeg.exe" is also working for debug. – marcgg Sep 07 '09 at 19:09
  • apparently when I run the debuger the program ends up in the loop while(! p.HasExited ) with p.HasExited == true :\ – marcgg Sep 07 '09 at 19:17
  • Ah, sorry for not making that clear - `p.StartInfo.RedirectStandardOutput = true` needs to go before the `p.Start()`. – Lucas Jones Sep 07 '09 at 19:23
  • Thanks. It fixed it... but now I have "The Process object must have the UseShellExecute property set to false in order to redirect IO streams." Is this something I have control over? – marcgg Sep 07 '09 at 19:25
  • I've edited and accepted your answer. If you could incorporate jon's answer that would be perfect :) But if you don't that was already helpful! thanks a lot – marcgg Sep 07 '09 at 19:30
  • I've added an example of using a StringBuilder at the bottom. :) – Lucas Jones Sep 07 '09 at 19:54
  • 2
    There is a race condition with this approach: If the process ends before you enter the while loop, the output will not be read. – chiccodoro Sep 04 '13 at 13:32
  • @chiccodoro is correct. There is a race condition, and the `while(!p.HasExited)` loop is never entered when the process is quick to run. E.g., try ipconfig. – gmm Sep 09 '13 at 15:53
  • @gmm - thank you for veryfying. For the reference of any future readers, I have created an alternative answer to fix this. – chiccodoro Sep 23 '13 at 13:55
22

Lucas' answer has a race condition: If the process finishes quickly the while loop is left (or never entered) even if there is some output left, that is you might miss out on some data. To prevent that, another ReadToEnd should be done after the process exited.

(Note that in comparison to the old version of my answer, I can no longer see a need for WaitForExit once the process.HasExited flag is true, so this boils down to:)

using (var process = Process.Start(startInfo))
{
    var standardOutput = new StringBuilder();

    // read chunk-wise while process is running.
    while (!process.HasExited)
    {
        standardOutput.Append(process.StandardOutput.ReadToEnd());
    }

    // make sure not to miss out on any remaindings.
    standardOutput.Append(process.StandardOutput.ReadToEnd());

    // ...
}
chiccodoro
  • 14,407
  • 19
  • 87
  • 130
  • I believe the first example creates a deadlock situation. If the process writes too much output, it will block waiting for an opportunity to write more output. This will not happen for output below a certain threshold. If it blocks, it is hung forever. [Until killed] – Cameron Jan 09 '15 at 01:05
  • @Cameron - I have updated the answer and removed the first code snippet. Furthermore, in the second snippet, I can't see a need for `WaitToExit` any longer: Once the while loop is left, it means the process has exited. However I am not sure about your claim. That would mean that even in a situation where you are not interested in the processes output you would always have to add a while-ReadToEnd structure wherevere in your code you want to start a process using `Process.Start` such as to make sure the process does not stand still waiting on "free space". – chiccodoro Jan 09 '15 at 11:43
  • I verified my claim after the post. Check it and see. – Cameron Jan 09 '15 at 18:09
  • 1
    `ReadToEnd()` is synchronous, and the shell process *will* stall if you redirect stdout but don't read from it, and the process fills its output buffer. On a related note, if you want both stdout and stderr, you need to use async I/O for one or both. See https://msdn.microsoft.com/en-us/library/system.diagnostics.process.beginoutputreadline(v=vs.110).aspx – fadden Aug 06 '18 at 22:01
4

For a more specific answer directly related to ffmpeg, passing the "-report" command into ffmpeg will make it dump a log into the current directory with what was said in the display of the process.

‘-report’

Dump full command line and console output to a file named program-YYYYMMDD-HHMMSS.log in the current directory. This file can be useful for bug reports. It also implies -loglevel verbose.

Note: setting the environment variable FFREPORT to any value has the same effect.

From FFMpeg Documentation.

Ben
  • 951
  • 12
  • 19
4

I know this question is old, but I'll add to it anyway.

If all you wish to do is display the output of a command line process, and you're spawning the process from a console window, you need only redirect the standard input (yes, I know it sounds wrong, but it works).

So:

System.Diagnostics.Process p = new System.Diagnostics.Process();
p.StartInfo = new System.Diagnostics.ProcessStartInfo(ffmpegPath, myParams);
p.UseShellExecute = false;
p.RedirectStandardInput = true;
p.Start();
p.WaitForExit();

Would do just fine.

Michael Vasquez
  • 111
  • 1
  • 2
  • 7