Difference between revisions of "Example Script using mythtranscode in fifodir mode"

From MythTV Official Wiki
Jump to: navigation, search
 
Line 1: Line 1:
The script pipes the output of mythtranscode into ffmpeg, using libx264 to compress the video. The intended use is to set it up as a myth user job. the <code>--passthrough</code> option can currently be used only on 0.25.
+
The script pipes the output of mythtranscode into ffmpeg, using libx264 to compress the video. The intended use is to set it up as a myth user job.
 
<pre>
 
<pre>
#!/bin/bash
+
#!/usr/bin/ruby -w
  
exec >/var/log/mythtv/mythar`date +%F-%T`.log 2>&1
+
require 'time'
 +
require 'fileutils'
 +
require 'tmpdir'
  
usage()
+
$stdout.reopen("/var/log/mythtv/mythar#{Time.now.strftime '%F-%T'}.log", "w")
{
+
$stderr.reopen($stdout)
echo "Usage: $0 --chanid <channel id> --starttime <start time> --title <title> [--subtitle <sub title>] --outdir <directory> [options]";
+
 
echo "      $0 --chanid <channel id> --starttime <start time> -outfile <path> [options]";
+
def usage
echo "";
+
    print <<-END
echo "      --chanid <channel id>                  The channel id for the recording. REQUIRED.";
+
Usage: #{$0} --chanid <channel id> --starttime <start time> --title <title> [--subtitle <sub title>] --outdir <directory> [options]
echo "      --starttime <start time>                The start time for the recording. REQUIRED.";
+
      #{$0} --chanid <channel id> --starttime <start time> -outfile <path> [options]
echo "      --title <title>                        The title of the recording.";
+
 
echo "      --subtitle <subtitle>                  The subtitle of the recording.";
+
      --chanid <channel id>                  The channel id for the recording. REQUIRED.
echo "      --outdir <path>                        The path of the directory for the output file. If one of";
+
      --starttime <start time>                The start time for the recording. REQUIRED.
echo "                                              --title --subtitle and --outdir are specified, then both --title and --outdir must";
+
      --title <title>                        The title of the recording.
echo "                                              be, and an output file will be chosen on their basis, avoiding";
+
      --subtitle <subtitle>                  The subtitle of the recording.
echo "                                              conflict with existing files.";
+
      --outdir <path>                        The path of the directory for the output file. If one of
echo "      --outfile <path>                        The exact path of the output file. ALTERNATIVE to";
+
                                              --title --subtitle and --outdir are specified, then both --title and --outdir must
echo "                                              --title --subtitle and --outdir.";
+
                                              be, and an output file will be chosen on their basis, avoiding
echo "      --vsize <width>x<height>                Scale video to specified resolution (the aspect ratio will";
+
                                              conflict with existing files.
echo "                                              be maintained using non-square pixels). If not specified,";
+
      --outfile <path>                        The exact path of the output file. ALTERNATIVE to
echo "                                              the source resolution will be maintained.";
+
                                              --title --subtitle and --outdir.
echo "      --interlaced                            Assume the source is interlaced and maintain the interlace,";
+
      --vsize <width>x<height>                Scale video to specified resolution (the aspect ratio will
echo "                                              provided --vsize not specified. If --vsize is specified then";
+
                                              be maintained using non-square pixels). If not specified,
echo "                                              recode at double the frame rate.";
+
                                              the source resolution will be maintained.
echo "      --passthrough                          Pass through the audio unaltered if possible.";
+
      --interlaced                            Assume the source is interlaced and maintain the interlace,
echo "      --quality <quality>                    Quality for compression (default 20.0)";
+
                                              provided --vsize not specified. If --vsize is specified then
}
+
                                              recode at double the frame rate.
 +
      --passthrough                          Pass through the audio unaltered if possible.
 +
      --quality <quality>                    Quality for compression (default 20.0)
 +
    END
 +
end
  
 
# Read arguments
 
