#!/bin/sh
# 
# fxtv_cnvt.sh
#    Fxtv Video/System Stream Conversion Script
#
#    Default videoCnvtScript delivered with BSD X TV
#
#    NOTE:  This script should not be called directly.  
#           It is "sourced" by Fxtv-generated encode scripts to do the 
#           work of audio/video/system stream encoding.
# 
#

#-------------------------------- SETTINGS -----------------------------------

# NOTE:  These are samples of settings that will be set by the parent script
#        that sources us.
#AV_RAW_FILES="T1,T2,T3,T4"
#AV_TARGET="MPEG"                       # IMAGES | MPEG
#
#TARGET_FN_BASE="Jag"
#
#AUDIO_ENABLED="YES"
#AUDIO_CAP_FMT_SAMPLE="LIN_S16_LE"      # MULAW_U8|LIN_{[US]8,[US]16_[LB]E}
#AUDIO_CAP_FMT_CHAN="2"
#AUDIO_CAP_FMT_FREQ="44100"
#AUDIO_TARGET_FMT="MPEG2"               # RAW|SUNAU|WAV|VOC|AIFF|MPEG2|MPEG3
#
#VIDEO_RES_X="320"
#VIDEO_RES_Y="240"
#VIDEO_CAP_FMT="IYUV"                   # RGB16|IYUV|YUY2|YUY2L
#VIDEO_TARGET_FPS="30"
#VIDEO_STREAM="YES"
#
#IMAGE_TARGET_FMT="YUV"                 # TIFF|PPM|YUV
#
#CLEANUP_TEMP_FILES="YES"

#--------------------------------- EXEC PATHS ---------------------------------

# Default to $PATH
if [ -z "$PATH_FXTV" ]; then
   PATH_FXTV="/usr/local/bin/fxtv"
fi
PATH_SOX="/usr/local/bin/sox"
PATH_MPEG_MUSICIN="/usr/local/bin/mp3encode"
PATH_MPEG_ENCODE="/usr/local/bin/mpeg_encode"
PATH_MPLEX="/usr/local/bin/mplex"

#--------------------- MPEG_ENCODE PARAMETER FILE TEMPLATES ------------------

MPEG_ENC_PARAM_STR_HEADER=\
'# 
# "mpeg_encode" parameter file; generated by BSD X TV
# 

PATTERN           IBPBIBPBPB
GOP_SIZE          10
SLICES_PER_FRAME  1
PIXEL             HALF
RANGE             8
PSEARCH_ALG       LOGARITHMIC
BSEARCH_ALG       SIMPLE
IQSCALE           4
PQSCALE           4
BQSCALE           12
FRAME_RATE        __FPS__
REFERENCE_FRAME   ORIGINAL
YUV_SIZE          __XRES__x__YRES__
OUTPUT            __OUTFILE__'

MPEG_ENC_PARAM_STR_TIFF=\
'BASE_FILE_FORMAT  PPM
INPUT_CONVERT     /usr/local/bin/tifftopnm *'

MPEG_ENC_PARAM_STR_PPM=\
'BASE_FILE_FORMAT  PPM
INPUT_CONVERT     *'

MPEG_ENC_PARAM_STR_IYUV=\
'BASE_FILE_FORMAT  YUV
INPUT_CONVERT     *
YUV_FORMAT        EYUV'

MPEG_ENC_PARAM_STR_INP_FILES=\
'INPUT_DIR         __FRAMEDIR__
INPUT
__FRAMEBASE__.*.__FRAMEEXT__  [00001-__NUMFRAMES__]
END_INPUT'

MPEG_ENC_PARAM_STR_INP_STDIN=\
'INPUT_DIR         stdin'

#--------------------------------- METHODS ------------------------------------

ExecCmd() {
    echo "::EXEC:: $@"
    eval "$@"
    status=$?
    echo "::STATUS:: $status"
    if [ $status != 0 ]; then
       echo 
       echo "Command Failed:"
       echo "  $@"
       echo "Conversion Aborted."
       exit 1
    fi
}

