]> git.sesse.net Git - vlc/blob - modules/access/vcdx/access.c
Some cleanup and unification with xine vcd plugin.
[vlc] / modules / access / vcdx / access.c
1 /*****************************************************************************
2  * vcd.c : VCD input module for vlc using libcdio, libvcd and libvcdinfo. 
3  *         vlc-specific things tend to go here.
4  *****************************************************************************
5  * Copyright (C) 2000, 2003, 2004 VideoLAN
6  * $Id$
7  *
8  * Authors: Rocky Bernstein <rocky@panix.com>
9  *   Some code is based on the non-libcdio VCD plugin (as there really
10  *   isn't real developer documentation yet on how to write a 
11  *   navigable plugin.)
12  *
13  * This program is free software; you can redistribute it and/or modify
14  * it under the terms of the GNU General Public License as published by
15  * the Free Software Foundation; either version 2 of the License, or
16  * (at your option) any later version.
17  *
18  * This program is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21  * GNU General Public License for more details.
22  *
23  * You should have received a copy of the GNU General Public License
24  * along with this program; if not, write to the Free Software
25  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
26  *****************************************************************************/
27
28 /*****************************************************************************
29  * Preamble
30  *****************************************************************************/
31
32 #include <vlc/vlc.h>
33 #include <vlc/intf.h>
34 #include <vlc/input.h>
35 #include "vlc_keys.h"
36
37 #include <cdio/cdio.h>
38 #include <cdio/cd_types.h>
39 #include <cdio/logging.h>
40 #include <cdio/util.h>
41 #include <libvcd/info.h>
42 #include <libvcd/logging.h>
43 #include "vcd.h"
44 #include "info.h"
45 #include "intf.h"
46
47 #define FREE_AND_NULL(ptr) if (NULL != ptr) free(ptr); ptr = NULL;
48
49 extern void VCDSetOrigin( access_t *p_access, lsn_t i_lsn, track_t i_track, 
50                           const vcdinfo_itemid_t *p_itemid );
51
52 /*****************************************************************************
53  * Local prototypes
54  *****************************************************************************/
55
56 /* First those which are accessed from outside (via pointers). */
57 static block_t *VCDReadBlock    ( access_t * );
58
59 static int      VCDControl      ( access_t *p_access, int i_query, 
60                                   va_list args );
61
62 /* Now those which are strictly internal */
63 static vlc_bool_t  VCDEntryPoints  ( access_t * );
64 static vlc_bool_t  VCDLIDs         ( access_t * );
65 static vlc_bool_t  VCDSegments     ( access_t * );
66 static int  VCDTitles       ( access_t * );
67 static char *VCDParse       ( access_t *,
68                               /*out*/ vcdinfo_itemid_t * p_itemid ,
69                               /*out*/ vlc_bool_t *play_single_item );
70
71 static void VCDUpdateVar( access_t *p_access, int i_entry, int i_action,
72                           const char *p_varname, char *p_label,
73                           const char *p_debug_label );
74
75 static vcdinfo_obj_t *vcd_Open   ( vlc_object_t *p_this, const char *psz_dev );
76
77 /****************************************************************************
78  * Private functions
79  ****************************************************************************/
80
81 /* FIXME: This variable is a hack. Would be nice to eliminate the
82    global-ness. */
83
84 static access_t *p_vcd_access = NULL;
85
86 /* process messages that originate from libcdio. */
87 static void
88 cdio_log_handler (cdio_log_level_t level, const char message[])
89 {
90   const vcdplayer_t *p_vcdplayer = (vcdplayer_t *)p_vcd_access->p_sys;
91   switch (level) {
92   case CDIO_LOG_DEBUG:
93   case CDIO_LOG_INFO:
94     if (p_vcdplayer->i_debug & INPUT_DBG_CDIO)
95       msg_Dbg( p_vcd_access, message);
96     break;
97   case CDIO_LOG_WARN:
98     msg_Warn( p_vcd_access, message);
99     break;
100   case CDIO_LOG_ERROR:
101   case CDIO_LOG_ASSERT:
102     msg_Err( p_vcd_access, message);
103     break;
104   default:
105     msg_Warn( p_vcd_access, message,
106             _("The above message had unknown log level"),
107             level);
108   }
109   return;
110 }
111
112 /* process messages that originate from vcdinfo. */
113 static void
114 vcd_log_handler (vcd_log_level_t level, const char message[])
115 {
116   vcdplayer_t *p_vcdplayer = (vcdplayer_t *)p_vcd_access->p_sys;
117   switch (level) {
118   case VCD_LOG_DEBUG:
119   case VCD_LOG_INFO:
120     if (p_vcdplayer->i_debug & INPUT_DBG_VCDINFO)
121       msg_Dbg( p_vcd_access, message);
122     break;
123   case VCD_LOG_WARN:
124     msg_Warn( p_vcd_access, message);
125     break;
126   case VCD_LOG_ERROR:
127   case VCD_LOG_ASSERT:
128     msg_Err( p_vcd_access, message);
129     break;
130   default:
131     msg_Warn( p_vcd_access, "%s\n%s %d", message,
132             _("The above message had unknown vcdimager log level"),
133             level);
134   }
135   return;
136 }
137
138 /*****************************************************************************
139   VCDRead: reads VCD_BLOCKS_ONCE from the VCD and returns that.
140   NULL is returned if something went wrong.
141  *****************************************************************************/
142 static block_t *
143 VCDReadBlock( access_t * p_access )
144 {
145     vcdplayer_t *p_vcdplayer= (vcdplayer_t *)p_access->p_sys;
146     const int    i_blocks   = p_vcdplayer->i_blocks_per_read;
147     block_t     *p_block;
148     int          i_read;
149     uint8_t     *p_buf;
150
151     i_read = 0;
152
153     dbg_print( (INPUT_DBG_LSN), "lsn: %lu", 
154                (long unsigned int) p_vcdplayer->i_lsn );
155
156     /* Allocate a block for the reading */
157     if( !( p_block = block_New( p_access, i_blocks * M2F2_SECTOR_SIZE ) ) )
158     {
159         msg_Err( p_access, "cannot get a new block of size: %i",
160                  i_blocks * M2F2_SECTOR_SIZE );
161         block_Release( p_block );
162         return NULL;
163     }
164
165     p_buf = (uint8_t *) p_block->p_buffer;
166     for ( i_read = 0 ; i_read < i_blocks ; i_read++ )
167     {
168       vcdplayer_read_status_t read_status = vcdplayer_read(p_access, p_buf);
169
170       p_access->info.i_pos += M2F2_SECTOR_SIZE;
171
172       switch ( read_status ) {
173       case READ_END:
174         /* End reached. Return NULL to indicated this. */
175         /* We also set the postion to the end so the higher level
176            (demux?) doesn't try to keep reading. If everything works out
177            right this shouldn't have to happen.
178          */
179 #if 0
180         if ( p_access->info.i_pos != p_access->info.i_size ) {
181           msg_Warn( p_access, 
182                     "At end but pos (%llu) is not size (%llu). Adjusting.",
183                     p_access->info.i_pos, p_access->info.i_size );
184           p_access->info.i_pos = p_access->info.i_size;
185         }
186 #endif
187
188 #if 1
189         block_Release( p_block );
190         return NULL;
191 #else
192         {
193           memset(p_buf, 0, M2F2_SECTOR_SIZE);
194           p_buf += 2;
195           *p_buf = 0x01;
196           printf("++++hacked\n");
197           return p_block;
198         }
199 #endif
200
201       case READ_ERROR:
202         /* Some sort of error. Should we increment lsn? to skip block?
203          */
204         block_Release( p_block );
205         return NULL;
206       case READ_STILL_FRAME: 
207         {
208           /* FIXME The below should be done in an event thread.
209              Until then...
210            */
211 #if 0
212           msleep( MILLISECONDS_PER_SEC * *p_buf );
213           p_vcd->in_still = VLC_FALSE;
214           dbg_print(INPUT_DBG_STILL, "still wait time done");
215 #else 
216           vcdIntfStillTime(p_vcdplayer->p_intf, *p_buf);
217 #endif
218
219 #if 1
220           block_Release( p_block );
221           return NULL;
222 #else
223           memset(p_buf, 0, M2F2_SECTOR_SIZE);
224           p_buf += 2;
225           *p_buf = 0x01;
226           return p_block;
227 #endif
228         }
229         
230       default:
231       case READ_BLOCK:
232         /* Read buffer */
233         ;
234       }
235
236       p_buf += M2F2_SECTOR_SIZE;
237       /* Update seekpoint */
238       if ( VCDINFO_ITEM_TYPE_ENTRY == p_vcdplayer->play_item.type )
239       {
240         unsigned int i_entry = p_vcdplayer->play_item.num+1;
241         lsn_t        i_lsn   = vcdinfo_get_entry_lba(p_vcdplayer->vcd, i_entry);
242         if ( p_vcdplayer->i_lsn >= i_lsn && i_lsn != VCDINFO_NULL_LBA )
243         {
244             const track_t i_track = p_vcdplayer->i_track;
245             p_vcdplayer->play_item.num = i_entry;
246             dbg_print( (INPUT_DBG_LSN|INPUT_DBG_PBC), "entry change" );
247             VCDSetOrigin( p_access,  i_lsn, i_track, 
248                           &(p_vcdplayer->play_item) );
249         }
250       }
251     }
252     
253     return p_block;
254 }
255
256
257 /****************************************************************************
258  * VCDSeek
259  ****************************************************************************/
260 int
261 VCDSeek( access_t * p_access, int64_t i_pos )
262 {
263     if (!p_access || !p_access->p_sys) return VLC_EGENERIC;
264     
265     {
266       vcdplayer_t         *p_vcdplayer = (vcdplayer_t *)p_vcd_access->p_sys;
267       const input_title_t *t = p_vcdplayer->p_title[p_access->info.i_title];
268       unsigned int         i_entry = VCDINFO_INVALID_ENTRY; 
269       int i_seekpoint;
270       
271       /* Next sector to read */
272       p_access->info.i_pos = i_pos;
273       p_vcdplayer->i_lsn = (i_pos / (int64_t)M2F2_SECTOR_SIZE) +
274         p_vcdplayer->track_lsn;
275
276       switch (p_vcdplayer->play_item.type) {
277       case VCDINFO_ITEM_TYPE_TRACK:
278       case VCDINFO_ITEM_TYPE_ENTRY:
279         break ;
280       default:
281         p_vcdplayer->b_valid_ep = VLC_FALSE;
282       }
283       
284       /* Find entry */
285       if( p_vcdplayer->b_valid_ep )
286       {
287           for( i_entry = 0 ; i_entry < p_vcdplayer->i_entries ; i_entry ++ )
288           {
289               if( p_vcdplayer->i_lsn < p_vcdplayer->p_entries[i_entry] )
290               {
291                   VCDUpdateVar( p_access, i_entry, VLC_VAR_SETVALUE,
292                                 "chapter", _("Entry"), "Setting entry" );
293                   break;
294               }
295           }
296           
297           { 
298               vcdinfo_itemid_t itemid;
299               itemid.num  = i_entry;
300               itemid.type = VCDINFO_ITEM_TYPE_ENTRY;
301               VCDSetOrigin(p_access, p_vcdplayer->i_lsn, p_vcdplayer->i_track, 
302                            &itemid);
303           }
304         }
305       
306       dbg_print( (INPUT_DBG_CALL|INPUT_DBG_EXT|INPUT_DBG_SEEK),
307                  "orig %lu, cur: %lu, offset: %lld, entry %d",
308                  (long unsigned int) p_vcdplayer->origin_lsn, 
309                  (long unsigned int) p_vcdplayer->i_lsn, i_pos,
310                  i_entry );
311       
312       /* Find seekpoint */
313       for( i_seekpoint = 0; i_seekpoint < t->i_seekpoint; i_seekpoint++ )
314         {
315           if( i_seekpoint + 1 >= t->i_seekpoint ) break;
316           if( i_pos < t->seekpoint[i_seekpoint + 1]->i_byte_offset ) break;
317         }
318       
319       /* Update current seekpoint */
320       if( i_seekpoint != p_access->info.i_seekpoint )
321         {
322           dbg_print( (INPUT_DBG_SEEK), "seekpoint change %lu", 
323                      (long unsigned int) i_seekpoint );
324           p_access->info.i_update |= INPUT_UPDATE_SEEKPOINT;
325           p_access->info.i_seekpoint = i_seekpoint;
326         }
327
328     }
329     return VLC_SUCCESS;
330     
331 }
332
333 /*****************************************************************************
334   VCDEntryPoints: Reads the information about the entry points on the disc
335   and initializes area information with that.
336   Before calling this track information should have been read in.
337  *****************************************************************************/
338 static vlc_bool_t
339 VCDEntryPoints( access_t * p_access )
340 {
341   if (!p_access || !p_access->p_sys) return VLC_FALSE;
342   
343   {
344     vcdplayer_t       *p_vcdplayer = (vcdplayer_t *) p_access->p_sys;
345     const unsigned int i_entries   = 
346       vcdinfo_get_num_entries(p_vcdplayer->vcd);
347     const track_t      i_last_track 
348       = cdio_get_num_tracks(vcdinfo_get_cd_image(p_vcdplayer->vcd))
349       + cdio_get_first_track_num(vcdinfo_get_cd_image(p_vcdplayer->vcd));
350     unsigned int i;
351    
352     if (0 == i_entries) {
353       LOG_ERR ("no entires found -- something is wrong" );
354       return VLC_FALSE;
355     }
356     
357     p_vcdplayer->p_entries  = malloc( sizeof( lsn_t ) * i_entries );
358     
359     if( p_vcdplayer->p_entries == NULL )
360       {
361         LOG_ERR ("not enough memory for entry points treatment" );
362         return VLC_FALSE;
363       }
364     
365     p_vcdplayer->i_entries = i_entries;
366     
367     for( i = 0 ; i < i_entries ; i++ )
368     {
369         const track_t i_track = vcdinfo_get_track(p_vcdplayer->vcd, i);
370         if( i_track <= i_last_track ) {
371           seekpoint_t *s = vlc_seekpoint_New();
372           char psz_entry[100];
373           
374           snprintf(psz_entry, sizeof(psz_entry), "%s%02d", _("Entry "), i );
375
376           p_vcdplayer->p_entries[i] = vcdinfo_get_entry_lba(p_vcdplayer->vcd, i);
377           
378           s->psz_name      = strdup(psz_entry);
379           s->i_byte_offset = 
380             (p_vcdplayer->p_entries[i] - vcdinfo_get_track_lba(p_vcdplayer->vcd, i_track))
381             * M2F2_SECTOR_SIZE;
382           
383           dbg_print( INPUT_DBG_MRL, 
384                      "%s, lsn %d,  byte_offset %ld",
385                      s->psz_name, p_vcdplayer->p_entries[i], 
386                      (unsigned long int) s->i_byte_offset);
387           TAB_APPEND( p_vcdplayer->p_title[i_track-1]->i_seekpoint,
388                       p_vcdplayer->p_title[i_track-1]->seekpoint, s );
389
390         } else 
391           msg_Warn( p_access, "wrong track number found in entry points" );
392     }
393     p_vcdplayer->b_valid_ep = VLC_TRUE;
394     return VLC_TRUE;
395   }
396 }
397
398 /*****************************************************************************
399  * VCDSegments: Reads the information about the segments the disc.
400  *****************************************************************************/
401 static vlc_bool_t
402 VCDSegments( access_t * p_access )
403 {
404     vcdplayer_t   *p_vcdplayer = (vcdplayer_t *) p_access->p_sys;
405     unsigned int  i;
406     input_title_t *t;
407
408     p_vcdplayer->i_segments = vcdinfo_get_num_segments(p_vcdplayer->vcd);
409
410     dbg_print( (INPUT_DBG_CALL|INPUT_DBG_MRL),
411                "Segments: %d", p_vcdplayer->i_segments);
412
413     if ( 0 == p_vcdplayer->i_segments ) return VLC_FALSE;
414
415     t = p_vcdplayer->p_title[p_vcdplayer->i_titles] = vlc_input_title_New();
416     p_vcdplayer->i_titles++;
417
418     t->i_size    = 0; /* Not sure Segments have a size associated */
419     t->psz_name  = strdup(_("Segments"));
420     
421     /* We have one additional segment allocated so we can get the size
422        by subtracting seg[i+1] - seg[i].
423      */
424     p_vcdplayer->p_segments = 
425       malloc( sizeof( lsn_t ) * (p_vcdplayer->i_segments+1) );
426     if( p_vcdplayer->p_segments == NULL )
427     {
428         LOG_ERR ("not enough memory for segment treatment" );
429         return VLC_FALSE;
430     }
431
432     for( i = 0 ; i < p_vcdplayer->i_segments ; i++ )
433     {
434         char psz_segment[100];
435         seekpoint_t *s = vlc_seekpoint_New();
436         p_vcdplayer->p_segments[i] = vcdinfo_get_seg_lsn(p_vcdplayer->vcd, i);
437         
438         snprintf( psz_segment, sizeof(psz_segment), "%s%02d", _("Segment "), 
439                   i );
440         
441         s->i_byte_offset = 0; /* Not sure what this would mean here */
442         s->psz_name  = strdup(psz_segment);
443         TAB_APPEND( t->i_seekpoint, t->seekpoint, s );
444     }
445
446     p_vcdplayer->p_segments[p_vcdplayer->i_segments] = 
447       p_vcdplayer->p_segments[p_vcdplayer->i_segments-1]+
448       vcdinfo_get_seg_sector_count(p_vcdplayer->vcd, 
449                                    p_vcdplayer->i_segments-1);
450
451     return VLC_TRUE;
452 }
453
454 /*****************************************************************************
455  Build title table which will be returned via ACCESS_GET_TITLE_INFO.
456
457  We start area addressing for tracks at 1 since the default area 0
458  is reserved for segments. 
459  *****************************************************************************/
460 static int
461 VCDTitles( access_t * p_access )
462 {
463     /* We'll assume a VCD has its first MPEG track
464        cdio_get_first_track_num()+1 could be used if one wanted to be
465        very careful about this. Note: cdio_get_first_track() will give the
466        ISO-9660 track before the MPEG tracks.
467      */
468   
469     if (!p_access || !p_access->p_sys) return VLC_EGENERIC;
470
471     {
472         vcdplayer_t *p_vcdplayer = (vcdplayer_t *) p_access->p_sys;
473         track_t      i;
474
475         p_vcdplayer->i_titles = 0;
476         for( i = 1 ; i <= p_vcdplayer->i_tracks ; i++ )
477         {
478             input_title_t *t = p_vcdplayer->p_title[i-1] = 
479               vlc_input_title_New();
480             char psz_track[100];
481             uint32_t i_secsize =
482               vcdinfo_get_track_sect_count( p_vcdplayer->vcd, i );
483             
484             snprintf( psz_track, sizeof(psz_track), "%s%02d", _("Track "), 
485                       i );
486             
487             t->i_size    = (i_secsize) * (int64_t) M2F2_SECTOR_SIZE;
488             t->psz_name  = strdup(psz_track);
489             
490             dbg_print( INPUT_DBG_MRL, "track[%d] i_size: %lld",
491                        i, t->i_size );
492
493             p_vcdplayer->i_titles++;
494         }
495       
496       return VLC_SUCCESS;
497     }
498 }
499
500 /*****************************************************************************
501   VCDLIDs: Reads the LIST IDs from the LOT.
502  *****************************************************************************/
503 static vlc_bool_t
504 VCDLIDs( access_t * p_access )
505 {
506     vcdplayer_t   *p_vcdplayer = (vcdplayer_t *) p_access->p_sys;
507     input_title_t *t;
508     unsigned int   i_lid, i_title;
509
510     p_vcdplayer->i_lids = vcdinfo_get_num_LIDs(p_vcdplayer->vcd);
511     p_vcdplayer->i_lid  = VCDINFO_INVALID_ENTRY;
512
513     dbg_print( (INPUT_DBG_CALL|INPUT_DBG_MRL),
514                "LIDs: %d", p_vcdplayer->i_lids);
515
516     if ( 0 == p_vcdplayer->i_lids ) return VLC_FALSE;
517     
518     if (vcdinfo_read_psd (p_vcdplayer->vcd)) {
519
520       vcdinfo_visit_lot (p_vcdplayer->vcd, VLC_FALSE);
521
522 #if FIXED
523     /*
524        We need to change libvcdinfo to be more robust when there are
525        problems reading the extended PSD. Given that area-highlighting and
526        selection features in the extended PSD haven't been implemented,
527        it's best then to not try to read this at all.
528      */
529       if (vcdinfo_get_psd_x_size(p_vcdplayer->vcd))
530         vcdinfo_visit_lot (p_vcdplayer->vcd, VLC_TRUE);
531 #endif
532     }
533
534     /* Set up LIDs Navigation Menu */
535     t = vlc_input_title_New();
536     t->b_menu = VLC_TRUE;
537     t->psz_name = strdup( "LIDs" );
538
539     i_title = p_vcdplayer->i_tracks;
540     for( i_lid =  1 ; i_lid <=  p_vcdplayer->i_lids ; i_lid++ )
541     {
542         char psz_lid[100];
543         seekpoint_t *s = vlc_seekpoint_New();
544             
545         snprintf( psz_lid, sizeof(psz_lid), "%s%02d", _("LID "), 
546                   i_lid );
547         
548         s->i_byte_offset = 0; /*  A lid doesn't have an offset
549                                   size associated with it */
550         s->psz_name  = strdup(psz_lid);
551         TAB_APPEND( t->i_seekpoint, t->seekpoint, s );
552         
553     }
554
555 #if DYNAMICALLY_ALLOCATED
556     TAB_APPEND( p_vcdplayer->i_titles, p_vcdplayer->p_title, t );
557 #else 
558     p_vcdplayer->p_title[p_vcdplayer->i_titles] = t;
559     p_vcdplayer->i_titles++;
560 #endif
561
562     return VLC_TRUE;
563 }
564
565 /*****************************************************************************
566  * VCDParse: parse command line
567  *****************************************************************************/
568 static char *
569 VCDParse( access_t * p_access, /*out*/ vcdinfo_itemid_t * p_itemid,
570           /*out*/ vlc_bool_t *play_single_item )
571 {
572     vcdplayer_t *p_vcdplayer = (vcdplayer_t *)p_access->p_sys;
573     char        *psz_parser;
574     char        *psz_source;
575     char        *psz_next;
576
577     if( config_GetInt( p_access, MODULE_STRING "-PBC" ) ) {
578       p_itemid->type = VCDINFO_ITEM_TYPE_LID;
579       p_itemid->num = 1;
580       *play_single_item = VLC_FALSE;
581     }
582     else 
583     {
584       p_itemid->type = VCDINFO_ITEM_TYPE_ENTRY;
585       p_itemid->num = 0;
586     }
587
588 #ifdef WIN32
589     /* On Win32 we want the VCD access plugin to be explicitly requested,
590      * we end up with lots of problems otherwise */
591     if( !p_access->psz_access || !*p_access->psz_access ) return NULL;
592 #endif
593
594     if( !p_access->psz_path )
595     {
596         return NULL;
597     }
598
599     psz_parser = psz_source = strdup( p_access->psz_path );
600
601     /* Parse input string :
602      * [device][@[type][title]] */
603     while( *psz_parser && *psz_parser != '@' )
604     {
605         psz_parser++;
606     }
607
608     if( *psz_parser == '@' )
609     {
610       /* Found the divide between the source name and the
611          type+entry number. */
612       unsigned int num;
613
614       *psz_parser = '\0';
615       ++psz_parser;
616       if( *psz_parser )
617         {
618           switch(*psz_parser) {
619           case 'E':
620             p_itemid->type = VCDINFO_ITEM_TYPE_ENTRY;
621             ++psz_parser;
622             *play_single_item = VLC_TRUE;
623             break;
624           case 'P':
625             p_itemid->type = VCDINFO_ITEM_TYPE_LID;
626             ++psz_parser;
627             *play_single_item = VLC_FALSE;
628             break;
629           case 'S':
630             p_itemid->type = VCDINFO_ITEM_TYPE_SEGMENT;
631             ++psz_parser;
632             *play_single_item = VLC_TRUE;
633             break;
634           case 'T':
635             p_itemid->type = VCDINFO_ITEM_TYPE_TRACK;
636             ++psz_parser;
637             *play_single_item = VLC_TRUE;
638             break;
639           default: ;
640           }
641         }
642
643       num = strtol( psz_parser, &psz_next, 10 );
644       if ( *psz_parser != '\0' && *psz_next == '\0')
645         {
646           p_itemid->num = num;
647         }
648
649     } else {
650       *play_single_item = ( VCDINFO_ITEM_TYPE_LID == p_itemid->type );
651     }
652
653
654     if( !*psz_source ) {
655
656       /* No source specified, so figure it out. */
657       if( !p_access->psz_access ) return NULL;
658
659       psz_source = config_GetPsz( p_access, "vcd" );
660
661       if( !psz_source || 0==strlen(psz_source) ) {
662         /* Scan for a CD-ROM drive with a VCD in it. */
663         char **cd_drives = cdio_get_devices_with_cap( NULL,
664                             ( CDIO_FS_ANAL_SVCD | CDIO_FS_ANAL_CVD
665                               |CDIO_FS_ANAL_VIDEOCD | CDIO_FS_UNKNOWN ),
666                                                      VLC_TRUE );
667         if( NULL == cd_drives ) return NULL;
668         if( cd_drives[0] == NULL )
669         {
670           cdio_free_device_list( cd_drives );
671           return NULL;
672         }
673         psz_source = strdup( cd_drives[0] );
674         cdio_free_device_list( cd_drives );
675       }
676     }
677
678     dbg_print( (INPUT_DBG_CALL|INPUT_DBG_MRL),
679                "source=%s entry=%d type=%d",
680                psz_source, p_itemid->num, p_itemid->type);
681
682     return psz_source;
683 }
684
685 /*
686    Set's start origin subsequent seeks/reads
687 */
688 void
689 VCDSetOrigin( access_t *p_access, lsn_t i_lsn, track_t i_track, 
690               const vcdinfo_itemid_t *p_itemid )
691 {
692   vcdplayer_t *p_vcdplayer= (vcdplayer_t *)p_access->p_sys;
693
694   dbg_print( (INPUT_DBG_CALL|INPUT_DBG_LSN),
695              "i_lsn: %lu, track: %d", (long unsigned int) i_lsn, 
696              i_track );
697
698   vcdplayer_set_origin(p_access, i_lsn, i_track, p_itemid);
699
700   p_access->info.i_pos     = ( i_lsn - p_vcdplayer->track_lsn ) 
701                              * M2F2_SECTOR_SIZE;
702   p_access->info.i_update |= INPUT_UPDATE_TITLE|INPUT_UPDATE_SIZE
703                           |  INPUT_UPDATE_SEEKPOINT;
704
705   
706   switch (p_vcdplayer->play_item.type) {
707   case VCDINFO_ITEM_TYPE_ENTRY: 
708       VCDUpdateVar( p_access, p_itemid->num, VLC_VAR_SETVALUE,
709                     "chapter", _("Entry"), "Setting entry/segment");
710       p_access->info.i_title     = i_track-1;
711       p_access->info.i_size      = p_vcdplayer->p_title[i_track-1]->i_size;
712       p_access->info.i_seekpoint = p_itemid->num;
713       break;
714
715   case VCDINFO_ITEM_TYPE_SEGMENT:
716       VCDUpdateVar( p_access, p_itemid->num, VLC_VAR_SETVALUE,
717                   "chapter", _("Segment"),  "Setting entry/segment");
718       /* The last title entry is the for segments (when segments exist
719          and they must here. The segment seekpoints are stored after
720          the entry seekpoints and (zeroed) lid seekpoints. 
721       */
722       p_access->info.i_title     = p_vcdplayer->i_titles - 1;
723       p_access->info.i_size      = 150 * M2F2_SECTOR_SIZE;
724       p_access->info.i_seekpoint = p_vcdplayer->i_entries 
725         + p_vcdplayer->i_lids + p_itemid->num;
726       break;
727       
728   case VCDINFO_ITEM_TYPE_TRACK: 
729       p_access->info.i_title     = i_track-1;
730       p_access->info.i_size      = p_vcdplayer->p_title[i_track-1]->i_size;
731       p_access->info.i_seekpoint = vcdinfo_track_get_entry(p_vcdplayer->vcd, 
732                                                            i_track);
733       break;
734   default:
735       msg_Warn( p_access, "can't set origin for play type %d", 
736                 p_vcdplayer->play_item.type );
737   }
738   
739
740   VCDUpdateTitle( p_access );
741   
742 }
743
744 /*****************************************************************************
745  * vcd_Open: Opens a VCD device or file initializes, a list of 
746    tracks, segements and entry lsns and sizes and returns an opaque handle.
747  *****************************************************************************/
748 static vcdinfo_obj_t *
749 vcd_Open( vlc_object_t *p_this, const char *psz_dev )
750 {
751     access_t    *p_access = (access_t *)p_this;
752     vcdplayer_t *p_vcdplayer    = (vcdplayer_t *) p_access->p_sys;
753     vcdinfo_obj_t *p_vcdobj;
754     char  *actual_dev;
755     unsigned int i;
756
757     dbg_print(INPUT_DBG_CALL, "called with %s", psz_dev);
758
759     if( !psz_dev ) return NULL;
760
761     actual_dev=strdup(psz_dev);
762     if ( vcdinfo_open(&p_vcdobj, &actual_dev, DRIVER_UNKNOWN, NULL) !=
763          VCDINFO_OPEN_VCD) {
764       free(actual_dev);
765       return NULL;
766     }
767     free(actual_dev);
768
769     /* 
770        Save summary info on tracks, segments and entries... 
771     */
772     
773     if ( 0 < (p_vcdplayer->i_tracks = vcdinfo_get_num_tracks(p_vcdobj)) ) {
774       p_vcdplayer->track = (vcdplayer_play_item_info_t *) 
775         calloc(p_vcdplayer->i_tracks, sizeof(vcdplayer_play_item_info_t));
776       
777       for (i=0; i<p_vcdplayer->i_tracks; i++) { 
778         unsigned int track_num=i+1;
779         p_vcdplayer->track[i].size  = 
780           vcdinfo_get_track_sect_count(p_vcdobj, track_num);
781         p_vcdplayer->track[i].start_LSN = 
782           vcdinfo_get_track_lba(p_vcdobj, track_num);
783       }
784     } else 
785       p_vcdplayer->track = NULL;
786     
787     if ( 0 < (p_vcdplayer->i_entries = vcdinfo_get_num_entries(p_vcdobj)) ) {
788       p_vcdplayer->entry = (vcdplayer_play_item_info_t *) 
789         calloc(p_vcdplayer->i_entries, sizeof(vcdplayer_play_item_info_t));
790       
791       for (i=0; i<p_vcdplayer->i_entries; i++) { 
792         p_vcdplayer->entry[i].size = 
793           vcdinfo_get_entry_sect_count(p_vcdobj, i);
794         p_vcdplayer->entry[i].start_LSN = vcdinfo_get_entry_lba(p_vcdobj, i);
795       }
796     } else 
797       p_vcdplayer->entry = NULL;
798     
799     if ( 0 < (p_vcdplayer->i_segments = vcdinfo_get_num_segments(p_vcdobj)) ) {
800       p_vcdplayer->segment = (vcdplayer_play_item_info_t *) 
801         calloc(p_vcdplayer->i_segments,  sizeof(vcdplayer_play_item_info_t));
802       
803       for (i=0; i<p_vcdplayer->i_segments; i++) { 
804         p_vcdplayer->segment[i].size =
805           vcdinfo_get_seg_sector_count(p_vcdobj, i);
806         p_vcdplayer->segment[i].start_LSN = vcdinfo_get_seg_lsn(p_vcdobj, i);
807       }
808     } else 
809       p_vcdplayer->segment = NULL;
810     
811     
812     return p_vcdobj;
813 }
814
815 /****************************************************************************
816  Update the "varname" variable to i_num without triggering a callback.
817 ****************************************************************************/
818 static void
819 VCDUpdateVar( access_t *p_access, int i_num, int i_action,
820               const char *p_varname, char *p_label, 
821               const char *p_debug_label)
822 {
823   vlc_value_t val;
824   val.i_int = i_num;
825   if (p_access) {
826     const vcdplayer_t *p_vcdplayer = (vcdplayer_t *)p_vcd_access->p_sys;
827     dbg_print( INPUT_DBG_PBC, "%s %d", p_debug_label, i_num );
828   }
829   if (p_label) {
830     vlc_value_t text;
831     text.psz_string = p_label;
832     var_Change( p_access, p_varname, VLC_VAR_SETTEXT, &text, NULL );
833   }
834   var_Change( p_access, p_varname, i_action, &val, NULL );
835 }
836
837
838 /*****************************************************************************
839  * Public routines.
840  *****************************************************************************/
841
842 /*****************************************************************************
843   VCDOpen: open VCD.
844   read in meta-information about VCD: the number of tracks, segments,
845   entries, size and starting information. Then set up state variables so
846   that we read/seek starting at the location specified.
847
848   On success we return VLC_SUCCESS, on memory exhausted VLC_ENOMEM,
849   and VLC_EGENERIC for some other error.
850  *****************************************************************************/
851 int
852 VCDOpen ( vlc_object_t *p_this )
853 {
854     access_t         *p_access = (access_t *)p_this;
855     vcdplayer_t      *p_vcdplayer;
856     char             *psz_source;
857     vcdinfo_itemid_t  itemid;
858     vlc_bool_t        play_single_item = VLC_FALSE;
859
860     p_access->pf_read          = NULL;
861     p_access->pf_block         = VCDReadBlock; 
862     p_access->pf_control       = VCDControl;
863     p_access->pf_seek          = VCDSeek;
864
865     p_access->info.i_update    = 0;
866     p_access->info.i_size      = 0;
867     p_access->info.i_pos       = 0;
868     p_access->info.b_eof       = VLC_FALSE;
869     p_access->info.i_title     = 0;
870     p_access->info.i_seekpoint = 0;
871
872     p_vcdplayer = malloc( sizeof(vcdplayer_t) );
873
874     if( p_vcdplayer == NULL )
875     {
876         LOG_ERR ("out of memory" );
877         return VLC_ENOMEM;
878     }
879
880     p_access->p_sys = (access_sys_t *) p_vcdplayer;
881
882     /* Set where to log errors messages from libcdio. */
883     p_vcd_access = p_access;
884     cdio_log_set_handler ( cdio_log_handler );
885     vcd_log_set_handler ( vcd_log_handler );
886
887     psz_source = VCDParse( p_access, &itemid, &play_single_item );
888
889     if ( NULL == psz_source )
890     {
891       free( p_vcdplayer );
892       return( VLC_EGENERIC );
893     }
894
895     dbg_print( (INPUT_DBG_CALL|INPUT_DBG_EXT), "source: %s: mrl: %s",
896                psz_source, p_access->psz_path );
897
898     p_vcdplayer->psz_source        = strdup(psz_source);
899     p_vcdplayer->i_debug           = config_GetInt( p_this, 
900                                               MODULE_STRING "-debug" );
901     p_vcdplayer->i_blocks_per_read = config_GetInt( p_this, MODULE_STRING 
902                                               "-blocks-per-read" );
903     p_vcdplayer->in_still          = VLC_FALSE;
904     p_vcdplayer->play_item.type    = VCDINFO_ITEM_TYPE_NOTFOUND;
905     p_vcdplayer->p_input           = vlc_object_find( p_access, 
906                                                       VLC_OBJECT_INPUT, 
907                                                       FIND_PARENT );
908     p_vcdplayer->p_demux           = vlc_object_find( p_access, 
909                                                       VLC_OBJECT_DEMUX, 
910                                                       FIND_PARENT );
911     p_vcdplayer->p_meta            = vlc_meta_New();
912     p_vcdplayer->p_segments        = NULL;
913     p_vcdplayer->p_entries         = NULL;
914
915     /* set up input  */
916
917     if( !(p_vcdplayer->vcd = vcd_Open( p_this, psz_source )) )
918     {
919         msg_Warn( p_access, "could not open %s", psz_source );
920         goto err_exit;
921     }
922
923     p_vcdplayer->b_svd= (vlc_bool_t) vcdinfo_get_tracksSVD(p_vcdplayer->vcd);;
924     
925     /* Get track information. */
926     p_vcdplayer->i_tracks = vcdinfo_get_num_tracks(p_vcdplayer->vcd);
927
928     if( p_vcdplayer->i_tracks < 1 || CDIO_INVALID_TRACK == p_vcdplayer->i_tracks ) {
929         vcdinfo_close( p_vcdplayer->vcd );
930         LOG_ERR ("no movie tracks found" );
931         goto err_exit;
932     }
933     
934     /* Build Navigation Title table for the tracks. */
935     VCDTitles( p_access );
936
937     /* Add into the above entry points as "Chapters". */
938     if( ! VCDEntryPoints( p_access ) )
939     {
940         msg_Warn( p_access, "could not read entry points, will not use them" );
941         p_vcdplayer->b_valid_ep = VLC_FALSE;
942     }
943
944     /* Initialize LID info and add that as a menu item */
945     if( ! VCDLIDs( p_access ) )
946     {
947         msg_Warn( p_access, "could not read entry LIDs" );
948     }
949
950     /* Do we set PBC (via LID) on? */
951     p_vcdplayer->i_lid = 
952       ( VCDINFO_ITEM_TYPE_LID == itemid.type 
953         && p_vcdplayer->i_lids > itemid.num )
954       ? itemid.num
955       :  VCDINFO_INVALID_ENTRY;
956
957     /* Initialize segment information and add that a "Track". */
958     VCDSegments( p_access );
959
960     vcdplayer_play( p_access, itemid );
961
962     p_access->psz_demux = strdup( "ps" );
963
964 #if FIXED
965     if (play_single_item)
966       VCDFixupPlayList( p_access, p_vcd, psz_source, &itemid, 
967                         play_single_item );
968 #endif
969     
970     p_vcdplayer->p_intf = intf_Create( p_access, "vcdx" );
971     p_vcdplayer->p_intf->b_block = VLC_FALSE;
972     p_vcdplayer->p_access = p_access;
973
974 #ifdef FIXED
975     intf_RunThread( p_vcdplayer->p_intf );
976 #endif
977
978     free( psz_source );
979
980     return VLC_SUCCESS;
981  err_exit:
982     free( psz_source );
983     free( p_vcdplayer );
984     return VLC_EGENERIC;
985 }
986
987 /*****************************************************************************
988  * VCDClose: closes VCD releasing allocated memory.
989  *****************************************************************************/
990 void
991 VCDClose ( vlc_object_t *p_this )
992 {
993     access_t    *p_access = (access_t *)p_this;
994     vcdplayer_t *p_vcdplayer = (vcdplayer_t *)p_access->p_sys;
995
996     dbg_print( (INPUT_DBG_CALL|INPUT_DBG_EXT), "VCDClose" );
997
998     vcdinfo_close( p_vcdplayer->vcd );
999
1000     FREE_AND_NULL( p_vcdplayer->p_entries );
1001     FREE_AND_NULL( p_vcdplayer->p_segments );
1002     FREE_AND_NULL( p_vcdplayer->psz_source );
1003     FREE_AND_NULL( p_vcdplayer->track );
1004     FREE_AND_NULL( p_vcdplayer->segment );
1005     FREE_AND_NULL( p_vcdplayer->entry ); 
1006
1007     free( p_vcdplayer );
1008     p_access->p_sys = NULL;
1009     p_vcd_access    = NULL;
1010 }
1011
1012 /*****************************************************************************
1013  * Control: The front-end or vlc engine calls here to ether get
1014  * information such as meta information or plugin capabilities or to
1015  * issue miscellaneous "set" requests.
1016  *****************************************************************************/
1017 static int VCDControl( access_t *p_access, int i_query, va_list args )
1018 {
1019     vcdplayer_t *p_vcdplayer = (vcdplayer_t *)p_access->p_sys;
1020     int         *pi_int;
1021     int i;
1022
1023     dbg_print( (INPUT_DBG_CALL|INPUT_DBG_EXT|INPUT_DBG_EVENT),
1024                "query %d", i_query );
1025
1026     switch( i_query )
1027     {
1028         /* Pass back a copy of meta information that was gathered when we
1029            during the Open/Initialize call.
1030          */
1031         case ACCESS_GET_META:
1032           { 
1033             vlc_meta_t **pp_meta = (vlc_meta_t**)va_arg( args, vlc_meta_t** );
1034
1035             dbg_print( INPUT_DBG_EVENT, "get meta info" );
1036
1037             if ( p_vcdplayer->p_meta ) {
1038               *pp_meta = vlc_meta_Duplicate( p_vcdplayer->p_meta );
1039               dbg_print( INPUT_DBG_META, "%s", "Meta copied" );
1040             } else 
1041               msg_Warn( p_access, "tried to copy NULL meta info" );
1042             
1043             return VLC_SUCCESS;
1044           }
1045           return VLC_EGENERIC;
1046
1047         case ACCESS_CAN_SEEK:
1048         case ACCESS_CAN_FASTSEEK:
1049         case ACCESS_CAN_PAUSE:
1050         case ACCESS_CAN_CONTROL_PACE: 
1051           {
1052             vlc_bool_t *pb_bool = (vlc_bool_t*)va_arg( args, vlc_bool_t* );
1053
1054             dbg_print( INPUT_DBG_EVENT, 
1055                        "seek/fastseek/pause/can_control_pace" );
1056             *pb_bool = VLC_TRUE;
1057             return VLC_SUCCESS;
1058             break;
1059           }
1060
1061         /* */
1062         case ACCESS_GET_MTU:
1063             pi_int = (int*)va_arg( args, int * );
1064             *pi_int = (p_vcdplayer->i_blocks_per_read * M2F2_SECTOR_SIZE);
1065             dbg_print( INPUT_DBG_EVENT, "GET MTU: %d", *pi_int );
1066             break;
1067
1068         case ACCESS_GET_PTS_DELAY:
1069           { 
1070             int64_t *pi_64 = (int64_t*)va_arg( args, int64_t * );
1071             *pi_64 = var_GetInteger( p_access, MODULE_STRING "-caching" )
1072               * MILLISECONDS_PER_SEC;
1073             dbg_print( INPUT_DBG_EVENT, "GET PTS DELAY" );
1074             return VLC_SUCCESS;
1075             break;
1076           }
1077
1078         /* */
1079         case ACCESS_SET_PAUSE_STATE:
1080             dbg_print( INPUT_DBG_EVENT, "SET PAUSE STATE" );
1081             return VLC_SUCCESS;
1082             break;
1083
1084         case ACCESS_GET_TITLE_INFO:
1085           { 
1086             unsigned int psz_mrl_max = strlen(VCD_MRL_PREFIX) 
1087               + strlen(p_vcdplayer->psz_source) + sizeof("@E999")+3;
1088             input_title_t ***ppp_title
1089               = (input_title_t***)va_arg( args, input_title_t*** );
1090             char *psz_mrl = malloc( psz_mrl_max );
1091             unsigned int i;
1092
1093             pi_int    = (int*)va_arg( args, int* );
1094
1095             dbg_print( INPUT_DBG_EVENT, "GET TITLE: i_titles %d", 
1096                        p_vcdplayer->i_titles );
1097
1098             if( psz_mrl == NULL ) {
1099                msg_Warn( p_access, "out of memory" );
1100             } else {
1101                snprintf(psz_mrl, psz_mrl_max, "%s%s",
1102                         VCD_MRL_PREFIX, p_vcdplayer->psz_source);
1103                VCDMetaInfo( p_access, psz_mrl );
1104                free(psz_mrl);
1105             }
1106
1107             /* Duplicate title info */
1108             if( p_vcdplayer->i_titles == 0 )
1109             {
1110                 *pi_int = 0; ppp_title = NULL;
1111                 return VLC_SUCCESS;
1112             }
1113             *pi_int = p_vcdplayer->i_titles;
1114             *ppp_title = malloc( sizeof( input_title_t **) 
1115                                  * p_vcdplayer->i_titles );
1116             
1117             if (!*ppp_title) return VLC_ENOMEM;
1118
1119             for( i = 0; i < p_vcdplayer->i_titles; i++ )
1120             {
1121                 if ( p_vcdplayer->p_title[i] )
1122                   (*ppp_title)[i] = 
1123                     vlc_input_title_Duplicate( p_vcdplayer->p_title[i] );
1124             }
1125           }
1126           break;
1127
1128         case ACCESS_SET_TITLE:
1129             i = (int)va_arg( args, int );
1130
1131             dbg_print( INPUT_DBG_EVENT, "set title %d" , i);
1132             if( i != p_access->info.i_title )
1133             {
1134                 vcdinfo_itemid_t itemid;
1135                 track_t          i_track = i+1;
1136                 unsigned int     i_entry = 
1137                   vcdinfo_track_get_entry( p_vcdplayer->vcd, i_track);
1138
1139                 /* FIXME! For now we are assuming titles are only 
1140                  tracks and that track == title+1 */
1141                 itemid.num = i_track;
1142                 itemid.type = VCDINFO_ITEM_TYPE_TRACK;
1143                 
1144                 VCDSetOrigin(p_access, 
1145                              vcdinfo_get_entry_lba(p_vcdplayer->vcd, i_entry),
1146                              i_track, &itemid );
1147             }
1148             break;
1149
1150         case ACCESS_SET_SEEKPOINT:
1151         {
1152             input_title_t *t = p_vcdplayer->p_title[p_access->info.i_title];
1153             unsigned int i = (unsigned int)va_arg( args, unsigned int );
1154
1155             dbg_print( INPUT_DBG_EVENT, "set seekpoint %d", i );
1156             if( t->i_seekpoint > 0 )
1157             {
1158                 track_t i_track = p_access->info.i_title+1;
1159                 lsn_t lsn;
1160                 
1161                 /* FIXME! For now we are assuming titles are only 
1162                  tracks and that track == title+1 and we the play
1163                  item is entries (not tracks or lids).
1164                  We need to generalize all of this.
1165                 */
1166
1167                 if (i < p_vcdplayer->i_entries) 
1168                 {
1169                     p_vcdplayer->play_item.num  = i;
1170                     p_vcdplayer->play_item.type = VCDINFO_ITEM_TYPE_ENTRY;
1171                     lsn = vcdinfo_get_entry_lba(p_vcdplayer->vcd, i);
1172                 } else if ( i < p_vcdplayer->i_entries + p_vcdplayer->i_lids ) 
1173                 {
1174                     p_vcdplayer->play_item.num  = i 
1175                       = i - p_vcdplayer->i_entries;
1176                     p_vcdplayer->play_item.type = VCDINFO_ITEM_TYPE_LID;
1177                     lsn = 0;
1178                 } else 
1179                 {
1180                     p_vcdplayer->play_item.num  = i 
1181                       = i - p_vcdplayer->i_entries - p_vcdplayer->i_lids;
1182                     p_vcdplayer->play_item.type = VCDINFO_ITEM_TYPE_SEGMENT;
1183                     lsn = vcdinfo_get_seg_lsn(p_vcdplayer->vcd, i);
1184                 }
1185                 
1186                 VCDSetOrigin( p_access, 
1187                               vcdinfo_get_entry_lba(p_vcdplayer->vcd, i),
1188                               i_track, &(p_vcdplayer->play_item) );
1189             }
1190             return VLC_SUCCESS;
1191         }
1192
1193         case ACCESS_SET_PRIVATE_ID_STATE:
1194             dbg_print( INPUT_DBG_EVENT, "set private id" );
1195             return VLC_EGENERIC;
1196
1197         default:
1198           msg_Warn( p_access, "unimplemented query in control" );
1199             return VLC_EGENERIC;
1200
1201     }
1202     return VLC_SUCCESS;
1203 }