twinge-rvideo 0.9.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (68) hide show
  1. data/CHANGELOG +70 -0
  2. data/ENV +100 -0
  3. data/ENV2 +129 -0
  4. data/LICENSE +20 -0
  5. data/Manifest +67 -0
  6. data/README +91 -0
  7. data/RULES +11 -0
  8. data/Rakefile +63 -0
  9. data/config/boot.rb +25 -0
  10. data/lib/rvideo.rb +44 -0
  11. data/lib/rvideo/errors.rb +24 -0
  12. data/lib/rvideo/float.rb +7 -0
  13. data/lib/rvideo/frame_capturer.rb +126 -0
  14. data/lib/rvideo/inspector.rb +481 -0
  15. data/lib/rvideo/reporter.rb +176 -0
  16. data/lib/rvideo/reporter/views/index.html.erb +27 -0
  17. data/lib/rvideo/reporter/views/report.css +27 -0
  18. data/lib/rvideo/reporter/views/report.html.erb +81 -0
  19. data/lib/rvideo/reporter/views/report.js +9 -0
  20. data/lib/rvideo/string.rb +5 -0
  21. data/lib/rvideo/tools/abstract_tool.rb +401 -0
  22. data/lib/rvideo/tools/ffmpeg.rb +277 -0
  23. data/lib/rvideo/tools/ffmpeg2theora.rb +42 -0
  24. data/lib/rvideo/tools/flvtool2.rb +50 -0
  25. data/lib/rvideo/tools/mencoder.rb +103 -0
  26. data/lib/rvideo/tools/mp4box.rb +21 -0
  27. data/lib/rvideo/tools/mp4creator.rb +35 -0
  28. data/lib/rvideo/tools/mplayer.rb +31 -0
  29. data/lib/rvideo/tools/qtfaststart.rb +37 -0
  30. data/lib/rvideo/tools/yamdi.rb +44 -0
  31. data/lib/rvideo/transcoder.rb +120 -0
  32. data/lib/rvideo/version.rb +9 -0
  33. data/rvideo.gemspec +37 -0
  34. data/scripts/txt2html +67 -0
  35. data/setup.rb +1585 -0
  36. data/spec/files/boat.avi +0 -0
  37. data/spec/files/kites.mp4 +0 -0
  38. data/spec/fixtures/ffmpeg_builds.yml +28 -0
  39. data/spec/fixtures/ffmpeg_results.yml +608 -0
  40. data/spec/fixtures/files.yml +398 -0
  41. data/spec/fixtures/recipes.yml +58 -0
  42. data/spec/integrations/formats_spec.rb +315 -0
  43. data/spec/integrations/frame_capturer_spec.rb +26 -0
  44. data/spec/integrations/inspection_spec.rb +112 -0
  45. data/spec/integrations/recipes_spec.rb +0 -0
  46. data/spec/integrations/rvideo_spec.rb +17 -0
  47. data/spec/integrations/transcoder_integration_spec.rb +29 -0
  48. data/spec/integrations/transcoding_spec.rb +9 -0
  49. data/spec/spec.opts +1 -0
  50. data/spec/spec_helper.rb +16 -0
  51. data/spec/support.rb +36 -0
  52. data/spec/units/abstract_tool_spec.rb +111 -0
  53. data/spec/units/ffmpeg_spec.rb +323 -0
  54. data/spec/units/flvtool2_spec.rb +324 -0
  55. data/spec/units/frame_capturer_spec.rb +72 -0
  56. data/spec/units/inspector_spec.rb +59 -0
  57. data/spec/units/mencoder_spec.rb +4994 -0
  58. data/spec/units/mp4box_spec.rb +34 -0
  59. data/spec/units/mp4creator_spec.rb +34 -0
  60. data/spec/units/mplayer_spec.rb +34 -0
  61. data/spec/units/qtfaststart_spec.rb +35 -0
  62. data/spec/units/string_spec.rb +8 -0
  63. data/spec/units/transcoder_spec.rb +156 -0
  64. data/tasks/deployment.rake +5 -0
  65. data/tasks/testing.rake +27 -0
  66. data/tasks/transcoding.rake +40 -0
  67. data/tasks/website.rake +8 -0
  68. metadata +175 -0
