我正在尝试编写我的第一个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" `
}
答案 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,理想情况下计算百分比:
实施例: 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”等搜索的顶部附近,所以我想我会贡献一个有效的例子。
2021-05-19-git-2261cc6d8a-full_build-www.gyan.dev
$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
,但是当尝试从进程捕获输出时,这很重要。请注意,您可以使用 stderr
将 stdout
重定向到 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)中读取 stderr
和 stdout
)。然后使用传入的参数和命令行启动该子进程。
重要
上述块的一个常见问题是您的命令(在本例中为 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。