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.18 2004/01/29 17:51:07 zorglub 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;
1011 playlist_item_t *p_item;
1013 dbg_print( INPUT_DBG_META, "field: %s: %s\n", title, str);
1014 input_AddInfo( p_cat, title, "%s", str );
1016 vlc_mutex_lock( &p_playlist->object_lock );
1017 p_item = playlist_ItemGetByPos( p_playlist, -1 );
1018 vlc_mutex_unlock( &p_playlist->object_lock );
1020 vlc_mutex_lock( &p_item->lock );
1021 playlist_ItemAddInfo( p_item, p_cat->psz_name, title,
1023 vlc_mutex_unlock( &p_item->lock );
1029 MetaInfoAddNum(input_thread_t *p_input, input_info_category_t *p_cat,
1030 playlist_t *p_playlist, char *title, int num)
1032 thread_vcd_data_t *p_vcd = (thread_vcd_data_t *) p_input->p_access_data;
1033 playlist_item_t *p_item;
1035 vlc_mutex_lock( &p_playlist->object_lock );
1036 p_item = playlist_ItemGetByPos( p_playlist, -1 );
1037 vlc_mutex_unlock( &p_playlist->object_lock );
1039 dbg_print( INPUT_DBG_META, "field %s: %d\n", title, num);
1040 input_AddInfo( p_cat, title, "%d", num );
1042 vlc_mutex_lock( &p_item->lock );
1043 playlist_ItemAddInfo( p_item , p_cat->psz_name, title, "%d",num );
1044 vlc_mutex_unlock( &p_item->lock );
1047 #define addstr(title, str) \
1048 MetaInfoAddStr( p_input, p_cat, p_playlist, title, str );
1050 #define addnum(title, num) \
1051 MetaInfoAddNum( p_input, p_cat, p_playlist, title, num );
1053 static void InformationCreate( input_thread_t *p_input )
1055 thread_vcd_data_t *p_vcd = (thread_vcd_data_t *) p_input->p_access_data;
1056 unsigned int i_nb = vcdinfo_get_num_entries(p_vcd->vcd);
1057 unsigned int last_entry = 0;
1058 input_info_category_t *p_cat;
1060 playlist_t *p_playlist = vlc_object_find( p_input, VLC_OBJECT_PLAYLIST,
1063 p_cat = input_InfoCategory( p_input, "General" );
1065 addstr( _("VCD Format"), vcdinfo_get_format_version_str(p_vcd->vcd) );
1066 addstr( _("Album"), vcdinfo_get_album_id(p_vcd->vcd));
1067 addstr( _("Application"), vcdinfo_get_application_id(p_vcd->vcd) );
1068 addstr( _("Preparer"), vcdinfo_get_preparer_id(p_vcd->vcd) );
1069 addnum( _("Vol #"), vcdinfo_get_volume_num(p_vcd->vcd) );
1070 addnum( _("Vol max #"), vcdinfo_get_volume_count(p_vcd->vcd) );
1071 addstr( _("Volume Set"), vcdinfo_get_volumeset_id(p_vcd->vcd) );
1072 addstr( _("Volume"), vcdinfo_get_volume_id(p_vcd->vcd) );
1073 addstr( _("Publisher"), vcdinfo_get_publisher_id(p_vcd->vcd) );
1074 addstr( _("System Id"), vcdinfo_get_system_id(p_vcd->vcd) );
1075 addnum( "LIDs", vcdinfo_get_num_LIDs(p_vcd->vcd) );
1076 addnum( _("Entries"), vcdinfo_get_num_entries(p_vcd->vcd) );
1077 addnum( _("Segments"), vcdinfo_get_num_segments(p_vcd->vcd) );
1078 addnum( _("Tracks"), vcdinfo_get_num_tracks(p_vcd->vcd) );
1080 /* Spit out track information. Could also include MSF info.
1083 #define TITLE_MAX 30
1084 for( i_track = 1 ; i_track < p_vcd->num_tracks ; i_track++ ) {
1085 char track_str[TITLE_MAX];
1086 unsigned int audio_type = vcdinfo_get_track_audio_type(p_vcd->vcd,
1088 snprintf(track_str, TITLE_MAX, "%s%02d", _("Track"), i_track);
1089 p_cat = input_InfoCategory( p_input, track_str );
1092 addnum(_("Audio Channels"),
1093 vcdinfo_audio_type_num_channels(p_vcd->vcd, audio_type) );
1096 addnum(_("First Entry Point"), last_entry );
1097 for ( ; last_entry < i_nb
1098 && vcdinfo_get_track(p_vcd->vcd, last_entry) == i_track;
1100 addnum(_("Last Entry Point"), last_entry-1 );
1104 #define add_format_str_info(val) \
1106 const char *str = val; \
1108 if (val != NULL) { \
1111 strncat(tp, str, TEMP_STR_LEN-(tp-temp_str)); \
1114 saw_control_prefix = false; \
1118 #define add_format_num_info(val, fmt) \
1122 sprintf(num_str, fmt, val); \
1123 len=strlen(num_str); \
1125 strncat(tp, num_str, TEMP_STR_LEN-(tp-temp_str)); \
1128 saw_control_prefix = false; \
1132 Take a format string and expand escape sequences, that is sequences that
1133 begin with %, with information from the current VCD.
1134 The expanded string is returned. Here is a list of escape sequences:
1136 %A : The album information
1137 %C : The VCD volume count - the number of CD's in the collection.
1138 %c : The VCD volume num - the number of the CD in the collection.
1139 %F : The VCD Format, e.g. VCD 1.0, VCD 1.1, VCD 2.0, or SVCD
1140 %I : The current entry/segment/playback type, e.g. ENTRY, TRACK, SEGMENT...
1141 %L : The playlist ID prefixed with " LID" if it exists
1143 %N : The current number of the %I - a decimal number
1144 %P : The publisher ID
1145 %p : The preparer ID
1146 %S : If we are in a segment (menu), the kind of segment
1147 %T : The track number
1148 %V : The volume set ID
1150 A number between 1 and the volume count.
1154 VCDFormatStr(const input_thread_t *p_input, thread_vcd_data_t *p_vcd,
1155 const char format_str[], const char *mrl,
1156 const vcdinfo_itemid_t *itemid)
1158 #define TEMP_STR_SIZE 256
1159 #define TEMP_STR_LEN (TEMP_STR_SIZE-1)
1160 static char temp_str[TEMP_STR_SIZE];
1162 char * tp = temp_str;
1163 bool saw_control_prefix = false;
1164 size_t format_len = strlen(format_str);
1166 bzero(temp_str, TEMP_STR_SIZE);
1168 for (i=0; i<format_len; i++) {
1170 if (!saw_control_prefix && format_str[i] != '%') {
1171 *tp++ = format_str[i];
1172 saw_control_prefix = false;
1176 switch(format_str[i]) {
1178 if (saw_control_prefix) {
1181 saw_control_prefix = !saw_control_prefix;
1184 add_format_str_info(vcdinfo_strip_trail(vcdinfo_get_album_id(p_vcd->vcd),
1189 add_format_num_info(vcdinfo_get_volume_num(p_vcd->vcd), "%d");
1193 add_format_num_info(vcdinfo_get_volume_count(p_vcd->vcd), "%d");
1197 add_format_str_info(vcdinfo_get_format_version_str(p_vcd->vcd));
1202 switch (itemid->type) {
1203 case VCDINFO_ITEM_TYPE_TRACK:
1204 strncat(tp, _("Track"), TEMP_STR_LEN-(tp-temp_str));
1205 tp += strlen(_("Track"));
1207 case VCDINFO_ITEM_TYPE_ENTRY:
1208 strncat(tp, _("Entry"), TEMP_STR_LEN-(tp-temp_str));
1209 tp += strlen(_("Entry"));
1211 case VCDINFO_ITEM_TYPE_SEGMENT:
1212 strncat(tp, _("Segment"), TEMP_STR_LEN-(tp-temp_str));
1213 tp += strlen(_("Segment"));
1215 case VCDINFO_ITEM_TYPE_LID:
1216 strncat(tp, _("List ID"), TEMP_STR_LEN-(tp-temp_str));
1217 tp += strlen(_("List ID"));
1219 case VCDINFO_ITEM_TYPE_SPAREID2:
1220 strncat(tp, _("Navigation"), TEMP_STR_LEN-(tp-temp_str));
1221 tp += strlen(_("Navigation"));
1227 saw_control_prefix = false;
1232 if (vcdplayer_pbc_is_on(p_vcd)) {
1234 sprintf(num_str, "%s %d", _("List ID"), p_vcd->cur_lid);
1235 strncat(tp, num_str, TEMP_STR_LEN-(tp-temp_str));
1236 tp += strlen(num_str);
1238 saw_control_prefix = false;
1242 add_format_str_info(mrl);
1246 add_format_num_info(itemid->num, "%d");
1250 add_format_str_info(vcdinfo_get_preparer_id(p_vcd->vcd));
1254 add_format_str_info(vcdinfo_get_publisher_id(p_vcd->vcd));
1258 if ( VCDINFO_ITEM_TYPE_SEGMENT==itemid->type ) {
1259 char seg_type_str[10];
1261 sprintf(seg_type_str, " %s",
1262 vcdinfo_video_type2str(p_vcd->vcd, itemid->num));
1263 strncat(tp, seg_type_str, TEMP_STR_LEN-(tp-temp_str));
1264 tp += strlen(seg_type_str);
1266 saw_control_prefix = false;
1270 add_format_num_info(p_vcd->cur_track, "%d");
1274 add_format_str_info(vcdinfo_get_volumeset_id(p_vcd->vcd));
1278 add_format_str_info(vcdinfo_get_volume_id(p_vcd->vcd));
1283 *tp++ = format_str[i];
1284 saw_control_prefix = false;
1287 return strdup(temp_str);
1291 VCDCreatePlayListItem(const input_thread_t *p_input,
1292 thread_vcd_data_t *p_vcd,
1293 playlist_t *p_playlist,
1294 const vcdinfo_itemid_t *itemid,
1295 char *psz_mrl, int psz_mrl_max,
1296 const char *psz_source, int playlist_operation,
1303 switch(itemid->type) {
1304 case VCDINFO_ITEM_TYPE_TRACK:
1307 case VCDINFO_ITEM_TYPE_SEGMENT:
1310 case VCDINFO_ITEM_TYPE_LID:
1313 case VCDINFO_ITEM_TYPE_ENTRY:
1321 snprintf(psz_mrl, psz_mrl_max, "%s%s@%c%u", VCD_MRL_PREFIX, psz_source,
1322 c_type, itemid->num);
1325 VCDFormatStr( p_input, p_vcd,
1326 config_GetPsz( p_input, MODULE_STRING "-title-format" ),
1329 playlist_Add( p_playlist, psz_mrl, p_title, playlist_operation, i_pos );
1332 VCDFormatStr( p_input, p_vcd,
1333 config_GetPsz( p_input, MODULE_STRING "-author-format" ),
1336 if( i_pos == PLAYLIST_END ) i_pos = p_playlist->i_size - 1;
1337 playlist_AddInfo(p_playlist, i_pos, _("General"), _("Author"), "%s",
1342 VCDFixupPlayList( input_thread_t *p_input, thread_vcd_data_t *p_vcd,
1343 const char *psz_source, vcdinfo_itemid_t *itemid,
1344 bool play_single_item )
1347 playlist_t * p_playlist;
1349 unsigned int psz_mrl_max = strlen(VCD_MRL_PREFIX) + strlen(psz_source) +
1350 strlen("@T") + strlen("100") + 1;
1352 psz_mrl = malloc( psz_mrl_max );
1354 if( psz_mrl == NULL )
1356 msg_Warn( p_input, "out of memory" );
1360 p_playlist = (playlist_t *) vlc_object_find( p_input, VLC_OBJECT_PLAYLIST,
1364 msg_Warn( p_input, "can't find playlist" );
1369 InformationCreate( p_input );
1371 if ( play_single_item )
1373 /* May fill out more information when the playlist user interface becomes
1376 VCDCreatePlayListItem(p_input, p_vcd, p_playlist, itemid,
1377 psz_mrl, psz_mrl_max, psz_source, PLAYLIST_REPLACE,
1378 p_playlist->i_index);
1383 vcdinfo_itemid_t list_itemid;
1384 list_itemid.type=VCDINFO_ITEM_TYPE_ENTRY;
1386 playlist_Delete( p_playlist, p_playlist->i_index);
1388 for( i = 0 ; i < p_vcd->num_entries ; i++ )
1391 VCDCreatePlayListItem(p_input, p_vcd, p_playlist, &list_itemid,
1392 psz_mrl, psz_mrl_max, psz_source,
1393 PLAYLIST_APPEND, PLAYLIST_END);
1396 playlist_Command( p_playlist, PLAYLIST_GOTO, 0 );
1400 vlc_object_release( p_playlist );
1405 /*****************************************************************************
1407 *****************************************************************************/
1409 E_(DebugCallback) ( vlc_object_t *p_this, const char *psz_name,
1410 vlc_value_t oldval, vlc_value_t val, void *p_data )
1412 thread_vcd_data_t *p_vcd;
1414 if (NULL == p_vcd_input) return VLC_EGENERIC;
1416 p_vcd = (thread_vcd_data_t *)p_vcd_input->p_access_data;
1418 if (p_vcd->i_debug & (INPUT_DBG_CALL|INPUT_DBG_EXT)) {
1419 msg_Dbg( p_vcd_input, "Old debug (x%0x) %d, new debug (x%0x) %d",
1420 p_vcd->i_debug, p_vcd->i_debug, val.i_int, val.i_int);
1422 p_vcd->i_debug = val.i_int;
1426 /*****************************************************************************
1428 read in meta-information about VCD: the number of tracks, segments,
1429 entries, size and starting information. Then set up state variables so
1430 that we read/seek starting at the location specified.
1432 On success we return VLC_SUCCESS, on memory exhausted VLC_ENOMEM,
1433 and VLC_EGENERIC for some other error.
1434 *****************************************************************************/
1436 E_(Open) ( vlc_object_t *p_this )
1438 input_thread_t * p_input = (input_thread_t *)p_this;
1439 thread_vcd_data_t * p_vcd;
1441 vcdinfo_itemid_t itemid;
1443 bool play_single_item = false;
1445 p_input->pf_read = VCDRead;
1446 p_input->pf_seek = VCDSeek;
1447 p_input->pf_set_area = VCDSetArea;
1448 p_input->pf_set_program = VCDSetProgram;
1450 p_vcd = malloc( sizeof(thread_vcd_data_t) );
1454 LOG_ERR ("out of memory" );
1458 p_input->p_access_data = (void *)p_vcd;
1459 p_vcd->i_debug = config_GetInt( p_this, MODULE_STRING "-debug" );
1461 /* Set where to log errors messages from libcdio. */
1462 p_vcd_input = (input_thread_t *)p_this;
1463 cdio_log_set_handler ( cdio_log_handler );
1464 vcd_log_set_handler ( vcd_log_handler );
1466 psz_source = VCDParse( p_input, &itemid, &play_single_item );
1468 if ( NULL == psz_source )
1471 return( VLC_EGENERIC );
1474 dbg_print( (INPUT_DBG_CALL|INPUT_DBG_EXT), "source: %s: mrl: %s",
1475 psz_source, p_input->psz_name );
1477 p_vcd->p_segments = NULL;
1478 p_vcd->p_entries = NULL;
1481 p_input->i_mtu = VCD_DATA_ONCE;
1483 vlc_mutex_lock( &p_input->stream.stream_lock );
1485 /* If we are here we can control the pace... */
1486 p_input->stream.b_pace_control = 1;
1488 p_input->stream.b_seekable = 1;
1489 p_input->stream.p_selected_area->i_size = 0;
1490 p_input->stream.p_selected_area->i_tell = 0;
1492 vlc_mutex_unlock( &p_input->stream.stream_lock );
1494 if( !(p_vcd->vcd = vcd_Open( p_this, psz_source )) )
1496 msg_Warn( p_input, "could not open %s", psz_source );
1500 p_vcd->b_svd= vcdinfo_get_tracksSVD(p_vcd->vcd);;
1502 /* Get track information. */
1503 p_vcd->num_tracks = ioctl_GetTracksMap( VLC_OBJECT(p_input),
1504 vcdinfo_get_cd_image(p_vcd->vcd),
1505 &p_vcd->p_sectors );
1506 if( p_vcd->num_tracks < 0 )
1507 LOG_ERR ("unable to count tracks" );
1508 else if( p_vcd->num_tracks <= 1 )
1509 LOG_ERR ("no movie tracks found" );
1510 if( p_vcd->num_tracks <= 1)
1512 vcdinfo_close( p_vcd->vcd );
1516 /* Set stream and area data */
1517 vlc_mutex_lock( &p_input->stream.stream_lock );
1519 /* Initialize ES structures */
1520 input_InitStream( p_input, sizeof( stream_ps_data_t ) );
1522 /* disc input method */
1523 p_input->stream.i_method = INPUT_METHOD_VCD;
1525 p_input->stream.i_area_nb = 1;
1528 /* Initialize segment information. */
1529 VCDSegments( p_input );
1531 /* Initialize track area information. */
1532 VCDTracks( p_input );
1534 if( VCDEntryPoints( p_input ) < 0 )
1536 msg_Warn( p_input, "could not read entry points, will not use them" );
1537 p_vcd->b_valid_ep = false;
1540 if( VCDLIDs( p_input ) < 0 )
1542 msg_Warn( p_input, "could not read entry LIDs" );
1545 b_play_ok = (VLC_SUCCESS == VCDPlay( p_input, itemid ));
1547 vlc_mutex_unlock( &p_input->stream.stream_lock );
1549 if ( ! b_play_ok ) {
1550 vcdinfo_close( p_vcd->vcd );
1554 if( !p_input->psz_demux || !*p_input->psz_demux )
1557 p_input->psz_demux = "vcdx";
1559 p_input->psz_demux = "ps";
1563 p_vcd->p_intf = intf_Create( p_input, "vcdx" );
1564 p_vcd->p_intf->b_block = VLC_FALSE;
1565 intf_RunThread( p_vcd->p_intf );
1567 if (play_single_item)
1568 VCDFixupPlayList( p_input, p_vcd, psz_source, &itemid, play_single_item );
1576 return VLC_EGENERIC;
1579 /*****************************************************************************
1580 * Close: closes VCD releasing allocated memory.
1581 *****************************************************************************/
1583 E_(Close) ( vlc_object_t *p_this )
1585 input_thread_t * p_input = (input_thread_t *)p_this;
1586 thread_vcd_data_t *p_vcd = (thread_vcd_data_t *)p_input->p_access_data;
1588 dbg_print( (INPUT_DBG_CALL|INPUT_DBG_EXT), "VCDClose" );
1589 vcdinfo_close( p_vcd->vcd );
1591 free( p_vcd->p_entries );
1592 free( p_vcd->p_segments );
1594 /* For reasons that are a mystery to me we don't have to deal with
1595 stopping, and destroying the p_vcd->p_intf thread. And if we do
1596 it causes problems upstream.
1598 if( p_vcd->p_intf != NULL )
1600 p_vcd->p_intf = NULL;
1604 p_input->p_access_data = NULL;