stlondemand-rvideo 0.9.7

Sign up to get free protection for your applications and to get access to all the features.
Files changed (73) 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 +72 -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 +49 -0
  11. data/lib/rvideo/command_executor.rb +91 -0
  12. data/lib/rvideo/errors.rb +24 -0
  13. data/lib/rvideo/float.rb +7 -0
  14. data/lib/rvideo/frame_capturer.rb +139 -0
  15. data/lib/rvideo/inspector.rb +519 -0
  16. data/lib/rvideo/reporter.rb +176 -0
  17. data/lib/rvideo/reporter/views/index.html.erb +27 -0
  18. data/lib/rvideo/reporter/views/report.css +27 -0
  19. data/lib/rvideo/reporter/views/report.html.erb +81 -0
  20. data/lib/rvideo/reporter/views/report.js +9 -0
  21. data/lib/rvideo/string.rb +5 -0
  22. data/lib/rvideo/tools/abstract_tool.rb +459 -0
  23. data/lib/rvideo/tools/ffmpeg.rb +314 -0
  24. data/lib/rvideo/tools/ffmpeg2theora.rb +72 -0
  25. data/lib/rvideo/tools/flvtool2.rb +50 -0
  26. data/lib/rvideo/tools/handbrakecli.rb +61 -0
  27. data/lib/rvideo/tools/lame.rb +58 -0
  28. data/lib/rvideo/tools/mencoder.rb +126 -0
  29. data/lib/rvideo/tools/mp4box.rb +21 -0
  30. data/lib/rvideo/tools/mp4creator.rb +35 -0
  31. data/lib/rvideo/tools/mplayer.rb +31 -0
  32. data/lib/rvideo/tools/qtfaststart.rb +37 -0
  33. data/lib/rvideo/tools/segmenter.rb +29 -0
  34. data/lib/rvideo/tools/yamdi.rb +44 -0
  35. data/lib/rvideo/transcoder.rb +170 -0
  36. data/lib/rvideo/version.rb +9 -0
  37. data/scripts/txt2html +67 -0
  38. data/spec/files/boat.avi +0 -0
  39. data/spec/files/kites.mp4 +0 -0
  40. data/spec/fixtures/ffmpeg_builds.yml +28 -0
  41. data/spec/fixtures/ffmpeg_results.yml +608 -0
  42. data/spec/fixtures/files.yml +398 -0
  43. data/spec/fixtures/recipes.yml +58 -0
  44. data/spec/integrations/formats_spec.rb +315 -0
  45. data/spec/integrations/frame_capturer_spec.rb +26 -0
  46. data/spec/integrations/inspection_spec.rb +125 -0
  47. data/spec/integrations/recipes_spec.rb +0 -0
  48. data/spec/integrations/rvideo_spec.rb +17 -0
  49. data/spec/integrations/transcoder_integration_spec.rb +29 -0
  50. data/spec/integrations/transcoding_spec.rb +9 -0
  51. data/spec/spec.opts +1 -0
  52. data/spec/spec_helper.rb +16 -0
  53. data/spec/support.rb +36 -0
  54. data/spec/units/abstract_tool_spec.rb +111 -0
  55. data/spec/units/command_executor_spec.rb +106 -0
  56. data/spec/units/ffmpeg_spec.rb +385 -0
  57. data/spec/units/flvtool2_spec.rb +323 -0
  58. data/spec/units/frame_capturer_spec.rb +71 -0
  59. data/spec/units/inspector_spec.rb +59 -0
  60. data/spec/units/mencoder_spec.rb +4994 -0
  61. data/spec/units/mp4box_spec.rb +34 -0
  62. data/spec/units/mp4creator_spec.rb +34 -0
  63. data/spec/units/mplayer_spec.rb +34 -0
  64. data/spec/units/qtfaststart_spec.rb +35 -0
  65. data/spec/units/string_spec.rb +8 -0
  66. data/spec/units/transcoder_spec.rb +154 -0
  67. data/stlondemand-rvideo.gemspec +36 -0
  68. data/tasks/deployment.rake +5 -0
  69. data/tasks/testing.rake +27 -0
  70. data/tasks/transcoding.rake +40 -0
  71. data/tasks/website.rake +8 -0
  72. data/test_progress_reporting.rb +14 -0
  73. metadata +187 -0