@@ -0,0 +1,176 @@
1
+ require 'erb'
2
+
3
+ module RVideo
4
+ class Reporter
5
+ include ERB::Util
6
+
7
+ def self.run
8
+ Reporter.new.run
9
+ end
10
+
11
+ def run
12
+ @current_report_path = Reporter.next_available_report_path(File.join(REPORT_PATH, 'generated_reports'))
13
+ files = available_files
14
+ recipes = available_recipes
15
+
16
+ puts "\nInput files:\n--#{files.collect { |file| File.basename(file) }.join("\n--")}"
17
+ puts "\nInput recipes:\n--#{recipes.map {|name, recipe| name }.join("\n--")}"
18
+ combinations = calculate_combinations_using recipes, files
19
+ results = mass_transcode combinations
20
+ build_report_from results
21
+ puts "Done! Report available at #{@current_report_path}"
22
+ puts "Launching report in browser..."
23
+ exec "open #{@current_report_path}/index.html"
24
+ end
25
+
26
+ private
27
+
28
+ def self.next_available_report_path(base_path)
29
+ ordered_reports = Dir[File.join(base_path, "*")].sort_by {|name| File.basename(name).to_i }
30
+ ordered_reports = ["0"] if ordered_reports.empty?
31
+ last_report = File.basename(ordered_reports.last)
32
+ new_report_name = (last_report.to_i + 1).to_s
33
+ new_dir = File.join(base_path, new_report_name)
34
+ FileUtils.mkdir_p(new_dir)
35
+ new_dir
36
+ end
37
+
38
+ def available_recipes
39
+ recipes = []
40
+ recipe_files = Dir[File.join(REPORT_PATH, "*.yml")]
41
+ recipe_files.each do |recipe_file|
42
+ YAML.load_file(recipe_file).each { |recipe| recipes << recipe }
43
+ end
44
+ if recipes.empty?
45
+ puts "No recipes found. Add recipe YAML files to report/."
46
+ exit
47
+ else
48
+ recipes
49
+ end
50
+ end
51
+
52
+ def available_files
53
+ files = Dir[File.join(REPORT_PATH, "files/input/*.*")]
54
+ if files.empty?
55
+ puts "No input files. Add files to report/files/input to test."
56
+ exit
57
+ else
58
+ files
59
+ end
60
+ end
61
+
62
+ def calculate_combinations_using(recipes, files)
63
+ @combinations = {}
64
+ files.each { |file| @combinations[file] = recipes }
65
+ @combinations
66
+ end
67
+
68
+ def build_report_from(results, options = nil)
69
+ @results = results
70
+ #build main report
71
+ report = load_view 'index'
72
+ full_report_path = File.join(@current_report_path, "index.html")
73
+ File.open(full_report_path, "w+") do |file|
74
+ file.write report
75
+ end
76
+ #build individual reports
77
+ @results.each do |input_file, recipes|
78
+ recipes.each do |recipe_name, result|
79
+ build_individual_report(input_file, recipe_name, result)
80
+ end
81
+ end
82
+ end
83
+
84
+ def build_individual_report(input_file, recipe_name, result)
85
+ #instance variables may no longer be necessary...
86
+ @input_file = input_file
87
+ @recipe_name = recipe_name
88
+ @result = result
89
+ individual_report = load_view 'report'
90
+ individual_report_name = "#{underscoreize_file_basename(input_file)}_#{recipe_name}.html"
91
+ File.makedirs(File.join(@current_report_path, "individual_reports"))
92
+ full_report_path = File.join(@current_report_path, "individual_reports", individual_report_name)
93
+ File.open(full_report_path, "w+") do |file|
94
+ file.write individual_report
95
+ end
96
+ end
97
+
98
+
99
+ def load_view(template_name)
100
+ template_file = "#{File.dirname(__FILE__)}/reporter/views/#{template_name}.html.erb"
101
+ template = File.read(template_file).gsub(/^ /, '')
102
+ ERB.new(template).result(binding)
103
+ end
104
+
105
+ def mass_transcode(combinations)
106
+ results = {}
107
+ combinations.each do |file, recipes|
108
+ results[file] = {}
109
+ recipes.each do |recipe_name, recipe|
110
+ puts "Transcoding #{File.basename(file)} using recipe #{recipe_name}"
111
+
112
+ #generate input/output file paths
113
+ input_file = File.expand_path(file)
114
+ output_file = generate_output_file_using input_file, recipe_name, recipe
115
+ #raise output_file
116
+ #input_file.gsub!(" ","\\ ")
117
+ input_file = "#{File.dirname(input_file)}/\"#{File.basename(input_file)}\""
118
+
119
+ #create logfile
120
+ log_file_name = underscoreize_file_basename(input_file) + "_" + recipe_name + ".log"
121
+ log_file = create_log_file(log_file_name)
122
+ RVideo.logger = Logger.new(log_file)
123
+
124
+ transcoder, errors = transcode(recipe, input_file, output_file)
125
+
126
+ #build the results object for the views
127
+ results[file][recipe_name] = {}
128
+ results[file][recipe_name]['output_file'] = output_file
129
+ results[file][recipe_name]['transcoder'] = transcoder
130
+ results[file][recipe_name]['errors'] = errors
131
+ results[file][recipe_name]['recipe'] = recipe
132
+ results[file][recipe_name]['log'] = log_file
133
+ end
134
+ end
135
+ return results
136
+ end
137
+
138
+ def generate_output_file_using(selected_file, recipe_name, recipe)
139
+ #File.join(@current_report_path, 'output_files')
140
+ output_path = File.join(@current_report_path, 'output_files' + underscoreize_file_basename(selected_file))
141
+ File.makedirs output_path
142
+ output_filename = "#{recipe_name}.#{recipe['extension']}"
143
+ output_file = File.join(output_path, output_filename)
144
+ #output_file.gsub(" ","_")
145
+ end
146
+
147
+ def underscoreize_file_basename(file)
148
+ File.basename(file).gsub(".","_").gsub(" ","_")
149
+ end
150
+
151
+ def transcode(recipe, input_file, output_file)
152
+ command = recipe['command']
153
+ errors = nil
154
+
155
+ #RVideo::Transcoder.logger = Logger.new(STDOUT)
156
+ begin
157
+ transcoder = RVideo::Transcoder.new
158
+ transcoder.execute(command, {:input_file => input_file,
159
+ :output_file => output_file})
160
+ #rescue => errors
161
+ end
162
+
163
+ return transcoder, errors
164
+ end
165
+
166
+ def create_log_file(log_file_name)
167
+ log_path = File.join(@current_report_path, "logs")
168
+ File.makedirs log_path
169
+ logfile = File.join(log_path, log_file_name)
170
+ File.open(logfile, "w+") { |file| }
171
+ logfile
172
+ end
173
+
174
+ end
175
+
176
+ end
@@ -0,0 +1,27 @@
1
+ <html>
2
+ <head>
3
+ <title>RVideo Reports Index</title>
4
+ <link rel="stylesheet" href="report.css" type="text/css" media="screen" />
5
+ <script type="text/javascript" src="report.js"></script>
6
+ </head>
7
+ <body>
8
+ <% @results.each do |input_file, recipes| %>
9
+ <h1>
10
+ <a href="<%= input_file %>"><%= File.basename(input_file) %> (launch file)</a>
11
+ </h1>
12
+ <ol>
13
+ <% recipes.each do |recipe_name, result| %>
14
+ <li>
15
+ <% css_class = 'warning' unless result['transcoder'].errors.empty? %>
16
+ <% css_class = 'critical' if result['errors'] %>
17
+ <% css_class = 'passed' if css_class.nil? %>
18
+ <div class="<%= css_class %>">
19
+ <% individual_report_url = "individual_reports/" + underscoreize_file_basename(input_file) + "_" + recipe_name + ".html" %>
20
+ <h2><a href="<%= result['output_file'] %>">Launch <%= recipe_name %></a> <a class="view-report" href="<%= individual_report_url %>" >view full report</a></h2>
21
+ </div>
22
+ </li>
23
+ <% end %>
24
+ </ol>
25
+ <% end %>
26
+ </body>
27
+ </html>
@@ -0,0 +1,27 @@
1
+ /* a { color: black; margin-left: 20px;}
2
+ a:visited { color: #111; }
3
+ .critical { background-color: #E6E6E6; border-left: 20px solid #F00; }
4
+ .warning { background-color: #E6E6E6; border-left: 20px solid orange; }
5
+ .passed { background-color: #E6E6E6; border-left: 20px solid #0F0; }
6
+
7
+ div { margin: 10px; border: 1px solid #ccc; padding: 5px; }
8
+ li { margin: 20px; padding: 5px; list-style-type: none; }
9
+ span { background-color: #ccc; padding: 5px; width: 500px;}
10
+ span:hover { cursor: pointer; text-decoration: underline; }
11
+ */
12
+
13
+ html { font-size: .75em;}
14
+ div { font-size: 1.2em;}
15
+ h1, h2, h3, h4, h5, ul { margin: .5em; }
16
+ a { color: black; margin-left: 2em; padding: 1em;}
17
+ a:visited { color: #111; }
18
+ .critical, .warning, .passed { background-color: #E6E6E6; border-left: 5em solid; }
19
+ .critical { border-left-color: #F00; }
20
+ .warning { border-left-color: orange; }
21
+ .passed { border-left-color: #0F0; }
22
+
23
+ div { margin: 1em; border: 1px solid #ccc; padding: .5em; }
24
+ ol li { margin: .01em; padding: 0; margin-left: 5em; }
25
+ ul li { margin: 1em; padding: 1em; list-style-type: none; }
26
+ span { background-color: #ccc; padding: 1em; width: 500px;}
27
+ span:hover { cursor: pointer; text-decoration: underline; }
@@ -0,0 +1,81 @@
1
+ <html>
2
+ <head>
3
+ <title>RVideo Reports</title>
4
+ <link rel="stylesheet" href="<%= report.css %>" type="text/css" media="screen" />
5
+ <script type="text/javascript" src="<%= report.js %>"></script>
6
+ </head>
7
+ <body>
8
+ <div>
9
+ <h2><a href="<%= @result['output_file'] %>">Launch output file</a></h2>
10
+ <ul>
11
+ <li>
12
+ <div>
13
+ <h2>Recipe: <%= @recipe_name %></h2>
14
+ <p>
15
+ <% @result['recipe'].each do |key, value|%>
16
+ <%= key %>: <%= value %><br />
17
+ <% end %>
18
+ </p>
19
+ </div>
20
+ </li>
21
+ <% unless @result['errors'].nil? %>
22
+ <li>
23
+ <span onclick="toggle('rescued-errors');">Hide/Show Rescued Errors</span>
24
+ <div id='rescued-errors' style="display: none;">
25
+ <h2>Rescued Error Backtrace</h2>
26
+ <h4><%= h(@result['errors'].class.name) %></h4>
27
+ <p><%= h(@result['errors'].message)%>
28
+ <p><%= h(@result['errors'].backtrace) %></p>
29
+ </div>
30
+ </li>
31
+ <% end %>
32
+ <li>
33
+ <span onclick="toggle('transcoder');">Hide/Show Transcoder</span>
34
+ <div id='transcoder' style="display: none;">
35
+ <h2>Transcoder</h2>
36
+ <% unless @result['transcoder'].errors.empty? %>
37
+ <h3>Transcoder Errors</h3>
38
+ <p><%= h(@result['transcoder'].errors.inspect) %></p>
39
+ <% end %>
40
+ <h3>Executed Commands</h3>
41
+ <p><%= h(@result['transcoder'].executed_commands.map(&:command)) %></p>
42
+ <h3>Raw Meta</h3>
43
+ <p><%= h(@result['transcoder'].metadata) %></p>
44
+ </div>
45
+ <li>
46
+ <span onclick="toggle('input-file');">Hide/Show Input File</span>
47
+ <div id='input-file' style="display: none;">
48
+ <h2>Original Input File</h2>
49
+ <h3>Raw Metadata</h3>
50
+ <p><%= h(@result['transcoder'].original.raw_metadata) %></p>
51
+ <h3>Raw Response</h3>
52
+ <p><%= h(@result['transcoder'].original.raw_response) %></p>
53
+ </div>
54
+ </li>
55
+ <% if @result['transcoder'].processed %>
56
+ <li>
57
+ <span onclick="toggle('output-file');">Hide/Show Output File</span>
58
+ <div id='output-file' style="display: none;">
59
+ <h2>Processed Output File</h2>
60
+ <h3>Raw Metadata</h3>
61
+ <p><%= h(@result['transcoder'].processed.raw_metadata) %></p>
62
+ <h3>Raw Response</h3>
63
+ <p><%= h(@result['transcoder'].processed.raw_response) %></p>
64
+ </div>
65
+ </li>
66
+ <% end %>
67
+ <li>
68
+ <span onclick="toggle('log');">Hide/Show Log</span>
69
+ <div id='log' style="display: none;">
70
+ <h2>Log</h2>
71
+ <p>
72
+ <% File.readlines(@result['log']).each do |line| %>
73
+ <%= line %> <br />
74
+ <% end %>
75
+ </p>
76
+ </div>
77
+ </li>
78
+ </ul>
79
+ </div>
80
+ </body>
81
+ </html>
@@ -0,0 +1,9 @@
1
+ function toggle(obj) {
2
+ var el = document.getElementById(obj);
3
+ if ( el.style.display != 'none' ) {
4
+ el.style.display = 'none';
5
+ }
6
+ else {
7
+ el.style.display = '';
8
+ }
9
+ }
@@ -0,0 +1,5 @@
1
+ class ::String
2
+ def shell_quoted
3
+ "'" << self << "'"
4
+ end
5
+ end
@@ -0,0 +1,401 @@
1
+ module RVideo # :nodoc:
2
+ module Tools # :nodoc:
3
+
4
+ # AbstractTool is an interface to every transcoder tool class
5
+ # (e.g. ffmpeg, flvtool2). Called by the Transcoder class.
6
+ class AbstractTool
7
+
8
+ def self.assign(cmd, options = {})
9
+ tool_name = cmd.split(" ").first
10
+ begin
11
+ tool = "RVideo::Tools::#{tool_name.underscore.classify}".constantize.send(:new, cmd, options)
12
+ # rescue NameError, /uninitialized constant/
13
+ # raise TranscoderError::UnknownTool, "The recipe tried to use the '#{tool_name}' tool, which does not exist."
14
+ rescue => e
15
+ RVideo.logger.info e.message
16
+ RVideo.logger.info e.backtrace.join("\n")
17
+ raise e
18
+ end
19
+ end
20
+
21
+
22
+ module InstanceMethods
23
+ # Defines abstract methods in the convention of "format_#{attribute}"
24
+ # which are meant to be redefined by classes including this behavior.
25
+ def self.abstract_attribute_formatter(*names)
26
+ names.map { |n| "format_#{n}" }.each do |name|
27
+ class_eval %{
28
+ def #{name}(params = {})
29
+ raise ParameterError,
30
+ "The #{self.class} tool has not implemented the :#{name} method."
31
+ end
32
+ }, __FILE__, __LINE__
33
+ end
34
+ end
35
+
36
+ abstract_attribute_formatter :resolution, :deinterlace, :fps,
37
+ :video_bit_rate, :video_bit_rate_tolerance,
38
+ :video_bit_rate_min, :video_bit_rate_max,
39
+ :audio_channels, :audio_bit_rate, :audio_sample_rate
40
+
41
+ ###
42
+
43
+ attr_reader :options, :command, :raw_result
44
+ attr_writer :original
45
+
46
+ def initialize(raw_command, options = {})
47
+ @raw_command = raw_command
48
+ @options = HashWithIndifferentAccess.new(options)
49
+ @command = interpolate_variables(raw_command)
50
+ end
51
+
52
+ def execute
53
+ @output_params = {}
54
+
55
+ # Dump the log output into a temp file
56
+ log_temp_file_name = "/tmp/transcode_output_#{Time.now.to_i}.txt"
57
+
58
+ final_command = "#{@command} 2>#{log_temp_file_name}"
59
+ RVideo.logger.info("\nExecuting Command: #{final_command}\n")
60
+ do_execute final_command
61
+
62
+ populate_raw_result(log_temp_file_name)
63
+
64
+ RVideo.logger.info("Result: \n#{@raw_result}")
65
+ parse_result(@raw_result)
66
+
67
+ # Cleanup log file
68
+ begin
69
+ File.delete(log_temp_file_name)
70
+ rescue Exception => e
71
+ RVideo.logger.error("Failed to delete output log file: #{log_temp_file_name}, e=#{e}")
72
+ end
73
+ end
74
+
75
+ # Wrapper around the system call, for whenever we need to
76
+ # hook on or redefine this without messing with Kernel
77
+ def do_execute(command)
78
+ system command
79
+ end
80
+
81
+ #
82
+ # Magic parameters
83
+ #
84
+ def temp_dir
85
+ if @options['output_file']
86
+ "#{File.dirname(@options['output_file'])}/"
87
+ else
88
+ ""
89
+ end
90
+ end
91
+
92
+ ###
93
+ # FPS aka framerate
94
+
95
+ def fps
96
+ format_fps(get_fps)
97
+ end
98
+
99
+ def get_fps
100
+ inspect_original if @original.nil?
101
+ fps = @options['fps'] || ""
102
+ case fps
103
+ when "copy"
104
+ get_original_fps
105
+ else
106
+ get_specific_fps
107
+ end
108
+ end
109
+
110
+ def get_original_fps
111
+ return {} if @original.fps.nil?
112
+ { :fps => @original.fps }
113
+ end
114
+
115
+ def get_specific_fps
116
+ { :fps => @options['fps'] }
117
+ end
118
+
119
+ ###
120
+ # Resolution
121
+
122
+ def deinterlace
123
+ format_deinterlace(get_deinterlace)
124
+ end
125
+
126
+ def get_deinterlace
127
+ { :deinterlace => @options['deinterlace'] ? true : false }
128
+ end
129
+
130
+ def resolution
131
+ format_resolution(get_resolution)
132
+ end
133
+
134
+ def get_resolution
135
+ inspect_original if @original.nil?
136
+
137
+ case @options['resolution']
138
+ when "copy" then get_original_resolution
139
+ when "width" then get_fit_to_width_resolution
140
+ when "height" then get_fit_to_height_resolution
141
+ when "letterbox" then get_letterbox_resolution
142
+ else
143
+ if @options["width"] and not @options["height"]
144
+ get_fit_to_width_resolution
145
+ elsif @options["height"] and not @options["width"]
146
+ get_fit_to_height_resolution
147
+ elsif @options["width"] and @options["height"]
148
+ get_specific_resolution
149
+ else
150
+ get_original_resolution
151
+ end
152
+ end
153
+ end
154
+
155
+ def get_fit_to_width_resolution
156
+ w = @options['width']
157
+
158
+ raise TranscoderError::ParameterError,
159
+ "invalid width of '#{w}' for fit to width" unless valid_dimension?(w)
160
+
161
+ h = calculate_height(@original.width, @original.height, w)
162
+
163
+ { :scale => { :width => w, :height => h } }
164
+ end
165
+
166
+ def get_fit_to_height_resolution
167
+ h = @options['height']
168
+
169
+ raise TranscoderError::ParameterError,
170
+ "invalid height of '#{h}' for fit to height" unless valid_dimension?(h)
171
+
172
+ w = calculate_width(@original.width, @original.height, h)
173
+
174
+ { :scale => { :width => w, :height => h } }
175
+ end
176
+
177
+ def get_letterbox_resolution
178
+ lw = @options['width'].to_i
179
+ lh = @options['height'].to_i
180
+
181
+ raise TranscoderError::ParameterError,
182
+ "invalid width of '#{lw}' for letterbox" unless valid_dimension?(lw)
183
+ raise TranscoderError::ParameterError,
184
+ "invalid height of '#{lh}' for letterbox" unless valid_dimension?(lh)
185
+
186
+ w = calculate_width(@original.width, @original.height, lh)
187
+ h = calculate_height(@original.width, @original.height, lw)
188
+
189
+ if w > lw
190
+ w = lw
191
+ h = calculate_height(@original.width, @original.height, lw)
192
+ else
193
+ h = lh
194
+ w = calculate_width(@original.width, @original.height, lh)
195
+ end
196
+
197
+ { :scale => { :width => w, :height => h },
198
+ :letterbox => { :width => lw, :height => lh } }
199
+ end
200
+
201
+ def get_original_resolution
202
+ { :scale => { :width => @original.width, :height => @original.height } }
203
+ end
204
+
205
+ def get_specific_resolution
206
+ w = @options['width']
207
+ h = @options['height']
208
+
209
+ raise TranscoderError::ParameterError,
210
+ "invalid width of '#{w}' for specific resolution" unless valid_dimension?(w)
211
+ raise TranscoderError::ParameterError,
212
+ "invalid height of '#{h}' for specific resolution" unless valid_dimension?(h)
213
+
214
+ { :scale => { :width => w, :height => h } }
215
+ end
216
+
217
+ def calculate_width(ow, oh, h)
218
+ w = ((ow.to_f / oh.to_f) * h.to_f).to_i
219
+ (w.to_f / 16).round * 16
220
+ end
221
+
222
+ def calculate_height(ow, oh, w)
223
+ h = (w.to_f / (ow.to_f / oh.to_f)).to_i
224
+ (h.to_f / 16).round * 16
225
+ end
226
+
227
+ def valid_dimension?(dim)
228
+ dim.to_i > 0
229
+ end
230
+
231
+ ###
232
+ # Audio channels
233
+
234
+ def audio_channels
235
+ format_audio_channels(get_audio_channels)
236
+ end
237
+
238
+ def get_audio_channels
239
+ channels = @options['audio_channels'] || ""
240
+ case channels
241
+ when "stereo"
242
+ get_stereo_audio
243
+ when "mono"
244
+ get_mono_audio
245
+ else
246
+ {}
247
+ end
248
+ end
249
+
250
+ def get_stereo_audio
251
+ { :channels => "2" }
252
+ end
253
+
254
+ def get_mono_audio
255
+ { :channels => "1" }
256
+ end
257
+
258
+ def get_specific_audio_bit_rate
259
+ { :bit_rate => @options['audio_bit_rate'] }
260
+ end
261
+
262
+ def get_specific_audio_sample_rate
263
+ { :sample_rate => @options['audio_sample_rate'] }
264
+ end
265
+
266
+ ###
267
+ # Audio bit rate
268
+
269
+ def audio_bit_rate
270
+ format_audio_bit_rate(get_audio_bit_rate)
271
+ end
272
+
273
+ def get_audio_bit_rate
274
+ bit_rate = @options['audio_bit_rate'] || ""
275
+ case bit_rate
276
+ when ""
277
+ {}
278
+ else
279
+ get_specific_audio_bit_rate
280
+ end
281
+ end
282
+
283
+ ###
284
+ # Audio sample rate
285
+
286
+ def audio_sample_rate
287
+ format_audio_sample_rate(get_audio_sample_rate)
288
+ end
289
+
290
+ def get_audio_sample_rate
291
+ sample_rate = @options['audio_sample_rate'] || ""
292
+ case sample_rate
293
+ when ""
294
+ {}
295
+ else
296
+ get_specific_audio_sample_rate
297
+ end
298
+ end
299
+
300
+ ###
301
+ # Video quality
302
+
303
+ def video_quality
304
+ format_video_quality(get_video_quality)
305
+ end
306
+
307
+ def get_video_quality
308
+ quality = @options['video_quality'] || 'medium'
309
+
310
+ { :video_quality => quality }.
311
+ merge!(get_fps).
312
+ merge!(get_resolution).
313
+ merge!(get_video_bit_rate)
314
+ end
315
+
316
+ def video_bit_rate
317
+ format_video_bit_rate(get_video_bit_rate)
318
+ end
319
+
320
+ def get_video_bit_rate
321
+ { :video_bit_rate => @options["video_bit_rate"] }
322
+ end
323
+
324
+ def video_bit_rate_tolerance
325
+ format_video_bit_rate_tolerance(get_video_bit_rate_tolerance)
326
+ end
327
+
328
+ def get_video_bit_rate_tolerance
329
+ { :video_bit_rate_tolerance => @options["video_bit_rate_tolerance"] }
330
+ end
331
+
332
+ def video_bit_rate_min
333
+ format_video_bit_rate_min(get_video_bit_rate_min)
334
+ end
335
+
336
+ def get_video_bit_rate_min
337
+ { :video_bit_rate_min => @options["video_bit_rate_min"] }
338
+ end
339
+
340
+ def video_bit_rate_max
341
+ format_video_bit_rate_max(get_video_bit_rate_max)
342
+ end
343
+
344
+ def get_video_bit_rate_max
345
+ { :video_bit_rate_max => @options["video_bit_rate_max"] }
346
+ end
347
+
348
+ private
349
+
350
+ VARIABLE_INTERPOLATION_SCAN_PATTERN = /[^\\]\$[-_a-zA-Z]+\$/
351
+
352
+ def interpolate_variables(raw_command)
353
+ raw_command.scan(VARIABLE_INTERPOLATION_SCAN_PATTERN).each do |match|
354
+ match = match[0..0] == "$" ? match : match[1..(match.size - 1)]
355
+ match.strip!
356
+
357
+ value = if ["$input_file$", "$output_file$"].include?(match)
358
+ matched_variable(match).to_s.shell_quoted
359
+ else
360
+ matched_variable(match).to_s
361
+ end
362
+
363
+ raw_command.gsub!(match, value)
364
+ end
365
+ raw_command.gsub("\\$", "$")
366
+ end
367
+
368
+ #
369
+ # Strip the $s. First, look for a supplied option that matches the
370
+ # variable name. If one is not found, look for a method that matches.
371
+ # If not found, raise ParameterError exception.
372
+ #
373
+
374
+ def matched_variable(match)
375
+ variable_name = match.gsub("$","")
376
+ if self.respond_to? variable_name
377
+ self.send(variable_name)
378
+ elsif @options.key?(variable_name)
379
+ @options[variable_name]
380
+ else
381
+ raise TranscoderError::ParameterError,
382
+ "command is looking for the #{variable_name} parameter, but it was not provided. (Command: #{@raw_command})"
383
+ end
384
+ end
385
+
386
+
387
+ def inspect_original
388
+ @original = Inspector.new(:file => options[:input_file])
389
+ end
390
+
391
+ # Pulls the interesting bits of the temp log file into memory. This is fairly tool-specific, so
392
+ # it's doubtful that this default version is going to work without being overridded.
393
+ def populate_raw_result(temp_file_name)
394
+ @raw_result = `tail -n 500 #{temp_file_name}`
395
+ end
396
+
397
+ end # InstanceMethods
398
+ end
399
+
400
+ end
401
+ end