X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=modules%2Faccess%2Fcdda%2Faccess.c;h=f4de5bd75d2bc2f2a0bd190671f023982f196530;hb=482d8344a9aadc7c313e63b67432233a7339e35b;hp=13aff10294d49cc2b943a9e462bda66f165ee759;hpb=562ab59b24114e526144b59b5967224de432412a;p=vlc diff --git a/modules/access/cdda/access.c b/modules/access/cdda/access.c index 13aff10294..f4de5bd75d 100644 --- a/modules/access/cdda/access.c +++ b/modules/access/cdda/access.c @@ -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 @@ -20,1122 +20,1082 @@ * * 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. *****************************************************************************/ /***************************************************************************** * Preamble *****************************************************************************/ -#include -#include - -#include -#include +#include "callback.h" /* FIXME - reorganize callback.h, cdda.h better */ +#include "cdda.h" /* private structures. Also #includes vlc things */ +#include "info.h" /* headers for meta info retrieval */ +#include "access.h" +#include "vlc_keys.h" +#include -#include #include +#include #include -#include "codecs.h" -#include "vlc_keys.h" -#ifdef HAVE_UNISTD_H -# include -#endif +/* #ifdef variables below are defined via config.h via #include vlc above. */ -#ifdef HAVE_ERRNO_H -# include +#ifdef HAVE_SYS_TYPES_H +#include #endif -#include - -#include "cdda.h" - -/* how many blocks Open will read in each loop */ -#define CDDA_BLOCKS_ONCE 20 -#define CDDA_DATA_ONCE (CDDA_BLOCKS_ONCE * CDIO_CD_FRAMESIZE_RAW) - -#define CDDA_MRL_PREFIX "cddax://" +#ifdef HAVE_UNISTD_H +# include +#endif /* FIXME: This variable is a hack. Would be nice to eliminate. */ -static input_thread_t *p_cdda_input = NULL; +access_t *p_cdda_input = NULL; /***************************************************************************** * Local prototypes *****************************************************************************/ -static int CDDARead ( input_thread_t *, byte_t *, size_t ); -static void CDDASeek ( input_thread_t *, off_t ); -static int CDDASetArea ( input_thread_t *, input_area_t * ); -static int CDDASetProgram ( input_thread_t *, pgrm_descriptor_t * ); - -static int CDDAFixupPlayList( input_thread_t *p_input, - cdda_data_t *p_cdda, const char *psz_source, - bool play_single_track); - -#if NEED_UPDATE_VAR -static void CDDAUpdateVar( input_thread_t *p_input, int i_entry, int i_action, - const char *p_varname, char *p_label, - const char *p_debug_label ); -#endif +static ssize_t CDDARead( access_t *, uint8_t *, size_t ); +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, + va_list args ); + +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[]) +cdio_log_handler( cdio_log_level_t level, const char message[] ) { - cdda_data_t *p_cdda = (cdda_data_t *)p_cdda_input->p_access_data; - switch (level) { - case CDIO_LOG_DEBUG: - case CDIO_LOG_INFO: - if (p_cdda->i_debug & INPUT_DBG_CDIO) - msg_Dbg( p_cdda_input, message); - break; - case CDIO_LOG_WARN: - msg_Warn( p_cdda_input, message); - break; - case CDIO_LOG_ERROR: - case CDIO_LOG_ASSERT: - msg_Err( p_cdda_input, message); - break; - default: - msg_Warn( p_cdda_input, message, - _("The above message had unknown vcdimager log level"), - level); - } - return; -} + cdda_data_t *p_cdda = (cdda_data_t *)p_cdda_input->p_sys; + if( p_cdda == NULL ) + return; + + switch( level ) + { + case CDIO_LOG_DEBUG: + case CDIO_LOG_INFO: + if (p_cdda->i_debug & INPUT_DBG_CDIO) + msg_Dbg( p_cdda_input, "%s", message); + break; + case CDIO_LOG_WARN: + msg_Warn( p_cdda_input, "%s", message); + break; + case CDIO_LOG_ERROR: + case CDIO_LOG_ASSERT: + msg_Err( p_cdda_input, "%s", message); + break; + default: + msg_Warn( p_cdda_input, "%s\n%s %d", message, + "the above message had unknown cdio log level", + level); + break; + } +} #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[]) +cddb_log_handler( cddb_log_level_t level, const char message[] ) { - cdda_data_t *p_cdda = (cdda_data_t *)p_cdda_input->p_access_data; - switch (level) { - case CDDB_LOG_DEBUG: - case CDDB_LOG_INFO: - if (!(p_cdda->i_debug & INPUT_DBG_CDDB)) return; - /* Fall through if to warn case */ - default: - cdio_log_handler (level, message); - } + cdda_data_t *p_cdda = (cdda_data_t *)p_cdda_input->p_sys; + switch( level ) + { + case CDDB_LOG_DEBUG: + case CDDB_LOG_INFO: + if( !(p_cdda->i_debug & INPUT_DBG_CDDB) ) + return; + /* Fall through if to warn case */ + default: + cdio_log_handler( level, message ); + break; + } } #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[]) +uninit_log_handler( cdio_log_level_t level, const char message[] ) { - cdda_data_t *p_cdda = (cdda_data_t *)p_cdda_input->p_access_data; - switch (level) { - case CDIO_LOG_DEBUG: - case CDIO_LOG_INFO: - if (!(p_cdda->i_debug & (INPUT_DBG_CDIO|INPUT_DBG_CDDB))) - return; - /* Fall through if to warn case */ - case CDIO_LOG_WARN: - fprintf(stderr, "WARN: %s\n", message); - break; - case CDIO_LOG_ERROR: - fprintf(stderr, "ERROR: %s\n", message); - break; - case CDIO_LOG_ASSERT: - fprintf(stderr, "ASSERT ERROR: %s\n", message); - break; - default: - fprintf(stderr, "UNKNOWN ERROR: %s\n%s %d\n", - message, - _("The above message had unknown cdio log level"), - level); - } - - /* gl_default_cdio_log_handler (level, message); */ + cdda_data_t *p_cdda = NULL; + + if( p_cdda_input ) + p_cdda = (cdda_data_t *)p_cdda_input->p_sys; + + switch( level ) + { + case CDIO_LOG_DEBUG: + case CDIO_LOG_INFO: + if( !p_cdda || !(p_cdda->i_debug & (INPUT_DBG_CDIO|INPUT_DBG_CDDB)) ) + return; + /* Fall through if to warn case */ + case CDIO_LOG_WARN: + fprintf( stderr, "WARN: %s\n", message ); + break; + case CDIO_LOG_ERROR: + fprintf( stderr, "ERROR: %s\n", message ); + break; + case CDIO_LOG_ASSERT: + fprintf( stderr, "ASSERT ERROR: %s\n", message ); + break; + default: + fprintf( stderr, "UNKNOWN ERROR: %s\n%s %d\n", message, + "The above message had unknown cdio log level", + level ); + break; + } + /* gl_default_cdio_log_handler (level, message); */ } -/***************************************************************************** - * CDDAPlay: Arrange things so we play the specified track. - * VLC_TRUE is returned if there was no error. - *****************************************************************************/ -vlc_bool_t -CDDAPlay( input_thread_t *p_input, int i_track ) +/* 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_input->p_access_data; + cdda_data_t *p_cdda = (cdda_data_t *) p_access->p_sys; + lsn_t i_offset; - if( i_track > p_cdda->i_nb_tracks || i_track < 1 ) - return VLC_FALSE; +#if LIBCDIO_VERSION_NUM >= 73 + if( p_cdda->b_audio_ctl ) + { + cdio_subchannel_t sub; + CdIo_t *p_cdio = p_cdda->p_cdio; - CDDASetArea( p_input, p_input->stream.pp_areas[i_track] ); - return VLC_TRUE; + 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 ) + { + i_offset = cdio_msf_to_lba( (&sub.abs_addr) ); + } + else + { + i_offset = cdio_msf_to_lba( (&sub.rel_addr) ); + } + } + else + { + i_offset = p_cdda->i_lsn; + } + } + else + { + i_offset = p_cdda->i_lsn; + } +#else + i_offset = p_cdda->i_lsn; +#endif + return i_offset; } /***************************************************************************** - * CDDARead: reads from the CDDA into PES packets. - ***************************************************************************** - * Returns -1 in case of error, 0 in case of EOF, otherwise the number of - * bytes. + * CDDAReadBlocks: reads a group of blocks from the CD-DA and returns + * an allocated pointer to the data. NULL is returned if no data + * read. It is also possible if we haven't read a RIFF header in which + * case one that we creaded during Open/Initialization is returned. *****************************************************************************/ -static int CDDARead( input_thread_t * p_input, byte_t * p_buffer, - size_t i_len ) +static block_t * CDDAReadBlocks( access_t * p_access ) { - cdda_data_t * p_cdda; - int i_blocks; - int i_index; - int i_read; + block_t *p_block; + cdda_data_t *p_cdda = (cdda_data_t *) p_access->p_sys; + int i_blocks = p_cdda->i_blocks_per_read; - p_cdda = (cdda_data_t *)p_input->p_access_data; + 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 ); - /* Compute the number of blocks we have to read */ - i_blocks = i_len / CDIO_CD_FRAMESIZE_RAW; - i_read = 0; + /* Check end of file */ + if( p_access->info.b_eof ) + return NULL; - if( !p_cdda->i_header_pos ) - { - p_cdda->i_header_pos = sizeof(WAVEHEADER); - i_blocks = (i_len - sizeof(WAVEHEADER)) / CDIO_CD_FRAMESIZE_RAW; - memcpy( p_buffer, &p_cdda->waveheader, sizeof(WAVEHEADER) ); - p_buffer += sizeof(WAVEHEADER); - i_read += sizeof(WAVEHEADER); + if( !p_cdda->b_header ) + { + /* Return only the dummy RIFF header we created in Open/Init */ + p_block = block_New( p_access, sizeof( WAVEHEADER ) ); + memcpy( p_block->p_buffer, &p_cdda->waveheader, sizeof(WAVEHEADER) ); + p_cdda->b_header = true; + return p_block; } - for( i_index = 0; i_index < i_blocks; i_index++ ) + /* Check end of track */ + while( p_cdda->i_lsn > cdio_get_track_last_lsn(p_cdda->p_cdio, + p_cdda->i_track) ) { + bool go_on; - if( cdio_read_audio_sector( p_cdda->p_cddev->cdio, p_buffer, - p_cdda->i_sector) != 0 ) + 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 ) { - msg_Err( p_input, "could not read sector %lu", - (long unsigned int) p_cdda->i_sector ); - return -1; + dbg_print( (INPUT_DBG_LSN), "EOF"); + p_access->info.b_eof = true; + return NULL; } - p_cdda->i_sector ++; - if( p_cdda->i_sector == p_cdda->p_sectors[p_cdda->i_track + 1] ) + p_access->info.i_update |= INPUT_UPDATE_TITLE | INPUT_UPDATE_META; + p_access->info.i_title++; + p_cdda->i_track++; + + if( p_cdda-> b_nav_mode ) { - input_area_t *p_area; + 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; + } + } - dbg_print( (INPUT_DBG_LSN|INPUT_DBG_CALL), - "end of track, cur: %lu", - (long unsigned int) p_cdda->i_sector ); + /* Possibly adjust i_blocks so we don't read past the end of a track. */ + if( p_cdda->i_lsn + i_blocks >= + cdio_get_track_lsn(p_cdda->p_cdio, p_cdda->i_track+1) ) + { + i_blocks = cdio_get_track_lsn( p_cdda->p_cdio, p_cdda->i_track+1 ) + - p_cdda->i_lsn; + } - /*???? if ( p_cdda->i_track >= p_cdda->i_nb_tracks - 1 )*/ - return 0; /* EOF */ + /* Do the actual reading */ + p_block = block_New( p_access, i_blocks * CDIO_CD_FRAMESIZE_RAW ); + if( !p_block) + { + msg_Err( p_access, "cannot get a new block of size: %i", + i_blocks * CDIO_CD_FRAMESIZE_RAW ); + intf_UserFatal( p_access, false, _("CD reading failed"), + _("VLC could not get a new block of size: %i."), + i_blocks * CDIO_CD_FRAMESIZE_RAW ); + return NULL; + } - vlc_mutex_lock( &p_input->stream.stream_lock ); - p_area = p_input->stream.pp_areas[ - p_input->stream.p_selected_area->i_id + 1 ]; + { +#if LIBCDIO_VERSION_NUM >= 72 + driver_return_code_t rc = DRIVER_OP_SUCCESS; - p_area->i_part = 1; - CDDASetArea( p_input, p_area ); - vlc_mutex_unlock( &p_input->stream.stream_lock ); + 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: "" ); + + free( psz_err ); + 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; } - i_read += CDIO_CD_FRAMESIZE_RAW; - p_buffer += CDIO_CD_FRAMESIZE_RAW; } - if ( i_len % CDIO_CD_FRAMESIZE_RAW ) /* this should not happen */ - { - msg_Err( p_input, "must read full sectors" ); - } + p_cdda->i_lsn += i_blocks; + p_access->info.i_pos += i_blocks * CDIO_CD_FRAMESIZE_RAW; - return i_read; + return p_block; } /***************************************************************************** - * CDDASetProgram: Does nothing since a CDDA is mono_program + * CDDARead: Handler for audio control reads the CD-DA. *****************************************************************************/ -static int CDDASetProgram( input_thread_t * p_input, - pgrm_descriptor_t * p_program) +static ssize_t +CDDARead( access_t * p_access, uint8_t *p_buffer, size_t i_len ) { - cdda_data_t * p_cdda= (cdda_data_t *) p_input->p_access_data; - dbg_print( (INPUT_DBG_CALL|INPUT_DBG_EXT), "" ); - return 0; -} - -/***************************************************************************** - * CDDASetArea: initialize input data for title x. - * It should be called for each user navigation request. - ****************************************************************************/ -static int CDDASetArea( input_thread_t * p_input, input_area_t * p_area ) -{ - cdda_data_t *p_cdda = (cdda_data_t*) p_input->p_access_data; - vlc_value_t val; - vlc_value_t text; - - dbg_print( (INPUT_DBG_CALL|INPUT_DBG_EXT), ""); + cdda_data_t *p_cdda = (cdda_data_t *) p_access->p_sys; - text.psz_string = _("Track"); - var_Change( p_input, "title", VLC_VAR_SETTEXT, &text, NULL ); + 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); - /* we can't use the interface slider until initilization is complete */ - p_input->stream.b_seekable = 0; + /* Check end of file */ + if( p_access->info.b_eof ) + return 0; - if( p_area != p_input->stream.p_selected_area ) { - /* Change the default area */ - p_input->stream.p_selected_area = p_area; - - /* Change the current track */ - p_cdda->i_track = p_area->i_id - 1; - p_cdda->i_sector = p_cdda->p_sectors[p_cdda->i_track]; + 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; + } - /* Update the navigation variables without triggering a callback */ - val.i_int = p_area->i_id; - var_Change( p_input, "title", VLC_VAR_SETVALUE, &val, NULL ); + p_cdda->i_lsn = i_lsn; + p_access->info.i_pos = p_cdda->i_lsn * CDIO_CD_FRAMESIZE_RAW; } - p_cdda->i_sector = p_cdda->p_sectors[p_cdda->i_track]; + dbg_print( (INPUT_DBG_LSN), "updated lsn: %d", p_cdda->i_lsn ); - p_input->stream.p_selected_area->i_tell = - (off_t)p_cdda->i_sector * (off_t)CDIO_CD_FRAMESIZE_RAW - - p_input->stream.p_selected_area->i_start; - - /* warn interface that something has changed */ - p_input->stream.b_seekable = 1; - p_input->stream.b_changed = 1; + /* 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 = true; + return 0; + } + p_access->info.i_update |= INPUT_UPDATE_TITLE; + p_access->info.i_title++; + p_cdda->i_track++; - return 0; + 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; } -/**************************************************************************** - * CDDASeek - ****************************************************************************/ -static void CDDASeek( input_thread_t * p_input, off_t i_off ) +/*! Pause CD playing via audio control */ +static bool cdda_audio_pause( CdIo_t *p_cdio ) { - cdda_data_t * p_cdda; - - p_cdda = (cdda_data_t *) p_input->p_access_data; - - p_cdda->i_sector = p_cdda->p_sectors[p_cdda->i_track] - + i_off / (off_t)CDIO_CD_FRAMESIZE_RAW; - - vlc_mutex_lock( &p_input->stream.stream_lock ); - p_input->stream.p_selected_area->i_tell = - (off_t)p_cdda->i_sector * (off_t)CDIO_CD_FRAMESIZE_RAW - - p_input->stream.p_selected_area->i_start; - - vlc_mutex_unlock( &p_input->stream.stream_lock ); - - dbg_print( (INPUT_DBG_CALL|INPUT_DBG_EXT|INPUT_DBG_SEEK), - "sector %lu, offset: %lld, i_tell: %lld", - (long unsigned int) p_cdda->i_sector, i_off, - p_input->stream.p_selected_area->i_tell ); + 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 NEED_UPDATE_VAR -/**************************************************************************** - Update the "varname" variable to i_num without triggering a callback. -****************************************************************************/ -static void -CDDAUpdateVar( input_thread_t *p_input, int i_num, int i_action, - const char *p_varname, char *p_label, - const char *p_debug_label) +#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 ) { - vlc_value_t val; - val.i_int = i_num; - if (p_label) { - vlc_value_t text; - text.psz_string = p_label; - var_Change( p_input, p_varname, VLC_VAR_SETTEXT, &text, NULL ); - } - var_Change( p_input, p_varname, i_action, &val, NULL ); + 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 -static void InformationCreate( input_thread_t *p_input ) +/**************************************************************************** + * 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 ) { - cdda_data_t *p_cdda = (cdda_data_t *) p_input->p_access_data; + cdda_data_t *p_cdda = (cdda_data_t *) p_access->p_sys; -#ifdef HAVE_LIBCDDB - if (p_cdda->i_cddb_enabled) { - - dbg_print( INPUT_DBG_META, "field %s: %s\n", "Title", - p_cdda->cddb.disc->title ); - input_Control( p_input, INPUT_ADD_INFO, _("General"), _("Title"), - "%s", p_cdda->cddb.disc->title ); - - dbg_print( INPUT_DBG_META, "field %s: %s\n", "Artist", - p_cdda->cddb.disc->artist ); - input_Control( p_input, INPUT_ADD_INFO, _("General"), _("Artist"), - "%s", p_cdda->cddb.disc->artist ); - - dbg_print( INPUT_DBG_META, "field %s: %s\n", "Genre", - p_cdda->cddb.disc->genre ); - input_Control( p_input, INPUT_ADD_INFO, _("General"), _("Genre"), - "%s", p_cdda->cddb.disc->genre ); - - dbg_print( INPUT_DBG_META, "field %s: %s\n", "Extended Data", - p_cdda->cddb.disc->ext_data ); - input_Control( p_input, INPUT_ADD_INFO, _("General"), _("Extended Data"), - "%s", p_cdda->cddb.disc->ext_data ); - - if (p_cdda->cddb.disc->year != 0) - input_Control( p_input, INPUT_ADD_INFO, _("General"), - _("Year"), "%d", p_cdda->cddb.disc->year ); - if ( p_cdda->cddb.disc->discid ) - input_Control( p_input, INPUT_ADD_INFO, _("General"), - _("CDDB Disc ID"), "%x", p_cdda->cddb.disc->discid ); - - if ( p_cdda->cddb.disc->category != CDDB_CAT_INVALID ) - input_Control( p_input, INPUT_ADD_INFO, _("General"), - _("CDDB Disc Category"), "%s", - CDDB_CATEGORY[p_cdda->cddb.disc->category] ); - } + dbg_print( (INPUT_DBG_CALL|INPUT_DBG_EXT|INPUT_DBG_SEEK), + "lsn %lu, offset: %lld", + (long unsigned int) p_cdda->i_lsn, i_pos ); -#endif /*HAVE_LIBCDDB*/ -#define TITLE_MAX 30 - - { - track_t i_track = p_cdda->i_nb_tracks; - char psz_buffer[MSTRTIME_MAX_SIZE]; - mtime_t i_duration = - (p_cdda->p_sectors[i_track] - p_cdda->p_sectors[0]) - / CDIO_CD_FRAMES_PER_SEC; - - dbg_print( INPUT_DBG_META, "Duration %ld", (long int) i_duration ); - input_Control( p_input, INPUT_ADD_INFO, _("General"), _("Duration"), "%s", - secstotimestr( psz_buffer, i_duration ) ); - - for( i_track = 0 ; i_track < p_cdda->i_nb_tracks ; i_track++ ) { - char track_str[TITLE_MAX]; - mtime_t i_duration = - (p_cdda->p_sectors[i_track+1] - p_cdda->p_sectors[i_track]) - / CDIO_CD_FRAMES_PER_SEC; - snprintf(track_str, TITLE_MAX, "%s %02d", _("Track"), i_track+1); - input_Control( p_input, INPUT_ADD_INFO, track_str, _("Duration"), "%s", - secstotimestr( psz_buffer, i_duration ) ); + p_cdda->i_lsn = (i_pos / CDIO_CD_FRAMESIZE_RAW); -#ifdef HAVE_LIBCDDB - if (p_cdda->i_cddb_enabled) { - cddb_track_t *t=cddb_disc_get_track(p_cdda->cddb.disc, - i_track); - if (t != NULL) { - if ( t->artist != NULL && strlen(t->artist) ) { - input_Control( p_input, INPUT_ADD_INFO, track_str, - _("Artist"), "%s", t->artist ); - } - if ( t->title != NULL && strlen(t->title) ) { - input_Control( p_input, INPUT_ADD_INFO, track_str, - _("Title"), "%s", t->title ); - } - if ( t->ext_data != NULL && strlen(t->ext_data) ) { - input_Control( p_input, INPUT_ADD_INFO, track_str, - _("Extended Data"), "%s", t->ext_data ); - } - } - } +#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 - } - } -} - -#ifdef HAVE_LIBCDDB +#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; -#define free_and_dup(var, val) \ - if (var) free(var); \ - if (val) var=strdup(val); + 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 -static void -GetCDDBInfo( const input_thread_t *p_input, cdda_data_t *p_cdda ) -{ + if( ! p_cdda->b_nav_mode ) + p_cdda->i_lsn += cdio_get_track_lsn( p_cdda->p_cdio, p_cdda->i_track ); - dbg_print( (INPUT_DBG_CALL|INPUT_DBG_EXT), "" ); + /* 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 ); + } - if (config_GetInt( p_input, MODULE_STRING "-cddb-enabled" )) { - int i, i_matches; - cddb_conn_t *conn = cddb_new(); - const CdIo *cdio = p_cdda->p_cddev->cdio; + p_access->info.i_pos = i_pos; + p_access->info.b_eof = false; + 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; - cddb_log_set_handler (uninit_log_handler); + dbg_print( (INPUT_DBG_CALL), "called track: %d\n", i_track ); - if (!conn) { - msg_Warn( p_input, "unable to initialize libcddb" ); - goto cddb_destroy; + 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; - cddb_set_email_address( conn, - config_GetPsz( p_input, - MODULE_STRING "-cddb-email") ); + /* 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 ); - cddb_set_server_name( conn, - config_GetPsz( p_input, - MODULE_STRING "-cddb-server") ); + p_cdda->last_frame = cdio_get_track_lsn( p_cdda->p_cdio, i_track+1 ) - 1; - cddb_set_server_port(conn, - config_GetInt( p_input, - MODULE_STRING "-cddb-port") ); - - /* Set the location of the local CDDB cache directory. - The default location of this directory is */ +#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; +} - if (!config_GetInt( p_input, MODULE_STRING "-cddb-enable-cache" )) - cddb_cache_disable(conn); +/**************************************************************************** + * Public functions + ****************************************************************************/ - cddb_cache_set_dir(conn, - config_GetPsz( p_input, - MODULE_STRING "-cddb-cachedir") ); +/***************************************************************************** + * Open: open cdda device or image file and initialize structures + * for subsequent operations. + *****************************************************************************/ +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_t *p_cdio; + track_t i_track = 1; + bool b_single_track = false; + int i_rc = VLC_EGENERIC; - cddb_set_timeout(conn, - config_GetInt( p_input, MODULE_STRING "-cddb-timeout") ); + p_access->p_sys = NULL; + /* Set where to log errors messages from libcdio. */ + p_cdda_input = p_access; - if (config_GetInt( p_input, MODULE_STRING "-cddb-httpd" )) { - cddb_http_enable(conn); - } else - cddb_http_disable(conn); + /* parse the options passed in command line : */ + if( p_access->psz_path && *p_access->psz_path ) + { + char *psz_parser = psz_source = strdup( p_access->psz_path ); - p_cdda->cddb.disc = cddb_disc_new(); - if (!p_cdda->cddb.disc) { - msg_Err( p_input, "Unable to create CDDB disc structure." ); - goto cddb_end; - } + while( *psz_parser && *psz_parser != '@' ) + { + psz_parser++; + } - for(i = 1; i <= p_cdda->i_nb_tracks; i++) { - cddb_track_t *t = cddb_track_new(); - t->frame_offset = cdio_get_track_lba(cdio, i); - cddb_disc_add_track(p_cdda->cddb.disc, t); - } + if( *psz_parser == '@' ) + { + /* Found options */ + *psz_parser = '\0'; + ++psz_parser; - p_cdda->cddb.disc->length = - cdio_get_track_lba(cdio, CDIO_CDROM_LEADOUT_TRACK) - / CDIO_CD_FRAMES_PER_SEC; + if ('T' == *psz_parser || 't' == *psz_parser ) + ++psz_parser; - if (!cddb_disc_calc_discid(p_cdda->cddb.disc)) { - msg_Err( p_input, "CDDB disc calc failed" ); - goto cddb_destroy; + i_track = (int)strtol( psz_parser, NULL, 10 ); + i_track = i_track ? i_track : 1; + b_single_track = true; + } } - i_matches = cddb_query(conn, p_cdda->cddb.disc); - if (i_matches > 0) { - if (i_matches > 1) - msg_Warn( p_input, "Found %d matches in CDDB. Using first one.", - i_matches); - cddb_read(conn, p_cdda->cddb.disc); - - if (p_cdda->i_debug & INPUT_DBG_CDDB) - cddb_disc_print(p_cdda->cddb.disc); + if( !psz_source || !*psz_source ) + { + /* No device/track given. Continue only when this plugin was + selected */ + if( !p_this->b_force ) + return VLC_EGENERIC; - } else { - msg_Warn( p_input, "CDDB error: %s", cddb_error_str(errno)); + psz_source = var_CreateGetString( p_this, "cd-audio" ); + if( !psz_source || !*psz_source ) + { + free( psz_source ); + /* Scan for a CD-ROM drive with a CD-DA in it. */ + char **ppsz_drives = + cdio_get_devices_with_cap( NULL, CDIO_FS_AUDIO, false ); + + if( (NULL == ppsz_drives) || (NULL == ppsz_drives[0]) ) + { + msg_Err( p_access, + "libcdio couldn't find something with a CD-DA in it" ); + if( ppsz_drives ) + cdio_free_device_list( ppsz_drives ); + return VLC_EGENERIC; + } + psz_source = strdup( ppsz_drives[0] ); + cdio_free_device_list( ppsz_drives ); + } } + cdio_log_set_handler( cdio_log_handler ); - cddb_destroy: - cddb_destroy(conn); - } - cddb_end: ; -} -#endif /*HAVE_LIBCDDB*/ + /* Open CDDA */ + if( !(p_cdio = cdio_open( psz_source, DRIVER_UNKNOWN )) ) + { + msg_Warn( p_access, "could not open %s", psz_source ); + free( psz_source ); + return VLC_EGENERIC; + } -#define add_format_str_info(val) \ - { \ - const char *str = val; \ - unsigned int len; \ - if (val != NULL) { \ - len=strlen(str); \ - if (len != 0) { \ - strncat(tp, str, TEMP_STR_LEN-(tp-temp_str)); \ - tp += len; \ - } \ - saw_control_prefix = false; \ - } \ - } - -#define add_format_num_info(val, fmt) \ - { \ - char num_str[10]; \ - unsigned int len; \ - sprintf(num_str, fmt, val); \ - len=strlen(num_str); \ - if (len != 0) { \ - strncat(tp, num_str, TEMP_STR_LEN-(tp-temp_str)); \ - tp += len; \ - } \ - saw_control_prefix = false; \ - } - -/*! - Take a format string and expand escape sequences, that is sequences that - begin with %, with information from the current CD. - The expanded string is returned. Here is a list of escape sequences: - - %a : The album artist ** - %A : The album information ** - %C : Category ** - %I : CDDB disk ID ** - %G : Genre ** - %M : The current MRL - %m : The CD-DA Media Catalog Number (MCN) - %n : The number of tracks on the CD - %p : The artist/performer/composer in the track ** - %T : The track number ** - %s : Number of seconds in this track - %t : The name ** - %Y : The year 19xx or 20xx ** - %% : a % -*/ -static char * -CDDAFormatStr(const input_thread_t *p_input, cdda_data_t *p_cdda, - const char format_str[], const char *mrl, int i_track) -{ -#define TEMP_STR_SIZE 256 -#define TEMP_STR_LEN (TEMP_STR_SIZE-1) - static char temp_str[TEMP_STR_SIZE]; - size_t i; - char * tp = temp_str; - bool saw_control_prefix = false; - size_t format_len = strlen(format_str); - - memset(temp_str, 0, TEMP_STR_SIZE); - - for (i=0; ii_cddb_enabled) goto not_special; - add_format_str_info(p_cdda->cddb.disc->artist); - break; - case 'A': - if (!p_cdda->i_cddb_enabled) goto not_special; - add_format_str_info(p_cdda->cddb.disc->title); - break; - case 'C': - if (!p_cdda->i_cddb_enabled) goto not_special; - add_format_str_info(CDDB_CATEGORY[p_cdda->cddb.disc->category]); - break; - case 'G': - if (!p_cdda->i_cddb_enabled) goto not_special; - add_format_str_info(p_cdda->cddb.disc->genre); - break; - case 'I': - if (!p_cdda->i_cddb_enabled) goto not_special; - add_format_num_info(p_cdda->cddb.disc->discid, "%x"); - break; - case 'Y': - if (!p_cdda->i_cddb_enabled) goto not_special; - add_format_num_info(p_cdda->cddb.disc->year, "%5d"); - break; - case 't': - if (p_cdda->i_cddb_enabled) { - cddb_track_t *t=cddb_disc_get_track(p_cdda->cddb.disc, - i_track-1); - if (t != NULL && t->title != NULL) - add_format_str_info(t->title); - } else goto not_special; - break; - case 'p': - if (p_cdda->i_cddb_enabled) { - cddb_track_t *t=cddb_disc_get_track(p_cdda->cddb.disc, - i_track-1); - if (t != NULL && t->artist != NULL) - add_format_str_info(t->artist); - } else goto not_special; - break; - case 'e': - if (p_cdda->i_cddb_enabled) { - cddb_track_t *t=cddb_disc_get_track(p_cdda->cddb.disc, - i_track-1); - if (t != NULL && t->ext_data != NULL) - add_format_str_info(t->ext_data); - } else goto not_special; - break; + cddb_log_set_handler ( cddb_log_handler ); + p_cdda->cddb.disc = NULL; + p_cdda->b_cddb_enabled = + config_GetInt( p_access, MODULE_STRING "-cddb-enabled" ); #endif - - case 'M': - add_format_str_info(mrl); - break; - -#if FINISHED - case 'm': - add_format_str_info(p_cdda->mcn); - break; + 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" ); +#if LIBCDIO_VERSION_NUM >= 73 + p_cdda->b_audio_ctl = + config_GetInt( p_access, MODULE_STRING "-analog-output" ); #endif - case 'n': - add_format_num_info(p_cdda->i_nb_tracks, "%d"); - break; - -#ifdef HAVE_LIBCDDB - case 's': - if (p_cdda->i_cddb_enabled) { - char psz_buffer[MSTRTIME_MAX_SIZE]; - mtime_t i_duration = - (p_cdda->p_sectors[i_track] - p_cdda->p_sectors[i_track-1]) - / CDIO_CD_FRAMES_PER_SEC; - add_format_str_info(secstotimestr( psz_buffer, i_duration ) ); - } else goto not_special; - break; -#endif - - case 'T': - add_format_num_info(i_track, "%02d"); - break; -#ifdef HAVE_LIBCDDB - not_special: -#endif - default: - *tp++ = '%'; - *tp++ = format_str[i]; - saw_control_prefix = false; + p_cdda->psz_source = strdup( psz_source ); + p_cdda->b_header = 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" ); + 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 ); + + if( 0 == p_cdda->i_blocks_per_read ) + p_cdda->i_blocks_per_read = DEFAULT_BLOCKS_PER_READ; + + if( (p_cdda->i_blocks_per_read < MIN_BLOCKS_PER_READ) + || (p_cdda->i_blocks_per_read > MAX_BLOCKS_PER_READ) ) + { + msg_Warn( p_cdda_input, + "number of blocks (%d) has to be between %d and %d. " + "Using %d.", + p_cdda->i_blocks_per_read, + MIN_BLOCKS_PER_READ, MAX_BLOCKS_PER_READ, + DEFAULT_BLOCKS_PER_READ ); + p_cdda->i_blocks_per_read = DEFAULT_BLOCKS_PER_READ; } - } - return strdup(temp_str); -} -static void -CDDACreatePlayListItem(const input_thread_t *p_input, cdda_data_t *p_cdda, - playlist_t *p_playlist, unsigned int i_track, - char *psz_mrl, int psz_mrl_max, - const char *psz_source, int playlist_operation, - int i_pos) -{ - mtime_t i_duration = - (p_cdda->p_sectors[i_track] - p_cdda->p_sectors[i_track-1]) - * (1000000 / CDIO_CD_FRAMES_PER_SEC) ; - char *p_author; - char *p_title; - char *config_varname = MODULE_STRING "-title-format"; - playlist_item_t *p_item; - -#ifdef HAVE_LIBCDDB - if (p_cdda->i_cddb_enabled) { - config_varname = MODULE_STRING "-cddb-title-format"; - } -#endif /*HAVE_LIBCDDB*/ - - snprintf(psz_mrl, psz_mrl_max, "%s%s@T%u", - CDDA_MRL_PREFIX, psz_source, i_track); - - p_title = CDDAFormatStr(p_input, p_cdda, - config_GetPsz( p_input, config_varname ), - psz_mrl, i_track); - - dbg_print( INPUT_DBG_META, "mrl: %s, title: %s, duration, %ld, pos %d", - psz_mrl, p_title, (long int) i_duration / 1000000 , i_pos ); - playlist_AddExt( p_playlist, psz_mrl, p_title, playlist_operation, - i_pos, i_duration , NULL, 0); - - if( i_pos == PLAYLIST_END ) i_pos = p_playlist->i_size - 1; - - vlc_mutex_lock( &p_playlist->object_lock ); - p_item = playlist_ItemGetByPos( p_playlist, i_pos ); - vlc_mutex_unlock( &p_playlist->object_lock ); - if( !p_item ) - return; - - vlc_mutex_lock( &p_item->input.lock ); - - p_author = - CDDAFormatStr( p_input, p_cdda, - config_GetPsz( p_input, MODULE_STRING "-author-format" ), - psz_mrl, i_track ); - - playlist_ItemAddInfo( p_item , _("General"),_("Author"), p_author); + dbg_print( (INPUT_DBG_CALL|INPUT_DBG_EXT), "%s", psz_source ); -#ifdef HAVE_LIBCDDB - if (p_cdda->i_cddb_enabled) { - const char *psz_general_cat = _("General"); - - playlist_ItemAddInfo( p_item, psz_general_cat, _("Album"), - "%s", p_cdda->cddb.disc->title); - playlist_ItemAddInfo( p_item, psz_general_cat, _("Disc Artist(s)"), - "%s", p_cdda->cddb.disc->artist); - playlist_ItemAddInfo( p_item, psz_general_cat, - _("CDDB Disc Category"), - "%s", CDDB_CATEGORY[p_cdda->cddb.disc->category]); - playlist_ItemAddInfo( p_item, psz_general_cat, _("Genre"), - "%s", p_cdda->cddb.disc->genre); - if ( p_cdda->cddb.disc->discid ) { - playlist_ItemAddInfo( p_item, psz_general_cat, _("CDDB Disc ID"), - "%x", p_cdda->cddb.disc->discid ); + /* Set up p_access */ + if( p_cdda->b_audio_ctl ) + { + p_access->pf_read = CDDARead; + p_access->pf_block = NULL; } - if (p_cdda->cddb.disc->year != 0) { - playlist_ItemAddInfo( p_item, psz_general_cat, - _("Year"), "%5d", p_cdda->cddb.disc->year ); + else + { + p_access->pf_read = NULL; + p_access->pf_block = CDDAReadBlocks; } - if (p_cdda->i_cddb_enabled) { - cddb_track_t *t=cddb_disc_get_track(p_cdda->cddb.disc, - i_track-1); - if (t != NULL && t->artist != NULL) { - playlist_ItemAddInfo( p_item, psz_general_cat, - _("Track Artist"), "%s", t->artist ); - playlist_ItemAddInfo( p_item , psz_general_cat, - _("Track Title"), "%s", t->title ); - } - } + p_access->pf_control = CDDAControl; + p_access->pf_seek = CDDASeek; - } -#endif /*HAVE_LIBCDDB*/ + { + lsn_t i_last_lsn; - vlc_mutex_unlock( &p_item->input.lock ); -} + 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 ); -static int -CDDAFixupPlayList( input_thread_t *p_input, cdda_data_t *p_cdda, - const char *psz_source, bool play_single_track) -{ - int i; - playlist_t * p_playlist; - char * psz_mrl; - unsigned int psz_mrl_max = strlen(CDDA_MRL_PREFIX) + strlen(psz_source) + - strlen("@T") + strlen("100") + 1; + 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; + } -#ifdef HAVE_LIBCDDB - p_cdda->i_cddb_enabled = - config_GetInt( p_input, MODULE_STRING "-cddb-enabled" ); - if( play_single_track && !p_cdda->i_cddb_enabled ) return 0; -#else - if( play_single_track ) return 0; -#endif + p_access->info.i_update = 0; + p_access->info.b_eof = false; + p_access->info.i_title = 0; + p_access->info.i_seekpoint = 0; + p_access->p_sys = (access_sys_t *) p_cdda; - psz_mrl = malloc( psz_mrl_max ); + /* We read the Table Of Content information */ + i_rc = CDDAInit( p_access, p_cdda ); + if( VLC_SUCCESS != i_rc ) + goto error; - if( psz_mrl == NULL ) - { - msg_Warn( p_input, "out of memory" ); - return -1; - } + cdda_play_track( p_access, i_track ); + CDDAFixupPlaylist( p_access, p_cdda, b_single_track ); - p_playlist = (playlist_t *) vlc_object_find( p_input, VLC_OBJECT_PLAYLIST, - FIND_ANYWHERE ); - if( !p_playlist ) +#if LIBCDIO_VERSION_NUM >= 72 { - msg_Warn( p_input, "can't find playlist" ); - free(psz_mrl); - return -1; - } + char *psz_paranoia = config_GetPsz( p_access, + MODULE_STRING "-paranoia" ); -#ifdef HAVE_LIBCDDB - if (p_cdda->i_cddb_enabled) - GetCDDBInfo(p_input, p_cdda); - else - p_cdda->cddb.disc = NULL; + p_cdda->e_paranoia = PARANOIA_MODE_DISABLE; + if( psz_paranoia && *psz_paranoia ) + { + if( !strncmp( psz_paranoia, "full", strlen("full") ) ) + p_cdda->e_paranoia = PARANOIA_MODE_FULL; + else if( !strncmp(psz_paranoia, "overlap", strlen("overlap")) ) + p_cdda->e_paranoia = PARANOIA_MODE_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_MODE_DISABLE; + } + 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_MODE_FULL == p_cdda->e_paranoia ? + PARANOIA_MODE_FULL^PARANOIA_MODE_NEVERSKIP : + PARANOIA_MODE_OVERLAP^PARANOIA_MODE_NEVERSKIP ); + } + } + } + free( psz_paranoia ); + } #endif - InformationCreate(p_input); - - if (play_single_track) { - /* May fill out more information when the playlist user interface becomes - more mature. + /* Build a WAV header to put in front of the output data. + This gets sent back in the Block (read) routine. */ - CDDACreatePlayListItem(p_input, p_cdda, p_playlist, p_cdda->i_track+1, - psz_mrl, psz_mrl_max, psz_source, PLAYLIST_REPLACE, - p_playlist->i_index); - } else { - - playlist_Delete( p_playlist, p_playlist->i_index); - - for( i = 1 ; i <= p_cdda->i_nb_tracks ; i++ ) - { - CDDACreatePlayListItem(p_input, p_cdda, p_playlist, i, psz_mrl, - psz_mrl_max, psz_source, PLAYLIST_APPEND, - PLAYLIST_END); - } - - playlist_Command( p_playlist, PLAYLIST_GOTO, 0 ); + memset( &p_cdda->waveheader, 0, sizeof(WAVEHEADER) ); - } + SetWLE( &p_cdda->waveheader.Format, 1 ); /*WAVE_FORMAT_PCM*/ + SetWLE( &p_cdda->waveheader.BitsPerSample, 16); - vlc_object_release( p_playlist ); - free(psz_mrl); - return 0; -} + p_cdda->waveheader.MainChunkID = VLC_FOURCC('R', 'I', 'F', 'F'); + p_cdda->waveheader.Length = 0; /* we just don't know */ + p_cdda->waveheader.ChunkTypeID = VLC_FOURCC('W', 'A', 'V', 'E'); + p_cdda->waveheader.SubChunkID = VLC_FOURCC('f', 'm', 't', ' '); -/**************************************************************************** - * Public functions - ****************************************************************************/ -int -E_(DebugCB) ( vlc_object_t *p_this, const char *psz_name, - vlc_value_t oldval, vlc_value_t val, void *p_data ) -{ - cdda_data_t *p_cdda; + SetDWLE( &p_cdda->waveheader.SubChunkLength, 16); + SetWLE( &p_cdda->waveheader.Modus, 2); + SetDWLE( &p_cdda->waveheader.SampleFreq, CDDA_FREQUENCY_SAMPLE); + SetWLE( &p_cdda->waveheader.BytesPerSample, + 2 /*Modus*/ * 16 /*BitsPerSample*/ / 8 ); + SetDWLE( &p_cdda->waveheader.BytesPerSec, + 2*16/8 /*BytesPerSample*/ * CDDA_FREQUENCY_SAMPLE ); - if (NULL == p_cdda_input) return VLC_EGENERIC; + p_cdda->waveheader.DataChunkID = VLC_FOURCC('d', 'a', 't', 'a'); + p_cdda->waveheader.DataLength = 0; /* we just don't know */ - p_cdda = (cdda_data_t *)p_cdda_input->p_access_data; + /* PTS delay */ + var_Create( p_access, MODULE_STRING "-caching", + VLC_VAR_INTEGER|VLC_VAR_DOINHERIT ); + vlc_object_release( p_cdda->p_input ); + return VLC_SUCCESS; - if (p_cdda->i_debug & (INPUT_DBG_CALL|INPUT_DBG_EXT)) { - msg_Dbg( p_cdda_input, "Old debug (x%0x) %d, new debug (x%0x) %d", - p_cdda->i_debug, p_cdda->i_debug, val.i_int, val.i_int); - } - p_cdda->i_debug = val.i_int; - return VLC_SUCCESS; + error: + cdio_destroy( p_cdda->p_cdio ); + free( psz_source ); + if( p_cdda ) + { + if ( p_cdda->p_input ) + vlc_object_release( p_cdda->p_input ); + free(p_cdda); + } + return i_rc; } -int -E_(CDDBEnabledCB) ( vlc_object_t *p_this, const char *psz_name, - vlc_value_t oldval, vlc_value_t val, void *p_data ) +/***************************************************************************** + * CDDAClose: closes cdda and frees any resources associded with it. + *****************************************************************************/ +void CDDAClose (vlc_object_t *p_this ) { - cdda_data_t *p_cdda; + 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 - if (NULL == p_cdda_input) return VLC_EGENERIC; + dbg_print( (INPUT_DBG_CALL|INPUT_DBG_EXT), "" ); - p_cdda = (cdda_data_t *)p_cdda_input->p_access_data; + /* Remove playlist titles */ + for( i = 0; i < p_cdda->i_titles; i++ ) + { + vlc_input_title_Delete( p_cdda->p_title[i] ); + } #ifdef HAVE_LIBCDDB - if (p_cdda->i_debug & (INPUT_DBG_CALL|INPUT_DBG_EXT)) { - msg_Dbg( p_cdda_input, "Old CDDB Enabled (x%0x) %d, new (x%0x) %d", - p_cdda->i_cddb_enabled, p_cdda->i_cddb_enabled, - val.i_int, val.i_int); - } - p_cdda->i_cddb_enabled = val.i_int; + 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 - return VLC_SUCCESS; -} -/*FIXME*/ -#if PLAYLIST_INTERFACE_IS_FIXED -int -E_(TitleFormatCB) ( vlc_object_t *p_this, const char *psz_name, - vlc_value_t oldval, vlc_value_t val, void *p_data ) -{ - cdda_data_t *p_cdda; + cdio_destroy( p_cdda->p_cdio ); + cdio_log_set_handler( uninit_log_handler ); - if (NULL == p_cdda_input) return VLC_EGENERIC; +#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 - p_cdda = (cdda_data_t *)p_cdda_input->p_access_data; + free( p_cdda->psz_mcn ); + free( p_cdda->psz_source ); - if (p_cdda->i_debug & (INPUT_DBG_CALL|INPUT_DBG_EXT)) { - msg_Dbg( p_cdda_input, "Old CDDB Enabled (%s), new (%s)", - oldval.psz_string, val.psz_string); - } - ???? - return VLC_SUCCESS; -} +#if LIBCDDB_VERSION_NUM >= 1 + libcddb_shutdown(); #endif + free( p_cdda ); + p_cdda = NULL; + p_cdda_input = NULL; +} /***************************************************************************** - * Open: open cdda + * 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. *****************************************************************************/ -int -E_(Open)( vlc_object_t *p_this ) +static int CDDAControl( access_t *p_access, int i_query, va_list args ) { - input_thread_t * p_input = (input_thread_t *)p_this; - char * psz_orig; - char * psz_parser; - char * psz_source; - cdda_data_t * p_cdda; - int i; - int i_track = 1; - cddev_t *p_cddev; - vlc_value_t val; - bool play_single_track = false; - - /* Set where to log errors messages from libcdio. */ - p_cdda_input = (input_thread_t *)p_this; - - /* parse the options passed in command line : */ - psz_orig = psz_parser = psz_source = strdup( p_input->psz_name ); + cdda_data_t *p_cdda = (cdda_data_t *) p_access->p_sys; + int *pi_int; + int i; - if( !psz_orig ) - { - return( -1 ); - } - - while( *psz_parser && *psz_parser != '@' ) - { - psz_parser++; - } + dbg_print( (INPUT_DBG_CALL|INPUT_DBG_EXT|INPUT_DBG_EVENT), + "query %d", i_query ); - if( *psz_parser == '@' ) + switch( i_query ) { - /* Found options */ - *psz_parser = '\0'; - ++psz_parser; + /* Pass back a copy of meta information that was gathered when we + during the Open/Initialize call. + */ + case ACCESS_GET_META: + { +#if 0 + vlc_meta_t **pp_meta = (vlc_meta_t**)va_arg( args, vlc_meta_t** ); + if( p_cdda->p_meta ) + { + *pp_meta = vlc_meta_Duplicate( p_cdda->p_meta ); + dbg_print( INPUT_DBG_META, "%s", "Meta copied" ); + return VLC_SUCCESS; + } + else +#endif + { + msg_Warn( p_access, "tried to copy NULL meta info" ); + return VLC_EGENERIC; + } + } - if ('T' == *psz_parser || 't' == *psz_parser ) - ++psz_parser; + case ACCESS_CAN_CONTROL_PACE: + { + bool *pb_bool = (bool*)va_arg( args, bool* ); + *pb_bool = p_cdda->b_audio_ctl ? false : true; + dbg_print( INPUT_DBG_META, "can control pace? %d", *pb_bool); + return VLC_SUCCESS; + } - i_track = (int)strtol( psz_parser, NULL, 10 ); - i_track = i_track ? i_track : 1; - play_single_track = true; - } + 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: + { + bool *pb_bool = (bool*)va_arg( args, bool* ); + *pb_bool = true; + return VLC_SUCCESS; + } + + /* */ + case ACCESS_GET_MTU: + { + 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; + } - if( !*psz_source ) { - /* No source specified, so figure it out. */ - if( !p_input->psz_access ) { - free( psz_orig ); - return -1; - } - psz_source = config_GetPsz( p_input, "cd-audio" ); - - if( !psz_source || 0==strlen(psz_source) ) { - /* Scan for a CD-ROM drive with a CD-DA in it. */ - char **cd_drives = - cdio_get_devices_with_cap(NULL, CDIO_FS_AUDIO, false); - if (NULL == cd_drives) return -1; - if (cd_drives[0] == NULL) { - cdio_free_device_list(cd_drives); - return -1; + case ACCESS_GET_PTS_DELAY: + { + int64_t *pi_64 = (int64_t*)va_arg( args, int64_t * ); + *pi_64 = var_GetInteger( p_access, MODULE_STRING "-caching" ) + * MILLISECONDS_PER_SEC; + break; } - psz_source = strdup(cd_drives[0]); - cdio_free_device_list(cd_drives); - } - } - /* Open CDDA */ - cdio_log_set_handler ( cdio_log_handler ); -#ifdef HAVE_LIBCDDB - cddb_log_set_handler ( cddb_log_handler ); -#endif + case ACCESS_GET_TITLE_INFO: + { + 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 */ + + dbg_print ( INPUT_DBG_EVENT, + "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 = 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] ) + { + (*ppp_title)[i] = + vlc_input_title_Duplicate( p_cdda->p_title[i] ); + } + } + break; + } - if( !(p_cddev = ioctl_Open( p_this, psz_source )) ) - { - msg_Warn( p_input, "could not open %s", psz_source ); - free( psz_source ); - return VLC_EGENERIC; - } + 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_title = i; + 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 = cdio_get_track_lsn( p_cdda->p_cdio, i_track ); + } + break; + } - p_cdda = malloc( sizeof(cdda_data_t) ); - if( p_cdda == NULL ) - { - msg_Err( p_input, "out of memory" ); - free( psz_source ); - return VLC_ENOMEM; - } + 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; - p_cdda->p_cddev = p_cddev; - p_cdda->i_debug = config_GetInt( p_this, MODULE_STRING "-debug" ); - p_input->p_access_data = (void *)p_cdda; + case ACCESS_SET_SEEKPOINT: + dbg_print( INPUT_DBG_META, "set seekpoint"); + return VLC_EGENERIC; - dbg_print( (INPUT_DBG_CALL|INPUT_DBG_EXT), "%s", psz_source ); + case ACCESS_SET_PRIVATE_ID_STATE: + dbg_print( INPUT_DBG_META, "set private id state"); + return VLC_EGENERIC; - p_input->i_mtu = CDDA_DATA_ONCE; + default: + msg_Warn( p_access, "unimplemented query in control" ); + return VLC_EGENERIC; - /* We read the Table Of Content information */ - p_cdda->i_nb_tracks = ioctl_GetTracksMap( VLC_OBJECT(p_input), - p_cdda->p_cddev->cdio, &p_cdda->p_sectors ); - if( p_cdda->i_nb_tracks < 0 ) - msg_Err( p_input, "unable to count tracks" ); - else if( p_cdda->i_nb_tracks <= 0 ) - msg_Err( p_input, "no audio tracks found" ); - - if( p_cdda->i_nb_tracks <= 0 ) - { - ioctl_Close( p_cdda->p_cddev ); - free( p_cdda ); - free( psz_source ); - return VLC_EGENERIC; } + return VLC_SUCCESS; +} - if( i_track > p_cdda->i_nb_tracks || i_track < 1 ) - i_track = 1; +/***************************************************************************** + CDDAInit: - /* Set stream and area data */ - vlc_mutex_lock( &p_input->stream.stream_lock ); + Initialize information pertaining to the CD: the number of tracks, + first track number, LSNs for each track and the leadout. The leadout + information is stored after the last track. The LSN array is + 0-origin, same as p_access->info. Add first_track to get what track + number this is on the CD. Note: libcdio uses the real track number. - /* Initialize ES structures */ - input_InitStream( p_input, 0 ); + On input we assume p_cdda->p_cdio and p_cdda->i_track have been set. - /* cdda input method */ - p_input->stream.i_method = INPUT_METHOD_CDDA; + We return the VLC-type status, e.g. VLC_SUCCESS, VLC_ENOMEM, etc. + *****************************************************************************/ +static int CDDAInit( access_t *p_access, cdda_data_t *p_cdda ) +{ + discmode_t discmode = CDIO_DISC_MODE_NO_INFO; - p_input->stream.b_pace_control = 1; - p_input->stream.b_seekable = 1; - p_input->stream.i_mux_rate = 44100 * 4 / 50; + p_cdda->i_tracks = cdio_get_num_tracks( p_cdda->p_cdio ); + p_cdda->i_first_track = cdio_get_first_track_num( p_cdda->p_cdio ); -#define area p_input->stream.pp_areas - for( i = 1 ; i <= p_cdda->i_nb_tracks ; i++ ) + discmode = cdio_get_discmode( p_cdda->p_cdio ); + switch( discmode ) { - input_AddArea( p_input, i, 1 ); - - /* Absolute start offset and size */ - area[i]->i_start = - (off_t)p_cdda->p_sectors[i-1] * (off_t)CDIO_CD_FRAMESIZE_RAW; - area[i]->i_size = - (off_t)(p_cdda->p_sectors[i] - p_cdda->p_sectors[i-1]) - * (off_t)CDIO_CD_FRAMESIZE_RAW; + case CDIO_DISC_MODE_CD_DA: + case CDIO_DISC_MODE_CD_MIXED: + /* These are possible for CD-DA */ + break; + default: + /* These are not possible for CD-DA */ + msg_Err( p_access, + "Disc seems not to be CD-DA. libcdio reports it is %s", + discmode2str[discmode] + ); + return VLC_EGENERIC; } -#undef area - - CDDAPlay( p_input, i_track); - vlc_mutex_unlock( &p_input->stream.stream_lock ); - - CDDAFixupPlayList(p_input, p_cdda, psz_source, play_single_track); - p_input->pf_read = CDDARead; - p_input->pf_seek = CDDASeek; - p_input->pf_set_area = CDDASetArea; - p_input->pf_set_program = CDDASetProgram; - - /* Update default_pts to a suitable value for cdda access */ - var_Create( p_input, MODULE_STRING "-caching", - VLC_VAR_INTEGER | VLC_VAR_DOINHERIT ); - var_Get( p_input, MODULE_STRING "-caching", &val ); - p_input->i_pts_delay = val.i_int * 1000; - - free( psz_source ); - - /* Build a WAV header for the output data */ - memset( &p_cdda->waveheader, 0, sizeof(WAVEHEADER) ); - SetWLE( &p_cdda->waveheader.Format, 1 ); /*WAVE_FORMAT_PCM*/ - SetWLE( &p_cdda->waveheader.BitsPerSample, 16); - p_cdda->waveheader.MainChunkID = VLC_FOURCC('R', 'I', 'F', 'F'); - p_cdda->waveheader.Length = 0; /* we just don't know */ - p_cdda->waveheader.ChunkTypeID = VLC_FOURCC('W', 'A', 'V', 'E'); - p_cdda->waveheader.SubChunkID = VLC_FOURCC('f', 'm', 't', ' '); - SetDWLE( &p_cdda->waveheader.SubChunkLength, 16); - SetWLE( &p_cdda->waveheader.Modus, 2); - SetDWLE( &p_cdda->waveheader.SampleFreq, 44100); - SetWLE( &p_cdda->waveheader.BytesPerSample, - 2 /*Modus*/ * 16 /*BitsPerSample*/ / 8 ); - SetDWLE( &p_cdda->waveheader.BytesPerSec, - 16 /*BytesPerSample*/ * 44100 /*SampleFreq*/ ); - p_cdda->waveheader.DataChunkID = VLC_FOURCC('d', 'a', 't', 'a'); - p_cdda->waveheader.DataLength = 0; /* we just don't know */ - p_cdda->i_header_pos = 0; + /* Set reading start LSN. */ + p_cdda->i_lsn = cdio_get_track_lsn(p_cdda->p_cdio, p_cdda->i_track); return VLC_SUCCESS; } -/***************************************************************************** - * CDDAClose: closes cdda - *****************************************************************************/ -void -E_(Close)( vlc_object_t *p_this ) -{ - input_thread_t * p_input = (input_thread_t *)p_this; - cdda_data_t *p_cdda = (cdda_data_t *)p_input->p_access_data; - - dbg_print( (INPUT_DBG_CALL|INPUT_DBG_EXT), "" ); - ioctl_Close( p_cdda->p_cddev ); - - cdio_log_set_handler (uninit_log_handler); - -#ifdef HAVE_LIBCDDB - cddb_log_set_handler (uninit_log_handler); - if (p_cdda->i_cddb_enabled) - cddb_disc_destroy(p_cdda->cddb.disc); -#endif - - free( p_cdda->p_sectors ); - free( p_cdda ); - p_cdda_input = NULL; -} +/* + * Local variables: + * mode: C + * style: gnu + * End: + */