开发者

BASH: how to make timecode calculations

开发者 https://www.devze.com 2023-02-15 09:39 出处:网络
I wish to know how to calculate the difference among two video timecodes tha are in frames (in this case, a second equals 30 frames).

I wish to know how to calculate the difference among two video timecodes tha are in frames (in this case, a second equals 30 frames).

Let say the point A is 600 (00:00:02) and the point B is 120 (00:00:04).

How can I calculate the difference among pont 开发者_如何学编程A and B and echo the result in the 00:00:00.00 format (h:m:s) using bash?


UPDATE:

This is perfection: http://www.1728.com/angle.htm


First off, the export statements are redundant since the current shell is expanding $A and $B.

To get those numbers from the timestamps, GNU date will help:

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

Now you can do your calculations, then translate back at the end for display (as Edwin Buck said, don't try to store intermediate results in this format):

jinx:754 Z$ date -d @1299214878.44 +%T.%N
00:01:18.440000000


Keep all of the times in "frames", then display the frame as a time through a single formatting routine. If you attempt to convert to times, you'll make the mathematical conversions much harder (as time is a base 60 system).

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

should convert the number of frames into a time, note that you need to add years, months, and days if your times exceed 24 hours.


(Not Perfect) SOLUTION:

With the help of my friends, here's my complete bash script:

#!/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

I saved the script as "v" and here's how I call it from cli:

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

This way I can cut a part of a video file directly from ffmpeg cli. But the calculations among timecodes are not quite precise, so I need to work it on a little bit more to achieve this: http://www.1728.com/angle.htm


Here is another approach not using the date tool:

    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"))))
0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号