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.5 2003/11/24 03:30:36 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>
36 #include "vcdplayer.h"
40 #include <cdio/cdio.h>
41 #include <cdio/util.h>
42 #include <libvcd/info.h>
45 Return true if playback control (PBC) is on
48 vcdplayer_pbc_is_on(const thread_vcd_data_t *p_vcd)
50 return VCDINFO_INVALID_ENTRY != p_vcd->cur_lid;
54 vcdplayer_update_entry( input_thread_t * p_input, uint16_t ofs,
55 uint16_t *entry, const char *label)
57 thread_vcd_data_t * p_vcd= (thread_vcd_data_t *)p_input->p_access_data;
59 if ( ofs == VCDINFO_INVALID_OFFSET ) {
60 *entry = VCDINFO_INVALID_ENTRY;
62 vcdinfo_offset_t *off_t = vcdinfo_get_offset_t(p_vcd->vcd, ofs);
65 dbg_print(INPUT_DBG_PBC, "%s: LID %d\n", label, off_t->lid);
67 *entry = VCDINFO_INVALID_ENTRY;
71 /* Handles navigation when NOT in PBC reaching the end of a play item.
73 The navigations rules here may be sort of made up, but the intent
74 is to do something that's probably right or helpful.
76 return true if the caller should return.
78 vcdplayer_read_status_t
79 vcdplayer_non_pbc_nav ( input_thread_t * p_input )
81 thread_vcd_data_t * p_vcd= (thread_vcd_data_t *)p_input->p_access_data;
83 /* Not in playback control. Do we advance automatically or stop? */
84 switch (p_vcd->play_item.type) {
85 case VCDINFO_ITEM_TYPE_TRACK:
86 case VCDINFO_ITEM_TYPE_ENTRY: {
89 dbg_print( INPUT_DBG_LSN, "new track %d, lsn %d", p_vcd->cur_track,
90 p_vcd->p_sectors[p_vcd->cur_track+1] );
92 if ( p_vcd->cur_track >= p_vcd->num_tracks - 1 )
93 return READ_END; /* EOF */
95 p_vcd->play_item.num = p_vcd->cur_track++;
97 vlc_mutex_lock( &p_input->stream.stream_lock );
98 p_area = p_input->stream.pp_areas[p_vcd->cur_track];
101 VCDSetArea( p_input, p_area );
102 vlc_mutex_unlock( &p_input->stream.stream_lock );
106 case VCDINFO_ITEM_TYPE_SPAREID2:
107 dbg_print( (INPUT_DBG_STILL|INPUT_DBG_LSN),
110 p_input->stream.b_seekable = 0;
113 return READ_STILL_FRAME ;
116 case VCDINFO_ITEM_TYPE_NOTFOUND:
117 LOG_ERR ("NOTFOUND outside PBC -- not supposed to happen");
119 case VCDINFO_ITEM_TYPE_LID:
120 LOG_ERR ("LID outside PBC -- not supposed to happen");
122 case VCDINFO_ITEM_TYPE_SEGMENT:
123 /* Hack: Just go back and do still again */
125 p_input->stream.b_seekable = 0;
128 dbg_print( (INPUT_DBG_STILL|INPUT_DBG_LSN),
129 "End of Segment - looping" );
130 return READ_STILL_FRAME;
137 /* FIXME: Will do whatever the right thing is later. */
138 #define SLEEP_1_SEC_AND_HANDLE_EVENTS sleep(1)
140 /* Handles PBC navigation when reaching the end of a play item. */
141 vcdplayer_read_status_t
142 vcdplayer_pbc_nav ( input_thread_t * p_input )
144 thread_vcd_data_t * p_vcd= (thread_vcd_data_t *)p_input->p_access_data;
146 /* We are in playback control. */
147 vcdinfo_itemid_t itemid;
149 if (0 != p_vcd->in_still && p_vcd->in_still != -5) {
150 SLEEP_1_SEC_AND_HANDLE_EVENTS;
151 if (p_vcd->in_still > 0) p_vcd->in_still--;
152 return READ_STILL_FRAME;
155 /* The end of an entry is really the end of the associated
156 sequence (or track). */
158 if ( (VCDINFO_ITEM_TYPE_ENTRY == p_vcd->play_item.type) &&
159 (p_vcd->cur_lsn < p_vcd->end_lsn) ) {
160 /* Set up to just continue to the next entry */
161 p_vcd->play_item.num++;
162 dbg_print( (INPUT_DBG_LSN|INPUT_DBG_PBC),
163 "continuing into next entry: %u", p_vcd->play_item.num);
164 VCDPlay( p_input, p_vcd->play_item );
165 /* p_vcd->update_title(); */
169 switch (p_vcd->pxd.descriptor_type) {
170 case PSD_TYPE_END_LIST:
173 case PSD_TYPE_PLAY_LIST: {
174 int wait_time = vcdinf_get_wait_time(p_vcd->pxd.pld);
176 dbg_print(INPUT_DBG_PBC, "playlist wait_time: %d", wait_time);
178 if (vcdplayer_inc_play_item(p_input))
181 /* Handle any wait time given. */
182 if (-5 == p_vcd->in_still) {
183 if (wait_time != 0) {
185 p_vcd->in_still = wait_time - 1;
186 SLEEP_1_SEC_AND_HANDLE_EVENTS ;
187 return READ_STILL_FRAME;
190 vcdplayer_update_entry( p_input,
191 vcdinf_pld_get_next_offset(p_vcd->pxd.pld),
192 &itemid.num, "next" );
193 itemid.type = VCDINFO_ITEM_TYPE_LID;
194 VCDPlay( p_input, itemid );
197 case PSD_TYPE_SELECTION_LIST: /* Selection List (+Ext. for SVCD) */
198 case PSD_TYPE_EXT_SELECTION_LIST: /* Extended Selection List (VCD2.0) */
200 int wait_time = vcdinf_get_timeout_time(p_vcd->pxd.psd);
201 uint16_t timeout_offs = vcdinf_get_timeout_offset(p_vcd->pxd.psd);
202 uint16_t max_loop = vcdinf_get_loop_count(p_vcd->pxd.psd);
203 vcdinfo_offset_t *offset_timeout_LID =
204 vcdinfo_get_offset_t(p_vcd->vcd, timeout_offs);
206 dbg_print(INPUT_DBG_PBC, "wait_time: %d, looped: %d, max_loop %d",
207 wait_time, p_vcd->loop_count, max_loop);
209 /* Handle any wait time given */
210 if (-5 == p_vcd->in_still) {
211 p_vcd->in_still = wait_time - 1;
212 SLEEP_1_SEC_AND_HANDLE_EVENTS ;
213 return READ_STILL_FRAME;
216 /* Handle any looping given. */
217 if ( max_loop == 0 || p_vcd->loop_count < max_loop ) {
219 if (p_vcd->loop_count == 0x7f) p_vcd->loop_count = 0;
220 VCDSeek( p_input, 0 );
221 /* if (p_vcd->in_still) p_vcd->force_redisplay();*/
225 /* Looping finished and wait finished. Move to timeout
226 entry or next entry, or handle still. */
228 if (NULL != offset_timeout_LID) {
229 /* Handle timeout_LID */
230 itemid.num = offset_timeout_LID->lid;
231 itemid.type = VCDINFO_ITEM_TYPE_LID;
232 dbg_print(INPUT_DBG_PBC, "timeout to: %d", itemid.num);
233 VCDPlay( p_input, itemid );
236 int num_selections = vcdinf_get_num_selections(p_vcd->pxd.psd);
237 if (num_selections > 0) {
238 /* Pick a random selection. */
239 unsigned int bsn=vcdinf_get_bsn(p_vcd->pxd.psd);
240 int rand_selection=bsn +
241 (int) ((num_selections+0.0)*rand()/(RAND_MAX+1.0));
242 lid_t rand_lid=vcdinfo_selection_get_lid (p_vcd->vcd,
245 itemid.num = rand_lid;
246 itemid.type = VCDINFO_ITEM_TYPE_LID;
247 dbg_print(INPUT_DBG_PBC, "random selection %d, lid: %d",
248 rand_selection - bsn, rand_lid);
249 VCDPlay( p_input, itemid );
251 } else if (p_vcd->in_still) {
252 /* Hack: Just go back and do still again */
253 SLEEP_1_SEC_AND_HANDLE_EVENTS ;
254 return READ_STILL_FRAME;
259 case VCDINFO_ITEM_TYPE_NOTFOUND:
260 LOG_ERR( "NOTFOUND in PBC -- not supposed to happen" );
262 case VCDINFO_ITEM_TYPE_SPAREID2:
263 LOG_ERR( "SPAREID2 in PBC -- not supposed to happen" );
265 case VCDINFO_ITEM_TYPE_LID:
266 LOG_ERR( "LID in PBC -- not supposed to happen" );
272 /* FIXME: Should handle autowait ... */
278 Get the next play-item in the list given in the LIDs. Note play-item
279 here refers to list of play-items for a single LID It shouldn't be
280 confused with a user's list of favorite things to play or the
281 "next" field of a LID which moves us to a different LID.
284 vcdplayer_inc_play_item( input_thread_t *p_input )
286 thread_vcd_data_t * p_vcd= (thread_vcd_data_t *)p_input->p_access_data;
290 dbg_print(INPUT_DBG_CALL, "called pli: %d", p_vcd->pdi);
292 if ( NULL == p_vcd || NULL == p_vcd->pxd.pld ) return false;
294 noi = vcdinf_pld_get_noi(p_vcd->pxd.pld);
296 if ( noi <= 0 ) return false;
298 /* Handle delays like autowait or wait here? */
302 if ( p_vcd->pdi < 0 || p_vcd->pdi >= noi ) return false;
305 uint16_t trans_itemid_num=vcdinf_pld_get_play_item(p_vcd->pxd.pld,
307 vcdinfo_itemid_t trans_itemid;
309 if (VCDINFO_INVALID_ITEMID == trans_itemid_num) return false;
311 vcdinfo_classify_itemid(trans_itemid_num, &trans_itemid);
312 dbg_print(INPUT_DBG_PBC, " play-item[%d]: %s",
313 p_vcd->pdi, vcdinfo_pin2str (trans_itemid_num));
314 return VLC_SUCCESS == VCDPlay( p_input, trans_itemid );
319 Play item assocated with the "default" selection.
321 Return false if there was some problem.
324 vcdplayer_play_default( input_thread_t * p_input )
326 thread_vcd_data_t *p_vcd= (thread_vcd_data_t *)p_input->p_access_data;
328 vcdinfo_itemid_t itemid;
330 dbg_print( (INPUT_DBG_CALL|INPUT_DBG_PBC),
331 "current: %d" , p_vcd->play_item.num);
333 itemid.type = p_vcd->play_item.type;
335 if (vcdplayer_pbc_is_on(p_vcd)) {
337 lid_t lid=vcdinfo_get_multi_default_lid(p_vcd->vcd, p_vcd->cur_lid,
340 if (VCDINFO_INVALID_LID != lid) {
342 itemid.type = VCDINFO_ITEM_TYPE_LID;
343 dbg_print(INPUT_DBG_PBC, "DEFAULT to %d\n", itemid.num);
345 dbg_print(INPUT_DBG_PBC, "no DEFAULT for LID %d\n", p_vcd->cur_lid);
351 /* PBC is not on. "default" selection beginning of current
354 p_vcd->play_item.num = p_vcd->play_item.num;
358 /** ??? p_vcd->update_title(); ***/
359 return VLC_SUCCESS == VCDPlay( p_input, itemid );
364 Play item assocated with the "next" selection.
366 Return false if there was some problem.
369 vcdplayer_play_next( input_thread_t * p_input )
371 thread_vcd_data_t *p_vcd= (thread_vcd_data_t *)p_input->p_access_data;
373 vcdinfo_obj_t *obj = p_vcd->vcd;
374 vcdinfo_itemid_t itemid;
376 dbg_print( (INPUT_DBG_CALL|INPUT_DBG_PBC),
377 "current: %d" , p_vcd->play_item.num);
379 itemid.type = p_vcd->play_item.type;
381 if (vcdplayer_pbc_is_on(p_vcd)) {
383 vcdinfo_lid_get_pxd(obj, &(p_vcd->pxd), p_vcd->cur_lid);
385 switch (p_vcd->pxd.descriptor_type) {
386 case PSD_TYPE_SELECTION_LIST:
387 case PSD_TYPE_EXT_SELECTION_LIST:
388 if (p_vcd->pxd.psd == NULL) return false;
389 vcdplayer_update_entry( p_input,
390 vcdinf_psd_get_next_offset(p_vcd->pxd.psd),
391 &itemid.num, "next");
392 itemid.type = VCDINFO_ITEM_TYPE_LID;
395 case PSD_TYPE_PLAY_LIST:
396 if (p_vcd->pxd.pld == NULL) return false;
397 vcdplayer_update_entry( p_input,
398 vcdinf_pld_get_next_offset(p_vcd->pxd.pld),
399 &itemid.num, "next");
400 itemid.type = VCDINFO_ITEM_TYPE_LID;
403 case PSD_TYPE_END_LIST:
404 case PSD_TYPE_COMMAND_LIST:
405 LOG_WARN( "There is no PBC 'next' selection here" );
410 /* PBC is not on. "Next" selection is play_item.num+1 if possible. */
414 switch (p_vcd->play_item.type) {
415 case VCDINFO_ITEM_TYPE_ENTRY:
416 case VCDINFO_ITEM_TYPE_SEGMENT:
417 case VCDINFO_ITEM_TYPE_TRACK:
419 switch (p_vcd->play_item.type) {
420 case VCDINFO_ITEM_TYPE_ENTRY:
421 max_entry = p_vcd->num_entries;
423 case VCDINFO_ITEM_TYPE_SEGMENT:
424 max_entry = p_vcd->num_segments;
426 case VCDINFO_ITEM_TYPE_TRACK:
427 max_entry = p_vcd->num_tracks;
429 default: ; /* Handle exceptional cases below */
432 if (p_vcd->play_item.num+1 < max_entry) {
433 itemid.num = p_vcd->play_item.num+1;
435 LOG_WARN( "At the end - non-PBC 'next' not possible here" );
441 case VCDINFO_ITEM_TYPE_LID:
443 /* Should have handled above. */
444 LOG_WARN( "Internal inconsistency - should not have gotten here." );
452 /** ??? p_vcd->update_title(); ***/
453 return VLC_SUCCESS == VCDPlay( p_input, itemid );
458 Play item assocated with the "prev" selection.
460 Return false if there was some problem.
463 vcdplayer_play_prev( input_thread_t * p_input )
465 thread_vcd_data_t *p_vcd= (thread_vcd_data_t *)p_input->p_access_data;
467 vcdinfo_obj_t *obj = p_vcd->vcd;
468 vcdinfo_itemid_t itemid;
470 dbg_print( (INPUT_DBG_CALL|INPUT_DBG_PBC),
471 "current: %d" , p_vcd->play_item.num);
473 itemid.type = p_vcd->play_item.type;
475 if (vcdplayer_pbc_is_on(p_vcd)) {
477 vcdinfo_lid_get_pxd(obj, &(p_vcd->pxd), p_vcd->cur_lid);
479 switch (p_vcd->pxd.descriptor_type) {
480 case PSD_TYPE_SELECTION_LIST:
481 case PSD_TYPE_EXT_SELECTION_LIST:
482 if (p_vcd->pxd.psd == NULL) return false;
483 vcdplayer_update_entry( p_input,
484 vcdinf_psd_get_prev_offset(p_vcd->pxd.psd),
485 &itemid.num, "prev");
486 itemid.type = VCDINFO_ITEM_TYPE_LID;
489 case PSD_TYPE_PLAY_LIST:
490 if (p_vcd->pxd.pld == NULL) return false;
491 vcdplayer_update_entry( p_input,
492 vcdinf_pld_get_prev_offset(p_vcd->pxd.pld),
493 &itemid.num, "prev");
494 itemid.type = VCDINFO_ITEM_TYPE_LID;
497 case PSD_TYPE_END_LIST:
498 case PSD_TYPE_COMMAND_LIST:
499 LOG_WARN( "There is no PBC 'prev' selection here" );
504 /* PBC is not on. "Prev" selection is play_item.num-1 if possible. */
506 int min_entry = (VCDINFO_ITEM_TYPE_ENTRY == p_vcd->play_item.type)
509 if (p_vcd->play_item.num > min_entry) {
510 itemid.num = p_vcd->play_item.num-1;
512 LOG_WARN( "At the beginning - non-PBC 'prev' not possible here" );
518 /** ??? p_vcd->update_title(); ***/
519 return VLC_SUCCESS == VCDPlay( p_input, itemid );
524 Play item assocated with the "return" selection.
526 Return false if there was some problem.
529 vcdplayer_play_return( input_thread_t * p_input )
531 thread_vcd_data_t *p_vcd= (thread_vcd_data_t *)p_input->p_access_data;
533 vcdinfo_obj_t *obj = p_vcd->vcd;
534 vcdinfo_itemid_t itemid;
536 dbg_print( (INPUT_DBG_CALL|INPUT_DBG_PBC),
537 "current: %d" , p_vcd->play_item.num);
539 itemid.type = p_vcd->play_item.type;
541 if (vcdplayer_pbc_is_on(p_vcd)) {
543 vcdinfo_lid_get_pxd(obj, &(p_vcd->pxd), p_vcd->cur_lid);
545 switch (p_vcd->pxd.descriptor_type) {
546 case PSD_TYPE_SELECTION_LIST:
547 case PSD_TYPE_EXT_SELECTION_LIST:
548 if (p_vcd->pxd.psd == NULL) return false;
549 vcdplayer_update_entry( p_input,
550 vcdinf_psd_get_return_offset(p_vcd->pxd.psd),
551 &itemid.num, "return");
552 itemid.type = VCDINFO_ITEM_TYPE_LID;
555 case PSD_TYPE_PLAY_LIST:
556 if (p_vcd->pxd.pld == NULL) return false;
557 vcdplayer_update_entry( p_input,
558 vcdinf_pld_get_return_offset(p_vcd->pxd.pld),
559 &itemid.num, "return");
560 itemid.type = VCDINFO_ITEM_TYPE_LID;
563 case PSD_TYPE_END_LIST:
564 case PSD_TYPE_COMMAND_LIST:
565 LOG_WARN( "There is no PBC 'return' selection here" );
570 /* PBC is not on. "Return" selection is min_entry if possible. */
572 p_vcd->play_item.num = (VCDINFO_ITEM_TYPE_ENTRY == p_vcd->play_item.type)
577 /** ??? p_vcd->update_title(); ***/
578 return VLC_SUCCESS == VCDPlay( p_input, itemid );