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.15 2004/01/05 13:07:02 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 *varname, const char *label );
81 static vcdinfo_obj_t *vcd_Open ( vlc_object_t *p_this, const char *psz_dev );
83 /****************************************************************************
85 ****************************************************************************/
87 /* FIXME: This variable is a hack. Would be nice to eliminate the
90 static input_thread_t *p_vcd_input = NULL;
92 /* process messages that originate from libcdio. */
94 cdio_log_handler (cdio_log_level_t level, const char message[])
96 thread_vcd_data_t *p_vcd = (thread_vcd_data_t *)p_vcd_input->p_access_data;
100 if (p_vcd->i_debug & INPUT_DBG_CDIO)
101 msg_Dbg( p_vcd_input, message);
104 msg_Warn( p_vcd_input, message);
107 case CDIO_LOG_ASSERT:
108 msg_Err( p_vcd_input, message);
111 msg_Warn( p_vcd_input, message,
112 _("The above message had unknown log level"),
118 /* process messages that originate from vcdinfo. */
120 vcd_log_handler (vcd_log_level_t level, const char message[])
122 thread_vcd_data_t *p_vcd = (thread_vcd_data_t *)p_vcd_input->p_access_data;
126 if (p_vcd->i_debug & INPUT_DBG_VCDINFO)
127 msg_Dbg( p_vcd_input, message);
130 msg_Warn( p_vcd_input, message);
134 msg_Err( p_vcd_input, message);
137 msg_Warn( p_vcd_input, "%s\n%s %d", message,
138 _("The above message had unknown vcdimager log level"),
144 /*****************************************************************************
145 * VCDRead: reads i_len bytes from the VCD into p_buffer.
146 *****************************************************************************
147 * Returns -1 in case of error, 0 in case of EOF, otherwise the number of
149 *****************************************************************************/
151 VCDRead( input_thread_t * p_input, byte_t * p_buffer, size_t i_len )
153 thread_vcd_data_t * p_vcd= (thread_vcd_data_t *)p_input->p_access_data;
157 byte_t p_last_sector[ M2F2_SECTOR_SIZE ];
161 dbg_print( (INPUT_DBG_CALL), "lsn: %u", p_vcd->cur_lsn );
163 /* Compute the number of blocks we have to read */
165 i_blocks = i_len / M2F2_SECTOR_SIZE;
167 for ( i_index = 0 ; i_index < i_blocks ; i_index++ )
170 if ( p_vcd->cur_lsn == p_vcd->end_lsn ) {
171 vcdplayer_read_status_t read_status;
173 /* We've run off of the end of this entry. Do we continue or stop? */
174 dbg_print( (INPUT_DBG_LSN|INPUT_DBG_PBC),
175 "end reached, cur: %u", p_vcd->cur_lsn );
177 read_status = vcdplayer_pbc_is_on( p_vcd )
178 ? vcdplayer_pbc_nav( p_input )
179 : vcdplayer_non_pbc_nav( p_input );
181 switch (read_status) {
183 /* End reached. Return NULL to indicated this. */
185 /* Some sort of error. */
188 case READ_STILL_FRAME:
190 /* Reached the end of a still frame. */
192 byte_t * p_buf = p_buffer;
193 pgrm_descriptor_t * p_pgrm = p_input->stream.p_selected_program;;
195 p_buf += (i_index*M2F2_SECTOR_SIZE);
196 memset(p_buf, 0, M2F2_SECTOR_SIZE);
199 dbg_print(INPUT_DBG_STILL, "Handled still event");
202 p_vcd->p_intf->p_sys->b_still = 1;
203 input_SetStatus( p_input, INPUT_STATUS_PAUSE );
206 vlc_mutex_lock( &p_input->stream.stream_lock );
208 p_pgrm = p_input->stream.p_selected_program;
209 p_pgrm->i_synchro_state = SYNCHRO_REINIT;
211 vlc_mutex_unlock( &p_input->stream.stream_lock );
213 input_ClockManageControl( p_input, p_pgrm, 0 );
215 p_vcd->p_intf->p_sys->b_still = 1;
216 input_SetStatus( p_input, INPUT_STATUS_PAUSE );
218 return i_read + M2F2_SECTOR_SIZE;
226 if ( VCDReadSector( VLC_OBJECT(p_input), p_vcd->vcd,
228 p_buffer + (i_index*M2F2_SECTOR_SIZE) ) < 0 )
230 LOG_ERR ("could not read sector %d", p_vcd->cur_lsn );
237 if( p_vcd->b_valid_ep &&
238 /* FIXME kludge so that read does not update chapter
239 * when a manual chapter change was requested and not
240 * yet accomplished */
241 !p_input->stream.p_new_area )
243 unsigned int i_entry = p_input->stream.p_selected_area->i_part;
245 vlc_mutex_lock( &p_input->stream.stream_lock );
247 if( i_entry < p_vcd->num_entries &&
248 p_vcd->cur_lsn >= p_vcd->p_entries[i_entry+1] )
250 dbg_print( INPUT_DBG_PBC,
251 "new entry, i_entry %d, sector %d, es %d",
252 i_entry, p_vcd->cur_lsn,
253 p_vcd->p_entries[i_entry] );
254 p_vcd->play_item.num =
255 ++ p_input->stream.p_selected_area->i_part;
256 p_vcd->play_item.type = VCDINFO_ITEM_TYPE_ENTRY;
257 VCDUpdateVar( p_input, p_vcd->play_item.num, VLC_VAR_SETVALUE,
258 "chapter", "Setting entry" );
260 vlc_mutex_unlock( &p_input->stream.stream_lock );
263 i_read += M2F2_SECTOR_SIZE;
266 if ( i_len % M2F2_SECTOR_SIZE ) /* this should not happen */
268 if ( VCDReadSector( VLC_OBJECT(p_input), p_vcd->vcd,
269 p_vcd->cur_lsn, p_last_sector ) < 0 )
271 LOG_ERR ("could not read sector %d", p_vcd->cur_lsn );
275 p_input->p_vlc->pf_memcpy( p_buffer + i_blocks * M2F2_SECTOR_SIZE,
276 p_last_sector, i_len % M2F2_SECTOR_SIZE );
277 i_read += i_len % M2F2_SECTOR_SIZE;
284 /*****************************************************************************
285 * VCDSetProgram: Does nothing since a VCD is mono_program
286 *****************************************************************************/
288 VCDSetProgram( input_thread_t * p_input, pgrm_descriptor_t * p_program)
290 thread_vcd_data_t * p_vcd= (thread_vcd_data_t *)p_input->p_access_data;
291 dbg_print( (INPUT_DBG_CALL|INPUT_DBG_EXT), "VCDSetProgram" );
296 /*****************************************************************************
297 * VCDSetArea: initialize internal data structures and input stream data
298 so set subsequent reading and seeking to reflect that we are
299 at track x, entry or segment y.
300 This is called for each user navigation request, e.g. the GUI
301 Chapter/Title selections or in initial MRL parsing.
302 ****************************************************************************/
304 VCDSetArea( input_thread_t * p_input, input_area_t * p_area )
306 thread_vcd_data_t *p_vcd = (thread_vcd_data_t*)p_input->p_access_data;
307 unsigned int i_entry = p_area->i_part;
308 track_t i_track = p_area->i_id;
309 int old_seekable = p_input->stream.b_seekable;
310 unsigned int i_nb = p_area->i_plugin_data + p_area->i_part_nb;
312 dbg_print( (INPUT_DBG_CALL|INPUT_DBG_EXT),
313 "track: %d, entry %d, seekable %d, area %lx, select area %lx ",
314 i_track, i_entry, old_seekable,
315 (long unsigned int) p_area,
316 (long unsigned int) p_input->stream.p_selected_area );
318 /* we can't use the interface slider until initilization is complete */
319 p_input->stream.b_seekable = 0;
321 if( p_area != p_input->stream.p_selected_area )
325 /* If is the result of a track change, make the entry valid. */
326 if (i_entry < p_area->i_plugin_data || i_entry >= i_nb)
327 i_entry = p_area->i_plugin_data;
329 /* Change the default area */
330 p_input->stream.p_selected_area = p_area;
332 /* Update the navigation variables without triggering a callback */
334 VCDUpdateVar( p_input, i_track, VLC_VAR_SETVALUE, "title",
337 var_Change( p_input, "chapter", VLC_VAR_CLEARCHOICES, NULL, NULL );
338 for( i = p_area->i_plugin_data; i < i_nb; i++ )
340 VCDUpdateVar( p_input, i , VLC_VAR_ADDCHOICE,
341 "chapter", "Adding entry choice");
345 unsigned int audio_type =
346 vcdinfo_get_track_audio_type(p_vcd->vcd, i_track);
347 unsigned int i_channels =
348 vcdinfo_audio_type_num_channels(p_vcd->vcd, audio_type);
350 var_Change( p_input, "audio_channels", VLC_VAR_CLEARCHOICES, NULL,
353 for( i = 0; i < i_channels; i++ )
355 VCDUpdateVar( p_input, i , VLC_VAR_ADDCHOICE,
356 "audio_channels", "Adding audio choice");
363 VCDSetOrigin( p_input, p_vcd->p_segments[i_entry],
364 p_vcd->p_segments[i_entry], p_vcd->p_segments[i_entry+1],
367 VCDSetOrigin( p_input, p_vcd->p_sectors[i_track],
368 vcdinfo_get_entry_lsn(p_vcd->vcd, i_entry),
369 p_vcd->p_sectors[i_track+1],
372 p_input->stream.b_seekable = old_seekable;
373 /* warn interface that something has changed */
374 p_input->stream.b_changed = 1;
380 /****************************************************************************
382 ****************************************************************************/
384 VCDSeek( input_thread_t * p_input, off_t i_off )
386 thread_vcd_data_t * p_vcd;
387 unsigned int i_entry=0; /* invalid entry */
389 p_vcd = (thread_vcd_data_t *) p_input->p_access_data;
391 p_vcd->cur_lsn = p_vcd->origin_lsn + (i_off / (off_t)M2F2_SECTOR_SIZE);
393 vlc_mutex_lock( &p_input->stream.stream_lock );
394 #define p_area p_input->stream.p_selected_area
396 if( p_vcd->b_valid_ep )
398 for( i_entry = 0 ; i_entry < p_vcd->num_entries ; i_entry ++ )
400 if( p_vcd->cur_lsn < p_vcd->p_entries[i_entry] )
402 VCDUpdateVar( p_input, i_entry, VLC_VAR_SETVALUE,
403 "chapter", "Setting entry" );
407 p_vcd->play_item.num = p_area->i_part = i_entry;
408 p_vcd->play_item.type = VCDINFO_ITEM_TYPE_ENTRY;
412 p_input->stream.p_selected_area->i_tell = i_off;
414 dbg_print( (INPUT_DBG_CALL|INPUT_DBG_EXT|INPUT_DBG_SEEK),
415 "orig %d, cur: %d, offset: %lld, start: %lld, entry %d",
416 p_vcd->origin_lsn, p_vcd->cur_lsn, i_off,
417 p_input->stream.p_selected_area->i_start, i_entry );
419 vlc_mutex_unlock( &p_input->stream.stream_lock );
422 /*****************************************************************************
423 VCDPlay: set up internal structures so seeking/reading places an item.
424 itemid: the thing to play.
425 user_entry: true if itemid is a user selection (rather than internally-
426 generated selection such as via PBC) in which case we may have to adjust
427 for differences in numbering.
428 *****************************************************************************/
430 VCDPlay( input_thread_t *p_input, vcdinfo_itemid_t itemid )
432 thread_vcd_data_t * p_vcd= (thread_vcd_data_t *)p_input->p_access_data;
433 input_area_t * p_area;
435 dbg_print(INPUT_DBG_CALL, "itemid.num: %d, itemid.type: %d\n",
436 itemid.num, itemid.type);
438 if (!p_input->p_access_data) return VLC_EGENERIC;
440 p_vcd->in_still = false;
441 p_vcd->cur_lid = VCDINFO_INVALID_LID;
443 #define area p_input->stream.pp_areas
445 switch (itemid.type) {
446 case VCDINFO_ITEM_TYPE_TRACK:
448 /* Valid tracks go from 1...num_tracks-1, because track 0 is unplayable.
451 if (itemid.num == 0 || itemid.num >= p_vcd->num_tracks) {
452 LOG_ERR ("Invalid track number %d", itemid.num );
455 p_area = area[itemid.num];
456 p_area->i_part = p_area->i_plugin_data;
457 p_input->stream.b_seekable = 1;
459 case VCDINFO_ITEM_TYPE_SEGMENT:
460 /* Valid segments go from 0...num_segments-1. */
461 if (itemid.num >= p_vcd->num_segments) {
462 LOG_ERR ( "Invalid segment number: %d", itemid.num );
465 vcdinfo_video_segment_type_t segtype =
466 vcdinfo_get_video_type(p_vcd->vcd, itemid.num);
468 dbg_print(INPUT_DBG_PBC, "%s (%d), seg_num: %d",
469 vcdinfo_video_type2str(p_vcd->vcd, itemid.num),
470 (int) segtype, itemid.num);
473 p_area->i_part = itemid.num;
477 case VCDINFO_FILES_VIDEO_NTSC_STILL:
478 case VCDINFO_FILES_VIDEO_NTSC_STILL2:
479 case VCDINFO_FILES_VIDEO_PAL_STILL:
480 case VCDINFO_FILES_VIDEO_PAL_STILL2:
481 p_input->stream.b_seekable = 0;
482 p_vcd->in_still = true;
485 p_input->stream.b_seekable = 1;
486 p_vcd->in_still = false;
491 case VCDINFO_ITEM_TYPE_LID:
492 /* LIDs go from 1..num_lids. */
493 if (itemid.num == 0 || itemid.num > p_vcd->num_lids) {
494 LOG_ERR ( "Invalid LID number: %d", itemid.num );
497 p_vcd->cur_lid = itemid.num;
498 vcdinfo_lid_get_pxd(p_vcd->vcd, &(p_vcd->pxd), itemid.num);
500 switch (p_vcd->pxd.descriptor_type) {
502 case PSD_TYPE_SELECTION_LIST:
503 case PSD_TYPE_EXT_SELECTION_LIST: {
504 vcdinfo_itemid_t trans_itemid;
505 uint16_t trans_itemid_num;
507 if (p_vcd->pxd.psd == NULL) return VLC_EGENERIC;
508 trans_itemid_num = vcdinf_psd_get_itemid(p_vcd->pxd.psd);
509 vcdinfo_classify_itemid(trans_itemid_num, &trans_itemid);
510 p_vcd->loop_count = 1;
511 p_vcd->loop_item = trans_itemid;
512 return VCDPlay( p_input, trans_itemid );
516 case PSD_TYPE_PLAY_LIST: {
517 if (p_vcd->pxd.pld == NULL) return VLC_EGENERIC;
519 return vcdplayer_inc_play_item(p_input)
520 ? VLC_SUCCESS : VLC_EGENERIC;
524 case PSD_TYPE_END_LIST:
525 case PSD_TYPE_COMMAND_LIST:
532 case VCDINFO_ITEM_TYPE_ENTRY:
533 /* Entries go from 0..num_entries-1. */
534 if (itemid.num >= p_vcd->num_entries) {
535 LOG_ERR ("Invalid entry number: %d", itemid.num );
538 track_t cur_track = vcdinfo_get_track(p_vcd->vcd, itemid.num);
539 p_area = area[cur_track];
540 p_area->i_part = itemid.num;
541 p_input->stream.b_seekable = 1;
545 LOG_ERR ("unknown entry type" );
549 VCDSetArea( p_input, p_area );
553 p_vcd->play_item = itemid;
555 dbg_print( (INPUT_DBG_CALL),
556 "i_start %lld, i_size: %lld, i_tell: %lld, lsn %d",
557 p_area->i_start, p_area->i_size,
558 p_area->i_tell, p_vcd->cur_lsn );
563 /*****************************************************************************
564 VCDEntryPoints: Reads the information about the entry points on the disc
565 and initializes area information with that.
566 Before calling this track information should have been read in.
567 *****************************************************************************/
569 VCDEntryPoints( input_thread_t * p_input )
571 thread_vcd_data_t * p_vcd;
573 unsigned int i, i_entry_index = 0;
574 unsigned int i_previous_track = CDIO_INVALID_TRACK;
576 p_vcd = (thread_vcd_data_t *) p_input->p_access_data;
578 i_nb = vcdinfo_get_num_entries(p_vcd->vcd);
582 p_vcd->p_entries = malloc( sizeof( lba_t ) * i_nb );
584 if( p_vcd->p_entries == NULL )
586 LOG_ERR ("not enough memory for entry points treatment" );
590 p_vcd->num_entries = 0;
592 for( i = 0 ; i < i_nb ; i++ )
594 track_t i_track = vcdinfo_get_track(p_vcd->vcd, i);
595 if( i_track <= p_input->stream.i_area_nb )
597 p_vcd->p_entries[i] =
598 vcdinfo_get_entry_lsn(p_vcd->vcd, i);
599 p_input->stream.pp_areas[i_track]->i_part_nb ++;
601 /* if this entry belongs to a new track */
602 if( i_track != i_previous_track )
604 /* i_plugin_data is used to store the first entry of the area*/
605 p_input->stream.pp_areas[i_track]->i_plugin_data =
607 i_previous_track = i_track;
608 p_input->stream.pp_areas[i_track]->i_part_nb = 1;
611 p_vcd->num_entries ++;
614 msg_Warn( p_input, "wrong track number found in entry points" );
616 p_vcd->b_valid_ep = true;
620 /*****************************************************************************
621 * VCDSegments: Reads the information about the segments the disc.
622 *****************************************************************************/
624 VCDSegments( input_thread_t * p_input )
626 thread_vcd_data_t * p_vcd;
628 unsigned int num_segments;
631 p_vcd = (thread_vcd_data_t *) p_input->p_access_data;
632 num_segments = p_vcd->num_segments = vcdinfo_get_num_segments(p_vcd->vcd);
634 #define area p_input->stream.pp_areas
636 /* area 0 is reserved for segments. Set Absolute start offset
638 area[0]->i_plugin_data = 0;
639 input_DelArea( p_input, area[0] );
640 input_AddArea( p_input, 0, 0 );
642 area[0]->i_start = (off_t)p_vcd->p_sectors[0]
643 * (off_t)M2F2_SECTOR_SIZE;
644 area[0]->i_size = (off_t)(p_vcd->p_sectors[1] - p_vcd->p_sectors[0])
645 * (off_t)M2F2_SECTOR_SIZE;
647 /* Default Segment */
650 /* i_plugin_data is used to store which entry point is the first
651 of the track (area) */
652 area[0]->i_plugin_data = 0;
654 area[0]->i_part_nb = 0;
656 dbg_print( INPUT_DBG_MRL,
657 "area[0] id: %d, i_start: %lld, i_size: %lld",
658 area[0]->i_id, area[0]->i_start, area[0]->i_size );
660 if (num_segments == 0) return 0;
662 /* We have one additional segment allocated so we can get the size
663 by subtracting seg[i+1] - seg[i].
665 p_vcd->p_segments = malloc( sizeof( lba_t ) * (num_segments+1) );
666 if( p_vcd->p_segments == NULL )
668 LOG_ERR ("not enough memory for segment treatment" );
672 /* Update the navigation variables without triggering a callback */
673 VCDUpdateVar( p_input, 0, VLC_VAR_SETVALUE, "title", "Setting track" );
674 var_Change( p_input, "chapter", VLC_VAR_CLEARCHOICES, NULL, NULL );
676 for( i = 0 ; i < num_segments ; i++ )
678 p_vcd->p_segments[i] = vcdinfo_get_seg_lsn(p_vcd->vcd, i);
679 area[0]->i_part_nb ++;
680 VCDUpdateVar( p_input, i , VLC_VAR_ADDCHOICE,
681 "chapter", "Adding segment choice");
686 p_vcd->p_segments[num_segments] = p_vcd->p_segments[num_segments-1]+
687 vcdinfo_get_seg_sector_count(p_vcd->vcd, num_segments-1);
692 /*****************************************************************************
693 VCDTracks: initializes area information.
694 Before calling this track information should have been read in.
695 *****************************************************************************/
697 VCDTracks( input_thread_t * p_input )
699 thread_vcd_data_t * p_vcd;
702 p_vcd = (thread_vcd_data_t *) p_input->p_access_data;
704 #define area p_input->stream.pp_areas
706 /* We start area addressing for tracks at 1 since the default area 0
707 is reserved for segments */
709 for( i = 1 ; i < p_vcd->num_tracks ; i++ )
711 /* Tracks are Program Chains */
712 input_AddArea( p_input, i, i );
714 /* Absolute start byte offset and byte size */
715 area[i]->i_start = (off_t) p_vcd->p_sectors[i]
716 * (off_t)M2F2_SECTOR_SIZE;
717 area[i]->i_size = (off_t)(p_vcd->p_sectors[i+1] - p_vcd->p_sectors[i])
718 * (off_t)M2F2_SECTOR_SIZE;
720 /* Current entry being played in track */
723 /* i_plugin_data is used to store which entry point is the first
724 * of the track (area) */
725 area[i]->i_plugin_data = 0;
727 dbg_print( INPUT_DBG_MRL,
728 "area[%d] id: %d, i_start: %lld, i_size: %lld",
729 i, area[i]->i_id, area[i]->i_start, area[i]->i_size );
737 /*****************************************************************************
738 VCDLIDs: Reads the LIST IDs from the LOT.
739 *****************************************************************************/
741 VCDLIDs( input_thread_t * p_input )
743 thread_vcd_data_t *p_vcd = (thread_vcd_data_t *) p_input->p_access_data;
745 p_vcd->num_lids = vcdinfo_get_num_LIDs(p_vcd->vcd);
746 p_vcd->cur_lid = VCDINFO_INVALID_ENTRY;
748 if (vcdinfo_read_psd (p_vcd->vcd)) {
750 vcdinfo_visit_lot (p_vcd->vcd, false);
754 We need to change libvcdinfo to be more robust when there are
755 problems reading the extended PSD. Given that area-highlighting and
756 selection features in the extended PSD haven't been implemented,
757 it's best then to not try to read this at all.
759 if (vcdinfo_get_psd_x_size(p_vcd->vcd))
760 vcdinfo_visit_lot (p_vcd->vcd, true);
764 dbg_print( (INPUT_DBG_CALL|INPUT_DBG_MRL),
765 "num LIDs=%d", p_vcd->num_lids);
770 /*****************************************************************************
771 * VCDParse: parse command line
772 *****************************************************************************/
774 VCDParse( input_thread_t * p_input, /*out*/ vcdinfo_itemid_t * p_itemid,
775 /*out*/ bool *play_single_item )
777 thread_vcd_data_t *p_vcd = (thread_vcd_data_t *)p_input->p_access_data;
782 if ( config_GetInt( p_input, MODULE_STRING "-PBC" ) ) {
783 p_itemid->type=VCDINFO_ITEM_TYPE_LID;
785 *play_single_item=false;
787 p_itemid->type=VCDINFO_ITEM_TYPE_ENTRY;
792 /* On Win32 we want the VCD access plugin to be explicitly requested,
793 * we end up with lots of problems otherwise */
794 if( !p_input->psz_access || !*p_input->psz_access ) return NULL;
797 if( !p_input->psz_name )
802 psz_parser = psz_source = strdup( p_input->psz_name );
804 /* Parse input string :
805 * [device][@[type][title]] */
806 while( *psz_parser && *psz_parser != '@' )
811 if( *psz_parser == '@' )
813 /* Found the divide between the source name and the
814 type+entry number. */
821 switch(*psz_parser) {
823 p_itemid->type = VCDINFO_ITEM_TYPE_ENTRY;
825 *play_single_item = true;
828 p_itemid->type = VCDINFO_ITEM_TYPE_LID;
830 *play_single_item = false;
833 p_itemid->type = VCDINFO_ITEM_TYPE_SEGMENT;
835 *play_single_item = true;
838 p_itemid->type = VCDINFO_ITEM_TYPE_TRACK;
840 *play_single_item = true;
846 num = strtol( psz_parser, &psz_next, 10 );
847 if ( *psz_parser != '\0' && *psz_next == '\0')
853 *play_single_item = ( VCDINFO_ITEM_TYPE_LID == p_itemid->type );
859 /* No source specified, so figure it out. */
860 if( !p_input->psz_access ) return NULL;
862 psz_source = config_GetPsz( p_input, "vcd" );
864 if( !psz_source || 0==strlen(psz_source) ) {
865 /* Scan for a CD-ROM drive with a VCD in it. */
866 char **cd_drives = cdio_get_devices_with_cap(NULL,
867 (CDIO_FS_ANAL_SVCD|CDIO_FS_ANAL_CVD
868 |CDIO_FS_ANAL_VIDEOCD|CDIO_FS_UNKNOWN),
870 if (NULL == cd_drives) return NULL;
871 if (cd_drives[0] == NULL) {
872 cdio_free_device_list(cd_drives);
875 psz_source = strdup(cd_drives[0]);
876 cdio_free_device_list(cd_drives);
880 dbg_print( (INPUT_DBG_CALL|INPUT_DBG_MRL),
881 "source=%s entry=%d type=%d",
882 psz_source, p_itemid->num, p_itemid->type);
888 Set's start origin subsequent seeks/reads
891 VCDSetOrigin( input_thread_t *p_input, lsn_t origin_lsn,
892 lsn_t cur_lsn, lsn_t end_lsn, int cur_entry, track_t cur_track )
894 thread_vcd_data_t * p_vcd = (thread_vcd_data_t *) p_input->p_access_data;
896 p_vcd->origin_lsn = origin_lsn;
897 p_vcd->cur_lsn = cur_lsn;
898 p_vcd->end_lsn = end_lsn;
899 p_vcd->cur_track = cur_track;
900 p_vcd->play_item.num = cur_entry;
901 p_vcd->play_item.type = VCDINFO_ITEM_TYPE_ENTRY;
903 dbg_print( (INPUT_DBG_CALL|INPUT_DBG_LSN),
904 "origin: %d, cur_lsn: %d, end_lsn: %d, entry: %d, track: %d",
905 origin_lsn, cur_lsn, end_lsn, cur_entry, cur_track );
907 p_input->stream.p_selected_area->i_tell =
908 (off_t) (p_vcd->cur_lsn - p_vcd->origin_lsn) * (off_t)M2F2_SECTOR_SIZE;
910 VCDUpdateVar( p_input, cur_entry, VLC_VAR_SETVALUE,
911 "chapter", "Setting entry");
914 /*****************************************************************************
915 * vcd_Open: Opens a VCD device or file and returns an opaque handle
916 *****************************************************************************/
917 static vcdinfo_obj_t *
918 vcd_Open( vlc_object_t *p_this, const char *psz_dev )
920 vcdinfo_obj_t *p_vcdobj;
923 if( !psz_dev ) return NULL;
925 actual_dev=strdup(psz_dev);
926 if ( vcdinfo_open(&p_vcdobj, &actual_dev, DRIVER_UNKNOWN, NULL) !=
936 /****************************************************************************
937 * VCDReadSector: Read a sector (2324 bytes)
938 ****************************************************************************/
940 VCDReadSector( vlc_object_t *p_this, const vcdinfo_obj_t *p_vcd,
941 lsn_t cur_lsn, byte_t * p_buffer )
944 uint8_t subheader [8];
945 uint8_t data [M2F2_SECTOR_SIZE];
947 vcdsector_t vcd_sector;
949 if (cdio_read_mode2_sector(vcdinfo_get_cd_image(p_vcd),
950 &vcd_sector, cur_lsn, true)
953 msg_Warn( p_this, "Could not read LSN %d", cur_lsn );
957 memcpy (p_buffer, vcd_sector.data, M2F2_SECTOR_SIZE);
962 /****************************************************************************
963 Update the "varname" variable to i_num without triggering a callback.
964 ****************************************************************************/
966 VCDUpdateVar( input_thread_t *p_input, int i_num, int i_action,
967 const char *varname, const char *label)
971 if (NULL != p_vcd_input) {
972 thread_vcd_data_t *p_vcd = (thread_vcd_data_t *)p_vcd_input->p_access_data;
973 dbg_print( INPUT_DBG_PBC, "%s %d", label, i_num );
975 var_Change( p_input, varname, i_action, &val, NULL );
979 #define meta_info_add_str(title, str) \
981 dbg_print( INPUT_DBG_META, "field: %s: %s\n", title, str); \
982 input_AddInfo( p_cat, _(title), "%s", str ); \
983 playlist_AddInfo( p_playlist, -1, p_cat->psz_name, _(title), \
987 #define meta_info_add_num(title, num) \
988 dbg_print( INPUT_DBG_META, "field %s: %d\n", title, num); \
989 input_AddInfo( p_cat, _(title), "%d", num ); \
990 playlist_AddInfo( p_playlist, -1, p_cat->psz_name, _(title), \
993 static void InformationCreate( input_thread_t *p_input )
995 thread_vcd_data_t *p_vcd = (thread_vcd_data_t *) p_input->p_access_data;
996 unsigned int i_nb = vcdinfo_get_num_entries(p_vcd->vcd);
997 unsigned int last_entry = 0;
998 input_info_category_t *p_cat;
1000 playlist_item_t *p_playlist = vlc_object_find( p_input, VLC_OBJECT_PLAYLIST,
1006 p_cat = input_InfoCategory( p_input, "General" );
1008 meta_info_add_str( _("VCD Format"),
1009 vcdinfo_get_format_version_str(p_vcd->vcd));
1010 meta_info_add_str( _("Album"),
1011 vcdinfo_get_album_id(p_vcd->vcd));
1012 meta_info_add_str( _("Application"),
1013 vcdinfo_get_application_id(p_vcd->vcd));
1014 meta_info_add_str( _("Preparer"),
1015 vcdinfo_get_preparer_id(p_vcd->vcd));
1016 meta_info_add_num( _("Vol #"),
1017 vcdinfo_get_volume_num(p_vcd->vcd));
1018 meta_info_add_num( _("Vol max #"),
1019 vcdinfo_get_volume_count(p_vcd->vcd));
1020 meta_info_add_str( _("Volume Set"),
1021 vcdinfo_get_volumeset_id(p_vcd->vcd));
1022 meta_info_add_str( _("Volume"),
1023 vcdinfo_get_volume_id(p_vcd->vcd));
1024 meta_info_add_str( _("Publisher"),
1025 vcdinfo_get_publisher_id(p_vcd->vcd));
1026 meta_info_add_str( _("System Id"),
1027 vcdinfo_get_system_id(p_vcd->vcd));
1028 meta_info_add_num( "LIDs", vcdinfo_get_num_LIDs(p_vcd->vcd));
1029 meta_info_add_num( _("Entries"),
1030 vcdinfo_get_num_entries(p_vcd->vcd));
1031 meta_info_add_num( _("Segments"),
1032 vcdinfo_get_num_segments(p_vcd->vcd));
1033 meta_info_add_num( _("Tracks"),
1034 vcdinfo_get_num_tracks(p_vcd->vcd));
1037 if( p_playlist) vlc_object_release( p_playlist );
1039 /* Spit out track information. Could also include MSF info.
1042 #define TITLE_MAX 30
1043 for( i_track = 1 ; i_track < p_vcd->num_tracks ; i_track++ ) {
1044 char track_str[TITLE_MAX];
1045 unsigned int audio_type = vcdinfo_get_track_audio_type(p_vcd->vcd,
1047 snprintf(track_str, TITLE_MAX, "%s%02d", _("Track"), i_track);
1048 p_cat = input_InfoCategory( p_input, track_str );
1051 meta_info_add_num( _("Audio Channels"),
1052 vcdinfo_audio_type_num_channels(p_vcd->vcd,
1056 meta_info_add_num( _("First Entry Point"), last_entry );
1057 for ( ; last_entry < i_nb
1058 && vcdinfo_get_track(p_vcd->vcd, last_entry) == i_track;
1060 meta_info_add_num( _("Last Entry Point"), last_entry-1 );
1065 #define add_format_str_info(val) \
1067 const char *str = val; \
1069 if (val != NULL) { \
1072 strncat(tp, str, TEMP_STR_LEN-(tp-temp_str)); \
1075 saw_control_prefix = false; \
1079 #define add_format_num_info(val, fmt) \
1083 sprintf(num_str, fmt, val); \
1084 len=strlen(num_str); \
1086 strncat(tp, num_str, TEMP_STR_LEN-(tp-temp_str)); \
1089 saw_control_prefix = false; \
1093 Take a format string and expand escape sequences, that is sequences that
1094 begin with %, with information from the current VCD.
1095 The expanded string is returned. Here is a list of escape sequences:
1097 %A : The album information
1098 %C : The VCD volume count - the number of CD's in the collection.
1099 %c : The VCD volume num - the number of the CD in the collection.
1100 %F : The VCD Format, e.g. VCD 1.0, VCD 1.1, VCD 2.0, or SVCD
1101 %I : The current entry/segment/playback type, e.g. ENTRY, TRACK, SEGMENT...
1102 %L : The playlist ID prefixed with " LID" if it exists
1104 %N : The current number of the %I - a decimal number
1105 %P : The publisher ID
1106 %p : The preparer ID
1107 %S : If we are in a segment (menu), the kind of segment
1108 %T : The track number
1109 %V : The volume set ID
1111 A number between 1 and the volume count.
1115 VCDFormatStr(const input_thread_t *p_input, thread_vcd_data_t *p_vcd,
1116 const char format_str[], const char *mrl,
1117 const vcdinfo_itemid_t *itemid)
1119 #define TEMP_STR_SIZE 256
1120 #define TEMP_STR_LEN (TEMP_STR_SIZE-1)
1121 static char temp_str[TEMP_STR_SIZE];
1123 char * tp = temp_str;
1124 bool saw_control_prefix = false;
1125 size_t format_len = strlen(format_str);
1127 bzero(temp_str, TEMP_STR_SIZE);
1129 for (i=0; i<format_len; i++) {
1131 if (!saw_control_prefix && format_str[i] != '%') {
1132 *tp++ = format_str[i];
1133 saw_control_prefix = false;
1137 switch(format_str[i]) {
1139 if (saw_control_prefix) {
1142 saw_control_prefix = !saw_control_prefix;
1145 add_format_str_info(vcdinfo_strip_trail(vcdinfo_get_album_id(p_vcd->vcd),
1150 add_format_num_info(vcdinfo_get_volume_num(p_vcd->vcd), "%d");
1154 add_format_num_info(vcdinfo_get_volume_count(p_vcd->vcd), "%d");
1158 add_format_str_info(vcdinfo_get_format_version_str(p_vcd->vcd));
1163 switch (itemid->type) {
1164 case VCDINFO_ITEM_TYPE_TRACK:
1165 strncat(tp, _("Track"), TEMP_STR_LEN-(tp-temp_str));
1166 tp += strlen(_("Track"));
1168 case VCDINFO_ITEM_TYPE_ENTRY:
1169 strncat(tp, _("Entry"), TEMP_STR_LEN-(tp-temp_str));
1170 tp += strlen(_("Entry"));
1172 case VCDINFO_ITEM_TYPE_SEGMENT:
1173 strncat(tp, _("Segment"), TEMP_STR_LEN-(tp-temp_str));
1174 tp += strlen(_("Segment"));
1176 case VCDINFO_ITEM_TYPE_LID:
1177 strncat(tp, _("List ID"), TEMP_STR_LEN-(tp-temp_str));
1178 tp += strlen(_("List ID"));
1180 case VCDINFO_ITEM_TYPE_SPAREID2:
1181 strncat(tp, _("Navigation"), TEMP_STR_LEN-(tp-temp_str));
1182 tp += strlen(_("Navigation"));
1188 saw_control_prefix = false;
1193 if (vcdplayer_pbc_is_on(p_vcd)) {
1195 sprintf(num_str, "%s %d", _("List ID"), p_vcd->cur_lid);
1196 strncat(tp, num_str, TEMP_STR_LEN-(tp-temp_str));
1197 tp += strlen(num_str);
1199 saw_control_prefix = false;
1203 add_format_str_info(mrl);
1207 add_format_num_info(itemid->num, "%d");
1211 add_format_str_info(vcdinfo_get_preparer_id(p_vcd->vcd));
1215 add_format_str_info(vcdinfo_get_publisher_id(p_vcd->vcd));
1219 if ( VCDINFO_ITEM_TYPE_SEGMENT==itemid->type ) {
1220 char seg_type_str[10];
1222 sprintf(seg_type_str, " %s",
1223 vcdinfo_video_type2str(p_vcd->vcd, itemid->num));
1224 strncat(tp, seg_type_str, TEMP_STR_LEN-(tp-temp_str));
1225 tp += strlen(seg_type_str);
1227 saw_control_prefix = false;
1231 add_format_num_info(p_vcd->cur_track, "%d");
1235 add_format_str_info(vcdinfo_get_volumeset_id(p_vcd->vcd));
1239 add_format_str_info(vcdinfo_get_volume_id(p_vcd->vcd));
1244 *tp++ = format_str[i];
1245 saw_control_prefix = false;
1248 return strdup(temp_str);
1252 VCDCreatePlayListItem(const input_thread_t *p_input,
1253 thread_vcd_data_t *p_vcd,
1254 playlist_t *p_playlist,
1255 const vcdinfo_itemid_t *itemid,
1256 char *psz_mrl, int psz_mrl_max,
1257 const char *psz_source, int playlist_operation,
1260 mtime_t i_duration = -1;
1265 switch(itemid->type) {
1266 case VCDINFO_ITEM_TYPE_TRACK:
1269 case VCDINFO_ITEM_TYPE_SEGMENT:
1272 case VCDINFO_ITEM_TYPE_LID:
1275 case VCDINFO_ITEM_TYPE_ENTRY:
1283 snprintf(psz_mrl, psz_mrl_max, "%s%s@%c%u", VCD_MRL_PREFIX, psz_source,
1284 c_type, itemid->num);
1287 VCDFormatStr( p_input, p_vcd,
1288 config_GetPsz( p_input, MODULE_STRING "-title-format" ),
1291 playlist_Add( p_playlist, psz_mrl, p_title, playlist_operation, i_pos );
1294 VCDFormatStr( p_input, p_vcd,
1295 config_GetPsz( p_input, MODULE_STRING "-author-format" ),
1298 /* FIXME: This is horrible, but until the playlist interface is fixed up
1299 something like this has to be done for the "Author" field.
1301 if( i_pos == PLAYLIST_END ) i_pos = p_playlist->i_size - 1;
1302 playlist_AddInfo(p_playlist, i_pos, _("General"), _("Author"), "%s",p_author);
1306 VCDFixupPlayList( input_thread_t *p_input, thread_vcd_data_t *p_vcd,
1307 const char *psz_source, vcdinfo_itemid_t *itemid,
1308 bool play_single_item )
1311 playlist_t * p_playlist;
1313 unsigned int psz_mrl_max = strlen(VCD_MRL_PREFIX) + strlen(psz_source) +
1314 strlen("@T") + strlen("100") + 1;
1316 psz_mrl = malloc( psz_mrl_max );
1318 if( psz_mrl == NULL )
1320 msg_Warn( p_input, "out of memory" );
1324 p_playlist = (playlist_t *) vlc_object_find( p_input, VLC_OBJECT_PLAYLIST,
1328 msg_Warn( p_input, "can't find playlist" );
1333 InformationCreate( p_input );
1335 if ( play_single_item ) {
1336 /* May fill out more information when the playlist user interface becomes
1339 VCDCreatePlayListItem(p_input, p_vcd, p_playlist, itemid,
1340 psz_mrl, psz_mrl_max, psz_source, PLAYLIST_REPLACE,
1341 p_playlist->i_index);
1343 vcdinfo_itemid_t list_itemid;
1344 list_itemid.type=VCDINFO_ITEM_TYPE_ENTRY;
1346 playlist_Delete( p_playlist, p_playlist->i_index);
1348 for( i = 0 ; i < p_vcd->num_entries ; i++ )
1351 VCDCreatePlayListItem(p_input, p_vcd, p_playlist, &list_itemid,
1352 psz_mrl, psz_mrl_max, psz_source,
1353 PLAYLIST_APPEND, PLAYLIST_END);
1356 playlist_Command( p_playlist, PLAYLIST_GOTO, 0 );
1360 vlc_object_release( p_playlist );
1365 /*****************************************************************************
1367 *****************************************************************************/
1369 E_(DebugCallback) ( vlc_object_t *p_this, const char *psz_name,
1370 vlc_value_t oldval, vlc_value_t val, void *p_data )
1372 thread_vcd_data_t *p_vcd;
1374 if (NULL == p_vcd_input) return VLC_EGENERIC;
1376 p_vcd = (thread_vcd_data_t *)p_vcd_input->p_access_data;
1378 if (p_vcd->i_debug & (INPUT_DBG_CALL|INPUT_DBG_EXT)) {
1379 msg_Dbg( p_vcd_input, "Old debug (x%0x) %d, new debug (x%0x) %d",
1380 p_vcd->i_debug, p_vcd->i_debug, val.i_int, val.i_int);
1382 p_vcd->i_debug = val.i_int;
1386 /*****************************************************************************
1388 read in meta-information about VCD: the number of tracks, segments,
1389 entries, size and starting information. Then set up state variables so
1390 that we read/seek starting at the location specified.
1392 On success we return VLC_SUCCESS, on memory exhausted VLC_ENOMEM,
1393 and VLC_EGENERIC for some other error.
1394 *****************************************************************************/
1396 E_(Open) ( vlc_object_t *p_this )
1398 input_thread_t * p_input = (input_thread_t *)p_this;
1399 thread_vcd_data_t * p_vcd;
1401 vcdinfo_itemid_t itemid;
1403 bool play_single_item = false;
1405 p_input->pf_read = VCDRead;
1406 p_input->pf_seek = VCDSeek;
1407 p_input->pf_set_area = VCDSetArea;
1408 p_input->pf_set_program = VCDSetProgram;
1410 p_vcd = malloc( sizeof(thread_vcd_data_t) );
1414 LOG_ERR ("out of memory" );
1418 p_input->p_access_data = (void *)p_vcd;
1419 p_vcd->i_debug = config_GetInt( p_this, MODULE_STRING "-debug" );
1421 /* Set where to log errors messages from libcdio. */
1422 p_vcd_input = (input_thread_t *)p_this;
1423 cdio_log_set_handler ( cdio_log_handler );
1424 vcd_log_set_handler ( vcd_log_handler );
1426 psz_source = VCDParse( p_input, &itemid, &play_single_item );
1428 if ( NULL == psz_source )
1431 return( VLC_EGENERIC );
1434 dbg_print( (INPUT_DBG_CALL|INPUT_DBG_EXT), "source: %s: mrl: %s",
1435 psz_source, p_input->psz_name );
1437 p_vcd->p_segments = NULL;
1438 p_vcd->p_entries = NULL;
1441 p_input->i_mtu = VCD_DATA_ONCE;
1443 vlc_mutex_lock( &p_input->stream.stream_lock );
1445 /* If we are here we can control the pace... */
1446 p_input->stream.b_pace_control = 1;
1448 p_input->stream.b_seekable = 1;
1449 p_input->stream.p_selected_area->i_size = 0;
1450 p_input->stream.p_selected_area->i_tell = 0;
1452 vlc_mutex_unlock( &p_input->stream.stream_lock );
1454 if( !(p_vcd->vcd = vcd_Open( p_this, psz_source )) )
1456 msg_Warn( p_input, "could not open %s", psz_source );
1460 p_vcd->b_svd= vcdinfo_get_tracksSVD(p_vcd->vcd);;
1462 /* Get track information. */
1463 p_vcd->num_tracks = ioctl_GetTracksMap( VLC_OBJECT(p_input),
1464 vcdinfo_get_cd_image(p_vcd->vcd),
1465 &p_vcd->p_sectors );
1466 if( p_vcd->num_tracks < 0 )
1467 LOG_ERR ("unable to count tracks" );
1468 else if( p_vcd->num_tracks <= 1 )
1469 LOG_ERR ("no movie tracks found" );
1470 if( p_vcd->num_tracks <= 1)
1472 vcdinfo_close( p_vcd->vcd );
1476 /* Set stream and area data */
1477 vlc_mutex_lock( &p_input->stream.stream_lock );
1479 /* Initialize ES structures */
1480 input_InitStream( p_input, sizeof( stream_ps_data_t ) );
1482 /* disc input method */
1483 p_input->stream.i_method = INPUT_METHOD_VCD;
1485 p_input->stream.i_area_nb = 1;
1488 /* Initialize segment information. */
1489 VCDSegments( p_input );
1491 /* Initialize track area information. */
1492 VCDTracks( p_input );
1494 if( VCDEntryPoints( p_input ) < 0 )
1496 msg_Warn( p_input, "could not read entry points, will not use them" );
1497 p_vcd->b_valid_ep = false;
1500 if( VCDLIDs( p_input ) < 0 )
1502 msg_Warn( p_input, "could not read entry LIDs" );
1505 b_play_ok = (VLC_SUCCESS == VCDPlay( p_input, itemid ));
1507 vlc_mutex_unlock( &p_input->stream.stream_lock );
1509 if ( ! b_play_ok ) {
1510 vcdinfo_close( p_vcd->vcd );
1514 if( !p_input->psz_demux || !*p_input->psz_demux )
1517 p_input->psz_demux = "vcdx";
1519 p_input->psz_demux = "ps";
1523 p_vcd->p_intf = intf_Create( p_input, "vcdx" );
1524 p_vcd->p_intf->b_block = VLC_FALSE;
1525 intf_RunThread( p_vcd->p_intf );
1527 VCDFixupPlayList( p_input, p_vcd, psz_source, &itemid, play_single_item );
1535 return VLC_EGENERIC;
1538 /*****************************************************************************
1539 * Close: closes VCD releasing allocated memory.
1540 *****************************************************************************/
1542 E_(Close) ( vlc_object_t *p_this )
1544 input_thread_t * p_input = (input_thread_t *)p_this;
1545 thread_vcd_data_t *p_vcd = (thread_vcd_data_t *)p_input->p_access_data;
1547 dbg_print( (INPUT_DBG_CALL|INPUT_DBG_EXT), "VCDClose" );
1548 vcdinfo_close( p_vcd->vcd );
1550 free( p_vcd->p_entries );
1551 free( p_vcd->p_segments );
1553 /* For reasons that are a mystery to me we don't have to deal with
1554 stopping, and destroying the p_vcd->p_intf thread. And if we do
1555 it causes problems upstream.
1557 if( p_vcd->p_intf != NULL )
1559 p_vcd->p_intf = NULL;
1563 p_input->p_access_data = NULL;