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