How to call ffmpeg.exe to convert audio files on Windows Azure? - c#

I run a Web Role on Windows Azure to receive AAC audio files (uploaded by base64 string) and store them into blob. it works fine by now.
Next, I also have to convert them into MP3 and store the MP3s into blob too. I decided to use something like ffmpeg.exe -i path.aac path.mp3.
The problems are that:
How to call external ffmpeg.exe inside a web service of a web role?
what would be the path?
Please help me if you know. Thank you in advance.

I suggest that you use a Local Storage Resource for your webrole where you can download the AAC files from the blob storage, and have them converted to MP3. Then upload back to blob storage.
Side note is that you can also use the Path.GetTempFileName() to get a temporary file name for your AAC / MP3 files, but I don't encourage to do so (even if I've done it before).
As for the actuall ffmpeg running, you might want to browse the code for AzureVideoConv, which I've built some time ago. You will find a lot of useful code there.
Here is a sample of the actual ffmpeg call (note that I download the exe from a blob storage, to avoid bloating my azure package with external exe files, and to easily update the ffmpeg.exe when required):
internal void ConvertFile(string inputFileName, Guid taskID)
{
string tmpName = string.Format(
"{0}\\{1}.flv",
Path.GetTempPath(), inputFileName.Substring(inputFileName.LastIndexOf("\\")+1));
ProcessStartInfo psi = new ProcessStartInfo();
psi.FileName = this._processorExecutable;
psi.Arguments = string.Format(#"-i ""{0}"" -y ""{1}""", inputFileName, tmpName);
psi.CreateNoWindow = true;
psi.ErrorDialog = false;
psi.UseShellExecute = false;
psi.WindowStyle = ProcessWindowStyle.Hidden;
psi.RedirectStandardOutput = true;
psi.RedirectStandardInput = false;
psi.RedirectStandardError = true;
try
{
// Start the process with the info we specified.
// Call WaitForExit and then the using statement will close.
using (Process exeProcess = Process.Start(psi))
{
exeProcess.PriorityClass = ProcessPriorityClass.High;
string outString = string.Empty;
// use ansynchronous reading for at least one of the streams
// to avoid deadlock
exeProcess.OutputDataReceived += (s, e) => {
outString += e.Data;
};
exeProcess.BeginOutputReadLine();
// now read the StandardError stream to the end
// this will cause our main thread to wait for the
// stream to close (which is when ffmpeg quits)
string errString = exeProcess.StandardError.ReadToEnd();
Trace.WriteLine(outString);
Trace.TraceError(errString);
byte[] fileBytes = File.ReadAllBytes(tmpName);
if (fileBytes.Length > 0)
{
this._sSystem.SaveOutputFile(
fileBytes,
tmpName.Substring(tmpName.LastIndexOf("\\")+1),
taskID
);
}
}
}
catch (Exception e)
{
Trace.TraceError(e.Message);
}
}
NOTE the last check in of the project is using Windows Azure SDK 1.3

Thank you a lot #astaykov. You did a good job. Though It's not specific for my case(I need a specific piece of code instead of a whole large project), but it really inspired me. For specifying into my case, I am going to answer this question by my own - note that I did this based on #astaykov's code with somewhere directly copy&paste.
Firstly, configure the role with a Local Storage Resource. Then get its path by these code:
LocalResource converter_path =
RoleEnvironment.GetLocalResource("AudioConvertSpace");
string rootPathName = converter_path.RootPath;
get the path of ffmpeg.exe, xxx.aac and xxx.mp3 in the local storage:
string aac_path = rootPathName + "\\" + "fmwa-" + guidguid + ".aac";
string mp3_path = rootPathName + "\\" + "fmwa-" + guidguid + ".mp3";
string exe_path = rootPathName + "\\" + "ffmpeg.exe";
write the .aac file to local storage:
System.IO.File.WriteAllBytes(aac_path, decoded_audio_byte_array);
keep in mind that the local storage is not guaranteed to be stable or durable, so check the existence of the ffmpeg.exe -- if it doesn't exist, download it from blob.
if (System.IO.File.Exists(exe_path) == false)
{
var exeblob = _BlobContainer.GetBlobReference("ffmpeg.exe");
exeblob.DownloadToFile(exe_path, null);
}
initial and run the ffmpeg.exe process:
ProcessStartInfo psi = new ProcessStartInfo();
psi.FileName = exe_path;
psi.Arguments = string.Format(#"-i ""{0}"" -y ""{1}""",
aac_path, mp3_path);
psi.CreateNoWindow = true;
psi.ErrorDialog = false;
psi.UseShellExecute = false;
psi.WindowStyle = ProcessWindowStyle.Hidden;
psi.RedirectStandardOutput = true;
psi.RedirectStandardInput = false;
psi.RedirectStandardError = true;
Process exeProcess = Process.Start(psi);
exeProcess.PriorityClass = ProcessPriorityClass.High;
string outString = string.Empty;
exeProcess.OutputDataReceived += (s, e) => {
outString += e.Data;
};
exeProcess.BeginOutputReadLine();
string errString = exeProcess.StandardError.ReadToEnd();
Trace.WriteLine(outString);
Trace.TraceError(errString);
exeProcess.WaitForExit();
upload the output of ffmpeg.exe into the blob storage:
byte[] mp3_audio_byte_array = System.IO.File.ReadAllBytes(mp3_path);
var mp3blob = _BlobContainer.GetBlobReference("fmwa-"+guidguid+".mp3");
mp3blob.Properties.ContentType = "audio/mp3";
mp3blob.UploadByteArray(mp3_audio_byte_array);
clean the temp files:
System.IO.File.Delete(aac_path);
System.IO.File.Delete(mp3_path);

Related

Execute cat command to overwrite file

I am trying to execute the cat command from my C# code, but I am running into problems.
So, this is just a very simple test, but I end up with the error:
Error: cat: '>': No such file or directory
Now... both source and target files actually exist.. and same result if target file does not exist.
If I open a console on my Raspberry Pi, it executes just fine. Any help is much appreciated.
My code:
var srcFile = "/home/pi/tm-satellite/Helpers/wpa_supplicant.conf";
var outFile = "/home/pi/tm-satellite/network-test.txt";
StringBuilder sb = new StringBuilder();
var info = new ProcessStartInfo();
info.FileName = "/bin/bash";
info.Arguments = $"-c 'cat {srcFile} > {outFile}'";
info.UseShellExecute = false;
info.CreateNoWindow = true;
info.RedirectStandardOutput = true;
info.RedirectStandardError = true;
var p = Process.Start(info);
//* Read the output (or the error)
sb.AppendLine($"Args: {info.Arguments}");
sb.AppendLine($"Output: {p!.StandardOutput.ReadToEnd()}");
sb.AppendLine($"Error: {p!.StandardError.ReadToEnd()}");
p!.WaitForExit();
return $"Overwrite system file {path}: {p.ExitCode}{Environment.NewLine}{sb}";
This is because you're passing the cat program the > argument.
> only makes sense in bash or sh process where it tells to the interpreter that stdout outputs should be dumped into file. It's not a valid argument for cat.
To get around this, invoke your cat process within your shell:
sh -c 'cat file1 > file2'
In C#
var srcFile = "/home/pi/tm-satellite/Helpers/wpa_supplicant.conf"
var outFile = "/home/pi/tm-satellite/network-test.txt"
var info = new ProcessStartInfo();
info.FileName = "sh";
info.Arguments = $"-c 'cat {srcFile} > {outFile}'";
Alternatively you can use C#'s File utilities to read the first file and write its contents into the second as it might be faster due to less I/O operations.
I've fixed my sample. Use double quotes instead of single quotes:
var srcFile = "~/bad";
var outFile = "~/good";
var pInfo = new ProcessStartInfo()
{
FileName = "sh",
Arguments = $"-c \"cat {srcFile} > {outFile}\""
};
var process = Process.Start(pInfo);
process.WaitForExit();

Fail to execute a specific cmd batch file in C#

I'm using Process to execute a batch file which will generate certificate file.
The code works great when I execute other file (which contains openssl command). But when I execute a file which contains keytool command, it executed, but no file was generated.
I've:
Set UseShellExecute true.
Set WaitForExit(-1) and find the return was true, so it did executed.
I clicked that batch file manually, and the file generates right away, so the command was fine :(
BTW I'm using .Net Core MVC.
I can't find any error code anywhere, so I'm at my wits' end now.
Does anyone has a clue? Any help would be very appriciated!
success code(openssl):
I generate a p12 file (a certificate format) in that folder first, and it works fine.
private string Gen_P12(string domain, string pwd)
{
//generate folder
string folder = #"D:\Temp\";
if (!Directory.Exists(folder))
Directory.CreateDirectory(folder);
//generate bat(p12)
string bat = "openssl.exe pkcs12 -export -inkey " + domain + ".key -in " + domain + ".cer -out " + domain + ".p12 -password pass:" + pwd +"\r\n";
//download in folder
var path = Path.Combine(folder, domain + "_P12.bat");
using (FileStream fs = System.IO.File.Create(path))
{
byte[] content = new UTF8Encoding(true).GetBytes(bat);
fs.Write(content, 0, content.Length);
}
Thread.Sleep(500);
//execute
ProcessStartInfo myBat = new ProcessStartInfo();
string name = domain + "_P12.bat";
myBat.FileName = name;
myBat.WorkingDirectory = folder;
myBat.UseShellExecute = true;
//Process.Start(myBat);
Process p = Process.Start(myBat);
p.WaitForExit(-1);
return folder;
}
fail code(keytool):
Trying to use that P12 file and keytool command to generate a keystore (also a certificate format) but fail.
private string Gen_KS(string domain, string folder, string CA_domain, byte[] cer, string pwd)
{
//generate bat
string bat = "keytool -importkeystore -srckeystore " + domain + ".p12 -srcstoretype PKCS12 -srcstorepass " + pwd + " -destkeystore " + domain + ".keystore -storepass " + pwd + "\r\n";
var path = Path.Combine(folder, domain + "_KS.bat");
using (FileStream fs = System.IO.File.Create(path))
{
byte[] content = new UTF8Encoding(true).GetBytes(bat);
fs.Write(content, 0, content.Length);
}
Thread.Sleep(700);
//execute
ProcessStartInfo myBat = new ProcessStartInfo();
myBat.WorkingDirectory = folder;
string name = domain + "_KS.bat";
myBat.FileName = name;
myBat.UseShellExecute = true;
Process p = Process.Start(myBat);
var a = p.WaitForExit(-1);
string route = folder + domain + ".keystore";
return route;
}
Thanks!
Thanks to #user9938, I solved the problem!
1. Brief conclusion:
I need to process the bat as administrator.
(And I still don't get why only do the keytool command needs administrator rights)
2. Find the errors: (How to apply StanderError when UseShellExecute=true)
In fact we don't have to set it true to execute commands.
Try this (replace execute section):
Process process = new Process();
try
{
process.StartInfo.UseShellExecute = false;
process.StartInfo.CreateNoWindow = true;
process.StartInfo.RedirectStandardInput = true;
process.StartInfo.RedirectStandardOutput = true;
process.StartInfo.RedirectStandardError = true;
process.StartInfo.FileName = "cmd.exe";
process.Start();
process.StandardInput.WriteLine(bat); //command string, not the bat file
process.StandardInput.AutoFlush = true;
process.StandardInput.WriteLine("exit");
StreamReader reader = process.StandardError;
string curLine = reader.ReadLine();
reader.Close();
process.WaitForExit();
process.Close();
}catch (Exception e){}
Check the value of curLine through Breakpoints, the error message was: "'keytool' is not recognized as an internal or external command, operable program or batch file".
3. How to solve it:
Just set the Verb attribute as "runas".
//execute
ProcessStartInfo myBat = new ProcessStartInfo();
myBat.WorkingDirectory = folder;
string name = domain + "_KS.bat";
myBat.Verb = "runas";
myBat.FileName = name;
myBat.UseShellExecute = true;
Process p = Process.Start(myBat);
var a = p.WaitForExit(-1);
Done! Thank you user9938<3

How to use printer on server [duplicate]

This question already has an answer here:
How to run console application which is send pdf to printer in server?
(1 answer)
Closed 2 years ago.
I download .zip file from outlook then send pdf files to printer. It works on my local machine while compiling, However I setup published app on server, it downloads .zip file from outlook and open it but it can not send to printer . How can I handle that?
This is my code:
static void Main(string[] args)
{
ExchangeService exchange = new ExchangeService(ExchangeVersion.Exchange2013_SP1);
exchange.Credentials = new WebCredentials("mail#", "password");
exchange.Url = new Uri("https://outlook.office365.com/EWS/Exchange.asmx");
if (exchange != null)
{
SearchFilter sf = new SearchFilter.SearchFilterCollection(LogicalOperator.And, new SearchFilter.IsEqualTo(EmailMessageSchema.IsRead, false));
FindItemsResults<Item> result = exchange.FindItems(WellKnownFolderName.Inbox, sf, new ItemView(1));
foreach (Item item in result)
{
EmailMessage message = EmailMessage.Bind(exchange, item.Id);
message.IsRead = true;
message.Update(ConflictResolutionMode.AutoResolve);
List<Attachment> zipList = message.Attachments.AsEnumerable().Where(x => x.Name.Contains(".zip")).ToList();
foreach (Attachment Attachment in zipList)
{
if (Attachment is FileAttachment)
{
FileAttachment f = Attachment as FileAttachment;
f.Load("C:\\TEST\\" + f.Name);
string zipPath = #"C:\TEST\" + f.Name;
string extractPath = #"C:\TEST\" + Path.GetRandomFileName();
ZipFile.ExtractToDirectory(zipPath, extractPath);
string[] filePaths = Directory.GetFiles(extractPath, "*.pdf",
SearchOption.TopDirectoryOnly);
foreach (string path in filePaths)
{
SendToPrinter(path);
}
}
}
}
}
}
static void SendToPrinter(string path)
{
try
{
var printerName = "EPSON L310 Series";
ProcessStartInfo info = new ProcessStartInfo(path);
info.Verb = "PrintTo";
info.UseShellExecute = false;
info.CreateNoWindow = true;
info.WindowStyle = ProcessWindowStyle.Hidden;
info.Arguments = "\"" + printerName + "\"";
Process p = new Process();
p.StartInfo = info;
p.Start();
p.WaitForInputIdle();
System.Threading.Thread.Sleep(3000);
if (false == p.CloseMainWindow())
p.Kill();
}
catch (Exception ex)
{
Console.WriteLine("error:", ex.Message);
Console.ReadLine();
}
}
As I said, everything is work fine but printer not working. I can print pdf manually I mean machine works. Also this app works on my local machine
You need to change:
info.UseShellExecute = false;
to:
info.UseShellExecute = true;
since the path is a path to a PDF file, not an executable.
As per the docs:
When you use the operating system shell to start processes, you can
start any document (which is any registered file type associated with
an executable that has a default open action) and perform operations
on the file, such as printing, by using the Process object. When
UseShellExecute is false, you can start only executables by using the
Process object.
Additionally:
If you set the WindowStyle to ProcessWindowStyle.Hidden,
UseShellExecute must be set to true.
You are using Hidden - thus UseShellExecute must be true.

Converting XSD into .cs class

I know this may seem to be a duplicated question but I highly doubt it.
I am currently making a Windows Form application where the user can select an XSD file using the OpenFileDialog
Once the XSD is uploaded/selected I want it to create a .cs file from it using the default developer XSD tool.
But for some reason, it just opens the selected XSD file in notepad(?)
I've tried to comment the code to give it some sense.
//Filter only .xsd files
ofd.Filter = "XSD|*.xsd";
if (ofd.ShowDialog() == DialogResult.OK)
{
//Read file name
string File = ofd.FileName;
string z = ofd.InitialDirectory;
//Start making commands for in the CMD
//Change directory to the folder where the Dev Command prompt is located
string changeDirectory = #"cd C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\Common7\Tools\";
//Open the Dev CMD
string bat = "VsDevCmd";
//Change folder to our test folder
string cd = #"cd C:\Users\Pierre\Desktop\testxsd";
//execute xsd /c *selected file* /c is used to create the .cs file.
string command = #"xsd /c " + File;
//Combine the commands into 1 line.
string x = cd + "&" + command;
string xyz = changeDirectory + "&" + bat + "&" + x;
//print the outcome -> When I copy paste this into CMD the .cs file is generated
Console.WriteLine(xyz);
ProcessStartInfo oInfo = new ProcessStartInfo(Environment.ExpandEnvironmentVariables(#"C:\WINDOWS\system32\cmd.exe"), xyz);
oInfo.UseShellExecute = false;
oInfo.ErrorDialog = false;
oInfo.CreateNoWindow = true;
oInfo.RedirectStandardOutput = true;
try
{
Process p = System.Diagnostics.Process.Start(oInfo);
System.IO.StreamReader oReader2 = p.StandardOutput;
string sRes = oReader2.ReadToEnd();
oReader2.Close();
// sRes now contains the output from xsd.exe
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
So, as you can see in the comments, when I copy paste the console.writeline(xyz) into CMD it got properly executed and the .cs file is generated as it should.
However, when I just launch this code it opens the selected xsd in notepad.
Literally no idea what could be wrong
You are kind of taking the very long panoramic route when there is actually a very quick one... As #PatrickHofman stated in the comments use xsd directly...
To do this, open the Visual Studio command prompt, and write where xsd to find exact path of xsd executable.
Then start a process using xsd from the path you found and the various options ie. /c and filename.
using System.Diagnostics;
...
FileInfo fi = new FileInfo(ofd.FileName);
Process process = new Process();
process.StartInfo.FileName = xsdPath;
process.StartInfo.Arguments = "/c " + fi.FullName;
process.StartInfo.WorkingDirectory = fi.DirectoryName;
process.Start();
//wait for exit if needed...
process.WaitForExit();
If for some reason this is not working, capture the output from the command by doing this before process.Start():
process.StartInfo.UseShellExecute = false;
process.StartInfo.RedirectStandardOutput = true;
process.OutputDataReceived +=
(sender, args) => Console.WriteLine("received output: {0}", args.Data);
process.BeginOutputReadLine();
I think you should use the XmlSchemaClassGenerator package for this (Nuget). That way you wont have to do all the process juggling yourself.
Example from GitHub readme:
var generator = new Generator
{
OutputFolder = outputFolder,
Log = s => Console.Out.WriteLine(s),
GenerateNullables = true,
NamespaceProvider = new Dictionary<NamespaceKey, string>
{
{ new NamespaceKey("http://wadl.dev.java.net/2009/02"), "Wadl" }
}
.ToNamespaceProvider(new GeneratorConfiguration { NamespacePrefix = "Wadl" }.NamespaceProvider.GenerateNamespace)
};
generator.Generate(files);

Create pdf from html using wkhtmltopdf

i am genrating pdf from html code.Here i find code for generating using wkhtmltopdf
private void WritePDF(string HTML)
{
string inFileName,
outFileName,
tempPath;
Process p;
System.IO.StreamWriter stdin;
ProcessStartInfo psi = new ProcessStartInfo();
tempPath = Request.PhysicalApplicationPath + "ExcelFiles\\";
inFileName = Session.SessionID + ".htm";
outFileName = Session.SessionID + ".pdf";
// run the conversion utility
psi.UseShellExecute = false;
psi.FileName = "E:\\wkhtmltopdf";
psi.CreateNoWindow = true;
psi.RedirectStandardInput = true;
psi.RedirectStandardOutput = true;
psi.RedirectStandardError = true;
// note that we tell wkhtmltopdf to be quiet and not run scripts
// NOTE: I couldn't figure out a way to get both stdin and stdout redirected so we have to write to a file and then clean up afterwards
psi.Arguments = "-q -n - " + tempPath + outFileName;
p = Process.Start(psi);
try
{
stdin = p.StandardInput;
stdin.AutoFlush = true;
stdin.Write(HTML);
stdin.Close();
if (p.WaitForExit(15000))
{
// NOTE: the application hangs when we use WriteFile (due to the Delete below?); this works
Response.BinaryWrite(System.IO.File.ReadAllBytes(tempPath + outFileName));
//Response.WriteFile(tempPath + outFileName);
}
}
finally
{
p.Close();
p.Dispose();
}
// delete the pdf
System.IO.File.Delete(tempPath + outFileName);
}
I found this answer from here
how to pass html as a string using wkhtmltopdf?
Here i get error Could not find file on
if (p.WaitForExit(15000))
{
// NOTE: the application hangs when we use WriteFile (due to the Delete below?); this works
Response.BinaryWrite(System.IO.File.ReadAllBytes(tempPath + outFileName));
//Response.WriteFile(tempPath + outFileName);
}
how i can create file for this now?
It gets Created on stdin.Close();
Please check if the html in stdin.Write(HTML); has some content in it

Categories