]> git.sesse.net Git - vlc/blob - modules/access/vcdx/access.c
Small cosmetic changes
[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*/ bool *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             input_SetStatus( p_input, INPUT_STATUS_PAUSE );
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             input_SetStatus( p_input, INPUT_STATUS_PAUSE );
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     bool                    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  = 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 = true;
493             break;
494           default:
495             p_input->stream.b_seekable = 1;
496             p_vcd->in_still = 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    = 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         input_SetStatus( p_input, INPUT_STATUS_END );
568         input_SetStatus( p_input, INPUT_STATUS_PLAY );
569       }
570     }
571 #endif
572
573     p_vcd->play_item = itemid;
574
575     dbg_print( (INPUT_DBG_CALL),
576                "i_start %lld, i_size: %lld, i_tell: %lld, lsn %lu",
577                p_area->i_start, p_area->i_size,
578                p_area->i_tell, 
579                (long unsigned int) p_vcd->cur_lsn );
580
581     return VLC_SUCCESS;
582 }
583
584 /*****************************************************************************
585   VCDEntryPoints: Reads the information about the entry points on the disc
586   and initializes area information with that.
587   Before calling this track information should have been read in.
588  *****************************************************************************/
589 static int
590 VCDEntryPoints( input_thread_t * p_input )
591 {
592     thread_vcd_data_t *               p_vcd;
593     unsigned int                      i_nb;
594     unsigned int                      i, i_entry_index = 0;
595     unsigned int                      i_previous_track = CDIO_INVALID_TRACK;
596
597     p_vcd = (thread_vcd_data_t *) p_input->p_access_data;
598
599     i_nb = vcdinfo_get_num_entries(p_vcd->vcd);
600     if (0 == i_nb)
601       return -1;
602
603     p_vcd->p_entries  = malloc( sizeof( lba_t ) * i_nb );
604
605     if( p_vcd->p_entries == NULL )
606     {
607         LOG_ERR ("not enough memory for entry points treatment" );
608         return -1;
609     }
610
611     p_vcd->num_entries = 0;
612
613     for( i = 0 ; i < i_nb ; i++ )
614     {
615         track_t i_track = vcdinfo_get_track(p_vcd->vcd, i);
616         if( i_track <= p_input->stream.i_area_nb )
617         {
618             p_vcd->p_entries[i] =
619               vcdinfo_get_entry_lsn(p_vcd->vcd, i);
620             p_input->stream.pp_areas[i_track]->i_part_nb ++;
621
622             /* if this entry belongs to a new track */
623             if( i_track != i_previous_track )
624             {
625                 /* i_plugin_data is used to store the first entry of the area*/
626                 p_input->stream.pp_areas[i_track]->i_plugin_data =
627                                                             i_entry_index;
628                 i_previous_track = i_track;
629                 p_input->stream.pp_areas[i_track]->i_part_nb = 1;
630             }
631             i_entry_index ++;
632             p_vcd->num_entries ++;
633         }
634         else
635             msg_Warn( p_input, "wrong track number found in entry points" );
636     }
637     p_vcd->b_valid_ep = true;
638     return 0;
639 }
640
641 /*****************************************************************************
642  * VCDSegments: Reads the information about the segments the disc.
643  *****************************************************************************/
644 static int
645 VCDSegments( input_thread_t * p_input )
646 {
647     thread_vcd_data_t * p_vcd;
648     unsigned int        i;
649     unsigned int        num_segments;
650
651
652     p_vcd = (thread_vcd_data_t *) p_input->p_access_data;
653     num_segments = p_vcd->num_segments = vcdinfo_get_num_segments(p_vcd->vcd);
654
655 #define area p_input->stream.pp_areas
656
657     /* area 0 is reserved for segments. Set Absolute start offset
658          and size */
659     area[0]->i_plugin_data = 0;
660     input_DelArea( p_input, area[0] );
661     input_AddArea( p_input, 0, 0 );
662
663     area[0]->i_start = (off_t)p_vcd->p_sectors[0]
664       * (off_t)M2F2_SECTOR_SIZE;
665     area[0]->i_size = (off_t)(p_vcd->p_sectors[1] - p_vcd->p_sectors[0])
666       * (off_t)M2F2_SECTOR_SIZE;
667
668     /* Default Segment  */
669     area[0]->i_part = 0;
670
671     /* i_plugin_data is used to store which entry point is the first
672        of the track (area) */
673     area[0]->i_plugin_data = 0;
674
675     area[0]->i_part_nb = 0;
676
677     dbg_print( INPUT_DBG_MRL,
678                "area[0] id: %d, i_start: %lld, i_size: %lld",
679                area[0]->i_id, area[0]->i_start, area[0]->i_size );
680
681     if (num_segments == 0) return 0;
682
683     /* We have one additional segment allocated so we can get the size
684        by subtracting seg[i+1] - seg[i].
685      */
686     p_vcd->p_segments = malloc( sizeof( lba_t ) * (num_segments+1) );
687     if( p_vcd->p_segments == NULL )
688     {
689         LOG_ERR ("not enough memory for segment treatment" );
690         return -1;
691     }
692
693     /* Update the navigation variables without triggering a callback */
694     VCDUpdateVar( p_input, 0, VLC_VAR_SETVALUE, "title", _("Track"),
695                   "Setting track" );
696
697     var_Change( p_input, "chapter", VLC_VAR_CLEARCHOICES, NULL, NULL );
698
699     for( i = 0 ; i < num_segments ; i++ )
700     {
701       p_vcd->p_segments[i] = vcdinfo_get_seg_lsn(p_vcd->vcd, i);
702       area[0]->i_part_nb ++;
703       VCDUpdateVar( p_input, i , VLC_VAR_ADDCHOICE,
704                     "chapter", _("Segment"), "Adding segment choice");
705     }
706
707 #undef area
708
709     p_vcd->p_segments[num_segments] = p_vcd->p_segments[num_segments-1]+
710       vcdinfo_get_seg_sector_count(p_vcd->vcd, num_segments-1);
711
712     return 0;
713 }
714
715 /*****************************************************************************
716  VCDTracks: initializes area information.
717  Before calling this track information should have been read in.
718  *****************************************************************************/
719 static void
720 VCDTracks( input_thread_t * p_input )
721 {
722     thread_vcd_data_t * p_vcd;
723     unsigned int        i;
724
725     p_vcd = (thread_vcd_data_t *) p_input->p_access_data;
726
727 #define area p_input->stream.pp_areas
728
729     /* We start area addressing for tracks at 1 since the default area 0
730        is reserved for segments */
731
732     for( i = 1 ; i < p_vcd->num_tracks ; i++ )
733     {
734         /* Tracks are Program Chains */
735         input_AddArea( p_input, i, i );
736
737         /* Absolute start byte offset and byte size */
738         area[i]->i_start = (off_t) p_vcd->p_sectors[i]
739                            * (off_t)M2F2_SECTOR_SIZE;
740         area[i]->i_size = (off_t)(p_vcd->p_sectors[i+1] - p_vcd->p_sectors[i])
741                            * (off_t)M2F2_SECTOR_SIZE;
742
743         /* Current entry being played in track */
744         area[i]->i_part = 0;
745
746         /* i_plugin_data is used to store which entry point is the first
747          * of the track (area) */
748         area[i]->i_plugin_data = 0;
749
750         dbg_print( INPUT_DBG_MRL,
751                    "area[%d] id: %d, i_start: %lld, i_size: %lld",
752                    i, area[i]->i_id, area[i]->i_start, area[i]->i_size );
753     }
754
755 #undef area
756
757     return ;
758 }
759
760 /*****************************************************************************
761   VCDLIDs: Reads the LIST IDs from the LOT.
762  *****************************************************************************/
763 static int
764 VCDLIDs( input_thread_t * p_input )
765 {
766     thread_vcd_data_t *p_vcd = (thread_vcd_data_t *) p_input->p_access_data;
767
768     p_vcd->num_lids = vcdinfo_get_num_LIDs(p_vcd->vcd);
769     p_vcd->cur_lid  = VCDINFO_INVALID_ENTRY;
770
771     if (vcdinfo_read_psd (p_vcd->vcd)) {
772
773       vcdinfo_visit_lot (p_vcd->vcd, false);
774
775 #if FIXED
776     /*
777        We need to change libvcdinfo to be more robust when there are
778        problems reading the extended PSD. Given that area-highlighting and
779        selection features in the extended PSD haven't been implemented,
780        it's best then to not try to read this at all.
781      */
782       if (vcdinfo_get_psd_x_size(p_vcd->vcd))
783         vcdinfo_visit_lot (p_vcd->vcd, true);
784 #endif
785     }
786
787     dbg_print( (INPUT_DBG_CALL|INPUT_DBG_MRL),
788                "num LIDs=%d", p_vcd->num_lids);
789
790     return 0;
791 }
792
793 /*****************************************************************************
794  * VCDParse: parse command line
795  *****************************************************************************/
796 static char *
797 VCDParse( input_thread_t * p_input, /*out*/ vcdinfo_itemid_t * p_itemid,
798           /*out*/ bool *play_single_item )
799 {
800     thread_vcd_data_t *p_vcd = (thread_vcd_data_t *)p_input->p_access_data;
801     char *             psz_parser;
802     char *             psz_source;
803     char *             psz_next;
804
805     if ( config_GetInt( p_input, MODULE_STRING "-PBC" ) ) {
806       p_itemid->type=VCDINFO_ITEM_TYPE_LID;
807       p_itemid->num=1;
808       *play_single_item=false;
809     } else {
810       p_itemid->type=VCDINFO_ITEM_TYPE_ENTRY;
811       p_itemid->num=0;
812     }
813
814 #ifdef WIN32
815     /* On Win32 we want the VCD access plugin to be explicitly requested,
816      * we end up with lots of problems otherwise */
817     if( !p_input->psz_access || !*p_input->psz_access ) return NULL;
818 #endif
819
820     if( !p_input->psz_name )
821     {
822         return NULL;
823     }
824
825     psz_parser = psz_source = strdup( p_input->psz_name );
826
827     /* Parse input string :
828      * [device][@[type][title]] */
829     while( *psz_parser && *psz_parser != '@' )
830     {
831         psz_parser++;
832     }
833
834     if( *psz_parser == '@' )
835     {
836       /* Found the divide between the source name and the
837          type+entry number. */
838       unsigned int num;
839
840       *psz_parser = '\0';
841       ++psz_parser;
842       if( *psz_parser )
843         {
844           switch(*psz_parser) {
845           case 'E':
846             p_itemid->type = VCDINFO_ITEM_TYPE_ENTRY;
847             ++psz_parser;
848             *play_single_item = true;
849             break;
850           case 'P':
851             p_itemid->type = VCDINFO_ITEM_TYPE_LID;
852             ++psz_parser;
853             *play_single_item = false;
854             break;
855           case 'S':
856             p_itemid->type = VCDINFO_ITEM_TYPE_SEGMENT;
857             ++psz_parser;
858             *play_single_item = true;
859             break;
860           case 'T':
861             p_itemid->type = VCDINFO_ITEM_TYPE_TRACK;
862             ++psz_parser;
863             *play_single_item = true;
864             break;
865           default: ;
866           }
867         }
868
869       num = strtol( psz_parser, &psz_next, 10 );
870       if ( *psz_parser != '\0' && *psz_next == '\0')
871         {
872           p_itemid->num = num;
873         }
874
875     } else {
876       *play_single_item = ( VCDINFO_ITEM_TYPE_LID == p_itemid->type );
877     }
878
879
880     if( !*psz_source ) {
881
882       /* No source specified, so figure it out. */
883       if( !p_input->psz_access ) return NULL;
884
885       psz_source = config_GetPsz( p_input, "vcd" );
886
887       if( !psz_source || 0==strlen(psz_source) ) {
888         /* Scan for a CD-ROM drive with a VCD in it. */
889         char **cd_drives = cdio_get_devices_with_cap(NULL,
890                             (CDIO_FS_ANAL_SVCD|CDIO_FS_ANAL_CVD
891                              |CDIO_FS_ANAL_VIDEOCD|CDIO_FS_UNKNOWN),
892                                                      true);
893         if (NULL == cd_drives) return NULL;
894         if (cd_drives[0] == NULL) {
895           cdio_free_device_list(cd_drives);
896           return NULL;
897         }
898         psz_source = strdup(cd_drives[0]);
899         cdio_free_device_list(cd_drives);
900       }
901     }
902
903     dbg_print( (INPUT_DBG_CALL|INPUT_DBG_MRL),
904                "source=%s entry=%d type=%d",
905                psz_source, p_itemid->num, p_itemid->type);
906
907     return psz_source;
908 }
909
910 /*
911    Set's start origin subsequent seeks/reads
912 */
913 static void
914 VCDSetOrigin( input_thread_t *p_input, lsn_t origin_lsn,
915               lsn_t cur_lsn, lsn_t end_lsn, int cur_entry, track_t cur_track )
916 {
917   thread_vcd_data_t * p_vcd  = (thread_vcd_data_t *) p_input->p_access_data;
918
919   p_vcd->origin_lsn = origin_lsn;
920   p_vcd->cur_lsn    = cur_lsn;
921   p_vcd->end_lsn    = end_lsn;
922   p_vcd->cur_track  = cur_track;
923   p_vcd->play_item.num  = cur_entry;
924   p_vcd->play_item.type = VCDINFO_ITEM_TYPE_ENTRY;
925
926   dbg_print( (INPUT_DBG_CALL|INPUT_DBG_LSN),
927              "origin: %lu, cur_lsn: %lu, end_lsn: %lu, entry: %d, track: %d",
928              (long unsigned int) origin_lsn, 
929              (long unsigned int) cur_lsn, 
930              (long unsigned int) end_lsn, cur_entry, cur_track );
931
932   p_input->stream.p_selected_area->i_tell =
933     (off_t) (p_vcd->cur_lsn - p_vcd->origin_lsn) * (off_t)M2F2_SECTOR_SIZE;
934
935   VCDUpdateVar( p_input, cur_entry, VLC_VAR_SETVALUE,
936                 "chapter", 
937                 p_vcd->play_item.type == VCDINFO_ITEM_TYPE_ENTRY ?
938                 _("Entry") : _("Segment"), 
939                 "Setting entry/segment");
940 }
941
942 /*****************************************************************************
943  * vcd_Open: Opens a VCD device or file and returns an opaque handle
944  *****************************************************************************/
945 static vcdinfo_obj_t *
946 vcd_Open( vlc_object_t *p_this, const char *psz_dev )
947 {
948     vcdinfo_obj_t *p_vcdobj;
949     char  *actual_dev;
950
951     if( !psz_dev ) return NULL;
952
953     actual_dev=strdup(psz_dev);
954     if ( vcdinfo_open(&p_vcdobj, &actual_dev, DRIVER_UNKNOWN, NULL) !=
955          VCDINFO_OPEN_VCD) {
956       free(actual_dev);
957       return NULL;
958     }
959     free(actual_dev);
960
961     return p_vcdobj;
962 }
963
964 /****************************************************************************
965  * VCDReadSector: Read a sector (2324 bytes)
966  ****************************************************************************/
967 static int
968 VCDReadSector( vlc_object_t *p_this, const vcdinfo_obj_t *p_vcd,
969                lsn_t cur_lsn, byte_t * p_buffer )
970 {
971   typedef struct {
972     uint8_t subheader   [8];
973     uint8_t data        [M2F2_SECTOR_SIZE];
974     uint8_t spare       [4];
975   } vcdsector_t;
976   vcdsector_t vcd_sector;
977
978   if (cdio_read_mode2_sector(vcdinfo_get_cd_image(p_vcd),
979                              &vcd_sector, cur_lsn, true)
980       != 0)
981     {
982       msg_Warn( p_this, "Could not read LSN %lu", 
983                 (long unsigned int) cur_lsn );
984       return -1;
985     }
986
987   memcpy (p_buffer, vcd_sector.data, M2F2_SECTOR_SIZE);
988
989   return( 0 );
990 }
991
992 /****************************************************************************
993  Update the "varname" variable to i_num without triggering a callback.
994 ****************************************************************************/
995 static void
996 VCDUpdateVar( input_thread_t *p_input, int i_num, int i_action,
997               const char *p_varname, char *p_label, 
998               const char *p_debug_label)
999 {
1000   vlc_value_t val;
1001   val.i_int = i_num;
1002   if (NULL != p_vcd_input) {
1003     thread_vcd_data_t *p_vcd = (thread_vcd_data_t *)p_vcd_input->p_access_data;
1004     dbg_print( INPUT_DBG_PBC, "%s %d", p_debug_label, i_num );
1005   }
1006   if (p_label) {
1007     vlc_value_t text;
1008     text.psz_string = p_label;
1009     var_Change( p_input, p_varname, VLC_VAR_SETTEXT, &text, NULL );
1010   }
1011   var_Change( p_input, p_varname, i_action, &val, NULL );
1012 }
1013
1014
1015 static inline void
1016 MetaInfoAddStr(input_thread_t *p_input, input_info_category_t *p_cat,
1017                playlist_t *p_playlist, char *title,
1018                const char *str)
1019 {
1020   thread_vcd_data_t *p_vcd = (thread_vcd_data_t *) p_input->p_access_data;
1021   playlist_item_t *p_item;
1022   if ( str ) {
1023     dbg_print( INPUT_DBG_META, "field: %s: %s\n", title, str);
1024     input_AddInfo( p_cat, title, "%s", str );
1025
1026     vlc_mutex_lock( &p_playlist->object_lock );
1027     p_item = playlist_ItemGetByPos( p_playlist, -1 );
1028     vlc_mutex_unlock( &p_playlist->object_lock );
1029
1030     vlc_mutex_lock( &p_item->lock );
1031     playlist_ItemAddInfo( p_item, p_cat->psz_name, title,
1032                           "%s",str );
1033     vlc_mutex_unlock( &p_item->lock );
1034   }
1035 }
1036
1037
1038 static inline void
1039 MetaInfoAddNum(input_thread_t *p_input, input_info_category_t *p_cat,
1040                playlist_t *p_playlist, char *title, int num)
1041 {
1042   thread_vcd_data_t *p_vcd = (thread_vcd_data_t *) p_input->p_access_data;
1043   playlist_item_t *p_item;
1044
1045   vlc_mutex_lock( &p_playlist->object_lock );
1046   p_item = playlist_ItemGetByPos( p_playlist, -1 );
1047   vlc_mutex_unlock( &p_playlist->object_lock );
1048
1049   dbg_print( INPUT_DBG_META, "field %s: %d\n", title, num);
1050   input_AddInfo( p_cat, title, "%d", num );
1051
1052   vlc_mutex_lock( &p_item->lock );
1053   playlist_ItemAddInfo( p_item ,  p_cat->psz_name, title, "%d",num );
1054   vlc_mutex_unlock( &p_item->lock );
1055 }
1056
1057 #define addstr(title, str) \
1058   MetaInfoAddStr( p_input, p_cat, p_playlist, title, str );
1059
1060 #define addnum(title, num) \
1061   MetaInfoAddNum( p_input, p_cat, p_playlist, title, num );
1062
1063 static void InformationCreate( input_thread_t *p_input  )
1064 {
1065   thread_vcd_data_t *p_vcd = (thread_vcd_data_t *) p_input->p_access_data;
1066   unsigned int i_nb = vcdinfo_get_num_entries(p_vcd->vcd);
1067   unsigned int last_entry = 0;
1068   input_info_category_t *p_cat;
1069   track_t i_track;
1070   playlist_t *p_playlist = vlc_object_find( p_input, VLC_OBJECT_PLAYLIST,
1071                                                  FIND_PARENT );
1072
1073   p_cat = input_InfoCategory( p_input, "General" );
1074
1075   addstr( _("VCD Format"),  vcdinfo_get_format_version_str(p_vcd->vcd) );
1076   addstr( _("Album"),       vcdinfo_get_album_id(p_vcd->vcd));
1077   addstr( _("Application"), vcdinfo_get_application_id(p_vcd->vcd) );
1078   addstr( _("Preparer"),    vcdinfo_get_preparer_id(p_vcd->vcd) );
1079   addnum( _("Vol #"),       vcdinfo_get_volume_num(p_vcd->vcd) );
1080   addnum( _("Vol max #"),   vcdinfo_get_volume_count(p_vcd->vcd) );
1081   addstr( _("Volume Set"),  vcdinfo_get_volumeset_id(p_vcd->vcd) );
1082   addstr( _("Volume"),      vcdinfo_get_volume_id(p_vcd->vcd) );
1083   addstr( _("Publisher"),   vcdinfo_get_publisher_id(p_vcd->vcd) );
1084   addstr( _("System Id"),   vcdinfo_get_system_id(p_vcd->vcd) );
1085   addnum( "LIDs",           vcdinfo_get_num_LIDs(p_vcd->vcd) );
1086   addnum( _("Entries"),     vcdinfo_get_num_entries(p_vcd->vcd) );
1087   addnum( _("Segments"),    vcdinfo_get_num_segments(p_vcd->vcd) );
1088   addnum( _("Tracks"),      vcdinfo_get_num_tracks(p_vcd->vcd) );
1089
1090   /* Spit out track information. Could also include MSF info.
1091    */
1092
1093 #define TITLE_MAX 30
1094   for( i_track = 1 ; i_track < p_vcd->num_tracks ; i_track++ ) {
1095     char psz_track[TITLE_MAX];
1096     unsigned int audio_type = vcdinfo_get_track_audio_type(p_vcd->vcd, 
1097                                                            i_track);
1098     snprintf(psz_track, TITLE_MAX, "%s%02d", _("Track "), i_track);
1099     p_cat = input_InfoCategory( p_input, psz_track );
1100
1101     if (p_vcd->b_svd) {
1102       addnum(_("Audio Channels"),  
1103              vcdinfo_audio_type_num_channels(p_vcd->vcd, audio_type) );
1104     }
1105
1106     addnum(_("First Entry Point"), last_entry );
1107     for ( ; last_entry < i_nb 
1108             && vcdinfo_get_track(p_vcd->vcd, last_entry) == i_track;
1109           last_entry++ ) ;
1110     addnum(_("Last Entry Point"), last_entry-1 );
1111   }
1112 }
1113
1114 #define add_format_str_info(val)                               \
1115   {                                                            \
1116     const char *str = val;                                     \
1117     unsigned int len;                                          \
1118     if (val != NULL) {                                         \
1119       len=strlen(str);                                         \
1120       if (len != 0) {                                          \
1121         strncat(tp, str, TEMP_STR_LEN-(tp-temp_str));          \
1122         tp += len;                                             \
1123       }                                                        \
1124       saw_control_prefix = false;                              \
1125     }                                                          \
1126   }
1127
1128 #define add_format_num_info(val, fmt)                          \
1129   {                                                            \
1130     char num_str[10];                                          \
1131     unsigned int len;                                          \
1132     sprintf(num_str, fmt, val);                                \
1133     len=strlen(num_str);                                       \
1134     if (len != 0) {                                            \
1135       strncat(tp, num_str, TEMP_STR_LEN-(tp-temp_str));        \
1136       tp += len;                                               \
1137     }                                                          \
1138     saw_control_prefix = false;                                \
1139   }
1140
1141 /*!
1142    Take a format string and expand escape sequences, that is sequences that
1143    begin with %, with information from the current VCD.
1144    The expanded string is returned. Here is a list of escape sequences:
1145
1146    %A : The album information
1147    %C : The VCD volume count - the number of CD's in the collection.
1148    %c : The VCD volume num - the number of the CD in the collection.
1149    %F : The VCD Format, e.g. VCD 1.0, VCD 1.1, VCD 2.0, or SVCD
1150    %I : The current entry/segment/playback type, e.g. ENTRY, TRACK, SEGMENT...
1151    %L : The playlist ID prefixed with " LID" if it exists
1152    %M : MRL
1153    %N : The current number of the %I - a decimal number
1154    %P : The publisher ID
1155    %p : The preparer ID
1156    %S : If we are in a segment (menu), the kind of segment
1157    %T : The track number
1158    %V : The volume set ID
1159    %v : The volume ID
1160        A number between 1 and the volume count.
1161    %% : a %
1162 */
1163 static char *
1164 VCDFormatStr(const input_thread_t *p_input, thread_vcd_data_t *p_vcd,
1165              const char format_str[], const char *mrl,
1166              const vcdinfo_itemid_t *itemid)
1167 {
1168 #define TEMP_STR_SIZE 256
1169 #define TEMP_STR_LEN (TEMP_STR_SIZE-1)
1170   static char    temp_str[TEMP_STR_SIZE];
1171   size_t i;
1172   char * tp = temp_str;
1173   bool saw_control_prefix = false;
1174   size_t format_len = strlen(format_str);
1175
1176   memset(temp_str, 0, TEMP_STR_SIZE);
1177
1178   for (i=0; i<format_len; i++) {
1179
1180     if (!saw_control_prefix && format_str[i] != '%') {
1181       *tp++ = format_str[i];
1182       saw_control_prefix = false;
1183       continue;
1184     }
1185
1186     switch(format_str[i]) {
1187     case '%':
1188       if (saw_control_prefix) {
1189         *tp++ = '%';
1190       }
1191       saw_control_prefix = !saw_control_prefix;
1192       break;
1193     case 'A':
1194       add_format_str_info(vcdinfo_strip_trail(vcdinfo_get_album_id(p_vcd->vcd),
1195                                               MAX_ALBUM_LEN));
1196       break;
1197
1198     case 'c':
1199       add_format_num_info(vcdinfo_get_volume_num(p_vcd->vcd), "%d");
1200       break;
1201
1202     case 'C':
1203       add_format_num_info(vcdinfo_get_volume_count(p_vcd->vcd), "%d");
1204       break;
1205
1206     case 'F':
1207       add_format_str_info(vcdinfo_get_format_version_str(p_vcd->vcd));
1208       break;
1209
1210     case 'I':
1211       {
1212         switch (itemid->type) {
1213         case VCDINFO_ITEM_TYPE_TRACK:
1214           strncat(tp, _("Track"), TEMP_STR_LEN-(tp-temp_str));
1215           tp += strlen(_("Track"));
1216         break;
1217         case VCDINFO_ITEM_TYPE_ENTRY:
1218           strncat(tp, _("Entry"), TEMP_STR_LEN-(tp-temp_str));
1219           tp += strlen(_("Entry"));
1220           break;
1221         case VCDINFO_ITEM_TYPE_SEGMENT:
1222           strncat(tp, _("Segment"), TEMP_STR_LEN-(tp-temp_str));
1223           tp += strlen(_("Segment"));
1224           break;
1225         case VCDINFO_ITEM_TYPE_LID:
1226           strncat(tp, _("List ID"), TEMP_STR_LEN-(tp-temp_str));
1227           tp += strlen(_("List ID"));
1228           break;
1229         case VCDINFO_ITEM_TYPE_SPAREID2:
1230           strncat(tp, _("Navigation"), TEMP_STR_LEN-(tp-temp_str));
1231           tp += strlen(_("Navigation"));
1232           break;
1233         default:
1234           /* What to do? */
1235           ;
1236         }
1237         saw_control_prefix = false;
1238       }
1239       break;
1240
1241     case 'L':
1242       if (vcdplayer_pbc_is_on(p_vcd)) {
1243         char num_str[40];
1244         sprintf(num_str, "%s %d", _("List ID"), p_vcd->cur_lid);
1245         strncat(tp, num_str, TEMP_STR_LEN-(tp-temp_str));
1246         tp += strlen(num_str);
1247       }
1248       saw_control_prefix = false;
1249       break;
1250
1251     case 'M':
1252       add_format_str_info(mrl);
1253       break;
1254
1255     case 'N':
1256       add_format_num_info(itemid->num, "%d");
1257       break;
1258
1259     case 'p':
1260       add_format_str_info(vcdinfo_get_preparer_id(p_vcd->vcd));
1261       break;
1262
1263     case 'P':
1264       add_format_str_info(vcdinfo_get_publisher_id(p_vcd->vcd));
1265       break;
1266
1267     case 'S':
1268       if ( VCDINFO_ITEM_TYPE_SEGMENT==itemid->type ) {
1269         char seg_type_str[10];
1270
1271         sprintf(seg_type_str, " %s",
1272                 vcdinfo_video_type2str(p_vcd->vcd, itemid->num));
1273         strncat(tp, seg_type_str, TEMP_STR_LEN-(tp-temp_str));
1274         tp += strlen(seg_type_str);
1275       }
1276       saw_control_prefix = false;
1277       break;
1278
1279     case 'T':
1280       add_format_num_info(p_vcd->cur_track, "%d");
1281       break;
1282
1283     case 'V':
1284       add_format_str_info(vcdinfo_get_volumeset_id(p_vcd->vcd));
1285       break;
1286
1287     case 'v':
1288       add_format_str_info(vcdinfo_get_volume_id(p_vcd->vcd));
1289       break;
1290
1291     default:
1292       *tp++ = '%';
1293       *tp++ = format_str[i];
1294       saw_control_prefix = false;
1295     }
1296   }
1297   return strdup(temp_str);
1298 }
1299
1300 static void
1301 VCDCreatePlayListItem(const input_thread_t *p_input,
1302                       thread_vcd_data_t *p_vcd,
1303                       playlist_t *p_playlist,
1304                       const vcdinfo_itemid_t *itemid,
1305                       char *psz_mrl, int psz_mrl_max,
1306                       const char *psz_source, int playlist_operation,
1307                       int i_pos)
1308 {
1309   char *p_author;
1310   char *p_title;
1311   char c_type;
1312
1313   switch(itemid->type) {
1314   case VCDINFO_ITEM_TYPE_TRACK:
1315     c_type='T';
1316     break;
1317   case VCDINFO_ITEM_TYPE_SEGMENT:
1318     c_type='S';
1319     break;
1320   case VCDINFO_ITEM_TYPE_LID:
1321     c_type='P';
1322     break;
1323   case VCDINFO_ITEM_TYPE_ENTRY:
1324     c_type='E';
1325     break;
1326   default:
1327     c_type='?';
1328     break;
1329   }
1330
1331   snprintf(psz_mrl, psz_mrl_max, "%s%s@%c%u", VCD_MRL_PREFIX, psz_source,
1332            c_type, itemid->num);
1333
1334   p_title =
1335     VCDFormatStr( p_input, p_vcd,
1336                   config_GetPsz( p_input, MODULE_STRING "-title-format" ),
1337                   psz_mrl, itemid );
1338   
1339   playlist_Add( p_playlist, psz_mrl, p_title, playlist_operation, i_pos );
1340
1341   p_author =
1342     VCDFormatStr( p_input, p_vcd,
1343                   config_GetPsz( p_input, MODULE_STRING "-author-format" ),
1344                   psz_mrl, itemid );
1345
1346   if( i_pos == PLAYLIST_END ) i_pos = p_playlist->i_size - 1;
1347   playlist_AddInfo(p_playlist, i_pos, _("General"), _("Author"), "%s",
1348                    p_author);
1349 }
1350
1351 static int
1352 VCDFixupPlayList( input_thread_t *p_input, thread_vcd_data_t *p_vcd,
1353                   const char *psz_source, vcdinfo_itemid_t *itemid,
1354                   bool play_single_item )
1355 {
1356   unsigned int i;
1357   playlist_t * p_playlist;
1358   char       * psz_mrl;
1359   unsigned int psz_mrl_max = strlen(VCD_MRL_PREFIX) + strlen(psz_source) +
1360     strlen("@T") + strlen("100") + 1;
1361
1362   psz_mrl = malloc( psz_mrl_max );
1363
1364   if( psz_mrl == NULL )
1365     {
1366       msg_Warn( p_input, "out of memory" );
1367       return -1;
1368     }
1369
1370   p_playlist = (playlist_t *) vlc_object_find( p_input, VLC_OBJECT_PLAYLIST,
1371                                                FIND_ANYWHERE );
1372   if( !p_playlist )
1373     {
1374       msg_Warn( p_input, "can't find playlist" );
1375       free(psz_mrl);
1376       return -1;
1377     }
1378
1379   if ( play_single_item ) 
1380     {
1381     /* May fill out more information when the playlist user interface becomes
1382        more mature.
1383      */
1384     VCDCreatePlayListItem(p_input, p_vcd, p_playlist, itemid,
1385                           psz_mrl, psz_mrl_max, psz_source, PLAYLIST_REPLACE,
1386                           p_playlist->i_index);
1387
1388     } 
1389   else 
1390     {
1391     vcdinfo_itemid_t list_itemid;
1392     list_itemid.type=VCDINFO_ITEM_TYPE_ENTRY;
1393
1394     playlist_Delete( p_playlist, p_playlist->i_index);
1395
1396     for( i = 0 ; i < p_vcd->num_entries ; i++ )
1397       {
1398         list_itemid.num=i;
1399         VCDCreatePlayListItem(p_input, p_vcd, p_playlist, &list_itemid,
1400                               psz_mrl, psz_mrl_max, psz_source,
1401                               PLAYLIST_APPEND, PLAYLIST_END);
1402       }
1403
1404     playlist_Command( p_playlist, PLAYLIST_GOTO, 0 );
1405
1406   }
1407
1408   vlc_object_release( p_playlist );
1409   free(psz_mrl);
1410   return 0;
1411 }
1412
1413 /*****************************************************************************
1414  * Public routines.
1415  *****************************************************************************/
1416 int
1417 E_(DebugCallback)   ( vlc_object_t *p_this, const char *psz_name,
1418                       vlc_value_t oldval, vlc_value_t val, void *p_data )
1419 {
1420   thread_vcd_data_t *p_vcd;
1421
1422   if (NULL == p_vcd_input) return VLC_EGENERIC;
1423
1424   p_vcd = (thread_vcd_data_t *)p_vcd_input->p_access_data;
1425
1426   if (p_vcd->i_debug & (INPUT_DBG_CALL|INPUT_DBG_EXT)) {
1427     msg_Dbg( p_vcd_input, "Old debug (x%0x) %d, new debug (x%0x) %d",
1428              p_vcd->i_debug, p_vcd->i_debug, val.i_int, val.i_int);
1429   }
1430   p_vcd->i_debug = val.i_int;
1431   return VLC_SUCCESS;
1432 }
1433
1434
1435 /*
1436   The front-ends change spu-es - which sort of represents a symbolic
1437   name. Internally we use spu-channel which runs from 0..3.
1438
1439   So we add a callback to intercept changes to spu-es and change them
1440   to updates to spu-channel. Ugly.
1441
1442 */
1443
1444 static int 
1445 VCDSPUCallback( vlc_object_t *p_this, const char *psz_variable,
1446                 vlc_value_t old_val, vlc_value_t new_val, void *param)
1447 {
1448
1449   input_thread_t    *p_input = (input_thread_t *)p_this;
1450   thread_vcd_data_t *p_vcd = (thread_vcd_data_t *)p_input->p_access_data;
1451   vlc_value_t val;
1452   
1453   dbg_print( (INPUT_DBG_CALL|INPUT_DBG_EVENT), "old_val: %x, new_val %x", 
1454              old_val.i_int, new_val.i_int);
1455
1456   val.i_int =  new_val.i_int;
1457   var_Set( p_this, "spu-channel", val );
1458   
1459   return VLC_SUCCESS;
1460 }
1461
1462 /*****************************************************************************
1463   Open: open VCD.
1464   read in meta-information about VCD: the number of tracks, segments,
1465   entries, size and starting information. Then set up state variables so
1466   that we read/seek starting at the location specified.
1467
1468   On success we return VLC_SUCCESS, on memory exhausted VLC_ENOMEM,
1469   and VLC_EGENERIC for some other error.
1470  *****************************************************************************/
1471 int
1472 E_(Open) ( vlc_object_t *p_this )
1473 {
1474     input_thread_t *        p_input = (input_thread_t *)p_this;
1475     thread_vcd_data_t *     p_vcd;
1476     char *                  psz_source;
1477     vcdinfo_itemid_t        itemid;
1478     bool                    b_play_ok;
1479     bool                    play_single_item = false;
1480
1481     p_input->pf_read        = VCDRead;
1482     p_input->pf_seek        = VCDSeek;
1483     p_input->pf_set_area    = VCDSetArea;
1484     p_input->pf_set_program = VCDSetProgram;
1485
1486     p_vcd = malloc( sizeof(thread_vcd_data_t) );
1487
1488     if( p_vcd == NULL )
1489     {
1490         LOG_ERR ("out of memory" );
1491         return VLC_ENOMEM;
1492     }
1493
1494     p_input->p_access_data = (void *)p_vcd;
1495     p_vcd->i_debug         = config_GetInt( p_this, MODULE_STRING "-debug" );
1496     p_vcd->in_still        = false;
1497     p_vcd->play_item.type  = VCDINFO_ITEM_TYPE_NOTFOUND;
1498
1499     /* Set where to log errors messages from libcdio. */
1500     p_vcd_input = (input_thread_t *)p_this;
1501     cdio_log_set_handler ( cdio_log_handler );
1502     vcd_log_set_handler ( vcd_log_handler );
1503
1504     psz_source = VCDParse( p_input, &itemid, &play_single_item );
1505
1506     if ( NULL == psz_source )
1507     {
1508       free( p_vcd );
1509       return( VLC_EGENERIC );
1510     }
1511
1512     dbg_print( (INPUT_DBG_CALL|INPUT_DBG_EXT), "source: %s: mrl: %s",
1513                psz_source, p_input->psz_name );
1514
1515     p_vcd->p_segments = NULL;
1516     p_vcd->p_entries  = NULL;
1517
1518     /* set up input  */
1519     p_input->i_mtu = VCD_DATA_ONCE;
1520
1521     vlc_mutex_lock( &p_input->stream.stream_lock );
1522
1523     /* If we are here we can control the pace... */
1524     p_input->stream.b_pace_control = 1;
1525
1526     p_input->stream.b_seekable = 1;
1527     p_input->stream.p_selected_area->i_size = 0;
1528     p_input->stream.p_selected_area->i_tell = 0;
1529
1530     vlc_mutex_unlock( &p_input->stream.stream_lock );
1531
1532     if( !(p_vcd->vcd = vcd_Open( p_this, psz_source )) )
1533     {
1534         msg_Warn( p_input, "could not open %s", psz_source );
1535         goto err_exit;
1536     }
1537
1538     p_vcd->b_svd= vcdinfo_get_tracksSVD(p_vcd->vcd);;
1539     
1540     /* Get track information. */
1541     p_vcd->num_tracks = ioctl_GetTracksMap( VLC_OBJECT(p_input),
1542                                             vcdinfo_get_cd_image(p_vcd->vcd),
1543                                             &p_vcd->p_sectors );
1544     if( p_vcd->num_tracks < 0 )
1545         LOG_ERR ("unable to count tracks" );
1546     else if( p_vcd->num_tracks <= 1 )
1547         LOG_ERR ("no movie tracks found" );
1548     if( p_vcd->num_tracks <= 1)
1549     {
1550         vcdinfo_close( p_vcd->vcd );
1551         goto err_exit;
1552     }
1553
1554     /* Set stream and area data */
1555     vlc_mutex_lock( &p_input->stream.stream_lock );
1556
1557     /* Initialize ES structures */
1558     input_InitStream( p_input, sizeof( stream_ps_data_t ) );
1559
1560     /* disc input method */
1561     p_input->stream.i_method = INPUT_METHOD_VCD;
1562
1563     p_input->stream.i_area_nb = 1;
1564
1565
1566     /* Initialize segment information. */
1567     VCDSegments( p_input );
1568
1569     /* Initialize track area information. */
1570     VCDTracks( p_input );
1571
1572     if( VCDEntryPoints( p_input ) < 0 )
1573     {
1574         msg_Warn( p_input, "could not read entry points, will not use them" );
1575         p_vcd->b_valid_ep = false;
1576     }
1577
1578     if( VCDLIDs( p_input ) < 0 )
1579     {
1580         msg_Warn( p_input, "could not read entry LIDs" );
1581     }
1582
1583     b_play_ok = (VLC_SUCCESS == VCDPlay( p_input, itemid ));
1584
1585     vlc_mutex_unlock( &p_input->stream.stream_lock );
1586
1587     if ( ! b_play_ok ) {
1588       vcdinfo_close( p_vcd->vcd );
1589       goto err_exit;
1590     }
1591
1592     if( !p_input->psz_demux || !*p_input->psz_demux )
1593     {
1594 #if FIXED
1595       p_input->psz_demux = "vcdx";
1596 #else
1597       p_input->psz_demux = "ps";
1598 #endif
1599     }
1600
1601     p_vcd->p_intf = intf_Create( p_input, "vcdx" );
1602     p_vcd->p_intf->b_block = VLC_FALSE;
1603     intf_RunThread( p_vcd->p_intf );
1604
1605     InformationCreate( p_input );
1606
1607     if (play_single_item)
1608       VCDFixupPlayList( p_input, p_vcd, psz_source, &itemid, 
1609                         play_single_item );
1610
1611     free( psz_source );
1612
1613     var_AddCallback( p_this, "spu-es", VCDSPUCallback, NULL );
1614
1615
1616     return VLC_SUCCESS;
1617  err_exit:
1618     free( psz_source );
1619     free( p_vcd );
1620     return VLC_EGENERIC;
1621 }
1622
1623 /*****************************************************************************
1624  * Close: closes VCD releasing allocated memory.
1625  *****************************************************************************/
1626 void
1627 E_(Close) ( vlc_object_t *p_this )
1628 {
1629     input_thread_t *   p_input = (input_thread_t *)p_this;
1630     thread_vcd_data_t *p_vcd = (thread_vcd_data_t *)p_input->p_access_data;
1631
1632     dbg_print( (INPUT_DBG_CALL|INPUT_DBG_EXT), "VCDClose" );
1633
1634     var_DelCallback( p_this, "spu-es", VCDSPUCallback, NULL );
1635     vcdinfo_close( p_vcd->vcd );
1636
1637     free( p_vcd->p_entries );
1638     free( p_vcd->p_segments );
1639     free( p_vcd->p_sectors );
1640
1641     /* For reasons that are a mystery to me we don't have to deal with
1642        stopping, and destroying the p_vcd->p_intf thread. And if we do
1643        it causes problems upstream.
1644      */
1645     if( p_vcd->p_intf != NULL )
1646     {
1647         p_vcd->p_intf = NULL;
1648     }
1649
1650     free( p_vcd );
1651     p_input->p_access_data = NULL;
1652     p_vcd_input = NULL;
1653 }