]> git.sesse.net Git - vlc/blob - modules/access/rdp.c
dtv: fix ISDB-S tuning
[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 #include <errno.h>
45 #ifdef HAVE_POLL
46 # include <poll.h>
47 #endif
48
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)")
55
56 #define CFG_PREFIX "rdp-"
57
58 /*****************************************************************************
59  * Module descriptor
60  *****************************************************************************/
61 static int  Open ( vlc_object_t * );
62 static void Close( vlc_object_t * );
63
64 vlc_module_begin()
65     set_shortname( N_("RDP") )
66     add_shortcut( "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 )
71
72     add_string( CFG_PREFIX "user", NULL, RDP_USER, RDP_USER, false )
73         change_safe()
74     add_password( CFG_PREFIX "password", NULL, RDP_PASSWORD, RDP_PASSWORD_LONGTEXT, false )
75         change_safe()
76     add_float( CFG_PREFIX "fps", 5, RDP_FPS, RDP_FPS_LONGTEXT, true )
77
78     add_bool( CFG_PREFIX "encrypt", false, RDP_ENCRYPT, RDP_ENCRYPT, true )
79         change_safe()
80
81     set_callbacks( Open, Close )
82 vlc_module_end()
83
84 #define RDP_MAX_FD 32
85
86 struct demux_sys_t
87 {
88     vlc_thread_t thread;
89     freerdp *p_instance;
90     block_t *p_block;
91     int i_framebuffersize;
92
93     float f_fps;
94     int i_frame_interval;
95     mtime_t i_starttime;
96
97     es_out_id_t *es;
98
99     /* pre-connect params */
100     char *psz_hostname;
101     int i_port;
102     /* cancelability */
103     int i_cancel_state;
104 };
105
106 /* context */
107
108 struct vlcrdp_context_t
109 {
110     rdpContext rdp_context; /* Extending API's struct */
111     demux_t *p_demux;
112     rdpSettings* p_settings;
113 };
114 typedef struct vlcrdp_context_t vlcrdp_context_t;
115
116 /*****************************************************************************
117  * Local prototypes
118  *****************************************************************************/
119
120 /* updates handlers */
121
122 static void desktopResizeHandler( rdpContext *p_context )
123 {
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;
127
128     if ( p_sys->es )
129     {
130         es_out_Del( p_vlccontext->p_demux->out, p_sys->es );
131         p_sys->es = NULL;
132     }
133
134     /* Now init and fill es format */
135     vlc_fourcc_t i_chroma;
136     switch( p_gdi->bytesPerPixel )
137     {
138         default:
139         case 16:
140             i_chroma = VLC_CODEC_RGB16;
141             break;
142         case 24:
143             i_chroma = VLC_CODEC_RGB24;
144             break;
145         case 32:
146             i_chroma = VLC_CODEC_RGB32;
147             break;
148     }
149     es_format_t fmt;
150     es_format_Init( &fmt, VIDEO_ES, i_chroma );
151
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;
160
161     if ( p_sys->p_block )
162         p_sys->p_block = block_Realloc( p_sys->p_block, 0, p_sys->i_framebuffersize );
163     else
164         p_sys->p_block = block_Alloc( p_sys->i_framebuffersize );
165
166     p_sys->es = es_out_Add( p_vlccontext->p_demux->out, &fmt );
167 }
168
169 static void beginPaintHandler( rdpContext *p_context )
170 {
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 );
178 }
179
180 static void endPaintHandler( rdpContext *p_context )
181 {
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;
185
186     if ( p_sys->p_block )
187     {
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 );
190     }
191 }
192
193 /* instance handlers */
194
195 static bool preConnectHandler( freerdp *p_instance )
196 {
197     vlcrdp_context_t * p_vlccontext = (vlcrdp_context_t *) p_instance->context;
198     demux_sys_t *p_sys = p_vlccontext->p_demux->p_sys;
199
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" );
211
212     return true;
213 }
214
215 static bool postConnectHandler( freerdp *p_instance )
216 {
217     vlcrdp_context_t * p_vlccontext = (vlcrdp_context_t *) p_instance->context;
218
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 );
223
224     p_instance->update->DesktopResize = desktopResizeHandler;
225     p_instance->update->BeginPaint = beginPaintHandler;
226     p_instance->update->EndPaint = endPaintHandler;
227
228     gdi_init( p_instance, CLRBUF_16BPP | CLRBUF_24BPP | CLRBUF_32BPP, NULL );
229
230     desktopResizeHandler( p_instance->context );
231     return true;
232 }
233
234 static bool authenticateHandler( freerdp *p_instance, char** ppsz_username,
235                                  char** ppsz_password, char** ppsz_domain )
236 {
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" );
241     return true;
242 }
243
244 /*****************************************************************************
245  * Control:
246  *****************************************************************************/
247 static int Control( demux_t *p_demux, int i_query, va_list args )
248 {
249     bool *pb;
250     int64_t *pi64;
251     double *p_dbl;
252     vlc_meta_t *p_meta;
253
254     switch( i_query )
255     {
256         case DEMUX_CAN_PAUSE:
257         case DEMUX_CAN_SEEK:
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 * );
262             *pb = false;
263             return VLC_SUCCESS;
264
265         case DEMUX_CAN_RECORD:
266             pb = (bool*)va_arg( args, bool * );
267             *pb = true;
268             return VLC_SUCCESS;
269
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" );
274             return VLC_SUCCESS;
275
276         case DEMUX_GET_TIME:
277             pi64 = (int64_t*)va_arg( args, int64_t * );
278             *pi64 = mdate() - p_demux->p_sys->i_starttime;
279             return VLC_SUCCESS;
280
281         case DEMUX_GET_LENGTH:
282             pi64 = (int64_t*)va_arg( args, int64_t * );
283             *pi64 = 0;
284             return VLC_SUCCESS;
285
286         case DEMUX_GET_FPS:
287             p_dbl = (double*)va_arg( args, double * );
288             *p_dbl = p_demux->p_sys->f_fps;
289             return VLC_SUCCESS;
290
291         case DEMUX_GET_META:
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 );
294             return VLC_SUCCESS;
295
296         default:
297             return VLC_EGENERIC;
298     }
299 }
300
301 /*****************************************************************************
302  * Demux:
303  *****************************************************************************/
304 static void *DemuxThread( void *p_data )
305 {
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;
310     int i_ret;
311
312     for(;;)
313     {
314         i_ret = 0;
315         p_sys->i_cancel_state = vlc_savecancel();
316         if ( freerdp_shall_disconnect( p_sys->p_instance ) )
317         {
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 );
321             p_sys->es = NULL;
322             return NULL;
323         }
324
325         struct
326         {
327             void* pp_rfds[RDP_MAX_FD]; /* Declared by rdp */
328             void* pp_wfds[RDP_MAX_FD];
329             int i_nbr;
330             int i_nbw;
331             struct pollfd ufds[RDP_MAX_FD];
332         } fds;
333
334         fds.i_nbr = fds.i_nbw = 0;
335
336         if ( freerdp_get_fds( p_sys->p_instance, fds.pp_rfds, &fds.i_nbr,
337                               fds.pp_wfds, &fds.i_nbw ) != true )
338         {
339             vlc_restorecancel( p_sys->i_cancel_state );
340             msg_Err( p_demux, "cannot get FDS" );
341         }
342         else
343         if ( (fds.i_nbr + fds.i_nbw) > 0 && p_sys->es )
344         {
345             vlc_restorecancel( p_sys->i_cancel_state );
346             int i_count = 0;
347
348             for( int i = 0; i < fds.i_nbr; i++ )
349             {
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;
353             }
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;
359             }
360             i_ret = poll( fds.ufds, i_count, p_sys->i_frame_interval * 1000/2 );
361         } else {
362             vlc_restorecancel( p_sys->i_cancel_state );
363         }
364
365         mwait( i_next_frame_date );
366         i_next_frame_date += p_sys->i_frame_interval;
367
368         if ( i_ret >= 0 )
369         {
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 ))
376             {
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;
381             }
382         }
383     }
384     return NULL;
385 }
386
387 /*****************************************************************************
388  * Open:
389  *****************************************************************************/
390 static int Open( vlc_object_t *p_this )
391 {
392     demux_t      *p_demux = (demux_t*)p_this;
393     demux_sys_t  *p_sys;
394
395     p_sys = calloc( 1, sizeof(demux_sys_t) );
396     if( !p_sys ) return VLC_ENOMEM;
397
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;
401
402     freerdp_channels_global_init();
403
404     p_sys->p_instance = freerdp_new();
405     if ( !p_sys->p_instance )
406     {
407         msg_Err( p_demux, "rdp instanciation error" );
408         free( p_sys );
409         return VLC_EGENERIC;
410     }
411
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;
416
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 );
420
421     vlcrdp_context_t * p_vlccontext = (vlcrdp_context_t *) p_sys->p_instance->context;
422     p_vlccontext->p_demux = p_demux;
423
424     /* Parse uri params for pre-connect */
425     vlc_url_t url;
426     vlc_UrlParse( &url, p_demux->psz_location, 0 );
427
428     if ( !EMPTY_STR(url.psz_host) )
429         p_sys->psz_hostname = strdup( url.psz_host );
430     else
431         p_sys->psz_hostname = strdup( "localhost" );
432
433     p_sys->i_port = ( url.i_port > 0 ) ? url.i_port : 3389;
434
435     vlc_UrlClean( &url );
436
437     if ( ! freerdp_connect( p_sys->p_instance ) )
438     {
439         msg_Err( p_demux, "can't connect to rdp server" );
440         goto error;
441     }
442
443     if ( vlc_clone( &p_sys->thread, DemuxThread, p_demux, VLC_THREAD_PRIORITY_INPUT ) != VLC_SUCCESS )
444     {
445         msg_Err( p_demux, "can't spawn thread" );
446         freerdp_disconnect( p_sys->p_instance );
447         goto error;
448     }
449
450     p_demux->pf_demux = NULL;
451     p_demux->pf_control = Control;
452
453     return VLC_SUCCESS;
454
455 error:
456     freerdp_free( p_sys->p_instance );
457     free( p_sys->psz_hostname );
458     free( p_sys );
459     return VLC_EGENERIC;
460 }
461
462 /*****************************************************************************
463  * Close:
464  *****************************************************************************/
465 static void Close( vlc_object_t *p_this )
466 {
467     demux_t     *p_demux = (demux_t*)p_this;
468     demux_sys_t *p_sys = p_demux->p_sys;
469
470     vlc_cancel( p_sys->thread );
471     vlc_join( p_sys->thread, NULL );
472
473     if ( p_sys->es )
474         es_out_Del( p_demux->out, p_sys->es );
475
476     freerdp_disconnect( p_sys->p_instance );
477     freerdp_free( p_sys->p_instance );
478     freerdp_channels_global_uninit();
479
480     if ( p_sys->p_block )
481         block_Release( p_sys->p_block );
482
483     free( p_sys->psz_hostname );
484     free( p_sys );
485 }