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, input_info_category_t *p_cat,
1017 playlist_t *p_playlist, char *title,
1020 thread_vcd_data_t *p_vcd = (thread_vcd_data_t *) p_input->p_access_data;
1021 playlist_item_t *p_item;
1023 dbg_print( INPUT_DBG_META, "field: %s: %s\n", title, str);
1024 input_AddInfo( p_cat, title, "%s", str );
1026 vlc_mutex_lock( &p_playlist->object_lock );
1027 p_item = playlist_ItemGetByPos( p_playlist, -1 );
1028 vlc_mutex_unlock( &p_playlist->object_lock );
1030 vlc_mutex_lock( &p_item->lock );
1031 playlist_ItemAddInfo( p_item, p_cat->psz_name, title,
1033 vlc_mutex_unlock( &p_item->lock );
1039 MetaInfoAddNum(input_thread_t *p_input, input_info_category_t *p_cat,
1040 playlist_t *p_playlist, char *title, int num)
1042 thread_vcd_data_t *p_vcd = (thread_vcd_data_t *) p_input->p_access_data;
1043 playlist_item_t *p_item;
1045 vlc_mutex_lock( &p_playlist->object_lock );
1046 p_item = playlist_ItemGetByPos( p_playlist, -1 );
1047 vlc_mutex_unlock( &p_playlist->object_lock );
1049 dbg_print( INPUT_DBG_META, "field %s: %d\n", title, num);
1050 input_AddInfo( p_cat, title, "%d", num );
1052 vlc_mutex_lock( &p_item->lock );
1053 playlist_ItemAddInfo( p_item , p_cat->psz_name, title, "%d",num );
1054 vlc_mutex_unlock( &p_item->lock );
1057 #define addstr(title, str) \
1058 MetaInfoAddStr( p_input, p_cat, p_playlist, title, str );
1060 #define addnum(title, num) \
1061 MetaInfoAddNum( p_input, p_cat, p_playlist, title, num );
1063 static void InformationCreate( input_thread_t *p_input )
1065 thread_vcd_data_t *p_vcd = (thread_vcd_data_t *) p_input->p_access_data;
1066 unsigned int i_nb = vcdinfo_get_num_entries(p_vcd->vcd);
1067 unsigned int last_entry = 0;
1068 input_info_category_t *p_cat;
1070 playlist_t *p_playlist = vlc_object_find( p_input, VLC_OBJECT_PLAYLIST,
1073 p_cat = input_InfoCategory( p_input, "General" );
1075 addstr( _("VCD Format"), vcdinfo_get_format_version_str(p_vcd->vcd) );
1076 addstr( _("Album"), vcdinfo_get_album_id(p_vcd->vcd));
1077 addstr( _("Application"), vcdinfo_get_application_id(p_vcd->vcd) );
1078 addstr( _("Preparer"), vcdinfo_get_preparer_id(p_vcd->vcd) );
1079 addnum( _("Vol #"), vcdinfo_get_volume_num(p_vcd->vcd) );
1080 addnum( _("Vol max #"), vcdinfo_get_volume_count(p_vcd->vcd) );
1081 addstr( _("Volume Set"), vcdinfo_get_volumeset_id(p_vcd->vcd) );
1082 addstr( _("Volume"), vcdinfo_get_volume_id(p_vcd->vcd) );
1083 addstr( _("Publisher"), vcdinfo_get_publisher_id(p_vcd->vcd) );
1084 addstr( _("System Id"), vcdinfo_get_system_id(p_vcd->vcd) );
1085 addnum( "LIDs", vcdinfo_get_num_LIDs(p_vcd->vcd) );
1086 addnum( _("Entries"), vcdinfo_get_num_entries(p_vcd->vcd) );
1087 addnum( _("Segments"), vcdinfo_get_num_segments(p_vcd->vcd) );
1088 addnum( _("Tracks"), vcdinfo_get_num_tracks(p_vcd->vcd) );
1090 /* Spit out track information. Could also include MSF info.
1093 #define TITLE_MAX 30
1094 for( i_track = 1 ; i_track < p_vcd->num_tracks ; i_track++ ) {
1095 char psz_track[TITLE_MAX];
1096 unsigned int audio_type = vcdinfo_get_track_audio_type(p_vcd->vcd,
1098 snprintf(psz_track, TITLE_MAX, "%s%02d", _("Track "), i_track);
1099 p_cat = input_InfoCategory( p_input, psz_track );
1102 addnum(_("Audio Channels"),
1103 vcdinfo_audio_type_num_channels(p_vcd->vcd, audio_type) );
1106 addnum(_("First Entry Point"), last_entry );
1107 for ( ; last_entry < i_nb
1108 && vcdinfo_get_track(p_vcd->vcd, last_entry) == i_track;
1110 addnum(_("Last Entry Point"), last_entry-1 );
1114 #define add_format_str_info(val) \
1116 const char *str = val; \
1118 if (val != NULL) { \
1121 strncat(tp, str, TEMP_STR_LEN-(tp-temp_str)); \
1124 saw_control_prefix = false; \
1128 #define add_format_num_info(val, fmt) \
1132 sprintf(num_str, fmt, val); \
1133 len=strlen(num_str); \
1135 strncat(tp, num_str, TEMP_STR_LEN-(tp-temp_str)); \
1138 saw_control_prefix = false; \
1142 Take a format string and expand escape sequences, that is sequences that
1143 begin with %, with information from the current VCD.
1144 The expanded string is returned. Here is a list of escape sequences:
1146 %A : The album information
1147 %C : The VCD volume count - the number of CD's in the collection.
1148 %c : The VCD volume num - the number of the CD in the collection.
1149 %F : The VCD Format, e.g. VCD 1.0, VCD 1.1, VCD 2.0, or SVCD
1150 %I : The current entry/segment/playback type, e.g. ENTRY, TRACK, SEGMENT...
1151 %L : The playlist ID prefixed with " LID" if it exists
1153 %N : The current number of the %I - a decimal number
1154 %P : The publisher ID
1155 %p : The preparer ID
1156 %S : If we are in a segment (menu), the kind of segment
1157 %T : The track number
1158 %V : The volume set ID
1160 A number between 1 and the volume count.
1164 VCDFormatStr(const input_thread_t *p_input, thread_vcd_data_t *p_vcd,
1165 const char format_str[], const char *mrl,
1166 const vcdinfo_itemid_t *itemid)
1168 #define TEMP_STR_SIZE 256
1169 #define TEMP_STR_LEN (TEMP_STR_SIZE-1)
1170 static char temp_str[TEMP_STR_SIZE];
1172 char * tp = temp_str;
1173 bool saw_control_prefix = false;
1174 size_t format_len = strlen(format_str);
1176 memset(temp_str, 0, TEMP_STR_SIZE);
1178 for (i=0; i<format_len; i++) {
1180 if (!saw_control_prefix && format_str[i] != '%') {
1181 *tp++ = format_str[i];
1182 saw_control_prefix = false;
1186 switch(format_str[i]) {
1188 if (saw_control_prefix) {
1191 saw_control_prefix = !saw_control_prefix;
1194 add_format_str_info(vcdinfo_strip_trail(vcdinfo_get_album_id(p_vcd->vcd),
1199 add_format_num_info(vcdinfo_get_volume_num(p_vcd->vcd), "%d");
1203 add_format_num_info(vcdinfo_get_volume_count(p_vcd->vcd), "%d");
1207 add_format_str_info(vcdinfo_get_format_version_str(p_vcd->vcd));
1212 switch (itemid->type) {
1213 case VCDINFO_ITEM_TYPE_TRACK:
1214 strncat(tp, _("Track"), TEMP_STR_LEN-(tp-temp_str));
1215 tp += strlen(_("Track"));
1217 case VCDINFO_ITEM_TYPE_ENTRY:
1218 strncat(tp, _("Entry"), TEMP_STR_LEN-(tp-temp_str));
1219 tp += strlen(_("Entry"));
1221 case VCDINFO_ITEM_TYPE_SEGMENT:
1222 strncat(tp, _("Segment"), TEMP_STR_LEN-(tp-temp_str));
1223 tp += strlen(_("Segment"));
1225 case VCDINFO_ITEM_TYPE_LID:
1226 strncat(tp, _("List ID"), TEMP_STR_LEN-(tp-temp_str));
1227 tp += strlen(_("List ID"));
1229 case VCDINFO_ITEM_TYPE_SPAREID2:
1230 strncat(tp, _("Navigation"), TEMP_STR_LEN-(tp-temp_str));
1231 tp += strlen(_("Navigation"));
1237 saw_control_prefix = false;
1242 if (vcdplayer_pbc_is_on(p_vcd)) {
1244 sprintf(num_str, "%s %d", _("List ID"), p_vcd->cur_lid);
1245 strncat(tp, num_str, TEMP_STR_LEN-(tp-temp_str));
1246 tp += strlen(num_str);
1248 saw_control_prefix = false;
1252 add_format_str_info(mrl);
1256 add_format_num_info(itemid->num, "%d");
1260 add_format_str_info(vcdinfo_get_preparer_id(p_vcd->vcd));
1264 add_format_str_info(vcdinfo_get_publisher_id(p_vcd->vcd));
1268 if ( VCDINFO_ITEM_TYPE_SEGMENT==itemid->type ) {
1269 char seg_type_str[10];
1271 sprintf(seg_type_str, " %s",
1272 vcdinfo_video_type2str(p_vcd->vcd, itemid->num));
1273 strncat(tp, seg_type_str, TEMP_STR_LEN-(tp-temp_str));
1274 tp += strlen(seg_type_str);
1276 saw_control_prefix = false;
1280 add_format_num_info(p_vcd->cur_track, "%d");
1284 add_format_str_info(vcdinfo_get_volumeset_id(p_vcd->vcd));
1288 add_format_str_info(vcdinfo_get_volume_id(p_vcd->vcd));
1293 *tp++ = format_str[i];
1294 saw_control_prefix = false;
1297 return strdup(temp_str);
1301 VCDCreatePlayListItem(const input_thread_t *p_input,
1302 thread_vcd_data_t *p_vcd,
1303 playlist_t *p_playlist,
1304 const vcdinfo_itemid_t *itemid,
1305 char *psz_mrl, int psz_mrl_max,
1306 const char *psz_source, int playlist_operation,
1313 switch(itemid->type) {
1314 case VCDINFO_ITEM_TYPE_TRACK:
1317 case VCDINFO_ITEM_TYPE_SEGMENT:
1320 case VCDINFO_ITEM_TYPE_LID:
1323 case VCDINFO_ITEM_TYPE_ENTRY:
1331 snprintf(psz_mrl, psz_mrl_max, "%s%s@%c%u", VCD_MRL_PREFIX, psz_source,
1332 c_type, itemid->num);
1335 VCDFormatStr( p_input, p_vcd,
1336 config_GetPsz( p_input, MODULE_STRING "-title-format" ),
1339 playlist_Add( p_playlist, psz_mrl, p_title, playlist_operation, i_pos );
1342 VCDFormatStr( p_input, p_vcd,
1343 config_GetPsz( p_input, MODULE_STRING "-author-format" ),
1346 if( i_pos == PLAYLIST_END ) i_pos = p_playlist->i_size - 1;
1347 playlist_AddInfo(p_playlist, i_pos, _("General"), _("Author"), "%s",
1352 VCDFixupPlayList( input_thread_t *p_input, thread_vcd_data_t *p_vcd,
1353 const char *psz_source, vcdinfo_itemid_t *itemid,
1354 bool play_single_item )
1357 playlist_t * p_playlist;
1359 unsigned int psz_mrl_max = strlen(VCD_MRL_PREFIX) + strlen(psz_source) +
1360 strlen("@T") + strlen("100") + 1;
1362 psz_mrl = malloc( psz_mrl_max );
1364 if( psz_mrl == NULL )
1366 msg_Warn( p_input, "out of memory" );
1370 p_playlist = (playlist_t *) vlc_object_find( p_input, VLC_OBJECT_PLAYLIST,
1374 msg_Warn( p_input, "can't find playlist" );
1379 if ( play_single_item )
1381 /* May fill out more information when the playlist user interface becomes
1384 VCDCreatePlayListItem(p_input, p_vcd, p_playlist, itemid,
1385 psz_mrl, psz_mrl_max, psz_source, PLAYLIST_REPLACE,
1386 p_playlist->i_index);
1391 vcdinfo_itemid_t list_itemid;
1392 list_itemid.type=VCDINFO_ITEM_TYPE_ENTRY;
1394 playlist_Delete( p_playlist, p_playlist->i_index);
1396 for( i = 0 ; i < p_vcd->num_entries ; i++ )
1399 VCDCreatePlayListItem(p_input, p_vcd, p_playlist, &list_itemid,
1400 psz_mrl, psz_mrl_max, psz_source,
1401 PLAYLIST_APPEND, PLAYLIST_END);
1404 playlist_Command( p_playlist, PLAYLIST_GOTO, 0 );
1408 vlc_object_release( p_playlist );
1413 /*****************************************************************************
1415 *****************************************************************************/
1417 E_(DebugCallback) ( vlc_object_t *p_this, const char *psz_name,
1418 vlc_value_t oldval, vlc_value_t val, void *p_data )
1420 thread_vcd_data_t *p_vcd;
1422 if (NULL == p_vcd_input) return VLC_EGENERIC;
1424 p_vcd = (thread_vcd_data_t *)p_vcd_input->p_access_data;
1426 if (p_vcd->i_debug & (INPUT_DBG_CALL|INPUT_DBG_EXT)) {
1427 msg_Dbg( p_vcd_input, "Old debug (x%0x) %d, new debug (x%0x) %d",
1428 p_vcd->i_debug, p_vcd->i_debug, val.i_int, val.i_int);
1430 p_vcd->i_debug = val.i_int;
1436 The front-ends change spu-es - which sort of represents a symbolic
1437 name. Internally we use spu-channel which runs from 0..3.
1439 So we add a callback to intercept changes to spu-es and change them
1440 to updates to spu-channel. Ugly.
1445 VCDSPUCallback( vlc_object_t *p_this, const char *psz_variable,
1446 vlc_value_t old_val, vlc_value_t new_val, void *param)
1449 input_thread_t *p_input = (input_thread_t *)p_this;
1450 thread_vcd_data_t *p_vcd = (thread_vcd_data_t *)p_input->p_access_data;
1453 dbg_print( (INPUT_DBG_CALL|INPUT_DBG_EVENT), "old_val: %x, new_val %x",
1454 old_val.i_int, new_val.i_int);
1456 val.i_int = new_val.i_int;
1457 var_Set( p_this, "spu-channel", val );
1462 /*****************************************************************************
1464 read in meta-information about VCD: the number of tracks, segments,
1465 entries, size and starting information. Then set up state variables so
1466 that we read/seek starting at the location specified.
1468 On success we return VLC_SUCCESS, on memory exhausted VLC_ENOMEM,
1469 and VLC_EGENERIC for some other error.
1470 *****************************************************************************/
1472 E_(Open) ( vlc_object_t *p_this )
1474 input_thread_t * p_input = (input_thread_t *)p_this;
1475 thread_vcd_data_t * p_vcd;
1477 vcdinfo_itemid_t itemid;
1479 bool play_single_item = false;
1481 p_input->pf_read = VCDRead;
1482 p_input->pf_seek = VCDSeek;
1483 p_input->pf_set_area = VCDSetArea;
1484 p_input->pf_set_program = VCDSetProgram;
1486 p_vcd = malloc( sizeof(thread_vcd_data_t) );
1490 LOG_ERR ("out of memory" );
1494 p_input->p_access_data = (void *)p_vcd;
1495 p_vcd->i_debug = config_GetInt( p_this, MODULE_STRING "-debug" );
1496 p_vcd->in_still = false;
1497 p_vcd->play_item.type = VCDINFO_ITEM_TYPE_NOTFOUND;
1499 /* Set where to log errors messages from libcdio. */
1500 p_vcd_input = (input_thread_t *)p_this;
1501 cdio_log_set_handler ( cdio_log_handler );
1502 vcd_log_set_handler ( vcd_log_handler );
1504 psz_source = VCDParse( p_input, &itemid, &play_single_item );
1506 if ( NULL == psz_source )
1509 return( VLC_EGENERIC );
1512 dbg_print( (INPUT_DBG_CALL|INPUT_DBG_EXT), "source: %s: mrl: %s",
1513 psz_source, p_input->psz_name );
1515 p_vcd->p_segments = NULL;
1516 p_vcd->p_entries = NULL;
1519 p_input->i_mtu = VCD_DATA_ONCE;
1521 vlc_mutex_lock( &p_input->stream.stream_lock );
1523 /* If we are here we can control the pace... */
1524 p_input->stream.b_pace_control = 1;
1526 p_input->stream.b_seekable = 1;
1527 p_input->stream.p_selected_area->i_size = 0;
1528 p_input->stream.p_selected_area->i_tell = 0;
1530 vlc_mutex_unlock( &p_input->stream.stream_lock );
1532 if( !(p_vcd->vcd = vcd_Open( p_this, psz_source )) )
1534 msg_Warn( p_input, "could not open %s", psz_source );
1538 p_vcd->b_svd= vcdinfo_get_tracksSVD(p_vcd->vcd);;
1540 /* Get track information. */
1541 p_vcd->num_tracks = ioctl_GetTracksMap( VLC_OBJECT(p_input),
1542 vcdinfo_get_cd_image(p_vcd->vcd),
1543 &p_vcd->p_sectors );
1544 if( p_vcd->num_tracks < 0 )
1545 LOG_ERR ("unable to count tracks" );
1546 else if( p_vcd->num_tracks <= 1 )
1547 LOG_ERR ("no movie tracks found" );
1548 if( p_vcd->num_tracks <= 1)
1550 vcdinfo_close( p_vcd->vcd );
1554 /* Set stream and area data */
1555 vlc_mutex_lock( &p_input->stream.stream_lock );
1557 /* Initialize ES structures */
1558 input_InitStream( p_input, sizeof( stream_ps_data_t ) );
1560 /* disc input method */
1561 p_input->stream.i_method = INPUT_METHOD_VCD;
1563 p_input->stream.i_area_nb = 1;
1566 /* Initialize segment information. */
1567 VCDSegments( p_input );
1569 /* Initialize track area information. */
1570 VCDTracks( p_input );
1572 if( VCDEntryPoints( p_input ) < 0 )
1574 msg_Warn( p_input, "could not read entry points, will not use them" );
1575 p_vcd->b_valid_ep = false;
1578 if( VCDLIDs( p_input ) < 0 )
1580 msg_Warn( p_input, "could not read entry LIDs" );
1583 b_play_ok = (VLC_SUCCESS == VCDPlay( p_input, itemid ));
1585 vlc_mutex_unlock( &p_input->stream.stream_lock );
1587 if ( ! b_play_ok ) {
1588 vcdinfo_close( p_vcd->vcd );
1592 if( !p_input->psz_demux || !*p_input->psz_demux )
1595 p_input->psz_demux = "vcdx";
1597 p_input->psz_demux = "ps";
1601 p_vcd->p_intf = intf_Create( p_input, "vcdx" );
1602 p_vcd->p_intf->b_block = VLC_FALSE;
1603 intf_RunThread( p_vcd->p_intf );
1605 InformationCreate( p_input );
1607 if (play_single_item)
1608 VCDFixupPlayList( p_input, p_vcd, psz_source, &itemid,
1613 var_AddCallback( p_this, "spu-es", VCDSPUCallback, NULL );
1620 return VLC_EGENERIC;
1623 /*****************************************************************************
1624 * Close: closes VCD releasing allocated memory.
1625 *****************************************************************************/
1627 E_(Close) ( vlc_object_t *p_this )
1629 input_thread_t * p_input = (input_thread_t *)p_this;
1630 thread_vcd_data_t *p_vcd = (thread_vcd_data_t *)p_input->p_access_data;
1632 dbg_print( (INPUT_DBG_CALL|INPUT_DBG_EXT), "VCDClose" );
1634 var_DelCallback( p_this, "spu-es", VCDSPUCallback, NULL );
1635 vcdinfo_close( p_vcd->vcd );
1637 free( p_vcd->p_entries );
1638 free( p_vcd->p_segments );
1639 free( p_vcd->p_sectors );
1641 /* For reasons that are a mystery to me we don't have to deal with
1642 stopping, and destroying the p_vcd->p_intf thread. And if we do
1643 it causes problems upstream.
1645 if( p_vcd->p_intf != NULL )
1647 p_vcd->p_intf = NULL;
1651 p_input->p_access_data = NULL;