如何在 Process.Start 中的两个进程之间使用管道

How to use a Pipe between two processes in Process.Start

我必须 运行 这个命令行使用 VB.NET:

"H:\videotest\test.vpy" - -y | "H:\Release\data\bin64\ffmpeg.exe" -hwaccel auto -y -i - -map 0:v:0 -c:v libx265 -crf 20.0 -preset 5 -x265-params level=0:profile=undefined:pmode:no-pme:pme:no-high-tier:ref=3:bframes=4:open-gop:keyint=250:min-keyint=25:b-adapt=2:bframe-bias=0:rc-lookahead=20:no-scenecut:b-pyramid:me=hex:subme=2:merange=57:temporal-mvp:weightp:no-weightb:max-merge=2:no-weightb:no-rect:no-amp:vbv-bufsize=0:vbv-maxrate=0:vbv-init=0.9:no-strict-cbr:qcomp=0.6:qstep=4:aq-mode=1:aq-strength=1.0:cutree:no-early-skip:min-cu-size=8:ctu=64:no-fast-cfb:ipratio=1.4:pbratio=1.3:cbqpoffs=0:crqpoffs=0:rd=3:psy-rd=0.3:psy-rdoq=1:no-b-intra:no-fast-intra:rdoq-level=1:no-tskip:no-tskip-fast:cu-lossless:tu-intra-depth=1:tu-inter-depth=1:strong-intra-smoothing:no-constrained-intra:nr-intra=0:nr-inter=0:qblur=0.5:cplxblur=20:signhide:sar=16 "H:\videotest\outputawdwd.mkv"

vspipe.exe 运行s test.vpy 脚本并对视频输入应用过滤器或调整其大小,然后将输出通过管道传输到 ffmpeg 进行编码。

如果我在 vspipe 中使用普通的流程声明,它会出现以下错误:

Unknown argument: |

脚本从命令行运行良好。我怀疑这意味着我必须在 vspipeffmpeg 之间手动进行管道传输。

是否可以手动将一个进程的输出通过管道传输到另一个进程?我必须手动操作吗?

这是我启动进程的函数:

executablepath = "H:\Project\VapourSynth\core64\vspipe.exe"

params = "H:\videotest\test.vpy" - -y | "H:\Release\data\bin64\ffmpeg.exe" -hwaccel auto -y -i - -map 0:v:0 -c:v libx265 -crf 20.0 -preset 5 -x265-params level=0:profile=undefined:pmode:no-pme:pme:no-high-tier:ref=3:bframes=4:open-gop:keyint=250:min-keyint=25:b-adapt=2:bframe-bias=0:rc-lookahead=20:no-scenecut:b-pyramid:me=hex:subme=2:merange=57:temporal-mvp:weightp:no-weightb:max-merge=2:no-weightb:no-rect:no-amp:vbv-bufsize=0:vbv-maxrate=0:vbv-init=0.9:no-strict-cbr:qcomp=0.6:qstep=4:aq-mode=1:aq-strength=1.0:cutree:no-early-skip:min-cu-size=8:ctu=64:no-fast-cfb:ipratio=1.4:pbratio=1.3:cbqpoffs=0:crqpoffs=0:rd=3:psy-rd=0.3:psy-rdoq=1:no-b-intra:no-fast-intra:rdoq-level=1:no-tskip:no-tskip-fast:cu-lossless:tu-intra-depth=1:tu-inter-depth=1:strong-intra-smoothing:no-constrained-intra:nr-intra=0:nr-inter=0:qblur=0.5:cplxblur=20:signhide:sar=16 "H:\videotest\outputawdwd.mkv"

Private Sub CreateJobProcess(ByVal Name, ByVal executablepath, ByVal params)

    Try

        If Not jobs_processes.ContainsKey(Name) Then

            Dim Proc As New Process

            Proc.StartInfo.UseShellExecute = False
            Proc.StartInfo.CreateNoWindow = True
            Proc.StartInfo.RedirectStandardError = True
            Proc.StartInfo.FileName = "" & executablepath & ""
            Proc.StartInfo.Arguments = params

            'start process
            Proc.Start()

            'add new process to dictionary
            jobs_processes.Add(Name, Proc)

            'TEMP
            My.Settings.giobbe -= 1

            'start background workers for statistics
            If Not ConversionStats.IsBusy Then
                ConversionStats.WorkerSupportsCancellation = True
                ConversionStats.RunWorkerAsync()
            End If

            If Not UpdateListJob.IsBusy Then
                UpdateListJob.WorkerSupportsCancellation = True
                UpdateListJob.RunWorkerAsync()
            End If

        End If

    Catch ex As Exception
        Me.Invoke(New MethodInvoker(Sub() Logbox.AppendText(Environment.NewLine & ">Program exception:" & Environment.NewLine & ex.Message & Environment.NewLine)))
        MsgBox(ex.Message)
    End Try
End Sub

更新:

这是我更改的块,此函数获取需要创建的作业的作业名称和参数,然后将过程保存在字典中。

              Dim Proc As New Process

                Proc.StartInfo.UseShellExecute = False
                Proc.StartInfo.CreateNoWindow = True
                Proc.StartInfo.RedirectStandardError = True
                Proc.StartInfo.FileName = "cmd"
                Proc.StartInfo.Arguments = params

                'start process
                Proc.Start()

                'add new process to dictionary
                jobs_processes.Add(Name, Proc)

                'TEMP
                My.Settings.giobbe -= 1

                'start background workers for statistics
                If Not ConversionStats.IsBusy Then
                    ConversionStats.WorkerSupportsCancellation = True
                    ConversionStats.RunWorkerAsync()
                End If

                If Not UpdateListJob.IsBusy Then
                    UpdateListJob.WorkerSupportsCancellation = True
                    UpdateListJob.RunWorkerAsync()
                End If

