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
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 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 General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, 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>
57 #include "vlc_filter.h"
58 #include "filter_common.h"
59 #include "vlc_image.h"
63 #include <vlc_network.h>
64 #include <gcrypt.h> /* to encrypt password */
65 #include <vlc_gcrypt.h>
67 #include "remoteosd_rfbproto.h" /* type definitions of the RFB protocol for VNC */
69 /*****************************************************************************
71 *****************************************************************************/
72 #define READ_BUFFER_SIZE 1000000
74 #define RMTOSD_HOST_TEXT N_("VNC Host")
75 #define RMTOSD_HOST_LONGTEXT N_( \
76 "VNC hostname or IP address." )
78 #define RMTOSD_PORT_TEXT N_("VNC Port")
79 #define RMTOSD_PORT_LONGTEXT N_( \
82 #define RMTOSD_PASSWORD_TEXT N_("VNC Password")
83 #define RMTOSD_PASSWORD_LONGTEXT N_( \
86 #define RMTOSD_UPDATE_TEXT N_("VNC poll interval" )
87 #define RMTOSD_UPDATE_LONGTEXT N_( \
88 "In this interval an update from VNC is requested, default every 300 ms. ")
90 #define RMTOSD_POLL_TEXT N_("VNC polling")
91 #define RMTOSD_POLL_LONGTEXT N_( \
92 "Activate VNC polling. Do NOT activate for use as VDR ffnetdev client." )
94 #define RMTOSD_MOUSE_TEXT N_("Mouse events")
95 #define RMTOSD_MOUSE_LONGTEXT N_( \
96 "Send mouse events to VNC host. Not needed for use as VDR ffnetdev client." )
98 #define RMTOSD_KEYS_TEXT N_("Key events")
99 #define RMTOSD_KEYS_LONGTEXT N_( \
100 "Send key events to VNC host." )
102 #define RMTOSD_ALPHA_TEXT N_("Alpha transparency value (default 255)")
103 #define RMTOSD_ALPHA_LONGTEXT N_( \
104 "The transparency of the OSD VNC can be changed by giving a value " \
105 "between 0 and 255. A lower value specifies more transparency a higher " \
106 "means less transparency. The default is being not transparent " \
107 "(value 255) the minimum is fully transparent (value 0)." )
109 #define RMTOSD_CFG "rmtosd-"
111 #define RMTOSD_UPDATE_MIN 200
112 #define RMTOSD_UPDATE_DEFAULT 1000
113 #define RMTOSD_UPDATE_MAX 300
115 static int CreateFilter ( vlc_object_t * );
116 static void DestroyFilter( vlc_object_t * );
119 set_description( N_("Remote-OSD over VNC") );
120 set_capability( "sub filter", 100 );
121 set_shortname( N_("Remote-OSD") );
122 set_category( CAT_VIDEO );
123 set_subcategory( SUBCAT_VIDEO_SUBPIC );
124 add_shortcut( "rmtosd" );
125 set_callbacks( CreateFilter, DestroyFilter );
127 add_string( RMTOSD_CFG "host", "myvdr", NULL, RMTOSD_HOST_TEXT,
128 RMTOSD_HOST_LONGTEXT, false );
129 add_integer_with_range( RMTOSD_CFG "port", 20001, 1, 0xFFFF, NULL,
130 RMTOSD_PORT_TEXT, RMTOSD_PORT_LONGTEXT, false );
131 add_password( RMTOSD_CFG "password", "", NULL, RMTOSD_PASSWORD_TEXT,
132 RMTOSD_PASSWORD_LONGTEXT, false );
133 add_integer_with_range( RMTOSD_CFG "update", RMTOSD_UPDATE_DEFAULT,
134 RMTOSD_UPDATE_MIN, RMTOSD_UPDATE_MAX, NULL, RMTOSD_UPDATE_TEXT,
135 RMTOSD_UPDATE_LONGTEXT, true );
136 add_bool( RMTOSD_CFG "vnc-polling", 0, NULL,
137 RMTOSD_POLL_TEXT , RMTOSD_POLL_LONGTEXT, false );
138 add_bool( RMTOSD_CFG "mouse-events", 0, NULL,
139 RMTOSD_MOUSE_TEXT , RMTOSD_MOUSE_LONGTEXT, false );
140 add_bool( RMTOSD_CFG "key-events", 0, NULL,
141 RMTOSD_KEYS_TEXT , RMTOSD_KEYS_LONGTEXT, false );
142 add_integer_with_range( RMTOSD_CFG "alpha", 255, 0, 255, NULL,
143 RMTOSD_ALPHA_TEXT, RMTOSD_ALPHA_LONGTEXT, true );
148 /*****************************************************************************
150 *****************************************************************************/
151 #define CHALLENGESIZE 16
152 #define MAX_VNC_SERVER_NAME_LENGTH 255
154 /* subfilter functions */
155 static subpicture_t *Filter( filter_t *, mtime_t );
157 static int MouseEvent ( vlc_object_t *p_this, char const *psz_var,
158 vlc_value_t oldval, vlc_value_t newval, void *p_data );
160 static int KeyEvent( vlc_object_t *p_this, char const *psz_var,
161 vlc_value_t oldval, vlc_value_t newval, void *p_data );
163 static void stop_osdvnc ( filter_t *p_filter );
165 static void* vnc_worker_thread ( vlc_object_t *p_thread_obj );
167 static void* update_request_thread( vlc_object_t *p_thread_obj );
169 static bool open_vnc_connection ( filter_t *p_filter );
171 static bool handshaking ( filter_t *p_filter );
173 static bool process_server_message ( filter_t *p_filter,
174 rfbServerToClientMsg *msg );
176 static inline void rgb_to_yuv( uint8_t *y, uint8_t *u, uint8_t *v,
177 int r, int g, int b );
179 static inline bool fill_rect( filter_sys_t* p_sys,
180 uint16_t i_x, uint16_t i_y,
181 uint16_t i_w, uint16_t i_h,
183 static inline bool copy_rect( filter_sys_t* p_sys,
184 uint16_t i_x, uint16_t i_y,
185 uint16_t i_w, uint16_t i_h,
186 uint16_t i_sx, uint16_t i_sy );
189 static inline bool raw_line( filter_sys_t* p_sys,
190 uint16_t i_x, uint16_t i_y,
193 static void vnc_encrypt_bytes( unsigned char *bytes, char *passwd );
196 /*****************************************************************************
198 *****************************************************************************/
200 /*****************************************************************************
202 *****************************************************************************/
207 bool b_need_update; /* VNC picture is updated, do update the OSD*/
208 mtime_t i_vnc_poll_interval; /* Update the OSD menu every n ms */
210 uint8_t i_alpha; /* alpha transparency value */
212 char *psz_host; /* VNC host */
215 char *psz_passwd; /* VNC password */
217 bool b_vnc_poll; /* Activate VNC polling ? */
218 bool b_vnc_mouse_events; /* Send MouseEvents ? */
219 bool b_vnc_key_events; /* Send KeyEvents ? */
221 bool b_connection_active; /* Handshaking finished ? */
223 vlc_mutex_t lock; /* To lock for read/write on picture */
225 picture_t *p_pic; /* The picture with OSD data from VNC */
227 vout_thread_t *p_vout; /* Pointer to video-out thread */
229 int i_socket; /* Socket used for VNC */
231 uint16_t i_vnc_width; /* The with of the VNC screen */
232 uint16_t i_vnc_height; /* The height of the VNC screen */
233 uint32_t i_vnc_pixels; /* The pixels of the VNC screen */
235 bool b_alpha_from_vnc; /* Special ffnetdev alpha feature enabled ? */
237 char read_buffer[READ_BUFFER_SIZE];
241 vlc_object_t* p_worker_thread;
243 uint8_t ar_color_table_yuv[256][4];
246 /*****************************************************************************
247 * CreateFilter: Create the filter and open the definition file
248 *****************************************************************************/
249 static int CreateFilter ( vlc_object_t *p_this )
251 filter_t *p_filter = (filter_t *)p_this;
252 filter_sys_t *p_sys = NULL;
254 msg_Dbg( p_filter, "Creating vnc osd filter..." );
256 p_filter->p_sys = p_sys = malloc( sizeof(*p_sys) );
257 if( !p_filter->p_sys )
259 memset( p_sys, 0, sizeof(*p_sys) );
261 /* Populating struct */
262 vlc_mutex_init( &p_sys->lock );
263 p_sys->b_continue = true;
264 p_sys->i_socket = -1;
267 p_sys->psz_host = var_CreateGetString( p_this, RMTOSD_CFG "host" );
268 if( EMPTY_STR(p_sys->psz_host) )
270 msg_Err( p_filter, "unable to get vnc host" );
274 p_sys->psz_passwd = var_CreateGetString( p_this, RMTOSD_CFG "password" );
275 if( !p_sys->psz_passwd )
277 msg_Err( p_filter, "unable to get vnc password" );
281 p_sys->i_port = var_CreateGetIntegerCommand( p_this, RMTOSD_CFG "port" );
283 p_sys->i_alpha = var_CreateGetIntegerCommand( p_this, RMTOSD_CFG "alpha" );
285 /* in miliseconds, 0 disables polling, should not be lower than 100 */
286 p_sys->i_vnc_poll_interval = var_CreateGetIntegerCommand( p_this,
287 RMTOSD_CFG "update" );
288 if ( p_sys->i_vnc_poll_interval < 100)
290 p_sys->i_vnc_poll_interval = 100;
293 for ( int i = 0; i < 256; i++ )
295 p_sys->ar_color_table_yuv[i][0] = 255;
296 p_sys->ar_color_table_yuv[i][1] = 255;
297 p_sys->ar_color_table_yuv[i][2] = 255;
298 p_sys->ar_color_table_yuv[i][3] = 255;
301 p_sys->b_vnc_poll = var_CreateGetBoolCommand( p_this,
302 RMTOSD_CFG "vnc-polling" );
303 p_sys->b_vnc_mouse_events = var_CreateGetBoolCommand( p_this,
304 RMTOSD_CFG "mouse-events" );
305 p_sys->b_vnc_key_events = var_CreateGetBoolCommand( p_this,
306 RMTOSD_CFG "key-events" );
308 /* Keep track of OSD Events */
309 p_sys->b_need_update = false;
311 /* Attach subpicture filter callback */
312 p_filter->pf_sub_filter = Filter;
314 p_sys->p_vout = vlc_object_find( p_this, VLC_OBJECT_VOUT, FIND_PARENT );
318 var_AddCallback( p_sys->p_vout, "mouse-moved",
319 MouseEvent, p_this );
320 var_AddCallback( p_sys->p_vout, "mouse-button-down",
321 MouseEvent, p_this );
322 var_AddCallback( p_sys->p_vout->p_libvlc, "key-pressed",
326 es_format_Init( &p_filter->fmt_out, SPU_ES, VLC_FOURCC( 's','p','u',' ' ) );
327 p_filter->fmt_out.i_priority = 0;
331 /* create the vnc worker thread */
332 p_sys->p_worker_thread = vlc_object_create( p_this,
333 sizeof( vlc_object_t ) );
334 vlc_object_attach( p_sys->p_worker_thread, p_this );
335 if( vlc_thread_create( p_sys->p_worker_thread, "vnc worker thread",
337 VLC_THREAD_PRIORITY_LOW, false ) )
339 vlc_object_detach( p_sys->p_worker_thread );
340 vlc_object_release( p_sys->p_worker_thread );
341 msg_Err( p_filter, "cannot spawn vnc message reader thread" );
345 msg_Dbg( p_filter, "osdvnc filter started" );
350 msg_Err( p_filter, "osdvnc filter discarded" );
352 stop_osdvnc( p_filter );
354 vlc_mutex_destroy( &p_sys->lock );
355 free( p_sys->psz_host );
356 free( p_sys->psz_passwd );
362 /*****************************************************************************
363 * DestroyFilter: Make a clean exit of this plugin
364 *****************************************************************************/
365 static void DestroyFilter( vlc_object_t *p_this )
367 filter_t *p_filter = (filter_t*)p_this;
368 filter_sys_t *p_sys = p_filter->p_sys;
370 msg_Dbg( p_filter, "DestroyFilter called." );
372 stop_osdvnc( p_filter );
376 var_DelCallback( p_sys->p_vout, "mouse-moved",
377 MouseEvent, p_this );
378 var_DelCallback( p_sys->p_vout, "mouse-button-down",
379 MouseEvent, p_this );
380 var_DelCallback( p_sys->p_vout->p_libvlc, "key-pressed",
383 vlc_object_release( p_sys->p_vout );
386 var_Destroy( p_this, RMTOSD_CFG "host" );
387 var_Destroy( p_this, RMTOSD_CFG "port" );
388 var_Destroy( p_this, RMTOSD_CFG "password" );
389 var_Destroy( p_this, RMTOSD_CFG "update" );
390 var_Destroy( p_this, RMTOSD_CFG "vnc-polling" );
391 var_Destroy( p_this, RMTOSD_CFG "mouse-events" );
392 var_Destroy( p_this, RMTOSD_CFG "key-events" );
393 var_Destroy( p_this, RMTOSD_CFG "alpha" );
395 vlc_mutex_destroy( &p_sys->lock );
396 free( p_sys->psz_host );
397 free( p_sys->psz_passwd );
401 static void stop_osdvnc ( filter_t *p_filter )
403 filter_sys_t *p_sys = p_filter->p_sys;
405 /* It will unlock socket reading */
406 vlc_object_kill( p_filter );
409 if( p_sys->p_worker_thread )
411 msg_Dbg( p_filter, "joining worker_thread" );
412 vlc_object_kill( p_sys->p_worker_thread );
413 vlc_thread_join( p_sys->p_worker_thread );
414 vlc_object_detach( p_sys->p_worker_thread );
415 vlc_object_release( p_sys->p_worker_thread );
416 msg_Dbg( p_filter, "released worker_thread" );
419 msg_Dbg( p_filter, "osdvnc stopped" );
422 static bool read_exact( filter_t *p_filter,
427 return i_bytes == net_Read( p_filter, i_socket, NULL,
428 (unsigned char*)p_readbuf,
433 static bool write_exact( filter_t *p_filter,
438 return i_bytes == net_Write( p_filter, i_socket, NULL,
439 (unsigned char*)p_writebuf, i_bytes );
442 static bool open_vnc_connection ( filter_t *p_filter )
444 filter_sys_t *p_sys = p_filter->p_sys;
446 msg_Dbg( p_filter, "Open socket to vnc server on %s:%u.",
447 p_sys->psz_host, p_sys->i_port );
449 p_sys->i_socket = net_ConnectTCP( p_filter, p_sys->psz_host, p_sys->i_port );
451 if( p_sys->i_socket < 0 )
453 msg_Err( p_filter, "Could not open socket" );
457 msg_Dbg( p_filter, "socket is open." );
462 static bool handshaking ( filter_t *p_filter )
464 filter_sys_t *p_sys = p_filter->p_sys;
466 msg_Dbg( p_filter, "Reading protocol version" );
468 rfbProtocolVersionMsg pv;
469 if ( !read_exact( p_filter, p_sys->i_socket, pv,
470 sz_rfbProtocolVersionMsg ) )
472 msg_Err( p_filter, "Could not read version message" );
475 pv[sz_rfbProtocolVersionMsg] = '\0'; /* pv size is sz_rfbProtocolVersionMsg+1 */
477 msg_Dbg( p_filter, "Server version is %s", pv );
479 strncpy(pv, "RFB 003.003\n", sz_rfbProtocolVersionMsg);
481 if( !write_exact(p_filter, p_sys->i_socket, pv,
482 sz_rfbProtocolVersionMsg) )
484 msg_Err( p_filter, "Could not write version message" );
488 msg_Dbg( p_filter, "Reading authentication scheme" );
489 uint32_t i_authScheme;
490 if( !read_exact( p_filter, p_sys->i_socket, (char*)&i_authScheme, 4 ) )
492 msg_Err( p_filter, "Could not read authentication scheme" );
495 i_authScheme = htonl(i_authScheme);
497 msg_Dbg( p_filter, "Authentication scheme = %x", i_authScheme );
498 if ( i_authScheme == rfbConnFailed )
500 msg_Err( p_filter, "Connection rejected by server" );
503 if (i_authScheme == rfbVncAuth)
505 unsigned char challenge[CHALLENGESIZE];
506 if ( !read_exact( p_filter, p_sys->i_socket,
507 (char*)challenge, CHALLENGESIZE ) )
509 msg_Err( p_filter, "Could not read password challenge" );
513 vnc_encrypt_bytes( challenge, p_sys->psz_passwd );
515 if( !write_exact(p_filter, p_sys->i_socket,
516 (char*)challenge, CHALLENGESIZE ) )
518 msg_Err( p_filter, "Could not write password" );
521 uint32_t i_authResult;
522 if( !read_exact( p_filter, p_sys->i_socket, (char*)&i_authResult, 4 ) )
524 msg_Err( p_filter, "Could not read authentication result" );
527 i_authResult = htonl(i_authResult);
528 if (i_authResult != rfbVncAuthOK)
530 msg_Err( p_filter, "VNC authentication failed" );
535 msg_Dbg( p_filter, "Writing client init message" );
538 if( !write_exact( p_filter, p_sys->i_socket,
539 (char*)&ci, sz_rfbClientInitMsg ) )
541 msg_Err( p_filter, "Could not write client init message" );
545 msg_Dbg( p_filter, "Reading server init message" );
547 if( !read_exact( p_filter, p_sys->i_socket,
548 (char*)&si, sz_rfbServerInitMsg ) )
550 msg_Err( p_filter, "Could not read server init message" );
553 si.framebufferWidth = htons(si.framebufferWidth);
554 si.framebufferHeight = htons(si.framebufferHeight);
555 si.format.redMax = htons(si.format.redMax);
556 si.format.greenMax = htons(si.format.greenMax);
557 si.format.blueMax = htons(si.format.blueMax);
559 p_sys->i_vnc_width = si.framebufferWidth;
560 p_sys->i_vnc_height = si.framebufferHeight;
562 msg_Dbg( p_filter, "Servers preferred pixelformat: "
563 "%ux%u, R(%u),G(%u),B(%u), %u bit, depht=%u, %s",
565 si.framebufferHeight,
569 si.format.bitsPerPixel,
571 si.format.trueColour ? "TrueColor" : "Not-TrueColor");
573 uint32_t i_nameLength = htonl(si.nameLength);
574 if( i_nameLength > MAX_VNC_SERVER_NAME_LENGTH )
576 msg_Err( p_filter, "Server name too long" );
579 char s_ServerName[MAX_VNC_SERVER_NAME_LENGTH+1];
581 msg_Dbg( p_filter, "Reading server name with size = %u", i_nameLength );
582 if( !read_exact( p_filter, p_sys->i_socket, s_ServerName, i_nameLength ) )
584 msg_Err( p_filter, "Could not read server name" );
587 s_ServerName[i_nameLength] = '\0';
589 if( strcmp( s_ServerName, "VDR-OSD") == 0 )
591 msg_Dbg( p_filter, "Server is a VDR" );
592 p_sys->b_alpha_from_vnc = true;
596 msg_Dbg( p_filter, "Server is a normal VNC" );
597 p_sys->b_alpha_from_vnc = false;
601 msg_Dbg( p_filter, "Server init message read properly" );
602 msg_Dbg( p_filter, "Server name is %s", s_ServerName );
604 msg_Dbg( p_filter, "Writing SetPixelFormat message" );
606 rfbSetPixelFormatMsg sp;
607 sp.type = rfbSetPixelFormat;
608 sp.pad1 = sp.pad2 = 0;
609 sp.format.bitsPerPixel = 8;
610 sp.format.depth = 8 ;
611 sp.format.bigEndian = 1;
612 sp.format.trueColour = 0;
613 sp.format.redMax = htons(31);
614 sp.format.greenMax = htons(31);
615 sp.format.blueMax = htons(31);
616 sp.format.redShift = 10;
617 sp.format.greenShift = 5;
618 sp.format.blueShift = 0;
619 sp.format.pad1 = sp.format.pad2 = 0;
621 if( !write_exact( p_filter, p_sys->i_socket,
622 (char*)&sp, sz_rfbSetPixelFormatMsg) )
624 msg_Err( p_filter, "Could not write SetPixelFormat message" );
628 msg_Dbg( p_filter, "Writing SetEncodings message" );
630 rfbSetEncodingsMsg se;
631 se.type = rfbSetEncodings;
633 se.nEncodings = htons( p_sys->b_alpha_from_vnc ? 3 : 2 );
635 if( !write_exact( p_filter, p_sys->i_socket,
636 (char*)&se, sz_rfbSetEncodingsMsg) )
638 msg_Err( p_filter, "Could not write SetEncodings message begin" );
644 msg_Dbg( p_filter, "Writing SetEncodings rfbEncodingCopyRect" );
645 i_encoding = htonl(rfbEncodingCopyRect);
646 if( !write_exact( p_filter, p_sys->i_socket, (char*)&i_encoding, 4) )
648 msg_Err( p_filter, "Could not write encoding type rfbEncodingCopyRect." );
652 msg_Dbg( p_filter, "Writing SetEncodings rfbEncodingRRE" );
653 i_encoding = htonl(rfbEncodingRRE);
654 if( !write_exact(p_filter, p_sys->i_socket, (char*)&i_encoding, 4) )
656 msg_Err( p_filter, "Could not write encoding type rfbEncodingRRE." );
660 if( p_sys->b_alpha_from_vnc )
662 msg_Dbg( p_filter, "Writing SetEncodings rfbEncSpecialUseAlpha" );
663 i_encoding = 0x00F0FFFF; /* rfbEncSpecialUseAlpha is 0xFFFFF000
664 * before we swap it */
665 if( !write_exact(p_filter, p_sys->i_socket, (char*)&i_encoding, 4) )
667 msg_Err( p_filter, "Could not write encoding type rfbEncSpecialUseAlpha." );
675 static void* vnc_worker_thread( vlc_object_t *p_thread_obj )
677 filter_t* p_filter = (filter_t*)(p_thread_obj->p_parent);
678 filter_sys_t *p_sys = p_filter->p_sys;
679 vlc_object_t *p_update_request_thread;
680 int canc = vlc_savecancel ();
682 msg_Dbg( p_filter, "VNC worker thread started" );
684 if( !open_vnc_connection ( p_filter ) )
686 msg_Err( p_filter, "Could not connect to vnc host" );
690 if( !handshaking ( p_filter ) )
692 msg_Err( p_filter, "Error occured while handshaking vnc host" );
696 p_sys->b_connection_active = true; /* to enable sending key
697 * and mouse events to host */
699 /* Create an empty picture for VNC the data */
700 vlc_mutex_lock( &p_sys->lock );
701 p_sys->p_pic = picture_New( VLC_FOURCC('Y','U','V','A'),
702 p_sys->i_vnc_width, p_sys->i_vnc_height, VOUT_ASPECT_FACTOR );
705 vlc_mutex_unlock( &p_sys->lock );
708 p_sys->i_vnc_pixels = p_sys->i_vnc_width * p_sys->i_vnc_height;
710 vlc_mutex_unlock( &p_sys->lock );
712 /* create the update request thread */
713 p_update_request_thread = vlc_object_create( p_filter,
714 sizeof( vlc_object_t ) );
715 vlc_object_attach( p_update_request_thread, p_filter );
716 if( vlc_thread_create( p_update_request_thread,
717 "vnc update request thread",
718 update_request_thread,
719 VLC_THREAD_PRIORITY_LOW, false ) )
721 vlc_object_detach( p_update_request_thread );
722 vlc_object_release( p_update_request_thread );
723 msg_Err( p_filter, "cannot spawn vnc update request thread" );
727 /* connection is initialized, now read and handle server messages */
728 while( vlc_object_alive( p_thread_obj ) )
730 rfbServerToClientMsg msg;
733 memset( &msg, 0, sizeof(msg) );
735 if( !read_exact(p_filter, p_sys->i_socket, (char*)&msg, 1 ) )
737 msg_Err( p_filter, "Error while waiting for next server message");
742 case rfbFramebufferUpdate:
743 i_msgSize = sz_rfbFramebufferUpdateMsg;
745 case rfbSetColourMapEntries:
746 i_msgSize = sz_rfbSetColourMapEntriesMsg;
749 i_msgSize = sz_rfbBellMsg;
751 case rfbServerCutText:
752 i_msgSize = sz_rfbServerCutTextMsg;
754 case rfbReSizeFrameBuffer:
755 i_msgSize = sz_rfbReSizeFrameBufferMsg;
759 msg_Err( p_filter, "Invalid message %u received", msg.type );
766 if( --i_msgSize > 0 )
768 if ( !read_exact( p_filter, p_sys->i_socket,
769 ((char*)&msg)+1, i_msgSize ) )
771 msg_Err( p_filter, "Error while reading message of type %u",
776 process_server_message( p_filter, &msg);
779 msg_Dbg( p_filter, "joining update_request_thread" );
780 vlc_object_kill( p_update_request_thread );
781 vlc_thread_join( p_update_request_thread );
782 vlc_object_detach( p_update_request_thread );
783 vlc_object_release( p_update_request_thread );
784 msg_Dbg( p_filter, "released update_request_thread" );
788 vlc_mutex_lock( &p_sys->lock );
789 p_sys->b_connection_active = false;
791 if (p_sys->i_socket >= 0)
792 net_Close(p_sys->i_socket);
795 picture_Release( p_sys->p_pic );
797 /* It will hide the subtitle */
798 p_sys->b_continue = false;
799 p_sys->b_need_update = true;
800 vlc_mutex_unlock( &p_sys->lock );
802 msg_Dbg( p_filter, "VNC message reader thread ended" );
803 vlc_restorecancel (canc);
807 static void* update_request_thread( vlc_object_t *p_thread_obj )
809 filter_t* p_filter = (filter_t*)(p_thread_obj->p_parent);
810 filter_sys_t *p_sys = p_filter->p_sys;
811 int canc = vlc_savecancel ();
813 msg_Dbg( p_filter, "VNC update request thread started" );
815 rfbFramebufferUpdateRequestMsg udr;
816 udr.type = rfbFramebufferUpdateRequest;
820 udr.w = htons(p_sys->i_vnc_width);
821 udr.h = htons(p_sys->i_vnc_height);
823 if( write_exact(p_filter, p_sys->i_socket, (char*)&udr,
824 sz_rfbFramebufferUpdateRequestMsg) == false)
826 msg_Err( p_filter, "Could not write rfbFramebufferUpdateRequestMsg." );
827 p_sys->b_continue = false;
832 mtime_t i_poll_interval_microsec = p_sys->i_vnc_poll_interval * 1000;
834 if( p_sys->b_vnc_poll)
836 while( vlc_object_alive( p_thread_obj ) )
838 msleep( i_poll_interval_microsec );
839 if( write_exact(p_filter, p_sys->i_socket, (char*)&udr,
840 sz_rfbFramebufferUpdateRequestMsg) == false)
842 msg_Err( p_filter, "Could not write rfbFramebufferUpdateRequestMsg." );
846 p_sys->b_continue = false;
850 msg_Dbg( p_filter, "VNC polling disabled." );
853 msg_Dbg( p_filter, "VNC update request thread ended" );
854 vlc_restorecancel (canc);
858 static bool process_server_message ( filter_t *p_filter,
859 rfbServerToClientMsg *msg )
861 filter_sys_t *p_sys = p_filter->p_sys;
865 case rfbFramebufferUpdate:
867 msg->fu.nRects = htons(msg->fu.nRects);
868 rfbFramebufferUpdateRectHeader hdr;
870 for (int i_rect = 0; i_rect < msg->fu.nRects; i_rect++)
872 if (!read_exact(p_filter, p_sys->i_socket, (char*)&hdr,
873 sz_rfbFramebufferUpdateRectHeader ) )
875 msg_Err( p_filter, "Could not read FrameBufferUpdate header" );
878 hdr.r.x = htons(hdr.r.x);
879 hdr.r.y = htons(hdr.r.y);
880 hdr.r.w = htons(hdr.r.w);
881 hdr.r.h = htons(hdr.r.h);
882 hdr.encoding = htonl(hdr.encoding);
884 switch (hdr.encoding)
889 for (i_line = 0; i_line < hdr.r.h; i_line++)
891 if ( !read_exact( p_filter, p_sys->i_socket,
892 p_sys->read_buffer, hdr.r.w ) )
895 "Could not read FrameBufferUpdate line data" );
898 vlc_mutex_lock( &p_sys->lock );
899 if ( !raw_line( p_sys, hdr.r.x,
903 msg_Err( p_filter, "raw_line failed." );
904 vlc_mutex_unlock( &p_sys->lock );
907 vlc_mutex_unlock( &p_sys->lock );
912 case rfbEncodingCopyRect:
916 if ( !read_exact( p_filter, p_sys->i_socket,
920 msg_Err( p_filter, "Could not read rfbCopyRect" );
923 rect.srcX = htons( rect.srcX );
924 rect.srcY = htons( rect.srcY );
926 vlc_mutex_lock( &p_sys->lock );
927 if ( !copy_rect( p_sys,
930 rect.srcX, rect.srcY ) )
932 msg_Err( p_filter, "copy_rect failed." );
933 vlc_mutex_unlock( &p_sys->lock );
936 vlc_mutex_unlock( &p_sys->lock );
943 if ( !read_exact( p_filter, p_sys->i_socket,
947 msg_Err( p_filter, "Could not read rfbRREHeader" );
951 if ( !read_exact(p_filter, p_sys->i_socket,
952 (char*)&i_pixcolor, 1 ) )
954 msg_Err( p_filter, "Could not read RRE pixcolor" );
958 vlc_mutex_lock( &p_sys->lock );
959 if ( !fill_rect( p_sys,
964 msg_Err( p_filter, "main fill_rect failed." );
965 vlc_mutex_unlock( &p_sys->lock );
968 vlc_mutex_unlock( &p_sys->lock );
970 rrehdr.nSubrects = htonl(rrehdr.nSubrects);
972 int i_datasize = rrehdr.nSubrects *
973 ( sizeof(i_pixcolor) + sz_rfbRectangle ) ;
974 if ( i_datasize > READ_BUFFER_SIZE )
976 msg_Err( p_filter, "Buffer too small, "
977 "need %u bytes", i_datasize );
980 if ( !read_exact( p_filter, p_sys->i_socket,
981 p_sys->read_buffer, i_datasize ) )
984 "Could not read RRE subrect data" );
989 rfbRectangle* p_subrect;
991 vlc_mutex_lock( &p_sys->lock );
993 i_subrect < rrehdr.nSubrects; i_subrect++)
995 i_pixcolor = p_sys->read_buffer[i_offset];
996 i_offset += sizeof(i_pixcolor);
998 (rfbRectangle*)(p_sys->read_buffer + i_offset);
999 i_offset += sz_rfbRectangle;
1001 if (!fill_rect( p_sys,
1002 htons(p_subrect->x) + hdr.r.x,
1003 htons(p_subrect->y) + hdr.r.y,
1004 htons(p_subrect->w),
1005 htons(p_subrect->h),
1009 "subrect %u fill_rect failed.", i_subrect );
1010 vlc_mutex_unlock( &p_sys->lock );
1014 vlc_mutex_unlock( &p_sys->lock );
1021 vlc_mutex_lock( &p_sys->lock );
1022 p_sys->b_need_update = true;
1023 vlc_mutex_unlock( &p_sys->lock );
1027 case rfbSetColourMapEntries:
1029 msg->scme.nColours = htons(msg->scme.nColours);
1030 msg->scme.firstColour = htons(msg->scme.firstColour);
1032 if ( p_sys->b_alpha_from_vnc == true )
1034 i_datasize = 2 * msg->scme.nColours * 4;
1038 i_datasize = 2 * msg->scme.nColours * 3;
1040 if ( i_datasize > READ_BUFFER_SIZE )
1042 msg_Err( p_filter, "Buffer too small, need %u bytes",
1047 if ( !read_exact( p_filter, p_sys->i_socket,
1048 p_sys->read_buffer, i_datasize ) )
1050 msg_Err( p_filter, "Could not read color map data" );
1054 uint8_t i_red, i_green, i_blue, i_alpha, i_color_index;
1055 uint16_t i_offset = 0;
1058 for (int i = 0; i < msg->scme.nColours; i++)
1060 i_color_index = i+msg->scme.firstColour;
1061 if ( p_sys->b_alpha_from_vnc == true )
1063 i_alpha = p_sys->read_buffer[i_offset];
1066 i_red = p_sys->read_buffer[i_offset];
1068 i_green = p_sys->read_buffer[i_offset];
1070 i_blue = p_sys->read_buffer[i_offset];
1072 rgb_to_yuv( &p_sys->ar_color_table_yuv[i_color_index][0],
1073 &p_sys->ar_color_table_yuv[i_color_index][1],
1074 &p_sys->ar_color_table_yuv[i_color_index][2],
1078 p_sys->ar_color_table_yuv[i][3] = i_alpha;
1084 msg_Err( p_filter, "rfbBell received" );
1087 case rfbServerCutText:
1088 msg->sct.length = htons(msg->sct.length);
1089 if ( msg->sct.length > READ_BUFFER_SIZE )
1091 msg_Err( p_filter, "Buffer too small, need %u bytes", msg->sct.length );
1094 if ( !read_exact(p_filter, p_sys->i_socket,
1095 p_sys->read_buffer, msg->sct.length ) )
1097 msg_Err( p_filter, "Could not read Reading rfbServerCutText data" );
1102 case rfbReSizeFrameBuffer:
1103 msg_Err( p_filter, "Reading rfbReSizeFrameBuffer not implemented" );
1107 msg_Err( p_filter, "Invalid message %u received", msg->type );
1113 /****************************************************************************
1114 * Filter: the whole thing
1115 ****************************************************************************
1116 * This function outputs subpictures at regular time intervals.
1117 ****************************************************************************/
1118 static subpicture_t *Filter( filter_t *p_filter, mtime_t date )
1120 filter_sys_t *p_sys = p_filter->p_sys;
1121 subpicture_t *p_spu;
1122 subpicture_region_t *p_region;
1126 if( !p_sys->b_need_update )
1131 vlc_mutex_lock( &p_sys->lock );
1133 p_pic = p_sys->p_pic;
1137 vlc_mutex_unlock( &p_sys->lock );
1141 /* Allocate the subpicture internal data. */
1142 p_spu = filter_NewSubpicture( p_filter );
1145 vlc_mutex_unlock( &p_sys->lock );
1149 p_spu->b_absolute = false;
1150 p_spu->i_start = date;
1152 p_spu->b_ephemer = true;
1154 if( !p_sys->b_continue )
1155 p_spu->i_stop = p_spu->i_start + 1;
1157 /* Create new SPU region */
1158 memset( &fmt, 0, sizeof(video_format_t) );
1159 fmt.i_chroma = VLC_FOURCC('Y','U','V','A');
1160 fmt.i_aspect = VOUT_ASPECT_FACTOR;
1161 fmt.i_sar_num = fmt.i_sar_den = 1;
1162 fmt.i_width = fmt.i_visible_width = p_pic->p[Y_PLANE].i_visible_pitch;
1163 fmt.i_height = fmt.i_visible_height = p_pic->p[Y_PLANE].i_visible_lines;
1164 fmt.i_x_offset = fmt.i_y_offset = 0;
1165 p_region = p_spu->pf_create_region( VLC_OBJECT(p_filter), &fmt );
1168 msg_Err( p_filter, "cannot allocate SPU region" );
1169 p_filter->pf_sub_buffer_del( p_filter, p_spu );
1170 vlc_mutex_unlock( &p_sys->lock );
1174 vout_CopyPicture( p_filter, &p_region->picture, p_pic );
1176 p_sys->b_need_update = false;
1178 vlc_mutex_unlock( &p_sys->lock );
1180 /* set to one of the 9 relative locations */
1181 p_region->i_align = 0; /* Center */
1182 p_spu->b_absolute = false;
1187 p_spu->i_original_picture_width = 0; /*Let vout core do the horizontal scaling */
1188 p_spu->i_original_picture_height = fmt.i_height;
1190 p_spu->p_region = p_region;
1192 p_spu->i_alpha = ( p_sys->i_alpha );
1198 static inline void rgb_to_yuv( uint8_t *y, uint8_t *u, uint8_t *v,
1199 int r, int g, int b )
1201 *y = ( ( ( 66 * r + 129 * g + 25 * b + 128 ) >> 8 ) + 16 );
1202 *u = ( ( -38 * r - 74 * g + 112 * b + 128 ) >> 8 ) + 128 ;
1203 *v = ( ( 112 * r - 94 * g - 18 * b + 128 ) >> 8 ) + 128 ;
1206 static inline bool fill_rect( filter_sys_t* p_sys,
1207 uint16_t i_x, uint16_t i_y,
1208 uint16_t i_w, uint16_t i_h,
1211 plane_t *p_outY = p_sys->p_pic->p+Y_PLANE;
1212 plane_t *p_outU = p_sys->p_pic->p+U_PLANE;
1213 plane_t *p_outV = p_sys->p_pic->p+V_PLANE;
1214 plane_t *p_outA = p_sys->p_pic->p+A_PLANE;
1215 int i_pitch = p_outY->i_pitch;
1216 int i_lines = p_outY->i_lines;
1217 if ( i_x + i_w > i_pitch)
1219 if ( i_y + i_h > i_lines)
1221 int i_line_offset = i_y * i_pitch;
1222 uint8_t i_yuv_y = p_sys->ar_color_table_yuv[i_color][0];
1223 uint8_t i_yuv_u = p_sys->ar_color_table_yuv[i_color][1];
1224 uint8_t i_yuv_v = p_sys->ar_color_table_yuv[i_color][2];
1225 uint8_t i_alpha = p_sys->ar_color_table_yuv[i_color][3];
1226 for( int i_line = 0; i_line < i_h; i_line++ )
1228 for( int i_column = 0; i_column < i_w; i_column++ )
1230 int i_total_offset = i_line_offset + i_x + i_column;
1231 p_outY->p_pixels[ i_total_offset ] = i_yuv_y;
1232 p_outU->p_pixels[ i_total_offset ] = i_yuv_u;
1233 p_outV->p_pixels[ i_total_offset ] = i_yuv_v;
1234 p_outA->p_pixels[ i_total_offset ] = i_alpha;
1236 i_line_offset += i_pitch;
1241 static inline bool copy_rect( filter_sys_t* p_sys,
1242 uint16_t i_x, uint16_t i_y,
1243 uint16_t i_w, uint16_t i_h,
1244 uint16_t i_sx, uint16_t i_sy )
1246 plane_t *p_Y = p_sys->p_pic->p+Y_PLANE;
1247 plane_t *p_U = p_sys->p_pic->p+U_PLANE;
1248 plane_t *p_V = p_sys->p_pic->p+V_PLANE;
1249 plane_t *p_A = p_sys->p_pic->p+A_PLANE;
1251 int i_pitch = p_Y->i_pitch;
1252 int i_lines = p_Y->i_lines;
1254 fprintf( stderr, "copy_rect: (%d,%d)+(%d,%d) -> (%d,%d)\n", i_x, i_y, i_w, i_h, i_sx, i_sy );
1256 if( i_x + i_w > i_pitch || i_sx + i_w > i_pitch )
1258 if( i_y + i_h > i_lines || i_sy + i_h > i_lines)
1261 if( i_w <= 0 || i_h <= 0 )
1264 uint8_t *pb_buffer = calloc( i_w * i_h, 4 );
1268 for( int i_line = 0; i_line < i_h; i_line++ )
1270 for( int i_column = 0; i_column < i_w; i_column++ )
1272 const int i_src_offset = ( i_sy + i_line ) * i_pitch + i_sx + i_column;
1273 const int i_tmp_offset = ( 0 + i_line ) * i_w + 0 + i_column;
1275 pb_buffer[4*i_tmp_offset + 0] = p_Y->p_pixels[i_src_offset];
1276 pb_buffer[4*i_tmp_offset + 1] = p_U->p_pixels[i_src_offset];
1277 pb_buffer[4*i_tmp_offset + 2] = p_V->p_pixels[i_src_offset];
1278 pb_buffer[4*i_tmp_offset + 3] = p_A->p_pixels[i_src_offset];
1282 for( int i_line = 0; i_line < i_h; i_line++ )
1284 for( int i_column = 0; i_column < i_w; i_column++ )
1286 const int i_tmp_offset = ( 0 + i_line ) * i_w + 0 + i_column;
1287 const int i_dst_offset = ( i_y + i_line ) * i_pitch + i_x + i_column;
1289 p_Y->p_pixels[i_dst_offset] = pb_buffer[4*i_tmp_offset + 0];
1290 p_U->p_pixels[i_dst_offset] = pb_buffer[4*i_tmp_offset + 1];
1291 p_V->p_pixels[i_dst_offset] = pb_buffer[4*i_tmp_offset + 2];
1292 p_A->p_pixels[i_dst_offset] = pb_buffer[4*i_tmp_offset + 3];
1300 static inline bool raw_line( filter_sys_t* p_sys,
1301 uint16_t i_x, uint16_t i_y,
1304 plane_t *p_outY = p_sys->p_pic->p+Y_PLANE;
1305 plane_t *p_outU = p_sys->p_pic->p+U_PLANE;
1306 plane_t *p_outV = p_sys->p_pic->p+V_PLANE;
1307 plane_t *p_outA = p_sys->p_pic->p+A_PLANE;
1308 int i_pitch = p_outY->i_pitch;
1309 int i_lines = p_outY->i_lines;
1310 if ( i_x + i_w > i_pitch)
1315 int i_line_offset = i_y * i_pitch + i_x;
1317 for( int i_column = 0; i_column < i_w; i_column++ )
1319 int i_offset = i_line_offset + i_column;
1320 uint8_t i_color = p_sys->read_buffer[i_column];
1321 p_outY->p_pixels[ i_offset ] = p_sys->ar_color_table_yuv[i_color][0];
1322 p_outU->p_pixels[ i_offset ] = p_sys->ar_color_table_yuv[i_color][1];
1323 p_outV->p_pixels[ i_offset ] = p_sys->ar_color_table_yuv[i_color][2];
1324 p_outA->p_pixels[ i_offset ] = p_sys->ar_color_table_yuv[i_color][3];
1331 /*****************************************************************************
1332 * MouseEvent: callback for mouse events
1333 *****************************************************************************/
1334 static int MouseEvent( vlc_object_t *p_this, char const *psz_var,
1335 vlc_value_t oldval, vlc_value_t newval, void *p_data )
1337 VLC_UNUSED(oldval); VLC_UNUSED(newval); VLC_UNUSED(psz_var);
1339 filter_t *p_filter = (filter_t *)p_data;
1340 filter_sys_t *p_sys = p_filter->p_sys;
1342 if( !p_sys->b_vnc_mouse_events )
1345 vout_thread_t *p_vout = (vout_thread_t*)p_sys->p_vout;
1350 i_v = var_GetInteger( p_sys->p_vout, "mouse-button-down" );
1351 i_y = var_GetInteger( p_sys->p_vout, "mouse-y" );
1352 i_x = var_GetInteger( p_sys->p_vout, "mouse-x" );
1354 vlc_mutex_lock( &p_sys->lock );
1356 const int v_h = p_vout->fmt_in.i_visible_height;
1357 const int v_w = p_sys->i_vnc_width * v_h / p_sys->i_vnc_height;
1358 const int v_x = (p_vout->fmt_in.i_visible_width-v_w)/2;
1362 if( i_y < 0 || i_x < 0 || i_y >= v_h || i_x >= v_w )
1364 vlc_mutex_unlock( &p_sys->lock );
1365 msg_Dbg( p_this, "invalid mouse event? x=%d y=%d btn=%x", i_x, i_y, i_v );
1368 if( !p_sys->b_connection_active )
1370 vlc_mutex_unlock( &p_sys->lock );
1375 msg_Dbg( p_this, "mouse event x=%d y=%d btn=%x", i_x, i_y, i_v );
1379 i_x = i_x * p_sys->i_vnc_width / v_w;
1380 i_y = i_y * p_sys->i_vnc_height / v_h;
1382 /* buttonMask bits 0-7 are buttons 1-8, 0=up, 1=down */
1383 rfbPointerEventMsg ev;
1384 ev.type = rfbPointerEvent;
1385 ev.buttonMask = i_v;
1389 write_exact( p_filter, p_sys->i_socket,
1390 (char*)&ev, sz_rfbPointerEventMsg);
1392 vlc_mutex_unlock( &p_sys->lock );
1397 /*****************************************************************************
1398 * KeyEvent: callback for keyboard events
1399 *****************************************************************************/
1400 static int KeyEvent( vlc_object_t *p_this, char const *psz_var,
1401 vlc_value_t oldval, vlc_value_t newval, void *p_data )
1403 VLC_UNUSED(psz_var); VLC_UNUSED(oldval);
1405 filter_t *p_filter = (filter_t *)p_data;
1406 filter_sys_t *p_sys = p_filter->p_sys;
1408 if( !p_sys->b_vnc_key_events )
1411 msg_Dbg( p_this, "key pressed (%d) ", newval.i_int );
1413 if ( !newval.i_int )
1415 msg_Err( p_this, "Received invalid key event %d", newval.i_int );
1416 return VLC_EGENERIC;
1419 vlc_mutex_lock( &p_sys->lock );
1420 if( !p_sys->b_connection_active )
1422 vlc_mutex_unlock( &p_sys->lock );
1426 uint32_t i_key32 = newval.i_int;
1427 i_key32 = htonl(i_key32);
1430 ev.type = rfbKeyEvent;
1434 /* first key-down for modifier-keys */
1435 if (newval.i_int & KEY_MODIFIER_CTRL)
1438 write_exact( p_filter, p_sys->i_socket,
1439 (char*)&ev, sz_rfbKeyEventMsg);
1441 if (newval.i_int & KEY_MODIFIER_SHIFT)
1444 write_exact( p_filter, p_sys->i_socket,
1445 (char*)&ev, sz_rfbKeyEventMsg);
1447 if (newval.i_int & KEY_MODIFIER_ALT)
1450 write_exact( p_filter, p_sys->i_socket,
1451 (char*)&ev, sz_rfbKeyEventMsg);
1454 /* then key-down for the pressed key */
1456 write_exact( p_filter, p_sys->i_socket,
1457 (char*)&ev, sz_rfbKeyEventMsg);
1461 /* then key-up for the pressed key */
1462 write_exact( p_filter, p_sys->i_socket,
1463 (char*)&ev, sz_rfbKeyEventMsg);
1465 /* last key-down for modifier-keys */
1466 if (newval.i_int & KEY_MODIFIER_CTRL)
1469 write_exact( p_filter, p_sys->i_socket,
1470 (char*)&ev, sz_rfbKeyEventMsg);
1472 if (newval.i_int & KEY_MODIFIER_SHIFT)
1475 write_exact( p_filter, p_sys->i_socket,
1476 (char*)&ev, sz_rfbKeyEventMsg);
1478 if (newval.i_int & KEY_MODIFIER_ALT)
1481 write_exact( p_filter, p_sys->i_socket,
1482 (char*)&ev, sz_rfbKeyEventMsg);
1484 vlc_mutex_unlock( &p_sys->lock );
1489 static void vnc_encrypt_bytes( unsigned char *bytes, char *passwd )
1491 unsigned char key[8];
1494 for (i = 0; i < 8; i++)
1495 key[i] = i < strlen( passwd ) ? passwd[i] : '\0';
1497 gcry_cipher_hd_t ctx;
1498 gcry_cipher_open( &ctx, GCRY_CIPHER_DES, GCRY_CIPHER_MODE_ECB,0);
1500 /* reverse bits of the key */
1501 for( i = 0 ; i < 8 ; i ++ )
1504 (((key[i] >> 6) & 0x01 ) << 1 ) +
1505 (((key[i] >> 5) & 0x01 ) << 2 ) +
1506 (((key[i] >> 4) & 0x01 ) << 3 ) +
1507 (((key[i] >> 3) & 0x01 ) << 4 ) +
1508 (((key[i] >> 2) & 0x01 ) << 5 ) +
1509 (((key[i] >> 1) & 0x01 ) << 6 ) +
1510 ((key[i] & 0x01) << 7 );
1512 gcry_cipher_setkey( ctx, key, 8 );
1513 gcry_cipher_encrypt( ctx, bytes, CHALLENGESIZE, bytes, CHALLENGESIZE );
1514 gcry_cipher_close( ctx );