Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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:

  1. How to call external ffmpeg.exe inside a web service of a web role?
  2. what would be the path?

Please help me if you know. Thank you in advance.

like image 500
Skyler Avatar asked Jan 04 '12 11:01

Skyler


2 Answers

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

like image 60
astaykov Avatar answered Nov 09 '22 12:11

astaykov


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.

  1. 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;
    
  2. 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";
    
  3. write the .aac file to local storage:

        System.IO.File.WriteAllBytes(aac_path, decoded_audio_byte_array);
    
  4. 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);
        }
    
  5. 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();
    
  6. 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);
    
  7. clean the temp files:

        System.IO.File.Delete(aac_path);
        System.IO.File.Delete(mp3_path);
    
like image 32
Skyler Avatar answered Nov 09 '22 12:11

Skyler