1 /*****************************************************************************
2 * vcd.c : VCD input module for vlc
3 * using libcdio, libvcd and libvcdinfo. vlc-specific things tend
5 *****************************************************************************
6 * Copyright (C) 2000, 2003, 2004 VideoLAN
7 * $Id: access.c,v 1.17 2004/01/25 04:53:16 rocky Exp $
9 * Authors: Rocky Bernstein <rocky@panix.com>
10 * Johan Bilien <jobi@via.ecp.fr>
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.
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.
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 *****************************************************************************/
27 /*****************************************************************************
29 *****************************************************************************/
32 #include <vlc/input.h>
35 #include "../../demux/mpeg/system.h"
37 #include "vcdplayer.h"
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>
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)
53 #define VCD_MRL_PREFIX "vcdx://"
55 /*****************************************************************************
57 *****************************************************************************/
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 * );
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,
74 static char *VCDParse ( input_thread_t *,
75 /*out*/ vcdinfo_itemid_t * p_itemid ,
76 /*out*/ bool *play_single_item );
78 static void VCDUpdateVar( input_thread_t *p_input, int i_entry, int i_action,
79 const char *p_varname, char *p_label,
80 const char *p_debug_label );
82 static vcdinfo_obj_t *vcd_Open ( vlc_object_t *p_this, const char *psz_dev );
84 /****************************************************************************
86 ****************************************************************************/
88 /* FIXME: This variable is a hack. Would be nice to eliminate the
91 static input_thread_t *p_vcd_input = NULL;
93 /* process messages that originate from libcdio. */
95 cdio_log_handler (cdio_log_level_t level, const char message[])
97 thread_vcd_data_t *p_vcd = (thread_vcd_data_t *)p_vcd_input->p_access_data;
101 if (p_vcd->i_debug & INPUT_DBG_CDIO)
102 msg_Dbg( p_vcd_input, message);
105 msg_Warn( p_vcd_input, message);
108 case CDIO_LOG_ASSERT:
109 msg_Err( p_vcd_input, message);
112 msg_Warn( p_vcd_input, message,
113 _("The above message had unknown log level"),
119 /* process messages that originate from vcdinfo. */
121 vcd_log_handler (vcd_log_level_t level, const char message[])
123 thread_vcd_data_t *p_vcd = (thread_vcd_data_t *)p_vcd_input->p_access_data;
127 if (p_vcd->i_debug & INPUT_DBG_VCDINFO)
128 msg_Dbg( p_vcd_input, message);
131 msg_Warn( p_vcd_input, message);
135 msg_Err( p_vcd_input, message);
138 msg_Warn( p_vcd_input, "%s\n%s %d", message,
139 _("The above message had unknown vcdimager log level"),
145 /*****************************************************************************
146 * VCDRead: reads i_len bytes from the VCD into p_buffer.
147 *****************************************************************************
148 * Returns -1 in case of error, 0 in case of EOF, otherwise the number of
150 *****************************************************************************/
152 VCDRead( input_thread_t * p_input, byte_t * p_buffer, size_t i_len )
154 thread_vcd_data_t * p_vcd= (thread_vcd_data_t *)p_input->p_access_data;
158 byte_t p_last_sector[ M2F2_SECTOR_SIZE ];
162 dbg_print( (INPUT_DBG_CALL), "lsn: %u", p_vcd->cur_lsn );
164 /* Compute the number of blocks we have to read */
166 i_blocks = i_len / M2F2_SECTOR_SIZE;
168 for ( i_index = 0 ; i_index < i_blocks ; i_index++ )
171 if ( p_vcd->cur_lsn == p_vcd->end_lsn ) {
172 vcdplayer_read_status_t read_status;
174 /* We've run off of the end of this entry. Do we continue or stop? */
175 dbg_print( (INPUT_DBG_LSN|INPUT_DBG_PBC),
176 "end reached, cur: %u", p_vcd->cur_lsn );
178 read_status = vcdplayer_pbc_is_on( p_vcd )
179 ? vcdplayer_pbc_nav( p_input )
180 : vcdplayer_non_pbc_nav( p_input );
182 switch (read_status) {
184 /* End reached. Return NULL to indicated this. */
186 /* Some sort of error. */
189 case READ_STILL_FRAME:
191 /* Reached the end of a still frame. */
193 byte_t * p_buf = p_buffer;
194 pgrm_descriptor_t * p_pgrm = p_input->stream.p_selected_program;;
196 p_buf += (i_index*M2F2_SECTOR_SIZE);
197 memset(p_buf, 0, M2F2_SECTOR_SIZE);
200 dbg_print(INPUT_DBG_STILL, "Handled still event");
203 p_vcd->p_intf->p_sys->b_still = 1;
204 input_SetStatus( p_input, INPUT_STATUS_PAUSE );
207 vlc_mutex_lock( &p_input->stream.stream_lock );
209 p_pgrm = p_input->stream.p_selected_program;
210 p_pgrm->i_synchro_state = SYNCHRO_REINIT;
212 vlc_mutex_unlock( &p_input->stream.stream_lock );
214 input_ClockManageControl( p_input, p_pgrm, 0 );
216 p_vcd->p_intf->p_sys->b_still = 1;
217 input_SetStatus( p_input, INPUT_STATUS_PAUSE );
219 return i_read + M2F2_SECTOR_SIZE;
227 if ( VCDReadSector( VLC_OBJECT(p_input), p_vcd->vcd,
229 p_buffer + (i_index*M2F2_SECTOR_SIZE) ) < 0 )
231 LOG_ERR ("could not read sector %d", p_vcd->cur_lsn );
238 if( p_vcd->b_valid_ep &&
239 /* FIXME kludge so that read does not update chapter
240 * when a manual chapter change was requested and not
241 * yet accomplished */
242 !p_input->stream.p_new_area )
244 unsigned int i_entry = p_input->stream.p_selected_area->i_part;
246 vlc_mutex_lock( &p_input->stream.stream_lock );
248 if( i_entry < p_vcd->num_entries &&
249 p_vcd->cur_lsn >= p_vcd->p_entries[i_entry+1] )
251 dbg_print( INPUT_DBG_PBC,
252 "new entry, i_entry %d, sector %d, es %d",
253 i_entry, p_vcd->cur_lsn,
254 p_vcd->p_entries[i_entry] );
255 p_vcd->play_item.num =
256 ++ p_input->stream.p_selected_area->i_part;
257 p_vcd->play_item.type = VCDINFO_ITEM_TYPE_ENTRY;
258 VCDUpdateVar( p_input, p_vcd->play_item.num, VLC_VAR_SETVALUE,
259 "chapter", _("Entry"), "Setting entry" );
261 vlc_mutex_unlock( &p_input->stream.stream_lock );
264 i_read += M2F2_SECTOR_SIZE;
267 if ( i_len % M2F2_SECTOR_SIZE ) /* this should not happen */
269 if ( VCDReadSector( VLC_OBJECT(p_input), p_vcd->vcd,
270 p_vcd->cur_lsn, p_last_sector ) < 0 )
272 LOG_ERR ("could not read sector %d", p_vcd->cur_lsn );
276 p_input->p_vlc->pf_memcpy( p_buffer + i_blocks * M2F2_SECTOR_SIZE,
277 p_last_sector, i_len % M2F2_SECTOR_SIZE );
278 i_read += i_len % M2F2_SECTOR_SIZE;
285 /*****************************************************************************
286 * VCDSetProgram: Does nothing since a VCD is mono_program
287 *****************************************************************************/
289 VCDSetProgram( input_thread_t * p_input, pgrm_descriptor_t * p_program)
291 thread_vcd_data_t * p_vcd= (thread_vcd_data_t *)p_input->p_access_data;
292 dbg_print( (INPUT_DBG_CALL|INPUT_DBG_EXT), "VCDSetProgram" );
297 /*****************************************************************************
298 * VCDSetArea: initialize internal data structures and input stream data
299 so set subsequent reading and seeking to reflect that we are
300 at track x, entry or segment y.
301 This is called for each user navigation request, e.g. the GUI
302 Chapter/Title selections or in initial MRL parsing.
303 ****************************************************************************/
305 VCDSetArea( input_thread_t * p_input, input_area_t * p_area )
307 thread_vcd_data_t *p_vcd = (thread_vcd_data_t*)p_input->p_access_data;
308 unsigned int i_entry = p_area->i_part;
309 track_t i_track = p_area->i_id;
310 int old_seekable = p_input->stream.b_seekable;
311 unsigned int i_nb = p_area->i_plugin_data + p_area->i_part_nb;
313 dbg_print( (INPUT_DBG_CALL|INPUT_DBG_EXT),
314 "track: %d, entry %d, seekable %d, area %lx, select area %lx ",
315 i_track, i_entry, old_seekable,
316 (long unsigned int) p_area,
317 (long unsigned int) p_input->stream.p_selected_area );
319 /* we can't use the interface slider until initilization is complete */
320 p_input->stream.b_seekable = 0;
322 if( p_area != p_input->stream.p_selected_area )
326 /* If is the result of a track change, make the entry valid. */
327 if (i_entry < p_area->i_plugin_data || i_entry >= i_nb)
328 i_entry = p_area->i_plugin_data;
330 /* Change the default area */
331 p_input->stream.p_selected_area = p_area;
333 /* Update the navigation variables without triggering a callback */
335 VCDUpdateVar( p_input, i_track, VLC_VAR_SETVALUE, "title",
336 _("Track"), "Setting track");
338 var_Change( p_input, "chapter", VLC_VAR_CLEARCHOICES, NULL, NULL );
339 for( i = p_area->i_plugin_data; i < i_nb; i++ )
341 VCDUpdateVar( p_input, i , VLC_VAR_ADDCHOICE,
343 p_vcd->play_item.type == VCDINFO_ITEM_TYPE_ENTRY ?
344 _("Entry") : _("Segment"),
345 "Adding entry choice");
349 unsigned int audio_type =
350 vcdinfo_get_track_audio_type(p_vcd->vcd, i_track);
351 unsigned int i_channels =
352 vcdinfo_audio_type_num_channels(p_vcd->vcd, audio_type);
354 var_Change( p_input, "audio_channels", VLC_VAR_CLEARCHOICES, NULL,
357 for( i = 0; i < i_channels; i++ )
359 VCDUpdateVar( p_input, i , VLC_VAR_ADDCHOICE,
360 "audio_channels", NULL, "Adding audio choice");
367 VCDSetOrigin( p_input, p_vcd->p_segments[i_entry],
368 p_vcd->p_segments[i_entry], p_vcd->p_segments[i_entry+1],
371 VCDSetOrigin( p_input, p_vcd->p_sectors[i_track],
372 vcdinfo_get_entry_lsn(p_vcd->vcd, i_entry),
373 p_vcd->p_sectors[i_track+1],
376 p_input->stream.b_seekable = old_seekable;
377 /* warn interface that something has changed */
378 p_input->stream.b_changed = 1;
384 /****************************************************************************
386 ****************************************************************************/
388 VCDSeek( input_thread_t * p_input, off_t i_off )
390 thread_vcd_data_t * p_vcd;
391 unsigned int i_entry=0; /* invalid entry */
393 p_vcd = (thread_vcd_data_t *) p_input->p_access_data;
395 p_vcd->cur_lsn = p_vcd->origin_lsn + (i_off / (off_t)M2F2_SECTOR_SIZE);
397 vlc_mutex_lock( &p_input->stream.stream_lock );
398 #define p_area p_input->stream.p_selected_area
400 if( p_vcd->b_valid_ep )
402 for( i_entry = 0 ; i_entry < p_vcd->num_entries ; i_entry ++ )
404 if( p_vcd->cur_lsn < p_vcd->p_entries[i_entry] )
406 VCDUpdateVar( p_input, i_entry, VLC_VAR_SETVALUE,
407 "chapter", _("Entry"), "Setting entry" );
411 p_vcd->play_item.num = p_area->i_part = i_entry;
412 p_vcd->play_item.type = VCDINFO_ITEM_TYPE_ENTRY;
416 p_input->stream.p_selected_area->i_tell = i_off;
418 dbg_print( (INPUT_DBG_CALL|INPUT_DBG_EXT|INPUT_DBG_SEEK),
419 "orig %d, cur: %d, offset: %lld, start: %lld, entry %d",
420 p_vcd->origin_lsn, p_vcd->cur_lsn, i_off,
421 p_input->stream.p_selected_area->i_start, i_entry );
423 vlc_mutex_unlock( &p_input->stream.stream_lock );
426 /*****************************************************************************
427 VCDPlay: set up internal structures so seeking/reading places an item.
428 itemid: the thing to play.
429 user_entry: true if itemid is a user selection (rather than internally-
430 generated selection such as via PBC) in which case we may have to adjust
431 for differences in numbering.
432 *****************************************************************************/
434 VCDPlay( input_thread_t *p_input, vcdinfo_itemid_t itemid )
436 thread_vcd_data_t * p_vcd= (thread_vcd_data_t *)p_input->p_access_data;
437 input_area_t * p_area;
440 dbg_print(INPUT_DBG_CALL, "itemid.num: %d, itemid.type: %d\n",
441 itemid.num, itemid.type);
443 if (!p_input->p_access_data) return VLC_EGENERIC;
445 b_was_still = p_vcd->in_still;
447 #define area p_input->stream.pp_areas
449 switch (itemid.type) {
450 case VCDINFO_ITEM_TYPE_TRACK:
452 /* Valid tracks go from 1...num_tracks-1, because track 0 is unplayable.
455 if (itemid.num == 0 || itemid.num >= p_vcd->num_tracks) {
456 LOG_ERR ("Invalid track number %d", itemid.num );
459 p_vcd->in_still = false;
460 p_area = area[itemid.num];
461 p_area->i_part = p_area->i_plugin_data;
462 p_input->stream.b_seekable = 1;
464 case VCDINFO_ITEM_TYPE_SEGMENT:
465 /* Valid segments go from 0...num_segments-1. */
466 if (itemid.num >= p_vcd->num_segments) {
467 LOG_ERR ( "Invalid segment number: %d", itemid.num );
470 vcdinfo_video_segment_type_t segtype =
471 vcdinfo_get_video_type(p_vcd->vcd, itemid.num);
473 dbg_print(INPUT_DBG_PBC, "%s (%d), seg_num: %d",
474 vcdinfo_video_type2str(p_vcd->vcd, itemid.num),
475 (int) segtype, itemid.num);
478 p_area->i_part = itemid.num;
482 case VCDINFO_FILES_VIDEO_NTSC_STILL:
483 case VCDINFO_FILES_VIDEO_NTSC_STILL2:
484 case VCDINFO_FILES_VIDEO_PAL_STILL:
485 case VCDINFO_FILES_VIDEO_PAL_STILL2:
486 p_input->stream.b_seekable = 0;
487 p_vcd->in_still = true;
490 p_input->stream.b_seekable = 1;
491 p_vcd->in_still = false;
496 case VCDINFO_ITEM_TYPE_LID:
497 /* LIDs go from 1..num_lids. */
498 if (itemid.num == 0 || itemid.num > p_vcd->num_lids) {
499 LOG_ERR ( "Invalid LID number: %d", itemid.num );
502 p_vcd->cur_lid = itemid.num;
503 vcdinfo_lid_get_pxd(p_vcd->vcd, &(p_vcd->pxd), itemid.num);
505 switch (p_vcd->pxd.descriptor_type) {
507 case PSD_TYPE_SELECTION_LIST:
508 case PSD_TYPE_EXT_SELECTION_LIST: {
509 vcdinfo_itemid_t trans_itemid;
510 uint16_t trans_itemid_num;
512 if (p_vcd->pxd.psd == NULL) return VLC_EGENERIC;
513 trans_itemid_num = vcdinf_psd_get_itemid(p_vcd->pxd.psd);
514 vcdinfo_classify_itemid(trans_itemid_num, &trans_itemid);
515 p_vcd->loop_count = 1;
516 p_vcd->loop_item = trans_itemid;
517 return VCDPlay( p_input, trans_itemid );
521 case PSD_TYPE_PLAY_LIST: {
522 if (p_vcd->pxd.pld == NULL) return VLC_EGENERIC;
524 return vcdplayer_inc_play_item(p_input)
525 ? VLC_SUCCESS : VLC_EGENERIC;
529 case PSD_TYPE_END_LIST:
530 case PSD_TYPE_COMMAND_LIST:
537 case VCDINFO_ITEM_TYPE_ENTRY:
538 /* Entries go from 0..num_entries-1. */
539 if (itemid.num >= p_vcd->num_entries) {
540 LOG_ERR ("Invalid entry number: %d", itemid.num );
543 track_t cur_track = vcdinfo_get_track(p_vcd->vcd, itemid.num);
544 p_vcd->in_still = false;
545 p_area = area[cur_track];
546 p_area->i_part = itemid.num;
547 p_input->stream.b_seekable = 1;
551 LOG_ERR ("unknown entry type" );
555 VCDSetArea( p_input, p_area );
560 if ( p_vcd->in_still != b_was_still ) {
561 if (p_input->stream.pp_selected_es) {
562 input_SetStatus( p_input, INPUT_STATUS_END );
563 input_SetStatus( p_input, INPUT_STATUS_PLAY );
568 p_vcd->play_item = itemid;
570 dbg_print( (INPUT_DBG_CALL),
571 "i_start %lld, i_size: %lld, i_tell: %lld, lsn %d",
572 p_area->i_start, p_area->i_size,
573 p_area->i_tell, p_vcd->cur_lsn );
578 /*****************************************************************************
579 VCDEntryPoints: Reads the information about the entry points on the disc
580 and initializes area information with that.
581 Before calling this track information should have been read in.
582 *****************************************************************************/
584 VCDEntryPoints( input_thread_t * p_input )
586 thread_vcd_data_t * p_vcd;
588 unsigned int i, i_entry_index = 0;
589 unsigned int i_previous_track = CDIO_INVALID_TRACK;
591 p_vcd = (thread_vcd_data_t *) p_input->p_access_data;
593 i_nb = vcdinfo_get_num_entries(p_vcd->vcd);
597 p_vcd->p_entries = malloc( sizeof( lba_t ) * i_nb );
599 if( p_vcd->p_entries == NULL )
601 LOG_ERR ("not enough memory for entry points treatment" );
605 p_vcd->num_entries = 0;
607 for( i = 0 ; i < i_nb ; i++ )
609 track_t i_track = vcdinfo_get_track(p_vcd->vcd, i);
610 if( i_track <= p_input->stream.i_area_nb )
612 p_vcd->p_entries[i] =
613 vcdinfo_get_entry_lsn(p_vcd->vcd, i);
614 p_input->stream.pp_areas[i_track]->i_part_nb ++;
616 /* if this entry belongs to a new track */
617 if( i_track != i_previous_track )
619 /* i_plugin_data is used to store the first entry of the area*/
620 p_input->stream.pp_areas[i_track]->i_plugin_data =
622 i_previous_track = i_track;
623 p_input->stream.pp_areas[i_track]->i_part_nb = 1;
626 p_vcd->num_entries ++;
629 msg_Warn( p_input, "wrong track number found in entry points" );
631 p_vcd->b_valid_ep = true;
635 /*****************************************************************************
636 * VCDSegments: Reads the information about the segments the disc.
637 *****************************************************************************/
639 VCDSegments( input_thread_t * p_input )
641 thread_vcd_data_t * p_vcd;
643 unsigned int num_segments;
646 p_vcd = (thread_vcd_data_t *) p_input->p_access_data;
647 num_segments = p_vcd->num_segments = vcdinfo_get_num_segments(p_vcd->vcd);
649 #define area p_input->stream.pp_areas
651 /* area 0 is reserved for segments. Set Absolute start offset
653 area[0]->i_plugin_data = 0;
654 input_DelArea( p_input, area[0] );
655 input_AddArea( p_input, 0, 0 );
657 area[0]->i_start = (off_t)p_vcd->p_sectors[0]
658 * (off_t)M2F2_SECTOR_SIZE;
659 area[0]->i_size = (off_t)(p_vcd->p_sectors[1] - p_vcd->p_sectors[0])
660 * (off_t)M2F2_SECTOR_SIZE;
662 /* Default Segment */
665 /* i_plugin_data is used to store which entry point is the first
666 of the track (area) */
667 area[0]->i_plugin_data = 0;
669 area[0]->i_part_nb = 0;
671 dbg_print( INPUT_DBG_MRL,
672 "area[0] id: %d, i_start: %lld, i_size: %lld",
673 area[0]->i_id, area[0]->i_start, area[0]->i_size );
675 if (num_segments == 0) return 0;
677 /* We have one additional segment allocated so we can get the size
678 by subtracting seg[i+1] - seg[i].
680 p_vcd->p_segments = malloc( sizeof( lba_t ) * (num_segments+1) );
681 if( p_vcd->p_segments == NULL )
683 LOG_ERR ("not enough memory for segment treatment" );
687 /* Update the navigation variables without triggering a callback */
688 VCDUpdateVar( p_input, 0, VLC_VAR_SETVALUE, "title", _("Track"),
691 var_Change( p_input, "chapter", VLC_VAR_CLEARCHOICES, NULL, NULL );
693 for( i = 0 ; i < num_segments ; i++ )
695 p_vcd->p_segments[i] = vcdinfo_get_seg_lsn(p_vcd->vcd, i);
696 area[0]->i_part_nb ++;
697 VCDUpdateVar( p_input, i , VLC_VAR_ADDCHOICE,
698 "chapter", _("Segment"), "Adding segment choice");
703 p_vcd->p_segments[num_segments] = p_vcd->p_segments[num_segments-1]+
704 vcdinfo_get_seg_sector_count(p_vcd->vcd, num_segments-1);
709 /*****************************************************************************
710 VCDTracks: initializes area information.
711 Before calling this track information should have been read in.
712 *****************************************************************************/
714 VCDTracks( input_thread_t * p_input )
716 thread_vcd_data_t * p_vcd;
719 p_vcd = (thread_vcd_data_t *) p_input->p_access_data;
721 #define area p_input->stream.pp_areas
723 /* We start area addressing for tracks at 1 since the default area 0
724 is reserved for segments */
726 for( i = 1 ; i < p_vcd->num_tracks ; i++ )
728 /* Tracks are Program Chains */
729 input_AddArea( p_input, i, i );
731 /* Absolute start byte offset and byte size */
732 area[i]->i_start = (off_t) p_vcd->p_sectors[i]
733 * (off_t)M2F2_SECTOR_SIZE;
734 area[i]->i_size = (off_t)(p_vcd->p_sectors[i+1] - p_vcd->p_sectors[i])
735 * (off_t)M2F2_SECTOR_SIZE;
737 /* Current entry being played in track */
740 /* i_plugin_data is used to store which entry point is the first
741 * of the track (area) */
742 area[i]->i_plugin_data = 0;
744 dbg_print( INPUT_DBG_MRL,
745 "area[%d] id: %d, i_start: %lld, i_size: %lld",
746 i, area[i]->i_id, area[i]->i_start, area[i]->i_size );
754 /*****************************************************************************
755 VCDLIDs: Reads the LIST IDs from the LOT.
756 *****************************************************************************/
758 VCDLIDs( input_thread_t * p_input )
760 thread_vcd_data_t *p_vcd = (thread_vcd_data_t *) p_input->p_access_data;
762 p_vcd->num_lids = vcdinfo_get_num_LIDs(p_vcd->vcd);
763 p_vcd->cur_lid = VCDINFO_INVALID_ENTRY;
765 if (vcdinfo_read_psd (p_vcd->vcd)) {
767 vcdinfo_visit_lot (p_vcd->vcd, false);
771 We need to change libvcdinfo to be more robust when there are
772 problems reading the extended PSD. Given that area-highlighting and
773 selection features in the extended PSD haven't been implemented,
774 it's best then to not try to read this at all.
776 if (vcdinfo_get_psd_x_size(p_vcd->vcd))
777 vcdinfo_visit_lot (p_vcd->vcd, true);
781 dbg_print( (INPUT_DBG_CALL|INPUT_DBG_MRL),
782 "num LIDs=%d", p_vcd->num_lids);
787 /*****************************************************************************
788 * VCDParse: parse command line
789 *****************************************************************************/
791 VCDParse( input_thread_t * p_input, /*out*/ vcdinfo_itemid_t * p_itemid,
792 /*out*/ bool *play_single_item )
794 thread_vcd_data_t *p_vcd = (thread_vcd_data_t *)p_input->p_access_data;
799 if ( config_GetInt( p_input, MODULE_STRING "-PBC" ) ) {
800 p_itemid->type=VCDINFO_ITEM_TYPE_LID;
802 *play_single_item=false;
804 p_itemid->type=VCDINFO_ITEM_TYPE_ENTRY;
809 /* On Win32 we want the VCD access plugin to be explicitly requested,
810 * we end up with lots of problems otherwise */
811 if( !p_input->psz_access || !*p_input->psz_access ) return NULL;
814 if( !p_input->psz_name )
819 psz_parser = psz_source = strdup( p_input->psz_name );
821 /* Parse input string :
822 * [device][@[type][title]] */
823 while( *psz_parser && *psz_parser != '@' )
828 if( *psz_parser == '@' )
830 /* Found the divide between the source name and the
831 type+entry number. */
838 switch(*psz_parser) {
840 p_itemid->type = VCDINFO_ITEM_TYPE_ENTRY;
842 *play_single_item = true;
845 p_itemid->type = VCDINFO_ITEM_TYPE_LID;
847 *play_single_item = false;
850 p_itemid->type = VCDINFO_ITEM_TYPE_SEGMENT;
852 *play_single_item = true;
855 p_itemid->type = VCDINFO_ITEM_TYPE_TRACK;
857 *play_single_item = true;
863 num = strtol( psz_parser, &psz_next, 10 );
864 if ( *psz_parser != '\0' && *psz_next == '\0')
870 *play_single_item = ( VCDINFO_ITEM_TYPE_LID == p_itemid->type );
876 /* No source specified, so figure it out. */
877 if( !p_input->psz_access ) return NULL;
879 psz_source = config_GetPsz( p_input, "vcd" );
881 if( !psz_source || 0==strlen(psz_source) ) {
882 /* Scan for a CD-ROM drive with a VCD in it. */
883 char **cd_drives = cdio_get_devices_with_cap(NULL,
884 (CDIO_FS_ANAL_SVCD|CDIO_FS_ANAL_CVD
885 |CDIO_FS_ANAL_VIDEOCD|CDIO_FS_UNKNOWN),
887 if (NULL == cd_drives) return NULL;
888 if (cd_drives[0] == NULL) {
889 cdio_free_device_list(cd_drives);
892 psz_source = strdup(cd_drives[0]);
893 cdio_free_device_list(cd_drives);
897 dbg_print( (INPUT_DBG_CALL|INPUT_DBG_MRL),
898 "source=%s entry=%d type=%d",
899 psz_source, p_itemid->num, p_itemid->type);
905 Set's start origin subsequent seeks/reads
908 VCDSetOrigin( input_thread_t *p_input, lsn_t origin_lsn,
909 lsn_t cur_lsn, lsn_t end_lsn, int cur_entry, track_t cur_track )
911 thread_vcd_data_t * p_vcd = (thread_vcd_data_t *) p_input->p_access_data;
913 p_vcd->origin_lsn = origin_lsn;
914 p_vcd->cur_lsn = cur_lsn;
915 p_vcd->end_lsn = end_lsn;
916 p_vcd->cur_track = cur_track;
917 p_vcd->play_item.num = cur_entry;
918 p_vcd->play_item.type = VCDINFO_ITEM_TYPE_ENTRY;
920 dbg_print( (INPUT_DBG_CALL|INPUT_DBG_LSN),
921 "origin: %d, cur_lsn: %d, end_lsn: %d, entry: %d, track: %d",
922 origin_lsn, cur_lsn, end_lsn, cur_entry, cur_track );
924 p_input->stream.p_selected_area->i_tell =
925 (off_t) (p_vcd->cur_lsn - p_vcd->origin_lsn) * (off_t)M2F2_SECTOR_SIZE;
927 VCDUpdateVar( p_input, cur_entry, VLC_VAR_SETVALUE,
929 p_vcd->play_item.type == VCDINFO_ITEM_TYPE_ENTRY ?
930 _("Entry") : _("Segment"),
931 "Setting entry/segment");
934 /*****************************************************************************
935 * vcd_Open: Opens a VCD device or file and returns an opaque handle
936 *****************************************************************************/
937 static vcdinfo_obj_t *
938 vcd_Open( vlc_object_t *p_this, const char *psz_dev )
940 vcdinfo_obj_t *p_vcdobj;
943 if( !psz_dev ) return NULL;
945 actual_dev=strdup(psz_dev);
946 if ( vcdinfo_open(&p_vcdobj, &actual_dev, DRIVER_UNKNOWN, NULL) !=
956 /****************************************************************************
957 * VCDReadSector: Read a sector (2324 bytes)
958 ****************************************************************************/
960 VCDReadSector( vlc_object_t *p_this, const vcdinfo_obj_t *p_vcd,
961 lsn_t cur_lsn, byte_t * p_buffer )
964 uint8_t subheader [8];
965 uint8_t data [M2F2_SECTOR_SIZE];
967 vcdsector_t vcd_sector;
969 if (cdio_read_mode2_sector(vcdinfo_get_cd_image(p_vcd),
970 &vcd_sector, cur_lsn, true)
973 msg_Warn( p_this, "Could not read LSN %d", cur_lsn );
977 memcpy (p_buffer, vcd_sector.data, M2F2_SECTOR_SIZE);
982 /****************************************************************************
983 Update the "varname" variable to i_num without triggering a callback.
984 ****************************************************************************/
986 VCDUpdateVar( input_thread_t *p_input, int i_num, int i_action,
987 const char *p_varname, char *p_label,
988 const char *p_debug_label)
992 if (NULL != p_vcd_input) {
993 thread_vcd_data_t *p_vcd = (thread_vcd_data_t *)p_vcd_input->p_access_data;
994 dbg_print( INPUT_DBG_PBC, "%s %d", p_debug_label, i_num );
998 text.psz_string = p_label;
999 var_Change( p_input, p_varname, VLC_VAR_SETTEXT, &text, NULL );
1001 var_Change( p_input, p_varname, i_action, &val, NULL );
1006 MetaInfoAddStr(input_thread_t *p_input, input_info_category_t *p_cat,
1007 playlist_t *p_playlist, char *title,
1010 thread_vcd_data_t *p_vcd = (thread_vcd_data_t *) p_input->p_access_data;
1012 dbg_print( INPUT_DBG_META, "field: %s: %s\n", title, str);
1013 input_AddInfo( p_cat, title, "%s", str );
1014 playlist_AddInfo( p_playlist, -1, p_cat->psz_name, title,
1021 MetaInfoAddNum(input_thread_t *p_input, input_info_category_t *p_cat,
1022 playlist_t *p_playlist, char *title, int num)
1024 thread_vcd_data_t *p_vcd = (thread_vcd_data_t *) p_input->p_access_data;
1025 dbg_print( INPUT_DBG_META, "field %s: %d\n", title, num);
1026 input_AddInfo( p_cat, title, "%d", num );
1027 playlist_AddInfo( p_playlist, -1, p_cat->psz_name, title,
1031 #define addstr(title, str) \
1032 MetaInfoAddStr( p_input, p_cat, p_playlist, title, str );
1034 #define addnum(title, num) \
1035 MetaInfoAddNum( p_input, p_cat, p_playlist, title, num );
1037 static void InformationCreate( input_thread_t *p_input )
1039 thread_vcd_data_t *p_vcd = (thread_vcd_data_t *) p_input->p_access_data;
1040 unsigned int i_nb = vcdinfo_get_num_entries(p_vcd->vcd);
1041 unsigned int last_entry = 0;
1042 input_info_category_t *p_cat;
1044 playlist_t *p_playlist = vlc_object_find( p_input, VLC_OBJECT_PLAYLIST,
1047 p_cat = input_InfoCategory( p_input, "General" );
1049 addstr( _("VCD Format"), vcdinfo_get_format_version_str(p_vcd->vcd) );
1050 addstr( _("Album"), vcdinfo_get_album_id(p_vcd->vcd));
1051 addstr( _("Application"), vcdinfo_get_application_id(p_vcd->vcd) );
1052 addstr( _("Preparer"), vcdinfo_get_preparer_id(p_vcd->vcd) );
1053 addnum( _("Vol #"), vcdinfo_get_volume_num(p_vcd->vcd) );
1054 addnum( _("Vol max #"), vcdinfo_get_volume_count(p_vcd->vcd) );
1055 addstr( _("Volume Set"), vcdinfo_get_volumeset_id(p_vcd->vcd) );
1056 addstr( _("Volume"), vcdinfo_get_volume_id(p_vcd->vcd) );
1057 addstr( _("Publisher"), vcdinfo_get_publisher_id(p_vcd->vcd) );
1058 addstr( _("System Id"), vcdinfo_get_system_id(p_vcd->vcd) );
1059 addnum( "LIDs", vcdinfo_get_num_LIDs(p_vcd->vcd) );
1060 addnum( _("Entries"), vcdinfo_get_num_entries(p_vcd->vcd) );
1061 addnum( _("Segments"), vcdinfo_get_num_segments(p_vcd->vcd) );
1062 addnum( _("Tracks"), vcdinfo_get_num_tracks(p_vcd->vcd) );
1064 /* Spit out track information. Could also include MSF info.
1067 #define TITLE_MAX 30
1068 for( i_track = 1 ; i_track < p_vcd->num_tracks ; i_track++ ) {
1069 char track_str[TITLE_MAX];
1070 unsigned int audio_type = vcdinfo_get_track_audio_type(p_vcd->vcd,
1072 snprintf(track_str, TITLE_MAX, "%s%02d", _("Track"), i_track);
1073 p_cat = input_InfoCategory( p_input, track_str );
1076 addnum(_("Audio Channels"),
1077 vcdinfo_audio_type_num_channels(p_vcd->vcd, audio_type) );
1080 addnum(_("First Entry Point"), last_entry );
1081 for ( ; last_entry < i_nb
1082 && vcdinfo_get_track(p_vcd->vcd, last_entry) == i_track;
1084 addnum(_("Last Entry Point"), last_entry-1 );
1088 #define add_format_str_info(val) \
1090 const char *str = val; \
1092 if (val != NULL) { \
1095 strncat(tp, str, TEMP_STR_LEN-(tp-temp_str)); \
1098 saw_control_prefix = false; \
1102 #define add_format_num_info(val, fmt) \
1106 sprintf(num_str, fmt, val); \
1107 len=strlen(num_str); \
1109 strncat(tp, num_str, TEMP_STR_LEN-(tp-temp_str)); \
1112 saw_control_prefix = false; \
1116 Take a format string and expand escape sequences, that is sequences that
1117 begin with %, with information from the current VCD.
1118 The expanded string is returned. Here is a list of escape sequences:
1120 %A : The album information
1121 %C : The VCD volume count - the number of CD's in the collection.
1122 %c : The VCD volume num - the number of the CD in the collection.
1123 %F : The VCD Format, e.g. VCD 1.0, VCD 1.1, VCD 2.0, or SVCD
1124 %I : The current entry/segment/playback type, e.g. ENTRY, TRACK, SEGMENT...
1125 %L : The playlist ID prefixed with " LID" if it exists
1127 %N : The current number of the %I - a decimal number
1128 %P : The publisher ID
1129 %p : The preparer ID
1130 %S : If we are in a segment (menu), the kind of segment
1131 %T : The track number
1132 %V : The volume set ID
1134 A number between 1 and the volume count.
1138 VCDFormatStr(const input_thread_t *p_input, thread_vcd_data_t *p_vcd,
1139 const char format_str[], const char *mrl,
1140 const vcdinfo_itemid_t *itemid)
1142 #define TEMP_STR_SIZE 256
1143 #define TEMP_STR_LEN (TEMP_STR_SIZE-1)
1144 static char temp_str[TEMP_STR_SIZE];
1146 char * tp = temp_str;
1147 bool saw_control_prefix = false;
1148 size_t format_len = strlen(format_str);
1150 bzero(temp_str, TEMP_STR_SIZE);
1152 for (i=0; i<format_len; i++) {
1154 if (!saw_control_prefix && format_str[i] != '%') {
1155 *tp++ = format_str[i];
1156 saw_control_prefix = false;
1160 switch(format_str[i]) {
1162 if (saw_control_prefix) {
1165 saw_control_prefix = !saw_control_prefix;
1168 add_format_str_info(vcdinfo_strip_trail(vcdinfo_get_album_id(p_vcd->vcd),
1173 add_format_num_info(vcdinfo_get_volume_num(p_vcd->vcd), "%d");
1177 add_format_num_info(vcdinfo_get_volume_count(p_vcd->vcd), "%d");
1181 add_format_str_info(vcdinfo_get_format_version_str(p_vcd->vcd));
1186 switch (itemid->type) {
1187 case VCDINFO_ITEM_TYPE_TRACK:
1188 strncat(tp, _("Track"), TEMP_STR_LEN-(tp-temp_str));
1189 tp += strlen(_("Track"));
1191 case VCDINFO_ITEM_TYPE_ENTRY:
1192 strncat(tp, _("Entry"), TEMP_STR_LEN-(tp-temp_str));
1193 tp += strlen(_("Entry"));
1195 case VCDINFO_ITEM_TYPE_SEGMENT:
1196 strncat(tp, _("Segment"), TEMP_STR_LEN-(tp-temp_str));
1197 tp += strlen(_("Segment"));
1199 case VCDINFO_ITEM_TYPE_LID:
1200 strncat(tp, _("List ID"), TEMP_STR_LEN-(tp-temp_str));
1201 tp += strlen(_("List ID"));
1203 case VCDINFO_ITEM_TYPE_SPAREID2:
1204 strncat(tp, _("Navigation"), TEMP_STR_LEN-(tp-temp_str));
1205 tp += strlen(_("Navigation"));
1211 saw_control_prefix = false;
1216 if (vcdplayer_pbc_is_on(p_vcd)) {
1218 sprintf(num_str, "%s %d", _("List ID"), p_vcd->cur_lid);
1219 strncat(tp, num_str, TEMP_STR_LEN-(tp-temp_str));
1220 tp += strlen(num_str);
1222 saw_control_prefix = false;
1226 add_format_str_info(mrl);
1230 add_format_num_info(itemid->num, "%d");
1234 add_format_str_info(vcdinfo_get_preparer_id(p_vcd->vcd));
1238 add_format_str_info(vcdinfo_get_publisher_id(p_vcd->vcd));
1242 if ( VCDINFO_ITEM_TYPE_SEGMENT==itemid->type ) {
1243 char seg_type_str[10];
1245 sprintf(seg_type_str, " %s",
1246 vcdinfo_video_type2str(p_vcd->vcd, itemid->num));
1247 strncat(tp, seg_type_str, TEMP_STR_LEN-(tp-temp_str));
1248 tp += strlen(seg_type_str);
1250 saw_control_prefix = false;
1254 add_format_num_info(p_vcd->cur_track, "%d");
1258 add_format_str_info(vcdinfo_get_volumeset_id(p_vcd->vcd));
1262 add_format_str_info(vcdinfo_get_volume_id(p_vcd->vcd));
1267 *tp++ = format_str[i];
1268 saw_control_prefix = false;
1271 return strdup(temp_str);
1275 VCDCreatePlayListItem(const input_thread_t *p_input,
1276 thread_vcd_data_t *p_vcd,
1277 playlist_t *p_playlist,
1278 const vcdinfo_itemid_t *itemid,
1279 char *psz_mrl, int psz_mrl_max,
1280 const char *psz_source, int playlist_operation,
1287 switch(itemid->type) {
1288 case VCDINFO_ITEM_TYPE_TRACK:
1291 case VCDINFO_ITEM_TYPE_SEGMENT:
1294 case VCDINFO_ITEM_TYPE_LID:
1297 case VCDINFO_ITEM_TYPE_ENTRY:
1305 snprintf(psz_mrl, psz_mrl_max, "%s%s@%c%u", VCD_MRL_PREFIX, psz_source,
1306 c_type, itemid->num);
1309 VCDFormatStr( p_input, p_vcd,
1310 config_GetPsz( p_input, MODULE_STRING "-title-format" ),
1313 playlist_Add( p_playlist, psz_mrl, p_title, playlist_operation, i_pos );
1316 VCDFormatStr( p_input, p_vcd,
1317 config_GetPsz( p_input, MODULE_STRING "-author-format" ),
1320 if( i_pos == PLAYLIST_END ) i_pos = p_playlist->i_size - 1;
1321 playlist_AddInfo(p_playlist, i_pos, _("General"), _("Author"), "%s",
1326 VCDFixupPlayList( input_thread_t *p_input, thread_vcd_data_t *p_vcd,
1327 const char *psz_source, vcdinfo_itemid_t *itemid,
1328 bool play_single_item )
1331 playlist_t * p_playlist;
1333 unsigned int psz_mrl_max = strlen(VCD_MRL_PREFIX) + strlen(psz_source) +
1334 strlen("@T") + strlen("100") + 1;
1336 psz_mrl = malloc( psz_mrl_max );
1338 if( psz_mrl == NULL )
1340 msg_Warn( p_input, "out of memory" );
1344 p_playlist = (playlist_t *) vlc_object_find( p_input, VLC_OBJECT_PLAYLIST,
1348 msg_Warn( p_input, "can't find playlist" );
1353 InformationCreate( p_input );
1355 if ( play_single_item )
1357 /* May fill out more information when the playlist user interface becomes
1360 VCDCreatePlayListItem(p_input, p_vcd, p_playlist, itemid,
1361 psz_mrl, psz_mrl_max, psz_source, PLAYLIST_REPLACE,
1362 p_playlist->i_index);
1367 vcdinfo_itemid_t list_itemid;
1368 list_itemid.type=VCDINFO_ITEM_TYPE_ENTRY;
1370 playlist_Delete( p_playlist, p_playlist->i_index);
1372 for( i = 0 ; i < p_vcd->num_entries ; i++ )
1375 VCDCreatePlayListItem(p_input, p_vcd, p_playlist, &list_itemid,
1376 psz_mrl, psz_mrl_max, psz_source,
1377 PLAYLIST_APPEND, PLAYLIST_END);
1380 playlist_Command( p_playlist, PLAYLIST_GOTO, 0 );
1384 vlc_object_release( p_playlist );
1389 /*****************************************************************************
1391 *****************************************************************************/
1393 E_(DebugCallback) ( vlc_object_t *p_this, const char *psz_name,
1394 vlc_value_t oldval, vlc_value_t val, void *p_data )
1396 thread_vcd_data_t *p_vcd;
1398 if (NULL == p_vcd_input) return VLC_EGENERIC;
1400 p_vcd = (thread_vcd_data_t *)p_vcd_input->p_access_data;
1402 if (p_vcd->i_debug & (INPUT_DBG_CALL|INPUT_DBG_EXT)) {
1403 msg_Dbg( p_vcd_input, "Old debug (x%0x) %d, new debug (x%0x) %d",
1404 p_vcd->i_debug, p_vcd->i_debug, val.i_int, val.i_int);
1406 p_vcd->i_debug = val.i_int;
1410 /*****************************************************************************
1412 read in meta-information about VCD: the number of tracks, segments,
1413 entries, size and starting information. Then set up state variables so
1414 that we read/seek starting at the location specified.
1416 On success we return VLC_SUCCESS, on memory exhausted VLC_ENOMEM,
1417 and VLC_EGENERIC for some other error.
1418 *****************************************************************************/
1420 E_(Open) ( vlc_object_t *p_this )
1422 input_thread_t * p_input = (input_thread_t *)p_this;
1423 thread_vcd_data_t * p_vcd;
1425 vcdinfo_itemid_t itemid;
1427 bool play_single_item = false;
1429 p_input->pf_read = VCDRead;
1430 p_input->pf_seek = VCDSeek;
1431 p_input->pf_set_area = VCDSetArea;
1432 p_input->pf_set_program = VCDSetProgram;
1434 p_vcd = malloc( sizeof(thread_vcd_data_t) );
1438 LOG_ERR ("out of memory" );
1442 p_input->p_access_data = (void *)p_vcd;
1443 p_vcd->i_debug = config_GetInt( p_this, MODULE_STRING "-debug" );
1445 /* Set where to log errors messages from libcdio. */
1446 p_vcd_input = (input_thread_t *)p_this;
1447 cdio_log_set_handler ( cdio_log_handler );
1448 vcd_log_set_handler ( vcd_log_handler );
1450 psz_source = VCDParse( p_input, &itemid, &play_single_item );
1452 if ( NULL == psz_source )
1455 return( VLC_EGENERIC );
1458 dbg_print( (INPUT_DBG_CALL|INPUT_DBG_EXT), "source: %s: mrl: %s",
1459 psz_source, p_input->psz_name );
1461 p_vcd->p_segments = NULL;
1462 p_vcd->p_entries = NULL;
1465 p_input->i_mtu = VCD_DATA_ONCE;
1467 vlc_mutex_lock( &p_input->stream.stream_lock );
1469 /* If we are here we can control the pace... */
1470 p_input->stream.b_pace_control = 1;
1472 p_input->stream.b_seekable = 1;
1473 p_input->stream.p_selected_area->i_size = 0;
1474 p_input->stream.p_selected_area->i_tell = 0;
1476 vlc_mutex_unlock( &p_input->stream.stream_lock );
1478 if( !(p_vcd->vcd = vcd_Open( p_this, psz_source )) )
1480 msg_Warn( p_input, "could not open %s", psz_source );
1484 p_vcd->b_svd= vcdinfo_get_tracksSVD(p_vcd->vcd);;
1486 /* Get track information. */
1487 p_vcd->num_tracks = ioctl_GetTracksMap( VLC_OBJECT(p_input),
1488 vcdinfo_get_cd_image(p_vcd->vcd),
1489 &p_vcd->p_sectors );
1490 if( p_vcd->num_tracks < 0 )
1491 LOG_ERR ("unable to count tracks" );
1492 else if( p_vcd->num_tracks <= 1 )
1493 LOG_ERR ("no movie tracks found" );
1494 if( p_vcd->num_tracks <= 1)
1496 vcdinfo_close( p_vcd->vcd );
1500 /* Set stream and area data */
1501 vlc_mutex_lock( &p_input->stream.stream_lock );
1503 /* Initialize ES structures */
1504 input_InitStream( p_input, sizeof( stream_ps_data_t ) );
1506 /* disc input method */
1507 p_input->stream.i_method = INPUT_METHOD_VCD;
1509 p_input->stream.i_area_nb = 1;
1512 /* Initialize segment information. */
1513 VCDSegments( p_input );
1515 /* Initialize track area information. */
1516 VCDTracks( p_input );
1518 if( VCDEntryPoints( p_input ) < 0 )
1520 msg_Warn( p_input, "could not read entry points, will not use them" );
1521 p_vcd->b_valid_ep = false;
1524 if( VCDLIDs( p_input ) < 0 )
1526 msg_Warn( p_input, "could not read entry LIDs" );
1529 b_play_ok = (VLC_SUCCESS == VCDPlay( p_input, itemid ));
1531 vlc_mutex_unlock( &p_input->stream.stream_lock );
1533 if ( ! b_play_ok ) {
1534 vcdinfo_close( p_vcd->vcd );
1538 if( !p_input->psz_demux || !*p_input->psz_demux )
1541 p_input->psz_demux = "vcdx";
1543 p_input->psz_demux = "ps";
1547 p_vcd->p_intf = intf_Create( p_input, "vcdx" );
1548 p_vcd->p_intf->b_block = VLC_FALSE;
1549 intf_RunThread( p_vcd->p_intf );
1551 if (play_single_item)
1552 VCDFixupPlayList( p_input, p_vcd, psz_source, &itemid, play_single_item );
1560 return VLC_EGENERIC;
1563 /*****************************************************************************
1564 * Close: closes VCD releasing allocated memory.
1565 *****************************************************************************/
1567 E_(Close) ( vlc_object_t *p_this )
1569 input_thread_t * p_input = (input_thread_t *)p_this;
1570 thread_vcd_data_t *p_vcd = (thread_vcd_data_t *)p_input->p_access_data;
1572 dbg_print( (INPUT_DBG_CALL|INPUT_DBG_EXT), "VCDClose" );
1573 vcdinfo_close( p_vcd->vcd );
1575 free( p_vcd->p_entries );
1576 free( p_vcd->p_segments );
1578 /* For reasons that are a mystery to me we don't have to deal with
1579 stopping, and destroying the p_vcd->p_intf thread. And if we do
1580 it causes problems upstream.
1582 if( p_vcd->p_intf != NULL )
1584 p_vcd->p_intf = NULL;
1588 p_input->p_access_data = NULL;