Permalink
Find file
Fetching contributors…
Cannot retrieve contributors at this time
executable file 199 lines (162 sloc) 6.17 KB
#!/usr/bin/ruby
# avi2dvd: Ruby script to automatically transcode video and author DVD's
require 'getoptlong'
MAX_THREADS = 1 # Edit for 1 per Processor core
MIN_AUDIO_BITRATE = 128 # In kb/s
AUDIO_BITRATE = 192 # In kb/s
MIN_VIDEO_BITRATE = 1500 # In kb/s
MAX_VIDEO_BITRATE = 6000 # In kb/s
DVD_SIZE = 4.2 # In gigabytes
MAX_RUNTIME = ((DVD_SIZE*1024*1024*8)/(MIN_VIDEO_BITRATE+MIN_AUDIO_BITRATE)/60).to_i # In minutes
FFMPEG_COMMAND = "ffmpeg"
#FFMPEG_COMMAND = "/opt/bin/ffmpeg"
def do_convert(options)
files = options[:files].collect do |f|
raise "File not found: #{f}" unless File.file?(f)
{
:filename => f ,
:length => get_duration(f),
:infile_base => f.sub(/\.[a-z0-9]{2,4}$/i,'')
}
end
total_runtime = files.inject(0){|sum,x| sum += x[:length].to_i}
unless total_runtime <= MAX_RUNTIME
puts "ERROR: Total runtime of #{total_runtime} is greater than #{MAX_RUNTIME} minutes"
return 1
end
audio_bitrate = (total_runtime > 300 ? MIN_AUDIO_BITRATE : AUDIO_BITRATE)
video_bitrate = get_video_bitrate(total_runtime, audio_bitrate)
files.each{|f| f[:video_bitrate] = video_bitrate; f[:audio_bitrate] = audio_bitrate}
if (files.size == 1 || options[:one_title])
base_name = "#{files.first[:infile_base]}"
else
base_name = "Compilation"
base_name = (base_name || '')+ "_dvd".gsub(/[^0-9a-z]/i,'_').gsub(/_+/,'_')
end
# Print Summary
puts "\n"
puts "Summary"
puts "======================================================="
puts " * #{files.size} Titles (#{total_runtime}m): Encoding at #{video_bitrate}kb/s VBR, #{audio_bitrate}kb/s ABR"
puts files.collect{|f| " - #{f[:filename]} => #{f[:length]}m"}.join("\n")
# Create DVD compatible MPG's
files.each do |file|
infile_base = file[:infile_base]
infile = file[:filename]
video_bitrate = file[:video_bitrate]
audio_bitrate = file[:audio_bitrate]
length = file[:length]
# Pre-Run Cleanup
system("rm -rf '#{base_name}_dvd/' '#{infile_base}_dvd.mpg' '#{base_name}.iso'")
# Create DVD compatible MPG
puts "\n\n"
puts "Creating DVD compliant video."
puts "======================================================="
puts " * Source: #{infile}"
puts " * Duration: #{length}"
puts " * Output: vbr => #{video_bitrate}, abr => #{audio_bitrate}"
ffmpeg_command = "#{FFMPEG_COMMAND} -threads #{MAX_THREADS} -i '#{infile}' \
-async 2 -aspect #{options[:ratio]} -target ntsc-dvd -b #{video_bitrate}k \
-ab #{audio_bitrate}k -ac 2 '#{infile_base}_dvd.mpg'"
puts "-------------------------------------------------------"
print "Command: "
puts ffmpeg_command
puts "-------------------------------------------------------"
system(ffmpeg_command)
raise "Error transcoding! #{infile_base}_dvd.mpg does not exist." unless File.exist?("#{infile_base}_dvd.mpg")
end
# Author DVD
puts "\n\n\n"
puts "Creating VIDEO_TS..."
puts "======================================================="
title_info = []
if options[:one_title]
if options[:with_chapters]
chapters = (["0"] + (1..length.divmod(5)[0]).to_a.collect{|t| (5*t).to_s + ":00"}).join(',')
end
file_args = files.collect{|f| "-f \"#{f[:infile_base]}_dvd.mpg\""}.join(' ')
file_info = "-t #{"--chapters='#{chapters}'" if chapters} #{file_args}"
title_info << file_info
else
files.each do |file|
if options[:with_chapters]
chapters = (["0"] + (1..file[:length].divmod(5)[0]).to_a.collect{|t| (5*t).to_s + ":00"}).join(',')
end
file_info = "-t #{"--chapters='#{chapters}'" if chapters} -f \"#{file[:infile_base]}_dvd.mpg\" "
title_info << file_info
end
end
author_command = "dvdauthor #{title_info.join(' ')} -o '#{base_name}_dvd/'"
puts author_command
system(author_command)
system("dvdauthor -o '#{base_name}_dvd/' -T")
# Create ISO
puts "\n\n\n"
puts "Creating ISO..."
puts "======================================================="
system("mkisofs -V '#{base_name[0..30]}' -dvd-video -udf -v -o '#{base_name}.iso' '#{base_name}_dvd'")
# Post-Run Cleanup
puts "\n\n\n"
puts "Cleaning Up.."
puts "======================================================="
system ("rm *_dvd.mpg")
end
def get_video_bitrate(length,audio_bitrate=AUDIO_BITRATE)
space_for_video = (DVD_SIZE*1024*1024 - audio_bitrate.to_f*length.to_f)
video_max_bitrate = ((space_for_video / length.to_f) / 8.0).to_i
bitrate = [video_max_bitrate,MAX_VIDEO_BITRATE].min
return bitrate
end
def get_duration(infile)
info = `ffmpeg -i "#{infile}" 2>&1`
duration_string = info.match(/Duration: ([0-9]{2}:[0-9]{2}:[0-9]{2})/)[1]
(hours,minutes,seconds) = duration_string.split(':')
length_in_minutes = (hours.to_i*60 + minutes.to_i + 1).to_i
end
def usage
return <<EOF
Usage: avi2dvd [OPTIONS] FILE FILE2 FILE3
Automatically transcode and author single or multi-title DVD's to ISO.
Video bitrate is automatically optimized for runtime.
Requires ffmpeg, dvdauthor, mkisofs.
-h, --help
show help
--ratio
specify aspect ratio (i.e. 4:3 or default 16:9)
--with-chapters
enable 5 minute chapter intervals
--one-title
merge files into one title
EOF
end
opts = GetoptLong.new(
['--help', '-h', GetoptLong::NO_ARGUMENT],
['--ratio', '-r', GetoptLong::REQUIRED_ARGUMENT],
['--with-chapters',GetoptLong::NO_ARGUMENT],
['--one-title',GetoptLong::NO_ARGUMENT]
)
options = {}
options[:files] = []
options[:ratio] = "16:9"
opts.each do |opt, arg|
case opt
when '--help'
show_usage = true
when '--ratio'
options[:ratio] = ((arg == "4:3") ? "4:3" : "16:9")
when '--with-chapters'
options[:with_chapters] = true
when '--one-title'
options[:one_title] = true
end
end
ARGV.each do |arg|
options[:files] << arg if File.file?(arg)
end
show_usage = true if ARGV.size == 0
if show_usage
puts usage()
exit()
else
do_convert(options)
end