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.2 2003/11/09 00:52:32 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_selection2lid ( input_thread_t *p_input, int entry_num )
56 /* FIXME: Some of this probably gets moved to vcdinfo. */
57 /* Convert selection number to lid and then entry number...*/
58 thread_vcd_data_t * p_vcd= (thread_vcd_data_t *)p_input->p_access_data;
60 unsigned int bsn=vcdinf_get_bsn(p_vcd->pxd.psd);
61 vcdinfo_obj_t *obj = p_vcd->vcd;
63 dbg_print( (INPUT_DBG_CALL|INPUT_DBG_PBC),
64 "Called lid %u, entry_num %d bsn %d", p_vcd->cur_lid,
67 if ( (entry_num - bsn + 1) > 0) {
68 offset = vcdinfo_lid_get_offset(obj, p_vcd->cur_lid, entry_num-bsn+1);
70 LOG_ERR( "Selection number %u too small. bsn %u", entry_num, bsn );
71 return VCDINFO_INVALID_LID;
74 if (offset != VCDINFO_INVALID_OFFSET) {
75 vcdinfo_offset_t *ofs;
79 case PSD_OFS_DISABLED:
80 LOG_ERR( "Selection %u disabled", entry_num );
81 return VCDINFO_INVALID_LID;
82 case PSD_OFS_MULTI_DEF:
83 LOG_ERR( "Selection %u multi_def", entry_num );
84 return VCDINFO_INVALID_LID;
85 case PSD_OFS_MULTI_DEF_NO_NUM:
86 LOG_ERR( "Selection %u multi_def_no_num", entry_num );
87 return VCDINFO_INVALID_LID;
91 ofs = vcdinfo_get_offset_t(obj, offset);
94 LOG_ERR( "error in vcdinfo_get_offset" );
97 dbg_print(INPUT_DBG_PBC,
98 "entry %u turned into selection lid %u",
103 LOG_ERR( "invalid or unset entry %u", entry_num );
104 return VCDINFO_INVALID_LID;
109 vcdplayer_update_entry( input_thread_t * p_input, uint16_t ofs,
110 uint16_t *entry, const char *label)
112 thread_vcd_data_t * p_vcd= (thread_vcd_data_t *)p_input->p_access_data;
114 if ( ofs == VCDINFO_INVALID_OFFSET ) {
115 *entry = VCDINFO_INVALID_ENTRY;
117 vcdinfo_offset_t *off_t = vcdinfo_get_offset_t(p_vcd->vcd, ofs);
120 dbg_print(INPUT_DBG_PBC, "%s: %d\n", label, off_t->lid);
122 *entry = VCDINFO_INVALID_ENTRY;
126 /* Handles navigation when NOT in PBC reaching the end of a play item.
128 The navigations rules here may be sort of made up, but the intent
129 is to do something that's probably right or helpful.
131 return true if the caller should return.
133 vcdplayer_read_status_t
134 vcdplayer_non_pbc_nav ( input_thread_t * p_input )
136 thread_vcd_data_t * p_vcd= (thread_vcd_data_t *)p_input->p_access_data;
138 /* Not in playback control. Do we advance automatically or stop? */
139 switch (p_vcd->play_item.type) {
140 case VCDINFO_ITEM_TYPE_TRACK:
141 case VCDINFO_ITEM_TYPE_ENTRY: {
142 input_area_t *p_area;
144 dbg_print( INPUT_DBG_LSN, "new track %d, lsn %d", p_vcd->cur_track,
145 p_vcd->p_sectors[p_vcd->cur_track+1] );
147 if ( p_vcd->cur_track >= p_vcd->num_tracks - 1 )
148 return READ_END; /* EOF */
150 p_vcd->play_item.num = p_vcd->cur_track++;
152 vlc_mutex_lock( &p_input->stream.stream_lock );
153 p_area = p_input->stream.pp_areas[p_vcd->cur_track];
156 VCDSetArea( p_input, p_area );
157 vlc_mutex_unlock( &p_input->stream.stream_lock );
161 case VCDINFO_ITEM_TYPE_SPAREID2:
162 dbg_print( (INPUT_DBG_STILL|INPUT_DBG_LSN),
165 p_input->stream.b_seekable = 0;
168 return READ_STILL_FRAME ;
171 case VCDINFO_ITEM_TYPE_NOTFOUND:
172 LOG_ERR ("NOTFOUND outside PBC -- not supposed to happen");
174 case VCDINFO_ITEM_TYPE_LID:
175 LOG_ERR ("LID outside PBC -- not supposed to happen");
177 case VCDINFO_ITEM_TYPE_SEGMENT:
178 /* Hack: Just go back and do still again */
180 p_input->stream.b_seekable = 0;
183 dbg_print( (INPUT_DBG_STILL|INPUT_DBG_LSN),
184 "End of Segment - looping" );
185 return READ_STILL_FRAME;
192 /* FIXME: Will do whatever the right thing is later. */
193 #define SLEEP_1_SEC_AND_HANDLE_EVENTS sleep(1)
195 /* Handles PBC navigation when reaching the end of a play item. */
196 vcdplayer_read_status_t
197 vcdplayer_pbc_nav ( input_thread_t * p_input )
199 thread_vcd_data_t * p_vcd= (thread_vcd_data_t *)p_input->p_access_data;
201 /* We are in playback control. */
202 vcdinfo_itemid_t itemid;
204 if (0 != p_vcd->in_still && p_vcd->in_still != -5) {
205 SLEEP_1_SEC_AND_HANDLE_EVENTS;
206 if (p_vcd->in_still > 0) p_vcd->in_still--;
207 return READ_STILL_FRAME;
210 /* The end of an entry is really the end of the associated
211 sequence (or track). */
213 if ( (VCDINFO_ITEM_TYPE_ENTRY == p_vcd->play_item.type) &&
214 (p_vcd->cur_lsn < p_vcd->end_lsn) ) {
215 /* Set up to just continue to the next entry */
216 p_vcd->play_item.num++;
217 dbg_print( (INPUT_DBG_LSN|INPUT_DBG_PBC),
218 "continuing into next entry: %u", p_vcd->play_item.num);
219 VCDPlay( p_input, p_vcd->play_item );
220 /* p_vcd->update_title(); */
224 switch (p_vcd->pxd.descriptor_type) {
225 case PSD_TYPE_END_LIST:
228 case PSD_TYPE_PLAY_LIST: {
229 int wait_time = vcdinf_get_wait_time(p_vcd->pxd.pld);
231 dbg_print(INPUT_DBG_PBC, "playlist wait_time: %d", wait_time);
233 if (vcdplayer_inc_play_item(p_input))
236 /* Handle any wait time given. */
237 if (-5 == p_vcd->in_still) {
238 if (wait_time != 0) {
240 p_vcd->in_still = wait_time - 1;
241 SLEEP_1_SEC_AND_HANDLE_EVENTS ;
242 return READ_STILL_FRAME;
245 vcdplayer_update_entry( p_input,
246 vcdinf_pld_get_next_offset(p_vcd->pxd.pld),
247 &itemid.num, "next" );
248 itemid.type = VCDINFO_ITEM_TYPE_LID;
249 VCDPlay( p_input, itemid );
252 case PSD_TYPE_SELECTION_LIST: /* Selection List (+Ext. for SVCD) */
253 case PSD_TYPE_EXT_SELECTION_LIST: /* Extended Selection List (VCD2.0) */
255 int wait_time = vcdinf_get_timeout_time(p_vcd->pxd.psd);
256 uint16_t timeout_offs = vcdinf_get_timeout_offset(p_vcd->pxd.psd);
257 uint16_t max_loop = vcdinf_get_loop_count(p_vcd->pxd.psd);
258 vcdinfo_offset_t *offset_timeout_LID =
259 vcdinfo_get_offset_t(p_vcd->vcd, timeout_offs);
261 dbg_print(INPUT_DBG_PBC, "wait_time: %d, looped: %d, max_loop %d",
262 wait_time, p_vcd->loop_count, max_loop);
264 /* Handle any wait time given */
265 if (-5 == p_vcd->in_still) {
266 p_vcd->in_still = wait_time - 1;
267 SLEEP_1_SEC_AND_HANDLE_EVENTS ;
268 return READ_STILL_FRAME;
271 /* Handle any looping given. */
272 if ( max_loop == 0 || p_vcd->loop_count < max_loop ) {
274 if (p_vcd->loop_count == 0x7f) p_vcd->loop_count = 0;
275 VCDSeek( p_input, 0 );
276 /* if (p_vcd->in_still) p_vcd->force_redisplay();*/
280 /* Looping finished and wait finished. Move to timeout
281 entry or next entry, or handle still. */
283 if (NULL != offset_timeout_LID) {
284 /* Handle timeout_LID */
285 itemid.num = offset_timeout_LID->lid;
286 itemid.type = VCDINFO_ITEM_TYPE_LID;
287 dbg_print(INPUT_DBG_PBC, "timeout to: %d", itemid.num);
288 VCDPlay( p_input, itemid );
291 int num_selections = vcdinf_get_num_selections(p_vcd->pxd.psd);
292 if (num_selections > 0) {
293 /* Pick a random selection. */
294 unsigned int bsn=vcdinf_get_bsn(p_vcd->pxd.psd);
295 int rand_selection=bsn +
296 (int) ((num_selections+0.0)*rand()/(RAND_MAX+1.0));
297 lid_t rand_lid=vcdplayer_selection2lid (p_input, rand_selection);
298 itemid.num = rand_lid;
299 itemid.type = VCDINFO_ITEM_TYPE_LID;
300 dbg_print(INPUT_DBG_PBC, "random selection %d, lid: %d",
301 rand_selection - bsn, rand_lid);
302 VCDPlay( p_input, itemid );
304 } else if (p_vcd->in_still) {
305 /* Hack: Just go back and do still again */
306 SLEEP_1_SEC_AND_HANDLE_EVENTS ;
307 return READ_STILL_FRAME;
312 case VCDINFO_ITEM_TYPE_NOTFOUND:
313 LOG_ERR( "NOTFOUND in PBC -- not supposed to happen" );
315 case VCDINFO_ITEM_TYPE_SPAREID2:
316 LOG_ERR( "SPAREID2 in PBC -- not supposed to happen" );
318 case VCDINFO_ITEM_TYPE_LID:
319 LOG_ERR( "LID in PBC -- not supposed to happen" );
325 /* FIXME: Should handle autowait ... */
331 Get the next play-item in the list given in the LIDs. Note play-item
332 here refers to list of play-items for a single LID It shouldn't be
333 confused with a user's list of favorite things to play or the
334 "next" field of a LID which moves us to a different LID.
337 vcdplayer_inc_play_item( input_thread_t *p_input )
339 thread_vcd_data_t * p_vcd= (thread_vcd_data_t *)p_input->p_access_data;
343 dbg_print(INPUT_DBG_CALL, "called pli: %d", p_vcd->pdi);
345 if ( NULL == p_vcd || NULL == p_vcd->pxd.pld ) return false;
347 noi = vcdinf_pld_get_noi(p_vcd->pxd.pld);
349 if ( noi <= 0 ) return false;
351 /* Handle delays like autowait or wait here? */
355 if ( p_vcd->pdi < 0 || p_vcd->pdi >= noi ) return false;
358 uint16_t trans_itemid_num=vcdinf_pld_get_play_item(p_vcd->pxd.pld,
360 vcdinfo_itemid_t trans_itemid;
362 if (VCDINFO_INVALID_ITEMID == trans_itemid_num) return false;
364 vcdinfo_classify_itemid(trans_itemid_num, &trans_itemid);
365 dbg_print(INPUT_DBG_PBC, " play-item[%d]: %s",
366 p_vcd->pdi, vcdinfo_pin2str (trans_itemid_num));
367 return VLC_SUCCESS == VCDPlay( p_input, trans_itemid );
372 Play item assocated with the "default" selection.
374 Return false if there was some problem.
377 vcdplayer_play_default( input_thread_t * p_input )
379 thread_vcd_data_t *p_vcd= (thread_vcd_data_t *)p_input->p_access_data;
381 vcdinfo_obj_t *obj = p_vcd->vcd;
382 vcdinfo_itemid_t itemid;
384 dbg_print( (INPUT_DBG_CALL|INPUT_DBG_PBC),
385 "current: %d" , p_vcd->play_item.num);
387 itemid.type = p_vcd->play_item.type;
389 if (vcdplayer_pbc_is_on(p_vcd)) {
391 vcdinfo_lid_get_pxd(obj, &(p_vcd->pxd), p_vcd->cur_lid);
393 switch (p_vcd->pxd.descriptor_type) {
394 case PSD_TYPE_SELECTION_LIST:
395 case PSD_TYPE_EXT_SELECTION_LIST:
396 if (p_vcd->pxd.psd == NULL) return false;
397 vcdplayer_update_entry( p_input,
398 vcdinfo_get_default_offset(p_vcd->vcd,
400 &itemid.num, "default");
403 case PSD_TYPE_PLAY_LIST:
404 case PSD_TYPE_END_LIST:
405 case PSD_TYPE_COMMAND_LIST:
406 LOG_WARN( "There is no PBC 'default' selection here" );
411 /* PBC is not on. "default" selection beginning of current
414 p_vcd->play_item.num = p_vcd->play_item.num;
418 /** ??? p_vcd->update_title(); ***/
419 return VLC_SUCCESS == VCDPlay( p_input, itemid );
424 Play item assocated with the "next" selection.
426 Return false if there was some problem.
429 vcdplayer_play_next( input_thread_t * p_input )
431 thread_vcd_data_t *p_vcd= (thread_vcd_data_t *)p_input->p_access_data;
433 vcdinfo_obj_t *obj = p_vcd->vcd;
434 vcdinfo_itemid_t itemid;
436 dbg_print( (INPUT_DBG_CALL|INPUT_DBG_PBC),
437 "current: %d" , p_vcd->play_item.num);
439 itemid.type = p_vcd->play_item.type;
441 if (vcdplayer_pbc_is_on(p_vcd)) {
443 vcdinfo_lid_get_pxd(obj, &(p_vcd->pxd), p_vcd->cur_lid);
445 switch (p_vcd->pxd.descriptor_type) {
446 case PSD_TYPE_SELECTION_LIST:
447 case PSD_TYPE_EXT_SELECTION_LIST:
448 if (p_vcd->pxd.psd == NULL) return false;
449 vcdplayer_update_entry( p_input,
450 vcdinf_psd_get_next_offset(p_vcd->pxd.psd),
451 &itemid.num, "next");
454 case PSD_TYPE_PLAY_LIST:
455 if (p_vcd->pxd.pld == NULL) return false;
456 vcdplayer_update_entry( p_input,
457 vcdinf_pld_get_next_offset(p_vcd->pxd.pld),
458 &itemid.num, "next");
461 case PSD_TYPE_END_LIST:
462 case PSD_TYPE_COMMAND_LIST:
463 LOG_WARN( "There is no PBC 'next' selection here" );
468 /* PBC is not on. "Next" selection is play_item.num+1 if possible. */
472 switch (p_vcd->play_item.type) {
473 case VCDINFO_ITEM_TYPE_ENTRY:
474 case VCDINFO_ITEM_TYPE_SEGMENT:
475 case VCDINFO_ITEM_TYPE_TRACK:
477 switch (p_vcd->play_item.type) {
478 case VCDINFO_ITEM_TYPE_ENTRY:
479 max_entry = p_vcd->num_entries;
481 case VCDINFO_ITEM_TYPE_SEGMENT:
482 max_entry = p_vcd->num_segments;
484 case VCDINFO_ITEM_TYPE_TRACK:
485 max_entry = p_vcd->num_tracks;
487 default: ; /* Handle exceptional cases below */
490 if (p_vcd->play_item.num+1 < max_entry) {
491 itemid.num = p_vcd->play_item.num+1;
493 LOG_WARN( "At the end - non-PBC 'next' not possible here" );
499 case VCDINFO_ITEM_TYPE_LID:
501 /* Should have handled above. */
502 LOG_WARN( "Internal inconsistency - should not have gotten here." );
510 /** ??? p_vcd->update_title(); ***/
511 return VLC_SUCCESS == VCDPlay( p_input, itemid );
516 Play item assocated with the "prev" selection.
518 Return false if there was some problem.
521 vcdplayer_play_prev( input_thread_t * p_input )
523 thread_vcd_data_t *p_vcd= (thread_vcd_data_t *)p_input->p_access_data;
525 vcdinfo_obj_t *obj = p_vcd->vcd;
526 vcdinfo_itemid_t itemid;
528 dbg_print( (INPUT_DBG_CALL|INPUT_DBG_PBC),
529 "current: %d" , p_vcd->play_item.num);
531 itemid.type = p_vcd->play_item.type;
533 if (vcdplayer_pbc_is_on(p_vcd)) {
535 vcdinfo_lid_get_pxd(obj, &(p_vcd->pxd), p_vcd->cur_lid);
537 switch (p_vcd->pxd.descriptor_type) {
538 case PSD_TYPE_SELECTION_LIST:
539 case PSD_TYPE_EXT_SELECTION_LIST:
540 if (p_vcd->pxd.psd == NULL) return false;
541 vcdplayer_update_entry( p_input,
542 vcdinf_psd_get_prev_offset(p_vcd->pxd.psd),
543 &itemid.num, "prev");
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_prev_offset(p_vcd->pxd.pld),
550 &itemid.num, "prev");
553 case PSD_TYPE_END_LIST:
554 case PSD_TYPE_COMMAND_LIST:
555 LOG_WARN( "There is no PBC 'prev' selection here" );
560 /* PBC is not on. "Prev" selection is play_item.num-1 if possible. */
562 int min_entry = (VCDINFO_ITEM_TYPE_ENTRY == p_vcd->play_item.type)
565 if (p_vcd->play_item.num > min_entry) {
566 itemid.num = p_vcd->play_item.num-1;
568 LOG_WARN( "At the beginning - non-PBC 'prev' not possible here" );
574 /** ??? p_vcd->update_title(); ***/
575 return VLC_SUCCESS == VCDPlay( p_input, itemid );
580 Play item assocated with the "return" selection.
582 Return false if there was some problem.
585 vcdplayer_play_return( input_thread_t * p_input )
587 thread_vcd_data_t *p_vcd= (thread_vcd_data_t *)p_input->p_access_data;
589 vcdinfo_obj_t *obj = p_vcd->vcd;
590 vcdinfo_itemid_t itemid;
592 dbg_print( (INPUT_DBG_CALL|INPUT_DBG_PBC),
593 "current: %d" , p_vcd->play_item.num);
595 itemid.type = p_vcd->play_item.type;
597 if (vcdplayer_pbc_is_on(p_vcd)) {
599 vcdinfo_lid_get_pxd(obj, &(p_vcd->pxd), p_vcd->cur_lid);
601 switch (p_vcd->pxd.descriptor_type) {
602 case PSD_TYPE_SELECTION_LIST:
603 case PSD_TYPE_EXT_SELECTION_LIST:
604 if (p_vcd->pxd.psd == NULL) return false;
605 vcdplayer_update_entry( p_input,
606 vcdinf_psd_get_return_offset(p_vcd->pxd.psd),
607 &itemid.num, "return");
610 case PSD_TYPE_PLAY_LIST:
611 if (p_vcd->pxd.pld == NULL) return false;
612 vcdplayer_update_entry( p_input,
613 vcdinf_pld_get_return_offset(p_vcd->pxd.pld),
614 &itemid.num, "return");
617 case PSD_TYPE_END_LIST:
618 case PSD_TYPE_COMMAND_LIST:
619 LOG_WARN( "There is no PBC 'return' selection here" );
624 /* PBC is not on. "Return" selection is min_entry if possible. */
626 p_vcd->play_item.num = (VCDINFO_ITEM_TYPE_ENTRY == p_vcd->play_item.type)
631 /** ??? p_vcd->update_title(); ***/
632 return VLC_SUCCESS == VCDPlay( p_input, itemid );