class FfmpegWrapper::FFmpeg
Public Class Methods
Singleton method defined on FFmpeg
after calling filter_blackdetect
. Writes info about black intervals as a plain text to a file. @param (String) filename
# File lib/ffmpeg_wrapper/filters/blackdetect.rb, line 27 def self.and_write_to(filename) @post_exec_hooks << proc do File.open(filename, 'w') do |f| @result[:blacks].each do |black| f.puts 'black_interval start: %f end: %f duration: %f' % [black[:start], black[:end], black[:duration]] end end end end
# File lib/ffmpeg_wrapper/ffmpeg.rb, line 3 def initialize @command = 'ffmpeg -y -hide_banner' @inputs = [] @audios = [] @videos = [] @mappings = [] @filters = [] @n = 0 @result = {} @post_exec_hooks = [] end
Construct ffmpeg command using that function, then execute. All opts hashe’s option meant to be axactly the same as according ffmpeg flags. That is -pix_fmt => :pix_fmt
@return [FFmpeg] @example
FFmpeg.run do media 'somefile.mp4' map(0,1).applying acodec: 'libmp3lame', ac: 2, ar: '44.1k' # .applying is a dynamic method of a # String returned from #map output 'out.mp3' end
# File lib/ffmpeg_wrapper/ffmpeg.rb, line 28 def self.run(*flags, &block) lo = Logger.new(STDOUT) if flags.include?(:debug) ff = FFmpeg.new ff.instance_eval do instance_eval(&block) @command << ' ' << @inputs.join(' ') @command << ' ' << @filters.join(' ') if @filters.any? @command << ' ' << @mappings.join(' ') if @mappings.any? @command << ' ' << @output if @output lo.info @command if lo @out = IO.popen(@command, err: [:child, :out]) do |io| io.read end @post_exec_hooks.each { |h| instance_eval(&h) } fail @out.to_s unless $?.success? end ff.instance_variable_get(:@result) end
Public Instance Methods
Adds input audio file (no video will be extracted anyway) @param filename [String] @param opts [Hash] options for this input file @return id [Integer] input file id
# File lib/ffmpeg_wrapper/ffmpeg.rb, line 84 def audio(filename, opts = {}) n = input(filename, opts) @audios << n n end
Find intervals of blackness in a video file. This info then can be found in a hash, returned from FFmpeg.run
by key :blacks. TODO: add argument support @param opts [Hash] @return FFmpeg
self. Chainable, see example. @example
FFmpeg.run do media 'video.mp4' detect_black.and_write_to 'black_intervals.txt' end # => { ... :blacks => [ { :start => x, :end => y, :duration => z}, ... ] }
# File lib/ffmpeg_wrapper/filters/blackdetect.rb, line 16 def filter_blackdetect(opts = {}) @filters << '-vf blackdetect' @output = ' -f null /dev/null ' @redirection = [:child, :out] @result[:blacks] = [] # Singleton method defined on FFmpeg # after calling #filter_blackdetect. # Writes info about black intervals as a plain text to # a file. # @param (String) filename def self.and_write_to(filename) @post_exec_hooks << proc do File.open(filename, 'w') do |f| @result[:blacks].each do |black| f.puts 'black_interval start: %f end: %f duration: %f' % [black[:start], black[:end], black[:duration]] end end end end @post_exec_hooks << proc do bdlines = @out.lines.grep(/blackdetect/) bdlines.map do |l| /black_start:(?<st>\S+).* black_end:(?<en>\S+).* black_duration:(?<du>\S+)/x =~ l @result[:blacks] << { start: st.to_f, end: en.to_f, duration: du.to_f } end end self end
This function extends basic {FFmpeg} functionality providing filter command. TODO: non-trivial mappings(i.e. multi-streamed inputs)
# File lib/ffmpeg_wrapper/filters/concat.rb, line 6 def filter_concat(_mappings = {}) line = '' line << filter_complex(:audio) line << filter_complex(:video) @filters << line mappings = [] mappings << (@videos.size > 1 ? '[v]' : @videos.first) mappings << (@audios.size > 1 ? '[a]' : @audios.first) mappings end
Specify mapping: what input stream or alias (from format, e. g. [a]) @param file [String, Int] file number or alias @param index [Int] stream specifier @see FFmpeg#map
map
method example for call sequence @see trac.ffmpeg.org/wiki/How%20to%20use%20-map%20option ffmpeg
map option guide
@return [String]
# File lib/ffmpeg_wrapper/ffmpeg.rb, line 103 def map(file, index = nil) line = "-map #{file}#{':' + index.to_s if index}" @mappings << line def line.applying(opts = {}) opts.each do |k, v| self << " -#{k} #{v}" end end line end
Adds input file, containing audio and video. @param filename [String] @param opts [Hash] options for this input file. See list of options below. If input file is a media container,
-
mpeg4 or avi you don’t need to explicitly specify any.
-
If file is raw video or audio, specify V for video and A for audio @option opts [String] :f V format ‘rawvideo’ @option opts [String] :s V geometry WxH(+X,Y) @option opts [String] :pix_fmt V pixel format bgr8, rgba, etc… @option opts [Int] :r V fps @option opts [String] :f A format ‘alaw’, etc… @option opts [String] :ar A sample rate, e.g. ‘44.1k’ @option opts [Int] :ac A channels @see FFmpeg.map
.run method example for call sequence @return id [Integer] input file id
# File lib/ffmpeg_wrapper/ffmpeg.rb, line 63 def media(filename, opts = {}) n = input(filename, opts) @videos << n @audios << n n end
Specify filename for output file @param filename [String]
# File lib/ffmpeg_wrapper/ffmpeg.rb, line 92 def output(filename) @output = " #{filename}" end
Adds input video file (no audio will be extracted anyway) @param filename [String] @param opts [Hash] options for this input file @return id [Integer] input file id
# File lib/ffmpeg_wrapper/ffmpeg.rb, line 74 def video(filename, opts = {}) n = input(filename, opts) @videos << n n end
Private Instance Methods
# File lib/ffmpeg_wrapper/filters/concat.rb, line 41 def concat_for_audio "concat=n=#{@audios.size}:v=0:a=1 [a]" end
# File lib/ffmpeg_wrapper/filters/concat.rb, line 37 def concat_for_video "concat=n=#{@videos.size}:v=1:a=0 [v]" end
# File lib/ffmpeg_wrapper/filters/concat.rb, line 45 def filter_complex(str) fail ArgumentError unless [:audio, :video].include?(str) str = str.to_s return '' if instance_variable_get('@' + str + 's').count < 2 line = ' -filter_complex ' line << "'" line << send("stream_map_for_#{str}") line << send("concat_for_#{str}") line << "'" end
# File lib/ffmpeg_wrapper/ffmpeg.rb, line 116 def input(filename, opts = {}) line = '' opts.each do |k, v| line << " -#{k} #{v}" end line << " -i #{filename}" @inputs << line @n += 1 @n - 1 end
# File lib/ffmpeg_wrapper/filters/concat.rb, line 28 def stream_map_for_audio return if @audios.empty? line = '' @audios.each do |file| line << " [#{file}:0] " end line end
# File lib/ffmpeg_wrapper/filters/concat.rb, line 19 def stream_map_for_video return if @videos.empty? line = '' @videos.each do |file| line << " [#{file}:0] " end line end