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