Question

I am playing with TagLib (on Windows, built with MingW). I am trying to get TagLib to recognize when there is no ID3v1 or ID3v2 information in an MP3 file. According to the TagLib documentation, the ID3v2Tag() function in an MPEG File object should return a NULL pointer when there is no ID3v2 information in the file.

Unfortunately, this is not occurring. I have some test MP3 files I have made that I use in my code (I have made the files available):

  • blank.mp3 (download), no ID3v1 or ID3v2 information at all. I can confirm this by doing a plain text search for "TAG" and "ID3" in the files binary content.
  • only_album_id3v2.mp3 (download), has ID3v2 information (only the album is set)
  • only_album_id3v1.mp3 (download), has ID3v1 information (only the album is set)

Here is my code.

#include <iostream>

#include <mpeg/mpegfile.h>
#include <mpeg/id3v2/id3v2tag.h>

using namespace std;

int main()
{
    cout << "Test." << endl;

    TagLib::MPEG::File a("tests/other/blank.mp3");
    TagLib::MPEG::File b("tests/id3v2/only_album_id3v2.mp3");
    TagLib::MPEG::File c("tests/id3v1/only_album_id3v1.mp3");


    TagLib::ID3v2::Tag * at = a.ID3v2Tag();
    TagLib::ID3v2::Tag * bt = b.ID3v2Tag();
    TagLib::ID3v2::Tag * ct = c.ID3v2Tag();

    cout << at->album() << endl;
    cout << bt->album() << endl;
    cout << ct->album() << endl;

    cout << "The program is done.";

    return 0;
}

Running this program should break, due to a NULL pointer error on cout << at->album() << endl;, but it runs just fine. Also, when I cout << ct << endl;, it returns a memory address.

Here is the output:

Test.

test album id3v2

The program is done.

EDIT: Here is a new test.

#include <iostream>

#include <mpeg/mpegfile.h>
#include <mpeg/id3v2/id3v2tag.h>

using namespace std;

int main()
{
    cout << "Test." << endl;

    TagLib::MPEG::File a("tests/other/blank.mp3");
    TagLib::MPEG::File b("tests/id3v2/only_album_id3v2.mp3");
    TagLib::MPEG::File c("tests/id3v1/only_album_id3v1.mp3");


    TagLib::ID3v2::Tag * at = a.ID3v2Tag();
    TagLib::ID3v2::Tag * bt = b.ID3v2Tag();
    TagLib::ID3v2::Tag * ct = c.ID3v2Tag();

    if(at == NULL)
    {
        cout << "at is NULL.";
    }
    else
    {
        cout << "at is not NULL.";
    }
    cout << endl;

    if(bt == NULL)
    {
        cout << "bt is NULL.";
    }
    else
    {
        cout << "bt is not NULL.";
    }
    cout << endl;

    if(ct == NULL)
    {
        cout << "ct is NULL.";
    }
    else
    {
        cout << "ct is not NULL.";
    }
    cout << endl;

    cout << "The program is done.";

    return 0;
}

And here is the output.

Test.
at is not NULL.
bt is not NULL.
ct is not NULL.
The program is done.

Was it helpful?

Solution

I examined TagLib's code briefly.

I know nothing about it and never used it, but the code looks buggy to me. Here's why -

In MPEG::File::read(), we are looking for a tag - d->ID3v2Location = findID3v2();. If it doesn't exist, it isn't added to the tags vector. This is the check - if(d->ID3v2Location >= 0).

However, at the end of the function, just before returning, we have -

// Make sure that we have our default tag types available.
ID3v2Tag(true);
ID3v1Tag(true);

Now, Id3v2Tag(create) with a true parameter, actually calls return d->tag.access(ID3v2Index, create);. The access() function is -

template <class T> T *access(int index, bool create)
{
  if(!create || tag(index))
    return static_cast<T *>(tag(index));

  set(index, new T);
  return static_cast<T *>(tag(index));
}

So when create is true, we are creating a brand new, empty tag and placing it in the vector (using the set() function).

This means that no matter whether the file contains the tags or not, they are added to the vector. This isn't the documented behavior. Looks like a bug.

I don't know why these two lines are needed there - looking at the history of this file might hint as to why they were added, but I didn't do that.

Anyway, I want to stress that I never actually executed this code. This is based on purely statically reading only very small parts, without being aware of large scale issues.

I think that opening a bug report can't hurt.

OTHER TIPS

Using a null pointer doesn't necessarily result in any error you can see; it's undefined behavior. It might appear to work, or it might do something really really weird.

In this case the compiler is probably generating a call to TagLib::ID3v2::Tag::album with the this pointer set to null, but even this is not guaranteed. What happens inside the function is anyone's guess.

If the function can return NULL, you should be explicitly checking for it and doing something different.

Taglib create an "Empty" ID3v2Tag and ID3v1Tag in the object if the file do not have one.

I am having simmilar problem and hopefully I've found workaround for ID3v1/ID3v2 presence checking.

It's method virtual bool TagLib::Tag::isEmpty() const

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top