]> git.sesse.net Git - vlc/blob - modules/access/vcdx/access.c
Make more like the French translation which is more correct. :-\
[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 VideoLAN
7  * $Id: access.c,v 1.7 2003/12/03 21:55:33 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_interface.h>
34
35 #include "../../demux/mpeg/system.h"
36 #include "vcd.h"
37 #include "intf.h"
38 #include "vcdplayer.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 /*****************************************************************************
54  * Local prototypes
55  *****************************************************************************/
56
57 /* First those which are accessed from outside (via pointers). */
58 static int  VCDRead         ( input_thread_t *, byte_t *, size_t );
59 static int  VCDSetProgram   ( input_thread_t *, pgrm_descriptor_t * );
60
61 /* Now those which are strictly internal */
62 static void VCDSetOrigin    ( input_thread_t *, lsn_t origin_lsn, 
63                               lsn_t cur_lsn, lsn_t end_lsn, 
64                               int cur_entry, track_t track );
65 static int  VCDEntryPoints  ( input_thread_t * );
66 static int  VCDLIDs         ( input_thread_t * );
67 static int  VCDSegments     ( input_thread_t * );
68 static void VCDTracks       ( input_thread_t * );
69 static int  VCDReadSector   ( vlc_object_t *p_this, 
70                               const vcdinfo_obj_t *p_vcd, lsn_t cur_lsn, 
71                               byte_t * p_buffer );
72 static char *VCDParse       ( input_thread_t *, 
73                               /*out*/ vcdinfo_itemid_t * p_itemid );
74
75 static void VCDUpdateVar( input_thread_t *p_input, int i_entry, int i_action,
76                           const char *varname, const char *label );
77
78 static vcdinfo_obj_t *vcd_Open   ( vlc_object_t *p_this, const char *psz_dev );
79
80 /****************************************************************************
81  * Private functions
82  ****************************************************************************/
83
84 /* FIXME: This variable is a hack. Would be nice to eliminate the 
85    global-ness. */
86
87 static input_thread_t *p_vcd_input = NULL;
88
89 /* process messages that originate from libcdio. */
90 static void
91 cdio_log_handler (cdio_log_level_t level, const char message[])
92 {
93   thread_vcd_data_t *p_vcd = (thread_vcd_data_t *)p_vcd_input->p_access_data;
94   switch (level) {
95   case CDIO_LOG_DEBUG:
96   case CDIO_LOG_INFO:
97     if (p_vcd->i_debug & INPUT_DBG_CDIO) 
98       msg_Dbg( p_vcd_input, message);
99     break;
100   case CDIO_LOG_WARN:
101     msg_Warn( p_vcd_input, message);
102     break;
103   case CDIO_LOG_ERROR:
104   case CDIO_LOG_ASSERT:
105     msg_Err( p_vcd_input, message);
106     break;
107   default:
108     msg_Warn( p_vcd_input, message,
109             _("The above message had unknown log level"), 
110             level);
111   }
112   return;
113 }
114
115 /* process messages that originate from vcdinfo. */
116 static void
117 vcd_log_handler (vcd_log_level_t level, const char message[])
118 {
119   thread_vcd_data_t *p_vcd = (thread_vcd_data_t *)p_vcd_input->p_access_data;
120   switch (level) {
121   case VCD_LOG_DEBUG:
122   case VCD_LOG_INFO:
123     if (p_vcd->i_debug & INPUT_DBG_VCDINFO)
124       msg_Dbg( p_vcd_input, message);
125     break;
126   case VCD_LOG_WARN:
127     msg_Warn( p_vcd_input, message);
128     break;
129   case VCD_LOG_ERROR:
130   case VCD_LOG_ASSERT:
131     msg_Err( p_vcd_input, message);
132     break;
133   default:
134     msg_Warn( p_vcd_input, "%s\n%s %d", message,
135             _("The above message had unknown vcdimager log level"), 
136             level);
137   }
138   return;
139 }
140
141 /*****************************************************************************
142  * VCDRead: reads i_len bytes from the VCD into p_buffer.
143  *****************************************************************************
144  * Returns -1 in case of error, 0 in case of EOF, otherwise the number of
145  * bytes.
146  *****************************************************************************/
147 static int 
148 VCDRead( input_thread_t * p_input, byte_t * p_buffer, size_t i_len )
149 {
150     thread_vcd_data_t *     p_vcd= (thread_vcd_data_t *)p_input->p_access_data;
151     int                     i_blocks;
152     int                     i_index;
153     int                     i_read;
154     byte_t                  p_last_sector[ M2F2_SECTOR_SIZE ];
155
156     i_read = 0;
157
158     dbg_print( (INPUT_DBG_CALL), "lsn: %u", p_vcd->cur_lsn );
159
160     /* Compute the number of blocks we have to read */
161
162     i_blocks = i_len / M2F2_SECTOR_SIZE;
163
164     for ( i_index = 0 ; i_index < i_blocks ; i_index++ )
165     {
166
167       if ( p_vcd->cur_lsn == p_vcd->end_lsn ) {
168         vcdplayer_read_status_t read_status;
169
170         /* We've run off of the end of this entry. Do we continue or stop? */
171         dbg_print( (INPUT_DBG_LSN|INPUT_DBG_PBC), 
172                    "end reached, cur: %u", p_vcd->cur_lsn );
173
174         read_status = vcdplayer_pbc_is_on( p_vcd ) 
175           ? vcdplayer_pbc_nav( p_input ) 
176           : vcdplayer_non_pbc_nav( p_input );
177
178         switch (read_status) {
179         case READ_END:
180           /* End reached. Return NULL to indicated this. */
181         case READ_ERROR:
182           /* Some sort of error. */
183           return i_read;
184
185         case READ_STILL_FRAME: 
186           {
187             /* Reached the end of a still frame. */
188
189             byte_t * p_buf = p_buffer;
190             pgrm_descriptor_t * p_pgrm = p_input->stream.p_selected_program;;
191
192             p_buf += (i_index*M2F2_SECTOR_SIZE);
193             memset(p_buf, 0, M2F2_SECTOR_SIZE);
194             p_buf += 2;
195             *p_buf = 0x01;
196             dbg_print(INPUT_DBG_STILL, "Handled still event\n");
197
198             /* p_vcd->p_intf->b_end_of_cell = true; */
199             input_SetStatus( p_input, INPUT_STATUS_PAUSE );
200
201             vlc_mutex_lock( &p_input->stream.stream_lock );
202
203             p_pgrm = p_input->stream.p_selected_program;
204             p_pgrm->i_synchro_state = SYNCHRO_REINIT;
205
206             vlc_mutex_unlock( &p_input->stream.stream_lock );
207
208             input_ClockManageControl( p_input, p_pgrm, 0 );
209
210             return i_read + M2F2_SECTOR_SIZE;
211           }
212         default:
213         case READ_BLOCK:
214           break;
215         }
216       }
217
218       if ( VCDReadSector( VLC_OBJECT(p_input), p_vcd->vcd,
219                           p_vcd->cur_lsn, 
220                           p_buffer + (i_index*M2F2_SECTOR_SIZE) ) < 0 )
221         {
222           LOG_ERR ("could not read sector %d", p_vcd->cur_lsn );
223           return -1;
224         }
225       
226       p_vcd->cur_lsn ++;
227       
228       /* Update chapter */
229       if( p_vcd->b_valid_ep &&
230           /* FIXME kludge so that read does not update chapter
231            * when a manual chapter change was requested and not
232            * yet accomplished */
233           !p_input->stream.p_new_area )
234         {
235           unsigned int i_entry = p_input->stream.p_selected_area->i_part;
236           
237           vlc_mutex_lock( &p_input->stream.stream_lock );
238           
239           if( i_entry < p_vcd->num_entries &&
240               p_vcd->cur_lsn >= p_vcd->p_entries[i_entry+1] )
241             {
242               dbg_print( INPUT_DBG_PBC, 
243                          "new entry, i_entry %d, sector %d, es %d",
244                          i_entry, p_vcd->cur_lsn, 
245                          p_vcd->p_entries[i_entry] );
246               p_vcd->play_item.num = 
247                 ++ p_input->stream.p_selected_area->i_part;
248               p_vcd->play_item.type = VCDINFO_ITEM_TYPE_ENTRY;
249               VCDUpdateVar( p_input, p_vcd->play_item.num, VLC_VAR_SETVALUE,
250                             "chapter", "Setting entry" );
251             }
252           vlc_mutex_unlock( &p_input->stream.stream_lock );
253         }
254
255         i_read += M2F2_SECTOR_SIZE;
256     }
257
258     if ( i_len % M2F2_SECTOR_SIZE ) /* this should not happen */
259     {
260         if ( VCDReadSector( VLC_OBJECT(p_input), p_vcd->vcd,
261                             p_vcd->cur_lsn, p_last_sector ) < 0 )
262         {
263             LOG_ERR ("could not read sector %d", p_vcd->cur_lsn );
264             return -1;
265         }
266
267         p_input->p_vlc->pf_memcpy( p_buffer + i_blocks * M2F2_SECTOR_SIZE,
268                                    p_last_sector, i_len % M2F2_SECTOR_SIZE );
269         i_read += i_len % M2F2_SECTOR_SIZE;
270     }
271
272     return i_read;
273 }
274
275
276 /*****************************************************************************
277  * VCDSetProgram: Does nothing since a VCD is mono_program
278  *****************************************************************************/
279 static int 
280 VCDSetProgram( input_thread_t * p_input, pgrm_descriptor_t * p_program)
281 {
282     thread_vcd_data_t *     p_vcd= (thread_vcd_data_t *)p_input->p_access_data;
283     dbg_print( (INPUT_DBG_CALL|INPUT_DBG_EXT), "VCDSetProgram" );
284     return 0;
285 }
286
287
288 /*****************************************************************************
289  * VCDSetArea: initialize internal data structures and input stream data 
290    so set subsequent reading and seeking to reflect that we are
291    at track x, entry or segment y.
292    This is called for each user navigation request, e.g. the GUI 
293    Chapter/Title selections or in initial MRL parsing. 
294  ****************************************************************************/
295 int 
296 VCDSetArea( input_thread_t * p_input, input_area_t * p_area )
297 {
298     thread_vcd_data_t *p_vcd = (thread_vcd_data_t*)p_input->p_access_data;
299     unsigned int i_entry = p_area->i_part;
300     track_t i_track      = p_area->i_id;
301     int old_seekable     = p_input->stream.b_seekable;
302     unsigned int i_nb    = p_area->i_plugin_data + p_area->i_part_nb;
303
304     dbg_print( (INPUT_DBG_CALL|INPUT_DBG_EXT),
305                "track: %d, entry %d, seekable %d, area %lx, select area %lx ",
306                i_track, i_entry, old_seekable, 
307                (long unsigned int) p_area, 
308                (long unsigned int) p_input->stream.p_selected_area );
309
310     /* we can't use the interface slider until initilization is complete */
311     p_input->stream.b_seekable = 0;
312
313     if( p_area != p_input->stream.p_selected_area )
314     {
315         unsigned int i;
316
317         /* If is the result of a track change, make the entry valid. */
318         if (i_entry < p_area->i_plugin_data || i_entry >= i_nb) 
319           i_entry = p_area->i_plugin_data;
320
321         /* Change the default area */
322         p_input->stream.p_selected_area = p_area;
323
324         /* Update the navigation variables without triggering a callback */
325         VCDUpdateVar( p_input, i_track, VLC_VAR_SETVALUE, "title", 
326                       "Setting track");
327
328         var_Change( p_input, "chapter", VLC_VAR_CLEARCHOICES, NULL, NULL );
329         for( i = p_area->i_plugin_data; i < i_nb; i++ )
330         {
331           VCDUpdateVar( p_input, i , VLC_VAR_ADDCHOICE, 
332                         "chapter",  "Adding entry choice");
333         }
334     }
335
336     if (i_track == 0) 
337       VCDSetOrigin( p_input, p_vcd->p_segments[i_entry], 
338                     p_vcd->p_segments[i_entry], p_vcd->p_segments[i_entry+1],
339                     i_entry, 0 );
340     else
341       VCDSetOrigin( p_input, p_vcd->p_sectors[i_track], 
342                     vcdinfo_get_entry_lsn(p_vcd->vcd, i_entry), 
343                     p_vcd->p_sectors[i_track+1],
344                     i_entry, i_track );
345
346     p_input->stream.b_seekable = old_seekable;
347     /* warn interface that something has changed */
348     p_input->stream.b_changed = 1;
349
350     return VLC_SUCCESS;
351 }
352
353
354 /****************************************************************************
355  * VCDSeek
356  ****************************************************************************/
357 void 
358 VCDSeek( input_thread_t * p_input, off_t i_off )
359 {
360     thread_vcd_data_t * p_vcd;
361     unsigned int i_entry=0; /* invalid entry */
362
363     p_vcd = (thread_vcd_data_t *) p_input->p_access_data;
364
365     p_vcd->cur_lsn = p_vcd->origin_lsn + (i_off / (off_t)M2F2_SECTOR_SIZE);
366
367     vlc_mutex_lock( &p_input->stream.stream_lock );
368 #define p_area p_input->stream.p_selected_area
369     /* Find entry */
370     if( p_vcd->b_valid_ep )
371     {
372         for( i_entry = 1 ; i_entry < p_vcd->num_entries ; i_entry ++ )
373         {
374             if( p_vcd->cur_lsn < p_vcd->p_entries[i_entry] )
375             {
376               VCDUpdateVar( p_input, i_entry, VLC_VAR_SETVALUE, 
377                             "chapter", "Setting entry" );
378               break;
379             }
380         }
381         p_vcd->play_item.num  = p_area->i_part = i_entry;
382         p_vcd->play_item.type = VCDINFO_ITEM_TYPE_ENTRY;
383     }
384 #undef p_area
385
386     p_input->stream.p_selected_area->i_tell = i_off;
387
388     dbg_print( (INPUT_DBG_CALL|INPUT_DBG_EXT|INPUT_DBG_SEEK),
389     "orig %d, cur: %d, offset: %lld, start: %lld, entry %d", 
390                p_vcd->origin_lsn, p_vcd->cur_lsn, i_off, 
391                p_input->stream.p_selected_area->i_start, i_entry );
392
393     vlc_mutex_unlock( &p_input->stream.stream_lock );
394 }
395
396 /*****************************************************************************
397   VCDPlay: set up internal structures so seeking/reading places an item.
398   itemid: the thing to play.
399   user_entry: true if itemid is a user selection (rather than internally-
400   generated selection such as via PBC) in which case we may have to adjust 
401   for differences in numbering. 
402  *****************************************************************************/
403 int
404 VCDPlay( input_thread_t *p_input, vcdinfo_itemid_t itemid )
405 {
406     thread_vcd_data_t *     p_vcd= (thread_vcd_data_t *)p_input->p_access_data;
407     input_area_t *          p_area;
408     
409     p_vcd->in_still = 0;
410
411     dbg_print(INPUT_DBG_CALL, "itemid.num: %d, itemid.type: %d\n", 
412               itemid.num, itemid.type);
413
414 #define area p_input->stream.pp_areas
415
416     switch (itemid.type) {
417     case VCDINFO_ITEM_TYPE_TRACK:
418
419       /* Valid tracks go from 1...num_tracks-1, because track 0 is unplayable.
420        */
421
422       if (itemid.num == 0 || itemid.num >= p_vcd->num_tracks) {
423         LOG_ERR ("Invalid track number %d", itemid.num );
424         return VLC_EGENERIC;
425       }
426       p_area           = area[itemid.num];
427       p_area->i_part   = p_area->i_plugin_data;
428       p_input->stream.b_seekable = 1;
429       break;
430     case VCDINFO_ITEM_TYPE_SEGMENT: 
431       /* Valid segments go from 0...num_segments-1. */
432       if (itemid.num >= p_vcd->num_segments) {
433         LOG_ERR ( "Invalid segment number: %d", itemid.num );
434         return VLC_EGENERIC;
435       } else {
436         vcdinfo_video_segment_type_t segtype = 
437           vcdinfo_get_video_type(p_vcd->vcd, itemid.num);
438         
439         dbg_print(INPUT_DBG_PBC, "%s (%d), seg_num: %d", 
440                   vcdinfo_video_type2str(p_vcd->vcd, itemid.num), 
441                   (int) segtype, itemid.num);
442         
443         p_area           = area[0];
444         p_area->i_part   = itemid.num;
445         
446         switch (segtype)
447           {
448           case VCDINFO_FILES_VIDEO_NTSC_STILL:
449           case VCDINFO_FILES_VIDEO_NTSC_STILL2:
450           case VCDINFO_FILES_VIDEO_PAL_STILL:
451           case VCDINFO_FILES_VIDEO_PAL_STILL2:
452             p_input->stream.b_seekable = 0;
453             p_vcd->in_still = -5;
454             break;
455           default:
456             p_input->stream.b_seekable = 1;
457             p_vcd->in_still = 0;
458           }
459       }
460       break;
461       
462     case VCDINFO_ITEM_TYPE_LID:
463       /* LIDs go from 1..num_lids. */
464       if (itemid.num == 0 || itemid.num > p_vcd->num_lids) {
465         LOG_ERR ( "Invalid LID number: %d", itemid.num );
466         return VLC_EGENERIC;
467       } else {
468         p_vcd->cur_lid = itemid.num;
469         vcdinfo_lid_get_pxd(p_vcd->vcd, &(p_vcd->pxd), itemid.num);
470     
471         switch (p_vcd->pxd.descriptor_type) {
472       
473         case PSD_TYPE_SELECTION_LIST:
474         case PSD_TYPE_EXT_SELECTION_LIST: {
475           vcdinfo_itemid_t trans_itemid;
476           uint16_t trans_itemid_num;
477           
478           if (p_vcd->pxd.psd == NULL) return VLC_EGENERIC;
479           trans_itemid_num  = vcdinf_psd_get_itemid(p_vcd->pxd.psd);
480           vcdinfo_classify_itemid(trans_itemid_num, &trans_itemid);
481           p_vcd->loop_count = 1;
482           p_vcd->loop_item  = trans_itemid;
483           return VCDPlay( p_input, trans_itemid );
484           break;
485         }
486           
487         case PSD_TYPE_PLAY_LIST: {
488           if (p_vcd->pxd.pld == NULL) return VLC_EGENERIC;
489           p_vcd->pdi = -1;
490           return vcdplayer_inc_play_item(p_input) 
491             ? VLC_SUCCESS : VLC_EGENERIC;
492           break;
493         }
494           
495         case PSD_TYPE_END_LIST:
496         case PSD_TYPE_COMMAND_LIST:
497           
498         default:
499           ;
500         }
501       }
502       return VLC_EGENERIC;
503     case VCDINFO_ITEM_TYPE_ENTRY:
504       /* Entries go from 0..num_entries-1. */
505       if (itemid.num >= p_vcd->num_entries) {
506         LOG_ERR ("Invalid entry number: %d", itemid.num );
507         return VLC_EGENERIC;
508       } else {
509         track_t cur_track  = vcdinfo_get_track(p_vcd->vcd,  itemid.num);
510         p_area             = area[cur_track];
511         p_area->i_part     = itemid.num;
512         p_input->stream.b_seekable = 1;
513       }
514       break;
515     default:
516       LOG_ERR ("unknown entry type" );
517       return VLC_EGENERIC;
518     }
519
520     VCDSetArea( p_input, p_area );
521
522 #undef area
523
524     p_vcd->play_item = itemid;
525
526     dbg_print( (INPUT_DBG_CALL), 
527                "i_start %lld, i_size: %lld, i_tell: %lld, lsn %d", 
528                p_area->i_start, p_area->i_size, 
529                p_area->i_tell, p_vcd->cur_lsn );
530         
531     return VLC_SUCCESS;
532 }
533
534 /*****************************************************************************
535   VCDEntryPoints: Reads the information about the entry points on the disc
536   and initializes area information with that.
537   Before calling this track information should have been read in.
538  *****************************************************************************/
539 static int 
540 VCDEntryPoints( input_thread_t * p_input )
541 {
542     thread_vcd_data_t *               p_vcd;
543     unsigned int                      i_nb;
544     unsigned int                      i, i_entry_index = 0;
545     unsigned int                      i_previous_track = CDIO_INVALID_TRACK;
546
547     p_vcd = (thread_vcd_data_t *) p_input->p_access_data;
548
549     i_nb = vcdinfo_get_num_entries(p_vcd->vcd);
550     if (0 == i_nb) 
551       return -1;
552     
553     p_vcd->p_entries  = malloc( sizeof( lba_t ) * i_nb );
554
555     if( p_vcd->p_entries == NULL )
556     {
557         LOG_ERR ("not enough memory for entry points treatment" );
558         return -1;
559     }
560
561     p_vcd->num_entries = 0;
562
563     for( i = 0 ; i < i_nb ; i++ )
564     {
565         track_t i_track = vcdinfo_get_track(p_vcd->vcd, i);
566         if( i_track <= p_input->stream.i_area_nb )
567         {
568             p_vcd->p_entries[i] = 
569               vcdinfo_get_entry_lsn(p_vcd->vcd, i);
570             p_input->stream.pp_areas[i_track]->i_part_nb ++;
571
572             /* if this entry belongs to a new track */
573             if( i_track != i_previous_track )
574             {
575                 /* i_plugin_data is used to store the first entry of the area*/
576                 p_input->stream.pp_areas[i_track]->i_plugin_data =
577                                                             i_entry_index;
578                 i_previous_track = i_track;
579                 p_input->stream.pp_areas[i_track]->i_part_nb = 1;
580             }
581             i_entry_index ++;
582             p_vcd->num_entries ++;
583         }
584         else
585             msg_Warn( p_input, "wrong track number found in entry points" );
586     }
587     p_vcd->b_valid_ep = true;
588     return 0;
589 }
590
591 /*****************************************************************************
592  * VCDSegments: Reads the information about the segments the disc.
593  *****************************************************************************/
594 static int
595 VCDSegments( input_thread_t * p_input )
596 {
597     thread_vcd_data_t * p_vcd;
598     unsigned int        i;
599     unsigned int        num_segments;
600     
601
602     p_vcd = (thread_vcd_data_t *) p_input->p_access_data;
603     num_segments = p_vcd->num_segments = vcdinfo_get_num_segments(p_vcd->vcd);
604
605 #define area p_input->stream.pp_areas
606
607     /* area 0 is reserved for segments. Set Absolute start offset
608          and size */
609     area[0]->i_plugin_data = 0;
610     input_DelArea( p_input, area[0] );
611     input_AddArea( p_input, 0, 0 );
612     
613     area[0]->i_start = (off_t)p_vcd->p_sectors[0] 
614       * (off_t)M2F2_SECTOR_SIZE;
615     area[0]->i_size = (off_t)(p_vcd->p_sectors[1] - p_vcd->p_sectors[0])
616       * (off_t)M2F2_SECTOR_SIZE;
617     
618     /* Default Segment  */
619     area[0]->i_part = 0;
620     
621     /* i_plugin_data is used to store which entry point is the first
622        of the track (area) */
623     area[0]->i_plugin_data = 0;
624
625     area[0]->i_part_nb = 0;
626     
627     dbg_print( INPUT_DBG_MRL, 
628                "area[0] id: %d, i_start: %lld, i_size: %lld", 
629                area[0]->i_id, area[0]->i_start, area[0]->i_size );
630
631     if (num_segments == 0) return 0;
632
633     /* We have one additional segment allocated so we can get the size
634        by subtracting seg[i+1] - seg[i].
635      */
636     p_vcd->p_segments = malloc( sizeof( lba_t ) * (num_segments+1) );
637     if( p_vcd->p_segments == NULL )
638     {
639         LOG_ERR ("not enough memory for segment treatment" );
640         return -1;
641     }
642
643     /* Update the navigation variables without triggering a callback */
644     VCDUpdateVar( p_input, 0, VLC_VAR_SETVALUE, "title", "Setting track" );
645     var_Change( p_input, "chapter", VLC_VAR_CLEARCHOICES, NULL, NULL );
646     
647     for( i = 0 ; i < num_segments ; i++ )
648     {
649       p_vcd->p_segments[i] = vcdinfo_get_seg_lsn(p_vcd->vcd, i);
650       area[0]->i_part_nb ++;
651       VCDUpdateVar( p_input, i , VLC_VAR_ADDCHOICE, 
652                     "chapter", "Adding segment choice");
653     }
654
655 #undef area
656
657     p_vcd->p_segments[num_segments] = p_vcd->p_segments[num_segments-1]+
658       vcdinfo_get_seg_sector_count(p_vcd->vcd, num_segments-1);
659     
660     return 0;
661 }
662
663 /*****************************************************************************
664  VCDTracks: initializes area information. 
665  Before calling this track information should have been read in.
666  *****************************************************************************/
667 static void
668 VCDTracks( input_thread_t * p_input )
669 {
670     thread_vcd_data_t * p_vcd;
671     unsigned int        i;
672
673     p_vcd = (thread_vcd_data_t *) p_input->p_access_data;
674
675 #define area p_input->stream.pp_areas
676
677     /* We start area addressing for tracks at 1 since the default area 0
678        is reserved for segments */
679
680     for( i = 1 ; i < p_vcd->num_tracks ; i++ )
681     {
682         /* Tracks are Program Chains */
683         input_AddArea( p_input, i, i );
684
685         /* Absolute start byte offset and byte size */
686         area[i]->i_start = (off_t) p_vcd->p_sectors[i] 
687                            * (off_t)M2F2_SECTOR_SIZE;
688         area[i]->i_size = (off_t)(p_vcd->p_sectors[i+1] - p_vcd->p_sectors[i])
689                            * (off_t)M2F2_SECTOR_SIZE;
690
691         /* Current entry being played in track */
692         area[i]->i_part = 0;
693
694         /* i_plugin_data is used to store which entry point is the first
695          * of the track (area) */
696         area[i]->i_plugin_data = 0;
697
698         dbg_print( INPUT_DBG_MRL, 
699                    "area[%d] id: %d, i_start: %lld, i_size: %lld", 
700                    i, area[i]->i_id, area[i]->i_start, area[i]->i_size );
701     }
702
703 #undef area
704
705     return ;
706 }
707
708 /*****************************************************************************
709   VCDLIDs: Reads the LIST IDs from the LOT.
710  *****************************************************************************/
711 static int 
712 VCDLIDs( input_thread_t * p_input )
713 {
714     thread_vcd_data_t *p_vcd = (thread_vcd_data_t *) p_input->p_access_data;
715
716     p_vcd->num_lids = vcdinfo_get_num_LIDs(p_vcd->vcd);
717     p_vcd->cur_lid  = VCDINFO_INVALID_ENTRY;
718
719     if (vcdinfo_read_psd (p_vcd->vcd)) {
720       
721       vcdinfo_visit_lot (p_vcd->vcd, false);
722       
723 #if FIXED
724     /* 
725        We need to change libvcdinfo to be more robust when there are 
726        problems reading the extended PSD. Given that area-highlighting and 
727        selection features in the extended PSD haven't been implemented,
728        it's best then to not try to read this at all.
729      */
730       if (vcdinfo_get_psd_x_size(p_vcd->vcd))
731         vcdinfo_visit_lot (p_vcd->vcd, true);
732 #endif 
733     }
734
735     dbg_print( (INPUT_DBG_CALL|INPUT_DBG_MRL), 
736                "num LIDs=%d", p_vcd->num_lids);
737
738     return 0;
739 }
740
741 /*****************************************************************************
742  * VCDParse: parse command line
743  *****************************************************************************/
744 static char * 
745 VCDParse( input_thread_t * p_input, /*out*/ vcdinfo_itemid_t * p_itemid )
746 {
747     thread_vcd_data_t *p_vcd = (thread_vcd_data_t *)p_input->p_access_data;
748     char *             psz_parser;
749     char *             psz_source;
750     char *             psz_next;
751
752     if ( config_GetInt( p_input, MODULE_STRING "-PBC" ) ) {
753       p_itemid->type=VCDINFO_ITEM_TYPE_LID;
754       p_itemid->num=1;
755     } else {
756       p_itemid->type=VCDINFO_ITEM_TYPE_TRACK;
757       p_itemid->num=1;
758     }
759     
760 #ifdef WIN32
761     /* On Win32 we want the VCD access plugin to be explicitly requested,
762      * we end up with lots of problems otherwise */
763     if( !p_input->psz_access || !*p_input->psz_access ) return NULL;
764 #endif
765
766     if( !p_input->psz_name )
767     {
768         return NULL;
769     }
770
771     psz_parser = psz_source = strdup( p_input->psz_name );
772
773     /* Parse input string :
774      * [device][@[type][title]] */
775     while( *psz_parser && *psz_parser != '@' )
776     {
777         psz_parser++;
778     }
779
780     if( *psz_parser == '@' )
781     {
782       /* Found the divide between the source name and the 
783          type+entry number. */
784       unsigned int num;
785       
786       *psz_parser = '\0';
787       ++psz_parser;
788       if( *psz_parser )
789         {
790           switch(*psz_parser) {
791           case 'E': 
792             p_itemid->type = VCDINFO_ITEM_TYPE_ENTRY;
793             ++psz_parser;
794             break;
795           case 'P': 
796             p_itemid->type = VCDINFO_ITEM_TYPE_LID;
797             ++psz_parser;
798             break;
799           case 'S': 
800             p_itemid->type = VCDINFO_ITEM_TYPE_SEGMENT;
801             ++psz_parser;
802             break;
803           case 'T': 
804             p_itemid->type = VCDINFO_ITEM_TYPE_TRACK;
805             ++psz_parser;
806             break;
807           default: ;
808           }
809         }
810       
811       num = strtol( psz_parser, &psz_next, 10 );
812       if ( *psz_parser != '\0' && *psz_next == '\0') 
813         {
814           p_itemid->num = num;
815         }
816       
817     }
818
819     if( !*psz_source ) {
820
821       /* No source specified, so figure it out. */
822       if( !p_input->psz_access ) return NULL;
823       
824       psz_source = config_GetPsz( p_input, MODULE_STRING "-device" );
825
826       if( !psz_source || 0==strlen(psz_source) ) {
827         /* Scan for a CD-ROM drive with a VCD in it. */
828         char **cd_drives = cdio_get_devices_with_cap(NULL, 
829                             (CDIO_FS_ANAL_SVCD|CDIO_FS_ANAL_CVD
830                              |CDIO_FS_ANAL_VIDEOCD|CDIO_FS_UNKNOWN),
831                                                      true);
832         if (NULL == cd_drives) return NULL;
833         if (cd_drives[0] == NULL) {
834           cdio_free_device_list(cd_drives);
835           return NULL;
836         }
837         psz_source = strdup(cd_drives[0]);
838         cdio_free_device_list(cd_drives);
839       }
840     }
841
842     dbg_print( (INPUT_DBG_CALL|INPUT_DBG_MRL), 
843                "source=%s entry=%d type=%d",
844                psz_source, p_itemid->num, p_itemid->type);
845
846     return psz_source;
847 }
848
849 /* 
850    Set's start origin subsequent seeks/reads
851 */
852 static void 
853 VCDSetOrigin( input_thread_t *p_input, lsn_t origin_lsn, 
854               lsn_t cur_lsn, lsn_t end_lsn, int cur_entry, track_t cur_track )
855 {
856   thread_vcd_data_t * p_vcd  = (thread_vcd_data_t *) p_input->p_access_data;
857
858   p_vcd->origin_lsn = origin_lsn;
859   p_vcd->cur_lsn    = cur_lsn;
860   p_vcd->end_lsn    = end_lsn;
861   p_vcd->cur_track  = cur_track;
862   p_vcd->play_item.num  = cur_entry;
863   p_vcd->play_item.type = VCDINFO_ITEM_TYPE_ENTRY;
864   
865   dbg_print( (INPUT_DBG_CALL|INPUT_DBG_LSN),
866              "origin: %d, cur_lsn: %d, end_lsn: %d, entry: %d, track: %d",
867              origin_lsn, cur_lsn, end_lsn, cur_entry, cur_track );
868
869   p_input->stream.p_selected_area->i_tell =
870     (off_t) (p_vcd->cur_lsn - p_vcd->origin_lsn) * (off_t)M2F2_SECTOR_SIZE;
871
872   VCDUpdateVar( p_input, cur_entry, VLC_VAR_SETVALUE, 
873                 "chapter", "Setting entry");
874 }
875
876 /*****************************************************************************
877  * vcd_Open: Opens a VCD device or file and returns an opaque handle
878  *****************************************************************************/
879 static vcdinfo_obj_t *
880 vcd_Open( vlc_object_t *p_this, const char *psz_dev )
881 {
882     vcdinfo_obj_t *p_vcdobj;
883     char  *actual_dev;
884
885     if( !psz_dev ) return NULL;
886
887     /* Set where to log errors messages from libcdio. */
888     p_vcd_input = (input_thread_t *)p_this;
889     cdio_log_set_handler ( cdio_log_handler );
890     vcd_log_set_handler ( vcd_log_handler );
891     
892     actual_dev=strdup(psz_dev);
893     if ( vcdinfo_open(&p_vcdobj, &actual_dev, DRIVER_UNKNOWN, NULL) != 
894          VCDINFO_OPEN_VCD) {
895       free(actual_dev);
896       return NULL;
897     }
898     free(actual_dev);
899
900     return p_vcdobj;
901 }
902
903 /****************************************************************************
904  * VCDReadSector: Read a sector (2324 bytes)
905  ****************************************************************************/
906 static int 
907 VCDReadSector( vlc_object_t *p_this, const vcdinfo_obj_t *p_vcd,
908                lsn_t cur_lsn, byte_t * p_buffer )
909 {
910   typedef struct {
911     uint8_t subheader   [8];
912     uint8_t data        [M2F2_SECTOR_SIZE];
913   } vcdsector_t;
914   vcdsector_t vcd_sector;
915   
916   if (cdio_read_mode2_sector(vcdinfo_get_cd_image(p_vcd), 
917                              &vcd_sector, cur_lsn, true) 
918       != 0)
919     {
920       msg_Warn( p_this, "Could not read LSN %d", cur_lsn );
921       return -1;
922     }
923     
924   memcpy (p_buffer, vcd_sector.data, M2F2_SECTOR_SIZE);
925   
926   return( 0 );
927 }
928
929 /****************************************************************************
930  Update the "varname" variable to i_num without triggering a callback.
931 ****************************************************************************/
932 static void
933 VCDUpdateVar( input_thread_t *p_input, int i_num, int i_action,
934               const char *varname, const char *label)
935 {
936   vlc_value_t val;
937   val.i_int = i_num;
938   if (NULL != p_vcd_input) {
939     thread_vcd_data_t *p_vcd = (thread_vcd_data_t *)p_vcd_input->p_access_data;
940     dbg_print( INPUT_DBG_PBC, "%s %d", label, i_num );
941   }
942   var_Change( p_input, varname, i_action, &val, NULL );
943 }
944
945
946 #define meta_info_add_str(title, str) \
947   if ( str ) {                                                          \
948     dbg_print( INPUT_DBG_META, "field: %s: %s\n", title, str);  \
949     input_AddInfo( p_cat, _(title), "%s", str );                        \
950   }
951
952 #define meta_info_add_num(title, num) \
953   dbg_print( INPUT_DBG_META, "field %s: %d\n", title, num);             \
954   input_AddInfo( p_cat, _(title), "%d", num );                          \
955
956 static void InformationCreate( input_thread_t *p_input  )
957 {
958   thread_vcd_data_t *p_vcd = (thread_vcd_data_t *) p_input->p_access_data;
959   input_info_category_t *p_cat;
960   
961   p_cat = input_InfoCategory( p_input, "General" );
962
963   meta_info_add_str( "VCD Format", vcdinfo_get_format_version_str(p_vcd->vcd));
964   meta_info_add_str( "Album",      vcdinfo_get_album_id(p_vcd->vcd));
965   meta_info_add_str( "Application",vcdinfo_get_application_id(p_vcd->vcd));
966   meta_info_add_str( "Preparer",   vcdinfo_get_preparer_id(p_vcd->vcd));
967   meta_info_add_num( "Vol #",      vcdinfo_get_volume_num(p_vcd->vcd));
968   meta_info_add_num( "Vol max #",  vcdinfo_get_volume_count(p_vcd->vcd));
969   meta_info_add_str( "Volume Set", vcdinfo_get_volumeset_id(p_vcd->vcd));
970   meta_info_add_str( "Volume",     vcdinfo_get_volume_id(p_vcd->vcd));
971   meta_info_add_str( "Publisher",  vcdinfo_get_publisher_id(p_vcd->vcd));
972   meta_info_add_str( "System Id",  vcdinfo_get_system_id(p_vcd->vcd));
973   meta_info_add_num( "LIDs",       vcdinfo_get_num_LIDs(p_vcd->vcd));
974   meta_info_add_num( "Entries",    vcdinfo_get_num_entries(p_vcd->vcd));
975   meta_info_add_num( "Segments",   vcdinfo_get_num_segments(p_vcd->vcd));
976   meta_info_add_num( "Tracks",     vcdinfo_get_num_tracks(p_vcd->vcd));
977
978 }
979
980 /*****************************************************************************
981  * Public routines.
982  *****************************************************************************/
983 int
984 E_(DebugCallback)   ( vlc_object_t *p_this, const char *psz_name,
985                       vlc_value_t oldval, vlc_value_t val, void *p_data )
986 {
987   thread_vcd_data_t *p_vcd;
988
989   if (NULL == p_vcd_input) return VLC_EGENERIC;
990   
991   p_vcd = (thread_vcd_data_t *)p_vcd_input->p_access_data;
992
993   if (p_vcd->i_debug & (INPUT_DBG_CALL|INPUT_DBG_EXT)) {
994     msg_Dbg( p_vcd_input, "Old debug (x%0x) %d, new debug (x%0x) %d", 
995              p_vcd->i_debug, p_vcd->i_debug, val.i_int, val.i_int);
996   }
997   p_vcd->i_debug = val.i_int;
998   return VLC_SUCCESS;
999 }
1000
1001 /*****************************************************************************
1002   Open: open VCD.
1003   read in meta-information about VCD: the number of tracks, segments, 
1004   entries, size and starting information. Then set up state variables so
1005   that we read/seek starting at the location specified.
1006
1007   On success we return VLC_SUCCESS, on memory exhausted VLC_ENOMEM, 
1008   and VLC_EGENERIC for some other error.
1009  *****************************************************************************/
1010 int 
1011 E_(Open) ( vlc_object_t *p_this )
1012 {
1013     input_thread_t *        p_input = (input_thread_t *)p_this;
1014     thread_vcd_data_t *     p_vcd;
1015     char *                  psz_source;
1016     vcdinfo_itemid_t        itemid;
1017     bool                    b_play_ok;
1018     
1019     p_input->pf_read        = VCDRead;
1020     p_input->pf_seek        = VCDSeek;
1021     p_input->pf_set_area    = VCDSetArea;
1022     p_input->pf_set_program = VCDSetProgram;
1023
1024     p_vcd = malloc( sizeof(thread_vcd_data_t) );
1025
1026     if( p_vcd == NULL )
1027     {
1028         LOG_ERR ("out of memory" );
1029         return VLC_ENOMEM;
1030     }
1031
1032     p_input->p_access_data = (void *)p_vcd;
1033     p_vcd->i_debug         = config_GetInt( p_this, MODULE_STRING "-debug" );
1034     psz_source             = VCDParse( p_input, &itemid );
1035
1036     if ( NULL == psz_source ) 
1037     {
1038       free( p_vcd );
1039       return( VLC_EGENERIC );
1040     }
1041
1042     dbg_print( (INPUT_DBG_CALL|INPUT_DBG_EXT), "%s", psz_source );
1043
1044     p_vcd->p_segments = NULL;
1045     p_vcd->p_entries  = NULL;
1046     
1047     /* set up input  */
1048     p_input->i_mtu = VCD_DATA_ONCE;
1049
1050     vlc_mutex_lock( &p_input->stream.stream_lock );
1051
1052     /* If we are here we can control the pace... */
1053     p_input->stream.b_pace_control = 1;
1054
1055     p_input->stream.b_seekable = 1;
1056     p_input->stream.p_selected_area->i_size = 0;
1057     p_input->stream.p_selected_area->i_tell = 0;
1058
1059     vlc_mutex_unlock( &p_input->stream.stream_lock );
1060
1061     if( !(p_vcd->vcd = vcd_Open( p_this, psz_source )) )
1062     {
1063         msg_Warn( p_input, "could not open %s", psz_source );
1064         free( psz_source );
1065         free( p_vcd );
1066         return VLC_EGENERIC;
1067     }
1068
1069     /* Get track information. */
1070     p_vcd->num_tracks = ioctl_GetTracksMap( VLC_OBJECT(p_input),
1071                                             vcdinfo_get_cd_image(p_vcd->vcd), 
1072                                             &p_vcd->p_sectors );
1073     free( psz_source );
1074     if( p_vcd->num_tracks < 0 )
1075         LOG_ERR ("unable to count tracks" );
1076     else if( p_vcd->num_tracks <= 1 )
1077         LOG_ERR ("no movie tracks found" );
1078     if( p_vcd->num_tracks <= 1)
1079     {
1080         vcdinfo_close( p_vcd->vcd );
1081         free( p_vcd );
1082         return VLC_EGENERIC;
1083     }
1084
1085     /* Set stream and area data */
1086     vlc_mutex_lock( &p_input->stream.stream_lock );
1087
1088     /* Initialize ES structures */
1089     input_InitStream( p_input, sizeof( stream_ps_data_t ) );
1090
1091     /* disc input method */
1092     p_input->stream.i_method = INPUT_METHOD_VCD;
1093
1094     p_input->stream.i_area_nb = 1;
1095     
1096
1097     /* Initialize segment information. */
1098     VCDSegments( p_input );
1099     
1100     /* Initialize track area information. */
1101     VCDTracks( p_input );
1102     
1103     if( VCDEntryPoints( p_input ) < 0 )
1104     {
1105         msg_Warn( p_input, "could not read entry points, will not use them" );
1106         p_vcd->b_valid_ep = false;
1107     }
1108
1109     if( VCDLIDs( p_input ) < 0 )
1110     {
1111         msg_Warn( p_input, "could not read entry LIDs" );
1112     }
1113
1114     b_play_ok = (VLC_SUCCESS == VCDPlay( p_input, itemid ));
1115     
1116     vlc_mutex_unlock( &p_input->stream.stream_lock );
1117
1118     if ( ! b_play_ok ) {
1119       vcdinfo_close( p_vcd->vcd );
1120       free( p_vcd );
1121       return VLC_EGENERIC;
1122     }
1123
1124     if( !p_input->psz_demux || !*p_input->psz_demux )
1125     {
1126 #if FIXED
1127       p_input->psz_demux = "vcdx";
1128 #else
1129       p_input->psz_demux = "ps";
1130 #endif
1131     }
1132
1133     p_vcd->p_intf = intf_Create( p_input, "vcdx" );
1134     p_vcd->p_intf->b_block = VLC_FALSE;
1135     intf_RunThread( p_vcd->p_intf );
1136
1137     InformationCreate( p_input );
1138
1139     return VLC_SUCCESS;
1140 }
1141
1142 /*****************************************************************************
1143  * Close: closes VCD releasing allocated memory.
1144  *****************************************************************************/
1145 void 
1146 E_(Close) ( vlc_object_t *p_this )
1147 {
1148     input_thread_t *   p_input = (input_thread_t *)p_this;
1149     thread_vcd_data_t *p_vcd = (thread_vcd_data_t *)p_input->p_access_data;
1150
1151     dbg_print( (INPUT_DBG_CALL|INPUT_DBG_EXT), "VCDClose" );
1152     vcdinfo_close( p_vcd->vcd );
1153
1154     free( p_vcd->p_entries );
1155     free( p_vcd->p_segments );
1156
1157     /* For reasons that are a mystery to me we don't have to deal with
1158        stopping, and destroying the p_vcd->p_intf thread. And if we do
1159        it causes problems upstream.
1160      */
1161     if( p_vcd->p_intf != NULL )
1162     {
1163         p_vcd->p_intf = NULL;
1164     }
1165
1166     free( p_vcd );
1167     p_input->p_access_data = NULL;
1168     p_vcd_input = NULL;
1169 }