# Read arguments
unset CHANID
+
chanid      = nil
unset STARTTIME
+
starttime  = nil
unset TITLE
+
title      = nil
unset SUBTITLE
+
subtitle    = nil
unset OUTDIR
+
outdir      = nil
unset OUTFILE
+
outfile    = nil
unset VSIZE
+
vsize      = nil
unset INTERLACED
+
interlaced  = false
unset PASSTHROUGH
+
passthrough = false
unset QUALITY
+
quality    = '20.0'
 +
 
 +
processOption = Proc.new do |args|
 +
    raise "Misspecified option: #{args.join(', ')}" unless args.length == 2
 +
    option = args[0].sub(/^--/, '')
 +
    val = args[1]
 +
    eval("#{option} = val")
 +
end
 +
 
 +
processFlag = Proc.new do |args|
 +
    raise "Misspecified option: #{args.join(', ')}" unless args.length == 1
 +
    option = args[0].sub(/^--/, '')
 +
    eval("#{option} = true")
 +
end
 +
 
 +
handler = {'--chanid'      => processOption,
 +
          '--starttime'  => processOption,
 +
          '--title'      => processOption,
 +
          '--subtitle'    => processOption,
 +
          '--outdir'      => processOption,
 +
          '--outfile'    => processOption,
 +
          '--vsize'      => processOption,
 +
          '--interlaced'  => processFlag,
 +
          '--passthrough' => processFlag,
 +
          '--quality'    => processOption}
 +
 
 +
 
 +
ARGV.slice_before{|arg| arg =~ /^--/}.each do |par|
 +
    raise "Unrecognised option: #{par.join(', ')}" unless handler.has_key? par[0]
 +
    handler[par[0]].call(par)
 +
end
  
ARGS=("$@")
 
I=0
 
while (( $I < $# )); do
 
{
 
    case ${ARGS[I]} in
 
        --chanid)      CHANID=${ARGS[I+1]};      I=$((I+2));;
 
        --starttime)  STARTTIME=${ARGS[I+1]};  I=$((I+2));;
 
        --title)      TITLE=${ARGS[I+1]};      I=$((I+2));;
 
        --subtitle)    SUBTITLE=${ARGS[I+1]};    I=$((I+2));;
 
        --outdir)      OUTDIR=${ARGS[I+1]};      I=$((I+2));;
 
        --outfile)    OUTFILE=${ARGS[I+1]};    I=$((I+2));;
 
        --vsize)      VSIZE=${ARGS[I+1]};      I=$((I+2));;
 
        --interlaced)  INTERLACED=1;            I=$((I+1));;
 
        --passthrough) PASSTHROUGH=1;            I=$((I+1));;
 
        --quality)    QUALITY=${ARGS[I+1]};    I=$((I+2));;
 
        *)            echo "Illegal option ${ARGS[I]}"; usage; exit 1;;
 
    esac
 
} done
 
  
# Check arguments make sense
+
if chanid.nil? || starttime.nil?
if [[ ! -n $CHANID || ! -n $STARTTIME ]]; then
+
     raise '--chanid and --starttime required'
{
+
end
     echo "--chanid and --starttime required"
 
    usage
 
    exit 1
 
} fi
 
  
if [[ ! -n $OUTFILE && ! -n $OUTDIR ]]; then
+
if outfile.nil? && outdir.nil?
{
+
     raise 'Use either --outfile, or both --title --outdir, optionally with --subtitle'
     echo "Use either --outfile, or both --title --outdir, with optionally --subtitle"
+
end
    usage
 
    exit 1
 
} fi
 
  
if [[ -n $OUTFILE && ( -n $TITLE || -n $SUBTITLE || -n $OUTDIR) ]]; then
+
if outfile && (title || subtitle || outdir)
{
+
     raise 'Use either --outfile, or both --title --outdir, optionally with --subtitle'
     echo "Use either --outfile, or both --title --outdir, with optionally --subtitle"
+
end
    usage
 
    exit 1
 
} fi
 
  
if [[ ( -n $TITLE || -n $OUTDIR || -n $SUBTITLE) && ( ! -n $OUTDIR || ! -n $TITLE ) ]]; then
+
if (title || outdir || subtitle) && ! (title && outdir)
{
+
     raise 'Both --title and --outdir required for this mode of oparation'
     echo "Both --title and --outdir required for this mode of oparation"
+
end
    usage
 
    exit 1
 
} fi
 
  
if [[ -n $PASSTHROUGH ]]; then
+
mythPassthrough = passthrough ? '--passthrough' : ''
{
 
    MYTHPASSTHROUGH="--passthrough"
 
}
 
