Subversion Repositories pub

Compare Revisions

Ignore whitespace Rev 361 → Rev 362

/video-contact-sheet/trunk/vcs
5,7 → 5,7
# vcs
# Video Contact Sheet *NIX: Generates contact sheets (previews) of videos
#
# Copyright (C) 2007, 2008 Toni Corvera
# Copyright (C) 2007, 2008, 2009 Toni Corvera
# with patches from Phil Grundig and suggestions/corrections from
# many others (see homepage)
#
47,21 → 47,27
# <http://wiki.multimedia.cx/index.php?title=VC-1>
#
 
declare -r VERSION="1.0.12"
declare -r VERSION="1.0.99" # ("1.1.0 RC")
# {{{ # CHANGELOG
# History (The full changelog can be found at <http://p.outlyer.net/vcs/files/CHANGELOG>).
#
# 1.0.12: (2008-04-16)
# * BUGFIX/COSMETIC: Corrected 0ms timestamps
# * COSMETIC: Re-added the (disabled for long) black border after highlights
# * BUGFIX/COSMETIC: Corrected the count of captures in manual-only mode (-m)
# * FEATURE: Added a minimun length to use the end offset
# * BUGFIX: Fixed the regression on highlights from the last version (extra
# padding was being added my IM automatically)
# * INTERNAL: Simplified use of IM's identify
# * BUGFIX: Fixed parsing of manual timestamps including milliseconds
# (when seconds didn't include the s character they were accidentally
# multiplied by 10!)
# 1.0.99: (2009-3-11)
# * FEATURE: Experimental support for DVDs (-V)
# * FEATURE: Added JPEG 2000 output format (-j2)
# * FEATURE/COSMETIC: Polaroid mode now produces a polaroid-like frame, the
# older version is now renamed as simply 'photos'
# New "funky" modes: newer polaroid, photos (older polaroid),
# polaroidframe.
# * Overrideable variables: DISABLE_SHADOWS and DISABLE_TIMESTAMPS (set to
# 1 to disable)
# * BUGFIX/COSMETIC: Re-added the missed space before filename
# * BUGFIX/COSMETIC: Reworked alignment and padding
# * Timestamps size is adjusted with smaller captures
# * BUGFIX: Fixed polaroid/rotate bug where all images overlapped on the
# same position (reported by Aleksandar Urošević, formerly
# unreproducible)
# * Better detection of video/audio features by falling back to ffmpeg when
# appropriate
# }}} # CHANGELOG
 
set -e
72,6 → 78,8
# * [[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.
# * Better DVD support (e.g. real detection of aspect ratio)
# * Use ffmpeg's detected length if shorter than mplayer's
#
 
# }}} # TODO
122,6 → 130,8
 
# This is the horizontal padding added to each capture. Changing it might break
# extended set's alignement so keep this in mind if you tinker with it
# When shadows are enabled, 5 is substracted from this value in csheet_montage
# so keep this in mind!
declare -ri HPAD=8
# }}} # End of constants
 
153,6 → 163,7
declare bg_sign=SlateGray # Background for signature
declare bg_title=White # Background for the title (see -T)
declare bg_contact=White # Background of the thumbnails
declare bg_tstamps='#000000aa' # Background for the timestamps box
declare fg_heading=black # Font colour for meta info box
declare fg_sign=black # Font colour for signature
declare fg_tstamps=white # Font colour for timestamps
227,6 → 238,9
declare FONT_MINCHO=/usr/share/fonts/truetype/kochi/kochi-mincho.ttf
# Output of capturing programs is redirected here
declare stdout=/dev/null stderr=/dev/null
declare -i DVD_MODE=0 DVD_TITLE=1
declare DVD_FILE=
declare -i multiple_input_files=0
 
# }}} # End of override-able variables
 
253,6 → 267,7
# go there
# This holds the output of mplayer -identify on the current video
declare MPLAYER_CACHE=
declare FFMPEG_CACHE=
# This holds the parsed values of MPLAYER_CACHE, see also the Indexes in VID
# (defined in the constants block)
declare -a VID=
269,21 → 284,21
# This number of seconds is *not* captured from the end of the video
declare -i end_offset=$DEFAULT_END_OFFSET
 
# Experimental in 1.0.7b: transformations/filters
# Operations are decomposed into independent optional steps, this will allow
# to add some intermediate steps (e.g. polaroid mode)
# Transformations/filters
# Operations are decomposed into independent optional steps, this allows
# to add some intermediate steps (e.g. polaroid/photo mode's frames)
# Filters in this context are functions.
# There're two kinds of filters and a delegate:
# * individual filters are run over each vidcap
# * global filters are run over all vidcaps at once
# * global filters are run over all vidcaps at once (currently deprecated)
# * The contact sheet creator delegates on some function to create the actual
# contact sheet
#
# Individual filters take the form:
# filt_name( vidcapfile, timestamp in seconds.milliseconds, width, height )
# filt_name( vidcapfile, timestamp in seconds.milliseconds, width, height, [context, [index]] )
# They're executed in order by filter_vidcap()
declare -a FILTERS_IND=( 'filt_resize' 'filt_apply_stamp' )
# Global filters take the form
declare -a FILTERS_IND=( 'filt_resize' 'filt_apply_stamp' 'filt_softshadow' )
# Deprecated: Global filters take the form
# filtall_name( vidcapfile1, vidcapfile2, ... )
# They're executed in order by filter_all_vidcaps
declare -a FILTERS_CS
301,8 → 316,9
# When set to 1 the signature won't contain the "Preview created by..." line
declare -i anonymous_mode=0
 
