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