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.6 2003/12/05 04:24:47 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 /* FIXME: Will do whatever the right thing is later. */
140 #define SLEEP_1_SEC_AND_HANDLE_EVENTS sleep(1)
142 /* Handles PBC navigation when reaching the end of a play item. */
143 vcdplayer_read_status_t
144 vcdplayer_pbc_nav ( input_thread_t * p_input )
146 thread_vcd_data_t * p_vcd= (thread_vcd_data_t *)p_input->p_access_data;
148 /* We are in playback control. */
149 vcdinfo_itemid_t itemid;
151 if (0 != p_vcd->in_still && p_vcd->in_still != -5) {
152 SLEEP_1_SEC_AND_HANDLE_EVENTS;
153 if (p_vcd->in_still > 0) p_vcd->in_still--;
154 return READ_STILL_FRAME;
157 /* The end of an entry is really the end of the associated
158 sequence (or track). */
160 if ( (VCDINFO_ITEM_TYPE_ENTRY == p_vcd->play_item.type) &&
161 (p_vcd->cur_lsn < p_vcd->end_lsn) ) {
162 /* Set up to just continue to the next entry */
163 p_vcd->play_item.num++;
164 dbg_print( (INPUT_DBG_LSN|INPUT_DBG_PBC),
165 "continuing into next entry: %u", p_vcd->play_item.num);
166 VCDPlay( p_input, p_vcd->play_item );
167 /* p_vcd->update_title(); */
171 switch (p_vcd->pxd.descriptor_type) {
172 case PSD_TYPE_END_LIST:
175 case PSD_TYPE_PLAY_LIST: {
176 int wait_time = vcdinf_get_wait_time(p_vcd->pxd.pld);
178 dbg_print(INPUT_DBG_PBC, "playlist wait_time: %d", wait_time);
180 if (vcdplayer_inc_play_item(p_input))
183 /* Handle any wait time given. */
184 if (-5 == p_vcd->in_still) {
185 if (wait_time != 0) {
187 p_vcd->in_still = wait_time - 1;
188 p_vcd->p_intf->p_sys->m_still_time = (wait_time - 1) * 1000000;
189 return READ_STILL_FRAME;
191 p_vcd->p_intf->p_sys->m_still_time = 0;
192 p_vcd->p_intf->p_sys->b_inf_still = 1;
196 vcdplayer_update_entry( p_input,
197 vcdinf_pld_get_next_offset(p_vcd->pxd.pld),
198 &itemid.num, "next" );
199 itemid.type = VCDINFO_ITEM_TYPE_LID;
200 VCDPlay( p_input, itemid );
203 case PSD_TYPE_SELECTION_LIST: /* Selection List (+Ext. for SVCD) */
204 case PSD_TYPE_EXT_SELECTION_LIST: /* Extended Selection List (VCD2.0) */
206 int wait_time = vcdinf_get_timeout_time(p_vcd->pxd.psd);
207 uint16_t timeout_offs = vcdinf_get_timeout_offset(p_vcd->pxd.psd);
208 uint16_t max_loop = vcdinf_get_loop_count(p_vcd->pxd.psd);
209 vcdinfo_offset_t *offset_timeout_LID =
210 vcdinfo_get_offset_t(p_vcd->vcd, timeout_offs);
212 dbg_print(INPUT_DBG_PBC, "wait_time: %d, looped: %d, max_loop %d",
213 wait_time, p_vcd->loop_count, max_loop);
215 /* Handle any wait time given */
216 if (-5 == p_vcd->in_still) {
217 if (wait_time != 0) {
219 p_vcd->in_still = wait_time - 1;
220 p_vcd->p_intf->p_sys->m_still_time = (wait_time - 1) * 1000000;
221 return READ_STILL_FRAME;
223 p_vcd->p_intf->p_sys->m_still_time = 0;
224 p_vcd->p_intf->p_sys->b_inf_still = 1;
226 return READ_STILL_FRAME;
229 /* Handle any looping given. */
230 if ( max_loop == 0 || p_vcd->loop_count < max_loop ) {
232 if (p_vcd->loop_count == 0x7f) p_vcd->loop_count = 0;
233 VCDSeek( p_input, 0 );
234 /* if (p_vcd->in_still) p_vcd->force_redisplay();*/
238 /* Looping finished and wait finished. Move to timeout
239 entry or next entry, or handle still. */
241 if (NULL != offset_timeout_LID) {
242 /* Handle timeout_LID */
243 itemid.num = offset_timeout_LID->lid;
244 itemid.type = VCDINFO_ITEM_TYPE_LID;
245 dbg_print(INPUT_DBG_PBC, "timeout to: %d", itemid.num);
246 VCDPlay( p_input, itemid );
249 int num_selections = vcdinf_get_num_selections(p_vcd->pxd.psd);
250 if (num_selections > 0) {
251 /* Pick a random selection. */
252 unsigned int bsn=vcdinf_get_bsn(p_vcd->pxd.psd);
253 int rand_selection=bsn +
254 (int) ((num_selections+0.0)*rand()/(RAND_MAX+1.0));
255 lid_t rand_lid=vcdinfo_selection_get_lid (p_vcd->vcd,
258 itemid.num = rand_lid;
259 itemid.type = VCDINFO_ITEM_TYPE_LID;
260 dbg_print(INPUT_DBG_PBC, "random selection %d, lid: %d",
261 rand_selection - bsn, rand_lid);
262 VCDPlay( p_input, itemid );
264 } else if (p_vcd->in_still) {
265 /* Hack: Just go back and do still again */
266 SLEEP_1_SEC_AND_HANDLE_EVENTS ;
267 return READ_STILL_FRAME;
272 case VCDINFO_ITEM_TYPE_NOTFOUND:
273 LOG_ERR( "NOTFOUND in PBC -- not supposed to happen" );
275 case VCDINFO_ITEM_TYPE_SPAREID2:
276 LOG_ERR( "SPAREID2 in PBC -- not supposed to happen" );
278 case VCDINFO_ITEM_TYPE_LID:
279 LOG_ERR( "LID in PBC -- not supposed to happen" );
285 /* FIXME: Should handle autowait ... */
291 Get the next play-item in the list given in the LIDs. Note play-item
292 here refers to list of play-items for a single LID It shouldn't be
293 confused with a user's list of favorite things to play or the
294 "next" field of a LID which moves us to a different LID.
297 vcdplayer_inc_play_item( input_thread_t *p_input )
299 thread_vcd_data_t * p_vcd= (thread_vcd_data_t *)p_input->p_access_data;
303 dbg_print(INPUT_DBG_CALL, "called pli: %d", p_vcd->pdi);
305 if ( NULL == p_vcd || NULL == p_vcd->pxd.pld ) return false;
307 noi = vcdinf_pld_get_noi(p_vcd->pxd.pld);
309 if ( noi <= 0 ) return false;
311 /* Handle delays like autowait or wait here? */
315 if ( p_vcd->pdi < 0 || p_vcd->pdi >= noi ) return false;
318 uint16_t trans_itemid_num=vcdinf_pld_get_play_item(p_vcd->pxd.pld,
320 vcdinfo_itemid_t trans_itemid;
322 if (VCDINFO_INVALID_ITEMID == trans_itemid_num) return false;
324 vcdinfo_classify_itemid(trans_itemid_num, &trans_itemid);
325 dbg_print(INPUT_DBG_PBC, " play-item[%d]: %s",
326 p_vcd->pdi, vcdinfo_pin2str (trans_itemid_num));
327 return VLC_SUCCESS == VCDPlay( p_input, trans_itemid );
332 Play item assocated with the "default" selection.
334 Return false if there was some problem.
337 vcdplayer_play_default( input_thread_t * p_input )
339 thread_vcd_data_t *p_vcd= (thread_vcd_data_t *)p_input->p_access_data;
341 vcdinfo_itemid_t itemid;
343 dbg_print( (INPUT_DBG_CALL|INPUT_DBG_PBC),
344 "current: %d" , p_vcd->play_item.num);
346 itemid.type = p_vcd->play_item.type;
348 if (vcdplayer_pbc_is_on(p_vcd)) {
350 lid_t lid=vcdinfo_get_multi_default_lid(p_vcd->vcd, p_vcd->cur_lid,
353 if (VCDINFO_INVALID_LID != lid) {
355 itemid.type = VCDINFO_ITEM_TYPE_LID;
356 dbg_print(INPUT_DBG_PBC, "DEFAULT to %d\n", itemid.num);
358 dbg_print(INPUT_DBG_PBC, "no DEFAULT for LID %d\n", p_vcd->cur_lid);
364 /* PBC is not on. "default" selection beginning of current
367 p_vcd->play_item.num = p_vcd->play_item.num;
371 /** ??? p_vcd->update_title(); ***/
372 return VLC_SUCCESS == VCDPlay( p_input, itemid );
377 Play item assocated with the "next" selection.
379 Return false if there was some problem.
382 vcdplayer_play_next( input_thread_t * p_input )
384 thread_vcd_data_t *p_vcd= (thread_vcd_data_t *)p_input->p_access_data;
386 vcdinfo_obj_t *obj = p_vcd->vcd;
387 vcdinfo_itemid_t itemid;
389 dbg_print( (INPUT_DBG_CALL|INPUT_DBG_PBC),
390 "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 );