# See csheet_montage for more details
# See coherence_check for more details
declare -i DISABLE_SHADOWS=0
declare -i DISABLE_TIMESTAMPS=0
 
# }}} # Variables
 
341,6 → 357,7
'DEFAULT_END_OFFSET'
'MIN_LENGTH_FOR_END_OFFSET'
'DEBUG'
'DISABLE_.*'
)
 
# This is only used to exit when -DD is used
604,16 → 621,11
sed -r 's/\.([0-9][0-9]).*/.\1/'<<<$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)
# Prints a given size in human friendly form
get_pretty_size() {
local f="$1"
local bytes=$1
local size=
 
local bytes=$(get_file_size "$f")
local size=""
 
if [ "$bytes" -gt $(( 1024**3 )) ]; then
local gibs=$(( $bytes / 1024**3 ))
local mibs=$(( ( $bytes % 1024**3 ) / 1024**2 ))
633,6 → 645,17
echo $size
}
 
# 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_file_size($1 = file)
get_pretty_file_size() {
local f="$1"
local bytes=$(get_file_size "$f")
 
get_pretty_size "$bytes"
}
 
# Rename a file, if the target exists, try with appending numbers to the name
# And print the output name to stdout
# See $safe_rename_pattern
686,6 → 709,29
cut -f1 <<<"$bytes"
}
 
# Gets the size of a block device
get_blockdev_size() {
# This is something I've never done so I'm still looking for the right
# way to do it portably.
# Alternatives:
# * fdisk -s (no need for privileged access, read-only)
# Prints the number of blocks. In Linux they're always 1024 AFAICT,
# but I'm not sure about other unices (the -in disk- block size for
# DVDs is 2048).
# * hal
# hal-find-by-property --key block.device --string <(REAL)DEVICE>
# hal-get-property --udi <DEVICEID> --key volume.disc.capacity
# Gets byte size but HAL is far from standard (only Linux
# and FreeBSD have it AFAIK)
# * sysfs
# cat /sys/block/<(KERNEL)DEVICE>/size
# Size is given in sectors (512 blocks). Linux only. *BSD has
# sysctl of which I've no clue.
local dev="$1"
local numblocks=$(/sbin/fdisk -s "$dev")
get_pretty_size $(( 1024 * $numblocks ))
}
 
# Tests the presence of all required programs
# test_programs()
test_programs() {
777,6 → 823,18
echo "[TRACE]: $@" >&2
}
 
#
# Tests if the filter chain contains the provided filter
# has_filter($1 = filtername)
has_filter() {
local filter= ref=$1
for filter in ${FILTERS_IND[@]} ; do
[ "$filter" == "$ref" ] || continue
return 0
done
return 1
}
 
# }}} # Convenience functions
 
# {{{ # Core functionality
1007,10 → 1065,16
# globals: $shoehorned $decoder
 
if [ $decoder -eq $DEC_MPLAYER ]; then
# Capture 5 frames and drop the first 4, fixes a weird bug/feature of mplayer ([M1])
{
# Capture 5 frames and drop the first 4, fixes a weird bug/feature of mplayer ([M1])
mplayer -sws 9 -ao null -benchmark -vo "png:z=0" -quiet \
-frames 5 -ss $stamp $shoehorned "$f"
if [ $DVD_MODE -eq 1 ]; then
mplayer -sws 9 -ao null -benchmark -vo "png:z=0" -quiet \
-frames 5 -ss $stamp $shoehorned -dvd-device "$DVD_FILE" \
"dvd://$DVD_TITLE"
else
mplayer -sws 9 -ao null -benchmark -vo "png:z=0" -quiet \
-frames 5 -ss $stamp $shoehorned "$f"
fi
} >"$stdout" 2>"$stderr"
rm -f 0000000{1,2,3,4}.png # Remove the first four
elif [ $decoder -eq $DEC_FFMPEG ]; then
1040,7 → 1104,7
}
 
