Tag Parser  7.1.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 ConversionUtilities;
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<uint32>::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<uint32>(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 
149 MatroskaTagMaker MatroskaTag::prepareMaking(Diagnostics &diag)
150 {
151  return MatroskaTagMaker(*this, diag);
152 }
153 
162 void MatroskaTag::make(ostream &stream, Diagnostics &diag)
163 {
164  prepareMaking(diag).make(stream);
165 }
166 
174 void MatroskaTag::parseTargets(EbmlElement &targetsElement, Diagnostics &diag)
175 {
176  static const string context("parsing targets of Matroska tag");
177  m_target.clear();
178  bool targetTypeValueFound = false;
179  bool targetTypeFound = false;
180  targetsElement.parse(diag);
181  for (EbmlElement *child = targetsElement.firstChild(); child; child = child->nextSibling()) {
182  try {
183  child->parse(diag);
184  } catch (const Failure &) {
185  diag.emplace_back(DiagLevel::Critical, "Unable to parse childs of Targets element.", context);
186  break;
187  }
188  switch (child->id()) {
190  if (!targetTypeValueFound) {
191  m_target.setLevel(child->readUInteger());
192  targetTypeValueFound = true;
193  } else {
194  diag.emplace_back(
195  DiagLevel::Warning, "Targets element contains multiple TargetTypeValue elements. Surplus elements will be ignored.", context);
196  }
197  break;
199  if (!targetTypeFound) {
200  m_target.setLevelName(child->readString());
201  targetTypeFound = true;
202  } else {
203  diag.emplace_back(
204  DiagLevel::Warning, "Targets element contains multiple TargetType elements. Surplus elements will be ignored.", context);
205  }
206  break;
208  m_target.tracks().emplace_back(child->readUInteger());
209  break;
211  m_target.editions().emplace_back(child->readUInteger());
212  break;
214  m_target.chapters().emplace_back(child->readUInteger());
215  break;
217  m_target.attachments().emplace_back(child->readUInteger());
218  break;
219  default:
220  diag.emplace_back(DiagLevel::Warning, "Targets element contains unknown element. It will be ignored.", context);
221  }
222  }
223  if (!m_target.level()) {
224  m_target.setLevel(50); // default level
225  }
226 }
227 
239 MatroskaTagMaker::MatroskaTagMaker(MatroskaTag &tag, Diagnostics &diag)
240  : m_tag(tag)
241 {
242  // calculate size of "Targets" element
243  m_targetsSize = 0; // NOT including ID and size
244  if (m_tag.target().level() != 50) {
245  // size of "TargetTypeValue"
246  m_targetsSize += 2 + 1 + EbmlElement::calculateUIntegerLength(m_tag.target().level());
247  }
248  if (!m_tag.target().levelName().empty()) {
249  // size of "TargetType"
250  m_targetsSize += 2 + EbmlElement::calculateSizeDenotationLength(m_tag.target().levelName().size()) + m_tag.target().levelName().size();
251  }
252  for (const auto &v : initializer_list<vector<uint64>>{
253  m_tag.target().tracks(), m_tag.target().editions(), m_tag.target().chapters(), m_tag.target().attachments() }) {
254  for (auto uid : v) {
255  // size of UID denotation
256  m_targetsSize += 2 + 1 + EbmlElement::calculateUIntegerLength(uid);
257  }
258  }
259  m_tagSize = 2 + EbmlElement::calculateSizeDenotationLength(m_targetsSize) + m_targetsSize;
260  // calculate size of "SimpleTag" elements
261  m_maker.reserve(m_tag.fields().size());
262  m_simpleTagsSize = 0; // including ID and size
263  for (auto &pair : m_tag.fields()) {
264  try {
265  m_maker.emplace_back(pair.second.prepareMaking(diag));
266  m_simpleTagsSize += m_maker.back().requiredSize();
267  } catch (const Failure &) {
268  }
269  }
270  m_tagSize += m_simpleTagsSize;
271  m_totalSize = 2 + EbmlElement::calculateSizeDenotationLength(m_tagSize) + m_tagSize;
272 }
273 
281 void MatroskaTagMaker::make(ostream &stream) const
282 {
283  // write header
284  char buff[11];
285  BE::getBytes(static_cast<uint16>(MatroskaIds::Tag), buff);
286  stream.write(buff, 2); // ID
287  byte len = EbmlElement::makeSizeDenotation(m_tagSize, buff);
288  stream.write(buff, len); // size
289  // write "Targets" element
290  BE::getBytes(static_cast<uint16>(MatroskaIds::Targets), buff);
291  stream.write(buff, 2);
292  len = EbmlElement::makeSizeDenotation(m_targetsSize, buff);
293  stream.write(buff, len);
294  const TagTarget &t = m_tag.target();
295  if (t.level() != 50) {
296  // write "TargetTypeValue"
297  BE::getBytes(static_cast<uint16>(MatroskaIds::TargetTypeValue), buff);
298  stream.write(buff, 2);
299  len = EbmlElement::makeUInteger(t.level(), buff);
300  stream.put(static_cast<char>(0x80 | len));
301  stream.write(buff, len);
302  }
303  if (!t.levelName().empty()) {
304  // write "TargetType"
305  BE::getBytes(static_cast<uint16>(MatroskaIds::TargetType), buff);
306  stream.write(buff, 2);
307  len = EbmlElement::makeSizeDenotation(t.levelName().size(), buff);
308  stream.write(buff, len);
309  stream.write(t.levelName().c_str(), t.levelName().size());
310  }
311  // write UIDs
312  typedef pair<uint16, vector<uint64>> p;
313  for (const auto &pair : initializer_list<p>{ p(MatroskaIds::TagTrackUID, t.tracks()), p(MatroskaIds::TagEditionUID, t.editions()),
315  if (!pair.second.empty()) {
316  BE::getBytes(pair.first, buff);
317  for (auto uid : pair.second) {
318  len = EbmlElement::makeUInteger(uid, buff + 3);
319  *(buff + 2) = static_cast<char>(0x80 | len);
320  stream.write(buff, 3 + len);
321  }
322  }
323  }
324  // write "SimpleTag" elements using maker objects prepared previously
325  for (const auto &maker : m_maker) {
326  maker.make(stream);
327  }
328 }
329 
330 } // namespace TagParser
TAG_PARSER_EXPORT const char * composer()
Definition: matroskatagid.h:93
TAG_PARSER_EXPORT const char * language()
FieldMapBasedTagTraits< MatroskaTag >::FieldType::IdentifierType IdentifierType
Definition: fieldbasedtag.h:36
This exception is thrown when the an operation is invoked that has not been implemented yet...
Definition: exceptions.h:53
TAG_PARSER_EXPORT const char * description()
TAG_PARSER_EXPORT const char * lyricist()
TAG_PARSER_EXPORT const char * encoderSettings()
TAG_PARSER_EXPORT const char * genre()
const IdContainerType & attachments() const
Returns the attachments.
Definition: tagtarget.h:152
ImplementationType * firstChild()
Returns the first child of the element.
TAG_PARSER_EXPORT const char * dateRelease()
TAG_PARSER_EXPORT const char * rating()
static byte calculateSizeDenotationLength(uint64 size)
Returns the length of the size denotation for the specified size in byte.
uint64 level() const
Returns the level.
Definition: tagtarget.h:72
STL namespace.
TAG_PARSER_EXPORT const char * lyrics()
The MatroskaTagMaker class helps writing Matroska "Tag"-elements storing tag information.
Definition: matroskatag.h:14
KnownField
Specifies the field.
Definition: tag.h:40
TAG_PARSER_EXPORT const char * comment()
TAG_PARSER_EXPORT const char * partNumber()
Definition: matroskatagid.h:30
static byte calculateUIntegerLength(uint64 integer)
Returns the length of the specified unsigned integer in byte.
The EbmlElement class helps to parse EBML files such as Matroska files.
Definition: ebmlelement.h:31
uint64 totalSize() const
Returns the total size of the element.
The MatroskaTagField class is used by MatroskaTag to store the fields.
const TagTarget & target() const
Returns the target of tag.
Definition: tag.h:232
TAG_PARSER_EXPORT const char * totalParts()
Definition: matroskatagid.h:26
void parse(Diagnostics &diag)
Parses the header information of the element which is read from the related stream at the start offse...
static byte makeUInteger(uint64 value, char *buff)
Writes value to buff.
static byte makeSizeDenotation(uint64 size, char *buff)
Makes the size denotation for the specified size and stores it to buff.
The TagTarget class specifies the target of a tag.
Definition: tagtarget.h:21
TAG_PARSER_EXPORT const char * artist()
Definition: matroskatagid.h:77
const IdentifierType & id() const
Returns the id of the current TagField.
TAG_PARSER_EXPORT const char * encoder()
TAG_PARSER_EXPORT const char * album()
Definition: matroskatagid.h:81
TAG_PARSER_EXPORT const char * duration()
void make(std::ostream &stream) const
Saves the tag (specified when constructing the object) to the specified stream (makes a "Tag"-element...
const IdContainerType & tracks() const
Returns the tracks.
Definition: tagtarget.h:104
TAG_PARSER_EXPORT const char * dateRecorded()
TAG_PARSER_EXPORT const char * bps()
TAG_PARSER_EXPORT const char * actor()
The class inherits from std::exception and serves as base class for exceptions thrown by the elements...
Definition: exceptions.h:11
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 & chapters() const
Returns the chapters.
Definition: tagtarget.h:120
Contains all classes and functions of the TagInfo library.
Definition: aaccodebook.h:9
TAG_PARSER_EXPORT const char * title()
Definition: matroskatagid.h:39
void reparse(EbmlElement &simpleTagElement, Diagnostics &diag, bool parseNestedFields=true)
Parses field information from the specified EbmlElement.
The Diagnostics class is a container for DiagMessage.
Definition: diagnostics.h:154
TAG_PARSER_EXPORT const char * bpm()