rvideo 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. data/ENV +100 -0
  2. data/ENV2 +129 -0
  3. data/History.txt +3 -0
  4. data/License.txt +20 -0
  5. data/Manifest.txt +44 -0
  6. data/README.txt +91 -0
  7. data/RULES +11 -0
  8. data/Rakefile +163 -0
  9. data/config/boot.rb +16 -0
  10. data/lib/rvideo.rb +19 -0
  11. data/lib/rvideo/errors.rb +21 -0
  12. data/lib/rvideo/inspector.rb +486 -0
  13. data/lib/rvideo/reporter.rb +176 -0
  14. data/lib/rvideo/reporter/views/index.html.erb +27 -0
  15. data/lib/rvideo/reporter/views/report.css +27 -0
  16. data/lib/rvideo/reporter/views/report.html.erb +81 -0
  17. data/lib/rvideo/reporter/views/report.js +9 -0
  18. data/lib/rvideo/tools/abstract_tool.rb +79 -0
  19. data/lib/rvideo/tools/ffmpeg.rb +111 -0
  20. data/lib/rvideo/tools/flvtool2.rb +45 -0
  21. data/lib/rvideo/transcoder.rb +113 -0
  22. data/lib/rvideo/version.rb +9 -0
  23. data/scripts/txt2html +67 -0
  24. data/setup.rb +1585 -0
  25. data/spec/files/kites.mp4 +0 -0
  26. data/spec/fixtures/ffmpeg_builds.yml +28 -0
  27. data/spec/fixtures/files.yml +374 -0
  28. data/spec/fixtures/recipes.yml +6 -0
  29. data/spec/integrations/files/files.yml +361 -0
  30. data/spec/integrations/formats_spec.rb +287 -0
  31. data/spec/integrations/inspection_spec.rb +15 -0
  32. data/spec/integrations/recipes_spec.rb +0 -0
  33. data/spec/integrations/rvideo_spec.rb +17 -0
  34. data/spec/integrations/transcoding_spec.rb +9 -0
  35. data/spec/spec.opts +1 -0
  36. data/spec/spec_helper.rb +11 -0
  37. data/spec/units/abstract_tool_spec.rb +112 -0
  38. data/spec/units/ffmpeg_spec.rb +703 -0
  39. data/spec/units/flvtool2_spec.rb +314 -0
  40. data/spec/units/inspector_spec.rb +41 -0
  41. data/spec/units/transcoder_spec.rb +140 -0
  42. data/website/index.html +219 -0
  43. data/website/index.txt +142 -0
  44. data/website/javascripts/rounded_corners_lite.inc.js +285 -0
  45. metadata +94 -0