# Applies all individual vidcap filters
# filter_vidcap($1 = filename, $2 = timestamp, $3 = width, $4 = height)
# filter_vidcap($1 = filename, $2 = timestamp, $3 = width, $4 = height, $5 = context, $6 = index[1..])
filter_vidcap() {
trace $FUNCNAME $@
# For performance purposes each filter simply prints a set of options
1048,7 → 1112,7
# filters.
local cmdopts=
for filter in ${FILTERS_IND[@]}; do
cmdopts="$cmdopts $( $filter "$1" "$2" "$3" "$4" ) "
cmdopts="$cmdopts $( $filter "$1" "$2" "$3" "$4" "$5" "$6" ) -flatten "
done
local t=$(new_temp_file .png)
eval "convert '$1' $cmdopts '$t'"
1071,30 → 1135,85
}
 
# Draw a timestamp in the file
# filt_apply_stamp($1 = filename, $2 = timestamp, $3 = width, $4 = height)
# filt_apply_stamp($1 = filename, $2 = timestamp, $3 = width, $4 = height, $5 = context, $6 = index)
filt_apply_stamp() {
trace $FUNCNAME $@
local filename=$1 timestamp=$2 width=$3 height=$4
local filename=$1 timestamp=$2 width=$3 height=$4 context=$5 index=$6
 
echo -n " \( -box '#000000aa' -fill '$fg_tstamps' -pointsize '$pts_tstamps' "
local pts=$pts_tstamps
if [ $height -lt 200 ]; then
pts=$(( $pts_tstamps / 3 ))
elif [ $height -lt 400 ]; then
pts=$(( $pts_tstamps * 2 / 3 ))
fi
# If the size is too small they won't be readable at all
if [ $pts -le 8 ]; then
pts=8
if [ $index -eq 1 ] && [ $context -ne $CTX_EXT ]; then
warn "Beware, using very small timestamps to accomodate smaller captures,\
you might prefer using -dt to disable them"
fi
fi
# The last -gravity None is used to "forget" the previous gravity (otherwise it would
# affect stuff like the polaroid frames)
echo -n " \( -box '$bg_tstamps' -fill '$fg_tstamps' -pointsize '$pts' "
echo -n " -gravity '$grav_timestamp' -stroke none -strokewidth 3 -annotate +5+5 "
echo " ' $(pretty_stamp $stamp) ' \) -flatten "
echo " ' $(pretty_stamp $stamp) ' \) -flatten -gravity None "
}
 
# Apply a Polaroid-like effect
# Apply a framed photo-like effect
# Taken from <http://www.imagemagick.org/Usage/thumbnails/#polaroid>
# filt_photoframe($1 = filename, $2 = timestamp, $3 = width, $4 = height)
filt_photoframe() {
filt_photoframe0() {
trace $FUNCNAME $@
# local file="$1" ts=$2 w=$3 h=$4
# Tweaking the size gives a nice effect too
# w=$(( $w - ( $RANDOM % ( $w / 3 ) ) ))
# TODO: Split softshadow in a filter
echo -n "-bordercolor white -border 6 -bordercolor grey60 -border 1 "
# The border is relative to the input size (since 1.0.99), with a maximum of 6
# Should probably be bigger for really big frames
# Note that only images below 21600px (e.g. 160x120) go below a 6px border
local border=$(( ($3*$4) / 3600 ))
[ $border -lt 7 ] || border=6
echo -n "-bordercolor white -border $border -bordercolor grey60 -border 1 "
echo -n "-background black \( +clone -shadow 60x4+4+4 \) +swap "
echo "-background none -flatten -trim +repage"
}
 
filt_photoframe() {
trace $FUNCNAME $@
# local file="$1" ts=$2 w=$3 h=$4
# Tweaking the size gives a nice effect too
# w=$(( $w - ( $RANDOM % ( $w / 3 ) ) ))
# The border is relative to the input size (since 1.0.99), with a maximum of 6
# Should probably be bigger for really big frames
# Note that only images below 21600px (e.g. 160x120) go below a 6px border
local border=$(( ($3*$4) / 3600 ))
[ $border -lt 7 ] || border=6
echo -n "-bordercolor white -border $border -bordercolor grey60 -border 1 "
}
 
filt_softshadow() {
# Before this was a filter, there was the global (montage) softshadow (50x2+10+10) and the
# photoframe inline softshadow 60x4+4+4
echo -n "\( -background black +clone -shadow 50x2+4+4 -background none \) +swap -flatten -trim +repage "
}
 
 
# Apply a polaroid-like border effect
# Based on filt_photoframe(), with a bigger lower border
# filt_polaroid($1 = filename, $2 = timestamp, $3 = width, $4 = height)
filt_polaroid() {
trace $FUNCNAME $@
# local file="$1" ts=$2 w=$3 h=$4
local border=$(( ($3*$4) / 3600 )) # Read filt_photoframe for details
[ $border -lt 7 ] || border=6
echo -n "-bordercolor white -mattecolor white -frame ${border}x${border} "
# FIXME: This is rather ugly (double-flipping) there's sure a better way
echo -n "\( -flip -splice 0x$(( $border*5 )) \) "
echo "-flip -bordercolor grey60 -border 1 +repage"
}
 
# Applies a random rotation
# Taken from <http://www.imagemagick.org/Usage/thumbnails/#polaroid>
# filt_randrot($1 = filename, $2 = timestamp, $3 = width, $4 = height)
1163,18 → 1282,23
shift 4
# Padding is no longer dependant upong context since alignment of the
# captures was far trickier then
local hpad=$HPAD vpad=4
local hpad= vpad= splice=
 
# Using transparent seems to make -shadow futile
# The shadows already add a good amount of padding
if has_filter filt_softshadow ; then
hpad=$(( $HPAD-5 ))
vpad=0
splice=5x10
else
hpad=$HPAD
vpad=4
splice=0x8
fi
 
montage -background Transparent "$@" -geometry +$hpad+$vpad -tile "$cols"x "$output"
# With the shadows moved to a filter, there's not enough spacing to header
convert "$output" -background Transparent -splice $splice "$output"
 
# This should actually be moved to a filter but with the current
# architecture I'm unable to come up with the correct convert options
if [ $DISABLE_SHADOWS -eq 0 ]; then
# This produces soft-shadows, which look much better than the montage ones
convert \( -shadow 50x2+10+10 "$output" \) "$output" -composite "$output"
fi
 
# FIXME: Error handling
echo $output
}
1237,9 → 1361,7
accoffset=0
cmdopts= # This command is pretty time-consuming, let's make it in a row
 
