надежный способ получить прогресс конвертации ffmpeg в BASH - PullRequest
4 голосов
/ 24 мая 2010

Я провел небольшой поиск здесь и на других сайтах, кажется, что этот вопрос задавался несколько раз, но вместо того, чтобы давать реальный код, люди только дают теорию.

Я хочу создать надежный способ получения прогресса транскодирования. Кажется, лучший способ - использовать общее количество кадров исходного файла, а затем получить текущий кадр, в котором находится 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 сделать мой код немного более устойчивым к ошибкам и надежным.

1 Ответ

3 голосов
/ 24 мая 2010

Причина возврата каретки, как я уверен, вы знаете, заключается в том, что строки прогресса перезаписывают друг друга (вывод остается на одной строке, поскольку нет перевода строки).

Вы можете попробовать этот метод для извлечения строки после последнего возврата каретки:

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

Строки, отмеченные «A» и «B», являются основными, остальные - только для демонстрации. Используя этот метод, вы сможете устранить некоторые из вращений, которые вам нужно использовать сейчас.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...