class ScreenRecorder::Common
@since 1.0.0-beta11
@api private
Constants
- PROCESS_TIMEOUT
Attributes
Public Class Methods
# File lib/screen-recorder/common.rb, line 11 def initialize(input:, output:, advanced: {}) raise Errors::DependencyNotFound unless ffmpeg_exists? @options = Options.new(input: input, output: output, advanced: advanced) @video = nil @process = nil end
Public Instance Methods
Discards the recorded file. Useful in automated testing when a test passes and the recorded file is no longer needed.
# File lib/screen-recorder/common.rb, line 48 def discard File.delete options.output end
Starts the recording
# File lib/screen-recorder/common.rb, line 22 def start ScreenRecorder.logger.debug 'Starting recorder...' @video = nil # New file @process = start_ffmpeg ScreenRecorder.logger.info 'Recording...' @process end
Stops the recording
# File lib/screen-recorder/common.rb, line 33 def stop ScreenRecorder.logger.debug 'Stopping ffmpeg...' exit_code = stop_ffmpeg return if exit_code == 1 # recording failed ScreenRecorder.logger.debug 'Stopped ffmpeg.' ScreenRecorder.logger.info 'Recording complete.' @video = prepare_video unless exit_code == 1 end
Private Instance Methods
Executes the given command and outputs to the optional logfile
# File lib/screen-recorder/common.rb, line 141 def execute_command(cmd, logfile = nil) ScreenRecorder.logger.debug "Executing command: #{cmd}" process = new_process(cmd) process.duplex = true if logfile @log_file = File.new(logfile, 'w+') process.io.stdout = process.io.stderr = @log_file @log_file.sync = true end process.start process end
# File lib/screen-recorder/common.rb, line 101 def ffmpeg_bin "#{ScreenRecorder.ffmpeg_binary} -y" end
Generates the command line arguments based on the given options.
# File lib/screen-recorder/common.rb, line 109 def ffmpeg_command "#{ffmpeg_bin} #{@options.parsed}" end
Returns true if ffmpeg binary is found.
# File lib/screen-recorder/common.rb, line 116 def ffmpeg_exists? return true if FFMPEG.ffmpeg_binary false rescue Errno::ENOENT # Raised when binary is not set in project or found in ENV false end
Returns lines from the log file
# File lib/screen-recorder/common.rb, line 127 def lines_from_log(position = :last, count = 2) f = File.open(options.log) lines = f.readlines lines = lines.last(count) if position == :last lines = lines.first(count) if position == :first f.close lines.join(' ') end
Calls Childprocess.new with OS specific arguments to start the given process.
# File lib/screen-recorder/common.rb, line 158 def new_process(process) ChildProcess.posix_spawn = true if RUBY_PLATFORM == 'java' # Support JRuby. if OS.windows? ChildProcess.new('cmd.exe', '/c', process) else ChildProcess.new('sh', '-c', process) end end
Runs ffprobe on the output video file and returns a FFMPEG::Movie object.
# File lib/screen-recorder/common.rb, line 84 def prepare_video max_attempts = 3 attempts_made = 0 delay = 1.0 begin # Fixes #79 ScreenRecorder.logger.info 'Running ffprobe to prepare video (output) file.' FFMPEG::Movie.new(options.output) rescue Errno::EAGAIN, Errno::EACCES attempts_made += 1 ScreenRecorder.logger.error "Failed to run ffprobe. Retrying... (#{attempts_made}/#{max_attempts})" sleep(delay) retry if attempts_made < max_attempts raise end end
Launches the ffmpeg binary using a generated command based on the given options.
# File lib/screen-recorder/common.rb, line 60 def start_ffmpeg process = execute_command(ffmpeg_command, options.log) sleep(1.5) # Takes ~1.5s to initialize ffmpeg # Check if it exited unexpectedly raise FFMPEG::Error, "Failed to start ffmpeg. Reason: #{lines_from_log(:last, 2)}" if process.exited? process end
Sends 'q' to the ffmpeg binary to gracefully stop the process. Forcefully terminates it if it takes more than 5s.
# File lib/screen-recorder/common.rb, line 73 def stop_ffmpeg @process.io.stdin.puts 'q' # Gracefully exit ffmpeg @process.io.stdin.close @log_file.close wait_for_process_exit(@process) end
Waits for given process to exit. Forcefully kills the process if it does not exit within 5 seconds. Returns exit code.
# File lib/screen-recorder/common.rb, line 172 def wait_for_process_exit(process) process.poll_for_exit(PROCESS_TIMEOUT) process.exit_code # 0 rescue ChildProcess::TimeoutError ScreenRecorder.logger.error 'ffmpeg failed to stop. Force killing it...' process.stop # Tries increasingly harsher methods to kill the process. ScreenRecorder.logger.error 'Forcefully killed ffmpeg. Recording failed!' 1 end