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