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>
6 * $Id: vcdplayer.c,v 1.9 2003/12/13 12:56:14 rocky Exp $
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 Return true if playback control (PBC) is on
50 vcdplayer_pbc_is_on(const thread_vcd_data_t *p_vcd)
52 return VCDINFO_INVALID_ENTRY != p_vcd->cur_lid;
56 vcdplayer_update_entry( input_thread_t * p_input, uint16_t ofs,
57 uint16_t *entry, const char *label)
59 thread_vcd_data_t * p_vcd= (thread_vcd_data_t *)p_input->p_access_data;
61 if ( ofs == VCDINFO_INVALID_OFFSET ) {
62 *entry = VCDINFO_INVALID_ENTRY;
64 vcdinfo_offset_t *off_t = vcdinfo_get_offset_t(p_vcd->vcd, ofs);
67 dbg_print(INPUT_DBG_PBC, "%s: LID %d\n", label, off_t->lid);
69 *entry = VCDINFO_INVALID_ENTRY;
73 /* Handles navigation when NOT in PBC reaching the end of a play item.
75 The navigations rules here may be sort of made up, but the intent
76 is to do something that's probably right or helpful.
78 return true if the caller should return.
80 vcdplayer_read_status_t
81 vcdplayer_non_pbc_nav ( input_thread_t * p_input )
83 thread_vcd_data_t * p_vcd= (thread_vcd_data_t *)p_input->p_access_data;
85 /* Not in playback control. Do we advance automatically or stop? */
86 switch (p_vcd->play_item.type) {
87 case VCDINFO_ITEM_TYPE_TRACK:
88 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.
261 vcdplayer_inc_play_item( input_thread_t *p_input )
263 thread_vcd_data_t * p_vcd= (thread_vcd_data_t *)p_input->p_access_data;
267 dbg_print(INPUT_DBG_CALL, "called pli: %d", p_vcd->pdi);
269 if ( NULL == p_vcd || NULL == p_vcd->pxd.pld ) return false;
271 noi = vcdinf_pld_get_noi(p_vcd->pxd.pld);
273 if ( noi <= 0 ) return false;
275 /* Handle delays like autowait or wait here? */
279 if ( p_vcd->pdi < 0 || p_vcd->pdi >= noi ) return false;
282 uint16_t trans_itemid_num=vcdinf_pld_get_play_item(p_vcd->pxd.pld,
284 vcdinfo_itemid_t trans_itemid;
286 if (VCDINFO_INVALID_ITEMID == trans_itemid_num) return false;
288 vcdinfo_classify_itemid(trans_itemid_num, &trans_itemid);
289 dbg_print(INPUT_DBG_PBC, " play-item[%d]: %s",
290 p_vcd->pdi, vcdinfo_pin2str (trans_itemid_num));
291 return VLC_SUCCESS == VCDPlay( p_input, trans_itemid );
296 Play item assocated with the "default" selection.
298 Return false if there was some problem.
301 vcdplayer_play_default( input_thread_t * p_input )
303 thread_vcd_data_t *p_vcd= (thread_vcd_data_t *)p_input->p_access_data;
305 vcdinfo_itemid_t itemid;
307 dbg_print( (INPUT_DBG_CALL|INPUT_DBG_PBC),
308 "current: %d" , p_vcd->play_item.num);
310 itemid.type = p_vcd->play_item.type;
312 if (vcdplayer_pbc_is_on(p_vcd)) {
314 lid_t lid=vcdinfo_get_multi_default_lid(p_vcd->vcd, p_vcd->cur_lid,
317 if (VCDINFO_INVALID_LID != lid) {
319 itemid.type = VCDINFO_ITEM_TYPE_LID;
320 dbg_print(INPUT_DBG_PBC, "DEFAULT to %d\n", itemid.num);
322 dbg_print(INPUT_DBG_PBC, "no DEFAULT for LID %d\n", p_vcd->cur_lid);
328 /* PBC is not on. "default" selection beginning of current
331 p_vcd->play_item.num = p_vcd->play_item.num;
335 /** ??? p_vcd->update_title(); ***/
336 return VLC_SUCCESS == VCDPlay( p_input, itemid );
341 Play item assocated with the "next" selection.
343 Return false if there was some problem.
346 vcdplayer_play_next( input_thread_t * p_input )
348 thread_vcd_data_t *p_vcd= (thread_vcd_data_t *)p_input->p_access_data;
351 vcdinfo_itemid_t itemid;
353 if (!p_vcd) return false;
355 dbg_print( (INPUT_DBG_CALL|INPUT_DBG_PBC),
356 "current: %d" , p_vcd->play_item.num);
360 itemid.type = p_vcd->play_item.type;
362 if (vcdplayer_pbc_is_on(p_vcd)) {
364 vcdinfo_lid_get_pxd(obj, &(p_vcd->pxd), p_vcd->cur_lid);
366 switch (p_vcd->pxd.descriptor_type) {
367 case PSD_TYPE_SELECTION_LIST:
368 case PSD_TYPE_EXT_SELECTION_LIST:
369 if (p_vcd->pxd.psd == NULL) return false;
370 vcdplayer_update_entry( p_input,
371 vcdinf_psd_get_next_offset(p_vcd->pxd.psd),
372 &itemid.num, "next");
373 itemid.type = VCDINFO_ITEM_TYPE_LID;
376 case PSD_TYPE_PLAY_LIST:
377 if (p_vcd->pxd.pld == NULL) return false;
378 vcdplayer_update_entry( p_input,
379 vcdinf_pld_get_next_offset(p_vcd->pxd.pld),
380 &itemid.num, "next");
381 itemid.type = VCDINFO_ITEM_TYPE_LID;
384 case PSD_TYPE_END_LIST:
385 case PSD_TYPE_COMMAND_LIST:
386 LOG_WARN( "There is no PBC 'next' selection here" );
391 /* PBC is not on. "Next" selection is play_item.num+1 if possible. */
395 switch (p_vcd->play_item.type) {
396 case VCDINFO_ITEM_TYPE_ENTRY:
397 case VCDINFO_ITEM_TYPE_SEGMENT:
398 case VCDINFO_ITEM_TYPE_TRACK:
400 switch (p_vcd->play_item.type) {
401 case VCDINFO_ITEM_TYPE_ENTRY:
402 max_entry = p_vcd->num_entries;
404 case VCDINFO_ITEM_TYPE_SEGMENT:
405 max_entry = p_vcd->num_segments;
407 case VCDINFO_ITEM_TYPE_TRACK:
408 max_entry = p_vcd->num_tracks;
410 default: ; /* Handle exceptional cases below */
413 if (p_vcd->play_item.num+1 < max_entry) {
414 itemid.num = p_vcd->play_item.num+1;
416 LOG_WARN( "At the end - non-PBC 'next' not possible here" );
422 case VCDINFO_ITEM_TYPE_LID:
424 /* Should have handled above. */
425 LOG_WARN( "Internal inconsistency - should not have gotten here." );
433 /** ??? p_vcd->update_title(); ***/
434 return VLC_SUCCESS == VCDPlay( p_input, itemid );
439 Play item assocated with the "prev" selection.
441 Return false if there was some problem.
444 vcdplayer_play_prev( input_thread_t * p_input )
446 thread_vcd_data_t *p_vcd= (thread_vcd_data_t *)p_input->p_access_data;
448 vcdinfo_obj_t *obj = p_vcd->vcd;
449 vcdinfo_itemid_t itemid;
451 dbg_print( (INPUT_DBG_CALL|INPUT_DBG_PBC),
452 "current: %d" , p_vcd->play_item.num);
454 itemid.type = p_vcd->play_item.type;
456 if (vcdplayer_pbc_is_on(p_vcd)) {
458 vcdinfo_lid_get_pxd(obj, &(p_vcd->pxd), p_vcd->cur_lid);
460 switch (p_vcd->pxd.descriptor_type) {
461 case PSD_TYPE_SELECTION_LIST:
462 case PSD_TYPE_EXT_SELECTION_LIST:
463 if (p_vcd->pxd.psd == NULL) return false;
464 vcdplayer_update_entry( p_input,
465 vcdinf_psd_get_prev_offset(p_vcd->pxd.psd),
466 &itemid.num, "prev");
467 itemid.type = VCDINFO_ITEM_TYPE_LID;
470 case PSD_TYPE_PLAY_LIST:
471 if (p_vcd->pxd.pld == NULL) return false;
472 vcdplayer_update_entry( p_input,
473 vcdinf_pld_get_prev_offset(p_vcd->pxd.pld),
474 &itemid.num, "prev");
475 itemid.type = VCDINFO_ITEM_TYPE_LID;
478 case PSD_TYPE_END_LIST:
479 case PSD_TYPE_COMMAND_LIST:
480 LOG_WARN( "There is no PBC 'prev' selection here" );
485 /* PBC is not on. "Prev" selection is play_item.num-1 if possible. */
487 int min_entry = (VCDINFO_ITEM_TYPE_ENTRY == p_vcd->play_item.type)
490 if (p_vcd->play_item.num > min_entry) {
491 itemid.num = p_vcd->play_item.num-1;
493 LOG_WARN( "At the beginning - non-PBC 'prev' not possible here" );
499 /** ??? p_vcd->update_title(); ***/
500 return VLC_SUCCESS == VCDPlay( p_input, itemid );
505 Play item assocated with the "return" selection.
507 Return false if there was some problem.
510 vcdplayer_play_return( input_thread_t * p_input )
512 thread_vcd_data_t *p_vcd= (thread_vcd_data_t *)p_input->p_access_data;
514 vcdinfo_obj_t *obj = p_vcd->vcd;
515 vcdinfo_itemid_t itemid;
517 dbg_print( (INPUT_DBG_CALL|INPUT_DBG_PBC),
518 "current: %d" , p_vcd->play_item.num);
520 itemid.type = p_vcd->play_item.type;
522 if (vcdplayer_pbc_is_on(p_vcd)) {
524 vcdinfo_lid_get_pxd(obj, &(p_vcd->pxd), p_vcd->cur_lid);
526 switch (p_vcd->pxd.descriptor_type) {
527 case PSD_TYPE_SELECTION_LIST:
528 case PSD_TYPE_EXT_SELECTION_LIST:
529 if (p_vcd->pxd.psd == NULL) return false;
530 vcdplayer_update_entry( p_input,
531 vcdinf_psd_get_return_offset(p_vcd->pxd.psd),
532 &itemid.num, "return");
533 itemid.type = VCDINFO_ITEM_TYPE_LID;
536 case PSD_TYPE_PLAY_LIST:
537 if (p_vcd->pxd.pld == NULL) return false;
538 vcdplayer_update_entry( p_input,
539 vcdinf_pld_get_return_offset(p_vcd->pxd.pld),
540 &itemid.num, "return");
541 itemid.type = VCDINFO_ITEM_TYPE_LID;
544 case PSD_TYPE_END_LIST:
545 case PSD_TYPE_COMMAND_LIST:
546 LOG_WARN( "There is no PBC 'return' selection here" );
551 /* PBC is not on. "Return" selection is min_entry if possible. */
553 p_vcd->play_item.num = (VCDINFO_ITEM_TYPE_ENTRY == p_vcd->play_item.type)
558 /** ??? p_vcd->update_title(); ***/
559 return VLC_SUCCESS == VCDPlay( p_input, itemid );