lib/ffmpeg/transcoder.rb in streamio-ffmpeg-0.9.0 vs lib/ffmpeg/transcoder.rb in streamio-ffmpeg-1.0.0

- old
+ new

@@ -1,11 +1,11 @@ require 'open3' require 'shellwords' module FFMPEG class Transcoder - @@timeout = 200 + @@timeout = 30 def self.timeout=(time) @@timeout = time end @@ -14,91 +14,97 @@ end def initialize(movie, output_file, options = EncodingOptions.new, transcoder_options = {}) @movie = movie @output_file = output_file - + if options.is_a?(String) || options.is_a?(EncodingOptions) @raw_options = options elsif options.is_a?(Hash) @raw_options = EncodingOptions.new(options) else raise ArgumentError, "Unknown options format '#{options.class}', should be either EncodingOptions, Hash or String." end - + @transcoder_options = transcoder_options @errors = [] - + apply_transcoder_options end - - # ffmpeg < 0.8: frame= 413 fps= 48 q=31.0 size= 2139kB time=16.52 bitrate=1060.6kbits/s - # ffmpeg >= 0.8: frame= 4855 fps= 46 q=31.0 size= 45306kB time=00:02:42.28 bitrate=2287.0kbits/ - def run - command = "#{FFMPEG.ffmpeg_binary} -y -i #{Shellwords.escape(@movie.path)} #{@raw_options} #{Shellwords.escape(@output_file)}" - FFMPEG.logger.info("Running transcoding...\n#{command}\n") - output = "" - last_output = nil - Open3.popen3(command) do |stdin, stdout, stderr, wait_thr| + + def run(&block) + transcode_movie(&block) + if @transcoder_options[:validate] + validate_output_file(&block) + return encoded + else + return nil + end + end + + def encoding_succeeded? + @errors << "no output file created" and return false unless File.exists?(@output_file) + @errors << "encoded file is invalid" and return false unless encoded.valid? + true + end + + def encoded + @encoded ||= Movie.new(@output_file) + end + + private + # frame= 4855 fps= 46 q=31.0 size= 45306kB time=00:02:42.28 bitrate=2287.0kbits/ + def transcode_movie + @command = "#{FFMPEG.ffmpeg_binary} -y -i #{Shellwords.escape(@movie.path)} #{@raw_options} #{Shellwords.escape(@output_file)}" + FFMPEG.logger.info("Running transcoding...\n#{@command}\n") + @output = "" + + Open3.popen3(@command) do |stdin, stdout, stderr, wait_thr| begin yield(0.0) if block_given? next_line = Proc.new do |line| fix_encoding(line) - output << line + @output << line if line.include?("time=") if line =~ /time=(\d+):(\d+):(\d+.\d+)/ # ffmpeg 0.8 and above style time = ($1.to_i * 3600) + ($2.to_i * 60) + $3.to_f - elsif line =~ /time=(\d+.\d+)/ # ffmpeg 0.7 and below style - time = $1.to_f else # better make sure it wont blow up in case of unexpected output time = 0.0 end progress = time / @movie.duration yield(progress) if block_given? end - if line =~ /Unsupported codec/ - FFMPEG.logger.error "Failed encoding...\nCommand\n#{command}\nOutput\n#{output}\n" - raise "Failed encoding: #{line}" - end end - + if @@timeout - stderr.each_with_timeout(wait_thr.pid, @@timeout, "r", &next_line) + stderr.each_with_timeout(wait_thr.pid, @@timeout, 'size=', &next_line) else - stderr.each("r", &next_line) + stderr.each('size=', &next_line) end - + rescue Timeout::Error => e - FFMPEG.logger.error "Process hung...\nCommand\n#{command}\nOutput\n#{output}\n" - raise FFMPEG::Error, "Process hung. Full output: #{output}" + FFMPEG.logger.error "Process hung...\n@command\n#{@command}\nOutput\n#{@output}\n" + raise Error, "Process hung. Full output: #{@output}" end end + end + def validate_output_file(&block) if encoding_succeeded? yield(1.0) if block_given? FFMPEG.logger.info "Transcoding of #{@movie.path} to #{@output_file} succeeded\n" else errors = "Errors: #{@errors.join(", ")}. " - FFMPEG.logger.error "Failed encoding...\n#{command}\n\n#{output}\n#{errors}\n" - raise FFMPEG::Error, "Failed encoding.#{errors}Full output: #{output}" + FFMPEG.logger.error "Failed encoding...\n#{@command}\n\n#{@output}\n#{errors}\n" + raise Error, "Failed encoding.#{errors}Full output: #{@output}" end - - encoded end - - def encoding_succeeded? - @errors << "no output file created" and return false unless File.exists?(@output_file) - @errors << "encoded file is invalid" and return false unless encoded.valid? - true - end - - def encoded - @encoded ||= Movie.new(@output_file) - end - - private + def apply_transcoder_options + # if true runs #validate_output_file + @transcoder_options[:validate] = @transcoder_options.fetch(:validate) { true } + return if @movie.calculated_aspect_ratio.nil? case @transcoder_options[:preserve_aspect_ratio].to_s when "width" new_height = @raw_options.width / @movie.calculated_aspect_ratio new_height = new_height.ceil.even? ? new_height.ceil : new_height.floor @@ -109,10 +115,10 @@ new_width = new_width.ceil.even? ? new_width.ceil : new_width.floor new_width += 1 if new_width.odd? @raw_options[:resolution] = "#{new_width}x#{@raw_options.height}" end end - + def fix_encoding(output) output[/test/] rescue ArgumentError output.force_encoding("ISO-8859-1") end