]> git.sesse.net Git - vlc/blob - modules/access/vcdx/access.c
* vcdx Coding style fixes.
[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  *          Johan Bilien <jobi@via.ecp.fr>
11  *
12  * This program is free software; you can redistribute it and/or modify
13  * it under the terms of the GNU General Public License as published by
14  * the Free Software Foundation; either version 2 of the License, or
15  * (at your option) any later version.
16  *
17  * This program is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20  * GNU General Public License for more details.
21  *
22  * You should have received a copy of the GNU General Public License
23  * along with this program; if not, write to the Free Software
24  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
25  *****************************************************************************/
26
27 /*****************************************************************************
28  * Preamble
29  *****************************************************************************/
30
31 #include <vlc/vlc.h>
32 #include <vlc/input.h>
33 #include <vlc/intf.h>
34
35 #include "../../demux/mpeg/system.h"
36 #include "vcd.h"
37 #include "vcdplayer.h"
38 #include "intf.h"
39
40 #include <cdio/cdio.h>
41 #include <cdio/cd_types.h>
42 #include <cdio/logging.h>
43 #include <cdio/util.h>
44 #include <libvcd/info.h>
45 #include <libvcd/logging.h>
46
47 #include "cdrom.h"
48
49 /* how many blocks VCDRead will read in each loop */
50 #define VCD_BLOCKS_ONCE 20
51 #define VCD_DATA_ONCE   (VCD_BLOCKS_ONCE * M2F2_SECTOR_SIZE)
52
53 #define VCD_MRL_PREFIX "vcdx://"
54
55 /*****************************************************************************
56  * Local prototypes
57  *****************************************************************************/
58
59 /* First those which are accessed from outside (via pointers). */
60 static int  VCDRead         ( input_thread_t *, byte_t *, size_t );
61 static int  VCDSetProgram   ( input_thread_t *, pgrm_descriptor_t * );
62
63 /* Now those which are strictly internal */
64 static void VCDSetOrigin    ( input_thread_t *, lsn_t origin_lsn,
65                               lsn_t cur_lsn, lsn_t end_lsn,
66                               int cur_entry, track_t track );
67 static int  VCDEntryPoints  ( input_thread_t * );
68 static int  VCDLIDs         ( input_thread_t * );
69 static int  VCDSegments     ( input_thread_t * );
70 static void VCDTracks       ( input_thread_t * );
71 static int  VCDReadSector   ( vlc_object_t *p_this,
72                               const vcdinfo_obj_t *p_vcd, lsn_t cur_lsn,
73                               byte_t * p_buffer );
74 static char *VCDParse       ( input_thread_t *,
75                               /*out*/ vcdinfo_itemid_t * p_itemid ,
76                               /*out*/ vlc_bool_t *play_single_item );
77
78 static void VCDUpdateVar( input_thread_t *p_input, int i_entry, int i_action,
79                           const char *p_varname, char *p_label,
80                           const char *p_debug_label );
81
82 static vcdinfo_obj_t *vcd_Open   ( vlc_object_t *p_this, const char *psz_dev );
83
84 /****************************************************************************
85  * Private functions
86  ****************************************************************************/
87
88 /* FIXME: This variable is a hack. Would be nice to eliminate the
89    global-ness. */
90
91 static input_thread_t *p_vcd_input = NULL;
92
93 /* process messages that originate from libcdio. */
94 static void
95 cdio_log_handler (cdio_log_level_t level, const char message[])
96 {
97   thread_vcd_data_t *p_vcd = (thread_vcd_data_t *)p_vcd_input->p_access_data;
98   switch (level) {
99   case CDIO_LOG_DEBUG:
100   case CDIO_LOG_INFO:
101     if (p_vcd->i_debug & INPUT_DBG_CDIO)
102       msg_Dbg( p_vcd_input, message);
103     break;
104   case CDIO_LOG_WARN:
105     msg_Warn( p_vcd_input, message);
106     break;
107   case CDIO_LOG_ERROR:
108   case CDIO_LOG_ASSERT:
109     msg_Err( p_vcd_input, message);
110     break;
111   default:
112     msg_Warn( p_vcd_input, message,
113             _("The above message had unknown log level"),
114             level);
115   }
116   return;
117 }
118
119 /* process messages that originate from vcdinfo. */
120 static void
121 vcd_log_handler (vcd_log_level_t level, const char message[])
122 {
123   thread_vcd_data_t *p_vcd = (thread_vcd_data_t *)p_vcd_input->p_access_data;
124   switch (level) {
125   case VCD_LOG_DEBUG:
126   case VCD_LOG_INFO:
127     if (p_vcd->i_debug & INPUT_DBG_VCDINFO)
128       msg_Dbg( p_vcd_input, message);
129     break;
130   case VCD_LOG_WARN:
131     msg_Warn( p_vcd_input, message);
132     break;
133   case VCD_LOG_ERROR:
134   case VCD_LOG_ASSERT:
135     msg_Err( p_vcd_input, message);
136     break;
137   default:
138     msg_Warn( p_vcd_input, "%s\n%s %d", message,
139             _("The above message had unknown vcdimager log level"),
140             level);
141   }
142   return;
143 }
144
145 /*****************************************************************************
146  * VCDRead: reads i_len bytes from the VCD into p_buffer.
147  *****************************************************************************
148  * Returns -1 in case of error, 0 in case of EOF, otherwise the number of
149  * bytes.
150  *****************************************************************************/
151 static int
152 VCDRead( input_thread_t * p_input, byte_t * p_buffer, size_t i_len )
153 {
154     thread_vcd_data_t *     p_vcd= (thread_vcd_data_t *)p_input->p_access_data;
155     int                     i_blocks;
156     int                     i_index;
157     int                     i_read;
158     byte_t                  p_last_sector[ M2F2_SECTOR_SIZE ];
159
160     i_read = 0;
161
162     dbg_print( (INPUT_DBG_CALL), "lsn: %lu", 
163                (long unsigned int) p_vcd->cur_lsn );
164
165     /* Compute the number of blocks we have to read */
166
167     i_blocks = i_len / M2F2_SECTOR_SIZE;
168
169     for ( i_index = 0 ; i_index < i_blocks ; i_index++ )
170     {
171
172       if ( p_vcd->cur_lsn == p_vcd->end_lsn ) {
173         vcdplayer_read_status_t read_status;
174
175         /* We've run off of the end of this entry. Do we continue or stop? */
176         dbg_print( (INPUT_DBG_LSN|INPUT_DBG_PBC),
177                    "end reached, cur: %lu", 
178                    (long unsigned int) p_vcd->cur_lsn );
179
180         read_status = vcdplayer_pbc_is_on( p_vcd )
181           ? vcdplayer_pbc_nav( p_input )
182           : vcdplayer_non_pbc_nav( p_input );
183
184         switch (read_status) {
185         case READ_END:
186           /* End reached. Return NULL to indicated this. */
187         case READ_ERROR:
188           /* Some sort of error. */
189           return i_read;
190
191         case READ_STILL_FRAME:
192           {
193             /* Reached the end of a still frame. */
194
195             byte_t * p_buf = p_buffer;
196             pgrm_descriptor_t * p_pgrm = p_input->stream.p_selected_program;;
197
198             p_buf += (i_index*M2F2_SECTOR_SIZE);
199             memset(p_buf, 0, M2F2_SECTOR_SIZE);
200             p_buf += 2;
201             *p_buf = 0x01;
202             dbg_print(INPUT_DBG_STILL, "Handled still event");
203
204 #if 1
205             p_vcd->p_intf->p_sys->b_still = 1;
206             var_SetInteger( p_input, "state", PAUSE_S );
207 #endif
208
209             vlc_mutex_lock( &p_input->stream.stream_lock );
210
211             p_pgrm = p_input->stream.p_selected_program;
212             p_pgrm->i_synchro_state = SYNCHRO_REINIT;
213
214             vlc_mutex_unlock( &p_input->stream.stream_lock );
215
216             input_ClockManageControl( p_input, p_pgrm, 0 );
217
218             p_vcd->p_intf->p_sys->b_still = 1;
219             var_SetInteger( p_input, "state", PAUSE_S );
220
221             return i_read + M2F2_SECTOR_SIZE;
222           }
223         default:
224         case READ_BLOCK:
225           break;
226         }
227       }
228
229       if ( VCDReadSector( VLC_OBJECT(p_input), p_vcd->vcd,
230                           p_vcd->cur_lsn,
231                           p_buffer + (i_index*M2F2_SECTOR_SIZE) ) < 0 )
232         {
233           LOG_ERR ("could not read sector %lu", 
234                    (long unsigned int) p_vcd->cur_lsn );
235           return -1;
236         }
237
238       p_vcd->cur_lsn ++;
239
240       /* Update chapter */
241       if( p_vcd->b_valid_ep &&
242           /* FIXME kludge so that read does not update chapter
243            * when a manual chapter change was requested and not
244            * yet accomplished */
245           !p_input->stream.p_new_area )
246         {
247           unsigned int i_entry = p_input->stream.p_selected_area->i_part;
248
249           vlc_mutex_lock( &p_input->stream.stream_lock );
250
251           if( i_entry < p_vcd->num_entries &&
252               p_vcd->cur_lsn >= p_vcd->p_entries[i_entry+1] )
253             {
254               dbg_print( INPUT_DBG_PBC,
255                          "new entry, i_entry %d, sector %lu, es %u",
256                          i_entry, (long unsigned int) p_vcd->cur_lsn,
257                          p_vcd->p_entries[i_entry] );
258               p_vcd->play_item.num =
259                 ++ p_input->stream.p_selected_area->i_part;
260               p_vcd->play_item.type = VCDINFO_ITEM_TYPE_ENTRY;
261               VCDUpdateVar( p_input, p_vcd->play_item.num, VLC_VAR_SETVALUE,
262                             "chapter", _("Entry"), "Setting entry" );
263             }
264           vlc_mutex_unlock( &p_input->stream.stream_lock );
265         }
266
267         i_read += M2F2_SECTOR_SIZE;
268     }
269
270     if ( i_len % M2F2_SECTOR_SIZE ) /* this should not happen */
271     {
272         if ( VCDReadSector( VLC_OBJECT(p_input), p_vcd->vcd,
273                             p_vcd->cur_lsn, p_last_sector ) < 0 )
274         {
275             LOG_ERR ("could not read sector %lu", 
276                      (long unsigned int) p_vcd->cur_lsn );
277             return -1;
278         }
279
280         p_input->p_vlc->pf_memcpy( p_buffer + i_blocks * M2F2_SECTOR_SIZE,
281                                    p_last_sector, i_len % M2F2_SECTOR_SIZE );
282         i_read += i_len % M2F2_SECTOR_SIZE;
283     }
284
285     return i_read;
286 }
287
288
289 /*****************************************************************************
290  * VCDSetProgram: Does nothing since a VCD is mono_program
291  *****************************************************************************/
292 static int
293 VCDSetProgram( input_thread_t * p_input, pgrm_descriptor_t * p_program)
294 {
295     thread_vcd_data_t *     p_vcd= (thread_vcd_data_t *)p_input->p_access_data;
296     dbg_print( (INPUT_DBG_CALL|INPUT_DBG_EXT), "VCDSetProgram" );
297     return 0;
298 }
299
300
301 /*****************************************************************************
302  * VCDSetArea: initialize internal data structures and input stream data
303    so set subsequent reading and seeking to reflect that we are
304    at track x, entry or segment y.
305    This is called for each user navigation request, e.g. the GUI
306    Chapter/Title selections or in initial MRL parsing.
307  ****************************************************************************/
308 int
309 VCDSetArea( input_thread_t * p_input, input_area_t * p_area )
310 {
311     thread_vcd_data_t *p_vcd = (thread_vcd_data_t*)p_input->p_access_data;
312     unsigned int i_entry = p_area->i_part;
313     track_t i_track      = p_area->i_id;
314     int old_seekable     = p_input->stream.b_seekable;
315     unsigned int i_nb    = p_area->i_plugin_data + p_area->i_part_nb;
316
317     dbg_print( (INPUT_DBG_CALL|INPUT_DBG_EXT),
318                "track: %d, entry %d, seekable %d, area %lx, select area %lx ",
319                i_track, i_entry, old_seekable,
320                (long unsigned int) p_area,
321                (long unsigned int) p_input->stream.p_selected_area );
322
323     /* we can't use the interface slider until initilization is complete */
324     p_input->stream.b_seekable = 0;
325
326     if( p_area != p_input->stream.p_selected_area )
327     {
328         unsigned int i;
329
330         /* If is the result of a track change, make the entry valid. */
331         if (i_entry < p_area->i_plugin_data || i_entry >= i_nb)
332           i_entry = p_area->i_plugin_data;
333
334         /* Change the default area */
335         p_input->stream.p_selected_area = p_area;
336
337         /* Update the navigation variables without triggering a callback */
338
339         VCDUpdateVar( p_input, i_track, VLC_VAR_SETVALUE, "title",
340                       _("Track"), "Setting track");
341
342         var_Change( p_input, "chapter", VLC_VAR_CLEARCHOICES, NULL, NULL );
343         for( i = p_area->i_plugin_data; i < i_nb; i++ )
344         {
345           VCDUpdateVar( p_input, i , VLC_VAR_ADDCHOICE,
346                         "chapter",  
347                         p_vcd->play_item.type == VCDINFO_ITEM_TYPE_SEGMENT ?
348                         _("Segment"): _("Entry"),
349                         "Adding entry choice");
350         }
351
352         if (p_vcd->b_svd) {
353           unsigned int audio_type = 
354             vcdinfo_get_track_audio_type(p_vcd->vcd, i_track);
355           unsigned int i_channels = 
356             vcdinfo_audio_type_num_channels(p_vcd->vcd, audio_type);
357           
358           var_Change( p_input, "audio_channels", VLC_VAR_CLEARCHOICES, NULL, 
359                       NULL );
360           
361           for( i = 0;  i < i_channels; i++ )
362             {
363               VCDUpdateVar( p_input, i , VLC_VAR_ADDCHOICE,
364                             "audio_channels",  NULL, "Adding audio choice");
365             }
366         }
367
368     }
369
370     if (i_track == 0)
371       VCDSetOrigin( p_input, p_vcd->p_segments[i_entry],
372                     p_vcd->p_segments[i_entry], p_vcd->p_segments[i_entry+1],
373                     i_entry, 0 );
374     else
375       VCDSetOrigin( p_input, 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_entry, i_track );
379
380     p_input->stream.b_seekable = old_seekable;
381     /* warn interface that something has changed */
382     p_input->stream.b_changed = 1;
383
384     return VLC_SUCCESS;
385 }
386
387
388 /****************************************************************************
389  * VCDSeek
390  ****************************************************************************/
391 void
392 VCDSeek( input_thread_t * p_input, off_t i_off )
393 {
394     thread_vcd_data_t * p_vcd;
395     unsigned int i_entry=0; /* invalid entry */
396
397     p_vcd = (thread_vcd_data_t *) p_input->p_access_data;
398
399     p_vcd->cur_lsn = p_vcd->origin_lsn + (i_off / (off_t)M2F2_SECTOR_SIZE);
400
401     vlc_mutex_lock( &p_input->stream.stream_lock );
402 #define p_area p_input->stream.p_selected_area
403     /* Find entry */
404     if( p_vcd->b_valid_ep )
405     {
406         for( i_entry = 0 ; i_entry < p_vcd->num_entries ; i_entry ++ )
407         {
408             if( p_vcd->cur_lsn < p_vcd->p_entries[i_entry] )
409             {
410               VCDUpdateVar( p_input, i_entry, VLC_VAR_SETVALUE,
411                             "chapter", _("Entry"), "Setting entry" );
412               break;
413             }
414         }
415         p_vcd->play_item.num  = p_area->i_part = i_entry;
416         p_vcd->play_item.type = VCDINFO_ITEM_TYPE_ENTRY;
417     }
418 #undef p_area
419
420     p_input->stream.p_selected_area->i_tell = i_off;
421
422     dbg_print( (INPUT_DBG_CALL|INPUT_DBG_EXT|INPUT_DBG_SEEK),
423                "orig %lu, cur: %lu, offset: %lld, start: %lld, entry %d",
424                (long unsigned int) p_vcd->origin_lsn, 
425                (long unsigned int) p_vcd->cur_lsn, i_off,
426                p_input->stream.p_selected_area->i_start, i_entry );
427
428     vlc_mutex_unlock( &p_input->stream.stream_lock );
429 }
430
431 /*****************************************************************************
432   VCDPlay: set up internal structures so seeking/reading places an item.
433   itemid: the thing to play.
434   user_entry: true if itemid is a user selection (rather than internally-
435   generated selection such as via PBC) in which case we may have to adjust
436   for differences in numbering.
437  *****************************************************************************/
438 int
439 VCDPlay( input_thread_t *p_input, vcdinfo_itemid_t itemid )
440 {
441     thread_vcd_data_t *     p_vcd= (thread_vcd_data_t *)p_input->p_access_data;
442     input_area_t *          p_area;
443     vlc_bool_t              b_was_still;
444
445     dbg_print(INPUT_DBG_CALL, "itemid.num: %d, itemid.type: %d\n",
446               itemid.num, itemid.type);
447
448     if (!p_input->p_access_data) return VLC_EGENERIC;
449     
450     b_was_still = p_vcd->in_still;
451
452 #define area p_input->stream.pp_areas
453
454     switch (itemid.type) {
455     case VCDINFO_ITEM_TYPE_TRACK:
456
457       /* Valid tracks go from 1...num_tracks-1, because track 0 is unplayable.
458        */
459
460       if (itemid.num == 0 || itemid.num >= p_vcd->num_tracks) {
461         LOG_ERR ("Invalid track number %d", itemid.num );
462         return VLC_EGENERIC;
463       }
464       p_vcd->in_still  = VLC_FALSE;
465       p_area           = area[itemid.num];
466       p_area->i_part   = p_area->i_plugin_data;
467       p_input->stream.b_seekable = 1;
468       break;
469     case VCDINFO_ITEM_TYPE_SEGMENT:
470       /* Valid segments go from 0...num_segments-1. */
471       if (itemid.num >= p_vcd->num_segments) {
472         LOG_ERR ( "Invalid segment number: %d", itemid.num );
473         return VLC_EGENERIC;
474       } else {
475         vcdinfo_video_segment_type_t segtype =
476           vcdinfo_get_video_type(p_vcd->vcd, itemid.num);
477
478         dbg_print(INPUT_DBG_PBC, "%s (%d), seg_num: %d",
479                   vcdinfo_video_type2str(p_vcd->vcd, itemid.num),
480                   (int) segtype, itemid.num);
481
482         p_area           = area[0];
483         p_area->i_part   = itemid.num;
484
485         switch (segtype)
486           {
487           case VCDINFO_FILES_VIDEO_NTSC_STILL:
488           case VCDINFO_FILES_VIDEO_NTSC_STILL2:
489           case VCDINFO_FILES_VIDEO_PAL_STILL:
490           case VCDINFO_FILES_VIDEO_PAL_STILL2:
491             p_input->stream.b_seekable = 0;
492             p_vcd->in_still = VLC_TRUE;
493             break;
494           default:
495             p_input->stream.b_seekable = 1;
496             p_vcd->in_still = VLC_FALSE;
497           }
498       }
499       break;
500
501     case VCDINFO_ITEM_TYPE_LID:
502       /* LIDs go from 1..num_lids. */
503       if (itemid.num == 0 || itemid.num > p_vcd->num_lids) {
504         LOG_ERR ( "Invalid LID number: %d", itemid.num );
505         return VLC_EGENERIC;
506       } else {
507         p_vcd->cur_lid = itemid.num;
508         vcdinfo_lid_get_pxd(p_vcd->vcd, &(p_vcd->pxd), itemid.num);
509
510         switch (p_vcd->pxd.descriptor_type) {
511
512         case PSD_TYPE_SELECTION_LIST:
513         case PSD_TYPE_EXT_SELECTION_LIST: {
514           vcdinfo_itemid_t trans_itemid;
515           uint16_t trans_itemid_num;
516
517           if (p_vcd->pxd.psd == NULL) return VLC_EGENERIC;
518           trans_itemid_num  = vcdinf_psd_get_itemid(p_vcd->pxd.psd);
519           vcdinfo_classify_itemid(trans_itemid_num, &trans_itemid);
520           p_vcd->loop_count = 1;
521           p_vcd->loop_item  = trans_itemid;
522           return VCDPlay( p_input, trans_itemid );
523           break;
524         }
525
526         case PSD_TYPE_PLAY_LIST: {
527           if (p_vcd->pxd.pld == NULL) return VLC_EGENERIC;
528           p_vcd->pdi = -1;
529           return vcdplayer_inc_play_item(p_input)
530             ? VLC_SUCCESS : VLC_EGENERIC;
531           break;
532         }
533
534         case PSD_TYPE_END_LIST:
535         case PSD_TYPE_COMMAND_LIST:
536
537         default:
538           ;
539         }
540       }
541       return VLC_EGENERIC;
542     case VCDINFO_ITEM_TYPE_ENTRY:
543       /* Entries go from 0..num_entries-1. */
544       if (itemid.num >= p_vcd->num_entries) {
545         LOG_ERR ("Invalid entry number: %d", itemid.num );
546         return VLC_EGENERIC;
547       } else {
548         track_t cur_track  = vcdinfo_get_track(p_vcd->vcd,  itemid.num);
549         p_vcd->in_still    = VLC_FALSE;
550         p_area             = area[cur_track];
551         p_area->i_part     = itemid.num;
552         p_input->stream.b_seekable = 1;
553       }
554       break;
555     default:
556       LOG_ERR ("unknown entry type" );
557       return VLC_EGENERIC;
558     }
559
560     VCDSetArea( p_input, p_area );
561
562 #undef area
563
564 #if 1
565     if ( p_vcd->in_still != b_was_still ) {
566       if (p_input->stream.pp_selected_es) {
567         var_SetInteger( p_input, "state", PLAYING_S );
568       }
569     }
570 #endif
571
572     p_vcd->play_item = itemid;
573
574     dbg_print( (INPUT_DBG_CALL),
575                "i_start %lld, i_size: %lld, i_tell: %lld, lsn %lu",
576                p_area->i_start, p_area->i_size,
577                p_area->i_tell, 
578                (long unsigned int) p_vcd->cur_lsn );
579
580     return VLC_SUCCESS;
581 }
582
583 /*****************************************************************************
584   VCDEntryPoints: Reads the information about the entry points on the disc
585   and initializes area information with that.
586   Before calling this track information should have been read in.
587  *****************************************************************************/
588 static int
589 VCDEntryPoints( input_thread_t * p_input )
590 {
591     thread_vcd_data_t *               p_vcd;
592     unsigned int                      i_nb;
593     unsigned int                      i, i_entry_index = 0;
594     unsigned int                      i_previous_track = CDIO_INVALID_TRACK;
595
596     p_vcd = (thread_vcd_data_t *) p_input->p_access_data;
597
598     i_nb = vcdinfo_get_num_entries(p_vcd->vcd);
599     if (0 == i_nb)
600       return -1;
601
602     p_vcd->p_entries  = malloc( sizeof( lba_t ) * i_nb );
603
604     if( p_vcd->p_entries == NULL )
605     {
606         LOG_ERR ("not enough memory for entry points treatment" );
607         return -1;
608     }
609
610     p_vcd->num_entries = 0;
611
612     for( i = 0 ; i < i_nb ; i++ )
613     {
614         track_t i_track = vcdinfo_get_track(p_vcd->vcd, i);
615         if( i_track <= p_input->stream.i_area_nb )
616         {
617             p_vcd->p_entries[i] =
618               vcdinfo_get_entry_lsn(p_vcd->vcd, i);
619             p_input->stream.pp_areas[i_track]->i_part_nb ++;
620
621             /* if this entry belongs to a new track */
622             if( i_track != i_previous_track )
623             {
624                 /* i_plugin_data is used to store the first entry of the area*/
625                 p_input->stream.pp_areas[i_track]->i_plugin_data =
626                                                             i_entry_index;
627                 i_previous_track = i_track;
628                 p_input->stream.pp_areas[i_track]->i_part_nb = 1;
629             }
630             i_entry_index ++;
631             p_vcd->num_entries ++;
632         }
633         else
634             msg_Warn( p_input, "wrong track number found in entry points" );
635     }
636     p_vcd->b_valid_ep = VLC_TRUE;
637     return 0;
638 }
639
640 /*****************************************************************************
641  * VCDSegments: Reads the information about the segments the disc.
642  *****************************************************************************/
643 static int
644 VCDSegments( input_thread_t * p_input )
645 {
646     thread_vcd_data_t * p_vcd;
647     unsigned int        i;
648     unsigned int        num_segments;
649
650
651     p_vcd = (thread_vcd_data_t *) p_input->p_access_data;
652     num_segments = p_vcd->num_segments = vcdinfo_get_num_segments(p_vcd->vcd);
653
654 #define area p_input->stream.pp_areas
655
656     /* area 0 is reserved for segments. Set Absolute start offset
657          and size */
658     area[0]->i_plugin_data = 0;
659     input_DelArea( p_input, area[0] );
660     input_AddArea( p_input, 0, 0 );
661
662     area[0]->i_start = (off_t)p_vcd->p_sectors[0]
663       * (off_t)M2F2_SECTOR_SIZE;
664     area[0]->i_size = (off_t)(p_vcd->p_sectors[1] - p_vcd->p_sectors[0])
665       * (off_t)M2F2_SECTOR_SIZE;
666
667     /* Default Segment  */
668     area[0]->i_part = 0;
669
670     /* i_plugin_data is used to store which entry point is the first
671        of the track (area) */
672     area[0]->i_plugin_data = 0;
673
674     area[0]->i_part_nb = 0;
675
676     dbg_print( INPUT_DBG_MRL,
677                "area[0] id: %d, i_start: %lld, i_size: %lld",
678                area[0]->i_id, area[0]->i_start, area[0]->i_size );
679
680     if (num_segments == 0) return 0;
681
682     /* We have one additional segment allocated so we can get the size
683        by subtracting seg[i+1] - seg[i].
684      */
685     p_vcd->p_segments = malloc( sizeof( lba_t ) * (num_segments+1) );
686     if( p_vcd->p_segments == NULL )
687     {
688         LOG_ERR ("not enough memory for segment treatment" );
689         return -1;
690     }
691
692     /* Update the navigation variables without triggering a callback */
693     VCDUpdateVar( p_input, 0, VLC_VAR_SETVALUE, "title", _("Track"),
694                   "Setting track" );
695
696     var_Change( p_input, "chapter", VLC_VAR_CLEARCHOICES, NULL, NULL );
697
698     for( i = 0 ; i < num_segments ; i++ )
699     {
700       p_vcd->p_segments[i] = vcdinfo_get_seg_lsn(p_vcd->vcd, i);
701       area[0]->i_part_nb ++;
702       VCDUpdateVar( p_input, i , VLC_VAR_ADDCHOICE,
703                     "chapter", _("Segment"), "Adding segment choice");
704     }
705
706 #undef area
707
708     p_vcd->p_segments[num_segments] = p_vcd->p_segments[num_segments-1]+
709       vcdinfo_get_seg_sector_count(p_vcd->vcd, num_segments-1);
710
711     return 0;
712 }
713
714 /*****************************************************************************
715  VCDTracks: initializes area information.
716  Before calling this track information should have been read in.
717  *****************************************************************************/
718 static void
719 VCDTracks( input_thread_t * p_input )
720 {
721     thread_vcd_data_t * p_vcd;
722     unsigned int        i;
723
724     p_vcd = (thread_vcd_data_t *) p_input->p_access_data;
725
726 #define area p_input->stream.pp_areas
727
728     /* We start area addressing for tracks at 1 since the default area 0
729        is reserved for segments */
730
731     for( i = 1 ; i < p_vcd->num_tracks ; i++ )
732     {
733         /* Tracks are Program Chains */
734         input_AddArea( p_input, i, i );
735
736         /* Absolute start byte offset and byte size */
737         area[i]->i_start = (off_t) p_vcd->p_sectors[i]
738                            * (off_t)M2F2_SECTOR_SIZE;
739         area[i]->i_size = (off_t)(p_vcd->p_sectors[i+1] - p_vcd->p_sectors[i])
740                            * (off_t)M2F2_SECTOR_SIZE;
741
742         /* Current entry being played in track */
743         area[i]->i_part = 0;
744
745         /* i_plugin_data is used to store which entry point is the first
746          * of the track (area) */
747         area[i]->i_plugin_data = 0;
748
749         dbg_print( INPUT_DBG_MRL,
750                    "area[%d] id: %d, i_start: %lld, i_size: %lld",
751                    i, area[i]->i_id, area[i]->i_start, area[i]->i_size );
752     }
753
754 #undef area
755
756     return ;
757 }
758
759 /*****************************************************************************
760   VCDLIDs: Reads the LIST IDs from the LOT.
761  *****************************************************************************/
762 static int
763 VCDLIDs( input_thread_t * p_input )
764 {
765     thread_vcd_data_t *p_vcd = (thread_vcd_data_t *) p_input->p_access_data;
766
767     p_vcd->num_lids = vcdinfo_get_num_LIDs(p_vcd->vcd);
768     p_vcd->cur_lid  = VCDINFO_INVALID_ENTRY;
769
770     if (vcdinfo_read_psd (p_vcd->vcd)) {
771
772       vcdinfo_visit_lot (p_vcd->vcd, VLC_FALSE);
773
774 #if FIXED
775     /*
776        We need to change libvcdinfo to be more robust when there are
777        problems reading the extended PSD. Given that area-highlighting and
778        selection features in the extended PSD haven't been implemented,
779        it's best then to not try to read this at all.
780      */
781       if (vcdinfo_get_psd_x_size(p_vcd->vcd))
782         vcdinfo_visit_lot (p_vcd->vcd, VLC_TRUE);
783 #endif
784     }
785
786     dbg_print( (INPUT_DBG_CALL|INPUT_DBG_MRL),
787                "num LIDs=%d", p_vcd->num_lids);
788
789     return 0;
790 }
791
792 /*****************************************************************************
793  * VCDParse: parse command line
794  *****************************************************************************/
795 static char *
796 VCDParse( input_thread_t * p_input, /*out*/ vcdinfo_itemid_t * p_itemid,
797           /*out*/ vlc_bool_t *play_single_item )
798 {
799     thread_vcd_data_t *p_vcd = (thread_vcd_data_t *)p_input->p_access_data;
800     char *             psz_parser;
801     char *             psz_source;
802     char *             psz_next;
803
804     if( config_GetInt( p_input, MODULE_STRING "-PBC" ) ) {
805       p_itemid->type = VCDINFO_ITEM_TYPE_LID;
806       p_itemid->num = 1;
807       *play_single_item = VLC_FALSE;
808     }
809     else 
810     {
811       p_itemid->type = VCDINFO_ITEM_TYPE_ENTRY;
812       p_itemid->num = 0;
813     }
814
815 #ifdef WIN32
816     /* On Win32 we want the VCD access plugin to be explicitly requested,
817      * we end up with lots of problems otherwise */
818     if( !p_input->psz_access || !*p_input->psz_access ) return NULL;
819 #endif
820
821     if( !p_input->psz_name )
822     {
823         return NULL;
824     }
825
826     psz_parser = psz_source = strdup( p_input->psz_name );
827
828     /* Parse input string :
829      * [device][@[type][title]] */
830     while( *psz_parser && *psz_parser != '@' )
831     {
832         psz_parser++;
833     }
834
835     if( *psz_parser == '@' )
836     {
837       /* Found the divide between the source name and the
838          type+entry number. */
839       unsigned int num;
840
841       *psz_parser = '\0';
842       ++psz_parser;
843       if( *psz_parser )
844         {
845           switch(*psz_parser) {
846           case 'E':
847             p_itemid->type = VCDINFO_ITEM_TYPE_ENTRY;
848             ++psz_parser;
849             *play_single_item = VLC_TRUE;
850             break;
851           case 'P':
852             p_itemid->type = VCDINFO_ITEM_TYPE_LID;
853             ++psz_parser;
854             *play_single_item = VLC_FALSE;
855             break;
856           case 'S':
857             p_itemid->type = VCDINFO_ITEM_TYPE_SEGMENT;
858             ++psz_parser;
859             *play_single_item = VLC_TRUE;
860             break;
861           case 'T':
862             p_itemid->type = VCDINFO_ITEM_TYPE_TRACK;
863             ++psz_parser;
864             *play_single_item = VLC_TRUE;
865             break;
866           default: ;
867           }
868         }
869
870       num = strtol( psz_parser, &psz_next, 10 );
871       if ( *psz_parser != '\0' && *psz_next == '\0')
872         {
873           p_itemid->num = num;
874         }
875
876     } else {
877       *play_single_item = ( VCDINFO_ITEM_TYPE_LID == p_itemid->type );
878     }
879
880
881     if( !*psz_source ) {
882
883       /* No source specified, so figure it out. */
884       if( !p_input->psz_access ) return NULL;
885
886       psz_source = config_GetPsz( p_input, "vcd" );
887
888       if( !psz_source || 0==strlen(psz_source) ) {
889         /* Scan for a CD-ROM drive with a VCD in it. */
890         char **cd_drives = cdio_get_devices_with_cap( NULL,
891                             ( CDIO_FS_ANAL_SVCD | CDIO_FS_ANAL_CVD
892                               |CDIO_FS_ANAL_VIDEOCD | CDIO_FS_UNKNOWN ),
893                                                      VLC_TRUE );
894         if( NULL == cd_drives ) return NULL;
895         if( cd_drives[0] == NULL )
896         {
897           cdio_free_device_list( cd_drives );
898           return NULL;
899         }
900         psz_source = strdup( cd_drives[0] );
901         cdio_free_device_list( cd_drives );
902       }
903     }
904
905     dbg_print( (INPUT_DBG_CALL|INPUT_DBG_MRL),
906                "source=%s entry=%d type=%d",
907                psz_source, p_itemid->num, p_itemid->type);
908
909     return psz_source;
910 }
911
912 /*
913    Set's start origin subsequent seeks/reads
914 */
915 static void
916 VCDSetOrigin( input_thread_t *p_input, lsn_t origin_lsn,
917               lsn_t cur_lsn, lsn_t end_lsn, int cur_entry, track_t cur_track )
918 {
919   thread_vcd_data_t * p_vcd  = (thread_vcd_data_t *) p_input->p_access_data;
920
921   p_vcd->origin_lsn = origin_lsn;
922   p_vcd->cur_lsn    = cur_lsn;
923   p_vcd->end_lsn    = end_lsn;
924   p_vcd->cur_track  = cur_track;
925   p_vcd->play_item.num  = cur_entry;
926   p_vcd->play_item.type = VCDINFO_ITEM_TYPE_ENTRY;
927
928   dbg_print( (INPUT_DBG_CALL|INPUT_DBG_LSN),
929              "origin: %lu, cur_lsn: %lu, end_lsn: %lu, entry: %d, track: %d",
930              (long unsigned int) origin_lsn, 
931              (long unsigned int) cur_lsn, 
932              (long unsigned int) end_lsn, cur_entry, cur_track );
933
934   p_input->stream.p_selected_area->i_tell =
935     (off_t) (p_vcd->cur_lsn - p_vcd->origin_lsn) * (off_t)M2F2_SECTOR_SIZE;
936
937   VCDUpdateVar( p_input, cur_entry, VLC_VAR_SETVALUE,
938                 "chapter", 
939                 p_vcd->play_item.type == VCDINFO_ITEM_TYPE_ENTRY ?
940                 _("Entry") : _("Segment"), 
941                 "Setting entry/segment");
942 }
943
944 /*****************************************************************************
945  * vcd_Open: Opens a VCD device or file and returns an opaque handle
946  *****************************************************************************/
947 static vcdinfo_obj_t *
948 vcd_Open( vlc_object_t *p_this, const char *psz_dev )
949 {
950     vcdinfo_obj_t *p_vcdobj;
951     char  *actual_dev;
952
953     if( !psz_dev ) return NULL;
954
955     actual_dev=strdup(psz_dev);
956     if ( vcdinfo_open(&p_vcdobj, &actual_dev, DRIVER_UNKNOWN, NULL) !=
957          VCDINFO_OPEN_VCD) {
958       free(actual_dev);
959       return NULL;
960     }
961     free(actual_dev);
962
963     return p_vcdobj;
964 }
965
966 /****************************************************************************
967  * VCDReadSector: Read a sector (2324 bytes)
968  ****************************************************************************/
969 static int
970 VCDReadSector( vlc_object_t *p_this, const vcdinfo_obj_t *p_vcd,
971                lsn_t cur_lsn, byte_t * p_buffer )
972 {
973   typedef struct {
974     uint8_t subheader   [8];
975     uint8_t data        [M2F2_SECTOR_SIZE];
976     uint8_t spare       [4];
977   } vcdsector_t;
978   vcdsector_t vcd_sector;
979
980   if( cdio_read_mode2_sector( vcdinfo_get_cd_image( p_vcd ),
981                               &vcd_sector, cur_lsn, VLC_TRUE )
982       != 0)
983   {
984       msg_Warn( p_this, "Could not read LSN %lu", 
985                 (long unsigned int) cur_lsn );
986       return -1;
987   }
988
989   memcpy (p_buffer, vcd_sector.data, M2F2_SECTOR_SIZE);
990
991   return( 0 );
992 }
993
994 /****************************************************************************
995  Update the "varname" variable to i_num without triggering a callback.
996 ****************************************************************************/
997 static void
998 VCDUpdateVar( input_thread_t *p_input, int i_num, int i_action,
999               const char *p_varname, char *p_label, 
1000               const char *p_debug_label)
1001 {
1002   vlc_value_t val;
1003   val.i_int = i_num;
1004   if (NULL != p_vcd_input) {
1005     thread_vcd_data_t *p_vcd = (thread_vcd_data_t *)p_vcd_input->p_access_data;
1006     dbg_print( INPUT_DBG_PBC, "%s %d", p_debug_label, i_num );
1007   }
1008   if (p_label) {
1009     vlc_value_t text;
1010     text.psz_string = p_label;
1011     var_Change( p_input, p_varname, VLC_VAR_SETTEXT, &text, NULL );
1012   }
1013   var_Change( p_input, p_varname, i_action, &val, NULL );
1014 }
1015
1016
1017 static inline void
1018 MetaInfoAddStr(input_thread_t *p_input, char *p_cat,
1019                char *title, const char *str)
1020 {
1021   thread_vcd_data_t *p_vcd = (thread_vcd_data_t *) p_input->p_access_data;
1022   if ( str ) {
1023     dbg_print( INPUT_DBG_META, "field: %s: %s\n", title, str);
1024     input_Control( p_input, INPUT_ADD_INFO, p_cat, title, "%s", str);
1025   }
1026 }
1027
1028
1029 static inline void
1030 MetaInfoAddNum(input_thread_t *p_input, char *p_cat, char *title, int num)
1031 {
1032   thread_vcd_data_t *p_vcd = (thread_vcd_data_t *) p_input->p_access_data;
1033   dbg_print( INPUT_DBG_META, "field %s: %d\n", title, num);
1034   input_Control( p_input, INPUT_ADD_INFO, p_cat, title, "%d", num );
1035 }
1036
1037 #define addstr(title, str) \
1038   MetaInfoAddStr( p_input, p_cat, title, str );
1039
1040 #define addnum(title, num) \
1041   MetaInfoAddNum( p_input, p_cat, title, num );
1042
1043 static void InformationCreate( input_thread_t *p_input  )
1044 {
1045   thread_vcd_data_t *p_vcd = (thread_vcd_data_t *) p_input->p_access_data;
1046   unsigned int i_nb = vcdinfo_get_num_entries(p_vcd->vcd);
1047   unsigned int last_entry = 0;
1048   char *p_cat;
1049   track_t i_track;
1050
1051   p_cat = _("General");
1052
1053   addstr( _("VCD Format"),  vcdinfo_get_format_version_str(p_vcd->vcd) );
1054   addstr( _("Album"),       vcdinfo_get_album_id(p_vcd->vcd));
1055   addstr( _("Application"), vcdinfo_get_application_id(p_vcd->vcd) );
1056   addstr( _("Preparer"),    vcdinfo_get_preparer_id(p_vcd->vcd) );
1057   addnum( _("Vol #"),       vcdinfo_get_volume_num(p_vcd->vcd) );
1058   addnum( _("Vol max #"),   vcdinfo_get_volume_count(p_vcd->vcd) );
1059   addstr( _("Volume Set"),  vcdinfo_get_volumeset_id(p_vcd->vcd) );
1060   addstr( _("Volume"),      vcdinfo_get_volume_id(p_vcd->vcd) );
1061   addstr( _("Publisher"),   vcdinfo_get_publisher_id(p_vcd->vcd) );
1062   addstr( _("System Id"),   vcdinfo_get_system_id(p_vcd->vcd) );
1063   addnum( "LIDs",           vcdinfo_get_num_LIDs(p_vcd->vcd) );
1064   addnum( _("Entries"),     vcdinfo_get_num_entries(p_vcd->vcd) );
1065   addnum( _("Segments"),    vcdinfo_get_num_segments(p_vcd->vcd) );
1066   addnum( _("Tracks"),      vcdinfo_get_num_tracks(p_vcd->vcd) );
1067
1068   /* Spit out track information. Could also include MSF info.
1069    */
1070
1071 #define TITLE_MAX 30
1072   for( i_track = 1 ; i_track < p_vcd->num_tracks ; i_track++ ) {
1073     char psz_track[TITLE_MAX];
1074     unsigned int audio_type = vcdinfo_get_track_audio_type(p_vcd->vcd, 
1075                                                            i_track);
1076     snprintf(psz_track, TITLE_MAX, "%s%02d", _("Track "), i_track);
1077     p_cat = psz_track;
1078
1079     if (p_vcd->b_svd) {
1080       addnum(_("Audio Channels"),  
1081              vcdinfo_audio_type_num_channels(p_vcd->vcd, audio_type) );
1082     }
1083
1084     addnum(_("First Entry Point"), last_entry );
1085     for ( ; last_entry < i_nb 
1086             && vcdinfo_get_track(p_vcd->vcd, last_entry) == i_track;
1087           last_entry++ ) ;
1088     addnum(_("Last Entry Point"), last_entry-1 );
1089   }
1090 }
1091
1092 #define add_format_str_info(val)                               \
1093   {                                                            \
1094     const char *str = val;                                     \
1095     unsigned int len;                                          \
1096     if (val != NULL) {                                         \
1097       len=strlen(str);                                         \
1098       if (len != 0) {                                          \
1099         strncat(tp, str, TEMP_STR_LEN-(tp-temp_str));          \
1100         tp += len;                                             \
1101       }                                                        \
1102       saw_control_prefix = VLC_FALSE;                          \
1103     }                                                          \
1104   }
1105
1106 #define add_format_num_info( val, fmt )                        \
1107   {                                                            \
1108     char num_str[10];                                          \
1109     unsigned int len;                                          \
1110     sprintf(num_str, fmt, val);                                \
1111     len = strlen(num_str);                                     \
1112     if( len != 0 )                                             \
1113     {                                                          \
1114       strncat(tp, num_str, TEMP_STR_LEN-(tp-temp_str));        \
1115       tp += len;                                               \
1116     }                                                          \
1117     saw_control_prefix = VLC_FALSE;                                \
1118   }
1119
1120 /*!
1121    Take a format string and expand escape sequences, that is sequences that
1122    begin with %, with information from the current VCD.
1123    The expanded string is returned. Here is a list of escape sequences:
1124
1125    %A : The album information
1126    %C : The VCD volume count - the number of CD's in the collection.
1127    %c : The VCD volume num - the number of the CD in the collection.
1128    %F : The VCD Format, e.g. VCD 1.0, VCD 1.1, VCD 2.0, or SVCD
1129    %I : The current entry/segment/playback type, e.g. ENTRY, TRACK, SEGMENT...
1130    %L : The playlist ID prefixed with " LID" if it exists
1131    %M : MRL
1132    %N : The current number of the %I - a decimal number
1133    %P : The publisher ID
1134    %p : The preparer ID
1135    %S : If we are in a segment (menu), the kind of segment
1136    %T : The track number
1137    %V : The volume set ID
1138    %v : The volume ID
1139        A number between 1 and the volume count.
1140    %% : a %
1141 */
1142 static char *
1143 VCDFormatStr(const input_thread_t *p_input, thread_vcd_data_t *p_vcd,
1144              const char format_str[], const char *mrl,
1145              const vcdinfo_itemid_t *itemid)
1146 {
1147 #define TEMP_STR_SIZE 256
1148 #define TEMP_STR_LEN (TEMP_STR_SIZE-1)
1149   static char    temp_str[TEMP_STR_SIZE];
1150   size_t         i;
1151   char *         tp = temp_str;
1152   vlc_bool_t     saw_control_prefix = VLC_FALSE;
1153   size_t         format_len = strlen(format_str);
1154
1155   memset(temp_str, 0, TEMP_STR_SIZE);
1156
1157   for (i=0; i<format_len; i++) {
1158
1159     if (!saw_control_prefix && format_str[i] != '%') {
1160       *tp++ = format_str[i];
1161       saw_control_prefix = VLC_FALSE;
1162       continue;
1163     }
1164
1165     switch(format_str[i]) {
1166     case '%':
1167       if (saw_control_prefix) {
1168         *tp++ = '%';
1169       }
1170       saw_control_prefix = !saw_control_prefix;
1171       break;
1172     case 'A':
1173       add_format_str_info(vcdinfo_strip_trail(vcdinfo_get_album_id(p_vcd->vcd),
1174                                               MAX_ALBUM_LEN));
1175       break;
1176
1177     case 'c':
1178       add_format_num_info(vcdinfo_get_volume_num(p_vcd->vcd), "%d");
1179       break;
1180
1181     case 'C':
1182       add_format_num_info(vcdinfo_get_volume_count(p_vcd->vcd), "%d");
1183       break;
1184
1185     case 'F':
1186       add_format_str_info(vcdinfo_get_format_version_str(p_vcd->vcd));
1187       break;
1188
1189     case 'I':
1190       {
1191         switch (itemid->type) {
1192         case VCDINFO_ITEM_TYPE_TRACK:
1193           strncat(tp, _("Track"), TEMP_STR_LEN-(tp-temp_str));
1194           tp += strlen(_("Track"));
1195         break;
1196         case VCDINFO_ITEM_TYPE_ENTRY:
1197           strncat(tp, _("Entry"), TEMP_STR_LEN-(tp-temp_str));
1198           tp += strlen(_("Entry"));
1199           break;
1200         case VCDINFO_ITEM_TYPE_SEGMENT:
1201           strncat(tp, _("Segment"), TEMP_STR_LEN-(tp-temp_str));
1202           tp += strlen(_("Segment"));
1203           break;
1204         case VCDINFO_ITEM_TYPE_LID:
1205           strncat(tp, _("List ID"), TEMP_STR_LEN-(tp-temp_str));
1206           tp += strlen(_("List ID"));
1207           break;
1208         case VCDINFO_ITEM_TYPE_SPAREID2:
1209           strncat(tp, _("Navigation"), TEMP_STR_LEN-(tp-temp_str));
1210           tp += strlen(_("Navigation"));
1211           break;
1212         default:
1213           /* What to do? */
1214           ;
1215         }
1216         saw_control_prefix = VLC_FALSE;
1217       }
1218       break;
1219
1220     case 'L':
1221       if (vcdplayer_pbc_is_on(p_vcd)) {
1222         char num_str[40];
1223         sprintf(num_str, "%s %d", _("List ID"), p_vcd->cur_lid);
1224         strncat(tp, num_str, TEMP_STR_LEN-(tp-temp_str));
1225         tp += strlen(num_str);
1226       }
1227       saw_control_prefix = VLC_FALSE;
1228       break;
1229
1230     case 'M':
1231       add_format_str_info(mrl);
1232       break;
1233
1234     case 'N':
1235       add_format_num_info(itemid->num, "%d");
1236       break;
1237
1238     case 'p':
1239       add_format_str_info(vcdinfo_get_preparer_id(p_vcd->vcd));
1240       break;
1241
1242     case 'P':
1243       add_format_str_info(vcdinfo_get_publisher_id(p_vcd->vcd));
1244       break;
1245
1246     case 'S':
1247       if ( VCDINFO_ITEM_TYPE_SEGMENT==itemid->type ) {
1248         char seg_type_str[10];
1249
1250         sprintf(seg_type_str, " %s",
1251                 vcdinfo_video_type2str(p_vcd->vcd, itemid->num));
1252         strncat(tp, seg_type_str, TEMP_STR_LEN-(tp-temp_str));
1253         tp += strlen(seg_type_str);
1254       }
1255       saw_control_prefix = VLC_FALSE;
1256       break;
1257
1258     case 'T':
1259       add_format_num_info(p_vcd->cur_track, "%d");
1260       break;
1261
1262     case 'V':
1263       add_format_str_info(vcdinfo_get_volumeset_id(p_vcd->vcd));
1264       break;
1265
1266     case 'v':
1267       add_format_str_info(vcdinfo_get_volume_id(p_vcd->vcd));
1268       break;
1269
1270     default:
1271       *tp++ = '%';
1272       *tp++ = format_str[i];
1273       saw_control_prefix = VLC_FALSE;
1274     }
1275   }
1276   return strdup(temp_str);
1277 }
1278
1279 static void
1280 VCDCreatePlayListItem(const input_thread_t *p_input,
1281                       thread_vcd_data_t *p_vcd,
1282                       playlist_t *p_playlist,
1283                       const vcdinfo_itemid_t *itemid,
1284                       char *psz_mrl, int psz_mrl_max,
1285                       const char *psz_source, int playlist_operation,
1286                       int i_pos)
1287 {
1288   char *p_author;
1289   char *p_title;
1290   char c_type;
1291
1292   switch(itemid->type) {
1293   case VCDINFO_ITEM_TYPE_TRACK:
1294     c_type='T';
1295     break;
1296   case VCDINFO_ITEM_TYPE_SEGMENT:
1297     c_type='S';
1298     break;
1299   case VCDINFO_ITEM_TYPE_LID:
1300     c_type='P';
1301     break;
1302   case VCDINFO_ITEM_TYPE_ENTRY:
1303     c_type='E';
1304     break;
1305   default:
1306     c_type='?';
1307     break;
1308   }
1309
1310   snprintf(psz_mrl, psz_mrl_max, "%s%s@%c%u", VCD_MRL_PREFIX, psz_source,
1311            c_type, itemid->num);
1312
1313   p_title =
1314     VCDFormatStr( p_input, p_vcd,
1315                   config_GetPsz( p_input, MODULE_STRING "-title-format" ),
1316                   psz_mrl, itemid );
1317   
1318   playlist_Add( p_playlist, psz_mrl, p_title, playlist_operation, i_pos );
1319
1320   p_author =
1321     VCDFormatStr( p_input, p_vcd,
1322                   config_GetPsz( p_input, MODULE_STRING "-author-format" ),
1323                   psz_mrl, itemid );
1324
1325   if( i_pos == PLAYLIST_END ) i_pos = p_playlist->i_size - 1;
1326   playlist_AddInfo(p_playlist, i_pos, _("General"), _("Author"), "%s",
1327                    p_author);
1328 }
1329
1330 static int
1331 VCDFixupPlayList( input_thread_t *p_input, thread_vcd_data_t *p_vcd,
1332                   const char *psz_source, vcdinfo_itemid_t *itemid,
1333                   vlc_bool_t play_single_item )
1334 {
1335   unsigned int i;
1336   playlist_t * p_playlist;
1337   char       * psz_mrl;
1338   unsigned int psz_mrl_max = strlen(VCD_MRL_PREFIX) + strlen(psz_source) +
1339     strlen("@T") + strlen("100") + 1;
1340
1341   psz_mrl = malloc( psz_mrl_max );
1342
1343   if( psz_mrl == NULL )
1344     {
1345       msg_Warn( p_input, "out of memory" );
1346       return -1;
1347     }
1348
1349   p_playlist = (playlist_t *) vlc_object_find( p_input, VLC_OBJECT_PLAYLIST,
1350                                                FIND_ANYWHERE );
1351   if( !p_playlist )
1352     {
1353       msg_Warn( p_input, "can't find playlist" );
1354       free(psz_mrl);
1355       return -1;
1356     }
1357
1358   if ( play_single_item ) 
1359     {
1360     /* May fill out more information when the playlist user interface becomes
1361        more mature.
1362      */
1363     VCDCreatePlayListItem(p_input, p_vcd, p_playlist, itemid,
1364                           psz_mrl, psz_mrl_max, psz_source, PLAYLIST_REPLACE,
1365                           p_playlist->i_index);
1366
1367     } 
1368   else 
1369     {
1370     vcdinfo_itemid_t list_itemid;
1371     list_itemid.type=VCDINFO_ITEM_TYPE_ENTRY;
1372
1373     playlist_Delete( p_playlist, p_playlist->i_index);
1374
1375     for( i = 0 ; i < p_vcd->num_entries ; i++ )
1376       {
1377         list_itemid.num=i;
1378         VCDCreatePlayListItem(p_input, p_vcd, p_playlist, &list_itemid,
1379                               psz_mrl, psz_mrl_max, psz_source,
1380                               PLAYLIST_APPEND, PLAYLIST_END);
1381       }
1382
1383     playlist_Command( p_playlist, PLAYLIST_GOTO, 0 );
1384
1385   }
1386
1387   vlc_object_release( p_playlist );
1388   free(psz_mrl);
1389   return 0;
1390 }
1391
1392 /*****************************************************************************
1393  * Public routines.
1394  *****************************************************************************/
1395 int
1396 E_(DebugCallback)   ( vlc_object_t *p_this, const char *psz_name,
1397                       vlc_value_t oldval, vlc_value_t val, void *p_data )
1398 {
1399   thread_vcd_data_t *p_vcd;
1400
1401   if (NULL == p_vcd_input) return VLC_EGENERIC;
1402
1403   p_vcd = (thread_vcd_data_t *)p_vcd_input->p_access_data;
1404
1405   if (p_vcd->i_debug & (INPUT_DBG_CALL|INPUT_DBG_EXT)) {
1406     msg_Dbg( p_vcd_input, "Old debug (x%0x) %d, new debug (x%0x) %d",
1407              p_vcd->i_debug, p_vcd->i_debug, val.i_int, val.i_int);
1408   }
1409   p_vcd->i_debug = val.i_int;
1410   return VLC_SUCCESS;
1411 }
1412
1413
1414 /*
1415   The front-ends change spu-es - which sort of represents a symbolic
1416   name. Internally we use spu-channel which runs from 0..3.
1417
1418   So we add a callback to intercept changes to spu-es and change them
1419   to updates to spu-channel. Ugly.
1420
1421 */
1422
1423 static int 
1424 VCDSPUCallback( vlc_object_t *p_this, const char *psz_variable,
1425                 vlc_value_t old_val, vlc_value_t new_val, void *param)
1426 {
1427
1428   input_thread_t    *p_input = (input_thread_t *)p_this;
1429   thread_vcd_data_t *p_vcd = (thread_vcd_data_t *)p_input->p_access_data;
1430   vlc_value_t val;
1431   
1432   dbg_print( (INPUT_DBG_CALL|INPUT_DBG_EVENT), "old_val: %x, new_val %x", 
1433              old_val.i_int, new_val.i_int);
1434
1435   val.i_int =  new_val.i_int;
1436   var_Set( p_this, "spu-channel", val );
1437   
1438   return VLC_SUCCESS;
1439 }
1440
1441 /*****************************************************************************
1442   Open: open VCD.
1443   read in meta-information about VCD: the number of tracks, segments,
1444   entries, size and starting information. Then set up state variables so
1445   that we read/seek starting at the location specified.
1446
1447   On success we return VLC_SUCCESS, on memory exhausted VLC_ENOMEM,
1448   and VLC_EGENERIC for some other error.
1449  *****************************************************************************/
1450 int
1451 E_(Open) ( vlc_object_t *p_this )
1452 {
1453     input_thread_t *        p_input = (input_thread_t *)p_this;
1454     thread_vcd_data_t *     p_vcd;
1455     char *                  psz_source;
1456     vcdinfo_itemid_t        itemid;
1457     vlc_bool_t              b_play_ok;
1458     vlc_bool_t              play_single_item = VLC_FALSE;
1459
1460     p_input->pf_read        = VCDRead;
1461     p_input->pf_seek        = VCDSeek;
1462     p_input->pf_set_area    = VCDSetArea;
1463     p_input->pf_set_program = VCDSetProgram;
1464
1465     p_vcd = malloc( sizeof(thread_vcd_data_t) );
1466
1467     if( p_vcd == NULL )
1468     {
1469         LOG_ERR ("out of memory" );
1470         return VLC_ENOMEM;
1471     }
1472
1473     p_input->p_access_data = (void *)p_vcd;
1474     p_vcd->i_debug         = config_GetInt( p_this, MODULE_STRING "-debug" );
1475     p_vcd->in_still        = VLC_FALSE;
1476     p_vcd->play_item.type  = VCDINFO_ITEM_TYPE_NOTFOUND;
1477
1478     /* Set where to log errors messages from libcdio. */
1479     p_vcd_input = (input_thread_t *)p_this;
1480     cdio_log_set_handler ( cdio_log_handler );
1481     vcd_log_set_handler ( vcd_log_handler );
1482
1483     psz_source = VCDParse( p_input, &itemid, &play_single_item );
1484
1485     if ( NULL == psz_source )
1486     {
1487       free( p_vcd );
1488       return( VLC_EGENERIC );
1489     }
1490
1491     dbg_print( (INPUT_DBG_CALL|INPUT_DBG_EXT), "source: %s: mrl: %s",
1492                psz_source, p_input->psz_name );
1493
1494     p_vcd->p_segments = NULL;
1495     p_vcd->p_entries  = NULL;
1496
1497     /* set up input  */
1498     p_input->i_mtu = VCD_DATA_ONCE;
1499
1500     vlc_mutex_lock( &p_input->stream.stream_lock );
1501
1502     /* If we are here we can control the pace... */
1503     p_input->stream.b_pace_control = 1;
1504
1505     p_input->stream.b_seekable = 1;
1506     p_input->stream.p_selected_area->i_size = 0;
1507     p_input->stream.p_selected_area->i_tell = 0;
1508
1509     vlc_mutex_unlock( &p_input->stream.stream_lock );
1510
1511     if( !(p_vcd->vcd = vcd_Open( p_this, psz_source )) )
1512     {
1513         msg_Warn( p_input, "could not open %s", psz_source );
1514         goto err_exit;
1515     }
1516
1517     p_vcd->b_svd= (vlc_bool_t) vcdinfo_get_tracksSVD(p_vcd->vcd);;
1518     
1519     /* Get track information. */
1520     p_vcd->num_tracks = ioctl_GetTracksMap( VLC_OBJECT(p_input),
1521                                             vcdinfo_get_cd_image(p_vcd->vcd),
1522                                             &p_vcd->p_sectors );
1523     if( p_vcd->num_tracks < 0 )
1524         LOG_ERR ("unable to count tracks" );
1525     else if( p_vcd->num_tracks <= 1 )
1526         LOG_ERR ("no movie tracks found" );
1527     if( p_vcd->num_tracks <= 1)
1528     {
1529         vcdinfo_close( p_vcd->vcd );
1530         goto err_exit;
1531     }
1532
1533     /* Set stream and area data */
1534     vlc_mutex_lock( &p_input->stream.stream_lock );
1535
1536     /* Initialize ES structures */
1537     input_InitStream( p_input, sizeof( stream_ps_data_t ) );
1538
1539     /* disc input method */
1540     p_input->stream.i_method = INPUT_METHOD_VCD;
1541
1542     p_input->stream.i_area_nb = 1;
1543
1544
1545     /* Initialize segment information. */
1546     VCDSegments( p_input );
1547
1548     /* Initialize track area information. */
1549     VCDTracks( p_input );
1550
1551     if( VCDEntryPoints( p_input ) < 0 )
1552     {
1553         msg_Warn( p_input, "could not read entry points, will not use them" );
1554         p_vcd->b_valid_ep = VLC_FALSE;
1555     }
1556
1557     if( VCDLIDs( p_input ) < 0 )
1558     {
1559         msg_Warn( p_input, "could not read entry LIDs" );
1560     }
1561
1562     b_play_ok = (VLC_SUCCESS == VCDPlay( p_input, itemid ));
1563
1564     vlc_mutex_unlock( &p_input->stream.stream_lock );
1565
1566     if ( ! b_play_ok ) {
1567       vcdinfo_close( p_vcd->vcd );
1568       goto err_exit;
1569     }
1570
1571     if( !p_input->psz_demux || !*p_input->psz_demux )
1572     {
1573 #if FIXED
1574       p_input->psz_demux = "vcdx";
1575 #else
1576       p_input->psz_demux = "ps";
1577 #endif
1578     }
1579
1580     p_vcd->p_intf = intf_Create( p_input, "vcdx" );
1581     p_vcd->p_intf->b_block = VLC_FALSE;
1582     intf_RunThread( p_vcd->p_intf );
1583
1584     InformationCreate( p_input );
1585
1586     if (play_single_item)
1587       VCDFixupPlayList( p_input, p_vcd, psz_source, &itemid, 
1588                         play_single_item );
1589
1590     free( psz_source );
1591
1592     var_AddCallback( p_this, "spu-es", VCDSPUCallback, NULL );
1593
1594
1595     return VLC_SUCCESS;
1596  err_exit:
1597     free( psz_source );
1598     free( p_vcd );
1599     return VLC_EGENERIC;
1600 }
1601
1602 /*****************************************************************************
1603  * Close: closes VCD releasing allocated memory.
1604  *****************************************************************************/
1605 void
1606 E_(Close) ( vlc_object_t *p_this )
1607 {
1608     input_thread_t *   p_input = (input_thread_t *)p_this;
1609     thread_vcd_data_t *p_vcd = (thread_vcd_data_t *)p_input->p_access_data;
1610
1611     dbg_print( (INPUT_DBG_CALL|INPUT_DBG_EXT), "VCDClose" );
1612
1613     var_DelCallback( p_this, "spu-es", VCDSPUCallback, NULL );
1614     vcdinfo_close( p_vcd->vcd );
1615
1616     free( p_vcd->p_entries );
1617     free( p_vcd->p_segments );
1618     free( p_vcd->p_sectors );
1619
1620     /* For reasons that are a mystery to me we don't have to deal with
1621        stopping, and destroying the p_vcd->p_intf thread. And if we do
1622        it causes problems upstream.
1623      */
1624     if( p_vcd->p_intf != NULL )
1625     {
1626         p_vcd->p_intf = NULL;
1627     }
1628
1629     free( p_vcd );
1630     p_input->p_access_data = NULL;
1631     p_vcd_input = NULL;
1632 }