Tag Parser  6.1.1
C++ library for reading and writing MP4 (iTunes), ID3, Vorbis, Opus, FLAC and Matroska tags
matroskatagfield.cpp
Go to the documentation of this file.
1 #include "./matroskatagfield.h"
2 #include "./ebmlelement.h"
3 #include "./matroskacontainer.h"
4 
5 #include "../exceptions.h"
6 
7 #include <c++utilities/io/binarywriter.h>
8 #include <c++utilities/misc/memory.h>
9 
10 using namespace std;
11 using namespace IoUtilities;
12 using namespace ConversionUtilities;
13 
14 namespace Media {
15 
24 MatroskaTagField::MatroskaTagField()
25 {}
26 
30 MatroskaTagField::MatroskaTagField(const string &id, const TagValue &value) :
31  TagField<MatroskaTagField>(id, value)
32 {}
33 
44 void MatroskaTagField::reparse(EbmlElement &simpleTagElement, bool parseNestedFields)
45 {
46  string context("parsing Matroska tag field");
47  clear();
48  simpleTagElement.parse();
49  EbmlElement *child = simpleTagElement.firstChild();
50  bool tagDefaultFound = false;
51  while(child) {
52  try {
53  child->parse();
54  } catch (Failure &) {
55  addNotification(NotificationType::Critical, "Unable to parse childs of \"SimpleTag\"-element.", context);
56  break;
57  }
58  switch(child->id()) {
60  if(id().empty()) {
61  setId(child->readString());
62  context = "parsing Matroska tag field " + id();
63  } else {
64  addNotification(NotificationType::Warning, "\"SimpleTag\"-element contains multiple \"TagName\"-elements. Surplus TagName elements will be ignored.", context);
65  }
66  break;
69  if(value().isEmpty()) {
70  unique_ptr<char[]> buffer = make_unique<char []>(child->dataSize());
71  child->stream().seekg(child->dataOffset());
72  child->stream().read(buffer.get(), child->dataSize());
73  switch(child->id()) {
76  break;
78  value().assignData(move(buffer), child->dataSize(), TagDataType::Undefined);
79  break;
80  }
81  } else {
82  addNotification(NotificationType::Warning, "\"SimpleTag\"-element contains multiple \"TagString\"/\"TagBinary\"-elements. Surplus \"TagName\"/\"TagBinary\"-elements will be ignored.", context);
83  }
84  break;
86  if(value().language().empty() || value().language() == "und") {
87  string lng = child->readString();
88  if(lng != "und") {
89  value().setLanguage(lng);
90  }
91  } else {
92  addNotification(NotificationType::Warning, "\"SimpleTag\"-element contains multiple \"TagLanguage\"-elements. Surplus \"TagLanguage\"-elements will be ignored.", context);
93  }
94  break;
96  if(!tagDefaultFound) {
97  setDefault(child->readUInteger() > 0);
98  tagDefaultFound = true;
99  } else {
100  addNotification(NotificationType::Warning, "\"SimpleTag\"-element contains multiple \"TagDefault\" elements. Surplus \"TagDefault\"-elements will be ignored.", context);
101  }
102  break;
104  if(parseNestedFields) {
105  nestedFields().emplace_back();
106  nestedFields().back().reparse(*child, true);
107  } else {
108  addNotification(NotificationType::Warning, "Nested fields are currently not supported. Nested tags can not be displayed and will be discarded when rewriting the file.", context);
109  }
110  break;
111  case EbmlIds::Crc32:
112  case EbmlIds::Void:
113  break;
114  default:
115  addNotification(NotificationType::Warning, "\"SimpleTag\"-element contains unknown element. It will be ignored.", context);
116  }
117  child = child->nextSibling();
118  }
119 }
120 
132 {
133  static const string context("making Matroska \"SimpleTag\" element.");
134  // check whether ID is empty
135  if(id().empty()) {
136  addNotification(NotificationType::Critical, "Can not make \"SimpleTag\" element with empty \"TagName\".", context);
137  throw InvalidDataException();
138  }
139  try {
140  return MatroskaTagFieldMaker(*this);
141  } catch(const ConversionException &) {
142  addNotification(NotificationType::Critical, "The assigned tag value can not be converted to be written appropriately.", context);
143  throw InvalidDataException();
144  }
145 }
146 
153 void MatroskaTagField::make(ostream &stream)
154 {
155  prepareMaking().make(stream);
156 }
157 
169 MatroskaTagFieldMaker::MatroskaTagFieldMaker(MatroskaTagField &field) :
170  m_field(field),
171  m_isBinary(false)
172 {
173  try {
174  m_stringValue = m_field.value().toString();
175  } catch(const ConversionException &) {
176  m_field.addNotification(NotificationType::Warning, "The assigned tag value can not be converted to a string and is treated as binary value (which is likely not what you want since official Matroska specifiecation doesn't list any binary fields).", "making Matroska \"SimpleTag\" element.");
177  m_isBinary = true;
178  }
179  size_t languageSize = m_field.value().language().size();
180  if(!languageSize) {
181  languageSize = 3; // if there's no language set, the 3 byte long value "und" is used
182  }
183  m_simpleTagSize =
184  // "TagName" element
185  + 2 + EbmlElement::calculateSizeDenotationLength(m_field.id().size()) + m_field.id().size()
186  // "TagLanguage" element
187  + 2 + EbmlElement::calculateSizeDenotationLength(languageSize) + languageSize
188  // "TagDefault" element
189  + 2 + 1 + 1
190  // "TagString" element
191  + 2 + EbmlElement::calculateSizeDenotationLength(m_stringValue.size()) + m_stringValue.size();
192  // nested tags
193  for(auto &nestedField : field.nestedFields()) {
194  m_nestedMaker.emplace_back(nestedField.prepareMaking());
195  m_simpleTagSize += m_nestedMaker.back().m_totalSize;
196  }
197  m_totalSize = 2 + EbmlElement::calculateSizeDenotationLength(m_simpleTagSize) + m_simpleTagSize;
198 }
199 
207 void MatroskaTagFieldMaker::make(ostream &stream) const
208 {
209  BinaryWriter writer(&stream);
210  char buff[8];
211  // write header of "SimpleTag" element
212  writer.writeUInt16BE(MatroskaIds::SimpleTag);
213  byte sizeDenotationLen = EbmlElement::makeSizeDenotation(m_simpleTagSize, buff);
214  stream.write(buff, sizeDenotationLen);
215  // write header of "TagName" element
216  writer.writeUInt16BE(MatroskaIds::TagName);
217  sizeDenotationLen = EbmlElement::makeSizeDenotation(m_field.id().size(), buff);
218  stream.write(buff, sizeDenotationLen);
219  stream.write(m_field.id().c_str(), m_field.id().size());
220  // write header of "TagLanguage" element
221  writer.writeUInt16BE(MatroskaIds::TagLanguage);
222  if(m_field.value().language().empty()) {
223  stream.put(static_cast<ostream::char_type>(0x80 | 3));
224  stream.write("und", 3);
225  } else {
226  sizeDenotationLen = EbmlElement::makeSizeDenotation(m_field.value().language().size(), buff);
227  stream.write(buff, sizeDenotationLen);
228  stream.write(m_field.value().language().c_str(), m_field.value().language().size());
229  }
230  // write header of "TagDefault" element
231  writer.writeUInt16BE(MatroskaIds::TagDefault);
232  stream.put(static_cast<ostream::char_type>(0x80 | 1));
233  stream.put(m_field.isDefault() ? 1 : 0);
234  // write header of "TagString"/"TagBinary" element
235  if(m_isBinary) {
236  writer.writeUInt16BE(MatroskaIds::TagBinary);
237  sizeDenotationLen = EbmlElement::makeSizeDenotation(m_field.value().dataSize(), buff);
238  stream.write(buff, sizeDenotationLen);
239  stream.write(m_field.value().dataPointer(), m_field.value().dataSize());
240  } else {
241  writer.writeUInt16BE(MatroskaIds::TagString);
242  sizeDenotationLen = EbmlElement::makeSizeDenotation(m_stringValue.size(), buff);
243  stream.write(buff, sizeDenotationLen);
244  stream.write(m_stringValue.data(), m_stringValue.size());
245  }
246  // make nested tags
247  for(const auto &maker : m_nestedMaker) {
248  maker.make(stream);
249  }
250 }
251 
252 }
The MatroskaTagFieldMaker class helps making tag fields.
The TagValue class wraps values of different types.
Definition: tagvalue.h:64
uint64 dataOffset() const
Returns the data offset of the element in the related stream.
std::iostream & stream()
Returns the related stream.
void make(std::ostream &stream) const
Saves the field (specified when constructing the object) to the specified stream (makes a "SimpleTag"...
static byte calculateSizeDenotationLength(uint64 size)
Returns the length of the size denotation for the specified size in byte.
TAG_PARSER_EXPORT const char * language()
implementationType * nextSibling()
Returns the next sibling of the element.
uint64 readUInteger()
Reads the content of the element as unsigned integer.
The EbmlElement class helps to parse EBML files such as Matroska files.
Definition: ebmlelement.h:50
void clear()
Clears id, value, type info and sets default flag to false.
dataSizeType dataSize() const
Returns the data size of the element in byte.
void parse()
Parses the header information of the element which is read from the related stream at the start offse...
void setDefault(bool isDefault)
Sets whether the field is labeled as default.
STL namespace.
void addNotification(const Notification &notification)
This protected method is meant to be called by the derived class to add a notification.
const identifierType & id() const
Returns the id of the current TagField.
static byte makeSizeDenotation(uint64 size, char *buff)
Makes the size denotation for the specified size and stores it to buff.
Contains utility classes helping to read and write streams.
The exception that is thrown when the data to be parsed or to be made seems invalid and therefore can...
Definition: exceptions.h:27
std::string readString()
Reads the content of the element as string.
MatroskaTagFieldMaker prepareMaking()
Prepares making.
TagValue & value()
Returns the value of the current TagField.
implementationType * firstChild()
Returns the first child of the element.
const identifierType & id() const
Returns the element ID.
The class inherits from std::exception and serves as base class for exceptions thrown by the elements...
Definition: exceptions.h:11
The TagField class is used by FieldMapBasedTag to store the fields.
void assignData(const char *data, size_t length, TagDataType type=TagDataType::Binary, TagTextEncoding encoding=TagTextEncoding::Latin1)
Assigns a copy of the given data.
Definition: tagvalue.cpp:624
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.
void make(std::ostream &stream)
Saves the field to the specified stream (makes a "SimpleTag" element).
Contains all classes and functions of the TagInfo library.
Definition: exceptions.h:9
void setLanguage(const std::string &value)
Sets the language.
Definition: tagvalue.h:430
const std::vector< MatroskaTagField > & nestedFields() const
Returns the nested fields.
void setId(const identifierType &id)
Sets the id of the current Tag Field.