]> git.sesse.net Git - vlc/blobdiff - modules/access/cdda/access.c
Bordel de saloperie de merde
[vlc] / modules / access / cdda / access.c
index 3c3b1e6fbd1fa69cf8a1c777bd73284bc9cc249a..daca3a2950121e78df763e21574f0702cf03267e 100644 (file)
@@ -1,7 +1,7 @@
 /*****************************************************************************
- * cddax.c : CD digital audio input module for vlc using libcdio
+ * access.c : CD digital audio input module for vlc using libcdio
  *****************************************************************************
- * Copyright (C) 2000, 2003, 2004 VideoLAN
+ * Copyright (C) 2000, 2003, 2004, 2005 the VideoLAN team
  * $Id$
  *
  * Authors: Rocky Bernstein <rocky@panix.com>
@@ -61,6 +61,7 @@ access_t *p_cdda_input = NULL;
 /*****************************************************************************
  * Local prototypes
  *****************************************************************************/
+static int      CDDARead( access_t *, uint8_t *, int );
 static block_t *CDDAReadBlocks( access_t * p_access );
 static int      CDDASeek( access_t * p_access, int64_t i_pos );
 static int      CDDAControl( access_t *p_access, int i_query,
@@ -73,7 +74,9 @@ static int      CDDAInit( access_t *p_access, cdda_data_t *p_cdda ) ;
  * Private functions
  ****************************************************************************/
 
-/* process messages that originate from libcdio. */
+/* process messages that originate from libcdio. 
+   called by CDDAOpen
+*/
 static void
 cdio_log_handler (cdio_log_level_t level, const char message[])
 {
@@ -106,7 +109,7 @@ cdio_log_handler (cdio_log_level_t level, const char message[])
 
 #ifdef HAVE_LIBCDDB
 /*! This routine is called by libcddb routines on error.
-   Setup is done by init_input_plugin.
+   called by CDDAOpen
 */
 static void
 cddb_log_handler (cddb_log_level_t level, const char message[])
@@ -126,8 +129,8 @@ cddb_log_handler (cddb_log_level_t level, const char message[])
 #endif /*HAVE_LIBCDDB*/
 
 
-/*! This routine is when xine is not fully set up (before full initialization)
-   or is not around (before finalization).
+/*! This routine is when vlc is not fully set up (before full initialization)
+  or is not around (before finalization).
 */
 static void
 uninit_log_handler (cdio_log_level_t level, const char message[])
@@ -163,6 +166,50 @@ uninit_log_handler (cdio_log_level_t level, const char message[])
     /* gl_default_cdio_log_handler (level, message); */
 }
 
+/* Only used in audio control mode. Gets the current LSN from the 
+   CD-ROM drive. */
+static int64_t
+get_audio_position ( access_t *p_access ) 
+{
+    cdda_data_t *p_cdda   = (cdda_data_t *) p_access->p_sys;
+    lsn_t i_offset;
+
+#if LIBCDIO_VERSION_NUM >= 73
+    if (p_cdda->b_audio_ctl) 
+      {
+       cdio_subchannel_t sub;
+       CdIo_t *p_cdio = p_cdda->p_cdio;
+       if (DRIVER_OP_SUCCESS == cdio_audio_read_subchannel(p_cdio, &sub)) {
+         if (sub.audio_status != CDIO_MMC_READ_SUB_ST_PAUSED &&
+             sub.audio_status != CDIO_MMC_READ_SUB_ST_PLAY) 
+           return CDIO_INVALID_LSN;
+         
+         if ( ! p_cdda->b_nav_mode ) {
+           // char *psz = cdio_msf_to_str(&sub.abs_addr);
+           // fprintf(stderr, "+++disk mode abs msf %s", psz);
+           // free(psz);
+           i_offset = cdio_msf_to_lba((&sub.abs_addr));
+           // fprintf(stderr, " frame offset %d\n", i_offset);
+         } else {
+           // char *psz = cdio_msf_to_str(&sub.rel_addr);
+           // fprintf(stderr, "+++track abs msf %s", psz);
+           // free(psz);
+           i_offset = cdio_msf_to_lba((&sub.rel_addr));
+           // fprintf(stderr, " frame offset %d\n", i_offset);
+         }
+       } else {
+         // fprintf(stderr, "+++Error reading current pos\n");
+         i_offset = p_cdda->i_lsn;
+       }
+      } else
+       i_offset = p_cdda->i_lsn;
+#else 
+    i_offset = p_cdda->i_lsn;
+  ;
+#endif
+  return i_offset;
+}
+
 /*****************************************************************************
  * CDDAReadBlocks: reads a group of blocks from the CD-DA and returns
  * an allocated pointer to the data. NULL is returned if no data
@@ -175,8 +222,9 @@ static block_t * CDDAReadBlocks( access_t * p_access )
     cdda_data_t *p_cdda   = (cdda_data_t *) p_access->p_sys;
     int          i_blocks = p_cdda->i_blocks_per_read;
 
-    dbg_print((INPUT_DBG_CALL|INPUT_DBG_EXT|INPUT_DBG_LSN), "called %d",
-              p_cdda->i_lsn);
+    dbg_print((INPUT_DBG_CALL|INPUT_DBG_EXT|INPUT_DBG_LSN), 
+             "called i_lsn: %d i_pos: %lld, size: %lld",
+              p_cdda->i_lsn, p_access->info.i_pos, p_access->info.i_size);
 
     /* Check end of file */
     if( p_access->info.b_eof ) return NULL;
@@ -191,26 +239,45 @@ static block_t * CDDAReadBlocks( access_t * p_access )
     }
 
     /* Check end of track */
