Subversion Repositories pub

Compare Revisions

No changes between revisions

Ignore whitespace Rev 285 → Rev 286

/video-contact-sheet/trunk/CHANGELOG
1,3 → 1,33
1.0.5b: (2007-04-20)
* INTERNAL: Split functionality in more separate pieces (functions)
* BUGFIX: Corrected --aspect declaration
* CLEANUP: Put all temporary files in the same temporary directory
* FEATURE: Highlight support
* FEATURE: Extended mode (-e)
* FEATURE: Added -U (--fullname)
* Requirements detection now prints all failed requirements
* BUGFIX: (Regression introduced in 1.0.4b) Fail if interval is longer
than video
* Don't print the sucess line unless it was really successful
* Allow quiet operation (-q and -qq), and different verbosity levels
(only through config overrides)
* Print vcs' identification on operation
* FEATURE: Auto aspect ratio (-A, --autoaspect)
* INTERNAL: Added better documentation of functions
* Print coloured messages if possible (can be disabled by overriding
$plain_messages)
* FEATURE: Command line overrides (-O, --override)
* BUGFIX: Don't allow setting -n0
* Renamed codec ids of WMA2 (to WMA8) and WMA3 (to WMA9)
* Changed audio codec ids from MPEG-1 to MPEG, as there's no difference,
from mplayer's identification at least, between MPEG-1 and MPEG-2
* Audio identified as MP2 can also actually be MP1, added it to the codec id
* Added codec ids for: Vorbis, WMA7/WMA1, WMV1/WMV7, PCM, DivX ;),
OpenDivX, LAVC MPEG-4, MSMPEG-4 family, M-JPEG, RealVideo family, I420,
Sorenson 1 & 3, QDM2, and some legacy codecs (Indeo 3.2, Indeo 5.0,
MS Video 1 and MS RLE)
* Print the number of channels if != 2
 
1.0.4b: (2007-04-17)
* Added error checks for failures to create vidcap or to process it
convert
/video-contact-sheet/trunk/vcs
1,6 → 1,6
#!/bin/bash
#
# $Rev: 272 $ $Date: 2007-04-17 04:31:12 +0200 (dt, 17 abr 2007) $
# $Rev: 291 $ $Date: 2007-04-21 02:21:32 +0200 (ds, 21 abr 2007) $
#
# vcs
# Video Contact Sheet *NIX: Generates contact sheets (previews) of videos
24,28 → 24,43
# Author: Toni Corvera <outlyer@outlyer.net>
#
 
declare -r VERSION="1.0.4b"
declare -r VERSION="1.0.5b"
#
# History (The full changelog was moved to a separate file and can be found
# at <http://p.outlyer.net/vcs/files/CHANGELOG>).
#
# 1.0.4b: (2007-04-17)
# * Added error checks for failures to create vidcap or to process it
# convert
# * BUGFIX: Corrected error check on tempdir creation
# * BUGFIX: Use temporary locations for temporary files (thanks to
# Alon Levy).
# * Aspect ratio support (might be buggy). Requires bc.
# * Added $safe_rename_pattern to allow overriding the default alternate
# naming when the output file exists
# * Moved previous previous versions' changes to a separate file.
# * Support for per-dir and system-wide configuration files. Precedence
# in ascending order:
# /etc/vcs.conf ~/.vcs.conf ./vcs.conf
# * Added default_options (broken, currently ignored)
# * BUGFIX: (Apparently) Corrected the one-vidcap-less/more bug
# * Added codec ids of WMV9 and WMA3
# TODO: Support for ms timestamps (ffmpeg supports it e.g. 54.9 is ok and != 54)
#
# 1.0.5b: (2007-04-20)
# * INTERNAL: Split functionality in more separate pieces (functions)
# * BUGFIX: Corrected --aspect declaration
# * CLEANUP: Put all temporary files in the same temporary directory
# * FEATURE: Highlight support
# * FEATURE: Extended mode (-e)
# * FEATURE: Added -U (--fullname)
# * Requirements detection now prints all failed requirements
# * BUGFIX: (Regression introduced in 1.0.4b) Fail if interval is longer
# than video
# * Don't print the sucess line unless it was really successful
# * Allow quiet operation (-q and -qq), and different verbosity levels
# (only through config overrides)
# * Print vcs' identification on operation
# * FEATURE: Auto aspect ratio (-A, --autoaspect)
# * INTERNAL: Added better documentation of functions
# * Print coloured messages if possible (can be disabled by overriding
# $plain_messages)
# * FEATURE: Command line overrides (-O, --override)
# * BUGFIX: Don't allow setting -n0
# * Renamed codec ids of WMA2 (to WMA8) and WMA3 (to WMA9)
# * Changed audio codec ids from MPEG-1 to MPEG, as there's no difference,
# from mplayer's identification at least, between MPEG-1 and MPEG-2
# * Audio identified as MP2 can also actually be MP1, added it to the codec id
# * Added codec ids for: Vorbis, WMA7/WMA1, WMV1/WMV7, PCM, DivX ;),
# OpenDivX, LAVC MPEG-4, MSMPEG-4 family, M-JPEG, RealVideo family, I420,
# Sorenson 1 & 3, QDM2, and some legacy codecs (Indeo 3.2, Indeo 5.0,
# MS Video 1 and MS RLE)
# * Print the number of channels if != 2
#
 
set -e
 
76,6 → 91,10
declare -r PROGRAM_SIGNATURE="with Video Contact Sheet *NIX ${VERSION} <http://p.outlyer.net/vcs/>"
# see $safe_rename_pattern
declare -r DEFAULT_SAFE_REN_PATT="%b-%N.%e"
# see $extended_factor
declare -ri DEFAULT_EXT_FACTOR=4
# see $verbosity
declare -ri V_ALL=5 V_NONE=-1 V_ERROR=1 V_WARN=2 V_INFO=3
 
# }}} # End of constants
 
