streamio-ffmpeg 0.3.0 → 0.4.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/CHANGELOG +5 -0
- data/README.rdoc +14 -7
- data/VERSION +1 -1
- data/lib/ffmpeg/encoding_options.rb +60 -0
- data/lib/ffmpeg/movie.rb +2 -3
- data/lib/ffmpeg/transcoder.rb +32 -9
- data/lib/streamio-ffmpeg.rb +1 -0
- data/spec/ffmpeg/encoding_options_spec.rb +52 -0
- data/spec/ffmpeg/movie_spec.rb +6 -4
- data/spec/ffmpeg/transcoder_spec.rb +60 -6
- data/streamio-ffmpeg.gemspec +6 -3
- metadata +5 -2
data/CHANGELOG
CHANGED
data/README.rdoc
CHANGED
@@ -34,21 +34,28 @@ Simplest possible wrapper around FFMPEG to get metadata from movie files and do
|
|
34
34
|
|
35
35
|
=== Transcoding
|
36
36
|
|
37
|
-
|
37
|
+
First argument is the output file path.
|
38
38
|
|
39
|
-
movie.transcode(
|
39
|
+
movie.transcode("tmp/movie.mp4") # Default ffmpeg settings for mp4 format
|
40
40
|
|
41
41
|
Keep track of progress with an optional block.
|
42
42
|
|
43
|
-
movie.transcode(
|
43
|
+
movie.transcode(movie.mp4") { |progress| puts progress } # 0.2 ... 0.5 ... 1.0
|
44
44
|
|
45
|
-
Give custom command line options
|
45
|
+
Give custom command line options with a string.
|
46
|
+
|
47
|
+
movie.transcode("movie.mp4", "-ac aac -vc libx264 -ac 2 ...")
|
48
|
+
|
49
|
+
Use the EncodingOptions parser for humanly readable transcoding options. Below you'll find all the supported options. Note that the :custom key will be used as is without modification so use it for any tricky business you might need.
|
50
|
+
|
51
|
+
options = {:video_codec => "libx264", :frame_rate => 10, :resolution => "320x240", :video_bitrate => 300,
|
52
|
+
:audio_codec => "libfaac", :audio_bitrate => 32, :audio_sample_rate => 22050, :audio_channels => 1,
|
53
|
+
:custom => "-flags +loop -cmp +chroma -partitions +parti4x4+partp8x8 -flags2 +mixed_refs -me_method umh -subq 6 -refs 6 -rc_eq 'blurCplx^(1-qComp)' -coder 0 -me_range 16 -g 250 -keyint_min 25 -sc_threshold 40 -i_qfactor 0.71 -qcomp 0.6 -qmin 10 -qmax 51 -qdiff 4 -level 21"}
|
54
|
+
movie.transcode("movie.mp4", options)
|
46
55
|
|
47
|
-
movie.transcode(:raw_options => "-ac aac -vc libx264 -ac 2 ...", :output_file => "movie.mp4")
|
48
|
-
|
49
56
|
The transcode function returns a Movie object for the encoded file.
|
50
57
|
|
51
|
-
transcoded_movie = movie.transcode(
|
58
|
+
transcoded_movie = movie.transcode("tmp/movie.flv")
|
52
59
|
|
53
60
|
transcoded_movie.video_codec # "flv"
|
54
61
|
transcoded_movie.audio_codec # "mp3"
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.4.0
|
@@ -0,0 +1,60 @@
|
|
1
|
+
# Include support for -bt -maxrate -minrate -bufsize?
|
2
|
+
|
3
|
+
module FFMPEG
|
4
|
+
class EncodingOptions < Hash
|
5
|
+
def initialize(options = {})
|
6
|
+
merge!(options)
|
7
|
+
end
|
8
|
+
|
9
|
+
def to_s
|
10
|
+
collect do |key, value|
|
11
|
+
send("convert_#{key}", value) if supports_option?(key)
|
12
|
+
end.join(" ")
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
def supports_option?(option)
|
17
|
+
private_methods.include?("convert_#{option}")
|
18
|
+
end
|
19
|
+
|
20
|
+
def convert_video_codec(value)
|
21
|
+
"-vcodec #{value}"
|
22
|
+
end
|
23
|
+
|
24
|
+
def convert_frame_rate(value)
|
25
|
+
"-r #{value}"
|
26
|
+
end
|
27
|
+
|
28
|
+
def convert_resolution(value)
|
29
|
+
"-s #{value}"
|
30
|
+
end
|
31
|
+
|
32
|
+
def convert_video_bitrate(value)
|
33
|
+
"-b #{value}#{k_format(value)}"
|
34
|
+
end
|
35
|
+
|
36
|
+
def convert_audio_codec(value)
|
37
|
+
"-acodec #{value}"
|
38
|
+
end
|
39
|
+
|
40
|
+
def convert_audio_bitrate(value)
|
41
|
+
"-ab #{value}#{k_format(value)}"
|
42
|
+
end
|
43
|
+
|
44
|
+
def convert_audio_sample_rate(value)
|
45
|
+
"-ar #{value}"
|
46
|
+
end
|
47
|
+
|
48
|
+
def convert_audio_channels(value)
|
49
|
+
"-ac #{value}"
|
50
|
+
end
|
51
|
+
|
52
|
+
def convert_custom(value)
|
53
|
+
value
|
54
|
+
end
|
55
|
+
|
56
|
+
def k_format(value)
|
57
|
+
"k" unless value.to_s.include?("k")
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
data/lib/ffmpeg/movie.rb
CHANGED
@@ -56,12 +56,11 @@ module FFMPEG
|
|
56
56
|
end
|
57
57
|
|
58
58
|
def frame_rate
|
59
|
-
video_stream[/(\d
|
59
|
+
video_stream[/(\d*\.?\d*)\s?fps/] ? $1.to_f : nil
|
60
60
|
end
|
61
61
|
|
62
62
|
def transcode(options, &block)
|
63
|
-
|
64
|
-
transcoder.run &block
|
63
|
+
Transcoder.new(self, options).run &block
|
65
64
|
end
|
66
65
|
end
|
67
66
|
end
|
data/lib/ffmpeg/transcoder.rb
CHANGED
@@ -2,15 +2,23 @@ require 'open3'
|
|
2
2
|
|
3
3
|
module FFMPEG
|
4
4
|
class Transcoder
|
5
|
-
def initialize(movie, options)
|
6
|
-
raise ArgumentError, "you need to specify options[:output_file]" unless options[:output_file]
|
7
|
-
|
5
|
+
def initialize(movie, output_file, options = EncodingOptions.new)
|
8
6
|
@movie = movie
|
9
|
-
@
|
7
|
+
@output_file = output_file
|
8
|
+
|
9
|
+
if options.is_a?(String) || options.is_a?(EncodingOptions)
|
10
|
+
@raw_options = options
|
11
|
+
elsif options.is_a?(Hash)
|
12
|
+
@raw_options = EncodingOptions.new(options)
|
13
|
+
else
|
14
|
+
raise ArgumentError, "Unknown options format '#{options.class}', should be either EncodingOptions, Hash or String."
|
15
|
+
end
|
16
|
+
|
17
|
+
@errors = []
|
10
18
|
end
|
11
19
|
|
12
20
|
def run
|
13
|
-
command = "ffmpeg -y -i '#{@movie.path}' #{@
|
21
|
+
command = "ffmpeg -y -i '#{@movie.path}' #{@raw_options} '#{@output_file}'"
|
14
22
|
last_output = nil
|
15
23
|
Open3.popen3(command) do |stdin, stdout, stderr|
|
16
24
|
stderr.each("r") do |line|
|
@@ -24,24 +32,39 @@ module FFMPEG
|
|
24
32
|
end
|
25
33
|
last_output = line
|
26
34
|
end
|
27
|
-
end
|
35
|
+
end
|
28
36
|
|
29
37
|
if encoding_succeeded?
|
30
38
|
yield(1.0) if block_given?
|
31
39
|
else
|
32
|
-
raise "Failed encoding. Last output: #{last_output}.
|
40
|
+
raise "Failed encoding. Last output: #{last_output}. Errors: #{@errors.join(", ")}"
|
33
41
|
end
|
34
42
|
|
35
43
|
encoded
|
36
44
|
end
|
37
45
|
|
38
46
|
def encoding_succeeded?
|
47
|
+
unless File.exists?(@output_file)
|
48
|
+
@errors << "no output file created"
|
49
|
+
return false
|
50
|
+
end
|
51
|
+
|
52
|
+
unless encoded.valid?
|
53
|
+
@errors << "encoded file is invalid"
|
54
|
+
return false
|
55
|
+
end
|
56
|
+
|
39
57
|
precision = 1.1
|
40
|
-
|
58
|
+
unless !(encoded.duration >= (@movie.duration * precision) or encoded.duration <= (@movie.duration / precision))
|
59
|
+
@errors << "encoded file duration differed from original (original: #{@movie.duration}sec, encoded: #{encoded.duration}sec)"
|
60
|
+
return false
|
61
|
+
end
|
62
|
+
|
63
|
+
true
|
41
64
|
end
|
42
65
|
|
43
66
|
def encoded
|
44
|
-
@encoded ||= Movie.new(@
|
67
|
+
@encoded ||= Movie.new(@output_file)
|
45
68
|
end
|
46
69
|
end
|
47
70
|
end
|
data/lib/streamio-ffmpeg.rb
CHANGED
@@ -0,0 +1,52 @@
|
|
1
|
+
require 'spec_helper.rb'
|
2
|
+
|
3
|
+
module FFMPEG
|
4
|
+
describe EncodingOptions do
|
5
|
+
describe "ffmpeg arguments conversion" do
|
6
|
+
it "should convert video codec" do
|
7
|
+
EncodingOptions.new(:video_codec => "libx264").to_s.should == "-vcodec libx264"
|
8
|
+
end
|
9
|
+
|
10
|
+
it "should convert frame rate" do
|
11
|
+
EncodingOptions.new(:frame_rate => 29.9).to_s.should == "-r 29.9"
|
12
|
+
end
|
13
|
+
|
14
|
+
it "should convert the resolution" do
|
15
|
+
EncodingOptions.new(:resolution => "640x480").to_s.should == "-s 640x480"
|
16
|
+
end
|
17
|
+
|
18
|
+
it "should convert video bitrate" do
|
19
|
+
EncodingOptions.new(:video_bitrate => "600k").to_s.should == "-b 600k"
|
20
|
+
end
|
21
|
+
|
22
|
+
it "should use k unit for video bitrate" do
|
23
|
+
EncodingOptions.new(:video_bitrate => 600).to_s.should == "-b 600k"
|
24
|
+
end
|
25
|
+
|
26
|
+
it "should convert audio codec" do
|
27
|
+
EncodingOptions.new(:audio_codec => "aac").to_s.should == "-acodec aac"
|
28
|
+
end
|
29
|
+
|
30
|
+
it "should convert audio bitrate" do
|
31
|
+
EncodingOptions.new(:audio_bitrate => "128k").to_s.should == "-ab 128k"
|
32
|
+
end
|
33
|
+
|
34
|
+
it "should use k unit for audio bitrate" do
|
35
|
+
EncodingOptions.new(:audio_bitrate => 128).to_s.should == "-ab 128k"
|
36
|
+
end
|
37
|
+
|
38
|
+
it "should convert audio sample rate" do
|
39
|
+
EncodingOptions.new(:audio_sample_rate => 44100).to_s.should == "-ar 44100"
|
40
|
+
end
|
41
|
+
|
42
|
+
it "should convert audio channels" do
|
43
|
+
EncodingOptions.new(:audio_channels => 2).to_s.should == "-ac 2"
|
44
|
+
end
|
45
|
+
|
46
|
+
it "should convert a lot of them simultaneously" do
|
47
|
+
converted = EncodingOptions.new(:video_codec => "libx264", :audio_codec => "aac", :video_bitrate => "1000k").to_s
|
48
|
+
converted.should match(/-acodec aac/)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
data/spec/ffmpeg/movie_spec.rb
CHANGED
@@ -86,11 +86,13 @@ module FFMPEG
|
|
86
86
|
|
87
87
|
describe "transcode" do
|
88
88
|
it "should run the transcoder" do
|
89
|
-
FileUtils.rm_f "#{tmp_path}/awesome.flv"
|
90
89
|
movie = Movie.new("#{fixture_path}/movies/awesome.mov")
|
91
|
-
|
92
|
-
|
93
|
-
|
90
|
+
|
91
|
+
mockery = mock(Transcoder)
|
92
|
+
Transcoder.should_receive(:new).with(movie, "#{tmp_path}/awesome.flv").and_return(mockery)
|
93
|
+
mockery.should_receive(:run)
|
94
|
+
|
95
|
+
movie.transcode("#{tmp_path}/awesome.flv")
|
94
96
|
end
|
95
97
|
end
|
96
98
|
end
|
@@ -2,21 +2,75 @@ require 'spec_helper.rb'
|
|
2
2
|
|
3
3
|
module FFMPEG
|
4
4
|
describe Transcoder do
|
5
|
-
describe "
|
6
|
-
|
7
|
-
|
5
|
+
describe "initialization" do
|
6
|
+
before(:each) do
|
7
|
+
@movie = Movie.new("#{fixture_path}/movies/awesome.mov")
|
8
|
+
@output_path = "#{tmp_path}/awesome.flv"
|
9
|
+
end
|
10
|
+
|
11
|
+
it "should accept EncodingOptions as options" do
|
12
|
+
lambda { Transcoder.new(@movie, @output_path, EncodingOptions.new) }.should_not raise_error(ArgumentError)
|
13
|
+
end
|
14
|
+
|
15
|
+
it "should accept Hash as options" do
|
16
|
+
lambda { Transcoder.new(@movie, @output_path, :video_codec => "libx264") }.should_not raise_error(ArgumentError)
|
17
|
+
end
|
18
|
+
|
19
|
+
it "should accept String as options" do
|
20
|
+
lambda { Transcoder.new(@movie, @output_path, "-vcodec libx264") }.should_not raise_error(ArgumentError)
|
21
|
+
end
|
22
|
+
|
23
|
+
it "should not accept anything else as options" do
|
24
|
+
lambda { Transcoder.new(@movie, @output_path, ["array?"]) }.should raise_error(ArgumentError, /Unknown options format/)
|
8
25
|
end
|
9
26
|
end
|
10
27
|
|
11
28
|
describe "transcoding" do
|
12
|
-
it "should transcode the movie" do
|
29
|
+
it "should transcode the movie with progress given an awesome movie" do
|
13
30
|
FileUtils.rm_f "#{tmp_path}/awesome.flv"
|
31
|
+
|
14
32
|
movie = Movie.new("#{fixture_path}/movies/awesome.mov")
|
15
|
-
|
16
|
-
transcoder.
|
33
|
+
|
34
|
+
transcoder = Transcoder.new(movie, "#{tmp_path}/awesome.flv")
|
35
|
+
stored_progress = 0
|
36
|
+
transcoder.run { |progress| stored_progress = progress }
|
17
37
|
transcoder.encoded.should be_valid
|
38
|
+
stored_progress.should == 1.0
|
18
39
|
File.exists?("#{tmp_path}/awesome.flv").should be_true
|
19
40
|
end
|
41
|
+
|
42
|
+
it "should transcode the movie with EncodingOptions" do
|
43
|
+
FileUtils.rm_f "#{tmp_path}/optionalized.mp4"
|
44
|
+
|
45
|
+
movie = Movie.new("#{fixture_path}/movies/awesome.mov")
|
46
|
+
options = {:video_codec => "libx264", :frame_rate => 10, :resolution => "320x240", :video_bitrate => 300,
|
47
|
+
:audio_codec => "libfaac", :audio_bitrate => 32, :audio_sample_rate => 22050, :audio_channels => 1,
|
48
|
+
:custom => "-flags +loop -cmp +chroma -partitions +parti4x4+partp8x8 -flags2 +mixed_refs -me_method umh -subq 6 -refs 6 -rc_eq 'blurCplx^(1-qComp)' -coder 0 -me_range 16 -g 250 -keyint_min 25 -sc_threshold 40 -i_qfactor 0.71 -qcomp 0.6 -qmin 10 -qmax 51 -qdiff 4 -level 21"}
|
49
|
+
|
50
|
+
encoded = Transcoder.new(movie, "#{tmp_path}/optionalized.mp4", options).run
|
51
|
+
encoded.video_codec.should == "h264"
|
52
|
+
encoded.resolution.should == "320x240"
|
53
|
+
encoded.frame_rate.should == 10.0
|
54
|
+
encoded.audio_codec.should == "aac"
|
55
|
+
encoded.audio_sample_rate.should == 22050
|
56
|
+
encoded.audio_channels.should == 1
|
57
|
+
end
|
58
|
+
|
59
|
+
it "should transcode the movie with String options" do
|
60
|
+
FileUtils.rm_f "#{tmp_path}/string_optionalized.flv"
|
61
|
+
|
62
|
+
movie = Movie.new("#{fixture_path}/movies/awesome.mov")
|
63
|
+
|
64
|
+
encoded = Transcoder.new(movie, "#{tmp_path}/string_optionalized.flv", "-s 300x200 -ac 2").run
|
65
|
+
encoded.resolution.should == "300x200"
|
66
|
+
encoded.audio_channels.should == 2
|
67
|
+
end
|
68
|
+
|
69
|
+
it "should fail when given an invalid movie" do
|
70
|
+
movie = Movie.new(__FILE__)
|
71
|
+
transcoder = Transcoder.new(movie, "#{tmp_path}/fail.flv")
|
72
|
+
lambda { transcoder.run }.should raise_error(RuntimeError, /no output file created/)
|
73
|
+
end
|
20
74
|
end
|
21
75
|
end
|
22
76
|
end
|
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.4.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-10}
|
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 = [
|
@@ -24,9 +24,11 @@ Gem::Specification.new do |s|
|
|
24
24
|
"README.rdoc",
|
25
25
|
"Rakefile",
|
26
26
|
"VERSION",
|
27
|
+
"lib/ffmpeg/encoding_options.rb",
|
27
28
|
"lib/ffmpeg/movie.rb",
|
28
29
|
"lib/ffmpeg/transcoder.rb",
|
29
30
|
"lib/streamio-ffmpeg.rb",
|
31
|
+
"spec/ffmpeg/encoding_options_spec.rb",
|
30
32
|
"spec/ffmpeg/movie_spec.rb",
|
31
33
|
"spec/ffmpeg/transcoder_spec.rb",
|
32
34
|
"spec/fixtures/movies/awesome.mov",
|
@@ -40,7 +42,8 @@ Gem::Specification.new do |s|
|
|
40
42
|
s.rubygems_version = %q{1.3.5}
|
41
43
|
s.summary = %q{Simple wrapper around ffmpeg to get metadata from movies and do transcoding}
|
42
44
|
s.test_files = [
|
43
|
-
"spec/ffmpeg/
|
45
|
+
"spec/ffmpeg/encoding_options_spec.rb",
|
46
|
+
"spec/ffmpeg/movie_spec.rb",
|
44
47
|
"spec/ffmpeg/transcoder_spec.rb",
|
45
48
|
"spec/spec_helper.rb"
|
46
49
|
]
|
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.4.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-10 00:00:00 +01:00
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
@@ -39,9 +39,11 @@ files:
|
|
39
39
|
- README.rdoc
|
40
40
|
- Rakefile
|
41
41
|
- VERSION
|
42
|
+
- lib/ffmpeg/encoding_options.rb
|
42
43
|
- lib/ffmpeg/movie.rb
|
43
44
|
- lib/ffmpeg/transcoder.rb
|
44
45
|
- lib/streamio-ffmpeg.rb
|
46
|
+
- spec/ffmpeg/encoding_options_spec.rb
|
45
47
|
- spec/ffmpeg/movie_spec.rb
|
46
48
|
- spec/ffmpeg/transcoder_spec.rb
|
47
49
|
- spec/fixtures/movies/awesome.mov
|
@@ -77,6 +79,7 @@ signing_key:
|
|
77
79
|
specification_version: 3
|
78
80
|
summary: Simple wrapper around ffmpeg to get metadata from movies and do transcoding
|
79
81
|
test_files:
|
82
|
+
- spec/ffmpeg/encoding_options_spec.rb
|
80
83
|
- spec/ffmpeg/movie_spec.rb
|
81
84
|
- spec/ffmpeg/transcoder_spec.rb
|
82
85
|
- spec/spec_helper.rb
|