-    while( p_cdda->i_lsn >= p_cdda->lsn[p_cdda->i_track+1] )
+    while( p_cdda->i_lsn > cdio_get_track_last_lsn(p_cdda->p_cdio, 
+                                                  p_cdda->i_track) )
     {
-        if( p_cdda->i_track >= p_cdda->i_first_track + p_cdda->i_titles - 1 )
+        bool go_on;
+      
+       if( p_cdda->b_nav_mode )
+         go_on = p_cdda->i_lsn > p_cdda->last_disc_frame;
+       else
+         go_on = p_cdda->i_track >= p_cdda->i_first_track+p_cdda->i_titles-1 ;
+
+        if( go_on )
         {
+           dbg_print( (INPUT_DBG_LSN), "EOF");
             p_access->info.b_eof = VLC_TRUE;
             return NULL;
         }
 
-        p_access->info.i_update |= INPUT_UPDATE_TITLE | INPUT_UPDATE_SIZE;
+        p_access->info.i_update |= INPUT_UPDATE_TITLE | INPUT_UPDATE_META;
         p_access->info.i_title++;
-        p_access->info.i_size =
-                  p_cdda->p_title[p_access->info.i_title]->i_size;
-        p_access->info.i_pos = 0;
         p_cdda->i_track++;
+
+       if ( p_cdda-> b_nav_mode ) {
+         char *psz_title = CDDAFormatTitle( p_access, p_cdda->i_track );
+         input_Control( p_cdda->p_input, INPUT_SET_NAME, psz_title );
+         free(psz_title);
+       } else {
+         p_access->info.i_size =
+           p_cdda->p_title[p_access->info.i_title]->i_size;
+         p_access->info.i_pos = 0;
+         p_access->info.i_update |= INPUT_UPDATE_SIZE;
+       }
     }
 
     /* Possibly adjust i_blocks so we don't read past the end of a track. */
-    if( p_cdda->i_lsn + i_blocks >= p_cdda->lsn[p_cdda->i_track+1] )
+    if( p_cdda->i_lsn + i_blocks >= 
+       cdio_get_track_lsn(p_cdda->p_cdio, p_cdda->i_track+1) )
     {
-        i_blocks = p_cdda->lsn[p_cdda->i_track+1 ] - p_cdda->i_lsn;
+      i_blocks = cdio_get_track_lsn( p_cdda->p_cdio, p_cdda->i_track+1 ) 
+       - p_cdda->i_lsn;
     }
 
     /* Do the actual reading */
@@ -221,47 +288,259 @@ static block_t * CDDAReadBlocks( access_t * p_access )
                i_blocks * CDIO_CD_FRAMESIZE_RAW );
       return NULL;
     }
