demux: avi: invalidate skipped chunks
[vlc.git] / modules / access / rdp.c
blob926994cbd7550b59778db25c9e68cf63003611d4
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 /*****************************************************************************
22 * Preamble
23 *****************************************************************************/
25 #ifdef HAVE_CONFIG_H
26 # include "config.h"
27 #endif
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>
35 #define boolean bool
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>
46 #endif
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
58 #endif
60 #include <errno.h>
61 #ifdef HAVE_POLL
62 # include <poll.h>
63 #endif
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 /*****************************************************************************
79 * Module descriptor
80 *****************************************************************************/
81 static int Open ( vlc_object_t * );
82 static void Close( vlc_object_t * );
84 vlc_module_begin()
85 set_shortname( N_("RDP") )
86 add_shortcut( "rdp" )
87 set_category( CAT_INPUT )
88 set_subcategory( SUBCAT_INPUT_ACCESS )
89 set_description( N_("RDP Remote Desktop") )
90 set_capability( "access_demux", 0 )
92 add_string( CFG_PREFIX "user", NULL, USER_TEXT, USER_LONGTEXT, false )
93 change_safe()
94 add_password( CFG_PREFIX "password", NULL, PASS_TEXT, PASS_LONGTEXT, false )
95 change_safe()
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 )
99 change_safe()
101 set_callbacks( Open, Close )
102 vlc_module_end()
104 #define RDP_MAX_FD 32
106 struct demux_sys_t
108 vlc_thread_t thread;
109 freerdp *p_instance;
110 block_t *p_block;
111 int i_framebuffersize;
113 float f_fps;
114 int i_frame_interval;
115 mtime_t i_starttime;
117 es_out_id_t *es;
119 /* pre-connect params */
120 char *psz_hostname;
121 int i_port;
122 /* cancelability */
123 int i_cancel_state;
126 /* context */
128 struct vlcrdp_context_t
130 rdpContext rdp_context; /* Extending API's struct */
131 demux_t *p_demux;
132 rdpSettings* p_settings;
134 typedef struct vlcrdp_context_t vlcrdp_context_t;
136 /*****************************************************************************
137 * Local prototypes
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;
148 if ( p_sys->es )
150 es_out_Del( p_vlccontext->p_demux->out, p_sys->es );
151 p_sys->es = NULL;
154 /* Now init and fill es format */
155 vlc_fourcc_t i_chroma;
156 switch( p_gdi->bytesPerPixel )
158 default:
159 case 16:
160 i_chroma = VLC_CODEC_RGB16;
161 break;
162 case 24:
163 i_chroma = VLC_CODEC_RGB24;
164 break;
165 case 32:
166 i_chroma = VLC_CODEC_RGB32;
167 break;
169 es_format_t fmt;
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 );
183 else
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" );
232 return true;
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
244 #else
245 p_instance->settings->width,
246 p_instance->settings->height,
247 p_instance->settings->color_depth
248 #endif
251 p_instance->update->DesktopResize = desktopResizeHandler;
252 p_instance->update->BeginPaint = beginPaintHandler;
253 p_instance->update->EndPaint = endPaintHandler;
255 gdi_init( p_instance,
256 CLRBUF_16BPP |
257 #if defined(FREERDP_VERSION_MAJOR) && defined(FREERDP_VERSION_MINOR) && \
258 !(FREERDP_VERSION_MAJOR > 1 || (FREERDP_VERSION_MAJOR == 1 && FREERDP_VERSION_MINOR >= 2))
259 CLRBUF_24BPP |
260 #endif
261 CLRBUF_32BPP, NULL );
263 desktopResizeHandler( p_instance->context );
264 return true;
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" );
274 return true;
277 /*****************************************************************************
278 * Control:
279 *****************************************************************************/
280 static int Control( demux_t *p_demux, int i_query, va_list args )
282 bool *pb;
283 int64_t *pi64;
284 double *p_dbl;
285 vlc_meta_t *p_meta;
287 switch( i_query )
289 case DEMUX_CAN_PAUSE:
290 case DEMUX_CAN_SEEK:
291 case DEMUX_CAN_CONTROL_PACE:
292 case DEMUX_CAN_CONTROL_RATE:
293 case DEMUX_HAS_UNSUPPORTED_META:
294 pb = va_arg( args, bool * );
295 *pb = false;
296 return VLC_SUCCESS;
298 case DEMUX_CAN_RECORD:
299 pb = va_arg( args, bool * );
300 *pb = true;
301 return VLC_SUCCESS;
303 case DEMUX_GET_PTS_DELAY:
304 pi64 = va_arg( args, int64_t * );
305 *pi64 = INT64_C(1000)
306 * var_InheritInteger( p_demux, "live-caching" );
307 return VLC_SUCCESS;
309 case DEMUX_GET_TIME:
310 pi64 = va_arg( args, int64_t * );
311 *pi64 = mdate() - p_demux->p_sys->i_starttime;
312 return VLC_SUCCESS;
314 case DEMUX_GET_LENGTH:
315 pi64 = va_arg( args, int64_t * );
316 *pi64 = 0;
317 return VLC_SUCCESS;
319 case DEMUX_GET_FPS:
320 p_dbl = va_arg( args, double * );
321 *p_dbl = p_demux->p_sys->f_fps;
322 return VLC_SUCCESS;
324 case DEMUX_GET_META:
325 p_meta = va_arg( args, vlc_meta_t * );
326 vlc_meta_Set( p_meta, vlc_meta_Title, p_demux->psz_location );
327 return VLC_SUCCESS;
329 default:
330 return VLC_EGENERIC;
334 /*****************************************************************************
335 * Demux:
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 = mdate();
342 mtime_t i_next_frame_date = mdate() + p_sys->i_frame_interval;
343 int i_ret;
345 for(;;)
347 i_ret = 0;
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 );
354 p_sys->es = NULL;
355 return NULL;
358 struct
360 void* pp_rfds[RDP_MAX_FD]; /* Declared by rdp */
361 void* pp_wfds[RDP_MAX_FD];
362 int i_nbr;
363 int i_nbw;
364 struct pollfd ufds[RDP_MAX_FD];
365 } fds;
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" );
375 else
376 if ( (fds.i_nbr + fds.i_nbw) > 0 && p_sys->es )
378 vlc_restorecancel( p_sys->i_cancel_state );
379 int i_count = 0;
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 );
394 } else {
395 vlc_restorecancel( p_sys->i_cancel_state );
398 mwait( i_next_frame_date );
399 i_next_frame_date += p_sys->i_frame_interval;
401 if ( i_ret >= 0 )
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 = mdate() - p_sys->i_starttime;
411 es_out_Control( p_demux->out, ES_OUT_SET_PCR, 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;
417 return NULL;
420 /*****************************************************************************
421 * Open:
422 *****************************************************************************/
423 static int Open( vlc_object_t *p_this )
425 demux_t *p_demux = (demux_t*)p_this;
426 demux_sys_t *p_sys;
428 p_sys = vlc_calloc( p_this, 1, sizeof(demux_sys_t) );
429 if( !p_sys ) return VLC_ENOMEM;
431 p_sys->f_fps = var_InheritFloat( p_demux, CFG_PREFIX "fps" );
432 if ( p_sys->f_fps <= 0 ) p_sys->f_fps = 1.0;
433 p_sys->i_frame_interval = 1000000 / p_sys->f_fps;
435 #if FREERDP_VERSION_MAJOR == 1 && FREERDP_VERSION_MINOR < 2
436 freerdp_channels_global_init();
437 #endif
439 p_sys->p_instance = freerdp_new();
440 if ( !p_sys->p_instance )
442 msg_Err( p_demux, "rdp instantiation error" );
443 return VLC_EGENERIC;
446 p_demux->p_sys = p_sys;
447 p_sys->p_instance->PreConnect = preConnectHandler;
448 p_sys->p_instance->PostConnect = postConnectHandler;
449 p_sys->p_instance->Authenticate = authenticateHandler;
451 /* Set up context handlers and let it be allocated */
452 p_sys->p_instance->ContextSize = sizeof( vlcrdp_context_t );
453 freerdp_context_new( p_sys->p_instance );
455 vlcrdp_context_t * p_vlccontext = (vlcrdp_context_t *) p_sys->p_instance->context;
456 p_vlccontext->p_demux = p_demux;
458 /* Parse uri params for pre-connect */
459 vlc_url_t url;
460 vlc_UrlParse( &url, p_demux->psz_location );
462 if ( !EMPTY_STR(url.psz_host) )
463 p_sys->psz_hostname = strdup( url.psz_host );
464 else
465 p_sys->psz_hostname = strdup( "localhost" );
467 p_sys->i_port = ( url.i_port > 0 ) ? url.i_port : 3389;
469 vlc_UrlClean( &url );
471 if ( ! freerdp_connect( p_sys->p_instance ) )
473 msg_Err( p_demux, "can't connect to rdp server" );
474 goto error;
477 if ( vlc_clone( &p_sys->thread, DemuxThread, p_demux, VLC_THREAD_PRIORITY_INPUT ) != VLC_SUCCESS )
479 msg_Err( p_demux, "can't spawn thread" );
480 freerdp_disconnect( p_sys->p_instance );
481 goto error;
484 p_demux->pf_demux = NULL;
485 p_demux->pf_control = Control;
487 return VLC_SUCCESS;
489 error:
490 freerdp_free( p_sys->p_instance );
491 free( p_sys->psz_hostname );
492 return VLC_EGENERIC;
495 /*****************************************************************************
496 * Close:
497 *****************************************************************************/
498 static void Close( vlc_object_t *p_this )
500 demux_t *p_demux = (demux_t*)p_this;
501 demux_sys_t *p_sys = p_demux->p_sys;
503 vlc_cancel( p_sys->thread );
504 vlc_join( p_sys->thread, NULL );
506 if ( p_sys->es )
507 es_out_Del( p_demux->out, p_sys->es );
509 freerdp_disconnect( p_sys->p_instance );
510 freerdp_free( p_sys->p_instance );
511 #if FREERDP_VERSION_MAJOR == 1 && FREERDP_VERSION_MINOR < 2
512 freerdp_channels_global_uninit();
513 #endif
515 if ( p_sys->p_block )
516 block_Release( p_sys->p_block );
518 free( p_sys->psz_hostname );