# Base canvas
inf "Creating polaroid base canvas $row/$numrows..."
convert -size ${canvasw}x${canvash} xc:transparent "$rowfile"
# Base canvas # Integrated in the row creation since 1.0.99
 
# Step through vidcaps (col=[0..cols-1])
for col in $(seq 0 $(( $cols - 1 ))); do
1248,23 → 1370,19
w=$(imw "$1")
 
# Stick the vicap in the canvas
#convert -geometry +${accoffset}+0 "$rowfile" "$1" -composite "$rowfile"
cmdopts="$cmdopts -geometry +${accoffset}+0 '$1' -composite "
cmdopts="$cmdopts '$1' -geometry +${accoffset}+0 -composite "
 
offset=$(( $minoffset + ( $RANDOM % $maxoffset ) ))
let 'accoffset=accoffset + w - offset'
shift
done
inf "Composing polaroid row $row/$numrows..."
eval convert "'$rowfile'" "$cmdopts" -trim +repage "'$rowfile'" >&2
inf "Composing overlapped row $row/$numrows..."
eval convert -size ${canvasw}x${canvash} xc:transparent -geometry +0+0 "$cmdopts" -trim +repage "'$rowfile'" >&2
done
 
inf "Merging polaroid rows..."
inf "Merging overlapped rows..."
output=$(new_temp_file .png)
# Standard composition
#convert -background Transparent "${rowfiles[@]}" -append polaroid.png
# Overlapped composition
convert -size ${canvasw}x$(( $canvash * $cols )) xc:transparent "$output"
 
cmdopts=
accoffset=0
local h
1275,13 → 1393,16
maxoffset=$(( $h / 4 ))
offset=$(( $minoffset + ( $RANDOM % $maxoffset ) ))
# The row is also offset horizontally
cmdopts="$cmdopts -geometry +$(( $RANDOM % $maxoffset ))+$accoffset '$row' -composite "
cmdopts="$cmdopts '$row' -geometry +$(( $RANDOM % $maxoffset ))+$accoffset -composite "
let 'accoffset=accoffset + h - offset'
done
# After the trim the top corners are too near the heading, we add some space
# with -splce
eval convert -background transparent "$output" $cmdopts -trim +repage \
-bordercolor Transparent -splice 0x10 "$output" >&2
# After the trim the image will be touching the outer borders and the heading and footer,
# older versions (prior to 1.0.99) used -splice 0x10 to correct the heading spacing, 1.0.99
# onwards uses -frame to add spacing in all borders + splice to add a bit more space on the
# upper border. Note splice uses the background colour while frame uses the matte colour
eval convert -size ${canvasw}x$(( $canvash * $cols )) xc:transparent -geometry +0+0 \
$cmdopts -trim +repage -bordercolor Transparent -background Transparent -mattecolor Transparent \
-frame 5x5 -splice 0x5 "$output" >&2
 
# FIXME: Error handling
echo $output
1303,7 → 1424,16
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)
if [ $DVD_MODE -eq 0 ]; then
MPLAYER_CACHE=$(mplayer -benchmark -ao null -vo null -identify -frames 0 \
-quiet "$f" 2>/dev/null | grep ^ID)
# Used as fallback. Introduced in 1.0.99 so expect it to fail :P
FFMPEG_CACHE=$(ffmpeg -i "$f" -dframes 0 -vframes 0 /dev/null 2>&1 | grep Stream)
else
MPLAYER_CACHE=$(mplayer -benchmark -ao null -vo null -identify -frames 0 \
-quiet -dvd-device $DVD_FILE dvd://$DVD_TITLE \
2>/dev/null | grep ^ID)
fi
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)
1310,7 → 1440,11
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)
if [ $DVD_MODE -eq 0 ]; then
VID[$LEN]=$(grep ID_LENGTH <<<"$MPLAYER_CACHE"| cut -d'=' -f2)
else
VID[$LEN]=$(grep ID_DVD_TITLE_${DVD_TITLE}_LENGTH <<<"$MPLAYER_CACHE"| cut -d'=' -f2)
fi
# 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)
 
