]> git.sesse.net Git - vlc/blob - modules/access/vcdx/access.c
Fix bad bug with was causing discontinuous video playback.
[vlc] / modules / access / vcdx / access.c
1 /*****************************************************************************
2  * vcd.c : VCD input module for vlc
3  *         using libcdio, libvcd and libvcdinfo. vlc-specific things tend
4  *         to go here.
5  *****************************************************************************
6  * Copyright (C) 2000, 2003, 2004 VideoLAN
7  * $Id$
8  *
9  * Authors: Rocky Bernstein <rocky@panix.com>
10  *   Some code is based on the non-libcdio VCD plugin (as there really
11  *   isn't real documentation yet.)
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_index;
158     int                     i_read;
159     byte_t                  p_last_sector[ M2F2_SECTOR_SIZE ];
160
161     i_read = 0;
162
163     dbg_print( (INPUT_DBG_CALL), "lsn: %lu", 
164                (long unsigned int) p_vcd->i_lsn );
165
166     /* Compute the number of blocks we have to read */
167
168     i_blocks = VCD_BLOCKS_ONCE ;
169
170     /* Allocate a block for the reading */
171     if( !( p_block = block_New( p_access, i_blocks * M2F2_SECTOR_SIZE ) ) )
172     {
173         msg_Err( p_access, "cannot get a new block of size: %i",
174                  i_blocks * M2F2_SECTOR_SIZE );
175         block_Release( p_block );
176         return NULL;
177     }
178
179     for ( i_index = 0 ; i_index < i_blocks ; i_index++ )
180     {
181
182       if ( p_vcd->i_lsn == p_vcd->end_lsn ) {
183         vcdplayer_read_status_t read_status;
184
185         /* We've run off of the end of this entry. Do we continue or stop? */
186         dbg_print( (INPUT_DBG_LSN|INPUT_DBG_PBC),
187                    "end reached, cur: %lu", 
188                    (long unsigned int) p_vcd->i_lsn );
189
190         read_status = vcdplayer_pbc_is_on( p_vcd )
191           ? vcdplayer_pbc_nav( p_access )
192           : vcdplayer_non_pbc_nav( p_access );
193
194         switch (read_status) {
195         case READ_END:
196           /* End reached. Return NULL to indicated this. */
197         case READ_ERROR:
198           /* Some sort of error. */
199           return NULL;
200
201         case READ_STILL_FRAME:
202           {
203             /* Reached the end of a still frame. */
204             byte_t * p_buf = (byte_t *) p_block->p_buffer;
205
206             p_buf += (i_index*M2F2_SECTOR_SIZE);
207             memset(p_buf, 0, M2F2_SECTOR_SIZE);
208             p_buf += 2;
209             *p_buf = 0x01;
210             dbg_print(INPUT_DBG_STILL, "Handled still event");
211
212             p_vcd->p_intf->p_sys->b_still = VLC_TRUE;
213             var_SetInteger( p_access, "state", PAUSE_S );
214
215             return p_block;
216           }
217         default:
218         case READ_BLOCK:
219           break;
220         }
221       }
222
223       if ( VCDReadSector( VLC_OBJECT(p_access), p_vcd->vcd,
224                           p_vcd->i_lsn,
225                           (byte_t *) p_block->p_buffer 
226                           + (i_index*M2F2_SECTOR_SIZE) ) < 0 )
227       {
228           LOG_ERR ("could not read sector %lu", 
229                    (long unsigned int) p_vcd->i_lsn );
230         /* Try to skip one sector (in case of bad sectors) */
231           p_vcd->i_lsn ++;
232           p_access->info.i_pos += M2F2_SECTOR_SIZE;
233           return NULL;
234       }
235
236       p_vcd->i_lsn ++;
237       p_access->info.i_pos += M2F2_SECTOR_SIZE;
238
239       /* Update seekpoint */
240       if ( VCDINFO_ITEM_TYPE_ENTRY == p_vcd->play_item.type )
241       {
242         const input_title_t *t = p_vcd->p_title[p_access->info.i_title];
243         
244         if( t->i_seekpoint > 0 &&
245             p_access->info.i_seekpoint + 1 < t->i_seekpoint &&
246             p_access->info.i_pos + i_read * M2F2_SECTOR_SIZE >=
247             t->seekpoint[p_access->info.i_seekpoint+1]->i_byte_offset )
248         {
249             const track_t i_track = p_vcd->i_track;
250             const unsigned int i_entry = ++p_vcd->play_item.num;
251             msg_Dbg( p_access, "seekpoint change" );
252             VCDSetOrigin( p_access, 
253                           vcdinfo_get_track_lsn(p_vcd->vcd, i_track),
254                           vcdinfo_get_entry_lsn(p_vcd->vcd, i_entry),
255                           vcdinfo_get_track_lsn(p_vcd->vcd, i_track+1),
256                           i_track, &(p_vcd->play_item) );
257         }
258       }
259     }
260
261     if ( i_index != i_blocks ) /* this should not happen */
262     {
263         if ( VCDReadSector( VLC_OBJECT(p_access), p_vcd->vcd,
264                             p_vcd->i_lsn, p_last_sector ) < 0 )
265         {
266             LOG_ERR ("could not read sector %lu", 
267                      (long unsigned int) p_vcd->i_lsn );
268         }
269         
270         p_vcd->i_lsn ++;
271         return NULL;
272     }
273
274     return p_block;
275 }
276
277
278 /****************************************************************************
279  * VCDSeek
280  ****************************************************************************/
281 int
282 VCDSeek( access_t * p_access, int64_t i_pos )
283 {
284     if (!p_access || !p_access->p_sys) return VLC_EGENERIC;
285     
286     {
287       access_vcd_data_t *p_vcd = 
288         (access_vcd_data_t *)p_vcd_access->p_sys;
289       const input_title_t *t = p_vcd->p_title[p_access->info.i_title];
290       int i_seekpoint;
291       unsigned int i_entry=VCDINFO_INVALID_ENTRY; 
292       
293       /* Next sector to read */
294       p_access->info.i_pos = i_pos;
295       p_vcd->i_lsn = (i_pos / (int64_t)M2F2_SECTOR_SIZE) +
296         p_vcd->origin_lsn;
297       
298       /* Find entry */
299       if( p_vcd->b_valid_ep )
300         {
301           for( i_entry = 0 ; i_entry < p_vcd->i_entries ; i_entry ++ )
302             {
303               if( p_vcd->i_lsn < p_vcd->p_entries[i_entry] )
304                 {
305                   VCDUpdateVar( p_access, i_entry, VLC_VAR_SETVALUE,
306                                 "chapter", _("Entry"), "Setting entry" );
307                   break;
308                 }
309             }
310           p_vcd->play_item.num  = i_entry;
311           p_vcd->play_item.type = VCDINFO_ITEM_TYPE_ENTRY;
312         }
313       
314       /* Find seekpoint */
315       for( i_seekpoint = 0; i_seekpoint < t->i_seekpoint; i_seekpoint++ )
316         {
317           if( i_seekpoint + 1 >= t->i_seekpoint ) break;
318           if( i_pos < t->seekpoint[i_seekpoint + 1]->i_byte_offset ) break;
319         }
320       
321       /* Update current seekpoint */
322       if( i_seekpoint != p_access->info.i_seekpoint )
323         {
324           msg_Dbg( p_access, "seekpoint change" );
325           p_access->info.i_update |= INPUT_UPDATE_SEEKPOINT;
326           p_access->info.i_seekpoint = i_seekpoint;
327         }
328
329       dbg_print( (INPUT_DBG_CALL|INPUT_DBG_EXT|INPUT_DBG_SEEK),
330                  "orig %lu, cur: %lu, offset: %lld, entry %d",
331                  (long unsigned int) p_vcd->origin_lsn, 
332                  (long unsigned int) p_vcd->i_lsn, i_pos,
333                  i_entry );
334       
335     }
336     return VLC_SUCCESS;
337     
338 }
339
340 /*****************************************************************************
341   VCDPlay: set up internal structures so seeking/reading places an item.
342   itemid: the thing to play.
343   user_entry: true if itemid is a user selection (rather than internally-
344   generated selection such as via PBC) in which case we may have to adjust
345   for differences in numbering.
346  *****************************************************************************/
347 int
348 VCDPlay( access_t *p_access, vcdinfo_itemid_t itemid )
349 {
350     if (!p_access || !p_access->p_sys) return VLC_EGENERIC;
351     
352     {
353       
354       access_vcd_data_t *p_vcd= (access_vcd_data_t *)p_access->p_sys;
355 #if 0
356       const vlc_bool_t   b_was_still = p_vcd->in_still;
357 #endif
358       
359       dbg_print(INPUT_DBG_CALL, "itemid.num: %d, itemid.type: %d\n",
360                 itemid.num, itemid.type);
361
362       switch (itemid.type) {
363       case VCDINFO_ITEM_TYPE_TRACK:
364         
365         {
366           track_t i_track = itemid.num;
367           unsigned int i_entry = 
368             vcdinfo_track_get_entry( p_vcd->vcd, i_track);
369           
370           /* Valid tracks go from 1...i_tracks-1, because track 0 is
371              unplayable. */
372         
373           if (i_track == 0 || i_track >= p_vcd->i_tracks) {
374             LOG_ERR ("Invalid track number %d", i_track );
375             return VLC_EGENERIC;
376           }
377           p_vcd->in_still  = VLC_FALSE;
378           VCDSetOrigin( p_access, 
379                         vcdinfo_get_track_lsn(p_vcd->vcd, i_track),
380                         vcdinfo_get_entry_lsn(p_vcd->vcd, i_entry),
381                         vcdinfo_get_track_lsn(p_vcd->vcd, i_track+1),
382                         i_track, &itemid );
383           break;
384         }
385         
386       case VCDINFO_ITEM_TYPE_SEGMENT:
387         {
388           int i_seg = itemid.num;
389           
390           /* Valid segments go from 0...i_segments-1. */
391           if (itemid.num >= p_vcd->i_segments) {
392             LOG_ERR ( "Invalid segment number: %d", i_seg );
393             return VLC_EGENERIC;
394           } else {
395             vcdinfo_video_segment_type_t segtype =
396               vcdinfo_get_video_type(p_vcd->vcd, i_seg);
397           
398             dbg_print(INPUT_DBG_PBC, "%s (%d), seg_num: %d",
399                       vcdinfo_video_type2str(p_vcd->vcd, i_seg),
400                       (int) segtype, itemid.num);
401             
402             switch (segtype)
403               {
404               case VCDINFO_FILES_VIDEO_NTSC_STILL:
405               case VCDINFO_FILES_VIDEO_NTSC_STILL2:
406               case VCDINFO_FILES_VIDEO_PAL_STILL:
407               case VCDINFO_FILES_VIDEO_PAL_STILL2:
408                 p_vcd->in_still = VLC_TRUE;
409                 break;
410               default:
411                 p_vcd->in_still = VLC_FALSE;
412               }
413             VCDSetOrigin( p_access, p_vcd->p_segments[i_seg],
414                           p_vcd->p_segments[i_seg], 
415                           p_vcd->p_segments[i_seg+1],
416                           0, &itemid );
417           }
418
419         }
420         break;
421         
422       case VCDINFO_ITEM_TYPE_LID:
423         /* LIDs go from 1..i_lids. */
424         if (itemid.num == 0 || itemid.num > p_vcd->i_lids) {
425           LOG_ERR ( "Invalid LID number: %d", itemid.num );
426           return VLC_EGENERIC;
427         } else {
428           p_vcd->i_lid = itemid.num;
429           vcdinfo_lid_get_pxd(p_vcd->vcd, &(p_vcd->pxd), itemid.num);
430           
431           switch (p_vcd->pxd.descriptor_type) {
432             
433           case PSD_TYPE_SELECTION_LIST:
434           case PSD_TYPE_EXT_SELECTION_LIST: {
435             vcdinfo_itemid_t trans_itemid;
436             uint16_t trans_itemid_num;
437             
438             if (p_vcd->pxd.psd == NULL) return VLC_EGENERIC;
439             trans_itemid_num  = vcdinf_psd_get_itemid(p_vcd->pxd.psd);
440             vcdinfo_classify_itemid(trans_itemid_num, &trans_itemid);
441             p_vcd->loop_count = 1;
442             p_vcd->loop_item  = trans_itemid;
443             return VCDPlay( p_access, trans_itemid );
444             break;
445           }
446             
447           case PSD_TYPE_PLAY_LIST: {
448             if (p_vcd->pxd.pld == NULL) return VLC_EGENERIC;
449             p_vcd->pdi = -1;
450             return vcdplayer_inc_play_item(p_access)
451               ? VLC_SUCCESS : VLC_EGENERIC;
452             break;
453           }
454             
455           case PSD_TYPE_END_LIST:
456           case PSD_TYPE_COMMAND_LIST:
457             
458           default:
459             ;
460           }
461         }
462         return VLC_EGENERIC;
463       case VCDINFO_ITEM_TYPE_ENTRY:
464         {
465           int i_entry = itemid.num;
466           
467           /* Entries go from 0..i_entries-1. */
468           if (itemid.num >= p_vcd->i_entries) {
469             LOG_ERR ("Invalid entry number: %d", i_entry );
470             return VLC_EGENERIC;
471           } else {
472             track_t i_track = vcdinfo_get_track(p_vcd->vcd,  i_entry);
473             p_vcd->in_still = VLC_FALSE;
474             VCDSetOrigin( p_access, 
475                           vcdinfo_get_entry_lsn(p_vcd->vcd, i_track),
476                           vcdinfo_get_entry_lsn(p_vcd->vcd, i_entry),
477                           vcdinfo_get_entry_lsn(p_vcd->vcd, i_track+1),
478                           i_track, &itemid );
479           }
480           break;
481         }
482         
483       default:
484         LOG_ERR ("unknown entry type" );
485         return VLC_EGENERIC;
486       }
487
488       p_vcd->play_item = itemid;
489       
490     }
491     
492       
493     return VLC_SUCCESS;
494 }
495
496 /*****************************************************************************
497   VCDEntryPoints: Reads the information about the entry points on the disc
498   and initializes area information with that.
499   Before calling this track information should have been read in.
500  *****************************************************************************/
501 static int
502 VCDEntryPoints( access_t * p_access )
503 {
504   if (!p_access || !p_access->p_sys) return VLC_EGENERIC;
505   
506   {
507     access_vcd_data_t *p_vcd = (access_vcd_data_t *) p_access->p_sys;
508     const unsigned int i_entries  =  vcdinfo_get_num_entries(p_vcd->vcd);
509     const track_t      i_last_track 
510       = cdio_get_num_tracks(vcdinfo_get_cd_image(p_vcd->vcd))
511       + cdio_get_first_track_num(vcdinfo_get_cd_image(p_vcd->vcd));
512     unsigned int i;
513    
514     if (0 == i_entries) {
515       LOG_ERR ("no entires found -- something is wrong" );
516       return VLC_EGENERIC;
517     }
518     
519     p_vcd->p_entries  = malloc( sizeof( lsn_t ) * i_entries );
520     
521     if( p_vcd->p_entries == NULL )
522       {
523         LOG_ERR ("not enough memory for entry points treatment" );
524         return VLC_EGENERIC;
525       }
526     
527     p_vcd->i_entries = i_entries;
528     
529     for( i = 0 ; i < i_entries ; i++ )
530     {
531         const track_t i_track = vcdinfo_get_track(p_vcd->vcd, i);
532         if( i_track <= i_last_track ) {
533           seekpoint_t *s = vlc_seekpoint_New();
534           char psz_entry[100];
535           
536           snprintf(psz_entry, sizeof(psz_entry), "%s%02d", _("Entry "), i );
537
538           p_vcd->p_entries[i] = vcdinfo_get_entry_lsn(p_vcd->vcd, i);
539           
540           s->psz_name      = strdup(psz_entry);
541           s->i_byte_offset = 
542             (p_vcd->p_entries[i] - vcdinfo_get_track_lsn(p_vcd->vcd, i_track))
543             * M2F2_SECTOR_SIZE;
544           
545           TAB_APPEND( p_vcd->p_title[i_track-1]->i_seekpoint,
546                       p_vcd->p_title[i_track-1]->seekpoint, s );
547         } else 
548           msg_Warn( p_access, "wrong track number found in entry points" );
549     }
550     p_vcd->b_valid_ep = VLC_TRUE;
551     return 0;
552   }
553 }
554
555 /*????? FIXME!!! */
556 #ifdef FIXED 
557 /*****************************************************************************
558  * VCDSegments: Reads the information about the segments the disc.
559  *****************************************************************************/
560 static int
561 VCDSegments( access_t * p_access )
562 {
563     access_vcd_data_t * p_vcd;
564     unsigned int        i;
565     unsigned int        i_segments;
566
567
568     p_vcd = (access_vcd_data_t *) p_access->p_sys;
569     i_segments = p_vcd->num_segments = vcdinfo_get_num_segments(p_vcd->vcd);
570
571 #define area p_access->stream.pp_areas
572
573     /* area 0 is reserved for segments. Set Absolute start offset
574          and size */
575     area[0]->i_plugin_data = 0;
576     input_DelArea( p_access, area[0] );
577     input_AddArea( p_access, 0, 0 );
578
579     area[0]->i_start = (off_t)p_vcd->p_sectors[0]
580       * (off_t)M2F2_SECTOR_SIZE;
581     area[0]->i_size = (off_t)(p_vcd->p_sectors[1] - p_vcd->p_sectors[0])
582       * (off_t)M2F2_SECTOR_SIZE;
583
584     /* Default Segment  */
585     area[0]->i_part = 0;
586
587     /* i_plugin_data is used to store which entry point is the first
588        of the track (area) */
589     area[0]->i_plugin_data = 0;
590
591     area[0]->i_part_nb = 0;
592
593     dbg_print( INPUT_DBG_MRL,
594                "area[0] id: %d, i_start: %lld, i_size: %lld",
595                area[0]->i_id, area[0]->i_start, area[0]->i_size );
596
597     if (i_segments == 0) return 0;
598
599     /* We have one additional segment allocated so we can get the size
600        by subtracting seg[i+1] - seg[i].
601      */
602     p_vcd->p_segments = malloc( sizeof( lsn_t ) * (i_segments+1) );
603     if( p_vcd->p_segments == NULL )
604     {
605         LOG_ERR ("not enough memory for segment treatment" );
606         return -1;
607     }
608
609     /* Update the navigation variables without triggering a callback */
610     VCDUpdateVar( p_access, 0, VLC_VAR_SETVALUE, "title", _("Track"),
611                   "Setting track" );
612
613     var_Change( p_access, "chapter", VLC_VAR_CLEARCHOICES, NULL, NULL );
614
615     for( i = 0 ; i < i_segments ; i++ )
616     {
617       p_vcd->p_segments[i] = vcdinfo_get_seg_lsn(p_vcd->vcd, i);
618       area[0]->i_part_nb ++;
619       VCDUpdateVar( p_access, i , VLC_VAR_ADDCHOICE,
620                     "chapter", _("Segment"), "Adding segment choice");
621     }
622
623 #undef area
624
625     p_vcd->p_segments[i_segments] = p_vcd->p_segments[i_segments-1]+
626       vcdinfo_get_seg_sector_count(p_vcd->vcd, i_segments-1);
627
628     return 0;
629 }
630 #endif
631
632 /*****************************************************************************
633  Build title table which will be returned via ACCESS_GET_TITLE_INFO.
634
635  We start area addressing for tracks at 1 since the default area 0
636  is reserved for segments. 
637  *****************************************************************************/
638 static int
639 VCDTitles( access_t * p_access )
640 {
641     /* We'll assume a VCD has its first MPEG track
642        cdio_get_first_track_num()+1 could be used if one wanted to be
643        very careful about this. Note: cdio_get_first_track() will give the
644        ISO-9660 track before the MPEG tracks.
645      */
646   
647     if (!p_access || !p_access->p_sys) return VLC_EGENERIC;
648
649     {
650         access_vcd_data_t *p_vcd = (access_vcd_data_t *) p_access->p_sys;
651         track_t            i;
652
653         p_vcd->i_titles = 0;
654         for( i = 1 ; i <= p_vcd->i_tracks ; i++ )
655         {
656             input_title_t *t = p_vcd->p_title[i-1] = vlc_input_title_New();
657             char psz_track[100];
658             uint32_t i_secsize = vcdinfo_get_track_sect_count( p_vcd->vcd, i );
659             
660             snprintf( psz_track, sizeof(psz_track), "%s%02d", _("Track "), 
661                       i );
662             
663             t->i_size    = (i_secsize) * (int64_t) M2F2_SECTOR_SIZE;
664             t->psz_name  = strdup(psz_track);
665             
666             dbg_print( INPUT_DBG_MRL, "track[%d] i_size: %lld",
667                        i, t->i_size );
668
669             p_vcd->i_titles++;
670         }
671       
672       return VLC_SUCCESS;
673     }
674 }
675
676 /*****************************************************************************
677   VCDLIDs: Reads the LIST IDs from the LOT.
678  *****************************************************************************/
679 static int
680 VCDLIDs( access_t * p_access )
681 {
682     access_vcd_data_t *p_vcd = (access_vcd_data_t *) p_access->p_sys;
683
684     p_vcd->i_lids = vcdinfo_get_num_LIDs(p_vcd->vcd);
685     p_vcd->i_lid  = VCDINFO_INVALID_ENTRY;
686
687     if (vcdinfo_read_psd (p_vcd->vcd)) {
688
689       vcdinfo_visit_lot (p_vcd->vcd, VLC_FALSE);
690
691 #if FIXED
692     /*
693        We need to change libvcdinfo to be more robust when there are
694        problems reading the extended PSD. Given that area-highlighting and
695        selection features in the extended PSD haven't been implemented,
696        it's best then to not try to read this at all.
697      */
698       if (vcdinfo_get_psd_x_size(p_vcd->vcd))
699         vcdinfo_visit_lot (p_vcd->vcd, VLC_TRUE);
700 #endif
701     }
702
703     dbg_print( (INPUT_DBG_CALL|INPUT_DBG_MRL),
704                "num LIDs=%d", p_vcd->i_lids);
705
706     return 0;
707 }
708
709 /*****************************************************************************
710  * VCDParse: parse command line
711  *****************************************************************************/
712 static char *
713 VCDParse( access_t * p_access, /*out*/ vcdinfo_itemid_t * p_itemid,
714           /*out*/ vlc_bool_t *play_single_item )
715 {
716     access_vcd_data_t *p_vcd = (access_vcd_data_t *)p_access->p_sys;
717     char *             psz_parser;
718     char *             psz_source;
719     char *             psz_next;
720
721     if( config_GetInt( p_access, MODULE_STRING "-PBC" ) ) {
722       p_itemid->type = VCDINFO_ITEM_TYPE_LID;
723       p_itemid->num = 1;
724       *play_single_item = VLC_FALSE;
725     }
726     else 
727     {
728       p_itemid->type = VCDINFO_ITEM_TYPE_ENTRY;
729       p_itemid->num = 0;
730     }
731
732 #ifdef WIN32
733     /* On Win32 we want the VCD access plugin to be explicitly requested,
734      * we end up with lots of problems otherwise */
735     if( !p_access->psz_access || !*p_access->psz_access ) return NULL;
736 #endif
737
738     if( !p_access->psz_path )
739     {
740         return NULL;
741     }
742
743     psz_parser = psz_source = strdup( p_access->psz_path );
744
745     /* Parse input string :
746      * [device][@[type][title]] */
747     while( *psz_parser && *psz_parser != '@' )
748     {
749         psz_parser++;
750     }
751
752     if( *psz_parser == '@' )
753     {
754       /* Found the divide between the source name and the
755          type+entry number. */
756       unsigned int num;
757
758       *psz_parser = '\0';
759       ++psz_parser;
760       if( *psz_parser )
761         {
762           switch(*psz_parser) {
763           case 'E':
764             p_itemid->type = VCDINFO_ITEM_TYPE_ENTRY;
765             ++psz_parser;
766             *play_single_item = VLC_TRUE;
767             break;
768           case 'P':
769             p_itemid->type = VCDINFO_ITEM_TYPE_LID;
770             ++psz_parser;
771             *play_single_item = VLC_FALSE;
772             break;
773           case 'S':
774             p_itemid->type = VCDINFO_ITEM_TYPE_SEGMENT;
775             ++psz_parser;
776             *play_single_item = VLC_TRUE;
777             break;
778           case 'T':
779             p_itemid->type = VCDINFO_ITEM_TYPE_TRACK;
780             ++psz_parser;
781             *play_single_item = VLC_TRUE;
782             break;
783           default: ;
784           }
785         }
786
787       num = strtol( psz_parser, &psz_next, 10 );
788       if ( *psz_parser != '\0' && *psz_next == '\0')
789         {
790           p_itemid->num = num;
791         }
792
793     } else {
794       *play_single_item = ( VCDINFO_ITEM_TYPE_LID == p_itemid->type );
795     }
796
797
798     if( !*psz_source ) {
799
800       /* No source specified, so figure it out. */
801       if( !p_access->psz_access ) return NULL;
802
803       psz_source = config_GetPsz( p_access, "vcd" );
804
805       if( !psz_source || 0==strlen(psz_source) ) {
806         /* Scan for a CD-ROM drive with a VCD in it. */
807         char **cd_drives = cdio_get_devices_with_cap( NULL,
808                             ( CDIO_FS_ANAL_SVCD | CDIO_FS_ANAL_CVD
809                               |CDIO_FS_ANAL_VIDEOCD | CDIO_FS_UNKNOWN ),
810                                                      VLC_TRUE );
811         if( NULL == cd_drives ) return NULL;
812         if( cd_drives[0] == NULL )
813         {
814           cdio_free_device_list( cd_drives );
815           return NULL;
816         }
817         psz_source = strdup( cd_drives[0] );
818         cdio_free_device_list( cd_drives );
819       }
820     }
821
822     dbg_print( (INPUT_DBG_CALL|INPUT_DBG_MRL),
823                "source=%s entry=%d type=%d",
824                psz_source, p_itemid->num, p_itemid->type);
825
826     return psz_source;
827 }
828
829 /*
830    Set's start origin subsequent seeks/reads
831 */
832 static void
833 VCDSetOrigin( access_t *p_access, lsn_t origin_lsn,
834               lsn_t i_lsn, lsn_t end_lsn, track_t i_track, 
835               const vcdinfo_itemid_t *p_itemid )
836 {
837   access_vcd_data_t *p_vcd= (access_vcd_data_t *)p_access->p_sys;
838
839   unsigned int i_title = i_track - 1; /* For now */
840
841   p_vcd->origin_lsn = origin_lsn;
842   p_vcd->i_lsn      = i_lsn;
843   p_vcd->end_lsn    = end_lsn;
844   p_vcd->i_track    = i_track;
845   p_vcd->play_item.num  = p_itemid->num;
846   p_vcd->play_item.type = p_itemid->type;
847
848   p_access->info.i_title     = i_track-1;
849   p_access->info.i_seekpoint = p_itemid->num;
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
1269             return VLC_SUCCESS;
1270           }
1271           break;
1272
1273         case ACCESS_SET_TITLE:
1274             i = (int)va_arg( args, int );
1275
1276             dbg_print( INPUT_DBG_EVENT, "set title %d" , i);
1277             if( i != p_access->info.i_title )
1278             {
1279                 vcdinfo_itemid_t itemid;
1280                 track_t          i_track = i+1;
1281                 unsigned int     i_entry = 
1282                   vcdinfo_track_get_entry( p_vcd->vcd, i_track);
1283
1284                 /* FIXME! For now we are assuming titles are only 
1285                  tracks and that track == title+1 */
1286                 itemid.num = i_track;
1287                 itemid.type = VCDINFO_ITEM_TYPE_TRACK;
1288                 
1289                 VCDSetOrigin(p_access, 
1290                              vcdinfo_get_track_lsn(p_vcd->vcd, i_track),
1291                              vcdinfo_get_entry_lsn(p_vcd->vcd, i_entry),
1292                              vcdinfo_get_track_lsn(p_vcd->vcd, i_track+1),
1293                              i_track, &itemid );
1294             }
1295             break;
1296
1297         case ACCESS_SET_SEEKPOINT:
1298         {
1299             input_title_t *t = p_vcd->p_title[p_access->info.i_title];
1300             i = (int)va_arg( args, int );
1301
1302             dbg_print( INPUT_DBG_EVENT, "set seekpoint" );
1303             if( t->i_seekpoint > 0 )
1304             {
1305                 track_t i_track = p_access->info.i_title+1;
1306                 lsn_t   track_lsn = vcdinfo_get_track_lsn(p_vcd->vcd, i_track);
1307
1308                 p_access->info.i_update |= INPUT_UPDATE_SEEKPOINT;
1309                 p_access->info.i_seekpoint = i;
1310
1311                 /* FIXME! For now we are assuming titles are only 
1312                  tracks and that track == title+1 */
1313
1314                 p_vcd->i_lsn = track_lsn 
1315                   + (t->seekpoint[i]->i_byte_offset / M2F2_SECTOR_SIZE);
1316
1317                 p_access->info.i_pos = (int64_t)(p_vcd->i_lsn - track_lsn)
1318                   * M2F2_SECTOR_SIZE;
1319             }
1320             return VLC_SUCCESS;
1321         }
1322
1323         case ACCESS_SET_PRIVATE_ID_STATE:
1324             dbg_print( INPUT_DBG_EVENT, "set private id" );
1325             return VLC_EGENERIC;
1326
1327         default:
1328           msg_Warn( p_access, "unimplemented query in control" );
1329             return VLC_EGENERIC;
1330
1331     }
1332     return VLC_SUCCESS;
1333 }