debprado-rvideo 0.9.6

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.
data/License.txt ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2007 Jonathan Dahl and Slantwise Design
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.txt ADDED
@@ -0,0 +1,91 @@
1
+ RVideo
2
+
3
+ RVideo allows you to inspect and process video files.
4
+
5
+
6
+ Installation is a little involved. First, install the gem:
7
+
8
+ sudo gem install rvideo
9
+
10
+ Next, install ffmpeg and (possibly) other related libraries. This is
11
+ documented elsewhere on the web, and can be a headache. If you are on OS X,
12
+ the Darwinports build is reasonably good (though not perfect). Install with:
13
+
14
+ sudo port install ffmpeg
15
+
16
+ Or, for a better build (recommended), add additional video- and audio-related
17
+ libraries, like this:
18
+
19
+ sudo port install ffmpeg +lame +libogg +vorbis +faac +faad +xvid +x264 +a52
20
+
21
+ Most package management systems include a build of ffmpeg, but many include a
22
+ poor build. So you may need to compile from scratch.
23
+
24
+ If you want to create Flash Video files, also install flvtool2:
25
+
26
+ sudo gem install flvtool2
27
+
28
+ Once ffmpeg and RVideo are installed, you're set.
29
+
30
+ To inspect a file, initialize an RVideo file inspector object. See the
31
+ documentation for details.
32
+
33
+ A few examples:
34
+
35
+ file = RVideo::Inspector.new(:file => "#{APP_ROOT}/files/input.mp4")
36
+
37
+ file = RVideo::Inspector.new(:raw_response => @existing_response)
38
+
39
+ file = RVideo::Inspector.new(:file => "#{APP_ROOT}/files/input.mp4",
40
+ :ffmpeg_binary => "#{APP_ROOT}/bin/ffmpeg")
41
+
42
+ file.fps # "29.97"
43
+ file.duration # "00:05:23.4"
44
+
45
+ To transcode a video, initialize a Transcoder object.
46
+
47
+ transcoder = RVideo::Transcoder.new
48
+
49
+ Then pass a command and valid options to the execute method
50
+
51
+ recipe = "ffmpeg -i $input_file$ -ar 22050 -ab 64 -f flv -r 29.97 -s"
52
+ recipe += " $resolution$ -y $output_file$"
53
+ recipe += "\nflvtool2 -U $output_file$"
54
+ begin
55
+ transcoder.execute(recipe, {:input_file => "/path/to/input.mp4",
56
+ :output_file => "/path/to/output.flv", :resolution => "640x360"})
57
+ rescue TranscoderError => e
58
+ puts "Unable to transcode file: #{e.class} - #{e.message}"
59
+ end
60
+
61
+ If the job succeeds, you can access the metadata of the input and output
62
+ files with:
63
+
64
+ transcoder.original # RVideo::Inspector object
65
+ transcoder.processed # RVideo::Inspector object
66
+
67
+ If the transcoding succeeds, the file may still have problems. RVideo
68
+ will populate an errors array if the duration of the processed video
69
+ differs from the duration of the original video, or if the processed
70
+ file is unreadable.
71
+
72
+ Thanks to Peter Boling for early work on RVideo.
73
+
74
+ Contribute to RVideo! If you want to help out, there are a few things you can
75
+ do.
76
+
77
+ - Use, test, and submit bugs/patches
78
+ - We need a RVideo::Tools::Mencoder class to add mencoder support.
79
+ - Other tool classes would be great - On2, mp4box, Quicktime (?), etc.
80
+ - Submit other fixes, features, optimizations, and refactorings
81
+
82
+ If RVideo is useful to you, you may also be interested in RMovie, another Ruby
83
+ video library. See http://rmovie.rubyforge.org/ for more.
84
+
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
+ distributed transcoding, a web-based transcoder dashboard, and more. See
88
+ http://zencoder.tv or http://slantwisedesign.com for more.
89
+
90
+ Copyright (c) 2007 Jonathan Dahl and Slantwise Design. Released under the MIT
91
+ license.
data/RULES ADDED
@@ -0,0 +1,11 @@
1
+ Collection of transcoding edge cases and rules
2
+ ------------------------
3
+
4
+ * mpeg4 output errors out if frame rate is not supplied
5
+
6
+ [mpeg4 @ 0x149e810]timebase not supported by mpeg 4 standard
7
+ Error while opening codec for output stream #0.0 - maybe incorrect parameters such as bit_rate, rate, width or height
8
+
9
+ Solution: provide a frame rate with -r (any frame rate will do)
10
+
11
+
@@ -0,0 +1,10 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = "debprado-rvideo"
3
+ s.version = "0.9.6"
4
+ s.summary = "RVideo is a Ruby library for inspecting and transcoding video and audio files."
5
+ s.homepage = "http://gemcutter.org/gems/debprado-rvideo"
6
+ s.description = "Gives you video"
7
+ s.has_rdoc = false
8
+ s.authors = ["Adam Singer", "Zencoder"]
9
+ s.files = ["lib", "License.txt", "debprado-rvideo.gemspec", "RULES", "README.txt", "lib/rvideo.rb", "lib/rvideo/errors.rb", "lib/rvideo/float.rb", "lib/rvideo/inspector.rb", "lib/rvideo/transcoder.rb", "lib/rvideo/version.rb", "lib/rvideo/tools/abstract_tool.rb", "lib/rvideo/tools/ffmpeg.rb", "lib/rvideo/tools/ffmpeg2theora.rb", "lib/rvideo/tools/flvtool2.rb", "lib/rvideo/tools/mencoder.rb", "lib/rvideo/tools/mp4box.rb", "lib/rvideo/tools/mp4creator.rb", "lib/rvideo/tools/mplayer.rb", "lib/rvideo/tools/yamdi.rb"]
10
+ end
data/lib/rvideo.rb ADDED
@@ -0,0 +1,22 @@
1
+ $LOAD_PATH.unshift File.dirname(__FILE__) + '/rvideo'
2
+
3
+ require 'inspector'
4
+ require 'float'
5
+ require 'tools/abstract_tool'
6
+ require 'tools/ffmpeg'
7
+ require 'tools/mencoder'
8
+ require 'tools/flvtool2'
9
+ require 'tools/mp4box'
10
+ require 'tools/mplayer'
11
+ require 'tools/mp4creator'
12
+ require 'tools/ffmpeg2theora'
13
+ require 'tools/yamdi'
14
+ require 'errors'
15
+ require 'transcoder'
16
+ require 'active_support'
17
+
18
+ TEMP_PATH = File.expand_path(File.dirname(__FILE__) + '/../tmp')
19
+ FIXTURE_PATH = File.expand_path(File.dirname(__FILE__) + '/../spec/fixtures')
20
+ TEST_FILE_PATH = File.expand_path(File.dirname(__FILE__) + '/../spec/files')
21
+ REPORT_PATH = File.expand_path(File.dirname(__FILE__) + '/../report')
22
+
@@ -0,0 +1,24 @@
1
+ module RVideo
2
+ class TranscoderError < RuntimeError
3
+ class InvalidCommand < TranscoderError
4
+ end
5
+
6
+ class InvalidFile < TranscoderError
7
+ end
8
+
9
+ class InputFileNotFound < TranscoderError
10
+ end
11
+
12
+ class UnexpectedResult < TranscoderError
13
+ end
14
+
15
+ class ParameterError < TranscoderError
16
+ end
17
+
18
+ class UnknownError < TranscoderError
19
+ end
20
+
21
+ class UnknownTool < TranscoderError
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,7 @@
1
+ # Add a rounding method to the Float class.
2
+ class Float
3
+ def round_to(x)
4
+ (self * 10**x).round.to_f / 10**x
5
+ end
6
+ end
7
+
@@ -0,0 +1,545 @@
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 TranscoderError::InputFileNotFound, "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 \#.*)\n[Must|At least]/m.match(@raw_response)
54
+
55
+ if /Unknown format/i.match(@raw_response) || metadata.nil?
56
+ @unknown_format = true
57
+ elsif /Duration: N\/A/im.match(@raw_response)
58
+ # elsif /Duration: N\/A|bitrate: N\/A/im.match(@raw_response)
59
+ @unreadable_file = true
60
+ @raw_metadata = metadata[1] # in this case, we can at least still get the container type
61
+ else
62
+ @raw_metadata = metadata[1]
63
+ end
64
+ end
65
+
66
+ #
67
+ # Returns true if the file can be read successfully. Returns false otherwise.
68
+ #
69
+
70
+ def valid?
71
+ if @unknown_format or @unreadable_file
72
+ false
73
+ else
74
+ true
75
+ end
76
+ end
77
+
78
+ #
79
+ # Returns false if the file can be read successfully. Returns false otherwise.
80
+ #
81
+
82
+ def invalid?
83
+ !valid?
84
+ end
85
+
86
+ #
87
+ # True if the format is not understood ("Unknown Format")
88
+ #
89
+
90
+ def unknown_format?
91
+ if @unknown_format
92
+ true
93
+ else
94
+ false
95
+ end
96
+ end
97
+
98
+ #
99
+ # True if the file is not readable ("Duration: N/A, bitrate: N/A")
100
+ #
101
+
102
+ def unreadable_file?
103
+ if @unreadable_file
104
+ true
105
+ else
106
+ false
107
+ end
108
+ end
109
+
110
+ #
111
+ # Does the file have an audio stream?
112
+ #
113
+
114
+ def audio?
115
+ if audio_match.nil?
116
+ false
117
+ else
118
+ true
119
+ end
120
+ end
121
+
122
+ #
123
+ # Does the file have a video stream?
124
+ #
125
+
126
+ def video?
127
+ if video_match.nil?
128
+ false
129
+ else
130
+ true
131
+ end
132
+ end
133
+
134
+ #
135
+ # Take a screengrab of a movie. Requires an input file and a time parameter, and optionally takes an output filename. If no output filename is specfied, constructs one.
136
+ #
137
+ # Three types of time parameters are accepted - percentage (e.g. 3%), time in seconds (e.g. 60 seconds), and raw frame (e.g. 37). Will raise an exception if the time in seconds or the frame are out of the bounds of the input file.
138
+ #
139
+ # Types:
140
+ # 37s (37 seconds)
141
+ # 37f (frame 37)
142
+ # 37% (37 percent)
143
+ # 37 (default to seconds)
144
+ #
145
+ # If a time is outside of the duration of the file, it will choose a frame at the 99% mark.
146
+ #
147
+ # Example:
148
+ #
149
+ # t = RVideo::Transcoder.new('path/to/input_file.mp4')
150
+ # t.capture_frame('10%') # => '/path/to/screenshot/input-10p.jpg'
151
+ #
152
+
153
+ def capture_frame(timecode, output_file = nil)
154
+ t = calculate_time(timecode)
155
+ unless output_file
156
+ output_file = "#{TEMP_PATH}/#{File.basename(@full_filename, ".*")}-#{timecode.gsub("%","p")}.jpg"
157
+ end
158
+ # do the work
159
+ # mplayer $input_file$ -ss $start_time$ -frames 1 -vo jpeg -o $output_file$
160
+ # ffmpeg -i $input_file$ -v nopb -ss $start_time$ -b $bitrate$ -an -vframes 1 -y $output_file$
161
+ command = "ffmpeg -i #{@full_filename} -ss #{t} -t 00:00:01 -r 1 -vframes 1 -f image2 #{output_file}"
162
+ Transcoder.logger.info("\nCreating Screenshot: #{command}\n")
163
+ frame_result = `#{command} 2>&1`
164
+ Transcoder.logger.info("\nScreenshot results: #{frame_result}")
165
+ output_file
166
+ end
167
+
168
+ def calculate_time(timecode)
169
+ m = /\A([0-9\.\,]*)(s|f|%)?\Z/.match(timecode)
170
+ if m.nil? or m[1].nil? or m[1].empty?
171
+ raise TranscoderError::ParameterError, "Invalid timecode for frame capture: #{timecode}. Must be a number, optionally followed by s, f, or %."
172
+ end
173
+
174
+ case m[2]
175
+ when "s", nil
176
+ t = m[1].to_f
177
+ when "f"
178
+ t = m[1].to_f / fps.to_f
179
+ when "%"
180
+ # milliseconds / 1000 * percent / 100
181
+ t = (duration.to_i / 1000.0) * (m[1].to_f / 100.0)
182
+ else
183
+ raise TranscoderError::ParameterError, "Invalid timecode for frame capture: #{timecode}. Must be a number, optionally followed by s, f, or p."
184
+ end
185
+
186
+ if (t * 1000) > duration
187
+ calculate_time("99%")
188
+ else
189
+ t
190
+ end
191
+ end
192
+
193
+ #
194
+ # Returns the version of ffmpeg used, In practice, this may or may not be
195
+ # useful.
196
+ #
197
+ # Examples:
198
+ #
199
+ # SVN-r6399
200
+ # CVS
201
+ #
202
+
203
+ def ffmpeg_version
204
+ @ffmpeg_version = @raw_response.split("\n").first.split("version").last.split(",").first.strip
205
+ end
206
+
207
+ #
208
+ # Returns the configuration options used to build ffmpeg.
209
+ #
210
+ # Example:
211
+ #
212
+ # --enable-mp3lame --enable-gpl --disable-ffplay --disable-ffserver
213
+ # --enable-a52 --enable-xvid
214
+ #
215
+
216
+ def ffmpeg_configuration
217
+ /(\s*configuration:)(.*)\n/.match(@raw_response)[2].strip
218
+ end
219
+
220
+ #
221
+ # Returns the versions of libavutil, libavcodec, and libavformat used by
222
+ # ffmpeg.
223
+ #
224
+ # Example:
225
+ #
226
+ # libavutil version: 49.0.0
227
+ # libavcodec version: 51.9.0
228
+ # libavformat version: 50.4.0
229
+ #
230
+
231
+ def ffmpeg_libav
232
+ /^(\s*lib.*\n)+/.match(@raw_response)[0].split("\n").each {|l| l.strip! }
233
+ end
234
+
235
+ #
236
+ # Returns the build description for ffmpeg.
237
+ #
238
+ # Example:
239
+ #
240
+ # built on Apr 15 2006 04:58:19, gcc: 4.0.1 (Apple Computer, Inc. build
241
+ # 5250)
242
+ #
243
+
244
+ def ffmpeg_build
245
+ /(\n\s*)(built on.*)(\n)/.match(@raw_response)[2]
246
+ end
247
+
248
+ #
249
+ # Returns the container format for the file. Instead of returning a single
250
+ # format, this may return a string of related formats.
251
+ #
252
+ # Examples:
253
+ #
254
+ # "avi"
255
+ #
256
+ # "mov,mp4,m4a,3gp,3g2,mj2"
257
+ #
258
+
259
+ def container
260
+ return nil if @unknown_format
261
+
262
+ /Input \#\d+\,\s*(\S+),\s*from/.match(@raw_metadata)[1]
263
+ end
264
+
265
+ #
266
+ # The duration of the movie, as a string.
267
+ #
268
+ # Example:
269
+ #
270
+ # "00:00:24.4" # 24.4 seconds
271
+ #
272
+ def raw_duration
273
+ return nil unless valid?
274
+
275
+ /Duration:\s*([0-9\:\.]+),/.match(@raw_metadata)[1]
276
+ end
277
+
278
+ #
279
+ # The duration of the movie in milliseconds, as an integer.
280
+ #
281
+ # Example:
282
+ #
283
+ # 24400 # 24.4 seconds
284
+ #
285
+ # Note that the precision of the duration is in tenths of a second, not
286
+ # thousandths, but milliseconds are a more standard unit of time than
287
+ # deciseconds.
288
+ #
289
+
290
+ def duration
291
+ return nil unless valid?
292
+
293
+ units = raw_duration.split(":")
294
+ (units[0].to_i * 60 * 60 * 1000) + (units[1].to_i * 60 * 1000) + (units[2].to_f * 1000).to_i
295
+ end
296
+
297
+ #
298
+ # The bitrate of the movie.
299
+ #
300
+ # Example:
301
+ #
302
+ # 3132
303
+ #
304
+
305
+ def bitrate
306
+ return nil unless valid?
307
+
308
+ bitrate_match[1].to_i
309
+ end
310
+
311
+ #
312
+ # The bitrate units used. In practice, this may always be kb/s.
313
+ #
314
+ # Example:
315
+ #
316
+ # "kb/s"
317
+ #
318
+
319
+ def bitrate_units
320
+ return nil unless valid?
321
+
322
+ bitrate_match[2]
323
+ end
324
+
325
+ def audio_bit_rate # :nodoc:
326
+ nil
327
+ end
328
+
329
+ def audio_stream
330
+ return nil unless valid?
331
+
332
+ #/\n\s*Stream.*Audio:.*\n/.match(@raw_response)[0].strip
333
+ match = /\n\s*Stream.*Audio:.*\n/.match(@raw_response)
334
+ return match[0].strip if match
335
+ end
336
+
337
+ #
338
+ # The audio codec used.
339
+ #
340
+ # Example:
341
+ #
342
+ # "aac"
343
+ #
344
+
345
+ def audio_codec
346
+ return nil unless audio?
347
+
348
+ audio_match[2]
349
+ end
350
+
351
+ #
352
+ # The sampling rate of the audio stream.
353
+ #
354
+ # Example:
355
+ #
356
+ # 44100
357
+ #
358
+
359
+ def audio_sample_rate
360
+ return nil unless audio?
361
+
362
+ audio_match[3].to_i
363
+ end
364
+
365
+ #
366
+ # The units used for the sampling rate. May always be Hz.
367
+ #
368
+ # Example:
369
+ #
370
+ # "Hz"
371
+ #
372
+
373
+ def audio_sample_units
374
+ return nil unless audio?
375
+
376
+ audio_match[4]
377
+ end
378
+
379
+ #
380
+ # The channels used in the audio stream.
381
+ #
382
+ # Examples:
383
+ # "stereo"
384
+ # "mono"
385
+ # "5:1"
386
+ #
387
+
388
+ def audio_channels_string
389
+ return nil unless audio?
390
+
391
+ audio_match[5]
392
+ end
393
+
394
+ def audio_channels
395
+ return nil unless audio?
396
+
397
+ case audio_match[5]
398
+ when "mono"
399
+ 1
400
+ when "stereo"
401
+ 2
402
+ else
403
+ raise RuntimeError, "Unknown number of channels: #{audio_channels}"
404
+ end
405
+ end
406
+
407
+ #
408
+ # The ID of the audio stream (useful for troubleshooting).
409
+ #
410
+ # Example:
411
+ # #0.1
412
+ #
413
+
414
+ def audio_stream_id
415
+ return nil unless audio?
416
+
417
+ audio_match[1]
418
+ end
419
+
420
+ def video_stream
421
+ return nil unless valid?
422
+
423
+ match = /\n\s*Stream.*Video:.*\n/.match(@raw_response)
424
+ return match[0].strip unless match.nil?
425
+ nil
426
+ end
427
+
428
+ #
429
+ # The ID of the video stream (useful for troubleshooting).
430
+ #
431
+ # Example:
432
+ # #0.0
433
+ #
434
+
435
+ def video_stream_id
436
+ return nil unless video?
437
+
438
+ video_match[1]
439
+ end
440
+
441
+ #
442
+ # The video codec used.
443
+ #
444
+ # Example:
445
+ #
446
+ # "mpeg4"
447
+ #
448
+
449
+ def video_codec
450
+ return nil unless video?
451
+
452
+ video_match[2]
453
+ end
454
+
455
+ #
456
+ # The colorspace of the video stream.
457
+ #
458
+ # Example:
459
+ #
460
+ # "yuv420p"
461
+ #
462
+
463
+ def video_colorspace
464
+ return nil unless video?
465
+
466
+ video_match[3]
467
+ end
468
+
469
+ #
470
+ # The width of the video in pixels.
471
+ #
472
+
473
+ def width
474
+ return nil unless video?
475
+
476
+ video_match[4].to_i
477
+ end
478
+
479
+ #
480
+ # The height of the video in pixels.
481
+ #
482
+
483
+ def height
484
+ return nil unless video?
485
+
486
+ video_match[5].to_i
487
+ end
488
+
489
+ #
490
+ # width x height, as a string.
491
+ #
492
+ # Examples:
493
+ # 320x240
494
+ # 1280x720
495
+ #
496
+
497
+ def resolution
498
+ return nil unless video?
499
+
500
+ "#{width}x#{height}"
501
+ end
502
+
503
+ #
504
+ # The frame rate of the video in frames per second
505
+ #
506
+ # Example:
507
+ #
508
+ # "29.97"
509
+ #
510
+
511
+ def fps
512
+ return nil unless video?
513
+
514
+ /([0-9\.]+) (fps|tb)/.match(video_stream)[1]
515
+ end
516
+
517
+ private
518
+
519
+ def bitrate_match
520
+ /bitrate: ([0-9\.]+)\s*(.*)\s+/.match(@raw_metadata)
521
+ end
522
+
523
+ def audio_match
524
+ return nil unless valid?
525
+
526
+ /Stream\s*(.*?)[,|:|\(|\[].*?\s*Audio:\s*(.*?),\s*([0-9\.]*) (\w*),\s*([a-zA-Z:]*)/.match(audio_stream)
527
+ end
528
+
529
+ def video_match
530
+ return nil unless valid?
531
+
532
+ match = /Stream\s*(.*?)[,|:|\(|\[].*?\s*Video:\s*(.*?),\s*(.*?),\s*(\d*)x(\d*)/.match(video_stream)
533
+
534
+ # work-around for Apple Intermediate format, which does not have a color space
535
+ # I fake up a match data object (yea, duck typing!) with an empty spot where
536
+ # the color space would be.
537
+ if match.nil?
538
+ match = /Stream\s*(.*?)[,|:|\(|\[].*?\s*Video:\s*(.*?),\s*(\d*)x(\d*)/.match(video_stream)
539
+ match = [nil, match[1], match[2], nil, match[3], match[4]] unless match.nil?
540
+ end
541
+
542
+ match
543
+ end
544
+ end
545
+ end