Tag Parser  10.0.0
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 
10 using namespace std;
11 using namespace CppUtilities;
12 
13 namespace TagParser {
14 
20 MatroskaTag::IdentifierType MatroskaTag::internallyGetFieldId(KnownField field) const
21 {
22  using namespace MatroskaTagIds;
23  switch (field) {
24  case KnownField::Artist:
25  return std::string(artist());
26  case KnownField::Album:
27  return std::string(album());
29  return std::string(comment());
30  case KnownField::RecordDate:
31  return std::string(dateRecorded());
32  case KnownField::ReleaseDate:
33  return std::string(dateRelease());
34  case KnownField::Title:
35  return std::string(title());
36  case KnownField::Genre:
37  return std::string(genre());
38  case KnownField::PartNumber:
39  return std::string(partNumber());
40  case KnownField::TotalParts:
41  return std::string(totalParts());
43  return std::string(encoder());
44  case KnownField::EncoderSettings:
45  return std::string(encoderSettings());
46  case KnownField::Bpm:
47  return std::string(bpm());
48  case KnownField::Bps:
49  return std::string(bps());
50  case KnownField::Rating:
51  return std::string(rating());
53  return std::string(description());
54  case KnownField::Lyrics:
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());
64  case KnownField::Length:
65  return std::string(duration());
66  case KnownField::Language:
67  return std::string(language());
68  default:
69  return std::string();
70  }
71 }
72 
73 KnownField MatroskaTag::internallyGetKnownField(const IdentifierType &id) const
74 {
75  using namespace MatroskaTagIds;
76  static const std::unordered_map<std::string_view, KnownField> fieldMap({
78  { album(), KnownField::Album },
80  { dateRecorded(), KnownField::RecordDate },
81  { dateRelease(), KnownField::ReleaseDate },
82  { title(), KnownField::Title },
83  { partNumber(), KnownField::PartNumber },
84  { totalParts(), KnownField::TotalParts },
86  { encoderSettings(), KnownField::EncoderSettings },
87  { bpm(), KnownField::Bpm },
88  { bps(), KnownField::Bps },
96  { duration(), KnownField::Length },
97  { language(), KnownField::Language },
98  });
99  const auto knownField(fieldMap.find(id));
100  return knownField != fieldMap.cend() ? knownField->second : KnownField::Invalid;
101 }
102 
110 void MatroskaTag::parse(EbmlElement &tagElement, Diagnostics &diag)
111 {
112  static const string context("parsing Matroska tag");
113  tagElement.parse(diag);
114  if (tagElement.totalSize() > numeric_limits<std::uint32_t>::max()) {
115  // FIXME: Support this? Likely not very useful in practise.
116  diag.emplace_back(DiagLevel::Critical, "Matroska tag is too big.", context);
117  throw NotImplementedException();
118  }
119  m_size = static_cast<std::uint32_t>(tagElement.totalSize());
120  for (EbmlElement *child = tagElement.firstChild(); child; child = child->nextSibling()) {
121  child->parse(diag);
122  switch (child->id()) {
124  try {
125  MatroskaTagField field;
126  field.reparse(*child, diag, true);
127  fields().emplace(field.id(), move(field));
128  } catch (const Failure &) {
129  }
130  break;
132  parseTargets(*child, diag);
133  break;
134  }
135  }
136 }
137 
145 void MatroskaTag::parseTargets(EbmlElement &targetsElement, Diagnostics &diag)
146 {
147  static const string context("parsing targets of Matroska tag");
148  m_target.clear();
149  bool targetTypeValueFound = false;
150  bool targetTypeFound = false;
151  targetsElement.parse(diag);
152  for (EbmlElement *child = targetsElement.firstChild(); child; child = child->nextSibling()) {
153  try {
154  child->parse(diag);
155  } catch (const Failure &) {
156  diag.emplace_back(DiagLevel::Critical, "Unable to parse children of Targets element.", context);
157  break;
158  }
159  switch (child->id()) {
161  if (!targetTypeValueFound) {
162  m_target.setLevel(child->readUInteger());
163  targetTypeValueFound = true;
164  } else {
165  diag.emplace_back(
166  DiagLevel::Warning, "Targets element contains multiple TargetTypeValue elements. Surplus elements will be ignored.", context);
167  }
168  break;
170  if (!targetTypeFound) {
171  m_target.setLevelName(child->readString());
172  targetTypeFound = true;
173  } else {
174  diag.emplace_back(
175  DiagLevel::Warning, "Targets element contains multiple TargetType elements. Surplus elements will be ignored.", context);
176  }
177  break;
179  m_target.tracks().emplace_back(child->readUInteger());
180  break;
182  m_target.editions().emplace_back(child->readUInteger());
183  break;
185  m_target.chapters().emplace_back(child->readUInteger());
186  break;
188  m_target.attachments().emplace_back(child->readUInteger());
189  break;
190  default:
191  diag.emplace_back(DiagLevel::Warning, "Targets element contains unknown element. It will be ignored.", context);
192  }
193  }
194  if (!m_target.level()) {
195  m_target.setLevel(50); // default level
196  }
197 }
198 
210 MatroskaTagMaker::MatroskaTagMaker(MatroskaTag &tag, Diagnostics &diag)
211  : m_tag(tag)
212 {
213  // calculate size of "Targets" element
214  m_targetsSize = 0; // NOT including ID and size
215  if (m_tag.target().level() != 50) {
216  // size of "TargetTypeValue"
217  m_targetsSize += 2u + 1u + EbmlElement::calculateUIntegerLength(m_tag.target().level());
218  }
219  if (!m_tag.target().levelName().empty()) {
220  // size of "TargetType"
221  m_targetsSize += 2u + EbmlElement::calculateSizeDenotationLength(m_tag.target().levelName().size()) + m_tag.target().levelName().size();
222  }
223  for (const auto &v : initializer_list<vector<std::uint64_t>>{
224  m_tag.target().tracks(), m_tag.target().editions(), m_tag.target().chapters(), m_tag.target().attachments() }) {
225  for (auto uid : v) {
226  // size of UID denotation
227  m_targetsSize += 2u + 1u + EbmlElement::calculateUIntegerLength(uid);
228  }
229  }
230  m_tagSize = 2u + EbmlElement::calculateSizeDenotationLength(m_targetsSize) + m_targetsSize;
231  // calculate size of "SimpleTag" elements
232  m_maker.reserve(m_tag.fields().size());
233  m_simpleTagsSize = 0; // including ID and size
234  for (auto &pair : m_tag.fields()) {
235  try {
236  m_maker.emplace_back(pair.second.prepareMaking(diag));
237  m_simpleTagsSize += m_maker.back().requiredSize();
238  } catch (const Failure &) {
239  }
240  }
241  m_tagSize += m_simpleTagsSize;
242  m_totalSize = 2u + EbmlElement::calculateSizeDenotationLength(m_tagSize) + m_tagSize;
243 }
244 
252 void MatroskaTagMaker::make(ostream &stream) const
253 {
254  // write header
255  char buff[11];
256  BE::getBytes(static_cast<std::uint16_t>(MatroskaIds::Tag), buff);
257  stream.write(buff, 2); // ID
258  std::uint8_t len = EbmlElement::makeSizeDenotation(m_tagSize, buff);
259  stream.write(buff, len); // size
260  // write "Targets" element
261  BE::getBytes(static_cast<std::uint16_t>(MatroskaIds::Targets), buff);
262  stream.write(buff, 2);
263  len = EbmlElement::makeSizeDenotation(m_targetsSize, buff);
264  stream.write(buff, len);
265  const TagTarget &t = m_tag.target();
266  if (t.level() != 50) {
267  // write "TargetTypeValue"
268  BE::getBytes(static_cast<std::uint16_t>(MatroskaIds::TargetTypeValue), buff);
269  stream.write(buff, 2);
270  len = EbmlElement::makeUInteger(t.level(), buff);
271  stream.put(static_cast<char>(0x80 | len));
272  stream.write(buff, len);
273  }
274  if (!t.levelName().empty()) {
275  // write "TargetType"
276  BE::getBytes(static_cast<std::uint16_t>(MatroskaIds::TargetType), buff);
277  stream.write(buff, 2);
278  len = EbmlElement::makeSizeDenotation(t.levelName().size(), buff);
279  stream.write(buff, len);
280  stream.write(t.levelName().c_str(), static_cast<std::streamsize>(t.levelName().size()));
281  }
282  // write UIDs
283  using p = pair<std::uint16_t, vector<std::uint64_t>>;
284  for (const auto &pair : initializer_list<p>{ p(MatroskaIds::TagTrackUID, t.tracks()), p(MatroskaIds::TagEditionUID, t.editions()),
286  if (!pair.second.empty()) {
287  BE::getBytes(pair.first, buff);
288  for (auto uid : pair.second) {
289  len = EbmlElement::makeUInteger(uid, buff + 3);
290  *(buff + 2) = static_cast<char>(0x80 | len);
291  stream.write(buff, 3 + len);
292  }
293  }
294  }
295  // write "SimpleTag" elements using maker objects prepared previously
296  for (const auto &maker : m_maker) {
297  maker.make(stream);
298  }
299 }
300 
301 } // 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
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.
void reparse(EbmlElement &simpleTagElement, Diagnostics &diag, bool parseNestedFields=true)
Parses field information from the specified EbmlElement.
void make(std::ostream &stream) const
Saves the tag (specified when constructing the object) to the specified stream (makes a "Tag"-element...
This exception is thrown when the an operation is invoked that has not been implemented yet.
Definition: exceptions.h:60
const IdentifierType & id() const
Returns the id of the current TagField.
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
const IdContainerType & attachments() const
Returns the attachments.
Definition: tagtarget.h:152
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