class FfmpegWrapper::FFmpeg

Public Class Methods

and_write_to(filename) click to toggle source

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
new() click to toggle source
# 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
run(*flags, &block) click to toggle source

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

audio(filename, opts = {}) click to toggle source

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
detect_black(opts = {})
Alias for: filter_blackdetect
filter_blackdetect(opts = {}) click to toggle source

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
Also aliased as: detect_black
filter_concat(_mappings = {}) click to toggle source

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
map(file, index = nil) click to toggle source

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
media(filename, opts = {}) click to toggle source

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,

    1. 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
output(filename) click to toggle source

Specify filename for output file @param filename [String]

# File lib/ffmpeg_wrapper/ffmpeg.rb, line 92
def output(filename)
  @output = " #{filename}"
end
video(filename, opts = {}) click to toggle source

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

concat_for_audio() click to toggle source
# File lib/ffmpeg_wrapper/filters/concat.rb, line 41
def concat_for_audio
  "concat=n=#{@audios.size}:v=0:a=1 [a]"
end
concat_for_video() click to toggle source
# File lib/ffmpeg_wrapper/filters/concat.rb, line 37
def concat_for_video
  "concat=n=#{@videos.size}:v=1:a=0 [v]"
end
filter_complex(str) click to toggle source
# 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
input(filename, opts = {}) click to toggle source
# 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
stream_map_for_audio() click to toggle source
# 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
stream_map_for_video() click to toggle source
# 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