Tag Parser  6.2.2
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 
9 #include <memory>
10 
11 using namespace std;
12 using namespace IoUtilities;
13 using namespace ConversionUtilities;
14 
15 namespace Media {
16 
25 MatroskaTagField::MatroskaTagField()
26 {}
27 
31 MatroskaTagField::MatroskaTagField(const string &id, const TagValue &value) :
32  TagField<MatroskaTagField>(id, value)
33 {}
34 
45 void MatroskaTagField::reparse(EbmlElement &simpleTagElement, bool parseNestedFields)
46 {
47  string context("parsing Matroska tag field");
48  clear();
49  simpleTagElement.parse();
50  EbmlElement *child = simpleTagElement.firstChild();
51  bool tagDefaultFound = false;
52  while(child) {
53  try {
54  child->parse();
55  } catch (Failure &) {
56  addNotification(NotificationType::Critical, "Unable to parse childs of \"SimpleTag\"-element.", context);
57  break;
58  }
59  switch(child->id()) {
61  if(id().empty()) {
62  setId(child->readString());
63  context = "parsing Matroska tag field " + id();
64  } else {
65  addNotification(NotificationType::Warning, "\"SimpleTag\"-element contains multiple \"TagName\"-elements. Surplus TagName elements will be ignored.", context);
66  }
67  break;
70  if(value().isEmpty()) {
71  unique_ptr<char[]> buffer = make_unique<char []>(child->dataSize());
72  child->stream().seekg(child->dataOffset());
73  child->stream().read(buffer.get(), child->dataSize());
74  switch(child->id()) {
77  break;
79  value().assignData(move(buffer), child->dataSize(), TagDataType::Undefined);
80  break;
81  }
82  } else {
83  addNotification(NotificationType::Warning, "\"SimpleTag\"-element contains multiple \"TagString\"/\"TagBinary\"-elements. Surplus \"TagName\"/\"TagBinary\"-elements will be ignored.", context);
84  }
85  break;
87  if(value().language().empty() || value().language() == "und") {
88  string lng = child->readString();
89  if(lng != "und") {
90  value().setLanguage(lng);
91  }
92  } else {
93  addNotification(NotificationType::Warning, "\"SimpleTag\"-element contains multiple \"TagLanguage\"-elements. Surplus \"TagLanguage\"-elements will be ignored.", context);
94  }
95  break;
97  if(!tagDefaultFound) {
98  setDefault(child->readUInteger() > 0);
99  tagDefaultFound = true;
100  } else {
101  addNotification(NotificationType::Warning, "\"SimpleTag\"-element contains multiple \"TagDefault\" elements. Surplus \"TagDefault\"-elements will be ignored.", context);
102  }
103  break;
105  if(parseNestedFields) {
106  nestedFields().emplace_back();
107  nestedFields().back().reparse(*child, true);
108  } else {
109  addNotification(NotificationType::Warning, "Nested fields are currently not supported. Nested tags can not be displayed and will be discarded when rewriting the file.", context);
110  }
111  break;
112  case EbmlIds::Crc32:
113  case EbmlIds::Void:
114  break;
115  default:
116  addNotification(NotificationType::Warning, "\"SimpleTag\"-element contains unknown element. It will be ignored.", context);
117  }
118  child = child->nextSibling();
119  }
120 }
121 
133 {
134  static const string context("making Matroska \"SimpleTag\" element.");
135  // check whether ID is empty
136  if(id().empty()) {
137  addNotification(NotificationType::Critical, "Can not make \"SimpleTag\" element with empty \"TagName\".", context);
138  throw InvalidDataException();
139  }
140  try {
141  return MatroskaTagFieldMaker(*this);
142  } catch(const ConversionException &) {
143  addNotification(NotificationType::Critical, "The assigned tag value can not be converted to be written appropriately.", context);
144  throw InvalidDataException();
145  }
146 }
147 
154 void MatroskaTagField::make(ostream &stream)
155 {
156  prepareMaking().make(stream);
157 }
158 
170 MatroskaTagFieldMaker::MatroskaTagFieldMaker(MatroskaTagField &field) :
171  m_field(field),
172  m_isBinary(false)
173 {
174  try {
175  m_stringValue = m_field.value().toString();
176  } catch(const ConversionException &) {
177  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.");
178  m_isBinary = true;
179  }
180  size_t languageSize = m_field.value().language().size();
181  if(!languageSize) {
182  languageSize = 3; // if there's no language set, the 3 byte long value "und" is used
183  }
184  m_simpleTagSize =
185  // "TagName" element
186  + 2 + EbmlElement::calculateSizeDenotationLength(m_field.id().size()) + m_field.id().size()
187  // "TagLanguage" element
188  + 2 + EbmlElement::calculateSizeDenotationLength(languageSize) + languageSize
189  // "TagDefault" element
190  + 2 + 1 + 1
191  // "TagString" element
192  + 2 + EbmlElement::calculateSizeDenotationLength(m_stringValue.size()) + m_stringValue.size();
193  // nested tags
194  for(auto &nestedField : field.nestedFields()) {
195  m_nestedMaker.emplace_back(nestedField.prepareMaking());
196  m_simpleTagSize += m_nestedMaker.back().m_totalSize;
197  }
198  m_totalSize = 2 + EbmlElement::calculateSizeDenotationLength(m_simpleTagSize) + m_simpleTagSize;
199 }
200 
208 void MatroskaTagFieldMaker::make(ostream &stream) const
209 {
210  BinaryWriter writer(&stream);
211  char buff[8];
212  // write header of "SimpleTag" element
213  writer.writeUInt16BE(MatroskaIds::SimpleTag);
214  byte sizeDenotationLen = EbmlElement::makeSizeDenotation(m_simpleTagSize, buff);
215  stream.write(buff, sizeDenotationLen);
216  // write header of "TagName" element
217  writer.writeUInt16BE(MatroskaIds::TagName);
218  sizeDenotationLen = EbmlElement::makeSizeDenotation(m_field.id().size(), buff);
219  stream.write(buff, sizeDenotationLen);
220  stream.write(m_field.id().c_str(), m_field.id().size());
221  // write header of "TagLanguage" element
222  writer.writeUInt16BE(MatroskaIds::TagLanguage);
223  if(m_field.value().language().empty()) {
224  stream.put(static_cast<ostream::char_type>(0x80 | 3));
225  stream.write("und", 3);
226  } else {
227  sizeDenotationLen = EbmlElement::makeSizeDenotation(m_field.value().language().size(), buff);
228  stream.write(buff, sizeDenotationLen);
229  stream.write(m_field.value().language().c_str(), m_field.value().language().size());
230  }
231  // write header of "TagDefault" element
232  writer.writeUInt16BE(MatroskaIds::TagDefault);
233  stream.put(static_cast<ostream::char_type>(0x80 | 1));
234  stream.put(m_field.isDefault() ? 1 : 0);
235  // write header of "TagString"/"TagBinary" element
236  if(m_isBinary) {
237  writer.writeUInt16BE(MatroskaIds::TagBinary);
238  sizeDenotationLen = EbmlElement::makeSizeDenotation(m_field.value().dataSize(), buff);
239  stream.write(buff, sizeDenotationLen);
240  stream.write(m_field.value().dataPointer(), m_field.value().dataSize());
241  } else {
242  writer.writeUInt16BE(MatroskaIds::TagString);
243  sizeDenotationLen = EbmlElement::makeSizeDenotation(m_stringValue.size(), buff);
244  stream.write(buff, sizeDenotationLen);
245  stream.write(m_stringValue.data(), m_stringValue.size());
246  }
247  // make nested tags
248  for(const auto &maker : m_nestedMaker) {
249  maker.make(stream);
250  }
251 }
252 
253 }
The MatroskaTagFieldMaker class helps making tag fields.
The TagValue class wraps values of different types.
Definition: tagvalue.h:63
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:629
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:439
const std::vector< MatroskaTagField > & nestedFields() const
Returns the nested fields.
void setId(const identifierType &id)
Sets the id of the current Tag Field.