Tag Parser  9.0.0
C++ library for reading and writing MP4 (iTunes), ID3, Vorbis, Opus, FLAC and Matroska tags
matroskaseekinfo.cpp
Go to the documentation of this file.
1 #include "./matroskaseekinfo.h"
2 #include "./matroskaid.h"
3 
4 #include "../diagnostics.h"
5 #include "../exceptions.h"
6 
7 #include <c++utilities/conversion/binaryconversion.h>
8 #include <c++utilities/conversion/stringbuilder.h>
9 
10 #include <string>
11 
12 using namespace std;
13 using namespace CppUtilities;
14 
15 namespace TagParser {
16 
25 void MatroskaSeekInfo::shift(std::uint64_t start, std::int64_t amount)
26 {
27  for (auto &info : m_info) {
28  if (get<1>(info) >= start) {
29  if (amount > 0) {
30  get<1>(info) += static_cast<std::uint64_t>(amount);
31  } else {
32  get<1>(info) -= static_cast<std::uint64_t>(-amount);
33  }
34  }
35  }
36 }
37 
51 void MatroskaSeekInfo::parse(EbmlElement *seekHeadElement, Diagnostics &diag, size_t maxIndirection)
52 {
53  static const string context("parsing \"SeekHead\"-element");
54 
55  m_seekHeadElements.emplace_back(seekHeadElement);
56 
57  for (EbmlElement *seekElement = seekHeadElement->firstChild(), *seekIdElement, *seekPositionElement; seekElement;
58  seekElement = seekElement->nextSibling()) {
59  seekElement->parse(diag);
60  switch (seekElement->id()) {
61  case MatroskaIds::Seek:
62  seekIdElement = seekPositionElement = nullptr;
63  for (auto *seekElementChild = seekElement->firstChild(); seekElementChild; seekElementChild = seekElementChild->nextSibling()) {
64  seekElementChild->parse(diag);
65  switch (seekElementChild->id()) {
67  if (seekIdElement) {
68  diag.emplace_back(DiagLevel::Warning,
69  "The \"Seek\"-element contains multiple \"SeekID\"-elements. Surplus elements will be ignored.", context);
70  }
71  seekIdElement = seekElementChild;
72  break;
74  if (seekPositionElement) {
75  diag.emplace_back(DiagLevel::Warning,
76  "The \"Seek\"-element contains multiple \"SeekPosition\"-elements. Surplus elements will be ignored.", context);
77  }
78  seekPositionElement = seekElementChild;
79  break;
80  case EbmlIds::Crc32:
81  case EbmlIds::Void:
82  break;
83  default:
84  diag.emplace_back(DiagLevel::Warning,
85  "The element \"" % seekElementChild->idToString()
86  + "\" within the \"Seek\" element is not a \"SeekID\"-element nor a \"SeekPosition\"-element and will be ignored.",
87  context);
88  }
89  }
90 
91  if (!seekIdElement || !seekPositionElement) {
92  diag.emplace_back(DiagLevel::Warning, "The \"Seek\"-element does not contain a \"SeekID\"- and a \"SeekPosition\"-element.", context);
93  break;
94  }
95 
96  m_info.emplace_back(seekIdElement->readUInteger(), seekPositionElement->readUInteger());
97 
98  // follow possibly referenced seek head element
99  if (m_info.back().first == MatroskaIds::SeekHead) {
100  const auto startOffset = m_info.back().second;
101  if (!maxIndirection) {
102  diag.emplace_back(DiagLevel::Warning,
103  argsToString("Not following reference by \"Seek\"-element at ", seekElement->startOffset(),
104  " which points to another \"SeekHead\"-element at ", startOffset, '.'),
105  context);
106  break;
107  }
108 
109  auto visited = false;
110  for (const auto *const visitedSeekHeadElement : m_seekHeadElements) {
111  if (visitedSeekHeadElement->startOffset() == startOffset) {
112  diag.emplace_back(DiagLevel::Warning,
113  argsToString("The \"Seek\"-element at ", seekElement->startOffset(), " contains a loop to the \"SeekHead\"-element at ",
114  visitedSeekHeadElement->startOffset(), '.'),
115  context);
116  visited = true;
117  break;
118  }
119  }
120  if (visited) {
121  break;
122  }
123  m_additionalSeekHeadElements.emplace_back(make_unique<EbmlElement>(seekHeadElement->container(), startOffset));
124  parse(m_additionalSeekHeadElements.back().get(), diag, maxIndirection - 1);
125  }
126 
127  break;
128  case EbmlIds::Crc32:
129  case EbmlIds::Void:
130  break;
131  default:
132  diag.emplace_back(
133  DiagLevel::Warning, "The element " % seekElement->idToString() + " is not a seek element and will be ignored.", context);
134  }
135  }
136  if (m_info.empty()) {
137  diag.emplace_back(DiagLevel::Warning, "No seek information found.", context);
138  }
139 }
140 
147 void MatroskaSeekInfo::make(ostream &stream, Diagnostics &diag)
148 {
149  CPP_UTILITIES_UNUSED(diag)
150 
151  std::uint64_t totalSize = 0;
152  char buff0[8];
153  char buff1[8];
154  char buff2[2];
155  std::uint8_t sizeLength0, sizeLength1;
156  // calculate size
157  for (const auto &info : m_info) {
158  // "Seek" element + "SeekID" element + "SeekPosition" element
159  totalSize += 2 + 1 + (2 + 1 + EbmlElement::calculateIdLength(get<0>(info))) + (2 + 1 + EbmlElement::calculateUIntegerLength(get<1>(info)));
160  }
161  // write ID and size
162  BE::getBytes(static_cast<std::uint32_t>(MatroskaIds::SeekHead), buff0);
163  stream.write(buff0, 4);
164  sizeLength0 = EbmlElement::makeSizeDenotation(totalSize, buff0);
165  stream.write(buff0, sizeLength0);
166  // write entries
167  for (const auto &info : m_info) {
168  // make values
169  sizeLength0 = EbmlElement::makeId(get<0>(info), buff0);
170  sizeLength1 = EbmlElement::makeUInteger(get<1>(info), buff1);
171  // "Seek" header
172  BE::getBytes(static_cast<std::uint16_t>(MatroskaIds::Seek), buff2);
173  stream.write(buff2, 2);
174  stream.put(static_cast<char>(0x80 | (2 + 1 + sizeLength0 + 2 + 1 + sizeLength1)));
175  // "SeekID" element
176  BE::getBytes(static_cast<std::uint16_t>(MatroskaIds::SeekID), buff2);
177  stream.write(buff2, 2);
178  stream.put(static_cast<char>(0x80 | sizeLength0));
179  stream.write(buff0, sizeLength0);
180  // "SeekPosition" element
181  BE::getBytes(static_cast<std::uint16_t>(MatroskaIds::SeekPosition), buff2);
182  stream.write(buff2, 2);
183  stream.put(static_cast<char>(0x80 | sizeLength1));
184  stream.write(buff1, sizeLength1);
185  }
186 }
187 
192 std::uint64_t MatroskaSeekInfo::minSize() const
193 {
194  std::uint64_t maxTotalSize = m_info.size() * (2 + 1 + 2 + 1 + 1 + 2 + 1 + 1);
195  return 4 + EbmlElement::calculateSizeDenotationLength(maxTotalSize) + maxTotalSize;
196 }
197 
202 std::uint64_t MatroskaSeekInfo::maxSize() const
203 {
204  std::uint64_t maxTotalSize = m_info.size() * (2 + 1 + 2 + 1 + 4 + 2 + 1 + 8);
205  return 4 + EbmlElement::calculateSizeDenotationLength(maxTotalSize) + maxTotalSize;
206 }
207 
212 std::uint64_t MatroskaSeekInfo::actualSize() const
213 {
214  std::uint64_t totalSize = 0;
215  for (const auto &info : m_info) {
216  // "Seek" element + "SeekID" element + "SeekPosition" element
217  totalSize += 2 + 1 + (2 + 1 + EbmlElement::calculateIdLength(get<0>(info))) + (2 + 1 + EbmlElement::calculateUIntegerLength(get<1>(info)));
218  }
219  return totalSize += 4 + EbmlElement::calculateSizeDenotationLength(totalSize);
220 }
221 
230 bool MatroskaSeekInfo::push(unsigned int index, EbmlElement::IdentifierType id, std::uint64_t offset)
231 {
232  unsigned int currentIndex = 0;
233  for (auto &entry : info()) {
234  if (get<0>(entry) == id) {
235  if (index == currentIndex) {
236  bool sizeUpdated = EbmlElement::calculateUIntegerLength(get<1>(entry)) != EbmlElement::calculateUIntegerLength(offset);
237  get<1>(entry) = offset;
238  return sizeUpdated;
239  }
240  ++currentIndex;
241  }
242  }
243  info().emplace_back(id, offset);
244  return true;
245 }
246 
250 void MatroskaSeekInfo::clear()
251 {
252  m_seekHeadElements.clear();
253  m_additionalSeekHeadElements.clear();
254  m_info.clear();
255 }
256 
257 } // namespace TagParser
matroskaseekinfo.h
TagParser::MatroskaIds::SeekPosition
Definition: matroskaid.h:37
TagParser::Diagnostics
The Diagnostics class is a container for DiagMessage.
Definition: diagnostics.h:156
TagParser::GenericFileElement::IdentifierType
typename FileElementTraits< ImplementationType >::IdentifierType IdentifierType
Specifies the type used to store identifiers.
Definition: genericfileelement.h:57
TagParser::MatroskaIds::SeekID
Definition: matroskaid.h:37
TagParser
Contains all classes and functions of the TagInfo library.
Definition: aaccodebook.h:10
TagParser::GenericFileElement::firstChild
ImplementationType * firstChild()
Returns the first child of the element.
Definition: genericfileelement.h:460
matroskaid.h
TagParser::EbmlElement
The EbmlElement class helps to parse EBML files such as Matroska files.
Definition: ebmlelement.h:31
CppUtilities
Definition: abstractcontainer.h:15
TagParser::EbmlIds::Void
Definition: ebmlid.h:28
TagParser::MatroskaIds::SeekHead
Definition: matroskaid.h:21
TagParser::EbmlIds::Crc32
Definition: ebmlid.h:28
TagParser::GenericFileElement::container
ContainerType & container()
Returns the related container.
Definition: genericfileelement.h:220
TagParser::MatroskaIds::Seek
Definition: matroskaid.h:31