streamio-ffmpeg 0.2.0 → 0.3.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.
- data/.gitignore +2 -0
- data/CHANGELOG +4 -0
- data/README.rdoc +23 -0
- data/VERSION +1 -1
- data/lib/ffmpeg/movie.rb +9 -4
- data/lib/ffmpeg/transcoder.rb +47 -0
- data/lib/streamio-ffmpeg.rb +1 -0
- data/spec/ffmpeg/movie_spec.rb +76 -59
- data/spec/ffmpeg/transcoder_spec.rb +22 -0
- data/spec/spec_helper.rb +8 -1
- data/streamio-ffmpeg.gemspec +5 -2
- metadata +5 -2
data/.gitignore
CHANGED
data/CHANGELOG
CHANGED
data/README.rdoc
CHANGED
@@ -10,6 +10,8 @@ Simplest possible wrapper around FFMPEG to get metadata from movie files and do
|
|
10
10
|
|
11
11
|
== Usage
|
12
12
|
|
13
|
+
=== Reading Metadata
|
14
|
+
|
13
15
|
movie = FFMPEG::Movie.new("path/to/movie.mov")
|
14
16
|
|
15
17
|
movie.duration # 7.5 (duration of the movie in seconds)
|
@@ -29,6 +31,27 @@ Simplest possible wrapper around FFMPEG to get metadata from movie files and do
|
|
29
31
|
movie.audio_channels # 2
|
30
32
|
|
31
33
|
movie.valid? # true (would be false if ffmpeg fails to read the movie)
|
34
|
+
|
35
|
+
=== Transcoding
|
36
|
+
|
37
|
+
Use :output_file option to specify the output file path.
|
38
|
+
|
39
|
+
movie.transcode(:output_file => "tmp/movie.mp4") # Default ffmpeg settings for mp4 format
|
40
|
+
|
41
|
+
Keep track of progress with an optional block.
|
42
|
+
|
43
|
+
movie.transcode(:output_file => "movie.mp4") { |progress| puts progress } # 0.2 ... 0.5 ... 1.0
|
44
|
+
|
45
|
+
Give custom command line options to ffmpeg through :raw_options.
|
46
|
+
|
47
|
+
movie.transcode(:raw_options => "-ac aac -vc libx264 -ac 2 ...", :output_file => "movie.mp4")
|
48
|
+
|
49
|
+
The transcode function returns a Movie object for the encoded file.
|
50
|
+
|
51
|
+
transcoded_movie = movie.transcode(:output_file => "tmp/movie.flv")
|
52
|
+
|
53
|
+
transcoded_movie.video_codec # "flv"
|
54
|
+
transcoded_movie.audio_codec # "mp3"
|
32
55
|
|
33
56
|
== Copyright
|
34
57
|
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.3.0
|
data/lib/ffmpeg/movie.rb
CHANGED
@@ -1,14 +1,14 @@
|
|
1
|
-
require 'open3'
|
2
|
-
|
3
1
|
module FFMPEG
|
4
2
|
class Movie
|
5
|
-
attr_reader :duration, :bitrate
|
3
|
+
attr_reader :path, :duration, :bitrate
|
6
4
|
attr_reader :video_stream, :video_codec, :colorspace, :resolution
|
7
5
|
attr_reader :audio_stream, :audio_codec, :audio_sample_rate
|
8
6
|
|
9
7
|
def initialize(path)
|
10
8
|
raise Errno::ENOENT, "the file '#{path}' does not exist" unless File.exists?(path)
|
11
9
|
|
10
|
+
@path = path
|
11
|
+
|
12
12
|
stdin, stdout, stderr = Open3.popen3("ffmpeg -i #{path}") # Output will land in stderr
|
13
13
|
output = stderr.read
|
14
14
|
|
@@ -58,5 +58,10 @@ module FFMPEG
|
|
58
58
|
def frame_rate
|
59
59
|
video_stream[/(\d*\.\d*)\s?fps/] ? $1.to_f : nil
|
60
60
|
end
|
61
|
+
|
62
|
+
def transcode(options, &block)
|
63
|
+
transcoder = Transcoder.new(self, options)
|
64
|
+
transcoder.run &block
|
65
|
+
end
|
61
66
|
end
|
62
|
-
end
|
67
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'open3'
|
2
|
+
|
3
|
+
module FFMPEG
|
4
|
+
class Transcoder
|
5
|
+
def initialize(movie, options)
|
6
|
+
raise ArgumentError, "you need to specify options[:output_file]" unless options[:output_file]
|
7
|
+
|
8
|
+
@movie = movie
|
9
|
+
@options = options
|
10
|
+
end
|
11
|
+
|
12
|
+
def run
|
13
|
+
command = "ffmpeg -y -i '#{@movie.path}' #{@options[:raw_options]} '#{@options[:output_file]}'"
|
14
|
+
last_output = nil
|
15
|
+
Open3.popen3(command) do |stdin, stdout, stderr|
|
16
|
+
stderr.each("r") do |line|
|
17
|
+
if line =~ /time=(\d+.\d+)/
|
18
|
+
time = $1.to_f
|
19
|
+
progress = time / @movie.duration
|
20
|
+
yield(progress) if block_given?
|
21
|
+
end
|
22
|
+
if line =~ /Unsupported codec/
|
23
|
+
raise "Failed encoding: #{line}"
|
24
|
+
end
|
25
|
+
last_output = line
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
if encoding_succeeded?
|
30
|
+
yield(1.0) if block_given?
|
31
|
+
else
|
32
|
+
raise "Failed encoding. Last output: #{last_output}. Original duration: #{@movie.duration}. Encoded duration: #{encoded.duration}."
|
33
|
+
end
|
34
|
+
|
35
|
+
encoded
|
36
|
+
end
|
37
|
+
|
38
|
+
def encoding_succeeded?
|
39
|
+
precision = 1.1
|
40
|
+
encoded.valid? && !(encoded.duration >= (@movie.duration * precision) or encoded.duration <= (@movie.duration / precision))
|
41
|
+
end
|
42
|
+
|
43
|
+
def encoded
|
44
|
+
@encoded ||= Movie.new(@options[:output_file])
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
data/lib/streamio-ffmpeg.rb
CHANGED
data/spec/ffmpeg/movie_spec.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
|
-
#require File.dirname(__FILE__) + '/../spec_helper.rb'
|
2
1
|
require 'spec_helper.rb'
|
3
2
|
|
4
3
|
module FFMPEG
|
4
|
+
|
5
5
|
describe Movie do
|
6
6
|
describe "given a non existing file" do
|
7
7
|
it "should throw ArgumentError" do
|
@@ -9,73 +9,90 @@ module FFMPEG
|
|
9
9
|
end
|
10
10
|
end
|
11
11
|
|
12
|
-
describe "
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
it "should not be valid" do
|
18
|
-
@movie.should_not be_valid
|
19
|
-
end
|
20
|
-
end
|
21
|
-
|
22
|
-
describe "given awesome.mov file" do
|
23
|
-
before(:all) do
|
24
|
-
@movie = Movie.new("#{fixture_path}/movies/awesome.mov")
|
25
|
-
end
|
26
|
-
|
27
|
-
it "should parse duration to number of seconds" do
|
28
|
-
@movie.duration.should == 7.5
|
29
|
-
end
|
30
|
-
|
31
|
-
it "should parse the bitrate" do
|
32
|
-
@movie.bitrate.should == 481
|
33
|
-
end
|
12
|
+
describe "parsing" do
|
13
|
+
describe "given a non movie file" do
|
14
|
+
before(:all) do
|
15
|
+
@movie = Movie.new(__FILE__)
|
16
|
+
end
|
34
17
|
|
35
|
-
|
36
|
-
|
18
|
+
it "should not be valid" do
|
19
|
+
@movie.should_not be_valid
|
20
|
+
end
|
37
21
|
end
|
38
22
|
|
39
|
-
|
40
|
-
|
41
|
-
|
23
|
+
describe "given awesome.mov file" do
|
24
|
+
before(:all) do
|
25
|
+
@movie = Movie.new("#{fixture_path}/movies/awesome.mov")
|
26
|
+
end
|
42
27
|
|
43
|
-
|
44
|
-
|
45
|
-
|
28
|
+
it "should remember the movie path" do
|
29
|
+
@movie.path.should == "#{fixture_path}/movies/awesome.mov"
|
30
|
+
end
|
46
31
|
|
47
|
-
|
48
|
-
|
49
|
-
|
32
|
+
it "should parse duration to number of seconds" do
|
33
|
+
@movie.duration.should == 7.5
|
34
|
+
end
|
50
35
|
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
end
|
36
|
+
it "should parse the bitrate" do
|
37
|
+
@movie.bitrate.should == 481
|
38
|
+
end
|
55
39
|
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
40
|
+
it "should parse video stream information" do
|
41
|
+
@movie.video_stream.should == "h264, yuv420p, 640x480 [PAR 1:1 DAR 4:3], 371 kb/s, 16.75 fps, 15 tbr, 600 tbn, 1200 tbc"
|
42
|
+
end
|
43
|
+
|
44
|
+
it "should know the video codec" do
|
45
|
+
@movie.video_codec.should == "h264"
|
46
|
+
end
|
47
|
+
|
48
|
+
it "should know the colorspace" do
|
49
|
+
@movie.colorspace.should == "yuv420p"
|
50
|
+
end
|
51
|
+
|
52
|
+
it "should know the resolution" do
|
53
|
+
@movie.resolution.should == "640x480"
|
54
|
+
end
|
55
|
+
|
56
|
+
it "should know the width and height" do
|
57
|
+
@movie.width.should == 640
|
58
|
+
@movie.height.should == 480
|
59
|
+
end
|
60
|
+
|
61
|
+
it "should know the framerate" do
|
62
|
+
@movie.frame_rate.should == 16.75
|
63
|
+
end
|
75
64
|
|
76
|
-
|
77
|
-
|
65
|
+
it "should parse audio stream information" do
|
66
|
+
@movie.audio_stream.should == "aac, 44100 Hz, stereo, s16, 75 kb/s"
|
67
|
+
end
|
68
|
+
|
69
|
+
it "should know the audio codec" do
|
70
|
+
@movie.audio_codec.should == "aac"
|
71
|
+
end
|
72
|
+
|
73
|
+
it "should know the sample rate" do
|
74
|
+
@movie.audio_sample_rate.should == 44100
|
75
|
+
end
|
76
|
+
|
77
|
+
it "should know the number of audio channels" do
|
78
|
+
@movie.audio_channels.should == 2
|
79
|
+
end
|
80
|
+
|
81
|
+
it "should should be valid" do
|
82
|
+
@movie.should be_valid
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
describe "transcode" do
|
88
|
+
it "should run the transcoder" do
|
89
|
+
FileUtils.rm_f "#{tmp_path}/awesome.flv"
|
90
|
+
movie = Movie.new("#{fixture_path}/movies/awesome.mov")
|
91
|
+
encoded = movie.transcode(:output_file => "#{tmp_path}/awesome.flv")
|
92
|
+
encoded.should be_valid
|
93
|
+
File.exists?("#{tmp_path}/awesome.flv").should be_true
|
78
94
|
end
|
79
95
|
end
|
80
96
|
end
|
97
|
+
|
81
98
|
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'spec_helper.rb'
|
2
|
+
|
3
|
+
module FFMPEG
|
4
|
+
describe Transcoder do
|
5
|
+
describe "initializing" do
|
6
|
+
it "should require an output_file option" do
|
7
|
+
lambda { Transcoder.new(nil, {}) }.should raise_error(ArgumentError, /output_file/)
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
describe "transcoding" do
|
12
|
+
it "should transcode the movie" do
|
13
|
+
FileUtils.rm_f "#{tmp_path}/awesome.flv"
|
14
|
+
movie = Movie.new("#{fixture_path}/movies/awesome.mov")
|
15
|
+
transcoder = Transcoder.new(movie, :output_file => "tmp/awesome.flv")
|
16
|
+
transcoder.run
|
17
|
+
transcoder.encoded.should be_valid
|
18
|
+
File.exists?("#{tmp_path}/awesome.flv").should be_true
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
data/spec/spec_helper.rb
CHANGED
@@ -3,6 +3,7 @@ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
|
3
3
|
require 'streamio-ffmpeg'
|
4
4
|
require 'spec'
|
5
5
|
require 'spec/autorun'
|
6
|
+
require 'fileutils'
|
6
7
|
|
7
8
|
Spec::Runner.configure do |config|
|
8
9
|
|
@@ -10,4 +11,10 @@ end
|
|
10
11
|
|
11
12
|
def fixture_path
|
12
13
|
@fixture_path ||= File.join(File.dirname(__FILE__), 'fixtures')
|
13
|
-
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def tmp_path
|
17
|
+
@tmp_path ||= File.join(File.dirname(__FILE__), "..", "tmp")
|
18
|
+
end
|
19
|
+
|
20
|
+
FileUtils.mkdir_p tmp_path
|
data/streamio-ffmpeg.gemspec
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{streamio-ffmpeg}
|
8
|
-
s.version = "0.
|
8
|
+
s.version = "0.3.0"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["David Backeus"]
|
12
|
-
s.date = %q{2010-02-
|
12
|
+
s.date = %q{2010-02-07}
|
13
13
|
s.description = %q{Simple wrapper around ffmpeg to get metadata from movies and do transcoding}
|
14
14
|
s.email = %q{duztdruid@gmail.com}
|
15
15
|
s.extra_rdoc_files = [
|
@@ -25,8 +25,10 @@ Gem::Specification.new do |s|
|
|
25
25
|
"Rakefile",
|
26
26
|
"VERSION",
|
27
27
|
"lib/ffmpeg/movie.rb",
|
28
|
+
"lib/ffmpeg/transcoder.rb",
|
28
29
|
"lib/streamio-ffmpeg.rb",
|
29
30
|
"spec/ffmpeg/movie_spec.rb",
|
31
|
+
"spec/ffmpeg/transcoder_spec.rb",
|
30
32
|
"spec/fixtures/movies/awesome.mov",
|
31
33
|
"spec/spec.opts",
|
32
34
|
"spec/spec_helper.rb",
|
@@ -39,6 +41,7 @@ Gem::Specification.new do |s|
|
|
39
41
|
s.summary = %q{Simple wrapper around ffmpeg to get metadata from movies and do transcoding}
|
40
42
|
s.test_files = [
|
41
43
|
"spec/ffmpeg/movie_spec.rb",
|
44
|
+
"spec/ffmpeg/transcoder_spec.rb",
|
42
45
|
"spec/spec_helper.rb"
|
43
46
|
]
|
44
47
|
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: streamio-ffmpeg
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- David Backeus
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2010-02-
|
12
|
+
date: 2010-02-07 00:00:00 +01:00
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
@@ -40,8 +40,10 @@ files:
|
|
40
40
|
- Rakefile
|
41
41
|
- VERSION
|
42
42
|
- lib/ffmpeg/movie.rb
|
43
|
+
- lib/ffmpeg/transcoder.rb
|
43
44
|
- lib/streamio-ffmpeg.rb
|
44
45
|
- spec/ffmpeg/movie_spec.rb
|
46
|
+
- spec/ffmpeg/transcoder_spec.rb
|
45
47
|
- spec/fixtures/movies/awesome.mov
|
46
48
|
- spec/spec.opts
|
47
49
|
- spec/spec_helper.rb
|
@@ -76,4 +78,5 @@ specification_version: 3
|
|
76
78
|
summary: Simple wrapper around ffmpeg to get metadata from movies and do transcoding
|
77
79
|
test_files:
|
78
80
|
- spec/ffmpeg/movie_spec.rb
|
81
|
+
- spec/ffmpeg/transcoder_spec.rb
|
79
82
|
- spec/spec_helper.rb
|