GenSoxArgs() {
   local fmt fmtsmp fmtchan fmtfreq
   
   fmt="$1"; fmtsmp="$2"; fmtchan="$3"; fmtfreq="$4"
    
   case $fmt in 
       RAW)     case $fmtsmp in
                   MULAW_U8)   SOXargs="-t raw -U -b"    ;;
                   LIN_S8)     SOXargs="-t raw -s -b"    ;;
                   LIN_U8)     SOXargs="-t raw -u -b"    ;;
                   LIN_S16_LE) SOXargs="-t raw -s -w"    ;;
                   LIN_U16_LE) SOXargs="-t raw -u -w"    ;;
                   LIN_S16_BE) SOXargs="-t raw -s -w -x" ;;
                   LIN_U16_BE) SOXargs="-t raw -u -w -x" ;;
                esac
                SOXargs="$SOXargs -c $fmtchan -r $fmtfreq"
                ;;
       SUNAU)      SOXargs="-t raw -U -b -c 1 -r 8012" ;;
       WAV)        SOXargs="-t wav"  ;;
       VOC)        SOXargs="-t voc"  ;;     
       AIFF)       SOXargs="-t aiff" ;;     
       MPEG2)      SOXargs="-t aiff" ;;     
       MPEG3)      SOXargs="-t aiff" ;;     
   esac
}

#
# GenAudioPipe
#   Generates a pipeline of commands to convert audio
#   from one format to another; the pipeline string is returned
#
GenAudioPipe() {
   local in_fn in_fmt in_fmtsmp in_fmtchan in_fmtfreq
   local out_fnbase out_fmt out_fmtsmp out_fmtchan out_fmtfreq
   local ext mpeg_enc out_soxfn out_soxfmt cmd
   
   in_fn="$1"
   in_fmt="$2"; in_fmtsmp="$3"; in_fmtchan="$4"; in_fmtfreq="$5"
   out_fnbase="$6"
   out_fmt="$7"; out_fmtsmp="$8"; out_fmtchan="$9"; out_fmtfreq="${10}"

   mpeg_enc=NO
   if [ $out_fmt = MPEG2 -o $out_fmt = MPEG3 ]; then
      mpeg_enc=YES
   fi

   # Sanity checks
   if [ $in_fmt = MPEG2 -o $in_fmt = MPEG3 ]; then
      echo Decode from MPEG-2/3 not supported yet
      exit 1
   fi
   if [ $mpeg_enc = YES -a $out_fnbase = - ]; then
      echo Encode MPEG-2/3 to stdout not supported yet
      exit 1
   fi

   # Generate SOX output filename
   if [ $out_fnbase = - ]; then
      GAout_fn="-"
   else
      case $out_fmt in 
          RAW)    ext="raw"  ;;  
          SUNAU)  ext="au"   ;;  
          WAV)    ext="wav"  ;;  
          VOC)    ext="voc"  ;;       
          AIFF)   ext="aiff" ;;       
          MPEG2)  ext="mp2"  ;;       
          MPEG3)  ext="mp3"  ;;    
      esac
      GAout_fn="$out_fnbase.$ext"
   fi

   # Do we need an intermediate file?
   if [ $mpeg_enc = YES ]; then
      out_soxfn="$out_fnbase.raw"
      out_soxfmt="RAW"
   else
      out_soxfn="$GAout_fn"
      out_soxfmt="$out_fmt"
   fi

   # Generate SOX pipe
   GenSoxArgs "$in_fmt" "$in_fmtsmp" "$in_fmtchan" "$in_fmtfreq"
   cmd="$PATH_SOX $SOXargs $in_fn"
   GenSoxArgs "$out_soxfmt" "$out_fmtsmp" "$out_fmtchan" "$out_fmtfreq"
   if [ $mpeg_enc = YES ]; then
     SOXargs="$SOXargs -s -w"   # Hack to force 16-bit signed (PCM driver)
   fi
   cmd="$cmd $SOXargs $out_soxfn"

   # Generate MPEG-2/3 encode pipe
   if [ $mpeg_enc = YES ]; then
      if [ $out_fmt = MPEG2 ]; then
         cmd="$cmd && $PATH_MPEG_MUSICIN -l 2 -p 1 $out_soxfn $GAout_fn"
      else
         cmd="$cmd && $PATH_MPEG_MUSICIN -l 3 -p 2 $out_soxfn $GAout_fn"
      fi
      if [ $CLEANUP_TEMP_FILES = YES ]; then
         cmd="$cmd && rm $out_soxfn"
      fi
   fi

   GAcmd="$cmd"
}