1327,10 → 1461,110
VID[$FPS]="${VID[$FPS]:0:$(( ${#VID[$FPS]} - 1 ))}"
fi
 
# Fallback for values known to fail often
if [ "$FFMPEG_CACHE" ]; then
# FPS=1000.00 happens often for WMV
if [ "${VID[$FPS]}" == "1000.00" ]; then
local fps2=$(grep Stream <<<"$FFMPEG_CACHE" | grep Video | head -1 | \
egrep -o ', [0-9]+\.[0-9]+ ' | egrep -o '[0-9]+.*[0-9]')
if is_float "$fps2" ; then
VID[$FPS]=$fps2
fi
unset fps2
fi
# Number of channels 0 happens for WMA in non-x86
# Mplayer seems to default to 2 for >2, so ffmpeg might be a better default option
# if [ "${VID[$CHANS]}" ] && ( ! is_number "${VID[$CHANS]}" || [ ${VID[$CHANS]} -eq 0 ] ) ; then
local ch2=$(grep Stream <<<"$FFMPEG_CACHE" | grep Audio | head -1 | cut -d, -f3 | sed 's/^ //')
if [ "$ch2" ]; then
case $ch2 in
mono) VID[$CHANS]=1 ;;
stereo) VID[$CHANS]=2 ;;
5.1) VID[$CHANS]=6 ;;
*) ;;
esac
fi
# fi
fi
 
 
# Check sanity of the most important values
is_number "${VID[$W]}" && is_number "${VID[$H]}" && is_float "${VID[$LEN]}"
}
 
# Checks if the provided arguments make sense and are allowed to be used
# together
coherence_check() {
# 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)"
return $EX_USAGE
fi
 
# Currently it's not allowed to use dvd mode with more than one input
# "file" (in this mode, input files are actually dvd titles of the file
# provided in -V)
if [ $DVD_MODE -eq 1 ] ; then
if [ $multiple_input_files -eq 1 ]; then
error "Only an input file is allowed in DVD mode"
return $EX_UNAVAILABLE
fi
 
# DVD Mode only works with mplayer, the decoder is changed when
# the DVD mode option is found, so if it's ffmpeg at this point,
# it's by user request (i.e. -F after -V)
if [ $decoder -ne $DEC_MPLAYER ]; then
warn "DVD mode requires the use of mplayer, falling back to it"
decoder=$DEC_MPLAYER
fi
fi
 
local filter=
if [ $DISABLE_TIMESTAMPS -eq 0 ] &&
local filts=( )
has_filter filt_polaroid && has_filter filt_apply_stamp ; then
 
for filter in ${FILTERS_IND[@]} ; do
if [ "$filter" == "filt_polaroid" ]; then
filts+=( $filter )
filts+=( filt_apply_stamp )
elif [ "$filter" == "filt_apply_stamp" ]; then
continue;
else
filts+=( $filter )
fi
done
FILTERS_IND=( ${filts[*]} )
unset filts
fi
# The shoftshadow and randrot filters must be in the correct place
# or they will affect the image incorrectly.
# Additionally the default filters can be disabled from the command
# line (with --disable), they're removed from the filter chain here
local filts=( ) end_filts=( )
for filter in ${FILTERS_IND[@]} ; do
case "$filter" in
filt_softshadow)
# Note the newer soft shadows code (1.0.99 onwards) behaves slightly
# differently. On previous versions disabling shadows only affected
# the montage shadow (but e.g. the polaroid mode preserved them),
# this is no longer true
if [ $DISABLE_SHADOWS -ne 1 ]; then
end_filts[100]="filt_softshadow"
fi
;;
filt_apply_stamp)
if [ $DISABLE_TIMESTAMPS -ne 1 ]; then
filts+=( "$filter" )
fi
;;
filt_randrot) end_filts[200]="filt_randrot" ;;
*) filts+=( "$filter" ) ;;
esac
done
FILTERS_IND=( ${filts[*]} ${end_filts[*]} )
}
 
# Main function.
# Creates the contact sheet.
# process($1 = file)
1340,12 → 1574,43
 
local numcols=
 
