PowerShell进度条将不会显示

时间:2014-07-23 11:31:35

标签: powershell ffmpeg

我正在尝试编写我的第一个PowerShell GUI。基本上我正在尝试运行ffmpeg命令,这很好并且有效,但是我无法运行进度条。 (我对此很陌生。)这是我的尝试。

cd C:\Users\brett\Documents\convert
$cmd = 'ffmpeg.exe'
$arg0 = '-i'
$arg1 = 'MASH_01.ts'
$arg2 = '-c:v'
$arg3 = '-c:a'
$arg4 = 'MASH_01.mp4'
$cf = 'copy'
$isFile = 'MASH_01.mp4'
if (-not(Test-Path -Path $isFile) -eq $false) {
  echo "Shit Go"
  del $isFile

      & $cmd $arg0 $arg1 $arg2 $cf $arg3 $cf $arg4
    for ($i = 1; $i -le 100; $i++) {
    Start-Sleep -m 100
    Write-Progress -Activity 'Progress Of The Coversion' -Status "$i Percent Complete" -PercentComplete $i;
    }
} else {
& $cmd $arg0 $arg1 $arg2 $cf $arg3 $cf $arg4
    for ($i = 1; $i -le 100; $i++) {
    Start-Sleep -m 100
    Write-Progress -Activity 'Progress Of The Coversion' -Status "$i Percent Complete" -PercentComplete $i;
    }
}

更新:我看不到错误输出,但进度条在文件处理后运行,而不是在处理过程中。

这是我最近的尝试..但现在我得到ffmpeg说“m”不是有效的开关

cd C:\Users\brett\Documents\convert
$oldVideo = Get-ChildItem -Include @("*.ts")
Write-Host -ForegroundColor Green -Object $ArgumentList;
# Pause the script until user hits enter
$isFile = 'MASH_01.mp4'
if( -not(Test-Path -Path $isFile) -eq $false) {
  echo "Shit Go"
  del $isFile
  }a
  $tool = ffmpeg.exe
$ArgumentList = '`-i'+' '+'MASH_01.ts'+' '+'-c:v'+' '+'copy'+' '+'-c:a'+' '+'copy'+' '+'MASH_01.mp4';
Invoke-Expression $tool $ArgumentList
for($i = 1; $i -le 100; $i++){
    ffmpeg $ArgumentList -m 100

    Write-Progress -Activity 'Progress Of The Coversion' -Status "$i Percent Complete" -PercentComplete $i 
   `-SecondsRemaining $a -CurrentOperation
   "$i% complete" `

    }

2 个答案:

答案 0 :(得分:1)

要查看使用进度:

cls
for($i =1; $i -le 100 ; $i++){
Start-Sleep -m 100
Write-Progress -Activity 'Progress Of The Conversion' -Status "$i Percent Complete" -PercentComplete $i
}

使用您自己的代码替换start-sleep cmdlet,理想情况下计算百分比:

http://blogs.technet.com/b/kpalmvig/archive/2013/10/29/easy-progress-bar-in-your-powershell-scripts.aspx

实施例: 1.将命令放在txt文件中 - 每行一个 - 你不需要&在文本文件中 2.使用此代码运行:

    cls
    $file = get-content 'c:\yourFolder\yourTextFile.txt'

    $i = 1
    foreach($line in $file) {

      $pct = (($i / $file.count) * 100)
      Write-Progress -activity "Processing...$i" -status "Progress: $([int]$pct)%" -PercentComplete $pct

      &"$line"

      $i++

}

答案 1 :(得分:0)

我知道这篇文章已经有 7 年以上的历史了,但它仍然出现在“ffmpeg display progress bar powershell”等搜索的顶部附近,所以我想我会贡献一个有效的例子。

环境

  • ffmpeg Windows 从 https://www.gyan.dev/ffmpeg/builds/ 构建 2021-05-19-git-2261cc6d8a-full_build-www.gyan.dev
  • Windows 10 专业版 20H2(内部版本 19042.985)
  • PowerShell 7.0.3

给我看代码

$theFile = "path\to\file\video.mp4"
$frames = Invoke-Expression "ffprobe.exe -v error -select_streams v:0 -count_packets -show_entries stream=nb_read_packets -of csv=p=0 ""$($theFile)"""
$ffmpeg_params = "-i  ""$($theFile)"" ""$($theFile)-processed.mkv"""

Write-Progress -Activity "ffmpeg" -Status "$theFile" -PercentComplete 0

