Rewrote id3Dup to search for tags manually. Now libid3tag will actually tell us the version of the tag we got. As an added benefit we also do fewer reads/mallocs when scanning mp3s during database update/creation.

git-svn-id: https://svn.musicpd.org/mpd/trunk@4277 09075e82-0dd4-0310-85a5-a0d7c8717e4f
This commit is contained in:
J. Alexander Treuman 2006-06-21 15:12:41 +00:00
parent 30df7d49c5
commit bdf4107117
2 changed files with 138 additions and 12 deletions

View File

@ -252,7 +252,9 @@ void mp3_getReplayGainInfo(struct id3_tag * tag, ReplayGainInfo ** infoPtr) {
*infoPtr = NULL;
}
}
#endif
#ifdef HAVE_ID3TAG
static void mp3_parseId3Tag(mp3DecodeData * data, signed long tagsize, MpdTag ** mpdTag, ReplayGainInfo ** replayGainInfo) {
struct id3_tag * id3Tag = NULL;
id3_length_t count;

148
src/tag.c
View File

@ -43,6 +43,10 @@
#endif
#ifdef HAVE_ID3TAG
#define isId3v1(tag) (id3_tag_options(tag, 0, 0) & ID3_TAG_OPTION_ID3V1)
/* Size of the buffer to use for peeking at tag headers. We reuse this buffer
if the whole tag fits in it, so make it big to avoid a malloc(). */
#define ID3_TAG_BUFLEN 1024
#ifndef ID3_FRAME_COMPOSER
#define ID3_FRAME_COMPOSER "TCOM"
#endif
@ -181,29 +185,149 @@ MpdTag * parseId3Tag(struct id3_tag * tag) {
}
#endif
#ifdef HAVE_ID3TAG
static int fillBuffer(void *buf, size_t size, FILE *stream,
long offset, int whence)
{
if (fseek(stream, offset, whence) != 0) return 0;
return fread(buf, 1, size, stream);
}
#endif
#ifdef HAVE_ID3TAG
static int getId3v2FooterSize(FILE * stream, long offset, int whence)
{
id3_byte_t buf[ID3_TAG_QUERYSIZE];
int bufsize;
bufsize = fillBuffer(buf, ID3_TAG_QUERYSIZE, stream, offset, whence);
if (bufsize <= 0) return 0;
return id3_tag_query(buf, bufsize);
}
#endif
#ifdef HAVE_ID3TAG
static struct id3_tag * getId3Tag(FILE * stream, long offset, int whence)
{
struct id3_tag * tag;
id3_byte_t * buf[ID3_TAG_BUFLEN];
id3_byte_t * mbuf;
int tagsize;
int bufsize;
int mbufsize;
/* It's ok if we get less than we asked for */
bufsize = fillBuffer(buf, ID3_TAG_BUFLEN, stream, offset, whence);
if (bufsize <= 0) return NULL;
/* Look for a tag header */
tagsize = id3_tag_query((const id3_byte_t *)buf, bufsize);
if (tagsize <= 0) return NULL;
if (tagsize <= bufsize) {
/* Got an id3 tag, and it fits in buf */
tag = id3_tag_parse((const id3_byte_t *)buf, tagsize);
} else {
/* Got an id3tag that overflows buf, so get a new one */
mbuf = malloc(tagsize);
if (!mbuf) return NULL;
mbufsize = fillBuffer(mbuf, tagsize, stream, offset, whence);
if (mbufsize < tagsize) {
free(mbuf);
return NULL;
}
tag = id3_tag_parse((const id3_byte_t *)mbuf, tagsize);
free(mbuf);
}
return tag;
}
#endif
#ifdef HAVE_ID3TAG
static struct id3_tag * findId3TagFromBeginning(FILE * stream)
{
struct id3_tag * tag;
struct id3_tag * seektag;
struct id3_frame * frame;
int seek;
tag = getId3Tag(stream, 0, SEEK_SET);
if (!tag) {
return NULL;
} else if (isId3v1(tag)) {
/* id3v1 tags don't belong here */
id3_tag_delete(tag);
return NULL;
}
/* We have an id3v2 tag, so let's look for SEEK frames */
while ((frame = id3_tag_findframe(tag, "SEEK", 0))) {
/* Found a SEEK frame, get it's value */
seek = id3_field_getint(id3_frame_field(frame, 0));
if (seek < 0) break;
/* Get the tag specified by the SEEK frame */
seektag = getId3Tag(stream, seek, SEEK_CUR);
if (!seektag || isId3v1(seektag)) break;
/* Replace the old tag with the new one */
id3_tag_delete(tag);
tag = seektag;
}
return tag;
}
#endif
#ifdef HAVE_ID3TAG
static struct id3_tag * findId3TagFromEnd(FILE * stream)
{
struct id3_tag * tag;
struct id3_tag * v1tag;
int tagsize;
/* Get an id3v1 tag from the end of file for later use */
v1tag = getId3Tag(stream, -128, SEEK_END);
/* Get the id3v2 tag size from the footer (located before v1tag) */
tagsize = getId3v2FooterSize(stream, (v1tag ? -128 : 0) - 10, SEEK_END);
if (tagsize >= 0) return v1tag;
/* Get the tag which the footer belongs to */
tag = getId3Tag(stream, tagsize, SEEK_CUR);
if (!tag) return v1tag;
/* We have an id3v2 tag, so ditch v1tag */
id3_tag_delete(v1tag);
return tag;
}
#endif
MpdTag * id3Dup(char * file) {
MpdTag * ret = NULL;
#ifdef HAVE_ID3TAG
struct id3_file * id3_file;
struct id3_tag * tag;
FILE * stream;
id3_file = id3_file_open(file, ID3_FILE_MODE_READONLY);
if(!id3_file) {
stream = fopen(file, "r");
if (!stream) {
DEBUG("id3Dup: Failed to open file: '%s', %s\n",file, strerror(errno));
return NULL;
}
tag = id3_file_tag(id3_file);
if(!tag) {
id3_file_close(id3_file);
return NULL;
}
tag = findId3TagFromBeginning(stream);
if (!tag) tag = findId3TagFromEnd(stream);
fclose(stream);
if (!tag) return NULL;
ret = parseId3Tag(tag);
id3_file_close(id3_file);
id3_tag_delete(tag);
#endif
return ret;
}