]> git.sesse.net Git - vlc/blob - modules/access/vcdx/vcdplayer.c
Work on getting slider seeking working better.
[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 FINISHED
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 #endif /* FINISHED */
253
254 /* 
255    Set's start origin and size for subsequent seeks.  
256    input: p_vcd->i_lsn, p_vcd->play_item
257    changed: p_vcd->origin_lsn, p_vcd->end_lsn
258 */
259 void 
260 vcdplayer_set_origin(access_t *p_access)
261 {
262   vcdplayer_t *p_vcd = (vcdplayer_t *)p_access->p_sys;
263   size_t       i_size= vcdplayer_get_item_size(p_access, p_vcd->play_item);
264
265   p_vcd->end_lsn    = p_vcd->i_lsn + i_size;
266   p_vcd->origin_lsn = p_vcd->i_lsn;
267
268   dbg_print((INPUT_DBG_CALL|INPUT_DBG_LSN), "end LSN: %u\n", p_vcd->end_lsn);
269 }
270
271 /* Handles PBC navigation when reaching the end of a play item. */
272 vcdplayer_read_status_t
273 vcdplayer_pbc_nav ( access_t * p_access )
274 {
275   vcdplayer_t *p_vcd= (vcdplayer_t *)p_access->p_sys;
276
277   /* We are in playback control. */
278   vcdinfo_itemid_t itemid;
279
280   /* The end of an entry is really the end of the associated 
281      sequence (or track). */
282   
283   if ( (VCDINFO_ITEM_TYPE_ENTRY == p_vcd->play_item.type) && 
284        (p_vcd->i_lsn < p_vcd->end_lsn) ) {
285     /* Set up to just continue to the next entry */
286     p_vcd->play_item.num++;
287     dbg_print( (INPUT_DBG_LSN|INPUT_DBG_PBC), 
288                "continuing into next entry: %u", p_vcd->play_item.num);
289     VCDPlay( p_access, p_vcd->play_item );
290     /* p_vcd->update_title(); */
291     return READ_BLOCK;
292   }
293   
294   switch (p_vcd->pxd.descriptor_type) {
295   case PSD_TYPE_END_LIST:
296     return READ_END;
297     break;
298   case PSD_TYPE_PLAY_LIST: {
299     int wait_time = vcdinf_get_wait_time(p_vcd->pxd.pld);
300     
301     dbg_print(INPUT_DBG_PBC, "playlist wait_time: %d", wait_time);
302     
303     if (vcdplayer_inc_play_item(p_access))
304       return READ_BLOCK;
305
306     /* Handle any wait time given. */
307 #if FIXED
308     if (p_vcd->in_still) {
309       vcdIntfStillTime( p_vcd->p_intf, wait_time );
310       return READ_STILL_FRAME;
311     }
312 #endif
313
314     vcdplayer_update_entry( p_access, 
315                             vcdinf_pld_get_next_offset(p_vcd->pxd.pld),
316                             &itemid.num, "next" );
317     itemid.type = VCDINFO_ITEM_TYPE_LID;
318     VCDPlay( p_access, itemid );
319     break;
320   }
321   case PSD_TYPE_SELECTION_LIST:     /* Selection List (+Ext. for SVCD) */
322   case PSD_TYPE_EXT_SELECTION_LIST: /* Extended Selection List (VCD2.0) */
323     {
324       int wait_time         = vcdinf_get_timeout_time(p_vcd->pxd.psd);
325       uint16_t timeout_offs = vcdinf_get_timeout_offset(p_vcd->pxd.psd);
326       uint16_t max_loop     = vcdinf_get_loop_count(p_vcd->pxd.psd);
327       vcdinfo_offset_t *offset_timeout_LID = 
328         vcdinfo_get_offset_t(p_vcd->vcd, timeout_offs);
329       
330       dbg_print(INPUT_DBG_PBC, "wait_time: %d, looped: %d, max_loop %d", 
331                 wait_time, p_vcd->loop_count, max_loop);
332       
333       /* Handle any wait time given */
334 #if FIXED
335       if (p_vcd->in_still) {
336         vcdIntfStillTime( p_vcd->p_intf, wait_time );
337         return READ_STILL_FRAME;
338       } 
339 #endif
340       
341       /* Handle any looping given. */
342       if ( max_loop == 0 || p_vcd->loop_count < max_loop ) {
343         p_vcd->loop_count++;
344         if (p_vcd->loop_count == 0x7f) p_vcd->loop_count = 0;
345         VCDSeek( p_access, 0 );
346         /* if (p_vcd->in_still) p_vcd->force_redisplay();*/
347         return READ_BLOCK;
348       }
349       
350       /* Looping finished and wait finished. Move to timeout
351          entry or next entry, or handle still. */
352       
353       if (NULL != offset_timeout_LID) {
354         /* Handle timeout_LID */
355         itemid.num  = offset_timeout_LID->lid;
356         itemid.type = VCDINFO_ITEM_TYPE_LID;
357         dbg_print(INPUT_DBG_PBC, "timeout to: %d", itemid.num);
358         VCDPlay( p_access, itemid );
359         return READ_BLOCK;
360       } else {
361         int num_selections = vcdinf_get_num_selections(p_vcd->pxd.psd);
362         if (num_selections > 0) {
363           /* Pick a random selection. */
364           unsigned int bsn=vcdinf_get_bsn(p_vcd->pxd.psd);
365           int rand_selection=bsn +
366             (int) ((num_selections+0.0)*rand()/(RAND_MAX+1.0));
367           lid_t rand_lid=vcdinfo_selection_get_lid (p_vcd->vcd, 
368                                                     p_vcd->i_lid, 
369                                                     rand_selection);
370           itemid.num = rand_lid;
371           itemid.type = VCDINFO_ITEM_TYPE_LID;
372           dbg_print(INPUT_DBG_PBC, "random selection %d, lid: %d", 
373                     rand_selection - bsn, rand_lid);
374           VCDPlay( p_access, itemid );
375           return READ_BLOCK;
376         } else if (p_vcd->in_still) {
377           /* Hack: Just go back and do still again */
378           sleep(1);
379           return READ_STILL_FRAME;
380         }
381       }
382       break;
383     }
384   case VCDINFO_ITEM_TYPE_NOTFOUND:  
385     LOG_ERR( "NOTFOUND in PBC -- not supposed to happen" );
386     break;
387   case VCDINFO_ITEM_TYPE_SPAREID2:  
388     LOG_ERR( "SPAREID2 in PBC -- not supposed to happen" );
389     break;
390   case VCDINFO_ITEM_TYPE_LID:  
391     LOG_ERR( "LID in PBC -- not supposed to happen" );
392     break;
393     
394   default:
395     ;
396   }
397   /* FIXME: Should handle autowait ...  */
398
399   return READ_ERROR;
400 }
401
402 /*!
403   Get the next play-item in the list given in the LIDs. Note play-item
404   here refers to list of play-items for a single LID It shouldn't be
405   confused with a user's list of favorite things to play or the 
406   "next" field of a LID which moves us to a different LID.
407  */
408 vlc_bool_t 
409 vcdplayer_inc_play_item( access_t *p_access )
410 {
411   vcdplayer_t *p_vcd= (vcdplayer_t *)p_access->p_sys;
412
413   int noi;
414
415   dbg_print(INPUT_DBG_CALL, "called pli: %d", p_vcd->pdi);
416
417   if ( NULL == p_vcd || NULL == p_vcd->pxd.pld  ) return VLC_FALSE;
418
419   noi = vcdinf_pld_get_noi(p_vcd->pxd.pld);
420   
421   if ( noi <= 0 ) return VLC_FALSE;
422   
423   /* Handle delays like autowait or wait here? */
424
425   p_vcd->pdi++;
426
427   if ( p_vcd->pdi < 0 || p_vcd->pdi >= noi ) return VLC_FALSE;
428
429   else {
430     uint16_t trans_itemid_num=vcdinf_pld_get_play_item(p_vcd->pxd.pld, 
431                                                        p_vcd->pdi);
432     vcdinfo_itemid_t trans_itemid;
433
434     if (VCDINFO_INVALID_ITEMID == trans_itemid_num) return VLC_FALSE;
435     
436     vcdinfo_classify_itemid(trans_itemid_num, &trans_itemid);
437     dbg_print(INPUT_DBG_PBC, "  play-item[%d]: %s",
438               p_vcd->pdi, vcdinfo_pin2str (trans_itemid_num));
439     return VLC_SUCCESS == VCDPlay( p_access, trans_itemid );
440   }
441 }
442
443 /*!
444   Play item assocated with the "default" selection.
445
446   Return VLC_FALSE if there was some problem.
447 */
448 vlc_bool_t 
449 vcdplayer_play_default( access_t * p_access )
450 {
451   vcdplayer_t *p_vcd= (vcdplayer_t *)p_access->p_sys;
452
453   vcdinfo_itemid_t   itemid;
454
455   if (!p_vcd) {
456     dbg_print( (INPUT_DBG_CALL|INPUT_DBG_PBC), 
457                "null p_vcd" );
458     return VLC_EGENERIC;
459   }
460   
461
462   dbg_print( (INPUT_DBG_CALL|INPUT_DBG_PBC), 
463              "current: %d" , p_vcd->play_item.num);
464
465   itemid.type = p_vcd->play_item.type;
466
467   if  (vcdplayer_pbc_is_on(p_vcd)) {
468
469 #if defined(LIBVCD_VERSION)
470     lid_t lid=vcdinfo_get_multi_default_lid(p_vcd->vcd, p_vcd->i_lid,
471                                             p_vcd->i_lsn);
472
473     if (VCDINFO_INVALID_LID != lid) {
474       itemid.num  = lid;
475       itemid.type = VCDINFO_ITEM_TYPE_LID;
476       dbg_print(INPUT_DBG_PBC, "DEFAULT to %d\n", itemid.num);
477     } else {
478       dbg_print(INPUT_DBG_PBC, "no DEFAULT for LID %d\n", p_vcd->i_lid);
479     }
480
481 #else 
482     vcdinfo_lid_get_pxd(p_vcd->vcd, &(p_vcd->pxd), p_vcd->i_lid);
483     
484     switch (p_vcd->pxd.descriptor_type) {
485     case PSD_TYPE_SELECTION_LIST:
486     case PSD_TYPE_EXT_SELECTION_LIST:
487       if (p_vcd->pxd.psd == NULL) return VLC_FALSE;
488       vcdplayer_update_entry( p_access, 
489                               vcdinfo_get_default_offset(p_vcd->vcd, 
490                                                          p_vcd->i_lid), 
491                               &itemid.num, "default");
492       break;
493
494     case PSD_TYPE_PLAY_LIST: 
495     case PSD_TYPE_END_LIST:
496     case PSD_TYPE_COMMAND_LIST:
497       LOG_WARN( "There is no PBC 'default' selection here" );
498       return VLC_FALSE;
499     }
500 #endif /* LIBVCD_VERSION (< 0.7.21) */
501     
502
503   } else {
504
505     /* PBC is not on. "default" selection beginning of current 
506        selection . */
507   
508     p_vcd->play_item.num = p_vcd->play_item.num;
509     
510   }
511
512   /** ??? p_vcd->update_title(); ***/
513   return VLC_SUCCESS == VCDPlay( p_access, itemid );
514
515 }
516
517 /*!
518   Play item assocated with the "next" selection.
519
520   Return VLC_FALSE if there was some problem.
521 */
522 vlc_bool_t 
523 vcdplayer_play_next( access_t * p_access )
524 {
525   vcdplayer_t *p_vcd= (vcdplayer_t *)p_access->p_sys;
526
527   vcdinfo_obj_t     *p_obj;
528   vcdinfo_itemid_t   itemid;
529
530   if (!p_vcd) return VLC_FALSE;
531
532   dbg_print( (INPUT_DBG_CALL|INPUT_DBG_PBC), 
533              "current: %d" , p_vcd->play_item.num);
534
535   p_obj = p_vcd->vcd;
536
537   itemid.type = p_vcd->play_item.type;
538
539   if  (vcdplayer_pbc_is_on(p_vcd)) {
540
541     vcdinfo_lid_get_pxd(p_obj, &(p_vcd->pxd), p_vcd->i_lid);
542     
543     switch (p_vcd->pxd.descriptor_type) {
544     case PSD_TYPE_SELECTION_LIST:
545     case PSD_TYPE_EXT_SELECTION_LIST:
546       if (p_vcd->pxd.psd == NULL) return VLC_FALSE;
547       vcdplayer_update_entry( p_access, 
548                               vcdinf_psd_get_next_offset(p_vcd->pxd.psd), 
549                               &itemid.num, "next");
550       itemid.type = VCDINFO_ITEM_TYPE_LID;
551       break;
552
553     case PSD_TYPE_PLAY_LIST: 
554       if (p_vcd->pxd.pld == NULL) return VLC_FALSE;
555       vcdplayer_update_entry( p_access, 
556                               vcdinf_pld_get_next_offset(p_vcd->pxd.pld), 
557                               &itemid.num, "next");
558       itemid.type = VCDINFO_ITEM_TYPE_LID;
559       break;
560       
561     case PSD_TYPE_END_LIST:
562     case PSD_TYPE_COMMAND_LIST:
563       LOG_WARN( "There is no PBC 'next' selection here" );
564       return VLC_FALSE;
565     }
566   } else {
567
568     /* PBC is not on. "Next" selection is play_item.num+1 if possible. */
569   
570     int max_entry = 0;
571
572     switch (p_vcd->play_item.type) {
573     case VCDINFO_ITEM_TYPE_ENTRY: 
574     case VCDINFO_ITEM_TYPE_SEGMENT: 
575     case VCDINFO_ITEM_TYPE_TRACK: 
576       
577       switch (p_vcd->play_item.type) {
578       case VCDINFO_ITEM_TYPE_ENTRY: 
579         max_entry = p_vcd->i_entries;
580         break;
581       case VCDINFO_ITEM_TYPE_SEGMENT: 
582         max_entry = p_vcd->i_segments;
583         break;
584       case VCDINFO_ITEM_TYPE_TRACK: 
585         max_entry = p_vcd->i_tracks;
586         break;
587       default: ; /* Handle exceptional cases below */
588       }
589       
590       if (p_vcd->play_item.num+1 < max_entry) {
591         itemid.num = p_vcd->play_item.num+1;
592       } else {
593         LOG_WARN( "At the end - non-PBC 'next' not possible here" );
594         return VLC_FALSE;
595       }
596       
597       break;
598       
599     case VCDINFO_ITEM_TYPE_LID: 
600       {
601         /* Should have handled above. */
602         LOG_WARN( "Internal inconsistency - should not have gotten here." );
603         return VLC_FALSE;
604       }
605     default: 
606       return VLC_FALSE;
607     }
608   }
609
610   /** ??? p_vcd->update_title(); ***/
611   return VLC_SUCCESS == VCDPlay( p_access, itemid );
612
613 }
614
615 /*!
616   Play item assocated with the "prev" selection.
617
618   Return VLC_FALSE if there was some problem.
619 */
620 vlc_bool_t 
621 vcdplayer_play_prev( access_t * p_access )
622 {
623   vcdplayer_t      *p_vcd= (vcdplayer_t *)p_access->p_sys;
624   vcdinfo_obj_t    *p_obj  = p_vcd->vcd;
625   vcdinfo_itemid_t  itemid;
626
627   dbg_print( (INPUT_DBG_CALL|INPUT_DBG_PBC), 
628              "current: %d" , p_vcd->play_item.num);
629
630   itemid.type = p_vcd->play_item.type;
631
632   if  (vcdplayer_pbc_is_on(p_vcd)) {
633
634     vcdinfo_lid_get_pxd(p_obj, &(p_vcd->pxd), p_vcd->i_lid);
635     
636     switch (p_vcd->pxd.descriptor_type) {
637     case PSD_TYPE_SELECTION_LIST:
638     case PSD_TYPE_EXT_SELECTION_LIST:
639       if (p_vcd->pxd.psd == NULL) return VLC_FALSE;
640       vcdplayer_update_entry( p_access, 
641                               vcdinf_psd_get_prev_offset(p_vcd->pxd.psd), 
642                               &itemid.num, "prev");
643       itemid.type = VCDINFO_ITEM_TYPE_LID;
644       break;
645
646     case PSD_TYPE_PLAY_LIST: 
647       if (p_vcd->pxd.pld == NULL) return VLC_FALSE;
648       vcdplayer_update_entry( p_access, 
649                               vcdinf_pld_get_prev_offset(p_vcd->pxd.pld), 
650                               &itemid.num, "prev");
651       itemid.type = VCDINFO_ITEM_TYPE_LID;
652       break;
653       
654     case PSD_TYPE_END_LIST:
655     case PSD_TYPE_COMMAND_LIST:
656       LOG_WARN( "There is no PBC 'prev' selection here" );
657       return VLC_FALSE;
658     }
659   } else {
660
661     /* PBC is not on. "Prev" selection is play_item.num-1 if possible. */
662   
663     int min_entry = (VCDINFO_ITEM_TYPE_ENTRY == p_vcd->play_item.type) 
664       ? 0 : 1;
665     
666     if (p_vcd->play_item.num > min_entry) {
667       itemid.num = p_vcd->play_item.num-1;
668     } else {
669       LOG_WARN( "At the beginning - non-PBC 'prev' not possible here" );
670       return VLC_FALSE;
671     }
672       
673   }
674
675   /** ??? p_vcd->update_title(); ***/
676   return VLC_SUCCESS == VCDPlay( p_access, itemid );
677
678 }
679
680 /*!
681   Play item assocated with the "return" selection.
682
683   Return VLC_FALSE if there was some problem.
684 */
685 vlc_bool_t 
686 vcdplayer_play_return( access_t * p_access )
687 {
688   vcdplayer_t      *p_vcd= (vcdplayer_t *)p_access->p_sys;
689   vcdinfo_obj_t    *p_obj  = p_vcd->vcd;
690   vcdinfo_itemid_t  itemid;
691
692   dbg_print( (INPUT_DBG_CALL|INPUT_DBG_PBC), 
693              "current: %d" , p_vcd->play_item.num);
694
695   itemid.type = p_vcd->play_item.type;
696
697   if  (vcdplayer_pbc_is_on(p_vcd)) {
698
699     vcdinfo_lid_get_pxd(p_obj, &(p_vcd->pxd), p_vcd->i_lid);
700     
701     switch (p_vcd->pxd.descriptor_type) {
702     case PSD_TYPE_SELECTION_LIST:
703     case PSD_TYPE_EXT_SELECTION_LIST:
704       if (p_vcd->pxd.psd == NULL) return VLC_FALSE;
705       vcdplayer_update_entry( p_access, 
706                               vcdinf_psd_get_return_offset(p_vcd->pxd.psd), 
707                               &itemid.num, "return");
708       itemid.type = VCDINFO_ITEM_TYPE_LID;
709       break;
710
711     case PSD_TYPE_PLAY_LIST: 
712       if (p_vcd->pxd.pld == NULL) return VLC_FALSE;
713       vcdplayer_update_entry( p_access, 
714                               vcdinf_pld_get_return_offset(p_vcd->pxd.pld), 
715                               &itemid.num, "return");
716       itemid.type = VCDINFO_ITEM_TYPE_LID;
717       break;
718       
719     case PSD_TYPE_END_LIST:
720     case PSD_TYPE_COMMAND_LIST:
721       LOG_WARN( "There is no PBC 'return' selection here" );
722       return VLC_FALSE;
723     }
724   } else {
725
726     /* PBC is not on. "Return" selection is min_entry if possible. */
727   
728     p_vcd->play_item.num = (VCDINFO_ITEM_TYPE_ENTRY == p_vcd->play_item.type) 
729       ? 0 : 1;
730     
731   }
732
733   /** ??? p_vcd->update_title(); ***/
734   return VLC_SUCCESS == VCDPlay( p_access, itemid );
735
736 }