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