25,7 → 25,9 |
# |
# References: |
# Pages from I've taken snippets or wrote code based on them. |
# I refer to them in comments as e.g. [[R1]]. [[R1#3]] Means section 3 in R1 |
# I refer to them in comments as e.g. [[R1]]. [[R1#3]] Means section 3 in R1. |
# I also use internal references in the form [x1] (anchor -where to point-) |
# and [[x1]] (x-reference -point to what-). |
# [R1] Bash (and other shells) tips |
# <http://wooledge.org/mywiki/BashFaq> |
# [R2] List of officially registered FOURCCs and WAVE Formats (aka wFormatTag) |
77,7 → 79,8 |
# * Added codec ids for On2's VP3, VP4, VP5 and VP6, TechSmith Screen Capture |
# Codec (Camtasia's) and Theora, expanded list of FOURCCs of Indeo's |
# codecs. |
# * Added $end_offset / -E, used to eliminate some seconds from the end |
# * Added -E / --end_offset / $DEFAULT_END_OFFSET, used to eliminate some |
# seconds from the end |
# |
|
set -e |
84,8 → 87,12 |
|
# TODO / FIXME: |
# * [[R1#22]] states that not all bc versions understand '<', more info required |
# * [[x1]] Find out why the order of ffmpeg arguments breaks some files. |
# * [[x2]] Find out if egrep is safe to use or grep -E is more commonplace. |
# |
|
# {{{ # Constants |
|
# Configuration file, please, use this file to modify the behaviour of the |
# script. Using this allows overriding some variables (see below) |
# to your liking. Only lines with a variable assignment are evaluated, |
104,7 → 111,6 |
# The variables that can be overriden are below the block of constants ahead. |
declare -r CFGFILE=~/.vcs.conf |
|
# Constants {{{ |
# see $decoder |
declare -ri DEC_MPLAYER=1 DEC_FFMPEG=3 |
# See $timecode_from |
119,10 → 125,19 |
declare -ri V_ALL=5 V_NONE=-1 V_ERROR=1 V_WARN=2 V_INFO=3 |
# see $font_filename and $FONT_MINCHO |
declare -ri FF_DEFAULT=5 FF_MINCHO=7 |
# Indexes in $VID |
declare -ri W=0 H=1 FPS=2 LEN=3 VCODEC=4 ACODEC=5 VDEC=6 CHANS=7 |
# Exit codes, same numbers as /usr/include/sysexits.h |
declare -r EX_OK=0 EX_USAGE=64 EX_UNAVAILABLE=69 \ |
EX_NOINPUT=66 EX_SOFTWARE=70 EX_CANTCREAT=73 \ |
EX_INTERRUPTED=79 # This one is not on sysexits.h |
# The context allows the creator to identify which contact sheet it is creating |
# (CTX_*) HL: Highlight (-l), STD: Normal, EXT: Extended (-e) |
declare -ri CTX_HL=1 CTX_STD=2 CTX_EXT=3 |
|
# }}} # End of constants |
|
# Override-able variables {{{ |
# {{{ # Override-able variables |
|
declare -i DEFAULT_INTERVAL=300 |
declare -i DEFAULT_NUMCAPS=16 |
221,6 → 236,8 |
|
# }}} # End of override-able variables |
|
# {{{ # Variables |
|
# Options and other internal usage variables, no need to mess with this! |
declare interval=$DEFAULT_INTERVAL # Interval of captures (=numsecs/numcaps) |
declare -i numcaps=$DEFAULT_NUMCAPS # Number of captures (=numsecs/interval) |
242,20 → 259,14 |
# go there |
# This holds the output of mplayer -identify on the current video |
declare MPLAYER_CACHE= |
# This holds the parsed values of MPLAYER_CACHE... |
# This holds the parsed values of MPLAYER_CACHE, see also the Indexes in VID |
# (defined in the constants block) |
declare -a VID= |
# ...and these are the indexes in $VID |
declare -ri W=0 H=1 FPS=2 LEN=3 VCODEC=4 ACODEC=5 VDEC=6 CHANS=7 |
|
# Exit codes, same codes as /usr/include/sysexits.h |
declare -r EX_OK=0 EX_USAGE=64 EX_UNAVAILABLE=69 \ |
EX_NOINPUT=66 EX_SOFTWARE=70 EX_CANTCREAT=73 \ |
EX_INTERRUPTED=79 # This one is not on sysexits.h |
|
# Workaounds: |
# Workarounds: |
# Argument order in FFmpeg is important -ss before or after -i will make |
# the capture work or not depending on the file. XXX: I've yet to find why :P |
# Anyway, this is used to change the order. See -Wo. |
# the capture work or not depending on the file. See -Wo. |
# TODO: [x1]. |
# Admittedly the workaraound is abit obscure: those variables will be added to |
# the ffmpeg arguments, before and after -i, replacing spaces by the timestamp. |
# e.g.: for second 50 '-ss ' will become '-ss 50' while '' will stay empty |
289,13 → 300,14 |
# The width and height are those of an individual capture |
# It is executed by create_contact_sheet() |
declare CSHEET_DELEGATE=csheet_montage |
# The context allows the creator to identify which contact sheet it is creating |
# (CTX_*) HL: Highlight (-l), STD: Normal, EXT: Extended (-e) |
declare -ri CTX_HL=1 CTX_STD=2 CTX_EXT=3 |
|
# Gravity of the timestamp (will be override-able in the future) |
declare grav_timestamp=SouthEast |
|
# }}} # Variables |
|
# {{{ # Configuration handling |
|
# These are the variables allowed to be overriden in the config file, |
# please. |
# They're REGEXes, they'll be concatenated to form a regex like |
381,6 → 393,8 |
fi |
} |
|
# }}} # Configuration handling |
|
# {{{ # Convenience functions |
|
# Returns true if input is composed only of numbers |
645,7 → 659,7 |
let 'retval++' |
fi |
done |
# XXX: Is egrep universal enough ? |
# TODO: [x2] |
|
return $retval |
} |
1577,6 → 1591,117 |
cleanup |
} |
|
# }}} # Core functionality |
|
# {{{ # Debugging helpers |
|
# Tests integrity of some operations. |
# Used to test internal changes for consistency. |
# It helps me to identify incorrect optimizations. |
# unit_test() |
unit_test() { |
local t op val ret comm retval=0 |
|
# Textual tests, compare output to expected output |
# Tests are in the form "operation arguments correct_result #Description" |
local TESTS=( |
"rmultiply 1,1 1 #Identity" |
"rmultiply 16/9,1 2 #Rounding" # 1.77 rounded 2 |
"rmultiply 4/3 1 #Rounding" # 1.33 rounded 1 |
"rmultiply 1,16/9 2 #Commutative property" |
"rmultiply 1.7 2 #Alternate syntax" |
|
"ceilmultiply 1,1 1 #" |
"ceilmultiply 4/3 2 #" # 1.33 rounded 2 |
"ceilmultiply 7,1/2 4 #" # 3.5 rounded 4 |
"ceilmultiply 7/2 4 #Alternative syntax" |
"ceilmultiply 1/2,7 4 #Commutative property" |
|
"pad 10 0 0000000000 #Padding" |
"pad 1 20 20 #Unneeded padding" |
"pad 5 23.3 023.3 #Floating point padding" |
|
"guess_aspect 720 576 4/3 #DVD AR Guess" |
"guess_aspect 1024 576 1024/576 #Unsupported Wide AR Guess" |
|
"tolower ABC abc #lowercase conversion" |
|
"pyth_th 4 3 5 #Integer pythagorean theorem" |
"pyth_th 16 9 18.35755975068581929849 #FP pythagorean theorem" |
) |
|
for t in "${TESTS[@]}" ; do |
# Note the use of ! as separator, this is because # and / are used in |
# many of the inputs |
comm=$(sed 's!.* #!!g' <<<$t) |
# Expected value |
val=$(sed -r "s!.* (.*) #$comm\$!\1!g"<<<$t) |
op=$(sed "s! $val #$comm\$!!g" <<<$t) |
if [ -z "$comm" ]; then |
comm=unnamed |
fi |
ret=$($op) || true |
|
if [ "$ret" != "$val" ] && fptest "$ret" -ne "$val" ; then |
error "Failed test ($comm): '$op $val'. Got result '$ret'." |
let 'retval++,1' # The ,1 ensures let doesn't fail |
else |
inf "Passed test ($comm): '$op $val'." |
fi |
done |
|
# Returned value tests, compare return to expected return |
local TESTS=( |
# Don't use anything with a RE meaning |
|
# Floating point numeric "test" |
"fptest 3 -eq 3 0 #FP test" |
"fptest 3.2 -gt 1 0 #FP test" |
"fptest 1/2 -le 2/3 0 #FP test" |
"fptest 6.34 -gt 6.34 1 #FP test" |
"fptest 1>0 -eq 1 0 #FP -logical- test" |
|
"is_number 3 0 #Numeric recognition" |
"is_number '3' 1 #Quoted numeric recognition" |
"is_number 3.3 1 #Non-numeric recognition" |
|
"is_float 3.33 0 #Float recognition" |
"is_float 3 0 #Float recognition" |
"is_float 1/3 1 #Non-float recognition" |
|
"is_fraction 1/1 0 #Fraction recognition" |
"is_fraction 1 1 #non-fraction recognition" |
"is_fraction 1.1 1 #Non-fraction recognition" |
) |
for t in "${TESTS[@]}"; do |
comm=$(sed 's!.* #!!g' <<<$t) |
# Expected value |
val=$(sed -r "s!.* (.*) #$comm\$!\1!g"<<<$t) |
op=$(sed "s! $val #$comm\$!!g" <<<$t) |
if [ -z "$comm" ]; then |
comm=unnamed |
fi |
ret=0 |
$op || { |
ret=$? |
} |
|
if [ $val -eq $ret ]; then |
inf "Passed test ($comm): '$op; returns $val'." |
else |
error "Failed test ($comm): '$op; returns $val'. Returned '$ret'" |
let 'retval++,1' |
fi |
done |
|
return $retval |
} |
|
|
# }}} # Debugging helpers |
|
# {{{ # Help / Info |
|
# Prints the program identification to stderr |
show_vcs_info() { # Won't be printed in quiet modes |
inf "Video Contact Sheet *NIX v${VERSION}, (c) 2007 Toni Corvera" "sgr0" |
1688,114 → 1813,8 |
EOF |
} |
|
# }}} # Core functionality |
# }}} # Help / Info |
|
# {{{ # Debugging helpers |
|
# Tests integrity of some operations. |
# Used to test internal changes for consistency. |
# It helps me to identify incorrect optimizations. |
# unit_test() |
unit_test() { |
local t op val ret comm retval=0 |
|
# Textual tests, compare output to expected output |
# Tests are in the form "operation arguments correct_result #Description" |
local TESTS=( |
"rmultiply 1,1 1 #Identity" |
"rmultiply 16/9,1 2 #Rounding" # 1.77 rounded 2 |
"rmultiply 4/3 1 #Rounding" # 1.33 rounded 1 |
"rmultiply 1,16/9 2 #Commutative property" |
"rmultiply 1.7 2 #Alternate syntax" |
|
"ceilmultiply 1,1 1 #" |
"ceilmultiply 4/3 2 #" # 1.33 rounded 2 |
"ceilmultiply 7,1/2 4 #" # 3.5 rounded 4 |
"ceilmultiply 7/2 4 #Alternative syntax" |
"ceilmultiply 1/2,7 4 #Commutative property" |
|
"pad 10 0 0000000000 #Padding" |
"pad 1 20 20 #Unneeded padding" |
"pad 5 23.3 023.3 #Floating point padding" |
|
"guess_aspect 720 576 4/3 #DVD AR Guess" |
"guess_aspect 1024 576 1024/576 #Unsupported Wide AR Guess" |
|
"tolower ABC abc #lowercase conversion" |
|
"pyth_th 4 3 5 #Integer pythagorean theorem" |
"pyth_th 16 9 18.35755975068581929849 #FP pythagorean theorem" |
) |
|
for t in "${TESTS[@]}" ; do |
# Note the use of ! as separator, this is because # and / are used in |
# many of the inputs |
comm=$(sed 's!.* #!!g' <<<$t) |
# Expected value |
val=$(sed -r "s!.* (.*) #$comm\$!\1!g"<<<$t) |
op=$(sed "s! $val #$comm\$!!g" <<<$t) |
if [ -z "$comm" ]; then |
comm=unnamed |
fi |
ret=$($op) || true |
|
if [ "$ret" != "$val" ] && fptest "$ret" -ne "$val" ; then |
error "Failed test ($comm): '$op $val'. Got result '$ret'." |
let 'retval++,1' # The ,1 ensures let doesn't fail |
else |
inf "Passed test ($comm): '$op $val'." |
fi |
done |
|
# Returned value tests, compare return to expected return |
local TESTS=( |
# Don't use anything with a RE meaning |
|
# Floating point numeric "test" |
"fptest 3 -eq 3 0 #FP test" |
"fptest 3.2 -gt 1 0 #FP test" |
"fptest 1/2 -le 2/3 0 #FP test" |
"fptest 6.34 -gt 6.34 1 #FP test" |
"fptest 1>0 -eq 1 0 #FP -logical- test" |
|
"is_number 3 0 #Numeric recognition" |
"is_number '3' 1 #Quoted numeric recognition" |
"is_number 3.3 1 #Non-numeric recognition" |
|
"is_float 3.33 0 #Float recognition" |
"is_float 3 0 #Float recognition" |
"is_float 1/3 1 #Non-float recognition" |
|
"is_fraction 1/1 0 #Fraction recognition" |
"is_fraction 1 1 #non-fraction recognition" |
"is_fraction 1.1 1 #Non-fraction recognition" |
) |
for t in "${TESTS[@]}"; do |
comm=$(sed 's!.* #!!g' <<<$t) |
# Expected value |
val=$(sed -r "s!.* (.*) #$comm\$!\1!g"<<<$t) |
op=$(sed "s! $val #$comm\$!!g" <<<$t) |
if [ -z "$comm" ]; then |
comm=unnamed |
fi |
ret=0 |
$op || { |
ret=$? |
} |
|
if [ $val -eq $ret ]; then |
inf "Passed test ($comm): '$op; returns $val'." |
else |
error "Failed test ($comm): '$op; returns $val'. Returned '$ret'" |
let 'retval++,1' |
fi |
done |
|
return $retval |
} |
|
# }}} # Debugging helpers |
|
#### Execution starts here #### |
|
# If tput isn't found simply ignore tput commands |
2077,4 → 2096,4 |
|
# }}} # Command line parsing |
|
# vim:set ts=4 ai: # |
# vim:set ts=4 ai foldmethod=marker: # |