86,12 → 105,14
declare -i DEFAULT_COLS=2
# Text before the user name in the signature
declare user_signature="Preview created by"
# By default sign as the system's username
# By default sign as the system's username (see -u, -U)
declare user=$(id -un)
# Which of the two methods should be used to guess the number of thumbnails
declare -i timecode_from=$TC_INTERVAL
# Which of the two vidcappers should be used (see -F, -M)
# mplayer seems to fail for mpeg or WMV9 files, at least on my system
# also, ffmpeg allows better seeking: ffmpeg allows exact second.fraction
# seeking while mplayer apparently only seeks to nearest keyframe
declare -i decoder=$DEC_FFMPEG
# Options used in imagemagick, these options set the final aspect
# of the contact sheet
128,6 → 149,11
#
# If overridden with an incorrect value it will be silently set to the default
declare safe_rename_pattern="$DEFAULT_SAFE_REN_PATT"
# Controls how many extra captures will be created in the extended mode
# (see -e), 0 is the same as disabling the extended mode
# This number is multiplied by the total number of captures to get
# the number of extra captures. So, e.g. -n2 -e2 leads to 4 extra captures.
declare extended_factor=0
# Options added always to the ones in the command line
# (command line options override them).
# Note using this is a bit tricky :P mostly because I've no clue of how this
135,8 → 161,13
# As an example: you want to set always the title to "My Title" and output
# to jpeg: default_options="-T'My Title' -j"
declare default_options=
# Verbosity level so far from the command line can only be muted (see -q)
# it can be overridden, though
declare -i verbosity=$V_ALL
# When set to 0 the status messages printed by vcs while running
# are coloured if the terminal supports it. Set to 1 if this annoys you.
declare -i plain_messages=0
 
 
# }}} # End of override-able variables
 
# Options and other internal usage variables, no need to mess with this!
150,45 → 181,58
declare -i cols=$DEFAULT_COLS # Number of output columns
declare -i manual_mode=0 # if 1, only command line timestamps will be used
declare aspect_ratio=0 # If 0 no transformations done (see -a)
# If -1 try to guess (see -A)
 
declare -a TEMPSTUFF=( ) # Temporal files
declare -a TIMECODES=( ) # Timestamps of the video captures
declare -a HLTIMECODES=( ) # Timestamps of the highlights (see -l)
 
declare VCSTEMPDIR= # Temporal directory, all temporal files
# go there
# This holds the output of mplayer -identify on the current video
declare MPLAYER_CACHE=
# This holds the parsed values of MPLAYER_CACHE...
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
 
# 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
# (override1|override2|...).
# Don't mess with this unless you're pretty sure of what you're doing.
# All this extra complexity is done to avoid including the config
# file directly for security reasons.
declare -ra ALLOWED_OVERRIDES=(
'user'
'user_signature'
'bg_.*'
'font_.*'
'pts_.*'
'fg_.*'
'output_quality'
'DEFAULT_INTERVAL'
'DEFAULT_NUMCAPS'
'DEFAULT_COLS'
'decoder'
'output_format'
'shoehorned'
'timecode_from'
'safe_rename_pattern'
'default_options'
'extended_factor'
'verbosity'
'plain_messages'
)
 
# Loads the configuration files if present
# load_config()
load_config() {
# 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
# (override1|override2|...).
# Don't mess with this unless you're pretty sure of what you're doing.
# All this extra complexity is done to avoid including the config
# file directly for security reasons.
declare -ra ALLOWED_OVERRIDES=(
'user'
'user_signature'
'bg_.*'
'font_.*'
'pts_.*'
'fg_.*'
'output_quality'
'DEFAULT_INTERVAL'
'DEFAULT_NUMCAPS'
'DEFAULT_COLS'
'decoder'
'output_format'
'shoehorned'
'timecode_from'
'safe_rename_pattern'
'default_options'
)
 
local compregex=$( sed 's/ /|/g' <<<${ALLOWED_OVERRIDES[*]} )
 
local basecfg="$(basename "$CFGFILE")"
local CONFIGS=( /etc/vcs.conf $CFGFILE ./vcs.conf)
 
for cfgfile in ${CONFIGS[*]} ;do
195,25 → 239,50
if [ ! -f "$cfgfile" ]; then continue; fi
 
while read line ; do # auto variable $line
# Don't allow ';', FIXME: dunno how secure that really is
if ! egrep -q '^[[:space:]]*[a-zA-Z_][a-zA-Z0-9_]*=[^;]*' <<<"$line" ; then
continue
fi
if ! egrep -q "^($compregex)=" <<<"$line" ; then
continue
fi
# FIXME: Only print in verbose mose
# FIXME: Only for really overridden ones
local varname=$(sed -r 's/^[[:space:]]*([a-zA-Z0-9_]*)=.*/\1/'<<<$line)
echo "Overridden variable $varname from file $cfgfile"
eval $line
override "$line" "file $cfgfile" # Feeding it comments should be harmless
done <$cfgfile
done
}
 
# Do an override
# It takes basically an assignment (in the same format as bash)
# to one of the override-able variables (see $ALLOWED_OVERRIDES).
# There are some restrictions though. Currently ';' is not allowed to
# be in the assignment.
# override($1 = bash variable assignment, $2 = source)
override() {
local o="$1"
local src="$2"
 
local compregex=$( sed 's/ /|/g' <<<${ALLOWED_OVERRIDES[*]} )
 
# Don't allow ';', FIXME: dunno how secure that really is...
# FIXME: ...it doesn't really works anyway
if ! egrep -q '^[[:space:]]*[a-zA-Z_][a-zA-Z0-9_]*=[^;]*' <<<"$o" ; then
return
fi
if ! egrep -q "^($compregex)=" <<<"$o" ; then
return
fi
 
local varname=$(sed -r 's/^[[:space:]]*([a-zA-Z0-9_]*)=.*/\1/'<<<"$o")
local varval=$(sed -r 's/[^=]*=(.*)/\1/'<<<"$o")
# FIXME: Security!
local curvarval=
eval curvarval='$'"$varname"
if [ "$curvarval" == "$varval" ]; then
warn "Ignored override '$varname' (already had same value)"
else
eval "$varname=\"$varval\""
# FIXME: Only for really overridden ones
warn "Overridden variable '$varname' from $src"
fi
}
 
# {{{ # Convenience functions
 
# Returns true if input is composed only of numbers
# is_number($1 = input)
is_number() {
egrep -q '^[0-9]+$' <<<"$1"
}
220,6 → 289,7
 
# Returns true if input can be parsed as a floating point number
# Accepted: XX.YY XX. .YY (.24=0.24
# is_float($1 = input)
is_float() {
egrep -q '^([0-9]+\.?([0-9])?+|(\.[0-9]+))$'<<<"$1"
}
226,22 → 296,40
 
# Returns true if input is a fraction
# Only accepts XX/YY
# is_fraction($1 = input)
is_fraction() {
egrep -q '^[0-9]+/[0-9]+$'<<<"$1"
}
 
# Rounded product
# multiplies parameters and prints the result, rounded to the closest int
# parameters can be separated by commas or spaces
# e.g.: rmultiply 4/3,576 OR 4/3 576 = 4/3 * 576 = 768
# rmultiply($1 = operator1, [$2 = operator2, ...])
# rmultiply($1 = "operator1,operator2,...")
rmultiply() {
local exp=$(sed 's/[ ,]/*/g'<<<"$@") # bc expression
local f=$(bc -lq<<<"$exp") # exact float value
# division is integer by default (without -l) so it's the smae
# as rounding to the lower int
bc -q <<<"( $f + 0.5 ) / 1"
}
 
