]> git.sesse.net Git - vlc/blob - modules/access/vcdx/vcdplayer.c
* modules/access/vcdx/*: Brand new VCD input module using libcdio, libvcd and libvcdi...
[vlc] / modules / access / vcdx / vcdplayer.c
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 $
7  *
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.
12  *
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.
17  *
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  *****************************************************************************/
22
23 /*
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.
27  */
28 /*****************************************************************************
29  * Preamble
30  *****************************************************************************/
31
32 #include <vlc/vlc.h>
33 #include <vlc/input.h>
34
35 #include "vcd.h"
36 #include "vcdplayer.h"
37
38 #include <string.h>
39
40 #include <cdio/cdio.h>
41 #include <cdio/util.h>
42 #include <libvcd/info.h>
43
44 /*!
45   Return true if playback control (PBC) is on
46 */
47 bool
48 vcdplayer_pbc_is_on(const thread_vcd_data_t *p_vcd) 
49 {
50   return VCDINFO_INVALID_ENTRY != p_vcd->cur_lid; 
51 }
52
53 lid_t
54 vcdplayer_selection2lid ( input_thread_t *p_input, int entry_num ) 
55 {
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;
59   unsigned int offset;
60   unsigned int bsn=vcdinf_get_bsn(p_vcd->pxd.psd);
61   vcdinfo_obj_t *obj = p_vcd->vcd;
62
63   dbg_print( (INPUT_DBG_CALL|INPUT_DBG_PBC), 
64             "Called lid %u, entry_num %d bsn %d", p_vcd->cur_lid, 
65              entry_num, bsn);
66
67   if ( (entry_num - bsn + 1) > 0) {
68     offset = vcdinfo_lid_get_offset(obj, p_vcd->cur_lid, entry_num-bsn+1);
69   } else {
70     LOG_ERR( "Selection number %u too small. bsn %u", entry_num, bsn );
71     return VCDINFO_INVALID_LID;
72   }
73   
74   if (offset != VCDINFO_INVALID_OFFSET) {
75     vcdinfo_offset_t *ofs;
76     int old = entry_num;
77     
78     switch (offset) {
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;
88     default: ;
89     }
90     
91     ofs = vcdinfo_get_offset_t(obj, offset);
92
93     if (NULL == ofs) {
94       LOG_ERR( "error in vcdinfo_get_offset" );
95       return -1;
96     }
97     dbg_print(INPUT_DBG_PBC,
98               "entry %u turned into selection lid %u", 
99               old, ofs->lid);
100     return ofs->lid;
101     
102   } else {
103     LOG_ERR( "invalid or unset entry %u", entry_num );
104     return VCDINFO_INVALID_LID;
105   }
106 }
107
108 static void
109 vcdplayer_update_entry( input_thread_t * p_input, uint16_t ofs, 
110                         uint16_t *entry, const char *label)
111 {
112   thread_vcd_data_t *     p_vcd= (thread_vcd_data_t *)p_input->p_access_data;
113
114   if ( ofs == VCDINFO_INVALID_OFFSET ) {
115     *entry = VCDINFO_INVALID_ENTRY;
116   } else {
117     vcdinfo_offset_t *off_t = vcdinfo_get_offset_t(p_vcd->vcd, ofs);
118     if (off_t != NULL) {
119       *entry = off_t->lid;
120       dbg_print(INPUT_DBG_PBC, "%s: %d\n", label, off_t->lid);
121     } else
122       *entry = VCDINFO_INVALID_ENTRY;
123   }
124 }
125
126 /* Handles navigation when NOT in PBC reaching the end of a play item. 
127
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.
130
131    return true if the caller should return.
132 */
133 vcdplayer_read_status_t
134 vcdplayer_non_pbc_nav ( input_thread_t * p_input )
135 {
136   thread_vcd_data_t *     p_vcd= (thread_vcd_data_t *)p_input->p_access_data;
137
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;
143
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] );
146     
147     if ( p_vcd->cur_track >= p_vcd->num_tracks - 1 )
148       return READ_END; /* EOF */
149         
150     p_vcd->play_item.num = p_vcd->cur_track++;
151     
152     vlc_mutex_lock( &p_input->stream.stream_lock );
153     p_area = p_input->stream.pp_areas[p_vcd->cur_track];
154     
155     p_area->i_part = 1;
156     VCDSetArea( p_input, p_area );
157     vlc_mutex_unlock( &p_input->stream.stream_lock );
158     return READ_BLOCK;
159     break;
160   }
161   case VCDINFO_ITEM_TYPE_SPAREID2:  
162     dbg_print( (INPUT_DBG_STILL|INPUT_DBG_LSN), 
163                "SPAREID2" );
164     /* FIXME */
165     p_input->stream.b_seekable = 0;
166     if (p_vcd->in_still)
167     {
168       return READ_STILL_FRAME ;
169     }
170     return READ_END;
171   case VCDINFO_ITEM_TYPE_NOTFOUND:  
172     LOG_ERR ("NOTFOUND outside PBC -- not supposed to happen");
173     return READ_ERROR;
174   case VCDINFO_ITEM_TYPE_LID:  
175     LOG_ERR ("LID outside PBC -- not supposed to happen");
176     return READ_ERROR;
177   case VCDINFO_ITEM_TYPE_SEGMENT:
178       /* Hack: Just go back and do still again */
179     /* FIXME */
180     p_input->stream.b_seekable = 0;
181     if (p_vcd->in_still) 
182     {
183       dbg_print( (INPUT_DBG_STILL|INPUT_DBG_LSN), 
184                  "End of Segment - looping" );
185       return READ_STILL_FRAME;
186     }
187     return READ_END;
188   }
189   return READ_BLOCK;
190 }
191
192 /* FIXME: Will do whatever the right thing is later. */
193 #define SLEEP_1_SEC_AND_HANDLE_EVENTS sleep(1)
194
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 )
198 {
199   thread_vcd_data_t *     p_vcd= (thread_vcd_data_t *)p_input->p_access_data;
200
201   /* We are in playback control. */
202   vcdinfo_itemid_t itemid;
203
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;
208   }
209
210   /* The end of an entry is really the end of the associated 
211      sequence (or track). */
212   
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(); */
221     return READ_BLOCK;
222   }
223   
224   switch (p_vcd->pxd.descriptor_type) {
225   case PSD_TYPE_END_LIST:
226     return READ_END;
227     break;
228   case PSD_TYPE_PLAY_LIST: {
229     int wait_time = vcdinf_get_wait_time(p_vcd->pxd.pld);
230     
231     dbg_print(INPUT_DBG_PBC, "playlist wait_time: %d", wait_time);
232     
233     if (vcdplayer_inc_play_item(p_input))
234       return READ_BLOCK;
235
236     /* Handle any wait time given. */
237     if (-5 == p_vcd->in_still) {
238       if (wait_time != 0) {
239         /* FIXME */
240         p_vcd->in_still = wait_time - 1;
241         SLEEP_1_SEC_AND_HANDLE_EVENTS ;
242         return READ_STILL_FRAME;
243       }
244     }
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 );
250     break;
251   }
252   case PSD_TYPE_SELECTION_LIST:     /* Selection List (+Ext. for SVCD) */
253   case PSD_TYPE_EXT_SELECTION_LIST: /* Extended Selection List (VCD2.0) */
254     {
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);
260       
261       dbg_print(INPUT_DBG_PBC, "wait_time: %d, looped: %d, max_loop %d", 
262                 wait_time, p_vcd->loop_count, max_loop);
263       
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;
269       }
270       
271       /* Handle any looping given. */
272       if ( max_loop == 0 || p_vcd->loop_count < max_loop ) {
273         p_vcd->loop_count++;
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();*/
277         return READ_BLOCK;
278       }
279       
280       /* Looping finished and wait finished. Move to timeout
281          entry or next entry, or handle still. */
282       
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 );
289         return READ_BLOCK;
290       } else {
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 );
303           return READ_BLOCK;
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;
308         }
309       }
310       break;
311     }
312   case VCDINFO_ITEM_TYPE_NOTFOUND:  
313     LOG_ERR( "NOTFOUND in PBC -- not supposed to happen" );
314     break;
315   case VCDINFO_ITEM_TYPE_SPAREID2:  
316     LOG_ERR( "SPAREID2 in PBC -- not supposed to happen" );
317     break;
318   case VCDINFO_ITEM_TYPE_LID:  
319     LOG_ERR( "LID in PBC -- not supposed to happen" );
320     break;
321     
322   default:
323     ;
324   }
325   /* FIXME: Should handle autowait ...  */
326
327   return READ_ERROR;
328 }
329
330 /*
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.
335  */
336 bool
337 vcdplayer_inc_play_item( input_thread_t *p_input )
338 {
339   thread_vcd_data_t *     p_vcd= (thread_vcd_data_t *)p_input->p_access_data;
340
341   int noi;
342
343   dbg_print(INPUT_DBG_CALL, "called pli: %d", p_vcd->pdi);
344
345   if ( NULL == p_vcd || NULL == p_vcd->pxd.pld  ) return false;
346
347   noi = vcdinf_pld_get_noi(p_vcd->pxd.pld);
348   
349   if ( noi <= 0 ) return false;
350   
351   /* Handle delays like autowait or wait here? */
352
353   p_vcd->pdi++;
354
355   if ( p_vcd->pdi < 0 || p_vcd->pdi >= noi ) return false;
356
357   else {
358     uint16_t trans_itemid_num=vcdinf_pld_get_play_item(p_vcd->pxd.pld, 
359                                                        p_vcd->pdi);
360     vcdinfo_itemid_t trans_itemid;
361
362     if (VCDINFO_INVALID_ITEMID == trans_itemid_num) return false;
363     
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 );
368   }
369 }