mhs-rvideo 0.9.4 → 0.9.6

Sign up to get free protection for your applications and to get access to all the features.
data/History.txt CHANGED
@@ -1,3 +1,23 @@
1
+ == 0.9.6 2010-01-04 (forked, mhs release)
2
+
3
+ * Updated RVideo::Inspect#capture_frame to raise RVideo::TranscoderError::OuptutFileNotFound error when ffmpeg fails to extract a screenshot from a given movie.
4
+
5
+ * Updated inspector to support ffmpeg output from the macports ffmpeg.
6
+
7
+
8
+ == 0.9.5 2008-09-09
9
+ Note:
10
+ * Moving hosting to GitHub.
11
+ http://github.com/zencoder/rvideo/tree
12
+
13
+ 2 major enhancements:
14
+ * Large files, or some tools, would dump out so much output that it could run a server out of memory.
15
+ Changed the way that output is parsed. Instead of pulling it all into memory, the log file is sent to
16
+ disk, and then post-processed.
17
+ * Added support for yamdi (http://yamdi.sourceforge.net/) for FLV metadata injection. This is much faster
18
+ than flvtool2 on huge files, and uses less memory as well.
19
+
20
+
1
21
  == 0.9.4 2007-12-12
2
22
 
3
23
  3 major enhancements:
data/README.txt CHANGED
@@ -82,10 +82,10 @@ do.
82
82
  If RVideo is useful to you, you may also be interested in RMovie, another Ruby
83
83
  video library. See http://rmovie.rubyforge.org/ for more.
84
84
 
85
- Finally, watch for Spinoza, a commercial video transcoder built by Slantwise
86
- Design. Spinoza uses RVideo for its video processing, but adds file queuing,
85
+ Finally, watch for Zencoder, a commercial video transcoder built by Slantwise
86
+ Design. Zencoder uses RVideo for its video processing, but adds file queuing,
87
87
  distributed transcoding, a web-based transcoder dashboard, and more. See
88
- http://spinoza.tv or http://slantwisedesign.com for more.
88
+ http://zencoder.tv or http://slantwisedesign.com for more.
89
89
 
90
90
  Copyright (c) 2007 Jonathan Dahl and Slantwise Design. Released under the MIT
91
91
  license.
data/lib/rvideo/errors.rb CHANGED
@@ -9,6 +9,9 @@ module RVideo
9
9
  class InputFileNotFound < TranscoderError
10
10
  end
11
11
 
12
+ class OutputFileNotFound < TranscoderError
13
+ end
14
+
12
15
  class UnexpectedResult < TranscoderError
13
16
  end
14
17
 
@@ -161,6 +161,20 @@ module RVideo # :nodoc:
161
161
  command = "ffmpeg -i #{@full_filename} -ss #{t} -t 00:00:01 -r 1 -vframes 1 -f image2 #{output_file}"
162
162
  Transcoder.logger.info("\nCreating Screenshot: #{command}\n")
163
163
  frame_result = `#{command} 2>&1`
164
+
165
+ # Different versions of ffmpeg report errors differently when a screenshot cannot be extracted from
166
+ # a video given its set of options. Some versions return a non-zero exit code and report errors while
167
+ # others simply
168
+ unless File.exists?(output_file)
169
+ msg = <<-EOT.gsub(/(^\s+|\n)/, '')
170
+ This means that ffmpeg could not extract a screenshot from the video. It may indicate that
171
+ the video file was corrupt or that the requested frame to be captured was outside the length
172
+ of the video. Full command: #{command}
173
+ EOT
174
+ Transcoder.logger.error msg
175
+ raise TranscoderError::OutputFileNotFound, msg
176
+ end
177
+
164
178
  Transcoder.logger.info("\nScreenshot results: #{frame_result}")
165
179
  output_file
166
180
  end
@@ -322,7 +336,7 @@ module RVideo # :nodoc:
322
336
  bitrate_match[2]
323
337
  end
324
338
 
325
- def audio_bitrate # :nodoc:
339
+ def audio_bit_rate # :nodoc:
326
340
  nil
327
341
  end
328
342
 
@@ -518,7 +532,7 @@ module RVideo # :nodoc:
518
532
 
519
533
  def metadata_match
520
534
  [
521
- /(Input \#.*)\nMust/m, # ffmpegX
535
+ /(Input \#.*)\nMust/m, # ffmpeg
522
536
  /(Input \#.*)\nAt least/m # ffmpeg macports
523
537
  ].each do |rgx|
524
538
  if md=rgx.match(@raw_response)
@@ -11,8 +11,11 @@ module RVideo # :nodoc:
11
11
  tool_name = cmd.split(" ").first
12
12
  begin
13
13
  tool = "RVideo::Tools::#{tool_name.classify}".constantize.send(:new, cmd, options)
14
- rescue NameError, /uninitialized constant/
15
- raise TranscoderError::UnknownTool, "The recipe tried to use the '#{tool_name}' tool, which does not exist."
14
+ # rescue NameError, /uninitialized constant/
15
+ # raise TranscoderError::UnknownTool, "The recipe tried to use the '#{tool_name}' tool, which does not exist."
16
+ rescue => e
17
+ LOGGER.info $!
18
+ LOGGER.info e.backtrace.join("\n")
16
19
  end
17
20
  end
18
21
 
@@ -30,86 +33,258 @@ module RVideo # :nodoc:
30
33
  #
31
34
  # Execute the command and parse the result.
32
35
  #
33
-
36
+ # def execute
37
+ # @output_params = {}
38
+ # final_command = "#{@command} 2>&1"
39
+ # Transcoder.logger.info("\nExecuting Command: #{final_command}\n")
40
+ # @raw_result = `#{final_command}`
41
+ # Transcoder.logger.info("Result: \n#{@raw_result}")
42
+ # parse_result(@raw_result)
43
+ # end
44
+
34
45
  def execute
35
- final_command = "#{@command} 2>&1"
46
+ @output_params = {}
47
+
48
+ # Dump the log output into a temp file
49
+ log_temp_file_name = "/tmp/transcode_output_#{Time.now.to_i}.txt"
50
+
51
+ final_command = "#{@command} 2>#{log_temp_file_name}"
36
52
  Transcoder.logger.info("\nExecuting Command: #{final_command}\n")
37
- @raw_result = `#{final_command}`
53
+ `#{final_command}`
54
+
55
+ populate_raw_result(log_temp_file_name)
56
+
38
57
  Transcoder.logger.info("Result: \n#{@raw_result}")
39
58
  parse_result(@raw_result)
59
+
60
+ # Cleanup log file
61
+ begin
62
+ File.delete(log_temp_file_name)
63
+ rescue Exception => e
64
+ Transcoder.logger.error("Failed to delete output log file: #{log_temp_file_name}, e=#{e}")
65
+ end
40
66
  end
41
67
 
42
68
  #
43
69
  # Magic parameters
44
70
  #
71
+ def temp_dir
72
+ if @options['output_file']
73
+ "#{File.dirname(@options['output_file'])}/"
74
+ else
75
+ ""
76
+ end
77
+ end
78
+
45
79
 
46
- def original_fps
47
- raise ParameterError, "The #{self.class} tool has not implemented the original_fps method."
80
+ def fps
81
+ format_fps(get_fps)
48
82
  end
49
83
 
50
- def resolution
51
- if @options['resolution']
52
- @options['resolution']
84
+ def get_fps
85
+ inspect_original if @original.nil?
86
+ fps = @options['fps'] || ""
87
+ case fps
88
+ when "copy"
89
+ get_original_fps
53
90
  else
54
- "#{calculate_width}x#{calculate_height}"
91
+ get_specific_fps
55
92
  end
56
93
  end
57
-
58
- def aspect_ratio
59
- "#{calculate_width}:#{calculate_height}"
94
+
95
+
96
+ def resolution
97
+ format_resolution(get_resolution)
60
98
  end
61
-
62
- def fps
63
- case fps.to_s
64
- when "?"
65
- inspect_original if @original.nil?
66
- @original.fps
99
+
100
+ def get_resolution
101
+ inspect_original if @original.nil?
102
+ resolution_setting = @options['resolution'] || ""
103
+ case resolution_setting
104
+ when "copy"
105
+ get_original_resolution
106
+ when "width"
107
+ get_fit_to_width_resolution
108
+ when "height"
109
+ get_fit_to_height_resolution
110
+ when "letterbox"
111
+ get_letterbox_resolution
67
112
  else
68
- @options['fps']
113
+ get_specific_resolution
69
114
  end
70
115
  end
116
+
117
+
118
+ def audio_channels
119
+ format_audio_channels(get_audio_channels)
120
+ end
71
121
 
72
- def calculate_width
73
- width = @options['width']
74
- height = @options['height']
75
- if width.to_i > 0
76
- width
122
+ def get_audio_channels
123
+ channels = @options['audio_channels'] || ""
124
+ case channels
125
+ when "stereo"
126
+ get_stereo_audio
127
+ when "mono"
128
+ get_mono_audio
129
+ else
130
+ {}
77
131
  end
78
-
79
- case width
80
- when "x"
81
- inspect_original if @original.nil?
82
- x = ((@original.width.to_f / @original.height.to_f) * height.to_f).to_i
83
- (x.to_f / 16).round * 16
84
- when "?"
85
- inspect_original if @original.nil?
86
- @original.width
132
+ end
133
+
134
+ def audio_bit_rate
135
+ format_audio_bit_rate(get_audio_bit_rate)
136
+ end
137
+
138
+ def get_audio_bit_rate
139
+ bit_rate = @options['audio_bit_rate'] || ""
140
+ case bit_rate
141
+ when ""
142
+ {}
87
143
  else
88
- width
144
+ get_specific_audio_bit_rate
89
145
  end
90
146
  end
91
-
92
- def calculate_height
93
- width = @options['width']
94
- height = @options['height']
95
- if height.to_i > 0
96
- height
147
+
148
+ def audio_sample_rate
149
+ format_audio_sample_rate(get_audio_sample_rate)
150
+ end
151
+
152
+ def get_audio_sample_rate
153
+ sample_rate = @options['audio_sample_rate'] || ""
154
+ case sample_rate
155
+ when ""
156
+ {}
157
+ else
158
+ get_specific_audio_sample_rate
97
159
  end
98
- #w/(ow/oh) = h
99
-
100
- case height
101
- when "x"
102
- inspect_original if @original.nil?
103
- x = (width.to_f / (@original.width.to_f / @original.height.to_f)).to_i
104
- (x.to_f / 16).round * 16
105
- when "?"
106
- inspect_original if @original.nil?
107
- @original.height
160
+ end
161
+
162
+ def video_quality
163
+ format_video_quality(get_video_quality)
164
+ end
165
+
166
+ def get_video_quality
167
+ inspect_original if @original.nil?
168
+ quality = @options['video_quality'] || 'medium'
169
+ video_bit_rate = @options['video_bit_rate'] || nil
170
+ h = {:video_quality => quality, :video_bit_rate => video_bit_rate}
171
+ h.merge!(get_fps).merge!(get_resolution)
172
+ end
173
+
174
+
175
+
176
+ def get_fit_to_width_resolution
177
+ w = @options['width']
178
+ raise TranscoderError::ParameterError, "invalid width of '#{w}' for fit to width" unless valid_dimension?(w)
179
+ h = calculate_height(@original.width, @original.height, w)
180
+ {:scale => {:width => w, :height => h}}
181
+ end
182
+
183
+ def get_fit_to_height_resolution
184
+ h = @options['height']
185
+ raise TranscoderError::ParameterError, "invalid height of '#{h}' for fit to height" unless valid_dimension?(h)
186
+ w = calculate_width(@original.width, @original.height, h)
187
+ {:scale => {:width => w, :height => h}}
188
+ end
189
+
190
+ def get_letterbox_resolution
191
+ lw = @options['width'].to_i
192
+ lh = @options['height'].to_i
193
+ raise TranscoderError::ParameterError, "invalid width of '#{lw}' for letterbox" unless valid_dimension?(lw)
194
+ raise TranscoderError::ParameterError, "invalid height of '#{lh}' for letterbox" unless valid_dimension?(lh)
195
+ w = calculate_width(@original.width, @original.height, lh)
196
+ h = calculate_height(@original.width, @original.height, lw)
197
+ if w > lw
198
+ w = lw
199
+ h = calculate_height(@original.width, @original.height, lw)
108
200
  else
109
- height
201
+ h = lh
202
+ w = calculate_width(@original.width, @original.height, lh)
110
203
  end
204
+ {:scale => {:width => w, :height => h}, :letterbox => {:width => lw, :height => lh}}
205
+ end
206
+
207
+ def get_original_resolution
208
+ {:scale => {:width => @original.width, :height => @original.height}}
209
+ end
210
+
211
+ def get_specific_resolution
212
+ w = @options['width']
213
+ h = @options['height']
214
+ raise TranscoderError::ParameterError, "invalid width of '#{w}' for specific resolution" unless valid_dimension?(w)
215
+ raise TranscoderError::ParameterError, "invalid height of '#{h}' for specific resolution" unless valid_dimension?(h)
216
+ {:scale => {:width => w, :height => h}}
217
+ end
218
+
219
+ def get_original_fps
220
+ return {} if @original.fps.nil?
221
+ {:fps => @original.fps}
222
+ end
223
+
224
+ def get_specific_fps
225
+ {:fps => @options['fps']}
226
+ end
227
+
228
+ # def get_video_quality
229
+ # fps = @options['fps'] || @original.fps
230
+ # raise TranscoderError::ParameterError, "could not find fps in order to determine video quality" if fps.nil?
231
+ # width = @original.width
232
+ # height = @
233
+ # format_video_quality({:quality => @options['video_quality'], :bit_rate => @options['video_bit_rate']})
234
+ # end
235
+
236
+ def get_stereo_audio
237
+ {:channels => "2"}
238
+ end
239
+
240
+ def get_mono_audio
241
+ {:channels => "1"}
242
+ end
243
+
244
+ def get_specific_audio_bit_rate
245
+ {:bit_rate => @options['audio_bit_rate']}
246
+ end
247
+
248
+ def get_specific_audio_sample_rate
249
+ {:sample_rate => @options['audio_sample_rate']}
111
250
  end
112
251
 
252
+ def calculate_width(ow, oh, h)
253
+ w = ((ow.to_f / oh.to_f) * h.to_f).to_i
254
+ (w.to_f / 16).round * 16
255
+ end
256
+
257
+ def calculate_height(ow, oh, w)
258
+ h = (w.to_f / (ow.to_f / oh.to_f)).to_i
259
+ (h.to_f / 16).round * 16
260
+ end
261
+
262
+
263
+ def valid_dimension?(dim)
264
+ return false if dim.to_i <= 0
265
+ return true
266
+ end
267
+
268
+ def format_resolution(params={})
269
+ raise ParameterError, "The #{self.class} tool has not implemented the format_resolution method."
270
+ end
271
+
272
+ def format_fps(params={})
273
+ raise ParameterError, "The #{self.class} tool has not implemented the format_fps method."
274
+ end
275
+
276
+ def format_audio_channels(params={})
277
+ raise ParameterError, "The #{self.class} tool has not implemented the format_audio_channels method."
278
+ end
279
+
280
+ def format_audio_bit_rate(params={})
281
+ raise ParameterError, "The #{self.class} tool has not implemented the format_audio_bit_rate method."
282
+ end
283
+
284
+ def format_audio_sample_rate(params={})
285
+ raise ParameterError, "The #{self.class} tool has not implemented the format_audio_sample_rate method."
286
+ end
287
+
113
288
  private
114
289
 
115
290
 
@@ -123,6 +298,7 @@ module RVideo # :nodoc:
123
298
 
124
299
  def interpolate_variables(raw_command)
125
300
  raw_command.scan(/[^\\]\$[-_a-zA-Z]+\$/).each do |match|
301
+ match = match[0..0] == "$" ? match : match[1..(match.size - 1)]
126
302
  match.strip!
127
303
  raw_command.gsub!(match, matched_variable(match))
128
304
  end
@@ -149,6 +325,13 @@ module RVideo # :nodoc:
149
325
  def inspect_original
150
326
  @original = Inspector.new(:file => options[:input_file])
151
327
  end
328
+
329
+ # Pulls the interesting bits of the temp log file into memory. This is fairly tool-specific, so
330
+ # it's doubtful that this default version is going to work without being overridded.
331
+ def populate_raw_result(temp_file_name)
332
+ @raw_result = `tail -n 500 #{temp_file_name}`
333
+ end
334
+
152
335
  end
153
336
 
154
337
  end
@@ -10,24 +10,78 @@ module RVideo
10
10
  'ffmpeg'
11
11
  end
12
12
 
13
- #
14
- # Return -r and the frame rate of the original file. E.g.:
15
- #
16
- # -r 29.97
17
- #
18
- # If the original frame rate can't be determined, return an empty
19
- # string.
20
- def original_fps
21
- inspect_original if @original.nil?
22
- if @original.fps
23
- "-r #{@original.fps}"
13
+ def format_fps(params={})
14
+ " -r #{params[:fps]}"
15
+ end
16
+ def format_video_quality(params={})
17
+ bitrate = params[:video_bit_rate].blank? ? nil : params[:video_bit_rate]
18
+ factor = (params[:scale][:width].to_f * params[:scale][:height].to_f * params[:fps].to_f)
19
+ case params[:video_quality]
20
+ when 'low'
21
+ bitrate ||= (factor / 12000).to_i
22
+ " -v #{bitrate}k -crf 30 -me zero -subq 1 -refs 1 -threads auto "
23
+ when 'medium'
24
+ bitrate ||= (factor / 9000).to_i
25
+ " -v #{bitrate}k -crf 22 -flags +loop -cmp +sad -partitions +parti4x4+partp8x8+partb8x8 -flags2 +mixed_refs -me hex -subq 3 -trellis 1 -refs 2 -bf 3 -b_strategy 1 -coder 1 -me_range 16 -g 250"
26
+ when 'high'
27
+ bitrate ||= (factor / 3600).to_i
28
+ " -v #{bitrate}k -crf 18 -flags +loop -cmp +sad -partitions +parti4x4+partp8x8+partb8x8 -flags2 +mixed_refs -me full -subq 6 -trellis 1 -refs 3 -bf 3 -b_strategy 1 -coder 1 -me_range 16 -g 250 -keyint_min 25 -sc_threshold 40 -i_qfactor 0.71"
24
29
  else
25
30
  ""
26
31
  end
32
+ end
33
+
34
+
35
+ def format_resolution(params={})
36
+ p = " -s #{params[:scale][:width]}x#{params[:scale][:height]} "
37
+ if params[:letterbox]
38
+ plr = ((params[:letterbox][:width] - params[:scale][:width]) / 2).to_i
39
+ ptb = ((params[:letterbox][:height] - params[:scale][:height]) / 2).to_i
40
+ p += " -padtop #{ptb} -padbottom #{ptb} -padleft #{plr} -padright #{plr} "
41
+ end
42
+ p
43
+ end
44
+
45
+ def format_audio_channels(params={})
46
+ " -ac #{params[:channels]}"
47
+ end
48
+
49
+ def format_audio_bit_rate(params={})
50
+ " -ab #{params[:bit_rate]}k"
27
51
  end
28
52
 
53
+ def format_audio_sample_rate(params={})
54
+ " -ar #{params[:sample_rate]}"
55
+ end
56
+
57
+
29
58
  private
30
59
 
60
+ # Turns the temp log file into a useful string, from which we can parse the
61
+ # transcoding results.
62
+ # These log files can be enormous, so pulling the whole thing into memory is not an
63
+ # option.
64
+ def populate_raw_result(temp_file_name)
65
+ @raw_result = ""
66
+
67
+ # Is the log file exceptionally long? It's really not a big deal to pull in a thousand lines or so
68
+ # into memory. It's the gigantic files that cause problems. If the files isn't too large,
69
+ # just pull it in.
70
+ line_count = 0
71
+ if m = /^\s*(\d+)/.match(`wc -l #{temp_file_name}`)
72
+ line_count = m[1].to_i
73
+ end
74
+
75
+ if line_count > 500
76
+ # Find the message indicating that the command is actually running.
77
+ running_string = "Press .* to stop encoding"
78
+ @raw_result << `grep "#{running_string}" #{temp_file_name}`
79
+ end
80
+
81
+ # Append the bottom of the log file, where the interesting bits live.
82
+ @raw_result << `tail -n 500 #{temp_file_name}`
83
+ end
84
+
31
85
  def parse_result(result)
32
86
 
33
87
  if m = /Unable for find a suitable output format for.*$/.match(result)
@@ -0,0 +1,42 @@
1
+ module RVideo
2
+ module Tools
3
+ class Ffmpeg2theora
4
+ include AbstractTool::InstanceMethods
5
+
6
+ attr_reader :raw_metadata
7
+
8
+ def tool_command
9
+ 'ffmpeg2theora'
10
+ end
11
+
12
+ def format_video_quality(params={})
13
+ bitrate = params[:video_bit_rate].blank? ? nil : params[:video_bit_rate]
14
+ factor = (params[:scale][:width].to_f * params[:scale][:height].to_f * params[:fps].to_f)
15
+ case params[:video_quality]
16
+ when 'low'
17
+ " -v 1 "
18
+ when 'medium'
19
+ "-v 5 "
20
+ when 'high'
21
+ "-v 10 "
22
+ else
23
+ ""
24
+ end
25
+ end
26
+
27
+ def parse_result(result)
28
+ if m = /does not exist or has an unknown data format/.match(result)
29
+ raise TranscoderError::InvalidFile, "I/O error"
30
+ end
31
+
32
+ if m = /General output options/.match(result)
33
+ raise TranscoderError::InvalidCommand, "no command passed to ffmpeg2theora, or no output file specified"
34
+ end
35
+
36
+ @raw_metadata = result.empty? ? "No Results" : result
37
+ return true
38
+ end
39
+
40
+ end
41
+ end
42
+ end
@@ -1,3 +1,4 @@
1
+ # Warning: If you're dealing with large files, you should consider using yamdi instead.
1
2
  module RVideo
2
3
  module Tools
3
4
  class Flvtool2
@@ -3,26 +3,67 @@ module RVideo
3
3
  class Mencoder
4
4
  include AbstractTool::InstanceMethods
5
5
 
6
- attr_reader :frame, :size, :time, :bitrate, :video_size, :audio_size, :fps
7
-
6
+ attr_reader :frame, :size, :time, :bitrate, :video_size, :audio_size, :output_fps
7
+
8
8
  def tool_command
9
9
  'mencoder'
10
10
  end
11
11
 
12
- def original_fps
13
- inspect_original if @original.nil?
14
- if @original.fps
15
- "-ofps #{@original.fps}"
12
+ def format_fps(params={})
13
+ " -ofps #{params[:fps]}"
14
+ end
15
+
16
+ def format_resolution(params={})
17
+ p = " -vf scale=#{params[:scale][:width]}:#{params[:scale][:height]}"
18
+ if params[:letterbox]
19
+ p += ",expand=#{params[:letterbox][:width]}:#{params[:letterbox][:height]}"
20
+ end
21
+ p += ",harddup"
22
+ end
23
+
24
+ def format_audio_channels(params={})
25
+ " -channels #{params[:channels]}"
26
+ end
27
+
28
+ def format_audio_bit_rate(params={})
29
+ " br=#{params[:bit_rate]}:"
30
+ end
31
+
32
+ def format_audio_sample_rate(params={})
33
+ " -srate #{params[:sample_rate]}"
34
+ end
35
+
36
+ def format_video_quality(params={})
37
+ bitrate = params[:video_bit_rate].blank? ? nil : params[:video_bit_rate]
38
+ factor = (params[:scale][:width].to_f * params[:scale][:height].to_f * params[:fps].to_f)
39
+ case params[:video_quality]
40
+ when 'low'
41
+ bitrate ||= (factor / 12000).to_i
42
+ " -x264encopts threads=auto:subq=1:me=dia:frameref=1:crf=30:bitrate=#{bitrate} "
43
+ when 'medium'
44
+ bitrate ||= (factor / 9000).to_i
45
+ " -x264encopts threads=auto:subq=3:me=hex:frameref=2:crf=22:bitrate=#{bitrate} "
46
+ when 'high'
47
+ bitrate ||= (factor / 3600).to_i
48
+ " -x264encopts threads=auto:subq=6:me=dia:frameref=3:crf=18:bitrate=#{bitrate} "
16
49
  else
17
50
  ""
18
51
  end
19
- end
52
+ end
53
+
20
54
 
55
+
56
+ private
57
+
21
58
  def parse_result(result)
22
59
  if m = /Exiting.*No output file specified/.match(result)
23
60
  raise TranscoderError::InvalidCommand, "no command passed to mencoder, or no output file specified"
24
61
  end
25
62
 
63
+ if m = /counldn't set specified parameters, exiting/.match(result)
64
+ raise TranscoderError::InvalidCommand, "a combination of the recipe parameters is invalid: #{result}"
65
+ end
66
+
26
67
  if m = /Sorry, this file format is not recognized\/supported/.match(result)
27
68
  raise TranscoderError::InvalidFile, "unknown format"
28
69
  end
@@ -41,7 +82,7 @@ module RVideo
41
82
  @video_size = sanitary_match(/size:\s*(\d*)\s*(\S*)/, video_details[0])
42
83
  @time = sanitary_match(/bytes\s*([0-9.]*)/, video_details[0])
43
84
  @frame = sanitary_match(/secs\s*(\d*)/, video_details[0])
44
- @fps = (@frame.to_f / @time.to_f).round_to(3)
85
+ @output_fps = (@frame.to_f / @time.to_f).round_to(3)
45
86
  elsif result =~ /Video stream is mandatory/
46
87
  raise TranscoderError::InvalidFile, "Video stream required, and no video stream found"
47
88
  end
@@ -9,13 +9,8 @@ module RVideo
9
9
  'mp4creator'
10
10
  end
11
11
 
12
- def original_fps
13
- inspect_original if @original.nil?
14
- if @original.fps
15
- "-rate=#{@original.fps}"
16
- else
17
- ""
18
- end
12
+ def format_fps(params={})
13
+ " -rate=#{params[:fps]}"
19
14
  end
20
15
 
21
16
  def parse_result(result)
@@ -27,6 +22,10 @@ module RVideo
27
22
  raise TranscoderError::InvalidFile, "I/O error"
28
23
  end
29
24
 
25
+ if @options['output_file'] && !File.exist?(@options['output_file'])
26
+ raise TranscoderError::UnexpectedResult, "An unknown error has occured with mp4creator:#{result}"
27
+ end
28
+
30
29
  @raw_metadata = result.empty? ? "No Results" : result
31
30
  return true
32
31
  end
@@ -0,0 +1,44 @@
1
+ module RVideo
2
+ module Tools
3
+ class Yamdi
4
+ include AbstractTool::InstanceMethods
5
+
6
+ attr_reader :raw_metadata
7
+
8
+ def tool_command
9
+ 'yamdi'
10
+ end
11
+
12
+ private
13
+
14
+ def parse_result(result)
15
+ if result.empty?
16
+ return true
17
+ end
18
+
19
+ if m = /Couldn't stat on (.*)/.match(result)
20
+ raise TranscoderError::InputFileNotFound, m[0]
21
+ end
22
+
23
+ if m = /The input file is not a FLV./.match(result)
24
+ raise TranscoderError::InvalidFile, "input must be a valid FLV file"
25
+ end
26
+
27
+ if m = /\(c\) \d{4} Ingo Oppermann/i.match(result)
28
+ raise TranscoderError::InvalidCommand, "command printed yamdi help text (and presumably didn't execute)"
29
+ end
30
+
31
+ if m = /Please provide at least one output file/i.match(result)
32
+ raise TranscoderError::InvalidCommand, "command did not contain a valid output file. Yamdi expects a -o switch."
33
+ end
34
+
35
+ if m = /ERROR: undefined method .?timestamp.? for nil/.match(result)
36
+ raise TranscoderError::InvalidFile, "Output file was empty (presumably)"
37
+ end
38
+
39
+ raise TranscoderError::UnexpectedResult, result
40
+ end
41
+
42
+ end
43
+ end
44
+ end
@@ -2,7 +2,7 @@ module Rvideo #:nodoc:
2
2
  module VERSION #:nodoc:
3
3
  MAJOR = 0
4
4
  MINOR = 9
5
- TINY = 4
5
+ TINY = 6
6
6
 
7
7
  STRING = [MAJOR, MINOR, TINY].join('.')
8
8
  end
data/lib/rvideo.rb CHANGED
@@ -9,6 +9,8 @@ require 'tools/flvtool2'
9
9
  require 'tools/mp4box'
10
10
  require 'tools/mplayer'
11
11
  require 'tools/mp4creator'
12
+ require 'tools/ffmpeg2theora'
13
+ require 'tools/yamdi'
12
14
  require 'errors'
13
15
  require 'transcoder'
14
16
  require 'active_support'
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mhs-rvideo
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.9.4
4
+ version: 0.9.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jonathan Dahl (Slantwise Design)
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2010-01-27 00:00:00 -05:00
12
+ date: 2010-02-04 00:00:00 -05:00
13
13
  default_executable:
14
14
  dependencies: []
15
15
 
@@ -35,6 +35,8 @@ files:
35
35
  - lib/rvideo/inspector.rb
36
36
  - lib/rvideo/tools/abstract_tool.rb
37
37
  - lib/rvideo/tools/ffmpeg.rb
38
+ - lib/rvideo/tools/ffmpeg2theora.rb
39
+ - lib/rvideo/tools/yamdi.rb
38
40
  - lib/rvideo/tools/flvtool2.rb
39
41
  - lib/rvideo/tools/mencoder.rb
40
42
  - lib/rvideo/tools/mp4box.rb