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