[vlc-devel] [PATCH] access: add Remote Desktop viewer module

Francois Cartegnie fcvlcdev at free.fr
Thu Feb 21 14:38:30 CET 2013


Here's an freerdp based Remote Desktop access/demux to record RDP sessions.

Note: By lack of compatible server, desktop resizing has not been tested.

Francois

---
 configure.ac              |    5 +
 modules/access/Modules.am |    3 +
 modules/access/rdp.c      |  450 +++++++++++++++++++++++++++++++++++++++++++++
 po/POTFILES.in            |    1 +
 4 files changed, 459 insertions(+), 0 deletions(-)
 create mode 100644 modules/access/rdp.c

diff --git a/configure.ac b/configure.ac
index 815bbdd..73f5651 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1894,6 +1894,11 @@ if test "${enable_screen}" != "no"; then
 fi
 
 dnl
+dnl  RDP/Remote Desktop access module
+dnl
+PKG_ENABLE_MODULES_VLC([LIBFREERDP], [rdp], [freerdp >= 1.0.1], (RDP/Remote Desktop client support) )
+
+dnl
 dnl  Real RTSP plugin
 dnl
 AC_ARG_ENABLE(realrtsp,
diff --git a/modules/access/Modules.am b/modules/access/Modules.am
index 64f5dbf..4b5770d 100644
--- a/modules/access/Modules.am
+++ b/modules/access/Modules.am
@@ -187,6 +187,9 @@ libscreen_plugin_la_LDFLAGS += "-Wl,-framework,OpenGL,-framework,ApplicationServ
 endif
 
 
+### RDP / Remote Desktop ###
+SOURCES_rdp = rdp.c
+
 ### Optical media ###
 
 SOURCES_cdda = \
diff --git a/modules/access/rdp.c b/modules/access/rdp.c
new file mode 100644
index 0000000..78d5f4c
--- /dev/null
+++ b/modules/access/rdp.c
@@ -0,0 +1,450 @@
+/*****************************************************************************
+ * rdp.c: libfreeRDP based Remote Desktop access
+ *****************************************************************************
+ * Copyright (C) 2013 VideoLAN Authors
+ *****************************************************************************
+ *
+ * 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 implied 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.
+ *****************************************************************************/
+
+/*****************************************************************************
+ * Preamble
+ *****************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <vlc_common.h>
+#include <vlc_plugin.h>
+#include <vlc_demux.h>
+#include <vlc_url.h>
+#include <vlc_meta.h>
+
+#define boolean bool
+
+/* see MS-RDPBCGR http://msdn.microsoft.com/en-us/library/cc240445.aspx */
+
+#include <freerdp/freerdp.h>
+#include <freerdp/settings.h>
+#include <freerdp/channels/channels.h>
+#include <freerdp/gdi/gdi.h>
+
+#include <errno.h>
+#ifdef HAVE_POLL
+# include <poll.h>
+#endif
+
+#define RDP_USER N_("RDP auth username")
+#define RDP_PASSWORD N_("RDP auth password")
+#define RDP_PASSWORD_LONGTEXT N_("RDP Password")
+#define RDP_ENCRYPT N_("Encrypted connexion")
+#define RDP_FPS N_("Frame rate")
+#define RDP_FPS_LONGTEXT N_("Acquisition rate (in fps)")
+
+#define CFG_PREFIX "rdp-"
+
+/*****************************************************************************
+ * Module descriptor
+ *****************************************************************************/
+static int  Open ( vlc_object_t * );
+static void Close( vlc_object_t * );
+
+vlc_module_begin()
+    set_shortname( N_("RDP") )
+    add_shortcut( "rdp" )
+    set_category( CAT_INPUT )
+    set_subcategory( SUBCAT_INPUT_ACCESS )
+    set_description( N_("RDP Remote Desktop") )
+    set_capability( "access_demux", 10 )
+
+    add_string( CFG_PREFIX "user", NULL, RDP_USER, RDP_USER, false )
+        change_safe()
+    add_string( CFG_PREFIX "password", NULL, RDP_PASSWORD, RDP_PASSWORD_LONGTEXT, false )
+        change_safe()
+    add_integer( CFG_PREFIX "fps", 5, RDP_FPS, RDP_FPS_LONGTEXT, true )
+        change_safe()
+    add_bool( CFG_PREFIX "encrypt", false, RDP_ENCRYPT, RDP_ENCRYPT, true )
+        change_safe()
+
+    set_callbacks( Open, Close )
+vlc_module_end()
+
+#define RDP_MAX_FD 32
+
+struct demux_sys_t
+{
+    freerdp *p_instance;
+
+    block_t *p_block;
+
+    int i_framebuffersize;
+
+    float f_fps;
+    int i_frame_interval;
+    mtime_t i_starttime;
+
+    es_format_t fmt;
+    es_out_id_t *es;
+
+    char *psz_hostname;
+    int i_port;
+};
+
+/* context */
+
+struct vlcrdp_context_t
+{
+    rdpContext rdp_context; /* Extending struct */
+    demux_t *p_demux;
+    rdpSettings* p_settings;
+};
+typedef struct vlcrdp_context_t vlcrdp_context_t;
+
+/*****************************************************************************
+ * Local prototypes
+ *****************************************************************************/
+
+/* updates handlers */
+
+static void desktopResizeHandler( rdpContext *p_context )
+{
+    vlcrdp_context_t * p_vlccontext = (vlcrdp_context_t *) p_context;
+    demux_sys_t *p_sys = p_vlccontext->p_demux->p_sys;
+    rdpGdi *p_gdi = p_context->gdi;
+
+    if ( p_sys->es )
+    {
+        es_out_Del( p_vlccontext->p_demux->out, p_sys->es );
+        p_sys->es = NULL;
+    }
+
+    /* Now init and fill es format */
+    vlc_fourcc_t i_chroma;
+    switch( p_gdi->bytesPerPixel )
+    {
+        default:
+        case 16:
+            i_chroma = VLC_CODEC_RGB16;
+            break;
+        case 24:
+            i_chroma = VLC_CODEC_RGB24;
+            break;
+        case 32:
+            i_chroma = VLC_CODEC_RGB32;
+            break;
+    }
+    es_format_Init( &p_sys->fmt, VIDEO_ES, i_chroma );
+
+    p_sys->fmt.video.i_chroma = i_chroma;
+    p_sys->fmt.video.i_visible_width =
+    p_sys->fmt.video.i_width = p_gdi->width;
+    p_sys->fmt.video.i_visible_height =
+    p_sys->fmt.video.i_height = p_gdi->height;
+    p_sys->i_framebuffersize = p_gdi->width * p_gdi->height * p_gdi->bytesPerPixel;
+
+    p_sys->es = es_out_Add( p_vlccontext->p_demux->out, &p_sys->fmt );
+}
+
+static void beginPaintHandler( rdpContext *p_context )
+{
+    VLC_UNUSED( p_context );
+    rdpGdi *p_gdi = p_context->gdi;
+    p_gdi->primary->hdc->hwnd->invalid->null = 1;
+    p_gdi->primary->hdc->hwnd->ninvalid = 0;
+}
+
+static void endPaintHandler( rdpContext *p_context )
+{
+    vlcrdp_context_t * p_vlccontext = (vlcrdp_context_t *) p_context;
+    demux_sys_t *p_sys = p_vlccontext->p_demux->p_sys;
+    rdpGdi *p_gdi = p_context->gdi;
+
+    if ( ! p_sys->p_block && p_sys->i_framebuffersize )
+    {
+        p_sys->p_block = block_Alloc( p_sys->i_framebuffersize );
+        if ( ! p_sys->p_block ) return;
+
+        p_sys->p_block->i_buffer = p_sys->i_framebuffersize;
+        memcpy( p_sys->p_block->p_buffer, p_gdi->primary_buffer, p_sys->p_block->i_buffer );
+    }
+}
+
+/* instance handlers */
+
+static bool preConnectHandler( freerdp *p_instance )
+{
+    vlcrdp_context_t * p_vlccontext = (vlcrdp_context_t *) p_instance->context;
+    demux_sys_t *p_sys = p_vlccontext->p_demux->p_sys;
+
+    /* Configure connexion */
+    p_instance->settings->sw_gdi = true; /* render in buffer */
+    p_instance->settings->fullscreen = true;
+    p_instance->settings->hostname = strdup( p_sys->psz_hostname );
+    p_instance->settings->username =
+            var_InheritString( p_vlccontext->p_demux, CFG_PREFIX "user" );
+    p_instance->settings->password =
+            var_InheritString( p_vlccontext->p_demux, CFG_PREFIX "password" );
+    p_instance->settings->port = p_sys->i_port;
+    p_instance->settings->encryption =
+            var_InheritBool( p_vlccontext->p_demux, CFG_PREFIX "encrypt" );
+
+    return true;
+}
+
+static bool postConnectHandler( freerdp *p_instance )
+{
+    vlcrdp_context_t * p_vlccontext = (vlcrdp_context_t *) p_instance->context;
+
+    msg_Dbg( p_vlccontext->p_demux, "connected to desktop %dx%d (%d bpp)",
+             p_instance->settings->width,
+             p_instance->settings->height,
+             p_instance->settings->color_depth );
+
+    p_instance->update->DesktopResize = desktopResizeHandler;
+    p_instance->update->BeginPaint = beginPaintHandler;
+    p_instance->update->EndPaint = endPaintHandler;
+
+    gdi_init( p_instance, CLRBUF_16BPP | CLRBUF_24BPP | CLRBUF_32BPP, NULL );
+
+    desktopResizeHandler( p_instance->context );
+    return true;
+}
+
+static bool authenticateHandler( freerdp *p_instance, char** ppsz_username,
+                                 char** ppsz_password, char** ppsz_domain )
+{
+    VLC_UNUSED(ppsz_domain);
+    vlcrdp_context_t * p_vlccontext = (vlcrdp_context_t *) p_instance->context;
+    *ppsz_username = var_InheritString( p_vlccontext->p_demux, CFG_PREFIX "user" );
+    *ppsz_password = var_InheritString( p_vlccontext->p_demux, CFG_PREFIX "password" );
+    return true;
+}
+
+/*****************************************************************************
+ * Control:
+ *****************************************************************************/
+static int Control( demux_t *p_demux, int i_query, va_list args )
+{
+    bool *pb;
+    int64_t *pi64;
+    double *p_dbl;
+    vlc_meta_t *p_meta;
+
+    switch( i_query )
+    {
+        case DEMUX_CAN_PAUSE:
+        case DEMUX_CAN_SEEK:
+        case DEMUX_CAN_CONTROL_PACE:
+        case DEMUX_CAN_CONTROL_RATE:
+        case DEMUX_HAS_UNSUPPORTED_META:
+            pb = (bool*)va_arg( args, bool * );
+            *pb = false;
+            return VLC_SUCCESS;
+
+        case DEMUX_CAN_RECORD:
+            pb = (bool*)va_arg( args, bool * );
+            *pb = true;
+            return VLC_SUCCESS;
+
+        case DEMUX_GET_PTS_DELAY:
+            pi64 = (int64_t*)va_arg( args, int64_t * );
+            *pi64 = INT64_C(1000)
+                  * var_InheritInteger( p_demux, "live-caching" );
+            return VLC_SUCCESS;
+
+        case DEMUX_GET_TIME:
+            pi64 = (int64_t*)va_arg( args, int64_t * );
+            *pi64 = mdate() - p_demux->p_sys->i_starttime;
+            return VLC_SUCCESS;
+
+        case DEMUX_GET_LENGTH:
+            pi64 = (int64_t*)va_arg( args, int64_t * );
+            *pi64 = 0;
+            return VLC_SUCCESS;
+
+        case DEMUX_GET_FPS:
+            p_dbl = (double*)va_arg( args, double * );
+            *p_dbl = p_demux->p_sys->f_fps;
+            return VLC_SUCCESS;
+
+        case DEMUX_GET_META:
+            p_meta = (vlc_meta_t*)va_arg( args, vlc_meta_t* );
+            vlc_meta_Set( p_meta, vlc_meta_Title, p_demux->psz_location );
+            return VLC_SUCCESS;
+
+        default:
+            return VLC_EGENERIC;
+    }
+}
+
+/*****************************************************************************
+ * Demux:
+ *****************************************************************************/
+static int Demux( demux_t *p_demux )
+{
+    demux_sys_t *p_sys = p_demux->p_sys;
+
+    if ( freerdp_shall_disconnect( p_sys->p_instance ) )
+    {
+        return 0; //EOF, will disconnect in Close()
+    }
+
+    struct
+    {
+        void* pp_rfds[RDP_MAX_FD]; /* Declared by rdp */
+        void* pp_wfds[RDP_MAX_FD];
+        int i_nbr;
+        int i_nbw;
+        struct pollfd ufds[RDP_MAX_FD];
+    } fds;
+
+    fds.i_nbr = fds.i_nbw = 0;
+
+    if ( freerdp_get_fds( p_sys->p_instance, fds.pp_rfds, &fds.i_nbr,
+                          fds.pp_wfds, &fds.i_nbw ) != true )
+    {
+        msg_Err( p_demux, "cannot get FDS" );
+    }
+    else
+    if ( (fds.i_nbr + fds.i_nbw) > 0 && p_sys->es )
+    {
+        int i_count = 0;
+
+        for( int i = 0; i < fds.i_nbr; i++ )
+        {
+            fds.ufds[ i_count ].fd = (long) fds.pp_rfds[ i ];
+            fds.ufds[ i_count ].events = POLLIN ;
+            fds.ufds[ i_count++ ].revents = 0;
+        }
+        for( int i = 0; i < fds.i_nbw && i_count < RDP_MAX_FD; i++ )
+        {   /* may be useless */
+            fds.ufds[ i_count ].fd = (long) fds.pp_wfds[ i ];
+            fds.ufds[ i_count ].events = POLLOUT;
+            fds.ufds[ i_count++ ].revents = 0;
+        }
+        int i_ret = poll( fds.ufds, i_count, p_sys->i_frame_interval / 2000 );
+        if ( i_ret >= 0 )
+        {
+            /* Do the rendering */
+            freerdp_check_fds( p_sys->p_instance );
+        }
+    }
+
+    if ( p_sys->p_block )
+    {
+        block_t *p_block = p_sys->p_block;
+        p_sys->p_block = NULL;
+
+        if ( p_sys->i_starttime == 0 ) p_sys->i_starttime = mdate();
+
+        p_block->i_dts = p_block->i_pts = mdate() - p_sys->i_starttime;
+
+        es_out_Control( p_demux->out, ES_OUT_SET_PCR, p_block->i_pts );
+        es_out_Send( p_demux->out, p_sys->es, p_block );
+    }
+
+    return 1;
+}
+
+/*****************************************************************************
+ * Open:
+ *****************************************************************************/
+static int Open( vlc_object_t *p_this )
+{
+    demux_t      *p_demux = (demux_t*)p_this;
+    demux_sys_t  *p_sys;
+
+    if( strncmp( p_demux->psz_access, "rdp", 3 ) != 0 )
+        return VLC_EGENERIC;
+
+    p_sys = calloc( 1, sizeof(demux_sys_t) );
+    if( !p_sys ) return VLC_ENOMEM;
+
+    p_sys->f_fps = var_InheritInteger( p_demux, CFG_PREFIX "fps" );
+    p_sys->i_frame_interval = 1000000 / p_sys->f_fps;
+
+    /* Parse uri params */
+    vlc_url_t url;
+    vlc_UrlParse( &url, p_demux->psz_location, 0 );
+
+    if ( !EMPTY_STR(url.psz_host) )
+        p_sys->psz_hostname = strdup( url.psz_host );
+    else
+        p_sys->psz_hostname = strdup( "localhost" );
+
+    p_sys->i_port = ( url.i_port > 0 ) ? url.i_port : 3389;
+
+    vlc_UrlClean( &url );
+
+    p_demux->p_sys = p_sys;
+
+    freerdp_channels_global_init();
+
+    p_sys->p_instance = freerdp_new();
+    if ( !p_sys->p_instance )
+    {
+        msg_Err( p_demux, "rdp instanciation error" );
+        free( p_sys );
+        p_demux->p_sys = NULL;
+        return VLC_EGENERIC;
+    }
+
+    p_sys->p_instance->PreConnect = preConnectHandler;
+    p_sys->p_instance->PostConnect = postConnectHandler;
+    p_sys->p_instance->Authenticate = authenticateHandler;
+
+    /* Set up context handlers and let it be allocated */
+    p_sys->p_instance->context_size = sizeof( vlcrdp_context_t );
+    freerdp_context_new( p_sys->p_instance );
+
+    vlcrdp_context_t * p_vlccontext = (vlcrdp_context_t *) p_sys->p_instance->context;
+    p_vlccontext->p_demux = p_demux;
+
+    if ( ! freerdp_connect( p_sys->p_instance ) )
+    {
+        msg_Err( p_demux, "rdp connect error" );
+        freerdp_free( p_sys->p_instance );
+        free( p_sys->psz_hostname );
+        free( p_sys );
+        p_demux->p_sys = NULL;
+        return VLC_EGENERIC;
+    }
+
+    p_demux->pf_demux = Demux;
+    p_demux->pf_control = Control;
+
+    return VLC_SUCCESS;
+}
+
+/*****************************************************************************
+ * Close:
+ *****************************************************************************/
+static void Close( vlc_object_t *p_this )
+{
+    demux_t     *p_demux = (demux_t*)p_this;
+    demux_sys_t *p_sys = p_demux->p_sys;
+
+    if ( p_sys->es )
+        es_out_Del( p_demux->out, p_sys->es );
+
+    freerdp_disconnect( p_sys->p_instance );
+    freerdp_free( p_sys->p_instance );
+
+    freerdp_channels_global_uninit();
+
+    free( p_sys->psz_hostname );
+    free( p_sys );
+}
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 1762f1f..d6ae8e1 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -235,6 +235,7 @@ modules/access/rar/access.c
 modules/access/rar/rar.c
 modules/access/rar/rar.h
 modules/access/rar/stream.c
+modules/access/rdp.c
 modules/access/rtp/input.c
 modules/access/rtp/rtp.c
 modules/access/rtp/rtp.h
-- 
1.7.9




More information about the vlc-devel mailing list