-
-    if( cdio_read_audio_sectors( p_cdda->p_cdio, p_block->p_buffer,
-                                 p_cdda->i_lsn, i_blocks) != 0 )
+    
     {
-        msg_Err( p_access, "could not read sector %lu",
-                 (long unsigned int) p_cdda->i_lsn );
-        block_Release( p_block );
-
-        /* If we had problems above, assume the problem is with
-           the first sector of the read and set to skip it.  In
-           the future libcdio may have cdparanoia support.
-        */
-        p_cdda->i_lsn++;
-        p_access->info.i_pos += CDIO_CD_FRAMESIZE_RAW;
-        return NULL;
+#if LIBCDIO_VERSION_NUM >= 72
+      driver_return_code_t rc = DRIVER_OP_SUCCESS;
+
+      if ( p_cdda->e_paranoia && p_cdda->paranoia ) 
+       {
+         int i;
+         for( i = 0; i < i_blocks; i++ )
+           {
+             int16_t *p_readbuf = cdio_paranoia_read(p_cdda->paranoia, NULL);
+             char *psz_err=cdio_cddap_errors(p_cdda->paranoia_cd);
+             char *psz_mes=cdio_cddap_messages(p_cdda->paranoia_cd);
+
+             if (psz_mes || psz_err)
+               msg_Err( p_access, "%s%s\n", psz_mes ? psz_mes: "", 
+                       psz_err ? psz_err: "" );
+             
+             if (psz_err) free(psz_err);
+             if (psz_mes) free(psz_mes);
+             if( !p_readbuf ) {
+               msg_Err( p_access, "paranoia read error on frame %i\n", 
+                        p_cdda->i_lsn+i );
+             } else 
+               memcpy(p_block->p_buffer + i * CDIO_CD_FRAMESIZE_RAW, 
+                      p_readbuf, CDIO_CD_FRAMESIZE_RAW);
+           }
+       }
+      else 
+       rc = cdio_read_audio_sectors( p_cdda->p_cdio, p_block->p_buffer,
+                                     p_cdda->i_lsn, i_blocks);
+#else
+#define DRIVER_OP_SUCCESS 0
+      int rc;
+      rc = cdio_read_audio_sectors( p_cdda->p_cdio, p_block->p_buffer,
+                                   p_cdda->i_lsn, i_blocks);
+#endif    
+      if( rc != DRIVER_OP_SUCCESS )
+       {
+         msg_Err( p_access, "could not read %d sectors starting from %lu",
+                  i_blocks, (long unsigned int) p_cdda->i_lsn );
+         block_Release( p_block );
+         
+         /* If we had problems above, assume the problem is with
+            the first sector of the read and set to skip it.  In
+            the future libcdio may have cdparanoia support.
+         */
+         p_cdda->i_lsn++;
+         p_access->info.i_pos += CDIO_CD_FRAMESIZE_RAW;
+         return NULL;
+       }
     }
 
     p_cdda->i_lsn        += i_blocks;
-    p_access->info.i_pos += p_block->i_buffer;
+    p_access->info.i_pos += i_blocks * CDIO_CD_FRAMESIZE_RAW;
 
     return p_block;
 }
 
+/*****************************************************************************
+ * CDDARead: Handler for audio control reads the CD-DA.
+ *****************************************************************************/
+static int
+CDDARead( access_t * p_access, uint8_t *p_buffer, int i_len )
+{
+    cdda_data_t *p_cdda   = (cdda_data_t *) p_access->p_sys;
+
+    dbg_print((INPUT_DBG_CALL|INPUT_DBG_EXT|INPUT_DBG_LSN), 
+             "called lsn: %d pos: %lld, size: %lld",
+              p_cdda->i_lsn, p_access->info.i_pos, p_access->info.i_size);
+
+    /* Check end of file */
+    if( p_access->info.b_eof ) return 0;
+    
+    {
+      lsn_t i_lsn = get_audio_position(p_access);
+      if (CDIO_INVALID_LSN == i_lsn) {
+       dbg_print((INPUT_DBG_LSN), "invalid lsn");
+       memset( p_buffer, 0, i_len );
+       return i_len;
+      }
+    
+      p_cdda->i_lsn = i_lsn;
+      p_access->info.i_pos = p_cdda->i_lsn * CDIO_CD_FRAMESIZE_RAW;
+    }
+    
+    dbg_print((INPUT_DBG_LSN), "updated lsn: %d", p_cdda->i_lsn);
+
+    /* Check end of track */
+    while( p_cdda->i_lsn > cdio_get_track_last_lsn(p_cdda->p_cdio, 
+                                                  p_cdda->i_track) )
+    {
+        if( p_cdda->i_track >= p_cdda->i_first_track + p_cdda->i_titles - 1 )
+        {
+           dbg_print( (INPUT_DBG_LSN), "EOF");
+            p_access->info.b_eof = VLC_TRUE;
+            return 0;
+        }
+
+        p_access->info.i_update |= INPUT_UPDATE_TITLE;
+        p_access->info.i_title++;
+        p_cdda->i_track++;
+
+       if ( p_cdda-> b_nav_mode ) {
+         char *psz_title = CDDAFormatTitle( p_access, p_cdda->i_track );
+         input_Control( p_cdda->p_input, INPUT_SET_NAME, psz_title );
+         free(psz_title);
+       } else {
+         p_access->info.i_size =
+           p_cdda->p_title[p_access->info.i_title]->i_size;
+         p_access->info.i_pos = 0;
+         p_access->info.i_update |= INPUT_UPDATE_SIZE;
+       }
+    }
+
+    memset( p_buffer, 0, i_len );
+    return i_len;
+}
+
+/*! Pause CD playing via audio control */
+static bool
+cdda_audio_pause(CdIo_t *p_cdio)
+{
+  bool b_ok = true;
+#if LIBCDIO_VERSION_NUM >= 73
+  cdio_subchannel_t sub;
+  if (DRIVER_OP_SUCCESS == cdio_audio_read_subchannel(p_cdio, &sub)) {
+    if (sub.audio_status == CDIO_MMC_READ_SUB_ST_PLAY) {
+      b_ok = DRIVER_OP_SUCCESS == cdio_audio_pause(p_cdio);
+    }
+  } else
+    b_ok = false;
+#endif
+  return b_ok;
+}
+
+#if LIBCDIO_VERSION_NUM >= 73
+/*! play CD using audio controls */
+static driver_return_code_t
+cdda_audio_play(CdIo_t *p_cdio, lsn_t start_lsn, lsn_t end_lsn)
+{
+  msf_t start_msf;
+  msf_t last_msf;
+  cdio_lsn_to_msf(start_lsn, &start_msf);
+  cdio_lsn_to_msf(end_lsn, &last_msf);
+  cdda_audio_pause(p_cdio);
+  return cdio_audio_play_msf (p_cdio, &start_msf, &last_msf);
+}
+#endif
+
 /****************************************************************************
  * CDDASeek - change position for subsequent reads. For example, this
  * can happen if the user moves a position slider bar in a GUI.
  ****************************************************************************/
