ffmpeg-screenrecorder 1.0.0.beta → 1.0.0.beta2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/.rubocop.yml +1 -1
- data/README.md +52 -2
- data/ffmpeg-screenrecorder.gemspec +31 -30
- data/lib/ffmpeg/recorder_options.rb +106 -0
- data/lib/ffmpeg/screenrecorder.rb +75 -111
- data/lib/ffmpeg/windows.rb +18 -0
- metadata +7 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e85788e1924b1350003ba607d5cec470878cb2ecc49a5cf53d5508c4604cb38d
|
4
|
+
data.tar.gz: b438f32841d00eb3c6d3b6a996379f61a9c5e1c4a74335729bd6fc725bf23bae
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f36dafd064d1363b8dd5c42e39c2b840bc6a68db1cc7d42b8c65a667b78e45140c304a47581350a32913a79e6860f34d82778cb89510ec4c1f5925b35f22ee8f
|
7
|
+
data.tar.gz: 98763086b2b600ee80cc319b0b5a86bd598aa5e561f309c929afc613ae002caccf535ebb0d00050ad1a75380fd2d87f3e44b106dd13304571b2d13b5499f6a88
|
data/.gitignore
CHANGED
data/.rubocop.yml
CHANGED
data/README.md
CHANGED
@@ -1,6 +1,10 @@
|
|
1
1
|
# FFMPEG::Screenrecorder
|
2
2
|
|
3
|
-
Ruby gem to record your computer screen using [FFMPEG](https://www.ffmpeg.org/).
|
3
|
+
Ruby gem to record your computer screen - desktop or specific application/window - using [FFMPEG](https://www.ffmpeg.org/).
|
4
|
+
|
5
|
+
## Support
|
6
|
+
|
7
|
+
Only supports Windows OS as of version `1.0.0-beta2`.
|
4
8
|
|
5
9
|
## Installation
|
6
10
|
|
@@ -20,7 +24,53 @@ Or install it yourself as:
|
|
20
24
|
|
21
25
|
## Usage
|
22
26
|
|
23
|
-
|
27
|
+
##### Record Desktop
|
28
|
+
|
29
|
+
```
|
30
|
+
opts = { output: 'ffmpeg-screenrecorder-output.mp4',
|
31
|
+
input: 'desktop',
|
32
|
+
framerate: 30.0,
|
33
|
+
device: 'gdigrab',
|
34
|
+
log: 'ffmpeg-screenrecorder-log.txt' }
|
35
|
+
@recorder = FFMPEG::Screenrecorder.new(opts)
|
36
|
+
|
37
|
+
# Start recording
|
38
|
+
@recorder.start
|
39
|
+
|
40
|
+
# ... Run tests or whatever you want to record
|
41
|
+
|
42
|
+
# Stop recording
|
43
|
+
@recorder.stop
|
44
|
+
|
45
|
+
# Recording file will be available: ffmpeg-screenrecorder-output.mp4
|
46
|
+
```
|
47
|
+
|
48
|
+
##### Record Specific Application/Window
|
49
|
+
```
|
50
|
+
require 'watir'
|
51
|
+
|
52
|
+
browser = Watir::Browser.new :firefox
|
53
|
+
|
54
|
+
FFMPEG::Screenrecorder.window_titles('firefox') # Name of exe
|
55
|
+
#=> "Mozilla Firefox"
|
56
|
+
|
57
|
+
opts = { output: 'ffmpeg-screenrecorder-firefox.mp4',
|
58
|
+
input: 'Mozilla Firefox',
|
59
|
+
framerate: 30.0,
|
60
|
+
device: 'gdigrab',
|
61
|
+
log: 'ffmpeg-screenrecorder-firefox.txt' }
|
62
|
+
@recorder = FFMPEG::Screenrecorder.new(opts)
|
63
|
+
|
64
|
+
# Start recording
|
65
|
+
@recorder.start
|
66
|
+
|
67
|
+
# ... Run tests or whatever you want to record
|
68
|
+
|
69
|
+
# Stop recording
|
70
|
+
@recorder.stop
|
71
|
+
|
72
|
+
# Recording file will be available: ffmpeg-screenrecorder-output.mp4
|
73
|
+
```
|
24
74
|
|
25
75
|
## Development
|
26
76
|
|
@@ -1,30 +1,31 @@
|
|
1
|
-
lib = File.expand_path('lib', __dir__)
|
2
|
-
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
3
|
-
|
4
|
-
Gem::Specification.new do |spec|
|
5
|
-
spec.name = 'ffmpeg-screenrecorder'
|
6
|
-
spec.version = '1.0.0.
|
7
|
-
spec.authors = ['Lakshya Kapoor']
|
8
|
-
spec.email = ['kapoorlakshya@gmail.com']
|
9
|
-
|
10
|
-
spec.summary = 'Record your computer screen using ffmpeg via Ruby.'
|
11
|
-
spec.description = '
|
12
|
-
|
13
|
-
spec.
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
spec.add_development_dependency '
|
23
|
-
spec.add_development_dependency '
|
24
|
-
spec.add_development_dependency '
|
25
|
-
spec.add_development_dependency '
|
26
|
-
spec.add_development_dependency '
|
27
|
-
|
28
|
-
|
29
|
-
spec.add_runtime_dependency '
|
30
|
-
|
1
|
+
lib = File.expand_path('lib', __dir__)
|
2
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
3
|
+
|
4
|
+
Gem::Specification.new do |spec|
|
5
|
+
spec.name = 'ffmpeg-screenrecorder'
|
6
|
+
spec.version = '1.0.0.beta2'
|
7
|
+
spec.authors = ['Lakshya Kapoor']
|
8
|
+
spec.email = ['kapoorlakshya@gmail.com']
|
9
|
+
|
10
|
+
spec.summary = 'Record your computer screen using ffmpeg via Ruby.'
|
11
|
+
spec.description = 'Ruby gem to record your computer screen - desktop or specific application/window' \
|
12
|
+
' - using [FFMPEG](https://www.ffmpeg.org/).'
|
13
|
+
spec.homepage = 'https://github.com/kapoorlakshya/ffmpeg-screenrecorder'
|
14
|
+
spec.license = 'MIT'
|
15
|
+
|
16
|
+
spec.files = Dir.chdir(File.expand_path(__dir__)) do
|
17
|
+
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
18
|
+
end
|
19
|
+
|
20
|
+
spec.require_paths = ['lib']
|
21
|
+
|
22
|
+
spec.add_development_dependency 'bundler', '~> 1.16'
|
23
|
+
spec.add_development_dependency 'pry-byebug', '~> 3.6'
|
24
|
+
spec.add_development_dependency 'rake', '~> 10.0'
|
25
|
+
spec.add_development_dependency 'rspec', '~> 3.0'
|
26
|
+
spec.add_development_dependency 'watir', '~> 6.0'
|
27
|
+
spec.add_development_dependency 'webdrivers', '~> 3.0'
|
28
|
+
|
29
|
+
spec.add_runtime_dependency 'os', '~> 0.9.0'
|
30
|
+
spec.add_runtime_dependency 'streamio-ffmpeg', '~> 1.0'
|
31
|
+
end
|
@@ -0,0 +1,106 @@
|
|
1
|
+
module FFMPEG
|
2
|
+
class RecorderOptions
|
3
|
+
|
4
|
+
def initialize(options)
|
5
|
+
@options = verify_options options
|
6
|
+
end
|
7
|
+
|
8
|
+
def format
|
9
|
+
@options[:format]
|
10
|
+
end
|
11
|
+
|
12
|
+
def framerate
|
13
|
+
@options[:framerate]
|
14
|
+
end
|
15
|
+
|
16
|
+
def infile
|
17
|
+
@options[:infile]
|
18
|
+
end
|
19
|
+
|
20
|
+
def output
|
21
|
+
@options[:output]
|
22
|
+
end
|
23
|
+
|
24
|
+
def advanced
|
25
|
+
@options[:advanced]
|
26
|
+
end
|
27
|
+
|
28
|
+
def log
|
29
|
+
@options[:log]
|
30
|
+
end
|
31
|
+
|
32
|
+
def log_level
|
33
|
+
@options[:log_level]
|
34
|
+
end
|
35
|
+
|
36
|
+
def all
|
37
|
+
@options
|
38
|
+
end
|
39
|
+
|
40
|
+
def parsed
|
41
|
+
vals = "-f #{@options[:format]} "
|
42
|
+
vals << "-r #{@options[:framerate]} "
|
43
|
+
vals << advanced_options if @options[:advanced]
|
44
|
+
vals << "-i #{determine_infile @options[:infile]} "
|
45
|
+
vals << @options[:output]
|
46
|
+
vals << ffmpeg_log_to(@options[:log]) # If provided
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
|
51
|
+
#
|
52
|
+
# Verifies the required options are provided
|
53
|
+
#
|
54
|
+
def verify_options(options)
|
55
|
+
missing_options = required_options.select { |req| options[req].nil? }
|
56
|
+
err = "Required options are missing: #{missing_options}"
|
57
|
+
raise(ArgumentError, err) unless missing_options.empty?
|
58
|
+
|
59
|
+
options
|
60
|
+
end
|
61
|
+
|
62
|
+
#
|
63
|
+
# Returns Array of require options a Symbols
|
64
|
+
#
|
65
|
+
def required_options
|
66
|
+
# -f format
|
67
|
+
# -r framerate
|
68
|
+
# -i input
|
69
|
+
# output
|
70
|
+
%i[format framerate infile output]
|
71
|
+
end
|
72
|
+
|
73
|
+
#
|
74
|
+
# Returns advanced options parsed and ready for ffmpeg to receive.
|
75
|
+
#
|
76
|
+
def advanced_options
|
77
|
+
return nil unless @options[:advanced]
|
78
|
+
raise(ArgumentError, ':advanced cannot be empty.') if options[:advanced].empty?
|
79
|
+
|
80
|
+
arr = []
|
81
|
+
options[:advanced].each { |k, v|
|
82
|
+
arr.push "-#{k} #{v}"
|
83
|
+
}
|
84
|
+
arr.join(' ') + ' '
|
85
|
+
end
|
86
|
+
|
87
|
+
#
|
88
|
+
# Determines if the ffmpeg output will be to a log
|
89
|
+
# file based on given options.
|
90
|
+
#
|
91
|
+
def ffmpeg_log_to(file)
|
92
|
+
return " 2> #{file}" if file
|
93
|
+
' > nul 2>&1' # No log file given
|
94
|
+
end
|
95
|
+
|
96
|
+
#
|
97
|
+
# Returns final infile parameter.
|
98
|
+
# Adds title= qualifier to infile parameter
|
99
|
+
# unless the user is recording the desktop.
|
100
|
+
#
|
101
|
+
def determine_infile(opt)
|
102
|
+
return opt if opt == 'desktop'
|
103
|
+
%Q("title=#{opt}")
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
@@ -1,111 +1,75 @@
|
|
1
|
-
require 'streamio-ffmpeg'
|
2
|
-
require 'os'
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
FFMPEG.logger.debug
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
def
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
end
|
65
|
-
|
66
|
-
def
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
FFMPEG.logger.
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
FFMPEG.logger.debug "Logger initialized."
|
77
|
-
end
|
78
|
-
|
79
|
-
def command
|
80
|
-
"#{FFMPEG.ffmpeg_binary} -y " \
|
81
|
-
"#{extra_opts}" \
|
82
|
-
"-f #{opts[:device]} " \
|
83
|
-
"-framerate #{opts[:framerate]} " \
|
84
|
-
"-i #{opts[:input]} " \
|
85
|
-
"#{opts[:output]} " \
|
86
|
-
"2> #{opts[:log]}"
|
87
|
-
end
|
88
|
-
|
89
|
-
def extra_opts
|
90
|
-
return nil unless opts[:extra_opts]
|
91
|
-
raise ':extra_opts cannot be empty.' if opts[:extra_opts].empty?
|
92
|
-
|
93
|
-
arr = []
|
94
|
-
opts[:extra_opts].each { |k, v|
|
95
|
-
arr.push "-#{k} #{v}"
|
96
|
-
}
|
97
|
-
' ' + arr.join(' ') + ' '
|
98
|
-
end
|
99
|
-
|
100
|
-
# def available_inputs_by(application)
|
101
|
-
# `tasklist /v /fi "imagename eq #{application}.exe" /fo list | findstr Window`
|
102
|
-
# .split("\n")
|
103
|
-
# .reject { |title| title == 'Window Title: N/A' }
|
104
|
-
# end
|
105
|
-
#
|
106
|
-
# def input
|
107
|
-
# return opts[:input] if opts[:input] == 'desktop'
|
108
|
-
# %Q(title="#{opts[:input].gsub('Window Title: ', '')}")
|
109
|
-
# end
|
110
|
-
end # class Recorder
|
111
|
-
end # module FFMPEG
|
1
|
+
require 'streamio-ffmpeg'
|
2
|
+
require 'os'
|
3
|
+
require_relative 'recorder_options'
|
4
|
+
require_relative 'windows'
|
5
|
+
|
6
|
+
module FFMPEG
|
7
|
+
class Screenrecorder
|
8
|
+
extend Windows
|
9
|
+
|
10
|
+
attr_reader :options, :video
|
11
|
+
|
12
|
+
def initialize(options = {})
|
13
|
+
@options = RecorderOptions.new(options)
|
14
|
+
@video = nil
|
15
|
+
@process = nil
|
16
|
+
initialize_logger(@options.log_level || Logger::ERROR)
|
17
|
+
end
|
18
|
+
|
19
|
+
def start
|
20
|
+
@video = nil # New file
|
21
|
+
start_time = Time.now
|
22
|
+
@process = start_ffmpeg
|
23
|
+
elapsed = Time.now - start_time
|
24
|
+
FFMPEG.logger.debug "Process started in #{elapsed}s"
|
25
|
+
FFMPEG.logger.info 'Recording...'
|
26
|
+
end
|
27
|
+
|
28
|
+
def stop
|
29
|
+
FFMPEG.logger.debug 'Stopping ffmpeg.exe...'
|
30
|
+
elapsed = kill_ffmpeg
|
31
|
+
FFMPEG.logger.debug "Stopped ffmpeg.exe in #{elapsed}s"
|
32
|
+
FFMPEG.logger.info 'Recording complete.'
|
33
|
+
@video = Movie.new(options.output)
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
def start_ffmpeg
|
39
|
+
FFMPEG.logger.debug "Command: #{command}"
|
40
|
+
process = IO.popen(command, 'r+')
|
41
|
+
sleep(1.5) # Takes ~1.5s on average to initialize
|
42
|
+
process
|
43
|
+
end
|
44
|
+
|
45
|
+
def kill_ffmpeg
|
46
|
+
@process.puts 'q' # Gracefully exit ffmpeg
|
47
|
+
elapsed = wait_for_io_eof(5)
|
48
|
+
@process.close_write # Close IO
|
49
|
+
elapsed
|
50
|
+
end
|
51
|
+
|
52
|
+
def initialize_logger(level)
|
53
|
+
FFMPEG.logger.progname = 'FFMPEG'
|
54
|
+
FFMPEG.logger.level = level
|
55
|
+
FFMPEG.logger.formatter = proc do |severity, time, progname, msg|
|
56
|
+
"#{time.strftime('%F %T')} #{progname} - #{severity} - #{msg}\n"
|
57
|
+
end
|
58
|
+
FFMPEG.logger.debug 'Logger initialized.'
|
59
|
+
end
|
60
|
+
|
61
|
+
def command
|
62
|
+
cmd = "#{FFMPEG.ffmpeg_binary} -y "
|
63
|
+
cmd << @options.parsed
|
64
|
+
end
|
65
|
+
|
66
|
+
def wait_for_io_eof(timeout)
|
67
|
+
start = Time.now
|
68
|
+
Timeout.timeout(timeout) do
|
69
|
+
sleep(0.1) until @process.eof?
|
70
|
+
end
|
71
|
+
FFMPEG.logger.debug "IO#eof? #{@process.eof?}"
|
72
|
+
Time.now - start
|
73
|
+
end
|
74
|
+
end # class Recorder
|
75
|
+
end # module FFMPEG
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module FFMPEG
|
2
|
+
module Windows
|
3
|
+
|
4
|
+
def window_titles(application)
|
5
|
+
FFMPEG.logger.debug "Retrieving available windows for: #{application}"
|
6
|
+
WindowGrabber.new.available_windows_for application
|
7
|
+
end
|
8
|
+
|
9
|
+
class WindowGrabber
|
10
|
+
def available_windows_for(application)
|
11
|
+
list = `tasklist /v /fi "imagename eq #{application}.exe" /fo list | findstr Window`
|
12
|
+
.split("\n")
|
13
|
+
.reject { |title| title == 'Window Title: N/A' }
|
14
|
+
list.map { |i| i.gsub('Window Title: ', '') } # Make it user friendly
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end # module Windows
|
18
|
+
end # module FFMPEG
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ffmpeg-screenrecorder
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.0.
|
4
|
+
version: 1.0.0.beta2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Lakshya Kapoor
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-
|
11
|
+
date: 2018-11-01 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -122,8 +122,8 @@ dependencies:
|
|
122
122
|
- - "~>"
|
123
123
|
- !ruby/object:Gem::Version
|
124
124
|
version: '1.0'
|
125
|
-
description:
|
126
|
-
|
125
|
+
description: Ruby gem to record your computer screen - desktop or specific application/window
|
126
|
+
- using [FFMPEG](https://www.ffmpeg.org/).
|
127
127
|
email:
|
128
128
|
- kapoorlakshya@gmail.com
|
129
129
|
executables: []
|
@@ -142,8 +142,10 @@ files:
|
|
142
142
|
- bin/setup
|
143
143
|
- ffmpeg-screenrecorder.gemspec
|
144
144
|
- lib/ffmpeg.rb
|
145
|
+
- lib/ffmpeg/recorder_options.rb
|
145
146
|
- lib/ffmpeg/screenrecorder.rb
|
146
|
-
|
147
|
+
- lib/ffmpeg/windows.rb
|
148
|
+
homepage: https://github.com/kapoorlakshya/ffmpeg-screenrecorder
|
147
149
|
licenses:
|
148
150
|
- MIT
|
149
151
|
metadata: {}
|