如何在PowerShell命令行向FFmpeg传递带参数名的-vf等额外参数
这个问题我之前也碰到过,PowerShell处理带连字符的外部命令参数确实容易踩坑,核心问题在于字符串形式的参数会被当成单个整体传给FFmpeg,而FFmpeg需要把-vf和scale=1920:-1当成两个独立的参数来解析。下面给你两种可靠的解决思路:
方法一:用数组类型传递额外参数(推荐)
首先修改你的函数,把接收额外参数的变量定义为字符串数组类型,这样PowerShell会把每个数组元素作为独立的参数传给FFmpeg,不会合并成一个字符串。
步骤1:更新函数参数定义
在param()块里添加一个数组类型的参数,比如[string[]]$ExtraParams:
function RecodeVP9 { [CmdletBinding()] param( # 你的其他现有参数(比如$quality, $bitrate, $audio等) [Parameter(Mandatory=$false)] [int]$quality = 30, [Parameter(Mandatory=$false)] [string]$bitrate = '128k', [Parameter(Mandatory=$false)] [string]$audio = 'stereo', # 新增的额外参数,数组类型 [Parameter(Mandatory=$false)] [string[]]$ExtraParams ) # 遍历文件的逻辑(保持你的原有代码) foreach ($i in Get-ChildItem -Path .\Videos -File -Filter *.mp4) { $outfile = Join-Path $i.DirectoryName "$($i.BaseName)_vp9.mkv" $encoder = 'ffmpeg' # 构建FFmpeg的基础参数数组 $baseArgs = @( '-hide_banner', '-i', $i.FullName, '-c:v', 'libvpx-vp9', '-b:v', '0', '-crf', $quality, '-tile-columns', '6', '-tile-rows', '2', '-threads', '8', '-speed', '2', '-frame-parallel', '0', '-row-mt', '1', '-c:a', 'libopus', '-b:a', $bitrate, '-af', "aformat=channel_layouts=$audio", '-c:s', 'copy', '-auto-alt-ref', '1', '-lag-in-frames', '25', '-y' ) # 合并基础参数、额外参数和输出文件路径 $finalArgs = $baseArgs + $ExtraParams + $outfile # 执行FFmpeg,注意这里用@展开数组 & $encoder @finalArgs } }
步骤2:调用函数时传递数组参数
调用的时候,把每个FFmpeg选项和它的值作为单独的数组元素传递,比如:
# 传递单个额外参数(-vf和scale参数分开) RecodeVP9 -ExtraParams '-vf', 'scale=1920:-1' # 传递多个额外参数 RecodeVP9 -ExtraParams '-vf', 'scale=1920:-1', '-aspect', '16:9', '-metadata', 'title="My Video"'
这样PowerShell会把每个元素单独传给FFmpeg,完全等价于你直接在命令行输入:
ffmpeg -hide_banner -i input.mp4 ... -vf scale=1920:-1 -aspect 16:9 -metadata title="My Video" -y output.mkv
方法二:如果习惯用字符串传递(兼容老用法)
如果你更习惯用单个字符串传递所有额外参数,可以在函数里把字符串拆分成数组,但要注意处理带空格的参数(比如包含空格的元数据)。这种方法需要用-split结合正则表达式,确保引号内的内容不被拆分:
function RecodeVP9 { [CmdletBinding()] param( # 其他参数同上... [Parameter(Mandatory=$false)] [string]$ExtraString ) # 把字符串拆分成参数数组(处理引号内的空格) if ($ExtraString) { $ExtraParams = [regex]::Matches($ExtraString, '(?:"[^"]+"|\S+)') | ForEach-Object { $_.Value -replace '^"|"$', '' } } else { $ExtraParams = @() } # 后续逻辑和方法一一致,合并参数数组执行FFmpeg }
调用的时候可以直接传带空格的字符串:
RecodeVP9 -ExtraString '-vf scale=1920:-1 -metadata title="My Cool Video"'
不过这种方法不如数组可靠,因为如果参数里有复杂的特殊字符,正则可能会出错,所以还是优先推荐方法一。
为什么之前的方法会报错?
当你把-vf scale=1920:-1作为单个字符串传给FFmpeg时,FFmpeg会把整个字符串当成一个参数,它会尝试寻找名为-vf scale=1920:-1的输出格式或文件,自然会报错"Unable to find a suitable output format for '-vf'"。而用数组传递时,每个选项和值都是独立的参数,FFmpeg就能正确识别-vf是一个滤镜选项,后面的scale=1920:-1是它的参数。
内容的提问来源于stack exchange,提问作者Tupsi