else
 
{
 
    MYTHPASSTHROUGH=""
 
} fi
 
  
 
#Detect source formats
 
#Detect source formats
INFO=`mythtranscode -v general --chanid $CHANID --starttime $STARTTIME --fifoinfo $MYTHPASSTHROUGH`
+
info=`mythtranscode -v general --chanid #{chanid} --starttime #{starttime} --fifoinfo #{mythPassthrough}`
echo $INFO
 
OLDIFS=$IFS
 
IFS=$'\n'
 
LINES=( $INFO )
 
IFS=$OLDIFS
 
  
unset SVWIDTH
+
svWidth  = nil
unset SVHEIGHT
+
svHeight = nil
unset SVASPECT
+
svAspect = nil
unset SVRATE
+
svRate  = nil
unset SAFMT
+
saFmt    = nil
unset SACHANS
+
saChans  = nil
unset SARATE
+
saRate  = nil
  
for LINE in "${LINES[@]}" ; do
+
info.each_line do |line|
{
+
     case line.chomp
     case $LINE in
+
    when / FifoVideoWidth (.*)$/      then svWidth  = $1
        *FifoVideoWidth*)         SVWIDTH=${LINE#*FifoVideoWidth };;
+
    when / FifoVideoHeight (.*)$/      then svHeight = $1
        *FifoVideoHeight*)       SVHEIGHT=${LINE#*FifoVideoHeight };;
+
    when / FifoVideoAspectRatio (.*)$/ then svAspect = $1
        *FifoVideoAspectRatio*)   SVASPECT=${LINE#*FifoVideoAspectRatio };;
+
    when / FifoVideoFrameRate (.*)$/  then svRate  = $1
        *FifoVideoFrameRate*)     SVRATE=${LINE#*FifoVideoFrameRate };;
+
    when / FifoAudioFormat (.*)$/      then saFmt    = $1
        *FifoAudioFormat*)       SAFMT=${LINE#*FifoAudioFormat };;
+
    when / FifoAudioChannels (.*)$/    then saChans  = $1
        *FifoAudioChannels*)     SACHANS=${LINE#*FifoAudioChannels };;
+
    when / FifoAudioHz (.*)$/          then saRate  = $1
        *FifoAudioHz*)           SARATE=${LINE#*FifoAudioHz };;
+
    when / FifoAudioSampleRate (.*)$/  then saRate  = $1
        *FifoAudioSampleRate*)   SARATE=${LINE#*FifoAudioSampleRate };;
+
     end
     esac
+
end
} done
 
  
if [[ ! -n $SVWIDTH || ! -n $SVHEIGHT || ! -n $SVASPECT || ! -n $SVRATE || ! -n $SAFMT || ! -n $SACHANS || ! -n $SARATE ]]; then
+
if !svWidth || !svHeight || !svAspect || !svRate || !saFmt || !saChans || !saRate
{
+
    print info
     echo "Failed to derive fifo formats"
+
     raise 'Failed to derive fifo formats'
    exit 1
+
end
} fi
 
  
SVSIZE=${SVWIDTH}x${SVHEIGHT}
+
svSize="#{svWidth}x#{svHeight}"
if [[ $SVASPECT == 1.77778 ]]; then
+
svAspect = '16:9' if svAspect == '1.77778'
{
+
svAspect = '4:3'  if svAspect == '1.33333'
    SVASPECT=16:9
 
}
 
elif [[ $SVASPECT == 1.33333 ]]; then
 
{
 
    SVASPECT=4:3
 
} fi
 
 
 
# Set up default values to unset variables
 
QUALITY=${QUALITY:-"20.0"}
 
  
  
if [[ $SAFMT == aac_latm ]]; then
+
saFmt = 'latm' if saFmt == 'acc_latm'
{
 
    SAFMT=latm;
 
} fi
 
  
  
