d-streamio-ffmpeg 3.0.3

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.
@@ -0,0 +1,141 @@
1
+ require 'open3'
2
+
3
+ module FFMPEG
4
+ class Transcoder
5
+ attr_reader :command, :input
6
+
7
+ @@timeout = 30
8
+
9
+ class << self
10
+ attr_accessor :timeout
11
+ end
12
+
13
+ def initialize(input, output_file, options = EncodingOptions.new, transcoder_options = {})
14
+ if input.is_a?(FFMPEG::Movie)
15
+ @movie = input
16
+ @input = input.path
17
+ end
18
+ @output_file = output_file
19
+
20
+ if options.is_a?(Array) || options.is_a?(EncodingOptions)
21
+ @raw_options = options
22
+ elsif options.is_a?(Hash)
23
+ @raw_options = EncodingOptions.new(options)
24
+ else
25
+ raise ArgumentError, "Unknown options format '#{options.class}', should be either EncodingOptions, Hash or Array."
26
+ end
27
+
28
+ @transcoder_options = transcoder_options
29
+ @errors = []
30
+
31
+ apply_transcoder_options
32
+
33
+ @input = @transcoder_options[:input] unless @transcoder_options[:input].nil?
34
+
35
+ input_options = @transcoder_options[:input_options] || []
36
+ iopts = []
37
+ input_options.each { |k, v| iopts += ['-' + k.to_s, v] }
38
+
39
+ @command = [FFMPEG.ffmpeg_binary, '-y', *iopts, '-i', @input, *@raw_options.to_a, @output_file]
40
+ end
41
+
42
+ def run(&block)
43
+ transcode_movie(&block)
44
+ if @transcoder_options[:validate]
45
+ validate_output_file(&block)
46
+ return encoded
47
+ else
48
+ return nil
49
+ end
50
+ end
51
+
52
+ def encoding_succeeded?
53
+ @errors << "no output file created" and return false unless File.exist?(@output_file)
54
+ @errors << "encoded file is invalid" and return false unless encoded.valid?
55
+ true
56
+ end
57
+
58
+ def encoded
59
+ @encoded ||= Movie.new(@output_file)
60
+ end
61
+
62
+ def timeout
63
+ self.class.timeout
64
+ end
65
+
66
+ private
67
+ # frame= 4855 fps= 46 q=31.0 size= 45306kB time=00:02:42.28 bitrate=2287.0kbits/
68
+ def transcode_movie
69
+ FFMPEG.logger.info("Running transcoding...\n#{command}\n")
70
+ @output = ""
71
+
72
+ Open3.popen3(*command) do |_stdin, _stdout, stderr, wait_thr|
73
+ begin
74
+ yield(0.0) if block_given?
75
+ next_line = Proc.new do |line|
76
+ fix_encoding(line)
77
+ @output << line
78
+ if line.include?("time=")
79
+ if line =~ /time=(\d+):(\d+):(\d+.\d+)/ # ffmpeg 0.8 and above style
80
+ time = ($1.to_i * 3600) + ($2.to_i * 60) + $3.to_f
81
+ else # better make sure it wont blow up in case of unexpected output
82
+ time = 0.0
83
+ end
84
+
85
+ if @movie
86
+ progress = time / @movie.duration
87
+ yield(progress) if block_given?
88
+ end
89
+ end
90
+ end
91
+
92
+ if timeout
93
+ stderr.each_with_timeout(wait_thr.pid, timeout, 'size=', &next_line)
94
+ else
95
+ stderr.each('size=', &next_line)
96
+ end
97
+
98
+ rescue Timeout::Error => e
99
+ FFMPEG.logger.error "Process hung...\n@command\n#{command}\nOutput\n#{@output}\n"
100
+ raise Error, "Process hung. Full output: #{@output}"
101
+ end
102
+ end
103
+ end
104
+
105
+ def validate_output_file(&block)
106
+ if encoding_succeeded?
107
+ yield(1.0) if block_given?
108
+ FFMPEG.logger.info "Transcoding of #{input} to #{@output_file} succeeded\n"
109
+ else
110
+ errors = "Errors: #{@errors.join(", ")}. "
111
+ FFMPEG.logger.error "Failed encoding...\n#{command}\n\n#{@output}\n#{errors}\n"
112
+ raise Error, "Failed encoding.#{errors}Full output: #{@output}"
113
+ end
114
+ end
115
+
116
+ def apply_transcoder_options
117
+ # if true runs #validate_output_file
118
+ @transcoder_options[:validate] = @transcoder_options.fetch(:validate) { true }
119
+
120
+ return if @movie.nil? || @movie.calculated_aspect_ratio.nil?
121
+ case @transcoder_options[:preserve_aspect_ratio].to_s
122
+ when "width"
123
+ new_height = @raw_options.width / @movie.calculated_aspect_ratio
124
+ new_height = new_height.ceil.even? ? new_height.ceil : new_height.floor
125
+ new_height += 1 if new_height.odd? # needed if new_height ended up with no decimals in the first place
126
+ @raw_options[:resolution] = "#{@raw_options.width}x#{new_height}"
127
+ when "height"
128
+ new_width = @raw_options.height * @movie.calculated_aspect_ratio
129
+ new_width = new_width.ceil.even? ? new_width.ceil : new_width.floor
130
+ new_width += 1 if new_width.odd?
131
+ @raw_options[:resolution] = "#{new_width}x#{@raw_options.height}"
132
+ end
133
+ end
134
+
135
+ def fix_encoding(output)
136
+ output[/test/]
137
+ rescue ArgumentError
138
+ output.force_encoding("ISO-8859-1")
139
+ end
140
+ end
141
+ end
@@ -0,0 +1,3 @@
1
+ module FFMPEG
2
+ VERSION = '3.0.3'
3
+ end
@@ -0,0 +1,108 @@
1
+ $LOAD_PATH.unshift File.dirname(__FILE__)
2
+
3
+ require 'logger'
4
+ require 'stringio'
5
+
6
+ require 'ffmpeg/version'
7
+ require 'ffmpeg/errors'
8
+ require 'ffmpeg/movie'
9
+ require 'ffmpeg/io_monkey'
10
+ require 'ffmpeg/transcoder'
11
+ require 'ffmpeg/encoding_options'
12
+
13
+ module FFMPEG
14
+ # FFMPEG logs information about its progress when it's transcoding.
15
+ # Jack in your own logger through this method if you wish to.
16
+ #
17
+ # @param [Logger] log your own logger
18
+ # @return [Logger] the logger you set
19
+ def self.logger=(log)
20
+ @logger = log
21
+ end
22
+
23
+ # Get FFMPEG logger.
24
+ #
25
+ # @return [Logger]
26
+ def self.logger
27
+ return @logger if @logger
28
+ logger = Logger.new(STDOUT)
29
+ logger.level = Logger::INFO
30
+ @logger = logger
31
+ end
32
+
33
+ # Set the path of the ffmpeg binary.
34
+ # Can be useful if you need to specify a path such as /usr/local/bin/ffmpeg
35
+ #
36
+ # @param [String] path to the ffmpeg binary
37
+ # @return [String] the path you set
38
+ # @raise Errno::ENOENT if the ffmpeg binary cannot be found
39
+ def self.ffmpeg_binary=(bin)
40
+ if bin.is_a?(String) && !File.executable?(bin)
41
+ raise Errno::ENOENT, "the ffmpeg binary, \'#{bin}\', is not executable"
42
+ end
43
+ @ffmpeg_binary = bin
44
+ end
45
+
46
+ # Get the path to the ffmpeg binary, defaulting to 'ffmpeg'
47
+ #
48
+ # @return [String] the path to the ffmpeg binary
49
+ # @raise Errno::ENOENT if the ffmpeg binary cannot be found
50
+ def self.ffmpeg_binary
51
+ @ffmpeg_binary || which('ffmpeg')
52
+ end
53
+
54
+ # Get the path to the ffprobe binary, defaulting to what is on ENV['PATH']
55
+ #
56
+ # @return [String] the path to the ffprobe binary
57
+ # @raise Errno::ENOENT if the ffprobe binary cannot be found
58
+ def self.ffprobe_binary
59
+ @ffprobe_binary || which('ffprobe')
60
+ end
61
+
62
+ # Set the path of the ffprobe binary.
63
+ # Can be useful if you need to specify a path such as /usr/local/bin/ffprobe
64
+ #
65
+ # @param [String] path to the ffprobe binary
66
+ # @return [String] the path you set
67
+ # @raise Errno::ENOENT if the ffprobe binary cannot be found
68
+ def self.ffprobe_binary=(bin)
69
+ if bin.is_a?(String) && !File.executable?(bin)
70
+ raise Errno::ENOENT, "the ffprobe binary, \'#{bin}\', is not executable"
71
+ end
72
+ @ffprobe_binary = bin
73
+ end
74
+
75
+ # Get the maximum number of http redirect attempts
76
+ #
77
+ # @return [Integer] the maximum number of retries
78
+ def self.max_http_redirect_attempts
79
+ @max_http_redirect_attempts.nil? ? 10 : @max_http_redirect_attempts
80
+ end
81
+
82
+ # Set the maximum number of http redirect attempts.
83
+ #
84
+ # @param [Integer] the maximum number of retries
85
+ # @return [Integer] the number of retries you set
86
+ # @raise Errno::ENOENT if the value is negative or not an Integer
87
+ def self.max_http_redirect_attempts=(v)
88
+ raise Errno::ENOENT, 'max_http_redirect_attempts must be an integer' if v && !v.is_a?(Integer)
89
+ raise Errno::ENOENT, 'max_http_redirect_attempts may not be negative' if v && v < 0
90
+ @max_http_redirect_attempts = v
91
+ end
92
+
93
+ # Cross-platform way of finding an executable in the $PATH.
94
+ #
95
+ # which('ruby') #=> /usr/bin/ruby
96
+ # see: http://stackoverflow.com/questions/2108727/which-in-ruby-checking-if-program-exists-in-path-from-ruby
97
+ def self.which(cmd)
98
+ exts = ENV['PATHEXT'] ? ENV['PATHEXT'].split(';') : ['']
99
+ ENV['PATH'].split(File::PATH_SEPARATOR).each do |path|
100
+ exts.each { |ext|
101
+ exe = File.join(path, "#{cmd}#{ext}")
102
+ return exe if File.executable? exe
103
+ }
104
+ end
105
+ raise Errno::ENOENT, "the #{cmd} binary could not be found in #{ENV['PATH']}"
106
+ end
107
+
108
+ end
metadata ADDED
@@ -0,0 +1,95 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: d-streamio-ffmpeg
3
+ version: !ruby/object:Gem::Version
4
+ version: 3.0.3
5
+ platform: ruby
6
+ authors:
7
+ - Rackfish AB
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2019-06-18 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: multi_json
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.8'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.8'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rspec
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '3'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '3'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '10.1'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '10.1'
55
+ description:
56
+ email:
57
+ - support@rackfish.com
58
+ - bikeath1337.com
59
+ executables: []
60
+ extensions: []
61
+ extra_rdoc_files: []
62
+ files:
63
+ - CHANGELOG
64
+ - LICENSE
65
+ - README.md
66
+ - lib/ffmpeg/encoding_options.rb
67
+ - lib/ffmpeg/errors.rb
68
+ - lib/ffmpeg/io_monkey.rb
69
+ - lib/ffmpeg/movie.rb
70
+ - lib/ffmpeg/transcoder.rb
71
+ - lib/ffmpeg/version.rb
72
+ - lib/streamio-ffmpeg.rb
73
+ homepage: https://github.com/luongvietdung/streamio-ffmpeg
74
+ licenses: []
75
+ metadata: {}
76
+ post_install_message:
77
+ rdoc_options: []
78
+ require_paths:
79
+ - lib
80
+ required_ruby_version: !ruby/object:Gem::Requirement
81
+ requirements:
82
+ - - ">="
83
+ - !ruby/object:Gem::Version
84
+ version: '0'
85
+ required_rubygems_version: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ requirements: []
91
+ rubygems_version: 3.0.1
92
+ signing_key:
93
+ specification_version: 4
94
+ summary: Wraps ffmpeg to read metadata and transcodes videos.
95
+ test_files: []