]> git.sesse.net Git - vlc/blob - modules/access/vcdx/vcdplayer.c
If not in PBC, playlist fills in all entries.
[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.9 2003/12/13 12:56:14 rocky 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 #include <vlc/intf.h>
35
36 #include "vcd.h"
37 #include "vcdplayer.h"
38 #include "intf.h"
39
40 #include <string.h>
41
42 #include <cdio/cdio.h>
43 #include <cdio/util.h>
44 #include <libvcd/info.h>
45
46 /*!
47   Return true if playback control (PBC) is on
48 */
49 bool
50 vcdplayer_pbc_is_on(const thread_vcd_data_t *p_vcd) 
51 {
52   return VCDINFO_INVALID_ENTRY != p_vcd->cur_lid; 
53 }
54
55 static void
56 vcdplayer_update_entry( input_thread_t * p_input, uint16_t ofs, 
57                         uint16_t *entry, const char *label)
58 {
59   thread_vcd_data_t *     p_vcd= (thread_vcd_data_t *)p_input->p_access_data;
60
61   if ( ofs == VCDINFO_INVALID_OFFSET ) {
62     *entry = VCDINFO_INVALID_ENTRY;
63   } else {
64     vcdinfo_offset_t *off_t = vcdinfo_get_offset_t(p_vcd->vcd, ofs);
65     if (off_t != NULL) {
66       *entry = off_t->lid;
67       dbg_print(INPUT_DBG_PBC, "%s: LID %d\n", label, off_t->lid);
68     } else
69       *entry = VCDINFO_INVALID_ENTRY;
70   }
71 }
72
73 /* Handles navigation when NOT in PBC reaching the end of a play item. 
74
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.
77
78    return true if the caller should return.
79 */
80 vcdplayer_read_status_t
81 vcdplayer_non_pbc_nav ( input_thread_t * p_input )
82 {
83   thread_vcd_data_t *     p_vcd= (thread_vcd_data_t *)p_input->p_access_data;
84
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: {
89     input_area_t *p_area;
90
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] );
93     return READ_END;
94     break;
95   }
96   case VCDINFO_ITEM_TYPE_SPAREID2:  
97     dbg_print( (INPUT_DBG_STILL|INPUT_DBG_LSN), 
98                "SPAREID2" );
99     /* FIXME */
100     p_input->stream.b_seekable = 0;
101     if (p_vcd->in_still)
102     {
103       return READ_STILL_FRAME ;
104     }
105     return READ_END;
106   case VCDINFO_ITEM_TYPE_NOTFOUND:  
107     LOG_ERR ("NOTFOUND outside PBC -- not supposed to happen");
108     return READ_ERROR;
109   case VCDINFO_ITEM_TYPE_LID:  
110     LOG_ERR ("LID outside PBC -- not supposed to happen");
111     return READ_ERROR;
112   case VCDINFO_ITEM_TYPE_SEGMENT:
113       /* Hack: Just go back and do still again */
114     /* FIXME */
115     p_input->stream.b_seekable = 0;
116     if (p_vcd->in_still) 
117     {
118       dbg_print( (INPUT_DBG_STILL|INPUT_DBG_LSN), 
119                  "End of Segment - looping" );
120       return READ_STILL_FRAME;
121     }
122     return READ_END;
123   }
124   return READ_BLOCK;
125 }
126
127 /* Handles PBC navigation when reaching the end of a play item. */
128 vcdplayer_read_status_t
129 vcdplayer_pbc_nav ( input_thread_t * p_input )
130 {
131   thread_vcd_data_t *     p_vcd= (thread_vcd_data_t *)p_input->p_access_data;
132
133   /* We are in playback control. */
134   vcdinfo_itemid_t itemid;
135
136   /* The end of an entry is really the end of the associated 
137      sequence (or track). */
138   
139   if ( (VCDINFO_ITEM_TYPE_ENTRY == p_vcd->play_item.type) && 
140        (p_vcd->cur_lsn < p_vcd->end_lsn) ) {
141     /* Set up to just continue to the next entry */
142     p_vcd->play_item.num++;
143     dbg_print( (INPUT_DBG_LSN|INPUT_DBG_PBC), 
144                "continuing into next entry: %u", p_vcd->play_item.num);
145     VCDPlay( p_input, p_vcd->play_item );
146     /* p_vcd->update_title(); */
147     return READ_BLOCK;
148   }
149   
150   switch (p_vcd->pxd.descriptor_type) {
151   case PSD_TYPE_END_LIST:
152     return READ_END;
153     break;
154   case PSD_TYPE_PLAY_LIST: {
155     int wait_time = vcdinf_get_wait_time(p_vcd->pxd.pld);
156     
157     dbg_print(INPUT_DBG_PBC, "playlist wait_time: %d", wait_time);
158     
159     if (vcdplayer_inc_play_item(p_input))
160       return READ_BLOCK;
161
162     /* Handle any wait time given. */
163     if (p_vcd->in_still) {
164       vcdIntfStillTime( p_vcd->p_intf, wait_time );
165       return READ_STILL_FRAME;
166     }
167
168     vcdplayer_update_entry( p_input, 
169                             vcdinf_pld_get_next_offset(p_vcd->pxd.pld),
170                             &itemid.num, "next" );
171     itemid.type = VCDINFO_ITEM_TYPE_LID;
172     VCDPlay( p_input, itemid );
173     break;
174   }
175   case PSD_TYPE_SELECTION_LIST:     /* Selection List (+Ext. for SVCD) */
176   case PSD_TYPE_EXT_SELECTION_LIST: /* Extended Selection List (VCD2.0) */
177     {
178       int wait_time         = vcdinf_get_timeout_time(p_vcd->pxd.psd);
179       uint16_t timeout_offs = vcdinf_get_timeout_offset(p_vcd->pxd.psd);
180       uint16_t max_loop     = vcdinf_get_loop_count(p_vcd->pxd.psd);
181       vcdinfo_offset_t *offset_timeout_LID = 
182         vcdinfo_get_offset_t(p_vcd->vcd, timeout_offs);
183       
184       dbg_print(INPUT_DBG_PBC, "wait_time: %d, looped: %d, max_loop %d", 
185                 wait_time, p_vcd->loop_count, max_loop);
186       
187       /* Handle any wait time given */
188       if (p_vcd->in_still) {
189         vcdIntfStillTime( p_vcd->p_intf, wait_time );
190         return READ_STILL_FRAME;
191       } 
192       
193       /* Handle any looping given. */
194       if ( max_loop == 0 || p_vcd->loop_count < max_loop ) {
195         p_vcd->loop_count++;
196         if (p_vcd->loop_count == 0x7f) p_vcd->loop_count = 0;
197         VCDSeek( p_input, 0 );
198         /* if (p_vcd->in_still) p_vcd->force_redisplay();*/
199         return READ_BLOCK;
200       }
201       
202       /* Looping finished and wait finished. Move to timeout
203          entry or next entry, or handle still. */
204       
205       if (NULL != offset_timeout_LID) {
206         /* Handle timeout_LID */
207         itemid.num  = offset_timeout_LID->lid;
208         itemid.type = VCDINFO_ITEM_TYPE_LID;
209         dbg_print(INPUT_DBG_PBC, "timeout to: %d", itemid.num);
210         VCDPlay( p_input, itemid );
211         return READ_BLOCK;
212       } else {
213         int num_selections = vcdinf_get_num_selections(p_vcd->pxd.psd);
214         if (num_selections > 0) {
215           /* Pick a random selection. */
216           unsigned int bsn=vcdinf_get_bsn(p_vcd->pxd.psd);
217           int rand_selection=bsn +
218             (int) ((num_selections+0.0)*rand()/(RAND_MAX+1.0));
219           lid_t rand_lid=vcdinfo_selection_get_lid (p_vcd->vcd, 
220                                                     p_vcd->cur_lid, 
221                                                     rand_selection);
222           itemid.num = rand_lid;
223           itemid.type = VCDINFO_ITEM_TYPE_LID;
224           dbg_print(INPUT_DBG_PBC, "random selection %d, lid: %d", 
225                     rand_selection - bsn, rand_lid);
226           VCDPlay( p_input, itemid );
227           return READ_BLOCK;
228         } else if (p_vcd->in_still) {
229           /* Hack: Just go back and do still again */
230           sleep(1);
231           return READ_STILL_FRAME;
232         }
233       }
234       break;
235     }
236   case VCDINFO_ITEM_TYPE_NOTFOUND:  
237     LOG_ERR( "NOTFOUND in PBC -- not supposed to happen" );
238     break;
239   case VCDINFO_ITEM_TYPE_SPAREID2:  
240     LOG_ERR( "SPAREID2 in PBC -- not supposed to happen" );
241     break;
242   case VCDINFO_ITEM_TYPE_LID:  
243     LOG_ERR( "LID in PBC -- not supposed to happen" );
244     break;
245     
246   default:
247     ;
248   }
249   /* FIXME: Should handle autowait ...  */
250
251   return READ_ERROR;
252 }
253
254 /*!
255   Get the next play-item in the list given in the LIDs. Note play-item
256   here refers to list of play-items for a single LID It shouldn't be
257   confused with a user's list of favorite things to play or the 
258   "next" field of a LID which moves us to a different LID.
259  */
260 bool
261 vcdplayer_inc_play_item( input_thread_t *p_input )
262 {
263   thread_vcd_data_t *     p_vcd= (thread_vcd_data_t *)p_input->p_access_data;
264
265   int noi;
266
267   dbg_print(INPUT_DBG_CALL, "called pli: %d", p_vcd->pdi);
268
269   if ( NULL == p_vcd || NULL == p_vcd->pxd.pld  ) return false;
270
271   noi = vcdinf_pld_get_noi(p_vcd->pxd.pld);
272   
273   if ( noi <= 0 ) return false;
274   
275   /* Handle delays like autowait or wait here? */
276
277   p_vcd->pdi++;
278
279   if ( p_vcd->pdi < 0 || p_vcd->pdi >= noi ) return false;
280
281   else {
282     uint16_t trans_itemid_num=vcdinf_pld_get_play_item(p_vcd->pxd.pld, 
283                                                        p_vcd->pdi);
284     vcdinfo_itemid_t trans_itemid;
285
286     if (VCDINFO_INVALID_ITEMID == trans_itemid_num) return false;
287     
288     vcdinfo_classify_itemid(trans_itemid_num, &trans_itemid);
289     dbg_print(INPUT_DBG_PBC, "  play-item[%d]: %s",
290               p_vcd->pdi, vcdinfo_pin2str (trans_itemid_num));
291     return VLC_SUCCESS == VCDPlay( p_input, trans_itemid );
292   }
293 }
294
295 /*!
296   Play item assocated with the "default" selection.
297
298   Return false if there was some problem.
299 */
300 bool
301 vcdplayer_play_default( input_thread_t * p_input )
302 {
303   thread_vcd_data_t *p_vcd= (thread_vcd_data_t *)p_input->p_access_data;
304
305   vcdinfo_itemid_t   itemid;
306
307   dbg_print( (INPUT_DBG_CALL|INPUT_DBG_PBC), 
308              "current: %d" , p_vcd->play_item.num);
309
310   itemid.type = p_vcd->play_item.type;
311
312   if  (vcdplayer_pbc_is_on(p_vcd)) {
313
314     lid_t lid=vcdinfo_get_multi_default_lid(p_vcd->vcd, p_vcd->cur_lid,
315                                             itemid.num);
316
317     if (VCDINFO_INVALID_LID != lid) {
318       itemid.num  = lid;
319       itemid.type = VCDINFO_ITEM_TYPE_LID;
320       dbg_print(INPUT_DBG_PBC, "DEFAULT to %d\n", itemid.num);
321     } else {
322       dbg_print(INPUT_DBG_PBC, "no DEFAULT for LID %d\n", p_vcd->cur_lid);
323     }
324     
325
326   } else {
327
328     /* PBC is not on. "default" selection beginning of current 
329        selection . */
330   
331     p_vcd->play_item.num = p_vcd->play_item.num;
332     
333   }
334
335   /** ??? p_vcd->update_title(); ***/
336   return VLC_SUCCESS == VCDPlay( p_input, itemid );
337
338 }
339
340 /*!
341   Play item assocated with the "next" selection.
342
343   Return false if there was some problem.
344 */
345 bool
346 vcdplayer_play_next( input_thread_t * p_input )
347 {
348   thread_vcd_data_t *p_vcd= (thread_vcd_data_t *)p_input->p_access_data;
349
350   vcdinfo_obj_t     *obj;
351   vcdinfo_itemid_t   itemid;
352
353   if (!p_vcd) return false;
354
355   dbg_print( (INPUT_DBG_CALL|INPUT_DBG_PBC), 
356              "current: %d" , p_vcd->play_item.num);
357
358   obj = p_vcd->vcd;
359
360   itemid.type = p_vcd->play_item.type;
361
362   if  (vcdplayer_pbc_is_on(p_vcd)) {
363
364     vcdinfo_lid_get_pxd(obj, &(p_vcd->pxd), p_vcd->cur_lid);
365     
366     switch (p_vcd->pxd.descriptor_type) {
367     case PSD_TYPE_SELECTION_LIST:
368     case PSD_TYPE_EXT_SELECTION_LIST:
369       if (p_vcd->pxd.psd == NULL) return false;
370       vcdplayer_update_entry( p_input, 
371                               vcdinf_psd_get_next_offset(p_vcd->pxd.psd), 
372                               &itemid.num, "next");
373       itemid.type = VCDINFO_ITEM_TYPE_LID;
374       break;
375
376     case PSD_TYPE_PLAY_LIST: 
377       if (p_vcd->pxd.pld == NULL) return false;
378       vcdplayer_update_entry( p_input, 
379                               vcdinf_pld_get_next_offset(p_vcd->pxd.pld), 
380                               &itemid.num, "next");
381       itemid.type = VCDINFO_ITEM_TYPE_LID;
382       break;
383       
384     case PSD_TYPE_END_LIST:
385     case PSD_TYPE_COMMAND_LIST:
386       LOG_WARN( "There is no PBC 'next' selection here" );
387       return false;
388     }
389   } else {
390
391     /* PBC is not on. "Next" selection is play_item.num+1 if possible. */
392   
393     int max_entry = 0;
394
395     switch (p_vcd->play_item.type) {
396     case VCDINFO_ITEM_TYPE_ENTRY: 
397     case VCDINFO_ITEM_TYPE_SEGMENT: 
398     case VCDINFO_ITEM_TYPE_TRACK: 
399       
400       switch (p_vcd->play_item.type) {
401       case VCDINFO_ITEM_TYPE_ENTRY: 
402         max_entry = p_vcd->num_entries;
403         break;
404       case VCDINFO_ITEM_TYPE_SEGMENT: 
405         max_entry = p_vcd->num_segments;
406         break;
407       case VCDINFO_ITEM_TYPE_TRACK: 
408         max_entry = p_vcd->num_tracks;
409         break;
410       default: ; /* Handle exceptional cases below */
411       }
412       
413       if (p_vcd->play_item.num+1 < max_entry) {
414         itemid.num = p_vcd->play_item.num+1;
415       } else {
416         LOG_WARN( "At the end - non-PBC 'next' not possible here" );
417         return false;
418       }
419       
420       break;
421       
422     case VCDINFO_ITEM_TYPE_LID: 
423       {
424         /* Should have handled above. */
425         LOG_WARN( "Internal inconsistency - should not have gotten here." );
426         return false;
427       }
428     default: 
429       return false;
430     }
431   }
432
433   /** ??? p_vcd->update_title(); ***/
434   return VLC_SUCCESS == VCDPlay( p_input, itemid );
435
436 }
437
438 /*!
439   Play item assocated with the "prev" selection.
440
441   Return false if there was some problem.
442 */
443 bool
444 vcdplayer_play_prev( input_thread_t * p_input )
445 {
446   thread_vcd_data_t *p_vcd= (thread_vcd_data_t *)p_input->p_access_data;
447
448   vcdinfo_obj_t     *obj  = p_vcd->vcd;
449   vcdinfo_itemid_t   itemid;
450
451   dbg_print( (INPUT_DBG_CALL|INPUT_DBG_PBC), 
452              "current: %d" , p_vcd->play_item.num);
453
454   itemid.type = p_vcd->play_item.type;
455
456   if  (vcdplayer_pbc_is_on(p_vcd)) {
457
458     vcdinfo_lid_get_pxd(obj, &(p_vcd->pxd), p_vcd->cur_lid);
459     
460     switch (p_vcd->pxd.descriptor_type) {
461     case PSD_TYPE_SELECTION_LIST:
462     case PSD_TYPE_EXT_SELECTION_LIST:
463       if (p_vcd->pxd.psd == NULL) return false;
464       vcdplayer_update_entry( p_input, 
465                               vcdinf_psd_get_prev_offset(p_vcd->pxd.psd), 
466                               &itemid.num, "prev");
467       itemid.type = VCDINFO_ITEM_TYPE_LID;
468       break;
469
470     case PSD_TYPE_PLAY_LIST: 
471       if (p_vcd->pxd.pld == NULL) return false;
472       vcdplayer_update_entry( p_input, 
473                               vcdinf_pld_get_prev_offset(p_vcd->pxd.pld), 
474                               &itemid.num, "prev");
475       itemid.type = VCDINFO_ITEM_TYPE_LID;
476       break;
477       
478     case PSD_TYPE_END_LIST:
479     case PSD_TYPE_COMMAND_LIST:
480       LOG_WARN( "There is no PBC 'prev' selection here" );
481       return false;
482     }
483   } else {
484
485     /* PBC is not on. "Prev" selection is play_item.num-1 if possible. */
486   
487     int min_entry = (VCDINFO_ITEM_TYPE_ENTRY == p_vcd->play_item.type) 
488       ? 0 : 1;
489     
490     if (p_vcd->play_item.num > min_entry) {
491       itemid.num = p_vcd->play_item.num-1;
492     } else {
493       LOG_WARN( "At the beginning - non-PBC 'prev' not possible here" );
494       return false;
495     }
496       
497   }
498
499   /** ??? p_vcd->update_title(); ***/
500   return VLC_SUCCESS == VCDPlay( p_input, itemid );
501
502 }
503
504 /*!
505   Play item assocated with the "return" selection.
506
507   Return false if there was some problem.
508 */
509 bool
510 vcdplayer_play_return( input_thread_t * p_input )
511 {
512   thread_vcd_data_t *p_vcd= (thread_vcd_data_t *)p_input->p_access_data;
513
514   vcdinfo_obj_t     *obj  = p_vcd->vcd;
515   vcdinfo_itemid_t   itemid;
516
517   dbg_print( (INPUT_DBG_CALL|INPUT_DBG_PBC), 
518              "current: %d" , p_vcd->play_item.num);
519
520   itemid.type = p_vcd->play_item.type;
521
522   if  (vcdplayer_pbc_is_on(p_vcd)) {
523
524     vcdinfo_lid_get_pxd(obj, &(p_vcd->pxd), p_vcd->cur_lid);
525     
526     switch (p_vcd->pxd.descriptor_type) {
527     case PSD_TYPE_SELECTION_LIST:
528     case PSD_TYPE_EXT_SELECTION_LIST:
529       if (p_vcd->pxd.psd == NULL) return false;
530       vcdplayer_update_entry( p_input, 
531                               vcdinf_psd_get_return_offset(p_vcd->pxd.psd), 
532                               &itemid.num, "return");
533       itemid.type = VCDINFO_ITEM_TYPE_LID;
534       break;
535
536     case PSD_TYPE_PLAY_LIST: 
537       if (p_vcd->pxd.pld == NULL) return false;
538       vcdplayer_update_entry( p_input, 
539                               vcdinf_pld_get_return_offset(p_vcd->pxd.pld), 
540                               &itemid.num, "return");
541       itemid.type = VCDINFO_ITEM_TYPE_LID;
542       break;
543       
544     case PSD_TYPE_END_LIST:
545     case PSD_TYPE_COMMAND_LIST:
546       LOG_WARN( "There is no PBC 'return' selection here" );
547       return false;
548     }
549   } else {
550
551     /* PBC is not on. "Return" selection is min_entry if possible. */
552   
553     p_vcd->play_item.num = (VCDINFO_ITEM_TYPE_ENTRY == p_vcd->play_item.type) 
554       ? 0 : 1;
555     
556   }
557
558   /** ??? p_vcd->update_title(); ***/
559   return VLC_SUCCESS == VCDPlay( p_input, itemid );
560
561 }