]> git.sesse.net Git - vlc/blobdiff - modules/access/vcdx/access.c
Used uint64_t for access_t::info.i_size/i_pos and access_t::pf_seek().
[vlc] / modules / access / vcdx / access.c
index 23d94fb9601f0522a1d7661366dccf599a091ac5..f6bb46c5595746f17422effe9e9a465eb86f4666 100644 (file)
@@ -1,13 +1,14 @@
 /*****************************************************************************
- * vcd.c : VCD input module for vlc
- *         using libcdio, libvcd and libvcdinfo. vlc-specific things tend
- *         to go here.
+ * vcd.c : VCD input module for vlc using libcdio, libvcd and libvcdinfo.
+ *         vlc-specific things tend to go here.
  *****************************************************************************
- * Copyright (C) 2000 VideoLAN
- * $Id: access.c,v 1.2 2003/11/07 10:31:38 rocky Exp $
+ * Copyright (C) 2000, 2003, 2004, 2005 the VideoLAN team
+ * $Id$
  *
- * Authors: Johan Bilien <jobi@via.ecp.fr>
- *          Rocky Bernstein <rocky@panix.com> 
+ * Authors: Rocky Bernstein <rocky@panix.com>
+ *   Some code is based on the non-libcdio VCD plugin (as there really
+ *   isn't real developer documentation yet on how to write a
+ *   navigable plugin.)
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  * GNU General Public License for more details.
  *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
  *****************************************************************************/
 
 /*****************************************************************************
  * Preamble
  *****************************************************************************/
 
-#if 0 // Disabled until this is working
-#include <vlc/vlc.h>
-#include <vlc/input.h>
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
 
-#include "../../demux/mpeg/system.h"
-#include "vcd.h"
-#include "intf.h"
-#include "vcdplayer.h"
+#include <vlc_common.h>
+#include <vlc_interface.h>
+#include <vlc_input.h>
+#include <vlc_access.h>
+#include <vlc_charset.h>
 
 #include <cdio/cdio.h>
+#include <cdio/cd_types.h>
 #include <cdio/logging.h>
 #include <cdio/util.h>
 #include <libvcd/info.h>
 #include <libvcd/logging.h>
-
-#include "cdrom.h"
-
-/* how many blocks VCDRead will read in each loop */
-#define VCD_BLOCKS_ONCE 20
-#define VCD_DATA_ONCE   (VCD_BLOCKS_ONCE * M2F2_SECTOR_SIZE)
+#include "vcd.h"
+#include "info.h"
+#include "access.h"
 
 /*****************************************************************************
  * Local prototypes
  *****************************************************************************/
 
 /* First those which are accessed from outside (via pointers). */
-static int  VCDOpen         ( vlc_object_t * );
-static void VCDClose        ( vlc_object_t * );
-static int  VCDRead         ( input_thread_t *, byte_t *, size_t );
-static int  VCDRead         ( input_thread_t *, byte_t *, size_t );
-static int  VCDSetProgram   ( input_thread_t *, pgrm_descriptor_t * );
+static block_t *VCDReadBlock    ( access_t * );
+
+static int      VCDControl      ( access_t *p_access, int i_query,
+                                  va_list args );
 
 /* Now those which are strictly internal */
-static void VCDSetOrigin    ( input_thread_t *, lsn_t origin_lsn, 
-                              lsn_t cur_lsn, lsn_t end_lsn, 
-                              int cur_entry, track_t track );
-static int  VCDEntryPoints  ( input_thread_t * );
-static int  VCDLIDs         ( input_thread_t * );
-static int  VCDSegments     ( input_thread_t * );
-static void VCDTracks       ( input_thread_t * );
-static int  VCDReadSector   ( vlc_object_t *p_this, 
-                              const vcdinfo_obj_t *p_vcd, lsn_t cur_lsn, 
-                              byte_t * p_buffer );
-static char *VCDParse       ( input_thread_t *, 
-                              /*out*/ vcdinfo_itemid_t * p_itemid );
-
-static void VCDUpdateVar( input_thread_t *p_input, int i_entry, int i_action,
-                          const char *varname, const char *label );
+static bool  VCDEntryPoints  ( access_t * );
+static bool  VCDLIDs         ( access_t * );
+static bool  VCDSegments     ( access_t * );
+static int  VCDTitles       ( access_t * );
+static char *VCDParse       ( access_t *,
+                              /*out*/ vcdinfo_itemid_t * p_itemid ,
+                              /*out*/ bool *play_single_item );
+
+static void VCDUpdateVar( access_t *p_access, int i_entry, int i_action,
+                          const char *p_varname, char *p_label,
+                          const char *p_debug_label );
 
 static vcdinfo_obj_t *vcd_Open   ( vlc_object_t *p_this, const char *psz_dev );
 
-static int debug_callback   ( vlc_object_t *p_this, const char *psz_name,
-                               vlc_value_t oldval, vlc_value_t val, 
-                               void *p_data );
-
-#define DEBUG_TEXT N_("set debug mask for additional debugging.")
-#define DEBUG_LONGTEXT N_( \
-    "This integer when viewed in binary is a debugging mask\n" \
-    "MRL             1\n" \
-    "external call   2\n" \
-    "all calls       4\n" \
-    "LSN             8\n" \
-    "PBC      (10)  16\n" \
-    "libcdio  (20)  32\n" \
-    "seeks    (40)  64\n" \
-    "still    (80) 128\n" \
-    "vcdinfo (100) 256\n" )
-
 /****************************************************************************
  * Private functions
  ****************************************************************************/
-/* FIXME: This variable is a hack. Would be nice to eliminate the 
+
+/* FIXME: This variable is a hack. Would be nice to eliminate the
    global-ness. */
-static input_thread_t *p_vcd_input = NULL;
 
-int
-vcd_debug_callback   ( vlc_object_t *p_this, const char *psz_name,
-                       vlc_value_t oldval, vlc_value_t val, void *p_data )
-{
-  thread_vcd_data_t *p_vcd;
-
-  if (NULL == p_vcd_input) return VLC_EGENERIC;
-  
-  p_vcd = (thread_vcd_data_t *)p_vcd_input->p_access_data;
-
-  if (p_vcd->i_debug & (INPUT_DBG_CALL|INPUT_DBG_EXT)) {
-    msg_Dbg( p_vcd_input, "Old debug (x%0x) %d, new debug (x%0x) %d", 
-             p_vcd->i_debug, p_vcd->i_debug, val.i_int, val.i_int);
-  }
-  p_vcd->i_debug = val.i_int;
-  return VLC_SUCCESS;
-}
+static access_t *p_vcd_access = NULL;
 
 /* process messages that originate from libcdio. */
 static void
 cdio_log_handler (cdio_log_level_t level, const char message[])
 {
-  thread_vcd_data_t *p_vcd = (thread_vcd_data_t *)p_vcd_input->p_access_data;
-  switch (level) {
-  case CDIO_LOG_DEBUG:
-  case CDIO_LOG_INFO:
-    if (p_vcd->i_debug & INPUT_DBG_CDIO) 
-      msg_Dbg( p_vcd_input, message);
-    break;
-  case CDIO_LOG_WARN:
-    msg_Warn( p_vcd_input, message);
-    break;
-  case CDIO_LOG_ERROR:
-  case CDIO_LOG_ASSERT:
-    msg_Err( p_vcd_input, message);
-    break;
-  default:
-    msg_Warn( p_vcd_input, message,
-            _("The above message had unknown vcdimager log level"), 
-            level);
-  }
-  return;
+    const vcdplayer_t *p_vcdplayer = (vcdplayer_t *)p_vcd_access->p_sys;
+    switch (level) {
+    case CDIO_LOG_DEBUG:
+    case CDIO_LOG_INFO:
+        if (p_vcdplayer->i_debug & INPUT_DBG_CDIO)
+            msg_Dbg( p_vcd_access, "%s", message);
+        break;
+    case CDIO_LOG_WARN:
+        msg_Warn( p_vcd_access, "%s", message);
+        break;
+    case CDIO_LOG_ERROR:
+    case CDIO_LOG_ASSERT:
+        msg_Err( p_vcd_access, "%s", message);
+        break;
+    default:
+        msg_Warn( p_vcd_access, "%s\n%s %d", message,
+                  "The above message had unknown log level", level);
+    }
+    return;
 }
 
 /* process messages that originate from vcdinfo. */
 static void
 vcd_log_handler (vcd_log_level_t level, const char message[])
 {
-  thread_vcd_data_t *p_vcd = (thread_vcd_data_t *)p_vcd_input->p_access_data;
-  switch (level) {
-  case VCD_LOG_DEBUG:
-  case VCD_LOG_INFO:
-    if (p_vcd->i_debug & INPUT_DBG_VCDINFO)
-      msg_Dbg( p_vcd_input, message);
-    break;
-  case VCD_LOG_WARN:
-    msg_Warn( p_vcd_input, message);
-    break;
-  case VCD_LOG_ERROR:
-  case VCD_LOG_ASSERT:
-    msg_Err( p_vcd_input, message);
-    break;
-  default:
-    msg_Warn( p_vcd_input, "%s\n%s %d", message,
-            _("The above message had unknown vcdimager log level"), 
-            level);
-  }
-  return;
+    vcdplayer_t *p_vcdplayer = (vcdplayer_t *)p_vcd_access->p_sys;
+    switch (level) {
+    case VCD_LOG_DEBUG:
+    case VCD_LOG_INFO:
+        if (p_vcdplayer->i_debug & INPUT_DBG_VCDINFO)
+            msg_Dbg( p_vcd_access, "%s", message);
+        break;
+    case VCD_LOG_WARN:
+        msg_Warn( p_vcd_access, "%s", message);
+        break;
+    case VCD_LOG_ERROR:
+    case VCD_LOG_ASSERT:
+        msg_Err( p_vcd_access, "%s", message);
+        break;
+    default:
+        msg_Warn( p_vcd_access, "%s\n%s %d", message,
+                  "The above message had unknown vcdimager log level", level);
+    }
+    return;
 }
 
-/*
- * Data reading functions
- */
-
 /*****************************************************************************
-  VCDOpen: open VCD.
-  read in meta-information about VCD: the number of tracks, segments, 
-  entries, size and starting information. Then set up state variables so
-  that we read/seek starting at the location specified.
-
-  On success we return VLC_SUCCESS, on memory exhausted VLC_ENOMEM, 
-  and VLC_EGENERIC for some other error.
+  VCDRead: reads VCD_BLOCKS_ONCE from the VCD and returns that.
+  NULL is returned if something went wrong.
  *****************************************************************************/
