几年前,当我还年轻、无忧无虑、呃、不太了解编写 shell 脚本的良好实践时;我编写了一个快速而肮脏的脚本来协助完成我面临的任务:
#!/bin/bash
# autohighlighter which generates highlights from a video clip and file
process_highlight_file() {
n=0
while read -r line; do
begin=$(echo "$line" | awk '{ print $1 }' )
end=$(echo "$line" | awk '{ print $2 }')
hilightname=$(echo "$line" | awk '{ print $3 }')
printf "Begin highlight called %s at %s, to %s\n" "$hilightname" "$begin" "$end"
echo "$begin $end"
sleep 2
echo "ffmpeg -y -ss $begin -i $videofile -to $end -c copy -avoid_negative_ts 1 $hilightname.mkv"
ffmpeg -loglevel quiet -hide_banner -y -ss "$begin" -i "$videofile" -to "$end" -c copy -avoid_negative_ts 1 "$hilightname.mkv"
if [ "$n" -eq 0 ]; then
echo -n "melt $hilightname.mkv " > constructed.melt
else
echo -n "$hilightname.mkv -mix 120 -mixer luma " >> constructed.melt
fi
(( n++ ))
done < $highlightfile
echo -n "-consumer avformat:$videofile-highlights.mkv crf=18" >> constructed.melt
}
highlightfile=$2
videofile=$1
process_highlight_file
exit 0
Run Code Online (Sandbox Code Playgroud)
我使用视频文件名和突出显示文件来调用它,其中包含以下制表符分隔的内容:
3:55 4:15 tutorialcomplete
10:50 11:15 firstkill
13:30 14:00 pickpocket
Run Code Online (Sandbox Code Playgroud)
如果我注释掉对 的实际调用ffmpeg,我会得到合理的输出:
Begin highlight called tutorialcomplete at 3:55, to 4:15
3:55 4:15
ffmpeg -y -ss 3:55 -i 2019-08-27 20-31-27.mkv -to 4:15 -c copy -avoid_negative_ts 1 tutorialcomplete.mkv
Begin highlight called firstkill at 10:50, to 11:15
10:50 11:15
ffmpeg -y -ss 10:50 -i /tmp/footage/gamefootage/pending/sor/2019-08-27 20-31-27.mkv -to 11:15 -c copy -avoid_negative_ts 1 firstkill.mkv
Begin highlight called pickpocket at 13:30, to 14:00
13:30 14:00
ffmpeg -y -ss 13:30 -i /tmp/footage/gamefootage/pending/sor/2019-08-27 20-31-27.mkv -to 14:00 -c copy -avoid_negative_ts 1 pickpocket.mkv
Run Code Online (Sandbox Code Playgroud)
一切都很好,只是对调试输出有点过分了。
如果我取消对 的调用的注释ffmpeg,我会得到:
Begin highlight called tutorialcomplete at 3:55, to 4:15
3:55 4:15
ffmpeg -y -ss 3:55 -i /tmp/footage/gamefootage/pending/sor/2019-08-27 20-31-27.mkv -to 4:15 -c copy -avoid_negative_ts 1 tutorialcomplete.mkv
Begin highlight called at firstkill, to
firstkill
ffmpeg -y -ss firstkill -i /tmp/footage/gamefootage/pending/sor/2019-08-27 20-31-27.mkv -to -c copy -avoid_negative_ts 1 .mkv
Begin highlight called pickpocket at 13:30, to 14:00
13:30 14:00
ffmpeg -y -ss 13:30 -i /tmp/footage/gamefootage/pending/sor/2019-08-27 20-31-27.mkv -to 14:00 -c copy -avoid_negative_ts 1 pickpocket.mkv
Run Code Online (Sandbox Code Playgroud)
当然,ffmpeg抱怨“首杀”不是寻求的有效时间。如果我向传入的文件添加更多行,它似乎只会影响循环的第二次传递。还产生额外的输出:
Enter command: <target>|all <time>|-1 <command>[ <argument>]
Run Code Online (Sandbox Code Playgroud)
目前的理论是线路没有正确终止。但是,我似乎无法在脚本或输入中找到它。
我知道这里存在许多不良做法、缺点和其他意想不到的行为,过去的我肯定对此负有责任并羞愧地低下了头!也就是说,为什么在此处的循环中调用 ffmpeg 会导致文件中的值被错误解析,或者:为什么通过调用 ffmpeg 注释掉该行会给变量提供正确的值。另外,为什么它只影响循环的第二遍?从哪里来Enter command?
此外,shellcheck.net 不会抱怨代码中的任何内容。
您需要防止 FFMpeg 消耗stdin字符流。
看:man ffmpeg.1
-stdin启用标准输入交互。除非使用标准输入作为输入,否则默认打开。要显式禁用交互,您需要指定
-nostdin.禁用标准输入上的交互很有用,例如,如果 ffmpeg 位于后台进程组中。可以实现大致相同的结果,
ffmpeg ... < /dev/null但它需要一个外壳。
在循环中,ffmpeg正在消耗循环命令块的输入while。
while read -r line; do
...
# here ffmpeg defaults to consuming the same input
# $highlightfile that is fed to the while loop commands block.
ffmpeg -loglevel quiet -hide_banner -y -ss "$begin" -i "$videofile" -to "$end" -c copy -avoid_negative_ts 1 "$hilightname.mkv"
done < $highlightfile
Run Code Online (Sandbox Code Playgroud)
代码的固定版本:
#!/usr/bin/env bash
# autohighlighter which generates highlights from a video clip and file
process_highlight_file() {
videofile="$1"
highlightfile="$2"
n=0
melt_caller=(melt)
while read -r begin end hilightname; do
printf 'Begin highlight called %s at %s, to %s\n' "$hilightname" "$begin" "$end"
ffmpeg_caller=(ffmpeg -nostdin -loglevel quiet -hide_banner -y -ss "$begin" -i "$videofile" -to "$end" -c copy -avoid_negative_ts 1 "$hilightname.mkv")
env printf '%q ' "${ffmpeg_caller[@]}" && echo
"${ffmpeg_caller[@]}"
((n++)) && melt_caller+=(-mix 120 -mixer luma)
melt_caller+=("$hilightname.mkv")
done <"$highlightfile"
melt_caller+=(-consumer "avformat:$videofile-highlights.mkv" 'crf=18')
env printf '%q ' "${melt_caller[@]}" && echo
"${melt_caller[@]}"
}
process_highlight_file "$@"
Run Code Online (Sandbox Code Playgroud)