-static int CDDASeek( access_t * p_access, int64_t i_pos )
+static int 
+CDDASeek( access_t * p_access, int64_t i_pos )
 {
     cdda_data_t *p_cdda = (cdda_data_t *) p_access->p_sys;
 
-    p_cdda->i_lsn = p_cdda->lsn[p_cdda->i_track]
-                  + (i_pos / CDIO_CD_FRAMESIZE_RAW);
-    p_access->info.i_pos = i_pos;
-
     dbg_print( (INPUT_DBG_CALL|INPUT_DBG_EXT|INPUT_DBG_SEEK),
                "lsn %lu, offset: %lld",
                (long unsigned int) p_cdda->i_lsn, i_pos );
+
+    p_cdda->i_lsn = (i_pos / CDIO_CD_FRAMESIZE_RAW);
+
+#if LIBCDIO_VERSION_NUM >= 72
+    if ( p_cdda->e_paranoia && p_cdda->paranoia ) 
+      cdio_paranoia_seek(p_cdda->paranoia, p_cdda->i_lsn, SEEK_SET);
+#endif
+
+#if LIBCDIO_VERSION_NUM >= 73
+    if ( p_cdda->b_audio_ctl ) {
+      track_t i_track = cdio_get_track(p_cdda->p_cdio, p_cdda->i_lsn);
+      lsn_t i_last_lsn;
+
+      if ( p_cdda->b_nav_mode )
+       i_last_lsn = p_cdda->last_disc_frame;
+      else 
+       i_last_lsn = cdio_get_track_last_lsn(p_cdda->p_cdio, i_track);
+
+      cdda_audio_play(p_cdda->p_cdio, p_cdda->i_lsn, i_last_lsn);
+    }
+#endif 
+
+    if ( ! p_cdda->b_nav_mode ) 
+      p_cdda->i_lsn += cdio_get_track_lsn(p_cdda->p_cdio, p_cdda->i_track);
+
+    /* Seeked backwards and we are doing disc mode. */
+    if ( p_cdda->b_nav_mode && p_access->info.i_pos > i_pos ) {
+      track_t i_track;
+      char *psz_title;
+      
+      for( i_track = p_cdda->i_track; 
+          i_track > 1 && 
+            p_cdda->i_lsn < cdio_get_track_lsn(p_cdda->p_cdio, i_track);
+          i_track--, p_access->info.i_title-- ) ;
+
+      p_cdda->i_track = i_track;
+      p_access->info.i_update |= INPUT_UPDATE_TITLE | INPUT_UPDATE_META ;
+      psz_title  = CDDAFormatTitle( p_access, p_cdda->i_track );
+      input_Control( p_cdda->p_input, INPUT_SET_NAME, 
+                    psz_title );
+      free(psz_title);
+
+    }
+    
+    p_access->info.i_pos = i_pos;
+
     return VLC_SUCCESS;
 }
 
+/*
+  Set up internal state so that we play a given track.
+  If we are using audio-ctl mode we also activate CD-ROM
+  to play.
+ */
+static bool
+cdda_play_track( access_t *p_access, track_t i_track ) 
+{
+    cdda_data_t *p_cdda = (cdda_data_t *) p_access->p_sys;
+
+    dbg_print((INPUT_DBG_CALL), "called track: %d\n", i_track);
+
+    if (i_track > p_cdda->i_tracks) 
+      {
+       msg_Err( p_access, "CD has %d tracks, and you requested track %d", 
+                p_cdda->i_tracks, i_track );
+       return false;
+      }
+
+    p_cdda->i_track = i_track;
+
+    /* set up the frame boundaries for this particular track */
+    p_cdda->first_frame = p_cdda->i_lsn = 
+    cdio_get_track_lsn(p_cdda->p_cdio, i_track);
+
+    p_cdda->last_frame  = cdio_get_track_lsn(p_cdda->p_cdio, i_track+1) - 1;
+
+#if LIBCDIO_VERSION_NUM >= 73
+    if (p_cdda->b_audio_ctl) 
+      {
+       lsn_t i_last_lsn;
+       if ( p_cdda->b_nav_mode )
+         i_last_lsn = p_cdda->last_disc_frame;
+       else 
+         i_last_lsn = cdio_get_track_last_lsn(p_cdda->p_cdio, i_track);
+       cdda_audio_play(p_cdda->p_cdio, p_cdda->i_lsn, i_last_lsn);
+      }
+#endif
+  
+  return true;
+}
+
 /****************************************************************************
  * Public functions
  ****************************************************************************/
