aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJ. Alexander Treuman <jat@spatialrift.net>2006-06-21 15:12:41 +0000
committerJ. Alexander Treuman <jat@spatialrift.net>2006-06-21 15:12:41 +0000
commitbdf4107117ea6b0e166c1be3ef270f631403a154 (patch)
tree5983aef600399a57f75928dd2858ed1318ccc9ff
parent30df7d49c5a4c5d377fed6111f8867f16be05a8c (diff)
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
-rw-r--r--src/inputPlugins/mp3_plugin.c2
-rw-r--r--src/tag.c148
2 files changed, 138 insertions, 12 deletions
diff --git a/src/inputPlugins/mp3_plugin.c b/src/inputPlugins/mp3_plugin.c
index 6fe9f384..79215bcd 100644
--- a/src/inputPlugins/mp3_plugin.c
+++ b/src/inputPlugins/mp3_plugin.c
@@ -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;
diff --git a/src/tag.c b/src/tag.c
index 4a716515..126d5bac 100644
--- a/src/tag.c
+++ b/src/tag.c
@@ -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
-MpdTag * id3Dup(char * file) {
- MpdTag * ret = NULL;
#ifdef HAVE_ID3TAG
- struct id3_file * id3_file;
+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;
+ }
- id3_file = id3_file_open(file, ID3_FILE_MODE_READONLY);
-
- if(!id3_file) {
- DEBUG("id3Dup: Failed to open file: '%s', %s\n",file, strerror(errno));
+ 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;
}
- tag = id3_file_tag(id3_file);
- if(!tag) {
- id3_file_close(id3_file);
+ /* 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_tag * tag;
+ FILE * stream;
+
+ stream = fopen(file, "r");
+ if (!stream) {
+ DEBUG("id3Dup: Failed to open file: '%s', %s\n",file, strerror(errno));
return NULL;
}
- ret = parseId3Tag(tag);
+ tag = findId3TagFromBeginning(stream);
+ if (!tag) tag = findId3TagFromEnd(stream);
- id3_file_close(id3_file);
+ fclose(stream);
+ if (!tag) return NULL;
+ ret = parseId3Tag(tag);
+ id3_tag_delete(tag);
#endif
return ret;
}