Tag Parser  9.1.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 <map>
8 #include <stdexcept>
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 artist();
26  case KnownField::Album:
27  return album();
29  return comment();
30  case KnownField::RecordDate:
31  return dateRecorded();
32  case KnownField::Year:
33  return dateRelease();
34  case KnownField::Title:
35  return title();
36  case KnownField::Genre:
37  return genre();
38  case KnownField::PartNumber:
39  return partNumber();
40  case KnownField::TotalParts:
41  return totalParts();
43  return encoder();
44  case KnownField::EncoderSettings:
45  return encoderSettings();
46  case KnownField::Bpm:
47  return bpm();
48  case KnownField::Bps:
49  return bps();
50  case KnownField::Rating:
51  return rating();
53  return description();
54  case KnownField::Lyrics:
55  return lyrics();
57  return label();
59  return actor();
61  return lyricist();
63  return composer();
64  case KnownField::Length:
65  return duration();
66  case KnownField::Language:
67  return language();
68  default:
69  return string();
70  }
71 }
72 
73 KnownField MatroskaTag::internallyGetKnownField(const IdentifierType &id) const
74 {
75  using namespace MatroskaTagIds;
76  static const map<string, KnownField> fieldMap({
78  { album(), KnownField::Album },
80  { dateRecorded(), KnownField::RecordDate },
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 childs 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 += 2 + 1 + EbmlElement::calculateUIntegerLength(m_tag.target().level());
218  }
219  if (!m_tag.target().levelName().empty()) {
220  // size of "TargetType"
221  m_targetsSize += 2 + 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 += 2 + 1 + EbmlElement::calculateUIntegerLength(uid);
228  }
229  }
230  m_tagSize = 2 + 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 = 2 + 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(), 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
TagParser::EbmlElement::makeUInteger
static std::uint8_t makeUInteger(std::uint64_t value, char *buff)
Writes value to buff.
Definition: ebmlelement.cpp:440
TagParser::MatroskaIds::SimpleTag
Definition: matroskaid.h:211
TagParser::TagTarget::level
std::uint64_t level() const
Returns the level.
Definition: tagtarget.h:72
TagParser::MatroskaTagIds::bps
constexpr const TAG_PARSER_EXPORT char * bps()
Definition: matroskatagid.h:330
TagParser::EbmlElement::calculateUIntegerLength
static std::uint8_t calculateUIntegerLength(std::uint64_t integer)
Returns the length of the specified unsigned integer in byte.
Definition: ebmlelement.cpp:415
TagParser::Mp4TagAtomIds::Album
Definition: mp4ids.h:86
TagParser::MatroskaTagIds::title
constexpr const TAG_PARSER_EXPORT char * title()
Definition: matroskatagid.h:39
TagParser::Mp4TagAtomIds::Lyrics
Definition: mp4ids.h:105
TagParser::Mp4TagAtomIds::Encoder
Definition: mp4ids.h:97
TagParser::FieldMapBasedTag< MatroskaTag >::IdentifierType
typename FieldMapBasedTagTraits< MatroskaTag >::FieldType::IdentifierType IdentifierType
Definition: fieldbasedtag.h:36
TagParser::EbmlElement::calculateSizeDenotationLength
static std::uint8_t calculateSizeDenotationLength(std::uint64_t size)
Returns the length of the size denotation for the specified size in byte.
Definition: ebmlelement.cpp:288
TagParser::TagTarget::tracks
const IdContainerType & tracks() const
Returns the tracks.
Definition: tagtarget.h:104
TagParser::MatroskaTagIds::bpm
constexpr const TAG_PARSER_EXPORT char * bpm()
Definition: matroskatagid.h:338
TagParser::MatroskaTagIds::dateRelease
constexpr const TAG_PARSER_EXPORT char * dateRelease()
Definition: matroskatagid.h:267
TagParser::Mp4TagExtendedNameIds::label
const char * label
Definition: mp4ids.cpp:31
TagParser::Diagnostics
The Diagnostics class is a container for DiagMessage.
Definition: diagnostics.h:156
TagParser::TagTarget::editions
const IdContainerType & editions() const
Returns the editions.
Definition: tagtarget.h:136
TagParser::MatroskaTagIds::dateRecorded
constexpr const TAG_PARSER_EXPORT char * dateRecorded()
Definition: matroskatagid.h:271
TagParser
Contains all classes and functions of the TagInfo library.
Definition: aaccodebook.h:10
TagParser::MatroskaTagIds::totalParts
constexpr const TAG_PARSER_EXPORT char * totalParts()
Definition: matroskatagid.h:26
TagParser::MatroskaIds::TagEditionUID
Definition: matroskaid.h:233
TagParser::MatroskaTagIds::encoderSettings
constexpr const TAG_PARSER_EXPORT char * encoderSettings()
Definition: matroskatagid.h:326
TagParser::MatroskaTagIds::genre
constexpr const TAG_PARSER_EXPORT char * genre()
Definition: matroskatagid.h:214
TagParser::MatroskaTagIds::composer
constexpr const TAG_PARSER_EXPORT char * composer()
Definition: matroskatagid.h:93
TagParser::TagField::id
const IdentifierType & id() const
Returns the id of the current TagField.
Definition: generictagfield.h:115
TagParser::GenericFileElement::parse
void parse(Diagnostics &diag)
Parses the header information of the element which is read from the related stream at the start offse...
Definition: genericfileelement.h:771
TagParser::MatroskaTagIds::lyricist
constexpr const TAG_PARSER_EXPORT char * lyricist()
Definition: matroskatagid.h:105
TagParser::MatroskaIds::TagAttachmentUID
Definition: matroskaid.h:235
TagParser::MatroskaTagIds::language
constexpr const TAG_PARSER_EXPORT char * language()
Definition: matroskatagid.h:346
matroskatag.h
TagParser::MatroskaTagIds::duration
constexpr const TAG_PARSER_EXPORT char * duration()
Definition: matroskatagid.h:342
TagParser::MatroskaIds::Title
Definition: matroskaid.h:54
TagParser::GenericFileElement::firstChild
ImplementationType * firstChild()
Returns the first child of the element.
Definition: genericfileelement.h:460
TagParser::Mp4TagAtomIds::Year
Definition: mp4ids.h:122
TagParser::Failure
The class inherits from std::exception and serves as base class for exceptions thrown by the elements...
Definition: exceptions.h:11
TagParser::EbmlElement::makeSizeDenotation
static std::uint8_t makeSizeDenotation(std::uint64_t size, char *buff)
Makes the size denotation for the specified size and stores it to buff.
Definition: ebmlelement.cpp:343
TagParser::Tag::target
const TagTarget & target() const
Returns the target of tag.
Definition: tag.h:174
TagParser::TagTarget::chapters
const IdContainerType & chapters() const
Returns the chapters.
Definition: tagtarget.h:120
TagParser::MatroskaIds::TargetType
Definition: matroskaid.h:231
TagParser::Mp4TagAtomIds::Lyricist
Definition: mp4ids.h:104
TagParser::EbmlElement
The EbmlElement class helps to parse EBML files such as Matroska files.
Definition: ebmlelement.h:31
TagParser::MatroskaTagField
The MatroskaTagField class is used by MatroskaTag to store the fields.
Definition: matroskatagfield.h:65
TagParser::MatroskaIds::Targets
Definition: matroskaid.h:212
TagParser::Mp4TagAtomIds::Description
Definition: mp4ids.h:95
TagParser::MatroskaIds::TagChapterUID
Definition: matroskaid.h:234
CppUtilities
Definition: abstractcontainer.h:15
TagParser::TagTarget::levelName
const std::string & levelName() const
Returns the level name.
Definition: tagtarget.h:88
TagParser::Mp4TagAtomIds::Genre
Definition: mp4ids.h:101
TagParser::MatroskaTagIds::artist
constexpr const TAG_PARSER_EXPORT char * artist()
Definition: matroskatagid.h:77
TagParser::MatroskaIds::TagTrackUID
Definition: matroskaid.h:232
TagParser::Mp4TagAtomIds::Rating
Definition: mp4ids.h:113
TagParser::MatroskaTagIds::actor
constexpr const TAG_PARSER_EXPORT char * actor()
Definition: matroskatagid.h:145
TagParser::MatroskaTagIds::partNumber
constexpr const TAG_PARSER_EXPORT char * partNumber()
Definition: matroskatagid.h:30
TagParser::MatroskaTagMaker::make
void make(std::ostream &stream) const
Saves the tag (specified when constructing the object) to the specified stream (makes a "Tag"-element...
Definition: matroskatag.cpp:252
TagParser::KnownField
KnownField
Specifies the field.
Definition: tag.h:42
TagParser::TagTarget::attachments
const IdContainerType & attachments() const
Returns the attachments.
Definition: tagtarget.h:152
TagParser::MatroskaTagIds::lyrics
constexpr const TAG_PARSER_EXPORT char * lyrics()
Definition: matroskatagid.h:101
TagParser::TagTarget
The TagTarget class specifies the target of a tag.
Definition: tagtarget.h:20
TagParser::Mp4TagAtomIds::Bpm
Definition: mp4ids.h:89
TagParser::MatroskaTagIds::comment
constexpr const TAG_PARSER_EXPORT char * comment()
Definition: matroskatagid.h:309
TagParser::MatroskaIds::Tag
Definition: matroskaid.h:204
ebmlelement.h
TagParser::MatroskaTagIds::encoder
constexpr const TAG_PARSER_EXPORT char * encoder()
Definition: matroskatagid.h:322
TagParser::MatroskaTagIds::album
constexpr const TAG_PARSER_EXPORT char * album()
Definition: matroskatagid.h:81
TagParser::Mp4TagAtomIds::Artist
Definition: mp4ids.h:88
TagParser::MatroskaTagIds::rating
constexpr const TAG_PARSER_EXPORT char * rating()
Definition: matroskatagid.h:317
TagParser::Mp4TagAtomIds::RecordLabel
Definition: mp4ids.h:114
TagParser::Mp4TagAtomIds::Composer
Definition: mp4ids.h:92
TagParser::Mp4TagAtomIds::Comment
Definition: mp4ids.h:91
TagParser::Mp4TagAtomIds::Performers
Definition: mp4ids.h:107
TagParser::MatroskaIds::TargetTypeValue
Definition: matroskaid.h:230
TagParser::GenericFileElement::totalSize
std::uint64_t totalSize() const
Returns the total size of the element.
Definition: genericfileelement.h:343
TagParser::NotImplementedException
This exception is thrown when the an operation is invoked that has not been implemented yet.
Definition: exceptions.h:60
TagParser::MatroskaTagIds::description
constexpr const TAG_PARSER_EXPORT char * description()
Definition: matroskatagid.h:234
TagParser::MatroskaTagField::reparse
void reparse(EbmlElement &simpleTagElement, Diagnostics &diag, bool parseNestedFields=true)
Parses field information from the specified EbmlElement.
Definition: matroskatagfield.cpp:46