Merge branch 'master' into merge-master-to-taglib2

# Conflicts:
#	taglib/ape/apetag.cpp
#	taglib/ape/apetag.h
#	taglib/flac/flacproperties.cpp
#	taglib/mpeg/id3v1/id3v1tag.cpp
#	taglib/mpeg/id3v2/id3v2tag.cpp
#	taglib/riff/rifffile.cpp
#	taglib/riff/wav/infotag.cpp
#	taglib/toolkit/tbytevector.cpp
This commit is contained in:
Tsuda Kageyu 2015-12-02 09:31:10 +09:00
commit d36550f96d
20 changed files with 191 additions and 95 deletions

View File

@ -7,5 +7,5 @@ Name: TagLib
Description: Audio meta-data library
Requires:
Version: ${TAGLIB_LIB_VERSION_STRING}
Libs: -L${LIB_INSTALL_DIR} -ltag
Cflags: -I${INCLUDE_INSTALL_DIR}/taglib
Libs: -L${dollar}{libdir} -ltag
Cflags: -I${dollar}{includedir}/taglib

View File

@ -48,7 +48,6 @@ class APE::Tag::TagPrivate
{
public:
Footer footer;
ItemListMap itemListMap;
};

View File

@ -160,7 +160,7 @@ namespace
return new Ogg::Opus::File(arg, readAudioProperties, audioPropertiesStyle);
if(ext == "TTA")
return new TrueAudio::File(arg, readAudioProperties, audioPropertiesStyle);
if(ext == "M4A" || ext == "M4R" || ext == "M4B" || ext == "M4P" || ext == "MP4" || ext == "3G2")
if(ext == "M4A" || ext == "M4R" || ext == "M4B" || ext == "M4P" || ext == "MP4" || ext == "3G2" || ext == "M4V")
return new MP4::File(arg, readAudioProperties, audioPropertiesStyle);
if(ext == "WMA" || ext == "ASF")
return new ASF::File(arg, readAudioProperties, audioPropertiesStyle);
@ -319,6 +319,7 @@ StringList FileRef::defaultFileExtensions()
l.append("m4p");
l.append("3g2");
l.append("mp4");
l.append("m4v");
l.append("wma");
l.append("asf");
l.append("aif");

View File

@ -152,8 +152,8 @@ void FLAC::AudioProperties::read(const ByteVector &data, long long streamLength)
// The last 4 bits are the most significant 4 bits for the 36 bit
// stream length in samples. (Audio files measured in days)
const ulonglong hi = flags & 0xf;
const ulonglong lo = data.toUInt32BE(pos);
const unsigned long long hi = flags & 0xf;
const unsigned long long lo = data.toUInt32BE(pos);
pos += 4;
d->sampleFrames = (hi << 32) | lo;

View File

