]> git.sesse.net Git - vlc/blob - modules/access/dvdnav.c
* demux/dvdnav.c, access/dvdnav.c: moved dvdnav.c in the access directory.
[vlc] / modules / access / dvdnav.c
1 /*****************************************************************************
2  * dvdnav.c: DVD module using the dvdnav library.
3  *****************************************************************************
4  * Copyright (C) 2004 VideoLAN
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., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
22  *****************************************************************************/
23
24 /*****************************************************************************
25  * Preamble
26  *****************************************************************************/
27 #include <stdlib.h>
28
29 #include <vlc/vlc.h>
30 #include <vlc/input.h>
31
32 #include "vlc_keys.h"
33 #include "iso_lang.h"
34
35 #include <dvdnav/dvdnav.h>
36
37 #include "../demux/ps.h"
38
39 /*****************************************************************************
40  * Module descriptor
41  *****************************************************************************/
42 #define CACHING_TEXT N_("caching value in ms")
43 #define CACHING_LONGTEXT N_( \
44     "Allows you to modify the default caching value for DVDnav streams. This "\
45     "value should be set in millisecond units." )
46
47 static int  Open ( vlc_object_t * );
48 static void Close( vlc_object_t * );
49
50 vlc_module_begin();
51     set_description( _("DVDnav Input") );
52     add_integer( "dvdnav-caching", DEFAULT_PTS_DELAY / 1000, NULL,
53         CACHING_TEXT, CACHING_LONGTEXT, VLC_TRUE );
54     set_capability( "access_demux", 0 );
55     add_shortcut( "dvd" );
56     add_shortcut( "dvdnav" );
57     add_shortcut( "dvdnavsimple" );
58     set_callbacks( Open, Close );
59 vlc_module_end();
60
61 /* Shall we use libdvdnav's read ahead cache? */
62 #define DVD_READ_CACHE 1
63
64 /*****************************************************************************
65  * Local prototypes
66  *****************************************************************************/
67 static char *ParseCL( vlc_object_t *, char *, vlc_bool_t, int *, int *, int *);
68
69 typedef struct
70 {
71     VLC_COMMON_MEMBERS
72
73     demux_t        *p_demux;
74     vlc_mutex_t     lock;
75
76     vlc_bool_t      b_moved;
77     vlc_bool_t      b_clicked;
78     vlc_bool_t      b_key;
79
80     vlc_bool_t      b_still;
81     int64_t         i_still_end;
82
83 } event_thread_t;
84
85 static int EventThread( vlc_object_t * );
86
87 struct demux_sys_t
88 {
89     dvdnav_t    *dvdnav;
90
91     /* track */
92     ps_track_t  tk[PS_TK_COUNT];
93
94     /* for spu variables */
95     input_thread_t *p_input;
96
97     /* event */
98     event_thread_t *p_ev;
99
100     /* FIXME */
101     uint8_t     alpha[4];
102     uint32_t    clut[16];
103
104     /* */
105     int i_aspect;
106
107     /* */
108     vlc_bool_t  b_es_out_ok;
109     vlc_bool_t  b_simple;
110
111     int           i_title;
112     input_title_t **title;
113 };
114
115 static int Control( demux_t *, int, va_list );
116 static int Demux  ( demux_t * );
117 static int DemuxBlock  ( demux_t *, uint8_t *pkt, int i_pkt );
118
119 enum
120 {
121     AR_SQUARE_PICTURE = 1,                          /* square pixels */
122     AR_3_4_PICTURE    = 2,                       /* 3:4 picture (TV) */
123     AR_16_9_PICTURE   = 3,             /* 16:9 picture (wide screen) */
124     AR_221_1_PICTURE  = 4,                 /* 2.21:1 picture (movie) */
125 };
126
127 static void DemuxTitles( demux_t *p_demux );
128 static void ESSubtitleUpdate( demux_t * );
129 static void ButtonUpdate( demux_t * );
130
131 /*****************************************************************************
132  * DemuxOpen:
133  *****************************************************************************/
134 static int Open( vlc_object_t *p_this )
135 {
136     demux_t     *p_demux = (demux_t*)p_this;
137     demux_sys_t *p_sys;
138     dvdnav_t    *p_dvdnav;
139     int         i_title, i_chapter, i_angle;
140     char        *psz_name;
141
142     psz_name = ParseCL( VLC_OBJECT(p_demux), p_demux->psz_path, VLC_TRUE,
143                         &i_title, &i_chapter, &i_angle );
144     if( !psz_name )
145     {
146         return VLC_EGENERIC;
147     }
148
149     /* Open dvdnav */
150     if( dvdnav_open( &p_dvdnav, psz_name ) != DVDNAV_STATUS_OK )
151     {
152         msg_Warn( p_demux, "cannot open dvdnav" );
153         free( psz_name );
154         return VLC_EGENERIC;
155     }
156     free( psz_name );
157
158     /* Fill p_demux field */
159     p_demux->pf_demux = Demux;
160     p_demux->pf_control = Control;
161     p_demux->p_sys = p_sys = malloc( sizeof( demux_sys_t ) );
162     memset( p_sys, 0, sizeof( demux_sys_t ) );
163     p_sys->dvdnav = p_dvdnav;
164
165     p_sys->b_simple =
166         strcmp( p_demux->psz_access, "dvdnavsimple" ) ? VLC_FALSE : VLC_TRUE;
167     if( p_sys->b_simple && i_title < 1 )
168     {
169         /* Skip menu part */
170         i_title = 1;
171     }
172
173     ps_track_init( p_sys->tk );
174     p_sys->i_aspect = -1;
175     p_sys->b_es_out_ok = VLC_FALSE;
176
177     if( 1 )
178     {
179         // Hack for libdvdnav CVS.
180         // Without it dvdnav_get_number_of_titles() fails.
181         // Remove when fixed in libdvdnav CVS.
182         uint8_t buffer[DVD_VIDEO_LB_LEN];
183         int i_event, i_len;
184
185         if( dvdnav_get_next_block( p_sys->dvdnav, buffer, &i_event, &i_len )
186               == DVDNAV_STATUS_ERR )
187         {
188             msg_Warn( p_demux, "dvdnav_get_next_block failed" );
189         }
190
191         dvdnav_sector_search( p_sys->dvdnav, 0, SEEK_SET );
192     }
193
194     /* Configure dvdnav */
195     if( dvdnav_set_readahead_flag( p_sys->dvdnav, DVD_READ_CACHE ) !=
196           DVDNAV_STATUS_OK )
197     {
198         msg_Warn( p_demux, "cannot set read-a-head flag" );
199     }
200
201     if( dvdnav_set_PGC_positioning_flag( p_sys->dvdnav, 1 ) !=
202           DVDNAV_STATUS_OK )
203     {
204         msg_Warn( p_demux, "cannot set PGC positioning flag" );
205     }
206
207     if( dvdnav_menu_language_select (p_sys->dvdnav,"en") != DVDNAV_STATUS_OK ||
208         dvdnav_audio_language_select(p_sys->dvdnav,"en") != DVDNAV_STATUS_OK ||
209         dvdnav_spu_language_select  (p_sys->dvdnav,"en") != DVDNAV_STATUS_OK )
210     {
211         msg_Warn( p_demux, "something failed while setting en language (%s)",
212                   dvdnav_err_to_string( p_sys->dvdnav ) );
213     }
214
215     DemuxTitles( p_demux );
216
217     /* Set forced title/chapter */
218     if( i_title != 0 )
219     {
220         if( dvdnav_title_play( p_sys->dvdnav, i_title ) != DVDNAV_STATUS_OK )
221         {
222             msg_Warn( p_demux, "cannot set title" );
223             i_title = 0;
224         }
225         else
226         {
227             p_demux->info.i_update |= INPUT_UPDATE_TITLE;
228             p_demux->info.i_title = i_title;
229         }
230     }
231
232     if( i_chapter != 0 && i_title != 0 )
233     {
234         if( dvdnav_part_play( p_sys->dvdnav, i_title, i_chapter ) !=
235             DVDNAV_STATUS_OK )
236         {
237             msg_Warn( p_demux, "cannot set chapter" );
238             i_chapter = 0;
239         }
240         else
241         {
242             p_demux->info.i_update |= INPUT_UPDATE_SEEKPOINT;
243             p_demux->info.i_seekpoint = i_chapter;
244         }
245     }
246
247     /* Update default_pts to a suitable value for dvdnav access */
248     var_Create( p_demux, "dvdnav-caching", VLC_VAR_INTEGER|VLC_VAR_DOINHERIT );
249
250     /* For simple mode (no menus), we're done */
251     if( p_sys->b_simple ) return VLC_SUCCESS;
252
253     /* FIXME hack hack hack hack FIXME */
254     /* Get p_input and create variable */
255     p_sys->p_input = vlc_object_find( p_demux, VLC_OBJECT_INPUT, FIND_PARENT );
256     var_Create( p_sys->p_input, "x-start", VLC_VAR_INTEGER );
257     var_Create( p_sys->p_input, "y-start", VLC_VAR_INTEGER );
258     var_Create( p_sys->p_input, "x-end", VLC_VAR_INTEGER );
259     var_Create( p_sys->p_input, "y-end", VLC_VAR_INTEGER );
260     var_Create( p_sys->p_input, "color", VLC_VAR_ADDRESS );
261     var_Create( p_sys->p_input, "contrast", VLC_VAR_ADDRESS );
262     var_Create( p_sys->p_input, "highlight", VLC_VAR_BOOL );
263     var_Create( p_sys->p_input, "highlight-mutex", VLC_VAR_MUTEX );
264
265     /* Now create our event thread catcher */
266     p_sys->p_ev = vlc_object_create( p_demux, sizeof( event_thread_t ) );
267     p_sys->p_ev->p_demux = p_demux;
268     vlc_thread_create( p_sys->p_ev, "dvdnav event thread handler", EventThread,
269                        VLC_THREAD_PRIORITY_LOW, VLC_FALSE );
270
271     return VLC_SUCCESS;
272 }
273
274 /*****************************************************************************
275  * Close:
276  *****************************************************************************/
277 static void Close( vlc_object_t *p_this )
278 {
279     demux_t     *p_demux = (demux_t*)p_this;
280     demux_sys_t *p_sys = p_demux->p_sys;
281     int i;
282
283     if( !p_sys->b_simple )
284     {
285         /* stop the event handler */
286         p_sys->p_ev->b_die = VLC_TRUE;
287         vlc_thread_join( p_sys->p_ev );
288         vlc_object_destroy( p_sys->p_ev );
289
290         var_Destroy( p_sys->p_input, "highlight-mutex" );
291         var_Destroy( p_sys->p_input, "highlight" );
292         var_Destroy( p_sys->p_input, "x-start" );
293         var_Destroy( p_sys->p_input, "x-end" );
294         var_Destroy( p_sys->p_input, "y-start" );
295         var_Destroy( p_sys->p_input, "y-end" );
296         var_Destroy( p_sys->p_input, "color" );
297         var_Destroy( p_sys->p_input, "contrast" );
298
299         vlc_object_release( p_sys->p_input );
300     }
301
302     for( i = 0; i < PS_TK_COUNT; i++ )
303     {
304         ps_track_t *tk = &p_sys->tk[i];
305         if( tk->b_seen )
306         {
307             es_format_Clean( &tk->fmt );
308             if( tk->es ) es_out_Del( p_demux->out, tk->es );
309         }
310     }
311
312     dvdnav_close( p_sys->dvdnav );
313     free( p_sys );
314 }
315
316 /*****************************************************************************
317  * Control:
318  *****************************************************************************/
319 static int Control( demux_t *p_demux, int i_query, va_list args )
320 {
321     demux_sys_t *p_sys = p_demux->p_sys;
322     double f, *pf;
323     vlc_bool_t *pb;
324     int64_t *pi64;
325     input_title_t ***ppp_title;
326     int          *pi_int;
327     int i;
328
329     switch( i_query )
330     {
331         case DEMUX_GET_POSITION:
332         {
333             uint32_t pos, len;
334             pf = (double*) va_arg( args, double* );
335             if( dvdnav_get_position( p_sys->dvdnav, &pos, &len ) ==
336                   DVDNAV_STATUS_OK && len > 0 )
337             {
338                 *pf = (double)pos / (double)len;
339             }
340             else
341             {
342                 *pf = 0.0;
343             }
344             return VLC_SUCCESS;
345         }
346         case DEMUX_SET_POSITION:
347         {
348             uint32_t pos, len;
349             f = (double)va_arg( args, double );
350             if( dvdnav_get_position( p_sys->dvdnav, &pos, &len ) ==
351                   DVDNAV_STATUS_OK && len > 0 )
352             {
353                 pos = f * len;
354                 if( dvdnav_sector_search( p_sys->dvdnav, pos, SEEK_SET ) ==
355                       DVDNAV_STATUS_OK )
356                 {
357                     return VLC_SUCCESS;
358                 }
359             }
360
361             return VLC_EGENERIC;
362         }
363
364         /* Special for access_demux */
365         case DEMUX_CAN_PAUSE:
366         case DEMUX_CAN_CONTROL_PACE:
367             /* TODO */
368             pb = (vlc_bool_t*)va_arg( args, vlc_bool_t * );
369             *pb = VLC_TRUE;
370             return VLC_SUCCESS;
371
372         case DEMUX_SET_PAUSE_STATE:
373             return VLC_SUCCESS;
374
375         case DEMUX_GET_TITLE_INFO:
376             ppp_title = (input_title_t***)va_arg( args, input_title_t*** );
377             pi_int    = (int*)va_arg( args, int* );
378
379             /* Duplicate title infos */
380             *pi_int = p_sys->i_title;
381             *ppp_title = malloc( sizeof( input_title_t ** ) * p_sys->i_title );
382             for( i = 0; i < p_sys->i_title; i++ )
383             {
384                 (*ppp_title)[i] = vlc_input_title_Duplicate( p_sys->title[i] );
385             }
386             return VLC_SUCCESS;
387
388         case DEMUX_SET_TITLE:
389             i = (int)va_arg( args, int );
390             if( ( i == 0 && dvdnav_menu_call( p_sys->dvdnav, DVD_MENU_Root )
391                   != DVDNAV_STATUS_OK ) ||
392                 ( i != 0 && dvdnav_title_play( p_sys->dvdnav, i )
393                   != DVDNAV_STATUS_OK ) )
394             {
395                 msg_Warn( p_demux, "cannot set title/chapter" );
396                 return VLC_EGENERIC;
397             }
398             p_demux->info.i_update |= INPUT_UPDATE_TITLE|INPUT_UPDATE_SEEKPOINT;
399             p_demux->info.i_title = i;
400             p_demux->info.i_seekpoint = 0;
401             return VLC_SUCCESS;
402
403         case DEMUX_SET_SEEKPOINT:
404             i = (int)va_arg( args, int );
405             if( p_demux->info.i_title == 0 )
406             {
407                 int i_ret;
408                 /* Special case */
409                 switch( i )
410                 {
411                 case 0:
412                     i_ret = dvdnav_menu_call( p_sys->dvdnav, DVD_MENU_Escape );
413                     break;
414                 case 1:
415                     i_ret = dvdnav_menu_call( p_sys->dvdnav, DVD_MENU_Root );
416                     break;
417                 case 2:
418                     i_ret = dvdnav_menu_call( p_sys->dvdnav, DVD_MENU_Title );
419                     break;
420                 case 3:
421                     i_ret = dvdnav_menu_call( p_sys->dvdnav, DVD_MENU_Part );
422                     break;
423                 case 4:
424                     i_ret = dvdnav_menu_call( p_sys->dvdnav,
425                                               DVD_MENU_Subpicture );
426                     break;
427                 case 5:
428                     i_ret = dvdnav_menu_call( p_sys->dvdnav, DVD_MENU_Audio );
429                     break;
430                 case 6:
431                     i_ret = dvdnav_menu_call( p_sys->dvdnav, DVD_MENU_Angle );
432                     break;
433                 default:
434                     return VLC_EGENERIC;
435                 }
436
437                 if( i_ret != DVDNAV_STATUS_OK )
438                     return VLC_EGENERIC;
439             }
440             else if( dvdnav_part_play( p_sys->dvdnav, p_demux->info.i_title,
441                                        i + 1 ) != DVDNAV_STATUS_OK )
442             {
443                 msg_Warn( p_demux, "cannot set title/chapter" );
444                 return VLC_EGENERIC;
445             }
446             p_demux->info.i_update |= INPUT_UPDATE_SEEKPOINT;
447             p_demux->info.i_seekpoint = i;
448             return VLC_SUCCESS;
449
450         case DEMUX_GET_PTS_DELAY:
451             pi64 = (int64_t*)va_arg( args, int64_t * );
452             *pi64 = (int64_t)var_GetInteger( p_demux, "dvdnav-caching" ) *1000;
453             return VLC_SUCCESS;
454
455         /* TODO implement others */
456         default:
457             return VLC_EGENERIC;
458     }
459 }
460
461 /*****************************************************************************
462  * Demux:
463  *****************************************************************************/
464 static int Demux( demux_t *p_demux )
465 {
466     demux_sys_t *p_sys = p_demux->p_sys;
467
468     uint8_t buffer[DVD_VIDEO_LB_LEN];
469     uint8_t *packet = buffer;
470     int i_event;
471     int i_len;
472
473     if( !p_sys->b_es_out_ok )
474     {
475         if( !p_sys->b_simple )
476         {
477             /* We do ourself the selection/unselection
478              * Problem: bypass --audio-channel and --spu-channel
479              * Solution: call ourself dvdnav_??set_channel -> TODO
480              */
481             es_out_Control( p_demux->out, ES_OUT_SET_MODE, ES_OUT_MODE_NONE );
482         }
483
484         p_sys->b_es_out_ok = VLC_TRUE;
485     }
486
487 #if DVD_READ_CACHE
488     if( dvdnav_get_next_cache_block( p_sys->dvdnav, &packet, &i_event, &i_len )
489         == DVDNAV_STATUS_ERR )
490 #else
491     if( dvdnav_get_next_block( p_sys->dvdnav, packet, &i_event, &i_len )
492         == DVDNAV_STATUS_ERR )
493 #endif
494     {
495         msg_Warn( p_demux, "cannot get next block (%s)",
496                   dvdnav_err_to_string( p_sys->dvdnav ) );
497         return -1;
498     }
499
500     switch( i_event )
501     {
502     case DVDNAV_BLOCK_OK:   /* mpeg block */
503         DemuxBlock( p_demux, packet, i_len );
504         break;
505
506     case DVDNAV_NOP:    /* Nothing */
507         msg_Dbg( p_demux, "DVDNAV_NOP" );
508         break;
509
510     case DVDNAV_STILL_FRAME:
511     {
512         dvdnav_still_event_t *event = (dvdnav_still_event_t*)packet;
513         if( !p_sys->b_simple )
514         {
515             vlc_mutex_lock( &p_sys->p_ev->lock );
516             if( !p_sys->p_ev->b_still )
517             {
518                 msg_Dbg( p_demux, "DVDNAV_STILL_FRAME" );
519                 msg_Dbg( p_demux, "     - length=0x%x", event->length );
520                 p_sys->p_ev->b_still = VLC_TRUE;
521                 if( event->length == 0xff )
522                 {
523                     p_sys->p_ev->i_still_end = 0;
524                 }
525                 else
526                 {
527                     p_sys->p_ev->i_still_end = (int64_t)event->length *
528                         1000000 + mdate() + p_sys->p_input->i_pts_delay;
529                 }
530             }
531             vlc_mutex_unlock( &p_sys->p_ev->lock );
532             msleep( 40000 );
533         }
534         else
535         {
536             dvdnav_still_skip( p_sys->dvdnav );
537         }
538         break;
539     }
540     case DVDNAV_SPU_STREAM_CHANGE:
541     {
542         dvdnav_spu_stream_change_event_t *event =
543             (dvdnav_spu_stream_change_event_t*)packet;
544         msg_Dbg( p_demux, "DVDNAV_SPU_STREAM_CHANGE" );
545         msg_Dbg( p_demux, "     - physical_wide=%d",
546                  event->physical_wide );
547         msg_Dbg( p_demux, "     - physical_letterbox=%d",
548                  event->physical_letterbox);
549         msg_Dbg( p_demux, "     - physical_pan_scan=%d",
550                  event->physical_pan_scan );
551
552         if( !p_sys->b_simple )
553         {
554             ESSubtitleUpdate( p_demux );
555         }
556         break;
557     }
558     case DVDNAV_AUDIO_STREAM_CHANGE:
559     {
560         dvdnav_audio_stream_change_event_t *event =
561             (dvdnav_audio_stream_change_event_t*)packet;
562         msg_Dbg( p_demux, "DVDNAV_AUDIO_STREAM_CHANGE" );
563         msg_Dbg( p_demux, "     - physical=%d", event->physical );
564         /* TODO */
565         break;
566     }
567     case DVDNAV_VTS_CHANGE:
568     {
569         int32_t i_title = 0;
570         int32_t i_part  = 0;
571         int i;
572
573         dvdnav_vts_change_event_t *event = (dvdnav_vts_change_event_t*)packet;
574         msg_Dbg( p_demux, "DVDNAV_VTS_CHANGE" );
575         msg_Dbg( p_demux, "     - vtsN=%d", event->new_vtsN );
576         msg_Dbg( p_demux, "     - domain=%d", event->new_domain );
577
578         /* dvdnav_get_video_aspect / dvdnav_get_video_scale_permission */
579         /* TODO check if we always have VTS and CELL */
580         p_sys->i_aspect = dvdnav_get_video_aspect( p_sys->dvdnav );
581
582         /* reset PCR */
583         es_out_Control( p_demux->out, ES_OUT_RESET_PCR );
584
585         for( i = 0; i < PS_TK_COUNT; i++ )
586         {
587             ps_track_t *tk = &p_sys->tk[i];
588             if( tk->b_seen )
589             {
590                 es_format_Clean( &tk->fmt );
591                 if( tk->es ) es_out_Del( p_demux->out, tk->es );
592             }
593             tk->b_seen = VLC_FALSE;
594         }
595
596         if( dvdnav_current_title_info( p_sys->dvdnav, &i_title,
597                                        &i_part ) == DVDNAV_STATUS_OK )
598         {
599             if( p_sys->b_simple && i_title == 0 )
600             {
601                 /* we have returned in menu, stop dvd */
602                 /* FIXME is it the right way ? */
603                 return 0;
604             }
605             if( i_title >= 0 && i_title < p_sys->i_title &&
606                 p_demux->info.i_title != i_title )
607             {
608                 p_demux->info.i_update |= INPUT_UPDATE_TITLE;
609                 p_demux->info.i_title = i_title;
610             }
611         }
612         break;
613     }
614     case DVDNAV_CELL_CHANGE:
615     {
616         int32_t i_title = 0;
617         int32_t i_part  = 0;
618
619         dvdnav_cell_change_event_t *event =
620             (dvdnav_cell_change_event_t*)packet;
621         msg_Dbg( p_demux, "DVDNAV_CELL_CHANGE" );
622         msg_Dbg( p_demux, "     - cellN=%d", event->cellN );
623         msg_Dbg( p_demux, "     - pgN=%d", event->pgN );
624         msg_Dbg( p_demux, "     - cell_length=%lld", event->cell_length );
625         msg_Dbg( p_demux, "     - pg_length=%lld", event->pg_length );
626         msg_Dbg( p_demux, "     - pgc_length=%lld", event->pgc_length );
627         msg_Dbg( p_demux, "     - cell_start=%lld", event->cell_start );
628         msg_Dbg( p_demux, "     - pg_start=%lld", event->pg_start );
629
630         /* FIXME is it correct or there is better way to know chapter change */
631         if( dvdnav_current_title_info( p_sys->dvdnav, &i_title,
632                                        &i_part ) == DVDNAV_STATUS_OK )
633         {
634             if( i_title >= 0 && i_title < p_sys->i_title &&
635                 i_part >= 1 && i_part <= p_sys->title[i_title]->i_seekpoint &&
636                 p_demux->info.i_seekpoint != i_part - 1 )
637             {
638                 p_demux->info.i_update |= INPUT_UPDATE_SEEKPOINT;
639                 p_demux->info.i_seekpoint = i_part - 1;
640             }
641         }
642         break;
643     }
644
645     case DVDNAV_NAV_PACKET:
646     {
647 #ifdef DVDNAV_DEBUG
648         msg_Dbg( p_demux, "DVDNAV_NAV_PACKET" );
649 #endif
650         /* A lot of thing to do here :
651          *  - handle packet
652          *  - fetch pts (for time display)
653          *  - ...
654          */
655         DemuxBlock( p_demux, packet, i_len );
656         break;
657     }
658     case DVDNAV_STOP:   /* EOF */
659         msg_Dbg( p_demux, "DVDNAV_STOP" );
660         return 0;
661
662     case DVDNAV_HIGHLIGHT:
663     {
664         dvdnav_highlight_event_t *event = (dvdnav_highlight_event_t*)packet;
665         msg_Dbg( p_demux, "DVDNAV_HIGHLIGHT" );
666         msg_Dbg( p_demux, "     - display=%d", event->display );
667         msg_Dbg( p_demux, "     - buttonN=%d", event->buttonN );
668         if( !p_sys->b_simple )
669         {
670             ButtonUpdate( p_demux );
671         }
672         break;
673     }
674
675     case DVDNAV_SPU_CLUT_CHANGE:
676         msg_Dbg( p_demux, "DVDNAV_SPU_CLUT_CHANGE" );
677         /* Update color lookup table (16 *uint32_t in packet) */
678         memcpy( p_sys->clut, packet, 16 * sizeof( uint32_t ) );
679         break;
680
681     case DVDNAV_HOP_CHANNEL:
682         msg_Dbg( p_demux, "DVDNAV_HOP_CHANNEL" );
683         /* We should try to flush all our internal buffer */
684         break;
685
686     case DVDNAV_WAIT:
687         msg_Dbg( p_demux, "DVDNAV_WAIT" );
688
689         dvdnav_wait_skip( p_sys->dvdnav );
690         break;
691
692     default:
693         msg_Warn( p_demux, "Unknown event (0x%x)", i_event );
694         break;
695     }
696
697 #if DVD_READ_CACHE
698     dvdnav_free_cache_block( p_sys->dvdnav, packet );
699 #endif
700
701     return 1;
702 }
703
704 /*****************************************************************************
705  * ParseCL: parse command line
706  *****************************************************************************/
707 static char *ParseCL( vlc_object_t *p_this, char *psz_name, vlc_bool_t b_force,
708                       int *i_title, int *i_chapter, int *i_angle )
709 {
710     char *psz_parser, *psz_source, *psz_next;
711
712     psz_source = strdup( psz_name );
713     if( psz_source == NULL ) return NULL;
714
715     *i_title = 0;
716     *i_chapter = 0;
717     *i_angle = 1;
718
719     /* Start with the end, because you could have :
720      * dvdnav:/Volumes/my@toto/VIDEO_TS@1,1
721      * (yes, this is kludgy). */
722     for( psz_parser = psz_source + strlen(psz_source) - 1;
723          psz_parser >= psz_source && *psz_parser != '@';
724          psz_parser-- );
725
726     if( psz_parser >= psz_source && *psz_parser == '@' )
727     {
728         /* Found options */
729         *psz_parser = '\0';
730         ++psz_parser;
731
732         *i_title = (int)strtol( psz_parser, &psz_next, 10 );
733         if( *psz_next )
734         {
735             psz_parser = psz_next + 1;
736             *i_chapter = (int)strtol( psz_parser, &psz_next, 10 );
737             if( *psz_next )
738             {
739                 *i_angle = (int)strtol( psz_next + 1, NULL, 10 );
740             }
741         }
742     }
743
744     *i_title   = *i_title >= 0 ? *i_title : 0;
745     *i_chapter = *i_chapter    ? *i_chapter : 0;
746     *i_angle   = *i_angle      ? *i_angle : 1;
747
748     if( !*psz_source )
749     {
750         free( psz_source );
751         if( !b_force )
752         {
753             return NULL;
754         }
755         psz_source = config_GetPsz( p_this, "dvd" );
756         if( !psz_source ) return NULL;
757     }
758
759 #ifdef WIN32
760     if( psz_source[0] && psz_source[1] == ':' &&
761         psz_source[2] == '\\' && psz_source[3] == '\0' )
762     {
763         psz_source[2] = '\0';
764     }
765 #endif
766
767     msg_Dbg( p_this, "dvdroot=%s title=%d chapter=%d angle=%d",
768              psz_source, *i_title, *i_chapter, *i_angle );
769
770     return psz_source;
771 }
772
773 static void DemuxTitles( demux_t *p_demux )
774 {
775     demux_sys_t *p_sys = p_demux->p_sys;
776     input_title_t *t;
777     seekpoint_t *s;
778     int32_t i_titles;
779     int i;
780
781     /* Menu */
782     t = vlc_input_title_New();
783     t->b_menu = VLC_TRUE;
784     t->psz_name = strdup( "DVD Menu" );
785
786     s = vlc_seekpoint_New();
787     s->psz_name = strdup( "Resume" );
788     TAB_APPEND( t->i_seekpoint, t->seekpoint, s );
789
790     s = vlc_seekpoint_New();
791     s->psz_name = strdup( "Root" );
792     TAB_APPEND( t->i_seekpoint, t->seekpoint, s );
793
794     s = vlc_seekpoint_New();
795     s->psz_name = strdup( "Title" );
796     TAB_APPEND( t->i_seekpoint, t->seekpoint, s );
797
798     s = vlc_seekpoint_New();
799     s->psz_name = strdup( "Chapter" );
800     TAB_APPEND( t->i_seekpoint, t->seekpoint, s );
801
802     s = vlc_seekpoint_New();
803     s->psz_name = strdup( "Subtitle" );
804     TAB_APPEND( t->i_seekpoint, t->seekpoint, s );
805
806     s = vlc_seekpoint_New();
807     s->psz_name = strdup( "Audio" );
808     TAB_APPEND( t->i_seekpoint, t->seekpoint, s );
809
810     s = vlc_seekpoint_New();
811     s->psz_name = strdup( "Angle" );
812     TAB_APPEND( t->i_seekpoint, t->seekpoint, s );
813
814     TAB_APPEND( p_sys->i_title, p_sys->title, t );
815
816     /* Find out number of titles/chapters */
817     dvdnav_get_number_of_titles( p_sys->dvdnav, &i_titles );
818     for( i = 1; i <= i_titles; i++ )
819     {
820         int32_t i_chapters = 0;
821         int j;
822
823         dvdnav_get_number_of_parts( p_sys->dvdnav, i, &i_chapters );
824
825         t = vlc_input_title_New();
826         for( j = 0; j < __MAX( i_chapters, 1 ); j++ )
827         {
828             s = vlc_seekpoint_New();
829             TAB_APPEND( t->i_seekpoint, t->seekpoint, s );
830         }
831
832         TAB_APPEND( p_sys->i_title, p_sys->title, t );
833     }
834 }
835
836 /*****************************************************************************
837  * Update functions:
838  *****************************************************************************/
839 static void ButtonUpdate( demux_t *p_demux )
840 {
841     demux_sys_t *p_sys = p_demux->p_sys;
842     vlc_value_t val;
843     int32_t i_title, i_part;
844
845     dvdnav_current_title_info( p_sys->dvdnav, &i_title, &i_part );
846
847     if( var_Get( p_sys->p_input, "highlight-mutex", &val ) == VLC_SUCCESS )
848     {
849         vlc_mutex_t *p_mutex = val.p_address;
850         dvdnav_highlight_area_t hl;
851         int32_t i_button;
852
853         if( dvdnav_get_current_highlight( p_sys->dvdnav, &i_button )
854             != DVDNAV_STATUS_OK )
855         {
856             msg_Err( p_demux, "dvdnav_get_current_highlight failed" );
857             return;
858         }
859
860         if( i_button > 0 && i_title ==  0 )
861         {
862             pci_t *pci = dvdnav_get_current_nav_pci( p_sys->dvdnav );
863
864             dvdnav_get_highlight_area( pci, i_button, 1, &hl );
865
866             /* I fear it is plain wrong */
867             //val.p_address = (void *)&hl.palette;
868             p_sys->alpha[0] = hl.palette&0x0f;
869             p_sys->alpha[1] = (hl.palette>>4)&0x0f;
870             p_sys->alpha[2] = (hl.palette>>8)&0x0f;
871             p_sys->alpha[3] = (hl.palette>>12)&0x0f;
872
873             vlc_mutex_lock( p_mutex );
874             val.i_int = hl.sx; var_Set( p_sys->p_input, "x-start", val );
875             val.i_int = hl.ex; var_Set( p_sys->p_input, "x-end", val );
876             val.i_int = hl.sy; var_Set( p_sys->p_input, "y-start", val );
877             val.i_int = hl.ey; var_Set( p_sys->p_input, "y-end", val );
878
879             val.p_address = (void *)p_sys->alpha;
880             var_Set( p_sys->p_input, "contrast", val );
881
882             val.b_bool = VLC_TRUE; var_Set( p_sys->p_input, "highlight", val );
883             vlc_mutex_unlock( p_mutex );
884
885             msg_Dbg( p_demux, "buttonUpdate %d", i_button );
886         }
887         else
888         {
889             msg_Dbg( p_demux, "buttonUpdate not done b=%d t=%d",
890                      i_button, i_title );
891
892             /* Show all */
893             vlc_mutex_lock( p_mutex );
894             val.b_bool = VLC_FALSE;
895             var_Set( p_sys->p_input, "highlight", val );
896             vlc_mutex_unlock( p_mutex );
897         }
898     }
899 }
900
901 static void ESNew( demux_t *p_demux, int i_id );
902
903 static void ESSubtitleUpdate( demux_t *p_demux )
904 {
905     demux_sys_t *p_sys = p_demux->p_sys;
906     int         i_spu = dvdnav_get_active_spu_stream( p_sys->dvdnav );
907     int32_t i_title, i_part;
908
909     ButtonUpdate( p_demux );
910
911     dvdnav_current_title_info( p_sys->dvdnav, &i_title, &i_part );
912     if( i_title > 0 )
913     {
914         return;
915     }
916
917     if( i_spu >= 0 && i_spu <= 0x1f )
918     {
919         ps_track_t *tk = &p_sys->tk[PS_ID_TO_TK(0xbd20 + i_spu)];
920
921         if( !tk->b_seen )
922         {
923             ESNew( p_demux, 0xbd20 + i_spu);
924         }
925         /* be sure to unselect it (reset) */
926         es_out_Control( p_demux->out, ES_OUT_SET_ES_STATE, tk->es,
927                         (vlc_bool_t)VLC_FALSE );
928
929         /* now select it */
930         es_out_Control( p_demux->out, ES_OUT_SET_ES, tk->es );
931     }
932     else
933     {
934         for( i_spu = 0; i_spu <= 0x1F; i_spu++ )
935         {
936             ps_track_t *tk = &p_sys->tk[PS_ID_TO_TK(0xbd20 + i_spu)];
937             if( tk->b_seen )
938             {
939                 es_out_Control( p_demux->out, ES_OUT_SET_ES_STATE, tk->es,
940                                 (vlc_bool_t)VLC_FALSE );
941             }
942         }
943     }
944 }
945
946 /*****************************************************************************
947  * DemuxBlock: demux a given block
948  *****************************************************************************/
949 static int DemuxBlock( demux_t *p_demux, uint8_t *pkt, int i_pkt )
950 {
951     demux_sys_t *p_sys = p_demux->p_sys;
952     uint8_t     *p = pkt;
953
954     while( p < &pkt[i_pkt] )
955     {
956         int i_size = ps_pkt_size( p, &pkt[i_pkt] - p );
957         block_t *p_pkt;
958         if( i_size <= 0 )
959         {
960             break;
961         }
962
963         /* Create a block */
964         p_pkt = block_New( p_demux, i_size );
965         memcpy( p_pkt->p_buffer, p, i_size);
966
967         /* Parse it and send it */
968         switch( 0x100 | p[3] )
969         {
970         case 0x1b9:
971         case 0x1bb:
972         case 0x1bc:
973 #ifdef DVDNAV_DEBUG
974             if( p[3] == 0xbc )
975             {
976                 msg_Warn( p_demux, "received a PSM packet" );
977             }
978             else if( p[3] == 0xbb )
979             {
980                 msg_Warn( p_demux, "received a SYSTEM packet" );
981             }
982 #endif
983             block_Release( p_pkt );
984             break;
985
986         case 0x1ba:
987         {
988             int64_t i_scr;
989             int i_mux_rate;
990             if( !ps_pkt_parse_pack( p_pkt, &i_scr, &i_mux_rate ) )
991             {
992                 es_out_Control( p_demux->out, ES_OUT_SET_PCR, i_scr );
993             }
994             block_Release( p_pkt );
995             break;
996         }
997         default:
998         {
999             int i_id = ps_pkt_id( p_pkt );
1000             if( i_id >= 0xc0 )
1001             {
1002                 ps_track_t *tk = &p_sys->tk[PS_ID_TO_TK(i_id)];
1003
1004                 if( !tk->b_seen )
1005                 {
1006                     ESNew( p_demux, i_id );
1007                 }
1008                 if( tk->b_seen && tk->es &&
1009                     !ps_pkt_parse_pes( p_pkt, tk->i_skip ) )
1010                 {
1011                     es_out_Send( p_demux->out, tk->es, p_pkt );
1012                 }
1013                 else
1014                 {
1015                     block_Release( p_pkt );
1016                 }
1017             }
1018             else
1019             {
1020                 block_Release( p_pkt );
1021             }
1022             break;
1023         }
1024         }
1025
1026         p += i_size;
1027     }
1028
1029     return VLC_SUCCESS;
1030 }
1031
1032 /*****************************************************************************
1033  * ESNew: register a new elementary stream
1034  *****************************************************************************/
1035 static void ESNew( demux_t *p_demux, int i_id )
1036 {
1037     demux_sys_t *p_sys = p_demux->p_sys;
1038     ps_track_t  *tk = &p_sys->tk[PS_ID_TO_TK(i_id)];
1039     vlc_bool_t  b_select = VLC_FALSE;
1040
1041     if( tk->b_seen )
1042     {
1043         return;
1044     }
1045
1046     if( ps_track_fill( tk, i_id ) )
1047     {
1048         msg_Warn( p_demux, "unknown codec for id=0x%x", i_id );
1049         return;
1050     }
1051
1052     /* Add a new ES */
1053     if( tk->fmt.i_cat == VIDEO_ES )
1054     {
1055         if( p_sys->i_aspect >= 0 )
1056         {
1057             tk->fmt.video.i_aspect = p_sys->i_aspect;
1058         }
1059         b_select = VLC_TRUE;
1060     }
1061     else if( tk->fmt.i_cat == AUDIO_ES )
1062     {
1063         int i_audio = -1;
1064         /* find the audio number PLEASE find another way */
1065         if( (i_id&0xbdf8) == 0xbd88 )       /* dts */
1066         {
1067             i_audio = i_id&0x07;
1068         }
1069         else if( (i_id&0xbdf0) == 0xbd80 )  /* a52 */
1070         {
1071             i_audio = i_id&0xf;
1072         }
1073         else if( (i_id&0xbdf0) == 0xbda0 )  /* lpcm */
1074         {
1075             i_audio = i_id&0x1f;
1076         }
1077         else if( ( i_id&0xe0 ) == 0xc0 )    /* mpga */
1078         {
1079             i_audio = i_id&0x1f;
1080         }
1081         if( i_audio >= 0 )
1082         {
1083             int i_lang = dvdnav_audio_stream_to_lang( p_sys->dvdnav, i_audio );
1084             if( i_lang != 0xffff )
1085             {
1086                 tk->fmt.psz_language = malloc( 3 );
1087                 tk->fmt.psz_language[0] = (i_lang >> 8)&0xff;
1088                 tk->fmt.psz_language[1] = (i_lang     )&0xff;
1089                 tk->fmt.psz_language[2] = 0;
1090             }
1091             if( dvdnav_get_active_audio_stream( p_sys->dvdnav ) == i_audio )
1092             {
1093                 b_select = VLC_TRUE;
1094             }
1095         }
1096     }
1097     else if( tk->fmt.i_cat == SPU_ES )
1098     {
1099         int32_t i_title, i_part;
1100         int i_lang = dvdnav_spu_stream_to_lang( p_sys->dvdnav, i_id&0x1f );
1101         if( i_lang != 0xffff )
1102         {
1103             tk->fmt.psz_language = malloc( 3 );
1104             tk->fmt.psz_language[0] = (i_lang >> 8)&0xff;
1105             tk->fmt.psz_language[1] = (i_lang     )&0xff;
1106             tk->fmt.psz_language[2] = 0;
1107         }
1108
1109         /* Palette */
1110         tk->fmt.subs.spu.palette[0] = 0xBeef;
1111         memcpy( &tk->fmt.subs.spu.palette[1], p_sys->clut,
1112                 16 * sizeof( uint32_t ) );
1113
1114         /* We select only when we are not in the menu */
1115         dvdnav_current_title_info( p_sys->dvdnav, &i_title, &i_part );
1116         if( i_title > 0 &&
1117             dvdnav_get_active_spu_stream( p_sys->dvdnav ) == (i_id&0x1f) )
1118         {
1119             b_select = VLC_TRUE;
1120         }
1121     }
1122
1123     tk->es = es_out_Add( p_demux->out, &tk->fmt );
1124     if( b_select )
1125     {
1126         es_out_Control( p_demux->out, ES_OUT_SET_ES, tk->es );
1127     }
1128     tk->b_seen = VLC_TRUE;
1129 }
1130
1131 /*****************************************************************************
1132  * Event handler code
1133  *****************************************************************************/
1134 static int  EventMouse( vlc_object_t *, char const *,
1135                         vlc_value_t, vlc_value_t, void * );
1136 static int  EventKey  ( vlc_object_t *, char const *,
1137                         vlc_value_t, vlc_value_t, void * );
1138
1139 static int EventThread( vlc_object_t *p_this )
1140 {
1141     event_thread_t *p_ev = (event_thread_t*)p_this;
1142     demux_sys_t    *p_sys = p_ev->p_demux->p_sys;
1143     vlc_object_t   *p_vout = NULL;
1144
1145     vlc_mutex_init( p_ev, &p_ev->lock );
1146     p_ev->b_moved   = VLC_FALSE;
1147     p_ev->b_clicked = VLC_FALSE;
1148     p_ev->b_key     = VLC_FALSE;
1149
1150     p_ev->b_still   = VLC_FALSE;
1151
1152     /* catch all key event */
1153     var_AddCallback( p_ev->p_vlc, "key-pressed", EventKey, p_ev );
1154
1155     /* main loop */
1156     while( !p_ev->b_die )
1157     {
1158         vlc_bool_t b_activated = VLC_FALSE;
1159
1160         /* KEY part */
1161         if( p_ev->b_key )
1162         {
1163             pci_t *pci = dvdnav_get_current_nav_pci( p_sys->dvdnav );
1164
1165             vlc_value_t valk;
1166             struct hotkey *p_hotkeys = p_ev->p_vlc->p_hotkeys;
1167             int i, i_action = -1;
1168
1169             vlc_mutex_lock( &p_ev->lock );
1170             var_Get( p_ev->p_vlc, "key-pressed", &valk );
1171             for( i = 0; p_hotkeys[i].psz_action != NULL; i++ )
1172             {
1173                 if( p_hotkeys[i].i_key == valk.i_int )
1174                 {
1175                     i_action = p_hotkeys[i].i_action;
1176                 }
1177             }
1178
1179             switch( i_action )
1180             {
1181             case ACTIONID_NAV_LEFT:
1182                 dvdnav_left_button_select( p_sys->dvdnav, pci );
1183                 break;
1184             case ACTIONID_NAV_RIGHT:
1185                 dvdnav_right_button_select( p_sys->dvdnav, pci );
1186                 break;
1187             case ACTIONID_NAV_UP:
1188                 dvdnav_upper_button_select( p_sys->dvdnav, pci );
1189                 break;
1190             case ACTIONID_NAV_DOWN:
1191                 dvdnav_lower_button_select( p_sys->dvdnav, pci );
1192                 break;
1193             case ACTIONID_NAV_ACTIVATE:
1194                 b_activated = VLC_TRUE;
1195                 dvdnav_button_activate( p_sys->dvdnav, pci );
1196                 break;
1197             default:
1198                 break;
1199             }
1200             p_ev->b_key = VLC_FALSE;
1201             vlc_mutex_unlock( &p_ev->lock );
1202         }
1203
1204         /* VOUT part */
1205         if( p_vout && ( p_ev->b_moved || p_ev->b_clicked ) )
1206         {
1207             pci_t       *pci = dvdnav_get_current_nav_pci( p_sys->dvdnav );
1208             vlc_value_t valx, valy;
1209
1210             vlc_mutex_lock( &p_ev->lock );
1211             var_Get( p_vout, "mouse-x", &valx );
1212             var_Get( p_vout, "mouse-y", &valy );
1213
1214             if( p_ev->b_moved )
1215             {
1216                 dvdnav_mouse_select( p_sys->dvdnav, pci, valx.i_int,
1217                                      valy.i_int );
1218             }
1219             if( p_ev->b_clicked )
1220             {
1221                 b_activated = VLC_TRUE;
1222                 dvdnav_mouse_activate( p_sys->dvdnav, pci, valx.i_int,
1223                                        valy.i_int );
1224             }
1225
1226             p_ev->b_moved = VLC_FALSE;
1227             p_ev->b_clicked = VLC_FALSE;
1228             vlc_mutex_unlock( &p_ev->lock );
1229         }
1230         if( p_vout && p_vout->b_die )
1231         {
1232             var_DelCallback( p_vout, "mouse-moved", EventMouse, p_ev );
1233             var_DelCallback( p_vout, "mouse-clicked", EventMouse, p_ev );
1234             vlc_object_release( p_vout );
1235             p_vout = NULL;
1236         }
1237         if( p_vout == NULL )
1238         {
1239             p_vout = vlc_object_find( p_sys->p_input, VLC_OBJECT_VOUT,
1240                                       FIND_CHILD );
1241             if( p_vout)
1242             {
1243                 var_AddCallback( p_vout, "mouse-moved", EventMouse, p_ev );
1244                 var_AddCallback( p_vout, "mouse-clicked", EventMouse, p_ev );
1245             }
1246         }
1247
1248         /* Still part */
1249         vlc_mutex_lock( &p_ev->lock );
1250         if( p_ev->b_still )
1251         {
1252             if( /* b_activated || // This breaks menus */
1253                 ( p_ev->i_still_end > 0 && p_ev->i_still_end < mdate() ))
1254             {
1255                 p_ev->b_still = VLC_FALSE;
1256                 dvdnav_still_skip( p_sys->dvdnav );
1257             }
1258         }
1259         vlc_mutex_unlock( &p_ev->lock );
1260
1261         /* Wait a bit */
1262         msleep( 10000 );
1263     }
1264
1265     /* Release callback */
1266     if( p_vout )
1267     {
1268         var_DelCallback( p_vout, "mouse-moved", EventMouse, p_ev );
1269         var_DelCallback( p_vout, "mouse-clicked", EventMouse, p_ev );
1270         vlc_object_release( p_vout );
1271     }
1272     var_DelCallback( p_ev->p_vlc, "key-pressed", EventKey, p_ev );
1273
1274     vlc_mutex_destroy( &p_ev->lock );
1275
1276     return VLC_SUCCESS;
1277 }
1278
1279 static int EventMouse( vlc_object_t *p_this, char const *psz_var,
1280                        vlc_value_t oldval, vlc_value_t newval, void *p_data )
1281 {
1282     event_thread_t *p_ev = p_data;
1283     vlc_mutex_lock( &p_ev->lock );
1284     if( psz_var[6] == 'c' )
1285         p_ev->b_clicked = VLC_TRUE;
1286     else if( psz_var[6] == 'm' )
1287         p_ev->b_moved = VLC_TRUE;
1288     vlc_mutex_unlock( &p_ev->lock );
1289
1290     return VLC_SUCCESS;
1291 }
1292
1293 static int EventKey( vlc_object_t *p_this, char const *psz_var,
1294                      vlc_value_t oldval, vlc_value_t newval, void *p_data )
1295 {
1296     event_thread_t *p_ev = p_data;
1297     vlc_mutex_lock( &p_ev->lock );
1298     p_ev->b_key = VLC_TRUE;
1299     vlc_mutex_unlock( &p_ev->lock );
1300
1301     return VLC_SUCCESS;
1302 }