Tag Parser  6.5.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 <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 
125 MatroskaTagMaker MatroskaTag::prepareMaking()
126 {
127  return MatroskaTagMaker(*this);
128 }
129 
138 void MatroskaTag::make(ostream &stream)
139 {
140  prepareMaking().make(stream);
141 }
142 
150 void MatroskaTag::parseTargets(EbmlElement &targetsElement)
151 {
152  static const string context("parsing targets of Matroska tag");
153  m_target.clear();
154  bool targetTypeValueFound = false;
155  bool targetTypeFound = false;
156  targetsElement.parse();
157  for(EbmlElement *child = targetsElement.firstChild(); child; child = child->nextSibling()) {
158  try {
159  child->parse();
160  } catch(const Failure &) {
161  addNotification(NotificationType::Critical, "Unable to parse childs of Targets element.", context);
162  break;
163  }
164  switch(child->id()) {
166  if(!targetTypeValueFound) {
167  m_target.setLevel(child->readUInteger());
168  targetTypeValueFound = true;
169  } else {
170  addNotification(NotificationType::Warning, "Targets element contains multiple TargetTypeValue elements. Surplus elements will be ignored.", context);
171  }
172  break;
174  if(!targetTypeFound) {
175  m_target.setLevelName(child->readString());
176  targetTypeFound = true;
177  } else {
178  addNotification(NotificationType::Warning, "Targets element contains multiple TargetType elements. Surplus elements will be ignored.", context);
179  }
180  break;
182  m_target.tracks().emplace_back(child->readUInteger());
183  break;
185  m_target.editions().emplace_back(child->readUInteger());
186  break;
188  m_target.chapters().emplace_back(child->readUInteger());
189  break;
191  m_target.attachments().emplace_back(child->readUInteger());
192  break;
193  default:
194  addNotification(NotificationType::Warning, "Targets element contains unknown element. It will be ignored.", context);
195  }
196  }
197  if(!m_target.level()) {
198  m_target.setLevel(50); // default level
199  }
200 }
201 
213 MatroskaTagMaker::MatroskaTagMaker(MatroskaTag &tag) :
214  m_tag(tag)
215 {
216  // calculate size of "Targets" element
217  m_targetsSize = 0; // NOT including ID and size
218  if(m_tag.target().level() != 50) {
219  // size of "TargetTypeValue"
220  m_targetsSize += 2 + 1 + EbmlElement::calculateUIntegerLength(m_tag.target().level());
221  }
222  if(!m_tag.target().levelName().empty()) {
223  // size of "TargetType"
224  m_targetsSize += 2 + EbmlElement::calculateSizeDenotationLength(m_tag.target().levelName().size()) + m_tag.target().levelName().size();
225  }
226  for(const auto &v : initializer_list<vector<uint64> >{m_tag.target().tracks(), m_tag.target().editions(), m_tag.target().chapters(), m_tag.target().attachments()}) {
227  for(auto uid : v) {
228  // size of UID denotation
229  m_targetsSize += 2 + 1 + EbmlElement::calculateUIntegerLength(uid);
230  }
231  }
232  m_tagSize = 2 + EbmlElement::calculateSizeDenotationLength(m_targetsSize) + m_targetsSize;
233  // calculate size of "SimpleTag" elements
234  m_maker.reserve(m_tag.fields().size());
235  m_simpleTagsSize = 0; // including ID and size
236  for(auto &pair : m_tag.fields()) {
237  try {
238  m_maker.emplace_back(pair.second.prepareMaking());
239  m_simpleTagsSize += m_maker.back().requiredSize();
240  } catch(const Failure &) {
241  // nothing to do here; notifications will be added anyways
242  }
243  m_tag.addNotifications(pair.second);
244  }
245  m_tagSize += m_simpleTagsSize;
246  m_totalSize = 2 + EbmlElement::calculateSizeDenotationLength(m_tagSize) + m_tagSize;
247 }
248 
256 void MatroskaTagMaker::make(ostream &stream) const
257 {
258  // write header
259  char buff[11];
260  BE::getBytes(static_cast<uint16>(MatroskaIds::Tag), buff);
261  stream.write(buff, 2); // ID
262  byte len = EbmlElement::makeSizeDenotation(m_tagSize, buff);
263  stream.write(buff, len); // size
264  // write "Targets" element
265  BE::getBytes(static_cast<uint16>(MatroskaIds::Targets), buff);
266  stream.write(buff, 2);
267  len = EbmlElement::makeSizeDenotation(m_targetsSize, buff);
268  stream.write(buff, len);
269  const TagTarget &t = m_tag.target();
270  if(t.level() != 50) {
271  // write "TargetTypeValue"
272  BE::getBytes(static_cast<uint16>(MatroskaIds::TargetTypeValue), buff);
273  stream.write(buff, 2);
274  len = EbmlElement::makeUInteger(t.level(), buff);
275  stream.put(0x80 | len);
276  stream.write(buff, len);
277  }
278  if(!t.levelName().empty()) {
279  // write "TargetType"
280  BE::getBytes(static_cast<uint16>(MatroskaIds::TargetType), buff);
281  stream.write(buff, 2);
282  len = EbmlElement::makeSizeDenotation(t.levelName().size(), buff);
283  stream.write(buff, len);
284  stream.write(t.levelName().c_str(), t.levelName().size());
285  }
286  // write UIDs
287  typedef pair<uint16, vector<uint64> > p;
288  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())}) {
289  if(!pair.second.empty()) {
290  BE::getBytes(pair.first, buff);
291  for(auto uid : pair.second) {
292  len = EbmlElement::makeUInteger(uid, buff + 3);
293  *(buff + 2) = 0x80 | len;
294  stream.write(buff, 3 + len);
295 
296  }
297  }
298  }
299  // write "SimpleTag" elements using maker objects prepared previously
300  for(const auto &maker : m_maker) {
301  maker.make(stream);
302  }
303 }
304 
305 } // namespace Media
TAG_PARSER_EXPORT const char * duration()
const TagTarget & target() const
Returns the target of tag.
Definition: tag.h:245
const std::string & levelName() const
Returns the level name.
Definition: tagtarget.h:97
static byte calculateSizeDenotationLength(uint64 size)
Returns the length of the size denotation for the specified size in byte.
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()
static byte makeUInteger(uint64 value, char *buff)
Writes value to buff.
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
static byte calculateUIntegerLength(uint64 integer)
Returns the length of the specified unsigned integer in byte.
TAG_PARSER_EXPORT const char * title()
TAG_PARSER_EXPORT const char * artist()
TAG_PARSER_EXPORT const char * dateRelease()
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 * 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()
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()
void make(std::ostream &stream) const
Saves the tag (specified when constructing the object) to the specified stream (makes a "Tag"-element...
TAG_PARSER_EXPORT const char * comment()
TAG_PARSER_EXPORT const char * genre()