c# - How to asynchronously stream output from ffmpeg - c#

I created a method to call ffmpeg binaries and do stuff for me. It worked perfectly fine on a standard console application. I am trying to make a Windows Form Application version but there are few problems. The app freezes (but the progress bar is still updating) when the ffmpeg process is still running. The textboxes are not being updated. I cannot move the app window. I suspect this is because of the loop and I googled some stuff and found out that I might need to do this asynchronously but how do I do that exactly.
public void ffmpeg(string ffmpeg_exe, string args)
{
Process p = new Process();
p.StartInfo.FileName = ffmpeg_exe;
p.StartInfo.RedirectStandardError = true;
p.StartInfo.CreateNoWindow = true;
p.StartInfo.Arguments = args;
p.StartInfo.UseShellExecute = false;
p.Start();
StreamReader reader = p.StandardError;
string line;
string size = "", current_duration = "", duration = "";
while ((line = reader.ReadLine()) != null)
{
if (!string.IsNullOrEmpty(line))
{
if (line.Contains("Duration") && line.Contains("bitrate") && line.Contains("start"))
{
duration = RemoveWhitespace(Between(line, "Duration:", ", start"));
totaltime.Text = duration;
}
if (line.Contains("frame=") && line.Contains("size=") && line.Contains("time="))
{
size = RemoveWhitespace(Between(line, "size=", " time"));
current_duration = RemoveWhitespace(Between(line, "time=", " bitrate"));
progressBar_main.Value = Convert.ToInt32(Math.Round(TimeSpan.Parse(current_duration.Substring(0, current_duration.Length -3)).TotalSeconds * 100 / TimeSpan.Parse(duration.Substring(0,duration.Length-3)).TotalSeconds, 3));
current_time.Text = current_duration;
filesize.Text = size;
}
}
}
p.Close();
current_time.Text = "";
filesize.Text = "";
totaltime.Text = "";
progressBar_main.Value = 0;
}

Try using Task like this :
Action action = () =>
{
//Do your streaming here
};
Then start it :
Task t2 = Task.Factory.StartNew(action);
Hope this was useful.

Related

How to show the C# windows form application?

I have an application developed in c#. This application send sms to client. This application was doing good till yesterday but suddenly this morning I find that if I start the application it is starting but not showing anywhere in the desktop not even in the task bar. I tried to stop it using the Task manager but I could not find it in the task manager. I have included some function in my code which restricts to run two instances of this application at the same time. So, when I try to run again it shows this Application is already running. I can only close it using TCP Viewer which helps to close background program. But I have not written any line of code to run it on the background.
Even when I am debugging it is going to the debug points correctly but not showing the form. Please help me on this.
Here is my form load code as sample:
private void Form1_Load(object sender, EventArgs e)
{
tcnt = 1;
thistime = DateTime.Now.Date;
techtime = "09:00:01";
techdate = DateTime.Now.Date;
dntime = DateTime.Now.Date;
rebillingdate = DateTime.Now.Date;
checkState[1] = true;
checkState[2] = false;
checkState[3] = false;
this.tmrLog.Enabled = true;
this.stStatus.Text = "Form loading";
string myDate = DateTime.Now.Date.ToString("yyyyMMdd");
string myFile = null;
string myDir = "logs\\schedule\\";
StreamReader sr = default(StreamReader);
string str = null;
DirectoryInfo dir = new DirectoryInfo(myDir);
//DirectoryInfo DirItem = default(DirectoryInfo);
FileInfo[] dirfiles = null;
//FileInfo myf = default(FileInfo);
this.Controls.Add(lsLog);
lsLog.Items.Clear();
if (dir.Exists == true)
{
foreach (var DirItem in dir.GetDirectories())
{
dirfiles = DirItem.GetFiles();
foreach (var myf in dirfiles)
{
if (myf.Name.IndexOf("schedule") > 0 & myf.Name.IndexOf(myDate) > 0)
{
myFile = "logs\\schedule\\" + DirItem.Name + "\\" + myf.Name;
if (DirItem.Name == "48" | DirItem.Name == "IPX")
{
sr = File.OpenText(myFile);
while (sr.Peek() != -1)
{
str = sr.ReadLine();
lsLog.Items.Insert(0, str);
}
sr.Close();
}
}
}
}
}
tmrLog.Enabled = true;
}