#
# GenAVRawExtractPipe
#   Generates a pipeline of commands to extract video (and possibly audio
#   from an Fxtv RAW AV Fileset.
#
GenAVRawExtractPipe() {
   local in_fnlist vid_fspec vid_frfmt audraw_fspec fps

   in_fnlist="$1"
   vid_fspec="$2"
   vid_frfmt="$3"
   audraw_fspec="$4"
   fps="$5"
   
   GAVcmd="$PATH_FXTV -batch streamavcap -streaminput $in_fnlist"
   GAVcmd="$GAVcmd -frameformat $vid_frfmt -videotarget $vid_fspec"
   
   if [ $fps -gt 0 ]; then
       GAVcmd="$GAVcmd -streamfps $fps"
   fi
   if [ -n "$audraw_fspec" ]; then
       GAVcmd="$GAVcmd -audiotarget $audraw_fspec"
   fi
}

WriteMPEGEncodeParamFile() {
    local out_fnbase outmpg res_x res_y fps frame_fmt dir str base ext
    local num_frames non0 num0

    out_fnbase="$1"; res_x="$2"; res_y="$3"; num_frames="$4" fps="$5"
    frame_fmt="$6"; video_stream="$7"

    MPout_fnmpg="$out_fnbase.mpg"
    MPout_fnparam="$out_fnbase.param"

    # Write the header
    echo "$MPEG_ENC_PARAM_STR_HEADER" | \
       sed -e "s/__FPS__/$fps/"         \
           -e "s/__XRES__/$res_x/"      \
           -e "s/__YRES__/$res_y/"      \
           -e "s|__OUTFILE__|$MPout_fnmpg|" \
       > $MPout_fnparam

    # Write the frame-format specific part 
    case $frame_fmt in
       TIFF)  str="$MPEG_ENC_PARAM_STR_TIFF";;
       PPM)   str="$MPEG_ENC_PARAM_STR_PPM" ;;
       YUV)   str="$MPEG_ENC_PARAM_STR_IYUV";;
    esac
    echo "$str" \
       >> $MPout_fnparam

    # Write part specific to streaming
    if [ $video_stream = YES ]; then
       str="$MPEG_ENC_PARAM_STR_INP_STDIN"
       echo "$str" \
          >> $MPout_fnparam
    else
       # Canonicalize num frames to 5 digits
       num_frames=`echo $num_frames | awk '{ printf "%05d", $1 }'`

       dir=`dirname $out_fnbase`
       base=`basename $out_fnbase`
       case $frame_fmt in
          TIFF)  ext="tif";;
          PPM)   ext="ppm";;
          YUV)   ext="yuv";;
       esac
       echo "$MPEG_ENC_PARAM_STR_INP_FILES" | \
          sed -e "s/__FRAMEDIR__/$dir/"       \
              -e "s/__FRAMEBASE__/$base/"     \
              -e "s/__FRAMEEXT__/$ext/"       \
              -e "s/__NUMFRAMES__/$num_frames/"   \
          >> $MPout_fnparam
    fi
}

#
# GenMPEGEncodePipe
#   Generates a pipeline of commands to encode an MPEG video stream.
#
GenMPEGEncodePipe() {
   local parm_file

   parm_file="$1"

   GMcmd="$PATH_MPEG_ENCODE $parm_file"
}

#
# GenMplexEncodePipe
#   Generates a pipeline of commands to encode an MPEG system stream.
#
GenMplexEncodePipe() {
   local in_fnmpg in_fnmpa
   in_fnmpg="$1"
   in_fnmpa="$2"
   
   GXout_fnmps="$TARGET_FN_BASE.mps"
   GXcmd="$PATH_MPLEX $in_fnmpg $in_fnmpa $GXout_fnmps"
}

CountFrameFiles() {
    local out_fnbase frame_fmt ext

    out_fnbase="$1"; frame_fmt="$2"

    case $frame_fmt in
       TIFF)  ext="tif";;
       PPM)   ext="ppm";;
       YUV)   ext="yuv";;
    esac
    
    CFFnum_frames=`ls -1 $out_fnbase*$ext | wc -l`
}


#------------------------------- MAIN PROGRAM ---------------------------------

#
# Make sure the relevent input settings have been set
#
#!/bin/sh
vars='$AV_RAW_FILES $AV_TARGET $TARGET_FN_BASE \
      $AUDIO_ENABLED $AUDIO_CAP_FMT_SAMPLE $AUDIO_CAP_FMT_CHAN \
      $AUDIO_CAP_FMT_FREQ $AUDIO_TARGET_FMT \
      $VIDEO_RES_X $VIDEO_RES_Y $VIDEO_CAP_FMT \
      $VIDEO_TARGET_FPS $VIDEO_STREAM $IMAGE_TARGET_FMT $CLEANUP_TEMP_FILES'

