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