lib/pipeline/base.rb in tracksperanto-1.8.4 vs lib/pipeline/base.rb in tracksperanto-1.9.0
- old
+ new
@@ -2,18 +2,19 @@
# applies the default middlewares and yields them for processing. Here's how a calling sequence for a pipeline looks like:
#
# pipe = Tracksperanto::Pipeline::Base.new
# pipe.progress_block = lambda{|percent, msg| puts("#{msg}..#{percent.to_i}%") }
#
-# pipe.run("/tmp/shakescript.shk", :pix_w => 720, :pix_h => 576) do | *all_middlewares |
+# pipe.run("/tmp/shakescript.shk", :width => 720, :height => 576) do | *all_middlewares |
# # configure middlewares here
# end
#
# The pipeline will also automatically allocate output files with the right extensions
# at the same place where the original file resides,
# and setup outputs for all supported export formats.
class Tracksperanto::Pipeline::Base
+ EXTENSION = /\.([^\.]+)$/ #:nodoc:
include Tracksperanto::BlockInit
# How many points have been converted. In general, the pipeline does not preserve the parsed tracker objects
# after they have been exported
@@ -29,139 +30,132 @@
attr_accessor :progress_block
# Assign an array of exporters to use them instead of the standard ones
attr_accessor :exporters
- DEFAULT_OPTIONS = {:pix_w => 720, :pix_h => 576, :parser => Tracksperanto::Import::ShakeScript }
+ DEFAULT_OPTIONS = {:width => 720, :height => 576, :parser => Tracksperanto::Import::ShakeScript }
+ # Contains arrays of the form ["MiddewareName", {:param => value}]
+ attr_accessor :middleware_tuples
+
+ def wrap_output_with_middlewares(output)
+ return output unless (middleware_tuples && middleware_tuples.any?)
+
+ middleware_tuples.reverse.inject(output) do | wrapped, (middleware_name, options) |
+ Tracksperanto.get_middleware(middleware_name).new(wrapped, options || {})
+ end
+ end
+
# Runs the whole pipeline. Accepts the following options
- # * pix_w - The comp width, for the case that the format does not support auto size
- # * pix_h - The comp height, for the case that the format does not support auto size
+ # * width - The comp width, for the case that the format does not support auto size
+ # * height - The comp height, for the case that the format does not support auto size
# * parser - The parser class, for the case that it can't be autodetected from the file name
def run(from_input_file_path, passed_options = {}) #:yields: *all_middlewares
o = DEFAULT_OPTIONS.merge(passed_options)
- pix_w, pix_h, parser_class = detect_importer_or_use_options(from_input_file_path, o)
# Reset stats
@converted_keyframes, @converted_points = 0, 0
- # Grab the input
- read_data = File.open(from_input_file_path, "rb")
-
# Assign the parser
- importer = parser_class.new(:width => pix_w, :height => pix_h)
+ importer = initialize_importer_with_path_and_options(from_input_file_path, o)
+ # Open the file
+ read_data = File.open(from_input_file_path, "rb")
+
# Setup a multiplexer
mux = setup_outputs_for(from_input_file_path)
# Setup middlewares
- middlewares = setup_middleware_chain_with(mux)
-
- # Yield middlewares to the block
- yield(*middlewares) if block_given?
-
- @converted_points, @converted_keyframes = run_export(read_data, importer, middlewares[-1])
+ endpoint = wrap_output_with_middlewares(mux)
+ @converted_points, @converted_keyframes = run_export(read_data, importer, endpoint)
end
def report_progress(percent_complete, message)
@progress_block.call(percent_complete, message) if @progress_block
end
- def detect_importer_or_use_options(path, opts)
- d = Tracksperanto::FormatDetector.new(path)
+ def initialize_importer_with_path_and_options(from_input_file_path, options)
+ d = Tracksperanto::FormatDetector.new(from_input_file_path)
if d.match? && d.auto_size?
- return [1, 1, d.importer_klass]
+ d.importer_klass.new
elsif d.match?
- raise "Width and height must be provided for a #{d.importer_klass}" unless (opts[:pix_w] && opts[:pix_h])
- opts[:parser] = d.importer_klass
+ require_dimensions_in!(opts)
+ d.importer_klass.new(:width => opts[:width], :height => opts[:height])
else
raise "Cannot autodetect the file format - please specify the importer explicitly" unless opts[:parser]
- unless opts[:parser].autodetects_size?
- raise "Width and height must be provided for this importer" unless (opts[:pix_w] && opts[:pix_h])
- end
+ klass = Tracksperanto.get_exporter(opts[:parser])
+ require_dimensions_in!(opts) unless klass.autodetects_size?
+ klass.new(:width => opts[:width], :height => opts[:height])
end
-
- [opts[:pix_w], opts[:pix_h], opts[:parser]]
end
+ def require_dimensions_in!(opts)
+ raise "Width and height must be provided for this importer" unless (opts[:width] && opts[:height])
+ end
+
# Runs the export and returns the number of points and keyframes processed.
# If a block is passed, the block will receive the percent complete and the last
# status message that you can pass back to the UI
- def run_export(tracker_data_io, parser, processor)
+ def run_export(tracker_data_io, parser, exporter)
points, keyframes, percent_complete = 0, 0, 0.0
-
+
report_progress(percent_complete, "Starting the parser")
-
+
# Report progress from the parser
parser.progress_block = lambda { | m | report_progress(percent_complete, m) }
-
+
# Wrap the input in a progressive IO, setup a lambda that will spy on the reader and
# update the percentage. We will only broadcast messages that come from the parser
# though (complementing it with a percentage)
io_with_progress = Tracksperanto::ProgressiveIO.new(tracker_data_io) do | offset, of_total |
percent_complete = (50.0 / of_total) * offset
end
@ios << io_with_progress
-
+
trackers = parser.parse(io_with_progress)
-
+
report_progress(percent_complete = 50.0, "Validating #{trackers.length} imported trackers")
-
+
validate_trackers!(trackers)
-
+
report_progress(percent_complete, "Starting export")
-
+
percent_per_tracker = (100.0 - percent_complete) / trackers.length
-
+
# Use the width and height provided by the parser itself
- processor.start_export(parser.width, parser.height)
+ exporter.start_export(parser.width, parser.height)
trackers.each_with_index do | t, tracker_idx |
kf_weight = percent_per_tracker / t.keyframes.length
points += 1
- processor.start_tracker_segment(t.name)
+ exporter.start_tracker_segment(t.name)
t.each_with_index do | kf, idx |
keyframes += 1
- processor.export_point(kf.frame, kf.abs_x, kf.abs_y, kf.residual)
+ exporter.export_point(kf.frame, kf.abs_x, kf.abs_y, kf.residual)
report_progress(percent_complete += kf_weight, "Writing keyframe #{idx+1} of #{t.name.inspect}, #{trackers.length - tracker_idx} trackers to go")
end
- processor.end_tracker_segment
+ exporter.end_tracker_segment
end
- processor.end_export
-
+ exporter.end_export
+
report_progress(100.0, "Wrote #{points} points and #{keyframes} keyframes")
-
+
[points, keyframes]
ensure
@ios.reject!{|e| e.close unless (!e.respond_to?(:closed?) || e.closed?) } if @ios
end
# Setup output files and return a single output
# that replays to all of them
def setup_outputs_for(input_file_path)
- file_name = File.basename(input_file_path).gsub(/\.([^\.]+)$/, '')
+ file_name = File.basename(input_file_path).gsub(EXTENSION, '')
export_klasses = exporters || Tracksperanto.exporters
Tracksperanto::Export::Mux.new(
export_klasses.map do | exporter_class |
export_name = [file_name, exporter_class.desc_and_extension].join("_")
export_path = File.join(File.dirname(input_file_path), export_name)
exporter = exporter_class.new(open_owned_export_file(export_path))
end
)
- end
-
- # Setup and return the output multiplexor wrapped in all necessary middlewares.
- # Middlewares must be returned in an array to be passed to the configuration block
- # afterwards
- def setup_middleware_chain_with(output)
- scaler = Tracksperanto::Middleware::Scaler.new(output)
- slipper = Tracksperanto::Middleware::Slipper.new(scaler)
- golden = Tracksperanto::Middleware::Golden.new(slipper)
- reformat = Tracksperanto::Middleware::Reformat.new(golden)
- shift = Tracksperanto::Middleware::Shift.new(reformat)
- prefix = Tracksperanto::Middleware::Prefix.new(shift)
- lerp = Tracksperanto::Middleware::Lerp.new(prefix)
- lengate = Tracksperanto::Middleware::LengthCutoff.new(lerp)
- [scaler, slipper, golden, reformat, shift, prefix, lerp, lengate]
end
# Open the file for writing and register it to be closed automatically
def open_owned_export_file(path_to_file)
@ios ||= []
\ No newline at end of file