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
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: %lu",
163 (long unsigned int) p_vcd->cur_lsn );
165 /* Compute the number of blocks we have to read */
167 i_blocks = i_len / M2F2_SECTOR_SIZE;
169 for ( i_index = 0 ; i_index < i_blocks ; i_index++ )
172 if ( p_vcd->cur_lsn == p_vcd->end_lsn ) {
173 vcdplayer_read_status_t read_status;
175 /* We've run off of the end of this entry. Do we continue or stop? */
176 dbg_print( (INPUT_DBG_LSN|INPUT_DBG_PBC),
177 "end reached, cur: %lu",
178 (long unsigned int) p_vcd->cur_lsn );
180 read_status = vcdplayer_pbc_is_on( p_vcd )
181 ? vcdplayer_pbc_nav( p_input )
182 : vcdplayer_non_pbc_nav( p_input );
184 switch (read_status) {
186 /* End reached. Return NULL to indicated this. */
188 /* Some sort of error. */
191 case READ_STILL_FRAME:
193 /* Reached the end of a still frame. */
195 byte_t * p_buf = p_buffer;
196 pgrm_descriptor_t * p_pgrm = p_input->stream.p_selected_program;;
198 p_buf += (i_index*M2F2_SECTOR_SIZE);
199 memset(p_buf, 0, M2F2_SECTOR_SIZE);
202 dbg_print(INPUT_DBG_STILL, "Handled still event");
205 p_vcd->p_intf->p_sys->b_still = 1;
206 input_SetStatus( p_input, INPUT_STATUS_PAUSE );
209 vlc_mutex_lock( &p_input->stream.stream_lock );
211 p_pgrm = p_input->stream.p_selected_program;
212 p_pgrm->i_synchro_state = SYNCHRO_REINIT;
214 vlc_mutex_unlock( &p_input->stream.stream_lock );
216 input_ClockManageControl( p_input, p_pgrm, 0 );
218 p_vcd->p_intf->p_sys->b_still = 1;
219 input_SetStatus( p_input, INPUT_STATUS_PAUSE );
221 return i_read + M2F2_SECTOR_SIZE;
229 if ( VCDReadSector( VLC_OBJECT(p_input), p_vcd->vcd,
231 p_buffer + (i_index*M2F2_SECTOR_SIZE) ) < 0 )
233 LOG_ERR ("could not read sector %lu",
234 (long unsigned int) p_vcd->cur_lsn );
241 if( p_vcd->b_valid_ep &&
242 /* FIXME kludge so that read does not update chapter
243 * when a manual chapter change was requested and not
244 * yet accomplished */
245 !p_input->stream.p_new_area )
247 unsigned int i_entry = p_input->stream.p_selected_area->i_part;
249 vlc_mutex_lock( &p_input->stream.stream_lock );
251 if( i_entry < p_vcd->num_entries &&
252 p_vcd->cur_lsn >= p_vcd->p_entries[i_entry+1] )
254 dbg_print( INPUT_DBG_PBC,
255 "new entry, i_entry %d, sector %lu, es %u",
256 i_entry, (long unsigned int) p_vcd->cur_lsn,
257 p_vcd->p_entries[i_entry] );
258 p_vcd->play_item.num =
259 ++ p_input->stream.p_selected_area->i_part;
260 p_vcd->play_item.type = VCDINFO_ITEM_TYPE_ENTRY;
261 VCDUpdateVar( p_input, p_vcd->play_item.num, VLC_VAR_SETVALUE,
262 "chapter", _("Entry"), "Setting entry" );
264 vlc_mutex_unlock( &p_input->stream.stream_lock );
267 i_read += M2F2_SECTOR_SIZE;
270 if ( i_len % M2F2_SECTOR_SIZE ) /* this should not happen */
272 if ( VCDReadSector( VLC_OBJECT(p_input), p_vcd->vcd,
273 p_vcd->cur_lsn, p_last_sector ) < 0 )
275 LOG_ERR ("could not read sector %lu",
276 (long unsigned int) p_vcd->cur_lsn );
280 p_input->p_vlc->pf_memcpy( p_buffer + i_blocks * M2F2_SECTOR_SIZE,
281 p_last_sector, i_len % M2F2_SECTOR_SIZE );
282 i_read += i_len % M2F2_SECTOR_SIZE;
289 /*****************************************************************************
290 * VCDSetProgram: Does nothing since a VCD is mono_program
291 *****************************************************************************/
293 VCDSetProgram( input_thread_t * p_input, pgrm_descriptor_t * p_program)
295 thread_vcd_data_t * p_vcd= (thread_vcd_data_t *)p_input->p_access_data;
296 dbg_print( (INPUT_DBG_CALL|INPUT_DBG_EXT), "VCDSetProgram" );
301 /*****************************************************************************
302 * VCDSetArea: initialize internal data structures and input stream data
303 so set subsequent reading and seeking to reflect that we are
304 at track x, entry or segment y.
305 This is called for each user navigation request, e.g. the GUI
306 Chapter/Title selections or in initial MRL parsing.
307 ****************************************************************************/
309 VCDSetArea( input_thread_t * p_input, input_area_t * p_area )
311 thread_vcd_data_t *p_vcd = (thread_vcd_data_t*)p_input->p_access_data;
312 unsigned int i_entry = p_area->i_part;
313 track_t i_track = p_area->i_id;
314 int old_seekable = p_input->stream.b_seekable;
315 unsigned int i_nb = p_area->i_plugin_data + p_area->i_part_nb;
317 dbg_print( (INPUT_DBG_CALL|INPUT_DBG_EXT),
318 "track: %d, entry %d, seekable %d, area %lx, select area %lx ",
319 i_track, i_entry, old_seekable,
320 (long unsigned int) p_area,
321 (long unsigned int) p_input->stream.p_selected_area );
323 /* we can't use the interface slider until initilization is complete */
324 p_input->stream.b_seekable = 0;
326 if( p_area != p_input->stream.p_selected_area )
330 /* If is the result of a track change, make the entry valid. */
331 if (i_entry < p_area->i_plugin_data || i_entry >= i_nb)
332 i_entry = p_area->i_plugin_data;
334 /* Change the default area */
335 p_input->stream.p_selected_area = p_area;
337 /* Update the navigation variables without triggering a callback */
339 VCDUpdateVar( p_input, i_track, VLC_VAR_SETVALUE, "title",
340 _("Track"), "Setting track");
342 var_Change( p_input, "chapter", VLC_VAR_CLEARCHOICES, NULL, NULL );
343 for( i = p_area->i_plugin_data; i < i_nb; i++ )
345 VCDUpdateVar( p_input, i , VLC_VAR_ADDCHOICE,
347 p_vcd->play_item.type == VCDINFO_ITEM_TYPE_SEGMENT ?
348 _("Segment"): _("Entry"),
349 "Adding entry choice");
353 unsigned int audio_type =
354 vcdinfo_get_track_audio_type(p_vcd->vcd, i_track);
355 unsigned int i_channels =
356 vcdinfo_audio_type_num_channels(p_vcd->vcd, audio_type);
358 var_Change( p_input, "audio_channels", VLC_VAR_CLEARCHOICES, NULL,
361 for( i = 0; i < i_channels; i++ )
363 VCDUpdateVar( p_input, i , VLC_VAR_ADDCHOICE,
364 "audio_channels", NULL, "Adding audio choice");
371 VCDSetOrigin( p_input, p_vcd->p_segments[i_entry],
372 p_vcd->p_segments[i_entry], p_vcd->p_segments[i_entry+1],
375 VCDSetOrigin( p_input, p_vcd->p_sectors[i_track],
376 vcdinfo_get_entry_lsn(p_vcd->vcd, i_entry),
377 p_vcd->p_sectors[i_track+1],
380 p_input->stream.b_seekable = old_seekable;
381 /* warn interface that something has changed */
382 p_input->stream.b_changed = 1;
388 /****************************************************************************
390 ****************************************************************************/
392 VCDSeek( input_thread_t * p_input, off_t i_off )
394 thread_vcd_data_t * p_vcd;
395 unsigned int i_entry=0; /* invalid entry */
397 p_vcd = (thread_vcd_data_t *) p_input->p_access_data;
399 p_vcd->cur_lsn = p_vcd->origin_lsn + (i_off / (off_t)M2F2_SECTOR_SIZE);
401 vlc_mutex_lock( &p_input->stream.stream_lock );
402 #define p_area p_input->stream.p_selected_area
404 if( p_vcd->b_valid_ep )
406 for( i_entry = 0 ; i_entry < p_vcd->num_entries ; i_entry ++ )
408 if( p_vcd->cur_lsn < p_vcd->p_entries[i_entry] )
410 VCDUpdateVar( p_input, i_entry, VLC_VAR_SETVALUE,
411 "chapter", _("Entry"), "Setting entry" );
415 p_vcd->play_item.num = p_area->i_part = i_entry;
416 p_vcd->play_item.type = VCDINFO_ITEM_TYPE_ENTRY;
420 p_input->stream.p_selected_area->i_tell = i_off;
422 dbg_print( (INPUT_DBG_CALL|INPUT_DBG_EXT|INPUT_DBG_SEEK),
423 "orig %lu, cur: %lu, offset: %lld, start: %lld, entry %d",
424 (long unsigned int) p_vcd->origin_lsn,
425 (long unsigned int) p_vcd->cur_lsn, i_off,
426 p_input->stream.p_selected_area->i_start, i_entry );
428 vlc_mutex_unlock( &p_input->stream.stream_lock );
431 /*****************************************************************************
432 VCDPlay: set up internal structures so seeking/reading places an item.
433 itemid: the thing to play.
434 user_entry: true if itemid is a user selection (rather than internally-
435 generated selection such as via PBC) in which case we may have to adjust
436 for differences in numbering.
437 *****************************************************************************/
439 VCDPlay( input_thread_t *p_input, vcdinfo_itemid_t itemid )
441 thread_vcd_data_t * p_vcd= (thread_vcd_data_t *)p_input->p_access_data;
442 input_area_t * p_area;
445 dbg_print(INPUT_DBG_CALL, "itemid.num: %d, itemid.type: %d\n",
446 itemid.num, itemid.type);
448 if (!p_input->p_access_data) return VLC_EGENERIC;
450 b_was_still = p_vcd->in_still;
452 #define area p_input->stream.pp_areas
454 switch (itemid.type) {
455 case VCDINFO_ITEM_TYPE_TRACK:
457 /* Valid tracks go from 1...num_tracks-1, because track 0 is unplayable.
460 if (itemid.num == 0 || itemid.num >= p_vcd->num_tracks) {
461 LOG_ERR ("Invalid track number %d", itemid.num );
464 p_vcd->in_still = false;
465 p_area = area[itemid.num];
466 p_area->i_part = p_area->i_plugin_data;
467 p_input->stream.b_seekable = 1;
469 case VCDINFO_ITEM_TYPE_SEGMENT:
470 /* Valid segments go from 0...num_segments-1. */
471 if (itemid.num >= p_vcd->num_segments) {
472 LOG_ERR ( "Invalid segment number: %d", itemid.num );
475 vcdinfo_video_segment_type_t segtype =
476 vcdinfo_get_video_type(p_vcd->vcd, itemid.num);
478 dbg_print(INPUT_DBG_PBC, "%s (%d), seg_num: %d",
479 vcdinfo_video_type2str(p_vcd->vcd, itemid.num),
480 (int) segtype, itemid.num);
483 p_area->i_part = itemid.num;
487 case VCDINFO_FILES_VIDEO_NTSC_STILL:
488 case VCDINFO_FILES_VIDEO_NTSC_STILL2:
489 case VCDINFO_FILES_VIDEO_PAL_STILL:
490 case VCDINFO_FILES_VIDEO_PAL_STILL2:
491 p_input->stream.b_seekable = 0;
492 p_vcd->in_still = true;
495 p_input->stream.b_seekable = 1;
496 p_vcd->in_still = false;
501 case VCDINFO_ITEM_TYPE_LID:
502 /* LIDs go from 1..num_lids. */
503 if (itemid.num == 0 || itemid.num > p_vcd->num_lids) {
504 LOG_ERR ( "Invalid LID number: %d", itemid.num );
507 p_vcd->cur_lid = itemid.num;
508 vcdinfo_lid_get_pxd(p_vcd->vcd, &(p_vcd->pxd), itemid.num);
510 switch (p_vcd->pxd.descriptor_type) {
512 case PSD_TYPE_SELECTION_LIST:
513 case PSD_TYPE_EXT_SELECTION_LIST: {
514 vcdinfo_itemid_t trans_itemid;
515 uint16_t trans_itemid_num;
517 if (p_vcd->pxd.psd == NULL) return VLC_EGENERIC;
518 trans_itemid_num = vcdinf_psd_get_itemid(p_vcd->pxd.psd);
519 vcdinfo_classify_itemid(trans_itemid_num, &trans_itemid);
520 p_vcd->loop_count = 1;
521 p_vcd->loop_item = trans_itemid;
522 return VCDPlay( p_input, trans_itemid );
526 case PSD_TYPE_PLAY_LIST: {
527 if (p_vcd->pxd.pld == NULL) return VLC_EGENERIC;
529 return vcdplayer_inc_play_item(p_input)
530 ? VLC_SUCCESS : VLC_EGENERIC;
534 case PSD_TYPE_END_LIST:
535 case PSD_TYPE_COMMAND_LIST:
542 case VCDINFO_ITEM_TYPE_ENTRY:
543 /* Entries go from 0..num_entries-1. */
544 if (itemid.num >= p_vcd->num_entries) {
545 LOG_ERR ("Invalid entry number: %d", itemid.num );
548 track_t cur_track = vcdinfo_get_track(p_vcd->vcd, itemid.num);
549 p_vcd->in_still = false;
550 p_area = area[cur_track];
551 p_area->i_part = itemid.num;
552 p_input->stream.b_seekable = 1;
556 LOG_ERR ("unknown entry type" );
560 VCDSetArea( p_input, p_area );
565 if ( p_vcd->in_still != b_was_still ) {
566 if (p_input->stream.pp_selected_es) {
567 input_SetStatus( p_input, INPUT_STATUS_END );
568 input_SetStatus( p_input, INPUT_STATUS_PLAY );
573 p_vcd->play_item = itemid;
575 dbg_print( (INPUT_DBG_CALL),
576 "i_start %lld, i_size: %lld, i_tell: %lld, lsn %lu",
577 p_area->i_start, p_area->i_size,
579 (long unsigned int) p_vcd->cur_lsn );
584 /*****************************************************************************
585 VCDEntryPoints: Reads the information about the entry points on the disc
586 and initializes area information with that.
587 Before calling this track information should have been read in.
588 *****************************************************************************/
590 VCDEntryPoints( input_thread_t * p_input )
592 thread_vcd_data_t * p_vcd;
594 unsigned int i, i_entry_index = 0;
595 unsigned int i_previous_track = CDIO_INVALID_TRACK;
597 p_vcd = (thread_vcd_data_t *) p_input->p_access_data;
599 i_nb = vcdinfo_get_num_entries(p_vcd->vcd);
603 p_vcd->p_entries = malloc( sizeof( lba_t ) * i_nb );
605 if( p_vcd->p_entries == NULL )
607 LOG_ERR ("not enough memory for entry points treatment" );
611 p_vcd->num_entries = 0;
613 for( i = 0 ; i < i_nb ; i++ )
615 track_t i_track = vcdinfo_get_track(p_vcd->vcd, i);
616 if( i_track <= p_input->stream.i_area_nb )
618 p_vcd->p_entries[i] =
619 vcdinfo_get_entry_lsn(p_vcd->vcd, i);
620 p_input->stream.pp_areas[i_track]->i_part_nb ++;
622 /* if this entry belongs to a new track */
623 if( i_track != i_previous_track )
625 /* i_plugin_data is used to store the first entry of the area*/
626 p_input->stream.pp_areas[i_track]->i_plugin_data =
628 i_previous_track = i_track;
629 p_input->stream.pp_areas[i_track]->i_part_nb = 1;
632 p_vcd->num_entries ++;
635 msg_Warn( p_input, "wrong track number found in entry points" );
637 p_vcd->b_valid_ep = true;
641 /*****************************************************************************
642 * VCDSegments: Reads the information about the segments the disc.
643 *****************************************************************************/
645 VCDSegments( input_thread_t * p_input )
647 thread_vcd_data_t * p_vcd;
649 unsigned int num_segments;
652 p_vcd = (thread_vcd_data_t *) p_input->p_access_data;
653 num_segments = p_vcd->num_segments = vcdinfo_get_num_segments(p_vcd->vcd);
655 #define area p_input->stream.pp_areas
657 /* area 0 is reserved for segments. Set Absolute start offset
659 area[0]->i_plugin_data = 0;
660 input_DelArea( p_input, area[0] );
661 input_AddArea( p_input, 0, 0 );
663 area[0]->i_start = (off_t)p_vcd->p_sectors[0]
664 * (off_t)M2F2_SECTOR_SIZE;
665 area[0]->i_size = (off_t)(p_vcd->p_sectors[1] - p_vcd->p_sectors[0])
666 * (off_t)M2F2_SECTOR_SIZE;
668 /* Default Segment */
671 /* i_plugin_data is used to store which entry point is the first
672 of the track (area) */
673 area[0]->i_plugin_data = 0;
675 area[0]->i_part_nb = 0;
677 dbg_print( INPUT_DBG_MRL,
678 "area[0] id: %d, i_start: %lld, i_size: %lld",
679 area[0]->i_id, area[0]->i_start, area[0]->i_size );
681 if (num_segments == 0) return 0;
683 /* We have one additional segment allocated so we can get the size
684 by subtracting seg[i+1] - seg[i].
686 p_vcd->p_segments = malloc( sizeof( lba_t ) * (num_segments+1) );
687 if( p_vcd->p_segments == NULL )
689 LOG_ERR ("not enough memory for segment treatment" );
693 /* Update the navigation variables without triggering a callback */
694 VCDUpdateVar( p_input, 0, VLC_VAR_SETVALUE, "title", _("Track"),
697 var_Change( p_input, "chapter", VLC_VAR_CLEARCHOICES, NULL, NULL );
699 for( i = 0 ; i < num_segments ; i++ )
701 p_vcd->p_segments[i] = vcdinfo_get_seg_lsn(p_vcd->vcd, i);
702 area[0]->i_part_nb ++;
703 VCDUpdateVar( p_input, i , VLC_VAR_ADDCHOICE,
704 "chapter", _("Segment"), "Adding segment choice");
709 p_vcd->p_segments[num_segments] = p_vcd->p_segments[num_segments-1]+
710 vcdinfo_get_seg_sector_count(p_vcd->vcd, num_segments-1);
715 /*****************************************************************************
716 VCDTracks: initializes area information.
717 Before calling this track information should have been read in.
718 *****************************************************************************/
720 VCDTracks( input_thread_t * p_input )
722 thread_vcd_data_t * p_vcd;
725 p_vcd = (thread_vcd_data_t *) p_input->p_access_data;
727 #define area p_input->stream.pp_areas
729 /* We start area addressing for tracks at 1 since the default area 0
730 is reserved for segments */
732 for( i = 1 ; i < p_vcd->num_tracks ; i++ )
734 /* Tracks are Program Chains */
735 input_AddArea( p_input, i, i );
737 /* Absolute start byte offset and byte size */
738 area[i]->i_start = (off_t) p_vcd->p_sectors[i]
739 * (off_t)M2F2_SECTOR_SIZE;
740 area[i]->i_size = (off_t)(p_vcd->p_sectors[i+1] - p_vcd->p_sectors[i])
741 * (off_t)M2F2_SECTOR_SIZE;
743 /* Current entry being played in track */
746 /* i_plugin_data is used to store which entry point is the first
747 * of the track (area) */
748 area[i]->i_plugin_data = 0;
750 dbg_print( INPUT_DBG_MRL,
751 "area[%d] id: %d, i_start: %lld, i_size: %lld",
752 i, area[i]->i_id, area[i]->i_start, area[i]->i_size );
760 /*****************************************************************************
761 VCDLIDs: Reads the LIST IDs from the LOT.
762 *****************************************************************************/
764 VCDLIDs( input_thread_t * p_input )
766 thread_vcd_data_t *p_vcd = (thread_vcd_data_t *) p_input->p_access_data;
768 p_vcd->num_lids = vcdinfo_get_num_LIDs(p_vcd->vcd);
769 p_vcd->cur_lid = VCDINFO_INVALID_ENTRY;
771 if (vcdinfo_read_psd (p_vcd->vcd)) {
773 vcdinfo_visit_lot (p_vcd->vcd, false);
777 We need to change libvcdinfo to be more robust when there are
778 problems reading the extended PSD. Given that area-highlighting and
779 selection features in the extended PSD haven't been implemented,
780 it's best then to not try to read this at all.
782 if (vcdinfo_get_psd_x_size(p_vcd->vcd))
783 vcdinfo_visit_lot (p_vcd->vcd, true);
787 dbg_print( (INPUT_DBG_CALL|INPUT_DBG_MRL),
788 "num LIDs=%d", p_vcd->num_lids);
793 /*****************************************************************************
794 * VCDParse: parse command line
795 *****************************************************************************/
797 VCDParse( input_thread_t * p_input, /*out*/ vcdinfo_itemid_t * p_itemid,
798 /*out*/ bool *play_single_item )
800 thread_vcd_data_t *p_vcd = (thread_vcd_data_t *)p_input->p_access_data;
805 if ( config_GetInt( p_input, MODULE_STRING "-PBC" ) ) {
806 p_itemid->type=VCDINFO_ITEM_TYPE_LID;
808 *play_single_item=false;
810 p_itemid->type=VCDINFO_ITEM_TYPE_ENTRY;
815 /* On Win32 we want the VCD access plugin to be explicitly requested,
816 * we end up with lots of problems otherwise */
817 if( !p_input->psz_access || !*p_input->psz_access ) return NULL;
820 if( !p_input->psz_name )
825 psz_parser = psz_source = strdup( p_input->psz_name );
827 /* Parse input string :
828 * [device][@[type][title]] */
829 while( *psz_parser && *psz_parser != '@' )
834 if( *psz_parser == '@' )
836 /* Found the divide between the source name and the
837 type+entry number. */
844 switch(*psz_parser) {
846 p_itemid->type = VCDINFO_ITEM_TYPE_ENTRY;
848 *play_single_item = true;
851 p_itemid->type = VCDINFO_ITEM_TYPE_LID;
853 *play_single_item = false;
856 p_itemid->type = VCDINFO_ITEM_TYPE_SEGMENT;
858 *play_single_item = true;
861 p_itemid->type = VCDINFO_ITEM_TYPE_TRACK;
863 *play_single_item = true;
869 num = strtol( psz_parser, &psz_next, 10 );
870 if ( *psz_parser != '\0' && *psz_next == '\0')
876 *play_single_item = ( VCDINFO_ITEM_TYPE_LID == p_itemid->type );
882 /* No source specified, so figure it out. */
883 if( !p_input->psz_access ) return NULL;
885 psz_source = config_GetPsz( p_input, "vcd" );
887 if( !psz_source || 0==strlen(psz_source) ) {
888 /* Scan for a CD-ROM drive with a VCD in it. */
889 char **cd_drives = cdio_get_devices_with_cap(NULL,
890 (CDIO_FS_ANAL_SVCD|CDIO_FS_ANAL_CVD
891 |CDIO_FS_ANAL_VIDEOCD|CDIO_FS_UNKNOWN),
893 if (NULL == cd_drives) return NULL;
894 if (cd_drives[0] == NULL) {
895 cdio_free_device_list(cd_drives);
898 psz_source = strdup(cd_drives[0]);
899 cdio_free_device_list(cd_drives);
903 dbg_print( (INPUT_DBG_CALL|INPUT_DBG_MRL),
904 "source=%s entry=%d type=%d",
905 psz_source, p_itemid->num, p_itemid->type);
911 Set's start origin subsequent seeks/reads
914 VCDSetOrigin( input_thread_t *p_input, lsn_t origin_lsn,
915 lsn_t cur_lsn, lsn_t end_lsn, int cur_entry, track_t cur_track )
917 thread_vcd_data_t * p_vcd = (thread_vcd_data_t *) p_input->p_access_data;
919 p_vcd->origin_lsn = origin_lsn;
920 p_vcd->cur_lsn = cur_lsn;
921 p_vcd->end_lsn = end_lsn;
922 p_vcd->cur_track = cur_track;
923 p_vcd->play_item.num = cur_entry;
924 p_vcd->play_item.type = VCDINFO_ITEM_TYPE_ENTRY;
926 dbg_print( (INPUT_DBG_CALL|INPUT_DBG_LSN),
927 "origin: %lu, cur_lsn: %lu, end_lsn: %lu, entry: %d, track: %d",
928 (long unsigned int) origin_lsn,
929 (long unsigned int) cur_lsn,
930 (long unsigned int) end_lsn, cur_entry, cur_track );
932 p_input->stream.p_selected_area->i_tell =
933 (off_t) (p_vcd->cur_lsn - p_vcd->origin_lsn) * (off_t)M2F2_SECTOR_SIZE;
935 VCDUpdateVar( p_input, cur_entry, VLC_VAR_SETVALUE,
937 p_vcd->play_item.type == VCDINFO_ITEM_TYPE_ENTRY ?
938 _("Entry") : _("Segment"),
939 "Setting entry/segment");
942 /*****************************************************************************
943 * vcd_Open: Opens a VCD device or file and returns an opaque handle
944 *****************************************************************************/
945 static vcdinfo_obj_t *
946 vcd_Open( vlc_object_t *p_this, const char *psz_dev )
948 vcdinfo_obj_t *p_vcdobj;
951 if( !psz_dev ) return NULL;
953 actual_dev=strdup(psz_dev);
954 if ( vcdinfo_open(&p_vcdobj, &actual_dev, DRIVER_UNKNOWN, NULL) !=
964 /****************************************************************************
965 * VCDReadSector: Read a sector (2324 bytes)
966 ****************************************************************************/
968 VCDReadSector( vlc_object_t *p_this, const vcdinfo_obj_t *p_vcd,
969 lsn_t cur_lsn, byte_t * p_buffer )
972 uint8_t subheader [8];
973 uint8_t data [M2F2_SECTOR_SIZE];
976 vcdsector_t vcd_sector;
978 if (cdio_read_mode2_sector(vcdinfo_get_cd_image(p_vcd),
979 &vcd_sector, cur_lsn, true)
982 msg_Warn( p_this, "Could not read LSN %lu",
983 (long unsigned int) cur_lsn );
987 memcpy (p_buffer, vcd_sector.data, M2F2_SECTOR_SIZE);
992 /****************************************************************************
993 Update the "varname" variable to i_num without triggering a callback.
994 ****************************************************************************/
996 VCDUpdateVar( input_thread_t *p_input, int i_num, int i_action,
997 const char *p_varname, char *p_label,
998 const char *p_debug_label)
1002 if (NULL != p_vcd_input) {
1003 thread_vcd_data_t *p_vcd = (thread_vcd_data_t *)p_vcd_input->p_access_data;
1004 dbg_print( INPUT_DBG_PBC, "%s %d", p_debug_label, i_num );
1008 text.psz_string = p_label;
1009 var_Change( p_input, p_varname, VLC_VAR_SETTEXT, &text, NULL );
1011 var_Change( p_input, p_varname, i_action, &val, NULL );
1016 MetaInfoAddStr(input_thread_t *p_input, char *p_cat,
1017 char *title, const char *str)
1019 thread_vcd_data_t *p_vcd = (thread_vcd_data_t *) p_input->p_access_data;
1021 dbg_print( INPUT_DBG_META, "field: %s: %s\n", title, str);
1022 input_Control( p_input, INPUT_ADD_INFO, p_cat, title, "%s", str);
1028 MetaInfoAddNum(input_thread_t *p_input, char *p_cat, char *title, int num)
1030 thread_vcd_data_t *p_vcd = (thread_vcd_data_t *) p_input->p_access_data;
1031 dbg_print( INPUT_DBG_META, "field %s: %d\n", title, num);
1032 input_Control( p_input, INPUT_ADD_INFO, p_cat, title, "%d", num );
1035 #define addstr(title, str) \
1036 MetaInfoAddStr( p_input, p_cat, title, str );
1038 #define addnum(title, num) \
1039 MetaInfoAddNum( p_input, p_cat, title, num );
1041 static void InformationCreate( input_thread_t *p_input )
1043 thread_vcd_data_t *p_vcd = (thread_vcd_data_t *) p_input->p_access_data;
1044 unsigned int i_nb = vcdinfo_get_num_entries(p_vcd->vcd);
1045 unsigned int last_entry = 0;
1049 p_cat = _("General");
1051 addstr( _("VCD Format"), vcdinfo_get_format_version_str(p_vcd->vcd) );
1052 addstr( _("Album"), vcdinfo_get_album_id(p_vcd->vcd));
1053 addstr( _("Application"), vcdinfo_get_application_id(p_vcd->vcd) );
1054 addstr( _("Preparer"), vcdinfo_get_preparer_id(p_vcd->vcd) );
1055 addnum( _("Vol #"), vcdinfo_get_volume_num(p_vcd->vcd) );
1056 addnum( _("Vol max #"), vcdinfo_get_volume_count(p_vcd->vcd) );
1057 addstr( _("Volume Set"), vcdinfo_get_volumeset_id(p_vcd->vcd) );
1058 addstr( _("Volume"), vcdinfo_get_volume_id(p_vcd->vcd) );
1059 addstr( _("Publisher"), vcdinfo_get_publisher_id(p_vcd->vcd) );
1060 addstr( _("System Id"), vcdinfo_get_system_id(p_vcd->vcd) );
1061 addnum( "LIDs", vcdinfo_get_num_LIDs(p_vcd->vcd) );
1062 addnum( _("Entries"), vcdinfo_get_num_entries(p_vcd->vcd) );
1063 addnum( _("Segments"), vcdinfo_get_num_segments(p_vcd->vcd) );
1064 addnum( _("Tracks"), vcdinfo_get_num_tracks(p_vcd->vcd) );
1066 /* Spit out track information. Could also include MSF info.
1069 #define TITLE_MAX 30
1070 for( i_track = 1 ; i_track < p_vcd->num_tracks ; i_track++ ) {
1071 char psz_track[TITLE_MAX];
1072 unsigned int audio_type = vcdinfo_get_track_audio_type(p_vcd->vcd,
1074 snprintf(psz_track, TITLE_MAX, "%s%02d", _("Track "), i_track);
1078 addnum(_("Audio Channels"),
1079 vcdinfo_audio_type_num_channels(p_vcd->vcd, audio_type) );
1082 addnum(_("First Entry Point"), last_entry );
1083 for ( ; last_entry < i_nb
1084 && vcdinfo_get_track(p_vcd->vcd, last_entry) == i_track;
1086 addnum(_("Last Entry Point"), last_entry-1 );
1090 #define add_format_str_info(val) \
1092 const char *str = val; \
1094 if (val != NULL) { \
1097 strncat(tp, str, TEMP_STR_LEN-(tp-temp_str)); \
1100 saw_control_prefix = false; \
1104 #define add_format_num_info(val, fmt) \
1108 sprintf(num_str, fmt, val); \
1109 len=strlen(num_str); \
1111 strncat(tp, num_str, TEMP_STR_LEN-(tp-temp_str)); \
1114 saw_control_prefix = false; \
1118 Take a format string and expand escape sequences, that is sequences that
1119 begin with %, with information from the current VCD.
1120 The expanded string is returned. Here is a list of escape sequences:
1122 %A : The album information
1123 %C : The VCD volume count - the number of CD's in the collection.
1124 %c : The VCD volume num - the number of the CD in the collection.
1125 %F : The VCD Format, e.g. VCD 1.0, VCD 1.1, VCD 2.0, or SVCD
1126 %I : The current entry/segment/playback type, e.g. ENTRY, TRACK, SEGMENT...
1127 %L : The playlist ID prefixed with " LID" if it exists
1129 %N : The current number of the %I - a decimal number
1130 %P : The publisher ID
1131 %p : The preparer ID
1132 %S : If we are in a segment (menu), the kind of segment
1133 %T : The track number
1134 %V : The volume set ID
1136 A number between 1 and the volume count.
1140 VCDFormatStr(const input_thread_t *p_input, thread_vcd_data_t *p_vcd,
1141 const char format_str[], const char *mrl,
1142 const vcdinfo_itemid_t *itemid)
1144 #define TEMP_STR_SIZE 256
1145 #define TEMP_STR_LEN (TEMP_STR_SIZE-1)
1146 static char temp_str[TEMP_STR_SIZE];
1148 char * tp = temp_str;
1149 bool saw_control_prefix = false;
1150 size_t format_len = strlen(format_str);
1152 memset(temp_str, 0, TEMP_STR_SIZE);
1154 for (i=0; i<format_len; i++) {
1156 if (!saw_control_prefix && format_str[i] != '%') {
1157 *tp++ = format_str[i];
1158 saw_control_prefix = false;
1162 switch(format_str[i]) {
1164 if (saw_control_prefix) {
1167 saw_control_prefix = !saw_control_prefix;
1170 add_format_str_info(vcdinfo_strip_trail(vcdinfo_get_album_id(p_vcd->vcd),
1175 add_format_num_info(vcdinfo_get_volume_num(p_vcd->vcd), "%d");
1179 add_format_num_info(vcdinfo_get_volume_count(p_vcd->vcd), "%d");
1183 add_format_str_info(vcdinfo_get_format_version_str(p_vcd->vcd));
1188 switch (itemid->type) {
1189 case VCDINFO_ITEM_TYPE_TRACK:
1190 strncat(tp, _("Track"), TEMP_STR_LEN-(tp-temp_str));
1191 tp += strlen(_("Track"));
1193 case VCDINFO_ITEM_TYPE_ENTRY:
1194 strncat(tp, _("Entry"), TEMP_STR_LEN-(tp-temp_str));
1195 tp += strlen(_("Entry"));
1197 case VCDINFO_ITEM_TYPE_SEGMENT:
1198 strncat(tp, _("Segment"), TEMP_STR_LEN-(tp-temp_str));
1199 tp += strlen(_("Segment"));
1201 case VCDINFO_ITEM_TYPE_LID:
1202 strncat(tp, _("List ID"), TEMP_STR_LEN-(tp-temp_str));
1203 tp += strlen(_("List ID"));
1205 case VCDINFO_ITEM_TYPE_SPAREID2:
1206 strncat(tp, _("Navigation"), TEMP_STR_LEN-(tp-temp_str));
1207 tp += strlen(_("Navigation"));
1213 saw_control_prefix = false;
1218 if (vcdplayer_pbc_is_on(p_vcd)) {
1220 sprintf(num_str, "%s %d", _("List ID"), p_vcd->cur_lid);
1221 strncat(tp, num_str, TEMP_STR_LEN-(tp-temp_str));
1222 tp += strlen(num_str);
1224 saw_control_prefix = false;
1228 add_format_str_info(mrl);
1232 add_format_num_info(itemid->num, "%d");
1236 add_format_str_info(vcdinfo_get_preparer_id(p_vcd->vcd));
1240 add_format_str_info(vcdinfo_get_publisher_id(p_vcd->vcd));
1244 if ( VCDINFO_ITEM_TYPE_SEGMENT==itemid->type ) {
1245 char seg_type_str[10];
1247 sprintf(seg_type_str, " %s",
1248 vcdinfo_video_type2str(p_vcd->vcd, itemid->num));
1249 strncat(tp, seg_type_str, TEMP_STR_LEN-(tp-temp_str));
1250 tp += strlen(seg_type_str);
1252 saw_control_prefix = false;
1256 add_format_num_info(p_vcd->cur_track, "%d");
1260 add_format_str_info(vcdinfo_get_volumeset_id(p_vcd->vcd));
1264 add_format_str_info(vcdinfo_get_volume_id(p_vcd->vcd));
1269 *tp++ = format_str[i];
1270 saw_control_prefix = false;
1273 return strdup(temp_str);
1277 VCDCreatePlayListItem(const input_thread_t *p_input,
1278 thread_vcd_data_t *p_vcd,
1279 playlist_t *p_playlist,
1280 const vcdinfo_itemid_t *itemid,
1281 char *psz_mrl, int psz_mrl_max,
1282 const char *psz_source, int playlist_operation,
1289 switch(itemid->type) {
1290 case VCDINFO_ITEM_TYPE_TRACK:
1293 case VCDINFO_ITEM_TYPE_SEGMENT:
1296 case VCDINFO_ITEM_TYPE_LID:
1299 case VCDINFO_ITEM_TYPE_ENTRY:
1307 snprintf(psz_mrl, psz_mrl_max, "%s%s@%c%u", VCD_MRL_PREFIX, psz_source,
1308 c_type, itemid->num);
1311 VCDFormatStr( p_input, p_vcd,
1312 config_GetPsz( p_input, MODULE_STRING "-title-format" ),
1315 playlist_Add( p_playlist, psz_mrl, p_title, playlist_operation, i_pos );
1318 VCDFormatStr( p_input, p_vcd,
1319 config_GetPsz( p_input, MODULE_STRING "-author-format" ),
1322 if( i_pos == PLAYLIST_END ) i_pos = p_playlist->i_size - 1;
1323 playlist_AddInfo(p_playlist, i_pos, _("General"), _("Author"), "%s",
1328 VCDFixupPlayList( input_thread_t *p_input, thread_vcd_data_t *p_vcd,
1329 const char *psz_source, vcdinfo_itemid_t *itemid,
1330 bool play_single_item )
1333 playlist_t * p_playlist;
1335 unsigned int psz_mrl_max = strlen(VCD_MRL_PREFIX) + strlen(psz_source) +
1336 strlen("@T") + strlen("100") + 1;
1338 psz_mrl = malloc( psz_mrl_max );
1340 if( psz_mrl == NULL )
1342 msg_Warn( p_input, "out of memory" );
1346 p_playlist = (playlist_t *) vlc_object_find( p_input, VLC_OBJECT_PLAYLIST,
1350 msg_Warn( p_input, "can't find playlist" );
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;
1412 The front-ends change spu-es - which sort of represents a symbolic
1413 name. Internally we use spu-channel which runs from 0..3.
1415 So we add a callback to intercept changes to spu-es and change them
1416 to updates to spu-channel. Ugly.
1421 VCDSPUCallback( vlc_object_t *p_this, const char *psz_variable,
1422 vlc_value_t old_val, vlc_value_t new_val, void *param)
1425 input_thread_t *p_input = (input_thread_t *)p_this;
1426 thread_vcd_data_t *p_vcd = (thread_vcd_data_t *)p_input->p_access_data;
1429 dbg_print( (INPUT_DBG_CALL|INPUT_DBG_EVENT), "old_val: %x, new_val %x",
1430 old_val.i_int, new_val.i_int);
1432 val.i_int = new_val.i_int;
1433 var_Set( p_this, "spu-channel", val );
1438 /*****************************************************************************
1440 read in meta-information about VCD: the number of tracks, segments,
1441 entries, size and starting information. Then set up state variables so
1442 that we read/seek starting at the location specified.
1444 On success we return VLC_SUCCESS, on memory exhausted VLC_ENOMEM,
1445 and VLC_EGENERIC for some other error.
1446 *****************************************************************************/
1448 E_(Open) ( vlc_object_t *p_this )
1450 input_thread_t * p_input = (input_thread_t *)p_this;
1451 thread_vcd_data_t * p_vcd;
1453 vcdinfo_itemid_t itemid;
1455 bool play_single_item = false;
1457 p_input->pf_read = VCDRead;
1458 p_input->pf_seek = VCDSeek;
1459 p_input->pf_set_area = VCDSetArea;
1460 p_input->pf_set_program = VCDSetProgram;
1462 p_vcd = malloc( sizeof(thread_vcd_data_t) );
1466 LOG_ERR ("out of memory" );
1470 p_input->p_access_data = (void *)p_vcd;
1471 p_vcd->i_debug = config_GetInt( p_this, MODULE_STRING "-debug" );
1472 p_vcd->in_still = false;
1473 p_vcd->play_item.type = VCDINFO_ITEM_TYPE_NOTFOUND;
1475 /* Set where to log errors messages from libcdio. */
1476 p_vcd_input = (input_thread_t *)p_this;
1477 cdio_log_set_handler ( cdio_log_handler );
1478 vcd_log_set_handler ( vcd_log_handler );
1480 psz_source = VCDParse( p_input, &itemid, &play_single_item );
1482 if ( NULL == psz_source )
1485 return( VLC_EGENERIC );
1488 dbg_print( (INPUT_DBG_CALL|INPUT_DBG_EXT), "source: %s: mrl: %s",
1489 psz_source, p_input->psz_name );
1491 p_vcd->p_segments = NULL;
1492 p_vcd->p_entries = NULL;
1495 p_input->i_mtu = VCD_DATA_ONCE;
1497 vlc_mutex_lock( &p_input->stream.stream_lock );
1499 /* If we are here we can control the pace... */
1500 p_input->stream.b_pace_control = 1;
1502 p_input->stream.b_seekable = 1;
1503 p_input->stream.p_selected_area->i_size = 0;
1504 p_input->stream.p_selected_area->i_tell = 0;
1506 vlc_mutex_unlock( &p_input->stream.stream_lock );
1508 if( !(p_vcd->vcd = vcd_Open( p_this, psz_source )) )
1510 msg_Warn( p_input, "could not open %s", psz_source );
1514 p_vcd->b_svd= vcdinfo_get_tracksSVD(p_vcd->vcd);;
1516 /* Get track information. */
1517 p_vcd->num_tracks = ioctl_GetTracksMap( VLC_OBJECT(p_input),
1518 vcdinfo_get_cd_image(p_vcd->vcd),
1519 &p_vcd->p_sectors );
1520 if( p_vcd->num_tracks < 0 )
1521 LOG_ERR ("unable to count tracks" );
1522 else if( p_vcd->num_tracks <= 1 )
1523 LOG_ERR ("no movie tracks found" );
1524 if( p_vcd->num_tracks <= 1)
1526 vcdinfo_close( p_vcd->vcd );
1530 /* Set stream and area data */
1531 vlc_mutex_lock( &p_input->stream.stream_lock );
1533 /* Initialize ES structures */
1534 input_InitStream( p_input, sizeof( stream_ps_data_t ) );
1536 /* disc input method */
1537 p_input->stream.i_method = INPUT_METHOD_VCD;
1539 p_input->stream.i_area_nb = 1;
1542 /* Initialize segment information. */
1543 VCDSegments( p_input );
1545 /* Initialize track area information. */
1546 VCDTracks( p_input );
1548 if( VCDEntryPoints( p_input ) < 0 )
1550 msg_Warn( p_input, "could not read entry points, will not use them" );
1551 p_vcd->b_valid_ep = false;
1554 if( VCDLIDs( p_input ) < 0 )
1556 msg_Warn( p_input, "could not read entry LIDs" );
1559 b_play_ok = (VLC_SUCCESS == VCDPlay( p_input, itemid ));
1561 vlc_mutex_unlock( &p_input->stream.stream_lock );
1563 if ( ! b_play_ok ) {
1564 vcdinfo_close( p_vcd->vcd );
1568 if( !p_input->psz_demux || !*p_input->psz_demux )
1571 p_input->psz_demux = "vcdx";
1573 p_input->psz_demux = "ps";
1577 p_vcd->p_intf = intf_Create( p_input, "vcdx" );
1578 p_vcd->p_intf->b_block = VLC_FALSE;
1579 intf_RunThread( p_vcd->p_intf );
1581 InformationCreate( p_input );
1583 if (play_single_item)
1584 VCDFixupPlayList( p_input, p_vcd, psz_source, &itemid,
1589 var_AddCallback( p_this, "spu-es", VCDSPUCallback, NULL );
1596 return VLC_EGENERIC;
1599 /*****************************************************************************
1600 * Close: closes VCD releasing allocated memory.
1601 *****************************************************************************/
1603 E_(Close) ( vlc_object_t *p_this )
1605 input_thread_t * p_input = (input_thread_t *)p_this;
1606 thread_vcd_data_t *p_vcd = (thread_vcd_data_t *)p_input->p_access_data;
1608 dbg_print( (INPUT_DBG_CALL|INPUT_DBG_EXT), "VCDClose" );
1610 var_DelCallback( p_this, "spu-es", VCDSPUCallback, NULL );
1611 vcdinfo_close( p_vcd->vcd );
1613 free( p_vcd->p_entries );
1614 free( p_vcd->p_segments );
1615 free( p_vcd->p_sectors );
1617 /* For reasons that are a mystery to me we don't have to deal with
1618 stopping, and destroying the p_vcd->p_intf thread. And if we do
1619 it causes problems upstream.
1621 if( p_vcd->p_intf != NULL )
1623 p_vcd->p_intf = NULL;
1627 p_input->p_access_data = NULL;