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