Fix seekhead

This commit is contained in:
complexlogic
2023-10-22 16:56:01 -07:00
committed by Urs Fleisch
parent 6d019a894c
commit 975eaac3ca
26 changed files with 834 additions and 111 deletions

View File

@ -230,15 +230,19 @@ if(WITH_SHORTEN)
endif()
set(tag_PRIVATE_HDRS
matroska/matroskaseekhead.h
matroska/matroskasegment.h
matroska/ebml/ebmlbinaryelement.h
matroska/ebml/ebmlelement.h
matroska/ebml/ebmlmasterelement.h
matroska/ebml/ebmlmkattachments.h
matroska/ebml/ebmlmkseekhead.h
matroska/ebml/ebmlmksegment.h
matroska/ebml/ebmlmktags.h
matroska/ebml/ebmlstringelement.h
matroska/ebml/ebmluintelement.h
matroska/ebml/ebmlutils.h
matroska/ebml/ebmlvoidelement.h
)
set(tag_HDRS ${tag_PUBLIC_HDRS} ${tag_PRIVATE_HDRS})
@ -248,6 +252,8 @@ set(matroska_SRCS
matroska/matroskaattachments.cpp
matroska/matroskaelement.cpp
matroska/matroskafile.cpp
matroska/matroskaseekhead.cpp
matroska/matroskasegment.cpp
matroska/matroskasimpletag.cpp
matroska/matroskatag.cpp
)
@ -257,11 +263,13 @@ set(ebml_SRCS
matroska/ebml/ebmlelement.cpp
matroska/ebml/ebmlmasterelement.cpp
matroska/ebml/ebmlmkattachments.cpp
matroska/ebml/ebmlmkseekhead.cpp
matroska/ebml/ebmlmksegment.cpp
matroska/ebml/ebmlmktags.cpp
matroska/ebml/ebmlstringelement.cpp
matroska/ebml/ebmluintelement.cpp
matroska/ebml/ebmlutils.cpp
matroska/ebml/ebmlvoidelement.cpp
)
set(mpeg_SRCS

View File

@ -19,8 +19,10 @@
***************************************************************************/
#include "ebmlelement.h"
#include "ebmlvoidelement.h"
#include "ebmlmasterelement.h"
#include "ebmlbinaryelement.h"
#include "ebmlmkseekhead.h"
#include "ebmlmksegment.h"
#include "ebmlmktags.h"
#include "ebmlmkattachments.h"
@ -68,6 +70,7 @@ EBML::Element* EBML::Element::factory(File &file)
case ElementIDs::MkTagTargets:
case ElementIDs::MkSimpleTag:
case ElementIDs::MkAttachedFile:
case ElementIDs::MkSeek:
return new MasterElement(id, sizeLength, dataSize, offset);
case ElementIDs::MkTagName:
@ -82,10 +85,18 @@ EBML::Element* EBML::Element::factory(File &file)
case ElementIDs::MkTagTargetTypeValue:
case ElementIDs::MkAttachedFileUID:
case ElementIDs::MkSeekPosition:
return new UIntElement(id, sizeLength, dataSize);
case ElementIDs::MkAttachedFileData:
case ElementIDs::MkSeekID:
return new BinaryElement(id, sizeLength, dataSize);
case ElementIDs::MkSeekHead:
return new MkSeekHead(sizeLength, dataSize, offset);
case ElementIDs::VoidElement:
return new VoidElement(sizeLength, dataSize);
default:
return new Element(id, sizeLength, dataSize);

View File

@ -43,6 +43,7 @@ namespace TagLib {
void skipData(File &file);
Id getId() const { return id; }
offset_t headSize() const;
offset_t getSize() const { return headSize() + dataSize; }
int getSizeLength() const { return sizeLength; }
int64_t getDataSize() const { return dataSize; }
ByteVector renderId();
@ -58,6 +59,7 @@ namespace TagLib {
namespace ElementIDs {
inline constexpr Element::Id EBMLHeader = 0x1A45DFA3;
inline constexpr Element::Id VoidElement = 0xEC;
inline constexpr Element::Id MkSegment = 0x18538067;
inline constexpr Element::Id MkTags = 0x1254C367;
inline constexpr Element::Id MkTag = 0x7373;
@ -76,6 +78,10 @@ namespace TagLib {
inline constexpr Element::Id MkAttachedFileMediaType = 0x4660;
inline constexpr Element::Id MkAttachedFileData = 0x465C;
inline constexpr Element::Id MkAttachedFileUID = 0x46AE;
inline constexpr Element::Id MkSeekHead = 0x114D9B74;
inline constexpr Element::Id MkSeek = 0x4DBB;
inline constexpr Element::Id MkSeekID = 0x53AB;
inline constexpr Element::Id MkSeekPosition = 0x53AC;
}
}

View File

@ -19,6 +19,7 @@
***************************************************************************/
#include "ebmlmasterelement.h"
#include "ebmlvoidelement.h"
#include "ebmlutils.h"
#include "matroskafile.h"
@ -55,5 +56,10 @@ ByteVector EBML::MasterElement::render()
dataSize = data.size();
buffer.append(renderVINT(dataSize, 0));
buffer.append(data);
if (minRenderSize) {
auto bufferSize = buffer.size();
if(minRenderSize >= (bufferSize + MIN_VOID_ELEMENT_SIZE))
buffer.append(VoidElement::renderSize(minRenderSize - bufferSize));
}
return buffer;
}

View File

@ -41,7 +41,6 @@ namespace TagLib {
{}
~MasterElement() override;
offset_t getOffset() const { return offset; }
offset_t getSize() const { return headSize() + dataSize; }
bool read(File &file) override;
ByteVector render() override;
void appendElement(Element *element) { elements.append(element); }
@ -49,9 +48,16 @@ namespace TagLib {
List<Element*>::Iterator end () { return elements.end(); }
List<Element*>::ConstIterator cbegin () const { return elements.cbegin(); }
List<Element*>::ConstIterator cend () const { return elements.cend(); }
offset_t getPadding() const { return padding; }
void setPadding(offset_t padding) { this->padding = padding; }
offset_t getMinRenderSize() const { return minRenderSize; }
void setMinRenderSize(offset_t minRenderSize) { this->minRenderSize = minRenderSize; }
protected:
offset_t offset;
offset_t padding = 0;
offset_t minRenderSize = 0;
List<Element*> elements;
};

View File

@ -0,0 +1,59 @@
/***************************************************************************
* 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/ *
***************************************************************************/
#include "ebmlmkseekhead.h"
#include "matroskaseekhead.h"
#include "ebmluintelement.h"
#include "ebmlbinaryelement.h"
using namespace TagLib;
Matroska::SeekHead* EBML::MkSeekHead::parse()
{
auto seekHead = new Matroska::SeekHead();
seekHead->setOffset(offset);
seekHead->setSize(getSize() + padding);
for(auto element : elements) {
if(element->getId() != ElementIDs::MkSeek)
continue;
auto seekElement = static_cast<MasterElement*>(element);
Matroska::Element::ID entryId = 0;
offset_t offset = 0;
for(auto seekElementChild : *seekElement) {
Id id = seekElementChild->getId();
if(id == ElementIDs::MkSeekID && !entryId) {
auto data = static_cast<BinaryElement*>(seekElementChild)->getValue();
if(data.size() == 4)
entryId = data.toUInt(true);
}
else if(id == ElementIDs::MkSeekPosition && !offset)
offset = static_cast<UIntElement*>(seekElementChild)->getValue();
}
if(entryId && offset)
seekHead->addEntry(entryId, offset);
else {
delete seekHead;
return nullptr;
}
}
return seekHead;
}

View File

@ -0,0 +1,49 @@
/***************************************************************************
* 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/ *
***************************************************************************/
#include "ebmlmasterelement.h"
#include "taglib.h"
#ifndef TAGLIB_EBMLMKSEEKHEAD_H
#define TAGLIB_EBMLMKSEEKHEAD_H
#ifndef DO_NOT_DOCUMENT
namespace TagLib {
namespace Matroska {
class SeekHead;
}
namespace EBML {
class MkSeekHead : public MasterElement
{
public:
MkSeekHead(int sizeLength, offset_t dataSize, offset_t offset)
: MasterElement(ElementIDs::MkSeekHead, sizeLength, dataSize, offset)
{}
MkSeekHead()
: MasterElement(ElementIDs::MkSeekHead, 0, 0, 0)
{}
Matroska::SeekHead* parse();
};
}
}
#endif
#endif

View File

@ -21,10 +21,13 @@
#include "ebmlmksegment.h"
#include "ebmlmktags.h"
#include "ebmlmkattachments.h"
#include "ebmlmkseekhead.h"
#include "ebmlutils.h"
#include "matroskafile.h"
#include "matroskatag.h"
#include "matroskaattachments.h"
#include "matroskaseekhead.h"
#include "matroskasegment.h"
#include "tutils.h"
#include "tbytevector.h"
#include "tdebug.h"
@ -35,16 +38,24 @@ EBML::MkSegment::~MkSegment()
{
delete tags;
delete attachments;
delete seekHead;
}
bool EBML::MkSegment::read(File &file)
{
offset_t maxOffset = file.tell() + dataSize;
EBML::Element *element = nullptr;
int i = 0;
int seekHeadIndex = -1;
while((element = findNextElement(file, maxOffset))) {
Id id = element->getId();
if(id == ElementIDs::MkTags) {
if(id == ElementIDs::MkSeekHead) {
seekHeadIndex = i;
seekHead = static_cast<MkSeekHead*>(element);
if(!seekHead->read(file))
return false;
}
else if(id == ElementIDs::MkTags) {
tags = static_cast<MkTags*>(element);
if(!tags->read(file))
return false;
@ -55,9 +66,15 @@ bool EBML::MkSegment::read(File &file)
return false;
}
else {
if(id == ElementIDs::VoidElement
&& seekHead
&& seekHeadIndex == (i - 1))
seekHead->setPadding(element->getSize());
element->skipData(file);
delete element;
}
i++;
}
return true;
}
@ -71,3 +88,13 @@ Matroska::Attachments* EBML::MkSegment::parseAttachments()
{
return attachments ? attachments->parse() : nullptr;
}
Matroska::SeekHead* EBML::MkSegment::parseSeekHead()
{
return seekHead ? seekHead->parse() : nullptr;
}
Matroska::Segment* EBML::MkSegment::parseSegment()
{
return new Matroska::Segment(sizeLength, dataSize, offset + idSize(id));
}

View File

@ -30,10 +30,13 @@ namespace TagLib {
namespace Matroska {
class Tag;
class Attachments;
class SeekHead;
class Segment;
}
namespace EBML {
class MkTags;
class MkAttachments;
class MkSeekHead;
class MkSegment : public MasterElement
{
public:
@ -44,10 +47,14 @@ namespace TagLib {
bool read(File &file) override;
Matroska::Tag* parseTag();
Matroska::Attachments* parseAttachments();
Matroska::SeekHead* parseSeekHead();
Matroska::Segment* parseSegment();
private:
offset_t dataOffset = 0;
MkTags *tags = nullptr;
MkAttachments *attachments = nullptr;
MkSeekHead *seekHead = nullptr;
};
}

View File

@ -36,6 +36,7 @@ Matroska::Tag* EBML::MkTags::parse()
auto mTag = new Matroska::Tag();
mTag->setOffset(offset);
mTag->setSize(getSize());
mTag->setID(id);
// Loop through each <Tag> element
for(auto tagsChild : elements) {

View File

@ -41,7 +41,7 @@ namespace TagLib {
MkTags()
: MasterElement(ElementIDs::MkTags, 0, 0, 0)
{}
//virtual void read(File &file) override;
Matroska::Tag* parse();
};

View File

@ -58,10 +58,8 @@ ByteVector EBML::UIntElement::render()
dataSize = 8;
ByteVector buffer = renderId();
//dataSize = minSize(value);
buffer.append(renderVINT(dataSize, 0));
unsigned long long value = this->value;
//debug(Utils::formatString("Writing %llu", value));
static const auto byteOrder = Utils::systemByteOrder();
if(byteOrder == Utils::LittleEndian)

View File

@ -0,0 +1,60 @@
/***************************************************************************
* 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/ *
***************************************************************************/
#include "ebmlvoidelement.h"
#include "ebmlutils.h"
#include "tbytevector.h"
#include "tdebug.h"
#include <algorithm>
using namespace TagLib;
ByteVector EBML::VoidElement::render()
{
offset_t bytesNeeded = targetSize;
ByteVector buffer = renderId();
bytesNeeded -= buffer.size();
sizeLength = std::min(bytesNeeded, static_cast<offset_t>(8));
bytesNeeded -= sizeLength;
dataSize = bytesNeeded;
buffer.append(renderVINT(dataSize, sizeLength));
if (dataSize)
buffer.append(ByteVector(dataSize, 0));
return buffer;
}
offset_t EBML::VoidElement::getTargetSize() const
{
return targetSize;
}
void EBML::VoidElement::setTargetSize(offset_t targetSize)
{
this->targetSize = std::max(targetSize, MIN_VOID_ELEMENT_SIZE);
}
ByteVector EBML::VoidElement::renderSize(offset_t targetSize)
{
VoidElement element;
element.setTargetSize(targetSize);
return element.render();
}

View File

@ -0,0 +1,56 @@
/***************************************************************************
* 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_EBMLVOIDELEMENT_H
#define TAGLIB_EBMLVOIDELEMENT_H
#ifndef DO_NOT_DOCUMENT
#include <cstdint>
#include "ebmlelement.h"
#include "tutils.h"
namespace TagLib {
class File;
namespace EBML {
inline constexpr offset_t MIN_VOID_ELEMENT_SIZE = 2;
class VoidElement : public Element
{
public:
VoidElement(int sizeLength, offset_t dataSize)
: Element(ElementIDs::VoidElement, sizeLength, dataSize)
{}
VoidElement()
: Element(ElementIDs::VoidElement, 0, 0)
{}
ByteVector render() override;
offset_t getTargetSize() const;
void setTargetSize(offset_t targetSize);
static ByteVector renderSize(offset_t size);
private:
offset_t targetSize = MIN_VOID_ELEMENT_SIZE;
};
}
}
#endif
#endif

View File

@ -19,11 +19,11 @@ public:
AttachmentsPrivate(const AttachmentsPrivate &) = delete;
AttachmentsPrivate &operator=(const AttachmentsPrivate &) = delete;
List<AttachedFile*> files;
};
Matroska::Attachments::Attachments()
: d(std::make_unique<AttachmentsPrivate>())
: Element(ElementIDs::MkAttachments),
d(std::make_unique<AttachmentsPrivate>())
{
d->files.setAutoDelete(true);
}
@ -53,7 +53,7 @@ const Matroska::Attachments::AttachedFileList& Matroska::Attachments::attachedFi
return d->files;
}
ByteVector Matroska::Attachments::render()
bool Matroska::Attachments::render()
{
EBML::MkAttachments attachments;
for(const auto attachedFile : d->files) {
@ -93,5 +93,14 @@ ByteVector Matroska::Attachments::render()
attachments.appendElement(attachedFileElement);
}
return attachments.render();
auto beforeSize = size();
auto data = attachments.render();
auto afterSize = data.size();
if (beforeSize != afterSize) {
if (!emitSizeChanged(afterSize - beforeSize))
return false;
}
setData(data);
return true;
}

View File

@ -28,13 +28,17 @@
namespace TagLib {
class File;
namespace EBML {
class MkAttachments;
}
namespace Matroska {
class AttachedFile;
class File;
class TAGLIB_EXPORT Attachments : private Element
class TAGLIB_EXPORT Attachments
#ifndef DO_NOT_DOCUMENT
: private Element
#endif
{
public:
using AttachedFileList = List<AttachedFile*>;
@ -45,7 +49,7 @@ namespace TagLib {
void removeAttachedFile(AttachedFile *file);
void clear();
const AttachedFileList& attachedFileList() const;
ByteVector render() override;
bool render() override;
private:
friend class EBML::MkAttachments;

View File

@ -1,6 +1,26 @@
/***************************************************************************
* 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/ *
***************************************************************************/
#include <memory>
#include "matroskaelement.h"
#include "tlist.h"
#include "tfile.h"
#include "tbytevector.h"
using namespace TagLib;
@ -14,12 +34,17 @@ public:
ElementPrivate &operator=(const ElementPrivate &) = delete;
offset_t size = 0;
offset_t offset = 0;
ID id = 0;
ByteVector data;
List<Element*> sizeListeners;
List<Element*> offsetListeners;
};
Matroska::Element::Element()
Matroska::Element::Element(ID id)
: e(std::make_unique<ElementPrivate>())
{
e->id = id;
}
Matroska::Element::~Element() = default;
@ -33,12 +58,97 @@ offset_t Matroska::Element::offset() const
return e->offset;
}
void Matroska::Element::setData(const ByteVector &data)
{
e->data = data;
}
const ByteVector& Matroska::Element::data() const
{
return e->data;
}
void Matroska::Element::setOffset(offset_t offset)
{
e->offset = offset;
}
void Matroska::Element::adjustOffset(offset_t delta)
{
e->offset += delta;
}
void Matroska::Element::setSize(offset_t size)
{
e->size = size;
}
}
Matroska::Element::ID Matroska::Element::id() const
{
return e->id;
}
void Matroska::Element::addSizeListener(Element *element)
{
e->sizeListeners.append(element);
}
void Matroska::Element::addSizeListeners(const List<Element*> &elements)
{
e->sizeListeners.append(elements);
}
void Matroska::Element::addOffsetListener(Element *element)
{
e->offsetListeners.append(element);
}
void Matroska::Element::addOffsetListeners(const List<Element*> &elements)
{
e->offsetListeners.append(elements);
}
void Matroska::Element::setID(ID id)
{
e->id = id;
}
bool Matroska::Element::emitSizeChanged(offset_t delta)
{
for(auto element : e->sizeListeners) {
if (!element->sizeChanged(*this, delta))
return false;
}
return true;
}
bool Matroska::Element::emitOffsetChanged(offset_t delta)
{
for(auto element : e->offsetListeners) {
if(!element->offsetChanged(*this, delta))
return false;
}
return true;
}
bool Matroska::Element::sizeChanged(Element &caller, offset_t delta)
{
if (caller.offset() < e->offset) {
e->offset += delta;
//return emitOffsetChanged(delta);
}
return true;
}
bool Matroska::Element::offsetChanged(Element &caller, offset_t delta)
{
// Most elements don't need to handle this
return true;
}
void Matroska::Element::write(TagLib::File &file)
{
file.insert(e->data, e->offset, e->size);
e->size = e->data.size();
}

View File

@ -20,32 +20,60 @@
#ifndef HAS_MATROSKAELEMENT_H
#define HAS_MATROSKAELEMENT_H
#ifndef DO_NOT_DOCUMENT
#include <memory>
#include "taglib_export.h"
#include "tutils.h"
#include "tbytevector.h"
#include "tlist.h"
namespace TagLib {
class File;
namespace Matroska {
class TAGLIB_EXPORT Element
{
public:
Element();
using ID = unsigned int;
Element(ID id);
virtual ~Element();
virtual ByteVector render() = 0;
offset_t size() const;
offset_t offset() const;
ID id() const;
void setOffset(offset_t offset);
void adjustOffset(offset_t delta);
void setSize(offset_t size);
void setID(ID id);
//virtual ByteVector render() = 0;
virtual bool render() = 0;
void setData(const ByteVector &data);
const ByteVector& data() const;
virtual void write(TagLib::File &file);
void addSizeListener(Element *element);
void addSizeListeners(const List<Element*> &elements);
void addOffsetListener(Element *element);
void addOffsetListeners(const List<Element*> &elements);
//virtual void updatePosition(Element &caller, offset_t delta) = 0;
bool emitSizeChanged(offset_t delta);
bool emitOffsetChanged(offset_t delta);
virtual bool offsetChanged(Element &caller, offset_t delta);
virtual bool sizeChanged(Element &caller, offset_t delta);
private:
class ElementPrivate;
std::unique_ptr<ElementPrivate> e;
};
namespace ElementIDs {
inline constexpr Element::ID MkTags = 0x1254C367;
inline constexpr Element::ID MkAttachments = 0x1941A469;
inline constexpr Element::ID MkSeekHead = 0x114D9B74;
inline constexpr Element::ID MkSegment = 0x18538067;
}
}
}
#endif
#endif
#endif

View File

@ -21,6 +21,8 @@
#include "matroskafile.h"
#include "matroskatag.h"
#include "matroskaattachments.h"
#include "matroskaseekhead.h"
#include "matroskasegment.h"
#include "ebmlutils.h"
#include "ebmlelement.h"
#include "ebmlmksegment.h"
@ -42,16 +44,15 @@ public:
{
delete tag;
delete attachments;
delete seekHead;
}
FilePrivate(const FilePrivate &) = delete;
FilePrivate &operator=(const FilePrivate &) = delete;
Matroska::Tag *tag = nullptr;
Matroska::Attachments *attachments = nullptr;
offset_t segmentSizeOffset = 0;
offset_t segmentSizeLength = 0;
offset_t segmentDataSize = 0;
Attachments *attachments = nullptr;
SeekHead *seekHead = nullptr;
Segment *segment = nullptr;
};
Matroska::File::File(FileName file, bool readProperties)
@ -65,6 +66,7 @@ Matroska::File::File(FileName file, bool readProperties)
}
read(readProperties);
}
Matroska::File::File(IOStream *stream, bool readProperties)
: TagLib::File(stream),
d(std::make_unique<FilePrivate>())
@ -76,6 +78,7 @@ Matroska::File::File(IOStream *stream, bool readProperties)
}
read(readProperties);
}
Matroska::File::~File() = default;
TagLib::Tag* Matroska::File::tag() const
@ -129,9 +132,6 @@ void Matroska::File::read(bool readProperties)
setValid(false);
return;
}
d->segmentSizeLength = segment->getSizeLength();
d->segmentSizeOffset = tell() - d->segmentSizeLength;
d->segmentDataSize = segment->getDataSize();
// Read the segment into memory from file
if(!segment->read(*this)) {
@ -139,11 +139,11 @@ void Matroska::File::read(bool readProperties)
setValid(false);
return;
}
// Parse the tag
// Parse the elements
d->segment = segment->parseSegment();
d->seekHead = segment->parseSeekHead();
d->tag = segment->parseTag();
// Parse the attachments
d->attachments = segment->parseAttachments();
setValid(true);
@ -151,86 +151,82 @@ void Matroska::File::read(bool readProperties)
bool Matroska::File::save()
{
std::vector<Element*> renderListExisting;
std::vector<Element*> renderListNew;
offset_t newSegmentDataSize = d->segmentDataSize;
if(d->tag) {
if(d->tag->size())
renderListExisting.push_back(d->tag);
else
renderListNew.push_back(d->tag);
if(readOnly()) {
debug("Matroska::File::save() -- File is read only.");
return false;
}
if(!isValid()) {
debug("Matroska::File::save() -- File is not valid.");
return false;
}
if(d->attachments) {
//d->attachments->setOffset(d->tag->offset());
//renderListExisting.push_back(d->attachments);
if(d->attachments->size())
renderListExisting.push_back(d->attachments);
else
renderListNew.push_back(d->attachments);
List<Element*> renderList;
List<Element*> newElements;
}
// List of all possible elements we can write
List<Element*> elements {
d->attachments,
d->tag
};
// Render from end to beginning so we don't have to shift
// the file offsets
std::sort(renderListExisting.begin(),
renderListExisting.end(),
[](auto a, auto b) { return a->offset() > b->offset(); }
);
// Overwrite existing elements
for(auto element : renderListExisting) {
offset_t offset = element->offset();
offset_t originalSize = element->size();
ByteVector data = element->render();
insert(data, offset, originalSize);
newSegmentDataSize += (data.size() - originalSize);
}
// Add new elements to the end of file
for(auto element : renderListNew) {
offset_t offset = length();
ByteVector data = element->render();
insert(data, offset, 0);
newSegmentDataSize += data.size();
}
// Write the new segment data size
if(newSegmentDataSize != d->segmentDataSize) {
auto segmentDataSizeBuffer = EBML::renderVINT(newSegmentDataSize, d->segmentSizeLength);
insert(segmentDataSizeBuffer, d->segmentSizeOffset, d->segmentSizeLength);
d->segmentDataSize = newSegmentDataSize;
}
/*
auto&& renderElements [&d->segmentDataSize](Element *element) {
auto offset = element->offset();
if(!offset)
}
if(d->tag) {
ByteVector tag = d->tag->render();
auto tagsOriginalSize = d->tag->size();
auto tagsOffset = d->tag->offset();
if(!tagsOriginalSize) {
tagsOffset = d->segmentSizeOffset + d->segmentSizeLength + d->segmentDataSize;
/* Build render list. New elements will be added
* to the end of the file. For new elements,
* the order is from least likely to change,
* to most likely to change:
* 1. Bookmarks (todo)
* 2. Attachments
* 3. Tags
*/
for (auto element : elements) {
if (!element)
continue;
if (element->size())
renderList.append(element);
else {
element->setOffset(length());
newElements.append(element);
}
insert(tag, tagsOffset, tagsOriginalSize);
d->segmentDataSize += (tag.size() - tagsOriginalSize);
auto segmentDataSizeBuffer = EBML::renderVINT(d->segmentDataSize, d->segmentSizeLength);
insert(segmentDataSizeBuffer, d->segmentSizeOffset, d->segmentSizeLength);
}
*/
/*
if(d->attachments) {
ByteVector attachments = d->attachments->render();
if (renderList.isEmpty())
return true;
auto sortAscending = [](const auto a, const auto b) { return a->offset() < b->offset(); };
renderList.sort(sortAscending);
renderList.append(newElements);
// Add our new elements to the Seek Head (if the file has one)
if (d->seekHead) {
auto segmentDataOffset = d->segment->dataOffset();
for (auto element : newElements)
d->seekHead->addEntry(element->id(), element->offset() - segmentDataOffset);
d->seekHead->sort();
}
*/
// Set up listeners, add seek head and segment length to the end
for(auto it = renderList.begin(); it != renderList.end(); ++it) {
for (auto it2 = std::next(it); it2 != renderList.end(); ++it2)
(*it)->addSizeListener(*it2);
if (d->seekHead)
(*it)->addSizeListener(d->seekHead);
(*it)->addSizeListener(d->segment);
}
if(d->seekHead) {
d->seekHead->addSizeListeners(renderList);
renderList.append(d->seekHead);
}
d->segment->addSizeListeners(renderList);
renderList.append(d->segment);
// Render the elements
for(auto element : renderList) {
if (!element->render())
return false;
}
// Write out to file
renderList.sort(sortAscending);
for(auto element : renderList)
element->write(*this);
return true;
}

View File

@ -1,8 +1,3 @@
/***************************************************************************
copyright : (C) 2002 - 2008 by Scott Wheeler
email : wheeler@kde.org
***************************************************************************/
/***************************************************************************
* This library is free software; you can redistribute it and/or modify *
* it under the terms of the GNU Lesser General Public License version *

View File

@ -0,0 +1,121 @@
/***************************************************************************
* 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/ *
***************************************************************************/
#include "matroskaseekhead.h"
#include "ebmlmkseekhead.h"
#include "ebmlbinaryelement.h"
#include "ebmluintelement.h"
#include "ebmlmasterelement.h"
#include "tdebug.h"
#include "tfile.h"
#include "tutils.h"
using namespace TagLib;
void Matroska::SeekHead::addEntry(Element &element)
{
entries.append({element.id(), element.offset()});
debug("adding to seekhead");
needsRender = true;
}
void Matroska::SeekHead::addEntry(ID id, offset_t offset)
{
entries.append({id, offset});
needsRender = true;
}
ByteVector Matroska::SeekHead::renderInternal()
{
auto beforeSize = size();
EBML::MkSeekHead seekHead;
seekHead.setMinRenderSize(beforeSize);
for(const auto& [id, position] : entries) {
auto seekElement = new EBML::MasterElement(EBML::ElementIDs::MkSeek);
auto idElement = new EBML::BinaryElement(EBML::ElementIDs::MkSeekID);
idElement->setValue(ByteVector::fromUInt(id, true));
seekElement->appendElement(idElement);
auto positionElement = new EBML::UIntElement(EBML::ElementIDs::MkSeekPosition);
positionElement->setValue(static_cast<unsigned long long>(position));
seekElement->appendElement(positionElement);
seekHead.appendElement(seekElement);
}
return seekHead.render();
}
bool Matroska::SeekHead::render()
{
if (!needsRender)
return true;
auto beforeSize = size();
auto data = renderInternal();
needsRender = false;
auto afterSize = data.size();
if (afterSize != beforeSize) {
return false;
// To do, handle expansion of seek head
if (!emitSizeChanged(afterSize - beforeSize))
return false;
}
setData(data);
return true;
}
void Matroska::SeekHead::write(TagLib::File &file)
{
if (!data().isEmpty())
Element::write(file);
}
void Matroska::SeekHead::sort()
{
entries.sort([](const auto &a, const auto &b) { return a.second < b.second; });
}
bool Matroska::SeekHead::sizeChanged(Element &caller, offset_t delta)
{
ID callerID = caller.id();
if (callerID == ElementIDs::MkSegment) {
adjustOffset(delta);
return true;
}
else {
offset_t offset = caller.offset();
auto it = entries.begin();
while (it != entries.end()) {
it = std::find_if(it,
entries.end(),
[offset](const auto a){ return a.second > offset; }
);
if (it != entries.end()) {
it->second += delta;
needsRender = true;
++it;
}
}
return true;
}
return false;
}

View File

@ -0,0 +1,55 @@
/***************************************************************************
* 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_MATROSKASEEKHEAD_H
#define TAGLIB_MATROSKASEEKHEAD_H
#ifndef DO_NOT_DOCUMENT
#include "matroskaelement.h"
#include "tbytevector.h"
#include "tutils.h"
#include "tlist.h"
namespace TagLib {
class File;
namespace Matroska {
class SeekHead : public Element
{
public:
SeekHead() : Element(ElementIDs::MkSeekHead) {};
virtual ~SeekHead() {};
void addEntry(Element &element);
void addEntry(ID id, offset_t offset);
bool render() override;
void write(TagLib::File &file) override;
void sort();
//bool offsetChanged(Element &caller, offset_t delta) override;
bool sizeChanged(Element &caller, offset_t delta) override;
private:
ByteVector renderInternal();
List<std::pair<unsigned int, offset_t>> entries;
bool needsRender = false;
};
}
}
#endif
#endif

View File

@ -0,0 +1,45 @@
/***************************************************************************
* 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/ *
***************************************************************************/
#include "matroskasegment.h"
#include "ebmlutils.h"
using namespace TagLib;
bool Matroska::Segment::render()
{
auto data = EBML::renderVINT(dataSize, sizeLength);
if (data.size() != sizeLength) {
sizeLength = 8;
if (!emitSizeChanged(sizeLength - data.size()))
return false;
data = EBML::renderVINT(dataSize, sizeLength);
if (data.size() != sizeLength)
return false;
}
setData(data);
return true;
}
bool Matroska::Segment::sizeChanged(Element &caller, offset_t delta)
{
dataSize += delta;
return true;
}

View File

@ -0,0 +1,52 @@
/***************************************************************************
* 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_MATROSKASEGMENT_H
#define TAGLIB_MATROSKASEGMENT_H
#ifndef DO_NOT_DOCUMENT
#include "matroskaelement.h"
#include "tutils.h"
namespace TagLib {
namespace Matroska {
class Segment : public Element
{
public:
Segment(offset_t sizeLength, offset_t dataSize, offset_t lengthOffset)
: Element(ElementIDs::MkSegment), sizeLength(sizeLength), dataSize(dataSize)
{
setOffset(lengthOffset);
setSize(sizeLength);
}
virtual ~Segment() = default;
bool render() override;
bool sizeChanged(Element &caller, offset_t delta) override;
offset_t dataOffset() const { return offset() + sizeLength; }
private:
offset_t sizeLength;
offset_t dataSize;
};
}
}
#endif
#endif

View File

@ -50,11 +50,13 @@ class Matroska::Tag::TagPrivate
TagPrivate() = default;
~TagPrivate() = default;
List<SimpleTag*> tags;
ByteVector data;
};
Matroska::Tag::Tag()
: TagLib::Tag(),
Element(ElementIDs::MkTags),
d(std::make_unique<TagPrivate>())
{
d->tags.setAutoDelete(true);
@ -183,7 +185,7 @@ bool Matroska::Tag::isEmpty() const
return d->tags.isEmpty();
}
ByteVector Matroska::Tag::render()
bool Matroska::Tag::render()
{
EBML::MkTags tags;
List<List<SimpleTag*>*> targetList;
@ -256,7 +258,15 @@ ByteVector Matroska::Tag::render()
tags.appendElement(tag);
}
return tags.render();
auto data = tags.render();
auto beforeSize = size();
auto afterSize = data.size();
if (afterSize != beforeSize) {
if (!emitSizeChanged(afterSize - beforeSize))
return false;
}
setData(data);
return true;
}
namespace

View File

@ -33,13 +33,17 @@
#include "matroskasimpletag.h"
namespace TagLib {
class File;
namespace EBML {
class MkTags;
}
namespace Matroska {
using SimpleTagsList = List<SimpleTag*>;
class TAGLIB_EXPORT Tag : public TagLib::Tag, private Element
class TAGLIB_EXPORT Tag : public TagLib::Tag
#ifndef DO_NOT_DOCUMENT
, private Element
#endif
{
public:
Tag();
@ -63,7 +67,7 @@ namespace TagLib {
void setYear(unsigned int i) override;
void setTrack(unsigned int i) override;
bool isEmpty() const override;
ByteVector render() override;
bool render() override;
PropertyMap properties() const override;
PropertyMap setProperties(const PropertyMap &propertyMap) override;
template <typename T>