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