@@ -0,0 +1,314 @@
1
+ module RVideo
2
+ module Tools
3
+ class Ffmpeg
4
+
5
+ RESOLUTION_ABBREVIATIONS = {
6
+ "sqcif" => "128x96",
7
+ "qcif" => "176x144",
8
+ "cif" => "352x288",
9
+ "4cif" => "704x576",
10
+ "qqvga" => "160x120",
11
+ "qvga" => "320x240",
12
+ "vga" => "640x480",
13
+ "svga" => "800x600",
14
+ "xga" => "1024x768",
15
+ "uxga" => "1600x1200",
16
+ "qxga" => "2048x1536",
17
+ "sxga" => "1280x1024",
18
+ "qsxga" => "2560x2048",
19
+ "hsxga" => "5120x4096",
20
+ "wvga" => "852x480",
21
+ "wxga" => "1366x768",
22
+ "wsxga" => "1600x1024",
23
+ "wuxga" => "1920x1200",
24
+ "woxga" => "2560x1600",
25
+ "wqsxga" => "3200x2048",
26
+ "wquxga" => "3840x2400",
27
+ "whsxga" => "6400x4096",
28
+ "whuxga" => "7680x4800",
29
+ "cga" => "320x200",
30
+ "ega" => "640x350",
31
+ "hd480" => "852x480",
32
+ "hd720" => "1280x720",
33
+ "hd1080" => "1920x1080"
34
+ }
35
+
36
+ VALID_ASPECT_STRINGS = ["4:3", "16:9"]
37
+ VALID_ASPECT_FLOATS = [1.3333, 1.7777]
38
+
39
+ # The flag used to set video bitrate has apparently changed between
40
+ # different ffmpeg versions. In the latest builds -b is used.
41
+ # In older builds it was -v which is now used to set verbosity of logging.
42
+ DEFAULT_VIDEO_BIT_RATE_PARAMETER = "b"
43
+ cattr_accessor :video_bit_rate_parameter
44
+ self.video_bit_rate_parameter = DEFAULT_VIDEO_BIT_RATE_PARAMETER
45
+
46
+ include AbstractTool::InstanceMethods
47
+
48
+ attr_reader :frame, :q, :size, :time, :output_bitrate, :video_size, :audio_size, :header_size, :overhead, :psnr, :output_fps, :pid
49
+
50
+ def initialize(raw_command, options = {})
51
+ @raw_command = raw_command
52
+ @options = HashWithIndifferentAccess.new(options)
53
+ @command = interpolate_variables(raw_command)
54
+ end
55
+
56
+ # Not sure if this is needed anymore...
57
+ def tool_command
58
+ 'ffmpeg'
59
+ end
60
+
61
+ def format_deinterlace(params={})
62
+ params[:deinterlace] ? "-deinterlace" : ""
63
+ end
64
+
65
+ def format_fps(params={})
66
+ "-r #{params[:fps]}"
67
+ end
68
+
69
+ def format_video_bit_rate(params = {})
70
+ "-#{video_bit_rate_parameter} #{params[:video_bit_rate]}k"
71
+ end
72
+
73
+ def format_video_bit_rate_tolerance(params = {})
74
+ "-bt #{params[:video_bit_rate_tolerance]}k"
75
+ end
76
+
77
+ def format_video_bit_rate_min(params = {})
78
+ "-minrate #{params[:video_bit_rate_min]}k"
79
+ end
80
+
81
+ def format_video_bit_rate_max(params = {})
82
+ "-maxrate #{params[:video_bit_rate_max]}k"
83
+ end
84
+
85
+ def format_video_quality(params={})
86
+ bitrate = params[:video_bit_rate].blank? ? nil : params[:video_bit_rate]
87
+
88
+ params.merge! get_original_fps unless params[:fps]
89
+
90
+ factor = params[:scale][:width].to_f * params[:scale][:height].to_f * params[:fps].to_f
91
+
92
+ case params[:video_quality]
93
+ when 'low'
94
+ bitrate ||= (factor / 12000).to_i
95
+ params[:video_bit_rate] = bitrate
96
+ "#{format_video_bit_rate params} -crf 30 -me zero -subq 1 -refs 1 -threads auto"
97
+ when 'medium'
98
+ bitrate ||= (factor / 9000).to_i
99
+ params[:video_bit_rate] = bitrate
100
+ "#{format_video_bit_rate params} -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"
101
+ when 'high'
102
+ bitrate ||= (factor / 3600).to_i
103
+ params[:video_bit_rate] = bitrate
104
+ "#{format_video_bit_rate params} -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"
105
+ end
106
+ end
107
+
108
+ def format_resolution(params={})
109
+ p = []
110
+
111
+ if original.rotated?
112
+ params[:rotate] = original.video_orientation
113
+ p << format_filter_rotation(params)
114
+ end
115
+
116
+ p << format_filter_scale(params)
117
+ p << format_filter_pad(params) if params.has_key?(:letterbox)
118
+ p.compact!
119
+
120
+ %(-vf '#{p.join(',')}')
121
+ end
122
+
123
+ def format_filter_rotation(params={})
124
+ "transpose=#{params[:rotate].to_i / 90}"
125
+ end
126
+
127
+ def format_filter_scale(params={})
128
+ "scale=#{params[:scale][:width].to_i}:#{params[:scale][:height].to_i}"
129
+ end
130
+
131
+ def format_filter_pad(params={})
132
+ plr = ((params[:letterbox][:width] - params[:scale][:width]) / 2).to_i
133
+ ptb = ((params[:letterbox][:height] - params[:scale][:height]) / 2).to_i
134
+ width = params[:letterbox][:width]
135
+ heigth = params[:letterbox][:height]
136
+
137
+ if(plr > 0 || ptb > 0)
138
+ "pad=#{width.to_i}:#{heigth.to_i}:#{plr.to_i}:#{ptb.to_i}"
139
+ end
140
+ end
141
+
142
+ def format_audio_channels(params={})
143
+ "-ac #{params[:channels]}"
144
+ end
145
+
146
+ def format_audio_bit_rate(params={})
147
+ "-ab #{params[:bit_rate]}k"
148
+ end
149
+
150
+ def format_audio_sample_rate(params={})
151
+ "-ar #{params[:sample_rate]}"
152
+ end
153
+
154
+ def do_execute_with_progress(command,&block)
155
+ @raw_result = ''
156
+ duration = 0
157
+ CommandExecutor::execute_with_block(command, "\r") do |line|
158
+ progress, duration = parse_progress(line, duration)
159
+ block.call(progress) if block && progress
160
+ @raw_result += line.to_s + "\r"
161
+ end
162
+ end
163
+
164
+ def execute_with_progress(&block)
165
+ RVideo.logger.info("\nExecuting Command: #{@command}\n")
166
+ do_execute_with_progress(@command, &block)
167
+ rescue RVideo::CommandExecutor::ProcessHungError
168
+ raise TranscoderError, "Transcoder hung."
169
+ end
170
+
171
+ private
172
+
173
+ def parse_progress(line, duration)
174
+ if line =~ /Duration: (\d{2}):(\d{2}):(\d{2}).(\d{1})/
175
+ duration = (($1.to_i * 60 + $2.to_i) * 60 + $3.to_i) * 10 + $4.to_i
176
+ end
177
+
178
+ if line =~ /time=(\d+).(\d+)/
179
+ if not duration.nil? and duration != 0
180
+ p = ($1.to_i * 10 + $2.to_i) * 100 / duration
181
+ else
182
+ p = 0
183
+ end
184
+ p = 100 if p > 100
185
+ end
186
+ return [p, duration]
187
+ end
188
+
189
+ def parse_result(result)
190
+ if m = /Expected .+ for (.+) but found: (.+)/.match(result)
191
+ raise TranscoderError::InvalidCommand, m.to_s
192
+ end
193
+
194
+ if m = /Unable for find a suitable output format for.*$/.match(result)
195
+ raise TranscoderError::InvalidCommand, m[0]
196
+ end
197
+
198
+ if m = /Unknown codec \'(.*)\'/.match(result)
199
+ raise TranscoderError::InvalidFile, "Codec #{m[1]} not supported by this build of ffmpeg"
200
+ end
201
+
202
+ if m = /could not find codec parameters/.match(result)
203
+ raise TranscoderError::InvalidFile, "Codec not supported by this build of ffmpeg"
204
+ end
205
+
206
+ if m = /I\/O error occured\n(.*)$/.match(result)
207
+ raise TranscoderError::InvalidFile, "I/O error: #{m[1].strip}"
208
+ end
209
+
210
+ if m = /\n(.*)Unknown Format$/.match(result)
211
+ raise TranscoderError::InvalidFile, "unknown format (#{m[1]})"
212
+ end
213
+
214
+ if m = /\nERROR.*/m.match(result)
215
+ raise TranscoderError::InvalidFile, m[0]
216
+ end
217
+
218
+ if m = /[(\S+) @ \d+x\d+]error, (.+?)/.match(result)
219
+ raise TranscoderError::InvalidFile, "#{m[1]}: #{m[2]}"
220
+ end
221
+
222
+ if result =~ /usage: ffmpeg/
223
+ raise TranscoderError::InvalidCommand, "must pass a command to ffmpeg"
224
+ end
225
+
226
+ if result =~ /Output file does not contain.*stream/
227
+ raise TranscoderError, "Output file does not contain any video or audio streams."
228
+ end
229
+
230
+ if m = /Unsupported codec.*id=(.*)\).*for input stream\s*(.*)\s*/.match(result)
231
+ case m[2]
232
+ when original.audio_stream_id
233
+ codec_type = "audio"
234
+ codec = original.audio_codec
235
+ when original.video_stream_id
236
+ codec_type = "video"
237
+ codec = original.video_codec
238
+ else
239
+ codec_type = "video or audio"
240
+ codec = "unknown"
241
+ end
242
+
243
+ raise TranscoderError::InvalidFile, "Unsupported #{codec_type} codec: #{codec} (id=#{m[1]}, stream=#{m[2]})"
244
+ #raise TranscoderError, "Codec #{m[1]} not supported (in stream #{m[2]})"
245
+ end
246
+
247
+ # Could not open './spec/../config/../tmp/processed/1/kites-1.avi'
248
+ if result =~ /Could not open .#{@output_file}.\Z/
249
+ raise TranscoderError, "Could not write output file to #{@output_file}"
250
+ end
251
+
252
+ full_details = /Press .* to stop[^\n]*\n(.*)/m.match(result)
253
+ raise TranscoderError, "Unexpected result details (#{result})" if full_details.nil?
254
+ details = full_details[1].strip.gsub(/\s*\n\s*/," - ")
255
+
256
+ if details =~ /Could not write header/
257
+ raise TranscoderError, details
258
+ end
259
+
260
+ #frame= 584 q=6.0 Lsize= 708kB time=19.5 bitrate= 297.8kbits/s
261
+ #video:49kB audio:153kB global headers:0kB muxing overhead 250.444444%
262
+
263
+ #frame= 4126 q=31.0 Lsize= 5917kB time=69.1 bitrate= 702.0kbits/s
264
+ #video:2417kB audio:540kB global headers:0kB muxing overhead 100.140277%
265
+
266
+ #frame= 273 fps= 31 q=10.0 Lsize= 398kB time=5.9 bitrate= 551.8kbits/s
267
+ #video:284kB audio:92kB global headers:0kB muxing overhead 5.723981%
268
+
269
+ #mdb:94, lastbuf:0 skipping granule 0
270
+ #size= 1080kB time=69.1 bitrate= 128.0kbits /s
271
+ #video:0kB audio:1080kB global headers:0kB muxing overhead 0.002893%
272
+
273
+ #size= 80kB time=5.1 bitrate= 128.0kbits/s ^Msize= 162kB time=10.3 bitrate= 128.0kbits/s ^Msize= 241kB time=15.4 bitrate= 128.0kbits/s ^Msize= 329kB time=21.1 bitrate= 128.0kbits/s ^Msize= 413kB time=26.4 bitrate= 128.0kbits/s ^Msize= 506kB time=32.4 bitrate= 128.0kbits/s ^Msize= 591kB time=37.8 bitrate= 128.0kbits/s ^Msize= 674kB time=43.2 bitrate= 128.0kbits/s ^Msize= 771kB time=49.4 bitrate= 128.0kbits/s ^Msize= 851kB time=54.5 bitrate= 128.0kbits/s ^Msize= 932kB time=59.6 bitrate= 128.0kbits/s ^Msize= 1015kB time=64.9 bitrate= 128.0kbits/s ^Msize= 1094kB time=70.0 bitrate= 128.0kbits/s ^Msize= 1175kB time=75.2 bitrate= 128.0kbits/s ^Msize= 1244kB time=79.6 bitrate= 128.0kbits/s ^Msize= 1335kB time=85.4 bitrate= 128.0kbits/s ^Msize= 1417kB time=90.7 bitrate= 128.0kbits/s ^Msize= 1508kB time=96.5 bitrate= 128.0kbits/s ^Msize= 1589kB time=101.7 bitrate= 128.0kbits/s ^Msize= 1671kB time=106.9 bitrate= 128.0kbits/s ^Msize= 1711kB time=109.5 bitrate= 128.0kbits/s - video:0kB audio:1711kB global headers:0kB muxing overhead 0.001826%
274
+
275
+ #mdb:14, lastbuf:0 skipping granule 0 - overread, skip -5 enddists: -2 -2 - overread, skip -5 enddists: -2 -2 - size= 90kB time=5.7 bitrate= 128.0kbits/s \nsize= 189kB time=12.1 bitrate= 128.0kbits/s
276
+
277
+ #size= 59kB time=20.2 bitrate= 24.0kbits/s \nsize= 139kB time=47.4 bitrate= 24.0kbits/s \nsize= 224kB time=76.5 bitrate= 24.0kbits/s \nsize= 304kB time=103.7 bitrate= 24.0kbits/s \nsi
278
+
279
+ #mdb:14, lastbuf:0 skipping granule 0 - overread, skip -5 enddists: -2 -2 - overread, skip -5 enddists: -2 -2 - size= 81kB time=10.3 bitrate= 64.0kbits/s \nsize= 153kB time=19.6 bitrate= 64.0kbits/s
280
+
281
+ #size= 65kB time=4.1 bitrate= 128.1kbits/s \nsize= 119kB time=7.6 bitrate= 128.0kbits/s \nsize= 188kB time=12.0 bitrate= 128.0kbits/s \nsize= 268kB time=17.1 bitrate= 128.0kbits/s \nsize=
282
+
283
+ #Error while decoding stream #0.1 [mpeg4aac @ 0xb7d089f0]faac: frame decoding failed: Gain control not yet implementedError while decoding stream #0.1frame= 2143 fps= 83 q=4.0 size= 4476kB time=71.3 bitrate= 514.5kbits/s ^M[mpeg4aac @ 0xb7d089f0]faac: frame decoding failed: Gain control not yet implementedError while decoding stream #0.1
284
+
285
+ # NOTE: had to remove "\s" from "\s.*L.*size=" from this regexp below.
286
+ # Not sure why. Unit tests were succeeding, but hand tests weren't.
287
+ if details =~ /video:/
288
+ #success = /^frame=\s*(\S*)\s*q=(\S*).*L.*size=\s*(\S*)\s*time=\s*(\S*)\s*bitrate=\s*(\S*)\s*/m.match(details)
289
+ @frame = details[/frame=\s*(\S*)/, 1]
290
+ @output_fps = details[/fps=\s*(\S*)/, 1]
291
+ @q = details[/\s+q=\s*(\S*)/, 1]
292
+ @size = details[/size=\s*(\S*)/, 1]
293
+ @time = details[/time=\s*(\S*)/, 1]
294
+ @output_bitrate = details[/bitrate=\s*(\S*)/, 1]
295
+
296
+ @video_size = details[/video:\s*(\S*)/, 1]
297
+ @audio_size = details[/audio:\s*(\S*)/, 1]
298
+ @header_size = details[/headers:\s*(\S*)/, 1]
299
+ @overhead = details[/overhead[:]*\s*(\S*)/, 1]
300
+
301
+ psnr_match = /PSNR=(.*)\s*size=/.match(details)
302
+ @psnr = psnr_match[1].strip if psnr_match
303
+ return true
304
+ end
305
+
306
+ #[mp3 @ 0x54340c]flv doesnt support that sample rate, choose from (44100, 22050, 11025)
307
+ #Could not write header for output file #0 (incorrect codec parameters ?)
308
+
309
+ raise TranscoderError::UnexpectedResult, details
310
+ end
311
+
312
+ end
313
+ end
314
+ end
@@ -0,0 +1,72 @@
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 do_execute_with_progress(command, &block)
13
+ @raw_result = ''
14
+ duration = 0
15
+ CommandExecutor::execute_with_block(command, "\r") do |line|
16
+ progress, duration = parse_progress(line, duration)
17
+ block.call(progress) if block && progress
18
+ @raw_result += line.to_s + "\r"
19
+ end
20
+ end
21
+
22
+ def execute_with_progress(&block)
23
+ RVideo.logger.info("\nExecuting Command: #{@command}\n")
24
+ do_execute_with_progress(@command, &block)
25
+ rescue RVideo::CommandExecutor::ProcessHungError
26
+ raise TranscoderError, "Transcoder hung."
27
+ end
28
+
29
+ def parse_progress(line, duration)
30
+ if line =~ /Duration: (\d{2}):(\d{2}):(\d{2}).(\d{1})/
31
+ duration = (($1.to_i * 60 + $2.to_i) * 60 + $3.to_i) * 10 + $4.to_i
32
+ end
33
+
34
+ if line =~ /(\d{1,2}):(\d{2}):(\d{2})\.(\d)\d audio: \d+kbps video: \d+kbps/
35
+ current_time = (($1.to_i * 60 + $2.to_i) * 60 + $3.to_i) * 10 + $4.to_i
36
+ end
37
+
38
+ progress = (current_time*100/duration) rescue nil
39
+ return [progress, duration]
40
+ end
41
+
42
+ def format_video_quality(params={})
43
+ bitrate = params[:video_bit_rate].blank? ? nil : params[:video_bit_rate]
44
+ factor = (params[:scale][:width].to_f * params[:scale][:height].to_f * params[:fps].to_f)
45
+ case params[:video_quality]
46
+ when 'low'
47
+ " -v 1 "
48
+ when 'medium'
49
+ "-v 5 "
50
+ when 'high'
51
+ "-v 10 "
52
+ else
53
+ ""
54
+ end
55
+ end
56
+
57
+ def parse_result(result)
58
+ if m = /does not exist or has an unknown data format/.match(result)
59
+ raise TranscoderError::InvalidFile, "I/O error"
60
+ end
61
+
62
+ if m = /General output options/.match(result)
63
+ raise TranscoderError::InvalidCommand, "no command passed to ffmpeg2theora, or no output file specified"
64
+ end
65
+
66
+ @raw_metadata = result.empty? ? "No Results" : result
67
+ return true
68
+ end
69
+
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,50 @@
1
+ # Warning: If you're dealing with large files, you should consider using yamdi instead.
2
+ module RVideo
3
+ module Tools
4
+ class Flvtool2
5
+ include AbstractTool::InstanceMethods
6
+
7
+ attr_reader :raw_metadata
8
+
9
+ #attr_reader :has_key_frames, :cue_points, :audiodatarate, :has_video, :stereo, :can_seek_to_end, :framerate, :audiosamplerate, :videocodecid, :datasize, :lasttimestamp,
10
+ # :audiosamplesize, :audiosize, :has_audio, :audiodelay, :videosize, :metadatadate, :metadatacreator, :lastkeyframetimestamp, :height, :filesize, :has_metadata, :audiocodecid,
11
+ # :duration, :videodatarate, :has_cue_points, :width
12
+
13
+ def tool_command
14
+ 'flvtool2'
15
+ end
16
+
17
+ private
18
+
19
+ def parse_result(result)
20
+ if result.empty?
21
+ return true
22
+ end
23
+
24
+ if m = /ERROR: No such file or directory(.*)\n/.match(result)
25
+ raise TranscoderError::InputFileNotFound, m[0]
26
+ end
27
+
28
+ if m = /ERROR: IO is not a FLV stream/.match(result)
29
+ raise TranscoderError::InvalidFile, "input must be a valid FLV file"
30
+ end
31
+
32
+ if m = /Copyright.*Norman Timmler/i.match(result)
33
+ raise TranscoderError::InvalidCommand, "command printed flvtool2 help text (and presumably didn't execute)"
34
+ end
35
+
36
+ if m = /ERROR: undefined method .?timestamp.? for nil/.match(result)
37
+ raise TranscoderError::InvalidFile, "Output file was empty (presumably)"
38
+ end
39
+
40
+ if m = /\A---(.*)...\Z/m.match(result)
41
+ @raw_metadata = m[0]
42
+ return true
43
+ end
44
+
45
+ raise TranscoderError::UnexpectedResult, result
46
+ end
47
+
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,61 @@
1
+ module RVideo
2
+ module Tools
3
+ class HandBrakeCli
4
+ include AbstractTool::InstanceMethods
5
+ attr_reader :raw_metadata
6
+
7
+ def tool_command
8
+ 'HandBrakeCLI'
9
+ end
10
+
11
+ def execute_with_progress(&block)
12
+ RVideo.logger.info("\nExecuting Command: #{@command}\n")
13
+ do_execute_with_progress(@command, &block)
14
+ rescue RVideo::CommandExecutor::ProcessHungError
15
+ raise TranscoderError, "Transcoder hung."
16
+ end
17
+
18
+ def format_resolution(params={})
19
+ "-w #{params[:scale][:width]} -l #{params[:scale][:height]}"
20
+ end
21
+
22
+ private
23
+
24
+ def parse_result(result)
25
+
26
+ if m = /Syntax: HandBrakeCLI [options] -i <device> -o <file>/.match(result)
27
+ raise TranscoderError::InvalidCommand, "Syntax: HandBrakeCLI [options] -i <device> -o <file>"
28
+ end
29
+
30
+ if m = /No such file or directory/.match(result)
31
+ raise TranscoderError::InvalidFile, "No such file or directory"
32
+ end
33
+
34
+ if m = /Undefined error:/.match(result)
35
+ raise TranscoderError::UnexpectedResult, "Undefined error"
36
+ end
37
+
38
+ @raw_metadata = result.to_s.empty? ? "No Results" : result
39
+ return true
40
+ end
41
+
42
+ def do_execute_with_progress(command,&block)
43
+ @raw_result = ''
44
+ CommandExecutor::execute_with_block(command, "\r", false) do |line|
45
+ progress = parse_progress(line)
46
+ block.call(progress) if block && progress
47
+ @raw_result += line.to_s + "\r"
48
+ end
49
+ end
50
+
51
+ def parse_progress(line)
52
+ if line =~ /Encoding: task \d of \d, (\d{1,3}\.\d{1,2}) \%/
53
+ $1.to_i > 100 ? 100 : $1.to_i
54
+ else
55
+ nil
56
+ end
57
+ end
58
+
59
+ end
60
+ end
61
+ end