]> git.sesse.net Git - vlc/blob - modules/access/vcdx/access.c
A little cleanup on wait timing. No bug fixes yet, though.
[vlc] / modules / access / vcdx / access.c
1 /*****************************************************************************
2  * vcd.c : VCD input module for vlc
3  *         using libcdio, libvcd and libvcdinfo. vlc-specific things tend
4  *         to go here.
5  *****************************************************************************
6  * Copyright (C) 2000,2003 VideoLAN
7  * $Id: access.c,v 1.9 2003/12/05 05:01:17 rocky Exp $
8  *
9  * Authors: Rocky Bernstein <rocky@panix.com> 
10  *          Johan Bilien <jobi@via.ecp.fr>
11  *
12  * This program is free software; you can redistribute it and/or modify
13  * it under the terms of the GNU General Public License as published by
14  * the Free Software Foundation; either version 2 of the License, or
15  * (at your option) any later version.
16  *
17  * This program is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20  * GNU General Public License for more details.
21  *
22  * You should have received a copy of the GNU General Public License
23  * along with this program; if not, write to the Free Software
24  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
25  *****************************************************************************/
26
27 /*****************************************************************************
28  * Preamble
29  *****************************************************************************/
30
31 #include <vlc/vlc.h>
32 #include <vlc/input.h>
33 #include <vlc/intf.h>
34
35 #include "../../demux/mpeg/system.h"
36 #include "vcd.h"
37 #include "vcdplayer.h"
38 #include "intf.h"
39
40 #include <cdio/cdio.h>
41 #include <cdio/cd_types.h>
42 #include <cdio/logging.h>
43 #include <cdio/util.h>
44 #include <libvcd/info.h>
45 #include <libvcd/logging.h>
46
47 #include "cdrom.h"
48
49 /* how many blocks VCDRead will read in each loop */
50 #define VCD_BLOCKS_ONCE 20
51 #define VCD_DATA_ONCE   (VCD_BLOCKS_ONCE * M2F2_SECTOR_SIZE)
52
53 #define VCD_MRL_PREFIX "vcdx://"
54
55 /*****************************************************************************
56  * Local prototypes
57  *****************************************************************************/
58
59 /* First those which are accessed from outside (via pointers). */
60 static int  VCDRead         ( input_thread_t *, byte_t *, size_t );
61 static int  VCDSetProgram   ( input_thread_t *, pgrm_descriptor_t * );
62
63 /* Now those which are strictly internal */
64 static void VCDSetOrigin    ( input_thread_t *, lsn_t origin_lsn, 
65                               lsn_t cur_lsn, lsn_t end_lsn, 
66                               int cur_entry, track_t track );
67 static int  VCDEntryPoints  ( input_thread_t * );
68 static int  VCDLIDs         ( input_thread_t * );
69 static int  VCDSegments     ( input_thread_t * );
70 static void VCDTracks       ( input_thread_t * );
71 static int  VCDReadSector   ( vlc_object_t *p_this, 
72                               const vcdinfo_obj_t *p_vcd, lsn_t cur_lsn, 
73                               byte_t * p_buffer );
74 static char *VCDParse       ( input_thread_t *, 
75                               /*out*/ vcdinfo_itemid_t * p_itemid );
76
77 static void VCDUpdateVar( input_thread_t *p_input, int i_entry, int i_action,
78                           const char *varname, const char *label );
79
80 static vcdinfo_obj_t *vcd_Open   ( vlc_object_t *p_this, const char *psz_dev );
81
82 /****************************************************************************
83  * Private functions
84  ****************************************************************************/
85
86 /* FIXME: This variable is a hack. Would be nice to eliminate the 
87    global-ness. */
88
89 static input_thread_t *p_vcd_input = NULL;
90
91 /* process messages that originate from libcdio. */
92 static void
93 cdio_log_handler (cdio_log_level_t level, const char message[])
94 {
95   thread_vcd_data_t *p_vcd = (thread_vcd_data_t *)p_vcd_input->p_access_data;
96   switch (level) {
97   case CDIO_LOG_DEBUG:
98   case CDIO_LOG_INFO:
99     if (p_vcd->i_debug & INPUT_DBG_CDIO) 
100       msg_Dbg( p_vcd_input, message);
101     break;
102   case CDIO_LOG_WARN:
103     msg_Warn( p_vcd_input, message);
104     break;
105   case CDIO_LOG_ERROR:
106   case CDIO_LOG_ASSERT:
107     msg_Err( p_vcd_input, message);
108     break;
109   default:
110     msg_Warn( p_vcd_input, message,
111             _("The above message had unknown log level"), 
112             level);
113   }
114   return;
115 }
116
117 /* process messages that originate from vcdinfo. */
118 static void
119 vcd_log_handler (vcd_log_level_t level, const char message[])
120 {
121   thread_vcd_data_t *p_vcd = (thread_vcd_data_t *)p_vcd_input->p_access_data;
122   switch (level) {
123   case VCD_LOG_DEBUG:
124   case VCD_LOG_INFO:
125     if (p_vcd->i_debug & INPUT_DBG_VCDINFO)
126       msg_Dbg( p_vcd_input, message);
127     break;
128   case VCD_LOG_WARN:
129     msg_Warn( p_vcd_input, message);
130     break;
131   case VCD_LOG_ERROR:
132   case VCD_LOG_ASSERT:
133     msg_Err( p_vcd_input, message);
134     break;
135   default:
136     msg_Warn( p_vcd_input, "%s\n%s %d", message,
137             _("The above message had unknown vcdimager log level"), 
138             level);
139   }
140   return;
141 }
142
143 /*****************************************************************************
144  * VCDRead: reads i_len bytes from the VCD into p_buffer.
145  *****************************************************************************
146  * Returns -1 in case of error, 0 in case of EOF, otherwise the number of
147  * bytes.
148  *****************************************************************************/
149 static int 
150 VCDRead( input_thread_t * p_input, byte_t * p_buffer, size_t i_len )
151 {
152     thread_vcd_data_t *     p_vcd= (thread_vcd_data_t *)p_input->p_access_data;
153     int                     i_blocks;
154     int                     i_index;
155     int                     i_read;
156     byte_t                  p_last_sector[ M2F2_SECTOR_SIZE ];
157
158     i_read = 0;
159
160     dbg_print( (INPUT_DBG_CALL), "lsn: %u", p_vcd->cur_lsn );
161
162     /* Compute the number of blocks we have to read */
163
164     i_blocks = i_len / M2F2_SECTOR_SIZE;
165
166     for ( i_index = 0 ; i_index < i_blocks ; i_index++ )
167     {
168
169       if ( p_vcd->cur_lsn == p_vcd->end_lsn ) {
170         vcdplayer_read_status_t read_status;
171
172         /* We've run off of the end of this entry. Do we continue or stop? */
173         dbg_print( (INPUT_DBG_LSN|INPUT_DBG_PBC), 
174                    "end reached, cur: %u", p_vcd->cur_lsn );
175
176         read_status = vcdplayer_pbc_is_on( p_vcd ) 
177           ? vcdplayer_pbc_nav( p_input ) 
178           : vcdplayer_non_pbc_nav( p_input );
179
180         switch (read_status) {
181         case READ_END:
182           /* End reached. Return NULL to indicated this. */
183         case READ_ERROR:
184           /* Some sort of error. */
185           return i_read;
186
187         case READ_STILL_FRAME: 
188           {
189             /* Reached the end of a still frame. */
190
191             byte_t * p_buf = p_buffer;
192             pgrm_descriptor_t * p_pgrm = p_input->stream.p_selected_program;;
193
194             p_buf += (i_index*M2F2_SECTOR_SIZE);
195             memset(p_buf, 0, M2F2_SECTOR_SIZE);
196             p_buf += 2;
197             *p_buf = 0x01;
198             dbg_print(INPUT_DBG_STILL, "Handled still event");
199
200             p_vcd->p_intf->p_sys->b_still = 1;
201             input_SetStatus( p_input, INPUT_STATUS_PAUSE );
202
203             vlc_mutex_lock( &p_input->stream.stream_lock );
204
205             p_pgrm = p_input->stream.p_selected_program;
206             p_pgrm->i_synchro_state = SYNCHRO_REINIT;
207
208             vlc_mutex_unlock( &p_input->stream.stream_lock );
209
210             dbg_print(INPUT_DBG_STILL, "Clock manage");
211             input_ClockManageControl( p_input, p_pgrm, 0 );
212             dbg_print(INPUT_DBG_STILL, "Clock manage done");
213
214             return i_read + M2F2_SECTOR_SIZE;
215           }
216         default:
217         case READ_BLOCK:
218           break;
219         }
220       }
221
222       if ( VCDReadSector( VLC_OBJECT(p_input), p_vcd->vcd,
223                           p_vcd->cur_lsn, 
224                           p_buffer + (i_index*M2F2_SECTOR_SIZE) ) < 0 )
225         {
226           LOG_ERR ("could not read sector %d", p_vcd->cur_lsn );
227           return -1;
228         }
229       
230       p_vcd->cur_lsn ++;
231       
232       /* Update chapter */
233       if( p_vcd->b_valid_ep &&
234           /* FIXME kludge so that read does not update chapter
235            * when a manual chapter change was requested and not
236            * yet accomplished */
237           !p_input->stream.p_new_area )
238         {
239           unsigned int i_entry = p_input->stream.p_selected_area->i_part;
240           
241           vlc_mutex_lock( &p_input->stream.stream_lock );
242           
243           if( i_entry < p_vcd->num_entries &&
244               p_vcd->cur_lsn >= p_vcd->p_entries[i_entry+1] )
245             {
246               dbg_print( INPUT_DBG_PBC, 
247                          "new entry, i_entry %d, sector %d, es %d",
248                          i_entry, p_vcd->cur_lsn, 
249                          p_vcd->p_entries[i_entry] );
250               p_vcd->play_item.num = 
251                 ++ p_input->stream.p_selected_area->i_part;
252               p_vcd->play_item.type = VCDINFO_ITEM_TYPE_ENTRY;
253               VCDUpdateVar( p_input, p_vcd->play_item.num, VLC_VAR_SETVALUE,
254                             "chapter", "Setting entry" );
255             }
256           vlc_mutex_unlock( &p_input->stream.stream_lock );
257         }
258
259         i_read += M2F2_SECTOR_SIZE;
260     }
261
262     if ( i_len % M2F2_SECTOR_SIZE ) /* this should not happen */
263     {
264         if ( VCDReadSector( VLC_OBJECT(p_input), p_vcd->vcd,
265                             p_vcd->cur_lsn, p_last_sector ) < 0 )
266         {
267             LOG_ERR ("could not read sector %d", p_vcd->cur_lsn );
268             return -1;
269         }
270
271         p_input->p_vlc->pf_memcpy( p_buffer + i_blocks * M2F2_SECTOR_SIZE,
272                                    p_last_sector, i_len % M2F2_SECTOR_SIZE );
273         i_read += i_len % M2F2_SECTOR_SIZE;
274     }
275
276     return i_read;
277 }
278
279
280 /*****************************************************************************
281  * VCDSetProgram: Does nothing since a VCD is mono_program
282  *****************************************************************************/
283 static int 
284 VCDSetProgram( input_thread_t * p_input, pgrm_descriptor_t * p_program)
285 {
286     thread_vcd_data_t *     p_vcd= (thread_vcd_data_t *)p_input->p_access_data;
287     dbg_print( (INPUT_DBG_CALL|INPUT_DBG_EXT), "VCDSetProgram" );
288     return 0;
289 }
290
291
292 /*****************************************************************************
293  * VCDSetArea: initialize internal data structures and input stream data 
294    so set subsequent reading and seeking to reflect that we are
295    at track x, entry or segment y.
296    This is called for each user navigation request, e.g. the GUI 
297    Chapter/Title selections or in initial MRL parsing. 
298  ****************************************************************************/
299 int 
300 VCDSetArea( input_thread_t * p_input, input_area_t * p_area )
301 {
302     thread_vcd_data_t *p_vcd = (thread_vcd_data_t*)p_input->p_access_data;
303     unsigned int i_entry = p_area->i_part;
304     track_t i_track      = p_area->i_id;
305     int old_seekable     = p_input->stream.b_seekable;
306     unsigned int i_nb    = p_area->i_plugin_data + p_area->i_part_nb;
307
308     dbg_print( (INPUT_DBG_CALL|INPUT_DBG_EXT),
309                "track: %d, entry %d, seekable %d, area %lx, select area %lx ",
310                i_track, i_entry, old_seekable, 
311                (long unsigned int) p_area, 
312                (long unsigned int) p_input->stream.p_selected_area );
313
314     /* we can't use the interface slider until initilization is complete */
315     p_input->stream.b_seekable = 0;
316
317     if( p_area != p_input->stream.p_selected_area )
318     {
319         unsigned int i;
320
321         /* If is the result of a track change, make the entry valid. */
322         if (i_entry < p_area->i_plugin_data || i_entry >= i_nb) 
323           i_entry = p_area->i_plugin_data;
324
325         /* Change the default area */
326         p_input->stream.p_selected_area = p_area;
327
328         /* Update the navigation variables without triggering a callback */
329         VCDUpdateVar( p_input, i_track, VLC_VAR_SETVALUE, "title", 
330                       "Setting track");
331
332         var_Change( p_input, "chapter", VLC_VAR_CLEARCHOICES, NULL, NULL );
333         for( i = p_area->i_plugin_data; i < i_nb; i++ )
334         {
335           VCDUpdateVar( p_input, i , VLC_VAR_ADDCHOICE, 
336                         "chapter",  "Adding entry choice");
337         }
338     }
339
340     if (i_track == 0) 
341       VCDSetOrigin( p_input, p_vcd->p_segments[i_entry], 
342                     p_vcd->p_segments[i_entry], p_vcd->p_segments[i_entry+1],
343                     i_entry, 0 );
344     else
345       VCDSetOrigin( p_input, p_vcd->p_sectors[i_track], 
346                     vcdinfo_get_entry_lsn(p_vcd->vcd, i_entry), 
347                     p_vcd->p_sectors[i_track+1],
348                     i_entry, i_track );
349
350     p_input->stream.b_seekable = old_seekable;
351     /* warn interface that something has changed */
352     p_input->stream.b_changed = 1;
353
354     return VLC_SUCCESS;
355 }
356
357
358 /****************************************************************************
359  * VCDSeek
360  ****************************************************************************/
361 void 
362 VCDSeek( input_thread_t * p_input, off_t i_off )
363 {
364     thread_vcd_data_t * p_vcd;
365     unsigned int i_entry=0; /* invalid entry */
366
367     p_vcd = (thread_vcd_data_t *) p_input->p_access_data;
368
369     p_vcd->cur_lsn = p_vcd->origin_lsn + (i_off / (off_t)M2F2_SECTOR_SIZE);
370
371     vlc_mutex_lock( &p_input->stream.stream_lock );
372 #define p_area p_input->stream.p_selected_area
373     /* Find entry */
374     if( p_vcd->b_valid_ep )
375     {
376         for( i_entry = 1 ; i_entry < p_vcd->num_entries ; i_entry ++ )
377         {
378             if( p_vcd->cur_lsn < p_vcd->p_entries[i_entry] )
379             {
380               VCDUpdateVar( p_input, i_entry, VLC_VAR_SETVALUE, 
381                             "chapter", "Setting entry" );
382               break;
383             }
384         }
385         p_vcd->play_item.num  = p_area->i_part = i_entry;
386         p_vcd->play_item.type = VCDINFO_ITEM_TYPE_ENTRY;
387     }
388 #undef p_area
389
390     p_input->stream.p_selected_area->i_tell = i_off;
391
392     dbg_print( (INPUT_DBG_CALL|INPUT_DBG_EXT|INPUT_DBG_SEEK),
393     "orig %d, cur: %d, offset: %lld, start: %lld, entry %d", 
394                p_vcd->origin_lsn, p_vcd->cur_lsn, i_off, 
395                p_input->stream.p_selected_area->i_start, i_entry );
396
397     vlc_mutex_unlock( &p_input->stream.stream_lock );
398 }
399
400 /*****************************************************************************
401   VCDPlay: set up internal structures so seeking/reading places an item.
402   itemid: the thing to play.
403   user_entry: true if itemid is a user selection (rather than internally-
404   generated selection such as via PBC) in which case we may have to adjust 
405   for differences in numbering. 
406  *****************************************************************************/
407 int
408 VCDPlay( input_thread_t *p_input, vcdinfo_itemid_t itemid )
409 {
410     thread_vcd_data_t *     p_vcd= (thread_vcd_data_t *)p_input->p_access_data;
411     input_area_t *          p_area;
412     
413     p_vcd->in_still = false;
414
415     dbg_print(INPUT_DBG_CALL, "itemid.num: %d, itemid.type: %d\n", 
416               itemid.num, itemid.type);
417
418 #define area p_input->stream.pp_areas
419
420     switch (itemid.type) {
421     case VCDINFO_ITEM_TYPE_TRACK:
422
423       /* Valid tracks go from 1...num_tracks-1, because track 0 is unplayable.
424        */
425
426       if (itemid.num == 0 || itemid.num >= p_vcd->num_tracks) {
427         LOG_ERR ("Invalid track number %d", itemid.num );
428         return VLC_EGENERIC;
429       }
430       p_area           = area[itemid.num];
431       p_area->i_part   = p_area->i_plugin_data;
432       p_input->stream.b_seekable = 1;
433       break;
434     case VCDINFO_ITEM_TYPE_SEGMENT: 
435       /* Valid segments go from 0...num_segments-1. */
436       if (itemid.num >= p_vcd->num_segments) {
437         LOG_ERR ( "Invalid segment number: %d", itemid.num );
438         return VLC_EGENERIC;
439       } else {
440         vcdinfo_video_segment_type_t segtype = 
441           vcdinfo_get_video_type(p_vcd->vcd, itemid.num);
442         
443         dbg_print(INPUT_DBG_PBC, "%s (%d), seg_num: %d", 
444                   vcdinfo_video_type2str(p_vcd->vcd, itemid.num), 
445                   (int) segtype, itemid.num);
446         
447         p_area           = area[0];
448         p_area->i_part   = itemid.num;
449         
450         switch (segtype)
451           {
452           case VCDINFO_FILES_VIDEO_NTSC_STILL:
453           case VCDINFO_FILES_VIDEO_NTSC_STILL2:
454           case VCDINFO_FILES_VIDEO_PAL_STILL:
455           case VCDINFO_FILES_VIDEO_PAL_STILL2:
456             p_input->stream.b_seekable = 0;
457             p_vcd->in_still = true;
458             break;
459           default:
460             p_input->stream.b_seekable = 1;
461             p_vcd->in_still = false;
462           }
463       }
464       break;
465       
466     case VCDINFO_ITEM_TYPE_LID:
467       /* LIDs go from 1..num_lids. */
468       if (itemid.num == 0 || itemid.num > p_vcd->num_lids) {
469         LOG_ERR ( "Invalid LID number: %d", itemid.num );
470         return VLC_EGENERIC;
471       } else {
472         p_vcd->cur_lid = itemid.num;
473         vcdinfo_lid_get_pxd(p_vcd->vcd, &(p_vcd->pxd), itemid.num);
474     
475         switch (p_vcd->pxd.descriptor_type) {
476       
477         case PSD_TYPE_SELECTION_LIST:
478         case PSD_TYPE_EXT_SELECTION_LIST: {
479           vcdinfo_itemid_t trans_itemid;
480           uint16_t trans_itemid_num;
481           
482           if (p_vcd->pxd.psd == NULL) return VLC_EGENERIC;
483           trans_itemid_num  = vcdinf_psd_get_itemid(p_vcd->pxd.psd);
484           vcdinfo_classify_itemid(trans_itemid_num, &trans_itemid);
485           p_vcd->loop_count = 1;
486           p_vcd->loop_item  = trans_itemid;
487           return VCDPlay( p_input, trans_itemid );
488           break;
489         }
490           
491         case PSD_TYPE_PLAY_LIST: {
492           if (p_vcd->pxd.pld == NULL) return VLC_EGENERIC;
493           p_vcd->pdi = -1;
494           return vcdplayer_inc_play_item(p_input) 
495             ? VLC_SUCCESS : VLC_EGENERIC;
496           break;
497         }
498           
499         case PSD_TYPE_END_LIST:
500         case PSD_TYPE_COMMAND_LIST:
501           
502         default:
503           ;
504         }
505       }
506       return VLC_EGENERIC;
507     case VCDINFO_ITEM_TYPE_ENTRY:
508       /* Entries go from 0..num_entries-1. */
509       if (itemid.num >= p_vcd->num_entries) {
510         LOG_ERR ("Invalid entry number: %d", itemid.num );
511         return VLC_EGENERIC;
512       } else {
513         track_t cur_track  = vcdinfo_get_track(p_vcd->vcd,  itemid.num);
514         p_area             = area[cur_track];
515         p_area->i_part     = itemid.num;
516         p_input->stream.b_seekable = 1;
517       }
518       break;
519     default:
520       LOG_ERR ("unknown entry type" );
521       return VLC_EGENERIC;
522     }
523
524     VCDSetArea( p_input, p_area );
525
526 #undef area
527
528     p_vcd->play_item = itemid;
529
530     dbg_print( (INPUT_DBG_CALL), 
531                "i_start %lld, i_size: %lld, i_tell: %lld, lsn %d", 
532                p_area->i_start, p_area->i_size, 
533                p_area->i_tell, p_vcd->cur_lsn );
534         
535     return VLC_SUCCESS;
536 }
537
538 /*****************************************************************************
539   VCDEntryPoints: Reads the information about the entry points on the disc
540   and initializes area information with that.
541   Before calling this track information should have been read in.
542  *****************************************************************************/
543 static int 
544 VCDEntryPoints( input_thread_t * p_input )
545 {
546     thread_vcd_data_t *               p_vcd;
547     unsigned int                      i_nb;
548     unsigned int                      i, i_entry_index = 0;
549     unsigned int                      i_previous_track = CDIO_INVALID_TRACK;
550
551     p_vcd = (thread_vcd_data_t *) p_input->p_access_data;
552
553     i_nb = vcdinfo_get_num_entries(p_vcd->vcd);
554     if (0 == i_nb) 
555       return -1;
556     
557     p_vcd->p_entries  = malloc( sizeof( lba_t ) * i_nb );
558
559     if( p_vcd->p_entries == NULL )
560     {
561         LOG_ERR ("not enough memory for entry points treatment" );
562         return -1;
563     }
564
565     p_vcd->num_entries = 0;
566
567     for( i = 0 ; i < i_nb ; i++ )
568     {
569         track_t i_track = vcdinfo_get_track(p_vcd->vcd, i);
570         if( i_track <= p_input->stream.i_area_nb )
571         {
572             p_vcd->p_entries[i] = 
573               vcdinfo_get_entry_lsn(p_vcd->vcd, i);
574             p_input->stream.pp_areas[i_track]->i_part_nb ++;
575
576             /* if this entry belongs to a new track */
577             if( i_track != i_previous_track )
578             {
579                 /* i_plugin_data is used to store the first entry of the area*/
580                 p_input->stream.pp_areas[i_track]->i_plugin_data =
581                                                             i_entry_index;
582                 i_previous_track = i_track;
583                 p_input->stream.pp_areas[i_track]->i_part_nb = 1;
584             }
585             i_entry_index ++;
586             p_vcd->num_entries ++;
587         }
588         else
589             msg_Warn( p_input, "wrong track number found in entry points" );
590     }
591     p_vcd->b_valid_ep = true;
592     return 0;
593 }
594
595 /*****************************************************************************
596  * VCDSegments: Reads the information about the segments the disc.
597  *****************************************************************************/
598 static int
599 VCDSegments( input_thread_t * p_input )
600 {
601     thread_vcd_data_t * p_vcd;
602     unsigned int        i;
603     unsigned int        num_segments;
604     
605
606     p_vcd = (thread_vcd_data_t *) p_input->p_access_data;
607     num_segments = p_vcd->num_segments = vcdinfo_get_num_segments(p_vcd->vcd);
608
609 #define area p_input->stream.pp_areas
610
611     /* area 0 is reserved for segments. Set Absolute start offset
612          and size */
613     area[0]->i_plugin_data = 0;
614     input_DelArea( p_input, area[0] );
615     input_AddArea( p_input, 0, 0 );
616     
617     area[0]->i_start = (off_t)p_vcd->p_sectors[0] 
618       * (off_t)M2F2_SECTOR_SIZE;
619     area[0]->i_size = (off_t)(p_vcd->p_sectors[1] - p_vcd->p_sectors[0])
620       * (off_t)M2F2_SECTOR_SIZE;
621     
622     /* Default Segment  */
623     area[0]->i_part = 0;
624     
625     /* i_plugin_data is used to store which entry point is the first
626        of the track (area) */
627     area[0]->i_plugin_data = 0;
628
629     area[0]->i_part_nb = 0;
630     
631     dbg_print( INPUT_DBG_MRL, 
632                "area[0] id: %d, i_start: %lld, i_size: %lld", 
633                area[0]->i_id, area[0]->i_start, area[0]->i_size );
634
635     if (num_segments == 0) return 0;
636
637     /* We have one additional segment allocated so we can get the size
638        by subtracting seg[i+1] - seg[i].
639      */
640     p_vcd->p_segments = malloc( sizeof( lba_t ) * (num_segments+1) );
641     if( p_vcd->p_segments == NULL )
642     {
643         LOG_ERR ("not enough memory for segment treatment" );
644         return -1;
645     }
646
647     /* Update the navigation variables without triggering a callback */
648     VCDUpdateVar( p_input, 0, VLC_VAR_SETVALUE, "title", "Setting track" );
649     var_Change( p_input, "chapter", VLC_VAR_CLEARCHOICES, NULL, NULL );
650     
651     for( i = 0 ; i < num_segments ; i++ )
652     {
653       p_vcd->p_segments[i] = vcdinfo_get_seg_lsn(p_vcd->vcd, i);
654       area[0]->i_part_nb ++;
655       VCDUpdateVar( p_input, i , VLC_VAR_ADDCHOICE, 
656                     "chapter", "Adding segment choice");
657     }
658
659 #undef area
660
661     p_vcd->p_segments[num_segments] = p_vcd->p_segments[num_segments-1]+
662       vcdinfo_get_seg_sector_count(p_vcd->vcd, num_segments-1);
663     
664     return 0;
665 }
666
667 /*****************************************************************************
668  VCDTracks: initializes area information. 
669  Before calling this track information should have been read in.
670  *****************************************************************************/
671 static void
672 VCDTracks( input_thread_t * p_input )
673 {
674     thread_vcd_data_t * p_vcd;
675     unsigned int        i;
676
677     p_vcd = (thread_vcd_data_t *) p_input->p_access_data;
678
679 #define area p_input->stream.pp_areas
680
681     /* We start area addressing for tracks at 1 since the default area 0
682        is reserved for segments */
683
684     for( i = 1 ; i < p_vcd->num_tracks ; i++ )
685     {
686         /* Tracks are Program Chains */
687         input_AddArea( p_input, i, i );
688
689         /* Absolute start byte offset and byte size */
690         area[i]->i_start = (off_t) p_vcd->p_sectors[i] 
691                            * (off_t)M2F2_SECTOR_SIZE;
692         area[i]->i_size = (off_t)(p_vcd->p_sectors[i+1] - p_vcd->p_sectors[i])
693                            * (off_t)M2F2_SECTOR_SIZE;
694
695         /* Current entry being played in track */
696         area[i]->i_part = 0;
697
698         /* i_plugin_data is used to store which entry point is the first
699          * of the track (area) */
700         area[i]->i_plugin_data = 0;
701
702         dbg_print( INPUT_DBG_MRL, 
703                    "area[%d] id: %d, i_start: %lld, i_size: %lld", 
704                    i, area[i]->i_id, area[i]->i_start, area[i]->i_size );
705     }
706
707 #undef area
708
709     return ;
710 }
711
712 /*****************************************************************************
713   VCDLIDs: Reads the LIST IDs from the LOT.
714  *****************************************************************************/
715 static int 
716 VCDLIDs( input_thread_t * p_input )
717 {
718     thread_vcd_data_t *p_vcd = (thread_vcd_data_t *) p_input->p_access_data;
719
720     p_vcd->num_lids = vcdinfo_get_num_LIDs(p_vcd->vcd);
721     p_vcd->cur_lid  = VCDINFO_INVALID_ENTRY;
722
723     if (vcdinfo_read_psd (p_vcd->vcd)) {
724       
725       vcdinfo_visit_lot (p_vcd->vcd, false);
726       
727 #if FIXED
728     /* 
729        We need to change libvcdinfo to be more robust when there are 
730        problems reading the extended PSD. Given that area-highlighting and 
731        selection features in the extended PSD haven't been implemented,
732        it's best then to not try to read this at all.
733      */
734       if (vcdinfo_get_psd_x_size(p_vcd->vcd))
735         vcdinfo_visit_lot (p_vcd->vcd, true);
736 #endif 
737     }
738
739     dbg_print( (INPUT_DBG_CALL|INPUT_DBG_MRL), 
740                "num LIDs=%d", p_vcd->num_lids);
741
742     return 0;
743 }
744
745 /*****************************************************************************
746  * VCDParse: parse command line
747  *****************************************************************************/
748 static char * 
749 VCDParse( input_thread_t * p_input, /*out*/ vcdinfo_itemid_t * p_itemid )
750 {
751     thread_vcd_data_t *p_vcd = (thread_vcd_data_t *)p_input->p_access_data;
752     char *             psz_parser;
753     char *             psz_source;
754     char *             psz_next;
755
756     if ( config_GetInt( p_input, MODULE_STRING "-PBC" ) ) {
757       p_itemid->type=VCDINFO_ITEM_TYPE_LID;
758       p_itemid->num=1;
759     } else {
760       p_itemid->type=VCDINFO_ITEM_TYPE_TRACK;
761       p_itemid->num=1;
762     }
763     
764 #ifdef WIN32
765     /* On Win32 we want the VCD access plugin to be explicitly requested,
766      * we end up with lots of problems otherwise */
767     if( !p_input->psz_access || !*p_input->psz_access ) return NULL;
768 #endif
769
770     if( !p_input->psz_name )
771     {
772         return NULL;
773     }
774
775     psz_parser = psz_source = strdup( p_input->psz_name );
776
777     /* Parse input string :
778      * [device][@[type][title]] */
779     while( *psz_parser && *psz_parser != '@' )
780     {
781         psz_parser++;
782     }
783
784     if( *psz_parser == '@' )
785     {
786       /* Found the divide between the source name and the 
787          type+entry number. */
788       unsigned int num;
789       
790       *psz_parser = '\0';
791       ++psz_parser;
792       if( *psz_parser )
793         {
794           switch(*psz_parser) {
795           case 'E': 
796             p_itemid->type = VCDINFO_ITEM_TYPE_ENTRY;
797             ++psz_parser;
798             break;
799           case 'P': 
800             p_itemid->type = VCDINFO_ITEM_TYPE_LID;
801             ++psz_parser;
802             break;
803           case 'S': 
804             p_itemid->type = VCDINFO_ITEM_TYPE_SEGMENT;
805             ++psz_parser;
806             break;
807           case 'T': 
808             p_itemid->type = VCDINFO_ITEM_TYPE_TRACK;
809             ++psz_parser;
810             break;
811           default: ;
812           }
813         }
814       
815       num = strtol( psz_parser, &psz_next, 10 );
816       if ( *psz_parser != '\0' && *psz_next == '\0') 
817         {
818           p_itemid->num = num;
819         }
820       
821     }
822
823     if( !*psz_source ) {
824
825       /* No source specified, so figure it out. */
826       if( !p_input->psz_access ) return NULL;
827       
828       psz_source = config_GetPsz( p_input, MODULE_STRING "-device" );
829
830       if( !psz_source || 0==strlen(psz_source) ) {
831         /* Scan for a CD-ROM drive with a VCD in it. */
832         char **cd_drives = cdio_get_devices_with_cap(NULL, 
833                             (CDIO_FS_ANAL_SVCD|CDIO_FS_ANAL_CVD
834                              |CDIO_FS_ANAL_VIDEOCD|CDIO_FS_UNKNOWN),
835                                                      true);
836         if (NULL == cd_drives) return NULL;
837         if (cd_drives[0] == NULL) {
838           cdio_free_device_list(cd_drives);
839           return NULL;
840         }
841         psz_source = strdup(cd_drives[0]);
842         cdio_free_device_list(cd_drives);
843       }
844     }
845
846     dbg_print( (INPUT_DBG_CALL|INPUT_DBG_MRL), 
847                "source=%s entry=%d type=%d",
848                psz_source, p_itemid->num, p_itemid->type);
849
850     return psz_source;
851 }
852
853 /* 
854    Set's start origin subsequent seeks/reads
855 */
856 static void 
857 VCDSetOrigin( input_thread_t *p_input, lsn_t origin_lsn, 
858               lsn_t cur_lsn, lsn_t end_lsn, int cur_entry, track_t cur_track )
859 {
860   thread_vcd_data_t * p_vcd  = (thread_vcd_data_t *) p_input->p_access_data;
861
862   p_vcd->origin_lsn = origin_lsn;
863   p_vcd->cur_lsn    = cur_lsn;
864   p_vcd->end_lsn    = end_lsn;
865   p_vcd->cur_track  = cur_track;
866   p_vcd->play_item.num  = cur_entry;
867   p_vcd->play_item.type = VCDINFO_ITEM_TYPE_ENTRY;
868   
869   dbg_print( (INPUT_DBG_CALL|INPUT_DBG_LSN),
870              "origin: %d, cur_lsn: %d, end_lsn: %d, entry: %d, track: %d",
871              origin_lsn, cur_lsn, end_lsn, cur_entry, cur_track );
872
873   p_input->stream.p_selected_area->i_tell =
874     (off_t) (p_vcd->cur_lsn - p_vcd->origin_lsn) * (off_t)M2F2_SECTOR_SIZE;
875
876   VCDUpdateVar( p_input, cur_entry, VLC_VAR_SETVALUE, 
877                 "chapter", "Setting entry");
878 }
879
880 /*****************************************************************************
881  * vcd_Open: Opens a VCD device or file and returns an opaque handle
882  *****************************************************************************/
883 static vcdinfo_obj_t *
884 vcd_Open( vlc_object_t *p_this, const char *psz_dev )
885 {
886     vcdinfo_obj_t *p_vcdobj;
887     char  *actual_dev;
888
889     if( !psz_dev ) return NULL;
890
891     /* Set where to log errors messages from libcdio. */
892     p_vcd_input = (input_thread_t *)p_this;
893     cdio_log_set_handler ( cdio_log_handler );
894     vcd_log_set_handler ( vcd_log_handler );
895     
896     actual_dev=strdup(psz_dev);
897     if ( vcdinfo_open(&p_vcdobj, &actual_dev, DRIVER_UNKNOWN, NULL) != 
898          VCDINFO_OPEN_VCD) {
899       free(actual_dev);
900       return NULL;
901     }
902     free(actual_dev);
903
904     return p_vcdobj;
905 }
906
907 /****************************************************************************
908  * VCDReadSector: Read a sector (2324 bytes)
909  ****************************************************************************/
910 static int 
911 VCDReadSector( vlc_object_t *p_this, const vcdinfo_obj_t *p_vcd,
912                lsn_t cur_lsn, byte_t * p_buffer )
913 {
914   typedef struct {
915     uint8_t subheader   [8];
916     uint8_t data        [M2F2_SECTOR_SIZE];
917   } vcdsector_t;
918   vcdsector_t vcd_sector;
919   
920   if (cdio_read_mode2_sector(vcdinfo_get_cd_image(p_vcd), 
921                              &vcd_sector, cur_lsn, true) 
922       != 0)
923     {
924       msg_Warn( p_this, "Could not read LSN %d", cur_lsn );
925       return -1;
926     }
927     
928   memcpy (p_buffer, vcd_sector.data, M2F2_SECTOR_SIZE);
929   
930   return( 0 );
931 }
932
933 /****************************************************************************
934  Update the "varname" variable to i_num without triggering a callback.
935 ****************************************************************************/
936 static void
937 VCDUpdateVar( input_thread_t *p_input, int i_num, int i_action,
938               const char *varname, const char *label)
939 {
940   vlc_value_t val;
941   val.i_int = i_num;
942   if (NULL != p_vcd_input) {
943     thread_vcd_data_t *p_vcd = (thread_vcd_data_t *)p_vcd_input->p_access_data;
944     dbg_print( INPUT_DBG_PBC, "%s %d", label, i_num );
945   }
946   var_Change( p_input, varname, i_action, &val, NULL );
947 }
948
949
950 #define meta_info_add_str(title, str) \
951   if ( str ) {                                                          \
952     dbg_print( INPUT_DBG_META, "field: %s: %s\n", title, str);          \
953     input_AddInfo( p_cat, _(title), "%s", str );                        \
954   }
955
956 #define meta_info_add_num(title, num) \
957   dbg_print( INPUT_DBG_META, "field %s: %d\n", title, num);             \
958   input_AddInfo( p_cat, _(title), "%d", num );                          \
959
960 static void InformationCreate( input_thread_t *p_input  )
961 {
962   thread_vcd_data_t *p_vcd = (thread_vcd_data_t *) p_input->p_access_data;
963   input_info_category_t *p_cat;
964   
965   p_cat = input_InfoCategory( p_input, "General" );
966
967   meta_info_add_str( "VCD Format", vcdinfo_get_format_version_str(p_vcd->vcd));
968   meta_info_add_str( "Album",      vcdinfo_get_album_id(p_vcd->vcd));
969   meta_info_add_str( "Application",vcdinfo_get_application_id(p_vcd->vcd));
970   meta_info_add_str( "Preparer",   vcdinfo_get_preparer_id(p_vcd->vcd));
971   meta_info_add_num( "Vol #",      vcdinfo_get_volume_num(p_vcd->vcd));
972   meta_info_add_num( "Vol max #",  vcdinfo_get_volume_count(p_vcd->vcd));
973   meta_info_add_str( "Volume Set", vcdinfo_get_volumeset_id(p_vcd->vcd));
974   meta_info_add_str( "Volume",     vcdinfo_get_volume_id(p_vcd->vcd));
975   meta_info_add_str( "Publisher",  vcdinfo_get_publisher_id(p_vcd->vcd));
976   meta_info_add_str( "System Id",  vcdinfo_get_system_id(p_vcd->vcd));
977   meta_info_add_num( "LIDs",       vcdinfo_get_num_LIDs(p_vcd->vcd));
978   meta_info_add_num( "Entries",    vcdinfo_get_num_entries(p_vcd->vcd));
979   meta_info_add_num( "Segments",   vcdinfo_get_num_segments(p_vcd->vcd));
980   meta_info_add_num( "Tracks",     vcdinfo_get_num_tracks(p_vcd->vcd));
981
982 }
983
984 #if FINISHED_PLAYLIST
985 static void
986 VCDCreatePlayListItem(const input_thread_t *p_input, 
987                       thread_vcd_data_t *p_vcd, 
988                       playlist_t *p_playlist, unsigned int i_track, 
989                       char *psz_mrl, int psz_mrl_max, 
990                       const char *psz_source, int playlist_operation, 
991                       unsigned int i_pos)
992 {
993   mtime_t i_duration = 
994     (vcdinfo_get_track_size(p_vcd->vcd, i_track) * 8 * 10000000)
995     / (400 * p_input->stream.control.i_rate) ;
996   char *p_title;
997   char *config_varname = MODULE_STRING "-title-format";
998
999   snprintf(psz_mrl, psz_mrl_max, "%s%s@T%u", 
1000            VCD_MRL_PREFIX, psz_source, i_track);
1001
1002 #if 0
1003   p_title = VCDFormatStr(p_input, p_vcd, 
1004                          config_GetPsz( p_input, config_varname ), 
1005                          psz_mrl, i_track);
1006 #else
1007   p_title = psz_mrl;
1008 #endif
1009
1010   playlist_AddExt( p_playlist, psz_mrl, p_title, i_duration, 
1011                    0, 0, playlist_operation, i_pos );
1012 }
1013
1014 static int
1015 VCDFixupPlayList( input_thread_t *p_input, thread_vcd_data_t *p_vcd, 
1016                   const char *psz_source )
1017 {
1018   unsigned int i;
1019   playlist_t * p_playlist;
1020   char       * psz_mrl;
1021   unsigned int psz_mrl_max = strlen(VCD_MRL_PREFIX) + strlen(psz_source) + 
1022     strlen("@T") + strlen("100") + 1;
1023
1024   psz_mrl = malloc( psz_mrl_max );
1025
1026   if( psz_mrl == NULL )
1027     {
1028       msg_Warn( p_input, "out of memory" );
1029       return -1;
1030     }
1031
1032   p_playlist = (playlist_t *) vlc_object_find( p_input, VLC_OBJECT_PLAYLIST,
1033                                                FIND_ANYWHERE );
1034   if( !p_playlist )
1035     {
1036       msg_Warn( p_input, "can't find playlist" );
1037       free(psz_mrl);
1038       return -1;
1039     }
1040
1041   if ( config_GetInt( p_input, MODULE_STRING "-PBC" ) ) {
1042     /* May fill out more information when the playlist user interface becomes
1043        more mature.
1044      */
1045     VCDCreatePlayListItem(p_input, p_vcd, p_playlist, p_vcd->cur_track, 
1046                           psz_mrl, psz_mrl_max, psz_source, PLAYLIST_REPLACE, 
1047                           p_playlist->i_index);
1048   } else {
1049   
1050     playlist_Delete( p_playlist, p_playlist->i_index);
1051
1052     for( i = 1 ; i < p_vcd->num_tracks ; i++ )
1053       {
1054         VCDCreatePlayListItem(p_input, p_vcd, p_playlist, i, psz_mrl, 
1055                               psz_mrl_max, psz_source, PLAYLIST_APPEND, 
1056                               PLAYLIST_END);
1057
1058       }
1059
1060     playlist_Command( p_playlist, PLAYLIST_GOTO, 0 );
1061
1062   }
1063     
1064   vlc_object_release( p_playlist );
1065   free(psz_mrl);
1066   return 0;
1067 }
1068 #endif
1069
1070 /*****************************************************************************
1071  * Public routines.
1072  *****************************************************************************/
1073 int
1074 E_(DebugCallback)   ( vlc_object_t *p_this, const char *psz_name,
1075                       vlc_value_t oldval, vlc_value_t val, void *p_data )
1076 {
1077   thread_vcd_data_t *p_vcd;
1078
1079   if (NULL == p_vcd_input) return VLC_EGENERIC;
1080   
1081   p_vcd = (thread_vcd_data_t *)p_vcd_input->p_access_data;
1082
1083   if (p_vcd->i_debug & (INPUT_DBG_CALL|INPUT_DBG_EXT)) {
1084     msg_Dbg( p_vcd_input, "Old debug (x%0x) %d, new debug (x%0x) %d", 
1085              p_vcd->i_debug, p_vcd->i_debug, val.i_int, val.i_int);
1086   }
1087   p_vcd->i_debug = val.i_int;
1088   return VLC_SUCCESS;
1089 }
1090
1091 /*****************************************************************************
1092   Open: open VCD.
1093   read in meta-information about VCD: the number of tracks, segments, 
1094   entries, size and starting information. Then set up state variables so
1095   that we read/seek starting at the location specified.
1096
1097   On success we return VLC_SUCCESS, on memory exhausted VLC_ENOMEM, 
1098   and VLC_EGENERIC for some other error.
1099  *****************************************************************************/
1100 int 
1101 E_(Open) ( vlc_object_t *p_this )
1102 {
1103     input_thread_t *        p_input = (input_thread_t *)p_this;
1104     thread_vcd_data_t *     p_vcd;
1105     char *                  psz_source;
1106     vcdinfo_itemid_t        itemid;
1107     bool                    b_play_ok;
1108     
1109     p_input->pf_read        = VCDRead;
1110     p_input->pf_seek        = VCDSeek;
1111     p_input->pf_set_area    = VCDSetArea;
1112     p_input->pf_set_program = VCDSetProgram;
1113
1114     p_vcd = malloc( sizeof(thread_vcd_data_t) );
1115
1116     if( p_vcd == NULL )
1117     {
1118         LOG_ERR ("out of memory" );
1119         return VLC_ENOMEM;
1120     }
1121
1122     p_input->p_access_data = (void *)p_vcd;
1123     p_vcd->i_debug         = config_GetInt( p_this, MODULE_STRING "-debug" );
1124     psz_source             = VCDParse( p_input, &itemid );
1125
1126     if ( NULL == psz_source ) 
1127     {
1128       free( p_vcd );
1129       return( VLC_EGENERIC );
1130     }
1131
1132     dbg_print( (INPUT_DBG_CALL|INPUT_DBG_EXT), "%s", psz_source );
1133
1134     p_vcd->p_segments = NULL;
1135     p_vcd->p_entries  = NULL;
1136     
1137     /* set up input  */
1138     p_input->i_mtu = VCD_DATA_ONCE;
1139
1140     vlc_mutex_lock( &p_input->stream.stream_lock );
1141
1142     /* If we are here we can control the pace... */
1143     p_input->stream.b_pace_control = 1;
1144
1145     p_input->stream.b_seekable = 1;
1146     p_input->stream.p_selected_area->i_size = 0;
1147     p_input->stream.p_selected_area->i_tell = 0;
1148
1149     vlc_mutex_unlock( &p_input->stream.stream_lock );
1150
1151     if( !(p_vcd->vcd = vcd_Open( p_this, psz_source )) )
1152     {
1153         msg_Warn( p_input, "could not open %s", psz_source );
1154         goto err_exit;
1155     }
1156
1157     /* Get track information. */
1158     p_vcd->num_tracks = ioctl_GetTracksMap( VLC_OBJECT(p_input),
1159                                             vcdinfo_get_cd_image(p_vcd->vcd), 
1160                                             &p_vcd->p_sectors );
1161     if( p_vcd->num_tracks < 0 )
1162         LOG_ERR ("unable to count tracks" );
1163     else if( p_vcd->num_tracks <= 1 )
1164         LOG_ERR ("no movie tracks found" );
1165     if( p_vcd->num_tracks <= 1)
1166     {
1167         vcdinfo_close( p_vcd->vcd );
1168         goto err_exit;
1169     }
1170
1171     /* Set stream and area data */
1172     vlc_mutex_lock( &p_input->stream.stream_lock );
1173
1174     /* Initialize ES structures */
1175     input_InitStream( p_input, sizeof( stream_ps_data_t ) );
1176
1177     /* disc input method */
1178     p_input->stream.i_method = INPUT_METHOD_VCD;
1179
1180     p_input->stream.i_area_nb = 1;
1181     
1182
1183     /* Initialize segment information. */
1184     VCDSegments( p_input );
1185     
1186     /* Initialize track area information. */
1187     VCDTracks( p_input );
1188     
1189     if( VCDEntryPoints( p_input ) < 0 )
1190     {
1191         msg_Warn( p_input, "could not read entry points, will not use them" );
1192         p_vcd->b_valid_ep = false;
1193     }
1194
1195     if( VCDLIDs( p_input ) < 0 )
1196     {
1197         msg_Warn( p_input, "could not read entry LIDs" );
1198     }
1199
1200     b_play_ok = (VLC_SUCCESS == VCDPlay( p_input, itemid ));
1201     
1202     vlc_mutex_unlock( &p_input->stream.stream_lock );
1203
1204     if ( ! b_play_ok ) {
1205       vcdinfo_close( p_vcd->vcd );
1206       goto err_exit;
1207     }
1208
1209     if( !p_input->psz_demux || !*p_input->psz_demux )
1210     {
1211 #if FIXED
1212       p_input->psz_demux = "vcdx";
1213 #else
1214       p_input->psz_demux = "ps";
1215 #endif
1216     }
1217
1218     p_vcd->p_intf = intf_Create( p_input, "vcdx" );
1219     p_vcd->p_intf->b_block = VLC_FALSE;
1220     intf_RunThread( p_vcd->p_intf );
1221
1222     InformationCreate( p_input );
1223
1224 #if FINISHED_PLAYLIST
1225     VCDFixupPlayList( p_input, p_vcd, psz_source );
1226 #endif
1227     
1228     free( psz_source );
1229
1230     return VLC_SUCCESS;
1231  err_exit:
1232     free( psz_source );
1233     free( p_vcd );
1234     return VLC_EGENERIC;
1235 }
1236
1237 /*****************************************************************************
1238  * Close: closes VCD releasing allocated memory.
1239  *****************************************************************************/
1240 void 
1241 E_(Close) ( vlc_object_t *p_this )
1242 {
1243     input_thread_t *   p_input = (input_thread_t *)p_this;
1244     thread_vcd_data_t *p_vcd = (thread_vcd_data_t *)p_input->p_access_data;
1245
1246     dbg_print( (INPUT_DBG_CALL|INPUT_DBG_EXT), "VCDClose" );
1247     vcdinfo_close( p_vcd->vcd );
1248
1249     free( p_vcd->p_entries );
1250     free( p_vcd->p_segments );
1251
1252     /* For reasons that are a mystery to me we don't have to deal with
1253        stopping, and destroying the p_vcd->p_intf thread. And if we do
1254        it causes problems upstream.
1255      */
1256     if( p_vcd->p_intf != NULL )
1257     {
1258         p_vcd->p_intf = NULL;
1259     }
1260
1261     free( p_vcd );
1262     p_input->p_access_data = NULL;
1263     p_vcd_input = NULL;
1264 }