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.19 2004/02/19 02:05:12 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];
968 vcdsector_t vcd_sector;
970 if (cdio_read_mode2_sector(vcdinfo_get_cd_image(p_vcd),
971 &vcd_sector, cur_lsn, true)
974 msg_Warn( p_this, "Could not read LSN %d", cur_lsn );
978 memcpy (p_buffer, vcd_sector.data, M2F2_SECTOR_SIZE);
983 /****************************************************************************
984 Update the "varname" variable to i_num without triggering a callback.
985 ****************************************************************************/
987 VCDUpdateVar( input_thread_t *p_input, int i_num, int i_action,
988 const char *p_varname, char *p_label,
989 const char *p_debug_label)
993 if (NULL != p_vcd_input) {
994 thread_vcd_data_t *p_vcd = (thread_vcd_data_t *)p_vcd_input->p_access_data;
995 dbg_print( INPUT_DBG_PBC, "%s %d", p_debug_label, i_num );
999 text.psz_string = p_label;
1000 var_Change( p_input, p_varname, VLC_VAR_SETTEXT, &text, NULL );
1002 var_Change( p_input, p_varname, i_action, &val, NULL );
1007 MetaInfoAddStr(input_thread_t *p_input, input_info_category_t *p_cat,
1008 playlist_t *p_playlist, char *title,
1011 thread_vcd_data_t *p_vcd = (thread_vcd_data_t *) p_input->p_access_data;
1012 playlist_item_t *p_item;
1014 dbg_print( INPUT_DBG_META, "field: %s: %s\n", title, str);
1015 input_AddInfo( p_cat, title, "%s", str );
1017 vlc_mutex_lock( &p_playlist->object_lock );
1018 p_item = playlist_ItemGetByPos( p_playlist, -1 );
1019 vlc_mutex_unlock( &p_playlist->object_lock );
1021 vlc_mutex_lock( &p_item->lock );
1022 playlist_ItemAddInfo( p_item, p_cat->psz_name, title,
1024 vlc_mutex_unlock( &p_item->lock );
1030 MetaInfoAddNum(input_thread_t *p_input, input_info_category_t *p_cat,
1031 playlist_t *p_playlist, char *title, int num)
1033 thread_vcd_data_t *p_vcd = (thread_vcd_data_t *) p_input->p_access_data;
1034 playlist_item_t *p_item;
1036 vlc_mutex_lock( &p_playlist->object_lock );
1037 p_item = playlist_ItemGetByPos( p_playlist, -1 );
1038 vlc_mutex_unlock( &p_playlist->object_lock );
1040 dbg_print( INPUT_DBG_META, "field %s: %d\n", title, num);
1041 input_AddInfo( p_cat, title, "%d", num );
1043 vlc_mutex_lock( &p_item->lock );
1044 playlist_ItemAddInfo( p_item , p_cat->psz_name, title, "%d",num );
1045 vlc_mutex_unlock( &p_item->lock );
1048 #define addstr(title, str) \
1049 MetaInfoAddStr( p_input, p_cat, p_playlist, title, str );
1051 #define addnum(title, num) \
1052 MetaInfoAddNum( p_input, p_cat, p_playlist, title, num );
1054 static void InformationCreate( input_thread_t *p_input )
1056 thread_vcd_data_t *p_vcd = (thread_vcd_data_t *) p_input->p_access_data;
1057 unsigned int i_nb = vcdinfo_get_num_entries(p_vcd->vcd);
1058 unsigned int last_entry = 0;
1059 input_info_category_t *p_cat;
1061 playlist_t *p_playlist = vlc_object_find( p_input, VLC_OBJECT_PLAYLIST,
1064 p_cat = input_InfoCategory( p_input, "General" );
1066 addstr( _("VCD Format"), vcdinfo_get_format_version_str(p_vcd->vcd) );
1067 addstr( _("Album"), vcdinfo_get_album_id(p_vcd->vcd));
1068 addstr( _("Application"), vcdinfo_get_application_id(p_vcd->vcd) );
1069 addstr( _("Preparer"), vcdinfo_get_preparer_id(p_vcd->vcd) );
1070 addnum( _("Vol #"), vcdinfo_get_volume_num(p_vcd->vcd) );
1071 addnum( _("Vol max #"), vcdinfo_get_volume_count(p_vcd->vcd) );
1072 addstr( _("Volume Set"), vcdinfo_get_volumeset_id(p_vcd->vcd) );
1073 addstr( _("Volume"), vcdinfo_get_volume_id(p_vcd->vcd) );
1074 addstr( _("Publisher"), vcdinfo_get_publisher_id(p_vcd->vcd) );
1075 addstr( _("System Id"), vcdinfo_get_system_id(p_vcd->vcd) );
1076 addnum( "LIDs", vcdinfo_get_num_LIDs(p_vcd->vcd) );
1077 addnum( _("Entries"), vcdinfo_get_num_entries(p_vcd->vcd) );
1078 addnum( _("Segments"), vcdinfo_get_num_segments(p_vcd->vcd) );
1079 addnum( _("Tracks"), vcdinfo_get_num_tracks(p_vcd->vcd) );
1081 /* Spit out track information. Could also include MSF info.
1084 #define TITLE_MAX 30
1085 for( i_track = 1 ; i_track < p_vcd->num_tracks ; i_track++ ) {
1086 char track_str[TITLE_MAX];
1087 unsigned int audio_type = vcdinfo_get_track_audio_type(p_vcd->vcd,
1089 snprintf(track_str, TITLE_MAX, "%s%02d", _("Track"), i_track);
1090 p_cat = input_InfoCategory( p_input, track_str );
1093 addnum(_("Audio Channels"),
1094 vcdinfo_audio_type_num_channels(p_vcd->vcd, audio_type) );
1097 addnum(_("First Entry Point"), last_entry );
1098 for ( ; last_entry < i_nb
1099 && vcdinfo_get_track(p_vcd->vcd, last_entry) == i_track;
1101 addnum(_("Last Entry Point"), last_entry-1 );
1105 #define add_format_str_info(val) \
1107 const char *str = val; \
1109 if (val != NULL) { \
1112 strncat(tp, str, TEMP_STR_LEN-(tp-temp_str)); \
1115 saw_control_prefix = false; \
1119 #define add_format_num_info(val, fmt) \
1123 sprintf(num_str, fmt, val); \
1124 len=strlen(num_str); \
1126 strncat(tp, num_str, TEMP_STR_LEN-(tp-temp_str)); \
1129 saw_control_prefix = false; \
1133 Take a format string and expand escape sequences, that is sequences that
1134 begin with %, with information from the current VCD.
1135 The expanded string is returned. Here is a list of escape sequences:
1137 %A : The album information
1138 %C : The VCD volume count - the number of CD's in the collection.
1139 %c : The VCD volume num - the number of the CD in the collection.
1140 %F : The VCD Format, e.g. VCD 1.0, VCD 1.1, VCD 2.0, or SVCD
1141 %I : The current entry/segment/playback type, e.g. ENTRY, TRACK, SEGMENT...
1142 %L : The playlist ID prefixed with " LID" if it exists
1144 %N : The current number of the %I - a decimal number
1145 %P : The publisher ID
1146 %p : The preparer ID
1147 %S : If we are in a segment (menu), the kind of segment
1148 %T : The track number
1149 %V : The volume set ID
1151 A number between 1 and the volume count.
1155 VCDFormatStr(const input_thread_t *p_input, thread_vcd_data_t *p_vcd,
1156 const char format_str[], const char *mrl,
1157 const vcdinfo_itemid_t *itemid)
1159 #define TEMP_STR_SIZE 256
1160 #define TEMP_STR_LEN (TEMP_STR_SIZE-1)
1161 static char temp_str[TEMP_STR_SIZE];
1163 char * tp = temp_str;
1164 bool saw_control_prefix = false;
1165 size_t format_len = strlen(format_str);
1167 bzero(temp_str, TEMP_STR_SIZE);
1169 for (i=0; i<format_len; i++) {
1171 if (!saw_control_prefix && format_str[i] != '%') {
1172 *tp++ = format_str[i];
1173 saw_control_prefix = false;
1177 switch(format_str[i]) {
1179 if (saw_control_prefix) {
1182 saw_control_prefix = !saw_control_prefix;
1185 add_format_str_info(vcdinfo_strip_trail(vcdinfo_get_album_id(p_vcd->vcd),
1190 add_format_num_info(vcdinfo_get_volume_num(p_vcd->vcd), "%d");
1194 add_format_num_info(vcdinfo_get_volume_count(p_vcd->vcd), "%d");
1198 add_format_str_info(vcdinfo_get_format_version_str(p_vcd->vcd));
1203 switch (itemid->type) {
1204 case VCDINFO_ITEM_TYPE_TRACK:
1205 strncat(tp, _("Track"), TEMP_STR_LEN-(tp-temp_str));
1206 tp += strlen(_("Track"));
1208 case VCDINFO_ITEM_TYPE_ENTRY:
1209 strncat(tp, _("Entry"), TEMP_STR_LEN-(tp-temp_str));
1210 tp += strlen(_("Entry"));
1212 case VCDINFO_ITEM_TYPE_SEGMENT:
1213 strncat(tp, _("Segment"), TEMP_STR_LEN-(tp-temp_str));
1214 tp += strlen(_("Segment"));
1216 case VCDINFO_ITEM_TYPE_LID:
1217 strncat(tp, _("List ID"), TEMP_STR_LEN-(tp-temp_str));
1218 tp += strlen(_("List ID"));
1220 case VCDINFO_ITEM_TYPE_SPAREID2:
1221 strncat(tp, _("Navigation"), TEMP_STR_LEN-(tp-temp_str));
1222 tp += strlen(_("Navigation"));
1228 saw_control_prefix = false;
1233 if (vcdplayer_pbc_is_on(p_vcd)) {
1235 sprintf(num_str, "%s %d", _("List ID"), p_vcd->cur_lid);
1236 strncat(tp, num_str, TEMP_STR_LEN-(tp-temp_str));
1237 tp += strlen(num_str);
1239 saw_control_prefix = false;
1243 add_format_str_info(mrl);
1247 add_format_num_info(itemid->num, "%d");
1251 add_format_str_info(vcdinfo_get_preparer_id(p_vcd->vcd));
1255 add_format_str_info(vcdinfo_get_publisher_id(p_vcd->vcd));
1259 if ( VCDINFO_ITEM_TYPE_SEGMENT==itemid->type ) {
1260 char seg_type_str[10];
1262 sprintf(seg_type_str, " %s",
1263 vcdinfo_video_type2str(p_vcd->vcd, itemid->num));
1264 strncat(tp, seg_type_str, TEMP_STR_LEN-(tp-temp_str));
1265 tp += strlen(seg_type_str);
1267 saw_control_prefix = false;
1271 add_format_num_info(p_vcd->cur_track, "%d");
1275 add_format_str_info(vcdinfo_get_volumeset_id(p_vcd->vcd));
1279 add_format_str_info(vcdinfo_get_volume_id(p_vcd->vcd));
1284 *tp++ = format_str[i];
1285 saw_control_prefix = false;
1288 return strdup(temp_str);
1292 VCDCreatePlayListItem(const input_thread_t *p_input,
1293 thread_vcd_data_t *p_vcd,
1294 playlist_t *p_playlist,
1295 const vcdinfo_itemid_t *itemid,
1296 char *psz_mrl, int psz_mrl_max,
1297 const char *psz_source, int playlist_operation,
1304 switch(itemid->type) {
1305 case VCDINFO_ITEM_TYPE_TRACK:
1308 case VCDINFO_ITEM_TYPE_SEGMENT:
1311 case VCDINFO_ITEM_TYPE_LID:
1314 case VCDINFO_ITEM_TYPE_ENTRY:
1322 snprintf(psz_mrl, psz_mrl_max, "%s%s@%c%u", VCD_MRL_PREFIX, psz_source,
1323 c_type, itemid->num);
1326 VCDFormatStr( p_input, p_vcd,
1327 config_GetPsz( p_input, MODULE_STRING "-title-format" ),
1330 playlist_Add( p_playlist, psz_mrl, p_title, playlist_operation, i_pos );
1333 VCDFormatStr( p_input, p_vcd,
1334 config_GetPsz( p_input, MODULE_STRING "-author-format" ),
1337 if( i_pos == PLAYLIST_END ) i_pos = p_playlist->i_size - 1;
1338 playlist_AddInfo(p_playlist, i_pos, _("General"), _("Author"), "%s",
1343 VCDFixupPlayList( input_thread_t *p_input, thread_vcd_data_t *p_vcd,
1344 const char *psz_source, vcdinfo_itemid_t *itemid,
1345 bool play_single_item )
1348 playlist_t * p_playlist;
1350 unsigned int psz_mrl_max = strlen(VCD_MRL_PREFIX) + strlen(psz_source) +
1351 strlen("@T") + strlen("100") + 1;
1353 psz_mrl = malloc( psz_mrl_max );
1355 if( psz_mrl == NULL )
1357 msg_Warn( p_input, "out of memory" );
1361 p_playlist = (playlist_t *) vlc_object_find( p_input, VLC_OBJECT_PLAYLIST,
1365 msg_Warn( p_input, "can't find playlist" );
1370 InformationCreate( p_input );
1372 if ( play_single_item )
1374 /* May fill out more information when the playlist user interface becomes
1377 VCDCreatePlayListItem(p_input, p_vcd, p_playlist, itemid,
1378 psz_mrl, psz_mrl_max, psz_source, PLAYLIST_REPLACE,
1379 p_playlist->i_index);
1384 vcdinfo_itemid_t list_itemid;
1385 list_itemid.type=VCDINFO_ITEM_TYPE_ENTRY;
1387 playlist_Delete( p_playlist, p_playlist->i_index);
1389 for( i = 0 ; i < p_vcd->num_entries ; i++ )
1392 VCDCreatePlayListItem(p_input, p_vcd, p_playlist, &list_itemid,
1393 psz_mrl, psz_mrl_max, psz_source,
1394 PLAYLIST_APPEND, PLAYLIST_END);
1397 playlist_Command( p_playlist, PLAYLIST_GOTO, 0 );
1401 vlc_object_release( p_playlist );
1406 /*****************************************************************************
1408 *****************************************************************************/
1410 E_(DebugCallback) ( vlc_object_t *p_this, const char *psz_name,
1411 vlc_value_t oldval, vlc_value_t val, void *p_data )
1413 thread_vcd_data_t *p_vcd;
1415 if (NULL == p_vcd_input) return VLC_EGENERIC;
1417 p_vcd = (thread_vcd_data_t *)p_vcd_input->p_access_data;
1419 if (p_vcd->i_debug & (INPUT_DBG_CALL|INPUT_DBG_EXT)) {
1420 msg_Dbg( p_vcd_input, "Old debug (x%0x) %d, new debug (x%0x) %d",
1421 p_vcd->i_debug, p_vcd->i_debug, val.i_int, val.i_int);
1423 p_vcd->i_debug = val.i_int;
1427 /*****************************************************************************
1429 read in meta-information about VCD: the number of tracks, segments,
1430 entries, size and starting information. Then set up state variables so
1431 that we read/seek starting at the location specified.
1433 On success we return VLC_SUCCESS, on memory exhausted VLC_ENOMEM,
1434 and VLC_EGENERIC for some other error.
1435 *****************************************************************************/
1437 E_(Open) ( vlc_object_t *p_this )
1439 input_thread_t * p_input = (input_thread_t *)p_this;
1440 thread_vcd_data_t * p_vcd;
1442 vcdinfo_itemid_t itemid;
1444 bool play_single_item = false;
1446 p_input->pf_read = VCDRead;
1447 p_input->pf_seek = VCDSeek;
1448 p_input->pf_set_area = VCDSetArea;
1449 p_input->pf_set_program = VCDSetProgram;
1451 p_vcd = malloc( sizeof(thread_vcd_data_t) );
1455 LOG_ERR ("out of memory" );
1459 p_input->p_access_data = (void *)p_vcd;
1460 p_vcd->i_debug = config_GetInt( p_this, MODULE_STRING "-debug" );
1462 /* Set where to log errors messages from libcdio. */
1463 p_vcd_input = (input_thread_t *)p_this;
1464 cdio_log_set_handler ( cdio_log_handler );
1465 vcd_log_set_handler ( vcd_log_handler );
1467 psz_source = VCDParse( p_input, &itemid, &play_single_item );
1469 if ( NULL == psz_source )
1472 return( VLC_EGENERIC );
1475 dbg_print( (INPUT_DBG_CALL|INPUT_DBG_EXT), "source: %s: mrl: %s",
1476 psz_source, p_input->psz_name );
1478 p_vcd->p_segments = NULL;
1479 p_vcd->p_entries = NULL;
1482 p_input->i_mtu = VCD_DATA_ONCE;
1484 vlc_mutex_lock( &p_input->stream.stream_lock );
1486 /* If we are here we can control the pace... */
1487 p_input->stream.b_pace_control = 1;
1489 p_input->stream.b_seekable = 1;
1490 p_input->stream.p_selected_area->i_size = 0;
1491 p_input->stream.p_selected_area->i_tell = 0;
1493 vlc_mutex_unlock( &p_input->stream.stream_lock );
1495 if( !(p_vcd->vcd = vcd_Open( p_this, psz_source )) )
1497 msg_Warn( p_input, "could not open %s", psz_source );
1501 p_vcd->b_svd= vcdinfo_get_tracksSVD(p_vcd->vcd);;
1503 /* Get track information. */
1504 p_vcd->num_tracks = ioctl_GetTracksMap( VLC_OBJECT(p_input),
1505 vcdinfo_get_cd_image(p_vcd->vcd),
1506 &p_vcd->p_sectors );
1507 if( p_vcd->num_tracks < 0 )
1508 LOG_ERR ("unable to count tracks" );
1509 else if( p_vcd->num_tracks <= 1 )
1510 LOG_ERR ("no movie tracks found" );
1511 if( p_vcd->num_tracks <= 1)
1513 vcdinfo_close( p_vcd->vcd );
1517 /* Set stream and area data */
1518 vlc_mutex_lock( &p_input->stream.stream_lock );
1520 /* Initialize ES structures */
1521 input_InitStream( p_input, sizeof( stream_ps_data_t ) );
1523 /* disc input method */
1524 p_input->stream.i_method = INPUT_METHOD_VCD;
1526 p_input->stream.i_area_nb = 1;
1529 /* Initialize segment information. */
1530 VCDSegments( p_input );
1532 /* Initialize track area information. */
1533 VCDTracks( p_input );
1535 if( VCDEntryPoints( p_input ) < 0 )
1537 msg_Warn( p_input, "could not read entry points, will not use them" );
1538 p_vcd->b_valid_ep = false;
1541 if( VCDLIDs( p_input ) < 0 )
1543 msg_Warn( p_input, "could not read entry LIDs" );
1546 b_play_ok = (VLC_SUCCESS == VCDPlay( p_input, itemid ));
1548 vlc_mutex_unlock( &p_input->stream.stream_lock );
1550 if ( ! b_play_ok ) {
1551 vcdinfo_close( p_vcd->vcd );
1555 if( !p_input->psz_demux || !*p_input->psz_demux )
1558 p_input->psz_demux = "vcdx";
1560 p_input->psz_demux = "ps";
1564 p_vcd->p_intf = intf_Create( p_input, "vcdx" );
1565 p_vcd->p_intf->b_block = VLC_FALSE;
1566 intf_RunThread( p_vcd->p_intf );
1568 if (play_single_item)
1569 VCDFixupPlayList( p_input, p_vcd, psz_source, &itemid, play_single_item );
1577 return VLC_EGENERIC;
1580 /*****************************************************************************
1581 * Close: closes VCD releasing allocated memory.
1582 *****************************************************************************/
1584 E_(Close) ( vlc_object_t *p_this )
1586 input_thread_t * p_input = (input_thread_t *)p_this;
1587 thread_vcd_data_t *p_vcd = (thread_vcd_data_t *)p_input->p_access_data;
1589 dbg_print( (INPUT_DBG_CALL|INPUT_DBG_EXT), "VCDClose" );
1590 vcdinfo_close( p_vcd->vcd );
1592 free( p_vcd->p_entries );
1593 free( p_vcd->p_segments );
1595 /* For reasons that are a mystery to me we don't have to deal with
1596 stopping, and destroying the p_vcd->p_intf thread. And if we do
1597 it causes problems upstream.
1599 if( p_vcd->p_intf != NULL )
1601 p_vcd->p_intf = NULL;
1605 p_input->p_access_data = NULL;