Tag Parser 10.3.1
C++ library for reading and writing MP4 (iTunes), ID3, Vorbis, Opus, FLAC and Matroska tags
matroskatag.cpp
Go to the documentation of this file.
1#include "./matroskatag.h"
2#include "./ebmlelement.h"
3
4#include "../diagnostics.h"
5
6#include <initializer_list>
7#include <stdexcept>
8#include <unordered_map>
9
10using namespace std;
11using namespace CppUtilities;
12
13namespace TagParser {
14
21{
22 using namespace MatroskaTagIds;
23 switch (field) {
25 return std::string(artist());
27 return std::string(album());
29 return std::string(comment());
31 return std::string(dateRecorded());
33 return std::string(dateRelease());
35 return std::string(title());
37 return std::string(genre());
39 return std::string(partNumber());
41 return std::string(totalParts());
43 return std::string(encoder());
45 return std::string(encoderSettings());
46 case KnownField::Bpm:
47 return std::string(bpm());
48 case KnownField::Bps:
49 return std::string(bps());
51 return std::string(rating());
53 return std::string(description());
55 return std::string(lyrics());
57 return std::string(label());
59 return std::string(actor());
61 return std::string(lyricist());
63 return std::string(composer());
65 return std::string(duration());
67 return std::string(language());
68 default:
69 return std::string();
70 }
71}
72
73KnownField MatroskaTag::internallyGetKnownField(const IdentifierType &id) const
74{
75 using namespace MatroskaTagIds;
76 static const std::unordered_map<std::string_view, KnownField> fieldMap({
87 { bpm(), KnownField::Bpm },
88 { bps(), KnownField::Bps },
98 });
99 const auto knownField(fieldMap.find(id));
100 return knownField != fieldMap.cend() ? knownField->second : KnownField::Invalid;
101}
102
111{
112 parse2(tagElement, MatroskaTagFlags::None, diag);
113}
114
123{
124 static const string context("parsing Matroska tag");
125 m_size = tagElement.totalSize();
126 tagElement.parse(diag);
127 if (tagElement.totalSize() > numeric_limits<std::uint32_t>::max()) {
128 // FIXME: Support this? Likely not very useful in practise.
129 diag.emplace_back(DiagLevel::Critical, "Matroska tag is too big.", context);
131 }
132 const auto normalize = flags & MatroskaTagFlags::NormalizeKnownFieldIds;
133 for (EbmlElement *child = tagElement.firstChild(); child; child = child->nextSibling()) {
134 child->parse(diag);
135 switch (child->id()) {
137 try {
138 auto field = MatroskaTagField();
139 field.reparse(*child, diag, true);
140 if (normalize) {
141 auto normalizedId = field.id();
142 MatroskaTagField::normalizeId(normalizedId);
143 if (internallyGetKnownField(normalizedId) != KnownField::Invalid) {
144 field.id() = std::move(normalizedId);
145 }
146 }
147 fields().emplace(field.id(), std::move(field));
148 } catch (const Failure &) {
149 // message will be added to diag anyways
150 }
151 break;
153 parseTargets(*child, diag);
154 break;
155 }
156 }
157}
158
166void MatroskaTag::parseTargets(EbmlElement &targetsElement, Diagnostics &diag)
167{
168 static const string context("parsing targets of Matroska tag");
169 m_target.clear();
170 bool targetTypeValueFound = false;
171 bool targetTypeFound = false;
172 targetsElement.parse(diag);
173 for (EbmlElement *child = targetsElement.firstChild(); child; child = child->nextSibling()) {
174 try {
175 child->parse(diag);
176 } catch (const Failure &) {
177 diag.emplace_back(DiagLevel::Critical, "Unable to parse children of Targets element.", context);
178 break;
179 }
180 switch (child->id()) {
182 if (!targetTypeValueFound) {
183 m_target.setLevel(child->readUInteger());
184 targetTypeValueFound = true;
185 } else {
186 diag.emplace_back(
187 DiagLevel::Warning, "Targets element contains multiple TargetTypeValue elements. Surplus elements will be ignored.", context);
188 }
189 break;
191 if (!targetTypeFound) {
192 m_target.setLevelName(child->readString());
193 targetTypeFound = true;
194 } else {
195 diag.emplace_back(
196 DiagLevel::Warning, "Targets element contains multiple TargetType elements. Surplus elements will be ignored.", context);
197 }
198 break;
200 m_target.tracks().emplace_back(child->readUInteger());
201 break;
203 m_target.editions().emplace_back(child->readUInteger());
204 break;
206 m_target.chapters().emplace_back(child->readUInteger());
207 break;
209 m_target.attachments().emplace_back(child->readUInteger());
210 break;
211 default:
212 diag.emplace_back(DiagLevel::Warning, "Targets element contains unknown element. It will be ignored.", context);
213 }
214 }
215 if (!m_target.level()) {
216 m_target.setLevel(50); // default level
217 }
218}
219
231MatroskaTagMaker::MatroskaTagMaker(MatroskaTag &tag, Diagnostics &diag)
232 : m_tag(tag)
233{
234 // calculate size of "Targets" element
235 m_targetsSize = 0; // NOT including ID and size
236 if (m_tag.target().level() != 50) {
237 // size of "TargetTypeValue"
238 m_targetsSize += 2u + 1u + EbmlElement::calculateUIntegerLength(m_tag.target().level());
239 }
240 if (!m_tag.target().levelName().empty()) {
241 // size of "TargetType"
242 m_targetsSize += 2u + EbmlElement::calculateSizeDenotationLength(m_tag.target().levelName().size()) + m_tag.target().levelName().size();
243 }
244 for (const auto &v : initializer_list<vector<std::uint64_t>>{
245 m_tag.target().tracks(), m_tag.target().editions(), m_tag.target().chapters(), m_tag.target().attachments() }) {
246 for (auto uid : v) {
247 // size of UID denotation
248 m_targetsSize += 2u + 1u + EbmlElement::calculateUIntegerLength(uid);
249 }
250 }
251 m_tagSize = 2u + EbmlElement::calculateSizeDenotationLength(m_targetsSize) + m_targetsSize;
252 // calculate size of "SimpleTag" elements
253 m_maker.reserve(m_tag.fields().size());
254 m_simpleTagsSize = 0; // including ID and size
255 for (auto &pair : m_tag.fields()) {
256 try {
257 m_maker.emplace_back(pair.second.prepareMaking(diag));
258 m_simpleTagsSize += m_maker.back().requiredSize();
259 } catch (const Failure &) {
260 }
261 }
262 m_tagSize += m_simpleTagsSize;
263 m_totalSize = 2u + EbmlElement::calculateSizeDenotationLength(m_tagSize) + m_tagSize;
264}
265
273void MatroskaTagMaker::make(ostream &stream) const
274{
275 // write header
276 char buff[11];
277 BE::getBytes(static_cast<std::uint16_t>(MatroskaIds::Tag), buff);
278 stream.write(buff, 2); // ID
279 std::uint8_t len = EbmlElement::makeSizeDenotation(m_tagSize, buff);
280 stream.write(buff, len); // size
281 // write "Targets" element
282 BE::getBytes(static_cast<std::uint16_t>(MatroskaIds::Targets), buff);
283 stream.write(buff, 2);
284 len = EbmlElement::makeSizeDenotation(m_targetsSize, buff);
285 stream.write(buff, len);
286 const TagTarget &t = m_tag.target();
287 if (t.level() != 50) {
288 // write "TargetTypeValue"
289 BE::getBytes(static_cast<std::uint16_t>(MatroskaIds::TargetTypeValue), buff);
290 stream.write(buff, 2);
291 len = EbmlElement::makeUInteger(t.level(), buff);
292 stream.put(static_cast<char>(0x80 | len));
293 stream.write(buff, len);
294 }
295 if (!t.levelName().empty()) {
296 // write "TargetType"
297 BE::getBytes(static_cast<std::uint16_t>(MatroskaIds::TargetType), buff);
298 stream.write(buff, 2);
299 len = EbmlElement::makeSizeDenotation(t.levelName().size(), buff);
300 stream.write(buff, len);
301 stream.write(t.levelName().c_str(), static_cast<std::streamsize>(t.levelName().size()));
302 }
303 // write UIDs
304 using p = pair<std::uint16_t, vector<std::uint64_t>>;
305 for (const auto &pair : initializer_list<p>{ p(MatroskaIds::TagTrackUID, t.tracks()), p(MatroskaIds::TagEditionUID, t.editions()),
307 if (!pair.second.empty()) {
308 BE::getBytes(pair.first, buff);
309 for (auto uid : pair.second) {
310 len = EbmlElement::makeUInteger(uid, buff + 3);
311 *(buff + 2) = static_cast<char>(0x80 | len);
312 stream.write(buff, 3 + len);
313 }
314 }
315 }
316 // write "SimpleTag" elements using maker objects prepared previously
317 for (const auto &maker : m_maker) {
318 maker.make(stream);
319 }
320}
321
322} // namespace TagParser
The Diagnostics class is a container for DiagMessage.
Definition: diagnostics.h:156
The EbmlElement class helps to parse EBML files such as Matroska files.
Definition: ebmlelement.h:32
static std::uint8_t makeUInteger(std::uint64_t value, char *buff)
Writes value to buff.
static std::uint8_t calculateSizeDenotationLength(std::uint64_t size)
Returns the length of the size denotation for the specified size in byte.
static std::uint8_t makeSizeDenotation(std::uint64_t size, char *buff)
Makes the size denotation for the specified size and stores it to buff.
static std::uint8_t calculateUIntegerLength(std::uint64_t integer)
Returns the length of the specified unsigned integer in byte.
The class inherits from std::exception and serves as base class for exceptions thrown by the elements...
Definition: exceptions.h:11
typename FieldMapBasedTagTraits< MatroskaTag >::FieldType::IdentifierType IdentifierType
Definition: fieldbasedtag.h:36
const std::multimap< IdentifierType, FieldType, Compare > & fields() const
Returns the fields of the tag by providing direct access to the field map of the tag.
KnownField knownField(const IdentifierType &id) const
Returns the field for the specified ID.
ImplementationType * nextSibling()
Returns the next sibling of the element.
ImplementationType * firstChild()
Returns the first child of the element.
std::uint64_t totalSize() const
Returns the total size of the element.
void parse(Diagnostics &diag)
Parses the header information of the element which is read from the related stream at the start offse...
The MatroskaTagField class is used by MatroskaTag to store the fields.
static void normalizeId(std::string &id)
Ensures the specified id is upper-case as recommended by the Matroska spec.
void make(std::ostream &stream) const
Saves the tag (specified when constructing the object) to the specified stream (makes a "Tag"-element...
void parse(EbmlElement &tagElement, Diagnostics &diag)
Parses tag information from the specified tagElement.
void parse2(EbmlElement &tagElement, MatroskaTagFlags flags, Diagnostics &diag)
Parses tag information from the specified tagElement.
IdentifierType internallyGetFieldId(KnownField field) const
Definition: matroskatag.cpp:20
KnownField internallyGetKnownField(const IdentifierType &id) const
Definition: matroskatag.cpp:73
This exception is thrown when the an operation is invoked that has not been implemented yet.
Definition: exceptions.h:60
The TagTarget class specifies the target of a tag.
Definition: tagtarget.h:20
const IdContainerType & tracks() const
Returns the tracks.
Definition: tagtarget.h:104
const IdContainerType & chapters() const
Returns the chapters.
Definition: tagtarget.h:120
std::uint64_t level() const
Returns the level.
Definition: tagtarget.h:72
const std::string & levelName() const
Returns the level name.
Definition: tagtarget.h:88
const IdContainerType & editions() const
Returns the editions.
Definition: tagtarget.h:136
void clear()
Clears the TagTarget.
Definition: tagtarget.h:176
const IdContainerType & attachments() const
Returns the attachments.
Definition: tagtarget.h:152
void setLevel(std::uint64_t level)
Sets the level.
Definition: tagtarget.h:80
void setLevelName(const std::string &levelName)
Sets the level name.
Definition: tagtarget.h:96
std::uint64_t m_size
Definition: tag.h:145
TagTarget m_target
Definition: tag.h:146
const TagTarget & target() const
Returns the target of tag.
Definition: tag.h:184
constexpr TAG_PARSER_EXPORT std::string_view bps()
constexpr TAG_PARSER_EXPORT std::string_view rating()
constexpr TAG_PARSER_EXPORT std::string_view album()
Definition: matroskatagid.h:80
constexpr TAG_PARSER_EXPORT std::string_view encoderSettings()
constexpr TAG_PARSER_EXPORT std::string_view description()
constexpr TAG_PARSER_EXPORT std::string_view title()
Definition: matroskatagid.h:38
constexpr TAG_PARSER_EXPORT std::string_view comment()
constexpr TAG_PARSER_EXPORT std::string_view language()
constexpr TAG_PARSER_EXPORT std::string_view actor()
constexpr TAG_PARSER_EXPORT std::string_view composer()
Definition: matroskatagid.h:92
constexpr TAG_PARSER_EXPORT std::string_view partNumber()
Definition: matroskatagid.h:29
constexpr TAG_PARSER_EXPORT std::string_view bpm()
constexpr TAG_PARSER_EXPORT std::string_view genre()
constexpr TAG_PARSER_EXPORT std::string_view duration()
constexpr TAG_PARSER_EXPORT std::string_view artist()
Definition: matroskatagid.h:76
constexpr TAG_PARSER_EXPORT std::string_view lyrics()
constexpr TAG_PARSER_EXPORT std::string_view encoder()
constexpr TAG_PARSER_EXPORT std::string_view dateRelease()
constexpr TAG_PARSER_EXPORT std::string_view totalParts()
Definition: matroskatagid.h:25
constexpr TAG_PARSER_EXPORT std::string_view dateRecorded()
constexpr TAG_PARSER_EXPORT std::string_view lyricist()
Contains all classes and functions of the TagInfo library.
Definition: aaccodebook.h:10
KnownField
Specifies the field.
Definition: tag.h:42
MatroskaTagFlags
The MatroskaTagFlags enum specifies flags which controls parsing and making of Matroska tags.
Definition: matroskatag.h:17