@@ -270,12 +549,13 @@ static int CDDASeek( access_t * p_access, int64_t i_pos )
  * Open: open cdda device or image file and initialize structures
  *       for subsequent operations.
  *****************************************************************************/
-int E_(CDDAOpen)( vlc_object_t *p_this )
+int 
+CDDAOpen( vlc_object_t *p_this )
 {
     access_t    *p_access = (access_t*)p_this;
     char *      psz_source = NULL;
     cdda_data_t *p_cdda    = NULL;
-    CdIo        *p_cdio;
+    CdIo_t      *p_cdio;
     track_t     i_track = 1;
     vlc_bool_t  b_single_track = false;
     int         i_rc = VLC_EGENERIC;
@@ -322,19 +602,19 @@ int E_(CDDAOpen)( vlc_object_t *p_this )
         if( !psz_source || !*psz_source )
         {
             /* Scan for a CD-ROM drive with a CD-DA in it. */
-            char **cd_drives =
+            char **ppsz_drives =
               cdio_get_devices_with_cap(NULL,  CDIO_FS_AUDIO, false);
 
-            if (NULL == cd_drives || NULL == cd_drives[0] )
+            if (NULL == ppsz_drives || NULL == ppsz_drives[0] )
             {
                 msg_Err( p_access,
                      "libcdio couldn't find something with a CD-DA in it" );
-                if (cd_drives) cdio_free_device_list(cd_drives);
+                if (ppsz_drives) cdio_free_device_list(ppsz_drives);
                 return VLC_EGENERIC;
             }
 
-            psz_source = strdup(cd_drives[0]);
-            cdio_free_device_list(cd_drives);
+            psz_source = strdup(ppsz_drives[0]);
+            cdio_free_device_list(ppsz_drives);
         }
     }
 
@@ -348,14 +628,13 @@ int E_(CDDAOpen)( vlc_object_t *p_this )
        return VLC_EGENERIC;
     }
 
-    p_cdda = malloc( sizeof(cdda_data_t) );
+    p_cdda = calloc( 1, sizeof(cdda_data_t) );
     if( p_cdda == NULL )
     {
         msg_Err( p_access, "out of memory" );
         free( psz_source );
         return VLC_ENOMEM;
     }
-    memset( p_cdda, 0, sizeof(cdda_data_t) );
 
 #ifdef HAVE_LIBCDDB
     cddb_log_set_handler ( cddb_log_handler );
@@ -364,19 +643,31 @@ int E_(CDDAOpen)( vlc_object_t *p_this )
       config_GetInt( p_access, MODULE_STRING "-cddb-enabled" );
 #endif
 
-    p_cdda->b_cdtext_enabled =
+    p_cdda->b_cdtext =
       config_GetInt( p_access, MODULE_STRING "-cdtext-enabled" );
 
     p_cdda->b_cdtext_prefer =
       config_GetInt( p_access, MODULE_STRING "-cdtext-prefer" );
 
-    p_cdda->b_header = VLC_FALSE;
-    p_cdda->p_cdio   = p_cdio;
-    p_cdda->i_titles = 0;
-    p_cdda->i_track  = i_track;
-    p_cdda->i_debug  = config_GetInt(p_this, MODULE_STRING "-debug");
+#if LIBCDIO_VERSION_NUM >= 73
+    p_cdda->b_audio_ctl =
+      config_GetInt( p_access, MODULE_STRING "-analog-output" );
+#endif
+
+    p_cdda->psz_source = strdup(psz_source);
+    p_cdda->b_header   = VLC_FALSE;
+    p_cdda->p_cdio     = p_cdio;
+    p_cdda->i_tracks   = 0;
+    p_cdda->i_titles   = 0;
+    p_cdda->i_debug    = config_GetInt(p_this, MODULE_STRING 
+                                      "-debug");
+    p_cdda->b_nav_mode = config_GetInt(p_this, MODULE_STRING 
+                                      "-navigation-mode" );
     p_cdda->i_blocks_per_read
-                     = config_GetInt(p_this, MODULE_STRING "-blocks-per-read");
+      = config_GetInt(p_this, MODULE_STRING "-blocks-per-read");
+
+    p_cdda->last_disc_frame = 
+      cdio_get_track_lsn(p_cdio, CDIO_CDROM_LEADOUT_TRACK);
 
     p_cdda->p_input  = vlc_object_find( p_access, VLC_OBJECT_INPUT,
                                         FIND_PARENT );
