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