EXT=".mp4"
+
if saFmt == 'latm'
FFMT="mp4"
+
    ext  = '.mkv'
 
+
    fFmt = 'matroska'
if [[ $SAFMT == latm ]]; then
+
else
{
+
     ext  = '.mp4'
     EXT=".mkv"
+
     fFmt = 'mp4'
     FFMT="matroska"
+
end
} fi
 
  
 
# If --outfile not specified, use --outdir --title and --subtitle
 
# If --outfile not specified, use --outdir --title and --subtitle
if [[ ! -n $OUTFILE ]]; then
+
if !outfile
{
+
     obName = File.join(outdir, title)
     OBNAME="$OUTDIR/$TITLE"
+
     obName += " - #{subtitle}" if (subtitle && subtitle.length > 0)
     if [[ "$SUBTITLE" != "" ]]; then OBNAME+=" - $SUBTITLE"; fi
 
  
     I=1
+
     (0..Float::INFINITY).each do |i|
    OUTFILE="$OBNAME$EXT"
+
        outfile = obName + (i == 0 ? "" : "(#{i})") + ext
    while [[ -a "$OUTFILE" ]]; do
+
         break if !File.exist? outfile
    {
+
     end
        I=$((I+1))
+
end
         OUTFILE="$OBNAME($I)$EXT"
 
     } done
 
} fi
 
  
 
# Can do pass through only with formats that ffmpeg will take as input
 
# Can do pass through only with formats that ffmpeg will take as input
if [[ $SAFMT != ac3 && $SAFMT != dts && $SAFMT != mp3 && $SAFMT != latm ]]; then
+
passthrough = false unless %w{ac3 dts mp3 latm}.include? saFmt
{
 
    unset PASSTHROUGH
 
} fi
 
  
 
# Tell ffmpeg what audio to expect and what to do with it
 
# Tell ffmpeg what audio to expect and what to do with it
if [[ -n $PASSTHROUGH ]]; then
+
if passthrough
{
+
     mythPassthrough = '--passthrough'
     MYTHPASSTHROUGH="--passthrough"
+
     audInDesc      = "-f #{saFmt}"
     AUDINDESC="-f $SAFMT"
+
     audCodec        = 'copy'
     AUDCODEC="copy"
 
}
 
 
else
 
else
{
+
     mythPassthrough = ''
     MYTHPASSTHROUGH=""
+
     audInDesc      = "-f s16le -ac 2 -ar #{saRate}"
     AUDINDESC="-f s16le -ac 2 -ar $SARATE"
+
     audCodec        = 'ac3 -ab 160k'
     AUDCODEC="ac3 -ab 160k"
+
end
} fi
+
 
 +
 
 +
filters = []
 +
vFilter = nil
 +
vRate  = nil
 +
vFlags  = nil
  
unset VSCALER
+
filters.push("crop=#{svWidth}:1080:0:0") if svHeight == '1088'
# If scaling use lanczos
 
if [[ -n $VSIZE ]]; then
 
{
 
    VSIZE=`echo $VSIZE | sed 's/x/:/'`
 
    # Lanczos seems to make the files bigger yet less sharp
 
    #VSCALER="-sws_flags lanczos"
 
} fi
 
  
unset VFILTER
+
if interlaced
unset VFLAGS
+
     if vsize
unset VRATE
 
if [[ -n $INTERLACED ]]; then
 
{
 
     if [[ -n $VSIZE ]]; then
 
    {
 
 
         # If scaling interlaced video, we must deinterlace first
 
         # If scaling interlaced video, we must deinterlace first
         VFILTER="yadif=3,scale=$VSIZE"
+
         filters.push('yadif=3')
         VRATE="-r `echo 2*$SVRATE | bc`"
+
         vRate  = "-r #{2 * svRate.to_i}"
    }
 
 
     else
 
     else
    {
 
 
         # Otherwise maintain the interlace
 
         # Otherwise maintain the interlace
         VFILTER=
+
         vFlags  = '-flags +ilme+ildct'
        VFLAGS="-flags +ilme+ildct"
+
     end
    } fi
+
end
}
 
