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 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 = vcdinfo_get_offset_t(p_vcd->vcd, ofs);
67 dbg_print(INPUT_DBG_PBC, "%s: LID %d\n", label, off->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: {
90 dbg_print( INPUT_DBG_LSN, "new track %d, lsn %d", p_vcd->cur_track,
91 p_vcd->p_sectors[p_vcd->cur_track+1] );
95 case VCDINFO_ITEM_TYPE_SPAREID2:
96 dbg_print( (INPUT_DBG_STILL|INPUT_DBG_LSN),
99 p_input->stream.b_seekable = 0;
102 return READ_STILL_FRAME ;
105 case VCDINFO_ITEM_TYPE_NOTFOUND:
106 LOG_ERR ("NOTFOUND outside PBC -- not supposed to happen");
108 case VCDINFO_ITEM_TYPE_LID:
109 LOG_ERR ("LID outside PBC -- not supposed to happen");
111 case VCDINFO_ITEM_TYPE_SEGMENT:
112 /* Hack: Just go back and do still again */
114 p_input->stream.b_seekable = 0;
117 dbg_print( (INPUT_DBG_STILL|INPUT_DBG_LSN),
118 "End of Segment - looping" );
119 return READ_STILL_FRAME;
126 /* Handles PBC navigation when reaching the end of a play item. */
127 vcdplayer_read_status_t
128 vcdplayer_pbc_nav ( input_thread_t * p_input )
130 thread_vcd_data_t * p_vcd= (thread_vcd_data_t *)p_input->p_access_data;
132 /* We are in playback control. */
133 vcdinfo_itemid_t itemid;
135 /* The end of an entry is really the end of the associated
136 sequence (or track). */
138 if ( (VCDINFO_ITEM_TYPE_ENTRY == p_vcd->play_item.type) &&
139 (p_vcd->cur_lsn < p_vcd->end_lsn) ) {
140 /* Set up to just continue to the next entry */
141 p_vcd->play_item.num++;
142 dbg_print( (INPUT_DBG_LSN|INPUT_DBG_PBC),
143 "continuing into next entry: %u", p_vcd->play_item.num);
144 VCDPlay( p_input, p_vcd->play_item );
145 /* p_vcd->update_title(); */
149 switch (p_vcd->pxd.descriptor_type) {
150 case PSD_TYPE_END_LIST:
153 case PSD_TYPE_PLAY_LIST: {
154 int wait_time = vcdinf_get_wait_time(p_vcd->pxd.pld);
156 dbg_print(INPUT_DBG_PBC, "playlist wait_time: %d", wait_time);
158 if (vcdplayer_inc_play_item(p_input))
161 /* Handle any wait time given. */
162 if (p_vcd->in_still) {
163 vcdIntfStillTime( p_vcd->p_intf, wait_time );
164 return READ_STILL_FRAME;
167 vcdplayer_update_entry( p_input,
168 vcdinf_pld_get_next_offset(p_vcd->pxd.pld),
169 &itemid.num, "next" );
170 itemid.type = VCDINFO_ITEM_TYPE_LID;
171 VCDPlay( p_input, itemid );
174 case PSD_TYPE_SELECTION_LIST: /* Selection List (+Ext. for SVCD) */
175 case PSD_TYPE_EXT_SELECTION_LIST: /* Extended Selection List (VCD2.0) */
177 int wait_time = vcdinf_get_timeout_time(p_vcd->pxd.psd);
178 uint16_t timeout_offs = vcdinf_get_timeout_offset(p_vcd->pxd.psd);
179 uint16_t max_loop = vcdinf_get_loop_count(p_vcd->pxd.psd);
180 vcdinfo_offset_t *offset_timeout_LID =
181 vcdinfo_get_offset_t(p_vcd->vcd, timeout_offs);
183 dbg_print(INPUT_DBG_PBC, "wait_time: %d, looped: %d, max_loop %d",
184 wait_time, p_vcd->loop_count, max_loop);
186 /* Handle any wait time given */
187 if (p_vcd->in_still) {
188 vcdIntfStillTime( p_vcd->p_intf, wait_time );
189 return READ_STILL_FRAME;
192 /* Handle any looping given. */
193 if ( max_loop == 0 || p_vcd->loop_count < max_loop ) {
195 if (p_vcd->loop_count == 0x7f) p_vcd->loop_count = 0;
196 VCDSeek( p_input, 0 );
197 /* if (p_vcd->in_still) p_vcd->force_redisplay();*/
201 /* Looping finished and wait finished. Move to timeout
202 entry or next entry, or handle still. */
204 if (NULL != offset_timeout_LID) {
205 /* Handle timeout_LID */
206 itemid.num = offset_timeout_LID->lid;
207 itemid.type = VCDINFO_ITEM_TYPE_LID;
208 dbg_print(INPUT_DBG_PBC, "timeout to: %d", itemid.num);
209 VCDPlay( p_input, itemid );
212 int num_selections = vcdinf_get_num_selections(p_vcd->pxd.psd);
213 if (num_selections > 0) {
214 /* Pick a random selection. */
215 unsigned int bsn=vcdinf_get_bsn(p_vcd->pxd.psd);
216 int rand_selection=bsn +
217 (int) ((num_selections+0.0)*rand()/(RAND_MAX+1.0));
218 lid_t rand_lid=vcdinfo_selection_get_lid (p_vcd->vcd,
221 itemid.num = rand_lid;
222 itemid.type = VCDINFO_ITEM_TYPE_LID;
223 dbg_print(INPUT_DBG_PBC, "random selection %d, lid: %d",
224 rand_selection - bsn, rand_lid);
225 VCDPlay( p_input, itemid );
227 } else if (p_vcd->in_still) {
228 /* Hack: Just go back and do still again */
230 return READ_STILL_FRAME;
235 case VCDINFO_ITEM_TYPE_NOTFOUND:
236 LOG_ERR( "NOTFOUND in PBC -- not supposed to happen" );
238 case VCDINFO_ITEM_TYPE_SPAREID2:
239 LOG_ERR( "SPAREID2 in PBC -- not supposed to happen" );
241 case VCDINFO_ITEM_TYPE_LID:
242 LOG_ERR( "LID in PBC -- not supposed to happen" );
248 /* FIXME: Should handle autowait ... */
254 Get the next play-item in the list given in the LIDs. Note play-item
255 here refers to list of play-items for a single LID It shouldn't be
256 confused with a user's list of favorite things to play or the
257 "next" field of a LID which moves us to a different LID.
260 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 false;
270 noi = vcdinf_pld_get_noi(p_vcd->pxd.pld);
272 if ( noi <= 0 ) return false;
274 /* Handle delays like autowait or wait here? */
278 if ( p_vcd->pdi < 0 || p_vcd->pdi >= noi ) return 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 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 false if there was some problem.
300 vcdplayer_play_default( input_thread_t * p_input )
302 thread_vcd_data_t *p_vcd= (thread_vcd_data_t *)p_input->p_access_data;
304 vcdinfo_itemid_t itemid;
306 dbg_print( (INPUT_DBG_CALL|INPUT_DBG_PBC),
307 "current: %d" , p_vcd->play_item.num);
309 itemid.type = p_vcd->play_item.type;
311 if (vcdplayer_pbc_is_on(p_vcd)) {
313 lid_t lid=vcdinfo_get_multi_default_lid(p_vcd->vcd, p_vcd->cur_lid,
316 if (VCDINFO_INVALID_LID != lid) {
318 itemid.type = VCDINFO_ITEM_TYPE_LID;
319 dbg_print(INPUT_DBG_PBC, "DEFAULT to %d\n", itemid.num);
321 dbg_print(INPUT_DBG_PBC, "no DEFAULT for LID %d\n", p_vcd->cur_lid);
327 /* PBC is not on. "default" selection beginning of current
330 p_vcd->play_item.num = p_vcd->play_item.num;
334 /** ??? p_vcd->update_title(); ***/
335 return VLC_SUCCESS == VCDPlay( p_input, itemid );
340 Play item assocated with the "next" selection.
342 Return false if there was some problem.
345 vcdplayer_play_next( input_thread_t * p_input )
347 thread_vcd_data_t *p_vcd= (thread_vcd_data_t *)p_input->p_access_data;
350 vcdinfo_itemid_t itemid;
352 if (!p_vcd) return false;
354 dbg_print( (INPUT_DBG_CALL|INPUT_DBG_PBC),
355 "current: %d" , p_vcd->play_item.num);
359 itemid.type = p_vcd->play_item.type;
361 if (vcdplayer_pbc_is_on(p_vcd)) {
363 vcdinfo_lid_get_pxd(obj, &(p_vcd->pxd), p_vcd->cur_lid);
365 switch (p_vcd->pxd.descriptor_type) {
366 case PSD_TYPE_SELECTION_LIST:
367 case PSD_TYPE_EXT_SELECTION_LIST:
368 if (p_vcd->pxd.psd == NULL) return false;
369 vcdplayer_update_entry( p_input,
370 vcdinf_psd_get_next_offset(p_vcd->pxd.psd),
371 &itemid.num, "next");
372 itemid.type = VCDINFO_ITEM_TYPE_LID;
375 case PSD_TYPE_PLAY_LIST:
376 if (p_vcd->pxd.pld == NULL) return false;
377 vcdplayer_update_entry( p_input,
378 vcdinf_pld_get_next_offset(p_vcd->pxd.pld),
379 &itemid.num, "next");
380 itemid.type = VCDINFO_ITEM_TYPE_LID;
383 case PSD_TYPE_END_LIST:
384 case PSD_TYPE_COMMAND_LIST:
385 LOG_WARN( "There is no PBC 'next' selection here" );
390 /* PBC is not on. "Next" selection is play_item.num+1 if possible. */
394 switch (p_vcd->play_item.type) {
395 case VCDINFO_ITEM_TYPE_ENTRY:
396 case VCDINFO_ITEM_TYPE_SEGMENT:
397 case VCDINFO_ITEM_TYPE_TRACK:
399 switch (p_vcd->play_item.type) {
400 case VCDINFO_ITEM_TYPE_ENTRY:
401 max_entry = p_vcd->num_entries;
403 case VCDINFO_ITEM_TYPE_SEGMENT:
404 max_entry = p_vcd->num_segments;
406 case VCDINFO_ITEM_TYPE_TRACK:
407 max_entry = p_vcd->num_tracks;
409 default: ; /* Handle exceptional cases below */
412 if (p_vcd->play_item.num+1 < max_entry) {
413 itemid.num = p_vcd->play_item.num+1;
415 LOG_WARN( "At the end - non-PBC 'next' not possible here" );
421 case VCDINFO_ITEM_TYPE_LID:
423 /* Should have handled above. */
424 LOG_WARN( "Internal inconsistency - should not have gotten here." );
432 /** ??? p_vcd->update_title(); ***/
433 return VLC_SUCCESS == VCDPlay( p_input, itemid );
438 Play item assocated with the "prev" selection.
440 Return false if there was some problem.
443 vcdplayer_play_prev( input_thread_t * p_input )
445 thread_vcd_data_t *p_vcd= (thread_vcd_data_t *)p_input->p_access_data;
447 vcdinfo_obj_t *obj = p_vcd->vcd;
448 vcdinfo_itemid_t itemid;
450 dbg_print( (INPUT_DBG_CALL|INPUT_DBG_PBC),
451 "current: %d" , p_vcd->play_item.num);
453 itemid.type = p_vcd->play_item.type;
455 if (vcdplayer_pbc_is_on(p_vcd)) {
457 vcdinfo_lid_get_pxd(obj, &(p_vcd->pxd), p_vcd->cur_lid);
459 switch (p_vcd->pxd.descriptor_type) {
460 case PSD_TYPE_SELECTION_LIST:
461 case PSD_TYPE_EXT_SELECTION_LIST:
462 if (p_vcd->pxd.psd == NULL) return false;
463 vcdplayer_update_entry( p_input,
464 vcdinf_psd_get_prev_offset(p_vcd->pxd.psd),
465 &itemid.num, "prev");
466 itemid.type = VCDINFO_ITEM_TYPE_LID;
469 case PSD_TYPE_PLAY_LIST:
470 if (p_vcd->pxd.pld == NULL) return false;
471 vcdplayer_update_entry( p_input,
472 vcdinf_pld_get_prev_offset(p_vcd->pxd.pld),
473 &itemid.num, "prev");
474 itemid.type = VCDINFO_ITEM_TYPE_LID;
477 case PSD_TYPE_END_LIST:
478 case PSD_TYPE_COMMAND_LIST:
479 LOG_WARN( "There is no PBC 'prev' selection here" );
484 /* PBC is not on. "Prev" selection is play_item.num-1 if possible. */
486 int min_entry = (VCDINFO_ITEM_TYPE_ENTRY == p_vcd->play_item.type)
489 if (p_vcd->play_item.num > min_entry) {
490 itemid.num = p_vcd->play_item.num-1;
492 LOG_WARN( "At the beginning - non-PBC 'prev' not possible here" );
498 /** ??? p_vcd->update_title(); ***/
499 return VLC_SUCCESS == VCDPlay( p_input, itemid );
504 Play item assocated with the "return" selection.
506 Return false if there was some problem.
509 vcdplayer_play_return( input_thread_t * p_input )
511 thread_vcd_data_t *p_vcd= (thread_vcd_data_t *)p_input->p_access_data;
513 vcdinfo_obj_t *obj = p_vcd->vcd;
514 vcdinfo_itemid_t itemid;
516 dbg_print( (INPUT_DBG_CALL|INPUT_DBG_PBC),
517 "current: %d" , p_vcd->play_item.num);
519 itemid.type = p_vcd->play_item.type;
521 if (vcdplayer_pbc_is_on(p_vcd)) {
523 vcdinfo_lid_get_pxd(obj, &(p_vcd->pxd), p_vcd->cur_lid);
525 switch (p_vcd->pxd.descriptor_type) {
526 case PSD_TYPE_SELECTION_LIST:
527 case PSD_TYPE_EXT_SELECTION_LIST:
528 if (p_vcd->pxd.psd == NULL) return false;
529 vcdplayer_update_entry( p_input,
530 vcdinf_psd_get_return_offset(p_vcd->pxd.psd),
531 &itemid.num, "return");
532 itemid.type = VCDINFO_ITEM_TYPE_LID;
535 case PSD_TYPE_PLAY_LIST:
536 if (p_vcd->pxd.pld == NULL) return false;
537 vcdplayer_update_entry( p_input,
538 vcdinf_pld_get_return_offset(p_vcd->pxd.pld),
539 &itemid.num, "return");
540 itemid.type = VCDINFO_ITEM_TYPE_LID;
543 case PSD_TYPE_END_LIST:
544 case PSD_TYPE_COMMAND_LIST:
545 LOG_WARN( "There is no PBC 'return' selection here" );
550 /* PBC is not on. "Return" selection is min_entry if possible. */
552 p_vcd->play_item.num = (VCDINFO_ITEM_TYPE_ENTRY == p_vcd->play_item.type)
557 /** ??? p_vcd->update_title(); ***/
558 return VLC_SUCCESS == VCDPlay( p_input, itemid );