7 ; audio_bytes_per_frame
: int
8 ; audio_bytes_processed
: int
10 ; audio_running
: bool
15 ; update_requested
: bool
16 ; last_video_update
: float
30 type t
= (int * int * int * int * int)
31 external reset
: t
-> int = "ml_scale_reset"
32 external scale
: string -> string -> unit = "ml_scale"
34 let reset ~w ~h ~out_w ~out_h ~pitch
=
35 reset (w
, h
, out_w
, out_h
, pitch
)
38 let fail fmt
= Printf.kprintf
(fun s
-> failwith s
) fmt
;;
40 let log fmt
= Printf.kprintf ignore fmt
;;
42 let r8 s o
= Char.code s
.[o
];;
43 let r16 s o
= let b1 = r8 s o
and b2
= r8 s
(o
+1) in (b1 lsl 8) lor b2
;;
44 let r16s s o
= let i = r16 s
0 in i - ((i lor 0x8000) lsl 1);;
47 and w2
= r16 s
(o
+2) in
48 Int32.logor
(Int32.shift_left
(Int32.of_int
w1) 16) (Int32.of_int w2
)
50 let rint s o
= let l = r32 s o
in Int32.to_int
l;;
51 let w8 s o
i = s
.[o
] <- Char.chr
(i land 255);;
52 let w16 s o
i = w8 s
(o
+1) i; w8 s o
(i lsr 8);;
53 let wint s o
i = w16 s
(o
+2) i; w16 s o
(i asr 16);;
55 w16 s
(o
+2) (Int32.to_int
l);
56 w16 s o
(Int32.to_int
(Int32.shift_right
l 16));
60 let b = Buffer.create
20 in
61 Buffer.add_string
b "\002xyy";
63 let nencodings = List.fold_left
64 (fun n
i -> w32 c 0 i; Buffer.add_string
b c; succ n
)
65 0 (0l :: -223l :: (if t
.useaudio
then [-259l] else []))
67 let s = Buffer.contents
b in
73 let s = String.create
20 in
88 let setaudiofmt ~fmt ~nchannels ~freq
=
89 let s = String.create
10 in
100 let s = String.create
4 in
107 let resize t w h out_w out_h
=
108 let pitch = w
* t
.depth
in
109 let scaledsize = Scale.reset ~w ~h ~out_w ~out_h ~
pitch in
110 let video = String.create
(pitch * h
) in
111 { t
with video = video
115 ; scaledsize = scaledsize
120 let create_context ~w ~h ~encset
=
121 let bytes_per_frame =
123 match encset
.audiofmt
with
127 | _
-> fail "invalid audiofmt %d" encset
.audiofmt
129 let frame_size = sample_size * encset
.nchannels
in
130 (encset
.freq
* frame_size) / encset
.fps
133 { video = ""; w
= -1; h
= -1; pitch = -1; depth
= 2
134 ; audio_bytes_per_frame
= bytes_per_frame
135 ; audio_bytes_processed
= 0
137 ; update_requested
= false
138 ; audio_running
= false
140 ; last_video_update
= 0.0
142 String.make
bytes_per_frame '
\000'
(* wrong for unsigned *)
144 w h encset
.out_w encset
.out_h
148 let n = rint (pump#get
4) 0 in
149 let s = pump#get
n in
150 fail "Protocol failure, reason: `%S'" s;
153 let rec handshake encset pump
=
154 let s = pump#get
12 in
157 let dec a
b c = a
*100 + b*10 + c in
158 Scanf.sscanf
s "RFB %1i%1i%1i.%1i%1i%1i\n"
159 (fun a
b c d e f
-> dec a
b c, dec d e f
)
161 fail "can't parse ProtocolVersion `%S'" s
164 if maj < 3 || (maj == 3 && min
< 8)
165 then fail "Too old of a protocol %d.%d" maj min
;
167 pump#put
"RFB 003.008\n";
168 let s = let nsectypes = r8 (pump#get
1) 0 in pump#get
nsectypes in
169 (try ignore
(String.index
s '
\001'
)
170 with Not_found
-> fail "None auth not found");
172 if r32 (pump#get
4) 0 <> 0l then reason pump
;
174 serverinit encset pump
176 and serverinit encset pump
=
177 let s = pump#get
24 in
180 and namelen
= rint s 20 in
181 let name = pump#get namelen
in
183 printf
"server is `%S'@." name;
184 pump#put
(encodings encset
);
187 let context = create_context ~
w ~h ~encset
in
188 boot encset
context pump
190 and boot encset t pump
=
191 let request_update t
=
192 if not t
.update_requested
then (
193 let s = "\003\001\000\000\000\000wwhh" in
198 { t
with update_requested
= true }
203 match r8 (pump#get
1) 0 with
204 | 000 -> framebuffer_update t
206 | msg
-> fail "unexpected server message %x" msg
212 if t.audio_bytes_processed
> t.audio_bytes_per_frame
213 then request_update t else t
215 let fdur = 1.0 /. float encset
.fps
in
216 let deadline = t.last_video_update
+. fdur in
217 let now = Unix.gettimeofday
() in
218 let rec loop t delta
=
220 let got_input = if delta
> 0.0 then pump#wait delta
else false in
225 if t.update_requested
226 then (pump#add_audio
t.silence
; pump#add_video
t.lastout
; t)
227 else request_update t
232 loop t (deadline -. now)
237 let fmt = encset
.audiofmt
238 and nchannels
= encset
.nchannels
239 and freq
= Int32.of_int encset
.freq
in
240 pump#put
(setaudiofmt ~
fmt ~nchannels ~freq
);
245 match r8 (pump#get
1) 0 with
247 | msg
-> fail "unexpected aliguori message %x" msg
249 and framebuffer_update
t =
250 let nrects = r16 (pump#get
3) 1 in
251 let rec loop t i = if i = nrects then t else
252 let s = pump#get
12 in
259 | 0l -> raw_rect
t x y
w h
; loop t (i+1)
260 | -223l -> loop (resize t w h encset
.out_w encset
.out_h
) (i+1)
261 | -259l -> loop (audio_ack
t) (i+1)
262 | _
-> fail "rect with unhandled encoding %lx, %S" e
s
265 let out = String.create
t.scaledsize in
266 Scale.scale
t.video out;
268 if not
t.audio_running
then pump#add_audio
t.silence
;
270 last_video_update
= Unix.gettimeofday
();
272 update_requested
= false;
273 audio_bytes_processed
= t.audio_bytes_processed
- t.audio_bytes_per_frame
}
275 and raw_rect
t x y
w h
=
276 let src = pump#get
(w*h
*t.depth
) in
278 let len = w*t.depth
in
279 let pitch = t.pitch in
280 let rec loop row src_pos dst_pos
=
281 if row
= h
then () else (
282 StringLabels.blit ~
src ~
dst ~src_pos ~dst_pos ~
len;
283 loop (row
+1) (src_pos
+len) (dst_pos
+pitch)
286 loop 0 0 (x*t.depth
+ y
*t.pitch)
289 match r16 (pump#get
2) 0 with
290 | 000 -> drain
{ t with audio_running
= false }
291 | 001 -> drain
{ t with audio_running
= true }
292 | 002 -> audio_data
t
293 | msg
-> fail "unexpected audio message %x" msg
296 let nbytes = rint (pump#get
4) 0 in
297 let audio = pump#get
nbytes in
298 pump#add_audio
audio;
300 { t with audio_bytes_processed
= t.audio_bytes_processed
+ nbytes }
305 srv1 (request_update t);
310 if String.length addr
> 5 && String.sub addr
0 5 = "unix:"
312 let path = String.sub addr
5 (String.length addr
- 5) in
313 let sock = Unix.socket
Unix.PF_UNIX
Unix.SOCK_STREAM
0 in
314 sock, Unix.ADDR_UNIX
path
316 let sock = Unix.socket
Unix.PF_INET
Unix.SOCK_STREAM
0 in
319 let p = String.index
addr '
:'
in
320 let s = String.sub
addr (p + 1) (String.length
addr - p - 1) in
322 try int_of_string
s with exn
->
323 fail "can't parse port in `%S': %s"
324 addr (Printexc.to_string exn
)
326 let addr = String.sub
addr 0 p in
331 let addr = (Unix.gethostbyname
addr).Unix.h_addr_list
.(0) in
332 sock, Unix.ADDR_INET
(addr, port)
334 Unix.connect
sock addr;
338 let pump encset
sock =
339 let null = Unix.openfile
"/dev/null" [] 0 in
340 let rapipe, wapipe
= Unix.pipe
() in
341 let rvpipe, wvpipe
= Unix.pipe
() in
343 Unix.set_close_on_exec wapipe
;
344 Unix.set_close_on_exec wvpipe
;
345 Unix.set_close_on_exec
sock;
346 Unix.set_nonblock wapipe
;
347 Unix.set_nonblock wvpipe
;
349 let _pid = Unix.create_process
351 [| "/bin/sh"; "./qcap.sh"
352 ; string_of_int encset
.out_w
353 ; string_of_int encset
.out_h
354 ; string_of_int
(Obj.magic
rvpipe)
355 ; string_of_int
(Obj.magic
rapipe)
356 ; string_of_int encset
.fps
365 if n = 0 then fail "%s: end of file" c;
366 if n = l then () else loop (p + n) (l - n)
367 in loop 0 (String.length
s)
369 let rec write s fd buf pos
len () =
372 let n = Unix.write fd buf pos
len in
373 if n = 0 then fail "EOF while writing %s" s;
375 with Unix.Unix_error
(Unix.EAGAIN
, _
, _
) -> 0
377 if n = 0 then `again
else
380 else `more
(write s fd buf
(pos
+n) (len-n))
391 | _
-> [wvpipe
; wapipe
]
393 if l = [] then () else
394 let _, l, _ = Unix.select
[] l [] 0.0 in
395 let rec loop = function
397 let lr = if fd
= wapipe
then al
else vl
in
398 let rec wrall accu
= function
402 | `completed
-> wrall accu tl
403 | `again
-> f
:: accu
404 | `more f
-> f
:: accu
406 lr := List.rev
(wrall [] !lr);
412 method add_video data
=
413 if String.length data
= 0 then fail "empty data passed to add_video";
414 vl
:= !vl
@ [write "video" wvpipe data
0 (String.length data
)]
416 method add_audio data
=
417 al
:= !al
@ [write "audio" wapipe data
0 (String.length data
)]
420 let r, _, _ = Unix.select
[sock] [] [] dur
in
423 method get
n = let s = String.create
n in rw "get" (Unix.read
sock) s; s
424 method put
s = rw "put" (Unix.write sock) s
432 and noaudio
= ref false
433 and host
= ref "localhost:5900" in
434 let setdim s = Scanf.sscanf
s "%dx%d" (fun w h
-> out_w := w; out_h
:= h
) in
436 ["-s", Arg.String
setdim , "<int>x<int> output dimensions"
437 ;"-fps", Arg.Set_int fps
, "<int> desired fps"
438 ;"-noaudio", Arg.Set noaudio
, "<bool> do not capture audio"])
440 "Usage: vcap [options] [host]\nOptions are:";
448 ; useaudio = not
!noaudio
452 let sock = vncopen !host
in
453 let pump = pump encset sock in
454 handshake encset pump;
455 with Unix.Unix_error
(e
, s1
, s2
) ->
456 eprintf
"%s(%s): %s@." s1 s2
(Unix.error_message e
)