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