You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1340 lines
44 KiB
1340 lines
44 KiB
/*****************************************************************************
|
|
* remoteosd.c: remote osd over vnc filter module
|
|
*****************************************************************************
|
|
* Copyright (C) 2007-2008 Matthias Bauer
|
|
* $Id$
|
|
*
|
|
* Authors: Matthias Bauer <matthias dot bauer #_at_# gmx dot ch>
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify it
|
|
* under the terms of the GNU Lesser General Public License as published by
|
|
* the Free Software Foundation; either version 2.1 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implid warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public License
|
|
* along with this program; if not, write to the Free Software Foundation,
|
|
* Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
|
|
*****************************************************************************/
|
|
|
|
/*****************************************************************************
|
|
* RemoteOSD uses the RFB-Protocol of VNC to display an On-Screen-Display
|
|
* menu generated by a streaming server as overlay for the streamed video.
|
|
*
|
|
* The streaming server that implements this is the ffnetdev plugin for VDR.
|
|
* VDR (VideoDiskRecorder) is an Linux based OpenSource harddisk recorder
|
|
* software.
|
|
* The VDR ffnetdev plugin emulates the hardware MPEG decoder and streams the
|
|
* video over the network instead of hardware video outputs.
|
|
* The OSD menu of VDR is offered with the RFB protocol to a VNC client.
|
|
*
|
|
* In fact this video-filter is a simple VNC client that could be also used to
|
|
* connect to a real VNC host.
|
|
* Only 8-bit color is supported at the moment.
|
|
* Using password protected VNC hosts is supported but not recommended, because
|
|
* you need to insert the used password in the plugin configuration page of
|
|
* VLC configuration in plain text and it's saved in plain text.
|
|
*****************************************************************************/
|
|
|
|
//#define VNC_DEBUG
|
|
|
|
/*****************************************************************************
|
|
* Preamble
|
|
*****************************************************************************/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
# include "config.h"
|
|
#endif
|
|
|
|
#include <vlc_common.h>
|
|
#include <vlc_plugin.h>
|
|
#include <vlc_filter.h>
|
|
#include <vlc_mouse.h>
|
|
#include <vlc_subpicture.h>
|
|
#include <vlc_keys.h> /* KEY_MODIFIER_CTRL */
|
|
#include <vlc_network.h> /* net_*, htonl */
|
|
#include <gcrypt.h> /* to encrypt password */
|
|
#include <vlc_gcrypt.h>
|
|
|
|
#include "remoteosd_rfbproto.h" /* type definitions of the RFB protocol for VNC */
|
|
|
|
/*****************************************************************************
|
|
* Module descriptor
|
|
*****************************************************************************/
|
|
#define READ_BUFFER_SIZE 1000000
|
|
|
|
#define RMTOSD_HOST_TEXT N_("VNC Host")
|
|
#define RMTOSD_HOST_LONGTEXT N_( \
|
|
"VNC hostname or IP address." )
|
|
|
|
#define RMTOSD_PORT_TEXT N_("VNC Port")
|
|
#define RMTOSD_PORT_LONGTEXT N_( \
|
|
"VNC port number." )
|
|
|
|
#define RMTOSD_PASSWORD_TEXT N_("VNC Password")
|
|
#define RMTOSD_PASSWORD_LONGTEXT N_( \
|
|
"VNC password." )
|
|
|
|
#define RMTOSD_UPDATE_TEXT N_("VNC poll interval" )
|
|
#define RMTOSD_UPDATE_LONGTEXT N_( \
|
|
"In this interval an update from VNC is requested, default every 300 ms. ")
|
|
|
|
#define RMTOSD_POLL_TEXT N_("VNC polling")
|
|
#define RMTOSD_POLL_LONGTEXT N_( \
|
|
"Activate VNC polling. Do NOT activate for use as VDR ffnetdev client." )
|
|
|
|
#define RMTOSD_MOUSE_TEXT N_("Mouse events")
|
|
#define RMTOSD_MOUSE_LONGTEXT N_( \
|
|
"Send mouse events to VNC host. Not needed for use as VDR ffnetdev client." )
|
|
|
|
#define RMTOSD_KEYS_TEXT N_("Key events")
|
|
#define RMTOSD_KEYS_LONGTEXT N_( \
|
|
"Send key events to VNC host." )
|
|
|
|
#define RMTOSD_ALPHA_TEXT N_("Alpha transparency value (default 255)")
|
|
#define RMTOSD_ALPHA_LONGTEXT N_( \
|
|
"The transparency of the OSD VNC can be changed by giving a value " \
|
|
"between 0 and 255. A lower value specifies more transparency a higher " \
|
|
"means less transparency. The default is being not transparent " \
|
|
"(value 255) the minimum is fully transparent (value 0)." )
|
|
|
|
#define RMTOSD_CFG "rmtosd-"
|
|
|
|
#define RMTOSD_UPDATE_MIN 200
|
|
#define RMTOSD_UPDATE_DEFAULT 1000
|
|
#define RMTOSD_UPDATE_MAX 300
|
|
|
|
static int CreateFilter ( vlc_object_t * );
|
|
static void DestroyFilter( vlc_object_t * );
|
|
|
|
vlc_module_begin ()
|
|
set_description( N_("Remote-OSD over VNC") )
|
|
set_capability( "sub source", 100 )
|
|
set_shortname( N_("Remote-OSD") )
|
|
set_category( CAT_VIDEO )
|
|
set_subcategory( SUBCAT_VIDEO_SUBPIC )
|
|
add_shortcut( "rmtosd" )
|
|
set_callbacks( CreateFilter, DestroyFilter )
|
|
|
|
add_string( RMTOSD_CFG "host", "myvdr", RMTOSD_HOST_TEXT,
|
|
RMTOSD_HOST_LONGTEXT, false )
|
|
add_integer_with_range( RMTOSD_CFG "port", 20001, 1, 0xFFFF,
|
|
RMTOSD_PORT_TEXT, RMTOSD_PORT_LONGTEXT, false )
|
|
add_password( RMTOSD_CFG "password", "", RMTOSD_PASSWORD_TEXT,
|
|
RMTOSD_PASSWORD_LONGTEXT, false )
|
|
add_integer_with_range( RMTOSD_CFG "update", RMTOSD_UPDATE_DEFAULT,
|
|
RMTOSD_UPDATE_MIN, RMTOSD_UPDATE_MAX, RMTOSD_UPDATE_TEXT,
|
|
RMTOSD_UPDATE_LONGTEXT, true )
|
|
add_bool( RMTOSD_CFG "vnc-polling", false,
|
|
RMTOSD_POLL_TEXT , RMTOSD_POLL_LONGTEXT, false )
|
|
add_bool( RMTOSD_CFG "mouse-events", false,
|
|
RMTOSD_MOUSE_TEXT , RMTOSD_MOUSE_LONGTEXT, false )
|
|
add_bool( RMTOSD_CFG "key-events", false,
|
|
RMTOSD_KEYS_TEXT , RMTOSD_KEYS_LONGTEXT, false )
|
|
add_integer_with_range( RMTOSD_CFG "alpha", 255, 0, 255,
|
|
RMTOSD_ALPHA_TEXT, RMTOSD_ALPHA_LONGTEXT, true )
|
|
|
|
vlc_module_end ()
|
|
|
|
|
|
/*****************************************************************************
|
|
* Local prototypes
|
|
*****************************************************************************/
|
|
#define CHALLENGESIZE 16
|
|
#define MAX_VNC_SERVER_NAME_LENGTH 255
|
|
|
|
/* subsource functions */
|
|
static subpicture_t *Filter( filter_t *, mtime_t );
|
|
|
|
static int MouseEvent( filter_t *,
|
|
const vlc_mouse_t *,
|
|
const vlc_mouse_t *,
|
|
const video_format_t * );
|
|
|
|
static int KeyEvent( vlc_object_t *p_this, char const *psz_var,
|
|
vlc_value_t oldval, vlc_value_t newval, void *p_data );
|
|
|
|
static void* vnc_worker_thread ( void * );
|
|
|
|
static void* update_request_thread( void * );
|
|
|
|
static bool process_server_message ( filter_t *p_filter,
|
|
rfbServerToClientMsg *msg );
|
|
|
|
static inline void rgb_to_yuv( uint8_t *y, uint8_t *u, uint8_t *v,
|
|
int r, int g, int b );
|
|
|
|
static inline bool fill_rect( filter_sys_t* p_sys,
|
|
uint16_t i_x, uint16_t i_y,
|
|
uint16_t i_w, uint16_t i_h,
|
|
uint8_t i_color );
|
|
static inline bool copy_rect( filter_sys_t* p_sys,
|
|
uint16_t i_x, uint16_t i_y,
|
|
uint16_t i_w, uint16_t i_h,
|
|
uint16_t i_sx, uint16_t i_sy );
|
|
|
|
|
|
static inline bool raw_line( filter_sys_t* p_sys,
|
|
uint16_t i_x, uint16_t i_y,
|
|
uint16_t i_w );
|
|
|
|
static void vnc_encrypt_bytes( unsigned char *bytes, char *passwd );
|
|
|
|
|
|
/*****************************************************************************
|
|
* Sub source code
|
|
*****************************************************************************/
|
|
|
|
/*****************************************************************************
|
|
* Local prototypes
|
|
*****************************************************************************/
|
|
struct filter_sys_t
|
|
{
|
|
vlc_mutex_t lock; /* To lock for read/write on picture */
|
|
|
|
bool b_need_update; /* VNC picture is updated, do update the OSD*/
|
|
uint8_t i_alpha; /* alpha transparency value */
|
|
|
|
char *psz_host; /* VNC host */
|
|
|
|
char *psz_passwd; /* VNC password */
|
|
|
|
picture_t *p_pic; /* The picture with OSD data from VNC */
|
|
|
|
int i_socket; /* Socket used for VNC */
|
|
|
|
uint16_t i_vnc_width; /* The with of the VNC screen */
|
|
uint16_t i_vnc_height; /* The height of the VNC screen */
|
|
|
|
bool b_vnc_key_events; /* Send KeyEvents ? */
|
|
bool b_alpha_from_vnc; /* Special ffnetdev alpha feature enabled ? */
|
|
|
|
char read_buffer[READ_BUFFER_SIZE];
|
|
|
|
vlc_thread_t worker_thread;
|
|
|
|
uint8_t ar_color_table_yuv[256][4];
|
|
};
|
|
|
|
/*****************************************************************************
|
|
* CreateFilter: Create the filter and open the definition file
|
|
*****************************************************************************/
|
|
static int CreateFilter ( vlc_object_t *p_this )
|
|
{
|
|
filter_t *p_filter = (filter_t *)p_this;
|
|
|
|
filter_sys_t *p_sys = malloc( sizeof (*p_sys) );
|
|
if( unlikely(p_sys == NULL) )
|
|
return VLC_ENOMEM;
|
|
|
|
/* Populating struct */
|
|
vlc_mutex_init( &p_sys->lock );
|
|
p_sys->b_need_update = false;
|
|
p_sys->psz_host = var_InheritString( p_this, RMTOSD_CFG "host" );
|
|
p_sys->psz_passwd = var_InheritString( p_this, RMTOSD_CFG "password" );
|
|
p_sys->i_alpha = var_InheritInteger( p_this, RMTOSD_CFG "alpha" );
|
|
p_sys->p_pic = NULL;
|
|
p_sys->i_socket = -1;
|
|
|
|
memset( p_sys->ar_color_table_yuv, 255,
|
|
sizeof( p_sys->ar_color_table_yuv ) );
|
|
|
|
if( p_sys->psz_host == NULL )
|
|
{
|
|
msg_Err( p_filter, "unable to get vnc host" );
|
|
goto error;
|
|
}
|
|
|
|
if( p_sys->psz_passwd == NULL )
|
|
{
|
|
msg_Err( p_filter, "unable to get vnc password" );
|
|
goto error;
|
|
}
|
|
|
|
p_filter->p_sys = p_sys;
|
|
|
|
vlc_gcrypt_init();
|
|
|
|
/* create the vnc worker thread */
|
|
if( vlc_clone( &p_sys->worker_thread,
|
|
vnc_worker_thread, p_filter, VLC_THREAD_PRIORITY_LOW ) )
|
|
{
|
|
msg_Err( p_filter, "cannot spawn vnc message reader thread" );
|
|
goto error;
|
|
}
|
|
|
|
/* Attach subpicture source callback */
|
|
p_filter->pf_sub_source = Filter;
|
|
|
|
es_format_Init( &p_filter->fmt_out, SPU_ES, VLC_CODEC_SPU );
|
|
p_filter->fmt_out.i_priority = ES_PRIORITY_SELECTABLE_MIN;
|
|
|
|
if( var_InheritBool( p_this, RMTOSD_CFG "mouse-events" ) )
|
|
p_filter->pf_sub_mouse = MouseEvent;
|
|
|
|
p_sys->b_vnc_key_events = var_InheritBool( p_this,
|
|
RMTOSD_CFG "key-events" );
|
|
if( p_sys->b_vnc_key_events )
|
|
var_AddCallback( p_filter->obj.libvlc, "key-pressed", KeyEvent, p_this );
|
|
|
|
msg_Dbg( p_filter, "osdvnc filter started" );
|
|
|
|
return VLC_SUCCESS;
|
|
|
|
error:
|
|
msg_Err( p_filter, "osdvnc filter discarded" );
|
|
|
|
vlc_mutex_destroy( &p_sys->lock );
|
|
free( p_sys->psz_host );
|
|
free( p_sys->psz_passwd );
|
|
free( p_sys );
|
|
|
|
return VLC_EGENERIC;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* DestroyFilter: Make a clean exit of this plugin
|
|
*****************************************************************************/
|
|
static void DestroyFilter( vlc_object_t *p_this )
|
|
{
|
|
filter_t *p_filter = (filter_t*)p_this;
|
|
filter_sys_t *p_sys = p_filter->p_sys;
|
|
|
|
msg_Dbg( p_filter, "DestroyFilter called." );
|
|
|
|
if( p_sys->b_vnc_key_events )
|
|
var_DelCallback( p_filter->obj.libvlc, "key-pressed", KeyEvent, p_this );
|
|
|
|
vlc_cancel( p_sys->worker_thread );
|
|
vlc_join( p_sys->worker_thread, NULL );
|
|
|
|
if( p_sys->p_pic != NULL )
|
|
picture_Release( p_sys->p_pic );
|
|
if( p_sys->i_socket >= 0 )
|
|
net_Close( p_sys->i_socket );
|
|
|
|
vlc_mutex_destroy( &p_sys->lock );
|
|
free( p_sys->psz_host );
|
|
free( p_sys->psz_passwd );
|
|
free( p_sys );
|
|
}
|
|
|
|
static bool read_exact( filter_t *obj, int fd, void *buf, size_t len )
|
|
{
|
|
return (ssize_t)len == net_Read( obj, fd, buf, len );
|
|
}
|
|
|
|
|
|
static bool write_exact( filter_t *obj, int fd, const void *buf, size_t len )
|
|
{
|
|
return (ssize_t)len == net_Write( obj, fd, buf, len );
|
|
}
|
|
|
|
static int vnc_connect( filter_t *p_filter )
|
|
{
|
|
filter_sys_t *p_sys = p_filter->p_sys;
|
|
|
|
int port = var_InheritInteger( p_filter, RMTOSD_CFG "port" );
|
|
|
|
int fd = net_ConnectTCP( p_filter, p_sys->psz_host, port );
|
|
if( fd == -1 )
|
|
{
|
|
msg_Err( p_filter, "Could not connect to VNC host" );
|
|
return -1;
|
|
}
|
|
|
|
msg_Dbg( p_filter, "Reading protocol version" );
|
|
|
|
rfbProtocolVersionMsg pv;
|
|
if ( !read_exact( p_filter, fd, pv, sz_rfbProtocolVersionMsg ) )
|
|
{
|
|
msg_Err( p_filter, "Could not read version message" );
|
|
goto error;
|
|
}
|
|
pv[sz_rfbProtocolVersionMsg] = '\0'; /* pv size is sz_rfbProtocolVersionMsg+1 */
|
|
|
|
msg_Dbg( p_filter, "Server version is %s", pv );
|
|
|
|
strncpy(pv, "RFB 003.003\n", sz_rfbProtocolVersionMsg);
|
|
|
|
if( !write_exact(p_filter, fd, pv, sz_rfbProtocolVersionMsg) )
|
|
{
|
|
msg_Err( p_filter, "Could not write version message" );
|
|
goto error;
|
|
}
|
|
|
|
msg_Dbg( p_filter, "Reading authentication scheme" );
|
|
uint32_t i_authScheme;
|
|
if( !read_exact( p_filter, fd, &i_authScheme, 4 ) )
|
|
{
|
|
msg_Err( p_filter, "Could not read authentication scheme" );
|
|
goto error;
|
|
}
|
|
i_authScheme = htonl(i_authScheme);
|
|
|
|
msg_Dbg( p_filter, "Authentication scheme = %x", i_authScheme );
|
|
if ( i_authScheme == rfbConnFailed )
|
|
{
|
|
msg_Err( p_filter, "Connection rejected by server" );
|
|
goto error;
|
|
}
|
|
if (i_authScheme == rfbVncAuth)
|
|
{
|
|
unsigned char challenge[CHALLENGESIZE];
|
|
if ( !read_exact( p_filter, fd, challenge, CHALLENGESIZE ) )
|
|
{
|
|
msg_Err( p_filter, "Could not read password challenge" );
|
|
goto error;
|
|
}
|
|
|
|
vnc_encrypt_bytes( challenge, p_sys->psz_passwd );
|
|
|
|
if( !write_exact(p_filter, fd, challenge, CHALLENGESIZE ) )
|
|
{
|
|
msg_Err( p_filter, "Could not write password" );
|
|
goto error;
|
|
}
|
|
uint32_t i_authResult;
|
|
if( !read_exact( p_filter, fd, &i_authResult, 4 ) )
|
|
{
|
|
msg_Err( p_filter, "Could not read authentication result" );
|
|
goto error;
|
|
}
|
|
i_authResult = htonl(i_authResult);
|
|
if (i_authResult != rfbVncAuthOK)
|
|
{
|
|
msg_Err( p_filter, "VNC authentication failed" );
|
|
goto error;
|
|
}
|
|
}
|
|
|
|
msg_Dbg( p_filter, "Writing client init message" );
|
|
rfbClientInitMsg ci;
|
|
ci.shared = 1;
|
|
if( !write_exact( p_filter, fd, &ci, sz_rfbClientInitMsg ) )
|
|
{
|
|
msg_Err( p_filter, "Could not write client init message" );
|
|
goto error;
|
|
}
|
|
|
|
msg_Dbg( p_filter, "Reading server init message" );
|
|
rfbServerInitMsg si;
|
|
if( !read_exact( p_filter, fd, &si, sz_rfbServerInitMsg ) )
|
|
{
|
|
msg_Err( p_filter, "Could not read server init message" );
|
|
goto error;
|
|
}
|
|
si.framebufferWidth = htons(si.framebufferWidth);
|
|
si.framebufferHeight = htons(si.framebufferHeight);
|
|
si.format.redMax = htons(si.format.redMax);
|
|
si.format.greenMax = htons(si.format.greenMax);
|
|
si.format.blueMax = htons(si.format.blueMax);
|
|
|
|
p_sys->i_vnc_width = si.framebufferWidth;
|
|
p_sys->i_vnc_height = si.framebufferHeight;
|
|
|
|
msg_Dbg( p_filter, "Servers preferred pixelformat: "
|
|
"%ux%u, R(%u),G(%u),B(%u), %u bit, depht=%u, %s",
|
|
si.framebufferWidth,
|
|
si.framebufferHeight,
|
|
si.format.redMax,
|
|
si.format.greenMax,
|
|
si.format.blueMax,
|
|
si.format.bitsPerPixel,
|
|
si.format.depth,
|
|
si.format.trueColour ? "TrueColor" : "Not-TrueColor");
|
|
|
|
uint32_t i_nameLength = htonl(si.nameLength);
|
|
if( i_nameLength > MAX_VNC_SERVER_NAME_LENGTH )
|
|
{
|
|
msg_Err( p_filter, "Server name too long" );
|
|
goto error;
|
|
}
|
|
char s_ServerName[MAX_VNC_SERVER_NAME_LENGTH+1];
|
|
|
|
msg_Dbg( p_filter, "Reading server name with size = %u", i_nameLength );
|
|
if( !read_exact( p_filter, fd, s_ServerName, i_nameLength ) )
|
|
{
|
|
msg_Err( p_filter, "Could not read server name" );
|
|
goto error;
|
|
}
|
|
s_ServerName[i_nameLength] = '\0';
|
|
|
|
if( strcmp( s_ServerName, "VDR-OSD") == 0 )
|
|
{
|
|
msg_Dbg( p_filter, "Server is a VDR" );
|
|
p_sys->b_alpha_from_vnc = true;
|
|
}
|
|
else
|
|
{
|
|
msg_Dbg( p_filter, "Server is a normal VNC" );
|
|
p_sys->b_alpha_from_vnc = false;
|
|
}
|
|
|
|
|
|
msg_Dbg( p_filter, "Server init message read properly" );
|
|
msg_Dbg( p_filter, "Server name is %s", s_ServerName );
|
|
|
|
msg_Dbg( p_filter, "Writing SetPixelFormat message" );
|
|
|
|
rfbSetPixelFormatMsg sp;
|
|
sp.type = rfbSetPixelFormat;
|
|
sp.pad1 = sp.pad2 = 0;
|
|
sp.format.bitsPerPixel = 8;
|
|
sp.format.depth = 8 ;
|
|
sp.format.bigEndian = 1;
|
|
sp.format.trueColour = 0;
|
|
sp.format.redMax = htons(31);
|
|
sp.format.greenMax = htons(31);
|
|
sp.format.blueMax = htons(31);
|
|
sp.format.redShift = 10;
|
|
sp.format.greenShift = 5;
|
|
sp.format.blueShift = 0;
|
|
sp.format.pad1 = sp.format.pad2 = 0;
|
|
|
|
if( !write_exact( p_filter, fd, &sp, sz_rfbSetPixelFormatMsg) )
|
|
{
|
|
msg_Err( p_filter, "Could not write SetPixelFormat message" );
|
|
goto error;
|
|
}
|
|
|
|
msg_Dbg( p_filter, "Writing SetEncodings message" );
|
|
|
|
rfbSetEncodingsMsg se;
|
|
se.type = rfbSetEncodings;
|
|
se.pad = 0;
|
|
se.nEncodings = htons( p_sys->b_alpha_from_vnc ? 3 : 2 );
|
|
|
|
if( !write_exact( p_filter, fd, &se, sz_rfbSetEncodingsMsg) )
|
|
{
|
|
msg_Err( p_filter, "Could not write SetEncodings message begin" );
|
|
goto error;
|
|
}
|
|
|
|
uint32_t i_encoding;
|
|
|
|
msg_Dbg( p_filter, "Writing SetEncodings rfbEncodingCopyRect" );
|
|
i_encoding = htonl(rfbEncodingCopyRect);
|
|
if( !write_exact( p_filter, fd, &i_encoding, 4) )
|
|
{
|
|
msg_Err( p_filter, "Could not write encoding type rfbEncodingCopyRect." );
|
|
goto error;
|
|
}
|
|
|
|
msg_Dbg( p_filter, "Writing SetEncodings rfbEncodingRRE" );
|
|
i_encoding = htonl(rfbEncodingRRE);
|
|
if( !write_exact(p_filter, fd, &i_encoding, 4) )
|
|
{
|
|
msg_Err( p_filter, "Could not write encoding type rfbEncodingRRE." );
|
|
goto error;
|
|
}
|
|
|
|
if( p_sys->b_alpha_from_vnc )
|
|
{
|
|
msg_Dbg( p_filter, "Writing SetEncodings rfbEncSpecialUseAlpha" );
|
|
i_encoding = 0x00F0FFFF; /* rfbEncSpecialUseAlpha is 0xFFFFF000
|
|
* before we swap it */
|
|
if( !write_exact(p_filter, fd, &i_encoding, 4) )
|
|
{
|
|
msg_Err( p_filter, "Could not write encoding type rfbEncSpecialUseAlpha." );
|
|
goto error;
|
|
}
|
|
}
|
|
|
|
return fd;
|
|
error:
|
|
net_Close( fd );
|
|
return -1;
|
|
}
|
|
|
|
static int write_update_request(filter_t *p_filter, bool incremental)
|
|
{
|
|
filter_sys_t *p_sys = p_filter->p_sys;
|
|
rfbFramebufferUpdateRequestMsg udr;
|
|
|
|
udr.type = rfbFramebufferUpdateRequest;
|
|
udr.incremental = incremental;
|
|
udr.x = 0;
|
|
udr.y = 0;
|
|
udr.w = htons(p_sys->i_vnc_width);
|
|
udr.h = htons(p_sys->i_vnc_height);
|
|
|
|
int w = write_exact(p_filter, p_sys->i_socket, &udr,
|
|
sz_rfbFramebufferUpdateRequestMsg);
|
|
if( !w )
|
|
msg_Err( p_filter, "Could not write rfbFramebufferUpdateRequestMsg." );
|
|
return w;
|
|
}
|
|
|
|
static void update_thread_cleanup( void *p )
|
|
{
|
|
vlc_thread_t *th = p;
|
|
|
|
vlc_cancel( *th );
|
|
vlc_join( *th, NULL );
|
|
}
|
|
|
|
static void dummy_cleanup( void *p )
|
|
{
|
|
(void) p;
|
|
}
|
|
|
|
static void* vnc_worker_thread( void *obj )
|
|
{
|
|
filter_t* p_filter = (filter_t*)obj;
|
|
filter_sys_t *p_sys = p_filter->p_sys;
|
|
vlc_thread_t update_thread;
|
|
int canc = vlc_savecancel ();
|
|
|
|
msg_Dbg( p_filter, "VNC worker thread started" );
|
|
|
|
int fd = vnc_connect( p_filter );
|
|
if( fd == -1 )
|
|
{
|
|
msg_Err( p_filter, "Error occurred while handshaking VNC host" );
|
|
return NULL;
|
|
}
|
|
|
|
/* Create an empty picture for VNC the data */
|
|
picture_t *pic = picture_New( VLC_CODEC_YUVA, p_sys->i_vnc_width,
|
|
p_sys->i_vnc_height, 1, 1 );
|
|
if( likely(pic != NULL) )
|
|
{
|
|
vlc_mutex_lock( &p_sys->lock );
|
|
p_sys->i_socket = fd;
|
|
p_sys->p_pic = pic;
|
|
vlc_mutex_unlock( &p_sys->lock );
|
|
}
|
|
else
|
|
{
|
|
net_Close( fd );
|
|
return NULL;
|
|
}
|
|
|
|
write_update_request( p_filter, false );
|
|
|
|
/* create the update request thread */
|
|
bool polling = var_InheritBool( p_filter, RMTOSD_CFG "vnc-polling" );
|
|
if( polling
|
|
&& vlc_clone( &update_thread, update_request_thread,
|
|
p_filter, VLC_THREAD_PRIORITY_LOW ) )
|
|
{
|
|
msg_Err( p_filter, "cannot spawn VNC update request thread" );
|
|
polling = false;
|
|
}
|
|
|
|
vlc_cleanup_push( polling ? update_thread_cleanup : dummy_cleanup,
|
|
&update_thread );
|
|
|
|
/* connection is initialized, now read and handle server messages */
|
|
for( ;; )
|
|
{
|
|
rfbServerToClientMsg msg;
|
|
int i_msgSize;
|
|
|
|
memset( &msg, 0, sizeof(msg) );
|
|
vlc_restorecancel (canc);
|
|
|
|
if( !read_exact(p_filter, fd, &msg, 1 ) )
|
|
{
|
|
msg_Err( p_filter, "Error while waiting for next server message");
|
|
break;
|
|
}
|
|
switch (msg.type)
|
|
{
|
|
case rfbFramebufferUpdate:
|
|
i_msgSize = sz_rfbFramebufferUpdateMsg;
|
|
break;
|
|
case rfbSetColourMapEntries:
|
|
i_msgSize = sz_rfbSetColourMapEntriesMsg;
|
|
break;
|
|
case rfbBell:
|
|
i_msgSize = sz_rfbBellMsg;
|
|
break;
|
|
case rfbServerCutText:
|
|
i_msgSize = sz_rfbServerCutTextMsg;
|
|
break;
|
|
case rfbReSizeFrameBuffer:
|
|
i_msgSize = sz_rfbReSizeFrameBufferMsg;
|
|
break;
|
|
default:
|
|
i_msgSize = 0;
|
|
msg_Err( p_filter, "Invalid message %u received", msg.type );
|
|
break;
|
|
}
|
|
|
|
if( i_msgSize <= 0 )
|
|
break;
|
|
|
|
if( --i_msgSize > 0 )
|
|
{
|
|
if ( !read_exact( p_filter, fd, ((char *)&msg) + 1, i_msgSize ) )
|
|
{
|
|
msg_Err( p_filter, "Error while reading message of type %u",
|
|
msg.type );
|
|
break;
|
|
}
|
|
}
|
|
|
|
canc = vlc_savecancel ();
|
|
process_server_message( p_filter, &msg);
|
|
}
|
|
|
|
vlc_cleanup_pop();
|
|
if( polling )
|
|
update_thread_cleanup( &update_thread );
|
|
|
|
msg_Dbg( p_filter, "VNC message reader thread ended" );
|
|
vlc_restorecancel (canc);
|
|
return NULL;
|
|
}
|
|
|
|
static void* update_request_thread( void *obj )
|
|
{
|
|
filter_t* p_filter = (filter_t*)obj;
|
|
int canc = vlc_savecancel();
|
|
mtime_t interval = var_InheritInteger( p_filter, RMTOSD_CFG "update" );
|
|
vlc_restorecancel(canc);
|
|
|
|
if( interval < 100 )
|
|
interval = 100;
|
|
interval *= 1000; /* ms -> µs */
|
|
|
|
do
|
|
msleep( interval );
|
|
while( write_update_request( p_filter, true ) );
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static bool process_server_message ( filter_t *p_filter,
|
|
rfbServerToClientMsg *msg )
|
|
{
|
|
filter_sys_t *p_sys = p_filter->p_sys;
|
|
|
|
switch (msg->type)
|
|
{
|
|
case rfbFramebufferUpdate:
|
|
{
|
|
msg->fu.nRects = htons(msg->fu.nRects);
|
|
rfbFramebufferUpdateRectHeader hdr;
|
|
|
|
for (int i_rect = 0; i_rect < msg->fu.nRects; i_rect++)
|
|
{
|
|
if (!read_exact(p_filter, p_sys->i_socket, &hdr,
|
|
sz_rfbFramebufferUpdateRectHeader ) )
|
|
{
|
|
msg_Err( p_filter, "Could not read FrameBufferUpdate header" );
|
|
return false;
|
|
}
|
|
hdr.r.x = htons(hdr.r.x);
|
|
hdr.r.y = htons(hdr.r.y);
|
|
hdr.r.w = htons(hdr.r.w);
|
|
hdr.r.h = htons(hdr.r.h);
|
|
hdr.encoding = htonl(hdr.encoding);
|
|
|
|
switch (hdr.encoding)
|
|
{
|
|
case rfbEncodingRaw:
|
|
{
|
|
int i_line;
|
|
for (i_line = 0; i_line < hdr.r.h; i_line++)
|
|
{
|
|
if ( !read_exact( p_filter, p_sys->i_socket,
|
|
p_sys->read_buffer, hdr.r.w ) )
|
|
{
|
|
msg_Err( p_filter,
|
|
"Could not read FrameBufferUpdate line data" );
|
|
return false;
|
|
}
|
|
vlc_mutex_lock( &p_sys->lock );
|
|
if ( !raw_line( p_sys, hdr.r.x,
|
|
hdr.r.y + i_line,
|
|
hdr.r.w ) )
|
|
{
|
|
msg_Err( p_filter, "raw_line failed." );
|
|
vlc_mutex_unlock( &p_sys->lock );
|
|
return false;
|
|
}
|
|
vlc_mutex_unlock( &p_sys->lock );
|
|
}
|
|
}
|
|
break;
|
|
|
|
case rfbEncodingCopyRect:
|
|
{
|
|
rfbCopyRect rect;
|
|
|
|
if ( !read_exact( p_filter, p_sys->i_socket,
|
|
&rect, sz_rfbCopyRect ) )
|
|
{
|
|
msg_Err( p_filter, "Could not read rfbCopyRect" );
|
|
return false;
|
|
}
|
|
rect.srcX = htons( rect.srcX );
|
|
rect.srcY = htons( rect.srcY );
|
|
|
|
vlc_mutex_lock( &p_sys->lock );
|
|
if ( !copy_rect( p_sys,
|
|
hdr.r.x, hdr.r.y,
|
|
hdr.r.w, hdr.r.h,
|
|
rect.srcX, rect.srcY ) )
|
|
{
|
|
msg_Err( p_filter, "copy_rect failed." );
|
|
vlc_mutex_unlock( &p_sys->lock );
|
|
return false;
|
|
}
|
|
vlc_mutex_unlock( &p_sys->lock );
|
|
}
|
|
break;
|
|
|
|
case rfbEncodingRRE:
|
|
{
|
|
rfbRREHeader rrehdr;
|
|
if ( !read_exact( p_filter, p_sys->i_socket,
|
|
&rrehdr, sz_rfbRREHeader ) )
|
|
{
|
|
msg_Err( p_filter, "Could not read rfbRREHeader" );
|
|
return false;
|
|
}
|
|
uint8_t i_pixcolor;
|
|
if ( !read_exact( p_filter, p_sys->i_socket,
|
|
&i_pixcolor, 1 ) )
|
|
{
|
|
msg_Err( p_filter, "Could not read RRE pixcolor" );
|
|
return false;
|
|
}
|
|
|
|
vlc_mutex_lock( &p_sys->lock );
|
|
if ( !fill_rect( p_sys,
|
|
hdr.r.x, hdr.r.y,
|
|
hdr.r.w, hdr.r.h,
|
|
i_pixcolor) )
|
|
{
|
|
msg_Err( p_filter, "main fill_rect failed." );
|
|
vlc_mutex_unlock( &p_sys->lock );
|
|
return false;
|
|
}
|
|
vlc_mutex_unlock( &p_sys->lock );
|
|
|
|
rrehdr.nSubrects = htonl(rrehdr.nSubrects);
|
|
|
|
int i_datasize = rrehdr.nSubrects *
|
|
( sizeof(i_pixcolor) + sz_rfbRectangle ) ;
|
|
if ( i_datasize > READ_BUFFER_SIZE )
|
|
{
|
|
msg_Err( p_filter, "Buffer too small, "
|
|
"need %u bytes", i_datasize );
|
|
return false;
|
|
}
|
|
if ( !read_exact( p_filter, p_sys->i_socket,
|
|
p_sys->read_buffer, i_datasize ) )
|
|
{
|
|
msg_Err( p_filter,
|
|
"Could not read RRE subrect data" );
|
|
return false;
|
|
}
|
|
|
|
uint32_t i_subrect;
|
|
rfbRectangle* p_subrect;
|
|
int i_offset = 0;
|
|
vlc_mutex_lock( &p_sys->lock );
|
|
for ( i_subrect = 0;
|
|
i_subrect < rrehdr.nSubrects; i_subrect++)
|
|
{
|
|
i_pixcolor = p_sys->read_buffer[i_offset];
|
|
i_offset += sizeof(i_pixcolor);
|
|
p_subrect =
|
|
(rfbRectangle*)(p_sys->read_buffer + i_offset);
|
|
i_offset += sz_rfbRectangle;
|
|
|
|
if (!fill_rect( p_sys,
|
|
htons(p_subrect->x) + hdr.r.x,
|
|
htons(p_subrect->y) + hdr.r.y,
|
|
htons(p_subrect->w),
|
|
htons(p_subrect->h),
|
|
i_pixcolor) )
|
|
{
|
|
msg_Err( p_filter,
|
|
"subrect %u fill_rect failed.", i_subrect );
|
|
vlc_mutex_unlock( &p_sys->lock );
|
|
return false;
|
|
}
|
|
}
|
|
vlc_mutex_unlock( &p_sys->lock );
|
|
}
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
vlc_mutex_lock( &p_sys->lock );
|
|
p_sys->b_need_update = true;
|
|
vlc_mutex_unlock( &p_sys->lock );
|
|
}
|
|
return true;
|
|
|
|
case rfbSetColourMapEntries:
|
|
{
|
|
msg->scme.nColours = htons(msg->scme.nColours);
|
|
msg->scme.firstColour = htons(msg->scme.firstColour);
|
|
int i_datasize;
|
|
if ( p_sys->b_alpha_from_vnc )
|
|
{
|
|
i_datasize = 2 * msg->scme.nColours * 4;
|
|
}
|
|
else
|
|
{
|
|
i_datasize = 2 * msg->scme.nColours * 3;
|
|
}
|
|
if ( i_datasize > READ_BUFFER_SIZE )
|
|
{
|
|
msg_Err( p_filter, "Buffer too small, need %u bytes",
|
|
i_datasize );
|
|
return false;
|
|
}
|
|
|
|
if ( !read_exact( p_filter, p_sys->i_socket,
|
|
p_sys->read_buffer, i_datasize ) )
|
|
{
|
|
msg_Err( p_filter, "Could not read color map data" );
|
|
return false;
|
|
}
|
|
|
|
uint8_t i_red, i_green, i_blue, i_alpha, i_color_index;
|
|
uint16_t i_offset = 0;
|
|
i_alpha = 255;
|
|
|
|
for (int i = 0; i < msg->scme.nColours; i++)
|
|
{
|
|
i_color_index = i+msg->scme.firstColour;
|
|
if ( p_sys->b_alpha_from_vnc )
|
|
{
|
|
i_alpha = p_sys->read_buffer[i_offset];
|
|
i_offset += 2;
|
|
}
|
|
i_red = p_sys->read_buffer[i_offset];
|
|
i_offset += 2;
|
|
i_green = p_sys->read_buffer[i_offset];
|
|
i_offset += 2;
|
|
i_blue = p_sys->read_buffer[i_offset];
|
|
i_offset += 2;
|
|
rgb_to_yuv( &p_sys->ar_color_table_yuv[i_color_index][0],
|
|
&p_sys->ar_color_table_yuv[i_color_index][1],
|
|
&p_sys->ar_color_table_yuv[i_color_index][2],
|
|
i_red,
|
|
i_green,
|
|
i_blue );
|
|
p_sys->ar_color_table_yuv[i][3] = i_alpha;
|
|
}
|
|
}
|
|
return true;
|
|
|
|
case rfbBell:
|
|
msg_Err( p_filter, "rfbBell received" );
|
|
return true;
|
|
|
|
case rfbServerCutText:
|
|
msg->sct.length = htons(msg->sct.length);
|
|
if ( msg->sct.length > READ_BUFFER_SIZE )
|
|
{
|
|
msg_Err( p_filter, "Buffer too small, need %u bytes", msg->sct.length );
|
|
return false;
|
|
}
|
|
if ( !read_exact( p_filter, p_sys->i_socket,
|
|
p_sys->read_buffer, msg->sct.length ) )
|
|
{
|
|
msg_Err( p_filter, "Could not read Reading rfbServerCutText data" );
|
|
return false;
|
|
}
|
|
return true;
|
|
|
|
case rfbReSizeFrameBuffer:
|
|
msg_Err( p_filter, "Reading rfbReSizeFrameBuffer not implemented" );
|
|
return false;
|
|
|
|
default:
|
|
msg_Err( p_filter, "Invalid message %u received", msg->type );
|
|
return false;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Filter: the whole thing
|
|
****************************************************************************
|
|
* This function outputs subpictures at regular time intervals.
|
|
****************************************************************************/
|
|
static subpicture_t *Filter( filter_t *p_filter, mtime_t date )
|
|
{
|
|
filter_sys_t *p_sys = p_filter->p_sys;
|
|
subpicture_t *p_spu;
|
|
subpicture_region_t *p_region;
|
|
video_format_t fmt;
|
|
picture_t *p_pic = NULL;
|
|
|
|
vlc_mutex_lock( &p_sys->lock );
|
|
|
|
if( p_sys->b_need_update )
|
|
p_pic = p_sys->p_pic;
|
|
|
|
if( p_pic == NULL )
|
|
{
|
|
vlc_mutex_unlock( &p_sys->lock );
|
|
return NULL;
|
|
}
|
|
|
|
/* Allocate the subpicture internal data. */
|
|
p_spu = filter_NewSubpicture( p_filter );
|
|
if( !p_spu )
|
|
{
|
|
vlc_mutex_unlock( &p_sys->lock );
|
|
return NULL;
|
|
}
|
|
|
|
p_spu->b_absolute = false;
|
|
p_spu->i_start = date;
|
|
p_spu->i_stop = 0;
|
|
p_spu->b_ephemer = true;
|
|
|
|
/* Create new SPU region */
|
|
memset( &fmt, 0, sizeof(video_format_t) );
|
|
fmt.i_chroma = VLC_CODEC_YUVA;
|
|
fmt.i_sar_num = fmt.i_sar_den = 1;
|
|
fmt.i_width = fmt.i_visible_width = p_pic->p[Y_PLANE].i_visible_pitch;
|
|
fmt.i_height = fmt.i_visible_height = p_pic->p[Y_PLANE].i_visible_lines;
|
|
fmt.i_x_offset = fmt.i_y_offset = 0;
|
|
p_region = subpicture_region_New( &fmt );
|
|
if( !p_region )
|
|
{
|
|
msg_Err( p_filter, "cannot allocate SPU region" );
|
|
subpicture_Delete( p_spu );
|
|
vlc_mutex_unlock( &p_sys->lock );
|
|
return NULL;
|
|
}
|
|
|
|
/* FIXME the copy is probably not needed anymore */
|
|
picture_Copy( p_region->p_picture, p_pic );
|
|
|
|
p_sys->b_need_update = false;
|
|
|
|
vlc_mutex_unlock( &p_sys->lock );
|
|
|
|
/* set to one of the 9 relative locations */
|
|
p_region->i_align = 0; /* Center */
|
|
p_spu->b_absolute = false;
|
|
|
|
|
|
p_spu->i_original_picture_width = 0; /*Let vout core do the horizontal scaling */
|
|
p_spu->i_original_picture_height = fmt.i_height;
|
|
|
|
p_spu->p_region = p_region;
|
|
|
|
p_spu->i_alpha = ( p_sys->i_alpha );
|
|
|
|
return p_spu;
|
|
}
|
|
|
|
|
|
static inline void rgb_to_yuv( uint8_t *y, uint8_t *u, uint8_t *v,
|
|
int r, int g, int b )
|
|
{
|
|
*y = ( ( ( 66 * r + 129 * g + 25 * b + 128 ) >> 8 ) + 16 );
|
|
*u = ( ( -38 * r - 74 * g + 112 * b + 128 ) >> 8 ) + 128 ;
|
|
*v = ( ( 112 * r - 94 * g - 18 * b + 128 ) >> 8 ) + 128 ;
|
|
}
|
|
|
|
static inline bool fill_rect( filter_sys_t* p_sys,
|
|
uint16_t i_x, uint16_t i_y,
|
|
uint16_t i_w, uint16_t i_h,
|
|
uint8_t i_color)
|
|
{
|
|
plane_t *p_outY = p_sys->p_pic->p+Y_PLANE;
|
|
plane_t *p_outU = p_sys->p_pic->p+U_PLANE;
|
|
plane_t *p_outV = p_sys->p_pic->p+V_PLANE;
|
|
plane_t *p_outA = p_sys->p_pic->p+A_PLANE;
|
|
int i_pitch = p_outY->i_pitch;
|
|
int i_lines = p_outY->i_lines;
|
|
if ( i_x + i_w > i_pitch)
|
|
return false;
|
|
if ( i_y + i_h > i_lines)
|
|
return false;
|
|
int i_line_offset = i_y * i_pitch;
|
|
uint8_t i_yuv_y = p_sys->ar_color_table_yuv[i_color][0];
|
|
uint8_t i_yuv_u = p_sys->ar_color_table_yuv[i_color][1];
|
|
uint8_t i_yuv_v = p_sys->ar_color_table_yuv[i_color][2];
|
|
uint8_t i_alpha = p_sys->ar_color_table_yuv[i_color][3];
|
|
for( int i_line = 0; i_line < i_h; i_line++ )
|
|
{
|
|
for( int i_column = 0; i_column < i_w; i_column++ )
|
|
{
|
|
int i_total_offset = i_line_offset + i_x + i_column;
|
|
p_outY->p_pixels[ i_total_offset ] = i_yuv_y;
|
|
p_outU->p_pixels[ i_total_offset ] = i_yuv_u;
|
|
p_outV->p_pixels[ i_total_offset ] = i_yuv_v;
|
|
p_outA->p_pixels[ i_total_offset ] = i_alpha;
|
|
}
|
|
i_line_offset += i_pitch;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
static inline bool copy_rect( filter_sys_t* p_sys,
|
|
uint16_t i_x, uint16_t i_y,
|
|
uint16_t i_w, uint16_t i_h,
|
|
uint16_t i_sx, uint16_t i_sy )
|
|
{
|
|
plane_t *p_Y = p_sys->p_pic->p+Y_PLANE;
|
|
plane_t *p_U = p_sys->p_pic->p+U_PLANE;
|
|
plane_t *p_V = p_sys->p_pic->p+V_PLANE;
|
|
plane_t *p_A = p_sys->p_pic->p+A_PLANE;
|
|
|
|
int i_pitch = p_Y->i_pitch;
|
|
int i_lines = p_Y->i_lines;
|
|
|
|
fprintf( stderr, "copy_rect: (%d,%d)+(%d,%d) -> (%d,%d)\n", i_x, i_y, i_w, i_h, i_sx, i_sy );
|
|
|
|
if( i_x + i_w > i_pitch || i_sx + i_w > i_pitch )
|
|
return false;
|
|
if( i_y + i_h > i_lines || i_sy + i_h > i_lines)
|
|
return false;
|
|
|
|
if( i_w <= 0 || i_h <= 0 )
|
|
return true;
|
|
|
|
uint8_t *pb_buffer = calloc( i_w * i_h, 4 );
|
|
if( !pb_buffer )
|
|
return false;
|
|
|
|
for( int i_line = 0; i_line < i_h; i_line++ )
|
|
{
|
|
for( int i_column = 0; i_column < i_w; i_column++ )
|
|
{
|
|
const int i_src_offset = ( i_sy + i_line ) * i_pitch + i_sx + i_column;
|
|
const int i_tmp_offset = ( 0 + i_line ) * i_w + 0 + i_column;
|
|
|
|
pb_buffer[4*i_tmp_offset + 0] = p_Y->p_pixels[i_src_offset];
|
|
pb_buffer[4*i_tmp_offset + 1] = p_U->p_pixels[i_src_offset];
|
|
pb_buffer[4*i_tmp_offset + 2] = p_V->p_pixels[i_src_offset];
|
|
pb_buffer[4*i_tmp_offset + 3] = p_A->p_pixels[i_src_offset];
|
|
}
|
|
}
|
|
|
|
for( int i_line = 0; i_line < i_h; i_line++ )
|
|
{
|
|
for( int i_column = 0; i_column < i_w; i_column++ )
|
|
{
|
|
const int i_tmp_offset = ( 0 + i_line ) * i_w + 0 + i_column;
|
|
const int i_dst_offset = ( i_y + i_line ) * i_pitch + i_x + i_column;
|
|
|
|
p_Y->p_pixels[i_dst_offset] = pb_buffer[4*i_tmp_offset + 0];
|
|
p_U->p_pixels[i_dst_offset] = pb_buffer[4*i_tmp_offset + 1];
|
|
p_V->p_pixels[i_dst_offset] = pb_buffer[4*i_tmp_offset + 2];
|
|
p_A->p_pixels[i_dst_offset] = pb_buffer[4*i_tmp_offset + 3];
|
|
}
|
|
}
|
|
free( pb_buffer );
|
|
return true;
|
|
|
|
}
|
|
|
|
static inline bool raw_line( filter_sys_t* p_sys,
|
|
uint16_t i_x, uint16_t i_y,
|
|
uint16_t i_w )
|
|
{
|
|
plane_t *p_outY = p_sys->p_pic->p+Y_PLANE;
|
|
plane_t *p_outU = p_sys->p_pic->p+U_PLANE;
|
|
plane_t *p_outV = p_sys->p_pic->p+V_PLANE;
|
|
plane_t *p_outA = p_sys->p_pic->p+A_PLANE;
|
|
int i_pitch = p_outY->i_pitch;
|
|
int i_lines = p_outY->i_lines;
|
|
if ( i_x + i_w > i_pitch)
|
|
return false;
|
|
if ( i_y > i_lines)
|
|
return false;
|
|
|
|
int i_line_offset = i_y * i_pitch + i_x;
|
|
|
|
for( int i_column = 0; i_column < i_w; i_column++ )
|
|
{
|
|
int i_offset = i_line_offset + i_column;
|
|
uint8_t i_color = p_sys->read_buffer[i_column];
|
|
p_outY->p_pixels[ i_offset ] = p_sys->ar_color_table_yuv[i_color][0];
|
|
p_outU->p_pixels[ i_offset ] = p_sys->ar_color_table_yuv[i_color][1];
|
|
p_outV->p_pixels[ i_offset ] = p_sys->ar_color_table_yuv[i_color][2];
|
|
p_outA->p_pixels[ i_offset ] = p_sys->ar_color_table_yuv[i_color][3];
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
* MouseEvent: callback for mouse events
|
|
*****************************************************************************/
|
|
static int MouseEvent( filter_t *p_filter,
|
|
const vlc_mouse_t *p_old,
|
|
const vlc_mouse_t *p_new,
|
|
const video_format_t *p_fmt )
|
|
{
|
|
filter_sys_t *p_sys = p_filter->p_sys;
|
|
VLC_UNUSED(p_old);
|
|
|
|
int i_v = p_new->i_pressed;
|
|
int i_x = p_new->i_x;
|
|
int i_y = p_new->i_y;
|
|
|
|
vlc_mutex_lock( &p_sys->lock );
|
|
|
|
const int v_h = p_fmt->i_visible_height;
|
|
const int v_w = p_sys->i_vnc_width * v_h / p_sys->i_vnc_height;
|
|
const int v_x = (p_fmt->i_visible_width-v_w)/2;
|
|
|
|
i_x -= v_x;
|
|
|
|
if( i_y < 0 || i_x < 0 || i_y >= v_h || i_x >= v_w )
|
|
{
|
|
vlc_mutex_unlock( &p_sys->lock );
|
|
msg_Dbg( p_filter, "invalid mouse event? x=%d y=%d btn=%x", i_x, i_y, i_v );
|
|
return VLC_SUCCESS;
|
|
}
|
|
|
|
if( p_sys->i_socket == -1 )
|
|
{
|
|
vlc_mutex_unlock( &p_sys->lock );
|
|
return VLC_SUCCESS;
|
|
}
|
|
|
|
#ifdef VNC_DEBUG
|
|
msg_Dbg( p_filter, "mouse event x=%d y=%d btn=%x", i_x, i_y, i_v );
|
|
#endif
|
|
|
|
/* */
|
|
i_x = i_x * p_sys->i_vnc_width / v_w;
|
|
i_y = i_y * p_sys->i_vnc_height / v_h;
|
|
|
|
/* buttonMask bits 0-7 are buttons 1-8, 0=up, 1=down */
|
|
rfbPointerEventMsg ev;
|
|
ev.type = rfbPointerEvent;
|
|
ev.buttonMask = i_v;
|
|
ev.x = htons(i_x);
|
|
ev.y = htons(i_y);
|
|
|
|
write_exact( p_filter, p_sys->i_socket, &ev, sz_rfbPointerEventMsg);
|
|
vlc_mutex_unlock( &p_sys->lock );
|
|
|
|
return VLC_EGENERIC;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* KeyEvent: callback for keyboard events
|
|
*****************************************************************************/
|
|
static int KeyEvent( vlc_object_t *p_this, char const *psz_var,
|
|
vlc_value_t oldval, vlc_value_t newval, void *p_data )
|
|
{
|
|
VLC_UNUSED(psz_var); VLC_UNUSED(oldval);
|
|
|
|
filter_t *p_filter = (filter_t *)p_data;
|
|
filter_sys_t *p_sys = p_filter->p_sys;
|
|
|
|
msg_Dbg( p_this, "key pressed (%"PRId64") ", newval.i_int );
|
|
|
|
if ( !newval.i_int )
|
|
{
|
|
msg_Err( p_this, "Received invalid key event 0" );
|
|
return VLC_EGENERIC;
|
|
}
|
|
|
|
vlc_mutex_lock( &p_sys->lock );
|
|
if( p_sys->i_socket == -1 )
|
|
{
|
|
vlc_mutex_unlock( &p_sys->lock );
|
|
return VLC_SUCCESS;
|
|
}
|
|
|
|
uint32_t i_key32 = newval.i_int;
|
|
i_key32 = htonl(i_key32);
|
|
rfbKeyEventMsg ev;
|
|
|
|
ev.type = rfbKeyEvent;
|
|
ev.down = 1;
|
|
ev.pad = 0;
|
|
|
|
/* first key-down for modifier-keys */
|
|
if (newval.i_int & KEY_MODIFIER_CTRL)
|
|
{
|
|
ev.key = 0xffe3;
|
|
write_exact( p_filter, p_sys->i_socket, &ev, sz_rfbKeyEventMsg );
|
|
}
|
|
if (newval.i_int & KEY_MODIFIER_SHIFT)
|
|
{
|
|
ev.key = 0xffe1;
|
|
write_exact( p_filter, p_sys->i_socket, &ev, sz_rfbKeyEventMsg );
|
|
}
|
|
if (newval.i_int & KEY_MODIFIER_ALT)
|
|
{
|
|
ev.key = 0xffe9;
|
|
write_exact( p_filter, p_sys->i_socket, &ev, sz_rfbKeyEventMsg );
|
|
}
|
|
|
|
/* then key-down for the pressed key */
|
|
ev.key = i_key32;
|
|
write_exact( p_filter, p_sys->i_socket, &ev, sz_rfbKeyEventMsg );
|
|
|
|
ev.down = 0;
|
|
|
|
/* then key-up for the pressed key */
|
|
write_exact( p_filter, p_sys->i_socket, &ev, sz_rfbKeyEventMsg );
|
|
|
|
/* last key-down for modifier-keys */
|
|
if (newval.i_int & KEY_MODIFIER_CTRL)
|
|
{
|
|
ev.key = 0xffe3;
|
|
write_exact( p_filter, p_sys->i_socket, &ev, sz_rfbKeyEventMsg );
|
|
}
|
|
if (newval.i_int & KEY_MODIFIER_SHIFT)
|
|
{
|
|
ev.key = 0xffe1;
|
|
write_exact( p_filter, p_sys->i_socket, &ev, sz_rfbKeyEventMsg );
|
|
}
|
|
if (newval.i_int & KEY_MODIFIER_ALT)
|
|
{
|
|
ev.key = 0xffe9;
|
|
write_exact( p_filter, p_sys->i_socket, &ev, sz_rfbKeyEventMsg );
|
|
}
|
|
vlc_mutex_unlock( &p_sys->lock );
|
|
|
|
return VLC_SUCCESS;
|
|
}
|
|
|
|
static void vnc_encrypt_bytes( unsigned char *bytes, char *passwd )
|
|
{
|
|
unsigned char key[8];
|
|
|
|
for( unsigned i = 0; i < 8; i++ )
|
|
key[i] = i < strlen( passwd ) ? passwd[i] : '\0';
|
|
|
|
gcry_cipher_hd_t ctx;
|
|
gcry_cipher_open( &ctx, GCRY_CIPHER_DES, GCRY_CIPHER_MODE_ECB,0);
|
|
|
|
/* reverse bits of the key */
|
|
for( unsigned i = 0 ; i < 8 ; i++ )
|
|
key[i] =
|
|
(key[i] >> 7) +
|
|
(((key[i] >> 6) & 0x01 ) << 1 ) +
|
|
(((key[i] >> 5) & 0x01 ) << 2 ) +
|
|
(((key[i] >> 4) & 0x01 ) << 3 ) +
|
|
(((key[i] >> 3) & 0x01 ) << 4 ) +
|
|
(((key[i] >> 2) & 0x01 ) << 5 ) +
|
|
(((key[i] >> 1) & 0x01 ) << 6 ) +
|
|
((key[i] & 0x01) << 7 );
|
|
|
|
gcry_cipher_setkey( ctx, key, 8 );
|
|
gcry_cipher_encrypt( ctx, bytes, CHALLENGESIZE, bytes, CHALLENGESIZE );
|
|
gcry_cipher_close( ctx );
|
|
}
|
|
|
|
|