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