-static int 
-VCDOpen( vlc_object_t *p_this )
+static block_t *
+VCDReadBlock( access_t * p_access )
 {
-    input_thread_t *        p_input = (input_thread_t *)p_this;
-    thread_vcd_data_t *     p_vcd;
-    char *                  psz_source;
-    vcdinfo_itemid_t        itemid;
-    bool                    play_ok;
-    
-    p_input->pf_read        = VCDRead;
-    p_input->pf_seek        = VCDSeek;
-    p_input->pf_set_area    = VCDSetArea;
-    p_input->pf_set_program = VCDSetProgram;
-
-    p_vcd = malloc( sizeof(thread_vcd_data_t) );
-
-    if( p_vcd == NULL )
-    {
-        LOG_ERR ("out of memory" );
-        return VLC_ENOMEM;
-    }
+    vcdplayer_t *p_vcdplayer= (vcdplayer_t *)p_access->p_sys;
+    const int    i_blocks   = p_vcdplayer->i_blocks_per_read;
+    block_t     *p_block;
+    int          i_read;
+    uint8_t     *p_buf;
 
-    p_input->p_access_data = (void *)p_vcd;
-    p_vcd->i_debug         = config_GetInt( p_this, MODULE_STRING "-debug" );
-    psz_source             = VCDParse( p_input, &itemid );
+    dbg_print( (INPUT_DBG_LSN), "lsn: %lu",
+               (long unsigned int) p_vcdplayer->i_lsn );
 
-    if ( NULL == psz_source ) 
+    /* Allocate a block for the reading */
+    if( !( p_block = block_New( p_access, i_blocks * M2F2_SECTOR_SIZE ) ) )
     {
-      free( p_vcd );
-      return( VLC_EGENERIC );
-    }
-
-    dbg_print( (INPUT_DBG_CALL|INPUT_DBG_EXT), "%s", psz_source );
-
-    p_vcd->p_segments = NULL;
-    p_vcd->p_entries  = NULL;
-    
-    /* set up input  */
-    p_input->i_mtu = VCD_DATA_ONCE;
-
-    vlc_mutex_lock( &p_input->stream.stream_lock );
-
-    /* If we are here we can control the pace... */
-    p_input->stream.b_pace_control = 1;
-
-    p_input->stream.b_seekable = 1;
-    p_input->stream.p_selected_area->i_size = 0;
-    p_input->stream.p_selected_area->i_tell = 0;
-
-    vlc_mutex_unlock( &p_input->stream.stream_lock );
-
-    if( !(p_vcd->vcd = vcd_Open( p_this, psz_source )) )
-    {
-        msg_Warn( p_input, "could not open %s", psz_source );
-        free( psz_source );
-        free( p_vcd );
-        return VLC_EGENERIC;
-    }
-
-    /* Get track information. */
-    p_vcd->num_tracks = ioctl_GetTracksMap( VLC_OBJECT(p_input),
-                                            vcdinfo_get_cd_image(p_vcd->vcd), 
-                                            &p_vcd->p_sectors );
-    free( psz_source );
-    if( p_vcd->num_tracks < 0 )
-        LOG_ERR ("unable to count tracks" );
-    else if( p_vcd->num_tracks <= 1 )
-        LOG_ERR ("no movie tracks found" );
-    if( p_vcd->num_tracks <= 1)
-    {
-        vcdinfo_close( p_vcd->vcd );
-        free( p_vcd );
-        return VLC_EGENERIC;
+        msg_Err( p_access, "cannot get a new block of size: %i",
+                 i_blocks * M2F2_SECTOR_SIZE );
+        block_Release( p_block );
+        return NULL;
     }
 
-    /* Set stream and area data */
-    vlc_mutex_lock( &p_input->stream.stream_lock );
-
-    /* Initialize ES structures */
-    input_InitStream( p_input, sizeof( stream_ps_data_t ) );
-
-    /* disc input method */
-    p_input->stream.i_method = INPUT_METHOD_VCD;
-
-    p_input->stream.i_area_nb = 1;
-    
-
-    /* Initialize segment information. */
-    VCDSegments( p_input );
-    
-    /* Initialize track area information. */
-    VCDTracks( p_input );
-    
-    if( VCDEntryPoints( p_input ) < 0 )
+    p_buf = (uint8_t *) p_block->p_buffer;
+    for ( i_read = 0 ; i_read < i_blocks ; i_read++ )
     {
-        msg_Warn( p_input, "could not read entry points, will not use them" );
-        p_vcd->b_valid_ep = false;
-    }
+        vcdplayer_read_status_t read_status = vcdplayer_read(p_access, p_buf);
 
-    if( VCDLIDs( p_input ) < 0 )
-    {
-        msg_Warn( p_input, "could not read entry LIDs" );
-    }
+        p_access->info.i_pos += M2F2_SECTOR_SIZE;
 
-    play_ok = (VLC_SUCCESS == VCDPlay( p_input, itemid ));
-    
-    vlc_mutex_unlock( &p_input->stream.stream_lock );
+        switch ( read_status ) {
+        case READ_END:
+            /* End reached. Return NULL to indicated this. */
+            /* We also set the postion to the end so the higher level
+               (demux?) doesn't try to keep reading. If everything works out
+               right this shouldn't have to happen.
+             */
+#if 0
+            if( p_access->info.i_pos != p_access->info.i_size ) {
+                msg_Warn( p_access,
+                    "At end but pos (%llu) is not size (%llu). Adjusting.",
+                    p_access->info.i_pos, p_access->info.i_size );
+                p_access->info.i_pos = p_access->info.i_size;
+            }
+#endif
 
-    if ( ! play_ok ) {
-      vcdinfo_close( p_vcd->vcd );
-      free( p_vcd );
-      return VLC_EGENERIC;
-    }
+            block_Release( p_block );
+            return NULL;
 
-    if( !p_input->psz_demux || !*p_input->psz_demux )
-    {
-#if FIXED
-      p_input->psz_demux = "vcdx";
+        case READ_ERROR:
+            /* Some sort of error. Should we increment lsn? to skip block? */
+            block_Release( p_block );
+            return NULL;
+        case READ_STILL_FRAME:
+          /* FIXME The below should be done in an event thread.
+             Until then...
+           */
+#if 1
+            msleep( MILLISECONDS_PER_SEC * *p_buf );
+            VCDSetOrigin(p_access, p_vcdplayer->origin_lsn,
+                         p_vcdplayer->i_track, &(p_vcdplayer->play_item));
+            // p_vcd->in_still = false;
+            dbg_print(INPUT_DBG_STILL, "still wait time done");
 #else
-      p_input->psz_demux = "ps";
+            vcdIntfStillTime(p_vcdplayer->p_intf, *p_buf);
 #endif
-    }
-
-    return VLC_SUCCESS;
-}
-
-/*****************************************************************************
- * VCDClose: closes VCD releasing allocated memory.
- *****************************************************************************/
-static void 
-VCDClose( vlc_object_t *p_this )
-{
-    input_thread_t *   p_input = (input_thread_t *)p_this;
-    thread_vcd_data_t *p_vcd = (thread_vcd_data_t *)p_input->p_access_data;
-
-    dbg_print( (INPUT_DBG_CALL|INPUT_DBG_EXT), "VCDClose" );
-    vcdinfo_close( p_vcd->vcd );
-
-    free( p_vcd->p_entries );
-    free( p_vcd->p_segments );
-    free( p_vcd );
-    p_vcd_input = NULL;
-}
-
-/*****************************************************************************
- * VCDRead: reads i_len bytes from the VCD into p_buffer.
- *****************************************************************************
- * Returns -1 in case of error, 0 in case of EOF, otherwise the number of
- * bytes.
- *****************************************************************************/
-static int 
-VCDRead( input_thread_t * p_input, byte_t * p_buffer, size_t i_len )
-{
-    thread_vcd_data_t *     p_vcd= (thread_vcd_data_t *)p_input->p_access_data;
-    int                     i_blocks;
-    int                     i_index;
-    int                     i_read;
-    byte_t                  p_last_sector[ M2F2_SECTOR_SIZE ];
-
-    i_read = 0;
 
-    /* Compute the number of blocks we have to read */
-
-    i_blocks = i_len / M2F2_SECTOR_SIZE;
-
-    for ( i_index = 0 ; i_index < i_blocks ; i_index++ )
-    {
-
-      if ( p_vcd->cur_lsn == p_vcd->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", p_vcd->cur_lsn );
-
-        read_status = vcdplayer_pbc_is_on( p_vcd ) 
-          ? vcdplayer_pbc_nav( p_input ) 
-          : vcdplayer_non_pbc_nav( p_input );
+            block_Release( p_block );
+            return NULL;
 
-        switch (read_status) {
-        case READ_END:
-          /* End reached. Return NULL to indicated this. */
-        case READ_ERROR:
-          /* Some sort of error. */
-          return i_read;
-        case READ_STILL_FRAME: 
-          {
-            byte_t * p_buf = p_buffer;
-            p_buf += (i_index*M2F2_SECTOR_SIZE);
-            memset(p_buf, 0, M2F2_SECTOR_SIZE);
-            p_buf += 2;
-            *p_buf = 0x01;
-            dbg_print(INPUT_DBG_STILL, "Handled still event\n");
-            return i_read + M2F2_SECTOR_SIZE;
-          }
         default:
         case READ_BLOCK:
-          break;
+            /* Read buffer */
+            break;
         }
-      }
 
-      if ( VCDReadSector( VLC_OBJECT(p_input), p_vcd->vcd,
-                          p_vcd->cur_lsn, 
-                          p_buffer + (i_index*M2F2_SECTOR_SIZE) ) < 0 )
+        p_buf += M2F2_SECTOR_SIZE;
+        /* Update seekpoint */
+        if ( VCDINFO_ITEM_TYPE_ENTRY == p_vcdplayer->play_item.type )
         {
-          LOG_ERR ("could not read sector %d", p_vcd->cur_lsn );
-          return -1;
-        }
-      
-      p_vcd->cur_lsn ++;
-      
-      /* Update chapter */
-      if( p_vcd->b_valid_ep &&
-          /* FIXME kludge so that read does not update chapter
-           * when a manual chapter change was requested and not
-           * yet accomplished */
-          !p_input->stream.p_new_area )
-        {
-          unsigned int i_entry = p_input->stream.p_selected_area->i_part;
-          
-          vlc_mutex_lock( &p_input->stream.stream_lock );
-          
-          if( i_entry < p_vcd->num_entries &&
-              p_vcd->cur_lsn >= p_vcd->p_entries[i_entry+1] )
+            size_t i_entry = p_vcdplayer->play_item.num+1;
+            lsn_t  i_lsn   = vcdinfo_get_entry_lsn(p_vcdplayer->vcd, i_entry);
+            if ( p_vcdplayer->i_lsn >= i_lsn && i_lsn != VCDINFO_NULL_LSN )
             {
-              dbg_print( INPUT_DBG_PBC, 
-                         "new entry, i_entry %d, sector %d, es %d",
-                         i_entry, p_vcd->cur_lsn, 
-                         p_vcd->p_entries[i_entry] );
-              p_vcd->play_item.num = 
-                ++ p_input->stream.p_selected_area->i_part;
-              p_vcd->play_item.type = VCDINFO_ITEM_TYPE_ENTRY;
-              VCDUpdateVar( p_input, p_vcd->play_item.num, VLC_VAR_SETVALUE,
-                            "chapter", "Setting entry" );
-            }
-          vlc_mutex_unlock( &p_input->stream.stream_lock );
-        }
+                dbg_print( (INPUT_DBG_LSN|INPUT_DBG_PBC),
+                           "entry change to %zu, current LSN %u >= end %u",
+                           i_entry, p_vcdplayer->i_lsn, i_lsn);
 
-        i_read += M2F2_SECTOR_SIZE;
-    }
+                p_vcdplayer->play_item.num = i_entry;
 
-    if ( i_len % M2F2_SECTOR_SIZE ) /* this should not happen */
-    {
-        if ( VCDReadSector( VLC_OBJECT(p_input), p_vcd->vcd,
-                            p_vcd->cur_lsn, p_last_sector ) < 0 )
-        {
-            LOG_ERR ("could not read sector %d", p_vcd->cur_lsn );
-            return -1;
+                VCDSetOrigin( p_access,  i_lsn, p_vcdplayer->i_track,
+                              &(p_vcdplayer->play_item) );
+            }
         }
-
-        p_input->p_vlc->pf_memcpy( p_buffer + i_blocks * M2F2_SECTOR_SIZE,
-                                   p_last_sector, i_len % M2F2_SECTOR_SIZE );
-        i_read += i_len % M2F2_SECTOR_SIZE;
     }
 
-    return i_read;
-}
-
-
-/*****************************************************************************
- * VCDSetProgram: Does nothing since a VCD is mono_program
- *****************************************************************************/
-static int 
-VCDSetProgram( input_thread_t * p_input, pgrm_descriptor_t * p_program)
-{
-    thread_vcd_data_t *     p_vcd= (thread_vcd_data_t *)p_input->p_access_data;
-    dbg_print( (INPUT_DBG_CALL|INPUT_DBG_EXT), "VCDSetProgram" );
-    return 0;
+    return p_block;
 }
 
 