Running two exe files in one c# project

I'm trying to write a program which executes 2 different .exe files and outputs their results to a text file. When I execute them separately, they work fine, but when I try to execute them both, the second process doesn't run. Can anyone help?
Here is the code. Player1.exe and Player2.exe are console applications returning 0 or 1.
static void Main(string[] args)
{
Process process1 = new Process();
process1.StartInfo.FileName = "C:/Programming/Tournament/Player1.exe";
process1.StartInfo.Arguments = "";
process1.StartInfo.UseShellExecute = false;
process1.StartInfo.RedirectStandardOutput = true;
process1.Start();
var result1 = process1.StandardOutput.ReadToEnd();
process1.Close();
Process process2 = new Process();
process2.StartInfo.FileName = "C:/Programming/Tournament/Player2.exe";
process2.StartInfo.Arguments = "";
process2.StartInfo.UseShellExecute = false;
process2.StartInfo.RedirectStandardOutput = true;
process2.Start();
string result2 = process2.StandardOutput.ReadToEnd().ToString();
process2.Close();
string resultsPath = "C:/Programming/Tournament/Results/Player1vsPlayer2.txt";
if (!File.Exists(resultsPath))
{
StreamWriter sw = File.CreateText(resultsPath);
sw.WriteLine("Player1 " + "Player2");
sw.WriteLine(result1 + " " + result2);
sw.Flush();
}
}
1.
you wrote in a comment: "The program doesn't even reach to process2. I tested that by putting a breakpoint."
process1.Start() may be throwing an exception. Rather than setting a breakpoint at process2, step through the lines and find the exception. Or better yet, add a try/catch block and report an error.
2.
Another possibility is that ReadToEnd is not behaving as expected. You can Peek and see if there is more data to read by looping like this:
var result1 = new StringBuilder()
while (process1.StandardOutput.Peek() > -1)
{
result1.Append(process1.StandardOutput.ReadLine());
}
3.
If these processes don't immediately provide data and end, then you need to get them both started before you begin waiting on their StandardOutput. Call Start() on each process before doing the reads.
I don't know too much about using process, but I use this approach when I need separate things to run at once. I didn't pull in the bottom portion of your project, but check this out to see if it helps in any way.
class Program
{
static void Main(string[] args)
{
Process process1 = new Process();
process1.StartInfo.FileName = "C:/Programming/Tournament/Player1.exe";
process1.StartInfo.Arguments = "";
process1.StartInfo.UseShellExecute = false;
process1.StartInfo.RedirectStandardOutput = true;
Process process2 = new Process();
process2.StartInfo.FileName = "C:/Programming/Tournament/Player2.exe";
process2.StartInfo.Arguments = "";
process2.StartInfo.UseShellExecute = false;
process2.StartInfo.RedirectStandardOutput = true;
Thread T1 = new Thread(delegate() {
doProcess(process1);
});
Thread T2 = new Thread(delegate() {
doProcess(process2);
});
T1.Start();
T2.Start();
}
public static void doProcess(Process myProcess)
{
myProcess.Start();
var result1 = myProcess.StandardOutput.ReadToEnd();
myProcess.Close();
}
}

how to handle winrar diagnostic messages from code

I am developing an windows application in that application i use winrar command line utility to make rar files.
Code
public static string RarFiles(string rarPackagePath,
Dictionary<int, string> accFiles)
{
string error = "";
try
{
string[] files = new string[accFiles.Count];
int i = 0;
foreach (var fList_item in accFiles)
{
files[i] = "\"" + fList_item.Value;
i++;
}
string fileList = string.Join("\" ", files);
fileList += "\"";
System.Diagnostics.ProcessStartInfo sdp = new System.Diagnostics.ProcessStartInfo();
string cmdArgs = string.Format("A {0} {1} -ep1 -r",
String.Format("\"{0}\"", rarPackagePath),
fileList);
sdp.ErrorDialog = true;
sdp.UseShellExecute = true;
sdp.Arguments = cmdArgs;
sdp.FileName = winrarPath;//Winrar.exe path
sdp.CreateNoWindow = false;
sdp.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden;
System.Diagnostics.Process process = System.Diagnostics.Process.Start(sdp);
process.WaitForExit();
//string s = process.StandardOutput.ReadToEnd();
error = "OK";
}
catch (Exception ex)
{
error = ex.Message;
}
return error;
}
Can any one tell me how can i handle winrar diagnostic messages.
i think u missed some parts :
try adding these :
sdp.StartInfo.RedirectStandardOutput = true;
after Start, add a string to get the output. After that, call WaitForExit()
sdp.Start();
string output = stillc.StandardOutput.ReadToEnd();
sdp.WaitForExit();
*Note : This works only if an output is shown in the console window.
Hope it helps :)