# Prints the width correspoding to the input height and the variable
# aspect ratio
# compute_width(height) (=AR*height) (rounded)
# compute_width($1 = height) (=AR*height) (rounded)
compute_width() {
local wfloat=$(bc -lq <<< "$aspect_ratio * $1")
local wint=$(bc -q <<<"($wfloat+0.5)/1")
echo $wint
rmultiply $aspect_ratio,$1
}
 
# Parse an interval and print the corresponding value in seconds
# returns something not 0 if the interval is not recognized.
#
# The current code is a tad permissive, it allows e.g. things like
# 10m1h (equivalent to 1h10m)
# 1m1m (equivalent to 2m)
# I don't see reason to make it more anal, though.
# get_interval($1 = interval)
get_interval() {
if is_number "$1" ; then echo $1 ; return 0 ; fi
 
282,6 → 370,9
return 0
}
 
# Pads a string with zeroes on the left until it is at least
# the indicated length
# pad($1 = minimum length, $2 = string)
pad() {
local len=$1
local str=$2
293,6 → 384,9
echo $str
}
 
# Prints a number of seconds in a more human readable form
# e.g.: 3600 becomes 1:00:00
# pretty_stamp($1 = seconds)
pretty_stamp() {
if ! is_number "$1" ; then return $EX_USAGE ; fi
 
313,6 → 407,10
echo $R
}
 
# Prints the size of a file in a human friendly form
# The units are in the IEC/IEEE/binary format (e.g. MiB -for mebibytes-
# instead of MB -for megabytes-)
# get_pretty_size($1 = file)
get_pretty_size() {
local f="$1"
 
339,7 → 437,9
}
 
# Rename a file, if the target exists, try with appending numbers to the name
# And print the output name to stderr
# And print the output name to stdout
# See $safe_rename_pattern
# safe_rename($1 = original file, $2 = target file)
safe_rename() {
local from="$1"
local to="$2"
366,48 → 466,122
done
 
mv "$from" "$to"
echo "$to" >&2
echo "$to"
}
 
# Tests the presence of all required programs
# test_programs()
test_programs() {
for prog in mplayer convert montage ffmpeg bc ; do
local retval=0 last=0
for prog in mplayer convert montage bc ffmpeg ; do
type -pf "$prog" >/dev/null
local retval=$?
if [ $retval -ne 0 ] ; then
if [ $? -ne 0 ] ; then
error "Required program $prog not found!"
return $retval
let 'retval++'
fi
done
 
return $retval
}
 
# Remove any temporal files
# Does nothing if none has been created so far
# cleanup()
cleanup() {
if [ -z $TEMPSTUFF ]; then return 0 ; fi
echo "Cleaning up..." >&2 # TODO: Only in verbose mode
info "Cleaning up..."
rm -rf ${TEMPSTUFF[*]}
TEMPSTUFF=( )
}
 
# Exit callback. This function is executed on exit (correct, failed or
# interrupted)
# exithdlr()
exithdlr() {
cleanup
}
 
# Print some text to stderr
# Feedback handling, these functions are use to print messages respecting
# the verbosity level
# Optional color usage added from explanation found in
# <http://wooledge.org/mywiki/BashFaq>
#
# error($1 = text)
error() {
echo "$1" >&2
if [ $verbosity -ge $V_ERROR ]; then
if [ $plain_messages -eq 0 ]; then
tput bold ; tput setaf 1;
fi
# sgr0 is always used, this way if
# a) something prints inbetween messages it isn't affected
# b) if plain_messages is overridden colour stops after the override
echo "$1" >&2 ; tput sgr0
fi
}
#
# Print a non-fatal error or warning
# warning($1 = text)
warn() {
if [ $verbosity -ge $V_WARN ]; then
if [ $plain_messages -eq 0 ]; then
tput bold ; tput setaf 3;
fi
echo "$1" >&2 ; tput sgr0
fi
}
#
# Print an informational message
# info($1 = text)
info() {
if [ $verbosity -ge $V_INFO ]; then
if [ $plain_messages -eq 0 ]; then
tput bold ; tput setaf 2;
fi
echo "$1" >&2 ; tput sgr0
fi
}
#
# Same as info but with no colour ever.
# infoplain($1 = text)
infoplain() {
if [ $verbosity -ge $V_INFO ]; then
echo "$1" >&2
fi
}
 
# }}} # Convenience functions
 
# {{{ # Core functionality
 
