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