]> git.sesse.net Git - vlc/blob - modules/access/vcdx/vcdplayer.c
configure.ac: reinstate libcdio CD-DA and VCD plugins (on demand)
[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     const vcdinfo_obj_t *p_vcdinfo = 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_vcdinfo, 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   const unsigned int   i_entries = vcdinfo_get_num_entries(p_vcdinfo);
196
197   if (num >= i_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->i_still  = 0;
206
207     VCDSetOrigin(p_access, vcdinfo_get_entry_lsn(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->i_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->i_still = STILL_READING;
280           break;
281         default:
282           p_vcdplayer->i_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 i_entries = vcdinfo_get_num_entries(p_vcdinfo);
297       dbg_print(INPUT_DBG_PBC, "entry %d", itemid.num);
298       if (itemid.num >= i_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_lsn(p_vcdplayer->vcd, 
348                                                       i_track);
349
350   dbg_print((INPUT_DBG_CALL|INPUT_DBG_LSN), 
351             "lsn %u, end LSN: %u item.num %d, item.type %d", 
352             p_vcdplayer->i_lsn, p_vcdplayer->end_lsn,
353             p_vcdplayer->play_item.num, p_vcdplayer->play_item.type);
354 }
355
356 /*!
357   Get the next play-item in the list given in the LIDs. Note play-item
358   here refers to list of play-items for a single LID It shouldn't be
359   confused with a user's list of favorite things to play or the 
360   "next" field of a LID which moves us to a different LID.
361  */
362 static bool
363 vcdplayer_inc_play_item(access_t *p_access)
364 {
365
366   vcdplayer_t *p_vcdplayer = (vcdplayer_t *)p_access->p_sys;
367   int noi;
368
369   dbg_print(INPUT_DBG_CALL, "called pli: %d", p_vcdplayer->pdi);
370
371   if ( NULL == p_vcdplayer || NULL == p_vcdplayer->pxd.pld  ) return false;
372
373   noi = vcdinf_pld_get_noi(p_vcdplayer->pxd.pld);
374   
375   if ( noi <= 0 ) return false;
376   
377   /* Handle delays like autowait or wait here? */
378
379   p_vcdplayer->pdi++;
380
381   if ( p_vcdplayer->pdi < 0 || p_vcdplayer->pdi >= noi ) return false;
382
383   else {
384     uint16_t trans_itemid_num=vcdinf_pld_get_play_item(p_vcdplayer->pxd.pld, 
385                                                        p_vcdplayer->pdi);
386     vcdinfo_itemid_t trans_itemid;
387
388     if (VCDINFO_INVALID_ITEMID == trans_itemid_num) return false;
389     
390     vcdinfo_classify_itemid(trans_itemid_num, &trans_itemid);
391     dbg_print(INPUT_DBG_PBC, "  play-item[%d]: %s",
392               p_vcdplayer->pdi, vcdinfo_pin2str (trans_itemid_num));
393     return vcdplayer_play_single_item(p_access, trans_itemid);
394   }
395 }
396
397 void
398 vcdplayer_play(access_t *p_access, vcdinfo_itemid_t itemid)
399 {
400   vcdplayer_t *p_vcdplayer = (vcdplayer_t *)p_access->p_sys;
401
402   dbg_print(INPUT_DBG_CALL, "called itemid.num: %d itemid.type: %d", 
403             itemid.num, itemid.type);
404
405   if  (!vcdplayer_pbc_is_on(p_vcdplayer)) {
406     vcdplayer_play_single_item(p_access, itemid);
407   } else {
408     /* PBC on - Itemid.num is LID. */
409
410     vcdinfo_obj_t *p_vcdinfo = p_vcdplayer->vcd;
411
412     if (p_vcdinfo == NULL) return;
413
414     p_vcdplayer->i_lid = itemid.num;
415     vcdinfo_lid_get_pxd(p_vcdinfo, &(p_vcdplayer->pxd), itemid.num);
416     
417     switch (p_vcdplayer->pxd.descriptor_type) {
418       
419     case PSD_TYPE_SELECTION_LIST:
420     case PSD_TYPE_EXT_SELECTION_LIST: {
421       vcdinfo_itemid_t trans_itemid;
422       uint16_t trans_itemid_num;
423
424       if (p_vcdplayer->pxd.psd == NULL) return;
425       trans_itemid_num  = vcdinf_psd_get_itemid(p_vcdplayer->pxd.psd);
426       vcdinfo_classify_itemid(trans_itemid_num, &trans_itemid);
427       p_vcdplayer->i_loop     = 1;
428       p_vcdplayer->loop_item  = trans_itemid;
429       vcdplayer_play_single_item(p_access, trans_itemid);
430       break;
431     }
432       
433     case PSD_TYPE_PLAY_LIST: {
434       if (p_vcdplayer->pxd.pld == NULL) return;
435       p_vcdplayer->pdi = -1;
436       vcdplayer_inc_play_item(p_access);
437       break;
438     }
439       
440     case PSD_TYPE_END_LIST:
441     case PSD_TYPE_COMMAND_LIST:
442       
443     default:
444       ;
445     }
446   }
447 }
448
449 /* Handles PBC navigation when reaching the end of a play item. */
450 vcdplayer_read_status_t
451 vcdplayer_pbc_nav ( access_t * p_access, uint8_t *wait_time )
452 {
453   vcdplayer_t *p_vcdplayer= (vcdplayer_t *)p_access->p_sys;
454
455   /* We are in playback control. */
456   vcdinfo_itemid_t itemid;
457
458   /* The end of an entry is really the end of the associated 
459      sequence (or track). */
460   
461   if ( (VCDINFO_ITEM_TYPE_ENTRY == p_vcdplayer->play_item.type) && 
462        (p_vcdplayer->i_lsn < p_vcdplayer->end_lsn) ) {
463     /* Set up to just continue to the next entry */
464     p_vcdplayer->play_item.num++;
465     dbg_print( (INPUT_DBG_LSN|INPUT_DBG_PBC), 
466                "continuing into next entry: %u", p_vcdplayer->play_item.num);
467     vcdplayer_play_single_item( p_access, p_vcdplayer->play_item );
468     /* p_vcdplayer->update_title(); */
469     return READ_BLOCK;
470   }
471   
472   switch (p_vcdplayer->pxd.descriptor_type) {
473   case PSD_TYPE_END_LIST:
474     return READ_END;
475     break;
476   case PSD_TYPE_PLAY_LIST: {
477     if (vcdplayer_inc_play_item(p_access))
478       return READ_BLOCK;
479
480     /* Set up for caller process wait time given. */
481     if (p_vcdplayer->i_still) {
482       *wait_time = vcdinf_get_wait_time(p_vcdplayer->pxd.pld);
483       dbg_print((INPUT_DBG_PBC|INPUT_DBG_STILL), 
484                 "playlist wait time: %d", *wait_time);
485       return READ_STILL_FRAME;
486     }
487
488     /* Wait time has been processed; continue with next entry. */
489     vcdplayer_update_entry( p_access, 
490                             vcdinf_pld_get_next_offset(p_vcdplayer->pxd.pld),
491                             &itemid.num, "next" );
492     itemid.type = VCDINFO_ITEM_TYPE_LID;
493     vcdplayer_play( p_access, itemid );
494     break;
495   }
496   case PSD_TYPE_SELECTION_LIST:     /* Selection List (+Ext. for SVCD) */
497   case PSD_TYPE_EXT_SELECTION_LIST: /* Extended Selection List (VCD2.0) */
498     {
499       uint16_t timeout_offs = vcdinf_get_timeout_offset(p_vcdplayer->pxd.psd);
500       uint16_t max_loop     = vcdinf_get_loop_count(p_vcdplayer->pxd.psd);
501       vcdinfo_offset_t *offset_timeout_LID = 
502         vcdinfo_get_offset_t(p_vcdplayer->vcd, timeout_offs);
503       
504       dbg_print(INPUT_DBG_PBC, "looped: %d, max_loop %d", 
505                 p_vcdplayer->i_loop, max_loop);
506       
507       /* Set up for caller process wait time given. */
508       if (p_vcdplayer->i_still) {
509         *wait_time = vcdinf_get_timeout_time(p_vcdplayer->pxd.psd);
510         dbg_print((INPUT_DBG_PBC|INPUT_DBG_STILL),
511                   "playlist wait_time: %d", *wait_time);
512         return READ_STILL_FRAME;
513       } 
514       
515       /* Wait time has been processed; continue with next entry. */
516       /* Handle any looping given. */
517       if ( max_loop == 0 || p_vcdplayer->i_loop < max_loop ) {
518         p_vcdplayer->i_loop++;
519         if (p_vcdplayer->i_loop == 0x7f) p_vcdplayer->i_loop = 0;
520         vcdplayer_play_single_item(p_access, p_vcdplayer->loop_item);
521         /* if (p_vcdplayer->i_still) p_vcdplayer->force_redisplay();*/
522         return READ_BLOCK;
523       }
524       
525       /* Looping finished and wait finished. Move to timeout
526          entry or next entry, or handle still. */
527       
528       if (NULL != offset_timeout_LID) {
529         /* Handle timeout_LID */
530         itemid.num  = offset_timeout_LID->lid;
531         itemid.type = VCDINFO_ITEM_TYPE_LID;
532         dbg_print(INPUT_DBG_PBC, "timeout to: %d", itemid.num);
533         vcdplayer_play( p_access, itemid );
534         return READ_BLOCK;
535       } else {
536         int i_selections = vcdinf_get_num_selections(p_vcdplayer->pxd.psd);
537         if (i_selections > 0) {
538           /* Pick a random selection. */
539           unsigned int bsn=vcdinf_get_bsn(p_vcdplayer->pxd.psd);
540           int rand_selection=bsn +
541             (int) ((i_selections+0.0)*rand()/(RAND_MAX+1.0));
542           lid_t rand_lid=vcdinfo_selection_get_lid (p_vcdplayer->vcd, 
543                                                     p_vcdplayer->i_lid, 
544                                                     rand_selection);
545           itemid.num = rand_lid;
546           itemid.type = VCDINFO_ITEM_TYPE_LID;
547           dbg_print(INPUT_DBG_PBC, "random selection %d, lid: %d", 
548                     rand_selection - bsn, rand_lid);
549           vcdplayer_play( p_access, itemid );
550           return READ_BLOCK;
551         } else if (p_vcdplayer->i_still) {
552           /* Hack: Just go back and do still again */
553           sleep(1);
554           return READ_STILL_FRAME;
555         }
556       }
557       break;
558     }
559   case VCDINFO_ITEM_TYPE_NOTFOUND:  
560     LOG_ERR( "NOTFOUND in PBC -- not supposed to happen" );
561     break;
562   case VCDINFO_ITEM_TYPE_SPAREID2:  
563     LOG_ERR( "SPAREID2 in PBC -- not supposed to happen" );
564     break;
565   case VCDINFO_ITEM_TYPE_LID:  
566     LOG_ERR( "LID in PBC -- not supposed to happen" );
567     break;
568     
569   default:
570     ;
571   }
572   /* FIXME: Should handle autowait ...  */
573
574   return READ_ERROR;
575 }
576
577 /*!
578   Read block into p_buf and return the status back.
579
580   This routine is a bit complicated because on reaching the end of 
581   a track or entry we may automatically advance to the item, or 
582   interpret the next item in the playback-control list.
583 */
584 vcdplayer_read_status_t
585 vcdplayer_read (access_t * p_access, uint8_t *p_buf)
586 {
587
588   /* p_access->handle_events (); */
589   uint8_t wait_time;
590
591   vcdplayer_t *p_vcdplayer= (vcdplayer_t *)p_access->p_sys;
592   if ( p_vcdplayer->i_lsn > p_vcdplayer->end_lsn ) {
593     vcdplayer_read_status_t read_status;
594     
595     /* We've run off of the end of this entry. Do we continue or stop? */
596     dbg_print( (INPUT_DBG_LSN|INPUT_DBG_PBC), 
597               "end reached, cur: %u, end: %u\n", p_vcdplayer->i_lsn, p_vcdplayer->end_lsn);
598
599   handle_item_continuation:
600     read_status = vcdplayer_pbc_is_on( p_vcdplayer ) 
601       ? vcdplayer_pbc_nav( p_access, &wait_time ) 
602       : vcdplayer_non_pbc_nav( p_access, &wait_time );
603
604     if (READ_STILL_FRAME == read_status) {
605       *p_buf = wait_time;
606       return READ_STILL_FRAME;
607     }
608
609     if (READ_BLOCK != read_status) return read_status;
610   }
611
612   /* Read the next block. 
613      
614     Important note: we probably speed things up by removing "data"
615     and the memcpy to it by extending vcd_image_source_read_mode2
616     to allow a mode to do what's below in addition to its 
617     "raw" and "block" mode. It also would probably improve the modularity
618     a little bit as well.
619   */
620
621   {
622     CdIo *p_img = vcdinfo_get_cd_image(p_vcdplayer->vcd);
623     typedef struct {
624       uint8_t subheader [CDIO_CD_SUBHEADER_SIZE];
625       uint8_t data      [M2F2_SECTOR_SIZE];
626       uint8_t spare     [4];
627     } vcdsector_t;
628     vcdsector_t vcd_sector;
629
630     do {
631       if (cdio_read_mode2_sector(p_img, &vcd_sector, 
632                                  p_vcdplayer->i_lsn, true)!=0) {
633         dbg_print(INPUT_DBG_LSN, "read error\n");
634         p_vcdplayer->i_lsn++;
635         return READ_ERROR;
636       }
637       p_vcdplayer->i_lsn++;
638
639       if ( p_vcdplayer->i_lsn >= p_vcdplayer->end_lsn ) {
640         /* We've run off of the end of this entry. Do we continue or stop? */
641         dbg_print( (INPUT_DBG_LSN|INPUT_DBG_PBC), 
642                    "end reached in reading, cur: %u, end: %u\n", 
643                    p_vcdplayer->i_lsn, p_vcdplayer->end_lsn);
644         break;
645       }
646       
647       /* Check header ID for a padding sector and simply discard
648          these.  It is alleged that VCD's put these in to keep the
649          bitrate constant.
650       */
651     } while((vcd_sector.subheader[2]&~0x01)==0x60);
652
653     if ( p_vcdplayer->i_lsn >= p_vcdplayer->end_lsn ) 
654       /* We've run off of the end of this entry. Do we continue or stop? */
655       goto handle_item_continuation;
656       
657     memcpy (p_buf, vcd_sector.data, M2F2_SECTOR_SIZE);
658     return READ_BLOCK;
659   }
660 }
661
662 /*!
663   Play item assocated with the "default" selection.
664
665   Return false if there was some problem.
666 */
667 bool 
668 vcdplayer_play_default( access_t * p_access )
669 {
670   vcdplayer_t *p_vcdplayer= (vcdplayer_t *)p_access->p_sys;
671
672   vcdinfo_itemid_t itemid;
673
674   if (!p_vcdplayer) {
675     dbg_print( (INPUT_DBG_CALL|INPUT_DBG_PBC), 
676                "null p_vcdplayer" );
677     return VLC_EGENERIC;
678   }
679   
680
681   dbg_print( (INPUT_DBG_CALL|INPUT_DBG_PBC), 
682              "current: %d" , p_vcdplayer->play_item.num);
683
684   itemid.type = p_vcdplayer->play_item.type;
685
686   if  (vcdplayer_pbc_is_on(p_vcdplayer)) {
687
688 #if defined(LIBVCD_VERSION)
689     lid_t lid=vcdinfo_get_multi_default_lid(p_vcdplayer->vcd, p_vcdplayer->i_lid,
690                                             p_vcdplayer->i_lsn);
691
692     if (VCDINFO_INVALID_LID != lid) {
693       itemid.num  = lid;
694       itemid.type = VCDINFO_ITEM_TYPE_LID;
695       dbg_print(INPUT_DBG_PBC, "DEFAULT to %d", itemid.num);
696     } else {
697       dbg_print(INPUT_DBG_PBC, "no DEFAULT for LID %d", p_vcdplayer->i_lid);
698     }
699
700 #else 
701     vcdinfo_lid_get_pxd(p_vcdplayer->vcd, &(p_vcdplayer->pxd), p_vcdplayer->i_lid);
702     
703     switch (p_vcdplayer->pxd.descriptor_type) {
704     case PSD_TYPE_SELECTION_LIST:
705     case PSD_TYPE_EXT_SELECTION_LIST:
706       if (p_vcdplayer->pxd.psd == NULL) return false;
707       vcdplayer_update_entry( p_access, 
708                               vcdinfo_get_default_offset(p_vcdplayer->vcd, 
709                                                          p_vcdplayer->i_lid), 
710                               &itemid.num, "default");
711       break;
712
713     case PSD_TYPE_PLAY_LIST: 
714     case PSD_TYPE_END_LIST:
715     case PSD_TYPE_COMMAND_LIST:
716       LOG_WARN( "There is no PBC 'default' selection here" );
717       return false;
718     }
719 #endif /* LIBVCD_VERSION (< 0.7.21) */
720     
721
722   } else {
723
724     /* PBC is not on. "default" selection beginning of current 
725        selection . */
726   
727     p_vcdplayer->play_item.num = p_vcdplayer->play_item.num;
728     
729   }
730
731   /** ??? p_vcdplayer->update_title(); ***/
732   vcdplayer_play( p_access, itemid );
733   return VLC_SUCCESS;
734
735 }
736
737 /*!
738   Play item assocated with the "next" selection.
739
740   Return false if there was some problem.
741 */
742 bool 
743 vcdplayer_play_next( access_t * p_access )
744 {
745   vcdplayer_t *p_vcdplayer= (vcdplayer_t *)p_access->p_sys;
746
747   vcdinfo_obj_t     *p_vcdinfo;
748   vcdinfo_itemid_t   itemid;
749
750   if (!p_vcdplayer) return false;
751
752   dbg_print( (INPUT_DBG_CALL|INPUT_DBG_PBC), 
753              "current: %d" , p_vcdplayer->play_item.num);
754
755   p_vcdinfo = p_vcdplayer->vcd;
756
757   itemid.type = p_vcdplayer->play_item.type;
758
759   if  (vcdplayer_pbc_is_on(p_vcdplayer)) {
760
761     vcdinfo_lid_get_pxd(p_vcdinfo, &(p_vcdplayer->pxd), p_vcdplayer->i_lid);
762     
763     switch (p_vcdplayer->pxd.descriptor_type) {
764     case PSD_TYPE_SELECTION_LIST:
765     case PSD_TYPE_EXT_SELECTION_LIST:
766       if (p_vcdplayer->pxd.psd == NULL) return false;
767       vcdplayer_update_entry( p_access, 
768                               vcdinf_psd_get_next_offset(p_vcdplayer->pxd.psd), 
769                               &itemid.num, "next");
770       itemid.type = VCDINFO_ITEM_TYPE_LID;
771       break;
772
773     case PSD_TYPE_PLAY_LIST: 
774       if (p_vcdplayer->pxd.pld == NULL) return false;
775       vcdplayer_update_entry( p_access, 
776                               vcdinf_pld_get_next_offset(p_vcdplayer->pxd.pld), 
777                               &itemid.num, "next");
778       itemid.type = VCDINFO_ITEM_TYPE_LID;
779       break;
780       
781     case PSD_TYPE_END_LIST:
782     case PSD_TYPE_COMMAND_LIST:
783       LOG_WARN( "There is no PBC 'next' selection here" );
784       return false;
785     }
786   } else {
787
788     /* PBC is not on. "Next" selection is play_item.num+1 if possible. */
789   
790     int max_entry = 0;
791
792     switch (p_vcdplayer->play_item.type) {
793     case VCDINFO_ITEM_TYPE_ENTRY: 
794     case VCDINFO_ITEM_TYPE_SEGMENT: 
795     case VCDINFO_ITEM_TYPE_TRACK: 
796       
797       switch (p_vcdplayer->play_item.type) {
798       case VCDINFO_ITEM_TYPE_ENTRY: 
799         max_entry = p_vcdplayer->i_entries;
800         break;
801       case VCDINFO_ITEM_TYPE_SEGMENT: 
802         max_entry = p_vcdplayer->i_segments;
803         break;
804       case VCDINFO_ITEM_TYPE_TRACK: 
805         max_entry = p_vcdplayer->i_tracks;
806         break;
807       default: ; /* Handle exceptional cases below */
808       }
809       
810       if (p_vcdplayer->play_item.num+1 < max_entry) {
811         itemid.num = p_vcdplayer->play_item.num+1;
812       } else {
813         LOG_WARN( "At the end - non-PBC 'next' not possible here" );
814         return false;
815       }
816       
817       break;
818       
819     case VCDINFO_ITEM_TYPE_LID: 
820       {
821         /* Should have handled above. */
822         LOG_WARN( "Internal inconsistency - should not have gotten here." );
823         return false;
824       }
825     default: 
826       return false;
827     }
828   }
829
830   /** ??? p_vcdplayer->update_title(); ***/
831   vcdplayer_play( p_access, itemid );
832   return VLC_SUCCESS;
833
834 }
835
836 /*!
837   Play item assocated with the "prev" selection.
838
839   Return false if there was some problem.
840 */
841 bool 
842 vcdplayer_play_prev( access_t * p_access )
843 {
844   vcdplayer_t      *p_vcdplayer= (vcdplayer_t *)p_access->p_sys;
845   vcdinfo_obj_t    *p_vcdinfo  = p_vcdplayer->vcd;
846   vcdinfo_itemid_t  itemid;
847
848   dbg_print( (INPUT_DBG_CALL|INPUT_DBG_PBC), 
849              "current: %d" , p_vcdplayer->play_item.num);
850
851   itemid.type = p_vcdplayer->play_item.type;
852
853   if  (vcdplayer_pbc_is_on(p_vcdplayer)) {
854
855     vcdinfo_lid_get_pxd(p_vcdinfo, &(p_vcdplayer->pxd), p_vcdplayer->i_lid);
856     
857     switch (p_vcdplayer->pxd.descriptor_type) {
858     case PSD_TYPE_SELECTION_LIST:
859     case PSD_TYPE_EXT_SELECTION_LIST:
860       if (p_vcdplayer->pxd.psd == NULL) return false;
861       vcdplayer_update_entry( p_access, 
862                               vcdinf_psd_get_prev_offset(p_vcdplayer->pxd.psd), 
863                               &itemid.num, "prev");
864       itemid.type = VCDINFO_ITEM_TYPE_LID;
865       break;
866
867     case PSD_TYPE_PLAY_LIST: 
868       if (p_vcdplayer->pxd.pld == NULL) return false;
869       vcdplayer_update_entry( p_access, 
870                               vcdinf_pld_get_prev_offset(p_vcdplayer->pxd.pld), 
871                               &itemid.num, "prev");
872       itemid.type = VCDINFO_ITEM_TYPE_LID;
873       break;
874       
875     case PSD_TYPE_END_LIST:
876     case PSD_TYPE_COMMAND_LIST:
877       LOG_WARN( "There is no PBC 'prev' selection here" );
878       return false;
879     }
880   } else {
881
882     /* PBC is not on. "Prev" selection is play_item.num-1 if possible. */
883   
884     int min_entry = (VCDINFO_ITEM_TYPE_ENTRY == p_vcdplayer->play_item.type) 
885       ? 0 : 1;
886     
887     if (p_vcdplayer->play_item.num > min_entry) {
888       itemid.num = p_vcdplayer->play_item.num-1;
889     } else {
890       LOG_WARN( "At the beginning - non-PBC 'prev' not possible here" );
891       return false;
892     }
893       
894   }
895
896   /** ??? p_vcdplayer->update_title(); ***/
897   vcdplayer_play( p_access, itemid );
898   return VLC_SUCCESS;
899
900 }
901
902 /*!
903   Play item assocated with the "return" selection.
904
905   Return false if there was some problem.
906 */
907 bool 
908 vcdplayer_play_return( access_t * p_access )
909 {
910   vcdplayer_t      *p_vcdplayer= (vcdplayer_t *)p_access->p_sys;
911   vcdinfo_obj_t    *p_vcdinfo  = p_vcdplayer->vcd;
912   vcdinfo_itemid_t  itemid;
913
914   dbg_print( (INPUT_DBG_CALL|INPUT_DBG_PBC), 
915              "current: %d" , p_vcdplayer->play_item.num);
916
917   itemid.type = p_vcdplayer->play_item.type;
918
919   if  (vcdplayer_pbc_is_on(p_vcdplayer)) {
920
921     vcdinfo_lid_get_pxd(p_vcdinfo, &(p_vcdplayer->pxd), p_vcdplayer->i_lid);
922     
923     switch (p_vcdplayer->pxd.descriptor_type) {
924     case PSD_TYPE_SELECTION_LIST:
925     case PSD_TYPE_EXT_SELECTION_LIST:
926       if (p_vcdplayer->pxd.psd == NULL) return false;
927       vcdplayer_update_entry( p_access, 
928                               vcdinf_psd_get_return_offset(p_vcdplayer->pxd.psd), 
929                               &itemid.num, "return");
930       itemid.type = VCDINFO_ITEM_TYPE_LID;
931       break;
932
933     case PSD_TYPE_PLAY_LIST: 
934       if (p_vcdplayer->pxd.pld == NULL) return false;
935       vcdplayer_update_entry( p_access, 
936                               vcdinf_pld_get_return_offset(p_vcdplayer->pxd.pld), 
937                               &itemid.num, "return");
938       itemid.type = VCDINFO_ITEM_TYPE_LID;
939       break;
940       
941     case PSD_TYPE_END_LIST:
942     case PSD_TYPE_COMMAND_LIST:
943       LOG_WARN( "There is no PBC 'return' selection here" );
944       return false;
945     }
946   } else {
947
948     /* PBC is not on. "Return" selection is min_entry if possible. */
949   
950     p_vcdplayer->play_item.num = 
951       (VCDINFO_ITEM_TYPE_ENTRY == p_vcdplayer->play_item.type) 
952       ? 0 : 1;
953     
954   }
955
956   /** ??? p_vcdplayer->update_title(); ***/
957   vcdplayer_play( p_access, itemid );
958   return VLC_SUCCESS;
959
960 }
961
962 /* 
963  * Local variables:
964  *  c-file-style: "gnu"
965  *  tab-width: 8
966  *  indent-tabs-mode: nil
967  * End:
968  */