MPLAYER_CACHE=
numsecs() {
echo $(grep ID_LENGTH <<<"$MPLAYER_CACHE"| cut -d'=' -f2 | cut -d. -f1)
# Creates a new temporary directory
# create_temp_dir()
create_temp_dir() {
VCSTEMPDIR=$(mktemp -d -t vcs.XXXXXX)
if [ ! -d "$VCSTEMPDIR" ]; then
error "Error creating temporary directory"
return $EX_CANTCREAT
fi
TEMPSTUFF+=( "$VCSTEMPDIR" )
}
 
# Create a new temporal file and print its filename
# new_temp_file($1 = suffix)
new_temp_file() {
local r=$(tempfile -d "$VCSTEMPDIR" -p "vcs-" -s "$1")
if [ ! -f "$r" ]; then
error "Failed to create temporary file"
return $EX_CANTCREAT
fi
TEMPSTUFF+=( "$r" )
echo "$r"
}
 
# Add to $TIMECODES the timecodes at which a capture should be taken
# from the current video
# compute_timecodes($1 = timecode_from, $2 = interval, $3 = numcaps)
compute_timecodes() {
local st=0 numsecs=$(numsecs) end=
end=$numsecs
local st=0 end=${VID[$LEN]} tcfrom=$1 tcint=$2 tcnumcaps=$3
# globals: fromtime, totime, timecode_from, TIMECODES
if [ $st -lt $fromtime ]; then
st=$fromtime
fi
416,15 → 590,15
fi
 
local inc=
if [ "$timecode_from" -eq $TC_INTERVAL ]; then
inc=$interval
elif [ "$timecode_from" -eq $TC_NUMCAPS ]; then
if [ "$tcfrom" -eq $TC_INTERVAL ]; then
inc=$tcint
elif [ "$tcfrom" -eq $TC_NUMCAPS ]; then
# Numcaps mandates: timecodes are obtained dividing the length
# by the number of captures
if [ $numcaps -eq 1 ]; then # Special case, just one capture, center it
if [ $tcnumcaps -eq 1 ]; then # Special case, just one capture, center it
inc=$(( ($end-$st) / 2 + 1))
else
inc=$(( ($end-$st) / $numcaps ))
inc=$(( ($end-$st) / $tcnumcaps ))
fi
else
error "Internal error"
431,7 → 605,7
return $EX_SOFTWARE
fi
 
if [ $inc -gt $numsecs ]; then
if [ $inc -gt ${VID[$LEN]} ]; then
error "Interval is longer than video length, skipping $f"
return $EX_USAGE
fi
440,10 → 614,136
for stamp in $(seq $st $inc $end); do
LTC+=( $stamp )
done
unset LTC[0] # Initial cap (=$st)
unset LTC[0] # Discard initial cap (=$st)
TIMECODES=( ${TIMECODES[*]} ${LTC[*]} )
}
 
# Tries to guess an aspect ratio comparing width and height to some
# known values (e.g. VCD resolution turns into 4/3)
# guess_aspect()
guess_aspect() {
# mplayer's ID_ASPECT seems to be always 0 ¿?
local w=${VID[$W]} h=${VID[$H]}
if [ $w -eq 352 ]; then # VCD / DVD @ VCD Res. / Half-D1 / CVD
if [ $h -eq 288 ] || [ $h -eq 240 ]; then
aspect_ratio=4/3
elif [ $h -eq 576 ] || [ $h -eq 480 ]; then # Half-D1 / CVD
aspect_ratio=4/3
fi
elif [ $w -eq 704 ] || [ $w -eq 720 ]; then # DVD / DVB
# Actually for 720x576/720x480 16/9 is as good a guess
if [ $h -eq 576 ] || [ $h -eq 480 ]; then
aspect_ratio=4/3
fi
elif [ $w -eq 480 ]; then # SVCD
if [ $h -eq 576 ] || [ $h -eq 480 ]; then
aspect_ratio=4/3
fi
else
warn "Couldn't guess aspect ratio."
aspect_ratio=$(bc -lq <<<"$w / $h")
fi
local AR=$(sed -r 's/(\.[0-9]{2}).*/\1/g'<<<$aspect_ratio)
info "Aspect ratio set to $AR"
}
 
# Capture a frame
# capture($1 = filename, $2 = second)
capture() {
local f=$1 stamp=$2
local VIDCAPFILE=00000001.png
# globals: $shoehorned $decoder
 
if [ $decoder -eq $DEC_MPLAYER ]; then
mplayer -sws 9 -ao null -benchmark -vo "png:z=0" -quiet \
-frames 1 -ss $stamp $shoehorned "$f" >/dev/null 2>&1
elif [ $decoder -eq $DEC_FFMPEG ]; then
# XXX: For some reason -ss before -i failed on my mkv sample
# while after -i it failed on my wmv9 sample ¿?
ffmpeg -y -ss $stamp -i "$f" -an -dframes 1 -vframes 1 -vcodec png \
-f rawvideo $shoehorned $VIDCAPFILE >/dev/null 2>&1
else
error "Internal error!"
return $EX_SOFTWARE
fi || {
local retval=$?
error "The capturing program failed!"
return $retval
}
if [ ! -f "$VIDCAPFILE" ] || [ "0" == "$(du "$VIDCAPFILE" | cut -f1)" ]; then
error "Failed to capture frame (at second $stamp)"
return $EX_SOFTWARE
fi
 
return 0
}
 
# Draw a timestamp in the file
# apply_stamp($1 = filename, $2 = timestamp, $3 = width, $4 = height)
apply_stamp() {
local filename=$1 timestamp=$2 width=$3 height=$4
 
local temp=$(new_temp_file ".png")
mv "$filename" "$temp"
# Add the timestamp to each vidcap, doing it here is much powerful/simple
# than with the next montage command
# Note the '!', it is necessary to apply aspect ratio change
convert -box '#000000aa' -geometry ${width}x${height}! \
-fill $fg_tstamps -pointsize $pts_tstamps -gravity SouthEast \
-stroke none -strokewidth 3 -annotate +5+5 " $(pretty_stamp $stamp) " \
"$temp" "$filename"
if [ ! -f "$filename" ]; then
error "Failed to add timestamp to capture"
mv "$temp" "$filename" # Leave things as before
return $EX_CANTCREAT
fi
}
 
# Sorts timestamps and removes duplicates
# clean_timestamps($1 = space separated timestamps)
clean_timestamps() {
# Note AFAIK sort only sorts lines, that's why y replace spaces by newlines
local s=$1
sed 's/ /\n/g'<<<"$s" | sort -n | uniq
}
 
# Fills the $MPLAYER_CACHE and $VID variables with the video data
# identify_video($1 = file)
identify_video() {
local f=$1
# Meta data extraction
# Note to self: Don't change the -vc as it would affect $vdec
MPLAYER_CACHE=$(mplayer -benchmark -ao null -vo null -identify -frames 0 -quiet "$f" 2>/dev/null | grep ^ID)
VID[$VCODEC]=$(grep ID_VIDEO_FORMAT <<<"$MPLAYER_CACHE" | cut -d'=' -f2) # FourCC
VID[$ACODEC]=$(grep ID_AUDIO_FORMAT <<<"$MPLAYER_CACHE" | cut -d'=' -f2)
VID[$VDEC]=$(grep ID_VIDEO_CODEC <<<"$MPLAYER_CACHE" | cut -d'=' -f2) # Decoder (!= Codec)
VID[$W]=$(grep ID_VIDEO_WIDTH <<<"$MPLAYER_CACHE" | cut -d'=' -f2)
VID[$H]=$(grep ID_VIDEO_HEIGHT <<<"$MPLAYER_CACHE" | cut -d'=' -f2)
VID[$FPS]=$(grep ID_VIDEO_FPS <<<"$MPLAYER_CACHE" | cut -d'=' -f2)
VID[$LEN]=$(grep ID_LENGTH <<<"$MPLAYER_CACHE"| cut -d'=' -f2 | cut -d. -f1)
# For some reason my (one track) samples have two ..._NCH, first one 0
VID[$CHANS]=$(grep ID_AUDIO_NCH <<<"$MPLAYER_CACHE"|cut -d'=' -f2|head -2|tail -1)
 
# Upon consideration:
#if grep -q '\.[0-9]*0$' <<<${VID[$FPS]} ; then
# # Remove trailing zeroes...
# VID[$FPS]=$(sed -r 's/(\.[1-9]*)0*$/\1/' <<<${VID[$FPS]})
# # ...And trailing decimal point
# VID[$FPS]=$(sed 's/\.$//'<<<${VID[$FPS]})
#fi
 
# Voodoo :P Remove (one) trailing zero
if [ "${VID[$FPS]:$(( ${#VID[$FPS]} - 1 ))}" == "0" ]; then
VID[$FPS]="${VID[$FPS]:0:$(( ${#VID[$FPS]} - 1 ))}"
fi
 
# Check sanity of the most important values
is_number "${VID[$W]}" && is_number "${VID[$H]}" && is_number "${VID[$LEN]}"
}
 
# Main function.
# Creates the contact sheet.
# process($1 = file)
process() {
local f=$1
 
453,34 → 753,27
error "File \"$f\" doesn't exist"
return $EX_NOINPUT
fi
echo "Processing $f..." >&2
info "Processing $f..."
 
# Meta data extraction
# Note to self: Don't change the -vc as it would affect $vdec
MPLAYER_CACHE=$(mplayer -benchmark -ao null -vo null -identify -frames 0 -quiet "$f" 2>/dev/null | grep ^ID)
local vcodec=$(grep ID_VIDEO_FORMAT <<<"$MPLAYER_CACHE" | cut -d'=' -f2) # FourCC
local acodec=$(grep ID_AUDIO_FORMAT <<<"$MPLAYER_CACHE" | cut -d'=' -f2)
local vdec=$(grep ID_VIDEO_CODEC <<<"$MPLAYER_CACHE" | cut -d'=' -f2) # Decoder
local width=$(grep ID_VIDEO_WIDTH <<<"$MPLAYER_CACHE" | cut -d'=' -f2)
local height=$(grep ID_VIDEO_HEIGHT <<<"$MPLAYER_CACHE" | cut -d'=' -f2)
local fps=$(grep ID_VIDEO_FPS <<<"$MPLAYER_CACHE" | cut -d'=' -f2)
identify_video "$f" || {
error "Found unsupported value while identifying video. Can't continue."
return $EX_SOFTWARE
}
 
# Vidcap/Thumbnail height
local vidcap_height=$th_height
if ! is_number "$vidcap_height" || [ "$vidcap_height" -eq 0 ]; then
vidcap_height=$height
vidcap_height=${VID[$H]}
fi
if [ "0" == "$aspect_ratio" ]; then
aspect_ratio=$(bc -lq <<< "$width / $height")
aspect_ratio=$(bc -lq <<< "${VID[$W]} / ${VID[$H]}")
elif [ "-1" == "$aspect_ratio" ]; then
guess_aspect
fi
local vidcap_width=$(compute_width $vidcap_height)
 
local numsecs=$(grep ID_LENGTH <<<"$MPLAYER_CACHE"| cut -d'=' -f2 | cut -d. -f1)
if ! is_number $numsecs ; then
error "Internal error!"
return $EX_SOFTWARE
fi
 
local nc=$numcaps
 
# Contact sheet minimum cols:
488,35 → 781,19
numcols=$nc
fi
 
# Tempdir
create_temp_dir
 
local dir=$(mktemp -d -t vcs.XXXXXX)
if [ ! -d "$dir" ]; then
error "Error creating temporary directory"
return $EX_CANTCREAT
fi
TEMPSTUFF+=( "$dir" )
 
local n=
 
# Compute the stamps (if in auto mode)...
TIMECODES=${initial_stamps[*]}
if [ $manual_mode -ne 1 ]; then
compute_timecodes
compute_timecodes $timecode_from $interval $numcaps || {
return $?
}
fi
 
n=1
local p=""
local montage_command="montage -font $font_tstamps -pointsize $pts_tstamps \
local base_montage_command="montage -font $font_tstamps -pointsize $pts_tstamps \
-gravity SouthEast -fill white "
local output=$(tempfile --prefix "vcs-" --suffix '-preview.png')
TEMPSTUFF+=( "$output" )
 
# Let's reorder the stamps, this away user-added stamps get their correct
# position also remove duplicates. Note AFAIK sort only sorts lines, that's
# why y replace spaces by newlines.
local stamps=$( sed 's/ /\n/g' <<<"${TIMECODES[*]}" | sort -n | uniq )
 
local output=$(new_temp_file '-preview.png')
local VIDCAPFILE=00000001.png
 
# If the temporal vidcap already exists, abort
525,60 → 802,64
return $EX_CANTCREAT
fi
 
local NUMSTAMPS=$(wc -w <<<"$stamps")
TEMPSTUFF+=( $VIDCAPFILE )
local capfile=
# TODO: Aspect ratio
for stamp in $stamps; do
# Note that it must be checked against numsecs and not endsec, to allow
# the user manually setting stamps beyond the boundaries
if [ $stamp -gt $numsecs ]; then continue; fi
 
echo "Generating capture #${n}/${NUMSTAMPS}..." >&2
# Highlighs
local hlfile="$VCSTEMPDIR/highlights.png" n=1 # Must be outside the if!
if [ "$HLTIMECODES" ]; then
local hlmontage_command="montage -gravity SouthEast -texture xc:LightGoldenRod "
local hlcapfile=
local pretty=
for stamp in $(clean_timestamps "${HLTIMECODES[*]}"); do
if [ $stamp -gt $numsecs ]; then let 'n++' && continue ; fi
pretty=$(pretty_stamp $stamp)
info "Generating highlight #${n}/${#HLTIMECODES[*]} ($pretty)..."
 
p=$(pad 6 $stamp).png
capfile=$dir/$p
capture "$f" $stamp || return $?
apply_stamp "$VIDCAPFILE" $pretty $vidcap_width $vidcap_height || return $?
 
if [ $decoder -eq $DEC_MPLAYER ]; then
mplayer -sws 9 -ao null -benchmark -vo "png:z=0" -quiet \
-frames 1 -ss $stamp $shoehorned "$f" >/dev/null 2>&1
elif [ $decoder -eq $DEC_FFMPEG ]; then
ffmpeg -y -ss $stamp -i "$f" -an -dframes 1 -vframes 1 -vcodec png \
-f rawvideo $shoehorned $VIDCAPFILE >/dev/null 2>&1
else
error "Internal error!"
return $EX_SOFTWARE
fi || {
local retval=$?
error "The capturing program failed!"
return $retval
}
if [ "0" == "$(du "$VIDCAPFILE" | cut -f1)" ]; then
error "Failed to capture frame (at second $stamp)"
return $EX_SOFTWARE
fi
hlcapfile=$(new_temp_file "-hl-$(pad 6 $n).png")
mv "$VIDCAPFILE" "$hlcapfile"
hlmontage_command+=" \"$hlcapfile\""
let 'n++'
done
 
let 'n++' # $n++
#if [ "$title" ]; then
# hlmontage_command+=" -font $font_heading -fill $fg_heading -title '$title'"
#fi
info "Composing highlights contact sheet..."
eval "$hlmontage_command -geometry ${vidcap_width}x${vidcap_height}!+10+5 \
-tile ${numcols}x -shadow \"$hlfile\""
unset hlcapfile hlmontage_command pretty
fi
unset n
 
# Add the timestamp to each vidcap, doing it here is much powerful/simple
# than with the next montage command
# Note the '!', it is necessary to apply aspect ratio change
convert -box '#000000aa' -geometry ${vidcap_width}x${vidcap_height}! \
-fill $fg_tstamps -pointsize $pts_tstamps -gravity SouthEast \
-stroke none -strokewidth 3 -annotate +5+5 " $(pretty_stamp $stamp) " \
$VIDCAPFILE "$capfile"
if [ ! -f "$capfile" ]; then
error "Failed to process capture"
return $EX_CANTCREAT
fi
# Normal captures
# TODO: Don't reference $VIDCAPFILE
local capfile= pretty= n=1 montage_command=$base_montage_command
for stamp in $(clean_timestamps "${TIMECODES[*]}"); do
pretty=$(pretty_stamp $stamp)
# Note that it must be checked against numsecs and not endsec, to allow
# the user manually setting stamps beyond the boundaries
# This shouldn't occur automatically anymore with the new code.
if [ $stamp -gt $numsecs ]; then let 'n++' && continue; fi
 
info "Generating capture #${n}/${#TIMECODES[*]} ($pretty)..."
 
capture "$f" $stamp || return $?
apply_stamp "$VIDCAPFILE" $pretty $vidcap_width $vidcap_height || return $?
capfile=$(new_temp_file "-cap-$(pad 6 $n).png")
# move to tempdir/<frame num>.png, cap num is padded to 6 characters
mv "$VIDCAPFILE" "$capfile"
montage_command+=" \"$capfile\""
let 'n++' # $n++
done
unset capfile
unset capfile pretty n
 
# geometry affects the source images, not the target one!
# Note the file name could also be added by using the "-title" option, but I reserved
# it for used set titles
# FIXME: Title should go before the highlights
montage_command+=" -geometry ${vidcap_width}x${vidcap_height}+10+5 -tile ${numcols}x -shadow"
if [ "$title" ]; then
montage_command+=" -font $font_heading -fill $fg_heading -title '$title'"
585,44 → 866,159
fi
montage_command+=" \"$output\""
 
echo "Composing contact sheet..." >&2
info "Composing standard contact sheet..."
eval $montage_command # eval is required to evaluate correctly the text in quotes!
unset montage_command
 
# Extended mode
local extoutput=
if [ "$extended_factor" != 0 ]; then
# Number of captures. Always rounded to a multiplier of 2
# TODO: Round it to a multiplier of the number of columns
local hlnc=$(bc -q <<<"( (${#TIMECODES[*]} * $extended_factor) / 2 * 2)")
unset TIMECODES # required step to get the right count
TIMECODES=${initial_stamps[*]}
compute_timecodes $TC_NUMCAPS "" $hlnc
unset hlnc
 
local n=1 w= h= capfile= pretty= montage_command=$base_montage_command
extoutput=$(new_temp_file "-extended.png")
 
# The image size of the extra captures is 1/4
let 'w=vidcap_width/2, h=vidcap_height/2'
 
for stamp in $(clean_timestamps "${TIMECODES[*]}"); do
pretty=$(pretty_stamp $stamp)
info "Generating capture from extended set: ${n}/${#TIMECODES[*]} ($pretty)..."
capture "$f" $stamp || return $?
apply_stamp "$VIDCAPFILE" $pretty $w $h || return $?
capfile=$(new_temp_file "-excap-$(pad 6 $n).png")
mv "$VIDCAPFILE" "$capfile"
montage_command+=" \"$capfile\""
let 'n++'
done
montage_command+=" -geometry ${w}x${h}+5+2 -tile $(($numcols * 2))x -shadow"
info "Composing extended contact sheet..."
eval $montage_command "$extoutput"
unset montage_command w h capfile pretty n
fi
 
# Codec "prettyfication"
case $( tr '[A-Z]' '[a-z]' <<<$vcodec) in
# Official FourCCs: <http://msdn2.microsoft.com/en-us/library/ms867195.aspx>
# Unofficial list: <http://www.fourcc.org/>
# Another software with a list of fourccs -> name mappings:
# <http://webcvs.freedesktop.org/clipart/experimental/rejon/getid3/getid3/module.audio-video.riff.php?view=markup>
local vcodec= acodec=
case $( tr '[A-Z]' '[a-z]' <<<${VID[$VCODEC]}) in
xvid) vcodec=Xvid ;;
dx50) vcodec="DivX 5" ;;
0x10000001) vcodec="MPEG-1" ;;
0x10000002) vcodec="MPEG-2" ;;
0x00000000) vcodec="Raw RGB" ;; # How correct is this?
avc1) vcodec="MPEG-4 AVC" ;;
wmv2) vcodec="WMV8" ;; # v2 is same as v8
wmv1) vcodec="WMV7" ;;
wmv2) vcodec="WMV8" ;;
wmv3) vcodec="WMV9" ;;
fmp4) vcodec="FFmpeg" ;; # XXX: Would LAVC be a better name?
mp42) vcodec="MS MPEG-4 v2" ;;
mpg4) vcodec="MS MPEG-4 v1" ;;
mp43) vcodec="MS MPEG-4 v3" ;;
div3) vcodec="DivX ;) Low Motion" ;; # Technically same as mp43
i420) vcodec="Raw I420" ;; # XXX: 420 presumably stands by 4:2:0 ?
rv10) vcodec="RealVideo 1.0/5.0" ;;
rv20) vcodec="RealVideo G2" ;;
svq1) vcodec="Sorenson Video 1" ;;
svq3) vcodec="Sorenson Video 3" ;;
# These are known FourCCs that I haven't tested against so far
wmva) vcodec="WMV9 Advanced Profile" ;; # Not VC1 compliant. Unsupported.
rv30) vcodec="RealVideo 8" ;;
rv40) vcodec="RealVideo 9/10" ;;
div4) vcodec="DivX ;) Fast Motion" ;;
divx) vcodec="DivX" ;; # OpenDivX / DivX 5(?)
iv50) vcodec="Indeo 5.0" ;;
mjpg) vcodec="M-JPEG" ;; # XXX: Actually mJPG != MJPG
# Legacy(-er) codecs (haven't seen files in these formats in awhile)
iv32) vcodec="Indeo 3.2" ;;
msvc) vcodec="Microsoft Video 1" ;;
mrle) vcodec="Microsoft RLE" ;;
*) # If not recognized show FOURCC
vcodec=${VID[$VCODEC]}
;;
esac
if [ "$vdec" == "ffodivx" ]; then
if [ "${VID[$VDEC]}" == "ffodivx" ]; then
vcodec+=" (MPEG-4)"
elif [ "$vdec" == "ffh264" ]; then
elif [ "${VID[$VDEC]}" == "ffh264" ]; then
vcodec+=" (h.264)"
fi
 
