1 /*****************************************************************************
2 * rdp.c: libfreeRDP based Remote Desktop access
3 *****************************************************************************
4 * Copyright (C) 2013 VideoLAN and VLC 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>
44 #if !defined(FREERDP_INTERFACE_VERSION)
45 # include <freerdp/version.h>
48 #if !defined(FREERDP_VERSION_MAJOR) || \
49 (defined(FREERDP_VERSION_MAJOR) && !(FREERDP_VERSION_MAJOR > 1 || (FREERDP_VERSION_MAJOR == 1 && FREERDP_VERSION_MINOR >= 1)))
50 # define SoftwareGdi sw_gdi
51 # define Fullscreen fullscreen
52 # define ServerHostname hostname
53 # define Username username
54 # define Password password
55 # define ServerPort port
56 # define EncryptionMethods encryption
57 # define ContextSize context_size
65 #define USER_TEXT N_("Username")
66 #define USER_LONGTEXT N_("Username that will be used for the connection, " \
67 "if no username is set in the URL.")
68 #define PASS_TEXT N_("Password")
69 #define PASS_LONGTEXT N_("Password that will be used for the connection, " \
70 "if no username or password are set in URL.")
72 #define RDP_ENCRYPT N_("Encrypted connexion")
73 #define RDP_FPS N_("Frame rate")
74 #define RDP_FPS_LONGTEXT N_("Acquisition rate (in fps)")
76 #define CFG_PREFIX "rdp-"
78 /*****************************************************************************
80 *****************************************************************************/
81 static int Open ( vlc_object_t
* );
82 static void Close( vlc_object_t
* );
85 set_shortname( N_("RDP") )
87 set_category( CAT_INPUT
)
88 set_subcategory( SUBCAT_INPUT_ACCESS
)
89 set_description( N_("RDP Remote Desktop") )
90 set_capability( "access", 0 )
92 add_string( CFG_PREFIX
"user", NULL
, USER_TEXT
, USER_LONGTEXT
, false )
94 add_password(CFG_PREFIX
"password", NULL
, PASS_TEXT
, PASS_LONGTEXT
)
96 add_float( CFG_PREFIX
"fps", 5, RDP_FPS
, RDP_FPS_LONGTEXT
, true )
98 add_bool( CFG_PREFIX
"encrypt", false, RDP_ENCRYPT
, RDP_ENCRYPT
, true )
101 set_callbacks( Open
, Close
)
104 #define RDP_MAX_FD 32
111 int i_framebuffersize
;
114 int i_frame_interval
;
115 vlc_tick_t i_starttime
;
119 /* pre-connect params */
128 struct vlcrdp_context_t
130 rdpContext rdp_context
; /* Extending API's struct */
132 rdpSettings
* p_settings
;
134 typedef struct vlcrdp_context_t vlcrdp_context_t
;
136 /*****************************************************************************
138 *****************************************************************************/
140 /* updates handlers */
142 static void desktopResizeHandler( rdpContext
*p_context
)
144 vlcrdp_context_t
* p_vlccontext
= (vlcrdp_context_t
*) p_context
;
145 demux_sys_t
*p_sys
= p_vlccontext
->p_demux
->p_sys
;
146 rdpGdi
*p_gdi
= p_context
->gdi
;
150 es_out_Del( p_vlccontext
->p_demux
->out
, p_sys
->es
);
154 /* Now init and fill es format */
155 vlc_fourcc_t i_chroma
;
156 switch( p_gdi
->bytesPerPixel
)
160 i_chroma
= VLC_CODEC_RGB16
;
163 i_chroma
= VLC_CODEC_RGB24
;
166 i_chroma
= VLC_CODEC_RGB32
;
170 es_format_Init( &fmt
, VIDEO_ES
, i_chroma
);
172 fmt
.video
.i_chroma
= i_chroma
;
173 fmt
.video
.i_visible_width
=
174 fmt
.video
.i_width
= p_gdi
->width
;
175 fmt
.video
.i_visible_height
=
176 fmt
.video
.i_height
= p_gdi
->height
;
177 fmt
.video
.i_frame_rate_base
= 1000;
178 fmt
.video
.i_frame_rate
= 1000 * p_sys
->f_fps
;
179 p_sys
->i_framebuffersize
= p_gdi
->width
* p_gdi
->height
* p_gdi
->bytesPerPixel
;
181 if ( p_sys
->p_block
)
182 p_sys
->p_block
= block_Realloc( p_sys
->p_block
, 0, p_sys
->i_framebuffersize
);
184 p_sys
->p_block
= block_Alloc( p_sys
->i_framebuffersize
);
186 p_sys
->es
= es_out_Add( p_vlccontext
->p_demux
->out
, &fmt
);
189 static void beginPaintHandler( rdpContext
*p_context
)
191 vlcrdp_context_t
* p_vlccontext
= (vlcrdp_context_t
*) p_context
;
192 demux_sys_t
*p_sys
= p_vlccontext
->p_demux
->p_sys
;
193 rdpGdi
*p_gdi
= p_context
->gdi
;
194 p_gdi
->primary
->hdc
->hwnd
->invalid
->null
= 1;
195 p_gdi
->primary
->hdc
->hwnd
->ninvalid
= 0;
196 if ( ! p_sys
->p_block
&& p_sys
->i_framebuffersize
)
197 p_sys
->p_block
= block_Alloc( p_sys
->i_framebuffersize
);
200 static void endPaintHandler( rdpContext
*p_context
)
202 vlcrdp_context_t
* p_vlccontext
= (vlcrdp_context_t
*) p_context
;
203 demux_sys_t
*p_sys
= p_vlccontext
->p_demux
->p_sys
;
204 rdpGdi
*p_gdi
= p_context
->gdi
;
206 if ( p_sys
->p_block
)
208 p_sys
->p_block
->i_buffer
= p_sys
->i_framebuffersize
;
209 memcpy( p_sys
->p_block
->p_buffer
, p_gdi
->primary_buffer
, p_sys
->p_block
->i_buffer
);
213 /* instance handlers */
215 static bool preConnectHandler( freerdp
*p_instance
)
217 vlcrdp_context_t
* p_vlccontext
= (vlcrdp_context_t
*) p_instance
->context
;
218 demux_sys_t
*p_sys
= p_vlccontext
->p_demux
->p_sys
;
220 /* Configure connexion */
221 p_instance
->settings
->SoftwareGdi
= true; /* render in buffer */
222 p_instance
->settings
->Fullscreen
= true;
223 p_instance
->settings
->ServerHostname
= strdup( p_sys
->psz_hostname
);
224 p_instance
->settings
->Username
=
225 var_InheritString( p_vlccontext
->p_demux
, CFG_PREFIX
"user" );
226 p_instance
->settings
->Password
=
227 var_InheritString( p_vlccontext
->p_demux
, CFG_PREFIX
"password" );
228 p_instance
->settings
->ServerPort
= p_sys
->i_port
;
229 p_instance
->settings
->EncryptionMethods
=
230 var_InheritBool( p_vlccontext
->p_demux
, CFG_PREFIX
"encrypt" );
235 static bool postConnectHandler( freerdp
*p_instance
)
237 vlcrdp_context_t
* p_vlccontext
= (vlcrdp_context_t
*) p_instance
->context
;
239 msg_Dbg( p_vlccontext
->p_demux
, "connected to desktop %dx%d (%d bpp)",
240 #if defined(FREERDP_VERSION_MAJOR) && (FREERDP_VERSION_MAJOR > 1 || (FREERDP_VERSION_MAJOR == 1 && FREERDP_VERSION_MINOR >= 1))
241 p_instance
->settings
->DesktopWidth
,
242 p_instance
->settings
->DesktopHeight
,
243 p_instance
->settings
->ColorDepth
245 p_instance
->settings
->width
,
246 p_instance
->settings
->height
,
247 p_instance
->settings
->color_depth
251 p_instance
->update
->DesktopResize
= desktopResizeHandler
;
252 p_instance
->update
->BeginPaint
= beginPaintHandler
;
253 p_instance
->update
->EndPaint
= endPaintHandler
;
255 gdi_init( p_instance
,
257 #if defined(FREERDP_VERSION_MAJOR) && defined(FREERDP_VERSION_MINOR) && \
258 !(FREERDP_VERSION_MAJOR > 1 || (FREERDP_VERSION_MAJOR == 1 && FREERDP_VERSION_MINOR >= 2))
261 CLRBUF_32BPP
, NULL
);
263 desktopResizeHandler( p_instance
->context
);
267 static bool authenticateHandler( freerdp
*p_instance
, char** ppsz_username
,
268 char** ppsz_password
, char** ppsz_domain
)
270 VLC_UNUSED(ppsz_domain
);
271 vlcrdp_context_t
* p_vlccontext
= (vlcrdp_context_t
*) p_instance
->context
;
272 *ppsz_username
= var_InheritString( p_vlccontext
->p_demux
, CFG_PREFIX
"user" );
273 *ppsz_password
= var_InheritString( p_vlccontext
->p_demux
, CFG_PREFIX
"password" );
277 /*****************************************************************************
279 *****************************************************************************/
280 static int Control( demux_t
*p_demux
, int i_query
, va_list args
)
282 demux_sys_t
*p_sys
= p_demux
->p_sys
;
290 case DEMUX_CAN_PAUSE
:
292 case DEMUX_CAN_CONTROL_PACE
:
293 case DEMUX_CAN_CONTROL_RATE
:
294 case DEMUX_HAS_UNSUPPORTED_META
:
295 pb
= va_arg( args
, bool * );
299 case DEMUX_CAN_RECORD
:
300 pb
= va_arg( args
, bool * );
304 case DEMUX_GET_PTS_DELAY
:
305 *va_arg( args
, vlc_tick_t
* ) =
306 VLC_TICK_FROM_MS(var_InheritInteger( p_demux
, "live-caching" ));
310 pi64
= va_arg( args
, int64_t * );
311 *pi64
= vlc_tick_now() - p_sys
->i_starttime
;
314 case DEMUX_GET_LENGTH
:
315 pi64
= va_arg( args
, int64_t * );
320 p_dbl
= va_arg( args
, double * );
321 *p_dbl
= p_sys
->f_fps
;
325 p_meta
= va_arg( args
, vlc_meta_t
* );
326 vlc_meta_Set( p_meta
, vlc_meta_Title
, p_demux
->psz_location
);
334 /*****************************************************************************
336 *****************************************************************************/
337 static void *DemuxThread( void *p_data
)
339 demux_t
*p_demux
= (demux_t
*) p_data
;
340 demux_sys_t
*p_sys
= p_demux
->p_sys
;
341 p_sys
->i_starttime
= vlc_tick_now();
342 vlc_tick_t i_next_frame_date
= vlc_tick_now() + p_sys
->i_frame_interval
;
348 p_sys
->i_cancel_state
= vlc_savecancel();
349 if ( freerdp_shall_disconnect( p_sys
->p_instance
) )
351 vlc_restorecancel( p_sys
->i_cancel_state
);
352 msg_Warn( p_demux
, "RDP server closed session" );
353 es_out_Del( p_demux
->out
, p_sys
->es
);
360 void* pp_rfds
[RDP_MAX_FD
]; /* Declared by rdp */
361 void* pp_wfds
[RDP_MAX_FD
];
364 struct pollfd ufds
[RDP_MAX_FD
];
367 fds
.i_nbr
= fds
.i_nbw
= 0;
369 if ( freerdp_get_fds( p_sys
->p_instance
, fds
.pp_rfds
, &fds
.i_nbr
,
370 fds
.pp_wfds
, &fds
.i_nbw
) != true )
372 vlc_restorecancel( p_sys
->i_cancel_state
);
373 msg_Err( p_demux
, "cannot get FDS" );
376 if ( (fds
.i_nbr
+ fds
.i_nbw
) > 0 && p_sys
->es
)
378 vlc_restorecancel( p_sys
->i_cancel_state
);
381 for( int i
= 0; i
< fds
.i_nbr
; i
++ )
383 fds
.ufds
[ i_count
].fd
= (long) fds
.pp_rfds
[ i
];
384 fds
.ufds
[ i_count
].events
= POLLIN
;
385 fds
.ufds
[ i_count
++ ].revents
= 0;
387 for( int i
= 0; i
< fds
.i_nbw
&& i_count
< RDP_MAX_FD
; i
++ )
388 { /* may be useless */
389 fds
.ufds
[ i_count
].fd
= (long) fds
.pp_wfds
[ i
];
390 fds
.ufds
[ i_count
].events
= POLLOUT
;
391 fds
.ufds
[ i_count
++ ].revents
= 0;
393 i_ret
= poll( fds
.ufds
, i_count
, p_sys
->i_frame_interval
* 1000/2 );
395 vlc_restorecancel( p_sys
->i_cancel_state
);
398 vlc_tick_wait( i_next_frame_date
);
399 i_next_frame_date
+= p_sys
->i_frame_interval
;
403 /* Do the rendering */
404 p_sys
->i_cancel_state
= vlc_savecancel();
405 freerdp_check_fds( p_sys
->p_instance
);
406 vlc_restorecancel( p_sys
->i_cancel_state
);
407 block_t
*p_block
= block_Duplicate( p_sys
->p_block
);
408 if (likely( p_block
&& p_sys
->p_block
))
410 p_sys
->p_block
->i_dts
= p_sys
->p_block
->i_pts
= vlc_tick_now() - p_sys
->i_starttime
;
411 es_out_SetPCR( p_demux
->out
, p_sys
->p_block
->i_pts
);
412 es_out_Send( p_demux
->out
, p_sys
->es
, p_sys
->p_block
);
413 p_sys
->p_block
= p_block
;
420 /*****************************************************************************
422 *****************************************************************************/
423 static int Open( vlc_object_t
*p_this
)
425 demux_t
*p_demux
= (demux_t
*)p_this
;
428 if (p_demux
->out
== NULL
)
431 p_sys
= vlc_obj_calloc( p_this
, 1, sizeof(demux_sys_t
) );
432 if( !p_sys
) return VLC_ENOMEM
;
434 p_sys
->f_fps
= var_InheritFloat( p_demux
, CFG_PREFIX
"fps" );
435 if ( p_sys
->f_fps
<= 0 ) p_sys
->f_fps
= 1.0;
436 p_sys
->i_frame_interval
= CLOCK_FREQ
/ p_sys
->f_fps
;
438 #if FREERDP_VERSION_MAJOR == 1 && FREERDP_VERSION_MINOR < 2
439 freerdp_channels_global_init();
442 p_sys
->p_instance
= freerdp_new();
443 if ( !p_sys
->p_instance
)
445 msg_Err( p_demux
, "rdp instantiation error" );
449 p_demux
->p_sys
= p_sys
;
450 p_sys
->p_instance
->PreConnect
= preConnectHandler
;
451 p_sys
->p_instance
->PostConnect
= postConnectHandler
;
452 p_sys
->p_instance
->Authenticate
= authenticateHandler
;
454 /* Set up context handlers and let it be allocated */
455 p_sys
->p_instance
->ContextSize
= sizeof( vlcrdp_context_t
);
456 freerdp_context_new( p_sys
->p_instance
);
458 vlcrdp_context_t
* p_vlccontext
= (vlcrdp_context_t
*) p_sys
->p_instance
->context
;
459 p_vlccontext
->p_demux
= p_demux
;
461 /* Parse uri params for pre-connect */
463 vlc_UrlParse( &url
, p_demux
->psz_location
);
465 if ( !EMPTY_STR(url
.psz_host
) )
466 p_sys
->psz_hostname
= strdup( url
.psz_host
);
468 p_sys
->psz_hostname
= strdup( "localhost" );
470 p_sys
->i_port
= ( url
.i_port
> 0 ) ? url
.i_port
: 3389;
472 vlc_UrlClean( &url
);
474 if ( ! freerdp_connect( p_sys
->p_instance
) )
476 msg_Err( p_demux
, "can't connect to rdp server" );
480 if ( vlc_clone( &p_sys
->thread
, DemuxThread
, p_demux
, VLC_THREAD_PRIORITY_INPUT
) != VLC_SUCCESS
)
482 msg_Err( p_demux
, "can't spawn thread" );
483 freerdp_disconnect( p_sys
->p_instance
);
487 p_demux
->pf_demux
= NULL
;
488 p_demux
->pf_control
= Control
;
493 freerdp_free( p_sys
->p_instance
);
494 free( p_sys
->psz_hostname
);
498 /*****************************************************************************
500 *****************************************************************************/
501 static void Close( vlc_object_t
*p_this
)
503 demux_t
*p_demux
= (demux_t
*)p_this
;
504 demux_sys_t
*p_sys
= p_demux
->p_sys
;
506 vlc_cancel( p_sys
->thread
);
507 vlc_join( p_sys
->thread
, NULL
);
510 es_out_Del( p_demux
->out
, p_sys
->es
);
512 freerdp_disconnect( p_sys
->p_instance
);
513 freerdp_free( p_sys
->p_instance
);
514 #if FREERDP_VERSION_MAJOR == 1 && FREERDP_VERSION_MINOR < 2
515 freerdp_channels_global_uninit();
518 if ( p_sys
->p_block
)
519 block_Release( p_sys
->p_block
);
521 free( p_sys
->psz_hostname
);