newbamboo-rvideo 0.9.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (69) 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 +68 -0
  6. data/README +106 -0
  7. data/RULES +11 -0
  8. data/Rakefile +63 -0
  9. data/config/boot.rb +25 -0
  10. data/lib/rvideo.rb +46 -0
  11. data/lib/rvideo/errors.rb +24 -0
  12. data/lib/rvideo/float.rb +7 -0
  13. data/lib/rvideo/frame_capturer.rb +127 -0
  14. data/lib/rvideo/inspector.rb +482 -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 +406 -0
  22. data/lib/rvideo/tools/ffmpeg.rb +356 -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/segmenter.rb +21 -0
  31. data/lib/rvideo/tools/yamdi.rb +44 -0
  32. data/lib/rvideo/transcoder.rb +139 -0
  33. data/lib/rvideo/version.rb +9 -0
  34. data/rvideo.gemspec +36 -0
  35. data/scripts/txt2html +67 -0
  36. data/setup.rb +1585 -0
  37. data/spec/files/boat.avi +0 -0
  38. data/spec/files/kites.mp4 +0 -0
  39. data/spec/fixtures/ffmpeg_builds.yml +28 -0
  40. data/spec/fixtures/ffmpeg_results.yml +608 -0
  41. data/spec/fixtures/files.yml +398 -0
  42. data/spec/fixtures/recipes.yml +58 -0
  43. data/spec/integrations/formats_spec.rb +315 -0
  44. data/spec/integrations/frame_capturer_spec.rb +26 -0
  45. data/spec/integrations/inspection_spec.rb +112 -0
  46. data/spec/integrations/recipes_spec.rb +0 -0
  47. data/spec/integrations/rvideo_spec.rb +17 -0
  48. data/spec/integrations/transcoder_integration_spec.rb +29 -0
  49. data/spec/integrations/transcoding_spec.rb +9 -0
  50. data/spec/spec.opts +1 -0
  51. data/spec/spec_helper.rb +16 -0
  52. data/spec/support.rb +36 -0
  53. data/spec/units/abstract_tool_spec.rb +111 -0
  54. data/spec/units/ffmpeg_spec.rb +323 -0
  55. data/spec/units/flvtool2_spec.rb +324 -0
  56. data/spec/units/frame_capturer_spec.rb +72 -0
  57. data/spec/units/inspector_spec.rb +59 -0
  58. data/spec/units/mencoder_spec.rb +4994 -0
  59. data/spec/units/mp4box_spec.rb +34 -0
  60. data/spec/units/mp4creator_spec.rb +34 -0
  61. data/spec/units/mplayer_spec.rb +34 -0
  62. data/spec/units/qtfaststart_spec.rb +35 -0
  63. data/spec/units/string_spec.rb +8 -0
  64. data/spec/units/transcoder_spec.rb +156 -0
  65. data/tasks/deployment.rake +5 -0
  66. data/tasks/testing.rake +27 -0
  67. data/tasks/transcoding.rake +40 -0
  68. data/tasks/website.rake +8 -0
  69. metadata +179 -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,406 @@
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, :progress
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
+ if block_given? and self.respond_to?(:execute_with_progress)
55
+ execute_with_progress do |progress|
56
+ yield progress
57
+ end
58
+ else
59
+ # Dump the log output into a temp file
60
+ log_temp_file_name = "/tmp/transcode_output_#{Time.now.to_i}_#{rand(100)}.txt"
61
+
62
+ final_command = "#{@command} 2>#{log_temp_file_name}"
63
+ RVideo.logger.info("\nExecuting Command: #{final_command}\n")
64
+
65
+ do_execute final_command
66
+ populate_raw_result(log_temp_file_name)
67
+ end
68
+
69
+ RVideo.logger.info("Result: \n#{@raw_result}")
70
+ parse_result(@raw_result)
71
+
72
+ # Cleanup log file
73
+ begin
74
+ File.delete(log_temp_file_name)
75
+ rescue Exception => e
76
+ RVideo.logger.error("Failed to delete output log file: #{log_temp_file_name}, e=#{e}")
77
+ end
78
+ end
79
+
80
+ # Wrapper around the system call, for whenever we need to
81
+ # hook on or redefine this without messing with Kernel
82
+ def do_execute(command)
83
+ system command
84
+ end
85
+
86
+ #
87
+ # Magic parameters
88
+ #
89
+ def temp_dir
90
+ if @options['output_file']
91
+ "#{File.dirname(@options['output_file'])}/"
92
+ else
93
+ ""
94
+ end
95
+ end
96
+
97
+ ###
98
+ # FPS aka framerate
99
+
100
+ def fps
101
+ format_fps(get_fps)
102
+ end
103
+
104
+ def get_fps
105
+ inspect_original if @original.nil?
106
+ fps = @options['fps'] || ""
107
+ case fps
108
+ when "copy"
109
+ get_original_fps
110
+ else
111
+ get_specific_fps
112
+ end
113
+ end
114
+
115
+ def get_original_fps
116
+ return {} if @original.fps.nil?
117
+ { :fps => @original.fps }
118
+ end
119
+
120
+ def get_specific_fps
121
+ { :fps => @options['fps'] }
122
+ end
123
+
124
+ ###
125
+ # Resolution
126
+
127
+ def deinterlace
128
+ format_deinterlace(get_deinterlace)
129
+ end
130
+
131
+ def get_deinterlace
132
+ { :deinterlace => @options['deinterlace'] ? true : false }
133
+ end
134
+
135
+ def resolution
136
+ format_resolution(get_resolution)
137
+ end
138
+
139
+ def get_resolution
140
+ inspect_original if @original.nil?
141
+
142
+ case @options['resolution']
143
+ when "copy" then get_original_resolution
144
+ when "width" then get_fit_to_width_resolution
145
+ when "height" then get_fit_to_height_resolution
146
+ when "letterbox" then get_letterbox_resolution
147
+ else
148
+ if @options["width"] and not @options["height"]
149
+ get_fit_to_width_resolution
150
+ elsif @options["height"] and not @options["width"]
151
+ get_fit_to_height_resolution
152
+ elsif @options["width"] and @options["height"]
153
+ get_specific_resolution
154
+ else
155
+ get_original_resolution
156
+ end
157
+ end
158
+ end
159
+
160
+ def get_fit_to_width_resolution
161
+ w = @options['width']
162
+
163
+ raise TranscoderError::ParameterError,
164
+ "invalid width of '#{w}' for fit to width" unless valid_dimension?(w)
165
+
166
+ h = calculate_height(@original.width, @original.height, w)
167
+
168
+ { :scale => { :width => w, :height => h } }
169
+ end
170
+
171
+ def get_fit_to_height_resolution
172
+ h = @options['height']
173
+
174
+ raise TranscoderError::ParameterError,
175
+ "invalid height of '#{h}' for fit to height" unless valid_dimension?(h)
176
+
177
+ w = calculate_width(@original.width, @original.height, h)
178
+
179
+ { :scale => { :width => w, :height => h } }
180
+ end
181
+
182
+ def get_letterbox_resolution
183
+ lw = @options['width'].to_i
184
+ lh = @options['height'].to_i
185
+
186
+ raise TranscoderError::ParameterError,
187
+ "invalid width of '#{lw}' for letterbox" unless valid_dimension?(lw)
188
+ raise TranscoderError::ParameterError,
189
+ "invalid height of '#{lh}' for letterbox" unless valid_dimension?(lh)
190
+
191
+ w = calculate_width(@original.width, @original.height, lh)
192
+ h = calculate_height(@original.width, @original.height, lw)
193
+
194
+ if w > lw
195
+ w = lw
196
+ h = calculate_height(@original.width, @original.height, lw)
197
+ else
198
+ h = lh
199
+ w = calculate_width(@original.width, @original.height, lh)
200
+ end
201
+
202
+ { :scale => { :width => w, :height => h },
203
+ :letterbox => { :width => lw, :height => lh } }
204
+ end
205
+
206
+ def get_original_resolution
207
+ { :scale => { :width => @original.width, :height => @original.height } }
208
+ end
209
+
210
+ def get_specific_resolution
211
+ w = @options['width']
212
+ h = @options['height']
213
+
214
+ raise TranscoderError::ParameterError,
215
+ "invalid width of '#{w}' for specific resolution" unless valid_dimension?(w)
216
+ raise TranscoderError::ParameterError,
217
+ "invalid height of '#{h}' for specific resolution" unless valid_dimension?(h)
218
+
219
+ { :scale => { :width => w, :height => h } }
220
+ end
221
+
222
+ def calculate_width(ow, oh, h)
223
+ w = ((ow.to_f / oh.to_f) * h.to_f).to_i
224
+ (w.to_f / 16).round * 16
225
+ end
226
+
227
+ def calculate_height(ow, oh, w)
228
+ h = (w.to_f / (ow.to_f / oh.to_f)).to_i
229
+ (h.to_f / 16).round * 16
230
+ end
231
+
232
+ def valid_dimension?(dim)
233
+ dim.to_i > 0
234
+ end
235
+
236
+ ###
237
+ # Audio channels
238
+
239
+ def audio_channels
240
+ format_audio_channels(get_audio_channels)
241
+ end
242
+
243
+ def get_audio_channels
244
+ channels = @options['audio_channels'] || ""
245
+ case channels
246
+ when "stereo"
247
+ get_stereo_audio
248
+ when "mono"
249
+ get_mono_audio
250
+ else
251
+ {}
252
+ end
253
+ end
254
+
255
+ def get_stereo_audio
256
+ { :channels => "2" }
257
+ end
258
+
259
+ def get_mono_audio
260
+ { :channels => "1" }
261
+ end
262
+
263
+ def get_specific_audio_bit_rate
264
+ { :bit_rate => @options['audio_bit_rate'] }
265
+ end
266
+
267
+ def get_specific_audio_sample_rate
268
+ { :sample_rate => @options['audio_sample_rate'] }
269
+ end
270
+
271
+ ###
272
+ # Audio bit rate
273
+
274
+ def audio_bit_rate
275
+ format_audio_bit_rate(get_audio_bit_rate)
276
+ end
277
+
278
+ def get_audio_bit_rate
279
+ bit_rate = @options['audio_bit_rate'] || ""
280
+ case bit_rate
281
+ when ""
282
+ {}
283
+ else
284
+ get_specific_audio_bit_rate
285
+ end
286
+ end
287
+
288
+ ###
289
+ # Audio sample rate
290
+
291
+ def audio_sample_rate
292
+ format_audio_sample_rate(get_audio_sample_rate)
293
+ end
294
+
295
+ def get_audio_sample_rate
296
+ sample_rate = @options['audio_sample_rate'] || ""
297
+ case sample_rate
298
+ when ""
299
+ {}
300
+ else
301
+ get_specific_audio_sample_rate
302
+ end
303
+ end
304
+
305
+ ###
306
+ # Video quality
307
+
308
+ def video_quality
309
+ format_video_quality(get_video_quality)
310
+ end
311
+
312
+ def get_video_quality
313
+ quality = @options['video_quality'] || 'medium'
314
+
315
+ { :video_quality => quality }.
316
+ merge!(get_fps).
317
+ merge!(get_resolution).
318
+ merge!(get_video_bit_rate)
319
+ end
320
+
321
+ def video_bit_rate
322
+ format_video_bit_rate(get_video_bit_rate)
323
+ end
324
+
325
+ def get_video_bit_rate
326
+ { :video_bit_rate => @options["video_bit_rate"] }
327
+ end
328
+
329
+ def video_bit_rate_tolerance
330
+ format_video_bit_rate_tolerance(get_video_bit_rate_tolerance)
331
+ end
332
+
333
+ def get_video_bit_rate_tolerance
334
+ { :video_bit_rate_tolerance => @options["video_bit_rate_tolerance"] }
335
+ end
336
+
337
+ def video_bit_rate_min
338
+ format_video_bit_rate_min(get_video_bit_rate_min)
339
+ end
340
+
341
+ def get_video_bit_rate_min
342
+ { :video_bit_rate_min => @options["video_bit_rate_min"] }
343
+ end
344
+
345
+ def video_bit_rate_max
346
+ format_video_bit_rate_max(get_video_bit_rate_max)
347
+ end
348
+
349
+ def get_video_bit_rate_max
350
+ { :video_bit_rate_max => @options["video_bit_rate_max"] }
351
+ end
352
+
353
+ private
354
+
355
+ VARIABLE_INTERPOLATION_SCAN_PATTERN = /[^\\]\$[-_a-zA-Z]+\$/
356
+
357
+ def interpolate_variables(raw_command)
358
+ raw_command.scan(VARIABLE_INTERPOLATION_SCAN_PATTERN).each do |match|
359
+ match = match[0..0] == "$" ? match : match[1..(match.size - 1)]
360
+ match.strip!
361
+
362
+ value = if ["$input_file$", "$output_file$"].include?(match)
363
+ matched_variable(match).to_s.shell_quoted
364
+ else
365
+ matched_variable(match).to_s
366
+ end
367
+
368
+ raw_command.gsub!(match, value)
369
+ end
370
+ raw_command.gsub("\\$", "$")
371
+ end
372
+
373
+ #
374
+ # Strip the $s. First, look for a supplied option that matches the
375
+ # variable name. If one is not found, look for a method that matches.
376
+ # If not found, raise ParameterError exception.
377
+ #
378
+
379
+ def matched_variable(match)
380
+ variable_name = match.gsub("$","")
381
+ if self.respond_to? variable_name
382
+ self.send(variable_name)
383
+ elsif @options.key?(variable_name)
384
+ @options[variable_name]
385
+ else
386
+ raise TranscoderError::ParameterError,
387
+ "command is looking for the #{variable_name} parameter, but it was not provided. (Command: #{@raw_command})"
388
+ end
389
+ end
390
+
391
+
392
+ def inspect_original
393
+ @original = Inspector.new(:file => options[:input_file])
394
+ end
395
+
396
+ # Pulls the interesting bits of the temp log file into memory. This is fairly tool-specific, so
397
+ # it's doubtful that this default version is going to work without being overridded.
398
+ def populate_raw_result(temp_file_name)
399
+ @raw_result = `tail -n 500 #{temp_file_name}`
400
+ end
401
+
402
+ end # InstanceMethods
403
+ end
404
+
405
+ end
406
+ end