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