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