#!/usr/bin/env ruby
$VERBOSE=1

require 'optparse'

# Some nice defines that shouldn't be overridden
FFMPEG='ffmpeg'
MAX_RATE='1450k'
MAX_SIZE='480x320'

# These are not the exact ratios, but to make the compression
# happiest, each value should be a multiple of 16.  These are the
# closest values without going too far askew.
# The ideal world:
#   4:3 => '427x320'
#  16:9 => '480x270'
RATIO2VIDEO = {
  '4:3' => '432x320',
  '16:9' => '480x272'
}.freeze

# Some options that can be overridden
audio_bit_rate='48k'
video_bit_rate='768k'
aspect_ratio='16:9'
ffmpeg='ffmpeg'
test_mode = false
threads=3
passes=2
audio_map=nil
echo = false

opts = OptionParser.new
opts.on('-4', '--4x3', 'Use 4:3 as the aspect ratio') { aspect_ratio = '4:3' }
opts.on('-v BITRATE', '--video_bitrate BITRATE') { |v| video_bit_rate = v }
opts.on('-a BITRATE', '--audio_bitrate BITRATE') { |a| audio_bit_rate = a }
opts.on('-e', '--echo', 'Echo the command') { echo = true }
opts.on('-s', '--single', 'Single pass only') { passes = 1 }
opts.on('-m TRACK', '--map TRACK', 'Map Audio Track') { |t| audio_map = t }
opts.on('-p THREADS', '--threads THREADS',
        'Tell ffmpeg to use this many threads') { |t| threads = t }
opts.on('-t', '--test', 'Use test mode') { test_mode = true }
opts.on('-h', '--help', 'Show this message') do
  puts opts
  exit
end
begin
  opts.parse!(ARGV)
rescue OptionParser::ParseError => e
  puts "Incorrect command line usage: " + e.reason + " for " + e.args.to_s
  exit 1
end

# For some reason, the iPhone really likes the -aspect flag as having
# the actual dimentions, rather than something like '16:9'
video_size = RATIO2VIDEO[aspect_ratio]
vaspect = video_size.sub('x', ':')

ffmpeg_args = "-threads #{threads} -f mp4"
ffmpeg_videoargs = "-s #{video_size} -aspect #{vaspect} -r 30000/1001 -bitexact h264 -vcodec h264 -g 250 -b #{video_bit_rate} -bufsize #{video_bit_rate} -level 30 -loop 1 -maxrate #{MAX_RATE} -trellis 2"
ffmpeg_audioargs = "-acodec libfaac -ar 48000 -ac 2 -ab 48k"

# If this is a test, only encode 30 seconds.
ffmpeg_args = "-t 30 " + ffmpeg_args if test_mode

# Remap the audio track.
ffmpeg_audioargs += " -map 0:0 -map 0:#{audio_map}" if !audio_map.nil?

# I should make a provision for taking data from stdin.  Of course, you
# can't really do a 2 pass on stdin.
ARGV.each do |arg|
  outfile = File.basename(arg, File.extname(arg)) + ".mp4"

  # I need to find a better way to do this, but the lazy way wins out for now.
  if passes >= 2
    command = "#{FFMPEG} -pass 1 -i '#{arg}' #{ffmpeg_args} #{ffmpeg_videoargs} #{ffmpeg_audioargs} -y /dev/null"
    puts command if echo
    system(command)
    command = "#{FFMPEG} -pass 2 -i '#{arg}' #{ffmpeg_args} #{ffmpeg_videoargs} #{ffmpeg_audioargs} '#{outfile}'"
    puts command if echo
    system(command)
  else
    command = "#{FFMPEG} -i '#{arg}' #{ffmpeg_args} #{ffmpeg_videoargs} #{ffmpeg_audioargs} '#{outfile}'"
    puts command if echo
    system(command)
  end
end
