You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何优化PowerShell视频编码脚本,基于CPU使用率实现并行任务?

最优实现方案及脚本修改建议

核心思路

  • 替换Start-Job为Start-ThreadJob:线程作业开销远低于传统后台作业,更适配本地文件转码这类CPU密集型任务
  • 动态并发控制:同时维持3-4个运行任务,仅当CPU整体使用率低于80%时启动新任务,充分利用闲置资源
  • 任务队列管理:遍历生成待处理文件列表后,循环监控运行任务状态,完成一个就补充一个新任务,直到所有文件处理完毕

具体脚本修改步骤

1. 定义基础配置参数

先设置转码、质量控制和并发相关的固定参数:

$sourceFolder = "C:\Your\FFV1_Folder_Path"
$outputFolder = "C:\Your\H264_Output_Path"
$initialCRF = 28
$vmafThreshold = 90
$maxConcurrentJobs = 4
$cpuUsageThreshold = 80
$ffmpegPath = "ffmpeg.exe" # 若ffmpeg不在系统PATH,需填写绝对路径

# 自动创建输出目录
if (-not (Test-Path $outputFolder)) {
    New-Item -ItemType Directory -Path $outputFolder | Out-Null
}

2. 筛选FFV1编码的MOV文件

遍历源文件夹,只保留真正的FFV1编码文件,避免误处理:

$sourceFiles = Get-ChildItem -Path $sourceFolder -Filter "*.mov" -File | ForEach-Object {
    # 通过ffprobe验证视频编码为FFV1
    $codec = & $ffmpegPath -v error -select_streams v:0 -show_entries stream=codec_name -of default=noprint_wrappers=1:nokey=1 $_.FullName
    if ($codec -eq "ffv1") { $_ }
}

3. 并行转码与动态控制逻辑

核心循环处理任务队列,结合CPU监控启动新任务:

$runningJobs = @()
$currentFileIndex = 0

while ($currentFileIndex -lt $sourceFiles.Count -or $runningJobs.Count -gt 0) {
    # 清理已完成的任务,获取处理结果
    $completedJobs = $runningJobs | Where-Object { $_.State -eq "Completed" }
    foreach ($job in $completedJobs) {
        $result = Receive-Job $job
        Write-Host "处理完成: $($result.SourceFile) | 最终CRF: $($result.FinalCRF) | VMAF分数: $($result.VMAFScore)"
        Remove-Job $job
        $runningJobs = $runningJobs | Where-Object { $_ -ne $job }
    }

    # 尝试启动新任务
    if ($currentFileIndex -lt $sourceFiles.Count -and $runningJobs.Count -lt $maxConcurrentJobs) {
        # 获取最近10秒CPU平均使用率,避免瞬时波动影响判断
        $avgCpuUsage = (Get-Counter -Counter "\Processor(_Total)\% Processor Time" -SampleInterval 1 -MaxSamples 10 |
            Select-Object -ExpandProperty CounterSamples |
            Select-Object -ExpandProperty CookedValue) | Measure-Object -Average | Select-Object -ExpandProperty Average

        if ($avgCpuUsage -lt $cpuUsageThreshold) {
            $targetFile = $sourceFiles[$currentFileIndex]
            $currentFileIndex++

            # 启动线程作业处理单个文件的转码+VMAF校验流程
            $job = Start-ThreadJob -ArgumentList $targetFile, $outputFolder, $initialCRF, $vmafThreshold, $ffmpegPath -ScriptBlock {
                param($sourceFile, $outputDir, $startCRF, $vmafMinScore, $ffmpeg)

                $baseName = $sourceFile.BaseName
                $tempOutput = Join-Path $outputDir "$baseName_temp.mp4"
                $finalOutput = Join-Path $outputDir "$baseName.mp4"

                $currentCRF = $startCRF
                $vmafScore = 0

                do {
                    # 执行H.264转码
                    & $ffmpeg -i $sourceFile.FullName -c:v libx264 -crf $currentCRF -preset medium -c:a copy $tempOutput -y -hide_banner -loglevel error

                    # 计算VMAF分数
                    $vmafLog = & $ffmpeg -i $sourceFile.FullName -i $tempOutput -lavfi "[0:v]setpts=PTS-STARTPTS[ref];[1:v]setpts=PTS-STARTPTS[dist];[dist][ref]libvmaf=log_fmt=json" -f null - -hide_banner -loglevel error
                    $vmafScore = [double]($vmafLog | Select-String -Pattern '"mean":\s*(\d+\.?\d*)' | ForEach-Object { $_.Matches.Groups[1].Value })

                    # 调整CRF:分数低于阈值则降低CRF(提升质量),最低限制为18
                    if ($vmafScore -lt $vmafMinScore) {
                        $currentCRF = [math]::Max(18, $currentCRF - 2)
                    } else {
                        break
                    }

                    # 删除临时文件,准备重新转码
                    if (Test-Path $tempOutput) { Remove-Item $tempOutput -Force }
                } while ($currentCRF -ge 18)

                # 重命名临时文件为最终输出
                if (Test-Path $tempOutput) { Rename-Item $tempOutput $finalOutput -Force }

                # 返回处理结果
                [PSCustomObject]@{
                    SourceFile = $sourceFile.FullName
                    FinalCRF = $currentCRF
                    VMAFScore = $vmafScore
                }
            }

            $runningJobs += $job
            Write-Host "启动转码任务: $($targetFile.FullName) | 初始CRF: $initialCRF"
        }
    }

    # 短暂休眠,避免循环占用过多CPU
    Start-Sleep -Seconds 2
}

4. 关键注意事项

  • FFmpeg环境要求:必须使用编译了libvmaf的FFmpeg版本,否则无法计算VMAF分数
  • CRF调整策略:示例中采用每次减2的步长,可根据实际需求调整(比如减1更精细),同时设置最低CRF=18防止无限循环
  • CPU监控精度:取10秒平均值是为了过滤瞬时CPU峰值,避免误判系统负载
  • 资源隔离:线程作业会共享进程内存,但每个任务处理独立文件,不会出现文件冲突问题

内容的提问来源于stack exchange,提问作者Andy Croon

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.24 13:34:57