mirror of
https://github.com/taglib/taglib.git
synced 2025-06-04 01:28:21 -04:00
Support for MP4 cover art
CCMAIL:martin.trashbox@gmail.com git-svn-id: svn://anonsvn.kde.org/home/kde/trunk/kdesupport/taglib@1039809 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
This commit is contained in:
parent
bffd4da8b6
commit
e8281e1b9f
5
NEWS
5
NEWS
@ -1,14 +1,15 @@
|
||||
TagLib 1.6.1
|
||||
============
|
||||
|
||||
* Better detection of .oga files in FileRef.
|
||||
* Better detection of the audio codec of .oga files in FileRef.
|
||||
* Fixed saving of Vorbis comments to Ogg FLAC files. TagLib tried to
|
||||
include the Vorbis framing bit, which is only correct for Ogg Vorbis.
|
||||
* Public symbols now have explicitly set visibility to "default" on GCC.
|
||||
* Added missing exports for static ID3v1 functions.
|
||||
* Fixed a typo in taglib_c.pc
|
||||
* Fixed a failing test on ppc64.
|
||||
* Support for binary 'covr' atom in MP4 files.
|
||||
* Support for binary 'covr' atom in MP4 files. TagLib 1.6 treated them
|
||||
as text atoms, which corrupted them in some cases.
|
||||
|
||||
TagLib 1.6
|
||||
==========
|
||||
|
1
include/mp4coverart.h
Normal file
1
include/mp4coverart.h
Normal file
@ -0,0 +1 @@
|
||||
#include "../taglib/mp4/mp4coverart.h"
|
@ -111,6 +111,7 @@ mp4/mp4atom.cpp
|
||||
mp4/mp4tag.cpp
|
||||
mp4/mp4item.cpp
|
||||
mp4/mp4properties.cpp
|
||||
mp4/mp4coverart.cpp
|
||||
)
|
||||
ELSE(WITH_MP4)
|
||||
SET(mp4_SRCS)
|
||||
@ -200,6 +201,7 @@ SET_TARGET_PROPERTIES(tag PROPERTIES
|
||||
SOVERSION ${TAGLIB_LIB_MAJOR_VERSION}
|
||||
INSTALL_NAME_DIR ${LIB_INSTALL_DIR}
|
||||
DEFINE_SYMBOL MAKE_TAGLIB_LIB
|
||||
LINK_INTERFACE_LIBRARIES ""
|
||||
)
|
||||
INSTALL(TARGETS tag
|
||||
LIBRARY DESTINATION ${LIB_INSTALL_DIR}
|
||||
|
@ -1 +1 @@
|
||||
INSTALL( FILES mp4file.h mp4atom.h mp4tag.h mp4item.h mp4properties.h DESTINATION ${INCLUDE_INSTALL_DIR}/taglib)
|
||||
INSTALL( FILES mp4file.h mp4atom.h mp4tag.h mp4item.h mp4properties.h mp4coverart.h DESTINATION ${INCLUDE_INSTALL_DIR}/taglib)
|
||||
|
@ -7,7 +7,7 @@ INCLUDES = \
|
||||
|
||||
noinst_LTLIBRARIES = libmp4.la
|
||||
|
||||
libmp4_la_SOURCES = mp4atom.cpp mp4file.cpp mp4item.cpp mp4properties.cpp mp4tag.cpp
|
||||
libmp4_la_SOURCES = mp4atom.cpp mp4file.cpp mp4item.cpp mp4properties.cpp mp4tag.cpp mp4coverart.cpp
|
||||
|
||||
taglib_include_HEADERS = mp4atom.h mp4file.h mp4item.h mp4properties.h mp4tag.h
|
||||
taglib_include_HEADERS = mp4atom.h mp4file.h mp4item.h mp4properties.h mp4tag.h mp4coverart.h
|
||||
taglib_includedir = $(includedir)/taglib
|
||||
|
89
taglib/mp4/mp4coverart.cpp
Normal file
89
taglib/mp4/mp4coverart.cpp
Normal file
@ -0,0 +1,89 @@
|
||||
/**************************************************************************
|
||||
copyright : (C) 2009 by Lukáš Lalinský
|
||||
email : lalinsky@gmail.com
|
||||
**************************************************************************/
|
||||
|
||||
/***************************************************************************
|
||||
* This library is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU Lesser General Public License version *
|
||||
* 2.1 as published by the Free Software Foundation. *
|
||||
* *
|
||||
* This library is distributed in the hope that it will be useful, but *
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
|
||||
* Lesser General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU Lesser General Public *
|
||||
* License along with this library; if not, write to the Free Software *
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
|
||||
* USA *
|
||||
* *
|
||||
* Alternatively, this file is available under the Mozilla Public *
|
||||
* License Version 1.1. You may obtain a copy of the License at *
|
||||
* http://www.mozilla.org/MPL/ *
|
||||
***************************************************************************/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#ifdef WITH_MP4
|
||||
|
||||
#include <taglib.h>
|
||||
#include <tdebug.h>
|
||||
#include "mp4coverart.h"
|
||||
|
||||
using namespace TagLib;
|
||||
|
||||
class MP4::CoverArt::CoverArtPrivate : public RefCounter
|
||||
{
|
||||
public:
|
||||
CoverArtPrivate() : RefCounter(), format(MP4::CoverArt::JPEG) {}
|
||||
|
||||
Format format;
|
||||
ByteVector data;
|
||||
};
|
||||
|
||||
MP4::CoverArt::CoverArt(Format format, const ByteVector &data)
|
||||
{
|
||||
d = new CoverArtPrivate;
|
||||
d->format = format;
|
||||
d->data = data;
|
||||
}
|
||||
|
||||
MP4::CoverArt::CoverArt(const CoverArt &item) : d(item.d)
|
||||
{
|
||||
d->ref();
|
||||
}
|
||||
|
||||
MP4::CoverArt &
|
||||
MP4::CoverArt::operator=(const CoverArt &item)
|
||||
{
|
||||
if(d->deref()) {
|
||||
delete d;
|
||||
}
|
||||
d = item.d;
|
||||
d->ref();
|
||||
return *this;
|
||||
}
|
||||
|
||||
MP4::CoverArt::~CoverArt()
|
||||
{
|
||||
if(d->deref()) {
|
||||
delete d;
|
||||
}
|
||||
}
|
||||
|
||||
MP4::CoverArt::Format
|
||||
MP4::CoverArt::format() const
|
||||
{
|
||||
return d->format;
|
||||
}
|
||||
|
||||
ByteVector
|
||||
MP4::CoverArt::data() const
|
||||
{
|
||||
return d->data;
|
||||
}
|
||||
|
||||
#endif
|
71
taglib/mp4/mp4coverart.h
Normal file
71
taglib/mp4/mp4coverart.h
Normal file
@ -0,0 +1,71 @@
|
||||
/**************************************************************************
|
||||
copyright : (C) 2009 by Lukáš Lalinský
|
||||
email : lalinsky@gmail.com
|
||||
**************************************************************************/
|
||||
|
||||
/***************************************************************************
|
||||
* This library is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU Lesser General Public License version *
|
||||
* 2.1 as published by the Free Software Foundation. *
|
||||
* *
|
||||
* This library is distributed in the hope that it will be useful, but *
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
|
||||
* Lesser General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU Lesser General Public *
|
||||
* License along with this library; if not, write to the Free Software *
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
|
||||
* USA *
|
||||
* *
|
||||
* Alternatively, this file is available under the Mozilla Public *
|
||||
* License Version 1.1. You may obtain a copy of the License at *
|
||||
* http://www.mozilla.org/MPL/ *
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef TAGLIB_MP4COVERART_H
|
||||
#define TAGLIB_MP4COVERART_H
|
||||
|
||||
#include "tlist.h"
|
||||
#include "tbytevector.h"
|
||||
#include "taglib_export.h"
|
||||
|
||||
namespace TagLib {
|
||||
|
||||
namespace MP4 {
|
||||
|
||||
class TAGLIB_EXPORT CoverArt
|
||||
{
|
||||
public:
|
||||
/*!
|
||||
* This describes the image type.
|
||||
*/
|
||||
enum Format {
|
||||
JPEG = 0x0D,
|
||||
PNG = 0x0E
|
||||
};
|
||||
|
||||
CoverArt(Format format, const ByteVector &data);
|
||||
~CoverArt();
|
||||
|
||||
CoverArt(const CoverArt &item);
|
||||
CoverArt &operator=(const CoverArt &item);
|
||||
|
||||
//! Format of the image
|
||||
Format format() const;
|
||||
|
||||
//! The image data
|
||||
ByteVector data() const;
|
||||
|
||||
private:
|
||||
class CoverArtPrivate;
|
||||
CoverArtPrivate *d;
|
||||
};
|
||||
|
||||
typedef List<CoverArt> CoverArtList;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
@ -47,6 +47,7 @@ public:
|
||||
IntPair m_intPair;
|
||||
};
|
||||
StringList m_stringList;
|
||||
MP4::CoverArtList m_coverArtList;
|
||||
};
|
||||
|
||||
MP4::Item::Item()
|
||||
@ -103,6 +104,12 @@ MP4::Item::Item(const StringList &value)
|
||||
d->m_stringList = value;
|
||||
}
|
||||
|
||||
MP4::Item::Item(const MP4::CoverArtList &value)
|
||||
{
|
||||
d = new ItemPrivate;
|
||||
d->m_coverArtList = value;
|
||||
}
|
||||
|
||||
bool
|
||||
MP4::Item::toBool() const
|
||||
{
|
||||
@ -127,6 +134,12 @@ MP4::Item::toStringList() const
|
||||
return d->m_stringList;
|
||||
}
|
||||
|
||||
MP4::CoverArtList
|
||||
MP4::Item::toCoverArtList() const
|
||||
{
|
||||
return d->m_coverArtList;
|
||||
}
|
||||
|
||||
bool
|
||||
MP4::Item::isValid() const
|
||||
{
|
||||
|
@ -27,6 +27,7 @@
|
||||
#define TAGLIB_MP4ITEM_H
|
||||
|
||||
#include "tstringlist.h"
|
||||
#include "mp4coverart.h"
|
||||
#include "taglib_export.h"
|
||||
|
||||
namespace TagLib {
|
||||
@ -49,11 +50,13 @@ namespace TagLib {
|
||||
Item(bool value);
|
||||
Item(int first, int second);
|
||||
Item(const StringList &value);
|
||||
Item(const CoverArtList &value);
|
||||
|
||||
int toInt() const;
|
||||
bool toBool() const;
|
||||
IntPair toIntPair() const;
|
||||
StringList toStringList() const;
|
||||
CoverArtList toCoverArtList() const;
|
||||
|
||||
bool isValid() const;
|
||||
|
||||
|
@ -77,6 +77,9 @@ MP4::Tag::Tag(File *file, MP4::Atoms *atoms)
|
||||
else if(atom->name == "gnre") {
|
||||
parseGnre(atom, file);
|
||||
}
|
||||
else if(atom->name == "covr") {
|
||||
parseCovr(atom, file);
|
||||
}
|
||||
else {
|
||||
parseText(atom, file);
|
||||
}
|
||||
@ -194,6 +197,30 @@ MP4::Tag::parseFreeForm(MP4::Atom *atom, TagLib::File *file)
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
MP4::Tag::parseCovr(MP4::Atom *atom, TagLib::File *file)
|
||||
{
|
||||
MP4::CoverArtList value;
|
||||
ByteVector data = file->readBlock(atom->length - 8);
|
||||
unsigned int pos = 0;
|
||||
while(pos < data.size()) {
|
||||
int length = data.mid(pos, 4).toUInt();
|
||||
ByteVector name = data.mid(pos + 4, 4);
|
||||
int flags = data.mid(pos + 8, 4).toUInt();
|
||||
if(name != "data") {
|
||||
debug("MP4: Unexpected atom \"" + name + "\", expecting \"data\"");
|
||||
return;
|
||||
}
|
||||
if(flags == MP4::CoverArt::PNG || flags == MP4::CoverArt::JPEG) {
|
||||
value.append(MP4::CoverArt(MP4::CoverArt::Format(flags),
|
||||
data.mid(pos + 16, length - 16)));
|
||||
}
|
||||
pos += length;
|
||||
}
|
||||
if(value.size() > 0)
|
||||
d->items.insert(atom->name, value);
|
||||
}
|
||||
|
||||
ByteVector
|
||||
MP4::Tag::padIlst(const ByteVector &data, int length)
|
||||
{
|
||||
@ -267,6 +294,18 @@ MP4::Tag::renderText(const ByteVector &name, MP4::Item &item, int flags)
|
||||
return renderData(name, flags, data);
|
||||
}
|
||||
|
||||
ByteVector
|
||||
MP4::Tag::renderCovr(const ByteVector &name, MP4::Item &item)
|
||||
{
|
||||
ByteVector data;
|
||||
MP4::CoverArtList value = item.toCoverArtList();
|
||||
for(unsigned int i = 0; i < value.size(); i++) {
|
||||
data.append(renderAtom("data", ByteVector::fromUInt(value[i].format()) +
|
||||
ByteVector(4, '\0') + value[i].data()));
|
||||
}
|
||||
return renderAtom(name, data);
|
||||
}
|
||||
|
||||
ByteVector
|
||||
MP4::Tag::renderFreeForm(const String &name, MP4::Item &item)
|
||||
{
|
||||
@ -306,6 +345,9 @@ MP4::Tag::save()
|
||||
else if(name == "tmpo") {
|
||||
data.append(renderInt(name.data(String::Latin1), i->second));
|
||||
}
|
||||
else if(name == "covr") {
|
||||
data.append(renderCovr(name.data(String::Latin1), i->second));
|
||||
}
|
||||
else if(name.size() == 4){
|
||||
data.append(renderText(name.data(String::Latin1), i->second));
|
||||
}
|
||||
|
@ -74,6 +74,7 @@ namespace TagLib {
|
||||
void parseGnre(Atom *atom, TagLib::File *file);
|
||||
void parseIntPair(Atom *atom, TagLib::File *file);
|
||||
void parseBool(Atom *atom, TagLib::File *file);
|
||||
void parseCovr(Atom *atom, TagLib::File *file);
|
||||
|
||||
TagLib::ByteVector padIlst(const ByteVector &data, int length = -1);
|
||||
TagLib::ByteVector renderAtom(const ByteVector &name, const TagLib::ByteVector &data);
|
||||
@ -84,6 +85,7 @@ namespace TagLib {
|
||||
TagLib::ByteVector renderInt(const ByteVector &name, Item &item);
|
||||
TagLib::ByteVector renderIntPair(const ByteVector &name, Item &item);
|
||||
TagLib::ByteVector renderIntPairNoTrailing(const ByteVector &name, Item &item);
|
||||
TagLib::ByteVector renderCovr(const ByteVector &name, Item &item);
|
||||
|
||||
void updateParents(AtomList &path, long delta, int ignore = 0);
|
||||
void updateOffsets(long delta, long offset);
|
||||
|
@ -38,7 +38,11 @@ SET(test_runner_SRCS
|
||||
test_oggflac.cpp
|
||||
)
|
||||
IF(WITH_MP4)
|
||||
SET(test_runner_SRCS ${test_runner_SRCS} test_mp4.cpp)
|
||||
SET(test_runner_SRCS ${test_runner_SRCS}
|
||||
test_mp4.cpp
|
||||
test_mp4item.cpp
|
||||
test_mp4coverart.cpp
|
||||
)
|
||||
ENDIF(WITH_MP4)
|
||||
|
||||
IF(WITH_ASF)
|
||||
|
@ -19,6 +19,8 @@ class TestMP4 : public CppUnit::TestFixture
|
||||
CPPUNIT_TEST(testUpdateStco);
|
||||
CPPUNIT_TEST(testSaveExisingWhenIlstIsLast);
|
||||
CPPUNIT_TEST(test64BitAtom);
|
||||
CPPUNIT_TEST(testCovrRead);
|
||||
CPPUNIT_TEST(testCovrWrite);
|
||||
CPPUNIT_TEST_SUITE_END();
|
||||
|
||||
public:
|
||||
@ -145,6 +147,45 @@ public:
|
||||
deleteFile(filename);
|
||||
}
|
||||
|
||||
void testCovrRead()
|
||||
{
|
||||
MP4::File *f = new MP4::File("data/has-tags.m4a");
|
||||
CPPUNIT_ASSERT(f->tag()->itemListMap().contains("covr"));
|
||||
MP4::CoverArtList l = f->tag()->itemListMap()["covr"].toCoverArtList();
|
||||
CPPUNIT_ASSERT_EQUAL(TagLib::uint(2), l.size());
|
||||
CPPUNIT_ASSERT_EQUAL(MP4::CoverArt::PNG, l[0].format());
|
||||
CPPUNIT_ASSERT_EQUAL(TagLib::uint(79), l[0].data().size());
|
||||
CPPUNIT_ASSERT_EQUAL(MP4::CoverArt::JPEG, l[1].format());
|
||||
CPPUNIT_ASSERT_EQUAL(TagLib::uint(287), l[1].data().size());
|
||||
}
|
||||
|
||||
void testCovrWrite()
|
||||
{
|
||||
string filename = copyFile("has-tags", ".m4a");
|
||||
|
||||
MP4::File *f = new MP4::File(filename.c_str());
|
||||
CPPUNIT_ASSERT(f->tag()->itemListMap().contains("covr"));
|
||||
MP4::CoverArtList l = f->tag()->itemListMap()["covr"].toCoverArtList();
|
||||
l.append(MP4::CoverArt(MP4::CoverArt::PNG, "foo"));
|
||||
f->tag()->itemListMap()["covr"] = l;
|
||||
f->save();
|
||||
delete f;
|
||||
|
||||
f = new MP4::File(filename.c_str());
|
||||
CPPUNIT_ASSERT(f->tag()->itemListMap().contains("covr"));
|
||||
l = f->tag()->itemListMap()["covr"].toCoverArtList();
|
||||
CPPUNIT_ASSERT_EQUAL(TagLib::uint(3), l.size());
|
||||
CPPUNIT_ASSERT_EQUAL(MP4::CoverArt::PNG, l[0].format());
|
||||
CPPUNIT_ASSERT_EQUAL(TagLib::uint(79), l[0].data().size());
|
||||
CPPUNIT_ASSERT_EQUAL(MP4::CoverArt::JPEG, l[1].format());
|
||||
CPPUNIT_ASSERT_EQUAL(TagLib::uint(287), l[1].data().size());
|
||||
CPPUNIT_ASSERT_EQUAL(MP4::CoverArt::PNG, l[2].format());
|
||||
CPPUNIT_ASSERT_EQUAL(TagLib::uint(3), l[2].data().size());
|
||||
delete f;
|
||||
|
||||
deleteFile(filename);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
CPPUNIT_TEST_SUITE_REGISTRATION(TestMP4);
|
||||
|
49
tests/test_mp4coverart.cpp
Normal file
49
tests/test_mp4coverart.cpp
Normal file
@ -0,0 +1,49 @@
|
||||
#include <cppunit/extensions/HelperMacros.h>
|
||||
#include <string>
|
||||
#include <stdio.h>
|
||||
#include <tag.h>
|
||||
#include <mp4coverart.h>
|
||||
#include "utils.h"
|
||||
|
||||
using namespace std;
|
||||
using namespace TagLib;
|
||||
|
||||
class TestMP4CoverArt : public CppUnit::TestFixture
|
||||
{
|
||||
CPPUNIT_TEST_SUITE(TestMP4CoverArt);
|
||||
CPPUNIT_TEST(testSimple);
|
||||
CPPUNIT_TEST(testList);
|
||||
CPPUNIT_TEST_SUITE_END();
|
||||
|
||||
public:
|
||||
|
||||
void testSimple()
|
||||
{
|
||||
MP4::CoverArt c(MP4::CoverArt::PNG, "foo");
|
||||
CPPUNIT_ASSERT_EQUAL(MP4::CoverArt::PNG, c.format());
|
||||
CPPUNIT_ASSERT_EQUAL(ByteVector("foo"), c.data());
|
||||
|
||||
MP4::CoverArt c2(c);
|
||||
CPPUNIT_ASSERT_EQUAL(MP4::CoverArt::PNG, c2.format());
|
||||
CPPUNIT_ASSERT_EQUAL(ByteVector("foo"), c2.data());
|
||||
|
||||
MP4::CoverArt c3 = c;
|
||||
CPPUNIT_ASSERT_EQUAL(MP4::CoverArt::PNG, c3.format());
|
||||
CPPUNIT_ASSERT_EQUAL(ByteVector("foo"), c3.data());
|
||||
}
|
||||
|
||||
void testList()
|
||||
{
|
||||
MP4::CoverArtList l;
|
||||
l.append(MP4::CoverArt(MP4::CoverArt::PNG, "foo"));
|
||||
l.append(MP4::CoverArt(MP4::CoverArt::JPEG, "bar"));
|
||||
|
||||
CPPUNIT_ASSERT_EQUAL(MP4::CoverArt::PNG, l[0].format());
|
||||
CPPUNIT_ASSERT_EQUAL(ByteVector("foo"), l[0].data());
|
||||
CPPUNIT_ASSERT_EQUAL(MP4::CoverArt::JPEG, l[1].format());
|
||||
CPPUNIT_ASSERT_EQUAL(ByteVector("bar"), l[1].data());
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
CPPUNIT_TEST_SUITE_REGISTRATION(TestMP4CoverArt);
|
37
tests/test_mp4item.cpp
Normal file
37
tests/test_mp4item.cpp
Normal file
@ -0,0 +1,37 @@
|
||||
#include <cppunit/extensions/HelperMacros.h>
|
||||
#include <string>
|
||||
#include <stdio.h>
|
||||
#include <tag.h>
|
||||
#include <mp4coverart.h>
|
||||
#include <mp4item.h>
|
||||
#include "utils.h"
|
||||
|
||||
using namespace std;
|
||||
using namespace TagLib;
|
||||
|
||||
class TestMP4Item : public CppUnit::TestFixture
|
||||
{
|
||||
CPPUNIT_TEST_SUITE(TestMP4Item);
|
||||
CPPUNIT_TEST(testCoverArtList);
|
||||
CPPUNIT_TEST_SUITE_END();
|
||||
|
||||
public:
|
||||
|
||||
void testCoverArtList()
|
||||
{
|
||||
MP4::CoverArtList l;
|
||||
l.append(MP4::CoverArt(MP4::CoverArt::PNG, "foo"));
|
||||
l.append(MP4::CoverArt(MP4::CoverArt::JPEG, "bar"));
|
||||
|
||||
MP4::Item i(l);
|
||||
MP4::CoverArtList l2 = i.toCoverArtList();
|
||||
|
||||
CPPUNIT_ASSERT_EQUAL(MP4::CoverArt::PNG, l[0].format());
|
||||
CPPUNIT_ASSERT_EQUAL(ByteVector("foo"), l[0].data());
|
||||
CPPUNIT_ASSERT_EQUAL(MP4::CoverArt::JPEG, l[1].format());
|
||||
CPPUNIT_ASSERT_EQUAL(ByteVector("bar"), l[1].data());
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
CPPUNIT_TEST_SUITE_REGISTRATION(TestMP4Item);
|
Loading…
x
Reference in New Issue
Block a user