Run a command line program from a C Sharp app

I wanted a C Sharp app as a front end to the FFmpeg command-line app for converting MKV videos. This is how I ran FFmpeg silently and parsed the output.

Return to blog
Posted: November 5, 2019 22:01

Either my home media server or the media server client on my TV have a problem with MKV videos. Sometimes I cannot fast forward, sometimes it doesn't know the length of the video or the current position. I am not sure where the problem lies, but I know it has none of these problems with MP4 videos. Since both MKV and MP4 are basically containers for video and audio streams, I wanted to use FFmpeg to do the conversion.

I didn't want to have to either keep typing in long command-lines or start creating batch files for each type of conversion, so I wrote a C Sharp front-end for my conversions with FFmpeg. I wanted my app to call the FFmpeg program and monitor/parse the output. Here is the full function call. Note: the FFmpeg executable is currently hard coded in this version of the app, but I will be fixing that soon.

using System.Diagnostics;

private void RunConversion(string args)
{
    int pos, progress;
    string line;

    Process p = new Process();
    p.StartInfo.FileName = @"c:\Program Files\ffmpeg\bin\ffmpeg.exe";
    p.StartInfo.Arguments = args;
    p.StartInfo.UseShellExecute = false;
    p.StartInfo.RedirectStandardOutput = true;
    p.StartInfo.RedirectStandardError = true;
    p.StartInfo.CreateNoWindow = true;
    p.Start();
    //FFmpeg puts its output through standard error, not standard out, for some reason
    while ((line = p.StandardError.ReadLine()) != null)
    {
        //read and process each output, line by line
        line = line.Trim();
        //lines that start with frame= has a time stamp so we can calculate progress
        //frame=31210...
        if (line.Substring(0, 6) == "frame=")
        {
            //this is a progress line
            pos = line.IndexOf("time=");
            if (pos > -1)
            {
                //found a "time=00:21:41.63" entry, so update progress
                progress = HMStoSeconds(line.Substring(pos + 5, 8));
                progbarConvert.Value = progress;
            }
        }
    }
    progbarConvert.Value = progbarConvert.Maximum;
    p.WaitForExit();
    //dispose
    p.Dispose();
}

Setting up to run a new process involves the following.

Process p = new Process();
p.StartInfo.FileName = @"c:\Program Files\ffmpeg\bin\ffmpeg.exe";
p.StartInfo.Arguments = args;
p.StartInfo.UseShellExecute = false;
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.RedirectStandardError = true;
p.StartInfo.CreateNoWindow = true;
p.Start();

FileName sets the executable and Arguments are passed in. To disable the standard Windows command line output you need to set UseShellExecute to false and CreateNoWindow to true. To capture the output text you need to set both RedirectStandardOutput and RedirectStandardError to true. In this case FFmpeg puts all of its output through Standard Errror rather than Standard Out like most apps.

The basic output processing loop is as follows:

p.Start();
while ((line = p.StandardError.ReadLine()) != null)
{
    //processing code here
}
p.WaitForExit();
p.Dispose();

Start() runs the executable and the while loop reads any output from Standard Error, in this case. Once there is no more output, it will fall out of the loop. WaitForExit() lets the executable shut down and then we dispose of the process object that was created.

While this handled the the running of FFmpeg, I did start running into situations where the UI became unresponsive and even whited out when the FFmpeg command was running. In another blog post, I will show how I added threading to solve this problem.