Tag Parser  7.0.1
C++ library for reading and writing MP4 (iTunes), ID3, Vorbis, Opus, FLAC and Matroska tags
matroskaattachment.cpp
Go to the documentation of this file.
1 #include "./matroskaattachment.h"
2 #include "./ebmlelement.h"
3 #include "./matroskacontainer.h"
4 #include "./matroskaid.h"
5 
6 #include <c++utilities/conversion/binaryconversion.h>
7 #include <c++utilities/conversion/stringbuilder.h>
8 
9 #include <memory>
10 
11 using namespace std;
12 using namespace ConversionUtilities;
13 using namespace IoUtilities;
14 
15 namespace TagParser {
16 
29 void MatroskaAttachment::parse(EbmlElement *attachedFileElement, Diagnostics &diag)
30 {
31  clear();
32  static const string context("parsing \"AttachedFile\"-element");
33  m_attachedFileElement = attachedFileElement;
34  EbmlElement *subElement = attachedFileElement->firstChild();
35  while (subElement) {
36  subElement->parse(diag);
37  switch (subElement->id()) {
39  if (description().empty()) {
40  setDescription(subElement->readString());
41  } else {
42  diag.emplace_back(DiagLevel::Warning, "Multiple \"FileDescription\"-elements found. Surplus elements will be ignored.", context);
43  }
44  break;
46  if (name().empty()) {
47  setName(subElement->readString());
48  } else {
49  diag.emplace_back(DiagLevel::Warning, "Multiple \"FileName\"-elements found. Surplus elements will be ignored.", context);
50  }
51  break;
53  if (mimeType().empty()) {
54  setMimeType(subElement->readString());
55  } else {
56  diag.emplace_back(DiagLevel::Warning, "Multiple \"FileMimeType\"-elements found. Surplus elements will be ignored.", context);
57  }
58  break;
60  if (data()) {
61  diag.emplace_back(DiagLevel::Warning, "Multiple \"FileData\"-elements found. Surplus elements will be ignored.", context);
62  } else {
63  setData(make_unique<StreamDataBlock>(std::bind(&EbmlElement::stream, subElement), subElement->dataOffset(), ios_base::beg,
64  subElement->startOffset() + subElement->totalSize(), ios_base::beg));
65  }
66  break;
68  if (id()) {
69  diag.emplace_back(DiagLevel::Warning, "Multiple \"FileUID\"-elements found. Surplus elements will be ignored.", context);
70  } else {
71  setId(subElement->readUInteger());
72  }
73  break;
77  case EbmlIds::Crc32:
78  case EbmlIds::Void:
79  break;
80  default:
81  diag.emplace_back(DiagLevel::Warning, "Unknown child element \"" % subElement->idToString() + "\" found.", context);
82  }
83  subElement = subElement->nextSibling();
84  }
85 }
86 
94 void MatroskaAttachment::make(std::ostream &stream, Diagnostics &diag)
95 {
96  if (!data() || !data()->size()) {
97  diag.emplace_back(DiagLevel::Critical, "There is no data assigned.", "making Matroska attachment");
98  throw InvalidDataException();
99  }
100  prepareMaking(diag).make(stream, diag);
101 }
102 
114 MatroskaAttachmentMaker::MatroskaAttachmentMaker(MatroskaAttachment &attachment, Diagnostics &diag)
115  : m_attachment(attachment)
116 {
117  m_attachedFileElementSize = 2 + EbmlElement::calculateSizeDenotationLength(attachment.name().size()) + attachment.name().size() + 2
118  + EbmlElement::calculateSizeDenotationLength(attachment.mimeType().size()) + attachment.mimeType().size() + 2 + 1
120  if (auto dataSize = attachment.data() ? attachment.data()->size() : static_cast<istream::pos_type>(0)) {
121  m_attachedFileElementSize += 2 + EbmlElement::calculateSizeDenotationLength(dataSize) + dataSize;
122  }
123  if (!attachment.description().empty()) {
124  m_attachedFileElementSize
125  += 2 + EbmlElement::calculateSizeDenotationLength(attachment.description().size()) + attachment.description().size();
126  }
127  if (attachment.attachedFileElement()) {
128  EbmlElement *child;
129  for (auto id : initializer_list<EbmlElement::IdentifierType>{
131  if ((child = attachment.attachedFileElement()->childById(id, diag))) {
132  m_attachedFileElementSize += child->totalSize();
133  }
134  }
135  }
136  m_totalSize = 2 + EbmlElement::calculateSizeDenotationLength(m_attachedFileElementSize) + m_attachedFileElementSize;
137 }
138 
146 void MatroskaAttachmentMaker::make(ostream &stream, Diagnostics &diag) const
147 {
148  char buff[8];
149  BE::getBytes(static_cast<uint16>(MatroskaIds::AttachedFile), buff);
150  stream.write(buff, 2);
151  byte len = EbmlElement::makeSizeDenotation(m_attachedFileElementSize, buff);
152  stream.write(buff, len);
153  // make elements
155  if (!attachment().description().empty()) {
157  }
160  if (attachment().attachedFileElement()) {
161  EbmlElement *child;
162  for (auto id : initializer_list<EbmlElement::IdentifierType>{
164  if ((child = attachment().attachedFileElement()->childById(id, diag))) {
165  if (child->buffer()) {
166  child->copyBuffer(stream);
167  } else {
168  child->copyEntirely(stream, diag, nullptr);
169  }
170  }
171  }
172  }
173  if (attachment().data() && attachment().data()->size()) {
174  BE::getBytes(static_cast<uint16>(MatroskaIds::FileData), buff);
175  stream.write(buff, 2);
176  len = EbmlElement::makeSizeDenotation(attachment().data()->size(), buff);
177  stream.write(buff, len);
178  attachment().data()->copyTo(stream);
179  }
180 }
181 
183 {
184  EbmlElement *child;
185  if (attachment().attachedFileElement()) {
186  for (auto id : initializer_list<EbmlElement::IdentifierType>{
188  if ((child = attachment().attachedFileElement()->childById(id, diag))) {
189  child->makeBuffer();
190  }
191  }
192  }
193  if (attachment().data() && attachment().data()->size() && !attachment().isDataFromFile()) {
194  attachment().data()->makeBuffer();
195  }
196 }
197 
198 } // namespace TagParser
TAG_PARSER_EXPORT const char * description()
static void makeSimpleElement(std::ostream &stream, IdentifierType id, uint64 content)
Makes a simple EBML element.
void copyEntirely(std::ostream &targetStream, Diagnostics &diag, AbortableProgressFeedback *progress)
Writes the entire element including all childs to the specified targetStream.
const std::string & description() const
Returns a description of the attachment.
void makeBuffer()
Buffers the element (header and data).
const std::unique_ptr< char[]> & buffer()
Returns buffered data.
ImplementationType * nextSibling()
Returns the next sibling of the element.
ImplementationType * firstChild()
Returns the first child of the element.
static byte calculateSizeDenotationLength(uint64 size)
Returns the length of the size denotation for the specified size in byte.
std::string readString()
Reads the content of the element as string.
const MatroskaAttachment & attachment() const
Returns the associated attachment.
STL namespace.
void bufferCurrentAttachments(Diagnostics &diag)
uint64 startOffset() const
Returns the start offset in the related stream.
void make(std::ostream &stream, Diagnostics &diag) const
Saves the attachment (specified when constructing the object) to the specified stream (makes an "Atta...
static byte calculateUIntegerLength(uint64 integer)
Returns the length of the specified unsigned integer in byte.
void makeBuffer() const
Buffers the data block.
const StreamDataBlock * data() const
Returns a reference to the data of the attachment.
uint64 totalSize() const
Returns the total size of the element.
std::string idToString() const
Converts the specified EBML ID to a printable string.
Definition: ebmlelement.h:71
uint64 dataOffset() const
Returns the data offset of the element in the related stream.
uint64 id() const
Returns the ID of the attachment.
Contains utility classes helping to read and write streams.
void parse(Diagnostics &diag)
Parses the header information of the element which is read from the related stream at the start offse...
const std::string & name() const
Returns the (file) name of the attachment.
static byte makeSizeDenotation(uint64 size, char *buff)
Makes the size denotation for the specified size and stores it to buff.
uint64 readUInteger()
Reads the content of the element as unsigned integer.
void copyBuffer(std::ostream &targetStream)
Copies buffered data to targetStream.
EbmlElement * attachedFileElement() const
Returns the "AttachedFile"-element which has been specified when the parse() method has been called...
std::istream::pos_type size() const
Returns the size of the data block.
void copyTo(std::ostream &stream) const
Copies the data to the specified stream.
const IdentifierType & id() const
Returns the element ID.
ImplementationType * childById(const IdentifierType &id, Diagnostics &diag)
Returns the first child with the specified id.
const std::string & mimeType() const
Returns the MIME-type of the attachment.