【问题标题】:a reliable way to get the progress of an ffmpeg conversion in BASH在 BASH 中获取 ffmpeg 转换进度的可靠方法
【发布时间】:2011-02-23 05:03:45
【问题描述】:

我在这里和其他网站上做了一些搜索,似乎这个问题已经被问过几次了,但是人们没有给出实际的代码,而是给出了理论。

我希望创建一种可靠的方法来获取转码的进度。似乎最好的方法是使用源文件的总帧数,然后获取 ffmpeg 所在的当前帧。正如人们指出的那样,因为 ffmpeg 奇怪地使用回车(/r 或 ^M)输出它的进度,并且因为输出之间有时有空格有时没有,所以这充其量是不可靠的。以下是输出示例:

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 函数:

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 可以使我的代码更具容错性和可靠性。

【问题讨论】:

    标签: bash ffmpeg


    【解决方案1】:

    我相信您知道,回车的原因是进度线相互覆盖(由于没有换行,输出保持在一行上)。

    你可以试试这个方法提取最后一个回车后的字符串:

    $ sample=$'cherry\rbanana\rapple'
    $ echo "$sample"
    applea
    $ cr=$'\r'                    # A
    $ extract="${sample##*$cr}"   # B
    $ echo "$extract"
    apple
    

    标有“A”和“B”的行是必需的,其余的只是为了演示。使用这种方法,您也许可以消除一些您现在必须使用的回旋。

    【讨论】:

      猜你喜欢
      • 2013-12-05
      • 2015-06-10
      • 1970-01-01
      • 1970-01-01
      • 2011-06-14
      • 2011-12-29
      • 2012-10-05
      • 2010-10-25
      相关资源
      最近更新 更多