]> git.sesse.net Git - vlc/blob - modules/access/vcdx/access.c
More segment playing improvements, be able to map seekpoints to
[vlc] / modules / access / vcdx / access.c
1 /*****************************************************************************
2  * vcd.c : VCD input module for vlc using libcdio, libvcd and libvcdinfo. 
3  *         vlc-specific things tend to go here.
4  *****************************************************************************
5  * Copyright (C) 2000, 2003, 2004 VideoLAN
6  * $Id$
7  *
8  * Authors: Rocky Bernstein <rocky@panix.com>
9  *   Some code is based on the non-libcdio VCD plugin (as there really
10  *   isn't real developer documentation yet on how to write a 
11  *   navigable plugin.)
12  *
13  * This program is free software; you can redistribute it and/or modify
14  * it under the terms of the GNU General Public License as published by
15  * the Free Software Foundation; either version 2 of the License, or
16  * (at your option) any later version.
17  *
18  * This program is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21  * GNU General Public License for more details.
22  *
23  * You should have received a copy of the GNU General Public License
24  * along with this program; if not, write to the Free Software
25  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
26  *****************************************************************************/
27
28 /*****************************************************************************
29  * Preamble
30  *****************************************************************************/
31
32 #include <vlc/vlc.h>
33 #include <vlc/intf.h>
34 #include <vlc/input.h>
35
36 #include "vcd.h"
37 #include "info.h"
38 #include "intf.h"
39 #include "vlc_keys.h"
40
41 #include <cdio/cdio.h>
42 #include <cdio/cd_types.h>
43 #include <cdio/logging.h>
44 #include <cdio/util.h>
45 #include <libvcd/info.h>
46 #include <libvcd/logging.h>
47
48 #define FREE_AND_NULL(ptr) if (NULL != ptr) free(ptr); ptr = NULL;
49
50 /*****************************************************************************
51  * Local prototypes
52  *****************************************************************************/
53
54 /* First those which are accessed from outside (via pointers). */
55 static block_t *VCDReadBlock    ( access_t * );
56
57 static int      VCDControl      ( access_t *p_access, int i_query, 
58                                   va_list args );
59
60 /* Now those which are strictly internal */
61 static void VCDSetOrigin    ( access_t *p_access, lsn_t i_lsn, 
62                               track_t track,  
63                               const vcdinfo_itemid_t * p_itemid );
64 static vlc_bool_t  VCDEntryPoints  ( access_t * );
65 static vlc_bool_t  VCDLIDs         ( access_t * );
66 static vlc_bool_t  VCDSegments     ( access_t * );
67 static int  VCDTitles       ( access_t * );
68 static int  VCDReadSector   ( vlc_object_t *p_this,
69                               const vcdinfo_obj_t *p_vcd, lsn_t i_lsn,
70                               byte_t * p_buffer );
71 static char *VCDParse       ( access_t *,
72                               /*out*/ vcdinfo_itemid_t * p_itemid ,
73                               /*out*/ vlc_bool_t *play_single_item );
74
75 static void VCDUpdateVar( access_t *p_access, int i_entry, int i_action,
76                           const char *p_varname, char *p_label,
77                           const char *p_debug_label );
78
79 static vcdinfo_obj_t *vcd_Open   ( vlc_object_t *p_this, const char *psz_dev );
80
81 /****************************************************************************
82  * Private functions
83  ****************************************************************************/
84
85 /* FIXME: This variable is a hack. Would be nice to eliminate the
86    global-ness. */
87
88 static access_t *p_vcd_access = NULL;
89
90 /* process messages that originate from libcdio. */
91 static void
92 cdio_log_handler (cdio_log_level_t level, const char message[])
93 {
94   const vcdplayer_t *p_vcd = (vcdplayer_t *)p_vcd_access->p_sys;
95   switch (level) {
96   case CDIO_LOG_DEBUG:
97   case CDIO_LOG_INFO:
98     if (p_vcd->i_debug & INPUT_DBG_CDIO)
99       msg_Dbg( p_vcd_access, message);
100     break;
101   case CDIO_LOG_WARN:
102     msg_Warn( p_vcd_access, message);
103     break;
104   case CDIO_LOG_ERROR:
105   case CDIO_LOG_ASSERT:
106     msg_Err( p_vcd_access, message);
107     break;
108   default:
109     msg_Warn( p_vcd_access, message,
110             _("The above message had unknown log level"),
111             level);
112   }
113   return;
114 }
115
116 /* process messages that originate from vcdinfo. */
117 static void
118 vcd_log_handler (vcd_log_level_t level, const char message[])
119 {
120   vcdplayer_t *p_vcd = (vcdplayer_t *)p_vcd_access->p_sys;
121   switch (level) {
122   case VCD_LOG_DEBUG:
123   case VCD_LOG_INFO:
124     if (p_vcd->i_debug & INPUT_DBG_VCDINFO)
125       msg_Dbg( p_vcd_access, message);
126     break;
127   case VCD_LOG_WARN:
128     msg_Warn( p_vcd_access, message);
129     break;
130   case VCD_LOG_ERROR:
131   case VCD_LOG_ASSERT:
132     msg_Err( p_vcd_access, message);
133     break;
134   default:
135     msg_Warn( p_vcd_access, "%s\n%s %d", message,
136             _("The above message had unknown vcdimager log level"),
137             level);
138   }
139   return;
140 }
141
142 /*****************************************************************************
143   VCDRead: reads VCD_BLOCKS_ONCE from the VCD and returns that.
144   NULL is returned if something went wrong.
145  *****************************************************************************/
146 static block_t *
147 VCDReadBlock( access_t * p_access )
148 {
149     vcdplayer_t *p_vcd= (vcdplayer_t *)p_access->p_sys;
150     block_t     *p_block;
151     const int   i_blocks = p_vcd->i_blocks_per_read;
152     int         i_read;
153     byte_t      p_last_sector[ M2F2_SECTOR_SIZE ];
154
155     i_read = 0;
156
157 #if 0
158     dbg_print( (INPUT_DBG_CALL), "lsn: %lu", 
159                (long unsigned int) p_vcd->i_lsn );
160 #endif
161
162     /* Allocate a block for the reading */
163     if( !( p_block = block_New( p_access, i_blocks * M2F2_SECTOR_SIZE ) ) )
164     {
165         msg_Err( p_access, "cannot get a new block of size: %i",
166                  i_blocks * M2F2_SECTOR_SIZE );
167         block_Release( p_block );
168         return NULL;
169     }
170
171     for ( i_read = 0 ; i_read < i_blocks ; i_read++ )
172     {
173
174       if ( p_vcd->i_lsn >= p_vcd->end_lsn ) {
175         vcdplayer_read_status_t read_status;
176
177         /* We've run off of the end of this entry. Do we continue or stop? */
178         dbg_print( (INPUT_DBG_LSN|INPUT_DBG_PBC),
179                    "end reached, cur: %lu, end: %u", 
180                    (long unsigned int) p_vcd->i_lsn,
181                    p_vcd->end_lsn);
182
183         read_status = vcdplayer_pbc_is_on( p_vcd )
184           ? vcdplayer_pbc_nav( p_access )
185           : vcdplayer_non_pbc_nav( p_access );
186
187         switch (read_status) {
188         case READ_END:
189           /* End reached. Return NULL to indicated this. */
190         case READ_ERROR:
191           /* Some sort of error. */
192           return NULL;
193
194         case READ_STILL_FRAME:
195           {
196             /* Reached the end of a still frame. */
197             byte_t * p_buf = (byte_t *) p_block->p_buffer;
198
199             p_buf += (i_read*M2F2_SECTOR_SIZE);
200             memset(p_buf, 0, M2F2_SECTOR_SIZE);
201             p_buf += 2;
202             *p_buf = 0x01;
203             dbg_print(INPUT_DBG_STILL, "Handled still event");
204
205             p_vcd->in_still = VLC_TRUE;
206             var_SetInteger( p_access, "state", PAUSE_S );
207
208             return p_block;
209           }
210         default:
211         case READ_BLOCK:
212           break;
213         }
214       }
215
216       if ( VCDReadSector( VLC_OBJECT(p_access), p_vcd->vcd,
217                           p_vcd->i_lsn,
218                           (byte_t *) p_block->p_buffer 
219                           + (i_read*M2F2_SECTOR_SIZE) ) < 0 )
220       {
221           LOG_ERR ("could not read sector %lu", 
222                    (long unsigned int) p_vcd->i_lsn );
223         /* Try to skip one sector (in case of bad sectors) */
224           p_vcd->i_lsn ++;
225           p_access->info.i_pos += M2F2_SECTOR_SIZE;
226           return NULL;
227       }
228
229       p_vcd->i_lsn ++;
230       p_access->info.i_pos += M2F2_SECTOR_SIZE;
231
232       /* Update seekpoint */
233       if ( VCDINFO_ITEM_TYPE_ENTRY == p_vcd->play_item.type )
234       {
235         unsigned int i_entry = p_vcd->play_item.num+1;
236
237         if (p_vcd->i_lsn >= vcdinfo_get_entry_lba(p_vcd->vcd, i_entry))
238         {
239             const track_t i_track = p_vcd->i_track;
240             p_vcd->play_item.num = i_entry;
241             dbg_print( (INPUT_DBG_LSN|INPUT_DBG_PBC), "entry change" );
242             VCDSetOrigin( p_access, 
243                           vcdinfo_get_entry_lba(p_vcd->vcd, i_entry),
244                           i_track, &(p_vcd->play_item) );
245         }
246       }
247     }
248
249     if ( i_read != i_blocks ) /* this should not happen */
250     {
251         if ( VCDReadSector( VLC_OBJECT(p_access), p_vcd->vcd,
252                             p_vcd->i_lsn, p_last_sector ) < 0 )
253         {
254             LOG_ERR ("could not read sector %lu", 
255                      (long unsigned int) p_vcd->i_lsn );
256         }
257         
258         p_vcd->i_lsn ++;
259         return NULL;
260     }
261
262     return p_block;
263 }
264
265
266 /****************************************************************************
267  * VCDSeek
268  ****************************************************************************/
269 int
270 VCDSeek( access_t * p_access, int64_t i_pos )
271 {
272     if (!p_access || !p_access->p_sys) return VLC_EGENERIC;
273     
274     {
275       vcdplayer_t *p_vcd = (vcdplayer_t *)p_vcd_access->p_sys;
276       const input_title_t *t = p_vcd->p_title[p_access->info.i_title];
277       int i_seekpoint;
278       unsigned int i_entry=VCDINFO_INVALID_ENTRY; 
279       
280       /* Next sector to read */
281       p_access->info.i_pos = i_pos;
282       p_vcd->i_lsn = (i_pos / (int64_t)M2F2_SECTOR_SIZE) +
283         p_vcd->track_lsn;
284       
285       /* Find entry */
286       if( p_vcd->b_valid_ep )
287         {
288           for( i_entry = 0 ; i_entry < p_vcd->i_entries ; i_entry ++ )
289             {
290               if( p_vcd->i_lsn < p_vcd->p_entries[i_entry] )
291                 {
292                   VCDUpdateVar( p_access, i_entry, VLC_VAR_SETVALUE,
293                                 "chapter", _("Entry"), "Setting entry" );
294                   break;
295                 }
296             }
297           p_vcd->play_item.num  = i_entry;
298           p_vcd->play_item.type = VCDINFO_ITEM_TYPE_ENTRY;
299           vcdplayer_set_origin(p_access);
300           VCDUpdateTitle(p_access);
301         }
302       
303       dbg_print( (INPUT_DBG_CALL|INPUT_DBG_EXT|INPUT_DBG_SEEK),
304                  "orig %lu, cur: %lu, offset: %lld, entry %d",
305                  (long unsigned int) p_vcd->origin_lsn, 
306                  (long unsigned int) p_vcd->i_lsn, i_pos,
307                  i_entry );
308       
309       /* Find seekpoint */
310       for( i_seekpoint = 0; i_seekpoint < t->i_seekpoint; i_seekpoint++ )
311         {
312           if( i_seekpoint + 1 >= t->i_seekpoint ) break;
313           if( i_pos < t->seekpoint[i_seekpoint + 1]->i_byte_offset ) break;
314         }
315       
316       /* Update current seekpoint */
317       if( i_seekpoint != p_access->info.i_seekpoint )
318         {
319           dbg_print( (INPUT_DBG_SEEK), "seekpoint change %lu", 
320                      (long unsigned int) i_seekpoint );
321           p_access->info.i_update |= INPUT_UPDATE_SEEKPOINT;
322           p_access->info.i_seekpoint = i_seekpoint;
323         }
324
325     }
326     return VLC_SUCCESS;
327     
328 }
329
330 /*****************************************************************************
331   VCDPlay: set up internal structures so seeking/reading places an item.
332   itemid: the thing to play.
333   user_entry: true if itemid is a user selection (rather than internally-
334   generated selection such as via PBC) in which case we may have to adjust
335   for differences in numbering.
336  *****************************************************************************/
337 int
338 VCDPlay( access_t *p_access, vcdinfo_itemid_t itemid )
339 {
340     if (!p_access || !p_access->p_sys) return VLC_EGENERIC;
341     
342     {
343       
344       vcdplayer_t *p_vcd= (vcdplayer_t *)p_access->p_sys;
345 #if 0
346       const vlc_bool_t   b_was_still = p_vcd->in_still;
347 #endif
348       
349       dbg_print(INPUT_DBG_CALL, "itemid.num: %d, itemid.type: %d\n",
350                 itemid.num, itemid.type);
351
352       switch (itemid.type) {
353       case VCDINFO_ITEM_TYPE_TRACK:
354         
355         {
356           track_t i_track = itemid.num;
357           unsigned int i_entry = 
358             vcdinfo_track_get_entry( p_vcd->vcd, i_track);
359           
360           /* Valid MPEG tracks go from 1...i_tracks. */
361         
362           if (i_track == 0 || i_track > p_vcd->i_tracks) {
363             LOG_ERR ("Invalid track number %d", i_track );
364             return VLC_EGENERIC;
365           }
366           p_vcd->in_still  = VLC_FALSE;
367           VCDSetOrigin( p_access, 
368                         vcdinfo_get_entry_lba(p_vcd->vcd, i_entry),
369                         i_track, &itemid );
370           break;
371         }
372         
373       case VCDINFO_ITEM_TYPE_SEGMENT:
374         {
375           int i_seg = itemid.num;
376           
377           /* Valid segments go from 0...i_segments-1. */
378           if (itemid.num >= p_vcd->i_segments) {
379             LOG_ERR ( "Invalid segment number: %d", i_seg );
380             return VLC_EGENERIC;
381           } else {
382             vcdinfo_video_segment_type_t segtype =
383               vcdinfo_get_video_type(p_vcd->vcd, i_seg);
384           
385             dbg_print(INPUT_DBG_PBC, "%s (%d), seg_num: %d",
386                       vcdinfo_video_type2str(p_vcd->vcd, i_seg),
387                       (int) segtype, itemid.num);
388             
389             switch (segtype)
390               {
391               case VCDINFO_FILES_VIDEO_NTSC_STILL:
392               case VCDINFO_FILES_VIDEO_NTSC_STILL2:
393               case VCDINFO_FILES_VIDEO_PAL_STILL:
394               case VCDINFO_FILES_VIDEO_PAL_STILL2:
395                 p_vcd->in_still = VLC_TRUE;
396                 break;
397               default:
398                 p_vcd->in_still = VLC_FALSE;
399               }
400             VCDSetOrigin( p_access, p_vcd->p_segments[i_seg], 
401                           0, &itemid );
402           }
403
404         }
405         break;
406         
407       case VCDINFO_ITEM_TYPE_LID:
408         /* LIDs go from 1..i_lids. */
409         if (itemid.num == 0 || itemid.num > p_vcd->i_lids) {
410           LOG_ERR ( "Invalid LID number: %d", itemid.num );
411           return VLC_EGENERIC;
412         } else {
413           p_vcd->i_lid = itemid.num;
414           vcdinfo_lid_get_pxd(p_vcd->vcd, &(p_vcd->pxd), itemid.num);
415           
416           switch (p_vcd->pxd.descriptor_type) {
417             
418           case PSD_TYPE_SELECTION_LIST:
419           case PSD_TYPE_EXT_SELECTION_LIST: {
420             vcdinfo_itemid_t trans_itemid;
421             uint16_t trans_itemid_num;
422             
423             if (p_vcd->pxd.psd == NULL) return VLC_EGENERIC;
424             trans_itemid_num  = vcdinf_psd_get_itemid(p_vcd->pxd.psd);
425             vcdinfo_classify_itemid(trans_itemid_num, &trans_itemid);
426             p_vcd->loop_count = 1;
427             p_vcd->loop_item  = trans_itemid;
428             return VCDPlay( p_access, trans_itemid );
429             break;
430           }
431             
432           case PSD_TYPE_PLAY_LIST: {
433             if (p_vcd->pxd.pld == NULL) return VLC_EGENERIC;
434             p_vcd->pdi = -1;
435             return vcdplayer_inc_play_item(p_access)
436               ? VLC_SUCCESS : VLC_EGENERIC;
437             break;
438           }
439             
440           case PSD_TYPE_END_LIST:
441           case PSD_TYPE_COMMAND_LIST:
442             
443           default:
444             ;
445           }
446         }
447         return VLC_EGENERIC;
448       case VCDINFO_ITEM_TYPE_ENTRY:
449         {
450           int i_entry = itemid.num;
451           
452           /* Entries go from 0..i_entries-1. */
453           if (itemid.num >= p_vcd->i_entries) {
454             LOG_ERR ("Invalid entry number: %d", i_entry );
455             return VLC_EGENERIC;
456           } else {
457             track_t i_track = vcdinfo_get_track(p_vcd->vcd,  i_entry);
458             p_vcd->in_still = VLC_FALSE;
459             VCDSetOrigin( p_access, 
460                           vcdinfo_get_entry_lba(p_vcd->vcd, i_entry),
461                           i_track, &itemid );
462           }
463           break;
464         }
465         
466       default:
467         LOG_ERR ("unknown entry type" );
468         return VLC_EGENERIC;
469       }
470
471       p_vcd->play_item = itemid;
472       
473     }
474     
475       
476     return VLC_SUCCESS;
477 }
478
479 /*****************************************************************************
480   VCDEntryPoints: Reads the information about the entry points on the disc
481   and initializes area information with that.
482   Before calling this track information should have been read in.
483  *****************************************************************************/
484 static vlc_bool_t
485 VCDEntryPoints( access_t * p_access )
486 {
487   if (!p_access || !p_access->p_sys) return VLC_FALSE;
488   
489   {
490     vcdplayer_t *p_vcd = (vcdplayer_t *) p_access->p_sys;
491     const unsigned int i_entries  =  vcdinfo_get_num_entries(p_vcd->vcd);
492     const track_t      i_last_track 
493       = cdio_get_num_tracks(vcdinfo_get_cd_image(p_vcd->vcd))
494       + cdio_get_first_track_num(vcdinfo_get_cd_image(p_vcd->vcd));
495     unsigned int i;
496    
497     if (0 == i_entries) {
498       LOG_ERR ("no entires found -- something is wrong" );
499       return VLC_FALSE;
500     }
501     
502     p_vcd->p_entries  = malloc( sizeof( lsn_t ) * i_entries );
503     
504     if( p_vcd->p_entries == NULL )
505       {
506         LOG_ERR ("not enough memory for entry points treatment" );
507         return VLC_FALSE;
508       }
509     
510     p_vcd->i_entries = i_entries;
511     
512     for( i = 0 ; i < i_entries ; i++ )
513     {
514         const track_t i_track = vcdinfo_get_track(p_vcd->vcd, i);
515         if( i_track <= i_last_track ) {
516           seekpoint_t *s = vlc_seekpoint_New();
517           char psz_entry[100];
518           
519           snprintf(psz_entry, sizeof(psz_entry), "%s%02d", _("Entry "), i );
520
521           p_vcd->p_entries[i] = vcdinfo_get_entry_lba(p_vcd->vcd, i);
522           
523           s->psz_name      = strdup(psz_entry);
524           s->i_byte_offset = 
525             (p_vcd->p_entries[i] - vcdinfo_get_track_lba(p_vcd->vcd, i_track))
526             * M2F2_SECTOR_SIZE;
527           
528           dbg_print( INPUT_DBG_MRL, 
529                      "%s, lsn %d,  byte_offset %ld\n",
530                      s->psz_name, p_vcd->p_entries[i], 
531                      (unsigned long int) s->i_byte_offset);
532           TAB_APPEND( p_vcd->p_title[i_track-1]->i_seekpoint,
533                       p_vcd->p_title[i_track-1]->seekpoint, s );
534
535         } else 
536           msg_Warn( p_access, "wrong track number found in entry points" );
537     }
538     p_vcd->b_valid_ep = VLC_TRUE;
539     return VLC_TRUE;
540   }
541 }
542
543 /*****************************************************************************
544  * VCDSegments: Reads the information about the segments the disc.
545  *****************************************************************************/
546 static vlc_bool_t
547 VCDSegments( access_t * p_access )
548 {
549     vcdplayer_t *p_vcd = (vcdplayer_t *) p_access->p_sys;
550     unsigned int i;
551     input_title_t *t;
552
553     p_vcd->i_segments = vcdinfo_get_num_segments(p_vcd->vcd);
554
555     dbg_print( (INPUT_DBG_CALL|INPUT_DBG_MRL),
556                "num LIDs=%d", p_vcd->i_segments);
557
558     if ( 0 == p_vcd->i_segments ) return VLC_FALSE;
559
560     t = p_vcd->p_title[p_vcd->i_titles] = vlc_input_title_New();
561     p_vcd->i_titles++;
562
563     t->i_size    = 0; /* Not sure Segments have a size associated */
564     t->psz_name  = strdup(_("Segments"));
565     
566     /* We have one additional segment allocated so we can get the size
567        by subtracting seg[i+1] - seg[i].
568      */
569     p_vcd->p_segments = malloc( sizeof( lsn_t ) * (p_vcd->i_segments+1) );
570     if( p_vcd->p_segments == NULL )
571     {
572         LOG_ERR ("not enough memory for segment treatment" );
573         return VLC_FALSE;
574     }
575
576     for( i = 0 ; i < p_vcd->i_segments ; i++ )
577     {
578         char psz_segment[100];
579         seekpoint_t *s = vlc_seekpoint_New();
580         p_vcd->p_segments[i] = vcdinfo_get_seg_lsn(p_vcd->vcd, i);
581         
582         snprintf( psz_segment, sizeof(psz_segment), "%s%02d", _("Segment "), 
583                   i );
584         
585         s->i_byte_offset = 0; /* Not sure what this would mean here */
586         s->psz_name  = strdup(psz_segment);
587         TAB_APPEND( t->i_seekpoint, t->seekpoint, s );
588     }
589
590     p_vcd->p_segments[p_vcd->i_segments] = 
591       p_vcd->p_segments[p_vcd->i_segments-1]+
592       vcdinfo_get_seg_sector_count(p_vcd->vcd, p_vcd->i_segments-1);
593
594     return VLC_TRUE;
595 }
596
597 /*****************************************************************************
598  Build title table which will be returned via ACCESS_GET_TITLE_INFO.
599
600  We start area addressing for tracks at 1 since the default area 0
601  is reserved for segments. 
602  *****************************************************************************/
603 static int
604 VCDTitles( access_t * p_access )
605 {
606     /* We'll assume a VCD has its first MPEG track
607        cdio_get_first_track_num()+1 could be used if one wanted to be
608        very careful about this. Note: cdio_get_first_track() will give the
609        ISO-9660 track before the MPEG tracks.
610      */
611   
612     if (!p_access || !p_access->p_sys) return VLC_EGENERIC;
613
614     {
615         vcdplayer_t *p_vcd = (vcdplayer_t *) p_access->p_sys;
616         track_t            i;
617
618         p_vcd->i_titles = 0;
619         for( i = 1 ; i <= p_vcd->i_tracks ; i++ )
620         {
621             input_title_t *t = p_vcd->p_title[i-1] = vlc_input_title_New();
622             char psz_track[100];
623             uint32_t i_secsize = vcdinfo_get_track_sect_count( p_vcd->vcd, i );
624             
625             snprintf( psz_track, sizeof(psz_track), "%s%02d", _("Track "), 
626                       i );
627             
628             t->i_size    = (i_secsize) * (int64_t) M2F2_SECTOR_SIZE;
629             t->psz_name  = strdup(psz_track);
630             
631             dbg_print( INPUT_DBG_MRL, "track[%d] i_size: %lld",
632                        i, t->i_size );
633
634             p_vcd->i_titles++;
635         }
636       
637       return VLC_SUCCESS;
638     }
639 }
640
641 /*****************************************************************************
642   VCDLIDs: Reads the LIST IDs from the LOT.
643  *****************************************************************************/
644 static vlc_bool_t
645 VCDLIDs( access_t * p_access )
646 {
647     vcdplayer_t *p_vcd = (vcdplayer_t *) p_access->p_sys;
648     input_title_t *t;
649     unsigned int i_lid, i_title;
650
651     p_vcd->i_lids = vcdinfo_get_num_LIDs(p_vcd->vcd);
652     p_vcd->i_lid  = VCDINFO_INVALID_ENTRY;
653
654     dbg_print( (INPUT_DBG_CALL|INPUT_DBG_MRL),
655                "num LIDs=%d", p_vcd->i_lids);
656
657     if ( 0 == p_vcd->i_lids ) return VLC_FALSE;
658     
659     if (vcdinfo_read_psd (p_vcd->vcd)) {
660
661       vcdinfo_visit_lot (p_vcd->vcd, VLC_FALSE);
662
663 #if FIXED
664     /*
665        We need to change libvcdinfo to be more robust when there are
666        problems reading the extended PSD. Given that area-highlighting and
667        selection features in the extended PSD haven't been implemented,
668        it's best then to not try to read this at all.
669      */
670       if (vcdinfo_get_psd_x_size(p_vcd->vcd))
671         vcdinfo_visit_lot (p_vcd->vcd, VLC_TRUE);
672 #endif
673     }
674
675     /* Set up LIDs Navigation Menu */
676     t = vlc_input_title_New();
677     t->b_menu = VLC_TRUE;
678     t->psz_name = strdup( "LIDs" );
679
680     i_title = p_vcd->i_tracks;
681     for( i_lid =  1 ; i_lid <=  p_vcd->i_lids ; i_lid++ )
682     {
683         char psz_lid[100];
684         seekpoint_t *s = vlc_seekpoint_New();
685             
686         snprintf( psz_lid, sizeof(psz_lid), "%s%02d", _("LID "), 
687                   i_lid );
688         
689         s->i_byte_offset = 0; /*  A lid doesn't have an offset
690                                   size associated with it */
691         s->psz_name  = strdup(psz_lid);
692         TAB_APPEND( t->i_seekpoint, t->seekpoint, s );
693         
694     }
695
696 #if DYNAMICALLY_ALLOCATED
697     TAB_APPEND( p_vcd->i_titles, p_vcd->p_title, t );
698 #else 
699     p_vcd->p_title[p_vcd->i_titles] = t;
700     p_vcd->i_titles++;
701 #endif
702
703     return VLC_TRUE;
704 }
705
706 /*****************************************************************************
707  * VCDParse: parse command line
708  *****************************************************************************/
709 static char *
710 VCDParse( access_t * p_access, /*out*/ vcdinfo_itemid_t * p_itemid,
711           /*out*/ vlc_bool_t *play_single_item )
712 {
713     vcdplayer_t *p_vcd = (vcdplayer_t *)p_access->p_sys;
714     char        *psz_parser;
715     char        *psz_source;
716     char        *psz_next;
717
718     if( config_GetInt( p_access, MODULE_STRING "-PBC" ) ) {
719       p_itemid->type = VCDINFO_ITEM_TYPE_LID;
720       p_itemid->num = 1;
721       *play_single_item = VLC_FALSE;
722     }
723     else 
724     {
725       p_itemid->type = VCDINFO_ITEM_TYPE_ENTRY;
726       p_itemid->num = 0;
727     }
728
729 #ifdef WIN32
730     /* On Win32 we want the VCD access plugin to be explicitly requested,
731      * we end up with lots of problems otherwise */
732     if( !p_access->psz_access || !*p_access->psz_access ) return NULL;
733 #endif
734
735     if( !p_access->psz_path )
736     {
737         return NULL;
738     }
739
740     psz_parser = psz_source = strdup( p_access->psz_path );
741
742     /* Parse input string :
743      * [device][@[type][title]] */
744     while( *psz_parser && *psz_parser != '@' )
745     {
746         psz_parser++;
747     }
748
749     if( *psz_parser == '@' )
750     {
751       /* Found the divide between the source name and the
752          type+entry number. */
753       unsigned int num;
754
755       *psz_parser = '\0';
756       ++psz_parser;
757       if( *psz_parser )
758         {
759           switch(*psz_parser) {
760           case 'E':
761             p_itemid->type = VCDINFO_ITEM_TYPE_ENTRY;
762             ++psz_parser;
763             *play_single_item = VLC_TRUE;
764             break;
765           case 'P':
766             p_itemid->type = VCDINFO_ITEM_TYPE_LID;
767             ++psz_parser;
768             *play_single_item = VLC_FALSE;
769             break;
770           case 'S':
771             p_itemid->type = VCDINFO_ITEM_TYPE_SEGMENT;
772             ++psz_parser;
773             *play_single_item = VLC_TRUE;
774             break;
775           case 'T':
776             p_itemid->type = VCDINFO_ITEM_TYPE_TRACK;
777             ++psz_parser;
778             *play_single_item = VLC_TRUE;
779             break;
780           default: ;
781           }
782         }
783
784       num = strtol( psz_parser, &psz_next, 10 );
785       if ( *psz_parser != '\0' && *psz_next == '\0')
786         {
787           p_itemid->num = num;
788         }
789
790     } else {
791       *play_single_item = ( VCDINFO_ITEM_TYPE_LID == p_itemid->type );
792     }
793
794
795     if( !*psz_source ) {
796
797       /* No source specified, so figure it out. */
798       if( !p_access->psz_access ) return NULL;
799
800       psz_source = config_GetPsz( p_access, "vcd" );
801
802       if( !psz_source || 0==strlen(psz_source) ) {
803         /* Scan for a CD-ROM drive with a VCD in it. */
804         char **cd_drives = cdio_get_devices_with_cap( NULL,
805                             ( CDIO_FS_ANAL_SVCD | CDIO_FS_ANAL_CVD
806                               |CDIO_FS_ANAL_VIDEOCD | CDIO_FS_UNKNOWN ),
807                                                      VLC_TRUE );
808         if( NULL == cd_drives ) return NULL;
809         if( cd_drives[0] == NULL )
810         {
811           cdio_free_device_list( cd_drives );
812           return NULL;
813         }
814         psz_source = strdup( cd_drives[0] );
815         cdio_free_device_list( cd_drives );
816       }
817     }
818
819     dbg_print( (INPUT_DBG_CALL|INPUT_DBG_MRL),
820                "source=%s entry=%d type=%d",
821                psz_source, p_itemid->num, p_itemid->type);
822
823     return psz_source;
824 }
825
826 /*
827    Set's start origin subsequent seeks/reads
828 */
829 static void
830 VCDSetOrigin( access_t *p_access, lsn_t i_lsn, track_t i_track, 
831               const vcdinfo_itemid_t *p_itemid )
832 {
833   vcdplayer_t *p_vcd= (vcdplayer_t *)p_access->p_sys;
834   
835   unsigned int i_title = i_track - 1; /* For now */
836
837   dbg_print( (INPUT_DBG_CALL|INPUT_DBG_LSN),
838              "i_lsn: %lu, track: %d", (long unsigned int) i_lsn, 
839              i_track );
840
841   p_vcd->i_lsn      = i_lsn;
842   p_vcd->i_track    = i_track;
843   p_vcd->track_lsn  = vcdinfo_get_track_lsn(p_vcd->vcd, i_track);
844
845   p_vcd->play_item.num  = p_itemid->num;
846   p_vcd->play_item.type = p_itemid->type;
847
848   vcdplayer_set_origin(p_access);
849
850   p_access->info.i_pos       = ( i_lsn - p_vcd->track_lsn ) 
851                              * M2F2_SECTOR_SIZE;
852   p_access->info.i_update   |= INPUT_UPDATE_TITLE|INPUT_UPDATE_SIZE
853                             |  INPUT_UPDATE_SEEKPOINT;
854
855   if (p_itemid->type == VCDINFO_ITEM_TYPE_ENTRY) {
856     VCDUpdateVar( p_access, p_itemid->num, VLC_VAR_SETVALUE,
857                   "chapter", _("Entry"), "Setting entry/segment");
858     p_access->info.i_title     = i_track-1;
859     p_access->info.i_size      = p_vcd->p_title[i_title]->i_size;
860     p_access->info.i_seekpoint = p_itemid->num;
861   } else if (p_itemid->type == VCDINFO_ITEM_TYPE_SEGMENT) {
862     VCDUpdateVar( p_access, p_itemid->num, VLC_VAR_SETVALUE,
863                   "chapter", _("Segment"),  "Setting entry/segment");
864     /* The last title entry is the for segments (when segments exist
865        and they must here. The segment seekpoints are stored after
866        the entry seekpoints and (zeroed) lid seekpoints. 
867     */
868     p_access->info.i_title     = p_vcd->i_titles - 1;
869     p_access->info.i_size      = 150 * M2F2_SECTOR_SIZE;
870     p_access->info.i_seekpoint = p_vcd->i_entries 
871                                + p_vcd->i_lids + p_itemid->num;
872
873   }
874
875   VCDUpdateTitle( p_access );
876   
877 }
878
879 /*****************************************************************************
880  * vcd_Open: Opens a VCD device or file initializes, a list of 
881    tracks, segements and entry lsns and sizes and returns an opaque handle.
882  *****************************************************************************/
883 static vcdinfo_obj_t *
884 vcd_Open( vlc_object_t *p_this, const char *psz_dev )
885 {
886     access_t    *p_access = (access_t *)p_this;
887     vcdplayer_t *p_vcd    = (vcdplayer_t *) p_access->p_sys;
888     vcdinfo_obj_t *p_vcdobj;
889     char  *actual_dev;
890     unsigned int i;
891
892     dbg_print(INPUT_DBG_CALL, "called with %s\n", psz_dev);
893
894     if( !psz_dev ) return NULL;
895
896     actual_dev=strdup(psz_dev);
897     if ( vcdinfo_open(&p_vcdobj, &actual_dev, DRIVER_UNKNOWN, NULL) !=
898          VCDINFO_OPEN_VCD) {
899       free(actual_dev);
900       return NULL;
901     }
902     free(actual_dev);
903
904     /* 
905        Save summary info on tracks, segments and entries... 
906     */
907     
908     if ( 0 < (p_vcd->i_tracks = vcdinfo_get_num_tracks(p_vcdobj)) ) {
909       p_vcd->track = (vcdplayer_play_item_info_t *) 
910         calloc(p_vcd->i_tracks, sizeof(vcdplayer_play_item_info_t));
911       
912       for (i=0; i<p_vcd->i_tracks; i++) { 
913         unsigned int track_num=i+1;
914         p_vcd->track[i].size  = 
915           vcdinfo_get_track_sect_count(p_vcdobj, track_num);
916         p_vcd->track[i].start_LSN = 
917           vcdinfo_get_track_lsn(p_vcdobj, track_num);
918       }
919     } else 
920       p_vcd->track = NULL;
921     
922     if ( 0 < (p_vcd->i_entries = vcdinfo_get_num_entries(p_vcdobj)) ) {
923       p_vcd->entry = (vcdplayer_play_item_info_t *) 
924         calloc(p_vcd->i_entries, sizeof(vcdplayer_play_item_info_t));
925       
926       for (i=0; i<p_vcd->i_entries; i++) { 
927         p_vcd->entry[i].size      = vcdinfo_get_entry_sect_count(p_vcdobj, i);
928         p_vcd->entry[i].start_LSN = vcdinfo_get_entry_lba(p_vcdobj, i);
929       }
930     } else 
931       p_vcd->entry = NULL;
932     
933     if ( 0 < (p_vcd->i_segments = vcdinfo_get_num_segments(p_vcdobj)) ) {
934       p_vcd->segment = (vcdplayer_play_item_info_t *) 
935         calloc(p_vcd->i_segments,  sizeof(vcdplayer_play_item_info_t));
936       
937       for (i=0; i<p_vcd->i_segments; i++) { 
938         p_vcd->segment[i].size = vcdinfo_get_seg_sector_count(p_vcdobj, i);
939         p_vcd->segment[i].start_LSN = vcdinfo_get_seg_lsn(p_vcdobj, i);
940       }
941     } else 
942       p_vcd->segment = NULL;
943     
944     
945     return p_vcdobj;
946 }
947
948 /****************************************************************************
949  * VCDReadSector: Read a sector (2324 bytes)
950  ****************************************************************************/
951 static int
952 VCDReadSector( vlc_object_t *p_this, const vcdinfo_obj_t *p_vcd,
953                lsn_t i_lsn, byte_t * p_buffer )
954 {
955   typedef struct {
956     uint8_t subheader   [CDIO_CD_SUBHEADER_SIZE];
957     uint8_t data        [M2F2_SECTOR_SIZE];
958     uint8_t spare       [4];
959   } vcdsector_t;
960   vcdsector_t vcd_sector;
961
962   if( cdio_read_mode2_sector( vcdinfo_get_cd_image( p_vcd ),
963                               &vcd_sector, i_lsn, VLC_TRUE )
964       != 0)
965   {
966       msg_Warn( p_this, "Could not read LSN %lu", 
967                 (long unsigned int) i_lsn );
968       return -1;
969   }
970
971   memcpy (p_buffer, vcd_sector.data, M2F2_SECTOR_SIZE);
972
973   return( 0 );
974 }
975
976 /****************************************************************************
977  Update the "varname" variable to i_num without triggering a callback.
978 ****************************************************************************/
979 static void
980 VCDUpdateVar( access_t *p_access, int i_num, int i_action,
981               const char *p_varname, char *p_label, 
982               const char *p_debug_label)
983 {
984   vlc_value_t val;
985   val.i_int = i_num;
986   if (p_access) {
987     const vcdplayer_t *p_vcd = (vcdplayer_t *)p_vcd_access->p_sys;
988     dbg_print( INPUT_DBG_PBC, "%s %d", p_debug_label, i_num );
989   }
990   if (p_label) {
991     vlc_value_t text;
992     text.psz_string = p_label;
993     var_Change( p_access, p_varname, VLC_VAR_SETTEXT, &text, NULL );
994   }
995   var_Change( p_access, p_varname, i_action, &val, NULL );
996 }
997
998
999 /*****************************************************************************
1000  * Public routines.
1001  *****************************************************************************/
1002
1003 /*****************************************************************************
1004   VCDOpen: open VCD.
1005   read in meta-information about VCD: the number of tracks, segments,
1006   entries, size and starting information. Then set up state variables so
1007   that we read/seek starting at the location specified.
1008
1009   On success we return VLC_SUCCESS, on memory exhausted VLC_ENOMEM,
1010   and VLC_EGENERIC for some other error.
1011  *****************************************************************************/
1012 int
1013 E_(VCDOpen) ( vlc_object_t *p_this )
1014 {
1015     access_t         *p_access = (access_t *)p_this;
1016     vcdplayer_t      *p_vcd;
1017     char             *psz_source;
1018     vcdinfo_itemid_t  itemid;
1019     vlc_bool_t        b_play_ok;
1020     vlc_bool_t        play_single_item = VLC_FALSE;
1021
1022     p_access->pf_read          = NULL;
1023     p_access->pf_block         = VCDReadBlock; 
1024     p_access->pf_control       = VCDControl;
1025     p_access->pf_seek          = VCDSeek;
1026
1027     p_access->info.i_update    = 0;
1028     p_access->info.i_size      = 0;
1029     p_access->info.i_pos       = 0;
1030     p_access->info.b_eof       = VLC_FALSE;
1031     p_access->info.i_title     = 0;
1032     p_access->info.i_seekpoint = 0;
1033
1034     p_vcd = malloc( sizeof(vcdplayer_t) );
1035
1036     if( p_vcd == NULL )
1037     {
1038         LOG_ERR ("out of memory" );
1039         return VLC_ENOMEM;
1040     }
1041
1042     p_access->p_sys     = (access_sys_t *) p_vcd;
1043
1044     /* Set where to log errors messages from libcdio. */
1045     p_vcd_access = p_access;
1046     cdio_log_set_handler ( cdio_log_handler );
1047     vcd_log_set_handler ( vcd_log_handler );
1048
1049     psz_source = VCDParse( p_access, &itemid, &play_single_item );
1050
1051     if ( NULL == psz_source )
1052     {
1053       free( p_vcd );
1054       return( VLC_EGENERIC );
1055     }
1056
1057     dbg_print( (INPUT_DBG_CALL|INPUT_DBG_EXT), "source: %s: mrl: %s",
1058                psz_source, p_access->psz_path );
1059
1060     p_vcd->psz_source        = strdup(psz_source);
1061     p_vcd->i_debug           = config_GetInt( p_this, 
1062                                               MODULE_STRING "-debug" );
1063     p_vcd->i_blocks_per_read = config_GetInt( p_this, MODULE_STRING 
1064                                               "-blocks-per-read" );
1065     p_vcd->in_still          = VLC_FALSE;
1066     p_vcd->play_item.type    = VCDINFO_ITEM_TYPE_NOTFOUND;
1067     p_vcd->p_input           = vlc_object_find( p_access, VLC_OBJECT_INPUT, 
1068                                               FIND_PARENT );
1069     p_vcd->p_meta            = vlc_meta_New();
1070     p_vcd->p_segments        = NULL;
1071     p_vcd->p_entries         = NULL;
1072
1073     /* set up input  */
1074
1075     if( !(p_vcd->vcd = vcd_Open( p_this, psz_source )) )
1076     {
1077         msg_Warn( p_access, "could not open %s", psz_source );
1078         goto err_exit;
1079     }
1080
1081     p_vcd->b_svd= (vlc_bool_t) vcdinfo_get_tracksSVD(p_vcd->vcd);;
1082     
1083     /* Get track information. */
1084     p_vcd->i_tracks = vcdinfo_get_num_tracks(p_vcd->vcd);
1085
1086     if( p_vcd->i_tracks < 1 || CDIO_INVALID_TRACK == p_vcd->i_tracks ) {
1087         vcdinfo_close( p_vcd->vcd );
1088         LOG_ERR ("no movie tracks found" );
1089         goto err_exit;
1090     }
1091     
1092     /* Build Navigation Title table for the tracks. */
1093     VCDTitles( p_access );
1094
1095     /* Add into the above entry points as "Chapters". */
1096     if( ! VCDEntryPoints( p_access ) )
1097     {
1098         msg_Warn( p_access, "could not read entry points, will not use them" );
1099         p_vcd->b_valid_ep = VLC_FALSE;
1100     }
1101
1102     /* Initialize LID info and add that as a menu item */
1103     if( ! VCDLIDs( p_access ) )
1104     {
1105         msg_Warn( p_access, "could not read entry LIDs" );
1106     }
1107
1108     /* Initialize segment information and add that a "Track". */
1109     VCDSegments( p_access );
1110
1111     b_play_ok = (VLC_SUCCESS == VCDPlay( p_access, itemid ));
1112
1113     if ( ! b_play_ok ) {
1114       vcdinfo_close( p_vcd->vcd );
1115       goto err_exit;
1116     }
1117
1118     p_access->psz_demux = strdup( "ps" );
1119
1120 #if FIXED
1121     p_vcd->p_intf = intf_Create( p_access, "vcdx" );
1122     p_vcd->p_intf->b_block = VLC_FALSE;
1123     intf_RunThread( p_vcd->p_intf );
1124 #endif
1125
1126 #if FIXED
1127     if (play_single_item)
1128       VCDFixupPlayList( p_access, p_vcd, psz_source, &itemid, 
1129                         play_single_item );
1130 #endif
1131     
1132
1133     free( psz_source );
1134
1135     return VLC_SUCCESS;
1136  err_exit:
1137     free( psz_source );
1138     free( p_vcd );
1139     return VLC_EGENERIC;
1140 }
1141
1142 /*****************************************************************************
1143  * VCDClose: closes VCD releasing allocated memory.
1144  *****************************************************************************/
1145 void
1146 E_(VCDClose) ( vlc_object_t *p_this )
1147 {
1148     access_t    *p_access = (access_t *)p_this;
1149     vcdplayer_t *p_vcd = (vcdplayer_t *)p_access->p_sys;
1150
1151     dbg_print( (INPUT_DBG_CALL|INPUT_DBG_EXT), "VCDClose" );
1152
1153     vcdinfo_close( p_vcd->vcd );
1154
1155     FREE_AND_NULL( p_vcd->p_entries );
1156     FREE_AND_NULL( p_vcd->p_segments );
1157     FREE_AND_NULL( p_vcd->psz_source );
1158     FREE_AND_NULL( p_vcd->track );
1159     FREE_AND_NULL( p_vcd->segment );
1160     FREE_AND_NULL( p_vcd->entry ); 
1161
1162     free( p_vcd );
1163     p_access->p_sys = NULL;
1164     p_vcd_access    = NULL;
1165 }
1166
1167 /*****************************************************************************
1168  * Control: The front-end or vlc engine calls here to ether get
1169  * information such as meta information or plugin capabilities or to
1170  * issue miscellaneous "set" requests.
1171  *****************************************************************************/
1172 static int VCDControl( access_t *p_access, int i_query, va_list args )
1173 {
1174     vcdplayer_t *p_vcd= (vcdplayer_t *)p_access->p_sys;
1175     int         *pi_int;
1176     int i;
1177
1178     dbg_print( (INPUT_DBG_CALL|INPUT_DBG_EXT|INPUT_DBG_EVENT),
1179                "query %d", i_query );
1180
1181     switch( i_query )
1182     {
1183         /* Pass back a copy of meta information that was gathered when we
1184            during the Open/Initialize call.
1185          */
1186         case ACCESS_GET_META:
1187           { 
1188             vlc_meta_t **pp_meta = (vlc_meta_t**)va_arg( args, vlc_meta_t** );
1189
1190             dbg_print( INPUT_DBG_EVENT, "get meta info" );
1191
1192             if ( p_vcd->p_meta ) {
1193               *pp_meta = vlc_meta_Duplicate( p_vcd->p_meta );
1194               dbg_print( INPUT_DBG_META, "%s", "Meta copied" );
1195             } else 
1196               msg_Warn( p_access, "tried to copy NULL meta info" );
1197             
1198             return VLC_SUCCESS;
1199           }
1200           return VLC_EGENERIC;
1201
1202         case ACCESS_CAN_SEEK:
1203         case ACCESS_CAN_FASTSEEK:
1204         case ACCESS_CAN_PAUSE:
1205         case ACCESS_CAN_CONTROL_PACE: 
1206           {
1207             vlc_bool_t *pb_bool = (vlc_bool_t*)va_arg( args, vlc_bool_t* );
1208
1209             dbg_print( INPUT_DBG_EVENT, 
1210                        "seek/fastseek/pause/can_control_pace" );
1211             *pb_bool = VLC_TRUE;
1212             return VLC_SUCCESS;
1213             break;
1214           }
1215
1216         /* */
1217         case ACCESS_GET_MTU:
1218             pi_int = (int*)va_arg( args, int * );
1219             *pi_int = (p_vcd->i_blocks_per_read * M2F2_SECTOR_SIZE);
1220             dbg_print( INPUT_DBG_EVENT, "GET MTU: %d", *pi_int );
1221             break;
1222
1223         case ACCESS_GET_PTS_DELAY:
1224           { 
1225             int64_t *pi_64 = (int64_t*)va_arg( args, int64_t * );
1226             *pi_64 = var_GetInteger( p_access, MODULE_STRING "-caching" )
1227               * MILLISECONDS_PER_SEC;
1228             dbg_print( INPUT_DBG_EVENT, "GET PTS DELAY" );
1229             return VLC_SUCCESS;
1230             break;
1231           }
1232
1233         /* */
1234         case ACCESS_SET_PAUSE_STATE:
1235             dbg_print( INPUT_DBG_EVENT, "SET PAUSE STATE" );
1236             return VLC_SUCCESS;
1237             break;
1238
1239         case ACCESS_GET_TITLE_INFO:
1240           { 
1241             unsigned int psz_mrl_max = strlen(VCD_MRL_PREFIX) 
1242               + strlen(p_vcd->psz_source) + sizeof("@E999")+3;
1243             input_title_t ***ppp_title
1244               = (input_title_t***)va_arg( args, input_title_t*** );
1245             char *psz_mrl = malloc( psz_mrl_max );
1246             unsigned int i;
1247
1248             pi_int    = (int*)va_arg( args, int* );
1249
1250             dbg_print( INPUT_DBG_EVENT, "GET TITLE: i_titles %d", 
1251                        p_vcd->i_titles );
1252
1253             if( psz_mrl == NULL ) {
1254                msg_Warn( p_access, "out of memory" );
1255             } else {
1256                snprintf(psz_mrl, psz_mrl_max, "%s%s",
1257                         VCD_MRL_PREFIX, p_vcd->psz_source);
1258                VCDMetaInfo( p_access, psz_mrl );
1259                free(psz_mrl);
1260             }
1261
1262             /* Duplicate title info */
1263             if( p_vcd->i_titles == 0 )
1264             {
1265                 *pi_int = 0; ppp_title = NULL;
1266                 return VLC_SUCCESS;
1267             }
1268             *pi_int = p_vcd->i_titles;
1269             *ppp_title = malloc(sizeof( input_title_t **) * p_vcd->i_titles );
1270
1271             if (!*ppp_title) return VLC_ENOMEM;
1272
1273             for( i = 0; i < p_vcd->i_titles; i++ )
1274             {
1275                 if ( p_vcd->p_title[i] )
1276                   (*ppp_title)[i] = 
1277                     vlc_input_title_Duplicate( p_vcd->p_title[i] );
1278             }
1279           }
1280           break;
1281
1282         case ACCESS_SET_TITLE:
1283             i = (int)va_arg( args, int );
1284
1285             dbg_print( INPUT_DBG_EVENT, "set title %d" , i);
1286             if( i != p_access->info.i_title )
1287             {
1288                 vcdinfo_itemid_t itemid;
1289                 track_t          i_track = i+1;
1290                 unsigned int     i_entry = 
1291                   vcdinfo_track_get_entry( p_vcd->vcd, i_track);
1292
1293                 /* FIXME! For now we are assuming titles are only 
1294                  tracks and that track == title+1 */
1295                 itemid.num = i_track;
1296                 itemid.type = VCDINFO_ITEM_TYPE_TRACK;
1297                 
1298                 VCDSetOrigin(p_access, 
1299                              vcdinfo_get_entry_lba(p_vcd->vcd, i_entry),
1300                              i_track, &itemid );
1301             }
1302             break;
1303
1304         case ACCESS_SET_SEEKPOINT:
1305         {
1306             input_title_t *t = p_vcd->p_title[p_access->info.i_title];
1307             unsigned int i = (unsigned int)va_arg( args, unsigned int );
1308
1309             dbg_print( INPUT_DBG_EVENT, "set seekpoint %d", i );
1310             if( t->i_seekpoint > 0 )
1311             {
1312                 track_t i_track = p_access->info.i_title+1;
1313                 lsn_t lsn;
1314                 
1315                 /* FIXME! For now we are assuming titles are only 
1316                  tracks and that track == title+1 and we the play
1317                  item is entries (not tracks or lids).
1318                  We need to generalize all of this.
1319                 */
1320
1321                 if (i < p_vcd->i_entries) 
1322                 {
1323                     p_vcd->play_item.num  = i;
1324                     p_vcd->play_item.type = VCDINFO_ITEM_TYPE_ENTRY;
1325                     lsn = vcdinfo_get_entry_lba(p_vcd->vcd, i);
1326                 } else if ( i < p_vcd->i_entries + p_vcd->i_lids ) 
1327                 {
1328                     p_vcd->play_item.num  = i = i - p_vcd->i_entries;
1329                     p_vcd->play_item.type = VCDINFO_ITEM_TYPE_LID;
1330                     lsn = 0;
1331                 } else 
1332                 {
1333                     p_vcd->play_item.num  = i = i - p_vcd->i_entries 
1334                       - p_vcd->i_lids;
1335                     p_vcd->play_item.type = VCDINFO_ITEM_TYPE_SEGMENT;
1336                     lsn = vcdinfo_get_seg_lsn(p_vcd->vcd, i);
1337                 }
1338                 
1339                 VCDSetOrigin( p_access, 
1340                               vcdinfo_get_entry_lba(p_vcd->vcd, i),
1341                               i_track, &(p_vcd->play_item) );
1342             }
1343             return VLC_SUCCESS;
1344         }
1345
1346         case ACCESS_SET_PRIVATE_ID_STATE:
1347             dbg_print( INPUT_DBG_EVENT, "set private id" );
1348             return VLC_EGENERIC;
1349
1350         default:
1351           msg_Warn( p_access, "unimplemented query in control" );
1352             return VLC_EGENERIC;
1353
1354     }
1355     return VLC_SUCCESS;
1356 }