BASH:如何进行时间码计算

BASH:如何进行时间码计算,bash,ffmpeg,Bash,Ffmpeg,我想知道如何计算以帧为单位的两个视频时间码之间的差在这种情况下,一秒钟等于30帧 假设A点是600 00:00:02,B点是120 00:00:04 如何计算pont A和B之间的差异,并使用bash以00:00:00.00格式h:m:s回显结果 更新: 这是完美的:将所有时间保留在帧中,然后通过单个格式化例程将帧显示为时间。如果你试图转换成时间,你会使数学转换更加困难,因为时间是一个基本的60系统 frames2date () { local secs=$((2 * $1)); dat

我想知道如何计算以帧为单位的两个视频时间码之间的差在这种情况下,一秒钟等于30帧

假设A点是600 00:00:02,B点是120 00:00:04

如何计算pont A和B之间的差异,并使用bash以00:00:00.00格式h:m:s回显结果

更新:


这是完美的:

将所有时间保留在帧中,然后通过单个格式化例程将帧显示为时间。如果你试图转换成时间,你会使数学转换更加困难,因为时间是一个基本的60系统

frames2date () {
  local secs=$((2 * $1));
  date --utc --date "1970-01-01 ${secs} sec" "+%T"
}

如果要将帧数转换为时间,请注意,如果时间超过24小时,则需要添加年、月和日。

将所有时间保留在帧中,然后通过单个格式化例程将帧显示为时间。如果你试图转换成时间,你会使数学转换更加困难,因为时间是一个基本的60系统

frames2date () {
  local secs=$((2 * $1));
  date --utc --date "1970-01-01 ${secs} sec" "+%T"
}
如果要将帧数转换为时间,请注意,如果时间超过24小时,则需要添加年、月和日。

首先,导出语句是多余的,因为当前shell正在扩展$a和$B

要从时间戳中获取这些数字,GNU date将有助于:

jinx:752 Z$ date -d 00:01:18.44 +%T.%N # to show that it works
00:01:18.440000000
jinx:753 Z$ date -d 00:01:18.44 +%s.%N
1299214878.440000000
现在,您可以进行计算,然后在最后转换回显示,正如Edwin Buck所说,不要尝试以这种格式存储中间结果:

jinx:754 Z$ date -d @1299214878.44 +%T.%N
00:01:18.440000000
首先,导出语句是多余的,因为当前shell正在扩展$A和$B

要从时间戳中获取这些数字,GNU date将有助于:

jinx:752 Z$ date -d 00:01:18.44 +%T.%N # to show that it works
00:01:18.440000000
jinx:753 Z$ date -d 00:01:18.44 +%s.%N
1299214878.440000000
现在,您可以进行计算,然后在最后转换回显示,正如Edwin Buck所说,不要尝试以这种格式存储中间结果:

jinx:754 Z$ date -d @1299214878.44 +%T.%N
00:01:18.440000000
非完美解决方案:

在朋友们的帮助下,以下是我完整的bash脚本:

#!/bin/bash

#ffmpeg [inputfile] [starttimecode] [videoduration] [video details] [size] [audio details] [alltherest] [outputfile]

video=-'vcodec libx264 -vpre slower -crf 22'
size='-s 320x240'
audio='-acodec libmp3lame -aq 0 -ac 1'

in=$(date --utc --date "$3" +%s)
out=$(date --utc --date "$4" +%s)
dur=$(date --utc --date "1970-01-01 $[$out-$in] sec" "+%T")
ss=$(date --utc --date "1970-01-01 $[$in] sec" "+%T")

ffmpeg -i $1.$2 $video $size $audio -threads 0 -y $1.mp4

#ffmpeg -i t1.flv -ss 00:00:32 -t 00:00:04 -vcodec libx264 -vpre slower -crf 22 -s 320x240 -acodec libmp3lame -aq 0 -ac 1 -threads 0 -y t1.mp4
我将脚本保存为v,下面是如何从cli调用它:

v t1 flv 00:00:32.658 00:00:36.060
通过这种方式,我可以直接从ffmpeg cli剪切视频文件的一部分。但是时间码之间的计算不是很精确,所以我需要做更多的工作来实现这一点:

不是完美的解决方案:

在朋友们的帮助下,以下是我完整的bash脚本:

#!/bin/bash

#ffmpeg [inputfile] [starttimecode] [videoduration] [video details] [size] [audio details] [alltherest] [outputfile]

video=-'vcodec libx264 -vpre slower -crf 22'
size='-s 320x240'
audio='-acodec libmp3lame -aq 0 -ac 1'

in=$(date --utc --date "$3" +%s)
out=$(date --utc --date "$4" +%s)
dur=$(date --utc --date "1970-01-01 $[$out-$in] sec" "+%T")
ss=$(date --utc --date "1970-01-01 $[$in] sec" "+%T")

ffmpeg -i $1.$2 $video $size $audio -threads 0 -y $1.mp4

#ffmpeg -i t1.flv -ss 00:00:32 -t 00:00:04 -vcodec libx264 -vpre slower -crf 22 -s 320x240 -acodec libmp3lame -aq 0 -ac 1 -threads 0 -y t1.mp4
我将脚本保存为v,下面是如何从cli调用它:

v t1 flv 00:00:32.658 00:00:36.060

通过这种方式,我可以直接从ffmpeg cli剪切视频文件的一部分。但是时间码之间的计算并不十分精确,因此我需要进一步研究以实现这一点:

这里有另一种不使用日期工具的方法:

    function timecode_to_int() {
      if [[ "$1" =~ ^((0*([0-9]+):)?0*([0-9]+):)?0*([0-9]+)(\.([0-9]{1,3}))? ]]; then
        VALUE=$((${BASH_REMATCH[5]}))
        [[ -n "${BASH_REMATCH[4]}" ]] && VALUE=$((${BASH_REMATCH[4]}*60+$VALUE))
        [[ -n "${BASH_REMATCH[3]}" ]] && VALUE=$((${BASH_REMATCH[3]}*60*60+$VALUE))
        VALUE=$(($VALUE*1000))

        if [[ -n "${BASH_REMATCH[7]}" ]]; then
          VALUE=$(($VALUE+10#$(printf %-3s ${BASH_REMATCH[7]} | sed s_\ _0_g)))
        fi
        echo "${VALUE}"
      else
        echo "Invalid timecode '$1', aborting." >&2
        exit 1
      fi
    }

    function int_to_timecode() {
        MILLISECONDS="$(($1%1000))"
        SECS="$(($1/1000%60))"
        MINUTES="$(($1/1000/60%60))"
        HOURS="$(($1/1000/60/60))"

        if [[ $HOURS -ne 0 ]]; then
            printf "%d:%02d:%02d" $HOURS $MINUTES $SECS
        elif [[ $MINUTES -ne 0 ]]; then
            printf "%d:%02d" $MINUTES $SECS
        else
            printf "%d" $SECS
        fi
        if [[ ${MILLISECONDS} -ne 0 ]]; then
            printf ".%03d" $MILLISECONDS | sed s_0*\$__
        fi
    }

    # To answer your question completely:
    echo $(int_to_timecode $(($(timecode_to_int "00:00:04")-$(timecode_to_int "00:00:02"))))

以下是不使用日期工具的另一种方法:

    function timecode_to_int() {
      if [[ "$1" =~ ^((0*([0-9]+):)?0*([0-9]+):)?0*([0-9]+)(\.([0-9]{1,3}))? ]]; then
        VALUE=$((${BASH_REMATCH[5]}))
        [[ -n "${BASH_REMATCH[4]}" ]] && VALUE=$((${BASH_REMATCH[4]}*60+$VALUE))
        [[ -n "${BASH_REMATCH[3]}" ]] && VALUE=$((${BASH_REMATCH[3]}*60*60+$VALUE))
        VALUE=$(($VALUE*1000))

        if [[ -n "${BASH_REMATCH[7]}" ]]; then
          VALUE=$(($VALUE+10#$(printf %-3s ${BASH_REMATCH[7]} | sed s_\ _0_g)))
        fi
        echo "${VALUE}"
      else
        echo "Invalid timecode '$1', aborting." >&2
        exit 1
      fi
    }

    function int_to_timecode() {
        MILLISECONDS="$(($1%1000))"
        SECS="$(($1/1000%60))"
        MINUTES="$(($1/1000/60%60))"
        HOURS="$(($1/1000/60/60))"

        if [[ $HOURS -ne 0 ]]; then
            printf "%d:%02d:%02d" $HOURS $MINUTES $SECS
        elif [[ $MINUTES -ne 0 ]]; then
            printf "%d:%02d" $MINUTES $SECS
        else
            printf "%d" $SECS
        fi
        if [[ ${MILLISECONDS} -ne 0 ]]; then
            printf ".%03d" $MILLISECONDS | sed s_0*\$__
        fi
    }

    # To answer your question completely:
    echo $(int_to_timecode $(($(timecode_to_int "00:00:04")-$(timecode_to_int "00:00:02"))))

修复了bash中未正确转义数学的问题。若要在此情况下调用此FramesDate$$B-$A,时间码将始终低于24小时。我猜FramesDate$60的函数有问题,它的结果是00:02:00,而60帧的结果应该是00:00:02…修复了在bash中无法正确转义数学的问题。在这种情况下,要调用此FramesDate$$B-$A,时间码总是低于24小时。我猜frames2date$60的函数有问题,它的结果是00:02:00,而60帧的结果应该是00:00:02…如果输入时间是帧而不是时间格式,我如何使用您的建议?在这种情况下,30帧等于1秒。当我阅读原始问题时,你会得到它们的时间戳。如果是以帧数计算,则可以忽略输入转换步骤,这将为您提供下一个答案。如果输入时间是以帧数计算而不是以时间格式计算,我如何使用您的建议?在这种情况下,30帧等于1秒。当我阅读原始问题时,你会得到它们的时间戳。如果以帧数计算,则可以忽略输入转换步骤,这将为您提供下一个答案。对于命令行时间码转换和算术,您可能需要签出。对于命令行时间码转换和算术,您可能需要签出。