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