1 /*****************************************************************************
2 * vcdplayer.c : VCD input module for vlc
3 * using libcdio, libvcd and libvcdinfo
4 *****************************************************************************
5 * Copyright (C) 2003 Rocky Bernstein <rocky@panix.com>
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
21 *****************************************************************************/
24 This contains more of the vlc-independent parts that might be used
25 in any VCD input module for a media player. However at present there
26 are vlc-specific structures. See also vcdplayer.c of the xine plugin.
28 /*****************************************************************************
30 *****************************************************************************/
33 #include <vlc/input.h>
37 #include "vcdplayer.h"
42 #include <cdio/cdio.h>
43 #include <cdio/util.h>
44 #include <libvcd/info.h>
47 #define sleep(A) Sleep((A)*1000)
51 Return VLC_TRUE if playback control (PBC) is on
53 vlc_bool_t vcdplayer_pbc_is_on( const thread_vcd_data_t *p_vcd )
55 return VCDINFO_INVALID_ENTRY != p_vcd->cur_lid;
58 static void vcdplayer_update_entry( input_thread_t * p_input, uint16_t ofs,
59 uint16_t *entry, const char *label)
61 thread_vcd_data_t * p_vcd= (thread_vcd_data_t *)p_input->p_access_data;
63 if ( ofs == VCDINFO_INVALID_OFFSET ) {
64 *entry = VCDINFO_INVALID_ENTRY;
66 vcdinfo_offset_t *off = vcdinfo_get_offset_t(p_vcd->vcd, ofs);
69 dbg_print(INPUT_DBG_PBC, "%s: LID %d\n", label, off->lid);
71 *entry = VCDINFO_INVALID_ENTRY;
75 /* Handles navigation when NOT in PBC reaching the end of a play item.
77 The navigations rules here may be sort of made up, but the intent
78 is to do something that's probably right or helpful.
80 return VLC_TRUE if the caller should return.
82 vcdplayer_read_status_t vcdplayer_non_pbc_nav ( input_thread_t * p_input )
84 thread_vcd_data_t * p_vcd= (thread_vcd_data_t *)p_input->p_access_data;
86 /* Not in playback control. Do we advance automatically or stop? */
87 switch (p_vcd->play_item.type) {
88 case VCDINFO_ITEM_TYPE_TRACK:
89 case VCDINFO_ITEM_TYPE_ENTRY: {
91 dbg_print( INPUT_DBG_LSN, "new track %d, lsn %d", p_vcd->cur_track,
92 p_vcd->p_sectors[p_vcd->cur_track+1] );
96 case VCDINFO_ITEM_TYPE_SPAREID2:
97 dbg_print( (INPUT_DBG_STILL|INPUT_DBG_LSN),
100 p_input->stream.b_seekable = 0;
103 return READ_STILL_FRAME ;
106 case VCDINFO_ITEM_TYPE_NOTFOUND:
107 LOG_ERR ("NOTFOUND outside PBC -- not supposed to happen");
109 case VCDINFO_ITEM_TYPE_LID:
110 LOG_ERR ("LID outside PBC -- not supposed to happen");
112 case VCDINFO_ITEM_TYPE_SEGMENT:
113 /* Hack: Just go back and do still again */
115 p_input->stream.b_seekable = 0;
118 dbg_print( (INPUT_DBG_STILL|INPUT_DBG_LSN),
119 "End of Segment - looping" );
120 return READ_STILL_FRAME;
127 /* Handles PBC navigation when reaching the end of a play item. */
128 vcdplayer_read_status_t
129 vcdplayer_pbc_nav ( input_thread_t * p_input )
131 thread_vcd_data_t * p_vcd= (thread_vcd_data_t *)p_input->p_access_data;
133 /* We are in playback control. */
134 vcdinfo_itemid_t itemid;
136 /* The end of an entry is really the end of the associated
137 sequence (or track). */
139 if ( (VCDINFO_ITEM_TYPE_ENTRY == p_vcd->play_item.type) &&
140 (p_vcd->cur_lsn < p_vcd->end_lsn) ) {
141 /* Set up to just continue to the next entry */
142 p_vcd->play_item.num++;
143 dbg_print( (INPUT_DBG_LSN|INPUT_DBG_PBC),
144 "continuing into next entry: %u", p_vcd->play_item.num);
145 VCDPlay( p_input, p_vcd->play_item );
146 /* p_vcd->update_title(); */
150 switch (p_vcd->pxd.descriptor_type) {
151 case PSD_TYPE_END_LIST:
154 case PSD_TYPE_PLAY_LIST: {
155 int wait_time = vcdinf_get_wait_time(p_vcd->pxd.pld);
157 dbg_print(INPUT_DBG_PBC, "playlist wait_time: %d", wait_time);
159 if (vcdplayer_inc_play_item(p_input))
162 /* Handle any wait time given. */
163 if (p_vcd->in_still) {
164 vcdIntfStillTime( p_vcd->p_intf, wait_time );
165 return READ_STILL_FRAME;
168 vcdplayer_update_entry( p_input,
169 vcdinf_pld_get_next_offset(p_vcd->pxd.pld),
170 &itemid.num, "next" );
171 itemid.type = VCDINFO_ITEM_TYPE_LID;
172 VCDPlay( p_input, itemid );
175 case PSD_TYPE_SELECTION_LIST: /* Selection List (+Ext. for SVCD) */
176 case PSD_TYPE_EXT_SELECTION_LIST: /* Extended Selection List (VCD2.0) */
178 int wait_time = vcdinf_get_timeout_time(p_vcd->pxd.psd);
179 uint16_t timeout_offs = vcdinf_get_timeout_offset(p_vcd->pxd.psd);
180 uint16_t max_loop = vcdinf_get_loop_count(p_vcd->pxd.psd);
181 vcdinfo_offset_t *offset_timeout_LID =
182 vcdinfo_get_offset_t(p_vcd->vcd, timeout_offs);
184 dbg_print(INPUT_DBG_PBC, "wait_time: %d, looped: %d, max_loop %d",
185 wait_time, p_vcd->loop_count, max_loop);
187 /* Handle any wait time given */
188 if (p_vcd->in_still) {
189 vcdIntfStillTime( p_vcd->p_intf, wait_time );
190 return READ_STILL_FRAME;
193 /* Handle any looping given. */
194 if ( max_loop == 0 || p_vcd->loop_count < max_loop ) {
196 if (p_vcd->loop_count == 0x7f) p_vcd->loop_count = 0;
197 VCDSeek( p_input, 0 );
198 /* if (p_vcd->in_still) p_vcd->force_redisplay();*/
202 /* Looping finished and wait finished. Move to timeout
203 entry or next entry, or handle still. */
205 if (NULL != offset_timeout_LID) {
206 /* Handle timeout_LID */
207 itemid.num = offset_timeout_LID->lid;
208 itemid.type = VCDINFO_ITEM_TYPE_LID;
209 dbg_print(INPUT_DBG_PBC, "timeout to: %d", itemid.num);
210 VCDPlay( p_input, itemid );
213 int num_selections = vcdinf_get_num_selections(p_vcd->pxd.psd);
214 if (num_selections > 0) {
215 /* Pick a random selection. */
216 unsigned int bsn=vcdinf_get_bsn(p_vcd->pxd.psd);
217 int rand_selection=bsn +
218 (int) ((num_selections+0.0)*rand()/(RAND_MAX+1.0));
219 lid_t rand_lid=vcdinfo_selection_get_lid (p_vcd->vcd,
222 itemid.num = rand_lid;
223 itemid.type = VCDINFO_ITEM_TYPE_LID;
224 dbg_print(INPUT_DBG_PBC, "random selection %d, lid: %d",
225 rand_selection - bsn, rand_lid);
226 VCDPlay( p_input, itemid );
228 } else if (p_vcd->in_still) {
229 /* Hack: Just go back and do still again */
231 return READ_STILL_FRAME;
236 case VCDINFO_ITEM_TYPE_NOTFOUND:
237 LOG_ERR( "NOTFOUND in PBC -- not supposed to happen" );
239 case VCDINFO_ITEM_TYPE_SPAREID2:
240 LOG_ERR( "SPAREID2 in PBC -- not supposed to happen" );
242 case VCDINFO_ITEM_TYPE_LID:
243 LOG_ERR( "LID in PBC -- not supposed to happen" );
249 /* FIXME: Should handle autowait ... */
255 Get the next play-item in the list given in the LIDs. Note play-item
256 here refers to list of play-items for a single LID It shouldn't be
257 confused with a user's list of favorite things to play or the
258 "next" field of a LID which moves us to a different LID.
260 vlc_bool_t vcdplayer_inc_play_item( input_thread_t *p_input )
262 thread_vcd_data_t * p_vcd= (thread_vcd_data_t *)p_input->p_access_data;
266 dbg_print(INPUT_DBG_CALL, "called pli: %d", p_vcd->pdi);
268 if ( NULL == p_vcd || NULL == p_vcd->pxd.pld ) return VLC_FALSE;
270 noi = vcdinf_pld_get_noi(p_vcd->pxd.pld);
272 if ( noi <= 0 ) return VLC_FALSE;
274 /* Handle delays like autowait or wait here? */
278 if ( p_vcd->pdi < 0 || p_vcd->pdi >= noi ) return VLC_FALSE;
281 uint16_t trans_itemid_num=vcdinf_pld_get_play_item(p_vcd->pxd.pld,
283 vcdinfo_itemid_t trans_itemid;
285 if (VCDINFO_INVALID_ITEMID == trans_itemid_num) return VLC_FALSE;
287 vcdinfo_classify_itemid(trans_itemid_num, &trans_itemid);
288 dbg_print(INPUT_DBG_PBC, " play-item[%d]: %s",
289 p_vcd->pdi, vcdinfo_pin2str (trans_itemid_num));
290 return VLC_SUCCESS == VCDPlay( p_input, trans_itemid );
295 Play item assocated with the "default" selection.
297 Return VLC_FALSE if there was some problem.
299 vlc_bool_t vcdplayer_play_default( input_thread_t * p_input )
301 thread_vcd_data_t *p_vcd= (thread_vcd_data_t *)p_input->p_access_data;
303 vcdinfo_itemid_t itemid;
306 dbg_print( (INPUT_DBG_CALL|INPUT_DBG_PBC),
312 dbg_print( (INPUT_DBG_CALL|INPUT_DBG_PBC),
313 "current: %d" , p_vcd->play_item.num);
315 itemid.type = p_vcd->play_item.type;
317 if (vcdplayer_pbc_is_on(p_vcd)) {
319 #if defined(LIBVCD_VERSION)
320 lid_t lid=vcdinfo_get_multi_default_lid(p_vcd->vcd, p_vcd->cur_lid,
323 if (VCDINFO_INVALID_LID != lid) {
325 itemid.type = VCDINFO_ITEM_TYPE_LID;
326 dbg_print(INPUT_DBG_PBC, "DEFAULT to %d\n", itemid.num);
328 dbg_print(INPUT_DBG_PBC, "no DEFAULT for LID %d\n", p_vcd->cur_lid);
332 vcdinfo_lid_get_pxd(p_vcd->vcd, &(p_vcd->pxd), p_vcd->cur_lid);
334 switch (p_vcd->pxd.descriptor_type) {
335 case PSD_TYPE_SELECTION_LIST:
336 case PSD_TYPE_EXT_SELECTION_LIST:
337 if (p_vcd->pxd.psd == NULL) return VLC_FALSE;
338 vcdplayer_update_entry( p_input,
339 vcdinfo_get_default_offset(p_vcd->vcd,
341 &itemid.num, "default");
344 case PSD_TYPE_PLAY_LIST:
345 case PSD_TYPE_END_LIST:
346 case PSD_TYPE_COMMAND_LIST:
347 LOG_WARN( "There is no PBC 'default' selection here" );
350 #endif /* LIBVCD_VERSION (< 0.7.21) */
355 /* PBC is not on. "default" selection beginning of current
358 p_vcd->play_item.num = p_vcd->play_item.num;
362 /** ??? p_vcd->update_title(); ***/
363 return VLC_SUCCESS == VCDPlay( p_input, itemid );
368 Play item assocated with the "next" selection.
370 Return VLC_FALSE if there was some problem.
372 vlc_bool_t vcdplayer_play_next( input_thread_t * p_input )
374 thread_vcd_data_t *p_vcd= (thread_vcd_data_t *)p_input->p_access_data;
377 vcdinfo_itemid_t itemid;
379 if (!p_vcd) return VLC_FALSE;
381 dbg_print( (INPUT_DBG_CALL|INPUT_DBG_PBC),
382 "current: %d" , p_vcd->play_item.num);
386 itemid.type = p_vcd->play_item.type;
388 if (vcdplayer_pbc_is_on(p_vcd)) {
390 vcdinfo_lid_get_pxd(obj, &(p_vcd->pxd), p_vcd->cur_lid);
392 switch (p_vcd->pxd.descriptor_type) {
393 case PSD_TYPE_SELECTION_LIST:
394 case PSD_TYPE_EXT_SELECTION_LIST:
395 if (p_vcd->pxd.psd == NULL) return VLC_FALSE;
396 vcdplayer_update_entry( p_input,
397 vcdinf_psd_get_next_offset(p_vcd->pxd.psd),
398 &itemid.num, "next");
399 itemid.type = VCDINFO_ITEM_TYPE_LID;
402 case PSD_TYPE_PLAY_LIST:
403 if (p_vcd->pxd.pld == NULL) return VLC_FALSE;
404 vcdplayer_update_entry( p_input,
405 vcdinf_pld_get_next_offset(p_vcd->pxd.pld),
406 &itemid.num, "next");
407 itemid.type = VCDINFO_ITEM_TYPE_LID;
410 case PSD_TYPE_END_LIST:
411 case PSD_TYPE_COMMAND_LIST:
412 LOG_WARN( "There is no PBC 'next' selection here" );
417 /* PBC is not on. "Next" selection is play_item.num+1 if possible. */
421 switch (p_vcd->play_item.type) {
422 case VCDINFO_ITEM_TYPE_ENTRY:
423 case VCDINFO_ITEM_TYPE_SEGMENT:
424 case VCDINFO_ITEM_TYPE_TRACK:
426 switch (p_vcd->play_item.type) {
427 case VCDINFO_ITEM_TYPE_ENTRY:
428 max_entry = p_vcd->num_entries;
430 case VCDINFO_ITEM_TYPE_SEGMENT:
431 max_entry = p_vcd->num_segments;
433 case VCDINFO_ITEM_TYPE_TRACK:
434 max_entry = p_vcd->num_tracks;
436 default: ; /* Handle exceptional cases below */
439 if (p_vcd->play_item.num+1 < max_entry) {
440 itemid.num = p_vcd->play_item.num+1;
442 LOG_WARN( "At the end - non-PBC 'next' not possible here" );
448 case VCDINFO_ITEM_TYPE_LID:
450 /* Should have handled above. */
451 LOG_WARN( "Internal inconsistency - should not have gotten here." );
459 /** ??? p_vcd->update_title(); ***/
460 return VLC_SUCCESS == VCDPlay( p_input, itemid );
465 Play item assocated with the "prev" selection.
467 Return VLC_FALSE if there was some problem.
469 vlc_bool_t vcdplayer_play_prev( input_thread_t * p_input )
471 thread_vcd_data_t *p_vcd= (thread_vcd_data_t *)p_input->p_access_data;
473 vcdinfo_obj_t *obj = p_vcd->vcd;
474 vcdinfo_itemid_t itemid;
476 dbg_print( (INPUT_DBG_CALL|INPUT_DBG_PBC),
477 "current: %d" , p_vcd->play_item.num);
479 itemid.type = p_vcd->play_item.type;
481 if (vcdplayer_pbc_is_on(p_vcd)) {
483 vcdinfo_lid_get_pxd(obj, &(p_vcd->pxd), p_vcd->cur_lid);
485 switch (p_vcd->pxd.descriptor_type) {
486 case PSD_TYPE_SELECTION_LIST:
487 case PSD_TYPE_EXT_SELECTION_LIST:
488 if (p_vcd->pxd.psd == NULL) return VLC_FALSE;
489 vcdplayer_update_entry( p_input,
490 vcdinf_psd_get_prev_offset(p_vcd->pxd.psd),
491 &itemid.num, "prev");
492 itemid.type = VCDINFO_ITEM_TYPE_LID;
495 case PSD_TYPE_PLAY_LIST:
496 if (p_vcd->pxd.pld == NULL) return VLC_FALSE;
497 vcdplayer_update_entry( p_input,
498 vcdinf_pld_get_prev_offset(p_vcd->pxd.pld),
499 &itemid.num, "prev");
500 itemid.type = VCDINFO_ITEM_TYPE_LID;
503 case PSD_TYPE_END_LIST:
504 case PSD_TYPE_COMMAND_LIST:
505 LOG_WARN( "There is no PBC 'prev' selection here" );
510 /* PBC is not on. "Prev" selection is play_item.num-1 if possible. */
512 int min_entry = (VCDINFO_ITEM_TYPE_ENTRY == p_vcd->play_item.type)
515 if (p_vcd->play_item.num > min_entry) {
516 itemid.num = p_vcd->play_item.num-1;
518 LOG_WARN( "At the beginning - non-PBC 'prev' not possible here" );
524 /** ??? p_vcd->update_title(); ***/
525 return VLC_SUCCESS == VCDPlay( p_input, itemid );
530 Play item assocated with the "return" selection.
532 Return VLC_FALSE if there was some problem.
534 vlc_bool_t vcdplayer_play_return( input_thread_t * p_input )
536 thread_vcd_data_t *p_vcd= (thread_vcd_data_t *)p_input->p_access_data;
538 vcdinfo_obj_t *obj = p_vcd->vcd;
539 vcdinfo_itemid_t itemid;
541 dbg_print( (INPUT_DBG_CALL|INPUT_DBG_PBC),
542 "current: %d" , p_vcd->play_item.num);
544 itemid.type = p_vcd->play_item.type;
546 if (vcdplayer_pbc_is_on(p_vcd)) {
548 vcdinfo_lid_get_pxd(obj, &(p_vcd->pxd), p_vcd->cur_lid);
550 switch (p_vcd->pxd.descriptor_type) {
551 case PSD_TYPE_SELECTION_LIST:
552 case PSD_TYPE_EXT_SELECTION_LIST:
553 if (p_vcd->pxd.psd == NULL) return VLC_FALSE;
554 vcdplayer_update_entry( p_input,
555 vcdinf_psd_get_return_offset(p_vcd->pxd.psd),
556 &itemid.num, "return");
557 itemid.type = VCDINFO_ITEM_TYPE_LID;
560 case PSD_TYPE_PLAY_LIST:
561 if (p_vcd->pxd.pld == NULL) return VLC_FALSE;
562 vcdplayer_update_entry( p_input,
563 vcdinf_pld_get_return_offset(p_vcd->pxd.pld),
564 &itemid.num, "return");
565 itemid.type = VCDINFO_ITEM_TYPE_LID;
568 case PSD_TYPE_END_LIST:
569 case PSD_TYPE_COMMAND_LIST:
570 LOG_WARN( "There is no PBC 'return' selection here" );
575 /* PBC is not on. "Return" selection is min_entry if possible. */
577 p_vcd->play_item.num = (VCDINFO_ITEM_TYPE_ENTRY == p_vcd->play_item.type)
582 /** ??? p_vcd->update_title(); ***/
583 return VLC_SUCCESS == VCDPlay( p_input, itemid );