Exemple #1
0
/*!
  Play item assocated with the "prev" selection.

  Return false if there was some problem.
*/
bool
vcdplayer_play_prev( access_t * p_access )
{
  vcdplayer_t      *p_vcdplayer= (vcdplayer_t *)p_access->p_sys;
  vcdinfo_obj_t    *p_vcdinfo  = p_vcdplayer->vcd;
  vcdinfo_itemid_t  itemid;

  dbg_print( (INPUT_DBG_CALL|INPUT_DBG_PBC),
         "current: %d" , p_vcdplayer->play_item.num);

  itemid = p_vcdplayer->play_item;

  if (vcdplayer_pbc_is_on(p_vcdplayer)) {

    vcdinfo_lid_get_pxd(p_vcdinfo, &(p_vcdplayer->pxd), p_vcdplayer->i_lid);
 
    switch (p_vcdplayer->pxd.descriptor_type) {
    case PSD_TYPE_SELECTION_LIST:
    case PSD_TYPE_EXT_SELECTION_LIST:
      if (p_vcdplayer->pxd.psd == NULL) return false;
      vcdplayer_update_entry(p_access,
                             vcdinf_psd_get_prev_offset(p_vcdplayer->pxd.psd),
                             &itemid.num, "prev");
      itemid.type = VCDINFO_ITEM_TYPE_LID;
      break;

    case PSD_TYPE_PLAY_LIST:
      if (p_vcdplayer->pxd.pld == NULL) return false;
      vcdplayer_update_entry(p_access,
                             vcdinf_pld_get_prev_offset(p_vcdplayer->pxd.pld),
                             &itemid.num, "prev");
      itemid.type = VCDINFO_ITEM_TYPE_LID;
      break;
 
    case PSD_TYPE_END_LIST:
    case PSD_TYPE_COMMAND_LIST:
      LOG_WARN( "There is no PBC 'prev' selection here" );
      return false;
    }
  } else {

    /* PBC is not on. "Prev" selection is play_item.num-1 if possible. */
 
    int min_entry = (VCDINFO_ITEM_TYPE_ENTRY == p_vcdplayer->play_item.type)
                    ? 0 : 1;
 
    if (p_vcdplayer->play_item.num > min_entry) {
      itemid.num = p_vcdplayer->play_item.num-1;
    } else {
      LOG_WARN( "At the beginning - non-PBC 'prev' not possible here" );
      return false;
    }
 
  }

  /** ??? p_vcdplayer->update_title(); ***/
  vcdplayer_play( p_access, itemid );
  return VLC_SUCCESS;

}
Exemple #2
0
/*!
  Play item assocated with the "prev" selection.

  Return VLC_FALSE if there was some problem.
*/
vlc_bool_t vcdplayer_play_prev( input_thread_t * p_input )
{
  thread_vcd_data_t *p_vcd= (thread_vcd_data_t *)p_input->p_access_data;

  vcdinfo_obj_t     *obj  = p_vcd->vcd;
  vcdinfo_itemid_t   itemid;

  dbg_print( (INPUT_DBG_CALL|INPUT_DBG_PBC), 
	     "current: %d" , p_vcd->play_item.num);

  itemid.type = p_vcd->play_item.type;

  if  (vcdplayer_pbc_is_on(p_vcd)) {

    vcdinfo_lid_get_pxd(obj, &(p_vcd->pxd), p_vcd->cur_lid);
    
    switch (p_vcd->pxd.descriptor_type) {
    case PSD_TYPE_SELECTION_LIST:
    case PSD_TYPE_EXT_SELECTION_LIST:
      if (p_vcd->pxd.psd == NULL) return VLC_FALSE;
      vcdplayer_update_entry( p_input, 
			      vcdinf_psd_get_prev_offset(p_vcd->pxd.psd), 
			      &itemid.num, "prev");
      itemid.type = VCDINFO_ITEM_TYPE_LID;
      break;

    case PSD_TYPE_PLAY_LIST: 
      if (p_vcd->pxd.pld == NULL) return VLC_FALSE;
      vcdplayer_update_entry( p_input, 
			      vcdinf_pld_get_prev_offset(p_vcd->pxd.pld), 
			      &itemid.num, "prev");
      itemid.type = VCDINFO_ITEM_TYPE_LID;
      break;
      
    case PSD_TYPE_END_LIST:
    case PSD_TYPE_COMMAND_LIST:
      LOG_WARN( "There is no PBC 'prev' selection here" );
      return VLC_FALSE;
    }
  } else {

    /* PBC is not on. "Prev" selection is play_item.num-1 if possible. */
  
    int min_entry = (VCDINFO_ITEM_TYPE_ENTRY == p_vcd->play_item.type) 
      ? 0 : 1;
    
    if (p_vcd->play_item.num > min_entry) {
      itemid.num = p_vcd->play_item.num-1;
    } else {
      LOG_WARN( "At the beginning - non-PBC 'prev' not possible here" );
      return VLC_FALSE;
    }
      
  }

  /** ??? p_vcd->update_title(); ***/
  return VLC_SUCCESS == VCDPlay( p_input, itemid );

}
Exemple #3
0
void
vcdplayer_play(access_t *p_access, vcdinfo_itemid_t itemid)
{
  vcdplayer_t *p_vcdplayer = (vcdplayer_t *)p_access->p_sys;

  dbg_print(INPUT_DBG_CALL, "called itemid.num: %d itemid.type: %d",
            itemid.num, itemid.type);

  if  (!vcdplayer_pbc_is_on(p_vcdplayer)) {
    vcdplayer_play_single_item(p_access, itemid);
  } else {
    /* PBC on - Itemid.num is LID. */

    vcdinfo_obj_t *p_vcdinfo = p_vcdplayer->vcd;

    if (p_vcdinfo == NULL)
      return;

    p_vcdplayer->i_lid = itemid.num;
    vcdinfo_lid_get_pxd(p_vcdinfo, &(p_vcdplayer->pxd), itemid.num);
 
    switch (p_vcdplayer->pxd.descriptor_type) {
 
    case PSD_TYPE_SELECTION_LIST:
    case PSD_TYPE_EXT_SELECTION_LIST: {
      vcdinfo_itemid_t trans_itemid;
      uint16_t trans_itemid_num;

      if (p_vcdplayer->pxd.psd == NULL) return;
      trans_itemid_num  = vcdinf_psd_get_itemid(p_vcdplayer->pxd.psd);
      vcdinfo_classify_itemid(trans_itemid_num, &trans_itemid);
      p_vcdplayer->i_loop     = 1;
      p_vcdplayer->loop_item  = trans_itemid;
      vcdplayer_play_single_item(p_access, trans_itemid);
      break;
    }
 
    case PSD_TYPE_PLAY_LIST: {
      if (p_vcdplayer->pxd.pld == NULL) return;
      p_vcdplayer->pdi = -1;
      vcdplayer_inc_play_item(p_access);
      break;
    }
 
    case PSD_TYPE_END_LIST:
    case PSD_TYPE_COMMAND_LIST:
 
    default:
      ;
    }
  }
}
Exemple #4
0
/*!
   Take a format string and expand escape sequences, that is sequences that
   begin with %, with information from the current VCD.
   The expanded string is returned. Here is a list of escape sequences:

   %A : The album information
   %C : The VCD volume count - the number of CD's in the collection.
   %c : The VCD volume num - the number of the CD in the collection.
   %F : The VCD Format, e.g. VCD 1.0, VCD 1.1, VCD 2.0, or SVCD
   %I : The current entry/segment/playback type, e.g. ENTRY, TRACK, SEGMENT...
   %L : The playlist ID prefixed with " LID" if it exists
   %M : MRL
   %N : The current number of the %I - a decimal number
   %P : The publisher ID
   %p : The preparer ID
   %S : If we are in a segment (menu), the kind of segment
   %T : The track number
   %V : The volume set ID
   %v : The volume ID
       A number between 1 and the volume count.
   %% : a %
*/
static char *
VCDFormatStr(vcdplayer_t *p_vcdplayer,
             const char *format_str, const char *mrl,
             const vcdinfo_itemid_t *itemid)
{
#define TEMP_STR_SIZE 256
  char        temp_str[TEMP_STR_SIZE];
  char       *tp = temp_str;
  const char *te = tp+TEMP_STR_SIZE-1;
  bool        saw_control_prefix = false;

  memset(temp_str, 0, TEMP_STR_SIZE);

  for (; *format_str && tp<te; ++format_str) {

    if (!saw_control_prefix && *format_str != '%') {
      *tp++ = *format_str;
      saw_control_prefix = false;
      continue;
    }

    switch(*format_str) {
    case '%':
      if (saw_control_prefix) {
        *tp++ = '%';
      }
      saw_control_prefix = !saw_control_prefix;
      break;
    case 'A':
      tp += snprintf(tp,te-tp,"%s",
                   vcdinfo_strip_trail(vcdinfo_get_album_id(p_vcdplayer->vcd),
                                                              MAX_ALBUM_LEN));
      break;

    case 'c':
      tp += snprintf(tp,te-tp,"%d",vcdinfo_get_volume_num(p_vcdplayer->vcd));
      break;

    case 'C':
      tp += snprintf(tp,te-tp,"%d",vcdinfo_get_volume_count(p_vcdplayer->vcd));
      break;

    case 'F':
      tp += snprintf(tp,te-tp,"%s",
                            vcdinfo_get_format_version_str(p_vcdplayer->vcd));
      break;

    case 'I':
      {
        switch (itemid->type) {
        case VCDINFO_ITEM_TYPE_TRACK:
          tp += snprintf(tp,te-tp,"%s",_("Track"));
        break;
        case VCDINFO_ITEM_TYPE_ENTRY:
          tp += snprintf(tp,te-tp,"%s",_("Entry"));
          break;
        case VCDINFO_ITEM_TYPE_SEGMENT:
          tp += snprintf(tp,te-tp,"%s",_("Segment"));
          break;
        case VCDINFO_ITEM_TYPE_LID:
          tp += snprintf(tp,te-tp,"%s",_("List ID"));
          break;
        case VCDINFO_ITEM_TYPE_SPAREID2:
          tp += snprintf(tp,te-tp,"%s",_("Navigation"));
          break;
        default:
          /* What to do? */
          ;
        }
        saw_control_prefix = false;
      }
      break;

    case 'L':
      if (vcdplayer_pbc_is_on(p_vcdplayer))
        tp += snprintf(tp,te-tp,"%s %d",_("List ID"),p_vcdplayer->i_lid);
      saw_control_prefix = false;
      break;

    case 'M':
      tp += snprintf(tp,te-tp,"%s",mrl);
      break;

    case 'N':
      tp += snprintf(tp,te-tp,"%d",itemid->num);
      break;

    case 'p':
      tp += snprintf(tp,te-tp,"%s",vcdinfo_get_preparer_id(p_vcdplayer->vcd));
      break;

    case 'P':
      tp += snprintf(tp,te-tp,"%s",vcdinfo_get_publisher_id(p_vcdplayer->vcd));
      break;

    case 'S':
      if ( VCDINFO_ITEM_TYPE_SEGMENT==itemid->type ) {
        tp += snprintf(tp,te-tp," %s",
                vcdinfo_video_type2str(p_vcdplayer->vcd, itemid->num));
      }
      saw_control_prefix = false;
      break;

    case 'T':
      tp += snprintf(tp,te-tp,"%d",p_vcdplayer->i_track);
      break;

    case 'V':
      tp += snprintf(tp,te-tp,"%s",vcdinfo_get_volumeset_id(p_vcdplayer->vcd));
      break;

    case 'v':
      tp += snprintf(tp,te-tp,"%s",vcdinfo_get_volume_id(p_vcdplayer->vcd));
      break;

    default:
      *tp++ = '%';
      if(tp<te)
        *tp++ = *format_str;
      saw_control_prefix = false;
    }
  }
  return strdup(temp_str);
}
Exemple #5
0
/*!
  Play item assocated with the "next" selection.

  Return false if there was some problem.
*/
bool
vcdplayer_play_next( access_t * p_access )
{
  vcdplayer_t *p_vcdplayer= (vcdplayer_t *)p_access->p_sys;

  vcdinfo_obj_t     *p_vcdinfo;
  vcdinfo_itemid_t   itemid;

  if (!p_vcdplayer) return false;

  dbg_print( (INPUT_DBG_CALL|INPUT_DBG_PBC),
         "current: %d" , p_vcdplayer->play_item.num);

  p_vcdinfo = p_vcdplayer->vcd;

  itemid = p_vcdplayer->play_item;

  if  (vcdplayer_pbc_is_on(p_vcdplayer)) {

    vcdinfo_lid_get_pxd(p_vcdinfo, &(p_vcdplayer->pxd), p_vcdplayer->i_lid);
 
    switch (p_vcdplayer->pxd.descriptor_type) {
    case PSD_TYPE_SELECTION_LIST:
    case PSD_TYPE_EXT_SELECTION_LIST:
      if (p_vcdplayer->pxd.psd == NULL) return false;
      vcdplayer_update_entry(p_access,
                             vcdinf_psd_get_next_offset(p_vcdplayer->pxd.psd),
                             &itemid.num, "next");
      itemid.type = VCDINFO_ITEM_TYPE_LID;
      break;

    case PSD_TYPE_PLAY_LIST:
      if (p_vcdplayer->pxd.pld == NULL) return false;
      vcdplayer_update_entry(p_access,
                             vcdinf_pld_get_next_offset(p_vcdplayer->pxd.pld),
                             &itemid.num, "next");
      itemid.type = VCDINFO_ITEM_TYPE_LID;
      break;
 
    case PSD_TYPE_END_LIST:
    case PSD_TYPE_COMMAND_LIST:
      LOG_WARN( "There is no PBC 'next' selection here" );
      return false;
    }
  } else {

    /* PBC is not on. "Next" selection is play_item.num+1 if possible. */
 
    int max_entry = 0;

    switch (p_vcdplayer->play_item.type) {
    case VCDINFO_ITEM_TYPE_ENTRY:
    case VCDINFO_ITEM_TYPE_SEGMENT:
    case VCDINFO_ITEM_TYPE_TRACK:
 
      switch (p_vcdplayer->play_item.type) {
      case VCDINFO_ITEM_TYPE_ENTRY:
        max_entry = p_vcdplayer->i_entries;
        break;
      case VCDINFO_ITEM_TYPE_SEGMENT:
        max_entry = p_vcdplayer->i_segments;
        break;
      case VCDINFO_ITEM_TYPE_TRACK:
        max_entry = p_vcdplayer->i_tracks;
        break;
      default: ; /* Handle exceptional cases below */
      }
 
      if (p_vcdplayer->play_item.num+1 < max_entry) {
        itemid.num = p_vcdplayer->play_item.num+1;
      } else {
        LOG_WARN( "At the end - non-PBC 'next' not possible here" );
        return false;
      }
 
      break;
 
    case VCDINFO_ITEM_TYPE_LID:
      /* Should have handled above. */
      LOG_WARN( "Internal inconsistency - should not have gotten here." );
      return false;
    default:
      return false;
    }
  }

  /** ??? p_vcdplayer->update_title(); ***/
  vcdplayer_play( p_access, itemid );
  return VLC_SUCCESS;

}
Exemple #6
0
/*!
  Play item assocated with the "default" selection.

  Return false if there was some problem.
*/
bool
vcdplayer_play_default( access_t * p_access )
{
  vcdplayer_t *p_vcdplayer= (vcdplayer_t *)p_access->p_sys;

  vcdinfo_itemid_t itemid;

  if (!p_vcdplayer) {
    dbg_print( (INPUT_DBG_CALL|INPUT_DBG_PBC),
           "null p_vcdplayer" );
    return VLC_EGENERIC;
  }
 

  dbg_print( (INPUT_DBG_CALL|INPUT_DBG_PBC),
         "current: %d" , p_vcdplayer->play_item.num);

  itemid.type = p_vcdplayer->play_item.type;

  if (vcdplayer_pbc_is_on(p_vcdplayer)) {

#if defined(LIBVCD_VERSION)
    lid_t lid=vcdinfo_get_multi_default_lid(p_vcdplayer->vcd, p_vcdplayer->i_lid,
                        p_vcdplayer->i_lsn);

    if (VCDINFO_INVALID_LID != lid) {
      itemid.num  = lid;
      itemid.type = VCDINFO_ITEM_TYPE_LID;
      dbg_print(INPUT_DBG_PBC, "DEFAULT to %d", itemid.num);
    } else {
      dbg_print(INPUT_DBG_PBC, "no DEFAULT for LID %d", p_vcdplayer->i_lid);
      return VLC_EGENERIC;
    }

#else
    vcdinfo_lid_get_pxd(p_vcdplayer->vcd, &(p_vcdplayer->pxd),
                        p_vcdplayer->i_lid);
 
    switch (p_vcdplayer->pxd.descriptor_type) {
    case PSD_TYPE_SELECTION_LIST:
    case PSD_TYPE_EXT_SELECTION_LIST:
      if (p_vcdplayer->pxd.psd == NULL) return false;
      vcdplayer_update_entry(p_access,
                             vcdinfo_get_default_offset(p_vcdplayer->vcd,
                                                        p_vcdplayer->i_lid),
                             &itemid.num, "default");
      break;

    case PSD_TYPE_PLAY_LIST:
    case PSD_TYPE_END_LIST:
    case PSD_TYPE_COMMAND_LIST:
      LOG_WARN( "There is no PBC 'default' selection here" );
      return false;
    }
#endif /* LIBVCD_VERSION (< 0.7.21) */
 

  } else {

    /* PBC is not on. "default" selection beginning of current
       selection . */
 
    itemid.num = p_vcdplayer->play_item.num;
 
  }

  /** ??? p_vcdplayer->update_title(); ***/
  vcdplayer_play( p_access, itemid );
  return VLC_SUCCESS;

}
Exemple #7
0
/*!
  Read block into p_buf and return the status back.

  This routine is a bit complicated because on reaching the end of
  a track or entry we may automatically advance to the item, or
  interpret the next item in the playback-control list.
*/
vcdplayer_read_status_t
vcdplayer_read (access_t * p_access, uint8_t *p_buf)
{

  /* p_access->handle_events (); */
  uint8_t wait_time=0;

  vcdplayer_t *p_vcdplayer= (vcdplayer_t *)p_access->p_sys;
  if ( p_vcdplayer->i_lsn > p_vcdplayer->end_lsn ) {
    vcdplayer_read_status_t read_status;
 
    /* We've run off of the end of this entry. Do we continue or stop? */
    dbg_print( (INPUT_DBG_LSN|INPUT_DBG_PBC),
              "end reached, cur: %u, end: %u\n",
              p_vcdplayer->i_lsn, p_vcdplayer->end_lsn);

  handle_item_continuation:
    read_status = vcdplayer_pbc_is_on( p_vcdplayer )
      ? vcdplayer_pbc_nav( p_access, &wait_time )
      : vcdplayer_non_pbc_nav( p_access, &wait_time );

    if (READ_STILL_FRAME == read_status) {
      *p_buf = wait_time;
      return READ_STILL_FRAME;
    }

    if (READ_BLOCK != read_status) return read_status;
  }

  /* Read the next block.
 
    Important note: we probably speed things up by removing "data"
    and the memcpy to it by extending vcd_image_source_read_mode2
    to allow a mode to do what's below in addition to its
    "raw" and "block" mode. It also would probably improve the modularity
    a little bit as well.
  */

  {
    CdIo *p_img = vcdinfo_get_cd_image(p_vcdplayer->vcd);
    typedef struct {
      uint8_t subheader [CDIO_CD_SUBHEADER_SIZE];
      uint8_t data    [M2F2_SECTOR_SIZE];
      uint8_t spare     [4];
    } vcdsector_t;
    vcdsector_t vcd_sector;

    do {
      if (cdio_read_mode2_sector(p_img, &vcd_sector,
                                 p_vcdplayer->i_lsn, true)!=0) {
        dbg_print(INPUT_DBG_LSN, "read error\n");
        p_vcdplayer->i_lsn++;
        return READ_ERROR;
      }
      p_vcdplayer->i_lsn++;

      if ( p_vcdplayer->i_lsn >= p_vcdplayer->end_lsn ) {
        /* We've run off of the end of this entry. Do we continue or stop? */
        dbg_print( (INPUT_DBG_LSN|INPUT_DBG_PBC),
                   "end reached in reading, cur: %u, end: %u\n",
                   p_vcdplayer->i_lsn, p_vcdplayer->end_lsn);
        break;
      }
 
      /* Check header ID for a padding sector and simply discard
         these.  It is alleged that VCD's put these in to keep the
         bitrate constant.
      */
    } while((vcd_sector.subheader[2]&~0x01)==0x60);

    if ( p_vcdplayer->i_lsn >= p_vcdplayer->end_lsn )
      /* We've run off of the end of this entry. Do we continue or stop? */
      goto handle_item_continuation;
 
    memcpy (p_buf, vcd_sector.data, M2F2_SECTOR_SIZE);
    return READ_BLOCK;
  }
}
Exemple #8
0
/*****************************************************************************
 * RunIntf: main loop
 *****************************************************************************/
