Skip to content

Commit 8083748

Browse files
complexlogicufleisch
authored andcommitted
Added write support
1 parent 47e9b9a commit 8083748

23 files changed

Lines changed: 756 additions & 152 deletions

examples/CMakeLists.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,11 @@ target_link_libraries(strip-id3v1 tag)
4444
add_executable(matroskareader matroskareader.cpp)
4545
target_link_libraries(matroskareader tag)
4646

47+
########### next target ###############
48+
49+
add_executable(matroskawriter matroskawriter.cpp)
50+
target_link_libraries(matroskawriter tag)
51+
4752
install(TARGETS tagreader tagreader_c tagwriter framelist strip-id3v1
4853
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
4954
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}

examples/matroskawriter.cpp

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
#include <cstdio>
2+
#include "matroskafile.h"
3+
#include "matroskatag.h"
4+
#include "matroskasimpletag.h"
5+
#include "tstring.h"
6+
#include "tutils.h"
7+
8+
int main(int argc, char *argv[])
9+
{
10+
if (argc != 2) {
11+
printf("Usage: matroskawriter FILE\n");
12+
return 1;
13+
}
14+
TagLib::Matroska::File file(argv[1]);
15+
if (!file.isValid()) {
16+
printf("File is not valid\n");
17+
return 1;
18+
}
19+
auto tag = file.tag(true);
20+
tag->clearSimpleTags();
21+
22+
auto simpleTag = new TagLib::Matroska::SimpleTagString();
23+
simpleTag->setName("Test Name 1");
24+
simpleTag->setTargetTypeValue(TagLib::Matroska::SimpleTag::TargetTypeValue::Track);
25+
simpleTag->setValue("Test Value 1");
26+
tag->addSimpleTag(simpleTag);
27+
28+
simpleTag = new TagLib::Matroska::SimpleTagString();
29+
simpleTag->setName("Test Name 2");
30+
simpleTag->setTargetTypeValue(TagLib::Matroska::SimpleTag::TargetTypeValue::Album);
31+
simpleTag->setValue("Test Value 2");
32+
tag->addSimpleTag(simpleTag);
33+
34+
file.save();
35+
36+
return 0;
37+
}

taglib/fileref.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
#include "tstringlist.h"
3939
#include "tvariant.h"
4040
#include "tdebug.h"
41+
#include "matroskafile.h"
4142
#include "mpegfile.h"
4243
#ifdef TAGLIB_WITH_RIFF
4344
#include "aifffile.h"
@@ -220,6 +221,8 @@ namespace
220221
else if(ext == "SHN")
221222
file = new Shorten::File(stream, readAudioProperties, audioPropertiesStyle);
222223
#endif
224+
else if(ext == "MKA" || ext == "MKV" || ext == "WEBM")
225+
file = new Matroska::File(stream, readAudioProperties);
223226

224227
// if file is not valid, leave it to content-based detection.
225228

taglib/matroska/ebml/ebmlelement.cpp

Lines changed: 50 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@
2929
#include "tdebug.h"
3030
#include "tutils.h"
3131

32+
#include <cstdint>
33+
3234
using namespace TagLib;
3335

3436
EBML::Element* EBML::Element::factory(File &file)
@@ -47,28 +49,28 @@ EBML::Element* EBML::Element::factory(File &file)
4749

4850
// Return the subclass
4951
switch(id) {
50-
case EBML_ID_HEAD:
52+
case ElementIDs::EBMLHeader:
5153
return new Element(id, sizeLength, dataSize);
5254

53-
case EBML_ID_MK_SEGMENT:
55+
case ElementIDs::MkSegment:
5456
return new MkSegment(sizeLength, dataSize);
5557

56-
case EBML_ID_MK_TAGS:
58+
case ElementIDs::MkTags:
5759
return new MkTags(sizeLength, dataSize);
5860

59-
case EBML_ID_MK_TAG:
60-
case EBML_ID_MK_TAG_TARGETS:
61-
case EBML_ID_MK_SIMPLE_TAG:
61+
case ElementIDs::MkTag:
62+
case ElementIDs::MkTagTargets:
63+
case ElementIDs::MkSimpleTag:
6264
return new MasterElement(id, sizeLength, dataSize);
6365

64-
case EBML_ID_MK_TAG_NAME:
65-
case EBML_ID_MK_TAG_STRING:
66+
case ElementIDs::MkTagName:
67+
case ElementIDs::MkTagString:
6668
return new UTF8StringElement(id, sizeLength, dataSize);
6769

68-
case EBML_ID_MK_TAG_LANGUAGE:
70+
case ElementIDs::MkTagLanguage:
6971
return new Latin1StringElement(id, sizeLength, dataSize);
7072

71-
case EBML_ID_MK_TARGET_TYPE_VALUE:
73+
case ElementIDs::MkTagTargetTypeValue:
7274
return new UIntElement(id, sizeLength, dataSize);
7375

7476
default:
@@ -78,7 +80,45 @@ EBML::Element* EBML::Element::factory(File &file)
7880
return nullptr;
7981
}
8082

