Commit bea3eda7 authored by Rocky Bernstein's avatar Rocky Bernstein

First attempt at adding CD-Text.

Break out meta-info gathering.
Various fixes and cleanups, but more work is still needed.
Some conceptual work on how things *should* work may be in order.
parent 53dee7b9
......@@ -5,4 +5,6 @@ SOURCES_cddax = \
callback.h \
cdda.c \
cdda.h \
info.c \
info.h \
$(NULL)
......@@ -28,6 +28,7 @@
*****************************************************************************/
#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 <vlc_playlist.h> /* Has to come *after* cdda.h */
#include "vlc_keys.h"
......@@ -54,15 +55,6 @@
# include <unistd.h>
#endif
#ifdef HAVE_ERRNO_H
# include <errno.h>
#endif
#define CDDA_MRL_PREFIX "cddax://"
/* Frequency of sample in bits per second. */
#define CDDA_FREQUENCY_SAMPLE 44100
/* FIXME: This variable is a hack. Would be nice to eliminate. */
access_t *p_cdda_input = NULL;
......@@ -73,22 +65,8 @@ 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 void CDDAMetaInfo( access_t *p_access );
static int CDDAFixupPlaylist( access_t *p_access, cdda_data_t *p_cdda,
const char *psz_source,
vlc_bool_t b_single_track );
static void CDDACreatePlaylistItem(const access_t *p_access,
cdda_data_t *p_cdda,
playlist_t *p_playlist,
track_t i_track,
char *psz_mrl, int psz_mrl_max,
const char *psz_source,
int playlist_operation,
int i_pos);
static int GetCDInfo( access_t *p_access, cdda_data_t *p_cdda ) ;
static int CDDAInit( access_t *p_access, cdda_data_t *p_cdda ) ;
/****************************************************************************
......@@ -211,9 +189,9 @@ CDDAReadBlocks( access_t * p_access )
}
/* Check end of track */
while( p_cdda->i_lsn >= p_cdda->p_lsns[p_access->info.i_title + 1] )
while( p_cdda->i_lsn >= p_cdda->lsn[p_cdda->i_track+1] )
{
if( p_access->info.i_title + 1 >= p_cdda->i_tracks )
if( p_cdda->i_track >= p_cdda->i_first_track + p_cdda->i_titles - 1 )
{
p_access->info.b_eof = VLC_TRUE;
return NULL;
......@@ -228,11 +206,9 @@ CDDAReadBlocks( access_t * p_access )
}
/* Possibly adjust i_blocks so we don't read past the end of a track. */
if( p_cdda->i_lsn + i_blocks >=
p_cdda->p_lsns[p_access->info.i_title + 1] )
if( p_cdda->i_lsn + i_blocks >= p_cdda->lsn[p_cdda->i_track+1] )
{
i_blocks = p_cdda->p_lsns[p_access->info.i_title + 1 ] -
p_cdda->i_lsn;
i_blocks = p_cdda->lsn[p_cdda->i_track+1 ] - p_cdda->i_lsn;
}
/* Do the actual reading */
......@@ -260,7 +236,7 @@ CDDAReadBlocks( access_t * p_access )
return NULL;
}
p_cdda->i_lsn += i_blocks;
p_cdda->i_lsn += i_blocks;
p_access->info.i_pos += p_block->i_buffer;
return p_block;
......@@ -275,7 +251,7 @@ 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->p_lsns[p_access->info.i_title]
p_cdda->i_lsn = p_cdda->lsn[p_cdda->i_track]
+ (i_pos / CDIO_CD_FRAMESIZE_RAW);
p_access->info.i_pos = i_pos;
......@@ -285,576 +261,6 @@ CDDASeek( access_t * p_access, int64_t i_pos )
return VLC_SUCCESS;
}
#ifdef HAVE_LIBCDDB
#define free_and_dup(var, val) \
if (var) free(var); \
if (val) var=strdup(val);
static void
GetCDDBInfo( access_t *p_access, cdda_data_t *p_cdda )
{
dbg_print( (INPUT_DBG_CALL|INPUT_DBG_EXT), "" );
if (config_GetInt( p_access, MODULE_STRING "-cddb-enabled" )) {
int i, i_matches;
cddb_conn_t *conn = cddb_new();
const CdIo *p_cdio = p_cdda->p_cdio;
cddb_log_set_handler (uninit_log_handler);
if (!conn) {
msg_Warn( p_access, "Unable to initialize libcddb" );
goto cddb_destroy;
}
cddb_set_email_address( conn,
config_GetPsz( p_access,
MODULE_STRING "-cddb-email") );
cddb_set_server_name( conn,
config_GetPsz( p_access,
MODULE_STRING "-cddb-server") );
cddb_set_server_port(conn,
config_GetInt( p_access,
MODULE_STRING "-cddb-port") );
/* Set the location of the local CDDB cache directory.
The default location of this directory is */
if (!config_GetInt( p_access, MODULE_STRING "-cddb-enable-cache" ))
cddb_cache_disable(conn);
cddb_cache_set_dir(conn,
config_GetPsz( p_access,
MODULE_STRING "-cddb-cachedir") );
cddb_set_timeout(conn,
config_GetInt( p_access, MODULE_STRING "-cddb-timeout") );
if (config_GetInt( p_access, MODULE_STRING "-cddb-httpd" )) {
cddb_http_enable(conn);
} else
cddb_http_disable(conn);
p_cdda->cddb.disc = cddb_disc_new();
if (!p_cdda->cddb.disc) {
msg_Err( p_access, "Unable to create CDDB disc structure." );
goto cddb_end;
}
p_cdda->psz_mcn = cdio_get_mcn(p_cdio);
for(i = 1; i <= p_cdda->i_tracks; i++) {
cddb_track_t *t = cddb_track_new();
t->frame_offset = cdio_get_track_lba(p_cdio, i);
cddb_disc_add_track(p_cdda->cddb.disc, t);
}
p_cdda->cddb.disc->length =
cdio_get_track_lba(p_cdio, CDIO_CDROM_LEADOUT_TRACK)
/ CDIO_CD_FRAMES_PER_SEC;
if (!cddb_disc_calc_discid(p_cdda->cddb.disc)) {
msg_Err( p_access, "CDDB disc ID calculation failed" );
goto cddb_destroy;
}
i_matches = cddb_query(conn, p_cdda->cddb.disc);
if (i_matches > 0) {
if (i_matches > 1)
msg_Warn( p_access, "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);
} else {
msg_Warn( p_access, "CDDB error: %s", cddb_error_str(errno));
}
cddb_destroy:
cddb_destroy(conn);
}
cddb_end: ;
}
#endif /*HAVE_LIBCDDB*/
#define add_meta_val(FIELD, VLC_META, VAL) \
if ( p_cdda->p_meta && VAL) { \
vlc_meta_Add( p_cdda->p_meta, VLC_META, VAL ); \
dbg_print( INPUT_DBG_META, "field %s: %s\n", VLC_META, VAL ); \
} \
#define add_cddb_meta(FIELD, VLC_META) \
add_meta_val(FIELD, VLC_META, p_cdda->cddb.disc->FIELD);
#define add_cddb_meta_fmt(FIELD, FORMAT_SPEC, VLC_META) \
{ \
char psz_buf[100]; \
snprintf( psz_buf, sizeof(psz_buf)-1, FORMAT_SPEC, \
p_cdda->cddb.disc->FIELD ); \
psz_buf[sizeof(psz_buf)-1] = '\0'; \
add_meta_val(FIELD, VLC_META, psz_buf); \
}
/*
Gets and saves CDDA Meta Information. In the Control routine,
we handle Meta Information requests and basically copy what we've
saved here.
*/
static void CDDAMetaInfo( access_t *p_access )
{
cdda_data_t *p_cdda = (cdda_data_t *) p_access->p_sys;
#ifdef HAVE_LIBCDDB
if ( p_cdda && p_cdda->i_cddb_enabled ) {
GetCDDBInfo(p_access, p_cdda);
if ( p_cdda->cddb.disc ) {
p_cdda->p_meta = vlc_meta_New();
add_cddb_meta(title, VLC_META_CDDB_TITLE);
add_cddb_meta(artist, VLC_META_CDDB_ARTIST);
add_cddb_meta(genre, VLC_META_CDDB_GENRE);
add_cddb_meta(ext_data, VLC_META_CDDB_EXT_DATA);
add_cddb_meta_fmt(year, "%d", VLC_META_CDDB_YEAR);
add_cddb_meta_fmt(discid, "%x", VLC_META_CDDB_DISCID);
}
}
#endif /*HAVE_LIBCDDB*/
#define TITLE_MAX 30
/* Adds a string-valued entry to the stream and media information if
the string is not null or the null string.
*/
#define add_cddb_info_str(CATEGORY, TITLE, FIELD) \
if (FIELD && strlen(FIELD)) { \
input_Control( p_cdda->p_input, INPUT_ADD_INFO, CATEGORY, \
_(TITLE), "%s", FIELD ); \
}
/* Adds a numeric-valued entry to the stream and media information */
#define add_cddb_info_val(CATEGORY, TITLE, FMT, FIELD) \
if (FIELD) { \
input_Control( p_cdda->p_input, INPUT_ADD_INFO, CATEGORY, \
_(TITLE), FMT, FIELD ); \
}
#define add_cddb_disc_info_str(TITLE, FIELD) \
add_cddb_info_str("Disc", TITLE, p_cdda->cddb.disc->FIELD)
#define add_cddb_disc_info_val(TITLE, FMT, FIELD) \
add_cddb_info_val("Disc", TITLE, FMT, p_cdda->cddb.disc->FIELD)
/*#if UPDATE_TRACK_INFORMATION_FINISHED*/
#if 1
{
track_t i_track = p_cdda->i_tracks;
char psz_buffer[MSTRTIME_MAX_SIZE];
mtime_t i_duration =
(p_cdda->p_lsns[i_track] - p_cdda->p_lsns[0])
/ CDIO_CD_FRAMES_PER_SEC;
dbg_print( INPUT_DBG_META, "Duration %ld", (long int) i_duration );
input_Control( p_cdda->p_input, INPUT_ADD_INFO,
_("Disc"), _("Duration"), "%s",
secstotimestr( psz_buffer, i_duration ) );
#ifdef HAVE_LIBCDDB
if (p_cdda->i_cddb_enabled && p_cdda->cddb.disc) {
add_cddb_disc_info_str("Artist (CDDB)", artist);
add_cddb_disc_info_str("Genre (CDDB)", genre);
add_cddb_disc_info_str("Extended Data (CDDB)", ext_data);
add_cddb_disc_info_val("Year (CDDB)", "%d", year);
add_cddb_disc_info_val("Disc ID (CDDB)", "%x", discid);
add_cddb_disc_info_str("Title (CDDB)", title);
add_cddb_info_str("Disc", "Category (CDDB)",
CDDB_CATEGORY[p_cdda->cddb.disc->category]);
}
#endif /*HAVE_LIBCDDB*/
for( i_track = 0 ; i_track < p_cdda->i_tracks ; i_track++ ) {
char track_str[TITLE_MAX];
mtime_t i_duration =
(p_cdda->p_lsns[i_track+1] - p_cdda->p_lsns[i_track])
/ CDIO_CD_FRAMES_PER_SEC;
snprintf(track_str, TITLE_MAX, "%s %02d", _("Track"), i_track+1);
input_Control( p_cdda->p_input, INPUT_ADD_INFO, track_str,
_("Duration"), "%s",
secstotimestr( psz_buffer, i_duration ) );
#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) {
add_cddb_info_str(track_str, "Artist (CDDB)", t->artist);
add_cddb_info_str(track_str, "Title (CDDB)", t->title);
add_cddb_info_str(track_str, "Extended Data (CDDB)", t->ext_data);
}
}
#endif /*HAVE_LIBCDDB*/
}
}
#endif /* UPDATE_TRACK_INFORMATION_FINISHED */
}
#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 access_t *p_access, 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;
vlc_bool_t saw_control_prefix = false;
size_t format_len = strlen(format_str);
memset(temp_str, 0, TEMP_STR_SIZE);
for (i=0; i<format_len; i++) {
if (!saw_control_prefix && format_str[i] != '%') {
*tp++ = format_str[i];
saw_control_prefix = false;
continue;
}
switch(format_str[i]) {
case '%':
if (saw_control_prefix) {
*tp++ = '%';
}
saw_control_prefix = !saw_control_prefix;
break;
#ifdef HAVE_LIBCDDB
case 'a':
if (!p_cdda->i_cddb_enabled) goto not_special;
if (p_cdda->cddb.disc)
add_format_str_info(p_cdda->cddb.disc->artist);
break;
case 'A':
if (!p_cdda->i_cddb_enabled) goto not_special;
if (p_cdda->cddb.disc)
add_format_str_info(p_cdda->cddb.disc->title);
break;
case 'C':
if (!p_cdda->i_cddb_enabled) goto not_special;
if (p_cdda->cddb.disc)
add_format_str_info(CDDB_CATEGORY[p_cdda->cddb.disc->category]);
break;
case 'G':
if (!p_cdda->i_cddb_enabled) goto not_special;
if (p_cdda->cddb.disc)
add_format_str_info(p_cdda->cddb.disc->genre);
break;
case 'I':
if (!p_cdda->i_cddb_enabled) goto not_special;
if (p_cdda->cddb.disc)
add_format_num_info(p_cdda->cddb.disc->discid, "%x");
break;
case 'Y':
if (!p_cdda->i_cddb_enabled) goto not_special;
if (p_cdda->cddb.disc)
add_format_num_info(p_cdda->cddb.disc->year, "%5d");
break;
case 't':
if (p_cdda && p_cdda->i_cddb_enabled && p_cdda->cddb.disc) {
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 && p_cdda->cddb.disc) {
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 && p_cdda->cddb.disc) {
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;
#endif
case 'M':
add_format_str_info(mrl);
break;
case 'm':
add_format_str_info(p_cdda->psz_mcn);
break;
case 'n':
add_format_num_info(p_cdda->i_tracks, "%d");
break;
case 's':
if (p_cdda->i_cddb_enabled) {
char psz_buffer[MSTRTIME_MAX_SIZE];
mtime_t i_duration =
(p_cdda->p_lsns[i_track] - p_cdda->p_lsns[i_track-1])
/ CDIO_CD_FRAMES_PER_SEC;
add_format_str_info(secstotimestr( psz_buffer, i_duration ) );
} else goto not_special;
break;
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;
}
}
return strdup(temp_str);
}
/* Adds a string-valued entry to the playlist information under "Disc"
if the string is not null or the null string.
*/
#define add_playlist_disc_info_str(TITLE, FIELD) \
if (FIELD && strlen(FIELD)) { \
playlist_ItemAddInfo( p_item, _("Disc"), _(TITLE), \
"%s", FIELD); \
}
static void
CDDACreatePlaylistItem(const access_t *p_access, cdda_data_t *p_cdda,
playlist_t *p_playlist, track_t 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_lsns[i_track] - p_cdda->p_lsns[i_track-1])
* (1000000 / CDIO_CD_FRAMES_PER_SEC) ;
char *psz_disc_info;
char *psz_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);
psz_title = CDDAFormatStr(p_access, p_cdda,
config_GetPsz( p_access, config_varname ),
psz_mrl, i_track);
dbg_print( INPUT_DBG_META, "mrl: %s, title: %s, duration, %ld, pos %d",
psz_mrl, psz_title, (long int) i_duration / 1000000 , i_pos );
playlist_AddExt( p_playlist, psz_mrl, psz_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 );
psz_disc_info =
CDDAFormatStr( p_access, p_cdda,
config_GetPsz( p_access, MODULE_STRING "-title-format" ),
psz_mrl, i_track );
playlist_ItemAddInfo( p_item , _("Disc"),_("Title"), psz_disc_info);
#ifdef HAVE_LIBCDDB
if (p_cdda->i_cddb_enabled) {
const char *psz_general_cat = _("Disc");
cddb_track_t *t=cddb_disc_get_track(p_cdda->cddb.disc, i_track-1);
add_playlist_disc_info_str("Disc Album (CDDB)",
p_cdda->cddb.disc->title);
add_playlist_disc_info_str("Disc Artist(s) (CDDB)",
p_cdda->cddb.disc->artist);
add_playlist_disc_info_str("Disc Category (CDDB)",
CDDB_CATEGORY[p_cdda->cddb.disc->category]);
add_playlist_disc_info_str("Disc Genre (CDDB)",
p_cdda->cddb.disc->genre);
if ( p_cdda->cddb.disc->discid ) {
playlist_ItemAddInfo( p_item, psz_general_cat, _("Disc ID (CDDB)"),
"%x", p_cdda->cddb.disc->discid );
}
if (p_cdda->cddb.disc->year != 0) {
playlist_ItemAddInfo( p_item, psz_general_cat,
_("Year"), "%5d", p_cdda->cddb.disc->year );
}
if (t) {
if (t->artist)
add_playlist_disc_info_str("Track Artist (CDDB)",
t->artist);
if (t->title)
add_playlist_disc_info_str("Track Title (CDDB)",
t->title);
}
}
#endif /*HAVE_LIBCDDB*/
vlc_mutex_unlock( &p_item->input.lock );
}
static int
CDDAFixupPlaylist( access_t *p_access, cdda_data_t *p_cdda,
const char *psz_source, vlc_bool_t b_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;
#ifdef HAVE_LIBCDDB
p_cdda->i_cddb_enabled =
config_GetInt( p_access, MODULE_STRING "-cddb-enabled" );
if( b_single_track && !p_cdda->i_cddb_enabled ) return VLC_SUCCESS;
#else
if( b_single_track ) return VLC_SUCCESS;
#endif
psz_mrl = malloc( psz_mrl_max );
if( psz_mrl == NULL )
{
msg_Warn( p_access, "out of memory" );
return VLC_ENOMEM;
}
p_playlist = (playlist_t *) vlc_object_find( p_access, VLC_OBJECT_PLAYLIST,
FIND_ANYWHERE );
if( !p_playlist )
{
msg_Warn( p_access, "can't find playlist" );
free(psz_mrl);
return VLC_EGENERIC;
}
CDDAMetaInfo(p_access);
if (b_single_track) {
/* May fill out more information when the playlist user interface becomes
more mature.
*/
track_t i_track = p_cdda->i_track;
input_title_t *t = p_cdda->p_title[i_track-1] = vlc_input_title_New();
asprintf( &t->psz_name, _("Track %i"), i_track );
t->i_size = ( p_cdda->p_lsns[i_track] - p_cdda->p_lsns[i_track-1] ) *
(int64_t)CDIO_CD_FRAMESIZE_RAW;