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