else
 
{
 
    if [[ -n $VSIZE ]]; then
 
    {
 
        VFILTER="scale=$VSIZE"
 
     } fi
 
} fi
 
 
 
if [[ $SVHEIGHT == 1088 ]]; then
 
{
 
    if [[ -n $VFILTER ]]; then
 
    {
 
        VFILTER="crop=$SVWIDTH:1080:0:0,$VFILTER"
 
    }
 
    else
 
    {
 
        VFILTER="crop=$SVWIDTH:1080:0:0"
 
    } fi
 
  
} fi
+
if vsize
 +
    filters.push("scale=#{vsize.sub('x', ':')}")
 +
end
  
if [[ -n $VFILTER ]]; then
+
vFilter = '-vf ' + filters.join(',') if filters.length > 0
{
 
    VFILTER="-vf $VFILTER"
 
} fi
 
  
 
# Create fifo dir
 
# Create fifo dir
FIFODIR=/tmp/mythar$$
+
fifoDir = Dir.mktmpdir('mythar')
/bin/mkdir $FIFODIR
+
fifoAudio = File.join(fifoDir, 'audout')
trap "rm -r $FIFODIR" EXIT
+
fifoVideo = File.join(fifoDir, 'vidout')
 
 
# Start mythtranscode outputting into the pipes
 
echo /usr/bin/mythtranscode --chanid $CHANID --starttime $STARTTIME --honorcutlist $MYTHPASSTHROUGH --fifodir $FIFODIR --cleancut &
 
/usr/bin/mythtranscode --chanid $CHANID --starttime $STARTTIME --honorcutlist $MYTHPASSTHROUGH --fifodir $FIFODIR --cleancut &
 
  
# Ensure we kill the background task at the end of the run
+
transcodeCmd = "/usr/bin/mythtranscode --chanid #{chanid} --starttime #{starttime} --honorcutlist #{mythPassthrough} --fifodir #{fifoDir} --cleancut"
BACKGROUND=$!
+
ffmpegCmd = "/usr/bin/ffmpeg #{audInDesc} -i #{fifoAudio} -f rawvideo -top 1 -pix_fmt yuv420p -s #{svSize} -r #{svRate} -i #{fifoVideo} -threads 2 #{vFilter} -vcodec libx264 -preset medium -profile high #{vFlags} -crf #{quality} #{vRate} -aspect #{svAspect} -acodec #{audCodec} -ar #{saRate} -f #{fFmt} \"#{outfile}\""
trap "kill -9 $BACKGROUND" EXIT
 
  
# Wait for the background task to create the pipes
+
puts transcodeCmd
while [[ ! -e "$FIFODIR/audout" || ! -e "$FIFODIR/vidout" ]]; do /bin/sleep 1; done
+
puts
 +
puts ffmpegCmd
 +
puts
  
echo /usr/bin/ffmpeg $AUDINDESC -i $FIFODIR/audout -f rawvideo -top 1 -pix_fmt yuv420p -s $SVSIZE -r $SVRATE -i $FIFODIR/vidout -threads 2 $VFILTER $VSCALER -vcodec libx264 -preset medium -profile high $VFLAGS -crf $QUALITY $VRATE -aspect $SVASPECT -acodec $AUDCODEC -ar $SARATE -f $FFMT "$OUTFILE"
+
pid = 0
/usr/bin/ffmpeg $AUDINDESC -i $FIFODIR/audout -f rawvideo -top 1 -pix_fmt yuv420p -s $SVSIZE -r $SVRATE -i $FIFODIR/vidout -threads 2 $VFILTER $VSCALER -vcodec libx264 -preset medium -profile high $VFLAGS -crf $QUALITY $VRATE -aspect $SVASPECT -acodec $AUDCODEC -ar $SARATE -f $FFMT "$OUTFILE"
+
begin
 +
    pid = Process.spawn(transcodeCmd)
 +
    while !File.exist?(fifoAudio) || !File.exist?(fifoVideo)
 +
        raise 'Transcode died before creating pipes' unless Process.wait(pid, Process::WNOHANG).nil?
 +
        sleep(1)
 +
    end
 +
    system(ffmpegCmd)
 +