-/*****************************************************************************
- * VCDSetArea: initialize internal data structures and input stream data 
-   so set subsequent reading and seeking to reflect that we are
-   at track x, entry or segment y.
-   This is called for each user navigation request, e.g. the GUI 
-   Chapter/Title selections or in initial MRL parsing. 
+/****************************************************************************
+ * VCDSeek
  ****************************************************************************/
-int 
-VCDSetArea( input_thread_t * p_input, input_area_t * p_area )
+int
+VCDSeek( access_t * p_access, uint64_t i_pos )
 {
-    thread_vcd_data_t *p_vcd = (thread_vcd_data_t*)p_input->p_access_data;
-    unsigned int i_entry = p_area->i_part;
-    track_t i_track      = p_area->i_id;
-    int old_seekable     = p_input->stream.b_seekable;
-    unsigned int i_nb    = p_area->i_plugin_data + p_area->i_part_nb;
-
-    dbg_print( (INPUT_DBG_CALL|INPUT_DBG_EXT),
-               "track: %d, entry %d, seekable %d",
-               i_track, i_entry, old_seekable );
-
-    /* we can't use the interface slider until initilization is complete */
-    p_input->stream.b_seekable = 0;
-
-    if( p_area != p_input->stream.p_selected_area )
+    if (!p_access || !p_access->p_sys) return VLC_EGENERIC;
     {
-        unsigned int i;
-
-        /* If is the result of a track change, make the entry valid. */
-        if (i_entry < p_area->i_plugin_data || i_entry >= i_nb) 
-          i_entry = p_area->i_plugin_data;
+        vcdplayer_t         *p_vcdplayer = (vcdplayer_t *)p_vcd_access->p_sys;
+        const input_title_t *t = p_vcdplayer->p_title[p_access->info.i_title];
+        unsigned int         i_entry = VCDINFO_INVALID_ENTRY;
+        int i_seekpoint;
 
-        /* Change the default area */
-        p_input->stream.p_selected_area = p_area;
+        /* Next sector to read */
+        p_access->info.i_pos = i_pos;
+        p_vcdplayer->i_lsn = (i_pos / (uint64_t) M2F2_SECTOR_SIZE) +
+                             p_vcdplayer->origin_lsn;
 
-        /* Update the navigation variables without triggering a callback */
-        VCDUpdateVar( p_input, i_track, VLC_VAR_SETVALUE, "title", 
-                      "Setting track");
-
-        var_Change( p_input, "chapter", VLC_VAR_CLEARCHOICES, NULL, NULL );
-        for( i = p_area->i_plugin_data; i < i_nb; i++ )
+        switch (p_vcdplayer->play_item.type)
         {
-          VCDUpdateVar( p_input, i , VLC_VAR_ADDCHOICE, 
-                        "chapter",  "Adding entry choice");
+        case VCDINFO_ITEM_TYPE_TRACK:
+        case VCDINFO_ITEM_TYPE_ENTRY:
+            break;
+        default:
+            p_vcdplayer->b_valid_ep = false;
+            break;
         }
-    }
-
-    if (i_track == 0) 
-      VCDSetOrigin( p_input, p_vcd->p_segments[i_entry], 
-                    p_vcd->p_segments[i_entry], p_vcd->p_segments[i_entry+1],
-                    i_entry, 0 );
-    else
-      VCDSetOrigin( p_input, p_vcd->p_sectors[i_track], 
-                    vcdinfo_get_entry_lsn(p_vcd->vcd, i_entry), 
-                    p_vcd->p_sectors[i_track+1],
-                    i_entry, i_track );
-
-    p_input->stream.b_seekable = old_seekable;
-    /* warn interface that something has changed */
-    p_input->stream.b_changed = 1;
-
-    return VLC_SUCCESS;
-}
-
-
-/****************************************************************************
- * VCDSeek
- ****************************************************************************/
-void 
-VCDSeek( input_thread_t * p_input, off_t i_off )
-{
-    thread_vcd_data_t * p_vcd;
-    unsigned int i_entry=0; /* invalid entry */
-
-    p_vcd = (thread_vcd_data_t *) p_input->p_access_data;
 
-    p_vcd->cur_lsn = p_vcd->origin_lsn + (i_off / (off_t)M2F2_SECTOR_SIZE);
-
-    vlc_mutex_lock( &p_input->stream.stream_lock );
-#define p_area p_input->stream.p_selected_area
-    /* Find entry */
-    if( p_vcd->b_valid_ep )
-    {
-        for( i_entry = 1 ; i_entry < p_vcd->num_entries ; i_entry ++ )
+        /* Find entry */
+        if( p_vcdplayer->b_valid_ep )
         {
-            if( p_vcd->cur_lsn < p_vcd->p_entries[i_entry] )
+            for( i_entry = 0 ; i_entry < p_vcdplayer->i_entries ; i_entry ++ )
             {
-              VCDUpdateVar( p_input, i_entry, VLC_VAR_SETVALUE, 
-                            "chapter", "Setting entry" );
-              break;
+                if( p_vcdplayer->i_lsn < p_vcdplayer->p_entries[i_entry] )
+                {
+                    VCDUpdateVar( p_access, i_entry, VLC_VAR_SETVALUE,
+                                  "chapter", _("Entry"), "Setting entry" );
+                    break;
+                }
             }
-        }
-        p_vcd->play_item.num  = p_area->i_part = i_entry;
-        p_vcd->play_item.type = VCDINFO_ITEM_TYPE_ENTRY;
-    }
-#undef p_area
-
-    p_input->stream.p_selected_area->i_tell = i_off;
-
-    dbg_print( (INPUT_DBG_CALL|INPUT_DBG_EXT|INPUT_DBG_SEEK),
-    "orig %d, cur: %d, offset: %lld, start: %lld, entry %d", 
-               p_vcd->origin_lsn, p_vcd->cur_lsn, i_off, 
-               p_input->stream.p_selected_area->i_start, i_entry );
-
-    vlc_mutex_unlock( &p_input->stream.stream_lock );
-}
-
-/*****************************************************************************
-  VCDPlay: set up internal structures so seeking/reading places an item.
-  itemid: the thing to play.
-  user_entry: true if itemid is a user selection (rather than internally-
-  generated selection such as via PBC) in which case we may have to adjust 
-  for differences in numbering. 
- *****************************************************************************/
-int
-VCDPlay( input_thread_t *p_input, vcdinfo_itemid_t itemid )
-{
-    thread_vcd_data_t *     p_vcd= (thread_vcd_data_t *)p_input->p_access_data;
-    input_area_t *          p_area;
-    
-    p_vcd->in_still = 0;
-
-#define area p_input->stream.pp_areas
-
-    switch (itemid.type) {
-    case VCDINFO_ITEM_TYPE_TRACK:
-
-      /* Valid tracks go from 1...num_tracks-1, because track 0 is unplayable.
-       */
 
-      if (itemid.num == 0 || itemid.num >= p_vcd->num_tracks) {
-        LOG_ERR ("Invalid track number %d", itemid.num );
-        return VLC_EGENERIC;
-      }
-      p_area           = area[itemid.num];
-      p_area->i_part   = p_area->i_plugin_data;
-      p_input->stream.b_seekable = 1;
-      break;
-    case VCDINFO_ITEM_TYPE_SEGMENT: 
-      /* Valid segments go from 0...num_segments-1. */
-      if (itemid.num >= p_vcd->num_segments) {
-        LOG_ERR ( "Invalid segment number: %d", itemid.num );
-        return VLC_EGENERIC;
-      } else {
-        vcdinfo_video_segment_type_t segtype = 
-          vcdinfo_get_video_type(p_vcd->vcd, itemid.num);
-        
-        dbg_print(INPUT_DBG_PBC, "%s (%d), seg_num: %d", 
-                  vcdinfo_video_type2str(p_vcd->vcd, itemid.num), 
-                  (int) segtype, itemid.num);
-        
-        p_area           = area[0];
-        p_area->i_part   = itemid.num;
-        
-        switch (segtype)
-          {
-          case VCDINFO_FILES_VIDEO_NTSC_STILL:
-          case VCDINFO_FILES_VIDEO_NTSC_STILL2:
-          case VCDINFO_FILES_VIDEO_PAL_STILL:
-          case VCDINFO_FILES_VIDEO_PAL_STILL2:
-            p_input->stream.b_seekable = 0;
-            p_vcd->in_still = -5;
-            break;
-          default:
-            p_input->stream.b_seekable = 1;
-            p_vcd->in_still = 0;
-          }
-      }
-      break;
-      
-    case VCDINFO_ITEM_TYPE_LID:
-      /* LIDs go from 1..num_lids. */
-      if (itemid.num == 0 || itemid.num > p_vcd->num_lids) {
-        LOG_ERR ( "Invalid LID number: %d", itemid.num );
-        return VLC_EGENERIC;
-      } else {
-        p_vcd->cur_lid = itemid.num;
-        vcdinfo_lid_get_pxd(p_vcd->vcd, &(p_vcd->pxd), itemid.num);
-    
-        switch (p_vcd->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_vcd->pxd.psd == NULL) return VLC_EGENERIC;
-          trans_itemid_num  = vcdinf_psd_get_itemid(p_vcd->pxd.psd);
-          vcdinfo_classify_itemid(trans_itemid_num, &trans_itemid);
-          p_vcd->loop_count = 1;
-          p_vcd->loop_item  = trans_itemid;
-          return VCDPlay( p_input, trans_itemid );
-          break;
+            {
+                vcdinfo_itemid_t itemid;
+                itemid.num  = i_entry;
+                itemid.type = VCDINFO_ITEM_TYPE_ENTRY;
+                VCDSetOrigin(p_access, p_vcdplayer->i_lsn,
+                             p_vcdplayer->i_track, &itemid);
+            }
         }
-          
-        case PSD_TYPE_PLAY_LIST: {
-          if (p_vcd->pxd.pld == NULL) return VLC_EGENERIC;
-          p_vcd->pdi = -1;
-          return vcdplayer_inc_play_item(p_input) 
-            ? VLC_SUCCESS : VLC_EGENERIC;
-          break;
+
+        dbg_print( (INPUT_DBG_CALL|INPUT_DBG_EXT|INPUT_DBG_SEEK),
+                   "orig %lu, cur: %lu, offset: %"PRIi64", entry %d",
+                   (long unsigned int) p_vcdplayer->origin_lsn,
+                   (long unsigned int) p_vcdplayer->i_lsn, i_pos,
+                   i_entry );
+        /* Find seekpoint */
+        for( i_seekpoint = 0; i_seekpoint < t->i_seekpoint; i_seekpoint++ )
+        {
+            if( i_seekpoint + 1 >= t->i_seekpoint ) break;
+            if( i_pos < t->seekpoint[i_seekpoint + 1]->i_byte_offset ) break;
         }
-          
-        case PSD_TYPE_END_LIST:
-        case PSD_TYPE_COMMAND_LIST:
-          
-        default:
-          ;
+        /* Update current seekpoint */
+        if( i_seekpoint != p_access->info.i_seekpoint )
+        {
+            dbg_print( (INPUT_DBG_SEEK), "seekpoint change %lu",
+                       (long unsigned int) i_seekpoint );
+            p_access->info.i_update |= INPUT_UPDATE_SEEKPOINT;
+            p_access->info.i_seekpoint = i_seekpoint;
         }
-      }
-      return VLC_EGENERIC;
-    case VCDINFO_ITEM_TYPE_ENTRY:
-      /* Entries go from 0..num_entries-1. */
-      if (itemid.num >= p_vcd->num_entries) {
-        LOG_ERR ("Invalid entry number: %d", itemid.num );
-        return VLC_EGENERIC;
-      } else {
-        track_t cur_track  = vcdinfo_get_track(p_vcd->vcd,  itemid.num);
-        p_area             = area[cur_track];
-        p_area->i_part     = itemid.num;
-        p_input->stream.b_seekable = 1;
-      }
-      break;
-    default:
-      LOG_ERR ("unknown entry type" );
-      return VLC_EGENERIC;
     }
-
-    VCDSetArea( p_input, p_area );
-
-#undef area
-
-    p_vcd->play_item = itemid;
-
-    dbg_print( (INPUT_DBG_CALL), 
-               "i_start %lld, i_size: %lld, i_tell: %lld, lsn %d", 
-               p_area->i_start, p_area->i_size, 
-               p_area->i_tell, p_vcd->cur_lsn );
-        
+    p_access->info.b_eof = false;
     return VLC_SUCCESS;
 }
 
