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