Tag Parser  7.0.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 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> map({
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  try {
100  return map.at(id);
101  } catch (const out_of_range &) {
102  return KnownField::Invalid;
103  }
104 }
105 
113 void MatroskaTag::parse(EbmlElement &tagElement, Diagnostics &diag)
114 {
115  static const string context("parsing Matroska tag");
116  tagElement.parse(diag);
117  m_size = tagElement.totalSize();
118  for (EbmlElement *child = tagElement.firstChild(); child; child = child->nextSibling()) {
119  child->parse(diag);
120  switch (child->id()) {
122  try {
123  MatroskaTagField field;
124  field.reparse(*child, diag, true);
125  fields().emplace(field.id(), move(field));
126  } catch (const Failure &) {
127  }
128  break;
130  parseTargets(*child, diag);
131  break;
132  }
133  }
134 }
135 
147 MatroskaTagMaker MatroskaTag::prepareMaking(Diagnostics &diag)
148 {
149  return MatroskaTagMaker(*this, diag);
150 }
151 
160 void MatroskaTag::make(ostream &stream, Diagnostics &diag)
161 {
162  prepareMaking(diag).make(stream);
163 }
164 
172 void MatroskaTag::parseTargets(EbmlElement &targetsElement, Diagnostics &diag)
173 {
174  static const string context("parsing targets of Matroska tag");
175  m_target.clear();
176  bool targetTypeValueFound = false;
177  bool targetTypeFound = false;
178  targetsElement.parse(diag);
179  for (EbmlElement *child = targetsElement.firstChild(); child; child = child->nextSibling()) {
180  try {
181  child->parse(diag);
182  } catch (const Failure &) {
183  diag.emplace_back(DiagLevel::Critical, "Unable to parse childs of Targets element.", context);
184  break;
185  }
186  switch (child->id()) {
188  if (!targetTypeValueFound) {
189  m_target.setLevel(child->readUInteger());
190  targetTypeValueFound = true;
191  } else {
192  diag.emplace_back(
193  DiagLevel::Warning, "Targets element contains multiple TargetTypeValue elements. Surplus elements will be ignored.", context);
194  }
195  break;
197  if (!targetTypeFound) {
198  m_target.setLevelName(child->readString());
199  targetTypeFound = true;
200  } else {
201  diag.emplace_back(
202  DiagLevel::Warning, "Targets element contains multiple TargetType elements. Surplus elements will be ignored.", context);
203  }
204  break;
206  m_target.tracks().emplace_back(child->readUInteger());
207  break;
209  m_target.editions().emplace_back(child->readUInteger());
210  break;
212  m_target.chapters().emplace_back(child->readUInteger());
213  break;
215  m_target.attachments().emplace_back(child->readUInteger());
216  break;
217  default:
218  diag.emplace_back(DiagLevel::Warning, "Targets element contains unknown element. It will be ignored.", context);
219  }
220  }
221  if (!m_target.level()) {
222  m_target.setLevel(50); // default level
223  }
224 }
225 
237 MatroskaTagMaker::MatroskaTagMaker(MatroskaTag &tag, Diagnostics &diag)
238  : m_tag(tag)
239 {
240  // calculate size of "Targets" element
241  m_targetsSize = 0; // NOT including ID and size
242  if (m_tag.target().level() != 50) {
243  // size of "TargetTypeValue"
244  m_targetsSize += 2 + 1 + EbmlElement::calculateUIntegerLength(m_tag.target().level());
245  }
246  if (!m_tag.target().levelName().empty()) {
247  // size of "TargetType"
248  m_targetsSize += 2 + EbmlElement::calculateSizeDenotationLength(m_tag.target().levelName().size()) + m_tag.target().levelName().size();
249  }
250  for (const auto &v : initializer_list<vector<uint64>>{
251  m_tag.target().tracks(), m_tag.target().editions(), m_tag.target().chapters(), m_tag.target().attachments() }) {
252  for (auto uid : v) {
253  // size of UID denotation
254  m_targetsSize += 2 + 1 + EbmlElement::calculateUIntegerLength(uid);
255  }
256  }
257  m_tagSize = 2 + EbmlElement::calculateSizeDenotationLength(m_targetsSize) + m_targetsSize;
258  // calculate size of "SimpleTag" elements
259  m_maker.reserve(m_tag.fields().size());
260  m_simpleTagsSize = 0; // including ID and size
261  for (auto &pair : m_tag.fields()) {
262  try {
263  m_maker.emplace_back(pair.second.prepareMaking(diag));
264  m_simpleTagsSize += m_maker.back().requiredSize();
265  } catch (const Failure &) {
266  }
267  }
268  m_tagSize += m_simpleTagsSize;
269  m_totalSize = 2 + EbmlElement::calculateSizeDenotationLength(m_tagSize) + m_tagSize;
270 }
271 
279 void MatroskaTagMaker::make(ostream &stream) const
280 {
281  // write header
282  char buff[11];
283  BE::getBytes(static_cast<uint16>(MatroskaIds::Tag), buff);
284  stream.write(buff, 2); // ID
285  byte len = EbmlElement::makeSizeDenotation(m_tagSize, buff);
286  stream.write(buff, len); // size
287  // write "Targets" element
288  BE::getBytes(static_cast<uint16>(MatroskaIds::Targets), buff);
289  stream.write(buff, 2);
290  len = EbmlElement::makeSizeDenotation(m_targetsSize, buff);
291  stream.write(buff, len);
292  const TagTarget &t = m_tag.target();
293  if (t.level() != 50) {
294  // write "TargetTypeValue"
295  BE::getBytes(static_cast<uint16>(MatroskaIds::TargetTypeValue), buff);
296  stream.write(buff, 2);
297  len = EbmlElement::makeUInteger(t.level(), buff);
298  stream.put(0x80 | len);
299  stream.write(buff, len);
300  }
301  if (!t.levelName().empty()) {
302  // write "TargetType"
303  BE::getBytes(static_cast<uint16>(MatroskaIds::TargetType), buff);
304  stream.write(buff, 2);
305  len = EbmlElement::makeSizeDenotation(t.levelName().size(), buff);
306  stream.write(buff, len);
307  stream.write(t.levelName().c_str(), t.levelName().size());
308  }
309  // write UIDs
310  typedef pair<uint16, vector<uint64>> p;
311  for (const auto &pair : initializer_list<p>{ p(MatroskaIds::TagTrackUID, t.tracks()), p(MatroskaIds::TagEditionUID, t.editions()),
313  if (!pair.second.empty()) {
314  BE::getBytes(pair.first, buff);
315  for (auto uid : pair.second) {
316  len = EbmlElement::makeUInteger(uid, buff + 3);
317  *(buff + 2) = 0x80 | len;
318  stream.write(buff, 3 + len);
319  }
320  }
321  }
322  // write "SimpleTag" elements using maker objects prepared previously
323  for (const auto &maker : m_maker) {
324  maker.make(stream);
325  }
326 }
327 
328 } // 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
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()
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.
uint64 totalSize() const
Returns the total size of the element.
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.
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()
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
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.
TAG_PARSER_EXPORT const char * bpm()