mirror of
https://github.com/taglib/taglib.git
synced 2025-06-03 17:18:11 -04:00
Merge branch 'master' into merge-master-to-taglib2
# Conflicts: # taglib/mp4/mp4atom.cpp # taglib/toolkit/tutils.h # tests/test_apetag.cpp
This commit is contained in:
commit
bd5688ae5f
2
NEWS
2
NEWS
@ -1,7 +1,9 @@
|
||||
============================
|
||||
|
||||
* Added support for WinRT.
|
||||
* Added support for classical music tags of iTunes 12.5.
|
||||
* Dropped support for Windows 9x and NT 4.0 or older.
|
||||
* Fixed reading MP4 atoms with zero length.
|
||||
* Several smaller bug fixes and performance improvements.
|
||||
|
||||
TagLib 1.11.1 (Oct 24, 2016)
|
||||
|
26
README.md
Normal file
26
README.md
Normal file
@ -0,0 +1,26 @@
|
||||
# TagLib
|
||||
|
||||
[](https://travis-ci.org/taglib/taglib)
|
||||
|
||||
### TagLib Audio Meta-Data Library
|
||||
|
||||
http://taglib.org/
|
||||
|
||||
TagLib is a library for reading and editing the meta-data of several
|
||||
popular audio formats. Currently it supports both ID3v1 and [ID3v2][]
|
||||
for MP3 files, [Ogg Vorbis][] comments and ID3 tags and Vorbis comments
|
||||
in [FLAC][], MPC, Speex, WavPack, TrueAudio, WAV, AIFF, MP4 and ASF
|
||||
files.
|
||||
|
||||
TagLib is distributed under the [GNU Lesser General Public License][]
|
||||
(LGPL) and [Mozilla Public License][] (MPL). Essentially that means that
|
||||
it may be used in proprietary applications, but if changes are made to
|
||||
TagLib they must be contributed back to the project. Please review the
|
||||
licenses if you are considering using TagLib in your project.
|
||||
|
||||
[ID3v2]: http://www.id3.org
|
||||
[Ogg Vorbis]: http://vorbis.com/
|
||||
[FLAC]: https://xiph.org/flac/
|
||||
[GNU Lesser General Public License]: http://www.gnu.org/licenses/lgpl.html
|
||||
[Mozilla Public License]: http://www.mozilla.org/MPL/MPL-1.1.html
|
||||
|
@ -48,23 +48,23 @@ using namespace APE;
|
||||
|
||||
namespace
|
||||
{
|
||||
bool isKeyValid(const char *key, size_t length)
|
||||
const unsigned int MinKeyLength = 2;
|
||||
const unsigned int MaxKeyLength = 255;
|
||||
|
||||
bool isKeyValid(const ByteVector &key)
|
||||
{
|
||||
const char *invalidKeys[] = { "ID3", "TAG", "OGGS", "MP+", 0 };
|
||||
|
||||
if(length < 2 || length > 255)
|
||||
return false;
|
||||
|
||||
// only allow printable ASCII including space (32..126)
|
||||
|
||||
for(const char *p = key; p < key + length; ++p) {
|
||||
const int c = static_cast<unsigned char>(*p);
|
||||
for(ByteVector::ConstIterator it = key.begin(); it != key.end(); ++it) {
|
||||
const int c = static_cast<unsigned char>(*it);
|
||||
if(c < 32 || c > 126)
|
||||
return false;
|
||||
}
|
||||
|
||||
for(size_t i = 0; invalidKeys[i] != 0; ++i) {
|
||||
if(Utils::equalsIgnoreCase(key, invalidKeys[i]))
|
||||
if(String(key).upper() == invalidKeys[i])
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -390,11 +390,10 @@ PropertyMap APE::Tag::setProperties(const PropertyMap &origProps)
|
||||
|
||||
bool APE::Tag::checkKey(const String &key)
|
||||
{
|
||||
if(!key.isLatin1())
|
||||
if(key.size() < MinKeyLength || key.size() > MaxKeyLength)
|
||||
return false;
|
||||
|
||||
const std::string data = key.to8Bit(false);
|
||||
return isKeyValid(data.c_str(), data.size());
|
||||
return isKeyValid(key.data(String::UTF8));
|
||||
}
|
||||
|
||||
APE::Footer *APE::Tag::footer() const
|
||||
@ -513,7 +512,10 @@ void APE::Tag::parse(const ByteVector &data)
|
||||
const size_t keyLength = nullPos - pos - 8;
|
||||
const size_t valLegnth = data.toUInt32LE(pos);
|
||||
|
||||
if(isKeyValid(&data[pos + 8], keyLength)){
|
||||
if(keyLength >= MinKeyLength
|
||||
&& keyLength <= MaxKeyLength
|
||||
&& isKeyValid(data.mid(pos + 8, keyLength)))
|
||||
{
|
||||
APE::Item item;
|
||||
item.parse(data.mid(pos));
|
||||
|
||||
|
@ -53,8 +53,14 @@ MP4::Atom::Atom(File *file)
|
||||
|
||||
length = header.toUInt32BE(0);
|
||||
|
||||
if(length == 1)
|
||||
if(length == 0) {
|
||||
// The last atom which extends to the end of the file.
|
||||
length = file->length() - offset;
|
||||
}
|
||||
else if(length == 1) {
|
||||
// The atom has a 64-bit length.
|
||||
length = file->readBlock(8).toInt64BE(0);
|
||||
}
|
||||
|
||||
if(length < 8) {
|
||||
debug("MP4: Invalid atom size");
|
||||
|
@ -72,10 +72,10 @@ MP4::Tag::Tag(TagLib::File *file, MP4::Atoms *atoms) :
|
||||
parseIntPair(atom);
|
||||
}
|
||||
else if(atom->name == "cpil" || atom->name == "pgap" || atom->name == "pcst" ||
|
||||
atom->name == "hdvd") {
|
||||
atom->name == "hdvd" || atom->name == "shwm") {
|
||||
parseBool(atom);
|
||||
}
|
||||
else if(atom->name == "tmpo") {
|
||||
else if(atom->name == "tmpo" || atom->name == "\251mvi" || atom->name == "\251mvc") {
|
||||
parseInt(atom);
|
||||
}
|
||||
else if(atom->name == "tvsn" || atom->name == "tves" || atom->name == "cnID" ||
|
||||
@ -473,10 +473,11 @@ MP4::Tag::save()
|
||||
else if(name == "disk") {
|
||||
data.append(renderIntPairNoTrailing(name.data(String::Latin1), it->second));
|
||||
}
|
||||
else if(name == "cpil" || name == "pgap" || name == "pcst" || name == "hdvd") {
|
||||
else if(name == "cpil" || name == "pgap" || name == "pcst" || name == "hdvd" ||
|
||||
name == "shwm") {
|
||||
data.append(renderBool(name.data(String::Latin1), it->second));
|
||||
}
|
||||
else if(name == "tmpo") {
|
||||
else if(name == "tmpo" || name == "\251mvi" || name == "\251mvc") {
|
||||
data.append(renderInt(name.data(String::Latin1), it->second));
|
||||
}
|
||||
else if(name == "tvsn" || name == "tves" || name == "cnID" ||
|
||||
@ -924,6 +925,11 @@ namespace
|
||||
{ "sonm", "TITLESORT" },
|
||||
{ "soco", "COMPOSERSORT" },
|
||||
{ "sosn", "SHOWSORT" },
|
||||
{ "shwm", "SHOWWORKMOVEMENT" },
|
||||
{ "\251wrk", "WORK" },
|
||||
{ "\251mvn", "MOVEMENTNAME" },
|
||||
{ "\251mvi", "MOVEMENTNUMBER" },
|
||||
{ "\251mvc", "MOVEMENTCOUNT" },
|
||||
{ "----:com.apple.iTunes:MusicBrainz Track Id", "MUSICBRAINZ_TRACKID" },
|
||||
{ "----:com.apple.iTunes:MusicBrainz Artist Id", "MUSICBRAINZ_ARTISTID" },
|
||||
{ "----:com.apple.iTunes:MusicBrainz Album Id", "MUSICBRAINZ_ALBUMID" },
|
||||
@ -977,10 +983,10 @@ PropertyMap MP4::Tag::properties() const
|
||||
}
|
||||
props[key] = value;
|
||||
}
|
||||
else if(key == "BPM") {
|
||||
else if(key == "BPM" || key == "MOVEMENTNUMBER" || key == "MOVEMENTCOUNT") {
|
||||
props[key] = String::number(it->second.toInt());
|
||||
}
|
||||
else if(key == "COMPILATION") {
|
||||
else if(key == "COMPILATION" || key == "SHOWWORKMOVEMENT") {
|
||||
props[key] = String::number(it->second.toBool());
|
||||
}
|
||||
else {
|
||||
@ -1032,11 +1038,11 @@ PropertyMap MP4::Tag::setProperties(const PropertyMap &props)
|
||||
d->items[name] = MP4::Item(first, second);
|
||||
}
|
||||
}
|
||||
else if(it->first == "BPM" && !it->second.isEmpty()) {
|
||||
else if((it->first == "BPM" || it->first == "MOVEMENTNUMBER" || it->first == "MOVEMENTCOUNT") && !it->second.isEmpty()) {
|
||||
int value = it->second.front().toInt();
|
||||
d->items[name] = MP4::Item(value);
|
||||
}
|
||||
else if(it->first == "COMPILATION" && !it->second.isEmpty()) {
|
||||
else if((it->first == "COMPILATION" || it->first == "SHOWWORKMOVEMENT") && !it->second.isEmpty()) {
|
||||
bool value = (it->second.front().toInt() != 0);
|
||||
d->items[name] = MP4::Item(value);
|
||||
}
|
||||
|
@ -37,7 +37,11 @@ class ChapterFrame::ChapterFramePrivate
|
||||
{
|
||||
public:
|
||||
ChapterFramePrivate() :
|
||||
tagHeader(0)
|
||||
tagHeader(0),
|
||||
startTime(0),
|
||||
endTime(0),
|
||||
startOffset(0),
|
||||
endOffset(0)
|
||||
{
|
||||
embeddedFrameList.setAutoDelete(true);
|
||||
}
|
||||
|
@ -111,8 +111,8 @@ Frame *Frame::createTextualFrame(const String &key, const StringList &values) //
|
||||
// check if the key is contained in the key<=>frameID mapping
|
||||
ByteVector frameID = keyToFrameID(key);
|
||||
if(!frameID.isEmpty()) {
|
||||
// Apple proprietary WFED (Podcast URL) is in fact a text frame.
|
||||
if(frameID[0] == 'T' || frameID == "WFED"){ // text frame
|
||||
// Apple proprietary WFED (Podcast URL), MVNM (Movement Name), MVIN (Movement Number) are in fact text frames.
|
||||
if(frameID[0] == 'T' || frameID == "WFED" || frameID == "MVNM" || frameID == "MVIN"){ // text frame
|
||||
TextIdentificationFrame *frame = new TextIdentificationFrame(frameID, String::UTF8);
|
||||
frame->setText(values);
|
||||
return frame;
|
||||
@ -386,6 +386,8 @@ namespace
|
||||
{ "TDES", "PODCASTDESC" },
|
||||
{ "TGID", "PODCASTID" },
|
||||
{ "WFED", "PODCASTURL" },
|
||||
{ "MVNM", "MOVEMENTNAME" },
|
||||
{ "MVIN", "MOVEMENTNUMBER" },
|
||||
};
|
||||
const size_t frameTranslationSize = sizeof(frameTranslation) / sizeof(frameTranslation[0]);
|
||||
|
||||
@ -468,8 +470,8 @@ PropertyMap Frame::asProperties() const
|
||||
// workaround until this function is virtual
|
||||
if(id == "TXXX")
|
||||
return dynamic_cast< const UserTextIdentificationFrame* >(this)->asProperties();
|
||||
// Apple proprietary WFED (Podcast URL) is in fact a text frame.
|
||||
else if(id[0] == 'T' || id == "WFED")
|
||||
// Apple proprietary WFED (Podcast URL), MVNM (Movement Name), MVIN (Movement Number) are in fact text frames.
|
||||
else if(id[0] == 'T' || id == "WFED" || id == "MVNM" || id == "MVIN")
|
||||
return dynamic_cast< const TextIdentificationFrame* >(this)->asProperties();
|
||||
else if(id == "WXXX")
|
||||
return dynamic_cast< const UserUrlLinkFrame* >(this)->asProperties();
|
||||
|
@ -186,8 +186,8 @@ Frame *FrameFactory::createFrame(const ByteVector &origData, const Header *tagHe
|
||||
|
||||
// Text Identification (frames 4.2)
|
||||
|
||||
// Apple proprietary WFED (Podcast URL) is in fact a text frame.
|
||||
if(frameID.startsWith("T") || frameID == "WFED") {
|
||||
// Apple proprietary WFED (Podcast URL), MVNM (Movement Name), MVIN (Movement Number) are in fact text frames.
|
||||
if(frameID.startsWith("T") || frameID == "WFED" || frameID == "MVNM" || frameID == "MVIN") {
|
||||
|
||||
TextIdentificationFrame *f = frameID != "TXXX"
|
||||
? new TextIdentificationFrame(data, header)
|
||||
@ -444,6 +444,8 @@ namespace
|
||||
{ "TDS", "TDES" },
|
||||
{ "TID", "TGID" },
|
||||
{ "WFD", "WFED" },
|
||||
{ "MVN", "MVNM" },
|
||||
{ "MVI", "MVIN" },
|
||||
};
|
||||
const size_t frameConversion2Size = sizeof(frameConversion2) / sizeof(frameConversion2[0]);
|
||||
|
||||
|
@ -349,18 +349,7 @@ void FileStream::seek(long long offset, Position p)
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
DWORD whence;
|
||||
switch(p) {
|
||||
case Beginning:
|
||||
whence = FILE_BEGIN;
|
||||
break;
|
||||
case Current:
|
||||
whence = FILE_CURRENT;
|
||||
break;
|
||||
case End:
|
||||
whence = FILE_END;
|
||||
break;
|
||||
default:
|
||||
if(p != Beginning && p != Current && p != End) {
|
||||
debug("FileStream::seek() -- Invalid Position value.");
|
||||
return;
|
||||
}
|
||||
@ -368,7 +357,7 @@ void FileStream::seek(long long offset, Position p)
|
||||
LARGE_INTEGER liOffset;
|
||||
liOffset.QuadPart = offset;
|
||||
|
||||
if(!SetFilePointerEx(d->file, liOffset, NULL, whence)) {
|
||||
if(!SetFilePointerEx(d->file, liOffset, NULL, static_cast<DWORD>(p))) {
|
||||
debug("FileStream::seek() -- Failed to set the file pointer.");
|
||||
}
|
||||
|
||||
|
@ -222,25 +222,8 @@ namespace TagLib
|
||||
}
|
||||
|
||||
/*!
|
||||
* Returns whether the two strings s1 and s2 are equal, ignoring the case of
|
||||
* the characters.
|
||||
*
|
||||
* We took the trouble to define this one here, since there are some
|
||||
* incompatible variations of case insensitive strcmp().
|
||||
* Returns the integer byte order of the system.
|
||||
*/
|
||||
inline bool equalsIgnoreCase(const char *s1, const char *s2)
|
||||
{
|
||||
while(*s1 != '\0' && *s2 != '\0' && ::tolower(*s1) == ::tolower(*s2)) {
|
||||
s1++;
|
||||
s2++;
|
||||
}
|
||||
|
||||
return (*s1 == '\0' && *s2 == '\0');
|
||||
}
|
||||
|
||||
/*!
|
||||
* Returns the integer byte order of the system.
|
||||
*/
|
||||
inline ByteOrder systemByteOrder()
|
||||
{
|
||||
union {
|
||||
|
BIN
tests/data/zero-length-mdat.m4a
Normal file
BIN
tests/data/zero-length-mdat.m4a
Normal file
Binary file not shown.
@ -114,18 +114,21 @@ public:
|
||||
PropertyMap properties;
|
||||
properties["A"] = String("invalid key: one character");
|
||||
properties["MP+"] = String("invalid key: forbidden string");
|
||||
properties[L"\x1234\x3456"] = String("invalid key: Unicode");
|
||||
properties["A B~C"] = String("valid key: space and tilde");
|
||||
properties["ARTIST"] = String("valid key: normal one");
|
||||
|
||||
APE::Tag tag;
|
||||
PropertyMap unsuccessful = tag.setProperties(properties);
|
||||
CPPUNIT_ASSERT_EQUAL((size_t)2, unsuccessful.size());
|
||||
CPPUNIT_ASSERT_EQUAL((size_t)3, unsuccessful.size());
|
||||
CPPUNIT_ASSERT(unsuccessful.contains("A"));
|
||||
CPPUNIT_ASSERT(unsuccessful.contains("MP+"));
|
||||
CPPUNIT_ASSERT(unsuccessful.contains(L"\x1234\x3456"));
|
||||
|
||||
CPPUNIT_ASSERT_EQUAL((size_t)2, tag.itemListMap().size());
|
||||
tag.addValue("VALID KEY", "Test Value 1");
|
||||
tag.addValue("INVALID KEY \x7f", "Test Value 2");
|
||||
tag.addValue(L"INVALID KEY \x1234\x3456", "Test Value 3");
|
||||
CPPUNIT_ASSERT_EQUAL((size_t)3, tag.itemListMap().size());
|
||||
}
|
||||
|
||||
|
@ -111,6 +111,7 @@ class TestID3v2 : public CppUnit::TestFixture
|
||||
CPPUNIT_TEST(testW000);
|
||||
CPPUNIT_TEST(testPropertyInterface);
|
||||
CPPUNIT_TEST(testPropertyInterface2);
|
||||
CPPUNIT_TEST(testPropertiesMovement);
|
||||
CPPUNIT_TEST(testDeleteFrame);
|
||||
CPPUNIT_TEST(testSaveAndStripID3v1ShouldNotAddFrameFromID3v1ToId3v2);
|
||||
CPPUNIT_TEST(testParseChapterFrame);
|
||||
@ -932,6 +933,48 @@ public:
|
||||
CPPUNIT_ASSERT_EQUAL(frame6, ID3v2::UniqueFileIdentifierFrame::findByOwner(&tag, "http://musicbrainz.org"));
|
||||
}
|
||||
|
||||
void testPropertiesMovement()
|
||||
{
|
||||
ID3v2::Tag tag;
|
||||
ID3v2::TextIdentificationFrame *frameMvnm = new ID3v2::TextIdentificationFrame("MVNM");
|
||||
frameMvnm->setText("Movement Name");
|
||||
tag.addFrame(frameMvnm);
|
||||
|
||||
ID3v2::TextIdentificationFrame *frameMvin = new ID3v2::TextIdentificationFrame("MVIN");
|
||||
frameMvin->setText("2/3");
|
||||
tag.addFrame(frameMvin);
|
||||
|
||||
PropertyMap properties = tag.properties();
|
||||
CPPUNIT_ASSERT(properties.contains("MOVEMENTNAME"));
|
||||
CPPUNIT_ASSERT(properties.contains("MOVEMENTNUMBER"));
|
||||
CPPUNIT_ASSERT_EQUAL(String("Movement Name"), properties["MOVEMENTNAME"].front());
|
||||
CPPUNIT_ASSERT_EQUAL(String("2/3"), properties["MOVEMENTNUMBER"].front());
|
||||
|
||||
ByteVector frameDataMvnm("MVNM"
|
||||
"\x00\x00\x00\x0e"
|
||||
"\x00\x00"
|
||||
"\x00"
|
||||
"Movement Name", 24);
|
||||
CPPUNIT_ASSERT_EQUAL(frameDataMvnm, frameMvnm->render());
|
||||
ByteVector frameDataMvin("MVIN"
|
||||
"\x00\x00\x00\x04"
|
||||
"\x00\x00"
|
||||
"\x00"
|
||||
"2/3", 14);
|
||||
CPPUNIT_ASSERT_EQUAL(frameDataMvin, frameMvin->render());
|
||||
|
||||
ID3v2::Header header;
|
||||
ID3v2::FrameFactory *factory = ID3v2::FrameFactory::instance();
|
||||
ID3v2::TextIdentificationFrame *parsedFrameMvnm =
|
||||
dynamic_cast<ID3v2::TextIdentificationFrame *>(factory->createFrame(frameDataMvnm, &header));
|
||||
ID3v2::TextIdentificationFrame *parsedFrameMvin =
|
||||
dynamic_cast<ID3v2::TextIdentificationFrame *>(factory->createFrame(frameDataMvin, &header));
|
||||
CPPUNIT_ASSERT(parsedFrameMvnm);
|
||||
CPPUNIT_ASSERT(parsedFrameMvin);
|
||||
CPPUNIT_ASSERT_EQUAL(String("Movement Name"), parsedFrameMvnm->toString());
|
||||
CPPUNIT_ASSERT_EQUAL(String("2/3"), parsedFrameMvin->toString());
|
||||
}
|
||||
|
||||
void testDeleteFrame()
|
||||
{
|
||||
ScopedFileCopy copy("rare_frames", ".mp3");
|
||||
|
@ -55,8 +55,10 @@ class TestMP4 : public CppUnit::TestFixture
|
||||
CPPUNIT_TEST(testCovrWrite);
|
||||
CPPUNIT_TEST(testCovrRead2);
|
||||
CPPUNIT_TEST(testProperties);
|
||||
CPPUNIT_TEST(testPropertiesMovement);
|
||||
CPPUNIT_TEST(testFuzzedFile);
|
||||
CPPUNIT_TEST(testRepeatedSave);
|
||||
CPPUNIT_TEST(testWithZeroLengthAtom);
|
||||
CPPUNIT_TEST_SUITE_END();
|
||||
|
||||
public:
|
||||
@ -378,6 +380,58 @@ public:
|
||||
f.setProperties(tags);
|
||||
}
|
||||
|
||||
void testPropertiesMovement()
|
||||
{
|
||||
MP4::File f(TEST_FILE_PATH_C("has-tags.m4a"));
|
||||
|
||||
PropertyMap tags = f.properties();
|
||||
|
||||
tags["WORK"] = StringList("Foo");
|
||||
tags["MOVEMENTNAME"] = StringList("Bar");
|
||||
tags["MOVEMENTNUMBER"] = StringList("2");
|
||||
tags["MOVEMENTCOUNT"] = StringList("3");
|
||||
tags["SHOWWORKMOVEMENT"] = StringList("1");
|
||||
f.setProperties(tags);
|
||||
|
||||
tags = f.properties();
|
||||
|
||||
CPPUNIT_ASSERT(f.tag()->contains("\251wrk"));
|
||||
CPPUNIT_ASSERT_EQUAL(StringList("Foo"), f.tag()->item("\251wrk").toStringList());
|
||||
CPPUNIT_ASSERT_EQUAL(StringList("Foo"), tags["WORK"]);
|
||||
|
||||
CPPUNIT_ASSERT(f.tag()->contains("\251mvn"));
|
||||
CPPUNIT_ASSERT_EQUAL(StringList("Bar"), f.tag()->item("\251mvn").toStringList());
|
||||
CPPUNIT_ASSERT_EQUAL(StringList("Bar"), tags["MOVEMENTNAME"]);
|
||||
|
||||
CPPUNIT_ASSERT(f.tag()->contains("\251mvi"));
|
||||
CPPUNIT_ASSERT_EQUAL(2, f.tag()->item("\251mvi").toInt());
|
||||
CPPUNIT_ASSERT_EQUAL(StringList("2"), tags["MOVEMENTNUMBER"]);
|
||||
|
||||
CPPUNIT_ASSERT(f.tag()->contains("\251mvc"));
|
||||
CPPUNIT_ASSERT_EQUAL(3, f.tag()->item("\251mvc").toInt());
|
||||
CPPUNIT_ASSERT_EQUAL(StringList("3"), tags["MOVEMENTCOUNT"]);
|
||||
|
||||
CPPUNIT_ASSERT(f.tag()->contains("shwm"));
|
||||
CPPUNIT_ASSERT_EQUAL(true, f.tag()->item("shwm").toBool());
|
||||
CPPUNIT_ASSERT_EQUAL(StringList("1"), tags["SHOWWORKMOVEMENT"]);
|
||||
|
||||
tags["SHOWWORKMOVEMENT"] = StringList("0");
|
||||
f.setProperties(tags);
|
||||
|
||||
tags = f.properties();
|
||||
|
||||
CPPUNIT_ASSERT(f.tag()->contains("shwm"));
|
||||
CPPUNIT_ASSERT_EQUAL(false, f.tag()->item("shwm").toBool());
|
||||
CPPUNIT_ASSERT_EQUAL(StringList("0"), tags["SHOWWORKMOVEMENT"]);
|
||||
|
||||
tags["WORK"] = StringList();
|
||||
tags["MOVEMENTNAME"] = StringList();
|
||||
tags["MOVEMENTNUMBER"] = StringList();
|
||||
tags["MOVEMENTCOUNT"] = StringList();
|
||||
tags["SHOWWORKMOVEMENT"] = StringList();
|
||||
f.setProperties(tags);
|
||||
}
|
||||
|
||||
void testFuzzedFile()
|
||||
{
|
||||
MP4::File f(TEST_FILE_PATH_C("infloop.m4a"));
|
||||
@ -395,6 +449,15 @@ public:
|
||||
CPPUNIT_ASSERT_EQUAL(2862LL, f.find("0123456789"));
|
||||
CPPUNIT_ASSERT_EQUAL(-1LL, f.find("0123456789", 2863));
|
||||
}
|
||||
|
||||
void testWithZeroLengthAtom()
|
||||
{
|
||||
MP4::File f(TEST_FILE_PATH_C("zero-length-mdat.m4a"));
|
||||
CPPUNIT_ASSERT(f.isValid());
|
||||
CPPUNIT_ASSERT_EQUAL(1115, f.audioProperties()->lengthInMilliseconds());
|
||||
CPPUNIT_ASSERT_EQUAL(22050, f.audioProperties()->sampleRate());
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
CPPUNIT_TEST_SUITE_REGISTRATION(TestMP4);
|
||||
|
Loading…
x
Reference in New Issue
Block a user