]> git.sesse.net Git - vlc/blob - modules/access/rdp.c
android_window: check subtitles bounds
[vlc] / modules / access / rdp.c
1 /*****************************************************************************
2  * rdp.c: libfreeRDP based Remote Desktop access
3  *****************************************************************************
4  * Copyright (C) 2013 VideoLAN Authors
5  *****************************************************************************
6  *
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.
11  *
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.
16  *
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  *****************************************************************************/
20
21 /*****************************************************************************
22  * Preamble
23  *****************************************************************************/
24
25 #ifdef HAVE_CONFIG_H
26 # include "config.h"
27 #endif
28
29 #include <vlc_common.h>
30 #include <vlc_plugin.h>
31 #include <vlc_demux.h>
32 #include <vlc_url.h>
33 #include <vlc_meta.h>
34
35 #define boolean bool
36
37 /* see MS-RDPBCGR http://msdn.microsoft.com/en-us/library/cc240445.aspx */
38
39 #include <freerdp/freerdp.h>
40 #include <freerdp/settings.h>
41 #include <freerdp/channels/channels.h>
42 #include <freerdp/gdi/gdi.h>
43
44 #if !defined(FREERDP_INTERFACE_VERSION)
45 # include <freerdp/version.h>
46 #endif
47
48 #if !defined(FREERDP_VERSION_MAJOR) || \
49     (defined(FREERDP_VERSION_MAJOR) && !(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
58 #endif
59
60 #include <errno.h>
61 #ifdef HAVE_POLL
62 # include <poll.h>
63 #endif
64
65 #define RDP_USER N_("RDP auth username")
66 #define RDP_PASSWORD N_("RDP auth password")
67 #define RDP_PASSWORD_LONGTEXT N_("RDP Password")
68 #define RDP_ENCRYPT N_("Encrypted connexion")
69 #define RDP_FPS N_("Frame rate")
70 #define RDP_FPS_LONGTEXT N_("Acquisition rate (in fps)")
71
72 #define CFG_PREFIX "rdp-"
73
74 /*****************************************************************************
75  * Module descriptor
76  *****************************************************************************/
77 static int  Open ( vlc_object_t * );
78 static void Close( vlc_object_t * );
79
80 vlc_module_begin()
81     set_shortname( N_("RDP") )
82     add_shortcut( "rdp" )
83     set_category( CAT_INPUT )
84     set_subcategory( SUBCAT_INPUT_ACCESS )
85     set_description( N_("RDP Remote Desktop") )
86     set_capability( "access_demux", 0 )
87
88     add_string( CFG_PREFIX "user", NULL, RDP_USER, RDP_USER, false )
89         change_safe()
90     add_password( CFG_PREFIX "password", NULL, RDP_PASSWORD, RDP_PASSWORD_LONGTEXT, false )
91         change_safe()
92     add_float( CFG_PREFIX "fps", 5, RDP_FPS, RDP_FPS_LONGTEXT, true )
93
94     add_bool( CFG_PREFIX "encrypt", false, RDP_ENCRYPT, RDP_ENCRYPT, true )
95         change_safe()
96
97     set_callbacks( Open, Close )
98 vlc_module_end()
99
100 #define RDP_MAX_FD 32
101
102 struct demux_sys_t
103 {
104     vlc_thread_t thread;
105     freerdp *p_instance;
106     block_t *p_block;
107     int i_framebuffersize;
108
109     float f_fps;
110     int i_frame_interval;
111     mtime_t i_starttime;
112
113     es_out_id_t *es;
114
115     /* pre-connect params */
116     char *psz_hostname;
117     int i_port;
118     /* cancelability */
119     int i_cancel_state;
120 };
121
122 /* context */
123
124 struct vlcrdp_context_t
125 {
126     rdpContext rdp_context; /* Extending API's struct */
127     demux_t *p_demux;
128     rdpSettings* p_settings;
129 };
130 typedef struct vlcrdp_context_t vlcrdp_context_t;
131
132 /*****************************************************************************
133  * Local prototypes
134  *****************************************************************************/
135
136 /* updates handlers */
137
138 static void desktopResizeHandler( rdpContext *p_context )
139 {
140     vlcrdp_context_t * p_vlccontext = (vlcrdp_context_t *) p_context;
141     demux_sys_t *p_sys = p_vlccontext->p_demux->p_sys;
142     rdpGdi *p_gdi = p_context->gdi;
143
144     if ( p_sys->es )
145     {
146         es_out_Del( p_vlccontext->p_demux->out, p_sys->es );
147         p_sys->es = NULL;
148     }
149
150     /* Now init and fill es format */
151     vlc_fourcc_t i_chroma;
152     switch( p_gdi->bytesPerPixel )
153     {
154         default:
155         case 16:
156             i_chroma = VLC_CODEC_RGB16;
157             break;
158         case 24:
159             i_chroma = VLC_CODEC_RGB24;
160             break;
161         case 32:
162             i_chroma = VLC_CODEC_RGB32;
163             break;
164     }
165     es_format_t fmt;
166     es_format_Init( &fmt, VIDEO_ES, i_chroma );
167
168     fmt.video.i_chroma = i_chroma;
169     fmt.video.i_visible_width =
170     fmt.video.i_width = p_gdi->width;
171     fmt.video.i_visible_height =
172     fmt.video.i_height = p_gdi->height;
173     fmt.video.i_frame_rate_base = 1000;
174     fmt.video.i_frame_rate = 1000 * p_sys->f_fps;
175     p_sys->i_framebuffersize = p_gdi->width * p_gdi->height * p_gdi->bytesPerPixel;
176
177     if ( p_sys->p_block )
178         p_sys->p_block = block_Realloc( p_sys->p_block, 0, p_sys->i_framebuffersize );
179     else
180         p_sys->p_block = block_Alloc( p_sys->i_framebuffersize );
181
182     p_sys->es = es_out_Add( p_vlccontext->p_demux->out, &fmt );
183 }
184
185 static void beginPaintHandler( rdpContext *p_context )
186 {
187     vlcrdp_context_t * p_vlccontext = (vlcrdp_context_t *) p_context;
188     demux_sys_t *p_sys = p_vlccontext->p_demux->p_sys;
189     rdpGdi *p_gdi = p_context->gdi;
190     p_gdi->primary->hdc->hwnd->invalid->null = 1;
191     p_gdi->primary->hdc->hwnd->ninvalid = 0;
192     if ( ! p_sys->p_block && p_sys->i_framebuffersize )
193         p_sys->p_block = block_Alloc( p_sys->i_framebuffersize );
194 }
195
196 static void endPaintHandler( rdpContext *p_context )
197 {
198     vlcrdp_context_t * p_vlccontext = (vlcrdp_context_t *) p_context;
199     demux_sys_t *p_sys = p_vlccontext->p_demux->p_sys;
200     rdpGdi *p_gdi = p_context->gdi;
201
202     if ( p_sys->p_block )
203     {
204         p_sys->p_block->i_buffer = p_sys->i_framebuffersize;
205         memcpy( p_sys->p_block->p_buffer, p_gdi->primary_buffer, p_sys->p_block->i_buffer );
206     }
207 }
208
209 /* instance handlers */
210
211 static bool preConnectHandler( freerdp *p_instance )
212 {
213     vlcrdp_context_t * p_vlccontext = (vlcrdp_context_t *) p_instance->context;
214     demux_sys_t *p_sys = p_vlccontext->p_demux->p_sys;
215
216     /* Configure connexion */
217     p_instance->settings->SoftwareGdi = true; /* render in buffer */
218     p_instance->settings->Fullscreen = true;
219     p_instance->settings->ServerHostname = strdup( p_sys->psz_hostname );
220     p_instance->settings->Username =
221             var_InheritString( p_vlccontext->p_demux, CFG_PREFIX "user" );
222     p_instance->settings->Password =
223             var_InheritString( p_vlccontext->p_demux, CFG_PREFIX "password" );
224     p_instance->settings->ServerPort = p_sys->i_port;
225     p_instance->settings->EncryptionMethods =
226             var_InheritBool( p_vlccontext->p_demux, CFG_PREFIX "encrypt" );
227
228     return true;
229 }
230
231 static bool postConnectHandler( freerdp *p_instance )
232 {
233     vlcrdp_context_t * p_vlccontext = (vlcrdp_context_t *) p_instance->context;
234
235     msg_Dbg( p_vlccontext->p_demux, "connected to desktop %dx%d (%d bpp)",
236 #if defined(FREERDP_VERSION_MAJOR)  && (FREERDP_VERSION_MAJOR >= 1 && FREERDP_VERSION_MINOR >= 1 )
237              p_instance->settings->DesktopWidth,
238              p_instance->settings->DesktopHeight,
239              p_instance->settings->ColorDepth
240 #else
241              p_instance->settings->width,
242              p_instance->settings->height,
243              p_instance->settings->color_depth
244 #endif
245              );
246
247     p_instance->update->DesktopResize = desktopResizeHandler;
248     p_instance->update->BeginPaint = beginPaintHandler;
249     p_instance->update->EndPaint = endPaintHandler;
250
251     gdi_init( p_instance,
252                 CLRBUF_16BPP |
253 #if defined(FREERDP_VERSION_MAJOR) && defined(FREERDP_VERSION_MINOR) && \
254     !(FREERDP_VERSION_MAJOR > 1 || (FREERDP_VERSION_MAJOR == 1 && FREERDP_VERSION_MINOR >= 2))
255                 CLRBUF_24BPP |
256 #endif
257                 CLRBUF_32BPP, NULL );
258
259     desktopResizeHandler( p_instance->context );
260     return true;
261 }
262
263 static bool authenticateHandler( freerdp *p_instance, char** ppsz_username,
264                                  char** ppsz_password, char** ppsz_domain )
265 {
266     VLC_UNUSED(ppsz_domain);
267     vlcrdp_context_t * p_vlccontext = (vlcrdp_context_t *) p_instance->context;
268     *ppsz_username = var_InheritString( p_vlccontext->p_demux, CFG_PREFIX "user" );
269     *ppsz_password = var_InheritString( p_vlccontext->p_demux, CFG_PREFIX "password" );
270     return true;
271 }
272
273 /*****************************************************************************
274  * Control:
275  *****************************************************************************/
276 static int Control( demux_t *p_demux, int i_query, va_list args )
277 {
278     bool *pb;
279     int64_t *pi64;
280     double *p_dbl;
281     vlc_meta_t *p_meta;
282
283     switch( i_query )
284     {
285         case DEMUX_CAN_PAUSE:
286         case DEMUX_CAN_SEEK:
287         case DEMUX_CAN_CONTROL_PACE:
288         case DEMUX_CAN_CONTROL_RATE:
289         case DEMUX_HAS_UNSUPPORTED_META:
290             pb = (bool*)va_arg( args, bool * );
291             *pb = false;
292             return VLC_SUCCESS;
293
294         case DEMUX_CAN_RECORD:
295             pb = (bool*)va_arg( args, bool * );
296             *pb = true;
297             return VLC_SUCCESS;
298
299         case DEMUX_GET_PTS_DELAY:
300             pi64 = (int64_t*)va_arg( args, int64_t * );
301             *pi64 = INT64_C(1000)
302                   * var_InheritInteger( p_demux, "live-caching" );
303             return VLC_SUCCESS;
304
305         case DEMUX_GET_TIME:
306             pi64 = (int64_t*)va_arg( args, int64_t * );
307             *pi64 = mdate() - p_demux->p_sys->i_starttime;
308             return VLC_SUCCESS;
309
310         case DEMUX_GET_LENGTH:
311             pi64 = (int64_t*)va_arg( args, int64_t * );
312             *pi64 = 0;
313             return VLC_SUCCESS;
314
315         case DEMUX_GET_FPS:
316             p_dbl = (double*)va_arg( args, double * );
317             *p_dbl = p_demux->p_sys->f_fps;
318             return VLC_SUCCESS;
319
320         case DEMUX_GET_META:
321             p_meta = (vlc_meta_t*)va_arg( args, vlc_meta_t* );
322             vlc_meta_Set( p_meta, vlc_meta_Title, p_demux->psz_location );
323             return VLC_SUCCESS;
324
325         default:
326             return VLC_EGENERIC;
327     }
328 }
329
330 /*****************************************************************************
331  * Demux:
332  *****************************************************************************/
333 static void *DemuxThread( void *p_data )
334 {
335     demux_t *p_demux = (demux_t *) p_data;
336     demux_sys_t *p_sys = p_demux->p_sys;
337     p_sys->i_starttime = mdate();
338     mtime_t i_next_frame_date = mdate() + p_sys->i_frame_interval;
339     int i_ret;
340
341     for(;;)
342     {
343         i_ret = 0;
344         p_sys->i_cancel_state = vlc_savecancel();
345         if ( freerdp_shall_disconnect( p_sys->p_instance ) )
346         {
347             vlc_restorecancel( p_sys->i_cancel_state );
348             msg_Warn( p_demux, "RDP server closed session" );
349             es_out_Del( p_demux->out, p_sys->es );
350             p_sys->es = NULL;
351             return NULL;
352         }
353
354         struct
355         {
356             void* pp_rfds[RDP_MAX_FD]; /* Declared by rdp */
357             void* pp_wfds[RDP_MAX_FD];
358             int i_nbr;
359             int i_nbw;
360             struct pollfd ufds[RDP_MAX_FD];
361         } fds;
362
363         fds.i_nbr = fds.i_nbw = 0;
364
365         if ( freerdp_get_fds( p_sys->p_instance, fds.pp_rfds, &fds.i_nbr,
366                               fds.pp_wfds, &fds.i_nbw ) != true )
367         {
368             vlc_restorecancel( p_sys->i_cancel_state );
369             msg_Err( p_demux, "cannot get FDS" );
370         }
371         else
372         if ( (fds.i_nbr + fds.i_nbw) > 0 && p_sys->es )
373         {
374             vlc_restorecancel( p_sys->i_cancel_state );
375             int i_count = 0;
376
377             for( int i = 0; i < fds.i_nbr; i++ )
378             {
379                 fds.ufds[ i_count ].fd = (long) fds.pp_rfds[ i ];
380                 fds.ufds[ i_count ].events = POLLIN ;
381                 fds.ufds[ i_count++ ].revents = 0;
382             }
383             for( int i = 0; i < fds.i_nbw && i_count < RDP_MAX_FD; i++ )
384             {   /* may be useless */
385                 fds.ufds[ i_count ].fd = (long) fds.pp_wfds[ i ];
386                 fds.ufds[ i_count ].events = POLLOUT;
387                 fds.ufds[ i_count++ ].revents = 0;
388             }
389             i_ret = poll( fds.ufds, i_count, p_sys->i_frame_interval * 1000/2 );
390         } else {
391             vlc_restorecancel( p_sys->i_cancel_state );
392         }
393
394         mwait( i_next_frame_date );
395         i_next_frame_date += p_sys->i_frame_interval;
396
397         if ( i_ret >= 0 )
398         {
399             /* Do the rendering */
400             p_sys->i_cancel_state = vlc_savecancel();
401             freerdp_check_fds( p_sys->p_instance );
402             vlc_restorecancel( p_sys->i_cancel_state );
403             block_t *p_block = block_Duplicate( p_sys->p_block );
404             if (likely( p_block && p_sys->p_block ))
405             {
406                 p_sys->p_block->i_dts = p_sys->p_block->i_pts = mdate() - p_sys->i_starttime;
407                 es_out_Control( p_demux->out, ES_OUT_SET_PCR, p_sys->p_block->i_pts );
408                 es_out_Send( p_demux->out, p_sys->es, p_sys->p_block );
409                 p_sys->p_block = p_block;
410             }
411         }
412     }
413     return NULL;
414 }
415
416 /*****************************************************************************
417  * Open:
418  *****************************************************************************/
419 static int Open( vlc_object_t *p_this )
420 {
421     demux_t      *p_demux = (demux_t*)p_this;
422     demux_sys_t  *p_sys;
423
424     p_sys = calloc( 1, sizeof(demux_sys_t) );
425     if( !p_sys ) return VLC_ENOMEM;
426
427     p_sys->f_fps = var_InheritFloat( p_demux, CFG_PREFIX "fps" );
428     if ( p_sys->f_fps <= 0 ) p_sys->f_fps = 1.0;
429     p_sys->i_frame_interval = 1000000 / p_sys->f_fps;
430
431     freerdp_channels_global_init();
432
433     p_sys->p_instance = freerdp_new();
434     if ( !p_sys->p_instance )
435     {
436         msg_Err( p_demux, "rdp instanciation error" );
437         free( p_sys );
438         return VLC_EGENERIC;
439     }
440
441     p_demux->p_sys = p_sys;
442     p_sys->p_instance->PreConnect = preConnectHandler;
443     p_sys->p_instance->PostConnect = postConnectHandler;
444     p_sys->p_instance->Authenticate = authenticateHandler;
445
446     /* Set up context handlers and let it be allocated */
447     p_sys->p_instance->ContextSize = sizeof( vlcrdp_context_t );
448     freerdp_context_new( p_sys->p_instance );
449
450     vlcrdp_context_t * p_vlccontext = (vlcrdp_context_t *) p_sys->p_instance->context;
451     p_vlccontext->p_demux = p_demux;
452
453     /* Parse uri params for pre-connect */
454     vlc_url_t url;
455     vlc_UrlParse( &url, p_demux->psz_location, 0 );
456
457     if ( !EMPTY_STR(url.psz_host) )
458         p_sys->psz_hostname = strdup( url.psz_host );
459     else
460         p_sys->psz_hostname = strdup( "localhost" );
461
462     p_sys->i_port = ( url.i_port > 0 ) ? url.i_port : 3389;
463
464     vlc_UrlClean( &url );
465
466     if ( ! freerdp_connect( p_sys->p_instance ) )
467     {
468         msg_Err( p_demux, "can't connect to rdp server" );
469         goto error;
470     }
471
472     if ( vlc_clone( &p_sys->thread, DemuxThread, p_demux, VLC_THREAD_PRIORITY_INPUT ) != VLC_SUCCESS )
473     {
474         msg_Err( p_demux, "can't spawn thread" );
475         freerdp_disconnect( p_sys->p_instance );
476         goto error;
477     }
478
479     p_demux->pf_demux = NULL;
480     p_demux->pf_control = Control;
481
482     return VLC_SUCCESS;
483
484 error:
485     freerdp_free( p_sys->p_instance );
486     free( p_sys->psz_hostname );
487     free( p_sys );
488     return VLC_EGENERIC;
489 }
490
491 /*****************************************************************************
492  * Close:
493  *****************************************************************************/
494 static void Close( vlc_object_t *p_this )
495 {
496     demux_t     *p_demux = (demux_t*)p_this;
497     demux_sys_t *p_sys = p_demux->p_sys;
498
499     vlc_cancel( p_sys->thread );
500     vlc_join( p_sys->thread, NULL );
501
502     if ( p_sys->es )
503         es_out_Del( p_demux->out, p_sys->es );
504
505     freerdp_disconnect( p_sys->p_instance );
506     freerdp_free( p_sys->p_instance );
507     freerdp_channels_global_uninit();
508
509     if ( p_sys->p_block )
510         block_Release( p_sys->p_block );
511
512     free( p_sys->psz_hostname );
513     free( p_sys );
514 }