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: # |