stlondemand-rvideo 0.9.7

Sign up to get free protection for your applications and to get access to all the features.
Files changed (73) hide show
  1. data/CHANGELOG +70 -0
  2. data/ENV +100 -0
  3. data/ENV2 +129 -0
  4. data/LICENSE +20 -0
  5. data/Manifest +72 -0
  6. data/README +106 -0
  7. data/RULES +11 -0
  8. data/Rakefile +63 -0
  9. data/config/boot.rb +25 -0
  10. data/lib/rvideo.rb +49 -0
  11. data/lib/rvideo/command_executor.rb +91 -0
  12. data/lib/rvideo/errors.rb +24 -0
  13. data/lib/rvideo/float.rb +7 -0
  14. data/lib/rvideo/frame_capturer.rb +139 -0
  15. data/lib/rvideo/inspector.rb +519 -0
  16. data/lib/rvideo/reporter.rb +176 -0
  17. data/lib/rvideo/reporter/views/index.html.erb +27 -0
  18. data/lib/rvideo/reporter/views/report.css +27 -0
  19. data/lib/rvideo/reporter/views/report.html.erb +81 -0
  20. data/lib/rvideo/reporter/views/report.js +9 -0
  21. data/lib/rvideo/string.rb +5 -0
  22. data/lib/rvideo/tools/abstract_tool.rb +459 -0
  23. data/lib/rvideo/tools/ffmpeg.rb +314 -0
  24. data/lib/rvideo/tools/ffmpeg2theora.rb +72 -0
  25. data/lib/rvideo/tools/flvtool2.rb +50 -0
  26. data/lib/rvideo/tools/handbrakecli.rb +61 -0
  27. data/lib/rvideo/tools/lame.rb +58 -0
  28. data/lib/rvideo/tools/mencoder.rb +126 -0
  29. data/lib/rvideo/tools/mp4box.rb +21 -0
  30. data/lib/rvideo/tools/mp4creator.rb +35 -0
  31. data/lib/rvideo/tools/mplayer.rb +31 -0
  32. data/lib/rvideo/tools/qtfaststart.rb +37 -0
  33. data/lib/rvideo/tools/segmenter.rb +29 -0
  34. data/lib/rvideo/tools/yamdi.rb +44 -0
  35. data/lib/rvideo/transcoder.rb +170 -0
  36. data/lib/rvideo/version.rb +9 -0
  37. data/scripts/txt2html +67 -0
  38. data/spec/files/boat.avi +0 -0
  39. data/spec/files/kites.mp4 +0 -0
  40. data/spec/fixtures/ffmpeg_builds.yml +28 -0
  41. data/spec/fixtures/ffmpeg_results.yml +608 -0
  42. data/spec/fixtures/files.yml +398 -0
  43. data/spec/fixtures/recipes.yml +58 -0
  44. data/spec/integrations/formats_spec.rb +315 -0
  45. data/spec/integrations/frame_capturer_spec.rb +26 -0
  46. data/spec/integrations/inspection_spec.rb +125 -0
  47. data/spec/integrations/recipes_spec.rb +0 -0
  48. data/spec/integrations/rvideo_spec.rb +17 -0
  49. data/spec/integrations/transcoder_integration_spec.rb +29 -0
  50. data/spec/integrations/transcoding_spec.rb +9 -0
  51. data/spec/spec.opts +1 -0
  52. data/spec/spec_helper.rb +16 -0
  53. data/spec/support.rb +36 -0
  54. data/spec/units/abstract_tool_spec.rb +111 -0
  55. data/spec/units/command_executor_spec.rb +106 -0
  56. data/spec/units/ffmpeg_spec.rb +385 -0
  57. data/spec/units/flvtool2_spec.rb +323 -0
  58. data/spec/units/frame_capturer_spec.rb +71 -0
  59. data/spec/units/inspector_spec.rb +59 -0
  60. data/spec/units/mencoder_spec.rb +4994 -0
  61. data/spec/units/mp4box_spec.rb +34 -0
  62. data/spec/units/mp4creator_spec.rb +34 -0
  63. data/spec/units/mplayer_spec.rb +34 -0
  64. data/spec/units/qtfaststart_spec.rb +35 -0
  65. data/spec/units/string_spec.rb +8 -0
  66. data/spec/units/transcoder_spec.rb +154 -0
  67. data/stlondemand-rvideo.gemspec +36 -0
  68. data/tasks/deployment.rake +5 -0
  69. data/tasks/testing.rake +27 -0
  70. data/tasks/transcoding.rake +40 -0
  71. data/tasks/website.rake +8 -0
  72. data/test_progress_reporting.rb +14 -0
  73. metadata +187 -0