83+
EBML::Element::Id EBML::Element::readId(File &file)
84+
{
85+
auto buffer = file.readBlock(1);
86+
if (buffer.size() != 1) {
87+
debug("Failed to read VINT size");
88+
return 0;
89+
}
90+
unsigned int nb_bytes = VINTSizeLength<4>(*buffer.begin());
91+
if (!nb_bytes)
92+
return 0;
93+
if (nb_bytes > 1)
94+
buffer.append(file.readBlock(nb_bytes - 1));
95+
if (buffer.size() != nb_bytes) {
96+
debug("Failed to read VINT data");
97+
return 0;
98+
}
99+
return buffer.toUInt(true);
100+
}
101+
81102
void EBML::Element::skipData(File &file)
82103
{
83104
file.seek(dataSize, File::Position::Current);
84105
}
106+
107+
offset_t EBML::Element::headSize() const
108+
{
109+
return EBML::idSize(id) + sizeLength;
110+
}
111+
112+
ByteVector EBML::Element::render()
113+
{
114+
ByteVector buffer = renderId();
115+
buffer.append(renderVINT(0, 0));
116+
return buffer;
117+
}
118+
119+
ByteVector EBML::Element::renderId()
120+
{
121+
int numBytes = idSize(id);
122+
id = Utils::byteSwap(id);
123+
return ByteVector((char*) &id + (4 - numBytes), numBytes);
124+
}

taglib/matroska/ebml/ebmlelement.h

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,14 +23,16 @@
2323
#ifndef DO_NOT_DOCUMENT
2424

2525
#include "tfile.h"
26-
#include "ebmlutils.h"
26+
#include "tutils.h"
27+
//#include "ebmlutils.h"
2728
#include "taglib.h"
2829

2930
namespace TagLib {
3031
namespace EBML {
3132
class Element
3233
{
3334
public:
35+
using Id = unsigned int;
3436
Element(Id id, int sizeLength, offset_t dataSize)
3537
: id(id), sizeLength(sizeLength), dataSize(dataSize)
3638
{}
@@ -42,15 +44,32 @@ namespace TagLib {
4244
}
4345
void skipData(File &file);
4446
Id getId() const { return id; }
47+
offset_t headSize() const;
4548
int getSizeLength() const { return sizeLength; }
4649
int64_t getDataSize() const { return dataSize; }
50+
ByteVector renderId();
51+
virtual ByteVector render();
4752
static Element* factory(File &file);
53+
static Id readId(File &file);
4854

4955
protected:
5056
Id id;
5157
int sizeLength;
5258
offset_t dataSize;
5359
};
60+
61+
namespace ElementIDs {
62+
inline constexpr Element::Id EBMLHeader = 0x1A45DFA3;
63+
inline constexpr Element::Id MkSegment = 0x18538067;
64+
inline constexpr Element::Id MkTags = 0x1254C367;
65+
inline constexpr Element::Id MkTag = 0x7373;
66+
inline constexpr Element::Id MkTagTargets = 0x63C0;
67+
inline constexpr Element::Id MkTagTargetTypeValue = 0x68CA;
68+
inline constexpr Element::Id MkSimpleTag = 0x67C8;
69+
inline constexpr Element::Id MkTagName = 0x45A3;
70+
inline constexpr Element::Id MkTagLanguage = 0x447A;
71+
inline constexpr Element::Id MkTagString = 0x4487;
72+
}
5473
}
5574
}
5675

