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.14 2004/01/03 20:43:24 rocky Exp $
9 * Authors: Rocky Bernstein <rocky@panix.com>
10 * Johan Bilien <jobi@via.ecp.fr>
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or
15 * (at your option) any later version.
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software
24 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
25 *****************************************************************************/
27 /*****************************************************************************
29 *****************************************************************************/
32 #include <vlc/input.h>
35 #include "../../demux/mpeg/system.h"
37 #include "vcdplayer.h"
40 #include <cdio/cdio.h>
41 #include <cdio/cd_types.h>
42 #include <cdio/logging.h>
43 #include <cdio/util.h>
44 #include <libvcd/info.h>
45 #include <libvcd/logging.h>
49 /* how many blocks VCDRead will read in each loop */
50 #define VCD_BLOCKS_ONCE 20
51 #define VCD_DATA_ONCE (VCD_BLOCKS_ONCE * M2F2_SECTOR_SIZE)
53 #define VCD_MRL_PREFIX "vcdx://"
55 /*****************************************************************************
57 *****************************************************************************/
59 /* First those which are accessed from outside (via pointers). */
60 static int VCDRead ( input_thread_t *, byte_t *, size_t );
61 static int VCDSetProgram ( input_thread_t *, pgrm_descriptor_t * );
63 /* Now those which are strictly internal */
64 static void VCDSetOrigin ( input_thread_t *, lsn_t origin_lsn,
65 lsn_t cur_lsn, lsn_t end_lsn,
66 int cur_entry, track_t track );
67 static int VCDEntryPoints ( input_thread_t * );
68 static int VCDLIDs ( input_thread_t * );
69 static int VCDSegments ( input_thread_t * );
70 static void VCDTracks ( input_thread_t * );
71 static int VCDReadSector ( vlc_object_t *p_this,
72 const vcdinfo_obj_t *p_vcd, lsn_t cur_lsn,
74 static char *VCDParse ( input_thread_t *,
75 /*out*/ vcdinfo_itemid_t * p_itemid ,
76 /*out*/ bool *play_single_item );
78 static void VCDUpdateVar( input_thread_t *p_input, int i_entry, int i_action,
79 const char *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 ); \
985 #define meta_info_add_num(title, num) \
986 dbg_print( INPUT_DBG_META, "field %s: %d\n", title, num); \
987 input_AddInfo( p_cat, _(title), "%d", num ); \
989 static void InformationCreate( input_thread_t *p_input )
991 thread_vcd_data_t *p_vcd = (thread_vcd_data_t *) p_input->p_access_data;
992 unsigned int i_nb = vcdinfo_get_num_entries(p_vcd->vcd);
993 unsigned int last_entry = 0;
994 input_info_category_t *p_cat;
997 p_cat = input_InfoCategory( p_input, "General" );
999 meta_info_add_str( _("VCD Format"),
1000 vcdinfo_get_format_version_str(p_vcd->vcd));
1001 meta_info_add_str( _("Album"),
1002 vcdinfo_get_album_id(p_vcd->vcd));
1003 meta_info_add_str( _("Application"),
1004 vcdinfo_get_application_id(p_vcd->vcd));
1005 meta_info_add_str( _("Preparer"),
1006 vcdinfo_get_preparer_id(p_vcd->vcd));
1007 meta_info_add_num( _("Vol #"),
1008 vcdinfo_get_volume_num(p_vcd->vcd));
1009 meta_info_add_num( _("Vol max #"),
1010 vcdinfo_get_volume_count(p_vcd->vcd));
1011 meta_info_add_str( _("Volume Set"),
1012 vcdinfo_get_volumeset_id(p_vcd->vcd));
1013 meta_info_add_str( _("Volume"),
1014 vcdinfo_get_volume_id(p_vcd->vcd));
1015 meta_info_add_str( _("Publisher"),
1016 vcdinfo_get_publisher_id(p_vcd->vcd));
1017 meta_info_add_str( _("System Id"),
1018 vcdinfo_get_system_id(p_vcd->vcd));
1019 meta_info_add_num( "LIDs", vcdinfo_get_num_LIDs(p_vcd->vcd));
1020 meta_info_add_num( _("Entries"),
1021 vcdinfo_get_num_entries(p_vcd->vcd));
1022 meta_info_add_num( _("Segments"),
1023 vcdinfo_get_num_segments(p_vcd->vcd));
1024 meta_info_add_num( _("Tracks"),
1025 vcdinfo_get_num_tracks(p_vcd->vcd));
1027 /* Spit out track information. Could also include MSF info.
1030 #define TITLE_MAX 30
1031 for( i_track = 1 ; i_track < p_vcd->num_tracks ; i_track++ ) {
1032 char track_str[TITLE_MAX];
1033 unsigned int audio_type = vcdinfo_get_track_audio_type(p_vcd->vcd,
1035 snprintf(track_str, TITLE_MAX, "%s%02d", _("Track"), i_track);
1036 p_cat = input_InfoCategory( p_input, track_str );
1039 meta_info_add_num( _("Audio Channels"),
1040 vcdinfo_audio_type_num_channels(p_vcd->vcd,
1044 meta_info_add_num( _("First Entry Point"), last_entry );
1045 for ( ; last_entry < i_nb
1046 && vcdinfo_get_track(p_vcd->vcd, last_entry) == i_track;
1048 meta_info_add_num( _("Last Entry Point"), last_entry-1 );
1052 #define add_format_str_info(val) \
1054 const char *str = val; \
1056 if (val != NULL) { \
1059 strncat(tp, str, TEMP_STR_LEN-(tp-temp_str)); \
1062 saw_control_prefix = false; \
1066 #define add_format_num_info(val, fmt) \
1070 sprintf(num_str, fmt, val); \
1071 len=strlen(num_str); \
1073 strncat(tp, num_str, TEMP_STR_LEN-(tp-temp_str)); \
1076 saw_control_prefix = false; \
1080 Take a format string and expand escape sequences, that is sequences that
1081 begin with %, with information from the current VCD.
1082 The expanded string is returned. Here is a list of escape sequences:
1084 %A : The album information
1085 %C : The VCD volume count - the number of CD's in the collection.
1086 %c : The VCD volume num - the number of the CD in the collection.
1087 %F : The VCD Format, e.g. VCD 1.0, VCD 1.1, VCD 2.0, or SVCD
1088 %I : The current entry/segment/playback type, e.g. ENTRY, TRACK, SEGMENT...
1089 %L : The playlist ID prefixed with " LID" if it exists
1091 %N : The current number of the %I - a decimal number
1092 %P : The publisher ID
1093 %p : The preparer ID
1094 %S : If we are in a segment (menu), the kind of segment
1095 %T : The track number
1096 %V : The volume set ID
1098 A number between 1 and the volume count.
1102 VCDFormatStr(const input_thread_t *p_input, thread_vcd_data_t *p_vcd,
1103 const char format_str[], const char *mrl,
1104 const vcdinfo_itemid_t *itemid)
1106 #define TEMP_STR_SIZE 256
1107 #define TEMP_STR_LEN (TEMP_STR_SIZE-1)
1108 static char temp_str[TEMP_STR_SIZE];
1110 char * tp = temp_str;
1111 bool saw_control_prefix = false;
1112 size_t format_len = strlen(format_str);
1114 bzero(temp_str, TEMP_STR_SIZE);
1116 for (i=0; i<format_len; i++) {
1118 if (!saw_control_prefix && format_str[i] != '%') {
1119 *tp++ = format_str[i];
1120 saw_control_prefix = false;
1124 switch(format_str[i]) {
1126 if (saw_control_prefix) {
1129 saw_control_prefix = !saw_control_prefix;
1132 add_format_str_info(vcdinfo_strip_trail(vcdinfo_get_album_id(p_vcd->vcd),
1137 add_format_num_info(vcdinfo_get_volume_num(p_vcd->vcd), "%d");
1141 add_format_num_info(vcdinfo_get_volume_count(p_vcd->vcd), "%d");
1145 add_format_str_info(vcdinfo_get_format_version_str(p_vcd->vcd));
1150 switch (itemid->type) {
1151 case VCDINFO_ITEM_TYPE_TRACK:
1152 strncat(tp, _("Track"), TEMP_STR_LEN-(tp-temp_str));
1153 tp += strlen(_("Track"));
1155 case VCDINFO_ITEM_TYPE_ENTRY:
1156 strncat(tp, _("Entry"), TEMP_STR_LEN-(tp-temp_str));
1157 tp += strlen(_("Entry"));
1159 case VCDINFO_ITEM_TYPE_SEGMENT:
1160 strncat(tp, _("Segment"), TEMP_STR_LEN-(tp-temp_str));
1161 tp += strlen(_("Segment"));
1163 case VCDINFO_ITEM_TYPE_LID:
1164 strncat(tp, _("List ID"), TEMP_STR_LEN-(tp-temp_str));
1165 tp += strlen(_("List ID"));
1167 case VCDINFO_ITEM_TYPE_SPAREID2:
1168 strncat(tp, _("Navigation"), TEMP_STR_LEN-(tp-temp_str));
1169 tp += strlen(_("Navigation"));
1175 saw_control_prefix = false;
1180 if (vcdplayer_pbc_is_on(p_vcd)) {
1182 sprintf(num_str, "%s %d", _("List ID"), p_vcd->cur_lid);
1183 strncat(tp, num_str, TEMP_STR_LEN-(tp-temp_str));
1184 tp += strlen(num_str);
1186 saw_control_prefix = false;
1190 add_format_str_info(mrl);
1194 add_format_num_info(itemid->num, "%d");
1198 add_format_str_info(vcdinfo_get_preparer_id(p_vcd->vcd));
1202 add_format_str_info(vcdinfo_get_publisher_id(p_vcd->vcd));
1206 if ( VCDINFO_ITEM_TYPE_SEGMENT==itemid->type ) {
1207 char seg_type_str[10];
1209 sprintf(seg_type_str, " %s",
1210 vcdinfo_video_type2str(p_vcd->vcd, itemid->num));
1211 strncat(tp, seg_type_str, TEMP_STR_LEN-(tp-temp_str));
1212 tp += strlen(seg_type_str);
1214 saw_control_prefix = false;
1218 add_format_num_info(p_vcd->cur_track, "%d");
1222 add_format_str_info(vcdinfo_get_volumeset_id(p_vcd->vcd));
1226 add_format_str_info(vcdinfo_get_volume_id(p_vcd->vcd));
1231 *tp++ = format_str[i];
1232 saw_control_prefix = false;
1235 return strdup(temp_str);
1239 VCDCreatePlayListItem(const input_thread_t *p_input,
1240 thread_vcd_data_t *p_vcd,
1241 playlist_t *p_playlist,
1242 const vcdinfo_itemid_t *itemid,
1243 char *psz_mrl, int psz_mrl_max,
1244 const char *psz_source, int playlist_operation,
1247 mtime_t i_duration = -1;
1252 switch(itemid->type) {
1253 case VCDINFO_ITEM_TYPE_TRACK:
1256 case VCDINFO_ITEM_TYPE_SEGMENT:
1259 case VCDINFO_ITEM_TYPE_LID:
1262 case VCDINFO_ITEM_TYPE_ENTRY:
1270 snprintf(psz_mrl, psz_mrl_max, "%s%s@%c%u", VCD_MRL_PREFIX, psz_source,
1271 c_type, itemid->num);
1274 VCDFormatStr( p_input, p_vcd,
1275 config_GetPsz( p_input, MODULE_STRING "-title-format" ),
1278 playlist_AddExt( p_playlist, psz_mrl, p_title, i_duration,
1279 0, 0, playlist_operation, i_pos );
1282 VCDFormatStr( p_input, p_vcd,
1283 config_GetPsz( p_input, MODULE_STRING "-author-format" ),
1286 /* FIXME: This is horrible, but until the playlist interface is fixed up
1287 something like this has to be done for the "Author" field.
1289 if( i_pos == PLAYLIST_END ) i_pos = p_playlist->i_size - 1;
1290 free(p_playlist->pp_items[i_pos]->psz_author);
1291 p_playlist->pp_items[i_pos]->psz_author = strdup(p_author);
1295 VCDFixupPlayList( input_thread_t *p_input, thread_vcd_data_t *p_vcd,
1296 const char *psz_source, vcdinfo_itemid_t *itemid,
1297 bool play_single_item )
1300 playlist_t * p_playlist;
1302 unsigned int psz_mrl_max = strlen(VCD_MRL_PREFIX) + strlen(psz_source) +
1303 strlen("@T") + strlen("100") + 1;
1305 psz_mrl = malloc( psz_mrl_max );
1307 if( psz_mrl == NULL )
1309 msg_Warn( p_input, "out of memory" );
1313 p_playlist = (playlist_t *) vlc_object_find( p_input, VLC_OBJECT_PLAYLIST,
1317 msg_Warn( p_input, "can't find playlist" );
1322 InformationCreate( p_input );
1324 if ( play_single_item ) {
1325 /* May fill out more information when the playlist user interface becomes
1328 VCDCreatePlayListItem(p_input, p_vcd, p_playlist, itemid,
1329 psz_mrl, psz_mrl_max, psz_source, PLAYLIST_REPLACE,
1330 p_playlist->i_index);
1332 vcdinfo_itemid_t list_itemid;
1333 list_itemid.type=VCDINFO_ITEM_TYPE_ENTRY;
1335 playlist_Delete( p_playlist, p_playlist->i_index);
1337 for( i = 0 ; i < p_vcd->num_entries ; i++ )
1340 VCDCreatePlayListItem(p_input, p_vcd, p_playlist, &list_itemid,
1341 psz_mrl, psz_mrl_max, psz_source,
1342 PLAYLIST_APPEND, PLAYLIST_END);
1345 playlist_Command( p_playlist, PLAYLIST_GOTO, 0 );
1349 vlc_object_release( p_playlist );
1354 /*****************************************************************************
1356 *****************************************************************************/
1358 E_(DebugCallback) ( vlc_object_t *p_this, const char *psz_name,
1359 vlc_value_t oldval, vlc_value_t val, void *p_data )
1361 thread_vcd_data_t *p_vcd;
1363 if (NULL == p_vcd_input) return VLC_EGENERIC;
1365 p_vcd = (thread_vcd_data_t *)p_vcd_input->p_access_data;
1367 if (p_vcd->i_debug & (INPUT_DBG_CALL|INPUT_DBG_EXT)) {
1368 msg_Dbg( p_vcd_input, "Old debug (x%0x) %d, new debug (x%0x) %d",
1369 p_vcd->i_debug, p_vcd->i_debug, val.i_int, val.i_int);
1371 p_vcd->i_debug = val.i_int;
1375 /*****************************************************************************
1377 read in meta-information about VCD: the number of tracks, segments,
1378 entries, size and starting information. Then set up state variables so
1379 that we read/seek starting at the location specified.
1381 On success we return VLC_SUCCESS, on memory exhausted VLC_ENOMEM,
1382 and VLC_EGENERIC for some other error.
1383 *****************************************************************************/
1385 E_(Open) ( vlc_object_t *p_this )
1387 input_thread_t * p_input = (input_thread_t *)p_this;
1388 thread_vcd_data_t * p_vcd;
1390 vcdinfo_itemid_t itemid;
1392 bool play_single_item = false;
1394 p_input->pf_read = VCDRead;
1395 p_input->pf_seek = VCDSeek;
1396 p_input->pf_set_area = VCDSetArea;
1397 p_input->pf_set_program = VCDSetProgram;
1399 p_vcd = malloc( sizeof(thread_vcd_data_t) );
1403 LOG_ERR ("out of memory" );
1407 p_input->p_access_data = (void *)p_vcd;
1408 p_vcd->i_debug = config_GetInt( p_this, MODULE_STRING "-debug" );
1410 /* Set where to log errors messages from libcdio. */
1411 p_vcd_input = (input_thread_t *)p_this;
1412 cdio_log_set_handler ( cdio_log_handler );
1413 vcd_log_set_handler ( vcd_log_handler );
1415 psz_source = VCDParse( p_input, &itemid, &play_single_item );
1417 if ( NULL == psz_source )
1420 return( VLC_EGENERIC );
1423 dbg_print( (INPUT_DBG_CALL|INPUT_DBG_EXT), "source: %s: mrl: %s",
1424 psz_source, p_input->psz_name );
1426 p_vcd->p_segments = NULL;
1427 p_vcd->p_entries = NULL;
1430 p_input->i_mtu = VCD_DATA_ONCE;
1432 vlc_mutex_lock( &p_input->stream.stream_lock );
1434 /* If we are here we can control the pace... */
1435 p_input->stream.b_pace_control = 1;
1437 p_input->stream.b_seekable = 1;
1438 p_input->stream.p_selected_area->i_size = 0;
1439 p_input->stream.p_selected_area->i_tell = 0;
1441 vlc_mutex_unlock( &p_input->stream.stream_lock );
1443 if( !(p_vcd->vcd = vcd_Open( p_this, psz_source )) )
1445 msg_Warn( p_input, "could not open %s", psz_source );
1449 p_vcd->b_svd= vcdinfo_get_tracksSVD(p_vcd->vcd);;
1451 /* Get track information. */
1452 p_vcd->num_tracks = ioctl_GetTracksMap( VLC_OBJECT(p_input),
1453 vcdinfo_get_cd_image(p_vcd->vcd),
1454 &p_vcd->p_sectors );
1455 if( p_vcd->num_tracks < 0 )
1456 LOG_ERR ("unable to count tracks" );
1457 else if( p_vcd->num_tracks <= 1 )
1458 LOG_ERR ("no movie tracks found" );
1459 if( p_vcd->num_tracks <= 1)
1461 vcdinfo_close( p_vcd->vcd );
1465 /* Set stream and area data */
1466 vlc_mutex_lock( &p_input->stream.stream_lock );
1468 /* Initialize ES structures */
1469 input_InitStream( p_input, sizeof( stream_ps_data_t ) );
1471 /* disc input method */
1472 p_input->stream.i_method = INPUT_METHOD_VCD;
1474 p_input->stream.i_area_nb = 1;
1477 /* Initialize segment information. */
1478 VCDSegments( p_input );
1480 /* Initialize track area information. */
1481 VCDTracks( p_input );
1483 if( VCDEntryPoints( p_input ) < 0 )
1485 msg_Warn( p_input, "could not read entry points, will not use them" );
1486 p_vcd->b_valid_ep = false;
1489 if( VCDLIDs( p_input ) < 0 )
1491 msg_Warn( p_input, "could not read entry LIDs" );
1494 b_play_ok = (VLC_SUCCESS == VCDPlay( p_input, itemid ));
1496 vlc_mutex_unlock( &p_input->stream.stream_lock );
1498 if ( ! b_play_ok ) {
1499 vcdinfo_close( p_vcd->vcd );
1503 if( !p_input->psz_demux || !*p_input->psz_demux )
1506 p_input->psz_demux = "vcdx";
1508 p_input->psz_demux = "ps";
1512 p_vcd->p_intf = intf_Create( p_input, "vcdx" );
1513 p_vcd->p_intf->b_block = VLC_FALSE;
1514 intf_RunThread( p_vcd->p_intf );
1516 VCDFixupPlayList( p_input, p_vcd, psz_source, &itemid, play_single_item );
1524 return VLC_EGENERIC;
1527 /*****************************************************************************
1528 * Close: closes VCD releasing allocated memory.
1529 *****************************************************************************/
1531 E_(Close) ( vlc_object_t *p_this )
1533 input_thread_t * p_input = (input_thread_t *)p_this;
1534 thread_vcd_data_t *p_vcd = (thread_vcd_data_t *)p_input->p_access_data;
1536 dbg_print( (INPUT_DBG_CALL|INPUT_DBG_EXT), "VCDClose" );
1537 vcdinfo_close( p_vcd->vcd );
1539 free( p_vcd->p_entries );
1540 free( p_vcd->p_segments );
1542 /* For reasons that are a mystery to me we don't have to deal with
1543 stopping, and destroying the p_vcd->p_intf thread. And if we do
1544 it causes problems upstream.
1546 if( p_vcd->p_intf != NULL )
1548 p_vcd->p_intf = NULL;
1552 p_input->p_access_data = NULL;