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 true if playback control (PBC) is on
54 vcdplayer_pbc_is_on(const thread_vcd_data_t *p_vcd)
56 return VCDINFO_INVALID_ENTRY != p_vcd->cur_lid;
60 vcdplayer_update_entry( input_thread_t * p_input, uint16_t ofs,
61 uint16_t *entry, const char *label)
63 thread_vcd_data_t * p_vcd= (thread_vcd_data_t *)p_input->p_access_data;
65 if ( ofs == VCDINFO_INVALID_OFFSET ) {
66 *entry = VCDINFO_INVALID_ENTRY;
68 vcdinfo_offset_t *off = vcdinfo_get_offset_t(p_vcd->vcd, ofs);
71 dbg_print(INPUT_DBG_PBC, "%s: LID %d\n", label, off->lid);
73 *entry = VCDINFO_INVALID_ENTRY;
77 /* Handles navigation when NOT in PBC reaching the end of a play item.
79 The navigations rules here may be sort of made up, but the intent
80 is to do something that's probably right or helpful.
82 return true if the caller should return.
84 vcdplayer_read_status_t
85 vcdplayer_non_pbc_nav ( input_thread_t * p_input )
87 thread_vcd_data_t * p_vcd= (thread_vcd_data_t *)p_input->p_access_data;
89 /* Not in playback control. Do we advance automatically or stop? */
90 switch (p_vcd->play_item.type) {
91 case VCDINFO_ITEM_TYPE_TRACK:
92 case VCDINFO_ITEM_TYPE_ENTRY: {
94 dbg_print( INPUT_DBG_LSN, "new track %d, lsn %d", p_vcd->cur_track,
95 p_vcd->p_sectors[p_vcd->cur_track+1] );
99 case VCDINFO_ITEM_TYPE_SPAREID2:
100 dbg_print( (INPUT_DBG_STILL|INPUT_DBG_LSN),
103 p_input->stream.b_seekable = 0;
106 return READ_STILL_FRAME ;
109 case VCDINFO_ITEM_TYPE_NOTFOUND:
110 LOG_ERR ("NOTFOUND outside PBC -- not supposed to happen");
112 case VCDINFO_ITEM_TYPE_LID:
113 LOG_ERR ("LID outside PBC -- not supposed to happen");
115 case VCDINFO_ITEM_TYPE_SEGMENT:
116 /* Hack: Just go back and do still again */
118 p_input->stream.b_seekable = 0;
121 dbg_print( (INPUT_DBG_STILL|INPUT_DBG_LSN),
122 "End of Segment - looping" );
123 return READ_STILL_FRAME;
130 /* Handles PBC navigation when reaching the end of a play item. */
131 vcdplayer_read_status_t
132 vcdplayer_pbc_nav ( input_thread_t * p_input )
134 thread_vcd_data_t * p_vcd= (thread_vcd_data_t *)p_input->p_access_data;
136 /* We are in playback control. */
137 vcdinfo_itemid_t itemid;
139 /* The end of an entry is really the end of the associated
140 sequence (or track). */
142 if ( (VCDINFO_ITEM_TYPE_ENTRY == p_vcd->play_item.type) &&
143 (p_vcd->cur_lsn < p_vcd->end_lsn) ) {
144 /* Set up to just continue to the next entry */
145 p_vcd->play_item.num++;
146 dbg_print( (INPUT_DBG_LSN|INPUT_DBG_PBC),
147 "continuing into next entry: %u", p_vcd->play_item.num);
148 VCDPlay( p_input, p_vcd->play_item );
149 /* p_vcd->update_title(); */
153 switch (p_vcd->pxd.descriptor_type) {
154 case PSD_TYPE_END_LIST:
157 case PSD_TYPE_PLAY_LIST: {
158 int wait_time = vcdinf_get_wait_time(p_vcd->pxd.pld);
160 dbg_print(INPUT_DBG_PBC, "playlist wait_time: %d", wait_time);
162 if (vcdplayer_inc_play_item(p_input))
165 /* Handle any wait time given. */
166 if (p_vcd->in_still) {
167 vcdIntfStillTime( p_vcd->p_intf, wait_time );
168 return READ_STILL_FRAME;
171 vcdplayer_update_entry( p_input,
172 vcdinf_pld_get_next_offset(p_vcd->pxd.pld),
173 &itemid.num, "next" );
174 itemid.type = VCDINFO_ITEM_TYPE_LID;
175 VCDPlay( p_input, itemid );
178 case PSD_TYPE_SELECTION_LIST: /* Selection List (+Ext. for SVCD) */
179 case PSD_TYPE_EXT_SELECTION_LIST: /* Extended Selection List (VCD2.0) */
181 int wait_time = vcdinf_get_timeout_time(p_vcd->pxd.psd);
182 uint16_t timeout_offs = vcdinf_get_timeout_offset(p_vcd->pxd.psd);
183 uint16_t max_loop = vcdinf_get_loop_count(p_vcd->pxd.psd);
184 vcdinfo_offset_t *offset_timeout_LID =
185 vcdinfo_get_offset_t(p_vcd->vcd, timeout_offs);
187 dbg_print(INPUT_DBG_PBC, "wait_time: %d, looped: %d, max_loop %d",
188 wait_time, p_vcd->loop_count, max_loop);
190 /* Handle any wait time given */
191 if (p_vcd->in_still) {
192 vcdIntfStillTime( p_vcd->p_intf, wait_time );
193 return READ_STILL_FRAME;
196 /* Handle any looping given. */
197 if ( max_loop == 0 || p_vcd->loop_count < max_loop ) {
199 if (p_vcd->loop_count == 0x7f) p_vcd->loop_count = 0;
200 VCDSeek( p_input, 0 );
201 /* if (p_vcd->in_still) p_vcd->force_redisplay();*/
205 /* Looping finished and wait finished. Move to timeout
206 entry or next entry, or handle still. */
208 if (NULL != offset_timeout_LID) {
209 /* Handle timeout_LID */
210 itemid.num = offset_timeout_LID->lid;
211 itemid.type = VCDINFO_ITEM_TYPE_LID;
212 dbg_print(INPUT_DBG_PBC, "timeout to: %d", itemid.num);
213 VCDPlay( p_input, itemid );
216 int num_selections = vcdinf_get_num_selections(p_vcd->pxd.psd);
217 if (num_selections > 0) {
218 /* Pick a random selection. */
219 unsigned int bsn=vcdinf_get_bsn(p_vcd->pxd.psd);
220 int rand_selection=bsn +
221 (int) ((num_selections+0.0)*rand()/(RAND_MAX+1.0));
222 lid_t rand_lid=vcdinfo_selection_get_lid (p_vcd->vcd,
225 itemid.num = rand_lid;
226 itemid.type = VCDINFO_ITEM_TYPE_LID;
227 dbg_print(INPUT_DBG_PBC, "random selection %d, lid: %d",
228 rand_selection - bsn, rand_lid);
229 VCDPlay( p_input, itemid );
231 } else if (p_vcd->in_still) {
232 /* Hack: Just go back and do still again */
234 return READ_STILL_FRAME;
239 case VCDINFO_ITEM_TYPE_NOTFOUND:
240 LOG_ERR( "NOTFOUND in PBC -- not supposed to happen" );
242 case VCDINFO_ITEM_TYPE_SPAREID2:
243 LOG_ERR( "SPAREID2 in PBC -- not supposed to happen" );
245 case VCDINFO_ITEM_TYPE_LID:
246 LOG_ERR( "LID in PBC -- not supposed to happen" );
252 /* FIXME: Should handle autowait ... */
258 Get the next play-item in the list given in the LIDs. Note play-item
259 here refers to list of play-items for a single LID It shouldn't be
260 confused with a user's list of favorite things to play or the
261 "next" field of a LID which moves us to a different LID.
264 vcdplayer_inc_play_item( input_thread_t *p_input )
266 thread_vcd_data_t * p_vcd= (thread_vcd_data_t *)p_input->p_access_data;
270 dbg_print(INPUT_DBG_CALL, "called pli: %d", p_vcd->pdi);
272 if ( NULL == p_vcd || NULL == p_vcd->pxd.pld ) return false;
274 noi = vcdinf_pld_get_noi(p_vcd->pxd.pld);
276 if ( noi <= 0 ) return false;
278 /* Handle delays like autowait or wait here? */
282 if ( p_vcd->pdi < 0 || p_vcd->pdi >= noi ) return false;
285 uint16_t trans_itemid_num=vcdinf_pld_get_play_item(p_vcd->pxd.pld,
287 vcdinfo_itemid_t trans_itemid;
289 if (VCDINFO_INVALID_ITEMID == trans_itemid_num) return false;
291 vcdinfo_classify_itemid(trans_itemid_num, &trans_itemid);
292 dbg_print(INPUT_DBG_PBC, " play-item[%d]: %s",
293 p_vcd->pdi, vcdinfo_pin2str (trans_itemid_num));
294 return VLC_SUCCESS == VCDPlay( p_input, trans_itemid );
299 Play item assocated with the "default" selection.
301 Return false if there was some problem.
304 vcdplayer_play_default( input_thread_t * p_input )
306 thread_vcd_data_t *p_vcd= (thread_vcd_data_t *)p_input->p_access_data;
308 vcdinfo_itemid_t itemid;
310 dbg_print( (INPUT_DBG_CALL|INPUT_DBG_PBC),
311 "current: %d" , p_vcd->play_item.num);
313 itemid.type = p_vcd->play_item.type;
315 if (vcdplayer_pbc_is_on(p_vcd)) {
317 lid_t lid=vcdinfo_get_multi_default_lid(p_vcd->vcd, p_vcd->cur_lid,
320 if (VCDINFO_INVALID_LID != lid) {
322 itemid.type = VCDINFO_ITEM_TYPE_LID;
323 dbg_print(INPUT_DBG_PBC, "DEFAULT to %d\n", itemid.num);
325 dbg_print(INPUT_DBG_PBC, "no DEFAULT for LID %d\n", p_vcd->cur_lid);
331 /* PBC is not on. "default" selection beginning of current
334 p_vcd->play_item.num = p_vcd->play_item.num;
338 /** ??? p_vcd->update_title(); ***/
339 return VLC_SUCCESS == VCDPlay( p_input, itemid );
344 Play item assocated with the "next" selection.
346 Return false if there was some problem.
349 vcdplayer_play_next( input_thread_t * p_input )
351 thread_vcd_data_t *p_vcd= (thread_vcd_data_t *)p_input->p_access_data;
354 vcdinfo_itemid_t itemid;
356 if (!p_vcd) return false;
358 dbg_print( (INPUT_DBG_CALL|INPUT_DBG_PBC),
359 "current: %d" , p_vcd->play_item.num);
363 itemid.type = p_vcd->play_item.type;
365 if (vcdplayer_pbc_is_on(p_vcd)) {
367 vcdinfo_lid_get_pxd(obj, &(p_vcd->pxd), p_vcd->cur_lid);
369 switch (p_vcd->pxd.descriptor_type) {
370 case PSD_TYPE_SELECTION_LIST:
371 case PSD_TYPE_EXT_SELECTION_LIST:
372 if (p_vcd->pxd.psd == NULL) return false;
373 vcdplayer_update_entry( p_input,
374 vcdinf_psd_get_next_offset(p_vcd->pxd.psd),
375 &itemid.num, "next");
376 itemid.type = VCDINFO_ITEM_TYPE_LID;
379 case PSD_TYPE_PLAY_LIST:
380 if (p_vcd->pxd.pld == NULL) return false;
381 vcdplayer_update_entry( p_input,
382 vcdinf_pld_get_next_offset(p_vcd->pxd.pld),
383 &itemid.num, "next");
384 itemid.type = VCDINFO_ITEM_TYPE_LID;
387 case PSD_TYPE_END_LIST:
388 case PSD_TYPE_COMMAND_LIST:
389 LOG_WARN( "There is no PBC 'next' selection here" );
394 /* PBC is not on. "Next" selection is play_item.num+1 if possible. */
398 switch (p_vcd->play_item.type) {
399 case VCDINFO_ITEM_TYPE_ENTRY:
400 case VCDINFO_ITEM_TYPE_SEGMENT:
401 case VCDINFO_ITEM_TYPE_TRACK:
403 switch (p_vcd->play_item.type) {
404 case VCDINFO_ITEM_TYPE_ENTRY:
405 max_entry = p_vcd->num_entries;
407 case VCDINFO_ITEM_TYPE_SEGMENT:
408 max_entry = p_vcd->num_segments;
410 case VCDINFO_ITEM_TYPE_TRACK:
411 max_entry = p_vcd->num_tracks;
413 default: ; /* Handle exceptional cases below */
416 if (p_vcd->play_item.num+1 < max_entry) {
417 itemid.num = p_vcd->play_item.num+1;
419 LOG_WARN( "At the end - non-PBC 'next' not possible here" );
425 case VCDINFO_ITEM_TYPE_LID:
427 /* Should have handled above. */
428 LOG_WARN( "Internal inconsistency - should not have gotten here." );
436 /** ??? p_vcd->update_title(); ***/
437 return VLC_SUCCESS == VCDPlay( p_input, itemid );
442 Play item assocated with the "prev" selection.
444 Return false if there was some problem.
447 vcdplayer_play_prev( input_thread_t * p_input )
449 thread_vcd_data_t *p_vcd= (thread_vcd_data_t *)p_input->p_access_data;
451 vcdinfo_obj_t *obj = p_vcd->vcd;
452 vcdinfo_itemid_t itemid;
454 dbg_print( (INPUT_DBG_CALL|INPUT_DBG_PBC),
455 "current: %d" , p_vcd->play_item.num);
457 itemid.type = p_vcd->play_item.type;
459 if (vcdplayer_pbc_is_on(p_vcd)) {
461 vcdinfo_lid_get_pxd(obj, &(p_vcd->pxd), p_vcd->cur_lid);
463 switch (p_vcd->pxd.descriptor_type) {
464 case PSD_TYPE_SELECTION_LIST:
465 case PSD_TYPE_EXT_SELECTION_LIST:
466 if (p_vcd->pxd.psd == NULL) return false;
467 vcdplayer_update_entry( p_input,
468 vcdinf_psd_get_prev_offset(p_vcd->pxd.psd),
469 &itemid.num, "prev");
470 itemid.type = VCDINFO_ITEM_TYPE_LID;
473 case PSD_TYPE_PLAY_LIST:
474 if (p_vcd->pxd.pld == NULL) return false;
475 vcdplayer_update_entry( p_input,
476 vcdinf_pld_get_prev_offset(p_vcd->pxd.pld),
477 &itemid.num, "prev");
478 itemid.type = VCDINFO_ITEM_TYPE_LID;
481 case PSD_TYPE_END_LIST:
482 case PSD_TYPE_COMMAND_LIST:
483 LOG_WARN( "There is no PBC 'prev' selection here" );
488 /* PBC is not on. "Prev" selection is play_item.num-1 if possible. */
490 int min_entry = (VCDINFO_ITEM_TYPE_ENTRY == p_vcd->play_item.type)
493 if (p_vcd->play_item.num > min_entry) {
494 itemid.num = p_vcd->play_item.num-1;
496 LOG_WARN( "At the beginning - non-PBC 'prev' not possible here" );
502 /** ??? p_vcd->update_title(); ***/
503 return VLC_SUCCESS == VCDPlay( p_input, itemid );
508 Play item assocated with the "return" selection.
510 Return false if there was some problem.
513 vcdplayer_play_return( input_thread_t * p_input )
515 thread_vcd_data_t *p_vcd= (thread_vcd_data_t *)p_input->p_access_data;
517 vcdinfo_obj_t *obj = p_vcd->vcd;
518 vcdinfo_itemid_t itemid;
520 dbg_print( (INPUT_DBG_CALL|INPUT_DBG_PBC),
521 "current: %d" , p_vcd->play_item.num);
523 itemid.type = p_vcd->play_item.type;
525 if (vcdplayer_pbc_is_on(p_vcd)) {
527 vcdinfo_lid_get_pxd(obj, &(p_vcd->pxd), p_vcd->cur_lid);
529 switch (p_vcd->pxd.descriptor_type) {
530 case PSD_TYPE_SELECTION_LIST:
531 case PSD_TYPE_EXT_SELECTION_LIST:
532 if (p_vcd->pxd.psd == NULL) return false;
533 vcdplayer_update_entry( p_input,
534 vcdinf_psd_get_return_offset(p_vcd->pxd.psd),
535 &itemid.num, "return");
536 itemid.type = VCDINFO_ITEM_TYPE_LID;
539 case PSD_TYPE_PLAY_LIST:
540 if (p_vcd->pxd.pld == NULL) return false;
541 vcdplayer_update_entry( p_input,
542 vcdinf_pld_get_return_offset(p_vcd->pxd.pld),
543 &itemid.num, "return");
544 itemid.type = VCDINFO_ITEM_TYPE_LID;
547 case PSD_TYPE_END_LIST:
548 case PSD_TYPE_COMMAND_LIST:
549 LOG_WARN( "There is no PBC 'return' selection here" );
554 /* PBC is not on. "Return" selection is min_entry if possible. */
556 p_vcd->play_item.num = (VCDINFO_ITEM_TYPE_ENTRY == p_vcd->play_item.type)
561 /** ??? p_vcd->update_title(); ***/
562 return VLC_SUCCESS == VCDPlay( p_input, itemid );