1 /*****************************************************************************
2 * remoteosd.c: remote osd over vnc filter module
3 *****************************************************************************
4 * Copyright (C) 2007-2008 Matthias Bauer
7 * Authors: Matthias Bauer <matthias dot bauer #_at_# gmx dot ch>
9 * This program is free software; you can redistribute it and/or modify it
10 * under the terms of the GNU Lesser General Public License as published by
11 * the Free Software Foundation; either version 2.1 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implid warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU Lesser General Public License for more details.
19 * You should have received a copy of the GNU Lesser General Public License
20 * along with this program; if not, write to the Free Software Foundation,
21 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22 *****************************************************************************/
24 /*****************************************************************************
25 * RemoteOSD uses the RFB-Protocol of VNC to display an On-Screen-Display
26 * menu generated by a streaming server as overlay for the streamed video.
28 * The streaming server that implements this is the ffnetdev plugin for VDR.
29 * VDR (VideoDiskRecorder) is an Linux based OpenSource harddisk recorder
31 * The VDR ffnetdev plugin emulates the hardware MPEG decoder and streams the
32 * video over the network instead of hardware video outputs.
33 * The OSD menu of VDR is offered with the RFB protocol to a VNC client.
35 * In fact this video-filter is a simple VNC client that could be also used to
36 * connect to a real VNC host.
37 * Only 8-bit color is supported at the moment.
38 * Using password protected VNC hosts is supported but not recommended, because
39 * you need to insert the used password in the plugin configuration page of
40 * VLC configuration in plain text and it's saved in plain text.
41 *****************************************************************************/
45 /*****************************************************************************
47 *****************************************************************************/
53 #include <vlc_common.h>
54 #include <vlc_plugin.h>
56 #include <vlc_filter.h>
57 #include <vlc_keys.h> /* KEY_MODIFIER_CTRL */
59 #include <vlc_network.h> /* net_*, htonl */
60 #include <gcrypt.h> /* to encrypt password */
61 #include <vlc_gcrypt.h>
63 #include "remoteosd_rfbproto.h" /* type definitions of the RFB protocol for VNC */
65 /*****************************************************************************
67 *****************************************************************************/
68 #define READ_BUFFER_SIZE 1000000
70 #define RMTOSD_HOST_TEXT N_("VNC Host")
71 #define RMTOSD_HOST_LONGTEXT N_( \
72 "VNC hostname or IP address." )
74 #define RMTOSD_PORT_TEXT N_("VNC Port")
75 #define RMTOSD_PORT_LONGTEXT N_( \
78 #define RMTOSD_PASSWORD_TEXT N_("VNC Password")
79 #define RMTOSD_PASSWORD_LONGTEXT N_( \
82 #define RMTOSD_UPDATE_TEXT N_("VNC poll interval" )
83 #define RMTOSD_UPDATE_LONGTEXT N_( \
84 "In this interval an update from VNC is requested, default every 300 ms. ")
86 #define RMTOSD_POLL_TEXT N_("VNC polling")
87 #define RMTOSD_POLL_LONGTEXT N_( \
88 "Activate VNC polling. Do NOT activate for use as VDR ffnetdev client." )
90 #define RMTOSD_MOUSE_TEXT N_("Mouse events")
91 #define RMTOSD_MOUSE_LONGTEXT N_( \
92 "Send mouse events to VNC host. Not needed for use as VDR ffnetdev client." )
94 #define RMTOSD_KEYS_TEXT N_("Key events")
95 #define RMTOSD_KEYS_LONGTEXT N_( \
96 "Send key events to VNC host." )
98 #define RMTOSD_ALPHA_TEXT N_("Alpha transparency value (default 255)")
99 #define RMTOSD_ALPHA_LONGTEXT N_( \
100 "The transparency of the OSD VNC can be changed by giving a value " \
101 "between 0 and 255. A lower value specifies more transparency a higher " \
102 "means less transparency. The default is being not transparent " \
103 "(value 255) the minimum is fully transparent (value 0)." )
105 #define RMTOSD_CFG "rmtosd-"
107 #define RMTOSD_UPDATE_MIN 200
108 #define RMTOSD_UPDATE_DEFAULT 1000
109 #define RMTOSD_UPDATE_MAX 300
111 static int CreateFilter ( vlc_object_t * );
112 static void DestroyFilter( vlc_object_t * );
115 set_description( N_("Remote-OSD over VNC") )
116 set_capability( "sub source", 100 )
117 set_shortname( N_("Remote-OSD") )
118 set_category( CAT_VIDEO )
119 set_subcategory( SUBCAT_VIDEO_SUBPIC )
120 add_shortcut( "rmtosd" )
121 set_callbacks( CreateFilter, DestroyFilter )
123 add_string( RMTOSD_CFG "host", "myvdr", RMTOSD_HOST_TEXT,
124 RMTOSD_HOST_LONGTEXT, false )
125 add_integer_with_range( RMTOSD_CFG "port", 20001, 1, 0xFFFF,
126 RMTOSD_PORT_TEXT, RMTOSD_PORT_LONGTEXT, false )
127 add_password( RMTOSD_CFG "password", "", RMTOSD_PASSWORD_TEXT,
128 RMTOSD_PASSWORD_LONGTEXT, false )
129 add_integer_with_range( RMTOSD_CFG "update", RMTOSD_UPDATE_DEFAULT,
130 RMTOSD_UPDATE_MIN, RMTOSD_UPDATE_MAX, RMTOSD_UPDATE_TEXT,
131 RMTOSD_UPDATE_LONGTEXT, true )
132 add_bool( RMTOSD_CFG "vnc-polling", false,
133 RMTOSD_POLL_TEXT , RMTOSD_POLL_LONGTEXT, false )
134 add_bool( RMTOSD_CFG "mouse-events", false,
135 RMTOSD_MOUSE_TEXT , RMTOSD_MOUSE_LONGTEXT, false )
136 add_bool( RMTOSD_CFG "key-events", false,
137 RMTOSD_KEYS_TEXT , RMTOSD_KEYS_LONGTEXT, false )
138 add_integer_with_range( RMTOSD_CFG "alpha", 255, 0, 255,
139 RMTOSD_ALPHA_TEXT, RMTOSD_ALPHA_LONGTEXT, true )
144 /*****************************************************************************
146 *****************************************************************************/
147 #define CHALLENGESIZE 16
148 #define MAX_VNC_SERVER_NAME_LENGTH 255
150 /* subsource functions */
151 static subpicture_t *Filter( filter_t *, mtime_t );
153 static int MouseEvent( filter_t *,
156 const video_format_t * );
158 static int KeyEvent( vlc_object_t *p_this, char const *psz_var,
159 vlc_value_t oldval, vlc_value_t newval, void *p_data );
161 static void stop_osdvnc ( filter_t *p_filter );
163 static void* vnc_worker_thread ( void * );
165 static void* update_request_thread( void * );
167 static bool open_vnc_connection ( filter_t *p_filter );
169 static bool handshaking ( filter_t *p_filter );
171 static bool process_server_message ( filter_t *p_filter,
172 rfbServerToClientMsg *msg );
174 static inline void rgb_to_yuv( uint8_t *y, uint8_t *u, uint8_t *v,
175 int r, int g, int b );
177 static inline bool fill_rect( filter_sys_t* p_sys,
178 uint16_t i_x, uint16_t i_y,
179 uint16_t i_w, uint16_t i_h,
181 static inline bool copy_rect( filter_sys_t* p_sys,
182 uint16_t i_x, uint16_t i_y,
183 uint16_t i_w, uint16_t i_h,
184 uint16_t i_sx, uint16_t i_sy );
187 static inline bool raw_line( filter_sys_t* p_sys,
188 uint16_t i_x, uint16_t i_y,
191 static void vnc_encrypt_bytes( unsigned char *bytes, char *passwd );
194 /*****************************************************************************
196 *****************************************************************************/
198 /*****************************************************************************
200 *****************************************************************************/
203 bool b_need_update; /* VNC picture is updated, do update the OSD*/
204 mtime_t i_vnc_poll_interval; /* Update the OSD menu every n ms */
206 uint8_t i_alpha; /* alpha transparency value */
208 char *psz_host; /* VNC host */
211 char *psz_passwd; /* VNC password */
213 bool b_vnc_poll; /* Activate VNC polling ? */
214 bool b_vnc_mouse_events; /* Send MouseEvents ? */
215 bool b_vnc_key_events; /* Send KeyEvents ? */
217 bool b_connection_active; /* Handshaking finished ? */
219 vlc_mutex_t lock; /* To lock for read/write on picture */
221 picture_t *p_pic; /* The picture with OSD data from VNC */
223 int i_socket; /* Socket used for VNC */
225 uint16_t i_vnc_width; /* The with of the VNC screen */
226 uint16_t i_vnc_height; /* The height of the VNC screen */
227 uint32_t i_vnc_pixels; /* The pixels of the VNC screen */
229 bool b_alpha_from_vnc; /* Special ffnetdev alpha feature enabled ? */
231 char read_buffer[READ_BUFFER_SIZE];
235 vlc_thread_t worker_thread;
237 uint8_t ar_color_table_yuv[256][4];
240 /*****************************************************************************
241 * CreateFilter: Create the filter and open the definition file
242 *****************************************************************************/
243 static int CreateFilter ( vlc_object_t *p_this )
245 filter_t *p_filter = (filter_t *)p_this;
246 filter_sys_t *p_sys = NULL;
248 msg_Dbg( p_filter, "Creating vnc osd filter..." );
250 p_filter->p_sys = p_sys = calloc( 1, sizeof(*p_sys) );
251 if( !p_filter->p_sys )
254 /* Populating struct */
255 vlc_mutex_init( &p_sys->lock );
256 p_sys->b_continue = true;
257 p_sys->i_socket = -1;
260 p_sys->psz_host = var_CreateGetString( p_this, RMTOSD_CFG "host" );
261 if( EMPTY_STR(p_sys->psz_host) )
263 msg_Err( p_filter, "unable to get vnc host" );
267 p_sys->psz_passwd = var_CreateGetString( p_this, RMTOSD_CFG "password" );
268 if( !p_sys->psz_passwd )
270 msg_Err( p_filter, "unable to get vnc password" );
274 p_sys->i_port = var_CreateGetIntegerCommand( p_this, RMTOSD_CFG "port" );
276 p_sys->i_alpha = var_CreateGetIntegerCommand( p_this, RMTOSD_CFG "alpha" );
278 /* in milliseconds, 0 disables polling, should not be lower than 100 */
279 p_sys->i_vnc_poll_interval = var_CreateGetIntegerCommand( p_this,
280 RMTOSD_CFG "update" );
281 if ( p_sys->i_vnc_poll_interval < 100)
283 p_sys->i_vnc_poll_interval = 100;
286 for ( int i = 0; i < 256; i++ )
288 p_sys->ar_color_table_yuv[i][0] = 255;
289 p_sys->ar_color_table_yuv[i][1] = 255;
290 p_sys->ar_color_table_yuv[i][2] = 255;
291 p_sys->ar_color_table_yuv[i][3] = 255;
294 p_sys->b_vnc_poll = var_CreateGetBoolCommand( p_this,
295 RMTOSD_CFG "vnc-polling" );
296 p_sys->b_vnc_mouse_events = var_CreateGetBoolCommand( p_this,
297 RMTOSD_CFG "mouse-events" );
298 p_sys->b_vnc_key_events = var_CreateGetBoolCommand( p_this,
299 RMTOSD_CFG "key-events" );
301 /* Keep track of OSD Events */
302 p_sys->b_need_update = false;
304 /* Attach subpicture source callback */
305 p_filter->pf_sub_source = Filter;
306 p_filter->pf_sub_mouse = MouseEvent;
308 var_AddCallback( p_filter->p_libvlc, "key-pressed", KeyEvent, p_this );
310 es_format_Init( &p_filter->fmt_out, SPU_ES, VLC_CODEC_SPU );
311 p_filter->fmt_out.i_priority = ES_PRIORITY_SELECTABLE_MIN;
315 /* create the vnc worker thread */
316 if( vlc_clone( &p_sys->worker_thread,
317 vnc_worker_thread, p_filter, VLC_THREAD_PRIORITY_LOW ) )
319 msg_Err( p_filter, "cannot spawn vnc message reader thread" );
323 msg_Dbg( p_filter, "osdvnc filter started" );
328 msg_Err( p_filter, "osdvnc filter discarded" );
330 stop_osdvnc( p_filter );
332 vlc_mutex_destroy( &p_sys->lock );
333 free( p_sys->psz_host );
334 free( p_sys->psz_passwd );
340 /*****************************************************************************
341 * DestroyFilter: Make a clean exit of this plugin
342 *****************************************************************************/
343 static void DestroyFilter( vlc_object_t *p_this )
345 filter_t *p_filter = (filter_t*)p_this;
346 filter_sys_t *p_sys = p_filter->p_sys;
348 msg_Dbg( p_filter, "DestroyFilter called." );
350 stop_osdvnc( p_filter );
352 var_DelCallback( p_filter->p_libvlc, "key-pressed", KeyEvent, p_this );
354 var_Destroy( p_this, RMTOSD_CFG "host" );
355 var_Destroy( p_this, RMTOSD_CFG "port" );
356 var_Destroy( p_this, RMTOSD_CFG "password" );
357 var_Destroy( p_this, RMTOSD_CFG "update" );
358 var_Destroy( p_this, RMTOSD_CFG "vnc-polling" );
359 var_Destroy( p_this, RMTOSD_CFG "mouse-events" );
360 var_Destroy( p_this, RMTOSD_CFG "key-events" );
361 var_Destroy( p_this, RMTOSD_CFG "alpha" );
363 vlc_mutex_destroy( &p_sys->lock );
364 free( p_sys->psz_host );
365 free( p_sys->psz_passwd );
369 static void stop_osdvnc ( filter_t *p_filter )
371 filter_sys_t *p_sys = p_filter->p_sys;
373 msg_Dbg( p_filter, "joining worker_thread" );
374 vlc_cancel( p_sys->worker_thread );
375 vlc_join( p_sys->worker_thread, NULL );
376 msg_Dbg( p_filter, "released worker_thread" );
378 msg_Dbg( p_filter, "osdvnc stopped" );
381 static bool read_exact( filter_t *p_filter,
386 return i_bytes == net_Read( p_filter, i_socket, NULL,
387 (unsigned char*)p_readbuf,
392 static bool write_exact( filter_t *p_filter,
397 return i_bytes == net_Write( p_filter, i_socket, NULL,
398 (unsigned char*)p_writebuf, i_bytes );
401 static bool open_vnc_connection ( filter_t *p_filter )
403 filter_sys_t *p_sys = p_filter->p_sys;
405 msg_Dbg( p_filter, "Open socket to vnc server on %s:%u.",
406 p_sys->psz_host, p_sys->i_port );
408 p_sys->i_socket = net_ConnectTCP( p_filter, p_sys->psz_host, p_sys->i_port );
410 if( p_sys->i_socket < 0 )
412 msg_Err( p_filter, "Could not open socket" );
416 msg_Dbg( p_filter, "socket is open." );
421 static bool handshaking ( filter_t *p_filter )
423 filter_sys_t *p_sys = p_filter->p_sys;
425 msg_Dbg( p_filter, "Reading protocol version" );
427 rfbProtocolVersionMsg pv;
428 if ( !read_exact( p_filter, p_sys->i_socket, pv,
429 sz_rfbProtocolVersionMsg ) )
431 msg_Err( p_filter, "Could not read version message" );
434 pv[sz_rfbProtocolVersionMsg] = '\0'; /* pv size is sz_rfbProtocolVersionMsg+1 */
436 msg_Dbg( p_filter, "Server version is %s", pv );
438 strncpy(pv, "RFB 003.003\n", sz_rfbProtocolVersionMsg);
440 if( !write_exact(p_filter, p_sys->i_socket, pv,
441 sz_rfbProtocolVersionMsg) )
443 msg_Err( p_filter, "Could not write version message" );
447 msg_Dbg( p_filter, "Reading authentication scheme" );
448 uint32_t i_authScheme;
449 if( !read_exact( p_filter, p_sys->i_socket, (char*)&i_authScheme, 4 ) )
451 msg_Err( p_filter, "Could not read authentication scheme" );
454 i_authScheme = htonl(i_authScheme);
456 msg_Dbg( p_filter, "Authentication scheme = %x", i_authScheme );
457 if ( i_authScheme == rfbConnFailed )
459 msg_Err( p_filter, "Connection rejected by server" );
462 if (i_authScheme == rfbVncAuth)
464 unsigned char challenge[CHALLENGESIZE];
465 if ( !read_exact( p_filter, p_sys->i_socket,
466 (char*)challenge, CHALLENGESIZE ) )
468 msg_Err( p_filter, "Could not read password challenge" );
472 vnc_encrypt_bytes( challenge, p_sys->psz_passwd );
474 if( !write_exact(p_filter, p_sys->i_socket,
475 (char*)challenge, CHALLENGESIZE ) )
477 msg_Err( p_filter, "Could not write password" );
480 uint32_t i_authResult;
481 if( !read_exact( p_filter, p_sys->i_socket, (char*)&i_authResult, 4 ) )
483 msg_Err( p_filter, "Could not read authentication result" );
486 i_authResult = htonl(i_authResult);
487 if (i_authResult != rfbVncAuthOK)
489 msg_Err( p_filter, "VNC authentication failed" );
494 msg_Dbg( p_filter, "Writing client init message" );
497 if( !write_exact( p_filter, p_sys->i_socket,
498 (char*)&ci, sz_rfbClientInitMsg ) )
500 msg_Err( p_filter, "Could not write client init message" );
504 msg_Dbg( p_filter, "Reading server init message" );
506 if( !read_exact( p_filter, p_sys->i_socket,
507 (char*)&si, sz_rfbServerInitMsg ) )
509 msg_Err( p_filter, "Could not read server init message" );
512 si.framebufferWidth = htons(si.framebufferWidth);
513 si.framebufferHeight = htons(si.framebufferHeight);
514 si.format.redMax = htons(si.format.redMax);
515 si.format.greenMax = htons(si.format.greenMax);
516 si.format.blueMax = htons(si.format.blueMax);
518 p_sys->i_vnc_width = si.framebufferWidth;
519 p_sys->i_vnc_height = si.framebufferHeight;
521 msg_Dbg( p_filter, "Servers preferred pixelformat: "
522 "%ux%u, R(%u),G(%u),B(%u), %u bit, depht=%u, %s",
524 si.framebufferHeight,
528 si.format.bitsPerPixel,
530 si.format.trueColour ? "TrueColor" : "Not-TrueColor");
532 uint32_t i_nameLength = htonl(si.nameLength);
533 if( i_nameLength > MAX_VNC_SERVER_NAME_LENGTH )
535 msg_Err( p_filter, "Server name too long" );
538 char s_ServerName[MAX_VNC_SERVER_NAME_LENGTH+1];
540 msg_Dbg( p_filter, "Reading server name with size = %u", i_nameLength );
541 if( !read_exact( p_filter, p_sys->i_socket, s_ServerName, i_nameLength ) )
543 msg_Err( p_filter, "Could not read server name" );
546 s_ServerName[i_nameLength] = '\0';
548 if( strcmp( s_ServerName, "VDR-OSD") == 0 )
550 msg_Dbg( p_filter, "Server is a VDR" );
551 p_sys->b_alpha_from_vnc = true;
555 msg_Dbg( p_filter, "Server is a normal VNC" );
556 p_sys->b_alpha_from_vnc = false;
560 msg_Dbg( p_filter, "Server init message read properly" );
561 msg_Dbg( p_filter, "Server name is %s", s_ServerName );
563 msg_Dbg( p_filter, "Writing SetPixelFormat message" );
565 rfbSetPixelFormatMsg sp;
566 sp.type = rfbSetPixelFormat;
567 sp.pad1 = sp.pad2 = 0;
568 sp.format.bitsPerPixel = 8;
569 sp.format.depth = 8 ;
570 sp.format.bigEndian = 1;
571 sp.format.trueColour = 0;
572 sp.format.redMax = htons(31);
573 sp.format.greenMax = htons(31);
574 sp.format.blueMax = htons(31);
575 sp.format.redShift = 10;
576 sp.format.greenShift = 5;
577 sp.format.blueShift = 0;
578 sp.format.pad1 = sp.format.pad2 = 0;
580 if( !write_exact( p_filter, p_sys->i_socket,
581 (char*)&sp, sz_rfbSetPixelFormatMsg) )
583 msg_Err( p_filter, "Could not write SetPixelFormat message" );
587 msg_Dbg( p_filter, "Writing SetEncodings message" );
589 rfbSetEncodingsMsg se;
590 se.type = rfbSetEncodings;
592 se.nEncodings = htons( p_sys->b_alpha_from_vnc ? 3 : 2 );
594 if( !write_exact( p_filter, p_sys->i_socket,
595 (char*)&se, sz_rfbSetEncodingsMsg) )
597 msg_Err( p_filter, "Could not write SetEncodings message begin" );
603 msg_Dbg( p_filter, "Writing SetEncodings rfbEncodingCopyRect" );
604 i_encoding = htonl(rfbEncodingCopyRect);
605 if( !write_exact( p_filter, p_sys->i_socket, (char*)&i_encoding, 4) )
607 msg_Err( p_filter, "Could not write encoding type rfbEncodingCopyRect." );
611 msg_Dbg( p_filter, "Writing SetEncodings rfbEncodingRRE" );
612 i_encoding = htonl(rfbEncodingRRE);
613 if( !write_exact(p_filter, p_sys->i_socket, (char*)&i_encoding, 4) )
615 msg_Err( p_filter, "Could not write encoding type rfbEncodingRRE." );
619 if( p_sys->b_alpha_from_vnc )
621 msg_Dbg( p_filter, "Writing SetEncodings rfbEncSpecialUseAlpha" );
622 i_encoding = 0x00F0FFFF; /* rfbEncSpecialUseAlpha is 0xFFFFF000
623 * before we swap it */
624 if( !write_exact(p_filter, p_sys->i_socket, (char*)&i_encoding, 4) )
626 msg_Err( p_filter, "Could not write encoding type rfbEncSpecialUseAlpha." );
634 static void* vnc_worker_thread( void *obj )
636 filter_t* p_filter = (filter_t*)obj;
637 filter_sys_t *p_sys = p_filter->p_sys;
638 vlc_thread_t update_request_thread_handle;
639 int canc = vlc_savecancel ();
641 msg_Dbg( p_filter, "VNC worker thread started" );
643 if( !open_vnc_connection ( p_filter ) )
645 msg_Err( p_filter, "Could not connect to vnc host" );
649 if( !handshaking ( p_filter ) )
651 msg_Err( p_filter, "Error occurred while handshaking vnc host" );
655 p_sys->b_connection_active = true; /* to enable sending key
656 * and mouse events to host */
658 /* Create an empty picture for VNC the data */
659 vlc_mutex_lock( &p_sys->lock );
660 p_sys->p_pic = picture_New( VLC_CODEC_YUVA,
661 p_sys->i_vnc_width, p_sys->i_vnc_height, 1, 1 );
664 vlc_mutex_unlock( &p_sys->lock );
667 p_sys->i_vnc_pixels = p_sys->i_vnc_width * p_sys->i_vnc_height;
669 vlc_mutex_unlock( &p_sys->lock );
671 /* create the update request thread */
672 if( vlc_clone( &update_request_thread_handle,
673 update_request_thread, p_filter,
674 VLC_THREAD_PRIORITY_LOW ) )
676 msg_Err( p_filter, "cannot spawn vnc update request thread" );
680 /* connection is initialized, now read and handle server messages */
681 vlc_restorecancel (canc);
684 rfbServerToClientMsg msg;
687 memset( &msg, 0, sizeof(msg) );
689 if( !read_exact(p_filter, p_sys->i_socket, (char*)&msg, 1 ) )
691 msg_Err( p_filter, "Error while waiting for next server message");
696 case rfbFramebufferUpdate:
697 i_msgSize = sz_rfbFramebufferUpdateMsg;
699 case rfbSetColourMapEntries:
700 i_msgSize = sz_rfbSetColourMapEntriesMsg;
703 i_msgSize = sz_rfbBellMsg;
705 case rfbServerCutText:
706 i_msgSize = sz_rfbServerCutTextMsg;
708 case rfbReSizeFrameBuffer:
709 i_msgSize = sz_rfbReSizeFrameBufferMsg;
713 msg_Err( p_filter, "Invalid message %u received", msg.type );
720 if( --i_msgSize > 0 )
722 if ( !read_exact( p_filter, p_sys->i_socket,
723 ((char*)&msg)+1, i_msgSize ) )
725 msg_Err( p_filter, "Error while reading message of type %u",
731 canc = vlc_savecancel ();
732 process_server_message( p_filter, &msg);
733 vlc_restorecancel (canc);
735 canc = vlc_savecancel ();
737 msg_Dbg( p_filter, "joining update_request_thread" );
738 vlc_cancel( update_request_thread_handle );
739 vlc_join( update_request_thread_handle, NULL );
740 msg_Dbg( p_filter, "released update_request_thread" );
744 vlc_mutex_lock( &p_sys->lock );
745 p_sys->b_connection_active = false;
747 if (p_sys->i_socket >= 0)
748 net_Close(p_sys->i_socket);
751 picture_Release( p_sys->p_pic );
753 /* It will hide the subtitle */
754 p_sys->b_continue = false;
755 p_sys->b_need_update = true;
756 vlc_mutex_unlock( &p_sys->lock );
758 msg_Dbg( p_filter, "VNC message reader thread ended" );
759 vlc_restorecancel (canc);
763 static void update_request_thread_cleanup( void *obj )
765 filter_t* p_filter = (filter_t*)obj;
767 p_filter->p_sys->b_continue = false;
770 static void* update_request_thread( void *obj )
772 filter_t* p_filter = (filter_t*)obj;
773 filter_sys_t *p_sys = p_filter->p_sys;
775 msg_Dbg( p_filter, "VNC update request thread started" );
777 rfbFramebufferUpdateRequestMsg udr;
778 udr.type = rfbFramebufferUpdateRequest;
782 udr.w = htons(p_sys->i_vnc_width);
783 udr.h = htons(p_sys->i_vnc_height);
786 vlc_cleanup_push( update_request_thread_cleanup, p_filter );
787 w = write_exact(p_filter, p_sys->i_socket, (char*)&udr,
788 sz_rfbFramebufferUpdateRequestMsg);
793 msg_Err( p_filter, "Could not write rfbFramebufferUpdateRequestMsg." );
794 update_request_thread_cleanup( p_filter );
800 if( p_sys->b_vnc_poll)
802 vlc_cleanup_push( update_request_thread_cleanup, p_filter );
805 msleep( p_sys->i_vnc_poll_interval * 1000 );
806 if( !write_exact(p_filter, p_sys->i_socket, (char*)&udr,
807 sz_rfbFramebufferUpdateRequestMsg))
809 msg_Err( p_filter, "Could not write rfbFramebufferUpdateRequestMsg." );
817 msg_Dbg( p_filter, "VNC polling disabled." );
820 msg_Dbg( p_filter, "VNC update request thread ended" );
824 static bool process_server_message ( filter_t *p_filter,
825 rfbServerToClientMsg *msg )
827 filter_sys_t *p_sys = p_filter->p_sys;
831 case rfbFramebufferUpdate:
833 msg->fu.nRects = htons(msg->fu.nRects);
834 rfbFramebufferUpdateRectHeader hdr;
836 for (int i_rect = 0; i_rect < msg->fu.nRects; i_rect++)
838 if (!read_exact(p_filter, p_sys->i_socket, (char*)&hdr,
839 sz_rfbFramebufferUpdateRectHeader ) )
841 msg_Err( p_filter, "Could not read FrameBufferUpdate header" );
844 hdr.r.x = htons(hdr.r.x);
845 hdr.r.y = htons(hdr.r.y);
846 hdr.r.w = htons(hdr.r.w);
847 hdr.r.h = htons(hdr.r.h);
848 hdr.encoding = htonl(hdr.encoding);
850 switch (hdr.encoding)
855 for (i_line = 0; i_line < hdr.r.h; i_line++)
857 if ( !read_exact( p_filter, p_sys->i_socket,
858 p_sys->read_buffer, hdr.r.w ) )
861 "Could not read FrameBufferUpdate line data" );
864 vlc_mutex_lock( &p_sys->lock );
865 if ( !raw_line( p_sys, hdr.r.x,
869 msg_Err( p_filter, "raw_line failed." );
870 vlc_mutex_unlock( &p_sys->lock );
873 vlc_mutex_unlock( &p_sys->lock );
878 case rfbEncodingCopyRect:
882 if ( !read_exact( p_filter, p_sys->i_socket,
886 msg_Err( p_filter, "Could not read rfbCopyRect" );
889 rect.srcX = htons( rect.srcX );
890 rect.srcY = htons( rect.srcY );
892 vlc_mutex_lock( &p_sys->lock );
893 if ( !copy_rect( p_sys,
896 rect.srcX, rect.srcY ) )
898 msg_Err( p_filter, "copy_rect failed." );
899 vlc_mutex_unlock( &p_sys->lock );
902 vlc_mutex_unlock( &p_sys->lock );
909 if ( !read_exact( p_filter, p_sys->i_socket,
913 msg_Err( p_filter, "Could not read rfbRREHeader" );
917 if ( !read_exact(p_filter, p_sys->i_socket,
918 (char*)&i_pixcolor, 1 ) )
920 msg_Err( p_filter, "Could not read RRE pixcolor" );
924 vlc_mutex_lock( &p_sys->lock );
925 if ( !fill_rect( p_sys,
930 msg_Err( p_filter, "main fill_rect failed." );
931 vlc_mutex_unlock( &p_sys->lock );
934 vlc_mutex_unlock( &p_sys->lock );
936 rrehdr.nSubrects = htonl(rrehdr.nSubrects);
938 int i_datasize = rrehdr.nSubrects *
939 ( sizeof(i_pixcolor) + sz_rfbRectangle ) ;
940 if ( i_datasize > READ_BUFFER_SIZE )
942 msg_Err( p_filter, "Buffer too small, "
943 "need %u bytes", i_datasize );
946 if ( !read_exact( p_filter, p_sys->i_socket,
947 p_sys->read_buffer, i_datasize ) )
950 "Could not read RRE subrect data" );
955 rfbRectangle* p_subrect;
957 vlc_mutex_lock( &p_sys->lock );
959 i_subrect < rrehdr.nSubrects; i_subrect++)
961 i_pixcolor = p_sys->read_buffer[i_offset];
962 i_offset += sizeof(i_pixcolor);
964 (rfbRectangle*)(p_sys->read_buffer + i_offset);
965 i_offset += sz_rfbRectangle;
967 if (!fill_rect( p_sys,
968 htons(p_subrect->x) + hdr.r.x,
969 htons(p_subrect->y) + hdr.r.y,
975 "subrect %u fill_rect failed.", i_subrect );
976 vlc_mutex_unlock( &p_sys->lock );
980 vlc_mutex_unlock( &p_sys->lock );
987 vlc_mutex_lock( &p_sys->lock );
988 p_sys->b_need_update = true;
989 vlc_mutex_unlock( &p_sys->lock );
993 case rfbSetColourMapEntries:
995 msg->scme.nColours = htons(msg->scme.nColours);
996 msg->scme.firstColour = htons(msg->scme.firstColour);
998 if ( p_sys->b_alpha_from_vnc )
1000 i_datasize = 2 * msg->scme.nColours * 4;
1004 i_datasize = 2 * msg->scme.nColours * 3;
1006 if ( i_datasize > READ_BUFFER_SIZE )
1008 msg_Err( p_filter, "Buffer too small, need %u bytes",
1013 if ( !read_exact( p_filter, p_sys->i_socket,
1014 p_sys->read_buffer, i_datasize ) )
1016 msg_Err( p_filter, "Could not read color map data" );
1020 uint8_t i_red, i_green, i_blue, i_alpha, i_color_index;
1021 uint16_t i_offset = 0;
1024 for (int i = 0; i < msg->scme.nColours; i++)
1026 i_color_index = i+msg->scme.firstColour;
1027 if ( p_sys->b_alpha_from_vnc )
1029 i_alpha = p_sys->read_buffer[i_offset];
1032 i_red = p_sys->read_buffer[i_offset];
1034 i_green = p_sys->read_buffer[i_offset];
1036 i_blue = p_sys->read_buffer[i_offset];
1038 rgb_to_yuv( &p_sys->ar_color_table_yuv[i_color_index][0],
1039 &p_sys->ar_color_table_yuv[i_color_index][1],
1040 &p_sys->ar_color_table_yuv[i_color_index][2],
1044 p_sys->ar_color_table_yuv[i][3] = i_alpha;
1050 msg_Err( p_filter, "rfbBell received" );
1053 case rfbServerCutText:
1054 msg->sct.length = htons(msg->sct.length);
1055 if ( msg->sct.length > READ_BUFFER_SIZE )
1057 msg_Err( p_filter, "Buffer too small, need %u bytes", msg->sct.length );
1060 if ( !read_exact(p_filter, p_sys->i_socket,
1061 p_sys->read_buffer, msg->sct.length ) )
1063 msg_Err( p_filter, "Could not read Reading rfbServerCutText data" );
1068 case rfbReSizeFrameBuffer:
1069 msg_Err( p_filter, "Reading rfbReSizeFrameBuffer not implemented" );
1073 msg_Err( p_filter, "Invalid message %u received", msg->type );
1079 /****************************************************************************
1080 * Filter: the whole thing
1081 ****************************************************************************
1082 * This function outputs subpictures at regular time intervals.
1083 ****************************************************************************/
1084 static subpicture_t *Filter( filter_t *p_filter, mtime_t date )
1086 filter_sys_t *p_sys = p_filter->p_sys;
1087 subpicture_t *p_spu;
1088 subpicture_region_t *p_region;
1092 if( !p_sys->b_need_update )
1097 vlc_mutex_lock( &p_sys->lock );
1099 p_pic = p_sys->p_pic;
1103 vlc_mutex_unlock( &p_sys->lock );
1107 /* Allocate the subpicture internal data. */
1108 p_spu = filter_NewSubpicture( p_filter );
1111 vlc_mutex_unlock( &p_sys->lock );
1115 p_spu->b_absolute = false;
1116 p_spu->i_start = date;
1118 p_spu->b_ephemer = true;
1120 if( !p_sys->b_continue )
1121 p_spu->i_stop = p_spu->i_start + 1;
1123 /* Create new SPU region */
1124 memset( &fmt, 0, sizeof(video_format_t) );
1125 fmt.i_chroma = VLC_CODEC_YUVA;
1126 fmt.i_sar_num = fmt.i_sar_den = 1;
1127 fmt.i_width = fmt.i_visible_width = p_pic->p[Y_PLANE].i_visible_pitch;
1128 fmt.i_height = fmt.i_visible_height = p_pic->p[Y_PLANE].i_visible_lines;
1129 fmt.i_x_offset = fmt.i_y_offset = 0;
1130 p_region = subpicture_region_New( &fmt );
1133 msg_Err( p_filter, "cannot allocate SPU region" );
1134 p_filter->pf_sub_buffer_del( p_filter, p_spu );
1135 vlc_mutex_unlock( &p_sys->lock );
1139 /* FIXME the copy is probably not needed anymore */
1140 picture_Copy( p_region->p_picture, p_pic );
1142 p_sys->b_need_update = false;
1144 vlc_mutex_unlock( &p_sys->lock );
1146 /* set to one of the 9 relative locations */
1147 p_region->i_align = 0; /* Center */
1148 p_spu->b_absolute = false;
1151 p_spu->i_original_picture_width = 0; /*Let vout core do the horizontal scaling */
1152 p_spu->i_original_picture_height = fmt.i_height;
1154 p_spu->p_region = p_region;
1156 p_spu->i_alpha = ( p_sys->i_alpha );
1162 static inline void rgb_to_yuv( uint8_t *y, uint8_t *u, uint8_t *v,
1163 int r, int g, int b )
1165 *y = ( ( ( 66 * r + 129 * g + 25 * b + 128 ) >> 8 ) + 16 );
1166 *u = ( ( -38 * r - 74 * g + 112 * b + 128 ) >> 8 ) + 128 ;
1167 *v = ( ( 112 * r - 94 * g - 18 * b + 128 ) >> 8 ) + 128 ;
1170 static inline bool fill_rect( filter_sys_t* p_sys,
1171 uint16_t i_x, uint16_t i_y,
1172 uint16_t i_w, uint16_t i_h,
1175 plane_t *p_outY = p_sys->p_pic->p+Y_PLANE;
1176 plane_t *p_outU = p_sys->p_pic->p+U_PLANE;
1177 plane_t *p_outV = p_sys->p_pic->p+V_PLANE;
1178 plane_t *p_outA = p_sys->p_pic->p+A_PLANE;
1179 int i_pitch = p_outY->i_pitch;
1180 int i_lines = p_outY->i_lines;
1181 if ( i_x + i_w > i_pitch)
1183 if ( i_y + i_h > i_lines)
1185 int i_line_offset = i_y * i_pitch;
1186 uint8_t i_yuv_y = p_sys->ar_color_table_yuv[i_color][0];
1187 uint8_t i_yuv_u = p_sys->ar_color_table_yuv[i_color][1];
1188 uint8_t i_yuv_v = p_sys->ar_color_table_yuv[i_color][2];
1189 uint8_t i_alpha = p_sys->ar_color_table_yuv[i_color][3];
1190 for( int i_line = 0; i_line < i_h; i_line++ )
1192 for( int i_column = 0; i_column < i_w; i_column++ )
1194 int i_total_offset = i_line_offset + i_x + i_column;
1195 p_outY->p_pixels[ i_total_offset ] = i_yuv_y;
1196 p_outU->p_pixels[ i_total_offset ] = i_yuv_u;
1197 p_outV->p_pixels[ i_total_offset ] = i_yuv_v;
1198 p_outA->p_pixels[ i_total_offset ] = i_alpha;
1200 i_line_offset += i_pitch;
1205 static inline bool copy_rect( filter_sys_t* p_sys,
1206 uint16_t i_x, uint16_t i_y,
1207 uint16_t i_w, uint16_t i_h,
1208 uint16_t i_sx, uint16_t i_sy )
1210 plane_t *p_Y = p_sys->p_pic->p+Y_PLANE;
1211 plane_t *p_U = p_sys->p_pic->p+U_PLANE;
1212 plane_t *p_V = p_sys->p_pic->p+V_PLANE;
1213 plane_t *p_A = p_sys->p_pic->p+A_PLANE;
1215 int i_pitch = p_Y->i_pitch;
1216 int i_lines = p_Y->i_lines;
1218 fprintf( stderr, "copy_rect: (%d,%d)+(%d,%d) -> (%d,%d)\n", i_x, i_y, i_w, i_h, i_sx, i_sy );
1220 if( i_x + i_w > i_pitch || i_sx + i_w > i_pitch )
1222 if( i_y + i_h > i_lines || i_sy + i_h > i_lines)
1225 if( i_w <= 0 || i_h <= 0 )
1228 uint8_t *pb_buffer = calloc( i_w * i_h, 4 );
1232 for( int i_line = 0; i_line < i_h; i_line++ )
1234 for( int i_column = 0; i_column < i_w; i_column++ )
1236 const int i_src_offset = ( i_sy + i_line ) * i_pitch + i_sx + i_column;
1237 const int i_tmp_offset = ( 0 + i_line ) * i_w + 0 + i_column;
1239 pb_buffer[4*i_tmp_offset + 0] = p_Y->p_pixels[i_src_offset];
1240 pb_buffer[4*i_tmp_offset + 1] = p_U->p_pixels[i_src_offset];
1241 pb_buffer[4*i_tmp_offset + 2] = p_V->p_pixels[i_src_offset];
1242 pb_buffer[4*i_tmp_offset + 3] = p_A->p_pixels[i_src_offset];
1246 for( int i_line = 0; i_line < i_h; i_line++ )
1248 for( int i_column = 0; i_column < i_w; i_column++ )
1250 const int i_tmp_offset = ( 0 + i_line ) * i_w + 0 + i_column;
1251 const int i_dst_offset = ( i_y + i_line ) * i_pitch + i_x + i_column;
1253 p_Y->p_pixels[i_dst_offset] = pb_buffer[4*i_tmp_offset + 0];
1254 p_U->p_pixels[i_dst_offset] = pb_buffer[4*i_tmp_offset + 1];
1255 p_V->p_pixels[i_dst_offset] = pb_buffer[4*i_tmp_offset + 2];
1256 p_A->p_pixels[i_dst_offset] = pb_buffer[4*i_tmp_offset + 3];
1264 static inline bool raw_line( filter_sys_t* p_sys,
1265 uint16_t i_x, uint16_t i_y,
1268 plane_t *p_outY = p_sys->p_pic->p+Y_PLANE;
1269 plane_t *p_outU = p_sys->p_pic->p+U_PLANE;
1270 plane_t *p_outV = p_sys->p_pic->p+V_PLANE;
1271 plane_t *p_outA = p_sys->p_pic->p+A_PLANE;
1272 int i_pitch = p_outY->i_pitch;
1273 int i_lines = p_outY->i_lines;
1274 if ( i_x + i_w > i_pitch)
1279 int i_line_offset = i_y * i_pitch + i_x;
1281 for( int i_column = 0; i_column < i_w; i_column++ )
1283 int i_offset = i_line_offset + i_column;
1284 uint8_t i_color = p_sys->read_buffer[i_column];
1285 p_outY->p_pixels[ i_offset ] = p_sys->ar_color_table_yuv[i_color][0];
1286 p_outU->p_pixels[ i_offset ] = p_sys->ar_color_table_yuv[i_color][1];
1287 p_outV->p_pixels[ i_offset ] = p_sys->ar_color_table_yuv[i_color][2];
1288 p_outA->p_pixels[ i_offset ] = p_sys->ar_color_table_yuv[i_color][3];
1295 /*****************************************************************************
1296 * MouseEvent: callback for mouse events
1297 *****************************************************************************/
1298 static int MouseEvent( filter_t *p_filter,
1299 const vlc_mouse_t *p_old,
1300 const vlc_mouse_t *p_new,
1301 const video_format_t *p_fmt )
1303 filter_sys_t *p_sys = p_filter->p_sys;
1306 if( !p_sys->b_vnc_mouse_events )
1309 int i_v = p_new->i_pressed;
1310 int i_x = p_new->i_x;
1311 int i_y = p_new->i_y;
1313 vlc_mutex_lock( &p_sys->lock );
1315 const int v_h = p_fmt->i_visible_height;
1316 const int v_w = p_sys->i_vnc_width * v_h / p_sys->i_vnc_height;
1317 const int v_x = (p_fmt->i_visible_width-v_w)/2;
1321 if( i_y < 0 || i_x < 0 || i_y >= v_h || i_x >= v_w )
1323 vlc_mutex_unlock( &p_sys->lock );
1324 msg_Dbg( p_filter, "invalid mouse event? x=%d y=%d btn=%x", i_x, i_y, i_v );
1327 if( !p_sys->b_connection_active )
1329 vlc_mutex_unlock( &p_sys->lock );
1334 msg_Dbg( p_filter, "mouse event x=%d y=%d btn=%x", i_x, i_y, i_v );
1338 i_x = i_x * p_sys->i_vnc_width / v_w;
1339 i_y = i_y * p_sys->i_vnc_height / v_h;
1341 /* buttonMask bits 0-7 are buttons 1-8, 0=up, 1=down */
1342 rfbPointerEventMsg ev;
1343 ev.type = rfbPointerEvent;
1344 ev.buttonMask = i_v;
1348 write_exact( p_filter, p_sys->i_socket,
1349 (char*)&ev, sz_rfbPointerEventMsg);
1351 vlc_mutex_unlock( &p_sys->lock );
1353 return VLC_EGENERIC;
1356 /*****************************************************************************
1357 * KeyEvent: callback for keyboard events
1358 *****************************************************************************/
1359 static int KeyEvent( vlc_object_t *p_this, char const *psz_var,
1360 vlc_value_t oldval, vlc_value_t newval, void *p_data )
1362 VLC_UNUSED(psz_var); VLC_UNUSED(oldval);
1364 filter_t *p_filter = (filter_t *)p_data;
1365 filter_sys_t *p_sys = p_filter->p_sys;
1367 if( !p_sys->b_vnc_key_events )
1370 msg_Dbg( p_this, "key pressed (%"PRId64") ", newval.i_int );
1372 if ( !newval.i_int )
1374 msg_Err( p_this, "Received invalid key event 0" );
1375 return VLC_EGENERIC;
1378 vlc_mutex_lock( &p_sys->lock );
1379 if( !p_sys->b_connection_active )
1381 vlc_mutex_unlock( &p_sys->lock );
1385 uint32_t i_key32 = newval.i_int;
1386 i_key32 = htonl(i_key32);
1389 ev.type = rfbKeyEvent;
1393 /* first key-down for modifier-keys */
1394 if (newval.i_int & KEY_MODIFIER_CTRL)
1397 write_exact( p_filter, p_sys->i_socket,
1398 (char*)&ev, sz_rfbKeyEventMsg);
1400 if (newval.i_int & KEY_MODIFIER_SHIFT)
1403 write_exact( p_filter, p_sys->i_socket,
1404 (char*)&ev, sz_rfbKeyEventMsg);
1406 if (newval.i_int & KEY_MODIFIER_ALT)
1409 write_exact( p_filter, p_sys->i_socket,
1410 (char*)&ev, sz_rfbKeyEventMsg);
1413 /* then key-down for the pressed key */
1415 write_exact( p_filter, p_sys->i_socket,
1416 (char*)&ev, sz_rfbKeyEventMsg);
1420 /* then key-up for the pressed key */
1421 write_exact( p_filter, p_sys->i_socket,
1422 (char*)&ev, sz_rfbKeyEventMsg);
1424 /* last key-down for modifier-keys */
1425 if (newval.i_int & KEY_MODIFIER_CTRL)
1428 write_exact( p_filter, p_sys->i_socket,
1429 (char*)&ev, sz_rfbKeyEventMsg);
1431 if (newval.i_int & KEY_MODIFIER_SHIFT)
1434 write_exact( p_filter, p_sys->i_socket,
1435 (char*)&ev, sz_rfbKeyEventMsg);
1437 if (newval.i_int & KEY_MODIFIER_ALT)
1440 write_exact( p_filter, p_sys->i_socket,
1441 (char*)&ev, sz_rfbKeyEventMsg);
1443 vlc_mutex_unlock( &p_sys->lock );
1448 static void vnc_encrypt_bytes( unsigned char *bytes, char *passwd )
1450 unsigned char key[8];
1453 for (i = 0; i < 8; i++)
1454 key[i] = i < strlen( passwd ) ? passwd[i] : '\0';
1456 gcry_cipher_hd_t ctx;
1457 gcry_cipher_open( &ctx, GCRY_CIPHER_DES, GCRY_CIPHER_MODE_ECB,0);
1459 /* reverse bits of the key */
1460 for( i = 0 ; i < 8 ; i ++ )
1463 (((key[i] >> 6) & 0x01 ) << 1 ) +
1464 (((key[i] >> 5) & 0x01 ) << 2 ) +
1465 (((key[i] >> 4) & 0x01 ) << 3 ) +
1466 (((key[i] >> 3) & 0x01 ) << 4 ) +
1467 (((key[i] >> 2) & 0x01 ) << 5 ) +
1468 (((key[i] >> 1) & 0x01 ) << 6 ) +
1469 ((key[i] & 0x01) << 7 );
1471 gcry_cipher_setkey( ctx, key, 8 );
1472 gcry_cipher_encrypt( ctx, bytes, CHALLENGESIZE, bytes, CHALLENGESIZE );
1473 gcry_cipher_close( ctx );