ensure
 +
    if pid != 0
 +
        Process.kill('KILL', pid)
 +
        Process.wait(pid)
 +
    end
 +
    FileUtils.rm_rf fifoDir
 +
end
 
</pre>
 
</pre>

Latest revision as of 22:14, 25 February 2015

The script pipes the output of mythtranscode into ffmpeg, using libx264 to compress the video. The intended use is to set it up as a myth user job.

#!/usr/bin/ruby -w

require 'time'
require 'fileutils'
require 'tmpdir'

$stdout.reopen("/var/log/mythtv/mythar#{Time.now.strftime '%F-%T'}.log", "w")
$stderr.reopen($stdout)

def usage
    print <<-END
Usage: #{$0} --chanid <channel id> --starttime <start time> --title <title> [--subtitle <sub title>] --outdir <directory> [options]
       #{$0} --chanid <channel id> --starttime <start time> -outfile <path> [options]

       --chanid <channel id>                   The channel id for the recording. REQUIRED.
       --starttime <start time>                The start time for the recording. REQUIRED.
       --title <title>                         The title of the recording.
       --subtitle <subtitle>                   The subtitle of the recording.
       --outdir <path>                         The path of the directory for the output file. If one of
                                               --title --subtitle and --outdir are specified, then both --title and --outdir must
                                               be, and an output file will be chosen on their basis, avoiding
                                               conflict with existing files.
       --outfile <path>                        The exact path of the output file. ALTERNATIVE to
                                               --title --subtitle and --outdir.
       --vsize <width>x<height>                Scale video to specified resolution (the aspect ratio will
                                               be maintained using non-square pixels). If not specified,
                                               the source resolution will be maintained.
       --interlaced                            Assume the source is interlaced and maintain the interlace,
                                               provided --vsize not specified. If --vsize is specified then
                                               recode at double the frame rate.
       --passthrough                           Pass through the audio unaltered if possible.
       --quality <quality>                     Quality for compression (default 20.0)
    END
end

# Read arguments
chanid      = nil
starttime   = nil
title       = nil
subtitle    = nil
outdir      = nil
outfile     = nil
vsize       = nil
interlaced  = false
passthrough = false
quality     = '20.0'

processOption = Proc.new do |args|
    raise "Misspecified option: #{args.join(', ')}" unless args.length == 2
    option = args[0].sub(/^--/, '')
    val = args[1]
    eval("#{option} = val")
end

processFlag = Proc.new do |args|
    raise "Misspecified option: #{args.join(', ')}" unless args.length == 1
    option = args[0].sub(/^--/, '')
    eval("#{option} = true")
end

handler = {'--chanid'      => processOption,
           '--starttime'   => processOption,
           '--title'       => processOption,
           '--subtitle'    => processOption,
           '--outdir'      => processOption,
           '--outfile'     => processOption,
           '--vsize'       => processOption,
           '--interlaced'  => processFlag,
           '--passthrough' => processFlag,
           '--quality'     => processOption}


ARGV.slice_before{|arg| arg =~ /^--/}.each do |par|
    raise "Unrecognised option: #{par.join(', ')}" unless handler.has_key? par[0]
    handler[par[0]].call(par)
end


if chanid.nil? || starttime.nil?
    raise '--chanid and --starttime required'
end

if outfile.nil? && outdir.nil?
    raise 'Use either --outfile, or both --title --outdir, optionally with --subtitle'
end

if outfile && (title || subtitle || outdir)
    raise 'Use either --outfile, or both --title --outdir, optionally with --subtitle'
end

if (title || outdir || subtitle) && ! (title && outdir)
    raise 'Both --title and --outdir required for this mode of oparation'
end

mythPassthrough = passthrough ? '--passthrough' : ''

#Detect source formats
info=`mythtranscode -v general --chanid #{chanid} --starttime #{starttime} --fifoinfo #{mythPassthrough}`

svWidth  = nil
svHeight = nil
svAspect = nil
svRate   = nil
saFmt    = nil
saChans  = nil
saRate   = nil

