Tag Parser  7.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 ConversionUtilities;
14 
15 namespace TagParser {
16 
25 void MatroskaSeekInfo::shift(uint64 start, int64 amount)
26 {
27  for (auto &info : m_info) {
28  if (get<1>(info) >= start) {
29  get<1>(info) += amount;
30  }
31  }
32 }
33 
40 void MatroskaSeekInfo::parse(EbmlElement *seekHeadElement, Diagnostics &diag)
41 {
42  static const string context("parsing \"SeekHead\"-element");
43  m_seekHeadElement = seekHeadElement;
44  m_info.clear();
45  EbmlElement *seekElement = seekHeadElement->firstChild();
46  EbmlElement *seekElementChild, *seekIdElement, *seekPositionElement;
47  while (seekElement) {
48  seekElement->parse(diag);
49  switch (seekElement->id()) {
50  case MatroskaIds::Seek:
51  seekElementChild = seekElement->firstChild();
52  seekIdElement = seekPositionElement = nullptr;
53  while (seekElementChild) {
54  seekElementChild->parse(diag);
55  switch (seekElementChild->id()) {
57  if (seekIdElement) {
58  diag.emplace_back(DiagLevel::Warning,
59  "The \"Seek\"-element contains multiple \"SeekID\"-elements. Surplus elements will be ignored.", context);
60  }
61  seekIdElement = seekElementChild;
62  break;
64  if (seekPositionElement) {
65  diag.emplace_back(DiagLevel::Warning,
66  "The \"Seek\"-element contains multiple \"SeekPosition\"-elements. Surplus elements will be ignored.", context);
67  }
68  seekPositionElement = seekElementChild;
69  break;
70  case EbmlIds::Crc32:
71  case EbmlIds::Void:
72  break;
73  default:
74  diag.emplace_back(DiagLevel::Warning,
75  "The element \"" % seekElementChild->idToString()
76  + "\" within the \"Seek\" element is not a \"SeekID\"-element nor a \"SeekPosition\"-element and will be ignored.",
77  context);
78  }
79  seekElementChild = seekElementChild->nextSibling();
80  }
81  if (seekIdElement && seekPositionElement) {
82  m_info.emplace_back(seekIdElement->readUInteger(), seekPositionElement->readUInteger());
83  } else {
84  diag.emplace_back(DiagLevel::Warning, "The \"Seek\"-element does not contain a \"SeekID\"- and a \"SeekPosition\"-element.", context);
85  }
86  break;
87  case EbmlIds::Crc32:
88  case EbmlIds::Void:
89  break;
90  default:
91  diag.emplace_back(
92  DiagLevel::Warning, "The element " % seekElement->idToString() + " is not a seek element and will be ignored.", context);
93  }
94  seekElement = seekElement->nextSibling();
95  }
96  if (m_info.empty()) {
97  diag.emplace_back(DiagLevel::Warning, "No seek information found.", context);
98  }
99 }
100 
107 void MatroskaSeekInfo::make(ostream &stream, Diagnostics &diag)
108 {
109  uint64 totalSize = 0;
110  char buff0[8];
111  char buff1[8];
112  char buff2[2];
113  byte sizeLength0, sizeLength1;
114  // calculate size
115  for (const auto &info : m_info) {
116  // "Seek" element + "SeekID" element + "SeekPosition" element
117  totalSize += 2 + 1 + (2 + 1 + EbmlElement::calculateIdLength(get<0>(info))) + (2 + 1 + EbmlElement::calculateUIntegerLength(get<1>(info)));
118  }
119  // write ID and size
120  BE::getBytes(static_cast<uint32>(MatroskaIds::SeekHead), buff0);
121  stream.write(buff0, 4);
122  sizeLength0 = EbmlElement::makeSizeDenotation(totalSize, buff0);
123  stream.write(buff0, sizeLength0);
124  // write entries
125  for (const auto &info : m_info) {
126  // make values
127  sizeLength0 = EbmlElement::makeId(get<0>(info), buff0);
128  sizeLength1 = EbmlElement::makeUInteger(get<1>(info), buff1);
129  // "Seek" header
130  BE::getBytes(static_cast<uint16>(MatroskaIds::Seek), buff2);
131  stream.write(buff2, 2);
132  stream.put(0x80 | (2 + 1 + sizeLength0 + 2 + 1 + sizeLength1));
133  // "SeekID" element
134  BE::getBytes(static_cast<uint16>(MatroskaIds::SeekID), buff2);
135  stream.write(buff2, 2);
136  stream.put(0x80 | sizeLength0);
137  stream.write(buff0, sizeLength0);
138  // "SeekPosition" element
139  BE::getBytes(static_cast<uint16>(MatroskaIds::SeekPosition), buff2);
140  stream.write(buff2, 2);
141  stream.put(0x80 | sizeLength1);
142  stream.write(buff1, sizeLength1);
143  }
144 }
145 
150 uint64 MatroskaSeekInfo::minSize() const
151 {
152  uint64 maxTotalSize = m_info.size() * (2 + 1 + 2 + 1 + 1 + 2 + 1 + 1);
153  return 4 + EbmlElement::calculateSizeDenotationLength(maxTotalSize) + maxTotalSize;
154 }
155 
160 uint64 MatroskaSeekInfo::maxSize() const
161 {
162  uint64 maxTotalSize = m_info.size() * (2 + 1 + 2 + 1 + 4 + 2 + 1 + 8);
163  return 4 + EbmlElement::calculateSizeDenotationLength(maxTotalSize) + maxTotalSize;
164 }
165 
170 uint64 MatroskaSeekInfo::actualSize() const
171 {
172  uint64 totalSize = 0;
173  for (const auto &info : m_info) {
174  // "Seek" element + "SeekID" element + "SeekPosition" element
175  totalSize += 2 + 1 + (2 + 1 + EbmlElement::calculateIdLength(get<0>(info))) + (2 + 1 + EbmlElement::calculateUIntegerLength(get<1>(info)));
176  }
177  return totalSize += 4 + EbmlElement::calculateSizeDenotationLength(totalSize);
178 }
179 
188 bool MatroskaSeekInfo::push(unsigned int index, EbmlElement::IdentifierType id, uint64 offset)
189 {
190  unsigned int currentIndex = 0;
191  for (auto &entry : info()) {
192  if (get<0>(entry) == id) {
193  if (index == currentIndex) {
194  bool sizeUpdated = EbmlElement::calculateUIntegerLength(get<1>(entry)) != EbmlElement::calculateUIntegerLength(offset);
195  get<1>(entry) = offset;
196  return sizeUpdated;
197  }
198  ++currentIndex;
199  }
200  }
201  info().emplace_back(id, offset);
202  return true;
203 }
204 
208 void MatroskaSeekInfo::clear()
209 {
210  m_seekHeadElement = nullptr;
211  m_info.clear();
212 }
213 
217 std::pair<EbmlElement::IdentifierType, uint64> *MatroskaSeekInfo::findSeekInfo(std::vector<MatroskaSeekInfo> &seekInfos, uint64 offset)
218 {
219  for (auto &seekInfo : seekInfos) {
220  for (auto &entry : seekInfo.info()) {
221  if (get<1>(entry) == offset) {
222  return &entry;
223  }
224  }
225  }
226  return nullptr;
227 }
228 
233 bool MatroskaSeekInfo::updateSeekInfo(
234  const std::vector<MatroskaSeekInfo> &oldSeekInfos, std::vector<MatroskaSeekInfo> &newSeekInfos, uint64 oldOffset, uint64 newOffset)
235 {
236  bool updated = false;
237  auto oldIterator0 = oldSeekInfos.cbegin(), oldEnd0 = oldSeekInfos.cend();
238  auto newIterator0 = newSeekInfos.begin(), newEnd0 = newSeekInfos.end();
239  for (; oldIterator0 != oldEnd0 && newIterator0 != newEnd0; ++oldIterator0, ++newIterator0) {
240  auto oldIterator1 = oldIterator0->info().cbegin(), oldEnd1 = oldIterator0->info().cend();
241  auto newIterator1 = newIterator0->info().begin(), newEnd1 = newIterator0->info().end();
242  for (; oldIterator1 != oldEnd1 && newIterator1 != newEnd1; ++oldIterator1, ++newIterator1) {
243  if (get<1>(*oldIterator1) == oldOffset) {
244  if (get<1>(*newIterator1) != newOffset) {
245  updated
246  = updated || (EbmlElement::calculateUIntegerLength(newOffset) != EbmlElement::calculateUIntegerLength(get<1>(*newIterator1)));
247  get<1>(*newIterator1) = newOffset;
248  }
249  }
250  }
251  }
252  return updated;
253 }
254 
259 bool MatroskaSeekInfo::updateSeekInfo(std::vector<MatroskaSeekInfo> &newSeekInfos, uint64 oldOffset, uint64 newOffset)
260 {
261  if (oldOffset == newOffset) {
262  return false;
263  }
264  bool updated = false;
265  for (auto &seekInfo : newSeekInfos) {
266  for (auto &info : seekInfo.info()) {
267  if (get<1>(info) == oldOffset) {
268  get<1>(info) = newOffset;
269  updated = true;
270  }
271  }
272  }
273  return updated;
274 }
275 
276 } // namespace TagParser
ImplementationType * nextSibling()
Returns the next sibling of the element.
ImplementationType * firstChild()
Returns the first child of the element.
STL namespace.
std::string idToString() const
Converts the specified EBML ID to a printable string.
Definition: ebmlelement.h:71
void parse(Diagnostics &diag)
Parses the header information of the element which is read from the related stream at the start offse...
uint64 readUInteger()
Reads the content of the element as unsigned integer.
void clear()
Clears the status of the element.
const IdentifierType & id() const
Returns the element ID.
FileElementTraits< ImplementationType >::IdentifierType IdentifierType
Specifies the type used to store identifiers.