case $( tr '[A-Z]' '[a-z]' <<<$acodec ) in
85) acodec='MPEG-1 Layer III (MP3)' ;;
80) acodec='MPEG-1 Layer II (MP2)' ;;
mp4a) acodec='MPEG-4 AAC' ;;
353) acodec='WMA2' ;;
354) acodec='WMA3' ;;
case $( tr '[A-Z]' '[a-z]' <<<${VID[$ACODEC]} ) in
85) acodec='MPEG Layer III (MP3)' ;;
80) acodec='MPEG Layer I/II (MP1/MP2)' ;; # Apparently they use the same tag
mp4a) acodec='MPEG-4 AAC' ;; # LC and HE, apparently
352) acodec='WMA7' ;; # =WMA1
353) acodec='WMA8' ;; # =WMA2 No idea if lossless can be detected
354) acodec='WMA9' ;; # =WMA3
8192) acodec='AC3' ;;
1|65534)
# 1 is standard PCM (apparently all sample sizes)
# 65534 seems to be multichannel PCM
acodec='Linear PCM' ;;
vrbs|22127)
# 22127 = Vorbis in AVI (with ffmpeg) DON'T!
# vrbs = Vorbis in Matroska, probably other sane containers
acodec='Vorbis'
;;
qdm2) acodec="QDesign" ;;
"") acodec="no audio" ;;
# Following not seen by me so far, don't even know if mplayer would
# identify them
#<http://lists.mplayerhq.hu/pipermail/ffmpeg-devel/2005-November/005054.html>
355) acodec="WMA9 Lossless" ;;
10) acodec="WMA9 Voice" ;;
*) # If not recognized show audio id tag
acodec=${VID[$ACODEC]}
;;
esac
 
