C# FFmpeg rtsp byte stream - c#

How can I open a constant byte stream that I can read while it's playing? I am able to get a single image with a resolution of 100:75, but when I bump up the resolution the process never finishes.
I need help improving the performance this method, I'm sure it's possible but I know very little about FFmpeg.
I'd like to be able to:
List item
Get a full res image
Read while the process is running
Constantly read byte output from a live stream
This is how I get the single image, it's running on a background thread.
string stream = "-i " + url + " -vf scale=100:75 -an -vframes 1 -r 1 -f image2pipe pipe:bmp";
var process = new Process();
process.StartInfo.FileName = ffmpeg.exe;
process.StartInfo.Arguments = stream;
process.StartInfo.UseShellExecute = false;
process.StartInfo.RedirectStandardInput = true;
process.StartInfo.RedirectStandardOutput = true;
process.StartInfo.RedirectStandardError = true;
process.StartInfo.CreateNoWindow = true;
process.Start();
process.WaitForExit();
Stream outputReader = process.StandardOutput.BaseStream;
byte[] buffer = new byte[16*1024];
using (MemoryStream ms = new MemoryStream())
{
int read;
while ((read = outputReader.Read(buffer, 0, buffer.Length)) > 0)
{
ms.Write(buffer, 0, read);
}
BitmapImage bitmapImage = new BitmapImage();
bitmapImage.BeginInit();
bitmapImage.StreamSource = new MemoryStream(ms.ToArray());
bitmapImage.EndInit();
bitmapImage.Freeze();
Image = bitmapImage;
}

Related

How do I redirect the output of SpeechSynthesizer to a Process of ffmpeg

I am trying to have a SpeechSynthesizer generate some audio data, pipe it into a Process of FFmpeg, and have FFmpeg save the data to a file (output.wav). Eventually, the audio data will be used for something else, which is why I am using FFmpeg.
using (MemoryStream voiceStream = new MemoryStream())
using (Process ffmpeg = new Process())
{
SpeechSynthesizer synth = new SpeechSynthesizer();
int samplesPerSecond = 48000;
int bitsPerSample = 8;
int channelCount = 2;
int averageBytesPerSecond = samplesPerSecond * (bitsPerSample / 8) * channelCount;
int blockalign = (bitsPerSample / 8) * channelCount;
byte[] formatSpecificData = new byte[0];
synth.SetOutputToAudioStream(
voiceStream,
new System.Speech.AudioFormat.SpeechAudioFormatInfo(
System.Speech.AudioFormat.EncodingFormat.Pcm,
samplesPerSecond,
bitsPerSample,
channelCount,
averageBytesPerSecond,
blockalign,
formatSpecificData
)
);
synth.Speak("Hello there");
synth.SetOutputToNull();
ffmpeg.StartInfo = new ProcessStartInfo
{
FileName = "ffmpeg",
Arguments = $"-y -f u8 -ac 2 -ar 48000 -i pipe:0 out.wav",
UseShellExecute = false,
RedirectStandardOutput = true,
RedirectStandardInput = true
};
ffmpeg.Start();
using (Stream ffmpegIn = ffmpeg.StandardInput.BaseStream)
{
voiceStream.CopyTo(ffmpegIn);
ffmpegIn.FlushAsync();
}
}
When running the program, FFmpeg said that the input stream contains no data, and returns an empty file.
I believe that I do not interface properly with the Process object, however, the problem might also be my incorrect specification of the audio stream, since I do not know much about audio.

Edit image source C#

I'm trying to edit a image file where instead of path i should be able to provide ImageSource as parameter.
//EditCode:
Process proc = Process.Start(Path);
proc.EnableRaisingEvents = true;
ProcessStartInfo startInfo = new ProcessStartInfo();
startInfo.Verb = "edit";
proc.StartInfo = startInfo;
proc.Exited += new EventHandler((s, e) => myProcess_Exited(s, e, obj.ToString()));
The above code works well when i pass the Path of the image file as the parameter. But now i have only the ImageSource.
//ImageSourceCode:
private BitmapFrame LoadImage(string path)
{
BitmapDecoder decoder = null;
if (File.Exists(path) && (path != null))
{
using (var stream = new FileStream(path, FileMode.Open, FileAccess.Read))
{
decoder = BitmapDecoder.Create(stream, BitmapCreateOptions.None, BitmapCacheOption.OnLoad);
}
return decoder.Frames.FirstOrDefault();
}
else
return null;
}
The above code gets the path and convert an image as a source(BitmapFrame) and returns it.
Now i need this BitmapFrame to be passed in some function and edit the particular image file in paint and save it.
I neeed somthing like this,
Process proc = Process.Start(`ImageSource`);// Instead of path i need to pass the Image Source.
proc.EnableRaisingEvents = true;
ProcessStartInfo startInfo = new ProcessStartInfo();
startInfo.Verb = "edit";
proc.StartInfo = startInfo;
How can i achieve this?
You can't.
ProcessStartInfo starts a new process that is outside the App Domain of your application. You can't pass it structures in memory within your app domain as a command line parameter.

