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