Tag Parser  6.2.2
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 <map>
5 #include <initializer_list>
6 #include <stdexcept>
7 
8 using namespace std;
9 using namespace ConversionUtilities;
10 
11 namespace Media {
12 
18 std::string MatroskaTag::fieldId(KnownField field) const
19 {
20  using namespace MatroskaTagIds;
21  switch(field) {
22  case KnownField::Artist: return artist();
23  case KnownField::Album: return album();
24  case KnownField::Comment: return comment();
25  case KnownField::RecordDate: return dateRecorded();
26  case KnownField::Year: return dateRelease();
27  case KnownField::Title: return title();
28  case KnownField::Genre: return genre();
29  case KnownField::PartNumber: return partNumber();
30  case KnownField::TotalParts: return totalParts();
31  case KnownField::Encoder: return encoder();
32  case KnownField::EncoderSettings: return encoderSettings();
33  case KnownField::Bpm: return bpm();
34  case KnownField::Bps: return bps();
35  case KnownField::Rating: return rating();
36  case KnownField::Description: return description();
37  case KnownField::Lyrics: return lyrics();
38  case KnownField::RecordLabel: return label();
39  case KnownField::Performers: return actor();
40  case KnownField::Lyricist: return lyricist();
41  case KnownField::Composer: return composer();
42  case KnownField::Length: return duration();
43  case KnownField::Language: return language();
44  default: return string();
45  }
46 }
47 
48 KnownField MatroskaTag::knownField(const std::string &id) const
49 {
50  using namespace MatroskaTagIds;
51  static const map<string, KnownField> map({
55  {dateRecorded(), KnownField::RecordDate},
58  {partNumber(), KnownField::PartNumber},
59  {totalParts(), KnownField::TotalParts},
61  {encoderSettings(), KnownField::EncoderSettings},
62  {bpm(), KnownField::Bpm},
63  {bps(), KnownField::Bps},
71  {duration(), KnownField::Length},
72  {language(), KnownField::Language},
73  });
74  try {
75  return map.at(id);
76  } catch(const out_of_range &) {
77  return KnownField::Invalid;
78  }
79 }
80 
88 void MatroskaTag::parse(EbmlElement &tagElement)
89 {
90  invalidateStatus();
91  static const string context("parsing Matroska tag");
92  tagElement.parse();
93  m_size = tagElement.totalSize();
94  MatroskaTagField field;
95  for(EbmlElement *child = tagElement.firstChild(); child; child = child->nextSibling()) {
96  child->parse();
97  switch(child->id()) {
99  try {
100  field.invalidateNotifications();
101  field.reparse(*child, true);
102  fields().insert(make_pair(field.id(), field));
103  } catch(const Failure &) {
104  }
105  addNotifications(context, field);
106  break;
107  } case MatroskaIds::Targets:
108  parseTargets(*child);
109  break;
110  }
111  }
112 }
113 
124 MatroskaTagMaker MatroskaTag::prepareMaking()
125 {
126  return MatroskaTagMaker(*this);
127 }
128 
136 void MatroskaTag::make(ostream &stream)
137 {
138  prepareMaking().make(stream);
139 }
140 
148 void MatroskaTag::parseTargets(EbmlElement &targetsElement)
149 {
150  static const string context("parsing targets of Matroska tag");
151  m_target.clear();
152  bool targetTypeValueFound = false;
153  bool targetTypeFound = false;
154  targetsElement.parse();
155  for(EbmlElement *child = targetsElement.firstChild(); child; child = child->nextSibling()) {
156  try {
157  child->parse();
158  } catch(const Failure &) {
159  addNotification(NotificationType::Critical, "Unable to parse childs of Targets element.", context);
160  break;
161  }
162  switch(child->id()) {
164  if(!targetTypeValueFound) {
165  m_target.setLevel(child->readUInteger());
166  targetTypeValueFound = true;
167  } else {
168  addNotification(NotificationType::Warning, "Targets element contains multiple TargetTypeValue elements. Surplus elements will be ignored.", context);
169  }
170  break;
172  if(!targetTypeFound) {
173  m_target.setLevelName(child->readString());
174  targetTypeFound = true;
175  } else {
176  addNotification(NotificationType::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  addNotification(NotificationType::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) :
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<uint64> >{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());
237  m_simpleTagsSize += m_maker.back().requiredSize();
238  } catch(const Failure &) {
239  // nothing to do here; notifications will be added anyways
240  }
241  m_tag.addNotifications(pair.second);
242  }
243  m_tagSize += m_simpleTagsSize;
244  m_totalSize = 2 + EbmlElement::calculateSizeDenotationLength(m_tagSize) + m_tagSize;
245 }
246 
254 void MatroskaTagMaker::make(ostream &stream) const
255 {
256  // write header
257  char buff[11];
258  BE::getBytes(static_cast<uint16>(MatroskaIds::Tag), buff);
259  stream.write(buff, 2); // ID
260  byte len = EbmlElement::makeSizeDenotation(m_tagSize, buff);
261  stream.write(buff, len); // size
262  // write "Targets" element
263  BE::getBytes(static_cast<uint16>(MatroskaIds::Targets), buff);
264  stream.write(buff, 2);
265  len = EbmlElement::makeSizeDenotation(m_targetsSize, buff);
266  stream.write(buff, len);
267  const TagTarget &t = m_tag.target();
268  if(t.level() != 50) {
269  // write "TargetTypeValue"
270  BE::getBytes(static_cast<uint16>(MatroskaIds::TargetTypeValue), buff);
271  stream.write(buff, 2);
272  len = EbmlElement::makeUInteger(t.level(), buff);
273  stream.put(0x80 | len);
274  stream.write(buff, len);
275  }
276  if(!t.levelName().empty()) {
277  // write "TargetType"
278  BE::getBytes(static_cast<uint16>(MatroskaIds::TargetType), buff);
279  stream.write(buff, 2);
280  len = EbmlElement::makeSizeDenotation(t.levelName().size(), buff);
281  stream.write(buff, len);
282  stream.write(t.levelName().c_str(), t.levelName().size());
283  }
284  // write UIDs
285  typedef pair<uint16, vector<uint64> > p;
286  for(const auto &pair : initializer_list<p>{p(MatroskaIds::TagTrackUId, t.tracks()), p(MatroskaIds::TagEditionUId, t.editions()), p(MatroskaIds::TagChapterUId, t.chapters()), p(MatroskaIds::TagAttachmentUId, t.attachments())}) {
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) = 0x80 | len;
292  stream.write(buff, 3 + len);
293 
294  }
295  }
296  }
297  // write "SimpleTag" elements using maker objects prepared previously
298  for(const auto &maker : m_maker) {
299  maker.make(stream);
300  }
301 }
302 
303 } // namespace Media
TAG_PARSER_EXPORT const char * duration()
const std::string & levelName() const
Returns the level name.
Definition: tagtarget.h:97
TAG_PARSER_EXPORT const char * language()
const IdContainerType & attachments() const
Returns the attachments.
Definition: tagtarget.h:161
The EbmlElement class helps to parse EBML files such as Matroska files.
Definition: ebmlelement.h:50
TAG_PARSER_EXPORT const char * actor()
The MatroskaTagMaker class helps writing Matroska "Tag"-elements storing tag information.
Definition: matroskatag.h:14
void parse()
Parses the header information of the element which is read from the related stream at the start offse...
KnownField
Specifies the field.
Definition: tag.h:42
TAG_PARSER_EXPORT const char * encoder()
uint64 totalSize() const
Returns the total size of the element.
STL namespace.
const identifierType & id() const
Returns the id of the current TagField.
uint64 level() const
Returns the level.
Definition: tagtarget.h:81
TAG_PARSER_EXPORT const char * encoderSettings()
const IdContainerType & tracks() const
Returns the tracks.
Definition: tagtarget.h:113
TAG_PARSER_EXPORT const char * title()
TAG_PARSER_EXPORT const char * artist()
TAG_PARSER_EXPORT const char * dateRelease()
TAG_PARSER_EXPORT const char * dateRecorded()
TAG_PARSER_EXPORT const char * rating()
implementationType * firstChild()
Returns the first child of the element.
TAG_PARSER_EXPORT const char * totalParts()
Definition: matroskatagid.h:23
The class inherits from std::exception and serves as base class for exceptions thrown by the elements...
Definition: exceptions.h:11
TAG_PARSER_EXPORT const char * album()
const IdContainerType & editions() const
Returns the editions.
Definition: tagtarget.h:145
void reparse(EbmlElement &simpleTagElement, bool parseNestedFields=true)
Parses field information from the specified EbmlElement.
The MatroskaTagField class is used by MatroskaTag to store the fields.
TAG_PARSER_EXPORT const char * composer()
TAG_PARSER_EXPORT const char * bpm()
void invalidateNotifications()
Invalidates the object&#39;s notifications.
TAG_PARSER_EXPORT const char * description()
const IdContainerType & chapters() const
Returns the chapters.
Definition: tagtarget.h:129
TAG_PARSER_EXPORT const char * partNumber()
Implementation of Media::Tag for the Matroska container.
Definition: matroskatag.h:50
TAG_PARSER_EXPORT const char * lyricist()
The TagTarget class specifies the target of a tag.
Definition: tagtarget.h:31
Contains all classes and functions of the TagInfo library.
Definition: exceptions.h:9
TAG_PARSER_EXPORT const char * lyrics()
Definition: matroskatagid.h:80
TAG_PARSER_EXPORT const char * bps()
TAG_PARSER_EXPORT const char * comment()
TAG_PARSER_EXPORT const char * genre()