Read a byte array from a redirected process

I am using the process object in c#
I am also using FFMPEG.
I am trying to read the bytes from the redirected output. i know the data is an image but when I use the following code I do not get an image byte array.
this is my code:
var process = new Process();
process.StartInfo.FileName = #"C:\bin\ffmpeg.exe";
process.StartInfo.Arguments = #" -i rtsp://admin:admin#192.168.0.8:554/video_1 -an -f image2 -s 360x240 -vframes 1 -";
process.StartInfo.CreateNoWindow = true;
process.StartInfo.RedirectStandardError = true;
process.StartInfo.RedirectStandardOutput = true;
process.StartInfo.UseShellExecute = false;
process.Start();
var output = process.StandardOutput.ReadToEnd();
byte[] bytes = Encoding.ASCII.GetBytes(output);
The 1st bytes are not the header of a jpeg?
I think treating the output as a text stream is not the right thing to do here. Something like this worked for me, just directly read the data off the output pipe, it doesn't need conversion.
var process = new Process();
process.StartInfo.FileName = #"C:\bin\ffmpeg.exe";
// take frame at 17 seconds
process.StartInfo.Arguments = #" -i c:\temp\input.mp4 -an -f image2 -vframes 1 -ss 00:00:17 pipe:1";
process.StartInfo.CreateNoWindow = true;
process.StartInfo.RedirectStandardError = true;
process.StartInfo.RedirectStandardOutput = true;
process.StartInfo.UseShellExecute = false;
process.Start();
FileStream baseStream = process.StandardOutput.BaseStream as FileStream;
byte[] imageBytes = null;
int lastRead = 0;
using (MemoryStream ms = new MemoryStream())
{
byte[] buffer = new byte[4096];
do
{
lastRead = baseStream.Read(buffer, 0, buffer.Length);
ms.Write(buffer, 0, lastRead);
} while (lastRead > 0);
imageBytes = ms.ToArray();
}
using (FileStream s = new FileStream(#"c:\temp\singleFrame.jpeg", FileMode.Create))
{
s.Write(imageBytes, 0, imageBytes.Length);
}
Console.ReadKey();

How to get wkhtmltopdf logging info in C#?

I'm using wkhtmltopdf to convert some html to pdf. I think I'm getting some javascrpot errors and I'd like to get access to some debug\logging from wkhtmltopdf. Does anyone know how to get the logging info?
var wkhtmlDir = Settings.HtmlToPdfFolderPath;
var wkhtml = Settings.HtmlToPdfExePath;
var p = new Process();
p.StartInfo.CreateNoWindow = true;
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.RedirectStandardError = true;
p.StartInfo.RedirectStandardInput = true;
p.StartInfo.UseShellExecute = false;
p.StartInfo.FileName = wkhtml;
p.StartInfo.WorkingDirectory = wkhtmlDir;
string switches = "";
switches += "--print-media-type --redirect-delay 800 ";
//switches += "--margin-top 10mm --margin-bottom 10mm --margin-right 10mm --margin-left 10mm ";
switches += "--page-size Letter ";
p.StartInfo.Arguments = switches + " " + url
p.Start();
//read output
byte[] buffer = new byte[32768];
byte[] file;
using (var ms = new MemoryStream())
{
while (true)
{
int read = p.StandardOutput.BaseStream.Read(buffer, 0, buffer.Length);
if (read <= 0)
{
break;
}
ms.Write(buffer, 0, read);
}
file = ms.ToArray();
}
// wait or exit
p.WaitForExit(60000);
// read the exit code, close process
int returnCode = p.ExitCode;
p.Close();
//return returnCode == 0 ? file : null;
return file;
You can read standard error and standard output:
String output =string.Format("STD: {0}\nErr: {1}", p.StandardOutput.ReadToEnd(),
p.StandardError.ReadToEnd());
Is that what you are looking for?

Process hangs when trying to read bytes from buffer to int. Wkhtmltopdf

My method creates PDF out of HTML content with Wkhtmltopdf program with Process. I can't find a reason why it hangs. When there is a big string with HTML, i suppose it hangs when there are 30 pages or more. But everything works fine if there are less pages. Wkhtmltopdf.exe process i can see at the Task Manager, it does not exit forever. When i stop my MVC project with hanged Wkhtmltopdf, it creates normally PDF like it would wait for something.. Manually, of course, Wkhtmltopdf creates everything without any problem.
This is not duplicate of this post. Here i have issues when trying to read bytes into int.
public IActionResult createPdf()
{
string html = "content";
Process p;
ProcessStartInfo psi = new ProcessStartInfo();
psi.FileName = "...\\wkhtmltopdf.exe";
psi.WorkingDirectory = "...\\wkhtmltopdf\\bin";
psi.UseShellExecute = false;
psi.CreateNoWindow = true;
psi.RedirectStandardInput = true;
psi.RedirectStandardOutput = true;
psi.RedirectStandardError = true;
psi.StandardOutputEncoding = System.Text.Encoding.UTF8;
psi.Arguments = "-O landscape --footer-left qwe --footer-center [page]/[topage] --footer-right --footer-font-size 9 --no-stop-slow-scripts --zoom 0.8 --dpi 300 - - ";
p = Process.Start(psi);
byte[] pdf = null;
try
{
// Get PDF as bytes without temp files
using(StreamWriter stdin = new StreamWriter(p.StandardInput.BaseStream, Encoding.UTF8))
{
stdin.AutoFlush = true;
stdin.Write(html);
}
byte[] buffer = new byte[32768];
using(var ms = new MemoryStream())
{
while(true)
{
// HANGS HERE!!!
int read = p.StandardOutput.BaseStream.Read(buffer, 0, buffer.Length);
if(read <= 0)
{
break;
}
ms.Write(buffer, 0, read);
}
pdf = ms.ToArray();
}
}
...
}
I managed to solve this problem through merging separated pdf files into one using PdfSharp, instead of passing concatenated content to wkhtmltopdf as one. It lasts a little bit longer, but at least it works. Except of PdfSharp library (if you use MVC as me, then PdfSharp.Core to exclude version warnings), probably you would need to install System.Text.Encoding.CodePages to exclude encoding error that PdfSharp may cause. My method does not use temp files and works only with bytes, at the end it sends created pdf to a browser.
using PdfSharp.Pdf;
using PdfSharp.Pdf.IO;
public IActionResult createPdf()
{
string html = "";
byte[] pdf = null;
using(PdfDocument doc = new PdfDocument())
{
for(int i = 0; i < files.Length; i++)
{
html = "html content";
Process p;
ProcessStartInfo psi = new ProcessStartInfo();
psi.FileName = "...\\wkhtmltopdf.exe";
psi.WorkingDirectory = "...\\wkhtmltopdf\\bin";
psi.UseShellExecute = false;
psi.CreateNoWindow = true;
psi.RedirectStandardInput = true;
psi.RedirectStandardOutput = true;
psi.RedirectStandardError = true;
psi.StandardOutputEncoding = System.Text.Encoding.UTF8;
psi.Arguments = "-O landscape --footer-left qwe --footer-center [page]/[topage] --footer-right --footer-font-size 9 --no-stop-slow-scripts --zoom 0.8 --dpi 300 - - ";
p = Process.Start(psi);
using(StreamWriter stdin = new StreamWriter(p.StandardInput.BaseStream, Encoding.UTF8))
{
stdin.AutoFlush = true;
stdin.Write(html);
}
byte[] buffer = new byte[32768];
byte[] currentPdf = null;
using(var ms = new MemoryStream())
{
while(true)
{
int read = p.StandardOutput.BaseStream.Read(buffer, 0, buffer.Length);
if(read <= 0)
{
break;
}
ms.Write(buffer, 0, read);
}
currentPdf = ms.ToArray();
}
p.StandardOutput.Close();
p.WaitForExit(10000);
p.Close();
MemoryStream currentPDF = new MemoryStream(currentPdf);
// Merge separated pdfs into one
// Solves encoding errors
System.Text.Encoding.RegisterProvider(System.Text.CodePagesEncodingProvider.Instance);
using(PdfDocument pdfDoc = PdfReader.Open(currentPDF, PdfDocumentOpenMode.Import))
{
for(int j = 0; j < pdfDoc.PageCount; j++)
{
doc.AddPage(pdfDoc.Pages[j]);
}
}
currentPDF.Close();
}
// Get merged pdfs as bytes
MemoryStream rms = new MemoryStream();
doc.Save(rms, false);
pdf = rms.ToArray();
rms.Close();
}
MemoryStream PDF = new MemoryStream(pdf);
// Return PDF to browser
return new FileStreamResult(PDF, "application/x-msdownload")
{
FileDownloadName = "mergedPdfs.pdf"
};
}

Categories