Tag Parser  9.3.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 <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  case KnownField::Year:
32  return dateRecorded();
33  case KnownField::ReleaseDate:
34  return dateRelease();
35  case KnownField::Title:
36  return title();
37  case KnownField::Genre:
38  return genre();
39  case KnownField::PartNumber:
40  return partNumber();
41  case KnownField::TotalParts:
42  return totalParts();
44  return encoder();
45  case KnownField::EncoderSettings:
46  return encoderSettings();
47  case KnownField::Bpm:
48  return bpm();
49  case KnownField::Bps:
50  return bps();
51  case KnownField::Rating:
52  return rating();
54  return description();
55  case KnownField::Lyrics:
56  return lyrics();
58  return label();
60  return actor();
62  return lyricist();
64  return composer();
65  case KnownField::Length:
66  return duration();
67  case KnownField::Language:
68  return language();
69  default:
70  return string();
71  }
72 }
73 
74 KnownField MatroskaTag::internallyGetKnownField(const IdentifierType &id) const
75 {
76  using namespace MatroskaTagIds;
77  static const map<string, KnownField> fieldMap({
79  { album(), KnownField::Album },
81  { dateRecorded(), KnownField::RecordDate },
82  { dateRelease(), KnownField::ReleaseDate },
83  { title(), KnownField::Title },
84  { partNumber(), KnownField::PartNumber },
85  { totalParts(), KnownField::TotalParts },
87  { encoderSettings(), KnownField::EncoderSettings },
88  { bpm(), KnownField::Bpm },
89  { bps(), KnownField::Bps },
97  { duration(), KnownField::Length },
98  { language(), KnownField::Language },
99  });
100  const auto knownField(fieldMap.find(id));
101  return knownField != fieldMap.cend() ? knownField->second : KnownField::Invalid;
102 }
103 
111 void MatroskaTag::parse(EbmlElement &tagElement, Diagnostics &diag)
112 {
113  static const string context("parsing Matroska tag");
114  tagElement.parse(diag);
115  if (tagElement.totalSize() > numeric_limits<std::uint32_t>::max()) {
116  // FIXME: Support this? Likely not very useful in practise.
117  diag.emplace_back(DiagLevel::Critical, "Matroska tag is too big.", context);
118  throw NotImplementedException();
119  }
120  m_size = static_cast<std::uint32_t>(tagElement.totalSize());
121  for (EbmlElement *child = tagElement.firstChild(); child; child = child->nextSibling()) {
122  child->parse(diag);
123  switch (child->id()) {
125  try {
126  MatroskaTagField field;
127  field.reparse(*child, diag, true);
128  fields().emplace(field.id(), move(field));
129  } catch (const Failure &) {
130  }
131  break;
133  parseTargets(*child, diag);
134  break;
135  }
136  }
137 }
138 
146 void MatroskaTag::parseTargets(EbmlElement &targetsElement, Diagnostics &diag)
147 {
148  static const string context("parsing targets of Matroska tag");
149  m_target.clear();
150  bool targetTypeValueFound = false;
151  bool targetTypeFound = false;
152  targetsElement.parse(diag);
153  for (EbmlElement *child = targetsElement.firstChild(); child; child = child->nextSibling()) {
154  try {
155  child->parse(diag);
156  } catch (const Failure &) {
157  diag.emplace_back(DiagLevel::Critical, "Unable to parse children of Targets element.", context);
158  break;
159  }
160  switch (child->id()) {
162  if (!targetTypeValueFound) {
163  m_target.setLevel(child->readUInteger());
164  targetTypeValueFound = true;
165  } else {
166  diag.emplace_back(
167  DiagLevel::Warning, "Targets element contains multiple TargetTypeValue elements. Surplus elements will be ignored.", context);
168  }
169  break;
171  if (!targetTypeFound) {
172  m_target.setLevelName(child->readString());
173  targetTypeFound = true;
174  } else {
175  diag.emplace_back(
176  DiagLevel::Warning, "Targets element contains multiple TargetType elements. Surplus elements will be ignored.", context);
177  }
178  break;
180  m_target.tracks().emplace_back(child->readUInteger());
181  break;
183  m_target.editions().emplace_back(child->readUInteger());
184  break;
186  m_target.chapters().emplace_back(child->readUInteger());
187  break;
189  m_target.attachments().emplace_back(child->readUInteger());
190  break;
191  default:
192  diag.emplace_back(DiagLevel::Warning, "Targets element contains unknown element. It will be ignored.", context);
193  }
194  }
195  if (!m_target.level()) {
196  m_target.setLevel(50); // default level
197  }
198 }
199 
211 MatroskaTagMaker::MatroskaTagMaker(MatroskaTag &tag, Diagnostics &diag)
212  : m_tag(tag)
213 {
214  // calculate size of "Targets" element
215  m_targetsSize = 0; // NOT including ID and size
216  if (m_tag.target().level() != 50) {
217  // size of "TargetTypeValue"
218  m_targetsSize += 2 + 1 + EbmlElement::calculateUIntegerLength(m_tag.target().level());
219  }
220  if (!m_tag.target().levelName().empty()) {
221  // size of "TargetType"
222  m_targetsSize += 2 + EbmlElement::calculateSizeDenotationLength(m_tag.target().levelName().size()) + m_tag.target().levelName().size();
223  }
224  for (const auto &v : initializer_list<vector<std::uint64_t>>{
225  m_tag.target().tracks(), m_tag.target().editions(), m_tag.target().chapters(), m_tag.target().attachments() }) {
226  for (auto uid : v) {
227  // size of UID denotation
228  m_targetsSize += 2 + 1 + EbmlElement::calculateUIntegerLength(uid);
229  }
230  }
231  m_tagSize = 2 + EbmlElement::calculateSizeDenotationLength(m_targetsSize) + m_targetsSize;
232  // calculate size of "SimpleTag" elements
233  m_maker.reserve(m_tag.fields().size());
234  m_simpleTagsSize = 0; // including ID and size
235  for (auto &pair : m_tag.fields()) {
236  try {
237  m_maker.emplace_back(pair.second.prepareMaking(diag));
238  m_simpleTagsSize += m_maker.back().requiredSize();
239  } catch (const Failure &) {
240  }
241  }
242  m_tagSize += m_simpleTagsSize;
243  m_totalSize = 2 + EbmlElement::calculateSizeDenotationLength(m_tagSize) + m_tagSize;
244 }
245 
253 void MatroskaTagMaker::make(ostream &stream) const
254 {
255  // write header
256  char buff[11];
257  BE::getBytes(static_cast<std::uint16_t>(MatroskaIds::Tag), buff);
258  stream.write(buff, 2); // ID
259  std::uint8_t len = EbmlElement::makeSizeDenotation(m_tagSize, buff);
260  stream.write(buff, len); // size
261  // write "Targets" element
262  BE::getBytes(static_cast<std::uint16_t>(MatroskaIds::Targets), buff);
263  stream.write(buff, 2);
264  len = EbmlElement::makeSizeDenotation(m_targetsSize, buff);
265  stream.write(buff, len);
266  const TagTarget &t = m_tag.target();
267  if (t.level() != 50) {
268  // write "TargetTypeValue"
269  BE::getBytes(static_cast<std::uint16_t>(MatroskaIds::TargetTypeValue), buff);
270  stream.write(buff, 2);
271  len = EbmlElement::makeUInteger(t.level(), buff);
272  stream.put(static_cast<char>(0x80 | len));
273  stream.write(buff, len);
274  }
275  if (!t.levelName().empty()) {
276  // write "TargetType"
277  BE::getBytes(static_cast<std::uint16_t>(MatroskaIds::TargetType), buff);
278  stream.write(buff, 2);
279  len = EbmlElement::makeSizeDenotation(t.levelName().size(), buff);
280  stream.write(buff, len);
281  stream.write(t.levelName().c_str(), t.levelName().size());
282  }
283  // write UIDs
284  using p = pair<std::uint16_t, vector<std::uint64_t>>;
285  for (const auto &pair : initializer_list<p>{ p(MatroskaIds::TagTrackUID, t.tracks()), p(MatroskaIds::TagEditionUID, t.editions()),
287  if (!pair.second.empty()) {
288  BE::getBytes(pair.first, buff);
289  for (auto uid : pair.second) {
290  len = EbmlElement::makeUInteger(uid, buff + 3);
291  *(buff + 2) = static_cast<char>(0x80 | len);
292  stream.write(buff, 3 + len);
293  }
294  }
295  }
296  // write "SimpleTag" elements using maker objects prepared previously
297  for (const auto &maker : m_maker) {
298  maker.make(stream);
299  }
300 }
301 
302 } // 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
@ SimpleTag
Definition: matroskaid.h:211
TagParser::TagTarget::level
std::uint64_t level() const
Returns the level.
Definition: tagtarget.h:72
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
@ Album
Definition: mp4ids.h:86
TagParser::MatroskaTagIds::description
constexpr TAG_PARSER_EXPORT const char * description()
Definition: matroskatagid.h:231
TagParser::Mp4TagAtomIds::Lyrics
@ Lyrics
Definition: mp4ids.h:105
TagParser::Mp4TagAtomIds::Encoder
@ 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::Mp4TagExtendedNameIds::label
const char * label
Definition: mp4ids.cpp:32
TagParser::MatroskaTagIds::lyrics
constexpr TAG_PARSER_EXPORT const char * lyrics()
Definition: matroskatagid.h:98
TagParser::MatroskaTagIds::lyricist
constexpr TAG_PARSER_EXPORT const char * lyricist()
Definition: matroskatagid.h:102
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::bpm
constexpr TAG_PARSER_EXPORT const char * bpm()
Definition: matroskatagid.h:335
TagParser
Contains all classes and functions of the TagInfo library.
Definition: aaccodebook.h:10
TagParser::MatroskaIds::TagEditionUID
@ TagEditionUID
Definition: matroskaid.h:233
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::MatroskaIds::TagAttachmentUID
@ TagAttachmentUID
Definition: matroskaid.h:235
matroskatag.h
TagParser::MatroskaTagIds::dateRelease
constexpr TAG_PARSER_EXPORT const char * dateRelease()
Definition: matroskatagid.h:264
TagParser::MatroskaTagIds::artist
constexpr TAG_PARSER_EXPORT const char * artist()
Definition: matroskatagid.h:74
TagParser::MatroskaIds::Title
@ Title
Definition: matroskaid.h:54
TagParser::GenericFileElement::firstChild
ImplementationType * firstChild()
Returns the first child of the element.
Definition: genericfileelement.h:460
TagParser::Mp4TagAtomIds::Year
@ 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::MatroskaTagIds::dateRecorded
constexpr TAG_PARSER_EXPORT const char * dateRecorded()
Definition: matroskatagid.h:268
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:184
TagParser::MatroskaTagIds::actor
constexpr TAG_PARSER_EXPORT const char * actor()
Definition: matroskatagid.h:142
TagParser::TagTarget::chapters
const IdContainerType & chapters() const
Returns the chapters.
Definition: tagtarget.h:120
TagParser::MatroskaTagIds::encoder
constexpr TAG_PARSER_EXPORT const char * encoder()
Definition: matroskatagid.h:319
TagParser::MatroskaIds::TargetType
@ TargetType
Definition: matroskaid.h:231
TagParser::Mp4TagAtomIds::Lyricist
@ 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::MatroskaTagIds::partNumber
constexpr TAG_PARSER_EXPORT const char * partNumber()
Definition: matroskatagid.h:27
TagParser::MatroskaIds::Targets
@ Targets
Definition: matroskaid.h:212
TagParser::Mp4TagAtomIds::Description
@ Description
Definition: mp4ids.h:95
TagParser::MatroskaTagIds::language
constexpr TAG_PARSER_EXPORT const char * language()
Definition: matroskatagid.h:343
TagParser::MatroskaIds::TagChapterUID
@ 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
@ Genre
Definition: mp4ids.h:101
TagParser::MatroskaIds::TagTrackUID
@ TagTrackUID
Definition: matroskaid.h:232
TagParser::Mp4TagAtomIds::Rating
@ Rating
Definition: mp4ids.h:113
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:253
TagParser::KnownField
KnownField
Specifies the field.
Definition: tag.h:42
TagParser::MatroskaTagIds::bps
constexpr TAG_PARSER_EXPORT const char * bps()
Definition: matroskatagid.h:327
TagParser::MatroskaTagIds::comment
constexpr TAG_PARSER_EXPORT const char * comment()
Definition: matroskatagid.h:306
TagParser::TagTarget::attachments
const IdContainerType & attachments() const
Returns the attachments.
Definition: tagtarget.h:152
TagParser::TagTarget
The TagTarget class specifies the target of a tag.
Definition: tagtarget.h:20
TagParser::Mp4TagAtomIds::Bpm
@ Bpm
Definition: mp4ids.h:89
TagParser::MatroskaIds::Tag
@ Tag
Definition: matroskaid.h:204
ebmlelement.h
TagParser::MatroskaTagIds::totalParts
constexpr TAG_PARSER_EXPORT const char * totalParts()
Definition: matroskatagid.h:23
TagParser::MatroskaTagIds::album
constexpr TAG_PARSER_EXPORT const char * album()
Definition: matroskatagid.h:78
TagParser::Mp4TagAtomIds::Artist
@ Artist
Definition: mp4ids.h:88
TagParser::MatroskaTagIds::composer
constexpr TAG_PARSER_EXPORT const char * composer()
Definition: matroskatagid.h:90
TagParser::MatroskaTagIds::title
constexpr TAG_PARSER_EXPORT const char * title()
Definition: matroskatagid.h:36
TagParser::Mp4TagAtomIds::RecordLabel
@ RecordLabel
Definition: mp4ids.h:114
TagParser::MatroskaTagIds::encoderSettings
constexpr TAG_PARSER_EXPORT const char * encoderSettings()
Definition: matroskatagid.h:323
TagParser::MatroskaTagIds::genre
constexpr TAG_PARSER_EXPORT const char * genre()
Definition: matroskatagid.h:211
TagParser::Mp4TagAtomIds::Composer
@ Composer
Definition: mp4ids.h:92
TagParser::Mp4TagAtomIds::Comment
@ Comment
Definition: mp4ids.h:91
TagParser::MatroskaTagIds::rating
constexpr TAG_PARSER_EXPORT const char * rating()
Definition: matroskatagid.h:314
TagParser::MatroskaTagIds::duration
constexpr TAG_PARSER_EXPORT const char * duration()
Definition: matroskatagid.h:339
TagParser::Mp4TagAtomIds::Performers
@ Performers
Definition: mp4ids.h:107
TagParser::MatroskaIds::TargetTypeValue
@ 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::MatroskaTagField::reparse
void reparse(EbmlElement &simpleTagElement, Diagnostics &diag, bool parseNestedFields=true)
Parses field information from the specified EbmlElement.
Definition: matroskatagfield.cpp:46