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