]> git.sesse.net Git - vlc/blob - modules/access/vcdx/vcdplayer.c
Handle keyboard input: numeric entry, next, prev, return and default.
[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 Rocky Bernstein <rocky@panix.com>
6  * $Id: vcdplayer.c,v 1.2 2003/11/09 00:52:32 rocky Exp $
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
35 #include "vcd.h"
36 #include "vcdplayer.h"
37
38 #include <string.h>
39
40 #include <cdio/cdio.h>
41 #include <cdio/util.h>
42 #include <libvcd/info.h>
43
44 /*!
45   Return true if playback control (PBC) is on
46 */
47 bool
48 vcdplayer_pbc_is_on(const thread_vcd_data_t *p_vcd) 
49 {
50   return VCDINFO_INVALID_ENTRY != p_vcd->cur_lid; 
51 }
52
53 lid_t
54 vcdplayer_selection2lid ( input_thread_t *p_input, int entry_num ) 
55 {
56   /* FIXME: Some of this probably gets moved to vcdinfo. */
57   /* Convert selection number to lid and then entry number...*/
58   thread_vcd_data_t *     p_vcd= (thread_vcd_data_t *)p_input->p_access_data;
59   unsigned int offset;
60   unsigned int bsn=vcdinf_get_bsn(p_vcd->pxd.psd);
61   vcdinfo_obj_t *obj = p_vcd->vcd;
62
63   dbg_print( (INPUT_DBG_CALL|INPUT_DBG_PBC), 
64             "Called lid %u, entry_num %d bsn %d", p_vcd->cur_lid, 
65              entry_num, bsn);
66
67   if ( (entry_num - bsn + 1) > 0) {
68     offset = vcdinfo_lid_get_offset(obj, p_vcd->cur_lid, entry_num-bsn+1);
69   } else {
70     LOG_ERR( "Selection number %u too small. bsn %u", entry_num, bsn );
71     return VCDINFO_INVALID_LID;
72   }
73   
74   if (offset != VCDINFO_INVALID_OFFSET) {
75     vcdinfo_offset_t *ofs;
76     int old = entry_num;
77     
78     switch (offset) {
79     case PSD_OFS_DISABLED:
80       LOG_ERR( "Selection %u disabled", entry_num );
81       return VCDINFO_INVALID_LID;
82     case PSD_OFS_MULTI_DEF:
83       LOG_ERR( "Selection %u multi_def", entry_num );
84       return VCDINFO_INVALID_LID;
85     case PSD_OFS_MULTI_DEF_NO_NUM:
86       LOG_ERR( "Selection %u multi_def_no_num", entry_num );
87       return VCDINFO_INVALID_LID;
88     default: ;
89     }
90     
91     ofs = vcdinfo_get_offset_t(obj, offset);
92
93     if (NULL == ofs) {
94       LOG_ERR( "error in vcdinfo_get_offset" );
95       return -1;
96     }
97     dbg_print(INPUT_DBG_PBC,
98               "entry %u turned into selection lid %u", 
99               old, ofs->lid);
100     return ofs->lid;
101     
102   } else {
103     LOG_ERR( "invalid or unset entry %u", entry_num );
104     return VCDINFO_INVALID_LID;
105   }
106 }
107
108 static void
109 vcdplayer_update_entry( input_thread_t * p_input, uint16_t ofs, 
110                         uint16_t *entry, const char *label)
111 {
112   thread_vcd_data_t *     p_vcd= (thread_vcd_data_t *)p_input->p_access_data;
113
114   if ( ofs == VCDINFO_INVALID_OFFSET ) {
115     *entry = VCDINFO_INVALID_ENTRY;
116   } else {
117     vcdinfo_offset_t *off_t = vcdinfo_get_offset_t(p_vcd->vcd, ofs);
118     if (off_t != NULL) {
119       *entry = off_t->lid;
120       dbg_print(INPUT_DBG_PBC, "%s: %d\n", label, off_t->lid);
121     } else
122       *entry = VCDINFO_INVALID_ENTRY;
123   }
124 }
125
126 /* Handles navigation when NOT in PBC reaching the end of a play item. 
127
128    The navigations rules here may be sort of made up, but the intent 
129    is to do something that's probably right or helpful.
130
131    return true if the caller should return.
132 */
133 vcdplayer_read_status_t
134 vcdplayer_non_pbc_nav ( input_thread_t * p_input )
135 {
136   thread_vcd_data_t *     p_vcd= (thread_vcd_data_t *)p_input->p_access_data;
137
138   /* Not in playback control. Do we advance automatically or stop? */
139   switch (p_vcd->play_item.type) {
140   case VCDINFO_ITEM_TYPE_TRACK:
141   case VCDINFO_ITEM_TYPE_ENTRY: {
142     input_area_t *p_area;
143
144     dbg_print( INPUT_DBG_LSN, "new track %d, lsn %d", p_vcd->cur_track, 
145                p_vcd->p_sectors[p_vcd->cur_track+1] );
146     
147     if ( p_vcd->cur_track >= p_vcd->num_tracks - 1 )
148       return READ_END; /* EOF */
149         
150     p_vcd->play_item.num = p_vcd->cur_track++;
151     
152     vlc_mutex_lock( &p_input->stream.stream_lock );
153     p_area = p_input->stream.pp_areas[p_vcd->cur_track];
154     
155     p_area->i_part = 1;
156     VCDSetArea( p_input, p_area );
157     vlc_mutex_unlock( &p_input->stream.stream_lock );
158     return READ_BLOCK;
159     break;
160   }
161   case VCDINFO_ITEM_TYPE_SPAREID2:  
162     dbg_print( (INPUT_DBG_STILL|INPUT_DBG_LSN), 
163                "SPAREID2" );
164     /* FIXME */
165     p_input->stream.b_seekable = 0;
166     if (p_vcd->in_still)
167     {
168       return READ_STILL_FRAME ;
169     }
170     return READ_END;
171   case VCDINFO_ITEM_TYPE_NOTFOUND:  
172     LOG_ERR ("NOTFOUND outside PBC -- not supposed to happen");
173     return READ_ERROR;
174   case VCDINFO_ITEM_TYPE_LID:  
175     LOG_ERR ("LID outside PBC -- not supposed to happen");
176     return READ_ERROR;
177   case VCDINFO_ITEM_TYPE_SEGMENT:
178       /* Hack: Just go back and do still again */
179     /* FIXME */
180     p_input->stream.b_seekable = 0;
181     if (p_vcd->in_still) 
182     {
183       dbg_print( (INPUT_DBG_STILL|INPUT_DBG_LSN), 
184                  "End of Segment - looping" );
185       return READ_STILL_FRAME;
186     }
187     return READ_END;
188   }
189   return READ_BLOCK;
190 }
191
192 /* FIXME: Will do whatever the right thing is later. */
193 #define SLEEP_1_SEC_AND_HANDLE_EVENTS sleep(1)
194
195 /* Handles PBC navigation when reaching the end of a play item. */
196 vcdplayer_read_status_t
197 vcdplayer_pbc_nav ( input_thread_t * p_input )
198 {
199   thread_vcd_data_t *     p_vcd= (thread_vcd_data_t *)p_input->p_access_data;
200
201   /* We are in playback control. */
202   vcdinfo_itemid_t itemid;
203
204   if (0 != p_vcd->in_still && p_vcd->in_still != -5) {
205       SLEEP_1_SEC_AND_HANDLE_EVENTS;
206       if (p_vcd->in_still > 0) p_vcd->in_still--;
207       return READ_STILL_FRAME;
208   }
209
210   /* The end of an entry is really the end of the associated 
211      sequence (or track). */
212   
213   if ( (VCDINFO_ITEM_TYPE_ENTRY == p_vcd->play_item.type) && 
214        (p_vcd->cur_lsn < p_vcd->end_lsn) ) {
215     /* Set up to just continue to the next entry */
216     p_vcd->play_item.num++;
217     dbg_print( (INPUT_DBG_LSN|INPUT_DBG_PBC), 
218                "continuing into next entry: %u", p_vcd->play_item.num);
219     VCDPlay( p_input, p_vcd->play_item );
220     /* p_vcd->update_title(); */
221     return READ_BLOCK;
222   }
223   
224   switch (p_vcd->pxd.descriptor_type) {
225   case PSD_TYPE_END_LIST:
226     return READ_END;
227     break;
228   case PSD_TYPE_PLAY_LIST: {
229     int wait_time = vcdinf_get_wait_time(p_vcd->pxd.pld);
230     
231     dbg_print(INPUT_DBG_PBC, "playlist wait_time: %d", wait_time);
232     
233     if (vcdplayer_inc_play_item(p_input))
234       return READ_BLOCK;
235
236     /* Handle any wait time given. */
237     if (-5 == p_vcd->in_still) {
238       if (wait_time != 0) {
239         /* FIXME */
240         p_vcd->in_still = wait_time - 1;
241         SLEEP_1_SEC_AND_HANDLE_EVENTS ;
242         return READ_STILL_FRAME;
243       }
244     }
245     vcdplayer_update_entry( p_input, 
246                             vcdinf_pld_get_next_offset(p_vcd->pxd.pld),
247                             &itemid.num, "next" );
248     itemid.type = VCDINFO_ITEM_TYPE_LID;
249     VCDPlay( p_input, itemid );
250     break;
251   }
252   case PSD_TYPE_SELECTION_LIST:     /* Selection List (+Ext. for SVCD) */
253   case PSD_TYPE_EXT_SELECTION_LIST: /* Extended Selection List (VCD2.0) */
254     {
255       int wait_time         = vcdinf_get_timeout_time(p_vcd->pxd.psd);
256       uint16_t timeout_offs = vcdinf_get_timeout_offset(p_vcd->pxd.psd);
257       uint16_t max_loop     = vcdinf_get_loop_count(p_vcd->pxd.psd);
258       vcdinfo_offset_t *offset_timeout_LID = 
259         vcdinfo_get_offset_t(p_vcd->vcd, timeout_offs);
260       
261       dbg_print(INPUT_DBG_PBC, "wait_time: %d, looped: %d, max_loop %d", 
262                 wait_time, p_vcd->loop_count, max_loop);
263       
264       /* Handle any wait time given */
265       if (-5 == p_vcd->in_still) {
266         p_vcd->in_still = wait_time - 1;
267         SLEEP_1_SEC_AND_HANDLE_EVENTS ;
268         return READ_STILL_FRAME;
269       }
270       
271       /* Handle any looping given. */
272       if ( max_loop == 0 || p_vcd->loop_count < max_loop ) {
273         p_vcd->loop_count++;
274         if (p_vcd->loop_count == 0x7f) p_vcd->loop_count = 0;
275         VCDSeek( p_input, 0 );
276         /* if (p_vcd->in_still) p_vcd->force_redisplay();*/
277         return READ_BLOCK;
278       }
279       
280       /* Looping finished and wait finished. Move to timeout
281          entry or next entry, or handle still. */
282       
283       if (NULL != offset_timeout_LID) {
284         /* Handle timeout_LID */
285         itemid.num  = offset_timeout_LID->lid;
286         itemid.type = VCDINFO_ITEM_TYPE_LID;
287         dbg_print(INPUT_DBG_PBC, "timeout to: %d", itemid.num);
288         VCDPlay( p_input, itemid );
289         return READ_BLOCK;
290       } else {
291         int num_selections = vcdinf_get_num_selections(p_vcd->pxd.psd);
292         if (num_selections > 0) {
293           /* Pick a random selection. */
294           unsigned int bsn=vcdinf_get_bsn(p_vcd->pxd.psd);
295           int rand_selection=bsn +
296             (int) ((num_selections+0.0)*rand()/(RAND_MAX+1.0));
297           lid_t rand_lid=vcdplayer_selection2lid (p_input, rand_selection);
298           itemid.num = rand_lid;
299           itemid.type = VCDINFO_ITEM_TYPE_LID;
300           dbg_print(INPUT_DBG_PBC, "random selection %d, lid: %d", 
301                     rand_selection - bsn, rand_lid);
302           VCDPlay( p_input, itemid );
303           return READ_BLOCK;
304         } else if (p_vcd->in_still) {
305           /* Hack: Just go back and do still again */
306           SLEEP_1_SEC_AND_HANDLE_EVENTS ;
307           return READ_STILL_FRAME;
308         }
309       }
310       break;
311     }
312   case VCDINFO_ITEM_TYPE_NOTFOUND:  
313     LOG_ERR( "NOTFOUND in PBC -- not supposed to happen" );
314     break;
315   case VCDINFO_ITEM_TYPE_SPAREID2:  
316     LOG_ERR( "SPAREID2 in PBC -- not supposed to happen" );
317     break;
318   case VCDINFO_ITEM_TYPE_LID:  
319     LOG_ERR( "LID in PBC -- not supposed to happen" );
320     break;
321     
322   default:
323     ;
324   }
325   /* FIXME: Should handle autowait ...  */
326
327   return READ_ERROR;
328 }
329
330 /*!
331   Get the next play-item in the list given in the LIDs. Note play-item
332   here refers to list of play-items for a single LID It shouldn't be
333   confused with a user's list of favorite things to play or the 
334   "next" field of a LID which moves us to a different LID.
335  */
336 bool
337 vcdplayer_inc_play_item( input_thread_t *p_input )
338 {
339   thread_vcd_data_t *     p_vcd= (thread_vcd_data_t *)p_input->p_access_data;
340
341   int noi;
342
343   dbg_print(INPUT_DBG_CALL, "called pli: %d", p_vcd->pdi);
344
345   if ( NULL == p_vcd || NULL == p_vcd->pxd.pld  ) return false;
346
347   noi = vcdinf_pld_get_noi(p_vcd->pxd.pld);
348   
349   if ( noi <= 0 ) return false;
350   
351   /* Handle delays like autowait or wait here? */
352
353   p_vcd->pdi++;
354
355   if ( p_vcd->pdi < 0 || p_vcd->pdi >= noi ) return false;
356
357   else {
358     uint16_t trans_itemid_num=vcdinf_pld_get_play_item(p_vcd->pxd.pld, 
359                                                        p_vcd->pdi);
360     vcdinfo_itemid_t trans_itemid;
361
362     if (VCDINFO_INVALID_ITEMID == trans_itemid_num) return false;
363     
364     vcdinfo_classify_itemid(trans_itemid_num, &trans_itemid);
365     dbg_print(INPUT_DBG_PBC, "  play-item[%d]: %s",
366               p_vcd->pdi, vcdinfo_pin2str (trans_itemid_num));
367     return VLC_SUCCESS == VCDPlay( p_input, trans_itemid );
368   }
369 }
370
371 /*!
372   Play item assocated with the "default" selection.
373
374   Return false if there was some problem.
375 */
376 bool
377 vcdplayer_play_default( input_thread_t * p_input )
378 {
379   thread_vcd_data_t *p_vcd= (thread_vcd_data_t *)p_input->p_access_data;
380
381   vcdinfo_obj_t     *obj  = p_vcd->vcd;
382   vcdinfo_itemid_t   itemid;
383
384   dbg_print( (INPUT_DBG_CALL|INPUT_DBG_PBC), 
385              "current: %d" , p_vcd->play_item.num);
386
387   itemid.type = p_vcd->play_item.type;
388
389   if  (vcdplayer_pbc_is_on(p_vcd)) {
390
391     vcdinfo_lid_get_pxd(obj, &(p_vcd->pxd), p_vcd->cur_lid);
392     
393     switch (p_vcd->pxd.descriptor_type) {
394     case PSD_TYPE_SELECTION_LIST:
395     case PSD_TYPE_EXT_SELECTION_LIST:
396       if (p_vcd->pxd.psd == NULL) return false;
397       vcdplayer_update_entry( p_input, 
398                               vcdinfo_get_default_offset(p_vcd->vcd, 
399                                                          p_vcd->cur_lid), 
400                               &itemid.num, "default");
401       break;
402
403     case PSD_TYPE_PLAY_LIST: 
404     case PSD_TYPE_END_LIST:
405     case PSD_TYPE_COMMAND_LIST:
406       LOG_WARN( "There is no PBC 'default' selection here" );
407       return false;
408     }
409   } else {
410
411     /* PBC is not on. "default" selection beginning of current 
412        selection . */
413   
414     p_vcd->play_item.num = p_vcd->play_item.num;
415     
416   }
417
418   /** ??? p_vcd->update_title(); ***/
419   return VLC_SUCCESS == VCDPlay( p_input, itemid );
420
421 }
422
423 /*!
424   Play item assocated with the "next" selection.
425
426   Return false if there was some problem.
427 */
428 bool
429 vcdplayer_play_next( input_thread_t * p_input )
430 {
431   thread_vcd_data_t *p_vcd= (thread_vcd_data_t *)p_input->p_access_data;
432
433   vcdinfo_obj_t     *obj  = p_vcd->vcd;
434   vcdinfo_itemid_t   itemid;
435
436   dbg_print( (INPUT_DBG_CALL|INPUT_DBG_PBC), 
437              "current: %d" , p_vcd->play_item.num);
438
439   itemid.type = p_vcd->play_item.type;
440
441   if  (vcdplayer_pbc_is_on(p_vcd)) {
442
443     vcdinfo_lid_get_pxd(obj, &(p_vcd->pxd), p_vcd->cur_lid);
444     
445     switch (p_vcd->pxd.descriptor_type) {
446     case PSD_TYPE_SELECTION_LIST:
447     case PSD_TYPE_EXT_SELECTION_LIST:
448       if (p_vcd->pxd.psd == NULL) return false;
449       vcdplayer_update_entry( p_input, 
450                               vcdinf_psd_get_next_offset(p_vcd->pxd.psd), 
451                               &itemid.num, "next");
452       break;
453
454     case PSD_TYPE_PLAY_LIST: 
455       if (p_vcd->pxd.pld == NULL) return false;
456       vcdplayer_update_entry( p_input, 
457                               vcdinf_pld_get_next_offset(p_vcd->pxd.pld), 
458                               &itemid.num, "next");
459       break;
460       
461     case PSD_TYPE_END_LIST:
462     case PSD_TYPE_COMMAND_LIST:
463       LOG_WARN( "There is no PBC 'next' selection here" );
464       return false;
465     }
466   } else {
467
468     /* PBC is not on. "Next" selection is play_item.num+1 if possible. */
469   
470     int max_entry = 0;
471
472     switch (p_vcd->play_item.type) {
473     case VCDINFO_ITEM_TYPE_ENTRY: 
474     case VCDINFO_ITEM_TYPE_SEGMENT: 
475     case VCDINFO_ITEM_TYPE_TRACK: 
476       
477       switch (p_vcd->play_item.type) {
478       case VCDINFO_ITEM_TYPE_ENTRY: 
479         max_entry = p_vcd->num_entries;
480         break;
481       case VCDINFO_ITEM_TYPE_SEGMENT: 
482         max_entry = p_vcd->num_segments;
483         break;
484       case VCDINFO_ITEM_TYPE_TRACK: 
485         max_entry = p_vcd->num_tracks;
486         break;
487       default: ; /* Handle exceptional cases below */
488       }
489       
490       if (p_vcd->play_item.num+1 < max_entry) {
491         itemid.num = p_vcd->play_item.num+1;
492       } else {
493         LOG_WARN( "At the end - non-PBC 'next' not possible here" );
494         return false;
495       }
496       
497       break;
498       
499     case VCDINFO_ITEM_TYPE_LID: 
500       {
501         /* Should have handled above. */
502         LOG_WARN( "Internal inconsistency - should not have gotten here." );
503         return false;
504       }
505     default: 
506       return false;
507     }
508   }
509
510   /** ??? p_vcd->update_title(); ***/
511   return VLC_SUCCESS == VCDPlay( p_input, itemid );
512
513 }
514
515 /*!
516   Play item assocated with the "prev" selection.
517
518   Return false if there was some problem.
519 */
520 bool
521 vcdplayer_play_prev( input_thread_t * p_input )
522 {
523   thread_vcd_data_t *p_vcd= (thread_vcd_data_t *)p_input->p_access_data;
524
525   vcdinfo_obj_t     *obj  = p_vcd->vcd;
526   vcdinfo_itemid_t   itemid;
527
528   dbg_print( (INPUT_DBG_CALL|INPUT_DBG_PBC), 
529              "current: %d" , p_vcd->play_item.num);
530
531   itemid.type = p_vcd->play_item.type;
532
533   if  (vcdplayer_pbc_is_on(p_vcd)) {
534
535     vcdinfo_lid_get_pxd(obj, &(p_vcd->pxd), p_vcd->cur_lid);
536     
537     switch (p_vcd->pxd.descriptor_type) {
538     case PSD_TYPE_SELECTION_LIST:
539     case PSD_TYPE_EXT_SELECTION_LIST:
540       if (p_vcd->pxd.psd == NULL) return false;
541       vcdplayer_update_entry( p_input, 
542                               vcdinf_psd_get_prev_offset(p_vcd->pxd.psd), 
543                               &itemid.num, "prev");
544       break;
545
546     case PSD_TYPE_PLAY_LIST: 
547       if (p_vcd->pxd.pld == NULL) return false;
548       vcdplayer_update_entry( p_input, 
549                               vcdinf_pld_get_prev_offset(p_vcd->pxd.pld), 
550                               &itemid.num, "prev");
551       break;
552       
553     case PSD_TYPE_END_LIST:
554     case PSD_TYPE_COMMAND_LIST:
555       LOG_WARN( "There is no PBC 'prev' selection here" );
556       return false;
557     }
558   } else {
559
560     /* PBC is not on. "Prev" selection is play_item.num-1 if possible. */
561   
562     int min_entry = (VCDINFO_ITEM_TYPE_ENTRY == p_vcd->play_item.type) 
563       ? 0 : 1;
564     
565     if (p_vcd->play_item.num > min_entry) {
566       itemid.num = p_vcd->play_item.num-1;
567     } else {
568       LOG_WARN( "At the beginning - non-PBC 'prev' not possible here" );
569       return false;
570     }
571       
572   }
573
574   /** ??? p_vcd->update_title(); ***/
575   return VLC_SUCCESS == VCDPlay( p_input, itemid );
576
577 }
578
579 /*!
580   Play item assocated with the "return" selection.
581
582   Return false if there was some problem.
583 */
584 bool
585 vcdplayer_play_return( input_thread_t * p_input )
586 {
587   thread_vcd_data_t *p_vcd= (thread_vcd_data_t *)p_input->p_access_data;
588
589   vcdinfo_obj_t     *obj  = p_vcd->vcd;
590   vcdinfo_itemid_t   itemid;
591
592   dbg_print( (INPUT_DBG_CALL|INPUT_DBG_PBC), 
593              "current: %d" , p_vcd->play_item.num);
594
595   itemid.type = p_vcd->play_item.type;
596
597   if  (vcdplayer_pbc_is_on(p_vcd)) {
598
599     vcdinfo_lid_get_pxd(obj, &(p_vcd->pxd), p_vcd->cur_lid);
600     
601     switch (p_vcd->pxd.descriptor_type) {
602     case PSD_TYPE_SELECTION_LIST:
603     case PSD_TYPE_EXT_SELECTION_LIST:
604       if (p_vcd->pxd.psd == NULL) return false;
605       vcdplayer_update_entry( p_input, 
606                               vcdinf_psd_get_return_offset(p_vcd->pxd.psd), 
607                               &itemid.num, "return");
608       break;
609
610     case PSD_TYPE_PLAY_LIST: 
611       if (p_vcd->pxd.pld == NULL) return false;
612       vcdplayer_update_entry( p_input, 
613                               vcdinf_pld_get_return_offset(p_vcd->pxd.pld), 
614                               &itemid.num, "return");
615       break;
616       
617     case PSD_TYPE_END_LIST:
618     case PSD_TYPE_COMMAND_LIST:
619       LOG_WARN( "There is no PBC 'return' selection here" );
620       return false;
621     }
622   } else {
623
624     /* PBC is not on. "Return" selection is min_entry if possible. */
625   
626     p_vcd->play_item.num = (VCDINFO_ITEM_TYPE_ENTRY == p_vcd->play_item.type) 
627       ? 0 : 1;
628     
629   }
630
631   /** ??? p_vcd->update_title(); ***/
632   return VLC_SUCCESS == VCDPlay( p_input, itemid );
633
634 }