if [ ! -f "$f" ]; then
error "File \"$f\" doesn't exist"
return $EX_NOINPUT
# XXX: Some of this should be moved to coherence_check
if [ $DVD_MODE -eq 1 ]; then # DVD Mode
f="$DVD_FILE"
local dvdn="$f"
if [ -b "$dvdn" ]; then
dvdn="DVD"
elif [ ! -f "$dvdn" ]; then
error "File \"$dvdn\" doesn't exist"
return $EX_NOINPUT
fi
inf "Processing $dvdn..."
unset dvdn
if ! is_number "$1" ; then
error "DVD Title must be a number (e.g.: \$ vcs -V /dev/dvd 1)"
exit $EX_USAGE
fi
DVD_TITLE=$1
if [ $DVD_TITLE -eq 0 ]; then
local dt="$(lsdvd "$DVD_FILE" | grep 'Longest track:' | \
cut -d' ' -f3- | sed 's/^0*//')"
if ! is_number "$dt" ; then
error "Failed to autodetect longest DVD title"
exit $EX_INTERNAL
fi
DVD_TITLE=$dt
unset dt
inf "Using DVD Title #$DVD_TITLE"
fi
else
if [ ! -f "$f" ]; then
error "File \"$f\" doesn't exist"
return $EX_NOINPUT
fi
 
inf "Processing $f..."
fi
inf "Processing $f..."
 
