代码之家  ›  专栏  ›  技术社区  ›  Nobbe

.NET进程-重定向stdin和stdout而不引起死锁

  •  3
  • Nobbe  · 技术社区  · 7 年前

    然而,在重定向中的输出时,我遇到了一个似乎相当常见的问题 系统诊断过程 ,在文件备注中提到 here

    在为此争论了一天之后,我尝试了网上提出的几种解决方案,但我仍然找不到一种方法来实现它。我得到了一些数据,但这个过程总是在结束之前冻结。


    以下是产生上述问题的代码片段:

    static void Main(string[] args)
        {
    
            Process proc = new Process();
    
            proc.StartInfo.FileName = @"ffmpeg.exe";
            proc.StartInfo.Arguments = String.Format("-f rawvideo -vcodec rawvideo -s {0}x{1} -pix_fmt rgb24 -r {2} -i - -an -codec:v libx264 -preset veryfast -f mp4 -movflags frag_keyframe+empty_moov -",
                16, 9, 30);
            proc.StartInfo.UseShellExecute = false;
            proc.StartInfo.RedirectStandardInput = true;
            proc.StartInfo.RedirectStandardOutput = true;
    
            FileStream fs = new FileStream(@"out.mp4", FileMode.Create, FileAccess.Write);
    
            //read output asynchronously
            using (AutoResetEvent outputWaitHandle = new AutoResetEvent(false))
            {
                proc.OutputDataReceived += (sender, e) =>
                {
                    if (e.Data == null)
                    {
                        outputWaitHandle.Set();
                    }
                    else
                    {
                        string str = e.Data;
                        byte[] bytes = new byte[str.Length * sizeof(char)];
                        System.Buffer.BlockCopy(str.ToCharArray(), 0, bytes, 0, bytes.Length);
                        fs.Write(bytes, 0, bytes.Length);
                    }
                };
            }
    
    
            proc.Start();
            proc.BeginOutputReadLine();
    
            //Generate frames and write to stdin
            for (int i = 0; i < 30*60*60; i++)
            {
                byte[] myArray = Enumerable.Repeat((byte)Math.Min(i,255), 9*16*3).ToArray();
                proc.StandardInput.BaseStream.Write(myArray, 0, myArray.Length);
            }
    
            proc.WaitForExit();
            fs.Close();
            Console.WriteLine("Done!");
            Console.ReadKey();
    
        }
    

    如果有人知道解决方案,我们将不胜感激。

    1 回复  |  直到 7 年前
        1
  •  3
  •   Peter Duniho    7 年前

    您的进程挂起,因为您从未告诉ffmpeg您已完成对 StandardInput 流动所以它仍然在那里等待你发送更多的数据。

    填充 输出文件

    Process 类及其属性。

    特别是,这意味着两件事:

    1. 当程序为流生成原始字节数据时,不能将输出数据视为字符。
    2. 必须 完成后关闭输入流,以便程序知道已到达输入流的末尾。

    static void Main(string[] args)
    {
        Process proc = new Process();
    
        proc.StartInfo.FileName = @"ffmpeg.exe";
        proc.StartInfo.Arguments = String.Format("-f rawvideo -vcodec rawvideo -s {0}x{1} -pix_fmt rgb24 -r {2} -i - -an -codec:v libx264 -preset veryfast -f mp4 -movflags frag_keyframe+empty_moov -",
            16, 9, 30);
        proc.StartInfo.UseShellExecute = false;
        proc.StartInfo.RedirectStandardInput = true;
        proc.StartInfo.RedirectStandardOutput = true;
    
        FileStream fs = new FileStream(@"out.mp4", FileMode.Create, FileAccess.Write);
    
        proc.Start();
        var readTask = _ConsumeOutputAsync(fs, proc.StandardOutput.BaseStream);
    
        //Generate frames and write to stdin
        for (int i = 0; i < 30 * 60 * 60; i++)
        {
            byte[] myArray = Enumerable.Repeat((byte)Math.Min(i, 255), 9 * 16 * 3).ToArray();
            proc.StandardInput.BaseStream.Write(myArray, 0, myArray.Length);
        }
    
        proc.StandardInput.BaseStream.Close();
    
        readTask.Wait();
        fs.Close();
        Console.WriteLine("Done!");
        Console.ReadKey();
    }
    
    private static async Task _ConsumeOutputAsync(FileStream fs, Stream baseStream)
    {
        int cb;
        byte[] rgb = new byte[4096];
    
        while ((cb = await baseStream.ReadAsync(rgb, 0, rgb.Length)) > 0)
        {
            fs.Write(rgb, 0, cb);
        }
    }
    

    请注意,即使您动态生成数据,您的示例也显示了将输出写入文件。如果你真的想要输出到一个文件中,那么我会 肯定 输出文件 StandardOutput 你自己当您正在运行的外部进程将为您处理所有这些工作时,为什么要在数据处理中注入您自己的代码?

    AutoResetEvent 对象,因为您从未等待过该对象,并且您对它的实现被破坏,因为您在使用它之前就处理了该对象。因此,上述修改后的示例并没有试图复制这种行为。没有它也可以。