taglib/matroska/ebml/ebmlmasterelement.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,3 +45,15 @@ bool EBML::MasterElement::read(File &file)
4545
}
4646
return file.tell() == maxOffset;
4747
}
48+
49+
ByteVector EBML::MasterElement::render()
50+
{
51+
ByteVector buffer = renderId();
52+
ByteVector data;
53+
for (auto element : elements)
54+
data.append(element->render());
55+
dataSize = data.size();
56+
buffer.append(renderVINT(dataSize, 0));
57+
buffer.append(data);
58+
return buffer;
59+
}

taglib/matroska/ebml/ebmlmasterelement.h

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,9 @@
2222
#define TAGLIB_EBMLMASTERELEMENT_H
2323
#ifndef DO_NOT_DOCUMENT
2424

25-
#include "ebmlelement.h"
2625
#include "ebmlutils.h"
26+
#include "ebmlelement.h"
27+
#include "tbytevector.h"
2728
#include "tlist.h"
2829
#include "taglib.h"
2930

@@ -35,9 +36,14 @@ namespace TagLib {
3536
MasterElement(Id id, int sizeLength, offset_t dataSize)
3637
: Element(id, sizeLength, dataSize)
3738
{}
39+
MasterElement(Id id)
40+
: Element(id, 0, 0)
41+
{}
3842
~MasterElement() override;
3943
virtual bool isMaster() const override { return true; }
4044
virtual bool read(File &file) override;
45+
ByteVector render() override;
46+
void appendElement(Element *element) { elements.append(element); }
4147
List<Element*>::Iterator begin () { return elements.begin(); }
4248
List<Element*>::Iterator end () { return elements.end(); }
4349
List<Element*>::ConstIterator cbegin () const { return elements.cbegin(); }

taglib/matroska/ebml/ebmlmksegment.cpp

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
#include "matroskafile.h"
2525
#include "matroskatag.h"
2626
#include "tutils.h"
27+
#include "tbytevector.h"
2728
#include "tdebug.h"
2829

2930
using namespace TagLib;
@@ -36,13 +37,21 @@ EBML::MkSegment::~MkSegment()
3637
bool EBML::MkSegment::read(File &file)
3738
{
3839
offset_t maxOffset = file.tell() + dataSize;
39-
tags = static_cast<MkTags*>(findElement(file, EBML_ID_MK_TAGS, maxOffset));
40-
if (tags && !tags->read(file))
41-
return false;
40+
tags = static_cast<MkTags*>(findElement(file, ElementIDs::MkTags, maxOffset));
41+
if (tags) {
42+
offset_t tagsHeadSize = tags->headSize();
43+
tagsOffset = file.tell() - tagsHeadSize;
44+
tagsOriginalSize = tagsHeadSize + tags->getDataSize();
45+
if (!tags->read(file))
46+
return false;
47+
}
4248
return true;
4349
}
4450

45-
Matroska::Tag* EBML::MkSegment::parseTag()
51+
std::tuple<Matroska::Tag*, offset_t, offset_t> EBML::MkSegment::parseTag()
4652
{
47-
return tags ? tags->parse() : nullptr;
53+
if (tags)
54+
return {tags->parse(), tagsOffset, tagsOriginalSize};
55+
else
56+
return {nullptr, 0, 0};
4857
}

taglib/matroska/ebml/ebmlmksegment.h

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020

2121
#include "ebmlmasterelement.h"
2222
#include "taglib.h"
23+
#include <tuple>
2324

2425
#ifndef TAGLIB_EBMLMKSEGMENT_H
2526
#define TAGLIB_EBMLMKSEGMENT_H
@@ -36,14 +37,16 @@ namespace TagLib {
3637
{
3738
public:
3839
MkSegment(int sizeLength, offset_t dataSize)
39-
: MasterElement(EBML_ID_MK_SEGMENT, sizeLength, dataSize)
40+
: MasterElement(ElementIDs::MkSegment, sizeLength, dataSize)
4041
{}
4142
~MkSegment() override;
4243
bool read(File &file) override;
43-
Matroska::Tag* parseTag();
44+
std::tuple<Matroska::Tag*, offset_t, offset_t> parseTag();
4445

4546
private:
4647
MkTags *tags = nullptr;
48+
offset_t tagsOffset = 0;
49+
offset_t tagsOriginalSize = 0;
4750

4851
};
4952
}

taglib/matroska/ebml/ebmlmktags.cpp

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ Matroska::Tag* EBML::MkTags::parse()
3737

3838
// Loop through each <Tag> element
3939
for (auto tagsChild : elements) {
40-
if (tagsChild->getId() != EBML_ID_MK_TAG)
40+
if (tagsChild->getId() != ElementIDs::MkTag)
4141
continue;
4242
auto tag = static_cast<MasterElement*>(tagsChild);
4343
List<MasterElement*> simpleTags;
@@ -46,20 +46,20 @@ Matroska::Tag* EBML::MkTags::parse()
4646
// Identify the <Targets> element and the <SimpleTag> elements
4747
for (auto tagChild : *tag) {
4848
Id tagChildId = tagChild->getId();
49-
if (!targets && tagChildId == EBML_ID_MK_TAG_TARGETS)
49+
if (!targets && tagChildId == ElementIDs::MkTagTargets)
5050
targets = static_cast<MasterElement*>(tagChild);
51-
else if (tagChildId == EBML_ID_MK_SIMPLE_TAG)
51+
else if (tagChildId == ElementIDs::MkSimpleTag)
5252
simpleTags.append(static_cast<MasterElement*>(tagChild));
5353
}
5454

5555
// Parse the <Targets> element
56-
Matroska::Tag::TargetTypeValue targetTypeValue = Matroska::Tag::TargetTypeValue::None;
56+
Matroska::SimpleTag::TargetTypeValue targetTypeValue = Matroska::SimpleTag::TargetTypeValue::None;
5757
if (targets) {
5858
for (auto targetsChild : *targets) {
5959
Id id = targetsChild->getId();
60-
if (id == EBML_ID_MK_TARGET_TYPE_VALUE
61-
&& targetTypeValue == Matroska::Tag::TargetTypeValue::None) {
62-
targetTypeValue = static_cast<Matroska::Tag::TargetTypeValue>(
60+
if (id == ElementIDs::MkTagTargetTypeValue
61+
&& targetTypeValue == Matroska::SimpleTag::TargetTypeValue::None) {
62+
targetTypeValue = static_cast<Matroska::SimpleTag::TargetTypeValue>(
6363
static_cast<UIntElement*>(targetsChild)->getValue()
6464
);
6565
}
@@ -74,9 +74,9 @@ Matroska::Tag* EBML::MkTags::parse()
7474

7575
for (auto simpleTagChild : *simpleTag) {
7676
Id id = simpleTagChild->getId();
77-
if (id == EBML_ID_MK_TAG_NAME && !tagName)
77+
if (id == ElementIDs::MkTagName && !tagName)
7878
tagName = &(static_cast<UTF8StringElement*>(simpleTagChild)->getValue());
79-
else if (id == EBML_ID_MK_TAG_STRING && !tagValueString)
79+
else if (id == ElementIDs::MkTagString && !tagValueString)
8080
tagValueString = &(static_cast<UTF8StringElement*>(simpleTagChild)->getValue());
8181
}
8282
if (!tagName || (tagValueString && tagValueBinary) || (!tagValueString && !tagValueBinary))
@@ -85,12 +85,14 @@ Matroska::Tag* EBML::MkTags::parse()
8585
// Create a Simple Tag object and add it to the Tag object
8686
Matroska::SimpleTag *sTag = nullptr;
8787
if (tagValueString) {
88-
auto sTagString = new Matroska::SimpleTagString(targetTypeValue);
88+
auto sTagString = new Matroska::SimpleTagString();
89+
sTagString->setTargetTypeValue(targetTypeValue);
8990
sTagString->setValue(*tagValueString);
9091
sTag = sTagString;
9192
}
9293
else if (tagValueBinary) {
93-
auto sTagBinary = new Matroska::SimpleTagBinary(targetTypeValue);
94+
auto sTagBinary = new Matroska::SimpleTagBinary();
95+
sTagBinary->setTargetTypeValue(targetTypeValue);
9496
sTagBinary->setValue(*tagValueBinary);
9597
sTag = sTagBinary;
9698
}

0 commit comments

Comments
 (0)