@ -62,10 +62,9 @@ namespace
class ID3v1::Tag::TagPrivate
{
public:
TagPrivate() : file(0), tagOffset(-1), track(0), genre(255) {}
File *file;
long long tagOffset;
TagPrivate() :
track(0),
genre(255) {}
String title;
String artist;
@ -80,18 +79,17 @@ public:
// public methods
////////////////////////////////////////////////////////////////////////////////
ID3v1::Tag::Tag() : TagLib::Tag()
ID3v1::Tag::Tag() :
TagLib::Tag(),
d(new TagPrivate())
{
d = new TagPrivate;
}
ID3v1::Tag::Tag(File *file, long long tagOffset) : TagLib::Tag()
ID3v1::Tag::Tag(File *file, long long tagOffset) :
TagLib::Tag(),
d(new TagPrivate())
{
d = new TagPrivate;
d->file = file;
d->tagOffset = tagOffset;
read();
read(file, tagOffset);
}
ID3v1::Tag::~Tag()
@ -222,12 +220,12 @@ void ID3v1::Tag::setStringHandler(const TagLib::StringHandler *handler)
// protected methods
////////////////////////////////////////////////////////////////////////////////
void ID3v1::Tag::read()
void ID3v1::Tag::read(File *file, long tagOffset)
{
if(d->file && d->file->isValid()) {
d->file->seek(d->tagOffset);
if(file && file->isValid()) {
file->seek(tagOffset);
// read the tag -- always 128 bytes
ByteVector data = d->file->readBlock(128);
const ByteVector data = file->readBlock(128);
// some initial sanity checking
if(data.size() == 128 && data.startsWith("TAG"))

View File

@ -140,7 +140,7 @@ namespace TagLib {
/*!
* Reads from the file specified in the constructor.
*/
void read();
void read(File *file, long tagOffset);
/*!
* Pareses the body of the tag in \a data.
*/

View File

@ -84,8 +84,7 @@ class ID3v2::Tag::TagPrivate
{
public:
TagPrivate() :
file(0),
tagOffset(-1),
fileLength(0),
extendedHeader(0),
footer(0)
{
@ -98,8 +97,7 @@ public:
delete footer;
}
File *file;
long long tagOffset;
long long fileLength;
const FrameFactory *factory;
Header header;
@ -125,11 +123,9 @@ ID3v2::Tag::Tag(File *file, long long tagOffset, const FrameFactory *factory) :
TagLib::Tag(),
d(new TagPrivate())
{
d->file = file;
d->tagOffset = tagOffset;
d->factory = factory;
read();
read(file, tagOffset);
}
ID3v2::Tag::~Tag()
@ -794,7 +790,8 @@ ByteVector ID3v2::Tag::render(int version) const
// Compute the amount of padding, and append that to tagData.
long long paddingSize = d->header.tagSize() - (tagData.size() - Header::size());
long long originalSize = d->header.tagSize();
long long paddingSize = originalSize - (tagData.size() - Header::size());
if(paddingSize <= 0) {
paddingSize = MinPaddingSize;
@ -802,7 +799,7 @@ ByteVector ID3v2::Tag::render(int version) const
else {
// Padding won't increase beyond 1% of the file size or 1MB.
long long threshold = d->file ? d->file->length() / 100 : 0;
long long threshold = d->fileLength / 100;
threshold = std::max(threshold, MinPaddingSize);
threshold = std::min(threshold, MaxPaddingSize);
@ -816,6 +813,9 @@ ByteVector ID3v2::Tag::render(int version) const
d->header.setMajorVersion(version);
d->header.setTagSize(static_cast<TagLib::uint>(tagData.size() - Header::size()));
if(d->fileLength > 0)
d->fileLength += (d->header.tagSize() - originalSize);
// TODO: This should eventually include d->footer->render().
const ByteVector headerData = d->header.render();
std::copy(headerData.begin(), headerData.end(), tagData.begin());
@ -840,22 +840,24 @@ void ID3v2::Tag::setLatin1StringHandler(const TagLib::StringHandler *handler)
// protected members
////////////////////////////////////////////////////////////////////////////////
void ID3v2::Tag::read()
void ID3v2::Tag::read(TagLib::File *file, long offset)
{
if(!d->file)
if(!file)
return;
if(!d->file->isOpen())
if(!file->isOpen())
return;
d->file->seek(d->tagOffset);
d->header.setData(d->file->readBlock(Header::size()));
d->fileLength = file->length();
file->seek(offset);
d->header.setData(file->readBlock(Header::size()));
// If the tag size is 0, then this is an invalid tag (tags must contain at
// least one frame)
if(d->header.tagSize() != 0)
parse(d->file->readBlock(d->header.tagSize()));
parse(file->readBlock(d->header.tagSize()));
// Look for duplicate ID3v2 tags and treat them as an extra blank of this one.
// It leads to overwriting them with zero when saving the tag.
@ -867,9 +869,9 @@ void ID3v2::Tag::read()
while(true) {
d->file->seek(d->tagOffset + d->header.completeTagSize() + extraSize);
file->seek(offset + d->header.completeTagSize() + extraSize);
const ByteVector data = d->file->readBlock(Header::size());
const ByteVector data = file->readBlock(Header::size());
if(data.size() < Header::size() || !data.startsWith(Header::fileIdentifier()))
break;

View File

@ -356,7 +356,7 @@ namespace TagLib {
* the Header, the body of the tag (which contains the ExtendedHeader and
* frames) and Footer.
*/
void read();
void read(TagLib::File *file, long offset);
/*!
* This is called by read to parse the body of the tag. It determines if an

View File

@ -100,11 +100,16 @@ bool RIFF::AIFF::File::save()
return false;
}
removeChunk("ID3 ");
removeChunk("id3 ");
if(d->hasID3v2) {
removeChunk("ID3 ");
removeChunk("id3 ");
d->hasID3v2 = false;
}
setChunkData("ID3 ", d->tag->render());
d->hasID3v2 = true;
if(tag() && !tag()->isEmpty()) {
setChunkData("ID3 ", d->tag->render());
d->hasID3v2 = true;
}
return true;
}

View File

@ -23,13 +23,15 @@
* http://www.mozilla.org/MPL/ *
***************************************************************************/
#include <algorithm>
#include <vector>
#include <tbytevector.h>
#include <tdebug.h>
#include <tstring.h>
#include "rifffile.h"
#include <algorithm>
#include <vector>
#include "riffutils.h"
using namespace TagLib;
@ -47,13 +49,11 @@ namespace
class RIFF::File::FilePrivate
{
public:
FilePrivate() :
endianness(BigEndian),
size(0)
{
}
FilePrivate(ByteOrder endianness) :
endianness(endianness),
size(0) {}
ByteOrder endianness;
const ByteOrder endianness;
ByteVector type;
TagLib::uint size;
ByteVector format;
@ -88,20 +88,18 @@ bool RIFF::File::isValidChunkName(const ByteVector &name) // static
// protected members
////////////////////////////////////////////////////////////////////////////////
RIFF::File::File(FileName file, ByteOrder endianness) : TagLib::File(file)
RIFF::File::File(FileName file, ByteOrder endianness) :
TagLib::File(file),
d(new FilePrivate(endianness))
{
d = new FilePrivate;
d->endianness = endianness;
if(isOpen())
read();
}
RIFF::File::File(IOStream *stream, ByteOrder endianness) : TagLib::File(stream)
RIFF::File::File(IOStream *stream, ByteOrder endianness) :
TagLib::File(stream),
d(new FilePrivate(endianness))
{
d = new FilePrivate;
d->endianness = endianness;
if(isOpen())
read();
}
@ -288,7 +286,7 @@ void RIFF::File::read()
break;
}
if(static_cast<ulonglong>(tell()) + chunkSize > static_cast<ulonglong>(length())) {
if(tell() + chunkSize > length()) {
debug("RIFF::File::read() -- Chunk '" + chunkName + "' has invalid size (larger than the file size)");
setValid(false);
break;
@ -314,8 +312,8 @@ void RIFF::File::read()
chunk.padding = 1;
}
}
d->chunks.push_back(chunk);
d->chunks.push_back(chunk);
}
}

55
taglib/riff/riffutils.h Normal file
View File

@ -0,0 +1,55 @@
/***************************************************************************
copyright : (C) 2015 by Tsuda Kageyu
email : tsuda.kageyu@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., 51 Franklin Street, Fifth Floor, Boston, MA *
* 02110-1301 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_RIFFUTILS_H
#define TAGLIB_RIFFUTILS_H
// THIS FILE IS NOT A PART OF THE TAGLIB API
#ifndef DO_NOT_DOCUMENT // tell Doxygen not to document this header
namespace TagLib
{
namespace RIFF
{
static bool isValidChunkName(const ByteVector &name)
{
if(name.size() != 4)
return false;
for(ByteVector::ConstIterator it = name.begin(); it != name.end(); ++it) {
const uchar c = static_cast<uchar>(*it);
if(c < 32 || 127 < c)
return false;
}
return true;
}
}
}
#endif
#endif

View File

@ -29,6 +29,7 @@
#include "rifffile.h"
#include "infotag.h"
#include "riffutils.h"
using namespace TagLib;
using namespace RIFF::Info;
@ -66,16 +67,16 @@ public:
// public members
////////////////////////////////////////////////////////////////////////////////
RIFF::Info::Tag::Tag(const ByteVector &data)
: TagLib::Tag()
, d(new TagPrivate())
RIFF::Info::Tag::Tag(const ByteVector &data) :
TagLib::Tag(),
d(new TagPrivate())
{
parse(data);
}
RIFF::Info::Tag::Tag()
: TagLib::Tag()
, d(new TagPrivate())
RIFF::Info::Tag::Tag() :
TagLib::Tag(),
d(new TagPrivate())
{
}
@ -189,8 +190,8 @@ String RIFF::Info::Tag::fieldText(const ByteVector &id) const
void RIFF::Info::Tag::setFieldText(const ByteVector &id, const String &s)
{
// id must be four-byte long pure ASCII string.
if(!RIFF::File::isValidChunkName(id))
// id must be four-byte long pure ascii string.
if(!isValidChunkName(id))
return;
if(!s.isEmpty())
@ -209,8 +210,7 @@ ByteVector RIFF::Info::Tag::render() const
{
ByteVector data("INFO");
FieldMap::ConstIterator it = d->fieldMap.begin();
for(; it != d->fieldMap.end(); ++it) {
for(FieldMap::ConstIterator it = d->fieldMap.begin(); it != d->fieldMap.end(); ++it) {
ByteVector text = stringHandler->render(it->second);
if(text.isEmpty())
continue;
@ -251,7 +251,7 @@ void RIFF::Info::Tag::parse(const ByteVector &data)
break;
const ByteVector id = data.mid(p, 4);
if(RIFF::File::isValidChunkName(id)) {
if(isValidChunkName(id)) {
const String text = stringHandler->parse(data.mid(p + 8, size));
d->fieldMap[id] = text;
}

View File

@ -265,15 +265,15 @@ long double toFloat80(const ByteVector &v, size_t offset)
const int exponent = ((bytes[0] & 0x7F) << 8) | bytes[1];
// 64-bit fraction. Leading 1 is explicit.
const ulonglong fraction
= (static_cast<ulonglong>(bytes[2]) << 56)
| (static_cast<ulonglong>(bytes[3]) << 48)
| (static_cast<ulonglong>(bytes[4]) << 40)
| (static_cast<ulonglong>(bytes[5]) << 32)
| (static_cast<ulonglong>(bytes[6]) << 24)
| (static_cast<ulonglong>(bytes[7]) << 16)
| (static_cast<ulonglong>(bytes[8]) << 8)
| (static_cast<ulonglong>(bytes[9]));
const unsigned long long fraction
= (static_cast<unsigned long long>(bytes[2]) << 56)
| (static_cast<unsigned long long>(bytes[3]) << 48)
| (static_cast<unsigned long long>(bytes[4]) << 40)
| (static_cast<unsigned long long>(bytes[5]) << 32)
| (static_cast<unsigned long long>(bytes[6]) << 24)
| (static_cast<unsigned long long>(bytes[7]) << 16)
| (static_cast<unsigned long long>(bytes[8]) << 8)
| (static_cast<unsigned long long>(bytes[9]));
long double val;
if(exponent == 0 && fraction == 0)
@ -382,12 +382,12 @@ ByteVector ByteVector::fromFloat32BE(float value)
ByteVector ByteVector::fromFloat64LE(double value)
{
return fromFloat<double, ulonglong, LittleEndian>(value);
return fromFloat<double, unsigned long long, LittleEndian>(value);
}
ByteVector ByteVector::fromFloat64BE(double value)
{
return fromFloat<double, ulonglong, BigEndian>(value);
return fromFloat<double, unsigned long long, BigEndian>(value);
}
////////////////////////////////////////////////////////////////////////////////
@ -755,12 +755,12 @@ float ByteVector::toFloat32BE(size_t offset) const
double ByteVector::toFloat64LE(size_t offset) const
{
return toFloat<double, ulonglong, LittleEndian>(*this, offset);
return toFloat<double, unsigned long long, LittleEndian>(*this, offset);
}
double ByteVector::toFloat64BE(size_t offset) const
{
return toFloat<double, ulonglong, BigEndian>(*this, offset);
return toFloat<double, unsigned long long, BigEndian>(*this, offset);
}
long double ByteVector::toFloat80LE(size_t offset) const

View File

@ -174,7 +174,7 @@ namespace
if(length > 0) {
if(swap) {
for(size_t i = 0; i < length; ++i)
data[i] = Utils::byteSwap(static_cast<ushort>(s[i]));
data[i] = Utils::byteSwap(static_cast<TagLib::ushort>(s[i]));
}
else {
::wmemcpy(&data[0], s, length);
@ -194,7 +194,7 @@ namespace
}
// Uses memcpy instead of reinterpret_cast to avoid an alignment exception.
ushort bom;
TagLib::ushort bom;
::memcpy(&bom, s, 2);
if(bom == 0xfeff)
@ -215,7 +215,7 @@ namespace
data.resize(length / 2);
for(size_t i = 0; i < length / 2; ++i) {
ushort c;
TagLib::ushort c;
::memcpy(&c, s, 2);
if(swap)
c = Utils::byteSwap(c);

View File

@ -134,7 +134,7 @@ namespace TagLib
/*!
* Reverses the order of bytes in an 64-bit integer.
*/
inline ulonglong byteSwap(ulonglong x)
inline unsigned long long byteSwap(unsigned long long x)
{
#if defined(HAVE_BOOST_BYTESWAP)

BIN
tests/data/blank_video.m4v Normal file

Binary file not shown.

View File

@ -64,14 +64,23 @@ public:
{
RIFF::AIFF::File f(newname.c_str());
CPPUNIT_ASSERT(!f.hasID3v2Tag());
f.tag()->setTitle(L"TitleXXX");
f.save();
CPPUNIT_ASSERT(f.hasID3v2Tag());
}
{
RIFF::AIFF::File f(newname.c_str());
CPPUNIT_ASSERT(f.hasID3v2Tag());
CPPUNIT_ASSERT_EQUAL(String(L"TitleXXX"), f.tag()->title());
f.tag()->setTitle("");
f.save();
CPPUNIT_ASSERT(!f.hasID3v2Tag());
}
{
RIFF::AIFF::File f(newname.c_str());
CPPUNIT_ASSERT(!f.hasID3v2Tag());
}
}

View File

@ -38,6 +38,7 @@ class TestFileRef : public CppUnit::TestFixture
CPPUNIT_TEST(testMP4_1);
CPPUNIT_TEST(testMP4_2);
CPPUNIT_TEST(testMP4_3);
CPPUNIT_TEST(testMP4_4);
CPPUNIT_TEST(testTrueAudio);
CPPUNIT_TEST(testAPE);
CPPUNIT_TEST(testWav);
@ -155,6 +156,11 @@ public:
fileRefSave("no-tags", ".3g2");
}
void testMP4_4()
{
fileRefSave("blank_video", ".m4v");
}
void testWav()
{
fileRefSave("empty", ".wav");

View File

@ -17,6 +17,7 @@ class TestMP4 : public CppUnit::TestFixture
CPPUNIT_TEST_SUITE(TestMP4);
CPPUNIT_TEST(testPropertiesAAC);
CPPUNIT_TEST(testPropertiesALAC);
CPPUNIT_TEST(testPropertiesM4V);
CPPUNIT_TEST(testFreeForm);
CPPUNIT_TEST(testCheckValid);
CPPUNIT_TEST(testHasTag);
@ -64,6 +65,21 @@ public:
CPPUNIT_ASSERT_EQUAL(MP4::AudioProperties::ALAC, f.audioProperties()->codec());
}
void testPropertiesM4V()
{
MP4::File f(TEST_FILE_PATH_C("blank_video.m4v"));
CPPUNIT_ASSERT(f.audioProperties());
CPPUNIT_ASSERT_EQUAL(0, f.audioProperties()->length());
CPPUNIT_ASSERT_EQUAL(0, f.audioProperties()->lengthInSeconds());
CPPUNIT_ASSERT_EQUAL(975, f.audioProperties()->lengthInMilliseconds());
CPPUNIT_ASSERT_EQUAL(96, f.audioProperties()->bitrate());
CPPUNIT_ASSERT_EQUAL(2, f.audioProperties()->channels());
CPPUNIT_ASSERT_EQUAL(44100, f.audioProperties()->sampleRate());
CPPUNIT_ASSERT_EQUAL(16, f.audioProperties()->bitsPerSample());
CPPUNIT_ASSERT_EQUAL(false, f.audioProperties()->isEncrypted());
CPPUNIT_ASSERT_EQUAL(MP4::AudioProperties::AAC, f.audioProperties()->codec());
}
void testCheckValid()
{
MP4::File f(TEST_FILE_PATH_C("empty.aiff"));

View File

@ -91,26 +91,29 @@ public:
{
RIFF::WAV::File f(filename.c_str());
CPPUNIT_ASSERT(f.isValid());
CPPUNIT_ASSERT(!f.hasID3v2Tag());
f.ID3v2Tag()->setTitle(L"Title");
f.ID3v2Tag()->setArtist(L"Artist");
f.save();
CPPUNIT_ASSERT(f.hasID3v2Tag());
}
{
RIFF::WAV::File f(filename.c_str());
CPPUNIT_ASSERT(f.isValid());
CPPUNIT_ASSERT(f.hasID3v2Tag());
CPPUNIT_ASSERT_EQUAL(String(L"Title"), f.ID3v2Tag()->title());
CPPUNIT_ASSERT_EQUAL(String(L"Artist"), f.ID3v2Tag()->artist());
f.ID3v2Tag()->setTitle(L"");
f.ID3v2Tag()->setArtist(L"");
f.save();
CPPUNIT_ASSERT(!f.hasID3v2Tag());
}
{
RIFF::WAV::File f(filename.c_str());
CPPUNIT_ASSERT(f.isValid());
CPPUNIT_ASSERT(!f.hasID3v2Tag());
CPPUNIT_ASSERT_EQUAL(String(L""), f.ID3v2Tag()->title());
CPPUNIT_ASSERT_EQUAL(String(L""), f.ID3v2Tag()->artist());
}
@ -124,26 +127,30 @@ public:
{
RIFF::WAV::File f(filename.c_str());
CPPUNIT_ASSERT(f.isValid());
CPPUNIT_ASSERT(!f.hasInfoTag());
f.InfoTag()->setTitle(L"Title");
f.InfoTag()->setArtist(L"Artist");
f.save();
CPPUNIT_ASSERT(f.hasInfoTag());
}
{
RIFF::WAV::File f(filename.c_str());
CPPUNIT_ASSERT(f.isValid());
CPPUNIT_ASSERT(f.hasInfoTag());
CPPUNIT_ASSERT_EQUAL(String(L"Title"), f.InfoTag()->title());
CPPUNIT_ASSERT_EQUAL(String(L"Artist"), f.InfoTag()->artist());
f.InfoTag()->setTitle(L"");
f.InfoTag()->setArtist(L"");
f.save();
CPPUNIT_ASSERT(!f.hasInfoTag());
}
{
RIFF::WAV::File f(filename.c_str());
CPPUNIT_ASSERT(f.isValid());
CPPUNIT_ASSERT(!f.hasInfoTag());
CPPUNIT_ASSERT_EQUAL(String(L""), f.InfoTag()->title());
CPPUNIT_ASSERT_EQUAL(String(L""), f.InfoTag()->artist());
}