]> git.sesse.net Git - vlc/blob - modules/access/vcdx/access.c
Need to change libvcdinfo to be more robust when there are
[vlc] / modules / access / vcdx / access.c
1 /*****************************************************************************
2  * vcd.c : VCD input module for vlc
3  *         using libcdio, libvcd and libvcdinfo. vlc-specific things tend
4  *         to go here.
5  *****************************************************************************
6  * Copyright (C) 2000 VideoLAN
7  * $Id: access.c,v 1.2 2003/11/07 10:31:38 rocky Exp $
8  *
9  * Authors: Johan Bilien <jobi@via.ecp.fr>
10  *          Rocky Bernstein <rocky@panix.com> 
11  *
12  * This program is free software; you can redistribute it and/or modify
13  * it under the terms of the GNU General Public License as published by
14  * the Free Software Foundation; either version 2 of the License, or
15  * (at your option) any later version.
16  *
17  * This program is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20  * GNU General Public License for more details.
21  *
22  * You should have received a copy of the GNU General Public License
23  * along with this program; if not, write to the Free Software
24  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
25  *****************************************************************************/
26
27 /*****************************************************************************
28  * Preamble
29  *****************************************************************************/
30
31 #if 0 // Disabled until this is working
32 #include <vlc/vlc.h>
33 #include <vlc/input.h>
34
35 #include "../../demux/mpeg/system.h"
36 #include "vcd.h"
37 #include "intf.h"
38 #include "vcdplayer.h"
39
40 #include <cdio/cdio.h>
41 #include <cdio/logging.h>
42 #include <cdio/util.h>
43 #include <libvcd/info.h>
44 #include <libvcd/logging.h>
45
46 #include "cdrom.h"
47
48 /* how many blocks VCDRead will read in each loop */
49 #define VCD_BLOCKS_ONCE 20
50 #define VCD_DATA_ONCE   (VCD_BLOCKS_ONCE * M2F2_SECTOR_SIZE)
51
52 /*****************************************************************************
53  * Local prototypes
54  *****************************************************************************/
55
56 /* First those which are accessed from outside (via pointers). */
57 static int  VCDOpen         ( vlc_object_t * );
58 static void VCDClose        ( vlc_object_t * );
59 static int  VCDRead         ( input_thread_t *, byte_t *, size_t );
60 static int  VCDRead         ( input_thread_t *, byte_t *, size_t );
61 static int  VCDSetProgram   ( input_thread_t *, pgrm_descriptor_t * );
62
63 /* Now those which are strictly internal */
64 static void VCDSetOrigin    ( input_thread_t *, lsn_t origin_lsn, 
65                               lsn_t cur_lsn, lsn_t end_lsn, 
66                               int cur_entry, track_t track );
67 static int  VCDEntryPoints  ( input_thread_t * );
68 static int  VCDLIDs         ( input_thread_t * );
69 static int  VCDSegments     ( input_thread_t * );
70 static void VCDTracks       ( input_thread_t * );
71 static int  VCDReadSector   ( vlc_object_t *p_this, 
72                               const vcdinfo_obj_t *p_vcd, lsn_t cur_lsn, 
73                               byte_t * p_buffer );
74 static char *VCDParse       ( input_thread_t *, 
75                               /*out*/ vcdinfo_itemid_t * p_itemid );
76
77 static void VCDUpdateVar( input_thread_t *p_input, int i_entry, int i_action,
78                           const char *varname, const char *label );
79
80 static vcdinfo_obj_t *vcd_Open   ( vlc_object_t *p_this, const char *psz_dev );
81
82 static int debug_callback   ( vlc_object_t *p_this, const char *psz_name,
83                                vlc_value_t oldval, vlc_value_t val, 
84                                void *p_data );
85
86 #define DEBUG_TEXT N_("set debug mask for additional debugging.")
87 #define DEBUG_LONGTEXT N_( \
88     "This integer when viewed in binary is a debugging mask\n" \
89     "MRL             1\n" \
90     "external call   2\n" \
91     "all calls       4\n" \
92     "LSN             8\n" \
93     "PBC      (10)  16\n" \
94     "libcdio  (20)  32\n" \
95     "seeks    (40)  64\n" \
96     "still    (80) 128\n" \
97     "vcdinfo (100) 256\n" )
98
99 /****************************************************************************
100  * Private functions
101  ****************************************************************************/
102 /* FIXME: This variable is a hack. Would be nice to eliminate the 
103    global-ness. */
104 static input_thread_t *p_vcd_input = NULL;
105
106 int
107 vcd_debug_callback   ( vlc_object_t *p_this, const char *psz_name,
108                        vlc_value_t oldval, vlc_value_t val, void *p_data )
109 {
110   thread_vcd_data_t *p_vcd;
111
112   if (NULL == p_vcd_input) return VLC_EGENERIC;
113   
114   p_vcd = (thread_vcd_data_t *)p_vcd_input->p_access_data;
115
116   if (p_vcd->i_debug & (INPUT_DBG_CALL|INPUT_DBG_EXT)) {
117     msg_Dbg( p_vcd_input, "Old debug (x%0x) %d, new debug (x%0x) %d", 
118              p_vcd->i_debug, p_vcd->i_debug, val.i_int, val.i_int);
119   }
120   p_vcd->i_debug = val.i_int;
121   return VLC_SUCCESS;
122 }
123
124 /* process messages that originate from libcdio. */
125 static void
126 cdio_log_handler (cdio_log_level_t level, const char message[])
127 {
128   thread_vcd_data_t *p_vcd = (thread_vcd_data_t *)p_vcd_input->p_access_data;
129   switch (level) {
130   case CDIO_LOG_DEBUG:
131   case CDIO_LOG_INFO:
132     if (p_vcd->i_debug & INPUT_DBG_CDIO) 
133       msg_Dbg( p_vcd_input, message);
134     break;
135   case CDIO_LOG_WARN:
136     msg_Warn( p_vcd_input, message);
137     break;
138   case CDIO_LOG_ERROR:
139   case CDIO_LOG_ASSERT:
140     msg_Err( p_vcd_input, message);
141     break;
142   default:
143     msg_Warn( p_vcd_input, message,
144             _("The above message had unknown vcdimager log level"), 
145             level);
146   }
147   return;
148 }
149
150 /* process messages that originate from vcdinfo. */
151 static void
152 vcd_log_handler (vcd_log_level_t level, const char message[])
153 {
154   thread_vcd_data_t *p_vcd = (thread_vcd_data_t *)p_vcd_input->p_access_data;
155   switch (level) {
156   case VCD_LOG_DEBUG:
157   case VCD_LOG_INFO:
158     if (p_vcd->i_debug & INPUT_DBG_VCDINFO)
159       msg_Dbg( p_vcd_input, message);
160     break;
161   case VCD_LOG_WARN:
162     msg_Warn( p_vcd_input, message);
163     break;
164   case VCD_LOG_ERROR:
165   case VCD_LOG_ASSERT:
166     msg_Err( p_vcd_input, message);
167     break;
168   default:
169     msg_Warn( p_vcd_input, "%s\n%s %d", message,
170             _("The above message had unknown vcdimager log level"), 
171             level);
172   }
173   return;
174 }
175
176 /*
177  * Data reading functions
178  */
179
180 /*****************************************************************************
181   VCDOpen: open VCD.
182   read in meta-information about VCD: the number of tracks, segments, 
183   entries, size and starting information. Then set up state variables so
184   that we read/seek starting at the location specified.
185
186   On success we return VLC_SUCCESS, on memory exhausted VLC_ENOMEM, 
187   and VLC_EGENERIC for some other error.
188  *****************************************************************************/
189 static int 
190 VCDOpen( vlc_object_t *p_this )
191 {
192     input_thread_t *        p_input = (input_thread_t *)p_this;
193     thread_vcd_data_t *     p_vcd;
194     char *                  psz_source;
195     vcdinfo_itemid_t        itemid;
196     bool                    play_ok;
197     
198     p_input->pf_read        = VCDRead;
199     p_input->pf_seek        = VCDSeek;
200     p_input->pf_set_area    = VCDSetArea;
201     p_input->pf_set_program = VCDSetProgram;
202
203     p_vcd = malloc( sizeof(thread_vcd_data_t) );
204
205     if( p_vcd == NULL )
206     {
207         LOG_ERR ("out of memory" );
208         return VLC_ENOMEM;
209     }
210
211     p_input->p_access_data = (void *)p_vcd;
212     p_vcd->i_debug         = config_GetInt( p_this, MODULE_STRING "-debug" );
213     psz_source             = VCDParse( p_input, &itemid );
214
215     if ( NULL == psz_source ) 
216     {
217       free( p_vcd );
218       return( VLC_EGENERIC );
219     }
220
221     dbg_print( (INPUT_DBG_CALL|INPUT_DBG_EXT), "%s", psz_source );
222
223     p_vcd->p_segments = NULL;
224     p_vcd->p_entries  = NULL;
225     
226     /* set up input  */
227     p_input->i_mtu = VCD_DATA_ONCE;
228
229     vlc_mutex_lock( &p_input->stream.stream_lock );
230
231     /* If we are here we can control the pace... */
232     p_input->stream.b_pace_control = 1;
233
234     p_input->stream.b_seekable = 1;
235     p_input->stream.p_selected_area->i_size = 0;
236     p_input->stream.p_selected_area->i_tell = 0;
237
238     vlc_mutex_unlock( &p_input->stream.stream_lock );
239
240     if( !(p_vcd->vcd = vcd_Open( p_this, psz_source )) )
241     {
242         msg_Warn( p_input, "could not open %s", psz_source );
243         free( psz_source );
244         free( p_vcd );
245         return VLC_EGENERIC;
246     }
247
248     /* Get track information. */
249     p_vcd->num_tracks = ioctl_GetTracksMap( VLC_OBJECT(p_input),
250                                             vcdinfo_get_cd_image(p_vcd->vcd), 
251                                             &p_vcd->p_sectors );
252     free( psz_source );
253     if( p_vcd->num_tracks < 0 )
254         LOG_ERR ("unable to count tracks" );
255     else if( p_vcd->num_tracks <= 1 )
256         LOG_ERR ("no movie tracks found" );
257     if( p_vcd->num_tracks <= 1)
258     {
259         vcdinfo_close( p_vcd->vcd );
260         free( p_vcd );
261         return VLC_EGENERIC;
262     }
263
264     /* Set stream and area data */
265     vlc_mutex_lock( &p_input->stream.stream_lock );
266
267     /* Initialize ES structures */
268     input_InitStream( p_input, sizeof( stream_ps_data_t ) );
269
270     /* disc input method */
271     p_input->stream.i_method = INPUT_METHOD_VCD;
272
273     p_input->stream.i_area_nb = 1;
274     
275
276     /* Initialize segment information. */
277     VCDSegments( p_input );
278     
279     /* Initialize track area information. */
280     VCDTracks( p_input );
281     
282     if( VCDEntryPoints( p_input ) < 0 )
283     {
284         msg_Warn( p_input, "could not read entry points, will not use them" );
285         p_vcd->b_valid_ep = false;
286     }
287
288     if( VCDLIDs( p_input ) < 0 )
289     {
290         msg_Warn( p_input, "could not read entry LIDs" );
291     }
292
293     play_ok = (VLC_SUCCESS == VCDPlay( p_input, itemid ));
294     
295     vlc_mutex_unlock( &p_input->stream.stream_lock );
296
297     if ( ! play_ok ) {
298       vcdinfo_close( p_vcd->vcd );
299       free( p_vcd );
300       return VLC_EGENERIC;
301     }
302
303     if( !p_input->psz_demux || !*p_input->psz_demux )
304     {
305 #if FIXED
306       p_input->psz_demux = "vcdx";
307 #else
308       p_input->psz_demux = "ps";
309 #endif
310     }
311
312     return VLC_SUCCESS;
313 }
314
315 /*****************************************************************************
316  * VCDClose: closes VCD releasing allocated memory.
317  *****************************************************************************/
318 static void 
319 VCDClose( vlc_object_t *p_this )
320 {
321     input_thread_t *   p_input = (input_thread_t *)p_this;
322     thread_vcd_data_t *p_vcd = (thread_vcd_data_t *)p_input->p_access_data;
323
324     dbg_print( (INPUT_DBG_CALL|INPUT_DBG_EXT), "VCDClose" );
325     vcdinfo_close( p_vcd->vcd );
326
327     free( p_vcd->p_entries );
328     free( p_vcd->p_segments );
329     free( p_vcd );
330     p_vcd_input = NULL;
331 }
332
333 /*****************************************************************************
334  * VCDRead: reads i_len bytes from the VCD into p_buffer.
335  *****************************************************************************
336  * Returns -1 in case of error, 0 in case of EOF, otherwise the number of
337  * bytes.
338  *****************************************************************************/
339 static int 
340 VCDRead( input_thread_t * p_input, byte_t * p_buffer, size_t i_len )
341 {
342     thread_vcd_data_t *     p_vcd= (thread_vcd_data_t *)p_input->p_access_data;
343     int                     i_blocks;
344     int                     i_index;
345     int                     i_read;
346     byte_t                  p_last_sector[ M2F2_SECTOR_SIZE ];
347
348     i_read = 0;
349
350     /* Compute the number of blocks we have to read */
351
352     i_blocks = i_len / M2F2_SECTOR_SIZE;
353
354     for ( i_index = 0 ; i_index < i_blocks ; i_index++ )
355     {
356
357       if ( p_vcd->cur_lsn == p_vcd->end_lsn ) {
358         vcdplayer_read_status_t read_status;
359
360         /* We've run off of the end of this entry. Do we continue or stop? */
361         dbg_print( (INPUT_DBG_LSN|INPUT_DBG_PBC), 
362                    "end reached, cur: %u", p_vcd->cur_lsn );
363
364         read_status = vcdplayer_pbc_is_on( p_vcd ) 
365           ? vcdplayer_pbc_nav( p_input ) 
366           : vcdplayer_non_pbc_nav( p_input );
367
368         switch (read_status) {
369         case READ_END:
370           /* End reached. Return NULL to indicated this. */
371         case READ_ERROR:
372           /* Some sort of error. */
373           return i_read;
374         case READ_STILL_FRAME: 
375           {
376             byte_t * p_buf = p_buffer;
377             p_buf += (i_index*M2F2_SECTOR_SIZE);
378             memset(p_buf, 0, M2F2_SECTOR_SIZE);
379             p_buf += 2;
380             *p_buf = 0x01;
381             dbg_print(INPUT_DBG_STILL, "Handled still event\n");
382             return i_read + M2F2_SECTOR_SIZE;
383           }
384         default:
385         case READ_BLOCK:
386           break;
387         }
388       }
389
390       if ( VCDReadSector( VLC_OBJECT(p_input), p_vcd->vcd,
391                           p_vcd->cur_lsn, 
392                           p_buffer + (i_index*M2F2_SECTOR_SIZE) ) < 0 )
393         {
394           LOG_ERR ("could not read sector %d", p_vcd->cur_lsn );
395           return -1;
396         }
397       
398       p_vcd->cur_lsn ++;
399       
400       /* Update chapter */
401       if( p_vcd->b_valid_ep &&
402           /* FIXME kludge so that read does not update chapter
403            * when a manual chapter change was requested and not
404            * yet accomplished */
405           !p_input->stream.p_new_area )
406         {
407           unsigned int i_entry = p_input->stream.p_selected_area->i_part;
408           
409           vlc_mutex_lock( &p_input->stream.stream_lock );
410           
411           if( i_entry < p_vcd->num_entries &&
412               p_vcd->cur_lsn >= p_vcd->p_entries[i_entry+1] )
413             {
414               dbg_print( INPUT_DBG_PBC, 
415                          "new entry, i_entry %d, sector %d, es %d",
416                          i_entry, p_vcd->cur_lsn, 
417                          p_vcd->p_entries[i_entry] );
418               p_vcd->play_item.num = 
419                 ++ p_input->stream.p_selected_area->i_part;
420               p_vcd->play_item.type = VCDINFO_ITEM_TYPE_ENTRY;
421               VCDUpdateVar( p_input, p_vcd->play_item.num, VLC_VAR_SETVALUE,
422                             "chapter", "Setting entry" );
423             }
424           vlc_mutex_unlock( &p_input->stream.stream_lock );
425         }
426
427         i_read += M2F2_SECTOR_SIZE;
428     }
429
430     if ( i_len % M2F2_SECTOR_SIZE ) /* this should not happen */
431     {
432         if ( VCDReadSector( VLC_OBJECT(p_input), p_vcd->vcd,
433                             p_vcd->cur_lsn, p_last_sector ) < 0 )
434         {
435             LOG_ERR ("could not read sector %d", p_vcd->cur_lsn );
436             return -1;
437         }
438
439         p_input->p_vlc->pf_memcpy( p_buffer + i_blocks * M2F2_SECTOR_SIZE,
440                                    p_last_sector, i_len % M2F2_SECTOR_SIZE );
441         i_read += i_len % M2F2_SECTOR_SIZE;
442     }
443
444     return i_read;
445 }
446
447
448 /*****************************************************************************
449  * VCDSetProgram: Does nothing since a VCD is mono_program
450  *****************************************************************************/
451 static int 
452 VCDSetProgram( input_thread_t * p_input, pgrm_descriptor_t * p_program)
453 {
454     thread_vcd_data_t *     p_vcd= (thread_vcd_data_t *)p_input->p_access_data;
455     dbg_print( (INPUT_DBG_CALL|INPUT_DBG_EXT), "VCDSetProgram" );
456     return 0;
457 }
458
459
460 /*****************************************************************************
461  * VCDSetArea: initialize internal data structures and input stream data 
462    so set subsequent reading and seeking to reflect that we are
463    at track x, entry or segment y.
464    This is called for each user navigation request, e.g. the GUI 
465    Chapter/Title selections or in initial MRL parsing. 
466  ****************************************************************************/
467 int 
468 VCDSetArea( input_thread_t * p_input, input_area_t * p_area )
469 {
470     thread_vcd_data_t *p_vcd = (thread_vcd_data_t*)p_input->p_access_data;
471     unsigned int i_entry = p_area->i_part;
472     track_t i_track      = p_area->i_id;
473     int old_seekable     = p_input->stream.b_seekable;
474     unsigned int i_nb    = p_area->i_plugin_data + p_area->i_part_nb;
475
476     dbg_print( (INPUT_DBG_CALL|INPUT_DBG_EXT),
477                "track: %d, entry %d, seekable %d",
478                i_track, i_entry, old_seekable );
479
480     /* we can't use the interface slider until initilization is complete */
481     p_input->stream.b_seekable = 0;
482
483     if( p_area != p_input->stream.p_selected_area )
484     {
485         unsigned int i;
486
487         /* If is the result of a track change, make the entry valid. */
488         if (i_entry < p_area->i_plugin_data || i_entry >= i_nb) 
489           i_entry = p_area->i_plugin_data;
490
491         /* Change the default area */
492         p_input->stream.p_selected_area = p_area;
493
494         /* Update the navigation variables without triggering a callback */
495         VCDUpdateVar( p_input, i_track, VLC_VAR_SETVALUE, "title", 
496                       "Setting track");
497
498         var_Change( p_input, "chapter", VLC_VAR_CLEARCHOICES, NULL, NULL );
499         for( i = p_area->i_plugin_data; i < i_nb; i++ )
500         {
501           VCDUpdateVar( p_input, i , VLC_VAR_ADDCHOICE, 
502                         "chapter",  "Adding entry choice");
503         }
504     }
505
506     if (i_track == 0) 
507       VCDSetOrigin( p_input, p_vcd->p_segments[i_entry], 
508                     p_vcd->p_segments[i_entry], p_vcd->p_segments[i_entry+1],
509                     i_entry, 0 );
510     else
511       VCDSetOrigin( p_input, p_vcd->p_sectors[i_track], 
512                     vcdinfo_get_entry_lsn(p_vcd->vcd, i_entry), 
513                     p_vcd->p_sectors[i_track+1],
514                     i_entry, i_track );
515
516     p_input->stream.b_seekable = old_seekable;
517     /* warn interface that something has changed */
518     p_input->stream.b_changed = 1;
519
520     return VLC_SUCCESS;
521 }
522
523
524 /****************************************************************************
525  * VCDSeek
526  ****************************************************************************/
527 void 
528 VCDSeek( input_thread_t * p_input, off_t i_off )
529 {
530     thread_vcd_data_t * p_vcd;
531     unsigned int i_entry=0; /* invalid entry */
532
533     p_vcd = (thread_vcd_data_t *) p_input->p_access_data;
534
535     p_vcd->cur_lsn = p_vcd->origin_lsn + (i_off / (off_t)M2F2_SECTOR_SIZE);
536
537     vlc_mutex_lock( &p_input->stream.stream_lock );
538 #define p_area p_input->stream.p_selected_area
539     /* Find entry */
540     if( p_vcd->b_valid_ep )
541     {
542         for( i_entry = 1 ; i_entry < p_vcd->num_entries ; i_entry ++ )
543         {
544             if( p_vcd->cur_lsn < p_vcd->p_entries[i_entry] )
545             {
546               VCDUpdateVar( p_input, i_entry, VLC_VAR_SETVALUE, 
547                             "chapter", "Setting entry" );
548               break;
549             }
550         }
551         p_vcd->play_item.num  = p_area->i_part = i_entry;
552         p_vcd->play_item.type = VCDINFO_ITEM_TYPE_ENTRY;
553     }
554 #undef p_area
555
556     p_input->stream.p_selected_area->i_tell = i_off;
557
558     dbg_print( (INPUT_DBG_CALL|INPUT_DBG_EXT|INPUT_DBG_SEEK),
559     "orig %d, cur: %d, offset: %lld, start: %lld, entry %d", 
560                p_vcd->origin_lsn, p_vcd->cur_lsn, i_off, 
561                p_input->stream.p_selected_area->i_start, i_entry );
562
563     vlc_mutex_unlock( &p_input->stream.stream_lock );
564 }
565
566 /*****************************************************************************
567   VCDPlay: set up internal structures so seeking/reading places an item.
568   itemid: the thing to play.
569   user_entry: true if itemid is a user selection (rather than internally-
570   generated selection such as via PBC) in which case we may have to adjust 
571   for differences in numbering. 
572  *****************************************************************************/
573 int
574 VCDPlay( input_thread_t *p_input, vcdinfo_itemid_t itemid )
575 {
576     thread_vcd_data_t *     p_vcd= (thread_vcd_data_t *)p_input->p_access_data;
577     input_area_t *          p_area;
578     
579     p_vcd->in_still = 0;
580
581 #define area p_input->stream.pp_areas
582
583     switch (itemid.type) {
584     case VCDINFO_ITEM_TYPE_TRACK:
585
586       /* Valid tracks go from 1...num_tracks-1, because track 0 is unplayable.
587        */
588
589       if (itemid.num == 0 || itemid.num >= p_vcd->num_tracks) {
590         LOG_ERR ("Invalid track number %d", itemid.num );
591         return VLC_EGENERIC;
592       }
593       p_area           = area[itemid.num];
594       p_area->i_part   = p_area->i_plugin_data;
595       p_input->stream.b_seekable = 1;
596       break;
597     case VCDINFO_ITEM_TYPE_SEGMENT: 
598       /* Valid segments go from 0...num_segments-1. */
599       if (itemid.num >= p_vcd->num_segments) {
600         LOG_ERR ( "Invalid segment number: %d", itemid.num );
601         return VLC_EGENERIC;
602       } else {
603         vcdinfo_video_segment_type_t segtype = 
604           vcdinfo_get_video_type(p_vcd->vcd, itemid.num);
605         
606         dbg_print(INPUT_DBG_PBC, "%s (%d), seg_num: %d", 
607                   vcdinfo_video_type2str(p_vcd->vcd, itemid.num), 
608                   (int) segtype, itemid.num);
609         
610         p_area           = area[0];
611         p_area->i_part   = itemid.num;
612         
613         switch (segtype)
614           {
615           case VCDINFO_FILES_VIDEO_NTSC_STILL:
616           case VCDINFO_FILES_VIDEO_NTSC_STILL2:
617           case VCDINFO_FILES_VIDEO_PAL_STILL:
618           case VCDINFO_FILES_VIDEO_PAL_STILL2:
619             p_input->stream.b_seekable = 0;
620             p_vcd->in_still = -5;
621             break;
622           default:
623             p_input->stream.b_seekable = 1;
624             p_vcd->in_still = 0;
625           }
626       }
627       break;
628       
629     case VCDINFO_ITEM_TYPE_LID:
630       /* LIDs go from 1..num_lids. */
631       if (itemid.num == 0 || itemid.num > p_vcd->num_lids) {
632         LOG_ERR ( "Invalid LID number: %d", itemid.num );
633         return VLC_EGENERIC;
634       } else {
635         p_vcd->cur_lid = itemid.num;
636         vcdinfo_lid_get_pxd(p_vcd->vcd, &(p_vcd->pxd), itemid.num);
637     
638         switch (p_vcd->pxd.descriptor_type) {
639       
640         case PSD_TYPE_SELECTION_LIST:
641         case PSD_TYPE_EXT_SELECTION_LIST: {
642           vcdinfo_itemid_t trans_itemid;
643           uint16_t trans_itemid_num;
644           
645           if (p_vcd->pxd.psd == NULL) return VLC_EGENERIC;
646           trans_itemid_num  = vcdinf_psd_get_itemid(p_vcd->pxd.psd);
647           vcdinfo_classify_itemid(trans_itemid_num, &trans_itemid);
648           p_vcd->loop_count = 1;
649           p_vcd->loop_item  = trans_itemid;
650           return VCDPlay( p_input, trans_itemid );
651           break;
652         }
653           
654         case PSD_TYPE_PLAY_LIST: {
655           if (p_vcd->pxd.pld == NULL) return VLC_EGENERIC;
656           p_vcd->pdi = -1;
657           return vcdplayer_inc_play_item(p_input) 
658             ? VLC_SUCCESS : VLC_EGENERIC;
659           break;
660         }
661           
662         case PSD_TYPE_END_LIST:
663         case PSD_TYPE_COMMAND_LIST:
664           
665         default:
666           ;
667         }
668       }
669       return VLC_EGENERIC;
670     case VCDINFO_ITEM_TYPE_ENTRY:
671       /* Entries go from 0..num_entries-1. */
672       if (itemid.num >= p_vcd->num_entries) {
673         LOG_ERR ("Invalid entry number: %d", itemid.num );
674         return VLC_EGENERIC;
675       } else {
676         track_t cur_track  = vcdinfo_get_track(p_vcd->vcd,  itemid.num);
677         p_area             = area[cur_track];
678         p_area->i_part     = itemid.num;
679         p_input->stream.b_seekable = 1;
680       }
681       break;
682     default:
683       LOG_ERR ("unknown entry type" );
684       return VLC_EGENERIC;
685     }
686
687     VCDSetArea( p_input, p_area );
688
689 #undef area
690
691     p_vcd->play_item = itemid;
692
693     dbg_print( (INPUT_DBG_CALL), 
694                "i_start %lld, i_size: %lld, i_tell: %lld, lsn %d", 
695                p_area->i_start, p_area->i_size, 
696                p_area->i_tell, p_vcd->cur_lsn );
697         
698     return VLC_SUCCESS;
699 }
700
701 /*****************************************************************************
702   VCDEntryPoints: Reads the information about the entry points on the disc
703   and initializes area information with that.
704   Before calling this track information should have been read in.
705  *****************************************************************************/
706 static int 
707 VCDEntryPoints( input_thread_t * p_input )
708 {
709     thread_vcd_data_t *               p_vcd;
710     unsigned int                      i_nb;
711     unsigned int                      i, i_entry_index = 0;
712     unsigned int                      i_previous_track = CDIO_INVALID_TRACK;
713
714     p_vcd = (thread_vcd_data_t *) p_input->p_access_data;
715
716     i_nb = vcdinfo_get_num_entries(p_vcd->vcd);
717     if (0 == i_nb) 
718       return -1;
719     
720     p_vcd->p_entries  = malloc( sizeof( lba_t ) * i_nb );
721
722     if( p_vcd->p_entries == NULL )
723     {
724         LOG_ERR ("not enough memory for entry points treatment" );
725         return -1;
726     }
727
728     p_vcd->num_entries = 0;
729
730     for( i = 0 ; i < i_nb ; i++ )
731     {
732         track_t i_track = vcdinfo_get_track(p_vcd->vcd, i);
733         if( i_track <= p_input->stream.i_area_nb )
734         {
735             p_vcd->p_entries[i] = 
736               vcdinfo_get_entry_lsn(p_vcd->vcd, i);
737             p_input->stream.pp_areas[i_track]->i_part_nb ++;
738
739             /* if this entry belongs to a new track */
740             if( i_track != i_previous_track )
741             {
742                 /* i_plugin_data is used to store the first entry of the area*/
743                 p_input->stream.pp_areas[i_track]->i_plugin_data =
744                                                             i_entry_index;
745                 i_previous_track = i_track;
746                 p_input->stream.pp_areas[i_track]->i_part_nb = 1;
747             }
748             i_entry_index ++;
749             p_vcd->num_entries ++;
750         }
751         else
752             msg_Warn( p_input, "wrong track number found in entry points" );
753     }
754     p_vcd->b_valid_ep = true;
755     return 0;
756 }
757
758 /*****************************************************************************
759  * VCDSegments: Reads the information about the segments the disc.
760  *****************************************************************************/
761 static int
762 VCDSegments( input_thread_t * p_input )
763 {
764     thread_vcd_data_t * p_vcd;
765     unsigned int        i;
766     unsigned int        num_segments;
767     
768
769     p_vcd = (thread_vcd_data_t *) p_input->p_access_data;
770     num_segments = p_vcd->num_segments = vcdinfo_get_num_segments(p_vcd->vcd);
771
772 #define area p_input->stream.pp_areas
773
774     /* area 0 is reserved for segments. Set Absolute start offset
775          and size */
776     area[0]->i_plugin_data = 0;
777     input_DelArea( p_input, area[0] );
778     input_AddArea( p_input, 0, 0 );
779     
780     area[0]->i_start = (off_t)p_vcd->p_sectors[0] 
781       * (off_t)M2F2_SECTOR_SIZE;
782     area[0]->i_size = (off_t)(p_vcd->p_sectors[1] - p_vcd->p_sectors[0])
783       * (off_t)M2F2_SECTOR_SIZE;
784     
785     /* Default Segment  */
786     area[0]->i_part = 0;
787     
788     /* i_plugin_data is used to store which entry point is the first
789        of the track (area) */
790     area[0]->i_plugin_data = 0;
791
792     area[0]->i_part_nb = 0;
793     
794     dbg_print( INPUT_DBG_MRL, "area id %d, for segment track %d", 
795                area[0]->i_id, 0 );
796
797     if (num_segments == 0) return 0;
798
799     /* We have one additional segment allocated so we can get the size
800        by subtracting seg[i+1] - seg[i].
801      */
802     p_vcd->p_segments = malloc( sizeof( lba_t ) * (num_segments+1) );
803     if( p_vcd->p_segments == NULL )
804     {
805         LOG_ERR ("not enough memory for segment treatment" );
806         return -1;
807     }
808
809     /* Update the navigation variables without triggering a callback */
810     VCDUpdateVar( p_input, 0, VLC_VAR_SETVALUE, "title", "Setting track" );
811     var_Change( p_input, "chapter", VLC_VAR_CLEARCHOICES, NULL, NULL );
812     
813     for( i = 0 ; i < num_segments ; i++ )
814     {
815       p_vcd->p_segments[i] = vcdinfo_get_seg_lsn(p_vcd->vcd, i);
816       area[0]->i_part_nb ++;
817       VCDUpdateVar( p_input, i , VLC_VAR_ADDCHOICE, 
818                     "chapter", "Adding segment choice");
819     }
820
821 #undef area
822
823     p_vcd->p_segments[num_segments] = p_vcd->p_segments[num_segments-1]+
824       vcdinfo_get_seg_sector_count(p_vcd->vcd, num_segments-1);
825     
826     return 0;
827 }
828
829 /*****************************************************************************
830  VCDTracks: initializes area information. 
831  Before calling this track information should have been read in.
832  *****************************************************************************/
833 static void
834 VCDTracks( input_thread_t * p_input )
835 {
836     thread_vcd_data_t * p_vcd;
837     unsigned int        i;
838
839     p_vcd = (thread_vcd_data_t *) p_input->p_access_data;
840
841 #define area p_input->stream.pp_areas
842
843     /* We start area addressing for tracks at 1 since the default area 0
844        is reserved for segments */
845
846     for( i = 1 ; i < p_vcd->num_tracks ; i++ )
847     {
848         /* Tracks are Program Chains */
849         input_AddArea( p_input, i, i );
850
851         /* Absolute start byte offset and byte size */
852         area[i]->i_start = (off_t) p_vcd->p_sectors[i] 
853                            * (off_t)M2F2_SECTOR_SIZE;
854         area[i]->i_size = (off_t)(p_vcd->p_sectors[i+1] - p_vcd->p_sectors[i])
855                            * (off_t)M2F2_SECTOR_SIZE;
856
857         /* Current entry being played in track */
858         area[i]->i_part = 0;
859
860         /* i_plugin_data is used to store which entry point is the first
861          * of the track (area) */
862         area[i]->i_plugin_data = 0;
863
864         dbg_print( INPUT_DBG_MRL, 
865                    "area[%d] id: %d, i_start: %lld, i_size: %lld", 
866                    i, area[i]->i_id, area[i]->i_start, area[i]->i_size );
867     }
868
869 #undef area
870
871     return ;
872 }
873
874 /*****************************************************************************
875   VCDLIDs: Reads the LIST IDs from the LOT.
876  *****************************************************************************/
877 static int 
878 VCDLIDs( input_thread_t * p_input )
879 {
880     thread_vcd_data_t *p_vcd = (thread_vcd_data_t *) p_input->p_access_data;
881
882     p_vcd->num_lids = vcdinfo_get_num_LIDs(p_vcd->vcd);
883     p_vcd->cur_lid  = VCDINFO_INVALID_ENTRY;
884
885     if (vcdinfo_read_psd (p_vcd->vcd)) {
886       
887       vcdinfo_visit_lot (p_vcd->vcd, false);
888       
889 #if FIXED
890     /* 
891        We need to change libvcdinfo to be more robust when there are 
892        problems reading the extended PSD. Given that area-highlighting and 
893        selection features in the extended PSD haven't been implemented,
894        it's best then to not try to read this at all.
895      */
896       if (vcdinfo_get_psd_x_size(p_vcd->vcd))
897         vcdinfo_visit_lot (p_vcd->vcd, true);
898 #endif 
899     }
900
901     dbg_print( (INPUT_DBG_CALL|INPUT_DBG_MRL), 
902                "num LIDs=%d", p_vcd->num_lids);
903
904     return 0;
905 }
906
907 /*****************************************************************************
908  * VCDParse: parse command line
909  *****************************************************************************/
910 static char * 
911 VCDParse( input_thread_t * p_input, /*out*/ vcdinfo_itemid_t * p_itemid )
912 {
913     thread_vcd_data_t *p_vcd = (thread_vcd_data_t *)p_input->p_access_data;
914     char *             psz_parser;
915     char *             psz_source;
916     char *             psz_next;
917
918     p_itemid->type=VCDINFO_ITEM_TYPE_TRACK;
919     p_itemid->num=1;
920     
921 #ifdef WIN32
922     /* On Win32 we want the VCD access plugin to be explicitly requested,
923      * we end up with lots of problems otherwise */
924     if( !p_input->psz_access || !*p_input->psz_access ) return NULL;
925 #endif
926
927     if( !p_input->psz_name )
928     {
929         return NULL;
930     }
931
932     psz_parser = psz_source = strdup( p_input->psz_name );
933
934     /* Parse input string :
935      * [device][@[type][title]] */
936     while( *psz_parser && *psz_parser != '@' )
937     {
938         psz_parser++;
939     }
940
941     if( *psz_parser == '@' )
942     {
943       /* Found the divide between the source name and the 
944          type+entry number. */
945       unsigned int num;
946       
947       *psz_parser = '\0';
948       ++psz_parser;
949       if( *psz_parser )
950         {
951           switch(*psz_parser) {
952           case 'E': 
953             p_itemid->type = VCDINFO_ITEM_TYPE_ENTRY;
954             ++psz_parser;
955             break;
956           case 'P': 
957             p_itemid->type = VCDINFO_ITEM_TYPE_LID;
958             ++psz_parser;
959             break;
960           case 'S': 
961             p_itemid->type = VCDINFO_ITEM_TYPE_SEGMENT;
962             ++psz_parser;
963             break;
964           case 'T': 
965             p_itemid->type = VCDINFO_ITEM_TYPE_TRACK;
966             ++psz_parser;
967             break;
968           default: ;
969           }
970         }
971       
972       num = strtol( psz_parser, &psz_next, 10 );
973       if ( *psz_parser != '\0' && *psz_next == '\0') 
974         {
975           p_itemid->num = num;
976         }
977       
978     }
979
980     if( !*psz_source )
981     {
982         if( !p_input->psz_access )
983         {
984             return NULL;
985         }
986         psz_source = config_GetPsz( p_input, "vcd" );
987         if( !psz_source ) return NULL;
988     }
989
990     dbg_print( (INPUT_DBG_CALL|INPUT_DBG_MRL), 
991                "source=%s entry=%d type=%d",
992                psz_source, p_itemid->num, p_itemid->type);
993
994     return psz_source;
995 }
996
997 /* 
998    Set's start origin subsequent seeks/reads
999 */
1000 static void 
1001 VCDSetOrigin( input_thread_t *p_input, lsn_t origin_lsn, 
1002               lsn_t cur_lsn, lsn_t end_lsn, int cur_entry, track_t cur_track )
1003 {
1004   thread_vcd_data_t * p_vcd  = (thread_vcd_data_t *) p_input->p_access_data;
1005
1006   p_vcd->origin_lsn = origin_lsn;
1007   p_vcd->cur_lsn    = cur_lsn;
1008   p_vcd->end_lsn    = end_lsn;
1009   p_vcd->cur_track  = cur_track;
1010   p_vcd->play_item.num  = cur_entry;
1011   p_vcd->play_item.type = VCDINFO_ITEM_TYPE_ENTRY;
1012   
1013   dbg_print( (INPUT_DBG_CALL|INPUT_DBG_LSN),
1014              "origin: %d, cur_lsn: %d, end_lsn: %d, entry: %d, track: %d",
1015              origin_lsn, cur_lsn, end_lsn, cur_entry, cur_track );
1016
1017   p_input->stream.p_selected_area->i_tell =
1018     (off_t) (p_vcd->cur_lsn - p_vcd->origin_lsn) * (off_t)M2F2_SECTOR_SIZE;
1019
1020   VCDUpdateVar( p_input, cur_entry, VLC_VAR_SETVALUE, 
1021                 "chapter", "Setting entry");
1022 }
1023
1024 /*****************************************************************************
1025  * vcd_Open: Opens a VCD device or file and returns an opaque handle
1026  *****************************************************************************/
1027 static vcdinfo_obj_t *
1028 vcd_Open( vlc_object_t *p_this, const char *psz_dev )
1029 {
1030     vcdinfo_obj_t *p_vcdobj;
1031     char  *actual_dev;
1032
1033     if( !psz_dev ) return NULL;
1034
1035     /* Set where to log errors messages from libcdio. */
1036     p_vcd_input = (input_thread_t *)p_this;
1037     cdio_log_set_handler ( cdio_log_handler );
1038     vcd_log_set_handler ( vcd_log_handler );
1039     
1040     actual_dev=strdup(psz_dev);
1041     if ( vcdinfo_open(&p_vcdobj, &actual_dev, DRIVER_UNKNOWN, NULL) != 
1042          VCDINFO_OPEN_VCD) {
1043       free(actual_dev);
1044       return NULL;
1045     }
1046     free(actual_dev);
1047
1048     return p_vcdobj;
1049 }
1050
1051 /****************************************************************************
1052  * VCDReadSector: Read a sector (2324 bytes)
1053  ****************************************************************************/
1054 static int 
1055 VCDReadSector( vlc_object_t *p_this, const vcdinfo_obj_t *p_vcd,
1056                lsn_t cur_lsn, byte_t * p_buffer )
1057 {
1058   typedef struct {
1059     uint8_t subheader   [8];
1060     uint8_t data        [M2F2_SECTOR_SIZE];
1061   } vcdsector_t;
1062   vcdsector_t vcd_sector;
1063   
1064   if (cdio_read_mode2_sector(vcdinfo_get_cd_image(p_vcd), 
1065                              &vcd_sector, cur_lsn, true) 
1066       != 0)
1067     {
1068       msg_Warn( p_this, "Could not read LSN %d", cur_lsn );
1069       return -1;
1070     }
1071     
1072   memcpy (p_buffer, vcd_sector.data, M2F2_SECTOR_SIZE);
1073   
1074   return( 0 );
1075 }
1076
1077 /****************************************************************************
1078  Update the "varname" variable to i_num without triggering a callback.
1079 ****************************************************************************/
1080 static void
1081 VCDUpdateVar( input_thread_t *p_input, int i_num, int i_action,
1082               const char *varname, const char *label)
1083 {
1084   vlc_value_t val;
1085   val.i_int = i_num;
1086   if (NULL != p_vcd_input) {
1087     thread_vcd_data_t *p_vcd = (thread_vcd_data_t *)p_vcd_input->p_access_data;
1088     dbg_print( INPUT_DBG_PBC, "%s %d", label, i_num );
1089   }
1090   var_Change( p_input, varname, i_action, &val, NULL );
1091 }
1092 #endif