@@ -703,233 +316,259 @@ VCDPlay( input_thread_t *p_input, vcdinfo_itemid_t itemid )
   and initializes area information with that.
   Before calling this track information should have been read in.
  *****************************************************************************/
-static int 
-VCDEntryPoints( input_thread_t * p_input )
+static bool
+VCDEntryPoints( access_t * p_access )
 {
-    thread_vcd_data_t *               p_vcd;
-    unsigned int                      i_nb;
-    unsigned int                      i, i_entry_index = 0;
-    unsigned int                      i_previous_track = CDIO_INVALID_TRACK;
-
-    p_vcd = (thread_vcd_data_t *) p_input->p_access_data;
-
-    i_nb = vcdinfo_get_num_entries(p_vcd->vcd);
-    if (0 == i_nb) 
-      return -1;
-    
-    p_vcd->p_entries  = malloc( sizeof( lba_t ) * i_nb );
-
-    if( p_vcd->p_entries == NULL )
+    if (!p_access || !p_access->p_sys) return false;
+    vcdplayer_t       *p_vcdplayer = (vcdplayer_t *) p_access->p_sys;
+    const unsigned int i_entries   = vcdinfo_get_num_entries(p_vcdplayer->vcd);
+    const track_t      i_last_track
+           = cdio_get_num_tracks(vcdinfo_get_cd_image(p_vcdplayer->vcd))
+           + cdio_get_first_track_num(vcdinfo_get_cd_image(p_vcdplayer->vcd));
+    unsigned int i;
+    if (0 == i_entries) {
+        LOG_ERR ("no entires found -- something is wrong" );
+        return false;
+    }
+    p_vcdplayer->p_entries  = malloc( sizeof( lsn_t ) * i_entries );
+    if( p_vcdplayer->p_entries == NULL )
     {
         LOG_ERR ("not enough memory for entry points treatment" );
-        return -1;
+        return false;
     }
-
-    p_vcd->num_entries = 0;
-
-    for( i = 0 ; i < i_nb ; i++ )
+    p_vcdplayer->i_entries = i_entries;
+    for( i = 0 ; i < i_entries ; i++ )
     {
-        track_t i_track = vcdinfo_get_track(p_vcd->vcd, i);
-        if( i_track <= p_input->stream.i_area_nb )
+        const track_t i_track = vcdinfo_get_track(p_vcdplayer->vcd, i);
+        if( i_track <= i_last_track )
         {
-            p_vcd->p_entries[i] = 
-              vcdinfo_get_entry_lsn(p_vcd->vcd, i);
-            p_input->stream.pp_areas[i_track]->i_part_nb ++;
+            seekpoint_t *s = vlc_seekpoint_New();
+            char psz_entry[100];
+    
+            snprintf(psz_entry, sizeof(psz_entry), "%s %02d", _("Entry"), i );
 
-            /* if this entry belongs to a new track */
-            if( i_track != i_previous_track )
-            {
-                /* i_plugin_data is used to store the first entry of the area*/
-                p_input->stream.pp_areas[i_track]->i_plugin_data =
-                                                            i_entry_index;
-                i_previous_track = i_track;
-                p_input->stream.pp_areas[i_track]->i_part_nb = 1;
-            }
-            i_entry_index ++;
-            p_vcd->num_entries ++;
-        }
-        else
-            msg_Warn( p_input, "wrong track number found in entry points" );
+            p_vcdplayer->p_entries[i] =
+                                   vcdinfo_get_entry_lsn(p_vcdplayer->vcd, i);
+    
+            s->psz_name      = strdup(psz_entry);
+            s->i_byte_offset = (p_vcdplayer->p_entries[i]
+                             - vcdinfo_get_track_lsn(p_vcdplayer->vcd,i_track))
+                             * M2F2_SECTOR_SIZE;
+    
+            dbg_print( INPUT_DBG_MRL, "%s, lsn %d,  byte_offset %ld",
+                       s->psz_name, p_vcdplayer->p_entries[i],
+                       (unsigned long int) s->i_byte_offset);
+            TAB_APPEND( p_vcdplayer->p_title[i_track-1]->i_seekpoint,
+                        p_vcdplayer->p_title[i_track-1]->seekpoint, s );
+
+        } else
+            msg_Warn( p_access, "wrong track number found in entry points" );
     }
-    p_vcd->b_valid_ep = true;
-    return 0;
+    p_vcdplayer->b_valid_ep = true;
+    return true;
 }
 
 /*****************************************************************************
  * VCDSegments: Reads the information about the segments the disc.
  *****************************************************************************/
