vncrec 1.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
}
|