if [ "${VID[$CHANS]}" ] && is_number "${VID[$CHANS]}" &&[ ${VID[$CHANS]} -ne 2 ]; then
if [ ${VID[$CHANS]} -eq 0 ]; then
# This happens e.g. in non-i386 when playing WMA9 at the time of
# this writing
warn "Detected 0 audio channels."
warn " Does this version of mplayer support the audio codec ($acodec)?"
elif [ ${VID[$CHANS]} -eq 1 ]; then
acodec+=" (mono)"
else
acodec+=" (${VID[$CHANS]}ch)"
fi
fi
 
 
local meta="Filename: $(basename "$f")
File size: $(get_pretty_size "$f")
Length: $(pretty_stamp "$numsecs")"
local meta2="Dimensions: ${width}x${height}
Length: $(pretty_stamp "${VID[$LEN]}")"
local meta2="Dimensions: ${VID[$W]}x${VID[$H]}
Format: $vcodec / $acodec
FPS: $fps"
FPS: ${VID[$FPS]}"
local signature="$user_signature $user
$PROGRAM_SIGNATURE"
unset acodec vcodec
 
if [ "$HLTIMECODES" ] || [ "$extended_factor" != "0" ]; then
info "Merging contact sheets..."
fi
# If there were highlights then mix them in
if [ "$HLTIMECODES" ]; then
#\( -geometry x2 xc:black -background black \) # This breaks it!
convert \( "$hlfile" -background LightGoldenRod \) \
\( "$output" \) -append "$output"
fi
# Extended captures
if [ "$extended_factor" != 0 ]; then
convert "$output" "$extoutput" -append "$output"
fi
 