-static int
-VCDSegments( input_thread_t * p_input )
+static bool
+VCDSegments( access_t * p_access )
 {
-    thread_vcd_data_t * p_vcd;
-    unsigned int        i;
-    unsigned int        num_segments;
-    
+    vcdplayer_t   *p_vcdplayer = (vcdplayer_t *) p_access->p_sys;
+    unsigned int  i;
+    input_title_t *t;
 
-    p_vcd = (thread_vcd_data_t *) p_input->p_access_data;
-    num_segments = p_vcd->num_segments = vcdinfo_get_num_segments(p_vcd->vcd);
+    p_vcdplayer->i_segments = vcdinfo_get_num_segments(p_vcdplayer->vcd);
 
-#define area p_input->stream.pp_areas
+    dbg_print( (INPUT_DBG_CALL|INPUT_DBG_MRL),
+               "Segments: %d", p_vcdplayer->i_segments);
 
-    /* area 0 is reserved for segments. Set Absolute start offset
-         and size */
-    area[0]->i_plugin_data = 0;
-    input_DelArea( p_input, area[0] );
-    input_AddArea( p_input, 0, 0 );
-    
-    area[0]->i_start = (off_t)p_vcd->p_sectors[0] 
-      * (off_t)M2F2_SECTOR_SIZE;
-    area[0]->i_size = (off_t)(p_vcd->p_sectors[1] - p_vcd->p_sectors[0])
-      * (off_t)M2F2_SECTOR_SIZE;
-    
-    /* Default Segment  */
-    area[0]->i_part = 0;
-    
-    /* i_plugin_data is used to store which entry point is the first
-       of the track (area) */
-    area[0]->i_plugin_data = 0;
+    if ( 0 == p_vcdplayer->i_segments ) return false;
 
-    area[0]->i_part_nb = 0;
-    
-    dbg_print( INPUT_DBG_MRL, "area id %d, for segment track %d", 
-               area[0]->i_id, 0 );
+    t = p_vcdplayer->p_title[p_vcdplayer->i_titles] = vlc_input_title_New();
+    p_vcdplayer->i_titles++;
 
-    if (num_segments == 0) return 0;
+    t->i_size    = 0; /* Not sure Segments have a size associated */
+    t->psz_name  = strdup(_("Segments"));
 
     /* We have one additional segment allocated so we can get the size
        by subtracting seg[i+1] - seg[i].
      */
-    p_vcd->p_segments = malloc( sizeof( lba_t ) * (num_segments+1) );
-    if( p_vcd->p_segments == NULL )
+    p_vcdplayer->p_segments=malloc(sizeof(lsn_t)*(p_vcdplayer->i_segments+1));
+    if( p_vcdplayer->p_segments == NULL )
     {
         LOG_ERR ("not enough memory for segment treatment" );
-        return -1;
+        return false;
     }
 
-    /* Update the navigation variables without triggering a callback */
-    VCDUpdateVar( p_input, 0, VLC_VAR_SETVALUE, "title", "Setting track" );
-    var_Change( p_input, "chapter", VLC_VAR_CLEARCHOICES, NULL, NULL );
-    
-    for( i = 0 ; i < num_segments ; i++ )
+    for( i = 0 ; i < p_vcdplayer->i_segments ; i++ )
     {
-      p_vcd->p_segments[i] = vcdinfo_get_seg_lsn(p_vcd->vcd, i);
-      area[0]->i_part_nb ++;
-      VCDUpdateVar( p_input, i , VLC_VAR_ADDCHOICE, 
-                    "chapter", "Adding segment choice");
+        char psz_segment[100];
+        seekpoint_t *s = vlc_seekpoint_New();
+        p_vcdplayer->p_segments[i] = vcdinfo_get_seg_lsn(p_vcdplayer->vcd, i);
+
+        snprintf( psz_segment, sizeof(psz_segment), "%s %02d", _("Segment"),
+                  i );
+
+        s->i_byte_offset = 0; /* Not sure what this would mean here */
+        s->psz_name  = strdup(psz_segment);
+        TAB_APPEND( t->i_seekpoint, t->seekpoint, s );
     }
 
-#undef area
+    p_vcdplayer->p_segments[p_vcdplayer->i_segments] =
+      p_vcdplayer->p_segments[p_vcdplayer->i_segments-1]+
+      vcdinfo_get_seg_sector_count(p_vcdplayer->vcd,
+                                   p_vcdplayer->i_segments-1);
 
-    p_vcd->p_segments[num_segments] = p_vcd->p_segments[num_segments-1]+
-      vcdinfo_get_seg_sector_count(p_vcd->vcd, num_segments-1);
-    
-    return 0;
+    return true;
 }
 
 /*****************************************************************************
- VCDTracks: initializes area information. 
- Before calling this track information should have been read in.
+ Build title table which will be returned via ACCESS_GET_TITLE_INFO.
+
+ We start area addressing for tracks at 1 since the default area 0
+ is reserved for segments.
  *****************************************************************************/
-static void
-VCDTracks( input_thread_t * p_input )
+static int
+VCDTitles( access_t * p_access )
 {
-    thread_vcd_data_t * p_vcd;
-    unsigned int        i;
-
-    p_vcd = (thread_vcd_data_t *) p_input->p_access_data;
-
-#define area p_input->stream.pp_areas
-
-    /* We start area addressing for tracks at 1 since the default area 0
-       is reserved for segments */
+    /* We'll assume a VCD has its first MPEG track
+       cdio_get_first_track_num()+1 could be used if one wanted to be
+       very careful about this. Note: cdio_get_first_track() will give the
+       ISO-9660 track before the MPEG tracks.
+     */
+    if (!p_access || !p_access->p_sys) return VLC_EGENERIC;
 
-    for( i = 1 ; i < p_vcd->num_tracks ; i++ )
     {
-        /* Tracks are Program Chains */
-        input_AddArea( p_input, i, i );
-
-        /* Absolute start byte offset and byte size */
-        area[i]->i_start = (off_t) p_vcd->p_sectors[i] 
-                           * (off_t)M2F2_SECTOR_SIZE;
-        area[i]->i_size = (off_t)(p_vcd->p_sectors[i+1] - p_vcd->p_sectors[i])
-                           * (off_t)M2F2_SECTOR_SIZE;
+        vcdplayer_t *p_vcdplayer = (vcdplayer_t *) p_access->p_sys;
+        track_t      i;
 
-        /* Current entry being played in track */
-        area[i]->i_part = 0;
+        p_vcdplayer->i_titles = 0;
+        for( i = 1 ; i <= p_vcdplayer->i_tracks ; i++ )
+        {
+            input_title_t *t = p_vcdplayer->p_title[i-1] =
+                                                        vlc_input_title_New();
+            char psz_track[80];
 
-        /* i_plugin_data is used to store which entry point is the first
-         * of the track (area) */
-        area[i]->i_plugin_data = 0;
+            snprintf( psz_track, sizeof(psz_track), "%s %02d", _("Track"), i );
+            t->i_size    = (int64_t) vcdinfo_get_track_size( p_vcdplayer->vcd,
+                                 i ) * M2F2_SECTOR_SIZE / CDIO_CD_FRAMESIZE ;
+            t->psz_name  = strdup(psz_track);
 
-        dbg_print( INPUT_DBG_MRL, 
-                   "area[%d] id: %d, i_start: %lld, i_size: %lld", 
-                   i, area[i]->i_id, area[i]->i_start, area[i]->i_size );
-    }
+            dbg_print( INPUT_DBG_MRL, "track[%d] i_size: %"PRIi64, i, t->i_size );
 
-#undef area
+            p_vcdplayer->i_titles++;
+        }
 
-    return ;
+      return VLC_SUCCESS;
+    }
 }
 
 /*****************************************************************************
   VCDLIDs: Reads the LIST IDs from the LOT.
  *****************************************************************************/
-static int 
-VCDLIDs( input_thread_t * p_input )
+static bool
+VCDLIDs( access_t * p_access )
 {
-    thread_vcd_data_t *p_vcd = (thread_vcd_data_t *) p_input->p_access_data;
-
-    p_vcd->num_lids = vcdinfo_get_num_LIDs(p_vcd->vcd);
-    p_vcd->cur_lid  = VCDINFO_INVALID_ENTRY;
-
-    if (vcdinfo_read_psd (p_vcd->vcd)) {
-      
-      vcdinfo_visit_lot (p_vcd->vcd, false);
-      
-#if FIXED
-    /* 
-       We need to change libvcdinfo to be more robust when there are 
-       problems reading the extended PSD. Given that area-highlighting and 
+    vcdplayer_t   *p_vcdplayer = (vcdplayer_t *) p_access->p_sys;
+    input_title_t *t;
+    unsigned int   i_lid, i_title;
+
+    p_vcdplayer->i_lids = vcdinfo_get_num_LIDs(p_vcdplayer->vcd);
+    p_vcdplayer->i_lid  = VCDINFO_INVALID_ENTRY;
+
+    dbg_print( (INPUT_DBG_CALL|INPUT_DBG_MRL),
+               "LIDs: %d", p_vcdplayer->i_lids);
+
+    if ( 0 == p_vcdplayer->i_lids ) return false;
+
+    if (vcdinfo_read_psd (p_vcdplayer->vcd)) {
+
+      vcdinfo_visit_lot (p_vcdplayer->vcd, false);
+
+#ifdef FIXED
+    /*
+       We need to change libvcdinfo to be more robust when there are
+       problems reading the extended PSD. Given that area-highlighting and
        selection features in the extended PSD haven't been implemented,
        it's best then to not try to read this at all.
      */
-      if (vcdinfo_get_psd_x_size(p_vcd->vcd))
-        vcdinfo_visit_lot (p_vcd->vcd, true);
-#endif 
+      if (vcdinfo_get_psd_x_size(p_vcdplayer->vcd))
+        vcdinfo_visit_lot (p_vcdplayer->vcd, true);
+#endif
     }
 
-    dbg_print( (INPUT_DBG_CALL|INPUT_DBG_MRL), 
-               "num LIDs=%d", p_vcd->num_lids);
+    /* Set up LIDs Navigation Menu */
+    t = vlc_input_title_New();
+    t->b_menu = true;
+    t->psz_name = strdup( "LIDs" );
+
+    i_title = p_vcdplayer->i_tracks;
+    for( i_lid =  1 ; i_lid <=  p_vcdplayer->i_lids ; i_lid++ )
+    {
+        char psz_lid[100];
+        seekpoint_t *s = vlc_seekpoint_New();
+
+        snprintf( psz_lid, sizeof(psz_lid), "%s %02d", _("LID"), i_lid );
 
-    return 0;
+        s->i_byte_offset = 0; /*  A lid doesn't have an offset
+                                  size associated with it */
+        s->psz_name  = strdup(psz_lid);
+        TAB_APPEND( t->i_seekpoint, t->seekpoint, s );
+    }
+
+#ifdef DYNAMICALLY_ALLOCATED
+    TAB_APPEND( p_vcdplayer->i_titles, p_vcdplayer->p_title, t );
+#else
+    p_vcdplayer->p_title[p_vcdplayer->i_titles] = t;
+    p_vcdplayer->i_titles++;
+#endif
+
+    return true;
 }
 
 /*****************************************************************************
  * VCDParse: parse command line
  *****************************************************************************/
