]> git.sesse.net Git - vlc/blob - modules/access/dvdnav.c
Trailing ;
[vlc] / modules / access / dvdnav.c
1 /*****************************************************************************
2  * dvdnav.c: DVD module using the dvdnav library.
3  *****************************************************************************
4  * Copyright (C) 2004 the VideoLAN team
5  * $Id$
6  *
7  * Authors: Laurent Aimar <fenrir@via.ecp.fr>
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22  *****************************************************************************/
23
24 /*****************************************************************************
25  * Preamble
26  *****************************************************************************/
27
28 #ifdef HAVE_CONFIG_H
29 # include "config.h"
30 #endif
31
32 #include <vlc_common.h>
33 #include <vlc_plugin.h>
34 #include <vlc_input.h>
35 #include <vlc_access.h>
36 #include <vlc_demux.h>
37 #include <vlc_charset.h>
38
39 #include <vlc_interface.h>
40
41 #ifdef HAVE_UNISTD_H
42 #   include <unistd.h>
43 #endif
44 #ifdef HAVE_SYS_TYPES_H
45 #   include <sys/types.h>
46 #endif
47 #ifdef HAVE_SYS_STAT_H
48 #   include <sys/stat.h>
49 #endif
50 #ifdef HAVE_FCNTL_H
51 #   include <fcntl.h>
52 #endif
53
54 #include "vlc_keys.h"
55 #include "vlc_iso_lang.h"
56
57 /* FIXME we should find a better way than including that */
58 #include "../../src/text/iso-639_def.h"
59
60
61 #include <dvdnav/dvdnav.h>
62
63 #include "../demux/ps.h"
64
65 /*****************************************************************************
66  * Module descriptor
67  *****************************************************************************/
68 #define ANGLE_TEXT N_("DVD angle")
69 #define ANGLE_LONGTEXT N_( \
70      "Default DVD angle." )
71
72 #define CACHING_TEXT N_("Caching value in ms")
73 #define CACHING_LONGTEXT N_( \
74     "Caching value for DVDs. This "\
75     "value should be set in milliseconds." )
76 #define MENU_TEXT N_("Start directly in menu")
77 #define MENU_LONGTEXT N_( \
78     "Start the DVD directly in the main menu. This "\
79     "will try to skip all the useless warning introductions." )
80
81 #define LANGUAGE_DEFAULT ("en")
82
83 static int  Open ( vlc_object_t * );
84 static void Close( vlc_object_t * );
85
86 vlc_module_begin ()
87     set_shortname( N_("DVD with menus") )
88     set_description( N_("DVDnav Input") )
89     set_category( CAT_INPUT )
90     set_subcategory( SUBCAT_INPUT_ACCESS )
91     add_integer( "dvdnav-angle", 1, NULL, ANGLE_TEXT,
92         ANGLE_LONGTEXT, false )
93     add_integer( "dvdnav-caching", DEFAULT_PTS_DELAY / 1000, NULL,
94         CACHING_TEXT, CACHING_LONGTEXT, true )
95     add_bool( "dvdnav-menu", true, NULL,
96         MENU_TEXT, MENU_LONGTEXT, false )
97     set_capability( "access_demux", 5 )
98     add_shortcut( "dvd" )
99     add_shortcut( "dvdnav" )
100     add_shortcut( "file" )
101     set_callbacks( Open, Close )
102 vlc_module_end ()
103
104 /* Shall we use libdvdnav's read ahead cache? */
105 #define DVD_READ_CACHE 1
106
107 /*****************************************************************************
108  * Local prototypes
109  *****************************************************************************/
110 typedef struct
111 {
112     VLC_COMMON_MEMBERS
113
114     demux_t        *p_demux;
115     vlc_mutex_t     lock;
116
117     bool      b_moved;
118     bool      b_clicked;
119     int             i_key_action;
120
121     bool      b_still;
122     int64_t         i_still_end;
123
124 } event_thread_t;
125
126 static void* EventThread( vlc_object_t * );
127
128 struct demux_sys_t
129 {
130     dvdnav_t    *dvdnav;
131
132     /* track */
133     ps_track_t  tk[PS_TK_COUNT];
134     int         i_mux_rate;
135
136     /* for spu variables */
137     input_thread_t *p_input;
138
139     /* event */
140     event_thread_t *p_ev;
141
142     /* palette for menus */
143     uint32_t clut[16];
144     uint8_t  palette[4][4];
145     bool b_spu_change;
146
147     /* */
148     int i_aspect;
149
150     int           i_title;
151     input_title_t **title;
152
153     /* lenght of program group chain */
154     mtime_t     i_pgc_length;
155 };
156
157 static int Control( demux_t *, int, va_list );
158 static int Demux( demux_t * );
159 static int DemuxBlock( demux_t *, const uint8_t *, int );
160
161 static void DemuxTitles( demux_t * );
162 static void ESSubtitleUpdate( demux_t * );
163 static void ButtonUpdate( demux_t *, bool );
164
165 static void ESNew( demux_t *, int );
166 static int ProbeDVD( demux_t *, char * );
167
168 static char *DemuxGetLanguageCode( demux_t *p_demux, const char *psz_var );
169
170 static int ControlInternal( demux_t *, int, ... );
171
172 /*****************************************************************************
173  * DemuxOpen:
174  *****************************************************************************/
175 static int Open( vlc_object_t *p_this )
176 {
177     demux_t     *p_demux = (demux_t*)p_this;
178     demux_sys_t *p_sys;
179     dvdnav_t    *p_dvdnav;
180     int         i_angle;
181     char        *psz_name;
182     char        *psz_code;
183     vlc_value_t val;
184
185     if( !p_demux->psz_path || !*p_demux->psz_path )
186     {
187         /* Only when selected */
188         if( !p_this->b_force ) return VLC_EGENERIC;
189
190         psz_name = var_CreateGetString( p_this, "dvd" );
191         if( !psz_name )
192         {
193             psz_name = strdup("");
194         }
195     }
196     else
197         psz_name = ToLocaleDup( p_demux->psz_path );
198
199 #ifdef WIN32
200     if( psz_name[0] && psz_name[1] == ':' &&
201         psz_name[2] == '\\' && psz_name[3] == '\0' ) psz_name[2] = '\0';
202 #endif
203
204     /* Try some simple probing to avoid going through dvdnav_open too often */
205     if( ProbeDVD( p_demux, psz_name ) != VLC_SUCCESS )
206     {
207         free( psz_name );
208         return VLC_EGENERIC;
209     }
210
211     /* Open dvdnav */
212     if( dvdnav_open( &p_dvdnav, psz_name ) != DVDNAV_STATUS_OK )
213     {
214         msg_Warn( p_demux, "cannot open dvdnav" );
215         free( psz_name );
216         return VLC_EGENERIC;
217     }
218     free( psz_name );
219
220     /* Fill p_demux field */
221     DEMUX_INIT_COMMON(); p_sys = p_demux->p_sys;
222     p_sys->dvdnav = p_dvdnav;
223
224     ps_track_init( p_sys->tk );
225     p_sys->i_aspect = -1;
226     p_sys->i_mux_rate = 0;
227     p_sys->i_pgc_length = 0;
228     p_sys->b_spu_change = false;
229
230     if( 1 )
231     {
232         // Hack for libdvdnav CVS.
233         // Without it dvdnav_get_number_of_titles() fails.
234         // Remove when fixed in libdvdnav CVS.
235         uint8_t buffer[DVD_VIDEO_LB_LEN];
236         int i_event, i_len;
237
238         if( dvdnav_get_next_block( p_sys->dvdnav, buffer, &i_event, &i_len )
239               == DVDNAV_STATUS_ERR )
240         {
241             msg_Warn( p_demux, "dvdnav_get_next_block failed" );
242         }
243
244         dvdnav_sector_search( p_sys->dvdnav, 0, SEEK_SET );
245     }
246
247     /* Configure dvdnav */
248     if( dvdnav_set_readahead_flag( p_sys->dvdnav, DVD_READ_CACHE ) !=
249           DVDNAV_STATUS_OK )
250     {
251         msg_Warn( p_demux, "cannot set read-a-head flag" );
252     }
253
254     if( dvdnav_set_PGC_positioning_flag( p_sys->dvdnav, 1 ) !=
255           DVDNAV_STATUS_OK )
256     {
257         msg_Warn( p_demux, "cannot set PGC positioning flag" );
258     }
259
260     /* Set menu language
261      * XXX A menu-language may be better than sub-language */
262     psz_code = DemuxGetLanguageCode( p_demux, "sub-language" );
263     if( dvdnav_menu_language_select( p_sys->dvdnav, psz_code ) !=
264         DVDNAV_STATUS_OK )
265     {
266         msg_Warn( p_demux, "can't set menu language to '%s' (%s)",
267                   psz_code, dvdnav_err_to_string( p_sys->dvdnav ) );
268         /* We try to fall back to 'en' */
269         if( strcmp( psz_code, LANGUAGE_DEFAULT ) )
270             dvdnav_menu_language_select( p_sys->dvdnav, (char*)LANGUAGE_DEFAULT );
271     }
272     free( psz_code );
273
274     /* Set audio language */
275     psz_code = DemuxGetLanguageCode( p_demux, "audio-language" );
276     if( dvdnav_audio_language_select( p_sys->dvdnav, psz_code ) !=
277         DVDNAV_STATUS_OK )
278     {
279         msg_Warn( p_demux, "can't set audio language to '%s' (%s)",
280                   psz_code, dvdnav_err_to_string( p_sys->dvdnav ) );
281         /* We try to fall back to 'en' */
282         if( strcmp( psz_code, LANGUAGE_DEFAULT ) )
283             dvdnav_audio_language_select( p_sys->dvdnav, (char*)LANGUAGE_DEFAULT );
284     }
285     free( psz_code );
286
287     /* Set spu language */
288     psz_code = DemuxGetLanguageCode( p_demux, "sub-language" );
289     if( dvdnav_spu_language_select( p_sys->dvdnav, psz_code ) !=
290         DVDNAV_STATUS_OK )
291     {
292         msg_Warn( p_demux, "can't set spu language to '%s' (%s)",
293                   psz_code, dvdnav_err_to_string( p_sys->dvdnav ) );
294         /* We try to fall back to 'en' */
295         if( strcmp( psz_code, LANGUAGE_DEFAULT ) )
296             dvdnav_spu_language_select(p_sys->dvdnav, (char*)LANGUAGE_DEFAULT );
297     }
298     free( psz_code );
299
300     DemuxTitles( p_demux );
301
302     var_Create( p_demux, "dvdnav-menu", VLC_VAR_BOOL|VLC_VAR_DOINHERIT );
303     var_Get( p_demux, "dvdnav-menu", &val );
304     if( val.b_bool )
305     {
306         msg_Dbg( p_demux, "trying to go to dvd menu" );
307
308         if( dvdnav_title_play( p_sys->dvdnav, 1 ) != DVDNAV_STATUS_OK )
309         {
310             msg_Err( p_demux, "cannot set title (can't decrypt DVD?)" );
311             intf_UserFatal( p_demux, false, _("Playback failure"),
312                             _("VLC cannot set the DVD's title. It possibly "
313                               "cannot decrypt the entire disc.") );
314             dvdnav_close( p_sys->dvdnav );
315             free( p_sys );
316             return VLC_EGENERIC;
317         }
318
319         if( dvdnav_menu_call( p_sys->dvdnav, DVD_MENU_Title ) !=
320             DVDNAV_STATUS_OK )
321         {
322             /* Try going to menu root */
323             if( dvdnav_menu_call( p_sys->dvdnav, DVD_MENU_Root ) !=
324                 DVDNAV_STATUS_OK )
325                     msg_Warn( p_demux, "cannot go to dvd menu" );
326         }
327     }
328
329     var_Create( p_demux, "dvdnav-angle", VLC_VAR_INTEGER|VLC_VAR_DOINHERIT );
330     var_Get( p_demux, "dvdnav-angle", &val );
331     i_angle = val.i_int > 0 ? val.i_int : 1;
332
333     /* Update default_pts to a suitable value for dvdnav access */
334     var_Create( p_demux, "dvdnav-caching", VLC_VAR_INTEGER|VLC_VAR_DOINHERIT );
335
336     /* FIXME hack hack hack hack FIXME */
337     /* Get p_input and create variable */
338     p_sys->p_input = vlc_object_find( p_demux, VLC_OBJECT_INPUT, FIND_PARENT );
339     var_Create( p_sys->p_input, "x-start", VLC_VAR_INTEGER );
340     var_Create( p_sys->p_input, "y-start", VLC_VAR_INTEGER );
341     var_Create( p_sys->p_input, "x-end", VLC_VAR_INTEGER );
342     var_Create( p_sys->p_input, "y-end", VLC_VAR_INTEGER );
343     var_Create( p_sys->p_input, "color", VLC_VAR_ADDRESS );
344     var_Create( p_sys->p_input, "menu-palette", VLC_VAR_ADDRESS );
345     var_Create( p_sys->p_input, "highlight", VLC_VAR_BOOL );
346     var_Create( p_sys->p_input, "highlight-mutex", VLC_VAR_MUTEX );
347
348     /* Now create our event thread catcher */
349     p_sys->p_ev = vlc_object_create( p_demux, sizeof( event_thread_t ) );
350     p_sys->p_ev->p_demux = p_demux;
351     vlc_thread_create( p_sys->p_ev, "dvdnav event thread handler", EventThread,
352                        VLC_THREAD_PRIORITY_LOW );
353
354     return VLC_SUCCESS;
355 }
356
357 /*****************************************************************************
358  * Close:
359  *****************************************************************************/
360 static void Close( vlc_object_t *p_this )
361 {
362     demux_t     *p_demux = (demux_t*)p_this;
363     demux_sys_t *p_sys = p_demux->p_sys;
364     int i;
365
366     /* stop the event handler */
367     vlc_object_kill( p_sys->p_ev );
368     vlc_thread_join( p_sys->p_ev );
369     vlc_object_release( p_sys->p_ev );
370
371     var_Destroy( p_sys->p_input, "highlight-mutex" );
372     var_Destroy( p_sys->p_input, "highlight" );
373     var_Destroy( p_sys->p_input, "x-start" );
374     var_Destroy( p_sys->p_input, "x-end" );
375     var_Destroy( p_sys->p_input, "y-start" );
376     var_Destroy( p_sys->p_input, "y-end" );
377     var_Destroy( p_sys->p_input, "color" );
378     var_Destroy( p_sys->p_input, "menu-palette" );
379
380     vlc_object_release( p_sys->p_input );
381
382     for( i = 0; i < PS_TK_COUNT; i++ )
383     {
384         ps_track_t *tk = &p_sys->tk[i];
385         if( tk->b_seen )
386         {
387             es_format_Clean( &tk->fmt );
388             if( tk->es ) es_out_Del( p_demux->out, tk->es );
389         }
390     }
391
392     dvdnav_close( p_sys->dvdnav );
393     free( p_sys );
394 }
395
396 /*****************************************************************************
397  * Control:
398  *****************************************************************************/
399 static int Control( demux_t *p_demux, int i_query, va_list args )
400 {
401     demux_sys_t *p_sys = p_demux->p_sys;
402     double f, *pf;
403     bool *pb;
404     int64_t *pi64;
405     input_title_t ***ppp_title;
406     int          *pi_int;
407     int i;
408
409     switch( i_query )
410     {
411         case DEMUX_SET_POSITION:
412         case DEMUX_GET_POSITION:
413         case DEMUX_GET_TIME:
414         case DEMUX_GET_LENGTH:
415         {
416             uint32_t pos, len;
417             if( dvdnav_get_position( p_sys->dvdnav, &pos, &len ) !=
418                   DVDNAV_STATUS_OK || len == 0 )
419             {
420                 return VLC_EGENERIC;
421             }
422
423             if( i_query == DEMUX_GET_POSITION )
424             {
425                 pf = (double*)va_arg( args, double* );
426                 *pf = (double)pos / (double)len;
427                 return VLC_SUCCESS;
428             }
429             else if( i_query == DEMUX_SET_POSITION )
430             {
431                 f = (double)va_arg( args, double );
432                 pos = f * len;
433                 if( dvdnav_sector_search( p_sys->dvdnav, pos, SEEK_SET ) ==
434                       DVDNAV_STATUS_OK )
435                 {
436                     return VLC_SUCCESS;
437                 }
438             }
439             else if( i_query == DEMUX_GET_TIME )
440             {
441                 pi64 = (int64_t*)va_arg( args, int64_t * );
442                 if( p_sys->i_pgc_length > 0 )
443                 {
444                     *pi64 = p_sys->i_pgc_length * pos / len;
445                     return VLC_SUCCESS;
446                 }
447             }
448             else if( i_query == DEMUX_GET_LENGTH )
449             {
450                 pi64 = (int64_t*)va_arg( args, int64_t * );
451                 if( p_sys->i_pgc_length > 0 )
452                 {
453                     *pi64 = (int64_t)p_sys->i_pgc_length;
454                     return VLC_SUCCESS;
455                 }
456             }
457
458             return VLC_EGENERIC;
459         }
460
461         /* Special for access_demux */
462         case DEMUX_CAN_PAUSE:
463         case DEMUX_CAN_SEEK:
464         case DEMUX_CAN_CONTROL_PACE:
465             /* TODO */
466             pb = (bool*)va_arg( args, bool * );
467             *pb = true;
468             return VLC_SUCCESS;
469
470         case DEMUX_SET_PAUSE_STATE:
471             return VLC_SUCCESS;
472
473         case DEMUX_GET_TITLE_INFO:
474             ppp_title = (input_title_t***)va_arg( args, input_title_t*** );
475             pi_int    = (int*)va_arg( args, int* );
476             *((int*)va_arg( args, int* )) = 0; /* Title offset */
477             *((int*)va_arg( args, int* )) = 1; /* Chapter offset */
478
479             /* Duplicate title infos */
480             *pi_int = p_sys->i_title;
481             *ppp_title = malloc( sizeof( input_title_t ** ) * p_sys->i_title );
482             for( i = 0; i < p_sys->i_title; i++ )
483             {
484                 (*ppp_title)[i] = vlc_input_title_Duplicate( p_sys->title[i] );
485             }
486             return VLC_SUCCESS;
487
488         case DEMUX_SET_TITLE:
489             i = (int)va_arg( args, int );
490             if( ( i == 0 && dvdnav_menu_call( p_sys->dvdnav, DVD_MENU_Root )
491                   != DVDNAV_STATUS_OK ) ||
492                 ( i != 0 && dvdnav_title_play( p_sys->dvdnav, i )
493                   != DVDNAV_STATUS_OK ) )
494             {
495                 msg_Warn( p_demux, "cannot set title/chapter" );
496                 return VLC_EGENERIC;
497             }
498             p_demux->info.i_update |=
499                 INPUT_UPDATE_TITLE | INPUT_UPDATE_SEEKPOINT;
500             p_demux->info.i_title = i;
501             p_demux->info.i_seekpoint = 0;
502             return VLC_SUCCESS;
503
504         case DEMUX_SET_SEEKPOINT:
505             i = (int)va_arg( args, int );
506             if( p_demux->info.i_title == 0 )
507             {
508                 int i_ret;
509                 /* Special case */
510                 switch( i )
511                 {
512                 case 0:
513                     i_ret = dvdnav_menu_call( p_sys->dvdnav, DVD_MENU_Escape );
514                     break;
515                 case 1:
516                     i_ret = dvdnav_menu_call( p_sys->dvdnav, DVD_MENU_Root );
517                     break;
518                 case 2:
519                     i_ret = dvdnav_menu_call( p_sys->dvdnav, DVD_MENU_Title );
520                     break;
521                 case 3:
522                     i_ret = dvdnav_menu_call( p_sys->dvdnav, DVD_MENU_Part );
523                     break;
524                 case 4:
525                     i_ret = dvdnav_menu_call( p_sys->dvdnav,
526                                               DVD_MENU_Subpicture );
527                     break;
528                 case 5:
529                     i_ret = dvdnav_menu_call( p_sys->dvdnav, DVD_MENU_Audio );
530                     break;
531                 case 6:
532                     i_ret = dvdnav_menu_call( p_sys->dvdnav, DVD_MENU_Angle );
533                     break;
534                 default:
535                     return VLC_EGENERIC;
536                 }
537
538                 if( i_ret != DVDNAV_STATUS_OK )
539                     return VLC_EGENERIC;
540             }
541             else if( dvdnav_part_play( p_sys->dvdnav, p_demux->info.i_title,
542                                        i + 1 ) != DVDNAV_STATUS_OK )
543             {
544                 msg_Warn( p_demux, "cannot set title/chapter" );
545                 return VLC_EGENERIC;
546             }
547             p_demux->info.i_update |= INPUT_UPDATE_SEEKPOINT;
548             p_demux->info.i_seekpoint = i;
549             return VLC_SUCCESS;
550
551         case DEMUX_GET_PTS_DELAY:
552             pi64 = (int64_t*)va_arg( args, int64_t * );
553             *pi64 = (int64_t)var_GetInteger( p_demux, "dvdnav-caching" ) *1000;
554             return VLC_SUCCESS;
555
556         case DEMUX_GET_META:
557         {
558             const char *title_name = NULL;
559
560             dvdnav_get_title_string(p_sys->dvdnav, &title_name);
561             if( (NULL != title_name) && ('\0' != title_name[0]) )
562             {
563                 vlc_meta_t *p_meta = (vlc_meta_t*)va_arg( args, vlc_meta_t* );
564                 vlc_meta_Set( p_meta, vlc_meta_Title, title_name );
565                 return VLC_SUCCESS;
566             }
567             return VLC_EGENERIC;
568         }
569
570         /* TODO implement others */
571         default:
572             return VLC_EGENERIC;
573     }
574 }
575
576 static int ControlInternal( demux_t *p_demux, int i_query, ... )
577 {
578     va_list args;
579     int     i_result;
580
581     va_start( args, i_query );
582     i_result = Control( p_demux, i_query, args );
583     va_end( args );
584
585     return i_result;
586 }
587 /*****************************************************************************
588  * Demux:
589  *****************************************************************************/
590 static int Demux( demux_t *p_demux )
591 {
592     demux_sys_t *p_sys = p_demux->p_sys;
593
594     uint8_t buffer[DVD_VIDEO_LB_LEN];
595     uint8_t *packet = buffer;
596     int i_event;
597     int i_len;
598
599 #if DVD_READ_CACHE
600     if( dvdnav_get_next_cache_block( p_sys->dvdnav, &packet, &i_event, &i_len )
601         == DVDNAV_STATUS_ERR )
602 #else
603     if( dvdnav_get_next_block( p_sys->dvdnav, packet, &i_event, &i_len )
604         == DVDNAV_STATUS_ERR )
605 #endif
606     {
607         msg_Warn( p_demux, "cannot get next block (%s)",
608                   dvdnav_err_to_string( p_sys->dvdnav ) );
609         if( p_demux->info.i_title == 0 )
610         {
611             msg_Dbg( p_demux, "jumping to first title" );
612             return ControlInternal( p_demux, DEMUX_SET_TITLE, 1 ) == VLC_SUCCESS ? 1 : -1;
613         }
614         return -1;
615     }
616
617     switch( i_event )
618     {
619     case DVDNAV_BLOCK_OK:   /* mpeg block */
620         p_sys->p_ev->b_still = false;
621         DemuxBlock( p_demux, packet, i_len );
622         break;
623
624     case DVDNAV_NOP:    /* Nothing */
625         msg_Dbg( p_demux, "DVDNAV_NOP" );
626         break;
627
628     case DVDNAV_STILL_FRAME:
629     {
630         dvdnav_still_event_t *event = (dvdnav_still_event_t*)packet;
631         vlc_mutex_lock( &p_sys->p_ev->lock );
632         if( !p_sys->p_ev->b_still )
633         {
634             /* We send a dummy mpeg2 end of sequence to force still frame display */
635             static const uint8_t buffer[] = {
636                 0x00, 0x00, 0x01, 0xe0, 0x00, 0x07,
637                 0x80, 0x00, 0x00,
638                 0x00, 0x00, 0x01, 0xB7,
639             };
640             DemuxBlock( p_demux, buffer, sizeof(buffer) );
641
642             msg_Dbg( p_demux, "DVDNAV_STILL_FRAME" );
643             msg_Dbg( p_demux, "     - length=0x%x", event->length );
644             p_sys->p_ev->b_still = true;
645             if( event->length == 0xff )
646             {
647                 p_sys->p_ev->i_still_end = 0;
648             }
649             else
650             {
651                 p_sys->p_ev->i_still_end = (int64_t)event->length *
652                     1000000 + mdate();
653             }
654         }
655         vlc_mutex_unlock( &p_sys->p_ev->lock );
656         msleep( 40000 );
657         break;
658     }
659
660     case DVDNAV_SPU_CLUT_CHANGE:
661     {
662         int i;
663
664         msg_Dbg( p_demux, "DVDNAV_SPU_CLUT_CHANGE" );
665         /* Update color lookup table (16 *uint32_t in packet) */
666         memcpy( p_sys->clut, packet, 16 * sizeof( uint32_t ) );
667
668         /* HACK to get the SPU tracks registered in the right order */
669         for( i = 0; i < 0x1f; i++ )
670         {
671             if( dvdnav_spu_stream_to_lang( p_sys->dvdnav, i ) != 0xffff )
672                 ESNew( p_demux, 0xbd20 + i );
673         }
674         /* END HACK */
675         break;
676     }
677
678     case DVDNAV_SPU_STREAM_CHANGE:
679     {
680         dvdnav_spu_stream_change_event_t *event =
681             (dvdnav_spu_stream_change_event_t*)packet;
682         int i;
683
684         msg_Dbg( p_demux, "DVDNAV_SPU_STREAM_CHANGE" );
685         msg_Dbg( p_demux, "     - physical_wide=%d",
686                  event->physical_wide );
687         msg_Dbg( p_demux, "     - physical_letterbox=%d",
688                  event->physical_letterbox);
689         msg_Dbg( p_demux, "     - physical_pan_scan=%d",
690                  event->physical_pan_scan );
691
692         ESSubtitleUpdate( p_demux );
693         p_sys->b_spu_change = true;
694
695         /* HACK to get the SPU tracks registered in the right order */
696         for( i = 0; i < 0x1f; i++ )
697         {
698             if( dvdnav_spu_stream_to_lang( p_sys->dvdnav, i ) != 0xffff )
699                 ESNew( p_demux, 0xbd20 + i );
700         }
701         /* END HACK */
702         break;
703     }
704
705     case DVDNAV_AUDIO_STREAM_CHANGE:
706     {
707         dvdnav_audio_stream_change_event_t *event =
708             (dvdnav_audio_stream_change_event_t*)packet;
709         msg_Dbg( p_demux, "DVDNAV_AUDIO_STREAM_CHANGE" );
710         msg_Dbg( p_demux, "     - physical=%d", event->physical );
711         /* TODO */
712         break;
713     }
714
715     case DVDNAV_VTS_CHANGE:
716     {
717         int32_t i_title = 0;
718         int32_t i_part  = 0;
719         int i;
720
721         dvdnav_vts_change_event_t *event = (dvdnav_vts_change_event_t*)packet;
722         msg_Dbg( p_demux, "DVDNAV_VTS_CHANGE" );
723         msg_Dbg( p_demux, "     - vtsN=%d", event->new_vtsN );
724         msg_Dbg( p_demux, "     - domain=%d", event->new_domain );
725
726         /* dvdnav_get_video_aspect / dvdnav_get_video_scale_permission */
727         /* TODO check if we always have VTS and CELL */
728         p_sys->i_aspect = dvdnav_get_video_aspect( p_sys->dvdnav );
729
730         /* reset PCR */
731         es_out_Control( p_demux->out, ES_OUT_RESET_PCR );
732
733         for( i = 0; i < PS_TK_COUNT; i++ )
734         {
735             ps_track_t *tk = &p_sys->tk[i];
736             if( tk->b_seen )
737             {
738                 es_format_Clean( &tk->fmt );
739                 if( tk->es ) es_out_Del( p_demux->out, tk->es );
740             }
741             tk->b_seen = false;
742         }
743
744         if( dvdnav_current_title_info( p_sys->dvdnav, &i_title,
745                                        &i_part ) == DVDNAV_STATUS_OK )
746         {
747             if( i_title >= 0 && i_title < p_sys->i_title &&
748                 p_demux->info.i_title != i_title )
749             {
750                 p_demux->info.i_update |= INPUT_UPDATE_TITLE;
751                 p_demux->info.i_title = i_title;
752             }
753         }
754         break;
755     }
756
757     case DVDNAV_CELL_CHANGE:
758     {
759         int32_t i_title = 0;
760         int32_t i_part  = 0;
761
762         dvdnav_cell_change_event_t *event =
763             (dvdnav_cell_change_event_t*)packet;
764         msg_Dbg( p_demux, "DVDNAV_CELL_CHANGE" );
765         msg_Dbg( p_demux, "     - cellN=%d", event->cellN );
766         msg_Dbg( p_demux, "     - pgN=%d", event->pgN );
767         msg_Dbg( p_demux, "     - cell_length=%"PRId64, event->cell_length );
768         msg_Dbg( p_demux, "     - pg_length=%"PRId64, event->pg_length );
769         msg_Dbg( p_demux, "     - pgc_length=%"PRId64, event->pgc_length );
770         msg_Dbg( p_demux, "     - cell_start=%"PRId64, event->cell_start );
771         msg_Dbg( p_demux, "     - pg_start=%"PRId64, event->pg_start );
772
773         /* Store the lenght in time of the current PGC */
774         p_sys->i_pgc_length = event->pgc_length / 90 * 1000;
775
776         /* FIXME is it correct or there is better way to know chapter change */
777         if( dvdnav_current_title_info( p_sys->dvdnav, &i_title,
778                                        &i_part ) == DVDNAV_STATUS_OK )
779         {
780             if( i_title >= 0 && i_title < p_sys->i_title &&
781                 i_part >= 1 && i_part <= p_sys->title[i_title]->i_seekpoint &&
782                 p_demux->info.i_seekpoint != i_part - 1 )
783             {
784                 p_demux->info.i_update |= INPUT_UPDATE_SEEKPOINT;
785                 p_demux->info.i_seekpoint = i_part - 1;
786             }
787         }
788         break;
789     }
790
791     case DVDNAV_NAV_PACKET:
792     {
793 #ifdef DVDNAV_DEBUG
794         msg_Dbg( p_demux, "DVDNAV_NAV_PACKET" );
795 #endif
796         /* A lot of thing to do here :
797          *  - handle packet
798          *  - fetch pts (for time display)
799          *  - ...
800          */
801         DemuxBlock( p_demux, packet, i_len );
802         if( p_sys->b_spu_change )
803         {
804             ButtonUpdate( p_demux, false );
805             p_sys->b_spu_change = false;
806         }
807         break;
808     }
809
810     case DVDNAV_STOP:   /* EOF */
811         msg_Dbg( p_demux, "DVDNAV_STOP" );
812
813 #if DVD_READ_CACHE
814         dvdnav_free_cache_block( p_sys->dvdnav, packet );
815 #endif
816         return 0;
817
818     case DVDNAV_HIGHLIGHT:
819     {
820         dvdnav_highlight_event_t *event = (dvdnav_highlight_event_t*)packet;
821         msg_Dbg( p_demux, "DVDNAV_HIGHLIGHT" );
822         msg_Dbg( p_demux, "     - display=%d", event->display );
823         msg_Dbg( p_demux, "     - buttonN=%d", event->buttonN );
824         ButtonUpdate( p_demux, false );
825         break;
826     }
827
828     case DVDNAV_HOP_CHANNEL:
829         msg_Dbg( p_demux, "DVDNAV_HOP_CHANNEL" );
830         /* We should try to flush all our internal buffer */
831         break;
832
833     case DVDNAV_WAIT:
834         msg_Dbg( p_demux, "DVDNAV_WAIT" );
835
836         /* reset PCR */
837         es_out_Control( p_demux->out, ES_OUT_RESET_PCR );
838         dvdnav_wait_skip( p_sys->dvdnav );
839         break;
840
841     default:
842         msg_Warn( p_demux, "Unknown event (0x%x)", i_event );
843         break;
844     }
845
846 #if DVD_READ_CACHE
847     dvdnav_free_cache_block( p_sys->dvdnav, packet );
848 #endif
849
850     return 1;
851 }
852
853 /* Get a 2 char code
854  * FIXME: partiallyy duplicated from src/input/es_out.c
855  */
856 static char *DemuxGetLanguageCode( demux_t *p_demux, const char *psz_var )
857 {
858     const iso639_lang_t *pl;
859     char *psz_lang;
860     char *p;
861
862     psz_lang = var_CreateGetString( p_demux, psz_var );
863     if( !psz_lang )
864         return strdup(LANGUAGE_DEFAULT);
865
866     /* XXX: we will use only the first value
867      * (and ignore other ones in case of a list) */
868     if( ( p = strchr( psz_lang, ',' ) ) )
869         *p = '\0';
870
871     for( pl = p_languages; pl->psz_iso639_1 != NULL; pl++ )
872     {
873         if( *psz_lang == '\0' )
874             continue;
875         if( !strcasecmp( pl->psz_eng_name, psz_lang ) ||
876             !strcasecmp( pl->psz_native_name, psz_lang ) ||
877             !strcasecmp( pl->psz_iso639_1, psz_lang ) ||
878             !strcasecmp( pl->psz_iso639_2T, psz_lang ) ||
879             !strcasecmp( pl->psz_iso639_2B, psz_lang ) )
880             break;
881     }
882
883     free( psz_lang );
884
885     if( pl->psz_iso639_1 != NULL )
886         return strdup( pl->psz_iso639_1 );
887
888     return strdup(LANGUAGE_DEFAULT);
889 }
890
891 static void DemuxTitles( demux_t *p_demux )
892 {
893     demux_sys_t *p_sys = p_demux->p_sys;
894     input_title_t *t;
895     seekpoint_t *s;
896     int32_t i_titles;
897     int i;
898
899     /* Menu */
900     t = vlc_input_title_New();
901     t->b_menu = true;
902     t->psz_name = strdup( "DVD Menu" );
903
904     s = vlc_seekpoint_New();
905     s->psz_name = strdup( "Resume" );
906     TAB_APPEND( t->i_seekpoint, t->seekpoint, s );
907
908     s = vlc_seekpoint_New();
909     s->psz_name = strdup( "Root" );
910     TAB_APPEND( t->i_seekpoint, t->seekpoint, s );
911
912     s = vlc_seekpoint_New();
913     s->psz_name = strdup( "Title" );
914     TAB_APPEND( t->i_seekpoint, t->seekpoint, s );
915
916     s = vlc_seekpoint_New();
917     s->psz_name = strdup( "Chapter" );
918     TAB_APPEND( t->i_seekpoint, t->seekpoint, s );
919
920     s = vlc_seekpoint_New();
921     s->psz_name = strdup( "Subtitle" );
922     TAB_APPEND( t->i_seekpoint, t->seekpoint, s );
923
924     s = vlc_seekpoint_New();
925     s->psz_name = strdup( "Audio" );
926     TAB_APPEND( t->i_seekpoint, t->seekpoint, s );
927
928     s = vlc_seekpoint_New();
929     s->psz_name = strdup( "Angle" );
930     TAB_APPEND( t->i_seekpoint, t->seekpoint, s );
931
932     TAB_APPEND( p_sys->i_title, p_sys->title, t );
933
934     /* Find out number of titles/chapters */
935     dvdnav_get_number_of_titles( p_sys->dvdnav, &i_titles );
936     for( i = 1; i <= i_titles; i++ )
937     {
938         int32_t i_chapters = 0;
939         int j;
940
941         dvdnav_get_number_of_parts( p_sys->dvdnav, i, &i_chapters );
942
943         t = vlc_input_title_New();
944         for( j = 0; j < __MAX( i_chapters, 1 ); j++ )
945         {
946             s = vlc_seekpoint_New();
947             TAB_APPEND( t->i_seekpoint, t->seekpoint, s );
948         }
949
950         TAB_APPEND( p_sys->i_title, p_sys->title, t );
951     }
952 }
953
954 /*****************************************************************************
955  * Update functions:
956  *****************************************************************************/
957 static void ButtonUpdate( demux_t *p_demux, bool b_mode )
958 {
959     demux_sys_t *p_sys = p_demux->p_sys;
960     vlc_value_t val;
961     int32_t i_title, i_part;
962
963     dvdnav_current_title_info( p_sys->dvdnav, &i_title, &i_part );
964
965     if( var_Get( p_sys->p_input, "highlight-mutex", &val ) == VLC_SUCCESS )
966     {
967         vlc_mutex_t *p_mutex = val.p_address;
968         dvdnav_highlight_area_t hl;
969         int32_t i_button;
970         bool    b_button_ok;
971
972         if( dvdnav_get_current_highlight( p_sys->dvdnav, &i_button )
973             != DVDNAV_STATUS_OK )
974         {
975             msg_Err( p_demux, "dvdnav_get_current_highlight failed" );
976             return;
977         }
978
979         b_button_ok = false;
980         if( i_button > 0 && i_title ==  0 )
981         {
982             pci_t *pci = dvdnav_get_current_nav_pci( p_sys->dvdnav );
983
984             b_button_ok = dvdnav_get_highlight_area( pci, i_button, b_mode, &hl ) == DVDNAV_STATUS_OK;
985         }
986         if( b_button_ok )
987         {
988             int i;
989             for( i = 0; i < 4; i++ )
990             {
991                 uint32_t i_yuv = p_sys->clut[(hl.palette>>(16+i*4))&0x0f];
992                 uint8_t i_alpha = ( (hl.palette>>(i*4))&0x0f ) * 0xff / 0xf;
993
994                 p_sys->palette[i][0] = (i_yuv >> 16) & 0xff;
995                 p_sys->palette[i][1] = (i_yuv >> 0) & 0xff;
996                 p_sys->palette[i][2] = (i_yuv >> 8) & 0xff;
997                 p_sys->palette[i][3] = i_alpha;
998             }
999
1000             vlc_mutex_lock( p_mutex );
1001             val.i_int = hl.sx; var_Set( p_sys->p_input, "x-start", val );
1002             val.i_int = hl.ex; var_Set( p_sys->p_input, "x-end", val );
1003             val.i_int = hl.sy; var_Set( p_sys->p_input, "y-start", val );
1004             val.i_int = hl.ey; var_Set( p_sys->p_input, "y-end", val );
1005
1006             val.p_address = (void *)p_sys->palette;
1007             var_Set( p_sys->p_input, "menu-palette", val );
1008
1009             val.b_bool = true; var_Set( p_sys->p_input, "highlight", val );
1010             vlc_mutex_unlock( p_mutex );
1011
1012             msg_Dbg( p_demux, "buttonUpdate %d", i_button );
1013         }
1014         else
1015         {
1016             msg_Dbg( p_demux, "buttonUpdate not done b=%d t=%d",
1017                      i_button, i_title );
1018
1019             /* Show all */
1020             vlc_mutex_lock( p_mutex );
1021             val.b_bool = false;
1022             var_Set( p_sys->p_input, "highlight", val );
1023             vlc_mutex_unlock( p_mutex );
1024         }
1025     }
1026 }
1027
1028 static void ESSubtitleUpdate( demux_t *p_demux )
1029 {
1030     demux_sys_t *p_sys = p_demux->p_sys;
1031     int         i_spu = dvdnav_get_active_spu_stream( p_sys->dvdnav );
1032     int32_t i_title, i_part;
1033
1034     ButtonUpdate( p_demux, false );
1035
1036     dvdnav_current_title_info( p_sys->dvdnav, &i_title, &i_part );
1037     if( i_title > 0 ) return;
1038
1039     if( i_spu >= 0 && i_spu <= 0x1f )
1040     {
1041         ps_track_t *tk = &p_sys->tk[PS_ID_TO_TK(0xbd20 + i_spu)];
1042
1043         ESNew( p_demux, 0xbd20 + i_spu );
1044
1045         /* be sure to unselect it (reset) */
1046         es_out_Control( p_demux->out, ES_OUT_SET_ES_STATE, tk->es,
1047                         (bool)false );
1048
1049         /* now select it */
1050         es_out_Control( p_demux->out, ES_OUT_SET_ES, tk->es );
1051     }
1052     else
1053     {
1054         for( i_spu = 0; i_spu <= 0x1F; i_spu++ )
1055         {
1056             ps_track_t *tk = &p_sys->tk[PS_ID_TO_TK(0xbd20 + i_spu)];
1057             if( tk->b_seen )
1058             {
1059                 es_out_Control( p_demux->out, ES_OUT_SET_ES_STATE, tk->es,
1060                                 (bool)false );
1061             }
1062         }
1063     }
1064 }
1065
1066 /*****************************************************************************
1067  * DemuxBlock: demux a given block
1068  *****************************************************************************/
1069 static int DemuxBlock( demux_t *p_demux, const uint8_t *pkt, int i_pkt )
1070 {
1071     demux_sys_t *p_sys = p_demux->p_sys;
1072     const uint8_t     *p = pkt;
1073
1074     while( p < &pkt[i_pkt] )
1075     {
1076         int i_size = ps_pkt_size( p, &pkt[i_pkt] - p );
1077         block_t *p_pkt;
1078         if( i_size <= 0 )
1079         {
1080             break;
1081         }
1082
1083         /* Create a block */
1084         p_pkt = block_New( p_demux, i_size );
1085         memcpy( p_pkt->p_buffer, p, i_size);
1086
1087         /* Parse it and send it */
1088         switch( 0x100 | p[3] )
1089         {
1090         case 0x1b9:
1091         case 0x1bb:
1092         case 0x1bc:
1093 #ifdef DVDNAV_DEBUG
1094             if( p[3] == 0xbc )
1095             {
1096                 msg_Warn( p_demux, "received a PSM packet" );
1097             }
1098             else if( p[3] == 0xbb )
1099             {
1100                 msg_Warn( p_demux, "received a SYSTEM packet" );
1101             }
1102 #endif
1103             block_Release( p_pkt );
1104             break;
1105
1106         case 0x1ba:
1107         {
1108             int64_t i_scr;
1109             int i_mux_rate;
1110             if( !ps_pkt_parse_pack( p_pkt, &i_scr, &i_mux_rate ) )
1111             {
1112                 es_out_Control( p_demux->out, ES_OUT_SET_PCR, i_scr );
1113                 if( i_mux_rate > 0 ) p_sys->i_mux_rate = i_mux_rate;
1114             }
1115             block_Release( p_pkt );
1116             break;
1117         }
1118         default:
1119         {
1120             int i_id = ps_pkt_id( p_pkt );
1121             if( i_id >= 0xc0 )
1122             {
1123                 ps_track_t *tk = &p_sys->tk[PS_ID_TO_TK(i_id)];
1124
1125                 if( !tk->b_seen )
1126                 {
1127                     ESNew( p_demux, i_id );
1128                 }
1129                 if( tk->b_seen && tk->es &&
1130                     !ps_pkt_parse_pes( p_pkt, tk->i_skip ) )
1131                 {
1132                     es_out_Send( p_demux->out, tk->es, p_pkt );
1133                 }
1134                 else
1135                 {
1136                     block_Release( p_pkt );
1137                 }
1138             }
1139             else
1140             {
1141                 block_Release( p_pkt );
1142             }
1143             break;
1144         }
1145         }
1146
1147         p += i_size;
1148     }
1149
1150     return VLC_SUCCESS;
1151 }
1152
1153 /*****************************************************************************
1154  * ESNew: register a new elementary stream
1155  *****************************************************************************/
1156 static void ESNew( demux_t *p_demux, int i_id )
1157 {
1158     demux_sys_t *p_sys = p_demux->p_sys;
1159     ps_track_t  *tk = &p_sys->tk[PS_ID_TO_TK(i_id)];
1160     bool  b_select = false;
1161
1162     if( tk->b_seen ) return;
1163
1164     if( ps_track_fill( tk, 0, i_id ) )
1165     {
1166         msg_Warn( p_demux, "unknown codec for id=0x%x", i_id );
1167         return;
1168     }
1169
1170     /* Add a new ES */
1171     if( tk->fmt.i_cat == VIDEO_ES )
1172     {
1173         switch( p_sys->i_aspect )
1174         {
1175         case 1: tk->fmt.video.i_aspect = VOUT_ASPECT_FACTOR; break;
1176         case 2: tk->fmt.video.i_aspect = VOUT_ASPECT_FACTOR * 4 / 3; break;
1177         case 3: tk->fmt.video.i_aspect = VOUT_ASPECT_FACTOR * 16 / 9; break;
1178         case 4: tk->fmt.video.i_aspect = VOUT_ASPECT_FACTOR * 221 / 10; break;
1179         default:
1180             tk->fmt.video.i_aspect = 0;
1181             break;
1182         }
1183         b_select = true;
1184     }
1185     else if( tk->fmt.i_cat == AUDIO_ES )
1186     {
1187         int i_audio = -1;
1188         /* find the audio number PLEASE find another way */
1189         if( (i_id&0xbdf8) == 0xbd88 )       /* dts */
1190         {
1191             i_audio = i_id&0x07;
1192         }
1193         else if( (i_id&0xbdf0) == 0xbd80 )  /* a52 */
1194         {
1195             i_audio = i_id&0xf;
1196         }
1197         else if( (i_id&0xbdf0) == 0xbda0 )  /* lpcm */
1198         {
1199             i_audio = i_id&0x1f;
1200         }
1201         else if( ( i_id&0xe0 ) == 0xc0 )    /* mpga */
1202         {
1203             i_audio = i_id&0x1f;
1204         }
1205         if( i_audio >= 0 )
1206         {
1207             int i_lang = dvdnav_audio_stream_to_lang( p_sys->dvdnav, i_audio );
1208             if( i_lang != 0xffff )
1209             {
1210                 tk->fmt.psz_language = malloc( 3 );
1211                 tk->fmt.psz_language[0] = (i_lang >> 8)&0xff;
1212                 tk->fmt.psz_language[1] = (i_lang     )&0xff;
1213                 tk->fmt.psz_language[2] = 0;
1214             }
1215             if( dvdnav_get_active_audio_stream( p_sys->dvdnav ) == i_audio )
1216             {
1217                 b_select = true;
1218             }
1219         }
1220     }
1221     else if( tk->fmt.i_cat == SPU_ES )
1222     {
1223         int32_t i_title, i_part;
1224         int i_lang = dvdnav_spu_stream_to_lang( p_sys->dvdnav, i_id&0x1f );
1225         if( i_lang != 0xffff )
1226         {
1227             tk->fmt.psz_language = malloc( 3 );
1228             tk->fmt.psz_language[0] = (i_lang >> 8)&0xff;
1229             tk->fmt.psz_language[1] = (i_lang     )&0xff;
1230             tk->fmt.psz_language[2] = 0;
1231         }
1232
1233         /* Palette */
1234         tk->fmt.subs.spu.palette[0] = 0xBeef;
1235         memcpy( &tk->fmt.subs.spu.palette[1], p_sys->clut,
1236                 16 * sizeof( uint32_t ) );
1237
1238         /* We select only when we are not in the menu */
1239         dvdnav_current_title_info( p_sys->dvdnav, &i_title, &i_part );
1240         if( i_title > 0 &&
1241             dvdnav_get_active_spu_stream( p_sys->dvdnav ) == (i_id&0x1f) )
1242         {
1243             b_select = true;
1244         }
1245     }
1246
1247     tk->es = es_out_Add( p_demux->out, &tk->fmt );
1248     if( b_select )
1249     {
1250         es_out_Control( p_demux->out, ES_OUT_SET_ES, tk->es );
1251     }
1252     tk->b_seen = true;
1253
1254     if( tk->fmt.i_cat == VIDEO_ES ) ButtonUpdate( p_demux, false );
1255 }
1256
1257 /*****************************************************************************
1258  * Event handler code
1259  *****************************************************************************/
1260 static int  EventMouse( vlc_object_t *, char const *,
1261                         vlc_value_t, vlc_value_t, void * );
1262 static int  EventKey  ( vlc_object_t *, char const *,
1263                         vlc_value_t, vlc_value_t, void * );
1264
1265 static void* EventThread( vlc_object_t *p_this )
1266 {
1267     event_thread_t *p_ev = (event_thread_t*)p_this;
1268     demux_sys_t    *p_sys = p_ev->p_demux->p_sys;
1269     vlc_object_t   *p_vout = NULL;
1270
1271     vlc_mutex_init( &p_ev->lock );
1272     p_ev->b_moved   = false;
1273     p_ev->b_clicked = false;
1274     p_ev->i_key_action = 0;
1275     p_ev->b_still   = false;
1276
1277     int canc = vlc_savecancel ();
1278
1279     /* catch all key event */
1280     var_AddCallback( p_ev->p_libvlc, "key-action", EventKey, p_ev );
1281
1282     /* main loop */
1283     while( vlc_object_alive (p_ev) )
1284     {
1285         bool b_activated = false;
1286
1287         /* KEY part */
1288         if( p_ev->i_key_action != 0 )
1289         {
1290             pci_t *pci = dvdnav_get_current_nav_pci( p_sys->dvdnav );
1291
1292             vlc_mutex_lock( &p_ev->lock );
1293             switch( p_ev->i_key_action )
1294             {
1295             case ACTIONID_NAV_LEFT:
1296                 dvdnav_left_button_select( p_sys->dvdnav, pci );
1297                 break;
1298             case ACTIONID_NAV_RIGHT:
1299                 dvdnav_right_button_select( p_sys->dvdnav, pci );
1300                 break;
1301             case ACTIONID_NAV_UP:
1302                 dvdnav_upper_button_select( p_sys->dvdnav, pci );
1303                 break;
1304             case ACTIONID_NAV_DOWN:
1305                 dvdnav_lower_button_select( p_sys->dvdnav, pci );
1306                 break;
1307             case ACTIONID_NAV_ACTIVATE:
1308                 b_activated = true;
1309                 ButtonUpdate( p_ev->p_demux, true );
1310                 dvdnav_button_activate( p_sys->dvdnav, pci );
1311                 break;
1312             default:
1313                 break;
1314             }
1315             p_ev->i_key_action = 0;
1316             vlc_mutex_unlock( &p_ev->lock );
1317         }
1318
1319         /* VOUT part */
1320         if( p_vout && ( p_ev->b_moved || p_ev->b_clicked ) )
1321         {
1322             pci_t *pci = dvdnav_get_current_nav_pci( p_sys->dvdnav );
1323             vlc_value_t valx, valy;
1324
1325             vlc_mutex_lock( &p_ev->lock );
1326             var_Get( p_vout, "mouse-x", &valx );
1327             var_Get( p_vout, "mouse-y", &valy );
1328
1329             if( p_ev->b_moved )
1330             {
1331                 dvdnav_mouse_select( p_sys->dvdnav, pci, valx.i_int,
1332                                      valy.i_int );
1333             }
1334             if( p_ev->b_clicked )
1335             {
1336                 b_activated = true;
1337                 ButtonUpdate( p_ev->p_demux, true );
1338                 dvdnav_mouse_activate( p_sys->dvdnav, pci, valx.i_int,
1339                                        valy.i_int );
1340             }
1341
1342             p_ev->b_moved = false;
1343             p_ev->b_clicked = false;
1344             vlc_mutex_unlock( &p_ev->lock );
1345         }
1346         if( p_vout && !vlc_object_alive (p_vout) )
1347         {
1348             var_DelCallback( p_vout, "mouse-moved", EventMouse, p_ev );
1349             var_DelCallback( p_vout, "mouse-clicked", EventMouse, p_ev );
1350             vlc_object_release( p_vout );
1351             p_vout = NULL;
1352         }
1353         if( p_vout == NULL )
1354         {
1355             p_vout = vlc_object_find( p_sys->p_input, VLC_OBJECT_VOUT,
1356                                       FIND_CHILD );
1357             if( p_vout)
1358             {
1359                 var_AddCallback( p_vout, "mouse-moved", EventMouse, p_ev );
1360                 var_AddCallback( p_vout, "mouse-clicked", EventMouse, p_ev );
1361             }
1362         }
1363
1364         /* Still part */
1365         vlc_mutex_lock( &p_ev->lock );
1366         if( p_ev->b_still )
1367         {
1368             if( /* b_activated || // This breaks menus */
1369                 ( p_ev->i_still_end > 0 && p_ev->i_still_end < mdate() ))
1370             {
1371                 p_ev->b_still = false;
1372                 dvdnav_still_skip( p_sys->dvdnav );
1373             }
1374         }
1375         vlc_mutex_unlock( &p_ev->lock );
1376
1377         /* Wait a bit */
1378         msleep( 10000 );
1379     }
1380
1381     /* Release callback */
1382     if( p_vout )
1383     {
1384         var_DelCallback( p_vout, "mouse-moved", EventMouse, p_ev );
1385         var_DelCallback( p_vout, "mouse-clicked", EventMouse, p_ev );
1386         vlc_object_release( p_vout );
1387     }
1388     var_DelCallback( p_ev->p_libvlc, "key-action", EventKey, p_ev );
1389     vlc_restorecancel (canc);
1390     vlc_mutex_destroy( &p_ev->lock );
1391
1392     return NULL;
1393 }
1394
1395 static int EventMouse( vlc_object_t *p_this, char const *psz_var,
1396                        vlc_value_t oldval, vlc_value_t newval, void *p_data )
1397 {
1398     (void)p_this;    (void)oldval;    (void)newval;    (void)p_data;
1399     event_thread_t *p_ev = p_data;
1400     vlc_mutex_lock( &p_ev->lock );
1401     if( psz_var[6] == 'c' )
1402         p_ev->b_clicked = true;
1403     else if( psz_var[6] == 'm' )
1404         p_ev->b_moved = true;
1405     vlc_mutex_unlock( &p_ev->lock );
1406
1407     return VLC_SUCCESS;
1408 }
1409
1410 static int EventKey( vlc_object_t *p_this, char const *psz_var,
1411                      vlc_value_t oldval, vlc_value_t newval, void *p_data )
1412 {
1413     (void)p_this;    (void)psz_var;    (void)oldval;
1414     event_thread_t *p_ev = p_data;
1415     vlc_mutex_lock( &p_ev->lock );
1416     p_ev->i_key_action = newval.i_int;
1417     vlc_mutex_unlock( &p_ev->lock );
1418
1419     return VLC_SUCCESS;
1420 }
1421
1422 /*****************************************************************************
1423  * ProbeDVD: very weak probing that avoids going too often into a dvdnav_open()
1424  *****************************************************************************/
1425 static int ProbeDVD( demux_t *p_demux, char *psz_name )
1426 {
1427     (void)p_demux;
1428 #ifdef HAVE_SYS_STAT_H
1429     struct stat stat_info;
1430     uint8_t pi_anchor[2];
1431     int i_fd, i_ret;
1432
1433     if( !*psz_name )
1434     {
1435         /* Triggers libdvdcss autodetection */
1436         return VLC_SUCCESS;
1437     }
1438
1439     if( (i_fd = open( psz_name, O_RDONLY )) == -1 )
1440     {
1441         return VLC_SUCCESS; /* Let dvdnav_open() do the probing */
1442     }
1443
1444     if( fstat( i_fd, &stat_info ) || !S_ISREG( stat_info.st_mode ) )
1445     {
1446         close( i_fd );
1447         return VLC_SUCCESS; /* Let dvdnav_open() do the probing */
1448     }
1449
1450     /* Try to find the anchor (2 bytes at LBA 256) */
1451     i_ret = VLC_EGENERIC;
1452     if( lseek( i_fd, 256 * DVD_VIDEO_LB_LEN, SEEK_SET ) != -1
1453      && read( i_fd, pi_anchor, 2 ) == 2
1454      && GetWLE( pi_anchor ) == 2 )
1455         i_ret = VLC_SUCCESS; /* Found a potential anchor */
1456
1457     close( i_fd );
1458
1459     return i_ret;
1460 #else
1461
1462     return VLC_SUCCESS;
1463 #endif
1464 }