然后我有一个 backgroundworker (ConversionStats) 从字典中的每个进程获取 stderr 并将它们打印到文本框中:

           'take current selected process and set streamreader
            Dim tmpproc As Process = jobs_processes(CurrentJob)
            Dim ffmpeg_stats As StreamReader
            Dim stdoutput As String = ""

            'something that verify if the job is started 

            If statejob = 1 Then    'if job is working

                'take stderr from ffmpeg
                ffmpeg_stats = tmpproc.StandardError
                stdoutput = ffmpeg_stats.ReadLine()

                If stdoutput IsNot Nothing Then 'if ffmpeg stderr is not nothing 

                    'IF FFMPEG IS RETURNING STATS
                    If stdoutput.Contains("frame=") Or stdoutput.Contains("size=") Then

所以这是我的代码... 但是现在使用 cmd 获取 streamreader 的标准错误导致采用字符串 "Invalid Handle." 这是 cmd stderr 的错误或者 streamreader 有问题?

更新 2

我什至尝试启动一个干净的 cmd 进程,只声明参数,但结果只是带有主要信息的控制台。

微软 Windows [版本 6.3.9600] (c) 2013 年微软公司。全部保留。

H:\Project\bin\Release>

这是澄清的代码:

                Dim Proc As New Process

                Proc.StartInfo.FileName = "cmd"
                Proc.StartInfo.Arguments = params

                'start process
                Proc.Start()

再次有人可以指导我如何 PIPE/REDIRECT 从一个进程 (vspipe.exe) 到另一个进程 (ffmpeg.exe) 的标准输出?

既然您知道您的命令字符串在命令行上有效,那么最简单的做法就是让 cmd.exe 运行 为您编写代码。正如 Plutonix 在 , in this answer 中建议的那样,Mark 提供了一个如何在 C# 代码中执行此操作的示例。

Process test = new Process();
test.StartInfo.FileName = "cmd";
test.StartInfo.Arguments = @"/C ""echo testing | grep test""";
test.Start();

根据您的目的调整它,并转换为 VB.net 可能看起来像这样(使用您在代码中声明的相同变量):

Dim Proc As New Process()
Proc.StartInfo.FileName = "cmd"
Proc.StartInfo.Arguments = "/C """ & executablepath & " " & params & """
Proc.Start()

这与您之前所做的有何不同?您使用 Process.Start 到 运行 vspipe.exe,然后将其传递给您的 params。上面的新代码使用 Process.Start 代替 运行 cmd.exe,本质上打开命令提示符 window,然后输入完整的命令行字符串。

接收stdoutstderr需要两个步骤。首先,您必须将每个的 'redirect' 属性设置为 True,然后在启动该过程后,手动检索所需的输出。

Dim Proc As New Process()
Proc.StartInfo.FileName = "cmd"
Proc.StartInfo.Arguments = executablepath & " " & params

'Capture stdio & stderr:
Proc.StartInfo.RedirectStandardOutput = True
Proc.StartInfo.RedirectStandardError = True

Proc.Start()

'Read stdio & stderr:
Dim StdIO As String = Proc.StandardOutput.ReadToEnd()
Dim StdErr As String = Proc.StandardError.ReadToEnd()

回复:更新 2

这个特定案例中的问题是 Arguments 字符串是一堆双引号,必须对其进行转义。在 vb.net 中,这是通过双引号 ("") 完成的,如果它们位于字符串的开头或结尾,则可以显示为 "triple double-quotes" ("""),甚至更长的双引号倍数(如果有很多需要转义的话)。

虽然我的系统上没有测试所需的特定软件,但以下应该可以工作:

Dim Proc As New Process
Proc.StartInfo.CreateNoWindow = True
Proc.StartInfo.UseShellExecute = False
Proc.StartInfo.FileName = "cmd"
Proc.StartInfo.Arguments = "/C ""H:\Project\VapourSynth\core64\vspipe.exe ""H:\videotest\test.vpy"" - -y | ""H:\Release\data\bin64\ffmpeg.exe"" -hwaccel auto -y -i - -map 0:v:0 -c:v libx265 -crf 20.0 -preset 5 -x265-params level=0:profile=undefined:pmode:no-pme:pme:no-high-tier:ref=3:bframes=4:open-gop:keyint=250:min-keyint=25:b-adapt=2:bframe-bias=0:rc-lookahead=20:no-scenecut:b-pyramid:me=hex:subme=2:merange=57:temporal-mvp:weightp:no-weightb:max-merge=2:no-weightb:no-rect:no-amp:vbv-bufsize=0:vbv-maxrate=0:vbv-init=0.9:no-strict-cbr:qcomp=0.6:qstep=4:aq-mode=1:aq-strength=1.0:cutree:no-early-skip:min-cu-size=8:ctu=64:no-fast-cfb:ipratio=1.4:pbratio=1.3:cbqpoffs=0:crqpoffs=0:rd=3:psy-rd=0.3:psy-rdoq=1:no-b-intra:no-fast-intra:rdoq-level=1:no-tskip:no-tskip-fast:cu-lossless:tu-intra-depth=1:tu-inter-depth=1:strong-intra-smoothing:no-constrained-intra:nr-intra=0:nr-inter=0:qblur=0.5:cplxblur=20:signhide:sar=16 ""H:\videotest\outputawdwd.mkv"""
Proc.StartInfo.RedirectStandardOutput = True
Proc.StartInfo.RedirectStandardError = True

Proc.Start()
Dim so As String = Proc.StandardOutput.ReadToEnd
Dim se As String = Proc.StandardError.ReadToEnd
Proc.WaitForExit()