@@ -399,12 +690,32 @@ int E_(CDDAOpen)( vlc_object_t *p_this )
     dbg_print( (INPUT_DBG_CALL|INPUT_DBG_EXT), "%s", psz_source );
 
     /* Set up p_access */
-    p_access->pf_read    = NULL;
-    p_access->pf_block   = CDDAReadBlocks;
+    if (p_cdda->b_audio_ctl) 
+      {
+       p_access->pf_read  = CDDARead;
+       p_access->pf_block = NULL;
+      } else 
+      {
+       p_access->pf_read  = NULL;
+       p_access->pf_block = CDDAReadBlocks;
+      }
+    
     p_access->pf_control = CDDAControl;
     p_access->pf_seek    = CDDASeek;
 
-    p_access->info.i_size      = 0;
+    {
+      lsn_t i_last_lsn;
+      
+      if (p_cdda->b_nav_mode)
+         i_last_lsn = p_cdda->last_disc_frame;
+      else 
+         i_last_lsn = cdio_get_track_last_lsn(p_cdio, i_track);
+      
+      if (CDIO_INVALID_LSN != i_last_lsn)
+       p_access->info.i_size = i_last_lsn * (uint64_t) CDIO_CD_FRAMESIZE_RAW;
+      else 
+       p_access->info.i_size = 0;
+    }
 
     p_access->info.i_update    = 0;
     p_access->info.b_eof       = VLC_FALSE;
@@ -417,7 +728,50 @@ int E_(CDDAOpen)( vlc_object_t *p_this )
     i_rc = CDDAInit( p_access, p_cdda );
     if ( VLC_SUCCESS != i_rc ) goto error;
 
-    CDDAFixupPlaylist( p_access, p_cdda, psz_source, b_single_track );
+    cdda_play_track( p_access, i_track );
+
+    CDDAFixupPlaylist( p_access, p_cdda, b_single_track );
+
+#if LIBCDIO_VERSION_NUM >= 72
+    {
+   
+      char *psz_paranoia = config_GetPsz( p_access, 
+                                         MODULE_STRING "-paranoia" );
+      p_cdda->e_paranoia = paranoia_none;
+      if( psz_paranoia && *psz_paranoia )
+      {
+
+       if( !strncmp( psz_paranoia, "full", strlen("full") )  )
+         p_cdda->e_paranoia = paranoia_full;
+       else if( !strncmp( psz_paranoia, "overlap", strlen("overlap") )  )
+         p_cdda->e_paranoia = paranoia_overlap;
+       
+       /* Use CD Paranoia? */
+       if ( p_cdda->e_paranoia ) {
+         p_cdda->paranoia_cd = cdio_cddap_identify_cdio(p_cdio, 1, NULL);
+         /* We'll set for verbose paranoia messages. */
+         cdio_cddap_verbose_set(p_cdda->paranoia_cd, CDDA_MESSAGE_PRINTIT, 
+                                CDDA_MESSAGE_PRINTIT);
+         if ( 0 != cdio_cddap_open(p_cdda->paranoia_cd) ) {
+           msg_Warn( p_cdda_input, "Unable to get paranoia support - "
+                     "continuing without it." );
+           p_cdda->e_paranoia = paranoia_none;
+         } else {
+           p_cdda->paranoia = cdio_paranoia_init(p_cdda->paranoia_cd);
+           cdio_paranoia_seek(p_cdda->paranoia, p_cdda->i_lsn, SEEK_SET);
+           
+           /* Set reading mode for full or overlap paranoia, 
+              but allow skipping sectors. */
+           cdio_paranoia_modeset(p_cdda->paranoia,
+                                 paranoia_full == p_cdda->e_paranoia ?
+                                 PARANOIA_MODE_FULL^PARANOIA_MODE_NEVERSKIP :
+                                 PARANOIA_MODE_OVERLAP^PARANOIA_MODE_NEVERSKIP
+                                 );
+         }
+       }
+      }
+    }
+#endif    
 
     /* Build a WAV header to put in front of the output data.
        This gets sent back in the Block (read) routine.
@@ -461,12 +815,18 @@ int E_(CDDAOpen)( vlc_object_t *p_this )
 /*****************************************************************************
  * CDDAClose: closes cdda and frees any resources associded with it.
  *****************************************************************************/
-void E_(CDDAClose)( vlc_object_t *p_this )
+void 
+CDDAClose (vlc_object_t *p_this )
 {
     access_t    *p_access = (access_t *) p_this;
     cdda_data_t *p_cdda   = (cdda_data_t *) p_access->p_sys;
     track_t      i;
 
+#if LIBCDIO_VERSION_NUM >= 73
+    if (p_cdda->b_audio_ctl)
+      cdio_audio_stop(p_cdda->p_cdio);
+#endif
+
     dbg_print( (INPUT_DBG_CALL|INPUT_DBG_EXT), "" );
 
     /* Remove playlist titles */
@@ -475,18 +835,30 @@ void E_(CDDAClose)( vlc_object_t *p_this )
         vlc_input_title_Delete( p_cdda->p_title[i] );
     }
 
