是否可以根据用户输入有条件地启用或禁用过滤器/流?

是否可以根据用户输入有条件地启用或禁用过滤器/流?

我有一个 Bash 脚本,它在给定的视频文件上运行 FFmpeg,并使用叠加层为其添加 PNG 水印以及许多其他功能。我希望能够通过用户输入启用或禁用水印,仅在某些视频上应用水印,但我知道的唯一方法是有条件地分支两个单独的 FFmpeg 命令 - 一个添加水印,一个不添加水印。

以下是两个用于比较的命令。

有水印:

ffmpeg -y -i "$1" -i "outro.mp4" -loop 1 -i "../Watermark/watermark3.png" \     
-movflags +faststart \
-preset ultrafast \
-filter_complex \ 
"color=black:16x16:d=$total[base]; \
[0:v]scale=-2:'max(1080,ih)',setpts=PTS-STARTPTS[v0]; \
[1:v]fade=in:st=0:d=$fadeduration:alpha=1,setpts=PTS-STARTPTS+(($fadetime)/TB)[v1]; \
[2:v]lut=a=val*0.7,fade=in:st=5:d=2:alpha=1,fade=out:st=$length1:d=2:alpha=1[v2]; \
[base][v0]scale2ref[base][v0]; \
[base][v0]overlay[tmp]; \
[tmp][v1]overlay,setsar=1[tmp2]; \
[v2][tmp2]scale2ref=w=oh*mdar:h=ih*0.1[wm_scaled][video]; \
[video][wm_scaled]overlay=W-w-50:50:format=auto:shortest=1[outv]; \
[0:a][1:a]acrossfade=d=$fadeduration[outa]" \
-map "[outv]" -map "[outa]" -c:v libx264 -c:a libopus -crf 17 "$output"

无水印:

ffmpeg -y -i "$1" -i "outro.mp4" -loop 1 -i "../Watermark/watermark3.png" \ 
-movflags +faststart \
-preset ultrafast \
-filter_complex \
"color=black:16x16:d=$total[base]; \
[0:v]scale=-2:'max(1080,ih)',setpts=PTS-STARTPTS[v0]; \
[1:v]fade=in:st=0:d=$fadeduration:alpha=1,setpts=PTS-STARTPTS+(($fadetime)/TB)[v1]; \
[base][v0]scale2ref[base][v0]; \
[base][v0]overlay[tmp]; \
[tmp][v1]overlay,setsar=1[tmp2]; \
[tmp2]setsar=1[outv]; \
[0:a][1:a]acrossfade=d=$fadeduration[outa]" \
-map "[outv]" -map "[outa]" -c:v libx264 -c:a libopus -crf 17 "$output"

这并不理想,因为它要求我维护两个 90% 相同的不同命令,这违反了 DRY 原则并导致维护工作量增加一倍。更不用说每次我想以相同的方式有条件地启用另一个功能时,我还需要对命令进行另外两次排列,因此它完全不适应未来,而且很容易很快变成意大利面条式代码。

FFmpeg 中是否有任何功能可以让我根据用户输入有条件地启用/禁用某个过滤器?如果没有,那么实现此类操作的最佳方法是什么?

答案1

在最初的测试中,我认为这会非常复杂,但结果比想象的要简单 - 它可以完全在 Bash 中处理,通过包装每个流参考变量中的条件流。

真正的麻烦是鸽子汉堡猜测 - 正在照顾全部对流的引用,特别是最后输出视频的流:这个流的变量需要有条件地设置其内容,并且在我的例子中还涉及使用“虚拟”过滤器以setsar=1避免空流错误。

之后,最困难的部分是确保所有流标签在两种条件下都匹配 - 如果不匹配,则需要像最终流一样有条件地重命名它们。

下面的示例询问用户是否要向视频应用水印:如果输入了除N或之外的任何内容n,它会通过设置必要的流然后使用它们运行 FFmpeg 命令来应用水印。

read -p "Add watermark to video? [Y/n] " -n1 -r
if [[ $REPLY =~ ^[Nn]$ ]]; then
unset wmstream1
unset wmstream2
wmstream3="[tmp2]setsar=1[outv];"
else
wmstream1="[2:v]lut=a=val*0.7,fade=in:st=5:d=2:alpha=1,fade=out:st=$length1:d=2:alpha=1[v2];"
wmstream2="[v2][tmp2]scale2ref=w=oh*mdar:h=ih*0.1[wm_scaled][video];"
wmstream3="[video][wm_scaled]overlay=W-w-50:50:format=auto:shortest=1[outv];"
fi
ffmpeg -y   -i "$1" -i "outro.mp4" -loop 1 -i "../Watermark/watermark3.png" \
-t 20 \
-movflags +faststart \
-preset ultrafast \
-filter_complex \
"color=black:16x16:d=$total[base]; \
[0:v]scale=-2:'max(1080,ih)',setpts=PTS-STARTPTS[v0]; \
[1:v]fade=in:st=0:d=$fadeduration:alpha=1,setpts=PTS-STARTPTS+(($fadetime)/TB)[v1]; \
$wmstream1 \
[base][v0]scale2ref[base][v0]; \
[base][v0]overlay[tmp]; \
[tmp][v1]overlay,setsar=1[tmp2]; \
$wmstream2 \
$wmstream3 \
[0:a][1:a]acrossfade=d=$fadeduration[outa]" \
-map "[outv]" -map "[outa]" -c:v libx264 -c:a libopus -crf 17 "$output"

尽管它可能远不如比我更优秀的 Bash 程序员所能做到的那样健壮或简洁,但它绝对比使用两个单独的命令更简洁、更易于维护。

相关内容