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 CHANGED
@@ -1,3 +1,8 @@
1
+ == 0.4.0 2009-02-10
2
+
3
+ * Transcoding API changed to make use of more humanly readable options (see README for examples)
4
+ * Fixed frame rate parsing for integer frame rates
5
+
1
6
  == 0.3.0 2009-02-07
2
7
 
3
8
  * Simple transcoding
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
- Use :output_file option to specify the output file path.
37
+ First argument is the output file path.
38
38
 
39
- movie.transcode(:output_file => "tmp/movie.mp4") # Default ffmpeg settings for mp4 format
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(:output_file => "movie.mp4") { |progress| puts progress } # 0.2 ... 0.5 ... 1.0
43
+ movie.transcode(movie.mp4") { |progress| puts progress } # 0.2 ... 0.5 ... 1.0
44
44
 
45
- Give custom command line options to ffmpeg through :raw_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(:output_file => "tmp/movie.flv")
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.3.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*\.\d*)\s?fps/] ? $1.to_f : nil
59
+ video_stream[/(\d*\.?\d*)\s?fps/] ? $1.to_f : nil
60
60
  end
61
61
 
62
62
  def transcode(options, &block)
63
- transcoder = Transcoder.new(self, options)
64
- transcoder.run &block
63
+ Transcoder.new(self, options).run &block
65
64
  end
66
65
  end
67
66
  end
@@ -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
- @options = options
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}' #{@options[:raw_options]} '#{@options[:output_file]}'"
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}. Original duration: #{@movie.duration}. Encoded duration: #{encoded.duration}."
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
- encoded.valid? && !(encoded.duration >= (@movie.duration * precision) or encoded.duration <= (@movie.duration / precision))
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(@options[:output_file])
67
+ @encoded ||= Movie.new(@output_file)
45
68
  end
46
69
  end
47
70
  end
@@ -2,6 +2,7 @@ $LOAD_PATH.unshift File.dirname(__FILE__)
2
2
 
3
3
  require 'ffmpeg/movie'
4
4
  require 'ffmpeg/transcoder'
5
+ require 'ffmpeg/encoding_options'
5
6
 
6
7
  module FFMPEG
7
8
  VERSION = '0.1.0'
@@ -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
@@ -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
- encoded = movie.transcode(:output_file => "#{tmp_path}/awesome.flv")
92
- encoded.should be_valid
93
- File.exists?("#{tmp_path}/awesome.flv").should be_true
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 "initializing" do
6
- it "should require an output_file option" do
7
- lambda { Transcoder.new(nil, {}) }.should raise_error(ArgumentError, /output_file/)
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
- transcoder = Transcoder.new(movie, :output_file => "tmp/awesome.flv")
16
- transcoder.run
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
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{streamio-ffmpeg}
8
- s.version = "0.3.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-07}
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/movie_spec.rb",
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.3.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-07 00:00:00 +01:00
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