]> git.sesse.net Git - vlc/blob - modules/access/vcdx/vcd.c
d2330d9d03fc73d7915bbf4ce9718d967494c709
[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,2003 VideoLAN
7  * $Id: vcd.c,v 1.5 2003/11/20 02:15:37 rocky 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 #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  VCDOpen         ( vlc_object_t * );
59 static void VCDClose        ( vlc_object_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                    b_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     b_play_ok = (VLC_SUCCESS == VCDPlay( p_input, itemid ));
330     
331     vlc_mutex_unlock( &p_input->stream.stream_lock );
332
333     if ( ! b_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     p_vcd->p_intf = intf_Create( p_input, "vcdx" );
349     p_vcd->p_intf->b_block = VLC_FALSE;
350     intf_RunThread( p_vcd->p_intf );
351
352     return VLC_SUCCESS;
353 }
354
355 /*****************************************************************************
356  * VCDClose: closes VCD releasing allocated memory.
357  *****************************************************************************/
358 static void 
359 VCDClose( vlc_object_t *p_this )
360 {
361     input_thread_t *   p_input = (input_thread_t *)p_this;
362     thread_vcd_data_t *p_vcd = (thread_vcd_data_t *)p_input->p_access_data;
363
364     dbg_print( (INPUT_DBG_CALL|INPUT_DBG_EXT), "VCDClose" );
365     vcdinfo_close( p_vcd->vcd );
366
367     free( p_vcd->p_entries );
368     free( p_vcd->p_segments );
369
370     /* For reasons that are a mystery to me we don't have to deal with
371        stopping, and destroying the p_vcd->p_intf thread. And if we do
372        it causes problems upstream.
373      */
374     if( p_vcd->p_intf != NULL )
375     {
376         p_vcd->p_intf = NULL;
377     }
378
379     free( p_vcd );
380     p_input->p_access_data = NULL;
381     p_vcd_input = NULL;
382 }
383
384 /*****************************************************************************
385  * VCDRead: reads i_len bytes from the VCD into p_buffer.
386  *****************************************************************************
387  * Returns -1 in case of error, 0 in case of EOF, otherwise the number of
388  * bytes.
389  *****************************************************************************/
390 static int 
391 VCDRead( input_thread_t * p_input, byte_t * p_buffer, size_t i_len )
392 {
393     thread_vcd_data_t *     p_vcd= (thread_vcd_data_t *)p_input->p_access_data;
394     int                     i_blocks;
395     int                     i_index;
396     int                     i_read;
397     byte_t                  p_last_sector[ M2F2_SECTOR_SIZE ];
398
399     i_read = 0;
400
401     dbg_print( (INPUT_DBG_CALL), "lsn: %u", p_vcd->cur_lsn );
402
403     /* Compute the number of blocks we have to read */
404
405     i_blocks = i_len / M2F2_SECTOR_SIZE;
406
407     for ( i_index = 0 ; i_index < i_blocks ; i_index++ )
408     {
409
410       if ( p_vcd->cur_lsn == p_vcd->end_lsn ) {
411         vcdplayer_read_status_t read_status;
412
413         /* We've run off of the end of this entry. Do we continue or stop? */
414         dbg_print( (INPUT_DBG_LSN|INPUT_DBG_PBC), 
415                    "end reached, cur: %u", p_vcd->cur_lsn );
416
417         read_status = vcdplayer_pbc_is_on( p_vcd ) 
418           ? vcdplayer_pbc_nav( p_input ) 
419           : vcdplayer_non_pbc_nav( p_input );
420
421         switch (read_status) {
422         case READ_END:
423           /* End reached. Return NULL to indicated this. */
424         case READ_ERROR:
425           /* Some sort of error. */
426           return i_read;
427
428         case READ_STILL_FRAME: 
429           {
430             /* Reached the end of a still frame. */
431
432             byte_t * p_buf = p_buffer;
433             pgrm_descriptor_t * p_pgrm = p_input->stream.p_selected_program;;
434
435             p_buf += (i_index*M2F2_SECTOR_SIZE);
436             memset(p_buf, 0, M2F2_SECTOR_SIZE);
437             p_buf += 2;
438             *p_buf = 0x01;
439             dbg_print(INPUT_DBG_STILL, "Handled still event\n");
440
441             /* p_vcd->p_intf->b_end_of_cell = true; */
442             input_SetStatus( p_input, INPUT_STATUS_PAUSE );
443
444             vlc_mutex_lock( &p_input->stream.stream_lock );
445
446             p_pgrm = p_input->stream.p_selected_program;
447             p_pgrm->i_synchro_state = SYNCHRO_REINIT;
448
449             vlc_mutex_unlock( &p_input->stream.stream_lock );
450
451             input_ClockManageControl( p_input, p_pgrm, 0 );
452
453             return i_read + M2F2_SECTOR_SIZE;
454           }
455         default:
456         case READ_BLOCK:
457           break;
458         }
459       }
460
461       if ( VCDReadSector( VLC_OBJECT(p_input), p_vcd->vcd,
462                           p_vcd->cur_lsn, 
463                           p_buffer + (i_index*M2F2_SECTOR_SIZE) ) < 0 )
464         {
465           LOG_ERR ("could not read sector %d", p_vcd->cur_lsn );
466           return -1;
467         }
468       
469       p_vcd->cur_lsn ++;
470       
471       /* Update chapter */
472       if( p_vcd->b_valid_ep &&
473           /* FIXME kludge so that read does not update chapter
474            * when a manual chapter change was requested and not
475            * yet accomplished */
476           !p_input->stream.p_new_area )
477         {
478           unsigned int i_entry = p_input->stream.p_selected_area->i_part;
479           
480           vlc_mutex_lock( &p_input->stream.stream_lock );
481           
482           if( i_entry < p_vcd->num_entries &&
483               p_vcd->cur_lsn >= p_vcd->p_entries[i_entry+1] )
484             {
485               dbg_print( INPUT_DBG_PBC, 
486                          "new entry, i_entry %d, sector %d, es %d",
487                          i_entry, p_vcd->cur_lsn, 
488                          p_vcd->p_entries[i_entry] );
489               p_vcd->play_item.num = 
490                 ++ p_input->stream.p_selected_area->i_part;
491               p_vcd->play_item.type = VCDINFO_ITEM_TYPE_ENTRY;
492               VCDUpdateVar( p_input, p_vcd->play_item.num, VLC_VAR_SETVALUE,
493                             "chapter", "Setting entry" );
494             }
495           vlc_mutex_unlock( &p_input->stream.stream_lock );
496         }
497
498         i_read += M2F2_SECTOR_SIZE;
499     }
500
501     if ( i_len % M2F2_SECTOR_SIZE ) /* this should not happen */
502     {
503         if ( VCDReadSector( VLC_OBJECT(p_input), p_vcd->vcd,
504                             p_vcd->cur_lsn, p_last_sector ) < 0 )
505         {
506             LOG_ERR ("could not read sector %d", p_vcd->cur_lsn );
507             return -1;
508         }
509
510         p_input->p_vlc->pf_memcpy( p_buffer + i_blocks * M2F2_SECTOR_SIZE,
511                                    p_last_sector, i_len % M2F2_SECTOR_SIZE );
512         i_read += i_len % M2F2_SECTOR_SIZE;
513     }
514
515     return i_read;
516 }
517
518
519 /*****************************************************************************
520  * VCDSetProgram: Does nothing since a VCD is mono_program
521  *****************************************************************************/
522 static int 
523 VCDSetProgram( input_thread_t * p_input, pgrm_descriptor_t * p_program)
524 {
525     thread_vcd_data_t *     p_vcd= (thread_vcd_data_t *)p_input->p_access_data;
526     dbg_print( (INPUT_DBG_CALL|INPUT_DBG_EXT), "VCDSetProgram" );
527     return 0;
528 }
529
530
531 /*****************************************************************************
532  * VCDSetArea: initialize internal data structures and input stream data 
533    so set subsequent reading and seeking to reflect that we are
534    at track x, entry or segment y.
535    This is called for each user navigation request, e.g. the GUI 
536    Chapter/Title selections or in initial MRL parsing. 
537  ****************************************************************************/
538 int 
539 VCDSetArea( input_thread_t * p_input, input_area_t * p_area )
540 {
541     thread_vcd_data_t *p_vcd = (thread_vcd_data_t*)p_input->p_access_data;
542     unsigned int i_entry = p_area->i_part;
543     track_t i_track      = p_area->i_id;
544     int old_seekable     = p_input->stream.b_seekable;
545     unsigned int i_nb    = p_area->i_plugin_data + p_area->i_part_nb;
546
547     dbg_print( (INPUT_DBG_CALL|INPUT_DBG_EXT),
548                "track: %d, entry %d, seekable %d, area %lx, select area %lx ",
549                i_track, i_entry, old_seekable, 
550                (long unsigned int) p_area, 
551                (long unsigned int) p_input->stream.p_selected_area );
552
553     /* we can't use the interface slider until initilization is complete */
554     p_input->stream.b_seekable = 0;
555
556     if( p_area != p_input->stream.p_selected_area )
557     {
558         unsigned int i;
559
560         /* If is the result of a track change, make the entry valid. */
561         if (i_entry < p_area->i_plugin_data || i_entry >= i_nb) 
562           i_entry = p_area->i_plugin_data;
563
564         /* Change the default area */
565         p_input->stream.p_selected_area = p_area;
566
567         /* Update the navigation variables without triggering a callback */
568         VCDUpdateVar( p_input, i_track, VLC_VAR_SETVALUE, "title", 
569                       "Setting track");
570
571         var_Change( p_input, "chapter", VLC_VAR_CLEARCHOICES, NULL, NULL );
572         for( i = p_area->i_plugin_data; i < i_nb; i++ )
573         {
574           VCDUpdateVar( p_input, i , VLC_VAR_ADDCHOICE, 
575                         "chapter",  "Adding entry choice");
576         }
577     }
578
579     if (i_track == 0) 
580       VCDSetOrigin( p_input, p_vcd->p_segments[i_entry], 
581                     p_vcd->p_segments[i_entry], p_vcd->p_segments[i_entry+1],
582                     i_entry, 0 );
583     else
584       VCDSetOrigin( p_input, p_vcd->p_sectors[i_track], 
585                     vcdinfo_get_entry_lsn(p_vcd->vcd, i_entry), 
586                     p_vcd->p_sectors[i_track+1],
587                     i_entry, i_track );
588
589     p_input->stream.b_seekable = old_seekable;
590     /* warn interface that something has changed */
591     p_input->stream.b_changed = 1;
592
593     return VLC_SUCCESS;
594 }
595
596
597 /****************************************************************************
598  * VCDSeek
599  ****************************************************************************/
600 void 
601 VCDSeek( input_thread_t * p_input, off_t i_off )
602 {
603     thread_vcd_data_t * p_vcd;
604     unsigned int i_entry=0; /* invalid entry */
605
606     p_vcd = (thread_vcd_data_t *) p_input->p_access_data;
607
608     p_vcd->cur_lsn = p_vcd->origin_lsn + (i_off / (off_t)M2F2_SECTOR_SIZE);
609
610     vlc_mutex_lock( &p_input->stream.stream_lock );
611 #define p_area p_input->stream.p_selected_area
612     /* Find entry */
613     if( p_vcd->b_valid_ep )
614     {
615         for( i_entry = 1 ; i_entry < p_vcd->num_entries ; i_entry ++ )
616         {
617             if( p_vcd->cur_lsn < p_vcd->p_entries[i_entry] )
618             {
619               VCDUpdateVar( p_input, i_entry, VLC_VAR_SETVALUE, 
620                             "chapter", "Setting entry" );
621               break;
622             }
623         }
624         p_vcd->play_item.num  = p_area->i_part = i_entry;
625         p_vcd->play_item.type = VCDINFO_ITEM_TYPE_ENTRY;
626     }
627 #undef p_area
628
629     p_input->stream.p_selected_area->i_tell = i_off;
630
631     dbg_print( (INPUT_DBG_CALL|INPUT_DBG_EXT|INPUT_DBG_SEEK),
632     "orig %d, cur: %d, offset: %lld, start: %lld, entry %d", 
633                p_vcd->origin_lsn, p_vcd->cur_lsn, i_off, 
634                p_input->stream.p_selected_area->i_start, i_entry );
635
636     vlc_mutex_unlock( &p_input->stream.stream_lock );
637 }
638
639 /*****************************************************************************
640   VCDPlay: set up internal structures so seeking/reading places an item.
641   itemid: the thing to play.
642   user_entry: true if itemid is a user selection (rather than internally-
643   generated selection such as via PBC) in which case we may have to adjust 
644   for differences in numbering. 
645  *****************************************************************************/
646 int
647 VCDPlay( input_thread_t *p_input, vcdinfo_itemid_t itemid )
648 {
649     thread_vcd_data_t *     p_vcd= (thread_vcd_data_t *)p_input->p_access_data;
650     input_area_t *          p_area;
651     
652     p_vcd->in_still = 0;
653
654     dbg_print(INPUT_DBG_CALL, "itemid.num: %d, itemid.type: %d\n", 
655               itemid.num, itemid.type);
656
657 #define area p_input->stream.pp_areas
658
659     switch (itemid.type) {
660     case VCDINFO_ITEM_TYPE_TRACK:
661
662       /* Valid tracks go from 1...num_tracks-1, because track 0 is unplayable.
663        */
664
665       if (itemid.num == 0 || itemid.num >= p_vcd->num_tracks) {
666         LOG_ERR ("Invalid track number %d", itemid.num );
667         return VLC_EGENERIC;
668       }
669       p_area           = area[itemid.num];
670       p_area->i_part   = p_area->i_plugin_data;
671       p_input->stream.b_seekable = 1;
672       break;
673     case VCDINFO_ITEM_TYPE_SEGMENT: 
674       /* Valid segments go from 0...num_segments-1. */
675       if (itemid.num >= p_vcd->num_segments) {
676         LOG_ERR ( "Invalid segment number: %d", itemid.num );
677         return VLC_EGENERIC;
678       } else {
679         vcdinfo_video_segment_type_t segtype = 
680           vcdinfo_get_video_type(p_vcd->vcd, itemid.num);
681         
682         dbg_print(INPUT_DBG_PBC, "%s (%d), seg_num: %d", 
683                   vcdinfo_video_type2str(p_vcd->vcd, itemid.num), 
684                   (int) segtype, itemid.num);
685         
686         p_area           = area[0];
687         p_area->i_part   = itemid.num;
688         
689         switch (segtype)
690           {
691           case VCDINFO_FILES_VIDEO_NTSC_STILL:
692           case VCDINFO_FILES_VIDEO_NTSC_STILL2:
693           case VCDINFO_FILES_VIDEO_PAL_STILL:
694           case VCDINFO_FILES_VIDEO_PAL_STILL2:
695             p_input->stream.b_seekable = 0;
696             p_vcd->in_still = -5;
697             break;
698           default:
699             p_input->stream.b_seekable = 1;
700             p_vcd->in_still = 0;
701           }
702       }
703       break;
704       
705     case VCDINFO_ITEM_TYPE_LID:
706       /* LIDs go from 1..num_lids. */
707       if (itemid.num == 0 || itemid.num > p_vcd->num_lids) {
708         LOG_ERR ( "Invalid LID number: %d", itemid.num );
709         return VLC_EGENERIC;
710       } else {
711         p_vcd->cur_lid = itemid.num;
712         vcdinfo_lid_get_pxd(p_vcd->vcd, &(p_vcd->pxd), itemid.num);
713     
714         switch (p_vcd->pxd.descriptor_type) {
715       
716         case PSD_TYPE_SELECTION_LIST:
717         case PSD_TYPE_EXT_SELECTION_LIST: {
718           vcdinfo_itemid_t trans_itemid;
719           uint16_t trans_itemid_num;
720           
721           if (p_vcd->pxd.psd == NULL) return VLC_EGENERIC;
722           trans_itemid_num  = vcdinf_psd_get_itemid(p_vcd->pxd.psd);
723           vcdinfo_classify_itemid(trans_itemid_num, &trans_itemid);
724           p_vcd->loop_count = 1;
725           p_vcd->loop_item  = trans_itemid;
726           return VCDPlay( p_input, trans_itemid );
727           break;
728         }
729           
730         case PSD_TYPE_PLAY_LIST: {
731           if (p_vcd->pxd.pld == NULL) return VLC_EGENERIC;
732           p_vcd->pdi = -1;
733           return vcdplayer_inc_play_item(p_input) 
734             ? VLC_SUCCESS : VLC_EGENERIC;
735           break;
736         }
737           
738         case PSD_TYPE_END_LIST:
739         case PSD_TYPE_COMMAND_LIST:
740           
741         default:
742           ;
743         }
744       }
745       return VLC_EGENERIC;
746     case VCDINFO_ITEM_TYPE_ENTRY:
747       /* Entries go from 0..num_entries-1. */
748       if (itemid.num >= p_vcd->num_entries) {
749         LOG_ERR ("Invalid entry number: %d", itemid.num );
750         return VLC_EGENERIC;
751       } else {
752         track_t cur_track  = vcdinfo_get_track(p_vcd->vcd,  itemid.num);
753         p_area             = area[cur_track];
754         p_area->i_part     = itemid.num;
755         p_input->stream.b_seekable = 1;
756       }
757       break;
758     default:
759       LOG_ERR ("unknown entry type" );
760       return VLC_EGENERIC;
761     }
762
763     VCDSetArea( p_input, p_area );
764
765 #undef area
766
767     p_vcd->play_item = itemid;
768
769     dbg_print( (INPUT_DBG_CALL), 
770                "i_start %lld, i_size: %lld, i_tell: %lld, lsn %d", 
771                p_area->i_start, p_area->i_size, 
772                p_area->i_tell, p_vcd->cur_lsn );
773         
774     return VLC_SUCCESS;
775 }
776
777 /*****************************************************************************
778   VCDEntryPoints: Reads the information about the entry points on the disc
779   and initializes area information with that.
780   Before calling this track information should have been read in.
781  *****************************************************************************/
782 static int 
783 VCDEntryPoints( input_thread_t * p_input )
784 {
785     thread_vcd_data_t *               p_vcd;
786     unsigned int                      i_nb;
787     unsigned int                      i, i_entry_index = 0;
788     unsigned int                      i_previous_track = CDIO_INVALID_TRACK;
789
790     p_vcd = (thread_vcd_data_t *) p_input->p_access_data;
791
792     i_nb = vcdinfo_get_num_entries(p_vcd->vcd);
793     if (0 == i_nb) 
794       return -1;
795     
796     p_vcd->p_entries  = malloc( sizeof( lba_t ) * i_nb );
797
798     if( p_vcd->p_entries == NULL )
799     {
800         LOG_ERR ("not enough memory for entry points treatment" );
801         return -1;
802     }
803
804     p_vcd->num_entries = 0;
805
806     for( i = 0 ; i < i_nb ; i++ )
807     {
808         track_t i_track = vcdinfo_get_track(p_vcd->vcd, i);
809         if( i_track <= p_input->stream.i_area_nb )
810         {
811             p_vcd->p_entries[i] = 
812               vcdinfo_get_entry_lsn(p_vcd->vcd, i);
813             p_input->stream.pp_areas[i_track]->i_part_nb ++;
814
815             /* if this entry belongs to a new track */
816             if( i_track != i_previous_track )
817             {
818                 /* i_plugin_data is used to store the first entry of the area*/
819                 p_input->stream.pp_areas[i_track]->i_plugin_data =
820                                                             i_entry_index;
821                 i_previous_track = i_track;
822                 p_input->stream.pp_areas[i_track]->i_part_nb = 1;
823             }
824             i_entry_index ++;
825             p_vcd->num_entries ++;
826         }
827         else
828             msg_Warn( p_input, "wrong track number found in entry points" );
829     }
830     p_vcd->b_valid_ep = true;
831     return 0;
832 }
833
834 /*****************************************************************************
835  * VCDSegments: Reads the information about the segments the disc.
836  *****************************************************************************/
837 static int
838 VCDSegments( input_thread_t * p_input )
839 {
840     thread_vcd_data_t * p_vcd;
841     unsigned int        i;
842     unsigned int        num_segments;
843     
844
845     p_vcd = (thread_vcd_data_t *) p_input->p_access_data;
846     num_segments = p_vcd->num_segments = vcdinfo_get_num_segments(p_vcd->vcd);
847
848 #define area p_input->stream.pp_areas
849
850     /* area 0 is reserved for segments. Set Absolute start offset
851          and size */
852     area[0]->i_plugin_data = 0;
853     input_DelArea( p_input, area[0] );
854     input_AddArea( p_input, 0, 0 );
855     
856     area[0]->i_start = (off_t)p_vcd->p_sectors[0] 
857       * (off_t)M2F2_SECTOR_SIZE;
858     area[0]->i_size = (off_t)(p_vcd->p_sectors[1] - p_vcd->p_sectors[0])
859       * (off_t)M2F2_SECTOR_SIZE;
860     
861     /* Default Segment  */
862     area[0]->i_part = 0;
863     
864     /* i_plugin_data is used to store which entry point is the first
865        of the track (area) */
866     area[0]->i_plugin_data = 0;
867
868     area[0]->i_part_nb = 0;
869     
870     dbg_print( INPUT_DBG_MRL, 
871                "area[0] id: %d, i_start: %lld, i_size: %lld", 
872                area[0]->i_id, area[0]->i_start, area[0]->i_size );
873
874     if (num_segments == 0) return 0;
875
876     /* We have one additional segment allocated so we can get the size
877        by subtracting seg[i+1] - seg[i].
878      */
879     p_vcd->p_segments = malloc( sizeof( lba_t ) * (num_segments+1) );
880     if( p_vcd->p_segments == NULL )
881     {
882         LOG_ERR ("not enough memory for segment treatment" );
883         return -1;
884     }
885
886     /* Update the navigation variables without triggering a callback */
887     VCDUpdateVar( p_input, 0, VLC_VAR_SETVALUE, "title", "Setting track" );
888     var_Change( p_input, "chapter", VLC_VAR_CLEARCHOICES, NULL, NULL );
889     
890     for( i = 0 ; i < num_segments ; i++ )
891     {
892       p_vcd->p_segments[i] = vcdinfo_get_seg_lsn(p_vcd->vcd, i);
893       area[0]->i_part_nb ++;
894       VCDUpdateVar( p_input, i , VLC_VAR_ADDCHOICE, 
895                     "chapter", "Adding segment choice");
896     }
897
898 #undef area
899
900     p_vcd->p_segments[num_segments] = p_vcd->p_segments[num_segments-1]+
901       vcdinfo_get_seg_sector_count(p_vcd->vcd, num_segments-1);
902     
903     return 0;
904 }
905
906 /*****************************************************************************
907  VCDTracks: initializes area information. 
908  Before calling this track information should have been read in.
909  *****************************************************************************/
910 static void
911 VCDTracks( input_thread_t * p_input )
912 {
913     thread_vcd_data_t * p_vcd;
914     unsigned int        i;
915
916     p_vcd = (thread_vcd_data_t *) p_input->p_access_data;
917
918 #define area p_input->stream.pp_areas
919
920     /* We start area addressing for tracks at 1 since the default area 0
921        is reserved for segments */
922
923     for( i = 1 ; i < p_vcd->num_tracks ; i++ )
924     {
925         /* Tracks are Program Chains */
926         input_AddArea( p_input, i, i );
927
928         /* Absolute start byte offset and byte size */
929         area[i]->i_start = (off_t) p_vcd->p_sectors[i] 
930                            * (off_t)M2F2_SECTOR_SIZE;
931         area[i]->i_size = (off_t)(p_vcd->p_sectors[i+1] - p_vcd->p_sectors[i])
932                            * (off_t)M2F2_SECTOR_SIZE;
933
934         /* Current entry being played in track */
935         area[i]->i_part = 0;
936
937         /* i_plugin_data is used to store which entry point is the first
938          * of the track (area) */
939         area[i]->i_plugin_data = 0;
940
941         dbg_print( INPUT_DBG_MRL, 
942                    "area[%d] id: %d, i_start: %lld, i_size: %lld", 
943                    i, area[i]->i_id, area[i]->i_start, area[i]->i_size );
944     }
945
946 #undef area
947
948     return ;
949 }
950
951 /*****************************************************************************
952   VCDLIDs: Reads the LIST IDs from the LOT.
953  *****************************************************************************/
954 static int 
955 VCDLIDs( input_thread_t * p_input )
956 {
957     thread_vcd_data_t *p_vcd = (thread_vcd_data_t *) p_input->p_access_data;
958
959     p_vcd->num_lids = vcdinfo_get_num_LIDs(p_vcd->vcd);
960     p_vcd->cur_lid  = VCDINFO_INVALID_ENTRY;
961
962     if (vcdinfo_read_psd (p_vcd->vcd)) {
963       
964       vcdinfo_visit_lot (p_vcd->vcd, false);
965       
966 #if FIXED
967     /* 
968        We need to change libvcdinfo to be more robust when there are 
969        problems reading the extended PSD. Given that area-highlighting and 
970        selection features in the extended PSD haven't been implemented,
971        it's best then to not try to read this at all.
972      */
973       if (vcdinfo_get_psd_x_size(p_vcd->vcd))
974         vcdinfo_visit_lot (p_vcd->vcd, true);
975 #endif 
976     }
977
978     dbg_print( (INPUT_DBG_CALL|INPUT_DBG_MRL), 
979                "num LIDs=%d", p_vcd->num_lids);
980
981     return 0;
982 }
983
984 /*****************************************************************************
985  * VCDParse: parse command line
986  *****************************************************************************/
987 static char * 
988 VCDParse( input_thread_t * p_input, /*out*/ vcdinfo_itemid_t * p_itemid )
989 {
990     thread_vcd_data_t *p_vcd = (thread_vcd_data_t *)p_input->p_access_data;
991     char *             psz_parser;
992     char *             psz_source;
993     char *             psz_next;
994
995     p_itemid->type=VCDINFO_ITEM_TYPE_TRACK;
996     p_itemid->num=1;
997     
998 #ifdef WIN32
999     /* On Win32 we want the VCD access plugin to be explicitly requested,
1000      * we end up with lots of problems otherwise */
1001     if( !p_input->psz_access || !*p_input->psz_access ) return NULL;
1002 #endif
1003
1004     if( !p_input->psz_name )
1005     {
1006         return NULL;
1007     }
1008
1009     psz_parser = psz_source = strdup( p_input->psz_name );
1010
1011     /* Parse input string :
1012      * [device][@[type][title]] */
1013     while( *psz_parser && *psz_parser != '@' )
1014     {
1015         psz_parser++;
1016     }
1017
1018     if( *psz_parser == '@' )
1019     {
1020       /* Found the divide between the source name and the 
1021          type+entry number. */
1022       unsigned int num;
1023       
1024       *psz_parser = '\0';
1025       ++psz_parser;
1026       if( *psz_parser )
1027         {
1028           switch(*psz_parser) {
1029           case 'E': 
1030             p_itemid->type = VCDINFO_ITEM_TYPE_ENTRY;
1031             ++psz_parser;
1032             break;
1033           case 'P': 
1034             p_itemid->type = VCDINFO_ITEM_TYPE_LID;
1035             ++psz_parser;
1036             break;
1037           case 'S': 
1038             p_itemid->type = VCDINFO_ITEM_TYPE_SEGMENT;
1039             ++psz_parser;
1040             break;
1041           case 'T': 
1042             p_itemid->type = VCDINFO_ITEM_TYPE_TRACK;
1043             ++psz_parser;
1044             break;
1045           default: ;
1046           }
1047         }
1048       
1049       num = strtol( psz_parser, &psz_next, 10 );
1050       if ( *psz_parser != '\0' && *psz_next == '\0') 
1051         {
1052           p_itemid->num = num;
1053         }
1054       
1055     }
1056
1057     if( !*psz_source ) {
1058
1059       /* No source specified, so figure it out. */
1060       if( !p_input->psz_access ) return NULL;
1061       
1062       psz_source = config_GetPsz( p_input, MODULE_STRING "-device" );
1063
1064       if( !psz_source ) {
1065         /* Scan for a CD with a VCD in it. */
1066         char **cd_drives = cdio_get_devices_with_cap(NULL, 
1067                             (CDIO_FS_ANAL_SVCD|CDIO_FS_ANAL_CVD
1068                              |CDIO_FS_ANAL_VIDEOCD|CDIO_FS_UNKNOWN),
1069                                                      true);
1070         if (NULL == cd_drives) return NULL;
1071         if (cd_drives[0] == NULL) {
1072           cdio_free_device_list(cd_drives);
1073           return NULL;
1074         }
1075         psz_source = strdup(cd_drives[0]);
1076         cdio_free_device_list(cd_drives);
1077       }
1078     }
1079
1080     dbg_print( (INPUT_DBG_CALL|INPUT_DBG_MRL), 
1081                "source=%s entry=%d type=%d",
1082                psz_source, p_itemid->num, p_itemid->type);
1083
1084     return psz_source;
1085 }
1086
1087 /* 
1088    Set's start origin subsequent seeks/reads
1089 */
1090 static void 
1091 VCDSetOrigin( input_thread_t *p_input, lsn_t origin_lsn, 
1092               lsn_t cur_lsn, lsn_t end_lsn, int cur_entry, track_t cur_track )
1093 {
1094   thread_vcd_data_t * p_vcd  = (thread_vcd_data_t *) p_input->p_access_data;
1095
1096   p_vcd->origin_lsn = origin_lsn;
1097   p_vcd->cur_lsn    = cur_lsn;
1098   p_vcd->end_lsn    = end_lsn;
1099   p_vcd->cur_track  = cur_track;
1100   p_vcd->play_item.num  = cur_entry;
1101   p_vcd->play_item.type = VCDINFO_ITEM_TYPE_ENTRY;
1102   
1103   dbg_print( (INPUT_DBG_CALL|INPUT_DBG_LSN),
1104              "origin: %d, cur_lsn: %d, end_lsn: %d, entry: %d, track: %d",
1105              origin_lsn, cur_lsn, end_lsn, cur_entry, cur_track );
1106
1107   p_input->stream.p_selected_area->i_tell =
1108     (off_t) (p_vcd->cur_lsn - p_vcd->origin_lsn) * (off_t)M2F2_SECTOR_SIZE;
1109
1110   VCDUpdateVar( p_input, cur_entry, VLC_VAR_SETVALUE, 
1111                 "chapter", "Setting entry");
1112 }
1113
1114 /*****************************************************************************
1115  * vcd_Open: Opens a VCD device or file and returns an opaque handle
1116  *****************************************************************************/
1117 static vcdinfo_obj_t *
1118 vcd_Open( vlc_object_t *p_this, const char *psz_dev )
1119 {
1120     vcdinfo_obj_t *p_vcdobj;
1121     char  *actual_dev;
1122
1123     if( !psz_dev ) return NULL;
1124
1125     /* Set where to log errors messages from libcdio. */
1126     p_vcd_input = (input_thread_t *)p_this;
1127     cdio_log_set_handler ( cdio_log_handler );
1128     vcd_log_set_handler ( vcd_log_handler );
1129     
1130     actual_dev=strdup(psz_dev);
1131     if ( vcdinfo_open(&p_vcdobj, &actual_dev, DRIVER_UNKNOWN, NULL) != 
1132          VCDINFO_OPEN_VCD) {
1133       free(actual_dev);
1134       return NULL;
1135     }
1136     free(actual_dev);
1137
1138     return p_vcdobj;
1139 }
1140
1141 /****************************************************************************
1142  * VCDReadSector: Read a sector (2324 bytes)
1143  ****************************************************************************/
1144 static int 
1145 VCDReadSector( vlc_object_t *p_this, const vcdinfo_obj_t *p_vcd,
1146                lsn_t cur_lsn, byte_t * p_buffer )
1147 {
1148   typedef struct {
1149     uint8_t subheader   [8];
1150     uint8_t data        [M2F2_SECTOR_SIZE];
1151   } vcdsector_t;
1152   vcdsector_t vcd_sector;
1153   
1154   if (cdio_read_mode2_sector(vcdinfo_get_cd_image(p_vcd), 
1155                              &vcd_sector, cur_lsn, true) 
1156       != 0)
1157     {
1158       msg_Warn( p_this, "Could not read LSN %d", cur_lsn );
1159       return -1;
1160     }
1161     
1162   memcpy (p_buffer, vcd_sector.data, M2F2_SECTOR_SIZE);
1163   
1164   return( 0 );
1165 }
1166
1167 /****************************************************************************
1168  Update the "varname" variable to i_num without triggering a callback.
1169 ****************************************************************************/
1170 static void
1171 VCDUpdateVar( input_thread_t *p_input, int i_num, int i_action,
1172               const char *varname, const char *label)
1173 {
1174   vlc_value_t val;
1175   val.i_int = i_num;
1176   if (NULL != p_vcd_input) {
1177     thread_vcd_data_t *p_vcd = (thread_vcd_data_t *)p_vcd_input->p_access_data;
1178     dbg_print( INPUT_DBG_PBC, "%s %d", label, i_num );
1179   }
1180   var_Change( p_input, varname, i_action, &val, NULL );
1181 }