info.each_line do |line|
    case line.chomp
    when / FifoVideoWidth (.*)$/       then svWidth  = $1
    when / FifoVideoHeight (.*)$/      then svHeight = $1
    when / FifoVideoAspectRatio (.*)$/ then svAspect = $1
    when / FifoVideoFrameRate (.*)$/   then svRate   = $1
    when / FifoAudioFormat (.*)$/      then saFmt    = $1
    when / FifoAudioChannels (.*)$/    then saChans  = $1
    when / FifoAudioHz (.*)$/          then saRate   = $1
    when / FifoAudioSampleRate (.*)$/  then saRate   = $1
    end
end

if !svWidth || !svHeight || !svAspect || !svRate || !saFmt || !saChans || !saRate
    print info
    raise 'Failed to derive fifo formats'
end

svSize="#{svWidth}x#{svHeight}"
svAspect = '16:9' if svAspect == '1.77778'
svAspect = '4:3'  if svAspect == '1.33333'
	


saFmt = 'latm' if saFmt == 'acc_latm'


if saFmt == 'latm'
    ext  = '.mkv'
    fFmt = 'matroska'
else
    ext  = '.mp4'
    fFmt = 'mp4'
end

# If --outfile not specified, use --outdir --title and --subtitle
if !outfile
    obName = File.join(outdir, title)
    obName += " - #{subtitle}" if (subtitle && subtitle.length > 0)

    (0..Float::INFINITY).each do |i|
        outfile = obName + (i == 0 ? "" : "(#{i})") + ext
        break if !File.exist? outfile
    end
end

# Can do pass through only with formats that ffmpeg will take as input
passthrough = false unless %w{ac3 dts mp3 latm}.include? saFmt

# Tell ffmpeg what audio to expect and what to do with it
if passthrough
    mythPassthrough = '--passthrough'
    audInDesc       = "-f #{saFmt}"
    audCodec        = 'copy'
else
    mythPassthrough = ''
    audInDesc       = "-f s16le -ac 2 -ar #{saRate}"
    audCodec        = 'ac3 -ab 160k'
end


filters = []
vFilter = nil
vRate   = nil
vFlags  = nil

filters.push("crop=#{svWidth}:1080:0:0") if svHeight == '1088'

if interlaced
    if vsize
        # If scaling interlaced video, we must deinterlace first
        filters.push('yadif=3')
        vRate   = "-r #{2 * svRate.to_i}"
    else
        # Otherwise maintain the interlace
        vFlags  = '-flags +ilme+ildct'
    end
end

if vsize
    filters.push("scale=#{vsize.sub('x', ':')}")
end

vFilter = '-vf ' + filters.join(',') if filters.length > 0

# Create fifo dir
fifoDir = Dir.mktmpdir('mythar')
fifoAudio = File.join(fifoDir, 'audout')
fifoVideo = File.join(fifoDir, 'vidout')

transcodeCmd = "/usr/bin/mythtranscode --chanid #{chanid} --starttime #{starttime} --honorcutlist #{mythPassthrough} --fifodir #{fifoDir} --cleancut"
ffmpegCmd = "/usr/bin/ffmpeg #{audInDesc} -i #{fifoAudio} -f rawvideo -top 1 -pix_fmt yuv420p -s #{svSize} -r #{svRate} -i #{fifoVideo} -threads 2 #{vFilter} -vcodec libx264 -preset medium -profile high #{vFlags} -crf #{quality} #{vRate} -aspect #{svAspect} -acodec #{audCodec} -ar #{saRate} -f #{fFmt} \"#{outfile}\""

puts transcodeCmd
puts
puts ffmpegCmd
puts

pid = 0
begin
    pid = Process.spawn(transcodeCmd)
    while !File.exist?(fifoAudio) || !File.exist?(fifoVideo)
        raise 'Transcode died before creating pipes' unless Process.wait(pid, Process::WNOHANG).nil?
        sleep(1)
    end
    system(ffmpegCmd)
ensure
    if pid != 0
        Process.kill('KILL', pid)
        Process.wait(pid)
    end
    FileUtils.rm_rf fifoDir
end