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