static void RunIntf( intf_thread_t *p_intf )
{
    vlc_object_t      * p_vout = NULL;
    mtime_t             mtime = 0;
    mtime_t             mlast = 0;
    thread_vcd_data_t * p_vcd;
    input_thread_t    * p_input;

    /* What you add to the last input number entry. It accumulates all of
       the 10_ADD keypresses */
    int number_addend = 0;

    if( InitThread( p_intf ) < 0 )
    {
        msg_Err( p_intf, "can't initialize intf" );
        return;
    }

    p_input = p_intf->p_sys->p_input;
    p_vcd   = p_intf->p_sys->p_vcd =
      (thread_vcd_data_t *) p_input->p_access_data;

    dbg_print( INPUT_DBG_CALL, "intf initialized" );

    /* Main loop */
    while( !p_intf->b_die )
    {
      vlc_mutex_lock( &p_intf->change_lock );

        /*
         * Have we timed-out in showing a still frame?
         */
        if( p_intf->p_sys->b_still && !p_intf->p_sys->b_inf_still )
        {
            if( p_intf->p_sys->m_still_time > 0 )
            {
                /* Update remaining still time */
                dbg_print(INPUT_DBG_STILL, "updating still time");
                mtime = mdate();
                if( mlast )
                {
                    p_intf->p_sys->m_still_time -= mtime - mlast;
                }

                mlast = mtime;
            }
            else
            {
                /* Still time has elasped; set to continue playing. */
                dbg_print(INPUT_DBG_STILL, "wait time done - setting play");
                var_SetInteger( p_intf->p_sys->p_input, "state", PLAYING_S );
                p_intf->p_sys->m_still_time = 0;
                p_intf->p_sys->b_still = 0;
                mlast = 0;
            }
        }

      /*
       * Do we have a keyboard event?
       */
      if( p_vout && p_intf->p_sys->b_key_pressed )
        {
          vlc_value_t val;
          int i, i_action = -1;
          struct hotkey *p_hotkeys = p_intf->p_vlc->p_hotkeys;

          p_intf->p_sys->b_key_pressed = VLC_FALSE;

          /* Find action triggered by hotkey (if any) */
          var_Get( p_intf->p_vlc, "key-pressed", &val );

          dbg_print( INPUT_DBG_EVENT, "Key pressed %d", val.i_int );

          for( i = 0; p_hotkeys[i].psz_action != NULL; i++ )
            {
              if( p_hotkeys[i].i_key == val.i_int )
                {
                  i_action = p_hotkeys[i].i_action;
                }
            }

          if( i_action != -1) {
            switch (i_action) {

            case ACTIONID_NAV_LEFT:
              dbg_print( INPUT_DBG_EVENT, "ACTIONID_NAV_LEFT - prev (%d)",
                         number_addend );
              do {
                vcdplayer_play_prev( p_input );
              }        while (number_addend-- > 0);
              break;

            case ACTIONID_NAV_RIGHT:
              dbg_print( INPUT_DBG_EVENT, "ACTIONID_NAV_RIGHT - next (%d)",
                         number_addend );
              do {
                vcdplayer_play_next( p_input );
              } while (number_addend-- > 0);
              break;

            case ACTIONID_NAV_UP:
              dbg_print( INPUT_DBG_EVENT, "ACTIONID_NAV_UP - return" );
              do {
                vcdplayer_play_return( p_input );
              } while (number_addend-- > 0);
              break;

            case ACTIONID_NAV_DOWN:
              dbg_print( INPUT_DBG_EVENT, "ACTIONID_NAV_DOWN - default"  );
              vcdplayer_play_default( p_input );
              break;

            case ACTIONID_NAV_ACTIVATE:
              {
                vcdinfo_itemid_t itemid;
                itemid.type=p_vcd->play_item.type;

                dbg_print( INPUT_DBG_EVENT, "ACTIONID_NAV_ACTIVATE" );

                if ( vcdplayer_pbc_is_on( p_vcd ) && number_addend != 0 ) {
                  lid_t next_num=vcdinfo_selection_get_lid(p_vcd->vcd,
                                                           p_vcd->cur_lid,
                                                           number_addend);
                  if (VCDINFO_INVALID_LID != next_num) {
                    itemid.num  = next_num;
                    itemid.type = VCDINFO_ITEM_TYPE_LID;
                    VCDPlay( p_input, itemid );
                  }
                } else {
                  itemid.num = number_addend;
                  VCDPlay( p_input, itemid );
                }
                break;
              }
            }
            number_addend = 0;

            /* Any keypress gets rid of still frame waiting.
               FIXME - should handle just the ones that cause an action.
            */
            if( p_intf->p_sys->b_still )
              {
                dbg_print(INPUT_DBG_STILL, "Playing still after activate");
                var_SetInteger( p_intf->p_sys->p_input, "state", PLAYING_S );
                p_intf->p_sys->b_still = 0;
                p_intf->p_sys->b_inf_still = 0;
                p_intf->p_sys->m_still_time = 0;
              }

          } else {
            unsigned int digit_entered=0;

            switch (val.i_int) {
            case '9':
              digit_entered++;
            case '8':
              digit_entered++;
            case '7':
              digit_entered++;
            case '6':
              digit_entered++;
            case '5':
              digit_entered++;
            case '4':
              digit_entered++;
            case '3':
              digit_entered++;
            case '2':
              digit_entered++;
            case '1':
              digit_entered++;
            case '0':
              {
                number_addend *= 10;
                number_addend += digit_entered;
                dbg_print( INPUT_DBG_EVENT,
                           "Added %d. Number is now: %d\n",
                           digit_entered, number_addend);
                break;
              }
            }
          }
        }


      vlc_mutex_unlock( &p_intf->change_lock );

      if( p_vout == NULL )
        {
          p_vout = vlc_object_find( p_intf->p_sys->p_input,
                                    VLC_OBJECT_VOUT, FIND_CHILD );
          if( p_vout )
            {
              var_AddCallback( p_vout, "key-pressed", KeyEvent, p_intf );
            }
        }


      /* Wait a bit */
      msleep( INTF_IDLE_SLEEP );
    }

    if( p_vout )
    {
        var_DelCallback( p_vout, "key-pressed", KeyEvent, p_intf );
        vlc_object_release( p_vout );
    }

    vlc_object_release( p_intf->p_sys->p_input );
}