ez_video 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,41 @@
1
+ # Compiled source #
2
+ ###################
3
+ *.com
4
+ *.class
5
+ *.dll
6
+ *.exe
7
+ *.o
8
+ *.so
9
+ *.gem
10
+ .yardoc
11
+
12
+ # Packages #
13
+ ############
14
+ # it's better to unpack these files and commit the raw source
15
+ # git has its own built in compression methods
16
+ *.7z
17
+ *.dmg
18
+ *.gz
19
+ *.iso
20
+ *.jar
21
+ *.rar
22
+ *.tar
23
+ *.zip
24
+
25
+ # Logs and databases #
26
+ ######################
27
+ *.log
28
+ *.sql
29
+ *.sqlite
30
+
31
+ # OS generated files #
32
+ ######################
33
+ .DS_Store*
34
+ ehthumbs.db
35
+ Icon?
36
+ Thumbs.db
37
+
38
+ # IDE generated files #
39
+ #######################
40
+ .idea
41
+ *~
@@ -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.
@@ -0,0 +1,132 @@
1
+ # EZHttp
2
+
3
+ A wrapper for ruby net/http, supports http/https, RESTful methods, headers, certificate and file uploads.
4
+ It supports both command line and ruby code.
5
+
6
+ ## How to use it
7
+
8
+ ### Command Line
9
+
10
+ Send with query string
11
+
12
+ ezhttp \
13
+ --url "https://api.twitter.com/1/followers/ids.json" \
14
+ --method "get" \
15
+ --data "cursor=-1&screen_name=twitterapi"
16
+
17
+ Send with query string
18
+
19
+ ezhttp \
20
+ --url "https://api.twitter.com/1/followers/ids.json&cursor=-1&screen_name=twitterapi" \
21
+ --method "get"
22
+
23
+ Send with json
24
+
25
+ ezhttp \
26
+ --url 'http://127.0.0.1:3000/file/upload_file' \
27
+ --data '{"name":{"fn":"xxx","ln":"xxx"}}' \
28
+ --method 'post' \
29
+ --type 'application/json'
30
+
31
+ Send with header
32
+
33
+ ezhttp \
34
+ --url 'https://api.twitter.com/oauth/request_token' \
35
+ --method 'post' \
36
+ --header 'Authorization: OAuth oauth_nonce="K7ny27JTpKVsTgdyLdDfmQQWVLERj2zAK5BslRsqyw", oauth_callback="http%3A%2F%2Fmyapp.com%3A3005%2Ftwitter%2Fprocess_callback", oauth_signature_method="HMAC-SHA1", oauth_timestamp="1300228849", oauth_consumer_key="OqEqJeafRSF11jBMStrZz", oauth_signature="Pc%2BMLdv028fxCErFyi8KXFM%2BddU%3D", oauth_version="1.0"'
37
+
38
+
39
+ Upload file
40
+
41
+ ezhttp \
42
+ --url 'http://127.0.0.1:3000/file/upload_file' \
43
+ --files 'path_to_file.png'
44
+
45
+ Upload multiple files with header
46
+
47
+ ezhttp \
48
+ --url 'http://127.0.0.1:3000/file/upload_file' \
49
+ --files 'path_to_file1.zip','path_to_file2.jpg' \
50
+ --header 'authorization:Basic Zvsdwegbdgegsdv0xvsd='
51
+
52
+ ### Ruby
53
+
54
+ Send with encoded query string as data
55
+
56
+ # Post request
57
+ response = EZHttp.Post("https://www.example.com:83/api",
58
+ "user_id=12345&token=sdfwD12g%7Ecc")
59
+
60
+ # Get request
61
+ response = EZHttp.Get("http://www.example.com/api",
62
+ "user_id=12345&token=sdfwD12g%7Ecc")
63
+
64
+ # OR
65
+ response = EZHttp.Get("http://www.example.com/api?user_id=12345&token=sdfwD12g%7Ecc")
66
+
67
+ Send with hash as data
68
+
69
+ # Post request
70
+ response = EZHttp.Post("https://www.example.com:83/api",
71
+ {"name1"=>"value", "name2" => "value2"})
72
+
73
+ # Put request
74
+ response = EZHttp.Put("https://www.example.com:83/api",
75
+ {"name1"=>"value", "name2" => "value2"})
76
+
77
+ Send with extra headers
78
+
79
+ response = EZHttp.Post("https://www.example.com:83/api",
80
+ "user_id=12345&token=sdfwD12g%7Ecc",
81
+ nil,
82
+ [
83
+ "authentication:oAuth username=xxx&password=xxx",
84
+ "other_header:other_values"
85
+ ])
86
+
87
+ Send with pem certificate
88
+
89
+ response = EZHttp.Delete("https://www.example.com:83/api",
90
+ {"user_id"=>"12345"},
91
+ "application/json",
92
+ nil,
93
+ "/path_to_cert.pem")
94
+
95
+ Upload file(s)
96
+
97
+ #
98
+ files = []
99
+ file = File.open("path_to_file.extension", "rb")
100
+ files.push({"name" => File.basename(file), "content" => file.read})
101
+ file.close
102
+
103
+ # simply upload file
104
+ response = EZHttp.Upload("https://www.example.com:83/api",
105
+ files)
106
+
107
+ # upload file with headers
108
+ response = EZHttp.Upload("https://www.example.com:83/api",
109
+ files,
110
+ ["authorization:Basic Zvsdwegbdgegsdv0xvsd="])
111
+
112
+ Display response
113
+
114
+ puts response.body
115
+
116
+ ## Installation
117
+
118
+ Add the following line to rails "Gemfile"
119
+
120
+ gem "ez_http"
121
+
122
+ then execute
123
+
124
+ $ bundle install
125
+
126
+
127
+ See [http://rubygems.org/gems/ez_http](http://rubygems.org/gems/ez_http "EZHttp RubyGem Page") for more details
128
+
129
+ ## Authors
130
+
131
+ Tianyu Huang
132
+
@@ -0,0 +1,28 @@
1
+ lib = File.expand_path("../lib/", __FILE__)
2
+ $:.unshift lib unless $:.include?(lib)
3
+
4
+ require "rvideo/version"
5
+
6
+ Gem::Specification.new do |s|
7
+
8
+ # Basic info
9
+ s.name = "ez_video"
10
+ s.version = RVideo::VERSION::STRING
11
+ s.platform = Gem::Platform::RUBY
12
+ s.date = "2012-06-22"
13
+ s.summary = "A fork of Rvideo, have some bugs fixed."
14
+ s.description = "A video reader/converter, ruby wrapper for ffmpeg."
15
+ s.authors = ["Tianyu Huang"]
16
+ s.email = ["tianhsky@yahoo.com"]
17
+ s.homepage = "http://rubygems.org/gems/ez_video"
18
+
19
+ # Dependencies
20
+ #s.required_rubygems_version = ">= 1.8.22"
21
+
22
+ # Files
23
+ s.files = `git ls-files`.split("\n")
24
+ s.require_paths = ["lib"]
25
+ s.executables = `git ls-files -- bin/*`.split("\n").map { |f| File.basename(f) }
26
+ s.extra_rdoc_files = ["README.md"]
27
+
28
+ end
@@ -0,0 +1,15 @@
1
+ require 'rvideo/inspector'
2
+ require 'rvideo/float'
3
+ require 'rvideo/tools/abstract_tool'
4
+ require 'rvideo/tools/ffmpeg'
5
+ require 'rvideo/tools/mencoder'
6
+ require 'rvideo/tools/flvtool2'
7
+ require 'rvideo/tools/mp4box'
8
+ require 'rvideo/tools/mplayer'
9
+ require 'rvideo/tools/mp4creator'
10
+ require 'rvideo/tools/ffmpeg2theora'
11
+ require 'rvideo/tools/yamdi'
12
+ require 'rvideo/errors'
13
+ require 'rvideo/transcoder'
14
+ require 'active_support'
15
+ require 'shellwords'
@@ -0,0 +1,27 @@
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 OutputFileNotFound < TranscoderError
13
+ end
14
+
15
+ class UnexpectedResult < TranscoderError
16
+ end
17
+
18
+ class ParameterError < TranscoderError
19
+ end
20
+
21
+ class UnknownError < TranscoderError
22
+ end
23
+
24
+ class UnknownTool < TranscoderError
25
+ end
26
+ end
27
+ 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,573 @@
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 (#{@full_filename})" unless FileTest.exist?(@full_filename)
48
+ @raw_response = `#{@ffmpeg_binary} -i #{Shellwords.shellescape @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 = metadata_match
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
+
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
+
178
+ Transcoder.logger.info("\nScreenshot results: #{frame_result}")
179
+ output_file
180
+ end
181
+
182
+ def calculate_time(timecode)
183
+ m = /\A([0-9\.\,]*)(s|f|%)?\Z/.match(timecode)
184
+ if m.nil? or m[1].nil? or m[1].empty?
185
+ raise TranscoderError::ParameterError, "Invalid timecode for frame capture: #{timecode}. Must be a number, optionally followed by s, f, or %."
186
+ end
187
+
188
+ case m[2]
189
+ when "s", nil
190
+ t = m[1].to_f
191
+ when "f"
192
+ t = m[1].to_f / fps.to_f
193
+ when "%"
194
+ # milliseconds / 1000 * percent / 100
195
+ t = (duration.to_i / 1000.0) * (m[1].to_f / 100.0)
196
+ else
197
+ raise TranscoderError::ParameterError, "Invalid timecode for frame capture: #{timecode}. Must be a number, optionally followed by s, f, or p."
198
+ end
199
+
200
+ if (t * 1000) > duration
201
+ calculate_time("99%")
202
+ else
203
+ t
204
+ end
205
+ end
206
+
207
+ #
208
+ # Returns the version of ffmpeg used, In practice, this may or may not be
209
+ # useful.
210
+ #
211
+ # Examples:
212
+ #
213
+ # SVN-r6399
214
+ # CVS
215
+ #
216
+
217
+ def ffmpeg_version
218
+ @ffmpeg_version = @raw_response.split("\n").first.split("version").last.split(",").first.strip
219
+ end
220
+
221
+ #
222
+ # Returns the configuration options used to build ffmpeg.
223
+ #
224
+ # Example:
225
+ #
226
+ # --enable-mp3lame --enable-gpl --disable-ffplay --disable-ffserver
227
+ # --enable-a52 --enable-xvid
228
+ #
229
+
230
+ def ffmpeg_configuration
231
+ /(\s*configuration:)(.*)\n/.match(@raw_response)[2].strip
232
+ end
233
+
234
+ #
235
+ # Returns the versions of libavutil, libavcodec, and libavformat used by
236
+ # ffmpeg.
237
+ #
238
+ # Example:
239
+ #
240
+ # libavutil version: 49.0.0
241
+ # libavcodec version: 51.9.0
242
+ # libavformat version: 50.4.0
243
+ #
244
+
245
+ def ffmpeg_libav
246
+ /^(\s*lib.*\n)+/.match(@raw_response)[0].split("\n").each {|l| l.strip! }
247
+ end
248
+
249
+ #
250
+ # Returns the build description for ffmpeg.
251
+ #
252
+ # Example:
253
+ #
254
+ # built on Apr 15 2006 04:58:19, gcc: 4.0.1 (Apple Computer, Inc. build
255
+ # 5250)
256
+ #
257
+
258
+ def ffmpeg_build
259
+ /(\n\s*)(built on.*)(\n)/.match(@raw_response)[2]
260
+ end
261
+
262
+ #
263
+ # Returns the container format for the file. Instead of returning a single
264
+ # format, this may return a string of related formats.
265
+ #
266
+ # Examples:
267
+ #
268
+ # "avi"
269
+ #
270
+ # "mov,mp4,m4a,3gp,3g2,mj2"
271
+ #
272
+
273
+ def container
274
+ return nil if @unknown_format
275
+
276
+ /Input \#\d+\,\s*(\S+),\s*from/.match(@raw_metadata)[1]
277
+ end
278
+
279
+ #
280
+ # The duration of the movie, as a string.
281
+ #
282
+ # Example:
283
+ #
284
+ # "00:00:24.4" # 24.4 seconds
285
+ #
286
+ def raw_duration
287
+ return nil unless valid?
288
+
289
+ /Duration:\s*([0-9\:\.]+),/.match(@raw_metadata)[1]
290
+ end
291
+
292
+ #
293
+ # The duration of the movie in milliseconds, as an integer.
294
+ #
295
+ # Example:
296
+ #
297
+ # 24400 # 24.4 seconds
298
+ #
299
+ # Note that the precision of the duration is in tenths of a second, not
300
+ # thousandths, but milliseconds are a more standard unit of time than
301
+ # deciseconds.
302
+ #
303
+
304
+ def duration
305
+ return nil unless valid?
306
+
307
+ units = raw_duration.split(":")
308
+ (units[0].to_i * 60 * 60 * 1000) + (units[1].to_i * 60 * 1000) + (units[2].to_f * 1000).to_i
309
+ end
310
+
311
+ #
312
+ # The bitrate of the movie.
313
+ #
314
+ # Example:
315
+ #
316
+ # 3132
317
+ #
318
+
319
+ def bitrate
320
+ return nil unless valid?
321
+
322
+ bitrate_match[1].to_i
323
+ end
324
+
325
+ #
326
+ # The bitrate units used. In practice, this may always be kb/s.
327
+ #
328
+ # Example:
329
+ #
330
+ # "kb/s"
331
+ #
332
+
333
+ def bitrate_units
334
+ return nil unless valid?
335
+
336
+ bitrate_match[2]
337
+ end
338
+
339
+ def audio_bit_rate # :nodoc:
340
+ nil
341
+ end
342
+
343
+ def audio_stream
344
+ return nil unless valid?
345
+
346
+ #/\n\s*Stream.*Audio:.*\n/.match(@raw_response)[0].strip
347
+ match = /\n\s*Stream.*Audio:.*\n/.match(@raw_response)
348
+ return match[0].strip if match
349
+ end
350
+
351
+ #
352
+ # The audio codec used.
353
+ #
354
+ # Example:
355
+ #
356
+ # "aac"
357
+ #
358
+
359
+ def audio_codec
360
+ return nil unless audio?
361
+
362
+ audio_match[2]
363
+ end
364
+
365
+ #
366
+ # The sampling rate of the audio stream.
367
+ #
368
+ # Example:
369
+ #
370
+ # 44100
371
+ #
372
+
373
+ def audio_sample_rate
374
+ return nil unless audio?
375
+
376
+ audio_match[3].to_i
377
+ end
378
+
379
+ #
380
+ # The units used for the sampling rate. May always be Hz.
381
+ #
382
+ # Example:
383
+ #
384
+ # "Hz"
385
+ #
386
+
387
+ def audio_sample_units
388
+ return nil unless audio?
389
+
390
+ audio_match[4]
391
+ end
392
+
393
+ #
394
+ # The channels used in the audio stream.
395
+ #
396
+ # Examples:
397
+ # "stereo"
398
+ # "mono"
399
+ # "5:1"
400
+ #
401
+
402
+ def audio_channels_string
403
+ return nil unless audio?
404
+
405
+ audio_match[5]
406
+ end
407
+
408
+ def audio_channels
409
+ return nil unless audio?
410
+
411
+ case audio_match[5]
412
+ when "mono"
413
+ 1
414
+ when "stereo"
415
+ 2
416
+ when "5.1"
417
+ 6
418
+ else
419
+ raise RuntimeError, "Unknown number of channels: #{audio_match[5]}"
420
+ end
421
+ end
422
+
423
+ #
424
+ # The ID of the audio stream (useful for troubleshooting).
425
+ #
426
+ # Example:
427
+ # #0.1
428
+ #
429
+
430
+ def audio_stream_id
431
+ return nil unless audio?
432
+
433
+ audio_match[1]
434
+ end
435
+
436
+ def video_stream
437
+ return nil unless valid?
438
+
439
+ match = /\n\s*Stream.*Video:.*\n/.match(@raw_response)
440
+ return match[0].strip unless match.nil?
441
+ nil
442
+ end
443
+
444
+ #
445
+ # The ID of the video stream (useful for troubleshooting).
446
+ #
447
+ # Example:
448
+ # #0.0
449
+ #
450
+
451
+ def video_stream_id
452
+ return nil unless video?
453
+
454
+ video_match[1]
455
+ end
456
+
457
+ #
458
+ # The video codec used.
459
+ #
460
+ # Example:
461
+ #
462
+ # "mpeg4"
463
+ #
464
+
465
+ def video_codec
466
+ return nil unless video?
467
+
468
+ video_match[2]
469
+ end
470
+
471
+ #
472
+ # The colorspace of the video stream.
473
+ #
474
+ # Example:
475
+ #
476
+ # "yuv420p"
477
+ #
478
+
479
+ def video_colorspace
480
+ return nil unless video?
481
+
482
+ video_match[3]
483
+ end
484
+
485
+ #
486
+ # The width of the video in pixels.
487
+ #
488
+
489
+ def width
490
+ return nil unless video?
491
+
492
+ video_match[4].to_i
493
+ end
494
+
495
+ #
496
+ # The height of the video in pixels.
497
+ #
498
+
499
+ def height
500
+ return nil unless video?
501
+
502
+ video_match[5].to_i
503
+ end
504
+
505
+ #
506
+ # width x height, as a string.
507
+ #
508
+ # Examples:
509
+ # 320x240
510
+ # 1280x720
511
+ #
512
+
513
+ def resolution
514
+ return nil unless video?
515
+
516
+ "#{width}x#{height}"
517
+ end
518
+
519
+ #
520
+ # The frame rate of the video in frames per second
521
+ #
522
+ # Example:
523
+ #
524
+ # "29.97"
525
+ #
526
+
527
+ def fps
528
+ return nil unless video?
529
+
530
+ /([0-9\.]+) (fps|tb)/.match(video_stream)[1]
531
+ end
532
+
533
+ private
534
+
535
+ def metadata_match
536
+ [
537
+ /(Input \#.*)\nMust/m, # ffmpeg
538
+ /(Input \#.*)\nAt least/m # ffmpeg macports
539
+ ].each do |rgx|
540
+ if md=rgx.match(@raw_response)
541
+ return md
542
+ end
543
+ end
544
+ nil
545
+ end
546
+
547
+ def bitrate_match
548
+ /bitrate: ([0-9\.]+)\s*(.*)\s+/.match(@raw_metadata)
549
+ end
550
+
551
+ def audio_match
552
+ return nil unless valid?
553
+
554
+ /Stream\s*(.*?)[,|:|\(|\[].*?\s*Audio:\s*(.*?),\s*([0-9\.]*) (\w*),\s*([a-zA-Z:5\.1]*)/.match(audio_stream)
555
+ end
556
+
557
+ def video_match
558
+ return nil unless valid?
559
+
560
+ match = /Stream\s*(.*?)[,|:|\(|\[].*?\s*Video:\s*(.*?),\s*(.*?),\s*(\d*)x(\d*)/.match(video_stream)
561
+
562
+ # work-around for Apple Intermediate format, which does not have a color space
563
+ # I fake up a match data object (yea, duck typing!) with an empty spot where
564
+ # the color space would be.
565
+ if match.nil?
566
+ match = /Stream\s*(.*?)[,|:|\(|\[].*?\s*Video:\s*(.*?),\s*(\d*)x(\d*)/.match(video_stream)
567
+ match = [nil, match[1], match[2], nil, match[3], match[4]] unless match.nil?
568
+ end
569
+
570
+ match
571
+ end
572
+ end
573
+ end