$p = New-Object System.Diagnostics.Process
$p.StartInfo.Filename = "ffmpeg.exe"
$p.StartInfo.Arguments = "$ffmpeg_params"
$p.StartInfo.UseShellExecute = $false
$p.StartInfo.RedirectStandardError = $true
$p.StartInfo.RedirectStandardOutput = $true
$p.StartInfo.CreateNoWindow = $true
$p.Start() | Out-Null

while (-not $p.HasExited) {
  if ($p.StandardError.Peek()) {
    $line = $p.StandardError.ReadLineAsync().Result
    if ($line) {
      $info = $line.Trim();
      if ($info.StartsWith("frame=")) {
        $progperc = ([int]($info.split(" fps")[0]).split("=")[1] / $frames) * 100;
        if ($progperc -gt $PrevProgress) {
          Write-Progress "ffmpeg" -Status "$theFile" -PercentComplete $progperc
          $PrevProgress = $progperc
        }
      }
    }
  }
}

$p.WaitForExit();

Write-Progress  "ffmpeg" -Status "$theFile" -PercentComplete 100 -Completed

让我们把它拆开

一个重要的注意事项

默认情况下,ffmpeg 将其文本输出发送到 stderr 而不是预期的 stdout。这是因为 ffmpeg 能够在 stdout 上输出流以供其他消费。这是不明显的,因为大多数 shell 会以静默方式将 stderr 重定向到 stdout,但是当尝试从进程捕获输出时,这很重要。请注意,您可以使用 stderrstdout 重定向到 2>&1,但此处介绍的方法同样有效(例如捕获和读取 stderr) .请注意,ffprobe 没有,当您考虑该实用程序的功能时,这也很有意义。

我们开始

我不会解释第一行,它使我们:

$frames = Invoke-Expression "ffprobe.exe -v error -select_streams v:0 -count_packets -show_entries stream=nb_read_packets -of csv=p=0 ""$($theFile)"""

由于显示进度条取决于知道要计数的最终值,因此我们获得了视频中的帧数。 ffmpeg 在处理网格过程中的输出与此很好地匹配(也可以及时处理持续时间,因为输出也具有该持续时间并以这种方式计算,但是 明显 更烦人。)

$ffmpeg_params = "-i ""$($theFile)"" ""$($theFile)-processed.mkv"""

这只是设置要传递给ffmpeg的参数。请注意,这组特定的参数基本上不会做任何事情,除了从 .mp4 到具有默认值的 .mkv 格式的交叉编码之外,这几乎没有用,除了作为演示之外。将其替换为您需要传递给 ffmpeg 的任何参数。

Write-Progress -Activity "ffmpeg" -Status "$theFile" -PercentComplete 0

这会弹出实际的进度条。随意使用 -Activity-Status 参数。

$p = New-Object System.Diagnostics.Process
$p.StartInfo.Filename = "ffmpeg.exe"
$p.StartInfo.Arguments = "$ffmpeg_params"
$p.StartInfo.UseShellExecute = $false
$p.StartInfo.RedirectStandardError = $true
$p.StartInfo.RedirectStandardOutput = $true
$p.StartInfo.CreateNoWindow = $true
$p.Start() | Out-Null

这个块相当样板;它为 ffmpeg 创建一个子进程,传递它的参数,并且(这是重要的部分)设置父进程(PowerShell)以从它将创建的子进程(ffmpeg)中读取 stderrstdout )。然后使用传入的参数和命令行启动该子进程。

重要

上述块的一个常见问题是您的命令(在本例中为 ffmpeg.exe)不在您的路径中。您应该做更多的工作以确保 ffmpeg.exe 存在,并且可能将其传递给完全限定的路径。

while (-not $p.HasExited) {
  if ($p.StandardError.Peek()) {
    $line = $p.StandardError.ReadLineAsync().Result
    if ($line) {
      $info = $line.Trim();
      if ($info.StartsWith("frame=")) {
        $progperc = ([int]($info.split(" fps")[0]).split("=")[1] / $frames) * 100;
        if ($progperc -gt $PrevProgress) {
          Write-Progress "ffmpeg" -Status "$theFile" -PercentComplete $progperc
          $PrevProgress = $progperc
        }
      }
    }
  }
}

while 循环正在检查运行 ffmpeg 的进程是否已退出。如果没有,则在 StandardError 流中“Peeks”(即返回流中的下一个可用字符,但不使用它)以查看是否存在某些内容。如果存在,它会以异步方式(不阻塞进程)读取该行,对其进行修剪,然后真正的魔法开始。

为了完整起见,重要的是要知道 ffmpeg 的进程输出(一旦完成,就会为您提供大量关于您告诉它做什么的信息)看起来很像这样:

