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