Pipes between C# and Delphi, How to make it work?

I have a console application (Host.exe) that is written in Delphi. It accepts stdin readln and responses to stdout by writeln.
Now, I want to use Host.exe in C# application in a way that C# gives input to Host.exe and gets the output from Host.exe
Ideally, I write the code below but it doesn't work: it hangs somewhere in the outputReader.ReadLine();
System.IO.File.WriteAllText(tmp, vbs);
Process pProcess = new Process();
pProcess.StartInfo.WindowStyle = ProcessWindowStyle.Normal;
//strCommand is path and file name of command to run
pProcess.StartInfo.FileName = #"Host.exe";
pProcess.StartInfo.Arguments = "\"runa " + tmp +"\"";
// runs script file tmp in background
pProcess.StartInfo.CreateNoWindow = true;
pProcess.StartInfo.UseShellExecute = false;
pProcess.StartInfo.RedirectStandardOutput = true;
pProcess.StartInfo.RedirectStandardInput = true;
pProcess.StartInfo.RedirectStandardError = true;
pProcess.Start();
StreamWriter inputWriter = pProcess.StandardInput;
StreamReader outputReader = pProcess.StandardOutput;
while (true)
{
inputWriter.WriteLine("getmsg");
inputWriter.Flush();
string s = outputReader.ReadLine(); // then do something with it
inputWriter.WriteLine("progressglobal");
inputWriter.Flush();
string p = outputReader.ReadLine();
if (p == "100")
{
break;
}
Application.DoEvents();
Thread.Sleep(1000);
}
inputWriter.WriteLine("exit");
inputWriter.Flush();
pProcess.WaitForExit();
Many thanks for any suggestions in advance !
You read the line twice:
string s = outputReader.ReadLine();
and
string p = outputReader.ReadLine();
It seems you only need the last line as the variable s is not used.

How to get the video duration using FFMPEG in C# asp.net