frame=    1 fps=0.0 q=0.0 size=       5kB time=00:00:00.00 bitrate=N/A speed=   0x
frame=    1 fps=0.0 q=0.0 size=       5kB time=00:00:00.00 bitrate=N/A speed=   0x
frame=    2 fps=0.0 q=0.0 size=       5kB time=00:00:00.00 bitrate=N/A speed=   0x
frame=    2 fps=0.0 q=0.0 size=       5kB time=00:00:00.00 bitrate=N/A speed=   0x
frame=    2 fps=1.9 q=0.0 size=       5kB time=00:00:00.00 bitrate=N/A speed=   0x
frame=    2 fps=1.9 q=0.0 size=       5kB time=00:00:00.00 bitrate=N/A speed=   0x
frame=   16 fps= 10 q=0.0 size=       5kB time=00:00:00.59 bitrate=  63.5kbits/s speed=0.379x
frame=   16 fps= 10 q=0.0 size=       5kB time=00:00:00.59 bitrate=  63.5kbits/s speed=0.379x
frame=   31 fps= 15 q=0.0 size=       5kB time=00:00:01.09 bitrate=  34.6kbits/s speed=0.528x
frame=   31 fps= 15 q=0.0 size=       5kB time=00:00:01.09 bitrate=  34.6kbits/s speed=0.528x
frame=   46 fps= 18 q=0.0 size=       5kB time=00:00:01.59 bitrate=  23.8kbits/s speed=0.616x
frame=   46 fps= 18 q=0.0 size=       5kB time=00:00:01.59 bitrate=  23.8kbits/s speed=0.616x
frame=   61 fps= 20 q=0.0 size=       5kB time=00:00:02.09 bitrate=  18.1kbits/s speed=0.678x
frame=   61 fps= 20 q=0.0 size=       5kB time=00:00:02.09 bitrate=  18.1kbits/s speed=0.678x
frame=   76 fps= 21 q=29.0 size=       5kB time=00:00:02.59 bitrate=  14.7kbits/s speed=0.718x
frame=   76 fps= 21 q=29.0 size=       5kB time=00:00:02.59 bitrate=  14.7kbits/s speed=0.718x
frame=   91 fps= 22 q=29.0 size=       5kB time=00:00:03.09 bitrate=  12.3kbits/s speed=0.75x
frame=   91 fps= 22 q=29.0 size=       5kB time=00:00:03.09 bitrate=  12.3kbits/s speed=0.75x
frame=  106 fps= 23 q=29.0 size=       5kB time=00:00:03.59 bitrate=  10.6kbits/s speed=0.775x
frame=  106 fps= 23 q=29.0 size=       5kB time=00:00:03.59 bitrate=  10.6kbits/s speed=0.775x

...,至少在最近版本的 ffmpeg 中,它只显示在大约每秒一次的连续变化的单行上。但是,从我们的子进程的角度来看,每次更新时它都会流式传输一整行,这就是我们将用来计算进度在进度条上的位置的内容。您可能会在行首看到重要信息:frame= 2 等等。

因此,回到我们的代码;只要 line out 输出以 frame= 开头,我们就抓住那条线,对其进行一些拆分(这可能效率低下,并且 绝对 容易在 ffmpeg 的输出格式更改时中断;我'肯定有人可以比我更优雅地做到这一点,但我所做的工作),并将该数字与我们必须处理的总帧数进行比较。将该数字乘以 100 得到一个百分比,我们就有了进度,因此,只要当前进度大于上一个进度,我们就会更新进度条。

$p.WaitForExit();

同样,这是样板的子进程内容,只是在脚本中发生任何其他事情之前等待进程退出。

您还应该在此处进行一些验证,以查看它是否以常规退出代码 (if ($p.ExitCode -ne 0)) 退出,以及其他有价值的东西。

最后一行只是完成了进度条并使其远离 UI。

就是这样!根据需要修改。

对问题不重要——但与答案相关——信息

ffmpeg 及其兄弟(例如 ffprobe)如果文件名或路径中有空格,则需要双引号(")文件名,这会引起很多脚本开发人员的心痛。为了在 PowerShell 中处理这个问题,我们使用双双引号,因此您在命令中看到的双双引号是为了保护实际命令中的那些引号,以确保如果您的文件名中有空格,它会得到正确处理.

来源

https://stackoverflow.com/a/28376817/13902318 用于 ffprobe 命令。

大部分进度条代码改编自https://github.com/JoJoBond/Powershell-YouTube-Upload