set -- $vars
missing_var=NO
while [ $# -gt 0 ]; do
   shift
done

if [ $missing_var = YES ]; then
   exit 1
fi

#
# A hack to accommodate mpeg_encode's frame rate limitations (see
# mpeg_encode's users-guide.ps).
#
if [ `expr $VIDEO_TARGET_FPS '<=' 24` = 1 ]; then
   fps=24
elif [ `expr $VIDEO_TARGET_FPS '<=' 25` = 1 ]; then
   fps=25
else
   fps=30
fi
VIDEO_TARGET_FPS=$fps

#
# Generate Raw AV File Parse Pipe
#
can_stream_video=NO
if [ $AV_TARGET = MPEG ]; then
   if [ $IMAGE_TARGET_FMT = PPM -o $IMAGE_TARGET_FMT = YUV ]; then 
      can_stream_video=YES
   fi
   if [ $can_stream_video = NO ]; then
      VIDEO_STREAM=NO
   fi
fi


if [ $AV_TARGET = IMAGES -o $VIDEO_STREAM != YES ]; then
   image_fpec="$TARGET_FN_BASE%s"
   image_fps=0
else
   image_fpec="-"
   image_fps=$VIDEO_TARGET_FPS
fi

if [ $AUDIO_ENABLED = YES ]; then
   audio_fspec="$TARGET_FN_BASE.AUDraw"
else
   audio_fspec=""
fi

GenAVRawExtractPipe "$AV_RAW_FILES" "$image_fpec" "$IMAGE_TARGET_FMT" \
                    "$audio_fspec" "$image_fps"

#
# Parse the RAW AV File (and for MPEG, run mpeg_encode)
#
if [ $AV_TARGET = MPEG ]; then
   if [ $VIDEO_STREAM = YES ]; then
      WriteMPEGEncodeParamFile \
                       "$TARGET_FN_BASE" "$VIDEO_RES_X" "$VIDEO_RES_Y" \
                       "" "$VIDEO_TARGET_FPS" \
                       "$IMAGE_TARGET_FMT" "$VIDEO_STREAM"
      GenMPEGEncodePipe $MPout_fnparam
      ExecCmd "$GAVcmd | $GMcmd"
   else 
      ExecCmd "$GAVcmd"
      CountFrameFiles
      WriteMPEGEncodeParamFile \
                       "$TARGET_FN_BASE" "$VIDEO_RES_X" "$VIDEO_RES_Y" \
                       "$CFFnum_frames" "$VIDEO_TARGET_FPS" \
                       "$IMAGE_TARGET_FMT" "$VIDEO_STREAM"
      
      GenMPEGEncodePipe $MPout_fnparam
      ExecCmd "$GMcmd"
   fi

   if [ $CLEANUP_TEMP_FILES = YES ]; then
      ExecCmd "rm $MPout_fnparam"
   fi
else
   ExecCmd "$GAVcmd"
fi

#
# Encode the audio
#
if [ $AUDIO_ENABLED = YES ]; then
   GenAudioPipe "$audio_fspec" RAW \
          "$AUDIO_CAP_FMT_SAMPLE" "$AUDIO_CAP_FMT_CHAN" "$AUDIO_CAP_FMT_FREQ" \
          "$TARGET_FN_BASE" "$AUDIO_TARGET_FMT" \
          "$AUDIO_CAP_FMT_SAMPLE" "$AUDIO_CAP_FMT_CHAN" "$AUDIO_CAP_FMT_FREQ"
   ExecCmd "$GAcmd"

    if [ $CLEANUP_TEMP_FILES = YES ]; then
       ExecCmd "rm $audio_fspec"
    fi
fi

#
# Build an MPEG system stream
#
if [ $AV_TARGET = MPEG -a $AUDIO_ENABLED = YES ]; then
   GenMplexEncodePipe "$MPout_fnmpg" "$GAout_fn"
   ExecCmd "$GXcmd"

   if [ $CLEANUP_TEMP_FILES = YES ]; then
     ExecCmd "rm $MPout_fnmpg $GAout_fn"
   fi
fi

exit 0