identify_video "$f" || {
error "Found unsupported value while identifying video. Can't continue."
return $EX_SOFTWARE
1391,7 → 1656,7
 
# If the temporal vidcap already exists, abort
if [ -f $VIDCAPFILE ]; then
error "File 0000000$f.png exists and would be overwritten, move it out before running."
error "File $VIDCAPFILE exists and would be overwritten, move it out before running."
return $EX_CANTCREAT
fi
# mplayer will re-write also 00000001.png-00000004.png
1417,7 → 1682,7
inf "Generating highlight #${n}/${#HLTIMECODES[*]} ($pretty)..."
 
capture "$f" $stamp || return $?
filter_vidcap "$VIDCAPFILE" $pretty $vidcap_width $vidcap_height || {
filter_vidcap "$VIDCAPFILE" $pretty $vidcap_width $vidcap_height $CTX_HL $n || {
local r=$?
error "Failed to apply transformations to the capture."
return $r
1451,7 → 1716,7
inf "Generating capture #${n}/${#TIMECODES[*]} ($pretty)..."
 
capture "$f" $stamp || return $?
filter_vidcap "$VIDCAPFILE" $pretty $vidcap_width $vidcap_height || return $?
filter_vidcap "$VIDCAPFILE" $pretty $vidcap_width $vidcap_height $CTX_STD $n || return $?
 
# identified by capture number, padded to 6 characters
capfile=$(new_temp_file "-cap-$(pad 6 $n).png")
1493,7 → 1758,7
pretty=$(pretty_stamp $stamp)
inf "Generating capture from extended set: ${n}/${#TIMECODES[*]} ($pretty)..."
capture "$f" $stamp || return $?
filter_vidcap "$VIDCAPFILE" $pretty $w $h || return $?
filter_vidcap "$VIDCAPFILE" $pretty $w $h $CTX_EXT $n || return $?
 
capfile=$(new_temp_file "-excap-$(pad 6 $n).png")
mv "$VIDCAPFILE" "$capfile"
1611,9 → 1876,29
fi
 
 
local csw=$(imw "$output") exw= hlw=
local width=$csw
if [ "$HLTIMECODES" ] || [ "$extended_factor" != "0" ]; then
inf "Merging contact sheets..."
if [ "$HLTIMECODES" ]; then
local hlw=$(imw "$hlfile")
if [ $hlw -gt $width ]; then width=$hlw ; fi
fi
if [ "$extended_factor" != "0" ]; then
local exw=$(imw $extoutput)
if [ $exw -gt $width ]; then width=$exw ; fi
fi
fi
if [ $csw -lt $width ]; then
local csh=$(imh "$output")
# Expand the standard set to the maximum width of the sets by padding both sides
# For some reason the more obvious (to me) convert command-lines lose
# the transparency
convert \( -size $(( ($width - $csw) / 2 ))x$csh xc:transparent \) "$output" \
\( -size $(( ($width - $csw) / 2 ))x$csh xc:transparent \) +append "$output"
unset csh
fi
 
# If there were highlights then mix them in
if [ "$HLTIMECODES" ]; then
# For some reason adding the background also adds padding with:
1620,14 → 1905,27
# convert \( -background LightGoldenRod "$hlfile" -flatten \) \
# \( "$output" \) -append "$output"
# replacing it with a "-composite" operation apparently works
local geometry=$(identify -format '%wx%h' "$hlfile")
convert \( -size "$geometry" xc:LightGoldenRod "$hlfile" -composite \) \
\( -size "$(cut -d'x' -f1<<<$geometry)"x1 xc:black \) \
# Expand the highlights to the correct size by padding
local hlh=$(imh "$hlfile")
if [ $hlw -lt $width ]; then
convert \( -size $(( ($width - $hlw) / 2 ))x$hlh xc:transparent \) "$hlfile" \
\( -size $(( ($width - $hlw) / 2 ))x$hlh xc:transparent \) +append "$hlfile"
fi
convert \( -size ${width}x${hlh} xc:LightGoldenRod "$hlfile" -composite \) \
\( -size ${width}x1 xc:black \) \
"$output" -append "$output"
unset hlh
fi
# Extended captures
if [ "$extended_factor" != 0 ]; then
convert "$output" "$extoutput" -append "$output"
# Already set local exw=$(imw "$extoutput")
local exh=$(imh "$extoutput")
if [ $exw -lt $width ]; then
# Expand the extended set to be the correct size
convert \( -size $(( ($width - $exw) / 2 ))x$exh xc:transparent \) "$extoutput" \
\( -size $(( ($width - $exw) / 2 ))x$exh xc:transparent \) +append "$extoutput"
fi
convert "$output" -background Transparent "$extoutput" -append "$output"
fi
# Add the background
convert -background "$bg_contact" "$output" -flatten "$output"
1693,6 → 1991,28
# now the *complete header*
# * Add the contact sheet and append it to what we had.
# * Start a new image and annotate it with the signature, then append it too.
local filename_label="Filename"
local filesize_label="File size"
local filename_value=
local filesize_value=
if [ $DVD_MODE -eq 1 ]; then
local is_dev=0
test -b "$DVD_FILE" && is_dev=1
local dvd_label=$(lsdvd "$DVD_FILE" | grep -o 'Disc Title: .*' | cut -d' ' -f3-)
# lsdvd is guaranteed to be installed if DVD mode is enabled
if [ $is_dev -eq 1 ]; then # This is a real DVD, not an iso
filename_label="Disc label"
filename_value="$dvd_label"
filesize_label="Disc size"
filesize_value="$(get_blockdev_size "$DVD_FILE")"
else
filename_value="$(basename "$DVD_FILE") $filename_value (DVD Label: $dvd_label)"
filesize_value="$(get_pretty_file_size "$f")"
fi
else
filename_value="$(basename "$f")"
filesize_value="$(get_pretty_file_size "$f")"
fi
convert \
\( \
-size $(( ${headwidth} -18 ))x1 "xc:$bg_heading" +size \
1700,11 → 2020,11
-background "$bg_heading" -fill "$fg_heading" \
\( \
-gravity West \
\( label:"Filename:" \
-font "$fn_font" label:"$(basename "$f")" +append \
\( label:"$filename_label: " \
-font "$fn_font" label:"$filename_value" +append \
\) \
-font "$font_heading" \
label:"File size: $(get_pretty_size "$f")" \
label:"$filesize_label: $filesize_value" \
label:"Length: $(cut -d'.' -f1 <<<$(pretty_stamp ${VID[$LEN]}))" \
-append -crop ${headwidth}x${headheight}+0+0 \
\) \
1860,7 → 2180,7
 
# 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"
inf "Video Contact Sheet *NIX v${VERSION}, (c) 2007-2009 Toni Corvera" "sgr0"
}
 
# Prints the list of options to stdout
1888,11 → 2208,19
as -i.
-t|--to <arg> Set ending time. No caps beyond this. Same format
as -i.
-V|--dvd <file.iso|dvd_device>
DVD Mode, use file.iso as DVD. In this mode the
<file> argument must point to the title number, e.g.:
$ vcs -V somedvd.iso 1
Passing title 0 will use the default (longest) title.
$ vcs -V /dev/dvd 0
Implies -A (auto aspect ratio)
-E|--end_offset <arg> This time is ignored, from the end of the video. Same
format as -i. This value is not used when a explicit
ending time is set. By default it is $DEFAULT_END_OFFSET.
-T|--title <arg> Add a title above the vidcaps.
-j|--jpeg Output in jpeg (by default output is in png).
-j2|--jpeg 2 Output in jpeg 2000
-q|--quiet Don't print progess messages just errors. Repeat to
mute completely even on error.
-h|--help Show this text.
1938,9 → 2266,14
Randomly rotate each image.
"photoframe": Use '-kf' or '--funky photoframe'
Adds a photo-like white frame to each image.
"polaroidframe": Use '-kL' or '--funky polaroidframe'
Adds a polaroid picture-like white frame to each image.
"photos": Use '-kc' or '--funky photos'
Combination of rotate, photoframe and overlap.
Same as -kp -kr -ko.
"polaroid": Use '-kp' or '--funky polaroid'
Combination of rotate, photoframe and overlap.
Same as -kr -ko -kf.
Combination of rotate, polaroidframe and overlap.
Same as -kL -kr -ko.
"film": Use '-ki' or '--funky film'
Imitates filmstrip look.
"random": Use '-kx' or '--funky random'
1999,11 → 2332,11
ARGS="$@"
 
# [[R0]]
TEMP=$(getopt -s bash -o i:n:u:T:f:t:S:jhFMH:c:ma:l:De::U::qAO:I::k:W:E:d: \
--long "interval:,numcaps:,username:,title:,from:,to:,stamp:,jpeg,help,"\
TEMP=$(getopt -s bash -o i:n:u:T:f:t:S:j::hFMH:c:ma:l:De::U::qAO:I::k:W:E:d:V: \
--long "interval:,numcaps:,username:,title:,from:,to:,stamp:,jpeg::,help,"\
"shoehorn:,mplayer,ffmpeg,height:,columns:,manual,aspect:,highlight:,"\
"extended::,fullname,anonymous,quiet,autoaspect,override:,mincho,funky:,"\
"end_offset:,disable:" \
"end_offset:,disable:,dvd:" \
-n $0 -- "$@")
 
eval set -- "$TEMP"
2098,7 → 2431,19
HLTIMECODES+=( $temp )
shift
;;
-j|--jpeg) output_format=jpg ;;
-j|--jpeg)
if [ "$2" ]; then # Arg is optional, 2 is for JPEG 2000
# 2000 is also accepted
if [ "$2" != "2" ] && [ "$2" != "2000" ]; then
error "Use -j for jpeg output or -j2 for JPEG 2000 output. Got '-j$2'."
exit $EX_USAGE
fi
output_format=jp2
else
output_format=jpg
fi
shift
;;
-h|--help) show_help ; exit $EX_OK ;;
--shoehorn)
shoehorned="$2"
2187,12 → 2532,23
shift
;;
-k|--funky) # Funky modes
case $(tolower "$2") in
p|polaroid) # Same as overlap + rotate + photoframe
case "$2" in # Note older versions (<1.0.99) were case-insensitive
p|polaroid) # Same as overlap + rotate + polaroid
inf "Changed to polaroid funky mode."
FILTERS_IND+=( 'filt_polaroid' 'filt_randrot' )
CSHEET_DELEGATE='csheet_overlap'
# XXX: The newer version has a lot less flexibility with these many
# hardcoded values...
grav_timestamp=South
fg_tstamps=Black
bg_tstamps=Transparent
pts_tstamps=$(( $pts_tstamps * 3 / 2 ))
;;
c|photos) # Same as overlap + rotate + photoframe, this is the older polaroid
inf "Changed to polaroid funky mode."
FILTERS_IND+=( 'filt_photoframe' 'filt_randrot' )
CSHEET_DELEGATE='csheet_overlap'
# The timestamp must change location to be visible
# The timestamp must change location to be visible most of the time
grav_timestamp=NorthWest
;;
o|overlap) # Random overlap mode
2205,6 → 2561,13
f|photoframe) # White photo frame
FILTERS_IND+=( 'filt_photoframe' )
;;
L|polaroidframe) # White polaroid frame
FILTERS_IND+=( 'filt_polaroid ')
grav_timestamp=South
fg_tstamps=Black
bg_tstamps=Transparent
pts_tstamps=$(( $pts_tstamps * 3 / 2 ))
;;
i|film)
inf "Enabled film mode."
FILTERS_IND+=( 'filt_film' )
2224,16 → 2587,16
case $(tolower "$2") in
# timestamp (no final s) is undocumented but will stay
t|timestamps|timestamp)
inf "Timestamps disabled."
# TODO: Can array splicing be done in a saner way?
declare -a tmp=${FILTERS_IND[@]}
unset FILTERS_IND
FILTERS_IND=${tmp[@]/filt_apply_stamp/}
unset tmp
if [ $DISABLE_TIMESTAMPS -eq 0 ]; then
inf "Timestamps disabled."
# They'll be removed from the filter chain in coherence_check
DISABLE_TIMESTAMPS=1
fi
;;
s|shadows|shadow)
if [ $DISABLE_SHADOWS -eq 0 ]; then
inf "Shadows disabled."
# They will be removed from the filter chain in coherence_check
DISABLE_SHADOWS=1
fi
;;
2244,6 → 2607,18
esac
shift
;;
-V|--dvd)
# DVD Mode requires lsdvd
if ! type -pf lsdvd >/dev/null ; then
error "DVD Support requires the lsdvd program"
exit $EX_UNAVAILABLE
fi
DVD_MODE=1
DVD_FILE="$2"
decoder=$DEC_MPLAYER
aspect_ratio=-1
shift
;;
-q|--quiet)
# -q to only show errors
# -qq to be completely quiet
2277,21 → 2652,21
if [ ! "$1" ]; then
show_help
exit $EX_USAGE
elif [ "$2" ]; then
multiple_input_files=1
fi
 
# }}} # Command line parsing
 
# The coherence check ensures the processed options are
# not incoherent/incompatible with the input files or with
# other given options
coherence_check
 
# 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)"
exit $EX_USAGE
fi
 
set +e # Don't fail automatically
for arg do process "$arg" ; done
 
 
# vim:set ts=4 ai foldmethod=marker: #