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.8 2003/12/11 05:31:37 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] );
94 if ( p_vcd->cur_track >= p_vcd->num_tracks - 1 )
95 return READ_END; /* EOF */
97 p_vcd->play_item.num = p_vcd->cur_track++;
99 vlc_mutex_lock( &p_input->stream.stream_lock );
100 p_area = p_input->stream.pp_areas[p_vcd->cur_track];
103 VCDSetArea( p_input, p_area );
104 vlc_mutex_unlock( &p_input->stream.stream_lock );
108 case VCDINFO_ITEM_TYPE_SPAREID2:
109 dbg_print( (INPUT_DBG_STILL|INPUT_DBG_LSN),
112 p_input->stream.b_seekable = 0;
115 return READ_STILL_FRAME ;
118 case VCDINFO_ITEM_TYPE_NOTFOUND:
119 LOG_ERR ("NOTFOUND outside PBC -- not supposed to happen");
121 case VCDINFO_ITEM_TYPE_LID:
122 LOG_ERR ("LID outside PBC -- not supposed to happen");
124 case VCDINFO_ITEM_TYPE_SEGMENT:
125 /* Hack: Just go back and do still again */
127 p_input->stream.b_seekable = 0;
130 dbg_print( (INPUT_DBG_STILL|INPUT_DBG_LSN),
131 "End of Segment - looping" );
132 return READ_STILL_FRAME;
139 /* Handles PBC navigation when reaching the end of a play item. */
140 vcdplayer_read_status_t
141 vcdplayer_pbc_nav ( input_thread_t * p_input )
143 thread_vcd_data_t * p_vcd= (thread_vcd_data_t *)p_input->p_access_data;
145 /* We are in playback control. */
146 vcdinfo_itemid_t itemid;
148 /* The end of an entry is really the end of the associated
149 sequence (or track). */
151 if ( (VCDINFO_ITEM_TYPE_ENTRY == p_vcd->play_item.type) &&
152 (p_vcd->cur_lsn < p_vcd->end_lsn) ) {
153 /* Set up to just continue to the next entry */
154 p_vcd->play_item.num++;
155 dbg_print( (INPUT_DBG_LSN|INPUT_DBG_PBC),
156 "continuing into next entry: %u", p_vcd->play_item.num);
157 VCDPlay( p_input, p_vcd->play_item );
158 /* p_vcd->update_title(); */
162 switch (p_vcd->pxd.descriptor_type) {
163 case PSD_TYPE_END_LIST:
166 case PSD_TYPE_PLAY_LIST: {
167 int wait_time = vcdinf_get_wait_time(p_vcd->pxd.pld);
169 dbg_print(INPUT_DBG_PBC, "playlist wait_time: %d", wait_time);
171 if (vcdplayer_inc_play_item(p_input))
174 /* Handle any wait time given. */
175 if (p_vcd->in_still) {
176 vcdIntfStillTime( p_vcd->p_intf, wait_time );
177 return READ_STILL_FRAME;
180 vcdplayer_update_entry( p_input,
181 vcdinf_pld_get_next_offset(p_vcd->pxd.pld),
182 &itemid.num, "next" );
183 itemid.type = VCDINFO_ITEM_TYPE_LID;
184 VCDPlay( p_input, itemid );
187 case PSD_TYPE_SELECTION_LIST: /* Selection List (+Ext. for SVCD) */
188 case PSD_TYPE_EXT_SELECTION_LIST: /* Extended Selection List (VCD2.0) */
190 int wait_time = vcdinf_get_timeout_time(p_vcd->pxd.psd);
191 uint16_t timeout_offs = vcdinf_get_timeout_offset(p_vcd->pxd.psd);
192 uint16_t max_loop = vcdinf_get_loop_count(p_vcd->pxd.psd);
193 vcdinfo_offset_t *offset_timeout_LID =
194 vcdinfo_get_offset_t(p_vcd->vcd, timeout_offs);
196 dbg_print(INPUT_DBG_PBC, "wait_time: %d, looped: %d, max_loop %d",
197 wait_time, p_vcd->loop_count, max_loop);
199 /* Handle any wait time given */
200 if (p_vcd->in_still) {
201 vcdIntfStillTime( p_vcd->p_intf, wait_time );
202 return READ_STILL_FRAME;
205 /* Handle any looping given. */
206 if ( max_loop == 0 || p_vcd->loop_count < max_loop ) {
208 if (p_vcd->loop_count == 0x7f) p_vcd->loop_count = 0;
209 VCDSeek( p_input, 0 );
210 /* if (p_vcd->in_still) p_vcd->force_redisplay();*/
214 /* Looping finished and wait finished. Move to timeout
215 entry or next entry, or handle still. */
217 if (NULL != offset_timeout_LID) {
218 /* Handle timeout_LID */
219 itemid.num = offset_timeout_LID->lid;
220 itemid.type = VCDINFO_ITEM_TYPE_LID;
221 dbg_print(INPUT_DBG_PBC, "timeout to: %d", itemid.num);
222 VCDPlay( p_input, itemid );
225 int num_selections = vcdinf_get_num_selections(p_vcd->pxd.psd);
226 if (num_selections > 0) {
227 /* Pick a random selection. */
228 unsigned int bsn=vcdinf_get_bsn(p_vcd->pxd.psd);
229 int rand_selection=bsn +
230 (int) ((num_selections+0.0)*rand()/(RAND_MAX+1.0));
231 lid_t rand_lid=vcdinfo_selection_get_lid (p_vcd->vcd,
234 itemid.num = rand_lid;
235 itemid.type = VCDINFO_ITEM_TYPE_LID;
236 dbg_print(INPUT_DBG_PBC, "random selection %d, lid: %d",
237 rand_selection - bsn, rand_lid);
238 VCDPlay( p_input, itemid );
240 } else if (p_vcd->in_still) {
241 /* Hack: Just go back and do still again */
243 return READ_STILL_FRAME;
248 case VCDINFO_ITEM_TYPE_NOTFOUND:
249 LOG_ERR( "NOTFOUND in PBC -- not supposed to happen" );
251 case VCDINFO_ITEM_TYPE_SPAREID2:
252 LOG_ERR( "SPAREID2 in PBC -- not supposed to happen" );
254 case VCDINFO_ITEM_TYPE_LID:
255 LOG_ERR( "LID in PBC -- not supposed to happen" );
261 /* FIXME: Should handle autowait ... */
267 Get the next play-item in the list given in the LIDs. Note play-item
268 here refers to list of play-items for a single LID It shouldn't be
269 confused with a user's list of favorite things to play or the
270 "next" field of a LID which moves us to a different LID.
273 vcdplayer_inc_play_item( input_thread_t *p_input )
275 thread_vcd_data_t * p_vcd= (thread_vcd_data_t *)p_input->p_access_data;
279 dbg_print(INPUT_DBG_CALL, "called pli: %d", p_vcd->pdi);
281 if ( NULL == p_vcd || NULL == p_vcd->pxd.pld ) return false;
283 noi = vcdinf_pld_get_noi(p_vcd->pxd.pld);
285 if ( noi <= 0 ) return false;
287 /* Handle delays like autowait or wait here? */
291 if ( p_vcd->pdi < 0 || p_vcd->pdi >= noi ) return false;
294 uint16_t trans_itemid_num=vcdinf_pld_get_play_item(p_vcd->pxd.pld,
296 vcdinfo_itemid_t trans_itemid;
298 if (VCDINFO_INVALID_ITEMID == trans_itemid_num) return false;
300 vcdinfo_classify_itemid(trans_itemid_num, &trans_itemid);
301 dbg_print(INPUT_DBG_PBC, " play-item[%d]: %s",
302 p_vcd->pdi, vcdinfo_pin2str (trans_itemid_num));
303 return VLC_SUCCESS == VCDPlay( p_input, trans_itemid );
308 Play item assocated with the "default" selection.
310 Return false if there was some problem.
313 vcdplayer_play_default( input_thread_t * p_input )
315 thread_vcd_data_t *p_vcd= (thread_vcd_data_t *)p_input->p_access_data;
317 vcdinfo_itemid_t itemid;
319 dbg_print( (INPUT_DBG_CALL|INPUT_DBG_PBC),
320 "current: %d" , p_vcd->play_item.num);
322 itemid.type = p_vcd->play_item.type;
324 if (vcdplayer_pbc_is_on(p_vcd)) {
326 lid_t lid=vcdinfo_get_multi_default_lid(p_vcd->vcd, p_vcd->cur_lid,
329 if (VCDINFO_INVALID_LID != lid) {
331 itemid.type = VCDINFO_ITEM_TYPE_LID;
332 dbg_print(INPUT_DBG_PBC, "DEFAULT to %d\n", itemid.num);
334 dbg_print(INPUT_DBG_PBC, "no DEFAULT for LID %d\n", p_vcd->cur_lid);
340 /* PBC is not on. "default" selection beginning of current
343 p_vcd->play_item.num = p_vcd->play_item.num;
347 /** ??? p_vcd->update_title(); ***/
348 return VLC_SUCCESS == VCDPlay( p_input, itemid );
353 Play item assocated with the "next" selection.
355 Return false if there was some problem.
358 vcdplayer_play_next( input_thread_t * p_input )
360 thread_vcd_data_t *p_vcd= (thread_vcd_data_t *)p_input->p_access_data;
363 vcdinfo_itemid_t itemid;
365 if (!p_vcd) return false;
367 dbg_print( (INPUT_DBG_CALL|INPUT_DBG_PBC),
368 "current: %d" , p_vcd->play_item.num);
372 itemid.type = p_vcd->play_item.type;
374 if (vcdplayer_pbc_is_on(p_vcd)) {
376 vcdinfo_lid_get_pxd(obj, &(p_vcd->pxd), p_vcd->cur_lid);
378 switch (p_vcd->pxd.descriptor_type) {
379 case PSD_TYPE_SELECTION_LIST:
380 case PSD_TYPE_EXT_SELECTION_LIST:
381 if (p_vcd->pxd.psd == NULL) return false;
382 vcdplayer_update_entry( p_input,
383 vcdinf_psd_get_next_offset(p_vcd->pxd.psd),
384 &itemid.num, "next");
385 itemid.type = VCDINFO_ITEM_TYPE_LID;
388 case PSD_TYPE_PLAY_LIST:
389 if (p_vcd->pxd.pld == NULL) return false;
390 vcdplayer_update_entry( p_input,
391 vcdinf_pld_get_next_offset(p_vcd->pxd.pld),
392 &itemid.num, "next");
393 itemid.type = VCDINFO_ITEM_TYPE_LID;
396 case PSD_TYPE_END_LIST:
397 case PSD_TYPE_COMMAND_LIST:
398 LOG_WARN( "There is no PBC 'next' selection here" );
403 /* PBC is not on. "Next" selection is play_item.num+1 if possible. */
407 switch (p_vcd->play_item.type) {
408 case VCDINFO_ITEM_TYPE_ENTRY:
409 case VCDINFO_ITEM_TYPE_SEGMENT:
410 case VCDINFO_ITEM_TYPE_TRACK:
412 switch (p_vcd->play_item.type) {
413 case VCDINFO_ITEM_TYPE_ENTRY:
414 max_entry = p_vcd->num_entries;
416 case VCDINFO_ITEM_TYPE_SEGMENT:
417 max_entry = p_vcd->num_segments;
419 case VCDINFO_ITEM_TYPE_TRACK:
420 max_entry = p_vcd->num_tracks;
422 default: ; /* Handle exceptional cases below */
425 if (p_vcd->play_item.num+1 < max_entry) {
426 itemid.num = p_vcd->play_item.num+1;
428 LOG_WARN( "At the end - non-PBC 'next' not possible here" );
434 case VCDINFO_ITEM_TYPE_LID:
436 /* Should have handled above. */
437 LOG_WARN( "Internal inconsistency - should not have gotten here." );
445 /** ??? p_vcd->update_title(); ***/
446 return VLC_SUCCESS == VCDPlay( p_input, itemid );
451 Play item assocated with the "prev" selection.
453 Return false if there was some problem.
456 vcdplayer_play_prev( input_thread_t * p_input )
458 thread_vcd_data_t *p_vcd= (thread_vcd_data_t *)p_input->p_access_data;
460 vcdinfo_obj_t *obj = p_vcd->vcd;
461 vcdinfo_itemid_t itemid;
463 dbg_print( (INPUT_DBG_CALL|INPUT_DBG_PBC),
464 "current: %d" , p_vcd->play_item.num);
466 itemid.type = p_vcd->play_item.type;
468 if (vcdplayer_pbc_is_on(p_vcd)) {
470 vcdinfo_lid_get_pxd(obj, &(p_vcd->pxd), p_vcd->cur_lid);
472 switch (p_vcd->pxd.descriptor_type) {
473 case PSD_TYPE_SELECTION_LIST:
474 case PSD_TYPE_EXT_SELECTION_LIST:
475 if (p_vcd->pxd.psd == NULL) return false;
476 vcdplayer_update_entry( p_input,
477 vcdinf_psd_get_prev_offset(p_vcd->pxd.psd),
478 &itemid.num, "prev");
479 itemid.type = VCDINFO_ITEM_TYPE_LID;
482 case PSD_TYPE_PLAY_LIST:
483 if (p_vcd->pxd.pld == NULL) return false;
484 vcdplayer_update_entry( p_input,
485 vcdinf_pld_get_prev_offset(p_vcd->pxd.pld),
486 &itemid.num, "prev");
487 itemid.type = VCDINFO_ITEM_TYPE_LID;
490 case PSD_TYPE_END_LIST:
491 case PSD_TYPE_COMMAND_LIST:
492 LOG_WARN( "There is no PBC 'prev' selection here" );
497 /* PBC is not on. "Prev" selection is play_item.num-1 if possible. */
499 int min_entry = (VCDINFO_ITEM_TYPE_ENTRY == p_vcd->play_item.type)
502 if (p_vcd->play_item.num > min_entry) {
503 itemid.num = p_vcd->play_item.num-1;
505 LOG_WARN( "At the beginning - non-PBC 'prev' not possible here" );
511 /** ??? p_vcd->update_title(); ***/
512 return VLC_SUCCESS == VCDPlay( p_input, itemid );
517 Play item assocated with the "return" selection.
519 Return false if there was some problem.
522 vcdplayer_play_return( input_thread_t * p_input )
524 thread_vcd_data_t *p_vcd= (thread_vcd_data_t *)p_input->p_access_data;
526 vcdinfo_obj_t *obj = p_vcd->vcd;
527 vcdinfo_itemid_t itemid;
529 dbg_print( (INPUT_DBG_CALL|INPUT_DBG_PBC),
530 "current: %d" , p_vcd->play_item.num);
532 itemid.type = p_vcd->play_item.type;
534 if (vcdplayer_pbc_is_on(p_vcd)) {
536 vcdinfo_lid_get_pxd(obj, &(p_vcd->pxd), p_vcd->cur_lid);
538 switch (p_vcd->pxd.descriptor_type) {
539 case PSD_TYPE_SELECTION_LIST:
540 case PSD_TYPE_EXT_SELECTION_LIST:
541 if (p_vcd->pxd.psd == NULL) return false;
542 vcdplayer_update_entry( p_input,
543 vcdinf_psd_get_return_offset(p_vcd->pxd.psd),
544 &itemid.num, "return");
545 itemid.type = VCDINFO_ITEM_TYPE_LID;
548 case PSD_TYPE_PLAY_LIST:
549 if (p_vcd->pxd.pld == NULL) return false;
550 vcdplayer_update_entry( p_input,
551 vcdinf_pld_get_return_offset(p_vcd->pxd.pld),
552 &itemid.num, "return");
553 itemid.type = VCDINFO_ITEM_TYPE_LID;
556 case PSD_TYPE_END_LIST:
557 case PSD_TYPE_COMMAND_LIST:
558 LOG_WARN( "There is no PBC 'return' selection here" );
563 /* PBC is not on. "Return" selection is min_entry if possible. */
565 p_vcd->play_item.num = (VCDINFO_ITEM_TYPE_ENTRY == p_vcd->play_item.type)
570 /** ??? p_vcd->update_title(); ***/
571 return VLC_SUCCESS == VCDPlay( p_input, itemid );