在BASH中获取ffmpeg转换进度的可靠方法
我在这里和其他网站上做了一些搜索,似乎这个问题被问了好几次,但人们没有给出实际的代码,而是给出了理论 我希望创建一个可靠的方法来获得转码的进度。似乎最好的方法是使用源文件的总帧,然后获取ffmpeg所在的当前帧。正如人们所指出的,由于ffmpeg使用回车(/r或^M)奇怪地输出其进度,并且由于输出之间有时有空格,有时没有空格,因此这充其量是不可靠的。以下是输出的示例:在BASH中获取ffmpeg转换进度的可靠方法,bash,ffmpeg,Bash,Ffmpeg,我在这里和其他网站上做了一些搜索,似乎这个问题被问了好几次,但人们没有给出实际的代码,而是给出了理论 我希望创建一个可靠的方法来获得转码的进度。似乎最好的方法是使用源文件的总帧,然后获取ffmpeg所在的当前帧。正如人们所指出的,由于ffmpeg使用回车(/r或^M)奇怪地输出其进度,并且由于输出之间有时有空格,有时没有空格,因此这充其量是不可靠的。以下是输出的示例: frame=73834 fps=397 q=0.0 size= -0kB time=2462.14 bitrate=
frame=73834 fps=397 q=0.0 size= -0kB time=2462.14 bitrate= -0.0kbits/s frame=74028 fps=397 q=0.0 size= -0kB time=2468.64 bitrate= -0.0kbits/s frame=74133 fps=397 q=0.0 Lsize= -0kB time=2472.06 bitrate= -0.0kbits/
我已经编写了在ffmpeg转换进行时调用的函数。以下是我到目前为止得到的信息:
首先,要获取源文件的总帧数:
duration=( $(ffmpeg -i "$SourceFile" 2>&1 | sed -n "s/.* Duration: \([^,]*\), start: .*/\1/p") )
fps=( $(ffmpeg -i "$SourceFile" 2>&1 | sed -n "s/.*, \(.*\) tbr.*/\1/p") )
hours=( $(echo $duration | cut -d":" -f1) )
minutes=( $(echo $duration | cut -d":" -f2) )
seconds=( $(echo $duration | cut -d":" -f3) )
# Get the integer part with cut
frames=( $(echo "($hours*3600+$minutes*60+$seconds)*$fps" | bc | cut -d"." -f1) )
if [ -z $frames ]; then
zenity --info --title "$WindowTitle" --text "Can't calculate frames, sorry."
exit
echo ""$SourceFile" has $frames frames, now converting" > $ffmpeglog
echo ""$SourceFile" has $frames frames, now converting"
这是我在转换过程中调用的进度函数:
progress() {
sleep 10
#some shenanigans due to the way ffmpeg uses carriage returns
cat -v $ffmpeglog | tr '^M' '\n' > $ffmpeglog1
#calculate percentage progress based on frames
cframe=( $(tac $ffmpeglog1 | grep -m 1 frame= | awk '{print $1}' | cut -c 7-) )
if [ -z $cframe ]; then
cframe=( $(tac $ffmpeglog1 | grep -m 1 frame= | awk '{print $2}') )
fi
if is_integer $frame; then
percent=$((100 * cframe / frames))
#calculate time left and taken etc
fps=( $(tac $ffmpeglog1 | grep -m 1 frame= | awk '{print $3}') )
if [ "$fps" = "fps=" ]; then
fps=( $(tac $ffmpeglog1 | grep -m 1 frame= | awk '{print $4}') )
fi
total=$(( frames + cframe + percent + fps ))
#simple check to ensure all values are numbers
if is_integer $total; then
#all ok continue
if [ -z $fps ]; then
echo -ne "\rffmpeg: $cframe of $frames frames, progress: $percent"%" and ETA: error fps:0"
else
if [ -z $cframe ]; then
echo -ne "\rffmpeg: total $frames frames, error cframes:0"
else
remaining=$(( frames - cframe ))
seconds=$(( remaining / fps ))
h=$(( seconds / 3600 ))
m=$(( ( seconds / 60 ) % 60 ))
s=$(( seconds % 60 ))
echo -ne "\rffmpeg: $cframe of $frames frames, progress: $percent"%" and ETA: "$h"h "$m"m "$s"s"
fi
fi
else
echo "Error, one of the values wasn't a number, trying again in 10s."
fi
else
echo "Error, frames is 0, progress wont work, sorry."
fi
}
为了完整起见,这里是_整数函数:
is_integer() {
s=$(echo $1 | tr -d 0-9)
if [ -z "$s" ]; then
return 0
else
return 1
fi
}
如您所见,我的方法相当粗糙,因为我将一个日志文件写入另一个日志文件,然后处理第二个日志文件,以尝试处理回车。我发现的主要问题是,BASH可以调用的任何命令似乎都无法将ffmpegs输出减少到最后一行,这是由于使用了令人恼火的回车。我的awk技能不是很好,当frame=xxxxxx部分之间没有空格时,进度函数会失败,有时就是这样。我想知道是否有任何BASH headz可以使我的代码更具容错性和可靠性。正如您所知,回车的原因是进度行相互覆盖(由于没有换行,所以输出保持在一行上) 您可以尝试此方法在最后一次回车后提取字符串:
$ sample=$'cherry\rbanana\rapple'
$ echo "$sample"
applea
$ cr=$'\r' # A
$ extract="${sample##*$cr}" # B
$ echo "$extract"
apple
标有“A”和“B”的线条是最基本的,其余的只是为了演示。使用此方法,您可能能够消除一些现在必须使用的旋转