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 lid_t lid=vcdinfo_get_multi_default_lid(p_vcd->vcd, p_vcd->cur_lid,
327 if (VCDINFO_INVALID_LID != lid) {
329 itemid.type = VCDINFO_ITEM_TYPE_LID;
330 dbg_print(INPUT_DBG_PBC, "DEFAULT to %d\n", itemid.num);
332 dbg_print(INPUT_DBG_PBC, "no DEFAULT for LID %d\n", p_vcd->cur_lid);
338 /* PBC is not on. "default" selection beginning of current
341 p_vcd->play_item.num = p_vcd->play_item.num;
345 /** ??? p_vcd->update_title(); ***/
346 return VLC_SUCCESS == VCDPlay( p_input, itemid );
351 Play item assocated with the "next" selection.
353 Return false if there was some problem.
356 vcdplayer_play_next( input_thread_t * p_input )
358 thread_vcd_data_t *p_vcd= (thread_vcd_data_t *)p_input->p_access_data;
361 vcdinfo_itemid_t itemid;
363 if (!p_vcd) return false;
365 dbg_print( (INPUT_DBG_CALL|INPUT_DBG_PBC),
366 "current: %d" , p_vcd->play_item.num);
370 itemid.type = p_vcd->play_item.type;
372 if (vcdplayer_pbc_is_on(p_vcd)) {
374 vcdinfo_lid_get_pxd(obj, &(p_vcd->pxd), p_vcd->cur_lid);
376 switch (p_vcd->pxd.descriptor_type) {
377 case PSD_TYPE_SELECTION_LIST:
378 case PSD_TYPE_EXT_SELECTION_LIST:
379 if (p_vcd->pxd.psd == NULL) return false;
380 vcdplayer_update_entry( p_input,
381 vcdinf_psd_get_next_offset(p_vcd->pxd.psd),
382 &itemid.num, "next");
383 itemid.type = VCDINFO_ITEM_TYPE_LID;
386 case PSD_TYPE_PLAY_LIST:
387 if (p_vcd->pxd.pld == NULL) return false;
388 vcdplayer_update_entry( p_input,
389 vcdinf_pld_get_next_offset(p_vcd->pxd.pld),
390 &itemid.num, "next");
391 itemid.type = VCDINFO_ITEM_TYPE_LID;
394 case PSD_TYPE_END_LIST:
395 case PSD_TYPE_COMMAND_LIST:
396 LOG_WARN( "There is no PBC 'next' selection here" );
401 /* PBC is not on. "Next" selection is play_item.num+1 if possible. */
405 switch (p_vcd->play_item.type) {
406 case VCDINFO_ITEM_TYPE_ENTRY:
407 case VCDINFO_ITEM_TYPE_SEGMENT:
408 case VCDINFO_ITEM_TYPE_TRACK:
410 switch (p_vcd->play_item.type) {
411 case VCDINFO_ITEM_TYPE_ENTRY:
412 max_entry = p_vcd->num_entries;
414 case VCDINFO_ITEM_TYPE_SEGMENT:
415 max_entry = p_vcd->num_segments;
417 case VCDINFO_ITEM_TYPE_TRACK:
418 max_entry = p_vcd->num_tracks;
420 default: ; /* Handle exceptional cases below */
423 if (p_vcd->play_item.num+1 < max_entry) {
424 itemid.num = p_vcd->play_item.num+1;
426 LOG_WARN( "At the end - non-PBC 'next' not possible here" );
432 case VCDINFO_ITEM_TYPE_LID:
434 /* Should have handled above. */
435 LOG_WARN( "Internal inconsistency - should not have gotten here." );
443 /** ??? p_vcd->update_title(); ***/
444 return VLC_SUCCESS == VCDPlay( p_input, itemid );
449 Play item assocated with the "prev" selection.
451 Return false if there was some problem.
454 vcdplayer_play_prev( input_thread_t * p_input )
456 thread_vcd_data_t *p_vcd= (thread_vcd_data_t *)p_input->p_access_data;
458 vcdinfo_obj_t *obj = p_vcd->vcd;
459 vcdinfo_itemid_t itemid;
461 dbg_print( (INPUT_DBG_CALL|INPUT_DBG_PBC),
462 "current: %d" , p_vcd->play_item.num);
464 itemid.type = p_vcd->play_item.type;
466 if (vcdplayer_pbc_is_on(p_vcd)) {
468 vcdinfo_lid_get_pxd(obj, &(p_vcd->pxd), p_vcd->cur_lid);
470 switch (p_vcd->pxd.descriptor_type) {
471 case PSD_TYPE_SELECTION_LIST:
472 case PSD_TYPE_EXT_SELECTION_LIST:
473 if (p_vcd->pxd.psd == NULL) return false;
474 vcdplayer_update_entry( p_input,
475 vcdinf_psd_get_prev_offset(p_vcd->pxd.psd),
476 &itemid.num, "prev");
477 itemid.type = VCDINFO_ITEM_TYPE_LID;
480 case PSD_TYPE_PLAY_LIST:
481 if (p_vcd->pxd.pld == NULL) return false;
482 vcdplayer_update_entry( p_input,
483 vcdinf_pld_get_prev_offset(p_vcd->pxd.pld),
484 &itemid.num, "prev");
485 itemid.type = VCDINFO_ITEM_TYPE_LID;
488 case PSD_TYPE_END_LIST:
489 case PSD_TYPE_COMMAND_LIST:
490 LOG_WARN( "There is no PBC 'prev' selection here" );
495 /* PBC is not on. "Prev" selection is play_item.num-1 if possible. */
497 int min_entry = (VCDINFO_ITEM_TYPE_ENTRY == p_vcd->play_item.type)
500 if (p_vcd->play_item.num > min_entry) {
501 itemid.num = p_vcd->play_item.num-1;
503 LOG_WARN( "At the beginning - non-PBC 'prev' not possible here" );
509 /** ??? p_vcd->update_title(); ***/
510 return VLC_SUCCESS == VCDPlay( p_input, itemid );
515 Play item assocated with the "return" selection.
517 Return false if there was some problem.
520 vcdplayer_play_return( input_thread_t * p_input )
522 thread_vcd_data_t *p_vcd= (thread_vcd_data_t *)p_input->p_access_data;
524 vcdinfo_obj_t *obj = p_vcd->vcd;
525 vcdinfo_itemid_t itemid;
527 dbg_print( (INPUT_DBG_CALL|INPUT_DBG_PBC),
528 "current: %d" , p_vcd->play_item.num);
530 itemid.type = p_vcd->play_item.type;
532 if (vcdplayer_pbc_is_on(p_vcd)) {
534 vcdinfo_lid_get_pxd(obj, &(p_vcd->pxd), p_vcd->cur_lid);
536 switch (p_vcd->pxd.descriptor_type) {
537 case PSD_TYPE_SELECTION_LIST:
538 case PSD_TYPE_EXT_SELECTION_LIST:
539 if (p_vcd->pxd.psd == NULL) return false;
540 vcdplayer_update_entry( p_input,
541 vcdinf_psd_get_return_offset(p_vcd->pxd.psd),
542 &itemid.num, "return");
543 itemid.type = VCDINFO_ITEM_TYPE_LID;
546 case PSD_TYPE_PLAY_LIST:
547 if (p_vcd->pxd.pld == NULL) return false;
548 vcdplayer_update_entry( p_input,
549 vcdinf_pld_get_return_offset(p_vcd->pxd.pld),
550 &itemid.num, "return");
551 itemid.type = VCDINFO_ITEM_TYPE_LID;
554 case PSD_TYPE_END_LIST:
555 case PSD_TYPE_COMMAND_LIST:
556 LOG_WARN( "There is no PBC 'return' selection here" );
561 /* PBC is not on. "Return" selection is min_entry if possible. */
563 p_vcd->play_item.num = (VCDINFO_ITEM_TYPE_ENTRY == p_vcd->play_item.type)
568 /** ??? p_vcd->update_title(); ***/
569 return VLC_SUCCESS == VCDPlay( p_input, itemid );