vcdplayer.c
上传用户:riyaled888
上传日期:2009-03-27
资源大小:7338k
文件大小:17k
源码类别:

多媒体

开发平台:

MultiPlatform

  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 8313 2004-07-29 15:18:04Z hartman $
  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.    This contains more of the vlc-independent parts that might be used
  24.    in any VCD input module for a media player. However at present there
  25.    are vlc-specific structures. See also vcdplayer.c of the xine plugin.
  26.  */
  27. /*****************************************************************************
  28.  * Preamble
  29.  *****************************************************************************/
  30. #include <vlc/vlc.h>
  31. #include <vlc/input.h>
  32. #include <vlc/intf.h>
  33. #include "vcd.h"
  34. #include "vcdplayer.h"
  35. #include "intf.h"
  36. #include <string.h>
  37. #include <cdio/cdio.h>
  38. #include <cdio/util.h>
  39. #include <libvcd/info.h>
  40. #ifdef WIN32
  41. #define sleep(A) Sleep((A)*1000)
  42. #endif
  43. /*!
  44.   Return VLC_TRUE if playback control (PBC) is on
  45. */
  46. vlc_bool_t vcdplayer_pbc_is_on( const thread_vcd_data_t *p_vcd ) 
  47. {
  48.   return VCDINFO_INVALID_ENTRY != p_vcd->cur_lid; 
  49. }
  50. static void vcdplayer_update_entry( input_thread_t * p_input, uint16_t ofs, 
  51.                         uint16_t *entry, const char *label)
  52. {
  53.   thread_vcd_data_t *     p_vcd= (thread_vcd_data_t *)p_input->p_access_data;
  54.   if ( ofs == VCDINFO_INVALID_OFFSET ) {
  55.     *entry = VCDINFO_INVALID_ENTRY;
  56.   } else {
  57.     vcdinfo_offset_t *off = vcdinfo_get_offset_t(p_vcd->vcd, ofs);
  58.     if (off != NULL) {
  59.       *entry = off->lid;
  60.       dbg_print(INPUT_DBG_PBC, "%s: LID %dn", label, off->lid);
  61.     } else
  62.       *entry = VCDINFO_INVALID_ENTRY;
  63.   }
  64. }
  65. /* Handles navigation when NOT in PBC reaching the end of a play item. 
  66.    The navigations rules here may be sort of made up, but the intent 
  67.    is to do something that's probably right or helpful.
  68.    return VLC_TRUE if the caller should return.
  69. */
  70. vcdplayer_read_status_t vcdplayer_non_pbc_nav ( input_thread_t * p_input )
  71. {
  72.   thread_vcd_data_t *     p_vcd= (thread_vcd_data_t *)p_input->p_access_data;
  73.   /* Not in playback control. Do we advance automatically or stop? */
  74.   switch (p_vcd->play_item.type) {
  75.   case VCDINFO_ITEM_TYPE_TRACK:
  76.   case VCDINFO_ITEM_TYPE_ENTRY: {
  77.     dbg_print( INPUT_DBG_LSN, "new track %d, lsn %d", p_vcd->cur_track, 
  78.                p_vcd->p_sectors[p_vcd->cur_track+1] );
  79.     return READ_END;
  80.     break;
  81.   }
  82.   case VCDINFO_ITEM_TYPE_SPAREID2:  
  83.     dbg_print( (INPUT_DBG_STILL|INPUT_DBG_LSN), 
  84.                "SPAREID2" );
  85.     /* FIXME */
  86.     p_input->stream.b_seekable = 0;
  87.     if (p_vcd->in_still)
  88.     {
  89.       return READ_STILL_FRAME ;
  90.     }
  91.     return READ_END;
  92.   case VCDINFO_ITEM_TYPE_NOTFOUND:  
  93.     LOG_ERR ("NOTFOUND outside PBC -- not supposed to happen");
  94.     return READ_ERROR;
  95.   case VCDINFO_ITEM_TYPE_LID:  
  96.     LOG_ERR ("LID outside PBC -- not supposed to happen");
  97.     return READ_ERROR;
  98.   case VCDINFO_ITEM_TYPE_SEGMENT:
  99.       /* Hack: Just go back and do still again */
  100.     /* FIXME */
  101.     p_input->stream.b_seekable = 0;
  102.     if (p_vcd->in_still) 
  103.     {
  104.       dbg_print( (INPUT_DBG_STILL|INPUT_DBG_LSN), 
  105.                  "End of Segment - looping" );
  106.       return READ_STILL_FRAME;
  107.     }
  108.     return READ_END;
  109.   }
  110.   return READ_BLOCK;
  111. }
  112. /* Handles PBC navigation when reaching the end of a play item. */
  113. vcdplayer_read_status_t
  114. vcdplayer_pbc_nav ( input_thread_t * p_input )
  115. {
  116.   thread_vcd_data_t *     p_vcd= (thread_vcd_data_t *)p_input->p_access_data;
  117.   /* We are in playback control. */
  118.   vcdinfo_itemid_t itemid;
  119.   /* The end of an entry is really the end of the associated 
  120.      sequence (or track). */
  121.   
  122.   if ( (VCDINFO_ITEM_TYPE_ENTRY == p_vcd->play_item.type) && 
  123.        (p_vcd->cur_lsn < p_vcd->end_lsn) ) {
  124.     /* Set up to just continue to the next entry */
  125.     p_vcd->play_item.num++;
  126.     dbg_print( (INPUT_DBG_LSN|INPUT_DBG_PBC), 
  127.                "continuing into next entry: %u", p_vcd->play_item.num);
  128.     VCDPlay( p_input, p_vcd->play_item );
  129.     /* p_vcd->update_title(); */
  130.     return READ_BLOCK;
  131.   }
  132.   
  133.   switch (p_vcd->pxd.descriptor_type) {
  134.   case PSD_TYPE_END_LIST:
  135.     return READ_END;
  136.     break;
  137.   case PSD_TYPE_PLAY_LIST: {
  138.     int wait_time = vcdinf_get_wait_time(p_vcd->pxd.pld);
  139.     
  140.     dbg_print(INPUT_DBG_PBC, "playlist wait_time: %d", wait_time);
  141.     
  142.     if (vcdplayer_inc_play_item(p_input))
  143.       return READ_BLOCK;
  144.     /* Handle any wait time given. */
  145.     if (p_vcd->in_still) {
  146.       vcdIntfStillTime( p_vcd->p_intf, wait_time );
  147.       return READ_STILL_FRAME;
  148.     }
  149.     vcdplayer_update_entry( p_input, 
  150.                             vcdinf_pld_get_next_offset(p_vcd->pxd.pld),
  151.                             &itemid.num, "next" );
  152.     itemid.type = VCDINFO_ITEM_TYPE_LID;
  153.     VCDPlay( p_input, itemid );
  154.     break;
  155.   }
  156.   case PSD_TYPE_SELECTION_LIST:     /* Selection List (+Ext. for SVCD) */
  157.   case PSD_TYPE_EXT_SELECTION_LIST: /* Extended Selection List (VCD2.0) */
  158.     {
  159.       int wait_time         = vcdinf_get_timeout_time(p_vcd->pxd.psd);
  160.       uint16_t timeout_offs = vcdinf_get_timeout_offset(p_vcd->pxd.psd);
  161.       uint16_t max_loop     = vcdinf_get_loop_count(p_vcd->pxd.psd);
  162.       vcdinfo_offset_t *offset_timeout_LID = 
  163.         vcdinfo_get_offset_t(p_vcd->vcd, timeout_offs);
  164.       
  165.       dbg_print(INPUT_DBG_PBC, "wait_time: %d, looped: %d, max_loop %d", 
  166.                 wait_time, p_vcd->loop_count, max_loop);
  167.       
  168.       /* Handle any wait time given */
  169.       if (p_vcd->in_still) {
  170. vcdIntfStillTime( p_vcd->p_intf, wait_time );
  171. return READ_STILL_FRAME;
  172.       } 
  173.       
  174.       /* Handle any looping given. */
  175.       if ( max_loop == 0 || p_vcd->loop_count < max_loop ) {
  176.         p_vcd->loop_count++;
  177.         if (p_vcd->loop_count == 0x7f) p_vcd->loop_count = 0;
  178.         VCDSeek( p_input, 0 );
  179.         /* if (p_vcd->in_still) p_vcd->force_redisplay();*/
  180.         return READ_BLOCK;
  181.       }
  182.       
  183.       /* Looping finished and wait finished. Move to timeout
  184.          entry or next entry, or handle still. */
  185.       
  186.       if (NULL != offset_timeout_LID) {
  187.         /* Handle timeout_LID */
  188.         itemid.num  = offset_timeout_LID->lid;
  189.         itemid.type = VCDINFO_ITEM_TYPE_LID;
  190.         dbg_print(INPUT_DBG_PBC, "timeout to: %d", itemid.num);
  191.         VCDPlay( p_input, itemid );
  192.         return READ_BLOCK;
  193.       } else {
  194.         int num_selections = vcdinf_get_num_selections(p_vcd->pxd.psd);
  195.         if (num_selections > 0) {
  196.           /* Pick a random selection. */
  197.           unsigned int bsn=vcdinf_get_bsn(p_vcd->pxd.psd);
  198.           int rand_selection=bsn +
  199.             (int) ((num_selections+0.0)*rand()/(RAND_MAX+1.0));
  200.           lid_t rand_lid=vcdinfo_selection_get_lid (p_vcd->vcd, 
  201.     p_vcd->cur_lid, 
  202.     rand_selection);
  203.           itemid.num = rand_lid;
  204.           itemid.type = VCDINFO_ITEM_TYPE_LID;
  205.           dbg_print(INPUT_DBG_PBC, "random selection %d, lid: %d", 
  206.                     rand_selection - bsn, rand_lid);
  207.           VCDPlay( p_input, itemid );
  208.           return READ_BLOCK;
  209.         } else if (p_vcd->in_still) {
  210.           /* Hack: Just go back and do still again */
  211.           sleep(1);
  212.           return READ_STILL_FRAME;
  213.         }
  214.       }
  215.       break;
  216.     }
  217.   case VCDINFO_ITEM_TYPE_NOTFOUND:  
  218.     LOG_ERR( "NOTFOUND in PBC -- not supposed to happen" );
  219.     break;
  220.   case VCDINFO_ITEM_TYPE_SPAREID2:  
  221.     LOG_ERR( "SPAREID2 in PBC -- not supposed to happen" );
  222.     break;
  223.   case VCDINFO_ITEM_TYPE_LID:  
  224.     LOG_ERR( "LID in PBC -- not supposed to happen" );
  225.     break;
  226.     
  227.   default:
  228.     ;
  229.   }
  230.   /* FIXME: Should handle autowait ...  */
  231.   return READ_ERROR;
  232. }
  233. /*!
  234.   Get the next play-item in the list given in the LIDs. Note play-item
  235.   here refers to list of play-items for a single LID It shouldn't be
  236.   confused with a user's list of favorite things to play or the 
  237.   "next" field of a LID which moves us to a different LID.
  238.  */
  239. vlc_bool_t vcdplayer_inc_play_item( input_thread_t *p_input )
  240. {
  241.   thread_vcd_data_t *     p_vcd= (thread_vcd_data_t *)p_input->p_access_data;
  242.   int noi;
  243.   dbg_print(INPUT_DBG_CALL, "called pli: %d", p_vcd->pdi);
  244.   if ( NULL == p_vcd || NULL == p_vcd->pxd.pld  ) return VLC_FALSE;
  245.   noi = vcdinf_pld_get_noi(p_vcd->pxd.pld);
  246.   
  247.   if ( noi <= 0 ) return VLC_FALSE;
  248.   
  249.   /* Handle delays like autowait or wait here? */
  250.   p_vcd->pdi++;
  251.   if ( p_vcd->pdi < 0 || p_vcd->pdi >= noi ) return VLC_FALSE;
  252.   else {
  253.     uint16_t trans_itemid_num=vcdinf_pld_get_play_item(p_vcd->pxd.pld, 
  254.                                                        p_vcd->pdi);
  255.     vcdinfo_itemid_t trans_itemid;
  256.     if (VCDINFO_INVALID_ITEMID == trans_itemid_num) return VLC_FALSE;
  257.     
  258.     vcdinfo_classify_itemid(trans_itemid_num, &trans_itemid);
  259.     dbg_print(INPUT_DBG_PBC, "  play-item[%d]: %s",
  260.               p_vcd->pdi, vcdinfo_pin2str (trans_itemid_num));
  261.     return VLC_SUCCESS == VCDPlay( p_input, trans_itemid );
  262.   }
  263. }
  264. /*!
  265.   Play item assocated with the "default" selection.
  266.   Return VLC_FALSE if there was some problem.
  267. */
  268. vlc_bool_t vcdplayer_play_default( input_thread_t * p_input )
  269. {
  270.   thread_vcd_data_t *p_vcd= (thread_vcd_data_t *)p_input->p_access_data;
  271.   vcdinfo_itemid_t   itemid;
  272.   if (!p_vcd) {
  273.     dbg_print( (INPUT_DBG_CALL|INPUT_DBG_PBC), 
  274.        "null p_vcd" );
  275.     return VLC_EGENERIC;
  276.   }
  277.   
  278.   dbg_print( (INPUT_DBG_CALL|INPUT_DBG_PBC), 
  279.      "current: %d" , p_vcd->play_item.num);
  280.   itemid.type = p_vcd->play_item.type;
  281.   if  (vcdplayer_pbc_is_on(p_vcd)) {
  282. #if defined(LIBVCD_VERSION)
  283.     lid_t lid=vcdinfo_get_multi_default_lid(p_vcd->vcd, p_vcd->cur_lid,
  284.     p_vcd->cur_lsn);
  285.     if (VCDINFO_INVALID_LID != lid) {
  286.       itemid.num  = lid;
  287.       itemid.type = VCDINFO_ITEM_TYPE_LID;
  288.       dbg_print(INPUT_DBG_PBC, "DEFAULT to %dn", itemid.num);
  289.     } else {
  290.       dbg_print(INPUT_DBG_PBC, "no DEFAULT for LID %dn", p_vcd->cur_lid);
  291.     }
  292. #else 
  293.     vcdinfo_lid_get_pxd(p_vcd->vcd, &(p_vcd->pxd), p_vcd->cur_lid);
  294.     
  295.     switch (p_vcd->pxd.descriptor_type) {
  296.     case PSD_TYPE_SELECTION_LIST:
  297.     case PSD_TYPE_EXT_SELECTION_LIST:
  298.       if (p_vcd->pxd.psd == NULL) return VLC_FALSE;
  299.       vcdplayer_update_entry( p_input, 
  300.       vcdinfo_get_default_offset(p_vcd->vcd, 
  301.  p_vcd->cur_lid), 
  302.       &itemid.num, "default");
  303.       break;
  304.     case PSD_TYPE_PLAY_LIST: 
  305.     case PSD_TYPE_END_LIST:
  306.     case PSD_TYPE_COMMAND_LIST:
  307.       LOG_WARN( "There is no PBC 'default' selection here" );
  308.       return VLC_FALSE;
  309.     }
  310. #endif /* LIBVCD_VERSION (< 0.7.21) */
  311.     
  312.   } else {
  313.     /* PBC is not on. "default" selection beginning of current 
  314.        selection . */
  315.   
  316.     p_vcd->play_item.num = p_vcd->play_item.num;
  317.     
  318.   }
  319.   /** ??? p_vcd->update_title(); ***/
  320.   return VLC_SUCCESS == VCDPlay( p_input, itemid );
  321. }
  322. /*!
  323.   Play item assocated with the "next" selection.
  324.   Return VLC_FALSE if there was some problem.
  325. */
  326. vlc_bool_t vcdplayer_play_next( input_thread_t * p_input )
  327. {
  328.   thread_vcd_data_t *p_vcd= (thread_vcd_data_t *)p_input->p_access_data;
  329.   vcdinfo_obj_t     *obj;
  330.   vcdinfo_itemid_t   itemid;
  331.   if (!p_vcd) return VLC_FALSE;
  332.   dbg_print( (INPUT_DBG_CALL|INPUT_DBG_PBC), 
  333.      "current: %d" , p_vcd->play_item.num);
  334.   obj = p_vcd->vcd;
  335.   itemid.type = p_vcd->play_item.type;
  336.   if  (vcdplayer_pbc_is_on(p_vcd)) {
  337.     vcdinfo_lid_get_pxd(obj, &(p_vcd->pxd), p_vcd->cur_lid);
  338.     
  339.     switch (p_vcd->pxd.descriptor_type) {
  340.     case PSD_TYPE_SELECTION_LIST:
  341.     case PSD_TYPE_EXT_SELECTION_LIST:
  342.       if (p_vcd->pxd.psd == NULL) return VLC_FALSE;
  343.       vcdplayer_update_entry( p_input, 
  344.       vcdinf_psd_get_next_offset(p_vcd->pxd.psd), 
  345.       &itemid.num, "next");
  346.       itemid.type = VCDINFO_ITEM_TYPE_LID;
  347.       break;
  348.     case PSD_TYPE_PLAY_LIST: 
  349.       if (p_vcd->pxd.pld == NULL) return VLC_FALSE;
  350.       vcdplayer_update_entry( p_input, 
  351.       vcdinf_pld_get_next_offset(p_vcd->pxd.pld), 
  352.       &itemid.num, "next");
  353.       itemid.type = VCDINFO_ITEM_TYPE_LID;
  354.       break;
  355.       
  356.     case PSD_TYPE_END_LIST:
  357.     case PSD_TYPE_COMMAND_LIST:
  358.       LOG_WARN( "There is no PBC 'next' selection here" );
  359.       return VLC_FALSE;
  360.     }
  361.   } else {
  362.     /* PBC is not on. "Next" selection is play_item.num+1 if possible. */
  363.   
  364.     int max_entry = 0;
  365.     switch (p_vcd->play_item.type) {
  366.     case VCDINFO_ITEM_TYPE_ENTRY: 
  367.     case VCDINFO_ITEM_TYPE_SEGMENT: 
  368.     case VCDINFO_ITEM_TYPE_TRACK: 
  369.       
  370.       switch (p_vcd->play_item.type) {
  371.       case VCDINFO_ITEM_TYPE_ENTRY: 
  372. max_entry = p_vcd->num_entries;
  373. break;
  374.       case VCDINFO_ITEM_TYPE_SEGMENT: 
  375. max_entry = p_vcd->num_segments;
  376. break;
  377.       case VCDINFO_ITEM_TYPE_TRACK: 
  378. max_entry = p_vcd->num_tracks;
  379. break;
  380.       default: ; /* Handle exceptional cases below */
  381.       }
  382.       
  383.       if (p_vcd->play_item.num+1 < max_entry) {
  384. itemid.num = p_vcd->play_item.num+1;
  385.       } else {
  386. LOG_WARN( "At the end - non-PBC 'next' not possible here" );
  387. return VLC_FALSE;
  388.       }
  389.       
  390.       break;
  391.       
  392.     case VCDINFO_ITEM_TYPE_LID: 
  393.       {
  394. /* Should have handled above. */
  395. LOG_WARN( "Internal inconsistency - should not have gotten here." );
  396. return VLC_FALSE;
  397.       }
  398.     default: 
  399.       return VLC_FALSE;
  400.     }
  401.   }
  402.   /** ??? p_vcd->update_title(); ***/
  403.   return VLC_SUCCESS == VCDPlay( p_input, itemid );
  404. }
  405. /*!
  406.   Play item assocated with the "prev" selection.
  407.   Return VLC_FALSE if there was some problem.
  408. */
  409. vlc_bool_t vcdplayer_play_prev( input_thread_t * p_input )
  410. {
  411.   thread_vcd_data_t *p_vcd= (thread_vcd_data_t *)p_input->p_access_data;
  412.   vcdinfo_obj_t     *obj  = p_vcd->vcd;
  413.   vcdinfo_itemid_t   itemid;
  414.   dbg_print( (INPUT_DBG_CALL|INPUT_DBG_PBC), 
  415.      "current: %d" , p_vcd->play_item.num);
  416.   itemid.type = p_vcd->play_item.type;
  417.   if  (vcdplayer_pbc_is_on(p_vcd)) {
  418.     vcdinfo_lid_get_pxd(obj, &(p_vcd->pxd), p_vcd->cur_lid);
  419.     
  420.     switch (p_vcd->pxd.descriptor_type) {
  421.     case PSD_TYPE_SELECTION_LIST:
  422.     case PSD_TYPE_EXT_SELECTION_LIST:
  423.       if (p_vcd->pxd.psd == NULL) return VLC_FALSE;
  424.       vcdplayer_update_entry( p_input, 
  425.       vcdinf_psd_get_prev_offset(p_vcd->pxd.psd), 
  426.       &itemid.num, "prev");
  427.       itemid.type = VCDINFO_ITEM_TYPE_LID;
  428.       break;
  429.     case PSD_TYPE_PLAY_LIST: 
  430.       if (p_vcd->pxd.pld == NULL) return VLC_FALSE;
  431.       vcdplayer_update_entry( p_input, 
  432.       vcdinf_pld_get_prev_offset(p_vcd->pxd.pld), 
  433.       &itemid.num, "prev");
  434.       itemid.type = VCDINFO_ITEM_TYPE_LID;
  435.       break;
  436.       
  437.     case PSD_TYPE_END_LIST:
  438.     case PSD_TYPE_COMMAND_LIST:
  439.       LOG_WARN( "There is no PBC 'prev' selection here" );
  440.       return VLC_FALSE;
  441.     }
  442.   } else {
  443.     /* PBC is not on. "Prev" selection is play_item.num-1 if possible. */
  444.   
  445.     int min_entry = (VCDINFO_ITEM_TYPE_ENTRY == p_vcd->play_item.type) 
  446.       ? 0 : 1;
  447.     
  448.     if (p_vcd->play_item.num > min_entry) {
  449.       itemid.num = p_vcd->play_item.num-1;
  450.     } else {
  451.       LOG_WARN( "At the beginning - non-PBC 'prev' not possible here" );
  452.       return VLC_FALSE;
  453.     }
  454.       
  455.   }
  456.   /** ??? p_vcd->update_title(); ***/
  457.   return VLC_SUCCESS == VCDPlay( p_input, itemid );
  458. }
  459. /*!
  460.   Play item assocated with the "return" selection.
  461.   Return VLC_FALSE if there was some problem.
  462. */
  463. vlc_bool_t vcdplayer_play_return( input_thread_t * p_input )
  464. {
  465.   thread_vcd_data_t *p_vcd= (thread_vcd_data_t *)p_input->p_access_data;
  466.   vcdinfo_obj_t     *obj  = p_vcd->vcd;
  467.   vcdinfo_itemid_t   itemid;
  468.   dbg_print( (INPUT_DBG_CALL|INPUT_DBG_PBC), 
  469.      "current: %d" , p_vcd->play_item.num);
  470.   itemid.type = p_vcd->play_item.type;
  471.   if  (vcdplayer_pbc_is_on(p_vcd)) {
  472.     vcdinfo_lid_get_pxd(obj, &(p_vcd->pxd), p_vcd->cur_lid);
  473.     
  474.     switch (p_vcd->pxd.descriptor_type) {
  475.     case PSD_TYPE_SELECTION_LIST:
  476.     case PSD_TYPE_EXT_SELECTION_LIST:
  477.       if (p_vcd->pxd.psd == NULL) return VLC_FALSE;
  478.       vcdplayer_update_entry( p_input, 
  479.       vcdinf_psd_get_return_offset(p_vcd->pxd.psd), 
  480.       &itemid.num, "return");
  481.       itemid.type = VCDINFO_ITEM_TYPE_LID;
  482.       break;
  483.     case PSD_TYPE_PLAY_LIST: 
  484.       if (p_vcd->pxd.pld == NULL) return VLC_FALSE;
  485.       vcdplayer_update_entry( p_input, 
  486.       vcdinf_pld_get_return_offset(p_vcd->pxd.pld), 
  487.       &itemid.num, "return");
  488.       itemid.type = VCDINFO_ITEM_TYPE_LID;
  489.       break;
  490.       
  491.     case PSD_TYPE_END_LIST:
  492.     case PSD_TYPE_COMMAND_LIST:
  493.       LOG_WARN( "There is no PBC 'return' selection here" );
  494.       return VLC_FALSE;
  495.     }
  496.   } else {
  497.     /* PBC is not on. "Return" selection is min_entry if possible. */
  498.   
  499.     p_vcd->play_item.num = (VCDINFO_ITEM_TYPE_ENTRY == p_vcd->play_item.type) 
  500.       ? 0 : 1;
  501.     
  502.   }
  503.   /** ??? p_vcd->update_title(); ***/
  504.   return VLC_SUCCESS == VCDPlay( p_input, itemid );
  505. }