I want to get the video file duration in string using C#. I searched the internet and all i get is:
ffmpeg -i inputfile.avi
And every1 say that parse the output for duration.
Here is my code which is
string filargs = "-y -i " + inputavi + " -ar 22050 " + outputflv;
Process proc;
proc = new Process();
proc.StartInfo.FileName = spath;
proc.StartInfo.Arguments = filargs;
proc.StartInfo.UseShellExecute = false;
proc.StartInfo.CreateNoWindow = false;
proc.StartInfo.RedirectStandardOutput = false;
try
{
proc.Start();
}
catch (Exception ex)
{
Response.Write(ex.Message);
}
try
{
proc.WaitForExit(50 * 1000);
}
catch (Exception ex)
{ }
finally
{
proc.Close();
}
Now please tell me how can i save the output string and parse it for the video duration.
Thanks and regards,
There is another Option to get Video Length ,by using Media Info DLL
Using Ffmpeg :
proc.StartInfo.RedirectErrorOutput = true;
string message = proc.ErrorOutput.ReadToEnd();
Filtering shouldn't be an issue ,so do it you're self.
PS : using ffmpeg you should not read the StandardOutput but ErrorOutput i dont know why ,but it work's only like that.
FFmpeg is a little bit of an adventure to parse. But in any case, here's what you need to know.
First, FFmpeg doesn't play well with RedirectOutput options
What you'll need to do is instead of launching ffmpeg directly, launch cmd.exe, passing in ffmpeg as an argument, and redirecting the output to a "monitor file" through a command line output like so... note that in the while (!proc.HasExited) loop you can read this file for real-time FFmpeg status, or just read it at the end if this is a quick operation.
FileInfo monitorFile = new FileInfo(Path.Combine(ffMpegExe.Directory.FullName, "FFMpegMonitor_" + Guid.NewGuid().ToString() + ".txt"));
string ffmpegpath = Environment.SystemDirectory + "\\cmd.exe";
string ffmpegargs = "/C " + ffMpegExe.FullName + " " + encodeArgs + " 2>" + monitorFile.FullName;
string fullTestCmd = ffmpegpath + " " + ffmpegargs;
ProcessStartInfo psi = new ProcessStartInfo(ffmpegpath, ffmpegargs);
psi.WorkingDirectory = ffMpegExe.Directory.FullName;
psi.CreateNoWindow = true;
psi.UseShellExecute = false;
psi.Verb = "runas";
var proc = Process.Start(psi);
while (!proc.HasExited)
{
System.Threading.Thread.Sleep(1000);
}
string encodeLog = System.IO.File.ReadAllText(monitorFile.FullName);
Great, now you've got the log of what FFmpeg just spit out. Now to get the duration. The duration line will look something like this:
Duration: 00:10:53.79, start: 0.000000, bitrate: 9963 kb/s
Clean up the results into a List<string>:
var encodingLines = encodeLog.Split(System.Environment.NewLine[0]).Where(line => string.IsNullOrWhiteSpace(line) == false && string.IsNullOrEmpty(line.Trim()) == false).Select(s => s.Trim()).ToList();
... then loop through them looking for Duration.
foreach (var line in encodingLines)
{
// Duration: 00:10:53.79, start: 0.000000, bitrate: 9963 kb/s
if (line.StartsWith("Duration"))
{
var duration = ParseDurationLine(line);
}
}
Here's some code that can do the parse for you:
private TimeSpan ParseDurationLine(string line)
{
var itemsOfData = line.Split(" "[0], "="[0]).Where(s => string.IsNullOrEmpty(s) == false).Select(s => s.Trim().Replace("=", string.Empty).Replace(",", string.Empty)).ToList();
string duration = GetValueFromItemData(itemsOfData, "Duration:");
return TimeSpan.Parse(duration);
}
private string GetValueFromItemData(List<string> items, string targetKey)
{
var key = items.FirstOrDefault(i => i.ToUpper() == targetKey.ToUpper());
if (key == null) { return null; }
var idx = items.IndexOf(key);
var valueIdx = idx + 1;
if (valueIdx >= items.Count)
{
return null;
}
return items[valueIdx];
}
Just check it out::
//Create varriables
string ffMPEG = System.IO.Path.Combine(Application.StartupPath, "ffMPEG.exe");
system.Diagnostics.Process mProcess = null;
System.IO.StreamReader SROutput = null;
string outPut = "";
string filepath = "D:\\source.mp4";
string param = string.Format("-i \"{0}\"", filepath);
System.Diagnostics.ProcessStartInfo oInfo = null;
System.Text.RegularExpressions.Regex re = null;
System.Text.RegularExpressions.Match m = null;
TimeSpan Duration = null;
//Get ready with ProcessStartInfo
oInfo = new System.Diagnostics.ProcessStartInfo(ffMPEG, param);
oInfo.CreateNoWindow = true;
//ffMPEG uses StandardError for its output.
oInfo.RedirectStandardError = true;
oInfo.WindowStyle = ProcessWindowStyle.Hidden;
oInfo.UseShellExecute = false;
// Lets start the process
mProcess = System.Diagnostics.Process.Start(oInfo);
// Divert output
SROutput = mProcess.StandardError;
// Read all
outPut = SROutput.ReadToEnd();
// Please donot forget to call WaitForExit() after calling SROutput.ReadToEnd
mProcess.WaitForExit();
mProcess.Close();
mProcess.Dispose();
SROutput.Close();
SROutput.Dispose();
//get duration
re = new System.Text.RegularExpressions.Regex("[D|d]uration:.((\\d|:|\\.)*)");
m = re.Match(outPut);
if (m.Success) {
//Means the output has cantained the string "Duration"
string temp = m.Groups(1).Value;
string[] timepieces = temp.Split(new char[] {':', '.'});
if (timepieces.Length == 4) {
// Store duration
Duration = new TimeSpan(0, Convert.ToInt16(timepieces[0]), Convert.ToInt16(timepieces[1]), Convert.ToInt16(timepieces[2]), Convert.ToInt16(timepieces[3]));
}
}
With thanks,
Gouranga Das.

Categories

Resources