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