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