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.1 2003/10/04 18:55:13 gbazin 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 );