]> git.sesse.net Git - vlc/blobdiff - modules/access/cdda/access.c
FSF address change.
[vlc] / modules / access / cdda / access.c
index b7c986f0b430008c84892541fbd335000dc97180..cba080a543ec20c32e31b7e23e27f60176cb2ed4 100644 (file)
@@ -1,7 +1,7 @@
 /*****************************************************************************
  * access.c : CD digital audio input module for vlc using libcdio
  *****************************************************************************
- * Copyright (C) 2000, 2003, 2004, 2005 VideoLAN
+ * Copyright (C) 2000, 2003, 2004, 2005 the VideoLAN team
  * $Id$
  *
  * Authors: Rocky Bernstein <rocky@panix.com>
@@ -20,7 +20,7 @@
  *
  * 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.
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
  *****************************************************************************/
 
 /*****************************************************************************
@@ -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
@@ -176,7 +223,7 @@ static block_t * CDDAReadBlocks( access_t * p_access )
     int          i_blocks = p_cdda->i_blocks_per_read;
 
     dbg_print((INPUT_DBG_CALL|INPUT_DBG_EXT|INPUT_DBG_LSN), 
-             "called %d pos: %lld, size: %lld",
+             "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 */
@@ -195,13 +242,21 @@ static block_t * CDDAReadBlocks( access_t * p_access )
     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;
+        p_access->info.i_update |= INPUT_UPDATE_TITLE | INPUT_UPDATE_META;
         p_access->info.i_title++;
         p_cdda->i_track++;
 
@@ -292,14 +347,110 @@ static block_t * CDDAReadBlocks( access_t * p_access )
     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;
 
+    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
@@ -307,6 +458,20 @@ static int CDDASeek( access_t * p_access, int64_t i_pos )
       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);
 
@@ -321,7 +486,7 @@ static int CDDASeek( access_t * p_access, int64_t i_pos )
           i_track--, p_access->info.i_title-- ) ;
 
       p_cdda->i_track = i_track;
-      p_access->info.i_update |= INPUT_UPDATE_TITLE;
+      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 );
@@ -331,12 +496,51 @@ static int CDDASeek( access_t * p_access, int64_t i_pos )
     
     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 );
     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
  ****************************************************************************/
@@ -398,19 +602,19 @@ 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);
         }
     }
 
@@ -445,12 +649,16 @@ CDDAOpen( vlc_object_t *p_this )
     p_cdda->b_cdtext_prefer =
       config_GetInt( p_access, MODULE_STRING "-cdtext-prefer" );
 
+#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_track    = i_track;
     p_cdda->i_debug    = config_GetInt(p_this, MODULE_STRING 
                                       "-debug");
     p_cdda->b_nav_mode = config_GetInt(p_this, MODULE_STRING 
@@ -458,6 +666,9 @@ CDDAOpen( vlc_object_t *p_this )
     p_cdda->i_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 );
 
@@ -479,8 +690,16 @@ 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;
 
@@ -488,7 +707,7 @@ CDDAOpen( vlc_object_t *p_this )
       lsn_t i_last_lsn;
       
       if (p_cdda->b_nav_mode)
-         i_last_lsn = cdio_get_track_lsn(p_cdio, CDIO_CDROM_LEADOUT_TRACK);
+         i_last_lsn = p_cdda->last_disc_frame;
       else 
          i_last_lsn = cdio_get_track_last_lsn(p_cdio, i_track);
       
@@ -509,6 +728,8 @@ CDDAOpen( vlc_object_t *p_this )
     i_rc = CDDAInit( p_access, p_cdda );
     if ( VLC_SUCCESS != i_rc ) goto error;
 
+    cdda_play_track( p_access, i_track );
+
     CDDAFixupPlaylist( p_access, p_cdda, b_single_track );
 
 #if LIBCDIO_VERSION_NUM >= 72
@@ -601,6 +822,11 @@ CDDAClose (vlc_object_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 */
@@ -671,13 +897,24 @@ static int CDDAControl( access_t *p_access, int i_query, va_list args )
         }
 
         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:
+           dbg_print( INPUT_DBG_META, "can pause?");
+    common:
         {
             vlc_bool_t *pb_bool = (vlc_bool_t*)va_arg( args, vlc_bool_t* );
-           dbg_print( INPUT_DBG_META, 
-                      "can seek/fastseek/pause/control pace");
             *pb_bool = VLC_TRUE;
             return VLC_SUCCESS;
         }
@@ -699,11 +936,6 @@ static int CDDAControl( access_t *p_access, int i_query, va_list args )
             break;
         }
 
-        /* */
-        case ACCESS_SET_PAUSE_STATE:
-           dbg_print( INPUT_DBG_META, "Set pause state");
-            break;
-
         case ACCESS_GET_TITLE_INFO:
         {
            input_title_t ***ppp_title = 
@@ -755,42 +987,50 @@ static int CDDAControl( access_t *p_access, int i_query, va_list args )
            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_title = i;
                if ( p_cdda->b_nav_mode) 
                 {
                    lsn_t i_last_lsn;
-                   char *psz_title = 
-                     CDDAFormatTitle( p_access, i+1 );
+                   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 = p_cdda->i_first_track+i;
+                   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, p_cdda->i_track ) 
+                     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_pos  = 0;
                }
                 p_access->info.i_update = 
                  INPUT_UPDATE_TITLE | INPUT_UPDATE_SIZE;
 
                 /* Next sector to read */
-                p_cdda->i_lsn = 
-                 cdio_get_track_lsn(p_cdda->p_cdio, 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 seekpoint/set private id state");
+           dbg_print( INPUT_DBG_META, "set private id state");
             return VLC_EGENERIC;
 
         default: