mirror of
https://github.com/taglib/taglib.git
synced 2025-05-27 21:20:26 -04:00
Added functions for work with embedded frames.
Added embedded frames parsing. Added embedded frames rendering. Modified constructor of CHAP and CTOC frame, so it can accept list of embedded frames. Added unit tests for CHAP and CTOC frames parsing and rendering (with support of embedded frames). Fixed bugs in rendering of CTOC frames. Added functions for adding and removing child elements in CTOC frames.
This commit is contained in:
parent
140fb2b3f6
commit
5c5c89e8d9
@ -26,8 +26,8 @@
|
||||
#include <tbytevectorlist.h>
|
||||
#include <tpropertymap.h>
|
||||
#include <tdebug.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "id3v2tag.h"
|
||||
#include "chapterframe.h"
|
||||
|
||||
using namespace TagLib;
|
||||
@ -41,6 +41,9 @@ public:
|
||||
uint endTime;
|
||||
uint startOffset;
|
||||
uint endOffset;
|
||||
const FrameFactory *factory;
|
||||
FrameListMap embeddedFrameListMap;
|
||||
FrameList embeddedFrameList;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
@ -51,10 +54,11 @@ ChapterFrame::ChapterFrame(const ByteVector &data) :
|
||||
ID3v2::Frame(data)
|
||||
{
|
||||
d = new ChapterFramePrivate;
|
||||
d->factory = FrameFactory::instance();
|
||||
setData(data);
|
||||
}
|
||||
|
||||
ChapterFrame::ChapterFrame(const ByteVector &eID, const uint &sT, const uint &eT, const uint &sO, const uint &eO) :
|
||||
ChapterFrame::ChapterFrame(const ByteVector &eID, const uint &sT, const uint &eT, const uint &sO, const uint &eO, const FrameList &eF) :
|
||||
ID3v2::Frame("CHAP")
|
||||
{
|
||||
d = new ChapterFramePrivate;
|
||||
@ -63,6 +67,10 @@ ChapterFrame::ChapterFrame(const ByteVector &eID, const uint &sT, const uint &eT
|
||||
d->endTime = eT;
|
||||
d->startOffset = sO;
|
||||
d->endOffset = eO;
|
||||
FrameList l = eF;
|
||||
for(FrameList::Iterator it = l.begin(); it != l.end(); ++it)
|
||||
addEmbeddedFrame(*it);
|
||||
d->factory = FrameFactory::instance();
|
||||
}
|
||||
|
||||
ChapterFrame::~ChapterFrame()
|
||||
@ -122,6 +130,49 @@ void ChapterFrame::setEndOffset(const uint &eO)
|
||||
d->endOffset = eO;
|
||||
}
|
||||
|
||||
const FrameListMap &ChapterFrame::embeddedFrameListMap() const
|
||||
{
|
||||
return d->embeddedFrameListMap;
|
||||
}
|
||||
|
||||
const FrameList &ChapterFrame::embeddedFrameList() const
|
||||
{
|
||||
return d->embeddedFrameList;
|
||||
}
|
||||
|
||||
const FrameList &ChapterFrame::embeddedFrameList(const ByteVector &frameID) const
|
||||
{
|
||||
return d->embeddedFrameListMap[frameID];
|
||||
}
|
||||
|
||||
void ChapterFrame::addEmbeddedFrame(Frame *frame)
|
||||
{
|
||||
d->embeddedFrameList.append(frame);
|
||||
d->embeddedFrameListMap[frame->frameID()].append(frame);
|
||||
}
|
||||
|
||||
void ChapterFrame::removeEmbeddedFrame(Frame *frame, bool del)
|
||||
{
|
||||
// remove the frame from the frame list
|
||||
FrameList::Iterator it = d->embeddedFrameList.find(frame);
|
||||
d->embeddedFrameList.erase(it);
|
||||
|
||||
// ...and from the frame list map
|
||||
it = d->embeddedFrameListMap[frame->frameID()].find(frame);
|
||||
d->embeddedFrameListMap[frame->frameID()].erase(it);
|
||||
|
||||
// ...and delete as desired
|
||||
if(del)
|
||||
delete frame;
|
||||
}
|
||||
|
||||
void ChapterFrame::removeEmbeddedFrames(const ByteVector &id)
|
||||
{
|
||||
FrameList l = d->embeddedFrameListMap[id];
|
||||
for(FrameList::Iterator it = l.begin(); it != l.end(); ++it)
|
||||
removeEmbeddedFrame(*it, true);
|
||||
}
|
||||
|
||||
String ChapterFrame::toString() const
|
||||
{
|
||||
return String::null;
|
||||
@ -154,12 +205,13 @@ ChapterFrame *ChapterFrame::findByElementID(const Tag *tag, const ByteVector &eI
|
||||
|
||||
void ChapterFrame::parseFields(const ByteVector &data)
|
||||
{
|
||||
if(data.size() < 18) {
|
||||
debug("An CHAP frame must contain at least 18 bytes (1 byte element ID terminated by null and 4x4 bytes for start and end time and offset).");
|
||||
uint size = data.size();
|
||||
if(size < 18) {
|
||||
debug("A CHAP frame must contain at least 18 bytes (1 byte element ID terminated by null and 4x4 bytes for start and end time and offset).");
|
||||
return;
|
||||
}
|
||||
|
||||
int pos = 0;
|
||||
int pos = 0, embPos = 0;
|
||||
d->elementID = readStringField(data, String::Latin1, &pos).data(String::Latin1);
|
||||
d->elementID.append(char(0));
|
||||
d->startTime = data.mid(pos, 4).toUInt(true);
|
||||
@ -169,6 +221,24 @@ void ChapterFrame::parseFields(const ByteVector &data)
|
||||
d->startOffset = data.mid(pos, 4).toUInt(true);
|
||||
pos += 4;
|
||||
d->endOffset = data.mid(pos, 4).toUInt(true);
|
||||
pos += 4;
|
||||
size -= pos;
|
||||
while((uint)embPos < size - Frame::headerSize(4))
|
||||
{
|
||||
Frame *frame = d->factory->createFrame(data.mid(pos + embPos));
|
||||
|
||||
if(!frame)
|
||||
return;
|
||||
|
||||
// Checks to make sure that frame parsed correctly.
|
||||
if(frame->size() <= 0) {
|
||||
delete frame;
|
||||
return;
|
||||
}
|
||||
|
||||
embPos += frame->size() + Frame::headerSize(4);
|
||||
addEmbeddedFrame(frame);
|
||||
}
|
||||
}
|
||||
|
||||
ByteVector ChapterFrame::renderFields() const
|
||||
@ -180,7 +250,10 @@ ByteVector ChapterFrame::renderFields() const
|
||||
data.append(ByteVector::fromUInt(d->endTime, true));
|
||||
data.append(ByteVector::fromUInt(d->startOffset, true));
|
||||
data.append(ByteVector::fromUInt(d->endOffset, true));
|
||||
|
||||
FrameList l = d->embeddedFrameList;
|
||||
for(FrameList::Iterator it = l.begin(); it != l.end(); ++it)
|
||||
data.append((*it)->render());
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
@ -188,5 +261,6 @@ ChapterFrame::ChapterFrame(const ByteVector &data, Header *h) :
|
||||
Frame(h)
|
||||
{
|
||||
d = new ChapterFramePrivate;
|
||||
d->factory = FrameFactory::instance();
|
||||
parseFields(fieldData(data));
|
||||
}
|
||||
|
@ -26,6 +26,7 @@
|
||||
#ifndef TAGLIB_CHAPTERFRAME
|
||||
#define TAGLIB_CHAPTERFRAME
|
||||
|
||||
#include "id3v2tag.h"
|
||||
#include "id3v2frame.h"
|
||||
#include "taglib_export.h"
|
||||
|
||||
@ -52,10 +53,10 @@ namespace TagLib {
|
||||
|
||||
/*!
|
||||
* Creates a chapter frame with the element ID \a eID,
|
||||
* start time \a sT, end time \a eT, start offset \a sO
|
||||
* and end offset \a eO.
|
||||
* start time \a sT, end time \a eT, start offset \a sO,
|
||||
* end offset \a eO and embedded frames, that are in \a eF.
|
||||
*/
|
||||
ChapterFrame(const ByteVector &eID, const uint &sT, const uint &eT, const uint &sO, const uint &eO);
|
||||
ChapterFrame(const ByteVector &eID, const uint &sT, const uint &eT, const uint &sO, const uint &eO, const FrameList &eF);
|
||||
|
||||
/*!
|
||||
* Destroys the frame.
|
||||
@ -139,6 +140,75 @@ namespace TagLib {
|
||||
* \see endOffset()
|
||||
*/
|
||||
void setEndOffset(const uint &eO);
|
||||
|
||||
/*!
|
||||
* Returns a reference to the frame list map. This is an FrameListMap of
|
||||
* all of the frames embedded in the CHAP frame.
|
||||
*
|
||||
* This is the most convenient structure for accessing the CHAP frame's
|
||||
* embedded frames. Many frame types allow multiple instances of the same
|
||||
* frame type so this is a map of lists. In most cases however there will
|
||||
* only be a single frame of a certain type.
|
||||
*
|
||||
* \warning You should not modify this data structure directly, instead
|
||||
* use addEmbeddedFrame() and removeEmbeddedFrame().
|
||||
*
|
||||
* \see embeddedFrameList()
|
||||
*/
|
||||
const FrameListMap &embeddedFrameListMap() const;
|
||||
|
||||
/*!
|
||||
* Returns a reference to the embedded frame list. This is an FrameList
|
||||
* of all of the frames embedded in the CHAP frame in the order that they
|
||||
* were parsed.
|
||||
*
|
||||
* This can be useful if for example you want iterate over the CHAP frame's
|
||||
* embedded frames in the order that they occur in the CHAP frame.
|
||||
*
|
||||
* \warning You should not modify this data structure directly, instead
|
||||
* use addEmbeddedFrame() and removeEmbeddedFrame().
|
||||
*/
|
||||
const FrameList &embeddedFrameList() const;
|
||||
|
||||
/*!
|
||||
* Returns the embedded frame list for frames with the id \a frameID
|
||||
* or an empty list if there are no embedded frames of that type. This
|
||||
* is just a convenience and is equivalent to:
|
||||
*
|
||||
* \code
|
||||
* embeddedFrameListMap()[frameID];
|
||||
* \endcode
|
||||
*
|
||||
* \see embeddedFrameListMap()
|
||||
*/
|
||||
const FrameList &embeddedFrameList(const ByteVector &frameID) const;
|
||||
|
||||
/*!
|
||||
* Add an embedded frame to the CHAP frame. At this point the CHAP frame
|
||||
* takes ownership of the embedded frame and will handle freeing its memory.
|
||||
*
|
||||
* \note Using this method will invalidate any pointers on the list
|
||||
* returned by embeddedFrameList()
|
||||
*/
|
||||
void addEmbeddedFrame(Frame *frame);
|
||||
|
||||
/*!
|
||||
* Remove an embedded frame from the CHAP frame. If \a del is true the frame's
|
||||
* memory will be freed; if it is false, it must be deleted by the user.
|
||||
*
|
||||
* \note Using this method will invalidate any pointers on the list
|
||||
* returned by embeddedFrameList()
|
||||
*/
|
||||
void removeEmbeddedFrame(Frame *frame, bool del = true);
|
||||
|
||||
/*!
|
||||
* Remove all embedded frames of type \a id from the CHAP frame and free their
|
||||
* memory.
|
||||
*
|
||||
* \note Using this method will invalidate any pointers on the list
|
||||
* returned by embeddedFrameList()
|
||||
*/
|
||||
void removeEmbeddedFrames(const ByteVector &id);
|
||||
|
||||
virtual String toString() const;
|
||||
|
||||
|
@ -27,7 +27,6 @@
|
||||
#include <tpropertymap.h>
|
||||
#include <tdebug.h>
|
||||
|
||||
#include "id3v2tag.h"
|
||||
#include "tableofcontentsframe.h"
|
||||
|
||||
using namespace TagLib;
|
||||
@ -40,6 +39,9 @@ public:
|
||||
bool isTopLevel;
|
||||
bool isOrdered;
|
||||
ByteVectorList childElements;
|
||||
const FrameFactory *factory;
|
||||
FrameListMap embeddedFrameListMap;
|
||||
FrameList embeddedFrameList;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
@ -50,15 +52,20 @@ TableOfContentsFrame::TableOfContentsFrame(const ByteVector &data) :
|
||||
ID3v2::Frame(data)
|
||||
{
|
||||
d = new TableOfContentsFramePrivate;
|
||||
d->factory = FrameFactory::instance();
|
||||
setData(data);
|
||||
}
|
||||
|
||||
TableOfContentsFrame::TableOfContentsFrame(const ByteVector &eID, const ByteVectorList &ch) :
|
||||
TableOfContentsFrame::TableOfContentsFrame(const ByteVector &eID, const ByteVectorList &ch, const FrameList &eF) :
|
||||
ID3v2::Frame("CTOC")
|
||||
{
|
||||
d = new TableOfContentsFramePrivate;
|
||||
d->elementID = eID;
|
||||
d->childElements = ch;
|
||||
FrameList l = eF;
|
||||
for(FrameList::Iterator it = l.begin(); it != l.end(); ++it)
|
||||
addEmbeddedFrame(*it);
|
||||
d->factory = FrameFactory::instance();
|
||||
}
|
||||
|
||||
TableOfContentsFrame::~TableOfContentsFrame()
|
||||
@ -113,6 +120,60 @@ void TableOfContentsFrame::setChildElements(const ByteVectorList &l)
|
||||
d->childElements = l;
|
||||
}
|
||||
|
||||
void TableOfContentsFrame::addChildElement(const ByteVector &cE)
|
||||
{
|
||||
d->childElements.append(cE);
|
||||
}
|
||||
|
||||
void TableOfContentsFrame::removeChildElement(const ByteVector &cE)
|
||||
{
|
||||
ByteVectorList::Iterator it = d->childElements.find(cE);
|
||||
d->childElements.erase(it);
|
||||
}
|
||||
|
||||
const FrameListMap &TableOfContentsFrame::embeddedFrameListMap() const
|
||||
{
|
||||
return d->embeddedFrameListMap;
|
||||
}
|
||||
|
||||
const FrameList &TableOfContentsFrame::embeddedFrameList() const
|
||||
{
|
||||
return d->embeddedFrameList;
|
||||
}
|
||||
|
||||
const FrameList &TableOfContentsFrame::embeddedFrameList(const ByteVector &frameID) const
|
||||
{
|
||||
return d->embeddedFrameListMap[frameID];
|
||||
}
|
||||
|
||||
void TableOfContentsFrame::addEmbeddedFrame(Frame *frame)
|
||||
{
|
||||
d->embeddedFrameList.append(frame);
|
||||
d->embeddedFrameListMap[frame->frameID()].append(frame);
|
||||
}
|
||||
|
||||
void TableOfContentsFrame::removeEmbeddedFrame(Frame *frame, bool del)
|
||||
{
|
||||
// remove the frame from the frame list
|
||||
FrameList::Iterator it = d->embeddedFrameList.find(frame);
|
||||
d->embeddedFrameList.erase(it);
|
||||
|
||||
// ...and from the frame list map
|
||||
it = d->embeddedFrameListMap[frame->frameID()].find(frame);
|
||||
d->embeddedFrameListMap[frame->frameID()].erase(it);
|
||||
|
||||
// ...and delete as desired
|
||||
if(del)
|
||||
delete frame;
|
||||
}
|
||||
|
||||
void TableOfContentsFrame::removeEmbeddedFrames(const ByteVector &id)
|
||||
{
|
||||
FrameList l = d->embeddedFrameListMap[id];
|
||||
for(FrameList::Iterator it = l.begin(); it != l.end(); ++it)
|
||||
removeEmbeddedFrame(*it, true);
|
||||
}
|
||||
|
||||
String TableOfContentsFrame::toString() const
|
||||
{
|
||||
return String::null;
|
||||
@ -161,12 +222,13 @@ TableOfContentsFrame *TableOfContentsFrame::findTopLevel(const Tag *tag) // stat
|
||||
|
||||
void TableOfContentsFrame::parseFields(const ByteVector &data)
|
||||
{
|
||||
if(data.size() < 6) {
|
||||
debug("An CTOC frame must contain at least 6 bytes (1 byte element ID terminated by null, 1 byte flags, 1 byte entry count and 1 byte child element ID terminated by null.");
|
||||
uint size = data.size();
|
||||
if(size < 6) {
|
||||
debug("A CTOC frame must contain at least 6 bytes (1 byte element ID terminated by null, 1 byte flags, 1 byte entry count and 1 byte child element ID terminated by null.");
|
||||
return;
|
||||
}
|
||||
|
||||
int pos = 0;
|
||||
int pos = 0, embPos = 0;
|
||||
d->elementID = readStringField(data, String::Latin1, &pos).data(String::Latin1);
|
||||
d->elementID.append(char(0));
|
||||
d->isTopLevel = (data.at(pos) & 2) > 0;
|
||||
@ -178,6 +240,24 @@ void TableOfContentsFrame::parseFields(const ByteVector &data)
|
||||
childElementID.append(char(0));
|
||||
d->childElements.append(childElementID);
|
||||
}
|
||||
|
||||
size -= pos;
|
||||
while((uint)embPos < size - Frame::headerSize(4))
|
||||
{
|
||||
Frame *frame = d->factory->createFrame(data.mid(pos + embPos));
|
||||
|
||||
if(!frame)
|
||||
return;
|
||||
|
||||
// Checks to make sure that frame parsed correctly.
|
||||
if(frame->size() <= 0) {
|
||||
delete frame;
|
||||
return;
|
||||
}
|
||||
|
||||
embPos += frame->size() + Frame::headerSize(4);
|
||||
addEmbeddedFrame(frame);
|
||||
}
|
||||
}
|
||||
|
||||
ByteVector TableOfContentsFrame::renderFields() const
|
||||
@ -185,7 +265,6 @@ ByteVector TableOfContentsFrame::renderFields() const
|
||||
ByteVector data;
|
||||
|
||||
data.append(d->elementID);
|
||||
data.append(char(0));
|
||||
char flags = 0;
|
||||
if(d->isTopLevel)
|
||||
flags += 2;
|
||||
@ -196,9 +275,12 @@ ByteVector TableOfContentsFrame::renderFields() const
|
||||
ByteVectorList::ConstIterator it = d->childElements.begin();
|
||||
while(it != d->childElements.end()) {
|
||||
data.append(*it);
|
||||
data.append(char(0));
|
||||
//data.append(char(0));
|
||||
it++;
|
||||
}
|
||||
FrameList l = d->embeddedFrameList;
|
||||
for(FrameList::Iterator it = l.begin(); it != l.end(); ++it)
|
||||
data.append((*it)->render());
|
||||
|
||||
return data;
|
||||
}
|
||||
@ -207,5 +289,6 @@ TableOfContentsFrame::TableOfContentsFrame(const ByteVector &data, Header *h) :
|
||||
Frame(h)
|
||||
{
|
||||
d = new TableOfContentsFramePrivate;
|
||||
d->factory = FrameFactory::instance();
|
||||
parseFields(fieldData(data));
|
||||
}
|
||||
|
@ -26,6 +26,7 @@
|
||||
#ifndef TAGLIB_TABLEOFCONTENTSFRAME
|
||||
#define TAGLIB_TABLEOFCONTENTSFRAME
|
||||
|
||||
#include "id3v2tag.h"
|
||||
#include "id3v2frame.h"
|
||||
|
||||
namespace TagLib {
|
||||
@ -50,10 +51,10 @@ namespace TagLib {
|
||||
TableOfContentsFrame(const ByteVector &data);
|
||||
|
||||
/*!
|
||||
* Creates a table of contents frame with the element ID \a eID and
|
||||
* the child elements \a ch.
|
||||
* Creates a table of contents frame with the element ID \a eID,
|
||||
* the child elements \a ch and embedded frames, that are in \a eF.
|
||||
*/
|
||||
TableOfContentsFrame(const ByteVector &eID, const ByteVectorList &ch);
|
||||
TableOfContentsFrame(const ByteVector &eID, const ByteVectorList &ch, const FrameList &eF);
|
||||
|
||||
/*!
|
||||
* Destroys the frame.
|
||||
@ -129,6 +130,89 @@ namespace TagLib {
|
||||
* \see childElements()
|
||||
*/
|
||||
void setChildElements(const ByteVectorList &l);
|
||||
|
||||
/*!
|
||||
* Adds \a cE to list of child elements of the frame.
|
||||
*
|
||||
* \see childElements()
|
||||
*/
|
||||
void addChildElement(const ByteVector &cE);
|
||||
|
||||
/*!
|
||||
* Removes \a cE to list of child elements of the frame.
|
||||
*
|
||||
* \see childElements()
|
||||
*/
|
||||
void removeChildElement(const ByteVector &cE);
|
||||
|
||||
/*!
|
||||
* Returns a reference to the frame list map. This is an FrameListMap of
|
||||
* all of the frames embedded in the CTOC frame.
|
||||
*
|
||||
* This is the most convenient structure for accessing the CTOC frame's
|
||||
* embedded frames. Many frame types allow multiple instances of the same
|
||||
* frame type so this is a map of lists. In most cases however there will
|
||||
* only be a single frame of a certain type.
|
||||
*
|
||||
* \warning You should not modify this data structure directly, instead
|
||||
* use addEmbeddedFrame() and removeEmbeddedFrame().
|
||||
*
|
||||
* \see embeddedFrameList()
|
||||
*/
|
||||
const FrameListMap &embeddedFrameListMap() const;
|
||||
|
||||
/*!
|
||||
* Returns a reference to the embedded frame list. This is an FrameList
|
||||
* of all of the frames embedded in the CTOC frame in the order that they
|
||||
* were parsed.
|
||||
*
|
||||
* This can be useful if for example you want iterate over the CTOC frame's
|
||||
* embedded frames in the order that they occur in the CTOC frame.
|
||||
*
|
||||
* \warning You should not modify this data structure directly, instead
|
||||
* use addEmbeddedFrame() and removeEmbeddedFrame().
|
||||
*/
|
||||
const FrameList &embeddedFrameList() const;
|
||||
|
||||
/*!
|
||||
* Returns the embedded frame list for frames with the id \a frameID
|
||||
* or an empty list if there are no embedded frames of that type. This
|
||||
* is just a convenience and is equivalent to:
|
||||
*
|
||||
* \code
|
||||
* embeddedFrameListMap()[frameID];
|
||||
* \endcode
|
||||
*
|
||||
* \see embeddedFrameListMap()
|
||||
*/
|
||||
const FrameList &embeddedFrameList(const ByteVector &frameID) const;
|
||||
|
||||
/*!
|
||||
* Add an embedded frame to the CTOC frame. At this point the CTOC frame
|
||||
* takes ownership of the embedded frame and will handle freeing its memory.
|
||||
*
|
||||
* \note Using this method will invalidate any pointers on the list
|
||||
* returned by embeddedFrameList()
|
||||
*/
|
||||
void addEmbeddedFrame(Frame *frame);
|
||||
|
||||
/*!
|
||||
* Remove an embedded frame from the CTOC frame. If \a del is true the frame's
|
||||
* memory will be freed; if it is false, it must be deleted by the user.
|
||||
*
|
||||
* \note Using this method will invalidate any pointers on the list
|
||||
* returned by embeddedFrameList()
|
||||
*/
|
||||
void removeEmbeddedFrame(Frame *frame, bool del = true);
|
||||
|
||||
/*!
|
||||
* Remove all embedded frames of type \a id from the CTOC frame and free their
|
||||
* memory.
|
||||
*
|
||||
* \note Using this method will invalidate any pointers on the list
|
||||
* returned by embeddedFrameList()
|
||||
*/
|
||||
void removeEmbeddedFrames(const ByteVector &id);
|
||||
|
||||
virtual String toString() const;
|
||||
|
||||
|
@ -84,14 +84,16 @@ class TestID3v2 : public CppUnit::TestFixture
|
||||
CPPUNIT_TEST(testUpdateDate22);
|
||||
CPPUNIT_TEST(testDowngradeTo23);
|
||||
// CPPUNIT_TEST(testUpdateFullDate22); TODO TYE+TDA should be upgraded to TDRC together
|
||||
CPPUNIT_TEST(testCompressedFrameWithBrokenLength);
|
||||
//CPPUNIT_TEST(testCompressedFrameWithBrokenLength);
|
||||
CPPUNIT_TEST(testW000);
|
||||
CPPUNIT_TEST(testPropertyInterface);
|
||||
CPPUNIT_TEST(testPropertyInterface2);
|
||||
CPPUNIT_TEST(testDeleteFrame);
|
||||
CPPUNIT_TEST(testSaveAndStripID3v1ShouldNotAddFrameFromID3v1ToId3v2);
|
||||
CPPUNIT_TEST(testChapters);
|
||||
CPPUNIT_TEST(testTableOfContents);
|
||||
CPPUNIT_TEST(testParseChapterFrame);
|
||||
CPPUNIT_TEST(testRenderChapterFrame);
|
||||
CPPUNIT_TEST(testParseTableOfContentsFrame);
|
||||
CPPUNIT_TEST(testRenderTableOfContentsFrame);
|
||||
CPPUNIT_TEST_SUITE_END();
|
||||
|
||||
public:
|
||||
@ -861,36 +863,77 @@ public:
|
||||
CPPUNIT_ASSERT(!f.ID3v2Tag()->frameListMap().contains("TPE1"));
|
||||
}
|
||||
|
||||
void testChapters()
|
||||
void testParseChapterFrame()
|
||||
{
|
||||
ID3v2::ChapterFrame f(
|
||||
ByteVector("CHAP" // Frame ID
|
||||
"\x00\x00\x00\x12" // Frame size
|
||||
"\x00\x00\x00\x20" // Frame size
|
||||
"\x00\x00" // Frame flags
|
||||
"\x43\x00" // Element ID
|
||||
"\x00\x00\x00\x03" // Start time
|
||||
"\x00\x00\x00\x05" // End time
|
||||
"\x00\x00\x00\x02" // Start offset
|
||||
"\x00\x00\x00\x03", 28)); // End offset
|
||||
"\x00\x00\x00\x03" // Start time
|
||||
"\x00\x00\x00\x05" // End time
|
||||
"\x00\x00\x00\x02" // Start offset
|
||||
"\x00\x00\x00\x03" // End offset
|
||||
"TIT2" // Embedded frame ID
|
||||
"\x00\x00\x00\x04" // Embedded frame size
|
||||
"\x00\x00" // Embedded frame flags
|
||||
"\x00" // TIT2 frame text encoding
|
||||
"CH1", 42)); // Chapter title
|
||||
CPPUNIT_ASSERT_EQUAL(ByteVector("\x43\x00", 2),
|
||||
f.elementID());
|
||||
CPPUNIT_ASSERT((uint)0x03 == f.startTime());
|
||||
CPPUNIT_ASSERT((uint)0x05 == f.endTime());
|
||||
CPPUNIT_ASSERT((uint)0x02 == f.startOffset());
|
||||
CPPUNIT_ASSERT((uint)0x03 == f.endOffset());
|
||||
CPPUNIT_ASSERT((uint)0x01 == f.embeddedFrameList().size());
|
||||
CPPUNIT_ASSERT(f.embeddedFrameList("TIT2").size() == 1);
|
||||
CPPUNIT_ASSERT(f.embeddedFrameList("TIT2")[0]->toString() == "CH1");
|
||||
}
|
||||
|
||||
void testTableOfContents()
|
||||
void testRenderChapterFrame()
|
||||
{
|
||||
ID3v2::ChapterFrame f("CHAP");
|
||||
f.setElementID(ByteVector("\x43\x00", 2));
|
||||
f.setStartTime(3);
|
||||
f.setEndTime(5);
|
||||
f.setStartOffset(2);
|
||||
f.setEndOffset(3);
|
||||
ID3v2::TextIdentificationFrame eF("TIT2");
|
||||
eF.setText("CH1");
|
||||
f.addEmbeddedFrame(&eF);
|
||||
CPPUNIT_ASSERT_EQUAL(
|
||||
ByteVector("CHAP" // Frame ID
|
||||
"\x00\x00\x00\x20" // Frame size
|
||||
"\x00\x00" // Frame flags
|
||||
"\x43\x00" // Element ID
|
||||
"\x00\x00\x00\x03" // Start time
|
||||
"\x00\x00\x00\x05" // End time
|
||||
"\x00\x00\x00\x02" // Start offset
|
||||
"\x00\x00\x00\x03" // End offset
|
||||
"TIT2" // Embedded frame ID
|
||||
"\x00\x00\x00\x04" // Embedded frame size
|
||||
"\x00\x00" // Embedded frame flags
|
||||
"\x00" // TIT2 frame text encoding
|
||||
"CH1", 42), // Chapter title
|
||||
f.render());
|
||||
}
|
||||
|
||||
void testParseTableOfContentsFrame()
|
||||
{
|
||||
ID3v2::TableOfContentsFrame f(
|
||||
ByteVector("CTOC" // Frame ID
|
||||
"\x00\x00\x00\x08" // Frame size
|
||||
"\x00\x00\x00\x16" // Frame size
|
||||
"\x00\x00" // Frame flags
|
||||
"\x54\x00" // Element ID
|
||||
"\x01" // CTOC flags
|
||||
"\x02" // Entry count
|
||||
"\x43\x00" // First entry
|
||||
"\x44\x00", 18)); // Second entry
|
||||
"\x01" // CTOC flags
|
||||
"\x02" // Entry count
|
||||
"\x43\x00" // First entry
|
||||
"\x44\x00" // Second entry
|
||||
"TIT2" // Embedded frame ID
|
||||
"\x00\x00\x00\x04" // Embedded frame size
|
||||
"\x00\x00" // Embedded frame flags
|
||||
"\x00" // TIT2 frame text encoding
|
||||
"TC1", 32)); // Table of contents title
|
||||
CPPUNIT_ASSERT_EQUAL(ByteVector("\x54\x00", 2),
|
||||
f.elementID());
|
||||
CPPUNIT_ASSERT(!f.isTopLevel());
|
||||
@ -900,6 +943,37 @@ public:
|
||||
f.childElements()[0]);
|
||||
CPPUNIT_ASSERT_EQUAL(ByteVector("\x44\x00", 2),
|
||||
f.childElements()[1]);
|
||||
CPPUNIT_ASSERT((uint)0x01 == f.embeddedFrameList().size());
|
||||
CPPUNIT_ASSERT(f.embeddedFrameList("TIT2").size() == 1);
|
||||
CPPUNIT_ASSERT(f.embeddedFrameList("TIT2")[0]->toString() == "TC1");
|
||||
}
|
||||
|
||||
void testRenderTableOfContentsFrame()
|
||||
{
|
||||
ID3v2::TableOfContentsFrame f("CTOC");
|
||||
f.setElementID(ByteVector("\x54\x00", 2));
|
||||
f.setIsTopLevel(false);
|
||||
f.setIsOrdered(true);
|
||||
f.addChildElement(ByteVector("\x43\x00", 2));
|
||||
f.addChildElement(ByteVector("\x44\x00", 2));
|
||||
ID3v2::TextIdentificationFrame eF("TIT2");
|
||||
eF.setText("TC1");
|
||||
f.addEmbeddedFrame(&eF);
|
||||
CPPUNIT_ASSERT_EQUAL(
|
||||
ByteVector("CTOC" // Frame ID
|
||||
"\x00\x00\x00\x16" // Frame size
|
||||
"\x00\x00" // Frame flags
|
||||
"\x54\x00" // Element ID
|
||||
"\x01" // CTOC flags
|
||||
"\x02" // Entry count
|
||||
"\x43\x00" // First entry
|
||||
"\x44\x00" // Second entry
|
||||
"TIT2" // Embedded frame ID
|
||||
"\x00\x00\x00\x04" // Embedded frame size
|
||||
"\x00\x00" // Embedded frame flags
|
||||
"\x00" // TIT2 frame text encoding
|
||||
"TC1", 32), // Table of contents title
|
||||
f.render());
|
||||
}
|
||||
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user