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