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