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;
311 dbg_print( (INPUT_DBG_CALL|INPUT_DBG_PBC),
317 dbg_print( (INPUT_DBG_CALL|INPUT_DBG_PBC),
318 "current: %d" , p_vcd->play_item.num);
320 itemid.type = p_vcd->play_item.type;
322 if (vcdplayer_pbc_is_on(p_vcd)) {
324 #if defined(LIBVCD_VERSION)
325 lid_t lid=vcdinfo_get_multi_default_lid(p_vcd->vcd, p_vcd->cur_lid,
328 if (VCDINFO_INVALID_LID != lid) {
330 itemid.type = VCDINFO_ITEM_TYPE_LID;
331 dbg_print(INPUT_DBG_PBC, "DEFAULT to %d\n", itemid.num);
333 dbg_print(INPUT_DBG_PBC, "no DEFAULT for LID %d\n", p_vcd->cur_lid);
337 vcdinfo_lid_get_pxd(p_vcd->vcd, &(p_vcd->pxd), p_vcd->cur_lid);
339 switch (p_vcd->pxd.descriptor_type) {
340 case PSD_TYPE_SELECTION_LIST:
341 case PSD_TYPE_EXT_SELECTION_LIST:
342 if (p_vcd->pxd.psd == NULL) return false;
343 vcdplayer_update_entry( p_input,
344 vcdinfo_get_default_offset(p_vcd->vcd,
346 &itemid.num, "default");
349 case PSD_TYPE_PLAY_LIST:
350 case PSD_TYPE_END_LIST:
351 case PSD_TYPE_COMMAND_LIST:
352 LOG_WARN( "There is no PBC 'default' selection here" );
355 #endif /* LIBVCD_VERSION (< 0.7.21) */
360 /* PBC is not on. "default" selection beginning of current
363 p_vcd->play_item.num = p_vcd->play_item.num;
367 /** ??? p_vcd->update_title(); ***/
368 return VLC_SUCCESS == VCDPlay( p_input, itemid );
373 Play item assocated with the "next" selection.
375 Return false if there was some problem.
378 vcdplayer_play_next( input_thread_t * p_input )
380 thread_vcd_data_t *p_vcd= (thread_vcd_data_t *)p_input->p_access_data;
383 vcdinfo_itemid_t itemid;
385 if (!p_vcd) return false;
387 dbg_print( (INPUT_DBG_CALL|INPUT_DBG_PBC),
388 "current: %d" , p_vcd->play_item.num);
392 itemid.type = p_vcd->play_item.type;
394 if (vcdplayer_pbc_is_on(p_vcd)) {
396 vcdinfo_lid_get_pxd(obj, &(p_vcd->pxd), p_vcd->cur_lid);
398 switch (p_vcd->pxd.descriptor_type) {
399 case PSD_TYPE_SELECTION_LIST:
400 case PSD_TYPE_EXT_SELECTION_LIST:
401 if (p_vcd->pxd.psd == NULL) return false;
402 vcdplayer_update_entry( p_input,
403 vcdinf_psd_get_next_offset(p_vcd->pxd.psd),
404 &itemid.num, "next");
405 itemid.type = VCDINFO_ITEM_TYPE_LID;
408 case PSD_TYPE_PLAY_LIST:
409 if (p_vcd->pxd.pld == NULL) return false;
410 vcdplayer_update_entry( p_input,
411 vcdinf_pld_get_next_offset(p_vcd->pxd.pld),
412 &itemid.num, "next");
413 itemid.type = VCDINFO_ITEM_TYPE_LID;
416 case PSD_TYPE_END_LIST:
417 case PSD_TYPE_COMMAND_LIST:
418 LOG_WARN( "There is no PBC 'next' selection here" );
423 /* PBC is not on. "Next" selection is play_item.num+1 if possible. */
427 switch (p_vcd->play_item.type) {
428 case VCDINFO_ITEM_TYPE_ENTRY:
429 case VCDINFO_ITEM_TYPE_SEGMENT:
430 case VCDINFO_ITEM_TYPE_TRACK:
432 switch (p_vcd->play_item.type) {
433 case VCDINFO_ITEM_TYPE_ENTRY:
434 max_entry = p_vcd->num_entries;
436 case VCDINFO_ITEM_TYPE_SEGMENT:
437 max_entry = p_vcd->num_segments;
439 case VCDINFO_ITEM_TYPE_TRACK:
440 max_entry = p_vcd->num_tracks;
442 default: ; /* Handle exceptional cases below */
445 if (p_vcd->play_item.num+1 < max_entry) {
446 itemid.num = p_vcd->play_item.num+1;
448 LOG_WARN( "At the end - non-PBC 'next' not possible here" );
454 case VCDINFO_ITEM_TYPE_LID:
456 /* Should have handled above. */
457 LOG_WARN( "Internal inconsistency - should not have gotten here." );
465 /** ??? p_vcd->update_title(); ***/
466 return VLC_SUCCESS == VCDPlay( p_input, itemid );
471 Play item assocated with the "prev" selection.
473 Return false if there was some problem.
476 vcdplayer_play_prev( input_thread_t * p_input )
478 thread_vcd_data_t *p_vcd= (thread_vcd_data_t *)p_input->p_access_data;
480 vcdinfo_obj_t *obj = p_vcd->vcd;
481 vcdinfo_itemid_t itemid;
483 dbg_print( (INPUT_DBG_CALL|INPUT_DBG_PBC),
484 "current: %d" , p_vcd->play_item.num);
486 itemid.type = p_vcd->play_item.type;
488 if (vcdplayer_pbc_is_on(p_vcd)) {
490 vcdinfo_lid_get_pxd(obj, &(p_vcd->pxd), p_vcd->cur_lid);
492 switch (p_vcd->pxd.descriptor_type) {
493 case PSD_TYPE_SELECTION_LIST:
494 case PSD_TYPE_EXT_SELECTION_LIST:
495 if (p_vcd->pxd.psd == NULL) return false;
496 vcdplayer_update_entry( p_input,
497 vcdinf_psd_get_prev_offset(p_vcd->pxd.psd),
498 &itemid.num, "prev");
499 itemid.type = VCDINFO_ITEM_TYPE_LID;
502 case PSD_TYPE_PLAY_LIST:
503 if (p_vcd->pxd.pld == NULL) return false;
504 vcdplayer_update_entry( p_input,
505 vcdinf_pld_get_prev_offset(p_vcd->pxd.pld),
506 &itemid.num, "prev");
507 itemid.type = VCDINFO_ITEM_TYPE_LID;
510 case PSD_TYPE_END_LIST:
511 case PSD_TYPE_COMMAND_LIST:
512 LOG_WARN( "There is no PBC 'prev' selection here" );
517 /* PBC is not on. "Prev" selection is play_item.num-1 if possible. */
519 int min_entry = (VCDINFO_ITEM_TYPE_ENTRY == p_vcd->play_item.type)
522 if (p_vcd->play_item.num > min_entry) {
523 itemid.num = p_vcd->play_item.num-1;
525 LOG_WARN( "At the beginning - non-PBC 'prev' not possible here" );
531 /** ??? p_vcd->update_title(); ***/
532 return VLC_SUCCESS == VCDPlay( p_input, itemid );
537 Play item assocated with the "return" selection.
539 Return false if there was some problem.
542 vcdplayer_play_return( input_thread_t * p_input )
544 thread_vcd_data_t *p_vcd= (thread_vcd_data_t *)p_input->p_access_data;
546 vcdinfo_obj_t *obj = p_vcd->vcd;
547 vcdinfo_itemid_t itemid;
549 dbg_print( (INPUT_DBG_CALL|INPUT_DBG_PBC),
550 "current: %d" , p_vcd->play_item.num);
552 itemid.type = p_vcd->play_item.type;
554 if (vcdplayer_pbc_is_on(p_vcd)) {
556 vcdinfo_lid_get_pxd(obj, &(p_vcd->pxd), p_vcd->cur_lid);
558 switch (p_vcd->pxd.descriptor_type) {
559 case PSD_TYPE_SELECTION_LIST:
560 case PSD_TYPE_EXT_SELECTION_LIST:
561 if (p_vcd->pxd.psd == NULL) return false;
562 vcdplayer_update_entry( p_input,
563 vcdinf_psd_get_return_offset(p_vcd->pxd.psd),
564 &itemid.num, "return");
565 itemid.type = VCDINFO_ITEM_TYPE_LID;
568 case PSD_TYPE_PLAY_LIST:
569 if (p_vcd->pxd.pld == NULL) return false;
570 vcdplayer_update_entry( p_input,
571 vcdinf_pld_get_return_offset(p_vcd->pxd.pld),
572 &itemid.num, "return");
573 itemid.type = VCDINFO_ITEM_TYPE_LID;
576 case PSD_TYPE_END_LIST:
577 case PSD_TYPE_COMMAND_LIST:
578 LOG_WARN( "There is no PBC 'return' selection here" );
583 /* PBC is not on. "Return" selection is min_entry if possible. */
585 p_vcd->play_item.num = (VCDINFO_ITEM_TYPE_ENTRY == p_vcd->play_item.type)
590 /** ??? p_vcd->update_title(); ***/
591 return VLC_SUCCESS == VCDPlay( p_input, itemid );