-    cdio_destroy( p_cdda->p_cdio );
-
-    cdio_log_set_handler (uninit_log_handler);
-
 #ifdef HAVE_LIBCDDB
     cddb_log_set_handler ((cddb_log_handler_t) uninit_log_handler);
     if (p_cdda->b_cddb_enabled)
       cddb_disc_destroy(p_cdda->cddb.disc);
 #endif
 
-    if (p_cdda->psz_mcn) free( p_cdda->psz_mcn );
+    cdio_destroy( p_cdda->p_cdio );
+    cdio_log_set_handler (uninit_log_handler);
+
+#if LIBCDIO_VERSION_NUM >= 72
+    if (p_cdda->paranoia)
+      cdio_paranoia_free(p_cdda->paranoia);
+    if (p_cdda->paranoia_cd) 
+      cdio_cddap_close_no_free_cdio(p_cdda->paranoia_cd);
+#endif
+
+    if (p_cdda->psz_mcn)    free( p_cdda->psz_mcn );
+    if (p_cdda->psz_source) free( p_cdda->psz_source );
+
+#if LIBCDDB_VERSION_NUM >= 1
+    libcddb_shutdown();
+#endif
     free( p_cdda );
+    p_cdda = NULL;
     p_cdda_input = NULL;
 }
 
@@ -524,14 +896,27 @@ static int CDDAControl( access_t *p_access, int i_query, va_list args )
            }
         }
 
-        case ACCESS_CAN_SEEK:
+        case ACCESS_CAN_CONTROL_PACE:
+         {
+            vlc_bool_t *pb_bool = (vlc_bool_t*)va_arg( args, vlc_bool_t* );
+            *pb_bool = p_cdda->b_audio_ctl ? VLC_FALSE : VLC_TRUE;
+           dbg_print( INPUT_DBG_META, "can control pace? %d", *pb_bool);
+            return VLC_SUCCESS;
+         }
+           
         case ACCESS_CAN_FASTSEEK:
+           dbg_print( INPUT_DBG_META, "can fast seek?");
+           goto common;
+        case ACCESS_CAN_SEEK:
+           dbg_print( INPUT_DBG_META, "can seek?");
+           goto common;
         case ACCESS_CAN_PAUSE:
-        case ACCESS_CAN_CONTROL_PACE:
+           dbg_print( INPUT_DBG_META, "can pause?");
+    common:
         {
             vlc_bool_t *pb_bool = (vlc_bool_t*)va_arg( args, vlc_bool_t* );
             *pb_bool = VLC_TRUE;
-            return VLC_SUCCESS;;
+            return VLC_SUCCESS;
         }
 
         /* */
@@ -539,6 +924,7 @@ static int CDDAControl( access_t *p_access, int i_query, va_list args )
         {
             pi_int = (int*)va_arg( args, int * );
             *pi_int = p_cdda-> i_blocks_per_read * CDIO_CD_FRAMESIZE_RAW;
+           dbg_print( INPUT_DBG_META, "Get MTU %d", *pi_int);
             break;
         }
 
@@ -550,36 +936,46 @@ static int CDDAControl( access_t *p_access, int i_query, va_list args )
             break;
         }
 
-        /* */
-        case ACCESS_SET_PAUSE_STATE:
-            break;
-
         case ACCESS_GET_TITLE_INFO:
         {
-            input_title_t ***ppp_title;
-            ppp_title = (input_title_t***)va_arg( args, input_title_t*** );
+           input_title_t ***ppp_title = 
+             (input_title_t***)va_arg( args, input_title_t*** );
+
             pi_int    = (int*)va_arg( args, int* );
             *((int*)va_arg( args, int* )) = 1; /* Title offset */
 
-            /* Duplicate title info */
             dbg_print ( INPUT_DBG_EVENT,
-                        "GET TITLE: i_tracks %d, i_titles %d",
-                        p_cdda->i_tracks, p_cdda->i_titles );
+                        "GET TITLE: i_tracks %d, i_tracks %d",
+                        p_cdda->i_tracks, p_cdda->i_tracks );
+
+           CDDAMetaInfo( p_access, CDIO_INVALID_TRACK );
+
+           if ( p_cdda->b_nav_mode) {
+               char *psz_title = 
+                 CDDAFormatTitle( p_access, p_cdda->i_track );
+               input_Control( p_cdda->p_input, INPUT_SET_NAME, 
+                              psz_title );
+               free(psz_title);
+           }
+
+            /* Duplicate title info */
             if( p_cdda->i_titles == 0 )
             {
                 *pi_int = 0; ppp_title = NULL;
                 return VLC_SUCCESS;
             }
             *pi_int = p_cdda->i_titles;
