lib/screen-recorder/options.rb
# @since 1.0.0-beta11
#
# @api private
module ScreenRecorder
# @since 1.0.0-beta11
class Options
attr_reader :all
DEFAULT_LOG_FILE = 'ffmpeg.log'.freeze
DEFAULT_FPS = 15.0
DEFAULT_MAC_INPUT_PIX_FMT = 'uyvy422'.freeze # For avfoundation
DEFAULT_PIX_FMT = 'yuv420p'.freeze
YUV420P_SCALING = '"scale=trunc(iw/2)*2:trunc(ih/2)*2"'.freeze
def initialize(options)
# @todo Consider using OpenStruct
@all = verify_options options
advanced[:input] = default_advanced_input.merge(advanced_input)
advanced[:output] = default_advanced_output.merge(advanced_output)
advanced[:log] ||= DEFAULT_LOG_FILE
# Fix for using yuv420p pixel format for output
# @see https://www.reck.dk/ffmpeg-libx264-height-not-divisible-by-2/
advanced_output[:vf] = YUV420P_SCALING if advanced_output[:pix_fmt] == 'yuv420p'
end
#
# Returns given input file or input
#
def input
@all[:input]
end
#
# Returns capture device in use
#
def capture_device
determine_capture_device
end
#
# Returns given output filepath
#
def output
@all[:output]
end
#
# Returns given values that are optional
#
def advanced
@all[:advanced] ||= {}
end
#
# Returns given framerate
#
def framerate
ScreenRecorder.logger.warn '#framerate will not be available in the next release. Use #advanced instead.'
advanced[:output][:framerate]
end
#
# Returns given log filename
#
def log
advanced[:log]
end
#
# Returns a String with all options parsed and
# ready for the ffmpeg process to use
#
def parsed
vals = "-f #{capture_device} "
vals << parse_advanced(advanced_input)
vals << "-i #{input} " unless advanced_input[:i] # Input provided by user
vals << parse_advanced(advanced)
vals << parse_advanced(advanced_output)
vals << output
end
private
#
# Verifies the required options are provided and returns
# the given options Hash. Raises ArgumentError if all required
# options are not present in the given Hash.
#
def verify_options(options)
TypeChecker.check options, Hash
TypeChecker.check options[:advanced], Hash if options[:advanced]
missing_options = required_options.select { |req| options[req].nil? }
err = "Required options are missing: #{missing_options}"
raise(ArgumentError, err) unless missing_options.empty?
options
end
def advanced_input
advanced[:input] ||= {}
end
def advanced_output
advanced[:output] ||= {}
end
def default_advanced_input
{
pix_fmt: OS.mac? ? DEFAULT_MAC_INPUT_PIX_FMT : nil
}
end
def default_advanced_output
{
pix_fmt: DEFAULT_PIX_FMT,
framerate: advanced[:framerate] || DEFAULT_FPS
}
end
#
# Returns Array of required options as Symbols
#
def required_options
%i[input output]
end
#
# Returns given Hash parsed and ready for ffmpeg to receive.
#
def parse_advanced(opts)
# @todo Replace arr with opts.each_with_object([])
arr = []
rejects = %i[input output log]
# Do not parse input/output and log as they're placed separately in #parsed
opts.reject { |k, _| rejects.include? k }
.each do |k, v|
arr.push "-#{k} #{v}" unless v.nil? # Ignore blank params
end
"#{arr.join(' ')} "
end
#
# Returns input capture device based on user given value or the current OS.
#
def determine_capture_device
# User given capture device or format from advanced configs Hash
# @see https://www.ffmpeg.org/ffmpeg.html#Main-options
return advanced_input[:f] if advanced_input[:f]
return advanced_input[:fmt] if advanced_input[:fmt]
default_capture_device
end
#
# Returns input capture device for current OS.
#
def default_capture_device
return 'gdigrab' if OS.windows?
return 'x11grab' if OS.linux?
return 'avfoundation' if OS.mac?
raise 'Your OS is not supported. Feel free to create an Issue on GitHub.'
end
end
end