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,14 |
# <http://wiki.multimedia.cx/index.php?title=VC-1> |
# |
|
declare -r VERSION="1.0.12" |
declare -r VERSION="1.1.0" |
# {{{ # 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.1.0: (2009-?-?) |
# * FEATURE: Experimental support for DVDs (-V) |
# * FEATURE: Added JPEG 2000 output format (-j2) |
# * BUGFIX/COSMETIC: Re-added the missed space before filename |
# }}} # CHANGELOG |
|
set -e |
72,6 → 65,7 |
# * [[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) |
# |
|
# }}} # TODO |
227,6 → 221,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 |
|
604,16 → 601,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 → 625,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 → 689,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() { |
1007,10 → 1033,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 |
1303,7 → 1335,14 |
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) |
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 → 1349,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) |
|
1331,6 → 1374,16 |
is_number "${VID[$W]}" && is_number "${VID[$H]}" && is_float "${VID[$LEN]}" |
} |
|
coherence_check() { |
# 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 ] && [ $multiple_input_files -eq 1 ]; then |
error "Only an input file is allowed in DVD mode" |
return $EX_UNAVAILABLE |
fi |
} |
|
# Main function. |
# Creates the contact sheet. |
# process($1 = file) |
1340,12 → 1393,50 |
|
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 /dev/dvd | 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 |
# DVD Mode only works with mplayer, the decoder is changed when |
# the DVD mode option is found, so if it's ffmpeg, it's by user |
# request |
if [ $decoder -ne $DEC_MPLAYER ]; then |
warn "DVD mode requires the use of mplayer" |
decoder=$DEC_MPLAYER |
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 |
1693,6 → 1784,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="$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 → 1813,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 → 1973,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 → 2001,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. |
1999,11 → 2120,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 → 2219,18 |
HLTIMECODES+=( $temp ) |
shift |
;; |
-j|--jpeg) output_format=jpg ;; |
-j|--jpeg) |
if [ "$2" ]; then # Arg is optional, 2 is for JPEG 2000 |
if [ "$2" != "2" ]; 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" |
2244,6 → 2376,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,6 → 2421,8 |
if [ ! "$1" ]; then |
show_help |
exit $EX_USAGE |
elif [ "$2" ]; then |
multiple_input_files=1 |
fi |
|
# }}} # Command line parsing |
2290,8 → 2436,10 |
exit $EX_USAGE |
fi |
|
# The coherence check ensures the processed options are |
# not incoherent/incompatible with the input files or each other |
coherence_check |
set +e # Don't fail automatically |
for arg do process "$arg" ; done |
|
|
# vim:set ts=4 ai foldmethod=marker: # |