Tag Parser  6.2.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  default: return string();
44  }
45 }
46 
47 KnownField MatroskaTag::knownField(const std::string &id) const
48 {
49  using namespace MatroskaTagIds;
50  static const map<string, KnownField> map({
54  {dateRecorded(), KnownField::RecordDate},
57  {partNumber(), KnownField::PartNumber},
58  {totalParts(), KnownField::TotalParts},
60  {encoderSettings(), KnownField::EncoderSettings},
61  {bpm(), KnownField::Bpm},
62  {bps(), KnownField::Bps},
70  {duration(), KnownField::Length}
71  });
72  try {
73  return map.at(id);
74  } catch(const out_of_range &) {
75  return KnownField::Invalid;
76  }
77 }
78 
86 void MatroskaTag::parse(EbmlElement &tagElement)
87 {
88  invalidateStatus();
89  static const string context("parsing Matroska tag");
90  tagElement.parse();
91  m_size = tagElement.totalSize();
92  MatroskaTagField field;
93  for(EbmlElement *child = tagElement.firstChild(); child; child = child->nextSibling()) {
94  child->parse();
95  switch(child->id()) {
97  try {
99  field.reparse(*child, true);
100  fields().insert(make_pair(field.id(), field));
101  } catch(const Failure &) {
102  }
103  addNotifications(context, field);
104  break;
105  } case MatroskaIds::Targets:
106  parseTargets(*child);
107  break;
108  }
109  }
110 }
111 
122 MatroskaTagMaker MatroskaTag::prepareMaking()
123 {
124  return MatroskaTagMaker(*this);
125 }
126 
134 void MatroskaTag::make(ostream &stream)
135 {
136  prepareMaking().make(stream);
137 }
138 
146 void MatroskaTag::parseTargets(EbmlElement &targetsElement)
147 {
148  static const string context("parsing targets of Matroska tag");
149  m_target.clear();
150  bool targetTypeValueFound = false;
151  bool targetTypeFound = false;
152  targetsElement.parse();
153  for(EbmlElement *child = targetsElement.firstChild(); child; child = child->nextSibling()) {
154  try {
155  child->parse();
156  } catch(const Failure &) {
157  addNotification(NotificationType::Critical, "Unable to parse childs of Targets element.", context);
158  break;
159  }
160  switch(child->id()) {
162  if(!targetTypeValueFound) {
163  m_target.setLevel(child->readUInteger());
164  targetTypeValueFound = true;
165  } else {
166  addNotification(NotificationType::Warning, "Targets element contains multiple TargetTypeValue elements. Surplus elements will be ignored.", context);
167  }
168  break;
170  if(!targetTypeFound) {
171  m_target.setLevelName(child->readString());
172  targetTypeFound = true;
173  } else {
174  addNotification(NotificationType::Warning, "Targets element contains multiple TargetType elements. Surplus elements will be ignored.", context);
175  }
176  break;
178  m_target.tracks().emplace_back(child->readUInteger());
179  break;
181  m_target.editions().emplace_back(child->readUInteger());
182  break;
184  m_target.chapters().emplace_back(child->readUInteger());
185  break;
187  m_target.attachments().emplace_back(child->readUInteger());
188  break;
189  default:
190  addNotification(NotificationType::Warning, "Targets element contains unknown element. It will be ignored.", context);
191  }
192  }
193  if(!m_target.level()) {
194  m_target.setLevel(50); // default level
195  }
196 }
197 
209 MatroskaTagMaker::MatroskaTagMaker(MatroskaTag &tag) :
210  m_tag(tag)
211 {
212  // calculate size of "Targets" element
213  m_targetsSize = 0; // NOT including ID and size
214  if(m_tag.target().level() != 50) {
215  // size of "TargetTypeValue"
216  m_targetsSize += 2 + 1 + EbmlElement::calculateUIntegerLength(m_tag.target().level());
217  }
218  if(!m_tag.target().levelName().empty()) {
219  // size of "TargetType"
220  m_targetsSize += 2 + EbmlElement::calculateSizeDenotationLength(m_tag.target().levelName().size()) + m_tag.target().levelName().size();
221  }
222  for(const auto &v : initializer_list<vector<uint64> >{m_tag.target().tracks(), m_tag.target().editions(), m_tag.target().chapters(), m_tag.target().attachments()}) {
223  for(auto uid : v) {
224  // size of UID denotation
225  m_targetsSize += 2 + 1 + EbmlElement::calculateUIntegerLength(uid);
226  }
227  }
228  m_tagSize = 2 + EbmlElement::calculateSizeDenotationLength(m_targetsSize) + m_targetsSize;
229  // calculate size of "SimpleTag" elements
230  m_maker.reserve(m_tag.fields().size());
231  m_simpleTagsSize = 0; // including ID and size
232  for(auto &pair : m_tag.fields()) {
233  try {
234  m_maker.emplace_back(pair.second.prepareMaking());
235  m_simpleTagsSize += m_maker.back().requiredSize();
236  } catch(const Failure &) {
237  // nothing to do here; notifications will be added anyways
238  }
239  m_tag.addNotifications(pair.second);
240  }
241  m_tagSize += m_simpleTagsSize;
242  m_totalSize = 2 + EbmlElement::calculateSizeDenotationLength(m_tagSize) + m_tagSize;
243 }
244 
252 void MatroskaTagMaker::make(ostream &stream) const
253 {
254  // write header
255  char buff[11];
256  BE::getBytes(static_cast<uint16>(MatroskaIds::Tag), buff);
257  stream.write(buff, 2); // ID
258  byte len = EbmlElement::makeSizeDenotation(m_tagSize, buff);
259  stream.write(buff, len); // size
260  // write "Targets" element
261  BE::getBytes(static_cast<uint16>(MatroskaIds::Targets), buff);
262  stream.write(buff, 2);
263  len = EbmlElement::makeSizeDenotation(m_targetsSize, buff);
264  stream.write(buff, len);
265  const TagTarget &t = m_tag.target();
266  if(t.level() != 50) {
267  // write "TargetTypeValue"
268  BE::getBytes(static_cast<uint16>(MatroskaIds::TargetTypeValue), buff);
269  stream.write(buff, 2);
270  len = EbmlElement::makeUInteger(t.level(), buff);
271  stream.put(0x80 | len);
272  stream.write(buff, len);
273  }
274  if(!t.levelName().empty()) {
275  // write "TargetType"
276  BE::getBytes(static_cast<uint16>(MatroskaIds::TargetType), buff);
277  stream.write(buff, 2);
278  len = EbmlElement::makeSizeDenotation(t.levelName().size(), buff);
279  stream.write(buff, len);
280  stream.write(t.levelName().c_str(), t.levelName().size());
281  }
282  // write UIDs
283  typedef pair<uint16, vector<uint64> > p;
284  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())}) {
285  if(!pair.second.empty()) {
286  BE::getBytes(pair.first, buff);
287  for(auto uid : pair.second) {
288  len = EbmlElement::makeUInteger(uid, buff + 3);
289  *(buff + 2) = 0x80 | len;
290  stream.write(buff, 3 + len);
291 
292  }
293  }
294  }
295  // write "SimpleTag" elements using maker objects prepared previously
296  for(const auto &maker : m_maker) {
297  maker.make(stream);
298  }
299 }
300 
301 } // namespace Media
TAG_PARSER_EXPORT const char * duration()
const std::string & levelName() const
Returns the level name.
Definition: tagtarget.h:97
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()