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