1
0
mirror of http://mpg123.de/trunk/.git synced 2025-07-31 13:24:22 +03:00

Some things relating to ID3v2 handling:

- the named entries for artist, album, etc in struct mpg123_id3v2 are now pointers...
	- ... into the new arrays of ID3v2 text, comment and extra (TXXX) data
	- That makes a lot more info directly accessible; which id3dump now does access
	- adapted id3print.c to the pointer thing (by dropping some & ;-)
	- mpg123_copy_string() now produces an empty string (_not_ "") when copying from NULL

id3.c got a bit of structure, but the main parsing function is still to bloated.
In general, I hope this change of ID3v2 handling makes sense.
You have more data plus the possibility to easily loop through the entries...



git-svn-id: svn://scm.orgis.org/mpg123/trunk@1205 35dc7657-300d-0410-a2e5-dc2837fedb53
This commit is contained in:
thor
2007-12-01 21:42:44 +00:00
parent 57fe26c8e9
commit 56ed6fb018
7 changed files with 255 additions and 136 deletions

View File

@ -87,18 +87,56 @@ void print_v2(mpg123_id3v2 *v2)
int i; int i;
const char *names[] = { "Title", "Artist", "Album", "Year", "Comment", "Genre" }; const char *names[] = { "Title", "Artist", "Album", "Year", "Comment", "Genre" };
mpg123_string *sources[sizeof(names)/sizeof(char*)]; mpg123_string *sources[sizeof(names)/sizeof(char*)];
sources[0] = &v2->title; sources[0] = v2->title;
sources[1] = &v2->artist; sources[1] = v2->artist;
sources[2] = &v2->album; sources[2] = v2->album;
sources[3] = &v2->year; sources[3] = v2->year;
sources[4] = v2->generic_comment; sources[4] = v2->comment;
sources[5] = &v2->genre; sources[5] = v2->genre;
printf("title = %p\n", (void*)v2->title);
for(i=0; i<V1FIELDS; ++i) for(i=0; i<V1FIELDS; ++i)
{ {
print_lines(names[i], sources[i]); print_lines(names[i], sources[i]);
} }
} }
void print_raw_v2(mpg123_id3v2 *v2)
{
size_t i;
for(i=0; i<v2->texts; ++i)
{
char id[5];
memcpy(id, v2->text[i].id, 4);
id[4] = 0;
printf("%p %s\n", (void*)(&v2->text[i].text), id);
print_lines("", &v2->text[i].text);
}
for(i=0; i<v2->extras; ++i)
{
char id[5];
memcpy(id, v2->extra[i].id, 4);
id[4] = 0;
printf( "%s description(%s)\n",
id,
v2->extra[i].description.fill ? v2->extra[i].description.p : "" );
print_lines("", &v2->extra[i].text);
}
for(i=0; i<v2->comments; ++i)
{
char id[5];
char lang[3];
memcpy(id, v2->comment_list[i].id, 4);
id[4] = 0;
memcpy(lang, v2->comment_list[i].lang, 3);
lang[3] = 0;
printf( "%s description(%s) language(%s): \n",
id,
v2->comment_list[i].description.fill ? v2->comment_list[i].description.p : "",
lang );
print_lines("", &v2->comment_list[i].text);
}
}
int main(int argc, char **argv) int main(int argc, char **argv)
{ {
int i; int i;
@ -112,7 +150,7 @@ int main(int argc, char **argv)
mpg123_init(); mpg123_init();
m = mpg123_new(NULL, NULL); m = mpg123_new(NULL, NULL);
mpg123_param(m, MPG123_VERBOSE, 4, 0); mpg123_param(m, MPG123_VERBOSE, 4, 0);
for(i=1; i < argc; ++i) for(i=1; i < argc; ++i)
{ {
mpg123_id3v1 *v1; mpg123_id3v1 *v1;
@ -128,11 +166,14 @@ mpg123_param(m, MPG123_VERBOSE, 4, 0);
if(meta & MPG123_ID3 && mpg123_id3(m, &v1, &v2) == MPG123_OK) if(meta & MPG123_ID3 && mpg123_id3(m, &v1, &v2) == MPG123_OK)
{ {
printf("Tag data on %s:\n", argv[i]); printf("Tag data on %s:\n", argv[i]);
printf("\n==== ID3v1 ====\n"); printf("\n==== ID3v1 ====\n");
if(v1 != NULL) print_v1(v1); if(v1 != NULL) print_v1(v1);
printf("\n==== ID3v2 ====\n"); printf("\n==== ID3v2 ====\n");
if(v2 != NULL) print_v2(v2); if(v2 != NULL) print_v2(v2);
printf("\n==== ID3v2 Raw frames ====\n");
if(v2 != NULL) print_raw_v2(v2);
} }
else printf("Nothing found for %s.\n", argv[i]); else printf("Nothing found for %s.\n", argv[i]);

View File

@ -33,12 +33,12 @@ void print_id3_tag(mpg123_handle *mh, int long_id3, FILE *out)
if(v1 == NULL && v2 == NULL) return; if(v1 == NULL && v2 == NULL) return;
if(v2 != NULL) /* fill from ID3v2 data */ if(v2 != NULL) /* fill from ID3v2 data */
{ {
transform(&tag[TITLE], &v2->title); transform(&tag[TITLE], v2->title);
transform(&tag[ARTIST], &v2->artist); transform(&tag[ARTIST], v2->artist);
transform(&tag[ALBUM], &v2->album); transform(&tag[ALBUM], v2->album);
transform(&tag[COMMENT], v2->generic_comment); transform(&tag[COMMENT], v2->comment);
transform(&tag[YEAR], &v2->year); transform(&tag[YEAR], v2->year);
transform(&tag[GENRE], &v2->genre); transform(&tag[GENRE], v2->genre);
} }
if(v1 != NULL) /* fill gaps with ID3v1 data */ if(v1 != NULL) /* fill gaps with ID3v1 data */
{ {

View File

@ -34,80 +34,131 @@ const int encoding_widths[4] = { 1, 2, 2, 1 };
void init_id3(mpg123_handle *fr) void init_id3(mpg123_handle *fr)
{ {
fr->id3v2.version = 0; /* nothing there */ fr->id3v2.version = 0; /* nothing there */
mpg123_init_string(&fr->id3v2.title); fr->id3v2.title = NULL;
mpg123_init_string(&fr->id3v2.artist); fr->id3v2.artist = NULL;
mpg123_init_string(&fr->id3v2.album); fr->id3v2.album = NULL;
mpg123_init_string(&fr->id3v2.year); fr->id3v2.year = NULL;
mpg123_init_string(&fr->id3v2.genre); fr->id3v2.genre = NULL;
fr->id3v2.comments = 0; fr->id3v2.comments = 0;
fr->id3v2.comment = NULL; fr->id3v2.comment_list = NULL;
fr->id3v2.generic_comment = NULL; fr->id3v2.texts = 0;
fr->id3v2.text = NULL;
fr->id3v2.extras = 0;
fr->id3v2.extra = NULL;
} }
static void free_comment(mpg123_handle *mh) /* Managing of the text, comment and extra lists. */
/* Initialize one element. */
static void init_mpg123_text(mpg123_text *txt)
{
mpg123_init_string(&txt->text);
mpg123_init_string(&txt->description);
txt->id[0] = 0;
txt->id[1] = 0;
txt->id[2] = 0;
txt->id[3] = 0;
txt->lang[0] = 0;
txt->lang[1] = 0;
txt->lang[2] = 0;
}
/* Free memory of one element. */
static void free_mpg123_text(mpg123_text *txt)
{
mpg123_free_string(&txt->text);
mpg123_init_string(&txt->description);
}
/* Free memory of whole list. */
#define free_comment(mh) free_id3_text(&((mh)->id3v2.comment_list), &((mh)->id3v2.comments))
#define free_text(mh) free_id3_text(&((mh)->id3v2.text), &((mh)->id3v2.texts))
#define free_extra(mh) free_id3_text(&((mh)->id3v2.extra), &((mh)->id3v2.extras))
static void free_id3_text(mpg123_text **list, size_t *size)
{ {
size_t i; size_t i;
mpg123_id3v2 *id = &mh->id3v2; for(i=0; i<*size; ++i) free_mpg123_text(&((*list)[i]));
for(i=0; i<id->comments; ++i)
{ free(*list);
mpg123_free_string(&id->comment[i].description); *list = NULL;
mpg123_free_string(&id->comment[i].text); *size = 0;
}
free(id->comment);
id->comment = NULL;
id->comments = 0;
id->generic_comment = NULL;
} }
static mpg123_comment *add_comment(mpg123_handle *mh) /* Add items to the list. */
#define add_comment(mh) add_id3_text(&((mh)->id3v2.comment_list), &((mh)->id3v2.comments))
#define add_text(mh) add_id3_text(&((mh)->id3v2.text), &((mh)->id3v2.texts))
#define add_extra(mh) add_id3_text(&((mh)->id3v2.extra), &((mh)->id3v2.extras))
static mpg123_text *add_id3_text(mpg123_text **list, size_t *size)
{ {
mpg123_id3v2 *id = &mh->id3v2; mpg123_text *x = safe_realloc(*list, sizeof(mpg123_text)*(*size+1));
mpg123_comment *x = safe_realloc(id->comment, sizeof(mpg123_comment)*(id->comments+1));
if(x == NULL) return NULL; /* bad */ if(x == NULL) return NULL; /* bad */
id->comment = x; *list = x;
id->comments += 1; *size += 1;
id->comment[id->comments-1].lang[0] = 0; /* empty... */ init_mpg123_text(&((*list)[*size-1]));
mpg123_init_string(&id->comment[id->comments-1].description);
mpg123_init_string(&id->comment[id->comments-1].text); return &((*list)[*size-1]); /* Return pointer to the added text. */
return &id->comment[id->comments-1]; /* Return pointer to the added comment. */
} }
static void pop_comment(mpg123_handle *mh) /* Remove the last item. */
#define pop_comment(mh) pop_id3_text(&((mh)->id3v2.comment_list), &((mh)->id3v2.comments))
#define pop_text(mh) pop_id3_text(&((mh)->id3v2.text), &((mh)->id3v2.texts))
#define pop_extra(mh) pop_id3_text(&((mh)->id3v2.extra), &((mh)->id3v2.extras))
static void pop_id3_text(mpg123_text **list, size_t *size)
{ {
mpg123_comment *x; mpg123_text *x;
mpg123_id3v2 *id = &mh->id3v2; if(*size < 1) return;
if(id->comments < 1) return;
mpg123_free_string(&id->comment[id->comments-1].description); free_mpg123_text(&((*list)[*size-1]));
mpg123_free_string(&id->comment[id->comments-1].text); if(*size > 1)
x = safe_realloc(id->comment, sizeof(mpg123_comment)*(id->comments-1));
if(x != NULL)
{ {
id->comment = x; x = safe_realloc(*list, sizeof(mpg123_text)*(*size-1));
id->comments -= 1; if(x != NULL){ *list = x; *size -= 1; }
}
else
{
free(*list);
*list = NULL;
*size = 0;
} }
} }
/* OK, back t the higher level functions. */
void exit_id3(mpg123_handle *fr) void exit_id3(mpg123_handle *fr)
{ {
mpg123_free_string(&fr->id3v2.title);
mpg123_free_string(&fr->id3v2.artist);
mpg123_free_string(&fr->id3v2.album);
mpg123_free_string(&fr->id3v2.year);
mpg123_free_string(&fr->id3v2.genre);
free_comment(fr); free_comment(fr);
free_extra(fr);
free_text(fr);
} }
void reset_id3(mpg123_handle *fr) void reset_id3(mpg123_handle *fr)
{ {
fr->id3v2.version = 0; exit_id3(fr);
fr->id3v2.title.fill = 0; init_id3(fr);
fr->id3v2.artist.fill = 0; }
fr->id3v2.album.fill = 0;
fr->id3v2.year.fill = 0; /* Set the id3v2.artist id3v2.title ... links to elements of the array. */
fr->id3v2.genre.fill = 0; void id3_link(mpg123_handle *fr)
free_comment(fr); {
size_t i;
mpg123_id3v2 *v2 = &fr->id3v2;
for(i=0; i<v2->texts; ++i)
{
mpg123_text *entry = &v2->text[i];
fprintf(stderr, "entry %i id %c%c%c%c at %p\n", i, entry->id[0], entry->id[1], entry->id[2], entry->id[3], &entry->text);
if (!strncmp("TIT2", entry->id, 4)) v2->title = &entry->text;
else if(!strncmp("TALB", entry->id, 4)) v2->album = &entry->text;
else if(!strncmp("TPE1", entry->id, 4)) v2->artist = &entry->text;
else if(!strncmp("TYER", entry->id, 4)) v2->year = &entry->text;
else if(!strncmp("TCON", entry->id, 4)) v2->genre = &entry->text;
}
for(i=0; i<v2->comments; ++i)
{
mpg123_text *entry = &v2->comment_list[i];
if(entry->description.fill == 0 || entry->description.p[0] == 0)
v2->comment = &entry->text;
}
} }
/* /*
@ -183,8 +234,23 @@ char *next_text(char* prev, int encoding, size_t limit)
return text; return text;
} }
static void process_text(mpg123_handle *fr, char *realdata, size_t realsize, char *id)
{
/* Text encoding $xx */
/* The text (encoded) ... */
mpg123_text *t = add_text(fr);
if(t == NULL)
{
if(NOQUIET) error("Unable to attach new text!");
return;
}
memcpy(t->id, id, 4);
store_id3_text(&t->text, realdata, realsize);
if(VERBOSE4) fprintf(stderr, "Note: ID3v2 %c%c%c%c text frame: %s\n", id[0], id[1], id[2], id[3], t->text.p);
}
/* Store a new comment that perhaps is a RVA / RVA_ALBUM/AUDIOPHILE / RVA_MIX/RADIO one */ /* Store a new comment that perhaps is a RVA / RVA_ALBUM/AUDIOPHILE / RVA_MIX/RADIO one */
static void process_comment(mpg123_handle *fr, char *realdata, size_t realsize, int tt) static void process_comment(mpg123_handle *fr, char *realdata, size_t realsize, int rva_level, char *id)
{ {
/* Text encoding $xx */ /* Text encoding $xx */
/* Language $xx xx xx */ /* Language $xx xx xx */
@ -194,13 +260,14 @@ static void process_comment(mpg123_handle *fr, char *realdata, size_t realsize,
char *lang = realdata+1; /* I'll only use the 3 bytes! */ char *lang = realdata+1; /* I'll only use the 3 bytes! */
char *descr = realdata+4; char *descr = realdata+4;
char *text; char *text;
mpg123_comment *xcom = add_comment(fr); mpg123_text *xcom = add_comment(fr);
if(xcom == NULL) if(xcom == NULL)
{ {
if(NOQUIET) error("Unable to attach new comment!"); if(NOQUIET) error("Unable to attach new comment!");
return; return;
} }
memcpy(xcom->lang, lang, 3); memcpy(xcom->lang, lang, 3);
memcpy(xcom->id, id, 4);
xcom->lang[3] = 0; xcom->lang[3] = 0;
/* Now I can abuse a byte from lang for the encoding. */ /* Now I can abuse a byte from lang for the encoding. */
descr[-1] = encoding; descr[-1] = encoding;
@ -233,76 +300,79 @@ static void process_comment(mpg123_handle *fr, char *realdata, size_t realsize,
|| !strcasecmp(xcom->description.p, "rva_audiophile") || !strcasecmp(xcom->description.p, "rva_audiophile")
|| !strcasecmp(xcom->description.p, "rva_user")) || !strcasecmp(xcom->description.p, "rva_user"))
rva_mode = 1; rva_mode = 1;
if((rva_mode > -1) && (fr->rva.level[rva_mode] <= tt+1)) if((rva_mode > -1) && (fr->rva.level[rva_mode] <= rva_level))
{ {
fr->rva.gain[rva_mode] = atof(xcom->text.p); fr->rva.gain[rva_mode] = atof(xcom->text.p);
if(VERBOSE3) fprintf(stderr, "Note: RVA value %fdB\n", fr->rva.gain[rva_mode]); if(VERBOSE3) fprintf(stderr, "Note: RVA value %fdB\n", fr->rva.gain[rva_mode]);
fr->rva.peak[rva_mode] = 0; fr->rva.peak[rva_mode] = 0;
fr->rva.level[rva_mode] = tt+1; fr->rva.level[rva_mode] = rva_level;
} }
} }
/* Mark the last found generic comment. */
if(xcom->description.fill == 0 || xcom->description.p[0] == 0)
fr->id3v2.generic_comment = &xcom->text;
} }
void process_extra(mpg123_handle *fr, char* realdata, size_t realsize, int tt) void process_extra(mpg123_handle *fr, char* realdata, size_t realsize, int rva_level, char *id)
{ {
/* Text encoding $xx */ /* Text encoding $xx */
/* Description ... $00 (00) */ /* Description ... $00 (00) */
/* Text ... */ /* Text ... */
mpg123_string work;
char encoding = realdata[0]; char encoding = realdata[0];
char *descr = realdata+1; /* remember, the encoding is descr[-1] */ char *descr = realdata+1; /* remember, the encoding is descr[-1] */
char *text = next_text(descr, encoding, realsize-(descr-realdata)); char *text;
mpg123_text *xex;
text = next_text(descr, encoding, realsize-(descr-realdata));
if(text == NULL) if(text == NULL)
{ {
if(NOQUIET) error("No extra frame text / valid description?"); if(NOQUIET) error("No extra frame text / valid description?");
return; return;
} }
mpg123_init_string(&work); xex = add_extra(fr);
store_id3_text(&work, descr-1, text-descr+1); if(xex == NULL)
if(work.fill > 0) {
if(NOQUIET) error("Unable to attach new extra text!");
return;
}
memcpy(xex->id, id, 4);
store_id3_text(&xex->description, descr-1, text-descr+1);
text[-1] = encoding;
store_id3_text(&xex->text, text-1, realsize-(text-realdata)+1);
if(xex->description.fill > 0)
{ {
int is_peak = 0; int is_peak = 0;
int rva_mode = -1; /* mix / album */ int rva_mode = -1; /* mix / album */
if(!strncasecmp(work.p, "replaygain_track_",17)) if(!strncasecmp(xex->description.p, "replaygain_track_",17))
{ {
debug("ID3v2: track gain/peak"); debug("ID3v2: track gain/peak");
rva_mode = 0; rva_mode = 0;
if(!strcasecmp(work.p, "replaygain_track_peak")) is_peak = 1; if(!strcasecmp(xex->description.p, "replaygain_track_peak")) is_peak = 1;
else if(strcasecmp(work.p, "replaygain_track_gain")) rva_mode = -1; else if(strcasecmp(xex->description.p, "replaygain_track_gain")) rva_mode = -1;
} }
else else
if(!strncasecmp(work.p, "replaygain_album_",17)) if(!strncasecmp(xex->description.p, "replaygain_album_",17))
{ {
debug("ID3v2: album gain/peak"); debug("ID3v2: album gain/peak");
rva_mode = 1; rva_mode = 1;
if(!strcasecmp(work.p, "replaygain_album_peak")) is_peak = 1; if(!strcasecmp(xex->description.p, "replaygain_album_peak")) is_peak = 1;
else if(strcasecmp(work.p, "replaygain_album_gain")) rva_mode = -1; else if(strcasecmp(xex->description.p, "replaygain_album_gain")) rva_mode = -1;
} }
if((rva_mode > -1) && (fr->rva.level[rva_mode] <= tt+1)) if((rva_mode > -1) && (fr->rva.level[rva_mode] <= rva_level))
{ {
text[-1] = encoding; if(xex->text.fill > 0)
store_id3_text(&work, text-1, realsize-(text-realdata)+1);
if(work.fill > 0)
{ {
if(is_peak) if(is_peak)
{ {
fr->rva.peak[rva_mode] = atof(work.p); fr->rva.peak[rva_mode] = atof(xex->text.p);
if(VERBOSE3) fprintf(stderr, "Note: RVA peak %fdB\n", fr->rva.peak[rva_mode]); if(VERBOSE3) fprintf(stderr, "Note: RVA peak %fdB\n", fr->rva.peak[rva_mode]);
} }
else else
{ {
fr->rva.gain[rva_mode] = atof(work.p); fr->rva.gain[rva_mode] = atof(xex->text.p);
if(VERBOSE3) fprintf(stderr, "Note: RVA gain %fdB\n", fr->rva.gain[rva_mode]); if(VERBOSE3) fprintf(stderr, "Note: RVA gain %fdB\n", fr->rva.gain[rva_mode]);
} }
fr->rva.level[rva_mode] = tt+1; fr->rva.level[rva_mode] = rva_level;
} }
} }
} }
mpg123_free_string(&work);
} }
/* /*
@ -401,9 +471,9 @@ int parse_new_id3(mpg123_handle *fr, unsigned long first4bytes)
unsigned long pos = tagpos; unsigned long pos = tagpos;
/* level 1,2,3 - 0 is info from lame/info tag! */ /* level 1,2,3 - 0 is info from lame/info tag! */
/* rva tags with ascending significance, then general frames */ /* rva tags with ascending significance, then general frames */
#define KNOWN_FRAMES 8 #define KNOWN_FRAMES 3
const char frame_type[KNOWN_FRAMES][5] = { "COMM", "TXXX", "RVA2", "TPE1", "TALB", "TIT2", "TYER", "TCON" }; const char frame_type[KNOWN_FRAMES][5] = { "COMM", "TXXX", "RVA2" }; /* plus all text frames... */
enum { egal = -1, comment, extra, rva2, artist, album, title, year, genre } tt = egal; enum { unknown = -2, text = -1, comment, extra, rva2 } tt = unknown;
/* we may have entered the padding zone or any other strangeness: check if we have valid frame id characters */ /* we may have entered the padding zone or any other strangeness: check if we have valid frame id characters */
for(; i< 4; ++i) if( !( ((tagdata[tagpos+i] > 47) && (tagdata[tagpos+i] < 58)) for(; i< 4; ++i) if( !( ((tagdata[tagpos+i] > 47) && (tagdata[tagpos+i] < 58))
|| ((tagdata[tagpos+i] > 64) && (tagdata[tagpos+i] < 91)) ) ) || ((tagdata[tagpos+i] > 64) && (tagdata[tagpos+i] < 91)) ) )
@ -450,8 +520,10 @@ int parse_new_id3(mpg123_handle *fr, unsigned long first4bytes)
for(i = 0; i < KNOWN_FRAMES; ++i) for(i = 0; i < KNOWN_FRAMES; ++i)
if(!strncmp(frame_type[i], id, 4)){ tt = i; break; } if(!strncmp(frame_type[i], id, 4)){ tt = i; break; }
if(tt != egal) if(id[0] == 'T' && tt != extra) tt = text;
if(tt != unknown)
{ {
int rva_mode = -1; /* mix / album */ int rva_mode = -1; /* mix / album */
unsigned long realsize = framesize; unsigned long realsize = framesize;
@ -487,10 +559,10 @@ int parse_new_id3(mpg123_handle *fr, unsigned long first4bytes)
switch(tt) switch(tt)
{ {
case comment: case comment:
process_comment(fr, (char*)realdata, realsize, tt); process_comment(fr, (char*)realdata, realsize, comment+1, id);
break; break;
case extra: /* perhaps foobar2000's work */ case extra: /* perhaps foobar2000's work */
process_extra(fr, (char*)realdata, realsize, tt); process_extra(fr, (char*)realdata, realsize, extra+1, id);
break; break;
case rva2: /* "the" RVA tag */ case rva2: /* "the" RVA tag */
{ {
@ -503,7 +575,7 @@ int parse_new_id3(mpg123_handle *fr, unsigned long first4bytes)
|| !strncasecmp((char*)realdata, "audiophile", 10) || !strncasecmp((char*)realdata, "audiophile", 10)
|| !strncasecmp((char*)realdata, "user", 4)) || !strncasecmp((char*)realdata, "user", 4))
rva_mode = 1; rva_mode = 1;
if(fr->rva.level[rva_mode] <= tt+1) if(fr->rva.level[rva_mode] <= rva2+1)
{ {
pos += strlen((char*) realdata) + 1; pos += strlen((char*) realdata) + 1;
if(realdata[pos] == 1) if(realdata[pos] == 1)
@ -519,7 +591,7 @@ int parse_new_id3(mpg123_handle *fr, unsigned long first4bytes)
if(VERBOSE3) fprintf(stderr, "Note: RVA value %fdB\n", fr->rva.gain[rva_mode]); if(VERBOSE3) fprintf(stderr, "Note: RVA value %fdB\n", fr->rva.gain[rva_mode]);
/* heh, the peak value is represented by a number of bits - but in what manner? Skipping that part */ /* heh, the peak value is represented by a number of bits - but in what manner? Skipping that part */
fr->rva.peak[rva_mode] = 0; fr->rva.peak[rva_mode] = 0;
fr->rva.level[rva_mode] = tt+1; fr->rva.level[rva_mode] = rva2+1;
} }
} }
#else #else
@ -528,25 +600,8 @@ int parse_new_id3(mpg123_handle *fr, unsigned long first4bytes)
} }
break; break;
/* non-rva metainfo, simply store... */ /* non-rva metainfo, simply store... */
case artist: case text:
debug("ID3v2: parsing artist info"); process_text(fr, (char*)realdata, realsize, id);
store_id3_text(&fr->id3v2.artist, (char*) realdata, realsize);
break;
case album:
debug("ID3v2: parsing album info");
store_id3_text(&fr->id3v2.album, (char*) realdata, realsize);
break;
case title:
debug("ID3v2: parsing title info");
store_id3_text(&fr->id3v2.title, (char*) realdata, realsize);
break;
case year:
debug("ID3v2: parsing year info");
store_id3_text(&fr->id3v2.year, (char*) realdata, realsize);
break;
case genre:
debug("ID3v2: parsing genre info");
store_id3_text(&fr->id3v2.genre, (char*) realdata, realsize);
break; break;
default: error1("ID3v2: unknown frame type %i", tt); default: error1("ID3v2: unknown frame type %i", tt);
} }

View File

@ -16,5 +16,6 @@ void init_id3(mpg123_handle *fr);
void exit_id3(mpg123_handle *fr); void exit_id3(mpg123_handle *fr);
void reset_id3(mpg123_handle *fr); void reset_id3(mpg123_handle *fr);
int parse_new_id3(mpg123_handle *fr, unsigned long first4bytes); int parse_new_id3(mpg123_handle *fr, unsigned long first4bytes);
void id3_link(mpg123_handle *fr);
#endif #endif

View File

@ -897,6 +897,7 @@ int mpg123_id3(mpg123_handle *mh, mpg123_id3v1 **v1, mpg123_id3v2 **v2)
if(mh->metaflags & MPG123_ID3) if(mh->metaflags & MPG123_ID3)
{ {
id3_link(mh);
if(v1 != NULL && mh->rdat.flags & READER_ID3TAG) *v1 = (mpg123_id3v1*) mh->id3buf; if(v1 != NULL && mh->rdat.flags & READER_ID3TAG) *v1 = (mpg123_id3v1*) mh->id3buf;
if(v2 != NULL) *v2 = &mh->id3v2; if(v2 != NULL) *v2 = &mh->id3v2;
mh->metaflags |= MPG123_ID3; mh->metaflags |= MPG123_ID3;

View File

@ -566,13 +566,17 @@ EXPORT int mpg123_add_string(mpg123_string* sb, char* stuff);
* \return 0 on error, 1 on success */ * \return 0 on error, 1 on success */
EXPORT int mpg123_set_string(mpg123_string* sb, char* stuff); EXPORT int mpg123_set_string(mpg123_string* sb, char* stuff);
/** Sub data structure for ID3v2, for storing comments. */ /** Sub data structure for ID3v2, for storing various text fields (including comments).
* This is for ID3v2 COMM, TXXX and all the other text fields.
* Only COMM and TXXX have a description, only COMM has a language.
* You should consult the ID3v2 specification for the use of the various text fields ("frames" in ID3v2 documentation, I use "fields" here to separate from MPEG frames). */
typedef struct typedef struct
{ {
char lang[4]; /**< Three-letter language code (null-terminated). */ char lang[3]; /**< Three-letter language code (not terminated). */
char id[4]; /**< The ID3v2 text field id, like TALB, TPE2, ... (4 characters, no string termination). */
mpg123_string description; /**< Empty for the generic comment... */ mpg123_string description; /**< Empty for the generic comment... */
mpg123_string text; /**< ... */ mpg123_string text; /**< ... */
} mpg123_comment; } mpg123_text;
/** Data structure for storing IDV3v2 tags. /** Data structure for storing IDV3v2 tags.
* This structure is not a direct binary mapping with the file contents. * This structure is not a direct binary mapping with the file contents.
@ -582,14 +586,20 @@ typedef struct
typedef struct typedef struct
{ {
unsigned char version; /**< 3 or 4 for ID3v2.3 or ID3v2.4. */ unsigned char version; /**< 3 or 4 for ID3v2.3 or ID3v2.4. */
mpg123_string title; /**< Title string. */ mpg123_string *title; /**< Title string (pointer into text_list). */
mpg123_string artist; /**< Artist string. */ mpg123_string *artist; /**< Artist string (pointer into text_list). */
mpg123_string album; /**< Album string. */ mpg123_string *album; /**< Album string (pointer into text_list). */
mpg123_string year; /**< The year as a string. */ mpg123_string *year; /**< The year as a string (pointer into text_list). */
mpg123_string genre; /**< Genre String. The genre string(s) may very well need postprocessing, esp. for ID3v2.3. */ mpg123_string *genre; /**< Genre String (pointer into text_list). The genre string(s) may very well need postprocessing, esp. for ID3v2.3. */
size_t comments; /**< Number of comments. */ mpg123_string *comment; /**< Pointer to last encountered comment text with empty description. */
mpg123_comment *comment; /**< Array of comments. */ /* Encountered ID3v2 fields are appended to these lists.
mpg123_string *generic_comment; /**< Pointer to last encountered comment text with empty description. */ There can be multiple occurences, the pointers above always point to the last encountered data. */
mpg123_text *comment_list; /**< Array of comments. */
size_t comments; /**< Number of comments. */
mpg123_text *text; /**< Array of ID3v2 text fields */
size_t texts; /**< Numer of text fields. */
mpg123_text *extra; /**< The array of extra (TXXX) fields. */
size_t extras; /**< Number of extra text (TXXX) fields. */
} mpg123_id3v2; } mpg123_id3v2;
/** Data structure for ID3v1 tags (the last 128 bytes of a file). /** Data structure for ID3v1 tags (the last 128 bytes of a file).

View File

@ -53,14 +53,25 @@ int mpg123_resize_string(mpg123_string* sb, size_t new)
int mpg123_copy_string(mpg123_string* from, mpg123_string* to) int mpg123_copy_string(mpg123_string* from, mpg123_string* to)
{ {
size_t fill;
char *text;
if(to == NULL) return -1; if(to == NULL) return -1;
if(from == NULL) return mpg123_set_string(to, ""); if(from == NULL)
if(mpg123_resize_string(to, from->fill))
{ {
memcpy(to->p, from->p, to->size); fill = 0;
to->fill = to->size; text = NULL;
}
else
{
fill = from->fill;
text = from->p;
}
if(mpg123_resize_string(to, fill))
{
memcpy(to->p, text, fill);
to->fill = fill;
return 1; return 1;
} }
else return 0; else return 0;