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