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