@@ -0,0 +1,16 @@
1
+ $LOAD_PATH.unshift File.dirname(__FILE__) + '/../lib'
2
+
3
+ require 'rvideo/inspector'
4
+ require 'rvideo/tools/abstract_tool'
5
+ require 'rvideo/tools/ffmpeg'
6
+ require 'rvideo/tools/flvtool2'
7
+ require 'rvideo/errors'
8
+ require 'rvideo/transcoder'
9
+ require 'yaml'
10
+ require 'rubygems'
11
+ require 'active_support'
12
+
13
+ TEMP_PATH = File.expand_path(File.dirname(__FILE__) + '/../tmp')
14
+ FIXTURE_PATH = File.expand_path(File.dirname(__FILE__) + '/../spec/fixtures')
15
+ TEST_FILE_PATH = File.expand_path(File.dirname(__FILE__) + '/../spec/files')
16
+ REPORT_PATH = File.expand_path(File.dirname(__FILE__) + '/../report')
@@ -0,0 +1,19 @@
1
+ $LOAD_PATH.unshift File.dirname(__FILE__) + '/rvideo'
2
+
3
+ require 'inspector'
4
+ require 'tools/abstract_tool'
5
+ require 'tools/ffmpeg'
6
+ require 'tools/flvtool2'
7
+ require 'errors'
8
+ require 'transcoder'
9
+ #require 'version'
10
+
11
+ #require 'rubygems'
12
+ #require 'yaml'
13
+ require 'active_support'
14
+
15
+ TEMP_PATH = File.expand_path(File.dirname(__FILE__) + '/../tmp')
16
+ FIXTURE_PATH = File.expand_path(File.dirname(__FILE__) + '/../spec/fixtures')
17
+ TEST_FILE_PATH = File.expand_path(File.dirname(__FILE__) + '/../spec/files')
18
+ REPORT_PATH = File.expand_path(File.dirname(__FILE__) + '/../report')
19
+
@@ -0,0 +1,21 @@
1
+ module RVideo
2
+ class TranscoderError < RuntimeError
3
+ class RecipeError < TranscoderError
4
+ end
5
+
6
+ class InvalidCommand < TranscoderError
7
+ end
8
+
9
+ class InvalidFile < TranscoderError
10
+ end
11
+
12
+ class InputFileNotFound < TranscoderError
13
+ end
14
+
15
+ class UnexpectedResult < TranscoderError
16
+ end
17
+
18
+ class ParameterError < TranscoderError
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,486 @@
1
+ module RVideo # :nodoc:
2
+ class Inspector
3
+
4
+ attr_reader :filename, :path, :full_filename, :raw_response, :raw_metadata
5
+
6
+ attr_accessor :ffmpeg_binary
7
+
8
+ #
9
+ # To inspect a video or audio file, initialize an Inspector object.
10
+ #
11
+ # file = RVideo::Inspector.new(options_hash)
12
+ #
13
+ # Inspector accepts three options: file, raw_response, and ffmpeg_binary.
14
+ # Either raw_response or file is required; ffmpeg binary is optional.
15
+ #
16
+ # :file is a path to a file to be inspected.
17
+ #
18
+ # :raw_response is the full output of "ffmpeg -i [file]". If the
19
+ # :raw_response option is used, RVideo will not actually inspect a file;
20
+ # it will simply parse the provided response. This is useful if your
21
+ # application has already collected the ffmpeg -i response, and you don't
22
+ # want to call it again.
23
+ #
24
+ # :ffmpeg_binary is an optional argument that specifies the path to the
25
+ # ffmpeg binary to be used. If a path is not explicitly declared, RVideo
26
+ # will assume that ffmpeg exists in the Unix path. Type "which ffmpeg" to
27
+ # check if ffmpeg is installed and exists in your operating system's path.
28
+ #
29
+
30
+ def initialize(options = {})
31
+ if options[:raw_response]
32
+ @raw_response = options[:raw_response]
33
+ elsif options[:file]
34
+ if options[:ffmpeg_binary]
35
+ @ffmpeg_binary = options[:ffmpeg_binary]
36
+ raise RuntimeError, "ffmpeg could not be found (trying #{@ffmpeg_binary})" unless FileTest.exist?(@ffmpeg_binary)
37
+ else
38
+ # assume it is in the unix path
39
+ raise RuntimeError, 'ffmpeg could not be found (expected ffmpeg to be found in the Unix path)' unless FileTest.exist?(`which ffmpeg`.chomp)
40
+ @ffmpeg_binary = "ffmpeg"
41
+ end
42
+
43
+ file = options[:file]
44
+ @filename = File.basename(file)
45
+ @path = File.dirname(file)
46
+ @full_filename = file
47
+ raise ArgumentError, "File not found (#{file})" unless FileTest.exist?(file.gsub("\"",""))
48
+ @raw_response = `#{@ffmpeg_binary} -i #{@full_filename} 2>&1`
49
+ else
50
+ raise ArgumentError, "Must supply either an input file or a pregenerated response" if options[:raw_response].nil? and file.nil?
51
+ end
52
+
53
+ metadata = /(Input \#.*)\nMust/m.match(@raw_response)
54
+
55
+ if /Unknown format/i.match(@raw_response) || metadata.nil?
56
+ @unknown_format = true
57
+ elsif /Duration: N\/A, bitrate: N\/A/im.match(@raw_response)
58
+ @unreadable_file = true
59
+ @raw_metadata = metadata[1] # in this case, we can at least still get the container type
60
+ else
61
+ @raw_metadata = metadata[1]
62
+ end
63
+ end
64
+
65
+ #
66
+ # Returns true if the file can be read successfully. Returns false otherwise.
67
+ #
68
+
69
+ def valid?
70
+ if @unknown_format or @unreadable_file
71
+ false
72
+ else
73
+ true
74
+ end
75
+ end
76
+
77
+ #
78
+ # Returns false if the file can be read successfully. Returns false otherwise.
79
+ #
80
+
81
+ def invalid?
82
+ !valid?
83
+ end
84
+
85
+ #
86
+ # True if the format is not understood ("Unknown Format")
87
+ #
88
+
89
+ def unknown_format?
90
+ if @unknown_format
91
+ true
92
+ else
93
+ false
94
+ end
95
+ end
96
+
97
+ #
98
+ # True if the file is not readable ("Duration: N/A, bitrate: N/A")
99
+ #
100
+
101
+ def unreadable_file?
102
+ if @unreadable_file
103
+ true
104
+ else
105
+ false
106
+ end
107
+ end
108
+
109
+ #
110
+ # Does the file have an audio stream?
111
+ #
112
+
113
+ def audio?
114
+ if audio_match.nil?
115
+ false
116
+ else
117
+ true
118
+ end
119
+ end
120
+
121
+ #
122
+ # Does the file have a video stream?
123
+ #
124
+
125
+ def video?
126
+ if video_match.nil?
127
+ false
128
+ else
129
+ true
130
+ end
131
+ end
132
+
133
+
134
+ #
135
+ # Returns the version of ffmpeg used, In practice, this may or may not be
136
+ # useful.
137
+ #
138
+ # Examples:
139
+ #
140
+ # SVN-r6399
141
+ # CVS
142
+ #
143
+
144
+ def ffmpeg_version
145
+ @ffmpeg_version = @raw_response.split("\n").first.split("version").last.split(",").first.strip
146
+ end
147
+
148
+ #
149
+ # Returns the configuration options used to build ffmpeg.
150
+ #
151
+ # Example:
152
+ #
153
+ # --enable-mp3lame --enable-gpl --disable-ffplay --disable-ffserver
154
+ # --enable-a52 --enable-xvid
155
+ #
156
+
157
+ def ffmpeg_configuration
158
+ /(\s*configuration:)(.*)\n/.match(@raw_response)[2].strip
159
+ end
160
+
161
+ #
162
+ # Returns the versions of libavutil, libavcodec, and libavformat used by
163
+ # ffmpeg.
164
+ #
165
+ # Example:
166
+ #
167
+ # libavutil version: 49.0.0
168
+ # libavcodec version: 51.9.0
169
+ # libavformat version: 50.4.0
170
+ #
171
+
172
+ def ffmpeg_libav
173
+ /^(\s*lib.*\n)+/.match(@raw_response)[0].split("\n").each {|l| l.strip! }
174
+ end
175
+
176
+ #
177
+ # Returns the build description for ffmpeg.
178
+ #
179
+ # Example:
180
+ #
181
+ # built on Apr 15 2006 04:58:19, gcc: 4.0.1 (Apple Computer, Inc. build
182
+ # 5250)
183
+ #
184
+
185
+ def ffmpeg_build
186
+ /(\n\s*)(built on.*)(\n)/.match(@raw_response)[2]
187
+ end
188
+
189
+ #
190
+ # Returns the container format for the file. Instead of returning a single
191
+ # format, this may return a string of related formats.
192
+ #
193
+ # Examples:
194
+ #
195
+ # "avi"
196
+ #
197
+ # "mov,mp4,m4a,3gp,3g2,mj2"
198
+ #
199
+
200
+ def container
201
+ return nil if @unknown_format
202
+
203
+ /Input \#\d+\,\s*(\S+),\s*from/.match(@raw_metadata)[1]
204
+ end
205
+
206
+ #
207
+ # The duration of the movie, as a string.
208
+ #
209
+ # Example:
210
+ #
211
+ # "00:00:24.4" # 24.4 seconds
212
+ #
213
+ def raw_duration
214
+ return nil unless valid?
215
+
216
+ /Duration:\s*([0-9\:\.]+),/.match(@raw_metadata)[1]
217
+ end
218
+
219
+ #
220
+ # The duration of the movie in milliseconds, as an integer.
221
+ #
222
+ # Example:
223
+ #
224
+ # 24400 # 24.4 seconds
225
+ #
226
+ # Note that the precision of the duration is in tenths of a second, not
227
+ # thousandths, but milliseconds are a more standard unit of time than
228
+ # deciseconds.
229
+ #
230
+
231
+ def duration
232
+ return nil unless valid?
233
+
234
+ units = raw_duration.split(":")
235
+ (units[0].to_i * 60 * 60 * 1000) + (units[1].to_i * 60 * 1000) + (units[2].to_f * 1000).to_i
236
+ end
237
+
238
+ #
239
+ # The bitrate of the movie.
240
+ #
241
+ # Example:
242
+ #
243
+ # 3132
244
+ #
245
+
246
+ def bitrate
247
+ return nil unless valid?
248
+
249
+ bitrate_match[1].to_i
250
+ end
251
+
252
+ #
253
+ # The bitrate units used. In practice, this may always be kb/s.
254
+ #
255
+ # Example:
256
+ #
257
+ # "kb/s"
258
+ #
259
+
260
+ def bitrate_units
261
+ return nil unless valid?
262
+
263
+ bitrate_match[2]
264
+ end
265
+
266
+ def audio_bitrate # :nodoc:
267
+ nil
268
+ end
269
+
270
+ def audio_stream
271
+ return nil unless valid?
272
+
273
+ #/\n\s*Stream.*Audio:.*\n/.match(@raw_response)[0].strip
274
+ match = /\n\s*Stream.*Audio:.*\n/.match(@raw_response)
275
+ return match[0].strip if match
276
+ end
277
+
278
+ #
279
+ # The audio codec used.
280
+ #
281
+ # Example:
282
+ #
283
+ # "aac"
284
+ #
285
+
286
+ def audio_codec
287
+ return nil unless audio?
288
+
289
+ audio_match[2]
290
+ end
291
+
292
+ #
293
+ # The sampling rate of the audio stream.
294
+ #
295
+ # Example:
296
+ #
297
+ # 44100
298
+ #
299
+
300
+ def audio_sample_rate
301
+ return nil unless audio?
302
+
303
+ audio_match[3].to_i
304
+ end
305
+
306
+ #
307
+ # The units used for the sampling rate. May always be Hz.
308
+ #
309
+ # Example:
310
+ #
311
+ # "Hz"
312
+ #
313
+
314
+ def audio_sample_units
315
+ return nil unless audio?
316
+
317
+ audio_match[4]
318
+ end
319
+
320
+ #
321
+ # The channels used in the audio stream.
322
+ #
323
+ # Examples:
324
+ # "stereo"
325
+ # "mono"
326
+ # "5:1"
327
+ #
328
+
329
+ def audio_channels_string
330
+ return nil unless audio?
331
+
332
+ audio_match[5]
333
+ end
334
+
335
+ def audio_channels
336
+ return nil unless audio?
337
+
338
+ case audio_match[5]
339
+ when "mono"
340
+ 1
341
+ when "stereo"
342
+ 2
343
+ else
344
+ raise RuntimeError, "Unknown number of channels: #{audio_channels}"
345
+ end
346
+ end
347
+
348
+ #
349
+ # The ID of the audio stream (useful for troubleshooting).
350
+ #
351
+ # Example:
352
+ # #0.1
353
+ #
354
+
355
+ def audio_stream_id
356
+ return nil unless audio?
357
+
358
+ audio_match[1]
359
+ end
360
+
361
+ def video_stream
362
+ return nil unless valid?
363
+
364
+ match = /\n\s*Stream.*Video:.*\n/.match(@raw_response)
365
+ return match[0].strip unless match.nil?
366
+ nil
367
+ end
368
+
369
+ #
370
+ # The ID of the video stream (useful for troubleshooting).
371
+ #
372
+ # Example:
373
+ # #0.0
374
+ #
375
+
376
+ def video_stream_id
377
+ return nil unless video?
378
+
379
+ video_match[1]
380
+ end
381
+
382
+ #
383
+ # The video codec used.
384
+ #
385
+ # Example:
386
+ #
387
+ # "mpeg4"
388
+ #
389
+
390
+ def video_codec
391
+ return nil unless video?
392
+
393
+ video_match[2]
394
+ end
395
+
396
+ #
397
+ # The colorspace of the video stream.
398
+ #
399
+ # Example:
400
+ #
401
+ # "yuv420p"
402
+ #
403
+
404
+ def video_colorspace
405
+ return nil unless video?
406
+
407
+ video_match[3]
408
+ end
409
+
410
+ #
411
+ # The width of the video in pixels.
412
+ #
413
+
414
+ def width
415
+ return nil unless video?
416
+
417
+ video_match[4].to_i
418
+ end
419
+
420
+ #
421
+ # The height of the video in pixels.
422
+ #
423
+
424
+ def height
425
+ return nil unless video?
426
+
427
+ video_match[5].to_i
428
+ end
429
+
430
+ #
431
+ # width x height, as a string.
432
+ #
433
+ # Examples:
434
+ # 320x240
435
+ # 1280x720
436
+ #
437
+
438
+ def resolution
439
+ return nil unless video?
440
+
441
+ "#{width}x#{height}"
442
+ end
443
+
444
+ #
445
+ # The frame rate of the video in frames per second
446
+ #
447
+ # Example:
448
+ #
449
+ # "29.97"
450
+ #
451
+
452
+ def fps
453
+ return nil unless video?
454
+
455
+ /([0-9\.]+) fps/.match(video_stream)[1]
456
+ end
457
+
458
+ private
459
+
460
+ def bitrate_match
461
+ /bitrate: ([0-9\.]+)\s*(.*)\s+/.match(@raw_metadata)
462
+ end
463
+
464
+ def audio_match
465
+ return nil unless valid?
466
+
467
+ /Stream\s*(.*?)[,|:|\(|\[].*?\s*Audio:\s*(.*?),\s*([0-9\.]*) (\w*),\s*([a-zA-Z:]*)/.match(audio_stream)
468
+ end
469
+
470
+ def video_match
471
+ return nil unless valid?
472
+
473
+ match = /Stream\s*(.*?)[,|:|\(|\[].*?\s*Video:\s*(.*?),\s*(.*?),\s*(\d*)x(\d*)/.match(video_stream)
474
+
475
+ # work-around for Apple Intermediate format, which does not have a color space
476
+ # I fake up a match data object (yea, duck typing!) with an empty spot where
477
+ # the color space would be.
478
+ if match.nil?
479
+ match = /Stream\s*(.*?)[,|:|\(|\[].*?\s*Video:\s*(.*?),\s*(\d*)x(\d*)/.match(video_stream)
480
+ match = [nil, match[1], match[2], nil, match[3], match[4]] unless match.nil?
481
+ end
482
+
483
+ match
484
+ end
485
+ end
486
+ end