info "Creating header and footer..."
# Now let's add meta info
# This one enlarges the image to add the text, and puts
# meta info in two columns
643,25 → 1039,33
output="$newout"
fi
echo -n "Output wrote to " >&2
safe_rename "$output" "$(basename "$f").$output_format"
output_name=$( safe_rename "$output" "$(basename "$f").$output_format" ) || {
error "Failed to write the output file!"
return $EX_CANTCREAT
}
info "Done. Output wrote to $output_name"
 
cleanup
}
 
# Prints the program identification to stderr
show_vcs_info() { # Won't be printed in quiet modes
info "Video Contact Sheet *NIX v${VERSION}, (c) 2007 Toni Corvera" "sgr0"
}
 
# Prints the list of options to stdout
show_help() {
local P=$(basename $0)
cat <<EOF
Video Contact Sheet *NIX v${VERSION}, (c) 2007 Toni Corvera
 
Usage: $P [options] <file>
 
Options:
-i|--interval <arg> Set the interval to arg. An optional unit can be
used (case-insensitive) , e.g.:
Seconds: 90
-i|--interval <arg> Set the interval to arg. Units can be used
(case-insensitive), i.e.:
Seconds: 90 or 90s
Minutes: 3m
Hours: 1h
Combined: 1h3m90
Use either -i or -n.
-n|--numcaps <arg> Set the number of captured images to arg. Use either
-i or -n.
671,21 → 1075,34
as -i.
-T|--title <arg> Add a title above the vidcaps.
-u|--user <arg> Set the username found in the signature to this.
-S|--stamp <arg> Add the image found at the timestamp "arg", same format
-U|--fullname Use user's full/real name (e.g. John Smith) as found in
/etc/passwd.
-S|--stamp <arg> Add the image found at the timestamp "arg". Same format
as -i.
-l|--highlight <arg> Add the image found at the timestamp "arg" as a
highlight. Same format as -i.
-e[arg] | --extended=[arg]
Enables extended mode and optionally sets the extended
factor. By default it's $DEFAULT_EXT_FACTOR.
-m|--manual Manual mode: Only timestamps indicated by the user are
used (use in conjunction with -S), when using this
-i and -n are ignored.
-H|--height <arg> Set the output (individual thumbnail) height. Width is
derived accordingly. Note width cannot be manually set.
-a|--aspect <aspect> Aspect ration. Accepts floating point number or fractions.
-a|--aspect <aspect> Aspect ration. Accepts floating point number or
fractions.
-A|--autoaspect Try to guess aspect ratio from resolution.
-j|--jpeg Output in jpeg (by default output is in png).
-q|--quiet Don't print progess messages just errors. Repeat to
mute completely even on error.
-h|--help Show this text.
 
Options used for debugging purposes or to tweak the internal workings:
-M|--mplayer Force the usage of mplayer.
-F|--ffmpeg Force the usage of ffmpeg.
--shoehorn <arg> Pass "arg" to mplayer/ffmpeg. You probably don't need it.
--shoehorn <arg> Pass "arg" to mplayer/ffmpeg. You shouldn't need it.
-D Debug mode: Currently just prints the parsed
commandline as the title and to stderr.
 
Examples:
Create a contact sheet with default values (vidcaps at intervals of
700,6 → 1117,8
add an extra vidcap at 2m and another one at 19m:
\$ $P -f 3m -t 18m -S2m -S 19m input.avi
 
See more examples at vcs' homepage <http://p.outlyer.net/vcs/>.
 
EOF
}
 
710,8 → 1129,7
# Execute exithdlr on exit
trap exithdlr EXIT
 
# Test requirements
test_programs || exit $EX_UNAVAILABLE
show_vcs_info
 
load_config
 
722,8 → 1140,10
 
# TODO: use no name at all with -u noarg
#eval set -- "${default_options} ${@}"
TEMP=$(getopt -o i:n:u:T:f:t:S:jhFMH:c:ma: \
--long "interval:,numcaps:,username:,title:,from:,to:,stamp:,jpeg,help,shoehorn:,mplayer,ffmpeg,height:,columns:,manual,aspect" \
TEMP=$(getopt -s bash -o i:n:u:T:f:t:S:jhFMH:c:ma:l:De::UqAO: \
--long "interval:,numcaps:,username:,title:,from:,to:,stamp:,jpeg,help,"\
"shoehorn:,mplayer,ffmpeg,height:,columns:,manual,aspect:,highlight:,"\
"extended::,fullname,quiet,autoaspect,override:" \
-n $0 -- "$@")
 
eval set -- "$TEMP"
732,7 → 1152,7
case "$1" in
-i|--interval)
if ! interval=$(get_interval "$2") ; then
error "Interval must be a number (given $2)"
error "Interval must be a (positive) number. Got '$2'."
exit $EX_USAGE
fi
if [ "$interval" -le 0 ]; then
744,18 → 1164,29
;;
-n|--numcaps)
if ! is_number "$2" ; then
error "Number of caps must be a number! (given $2)"
error "Number of captures must be (positive) a number! Got '$2'."
exit $EX_USAGE
fi
if [ $2 -eq 0 ]; then
error "Number of captures must be greater than 0! Got '$2'."
exit $EX_USAGE
fi
numcaps="$2"
timecode_from=$TC_NUMCAPS
shift # Option arg
;;
-u|--username) user="$2" ; shift ;;
-U|--fullname)
user=$(grep ^$(id -un): /etc/passwd | cut -d':' -f5 |sed 's/,.*//g')
if [ -z "$user" ]; then
user=$(id -un)
error "No fullname found, falling back to default ($user)"
fi
;;
-T|--title) title="$2" ; shift ;;
-f|--from)
if ! fromtime=$(get_interval "$2") ; then
error "Starting timestamp must be a valid interval"
error "Starting timestamp must be a valid timecode. Got '$2'."
exit $EX_USAGE
fi
shift
762,11 → 1193,11
;;
-t|--to)
if ! totime=$(get_interval "$2") ; then
error "Ending timestamp must be a valid interval"
error "Ending timestamp must be a valid timecode. Got '$2'."
exit $EX_USAGE
fi
if [ "$totime" -eq 0 ]; then
error "Ending timestamp was set to 0, set to movie length"
error "Ending timestamp was set to 0, set to movie length."
totime=-1
fi
shift
773,12 → 1204,20
;;
-S|--stamp)
if ! temp=$(get_interval "$2") ; then
error "Timestamps must be a valid interval"
error "Timestamps must be a valid timecode. Got '$2'."
exit $EX_USAGE
fi
initial_stamps=( ${initial_stamps[*]} $temp )
shift
;;
-l|--highlight)
if ! temp=$(get_interval "$2"); then
error "Timestamps must be a valid timecode. Got '$2'."
exit $EX_USAGE
fi
HLTIMECODES+=( $temp )
shift
;;
-j|--jpeg) output_format=jpg ;;
-h|--help) show_help ; exit $EX_OK ;;
--shoehorn)
789,7 → 1228,7
-M) decoder=$DEC_MPLAYER ;;
-H|--height)
if ! is_number "$2" ; then
error "Height must be a number (given $2)"
error "Height must be a (positive) number. Got '$2'."
exit $EX_USAGE
fi
th_height="$2"
797,16 → 1236,17
;;
-a|--aspect)
if ! is_float "$2" && ! is_fraction "$2" ; then
error "Aspect ratio must be expressed as a floating point "\
"number or as a fraction (ie: 1, 1.33, 4/3, 2.5)"
exit 12
error "Aspect ratio must be expressed as a (positive) floating "
error " point number or a fraction (ie: 1, 1.33, 4/3, 2.5). Got '$2'."
exit $EX_USAGE
fi
aspect_ratio="$2"
shift
;;
-A|--autoaspect) aspect_ratio=-1 ;;
-c|--columns)
if ! is_number "$2" ; then
error "Columns must be a number (given $2)"
error "Columns must be a (positive) number. Got '$2'."
exit $EX_USAGE
fi
cols="$2"
813,6 → 1253,44
shift
;;
-m|--manual) manual_mode=1 ;;
-D) echo "Command line: $0 $*" && title="$0 $*" ; ;;
-e|--extended)
# Optional argument quirks: $2 is always present, set to '' if unused
# from the commandline it MUST be directly after the -e (-e2 not -e 2)
# the long format is --extended=VAL
# XXX: For some reason parsing of floats gives an error, so for now
# ints and only fractions are allowed
if [ "$2" ] && ! is_float "$2" && ! is_fraction "$2" ; then
error "Extended multiplier must be a (positive) number (integer, float "\
"or fraction)."
error " Got '$2'."
exit $EX_USAGE
fi
if [ "$2" ]; then
extended_factor="$2"
else
extended_factor=$DEFAULT_EXT_FACTOR
fi
shift
;;
-O|--override)
# Rough test
if ! egrep -q '[a-zA-Z_]+=[^;]*' <<<"$2"; then
error "Wrong override format, it should be variable=value. Got '$2'."
exit $EX_USAGE
fi
override "$2" "command line"
shift
;;
-q|--quiet)
# -q to only show errors
# -qq to be completely quiet
if [ $verbosity -gt $V_ERROR ]; then
verbosity=$V_ERROR
else
verbosity=$V_NONE
fi
;;
--) shift ; break ;;
*) error "Internal error! (remaining opts: $@)" ; exit $EX_SOFTWARE ;
esac
824,6 → 1302,10
show_help
exit $EX_USAGE
fi
 
# Test requirements
test_programs || exit $EX_UNAVAILABLE
 
# If -m is used then -S must be used
if [ $manual_mode -eq 1 ] && [ -z $initial_stamps ]; then
error "You must provide timestamps (-S) when using manual mode (-m)"
835,5 → 1317,4
 
# }}} # Command line parsing
 
 
# vim:set ts=4 ai: #
/video-contact-sheet/trunk/.
Property changes:
Modified: svn:mergeinfo
Merged /video-contact-sheet/branches/1.0.5b:r284-285