vncrec 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +16 -0
- data/.rspec +4 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +36 -0
- data/Rakefile +8 -0
- data/bin/vncrec +80 -0
- data/examples/exit.rb +8 -0
- data/examples/mp4.rb +10 -0
- data/examples/mp4audio.rb +11 -0
- data/ext/enchex_c/ReadRect.c +215 -0
- data/ext/enchex_c/extconf.rb +5 -0
- data/lib/vncrec.rb +6 -0
- data/lib/vncrec/constants.rb +10 -0
- data/lib/vncrec/recorder.rb +286 -0
- data/lib/vncrec/rfb/enchex.rb +115 -0
- data/lib/vncrec/rfb/encraw.rb +33 -0
- data/lib/vncrec/rfb/enczrle.rb +202 -0
- data/lib/vncrec/rfb/proxy.rb +200 -0
- data/lib/vncrec/version.rb +3 -0
- data/lib/vncrec/writers.rb +209 -0
- data/spec/executable_spec.rb +40 -0
- data/spec/recorder_spec.rb +338 -0
- data/spec/spec_helper.rb +171 -0
- data/spec/writers_spec.rb +121 -0
- data/vncrec.gemspec +34 -0
- metadata +165 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 44f235ad8e44ea1bab090676b4b011ba3eb53993
|
4
|
+
data.tar.gz: 314f906e6b36542cc0c8ed42b61fe14c0de4dc31
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: bcab87f697e3d8ea4da518a1e2bde030235b83016d1c6c48a327168f365d75f4c962589f037fcc4465b5faa09eb88ae4b77b2470fdfe1185fb35c4677ae529a6
|
7
|
+
data.tar.gz: ee53667bcbfc8f44e2e86304cd82932d3f095090dedfeabf59618f42680fb942fac35d69dc3b2c93e3aba33f3e0f8e875a6e318f916ad43b0dfa95a81e9fc30a
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2015 slowness pc
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
VNCRec
|
2
|
+
===========
|
3
|
+
|
4
|
+
VNCRec is a gem that provides you
|
5
|
+
tools to record VNC session.
|
6
|
+
|
7
|
+
##Usage
|
8
|
+
There is a binary called `vncrec`, which is a recorder tool.
|
9
|
+
|
10
|
+
With no host specified, it will listen on
|
11
|
+
given port (5900 by default) for connection
|
12
|
+
from VNC server (they refer to that mode as
|
13
|
+
_reverse connection_). This is intended to
|
14
|
+
be used when you want to record remote
|
15
|
+
desktop and you don't know if it's available.
|
16
|
+
|
17
|
+
Call `vncrec -h` for list of available options.
|
18
|
+
To stop recording send `SIGINT` (press ^C).
|
19
|
+
|
20
|
+
You can also use `VNCRec::Recorder` class in your ruby code.
|
21
|
+
|
22
|
+
```ruby
|
23
|
+
require 'vncrec'
|
24
|
+
|
25
|
+
r = VNCRec::Recorder.new(filename: "myvncsession.mp4")
|
26
|
+
r.run
|
27
|
+
# loop do
|
28
|
+
# sleep 100
|
29
|
+
# end
|
30
|
+
```
|
31
|
+
|
32
|
+
If you have FFmpeg installed, file extension is passed just through to it, so
|
33
|
+
you can specify encoder you want by passing .mp4 or .flv, etc. If no filename
|
34
|
+
specified, `vncrec` assumes raw. There is also a way to
|
35
|
+
record raw video by specifying filename with extension .raw. This way FFmpeg is
|
36
|
+
not required.
|
data/Rakefile
ADDED
data/bin/vncrec
ADDED
@@ -0,0 +1,80 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# encoding: utf-8
|
3
|
+
|
4
|
+
require 'optparse'
|
5
|
+
require 'vncrec'
|
6
|
+
|
7
|
+
options = VNCRec::Recorder::DEFAULTS
|
8
|
+
|
9
|
+
OptionParser.new do |opts|
|
10
|
+
opts.on('--pix_fmt F', 'Specify pixel format. List available with --pix_fmts') do |d|
|
11
|
+
options[:pix_fmt] = d.dup
|
12
|
+
end
|
13
|
+
opts.on('--pix_fmts', 'List available pix_fmts') do
|
14
|
+
puts 'Available pixel formats:'
|
15
|
+
puts "bgr8\t8 bits color palette"
|
16
|
+
puts "bgra\t32 bits color palette"
|
17
|
+
puts
|
18
|
+
exit 0
|
19
|
+
end
|
20
|
+
opts.on('--debug', 'Enable debugger, gem pry-debugger required') do
|
21
|
+
require 'pry-byebug'
|
22
|
+
options[:debug] = true
|
23
|
+
end
|
24
|
+
opts.on('-e ENC', '--encoding', 'Specify encoding') do |e|
|
25
|
+
name = "ENC_#{e.upcase}".to_sym
|
26
|
+
if VNCRec.constants.index(name)
|
27
|
+
options[:encoding] = VNCRec.const_get(name)
|
28
|
+
else
|
29
|
+
puts 'Unsupported encoding type. Pass option --encodings to see a list of available encodings.'
|
30
|
+
exit 1
|
31
|
+
end
|
32
|
+
end
|
33
|
+
opts.on('--encodings', 'Print list of available encodings') do
|
34
|
+
puts 'Available encodings are:'
|
35
|
+
puts "\traw"
|
36
|
+
puts "\thextile"
|
37
|
+
puts "\tzrle(not yet supported)"
|
38
|
+
puts
|
39
|
+
exit
|
40
|
+
end
|
41
|
+
opts.on('-g GEOM', '--geometry GEOM', 'Screen resolution, <X>x<Y>') do |opt|
|
42
|
+
unless opt =~ /\d+x\d+/
|
43
|
+
puts 'Unknown screen resolution format, use <x>x<y>'
|
44
|
+
exit 1
|
45
|
+
end
|
46
|
+
options[:geometry] = opt
|
47
|
+
end
|
48
|
+
opts.on('-h', '--help', 'Display this message') do
|
49
|
+
puts opts
|
50
|
+
exit
|
51
|
+
end
|
52
|
+
opts.on('--logging', 'Enable logging to stderr') do
|
53
|
+
options[:logging] = true
|
54
|
+
end
|
55
|
+
opts.on('-o NAME', '--output', 'Specify filename to write to. Special name is "stdout"') do |f|
|
56
|
+
options[:filename] = f.to_s
|
57
|
+
end
|
58
|
+
opts.on('-p PORT', '--port', 'Choose port to listen on') do |p|
|
59
|
+
options[:port] = p.to_i
|
60
|
+
end
|
61
|
+
opts.on('-r RATE', '--framerate <rate>', 'Specify framerate') do |r|
|
62
|
+
options[:fps] = r.to_i
|
63
|
+
end
|
64
|
+
opts.on('-s', '--server SERVER', 'Specify host address') do |s|
|
65
|
+
options[:host] = s
|
66
|
+
end
|
67
|
+
end.parse!
|
68
|
+
|
69
|
+
v = VNCRec::Recorder.new(options)
|
70
|
+
v.on_exit << ->() { exit }
|
71
|
+
Signal.trap('INT') { v.running? ? v.stop : exit }
|
72
|
+
Signal.trap('USR1') do
|
73
|
+
puts v.filesize
|
74
|
+
$stdout.flush
|
75
|
+
end
|
76
|
+
|
77
|
+
v.run
|
78
|
+
loop do
|
79
|
+
sleep 100
|
80
|
+
end
|
data/examples/exit.rb
ADDED
data/examples/mp4.rb
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# encoding: utf-8
|
3
|
+
|
4
|
+
require 'vncrec'
|
5
|
+
|
6
|
+
VNCRec::Recorder.new(
|
7
|
+
encoding: VNCRec::EncHextile,
|
8
|
+
filename: 'file.mp4',
|
9
|
+
ffmpeg_ia_opts: '-i /path/to/audio/file.mp3',
|
10
|
+
ffmpeg_out_opts: '-map 0 -vcodec libx264 -preset ultrafast -map 1 -acodec mp3'
|
11
|
+
)
|
@@ -0,0 +1,215 @@
|
|
1
|
+
#include <ruby.h>
|
2
|
+
#include <ruby/io.h>
|
3
|
+
#include <fcntl.h>
|
4
|
+
|
5
|
+
#include <math.h>
|
6
|
+
|
7
|
+
int my_readbyte(VALUE io){
|
8
|
+
return NUM2INT( rb_funcall(io, rb_intern("readbyte"),0));
|
9
|
+
}
|
10
|
+
|
11
|
+
char *my_read(VALUE io, long nbytes){
|
12
|
+
char * result = (char*)malloc(nbytes+1);
|
13
|
+
VALUE nbytes_v = INT2NUM(nbytes);
|
14
|
+
VALUE rstr = rb_funcall(io, rb_intern("read"), 1,nbytes_v);
|
15
|
+
memcpy(
|
16
|
+
result,
|
17
|
+
RSTRING_PTR(rstr),
|
18
|
+
RSTRING_LEN(rstr)
|
19
|
+
);
|
20
|
+
return result;
|
21
|
+
}
|
22
|
+
|
23
|
+
VALUE mVNC = Qnil;
|
24
|
+
VALUE mEncHextile = Qnil;
|
25
|
+
VALUE mRFB = Qnil;
|
26
|
+
|
27
|
+
void Init_enchex_c();
|
28
|
+
|
29
|
+
static VALUE read_rect_c(VALUE self,VALUE io, VALUE _x, VALUE _y, VALUE _w, VALUE _h, VALUE bitspp, VALUE fb, VALUE fbw, VALUE fbh);
|
30
|
+
|
31
|
+
void Init_enchex_c() {
|
32
|
+
mVNC = rb_define_module("VNCRec");
|
33
|
+
mRFB = rb_define_module_under(mVNC, "RFB");
|
34
|
+
mEncHextile = rb_define_module_under(mRFB, "EncHextile");
|
35
|
+
rb_define_module_function(mEncHextile, "read_rect", read_rect_c, 9);
|
36
|
+
}
|
37
|
+
|
38
|
+
void _read_subrect_c(int rx, int ry, int rw, int rh, int tx, int ty, char *fg, VALUE io, char* fb, int fbw, int fbh, int bpp){
|
39
|
+
|
40
|
+
unsigned char xy, wh;
|
41
|
+
xy = my_readbyte(io);
|
42
|
+
wh = my_readbyte(io);
|
43
|
+
|
44
|
+
if(!fb)
|
45
|
+
return;
|
46
|
+
|
47
|
+
unsigned char stx = (xy & 0xF0) >> 4;
|
48
|
+
unsigned char stw = ((wh & 0xF0) >> 4) + 1;
|
49
|
+
unsigned char sty = (xy & 0x0F);
|
50
|
+
unsigned char sth = (wh & 0x0F) + 1;
|
51
|
+
rw *= bpp;
|
52
|
+
rx *= bpp;
|
53
|
+
tx *= bpp;
|
54
|
+
stx *= bpp;
|
55
|
+
stw *= bpp;
|
56
|
+
char *fg_row = (char*)malloc(stw);
|
57
|
+
int k,l;
|
58
|
+
for(k = 0; k < stw; k+=bpp){
|
59
|
+
for(l = 0; l < bpp; l++){
|
60
|
+
fg_row[k+l] = fg[l];
|
61
|
+
}
|
62
|
+
}
|
63
|
+
|
64
|
+
int i;
|
65
|
+
for (i = 0; i < sth; i++) {
|
66
|
+
register int row_begin = fbw*(ry+ty+sty+i) + rx + tx + stx;
|
67
|
+
memcpy(&fb[row_begin], fg_row, stw);
|
68
|
+
}
|
69
|
+
free(fg_row);
|
70
|
+
}
|
71
|
+
|
72
|
+
void _read_subrect_c_raw(int rx, int ry, int rw, int rh, int tx, int ty, int tw, int th, VALUE io, char* fb, int fbw, int fbh, int bpp){
|
73
|
+
|
74
|
+
rw *= bpp;
|
75
|
+
rx *= bpp;
|
76
|
+
tw *= bpp;
|
77
|
+
tx *= bpp;
|
78
|
+
|
79
|
+
int trow;
|
80
|
+
char *data = my_read(io, tw*th);
|
81
|
+
|
82
|
+
if(!fb){
|
83
|
+
return;
|
84
|
+
}
|
85
|
+
|
86
|
+
for (trow = 0; trow < th; trow++) {
|
87
|
+
register int row_begin = fbw*(ry+ty+trow) + rx + tx;
|
88
|
+
memcpy( &fb[row_begin], &data[trow*tw],tw);
|
89
|
+
}
|
90
|
+
free(data);
|
91
|
+
}
|
92
|
+
|
93
|
+
void _fill_tile_bg(int rx, int ry, int rw, int rh, int tx, int ty, int tw, int th, char*bg, VALUE io, char* fb, int fbw, int fbh, int bpp){
|
94
|
+
if(!fb)
|
95
|
+
return;
|
96
|
+
|
97
|
+
rw *= bpp;
|
98
|
+
rx *= bpp;
|
99
|
+
tw *=bpp;
|
100
|
+
tx *= bpp;
|
101
|
+
int trow;
|
102
|
+
char *bg_row = (char*)malloc(tw);
|
103
|
+
int k,l;
|
104
|
+
for(k = 0; k < tw; k+=bpp){
|
105
|
+
for(l = 0; l < bpp; l++){
|
106
|
+
bg_row[k+l] = bg[l];
|
107
|
+
}
|
108
|
+
}
|
109
|
+
register int base_row_begin = fbw*(ry+ty) + rx + tx;
|
110
|
+
for (trow = 0; trow < th; trow++) {
|
111
|
+
register int row_begin = base_row_begin + fbw*trow;
|
112
|
+
memcpy(&fb[row_begin], bg_row, tw);
|
113
|
+
}
|
114
|
+
free(bg_row);
|
115
|
+
}
|
116
|
+
|
117
|
+
static VALUE read_rect_c(VALUE self,VALUE io, VALUE _x, VALUE _y, VALUE _w, VALUE _h, VALUE bitspp, VALUE _fb, VALUE _fbw, VALUE _fbh){
|
118
|
+
int w = NUM2INT(_w);
|
119
|
+
int h = NUM2INT(_h);
|
120
|
+
int rx = NUM2INT(_x);
|
121
|
+
int ry = NUM2INT(_y);
|
122
|
+
int fbw = NUM2INT(_fbw);
|
123
|
+
int fbh = NUM2INT(_fbh);
|
124
|
+
int bpp = (float)NUM2INT(bitspp) / 8.0;
|
125
|
+
|
126
|
+
|
127
|
+
int tiles_row_num = ceil((float)h/16.0);
|
128
|
+
int tiles_col_num = ceil((float)w/16.0);
|
129
|
+
|
130
|
+
char *fb;
|
131
|
+
int fbsize = fbw*fbh;
|
132
|
+
if(ry > fbh || rx * bpp > fbw)
|
133
|
+
fb = NULL; // skip
|
134
|
+
else
|
135
|
+
fb = (char*)malloc(fbsize);
|
136
|
+
if(fb)
|
137
|
+
memcpy(fb,RSTRING(_fb)->as.heap.ptr,RSTRING(_fb)->as.heap.len);
|
138
|
+
|
139
|
+
int last_tile_w = w % 16;
|
140
|
+
int last_tile_h = h % 16;
|
141
|
+
|
142
|
+
char prev_tile_bg[4];
|
143
|
+
char prev_tile_fg[4];
|
144
|
+
|
145
|
+
int i,j;
|
146
|
+
int tw, th;
|
147
|
+
int ty, tx;
|
148
|
+
int ti,tj,tk;
|
149
|
+
unsigned char subenc;
|
150
|
+
for (i = 0; i < tiles_row_num; i++) {
|
151
|
+
|
152
|
+
if ((i == tiles_row_num-1) && (last_tile_h > 0))
|
153
|
+
th = last_tile_h;
|
154
|
+
else
|
155
|
+
th = 16;
|
156
|
+
ty = 16 * i;
|
157
|
+
for (j = 0; j < tiles_col_num; j++) {
|
158
|
+
|
159
|
+
if ((j == tiles_col_num-1) && (last_tile_w > 0))
|
160
|
+
tw = last_tile_w;
|
161
|
+
else
|
162
|
+
tw = 16;
|
163
|
+
tx = 16 * j;
|
164
|
+
|
165
|
+
subenc = my_readbyte(io);
|
166
|
+
|
167
|
+
if(subenc & 1){ //raw
|
168
|
+
_read_subrect_c_raw(rx,ry,w,h,tx,ty,tw,th,io,fb,fbw,fbh,bpp);
|
169
|
+
}
|
170
|
+
if(subenc & 2){//background specified
|
171
|
+
for (tk = 0; tk < bpp; tk++) {
|
172
|
+
prev_tile_bg[tk] = my_readbyte(io);
|
173
|
+
}
|
174
|
+
}
|
175
|
+
if(!(subenc & 1)){//should not refill raw
|
176
|
+
for (ti = 0; ti < th; ti++) {
|
177
|
+
for(tj = 0; tj < tw; tj++){
|
178
|
+
_fill_tile_bg(rx,ry,w,h,tx,ty,tw,th,prev_tile_bg,io,fb,fbw,fbh,bpp);
|
179
|
+
}
|
180
|
+
}
|
181
|
+
}
|
182
|
+
if(subenc & 4){//foreground specified
|
183
|
+
for (tk = 0; tk < bpp; tk++) {
|
184
|
+
prev_tile_fg[tk] = my_readbyte(io);
|
185
|
+
}
|
186
|
+
}
|
187
|
+
if(subenc & 8){//any subrect
|
188
|
+
size_t subrects_number = my_readbyte(io);
|
189
|
+
size_t subrect;
|
190
|
+
char fg[4];
|
191
|
+
for (subrect = 0; subrect < subrects_number; subrect++) {
|
192
|
+
if(subenc & 16){//subrect colored
|
193
|
+
for (tk = 0; tk < bpp; tk++) {
|
194
|
+
fg[tk] = my_readbyte(io);
|
195
|
+
}
|
196
|
+
_read_subrect_c(rx,ry,w,h,tx,ty, fg, io, fb, fbw, fbh, bpp);
|
197
|
+
}else{
|
198
|
+
_read_subrect_c(rx,ry,w,h,tx,ty, prev_tile_fg, io, fb, fbw, fbh, bpp);
|
199
|
+
}
|
200
|
+
}
|
201
|
+
}
|
202
|
+
|
203
|
+
} //for j
|
204
|
+
}//for i
|
205
|
+
|
206
|
+
if(fb) {
|
207
|
+
for (i = 0; i < fbh; i++) {
|
208
|
+
rb_str_update(_fb, i*fbw, fbw,
|
209
|
+
rb_str_new(&fb[i * fbw], fbw));
|
210
|
+
}
|
211
|
+
free(fb);
|
212
|
+
}
|
213
|
+
|
214
|
+
return Qtrue;
|
215
|
+
}
|