1#!/bin/sh
2#
3# A convenient front-end for the various mpeg encoding tools.
4# Allows "1 command" production of a video stream...
5#
6
7# Licensed under GPL (see http://www.fsf.org/licenses/gpl.html or contact
8# the Free Software Foundation for a copy)
9# Copyright Scott Moser
10
11
12# these can be changed with env variables, just set and export to override
13NICEVAL=${NICEVAL:-19}
14
15LAV2YUV=${LAV2YUV:-"lav2yuv"}
16YUVSCALER=${YUVSCALER:-"yuvscaler"}
17MPEG2ENC=${MPEG2ENC:-"mpeg2enc"}
18LAV2WAV=${LAV2WAV:-"lav2wav"}
19AUDIOENC=${AUDIOENC:-"mp2enc"}       # can be "toolame" or "mp2enc"
20MPLEX=${MPLEX:-"mplex"}
21YUVDENOISE=${YUVDENOISE:-"yuvdenoise"}
22LAVINFO=${LAVINFO:-"lavinfo"}
23
24NOISYLOGFILE=""                       # if not set, will be outputfilename.log -- only used if -L flag sent
25QUIETLOG=${QUIETLOG:-"lav2mpeg.log"}  # if set to not "" will log all normal messages to this file as well as stdout
26LOGDATE=${LOGDATE:-1}                 # whether or not to output the date in log messages
27LOGDATESTR=${LOGDATESTR:-"+%H:%M:%S"} # format for the date - only used if logdate!=0
28LOGCOMMANDS=${LOGCOMMANDS:-0};        # if non-zero, will output the commands it runs.  commands are always logged to NOISYLOGFILE if used
29LOGONLY=${LOGONLY:-""}                # will only log the commands it would use if set  (for example useage/testing)
30LAV2MPEGRC=${LAV2MPEGRC:-"$HOME/.lav2mpegrc"}   # set this to contain your lav2mpeg rc file -- in bash sourceable syntax.
31                                      # it can modify any ENV vars in this script
32                                      # or set a "LAV2MPEG_OPTIONS" ENV to prepend default options that will be overridden by explicit command line options.
33if [ "$LOGONLY" != "" ]; then
34   LOGCOMMANDS=1
35fi
36VCD_MEDIUM_BR=${VCD_MEDIUM_BR:-1550}
37VCD_HIGH_BR=${VCD_HIGH_BR:-1800}
38SVCD_HIGH_BR=${SVCD_HIGH_BR:-3000}
39SVCD_HIGH_QUAL=${SVCD_HIGH_QUAL:-5}
40SVCD_HIGH_BUFFSIZE=${SVCD_HIGH_BUFFSIZE:-100}
41
42# extra flags for the encoders (will be the last thing on the line before file names)
43EXTRA_LAV2YUV=${EXTRA_LAV2YUV:-""}
44EXTRA_YUVSCALER=${EXTRA_YUVSCALER:-""}
45EXTRA_MPEG2ENC=${EXTRA_MPEG2ENC:-""}
46EXTRA_LAV2WAV=${EXTRA_LAV2WAV:-""}
47EXTRA_AUDIOENC=${EXTRA_AUDIOENC:-""}
48EXTRA_MPLEX=${EXTRA_MPLEX:-""}
49EXTRA_YUVDENOISE=${EXTRA_YUVDENOISE:-""}
50
51# things that can be changed with arguments
52# don't let an env override, to avoid confusion
53bitrate=1152
54quality=12
55saveraw=0
56mode="vcd"
57outputres=""
58encode_quality=2
59outfile=""
60stereo=1
61noisy=0
62logall=0
63aencbpr_stereo=224
64aencbpr_mono=112
65use_fifo=0
66
67# -- shouldn't have to change below here
68NICE="nice -n $NICEVAL"
69
70
71# functions
72logIt() {
73   if [ $LOGDATE -ne 0 ]; then
74      NOW=$(date $LOGDATESTR)
75      NOW="$NOW - "
76   fi
77   if [ "$logfile" = "" ]; then
78      echo "${NOW}$@"
79   else
80      echo "${NOW}$@" | tee -a $logfile
81   fi
82   if [ "$output_noisy" != ""  ]; then
83      echo "${NOW}$@" >> $noisylog
84   fi
85}
86
87cleanExit() {
88   # delete raw files if sawraw is 0 or exiting with non-zero
89   if [ $saveraw -eq 0 -a "$1" = "0" ]; then
90      rm -f $audio $video
91   fi
92   exit $1
93}
94
95getTimeDiff() {
96   if [ $# -lt 2 ]; then
97      return
98   fi
99   diff=$(expr $2 - $1)
100   hours=$(expr $diff / 3600)
101   temp=$(expr $hours \* 3600 )
102   diff=$(expr $diff - $temp )
103   minutes=$(expr $diff / 60)
104   sec=$(expr $diff % 60)
105   printf "%i:%02d:%02d\n" "$hours" "$minutes" "$sec"
106}
107
108doStep() {
109   if [ "$LOGCOMMANDS" = "0" ]; then
110      echo "COMMAND=${step[$count]}"
111   fi
112   if [ "$LOGONLY" = "" ]; then
113      eval ${step[$count]}
114   fi
115}
116
117
118usage()
119{
120  name=`basename $0`
121  cat << END
122Usage: $name [ -s/S -k/K -f/F -l/L -n/N -y/Y ] [ -m mode ] [ -e 0|1|2 ] [ -o outputFile ] [ -b vidbitrate ] [ -a audbitrate ] [ -q 0-30 ] [ -d XxY ] srcfile...
123  s/S - stereo : -s=off -S=on                                    (default auto)
124  k/K - keep raw output files (.m1v, .m2v, .mp2) : -k=no -K=yes  (default no)
125  f/F - use fifos : -f=no -F=yes                                 (default no)
126  l/L - log all encoding : -l=no -L=yes                          (default no)
127  n/N - be noisy (don't hide output of tools) : -n=no -N=yes     (default no)
128  y/Y - yuvdenoise : -y=don't use -Y=use           (default e=1|2 use e=0 dont)
129  m - one of MODES (see below)                                   (default $mode)
130  e - encoding quality : 0, 1, or 2                              (default $encode_quality)
131  o - output file ( defaults to firstInputFileName.mpg )
132  b - video bitrate in kbps  ( only used when -o is "mpeg1" or "mpeg2" )
133  a - audio bitrate in kpbs  ( only used when -o is not "vcd*" )
134  q - quality for mpeg2enc   ( only used when -o is "mpeg1" or "mpeg2" )
135  d - dimensions XxY         ( only used when -o is "mpeg1" or "mpeg2" )
136                               defaults to same as input
137
138  MODES:
139      vcd          -- standard VCD                    (352x240 or 352x288)
140      vcd_medium   -- ${VCD_MEDIUM_BR}kbps video VCD              (352x240 or 352x288)
141      vcd_high     -- ${VCD_HIGH_BR}kbps video VCD              (352x240 or 352x288)
142      svcd         -- standard SVCD                   (480x480 or 480x576)
143      svcd_high    -- ${SVCD_HIGH_BR}kbps(max) vbr @qual=${SVCD_HIGH_QUAL}       (480x480 or 480x576)
144      mpeg1        -- honor -a -b -q -d flags default resolution same as input
145      mpeg2        -- honor -a -b -q -d flags default resolution same as input
146
147   EXAMPLE:
148      VCD:  $name file.avi
149              - will create a VCD compatible mpeg named file.mpg
150      mpeg2: $name -m mpeg2 -o output.mpg file.edl
151              - will create a mpeg2 with default bitrate in same res as input
152      mpeg1: $name -a 160 -b 2110 -d 320x240 -m mpeg1 -o output.mpg file.edl
153              - will create a mpeg2 with videobitrate=2110 and audio=160
154              - and resolution 320x240
155      debug: export LOGONLY=1; $name -m mpeg2 -o output.mpg file.edl
156              - will print out the commands it would use to do this, but
157              - not do anything
158
159   please see the script for more info and environment variables set/used
160END
161exit 1
162}
163
164printDebugInfo() {
165   logIt "going from ${video_width}x${video_height} ($video_norm) to ${output_width}x${output_height} in $mode with quality=$quality, bitrate=$bitrate and encodequal=${encode_quality}"
166   logIt "outfile=$outfile audio=$audio video=$video"
167   logIt "lav2yuv_flags=$lav2yuv_flags"
168   logIt "yuscaler_flags=$yuvscaler_flags"
169   logIt "mpeg2enc_flags=$mpeg2enc_flags"
170   logIt "lav2wav_flags=$lav2wav_flags"
171   logIt "audioenc_flags=$audioenc_flags"
172   logIt "mplex_flags=$mplex_flags"
173   logIt "need_scale=$needscale"
174   logIt "downscale=$downscaling"
175}
176
177if [ $# -eq 0 -o "$1" = "--help" -o "$1" = "-h" ]; then
178   usage;
179   exit 1
180fi
181
182LAVRC_COUNT=0
183if [ -e $LAV2MPEGRC ]; then
184   logIt "Sourcing lav2mpgrc file $LAV2MPEGRC (this can change defaults!)"
185   . $LAV2MPEGRC
186   LAVRC_COUNT=$(echo $LAV2MPEG_OPTIONS | wc -w )
187fi
188
189infostr=""
190while getopts "sSkKfFlLnNyYb:a:q:d:m:e:o:" opt  $LAV2MPEG_OPTIONS $@
191do
192   case $opt in
193      s) userstereo=0
194         infostr="${infostr} using mono -"
195         ;;
196      S) userstereo=1
197         infostr="${infostr} using stereo -"
198         ;;
199      k) saveraw=0
200         infostr="${infostr} not saving raw files -"
201         ;;
202      K) saveraw=1
203         infostr="${infostr} saving raw files -"
204         ;;
205      f) use_fifo=0
206         infostr="${infostr} not using fifos-"
207         ;;
208      F) use_fifo=1
209         infostr="${infostr} using fifos-"
210         ;;
211      l) logall=0
212         infostr="${infostr} not logging all -"
213         ;;
214      L) logall=1
215         infostr="${infostr} logging all -"
216         ;;
217      n) noisy=0
218         infostr="${infostr} not being noisy -"
219         ;;
220      N) noisy=1
221         infostr="${infostr} being noisy -"
222         ;;
223      y) userdenoise=0
224         infostr="${infostr} not using yuvdenoise -"
225         ;;
226      Y) userdenoise=1
227         infostr="${infostr} using yuvdenoise -"
228         ;;
229      b) bitrate=$OPTARG
230         infostr="${infostr} using video bitrate=$bitrate -"
231         ;;
232      a) useraencbpr=$OPTARG
233         infostr="${infostr} using audio bitrate=$useraencbpr"
234         ;;
235      q) quality=$OPTARG
236         infostr="${infostr} using quality=$quality -"
237         ;;
238      d) outputres=$OPTARG
239         infostr="${infostr} using outputres=$outputres -"
240         ;;
241      m) mode=$OPTARG
242         infostr="${infostr} mode=$mode -"
243         ;;
244      e) encode_quality=$OPTARG
245         infostr="${infostr} encode_quality=$encode_quality -"
246         ;;
247      o) outfile=$OPTARG
248         infostr="${infostr} outfile=$outfile -"
249         ;;
250      ?)
251         usage
252         ;;
253    esac
254done
255let MOPTIND=OPTIND-LAVRC_COUNT
256shift `expr $MOPTIND - 1`
257
258if [ "${QUIETLOG}" != "" ]; then
259   logfile=${QUIETLOG}
260else
261   logfile="/dev/null"
262fi
263#uncomment to blank logfile
264#echo "" > $logfile
265
266logIt "$infostr"
267
268# lavinfo should set up video_frames, video_width
269# video_height, video_inter, video_norm, audio_chans
270eval $($LAVINFO $@ | grep "=")  # grep for = to remove Warnings
271if [ "$video_frames" = "" ]; then
272   logIt "'lavinfo $@' died! exiting"
273   logIt " maybe you don't have lavinfo. or your input flags were wrong"
274   logIt " input files must be the last input on the command line"
275   exit 1
276fi
277
278case $mode in
279   vcd*)
280      case $video_norm in
281         NTSC)  output_width=352; output_height=240 ;;
282         PAL)   output_width=352; output_height=288 ;;
283         SECAM) output_width="SECAM_VCD_WIDTH???"; output_height="SECAM_VCD_HEIGHT";;
284      esac
285      ;;
286   svcd*)
287      case $video_norm in
288         NTSC)  output_width=480; output_height=480 ;;
289         PAL)   output_width=480; output_height=576;;
290         SECAM) output_width="SECAM_SVCD_WIDTH???"; output_height="SECAM_SVCD_HEIGHT" ;;
291      esac
292      ;;
293   mpeg*)
294      if [ "$outputres" != "" ]; then
295         temp=$outputres
296         temp=${outputres%%[+-]*}
297         output_width=${temp%x*}
298         output_height=${temp#*x}
299         logIt "using output_width=$output_width, output_height=$output_height"
300         # these don't work yet, negative values would mess them up
301         temp=${outputres#*+}
302         output_xoff=${temp%%[+-]*}
303         output_yoff=${temp##*[+-]}
304      else
305         output_width=$video_width
306         output_height=$video_height
307      fi
308      ;;
309esac
310
311
312# hopefully sane input has been checked, lets start setting things up
313
314# simple check for logonly mode
315if [ "$LOGONLY" != "" -a "$LOGONLY" != "0" -a $use_fifo -eq 1 ]; then
316   use_fifo=0
317   logIt "can't do logonly with fifos, turning fifos off"
318fi
319
320# set up extra flags as possibly given by user
321lav2yuv_flags=$EXTRA_LAV2YUV
322yuvscaler_flags=$EXTRA_YUVSCALER
323mpeg2enc_flags=$EXTRA_MPEG2ENC
324lav2wav_flags=$EXTRA_LAV2WAV
325audioenc_flags=$EXTRA_AUDIOENC
326mplex_flags=$EXTRA_MPLEX
327yuvdenoise_flags=$EXTRA_YUVDENOISE
328
329# get output mpeg version and output file names
330case $mode in
331   vcd|vcd_medium|vcd_high|mpeg1) mpegver=1 ;;
332   svcd|svcd_high|mpeg2) mpegver=2 ;;
333esac
334
335if [ "$outfile" != "" ]; then
336   basename=${outfile%.*}
337else
338   basename=${1%.*}
339   outfile=${basename}.mpg
340fi
341video=${basename}.m${mpegver}v
342audio=${basename}.mp2
343
344if [ "$NOISYLOGFILE" = "" ]; then
345   noisylog=${basename}.log
346else
347   noisylog=$NOISYLOGFILE
348fi
349
350# set up the audio
351if [ "$userstereo" = "" ]; then
352   stereo=${audio_chans:-0}
353else
354   stereo=$userstereo
355fi
356
357if [ $AUDIOENC = "mp2enc" ]; then
358   nostereo_flag="-m"
359else
360   nostereo_flag="-a"
361fi
362
363if [ "$stereo" != "0" ]; then
364  aencbpr=$aencbpr_stereo
365else
366  aencbpr=$aencbpr_mono
367  mono_flag=$nostereo_flag
368fi
369# set useraencbpr to aencbpr unless set
370useraencbpr=${useraencbpr:-$aencbpr}
371
372# set up  always-on flags
373lav2yuv_flags="$lav2yuv_flags $@"
374case $video_norm in
375   PAL)    ysnorm="p";;
376   SECAM)  ysnorm="s";;
377   NTSC|*) ysnorm="n";;
378esac
379yuvscaler_flags="-n $ysnorm $yuvscaler_flags"
380mpeg2enc_flags="-o $video $mpeg2enc_flags"
381lav2wav_flags="$lav2wav_flags $@"
382if [ $AUDIOENC = "mp2enc" ]; then
383   audioenc_flags="$mono_flag -r 44100 ${audioenc_flags} -o $audio"
384else
385   # assume toolame
386   audioenc_flags="$mono_flag -s 44.1 /dev/stdin ${audioenc_flags} $audio"
387fi
388mplex_flags=" -o $outfile $mplex_flags $audio $video"
389
390# input/output dependent
391if [ $video_width -eq $output_width -a $video_height -eq $output_height ]; then
392   needscale=0;
393else
394   needscale=1
395fi
396downscaling=0
397if [ $video_width -gt $output_width -o $video_width -eq $output_width ]; then
398   if [ $video_height -gt $output_height -o $video_height -eq $output_height ];
399      then
400      downscaling=1
401   fi
402fi
403
404if [ "$video_inter" = "1" ]; then
405   yuvdenoise_flags="$yuvdenoise_flags -F"
406fi
407
408#encoder quality dependent
409case ${encode_quality} in
410      0)
411         mpeg2enc_flags="-4 4 -2 4 ${mpeg2enc_flags}"
412         if [ $downscaling -ne 0 ]; then
413            yuvscaler_flags="-M RESAMPLE ${yuvscaler_flags}"
414         fi
415         dodenoise=0
416         if [ "$userdenoise" != "" -a "$userdenoise" != "0" ]; then
417            logIt "set dodenoise to on at users request"
418            dodenoise=1
419         fi
420         ;;
421      2)
422         mpeg2enc_flags="-4 1 -2 1 ${mpeg2enc_flags}"  # -N here?
423         dodenoise=1
424         if [ "$userdenoise" != "" -a "$userdenoise" = "0" ]; then
425            dodenoise=0
426         fi
427         ;;
428      1|*)
429         dodenoise=1
430         if [ "$userdenoise" != "" -a "$userdenoise" = "0" ]; then
431            dodenoise=0
432         fi
433         ;;
434esac
435
436# output(mode) dependent
437case ${mode} in
438   vcd)
439      yuvscaler_flags="-O VCD ${yuvscaler_flags}"
440      mpeg2enc_flags="-a 2 -f 1 ${mpeg2enc_flags}"
441      mplex_flags="-f 1 ${mplex_flags}"
442      ;;
443   vcd_medium)
444      yuvscaler_flags="-O VCD ${yuvscaler_flags}"
445      mpeg2enc_flags="-a 2 -f 2 -b ${VCD_MEDIUM_BR} ${mpeg2enc_flags}"
446      mplex_flags="-f 2 ${mplex_flags}"
447      ;;
448   vcd_high)
449      yuvscaler_flags="-O VCD ${yuvscaler_flags}"
450      mpeg2enc_flags="-a 2 -f 2 -b ${VCD_HIGH_BR} ${mpeg2enc_flags}"
451      mplex_flags="-f 2 ${mplex_flags}"
452      ;;
453   mpeg1)
454      yuvscaler_flags="-O SIZE_${output_width}x${output_height} ${yuvscaler_flags}"
455      mpeg2enc_flags="-f 0 -b ${bitrate} ${mpeg2enc_flags}"
456      mplex_flags="-f 0 ${mplex_flags}"
457      aencbpr=$useraencbpr
458      ;;
459   svcd)
460      yuvscaler_flags="-O SVCD ${yuvscaler_flags}"
461      mpeg2enc_flags="-a 2 -f 4 ${mpeg2enc_flags}"
462      mplex_flags="-f 4 ${mplex_flags}"
463      aencbpr=$useraencbpr
464      audioenc_flags="-e $audioenc_flags"
465      ;;
466   svcd_high)
467      yuvscaler_flags="-O SVCD ${yuvscaler_flags}"
468      mpeg2enc_flags="-a 2 -f 5 -b ${SVCD_HIGH_BR} -V ${SVCD_HIGH_BUFFSIZE} -q ${SVCD_HIGH_QUAL} ${mpeg2enc_flags}"
469      mplex_flags="-f 5 ${mplex_flags}"
470      aencbpr=$useraencbpr
471      audioenc_flags="-e $audioenc_flags"
472      ;;
473   mpeg2)
474      yuvscaler_flags="-O SIZE_${output_width}x${output_height} ${yuvscaler_flags}"
475      mpeg2enc_flags="-f 3 -b ${bitrate} -q $quality ${mpeg2enc_flags}"
476      mplex_flags="-V -f 3 ${mplex_flags}"
477      aencbpr=$useraencbpr
478      ;;
479esac
480
481if [ $dodenoise -ne 0 ]; then
482   yuvdenoise_str="| $NICE $YUVDENOISE $yuvdenoise_flags"
483else
484   yuvdenoise_str=""
485   logIt "not using yuvdenoiser, dodenoise=$dodenoise"
486fi
487
488if [ $needscale -ne 0 ]; then
489   yuvscale_str="| $NICE $YUVSCALER $yuvscaler_flags"
490else
491   yuvscale_str=""
492fi
493
494#final flags  -- bitrate flag is -b for both mp2enc and toolame
495audioenc_flags="-b $aencbpr $audioenc_flags"
496
497
498# this is kinda nasty, but it ends up giving a somewhat clean loop at the
499# bottom without a whole lot of if's and such
500count=1;
501stepdesc[$count]="video encoding"
502step[$count]="$NICE $LAV2YUV $lav2yuv_flags $yuvdenoise_str $yuvscale_str | $NICE $MPEG2ENC $mpeg2enc_flags"
503
504count=2;
505stepdesc[$count]="audio encoding"
506step[$count]="$NICE $LAV2WAV $lav2wav_flags | $NICE $AUDIOENC $audioenc_flags"
507
508count=3;
509if [ $use_fifo -eq 1 ]; then
510   stepdesc[$count]="encoding with fifos"
511else
512   stepdesc[$count]="multiplexing"
513fi
514step[$count]="$NICE $MPLEX $mplex_flags"
515
516output_noisy=""
517if [ $noisy -ne 0 -a $logall -ne 0 ]; then
518   output_noisy="2>&1 | tee -a $noisylog"
519   echo "" > $noisylog
520fi
521if [ $noisy -eq 0 ]; then
522   if [ $logall -eq 0 ]; then
523      noisylog="/dev/null"
524   fi
525   echo "" > $noisylog
526   output_noisy=">>$noisylog 2>&1"
527fi
528
529detach=""
530if [ $use_fifo -ne 0 ]; then
531   detach="&";
532   saveraw=0
533   rm -f $video $audio
534   mkfifo $video
535   mkfifo $audio
536fi
537
538logIt "using mode=$mode, stereo=$stereo audio bpr=$aencbpr"
539if [ $logall -ne 0 ]; then
540   logIt "logging everything to $noisylog"
541fi
542logIt "beginning conversion of $@ to $outfile"
543logIt "had $video_frames to encode"
544
545STARTALL=$SECONDS
546for stepnum in 1 2 3
547do
548   count=$stepnum
549   START=$SECONDS
550   if [ $stepnum -eq 3 -o $use_fifo -ne 1 ]; then
551      logIt "beginning ${stepdesc[$count]}"
552      if [ $LOGCOMMANDS != "0" ]; then
553         logIt "COMMAND=${step[$count]}"
554      fi
555   fi
556   if [ $stepnum -eq 3 ]; then
557      detach="" # don't detach the last stem (mplex)
558   fi
559   eval doStep $output_noisy $detach
560   RET=$?
561   if [ $RET -ne 0 ]; then
562      logIt "ugh! ${stepdesc[$count]} failed, bailing. used command:"
563      logIt "${step[$count]}"
564      cleanExit $RET
565   fi
566   if [ $stepnum -eq 3 -o $use_fifo -ne 1 ]; then
567      diff=$(getTimeDiff $START $SECONDS)
568      elapsed=$(expr $SECONDS - $START)
569      if [ "$elapsed" != "0" ]; then
570         fps=$(echo "scale=3; $video_frames / $elapsed " | bc)
571         logIt "finished ${stepdesc[$count]} ( took $diff - $fps fps)"
572      else
573         logIt "finished ${stepdesc[$count]} ( took $diff )"
574      fi
575   fi
576done
577
578END=$SECONDS
579diff=$(getTimeDiff $STARTALL $SECONDS)
580temp=$(expr $SECONDS - $STARTALL)
581if [ "$temp" != "0" ]; then  # avoid divide by zero possibility
582   fps=$(echo "scale=3; $video_frames / $temp" | bc)
583   logIt "finished encoding (took $diff - $fps fps)"
584else
585   logIt "finished encoding (took $diff)"
586fi
587cleanExit 0
588