]> git.sesse.net Git - vlc/blob - modules/access/vcdx/vcdplayer.c
Remove gcc 4 uninitialize variable warning.
[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       return VLC_EGENERIC;
699     }
700
701 #else 
702     vcdinfo_lid_get_pxd(p_vcdplayer->vcd, &(p_vcdplayer->pxd), p_vcdplayer->i_lid);
703     
704     switch (p_vcdplayer->pxd.descriptor_type) {
705     case PSD_TYPE_SELECTION_LIST:
706     case PSD_TYPE_EXT_SELECTION_LIST:
707       if (p_vcdplayer->pxd.psd == NULL) return false;
708       vcdplayer_update_entry( p_access, 
709                               vcdinfo_get_default_offset(p_vcdplayer->vcd, 
710                                                          p_vcdplayer->i_lid), 
711                               &itemid.num, "default");
712       break;
713
714     case PSD_TYPE_PLAY_LIST: 
715     case PSD_TYPE_END_LIST:
716     case PSD_TYPE_COMMAND_LIST:
717       LOG_WARN( "There is no PBC 'default' selection here" );
718       return false;
719     }
720 #endif /* LIBVCD_VERSION (< 0.7.21) */
721     
722
723   } else {
724
725     /* PBC is not on. "default" selection beginning of current 
726        selection . */
727   
728     itemid.num = p_vcdplayer->play_item.num;
729     
730   }
731
732   /** ??? p_vcdplayer->update_title(); ***/
733   vcdplayer_play( p_access, itemid );
734   return VLC_SUCCESS;
735
736 }
737
738 /*!
739   Play item assocated with the "next" selection.
740
741   Return false if there was some problem.
742 */
743 bool 
744 vcdplayer_play_next( access_t * p_access )
745 {
746   vcdplayer_t *p_vcdplayer= (vcdplayer_t *)p_access->p_sys;
747
748   vcdinfo_obj_t     *p_vcdinfo;
749   vcdinfo_itemid_t   itemid;
750
751   if (!p_vcdplayer) return false;
752
753   dbg_print( (INPUT_DBG_CALL|INPUT_DBG_PBC), 
754              "current: %d" , p_vcdplayer->play_item.num);
755
756   p_vcdinfo = p_vcdplayer->vcd;
757
758   itemid = p_vcdplayer->play_item;
759
760   if  (vcdplayer_pbc_is_on(p_vcdplayer)) {
761
762     vcdinfo_lid_get_pxd(p_vcdinfo, &(p_vcdplayer->pxd), p_vcdplayer->i_lid);
763     
764     switch (p_vcdplayer->pxd.descriptor_type) {
765     case PSD_TYPE_SELECTION_LIST:
766     case PSD_TYPE_EXT_SELECTION_LIST:
767       if (p_vcdplayer->pxd.psd == NULL) return false;
768       vcdplayer_update_entry( p_access, 
769                               vcdinf_psd_get_next_offset(p_vcdplayer->pxd.psd), 
770                               &itemid.num, "next");
771       itemid.type = VCDINFO_ITEM_TYPE_LID;
772       break;
773
774     case PSD_TYPE_PLAY_LIST: 
775       if (p_vcdplayer->pxd.pld == NULL) return false;
776       vcdplayer_update_entry( p_access, 
777                               vcdinf_pld_get_next_offset(p_vcdplayer->pxd.pld), 
778                               &itemid.num, "next");
779       itemid.type = VCDINFO_ITEM_TYPE_LID;
780       break;
781       
782     case PSD_TYPE_END_LIST:
783     case PSD_TYPE_COMMAND_LIST:
784       LOG_WARN( "There is no PBC 'next' selection here" );
785       return false;
786     }
787   } else {
788
789     /* PBC is not on. "Next" selection is play_item.num+1 if possible. */
790   
791     int max_entry = 0;
792
793     switch (p_vcdplayer->play_item.type) {
794     case VCDINFO_ITEM_TYPE_ENTRY: 
795     case VCDINFO_ITEM_TYPE_SEGMENT: 
796     case VCDINFO_ITEM_TYPE_TRACK: 
797       
798       switch (p_vcdplayer->play_item.type) {
799       case VCDINFO_ITEM_TYPE_ENTRY: 
800         max_entry = p_vcdplayer->i_entries;
801         break;
802       case VCDINFO_ITEM_TYPE_SEGMENT: 
803         max_entry = p_vcdplayer->i_segments;
804         break;
805       case VCDINFO_ITEM_TYPE_TRACK: 
806         max_entry = p_vcdplayer->i_tracks;
807         break;
808       default: ; /* Handle exceptional cases below */
809       }
810       
811       if (p_vcdplayer->play_item.num+1 < max_entry) {
812         itemid.num = p_vcdplayer->play_item.num+1;
813       } else {
814         LOG_WARN( "At the end - non-PBC 'next' not possible here" );
815         return false;
816       }
817       
818       break;
819       
820     case VCDINFO_ITEM_TYPE_LID: 
821       {
822         /* Should have handled above. */
823         LOG_WARN( "Internal inconsistency - should not have gotten here." );
824         return false;
825       }
826     default: 
827       return false;
828     }
829   }
830
831   /** ??? p_vcdplayer->update_title(); ***/
832   vcdplayer_play( p_access, itemid );
833   return VLC_SUCCESS;
834
835 }
836
837 /*!
838   Play item assocated with the "prev" selection.
839
840   Return false if there was some problem.
841 */
842 bool 
843 vcdplayer_play_prev( access_t * p_access )
844 {
845   vcdplayer_t      *p_vcdplayer= (vcdplayer_t *)p_access->p_sys;
846   vcdinfo_obj_t    *p_vcdinfo  = p_vcdplayer->vcd;
847   vcdinfo_itemid_t  itemid;
848
849   dbg_print( (INPUT_DBG_CALL|INPUT_DBG_PBC), 
850              "current: %d" , p_vcdplayer->play_item.num);
851
852   itemid = p_vcdplayer->play_item;
853
854   if  (vcdplayer_pbc_is_on(p_vcdplayer)) {
855
856     vcdinfo_lid_get_pxd(p_vcdinfo, &(p_vcdplayer->pxd), p_vcdplayer->i_lid);
857     
858     switch (p_vcdplayer->pxd.descriptor_type) {
859     case PSD_TYPE_SELECTION_LIST:
860     case PSD_TYPE_EXT_SELECTION_LIST:
861       if (p_vcdplayer->pxd.psd == NULL) return false;
862       vcdplayer_update_entry( p_access, 
863                               vcdinf_psd_get_prev_offset(p_vcdplayer->pxd.psd), 
864                               &itemid.num, "prev");
865       itemid.type = VCDINFO_ITEM_TYPE_LID;
866       break;
867
868     case PSD_TYPE_PLAY_LIST: 
869       if (p_vcdplayer->pxd.pld == NULL) return false;
870       vcdplayer_update_entry( p_access, 
871                               vcdinf_pld_get_prev_offset(p_vcdplayer->pxd.pld), 
872                               &itemid.num, "prev");
873       itemid.type = VCDINFO_ITEM_TYPE_LID;
874       break;
875       
876     case PSD_TYPE_END_LIST:
877     case PSD_TYPE_COMMAND_LIST:
878       LOG_WARN( "There is no PBC 'prev' selection here" );
879       return false;
880     }
881   } else {
882
883     /* PBC is not on. "Prev" selection is play_item.num-1 if possible. */
884   
885     int min_entry = (VCDINFO_ITEM_TYPE_ENTRY == p_vcdplayer->play_item.type) 
886       ? 0 : 1;
887     
888     if (p_vcdplayer->play_item.num > min_entry) {
889       itemid.num = p_vcdplayer->play_item.num-1;
890     } else {
891       LOG_WARN( "At the beginning - non-PBC 'prev' not possible here" );
892       return false;
893     }
894       
895   }
896
897   /** ??? p_vcdplayer->update_title(); ***/
898   vcdplayer_play( p_access, itemid );
899   return VLC_SUCCESS;
900
901 }
902
903 /*!
904   Play item assocated with the "return" selection.
905
906   Return false if there was some problem.
907 */
908 bool 
909 vcdplayer_play_return( access_t * p_access )
910 {
911   vcdplayer_t      *p_vcdplayer= (vcdplayer_t *)p_access->p_sys;
912   vcdinfo_obj_t    *p_vcdinfo  = p_vcdplayer->vcd;
913   vcdinfo_itemid_t  itemid;
914
915   dbg_print( (INPUT_DBG_CALL|INPUT_DBG_PBC), 
916              "current: %d" , p_vcdplayer->play_item.num);
917
918   itemid = p_vcdplayer->play_item;
919
920   if  (vcdplayer_pbc_is_on(p_vcdplayer)) {
921
922     vcdinfo_lid_get_pxd(p_vcdinfo, &(p_vcdplayer->pxd), p_vcdplayer->i_lid);
923     
924     switch (p_vcdplayer->pxd.descriptor_type) {
925     case PSD_TYPE_SELECTION_LIST:
926     case PSD_TYPE_EXT_SELECTION_LIST:
927       if (p_vcdplayer->pxd.psd == NULL) return false;
928       vcdplayer_update_entry( p_access, 
929                               vcdinf_psd_get_return_offset(p_vcdplayer->pxd.psd), 
930                               &itemid.num, "return");
931       itemid.type = VCDINFO_ITEM_TYPE_LID;
932       break;
933
934     case PSD_TYPE_PLAY_LIST: 
935       if (p_vcdplayer->pxd.pld == NULL) return false;
936       vcdplayer_update_entry( p_access, 
937                               vcdinf_pld_get_return_offset(p_vcdplayer->pxd.pld), 
938                               &itemid.num, "return");
939       itemid.type = VCDINFO_ITEM_TYPE_LID;
940       break;
941       
942     case PSD_TYPE_END_LIST:
943     case PSD_TYPE_COMMAND_LIST:
944       LOG_WARN( "There is no PBC 'return' selection here" );
945       return false;
946     }
947   } else {
948
949     /* PBC is not on. "Return" selection is min_entry if possible. */
950   
951     p_vcdplayer->play_item.num = 
952       (VCDINFO_ITEM_TYPE_ENTRY == p_vcdplayer->play_item.type) 
953       ? 0 : 1;
954     
955   }
956
957   /** ??? p_vcdplayer->update_title(); ***/
958   vcdplayer_play( p_access, itemid );
959   return VLC_SUCCESS;
960
961 }
962
963 /* 
964  * Local variables:
965  *  c-file-style: "gnu"
966  *  tab-width: 8
967  *  indent-tabs-mode: nil
968  * End:
969  */