File without changes
@@ -0,0 +1,17 @@
1
+ require File.dirname(__FILE__) + "/../spec_helper"
2
+
3
+ module RVideo
4
+ describe RVideo, " with every recipe and file" do
5
+ it "should interpolate commands correctly" do
6
+
7
+ end
8
+
9
+ it "should parse prepopulated output successfully" do
10
+
11
+ end
12
+
13
+ it "should transcode to a viewable file" do
14
+
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,29 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper'
2
+
3
+ module RVideo
4
+ describe "The entire system should work together" do
5
+ it "with kites.mp4" do
6
+ output_file = "#{TEMP_PATH}/kites.flv"
7
+ FileUtils.rm_f(output_file)
8
+ FileTest.exist?(output_file).should_not be_true
9
+ transcoder = Transcoder.new(spec_file("kites.mp4"))
10
+ transcoder.original.class.should == Inspector
11
+ transcoder.original.duration.should == 19600
12
+ transcoder.execute(recipes('flash_300')['command'], {:output_file => output_file})
13
+ FileTest.exist?(output_file).should be_true
14
+ end
15
+
16
+ # it "with The Michael Scott Story" do
17
+ # file = 'michael_scott_story.mov'
18
+ # output_file = "#{TEMP_PATH}/#{file}.flv"
19
+ # FileUtils.rm_f(output_file)
20
+ # FileTest.exist?(output_file).should_not be_true
21
+ # transcoder = Transcoder.new("#{TEST_FILE_PATH}/#{file}")
22
+ # transcoder.original.class.should == Inspector
23
+ # transcoder.original.duration.should == 30400
24
+ # transcoder.execute(recipes('one_pass')['command'], {:output_file => output_file})
25
+ # FileTest.exist?(output_file).should be_true
26
+ # end
27
+
28
+ end
29
+ end
@@ -0,0 +1,9 @@
1
+ require File.dirname(__FILE__) + "/../spec_helper"
2
+
3
+ module RVideo
4
+ describe RVideo do
5
+ before do
6
+
7
+ end
8
+ end
9
+ end
@@ -0,0 +1 @@
1
+ --color
@@ -0,0 +1,16 @@
1
+ require File.dirname(__FILE__) + '/../lib/rvideo'
2
+
3
+ # Because rspec's assertion style is a stupid, giant
4
+ # fucking waste of time. It's true!
5
+ require 'spec/interop/test'
6
+ require File.dirname(__FILE__) + "/support"
7
+
8
+ # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
9
+ # NOTE some expectations in these specs may rely on behavior #
10
+ # not present in your local build of ffmpeg. #
11
+ # #
12
+ # Any work on supporting multiple builds packaged with the tests #
13
+ # would be accepted, but ffmpeg itself does not support old versions #
14
+ # so it might be best to upgrade if you want to rely on the accuracy of #
15
+ # these tests. #
16
+ # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
@@ -0,0 +1,36 @@
1
+ ###
2
+
3
+ FIXTURE_PATH = File.expand_path(File.dirname(__FILE__) + '/../spec/fixtures')
4
+
5
+ LOG_PATH = File.join(File.dirname(__FILE__), "spec.log")
6
+ RVideo.logger = Logger.new LOG_PATH
7
+
8
+ ###
9
+
10
+ def ffmpeg(key)
11
+ load_fixture(:ffmpeg_builds)[key.to_s]['response']
12
+ end
13
+
14
+ def files(key)
15
+ load_fixture(:files)[key.to_s]['response']
16
+ end
17
+
18
+ def recipes(key)
19
+ load_fixture(:recipes)[key.to_s]
20
+ end
21
+
22
+ # The strip in here is important as the result parsing is apparently
23
+ # quite fussy about leading or trailing whitespace.
24
+ def ffmpeg_result(key)
25
+ load_fixture(:ffmpeg_results)[key.to_s].strip
26
+ end
27
+
28
+ ###
29
+
30
+ def load_fixture(name)
31
+ YAML.load_file("#{FIXTURE_PATH}/#{name}.yml")
32
+ end
33
+
34
+ def spec_file(name)
35
+ File.expand_path File.join(File.dirname(__FILE__), "files", name)
36
+ end
@@ -0,0 +1,111 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper'
2
+
3
+ module RVideo
4
+ module Tools
5
+ describe AbstractTool, "#assign" do
6
+
7
+ before do
8
+ @command = "ffmpeg -i $input_file$ $output_file$"
9
+ @options = {
10
+ :input_file => spec_file("kites.mp4"), # "foo",
11
+ :output_file => "bar"
12
+ }
13
+ end
14
+
15
+ it "should assign commands properly with options - ffmpeg" do
16
+ Ffmpeg.should_receive(:new).with(@command, @options)
17
+ AbstractTool.assign(@command, @options)
18
+ end
19
+
20
+ it "should assign properly without options - ffmpeg" do
21
+ Ffmpeg.should_receive(:new).with(@command, {})
22
+ AbstractTool.assign(@command)
23
+ end
24
+
25
+ it "should return an instance of the specified tool" do
26
+ tool = AbstractTool.assign(@command, @options)
27
+ tool.class.should == Ffmpeg
28
+ end
29
+ end
30
+
31
+ describe AbstractTool, " when building a command" do
32
+
33
+ before do
34
+ @options = {
35
+ :input_file => spec_file("kites.mp4"), #"foo",
36
+ :output_file => "bar",
37
+ :resolution => "copy"
38
+ }
39
+ @simple_avi = "ffmpeg -i $input_file$ -ar 44100 -ab 64 -vcodec xvid -acodec mp3 -r 29.97 -y $output_file$"
40
+ end
41
+
42
+ it "should set supported options successfully" do
43
+ ffmpeg = Ffmpeg.new(@simple_avi, @options)
44
+ ffmpeg.options['resolution'].should == @options[:resolution]
45
+ ffmpeg.options['input_file'].should == @options[:input_file]
46
+ ffmpeg.options['output_file'].should == @options[:output_file]
47
+ end
48
+
49
+ it "should work if options defined as strings, but referenced as symbols" do
50
+ @options.stringify_keys!
51
+ ffmpeg = Ffmpeg.new(@simple_avi, @options)
52
+ ffmpeg.options[:resolution].should == @options['resolution']
53
+ ffmpeg.options[:input_file].should == @options['input_file']
54
+ ffmpeg.options[:output_file].should == @options['output_file']
55
+ end
56
+
57
+ it "should ignore extra options (not needed by the recipe)" do
58
+ Ffmpeg.new(@simple_avi, @options.merge(:foo => "bar"))
59
+ end
60
+
61
+ it "should interpolate variables successfully" do
62
+ ffmpeg = Ffmpeg.new(@simple_avi, @options)
63
+ ffmpeg.command.should == "ffmpeg -i '#{@options[:input_file]}' -ar 44100 -ab 64 -vcodec xvid -acodec mp3 -r 29.97 -y '#{@options[:output_file]}'"
64
+ end
65
+
66
+ it "the matched_variable method should reference the variable without $" do
67
+ ffmpeg = Ffmpeg.new(@simple_avi, @options)
68
+ ffmpeg.send(:matched_variable, "$input_file$").should == spec_file("kites.mp4")
69
+ end
70
+
71
+ it "the matched_variable method should raise an error when the variable is not found" do
72
+ ffmpeg = Ffmpeg.new(@simple_avi, @options)
73
+ lambda {
74
+ ffmpeg.send(:matched_variable, "$foo$")
75
+ }.should raise_error(TranscoderError::ParameterError)
76
+ end
77
+
78
+ it "should raise an exception when a required variable isn't set (1)" do
79
+ lambda {
80
+ ffmpeg = Ffmpeg.new(@simple_avi, {:output_file => "baz"})
81
+ }.should raise_error(TranscoderError::ParameterError)
82
+ end
83
+
84
+ it "should raise an error when a recipe includes a variable not supplied (2)" do
85
+ lambda {
86
+ ffmpeg = Ffmpeg.new(@simple_avi + " $novar$", @options)
87
+ }.should raise_error(TranscoderError::ParameterError)
88
+ end
89
+
90
+ it "should not raise an error when a variable is supplied but nil" do
91
+ ffmpeg = Ffmpeg.new(@simple_avi, @options.merge(:resolution => nil))
92
+ ffmpeg.command.should == "ffmpeg -i '#{@options[:input_file]}' -ar 44100 -ab 64 -vcodec xvid -acodec mp3 -r 29.97 -y '#{@options[:output_file]}'"
93
+ end
94
+
95
+ it 'should ignore escaped leading \$' do
96
+ ffmpeg = Ffmpeg.new('ffmpeg -i $input_file$ -ar 44100 -ab 64 -fakeoptions \$foo$ -vcodec xvid -acodec mp3 -r 29.97 $resolution$ -y $output_file$', @options)
97
+ ffmpeg.command.should == "ffmpeg -i '#{@options[:input_file]}' -ar 44100 -ab 64 -fakeoptions $foo$ -vcodec xvid -acodec mp3 -r 29.97 #{ffmpeg.resolution} -y '#{@options[:output_file]}'"
98
+ end
99
+
100
+ it 'should ignore escaped trailing \$' do
101
+ ffmpeg = Ffmpeg.new('ffmpeg -i $input_file$ -ar 44100 -ab 64 -fakeoptions $foo\$ -vcodec xvid -acodec mp3 -r 29.97 $resolution$ -y $output_file$', @options)
102
+ ffmpeg.command.should == "ffmpeg -i '#{@options[:input_file]}' -ar 44100 -ab 64 -fakeoptions $foo$ -vcodec xvid -acodec mp3 -r 29.97 #{ffmpeg.resolution} -y '#{@options[:output_file]}'"
103
+ end
104
+
105
+ it 'should ignore two escaped \$' do
106
+ ffmpeg = Ffmpeg.new('ffmpeg -i $input_file$ -ar 44100 -ab 64 -fakeoptions \$foo\$ -vcodec xvid -acodec mp3 -r 29.97 $resolution$ -y $output_file$', @options)
107
+ ffmpeg.command.should == "ffmpeg -i '#{@options[:input_file]}' -ar 44100 -ab 64 -fakeoptions $foo$ -vcodec xvid -acodec mp3 -r 29.97 #{ffmpeg.resolution} -y '#{@options[:output_file]}'"
108
+ end
109
+ end
110
+ end
111
+ end
@@ -0,0 +1,106 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper'
2
+
3
+ include RVideo
4
+
5
+ describe RVideo::CommandExecutor do
6
+
7
+ RVideo::CommandExecutor.const_set("STDOUT_TIMEOUT", 1.5)
8
+
9
+ before(:each) do
10
+ @file = "/tmp/command_executor_tmp_output"
11
+ FileUtils.rm_f(@file)
12
+ end
13
+
14
+
15
+ describe "exectue_tailing_stderr" do
16
+
17
+ it "should execute command" do
18
+ CommandExecutor::execute_tailing_stderr("echo abc 1>&2").should == "abc\n"
19
+ end
20
+
21
+ it "should return only specified number of lines from output" do
22
+ CommandExecutor::execute_tailing_stderr("echo abc 1>&2; echo def 1>&2; echo ghi 1>&2; echo jkl 1>&2;", 2).should == "ghi\njkl\n"
23
+ end
24
+
25
+ end
26
+
27
+
28
+ describe "execute with block" do
29
+
30
+ it "should execute command" do
31
+ CommandExecutor::execute_with_block("echo abc > #{@file}")
32
+ File.read(@file).chop.should == "abc"
33
+ end
34
+
35
+ it "should exit before command finishes if there is no output within timeout" do
36
+ lambda {
37
+ CommandExecutor::execute_with_block("sleep 2; echo abc > #{@file}")
38
+ }.should raise_error(RVideo::CommandExecutor::ProcessHungError)
39
+ File.exist?(@file).should be_false
40
+ end
41
+
42
+ it "should not exit if command doesn't exit timeout" do
43
+ CommandExecutor::execute_with_block("sleep 1; echo abc > #{@file}")
44
+ File.read(@file).chop.should == "abc"
45
+ end
46
+
47
+ it "should yield block for each line of stderr" do
48
+ string = ""
49
+ CommandExecutor::execute_with_block("echo abc 1>&2; echo def 1>&2") do |line|
50
+ string << line
51
+ end
52
+ string.should == "abc\ndef\n"
53
+ end
54
+
55
+ it "should stop reading IO when timeout exceeds" do
56
+ string = ""
57
+ lambda {
58
+ CommandExecutor::execute_with_block("sleep 1; echo abc 1>&2; sleep 1; echo def 1>&2; sleep 2; echo ghi 1>&2") do |line|
59
+ string << line
60
+ end
61
+ }.should raise_error(RVideo::CommandExecutor::ProcessHungError)
62
+ string.should == "abc\ndef\n"
63
+ end
64
+
65
+ it "should not return line from stdout" do
66
+ string = ""
67
+ CommandExecutor::execute_with_block("echo abc; echo def 1>&2;") do |line|
68
+ string << line
69
+ end
70
+ string.should == "def\n"
71
+ end
72
+
73
+ it "should kill the process" do
74
+ number = rand(100000000000) + 100000
75
+ lambda {
76
+ CommandExecutor::execute_with_block("sleep #{number}")
77
+ }.should raise_error(RVideo::CommandExecutor::ProcessHungError)
78
+ `ps aux | grep #{number} | grep -v grep`.should be_empty
79
+ end
80
+ end
81
+
82
+ it "should save stdout and stderr" do
83
+ string = ""
84
+ stderr_res = nil
85
+ stdout_res = nil
86
+ stderr_res, stdout_res = CommandExecutor::execute_with_block("echo abc; echo def 1>&2;", '\r') do |line|
87
+ string << line
88
+ end
89
+
90
+ stderr_res.should == "def\n"
91
+ stdout_res.should == "abc\n"
92
+ end
93
+
94
+ it "should save stdout and stderr using stdout" do
95
+ string = ""
96
+ stderr_res = nil
97
+ stdout_res = nil
98
+ stderr_res, stdout_res = CommandExecutor::execute_with_block("echo abc; echo def 1>&2;", '\r', false) do |line|
99
+ string << line
100
+ end
101
+
102
+ stderr_res.should == "def\n"
103
+ stdout_res.should == "abc\n"
104
+ end
105
+
106
+ end
@@ -0,0 +1,385 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper'
2
+
3
+ def setup_ffmpeg_spec
4
+ @options = {
5
+ :input_file => spec_file("kites.mp4"),
6
+ :output_file => "bar",
7
+ :width => "320", :height => "240"
8
+ }
9
+ @simple_avi = "ffmpeg -i $input_file$ -ar 44100 -ab 64 -vcodec xvid -acodec libmp3lame -r 29.97 $resolution$ -y $output_file$"
10
+ @ffmpeg = RVideo::Tools::Ffmpeg.new(@simple_avi, @options)
11
+ end
12
+
13
+ def parsing_result(result_fixture_key)
14
+ lambda { @ffmpeg.send(:parse_result, ffmpeg_result(result_fixture_key)) }
15
+ end
16
+
17
+ module RVideo
18
+ module Tools
19
+
20
+ describe Ffmpeg do
21
+ before do
22
+ setup_ffmpeg_spec
23
+ end
24
+
25
+ it "should initialize with valid arguments" do
26
+ @ffmpeg.class.should == Ffmpeg
27
+ end
28
+
29
+ it "should have the correct tool_command" do
30
+ @ffmpeg.tool_command.should == 'ffmpeg'
31
+ end
32
+
33
+ it "should call parse_result on execute, with a ffmpeg result string" do
34
+ @ffmpeg.should_receive(:parse_result).once.with /\AFFmpeg version/
35
+ @ffmpeg.execute
36
+ end
37
+
38
+ it "should execute execute_with_progress" do
39
+ @ffmpeg.execute_with_progress
40
+ end
41
+
42
+ it "should mixin AbstractTool" do
43
+ Ffmpeg.included_modules.include?(AbstractTool::InstanceMethods).should be_true
44
+ end
45
+
46
+ it "should set supported options successfully" do
47
+ @ffmpeg.options[:resolution].should == @options[:resolution]
48
+ @ffmpeg.options[:input_file].should == @options[:input_file]
49
+ @ffmpeg.options[:output_file].should == @options[:output_file]
50
+ end
51
+
52
+ end
53
+
54
+ describe Ffmpeg, " magic variables" do
55
+ before do
56
+ @options = {
57
+ :input_file => spec_file("boat.avi"),
58
+ :output_file => "test"
59
+ }
60
+
61
+ Ffmpeg.video_bit_rate_parameter = Ffmpeg::DEFAULT_VIDEO_BIT_RATE_PARAMETER
62
+ end
63
+
64
+ it 'supports copying the originsl :fps' do
65
+ @options.merge! :fps => "copy"
66
+ ffmpeg = Ffmpeg.new("ffmpeg -i $input_file$ -ar 44100 -ab 64 -vcodec xvid -acodec libmp3lame $fps$ -vf 'scale=320:240' -y $output_file$", @options)
67
+ ffmpeg.command.should == "ffmpeg -i '#{@options[:input_file]}' -ar 44100 -ab 64 -vcodec xvid -acodec libmp3lame -r 15.10 -vf 'scale=320:240' -y '#{@options[:output_file]}'"
68
+ end
69
+
70
+ it 'supports :width and :height options to build :resolution' do
71
+ @options.merge! :width => "640", :height => "360"
72
+ ffmpeg = Ffmpeg.new("ffmpeg -i $input_file$ -ar 44100 -ab 64 -vcodec xvid -acodec libmp3lame -r 29.97 $resolution$ -y $output_file$", @options)
73
+ ffmpeg.command.should == "ffmpeg -i '#{@options[:input_file]}' -ar 44100 -ab 64 -vcodec xvid -acodec libmp3lame -r 29.97 -vf 'scale=640:360' -y '#{@options[:output_file]}'"
74
+ end
75
+
76
+ it 'supports calculated :height' do
77
+ @options.merge! :width => "640"
78
+ ffmpeg = Ffmpeg.new("ffmpeg -i $input_file$ -ar 44100 -ab 64 -vcodec xvid -acodec libmp3lame -r 29.97 $resolution$ -y $output_file$", @options)
79
+ ffmpeg.command.should == "ffmpeg -i '#{@options[:input_file]}' -ar 44100 -ab 64 -vcodec xvid -acodec libmp3lame -r 29.97 -vf 'scale=640:480' -y '#{@options[:output_file]}'"
80
+ end
81
+
82
+ it 'supports calculated :width' do
83
+ @options.merge! :height => "360"
84
+ ffmpeg = Ffmpeg.new("ffmpeg -i $input_file$ -ar 44100 -ab 64 -vcodec xvid -acodec libmp3lame -r 29.97 $resolution$ -y $output_file$", @options)
85
+ ffmpeg.command.should == "ffmpeg -i '#{@options[:input_file]}' -ar 44100 -ab 64 -vcodec xvid -acodec libmp3lame -r 29.97 -vf 'scale=480:360' -y '#{@options[:output_file]}'"
86
+ end
87
+
88
+ it 'supports :width and :height options to build :resolution_and_padding' do
89
+ @options.merge! :width => "160", :height => "120"
90
+ ffmpeg = Ffmpeg.new("ffmpeg -i $input_file$ -ar 44100 -ab 64 -vcodec xvid -acodec libmp3lame -r 29.97 $resolution_and_padding$ -y $output_file$", @options)
91
+ ffmpeg.command.should == "ffmpeg -i '#{@options[:input_file]}' -ar 44100 -ab 64 -vcodec xvid -acodec libmp3lame -r 29.97 -vf 'scale=160:120' -y '#{@options[:output_file]}'"
92
+ end
93
+
94
+ it 'supports :width and :height options to build :resolution_and_padding with negatif ratio' do
95
+ @options.merge! :width => "120", :height => "160"
96
+ ffmpeg = Ffmpeg.new("ffmpeg -i $input_file$ -ar 44100 -ab 64 -vcodec xvid -acodec libmp3lame -r 29.97 $resolution_and_padding$ -y $output_file$", @options)
97
+ ffmpeg.command.should == "ffmpeg -i '#{@options[:input_file]}' -ar 44100 -ab 64 -vcodec xvid -acodec libmp3lame -r 29.97 -vf 'scale=120:90,pad=120:160:0:35' -y '#{@options[:output_file]}'"
98
+ end
99
+
100
+ it 'supports :width and :height options to build :resolution_keep_aspect_ratio' do
101
+ @options.merge! :width => "120", :height => "160"
102
+ ffmpeg = Ffmpeg.new("ffmpeg -i $input_file$ $resolution_keep_aspect_ratio$ -y $output_file$", @options)
103
+ ffmpeg.command.should == "ffmpeg -i '#{@options[:input_file]}' -vf 'scale=120:90' -y '#{@options[:output_file]}'"
104
+ end
105
+
106
+ it 'supports :width and :height options to build :resolution_and_padding with negatif ratio' do
107
+ @options.merge! :width => "160", :height => "100"
108
+ ffmpeg = Ffmpeg.new("ffmpeg -i $input_file$ -ar 44100 -ab 64 -vcodec xvid -acodec libmp3lame -r 29.97 $resolution_and_padding$ -y $output_file$", @options)
109
+ ffmpeg.command.should == "ffmpeg -i '#{@options[:input_file]}' -ar 44100 -ab 64 -vcodec xvid -acodec libmp3lame -r 29.97 -vf 'scale=134:100' -y '#{@options[:output_file]}'"
110
+ end
111
+
112
+ it 'supports odd value on width or height' do
113
+ @mock_original_file = mock(:original, :width => 640, :height => 480, :rotated? => false)
114
+ RVideo::Inspector.stub!(:new).and_return(@mock_original_file)
115
+
116
+ @options.merge! :width => "620", :height => "349"
117
+ ffmpeg = Ffmpeg.new("ffmpeg -i $input_file$ -ar 44100 -ab 64 -vcodec xvid -acodec libmp3lame -r 29.97 $resolution_and_padding$ -y $output_file$", @options)
118
+ ffmpeg.command.should == "ffmpeg -i '#{@options[:input_file]}' -ar 44100 -ab 64 -vcodec xvid -acodec libmp3lame -r 29.97 -vf 'scale=464:348' -y '#{@options[:output_file]}'"
119
+ end
120
+
121
+ it 'supports nil width or height' do
122
+ @mock_original_file = mock(:original, :width => 0, :height => 0, :rotated? => false)
123
+ RVideo::Inspector.stub!(:new).and_return(@mock_original_file)
124
+
125
+ @options.merge! :width => "620", :height => "349"
126
+ ffmpeg = Ffmpeg.new("ffmpeg -i $input_file$ -ar 44100 -ab 64 -vcodec xvid -acodec libmp3lame -r 29.97 $resolution_and_padding$ -y $output_file$", @options)
127
+ ffmpeg.command.should == "ffmpeg -i '#{@options[:input_file]}' -ar 44100 -ab 64 -vcodec xvid -acodec libmp3lame -r 29.97 -vf 'scale=620:348' -y '#{@options[:output_file]}'"
128
+ end
129
+
130
+ it 'supports odd value in the padding' do
131
+ @mock_original_file = mock(:original, :width => 320, :height => 240, :rotated? => false)
132
+ RVideo::Inspector.stub!(:new).and_return(@mock_original_file)
133
+
134
+ @options.merge! :width => "225", :height => "222"
135
+ ffmpeg = Ffmpeg.new("ffmpeg -i $input_file$ -ar 44100 -ab 64 -vcodec xvid -acodec libmp3lame -r 29.97 $resolution_and_padding$ -y $output_file$", @options)
136
+ ffmpeg.command.should == "ffmpeg -i '#{@options[:input_file]}' -ar 44100 -ab 64 -vcodec xvid -acodec libmp3lame -r 29.97 -vf 'scale=224:168,pad=224:222:0:27' -y '#{@options[:output_file]}'"
137
+ end
138
+
139
+ it 'supports odd value in the padding' do
140
+ @mock_original_file = mock(:original, :width => 1920, :height => 1080, :rotated? => false)
141
+ RVideo::Inspector.stub!(:new).and_return(@mock_original_file)
142
+
143
+ @options.merge! :width => "854", :height => "480"
144
+ ffmpeg = Ffmpeg.new("ffmpeg -i $input_file$ -ar 44100 -ab 64 -vcodec xvid -acodec libmp3lame -r 29.97 $resolution_and_padding$ -y $output_file$", @options)
145
+ ffmpeg.command.should == "ffmpeg -i '#{@options[:input_file]}' -ar 44100 -ab 64 -vcodec xvid -acodec libmp3lame -r 29.97 -vf 'scale=854:480' -y '#{@options[:output_file]}'"
146
+ end
147
+
148
+ it 'supports :video_bit_rate' do
149
+ @options.merge! :video_bit_rate => 666
150
+ ffmpeg = Ffmpeg.new("ffmpeg -i $input_file$ $video_bit_rate$ -y $output_file$", @options)
151
+ ffmpeg.command.should == "ffmpeg -i '#{@options[:input_file]}' -b 666k -y '#{@options[:output_file]}'"
152
+ end
153
+
154
+ it "supports :video_bit_rate and configurable command flag" do
155
+ Ffmpeg.video_bit_rate_parameter = "v"
156
+ @options.merge! :video_bit_rate => 666
157
+ ffmpeg = Ffmpeg.new("ffmpeg -i $input_file$ $video_bit_rate$ -y $output_file$", @options)
158
+ ffmpeg.command.should == "ffmpeg -i '#{@options[:input_file]}' -v 666k -y '#{@options[:output_file]}'"
159
+ end
160
+
161
+ ###
162
+
163
+ it "supports :video_bit_rate_tolerance" do
164
+ @options.merge! :video_bit_rate_tolerance => 666
165
+ ffmpeg = Ffmpeg.new("ffmpeg -i $input_file$ $video_bit_rate_tolerance$ -y $output_file$", @options)
166
+ ffmpeg.command.should == "ffmpeg -i '#{@options[:input_file]}' -bt 666k -y '#{@options[:output_file]}'"
167
+ end
168
+
169
+ ###
170
+
171
+ it "supports :video_bit_rate_max and :video_bit_rate_min" do
172
+ @options.merge! :video_bit_rate => 666, :video_bit_rate_min => 666, :video_bit_rate_max => 666
173
+ ffmpeg = Ffmpeg.new("ffmpeg -i $input_file$ $video_bit_rate$ $video_bit_rate_min$ $video_bit_rate_max$ -y $output_file$", @options)
174
+ ffmpeg.command.should == "ffmpeg -i '#{@options[:input_file]}' -b 666k -minrate 666k -maxrate 666k -y '#{@options[:output_file]}'"
175
+ end
176
+
177
+ it "supports :deinterlace => true" do
178
+ @options.merge! :deinterlace => true
179
+ ffmpeg = Ffmpeg.new("ffmpeg -i $input_file$ $deinterlace$ -y $output_file$", @options)
180
+ ffmpeg.command.should == "ffmpeg -i '#{@options[:input_file]}' -deinterlace -y '#{@options[:output_file]}'"
181
+ end
182
+
183
+ it "handles :deinterlace => false correct" do
184
+ @options.merge! :deinterlace => false
185
+ ffmpeg = Ffmpeg.new("ffmpeg -i $input_file$ $deinterlace$ -y $output_file$", @options)
186
+ ffmpeg.command.should == "ffmpeg -i '#{@options[:input_file]}' -y '#{@options[:output_file]}'"
187
+ end
188
+
189
+ ###
190
+
191
+ # TODO for these video quality specs we might want to show that the expected
192
+ # bitrate is calculated based on dimensions and framerate so you can better
193
+ # understand it without going to the source.
194
+
195
+ it "supports :video_quality => 'low'" do
196
+ @options.merge! :video_quality => "low"
197
+ ffmpeg = Ffmpeg.new("ffmpeg -i $input_file$ $video_quality$ -y $output_file$", @options)
198
+ ffmpeg.command.should == "ffmpeg -i '#{@options[:input_file]}' -b 96k -crf 30 -me zero -subq 1 -refs 1 -threads auto -y '#{@options[:output_file]}'"
199
+ end
200
+
201
+ it "supports :video_quality => 'medium'" do
202
+ @options.merge! :video_quality => "medium"
203
+ ffmpeg = Ffmpeg.new("ffmpeg -i $input_file$ $video_quality$ -y $output_file$", @options)
204
+ ffmpeg.command.should == "ffmpeg -i '#{@options[:input_file]}' -b 128k -crf 22 -flags +loop -cmp +sad -partitions +parti4x4+partp8x8+partb8x8 -flags2 +mixed_refs -me hex -subq 3 -trellis 1 -refs 2 -bf 3 -b_strategy 1 -coder 1 -me_range 16 -g 250 -y '#{@options[:output_file]}'"
205
+ end
206
+
207
+ it "supports :video_quality => 'high'" do
208
+ @options.merge! :video_quality => "high"
209
+ ffmpeg = Ffmpeg.new("ffmpeg -i $input_file$ $video_quality$ -y $output_file$", @options)
210
+ ffmpeg.command.should == "ffmpeg -i '#{@options[:input_file]}' -b 322k -crf 18 -flags +loop -cmp +sad -partitions +parti4x4+partp8x8+partb8x8 -flags2 +mixed_refs -me full -subq 6 -trellis 1 -refs 3 -bf 3 -b_strategy 1 -coder 1 -me_range 16 -g 250 -keyint_min 25 -sc_threshold 40 -i_qfactor 0.71 -y '#{@options[:output_file]}'"
211
+ end
212
+
213
+ ###
214
+
215
+ it "supports :video_quality => 'low' with arbitrary :video_bit_rate" do
216
+ @options.merge! :video_quality => "low", :video_bit_rate => 666
217
+ ffmpeg = Ffmpeg.new("ffmpeg -i $input_file$ $video_quality$ -y $output_file$", @options)
218
+ ffmpeg.command.should == "ffmpeg -i '#{@options[:input_file]}' -b 666k -crf 30 -me zero -subq 1 -refs 1 -threads auto -y '#{@options[:output_file]}'"
219
+ end
220
+
221
+ it "supports :video_quality => 'medium' with arbitrary :video_bit_rate" do
222
+ @options.merge! :video_quality => "medium", :video_bit_rate => 666
223
+ ffmpeg = Ffmpeg.new("ffmpeg -i $input_file$ $video_quality$ -y $output_file$", @options)
224
+ ffmpeg.command.should == "ffmpeg -i '#{@options[:input_file]}' -b 666k -crf 22 -flags +loop -cmp +sad -partitions +parti4x4+partp8x8+partb8x8 -flags2 +mixed_refs -me hex -subq 3 -trellis 1 -refs 2 -bf 3 -b_strategy 1 -coder 1 -me_range 16 -g 250 -y '#{@options[:output_file]}'"
225
+ end
226
+
227
+ it "supports :video_quality => 'high' with arbitrary :video_bit_rate" do
228
+ @options.merge! :video_quality => "high", :video_bit_rate => 666
229
+ ffmpeg = Ffmpeg.new("ffmpeg -i $input_file$ $video_quality$ -y $output_file$", @options)
230
+ ffmpeg.command.should == "ffmpeg -i '#{@options[:input_file]}' -b 666k -crf 18 -flags +loop -cmp +sad -partitions +parti4x4+partp8x8+partb8x8 -flags2 +mixed_refs -me full -subq 6 -trellis 1 -refs 3 -bf 3 -b_strategy 1 -coder 1 -me_range 16 -g 250 -keyint_min 25 -sc_threshold 40 -i_qfactor 0.71 -y '#{@options[:output_file]}'"
231
+ end
232
+
233
+ # These appear unsupported..
234
+ #
235
+ # it 'should support passthrough height' do
236
+ # options = {:input_file => spec_file("kites.mp4"), :output_file => "bar", :width => "640"}
237
+ # command = "ffmpeg -i $input_file$ -ar 44100 -ab 64 -vcodec xvid -acodec libmp3lame -r 29.97 $resolution$ -y $output_file$"
238
+ # ffmpeg = Ffmpeg.new(command, options)
239
+ # ffmpeg.command.should == "ffmpeg -i '#{options[:input_file]}' -ar 44100 -ab 64 -vcodec xvid -acodec libmp3lame -r 29.97 -s 640x720 -y 'bar'"
240
+ # end
241
+ #
242
+ # it 'should support passthrough width' do
243
+ # options = {:input_file => spec_file("kites.mp4"), :output_file => "bar", :height => "360"}
244
+ # command = "ffmpeg -i $input_file$ -ar 44100 -ab 64 -vcodec xvid -acodec libmp3lame -r 29.97 $resolution$ -y $output_file$"
245
+ # ffmpeg = Ffmpeg.new(command, options)
246
+ # ffmpeg.command.should == "ffmpeg -i '#{options[:input_file]}' -ar 44100 -ab 64 -vcodec xvid -acodec libmp3lame -r 29.97 -s 1280x360 -y 'bar'"
247
+ # end
248
+ end
249
+
250
+ describe Ffmpeg, " when parsing a result" do
251
+ before do
252
+ setup_ffmpeg_spec
253
+
254
+ @result = ffmpeg_result(:result1)
255
+ @result2 = ffmpeg_result(:result2)
256
+ @result3 = ffmpeg_result(:result3)
257
+ @result4 = ffmpeg_result(:result4)
258
+ end
259
+
260
+ it "should create correct result metadata" do
261
+ @ffmpeg.send(:parse_result, @result).should be_true
262
+ @ffmpeg.frame.should == '4126'
263
+ @ffmpeg.output_fps.should be_nil
264
+ @ffmpeg.q.should == '31.0'
265
+ @ffmpeg.size.should == '5917kB'
266
+ @ffmpeg.time.should == '69.1'
267
+ @ffmpeg.output_bitrate.should == '702.0kbits/s'
268
+ @ffmpeg.video_size.should == "2417kB"
269
+ @ffmpeg.audio_size.should == "540kB"
270
+ @ffmpeg.header_size.should == "0kB"
271
+ @ffmpeg.overhead.should == "100.140277%"
272
+ @ffmpeg.psnr.should be_nil
273
+ end
274
+
275
+ it "should create correct result metadata (2)" do
276
+ @ffmpeg.send(:parse_result, @result2).should be_true
277
+ @ffmpeg.frame.should == '584'
278
+ @ffmpeg.output_fps.should be_nil
279
+ @ffmpeg.q.should == '6.0'
280
+ @ffmpeg.size.should == '708kB'
281
+ @ffmpeg.time.should == '19.5'
282
+ @ffmpeg.output_bitrate.should == '297.8kbits/s'
283
+ @ffmpeg.video_size.should == "49kB"
284
+ @ffmpeg.audio_size.should == "153kB"
285
+ @ffmpeg.header_size.should == "0kB"
286
+ @ffmpeg.overhead.should == "250.444444%"
287
+ @ffmpeg.psnr.should be_nil
288
+ end
289
+
290
+ it "should create correct result metadata (3)" do
291
+ @ffmpeg.send(:parse_result, @result3).should be_true
292
+ @ffmpeg.frame.should == '273'
293
+ @ffmpeg.output_fps.should == "31"
294
+ @ffmpeg.q.should == '10.0'
295
+ @ffmpeg.size.should == '398kB'
296
+ @ffmpeg.time.should == '5.9'
297
+ @ffmpeg.output_bitrate.should == '551.8kbits/s'
298
+ @ffmpeg.video_size.should == "284kB"
299
+ @ffmpeg.audio_size.should == "92kB"
300
+ @ffmpeg.header_size.should == "0kB"
301
+ @ffmpeg.overhead.should == "5.723981%"
302
+ @ffmpeg.psnr.should be_nil
303
+ end
304
+
305
+ it "should create correct result metadata (4)" do
306
+ @ffmpeg.send(:parse_result, @result4).should be_true
307
+ @ffmpeg.frame.should be_nil
308
+ @ffmpeg.output_fps.should be_nil
309
+ @ffmpeg.q.should be_nil
310
+ @ffmpeg.size.should == '1080kB'
311
+ @ffmpeg.time.should == '69.1'
312
+ @ffmpeg.output_bitrate.should == '128.0kbits'
313
+ @ffmpeg.video_size.should == "0kB"
314
+ @ffmpeg.audio_size.should == "1080kB"
315
+ @ffmpeg.header_size.should == "0kB"
316
+ @ffmpeg.overhead.should == "0.002893%"
317
+ @ffmpeg.psnr.should be_nil
318
+ end
319
+
320
+ it "ffmpeg should calculate PSNR if it is turned on" do
321
+ @ffmpeg.send(:parse_result, @result.gsub("Lsize=","LPSNR=Y:33.85 U:37.61 V:37.46 *:34.77 size=")).should be_true
322
+ @ffmpeg.psnr.should == "Y:33.85 U:37.61 V:37.46 *:34.77"
323
+ end
324
+ end
325
+
326
+ describe Ffmpeg, "result parsing should raise an exception" do
327
+ before(:each) do
328
+ setup_ffmpeg_spec
329
+ @results = load_fixture :ffmpeg_results
330
+ end
331
+
332
+ it "when a param is missing a value" do
333
+ parsing_result(:param_missing_value).
334
+ should raise_error(TranscoderError::InvalidCommand, /Expected .+ for .+ but found: .+/)
335
+ end
336
+
337
+ it "when codec not supported" do
338
+ parsing_result(:amr_nb_not_supported).
339
+ should raise_error(TranscoderError::InvalidFile, "Codec amr_nb not supported by this build of ffmpeg")
340
+ end
341
+
342
+ it "when not passed a command" do
343
+ parsing_result(:missing_command).
344
+ should raise_error(TranscoderError::InvalidCommand, "must pass a command to ffmpeg")
345
+ end
346
+
347
+ it "when given a broken command" do
348
+ parsing_result(:broken_command).
349
+ should raise_error(TranscoderError::InvalidCommand, "Unable for find a suitable output format for 'foo'")
350
+ end
351
+
352
+ it "when the output file has no streams" do
353
+ parsing_result(:output_has_no_streams).
354
+ should raise_error(TranscoderError, /Output file does not contain.*stream/)
355
+ end
356
+
357
+ it "when given a missing input file" do
358
+ parsing_result(:missing_input_file).
359
+ should raise_error(TranscoderError::InvalidFile, /I\/O error: .+/)
360
+ end
361
+
362
+ it "when given a file it can't handle"
363
+
364
+ it "when cancelled halfway through"
365
+
366
+ it "when receiving unexpected results" do
367
+ parsing_result(:unexpected_results).
368
+ should raise_error(TranscoderError::UnexpectedResult, 'foo - bar')
369
+ end
370
+
371
+ it "with an unsupported codec" do
372
+ @ffmpeg.original = Inspector.new(:raw_response => files('kites2'))
373
+
374
+ parsing_result(:unsupported_codec).
375
+ should raise_error(TranscoderError::InvalidFile, /samr/)
376
+ end
377
+
378
+ it "when a stream cannot be written" do
379
+ parsing_result(:unwritable_stream).
380
+ should raise_error(TranscoderError, /flv doesnt support.*incorrect codec/)
381
+ end
382
+
383
+ end
384
+ end
385
+ end