-static char * 
-VCDParse( input_thread_t * p_input, /*out*/ vcdinfo_itemid_t * p_itemid )
+static char *
+VCDParse( access_t * p_access, /*out*/ vcdinfo_itemid_t * p_itemid,
+          /*out*/ bool *play_single_item )
 {
-    thread_vcd_data_t *p_vcd = (thread_vcd_data_t *)p_input->p_access_data;
-    char *             psz_parser;
-    char *             psz_source;
-    char *             psz_next;
+    vcdplayer_t *p_vcdplayer = (vcdplayer_t *)p_access->p_sys;
+    char        *psz_parser;
+    char        *psz_source;
+    char        *psz_next;
+
+    if( var_InheritInteger( p_access, MODULE_STRING "-PBC" ) ) {
+      p_itemid->type = VCDINFO_ITEM_TYPE_LID;
+      p_itemid->num = 1;
+      *play_single_item = false;
+    }
+    else
+    {
+      p_itemid->type = VCDINFO_ITEM_TYPE_ENTRY;
+      p_itemid->num = 0;
+    }
 
-    p_itemid->type=VCDINFO_ITEM_TYPE_TRACK;
-    p_itemid->num=1;
-    
 #ifdef WIN32
     /* On Win32 we want the VCD access plugin to be explicitly requested,
      * we end up with lots of problems otherwise */
-    if( !p_input->psz_access || !*p_input->psz_access ) return NULL;
+    if( !p_access->psz_access || !*p_access->psz_access ) return NULL;
 #endif
 
-    if( !p_input->psz_name )
+    if( !p_access->psz_path )
     {
         return NULL;
     }
 
-    psz_parser = psz_source = strdup( p_input->psz_name );
+    psz_parser = psz_source = strdup( p_access->psz_path );
 
     /* Parse input string :
      * [device][@[type][title]] */
@@ -940,153 +579,611 @@ VCDParse( input_thread_t * p_input, /*out*/ vcdinfo_itemid_t * p_itemid )
 
     if( *psz_parser == '@' )
     {
-      /* Found the divide between the source name and the 
+      /* Found the divide between the source name and the
          type+entry number. */
       unsigned int num;
-      
-      *psz_parser = '\0';
-      ++psz_parser;
-      if( *psz_parser )
-        {
-          switch(*psz_parser) {
-          case 'E': 
+
+        *psz_parser = '\0';
+        ++psz_parser;
+        if( *psz_parser )
+        switch(*psz_parser) {
+        case 'E':
             p_itemid->type = VCDINFO_ITEM_TYPE_ENTRY;
             ++psz_parser;
+            *play_single_item = true;
             break;
-          case 'P': 
+        case 'P':
             p_itemid->type = VCDINFO_ITEM_TYPE_LID;
             ++psz_parser;
+            *play_single_item = false;
             break;
-          case 'S': 
+        case 'S':
             p_itemid->type = VCDINFO_ITEM_TYPE_SEGMENT;
             ++psz_parser;
+            *play_single_item = true;
             break;
-          case 'T': 
+        case 'T':
             p_itemid->type = VCDINFO_ITEM_TYPE_TRACK;
             ++psz_parser;
+            *play_single_item = true;
+            break;
+        default:
             break;
-          default: ;
-          }
         }
-      
-      num = strtol( psz_parser, &psz_next, 10 );
-      if ( *psz_parser != '\0' && *psz_next == '\0') 
+
+        num = strtol( psz_parser, &psz_next, 10 );
+        if ( *psz_parser != '\0' && *psz_next == '\0')
         {
-          p_itemid->num = num;
+            p_itemid->num = num;
         }
-      
+
+    } else {
+        *play_single_item = ( VCDINFO_ITEM_TYPE_LID == p_itemid->type );
     }
 
+
     if( !*psz_source )
     {
-        if( !p_input->psz_access )
+
+        /* No source specified, so figure it out. */
+        if( !p_access->psz_access ) return NULL;
+
+        psz_source = var_InheritString( p_access, "vcd" );
+
+        if( !psz_source )
         {
-            return NULL;
+            /* Scan for a CD-ROM drive with a VCD in it. */
+            char **cd_drives = cdio_get_devices_with_cap(NULL,
+                                       (CDIO_FS_ANAL_SVCD|CDIO_FS_ANAL_CVD
+                                       |CDIO_FS_ANAL_VIDEOCD|CDIO_FS_UNKNOWN),
+                                       true);
+            if( NULL == cd_drives ) return NULL;
+            if( cd_drives[0] == NULL )
+            {
+                cdio_free_device_list( cd_drives );
+                return NULL;
+            }
+            psz_source = strdup( cd_drives[0] );
+            cdio_free_device_list( cd_drives );
         }
-        psz_source = config_GetPsz( p_input, "vcd" );
-        if( !psz_source ) return NULL;
     }
 
-    dbg_print( (INPUT_DBG_CALL|INPUT_DBG_MRL), 
+    dbg_print( (INPUT_DBG_CALL|INPUT_DBG_MRL),
                "source=%s entry=%d type=%d",
                psz_source, p_itemid->num, p_itemid->type);
 
     return psz_source;
 }
 
-/* 
-   Set's start origin subsequent seeks/reads
+/*
+   Sets start origin for subsequent seeks/reads
 */
-static void 
-VCDSetOrigin( input_thread_t *p_input, lsn_t origin_lsn, 
-              lsn_t cur_lsn, lsn_t end_lsn, int cur_entry, track_t cur_track )
+void
+VCDSetOrigin( access_t *p_access, lsn_t i_lsn, track_t i_track,
+              const vcdinfo_itemid_t *p_itemid )
 {
-  thread_vcd_data_t * p_vcd  = (thread_vcd_data_t *) p_input->p_access_data;
-
-  p_vcd->origin_lsn = origin_lsn;
-  p_vcd->cur_lsn    = cur_lsn;
-  p_vcd->end_lsn    = end_lsn;
-  p_vcd->cur_track  = cur_track;
-  p_vcd->play_item.num  = cur_entry;
-  p_vcd->play_item.type = VCDINFO_ITEM_TYPE_ENTRY;
-  
-  dbg_print( (INPUT_DBG_CALL|INPUT_DBG_LSN),
-             "origin: %d, cur_lsn: %d, end_lsn: %d, entry: %d, track: %d",
-             origin_lsn, cur_lsn, end_lsn, cur_entry, cur_track );
-
-  p_input->stream.p_selected_area->i_tell =
-    (off_t) (p_vcd->cur_lsn - p_vcd->origin_lsn) * (off_t)M2F2_SECTOR_SIZE;
-
-  VCDUpdateVar( p_input, cur_entry, VLC_VAR_SETVALUE, 
-                "chapter", "Setting entry");
+    vcdplayer_t *p_vcdplayer= (vcdplayer_t *)p_access->p_sys;
+
+    dbg_print( (INPUT_DBG_CALL|INPUT_DBG_LSN),
+               "i_lsn: %lu, track: %d", (long unsigned int) i_lsn, i_track );
+
+    vcdplayer_set_origin(p_access, i_lsn, i_track, p_itemid);
+
+    switch (p_vcdplayer->play_item.type)
+    {
+    case VCDINFO_ITEM_TYPE_ENTRY:
+        VCDUpdateVar( p_access, p_itemid->num, VLC_VAR_SETVALUE,
+                      "chapter", _("Entry"), "Setting entry/segment");
+        p_access->info.i_title     = i_track-1;
+        if (p_vcdplayer->b_track_length)
+        {
+            p_access->info.i_size = p_vcdplayer->p_title[i_track-1]->i_size;
+            p_access->info.i_pos  = (uint64_t) M2F2_SECTOR_SIZE *
+                     (vcdinfo_get_track_lsn(p_vcdplayer->vcd, i_track)-i_lsn);
+        } else {
+            p_access->info.i_size = M2F2_SECTOR_SIZE * (int64_t)
+                 vcdinfo_get_entry_sect_count(p_vcdplayer->vcd,p_itemid->num);
+            p_access->info.i_pos = 0;
+        }
+        dbg_print( (INPUT_DBG_LSN|INPUT_DBG_PBC), "size: %"PRIu64", pos: %"PRIu64,
+                   p_access->info.i_size, p_access->info.i_pos );
+        p_access->info.i_seekpoint = p_itemid->num;
+        break;
+
+    case VCDINFO_ITEM_TYPE_SEGMENT:
+        VCDUpdateVar( p_access, p_itemid->num, VLC_VAR_SETVALUE,
+                      "chapter", _("Segment"),  "Setting entry/segment");
+        /* The last title entry is the for segments (when segments exist
+           and they must here. The segment seekpoints are stored after
+           the entry seekpoints and (zeroed) lid seekpoints.
+        */
+        p_access->info.i_title     = p_vcdplayer->i_titles - 1;
+        p_access->info.i_size      = 0; /* No seeking on stills, please. */
+        p_access->info.i_pos       = 0;
+        p_access->info.i_seekpoint = p_vcdplayer->i_entries
+                                   + p_vcdplayer->i_lids + p_itemid->num;
+        break;
+
+    case VCDINFO_ITEM_TYPE_TRACK:
+        p_access->info.i_title     = i_track-1;
+        p_access->info.i_size      = p_vcdplayer->p_title[i_track-1]->i_size;
+        p_access->info.i_pos       = 0;
+        p_access->info.i_seekpoint = vcdinfo_track_get_entry(p_vcdplayer->vcd,
+                                                             i_track);
+        break;
+
+    default:
+        msg_Warn( p_access, "can't set origin for play type %d",
+                  p_vcdplayer->play_item.type );
+    }
+
+    p_access->info.i_update = INPUT_UPDATE_TITLE|INPUT_UPDATE_SIZE
+                              |INPUT_UPDATE_SEEKPOINT;
+
+    VCDUpdateTitle( p_access );
+
 }
 
 /*****************************************************************************
- * vcd_Open: Opens a VCD device or file and returns an opaque handle
+ * vcd_Open: Opens a VCD device or file initializes, a list of
+   tracks, segements and entry lsns and sizes and returns an opaque handle.
  *****************************************************************************/
 static vcdinfo_obj_t *
 vcd_Open( vlc_object_t *p_this, const char *psz_dev )
 {
+    access_t    *p_access = (access_t *)p_this;
+    vcdplayer_t *p_vcdplayer    = (vcdplayer_t *) p_access->p_sys;
     vcdinfo_obj_t *p_vcdobj;
     char  *actual_dev;
+    unsigned int i;
+
+    dbg_print(INPUT_DBG_CALL, "called with %s", psz_dev);
 
     if( !psz_dev ) return NULL;
 
-    /* Set where to log errors messages from libcdio. */
-    p_vcd_input = (input_thread_t *)p_this;
-    cdio_log_set_handler ( cdio_log_handler );
-    vcd_log_set_handler ( vcd_log_handler );
-    
-    actual_dev=strdup(psz_dev);
-    if ( vcdinfo_open(&p_vcdobj, &actual_dev, DRIVER_UNKNOWN, NULL) != 
-         VCDINFO_OPEN_VCD) {
-      free(actual_dev);
-      return NULL;
+    actual_dev= ToLocaleDup(psz_dev);
+    if( vcdinfo_open(&p_vcdobj, &actual_dev, DRIVER_UNKNOWN, NULL) !=
+                                                    VCDINFO_OPEN_VCD)
+    {
+        free(actual_dev);
+        return NULL;
     }
     free(actual_dev);
 
+    /*
+       Save summary info on tracks, segments and entries...
+    */
+
+    if ( 0 < (p_vcdplayer->i_tracks = vcdinfo_get_num_tracks(p_vcdobj)) )
+    {
+        p_vcdplayer->track = (vcdplayer_play_item_info_t *)
+          calloc(p_vcdplayer->i_tracks, sizeof(vcdplayer_play_item_info_t));
+
+        for (i=0; i<p_vcdplayer->i_tracks; i++)
+        {
+            unsigned int track_num=i+1;
+            p_vcdplayer->track[i].size  =
+            vcdinfo_get_track_sect_count(p_vcdobj, track_num);
+            p_vcdplayer->track[i].start_LSN =
+            vcdinfo_get_track_lsn(p_vcdobj, track_num);
+        }
+    } else
+        p_vcdplayer->track = NULL;
+
+    if( 0 < (p_vcdplayer->i_entries = vcdinfo_get_num_entries(p_vcdobj)) )
+    {
+        p_vcdplayer->entry = (vcdplayer_play_item_info_t *)
+            calloc(p_vcdplayer->i_entries, sizeof(vcdplayer_play_item_info_t));
+
+        for (i=0; i<p_vcdplayer->i_entries; i++)
+        {
+            p_vcdplayer->entry[i].size =
+                                    vcdinfo_get_entry_sect_count(p_vcdobj, i);
+            p_vcdplayer->entry[i].start_LSN =
+                                           vcdinfo_get_entry_lsn(p_vcdobj, i);
+        }
+    } else
+      p_vcdplayer->entry = NULL;
+
+    if ( 0 < (p_vcdplayer->i_segments = vcdinfo_get_num_segments(p_vcdobj)) )
+    {
+        p_vcdplayer->segment = (vcdplayer_play_item_info_t *)
+          calloc(p_vcdplayer->i_segments,  sizeof(vcdplayer_play_item_info_t));
+
+        for (i=0; i<p_vcdplayer->i_segments; i++)
+        {
+            p_vcdplayer->segment[i].size =
+                                    vcdinfo_get_seg_sector_count(p_vcdobj, i);
+            p_vcdplayer->segment[i].start_LSN =
+                                             vcdinfo_get_seg_lsn(p_vcdobj, i);
+        }
+    } else
+      p_vcdplayer->segment = NULL;
+
     return p_vcdobj;
 }
 
 /****************************************************************************
- * VCDReadSector: Read a sector (2324 bytes)
- ****************************************************************************/
-static int 
-VCDReadSector( vlc_object_t *p_this, const vcdinfo_obj_t *p_vcd,
-               lsn_t cur_lsn, byte_t * p_buffer )
+ Update the "varname" variable to i_num without triggering a callback.
+****************************************************************************/
+static void
+VCDUpdateVar( access_t *p_access, int i_num, int i_action,
+              const char *p_varname, char *p_label,
+              const char *p_debug_label)
 {
-  typedef struct {
-    uint8_t subheader   [8];
-    uint8_t data        [M2F2_SECTOR_SIZE];
-  } vcdsector_t;
-  vcdsector_t vcd_sector;
-  
-  if (cdio_read_mode2_sector(vcdinfo_get_cd_image(p_vcd), 
-                             &vcd_sector, cur_lsn, true) 
-      != 0)
+    vlc_value_t val;
+    val.i_int = i_num;
+    if( p_access )
     {
-      msg_Warn( p_this, "Could not read LSN %d", cur_lsn );
-      return -1;
+        const vcdplayer_t *p_vcdplayer = (vcdplayer_t *)p_vcd_access->p_sys;
+        dbg_print( INPUT_DBG_PBC, "%s %d", p_debug_label, i_num );
     }
-    
-  memcpy (p_buffer, vcd_sector.data, M2F2_SECTOR_SIZE);
-  
-  return( 0 );
+    if( p_label )
+    {
+        vlc_value_t text;
+        text.psz_string = p_label;
+        var_Change( p_access, p_varname, VLC_VAR_SETTEXT, &text, NULL );
+    }
+    var_Change( p_access, p_varname, i_action, &val, NULL );
 }
 
-/****************************************************************************
- Update the "varname" variable to i_num without triggering a callback.
-****************************************************************************/
-static void
-VCDUpdateVar( input_thread_t *p_input, int i_num, int i_action,
-              const char *varname, const char *label)
+
+/*****************************************************************************
+ * Public routines.
+ *****************************************************************************/
+
+/*****************************************************************************
+  VCDOpen: open VCD.
+  read in meta-information about VCD: the number of tracks, segments,
+  entries, size and starting information. Then set up state variables so
+  that we read/seek starting at the location specified.
+
+  On success we return VLC_SUCCESS, on memory exhausted VLC_ENOMEM,
+  and VLC_EGENERIC for some other error.
+ *****************************************************************************/
+int
+VCDOpen ( vlc_object_t *p_this )
+{
+    access_t         *p_access = (access_t *)p_this;
+    vcdplayer_t      *p_vcdplayer;
+    char             *psz_source;
+    vcdinfo_itemid_t  itemid;
+    bool        play_single_item = false;
+
+    p_access->pf_read          = NULL;
+    p_access->pf_block         = VCDReadBlock;
+    p_access->pf_control       = VCDControl;
+    p_access->pf_seek          = VCDSeek;
+
+    p_access->info.i_update    = 0;
+    p_access->info.i_size      = 0;
+    p_access->info.i_pos       = 0;
+    p_access->info.b_eof       = false;
+    p_access->info.i_title     = 0;
+    p_access->info.i_seekpoint = 0;
+
+    p_vcdplayer = malloc( sizeof(vcdplayer_t) );
+
+    if( p_vcdplayer == NULL )
+        return VLC_ENOMEM;
+
+    p_vcdplayer->i_debug = var_InheritInteger( p_this, MODULE_STRING "-debug" );
+    p_access->p_sys = (access_sys_t *) p_vcdplayer;
+
+    /* Set where to log errors messages from libcdio. */
+    p_vcd_access = p_access;
+    cdio_log_set_handler ( cdio_log_handler );
+    vcd_log_set_handler ( vcd_log_handler );
+
+    psz_source = VCDParse( p_access, &itemid, &play_single_item );
+
+    if ( NULL == psz_source )
+    {
+        free( p_vcdplayer );
+        return( VLC_EGENERIC );
+    }
+
+    dbg_print( (INPUT_DBG_CALL|INPUT_DBG_EXT), "source: %s: mrl: %s",
+               psz_source, p_access->psz_path );
+
+    p_vcdplayer->psz_source        = strdup(psz_source);
+    p_vcdplayer->i_blocks_per_read = var_InheritInteger( p_this, MODULE_STRING
+                                                    "-blocks-per-read" );
+    p_vcdplayer->b_track_length    = var_InheritInteger( p_this, MODULE_STRING
+                                                    "-track-length" );
+    p_vcdplayer->in_still          = false;
+    p_vcdplayer->play_item.type    = VCDINFO_ITEM_TYPE_NOTFOUND;
+    p_vcdplayer->p_input           = access_GetParentInput( p_access );
+//    p_vcdplayer->p_meta            = vlc_meta_New();
+    p_vcdplayer->p_segments        = NULL;
+    p_vcdplayer->p_entries         = NULL;
+
+    /* set up input  */
+
+    if( !(p_vcdplayer->vcd = vcd_Open( p_this, psz_source )) )
+    {
+        goto err_exit;
+    }
+
+    p_vcdplayer->b_svd = vcdinfo_get_tracksSVD(p_vcdplayer->vcd);
+
+    /* Get track information. */
+    p_vcdplayer->i_tracks = vcdinfo_get_num_tracks(p_vcdplayer->vcd);
+
+    if( p_vcdplayer->i_tracks<1 || CDIO_INVALID_TRACK==p_vcdplayer->i_tracks )
+    {
+        vcdinfo_close( p_vcdplayer->vcd );
+        LOG_ERR ("no movie tracks found" );
+        goto err_exit;
+    }
+
+    /* Build Navigation Title table for the tracks. */
+    VCDTitles( p_access );
+
+    /* Add into the above entry points as "Chapters". */
+    if( ! VCDEntryPoints( p_access ) )
+    {
+        msg_Warn( p_access, "could not read entry points, will not use them" );
+        p_vcdplayer->b_valid_ep = false;
+    }
+
+    /* Initialize LID info and add that as a menu item */
+    if( ! VCDLIDs( p_access ) )
+    {
+        msg_Warn( p_access, "could not read entry LIDs" );
+    }
+
+    /* Do we set PBC (via LID) on? */
+    p_vcdplayer->i_lid =
+      ( VCDINFO_ITEM_TYPE_LID == itemid.type
+        && p_vcdplayer->i_lids > itemid.num )
+      ? itemid.num
+      :  VCDINFO_INVALID_ENTRY;
+
+    /* Initialize segment information and add that a "Track". */
+    VCDSegments( p_access );
+
+    vcdplayer_play( p_access, itemid );
+
+    free( p_access->psz_demux );
+    p_access->psz_demux = strdup( "ps" );
+
+#ifdef FIXED
+    if( play_single_item )
+        VCDFixupPlayList(p_access,p_vcd,psz_source,&itemid,play_single_item);
+#endif
+
+#ifdef FIXED
+    p_vcdplayer->p_intf = intf_Create( p_access, "vcdx" );
+    p_vcdplayer->p_intf->b_block = false;
+#endif
+    p_vcdplayer->p_access = p_access;
+
+    free( psz_source );
+
+    return VLC_SUCCESS;
+ err_exit:
+    if( p_vcdplayer->p_input ) vlc_object_release( p_vcdplayer->p_input );
+    free( psz_source );
+    free( p_vcdplayer->psz_source );
+    free( p_vcdplayer );
+    return VLC_EGENERIC;
+}
+
+/*****************************************************************************
+ * VCDClose: closes VCD releasing allocated memory.
+ *****************************************************************************/
+void
+VCDClose ( vlc_object_t *p_this )
 {
-  vlc_value_t val;
-  val.i_int = i_num;
-  if (NULL != p_vcd_input) {
-    thread_vcd_data_t *p_vcd = (thread_vcd_data_t *)p_vcd_input->p_access_data;
-    dbg_print( INPUT_DBG_PBC, "%s %d", label, i_num );
-  }
-  var_Change( p_input, varname, i_action, &val, NULL );
+    access_t    *p_access = (access_t *)p_this;
+    vcdplayer_t *p_vcdplayer = (vcdplayer_t *)p_access->p_sys;
+
+    dbg_print( (INPUT_DBG_CALL|INPUT_DBG_EXT), "VCDClose" );
+
+    {
+        unsigned int i;
+        for (i=0 ; i<p_vcdplayer->i_titles; i++)
+            if (p_vcdplayer->p_title[i])
+                free(p_vcdplayer->p_title[i]->psz_name);
+    }
+    vcdinfo_close( p_vcdplayer->vcd );
+
+    if( p_vcdplayer->p_input ) vlc_object_release( p_vcdplayer->p_input );
+
+    FREENULL( p_vcdplayer->p_entries );
+    FREENULL( p_vcdplayer->p_segments );
+    FREENULL( p_vcdplayer->psz_source );
+    FREENULL( p_vcdplayer->track );
+    FREENULL( p_vcdplayer->segment );
+    FREENULL( p_vcdplayer->entry );
+    FREENULL( p_access->psz_demux );
+    FREENULL( p_vcdplayer );
+    p_vcd_access    = NULL;
 }
+
+/*****************************************************************************
+ * Control: The front-end or vlc engine calls here to ether get
+ * information such as meta information or plugin capabilities or to
+ * issue miscellaneous "set" requests.
+ *****************************************************************************/
+static int VCDControl( access_t *p_access, int i_query, va_list args )
+{
+    vcdplayer_t *p_vcdplayer = (vcdplayer_t *)p_access->p_sys;
+    int         *pi_int;
+    int i;
+
+    dbg_print( (INPUT_DBG_CALL|INPUT_DBG_EXT|INPUT_DBG_EVENT),
+               "query %d", i_query );
+
+    switch( i_query )
+    {
+        /* Pass back a copy of meta information that was gathered when we
+           during the Open/Initialize call.
+         */
+    case ACCESS_GET_META:
+        dbg_print( INPUT_DBG_EVENT, "get meta info" );
+#if 0
+        if( p_vcdplayer->p_meta )
+        {
+            vlc_meta_t **pp_meta = (vlc_meta_t**)va_arg(args,vlc_meta_t**);
+
+            *pp_meta = vlc_meta_Duplicate( p_vcdplayer->p_meta );
+            dbg_print( INPUT_DBG_META, "%s", "Meta copied" );
+        }
+        else
 #endif
+            msg_Warn( p_access, "tried to copy NULL meta info" );
+
+        return VLC_SUCCESS;
+
+    case ACCESS_CAN_SEEK:
+    case ACCESS_CAN_FASTSEEK:
+    case ACCESS_CAN_PAUSE:
+    case ACCESS_CAN_CONTROL_PACE:
+
+        dbg_print( INPUT_DBG_EVENT,
+                   "seek/fastseek/pause/can_control_pace" );
+        *((bool*)va_arg( args, bool* )) = true;
+        return VLC_SUCCESS;
+
+    /* */
+    case ACCESS_GET_PTS_DELAY:
+        *(int64_t*)va_arg(args,int64_t *) = MILLISECONDS_PER_SEC *
+                         var_GetInteger( p_access, MODULE_STRING "-caching" );
+        dbg_print( INPUT_DBG_EVENT, "GET PTS DELAY" );
+        return VLC_SUCCESS;
+
+        /* */
+    case ACCESS_SET_PAUSE_STATE:
+        dbg_print( INPUT_DBG_EVENT, "SET PAUSE STATE" );
+        return VLC_SUCCESS;
+
+    case ACCESS_GET_TITLE_INFO:
+    {
+        unsigned int psz_mrl_max = strlen(VCD_MRL_PREFIX)
+          + strlen(p_vcdplayer->psz_source) + sizeof("@E999")+3;
+        input_title_t ***ppp_title
+          = (input_title_t***)va_arg( args, input_title_t*** );
+        char *psz_mrl = malloc( psz_mrl_max );
+        unsigned int i;
+
+        pi_int    = (int*)va_arg( args, int* );
+
+        dbg_print( INPUT_DBG_EVENT, "GET TITLE: i_titles %d",
+                   p_vcdplayer->i_titles );
+
+        if( psz_mrl  )
+        {
+            snprintf(psz_mrl, psz_mrl_max, "%s%s",
+                     VCD_MRL_PREFIX, p_vcdplayer->psz_source);
+            VCDMetaInfo( p_access, psz_mrl );
+            free(psz_mrl);
+        }
+
+        /* Duplicate title info */
+        if( p_vcdplayer->i_titles == 0 )
+        {
+            *pi_int = 0; ppp_title = NULL;
+            return VLC_SUCCESS;
+        }
+        *pi_int = p_vcdplayer->i_titles;
+        *ppp_title = malloc(sizeof(input_title_t **)*p_vcdplayer->i_titles);
+
+        if (!*ppp_title) return VLC_ENOMEM;
+
+        for( i = 0; i < p_vcdplayer->i_titles; i++ )
+            if( p_vcdplayer->p_title[i] )
+                (*ppp_title)[i] =
+                           vlc_input_title_Duplicate(p_vcdplayer->p_title[i]);
+    }
+    break;
+
+    case ACCESS_SET_TITLE:
+        i = (int)va_arg( args, int );
+
+        dbg_print( INPUT_DBG_EVENT, "set title %d" , i);
+        if( i != p_access->info.i_title )
+        {
+            vcdinfo_itemid_t itemid;
+            track_t          i_track = i+1;
+            unsigned int     i_entry =
+                        vcdinfo_track_get_entry(p_vcdplayer->vcd,i_track);
+
+            if( i < p_vcdplayer->i_tracks )
+            {
+                /* FIXME! For now we are assuming titles are only
+                   tracks and that track == title+1 */
+                itemid.num = i_track;
+                itemid.type = VCDINFO_ITEM_TYPE_TRACK;
+            }
+            else
+            {
+                /* FIXME! i_tracks+2 are Segments, but we need to be able
+                   to figure out which segment of that. i_tracks+1 is
+                   either Segments (if no LIDs) or LIDs otherwise. Again
+                   need a way to get the LID number. */
+
+                msg_Warn(p_access,"Trying to set track (%u) beyond end "
+                         "of last track (%u).",i+1,p_vcdplayer->i_tracks);
+                return VLC_EGENERIC;
+            }
+
+            VCDSetOrigin(p_access,
+                         vcdinfo_get_entry_lsn(p_vcdplayer->vcd,i_entry),
+                         i_track, &itemid);
+        }
+        break;
+
+    case ACCESS_SET_SEEKPOINT:
+    {
+        input_title_t *t = p_vcdplayer->p_title[p_access->info.i_title];
+        unsigned int i = (unsigned int)va_arg( args, unsigned int );
+
+        dbg_print( INPUT_DBG_EVENT, "set seekpoint %d", i );
+        if( t->i_seekpoint > 0 )
+        {
+            track_t i_track = p_access->info.i_title+1;
+            lsn_t lsn;
+
+            /* FIXME! For now we are assuming titles are only tracks and
+               that track == title+1 and we the play item is entries (not
+               tracks or lids). We need to generalize all of this.
+             */
+
+            if (i < p_vcdplayer->i_entries)
+            {
+                p_vcdplayer->play_item.num  = i;
+                p_vcdplayer->play_item.type = VCDINFO_ITEM_TYPE_ENTRY;
+                lsn = vcdinfo_get_entry_lsn(p_vcdplayer->vcd, i);
+            } else if ( i < p_vcdplayer->i_entries + p_vcdplayer->i_lids )
+            {
+                p_vcdplayer->play_item.num = i = i - p_vcdplayer->i_entries;
+                p_vcdplayer->play_item.type = VCDINFO_ITEM_TYPE_LID;
+                lsn = 0;
+            } else
+            {
+                p_vcdplayer->play_item.num  = i
+                      = i - p_vcdplayer->i_entries - p_vcdplayer->i_lids;
+                p_vcdplayer->play_item.type = VCDINFO_ITEM_TYPE_SEGMENT;
+                lsn = vcdinfo_get_seg_lsn(p_vcdplayer->vcd, i);
+            }
+
+            VCDSetOrigin(p_access,vcdinfo_get_entry_lsn(p_vcdplayer->vcd,i),
+                         i_track,&(p_vcdplayer->play_item));
+        }
+        return VLC_SUCCESS;
+    }
+
+    case ACCESS_SET_PRIVATE_ID_STATE:
+        dbg_print( INPUT_DBG_EVENT, "set private id" );
+        return VLC_EGENERIC;
+
+    default:
+        msg_Warn( p_access, "unimplemented query in control" );
+        return VLC_EGENERIC;
+
+    }
+    return VLC_SUCCESS;
+}