-            *ppp_title = malloc(sizeof( input_title_t **) * p_cdda->i_titles );
+            *ppp_title = calloc(1, 
+                               sizeof( input_title_t **) * p_cdda->i_titles );
 
             if (!*ppp_title) return VLC_ENOMEM;
 
             for( i = 0; i < p_cdda->i_titles; i++ )
             {
-                if ( p_cdda->p_title[i] )
+             if ( p_cdda->p_title[i] ) {
                    (*ppp_title)[i] =
-                          vlc_input_title_Duplicate( p_cdda->p_title[i] );
+                    vlc_input_title_Duplicate( p_cdda->p_title[i] );
+             }
             }
             break;
         }
@@ -587,23 +983,54 @@ static int CDDAControl( access_t *p_access, int i_query, va_list args )
         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 )
             {
+               const track_t i_track = p_cdda->i_first_track + i;
                 /* Update info */
-                p_access->info.i_update |=
-                    INPUT_UPDATE_TITLE|INPUT_UPDATE_SIZE;
                 p_access->info.i_title = i;
-                p_access->info.i_size = p_cdda->p_title[i]->i_size;
-                p_access->info.i_pos = 0;
+               if ( p_cdda->b_nav_mode) 
+                {
+                   lsn_t i_last_lsn;
+                   char *psz_title = CDDAFormatTitle( p_access, i_track );
+                   input_Control( p_cdda->p_input, INPUT_SET_NAME, 
+                                  psz_title );
+                   free(psz_title);
+                   p_cdda->i_track = i_track;
+                   i_last_lsn = cdio_get_track_lsn(p_cdda->p_cdio, 
+                                                   CDIO_CDROM_LEADOUT_TRACK);
+                   if (CDIO_INVALID_LSN != i_last_lsn)
+                     p_access->info.i_size = (int64_t) CDIO_CD_FRAMESIZE_RAW 
+                       * i_last_lsn ;
+                   p_access->info.i_pos = (int64_t)
+                     cdio_get_track_lsn( p_cdda->p_cdio, i_track ) 
+                     * CDIO_CD_FRAMESIZE_RAW;
+               } else {
+                  p_access->info.i_size = p_cdda->p_title[i]->i_size;
+                  p_access->info.i_pos  = 0;
+               }
+                p_access->info.i_update = 
+                 INPUT_UPDATE_TITLE | INPUT_UPDATE_SIZE;
 
                 /* Next sector to read */
-                p_cdda->i_lsn = p_cdda->lsn[p_cdda->i_first_track+i];
+                p_cdda->i_lsn = cdio_get_track_lsn(p_cdda->p_cdio, i_track);
             }
             break;
         }
 
+        case ACCESS_SET_PAUSE_STATE:
+         dbg_print( INPUT_DBG_META, "Pause");
+         if (p_cdda->b_audio_ctl)
+           cdda_audio_pause(p_cdda->p_cdio);
+         break;
+
         case ACCESS_SET_SEEKPOINT:
+           dbg_print( INPUT_DBG_META, "set seekpoint");
+            return VLC_EGENERIC;
+
         case ACCESS_SET_PRIVATE_ID_STATE:
+           dbg_print( INPUT_DBG_META, "set private id state");
             return VLC_EGENERIC;
 
         default:
@@ -629,7 +1056,6 @@ static int CDDAControl( access_t *p_access, int i_query, va_list args )
  *****************************************************************************/
 static int CDDAInit( access_t *p_access, cdda_data_t *p_cdda )
 {
-    track_t i;
     discmode_t  discmode = CDIO_DISC_MODE_NO_INFO;
 
     p_cdda->i_tracks       = cdio_get_num_tracks(p_cdda->p_cdio);
@@ -650,18 +1076,15 @@ static int CDDAInit( access_t *p_access, cdda_data_t *p_cdda )
         return VLC_EGENERIC;
     }
 
-    /* Fill the lsn array with the track/sector matches.
-       Note cdio_get_track_lsn when given num_tracks + 1 will return
-       the leadout LSN.
-     */
-    for( i = 0 ; i <= p_cdda->i_tracks ; i++ )
-    {
-        track_t i_track = p_cdda->i_first_track + i;
-        (p_cdda->lsn)[ i_track ] = cdio_get_track_lsn(p_cdda->p_cdio, i_track);
-    }
-
     /* Set reading start LSN. */
-    p_cdda->i_lsn = p_cdda->lsn[p_cdda->i_track];
+    p_cdda->i_lsn = cdio_get_track_lsn(p_cdda->p_cdio, p_cdda->i_track);
 
     return VLC_SUCCESS;
 }
+
+/* 
+ * Local variables:
+ *  mode: C
+ *  style: gnu
+ * End:
+ */