video_dimensions 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,3 @@
1
+ Gemfile.lock
2
+ pkg/
3
+ vendor/cache/*.gem
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --colour --format progress
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2012 Robert Speicher
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.md ADDED
@@ -0,0 +1,70 @@
1
+ # video_dimensions
2
+
3
+ Quick and easy video attributes -- width, height, bitrate, codec, duration, framerate.
4
+ Inspired by Sam Stephenson's [Dimensions](https://github.com/sstephenson/dimensions) gem.
5
+
6
+ ## Features
7
+
8
+ Seamlessly supports multiple "backends" for sussing out video attributes, so it
9
+ will try to find whatever utility the system has available (see
10
+ [Supported Backends](#supported-backends)) and use that.
11
+
12
+ ## Examples
13
+
14
+ ```ruby
15
+ require 'video_dimensions'
16
+
17
+ VideoDimensions.dimensions('~/Movies/720p.wmv') # => [1280, 720]
18
+ VideoDimensions.width('~/Movies/720p.wmv') # => 1280
19
+ VideoDimensions.height('~/Movies/720p.wmv') # => 720
20
+ VideoDimensions.bitrate('~/Movies/720p.wmv') # => 5904
21
+ VideoDimensions.codec('~/Movies/720p.wmv') # => "WMV3"
22
+ VideoDimensions.duration('~/Movies/720p.wmv') # => "00:00:02"
23
+ VideoDimensions.framerate('~/Movies/720p.wmv') # => 21.83
24
+
25
+ v = VideoDimensions('~/Movies/720p.wmv')
26
+ v.dimensions # => [1280, 720]
27
+ v.width # => 1280
28
+ v.height # => 720
29
+ v.bitrate # => 5904
30
+ v.codec # => "WMV3"
31
+ v.duration # => "00:00:02"
32
+ v.framerate # => 21.83
33
+ ```
34
+
35
+ ## Requirements
36
+
37
+ One of the supported backends available in the system's `$PATH`.
38
+
39
+ ### Supported Backends
40
+
41
+ * [FFmpeg](http://ffmpeg.org/) - `ffmpeg`
42
+ * [MediaInfo](http://mediainfo.sourceforge.net/en) - `mediainfo`
43
+
44
+ ## Install
45
+
46
+ $ gem install video_dimensions
47
+
48
+ ## Version History
49
+
50
+ **0.3.0** (2013-03-25)
51
+
52
+ * Add `framerate` attribute
53
+
54
+ **0.2.0** (2012-08-25)
55
+
56
+ * Add `duration` attribute
57
+
58
+ **0.1.1** (2012-08-21)
59
+
60
+ * Update FFmpeg backend patterns to match more video types
61
+
62
+ **0.1.0** (2012-08-20)
63
+
64
+ * Initial release.
65
+
66
+ ## Copyright
67
+
68
+ Copyright (c) 2012 Robert Speicher
69
+
70
+ See LICENSE.txt for details.
data/Rakefile ADDED
@@ -0,0 +1,29 @@
1
+ # encoding: utf-8
2
+
3
+ require 'rubygems'
4
+
5
+ begin
6
+ require 'bundler'
7
+ rescue LoadError => e
8
+ warn e.message
9
+ warn "Run `gem install bundler` to install Bundler."
10
+ exit -1
11
+ end
12
+
13
+ begin
14
+ Bundler.setup(:development)
15
+ rescue Bundler::BundlerError => e
16
+ warn e.message
17
+ warn "Run `bundle install` to install missing gems."
18
+ exit e.status_code
19
+ end
20
+
21
+ require 'rake'
22
+
23
+ require 'rspec/core/rake_task'
24
+ RSpec::Core::RakeTask.new
25
+
26
+ task :test => :spec
27
+ task :default => :spec
28
+
29
+ require "bundler/gem_tasks"
@@ -0,0 +1,138 @@
1
+ require 'video_dimensions/backends'
2
+
3
+ # Returns a VideoDimensions instance for determining attributes for the
4
+ # provided video.
5
+ #
6
+ # input - Full path to video being processed
7
+ #
8
+ # Examples
9
+ #
10
+ # v = VideoDimensions('~/Movies/720p.wmv')
11
+ # v.dimensions # => [1280, 720]
12
+ # v.width # => 1280
13
+ # v.height # => 720
14
+ # v.bitrate # => 5904
15
+ # v.codec # => "WMV3"
16
+ # v.duration # => "00:00:02"
17
+ # v.framerate # => 21.83
18
+ #
19
+ # Returns a VideoDimensions::Backends object
20
+ def VideoDimensions(input)
21
+ VideoDimensions::Backends.first_available.new(input)
22
+ end
23
+
24
+ module VideoDimensions
25
+
26
+ # Public: Provides shortcuts to attribute methods
27
+ #
28
+ # Examples
29
+ #
30
+ # VideoDimensions.dimensions('~/Movies/720p.wmv') # => [1280, 720]
31
+ # VideoDimensions.width('~/Movies/720p.wmv') # => 1280
32
+ # VideoDimensions.height('~/Movies/720p.wmv') # => 720
33
+ # VideoDimensions.bitrate('~/Movies/720p.wmv') # => 5904
34
+ # VideoDimensions.codec('~/Movies/720p.wmv') # => "WMV3"
35
+ # VideoDimensions.duration('~/Movies/720p.wmv') # => "00:00:02"
36
+ # VideoDimensions.framerate('~/Movies/720p.wmv') # => 21.83
37
+ class << self
38
+
39
+ # Public: Video dimensions
40
+ #
41
+ # input - Path to video file
42
+ #
43
+ # Examples
44
+ #
45
+ # VideoDimensions('720p.wmv').dimensions # => [1280, 720]
46
+ #
47
+ # Returns video dimensions as an array of width and height in pixels.
48
+ def dimensions(input)
49
+ process(:dimensions, input)
50
+ end
51
+
52
+ # Public: Video width
53
+ #
54
+ # input - Path to video file
55
+ #
56
+ # Examples
57
+ #
58
+ # VideoDimensions('720p.wmv').width # => 1280
59
+ #
60
+ # Returns video width in pixels
61
+ def width(input)
62
+ process(:width, input)
63
+ end
64
+
65
+ # Public: Video height
66
+ #
67
+ # input - Path to video file
68
+ #
69
+ # Examples
70
+ #
71
+ # VideoDimensions('720p.wmv').height # => 720
72
+ #
73
+ # Returns video height in pixels
74
+ def height(input)
75
+ process(:height, input)
76
+ end
77
+
78
+ # Public: Video bitrate
79
+ #
80
+ # input - Path to video file
81
+ #
82
+ # Examples
83
+ #
84
+ # VideoDimensions('720p.wmv').bitrate # => 5904
85
+ #
86
+ # Returns video bitrate in kbps
87
+ def bitrate(input)
88
+ process(:bitrate, input)
89
+ end
90
+
91
+ # Public: Video codec
92
+ #
93
+ # input - Path to video file
94
+ #
95
+ # Examples
96
+ #
97
+ # VideoDimensions('720p.wmv').codec # => "wmv3"
98
+ #
99
+ # Returns video codec ID
100
+ def codec(input)
101
+ process(:codec, input)
102
+ end
103
+
104
+ # Public: Video duration
105
+ #
106
+ # input - Path to video file
107
+ #
108
+ # Examples
109
+ #
110
+ # VideoDimensions('720p.wmv').duration # => "00:00:02"
111
+ #
112
+ # Returns video duration as a string in hh:mm:ss format
113
+ def duration(input)
114
+ process(:duration, input)
115
+ end
116
+
117
+ # Public: Video framerate
118
+ #
119
+ # input - Path to video file
120
+ #
121
+ # Examples
122
+ #
123
+ # VideoDimensions('720p.wmv').framerate # => 21.83
124
+ #
125
+ # Returns video framerate in frames per second as a float
126
+ def framerate(input)
127
+ process(:framerate, input)
128
+ end
129
+
130
+ private
131
+
132
+ def process(msg, input)
133
+ Backends.first_available.new(input).send(msg)
134
+ end
135
+
136
+ end
137
+
138
+ end
@@ -0,0 +1,29 @@
1
+ require 'video_dimensions/backends/base'
2
+
3
+ module VideoDimensions
4
+ module Backends
5
+ autoload :FFmpeg, 'video_dimensions/backends/ffmpeg'
6
+ autoload :MediaInfo, 'video_dimensions/backends/media_info'
7
+
8
+ # Public: Returns the first available Backend class
9
+ #
10
+ # Examples
11
+ #
12
+ # # `ffmpeg` binary is available
13
+ # Backends.first_available # => FFmpeg
14
+ #
15
+ # # `ffmpeg` binary isn't available, but `mediainfo` is
16
+ # Backends.first_available # => MediaInfo
17
+ #
18
+ # Returns the class constant of the first available backend.
19
+ def self.first_available
20
+ available = constants.select { |c| const_get(c).available? }
21
+
22
+ if available.length > 0
23
+ const_get(available.first)
24
+ else
25
+ raise RuntimeError, "Could not find a compatible video attribute backend."
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,34 @@
1
+ module VideoDimensions
2
+ module Backends
3
+ # Public: Base interface for Backends
4
+ class Base
5
+ def self.available?
6
+ false
7
+ end
8
+
9
+ def dimensions
10
+ raise NotImplementedError
11
+ end
12
+
13
+ def width
14
+ raise NotImplementedError
15
+ end
16
+
17
+ def height
18
+ raise NotImplementedError
19
+ end
20
+
21
+ def bitrate
22
+ raise NotImplementedError
23
+ end
24
+
25
+ def codec
26
+ raise NotImplementedError
27
+ end
28
+
29
+ def duration
30
+ raise NotImplementedError
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,71 @@
1
+ module VideoDimensions
2
+ module Backends
3
+ class FFmpeg < Base
4
+ def self.available?
5
+ `which #{binary} 2>&1 > /dev/null`
6
+ $?.exitstatus == 0
7
+ end
8
+
9
+ def self.binary
10
+ 'ffmpeg'
11
+ end
12
+
13
+ def initialize(input)
14
+ @input = input
15
+ end
16
+
17
+ def dimensions
18
+ output.match(/Stream .+ Video: .+ (\d+)x(\d+).*/) do |m|
19
+ [m[1].to_i, m[2].to_i]
20
+ end
21
+ end
22
+
23
+ def width
24
+ dimensions && dimensions[0]
25
+ end
26
+
27
+ def height
28
+ dimensions && dimensions[1]
29
+ end
30
+
31
+ def bitrate
32
+ # Video streams don't always surface their own bitrate, so we'll settle
33
+ # for the "total" bitrate
34
+ output.match(/^\s+Duration: .+bitrate: (\d+) kb\/s$/m) do |m|
35
+ m[1].to_i
36
+ end
37
+ end
38
+
39
+ def codec
40
+ output.match(/Stream .+ Video: (\w+)/m) do |m|
41
+ m[1]
42
+ end
43
+ end
44
+
45
+ def duration
46
+ output.match(/Duration: ([\d\:]+)/) do |m|
47
+ m[1]
48
+ end
49
+ end
50
+
51
+ def framerate
52
+ output.match(/([\d\.]+) tbr/) do |m|
53
+ m[1].to_f
54
+ end
55
+ end
56
+
57
+ private
58
+
59
+ def output
60
+ unless @output
61
+ @output = `#{self.class.binary} -i "#{@input}" 2>&1`.strip
62
+
63
+ # Strip out the Audio codec section(s)
64
+ @output.gsub!(/.*(Input #0.*)output file must be specified.*/m, '\1')
65
+ end
66
+
67
+ @output
68
+ end
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,75 @@
1
+ module VideoDimensions
2
+ module Backends
3
+ class MediaInfo < Base
4
+ def self.available?
5
+ `which #{binary} 2>&1 > /dev/null`
6
+ $?.exitstatus == 0
7
+ end
8
+
9
+ def self.binary
10
+ 'mediainfo'
11
+ end
12
+
13
+ def initialize(input)
14
+ @input = input
15
+ end
16
+
17
+ def dimensions
18
+ [width, height] if width && height
19
+ end
20
+
21
+ def width
22
+ output.match(/^Width\s+: ([\d\s]+) pixels$/) do |m|
23
+ m[1].gsub(' ', '').to_i
24
+ end
25
+ end
26
+
27
+ def height
28
+ output.match(/^Height\s+: ([\d\s]+) pixels$/) do |m|
29
+ m[1].gsub(' ', '').to_i
30
+ end
31
+ end
32
+
33
+ def bitrate
34
+ output.match(/^Bit rate\s+: ([\d\s]+) Kbps$/) do |m|
35
+ m[1].gsub(' ', '').to_i
36
+ end
37
+ end
38
+
39
+ def codec
40
+ output.match(/^Codec ID\s+: (.+)$/) do |m|
41
+ m[1]
42
+ end
43
+ end
44
+
45
+ def duration
46
+ # MediaInfo only incudes the two longest durations in the ouptut (e.g.,
47
+ # 1h 5m; 5m 10s; 10s 15ms)
48
+ output.match(/^Duration\s+:( \d+h)?( \d+mn)?( \d+s)?(?: \d+ms)?$/) do |m|
49
+ # Map all of the captures to Integers, removing the time notation and
50
+ # converting nils to 0, and use sprintf to include leading zeros
51
+ sprintf("%02d:%02d:%02d", *m.captures.map(&:to_i))
52
+ end
53
+ end
54
+
55
+ def framerate
56
+ output.match(/^Frame rate\s+: ([\d\.]+) fps$/) do |m|
57
+ m[1].to_f
58
+ end
59
+ end
60
+
61
+ private
62
+
63
+ def output
64
+ unless @output
65
+ @output = `#{self.class.binary} "#{@input}"`
66
+
67
+ # Strip out the Audio codec section(s)
68
+ @output.gsub!(/(.*)^Audio( #\d+)?$.*/m, '\1')
69
+ end
70
+
71
+ @output
72
+ end
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,3 @@
1
+ module VideoDimensions
2
+ VERSION = "0.3.0"
3
+ end
Binary file
Binary file
@@ -0,0 +1,10 @@
1
+ require 'rspec'
2
+ require 'video_dimensions'
3
+
4
+ Dir[File.expand_path("./spec/support/*.rb")].each { |f| require(f) }
5
+
6
+ RSpec.configure do |c|
7
+ c.mock_with :mocha
8
+
9
+ c.include Fixtures
10
+ end
@@ -0,0 +1,5 @@
1
+ module Fixtures
2
+ def fixture(path)
3
+ File.expand_path(File.join('..', 'fixtures', path), File.dirname(__FILE__))
4
+ end
5
+ end
@@ -0,0 +1,10 @@
1
+ # https://github.com/mynyml/unindent
2
+ class String
3
+ def unindent
4
+ indent = self.split("\n").select {|line| !line.strip.empty? }.map do |line|
5
+ line.index(/[^\s]/)
6
+ end.compact.min || 0
7
+
8
+ self.gsub(/^[[:blank:]]{#{indent}}/, '')
9
+ end
10
+ end
@@ -0,0 +1,166 @@
1
+ require 'spec_helper'
2
+
3
+ module VideoDimensions::Backends
4
+ describe FFmpeg do
5
+ describe ".available?" do
6
+ it "returns true when utility is available" do
7
+ described_class.stubs(:binary).returns('whoami')
8
+ described_class.should be_available
9
+ end
10
+
11
+ it "returns false when utility is not available" do
12
+ described_class.stubs(:binary).returns('invalidbinary')
13
+ described_class.should_not be_available
14
+ end
15
+ end
16
+
17
+ describe "attribute methods" do
18
+ context "720p sample" do
19
+ subject { FFmpeg.new(fixture('720p.wmv')) }
20
+
21
+ its(:dimensions) { should == [1280, 720] }
22
+ its(:width) { should == 1280 }
23
+ its(:height) { should == 720 }
24
+ its(:bitrate) { should == 6503 }
25
+ its(:codec) { should == "wmv3" }
26
+ its(:duration) { should == '00:00:02' }
27
+ its(:framerate) { should == 21.83 }
28
+ end
29
+
30
+ context "1080p sample" do
31
+ subject { FFmpeg.new(fixture('1080p.wmv')) }
32
+
33
+ its(:dimensions) { should == [1440, 1080] }
34
+ its(:width) { should == 1440 }
35
+ its(:height) { should == 1080 }
36
+ its(:bitrate) { should == 9929 }
37
+ its(:codec) { should == "wmv3" }
38
+ its(:duration) { should == '00:00:02' }
39
+ its(:framerate) { should == 21.83 }
40
+ end
41
+
42
+ context "Matroska sample" do
43
+ subject { FFmpeg.new('') }
44
+
45
+ before do
46
+ # We don't want to store this particular file in Git as a fixture, so
47
+ # just fake the output
48
+ subject.stubs(:output).returns <<-EOF
49
+ Metadata:
50
+ creation_time : 1970-01-01 00:00:00
51
+ Duration: 00:47:10.78, start: 0.000000, bitrate: 3578 kb/s
52
+ Stream #0:0: Audio: ac3, 48000 Hz, 5.1(side), s16, 384 kb/s (default)
53
+ Stream #0:1(eng): Video: h264 (High), yuv420p, 1280x720, SAR 1:1 DAR 16:9, 23.98 fps, 23.98 tbr, 1k tbn, 47.95 tbc
54
+ EOF
55
+ end
56
+
57
+ its(:dimensions) { should == [1280, 720] }
58
+ its(:width) { should == 1280 }
59
+ its(:height) { should == 720 }
60
+ its(:bitrate) { should == 3578 }
61
+ its(:codec) { should == 'h264' }
62
+ its(:duration) { should == '00:47:10' }
63
+ its(:framerate) { should == 23.98 }
64
+ end
65
+
66
+ context "MP4 sample" do
67
+ subject { FFmpeg.new('') }
68
+
69
+ before do
70
+ subject.stubs(:output).returns <<-EOF
71
+ Metadata:
72
+ major_brand : isom
73
+ minor_version : 512
74
+ compatible_brands: isomiso2avc1mp41
75
+ creation_time : 1970-01-01 00:00:00
76
+ encoder : Lavf52.94.0
77
+ Duration: 00:21:08.81, start: 0.000000, bitrate: 1333 kb/s
78
+ Stream #0:0(und): Video: h264 (High) (avc1 / 0x31637661), yuv420p, 720x404 [SAR 1:1 DAR 180:101], 1200 kb/s, 23.98 fps, 23.98 tbr, 24k tbn, 47.95 tbc
79
+ Metadata:
80
+ creation_time : 1970-01-01 00:00:00
81
+ handler_name : VideoHandler
82
+ Stream #0:1(und): Audio: aac (mp4a / 0x6134706D), 48000 Hz, stereo, s16, 127 kb/s
83
+ Metadata:
84
+ creation_time : 1970-01-01 00:00:00
85
+ handler_name : SoundHandler
86
+ EOF
87
+ end
88
+
89
+ its(:dimensions) { should == [720, 404] }
90
+ its(:width) { should == 720 }
91
+ its(:height) { should == 404 }
92
+ its(:bitrate) { should == 1333 }
93
+ its(:codec) { should == 'h264' }
94
+ its(:duration) { should == '00:21:08' }
95
+ its(:framerate) { should == 23.98 }
96
+ end
97
+
98
+ context "XviD sample" do
99
+ subject { FFmpeg.new('') }
100
+
101
+ before do
102
+ subject.stubs(:output).returns <<-EOF
103
+ Metadata:
104
+ encoder : VirtualDubMod 1.5.10.2 (build 2540/release)
105
+ Duration: 00:21:58.56, start: 0.000000, bitrate: 1109 kb/s
106
+ Stream #0:0: Video: mpeg4 (Advanced Simple Profile) (XVID / 0x44495658), yuv420p, 624x352 [SAR 1:1 DAR 39:22], 23.98 tbr, 23.98 tbn, 23.98 tbc
107
+ Stream #0:1: Audio: mp3 (U[0][0][0] / 0x0055), 48000 Hz, stereo, s16, 32 kb/s
108
+ EOF
109
+ end
110
+
111
+ its(:dimensions) { should == [624, 352] }
112
+ its(:width) { should == 624 }
113
+ its(:height) { should == 352 }
114
+ its(:bitrate) { should == 1109 }
115
+ its(:codec) { should == 'mpeg4' }
116
+ its(:duration) { should == '00:21:58' }
117
+ its(:framerate) { should == 23.98 }
118
+ end
119
+
120
+ context "XviD sample 2" do
121
+ subject { FFmpeg.new('') }
122
+
123
+ before do
124
+ subject.stubs(:output).returns <<-EOF
125
+ Metadata:
126
+ encoder : VirtualDubMod 1.4.13
127
+ Duration: 00:51:33.55, start: 0.000000, bitrate: 949 kb/s
128
+ Stream #0:0: Video: mpeg4 (XVID / 0x44495658), yuv420p, 624x352 [SAR 1:1 DAR 39:22], 23.98 tbr, 23.98 tbn, 23.98 tbc
129
+ Stream #0:1: Audio: mp3 (U[0][0][0] / 0x0055), 48000 Hz, stereo, s16, 112 kb/s
130
+ EOF
131
+ end
132
+
133
+ its(:dimensions) { should == [624, 352] }
134
+ its(:width) { should == 624 }
135
+ its(:height) { should == 352 }
136
+ its(:bitrate) { should == 949 }
137
+ its(:codec) { should == 'mpeg4' }
138
+ its(:duration) { should == '00:51:33' }
139
+ its(:framerate) { should == 23.98 }
140
+ end
141
+
142
+ context "60 fps sample" do
143
+ subject { FFmpeg.new('') }
144
+
145
+ before do
146
+ subject.stubs(:output).returns <<-EOF
147
+ Metadata:
148
+ creation_time : 2010-09-02 07:01:52
149
+ Duration: 00:10:14.61, start: 0.000000, bitrate: 9728 kb/s
150
+ Stream #0:0(eng): Video: h264 (High), yuv420p, 1280x720 [SAR 1:1 DAR 16:9], 60 fps, 60 tbr, 1k tbn, 120 tbc (default)
151
+ Stream #0:1(eng): Audio: aac, 48000 Hz, stereo, s16 (default)
152
+ EOF
153
+ end
154
+
155
+
156
+ its(:dimensions) { should == [1280, 720] }
157
+ its(:width) { should == 1280 }
158
+ its(:height) { should == 720 }
159
+ its(:bitrate) { should == 9728 }
160
+ its(:codec) { should == 'h264' }
161
+ its(:duration) { should == '00:10:14' }
162
+ its(:framerate) { should == 60.00 }
163
+ end
164
+ end
165
+ end
166
+ end
@@ -0,0 +1,121 @@
1
+ require 'spec_helper'
2
+
3
+ module VideoDimensions::Backends
4
+ describe MediaInfo do
5
+ describe ".available?" do
6
+ it "returns true when utility is available" do
7
+ described_class.stubs(:binary).returns('whoami')
8
+ described_class.should be_available
9
+ end
10
+
11
+ it "returns false when utility is not available" do
12
+ described_class.stubs(:binary).returns('invalidbinary')
13
+ described_class.should_not be_available
14
+ end
15
+ end
16
+
17
+ describe "attribute methods" do
18
+ context "720p sample" do
19
+ subject { MediaInfo.new(fixture('720p.wmv')) }
20
+
21
+ its(:dimensions) { should == [1280, 720] }
22
+ its(:width) { should == 1280 }
23
+ its(:height) { should == 720 }
24
+ its(:bitrate) { should == 5904 }
25
+ its(:codec) { should == "WMV3" }
26
+ its(:duration) { should == '00:00:02' }
27
+ # its(:framerate) { should == 21.83 } # NOTE: MediaInfo mistakenly reports 1,000 fps?
28
+ end
29
+
30
+ context "1080p sample" do
31
+ subject { MediaInfo.new(fixture('1080p.wmv')) }
32
+
33
+ its(:dimensions) { should == [1440, 1080] }
34
+ its(:width) { should == 1440 }
35
+ its(:height) { should == 1080 }
36
+ its(:bitrate) { should == 9330 }
37
+ its(:codec) { should == "WMV3" }
38
+ its(:duration) { should == '00:00:02' }
39
+ # its(:framerate) { should == 21.83 } # NOTE: MediaInfo mistakenly reports 1,000 fps?
40
+ end
41
+
42
+ context "60 fps sample" do
43
+ subject { MediaInfo.new('') }
44
+
45
+ before do
46
+ subject.stubs(:output).returns(
47
+ """
48
+ Video
49
+ ID : 1
50
+ Format : AVC
51
+ Format/Info : Advanced Video Codec
52
+ Format profile : High@L3.2
53
+ Format settings, CABAC : No
54
+ Format settings, ReFrames : 4 frames
55
+ Codec ID : V_MPEG4/ISO/AVC
56
+ Duration : 10mn 14s
57
+ Width : 1 280 pixels
58
+ Height : 720 pixels
59
+ Display aspect ratio : 16:9
60
+ Frame rate mode : Constant
61
+ Frame rate : 60.000 fps
62
+ Color space : YUV
63
+ Chroma subsampling : 4:2:0
64
+ Bit depth : 8 bits
65
+ Scan type : Progressive
66
+ Writing library : x264 core 104 r1703 cd21d05
67
+ """.unindent)
68
+ end
69
+
70
+ its(:framerate) { should == 60.00 }
71
+ end
72
+
73
+ context "duration of at least 1 hour" do
74
+ subject { MediaInfo.new('') }
75
+
76
+ before do
77
+ subject.stubs(:output).returns(
78
+ """
79
+ General
80
+ Complete name : video.mp4
81
+ Format : MPEG-4
82
+ Format profile : Base Media / Version 2
83
+ Codec ID : mp42
84
+ File size : 984 MiB
85
+ Duration : 1h 32mn
86
+ Overall bit rate mode : Variable
87
+ Overall bit rate : 1 492 Kbps
88
+ Encoded date : UTC 2012-07-08 03:49:06
89
+ Tagged date : UTC 2012-07-08 04:11:57
90
+ Writing application : HandBrake 0.9.6 2012022800
91
+ """.unindent)
92
+ end
93
+
94
+ its(:duration) { should == '01:32:00' }
95
+ end
96
+
97
+ context "duration of at least 1 minute" do
98
+ subject { MediaInfo.new('') }
99
+
100
+ before do
101
+ subject.stubs(:output).returns(
102
+ """
103
+ General
104
+ Complete name : video.mp4
105
+ Format : MPEG-4
106
+ Format profile : Base Media
107
+ Codec ID : isom
108
+ File size : 11.3 MiB
109
+ Duration : 4mn 10s
110
+ Overall bit rate mode : Variable
111
+ Overall bit rate : 379 Kbps
112
+ Encoded date : UTC 2012-01-05 07:04:29
113
+ Tagged date : UTC 2012-01-05 07:04:29
114
+ """.unindent)
115
+ end
116
+
117
+ its(:duration) { should == '00:04:10' }
118
+ end
119
+ end
120
+ end
121
+ end
@@ -0,0 +1,24 @@
1
+ require 'spec_helper'
2
+
3
+ module VideoDimensions
4
+ describe Backends do
5
+ describe ".first_available" do
6
+ it "returns the first available backend" do
7
+ Backends::MediaInfo.stubs(:available?).returns(true)
8
+ Backends::FFmpeg.stubs(:available?).returns(false)
9
+ Backends.first_available.should == Backends::MediaInfo
10
+
11
+ Backends::MediaInfo.stubs(:available?).returns(false)
12
+ Backends::FFmpeg.stubs(:available?).returns(true)
13
+ Backends.first_available.should == Backends::FFmpeg
14
+ end
15
+
16
+ it "raises exception when none are available" do
17
+ Backends::FFmpeg.stubs(:available?).returns(false)
18
+ Backends::MediaInfo.stubs(:available?).returns(false)
19
+
20
+ expect { Backends.first_available }.to raise_error(RuntimeError, /compatible video attribute backend/)
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,20 @@
1
+ require 'spec_helper'
2
+
3
+ describe VideoDimensions do
4
+ describe "method" do
5
+ it "instantiates a Backend object" do
6
+ VideoDimensions(fixture('720p.wmv')).should be_kind_of(VideoDimensions::Backends::Base)
7
+ end
8
+ end
9
+
10
+ describe "API" do
11
+ context "class methods" do
12
+ it { should respond_to(:dimensions) }
13
+ it { should respond_to(:width) }
14
+ it { should respond_to(:height) }
15
+ it { should respond_to(:bitrate) }
16
+ it { should respond_to(:codec) }
17
+ it { should respond_to(:duration) }
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,23 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ require File.expand_path('../lib/video_dimensions/version', __FILE__)
4
+
5
+ Gem::Specification.new do |gem|
6
+ gem.name = "video_dimensions"
7
+ gem.version = VideoDimensions::VERSION
8
+ gem.summary = %q{Quick and easy video attributes}
9
+ gem.description = %q{Quick and easy video attributes -- width, height, bitrate, codec, duration, framerate.}
10
+ gem.license = "MIT"
11
+ gem.authors = ["Robert Speicher"]
12
+ gem.email = "rspeicher@gmail.com"
13
+ gem.homepage = "https://github.com/tsigo/video_dimensions"
14
+
15
+ gem.files = `git ls-files`.split($/)
16
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
17
+ gem.require_paths = ['lib']
18
+
19
+ gem.add_development_dependency 'bundler', '~> 1.0'
20
+ gem.add_development_dependency 'mocha'
21
+ gem.add_development_dependency 'rake', '~> 0.8'
22
+ gem.add_development_dependency 'rspec', '~> 2.4'
23
+ end
metadata ADDED
@@ -0,0 +1,147 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: video_dimensions
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.3.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Robert Speicher
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-03-25 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: bundler
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: '1.0'
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ version: '1.0'
30
+ - !ruby/object:Gem::Dependency
31
+ name: mocha
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :development
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ - !ruby/object:Gem::Dependency
47
+ name: rake
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ~>
52
+ - !ruby/object:Gem::Version
53
+ version: '0.8'
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ~>
60
+ - !ruby/object:Gem::Version
61
+ version: '0.8'
62
+ - !ruby/object:Gem::Dependency
63
+ name: rspec
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ~>
68
+ - !ruby/object:Gem::Version
69
+ version: '2.4'
70
+ type: :development
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ~>
76
+ - !ruby/object:Gem::Version
77
+ version: '2.4'
78
+ description: Quick and easy video attributes -- width, height, bitrate, codec, duration,
79
+ framerate.
80
+ email: rspeicher@gmail.com
81
+ executables: []
82
+ extensions: []
83
+ extra_rdoc_files: []
84
+ files:
85
+ - .gitignore
86
+ - .rspec
87
+ - Gemfile
88
+ - LICENSE.txt
89
+ - README.md
90
+ - Rakefile
91
+ - lib/video_dimensions.rb
92
+ - lib/video_dimensions/backends.rb
93
+ - lib/video_dimensions/backends/base.rb
94
+ - lib/video_dimensions/backends/ffmpeg.rb
95
+ - lib/video_dimensions/backends/media_info.rb
96
+ - lib/video_dimensions/version.rb
97
+ - spec/fixtures/1080p.wmv
98
+ - spec/fixtures/720p.wmv
99
+ - spec/spec_helper.rb
100
+ - spec/support/fixtures.rb
101
+ - spec/support/string.rb
102
+ - spec/video_dimensions/backends/ffmpeg_spec.rb
103
+ - spec/video_dimensions/backends/media_info_spec.rb
104
+ - spec/video_dimensions/backends_spec.rb
105
+ - spec/video_dimensions_spec.rb
106
+ - video_dimensions.gemspec
107
+ homepage: https://github.com/tsigo/video_dimensions
108
+ licenses:
109
+ - MIT
110
+ post_install_message:
111
+ rdoc_options: []
112
+ require_paths:
113
+ - lib
114
+ required_ruby_version: !ruby/object:Gem::Requirement
115
+ none: false
116
+ requirements:
117
+ - - ! '>='
118
+ - !ruby/object:Gem::Version
119
+ version: '0'
120
+ segments:
121
+ - 0
122
+ hash: -2405270881637614437
123
+ required_rubygems_version: !ruby/object:Gem::Requirement
124
+ none: false
125
+ requirements:
126
+ - - ! '>='
127
+ - !ruby/object:Gem::Version
128
+ version: '0'
129
+ segments:
130
+ - 0
131
+ hash: -2405270881637614437
132
+ requirements: []
133
+ rubyforge_project:
134
+ rubygems_version: 1.8.23
135
+ signing_key:
136
+ specification_version: 3
137
+ summary: Quick and easy video attributes
138
+ test_files:
139
+ - spec/fixtures/1080p.wmv
140
+ - spec/fixtures/720p.wmv
141
+ - spec/spec_helper.rb
142
+ - spec/support/fixtures.rb
143
+ - spec/support/string.rb
144
+ - spec/video_dimensions/backends/ffmpeg_spec.rb
145
+ - spec/video_dimensions/backends/media_info_spec.rb
146
+ - spec/video_dimensions/backends_spec.rb
147
+ - spec/video_dimensions_spec.rb