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