frames 0.0.1

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/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/.travis.yml ADDED
@@ -0,0 +1,8 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.9.3
4
+ - 1.9.2
5
+ - jruby-19mode
6
+ - rbx-19mode
7
+ #- ruby-head
8
+ - jruby-head
data/Gemfile ADDED
@@ -0,0 +1,8 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in frames.gemspec
4
+ gemspec
5
+
6
+ group :test do
7
+ gem 'rake'
8
+ end
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 brookemckim
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,48 @@
1
+ # Frames
2
+
3
+ Uses ffprobe to analyze video files frame by frame.
4
+
5
+ Use Cases:
6
+
7
+ * List all P/B/I frames of a video file.
8
+ * List all key frames of a video file.
9
+ * Analyze DTS/PTS of each frame.
10
+
11
+ ## Prerequisites
12
+
13
+ [ffprobe](http://ffmpeg.org/ffprobe.html)
14
+
15
+ ## Installation
16
+
17
+ Add this line to your application's Gemfile:
18
+
19
+ gem 'frames'
20
+
21
+ And then execute:
22
+
23
+ $ bundle
24
+
25
+ Or install it yourself as:
26
+
27
+ $ gem install frames
28
+
29
+ ## Usage
30
+
31
+ ```
32
+ # Point the analyzer to your video file.
33
+ frame_analyzer = Frames::Anaylzer.new("/Users/bmckim/Desktop/sample.avi")
34
+
35
+ # Get an Array of each frame.
36
+ frame_analyzer.frames
37
+
38
+ # Want the raw ffprobe output?
39
+ frame.probe
40
+ ```
41
+
42
+ ## Contributing
43
+
44
+ 1. Fork it
45
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
46
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
47
+ 4. Push to the branch (`git push origin my-new-feature`)
48
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,13 @@
1
+ require 'bundler/gem_tasks'
2
+
3
+ require 'rake/testtask'
4
+
5
+ Rake::TestTask.new do |t|
6
+ t.libs.push 'lib'
7
+ t.libs.push 'spec'
8
+ t.test_files = FileList['spec/**/*_spec.rb']
9
+ t.verbose = true
10
+ end
11
+
12
+ task :default => [:test]
13
+ task :spec => [:test]
data/frames.gemspec ADDED
@@ -0,0 +1,22 @@
1
+ # -*- encoding: utf-8 -*-
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'frames/version'
5
+
6
+ Gem::Specification.new do |gem|
7
+ gem.name = "frames"
8
+ gem.version = Frames::VERSION
9
+ gem.authors = ["brookemckim"]
10
+ gem.email = ["brooke.mckim@gmail.com"]
11
+ gem.description = %q{Uses ffprobe to retrieve metadata about each frame of a video.}
12
+ gem.summary = %q{Analyze video metadata frame by frame.}
13
+ gem.homepage = "http://github.com/brookemckim/frames"
14
+
15
+ gem.files = `git ls-files`.split($/)
16
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
17
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
18
+ gem.require_paths = ["lib"]
19
+
20
+ gem.add_development_dependency 'mocha'
21
+ end
22
+
@@ -0,0 +1,41 @@
1
+ module Frames
2
+ class FileError < StandardError; end
3
+
4
+ # Public: Run ffprobe on a file and analyze the frames from the output.
5
+ #
6
+ # file - The file to be anaylzed
7
+ #
8
+ # Examples
9
+ #
10
+ # analyzer = Anaylzer.new("/Users/bmckim/Desktop/sample.avi")
11
+ # analyzer.frames
12
+ # # => [...]
13
+ class Analyzer
14
+ def initialize(file)
15
+ @file = String(file)
16
+ end
17
+
18
+ # Public: Returns the Array of Frames.
19
+ def frames
20
+ @frames ||= constructor.frames
21
+ end
22
+
23
+ # Public: Probe the video file.
24
+ #
25
+ # Returns String of raw output from ffprobe.
26
+ def probe
27
+ output = `ffprobe -show_frames "#{@file}" 2>/dev/null`
28
+
29
+ if $? == 0
30
+ output
31
+ else
32
+ raise FileError, "File does not exist or ffprobe was unable to read it"
33
+ end
34
+ end
35
+
36
+ # Internal: Create FrameConstructor from ffprobe raw output.
37
+ def constructor
38
+ FrameConstructor.new(probe)
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,19 @@
1
+ # Internal: Takes a hash of video/audio frame attributes and makes them
2
+ # getters on an object.
3
+ module Frames
4
+ class Frame
5
+ def initialize(attrs = {})
6
+ attrs.each do |k,v|
7
+ self.instance_variable_set(:"@#{k}", v)
8
+ end
9
+ end
10
+
11
+ attr_reader :media_type, :key_frame, :pkt_pts, :pkt_pts_time, :pkt_dts,
12
+ :pkt_dts_time, :pkt_duration, :pkt_duration_time, :pkt_pos,
13
+ :sample_fmt, :nb_samples, :channels, :channel_layout, :width, :height,
14
+ :sample_aspect_ratio, :pict_type, :coded_picture_number,
15
+ :display_picture_number, :interlaced_frame, :top_field_first,
16
+ :repeat_pict, :reference
17
+ end
18
+ end
19
+
@@ -0,0 +1,63 @@
1
+ # Internal: Converts raw output from ffprobe into an Array of Frames.
2
+ #
3
+ # Examples
4
+ #
5
+ # fc = FrameConstructor.new(`ffprobe -show_frames /path/to/video.mp4`)
6
+ # fc.frames # => [<Frame>,<Frame>]
7
+ module Frames
8
+ class FrameConstructor
9
+ def initialize(ffprobe_output)
10
+ @raw_output = ffprobe_output
11
+ end
12
+
13
+ # Internal: Maps Array of hashes from output into Array Frame objects.
14
+ def frames
15
+ frame_hashes.map { |h| Frame.new(h) }
16
+ end
17
+
18
+ # Internal: Converts each raw frame into Hash.
19
+ #
20
+ # Returns Array of Hashes.
21
+ def frame_hashes
22
+ hashes = []
23
+
24
+ split_raw_frames.each do |frame|
25
+ hashes << raw_frame_to_hash(frame)
26
+ end
27
+
28
+ hashes
29
+ end
30
+
31
+ # Internal: Splits ffprobe output into seperate frames.
32
+ #
33
+ # Returns Array of raw ffprobe frames.
34
+ def split_raw_frames
35
+ @raw_output.split('[/FRAME]').take_while { |f|
36
+ f['[FRAME']
37
+ }
38
+ end
39
+
40
+ private
41
+
42
+ # Internal: Parses ffprobe frame metadata into hash.
43
+ #
44
+ # Returns Hash of frame attributes.
45
+ def raw_frame_to_hash(raw_frame)
46
+ hash_of_raw_frames = {}
47
+
48
+ raw_frame
49
+ .split("\n")
50
+ .delete_if { |value| !value['='] }
51
+ .each do |attribute|
52
+ attribute_array = attribute.split('=')
53
+ key = attribute_array[0]
54
+ value = attribute_array[1]
55
+
56
+ hash_of_raw_frames.merge!(key.to_sym => value)
57
+ end
58
+
59
+ hash_of_raw_frames
60
+ end
61
+ end
62
+ end
63
+
@@ -0,0 +1,3 @@
1
+ module Frames
2
+ VERSION = "0.0.1"
3
+ end
data/lib/frames.rb ADDED
@@ -0,0 +1,10 @@
1
+ $LOAD_PATH.unshift File.dirname(__FILE__)
2
+
3
+ require 'frames/version'
4
+ require 'frames/analyzer'
5
+ require 'frames/frame'
6
+ require 'frames/frame_constructor'
7
+
8
+ module Frames
9
+
10
+ end
@@ -0,0 +1,19 @@
1
+ require 'spec_helper'
2
+
3
+ describe Frames::Analyzer do
4
+ describe 'when video file is valid' do
5
+ before do
6
+ @analyzer = Frames::Analyzer.new('/path/to_video.mp4')
7
+
8
+ constructor = mock('constructor', :frames => 'framesgohere')
9
+ @analyzer.stubs(:probe)
10
+ @analyzer.stubs(:constructor).returns(constructor)
11
+ end
12
+
13
+ describe '#frames' do
14
+ it 'sets frames to value from the frame constructor' do
15
+ @analyzer.frames.must_equal 'framesgohere'
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,68 @@
1
+ require 'spec_helper'
2
+
3
+ describe Frames::FrameConstructor do
4
+ before do
5
+ @single_raw_output = <<EOF
6
+ [FRAME]\nmedia_type=video\nkey_frame=0\npkt_pts=N/A\npkt_pts_time=N/A\npkt_dts=N/A\npkt_dts_time=N/A\npkt_pos=6218536\nwidth=522\nheight=282\npix_fmt=yuv420p\nsample_aspect_ratio=1:1\npict_type=P\ncoded_picture_number=860\ndisplay_picture_number=0\ninterlaced_frame=0\ntop_field_first=0\nrepeat_pict=0\nreference=3\n[/FRAME]\n
7
+ EOF
8
+
9
+ @multiple_raw_output = <<EOF
10
+ [FRAME]\nmedia_type=video\nkey_frame=0\npkt_pts=N/A\npkt_pts_time=N/A\npkt_dts=N/A\npkt_dts_time=N/A\npkt_pos=6218536\nwidth=522\nheight=282\npix_fmt=yuv420p\nsample_aspect_ratio=1:1\npict_type=P\ncoded_picture_number=860\ndisplay_picture_number=0\ninterlaced_frame=0\ntop_field_first=0\nrepeat_pict=0\nreference=3\n[/FRAME]\n
11
+ [FRAME]\nmedia_type=video\nkey_frame=0\npkt_pts=855\npkt_pts_time=35.660625\npkt_dts=855\npkt_dts_time=35.660625\npkt_pos=6196580\nwidth=522\nheight=282\npix_fmt=yuv420p\nsample_aspect_ratio=1:1\npict_type=B\ncoded_picture_number=855\ndisplay_picture_number=0\ninterlaced_frame=0\ntop_field_first=0\nrepeat_pict=0\nreference=0\n[/FRAME]
12
+ EOF
13
+ @frame_constructor = Frames::FrameConstructor.new(@multiple_raw_output)
14
+ end
15
+
16
+ describe '#frames' do
17
+ it 'converts raw output to Array of frames' do
18
+ frames = @frame_constructor.frames
19
+
20
+ frames.must_be_instance_of(Array)
21
+
22
+ frames.size.must_equal 2
23
+ frames.each { |f| f.must_be_instance_of(Frames::Frame) }
24
+ end
25
+ end
26
+
27
+ describe '#from_hashes' do
28
+ it 'returns Array of formatted frames' do
29
+ @frame_constructor.stubs(:raw_frame_to_hash).returns({})
30
+
31
+ @frame_constructor.frame_hashes.must_equal [{},{}]
32
+ end
33
+ end
34
+
35
+ describe '#split_raw_frames' do
36
+ it 'splits the raw frames' do
37
+ @frame_constructor.split_raw_frames.size.must_equal 2
38
+ end
39
+ end
40
+
41
+ describe '#raw_frame_to_hash' do
42
+ it 'converts raw output frame to hash' do
43
+ frame_hash = @frame_constructor.send('raw_frame_to_hash', @single_raw_output)
44
+ expected_frame_hash = {
45
+ media_type: 'video',
46
+ key_frame: '0',
47
+ pkt_pts: 'N/A',
48
+ pkt_pts_time: 'N/A',
49
+ pkt_dts: 'N/A',
50
+ pkt_dts_time: 'N/A',
51
+ pkt_pos: '6218536',
52
+ width: '522',
53
+ height: '282',
54
+ pix_fmt: 'yuv420p',
55
+ sample_aspect_ratio: '1:1',
56
+ pict_type: 'P',
57
+ coded_picture_number: '860',
58
+ display_picture_number: '0',
59
+ interlaced_frame: '0',
60
+ top_field_first: '0',
61
+ repeat_pict: '0',
62
+ reference: '3'
63
+ }
64
+
65
+ frame_hash.must_equal expected_frame_hash
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,4 @@
1
+ require 'minitest/autorun'
2
+ require 'mocha'
3
+
4
+ require 'frames'
metadata ADDED
@@ -0,0 +1,80 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: frames
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - brookemckim
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-10-21 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: mocha
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '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: '0'
30
+ description: Uses ffprobe to retrieve metadata about each frame of a video.
31
+ email:
32
+ - brooke.mckim@gmail.com
33
+ executables: []
34
+ extensions: []
35
+ extra_rdoc_files: []
36
+ files:
37
+ - .gitignore
38
+ - .travis.yml
39
+ - Gemfile
40
+ - LICENSE.txt
41
+ - README.md
42
+ - Rakefile
43
+ - frames.gemspec
44
+ - lib/frames.rb
45
+ - lib/frames/analyzer.rb
46
+ - lib/frames/frame.rb
47
+ - lib/frames/frame_constructor.rb
48
+ - lib/frames/version.rb
49
+ - spec/frames/analyzer_spec.rb
50
+ - spec/frames/frame_constuctor_spec.rb
51
+ - spec/spec_helper.rb
52
+ homepage: http://github.com/brookemckim/frames
53
+ licenses: []
54
+ post_install_message:
55
+ rdoc_options: []
56
+ require_paths:
57
+ - lib
58
+ required_ruby_version: !ruby/object:Gem::Requirement
59
+ none: false
60
+ requirements:
61
+ - - ! '>='
62
+ - !ruby/object:Gem::Version
63
+ version: '0'
64
+ required_rubygems_version: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ! '>='
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ requirements: []
71
+ rubyforge_project:
72
+ rubygems_version: 1.8.24
73
+ signing_key:
74
+ specification_version: 3
75
+ summary: Analyze video metadata frame by frame.
76
+ test_files:
77
+ - spec/frames/analyzer_spec.rb
78
+ - spec/frames/frame_constuctor_spec.rb
79
+ - spec/spec_helper.rb
80
+ has_rdoc: