]> git.sesse.net Git - vlc/blob - modules/access/vcdx/vcdplayer.c
More segment playing improvements, be able to map seekpoints to
[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, 2004 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 VLC_TRUE if playback control (PBC) is on
52 */
53 vlc_bool_t 
54 vcdplayer_pbc_is_on( const vcdplayer_t *p_vcd ) 
55 {
56   return VCDINFO_INVALID_ENTRY != p_vcd->i_lid; 
57 }
58
59 /* Given an itemid, return the size for the object (via information
60    previously stored when opening the vcd). */
61 static size_t
62 vcdplayer_get_item_size(access_t * p_access, vcdinfo_itemid_t itemid) 
63 {
64   vcdplayer_t *p_vcd= (vcdplayer_t *)p_access->p_sys;
65
66   switch (itemid.type) {
67   case VCDINFO_ITEM_TYPE_ENTRY:
68     return p_vcd->entry[itemid.num].size;
69     break;
70   case VCDINFO_ITEM_TYPE_SEGMENT:
71     return p_vcd->segment[itemid.num].size;
72     break;
73   case VCDINFO_ITEM_TYPE_TRACK:
74     return p_vcd->track[itemid.num-1].size;
75     break;
76   case VCDINFO_ITEM_TYPE_LID:
77     /* Play list number (LID) */
78     return 0;
79     break;
80   case VCDINFO_ITEM_TYPE_NOTFOUND:
81   case VCDINFO_ITEM_TYPE_SPAREID2:
82   default:
83     LOG_ERR("%s %d\n", _("bad item type"), itemid.type);
84     return 0;
85   }
86 }
87
88 static void 
89 vcdplayer_update_entry( access_t * p_access, uint16_t ofs, 
90                         uint16_t *entry, const char *label)
91 {
92   vcdplayer_t *p_vcd= (vcdplayer_t *)p_access->p_sys;
93
94   if ( ofs == VCDINFO_INVALID_OFFSET ) {
95     *entry = VCDINFO_INVALID_ENTRY;
96   } else {
97     vcdinfo_offset_t *off = vcdinfo_get_offset_t(p_vcd->vcd, ofs);
98     if (off != NULL) {
99       *entry = off->lid;
100       dbg_print(INPUT_DBG_PBC, "%s: LID %d\n", label, off->lid);
101     } else
102       *entry = VCDINFO_INVALID_ENTRY;
103   }
104 }
105
106 /* Handles navigation when NOT in PBC reaching the end of a play item. 
107
108    The navigations rules here may be sort of made up, but the intent 
109    is to do something that's probably right or helpful.
110
111    return VLC_TRUE if the caller should return.
112 */
113 vcdplayer_read_status_t 
114 vcdplayer_non_pbc_nav ( access_t *p_access )
115 {
116   vcdplayer_t *p_vcd= (vcdplayer_t *)p_access->p_sys;
117
118   /* Not in playback control. Do we advance automatically or stop? */
119   switch (p_vcd->play_item.type) {
120   case VCDINFO_ITEM_TYPE_TRACK:
121   case VCDINFO_ITEM_TYPE_ENTRY: {
122
123     dbg_print( INPUT_DBG_LSN, "new track %d, lsn %d", p_vcd->i_track+1, 
124                vcdinfo_get_track_lsn(p_vcd->vcd, p_vcd->i_track+1) );
125     return READ_END;
126     break;
127   }
128   case VCDINFO_ITEM_TYPE_SPAREID2:  
129     dbg_print( (INPUT_DBG_STILL|INPUT_DBG_LSN), 
130                "SPAREID2" );
131     /* FIXME */
132     if (p_vcd->in_still)
133     {
134       return READ_STILL_FRAME ;
135     }
136     return READ_END;
137   case VCDINFO_ITEM_TYPE_NOTFOUND:  
138     LOG_ERR ("NOTFOUND outside PBC -- not supposed to happen");
139     return READ_ERROR;
140   case VCDINFO_ITEM_TYPE_LID:  
141     LOG_ERR ("LID outside PBC -- not supposed to happen");
142     return READ_ERROR;
143   case VCDINFO_ITEM_TYPE_SEGMENT:
144       /* Hack: Just go back and do still again */
145     /* FIXME */
146     if (p_vcd->in_still) 
147     {
148       dbg_print( (INPUT_DBG_STILL|INPUT_DBG_LSN), 
149                  "End of Segment - looping" );
150       return READ_STILL_FRAME;
151     }
152     return READ_END;
153   }
154   return READ_BLOCK;
155 }
156
157 #if 1
158 /*!
159   Set reading to play an entire track.
160 */
161 static void
162 _vcdplayer_set_track(access_t * p_access, track_t i_track) 
163 {
164   vcdplayer_t     *p_vcd = (vcdplayer_t *)p_access->p_sys;
165   if (i_track < 1 || i_track > p_vcd->i_tracks) 
166     return;
167   else {
168     vcdinfo_obj_t   *p_obj = p_vcd->vcd;
169     vcdinfo_itemid_t itemid;
170
171     itemid.num       = i_track;
172     itemid.type      = VCDINFO_ITEM_TYPE_TRACK;
173     p_vcd->in_still  = 0;
174     p_vcd->i_lsn     = vcdinfo_get_track_lsn(p_obj, i_track);
175     p_vcd->play_item = itemid;
176     p_vcd->i_track   = i_track;
177     p_vcd->track_lsn = p_vcd->i_lsn;
178
179     vcdplayer_set_origin(p_access);
180
181     dbg_print(INPUT_DBG_LSN, "LSN: %u\n", p_vcd->i_lsn);
182   }
183 }
184
185 /*!
186   Set reading to play an entry
187 */
188 static void
189 _vcdplayer_set_entry(access_t * p_access, unsigned int num) 
190 {
191   vcdplayer_t   *p_vcd = (vcdplayer_t *)p_access->p_sys;
192   vcdinfo_obj_t *p_obj = p_vcd->vcd;
193   unsigned int   num_entries = vcdinfo_get_num_entries(p_obj);
194
195   if (num >= num_entries) {
196     LOG_ERR("%s %d\n", _("bad entry number"), num);
197     return;
198   } else {
199     vcdinfo_itemid_t itemid;
200
201     itemid.num           = num;
202     itemid.type          = VCDINFO_ITEM_TYPE_ENTRY;
203     p_vcd->in_still      = 0;
204     p_vcd->i_lsn         = vcdinfo_get_entry_lsn(p_obj, num);
205     p_vcd->play_item     = itemid;
206     p_vcd->i_track       = vcdinfo_get_track(p_obj, num);
207     p_vcd->track_lsn     = vcdinfo_get_track_lsn(p_obj, p_vcd->i_track);
208     p_vcd->track_end_lsn = p_vcd->track_lsn + 
209       p_vcd->track[p_vcd->i_track-1].size;
210
211     vcdplayer_set_origin(p_access);
212
213     dbg_print(INPUT_DBG_LSN, "LSN: %u, track_end LSN: %u\n", 
214               p_vcd->i_lsn, p_vcd->track_end_lsn);
215   }
216 }
217
218 /*!
219   Set reading to play an segment (e.g. still frame)
220 */
221 static void
222 _vcdplayer_set_segment(access_t * p_access, unsigned int num) 
223 {
224   vcdplayer_t   *p_vcd = (vcdplayer_t *)p_access->p_sys;
225   vcdinfo_obj_t *p_obj = p_vcd->vcd;
226   segnum_t num_segs  = vcdinfo_get_num_segments(p_obj);
227
228   if (num >= num_segs) {
229     LOG_ERR("%s %d\n", _("bad segment number"), num);
230     return;
231   } else {
232     vcdinfo_itemid_t itemid;
233
234     p_vcd->i_lsn   = vcdinfo_get_seg_lsn(p_obj, num);
235     p_vcd->i_track = 0;
236
237     if (VCDINFO_NULL_LSN==p_vcd->i_lsn) {
238       LOG_ERR("%s %d\n", 
239               _("Error in getting current segment number"), num);
240       return;
241     }
242     
243     itemid.num = num;
244     itemid.type = VCDINFO_ITEM_TYPE_SEGMENT;
245     p_vcd->play_item = itemid;
246
247     vcdplayer_set_origin(p_access);
248     
249     dbg_print(INPUT_DBG_LSN, "LSN: %u\n", p_vcd->i_lsn);
250   }
251 }
252
253 /* Play entry. */
254 /* Play a single item. */
255 static void
256 vcdplayer_play_single_item( access_t * p_access, vcdinfo_itemid_t itemid)
257 {
258   vcdplayer_t   *p_vcd = (vcdplayer_t *)p_access->p_sys;
259   vcdinfo_obj_t *p_obj = p_vcd->vcd;
260
261   dbg_print(INPUT_DBG_CALL, "called itemid.num: %d, itemid.type: %d\n", 
262             itemid.num, itemid.type);
263
264   p_vcd->in_still = 0;
265
266   switch (itemid.type) {
267   case VCDINFO_ITEM_TYPE_SEGMENT: 
268     {
269       vcdinfo_video_segment_type_t segtype 
270         = vcdinfo_get_video_type(p_obj, itemid.num);
271       segnum_t num_segs = vcdinfo_get_num_segments(p_obj);
272
273       dbg_print(INPUT_DBG_PBC, "%s (%d), itemid.num: %d\n", 
274                 vcdinfo_video_type2str(p_obj, itemid.num), 
275                 (int) segtype, itemid.num);
276
277       if (itemid.num >= num_segs) return;
278       _vcdplayer_set_segment(p_access, itemid.num);
279       
280       switch (segtype)
281         {
282         case VCDINFO_FILES_VIDEO_NTSC_STILL:
283         case VCDINFO_FILES_VIDEO_NTSC_STILL2:
284         case VCDINFO_FILES_VIDEO_PAL_STILL:
285         case VCDINFO_FILES_VIDEO_PAL_STILL2:
286           p_vcd->in_still = -5;
287           break;
288         default:
289           p_vcd->in_still = 0;
290         }
291       
292       break;
293     }
294     
295   case VCDINFO_ITEM_TYPE_TRACK:
296     dbg_print(INPUT_DBG_PBC, "track %d\n", itemid.num);
297     if (itemid.num < 1 || itemid.num > p_vcd->i_tracks) return;
298     _vcdplayer_set_track(p_access, itemid.num);
299     break;
300     
301   case VCDINFO_ITEM_TYPE_ENTRY: 
302     {
303       unsigned int num_entries = vcdinfo_get_num_entries(p_obj);
304       dbg_print(INPUT_DBG_PBC, "entry %d\n", itemid.num);
305       if (itemid.num >= num_entries) return;
306       _vcdplayer_set_entry(p_access, itemid.num);
307       break;
308     }
309     
310   case VCDINFO_ITEM_TYPE_LID:
311     LOG_ERR("%s\n", _("Should have converted p_vcd above"));
312     break;
313
314   case VCDINFO_ITEM_TYPE_NOTFOUND:
315     dbg_print(INPUT_DBG_PBC, "play nothing\n");
316     p_vcd->i_lsn = p_vcd->end_lsn;
317     return;
318
319   default:
320     LOG_ERR("item type %d not implemented.\n", itemid.type);
321     return;
322   }
323   
324   p_vcd->play_item = itemid;
325
326   /* Some players like xine, have a fifo queue of audio and video buffers
327      that need to be flushed when playing a new selection. */
328   /*  if (p_vcd->flush_buffers)
329       p_vcd->flush_buffers(); */
330
331 }
332
333 #endif /* FINISHED */
334
335 /* 
336    Set's start origin and size for subsequent seeks.  
337    input: p_vcd->i_lsn, p_vcd->play_item
338    changed: p_vcd->origin_lsn, p_vcd->end_lsn
339 */
340
341 /* FIXME: add parameters lsn, i_track, p_itemid and set accordingly. */
342 void 
343 vcdplayer_set_origin(access_t *p_access)
344 {
345   vcdplayer_t *p_vcd = (vcdplayer_t *)p_access->p_sys;
346   size_t       i_size= vcdplayer_get_item_size(p_access, p_vcd->play_item);
347
348   p_vcd->end_lsn    = p_vcd->i_lsn + i_size;
349   p_vcd->origin_lsn = p_vcd->i_lsn;
350
351   dbg_print((INPUT_DBG_CALL|INPUT_DBG_LSN), "end LSN: %u\n", p_vcd->end_lsn);
352 }
353
354 /*
355   Get the next play-item in the list given in the LIDs. Note play-item
356   here refers to list of play-items for a single LID It shouldn't be
357   confused with a user's list of favorite things to play or the 
358   "next" field of a LID which moves us to a different LID.
359  */
360 static bool
361 _vcdplayer_inc_play_item(access_t *p_access)
362 {
363   vcdplayer_t *p_vcd = (vcdplayer_t *)p_access->p_sys;
364   int noi;
365
366   dbg_print(INPUT_DBG_CALL, "called pli: %d\n", p_vcd->pdi);
367
368   if ( NULL == p_vcd || NULL == p_vcd->pxd.pld  ) return false;
369
370   noi = vcdinf_pld_get_noi(p_vcd->pxd.pld);
371   
372   if ( noi <= 0 ) return false;
373   
374   /* Handle delays like autowait or wait here? */
375
376   p_vcd->pdi++;
377
378   if ( p_vcd->pdi < 0 || p_vcd->pdi >= noi ) return false;
379
380   else {
381     uint16_t trans_itemid_num=vcdinf_pld_get_play_item(p_vcd->pxd.pld, 
382                                                        p_vcd->pdi);
383     vcdinfo_itemid_t trans_itemid;
384
385     if (VCDINFO_INVALID_ITEMID == trans_itemid_num) return false;
386     
387     vcdinfo_classify_itemid(trans_itemid_num, &trans_itemid);
388     dbg_print(INPUT_DBG_PBC, "  play-item[%d]: %s\n",
389               p_vcd->pdi, vcdinfo_pin2str (trans_itemid_num));
390     vcdplayer_play_single_item(p_access, trans_itemid);
391     return true;
392   }
393 }
394
395 /* Handles PBC navigation when reaching the end of a play item. */
396 vcdplayer_read_status_t
397 vcdplayer_pbc_nav ( access_t * p_access )
398 {
399   vcdplayer_t *p_vcd= (vcdplayer_t *)p_access->p_sys;
400
401   /* We are in playback control. */
402   vcdinfo_itemid_t itemid;
403
404   /* The end of an entry is really the end of the associated 
405      sequence (or track). */
406   
407   if ( (VCDINFO_ITEM_TYPE_ENTRY == p_vcd->play_item.type) && 
408        (p_vcd->i_lsn < p_vcd->end_lsn) ) {
409     /* Set up to just continue to the next entry */
410     p_vcd->play_item.num++;
411     dbg_print( (INPUT_DBG_LSN|INPUT_DBG_PBC), 
412                "continuing into next entry: %u", p_vcd->play_item.num);
413     VCDPlay( p_access, p_vcd->play_item );
414     /* p_vcd->update_title(); */
415     return READ_BLOCK;
416   }
417   
418   switch (p_vcd->pxd.descriptor_type) {
419   case PSD_TYPE_END_LIST:
420     return READ_END;
421     break;
422   case PSD_TYPE_PLAY_LIST: {
423     int wait_time = vcdinf_get_wait_time(p_vcd->pxd.pld);
424     
425     dbg_print(INPUT_DBG_PBC, "playlist wait_time: %d", wait_time);
426     
427     if (vcdplayer_inc_play_item(p_access))
428       return READ_BLOCK;
429
430     /* Handle any wait time given. */
431 #if FIXED
432     if (p_vcd->in_still) {
433       vcdIntfStillTime( p_vcd->p_intf, wait_time );
434       return READ_STILL_FRAME;
435     }
436 #endif
437
438     vcdplayer_update_entry( p_access, 
439                             vcdinf_pld_get_next_offset(p_vcd->pxd.pld),
440                             &itemid.num, "next" );
441     itemid.type = VCDINFO_ITEM_TYPE_LID;
442     VCDPlay( p_access, itemid );
443     break;
444   }
445   case PSD_TYPE_SELECTION_LIST:     /* Selection List (+Ext. for SVCD) */
446   case PSD_TYPE_EXT_SELECTION_LIST: /* Extended Selection List (VCD2.0) */
447     {
448       int wait_time         = vcdinf_get_timeout_time(p_vcd->pxd.psd);
449       uint16_t timeout_offs = vcdinf_get_timeout_offset(p_vcd->pxd.psd);
450       uint16_t max_loop     = vcdinf_get_loop_count(p_vcd->pxd.psd);
451       vcdinfo_offset_t *offset_timeout_LID = 
452         vcdinfo_get_offset_t(p_vcd->vcd, timeout_offs);
453       
454       dbg_print(INPUT_DBG_PBC, "wait_time: %d, looped: %d, max_loop %d", 
455                 wait_time, p_vcd->loop_count, max_loop);
456       
457       /* Handle any wait time given */
458 #if FIXED
459       if (p_vcd->in_still) {
460         vcdIntfStillTime( p_vcd->p_intf, wait_time );
461         return READ_STILL_FRAME;
462       } 
463 #endif
464       
465       /* Handle any looping given. */
466       if ( max_loop == 0 || p_vcd->loop_count < max_loop ) {
467         p_vcd->loop_count++;
468         if (p_vcd->loop_count == 0x7f) p_vcd->loop_count = 0;
469         VCDSeek( p_access, 0 );
470         /* if (p_vcd->in_still) p_vcd->force_redisplay();*/
471         return READ_BLOCK;
472       }
473       
474       /* Looping finished and wait finished. Move to timeout
475          entry or next entry, or handle still. */
476       
477       if (NULL != offset_timeout_LID) {
478         /* Handle timeout_LID */
479         itemid.num  = offset_timeout_LID->lid;
480         itemid.type = VCDINFO_ITEM_TYPE_LID;
481         dbg_print(INPUT_DBG_PBC, "timeout to: %d", itemid.num);
482         VCDPlay( p_access, itemid );
483         return READ_BLOCK;
484       } else {
485         int num_selections = vcdinf_get_num_selections(p_vcd->pxd.psd);
486         if (num_selections > 0) {
487           /* Pick a random selection. */
488           unsigned int bsn=vcdinf_get_bsn(p_vcd->pxd.psd);
489           int rand_selection=bsn +
490             (int) ((num_selections+0.0)*rand()/(RAND_MAX+1.0));
491           lid_t rand_lid=vcdinfo_selection_get_lid (p_vcd->vcd, 
492                                                     p_vcd->i_lid, 
493                                                     rand_selection);
494           itemid.num = rand_lid;
495           itemid.type = VCDINFO_ITEM_TYPE_LID;
496           dbg_print(INPUT_DBG_PBC, "random selection %d, lid: %d", 
497                     rand_selection - bsn, rand_lid);
498           VCDPlay( p_access, itemid );
499           return READ_BLOCK;
500         } else if (p_vcd->in_still) {
501           /* Hack: Just go back and do still again */
502           sleep(1);
503           return READ_STILL_FRAME;
504         }
505       }
506       break;
507     }
508   case VCDINFO_ITEM_TYPE_NOTFOUND:  
509     LOG_ERR( "NOTFOUND in PBC -- not supposed to happen" );
510     break;
511   case VCDINFO_ITEM_TYPE_SPAREID2:  
512     LOG_ERR( "SPAREID2 in PBC -- not supposed to happen" );
513     break;
514   case VCDINFO_ITEM_TYPE_LID:  
515     LOG_ERR( "LID in PBC -- not supposed to happen" );
516     break;
517     
518   default:
519     ;
520   }
521   /* FIXME: Should handle autowait ...  */
522
523   return READ_ERROR;
524 }
525
526 /*!
527   Get the next play-item in the list given in the LIDs. Note play-item
528   here refers to list of play-items for a single LID It shouldn't be
529   confused with a user's list of favorite things to play or the 
530   "next" field of a LID which moves us to a different LID.
531  */
532 vlc_bool_t 
533 vcdplayer_inc_play_item( access_t *p_access )
534 {
535   vcdplayer_t *p_vcd= (vcdplayer_t *)p_access->p_sys;
536
537   int noi;
538
539   dbg_print(INPUT_DBG_CALL, "called pli: %d", p_vcd->pdi);
540
541   if ( NULL == p_vcd || NULL == p_vcd->pxd.pld  ) return VLC_FALSE;
542
543   noi = vcdinf_pld_get_noi(p_vcd->pxd.pld);
544   
545   if ( noi <= 0 ) return VLC_FALSE;
546   
547   /* Handle delays like autowait or wait here? */
548
549   p_vcd->pdi++;
550
551   if ( p_vcd->pdi < 0 || p_vcd->pdi >= noi ) return VLC_FALSE;
552
553   else {
554     uint16_t trans_itemid_num=vcdinf_pld_get_play_item(p_vcd->pxd.pld, 
555                                                        p_vcd->pdi);
556     vcdinfo_itemid_t trans_itemid;
557
558     if (VCDINFO_INVALID_ITEMID == trans_itemid_num) return VLC_FALSE;
559     
560     vcdinfo_classify_itemid(trans_itemid_num, &trans_itemid);
561     dbg_print(INPUT_DBG_PBC, "  play-item[%d]: %s",
562               p_vcd->pdi, vcdinfo_pin2str (trans_itemid_num));
563     return VLC_SUCCESS == VCDPlay( p_access, trans_itemid );
564   }
565 }
566
567 /*!
568   Play item assocated with the "default" selection.
569
570   Return VLC_FALSE if there was some problem.
571 */
572 vlc_bool_t 
573 vcdplayer_play_default( access_t * p_access )
574 {
575   vcdplayer_t *p_vcd= (vcdplayer_t *)p_access->p_sys;
576
577   vcdinfo_itemid_t itemid;
578
579   if (!p_vcd) {
580     dbg_print( (INPUT_DBG_CALL|INPUT_DBG_PBC), 
581                "null p_vcd" );
582     return VLC_EGENERIC;
583   }
584   
585
586   dbg_print( (INPUT_DBG_CALL|INPUT_DBG_PBC), 
587              "current: %d" , p_vcd->play_item.num);
588
589   itemid.type = p_vcd->play_item.type;
590
591   if  (vcdplayer_pbc_is_on(p_vcd)) {
592
593 #if defined(LIBVCD_VERSION)
594     lid_t lid=vcdinfo_get_multi_default_lid(p_vcd->vcd, p_vcd->i_lid,
595                                             p_vcd->i_lsn);
596
597     if (VCDINFO_INVALID_LID != lid) {
598       itemid.num  = lid;
599       itemid.type = VCDINFO_ITEM_TYPE_LID;
600       dbg_print(INPUT_DBG_PBC, "DEFAULT to %d\n", itemid.num);
601     } else {
602       dbg_print(INPUT_DBG_PBC, "no DEFAULT for LID %d\n", p_vcd->i_lid);
603     }
604
605 #else 
606     vcdinfo_lid_get_pxd(p_vcd->vcd, &(p_vcd->pxd), p_vcd->i_lid);
607     
608     switch (p_vcd->pxd.descriptor_type) {
609     case PSD_TYPE_SELECTION_LIST:
610     case PSD_TYPE_EXT_SELECTION_LIST:
611       if (p_vcd->pxd.psd == NULL) return VLC_FALSE;
612       vcdplayer_update_entry( p_access, 
613                               vcdinfo_get_default_offset(p_vcd->vcd, 
614                                                          p_vcd->i_lid), 
615                               &itemid.num, "default");
616       break;
617
618     case PSD_TYPE_PLAY_LIST: 
619     case PSD_TYPE_END_LIST:
620     case PSD_TYPE_COMMAND_LIST:
621       LOG_WARN( "There is no PBC 'default' selection here" );
622       return VLC_FALSE;
623     }
624 #endif /* LIBVCD_VERSION (< 0.7.21) */
625     
626
627   } else {
628
629     /* PBC is not on. "default" selection beginning of current 
630        selection . */
631   
632     p_vcd->play_item.num = p_vcd->play_item.num;
633     
634   }
635
636   /** ??? p_vcd->update_title(); ***/
637   return VLC_SUCCESS == VCDPlay( p_access, itemid );
638
639 }
640
641 /*!
642   Play item assocated with the "next" selection.
643
644   Return VLC_FALSE if there was some problem.
645 */
646 vlc_bool_t 
647 vcdplayer_play_next( access_t * p_access )
648 {
649   vcdplayer_t *p_vcd= (vcdplayer_t *)p_access->p_sys;
650
651   vcdinfo_obj_t     *p_obj;
652   vcdinfo_itemid_t   itemid;
653
654   if (!p_vcd) return VLC_FALSE;
655
656   dbg_print( (INPUT_DBG_CALL|INPUT_DBG_PBC), 
657              "current: %d" , p_vcd->play_item.num);
658
659   p_obj = p_vcd->vcd;
660
661   itemid.type = p_vcd->play_item.type;
662
663   if  (vcdplayer_pbc_is_on(p_vcd)) {
664
665     vcdinfo_lid_get_pxd(p_obj, &(p_vcd->pxd), p_vcd->i_lid);
666     
667     switch (p_vcd->pxd.descriptor_type) {
668     case PSD_TYPE_SELECTION_LIST:
669     case PSD_TYPE_EXT_SELECTION_LIST:
670       if (p_vcd->pxd.psd == NULL) return VLC_FALSE;
671       vcdplayer_update_entry( p_access, 
672                               vcdinf_psd_get_next_offset(p_vcd->pxd.psd), 
673                               &itemid.num, "next");
674       itemid.type = VCDINFO_ITEM_TYPE_LID;
675       break;
676
677     case PSD_TYPE_PLAY_LIST: 
678       if (p_vcd->pxd.pld == NULL) return VLC_FALSE;
679       vcdplayer_update_entry( p_access, 
680                               vcdinf_pld_get_next_offset(p_vcd->pxd.pld), 
681                               &itemid.num, "next");
682       itemid.type = VCDINFO_ITEM_TYPE_LID;
683       break;
684       
685     case PSD_TYPE_END_LIST:
686     case PSD_TYPE_COMMAND_LIST:
687       LOG_WARN( "There is no PBC 'next' selection here" );
688       return VLC_FALSE;
689     }
690   } else {
691
692     /* PBC is not on. "Next" selection is play_item.num+1 if possible. */
693   
694     int max_entry = 0;
695
696     switch (p_vcd->play_item.type) {
697     case VCDINFO_ITEM_TYPE_ENTRY: 
698     case VCDINFO_ITEM_TYPE_SEGMENT: 
699     case VCDINFO_ITEM_TYPE_TRACK: 
700       
701       switch (p_vcd->play_item.type) {
702       case VCDINFO_ITEM_TYPE_ENTRY: 
703         max_entry = p_vcd->i_entries;
704         break;
705       case VCDINFO_ITEM_TYPE_SEGMENT: 
706         max_entry = p_vcd->i_segments;
707         break;
708       case VCDINFO_ITEM_TYPE_TRACK: 
709         max_entry = p_vcd->i_tracks;
710         break;
711       default: ; /* Handle exceptional cases below */
712       }
713       
714       if (p_vcd->play_item.num+1 < max_entry) {
715         itemid.num = p_vcd->play_item.num+1;
716       } else {
717         LOG_WARN( "At the end - non-PBC 'next' not possible here" );
718         return VLC_FALSE;
719       }
720       
721       break;
722       
723     case VCDINFO_ITEM_TYPE_LID: 
724       {
725         /* Should have handled above. */
726         LOG_WARN( "Internal inconsistency - should not have gotten here." );
727         return VLC_FALSE;
728       }
729     default: 
730       return VLC_FALSE;
731     }
732   }
733
734   /** ??? p_vcd->update_title(); ***/
735   return VLC_SUCCESS == VCDPlay( p_access, itemid );
736
737 }
738
739 /*!
740   Play item assocated with the "prev" selection.
741
742   Return VLC_FALSE if there was some problem.
743 */
744 vlc_bool_t 
745 vcdplayer_play_prev( access_t * p_access )
746 {
747   vcdplayer_t      *p_vcd= (vcdplayer_t *)p_access->p_sys;
748   vcdinfo_obj_t    *p_obj  = p_vcd->vcd;
749   vcdinfo_itemid_t  itemid;
750
751   dbg_print( (INPUT_DBG_CALL|INPUT_DBG_PBC), 
752              "current: %d" , p_vcd->play_item.num);
753
754   itemid.type = p_vcd->play_item.type;
755
756   if  (vcdplayer_pbc_is_on(p_vcd)) {
757
758     vcdinfo_lid_get_pxd(p_obj, &(p_vcd->pxd), p_vcd->i_lid);
759     
760     switch (p_vcd->pxd.descriptor_type) {
761     case PSD_TYPE_SELECTION_LIST:
762     case PSD_TYPE_EXT_SELECTION_LIST:
763       if (p_vcd->pxd.psd == NULL) return VLC_FALSE;
764       vcdplayer_update_entry( p_access, 
765                               vcdinf_psd_get_prev_offset(p_vcd->pxd.psd), 
766                               &itemid.num, "prev");
767       itemid.type = VCDINFO_ITEM_TYPE_LID;
768       break;
769
770     case PSD_TYPE_PLAY_LIST: 
771       if (p_vcd->pxd.pld == NULL) return VLC_FALSE;
772       vcdplayer_update_entry( p_access, 
773                               vcdinf_pld_get_prev_offset(p_vcd->pxd.pld), 
774                               &itemid.num, "prev");
775       itemid.type = VCDINFO_ITEM_TYPE_LID;
776       break;
777       
778     case PSD_TYPE_END_LIST:
779     case PSD_TYPE_COMMAND_LIST:
780       LOG_WARN( "There is no PBC 'prev' selection here" );
781       return VLC_FALSE;
782     }
783   } else {
784
785     /* PBC is not on. "Prev" selection is play_item.num-1 if possible. */
786   
787     int min_entry = (VCDINFO_ITEM_TYPE_ENTRY == p_vcd->play_item.type) 
788       ? 0 : 1;
789     
790     if (p_vcd->play_item.num > min_entry) {
791       itemid.num = p_vcd->play_item.num-1;
792     } else {
793       LOG_WARN( "At the beginning - non-PBC 'prev' not possible here" );
794       return VLC_FALSE;
795     }
796       
797   }
798
799   /** ??? p_vcd->update_title(); ***/
800   return VLC_SUCCESS == VCDPlay( p_access, itemid );
801
802 }
803
804 /*!
805   Play item assocated with the "return" selection.
806
807   Return VLC_FALSE if there was some problem.
808 */
809 vlc_bool_t 
810 vcdplayer_play_return( access_t * p_access )
811 {
812   vcdplayer_t      *p_vcd= (vcdplayer_t *)p_access->p_sys;
813   vcdinfo_obj_t    *p_obj  = p_vcd->vcd;
814   vcdinfo_itemid_t  itemid;
815
816   dbg_print( (INPUT_DBG_CALL|INPUT_DBG_PBC), 
817              "current: %d" , p_vcd->play_item.num);
818
819   itemid.type = p_vcd->play_item.type;
820
821   if  (vcdplayer_pbc_is_on(p_vcd)) {
822
823     vcdinfo_lid_get_pxd(p_obj, &(p_vcd->pxd), p_vcd->i_lid);
824     
825     switch (p_vcd->pxd.descriptor_type) {
826     case PSD_TYPE_SELECTION_LIST:
827     case PSD_TYPE_EXT_SELECTION_LIST:
828       if (p_vcd->pxd.psd == NULL) return VLC_FALSE;
829       vcdplayer_update_entry( p_access, 
830                               vcdinf_psd_get_return_offset(p_vcd->pxd.psd), 
831                               &itemid.num, "return");
832       itemid.type = VCDINFO_ITEM_TYPE_LID;
833       break;
834
835     case PSD_TYPE_PLAY_LIST: 
836       if (p_vcd->pxd.pld == NULL) return VLC_FALSE;
837       vcdplayer_update_entry( p_access, 
838                               vcdinf_pld_get_return_offset(p_vcd->pxd.pld), 
839                               &itemid.num, "return");
840       itemid.type = VCDINFO_ITEM_TYPE_LID;
841       break;
842       
843     case PSD_TYPE_END_LIST:
844     case PSD_TYPE_COMMAND_LIST:
845       LOG_WARN( "There is no PBC 'return' selection here" );
846       return VLC_FALSE;
847     }
848   } else {
849
850     /* PBC is not on. "Return" selection is min_entry if possible. */
851   
852     p_vcd->play_item.num = (VCDINFO_ITEM_TYPE_ENTRY == p_vcd->play_item.type) 
853       ? 0 : 1;
854     
855   }
856
857   /** ??? p_vcd->update_title(); ***/
858   return VLC_SUCCESS == VCDPlay( p_access, itemid );
859
860 }