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