]> git.sesse.net Git - vlc/blob - modules/access/vcdx/access.c
fix next/previous chapter in disc mode
[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 VideoLAN
6  * $Id$
7  *
8  * Authors: Rocky Bernstein <rocky@panix.com>
9  *   Some code is based on the non-libcdio VCD plugin (as there really
10  *   isn't real developer documentation yet on how to write a
11  *   navigable plugin.)
12  *
13  * This program is free software; you can redistribute it and/or modify
14  * it under the terms of the GNU General Public License as published by
15  * the Free Software Foundation; either version 2 of the License, or
16  * (at your option) any later version.
17  *
18  * This program is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21  * GNU General Public License for more details.
22  *
23  * You should have received a copy of the GNU General Public License
24  * along with this program; if not, write to the Free Software
25  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
26  *****************************************************************************/
27
28 /*****************************************************************************
29  * Preamble
30  *****************************************************************************/
31
32 #include <vlc/vlc.h>
33 #include <vlc/intf.h>
34 #include <vlc/input.h>
35 #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) i_lsn * M2F2_SECTOR_SIZE;
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     FREE_AND_NULL( p_vcdplayer->p_entries );
1005     FREE_AND_NULL( p_vcdplayer->p_segments );
1006     FREE_AND_NULL( p_vcdplayer->psz_source );
1007     FREE_AND_NULL( p_vcdplayer->track );
1008     FREE_AND_NULL( p_vcdplayer->segment );
1009     FREE_AND_NULL( p_vcdplayer->entry );
1010     FREE_AND_NULL( p_access->psz_demux );
1011     FREE_AND_NULL( 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
1040             if ( p_vcdplayer->p_meta ) {
1041               *pp_meta = vlc_meta_Duplicate( p_vcdplayer->p_meta );
1042               dbg_print( INPUT_DBG_META, "%s", "Meta copied" );
1043             } else
1044               msg_Warn( p_access, "tried to copy NULL meta info" );
1045
1046             return VLC_SUCCESS;
1047           }
1048           return VLC_EGENERIC;
1049
1050         case ACCESS_CAN_SEEK:
1051         case ACCESS_CAN_FASTSEEK:
1052         case ACCESS_CAN_PAUSE:
1053         case ACCESS_CAN_CONTROL_PACE:
1054         {
1055             vlc_bool_t *pb_bool = (vlc_bool_t*)va_arg( args, vlc_bool_t* );
1056
1057             dbg_print( INPUT_DBG_EVENT,
1058                        "seek/fastseek/pause/can_control_pace" );
1059             *pb_bool = VLC_TRUE;
1060             return VLC_SUCCESS;
1061             break;
1062           }
1063
1064         /* */
1065         case ACCESS_GET_MTU:
1066             pi_int = (int*)va_arg( args, int * );
1067             *pi_int = (p_vcdplayer->i_blocks_per_read * M2F2_SECTOR_SIZE);
1068             dbg_print( INPUT_DBG_EVENT, "GET MTU: %d", *pi_int );
1069             break;
1070
1071         case ACCESS_GET_PTS_DELAY:
1072         {
1073             int64_t *pi_64 = (int64_t*)va_arg( args, int64_t * );
1074             *pi_64 = var_GetInteger( p_access, MODULE_STRING "-caching" )
1075               * MILLISECONDS_PER_SEC;
1076             dbg_print( INPUT_DBG_EVENT, "GET PTS DELAY" );
1077             return VLC_SUCCESS;
1078             break;
1079         }
1080
1081         /* */
1082         case ACCESS_SET_PAUSE_STATE:
1083             dbg_print( INPUT_DBG_EVENT, "SET PAUSE STATE" );
1084             return VLC_SUCCESS;
1085             break;
1086
1087         case ACCESS_GET_TITLE_INFO:
1088         {
1089             unsigned int psz_mrl_max = strlen(VCD_MRL_PREFIX)
1090               + strlen(p_vcdplayer->psz_source) + sizeof("@E999")+3;
1091             input_title_t ***ppp_title
1092               = (input_title_t***)va_arg( args, input_title_t*** );
1093             char *psz_mrl = malloc( psz_mrl_max );
1094             unsigned int i;
1095
1096             pi_int    = (int*)va_arg( args, int* );
1097
1098             dbg_print( INPUT_DBG_EVENT, "GET TITLE: i_titles %d",
1099                        p_vcdplayer->i_titles );
1100
1101             if( psz_mrl == NULL ) {
1102                msg_Warn( p_access, "out of memory" );
1103             } else {
1104                snprintf(psz_mrl, psz_mrl_max, "%s%s",
1105                         VCD_MRL_PREFIX, p_vcdplayer->psz_source);
1106                VCDMetaInfo( p_access, psz_mrl );
1107                free(psz_mrl);
1108             }
1109
1110             /* Duplicate title info */
1111             if( p_vcdplayer->i_titles == 0 )
1112             {
1113                 *pi_int = 0; ppp_title = NULL;
1114                 return VLC_SUCCESS;
1115             }
1116             *pi_int = p_vcdplayer->i_titles;
1117             *ppp_title = malloc( sizeof( input_title_t **)
1118                                          * p_vcdplayer->i_titles );
1119
1120             if (!*ppp_title) return VLC_ENOMEM;
1121
1122             for( i = 0; i < p_vcdplayer->i_titles; i++ )
1123             {
1124                 if ( p_vcdplayer->p_title[i] )
1125                   (*ppp_title)[i] =
1126                     vlc_input_title_Duplicate( p_vcdplayer->p_title[i] );
1127             }
1128           }
1129           break;
1130
1131         case ACCESS_SET_TITLE:
1132             i = (int)va_arg( args, int );
1133
1134             dbg_print( INPUT_DBG_EVENT, "set title %d" , i);
1135             if( i != p_access->info.i_title )
1136             {
1137                 vcdinfo_itemid_t itemid;
1138                 track_t          i_track = i+1;
1139                 unsigned int     i_entry =
1140                   vcdinfo_track_get_entry( p_vcdplayer->vcd, i_track);
1141
1142                 /* FIXME! For now we are assuming titles are only
1143                  tracks and that track == title+1 */
1144                 itemid.num = i_track;
1145                 itemid.type = VCDINFO_ITEM_TYPE_TRACK;
1146
1147                 VCDSetOrigin(p_access,
1148                      vcdinfo_get_entry_lsn(p_vcdplayer->vcd, i_entry),
1149                              i_track, &itemid );
1150             }
1151             break;
1152
1153         case ACCESS_SET_SEEKPOINT:
1154         {
1155             input_title_t *t = p_vcdplayer->p_title[p_access->info.i_title];
1156             unsigned int i = (unsigned int)va_arg( args, unsigned int );
1157
1158             dbg_print( INPUT_DBG_EVENT, "set seekpoint %d", i );
1159             if( t->i_seekpoint > 0 )
1160             {
1161                 track_t i_track = p_access->info.i_title+1;
1162                 lsn_t lsn;
1163
1164                 /* FIXME! For now we are assuming titles are only
1165                  tracks and that track == title+1 and we the play
1166                  item is entries (not tracks or lids).
1167                  We need to generalize all of this.
1168                 */
1169
1170                 if (i < p_vcdplayer->i_entries)
1171                 {
1172                     p_vcdplayer->play_item.num  = i;
1173                     p_vcdplayer->play_item.type = VCDINFO_ITEM_TYPE_ENTRY;
1174                     lsn = vcdinfo_get_entry_lsn(p_vcdplayer->vcd, i);
1175                 } else if ( i < p_vcdplayer->i_entries + p_vcdplayer->i_lids )
1176                 {
1177                     p_vcdplayer->play_item.num  = i
1178                       = i - p_vcdplayer->i_entries;
1179                     p_vcdplayer->play_item.type = VCDINFO_ITEM_TYPE_LID;
1180                     lsn = 0;
1181                 } else
1182                 {
1183                     p_vcdplayer->play_item.num  = i
1184                       = i - p_vcdplayer->i_entries - p_vcdplayer->i_lids;
1185                     p_vcdplayer->play_item.type = VCDINFO_ITEM_TYPE_SEGMENT;
1186                     lsn = vcdinfo_get_seg_lsn(p_vcdplayer->vcd, i);
1187                 }
1188
1189                 VCDSetOrigin( p_access,
1190                               vcdinfo_get_entry_lsn(p_vcdplayer->vcd, i),
1191                               i_track, &(p_vcdplayer->play_item) );
1192             }
1193             return VLC_SUCCESS;
1194         }
1195
1196         case ACCESS_SET_PRIVATE_ID_STATE:
1197             dbg_print( INPUT_DBG_EVENT, "set private id" );
1198             return VLC_EGENERIC;
1199
1200         default:
1201           msg_Warn( p_access, "unimplemented query in control" );
1202             return VLC_EGENERIC;
1203
1204     }
1205     return VLC_SUCCESS;
1206 }