I am executing an ffmpeg command with a very complex filter and with a pipe within a C# application. I am putting images into the ffmpeg input stream (pipe)for rendering this images as overlays to the final video.
I want to render images with the pipe until the pipe closes. Unfortunately, I do not know how I can recognize that the pipe of the ffmpeg process has closed. Is there any possibility to recognize this event within C#?
The process is started like:
this._ffmpegProcess = new Process
{
StartInfo = new ProcessStartInfo
{
FileName = this._ffmpegPath,
UseShellExecute = false,
RedirectStandardInput = true,
RedirectStandardOutput = true,
RedirectStandardError = true,
CreateNoWindow = true
}
};
this._ffmpegProcess.StartInfo.Arguments = ffmpegCmd;
this._ffmpegProcess.OutputDataReceived += this.Proc_DataReceived;
this._ffmpegProcess.ErrorDataReceived += this.Proc_DataReceived;
this._ffmpegProcess.Exited += this._ffmpegProcess_Exited;
this._ffmpegProcess.Start();
this._ffmpegProcess.BeginOutputReadLine();
this._ffmpegProcess.BeginErrorReadLine();
The rendering happens within a timer:
this._renderOverlayTimer = new Timer(this.RenderOverlay);
this._renderOverlayTimer.Change(0, 30);
The timer is started right after starting the ffmpeg process:
private void RenderOverlay(object state)
{
using (var ms = new MemoryStream())
{
using (var img = GetImage(...))
{
img.Save(ms, ImageFormat.Png);
ms.WriteTo(this._ffmpegProcess.StandardInput.BaseStream);
}
}
}
The problem is that I always receive a "The pipe has ended" error at "ms.WriteTo()".
I now use a named pipe and trace the number of frames to process. The named pipe is closed right after the last frame is processed. This solution leads to a correct video without IOExceptions.
Related
I am running into an issue reading the output/input of a DOSBox Process started by my C# console application. DOSBox has an encoding for code page 437, but setting up my StreamReader with that encoding is giving me a NotSupportedException. It is worth the noting, the StreamReader/Writer are not throwing an error when being set up, but no input/output is being sent. Upon debugging, the exception is thrown throughout the object.
This is how I am starting my DOSBox Process:
ProcessStartInfo startInfo = new ProcessStartInfo()
{
FileName = "<path-to-DOSBox.exe>",
Arguments = "<path-to-game> -noconsole",
UseShellExecute = false,
RedirectStandardInput = true,
RedirectStandardOutput = true,
RedirectStandardError = true,
};
process = new Process();
process.StartInfo = startInfo;
process.Start();
And then this is how I am attempting to read/write to the DOSBox console:
input = new StreamWriter(process.StandardInput.BaseStream, Encoding.GetEncoding(437));
input.WriteLine(command);
output = new StreamReader(process.StandardOutput.BaseStream, Encoding.GetEncoding(437));
Console.WriteLine(output.ReadToEnd());
I am registering the specific encoding with:
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
Here is a screenshot upon inspection of my StreamReader output:
I am trying to run a process in my code looping through each file and performing some task. Sometimes the EXE gets stuck when it runs across some files. And this doesn't move forward. If this kind of situation occurs how can I skip the files which is stuck in the process and save the file into a TXT file which has failed. And further move with the next file for processing. I tried to check it online and didn't get much help. Please suggest how can I get this done?
foreach (var file in files)
{
ProcessStartInfo processStartInfo = new ProcessStartInfo
{
FileName = exedir + "\\testing.exe",
Arguments = "-a -h -i -l " + file,
RedirectStandardOutput = true,
UseShellExecute = false,
CreateNoWindow = true
};
using (Process exeProcess = Process.Start(processStartInfo ))
{
var line = string.Empty;
using (StreamReader streamReader = exeProcess.StandardOutput)
{
while (!streamReader.EndOfStream)
{
var content = streamReader.ReadLine();
// Doing some task here
}
}
exeProcess.WaitForExit(10000);
}
}
I want to run an exe utility which produces some binary output (which is an image) based on given parameters.
I need to get the binary output of the utility as a stream (MemoryStream) and the Errors (if any).
It is also required to have a Timeout such that if app hangs or it takes a long time to respond, break its execution and return what ever result it output until that time.
One way is to use cmd like as:
cmd /C ... >out.jpg 2>err.txt
But this requires a temp file which is not acceptable, i want to capture output data directly as it should be called many times and output size is large.
The basic code to run the process is as following:
using (var p = new Process())
{
//p.StartInfo = new ProcessStartInfo("cmd", "/C " + command + " > x.jpg 2> err.txt");
p.StartInfo = new ProcessStartInfo()
{
FileName = #"x.exe", //dcraw
Arguments = #"-c -e x.jpg",
UseShellExecute = false, //it is usefull for non-exe like as for a Word file.
RedirectStandardOutput = true,
RedirectStandardError = true,
CreateNoWindow = true,
WindowStyle = ProcessWindowStyle.Hidden
};
int timeout = 5000; //msec
p.Start();
//MemoryStream outputStream = ...
//p.OutputDataReceived += ... //can't use this as it returns text not binary data
//string errorText = ...
p.WaitForExit();
//return new pResult(Image.FromStream(outputStream), errorText, isTimedout, ...);
}
How to get outputStream and errorText with specified timeout?
Note: I know there are several questions about process.start and output redirecting. Most of them are about capturing text output not binary data (like this or that). And this binary question does not answer my question completely as stated here (capturing both output and errors at the same time may cause dead-end locks, no timeout specified).
I am using GnuPlot from a C# application. I'd like to read the GnuPlot PNG output directly from Standard Output rather than saving to a file and then reading it. My code looks like this right now:
string Path = #"C:\Program Files\gnuplot\bin\gnuplot.exe";
Process GnuplotProcess = new Process();
GnuplotProcess.StartInfo.FileName = Path;
GnuplotProcess.StartInfo.UseShellExecute = false;
GnuplotProcess.StartInfo.RedirectStandardInput = true;
GnuplotProcess.StartInfo.RedirectStandardOutput = true;
GnuplotProcess.Start();
StreamWriter SW = GnuplotProcess.StandardInput;
StreamReader SR = GnuplotProcess.StandardOutput;
SW.WriteLine("set terminal pngcairo size 300,200");
foreach (LoadCaseOutput LCO in LoadCases)
{
foreach (LoadCaseOutput.MemberOutput MO in LCO.Members)
{
SW.WriteLine("plot " + MO.GenerateAFEquation(P));
MO.AFImage = Image.FromStream(SR.BaseStream);
}
}
SW.WriteLine("exit");
GnuplotProcess.Close();
Right now this seems to stall at the Image.FromStream() line. What's going wrong?
update (I have updated my code to reflect my comment below)
It appears the problem is when the "exit" command is sent to gnuPlot. Without the exit command sent to gnuPlot, the program waits.
I took your example and was able to get the program to complete by moving the gnuPlot exit command up in the execution tree.
string Path = #"z:\tools\gnuplot\bin\gnuplot.exe";
Process GnuplotProcess = new Process();
GnuplotProcess.StartInfo.FileName = Path;
GnuplotProcess.StartInfo.UseShellExecute = false;
GnuplotProcess.StartInfo.RedirectStandardInput = true;
GnuplotProcess.StartInfo.RedirectStandardOutput = true;
GnuplotProcess.Start();
StreamWriter SW = GnuplotProcess.StandardInput;
StreamReader SR = GnuplotProcess.StandardOutput;
SW.WriteLine("set terminal pngcairo size 300,200");
SW.WriteLine("plot f(x) = sin(x*a), a = .2, f(x), a = .4, f(x)");
SW.WriteLine("exit");
Image png = Image.FromStream(SR.BaseStream);
png.Save(#"z:\tools\try3a.png");
GnuplotProcess.Close();
This did correctly generate a PNG file. For testing, I did try reading from the stream before sending the exit command. The program waits on the FromStream call.
Matt
The problem is not with the reading otherwise you would be getting an exception, replace the reading bit with this:
Image.FromStream(oFileStream, false, true)
It will validate the image as soon as it receives the first bytes and you go from there.
I'm trying to automate svnadmin dump using C# ProcessStartInfo.
The way I've done it on the command line is like so,
svnadmin dump c:\Repositories\hackyhacky > c:\backup\hackyhacky.svn_dump
Works a treat and dumps successfully, and I can verify this by restoring it into another repository like so
svnadmin load c:\Repositories\restore_test < c:\backup\hackyhacky.svn_dump
Which restores successfully - yay!
Now... I need to replicate the command line piping into another file using C#, but for some reason
var startInfo = new ProcessStartInfo(Path.Combine(SvnPath, "svnadmin"),"dump c:\Repositories\hackyhacky")
{CreateNoWindow = true, RedirectStandardOutput = true,RedirectStandardError = true,UseShellExecute = false};
process.StartInfo = startInfo;
process.Start();
StreamReader reader = process.StandardOutput;
char[] standardOutputCharBuffer = new char[4096];
byte[] standardOutputByteBuffer;
int readChars = 0;
long totalReadBytes = 0;
// read from the StandardOutput, and write directly into another file
using (StreamWriter writer = new StreamWriter(#"C:\backup\hackyhacky.svn_dump", false)) {
while (!reader.EndOfStream) {
// read some chars from the standard out
readChars = reader.Read(standardOutputCharBuffer, 0, standardOutputCharBuffer.Length);
// convert the chars into bytes
standardOutputByteBuffer = reader.CurrentEncoding.GetBytes(standardOutputCharBuffer);
// write the bytes out into the file
writer.Write(standardOutputCharBuffer.Take(readChars).ToArray());
// increment the total read
totalReadBytes += standardOutputByteBuffer.Length;
}
}
Dumps the same repo into hackyhacky.svn_dump.
But when I run my load command line now
svnadmin load c:\Repositories\restore_test < c:\backup\hackyhacky.svn_dump
I get a checksum error weird-error!
svnadmin load c:\Repositories\restore_test < c:\backup\hackyhacky.svn_dump
< Started new transaction, based on original revision 1
* adding path : Dependencies ... done.
* adding path : Dependencies/BlogML.dll ...svnadmin: Checksum mismatch, fil
e '/Dependencies/BlogML.dll':
expected: d39863a4c14cf053d01f636002842bf9
actual: d19831be151d33650b3312a288aecadd
I'm guessing this is to do with how I'm redirecting and reading the StandardOutput.
Does anyone know the right way to mimic the command line file piping behaviour in C#?
Any help is greatly appreciated.
-CV
UPDATE
I've tried using a BinaryWriter and using the standardOutputByteBuffer to write to the file, but that doesn't work either. I get a different error about incorrect header format or something.
Alright! If you can't beat em, join em....
I found a post where the author pipes to a file directly within the Process StartInfo, and claims it works.
http://weblogs.asp.net/israelio/archive/2004/08/31/223447.aspx
It didn't work for me, as described in another gentleman's post
http://webcache.googleusercontent.com/search?q=cache:http://www.deadbeef.com/index.php/redirecting_the_output_of_a_program_to_a
He writes a batch file first with the piping and then executes it...
amWriter bat = File.CreateText("foo.bat");
bat.WriteLine("#echo off");
bat.WriteLine("foo.exe -arg >" + dumpDir + "\\foo_arg.txt");
bat.Close();
Process task = new Process();
task.StartInfo.UseShellExecute = false;
task.StartInfo.FileName = "foo.bat";
task.StartInfo.Arguments = "";
task.Start();
task.WaitForExit();
In his words:
Truly horrific, but it has the
advantage of working!
To be perfectly frank, I'm a bit annoyed this has taken me as long as it has, so the batch file solution works well so I'm going to stick with it.
The first thing I'd try is writing the character array - not the byte array - to the file.
That should work as long as the output is just simple text. There's other encoding issues, though, if the output is more complex: you're writing the file as UTF-8, whereas the default for command-line output (I believe) is Windows-1252.
I've been trying to do this very thing, and just stumbled on another solution to the problem used by Hector Sosa's svnmanagerlib sourceforge project:
The key to solving this was surrounding the call to WaitForExit() with
file operations. Also needed to make sure to write the output to disk.
Here are the relevant lines:
File.AppendAllText( destinationFile, myOutput.ReadToEnd() );
svnCommand.WaitForExit(); File.AppendAllText(destinationFile,
myOutput.ReadToEnd() );
Notice that I make a call to File.AppendAllText() twice. I have found
that the output stream does not write everything during the first call
to File.AppendAllText() on some occasions.
public static bool ExecuteWritesToDiskSvnCommand(string command, string arguments, string destinationFile, out string errors)
{
bool retval = false;
string errorLines = string.Empty;
Process svnCommand = null;
ProcessStartInfo psi = new ProcessStartInfo(command);
psi.RedirectStandardOutput = true;
psi.RedirectStandardError = true;
psi.WindowStyle = ProcessWindowStyle.Hidden;
psi.UseShellExecute = false;
psi.CreateNoWindow = true;
try
{
Process.Start(psi);
psi.Arguments = arguments;
svnCommand = Process.Start(psi);
StreamReader myOutput = svnCommand.StandardOutput;
StreamReader myErrors = svnCommand.StandardError;
File.AppendAllText(destinationFile, myOutput.ReadToEnd());
svnCommand.WaitForExit();
File.AppendAllText(destinationFile, myOutput.ReadToEnd());
if (svnCommand.HasExited)
{
errorLines = myErrors.ReadToEnd();
}
// Check for errors
if (errorLines.Trim().Length == 0)
{
retval = true;
}
}
catch (Exception ex)
{
string msg = ex.Message;
errorLines += Environment.NewLine + msg;
}
finally
{
if (svnCommand != null)
{
svnCommand.Close();
}
}
errors = errorLines;
return retval;
}