1#!/bin/bash 2 3# 4# MOC - music on console 5# Copyright (C) 2004-2005 Damian Pietras <daper@daper.net> 6# 7# md5check.sh Copyright (C) 2012 John Fitzgerald 8# 9# This program is free software; you can redistribute it and/or modify 10# it under the terms of the GNU General Public License as published by 11# the Free Software Foundation; either version 2 of the License, or 12# (at your option) any later version. 13# 14 15# 16# TODO: - Make format, rate and channels explicit where possible. 17# - Add remaining decoder check functions. 18# - Fix decoders with genuine MD5 mismatches. 19# - Fix decoders with genuine format mismatches. 20# - Handle format mismatches which aren't genuine. 21# - Possibly rewrite in Perl for speed. 22# 23 24declare -A UNKNOWN 25declare -A UNSUPPORTED 26 27EXTRA=false 28IGNORE=false 29RC=0 30SILENT=false 31TREMOR=false 32VERBOSE=false 33 34# Clean error termination. 35function die { 36 echo '***' $@ > /dev/stderr 37 exit 1 38} 39 40# Provide usage information. 41function usage () { 42 echo "Usage: ${0##*/} [-e] [-s|-v] [LOGFILE]" 43 echo " ${0##*/} -h" 44} 45 46# Provide help information. 47function help () { 48 echo 49 echo "MOC MD5 sum checking tool" 50 echo 51 usage 52 echo 53 echo " -e|--extra Perform extra checks" 54 echo " -h|--help This help information" 55 echo " -i|--ignore Ignore known problems" 56 echo " -s|--silent Output no results" 57 echo " -v|--verbose Output all results" 58 echo 59 echo " LOGFILE MOC server log file name, or '-' for stdin" 60 echo " (default: 'mocp_server_log' in the current directory)" 61 echo 62 echo "Exit codes: 0 - No errors or mismatches" 63 echo " 1 - Error occurred" 64 echo " 2 - Mismatch found" 65 echo 66} 67 68# Check the FAAD decoder's samples. 69FAAD=$(which faad 2>/dev/null) 70function aac () { 71 local ENDIAN OPTS 72 73 [[ -x "$FAAD" ]] || die faad2 not installed 74 75 [[ "${FMT:0:1}" = "f" ]] && ENDIAN=le 76 OPTS="-w -q -f2" 77 78 [[ "${FMT:1:2}" = "16" ]] && OPTS="$OPTS -b1" 79 [[ "${FMT:1:2}" = "24" ]] && OPTS="$OPTS -b2" 80 [[ "${FMT:1:2}" = "32" ]] && OPTS="$OPTS -b3" 81 82 SUM2=$($FAAD $OPTS "$FILE" | md5sum) 83 LEN2=$($FAAD $OPTS "$FILE" | wc -c) 84} 85 86# Check the FFmpeg decoder's samples. 87FFMPEG=$(which avconv 2>/dev/null || which ffmpeg 2>/dev/null) 88function ffmpeg () { 89 local ENDIAN OPTS 90 91 [[ -x "$FFMPEG" ]] || die ffmpeg/avconv not installed 92 93 [[ "${FMT:0:1}" = "f" ]] && ENDIAN=le 94 OPTS="-ac $CHANS -ar $RATE -f $FMT$ENDIAN" 95 96 SUM2="$($FFMPEG -i "$FILE" $OPTS - </dev/null 2>/dev/null | md5sum)" 97 LEN2=$($FFMPEG -i "$FILE" $OPTS - </dev/null 2>/dev/null | wc -c) 98 99 [[ "$($FFMPEG -i "$FILE" </dev/null 2>&1)" =~ Audio:\ .*\ (mono|stereo) ]] || \ 100 IGNORE_SUM=$IGNORE 101} 102 103# Check the FLAC decoder's samples. 104SOX=$(which sox 2>/dev/null) 105function flac () { 106 local OPTS 107 108 [[ -x "$SOX" ]] || die "SoX (for flac) not installed" 109 110 [[ "${FMT:0:1}" = "s" ]] && OPTS="-e signed" || OPTS="-e unsigned" 111 [[ "${FMT:1:1}" = "8" ]] && OPTS="$OPTS -b8 -L" 112 [[ "${FMT:1:2}" = "16" ]] && OPTS="$OPTS -b16" 113 [[ "${FMT:1:2}" = "24" ]] && OPTS="$OPTS -b24" 114 [[ "${FMT:1:2}" = "32" ]] && OPTS="$OPTS -b32" 115 [[ "$FMT" =~ "le" ]] && OPTS="$OPTS -L" 116 [[ "$FMT" =~ "be" ]] && OPTS="$OPTS -B" 117 OPTS="$OPTS -r$RATE -c$CHANS" 118 119 SUM2=$($SOX "$FILE" $OPTS -t raw - | md5sum) 120 LEN2=$($SOX "$FILE" $OPTS -t raw - | wc -c) 121} 122 123# Check the Ogg/Vorbis decoder's samples. 124OGGDEC=$(which oggdec 2>/dev/null) 125function vorbis () { 126 [[ -x "$OGGDEC" ]] || die oggdec not installed 127 SUM2="$($OGGDEC -RQ -o - "$FILE" | md5sum)" 128 LEN2=$($OGGDEC -RQ -o - "$FILE" | wc -c) 129} 130 131# Check the LibSndfile decoder's samples. 132SOX=$(which sox 2>/dev/null) 133function sndfile () { 134 # LibSndfile doesn't have a decoder, use SoX. 135 [[ -x "$SOX" ]] || die "sox (for sndfile) not installed" 136 SUM2="$($SOX "$FILE" -t f32 - | md5sum)" 137 LEN2=$($SOX "$FILE" -t f32 - | wc -c) 138 [[ "$NAME" == *-s32le-* ]] && IGNORE_SUM=$IGNORE 139} 140 141# Check the MP3 decoder's samples. 142SOX=$(which sox 2>/dev/null) 143function mp3 () { 144 # Lame's decoder only does 16-bit, use SoX. 145 [[ -x "$SOX" ]] || die "sox (for mp3) not installed" 146 SUM2="$($SOX "$FILE" -t s32 - | md5sum)" 147 LEN2=$($SOX "$FILE" -t s32 - | wc -c) 148 IGNORE_SUM=$IGNORE 149 IGNORE_LEN=$IGNORE 150} 151 152# Check the Speex decoder's samples. 153SPEEX=$(which speexdec 2>/dev/null) 154function speex () { 155 [[ -x "$SPEEX" ]] || die speexdec not installed 156 SUM2="$($SPEEX "$FILE" - 2>/dev/null | md5sum)" 157 LEN2=$($SPEEX "$FILE" - 2>/dev/null | wc -c) 158 IGNORE_SUM=$IGNORE 159 IGNORE_LEN=$IGNORE 160} 161 162# Process command line options. 163for OPTS 164do 165 case $1 in 166 -e|--extra) EXTRA=true 167 ;; 168 -i|--ignore) IGNORE=true 169 ;; 170 -v|--verbose) VERBOSE=true 171 SILENT=false 172 ;; 173 -s|--silent) SILENT=true 174 VERBOSE=false 175 ;; 176 -h|--help) help 177 exit 0 178 ;; 179 --|-) break 180 ;; 181 -*) echo Unrecognised option: $1 182 usage > /dev/stderr 183 exit 1 184 ;; 185 *) break 186 ;; 187 esac 188 shift 189done 190 191# Allow for log file parameter. 192LOG="${1:-mocp_server_log}" 193[[ "$LOG" = "-" ]] && LOG=/dev/fd/0 194 195# Output formatting. 196$SILENT || echo 197 198# Process server log file. 199while read 200do 201 202 # Extract MOC revision header. 203 [[ "$REPLY" =~ "This is Music On Console" ]] && \ 204 REVN="$(echo "$REPLY" | sed 's/^.*Music/Music/')" 205 206 # Check for Tremor decoder. 207 [[ "$REPLY" =~ Loaded\ [0-9]+\ decoders:.*vorbis\(tremor\) ]] && \ 208 TREMOR=true 209 210 # Extract file's full pathname. 211 [[ "$REPLY" =~ "Playing item" ]] && \ 212 FILE="$(echo "$REPLY" | sed 's/^.* item [0-9]*: \(.*\)$/\1/')" 213 214 # Ignore all non-MD5 lines. 215 [[ "$REPLY" =~ "MD5" ]] || continue 216 217 # Extract fields of interest. 218 NAME="$(echo "$REPLY" | sed 's/^.*MD5(\([^)]*\)) = .*$/\1/')" 219 REST="$(echo "$REPLY" | sed 's/^.*MD5([^)]*) = \(.*\)$/\1/')" 220 SUM=$(echo $REST | cut -f1 -d' ') 221 LEN=$(echo $REST | cut -f2 -d' ') 222 DEC=$(echo $REST | cut -f3 -d' ') 223 $TREMOR && [[ "$DEC" = "vorbis" ]] && DEC=tremor 224 FMT=$(echo $REST | cut -f4 -d' ') 225 CHANS=$(echo $REST | cut -f5 -d' ') 226 RATE=$(echo $REST | cut -f6 -d' ') 227 228 # Check that we have the full pathname and it's not a dangling symlink. 229 [[ "$NAME" = "$(basename "$FILE")" ]] || die Filename mismatch 230 [[ -L "$FILE" && ! -f "$FILE" ]] && continue 231 232 # Get the independant MD5 sum and length of audio file. 233 case $DEC in 234 aac|ffmpeg|flac|mp3|sndfile|speex|vorbis) 235 IGNORE_LEN=false 236 IGNORE_SUM=false 237 $DEC 238 SUM2=$(echo "$SUM2" | cut -f1 -d' ') 239 ;; 240 modplug|musepack|sidplay2|timidity|tremor|wavpack) 241 $IGNORE && continue 242 [[ "${UNSUPPORTED[$DEC]}" ]] || { 243 echo -e "*** Decoder not yet supported: $DEC\n" > /dev/stderr 244 UNSUPPORTED[$DEC]="Y" 245 } 246 continue 247 ;; 248 *) [[ "${UNKNOWN[$DEC]}" ]] || { 249 echo -e "*** Unknown decoder: $DEC\n" > /dev/stderr 250 UNKNOWN[$DEC]="Y" 251 } 252 continue 253 ;; 254 esac 255 256 # Compare results. 257 BADFMT=false 258 $EXTRA && [[ "${NAME:0:9}" = "sinewave-" ]] && { 259 FMT2=$(echo $NAME | cut -f2 -d'-' | sed "s/24/32/") 260 CHANS2=$(echo $NAME | cut -f3 -d'-') 261 RATE2=$(echo $NAME | cut -f4 -d'-' | cut -f1 -d'.') 262 [[ "$FMT" = "$FMT2" ]] || BADFMT=true 263 [[ "$CHANS" = "$CHANS2" ]] || BADFMT=true 264 [[ "$RATE" = "$RATE2" ]] || BADFMT=true 265 } 266 BADSUM=false; $IGNORE_SUM || [[ "$SUM" = "$SUM2" ]] || BADSUM=true 267 BADLEN=false; $IGNORE_LEN || [[ "$LEN" = "$LEN2" ]] || BADLEN=true 268 269 # Set exit code. 270 $BADFMT || $BADSUM || $BADLEN && RC=2 271 272 # Determine output requirements. 273 $SILENT && continue 274 $BADFMT || $BADSUM || $BADLEN || $VERBOSE || continue 275 276 # Report result. 277 [[ "$REVN" ]] && { 278 echo "Test Results for $REVN" 279 echo 280 REVN= 281 } 282 echo "$NAME:" 283 echo " $SUM $LEN $DEC $FMT $CHANS $RATE" 284 echo " $SUM2 $LEN2" 285 $BADFMT && echo "*** Format mismatch" 286 $BADSUM && echo "*** MD5 sum mismatch" 287 $BADLEN && echo "*** Length mismatch" 288 echo 289 290done < $LOG 291 292$SILENT || { 293 case "$RV" in 294 1) 295 echo "No mismatches found" 296 ;; 297 2) 298 echo "NOTE: This tool is still being refined. Do not accept mismatches" 299 echo " at face value; they may be due to factors such as sample size" 300 echo " differences. But it does provide a reason to investigate" 301 echo " such mismatches further (and further refine this tool if false)." 302 echo 303 ;; 304 esac 305} 306 307exit $RC 308