Why does ffmpeg never finish when converting a vid

2019-05-05 05:06发布

问题:

I am trying to convert a video when the user submits a form. It seems to convert ok but the file "is being used by another proccess" when I try to do anything with it. It looks like ffmpeg.exe never exits. My code is below is there anything I should be doing different to allow the process to release the file? If I run this manually it exits fine.

internal class ConversionUtility : Utility
{
    public void Convert(string videoFileName)
    {
        var video = new VideoFile(videoFileName);

        if (!video.infoGathered)
            GetVideoInfo(video);

        var Params = string.Format("-y -i \"{0}\" -coder ac -me_method full -me_range 16 -subq 5 -sc_threshold 40 -vcodec libx264 -cmp +chroma -partitions +parti4x4+partp8x8+partb8x8 -i_qfactor 0.71 -keyint_min 25 -b_strategy 1 -g 250 -r 20 \"{1}\"", video.Path, Path.ChangeExtension(videoFileName,".mp4"));
        //var Params = string.Format("-y -i \"{0}\" -acodec libfaac -ar 44100 -ab 96k -coder ac -me_method full -me_range 16 -subq 5 -sc_threshold 40 -vcodec libx264 -s 1280x544 -b 1600k -cmp +chroma -partitions +parti4x4+partp8x8+partb8x8 -i_qfactor 0.71 -keyint_min 25 -b_strategy 1 -g 250 -r 20 c:\\output3.mp4", video.Path, videoFileName);
        //var Params = String.Format(" {0} \"{1}\"",this.FFmpegLocation, video.Path);

        var threadStart = new ParameterizedThreadStart(del => RunProcess(Params));
        var thread = new Thread(threadStart);
        thread.Start();            
        //RunProcess(Params);
    }
}

internal class Utility
{
    public string FFmpegLocation { get; set; }        
    private string WorkingPath { get { return Path.GetDirectoryName(FFmpegLocation); } }

    protected string RunProcess(string Parameters)
    {
        //create a process info
        var oInfo = new ProcessStartInfo(this.FFmpegLocation, Parameters)
        {
            UseShellExecute = false,
            CreateNoWindow = true,
            RedirectStandardOutput = true,
            RedirectStandardError = true
        };

        //Create the output and streamreader to get the output
        string output = null; StreamReader srOutput = null;

        //try the process
        try
        {
            //run the process
            Process proc = System.Diagnostics.Process.Start(oInfo);

            proc.WaitForExit();
            //if (!proc.WaitForExit(10000))
            //    proc.Kill();


            //get the output
            srOutput = proc.StandardError;

            //now put it in a string
            output = srOutput.ReadToEnd();

            proc.Close();
        }
        catch (Exception)
        {
            output = string.Empty;
        }
        finally
        {
            //now, if we succeded, close out the streamreader
            if (srOutput != null)
            {
                srOutput.Close();
                srOutput.Dispose();
            }
        }
        return output;
    }

回答1:

After a good bit of research I came across this post. Which set me on the right track. I read more about RedirectStandardError and found I needed to change the order I was calling things. After calling WaitForExit at the end everything worked as expected. Here is the updated code:

protected string RunProcess(string Parameters)
{
    //create a process info
    var oInfo = new ProcessStartInfo(this.FFmpegLocation, Parameters)
    {
        UseShellExecute = false,
        CreateNoWindow = true,
        RedirectStandardOutput = true,
        RedirectStandardError = true
    };

    var output = string.Empty;

    try
    {                
        Process process = System.Diagnostics.Process.Start(oInfo);

        output = process.StandardError.ReadToEnd();
        process.WaitForExit();
        process.Close();
    }
    catch (Exception)
    {
        output = string.Empty;
    }                       
    return output;
}


回答2:

You should always read the StandardOutput and the StandardError buffers while the process is running. Whenever the buffers reach their limits, the process will literally hang until you read the buffers to end.

FFMpeg constantly reports information about the conversion. Every update of the information you see in the bottom of the Command Prompt is added to the StandardOutput buffer, thus filling up the buffer. The buffer is likely to fill up quickly when converting large files.



回答3:

Does your web application(app pool process identity) have the proper permissions for what's being done in the process threads you are spawning?

I have had issues like this in the past related to System.File.Move running Process Threads doing IO from ASP.NET, which is really a Win32 issue under the hood.

Use System.File.Copy or the Win32 equivalent and then delete the old file if you can. My guess is the .exe you are calling out(executing) to isn't yours(your own source code to be able to modify) and you can't change it.

You can also use SysInternals to see what may be holding on to or blocking resources.