1 /*****************************************************************************
2 * rdp.c: libfreeRDP based Remote Desktop access
3 *****************************************************************************
4 * Copyright (C) 2013 VideoLAN Authors
5 *****************************************************************************
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU Lesser General Public License as published by
9 * the Free Software Foundation; either version 2.1 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public License
18 * along with this program. If not, see <http://www.gnu.org/licenses/>.
19 *****************************************************************************/
21 /*****************************************************************************
23 *****************************************************************************/
29 #include <vlc_common.h>
30 #include <vlc_plugin.h>
31 #include <vlc_demux.h>
37 /* see MS-RDPBCGR http://msdn.microsoft.com/en-us/library/cc240445.aspx */
39 #include <freerdp/freerdp.h>
40 #include <freerdp/settings.h>
41 #include <freerdp/channels/channels.h>
42 #include <freerdp/gdi/gdi.h>
49 #define RDP_USER N_("RDP auth username")
50 #define RDP_PASSWORD N_("RDP auth password")
51 #define RDP_PASSWORD_LONGTEXT N_("RDP Password")
52 #define RDP_ENCRYPT N_("Encrypted connexion")
53 #define RDP_FPS N_("Frame rate")
54 #define RDP_FPS_LONGTEXT N_("Acquisition rate (in fps)")
56 #define CFG_PREFIX "rdp-"
58 /*****************************************************************************
60 *****************************************************************************/
61 static int Open ( vlc_object_t * );
62 static void Close( vlc_object_t * );
65 set_shortname( N_("RDP") )
67 set_category( CAT_INPUT )
68 set_subcategory( SUBCAT_INPUT_ACCESS )
69 set_description( N_("RDP Remote Desktop") )
70 set_capability( "access_demux", 10 )
72 add_string( CFG_PREFIX "user", NULL, RDP_USER, RDP_USER, false )
74 add_password( CFG_PREFIX "password", NULL, RDP_PASSWORD, RDP_PASSWORD_LONGTEXT, false )
76 add_float( CFG_PREFIX "fps", 5, RDP_FPS, RDP_FPS_LONGTEXT, true )
78 add_bool( CFG_PREFIX "encrypt", false, RDP_ENCRYPT, RDP_ENCRYPT, true )
81 set_callbacks( Open, Close )
91 int i_framebuffersize;
99 /* pre-connect params */
108 struct vlcrdp_context_t
110 rdpContext rdp_context; /* Extending API's struct */
112 rdpSettings* p_settings;
114 typedef struct vlcrdp_context_t vlcrdp_context_t;
116 /*****************************************************************************
118 *****************************************************************************/
120 /* updates handlers */
122 static void desktopResizeHandler( rdpContext *p_context )
124 vlcrdp_context_t * p_vlccontext = (vlcrdp_context_t *) p_context;
125 demux_sys_t *p_sys = p_vlccontext->p_demux->p_sys;
126 rdpGdi *p_gdi = p_context->gdi;
130 es_out_Del( p_vlccontext->p_demux->out, p_sys->es );
134 /* Now init and fill es format */
135 vlc_fourcc_t i_chroma;
136 switch( p_gdi->bytesPerPixel )
140 i_chroma = VLC_CODEC_RGB16;
143 i_chroma = VLC_CODEC_RGB24;
146 i_chroma = VLC_CODEC_RGB32;
150 es_format_Init( &fmt, VIDEO_ES, i_chroma );
152 fmt.video.i_chroma = i_chroma;
153 fmt.video.i_visible_width =
154 fmt.video.i_width = p_gdi->width;
155 fmt.video.i_visible_height =
156 fmt.video.i_height = p_gdi->height;
157 fmt.video.i_frame_rate_base = 1000;
158 fmt.video.i_frame_rate = 1000 * p_sys->f_fps;
159 p_sys->i_framebuffersize = p_gdi->width * p_gdi->height * p_gdi->bytesPerPixel;
161 if ( p_sys->p_block )
162 p_sys->p_block = block_Realloc( p_sys->p_block, 0, p_sys->i_framebuffersize );
164 p_sys->p_block = block_Alloc( p_sys->i_framebuffersize );
166 p_sys->es = es_out_Add( p_vlccontext->p_demux->out, &fmt );
169 static void beginPaintHandler( rdpContext *p_context )
171 vlcrdp_context_t * p_vlccontext = (vlcrdp_context_t *) p_context;
172 demux_sys_t *p_sys = p_vlccontext->p_demux->p_sys;
173 rdpGdi *p_gdi = p_context->gdi;
174 p_gdi->primary->hdc->hwnd->invalid->null = 1;
175 p_gdi->primary->hdc->hwnd->ninvalid = 0;
176 if ( ! p_sys->p_block && p_sys->i_framebuffersize )
177 p_sys->p_block = block_Alloc( p_sys->i_framebuffersize );
180 static void endPaintHandler( rdpContext *p_context )
182 vlcrdp_context_t * p_vlccontext = (vlcrdp_context_t *) p_context;
183 demux_sys_t *p_sys = p_vlccontext->p_demux->p_sys;
184 rdpGdi *p_gdi = p_context->gdi;
186 if ( p_sys->p_block )
188 p_sys->p_block->i_buffer = p_sys->i_framebuffersize;
189 memcpy( p_sys->p_block->p_buffer, p_gdi->primary_buffer, p_sys->p_block->i_buffer );
193 /* instance handlers */
195 static bool preConnectHandler( freerdp *p_instance )
197 vlcrdp_context_t * p_vlccontext = (vlcrdp_context_t *) p_instance->context;
198 demux_sys_t *p_sys = p_vlccontext->p_demux->p_sys;
200 /* Configure connexion */
201 p_instance->settings->sw_gdi = true; /* render in buffer */
202 p_instance->settings->fullscreen = true;
203 p_instance->settings->hostname = strdup( p_sys->psz_hostname );
204 p_instance->settings->username =
205 var_InheritString( p_vlccontext->p_demux, CFG_PREFIX "user" );
206 p_instance->settings->password =
207 var_InheritString( p_vlccontext->p_demux, CFG_PREFIX "password" );
208 p_instance->settings->port = p_sys->i_port;
209 p_instance->settings->encryption =
210 var_InheritBool( p_vlccontext->p_demux, CFG_PREFIX "encrypt" );
215 static bool postConnectHandler( freerdp *p_instance )
217 vlcrdp_context_t * p_vlccontext = (vlcrdp_context_t *) p_instance->context;
219 msg_Dbg( p_vlccontext->p_demux, "connected to desktop %dx%d (%d bpp)",
220 p_instance->settings->width,
221 p_instance->settings->height,
222 p_instance->settings->color_depth );
224 p_instance->update->DesktopResize = desktopResizeHandler;
225 p_instance->update->BeginPaint = beginPaintHandler;
226 p_instance->update->EndPaint = endPaintHandler;
228 gdi_init( p_instance, CLRBUF_16BPP | CLRBUF_24BPP | CLRBUF_32BPP, NULL );
230 desktopResizeHandler( p_instance->context );
234 static bool authenticateHandler( freerdp *p_instance, char** ppsz_username,
235 char** ppsz_password, char** ppsz_domain )
237 VLC_UNUSED(ppsz_domain);
238 vlcrdp_context_t * p_vlccontext = (vlcrdp_context_t *) p_instance->context;
239 *ppsz_username = var_InheritString( p_vlccontext->p_demux, CFG_PREFIX "user" );
240 *ppsz_password = var_InheritString( p_vlccontext->p_demux, CFG_PREFIX "password" );
244 /*****************************************************************************
246 *****************************************************************************/
247 static int Control( demux_t *p_demux, int i_query, va_list args )
256 case DEMUX_CAN_PAUSE:
258 case DEMUX_CAN_CONTROL_PACE:
259 case DEMUX_CAN_CONTROL_RATE:
260 case DEMUX_HAS_UNSUPPORTED_META:
261 pb = (bool*)va_arg( args, bool * );
265 case DEMUX_CAN_RECORD:
266 pb = (bool*)va_arg( args, bool * );
270 case DEMUX_GET_PTS_DELAY:
271 pi64 = (int64_t*)va_arg( args, int64_t * );
272 *pi64 = INT64_C(1000)
273 * var_InheritInteger( p_demux, "live-caching" );
277 pi64 = (int64_t*)va_arg( args, int64_t * );
278 *pi64 = mdate() - p_demux->p_sys->i_starttime;
281 case DEMUX_GET_LENGTH:
282 pi64 = (int64_t*)va_arg( args, int64_t * );
287 p_dbl = (double*)va_arg( args, double * );
288 *p_dbl = p_demux->p_sys->f_fps;
292 p_meta = (vlc_meta_t*)va_arg( args, vlc_meta_t* );
293 vlc_meta_Set( p_meta, vlc_meta_Title, p_demux->psz_location );
301 /*****************************************************************************
303 *****************************************************************************/
304 static void *DemuxThread( void *p_data )
306 demux_t *p_demux = (demux_t *) p_data;
307 demux_sys_t *p_sys = p_demux->p_sys;
308 p_sys->i_starttime = mdate();
309 mtime_t i_next_frame_date = mdate() + p_sys->i_frame_interval;
315 p_sys->i_cancel_state = vlc_savecancel();
316 if ( freerdp_shall_disconnect( p_sys->p_instance ) )
318 vlc_restorecancel( p_sys->i_cancel_state );
319 msg_Warn( p_demux, "RDP server closed session" );
320 es_out_Del( p_demux->out, p_sys->es );
327 void* pp_rfds[RDP_MAX_FD]; /* Declared by rdp */
328 void* pp_wfds[RDP_MAX_FD];
331 struct pollfd ufds[RDP_MAX_FD];
334 fds.i_nbr = fds.i_nbw = 0;
336 if ( freerdp_get_fds( p_sys->p_instance, fds.pp_rfds, &fds.i_nbr,
337 fds.pp_wfds, &fds.i_nbw ) != true )
339 vlc_restorecancel( p_sys->i_cancel_state );
340 msg_Err( p_demux, "cannot get FDS" );
343 if ( (fds.i_nbr + fds.i_nbw) > 0 && p_sys->es )
345 vlc_restorecancel( p_sys->i_cancel_state );
348 for( int i = 0; i < fds.i_nbr; i++ )
350 fds.ufds[ i_count ].fd = (long) fds.pp_rfds[ i ];
351 fds.ufds[ i_count ].events = POLLIN ;
352 fds.ufds[ i_count++ ].revents = 0;
354 for( int i = 0; i < fds.i_nbw && i_count < RDP_MAX_FD; i++ )
355 { /* may be useless */
356 fds.ufds[ i_count ].fd = (long) fds.pp_wfds[ i ];
357 fds.ufds[ i_count ].events = POLLOUT;
358 fds.ufds[ i_count++ ].revents = 0;
360 i_ret = poll( fds.ufds, i_count, p_sys->i_frame_interval * 1000/2 );
362 vlc_restorecancel( p_sys->i_cancel_state );
365 mwait( i_next_frame_date );
366 i_next_frame_date += p_sys->i_frame_interval;
370 /* Do the rendering */
371 p_sys->i_cancel_state = vlc_savecancel();
372 freerdp_check_fds( p_sys->p_instance );
373 vlc_restorecancel( p_sys->i_cancel_state );
374 block_t *p_block = block_Duplicate( p_sys->p_block );
375 if (likely( p_block && p_sys->p_block ))
377 p_sys->p_block->i_dts = p_sys->p_block->i_pts = mdate() - p_sys->i_starttime;
378 es_out_Control( p_demux->out, ES_OUT_SET_PCR, p_sys->p_block->i_pts );
379 es_out_Send( p_demux->out, p_sys->es, p_sys->p_block );
380 p_sys->p_block = p_block;
387 /*****************************************************************************
389 *****************************************************************************/
390 static int Open( vlc_object_t *p_this )
392 demux_t *p_demux = (demux_t*)p_this;
395 p_sys = calloc( 1, sizeof(demux_sys_t) );
396 if( !p_sys ) return VLC_ENOMEM;
398 p_sys->f_fps = var_InheritFloat( p_demux, CFG_PREFIX "fps" );
399 if ( p_sys->f_fps <= 0 ) p_sys->f_fps = 1.0;
400 p_sys->i_frame_interval = 1000000 / p_sys->f_fps;
402 freerdp_channels_global_init();
404 p_sys->p_instance = freerdp_new();
405 if ( !p_sys->p_instance )
407 msg_Err( p_demux, "rdp instanciation error" );
412 p_demux->p_sys = p_sys;
413 p_sys->p_instance->PreConnect = preConnectHandler;
414 p_sys->p_instance->PostConnect = postConnectHandler;
415 p_sys->p_instance->Authenticate = authenticateHandler;
417 /* Set up context handlers and let it be allocated */
418 p_sys->p_instance->context_size = sizeof( vlcrdp_context_t );
419 freerdp_context_new( p_sys->p_instance );
421 vlcrdp_context_t * p_vlccontext = (vlcrdp_context_t *) p_sys->p_instance->context;
422 p_vlccontext->p_demux = p_demux;
424 /* Parse uri params for pre-connect */
426 vlc_UrlParse( &url, p_demux->psz_location, 0 );
428 if ( !EMPTY_STR(url.psz_host) )
429 p_sys->psz_hostname = strdup( url.psz_host );
431 p_sys->psz_hostname = strdup( "localhost" );
433 p_sys->i_port = ( url.i_port > 0 ) ? url.i_port : 3389;
435 vlc_UrlClean( &url );
437 if ( ! freerdp_connect( p_sys->p_instance ) )
439 msg_Err( p_demux, "can't connect to rdp server" );
443 if ( vlc_clone( &p_sys->thread, DemuxThread, p_demux, VLC_THREAD_PRIORITY_INPUT ) != VLC_SUCCESS )
445 msg_Err( p_demux, "can't spawn thread" );
446 freerdp_disconnect( p_sys->p_instance );
450 p_demux->pf_demux = NULL;
451 p_demux->pf_control = Control;
456 freerdp_free( p_sys->p_instance );
457 free( p_sys->psz_hostname );
462 /*****************************************************************************
464 *****************************************************************************/
465 static void Close( vlc_object_t *p_this )
467 demux_t *p_demux = (demux_t*)p_this;
468 demux_sys_t *p_sys = p_demux->p_sys;
470 vlc_cancel( p_sys->thread );
471 vlc_join( p_sys->thread, NULL );
474 es_out_Del( p_demux->out, p_sys->es );
476 freerdp_disconnect( p_sys->p_instance );
477 freerdp_free( p_sys->p_instance );
478 freerdp_channels_global_uninit();
480 if ( p_sys->p_block )
481 block_Release( p_sys->p_block );
483 free( p_sys->psz_hostname );